From f5c4671bfbad96bf346bd7e9a21fc4317b4959df Mon Sep 17 00:00:00 2001 From: Indrajith K L Date: Sat, 3 Dec 2022 17:00:20 +0530 Subject: Adds most of the tools --- v_windows/v/old/BSDmakefile | 8 + v_windows/v/old/CHANGELOG.md | 486 + v_windows/v/old/CODE_OF_CONDUCT.md | 4 + v_windows/v/old/CONTRIBUTING.md | 199 + v_windows/v/old/Dockerfile | 27 + v_windows/v/old/Dockerfile.alpine | 34 + v_windows/v/old/Dockerfile.cross | 10 + v_windows/v/old/LICENSE | 21 + v_windows/v/old/Makefile | 154 + v_windows/v/old/README.md | 280 + v_windows/v/old/ROADMAP.md | 32 + v_windows/v/old/TESTS.md | 115 + v_windows/v/old/cmd/tools/bench/wyhash.v | 56 + v_windows/v/old/cmd/tools/check_os_api_parity.v | 130 + v_windows/v/old/cmd/tools/fast/.gitignore | 5 + v_windows/v/old/cmd/tools/fast/fast.v | 178 + v_windows/v/old/cmd/tools/fast/fast_job.v | 43 + v_windows/v/old/cmd/tools/fast/fast_main.js | 67 + v_windows/v/old/cmd/tools/fast/footer.html | 4 + v_windows/v/old/cmd/tools/fast/header.html | 65 + v_windows/v/old/cmd/tools/fuzz/fuzz.sh | 18 + v_windows/v/old/cmd/tools/fuzz/map_fuzz.v | 144 + v_windows/v/old/cmd/tools/gen1m.v | 16 + v_windows/v/old/cmd/tools/gen_vc.v | 370 + v_windows/v/old/cmd/tools/missdoc.v | 141 + .../v/old/cmd/tools/modules/scripting/scripting.v | 180 + v_windows/v/old/cmd/tools/modules/testing/common.v | 488 + v_windows/v/old/cmd/tools/modules/vgit/vgit.v | 197 + v_windows/v/old/cmd/tools/modules/vhelp/vhelp.v | 14 + v_windows/v/old/cmd/tools/oldv.v | 176 + v_windows/v/old/cmd/tools/performance_compare.v | 215 + v_windows/v/old/cmd/tools/repeat.v | 374 + .../v/old/cmd/tools/test_if_v_test_system_works.v | 74 + v_windows/v/old/cmd/tools/test_os_process.v | 82 + v_windows/v/old/cmd/tools/vast/cjson.v | 114 + v_windows/v/old/cmd/tools/vast/test/.gitignore | 1 + v_windows/v/old/cmd/tools/vast/test/demo.v | 121 + v_windows/v/old/cmd/tools/vast/vast.v | 2246 ++ v_windows/v/old/cmd/tools/vbin2v.v | 146 + v_windows/v/old/cmd/tools/vbug.v | 208 + v_windows/v/old/cmd/tools/vbuild-examples.v | 15 + v_windows/v/old/cmd/tools/vbuild-tools.v | 71 + v_windows/v/old/cmd/tools/vbuild-vbinaries.v | 9 + v_windows/v/old/cmd/tools/vcheck-md.v | 540 + v_windows/v/old/cmd/tools/vcomplete.v | 451 + v_windows/v/old/cmd/tools/vcreate.v | 186 + v_windows/v/old/cmd/tools/vcreate_test.v | 79 + v_windows/v/old/cmd/tools/vdoc/html.v | 553 + .../v/old/cmd/tools/vdoc/html_tag_escape_test.v | 6 + v_windows/v/old/cmd/tools/vdoc/markdown.v | 55 + v_windows/v/old/cmd/tools/vdoc/resources/arrow.svg | 1 + .../v/old/cmd/tools/vdoc/resources/dark-mode.js | 6 + v_windows/v/old/cmd/tools/vdoc/resources/dark.svg | 1 + v_windows/v/old/cmd/tools/vdoc/resources/doc.css | 725 + v_windows/v/old/cmd/tools/vdoc/resources/doc.js | 235 + .../resources/favicons/android-chrome-192x192.png | Bin 0 -> 6083 bytes .../resources/favicons/android-chrome-512x512.png | Bin 0 -> 18209 bytes .../vdoc/resources/favicons/apple-touch-icon.png | Bin 0 -> 5707 bytes .../vdoc/resources/favicons/browserconfig.xml | 9 + .../vdoc/resources/favicons/favicon-16x16.png | Bin 0 -> 853 bytes .../vdoc/resources/favicons/favicon-32x32.png | Bin 0 -> 1305 bytes .../cmd/tools/vdoc/resources/favicons/favicon.ico | Bin 0 -> 15086 bytes .../vdoc/resources/favicons/mstile-144x144.png | Bin 0 -> 4512 bytes .../vdoc/resources/favicons/mstile-150x150.png | Bin 0 -> 4360 bytes .../vdoc/resources/favicons/mstile-310x150.png | Bin 0 -> 4927 bytes .../vdoc/resources/favicons/mstile-310x310.png | Bin 0 -> 10195 bytes .../tools/vdoc/resources/favicons/mstile-70x70.png | Bin 0 -> 3093 bytes .../vdoc/resources/favicons/safari-pinned-tab.svg | 39 + .../tools/vdoc/resources/favicons/site.webmanifest | 19 + v_windows/v/old/cmd/tools/vdoc/resources/light.svg | 1 + v_windows/v/old/cmd/tools/vdoc/resources/link.svg | 1 + v_windows/v/old/cmd/tools/vdoc/resources/menu.svg | 1 + .../v/old/cmd/tools/vdoc/resources/normalize.css | 171 + .../tools/vdoc/tests/testdata/project1/main.out | 1 + .../cmd/tools/vdoc/tests/testdata/project1/main.v | 8 + .../v/old/cmd/tools/vdoc/tests/vdoc_file_test.v | 72 + v_windows/v/old/cmd/tools/vdoc/utils.v | 275 + v_windows/v/old/cmd/tools/vdoc/vdoc.v | 511 + v_windows/v/old/cmd/tools/vdoctor.exe | Bin 0 -> 880128 bytes v_windows/v/old/cmd/tools/vdoctor.v | 264 + v_windows/v/old/cmd/tools/vfmt.v | 334 + v_windows/v/old/cmd/tools/vpm.exe | Bin 0 -> 962048 bytes v_windows/v/old/cmd/tools/vpm.v | 601 + v_windows/v/old/cmd/tools/vrepl.exe | Bin 0 -> 498688 bytes v_windows/v/old/cmd/tools/vrepl.v | 390 + v_windows/v/old/cmd/tools/vself.exe | Bin 0 -> 432128 bytes v_windows/v/old/cmd/tools/vself.v | 89 + v_windows/v/old/cmd/tools/vsetup-freetype.v | 28 + v_windows/v/old/cmd/tools/vsymlink.v | 182 + v_windows/v/old/cmd/tools/vtest-all.v | 187 + v_windows/v/old/cmd/tools/vtest-cleancode.v | 102 + v_windows/v/old/cmd/tools/vtest-fmt.v | 43 + v_windows/v/old/cmd/tools/vtest-parser.v | 289 + v_windows/v/old/cmd/tools/vtest-self.v | 220 + v_windows/v/old/cmd/tools/vtest.v | 135 + v_windows/v/old/cmd/tools/vtracev.v | 17 + v_windows/v/old/cmd/tools/vup.exe | Bin 0 -> 1010176 bytes v_windows/v/old/cmd/tools/vup.v | 164 + .../cmd/tools/vvet/tests/array_init_one_val.out | 2 + .../old/cmd/tools/vvet/tests/array_init_one_val.vv | 5 + .../old/cmd/tools/vvet/tests/indent_with_space.out | 6 + .../old/cmd/tools/vvet/tests/indent_with_space.vv | 24 + .../old/cmd/tools/vvet/tests/module_file_test.out | 5 + .../v/old/cmd/tools/vvet/tests/module_file_test.vv | 55 + .../v/old/cmd/tools/vvet/tests/parens_space_a.out | 2 + .../v/old/cmd/tools/vvet/tests/parens_space_a.vv | 4 + .../v/old/cmd/tools/vvet/tests/parens_space_b.out | 2 + .../v/old/cmd/tools/vvet/tests/parens_space_b.vv | 4 + .../v/old/cmd/tools/vvet/tests/trailing_space.out | 7 + .../v/old/cmd/tools/vvet/tests/trailing_space.vv | 16 + v_windows/v/old/cmd/tools/vvet/vet_test.v | 72 + v_windows/v/old/cmd/tools/vvet/vvet.v | 256 + v_windows/v/old/cmd/tools/vwatch.exe | Bin 0 -> 560640 bytes v_windows/v/old/cmd/tools/vwatch.v | 381 + v_windows/v/old/cmd/tools/vwipe-cache.v | 13 + v_windows/v/old/cmd/v/help/ast.txt | 12 + v_windows/v/old/cmd/v/help/bin2v.txt | 14 + v_windows/v/old/cmd/v/help/bug.txt | 8 + v_windows/v/old/cmd/v/help/build-c.txt | 240 + v_windows/v/old/cmd/v/help/build-js.txt | 22 + v_windows/v/old/cmd/v/help/build-native.txt | 17 + v_windows/v/old/cmd/v/help/build.txt | 157 + v_windows/v/old/cmd/v/help/check-md.txt | 22 + v_windows/v/old/cmd/v/help/default.txt | 60 + v_windows/v/old/cmd/v/help/doc.txt | 34 + v_windows/v/old/cmd/v/help/doctor.txt | 3 + v_windows/v/old/cmd/v/help/fmt.txt | 35 + v_windows/v/old/cmd/v/help/help.v | 47 + v_windows/v/old/cmd/v/help/help_test.v | 22 + v_windows/v/old/cmd/v/help/init.txt | 4 + v_windows/v/old/cmd/v/help/install.txt | 10 + v_windows/v/old/cmd/v/help/list.txt | 3 + v_windows/v/old/cmd/v/help/new.txt | 18 + v_windows/v/old/cmd/v/help/other.txt | 33 + v_windows/v/old/cmd/v/help/outdated.txt | 3 + v_windows/v/old/cmd/v/help/remove.txt | 9 + v_windows/v/old/cmd/v/help/repl.txt | 3 + v_windows/v/old/cmd/v/help/run.txt | 13 + v_windows/v/old/cmd/v/help/search.txt | 10 + v_windows/v/old/cmd/v/help/self.txt | 5 + v_windows/v/old/cmd/v/help/show.txt | 3 + v_windows/v/old/cmd/v/help/symlink.txt | 5 + v_windows/v/old/cmd/v/help/test.txt | 18 + v_windows/v/old/cmd/v/help/tracev.txt | 3 + v_windows/v/old/cmd/v/help/up.txt | 7 + v_windows/v/old/cmd/v/help/update.txt | 10 + v_windows/v/old/cmd/v/help/upgrade.txt | 7 + v_windows/v/old/cmd/v/help/version.txt | 3 + v_windows/v/old/cmd/v/help/vet.txt | 18 + v_windows/v/old/cmd/v/help/vpm.txt | 8 + v_windows/v/old/cmd/v/help/watch.txt | 28 + v_windows/v/old/cmd/v/v.v | 146 + v_windows/v/old/doc/docs.md | 5421 ++++ v_windows/v/old/doc/img/vscode-debugger.png | Bin 0 -> 7284 bytes v_windows/v/old/doc/upcoming.md | 195 + v_windows/v/old/doc/vscode.md | 107 + v_windows/v/old/examples/.gitignore | 2 + v_windows/v/old/examples/2048/.gitignore | 2 + v_windows/v/old/examples/2048/2048.v | 939 + v_windows/v/old/examples/2048/LICENSE | 21 + v_windows/v/old/examples/2048/README.md | 25 + v_windows/v/old/examples/2048/demo.png | Bin 0 -> 19426 bytes v_windows/v/old/examples/2048/v.mod | 7 + v_windows/v/old/examples/asm.v | 18 + v_windows/v/old/examples/assets/fonts/LICENSE | 202 + .../examples/assets/fonts/RobotoMono-Regular.ttf | Bin 0 -> 114624 bytes v_windows/v/old/examples/bfs.v | 41 + v_windows/v/old/examples/binary_search_tree.v | 171 + v_windows/v/old/examples/buf_reader.v | 19 + v_windows/v/old/examples/c_interop_wkhtmltopdf.v | 96 + v_windows/v/old/examples/cli.v | 78 + .../v/old/examples/compiletime/compile-time-for.v | 38 + v_windows/v/old/examples/concurrency/concurrency.v | 18 + .../v/old/examples/concurrency/concurrency_http.v | 32 + .../old/examples/concurrency/concurrency_returns.v | 13 + v_windows/v/old/examples/database/mysql.v | 17 + v_windows/v/old/examples/database/orm.v | 260 + v_windows/v/old/examples/database/psql/.gitignore | 1 + v_windows/v/old/examples/database/psql/customer.v | 63 + v_windows/v/old/examples/database/psql/mydb.sql | 122 + v_windows/v/old/examples/database/sqlite.v | 22 + v_windows/v/old/examples/dump_factorial.v | 10 + .../modules/library/library.v | 19 + .../v/old/examples/dynamic_library_loading/use.v | 17 + .../examples/dynamic_library_loading/use_test.v | 56 + v_windows/v/old/examples/errors.v | 20 + v_windows/v/old/examples/eventbus/eventbus.v | 13 + .../eventbus/modules/some_module/some_module.v | 34 + v_windows/v/old/examples/fetch.v | 12 + v_windows/v/old/examples/fibonacci.v | 37 + v_windows/v/old/examples/file_list.v | 18 + v_windows/v/old/examples/fireworks/fireworks.v | 119 + .../old/examples/fireworks/modules/objects/color.v | 12 + .../examples/fireworks/modules/objects/constants.v | 20 + .../examples/fireworks/modules/objects/particle.v | 36 + .../examples/fireworks/modules/objects/rocket.v | 69 + .../examples/fireworks/modules/objects/vector.v | 28 + v_windows/v/old/examples/fizz_buzz.v | 17 + v_windows/v/old/examples/flappylearning/.gitignore | 1 + v_windows/v/old/examples/flappylearning/LICENSE | 21 + v_windows/v/old/examples/flappylearning/README.md | 16 + .../flappylearning/assets/img/background.png | Bin 0 -> 3175 bytes .../examples/flappylearning/assets/img/bird.png | Bin 0 -> 382 bytes .../examples/flappylearning/assets/img/flappy.png | Bin 0 -> 17036 bytes .../flappylearning/assets/img/pipebottom.png | Bin 0 -> 1241 bytes .../examples/flappylearning/assets/img/pipetop.png | Bin 0 -> 1191 bytes v_windows/v/old/examples/flappylearning/game.v | 291 + .../modules/neuroevolution/neuronevolution.v | 287 + v_windows/v/old/examples/function_types.v | 32 + v_windows/v/old/examples/game_of_life/README.md | 10 + v_windows/v/old/examples/game_of_life/life.v | 25 + v_windows/v/old/examples/game_of_life/life_gg.v | 56 + .../game_of_life/modules/automaton/automaton.v | 132 + v_windows/v/old/examples/get_weather/README.md | 23 + v_windows/v/old/examples/get_weather/get_weather.v | 55 + v_windows/v/old/examples/gg/logo.png | Bin 0 -> 4328 bytes v_windows/v/old/examples/gg/polygons.v | 34 + v_windows/v/old/examples/gg/random.v | 63 + v_windows/v/old/examples/gg/raven_text_rendering.v | 104 + v_windows/v/old/examples/gg/rectangles.v | 52 + v_windows/v/old/examples/gg/stars.v | 134 + v_windows/v/old/examples/gg/worker_thread.v | 90 + v_windows/v/old/examples/hanoi.v | 22 + v_windows/v/old/examples/hello_v_js.v | 5 + v_windows/v/old/examples/hello_world.v | 1 + v_windows/v/old/examples/hot_reload/.gitignore | 1 + v_windows/v/old/examples/hot_reload/bounce.v | 84 + v_windows/v/old/examples/hot_reload/graph.v | 86 + v_windows/v/old/examples/hot_reload/message.v | 18 + v_windows/v/old/examples/json.v | 40 + v_windows/v/old/examples/lander.v | 62 + .../linear_regression/simple_linear_regression.v | 55 + v_windows/v/old/examples/links_scraper.v | 14 + v_windows/v/old/examples/log.v | 18 + v_windows/v/old/examples/mini_calculator.v | 134 + v_windows/v/old/examples/native/hello_world.v | 20 + v_windows/v/old/examples/nbody.v | 129 + v_windows/v/old/examples/net_peer_ip.v | 5 + v_windows/v/old/examples/net_raw_http.v | 20 + v_windows/v/old/examples/net_resolve.v | 24 + v_windows/v/old/examples/net_t.v | 21 + .../v/old/examples/net_udp_server_and_client.v | 44 + v_windows/v/old/examples/news_fetcher.v | 49 + v_windows/v/old/examples/path_tracing.v | 583 + v_windows/v/old/examples/pendulum_sim/sim.v | 366 + v_windows/v/old/examples/pico/pico.v | 52 + v_windows/v/old/examples/process/.ignore | 1 + v_windows/v/old/examples/process/command.v | 34 + v_windows/v/old/examples/process/execve.v | 17 + v_windows/v/old/examples/process/process_script.v | 59 + .../v/old/examples/process/process_stdin_trick.v | 83 + v_windows/v/old/examples/quick_sort.v | 42 + v_windows/v/old/examples/random_ips.v | 7 + v_windows/v/old/examples/regex/pcre.vv | 69 + v_windows/v/old/examples/regex/readme.md | 8 + v_windows/v/old/examples/regex/regex_example.v | 80 + .../v/old/examples/regex/regex_with_memoization.v | 127 + v_windows/v/old/examples/rune.v | 9 + v_windows/v/old/examples/smtp/mail.v | 36 + v_windows/v/old/examples/snek/snek.v | 221 + v_windows/v/old/examples/sokol/01_cubes/cube.v | 432 + .../examples/sokol/02_cubes_glsl/cube_glsl.glsl | 95 + .../v/old/examples/sokol/02_cubes_glsl/cube_glsl.v | 627 + v_windows/v/old/examples/sokol/02_cubes_glsl/v.mod | 0 .../sokol/03_march_tracing_glsl/rt_glsl.glsl | 695 + .../examples/sokol/03_march_tracing_glsl/rt_glsl.v | 438 + .../old/examples/sokol/03_march_tracing_glsl/v.mod | 0 .../examples/sokol/04_multi_shader_glsl/rt_glsl.v | 634 + .../sokol/04_multi_shader_glsl/rt_glsl_march.glsl | 695 + .../sokol/04_multi_shader_glsl/rt_glsl_puppy.glsl | 568 + .../old/examples/sokol/04_multi_shader_glsl/v.mod | 0 .../examples/sokol/05_instancing_glsl/rt_glsl.v | 521 + .../05_instancing_glsl/rt_glsl_instancing.glsl | 64 + .../v/old/examples/sokol/05_instancing_glsl/v.mod | 0 .../sokol/06_obj_viewer/assets/models/v.mtl | 20 + .../sokol/06_obj_viewer/assets/models/v.obj_ | 194 + .../old/examples/sokol/06_obj_viewer/gouraud.glsl | 108 + .../examples/sokol/06_obj_viewer/modules/obj/obj.v | 595 + .../sokol/06_obj_viewer/modules/obj/rend.v | 297 + .../sokol/06_obj_viewer/modules/obj/struct.v | 104 + .../sokol/06_obj_viewer/modules/obj/util.v | 44 + .../v/old/examples/sokol/06_obj_viewer/show_obj.v | 338 + v_windows/v/old/examples/sokol/06_obj_viewer/v.mod | 0 v_windows/v/old/examples/sokol/drawing.v | 75 + v_windows/v/old/examples/sokol/fonts.v | 164 + v_windows/v/old/examples/sokol/freetype_raven.v | 153 + .../sokol/particles/modules/particle/LICENSE | 21 + .../sokol/particles/modules/particle/color.v | 12 + .../sokol/particles/modules/particle/particle.v | 81 + .../sokol/particles/modules/particle/system.v | 99 + .../sokol/particles/modules/particle/v.mod | 0 .../sokol/particles/modules/particle/vec2/v.mod | 0 .../sokol/particles/modules/particle/vec2/vec2.v | 89 + .../v/old/examples/sokol/particles/particles.v | 155 + v_windows/v/old/examples/sokol/sounds/melody.v | 75 + .../v/old/examples/sokol/sounds/simple_sin_tones.v | 42 + v_windows/v/old/examples/sokol/sounds/uhoh.wav | Bin 0 -> 68562 bytes v_windows/v/old/examples/sokol/sounds/wav_player.v | 209 + v_windows/v/old/examples/spectral.v | 68 + v_windows/v/old/examples/submodule/main.v | 7 + .../examples/submodule/mymodules/main_functions.v | 5 + .../submodule/mymodules/submodule/sub_functions.v | 5 + v_windows/v/old/examples/tcp_echo_server.v | 40 + v_windows/v/old/examples/tcp_notify_echo_server.v | 73 + v_windows/v/old/examples/templates/data.json | 197 + v_windows/v/old/examples/templates/readme.md | 14 + v_windows/v/old/examples/templates/template.md | 16 + v_windows/v/old/examples/templates/templates.v | 196 + v_windows/v/old/examples/term.ui/README.md | 1 + v_windows/v/old/examples/term.ui/cursor_chaser.v | 100 + v_windows/v/old/examples/term.ui/event_viewer.v | 47 + v_windows/v/old/examples/term.ui/pong.v | 499 + v_windows/v/old/examples/term.ui/rectangles.v | 97 + .../v/old/examples/term.ui/screenshot_pong.png | Bin 0 -> 1054 bytes v_windows/v/old/examples/term.ui/term_drawing.v | 510 + v_windows/v/old/examples/term.ui/text_editor.v | 583 + v_windows/v/old/examples/term.ui/vyper.v | 475 + v_windows/v/old/examples/terminal_control.v | 36 + v_windows/v/old/examples/tetris/README.md | 9 + v_windows/v/old/examples/tetris/screenshot.png | Bin 0 -> 23598 bytes v_windows/v/old/examples/tetris/tetris.v | 518 + v_windows/v/old/examples/tree_of_nodes.v | 27 + .../v/old/examples/ttf_font/Graduate-Regular.ttf | Bin 0 -> 21292 bytes .../v/old/examples/ttf_font/Imprima-Regular.ttf | Bin 0 -> 23188 bytes v_windows/v/old/examples/ttf_font/OFL.txt | 93 + v_windows/v/old/examples/ttf_font/example_ttf.v | 171 + v_windows/v/old/examples/v_script.vsh | 32 + v_windows/v/old/examples/vcasino/README.md | 17 + v_windows/v/old/examples/vcasino/vcasino.v | 146 + v_windows/v/old/examples/vmod.v | 9 + v_windows/v/old/examples/vpwgen.v | 25 + .../v/old/examples/vweb/file_upload/index.html | 6 + .../v/old/examples/vweb/file_upload/upload.html | 14 + .../v/old/examples/vweb/file_upload/vweb_example.v | 32 + v_windows/v/old/examples/vweb/index.html | 15 + .../vweb/server_sent_events/assets/site.css | 19 + .../vweb/server_sent_events/assets/v-logo.svg | 1 + .../examples/vweb/server_sent_events/favicon.ico | Bin 0 -> 15406 bytes .../examples/vweb/server_sent_events/index.html | 38 + .../old/examples/vweb/server_sent_events/server.v | 39 + .../old/examples/vweb/vweb_assets/assets/index.css | 19 + .../examples/vweb/vweb_assets/assets/v-logo.svg | 1 + .../v/old/examples/vweb/vweb_assets/favicon.ico | Bin 0 -> 15406 bytes .../v/old/examples/vweb/vweb_assets/index.html | 12 + .../v/old/examples/vweb/vweb_assets/vweb_assets.v | 40 + v_windows/v/old/examples/vweb/vweb_example.v | 58 + v_windows/v/old/examples/web_crawler/README.md | 22 + v_windows/v/old/examples/web_crawler/web_crawler.v | 24 + .../old/examples/websocket/client-server/client.v | 54 + .../old/examples/websocket/client-server/server.v | 43 + v_windows/v/old/examples/websocket/ping.v | 86 + v_windows/v/old/examples/word_counter/README.md | 25 + .../v/old/examples/word_counter/cinderella.txt | 250 + .../v/old/examples/word_counter/word_counter.v | 70 + v_windows/v/old/thirdparty/.gitignore | 11 + v_windows/v/old/thirdparty/bignum/README.md | 2 + v_windows/v/old/thirdparty/bignum/bn.c | 664 + v_windows/v/old/thirdparty/bignum/bn.h | 123 + v_windows/v/old/thirdparty/cJSON/cJSON.c | 2973 ++ v_windows/v/old/thirdparty/cJSON/cJSON.h | 285 + v_windows/v/old/thirdparty/cJSON/readme.txt | 7 + v_windows/v/old/thirdparty/fontstash/fontstash.h | 1742 ++ .../v/old/thirdparty/fontstash/stb_truetype.h | 5011 +++ v_windows/v/old/thirdparty/ios/ios.m | 8 + v_windows/v/old/thirdparty/libgc/amalgamation.txt | 7 + v_windows/v/old/thirdparty/libgc/gc.c | 30266 +++++++++++++++++++ v_windows/v/old/thirdparty/libgc/gc.h | 1081 + .../v/old/thirdparty/mssql/include/.gitignore | 4 + v_windows/v/old/thirdparty/mssql/include/mssql.h | 20 + v_windows/v/old/thirdparty/picoev/picoev.c | 9 + v_windows/v/old/thirdparty/picoev/src/README.md | 21 + v_windows/v/old/thirdparty/picoev/src/picoev.h | 404 + .../v/old/thirdparty/picoev/src/picoev_epoll.c | 168 + .../v/old/thirdparty/picoev/src/picoev_kqueue.c | 209 + .../v/old/thirdparty/picoev/src/picoev_select.c | 169 + v_windows/v/old/thirdparty/picoev/src/picoev_w32.h | 15 + .../old/thirdparty/picohttpparser/picohttpparser.c | 28 + .../old/thirdparty/picohttpparser/picohttpparser.h | 112 + .../v/old/thirdparty/picohttpparser/src/README.md | 116 + .../thirdparty/picohttpparser/src/picohttpparser.c | 645 + .../thirdparty/picohttpparser/src/picohttpparser.h | 87 + v_windows/v/old/thirdparty/sokol/sokol_app.h | 10475 +++++++ v_windows/v/old/thirdparty/sokol/sokol_app2.h | 40 + v_windows/v/old/thirdparty/sokol/sokol_audio.h | 2147 ++ v_windows/v/old/thirdparty/sokol/sokol_gfx.h | 15672 ++++++++++ v_windows/v/old/thirdparty/sokol/sokol_v.h | 20 + .../v/old/thirdparty/sokol/util/sokol_fontstash.h | 1785 ++ v_windows/v/old/thirdparty/sokol/util/sokol_gl.h | 3297 ++ v_windows/v/old/thirdparty/stb_image/stb_image.h | 7766 +++++ v_windows/v/old/thirdparty/stb_image/stbi.c | 3 + v_windows/v/old/thirdparty/stdatomic/nix/atomic.h | 435 + .../v/old/thirdparty/stdatomic/nix/atomic_cpp.h | 541 + v_windows/v/old/thirdparty/stdatomic/win/atomic.h | 366 + v_windows/v/old/thirdparty/vschannel/vschannel.c | 1095 + v_windows/v/old/thirdparty/vschannel/vschannel.h | 47 + v_windows/v/old/thirdparty/zip/miniz.h | 6859 +++++ v_windows/v/old/thirdparty/zip/zip.c | 1034 + v_windows/v/old/thirdparty/zip/zip.h | 345 + .../building_a_simple_web_blog_with_vweb/README.md | 446 + .../code/blog/.gitignore | 1 + .../code/blog/article.v | 13 + .../code/blog/blog.sqlite | 17 + .../code/blog/blog.v | 83 + .../code/blog/index.html | 16 + .../code/blog/new.html | 13 + .../img/articles1.png | Bin 0 -> 105501 bytes .../img/articles_json.png | Bin 0 -> 110081 bytes .../img/hello.png | Bin 0 -> 92436 bytes .../img/hello_html.png | Bin 0 -> 98295 bytes .../img/time.png | Bin 0 -> 91776 bytes v_windows/v/old/v.def | 5 + v_windows/v/old/v.exe | Bin 0 -> 6958592 bytes v_windows/v/old/v.mod | 8 + v_windows/v/old/v_old.exe | Bin 0 -> 7165952 bytes v_windows/v/old/vc | 1 + v_windows/v/old/vlib/.vdocignore | 15 + v_windows/v/old/vlib/arrays/arrays.v | 126 + v_windows/v/old/vlib/arrays/arrays_test.v | 87 + v_windows/v/old/vlib/benchmark/README.md | 45 + v_windows/v/old/vlib/benchmark/benchmark.v | 218 + v_windows/v/old/vlib/bitfield/README.md | 11 + v_windows/v/old/vlib/bitfield/bitfield.v | 569 + v_windows/v/old/vlib/bitfield/bitfield_test.v | 333 + v_windows/v/old/vlib/builtin/array.v | 664 + v_windows/v/old/vlib/builtin/array_d_gcboehm_opt.v | 268 + .../v/old/vlib/builtin/array_notd_gcboehm_opt.v | 10 + v_windows/v/old/vlib/builtin/array_test.v | 1469 + v_windows/v/old/vlib/builtin/builtin.c.v | 527 + v_windows/v/old/vlib/builtin/builtin.v | 131 + v_windows/v/old/vlib/builtin/builtin_d_gcboehm.v | 91 + v_windows/v/old/vlib/builtin/builtin_ios.c.v | 6 + v_windows/v/old/vlib/builtin/builtin_nix.c.v | 144 + .../v/old/vlib/builtin/builtin_notd_gcboehm.v | 20 + v_windows/v/old/vlib/builtin/builtin_windows.c.v | 304 + v_windows/v/old/vlib/builtin/byte_test.v | 22 + v_windows/v/old/vlib/builtin/cfns.c.v | 462 + v_windows/v/old/vlib/builtin/chan.v | 32 + v_windows/v/old/vlib/builtin/float.v | 205 + v_windows/v/old/vlib/builtin/float_test.v | 147 + v_windows/v/old/vlib/builtin/float_x64.v | 6 + v_windows/v/old/vlib/builtin/int.v | 479 + v_windows/v/old/vlib/builtin/int_test.v | 230 + v_windows/v/old/vlib/builtin/isnil_test.v | 19 + v_windows/v/old/vlib/builtin/js/array.js.v | 195 + v_windows/v/old/vlib/builtin/js/builtin.js.v | 6 + v_windows/v/old/vlib/builtin/js/builtin.v | 96 + v_windows/v/old/vlib/builtin/js/byte.js.v | 8 + v_windows/v/old/vlib/builtin/js/int.js.v | 8 + v_windows/v/old/vlib/builtin/js/jsfns.js.v | 125 + v_windows/v/old/vlib/builtin/js/jsfns_browser.js.v | 59 + v_windows/v/old/vlib/builtin/js/jsfns_node.js.v | 31 + v_windows/v/old/vlib/builtin/js/map.js.v | 16 + v_windows/v/old/vlib/builtin/js/string.js.v | 466 + .../v/old/vlib/builtin/linux_bare/libc_impl.v | 157 + .../v/old/vlib/builtin/linux_bare/linux_syscalls.v | 433 + .../old/vlib/builtin/linux_bare/memory_managment.v | 29 + .../vlib/builtin/linux_bare/old/.checks/.gitignore | 5 + .../vlib/builtin/linux_bare/old/.checks/checks.v | 32 + .../builtin/linux_bare/old/.checks/consts/consts.v | 22 + .../linux_bare/old/.checks/forkedtest/forkedtest.v | 49 + .../linux_bare/old/.checks/linuxsys/linuxsys.v | 300 + .../vlib/builtin/linux_bare/old/.checks/readme.md | 5 + .../linux_bare/old/.checks/sample_text1.txt | 1 + .../builtin/linux_bare/old/.checks/string/string.v | 63 + .../linux_bare/old/.checks/structs/structs.v | 42 + .../v/old/vlib/builtin/linux_bare/old/array_bare.v | 53 + .../old/vlib/builtin/linux_bare/old/builtin_bare.v | 60 + .../vlib/builtin/linux_bare/old/linuxsys_bare.v | 759 + .../v/old/vlib/builtin/linux_bare/old/mm_bare.v | 58 + .../old/vlib/builtin/linux_bare/old/string_bare.v | 150 + .../builtin/linux_bare/old/syscallwrapper_test.v | 27 + v_windows/v/old/vlib/builtin/map.v | 794 + v_windows/v/old/vlib/builtin/map_d_gcboehm_opt.v | 146 + v_windows/v/old/vlib/builtin/map_of_floats_test.v | 27 + v_windows/v/old/vlib/builtin/map_test.v | 947 + v_windows/v/old/vlib/builtin/option.v | 103 + v_windows/v/old/vlib/builtin/prealloc.c.v | 114 + v_windows/v/old/vlib/builtin/rune.v | 62 + v_windows/v/old/vlib/builtin/sorted_map.v | 457 + v_windows/v/old/vlib/builtin/sorting_test.v | 84 + v_windows/v/old/vlib/builtin/string.v | 1640 + .../vlib/builtin/string_charptr_byteptr_helpers.v | 104 + v_windows/v/old/vlib/builtin/string_int_test.v | 221 + .../v/old/vlib/builtin/string_interpolation.v | 713 + .../v/old/vlib/builtin/string_strip_margin_test.v | 95 + v_windows/v/old/vlib/builtin/string_test.v | 912 + v_windows/v/old/vlib/builtin/utf8.c.v | 79 + v_windows/v/old/vlib/builtin/utf8.v | 188 + v_windows/v/old/vlib/builtin/utf8_test.v | 28 + v_windows/v/old/vlib/cli/README.md | 30 + v_windows/v/old/vlib/cli/command.v | 305 + v_windows/v/old/vlib/cli/command_test.v | 221 + v_windows/v/old/vlib/cli/flag.v | 310 + v_windows/v/old/vlib/cli/flag_test.v | 216 + v_windows/v/old/vlib/cli/help.v | 172 + v_windows/v/old/vlib/cli/version.v | 25 + v_windows/v/old/vlib/clipboard/clipboard.v | 37 + .../v/old/vlib/clipboard/clipboard_android.c.v | 15 + .../v/old/vlib/clipboard/clipboard_darwin.c.v | 70 + v_windows/v/old/vlib/clipboard/clipboard_darwin.m | 23 + .../v/old/vlib/clipboard/clipboard_default.c.v | 15 + .../v/old/vlib/clipboard/clipboard_solaris.c.v | 15 + v_windows/v/old/vlib/clipboard/clipboard_test.v | 29 + .../v/old/vlib/clipboard/clipboard_windows.c.v | 186 + .../v/old/vlib/clipboard/dummy/dummy_clipboard.v | 49 + v_windows/v/old/vlib/clipboard/x11/clipboard.c.v | 501 + v_windows/v/old/vlib/context/README.md | 166 + v_windows/v/old/vlib/context/_context.v | 81 + v_windows/v/old/vlib/context/cancel.v | 181 + v_windows/v/old/vlib/context/cancel_test.v | 42 + v_windows/v/old/vlib/context/deadline.v | 94 + v_windows/v/old/vlib/context/deadline_test.v | 48 + v_windows/v/old/vlib/context/empty.v | 42 + v_windows/v/old/vlib/context/empty_test.v | 17 + v_windows/v/old/vlib/context/err.v | 12 + v_windows/v/old/vlib/context/value.v | 57 + v_windows/v/old/vlib/context/value_test.v | 23 + v_windows/v/old/vlib/crypto/aes/aes.v | 77 + v_windows/v/old/vlib/crypto/aes/aes_cbc.v | 126 + v_windows/v/old/vlib/crypto/aes/aes_test.v | 27 + v_windows/v/old/vlib/crypto/aes/block_generic.v | 183 + v_windows/v/old/vlib/crypto/aes/const.v | 374 + v_windows/v/old/vlib/crypto/aes/cypher_generic.v | 16 + v_windows/v/old/vlib/crypto/cipher/xor_generic.v | 33 + v_windows/v/old/vlib/crypto/crypto.v | 23 + v_windows/v/old/vlib/crypto/hmac/hmac.v | 44 + v_windows/v/old/vlib/crypto/hmac/hmac_test.v | 226 + .../v/old/vlib/crypto/internal/subtle/aliasing.v | 29 + .../v/old/vlib/crypto/internal/subtle/comparison.v | 53 + .../vlib/crypto/internal/subtle/comparison_test.v | 65 + v_windows/v/old/vlib/crypto/md5/md5.v | 154 + v_windows/v/old/vlib/crypto/md5/md5_test.v | 8 + v_windows/v/old/vlib/crypto/md5/md5block_generic.v | 132 + .../v/old/vlib/crypto/rand/crypto_rand_read_test.v | 15 + v_windows/v/old/vlib/crypto/rand/rand.v | 10 + v_windows/v/old/vlib/crypto/rand/rand_darwin.c.v | 21 + v_windows/v/old/vlib/crypto/rand/rand_default.c.v | 9 + v_windows/v/old/vlib/crypto/rand/rand_linux.c.v | 39 + v_windows/v/old/vlib/crypto/rand/rand_solaris.c.v | 42 + v_windows/v/old/vlib/crypto/rand/rand_windows.c.v | 25 + v_windows/v/old/vlib/crypto/rand/utils.v | 55 + v_windows/v/old/vlib/crypto/rc4/rc4.v | 79 + v_windows/v/old/vlib/crypto/rc4/rc4_test.v | 22 + v_windows/v/old/vlib/crypto/readme.txt | 29 + v_windows/v/old/vlib/crypto/sha1/sha1.v | 155 + v_windows/v/old/vlib/crypto/sha1/sha1_test.v | 8 + .../v/old/vlib/crypto/sha1/sha1block_generic.v | 118 + v_windows/v/old/vlib/crypto/sha256/sha256.v | 226 + v_windows/v/old/vlib/crypto/sha256/sha256_test.v | 16 + .../v/old/vlib/crypto/sha256/sha256block_generic.v | 154 + v_windows/v/old/vlib/crypto/sha512/sha512.v | 324 + v_windows/v/old/vlib/crypto/sha512/sha512_test.v | 8 + .../v/old/vlib/crypto/sha512/sha512block_generic.v | 108 + v_windows/v/old/vlib/darwin/darwin.m | 9 + v_windows/v/old/vlib/darwin/darwin.v | 57 + v_windows/v/old/vlib/dl/dl.v | 48 + v_windows/v/old/vlib/dl/dl_nix.c.v | 43 + v_windows/v/old/vlib/dl/dl_test.v | 46 + v_windows/v/old/vlib/dl/dl_windows.c.v | 39 + v_windows/v/old/vlib/encoding/base64/base64.v | 224 + .../old/vlib/encoding/base64/base64_memory_test.v | 32 + v_windows/v/old/vlib/encoding/base64/base64_test.v | 132 + v_windows/v/old/vlib/encoding/binary/binary.v | 100 + v_windows/v/old/vlib/encoding/csv/README.md | 19 + v_windows/v/old/vlib/encoding/csv/reader.v | 196 + v_windows/v/old/vlib/encoding/csv/reader_test.v | 253 + v_windows/v/old/vlib/encoding/csv/writer.v | 80 + v_windows/v/old/vlib/encoding/csv/writer_test.v | 11 + .../encoding/utf8/east_asian/east_asian_width.v | 1204 + .../utf8/east_asian/east_asian_width_test.v | 23 + .../v/old/vlib/encoding/utf8/encoding_utf8_test.v | 9 + v_windows/v/old/vlib/encoding/utf8/utf8.v | 88 + v_windows/v/old/vlib/encoding/utf8/utf8_util.v | 1161 + .../v/old/vlib/encoding/utf8/utf8_util_test.v | 66 + v_windows/v/old/vlib/eventbus/README.md | 122 + v_windows/v/old/vlib/eventbus/eventbus.v | 121 + v_windows/v/old/vlib/eventbus/eventbus_test.v | 109 + v_windows/v/old/vlib/flag/README.md | 36 + .../v/old/vlib/flag/default_flag_options_test.v | 35 + v_windows/v/old/vlib/flag/flag.v | 624 + v_windows/v/old/vlib/flag/flag_test.v | 412 + .../simplest_flag_program.dashdash.help.out | 1 + .../simplest_flag_program.dashdash.version.out | 1 + .../flag/testdata/simplest_flag_program.help.out | 7 + .../vlib/flag/testdata/simplest_flag_program.out | 1 + .../old/vlib/flag/testdata/simplest_flag_program.v | 14 + .../testdata/simplest_flag_program.version.out | 1 + .../old/vlib/flag/testdata/usage_example.help.out | 13 + .../v/old/vlib/flag/testdata/usage_example.out | 1 + v_windows/v/old/vlib/flag/testdata/usage_example.v | 17 + .../vlib/flag/testdata/usage_example.version.out | 1 + v_windows/v/old/vlib/flag/usage_example_test.v | 35 + v_windows/v/old/vlib/fontstash/a_d_use_freetype.v | 19 + v_windows/v/old/vlib/fontstash/fontstash.v | 172 + v_windows/v/old/vlib/fontstash/fontstash_funcs.v | 53 + v_windows/v/old/vlib/fontstash/fontstash_structs.v | 80 + v_windows/v/old/vlib/gg/enums.v | 161 + v_windows/v/old/vlib/gg/gg.v | 946 + v_windows/v/old/vlib/gg/gg_android.c.v | 37 + v_windows/v/old/vlib/gg/gg_darwin.c.v | 21 + v_windows/v/old/vlib/gg/gg_darwin.m | 126 + v_windows/v/old/vlib/gg/image.v | 384 + v_windows/v/old/vlib/gg/m4/graphic.v | 110 + v_windows/v/old/vlib/gg/m4/m4_test.v | 235 + v_windows/v/old/vlib/gg/m4/matrix.v | 595 + v_windows/v/old/vlib/gg/m4/vector.v | 230 + v_windows/v/old/vlib/gg/text_rendering.v | 370 + v_windows/v/old/vlib/glm/glm.v | 428 + v_windows/v/old/vlib/glm/glm_test.v | 155 + v_windows/v/old/vlib/gx/color.v | 234 + v_windows/v/old/vlib/gx/color_test.v | 63 + v_windows/v/old/vlib/gx/image.v | 14 + v_windows/v/old/vlib/gx/text.v | 39 + v_windows/v/old/vlib/hash/crc32/crc32.v | 63 + v_windows/v/old/vlib/hash/crc32/crc32_test.v | 14 + v_windows/v/old/vlib/hash/fnv1a/fnv1a.v | 44 + v_windows/v/old/vlib/hash/fnv1a/fnv1a_test.v | 12 + v_windows/v/old/vlib/hash/hash.v | 20 + v_windows/v/old/vlib/hash/wyhash.c.v | 23 + v_windows/v/old/vlib/hash/wyhash.v | 82 + v_windows/v/old/vlib/io/buffered_reader.v | 145 + .../v/old/vlib/io/custom_string_reading_test.v | 57 + v_windows/v/old/vlib/io/io.v | 16 + v_windows/v/old/vlib/io/io_cp_test.v | 13 + v_windows/v/old/vlib/io/io_test.v | 41 + v_windows/v/old/vlib/io/multi_writer.v | 33 + v_windows/v/old/vlib/io/multi_writer_test.v | 66 + v_windows/v/old/vlib/io/os_file_reader_test.v | 29 + v_windows/v/old/vlib/io/reader.v | 76 + v_windows/v/old/vlib/io/reader_test.v | 130 + v_windows/v/old/vlib/io/readerwriter.v | 34 + v_windows/v/old/vlib/io/util/util.v | 104 + v_windows/v/old/vlib/io/util/util_test.v | 127 + v_windows/v/old/vlib/io/writer.v | 12 + v_windows/v/old/vlib/json/json_decode_test.v | 35 + v_windows/v/old/vlib/json/json_primitives.v | 204 + v_windows/v/old/vlib/json/json_test.v | 358 + v_windows/v/old/vlib/log/log.v | 185 + v_windows/v/old/vlib/math/big/big.v | 322 + v_windows/v/old/vlib/math/big/big_test.v | 167 + v_windows/v/old/vlib/math/bits.v | 59 + v_windows/v/old/vlib/math/bits/bits.v | 478 + v_windows/v/old/vlib/math/bits/bits_tables.v | 79 + v_windows/v/old/vlib/math/bits/bits_test.v | 288 + v_windows/v/old/vlib/math/complex/complex.v | 374 + v_windows/v/old/vlib/math/complex/complex_test.v | 797 + v_windows/v/old/vlib/math/const.v | 49 + v_windows/v/old/vlib/math/factorial/factorial.v | 80 + .../v/old/vlib/math/factorial/factorial_tables.v | 375 + .../v/old/vlib/math/factorial/factorial_test.v | 14 + .../v/old/vlib/math/fractions/approximations.v | 119 + .../old/vlib/math/fractions/approximations_test.v | 189 + v_windows/v/old/vlib/math/fractions/fraction.v | 259 + .../v/old/vlib/math/fractions/fraction_test.v | 269 + v_windows/v/old/vlib/math/math.c.v | 296 + v_windows/v/old/vlib/math/math.js.v | 260 + v_windows/v/old/vlib/math/math.v | 148 + v_windows/v/old/vlib/math/math_test.v | 806 + v_windows/v/old/vlib/math/mathutil/mathutil.v | 19 + v_windows/v/old/vlib/math/mathutil/mathutil_test.v | 22 + v_windows/v/old/vlib/math/stats/stats.v | 249 + v_windows/v/old/vlib/math/stats/stats_test.v | 269 + v_windows/v/old/vlib/math/unsafe.v | 38 + v_windows/v/old/vlib/math/util/util.v | 82 + v_windows/v/old/vlib/mssql/README.md | 69 + v_windows/v/old/vlib/mssql/_cdef_nix.c.v | 6 + v_windows/v/old/vlib/mssql/_cdef_windows.c.v | 12 + v_windows/v/old/vlib/mssql/_cdefs.c.v | 27 + v_windows/v/old/vlib/mssql/config.v | 20 + v_windows/v/old/vlib/mssql/mssql.v | 125 + v_windows/v/old/vlib/mssql/result.v | 13 + v_windows/v/old/vlib/mssql/stmt_handle.v | 127 + v_windows/v/old/vlib/mysql/README.md | 30 + v_windows/v/old/vlib/mysql/_cdefs.c.v | 101 + v_windows/v/old/vlib/mysql/_cdefs_nix.c.v | 11 + v_windows/v/old/vlib/mysql/_cdefs_windows.c.v | 5 + v_windows/v/old/vlib/mysql/consts.v | 13 + v_windows/v/old/vlib/mysql/enums.v | 78 + v_windows/v/old/vlib/mysql/mysql.v | 239 + v_windows/v/old/vlib/mysql/mysql_orm_test.v | 77 + v_windows/v/old/vlib/mysql/orm.v | 296 + v_windows/v/old/vlib/mysql/result.v | 153 + v_windows/v/old/vlib/mysql/stmt.c.v | 233 + v_windows/v/old/vlib/mysql/utils.v | 26 + v_windows/v/old/vlib/net/aasocket.c.v | 104 + v_windows/v/old/vlib/net/address.v | 258 + v_windows/v/old/vlib/net/address_darwin.c.v | 74 + v_windows/v/old/vlib/net/address_default.c.v | 32 + v_windows/v/old/vlib/net/address_freebsd.c.v | 77 + v_windows/v/old/vlib/net/address_linux.c.v | 63 + v_windows/v/old/vlib/net/address_test.v | 98 + v_windows/v/old/vlib/net/address_windows.c.v | 58 + v_windows/v/old/vlib/net/afunix.h | 26 + v_windows/v/old/vlib/net/common.v | 129 + v_windows/v/old/vlib/net/conv/conv.c.v | 21 + v_windows/v/old/vlib/net/conv/conv_default.c.v | 46 + v_windows/v/old/vlib/net/conv/conv_windows.c.v | 21 + v_windows/v/old/vlib/net/errors.v | 70 + v_windows/v/old/vlib/net/ftp/ftp.v | 265 + v_windows/v/old/vlib/net/ftp/ftp_test.v | 50 + v_windows/v/old/vlib/net/html/README.md | 16 + v_windows/v/old/vlib/net/html/data_structures.v | 91 + v_windows/v/old/vlib/net/html/dom.v | 189 + v_windows/v/old/vlib/net/html/dom_test.v | 56 + v_windows/v/old/vlib/net/html/html.v | 18 + v_windows/v/old/vlib/net/html/html_test.v | 15 + v_windows/v/old/vlib/net/html/parser.v | 260 + v_windows/v/old/vlib/net/html/parser_test.v | 41 + v_windows/v/old/vlib/net/html/tag.v | 68 + v_windows/v/old/vlib/net/http/backend_nix.c.v | 74 + v_windows/v/old/vlib/net/http/backend_windows.c.v | 28 + v_windows/v/old/vlib/net/http/chunked/dechunk.v | 72 + v_windows/v/old/vlib/net/http/cookie.v | 413 + v_windows/v/old/vlib/net/http/cookie_test.v | 468 + v_windows/v/old/vlib/net/http/download.v | 18 + v_windows/v/old/vlib/net/http/download_nix.c.v | 52 + v_windows/v/old/vlib/net/http/download_windows.c.v | 29 + v_windows/v/old/vlib/net/http/header.v | 700 + v_windows/v/old/vlib/net/http/header_test.v | 361 + v_windows/v/old/vlib/net/http/http.v | 175 + v_windows/v/old/vlib/net/http/http_httpbin_test.v | 95 + v_windows/v/old/vlib/net/http/http_test.v | 56 + v_windows/v/old/vlib/net/http/method.v | 48 + v_windows/v/old/vlib/net/http/request.v | 324 + v_windows/v/old/vlib/net/http/request_test.v | 138 + v_windows/v/old/vlib/net/http/response.v | 152 + v_windows/v/old/vlib/net/http/server.v | 80 + v_windows/v/old/vlib/net/http/status.v | 255 + v_windows/v/old/vlib/net/http/status_test.v | 49 + v_windows/v/old/vlib/net/http/version.v | 40 + v_windows/v/old/vlib/net/ipv6_v6only.h | 5 + v_windows/v/old/vlib/net/net_nix.c.v | 26 + v_windows/v/old/vlib/net/net_windows.c.v | 780 + v_windows/v/old/vlib/net/openssl/c.v | 120 + v_windows/v/old/vlib/net/openssl/openssl.v | 32 + v_windows/v/old/vlib/net/openssl/ssl_connection.v | 268 + v_windows/v/old/vlib/net/smtp/smtp.v | 190 + v_windows/v/old/vlib/net/smtp/smtp_test.v | 89 + v_windows/v/old/vlib/net/socket_options.c.v | 50 + v_windows/v/old/vlib/net/tcp.v | 381 + v_windows/v/old/vlib/net/tcp_read_line.v | 55 + .../v/old/vlib/net/tcp_simple_client_server_test.v | 150 + v_windows/v/old/vlib/net/tcp_test.v | 100 + v_windows/v/old/vlib/net/udp.v | 287 + v_windows/v/old/vlib/net/udp_test.v | 67 + v_windows/v/old/vlib/net/unix/aasocket.c.v | 104 + v_windows/v/old/vlib/net/unix/common.v | 128 + v_windows/v/old/vlib/net/unix/stream_nix.v | 284 + v_windows/v/old/vlib/net/unix/unix_test.v | 57 + v_windows/v/old/vlib/net/urllib/urllib.v | 1095 + v_windows/v/old/vlib/net/urllib/urllib_test.v | 51 + v_windows/v/old/vlib/net/urllib/values.v | 87 + v_windows/v/old/vlib/net/util.v | 27 + v_windows/v/old/vlib/net/websocket/events.v | 227 + v_windows/v/old/vlib/net/websocket/handshake.v | 185 + v_windows/v/old/vlib/net/websocket/io.v | 100 + v_windows/v/old/vlib/net/websocket/message.v | 295 + .../vlib/net/websocket/tests/autobahn/README.md | 20 + .../net/websocket/tests/autobahn/autobahn_client.v | 33 + .../websocket/tests/autobahn/autobahn_client_wss.v | 35 + .../net/websocket/tests/autobahn/autobahn_server.v | 27 + .../websocket/tests/autobahn/docker-compose.yml | 21 + .../tests/autobahn/fuzzing_server/Dockerfile | 5 + .../tests/autobahn/fuzzing_server/check_results.py | 46 + .../fuzzing_server/config/fuzzingclient.json | 22 + .../fuzzing_server/config/fuzzingserver.json | 14 + .../tests/autobahn/fuzzing_server_wss/Dockerfile | 9 + .../autobahn/fuzzing_server_wss/check_results.py | 35 + .../fuzzing_server_wss/config/fuzzingserver.json | 16 + .../autobahn/fuzzing_server_wss/config/server.crt | 19 + .../autobahn/fuzzing_server_wss/config/server.csr | 16 + .../autobahn/fuzzing_server_wss/config/server.key | 27 + .../autobahn/fuzzing_server_wss/config/server.pem | 19 + .../websocket/tests/autobahn/local_run/Dockerfile | 12 + .../tests/autobahn/local_run/autobahn_client.v | 33 + .../tests/autobahn/local_run/autobahn_client_wss.v | 35 + .../websocket/tests/autobahn/ws_test/Dockerfile | 12 + v_windows/v/old/vlib/net/websocket/uri.v | 16 + v_windows/v/old/vlib/net/websocket/utils.v | 54 + .../v/old/vlib/net/websocket/websocket_client.v | 488 + .../v/old/vlib/net/websocket/websocket_nix.c.v | 10 + .../v/old/vlib/net/websocket/websocket_server.v | 189 + .../v/old/vlib/net/websocket/websocket_test.v | 122 + .../v/old/vlib/net/websocket/websocket_windows.c.v | 12 + v_windows/v/old/vlib/orm/README.md | 85 + v_windows/v/old/vlib/orm/orm.v | 475 + v_windows/v/old/vlib/orm/orm_fn_test.v | 193 + v_windows/v/old/vlib/orm/orm_test.v | 290 + v_windows/v/old/vlib/os/args.v | 51 + v_windows/v/old/vlib/os/bare/bare_example_linux.v | 8 + v_windows/v/old/vlib/os/cmdline/cmdline.v | 82 + v_windows/v/old/vlib/os/cmdline/cmdline_test.v | 37 + v_windows/v/old/vlib/os/const.v | 1 + v_windows/v/old/vlib/os/const_nix.c.v | 16 + v_windows/v/old/vlib/os/const_windows.c.v | 161 + v_windows/v/old/vlib/os/environment.c.v | 108 + v_windows/v/old/vlib/os/environment_test.v | 49 + v_windows/v/old/vlib/os/fd.c.v | 61 + v_windows/v/old/vlib/os/file.c.v | 781 + v_windows/v/old/vlib/os/file_test.v | 372 + v_windows/v/old/vlib/os/glob_test.v | 80 + v_windows/v/old/vlib/os/inode.c.v | 92 + v_windows/v/old/vlib/os/inode_test.v | 43 + v_windows/v/old/vlib/os/notify/backend_default.c.v | 6 + v_windows/v/old/vlib/os/notify/backend_linux.c.v | 206 + v_windows/v/old/vlib/os/notify/notify.v | 35 + v_windows/v/old/vlib/os/notify/notify_test.v | 155 + v_windows/v/old/vlib/os/os.v | 662 + v_windows/v/old/vlib/os/os_android.c.v | 39 + v_windows/v/old/vlib/os/os_c.v | 979 + v_windows/v/old/vlib/os/os_darwin.c.v | 18 + v_windows/v/old/vlib/os/os_darwin.m | 7 + v_windows/v/old/vlib/os/os_linux.c.v | 19 + v_windows/v/old/vlib/os/os_nix.c.v | 549 + v_windows/v/old/vlib/os/os_test.v | 752 + v_windows/v/old/vlib/os/os_windows.c.v | 544 + v_windows/v/old/vlib/os/process.v | 317 + v_windows/v/old/vlib/os/process_nix.c.v | 146 + v_windows/v/old/vlib/os/process_test.v | 96 + v_windows/v/old/vlib/os/process_windows.c.v | 243 + v_windows/v/old/vlib/os/signal.c.v | 58 + v_windows/v/old/vlib/os/signal_test.v | 35 + v_windows/v/old/vlib/os2/keep_vfmt_happy.v | 2 + v_windows/v/old/vlib/os2/os2_darwin.c.v | 36 + v_windows/v/old/vlib/os2/os2_test.v | 11 + v_windows/v/old/vlib/os_js/environment.js.v | 31 + v_windows/v/old/vlib/os_js/file.js.v | 130 + v_windows/v/old/vlib/os_js/os.js.v | 95 + v_windows/v/old/vlib/os_js/os.v | 13 + v_windows/v/old/vlib/os_js/process.js.v | 173 + v_windows/v/old/vlib/pg/README.md | 25 + v_windows/v/old/vlib/pg/oid.v | 171 + v_windows/v/old/vlib/pg/orm.v | 272 + v_windows/v/old/vlib/pg/pg.v | 277 + v_windows/v/old/vlib/pg/pg_orm_test.v | 77 + v_windows/v/old/vlib/picoev/picoev.v | 265 + v_windows/v/old/vlib/picohttpparser/misc.v | 20 + .../v/old/vlib/picohttpparser/picohttpparser.v | 35 + v_windows/v/old/vlib/picohttpparser/request.v | 65 + v_windows/v/old/vlib/picohttpparser/response.v | 114 + v_windows/v/old/vlib/rand/README.md | 87 + v_windows/v/old/vlib/rand/constants/constants.v | 12 + v_windows/v/old/vlib/rand/dist/README.md | 10 + v_windows/v/old/vlib/rand/dist/dist.v | 85 + v_windows/v/old/vlib/rand/dist/dist_test.v | 134 + v_windows/v/old/vlib/rand/mt19937/mt19937.v | 325 + v_windows/v/old/vlib/rand/mt19937/mt19937_test.v | 341 + v_windows/v/old/vlib/rand/musl/musl_rng.v | 241 + v_windows/v/old/vlib/rand/musl/musl_rng_test.v | 331 + v_windows/v/old/vlib/rand/pcg32/pcg32.v | 226 + v_windows/v/old/vlib/rand/pcg32/pcg32_test.v | 337 + v_windows/v/old/vlib/rand/rand.v | 317 + .../v/old/vlib/rand/random_identifiers_test.v | 70 + v_windows/v/old/vlib/rand/random_numbers_test.v | 319 + v_windows/v/old/vlib/rand/seed/seed.v | 40 + v_windows/v/old/vlib/rand/splitmix64/splitmix64.v | 225 + .../v/old/vlib/rand/splitmix64/splitmix64_test.v | 331 + v_windows/v/old/vlib/rand/sys/system_rng.c.v | 275 + v_windows/v/old/vlib/rand/sys/system_rng.js.v | 15 + v_windows/v/old/vlib/rand/sys/system_rng_test.v | 354 + v_windows/v/old/vlib/rand/util/util.v | 52 + v_windows/v/old/vlib/rand/util/util_test.v | 57 + v_windows/v/old/vlib/rand/wyrand/wyrand.v | 252 + v_windows/v/old/vlib/rand/wyrand/wyrand_test.v | 331 + v_windows/v/old/vlib/readline/README.md | 20 + v_windows/v/old/vlib/readline/readline.v | 45 + v_windows/v/old/vlib/readline/readline_default.c.v | 78 + v_windows/v/old/vlib/readline/readline_linux.c.v | 554 + v_windows/v/old/vlib/readline/readline_test.v | 20 + v_windows/v/old/vlib/readline/readline_windows.c.v | 74 + v_windows/v/old/vlib/regex/README.md | 874 + v_windows/v/old/vlib/regex/regex.v | 2322 ++ v_windows/v/old/vlib/regex/regex_opt.v | 53 + v_windows/v/old/vlib/regex/regex_test.v | 606 + v_windows/v/old/vlib/regex/regex_util.v | 436 + v_windows/v/old/vlib/runtime/runtime.v | 56 + v_windows/v/old/vlib/runtime/runtime_nix.c.v | 11 + v_windows/v/old/vlib/runtime/runtime_test.v | 39 + v_windows/v/old/vlib/runtime/runtime_windows.c.v | 21 + v_windows/v/old/vlib/semver/LICENSE.md | 21 + v_windows/v/old/vlib/semver/README.md | 37 + v_windows/v/old/vlib/semver/compare.v | 59 + v_windows/v/old/vlib/semver/parse.v | 85 + v_windows/v/old/vlib/semver/range.v | 252 + v_windows/v/old/vlib/semver/semver.v | 110 + v_windows/v/old/vlib/semver/semver_test.v | 192 + v_windows/v/old/vlib/semver/util.v | 55 + v_windows/v/old/vlib/semver/v.mod | 5 + v_windows/v/old/vlib/sokol/audio/audio.v | 134 + v_windows/v/old/vlib/sokol/c/declaration.c.v | 58 + v_windows/v/old/vlib/sokol/f/f.v | 15 + v_windows/v/old/vlib/sokol/gfx/enums.v | 297 + v_windows/v/old/vlib/sokol/gfx/gfx.v | 266 + v_windows/v/old/vlib/sokol/gfx/gfx_funcs.v | 71 + v_windows/v/old/vlib/sokol/gfx/gfx_structs.v | 535 + v_windows/v/old/vlib/sokol/gfx/gfx_utils.v | 17 + v_windows/v/old/vlib/sokol/sapp/enums.v | 165 + v_windows/v/old/vlib/sokol/sapp/sapp.v | 237 + v_windows/v/old/vlib/sokol/sapp/sapp_funcs.v | 105 + v_windows/v/old/vlib/sokol/sapp/sapp_structs.v | 104 + v_windows/v/old/vlib/sokol/sfons/sfons.v | 28 + v_windows/v/old/vlib/sokol/sfons/sfons_funcs.v | 6 + v_windows/v/old/vlib/sokol/sgl/sgl.v | 375 + v_windows/v/old/vlib/sokol/sgl/sgl_funcs.v | 91 + v_windows/v/old/vlib/sokol/sgl/sgl_structs.v | 24 + v_windows/v/old/vlib/sokol/sokol.v | 19 + v_windows/v/old/vlib/sqlite/README.md | 16 + v_windows/v/old/vlib/sqlite/orm.v | 161 + v_windows/v/old/vlib/sqlite/sqlite.v | 236 + v_windows/v/old/vlib/sqlite/sqlite_orm_test.v | 70 + v_windows/v/old/vlib/sqlite/sqlite_test.v | 31 + v_windows/v/old/vlib/sqlite/stmt.v | 74 + v_windows/v/old/vlib/stbi/stbi.v | 87 + v_windows/v/old/vlib/strconv/atof.v | 441 + v_windows/v/old/vlib/strconv/atof_test.v | 75 + v_windows/v/old/vlib/strconv/atofq.v | 348 + v_windows/v/old/vlib/strconv/atoi.v | 261 + v_windows/v/old/vlib/strconv/atoi_test.v | 84 + .../v/old/vlib/strconv/f32_f64_to_string_test.v | 171 + v_windows/v/old/vlib/strconv/f32_str.v | 377 + v_windows/v/old/vlib/strconv/f64_str.v | 418 + v_windows/v/old/vlib/strconv/format.md | 250 + v_windows/v/old/vlib/strconv/format.v | 113 + v_windows/v/old/vlib/strconv/format_mem.v | 498 + v_windows/v/old/vlib/strconv/format_test.v | 110 + v_windows/v/old/vlib/strconv/ftoa.v | 39 + v_windows/v/old/vlib/strconv/number_to_base.v | 61 + v_windows/v/old/vlib/strconv/number_to_base_test.v | 38 + v_windows/v/old/vlib/strconv/structs.v | 55 + v_windows/v/old/vlib/strconv/tables.v | 738 + v_windows/v/old/vlib/strconv/utilities.v | 556 + v_windows/v/old/vlib/strconv/vprintf.v | 726 + v_windows/v/old/vlib/strings/builder.js.v | 51 + v_windows/v/old/vlib/strings/builder.v | 140 + v_windows/v/old/vlib/strings/builder_test.v | 96 + v_windows/v/old/vlib/strings/similarity.v | 69 + v_windows/v/old/vlib/strings/similarity_test.v | 13 + v_windows/v/old/vlib/strings/strings.c.v | 38 + v_windows/v/old/vlib/strings/strings.js.v | 17 + v_windows/v/old/vlib/strings/strings.v | 13 + v_windows/v/old/vlib/strings/strings_test.v | 14 + .../v/old/vlib/strings/textscanner/textscanner.v | 154 + .../vlib/strings/textscanner/textscanner_test.v | 159 + v_windows/v/old/vlib/sync/array_rlock_test.v | 38 + v_windows/v/old/vlib/sync/atomic2/atomic.v | 88 + v_windows/v/old/vlib/sync/atomic2/atomic_test.v | 105 + .../v/old/vlib/sync/bench/channel_bench_go.go | 68 + v_windows/v/old/vlib/sync/bench/channel_bench_v.v | 64 + .../many_writers_and_receivers_on_1_channel.v | 150 + v_windows/v/old/vlib/sync/bench/results.md | 48 + v_windows/v/old/vlib/sync/channel_1_test.v | 25 + v_windows/v/old/vlib/sync/channel_2_test.v | 19 + v_windows/v/old/vlib/sync/channel_3_test.v | 32 + v_windows/v/old/vlib/sync/channel_4_test.v | 32 + v_windows/v/old/vlib/sync/channel_array_mut_test.v | 35 + v_windows/v/old/vlib/sync/channel_close_test.v | 104 + v_windows/v/old/vlib/sync/channel_fill_test.v | 22 + .../v/old/vlib/sync/channel_opt_propagate_test.v | 39 + v_windows/v/old/vlib/sync/channel_polling_test.v | 56 + v_windows/v/old/vlib/sync/channel_push_or_1_test.v | 65 + v_windows/v/old/vlib/sync/channel_push_or_2_test.v | 25 + v_windows/v/old/vlib/sync/channel_select_2_test.v | 62 + v_windows/v/old/vlib/sync/channel_select_3_test.v | 122 + v_windows/v/old/vlib/sync/channel_select_4_test.v | 43 + v_windows/v/old/vlib/sync/channel_select_5_test.v | 61 + v_windows/v/old/vlib/sync/channel_select_6_test.v | 75 + v_windows/v/old/vlib/sync/channel_select_test.v | 84 + v_windows/v/old/vlib/sync/channel_try_buf_test.v | 17 + v_windows/v/old/vlib/sync/channel_try_unbuf_test.v | 13 + v_windows/v/old/vlib/sync/channels.v | 730 + v_windows/v/old/vlib/sync/pool/README.md | 36 + v_windows/v/old/vlib/sync/pool/pool.v | 165 + v_windows/v/old/vlib/sync/pool/pool_test.v | 52 + v_windows/v/old/vlib/sync/select_close_test.v | 92 + v_windows/v/old/vlib/sync/struct_chan_init_test.v | 14 + v_windows/v/old/vlib/sync/sync_default.c.v | 193 + v_windows/v/old/vlib/sync/sync_macos.c.v | 232 + v_windows/v/old/vlib/sync/sync_windows.c.v | 212 + v_windows/v/old/vlib/sync/threads/threads.c.v | 13 + v_windows/v/old/vlib/sync/threads/threads.v | 4 + v_windows/v/old/vlib/sync/waitgroup.v | 84 + v_windows/v/old/vlib/sync/waitgroup_test.v | 41 + v_windows/v/old/vlib/szip/szip.v | 296 + v_windows/v/old/vlib/szip/szip_test.v | 88 + v_windows/v/old/vlib/term/README.md | 84 + v_windows/v/old/vlib/term/colors.v | 199 + v_windows/v/old/vlib/term/control.v | 108 + v_windows/v/old/vlib/term/term.js.v | 8 + v_windows/v/old/vlib/term/term.v | 196 + v_windows/v/old/vlib/term/term_nix.c.v | 105 + v_windows/v/old/vlib/term/term_test.v | 115 + v_windows/v/old/vlib/term/term_windows.c.v | 125 + v_windows/v/old/vlib/term/ui/README.md | 99 + v_windows/v/old/vlib/term/ui/color.v | 88 + .../v/old/vlib/term/ui/consoleapi_windows.c.v | 82 + v_windows/v/old/vlib/term/ui/input.v | 241 + v_windows/v/old/vlib/term/ui/input_nix.c.v | 70 + v_windows/v/old/vlib/term/ui/input_windows.c.v | 326 + v_windows/v/old/vlib/term/ui/termios_nix.c.v | 530 + v_windows/v/old/vlib/term/ui/ui.v | 256 + v_windows/v/old/vlib/time/Y2K38_test.v | 13 + v_windows/v/old/vlib/time/format.v | 172 + v_windows/v/old/vlib/time/misc/misc.v | 13 + v_windows/v/old/vlib/time/misc/misc_test.v | 17 + v_windows/v/old/vlib/time/operator.v | 21 + v_windows/v/old/vlib/time/operator_test.v | 391 + v_windows/v/old/vlib/time/parse.v | 140 + v_windows/v/old/vlib/time/parse_test.v | 138 + v_windows/v/old/vlib/time/private_test.v | 13 + v_windows/v/old/vlib/time/stopwatch.v | 72 + v_windows/v/old/vlib/time/stopwatch_test.v | 36 + v_windows/v/old/vlib/time/time.v | 431 + v_windows/v/old/vlib/time/time_darwin.c.v | 84 + v_windows/v/old/vlib/time/time_format_test.v | 90 + v_windows/v/old/vlib/time/time_linux.c.v | 26 + v_windows/v/old/vlib/time/time_nix.c.v | 156 + v_windows/v/old/vlib/time/time_solaris.c.v | 32 + v_windows/v/old/vlib/time/time_test.v | 247 + v_windows/v/old/vlib/time/time_windows.c.v | 233 + v_windows/v/old/vlib/time/unix.v | 124 + v_windows/v/old/vlib/v/README.md | 119 + v_windows/v/old/vlib/v/TEMPLATES.md | 129 + v_windows/v/old/vlib/v/ast/ast.v | 2020 ++ v_windows/v/old/vlib/v/ast/attr.v | 62 + v_windows/v/old/vlib/v/ast/cflags.v | 89 + v_windows/v/old/vlib/v/ast/cflags_test.v | 72 + v_windows/v/old/vlib/v/ast/comptime_const_values.v | 271 + v_windows/v/old/vlib/v/ast/init.v | 70 + v_windows/v/old/vlib/v/ast/scope.v | 241 + v_windows/v/old/vlib/v/ast/str.v | 577 + v_windows/v/old/vlib/v/ast/table.v | 1371 + v_windows/v/old/vlib/v/ast/types.v | 1308 + v_windows/v/old/vlib/v/ast/types_test.v | 81 + v_windows/v/old/vlib/v/ast/walker/walker.v | 37 + v_windows/v/old/vlib/v/ast/walker/walker_test.v | 66 + v_windows/v/old/vlib/v/builder/builder.v | 475 + v_windows/v/old/vlib/v/builder/c.v | 63 + v_windows/v/old/vlib/v/builder/cc.v | 1005 + v_windows/v/old/vlib/v/builder/cflags.v | 45 + v_windows/v/old/vlib/v/builder/compile.v | 324 + v_windows/v/old/vlib/v/builder/js.v | 51 + v_windows/v/old/vlib/v/builder/msvc.v | 505 + v_windows/v/old/vlib/v/builder/native.v | 22 + v_windows/v/old/vlib/v/callgraph/callgraph.v | 129 + v_windows/v/old/vlib/v/cflag/cflags.v | 131 + v_windows/v/old/vlib/v/checker/check_types.v | 733 + v_windows/v/old/vlib/v/checker/checker.v | 8283 +++++ .../v/old/vlib/v/checker/comptime_const_eval.v | 159 + v_windows/v/old/vlib/v/checker/noreturn.v | 112 + .../v/old/vlib/v/checker/tests/.gitattributes | 2 + v_windows/v/old/vlib/v/checker/tests/.gitignore | 4 + .../tests/a_test_file_with_0_test_fns_test.out | 6 + .../tests/a_test_file_with_0_test_fns_test.vv | 1 + .../vlib/v/checker/tests/add_op_wrong_type_err.out | 42 + .../vlib/v/checker/tests/add_op_wrong_type_err.vv | 10 + .../alias_array_unknown_op_overloading_err.out | 13 + .../alias_array_unknown_op_overloading_err.vv | 19 + .../tests/alias_map_unknown_op_overloading_err.out | 13 + .../tests/alias_map_unknown_op_overloading_err.vv | 21 + .../old/vlib/v/checker/tests/alias_type_exists.out | 3 + .../old/vlib/v/checker/tests/alias_type_exists.vv | 1 + .../v/checker/tests/ambiguous_field_method_err.out | 13 + .../v/checker/tests/ambiguous_field_method_err.vv | 24 + .../v/checker/tests/ambiguous_function_call.out | 13 + .../v/checker/tests/ambiguous_function_call.vv | 13 + .../vlib/v/checker/tests/any_int_float_ban_err.out | 45 + .../vlib/v/checker/tests/any_int_float_ban_err.vv | 15 + .../v/old/vlib/v/checker/tests/append_err.out | 20 + v_windows/v/old/vlib/v/checker/tests/append_err.vv | 7 + .../tests/array_append_array_type_mismatch_err.out | 7 + .../tests/array_append_array_type_mismatch_err.vv | 5 + .../v/checker/tests/array_builtin_redefinition.out | 7 + .../v/checker/tests/array_builtin_redefinition.vv | 11 + .../v/old/vlib/v/checker/tests/array_cmp_err.out | 26 + .../v/old/vlib/v/checker/tests/array_cmp_err.vv | 6 + .../v/checker/tests/array_declare_element_a.out | 5 + .../v/checker/tests/array_declare_element_a.vv | 3 + .../v/checker/tests/array_declare_element_b.out | 6 + .../v/checker/tests/array_declare_element_b.vv | 4 + .../v/checker/tests/array_declare_element_c.out | 6 + .../v/checker/tests/array_declare_element_c.vv | 4 + .../vlib/v/checker/tests/array_element_type.out | 12 + .../old/vlib/v/checker/tests/array_element_type.vv | 4 + .../vlib/v/checker/tests/array_filter_fn_err.out | 27 + .../vlib/v/checker/tests/array_filter_fn_err.vv | 21 + .../v/old/vlib/v/checker/tests/array_index.out | 34 + .../v/old/vlib/v/checker/tests/array_index.vv | 11 + ...nit_sum_type_without_init_value_and_len_err.out | 14 + ...init_sum_type_without_init_value_and_len_err.vv | 9 + .../v/checker/tests/array_insert_type_mismatch.out | 56 + .../v/checker/tests/array_insert_type_mismatch.vv | 15 + .../v/checker/tests/array_literal_modify_err.out | 12 + .../v/checker/tests/array_literal_modify_err.vv | 4 + .../v/checker/tests/array_map_arg_mismatch.out | 13 + .../vlib/v/checker/tests/array_map_arg_mismatch.vv | 5 + .../old/vlib/v/checker/tests/array_map_fn_err.out | 41 + .../v/old/vlib/v/checker/tests/array_map_fn_err.vv | 30 + .../vlib/v/checker/tests/array_map_void_fn_err.out | 6 + .../vlib/v/checker/tests/array_map_void_fn_err.vv | 4 + .../array_of_interfaces_with_len_without_init.out | 7 + .../array_of_interfaces_with_len_without_init.vv | 16 + .../v/checker/tests/array_or_map_assign_err.out | 35 + .../v/checker/tests/array_or_map_assign_err.vv | 29 + .../checker/tests/array_prepend_type_mismatch.out | 56 + .../v/checker/tests/array_prepend_type_mismatch.vv | 15 + .../v/old/vlib/v/checker/tests/array_sort_err.out | 27 + .../v/old/vlib/v/checker/tests/array_sort_err.vv | 7 + .../tests/array_sort_struct_no_body_err.out | 5 + .../checker/tests/array_sort_struct_no_body_err.vv | 6 + .../tests/arrow_op_wrong_left_type_err_a.out | 6 + .../tests/arrow_op_wrong_left_type_err_a.vv | 5 + .../tests/arrow_op_wrong_left_type_err_b.out | 7 + .../tests/arrow_op_wrong_left_type_err_b.vv | 6 + .../tests/arrow_op_wrong_right_type_err_a.out | 6 + .../tests/arrow_op_wrong_right_type_err_a.vv | 5 + .../tests/arrow_op_wrong_right_type_err_b.out | 7 + .../tests/arrow_op_wrong_right_type_err_b.vv | 5 + .../old/vlib/v/checker/tests/asm_immutable_err.out | 7 + .../old/vlib/v/checker/tests/asm_immutable_err.vv | 16 + .../vlib/v/checker/tests/assert_optional_err.out | 6 + .../vlib/v/checker/tests/assert_optional_err.vv | 5 + .../tests/assign_array_init_with_no_type.out | 12 + .../tests/assign_array_init_with_no_type.vv | 4 + .../assign_deref_fn_call_on_left_side_err.out | 6 + .../tests/assign_deref_fn_call_on_left_side_err.vv | 9 + .../v/checker/tests/assign_expr_type_err_a.out | 7 + .../vlib/v/checker/tests/assign_expr_type_err_a.vv | 5 + .../v/checker/tests/assign_expr_type_err_b.out | 7 + .../vlib/v/checker/tests/assign_expr_type_err_b.vv | 5 + .../v/checker/tests/assign_expr_type_err_c.out | 7 + .../vlib/v/checker/tests/assign_expr_type_err_c.vv | 5 + .../v/checker/tests/assign_expr_type_err_d.out | 7 + .../vlib/v/checker/tests/assign_expr_type_err_d.vv | 5 + .../v/checker/tests/assign_expr_type_err_e.out | 7 + .../vlib/v/checker/tests/assign_expr_type_err_e.vv | 5 + .../v/checker/tests/assign_expr_type_err_f.out | 7 + .../vlib/v/checker/tests/assign_expr_type_err_f.vv | 5 + .../v/checker/tests/assign_expr_type_err_g.out | 7 + .../vlib/v/checker/tests/assign_expr_type_err_g.vv | 5 + .../v/checker/tests/assign_expr_type_err_h.out | 7 + .../vlib/v/checker/tests/assign_expr_type_err_h.vv | 5 + .../v/checker/tests/assign_expr_type_err_i.out | 7 + .../vlib/v/checker/tests/assign_expr_type_err_i.vv | 5 + .../checker/tests/assign_expr_undefined_err_a.out | 6 + .../v/checker/tests/assign_expr_undefined_err_a.vv | 4 + .../checker/tests/assign_expr_undefined_err_b.out | 6 + .../v/checker/tests/assign_expr_undefined_err_b.vv | 4 + .../checker/tests/assign_expr_undefined_err_c.out | 6 + .../v/checker/tests/assign_expr_undefined_err_c.vv | 4 + .../checker/tests/assign_expr_undefined_err_d.out | 6 + .../v/checker/tests/assign_expr_undefined_err_d.vv | 4 + .../checker/tests/assign_expr_undefined_err_e.out | 6 + .../v/checker/tests/assign_expr_undefined_err_e.vv | 4 + .../checker/tests/assign_expr_undefined_err_f.out | 6 + .../v/checker/tests/assign_expr_undefined_err_f.vv | 4 + .../checker/tests/assign_expr_undefined_err_g.out | 5 + .../v/checker/tests/assign_expr_undefined_err_g.vv | 3 + .../checker/tests/assign_expr_undefined_err_h.out | 7 + .../v/checker/tests/assign_expr_undefined_err_h.vv | 8 + .../checker/tests/assign_expr_undefined_err_i.out | 6 + .../v/checker/tests/assign_expr_undefined_err_i.vv | 4 + .../checker/tests/assign_expr_undefined_err_j.out | 6 + .../v/checker/tests/assign_expr_undefined_err_j.vv | 4 + .../checker/tests/assign_expr_undefined_err_k.out | 6 + .../v/checker/tests/assign_expr_undefined_err_k.vv | 4 + .../assign_expr_unresolved_variables_err_chain.out | 13 + .../assign_expr_unresolved_variables_err_chain.vv | 8 + .../tests/assign_fn_call_on_left_side_err.out | 6 + .../tests/assign_fn_call_on_left_side_err.vv | 7 + .../v/checker/tests/assign_multi_immutable_err.out | 21 + .../v/checker/tests/assign_multi_immutable_err.vv | 21 + .../vlib/v/checker/tests/assign_multi_mismatch.out | 81 + .../vlib/v/checker/tests/assign_multi_mismatch.vv | 30 + .../v/old/vlib/v/checker/tests/assign_mut.out | 7 + v_windows/v/old/vlib/v/checker/tests/assign_mut.vv | 6 + .../vlib/v/checker/tests/assign_sumtype2_err.out | 7 + .../vlib/v/checker/tests/assign_sumtype2_err.vv | 15 + .../vlib/v/checker/tests/assign_sumtype_err.out | 7 + .../old/vlib/v/checker/tests/assign_sumtype_err.vv | 15 + .../tests/bad_types_in_string_inter_lit.out | 8 + .../checker/tests/bad_types_in_string_inter_lit.vv | 2 + .../v/checker/tests/bin_lit_without_digit_err.out | 5 + .../v/checker/tests/bin_lit_without_digit_err.vv | 3 + .../v/checker/tests/bin_lit_wrong_digit_err.out | 5 + .../v/checker/tests/bin_lit_wrong_digit_err.vv | 3 + .../v/checker/tests/bit_op_wrong_left_type_err.out | 5 + .../v/checker/tests/bit_op_wrong_left_type_err.vv | 3 + .../checker/tests/bit_op_wrong_right_type_err.out | 5 + .../v/checker/tests/bit_op_wrong_right_type_err.vv | 3 + .../v/checker/tests/blank_ident_invalid_use.out | 5 + .../v/checker/tests/blank_ident_invalid_use.vv | 3 + .../v/old/vlib/v/checker/tests/blank_modify.out | 5 + .../v/old/vlib/v/checker/tests/blank_modify.vv | 3 + .../vlib/v/checker/tests/bool_string_cast_err.out | 12 + .../vlib/v/checker/tests/bool_string_cast_err.vv | 4 + .../old/vlib/v/checker/tests/break_anon_fn_err.out | 7 + .../old/vlib/v/checker/tests/break_anon_fn_err.vv | 8 + .../old/vlib/v/checker/tests/c_fn_surplus_args.out | 42 + .../old/vlib/v/checker/tests/c_fn_surplus_args.vv | 19 + .../vlib/v/checker/tests/cannot_assign_array.out | 6 + .../vlib/v/checker/tests/cannot_assign_array.vv | 10 + .../vlib/v/checker/tests/cannot_cast_to_alias.out | 6 + .../vlib/v/checker/tests/cannot_cast_to_alias.vv | 7 + .../vlib/v/checker/tests/cannot_cast_to_struct.out | 27 + .../vlib/v/checker/tests/cannot_cast_to_struct.vv | 16 + v_windows/v/old/vlib/v/checker/tests/cast_err.out | 27 + v_windows/v/old/vlib/v/checker/tests/cast_err.vv | 10 + .../v/old/vlib/v/checker/tests/cast_string_err.out | 6 + .../v/old/vlib/v/checker/tests/cast_string_err.vv | 4 + .../v/checker/tests/cast_string_with_byte_err.out | 5 + .../v/checker/tests/cast_string_with_byte_err.vv | 3 + v_windows/v/old/vlib/v/checker/tests/cast_void.out | 4 + v_windows/v/old/vlib/v/checker/tests/cast_void.vv | 2 + v_windows/v/old/vlib/v/checker/tests/chan_args.out | 28 + v_windows/v/old/vlib/v/checker/tests/chan_args.vv | 15 + v_windows/v/old/vlib/v/checker/tests/chan_mut.out | 28 + v_windows/v/old/vlib/v/checker/tests/chan_mut.vv | 27 + v_windows/v/old/vlib/v/checker/tests/chan_ref.out | 14 + v_windows/v/old/vlib/v/checker/tests/chan_ref.vv | 33 + v_windows/v/old/vlib/v/checker/tests/char_str.out | 3 + v_windows/v/old/vlib/v/checker/tests/char_str.vv | 1 + ...ing_typesymbol_to_a_type_should_not_compile.out | 7 + ...ring_typesymbol_to_a_type_should_not_compile.vv | 17 + .../vlib/v/checker/tests/comptime_call_method.out | 7 + .../vlib/v/checker/tests/comptime_call_method.vv | 12 + .../checker/tests/comptime_call_no_unused_var.out | 27 + .../v/checker/tests/comptime_call_no_unused_var.vv | 18 + .../tests/comptime_env/env_parser_errors_1.run.out | 3 + .../tests/comptime_env/env_parser_errors_1.vv | 1 + .../tests/comptime_env/env_parser_errors_2.run.out | 3 + .../tests/comptime_env/env_parser_errors_2.vv | 1 + .../tests/comptime_env/env_parser_errors_3.run.out | 3 + .../tests/comptime_env/env_parser_errors_3.vv | 1 + .../tests/comptime_env/using_comptime_env.run.out | 11 + .../comptime_env/using_comptime_env.var.run.out | 2 + .../using_comptime_env.var_invalid.run.out | 1 + .../tests/comptime_env/using_comptime_env.vv | 8 + .../comptime_field_selector_not_in_for_err.out | 7 + .../comptime_field_selector_not_in_for_err.vv | 14 + .../tests/comptime_field_selector_not_name_err.out | 21 + .../tests/comptime_field_selector_not_name_err.vv | 20 + .../v/old/vlib/v/checker/tests/comptime_for.out | 55 + .../v/old/vlib/v/checker/tests/comptime_for.vv | 23 + .../checker/tests/const_array_unknown_type_err.out | 4 + .../checker/tests/const_array_unknown_type_err.vv | 1 + .../checker/tests/const_define_in_function_err.out | 6 + .../checker/tests/const_define_in_function_err.vv | 4 + .../vlib/v/checker/tests/const_field_add_err.out | 6 + .../vlib/v/checker/tests/const_field_add_err.vv | 7 + .../vlib/v/checker/tests/const_field_dec_err.out | 6 + .../vlib/v/checker/tests/const_field_dec_err.vv | 7 + .../vlib/v/checker/tests/const_field_inc_err.out | 6 + .../vlib/v/checker/tests/const_field_inc_err.vv | 7 + .../tests/const_field_name_duplicate_err.out | 7 + .../tests/const_field_name_duplicate_err.vv | 7 + .../checker/tests/const_field_name_snake_case.out | 6 + .../v/checker/tests/const_field_name_snake_case.vv | 4 + .../vlib/v/checker/tests/const_field_sub_err.out | 6 + .../vlib/v/checker/tests/const_field_sub_err.vv | 7 + v_windows/v/old/vlib/v/checker/tests/ctdefine.out | 12 + v_windows/v/old/vlib/v/checker/tests/ctdefine.vv | 6 + .../custom_comptime_define_error.mysymbol.run.out | 2 + .../checker/tests/custom_comptime_define_error.out | 7 + .../checker/tests/custom_comptime_define_error.vv | 10 + .../custom_comptime_define_if_debug.cg.run.out | 3 + ...stom_comptime_define_if_debug.debug.bar.run.out | 3 + .../custom_comptime_define_if_debug.debug.run.out | 2 + .../custom_comptime_define_if_debug.g.run.out | 3 + .../tests/custom_comptime_define_if_debug.out | 0 .../tests/custom_comptime_define_if_debug.run.out | 1 + .../tests/custom_comptime_define_if_debug.vv | 18 + .../custom_comptime_define_if_flag.mydebug.run.out | 4 + .../custom_comptime_define_if_flag.nodebug.run.out | 2 + .../tests/custom_comptime_define_if_flag.out | 0 .../tests/custom_comptime_define_if_flag.vv | 13 + .../v/checker/tests/dec_lit_wrong_digit_err.out | 5 + .../v/checker/tests/dec_lit_wrong_digit_err.vv | 3 + .../vlib/v/checker/tests/decompose_type_err.out | 6 + .../old/vlib/v/checker/tests/decompose_type_err.vv | 5 + .../v/old/vlib/v/checker/tests/defer_in_for.out | 7 + .../v/old/vlib/v/checker/tests/defer_in_for.vv | 7 + .../v/old/vlib/v/checker/tests/defer_optional.out | 7 + .../v/old/vlib/v/checker/tests/defer_optional.vv | 8 + .../v/old/vlib/v/checker/tests/deprecations.out | 48 + .../v/old/vlib/v/checker/tests/deprecations.vv | 63 + .../v/checker/tests/direct_map_alias_init_err.out | 6 + .../v/checker/tests/direct_map_alias_init_err.vv | 5 + .../tests/disallow_pointer_arithmetic_err.out | 34 + .../tests/disallow_pointer_arithmetic_err.vv | 9 + .../checker/tests/div_mod_by_cast_zero_int_err.out | 40 + .../checker/tests/div_mod_by_cast_zero_int_err.vv | 10 + .../vlib/v/checker/tests/div_op_wrong_type_err.out | 42 + .../vlib/v/checker/tests/div_op_wrong_type_err.vv | 10 + .../tests/division_by_cast_zero_float_err.out | 12 + .../tests/division_by_cast_zero_float_err.vv | 4 + .../v/checker/tests/division_by_zero_float_err.out | 5 + .../v/checker/tests/division_by_zero_float_err.vv | 3 + .../v/checker/tests/division_by_zero_int_err.out | 26 + .../v/checker/tests/division_by_zero_int_err.vv | 6 + .../old/vlib/v/checker/tests/dump_of_void_expr.out | 5 + .../old/vlib/v/checker/tests/dump_of_void_expr.vv | 3 + .../v/checker/tests/duplicate_field_method_err.out | 13 + .../v/checker/tests/duplicate_field_method_err.vv | 10 + .../v/old/vlib/v/checker/tests/enum_as_int_err.out | 21 + .../v/old/vlib/v/checker/tests/enum_as_int_err.vv | 13 + v_windows/v/old/vlib/v/checker/tests/enum_cast.out | 6 + v_windows/v/old/vlib/v/checker/tests/enum_cast.vv | 7 + .../v/old/vlib/v/checker/tests/enum_empty.out | 3 + v_windows/v/old/vlib/v/checker/tests/enum_empty.vv | 1 + v_windows/v/old/vlib/v/checker/tests/enum_err.out | 14 + v_windows/v/old/vlib/v/checker/tests/enum_err.vv | 11 + .../tests/enum_field_name_duplicate_err.out | 7 + .../checker/tests/enum_field_name_duplicate_err.vv | 10 + .../vlib/v/checker/tests/enum_field_overflow.out | 6 + .../vlib/v/checker/tests/enum_field_overflow.vv | 5 + .../checker/tests/enum_field_value_duplicate_a.out | 13 + .../checker/tests/enum_field_value_duplicate_a.vv | 6 + .../checker/tests/enum_field_value_duplicate_b.out | 6 + .../checker/tests/enum_field_value_duplicate_b.vv | 5 + .../v/checker/tests/enum_field_value_overflow.out | 7 + .../v/checker/tests/enum_field_value_overflow.vv | 5 + .../v/old/vlib/v/checker/tests/enum_op_err.out | 34 + .../v/old/vlib/v/checker/tests/enum_op_err.vv | 13 + .../old/vlib/v/checker/tests/enum_op_flag_err.out | 20 + .../v/old/vlib/v/checker/tests/enum_op_flag_err.vv | 12 + .../vlib/v/checker/tests/enum_single_letter.out | 5 + .../old/vlib/v/checker/tests/enum_single_letter.vv | 4 + .../v/checker/tests/eq_ne_op_wrong_type_err.out | 139 + .../v/checker/tests/eq_ne_op_wrong_type_err.vv | 38 + .../vlib/v/checker/tests/error_fn_with_0_args.out | 5 + .../vlib/v/checker/tests/error_fn_with_0_args.vv | 3 + .../tests/error_with_comment_with_crlf_ending.out | 4 + .../tests/error_with_comment_with_crlf_ending.vv | 2 + .../tests/error_with_comment_with_lf_ending.out | 4 + .../tests/error_with_comment_with_lf_ending.vv | 2 + ...rror_with_several_comments_with_crlf_ending.out | 13 + ...error_with_several_comments_with_crlf_ending.vv | 11 + .../vlib/v/checker/tests/error_with_unicode.out | 56 + .../old/vlib/v/checker/tests/error_with_unicode.vv | 15 + .../tests/expression_should_return_an_option.out | 7 + .../tests/expression_should_return_an_option.vv | 31 + .../tests/filter_func_return_nonbool_err.out | 6 + .../tests/filter_func_return_nonbool_err.vv | 7 + .../vlib/v/checker/tests/filter_on_non_arr_err.out | 5 + .../vlib/v/checker/tests/filter_on_non_arr_err.vv | 3 + .../old/vlib/v/checker/tests/fixed_array_conv.out | 41 + .../v/old/vlib/v/checker/tests/fixed_array_conv.vv | 14 + .../tests/fixed_array_non_const_size_err.out | 7 + .../tests/fixed_array_non_const_size_err.vv | 7 + .../vlib/v/checker/tests/fixed_array_size_err.out | 14 + .../vlib/v/checker/tests/fixed_array_size_err.vv | 8 + .../tests/float_lit_exp_not_integer_err.out | 5 + .../checker/tests/float_lit_exp_not_integer_err.vv | 3 + .../tests/float_lit_exp_without_digit_err.out | 5 + .../tests/float_lit_exp_without_digit_err.vv | 3 + .../tests/float_lit_too_many_points_err.out | 5 + .../checker/tests/float_lit_too_many_points_err.vv | 3 + .../old/vlib/v/checker/tests/float_modulo_err.out | 12 + .../v/old/vlib/v/checker/tests/float_modulo_err.vv | 4 + v_windows/v/old/vlib/v/checker/tests/fn_args.out | 35 + v_windows/v/old/vlib/v/checker/tests/fn_args.vv | 23 + .../v/old/vlib/v/checker/tests/fn_call_no_body.out | 13 + .../v/old/vlib/v/checker/tests/fn_call_no_body.vv | 9 + .../v/old/vlib/v/checker/tests/fn_duplicate.out | 13 + .../v/old/vlib/v/checker/tests/fn_duplicate.vv | 7 + .../v/old/vlib/v/checker/tests/fn_init_sig.out | 12 + .../v/old/vlib/v/checker/tests/fn_init_sig.vv | 6 + .../old/vlib/v/checker/tests/fn_return_or_err.out | 7 + .../v/old/vlib/v/checker/tests/fn_return_or_err.vv | 13 + .../v/old/vlib/v/checker/tests/fn_type_exists.out | 10 + .../v/old/vlib/v/checker/tests/fn_type_exists.vv | 3 + v_windows/v/old/vlib/v/checker/tests/fn_var.out | 23 + v_windows/v/old/vlib/v/checker/tests/fn_var.vv | 8 + .../v/old/vlib/v/checker/tests/fn_variadic.out | 14 + .../v/old/vlib/v/checker/tests/fn_variadic.vv | 13 + .../vlib/v/checker/tests/for_in_index_optional.out | 7 + .../vlib/v/checker/tests/for_in_index_optional.vv | 6 + .../old/vlib/v/checker/tests/for_in_index_type.out | 13 + .../old/vlib/v/checker/tests/for_in_index_type.vv | 5 + .../checker/tests/for_in_map_one_variable_err.out | 8 + .../v/checker/tests/for_in_map_one_variable_err.vv | 6 + .../vlib/v/checker/tests/for_in_mut_val_type.out | 42 + .../vlib/v/checker/tests/for_in_mut_val_type.vv | 23 + .../checker/tests/for_in_range_not_match_type.out | 6 + .../v/checker/tests/for_in_range_not_match_type.vv | 5 + .../v/checker/tests/for_in_range_string_type.out | 6 + .../v/checker/tests/for_in_range_string_type.vv | 5 + .../v/old/vlib/v/checker/tests/for_match_err.out | 7 + .../v/old/vlib/v/checker/tests/for_match_err.vv | 15 + .../v/checker/tests/function_arg_mutable_err.out | 6 + .../v/checker/tests/function_arg_mutable_err.vv | 8 + .../v/checker/tests/function_arg_redefinition.out | 5 + .../v/checker/tests/function_arg_redefinition.vv | 7 + .../tests/function_count_of_args_mismatch_err.out | 27 + .../tests/function_count_of_args_mismatch_err.vv | 12 + .../checker/tests/function_missing_return_type.out | 12 + .../checker/tests/function_missing_return_type.vv | 22 + .../function_variadic_arg_array_decompose.out | 6 + .../tests/function_variadic_arg_array_decompose.vv | 12 + .../v/checker/tests/function_wrong_arg_type.out | 7 + .../v/checker/tests/function_wrong_arg_type.vv | 9 + .../v/checker/tests/function_wrong_return_type.out | 6 + .../v/checker/tests/function_wrong_return_type.vv | 8 + .../generic_fn_decl_without_generic_names_err.out | 14 + .../generic_fn_decl_without_generic_names_err.vv | 34 + .../tests/generic_param_used_as_an_array_err.out | 6 + .../tests/generic_param_used_as_an_array_err.vv | 12 + .../tests/generic_sumtype_invalid_variant.out | 14 + .../tests/generic_sumtype_invalid_variant.vv | 11 + .../tests/generics_fn_called_arg_mismatch.out | 27 + .../tests/generics_fn_called_arg_mismatch.vv | 10 + .../generics_fn_called_multi_args_mismatch.out | 14 + .../generics_fn_called_multi_args_mismatch.vv | 17 + .../tests/generics_fn_called_no_arg_err.out | 7 + .../checker/tests/generics_fn_called_no_arg_err.vv | 15 + .../generics_fn_called_outside_of_generic_fn.out | 6 + .../generics_fn_called_outside_of_generic_fn.vv | 5 + .../generics_fn_called_variadic_arg_mismatch.out | 14 + .../generics_fn_called_variadic_arg_mismatch.vv | 17 + .../generics_fn_return_generic_struct_err.out | 13 + .../tests/generics_fn_return_generic_struct_err.vv | 18 + .../tests/generics_method_receiver_type_err.out | 7 + .../tests/generics_method_receiver_type_err.vv | 16 + ...cs_non_generic_fn_called_like_a_generic_one.out | 7 + ...ics_non_generic_fn_called_like_a_generic_one.vv | 6 + .../tests/generics_struct_declaration_err.out | 7 + .../tests/generics_struct_declaration_err.vv | 24 + .../v/checker/tests/generics_struct_init_err.out | 14 + .../v/checker/tests/generics_struct_init_err.vv | 69 + .../checker/tests/generics_too_many_parameters.out | 6 + .../checker/tests/generics_too_many_parameters.vv | 7 + .../v/checker/tests/generics_type_ambiguous.out | 6 + .../v/checker/tests/generics_type_ambiguous.vv | 8 + .../v/checker/tests/globals/assign_no_value.out | 3 + .../v/checker/tests/globals/assign_no_value.vv | 1 + .../tests/globals/incorrect_name_global.out | 3 + .../checker/tests/globals/incorrect_name_global.vv | 1 + .../v/old/vlib/v/checker/tests/globals/no_type.out | 3 + .../v/old/vlib/v/checker/tests/globals/no_type.vv | 1 + .../v/checker/tests/globals/unexpected_eof.out | 3 + .../vlib/v/checker/tests/globals/unexpected_eof.vv | 2 + .../vlib/v/checker/tests/globals/unknown_typ.out | 11 + .../vlib/v/checker/tests/globals/unknown_typ.vv | 4 + .../v/old/vlib/v/checker/tests/globals_error.out | 6 + .../old/vlib/v/checker/tests/globals_error.run.out | 1 + .../v/old/vlib/v/checker/tests/globals_error.vv | 12 + .../globals_run/function_stored_in_global.run.out | 4 + .../tests/globals_run/function_stored_in_global.vv | 20 + .../global_array_indexed_by_global_fn.run.out | 3 + .../global_array_indexed_by_global_fn.vv | 40 + v_windows/v/old/vlib/v/checker/tests/go_expr.out | 5 + v_windows/v/old/vlib/v/checker/tests/go_expr.vv | 3 + .../v/old/vlib/v/checker/tests/go_mut_arg.out | 6 + v_windows/v/old/vlib/v/checker/tests/go_mut_arg.vv | 15 + .../v/old/vlib/v/checker/tests/go_mut_receiver.out | 6 + .../v/old/vlib/v/checker/tests/go_mut_receiver.vv | 15 + .../v/old/vlib/v/checker/tests/go_wait_or.out | 69 + v_windows/v/old/vlib/v/checker/tests/go_wait_or.vv | 41 + .../v/old/vlib/v/checker/tests/goto_label.out | 63 + v_windows/v/old/vlib/v/checker/tests/goto_label.vv | 33 + .../v/checker/tests/hex_lit_without_digit_err.out | 5 + .../v/checker/tests/hex_lit_without_digit_err.vv | 3 + .../v/checker/tests/hex_lit_wrong_digit_err.out | 5 + .../v/checker/tests/hex_lit_wrong_digit_err.vv | 3 + .../vlib/v/checker/tests/hex_literal_overflow.out | 18 + .../vlib/v/checker/tests/hex_literal_overflow.vv | 6 + .../v/checker/tests/ierror_in_return_tuple.out | 12 + .../vlib/v/checker/tests/ierror_in_return_tuple.vv | 7 + .../old/vlib/v/checker/tests/if_expr_last_stmt.out | 14 + .../old/vlib/v/checker/tests/if_expr_last_stmt.vv | 7 + .../old/vlib/v/checker/tests/if_expr_mismatch.out | 6 + .../v/old/vlib/v/checker/tests/if_expr_mismatch.vv | 4 + .../v/old/vlib/v/checker/tests/if_expr_no_else.out | 5 + .../v/old/vlib/v/checker/tests/if_expr_no_else.vv | 3 + .../vlib/v/checker/tests/if_expr_optional_err.out | 7 + .../vlib/v/checker/tests/if_expr_optional_err.vv | 10 + .../v/old/vlib/v/checker/tests/if_match_expr.out | 49 + .../v/old/vlib/v/checker/tests/if_match_expr.vv | 27 + .../old/vlib/v/checker/tests/if_match_expr_err.out | 7 + .../old/vlib/v/checker/tests/if_match_expr_err.vv | 13 + .../v/old/vlib/v/checker/tests/if_match_result.out | 34 + .../v/old/vlib/v/checker/tests/if_match_result.vv | 19 + .../old/vlib/v/checker/tests/if_non_bool_cond.out | 20 + .../v/old/vlib/v/checker/tests/if_non_bool_cond.vv | 13 + .../v/old/vlib/v/checker/tests/immutable_arg.out | 6 + .../v/old/vlib/v/checker/tests/immutable_arg.vv | 14 + .../checker/tests/immutable_array_field_assign.out | 6 + .../checker/tests/immutable_array_field_assign.vv | 10 + .../checker/tests/immutable_array_field_shift.out | 6 + .../v/checker/tests/immutable_array_field_shift.vv | 15 + .../tests/immutable_array_struct_assign.out | 6 + .../checker/tests/immutable_array_struct_assign.vv | 9 + .../checker/tests/immutable_array_struct_shift.out | 6 + .../checker/tests/immutable_array_struct_shift.vv | 9 + .../vlib/v/checker/tests/immutable_array_var.out | 6 + .../vlib/v/checker/tests/immutable_array_var.vv | 4 + .../v/checker/tests/immutable_builtin_modify.out | 11 + .../v/checker/tests/immutable_builtin_modify.vv | 5 + .../v/old/vlib/v/checker/tests/immutable_field.out | 6 + .../v/old/vlib/v/checker/tests/immutable_field.vv | 9 + .../v/checker/tests/immutable_field_postfix.out | 13 + .../v/checker/tests/immutable_field_postfix.vv | 9 + .../v/checker/tests/immutable_interface_field.out | 7 + .../v/checker/tests/immutable_interface_field.vv | 16 + .../v/old/vlib/v/checker/tests/immutable_map.out | 27 + .../v/old/vlib/v/checker/tests/immutable_map.vv | 7 + .../v/old/vlib/v/checker/tests/immutable_rec.out | 6 + .../v/old/vlib/v/checker/tests/immutable_rec.vv | 14 + .../v/checker/tests/immutable_struct_postfix.out | 13 + .../v/checker/tests/immutable_struct_postfix.vv | 10 + .../v/old/vlib/v/checker/tests/immutable_var.out | 7 + .../v/old/vlib/v/checker/tests/immutable_var.vv | 5 + .../vlib/v/checker/tests/immutable_var_postfix.out | 13 + .../vlib/v/checker/tests/immutable_var_postfix.vv | 5 + .../vlib/v/checker/tests/import_duplicate_err.out | 6 + .../vlib/v/checker/tests/import_duplicate_err.vv | 5 + .../old/vlib/v/checker/tests/import_middle_err.out | 7 + .../old/vlib/v/checker/tests/import_middle_err.vv | 8 + .../vlib/v/checker/tests/import_mod_as_mod_err.out | 5 + .../vlib/v/checker/tests/import_mod_as_mod_err.vv | 5 + .../v/checker/tests/import_mod_sub_as_sub_err.out | 5 + .../v/checker/tests/import_mod_sub_as_sub_err.vv | 5 + .../checker/tests/import_multiple_modules_err.out | 5 + .../v/checker/tests/import_multiple_modules_err.vv | 4 + .../vlib/v/checker/tests/import_not_found_err.out | 5 + .../vlib/v/checker/tests/import_not_found_err.vv | 4 + .../v/checker/tests/import_not_same_line_err.out | 6 + .../v/checker/tests/import_not_same_line_err.vv | 5 + .../vlib/v/checker/tests/import_symbol_empty.out | 3 + .../vlib/v/checker/tests/import_symbol_empty.vv | 1 + .../vlib/v/checker/tests/import_symbol_fn_err.out | 11 + .../vlib/v/checker/tests/import_symbol_fn_err.vv | 4 + .../vlib/v/checker/tests/import_symbol_invalid.out | 3 + .../vlib/v/checker/tests/import_symbol_invalid.vv | 1 + .../v/checker/tests/import_symbol_private_err.out | 48 + .../v/checker/tests/import_symbol_private_err.vv | 12 + .../v/checker/tests/import_symbol_type_err.out | 12 + .../vlib/v/checker/tests/import_symbol_type_err.vv | 4 + .../v/checker/tests/import_symbol_unclosed.out | 3 + .../vlib/v/checker/tests/import_symbol_unclosed.vv | 1 + .../old/vlib/v/checker/tests/import_syntax_err.out | 5 + .../old/vlib/v/checker/tests/import_syntax_err.vv | 4 + .../vlib/v/checker/tests/import_unused_warning.out | 5 + .../vlib/v/checker/tests/import_unused_warning.vv | 4 + .../old/vlib/v/checker/tests/in_mismatch_type.out | 77 + .../v/old/vlib/v/checker/tests/in_mismatch_type.vv | 44 + .../tests/incorrect_for_in_name_variable.out | 8 + .../tests/incorrect_for_in_name_variable.vv | 6 + .../v/checker/tests/incorrect_name_alias_type.out | 3 + .../v/checker/tests/incorrect_name_alias_type.vv | 1 + .../vlib/v/checker/tests/incorrect_name_const.out | 0 .../vlib/v/checker/tests/incorrect_name_const.vv | 3 + .../vlib/v/checker/tests/incorrect_name_enum.out | 5 + .../vlib/v/checker/tests/incorrect_name_enum.vv | 4 + .../v/checker/tests/incorrect_name_enum_field.out | 6 + .../v/checker/tests/incorrect_name_enum_field.vv | 5 + .../v/checker/tests/incorrect_name_fn_type.out | 3 + .../vlib/v/checker/tests/incorrect_name_fn_type.vv | 1 + .../v/checker/tests/incorrect_name_function.out | 3 + .../v/checker/tests/incorrect_name_function.vv | 1 + .../v/checker/tests/incorrect_name_interface.out | 3 + .../v/checker/tests/incorrect_name_interface.vv | 1 + .../tests/incorrect_name_interface_method.out | 5 + .../tests/incorrect_name_interface_method.vv | 3 + .../vlib/v/checker/tests/incorrect_name_module.out | 3 + .../vlib/v/checker/tests/incorrect_name_module.vv | 1 + .../vlib/v/checker/tests/incorrect_name_struct.out | 3 + .../vlib/v/checker/tests/incorrect_name_struct.vv | 1 + .../checker/tests/incorrect_name_struct_field.out | 5 + .../v/checker/tests/incorrect_name_struct_field.vv | 3 + .../v/checker/tests/incorrect_name_sum_type.out | 12 + .../v/checker/tests/incorrect_name_sum_type.vv | 6 + .../v/checker/tests/incorrect_name_variable.out | 6 + .../v/checker/tests/incorrect_name_variable.vv | 4 + .../v/old/vlib/v/checker/tests/index_expr.out | 69 + v_windows/v/old/vlib/v/checker/tests/index_expr.vv | 21 + v_windows/v/old/vlib/v/checker/tests/infix_err.out | 81 + v_windows/v/old/vlib/v/checker/tests/infix_err.vv | 24 + .../v/checker/tests/int_modulo_by_zero_err.out | 5 + .../vlib/v/checker/tests/int_modulo_by_zero_err.vv | 3 + .../tests/interface_implementing_interface.out | 6 + .../tests/interface_implementing_interface.vv | 16 + ...interface_implementing_own_interface_method.out | 7 + .../interface_implementing_own_interface_method.vv | 7 + .../vlib/v/checker/tests/interface_init_err.out | 6 + .../old/vlib/v/checker/tests/interface_init_err.vv | 16 + .../tests/interface_return_parameter_err.out | 14 + .../tests/interface_return_parameter_err.vv | 4 + .../tests/interface_too_many_embedding_levels.out | 7 + .../tests/interface_too_many_embedding_levels.vv | 431 + .../tests/interpolation_recursive_str_err.out | 7 + .../tests/interpolation_recursive_str_err.vv | 15 + .../old/vlib/v/checker/tests/invalid_char_err.out | 3 + .../v/old/vlib/v/checker/tests/invalid_char_err.vv | 1 + .../old/vlib/v/checker/tests/invalid_property.out | 18 + .../v/old/vlib/v/checker/tests/invalid_property.vv | 6 + .../v/checker/tests/invalid_vweb_param_type.out | 7 + .../v/checker/tests/invalid_vweb_param_type.vv | 12 + .../tests/invert_other_types_bits_error.out | 26 + .../checker/tests/invert_other_types_bits_error.vv | 6 + .../v/old/vlib/v/checker/tests/is_type_invalid.out | 14 + .../v/old/vlib/v/checker/tests/is_type_invalid.vv | 21 + .../old/vlib/v/checker/tests/is_type_not_exist.out | 7 + .../old/vlib/v/checker/tests/is_type_not_exist.vv | 11 + .../v/checker/tests/labelled_break_continue.out | 35 + .../v/checker/tests/labelled_break_continue.vv | 26 + .../vlib/v/checker/tests/lock_already_locked.out | 13 + .../vlib/v/checker/tests/lock_already_locked.vv | 16 + .../vlib/v/checker/tests/lock_already_rlocked.out | 13 + .../vlib/v/checker/tests/lock_already_rlocked.vv | 16 + .../v/old/vlib/v/checker/tests/lock_const.out | 7 + v_windows/v/old/vlib/v/checker/tests/lock_const.vv | 11 + .../v/old/vlib/v/checker/tests/lock_needed.out | 27 + .../v/old/vlib/v/checker/tests/lock_needed.vv | 31 + .../v/old/vlib/v/checker/tests/lock_nonshared.out | 7 + .../v/old/vlib/v/checker/tests/lock_nonshared.vv | 14 + .../vlib/v/checker/tests/main_and_script_err.out | 5 + .../vlib/v/checker/tests/main_and_script_err.vv | 4 + .../v/old/vlib/v/checker/tests/main_args_err.out | 5 + .../v/old/vlib/v/checker/tests/main_args_err.vv | 3 + .../v/old/vlib/v/checker/tests/main_called_err.out | 5 + .../v/old/vlib/v/checker/tests/main_called_err.vv | 3 + .../old/vlib/v/checker/tests/main_no_body_err.out | 3 + .../v/old/vlib/v/checker/tests/main_no_body_err.vv | 1 + .../v/old/vlib/v/checker/tests/main_return_err.out | 5 + .../v/old/vlib/v/checker/tests/main_return_err.vv | 3 + .../v/old/vlib/v/checker/tests/map_delete.out | 20 + v_windows/v/old/vlib/v/checker/tests/map_delete.vv | 11 + .../v/checker/tests/map_func_void_return_err.out | 6 + .../v/checker/tests/map_func_void_return_err.vv | 7 + .../v/checker/tests/map_init_invalid_syntax.out | 6 + .../v/checker/tests/map_init_invalid_syntax.vv | 4 + .../v/checker/tests/map_init_key_duplicate_err.out | 13 + .../v/checker/tests/map_init_key_duplicate_err.vv | 10 + .../vlib/v/checker/tests/map_init_wrong_type.out | 21 + .../vlib/v/checker/tests/map_init_wrong_type.vv | 7 + v_windows/v/old/vlib/v/checker/tests/map_ops.out | 20 + v_windows/v/old/vlib/v/checker/tests/map_ops.vv | 6 + .../old/vlib/v/checker/tests/map_unknown_value.out | 5 + .../old/vlib/v/checker/tests/map_unknown_value.vv | 3 + .../vlib/v/checker/tests/match_alias_type_err.out | 7 + .../vlib/v/checker/tests/match_alias_type_err.vv | 16 + .../v/checker/tests/match_duplicate_branch.out | 42 + .../vlib/v/checker/tests/match_duplicate_branch.vv | 61 + .../vlib/v/checker/tests/match_else_last_expr.out | 7 + .../vlib/v/checker/tests/match_else_last_expr.vv | 7 + .../tests/match_expr_and_expected_type_error.out | 14 + .../tests/match_expr_and_expected_type_error.vv | 11 + .../v/old/vlib/v/checker/tests/match_expr_else.out | 21 + .../v/old/vlib/v/checker/tests/match_expr_else.vv | 41 + .../v/checker/tests/match_expr_empty_branch.out | 6 + .../v/checker/tests/match_expr_empty_branch.vv | 4 + .../tests/match_expr_non_void_stmt_last.out | 7 + .../checker/tests/match_expr_non_void_stmt_last.vv | 17 + .../vlib/v/checker/tests/match_invalid_type.out | 21 + .../old/vlib/v/checker/tests/match_invalid_type.vv | 29 + .../tests/match_return_mismatch_type_err.out | 7 + .../tests/match_return_mismatch_type_err.vv | 7 + .../checker/tests/match_sumtype_multiple_types.out | 14 + .../checker/tests/match_sumtype_multiple_types.vv | 33 + .../vlib/v/checker/tests/match_undefined_cond.out | 21 + .../vlib/v/checker/tests/match_undefined_cond.vv | 10 + .../vlib/v/checker/tests/method_array_slice.out | 7 + .../old/vlib/v/checker/tests/method_array_slice.vv | 7 + .../v/checker/tests/method_generic_infer_err.out | 6 + .../v/checker/tests/method_generic_infer_err.vv | 10 + .../vlib/v/checker/tests/method_op_alias_err.out | 42 + .../vlib/v/checker/tests/method_op_alias_err.vv | 18 + .../v/old/vlib/v/checker/tests/method_op_err.out | 69 + .../v/old/vlib/v/checker/tests/method_op_err.vv | 40 + .../vlib/v/checker/tests/method_wrong_arg_type.out | 13 + .../vlib/v/checker/tests/method_wrong_arg_type.vv | 19 + .../v/checker/tests/minus_op_wrong_type_err.out | 62 + .../v/checker/tests/minus_op_wrong_type_err.vv | 20 + .../vlib/v/checker/tests/mismatched_ptr_op_ptr.out | 14 + .../vlib/v/checker/tests/mismatched_ptr_op_ptr.vv | 8 + .../v/checker/tests/missing_c_lib_header_1.out | 1 + .../vlib/v/checker/tests/missing_c_lib_header_1.vv | 6 + .../missing_c_lib_header_with_explanation_2.out | 1 + .../missing_c_lib_header_with_explanation_2.vv | 6 + .../vlib/v/checker/tests/mod_op_wrong_type_err.out | 56 + .../vlib/v/checker/tests/mod_op_wrong_type_err.vv | 13 + .../vlib/v/checker/tests/modify_const_with_ref.out | 14 + .../vlib/v/checker/tests/modify_const_with_ref.vv | 13 + .../checker/tests/module_not_at_same_line_err.out | 6 + .../v/checker/tests/module_not_at_same_line_err.vv | 5 + .../module_alias_started_with_underscore.out | 7 + .../module_alias_started_with_underscore/main.v | 7 + .../underscore.v | 5 + .../checker/tests/modules/overload_return_type.out | 6 + .../tests/modules/overload_return_type/main.v | 15 + .../tests/modules/overload_return_type/point.v | 11 + .../vlib/v/checker/tests/mul_op_wrong_type_err.out | 57 + .../vlib/v/checker/tests/mul_op_wrong_type_err.vv | 17 + .../tests/multi_const_field_name_duplicate_err.out | 6 + .../tests/multi_const_field_name_duplicate_err.vv | 5 + .../v/old/vlib/v/checker/tests/multi_names_err.out | 5 + .../v/old/vlib/v/checker/tests/multi_names_err.vv | 3 + .../v/checker/tests/multi_value_method_err.out | 3 + .../vlib/v/checker/tests/multi_value_method_err.vv | 1 + v_windows/v/old/vlib/v/checker/tests/mut_arg.out | 25 + v_windows/v/old/vlib/v/checker/tests/mut_arg.vv | 11 + .../old/vlib/v/checker/tests/mut_args_warning.out | 5 + .../v/old/vlib/v/checker/tests/mut_args_warning.vv | 5 + .../tests/mut_array_get_element_address_err.out | 7 + .../tests/mut_array_get_element_address_err.vv | 5 + v_windows/v/old/vlib/v/checker/tests/mut_int.out | 6 + v_windows/v/old/vlib/v/checker/tests/mut_int.vv | 5 + .../tests/mut_map_get_value_address_err.out | 7 + .../checker/tests/mut_map_get_value_address_err.vv | 5 + .../v/old/vlib/v/checker/tests/mut_receiver.out | 14 + .../v/old/vlib/v/checker/tests/mut_receiver.vv | 13 + .../old/vlib/v/checker/tests/mut_receiver_lit.out | 5 + .../v/old/vlib/v/checker/tests/mut_receiver_lit.vv | 10 + .../checker/tests/negative_assign_to_unsigned.out | 7 + .../v/checker/tests/negative_assign_to_unsigned.vv | 5 + .../v/old/vlib/v/checker/tests/nested_aliases.out | 4 + .../v/old/vlib/v/checker/tests/nested_aliases.vv | 2 + .../v/old/vlib/v/checker/tests/no_heap_struct.out | 21 + .../v/old/vlib/v/checker/tests/no_heap_struct.vv | 25 + .../checker/tests/no_interface_instantiation_a.out | 6 + .../checker/tests/no_interface_instantiation_a.vv | 5 + .../checker/tests/no_interface_instantiation_b.out | 6 + .../checker/tests/no_interface_instantiation_b.vv | 7 + .../checker/tests/no_interface_instantiation_c.out | 7 + .../checker/tests/no_interface_instantiation_c.vv | 11 + .../old/vlib/v/checker/tests/no_interface_str.out | 6 + .../v/old/vlib/v/checker/tests/no_interface_str.vv | 19 + .../v/old/vlib/v/checker/tests/no_main_mod.out | 3 + .../v/old/vlib/v/checker/tests/no_main_mod.vv | 1 + .../vlib/v/checker/tests/no_main_println_err.out | 3 + .../vlib/v/checker/tests/no_main_println_err.vv | 1 + .../tests/no_method_on_interface_propagation.out | 7 + .../tests/no_method_on_interface_propagation.vv | 20 + .../v/old/vlib/v/checker/tests/no_pub_in_main.out | 49 + .../tests/no_warning_for_in_mut_var_unused.out | 0 .../tests/no_warning_for_in_mut_var_unused.vv | 7 + .../vlib/v/checker/tests/non_lvalue_as_voidptr.out | 5 + .../vlib/v/checker/tests/non_lvalue_as_voidptr.vv | 5 + .../checker/tests/non_matching_functional_args.out | 16 + .../checker/tests/non_matching_functional_args.vv | 33 + .../vlib/v/checker/tests/none_type_cast_err.out | 75 + .../old/vlib/v/checker/tests/none_type_cast_err.vv | 13 + .../tests/noreturn_with_non_empty_loop_at_end.out | 13 + .../tests/noreturn_with_non_empty_loop_at_end.vv | 19 + .../vlib/v/checker/tests/noreturn_with_return.out | 19 + .../vlib/v/checker/tests/noreturn_with_return.vv | 19 + ...urn_without_loop_or_another_noreturn_at_end.out | 13 + ...turn_without_loop_or_another_noreturn_at_end.vv | 16 + .../v/checker/tests/oct_lit_without_digit_err.out | 5 + .../v/checker/tests/oct_lit_without_digit_err.vv | 3 + .../v/checker/tests/oct_lit_wrong_digit_err.out | 5 + .../v/checker/tests/oct_lit_wrong_digit_err.vv | 3 + .../v/old/vlib/v/checker/tests/optional_fn_err.out | 168 + .../v/old/vlib/v/checker/tests/optional_fn_err.vv | 76 + .../checker/tests/optional_in_println_mismatch.out | 6 + .../checker/tests/optional_in_println_mismatch.vv | 7 + .../checker/tests/optional_interface_mismatch.out | 6 + .../v/checker/tests/optional_interface_mismatch.vv | 12 + .../v/checker/tests/optional_or_block_mismatch.out | 7 + .../v/checker/tests/optional_or_block_mismatch.vv | 12 + .../v/checker/tests/optional_or_block_none_err.out | 7 + .../v/checker/tests/optional_or_block_none_err.vv | 22 + ...or_block_returns_value_of_incompatible_type.out | 7 + ..._or_block_returns_value_of_incompatible_type.vv | 16 + .../v/checker/tests/optional_propagate_nested.out | 14 + .../v/checker/tests/optional_propagate_nested.vv | 31 + .../v/checker/tests/optional_type_call_err.out | 6 + .../vlib/v/checker/tests/optional_type_call_err.vv | 5 + v_windows/v/old/vlib/v/checker/tests/or_err.out | 14 + v_windows/v/old/vlib/v/checker/tests/or_err.vv | 13 + .../v/checker/tests/or_expr_types_mismatch.out | 14 + .../vlib/v/checker/tests/or_expr_types_mismatch.vv | 17 + .../old/vlib/v/checker/tests/orm_empty_struct.out | 7 + .../v/old/vlib/v/checker/tests/orm_empty_struct.vv | 11 + v_windows/v/old/vlib/v/checker/tests/os_prefix.out | 18 + v_windows/v/old/vlib/v/checker/tests/os_prefix.vv | 7 + .../old/vlib/v/checker/tests/overflow_int_err.out | 14 + .../v/old/vlib/v/checker/tests/overflow_int_err.vv | 10 + .../vlib/v/checker/tests/overload_return_type.out | 6 + .../vlib/v/checker/tests/overload_return_type.vv | 15 + .../old/vlib/v/checker/tests/oversized_int_lit.out | 8 + .../old/vlib/v/checker/tests/oversized_int_lit.vv | 2 + .../v/old/vlib/v/checker/tests/pass_mut_lit.out | 5 + .../v/old/vlib/v/checker/tests/pass_mut_lit.vv | 10 + .../tests/passing_expr_to_fn_expecting_voidptr.out | 5 + .../tests/passing_expr_to_fn_expecting_voidptr.vv | 3 + .../v/old/vlib/v/checker/tests/pointer_ops.out | 49 + .../v/old/vlib/v/checker/tests/pointer_ops.vv | 13 + .../v/old/vlib/v/checker/tests/prefix_err.out | 95 + v_windows/v/old/vlib/v/checker/tests/prefix_err.vv | 22 + .../checker/tests/prefix_expr_decl_assign_err.out | 12 + .../v/checker/tests/prefix_expr_decl_assign_err.vv | 4 + .../v/old/vlib/v/checker/tests/print_char.out | 3 + v_windows/v/old/vlib/v/checker/tests/print_char.vv | 1 + .../println_can_not_print_void_expressions.out | 27 + .../println_can_not_print_void_expressions.vv | 7 + .../v/old/vlib/v/checker/tests/ptr_assign.out | 6 + v_windows/v/old/vlib/v/checker/tests/ptr_assign.vv | 4 + .../tests/receiver_unknown_type_single_letter.out | 4 + .../tests/receiver_unknown_type_single_letter.vv | 2 + .../v/checker/tests/recursive_interface_err.out | 5 + .../v/checker/tests/recursive_interface_err.vv | 3 + .../tests/reference_field_must_be_initialized.out | 7 + .../tests/reference_field_must_be_initialized.vv | 10 + .../old/vlib/v/checker/tests/reference_return.out | 7 + .../v/old/vlib/v/checker/tests/reference_return.vv | 14 + .../vlib/v/checker/tests/return_count_mismatch.out | 34 + .../vlib/v/checker/tests/return_count_mismatch.vv | 24 + .../tests/return_duplicate_with_none_err_a.out | 7 + .../tests/return_duplicate_with_none_err_a.vv | 9 + .../tests/return_duplicate_with_none_err_b.out | 7 + .../tests/return_duplicate_with_none_err_b.vv | 9 + .../vlib/v/checker/tests/return_fixed_array.out | 12 + .../old/vlib/v/checker/tests/return_fixed_array.vv | 7 + .../v/checker/tests/return_missing_comp_if.out | 7 + .../vlib/v/checker/tests/return_missing_comp_if.vv | 9 + .../tests/return_missing_comp_if_nested.out | 7 + .../checker/tests/return_missing_comp_if_nested.vv | 17 + .../tests/return_missing_if_else_simple.out | 12 + .../checker/tests/return_missing_if_else_simple.vv | 35 + .../v/checker/tests/return_missing_if_match.out | 7 + .../v/checker/tests/return_missing_if_match.vv | 13 + .../v/checker/tests/return_missing_match_if.out | 7 + .../v/checker/tests/return_missing_match_if.vv | 15 + .../checker/tests/return_missing_match_simple.out | 12 + .../v/checker/tests/return_missing_match_simple.vv | 12 + .../vlib/v/checker/tests/return_missing_nested.out | 7 + .../vlib/v/checker/tests/return_missing_nested.vv | 11 + .../vlib/v/checker/tests/return_missing_simple.out | 7 + .../vlib/v/checker/tests/return_missing_simple.vv | 7 + .../v/checker/tests/return_ref_as_no_ref_bug.out | 7 + .../v/checker/tests/return_ref_as_no_ref_bug.vv | 14 + .../v/old/vlib/v/checker/tests/return_type.out | 6 + .../v/old/vlib/v/checker/tests/return_type.vv | 5 + .../v/checker/tests/return_working_comp_if.out | 0 .../vlib/v/checker/tests/return_working_comp_if.vv | 9 + .../tests/return_working_comp_if_nested.out | 0 .../checker/tests/return_working_comp_if_nested.vv | 17 + .../v/checker/tests/return_working_if_match.out | 0 .../v/checker/tests/return_working_if_match.vv | 13 + .../v/checker/tests/return_working_match_if.out | 0 .../v/checker/tests/return_working_match_if.vv | 15 + .../vlib/v/checker/tests/return_working_nested.out | 0 .../vlib/v/checker/tests/return_working_nested.vv | 12 + .../vlib/v/checker/tests/return_working_simple.out | 0 .../vlib/v/checker/tests/return_working_simple.vv | 9 + .../vlib/v/checker/tests/return_working_two_if.out | 0 .../vlib/v/checker/tests/return_working_two_if.vv | 17 + .../vlib/v/checker/tests/return_working_unsafe.out | 0 .../vlib/v/checker/tests/return_working_unsafe.vv | 7 + .../checker/tests/returns/return_missing_simple.vv | 7 + .../tests/rshift_op_wrong_left_type_err.out | 5 + .../checker/tests/rshift_op_wrong_left_type_err.vv | 3 + .../tests/rshift_op_wrong_right_type_err.out | 5 + .../tests/rshift_op_wrong_right_type_err.vv | 3 + ...oreturn_fn_can_be_used_instead_of_panic.run.out | 1 + .../noreturn_fn_can_be_used_instead_of_panic.vv | 14 + .../tests/run/unused_variable_warning.run.out | 15 + .../v/checker/tests/run/unused_variable_warning.vv | 6 + .../v/checker/tests/selective_const_import.out | 3 + .../vlib/v/checker/tests/selective_const_import.vv | 1 + .../vlib/v/checker/tests/selector_expr_assign.out | 6 + .../vlib/v/checker/tests/selector_expr_assign.vv | 8 + .../v/checker/tests/selector_expr_optional_err.out | 6 + .../v/checker/tests/selector_expr_optional_err.vv | 5 + .../v/old/vlib/v/checker/tests/shared_bad_args.out | 83 + .../v/old/vlib/v/checker/tests/shared_bad_args.vv | 81 + .../vlib/v/checker/tests/shared_element_lock.out | 27 + .../vlib/v/checker/tests/shared_element_lock.vv | 46 + .../v/old/vlib/v/checker/tests/shared_lock.out | 21 + .../v/old/vlib/v/checker/tests/shared_lock.vv | 23 + .../vlib/v/checker/tests/shared_type_mismatch.out | 7 + .../vlib/v/checker/tests/shared_type_mismatch.vv | 18 + .../checker/tests/shift_op_wrong_left_type_err.out | 5 + .../checker/tests/shift_op_wrong_left_type_err.vv | 3 + .../tests/shift_op_wrong_right_type_err.out | 5 + .../checker/tests/shift_op_wrong_right_type_err.vv | 3 + .../v/checker/tests/short_struct_wrong_number.out | 13 + .../v/checker/tests/short_struct_wrong_number.vv | 9 + .../vlib/v/checker/tests/slice_reassignment.out | 7 + .../old/vlib/v/checker/tests/slice_reassignment.vv | 5 + .../sort_method_called_on_immutable_receiver.out | 13 + .../sort_method_called_on_immutable_receiver.vv | 9 + .../tests/static_vars_in_translated_mode.out | 6 + .../tests/static_vars_in_translated_mode.vv | 12 + .../old/vlib/v/checker/tests/store_string_err.out | 6 + .../v/old/vlib/v/checker/tests/store_string_err.vv | 6 + .../v/checker/tests/str_method_0_arguments.out | 7 + .../vlib/v/checker/tests/str_method_0_arguments.vv | 11 + .../v/checker/tests/str_method_return_string.out | 7 + .../v/checker/tests/str_method_return_string.vv | 11 + .../vlib/v/checker/tests/string_char_null_err.out | 5 + .../vlib/v/checker/tests/string_char_null_err.vv | 3 + .../vlib/v/checker/tests/string_escape_u_err_a.out | 5 + .../vlib/v/checker/tests/string_escape_u_err_a.vv | 3 + .../vlib/v/checker/tests/string_escape_u_err_b.out | 5 + .../vlib/v/checker/tests/string_escape_u_err_b.vv | 3 + .../vlib/v/checker/tests/string_escape_x_err_a.out | 5 + .../vlib/v/checker/tests/string_escape_x_err_a.vv | 3 + .../vlib/v/checker/tests/string_escape_x_err_b.out | 5 + .../vlib/v/checker/tests/string_escape_x_err_b.vv | 3 + .../v/checker/tests/string_index_assign_error.out | 7 + .../v/checker/tests/string_index_assign_error.vv | 4 + .../v/checker/tests/string_index_non_int_err.out | 20 + .../v/checker/tests/string_index_non_int_err.vv | 6 + .../tests/string_interpolation_invalid_fmt.out | 7 + .../tests/string_interpolation_invalid_fmt.vv | 5 + .../tests/string_interpolation_wrong_fmt.out | 63 + .../tests/string_interpolation_wrong_fmt.vv | 24 + .../tests/struct_assigned_to_pointer_to_struct.out | 7 + .../tests/struct_assigned_to_pointer_to_struct.vv | 7 + .../tests/struct_cast_to_struct_generic_err.out | 6 + .../tests/struct_cast_to_struct_generic_err.vv | 12 + .../tests/struct_cast_to_struct_mut_err_a.out | 6 + .../tests/struct_cast_to_struct_mut_err_a.vv | 13 + .../tests/struct_cast_to_struct_mut_err_b.out | 6 + .../tests/struct_cast_to_struct_mut_err_b.vv | 13 + .../tests/struct_cast_to_struct_pub_err_a.out | 6 + .../tests/struct_cast_to_struct_pub_err_a.vv | 13 + .../tests/struct_cast_to_struct_pub_err_b.out | 6 + .../tests/struct_cast_to_struct_pub_err_b.vv | 13 + .../v/checker/tests/struct_embed_invalid_type.out | 6 + .../v/checker/tests/struct_embed_invalid_type.vv | 5 + .../tests/struct_field_name_duplicate_err.out | 6 + .../tests/struct_field_name_duplicate_err.vv | 4 + .../vlib/v/checker/tests/struct_field_type_err.out | 43 + .../vlib/v/checker/tests/struct_field_type_err.vv | 20 + .../checker/tests/struct_init_update_type_err.out | 35 + .../v/checker/tests/struct_init_update_type_err.vv | 35 + .../old/vlib/v/checker/tests/struct_pub_field.out | 6 + .../v/old/vlib/v/checker/tests/struct_pub_field.vv | 10 + .../vlib/v/checker/tests/struct_required_field.out | 7 + .../vlib/v/checker/tests/struct_required_field.vv | 16 + .../v/checker/tests/struct_required_fn_field.out | 7 + .../v/checker/tests/struct_required_fn_field.vv | 16 + .../v/checker/tests/struct_short_init_warning.out | 13 + .../v/checker/tests/struct_short_init_warning.vv | 11 + .../vlib/v/checker/tests/struct_type_cast_err.out | 63 + .../vlib/v/checker/tests/struct_type_cast_err.vv | 17 + .../vlib/v/checker/tests/struct_unknown_field.out | 7 + .../vlib/v/checker/tests/struct_unknown_field.vv | 11 + .../v/checker/tests/struct_unneeded_default.out | 20 + .../v/checker/tests/struct_unneeded_default.vv | 8 + v_windows/v/old/vlib/v/checker/tests/sum.out | 28 + v_windows/v/old/vlib/v/checker/tests/sum.vv | 14 + .../tests/sum_type_assign_non_variant_err.out | 7 + .../tests/sum_type_assign_non_variant_err.vv | 13 + .../tests/sum_type_common_fields_alias_error.out | 20 + .../tests/sum_type_common_fields_alias_error.vv | 38 + .../checker/tests/sum_type_common_fields_error.out | 6 + .../checker/tests/sum_type_common_fields_error.vv | 54 + .../v/old/vlib/v/checker/tests/sum_type_exists.out | 3 + .../v/old/vlib/v/checker/tests/sum_type_exists.vv | 1 + .../vlib/v/checker/tests/sum_type_infix_err.out | 14 + .../old/vlib/v/checker/tests/sum_type_infix_err.vv | 8 + .../tests/sum_type_multiple_type_define.out | 5 + .../checker/tests/sum_type_multiple_type_define.vv | 3 + .../v/checker/tests/sum_type_mutable_cast_err.out | 14 + .../v/checker/tests/sum_type_mutable_cast_err.vv | 21 + .../v/checker/tests/sum_type_ref_variant_err.out | 18 + .../v/checker/tests/sum_type_ref_variant_err.vv | 9 + .../v/checker/tests/sumtype_in_sumtype_err.out | 3 + .../vlib/v/checker/tests/sumtype_in_sumtype_err.vv | 1 + .../v/checker/tests/sumtype_mismatched_type.out | 5 + .../v/checker/tests/sumtype_mismatched_type.vv | 4 + .../old/vlib/v/checker/tests/templates/index.html | 1 + .../test_functions_should_not_return_test.out | 7 + .../tests/test_functions_should_not_return_test.vv | 17 + .../v/checker/tests/trailing_comma_struct_attr.out | 6 + .../v/checker/tests/trailing_comma_struct_attr.vv | 4 + .../v/checker/tests/type_cast_optional_err.out | 5 + .../vlib/v/checker/tests/type_cast_optional_err.vv | 3 + .../v/checker/tests/typedef_attr_v_struct_err.out | 6 + .../v/checker/tests/typedef_attr_v_struct_err.vv | 7 + .../v/checker/tests/undefined_ident_of_struct.out | 7 + .../v/checker/tests/undefined_ident_of_struct.vv | 9 + .../v/old/vlib/v/checker/tests/unexpected_or.out | 6 + .../v/old/vlib/v/checker/tests/unexpected_or.vv | 7 + .../v/checker/tests/unexpected_or_propagate.out | 7 + .../v/checker/tests/unexpected_or_propagate.vv | 12 + .../old/vlib/v/checker/tests/unfinished_string.out | 2 + .../old/vlib/v/checker/tests/unfinished_string.vv | 1 + .../v/checker/tests/unimplemented_interface_a.out | 6 + .../v/checker/tests/unimplemented_interface_a.vv | 11 + .../v/checker/tests/unimplemented_interface_b.out | 7 + .../v/checker/tests/unimplemented_interface_b.vv | 14 + .../v/checker/tests/unimplemented_interface_c.out | 7 + .../v/checker/tests/unimplemented_interface_c.vv | 13 + .../v/checker/tests/unimplemented_interface_d.out | 7 + .../v/checker/tests/unimplemented_interface_d.vv | 13 + .../v/checker/tests/unimplemented_interface_e.out | 15 + .../v/checker/tests/unimplemented_interface_e.vv | 14 + .../v/checker/tests/unimplemented_interface_f.out | 7 + .../v/checker/tests/unimplemented_interface_f.vv | 12 + .../v/checker/tests/unimplemented_interface_g.out | 7 + .../tests/unimplemented_interface_g.vv.disabled | 14 + .../v/checker/tests/unimplemented_interface_h.out | 6 + .../v/checker/tests/unimplemented_interface_h.vv | 10 + .../v/checker/tests/unimplemented_interface_i.out | 6 + .../v/checker/tests/unimplemented_interface_i.vv | 12 + .../v/checker/tests/unimplemented_interface_j.out | 6 + .../v/checker/tests/unimplemented_interface_j.vv | 13 + .../vlib/v/checker/tests/union_unsafe_fields.out | 13 + .../vlib/v/checker/tests/union_unsafe_fields.vv | 12 + .../checker/tests/unknown_array_element_type_b.out | 7 + .../checker/tests/unknown_array_element_type_b.vv | 9 + .../v/old/vlib/v/checker/tests/unknown_as_type.out | 8 + .../v/old/vlib/v/checker/tests/unknown_as_type.vv | 13 + .../vlib/v/checker/tests/unknown_comptime_expr.out | 42 + .../vlib/v/checker/tests/unknown_comptime_expr.vv | 22 + .../v/old/vlib/v/checker/tests/unknown_field.out | 6 + .../v/old/vlib/v/checker/tests/unknown_field.vv | 8 + .../vlib/v/checker/tests/unknown_generic_type.out | 7 + .../vlib/v/checker/tests/unknown_generic_type.vv | 8 + .../v/old/vlib/v/checker/tests/unknown_method.out | 6 + .../v/old/vlib/v/checker/tests/unknown_method.vv | 8 + .../checker/tests/unknown_method_suggest_name.out | 22 + .../v/checker/tests/unknown_method_suggest_name.vv | 31 + .../v/checker/tests/unknown_sizeof_type_err_a.out | 6 + .../v/checker/tests/unknown_sizeof_type_err_a.vv | 15 + .../v/checker/tests/unknown_sizeof_type_err_b.out | 6 + .../v/checker/tests/unknown_sizeof_type_err_b.vv | 15 + .../tests/unknown_struct_field_suggest_name.out | 15 + .../tests/unknown_struct_field_suggest_name.vv | 14 + .../vlib/v/checker/tests/unknown_struct_name.out | 6 + .../vlib/v/checker/tests/unknown_struct_name.vv | 5 + .../vlib/v/checker/tests/unknown_var_assign.out | 5 + .../old/vlib/v/checker/tests/unknown_var_assign.vv | 3 + .../v/checker/tests/unnecessary_parenthesis.out | 20 + .../v/checker/tests/unnecessary_parenthesis.vv | 9 + .../old/vlib/v/checker/tests/unreachable_code.out | 7 + .../v/old/vlib/v/checker/tests/unreachable_code.vv | 8 + .../tests/unsafe_c_calls_should_be_checked.out | 13 + .../tests/unsafe_c_calls_should_be_checked.vv | 6 + .../v/checker/tests/unsafe_fixed_array_assign.out | 7 + .../v/checker/tests/unsafe_fixed_array_assign.vv | 10 + ...unsafe_pointer_arithmetic_should_be_checked.out | 28 + .../unsafe_pointer_arithmetic_should_be_checked.vv | 15 + .../v/old/vlib/v/checker/tests/unsafe_required.out | 20 + .../v/old/vlib/v/checker/tests/unsafe_required.vv | 21 + .../v/checker/tests/unwrapped_optional_infix.out | 6 + .../v/checker/tests/unwrapped_optional_infix.vv | 5 + .../tests/use_deprecated_function_warning.out | 20 + .../tests/use_deprecated_function_warning.vv | 24 + .../vlib/v/checker/tests/var_duplicate_const.out | 7 + .../vlib/v/checker/tests/var_duplicate_const.vv | 6 + .../old/vlib/v/checker/tests/var_eval_not_used.out | 6 + .../old/vlib/v/checker/tests/var_eval_not_used.vv | 7 + .../v/checker/tests/var_eval_not_used_scope.out | 7 + .../v/checker/tests/var_eval_not_used_scope.vv | 9 + .../checker/tests/var_used_before_declaration.out | 6 + .../v/checker/tests/var_used_before_declaration.vv | 5 + .../old/vlib/v/checker/tests/void_fn_as_value.out | 7 + .../v/old/vlib/v/checker/tests/void_fn_as_value.vv | 9 + .../tests/void_function_assign_to_string.out | 7 + .../tests/void_function_assign_to_string.vv | 8 + .../old/vlib/v/checker/tests/void_optional_err.out | 5 + .../old/vlib/v/checker/tests/void_optional_err.vv | 7 + .../vlib/v/checker/tests/vweb_routing_checks.out | 14 + .../vlib/v/checker/tests/vweb_routing_checks.vv | 47 + .../vlib/v/checker/tests/vweb_tmpl_used_var.out | 1 + .../old/vlib/v/checker/tests/vweb_tmpl_used_var.vv | 14 + .../tests/warnings_for_string_c2v_calls.out | 7 + .../checker/tests/warnings_for_string_c2v_calls.vv | 11 + .../v/checker/tests/wrong_propagate_ret_type.out | 7 + .../v/checker/tests/wrong_propagate_ret_type.vv | 8 + v_windows/v/old/vlib/v/compiler_errors_test.v | 337 + v_windows/v/old/vlib/v/depgraph/depgraph.v | 218 + v_windows/v/old/vlib/v/doc/comment.v | 25 + v_windows/v/old/vlib/v/doc/doc.v | 525 + v_windows/v/old/vlib/v/doc/doc_test.v | 18 + v_windows/v/old/vlib/v/doc/module.v | 87 + v_windows/v/old/vlib/v/doc/node.v | 70 + v_windows/v/old/vlib/v/doc/utils.v | 156 + v_windows/v/old/vlib/v/dotgraph/dotgraph.c.v | 8 + v_windows/v/old/vlib/v/dotgraph/dotgraph.v | 81 + v_windows/v/old/vlib/v/embed_file/embed_file.v | 105 + .../v/old/vlib/v/embed_file/embed_file_test.v | 25 + v_windows/v/old/vlib/v/embed_file/v.png | Bin 0 -> 603 bytes v_windows/v/old/vlib/v/errors/errors.v | 38 + v_windows/v/old/vlib/v/eval/eval.v | 99 + v_windows/v/old/vlib/v/fmt/align.v | 56 + v_windows/v/old/vlib/v/fmt/asm.v | 185 + v_windows/v/old/vlib/v/fmt/attrs.v | 60 + v_windows/v/old/vlib/v/fmt/comments.v | 141 + v_windows/v/old/vlib/v/fmt/fmt.v | 2381 ++ v_windows/v/old/vlib/v/fmt/fmt_keep_test.v | 115 + v_windows/v/old/vlib/v/fmt/fmt_test.v | 75 + v_windows/v/old/vlib/v/fmt/fmt_vlib_test.v | 73 + v_windows/v/old/vlib/v/fmt/struct.v | 288 + .../old/vlib/v/fmt/tests/anon_fn_as_param_keep.vv | 20 + .../v/old/vlib/v/fmt/tests/anon_fn_call_keep.vv | 7 + .../v/old/vlib/v/fmt/tests/anon_fn_expected.vv | 24 + v_windows/v/old/vlib/v/fmt/tests/anon_fn_input.vv | 23 + .../vlib/v/fmt/tests/array_decomposition_keep.vv | 8 + .../v/fmt/tests/array_init_comment_ending_keep.vv | 23 + .../v/fmt/tests/array_init_eol_comments_keep.vv | 104 + .../v/old/vlib/v/fmt/tests/array_init_expected.vv | 35 + .../v/fmt/tests/array_init_inline_comments_keep.vv | 2 + .../v/old/vlib/v/fmt/tests/array_init_input.vv | 31 + .../v/old/vlib/v/fmt/tests/array_init_keep.vv | 9 + .../v/old/vlib/v/fmt/tests/array_newlines_keep.vv | 17 + .../old/vlib/v/fmt/tests/array_slices_expected.vv | 12 + .../v/old/vlib/v/fmt/tests/array_slices_input.vv | 14 + .../v/old/vlib/v/fmt/tests/array_static_keep.vv | 14 + .../v/old/vlib/v/fmt/tests/assembly/asm_keep.vv | 10 + .../v/old/vlib/v/fmt/tests/asserts_expected.vv | 5 + v_windows/v/old/vlib/v/fmt/tests/asserts_input.vv | 5 + v_windows/v/old/vlib/v/fmt/tests/asserts_keep.vv | 6 + v_windows/v/old/vlib/v/fmt/tests/attrs_expected.vv | 9 + v_windows/v/old/vlib/v/fmt/tests/attrs_input.vv | 10 + v_windows/v/old/vlib/v/fmt/tests/attrs_keep.vv | 32 + v_windows/v/old/vlib/v/fmt/tests/bin2v_keep.vv | 2 + .../v/old/vlib/v/fmt/tests/blocks_expected.vv | 11 + v_windows/v/old/vlib/v/fmt/tests/blocks_input.vv | 7 + .../v/old/vlib/v/fmt/tests/c_struct_init_keep.vv | 30 + v_windows/v/old/vlib/v/fmt/tests/cast_expected.vv | 13 + v_windows/v/old/vlib/v/fmt/tests/cast_input.vv | 14 + v_windows/v/old/vlib/v/fmt/tests/chan_init_keep.vv | 27 + v_windows/v/old/vlib/v/fmt/tests/chan_ops_keep.vv | 65 + v_windows/v/old/vlib/v/fmt/tests/chan_or_keep.vv | 33 + .../vlib/v/fmt/tests/char_literal_backtick_keep.vv | 3 + .../v/old/vlib/v/fmt/tests/comments_array_keep.vv | 79 + .../v/old/vlib/v/fmt/tests/comments_expected.vv | 105 + v_windows/v/old/vlib/v/fmt/tests/comments_input.vv | 89 + v_windows/v/old/vlib/v/fmt/tests/comments_keep.vv | 101 + .../v/fmt/tests/comptime_field_selector_keep.vv | 23 + .../comptime_field_selector_parentheses_keep.vv | 23 + v_windows/v/old/vlib/v/fmt/tests/comptime_keep.vv | 91 + .../v/old/vlib/v/fmt/tests/concat_expr_expected.vv | 24 + .../v/old/vlib/v/fmt/tests/concat_expr_input.vv | 22 + .../v/fmt/tests/conditional_compilation_keep.vv | 19 + .../v/old/vlib/v/fmt/tests/conditions_expected.vv | 12 + .../v/old/vlib/v/fmt/tests/conditions_input.vv | 10 + .../v/old/vlib/v/fmt/tests/consts_expected.vv | 60 + v_windows/v/old/vlib/v/fmt/tests/consts_input.vv | 54 + v_windows/v/old/vlib/v/fmt/tests/consts_keep.vv | 27 + .../vlib/v/fmt/tests/consts_with_comments_keep.vv | 4 + .../v/old/vlib/v/fmt/tests/embed_file_keep.vv | 8 + .../v/fmt/tests/empty_curlies_and_parens_keep.vv | 44 + .../v/old/vlib/v/fmt/tests/empty_lines_expected.vv | 28 + .../v/old/vlib/v/fmt/tests/empty_lines_input.vv | 34 + .../v/old/vlib/v/fmt/tests/empty_lines_keep.vv | 98 + .../v/old/vlib/v/fmt/tests/enum_comments_keep.vv | 17 + v_windows/v/old/vlib/v/fmt/tests/enums_expected.vv | 9 + v_windows/v/old/vlib/v/fmt/tests/enums_input.vv | 5 + .../v/old/vlib/v/fmt/tests/expressions_expected.vv | 103 + .../v/old/vlib/v/fmt/tests/expressions_input.vv | 102 + .../v/fmt/tests/file_with_just_imports_keep.vv | 3 + .../vlib/v/fmt/tests/fixed_size_array_type_keep.vv | 28 + .../v/fmt/tests/fn_headers_with_no_bodies_keep.vv | 5 + .../v/old/vlib/v/fmt/tests/fn_multi_return_keep.vv | 13 + .../v/fmt/tests/fn_return_generic_struct_keep.vv | 39 + .../v/fmt/tests/fn_trailing_arg_syntax_expected.vv | 50 + .../v/fmt/tests/fn_trailing_arg_syntax_input.vv | 36 + .../v/fmt/tests/fn_trailing_arg_syntax_keep.vv | 57 + .../vlib/v/fmt/tests/fn_with_anon_params_keep.vv | 1 + .../vlib/v/fmt/tests/fntype_alias_array_keep.vv | 4 + .../v/old/vlib/v/fmt/tests/fntype_alias_keep.vv | 65 + .../tests/fntype_mut_args_with_optional_keep.vv | 1 + .../v/fmt/tests/fntype_return_optional_keep.vv | 3 + .../v/old/vlib/v/fmt/tests/functions_expected.vv | 68 + .../v/old/vlib/v/fmt/tests/functions_input.vv | 63 + .../v/fmt/tests/generic_recursive_structs_keep.vv | 28 + .../v/old/vlib/v/fmt/tests/generic_structs_keep.vv | 45 + .../v/fmt/tests/generics_cascade_types_keep.vv | 27 + v_windows/v/old/vlib/v/fmt/tests/generics_keep.vv | 32 + v_windows/v/old/vlib/v/fmt/tests/global_keep.vv | 10 + .../v/old/vlib/v/fmt/tests/go_stmt_expected.vv | 14 + v_windows/v/old/vlib/v/fmt/tests/go_stmt_input.vv | 16 + v_windows/v/old/vlib/v/fmt/tests/go_stmt_keep.vv | 9 + v_windows/v/old/vlib/v/fmt/tests/goto_expected.vv | 4 + v_windows/v/old/vlib/v/fmt/tests/goto_input.vv | 4 + .../v/old/vlib/v/fmt/tests/hashstmt_expected.vv | 3 + v_windows/v/old/vlib/v/fmt/tests/hashstmt_input.vv | 2 + v_windows/v/old/vlib/v/fmt/tests/hashstmt_keep.vv | 12 + .../v/fmt/tests/if_brace_on_newline_expected.vv | 12 + .../vlib/v/fmt/tests/if_brace_on_newline_input.vv | 10 + v_windows/v/old/vlib/v/fmt/tests/if_expected.vv | 9 + v_windows/v/old/vlib/v/fmt/tests/if_input.vv | 3 + v_windows/v/old/vlib/v/fmt/tests/if_keep.vv | 11 + .../v/old/vlib/v/fmt/tests/if_ternary_expected.vv | 48 + .../v/old/vlib/v/fmt/tests/if_ternary_input.vv | 19 + .../v/old/vlib/v/fmt/tests/if_ternary_keep.vv | 44 + .../vlib/v/fmt/tests/import_auto_added_expected.vv | 7 + .../vlib/v/fmt/tests/import_auto_added_input.vv | 5 + .../v/old/vlib/v/fmt/tests/import_comments_keep.vv | 11 + .../vlib/v/fmt/tests/import_duplicate_expected.vv | 10 + .../old/vlib/v/fmt/tests/import_duplicate_input.vv | 11 + .../tests/import_multiple_with_alias_expected.vv | 11 + .../fmt/tests/import_multiple_with_alias_input.vv | 11 + .../vlib/v/fmt/tests/import_selective_expected.vv | 67 + .../old/vlib/v/fmt/tests/import_selective_input.vv | 71 + .../old/vlib/v/fmt/tests/import_selective_keep.vv | 12 + .../v/old/vlib/v/fmt/tests/import_single_keep.vv | 5 + .../old/vlib/v/fmt/tests/import_with_alias_keep.vv | 25 + .../v/old/vlib/v/fmt/tests/infix_expr_expected.vv | 33 + .../v/old/vlib/v/fmt/tests/infix_expr_input.vv | 24 + .../v/old/vlib/v/fmt/tests/infix_expr_keep.vv | 15 + .../v/old/vlib/v/fmt/tests/integer_literal_keep.vv | 19 + .../tests/interface_declaration_comments_keep.vv | 31 + .../vlib/v/fmt/tests/interface_variadic_keep.vv | 4 + .../v/fmt/tests/interface_with_mut_fields_keep.vv | 6 + .../v/fmt/tests/labelled_break_continue_keep.vv | 38 + .../old/vlib/v/fmt/tests/language_prefixes_keep.vv | 13 + v_windows/v/old/vlib/v/fmt/tests/loops_expected.vv | 18 + v_windows/v/old/vlib/v/fmt/tests/loops_input.vv | 18 + v_windows/v/old/vlib/v/fmt/tests/manualfree_keep.v | 18 + v_windows/v/old/vlib/v/fmt/tests/maps_expected.vv | 16 + .../old/vlib/v/fmt/tests/maps_in_fn_args__keep.vv | 15 + v_windows/v/old/vlib/v/fmt/tests/maps_input.vv | 16 + v_windows/v/old/vlib/v/fmt/tests/maps_keep.vv | 17 + .../fmt/tests/maps_of_fns_with_string_keys_keep.vv | 13 + v_windows/v/old/vlib/v/fmt/tests/match_expected.vv | 38 + v_windows/v/old/vlib/v/fmt/tests/match_input.vv | 36 + v_windows/v/old/vlib/v/fmt/tests/match_keep.vv | 76 + .../tests/match_range_expression_branches_keep.vv | 15 + .../tests/match_with_commented_branches_keep.vv | 57 + .../vlib/v/fmt/tests/missing_import_expected.vv | 5 + .../v/old/vlib/v/fmt/tests/missing_import_input.vv | 3 + .../v/old/vlib/v/fmt/tests/module_alias_keep.vv | 36 + .../old/vlib/v/fmt/tests/module_interface_keep.vv | 5 + .../v/old/vlib/v/fmt/tests/module_struct_keep.vv | 11 + .../vlib/v/fmt/tests/multi_generic_test_keep.vv | 2 + .../old/vlib/v/fmt/tests/multiline_comment_keep.vv | 22 + .../v/old/vlib/v/fmt/tests/nested_map_type_keep.vv | 5 + v_windows/v/old/vlib/v/fmt/tests/newlines_keep.vv | 32 + .../v/old/vlib/v/fmt/tests/no_main_expected.vv | 1 + v_windows/v/old/vlib/v/fmt/tests/no_main_input.vv | 1 + v_windows/v/old/vlib/v/fmt/tests/offset_keep.vv | 8 + .../old/vlib/v/fmt/tests/operator_overload_keep.vv | 11 + v_windows/v/old/vlib/v/fmt/tests/optional_keep.vv | 5 + .../vlib/v/fmt/tests/optional_propagate_keep.vv | 3 + v_windows/v/old/vlib/v/fmt/tests/or_keep.vv | 40 + v_windows/v/old/vlib/v/fmt/tests/orm_keep.vv | 67 + .../v/old/vlib/v/fmt/tests/par_expr_expected.vv | 3 + v_windows/v/old/vlib/v/fmt/tests/par_expr_input.vv | 3 + .../v/old/vlib/v/fmt/tests/pointer_casts_keep.vv | 50 + .../tests/proto_module_importing_vproto_keep.vv | 35 + .../v/old/vlib/v/fmt/tests/ref_type_cast_keep.vv | 9 + v_windows/v/old/vlib/v/fmt/tests/select_keep.vv | 128 + .../v/old/vlib/v/fmt/tests/shared_expected.vv | 85 + v_windows/v/old/vlib/v/fmt/tests/shared_input.vv | 84 + .../vlib/v/fmt/tests/star__amp_int__cast_keep.vv | 5 + .../v/old/vlib/v/fmt/tests/static_mut_keep.vv | 12 + v_windows/v/old/vlib/v/fmt/tests/stmt_keep.vv | 8 + .../fmt/tests/string_interpolation_complex_keep.vv | 9 + .../v/fmt/tests/string_interpolation_expected.vv | 32 + .../vlib/v/fmt/tests/string_interpolation_input.vv | 32 + .../vlib/v/fmt/tests/string_interpolation_keep.vv | 17 + .../old/vlib/v/fmt/tests/string_quotes_expected.vv | 10 + .../v/old/vlib/v/fmt/tests/string_quotes_input.vv | 10 + .../vlib/v/fmt/tests/string_raw_and_cstr_keep.vv | 6 + .../v/old/vlib/v/fmt/tests/struct_decl_keep.vv | 5 + .../tests/struct_default_field_expressions_keep.vv | 14 + .../v/old/vlib/v/fmt/tests/struct_embed_keep.vv | 18 + .../v/old/vlib/v/fmt/tests/struct_init_keep.vv | 14 + .../v/fmt/tests/struct_init_with_comments_keep.vv | 20 + .../fmt/tests/struct_init_with_custom_len_keep.vv | 17 + .../v/fmt/tests/struct_init_with_ref_cast_keep.vv | 22 + v_windows/v/old/vlib/v/fmt/tests/struct_keep.vv | 46 + .../vlib/v/fmt/tests/struct_no_extra_attr_keep.vv | 27 + .../vlib/v/fmt/tests/struct_update_comment_keep.vv | 18 + .../v/old/vlib/v/fmt/tests/struct_update_keep.vv | 17 + .../vlib/v/fmt/tests/struct_with_fn_fields_keep.vv | 4 + .../v/old/vlib/v/fmt/tests/structs_expected.vv | 62 + v_windows/v/old/vlib/v/fmt/tests/structs_input.vv | 65 + .../v/old/vlib/v/fmt/tests/sum_smartcast_keep.vv | 20 + .../vlib/v/fmt/tests/thread_in_a_module_keep.vv | 17 + .../old/vlib/v/fmt/tests/to_string_2_forms_keep.vv | 33 + .../vlib/v/fmt/tests/trailing_space_expected.vv | 10 + .../v/old/vlib/v/fmt/tests/trailing_space_input.vv | 10 + v_windows/v/old/vlib/v/fmt/tests/type_ptr_keep.vv | 16 + v_windows/v/old/vlib/v/fmt/tests/typeof_keep.vv | 3 + v_windows/v/old/vlib/v/fmt/tests/types_expected.vv | 27 + v_windows/v/old/vlib/v/fmt/tests/types_input.vv | 37 + v_windows/v/old/vlib/v/fmt/tests/union_keep.vv | 4 + v_windows/v/old/vlib/v/fmt/tests/unsafe_keep.vv | 17 + .../v/old/vlib/v/fmt/tests/void_optional_keep.vv | 7 + v_windows/v/old/vlib/v/fmt/tests/vscript_keep.vv | 9 + v_windows/v/old/vlib/v/gen/c/array.v | 726 + v_windows/v/old/vlib/v/gen/c/assert.v | 128 + v_windows/v/old/vlib/v/gen/c/auto_eq_methods.v | 343 + v_windows/v/old/vlib/v/gen/c/auto_str_methods.v | 896 + v_windows/v/old/vlib/v/gen/c/cgen.v | 6698 ++++ v_windows/v/old/vlib/v/gen/c/cheaders.v | 639 + v_windows/v/old/vlib/v/gen/c/cmain.v | 214 + v_windows/v/old/vlib/v/gen/c/comptime.v | 676 + v_windows/v/old/vlib/v/gen/c/coutput_test.v | 174 + v_windows/v/old/vlib/v/gen/c/ctempvars.v | 25 + v_windows/v/old/vlib/v/gen/c/dumpexpr.v | 72 + v_windows/v/old/vlib/v/gen/c/embed.v | 69 + v_windows/v/old/vlib/v/gen/c/fn.v | 1452 + v_windows/v/old/vlib/v/gen/c/index.v | 452 + v_windows/v/old/vlib/v/gen/c/infix_expr.v | 564 + v_windows/v/old/vlib/v/gen/c/json.v | 339 + v_windows/v/old/vlib/v/gen/c/live.v | 104 + v_windows/v/old/vlib/v/gen/c/profile.v | 52 + v_windows/v/old/vlib/v/gen/c/sql.v | 826 + v_windows/v/old/vlib/v/gen/c/str.v | 149 + v_windows/v/old/vlib/v/gen/c/str_intp.v | 206 + .../v/gen/c/testdata/const_references.c.must_have | 3 + .../old/vlib/v/gen/c/testdata/const_references.out | 3 + .../old/vlib/v/gen/c/testdata/const_references.vv | 11 + v_windows/v/old/vlib/v/gen/c/utils.v | 49 + v_windows/v/old/vlib/v/gen/js/builtin_types.v | 393 + v_windows/v/old/vlib/v/gen/js/comptime.v | 311 + v_windows/v/old/vlib/v/gen/js/fast_deep_equal.js | 72 + v_windows/v/old/vlib/v/gen/js/js.v | 2103 ++ v_windows/v/old/vlib/v/gen/js/jsdoc.v | 96 + v_windows/v/old/vlib/v/gen/js/jsgen_test.v | 86 + v_windows/v/old/vlib/v/gen/js/program_test.v | 98 + .../v/old/vlib/v/gen/js/sourcemap/basic_test.v | 158 + .../v/old/vlib/v/gen/js/sourcemap/compare_test.v | 322 + v_windows/v/old/vlib/v/gen/js/sourcemap/mappings.v | 170 + v_windows/v/old/vlib/v/gen/js/sourcemap/sets.v | 16 + .../v/old/vlib/v/gen/js/sourcemap/source_map.v | 131 + .../vlib/v/gen/js/sourcemap/source_map_generator.v | 46 + v_windows/v/old/vlib/v/gen/js/sourcemap/vlq/vlq.v | 115 + .../vlib/v/gen/js/sourcemap/vlq/vlq_decode_test.v | 52 + .../vlib/v/gen/js/sourcemap/vlq/vlq_encode_test.v | 35 + .../v/old/vlib/v/gen/js/temp_fast_deep_equal.v | 78 + v_windows/v/old/vlib/v/gen/js/tests/.gitignore | 1 + v_windows/v/old/vlib/v/gen/js/tests/array.v | 138 + .../v/old/vlib/v/gen/js/tests/auto_deref_args.v | 13 + v_windows/v/old/vlib/v/gen/js/tests/enum.v | 19 + .../v/old/vlib/v/gen/js/tests/hello/hello.js.v | 5 + v_windows/v/old/vlib/v/gen/js/tests/hello/hello.v | 33 + .../old/vlib/v/gen/js/tests/hello/hello1/hello1.v | 5 + v_windows/v/old/vlib/v/gen/js/tests/interface.v | 47 + v_windows/v/old/vlib/v/gen/js/tests/interp.v | 188 + v_windows/v/old/vlib/v/gen/js/tests/js.v | 141 + v_windows/v/old/vlib/v/gen/js/tests/life.v | 98 + v_windows/v/old/vlib/v/gen/js/tests/optional.v | 33 + v_windows/v/old/vlib/v/gen/js/tests/simple.v | 5 + .../v/old/vlib/v/gen/js/tests/simple_sourcemap.v | 23 + v_windows/v/old/vlib/v/gen/js/tests/struct.v | 40 + .../v/old/vlib/v/gen/js/tests/testdata/array.out | 231 + .../v/old/vlib/v/gen/js/tests/testdata/array.v | 771 + .../vlib/v/gen/js/tests/testdata/byte_is_space.out | 2 + .../vlib/v/gen/js/tests/testdata/byte_is_space.v | 4 + .../vlib/v/gen/js/tests/testdata/compare_ints.out | 1 + .../vlib/v/gen/js/tests/testdata/compare_ints.v | 15 + .../v/old/vlib/v/gen/js/tests/testdata/hw.out | 1 + v_windows/v/old/vlib/v/gen/js/tests/testdata/hw.v | 1 + .../v/gen/js/tests/testdata/string_methods.out | 3 + .../vlib/v/gen/js/tests/testdata/string_methods.v | 3 + v_windows/v/old/vlib/v/gen/native/amd64.v | 1219 + v_windows/v/old/vlib/v/gen/native/arm64.v | 185 + v_windows/v/old/vlib/v/gen/native/elf.v | 113 + v_windows/v/old/vlib/v/gen/native/elf_obj.v | 157 + v_windows/v/old/vlib/v/gen/native/gen.v | 454 + v_windows/v/old/vlib/v/gen/native/macho.v | 405 + v_windows/v/old/vlib/v/gen/native/macho_test.v | 16 + .../v/old/vlib/v/gen/native/tests/expressions.vv | 60 + .../old/vlib/v/gen/native/tests/expressions.vv.out | 8 + v_windows/v/old/vlib/v/gen/native/tests/general.vv | 87 + .../v/old/vlib/v/gen/native/tests/general.vv.out | 14 + v_windows/v/old/vlib/v/gen/native/tests/hello.vv | 3 + .../v/old/vlib/v/gen/native/tests/hello.vv.out | 1 + v_windows/v/old/vlib/v/gen/native/tests/ifs.vv | 65 + v_windows/v/old/vlib/v/gen/native/tests/ifs.vv.out | 7 + .../v/old/vlib/v/gen/native/tests/native_test.v | 79 + .../old/vlib/v/gen/native/tests/simple_fn_calls.vv | 14 + .../vlib/v/gen/native/tests/simple_fn_calls.vv.out | 4 + v_windows/v/old/vlib/v/live/common.v | 69 + v_windows/v/old/vlib/v/live/executable/reloader.v | 163 + v_windows/v/old/vlib/v/live/live_test.v | 177 + v_windows/v/old/vlib/v/live/live_test_template.vv | 66 + .../v/old/vlib/v/live/sharedlib/live_sharedlib.v | 7 + v_windows/v/old/vlib/v/markused/markused.v | 421 + v_windows/v/old/vlib/v/markused/walker.v | 464 + v_windows/v/old/vlib/v/parser/assign.v | 236 + v_windows/v/old/vlib/v/parser/comptime.v | 359 + v_windows/v/old/vlib/v/parser/containers.v | 190 + v_windows/v/old/vlib/v/parser/expr.v | 650 + v_windows/v/old/vlib/v/parser/fn.v | 959 + v_windows/v/old/vlib/v/parser/for.v | 201 + v_windows/v/old/vlib/v/parser/if_match.v | 443 + v_windows/v/old/vlib/v/parser/lock.v | 113 + v_windows/v/old/vlib/v/parser/module.v | 66 + v_windows/v/old/vlib/v/parser/parse_type.v | 577 + v_windows/v/old/vlib/v/parser/parser.v | 3418 +++ v_windows/v/old/vlib/v/parser/sql.v | 262 + v_windows/v/old/vlib/v/parser/struct.v | 622 + v_windows/v/old/vlib/v/parser/tests/README.md | 1 + .../vlib/v/parser/tests/anon_fn_return_type.out | 6 + .../old/vlib/v/parser/tests/anon_fn_return_type.vv | 8 + .../old/vlib/v/parser/tests/anon_unused_param.out | 4 + .../v/old/vlib/v/parser/tests/anon_unused_param.vv | 2 + v_windows/v/old/vlib/v/parser/tests/array_init.out | 12 + v_windows/v/old/vlib/v/parser/tests/array_init.vv | 4 + .../v/old/vlib/v/parser/tests/array_pos_err.out | 5 + .../v/old/vlib/v/parser/tests/array_pos_err.vv | 3 + .../old/vlib/v/parser/tests/c_struct_no_embed.out | 5 + .../v/old/vlib/v/parser/tests/c_struct_no_embed.vv | 7 + .../v/old/vlib/v/parser/tests/const_index.out | 1 + v_windows/v/old/vlib/v/parser/tests/const_index.vv | 22 + .../vlib/v/parser/tests/const_missing_rpar_a.out | 3 + .../vlib/v/parser/tests/const_missing_rpar_a.vv | 2 + .../vlib/v/parser/tests/const_missing_rpar_b.out | 3 + .../vlib/v/parser/tests/const_missing_rpar_b.vv | 3 + .../old/vlib/v/parser/tests/const_only_keyword.out | 2 + .../old/vlib/v/parser/tests/const_only_keyword.vv | 1 + .../vlib/v/parser/tests/const_unexpected_eof.out | 2 + .../vlib/v/parser/tests/const_unexpected_eof.vv | 1 + .../v/old/vlib/v/parser/tests/dec_use_as_value.out | 6 + .../v/old/vlib/v/parser/tests/dec_use_as_value.vv | 4 + .../v/old/vlib/v/parser/tests/defer_propagate.out | 7 + .../v/old/vlib/v/parser/tests/defer_propagate.vv | 16 + .../v/old/vlib/v/parser/tests/defer_return.out | 7 + .../v/old/vlib/v/parser/tests/defer_return.vv | 5 + .../v/old/vlib/v/parser/tests/defer_return2.out | 7 + .../v/old/vlib/v/parser/tests/defer_return2.vv | 12 + .../v/parser/tests/duplicate_field_embed_err.out | 6 + .../v/parser/tests/duplicate_field_embed_err.vv | 9 + .../v/old/vlib/v/parser/tests/duplicate_type_a.out | 5 + .../v/old/vlib/v/parser/tests/duplicate_type_a.vv | 3 + .../v/old/vlib/v/parser/tests/duplicate_type_b.out | 5 + .../v/old/vlib/v/parser/tests/duplicate_type_b.vv | 3 + .../vlib/v/parser/tests/duplicated_generic_err.out | 3 + .../vlib/v/parser/tests/duplicated_generic_err.vv | 1 + .../vlib/v/parser/tests/empty_name_expr_err.out | 5 + .../old/vlib/v/parser/tests/empty_name_expr_err.vv | 3 + .../vlib/v/parser/tests/expected_type_enum_err.out | 6 + .../vlib/v/parser/tests/expected_type_enum_err.vv | 7 + .../v/parser/tests/expecting_assign_type_alias.out | 5 + .../v/parser/tests/expecting_assign_type_alias.vv | 3 + .../parser/tests/expr_evaluated_but_not_used_a.out | 5 + .../parser/tests/expr_evaluated_but_not_used_a.vv | 3 + .../parser/tests/expr_evaluated_but_not_used_b.out | 5 + .../parser/tests/expr_evaluated_but_not_used_b.vv | 3 + .../parser/tests/expr_evaluated_but_not_used_c.out | 7 + .../parser/tests/expr_evaluated_but_not_used_c.vv | 5 + .../parser/tests/expr_evaluated_but_not_used_d.out | 6 + .../parser/tests/expr_evaluated_but_not_used_d.vv | 5 + .../parser/tests/expr_evaluated_but_not_used_e.out | 7 + .../parser/tests/expr_evaluated_but_not_used_e.vv | 5 + .../tests/expr_evaluated_but_not_used_if.out | 7 + .../parser/tests/expr_evaluated_but_not_used_if.vv | 8 + .../tests/expr_evaluated_but_not_used_or.out | 7 + .../parser/tests/expr_evaluated_but_not_used_or.vv | 10 + .../tests/fn_attributes_duplicate_multiple.out | 6 + .../tests/fn_attributes_duplicate_multiple.vv | 7 + .../tests/fn_attributes_duplicate_single.out | 5 + .../parser/tests/fn_attributes_duplicate_single.vv | 6 + .../v/parser/tests/fn_attributes_empty_err.out | 5 + .../vlib/v/parser/tests/fn_attributes_empty_err.vv | 6 + .../vlib/v/parser/tests/fn_decl_unexpected_eof.out | 3 + .../vlib/v/parser/tests/fn_decl_unexpected_eof.vv | 1 + .../tests/fn_type_only_args_in_interfaces.out | 5 + .../tests/fn_type_only_args_in_interfaces.vv | 22 + .../v/parser/tests/fn_type_only_args_no_body.out | 6 + .../v/parser/tests/fn_type_only_args_no_body.vv | 6 + .../tests/fn_type_only_args_unknown_name.out | 6 + .../parser/tests/fn_type_only_args_unknown_name.vv | 6 + .../old/vlib/v/parser/tests/fn_use_builtin_err.out | 5 + .../old/vlib/v/parser/tests/fn_use_builtin_err.vv | 9 + v_windows/v/old/vlib/v/parser/tests/for.out | 4 + v_windows/v/old/vlib/v/parser/tests/for.vv | 2 + .../v/parser/tests/for_in_mut_index_of_array.out | 7 + .../v/parser/tests/for_in_mut_index_of_array.vv | 6 + .../vlib/v/parser/tests/for_in_mut_key_of_map.out | 7 + .../vlib/v/parser/tests/for_in_mut_key_of_map.vv | 6 + .../tests/function_variadic_arg_non_final.out | 5 + .../tests/function_variadic_arg_non_final.vv | 7 + .../vlib/v/parser/tests/generic_lowercase_err.out | 3 + .../vlib/v/parser/tests/generic_lowercase_err.vv | 1 + .../v/parser/tests/generic_type_alias_decl.out | 3 + .../vlib/v/parser/tests/generic_type_alias_decl.vv | 1 + .../vlib/v/parser/tests/if_guard_redefinition.out | 7 + .../vlib/v/parser/tests/if_guard_redefinition.vv | 10 + .../v/old/vlib/v/parser/tests/inc_use_as_value.out | 6 + .../v/old/vlib/v/parser/tests/inc_use_as_value.vv | 4 + .../tests/interface_duplicate_interface_method.out | 6 + .../tests/interface_duplicate_interface_method.vv | 5 + .../v/parser/tests/interface_duplicate_method.out | 5 + .../v/parser/tests/interface_duplicate_method.vv | 5 + .../vlib/v/parser/tests/invalid_attribute_a.out | 5 + .../old/vlib/v/parser/tests/invalid_attribute_a.vv | 3 + .../vlib/v/parser/tests/invalid_attribute_b.out | 5 + .../old/vlib/v/parser/tests/invalid_attribute_b.vv | 3 + .../vlib/v/parser/tests/invalid_attribute_c.out | 5 + .../old/vlib/v/parser/tests/invalid_attribute_c.vv | 3 + .../vlib/v/parser/tests/invalid_attribute_d.out | 5 + .../old/vlib/v/parser/tests/invalid_attribute_d.vv | 3 + .../v/parser/tests/invalid_fn_decl_script_err.out | 7 + .../v/parser/tests/invalid_fn_decl_script_err.vv | 5 + .../parser/tests/invalid_recursive_struct1_err.out | 5 + .../parser/tests/invalid_recursive_struct1_err.vv | 3 + .../parser/tests/invalid_recursive_struct2_err.out | 7 + .../parser/tests/invalid_recursive_struct2_err.vv | 7 + .../v/old/vlib/v/parser/tests/long_generic_err.out | 3 + .../v/old/vlib/v/parser/tests/long_generic_err.vv | 1 + v_windows/v/old/vlib/v/parser/tests/map_init.out | 6 + v_windows/v/old/vlib/v/parser/tests/map_init.vv | 4 + .../v/old/vlib/v/parser/tests/map_init_void.out | 5 + .../v/old/vlib/v/parser/tests/map_init_void.vv | 3 + .../v/old/vlib/v/parser/tests/map_init_void2.out | 5 + .../v/old/vlib/v/parser/tests/map_init_void2.vv | 7 + .../vlib/v/parser/tests/match_range_dotdot_err.out | 7 + .../vlib/v/parser/tests/match_range_dotdot_err.vv | 6 + .../tests/method_decl_on_non_local_array.out | 5 + .../parser/tests/method_decl_on_non_local_array.vv | 3 + .../parser/tests/method_decl_on_non_local_map.out | 5 + .../v/parser/tests/method_decl_on_non_local_map.vv | 3 + .../parser/tests/method_decl_on_non_local_type.out | 5 + .../parser/tests/method_decl_on_non_local_type.vv | 3 + .../v/parser/tests/module_multiple_names_err.out | 5 + .../v/parser/tests/module_multiple_names_err.vv | 4 + .../old/vlib/v/parser/tests/module_syntax_err.out | 5 + .../v/old/vlib/v/parser/tests/module_syntax_err.vv | 4 + .../v/parser/tests/multi_argumented_assign_err.out | 7 + .../v/parser/tests/multi_argumented_assign_err.vv | 5 + .../v/old/vlib/v/parser/tests/nested_defer.out | 7 + .../v/old/vlib/v/parser/tests/nested_defer.vv | 14 + .../old/vlib/v/parser/tests/nested_unsafe_expr.out | 7 + .../old/vlib/v/parser/tests/nested_unsafe_expr.vv | 7 + .../old/vlib/v/parser/tests/nested_unsafe_stmt.out | 7 + .../old/vlib/v/parser/tests/nested_unsafe_stmt.vv | 9 + .../old/vlib/v/parser/tests/operator_normal_fn.out | 5 + .../old/vlib/v/parser/tests/operator_normal_fn.vv | 3 + .../old/vlib/v/parser/tests/or_default_missing.out | 14 + .../old/vlib/v/parser/tests/or_default_missing.vv | 20 + .../v/old/vlib/v/parser/tests/postfix_err.out | 21 + v_windows/v/old/vlib/v/parser/tests/postfix_err.vv | 11 + .../v/old/vlib/v/parser/tests/postfix_inc.out | 5 + v_windows/v/old/vlib/v/parser/tests/postfix_inc.vv | 3 + .../v/old/vlib/v/parser/tests/prefix_first.out | 28 + .../v/old/vlib/v/parser/tests/prefix_first.vv | 29 + .../prohibit_redeclaration_of_builtin_types.out | 3 + .../prohibit_redeclaration_of_builtin_types.vv | 1 + .../parser/tests/redeclaration_of_imported_fn.out | 7 + .../v/parser/tests/redeclaration_of_imported_fn.vv | 5 + .../v/parser/tests/register_imported_alias.out | 4 + .../vlib/v/parser/tests/register_imported_alias.vv | 2 + .../vlib/v/parser/tests/register_imported_enum.out | 4 + .../vlib/v/parser/tests/register_imported_enum.vv | 2 + .../v/parser/tests/register_imported_interface.out | 4 + .../v/parser/tests/register_imported_interface.vv | 2 + .../v/parser/tests/register_imported_struct.out | 4 + .../v/parser/tests/register_imported_struct.vv | 2 + .../v/old/vlib/v/parser/tests/select_bad_key_1.out | 14 + .../v/old/vlib/v/parser/tests/select_bad_key_1.vv | 47 + .../v/old/vlib/v/parser/tests/select_bad_key_2.out | 7 + .../v/old/vlib/v/parser/tests/select_bad_key_2.vv | 13 + .../v/old/vlib/v/parser/tests/select_bad_key_3.out | 7 + .../v/old/vlib/v/parser/tests/select_bad_key_3.vv | 12 + .../v/old/vlib/v/parser/tests/select_bad_key_4.out | 7 + .../v/old/vlib/v/parser/tests/select_bad_key_4.vv | 13 + .../v/old/vlib/v/parser/tests/select_else_1.out | 7 + .../v/old/vlib/v/parser/tests/select_else_1.vv | 18 + .../v/old/vlib/v/parser/tests/select_else_2.out | 14 + .../v/old/vlib/v/parser/tests/select_else_2.vv | 18 + .../v/old/vlib/v/parser/tests/sql_no_db_expr_a.out | 5 + .../v/old/vlib/v/parser/tests/sql_no_db_expr_a.vv | 4 + .../v/old/vlib/v/parser/tests/sql_no_db_expr_b.out | 6 + .../v/old/vlib/v/parser/tests/sql_no_db_expr_b.vv | 4 + .../v/parser/tests/string_invalid_prefix_err.out | 6 + .../v/parser/tests/string_invalid_prefix_err.vv | 4 + .../vlib/v/parser/tests/struct_embed_duplicate.out | 7 + .../vlib/v/parser/tests/struct_embed_duplicate.vv | 9 + .../v/parser/tests/struct_embed_unknown_module.out | 5 + .../v/parser/tests/struct_embed_unknown_module.vv | 3 + .../tests/struct_embed_wrong_pos_long_err.out | 7 + .../tests/struct_embed_wrong_pos_long_err.vv | 6 + .../tests/struct_embed_wrong_pos_short_err.out | 7 + .../tests/struct_embed_wrong_pos_short_err.vv | 8 + .../vlib/v/parser/tests/struct_field_expected.out | 7 + .../vlib/v/parser/tests/struct_field_expected.vv | 9 + .../parser/tests/struct_field_unknown_module_a.out | 5 + .../parser/tests/struct_field_unknown_module_a.vv | 3 + .../parser/tests/struct_field_unknown_module_b.out | 5 + .../parser/tests/struct_field_unknown_module_b.vv | 3 + .../parser/tests/struct_field_unknown_module_c.out | 6 + .../parser/tests/struct_field_unknown_module_c.vv | 5 + .../vlib/v/parser/tests/struct_module_section.out | 12 + .../vlib/v/parser/tests/struct_module_section.vv | 18 + .../old/vlib/v/parser/tests/struct_update_err.out | 7 + .../v/old/vlib/v/parser/tests/struct_update_err.vv | 17 + .../vlib/v/parser/tests/too_many_generics_err.out | 3 + .../vlib/v/parser/tests/too_many_generics_err.vv | 1 + .../parser/tests/type_alias_existing_type_err.out | 7 + .../v/parser/tests/type_alias_existing_type_err.vv | 7 + .../v/parser/tests/type_alias_same_type_err.out | 5 + .../v/parser/tests/type_alias_same_type_err.vv | 5 + .../v/parser/tests/uncomplete_module_call_err.out | 5 + .../v/parser/tests/uncomplete_module_call_err.vv | 7 + .../v/old/vlib/v/parser/tests/unexpected_expr.out | 3 + .../v/old/vlib/v/parser/tests/unexpected_expr.vv | 1 + .../old/vlib/v/parser/tests/unexpected_keyword.out | 7 + .../old/vlib/v/parser/tests/unexpected_keyword.vv | 12 + .../v/old/vlib/v/parser/tests/unnecessary_mut.out | 7 + .../v/old/vlib/v/parser/tests/unnecessary_mut.vv | 7 + .../old/vlib/v/parser/tests/unnecessary_mut_2.out | 6 + .../v/old/vlib/v/parser/tests/unnecessary_mut_2.vv | 5 + v_windows/v/old/vlib/v/parser/tmpl.v | 238 + v_windows/v/old/vlib/v/parser/v_parser_test.v | 263 + v_windows/v/old/vlib/v/pkgconfig/README.md | 68 + v_windows/v/old/vlib/v/pkgconfig/bin/pkgconfig.v | 18 + v_windows/v/old/vlib/v/pkgconfig/main.v | 203 + v_windows/v/old/vlib/v/pkgconfig/pkgconfig.v | 277 + v_windows/v/old/vlib/v/pkgconfig/pkgconfig_test.v | 91 + .../v/old/vlib/v/pkgconfig/test_samples/alsa.pc | 12 + .../v/old/vlib/v/pkgconfig/test_samples/atk.pc | 10 + .../old/vlib/v/pkgconfig/test_samples/autoopts.pc | 26 + .../pkgconfig/test_samples/dep-resolution-fail.pc | 7 + .../v/old/vlib/v/pkgconfig/test_samples/expat.pc | 11 + .../v/old/vlib/v/pkgconfig/test_samples/form.pc | 19 + .../v/old/vlib/v/pkgconfig/test_samples/gio-2.0.pc | 25 + .../vlib/v/pkgconfig/test_samples/gio-unix-2.0.pc | 9 + .../old/vlib/v/pkgconfig/test_samples/glib-2.0.pc | 16 + .../vlib/v/pkgconfig/test_samples/gmodule-2.0.pc | 12 + .../test_samples/gmodule-no-export-2.0.pc | 13 + .../vlib/v/pkgconfig/test_samples/gobject-2.0.pc | 12 + .../v/old/vlib/v/pkgconfig/test_samples/libffi.pc | 10 + .../v/old/vlib/v/pkgconfig/test_samples/libpcre.pc | 13 + .../v/old/vlib/v/pkgconfig/test_samples/ncurses.pc | 19 + .../v/old/vlib/v/pkgconfig/test_samples/sdl2.pc | 15 + .../v/old/vlib/v/pkgconfig/test_samples/zlib.pc | 13 + v_windows/v/old/vlib/v/pkgconfig/v.mod | 12 + v_windows/v/old/vlib/v/pref/default.v | 206 + v_windows/v/old/vlib/v/pref/os.v | 116 + v_windows/v/old/vlib/v/pref/pref.v | 828 + v_windows/v/old/vlib/v/pref/should_compile.v | 217 + v_windows/v/old/vlib/v/preludes/README.md | 28 + v_windows/v/old/vlib/v/preludes/live.v | 9 + v_windows/v/old/vlib/v/preludes/live_main.v | 9 + v_windows/v/old/vlib/v/preludes/live_shared.v | 9 + v_windows/v/old/vlib/v/preludes/profiled_program.v | 7 + v_windows/v/old/vlib/v/preludes/tests_assertions.v | 106 + v_windows/v/old/vlib/v/preludes/tests_with_stats.v | 91 + .../v/old/vlib/v/preludes_js/tests_assertions.v | 87 + .../v/old/vlib/v/preludes_js/tests_with_stats.v | 80 + v_windows/v/old/vlib/v/scanner/scanner.v | 1422 + v_windows/v/old/vlib/v/scanner/scanner_test.v | 143 + .../tests/bin_consecutively_separator_err.out | 5 + .../tests/bin_consecutively_separator_err.vv | 3 + .../v/scanner/tests/bin_separator_in_front_err.out | 5 + .../v/scanner/tests/bin_separator_in_front_err.vv | 3 + .../tests/dec_consecutively_separator_err.out | 5 + .../tests/dec_consecutively_separator_err.vv | 3 + .../tests/hex_consecutively_separator_err.out | 5 + .../tests/hex_consecutively_separator_err.vv | 3 + .../v/scanner/tests/hex_separator_in_front_err.out | 5 + .../v/scanner/tests/hex_separator_in_front_err.vv | 3 + .../tests/oct_consecutively_separator_err.out | 5 + .../tests/oct_consecutively_separator_err.vv | 3 + .../v/scanner/tests/oct_separator_in_front_err.out | 5 + .../v/scanner/tests/oct_separator_in_front_err.vv | 3 + .../v/old/vlib/v/scanner/tests/position_0_err.out | 5 + .../v/old/vlib/v/scanner/tests/position_0_err.vv | 4 + .../tests/alias_array_operator_overloading_test.v | 19 + .../old/vlib/v/tests/alias_custom_type_map_test.v | 8 + .../old/vlib/v/tests/alias_fixed_array_init_test.v | 12 + .../v/old/vlib/v/tests/alias_fixed_array_test.v | 12 + .../v/tests/alias_in_a_struct_field_autostr_test.v | 12 + .../v/tests/alias_map_operator_overloading_test.v | 23 + .../vlib/v/tests/aliased_array_operations_test.v | 16 + v_windows/v/old/vlib/v/tests/anon_fn_call_test.v | 10 + .../old/vlib/v/tests/anon_fn_in_containers_test.v | 37 + .../v/old/vlib/v/tests/anon_fn_redefinition_test.v | 24 + .../vlib/v/tests/anon_fn_returning_question_test.v | 25 + v_windows/v/old/vlib/v/tests/anon_fn_test.v | 23 + .../v/tests/anon_fn_with_array_arguments_test.v | 12 + .../old/vlib/v/tests/anon_fn_with_optional_test.v | 30 + .../appending_to_mut_array_in_fn_param_test.v | 34 + .../vlib/v/tests/array_append_short_struct_test.v | 31 + v_windows/v/old/vlib/v/tests/array_cast_test.v | 19 + v_windows/v/old/vlib/v/tests/array_equality_test.v | 93 + v_windows/v/old/vlib/v/tests/array_init_test.v | 259 + v_windows/v/old/vlib/v/tests/array_map_or_test.v | 118 + v_windows/v/old/vlib/v/tests/array_map_ref_test.v | 56 + v_windows/v/old/vlib/v/tests/array_methods_test.v | 32 + .../v/old/vlib/v/tests/array_of_alias_slice_test.v | 12 + v_windows/v/old/vlib/v/tests/array_slice_test.v | 109 + .../old/vlib/v/tests/array_sort_lt_overload_test.v | 22 + v_windows/v/old/vlib/v/tests/array_test.v | 20 + .../v/old/vlib/v/tests/array_to_string_test.v | 57 + .../v/old/vlib/v/tests/array_type_alias_test.v | 11 + .../tests/as_cast_already_smartcast_sumtype_test.v | 30 + .../tests/as_cast_is_expr_sumtype_fn_result_test.v | 30 + .../v/old/vlib/v/tests/assembly/asm_test.amd64.v | 236 + .../v/old/vlib/v/tests/assembly/asm_test.i386.v | 188 + .../vlib/v/tests/assembly/naked_attr_test.amd64.v | 14 + .../vlib/v/tests/assembly/naked_attr_test.i386.v | 14 + .../v/tests/assembly/util/dot_amd64_util.amd64.v | 15 + v_windows/v/old/vlib/v/tests/assert_sumtype_test.v | 11 + .../v/old/vlib/v/tests/assert_with_newlines_test.v | 8 + .../v/tests/assign_bitops_with_type_aliases_test.v | 18 + .../v/tests/assign_map_value_of_fixed_array_test.v | 18 + v_windows/v/old/vlib/v/tests/attribute_test.v | 49 + .../v/old/vlib/v/tests/autolock_array1_test.v | 50 + .../v/old/vlib/v/tests/autolock_array2_test.v | 32 + v_windows/v/old/vlib/v/tests/backtrace_test.v | 11 + .../v/tests/bench/gcboehm/GC_Ryzen_3800X_Linux.pdf | Bin 0 -> 19044 bytes .../v/tests/bench/gcboehm/GC_Ryzen_3800X_Linux.svg | 10619 +++++++ .../v/old/vlib/v/tests/bench/gcboehm/GC_bench.plt | 17 + .../v/old/vlib/v/tests/bench/gcboehm/GC_bench.v | 65 + .../vlib/v/tests/bench/gcboehm/GC_bench_full.plt | 9 + .../vlib/v/tests/bench/gcboehm/GC_bench_incr.plt | 9 + .../v/tests/bench/gcboehm/GC_bench_non_opt.plt | 9 + .../vlib/v/tests/bench/gcboehm/GC_bench_opt.plt | 9 + .../v/old/vlib/v/tests/bench/gcboehm/Makefile | 66 + .../v/old/vlib/v/tests/bench/gcboehm/Resources.plt | 45 + .../bench/gcboehm/Resources_Ryzen_3800X_Linux.pdf | Bin 0 -> 18959 bytes .../bench/gcboehm/Resources_Ryzen_3800X_Linux.svg | 613 + .../v/old/vlib/v/tests/bench/gcboehm/resources.txt | 6 + v_windows/v/old/vlib/v/tests/bench/val_vs_ptr.c | 21 + v_windows/v/old/vlib/v/tests/blank_ident_test.v | 323 + v_windows/v/old/vlib/v/tests/break_in_lock_test.v | 55 + .../c_struct_free/c_struct_free_property_test.v | 13 + .../v/old/vlib/v/tests/c_struct_free/free_struct.c | 3 + ...ing_module_functions_with_maps_of_arrays_test.v | 10 + v_windows/v/old/vlib/v/tests/cast_to_byte_test.v | 41 + .../v/old/vlib/v/tests/cast_to_interface_test.v | 17 + .../v/old/vlib/v/tests/cflags/includes/myinclude.h | 4 + v_windows/v/old/vlib/v/tests/cflags/v.mod | 3 + .../vlib/v/tests/cflags/vmodroot_and_vroot_test.v | 18 + v_windows/v/old/vlib/v/tests/channels_test.v | 29 + .../v/tests/clash_var_name_of_array_and_map_test.v | 78 + v_windows/v/old/vlib/v/tests/complex_assign_test.v | 115 + v_windows/v/old/vlib/v/tests/comptime_at_test.v | 122 + .../v/tests/comptime_attribute_selector_test.v | 33 + .../v/tests/comptime_bittness_and_endianess_test.v | 25 + v_windows/v/old/vlib/v/tests/comptime_call_test.v | 67 + .../vlib/v/tests/comptime_field_selector_test.v | 62 + v_windows/v/old/vlib/v/tests/comptime_for_test.v | 100 + .../v/old/vlib/v/tests/comptime_if_expr_test.v | 125 + v_windows/v/old/vlib/v/tests/comptime_if_is_test.v | 28 + .../old/vlib/v/tests/comptime_if_pkgconfig_test.v | 10 + v_windows/v/old/vlib/v/tests/comptime_if_test.v | 54 + .../old/vlib/v/tests/comptime_if_threads_no_test.v | 11 + .../vlib/v/tests/comptime_if_threads_yes_test.v | 11 + .../v/old/vlib/v/tests/comptime_method_args_test.v | 54 + .../vlib/v/tests/const_can_use_optionals_test.v | 22 + .../tests/const_comptime_eval_before_vinit_test.v | 144 + v_windows/v/old/vlib/v/tests/const_embed_test.v | 11 + ..._eval_simple_int_expressions_at_comptime_test.v | 26 + .../v/old/vlib/v/tests/const_init_order_test.v | 10 + .../vlib/v/tests/const_reference_argument_test.v | 9 + .../v/old/vlib/v/tests/const_representation_test.v | 39 + v_windows/v/old/vlib/v/tests/const_test.v | 54 + v_windows/v/old/vlib/v/tests/conversions_test.v | 16 + v_windows/v/old/vlib/v/tests/cross_assign_test.v | 159 + v_windows/v/old/vlib/v/tests/cstrings_test.v | 14 + v_windows/v/old/vlib/v/tests/defer_return_test.v | 119 + v_windows/v/old/vlib/v/tests/defer_test.v | 156 + .../vlib/v/tests/differently_named_structs_test.v | 30 + .../v/old/vlib/v/tests/double_ref_deref_test.v | 10 + v_windows/v/old/vlib/v/tests/dump_fns_test.v | 22 + .../v/old/vlib/v/tests/enum_array_field_test.v | 20 + v_windows/v/old/vlib/v/tests/enum_bitfield_test.v | 52 + .../v/tests/enum_default_value_in_struct_test.v | 35 + v_windows/v/old/vlib/v/tests/enum_hex_test.v | 29 + v_windows/v/old/vlib/v/tests/enum_test.v | 146 + v_windows/v/old/vlib/v/tests/failing_tests_test.v | 32 + .../v/old/vlib/v/tests/field_publicity/embed.v | 10 + v_windows/v/old/vlib/v/tests/filter_in_map_test.v | 5 + .../old/vlib/v/tests/fixed_array_const_size_test.v | 46 + .../v/old/vlib/v/tests/fixed_array_init_test.v | 48 + .../v/old/vlib/v/tests/fixed_array_of_fn_test.v | 9 + v_windows/v/old/vlib/v/tests/fixed_array_test.v | 121 + .../old/vlib/v/tests/fixed_array_to_string_test.v | 41 + v_windows/v/old/vlib/v/tests/fn_assignment_test.v | 53 + .../v/old/vlib/v/tests/fn_cross_assign_test.v | 68 + .../fn_expecting_ref_but_returning_struct_test.v | 27 + ...ing_ref_but_returning_struct_time_module_test.v | 19 + v_windows/v/old/vlib/v/tests/fn_high_test.v | 148 + .../v/old/vlib/v/tests/fn_index_direct_call_test.v | 33 + .../v/old/vlib/v/tests/fn_multiple_returns_test.v | 84 + v_windows/v/old/vlib/v/tests/fn_mut_args_test.v | 69 + v_windows/v/old/vlib/v/tests/fn_return_fn_test.v | 20 + .../v/old/vlib/v/tests/fn_shared_return_test.v | 70 + v_windows/v/old/vlib/v/tests/fn_test.v | 170 + .../v/old/vlib/v/tests/fn_type_aliases_test.v | 15 + v_windows/v/old/vlib/v/tests/fn_variadic_test.v | 99 + .../tests/fn_with_fixed_array_function_args_test.v | 23 + .../v/old/vlib/v/tests/for_c_multi_vars_test.v | 34 + v_windows/v/old/vlib/v/tests/for_in_alias_test.v | 26 + .../tests/for_in_containers_of_fixed_array_test.v | 113 + .../v/old/vlib/v/tests/for_in_iterator_test.v | 44 + .../tests/for_in_mut_reference_selector_val_test.v | 26 + v_windows/v/old/vlib/v/tests/for_in_mut_val_test.v | 109 + .../v/old/vlib/v/tests/for_in_optional_test.v | 8 + .../vlib/v/tests/for_label_continue_break_test.v | 89 + v_windows/v/old/vlib/v/tests/for_loops_2_test.v | 55 + v_windows/v/old/vlib/v/tests/for_loops_test.v | 77 + v_windows/v/old/vlib/v/tests/for_smartcast_test.v | 56 + v_windows/v/old/vlib/v/tests/generic_chan_test.v | 22 + .../tests/generic_fn_assign_generics_struct_test.v | 38 + .../v/tests/generic_fn_infer_fixed_array_test.v | 30 + .../v/old/vlib/v/tests/generic_fn_infer_map_test.v | 36 + .../vlib/v/tests/generic_fn_infer_modifier_test.v | 16 + .../v/tests/generic_fn_infer_multi_paras_test.v | 50 + .../v/tests/generic_fn_infer_nested_struct_test.v | 20 + .../vlib/v/tests/generic_fn_infer_struct_test.v | 19 + .../v/old/vlib/v/tests/generic_fn_infer_test.v | 42 + .../tests/generic_fn_returning_type_with_T_test.v | 40 + .../old/vlib/v/tests/generic_fn_typeof_name_test.v | 60 + .../vlib/v/tests/generic_fn_upper_name_type_test.v | 18 + .../generic_functions_with_normal_function_test.v | 12 + .../v/old/vlib/v/tests/generic_interface_test.v | 98 + .../v/old/vlib/v/tests/generic_sumtype_test.v | 78 + .../old/vlib/v/tests/generics_array_typedef_test.v | 31 + ...generics_assign_reference_generic_struct_test.v | 46 + .../tests/generics_call_with_reference_arg_test.v | 26 + .../generics_fn_return_generic_interface_test.v | 32 + .../generics_from_modules/genericmodule/take.v | 8 + .../v/tests/generics_from_modules/inference_test.v | 35 + .../v/tests/generics_in_big_struct_method_test.v | 27 + .../v/old/vlib/v/tests/generics_in_generics_test.v | 12 + .../v/old/vlib/v/tests/generics_indirect_test.v | 32 + .../vlib/v/tests/generics_interface_decl_test.v | 23 + .../vlib/v/tests/generics_interface_method_test.v | 35 + ...ics_interface_with_multi_generic_structs_test.v | 58 + ...erics_interface_with_multi_generic_types_test.v | 37 + .../tests/generics_method_on_receiver_types_test.v | 13 + .../v/old/vlib/v/tests/generics_method_test.v | 70 + .../vlib/v/tests/generics_multi_array_in_test.v | 14 + .../tests/generics_multi_types_struct_init_test.v | 41 + .../v/tests/generics_return_generics_struct_test.v | 145 + ...eturn_inconsistent_types_generics_struct_test.v | 97 + .../v/tests/generics_return_multi_array_test.v | 25 + ...generics_return_multiple_generics_struct_test.v | 38 + ...enerics_return_recursive_generics_struct_test.v | 18 + ...enerics_return_reference_generics_struct_test.v | 15 + .../v/tests/generics_struct_anon_fn_fields_test.v | 14 + .../v/tests/generics_struct_anon_fn_type_test.v | 69 + .../v/old/vlib/v/tests/generics_struct_init_test.v | 15 + .../vlib/v/tests/generics_struct_to_string_test.v | 33 + v_windows/v/old/vlib/v/tests/generics_test.v | 490 + .../v/tests/generics_with_anon_generics_fn_test.v | 57 + ...ith_cascaded_multiple_nested_generics_fn_test.v | 35 + .../v/tests/generics_with_embed_generics_test.v | 36 + .../v/tests/generics_with_fixed_array_type_test.v | 10 + ...with_generics_fn_return_generics_fn_type_test.v | 105 + ...generics_with_generics_fn_type_parameter_test.v | 67 + .../generics_with_generics_struct_init_test.v | 32 + .../generics_with_generics_struct_receiver_test.v | 13 + ...enerics_with_multi_generics_struct_types_test.v | 19 + ...s_with_multiple_generics_struct_receiver_test.v | 28 + ...enerics_with_nested_external_generics_fn_test.v | 21 + ...generics_with_nested_generic_struct_init_test.v | 24 + .../tests/generics_with_nested_generics_fn_test.v | 27 + .../generics_with_recursive_generics_fn_test.v | 43 + .../generics_with_recursive_generics_struct_test.v | 29 + .../generics_with_variadic_generic_args_test.v | 31 + v_windows/v/old/vlib/v/tests/go_array_wait_test.v | 51 + .../v/old/vlib/v/tests/go_call_generic_fn_test.v | 14 + .../vlib/v/tests/go_call_interface_method_test.v | 28 + .../go_handle_for_functions_returning_array_test.v | 9 + v_windows/v/old/vlib/v/tests/go_wait_1_test.v | 10 + v_windows/v/old/vlib/v/tests/go_wait_2_test.v | 23 + v_windows/v/old/vlib/v/tests/go_wait_3_test.v | 22 + v_windows/v/old/vlib/v/tests/go_wait_option_test.v | 79 + .../v/tests/go_wait_with_fn_of_interface_para.v | 17 + v_windows/v/old/vlib/v/tests/goto_test.v | 13 + v_windows/v/old/vlib/v/tests/heap_interface_test.v | 53 + v_windows/v/old/vlib/v/tests/heap_reference_test.v | 101 + v_windows/v/old/vlib/v/tests/heap_struct_test.v | 134 + .../v/old/vlib/v/tests/if_expr_of_optional_test.v | 55 + v_windows/v/old/vlib/v/tests/if_expression_test.v | 215 + v_windows/v/old/vlib/v/tests/if_guard_test.v | 126 + .../vlib/v/tests/if_smartcast_multi_conds_test.v | 47 + .../if_smartcast_nested_selector_exprs_test.v | 28 + v_windows/v/old/vlib/v/tests/if_smartcast_test.v | 311 + .../v/old/vlib/v/tests/imported_symbols_test.v | 53 + v_windows/v/old/vlib/v/tests/in_expression_test.v | 274 + v_windows/v/old/vlib/v/tests/infix_expr_test.v | 83 + v_windows/v/old/vlib/v/tests/init_global_test.v | 174 + v_windows/v/old/vlib/v/tests/inout/.gitignore | 3 + v_windows/v/old/vlib/v/tests/inout/bad_st_as.out | 5 + v_windows/v/old/vlib/v/tests/inout/bad_st_as.vv | 13 + .../vlib/v/tests/inout/cli_command_no_execute.out | 0 .../vlib/v/tests/inout/cli_command_no_execute.vv | 11 + .../vlib/v/tests/inout/cli_root_default_help.out | 7 + .../vlib/v/tests/inout/cli_root_default_help.vv | 7 + v_windows/v/old/vlib/v/tests/inout/compiler_test.v | 101 + .../v/old/vlib/v/tests/inout/dump_expression.out | 17 + .../v/old/vlib/v/tests/inout/dump_expression.vv | 38 + v_windows/v/old/vlib/v/tests/inout/enum_print.out | 6 + v_windows/v/old/vlib/v/tests/inout/enum_print.vv | 24 + v_windows/v/old/vlib/v/tests/inout/file.html | 37 + v_windows/v/old/vlib/v/tests/inout/file.md | 8 + .../v/old/vlib/v/tests/inout/fixed_array_index.out | 6 + .../v/old/vlib/v/tests/inout/fixed_array_index.vv | 4 + .../v/old/vlib/v/tests/inout/fixed_array_slice.out | 5 + .../v/old/vlib/v/tests/inout/fixed_array_slice.vv | 3 + v_windows/v/old/vlib/v/tests/inout/footer.md | 1 + v_windows/v/old/vlib/v/tests/inout/header.md | 1 + v_windows/v/old/vlib/v/tests/inout/hello.out | 1 + v_windows/v/old/vlib/v/tests/inout/hello.vv | 3 + v_windows/v/old/vlib/v/tests/inout/hello_devs.out | 6 + v_windows/v/old/vlib/v/tests/inout/hello_devs.vv | 7 + .../v/old/vlib/v/tests/inout/nested_structs.out | 11 + .../v/old/vlib/v/tests/inout/nested_structs.vv | 22 + v_windows/v/old/vlib/v/tests/inout/os.out | 6 + v_windows/v/old/vlib/v/tests/inout/os.vv | 13 + .../v/old/vlib/v/tests/inout/panic_with_cg.out | 5 + .../v/old/vlib/v/tests/inout/panic_with_cg.vv | 8 + .../inout/printing_fixed_array_of_pointers.out | 60 + .../inout/printing_fixed_array_of_pointers.vv | 47 + .../v/old/vlib/v/tests/inout/string_interp.out | 1 + .../v/old/vlib/v/tests/inout/string_interp.vv | 5 + .../vlib/v/tests/inout/tmpl_all_in_one_folder.out | 8 + .../vlib/v/tests/inout/tmpl_all_in_one_folder.vv | 8 + .../v/old/vlib/v/tests/inout/tmpl_parse_html.out | 37 + .../v/old/vlib/v/tests/inout/tmpl_parse_html.vv | 8 + v_windows/v/old/vlib/v/tests/int_cmp_test.v | 22 + .../old/vlib/v/tests/interface_auto_str_gen_test.v | 50 + .../array_of_interfaces_test.v | 28 + .../array_of_interfaces_with_utility_fn_test.v | 28 + .../assign_to_interface_field_test.v | 27 + .../interface_edge_cases/empty_interface_1_test.v | 16 + .../empty_interface_println_test.v | 11 + ...fn_returning_voidptr_casted_as_interface_test.v | 27 + .../vlib/v/tests/interface_edge_cases/i1_test.v | 31 + .../vlib/v/tests/interface_edge_cases/i2_test.v | 34 + .../vlib/v/tests/interface_edge_cases/i3_test.v | 33 + .../vlib/v/tests/interface_edge_cases/i4_test.v | 31 + .../vlib/v/tests/interface_edge_cases/i5_test.v | 31 + .../vlib/v/tests/interface_edge_cases/i6_test.v | 31 + .../vlib/v/tests/interface_edge_cases/i7_test.v | 38 + .../vlib/v/tests/interface_edge_cases/i8_test.v | 31 + .../vlib/v/tests/interface_edge_cases/i9_test.v | 35 + .../interface_many_named_test.v | 26 + .../pass_voidptr_as_interface_reference_test.v | 19 + .../voidptr_casted_as_an_interface_test.v | 26 + .../tests/interface_embedding_deep_nesting_test.v | 415 + .../v/tests/interface_embedding_recursive_test.v | 78 + .../v/old/vlib/v/tests/interface_embedding_test.v | 56 + .../interface_embedding_with_interface_para_test.v | 38 + .../v/old/vlib/v/tests/interface_fields_test.v | 74 + .../vlib/v/tests/interface_fields_typearray_test.v | 7 + .../interface_fn_return_array_of_interface_test.v | 26 + .../v/tests/interface_fn_return_with_struct_init.v | 34 + .../old/vlib/v/tests/interface_nested_field_test.v | 75 + .../tests/interface_only_decl_with_optional_test.v | 8 + .../v/old/vlib/v/tests/interface_struct_test.v | 117 + v_windows/v/old/vlib/v/tests/interface_test.v | 338 + .../v/old/vlib/v/tests/interface_variadic_test.v | 42 + v_windows/v/old/vlib/v/tests/interfaces_map_test.v | 54 + v_windows/v/old/vlib/v/tests/interop_test.v | 21 + v_windows/v/old/vlib/v/tests/isreftype_test.v | 58 + .../v/old/vlib/v/tests/keep_args_alive_test.v | 75 + .../v/old/vlib/v/tests/keep_args_alive_test_c.h | 44 + v_windows/v/old/vlib/v/tests/local/local.v | 5 + v_windows/v/old/vlib/v/tests/local_test.v | 5 + v_windows/v/old/vlib/v/tests/lock_selector_test.v | 50 + v_windows/v/old/vlib/v/tests/map_alias_key_test.v | 34 + .../old/vlib/v/tests/map_and_array_with_fns_test.v | 122 + .../v/tests/map_assign_array_of_interface_test.v | 40 + .../vlib/v/tests/map_complex_fixed_array_test.v | 45 + v_windows/v/old/vlib/v/tests/map_enum_keys_test.v | 38 + v_windows/v/old/vlib/v/tests/map_equality_test.v | 38 + .../old/vlib/v/tests/map_high_order_assign_test.v | 6 + v_windows/v/old/vlib/v/tests/map_key_expr_test.v | 31 + .../vlib/v/tests/map_literals_method_call_test.v | 10 + v_windows/v/old/vlib/v/tests/map_mut_fn_test.v | 20 + v_windows/v/old/vlib/v/tests/map_to_string_test.v | 49 + v_windows/v/old/vlib/v/tests/map_type_alias_test.v | 8 + v_windows/v/old/vlib/v/tests/map_value_init_test.v | 9 + v_windows/v/old/vlib/v/tests/maps_equal_test.v | 13 + .../vlib/v/tests/match_compound_type_cond_test.v | 49 + .../v/old/vlib/v/tests/match_error_to_none_test.v | 25 + .../v/tests/match_expr_returning_optional_test.v | 36 + .../tests/match_expr_with_if_or_match_expr_test.v | 43 + .../vlib/v/tests/match_expr_with_one_branch_test.v | 12 + .../vlib/v/tests/match_expression_for_types_test.v | 64 + ...tch_expression_with_fn_names_in_branches_test.v | 63 + .../v/old/vlib/v/tests/match_in_fn_call_test.v | 49 + .../old/vlib/v/tests/match_in_if_expression_test.v | 13 + .../v/old/vlib/v/tests/match_in_map_init_test.v | 28 + .../v/old/vlib/v/tests/match_in_map_or_expr_test.v | 15 + .../v/old/vlib/v/tests/match_interface_test.v | 22 + .../v/old/vlib/v/tests/match_smartcast_test.v | 99 + .../v/tests/match_sumtype_var_shadow_and_as_test.v | 44 + v_windows/v/old/vlib/v/tests/match_test.v | 287 + .../match_with_complex_exprs_in_branches_test.v | 44 + .../tests/match_with_complex_sumtype_exprs_test.v | 153 + .../v/tests/match_with_multi_sumtype_exprs_test.v | 37 + .../old/vlib/v/tests/methods_on_interfaces_test.v | 20 + .../vlib/v/tests/missing_config_struct_arg_test.v | 24 + v_windows/v/old/vlib/v/tests/module_test.v | 46 + .../v/old/vlib/v/tests/module_type_cast_test.v | 8 + .../tests/modules/acommentedmodule/commentedfile.v | 3 + .../modules/amodule/another_internal_module_test.v | 14 + .../v/tests/modules/amodule/internal_module_test.v | 15 + .../v/old/vlib/v/tests/modules/amodule/module.v | 14 + .../v/old/vlib/v/tests/modules/geometry/geometry.v | 50 + .../methods_struct_test.v | 25 + .../v/tests/modules/simplemodule/importing_test.v | 11 + .../v/tests/modules/simplemodule/simplemodule.v | 20 + .../vlib/v/tests/modules/submodules/submodules.v | 10 + .../v/tests/modules/submodules/submodules_test.v | 21 + .../vlib/v/tests/modules/submodules/test/test.v | 10 + .../v/tests/modules/submodules/test/test2/test2.v | 10 + .../v/tests/multiple_assign_array_index_test.v | 24 + .../v/old/vlib/v/tests/multiple_assign_test.v | 66 + .../v/tests/multiple_paths_in_vmodules/main.vv | 9 + .../multiple_paths_in_vmodules/path1/.gitignore | 4 + .../tests/multiple_paths_in_vmodules/path1/xxx/m.v | 5 + .../tests/multiple_paths_in_vmodules/path2/yyy/m.v | 5 + .../tests/multiple_paths_in_vmodules/path3/zzz/m.v | 5 + .../vmodules_overrides_test.v | 33 + .../old/vlib/v/tests/multiret_with_ptrtype_test.v | 16 + .../mut_receiver_returned_as_reference_test.v | 36 + v_windows/v/old/vlib/v/tests/mut_test.v | 370 + .../v/old/vlib/v/tests/named_break_continue_test.v | 46 + v_windows/v/old/vlib/v/tests/nest_defer_fn_test.v | 30 + .../v/tests/nested_anonfunc_and_for_break_test.v | 16 + v_windows/v/old/vlib/v/tests/nested_map_test.v | 34 + .../vlib/v/tests/nested_multiline_comments_test.v | 5 + .../v/old/vlib/v/tests/nested_option_call_test.v | 67 + .../v/old/vlib/v/tests/num_lit_call_method_test.v | 37 + v_windows/v/old/vlib/v/tests/offsetof_test.v | 43 + .../vlib/v/tests/operator_overloading_cmp_test.v | 35 + ...or_overloading_with_string_interpolation_test.v | 83 + v_windows/v/old/vlib/v/tests/option_2_test.v | 66 + .../old/vlib/v/tests/option_default_values_test.v | 136 + .../tests/option_if_assign_and_fallthrough_test.v | 46 + v_windows/v/old/vlib/v/tests/option_if_expr_test.v | 16 + v_windows/v/old/vlib/v/tests/option_in_loop_test.v | 36 + .../v/old/vlib/v/tests/option_print_errors_test.v | 20 + v_windows/v/old/vlib/v/tests/option_test.v | 381 + v_windows/v/old/vlib/v/tests/option_void_test.v | 50 + .../v/old/vlib/v/tests/optional_assign_test.v | 25 + .../v/old/vlib/v/tests/orm_sub_array_struct_test.v | 49 + v_windows/v/old/vlib/v/tests/orm_sub_struct_test.v | 34 + v_windows/v/old/vlib/v/tests/parser_line_test.v | 9 + .../v/old/vlib/v/tests/pointer_arithmetic_test.v | 6 + .../vlib/v/tests/pointers_multilevel_casts_test.v | 76 + v_windows/v/old/vlib/v/tests/pointers_str_test.v | 8 + v_windows/v/old/vlib/v/tests/pointers_test.v | 46 + v_windows/v/old/vlib/v/tests/prefix_expr_test.v | 29 + v_windows/v/old/vlib/v/tests/print_test.v | 4 + .../old/vlib/v/tests/printing_c_structs/cstruct.h | 4 + .../printing_c_structs/string_interpolation_test.v | 26 + .../v/old/vlib/v/tests/printing_c_structs/v.mod | 0 v_windows/v/old/vlib/v/tests/prod/.gitignore | 2 + .../v/tests/prod/asserts_should_be_skipped.prod.v | 4 + .../asserts_should_be_skipped.prod.v.expected.txt | 1 + v_windows/v/old/vlib/v/tests/prod/assoc.prod.v | 27 + .../vlib/v/tests/prod/assoc.prod.v.expected.txt | 4 + v_windows/v/old/vlib/v/tests/prod_test.v | 28 + .../v/old/vlib/v/tests/profile/profile_test.v | 23 + .../v/old/vlib/v/tests/profile/profile_test_1.v | 11 + .../vlib/v/tests/project_with_c_code/.gitignore | 3 + .../vlib/v/tests/project_with_c_code/.v.mod.stop | 4 + .../v/old/vlib/v/tests/project_with_c_code/main.v | 9 + .../vlib/v/tests/project_with_c_code/main_test.v | 5 + .../v/tests/project_with_c_code/mod1/c/header.h | 10 + .../project_with_c_code/mod1/c/implementation.c | 5 + .../vlib/v/tests/project_with_c_code/mod1/v.mod | 5 + .../v/tests/project_with_c_code/mod1/wrapper.v | 17 + .../vlib/v/tests/project_with_c_code_2/.gitignore | 4 + .../vlib/v/tests/project_with_c_code_2/.v.mod.stop | 5 + .../old/vlib/v/tests/project_with_c_code_2/main.v | 14 + .../v/tests/project_with_c_code_2/main2_test.v | 7 + .../v/tests/project_with_c_code_2/modc/header.h | 19 + .../vlib/v/tests/project_with_c_code_2/modc/impl.c | 32 + .../vlib/v/tests/project_with_c_code_2/modc/v.mod | 5 + .../v/tests/project_with_c_code_2/modc/wrapper.v | 53 + .../v/tests/project_with_c_code_ct_ifs/a_linux.h | 1 + .../tests/project_with_c_code_ct_ifs/a_nonlinux.h | 1 + .../project_with_c_code_ct_ifs/ctimeifblock.v | 11 + .../vlib/v/tests/project_with_c_code_ct_ifs/v.mod | 0 .../.gitignore | 2 + .../README.md | 16 + .../bin/a_program_under_bin_can_find_mod1_test.v | 12 + .../bin/main.vsh | 14 + .../mod1/m.v | 5 + .../mod1/mod11/m.v | 5 + .../mod1/mod12/m.v | 5 + .../mod1/mod13/m.v | 5 + .../mod1/mod14/m.v | 7 + .../mod1/submodule/m.v | 15 + .../mod1/v.mod | 7 + .../tests/submodule_test.v | 10 + .../project_with_modules_having_submodules/v.mod | 7 + .../v/tests/project_with_tests_for_main/README.md | 25 + .../v/tests/project_with_tests_for_main/main.v | 8 + .../project_with_tests_for_main/my_other_test.v | 7 + .../v/tests/project_with_tests_for_main/my_test.v | 11 + .../vlib/v/tests/project_with_tests_for_main/v.mod | 5 + .../v/tests/projects_that_should_compile_test.v | 25 + v_windows/v/old/vlib/v/tests/ptr_arithmetic_test.v | 70 + v_windows/v/old/vlib/v/tests/raw_string_test.v | 3 + v_windows/v/old/vlib/v/tests/ref_return_test.v | 14 + v_windows/v/old/vlib/v/tests/ref_struct_test.v | 96 + .../v/old/vlib/v/tests/reference_return_test.v | 25 + .../old/vlib/v/tests/reliability/semaphore_wait.v | 142 + .../vlib/v/tests/repeated_multiret_values_test.v | 13 + v_windows/v/old/vlib/v/tests/repl/.gitattributes | 2 + v_windows/v/old/vlib/v/tests/repl/.gitignore | 3 + v_windows/v/old/vlib/v/tests/repl/README.md | 25 + .../v/old/vlib/v/tests/repl/array_filter.repl | 3 + v_windows/v/old/vlib/v/tests/repl/array_init.repl | 3 + .../v/old/vlib/v/tests/repl/bad_in_type.repl.skip | 4 + .../vlib/v/tests/repl/chained_fields/bd.repl.skip | 17 + .../vlib/v/tests/repl/chained_fields/c.repl.skip | 17 + .../vlib/v/tests/repl/chained_fields/c2.repl.skip | 5 + .../vlib/v/tests/repl/chained_fields/d.repl.skip | 5 + .../vlib/v/tests/repl/chained_fields/ef.repl.skip | 19 + .../vlib/v/tests/repl/conditional_blocks/for.repl | 8 + .../vlib/v/tests/repl/conditional_blocks/if.repl | 5 + .../v/tests/repl/conditional_blocks/if_else.repl | 7 + .../v/old/vlib/v/tests/repl/default_printing.repl | 5 + .../v/old/vlib/v/tests/repl/empty_struct.repl.skip | 4 + .../vlib/v/tests/repl/entire_commented_module.repl | 7 + v_windows/v/old/vlib/v/tests/repl/error.repl | 7 + .../v/old/vlib/v/tests/repl/error_nosave.repl.skip | 5 + .../v/old/vlib/v/tests/repl/function.repl.skip | 4 + .../repl/immutable_len_fields/fields.1.repl.skip | 11 + .../repl/immutable_len_fields/fields.2.repl.skip | 11 + .../repl/immutable_len_fields/fields.3.repl.skip | 11 + v_windows/v/old/vlib/v/tests/repl/import.repl | 4 + .../v/old/vlib/v/tests/repl/naked_strings.repl | 3 + v_windows/v/old/vlib/v/tests/repl/newlines.repl | 3 + v_windows/v/old/vlib/v/tests/repl/nomain.repl | 4 + v_windows/v/old/vlib/v/tests/repl/nothing.repl | 1 + .../vlib/v/tests/repl/open_close_string_check.repl | 5 + v_windows/v/old/vlib/v/tests/repl/option.repl.skip | 7 + .../v/old/vlib/v/tests/repl/optional_call.repl | 3 + .../v/old/vlib/v/tests/repl/postfix_operators.repl | 4 + v_windows/v/old/vlib/v/tests/repl/println.repl | 3 + v_windows/v/old/vlib/v/tests/repl/repl_test.v | 88 + v_windows/v/old/vlib/v/tests/repl/runner/runner.v | 145 + v_windows/v/old/vlib/v/tests/repl/var_decl.repl | 4 + ...reserved_keywords_can_be_escaped_with_at_test.v | 13 + v_windows/v/old/vlib/v/tests/return_in_lock_test.v | 53 + v_windows/v/old/vlib/v/tests/return_voidptr_test.v | 26 + .../v/tests/reusable_mut_multiret_values_test.v | 23 + .../a.v | 8 + .../b.v | 14 + .../v/old/vlib/v/tests/run_project_folders_test.v | 36 + .../old/vlib/v/tests/run_v_code_from_stdin_test.v | 28 + v_windows/v/old/vlib/v/tests/semaphore_test.v | 25 + .../v/old/vlib/v/tests/semaphore_timed_test.v | 28 + v_windows/v/old/vlib/v/tests/shared_arg_test.v | 94 + v_windows/v/old/vlib/v/tests/shared_array_test.v | 74 + .../v/old/vlib/v/tests/shared_autolock_test.v | 38 + v_windows/v/old/vlib/v/tests/shared_elem_test.v | 170 + .../v/old/vlib/v/tests/shared_fn_return_test.v | 68 + v_windows/v/old/vlib/v/tests/shared_lock_2_test.v | 52 + v_windows/v/old/vlib/v/tests/shared_lock_3_test.v | 57 + v_windows/v/old/vlib/v/tests/shared_lock_4_test.v | 57 + v_windows/v/old/vlib/v/tests/shared_lock_5_test.v | 68 + v_windows/v/old/vlib/v/tests/shared_lock_6_test.v | 59 + .../v/old/vlib/v/tests/shared_lock_expr_test.v | 123 + v_windows/v/old/vlib/v/tests/shared_lock_test.v | 52 + v_windows/v/old/vlib/v/tests/shared_map_test.v | 157 + .../old/vlib/v/tests/shared_unordered_mixed_test.v | 89 + v_windows/v/old/vlib/v/tests/shift_test.v | 71 + .../vlib/v/tests/short_struct_param_syntax_test.v | 18 + v_windows/v/old/vlib/v/tests/sizeof_2_test.v | 8 + v_windows/v/old/vlib/v/tests/sizeof_test.v | 26 + .../v/tests/skip_unused/assert_passes_test.run.out | 0 .../assert_passes_test.skip_unused.run.out | 0 .../vlib/v/tests/skip_unused/assert_passes_test.vv | 3 + .../v/tests/skip_unused/assert_works_test.run.out | 5 + .../assert_works_test.skip_unused.run.out | 5 + .../vlib/v/tests/skip_unused/assert_works_test.vv | 3 + .../v/old/vlib/v/tests/skip_unused/hw.run.out | 1 + .../v/tests/skip_unused/hw.skip_unused.run.out | 1 + v_windows/v/old/vlib/v/tests/skip_unused/hw.vv | 1 + .../skip_unused/println_os_executable.run.out | 1 + .../println_os_executable.skip_unused.run.out | 1 + .../v/tests/skip_unused/println_os_executable.vv | 7 + .../v/tests/sorting_by_different_criteria_test.v | 31 + .../old/vlib/v/tests/sorting_by_references_test.v | 32 + .../static_arrays_using_const_for_size_test.v | 39 + v_windows/v/old/vlib/v/tests/static_vars_test.v | 16 + v_windows/v/old/vlib/v/tests/str_gen_test.v | 446 + v_windows/v/old/vlib/v/tests/string_alias_test.v | 17 + .../v/old/vlib/v/tests/string_index_or_test.v | 52 + .../vlib/v/tests/string_interpolation_alias_test.v | 28 + .../vlib/v/tests/string_interpolation_array_test.v | 110 + .../v/tests/string_interpolation_custom_str_test.v | 20 + .../v/tests/string_interpolation_floats_test.v | 29 + .../v/tests/string_interpolation_function_test.v | 53 + .../tests/string_interpolation_multi_return_test.v | 47 + .../v/tests/string_interpolation_multistmt_test.v | 16 + ...string_interpolation_of_array_of_structs_test.v | 113 + .../v/tests/string_interpolation_shared_test.v | 66 + .../tests/string_interpolation_string_args_test.v | 31 + .../v/tests/string_interpolation_struct_test.v | 74 + .../v/tests/string_interpolation_sumtype_test.v | 9 + .../v/old/vlib/v/tests/string_interpolation_test.v | 201 + .../v/tests/string_interpolation_variadic_test.v | 44 + .../v/tests/string_struct_interpolation_test.v | 24 + .../v/old/vlib/v/tests/strings_unicode_test.v | 10 + ..._allow_both_field_defaults_and_skip_flag_test.v | 23 + .../v/tests/struct_chained_fields_correct_test.v | 47 + .../vlib/v/tests/struct_child_field_default_test.v | 22 + v_windows/v/old/vlib/v/tests/struct_embed_test.v | 224 + .../v/old/vlib/v/tests/struct_eq_op_only_test.v | 15 + .../v/old/vlib/v/tests/struct_equality_test.v | 25 + ...truct_field_default_value_interface_cast_test.v | 19 + .../struct_field_default_value_sumtype_cast_test.v | 19 + .../old/vlib/v/tests/struct_fields_required_test.v | 32 + .../v/tests/struct_fields_storing_functions_test.v | 29 + v_windows/v/old/vlib/v/tests/struct_ierror_test.v | 10 + .../old/vlib/v/tests/struct_init_and_assign_test.v | 26 + .../v/old/vlib/v/tests/struct_map_method_test.v | 35 + v_windows/v/old/vlib/v/tests/struct_test.v | 416 + .../v/old/vlib/v/tests/struct_transmute_test.v | 42 + ...ructs_with_voidptr_fields_can_be_printed_test.v | 13 + .../old/vlib/v/tests/sum_type_common_fields_test.v | 47 + v_windows/v/old/vlib/v/tests/sum_type_test.v | 714 + v_windows/v/old/vlib/v/tests/sumtype_assign_test.v | 46 + v_windows/v/old/vlib/v/tests/sumtype_calls_test.v | 77 + .../v/old/vlib/v/tests/sumtype_equality_test.v | 33 + .../v/old/vlib/v/tests/sumtype_literal_test.v | 84 + .../tests/sumtype_str_for_subtypes_with_str_test.v | 43 + v_windows/v/old/vlib/v/tests/sumtype_str_test.v | 77 + .../v/old/vlib/v/tests/supports__likely__test.v | 32 + v_windows/v/old/vlib/v/tests/testcase_leak.v | 12 + .../v/tests/testdata/enum_in_builtin/builtin.v | 18 + .../old/vlib/v/tests/testdata/enum_in_builtin/c.v | 6 + .../vlib/v/tests/testdata/enum_in_builtin/main.v | 5 + .../v/tests/testdata/sizeof_used_in_assert_test.v | 16 + .../tests_returning_options_failing_test.v | 20 + .../v/old/vlib/v/tests/thread_to_string_test.v | 8 + v_windows/v/old/vlib/v/tests/tmpl/base.txt | 9 + v_windows/v/old/vlib/v/tests/tmpl/inner.txt | 17 + v_windows/v/old/vlib/v/tests/tmpl_test.v | 84 + .../v/tests/type_alias_of_pointer_types_test.v | 74 + .../v/tests/type_alias_str_method_override_test.v | 29 + v_windows/v/old/vlib/v/tests/type_alias_test.v | 42 + v_windows/v/old/vlib/v/tests/type_name_test.v | 18 + v_windows/v/old/vlib/v/tests/type_promotion_test.v | 100 + v_windows/v/old/vlib/v/tests/type_voidptr_test.v | 21 + .../v/old/vlib/v/tests/typeof_simple_types_test.v | 72 + v_windows/v/old/vlib/v/tests/typeof_test.v | 187 + .../old/vlib/v/tests/unreachable_code_paths_test.v | 11 + v_windows/v/old/vlib/v/tests/unsafe_test.v | 104 + .../vlib/v/tests/valgrind/1.strings_and_arrays.v | 410 + .../v/tests/valgrind/fn_returning_string_param.v | 13 + .../fn_with_return_should_free_local_vars.v | 22 + .../old/vlib/v/tests/valgrind/option_reassigned.v | 7 + .../v/old/vlib/v/tests/valgrind/option_simple.v | 6 + .../vlib/v/tests/valgrind/simple_interpolation.v | 5 + .../valgrind/simple_interpolation_script_mode.v | 4 + .../simple_interpolation_script_mode_more_scopes.v | 7 + .../v/old/vlib/v/tests/valgrind/struct_field.v | 17 + .../v/old/vlib/v/tests/valgrind/valgrind_test.v | 113 + .../tests/vargs_auto_str_method_and_println_test.v | 50 + .../v/old/vlib/v/tests/vargs_empty_param_test.v | 20 + v_windows/v/old/vlib/v/tests/vmod_parser_test.v | 51 + .../old/vlib/v/tests/voidptr_to_u64_cast_a_test.v | 10 + .../old/vlib/v/tests/voidptr_to_u64_cast_b_test.v | 10 + .../v/tests/working_with_an_empty_struct_test.v | 16 + v_windows/v/old/vlib/v/token/position.v | 51 + v_windows/v/old/vlib/v/token/token.v | 490 + v_windows/v/old/vlib/v/util/diff.v | 17 + v_windows/v/old/vlib/v/util/diff/diff.v | 85 + v_windows/v/old/vlib/v/util/errors.v | 160 + v_windows/v/old/vlib/v/util/module.v | 122 + v_windows/v/old/vlib/v/util/quote.v | 134 + .../old/vlib/v/util/recompilation/recompilation.v | 26 + v_windows/v/old/vlib/v/util/scanning.v | 44 + v_windows/v/old/vlib/v/util/suggestions.v | 95 + v_windows/v/old/vlib/v/util/timers.v | 133 + v_windows/v/old/vlib/v/util/util.v | 487 + v_windows/v/old/vlib/v/util/version/version.v | 85 + v_windows/v/old/vlib/v/util/vtest/vtest.v | 37 + v_windows/v/old/vlib/v/vcache/vcache.v | 131 + v_windows/v/old/vlib/v/vcache/vcache_test.v | 101 + v_windows/v/old/vlib/v/vet/vet.v | 36 + v_windows/v/old/vlib/v/vmod/parser.v | 265 + v_windows/v/old/vlib/v/vmod/vmod.v | 171 + v_windows/v/old/vlib/vweb/README.md | 141 + v_windows/v/old/vlib/vweb/assets/assets.v | 201 + v_windows/v/old/vlib/vweb/assets/assets_test.v | 179 + v_windows/v/old/vlib/vweb/request.v | 157 + v_windows/v/old/vlib/vweb/request_test.v | 138 + v_windows/v/old/vlib/vweb/route_test.v | 270 + v_windows/v/old/vlib/vweb/sse/sse.v | 77 + v_windows/v/old/vlib/vweb/tests/vweb_test.v | 298 + v_windows/v/old/vlib/vweb/tests/vweb_test_server.v | 118 + v_windows/v/old/vlib/vweb/vweb.v | 640 + v_windows/v/old/vlib/vweb/vweb_app_test.v | 63 + v_windows/v/old/vlib/x/json2/README.md | 175 + v_windows/v/old/vlib/x/json2/any_test.v | 130 + v_windows/v/old/vlib/x/json2/decoder.v | 200 + v_windows/v/old/vlib/x/json2/decoder_test.v | 61 + v_windows/v/old/vlib/x/json2/encoder.v | 179 + v_windows/v/old/vlib/x/json2/encoder_test.v | 29 + v_windows/v/old/vlib/x/json2/json2.v | 122 + v_windows/v/old/vlib/x/json2/json2_test.v | 398 + v_windows/v/old/vlib/x/json2/scanner.v | 306 + v_windows/v/old/vlib/x/json2/scanner_test.v | 351 + v_windows/v/old/vlib/x/ttf/README.md | 310 + v_windows/v/old/vlib/x/ttf/common.v | 205 + v_windows/v/old/vlib/x/ttf/render_bmp.v | 825 + v_windows/v/old/vlib/x/ttf/render_sokol_cpu.v | 210 + v_windows/v/old/vlib/x/ttf/text_block.v | 120 + v_windows/v/old/vlib/x/ttf/ttf.v | 1085 + v_windows/v/old/vlib/x/ttf/ttf_test.v | 237 + v_windows/v/old/vlib/x/ttf/ttf_test_data.bin | Bin 0 -> 16124 bytes v_windows/v/old/vmake.bat | 435 + 3304 files changed, 359064 insertions(+) create mode 100644 v_windows/v/old/BSDmakefile create mode 100644 v_windows/v/old/CHANGELOG.md create mode 100644 v_windows/v/old/CODE_OF_CONDUCT.md create mode 100644 v_windows/v/old/CONTRIBUTING.md create mode 100644 v_windows/v/old/Dockerfile create mode 100644 v_windows/v/old/Dockerfile.alpine create mode 100644 v_windows/v/old/Dockerfile.cross create mode 100644 v_windows/v/old/LICENSE create mode 100644 v_windows/v/old/Makefile create mode 100644 v_windows/v/old/README.md create mode 100644 v_windows/v/old/ROADMAP.md create mode 100644 v_windows/v/old/TESTS.md create mode 100644 v_windows/v/old/cmd/tools/bench/wyhash.v create mode 100644 v_windows/v/old/cmd/tools/check_os_api_parity.v create mode 100644 v_windows/v/old/cmd/tools/fast/.gitignore create mode 100644 v_windows/v/old/cmd/tools/fast/fast.v create mode 100644 v_windows/v/old/cmd/tools/fast/fast_job.v create mode 100644 v_windows/v/old/cmd/tools/fast/fast_main.js create mode 100644 v_windows/v/old/cmd/tools/fast/footer.html create mode 100644 v_windows/v/old/cmd/tools/fast/header.html create mode 100644 v_windows/v/old/cmd/tools/fuzz/fuzz.sh create mode 100644 v_windows/v/old/cmd/tools/fuzz/map_fuzz.v create mode 100644 v_windows/v/old/cmd/tools/gen1m.v create mode 100644 v_windows/v/old/cmd/tools/gen_vc.v create mode 100644 v_windows/v/old/cmd/tools/missdoc.v create mode 100644 v_windows/v/old/cmd/tools/modules/scripting/scripting.v create mode 100644 v_windows/v/old/cmd/tools/modules/testing/common.v create mode 100644 v_windows/v/old/cmd/tools/modules/vgit/vgit.v create mode 100644 v_windows/v/old/cmd/tools/modules/vhelp/vhelp.v create mode 100644 v_windows/v/old/cmd/tools/oldv.v create mode 100644 v_windows/v/old/cmd/tools/performance_compare.v create mode 100644 v_windows/v/old/cmd/tools/repeat.v create mode 100644 v_windows/v/old/cmd/tools/test_if_v_test_system_works.v create mode 100644 v_windows/v/old/cmd/tools/test_os_process.v create mode 100644 v_windows/v/old/cmd/tools/vast/cjson.v create mode 100644 v_windows/v/old/cmd/tools/vast/test/.gitignore create mode 100644 v_windows/v/old/cmd/tools/vast/test/demo.v create mode 100644 v_windows/v/old/cmd/tools/vast/vast.v create mode 100644 v_windows/v/old/cmd/tools/vbin2v.v create mode 100644 v_windows/v/old/cmd/tools/vbug.v create mode 100644 v_windows/v/old/cmd/tools/vbuild-examples.v create mode 100644 v_windows/v/old/cmd/tools/vbuild-tools.v create mode 100644 v_windows/v/old/cmd/tools/vbuild-vbinaries.v create mode 100644 v_windows/v/old/cmd/tools/vcheck-md.v create mode 100644 v_windows/v/old/cmd/tools/vcomplete.v create mode 100644 v_windows/v/old/cmd/tools/vcreate.v create mode 100644 v_windows/v/old/cmd/tools/vcreate_test.v create mode 100644 v_windows/v/old/cmd/tools/vdoc/html.v create mode 100644 v_windows/v/old/cmd/tools/vdoc/html_tag_escape_test.v create mode 100644 v_windows/v/old/cmd/tools/vdoc/markdown.v create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/arrow.svg create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/dark-mode.js create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/dark.svg create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/doc.css create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/doc.js create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/favicons/android-chrome-192x192.png create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/favicons/android-chrome-512x512.png create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/favicons/apple-touch-icon.png create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/favicons/browserconfig.xml create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/favicons/favicon-16x16.png create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/favicons/favicon-32x32.png create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/favicons/favicon.ico create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-144x144.png create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-150x150.png create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-310x150.png create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-310x310.png create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-70x70.png create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/favicons/safari-pinned-tab.svg create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/favicons/site.webmanifest create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/light.svg create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/link.svg create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/menu.svg create mode 100644 v_windows/v/old/cmd/tools/vdoc/resources/normalize.css create mode 100644 v_windows/v/old/cmd/tools/vdoc/tests/testdata/project1/main.out create mode 100644 v_windows/v/old/cmd/tools/vdoc/tests/testdata/project1/main.v create mode 100644 v_windows/v/old/cmd/tools/vdoc/tests/vdoc_file_test.v create mode 100644 v_windows/v/old/cmd/tools/vdoc/utils.v create mode 100644 v_windows/v/old/cmd/tools/vdoc/vdoc.v create mode 100644 v_windows/v/old/cmd/tools/vdoctor.exe create mode 100644 v_windows/v/old/cmd/tools/vdoctor.v create mode 100644 v_windows/v/old/cmd/tools/vfmt.v create mode 100644 v_windows/v/old/cmd/tools/vpm.exe create mode 100644 v_windows/v/old/cmd/tools/vpm.v create mode 100644 v_windows/v/old/cmd/tools/vrepl.exe create mode 100644 v_windows/v/old/cmd/tools/vrepl.v create mode 100644 v_windows/v/old/cmd/tools/vself.exe create mode 100644 v_windows/v/old/cmd/tools/vself.v create mode 100644 v_windows/v/old/cmd/tools/vsetup-freetype.v create mode 100644 v_windows/v/old/cmd/tools/vsymlink.v create mode 100644 v_windows/v/old/cmd/tools/vtest-all.v create mode 100644 v_windows/v/old/cmd/tools/vtest-cleancode.v create mode 100644 v_windows/v/old/cmd/tools/vtest-fmt.v create mode 100644 v_windows/v/old/cmd/tools/vtest-parser.v create mode 100644 v_windows/v/old/cmd/tools/vtest-self.v create mode 100644 v_windows/v/old/cmd/tools/vtest.v create mode 100644 v_windows/v/old/cmd/tools/vtracev.v create mode 100644 v_windows/v/old/cmd/tools/vup.exe create mode 100644 v_windows/v/old/cmd/tools/vup.v create mode 100644 v_windows/v/old/cmd/tools/vvet/tests/array_init_one_val.out create mode 100644 v_windows/v/old/cmd/tools/vvet/tests/array_init_one_val.vv create mode 100644 v_windows/v/old/cmd/tools/vvet/tests/indent_with_space.out create mode 100644 v_windows/v/old/cmd/tools/vvet/tests/indent_with_space.vv create mode 100644 v_windows/v/old/cmd/tools/vvet/tests/module_file_test.out create mode 100644 v_windows/v/old/cmd/tools/vvet/tests/module_file_test.vv create mode 100644 v_windows/v/old/cmd/tools/vvet/tests/parens_space_a.out create mode 100644 v_windows/v/old/cmd/tools/vvet/tests/parens_space_a.vv create mode 100644 v_windows/v/old/cmd/tools/vvet/tests/parens_space_b.out create mode 100644 v_windows/v/old/cmd/tools/vvet/tests/parens_space_b.vv create mode 100644 v_windows/v/old/cmd/tools/vvet/tests/trailing_space.out create mode 100644 v_windows/v/old/cmd/tools/vvet/tests/trailing_space.vv create mode 100644 v_windows/v/old/cmd/tools/vvet/vet_test.v create mode 100644 v_windows/v/old/cmd/tools/vvet/vvet.v create mode 100644 v_windows/v/old/cmd/tools/vwatch.exe create mode 100644 v_windows/v/old/cmd/tools/vwatch.v create mode 100644 v_windows/v/old/cmd/tools/vwipe-cache.v create mode 100644 v_windows/v/old/cmd/v/help/ast.txt create mode 100644 v_windows/v/old/cmd/v/help/bin2v.txt create mode 100644 v_windows/v/old/cmd/v/help/bug.txt create mode 100644 v_windows/v/old/cmd/v/help/build-c.txt create mode 100644 v_windows/v/old/cmd/v/help/build-js.txt create mode 100644 v_windows/v/old/cmd/v/help/build-native.txt create mode 100644 v_windows/v/old/cmd/v/help/build.txt create mode 100644 v_windows/v/old/cmd/v/help/check-md.txt create mode 100644 v_windows/v/old/cmd/v/help/default.txt create mode 100644 v_windows/v/old/cmd/v/help/doc.txt create mode 100644 v_windows/v/old/cmd/v/help/doctor.txt create mode 100644 v_windows/v/old/cmd/v/help/fmt.txt create mode 100644 v_windows/v/old/cmd/v/help/help.v create mode 100644 v_windows/v/old/cmd/v/help/help_test.v create mode 100644 v_windows/v/old/cmd/v/help/init.txt create mode 100644 v_windows/v/old/cmd/v/help/install.txt create mode 100644 v_windows/v/old/cmd/v/help/list.txt create mode 100644 v_windows/v/old/cmd/v/help/new.txt create mode 100644 v_windows/v/old/cmd/v/help/other.txt create mode 100644 v_windows/v/old/cmd/v/help/outdated.txt create mode 100644 v_windows/v/old/cmd/v/help/remove.txt create mode 100644 v_windows/v/old/cmd/v/help/repl.txt create mode 100644 v_windows/v/old/cmd/v/help/run.txt create mode 100644 v_windows/v/old/cmd/v/help/search.txt create mode 100644 v_windows/v/old/cmd/v/help/self.txt create mode 100644 v_windows/v/old/cmd/v/help/show.txt create mode 100644 v_windows/v/old/cmd/v/help/symlink.txt create mode 100644 v_windows/v/old/cmd/v/help/test.txt create mode 100644 v_windows/v/old/cmd/v/help/tracev.txt create mode 100644 v_windows/v/old/cmd/v/help/up.txt create mode 100644 v_windows/v/old/cmd/v/help/update.txt create mode 100644 v_windows/v/old/cmd/v/help/upgrade.txt create mode 100644 v_windows/v/old/cmd/v/help/version.txt create mode 100644 v_windows/v/old/cmd/v/help/vet.txt create mode 100644 v_windows/v/old/cmd/v/help/vpm.txt create mode 100644 v_windows/v/old/cmd/v/help/watch.txt create mode 100644 v_windows/v/old/cmd/v/v.v create mode 100644 v_windows/v/old/doc/docs.md create mode 100644 v_windows/v/old/doc/img/vscode-debugger.png create mode 100644 v_windows/v/old/doc/upcoming.md create mode 100644 v_windows/v/old/doc/vscode.md create mode 100644 v_windows/v/old/examples/.gitignore create mode 100644 v_windows/v/old/examples/2048/.gitignore create mode 100644 v_windows/v/old/examples/2048/2048.v create mode 100644 v_windows/v/old/examples/2048/LICENSE create mode 100644 v_windows/v/old/examples/2048/README.md create mode 100644 v_windows/v/old/examples/2048/demo.png create mode 100644 v_windows/v/old/examples/2048/v.mod create mode 100644 v_windows/v/old/examples/asm.v create mode 100644 v_windows/v/old/examples/assets/fonts/LICENSE create mode 100644 v_windows/v/old/examples/assets/fonts/RobotoMono-Regular.ttf create mode 100644 v_windows/v/old/examples/bfs.v create mode 100644 v_windows/v/old/examples/binary_search_tree.v create mode 100644 v_windows/v/old/examples/buf_reader.v create mode 100644 v_windows/v/old/examples/c_interop_wkhtmltopdf.v create mode 100644 v_windows/v/old/examples/cli.v create mode 100644 v_windows/v/old/examples/compiletime/compile-time-for.v create mode 100644 v_windows/v/old/examples/concurrency/concurrency.v create mode 100644 v_windows/v/old/examples/concurrency/concurrency_http.v create mode 100644 v_windows/v/old/examples/concurrency/concurrency_returns.v create mode 100644 v_windows/v/old/examples/database/mysql.v create mode 100644 v_windows/v/old/examples/database/orm.v create mode 100644 v_windows/v/old/examples/database/psql/.gitignore create mode 100644 v_windows/v/old/examples/database/psql/customer.v create mode 100644 v_windows/v/old/examples/database/psql/mydb.sql create mode 100644 v_windows/v/old/examples/database/sqlite.v create mode 100644 v_windows/v/old/examples/dump_factorial.v create mode 100644 v_windows/v/old/examples/dynamic_library_loading/modules/library/library.v create mode 100644 v_windows/v/old/examples/dynamic_library_loading/use.v create mode 100644 v_windows/v/old/examples/dynamic_library_loading/use_test.v create mode 100644 v_windows/v/old/examples/errors.v create mode 100644 v_windows/v/old/examples/eventbus/eventbus.v create mode 100644 v_windows/v/old/examples/eventbus/modules/some_module/some_module.v create mode 100644 v_windows/v/old/examples/fetch.v create mode 100644 v_windows/v/old/examples/fibonacci.v create mode 100644 v_windows/v/old/examples/file_list.v create mode 100644 v_windows/v/old/examples/fireworks/fireworks.v create mode 100644 v_windows/v/old/examples/fireworks/modules/objects/color.v create mode 100644 v_windows/v/old/examples/fireworks/modules/objects/constants.v create mode 100644 v_windows/v/old/examples/fireworks/modules/objects/particle.v create mode 100644 v_windows/v/old/examples/fireworks/modules/objects/rocket.v create mode 100644 v_windows/v/old/examples/fireworks/modules/objects/vector.v create mode 100644 v_windows/v/old/examples/fizz_buzz.v create mode 100644 v_windows/v/old/examples/flappylearning/.gitignore create mode 100644 v_windows/v/old/examples/flappylearning/LICENSE create mode 100644 v_windows/v/old/examples/flappylearning/README.md create mode 100644 v_windows/v/old/examples/flappylearning/assets/img/background.png create mode 100644 v_windows/v/old/examples/flappylearning/assets/img/bird.png create mode 100644 v_windows/v/old/examples/flappylearning/assets/img/flappy.png create mode 100644 v_windows/v/old/examples/flappylearning/assets/img/pipebottom.png create mode 100644 v_windows/v/old/examples/flappylearning/assets/img/pipetop.png create mode 100644 v_windows/v/old/examples/flappylearning/game.v create mode 100644 v_windows/v/old/examples/flappylearning/modules/neuroevolution/neuronevolution.v create mode 100644 v_windows/v/old/examples/function_types.v create mode 100644 v_windows/v/old/examples/game_of_life/README.md create mode 100644 v_windows/v/old/examples/game_of_life/life.v create mode 100644 v_windows/v/old/examples/game_of_life/life_gg.v create mode 100644 v_windows/v/old/examples/game_of_life/modules/automaton/automaton.v create mode 100644 v_windows/v/old/examples/get_weather/README.md create mode 100644 v_windows/v/old/examples/get_weather/get_weather.v create mode 100644 v_windows/v/old/examples/gg/logo.png create mode 100644 v_windows/v/old/examples/gg/polygons.v create mode 100644 v_windows/v/old/examples/gg/random.v create mode 100644 v_windows/v/old/examples/gg/raven_text_rendering.v create mode 100644 v_windows/v/old/examples/gg/rectangles.v create mode 100644 v_windows/v/old/examples/gg/stars.v create mode 100644 v_windows/v/old/examples/gg/worker_thread.v create mode 100644 v_windows/v/old/examples/hanoi.v create mode 100644 v_windows/v/old/examples/hello_v_js.v create mode 100644 v_windows/v/old/examples/hello_world.v create mode 100644 v_windows/v/old/examples/hot_reload/.gitignore create mode 100644 v_windows/v/old/examples/hot_reload/bounce.v create mode 100644 v_windows/v/old/examples/hot_reload/graph.v create mode 100644 v_windows/v/old/examples/hot_reload/message.v create mode 100644 v_windows/v/old/examples/json.v create mode 100644 v_windows/v/old/examples/lander.v create mode 100644 v_windows/v/old/examples/linear_regression/simple_linear_regression.v create mode 100644 v_windows/v/old/examples/links_scraper.v create mode 100644 v_windows/v/old/examples/log.v create mode 100644 v_windows/v/old/examples/mini_calculator.v create mode 100644 v_windows/v/old/examples/native/hello_world.v create mode 100644 v_windows/v/old/examples/nbody.v create mode 100644 v_windows/v/old/examples/net_peer_ip.v create mode 100644 v_windows/v/old/examples/net_raw_http.v create mode 100644 v_windows/v/old/examples/net_resolve.v create mode 100644 v_windows/v/old/examples/net_t.v create mode 100644 v_windows/v/old/examples/net_udp_server_and_client.v create mode 100644 v_windows/v/old/examples/news_fetcher.v create mode 100644 v_windows/v/old/examples/path_tracing.v create mode 100644 v_windows/v/old/examples/pendulum_sim/sim.v create mode 100644 v_windows/v/old/examples/pico/pico.v create mode 100644 v_windows/v/old/examples/process/.ignore create mode 100644 v_windows/v/old/examples/process/command.v create mode 100644 v_windows/v/old/examples/process/execve.v create mode 100644 v_windows/v/old/examples/process/process_script.v create mode 100644 v_windows/v/old/examples/process/process_stdin_trick.v create mode 100644 v_windows/v/old/examples/quick_sort.v create mode 100644 v_windows/v/old/examples/random_ips.v create mode 100644 v_windows/v/old/examples/regex/pcre.vv create mode 100644 v_windows/v/old/examples/regex/readme.md create mode 100644 v_windows/v/old/examples/regex/regex_example.v create mode 100644 v_windows/v/old/examples/regex/regex_with_memoization.v create mode 100644 v_windows/v/old/examples/rune.v create mode 100644 v_windows/v/old/examples/smtp/mail.v create mode 100644 v_windows/v/old/examples/snek/snek.v create mode 100644 v_windows/v/old/examples/sokol/01_cubes/cube.v create mode 100644 v_windows/v/old/examples/sokol/02_cubes_glsl/cube_glsl.glsl create mode 100644 v_windows/v/old/examples/sokol/02_cubes_glsl/cube_glsl.v create mode 100644 v_windows/v/old/examples/sokol/02_cubes_glsl/v.mod create mode 100644 v_windows/v/old/examples/sokol/03_march_tracing_glsl/rt_glsl.glsl create mode 100644 v_windows/v/old/examples/sokol/03_march_tracing_glsl/rt_glsl.v create mode 100644 v_windows/v/old/examples/sokol/03_march_tracing_glsl/v.mod create mode 100644 v_windows/v/old/examples/sokol/04_multi_shader_glsl/rt_glsl.v create mode 100644 v_windows/v/old/examples/sokol/04_multi_shader_glsl/rt_glsl_march.glsl create mode 100644 v_windows/v/old/examples/sokol/04_multi_shader_glsl/rt_glsl_puppy.glsl create mode 100644 v_windows/v/old/examples/sokol/04_multi_shader_glsl/v.mod create mode 100644 v_windows/v/old/examples/sokol/05_instancing_glsl/rt_glsl.v create mode 100644 v_windows/v/old/examples/sokol/05_instancing_glsl/rt_glsl_instancing.glsl create mode 100644 v_windows/v/old/examples/sokol/05_instancing_glsl/v.mod create mode 100644 v_windows/v/old/examples/sokol/06_obj_viewer/assets/models/v.mtl create mode 100644 v_windows/v/old/examples/sokol/06_obj_viewer/assets/models/v.obj_ create mode 100644 v_windows/v/old/examples/sokol/06_obj_viewer/gouraud.glsl create mode 100644 v_windows/v/old/examples/sokol/06_obj_viewer/modules/obj/obj.v create mode 100644 v_windows/v/old/examples/sokol/06_obj_viewer/modules/obj/rend.v create mode 100644 v_windows/v/old/examples/sokol/06_obj_viewer/modules/obj/struct.v create mode 100644 v_windows/v/old/examples/sokol/06_obj_viewer/modules/obj/util.v create mode 100644 v_windows/v/old/examples/sokol/06_obj_viewer/show_obj.v create mode 100644 v_windows/v/old/examples/sokol/06_obj_viewer/v.mod create mode 100644 v_windows/v/old/examples/sokol/drawing.v create mode 100644 v_windows/v/old/examples/sokol/fonts.v create mode 100644 v_windows/v/old/examples/sokol/freetype_raven.v create mode 100644 v_windows/v/old/examples/sokol/particles/modules/particle/LICENSE create mode 100644 v_windows/v/old/examples/sokol/particles/modules/particle/color.v create mode 100644 v_windows/v/old/examples/sokol/particles/modules/particle/particle.v create mode 100644 v_windows/v/old/examples/sokol/particles/modules/particle/system.v create mode 100644 v_windows/v/old/examples/sokol/particles/modules/particle/v.mod create mode 100644 v_windows/v/old/examples/sokol/particles/modules/particle/vec2/v.mod create mode 100644 v_windows/v/old/examples/sokol/particles/modules/particle/vec2/vec2.v create mode 100644 v_windows/v/old/examples/sokol/particles/particles.v create mode 100644 v_windows/v/old/examples/sokol/sounds/melody.v create mode 100644 v_windows/v/old/examples/sokol/sounds/simple_sin_tones.v create mode 100644 v_windows/v/old/examples/sokol/sounds/uhoh.wav create mode 100644 v_windows/v/old/examples/sokol/sounds/wav_player.v create mode 100644 v_windows/v/old/examples/spectral.v create mode 100644 v_windows/v/old/examples/submodule/main.v create mode 100644 v_windows/v/old/examples/submodule/mymodules/main_functions.v create mode 100644 v_windows/v/old/examples/submodule/mymodules/submodule/sub_functions.v create mode 100644 v_windows/v/old/examples/tcp_echo_server.v create mode 100644 v_windows/v/old/examples/tcp_notify_echo_server.v create mode 100644 v_windows/v/old/examples/templates/data.json create mode 100644 v_windows/v/old/examples/templates/readme.md create mode 100644 v_windows/v/old/examples/templates/template.md create mode 100644 v_windows/v/old/examples/templates/templates.v create mode 100644 v_windows/v/old/examples/term.ui/README.md create mode 100644 v_windows/v/old/examples/term.ui/cursor_chaser.v create mode 100644 v_windows/v/old/examples/term.ui/event_viewer.v create mode 100644 v_windows/v/old/examples/term.ui/pong.v create mode 100644 v_windows/v/old/examples/term.ui/rectangles.v create mode 100644 v_windows/v/old/examples/term.ui/screenshot_pong.png create mode 100644 v_windows/v/old/examples/term.ui/term_drawing.v create mode 100644 v_windows/v/old/examples/term.ui/text_editor.v create mode 100644 v_windows/v/old/examples/term.ui/vyper.v create mode 100644 v_windows/v/old/examples/terminal_control.v create mode 100644 v_windows/v/old/examples/tetris/README.md create mode 100644 v_windows/v/old/examples/tetris/screenshot.png create mode 100644 v_windows/v/old/examples/tetris/tetris.v create mode 100644 v_windows/v/old/examples/tree_of_nodes.v create mode 100644 v_windows/v/old/examples/ttf_font/Graduate-Regular.ttf create mode 100644 v_windows/v/old/examples/ttf_font/Imprima-Regular.ttf create mode 100644 v_windows/v/old/examples/ttf_font/OFL.txt create mode 100644 v_windows/v/old/examples/ttf_font/example_ttf.v create mode 100644 v_windows/v/old/examples/v_script.vsh create mode 100644 v_windows/v/old/examples/vcasino/README.md create mode 100644 v_windows/v/old/examples/vcasino/vcasino.v create mode 100644 v_windows/v/old/examples/vmod.v create mode 100644 v_windows/v/old/examples/vpwgen.v create mode 100644 v_windows/v/old/examples/vweb/file_upload/index.html create mode 100644 v_windows/v/old/examples/vweb/file_upload/upload.html create mode 100644 v_windows/v/old/examples/vweb/file_upload/vweb_example.v create mode 100644 v_windows/v/old/examples/vweb/index.html create mode 100644 v_windows/v/old/examples/vweb/server_sent_events/assets/site.css create mode 100644 v_windows/v/old/examples/vweb/server_sent_events/assets/v-logo.svg create mode 100644 v_windows/v/old/examples/vweb/server_sent_events/favicon.ico create mode 100644 v_windows/v/old/examples/vweb/server_sent_events/index.html create mode 100644 v_windows/v/old/examples/vweb/server_sent_events/server.v create mode 100644 v_windows/v/old/examples/vweb/vweb_assets/assets/index.css create mode 100644 v_windows/v/old/examples/vweb/vweb_assets/assets/v-logo.svg create mode 100644 v_windows/v/old/examples/vweb/vweb_assets/favicon.ico create mode 100644 v_windows/v/old/examples/vweb/vweb_assets/index.html create mode 100644 v_windows/v/old/examples/vweb/vweb_assets/vweb_assets.v create mode 100644 v_windows/v/old/examples/vweb/vweb_example.v create mode 100644 v_windows/v/old/examples/web_crawler/README.md create mode 100644 v_windows/v/old/examples/web_crawler/web_crawler.v create mode 100644 v_windows/v/old/examples/websocket/client-server/client.v create mode 100644 v_windows/v/old/examples/websocket/client-server/server.v create mode 100644 v_windows/v/old/examples/websocket/ping.v create mode 100644 v_windows/v/old/examples/word_counter/README.md create mode 100644 v_windows/v/old/examples/word_counter/cinderella.txt create mode 100644 v_windows/v/old/examples/word_counter/word_counter.v create mode 100644 v_windows/v/old/thirdparty/.gitignore create mode 100644 v_windows/v/old/thirdparty/bignum/README.md create mode 100644 v_windows/v/old/thirdparty/bignum/bn.c create mode 100644 v_windows/v/old/thirdparty/bignum/bn.h create mode 100644 v_windows/v/old/thirdparty/cJSON/cJSON.c create mode 100644 v_windows/v/old/thirdparty/cJSON/cJSON.h create mode 100644 v_windows/v/old/thirdparty/cJSON/readme.txt create mode 100644 v_windows/v/old/thirdparty/fontstash/fontstash.h create mode 100644 v_windows/v/old/thirdparty/fontstash/stb_truetype.h create mode 100644 v_windows/v/old/thirdparty/ios/ios.m create mode 100644 v_windows/v/old/thirdparty/libgc/amalgamation.txt create mode 100644 v_windows/v/old/thirdparty/libgc/gc.c create mode 100644 v_windows/v/old/thirdparty/libgc/gc.h create mode 100644 v_windows/v/old/thirdparty/mssql/include/.gitignore create mode 100644 v_windows/v/old/thirdparty/mssql/include/mssql.h create mode 100644 v_windows/v/old/thirdparty/picoev/picoev.c create mode 100644 v_windows/v/old/thirdparty/picoev/src/README.md create mode 100644 v_windows/v/old/thirdparty/picoev/src/picoev.h create mode 100644 v_windows/v/old/thirdparty/picoev/src/picoev_epoll.c create mode 100644 v_windows/v/old/thirdparty/picoev/src/picoev_kqueue.c create mode 100644 v_windows/v/old/thirdparty/picoev/src/picoev_select.c create mode 100644 v_windows/v/old/thirdparty/picoev/src/picoev_w32.h create mode 100644 v_windows/v/old/thirdparty/picohttpparser/picohttpparser.c create mode 100644 v_windows/v/old/thirdparty/picohttpparser/picohttpparser.h create mode 100644 v_windows/v/old/thirdparty/picohttpparser/src/README.md create mode 100644 v_windows/v/old/thirdparty/picohttpparser/src/picohttpparser.c create mode 100644 v_windows/v/old/thirdparty/picohttpparser/src/picohttpparser.h create mode 100644 v_windows/v/old/thirdparty/sokol/sokol_app.h create mode 100644 v_windows/v/old/thirdparty/sokol/sokol_app2.h create mode 100644 v_windows/v/old/thirdparty/sokol/sokol_audio.h create mode 100644 v_windows/v/old/thirdparty/sokol/sokol_gfx.h create mode 100644 v_windows/v/old/thirdparty/sokol/sokol_v.h create mode 100644 v_windows/v/old/thirdparty/sokol/util/sokol_fontstash.h create mode 100644 v_windows/v/old/thirdparty/sokol/util/sokol_gl.h create mode 100644 v_windows/v/old/thirdparty/stb_image/stb_image.h create mode 100644 v_windows/v/old/thirdparty/stb_image/stbi.c create mode 100644 v_windows/v/old/thirdparty/stdatomic/nix/atomic.h create mode 100644 v_windows/v/old/thirdparty/stdatomic/nix/atomic_cpp.h create mode 100644 v_windows/v/old/thirdparty/stdatomic/win/atomic.h create mode 100644 v_windows/v/old/thirdparty/vschannel/vschannel.c create mode 100644 v_windows/v/old/thirdparty/vschannel/vschannel.h create mode 100644 v_windows/v/old/thirdparty/zip/miniz.h create mode 100644 v_windows/v/old/thirdparty/zip/zip.c create mode 100644 v_windows/v/old/thirdparty/zip/zip.h create mode 100644 v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/README.md create mode 100644 v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/code/blog/.gitignore create mode 100644 v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/code/blog/article.v create mode 100644 v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/code/blog/blog.sqlite create mode 100644 v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/code/blog/blog.v create mode 100644 v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/code/blog/index.html create mode 100644 v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/code/blog/new.html create mode 100644 v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/img/articles1.png create mode 100644 v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/img/articles_json.png create mode 100644 v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/img/hello.png create mode 100644 v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/img/hello_html.png create mode 100644 v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/img/time.png create mode 100644 v_windows/v/old/v.def create mode 100644 v_windows/v/old/v.exe create mode 100644 v_windows/v/old/v.mod create mode 100644 v_windows/v/old/v_old.exe create mode 160000 v_windows/v/old/vc create mode 100644 v_windows/v/old/vlib/.vdocignore create mode 100644 v_windows/v/old/vlib/arrays/arrays.v create mode 100644 v_windows/v/old/vlib/arrays/arrays_test.v create mode 100644 v_windows/v/old/vlib/benchmark/README.md create mode 100644 v_windows/v/old/vlib/benchmark/benchmark.v create mode 100644 v_windows/v/old/vlib/bitfield/README.md create mode 100644 v_windows/v/old/vlib/bitfield/bitfield.v create mode 100644 v_windows/v/old/vlib/bitfield/bitfield_test.v create mode 100644 v_windows/v/old/vlib/builtin/array.v create mode 100644 v_windows/v/old/vlib/builtin/array_d_gcboehm_opt.v create mode 100644 v_windows/v/old/vlib/builtin/array_notd_gcboehm_opt.v create mode 100644 v_windows/v/old/vlib/builtin/array_test.v create mode 100644 v_windows/v/old/vlib/builtin/builtin.c.v create mode 100644 v_windows/v/old/vlib/builtin/builtin.v create mode 100644 v_windows/v/old/vlib/builtin/builtin_d_gcboehm.v create mode 100644 v_windows/v/old/vlib/builtin/builtin_ios.c.v create mode 100644 v_windows/v/old/vlib/builtin/builtin_nix.c.v create mode 100644 v_windows/v/old/vlib/builtin/builtin_notd_gcboehm.v create mode 100644 v_windows/v/old/vlib/builtin/builtin_windows.c.v create mode 100644 v_windows/v/old/vlib/builtin/byte_test.v create mode 100644 v_windows/v/old/vlib/builtin/cfns.c.v create mode 100644 v_windows/v/old/vlib/builtin/chan.v create mode 100644 v_windows/v/old/vlib/builtin/float.v create mode 100644 v_windows/v/old/vlib/builtin/float_test.v create mode 100644 v_windows/v/old/vlib/builtin/float_x64.v create mode 100644 v_windows/v/old/vlib/builtin/int.v create mode 100644 v_windows/v/old/vlib/builtin/int_test.v create mode 100644 v_windows/v/old/vlib/builtin/isnil_test.v create mode 100644 v_windows/v/old/vlib/builtin/js/array.js.v create mode 100644 v_windows/v/old/vlib/builtin/js/builtin.js.v create mode 100644 v_windows/v/old/vlib/builtin/js/builtin.v create mode 100644 v_windows/v/old/vlib/builtin/js/byte.js.v create mode 100644 v_windows/v/old/vlib/builtin/js/int.js.v create mode 100644 v_windows/v/old/vlib/builtin/js/jsfns.js.v create mode 100644 v_windows/v/old/vlib/builtin/js/jsfns_browser.js.v create mode 100644 v_windows/v/old/vlib/builtin/js/jsfns_node.js.v create mode 100644 v_windows/v/old/vlib/builtin/js/map.js.v create mode 100644 v_windows/v/old/vlib/builtin/js/string.js.v create mode 100644 v_windows/v/old/vlib/builtin/linux_bare/libc_impl.v create mode 100644 v_windows/v/old/vlib/builtin/linux_bare/linux_syscalls.v create mode 100644 v_windows/v/old/vlib/builtin/linux_bare/memory_managment.v create mode 100644 v_windows/v/old/vlib/builtin/linux_bare/old/.checks/.gitignore create mode 100644 v_windows/v/old/vlib/builtin/linux_bare/old/.checks/checks.v create mode 100644 v_windows/v/old/vlib/builtin/linux_bare/old/.checks/consts/consts.v create mode 100644 v_windows/v/old/vlib/builtin/linux_bare/old/.checks/forkedtest/forkedtest.v create mode 100644 v_windows/v/old/vlib/builtin/linux_bare/old/.checks/linuxsys/linuxsys.v create mode 100644 v_windows/v/old/vlib/builtin/linux_bare/old/.checks/readme.md create mode 100644 v_windows/v/old/vlib/builtin/linux_bare/old/.checks/sample_text1.txt create mode 100644 v_windows/v/old/vlib/builtin/linux_bare/old/.checks/string/string.v create mode 100644 v_windows/v/old/vlib/builtin/linux_bare/old/.checks/structs/structs.v create mode 100644 v_windows/v/old/vlib/builtin/linux_bare/old/array_bare.v create mode 100644 v_windows/v/old/vlib/builtin/linux_bare/old/builtin_bare.v create mode 100644 v_windows/v/old/vlib/builtin/linux_bare/old/linuxsys_bare.v create mode 100644 v_windows/v/old/vlib/builtin/linux_bare/old/mm_bare.v create mode 100644 v_windows/v/old/vlib/builtin/linux_bare/old/string_bare.v create mode 100644 v_windows/v/old/vlib/builtin/linux_bare/old/syscallwrapper_test.v create mode 100644 v_windows/v/old/vlib/builtin/map.v create mode 100644 v_windows/v/old/vlib/builtin/map_d_gcboehm_opt.v create mode 100644 v_windows/v/old/vlib/builtin/map_of_floats_test.v create mode 100644 v_windows/v/old/vlib/builtin/map_test.v create mode 100644 v_windows/v/old/vlib/builtin/option.v create mode 100644 v_windows/v/old/vlib/builtin/prealloc.c.v create mode 100644 v_windows/v/old/vlib/builtin/rune.v create mode 100644 v_windows/v/old/vlib/builtin/sorted_map.v create mode 100644 v_windows/v/old/vlib/builtin/sorting_test.v create mode 100644 v_windows/v/old/vlib/builtin/string.v create mode 100644 v_windows/v/old/vlib/builtin/string_charptr_byteptr_helpers.v create mode 100644 v_windows/v/old/vlib/builtin/string_int_test.v create mode 100644 v_windows/v/old/vlib/builtin/string_interpolation.v create mode 100644 v_windows/v/old/vlib/builtin/string_strip_margin_test.v create mode 100644 v_windows/v/old/vlib/builtin/string_test.v create mode 100644 v_windows/v/old/vlib/builtin/utf8.c.v create mode 100644 v_windows/v/old/vlib/builtin/utf8.v create mode 100644 v_windows/v/old/vlib/builtin/utf8_test.v create mode 100644 v_windows/v/old/vlib/cli/README.md create mode 100644 v_windows/v/old/vlib/cli/command.v create mode 100644 v_windows/v/old/vlib/cli/command_test.v create mode 100644 v_windows/v/old/vlib/cli/flag.v create mode 100644 v_windows/v/old/vlib/cli/flag_test.v create mode 100644 v_windows/v/old/vlib/cli/help.v create mode 100644 v_windows/v/old/vlib/cli/version.v create mode 100644 v_windows/v/old/vlib/clipboard/clipboard.v create mode 100644 v_windows/v/old/vlib/clipboard/clipboard_android.c.v create mode 100644 v_windows/v/old/vlib/clipboard/clipboard_darwin.c.v create mode 100644 v_windows/v/old/vlib/clipboard/clipboard_darwin.m create mode 100644 v_windows/v/old/vlib/clipboard/clipboard_default.c.v create mode 100644 v_windows/v/old/vlib/clipboard/clipboard_solaris.c.v create mode 100644 v_windows/v/old/vlib/clipboard/clipboard_test.v create mode 100644 v_windows/v/old/vlib/clipboard/clipboard_windows.c.v create mode 100644 v_windows/v/old/vlib/clipboard/dummy/dummy_clipboard.v create mode 100644 v_windows/v/old/vlib/clipboard/x11/clipboard.c.v create mode 100644 v_windows/v/old/vlib/context/README.md create mode 100644 v_windows/v/old/vlib/context/_context.v create mode 100644 v_windows/v/old/vlib/context/cancel.v create mode 100644 v_windows/v/old/vlib/context/cancel_test.v create mode 100644 v_windows/v/old/vlib/context/deadline.v create mode 100644 v_windows/v/old/vlib/context/deadline_test.v create mode 100644 v_windows/v/old/vlib/context/empty.v create mode 100644 v_windows/v/old/vlib/context/empty_test.v create mode 100644 v_windows/v/old/vlib/context/err.v create mode 100644 v_windows/v/old/vlib/context/value.v create mode 100644 v_windows/v/old/vlib/context/value_test.v create mode 100644 v_windows/v/old/vlib/crypto/aes/aes.v create mode 100644 v_windows/v/old/vlib/crypto/aes/aes_cbc.v create mode 100644 v_windows/v/old/vlib/crypto/aes/aes_test.v create mode 100644 v_windows/v/old/vlib/crypto/aes/block_generic.v create mode 100644 v_windows/v/old/vlib/crypto/aes/const.v create mode 100644 v_windows/v/old/vlib/crypto/aes/cypher_generic.v create mode 100644 v_windows/v/old/vlib/crypto/cipher/xor_generic.v create mode 100644 v_windows/v/old/vlib/crypto/crypto.v create mode 100644 v_windows/v/old/vlib/crypto/hmac/hmac.v create mode 100644 v_windows/v/old/vlib/crypto/hmac/hmac_test.v create mode 100644 v_windows/v/old/vlib/crypto/internal/subtle/aliasing.v create mode 100644 v_windows/v/old/vlib/crypto/internal/subtle/comparison.v create mode 100644 v_windows/v/old/vlib/crypto/internal/subtle/comparison_test.v create mode 100644 v_windows/v/old/vlib/crypto/md5/md5.v create mode 100644 v_windows/v/old/vlib/crypto/md5/md5_test.v create mode 100644 v_windows/v/old/vlib/crypto/md5/md5block_generic.v create mode 100644 v_windows/v/old/vlib/crypto/rand/crypto_rand_read_test.v create mode 100644 v_windows/v/old/vlib/crypto/rand/rand.v create mode 100644 v_windows/v/old/vlib/crypto/rand/rand_darwin.c.v create mode 100644 v_windows/v/old/vlib/crypto/rand/rand_default.c.v create mode 100644 v_windows/v/old/vlib/crypto/rand/rand_linux.c.v create mode 100644 v_windows/v/old/vlib/crypto/rand/rand_solaris.c.v create mode 100644 v_windows/v/old/vlib/crypto/rand/rand_windows.c.v create mode 100644 v_windows/v/old/vlib/crypto/rand/utils.v create mode 100644 v_windows/v/old/vlib/crypto/rc4/rc4.v create mode 100644 v_windows/v/old/vlib/crypto/rc4/rc4_test.v create mode 100644 v_windows/v/old/vlib/crypto/readme.txt create mode 100644 v_windows/v/old/vlib/crypto/sha1/sha1.v create mode 100644 v_windows/v/old/vlib/crypto/sha1/sha1_test.v create mode 100644 v_windows/v/old/vlib/crypto/sha1/sha1block_generic.v create mode 100644 v_windows/v/old/vlib/crypto/sha256/sha256.v create mode 100644 v_windows/v/old/vlib/crypto/sha256/sha256_test.v create mode 100644 v_windows/v/old/vlib/crypto/sha256/sha256block_generic.v create mode 100644 v_windows/v/old/vlib/crypto/sha512/sha512.v create mode 100644 v_windows/v/old/vlib/crypto/sha512/sha512_test.v create mode 100644 v_windows/v/old/vlib/crypto/sha512/sha512block_generic.v create mode 100644 v_windows/v/old/vlib/darwin/darwin.m create mode 100644 v_windows/v/old/vlib/darwin/darwin.v create mode 100644 v_windows/v/old/vlib/dl/dl.v create mode 100644 v_windows/v/old/vlib/dl/dl_nix.c.v create mode 100644 v_windows/v/old/vlib/dl/dl_test.v create mode 100644 v_windows/v/old/vlib/dl/dl_windows.c.v create mode 100644 v_windows/v/old/vlib/encoding/base64/base64.v create mode 100644 v_windows/v/old/vlib/encoding/base64/base64_memory_test.v create mode 100644 v_windows/v/old/vlib/encoding/base64/base64_test.v create mode 100644 v_windows/v/old/vlib/encoding/binary/binary.v create mode 100644 v_windows/v/old/vlib/encoding/csv/README.md create mode 100644 v_windows/v/old/vlib/encoding/csv/reader.v create mode 100644 v_windows/v/old/vlib/encoding/csv/reader_test.v create mode 100644 v_windows/v/old/vlib/encoding/csv/writer.v create mode 100644 v_windows/v/old/vlib/encoding/csv/writer_test.v create mode 100644 v_windows/v/old/vlib/encoding/utf8/east_asian/east_asian_width.v create mode 100644 v_windows/v/old/vlib/encoding/utf8/east_asian/east_asian_width_test.v create mode 100644 v_windows/v/old/vlib/encoding/utf8/encoding_utf8_test.v create mode 100644 v_windows/v/old/vlib/encoding/utf8/utf8.v create mode 100644 v_windows/v/old/vlib/encoding/utf8/utf8_util.v create mode 100644 v_windows/v/old/vlib/encoding/utf8/utf8_util_test.v create mode 100644 v_windows/v/old/vlib/eventbus/README.md create mode 100644 v_windows/v/old/vlib/eventbus/eventbus.v create mode 100644 v_windows/v/old/vlib/eventbus/eventbus_test.v create mode 100644 v_windows/v/old/vlib/flag/README.md create mode 100644 v_windows/v/old/vlib/flag/default_flag_options_test.v create mode 100644 v_windows/v/old/vlib/flag/flag.v create mode 100644 v_windows/v/old/vlib/flag/flag_test.v create mode 100644 v_windows/v/old/vlib/flag/testdata/simplest_flag_program.dashdash.help.out create mode 100644 v_windows/v/old/vlib/flag/testdata/simplest_flag_program.dashdash.version.out create mode 100644 v_windows/v/old/vlib/flag/testdata/simplest_flag_program.help.out create mode 100644 v_windows/v/old/vlib/flag/testdata/simplest_flag_program.out create mode 100644 v_windows/v/old/vlib/flag/testdata/simplest_flag_program.v create mode 100644 v_windows/v/old/vlib/flag/testdata/simplest_flag_program.version.out create mode 100644 v_windows/v/old/vlib/flag/testdata/usage_example.help.out create mode 100644 v_windows/v/old/vlib/flag/testdata/usage_example.out create mode 100644 v_windows/v/old/vlib/flag/testdata/usage_example.v create mode 100644 v_windows/v/old/vlib/flag/testdata/usage_example.version.out create mode 100644 v_windows/v/old/vlib/flag/usage_example_test.v create mode 100644 v_windows/v/old/vlib/fontstash/a_d_use_freetype.v create mode 100644 v_windows/v/old/vlib/fontstash/fontstash.v create mode 100644 v_windows/v/old/vlib/fontstash/fontstash_funcs.v create mode 100644 v_windows/v/old/vlib/fontstash/fontstash_structs.v create mode 100644 v_windows/v/old/vlib/gg/enums.v create mode 100644 v_windows/v/old/vlib/gg/gg.v create mode 100644 v_windows/v/old/vlib/gg/gg_android.c.v create mode 100644 v_windows/v/old/vlib/gg/gg_darwin.c.v create mode 100644 v_windows/v/old/vlib/gg/gg_darwin.m create mode 100644 v_windows/v/old/vlib/gg/image.v create mode 100644 v_windows/v/old/vlib/gg/m4/graphic.v create mode 100644 v_windows/v/old/vlib/gg/m4/m4_test.v create mode 100644 v_windows/v/old/vlib/gg/m4/matrix.v create mode 100644 v_windows/v/old/vlib/gg/m4/vector.v create mode 100644 v_windows/v/old/vlib/gg/text_rendering.v create mode 100644 v_windows/v/old/vlib/glm/glm.v create mode 100644 v_windows/v/old/vlib/glm/glm_test.v create mode 100644 v_windows/v/old/vlib/gx/color.v create mode 100644 v_windows/v/old/vlib/gx/color_test.v create mode 100644 v_windows/v/old/vlib/gx/image.v create mode 100644 v_windows/v/old/vlib/gx/text.v create mode 100644 v_windows/v/old/vlib/hash/crc32/crc32.v create mode 100644 v_windows/v/old/vlib/hash/crc32/crc32_test.v create mode 100644 v_windows/v/old/vlib/hash/fnv1a/fnv1a.v create mode 100644 v_windows/v/old/vlib/hash/fnv1a/fnv1a_test.v create mode 100644 v_windows/v/old/vlib/hash/hash.v create mode 100644 v_windows/v/old/vlib/hash/wyhash.c.v create mode 100644 v_windows/v/old/vlib/hash/wyhash.v create mode 100644 v_windows/v/old/vlib/io/buffered_reader.v create mode 100644 v_windows/v/old/vlib/io/custom_string_reading_test.v create mode 100644 v_windows/v/old/vlib/io/io.v create mode 100644 v_windows/v/old/vlib/io/io_cp_test.v create mode 100644 v_windows/v/old/vlib/io/io_test.v create mode 100644 v_windows/v/old/vlib/io/multi_writer.v create mode 100644 v_windows/v/old/vlib/io/multi_writer_test.v create mode 100644 v_windows/v/old/vlib/io/os_file_reader_test.v create mode 100644 v_windows/v/old/vlib/io/reader.v create mode 100644 v_windows/v/old/vlib/io/reader_test.v create mode 100644 v_windows/v/old/vlib/io/readerwriter.v create mode 100644 v_windows/v/old/vlib/io/util/util.v create mode 100644 v_windows/v/old/vlib/io/util/util_test.v create mode 100644 v_windows/v/old/vlib/io/writer.v create mode 100644 v_windows/v/old/vlib/json/json_decode_test.v create mode 100644 v_windows/v/old/vlib/json/json_primitives.v create mode 100644 v_windows/v/old/vlib/json/json_test.v create mode 100644 v_windows/v/old/vlib/log/log.v create mode 100644 v_windows/v/old/vlib/math/big/big.v create mode 100644 v_windows/v/old/vlib/math/big/big_test.v create mode 100644 v_windows/v/old/vlib/math/bits.v create mode 100644 v_windows/v/old/vlib/math/bits/bits.v create mode 100644 v_windows/v/old/vlib/math/bits/bits_tables.v create mode 100644 v_windows/v/old/vlib/math/bits/bits_test.v create mode 100644 v_windows/v/old/vlib/math/complex/complex.v create mode 100644 v_windows/v/old/vlib/math/complex/complex_test.v create mode 100644 v_windows/v/old/vlib/math/const.v create mode 100644 v_windows/v/old/vlib/math/factorial/factorial.v create mode 100644 v_windows/v/old/vlib/math/factorial/factorial_tables.v create mode 100644 v_windows/v/old/vlib/math/factorial/factorial_test.v create mode 100644 v_windows/v/old/vlib/math/fractions/approximations.v create mode 100644 v_windows/v/old/vlib/math/fractions/approximations_test.v create mode 100644 v_windows/v/old/vlib/math/fractions/fraction.v create mode 100644 v_windows/v/old/vlib/math/fractions/fraction_test.v create mode 100644 v_windows/v/old/vlib/math/math.c.v create mode 100644 v_windows/v/old/vlib/math/math.js.v create mode 100644 v_windows/v/old/vlib/math/math.v create mode 100644 v_windows/v/old/vlib/math/math_test.v create mode 100644 v_windows/v/old/vlib/math/mathutil/mathutil.v create mode 100644 v_windows/v/old/vlib/math/mathutil/mathutil_test.v create mode 100644 v_windows/v/old/vlib/math/stats/stats.v create mode 100644 v_windows/v/old/vlib/math/stats/stats_test.v create mode 100644 v_windows/v/old/vlib/math/unsafe.v create mode 100644 v_windows/v/old/vlib/math/util/util.v create mode 100644 v_windows/v/old/vlib/mssql/README.md create mode 100644 v_windows/v/old/vlib/mssql/_cdef_nix.c.v create mode 100644 v_windows/v/old/vlib/mssql/_cdef_windows.c.v create mode 100644 v_windows/v/old/vlib/mssql/_cdefs.c.v create mode 100644 v_windows/v/old/vlib/mssql/config.v create mode 100644 v_windows/v/old/vlib/mssql/mssql.v create mode 100644 v_windows/v/old/vlib/mssql/result.v create mode 100644 v_windows/v/old/vlib/mssql/stmt_handle.v create mode 100644 v_windows/v/old/vlib/mysql/README.md create mode 100644 v_windows/v/old/vlib/mysql/_cdefs.c.v create mode 100644 v_windows/v/old/vlib/mysql/_cdefs_nix.c.v create mode 100644 v_windows/v/old/vlib/mysql/_cdefs_windows.c.v create mode 100644 v_windows/v/old/vlib/mysql/consts.v create mode 100644 v_windows/v/old/vlib/mysql/enums.v create mode 100644 v_windows/v/old/vlib/mysql/mysql.v create mode 100644 v_windows/v/old/vlib/mysql/mysql_orm_test.v create mode 100644 v_windows/v/old/vlib/mysql/orm.v create mode 100644 v_windows/v/old/vlib/mysql/result.v create mode 100644 v_windows/v/old/vlib/mysql/stmt.c.v create mode 100644 v_windows/v/old/vlib/mysql/utils.v create mode 100644 v_windows/v/old/vlib/net/aasocket.c.v create mode 100644 v_windows/v/old/vlib/net/address.v create mode 100644 v_windows/v/old/vlib/net/address_darwin.c.v create mode 100644 v_windows/v/old/vlib/net/address_default.c.v create mode 100644 v_windows/v/old/vlib/net/address_freebsd.c.v create mode 100644 v_windows/v/old/vlib/net/address_linux.c.v create mode 100644 v_windows/v/old/vlib/net/address_test.v create mode 100644 v_windows/v/old/vlib/net/address_windows.c.v create mode 100644 v_windows/v/old/vlib/net/afunix.h create mode 100644 v_windows/v/old/vlib/net/common.v create mode 100644 v_windows/v/old/vlib/net/conv/conv.c.v create mode 100644 v_windows/v/old/vlib/net/conv/conv_default.c.v create mode 100644 v_windows/v/old/vlib/net/conv/conv_windows.c.v create mode 100644 v_windows/v/old/vlib/net/errors.v create mode 100644 v_windows/v/old/vlib/net/ftp/ftp.v create mode 100644 v_windows/v/old/vlib/net/ftp/ftp_test.v create mode 100644 v_windows/v/old/vlib/net/html/README.md create mode 100644 v_windows/v/old/vlib/net/html/data_structures.v create mode 100644 v_windows/v/old/vlib/net/html/dom.v create mode 100644 v_windows/v/old/vlib/net/html/dom_test.v create mode 100644 v_windows/v/old/vlib/net/html/html.v create mode 100644 v_windows/v/old/vlib/net/html/html_test.v create mode 100644 v_windows/v/old/vlib/net/html/parser.v create mode 100644 v_windows/v/old/vlib/net/html/parser_test.v create mode 100644 v_windows/v/old/vlib/net/html/tag.v create mode 100644 v_windows/v/old/vlib/net/http/backend_nix.c.v create mode 100644 v_windows/v/old/vlib/net/http/backend_windows.c.v create mode 100644 v_windows/v/old/vlib/net/http/chunked/dechunk.v create mode 100644 v_windows/v/old/vlib/net/http/cookie.v create mode 100644 v_windows/v/old/vlib/net/http/cookie_test.v create mode 100644 v_windows/v/old/vlib/net/http/download.v create mode 100644 v_windows/v/old/vlib/net/http/download_nix.c.v create mode 100644 v_windows/v/old/vlib/net/http/download_windows.c.v create mode 100644 v_windows/v/old/vlib/net/http/header.v create mode 100644 v_windows/v/old/vlib/net/http/header_test.v create mode 100644 v_windows/v/old/vlib/net/http/http.v create mode 100644 v_windows/v/old/vlib/net/http/http_httpbin_test.v create mode 100644 v_windows/v/old/vlib/net/http/http_test.v create mode 100644 v_windows/v/old/vlib/net/http/method.v create mode 100644 v_windows/v/old/vlib/net/http/request.v create mode 100644 v_windows/v/old/vlib/net/http/request_test.v create mode 100644 v_windows/v/old/vlib/net/http/response.v create mode 100644 v_windows/v/old/vlib/net/http/server.v create mode 100644 v_windows/v/old/vlib/net/http/status.v create mode 100644 v_windows/v/old/vlib/net/http/status_test.v create mode 100644 v_windows/v/old/vlib/net/http/version.v create mode 100644 v_windows/v/old/vlib/net/ipv6_v6only.h create mode 100644 v_windows/v/old/vlib/net/net_nix.c.v create mode 100644 v_windows/v/old/vlib/net/net_windows.c.v create mode 100644 v_windows/v/old/vlib/net/openssl/c.v create mode 100644 v_windows/v/old/vlib/net/openssl/openssl.v create mode 100644 v_windows/v/old/vlib/net/openssl/ssl_connection.v create mode 100644 v_windows/v/old/vlib/net/smtp/smtp.v create mode 100644 v_windows/v/old/vlib/net/smtp/smtp_test.v create mode 100644 v_windows/v/old/vlib/net/socket_options.c.v create mode 100644 v_windows/v/old/vlib/net/tcp.v create mode 100644 v_windows/v/old/vlib/net/tcp_read_line.v create mode 100644 v_windows/v/old/vlib/net/tcp_simple_client_server_test.v create mode 100644 v_windows/v/old/vlib/net/tcp_test.v create mode 100644 v_windows/v/old/vlib/net/udp.v create mode 100644 v_windows/v/old/vlib/net/udp_test.v create mode 100644 v_windows/v/old/vlib/net/unix/aasocket.c.v create mode 100644 v_windows/v/old/vlib/net/unix/common.v create mode 100644 v_windows/v/old/vlib/net/unix/stream_nix.v create mode 100644 v_windows/v/old/vlib/net/unix/unix_test.v create mode 100644 v_windows/v/old/vlib/net/urllib/urllib.v create mode 100644 v_windows/v/old/vlib/net/urllib/urllib_test.v create mode 100644 v_windows/v/old/vlib/net/urllib/values.v create mode 100644 v_windows/v/old/vlib/net/util.v create mode 100644 v_windows/v/old/vlib/net/websocket/events.v create mode 100644 v_windows/v/old/vlib/net/websocket/handshake.v create mode 100644 v_windows/v/old/vlib/net/websocket/io.v create mode 100644 v_windows/v/old/vlib/net/websocket/message.v create mode 100644 v_windows/v/old/vlib/net/websocket/tests/autobahn/README.md create mode 100644 v_windows/v/old/vlib/net/websocket/tests/autobahn/autobahn_client.v create mode 100644 v_windows/v/old/vlib/net/websocket/tests/autobahn/autobahn_client_wss.v create mode 100644 v_windows/v/old/vlib/net/websocket/tests/autobahn/autobahn_server.v create mode 100644 v_windows/v/old/vlib/net/websocket/tests/autobahn/docker-compose.yml create mode 100644 v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server/Dockerfile create mode 100644 v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server/check_results.py create mode 100644 v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server/config/fuzzingclient.json create mode 100644 v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server/config/fuzzingserver.json create mode 100644 v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server_wss/Dockerfile create mode 100644 v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server_wss/check_results.py create mode 100644 v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server_wss/config/fuzzingserver.json create mode 100644 v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server_wss/config/server.crt create mode 100644 v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server_wss/config/server.csr create mode 100644 v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server_wss/config/server.key create mode 100644 v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server_wss/config/server.pem create mode 100644 v_windows/v/old/vlib/net/websocket/tests/autobahn/local_run/Dockerfile create mode 100644 v_windows/v/old/vlib/net/websocket/tests/autobahn/local_run/autobahn_client.v create mode 100644 v_windows/v/old/vlib/net/websocket/tests/autobahn/local_run/autobahn_client_wss.v create mode 100644 v_windows/v/old/vlib/net/websocket/tests/autobahn/ws_test/Dockerfile create mode 100644 v_windows/v/old/vlib/net/websocket/uri.v create mode 100644 v_windows/v/old/vlib/net/websocket/utils.v create mode 100644 v_windows/v/old/vlib/net/websocket/websocket_client.v create mode 100644 v_windows/v/old/vlib/net/websocket/websocket_nix.c.v create mode 100644 v_windows/v/old/vlib/net/websocket/websocket_server.v create mode 100644 v_windows/v/old/vlib/net/websocket/websocket_test.v create mode 100644 v_windows/v/old/vlib/net/websocket/websocket_windows.c.v create mode 100644 v_windows/v/old/vlib/orm/README.md create mode 100644 v_windows/v/old/vlib/orm/orm.v create mode 100644 v_windows/v/old/vlib/orm/orm_fn_test.v create mode 100644 v_windows/v/old/vlib/orm/orm_test.v create mode 100644 v_windows/v/old/vlib/os/args.v create mode 100644 v_windows/v/old/vlib/os/bare/bare_example_linux.v create mode 100644 v_windows/v/old/vlib/os/cmdline/cmdline.v create mode 100644 v_windows/v/old/vlib/os/cmdline/cmdline_test.v create mode 100644 v_windows/v/old/vlib/os/const.v create mode 100644 v_windows/v/old/vlib/os/const_nix.c.v create mode 100644 v_windows/v/old/vlib/os/const_windows.c.v create mode 100644 v_windows/v/old/vlib/os/environment.c.v create mode 100644 v_windows/v/old/vlib/os/environment_test.v create mode 100644 v_windows/v/old/vlib/os/fd.c.v create mode 100644 v_windows/v/old/vlib/os/file.c.v create mode 100644 v_windows/v/old/vlib/os/file_test.v create mode 100644 v_windows/v/old/vlib/os/glob_test.v create mode 100644 v_windows/v/old/vlib/os/inode.c.v create mode 100644 v_windows/v/old/vlib/os/inode_test.v create mode 100644 v_windows/v/old/vlib/os/notify/backend_default.c.v create mode 100644 v_windows/v/old/vlib/os/notify/backend_linux.c.v create mode 100644 v_windows/v/old/vlib/os/notify/notify.v create mode 100644 v_windows/v/old/vlib/os/notify/notify_test.v create mode 100644 v_windows/v/old/vlib/os/os.v create mode 100644 v_windows/v/old/vlib/os/os_android.c.v create mode 100644 v_windows/v/old/vlib/os/os_c.v create mode 100644 v_windows/v/old/vlib/os/os_darwin.c.v create mode 100644 v_windows/v/old/vlib/os/os_darwin.m create mode 100644 v_windows/v/old/vlib/os/os_linux.c.v create mode 100644 v_windows/v/old/vlib/os/os_nix.c.v create mode 100644 v_windows/v/old/vlib/os/os_test.v create mode 100644 v_windows/v/old/vlib/os/os_windows.c.v create mode 100644 v_windows/v/old/vlib/os/process.v create mode 100644 v_windows/v/old/vlib/os/process_nix.c.v create mode 100644 v_windows/v/old/vlib/os/process_test.v create mode 100644 v_windows/v/old/vlib/os/process_windows.c.v create mode 100644 v_windows/v/old/vlib/os/signal.c.v create mode 100644 v_windows/v/old/vlib/os/signal_test.v create mode 100644 v_windows/v/old/vlib/os2/keep_vfmt_happy.v create mode 100644 v_windows/v/old/vlib/os2/os2_darwin.c.v create mode 100644 v_windows/v/old/vlib/os2/os2_test.v create mode 100644 v_windows/v/old/vlib/os_js/environment.js.v create mode 100644 v_windows/v/old/vlib/os_js/file.js.v create mode 100644 v_windows/v/old/vlib/os_js/os.js.v create mode 100644 v_windows/v/old/vlib/os_js/os.v create mode 100644 v_windows/v/old/vlib/os_js/process.js.v create mode 100644 v_windows/v/old/vlib/pg/README.md create mode 100644 v_windows/v/old/vlib/pg/oid.v create mode 100644 v_windows/v/old/vlib/pg/orm.v create mode 100644 v_windows/v/old/vlib/pg/pg.v create mode 100644 v_windows/v/old/vlib/pg/pg_orm_test.v create mode 100644 v_windows/v/old/vlib/picoev/picoev.v create mode 100644 v_windows/v/old/vlib/picohttpparser/misc.v create mode 100644 v_windows/v/old/vlib/picohttpparser/picohttpparser.v create mode 100644 v_windows/v/old/vlib/picohttpparser/request.v create mode 100644 v_windows/v/old/vlib/picohttpparser/response.v create mode 100644 v_windows/v/old/vlib/rand/README.md create mode 100644 v_windows/v/old/vlib/rand/constants/constants.v create mode 100644 v_windows/v/old/vlib/rand/dist/README.md create mode 100644 v_windows/v/old/vlib/rand/dist/dist.v create mode 100644 v_windows/v/old/vlib/rand/dist/dist_test.v create mode 100644 v_windows/v/old/vlib/rand/mt19937/mt19937.v create mode 100644 v_windows/v/old/vlib/rand/mt19937/mt19937_test.v create mode 100644 v_windows/v/old/vlib/rand/musl/musl_rng.v create mode 100644 v_windows/v/old/vlib/rand/musl/musl_rng_test.v create mode 100644 v_windows/v/old/vlib/rand/pcg32/pcg32.v create mode 100644 v_windows/v/old/vlib/rand/pcg32/pcg32_test.v create mode 100644 v_windows/v/old/vlib/rand/rand.v create mode 100644 v_windows/v/old/vlib/rand/random_identifiers_test.v create mode 100644 v_windows/v/old/vlib/rand/random_numbers_test.v create mode 100644 v_windows/v/old/vlib/rand/seed/seed.v create mode 100644 v_windows/v/old/vlib/rand/splitmix64/splitmix64.v create mode 100644 v_windows/v/old/vlib/rand/splitmix64/splitmix64_test.v create mode 100644 v_windows/v/old/vlib/rand/sys/system_rng.c.v create mode 100644 v_windows/v/old/vlib/rand/sys/system_rng.js.v create mode 100644 v_windows/v/old/vlib/rand/sys/system_rng_test.v create mode 100644 v_windows/v/old/vlib/rand/util/util.v create mode 100644 v_windows/v/old/vlib/rand/util/util_test.v create mode 100644 v_windows/v/old/vlib/rand/wyrand/wyrand.v create mode 100644 v_windows/v/old/vlib/rand/wyrand/wyrand_test.v create mode 100644 v_windows/v/old/vlib/readline/README.md create mode 100644 v_windows/v/old/vlib/readline/readline.v create mode 100644 v_windows/v/old/vlib/readline/readline_default.c.v create mode 100644 v_windows/v/old/vlib/readline/readline_linux.c.v create mode 100644 v_windows/v/old/vlib/readline/readline_test.v create mode 100644 v_windows/v/old/vlib/readline/readline_windows.c.v create mode 100644 v_windows/v/old/vlib/regex/README.md create mode 100644 v_windows/v/old/vlib/regex/regex.v create mode 100644 v_windows/v/old/vlib/regex/regex_opt.v create mode 100644 v_windows/v/old/vlib/regex/regex_test.v create mode 100644 v_windows/v/old/vlib/regex/regex_util.v create mode 100644 v_windows/v/old/vlib/runtime/runtime.v create mode 100644 v_windows/v/old/vlib/runtime/runtime_nix.c.v create mode 100644 v_windows/v/old/vlib/runtime/runtime_test.v create mode 100644 v_windows/v/old/vlib/runtime/runtime_windows.c.v create mode 100644 v_windows/v/old/vlib/semver/LICENSE.md create mode 100644 v_windows/v/old/vlib/semver/README.md create mode 100644 v_windows/v/old/vlib/semver/compare.v create mode 100644 v_windows/v/old/vlib/semver/parse.v create mode 100644 v_windows/v/old/vlib/semver/range.v create mode 100644 v_windows/v/old/vlib/semver/semver.v create mode 100644 v_windows/v/old/vlib/semver/semver_test.v create mode 100644 v_windows/v/old/vlib/semver/util.v create mode 100644 v_windows/v/old/vlib/semver/v.mod create mode 100644 v_windows/v/old/vlib/sokol/audio/audio.v create mode 100644 v_windows/v/old/vlib/sokol/c/declaration.c.v create mode 100644 v_windows/v/old/vlib/sokol/f/f.v create mode 100644 v_windows/v/old/vlib/sokol/gfx/enums.v create mode 100644 v_windows/v/old/vlib/sokol/gfx/gfx.v create mode 100644 v_windows/v/old/vlib/sokol/gfx/gfx_funcs.v create mode 100644 v_windows/v/old/vlib/sokol/gfx/gfx_structs.v create mode 100644 v_windows/v/old/vlib/sokol/gfx/gfx_utils.v create mode 100644 v_windows/v/old/vlib/sokol/sapp/enums.v create mode 100644 v_windows/v/old/vlib/sokol/sapp/sapp.v create mode 100644 v_windows/v/old/vlib/sokol/sapp/sapp_funcs.v create mode 100644 v_windows/v/old/vlib/sokol/sapp/sapp_structs.v create mode 100644 v_windows/v/old/vlib/sokol/sfons/sfons.v create mode 100644 v_windows/v/old/vlib/sokol/sfons/sfons_funcs.v create mode 100644 v_windows/v/old/vlib/sokol/sgl/sgl.v create mode 100644 v_windows/v/old/vlib/sokol/sgl/sgl_funcs.v create mode 100644 v_windows/v/old/vlib/sokol/sgl/sgl_structs.v create mode 100644 v_windows/v/old/vlib/sokol/sokol.v create mode 100644 v_windows/v/old/vlib/sqlite/README.md create mode 100644 v_windows/v/old/vlib/sqlite/orm.v create mode 100644 v_windows/v/old/vlib/sqlite/sqlite.v create mode 100644 v_windows/v/old/vlib/sqlite/sqlite_orm_test.v create mode 100644 v_windows/v/old/vlib/sqlite/sqlite_test.v create mode 100644 v_windows/v/old/vlib/sqlite/stmt.v create mode 100644 v_windows/v/old/vlib/stbi/stbi.v create mode 100644 v_windows/v/old/vlib/strconv/atof.v create mode 100644 v_windows/v/old/vlib/strconv/atof_test.v create mode 100644 v_windows/v/old/vlib/strconv/atofq.v create mode 100644 v_windows/v/old/vlib/strconv/atoi.v create mode 100644 v_windows/v/old/vlib/strconv/atoi_test.v create mode 100644 v_windows/v/old/vlib/strconv/f32_f64_to_string_test.v create mode 100644 v_windows/v/old/vlib/strconv/f32_str.v create mode 100644 v_windows/v/old/vlib/strconv/f64_str.v create mode 100644 v_windows/v/old/vlib/strconv/format.md create mode 100644 v_windows/v/old/vlib/strconv/format.v create mode 100644 v_windows/v/old/vlib/strconv/format_mem.v create mode 100644 v_windows/v/old/vlib/strconv/format_test.v create mode 100644 v_windows/v/old/vlib/strconv/ftoa.v create mode 100644 v_windows/v/old/vlib/strconv/number_to_base.v create mode 100644 v_windows/v/old/vlib/strconv/number_to_base_test.v create mode 100644 v_windows/v/old/vlib/strconv/structs.v create mode 100644 v_windows/v/old/vlib/strconv/tables.v create mode 100644 v_windows/v/old/vlib/strconv/utilities.v create mode 100644 v_windows/v/old/vlib/strconv/vprintf.v create mode 100644 v_windows/v/old/vlib/strings/builder.js.v create mode 100644 v_windows/v/old/vlib/strings/builder.v create mode 100644 v_windows/v/old/vlib/strings/builder_test.v create mode 100644 v_windows/v/old/vlib/strings/similarity.v create mode 100644 v_windows/v/old/vlib/strings/similarity_test.v create mode 100644 v_windows/v/old/vlib/strings/strings.c.v create mode 100644 v_windows/v/old/vlib/strings/strings.js.v create mode 100644 v_windows/v/old/vlib/strings/strings.v create mode 100644 v_windows/v/old/vlib/strings/strings_test.v create mode 100644 v_windows/v/old/vlib/strings/textscanner/textscanner.v create mode 100644 v_windows/v/old/vlib/strings/textscanner/textscanner_test.v create mode 100644 v_windows/v/old/vlib/sync/array_rlock_test.v create mode 100644 v_windows/v/old/vlib/sync/atomic2/atomic.v create mode 100644 v_windows/v/old/vlib/sync/atomic2/atomic_test.v create mode 100644 v_windows/v/old/vlib/sync/bench/channel_bench_go.go create mode 100644 v_windows/v/old/vlib/sync/bench/channel_bench_v.v create mode 100644 v_windows/v/old/vlib/sync/bench/many_writers_and_receivers_on_1_channel.v create mode 100644 v_windows/v/old/vlib/sync/bench/results.md create mode 100644 v_windows/v/old/vlib/sync/channel_1_test.v create mode 100644 v_windows/v/old/vlib/sync/channel_2_test.v create mode 100644 v_windows/v/old/vlib/sync/channel_3_test.v create mode 100644 v_windows/v/old/vlib/sync/channel_4_test.v create mode 100644 v_windows/v/old/vlib/sync/channel_array_mut_test.v create mode 100644 v_windows/v/old/vlib/sync/channel_close_test.v create mode 100644 v_windows/v/old/vlib/sync/channel_fill_test.v create mode 100644 v_windows/v/old/vlib/sync/channel_opt_propagate_test.v create mode 100644 v_windows/v/old/vlib/sync/channel_polling_test.v create mode 100644 v_windows/v/old/vlib/sync/channel_push_or_1_test.v create mode 100644 v_windows/v/old/vlib/sync/channel_push_or_2_test.v create mode 100644 v_windows/v/old/vlib/sync/channel_select_2_test.v create mode 100644 v_windows/v/old/vlib/sync/channel_select_3_test.v create mode 100644 v_windows/v/old/vlib/sync/channel_select_4_test.v create mode 100644 v_windows/v/old/vlib/sync/channel_select_5_test.v create mode 100644 v_windows/v/old/vlib/sync/channel_select_6_test.v create mode 100644 v_windows/v/old/vlib/sync/channel_select_test.v create mode 100644 v_windows/v/old/vlib/sync/channel_try_buf_test.v create mode 100644 v_windows/v/old/vlib/sync/channel_try_unbuf_test.v create mode 100644 v_windows/v/old/vlib/sync/channels.v create mode 100644 v_windows/v/old/vlib/sync/pool/README.md create mode 100644 v_windows/v/old/vlib/sync/pool/pool.v create mode 100644 v_windows/v/old/vlib/sync/pool/pool_test.v create mode 100644 v_windows/v/old/vlib/sync/select_close_test.v create mode 100644 v_windows/v/old/vlib/sync/struct_chan_init_test.v create mode 100644 v_windows/v/old/vlib/sync/sync_default.c.v create mode 100644 v_windows/v/old/vlib/sync/sync_macos.c.v create mode 100644 v_windows/v/old/vlib/sync/sync_windows.c.v create mode 100644 v_windows/v/old/vlib/sync/threads/threads.c.v create mode 100644 v_windows/v/old/vlib/sync/threads/threads.v create mode 100644 v_windows/v/old/vlib/sync/waitgroup.v create mode 100644 v_windows/v/old/vlib/sync/waitgroup_test.v create mode 100644 v_windows/v/old/vlib/szip/szip.v create mode 100644 v_windows/v/old/vlib/szip/szip_test.v create mode 100644 v_windows/v/old/vlib/term/README.md create mode 100644 v_windows/v/old/vlib/term/colors.v create mode 100644 v_windows/v/old/vlib/term/control.v create mode 100644 v_windows/v/old/vlib/term/term.js.v create mode 100644 v_windows/v/old/vlib/term/term.v create mode 100644 v_windows/v/old/vlib/term/term_nix.c.v create mode 100644 v_windows/v/old/vlib/term/term_test.v create mode 100644 v_windows/v/old/vlib/term/term_windows.c.v create mode 100644 v_windows/v/old/vlib/term/ui/README.md create mode 100644 v_windows/v/old/vlib/term/ui/color.v create mode 100644 v_windows/v/old/vlib/term/ui/consoleapi_windows.c.v create mode 100644 v_windows/v/old/vlib/term/ui/input.v create mode 100644 v_windows/v/old/vlib/term/ui/input_nix.c.v create mode 100644 v_windows/v/old/vlib/term/ui/input_windows.c.v create mode 100644 v_windows/v/old/vlib/term/ui/termios_nix.c.v create mode 100644 v_windows/v/old/vlib/term/ui/ui.v create mode 100644 v_windows/v/old/vlib/time/Y2K38_test.v create mode 100644 v_windows/v/old/vlib/time/format.v create mode 100644 v_windows/v/old/vlib/time/misc/misc.v create mode 100644 v_windows/v/old/vlib/time/misc/misc_test.v create mode 100644 v_windows/v/old/vlib/time/operator.v create mode 100644 v_windows/v/old/vlib/time/operator_test.v create mode 100644 v_windows/v/old/vlib/time/parse.v create mode 100644 v_windows/v/old/vlib/time/parse_test.v create mode 100644 v_windows/v/old/vlib/time/private_test.v create mode 100644 v_windows/v/old/vlib/time/stopwatch.v create mode 100644 v_windows/v/old/vlib/time/stopwatch_test.v create mode 100644 v_windows/v/old/vlib/time/time.v create mode 100644 v_windows/v/old/vlib/time/time_darwin.c.v create mode 100644 v_windows/v/old/vlib/time/time_format_test.v create mode 100644 v_windows/v/old/vlib/time/time_linux.c.v create mode 100644 v_windows/v/old/vlib/time/time_nix.c.v create mode 100644 v_windows/v/old/vlib/time/time_solaris.c.v create mode 100644 v_windows/v/old/vlib/time/time_test.v create mode 100644 v_windows/v/old/vlib/time/time_windows.c.v create mode 100644 v_windows/v/old/vlib/time/unix.v create mode 100644 v_windows/v/old/vlib/v/README.md create mode 100644 v_windows/v/old/vlib/v/TEMPLATES.md create mode 100644 v_windows/v/old/vlib/v/ast/ast.v create mode 100644 v_windows/v/old/vlib/v/ast/attr.v create mode 100644 v_windows/v/old/vlib/v/ast/cflags.v create mode 100644 v_windows/v/old/vlib/v/ast/cflags_test.v create mode 100644 v_windows/v/old/vlib/v/ast/comptime_const_values.v create mode 100644 v_windows/v/old/vlib/v/ast/init.v create mode 100644 v_windows/v/old/vlib/v/ast/scope.v create mode 100644 v_windows/v/old/vlib/v/ast/str.v create mode 100644 v_windows/v/old/vlib/v/ast/table.v create mode 100644 v_windows/v/old/vlib/v/ast/types.v create mode 100644 v_windows/v/old/vlib/v/ast/types_test.v create mode 100644 v_windows/v/old/vlib/v/ast/walker/walker.v create mode 100644 v_windows/v/old/vlib/v/ast/walker/walker_test.v create mode 100644 v_windows/v/old/vlib/v/builder/builder.v create mode 100644 v_windows/v/old/vlib/v/builder/c.v create mode 100644 v_windows/v/old/vlib/v/builder/cc.v create mode 100644 v_windows/v/old/vlib/v/builder/cflags.v create mode 100644 v_windows/v/old/vlib/v/builder/compile.v create mode 100644 v_windows/v/old/vlib/v/builder/js.v create mode 100644 v_windows/v/old/vlib/v/builder/msvc.v create mode 100644 v_windows/v/old/vlib/v/builder/native.v create mode 100644 v_windows/v/old/vlib/v/callgraph/callgraph.v create mode 100644 v_windows/v/old/vlib/v/cflag/cflags.v create mode 100644 v_windows/v/old/vlib/v/checker/check_types.v create mode 100644 v_windows/v/old/vlib/v/checker/checker.v create mode 100644 v_windows/v/old/vlib/v/checker/comptime_const_eval.v create mode 100644 v_windows/v/old/vlib/v/checker/noreturn.v create mode 100644 v_windows/v/old/vlib/v/checker/tests/.gitattributes create mode 100644 v_windows/v/old/vlib/v/checker/tests/.gitignore create mode 100644 v_windows/v/old/vlib/v/checker/tests/a_test_file_with_0_test_fns_test.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/a_test_file_with_0_test_fns_test.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/add_op_wrong_type_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/add_op_wrong_type_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/alias_array_unknown_op_overloading_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/alias_array_unknown_op_overloading_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/alias_map_unknown_op_overloading_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/alias_map_unknown_op_overloading_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/alias_type_exists.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/alias_type_exists.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/ambiguous_field_method_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/ambiguous_field_method_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/ambiguous_function_call.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/ambiguous_function_call.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/any_int_float_ban_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/any_int_float_ban_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/append_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/append_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_append_array_type_mismatch_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_append_array_type_mismatch_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_builtin_redefinition.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_builtin_redefinition.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_cmp_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_cmp_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_declare_element_a.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_declare_element_a.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_declare_element_b.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_declare_element_b.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_declare_element_c.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_declare_element_c.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_element_type.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_element_type.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_filter_fn_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_filter_fn_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_index.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_index.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_init_sum_type_without_init_value_and_len_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_init_sum_type_without_init_value_and_len_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_insert_type_mismatch.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_insert_type_mismatch.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_literal_modify_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_literal_modify_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_map_arg_mismatch.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_map_arg_mismatch.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_map_fn_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_map_fn_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_map_void_fn_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_map_void_fn_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_of_interfaces_with_len_without_init.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_of_interfaces_with_len_without_init.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_or_map_assign_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_or_map_assign_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_prepend_type_mismatch.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_prepend_type_mismatch.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_sort_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_sort_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_sort_struct_no_body_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/array_sort_struct_no_body_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/arrow_op_wrong_left_type_err_a.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/arrow_op_wrong_left_type_err_a.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/arrow_op_wrong_left_type_err_b.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/arrow_op_wrong_left_type_err_b.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/arrow_op_wrong_right_type_err_a.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/arrow_op_wrong_right_type_err_a.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/arrow_op_wrong_right_type_err_b.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/arrow_op_wrong_right_type_err_b.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/asm_immutable_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/asm_immutable_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/assert_optional_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/assert_optional_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_array_init_with_no_type.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_array_init_with_no_type.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_deref_fn_call_on_left_side_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_deref_fn_call_on_left_side_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_a.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_a.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_b.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_b.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_c.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_c.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_d.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_d.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_e.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_e.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_f.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_f.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_g.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_g.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_h.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_h.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_i.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_i.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_a.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_a.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_b.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_b.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_c.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_c.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_d.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_d.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_e.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_e.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_f.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_f.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_g.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_g.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_h.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_h.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_i.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_i.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_j.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_j.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_k.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_k.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_unresolved_variables_err_chain.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_expr_unresolved_variables_err_chain.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_fn_call_on_left_side_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_fn_call_on_left_side_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_multi_immutable_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_multi_immutable_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_multi_mismatch.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_multi_mismatch.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_mut.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_mut.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_sumtype2_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_sumtype2_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_sumtype_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/assign_sumtype_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/bad_types_in_string_inter_lit.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/bad_types_in_string_inter_lit.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/bin_lit_without_digit_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/bin_lit_without_digit_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/bin_lit_wrong_digit_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/bin_lit_wrong_digit_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/bit_op_wrong_left_type_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/bit_op_wrong_left_type_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/bit_op_wrong_right_type_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/bit_op_wrong_right_type_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/blank_ident_invalid_use.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/blank_ident_invalid_use.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/blank_modify.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/blank_modify.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/bool_string_cast_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/bool_string_cast_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/break_anon_fn_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/break_anon_fn_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/c_fn_surplus_args.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/c_fn_surplus_args.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/cannot_assign_array.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/cannot_assign_array.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/cannot_cast_to_alias.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/cannot_cast_to_alias.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/cannot_cast_to_struct.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/cannot_cast_to_struct.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/cast_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/cast_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/cast_string_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/cast_string_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/cast_string_with_byte_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/cast_string_with_byte_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/cast_void.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/cast_void.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/chan_args.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/chan_args.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/chan_mut.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/chan_mut.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/chan_ref.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/chan_ref.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/char_str.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/char_str.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/comparing_typesymbol_to_a_type_should_not_compile.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/comparing_typesymbol_to_a_type_should_not_compile.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/comptime_call_method.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/comptime_call_method.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/comptime_call_no_unused_var.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/comptime_call_no_unused_var.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/comptime_env/env_parser_errors_1.run.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/comptime_env/env_parser_errors_1.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/comptime_env/env_parser_errors_2.run.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/comptime_env/env_parser_errors_2.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/comptime_env/env_parser_errors_3.run.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/comptime_env/env_parser_errors_3.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/comptime_env/using_comptime_env.run.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/comptime_env/using_comptime_env.var.run.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/comptime_env/using_comptime_env.var_invalid.run.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/comptime_env/using_comptime_env.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/comptime_field_selector_not_in_for_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/comptime_field_selector_not_in_for_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/comptime_field_selector_not_name_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/comptime_field_selector_not_name_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/comptime_for.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/comptime_for.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/const_array_unknown_type_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/const_array_unknown_type_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/const_define_in_function_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/const_define_in_function_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/const_field_add_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/const_field_add_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/const_field_dec_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/const_field_dec_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/const_field_inc_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/const_field_inc_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/const_field_name_duplicate_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/const_field_name_duplicate_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/const_field_name_snake_case.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/const_field_name_snake_case.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/const_field_sub_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/const_field_sub_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/ctdefine.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/ctdefine.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_error.mysymbol.run.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_error.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_error.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_debug.cg.run.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_debug.debug.bar.run.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_debug.debug.run.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_debug.g.run.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_debug.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_debug.run.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_debug.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_flag.mydebug.run.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_flag.nodebug.run.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_flag.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_flag.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/dec_lit_wrong_digit_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/dec_lit_wrong_digit_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/decompose_type_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/decompose_type_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/defer_in_for.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/defer_in_for.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/defer_optional.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/defer_optional.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/deprecations.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/deprecations.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/direct_map_alias_init_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/direct_map_alias_init_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/disallow_pointer_arithmetic_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/disallow_pointer_arithmetic_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/div_mod_by_cast_zero_int_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/div_mod_by_cast_zero_int_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/div_op_wrong_type_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/div_op_wrong_type_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/division_by_cast_zero_float_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/division_by_cast_zero_float_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/division_by_zero_float_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/division_by_zero_float_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/division_by_zero_int_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/division_by_zero_int_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/dump_of_void_expr.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/dump_of_void_expr.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/duplicate_field_method_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/duplicate_field_method_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/enum_as_int_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/enum_as_int_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/enum_cast.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/enum_cast.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/enum_empty.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/enum_empty.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/enum_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/enum_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/enum_field_name_duplicate_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/enum_field_name_duplicate_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/enum_field_overflow.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/enum_field_overflow.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/enum_field_value_duplicate_a.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/enum_field_value_duplicate_a.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/enum_field_value_duplicate_b.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/enum_field_value_duplicate_b.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/enum_field_value_overflow.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/enum_field_value_overflow.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/enum_op_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/enum_op_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/enum_op_flag_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/enum_op_flag_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/enum_single_letter.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/enum_single_letter.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/eq_ne_op_wrong_type_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/error_fn_with_0_args.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/error_fn_with_0_args.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/error_with_comment_with_crlf_ending.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/error_with_comment_with_crlf_ending.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/error_with_comment_with_lf_ending.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/error_with_comment_with_lf_ending.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/error_with_several_comments_with_crlf_ending.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/error_with_several_comments_with_crlf_ending.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/error_with_unicode.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/error_with_unicode.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/expression_should_return_an_option.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/expression_should_return_an_option.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/filter_func_return_nonbool_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/filter_func_return_nonbool_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/filter_on_non_arr_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/filter_on_non_arr_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/fixed_array_conv.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/fixed_array_conv.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/fixed_array_non_const_size_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/fixed_array_non_const_size_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/fixed_array_size_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/fixed_array_size_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/float_lit_exp_not_integer_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/float_lit_exp_not_integer_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/float_lit_exp_without_digit_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/float_lit_exp_without_digit_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/float_lit_too_many_points_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/float_lit_too_many_points_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/float_modulo_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/float_modulo_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/fn_args.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/fn_args.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/fn_call_no_body.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/fn_call_no_body.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/fn_duplicate.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/fn_duplicate.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/fn_init_sig.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/fn_init_sig.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/fn_return_or_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/fn_return_or_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/fn_type_exists.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/fn_type_exists.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/fn_var.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/fn_var.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/fn_variadic.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/fn_variadic.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/for_in_index_optional.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/for_in_index_optional.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/for_in_index_type.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/for_in_index_type.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/for_in_map_one_variable_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/for_in_map_one_variable_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/for_in_mut_val_type.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/for_in_mut_val_type.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/for_in_range_not_match_type.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/for_in_range_not_match_type.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/for_in_range_string_type.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/for_in_range_string_type.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/for_match_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/for_match_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/function_arg_mutable_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/function_arg_mutable_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/function_arg_redefinition.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/function_arg_redefinition.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/function_count_of_args_mismatch_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/function_count_of_args_mismatch_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/function_missing_return_type.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/function_missing_return_type.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/function_variadic_arg_array_decompose.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/function_variadic_arg_array_decompose.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/function_wrong_arg_type.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/function_wrong_arg_type.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/function_wrong_return_type.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/function_wrong_return_type.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/generic_fn_decl_without_generic_names_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/generic_fn_decl_without_generic_names_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/generic_param_used_as_an_array_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/generic_param_used_as_an_array_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/generic_sumtype_invalid_variant.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/generic_sumtype_invalid_variant.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/generics_fn_called_arg_mismatch.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/generics_fn_called_arg_mismatch.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/generics_fn_called_multi_args_mismatch.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/generics_fn_called_multi_args_mismatch.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/generics_fn_called_no_arg_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/generics_fn_called_no_arg_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/generics_fn_called_outside_of_generic_fn.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/generics_fn_called_outside_of_generic_fn.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/generics_fn_called_variadic_arg_mismatch.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/generics_fn_called_variadic_arg_mismatch.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/generics_fn_return_generic_struct_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/generics_fn_return_generic_struct_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/generics_method_receiver_type_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/generics_method_receiver_type_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/generics_non_generic_fn_called_like_a_generic_one.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/generics_non_generic_fn_called_like_a_generic_one.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/generics_struct_declaration_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/generics_struct_declaration_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/generics_struct_init_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/generics_struct_init_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/generics_too_many_parameters.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/generics_too_many_parameters.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/generics_type_ambiguous.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/generics_type_ambiguous.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/globals/assign_no_value.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/globals/assign_no_value.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/globals/incorrect_name_global.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/globals/incorrect_name_global.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/globals/no_type.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/globals/no_type.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/globals/unexpected_eof.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/globals/unexpected_eof.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/globals/unknown_typ.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/globals/unknown_typ.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/globals_error.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/globals_error.run.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/globals_error.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/globals_run/function_stored_in_global.run.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/globals_run/function_stored_in_global.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/globals_run/global_array_indexed_by_global_fn.run.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/globals_run/global_array_indexed_by_global_fn.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/go_expr.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/go_expr.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/go_mut_arg.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/go_mut_arg.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/go_mut_receiver.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/go_mut_receiver.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/go_wait_or.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/go_wait_or.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/goto_label.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/goto_label.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/hex_lit_without_digit_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/hex_lit_without_digit_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/hex_lit_wrong_digit_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/hex_lit_wrong_digit_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/hex_literal_overflow.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/hex_literal_overflow.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/ierror_in_return_tuple.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/ierror_in_return_tuple.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/if_expr_last_stmt.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/if_expr_last_stmt.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/if_expr_mismatch.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/if_expr_mismatch.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/if_expr_no_else.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/if_expr_no_else.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/if_expr_optional_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/if_expr_optional_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/if_match_expr.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/if_match_expr.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/if_match_expr_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/if_match_expr_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/if_match_result.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/if_match_result.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/if_non_bool_cond.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/if_non_bool_cond.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/immutable_arg.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/immutable_arg.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/immutable_array_field_assign.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/immutable_array_field_assign.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/immutable_array_field_shift.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/immutable_array_field_shift.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/immutable_array_struct_assign.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/immutable_array_struct_assign.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/immutable_array_struct_shift.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/immutable_array_struct_shift.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/immutable_array_var.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/immutable_array_var.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/immutable_builtin_modify.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/immutable_builtin_modify.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/immutable_field.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/immutable_field.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/immutable_field_postfix.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/immutable_field_postfix.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/immutable_interface_field.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/immutable_interface_field.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/immutable_map.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/immutable_map.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/immutable_rec.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/immutable_rec.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/immutable_struct_postfix.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/immutable_struct_postfix.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/immutable_var.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/immutable_var.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/immutable_var_postfix.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/immutable_var_postfix.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/import_duplicate_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/import_duplicate_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/import_middle_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/import_middle_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/import_mod_as_mod_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/import_mod_as_mod_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/import_mod_sub_as_sub_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/import_mod_sub_as_sub_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/import_multiple_modules_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/import_multiple_modules_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/import_not_found_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/import_not_found_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/import_not_same_line_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/import_not_same_line_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/import_symbol_empty.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/import_symbol_empty.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/import_symbol_fn_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/import_symbol_fn_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/import_symbol_invalid.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/import_symbol_invalid.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/import_symbol_private_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/import_symbol_private_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/import_symbol_type_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/import_symbol_type_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/import_symbol_unclosed.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/import_symbol_unclosed.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/import_syntax_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/import_syntax_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/import_unused_warning.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/import_unused_warning.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/in_mismatch_type.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/in_mismatch_type.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/incorrect_for_in_name_variable.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/incorrect_for_in_name_variable.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/incorrect_name_alias_type.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/incorrect_name_alias_type.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/incorrect_name_const.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/incorrect_name_const.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/incorrect_name_enum.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/incorrect_name_enum.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/incorrect_name_enum_field.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/incorrect_name_enum_field.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/incorrect_name_fn_type.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/incorrect_name_fn_type.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/incorrect_name_function.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/incorrect_name_function.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/incorrect_name_interface.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/incorrect_name_interface.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/incorrect_name_interface_method.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/incorrect_name_interface_method.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/incorrect_name_module.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/incorrect_name_module.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/incorrect_name_struct.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/incorrect_name_struct.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/incorrect_name_struct_field.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/incorrect_name_struct_field.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/incorrect_name_sum_type.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/incorrect_name_sum_type.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/incorrect_name_variable.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/incorrect_name_variable.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/index_expr.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/index_expr.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/infix_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/infix_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/int_modulo_by_zero_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/int_modulo_by_zero_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/interface_implementing_interface.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/interface_implementing_interface.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/interface_implementing_own_interface_method.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/interface_implementing_own_interface_method.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/interface_init_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/interface_init_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/interface_return_parameter_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/interface_return_parameter_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/interface_too_many_embedding_levels.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/interface_too_many_embedding_levels.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/interpolation_recursive_str_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/interpolation_recursive_str_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/invalid_char_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/invalid_char_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/invalid_property.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/invalid_property.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/invalid_vweb_param_type.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/invalid_vweb_param_type.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/invert_other_types_bits_error.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/invert_other_types_bits_error.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/is_type_invalid.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/is_type_invalid.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/is_type_not_exist.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/is_type_not_exist.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/labelled_break_continue.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/labelled_break_continue.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/lock_already_locked.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/lock_already_locked.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/lock_already_rlocked.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/lock_already_rlocked.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/lock_const.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/lock_const.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/lock_needed.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/lock_needed.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/lock_nonshared.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/lock_nonshared.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/main_and_script_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/main_and_script_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/main_args_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/main_args_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/main_called_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/main_called_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/main_no_body_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/main_no_body_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/main_return_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/main_return_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/map_delete.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/map_delete.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/map_func_void_return_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/map_func_void_return_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/map_init_invalid_syntax.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/map_init_invalid_syntax.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/map_init_key_duplicate_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/map_init_key_duplicate_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/map_init_wrong_type.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/map_init_wrong_type.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/map_ops.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/map_ops.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/map_unknown_value.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/map_unknown_value.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/match_alias_type_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/match_alias_type_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/match_duplicate_branch.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/match_duplicate_branch.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/match_else_last_expr.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/match_else_last_expr.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/match_expr_and_expected_type_error.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/match_expr_and_expected_type_error.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/match_expr_else.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/match_expr_else.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/match_expr_empty_branch.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/match_expr_empty_branch.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/match_expr_non_void_stmt_last.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/match_expr_non_void_stmt_last.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/match_invalid_type.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/match_invalid_type.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/match_return_mismatch_type_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/match_return_mismatch_type_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/match_sumtype_multiple_types.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/match_sumtype_multiple_types.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/match_undefined_cond.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/match_undefined_cond.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/method_array_slice.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/method_array_slice.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/method_generic_infer_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/method_generic_infer_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/method_op_alias_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/method_op_alias_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/method_op_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/method_op_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/method_wrong_arg_type.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/method_wrong_arg_type.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/minus_op_wrong_type_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/minus_op_wrong_type_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/mismatched_ptr_op_ptr.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/mismatched_ptr_op_ptr.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/missing_c_lib_header_1.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/missing_c_lib_header_1.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/missing_c_lib_header_with_explanation_2.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/missing_c_lib_header_with_explanation_2.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/mod_op_wrong_type_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/mod_op_wrong_type_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/modify_const_with_ref.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/modify_const_with_ref.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/module_not_at_same_line_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/module_not_at_same_line_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/modules/module_alias_started_with_underscore.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/modules/module_alias_started_with_underscore/main.v create mode 100644 v_windows/v/old/vlib/v/checker/tests/modules/module_alias_started_with_underscore/underscore.v create mode 100644 v_windows/v/old/vlib/v/checker/tests/modules/overload_return_type.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/modules/overload_return_type/main.v create mode 100644 v_windows/v/old/vlib/v/checker/tests/modules/overload_return_type/point.v create mode 100644 v_windows/v/old/vlib/v/checker/tests/mul_op_wrong_type_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/mul_op_wrong_type_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/multi_const_field_name_duplicate_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/multi_const_field_name_duplicate_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/multi_names_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/multi_names_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/multi_value_method_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/multi_value_method_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/mut_arg.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/mut_arg.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/mut_args_warning.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/mut_args_warning.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/mut_array_get_element_address_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/mut_array_get_element_address_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/mut_int.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/mut_int.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/mut_map_get_value_address_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/mut_map_get_value_address_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/mut_receiver.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/mut_receiver.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/mut_receiver_lit.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/mut_receiver_lit.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/negative_assign_to_unsigned.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/negative_assign_to_unsigned.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/nested_aliases.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/nested_aliases.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/no_heap_struct.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/no_heap_struct.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/no_interface_instantiation_a.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/no_interface_instantiation_a.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/no_interface_instantiation_b.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/no_interface_instantiation_b.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/no_interface_instantiation_c.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/no_interface_instantiation_c.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/no_interface_str.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/no_interface_str.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/no_main_mod.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/no_main_mod.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/no_main_println_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/no_main_println_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/no_method_on_interface_propagation.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/no_method_on_interface_propagation.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/no_pub_in_main.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/no_warning_for_in_mut_var_unused.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/no_warning_for_in_mut_var_unused.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/non_lvalue_as_voidptr.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/non_lvalue_as_voidptr.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/non_matching_functional_args.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/non_matching_functional_args.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/none_type_cast_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/none_type_cast_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/noreturn_with_non_empty_loop_at_end.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/noreturn_with_non_empty_loop_at_end.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/noreturn_with_return.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/noreturn_with_return.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/noreturn_without_loop_or_another_noreturn_at_end.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/noreturn_without_loop_or_another_noreturn_at_end.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/oct_lit_without_digit_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/oct_lit_without_digit_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/oct_lit_wrong_digit_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/oct_lit_wrong_digit_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/optional_fn_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/optional_fn_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/optional_in_println_mismatch.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/optional_in_println_mismatch.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/optional_interface_mismatch.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/optional_interface_mismatch.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/optional_or_block_mismatch.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/optional_or_block_mismatch.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/optional_or_block_none_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/optional_or_block_none_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/optional_or_block_returns_value_of_incompatible_type.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/optional_or_block_returns_value_of_incompatible_type.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/optional_propagate_nested.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/optional_propagate_nested.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/optional_type_call_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/optional_type_call_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/or_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/or_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/or_expr_types_mismatch.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/or_expr_types_mismatch.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/orm_empty_struct.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/orm_empty_struct.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/os_prefix.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/os_prefix.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/overflow_int_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/overflow_int_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/overload_return_type.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/overload_return_type.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/oversized_int_lit.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/oversized_int_lit.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/pass_mut_lit.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/pass_mut_lit.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/passing_expr_to_fn_expecting_voidptr.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/passing_expr_to_fn_expecting_voidptr.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/pointer_ops.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/pointer_ops.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/prefix_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/prefix_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/prefix_expr_decl_assign_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/prefix_expr_decl_assign_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/print_char.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/print_char.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/println_can_not_print_void_expressions.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/println_can_not_print_void_expressions.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/ptr_assign.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/ptr_assign.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/receiver_unknown_type_single_letter.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/receiver_unknown_type_single_letter.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/recursive_interface_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/recursive_interface_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/reference_field_must_be_initialized.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/reference_field_must_be_initialized.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/reference_return.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/reference_return.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_count_mismatch.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_count_mismatch.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_duplicate_with_none_err_a.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_duplicate_with_none_err_a.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_duplicate_with_none_err_b.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_duplicate_with_none_err_b.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_fixed_array.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_fixed_array.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_missing_comp_if.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_missing_comp_if.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_missing_comp_if_nested.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_missing_comp_if_nested.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_missing_if_else_simple.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_missing_if_else_simple.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_missing_if_match.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_missing_if_match.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_missing_match_if.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_missing_match_if.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_missing_match_simple.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_missing_match_simple.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_missing_nested.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_missing_nested.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_missing_simple.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_missing_simple.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_ref_as_no_ref_bug.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_ref_as_no_ref_bug.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_type.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_type.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_working_comp_if.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_working_comp_if.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_working_comp_if_nested.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_working_comp_if_nested.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_working_if_match.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_working_if_match.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_working_match_if.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_working_match_if.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_working_nested.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_working_nested.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_working_simple.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_working_simple.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_working_two_if.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_working_two_if.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_working_unsafe.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/return_working_unsafe.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/returns/return_missing_simple.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/rshift_op_wrong_left_type_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/rshift_op_wrong_left_type_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/rshift_op_wrong_right_type_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/rshift_op_wrong_right_type_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/run/noreturn_fn_can_be_used_instead_of_panic.run.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/run/noreturn_fn_can_be_used_instead_of_panic.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/run/unused_variable_warning.run.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/run/unused_variable_warning.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/selective_const_import.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/selective_const_import.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/selector_expr_assign.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/selector_expr_assign.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/selector_expr_optional_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/selector_expr_optional_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/shared_bad_args.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/shared_bad_args.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/shared_element_lock.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/shared_element_lock.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/shared_lock.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/shared_lock.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/shared_type_mismatch.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/shared_type_mismatch.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/shift_op_wrong_left_type_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/shift_op_wrong_left_type_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/shift_op_wrong_right_type_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/shift_op_wrong_right_type_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/short_struct_wrong_number.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/short_struct_wrong_number.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/slice_reassignment.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/slice_reassignment.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/sort_method_called_on_immutable_receiver.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/sort_method_called_on_immutable_receiver.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/static_vars_in_translated_mode.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/static_vars_in_translated_mode.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/store_string_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/store_string_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/str_method_0_arguments.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/str_method_0_arguments.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/str_method_return_string.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/str_method_return_string.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/string_char_null_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/string_char_null_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/string_escape_u_err_a.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/string_escape_u_err_a.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/string_escape_u_err_b.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/string_escape_u_err_b.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/string_escape_x_err_a.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/string_escape_x_err_a.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/string_escape_x_err_b.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/string_escape_x_err_b.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/string_index_assign_error.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/string_index_assign_error.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/string_index_non_int_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/string_index_non_int_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/string_interpolation_invalid_fmt.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/string_interpolation_invalid_fmt.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/string_interpolation_wrong_fmt.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/string_interpolation_wrong_fmt.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/struct_assigned_to_pointer_to_struct.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/struct_assigned_to_pointer_to_struct.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_generic_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_generic_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_mut_err_a.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_mut_err_a.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_mut_err_b.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_mut_err_b.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_pub_err_a.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_pub_err_a.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_pub_err_b.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_pub_err_b.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/struct_embed_invalid_type.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/struct_embed_invalid_type.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/struct_field_name_duplicate_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/struct_field_name_duplicate_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/struct_field_type_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/struct_field_type_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/struct_init_update_type_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/struct_init_update_type_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/struct_pub_field.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/struct_pub_field.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/struct_required_field.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/struct_required_field.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/struct_required_fn_field.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/struct_required_fn_field.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/struct_short_init_warning.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/struct_short_init_warning.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/struct_type_cast_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/struct_type_cast_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/struct_unknown_field.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/struct_unknown_field.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/struct_unneeded_default.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/struct_unneeded_default.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/sum.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/sum.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/sum_type_assign_non_variant_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/sum_type_assign_non_variant_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/sum_type_common_fields_alias_error.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/sum_type_common_fields_alias_error.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/sum_type_common_fields_error.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/sum_type_common_fields_error.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/sum_type_exists.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/sum_type_exists.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/sum_type_infix_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/sum_type_infix_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/sum_type_multiple_type_define.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/sum_type_multiple_type_define.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/sum_type_mutable_cast_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/sum_type_mutable_cast_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/sum_type_ref_variant_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/sum_type_ref_variant_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/sumtype_in_sumtype_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/sumtype_in_sumtype_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/sumtype_mismatched_type.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/sumtype_mismatched_type.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/templates/index.html create mode 100644 v_windows/v/old/vlib/v/checker/tests/test_functions_should_not_return_test.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/test_functions_should_not_return_test.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/trailing_comma_struct_attr.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/trailing_comma_struct_attr.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/type_cast_optional_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/type_cast_optional_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/typedef_attr_v_struct_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/typedef_attr_v_struct_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/undefined_ident_of_struct.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/undefined_ident_of_struct.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/unexpected_or.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/unexpected_or.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/unexpected_or_propagate.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/unexpected_or_propagate.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/unfinished_string.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/unfinished_string.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_a.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_a.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_b.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_b.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_c.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_c.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_d.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_d.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_e.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_e.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_f.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_f.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_g.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_g.vv.disabled create mode 100644 v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_h.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_h.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_i.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_i.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_j.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_j.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/union_unsafe_fields.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/union_unsafe_fields.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/unknown_array_element_type_b.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/unknown_array_element_type_b.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/unknown_as_type.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/unknown_as_type.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/unknown_comptime_expr.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/unknown_comptime_expr.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/unknown_field.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/unknown_field.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/unknown_generic_type.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/unknown_generic_type.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/unknown_method.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/unknown_method.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/unknown_method_suggest_name.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/unknown_method_suggest_name.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/unknown_sizeof_type_err_a.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/unknown_sizeof_type_err_a.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/unknown_sizeof_type_err_b.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/unknown_sizeof_type_err_b.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/unknown_struct_field_suggest_name.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/unknown_struct_field_suggest_name.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/unknown_struct_name.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/unknown_struct_name.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/unknown_var_assign.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/unknown_var_assign.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/unnecessary_parenthesis.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/unnecessary_parenthesis.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/unreachable_code.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/unreachable_code.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/unsafe_c_calls_should_be_checked.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/unsafe_c_calls_should_be_checked.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/unsafe_fixed_array_assign.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/unsafe_fixed_array_assign.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/unsafe_required.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/unsafe_required.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/unwrapped_optional_infix.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/unwrapped_optional_infix.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/use_deprecated_function_warning.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/use_deprecated_function_warning.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/var_duplicate_const.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/var_duplicate_const.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/var_eval_not_used.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/var_eval_not_used.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/var_eval_not_used_scope.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/var_eval_not_used_scope.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/var_used_before_declaration.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/var_used_before_declaration.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/void_fn_as_value.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/void_fn_as_value.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/void_function_assign_to_string.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/void_function_assign_to_string.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/void_optional_err.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/void_optional_err.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/vweb_routing_checks.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/vweb_routing_checks.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/vweb_tmpl_used_var.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/vweb_tmpl_used_var.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/warnings_for_string_c2v_calls.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/warnings_for_string_c2v_calls.vv create mode 100644 v_windows/v/old/vlib/v/checker/tests/wrong_propagate_ret_type.out create mode 100644 v_windows/v/old/vlib/v/checker/tests/wrong_propagate_ret_type.vv create mode 100644 v_windows/v/old/vlib/v/compiler_errors_test.v create mode 100644 v_windows/v/old/vlib/v/depgraph/depgraph.v create mode 100644 v_windows/v/old/vlib/v/doc/comment.v create mode 100644 v_windows/v/old/vlib/v/doc/doc.v create mode 100644 v_windows/v/old/vlib/v/doc/doc_test.v create mode 100644 v_windows/v/old/vlib/v/doc/module.v create mode 100644 v_windows/v/old/vlib/v/doc/node.v create mode 100644 v_windows/v/old/vlib/v/doc/utils.v create mode 100644 v_windows/v/old/vlib/v/dotgraph/dotgraph.c.v create mode 100644 v_windows/v/old/vlib/v/dotgraph/dotgraph.v create mode 100644 v_windows/v/old/vlib/v/embed_file/embed_file.v create mode 100644 v_windows/v/old/vlib/v/embed_file/embed_file_test.v create mode 100644 v_windows/v/old/vlib/v/embed_file/v.png create mode 100644 v_windows/v/old/vlib/v/errors/errors.v create mode 100644 v_windows/v/old/vlib/v/eval/eval.v create mode 100644 v_windows/v/old/vlib/v/fmt/align.v create mode 100644 v_windows/v/old/vlib/v/fmt/asm.v create mode 100644 v_windows/v/old/vlib/v/fmt/attrs.v create mode 100644 v_windows/v/old/vlib/v/fmt/comments.v create mode 100644 v_windows/v/old/vlib/v/fmt/fmt.v create mode 100644 v_windows/v/old/vlib/v/fmt/fmt_keep_test.v create mode 100644 v_windows/v/old/vlib/v/fmt/fmt_test.v create mode 100644 v_windows/v/old/vlib/v/fmt/fmt_vlib_test.v create mode 100644 v_windows/v/old/vlib/v/fmt/struct.v create mode 100644 v_windows/v/old/vlib/v/fmt/tests/anon_fn_as_param_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/anon_fn_call_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/anon_fn_expected.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/anon_fn_input.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/array_decomposition_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/array_init_comment_ending_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/array_init_eol_comments_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/array_init_expected.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/array_init_inline_comments_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/array_init_input.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/array_init_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/array_newlines_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/array_slices_expected.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/array_slices_input.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/array_static_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/assembly/asm_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/asserts_expected.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/asserts_input.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/asserts_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/attrs_expected.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/attrs_input.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/attrs_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/bin2v_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/blocks_expected.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/blocks_input.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/c_struct_init_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/cast_expected.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/cast_input.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/chan_init_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/chan_ops_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/chan_or_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/char_literal_backtick_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/comments_array_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/comments_expected.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/comments_input.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/comments_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/comptime_field_selector_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/comptime_field_selector_parentheses_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/comptime_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/concat_expr_expected.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/concat_expr_input.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/conditional_compilation_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/conditions_expected.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/conditions_input.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/consts_expected.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/consts_input.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/consts_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/consts_with_comments_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/embed_file_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/empty_curlies_and_parens_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/empty_lines_expected.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/empty_lines_input.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/empty_lines_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/enum_comments_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/enums_expected.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/enums_input.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/expressions_expected.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/expressions_input.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/file_with_just_imports_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/fixed_size_array_type_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/fn_headers_with_no_bodies_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/fn_multi_return_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/fn_return_generic_struct_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/fn_trailing_arg_syntax_expected.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/fn_trailing_arg_syntax_input.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/fn_trailing_arg_syntax_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/fn_with_anon_params_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/fntype_alias_array_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/fntype_alias_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/fntype_mut_args_with_optional_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/fntype_return_optional_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/functions_expected.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/functions_input.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/generic_recursive_structs_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/generic_structs_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/generics_cascade_types_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/generics_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/global_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/go_stmt_expected.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/go_stmt_input.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/go_stmt_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/goto_expected.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/goto_input.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/hashstmt_expected.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/hashstmt_input.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/hashstmt_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/if_brace_on_newline_expected.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/if_brace_on_newline_input.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/if_expected.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/if_input.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/if_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/if_ternary_expected.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/if_ternary_input.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/if_ternary_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/import_auto_added_expected.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/import_auto_added_input.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/import_comments_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/import_duplicate_expected.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/import_duplicate_input.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/import_multiple_with_alias_expected.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/import_multiple_with_alias_input.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/import_selective_expected.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/import_selective_input.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/import_selective_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/import_single_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/import_with_alias_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/infix_expr_expected.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/infix_expr_input.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/infix_expr_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/integer_literal_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/interface_declaration_comments_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/interface_variadic_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/interface_with_mut_fields_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/labelled_break_continue_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/language_prefixes_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/loops_expected.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/loops_input.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/manualfree_keep.v create mode 100644 v_windows/v/old/vlib/v/fmt/tests/maps_expected.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/maps_in_fn_args__keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/maps_input.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/maps_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/maps_of_fns_with_string_keys_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/match_expected.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/match_input.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/match_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/match_range_expression_branches_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/match_with_commented_branches_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/missing_import_expected.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/missing_import_input.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/module_alias_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/module_interface_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/module_struct_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/multi_generic_test_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/multiline_comment_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/nested_map_type_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/newlines_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/no_main_expected.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/no_main_input.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/offset_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/operator_overload_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/optional_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/optional_propagate_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/or_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/orm_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/par_expr_expected.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/par_expr_input.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/pointer_casts_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/proto_module_importing_vproto_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/ref_type_cast_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/select_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/shared_expected.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/shared_input.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/star__amp_int__cast_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/static_mut_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/stmt_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/string_interpolation_complex_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/string_interpolation_expected.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/string_interpolation_input.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/string_interpolation_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/string_quotes_expected.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/string_quotes_input.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/string_raw_and_cstr_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/struct_decl_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/struct_default_field_expressions_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/struct_embed_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/struct_init_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/struct_init_with_comments_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/struct_init_with_custom_len_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/struct_init_with_ref_cast_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/struct_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/struct_no_extra_attr_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/struct_update_comment_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/struct_update_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/struct_with_fn_fields_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/structs_expected.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/structs_input.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/sum_smartcast_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/thread_in_a_module_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/to_string_2_forms_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/trailing_space_expected.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/trailing_space_input.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/type_ptr_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/typeof_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/types_expected.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/types_input.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/union_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/unsafe_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/void_optional_keep.vv create mode 100644 v_windows/v/old/vlib/v/fmt/tests/vscript_keep.vv create mode 100644 v_windows/v/old/vlib/v/gen/c/array.v create mode 100644 v_windows/v/old/vlib/v/gen/c/assert.v create mode 100644 v_windows/v/old/vlib/v/gen/c/auto_eq_methods.v create mode 100644 v_windows/v/old/vlib/v/gen/c/auto_str_methods.v create mode 100644 v_windows/v/old/vlib/v/gen/c/cgen.v create mode 100644 v_windows/v/old/vlib/v/gen/c/cheaders.v create mode 100644 v_windows/v/old/vlib/v/gen/c/cmain.v create mode 100644 v_windows/v/old/vlib/v/gen/c/comptime.v create mode 100644 v_windows/v/old/vlib/v/gen/c/coutput_test.v create mode 100644 v_windows/v/old/vlib/v/gen/c/ctempvars.v create mode 100644 v_windows/v/old/vlib/v/gen/c/dumpexpr.v create mode 100644 v_windows/v/old/vlib/v/gen/c/embed.v create mode 100644 v_windows/v/old/vlib/v/gen/c/fn.v create mode 100644 v_windows/v/old/vlib/v/gen/c/index.v create mode 100644 v_windows/v/old/vlib/v/gen/c/infix_expr.v create mode 100644 v_windows/v/old/vlib/v/gen/c/json.v create mode 100644 v_windows/v/old/vlib/v/gen/c/live.v create mode 100644 v_windows/v/old/vlib/v/gen/c/profile.v create mode 100644 v_windows/v/old/vlib/v/gen/c/sql.v create mode 100644 v_windows/v/old/vlib/v/gen/c/str.v create mode 100644 v_windows/v/old/vlib/v/gen/c/str_intp.v create mode 100644 v_windows/v/old/vlib/v/gen/c/testdata/const_references.c.must_have create mode 100644 v_windows/v/old/vlib/v/gen/c/testdata/const_references.out create mode 100644 v_windows/v/old/vlib/v/gen/c/testdata/const_references.vv create mode 100644 v_windows/v/old/vlib/v/gen/c/utils.v create mode 100644 v_windows/v/old/vlib/v/gen/js/builtin_types.v create mode 100644 v_windows/v/old/vlib/v/gen/js/comptime.v create mode 100644 v_windows/v/old/vlib/v/gen/js/fast_deep_equal.js create mode 100644 v_windows/v/old/vlib/v/gen/js/js.v create mode 100644 v_windows/v/old/vlib/v/gen/js/jsdoc.v create mode 100644 v_windows/v/old/vlib/v/gen/js/jsgen_test.v create mode 100644 v_windows/v/old/vlib/v/gen/js/program_test.v create mode 100644 v_windows/v/old/vlib/v/gen/js/sourcemap/basic_test.v create mode 100644 v_windows/v/old/vlib/v/gen/js/sourcemap/compare_test.v create mode 100644 v_windows/v/old/vlib/v/gen/js/sourcemap/mappings.v create mode 100644 v_windows/v/old/vlib/v/gen/js/sourcemap/sets.v create mode 100644 v_windows/v/old/vlib/v/gen/js/sourcemap/source_map.v create mode 100644 v_windows/v/old/vlib/v/gen/js/sourcemap/source_map_generator.v create mode 100644 v_windows/v/old/vlib/v/gen/js/sourcemap/vlq/vlq.v create mode 100644 v_windows/v/old/vlib/v/gen/js/sourcemap/vlq/vlq_decode_test.v create mode 100644 v_windows/v/old/vlib/v/gen/js/sourcemap/vlq/vlq_encode_test.v create mode 100644 v_windows/v/old/vlib/v/gen/js/temp_fast_deep_equal.v create mode 100644 v_windows/v/old/vlib/v/gen/js/tests/.gitignore create mode 100644 v_windows/v/old/vlib/v/gen/js/tests/array.v create mode 100644 v_windows/v/old/vlib/v/gen/js/tests/auto_deref_args.v create mode 100644 v_windows/v/old/vlib/v/gen/js/tests/enum.v create mode 100644 v_windows/v/old/vlib/v/gen/js/tests/hello/hello.js.v create mode 100644 v_windows/v/old/vlib/v/gen/js/tests/hello/hello.v create mode 100644 v_windows/v/old/vlib/v/gen/js/tests/hello/hello1/hello1.v create mode 100644 v_windows/v/old/vlib/v/gen/js/tests/interface.v create mode 100644 v_windows/v/old/vlib/v/gen/js/tests/interp.v create mode 100644 v_windows/v/old/vlib/v/gen/js/tests/js.v create mode 100644 v_windows/v/old/vlib/v/gen/js/tests/life.v create mode 100644 v_windows/v/old/vlib/v/gen/js/tests/optional.v create mode 100644 v_windows/v/old/vlib/v/gen/js/tests/simple.v create mode 100644 v_windows/v/old/vlib/v/gen/js/tests/simple_sourcemap.v create mode 100644 v_windows/v/old/vlib/v/gen/js/tests/struct.v create mode 100644 v_windows/v/old/vlib/v/gen/js/tests/testdata/array.out create mode 100644 v_windows/v/old/vlib/v/gen/js/tests/testdata/array.v create mode 100644 v_windows/v/old/vlib/v/gen/js/tests/testdata/byte_is_space.out create mode 100644 v_windows/v/old/vlib/v/gen/js/tests/testdata/byte_is_space.v create mode 100644 v_windows/v/old/vlib/v/gen/js/tests/testdata/compare_ints.out create mode 100644 v_windows/v/old/vlib/v/gen/js/tests/testdata/compare_ints.v create mode 100644 v_windows/v/old/vlib/v/gen/js/tests/testdata/hw.out create mode 100644 v_windows/v/old/vlib/v/gen/js/tests/testdata/hw.v create mode 100644 v_windows/v/old/vlib/v/gen/js/tests/testdata/string_methods.out create mode 100644 v_windows/v/old/vlib/v/gen/js/tests/testdata/string_methods.v create mode 100644 v_windows/v/old/vlib/v/gen/native/amd64.v create mode 100644 v_windows/v/old/vlib/v/gen/native/arm64.v create mode 100644 v_windows/v/old/vlib/v/gen/native/elf.v create mode 100644 v_windows/v/old/vlib/v/gen/native/elf_obj.v create mode 100644 v_windows/v/old/vlib/v/gen/native/gen.v create mode 100644 v_windows/v/old/vlib/v/gen/native/macho.v create mode 100644 v_windows/v/old/vlib/v/gen/native/macho_test.v create mode 100644 v_windows/v/old/vlib/v/gen/native/tests/expressions.vv create mode 100644 v_windows/v/old/vlib/v/gen/native/tests/expressions.vv.out create mode 100644 v_windows/v/old/vlib/v/gen/native/tests/general.vv create mode 100644 v_windows/v/old/vlib/v/gen/native/tests/general.vv.out create mode 100644 v_windows/v/old/vlib/v/gen/native/tests/hello.vv create mode 100644 v_windows/v/old/vlib/v/gen/native/tests/hello.vv.out create mode 100644 v_windows/v/old/vlib/v/gen/native/tests/ifs.vv create mode 100644 v_windows/v/old/vlib/v/gen/native/tests/ifs.vv.out create mode 100644 v_windows/v/old/vlib/v/gen/native/tests/native_test.v create mode 100644 v_windows/v/old/vlib/v/gen/native/tests/simple_fn_calls.vv create mode 100644 v_windows/v/old/vlib/v/gen/native/tests/simple_fn_calls.vv.out create mode 100644 v_windows/v/old/vlib/v/live/common.v create mode 100644 v_windows/v/old/vlib/v/live/executable/reloader.v create mode 100644 v_windows/v/old/vlib/v/live/live_test.v create mode 100644 v_windows/v/old/vlib/v/live/live_test_template.vv create mode 100644 v_windows/v/old/vlib/v/live/sharedlib/live_sharedlib.v create mode 100644 v_windows/v/old/vlib/v/markused/markused.v create mode 100644 v_windows/v/old/vlib/v/markused/walker.v create mode 100644 v_windows/v/old/vlib/v/parser/assign.v create mode 100644 v_windows/v/old/vlib/v/parser/comptime.v create mode 100644 v_windows/v/old/vlib/v/parser/containers.v create mode 100644 v_windows/v/old/vlib/v/parser/expr.v create mode 100644 v_windows/v/old/vlib/v/parser/fn.v create mode 100644 v_windows/v/old/vlib/v/parser/for.v create mode 100644 v_windows/v/old/vlib/v/parser/if_match.v create mode 100644 v_windows/v/old/vlib/v/parser/lock.v create mode 100644 v_windows/v/old/vlib/v/parser/module.v create mode 100644 v_windows/v/old/vlib/v/parser/parse_type.v create mode 100644 v_windows/v/old/vlib/v/parser/parser.v create mode 100644 v_windows/v/old/vlib/v/parser/sql.v create mode 100644 v_windows/v/old/vlib/v/parser/struct.v create mode 100644 v_windows/v/old/vlib/v/parser/tests/README.md create mode 100644 v_windows/v/old/vlib/v/parser/tests/anon_fn_return_type.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/anon_fn_return_type.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/anon_unused_param.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/anon_unused_param.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/array_init.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/array_init.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/array_pos_err.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/array_pos_err.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/c_struct_no_embed.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/c_struct_no_embed.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/const_index.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/const_index.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/const_missing_rpar_a.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/const_missing_rpar_a.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/const_missing_rpar_b.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/const_missing_rpar_b.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/const_only_keyword.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/const_only_keyword.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/const_unexpected_eof.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/const_unexpected_eof.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/dec_use_as_value.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/dec_use_as_value.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/defer_propagate.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/defer_propagate.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/defer_return.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/defer_return.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/defer_return2.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/defer_return2.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/duplicate_field_embed_err.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/duplicate_field_embed_err.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/duplicate_type_a.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/duplicate_type_a.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/duplicate_type_b.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/duplicate_type_b.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/duplicated_generic_err.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/duplicated_generic_err.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/empty_name_expr_err.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/empty_name_expr_err.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/expected_type_enum_err.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/expected_type_enum_err.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/expecting_assign_type_alias.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/expecting_assign_type_alias.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_a.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_a.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_b.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_b.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_c.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_c.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_d.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_d.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_e.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_e.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_if.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_if.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_or.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_or.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/fn_attributes_duplicate_multiple.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/fn_attributes_duplicate_multiple.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/fn_attributes_duplicate_single.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/fn_attributes_duplicate_single.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/fn_attributes_empty_err.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/fn_attributes_empty_err.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/fn_decl_unexpected_eof.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/fn_decl_unexpected_eof.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/fn_type_only_args_in_interfaces.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/fn_type_only_args_in_interfaces.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/fn_type_only_args_no_body.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/fn_type_only_args_no_body.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/fn_type_only_args_unknown_name.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/fn_type_only_args_unknown_name.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/fn_use_builtin_err.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/fn_use_builtin_err.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/for.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/for.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/for_in_mut_index_of_array.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/for_in_mut_index_of_array.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/for_in_mut_key_of_map.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/for_in_mut_key_of_map.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/function_variadic_arg_non_final.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/function_variadic_arg_non_final.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/generic_lowercase_err.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/generic_lowercase_err.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/generic_type_alias_decl.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/generic_type_alias_decl.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/if_guard_redefinition.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/if_guard_redefinition.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/inc_use_as_value.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/inc_use_as_value.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/interface_duplicate_interface_method.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/interface_duplicate_interface_method.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/interface_duplicate_method.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/interface_duplicate_method.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/invalid_attribute_a.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/invalid_attribute_a.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/invalid_attribute_b.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/invalid_attribute_b.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/invalid_attribute_c.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/invalid_attribute_c.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/invalid_attribute_d.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/invalid_attribute_d.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/invalid_fn_decl_script_err.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/invalid_fn_decl_script_err.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/invalid_recursive_struct1_err.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/invalid_recursive_struct1_err.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/invalid_recursive_struct2_err.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/invalid_recursive_struct2_err.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/long_generic_err.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/long_generic_err.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/map_init.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/map_init.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/map_init_void.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/map_init_void.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/map_init_void2.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/map_init_void2.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/match_range_dotdot_err.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/match_range_dotdot_err.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/method_decl_on_non_local_array.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/method_decl_on_non_local_array.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/method_decl_on_non_local_map.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/method_decl_on_non_local_map.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/method_decl_on_non_local_type.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/method_decl_on_non_local_type.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/module_multiple_names_err.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/module_multiple_names_err.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/module_syntax_err.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/module_syntax_err.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/multi_argumented_assign_err.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/multi_argumented_assign_err.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/nested_defer.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/nested_defer.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/nested_unsafe_expr.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/nested_unsafe_expr.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/nested_unsafe_stmt.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/nested_unsafe_stmt.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/operator_normal_fn.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/operator_normal_fn.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/or_default_missing.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/or_default_missing.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/postfix_err.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/postfix_err.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/postfix_inc.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/postfix_inc.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/prefix_first.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/prefix_first.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/prohibit_redeclaration_of_builtin_types.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/prohibit_redeclaration_of_builtin_types.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/redeclaration_of_imported_fn.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/redeclaration_of_imported_fn.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/register_imported_alias.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/register_imported_alias.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/register_imported_enum.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/register_imported_enum.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/register_imported_interface.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/register_imported_interface.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/register_imported_struct.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/register_imported_struct.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/select_bad_key_1.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/select_bad_key_1.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/select_bad_key_2.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/select_bad_key_2.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/select_bad_key_3.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/select_bad_key_3.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/select_bad_key_4.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/select_bad_key_4.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/select_else_1.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/select_else_1.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/select_else_2.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/select_else_2.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/sql_no_db_expr_a.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/sql_no_db_expr_a.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/sql_no_db_expr_b.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/sql_no_db_expr_b.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/string_invalid_prefix_err.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/string_invalid_prefix_err.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/struct_embed_duplicate.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/struct_embed_duplicate.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/struct_embed_unknown_module.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/struct_embed_unknown_module.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/struct_embed_wrong_pos_long_err.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/struct_embed_wrong_pos_long_err.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/struct_embed_wrong_pos_short_err.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/struct_embed_wrong_pos_short_err.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/struct_field_expected.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/struct_field_expected.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/struct_field_unknown_module_a.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/struct_field_unknown_module_a.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/struct_field_unknown_module_b.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/struct_field_unknown_module_b.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/struct_field_unknown_module_c.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/struct_field_unknown_module_c.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/struct_module_section.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/struct_module_section.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/struct_update_err.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/struct_update_err.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/too_many_generics_err.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/too_many_generics_err.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/type_alias_existing_type_err.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/type_alias_existing_type_err.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/type_alias_same_type_err.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/type_alias_same_type_err.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/uncomplete_module_call_err.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/uncomplete_module_call_err.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/unexpected_expr.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/unexpected_expr.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/unexpected_keyword.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/unexpected_keyword.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/unnecessary_mut.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/unnecessary_mut.vv create mode 100644 v_windows/v/old/vlib/v/parser/tests/unnecessary_mut_2.out create mode 100644 v_windows/v/old/vlib/v/parser/tests/unnecessary_mut_2.vv create mode 100644 v_windows/v/old/vlib/v/parser/tmpl.v create mode 100644 v_windows/v/old/vlib/v/parser/v_parser_test.v create mode 100644 v_windows/v/old/vlib/v/pkgconfig/README.md create mode 100644 v_windows/v/old/vlib/v/pkgconfig/bin/pkgconfig.v create mode 100644 v_windows/v/old/vlib/v/pkgconfig/main.v create mode 100644 v_windows/v/old/vlib/v/pkgconfig/pkgconfig.v create mode 100644 v_windows/v/old/vlib/v/pkgconfig/pkgconfig_test.v create mode 100644 v_windows/v/old/vlib/v/pkgconfig/test_samples/alsa.pc create mode 100644 v_windows/v/old/vlib/v/pkgconfig/test_samples/atk.pc create mode 100644 v_windows/v/old/vlib/v/pkgconfig/test_samples/autoopts.pc create mode 100644 v_windows/v/old/vlib/v/pkgconfig/test_samples/dep-resolution-fail.pc create mode 100644 v_windows/v/old/vlib/v/pkgconfig/test_samples/expat.pc create mode 100644 v_windows/v/old/vlib/v/pkgconfig/test_samples/form.pc create mode 100644 v_windows/v/old/vlib/v/pkgconfig/test_samples/gio-2.0.pc create mode 100644 v_windows/v/old/vlib/v/pkgconfig/test_samples/gio-unix-2.0.pc create mode 100644 v_windows/v/old/vlib/v/pkgconfig/test_samples/glib-2.0.pc create mode 100644 v_windows/v/old/vlib/v/pkgconfig/test_samples/gmodule-2.0.pc create mode 100644 v_windows/v/old/vlib/v/pkgconfig/test_samples/gmodule-no-export-2.0.pc create mode 100644 v_windows/v/old/vlib/v/pkgconfig/test_samples/gobject-2.0.pc create mode 100644 v_windows/v/old/vlib/v/pkgconfig/test_samples/libffi.pc create mode 100644 v_windows/v/old/vlib/v/pkgconfig/test_samples/libpcre.pc create mode 100644 v_windows/v/old/vlib/v/pkgconfig/test_samples/ncurses.pc create mode 100644 v_windows/v/old/vlib/v/pkgconfig/test_samples/sdl2.pc create mode 100644 v_windows/v/old/vlib/v/pkgconfig/test_samples/zlib.pc create mode 100644 v_windows/v/old/vlib/v/pkgconfig/v.mod create mode 100644 v_windows/v/old/vlib/v/pref/default.v create mode 100644 v_windows/v/old/vlib/v/pref/os.v create mode 100644 v_windows/v/old/vlib/v/pref/pref.v create mode 100644 v_windows/v/old/vlib/v/pref/should_compile.v create mode 100644 v_windows/v/old/vlib/v/preludes/README.md create mode 100644 v_windows/v/old/vlib/v/preludes/live.v create mode 100644 v_windows/v/old/vlib/v/preludes/live_main.v create mode 100644 v_windows/v/old/vlib/v/preludes/live_shared.v create mode 100644 v_windows/v/old/vlib/v/preludes/profiled_program.v create mode 100644 v_windows/v/old/vlib/v/preludes/tests_assertions.v create mode 100644 v_windows/v/old/vlib/v/preludes/tests_with_stats.v create mode 100644 v_windows/v/old/vlib/v/preludes_js/tests_assertions.v create mode 100644 v_windows/v/old/vlib/v/preludes_js/tests_with_stats.v create mode 100644 v_windows/v/old/vlib/v/scanner/scanner.v create mode 100644 v_windows/v/old/vlib/v/scanner/scanner_test.v create mode 100644 v_windows/v/old/vlib/v/scanner/tests/bin_consecutively_separator_err.out create mode 100644 v_windows/v/old/vlib/v/scanner/tests/bin_consecutively_separator_err.vv create mode 100644 v_windows/v/old/vlib/v/scanner/tests/bin_separator_in_front_err.out create mode 100644 v_windows/v/old/vlib/v/scanner/tests/bin_separator_in_front_err.vv create mode 100644 v_windows/v/old/vlib/v/scanner/tests/dec_consecutively_separator_err.out create mode 100644 v_windows/v/old/vlib/v/scanner/tests/dec_consecutively_separator_err.vv create mode 100644 v_windows/v/old/vlib/v/scanner/tests/hex_consecutively_separator_err.out create mode 100644 v_windows/v/old/vlib/v/scanner/tests/hex_consecutively_separator_err.vv create mode 100644 v_windows/v/old/vlib/v/scanner/tests/hex_separator_in_front_err.out create mode 100644 v_windows/v/old/vlib/v/scanner/tests/hex_separator_in_front_err.vv create mode 100644 v_windows/v/old/vlib/v/scanner/tests/oct_consecutively_separator_err.out create mode 100644 v_windows/v/old/vlib/v/scanner/tests/oct_consecutively_separator_err.vv create mode 100644 v_windows/v/old/vlib/v/scanner/tests/oct_separator_in_front_err.out create mode 100644 v_windows/v/old/vlib/v/scanner/tests/oct_separator_in_front_err.vv create mode 100644 v_windows/v/old/vlib/v/scanner/tests/position_0_err.out create mode 100644 v_windows/v/old/vlib/v/scanner/tests/position_0_err.vv create mode 100644 v_windows/v/old/vlib/v/tests/alias_array_operator_overloading_test.v create mode 100644 v_windows/v/old/vlib/v/tests/alias_custom_type_map_test.v create mode 100644 v_windows/v/old/vlib/v/tests/alias_fixed_array_init_test.v create mode 100644 v_windows/v/old/vlib/v/tests/alias_fixed_array_test.v create mode 100644 v_windows/v/old/vlib/v/tests/alias_in_a_struct_field_autostr_test.v create mode 100644 v_windows/v/old/vlib/v/tests/alias_map_operator_overloading_test.v create mode 100644 v_windows/v/old/vlib/v/tests/aliased_array_operations_test.v create mode 100644 v_windows/v/old/vlib/v/tests/anon_fn_call_test.v create mode 100644 v_windows/v/old/vlib/v/tests/anon_fn_in_containers_test.v create mode 100644 v_windows/v/old/vlib/v/tests/anon_fn_redefinition_test.v create mode 100644 v_windows/v/old/vlib/v/tests/anon_fn_returning_question_test.v create mode 100644 v_windows/v/old/vlib/v/tests/anon_fn_test.v create mode 100644 v_windows/v/old/vlib/v/tests/anon_fn_with_array_arguments_test.v create mode 100644 v_windows/v/old/vlib/v/tests/anon_fn_with_optional_test.v create mode 100644 v_windows/v/old/vlib/v/tests/appending_to_mut_array_in_fn_param_test.v create mode 100644 v_windows/v/old/vlib/v/tests/array_append_short_struct_test.v create mode 100644 v_windows/v/old/vlib/v/tests/array_cast_test.v create mode 100644 v_windows/v/old/vlib/v/tests/array_equality_test.v create mode 100644 v_windows/v/old/vlib/v/tests/array_init_test.v create mode 100644 v_windows/v/old/vlib/v/tests/array_map_or_test.v create mode 100644 v_windows/v/old/vlib/v/tests/array_map_ref_test.v create mode 100644 v_windows/v/old/vlib/v/tests/array_methods_test.v create mode 100644 v_windows/v/old/vlib/v/tests/array_of_alias_slice_test.v create mode 100644 v_windows/v/old/vlib/v/tests/array_slice_test.v create mode 100644 v_windows/v/old/vlib/v/tests/array_sort_lt_overload_test.v create mode 100644 v_windows/v/old/vlib/v/tests/array_test.v create mode 100644 v_windows/v/old/vlib/v/tests/array_to_string_test.v create mode 100644 v_windows/v/old/vlib/v/tests/array_type_alias_test.v create mode 100644 v_windows/v/old/vlib/v/tests/as_cast_already_smartcast_sumtype_test.v create mode 100644 v_windows/v/old/vlib/v/tests/as_cast_is_expr_sumtype_fn_result_test.v create mode 100644 v_windows/v/old/vlib/v/tests/assembly/asm_test.amd64.v create mode 100644 v_windows/v/old/vlib/v/tests/assembly/asm_test.i386.v create mode 100644 v_windows/v/old/vlib/v/tests/assembly/naked_attr_test.amd64.v create mode 100644 v_windows/v/old/vlib/v/tests/assembly/naked_attr_test.i386.v create mode 100644 v_windows/v/old/vlib/v/tests/assembly/util/dot_amd64_util.amd64.v create mode 100644 v_windows/v/old/vlib/v/tests/assert_sumtype_test.v create mode 100644 v_windows/v/old/vlib/v/tests/assert_with_newlines_test.v create mode 100644 v_windows/v/old/vlib/v/tests/assign_bitops_with_type_aliases_test.v create mode 100644 v_windows/v/old/vlib/v/tests/assign_map_value_of_fixed_array_test.v create mode 100644 v_windows/v/old/vlib/v/tests/attribute_test.v create mode 100644 v_windows/v/old/vlib/v/tests/autolock_array1_test.v create mode 100644 v_windows/v/old/vlib/v/tests/autolock_array2_test.v create mode 100644 v_windows/v/old/vlib/v/tests/backtrace_test.v create mode 100644 v_windows/v/old/vlib/v/tests/bench/gcboehm/GC_Ryzen_3800X_Linux.pdf create mode 100644 v_windows/v/old/vlib/v/tests/bench/gcboehm/GC_Ryzen_3800X_Linux.svg create mode 100644 v_windows/v/old/vlib/v/tests/bench/gcboehm/GC_bench.plt create mode 100644 v_windows/v/old/vlib/v/tests/bench/gcboehm/GC_bench.v create mode 100644 v_windows/v/old/vlib/v/tests/bench/gcboehm/GC_bench_full.plt create mode 100644 v_windows/v/old/vlib/v/tests/bench/gcboehm/GC_bench_incr.plt create mode 100644 v_windows/v/old/vlib/v/tests/bench/gcboehm/GC_bench_non_opt.plt create mode 100644 v_windows/v/old/vlib/v/tests/bench/gcboehm/GC_bench_opt.plt create mode 100644 v_windows/v/old/vlib/v/tests/bench/gcboehm/Makefile create mode 100644 v_windows/v/old/vlib/v/tests/bench/gcboehm/Resources.plt create mode 100644 v_windows/v/old/vlib/v/tests/bench/gcboehm/Resources_Ryzen_3800X_Linux.pdf create mode 100644 v_windows/v/old/vlib/v/tests/bench/gcboehm/Resources_Ryzen_3800X_Linux.svg create mode 100644 v_windows/v/old/vlib/v/tests/bench/gcboehm/resources.txt create mode 100644 v_windows/v/old/vlib/v/tests/bench/val_vs_ptr.c create mode 100644 v_windows/v/old/vlib/v/tests/blank_ident_test.v create mode 100644 v_windows/v/old/vlib/v/tests/break_in_lock_test.v create mode 100644 v_windows/v/old/vlib/v/tests/c_struct_free/c_struct_free_property_test.v create mode 100644 v_windows/v/old/vlib/v/tests/c_struct_free/free_struct.c create mode 100644 v_windows/v/old/vlib/v/tests/calling_module_functions_with_maps_of_arrays_test.v create mode 100644 v_windows/v/old/vlib/v/tests/cast_to_byte_test.v create mode 100644 v_windows/v/old/vlib/v/tests/cast_to_interface_test.v create mode 100644 v_windows/v/old/vlib/v/tests/cflags/includes/myinclude.h create mode 100644 v_windows/v/old/vlib/v/tests/cflags/v.mod create mode 100644 v_windows/v/old/vlib/v/tests/cflags/vmodroot_and_vroot_test.v create mode 100644 v_windows/v/old/vlib/v/tests/channels_test.v create mode 100644 v_windows/v/old/vlib/v/tests/clash_var_name_of_array_and_map_test.v create mode 100644 v_windows/v/old/vlib/v/tests/complex_assign_test.v create mode 100644 v_windows/v/old/vlib/v/tests/comptime_at_test.v create mode 100644 v_windows/v/old/vlib/v/tests/comptime_attribute_selector_test.v create mode 100644 v_windows/v/old/vlib/v/tests/comptime_bittness_and_endianess_test.v create mode 100644 v_windows/v/old/vlib/v/tests/comptime_call_test.v create mode 100644 v_windows/v/old/vlib/v/tests/comptime_field_selector_test.v create mode 100644 v_windows/v/old/vlib/v/tests/comptime_for_test.v create mode 100644 v_windows/v/old/vlib/v/tests/comptime_if_expr_test.v create mode 100644 v_windows/v/old/vlib/v/tests/comptime_if_is_test.v create mode 100644 v_windows/v/old/vlib/v/tests/comptime_if_pkgconfig_test.v create mode 100644 v_windows/v/old/vlib/v/tests/comptime_if_test.v create mode 100644 v_windows/v/old/vlib/v/tests/comptime_if_threads_no_test.v create mode 100644 v_windows/v/old/vlib/v/tests/comptime_if_threads_yes_test.v create mode 100644 v_windows/v/old/vlib/v/tests/comptime_method_args_test.v create mode 100644 v_windows/v/old/vlib/v/tests/const_can_use_optionals_test.v create mode 100644 v_windows/v/old/vlib/v/tests/const_comptime_eval_before_vinit_test.v create mode 100644 v_windows/v/old/vlib/v/tests/const_embed_test.v create mode 100644 v_windows/v/old/vlib/v/tests/const_eval_simple_int_expressions_at_comptime_test.v create mode 100644 v_windows/v/old/vlib/v/tests/const_init_order_test.v create mode 100644 v_windows/v/old/vlib/v/tests/const_reference_argument_test.v create mode 100644 v_windows/v/old/vlib/v/tests/const_representation_test.v create mode 100644 v_windows/v/old/vlib/v/tests/const_test.v create mode 100644 v_windows/v/old/vlib/v/tests/conversions_test.v create mode 100644 v_windows/v/old/vlib/v/tests/cross_assign_test.v create mode 100644 v_windows/v/old/vlib/v/tests/cstrings_test.v create mode 100644 v_windows/v/old/vlib/v/tests/defer_return_test.v create mode 100644 v_windows/v/old/vlib/v/tests/defer_test.v create mode 100644 v_windows/v/old/vlib/v/tests/differently_named_structs_test.v create mode 100644 v_windows/v/old/vlib/v/tests/double_ref_deref_test.v create mode 100644 v_windows/v/old/vlib/v/tests/dump_fns_test.v create mode 100644 v_windows/v/old/vlib/v/tests/enum_array_field_test.v create mode 100644 v_windows/v/old/vlib/v/tests/enum_bitfield_test.v create mode 100644 v_windows/v/old/vlib/v/tests/enum_default_value_in_struct_test.v create mode 100644 v_windows/v/old/vlib/v/tests/enum_hex_test.v create mode 100644 v_windows/v/old/vlib/v/tests/enum_test.v create mode 100644 v_windows/v/old/vlib/v/tests/failing_tests_test.v create mode 100644 v_windows/v/old/vlib/v/tests/field_publicity/embed.v create mode 100644 v_windows/v/old/vlib/v/tests/filter_in_map_test.v create mode 100644 v_windows/v/old/vlib/v/tests/fixed_array_const_size_test.v create mode 100644 v_windows/v/old/vlib/v/tests/fixed_array_init_test.v create mode 100644 v_windows/v/old/vlib/v/tests/fixed_array_of_fn_test.v create mode 100644 v_windows/v/old/vlib/v/tests/fixed_array_test.v create mode 100644 v_windows/v/old/vlib/v/tests/fixed_array_to_string_test.v create mode 100644 v_windows/v/old/vlib/v/tests/fn_assignment_test.v create mode 100644 v_windows/v/old/vlib/v/tests/fn_cross_assign_test.v create mode 100644 v_windows/v/old/vlib/v/tests/fn_expecting_ref_but_returning_struct_test.v create mode 100644 v_windows/v/old/vlib/v/tests/fn_expecting_ref_but_returning_struct_time_module_test.v create mode 100644 v_windows/v/old/vlib/v/tests/fn_high_test.v create mode 100644 v_windows/v/old/vlib/v/tests/fn_index_direct_call_test.v create mode 100644 v_windows/v/old/vlib/v/tests/fn_multiple_returns_test.v create mode 100644 v_windows/v/old/vlib/v/tests/fn_mut_args_test.v create mode 100644 v_windows/v/old/vlib/v/tests/fn_return_fn_test.v create mode 100644 v_windows/v/old/vlib/v/tests/fn_shared_return_test.v create mode 100644 v_windows/v/old/vlib/v/tests/fn_test.v create mode 100644 v_windows/v/old/vlib/v/tests/fn_type_aliases_test.v create mode 100644 v_windows/v/old/vlib/v/tests/fn_variadic_test.v create mode 100644 v_windows/v/old/vlib/v/tests/fn_with_fixed_array_function_args_test.v create mode 100644 v_windows/v/old/vlib/v/tests/for_c_multi_vars_test.v create mode 100644 v_windows/v/old/vlib/v/tests/for_in_alias_test.v create mode 100644 v_windows/v/old/vlib/v/tests/for_in_containers_of_fixed_array_test.v create mode 100644 v_windows/v/old/vlib/v/tests/for_in_iterator_test.v create mode 100644 v_windows/v/old/vlib/v/tests/for_in_mut_reference_selector_val_test.v create mode 100644 v_windows/v/old/vlib/v/tests/for_in_mut_val_test.v create mode 100644 v_windows/v/old/vlib/v/tests/for_in_optional_test.v create mode 100644 v_windows/v/old/vlib/v/tests/for_label_continue_break_test.v create mode 100644 v_windows/v/old/vlib/v/tests/for_loops_2_test.v create mode 100644 v_windows/v/old/vlib/v/tests/for_loops_test.v create mode 100644 v_windows/v/old/vlib/v/tests/for_smartcast_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generic_chan_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generic_fn_assign_generics_struct_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generic_fn_infer_fixed_array_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generic_fn_infer_map_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generic_fn_infer_modifier_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generic_fn_infer_multi_paras_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generic_fn_infer_nested_struct_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generic_fn_infer_struct_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generic_fn_infer_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generic_fn_returning_type_with_T_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generic_fn_typeof_name_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generic_fn_upper_name_type_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generic_functions_with_normal_function_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generic_interface_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generic_sumtype_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_array_typedef_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_assign_reference_generic_struct_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_call_with_reference_arg_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_fn_return_generic_interface_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_from_modules/genericmodule/take.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_from_modules/inference_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_in_big_struct_method_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_in_generics_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_indirect_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_interface_decl_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_interface_method_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_interface_with_multi_generic_structs_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_interface_with_multi_generic_types_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_method_on_receiver_types_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_method_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_multi_array_in_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_multi_types_struct_init_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_return_generics_struct_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_return_inconsistent_types_generics_struct_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_return_multi_array_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_return_multiple_generics_struct_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_return_recursive_generics_struct_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_return_reference_generics_struct_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_struct_anon_fn_fields_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_struct_anon_fn_type_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_struct_init_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_struct_to_string_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_with_anon_generics_fn_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_with_cascaded_multiple_nested_generics_fn_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_with_embed_generics_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_with_fixed_array_type_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_with_generics_fn_return_generics_fn_type_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_with_generics_fn_type_parameter_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_with_generics_struct_init_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_with_generics_struct_receiver_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_with_multi_generics_struct_types_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_with_multiple_generics_struct_receiver_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_with_nested_external_generics_fn_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_with_nested_generic_struct_init_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_with_nested_generics_fn_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_with_recursive_generics_fn_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_with_recursive_generics_struct_test.v create mode 100644 v_windows/v/old/vlib/v/tests/generics_with_variadic_generic_args_test.v create mode 100644 v_windows/v/old/vlib/v/tests/go_array_wait_test.v create mode 100644 v_windows/v/old/vlib/v/tests/go_call_generic_fn_test.v create mode 100644 v_windows/v/old/vlib/v/tests/go_call_interface_method_test.v create mode 100644 v_windows/v/old/vlib/v/tests/go_handle_for_functions_returning_array_test.v create mode 100644 v_windows/v/old/vlib/v/tests/go_wait_1_test.v create mode 100644 v_windows/v/old/vlib/v/tests/go_wait_2_test.v create mode 100644 v_windows/v/old/vlib/v/tests/go_wait_3_test.v create mode 100644 v_windows/v/old/vlib/v/tests/go_wait_option_test.v create mode 100644 v_windows/v/old/vlib/v/tests/go_wait_with_fn_of_interface_para.v create mode 100644 v_windows/v/old/vlib/v/tests/goto_test.v create mode 100644 v_windows/v/old/vlib/v/tests/heap_interface_test.v create mode 100644 v_windows/v/old/vlib/v/tests/heap_reference_test.v create mode 100644 v_windows/v/old/vlib/v/tests/heap_struct_test.v create mode 100644 v_windows/v/old/vlib/v/tests/if_expr_of_optional_test.v create mode 100644 v_windows/v/old/vlib/v/tests/if_expression_test.v create mode 100644 v_windows/v/old/vlib/v/tests/if_guard_test.v create mode 100644 v_windows/v/old/vlib/v/tests/if_smartcast_multi_conds_test.v create mode 100644 v_windows/v/old/vlib/v/tests/if_smartcast_nested_selector_exprs_test.v create mode 100644 v_windows/v/old/vlib/v/tests/if_smartcast_test.v create mode 100644 v_windows/v/old/vlib/v/tests/imported_symbols_test.v create mode 100644 v_windows/v/old/vlib/v/tests/in_expression_test.v create mode 100644 v_windows/v/old/vlib/v/tests/infix_expr_test.v create mode 100644 v_windows/v/old/vlib/v/tests/init_global_test.v create mode 100644 v_windows/v/old/vlib/v/tests/inout/.gitignore create mode 100644 v_windows/v/old/vlib/v/tests/inout/bad_st_as.out create mode 100644 v_windows/v/old/vlib/v/tests/inout/bad_st_as.vv create mode 100644 v_windows/v/old/vlib/v/tests/inout/cli_command_no_execute.out create mode 100644 v_windows/v/old/vlib/v/tests/inout/cli_command_no_execute.vv create mode 100644 v_windows/v/old/vlib/v/tests/inout/cli_root_default_help.out create mode 100644 v_windows/v/old/vlib/v/tests/inout/cli_root_default_help.vv create mode 100644 v_windows/v/old/vlib/v/tests/inout/compiler_test.v create mode 100644 v_windows/v/old/vlib/v/tests/inout/dump_expression.out create mode 100644 v_windows/v/old/vlib/v/tests/inout/dump_expression.vv create mode 100644 v_windows/v/old/vlib/v/tests/inout/enum_print.out create mode 100644 v_windows/v/old/vlib/v/tests/inout/enum_print.vv create mode 100644 v_windows/v/old/vlib/v/tests/inout/file.html create mode 100644 v_windows/v/old/vlib/v/tests/inout/file.md create mode 100644 v_windows/v/old/vlib/v/tests/inout/fixed_array_index.out create mode 100644 v_windows/v/old/vlib/v/tests/inout/fixed_array_index.vv create mode 100644 v_windows/v/old/vlib/v/tests/inout/fixed_array_slice.out create mode 100644 v_windows/v/old/vlib/v/tests/inout/fixed_array_slice.vv create mode 100644 v_windows/v/old/vlib/v/tests/inout/footer.md create mode 100644 v_windows/v/old/vlib/v/tests/inout/header.md create mode 100644 v_windows/v/old/vlib/v/tests/inout/hello.out create mode 100644 v_windows/v/old/vlib/v/tests/inout/hello.vv create mode 100644 v_windows/v/old/vlib/v/tests/inout/hello_devs.out create mode 100644 v_windows/v/old/vlib/v/tests/inout/hello_devs.vv create mode 100644 v_windows/v/old/vlib/v/tests/inout/nested_structs.out create mode 100644 v_windows/v/old/vlib/v/tests/inout/nested_structs.vv create mode 100644 v_windows/v/old/vlib/v/tests/inout/os.out create mode 100644 v_windows/v/old/vlib/v/tests/inout/os.vv create mode 100644 v_windows/v/old/vlib/v/tests/inout/panic_with_cg.out create mode 100644 v_windows/v/old/vlib/v/tests/inout/panic_with_cg.vv create mode 100644 v_windows/v/old/vlib/v/tests/inout/printing_fixed_array_of_pointers.out create mode 100644 v_windows/v/old/vlib/v/tests/inout/printing_fixed_array_of_pointers.vv create mode 100644 v_windows/v/old/vlib/v/tests/inout/string_interp.out create mode 100644 v_windows/v/old/vlib/v/tests/inout/string_interp.vv create mode 100644 v_windows/v/old/vlib/v/tests/inout/tmpl_all_in_one_folder.out create mode 100644 v_windows/v/old/vlib/v/tests/inout/tmpl_all_in_one_folder.vv create mode 100644 v_windows/v/old/vlib/v/tests/inout/tmpl_parse_html.out create mode 100644 v_windows/v/old/vlib/v/tests/inout/tmpl_parse_html.vv create mode 100644 v_windows/v/old/vlib/v/tests/int_cmp_test.v create mode 100644 v_windows/v/old/vlib/v/tests/interface_auto_str_gen_test.v create mode 100644 v_windows/v/old/vlib/v/tests/interface_edge_cases/array_of_interfaces_test.v create mode 100644 v_windows/v/old/vlib/v/tests/interface_edge_cases/array_of_interfaces_with_utility_fn_test.v create mode 100644 v_windows/v/old/vlib/v/tests/interface_edge_cases/assign_to_interface_field_test.v create mode 100644 v_windows/v/old/vlib/v/tests/interface_edge_cases/empty_interface_1_test.v create mode 100644 v_windows/v/old/vlib/v/tests/interface_edge_cases/empty_interface_println_test.v create mode 100644 v_windows/v/old/vlib/v/tests/interface_edge_cases/fn_returning_voidptr_casted_as_interface_test.v create mode 100644 v_windows/v/old/vlib/v/tests/interface_edge_cases/i1_test.v create mode 100644 v_windows/v/old/vlib/v/tests/interface_edge_cases/i2_test.v create mode 100644 v_windows/v/old/vlib/v/tests/interface_edge_cases/i3_test.v create mode 100644 v_windows/v/old/vlib/v/tests/interface_edge_cases/i4_test.v create mode 100644 v_windows/v/old/vlib/v/tests/interface_edge_cases/i5_test.v create mode 100644 v_windows/v/old/vlib/v/tests/interface_edge_cases/i6_test.v create mode 100644 v_windows/v/old/vlib/v/tests/interface_edge_cases/i7_test.v create mode 100644 v_windows/v/old/vlib/v/tests/interface_edge_cases/i8_test.v create mode 100644 v_windows/v/old/vlib/v/tests/interface_edge_cases/i9_test.v create mode 100644 v_windows/v/old/vlib/v/tests/interface_edge_cases/interface_many_named_test.v create mode 100644 v_windows/v/old/vlib/v/tests/interface_edge_cases/pass_voidptr_as_interface_reference_test.v create mode 100644 v_windows/v/old/vlib/v/tests/interface_edge_cases/voidptr_casted_as_an_interface_test.v create mode 100644 v_windows/v/old/vlib/v/tests/interface_embedding_deep_nesting_test.v create mode 100644 v_windows/v/old/vlib/v/tests/interface_embedding_recursive_test.v create mode 100644 v_windows/v/old/vlib/v/tests/interface_embedding_test.v create mode 100644 v_windows/v/old/vlib/v/tests/interface_embedding_with_interface_para_test.v create mode 100644 v_windows/v/old/vlib/v/tests/interface_fields_test.v create mode 100644 v_windows/v/old/vlib/v/tests/interface_fields_typearray_test.v create mode 100644 v_windows/v/old/vlib/v/tests/interface_fn_return_array_of_interface_test.v create mode 100644 v_windows/v/old/vlib/v/tests/interface_fn_return_with_struct_init.v create mode 100644 v_windows/v/old/vlib/v/tests/interface_nested_field_test.v create mode 100644 v_windows/v/old/vlib/v/tests/interface_only_decl_with_optional_test.v create mode 100644 v_windows/v/old/vlib/v/tests/interface_struct_test.v create mode 100644 v_windows/v/old/vlib/v/tests/interface_test.v create mode 100644 v_windows/v/old/vlib/v/tests/interface_variadic_test.v create mode 100644 v_windows/v/old/vlib/v/tests/interfaces_map_test.v create mode 100644 v_windows/v/old/vlib/v/tests/interop_test.v create mode 100644 v_windows/v/old/vlib/v/tests/isreftype_test.v create mode 100644 v_windows/v/old/vlib/v/tests/keep_args_alive_test.v create mode 100644 v_windows/v/old/vlib/v/tests/keep_args_alive_test_c.h create mode 100644 v_windows/v/old/vlib/v/tests/local/local.v create mode 100644 v_windows/v/old/vlib/v/tests/local_test.v create mode 100644 v_windows/v/old/vlib/v/tests/lock_selector_test.v create mode 100644 v_windows/v/old/vlib/v/tests/map_alias_key_test.v create mode 100644 v_windows/v/old/vlib/v/tests/map_and_array_with_fns_test.v create mode 100644 v_windows/v/old/vlib/v/tests/map_assign_array_of_interface_test.v create mode 100644 v_windows/v/old/vlib/v/tests/map_complex_fixed_array_test.v create mode 100644 v_windows/v/old/vlib/v/tests/map_enum_keys_test.v create mode 100644 v_windows/v/old/vlib/v/tests/map_equality_test.v create mode 100644 v_windows/v/old/vlib/v/tests/map_high_order_assign_test.v create mode 100644 v_windows/v/old/vlib/v/tests/map_key_expr_test.v create mode 100644 v_windows/v/old/vlib/v/tests/map_literals_method_call_test.v create mode 100644 v_windows/v/old/vlib/v/tests/map_mut_fn_test.v create mode 100644 v_windows/v/old/vlib/v/tests/map_to_string_test.v create mode 100644 v_windows/v/old/vlib/v/tests/map_type_alias_test.v create mode 100644 v_windows/v/old/vlib/v/tests/map_value_init_test.v create mode 100644 v_windows/v/old/vlib/v/tests/maps_equal_test.v create mode 100644 v_windows/v/old/vlib/v/tests/match_compound_type_cond_test.v create mode 100644 v_windows/v/old/vlib/v/tests/match_error_to_none_test.v create mode 100644 v_windows/v/old/vlib/v/tests/match_expr_returning_optional_test.v create mode 100644 v_windows/v/old/vlib/v/tests/match_expr_with_if_or_match_expr_test.v create mode 100644 v_windows/v/old/vlib/v/tests/match_expr_with_one_branch_test.v create mode 100644 v_windows/v/old/vlib/v/tests/match_expression_for_types_test.v create mode 100644 v_windows/v/old/vlib/v/tests/match_expression_with_fn_names_in_branches_test.v create mode 100644 v_windows/v/old/vlib/v/tests/match_in_fn_call_test.v create mode 100644 v_windows/v/old/vlib/v/tests/match_in_if_expression_test.v create mode 100644 v_windows/v/old/vlib/v/tests/match_in_map_init_test.v create mode 100644 v_windows/v/old/vlib/v/tests/match_in_map_or_expr_test.v create mode 100644 v_windows/v/old/vlib/v/tests/match_interface_test.v create mode 100644 v_windows/v/old/vlib/v/tests/match_smartcast_test.v create mode 100644 v_windows/v/old/vlib/v/tests/match_sumtype_var_shadow_and_as_test.v create mode 100644 v_windows/v/old/vlib/v/tests/match_test.v create mode 100644 v_windows/v/old/vlib/v/tests/match_with_complex_exprs_in_branches_test.v create mode 100644 v_windows/v/old/vlib/v/tests/match_with_complex_sumtype_exprs_test.v create mode 100644 v_windows/v/old/vlib/v/tests/match_with_multi_sumtype_exprs_test.v create mode 100644 v_windows/v/old/vlib/v/tests/methods_on_interfaces_test.v create mode 100644 v_windows/v/old/vlib/v/tests/missing_config_struct_arg_test.v create mode 100644 v_windows/v/old/vlib/v/tests/module_test.v create mode 100644 v_windows/v/old/vlib/v/tests/module_type_cast_test.v create mode 100644 v_windows/v/old/vlib/v/tests/modules/acommentedmodule/commentedfile.v create mode 100644 v_windows/v/old/vlib/v/tests/modules/amodule/another_internal_module_test.v create mode 100644 v_windows/v/old/vlib/v/tests/modules/amodule/internal_module_test.v create mode 100644 v_windows/v/old/vlib/v/tests/modules/amodule/module.v create mode 100644 v_windows/v/old/vlib/v/tests/modules/geometry/geometry.v create mode 100644 v_windows/v/old/vlib/v/tests/modules/methods_struct_another_module/methods_struct_test.v create mode 100644 v_windows/v/old/vlib/v/tests/modules/simplemodule/importing_test.v create mode 100644 v_windows/v/old/vlib/v/tests/modules/simplemodule/simplemodule.v create mode 100644 v_windows/v/old/vlib/v/tests/modules/submodules/submodules.v create mode 100644 v_windows/v/old/vlib/v/tests/modules/submodules/submodules_test.v create mode 100644 v_windows/v/old/vlib/v/tests/modules/submodules/test/test.v create mode 100644 v_windows/v/old/vlib/v/tests/modules/submodules/test/test2/test2.v create mode 100644 v_windows/v/old/vlib/v/tests/multiple_assign_array_index_test.v create mode 100644 v_windows/v/old/vlib/v/tests/multiple_assign_test.v create mode 100644 v_windows/v/old/vlib/v/tests/multiple_paths_in_vmodules/main.vv create mode 100644 v_windows/v/old/vlib/v/tests/multiple_paths_in_vmodules/path1/.gitignore create mode 100644 v_windows/v/old/vlib/v/tests/multiple_paths_in_vmodules/path1/xxx/m.v create mode 100644 v_windows/v/old/vlib/v/tests/multiple_paths_in_vmodules/path2/yyy/m.v create mode 100644 v_windows/v/old/vlib/v/tests/multiple_paths_in_vmodules/path3/zzz/m.v create mode 100644 v_windows/v/old/vlib/v/tests/multiple_paths_in_vmodules/vmodules_overrides_test.v create mode 100644 v_windows/v/old/vlib/v/tests/multiret_with_ptrtype_test.v create mode 100644 v_windows/v/old/vlib/v/tests/mut_receiver_returned_as_reference_test.v create mode 100644 v_windows/v/old/vlib/v/tests/mut_test.v create mode 100644 v_windows/v/old/vlib/v/tests/named_break_continue_test.v create mode 100644 v_windows/v/old/vlib/v/tests/nest_defer_fn_test.v create mode 100644 v_windows/v/old/vlib/v/tests/nested_anonfunc_and_for_break_test.v create mode 100644 v_windows/v/old/vlib/v/tests/nested_map_test.v create mode 100644 v_windows/v/old/vlib/v/tests/nested_multiline_comments_test.v create mode 100644 v_windows/v/old/vlib/v/tests/nested_option_call_test.v create mode 100644 v_windows/v/old/vlib/v/tests/num_lit_call_method_test.v create mode 100644 v_windows/v/old/vlib/v/tests/offsetof_test.v create mode 100644 v_windows/v/old/vlib/v/tests/operator_overloading_cmp_test.v create mode 100644 v_windows/v/old/vlib/v/tests/operator_overloading_with_string_interpolation_test.v create mode 100644 v_windows/v/old/vlib/v/tests/option_2_test.v create mode 100644 v_windows/v/old/vlib/v/tests/option_default_values_test.v create mode 100644 v_windows/v/old/vlib/v/tests/option_if_assign_and_fallthrough_test.v create mode 100644 v_windows/v/old/vlib/v/tests/option_if_expr_test.v create mode 100644 v_windows/v/old/vlib/v/tests/option_in_loop_test.v create mode 100644 v_windows/v/old/vlib/v/tests/option_print_errors_test.v create mode 100644 v_windows/v/old/vlib/v/tests/option_test.v create mode 100644 v_windows/v/old/vlib/v/tests/option_void_test.v create mode 100644 v_windows/v/old/vlib/v/tests/optional_assign_test.v create mode 100644 v_windows/v/old/vlib/v/tests/orm_sub_array_struct_test.v create mode 100644 v_windows/v/old/vlib/v/tests/orm_sub_struct_test.v create mode 100644 v_windows/v/old/vlib/v/tests/parser_line_test.v create mode 100644 v_windows/v/old/vlib/v/tests/pointer_arithmetic_test.v create mode 100644 v_windows/v/old/vlib/v/tests/pointers_multilevel_casts_test.v create mode 100644 v_windows/v/old/vlib/v/tests/pointers_str_test.v create mode 100644 v_windows/v/old/vlib/v/tests/pointers_test.v create mode 100644 v_windows/v/old/vlib/v/tests/prefix_expr_test.v create mode 100644 v_windows/v/old/vlib/v/tests/print_test.v create mode 100644 v_windows/v/old/vlib/v/tests/printing_c_structs/cstruct.h create mode 100644 v_windows/v/old/vlib/v/tests/printing_c_structs/string_interpolation_test.v create mode 100644 v_windows/v/old/vlib/v/tests/printing_c_structs/v.mod create mode 100644 v_windows/v/old/vlib/v/tests/prod/.gitignore create mode 100644 v_windows/v/old/vlib/v/tests/prod/asserts_should_be_skipped.prod.v create mode 100644 v_windows/v/old/vlib/v/tests/prod/asserts_should_be_skipped.prod.v.expected.txt create mode 100644 v_windows/v/old/vlib/v/tests/prod/assoc.prod.v create mode 100644 v_windows/v/old/vlib/v/tests/prod/assoc.prod.v.expected.txt create mode 100644 v_windows/v/old/vlib/v/tests/prod_test.v create mode 100644 v_windows/v/old/vlib/v/tests/profile/profile_test.v create mode 100644 v_windows/v/old/vlib/v/tests/profile/profile_test_1.v create mode 100644 v_windows/v/old/vlib/v/tests/project_with_c_code/.gitignore create mode 100644 v_windows/v/old/vlib/v/tests/project_with_c_code/.v.mod.stop create mode 100644 v_windows/v/old/vlib/v/tests/project_with_c_code/main.v create mode 100644 v_windows/v/old/vlib/v/tests/project_with_c_code/main_test.v create mode 100644 v_windows/v/old/vlib/v/tests/project_with_c_code/mod1/c/header.h create mode 100644 v_windows/v/old/vlib/v/tests/project_with_c_code/mod1/c/implementation.c create mode 100644 v_windows/v/old/vlib/v/tests/project_with_c_code/mod1/v.mod create mode 100644 v_windows/v/old/vlib/v/tests/project_with_c_code/mod1/wrapper.v create mode 100644 v_windows/v/old/vlib/v/tests/project_with_c_code_2/.gitignore create mode 100644 v_windows/v/old/vlib/v/tests/project_with_c_code_2/.v.mod.stop create mode 100644 v_windows/v/old/vlib/v/tests/project_with_c_code_2/main.v create mode 100644 v_windows/v/old/vlib/v/tests/project_with_c_code_2/main2_test.v create mode 100644 v_windows/v/old/vlib/v/tests/project_with_c_code_2/modc/header.h create mode 100644 v_windows/v/old/vlib/v/tests/project_with_c_code_2/modc/impl.c create mode 100644 v_windows/v/old/vlib/v/tests/project_with_c_code_2/modc/v.mod create mode 100644 v_windows/v/old/vlib/v/tests/project_with_c_code_2/modc/wrapper.v create mode 100644 v_windows/v/old/vlib/v/tests/project_with_c_code_ct_ifs/a_linux.h create mode 100644 v_windows/v/old/vlib/v/tests/project_with_c_code_ct_ifs/a_nonlinux.h create mode 100644 v_windows/v/old/vlib/v/tests/project_with_c_code_ct_ifs/ctimeifblock.v create mode 100644 v_windows/v/old/vlib/v/tests/project_with_c_code_ct_ifs/v.mod create mode 100644 v_windows/v/old/vlib/v/tests/project_with_modules_having_submodules/.gitignore create mode 100644 v_windows/v/old/vlib/v/tests/project_with_modules_having_submodules/README.md create mode 100644 v_windows/v/old/vlib/v/tests/project_with_modules_having_submodules/bin/a_program_under_bin_can_find_mod1_test.v create mode 100644 v_windows/v/old/vlib/v/tests/project_with_modules_having_submodules/bin/main.vsh create mode 100644 v_windows/v/old/vlib/v/tests/project_with_modules_having_submodules/mod1/m.v create mode 100644 v_windows/v/old/vlib/v/tests/project_with_modules_having_submodules/mod1/mod11/m.v create mode 100644 v_windows/v/old/vlib/v/tests/project_with_modules_having_submodules/mod1/mod12/m.v create mode 100644 v_windows/v/old/vlib/v/tests/project_with_modules_having_submodules/mod1/mod13/m.v create mode 100644 v_windows/v/old/vlib/v/tests/project_with_modules_having_submodules/mod1/mod14/m.v create mode 100644 v_windows/v/old/vlib/v/tests/project_with_modules_having_submodules/mod1/submodule/m.v create mode 100644 v_windows/v/old/vlib/v/tests/project_with_modules_having_submodules/mod1/v.mod create mode 100644 v_windows/v/old/vlib/v/tests/project_with_modules_having_submodules/tests/submodule_test.v create mode 100644 v_windows/v/old/vlib/v/tests/project_with_modules_having_submodules/v.mod create mode 100644 v_windows/v/old/vlib/v/tests/project_with_tests_for_main/README.md create mode 100644 v_windows/v/old/vlib/v/tests/project_with_tests_for_main/main.v create mode 100644 v_windows/v/old/vlib/v/tests/project_with_tests_for_main/my_other_test.v create mode 100644 v_windows/v/old/vlib/v/tests/project_with_tests_for_main/my_test.v create mode 100644 v_windows/v/old/vlib/v/tests/project_with_tests_for_main/v.mod create mode 100644 v_windows/v/old/vlib/v/tests/projects_that_should_compile_test.v create mode 100644 v_windows/v/old/vlib/v/tests/ptr_arithmetic_test.v create mode 100644 v_windows/v/old/vlib/v/tests/raw_string_test.v create mode 100644 v_windows/v/old/vlib/v/tests/ref_return_test.v create mode 100644 v_windows/v/old/vlib/v/tests/ref_struct_test.v create mode 100644 v_windows/v/old/vlib/v/tests/reference_return_test.v create mode 100644 v_windows/v/old/vlib/v/tests/reliability/semaphore_wait.v create mode 100644 v_windows/v/old/vlib/v/tests/repeated_multiret_values_test.v create mode 100644 v_windows/v/old/vlib/v/tests/repl/.gitattributes create mode 100644 v_windows/v/old/vlib/v/tests/repl/.gitignore create mode 100644 v_windows/v/old/vlib/v/tests/repl/README.md create mode 100644 v_windows/v/old/vlib/v/tests/repl/array_filter.repl create mode 100644 v_windows/v/old/vlib/v/tests/repl/array_init.repl create mode 100644 v_windows/v/old/vlib/v/tests/repl/bad_in_type.repl.skip create mode 100644 v_windows/v/old/vlib/v/tests/repl/chained_fields/bd.repl.skip create mode 100644 v_windows/v/old/vlib/v/tests/repl/chained_fields/c.repl.skip create mode 100644 v_windows/v/old/vlib/v/tests/repl/chained_fields/c2.repl.skip create mode 100644 v_windows/v/old/vlib/v/tests/repl/chained_fields/d.repl.skip create mode 100644 v_windows/v/old/vlib/v/tests/repl/chained_fields/ef.repl.skip create mode 100644 v_windows/v/old/vlib/v/tests/repl/conditional_blocks/for.repl create mode 100644 v_windows/v/old/vlib/v/tests/repl/conditional_blocks/if.repl create mode 100644 v_windows/v/old/vlib/v/tests/repl/conditional_blocks/if_else.repl create mode 100644 v_windows/v/old/vlib/v/tests/repl/default_printing.repl create mode 100644 v_windows/v/old/vlib/v/tests/repl/empty_struct.repl.skip create mode 100644 v_windows/v/old/vlib/v/tests/repl/entire_commented_module.repl create mode 100644 v_windows/v/old/vlib/v/tests/repl/error.repl create mode 100644 v_windows/v/old/vlib/v/tests/repl/error_nosave.repl.skip create mode 100644 v_windows/v/old/vlib/v/tests/repl/function.repl.skip create mode 100644 v_windows/v/old/vlib/v/tests/repl/immutable_len_fields/fields.1.repl.skip create mode 100644 v_windows/v/old/vlib/v/tests/repl/immutable_len_fields/fields.2.repl.skip create mode 100644 v_windows/v/old/vlib/v/tests/repl/immutable_len_fields/fields.3.repl.skip create mode 100644 v_windows/v/old/vlib/v/tests/repl/import.repl create mode 100644 v_windows/v/old/vlib/v/tests/repl/naked_strings.repl create mode 100644 v_windows/v/old/vlib/v/tests/repl/newlines.repl create mode 100644 v_windows/v/old/vlib/v/tests/repl/nomain.repl create mode 100644 v_windows/v/old/vlib/v/tests/repl/nothing.repl create mode 100644 v_windows/v/old/vlib/v/tests/repl/open_close_string_check.repl create mode 100644 v_windows/v/old/vlib/v/tests/repl/option.repl.skip create mode 100644 v_windows/v/old/vlib/v/tests/repl/optional_call.repl create mode 100644 v_windows/v/old/vlib/v/tests/repl/postfix_operators.repl create mode 100644 v_windows/v/old/vlib/v/tests/repl/println.repl create mode 100644 v_windows/v/old/vlib/v/tests/repl/repl_test.v create mode 100644 v_windows/v/old/vlib/v/tests/repl/runner/runner.v create mode 100644 v_windows/v/old/vlib/v/tests/repl/var_decl.repl create mode 100644 v_windows/v/old/vlib/v/tests/reserved_keywords_can_be_escaped_with_at_test.v create mode 100644 v_windows/v/old/vlib/v/tests/return_in_lock_test.v create mode 100644 v_windows/v/old/vlib/v/tests/return_voidptr_test.v create mode 100644 v_windows/v/old/vlib/v/tests/reusable_mut_multiret_values_test.v create mode 100644 v_windows/v/old/vlib/v/tests/run_project_folders/issue_10023_multiple_anon_fns_with_same_position/a.v create mode 100644 v_windows/v/old/vlib/v/tests/run_project_folders/issue_10023_multiple_anon_fns_with_same_position/b.v create mode 100644 v_windows/v/old/vlib/v/tests/run_project_folders_test.v create mode 100644 v_windows/v/old/vlib/v/tests/run_v_code_from_stdin_test.v create mode 100644 v_windows/v/old/vlib/v/tests/semaphore_test.v create mode 100644 v_windows/v/old/vlib/v/tests/semaphore_timed_test.v create mode 100644 v_windows/v/old/vlib/v/tests/shared_arg_test.v create mode 100644 v_windows/v/old/vlib/v/tests/shared_array_test.v create mode 100644 v_windows/v/old/vlib/v/tests/shared_autolock_test.v create mode 100644 v_windows/v/old/vlib/v/tests/shared_elem_test.v create mode 100644 v_windows/v/old/vlib/v/tests/shared_fn_return_test.v create mode 100644 v_windows/v/old/vlib/v/tests/shared_lock_2_test.v create mode 100644 v_windows/v/old/vlib/v/tests/shared_lock_3_test.v create mode 100644 v_windows/v/old/vlib/v/tests/shared_lock_4_test.v create mode 100644 v_windows/v/old/vlib/v/tests/shared_lock_5_test.v create mode 100644 v_windows/v/old/vlib/v/tests/shared_lock_6_test.v create mode 100644 v_windows/v/old/vlib/v/tests/shared_lock_expr_test.v create mode 100644 v_windows/v/old/vlib/v/tests/shared_lock_test.v create mode 100644 v_windows/v/old/vlib/v/tests/shared_map_test.v create mode 100644 v_windows/v/old/vlib/v/tests/shared_unordered_mixed_test.v create mode 100644 v_windows/v/old/vlib/v/tests/shift_test.v create mode 100644 v_windows/v/old/vlib/v/tests/short_struct_param_syntax_test.v create mode 100644 v_windows/v/old/vlib/v/tests/sizeof_2_test.v create mode 100644 v_windows/v/old/vlib/v/tests/sizeof_test.v create mode 100644 v_windows/v/old/vlib/v/tests/skip_unused/assert_passes_test.run.out create mode 100644 v_windows/v/old/vlib/v/tests/skip_unused/assert_passes_test.skip_unused.run.out create mode 100644 v_windows/v/old/vlib/v/tests/skip_unused/assert_passes_test.vv create mode 100644 v_windows/v/old/vlib/v/tests/skip_unused/assert_works_test.run.out create mode 100644 v_windows/v/old/vlib/v/tests/skip_unused/assert_works_test.skip_unused.run.out create mode 100644 v_windows/v/old/vlib/v/tests/skip_unused/assert_works_test.vv create mode 100644 v_windows/v/old/vlib/v/tests/skip_unused/hw.run.out create mode 100644 v_windows/v/old/vlib/v/tests/skip_unused/hw.skip_unused.run.out create mode 100644 v_windows/v/old/vlib/v/tests/skip_unused/hw.vv create mode 100644 v_windows/v/old/vlib/v/tests/skip_unused/println_os_executable.run.out create mode 100644 v_windows/v/old/vlib/v/tests/skip_unused/println_os_executable.skip_unused.run.out create mode 100644 v_windows/v/old/vlib/v/tests/skip_unused/println_os_executable.vv create mode 100644 v_windows/v/old/vlib/v/tests/sorting_by_different_criteria_test.v create mode 100644 v_windows/v/old/vlib/v/tests/sorting_by_references_test.v create mode 100644 v_windows/v/old/vlib/v/tests/static_arrays_using_const_for_size_test.v create mode 100644 v_windows/v/old/vlib/v/tests/static_vars_test.v create mode 100644 v_windows/v/old/vlib/v/tests/str_gen_test.v create mode 100644 v_windows/v/old/vlib/v/tests/string_alias_test.v create mode 100644 v_windows/v/old/vlib/v/tests/string_index_or_test.v create mode 100644 v_windows/v/old/vlib/v/tests/string_interpolation_alias_test.v create mode 100644 v_windows/v/old/vlib/v/tests/string_interpolation_array_test.v create mode 100644 v_windows/v/old/vlib/v/tests/string_interpolation_custom_str_test.v create mode 100644 v_windows/v/old/vlib/v/tests/string_interpolation_floats_test.v create mode 100644 v_windows/v/old/vlib/v/tests/string_interpolation_function_test.v create mode 100644 v_windows/v/old/vlib/v/tests/string_interpolation_multi_return_test.v create mode 100644 v_windows/v/old/vlib/v/tests/string_interpolation_multistmt_test.v create mode 100644 v_windows/v/old/vlib/v/tests/string_interpolation_of_array_of_structs_test.v create mode 100644 v_windows/v/old/vlib/v/tests/string_interpolation_shared_test.v create mode 100644 v_windows/v/old/vlib/v/tests/string_interpolation_string_args_test.v create mode 100644 v_windows/v/old/vlib/v/tests/string_interpolation_struct_test.v create mode 100644 v_windows/v/old/vlib/v/tests/string_interpolation_sumtype_test.v create mode 100644 v_windows/v/old/vlib/v/tests/string_interpolation_test.v create mode 100644 v_windows/v/old/vlib/v/tests/string_interpolation_variadic_test.v create mode 100644 v_windows/v/old/vlib/v/tests/string_struct_interpolation_test.v create mode 100644 v_windows/v/old/vlib/v/tests/strings_unicode_test.v create mode 100644 v_windows/v/old/vlib/v/tests/struct_allow_both_field_defaults_and_skip_flag_test.v create mode 100644 v_windows/v/old/vlib/v/tests/struct_chained_fields_correct_test.v create mode 100644 v_windows/v/old/vlib/v/tests/struct_child_field_default_test.v create mode 100644 v_windows/v/old/vlib/v/tests/struct_embed_test.v create mode 100644 v_windows/v/old/vlib/v/tests/struct_eq_op_only_test.v create mode 100644 v_windows/v/old/vlib/v/tests/struct_equality_test.v create mode 100644 v_windows/v/old/vlib/v/tests/struct_field_default_value_interface_cast_test.v create mode 100644 v_windows/v/old/vlib/v/tests/struct_field_default_value_sumtype_cast_test.v create mode 100644 v_windows/v/old/vlib/v/tests/struct_fields_required_test.v create mode 100644 v_windows/v/old/vlib/v/tests/struct_fields_storing_functions_test.v create mode 100644 v_windows/v/old/vlib/v/tests/struct_ierror_test.v create mode 100644 v_windows/v/old/vlib/v/tests/struct_init_and_assign_test.v create mode 100644 v_windows/v/old/vlib/v/tests/struct_map_method_test.v create mode 100644 v_windows/v/old/vlib/v/tests/struct_test.v create mode 100644 v_windows/v/old/vlib/v/tests/struct_transmute_test.v create mode 100644 v_windows/v/old/vlib/v/tests/structs_with_voidptr_fields_can_be_printed_test.v create mode 100644 v_windows/v/old/vlib/v/tests/sum_type_common_fields_test.v create mode 100644 v_windows/v/old/vlib/v/tests/sum_type_test.v create mode 100644 v_windows/v/old/vlib/v/tests/sumtype_assign_test.v create mode 100644 v_windows/v/old/vlib/v/tests/sumtype_calls_test.v create mode 100644 v_windows/v/old/vlib/v/tests/sumtype_equality_test.v create mode 100644 v_windows/v/old/vlib/v/tests/sumtype_literal_test.v create mode 100644 v_windows/v/old/vlib/v/tests/sumtype_str_for_subtypes_with_str_test.v create mode 100644 v_windows/v/old/vlib/v/tests/sumtype_str_test.v create mode 100644 v_windows/v/old/vlib/v/tests/supports__likely__test.v create mode 100644 v_windows/v/old/vlib/v/tests/testcase_leak.v create mode 100644 v_windows/v/old/vlib/v/tests/testdata/enum_in_builtin/builtin.v create mode 100644 v_windows/v/old/vlib/v/tests/testdata/enum_in_builtin/c.v create mode 100644 v_windows/v/old/vlib/v/tests/testdata/enum_in_builtin/main.v create mode 100644 v_windows/v/old/vlib/v/tests/testdata/sizeof_used_in_assert_test.v create mode 100644 v_windows/v/old/vlib/v/tests/testdata/tests_returning_options_failing_test.v create mode 100644 v_windows/v/old/vlib/v/tests/thread_to_string_test.v create mode 100644 v_windows/v/old/vlib/v/tests/tmpl/base.txt create mode 100644 v_windows/v/old/vlib/v/tests/tmpl/inner.txt create mode 100644 v_windows/v/old/vlib/v/tests/tmpl_test.v create mode 100644 v_windows/v/old/vlib/v/tests/type_alias_of_pointer_types_test.v create mode 100644 v_windows/v/old/vlib/v/tests/type_alias_str_method_override_test.v create mode 100644 v_windows/v/old/vlib/v/tests/type_alias_test.v create mode 100644 v_windows/v/old/vlib/v/tests/type_name_test.v create mode 100644 v_windows/v/old/vlib/v/tests/type_promotion_test.v create mode 100644 v_windows/v/old/vlib/v/tests/type_voidptr_test.v create mode 100644 v_windows/v/old/vlib/v/tests/typeof_simple_types_test.v create mode 100644 v_windows/v/old/vlib/v/tests/typeof_test.v create mode 100644 v_windows/v/old/vlib/v/tests/unreachable_code_paths_test.v create mode 100644 v_windows/v/old/vlib/v/tests/unsafe_test.v create mode 100644 v_windows/v/old/vlib/v/tests/valgrind/1.strings_and_arrays.v create mode 100644 v_windows/v/old/vlib/v/tests/valgrind/fn_returning_string_param.v create mode 100644 v_windows/v/old/vlib/v/tests/valgrind/fn_with_return_should_free_local_vars.v create mode 100644 v_windows/v/old/vlib/v/tests/valgrind/option_reassigned.v create mode 100644 v_windows/v/old/vlib/v/tests/valgrind/option_simple.v create mode 100644 v_windows/v/old/vlib/v/tests/valgrind/simple_interpolation.v create mode 100644 v_windows/v/old/vlib/v/tests/valgrind/simple_interpolation_script_mode.v create mode 100644 v_windows/v/old/vlib/v/tests/valgrind/simple_interpolation_script_mode_more_scopes.v create mode 100644 v_windows/v/old/vlib/v/tests/valgrind/struct_field.v create mode 100644 v_windows/v/old/vlib/v/tests/valgrind/valgrind_test.v create mode 100644 v_windows/v/old/vlib/v/tests/vargs_auto_str_method_and_println_test.v create mode 100644 v_windows/v/old/vlib/v/tests/vargs_empty_param_test.v create mode 100644 v_windows/v/old/vlib/v/tests/vmod_parser_test.v create mode 100644 v_windows/v/old/vlib/v/tests/voidptr_to_u64_cast_a_test.v create mode 100644 v_windows/v/old/vlib/v/tests/voidptr_to_u64_cast_b_test.v create mode 100644 v_windows/v/old/vlib/v/tests/working_with_an_empty_struct_test.v create mode 100644 v_windows/v/old/vlib/v/token/position.v create mode 100644 v_windows/v/old/vlib/v/token/token.v create mode 100644 v_windows/v/old/vlib/v/util/diff.v create mode 100644 v_windows/v/old/vlib/v/util/diff/diff.v create mode 100644 v_windows/v/old/vlib/v/util/errors.v create mode 100644 v_windows/v/old/vlib/v/util/module.v create mode 100644 v_windows/v/old/vlib/v/util/quote.v create mode 100644 v_windows/v/old/vlib/v/util/recompilation/recompilation.v create mode 100644 v_windows/v/old/vlib/v/util/scanning.v create mode 100644 v_windows/v/old/vlib/v/util/suggestions.v create mode 100644 v_windows/v/old/vlib/v/util/timers.v create mode 100644 v_windows/v/old/vlib/v/util/util.v create mode 100644 v_windows/v/old/vlib/v/util/version/version.v create mode 100644 v_windows/v/old/vlib/v/util/vtest/vtest.v create mode 100644 v_windows/v/old/vlib/v/vcache/vcache.v create mode 100644 v_windows/v/old/vlib/v/vcache/vcache_test.v create mode 100644 v_windows/v/old/vlib/v/vet/vet.v create mode 100644 v_windows/v/old/vlib/v/vmod/parser.v create mode 100644 v_windows/v/old/vlib/v/vmod/vmod.v create mode 100644 v_windows/v/old/vlib/vweb/README.md create mode 100644 v_windows/v/old/vlib/vweb/assets/assets.v create mode 100644 v_windows/v/old/vlib/vweb/assets/assets_test.v create mode 100644 v_windows/v/old/vlib/vweb/request.v create mode 100644 v_windows/v/old/vlib/vweb/request_test.v create mode 100644 v_windows/v/old/vlib/vweb/route_test.v create mode 100644 v_windows/v/old/vlib/vweb/sse/sse.v create mode 100644 v_windows/v/old/vlib/vweb/tests/vweb_test.v create mode 100644 v_windows/v/old/vlib/vweb/tests/vweb_test_server.v create mode 100644 v_windows/v/old/vlib/vweb/vweb.v create mode 100644 v_windows/v/old/vlib/vweb/vweb_app_test.v create mode 100644 v_windows/v/old/vlib/x/json2/README.md create mode 100644 v_windows/v/old/vlib/x/json2/any_test.v create mode 100644 v_windows/v/old/vlib/x/json2/decoder.v create mode 100644 v_windows/v/old/vlib/x/json2/decoder_test.v create mode 100644 v_windows/v/old/vlib/x/json2/encoder.v create mode 100644 v_windows/v/old/vlib/x/json2/encoder_test.v create mode 100644 v_windows/v/old/vlib/x/json2/json2.v create mode 100644 v_windows/v/old/vlib/x/json2/json2_test.v create mode 100644 v_windows/v/old/vlib/x/json2/scanner.v create mode 100644 v_windows/v/old/vlib/x/json2/scanner_test.v create mode 100644 v_windows/v/old/vlib/x/ttf/README.md create mode 100644 v_windows/v/old/vlib/x/ttf/common.v create mode 100644 v_windows/v/old/vlib/x/ttf/render_bmp.v create mode 100644 v_windows/v/old/vlib/x/ttf/render_sokol_cpu.v create mode 100644 v_windows/v/old/vlib/x/ttf/text_block.v create mode 100644 v_windows/v/old/vlib/x/ttf/ttf.v create mode 100644 v_windows/v/old/vlib/x/ttf/ttf_test.v create mode 100644 v_windows/v/old/vlib/x/ttf/ttf_test_data.bin create mode 100644 v_windows/v/old/vmake.bat (limited to 'v_windows/v/old') diff --git a/v_windows/v/old/BSDmakefile b/v_windows/v/old/BSDmakefile new file mode 100644 index 0000000..756fa76 --- /dev/null +++ b/v_windows/v/old/BSDmakefile @@ -0,0 +1,8 @@ +CC ?= cc + +all: + rm -rf vc/ + git clone --depth 1 --quiet https://github.com/vlang/vc + $(CC) -std=gnu11 -w -o v vc/v.c -lm -lexecinfo + rm -rf vc/ + @echo "V has been successfully built" diff --git a/v_windows/v/old/CHANGELOG.md b/v_windows/v/old/CHANGELOG.md new file mode 100644 index 0000000..ae656f3 --- /dev/null +++ b/v_windows/v/old/CHANGELOG.md @@ -0,0 +1,486 @@ +-## V 0.2.4 +-*Not yet released, changelog is not full* +- String interpolation and struct stringers are now implemented in pure V +with a much cleaner and faster implementation. Previously libc's `sprintf` +was used. +- Improved `unused variable` warning. Assigning to a variable no longer marks it as used. +- Bare metal support. Vinix OS kernel is now being developed in V. + +## V 0.2.2 - 0.2.3 +*22 Jan 2021* +- Allow interfaces to define fields, not just methods. +- `vweb` now uses struct embedding: `app.vweb.text('hello') => app.text('hello')`. +- Consts can now be declared outside of `const()` blocks: `const x = 0`. +- Overloading of `>`, `<`, `!=`, `==`, `<=` and `>=` operators. +- New struct updating syntax: `User{ ...u, name: 'new' }` to replace `{ u | name: 'new' }`. +- `byte.str()` has been fixed and works like all other numbers. `byte.ascii_str()` has been added. +- Smart cast in for loops: `for mut x is string {}`. +- `[noinit]` struct attribute to disallow direct struct initialization with `Foo{}`. +- Array decompose: `[1, 2, 3]...` is now `...[1, 2, 3]` +- Treating `enum` as `int` and operations on `enum` except `==` and `!=` are removed for strict type checking. +- Support `[manualfree] fn f1(){}` and `[manualfree] module m1`, for functions doing their own memory management. +- Allow usage of `<` and `>` operators for struct in `.sort` method for arrays, i.e. `arr.sort(a < b)`. +- Auto generate assignment operators like `+=`, `-=`, `*=`, `/=` and `%=` if the operators are defined. +- Colorize and improve failing tests output. +- Fix `go` with a generic function: `go test(c, 'abcd')`. +- Add comptime `x := $embed_file('v.png') println(x.len) println(ptr_str(x.data()))`, for embedding files into binaries. +- Advanced vdoc search on mobile layout. +- string's `left()`/`right` were removed in favor of slicing syntax: `str[..pos]`. +- gg: native graphics mode on macOS/iOS (using Cocoa Drawing API). +- Full path to consts must be specified everywhere. This makes it easy to distinguish them +from local variables. +- `__offsetof` for low level needs (works like `offsetof` in C). +- vfmt now preserves empty lines, like gofmt. +- Support for compile time environment variables via `$env('ENV_VAR')`. +- Allow method declaration of `==` and `<` operators and auto generate `!=`, `>`, `<=` and `>=`. +- support `dump(expr)`, i.e. tracing of both the location, name and value of an expression +- deprecate os.exec in favour of os.executable() which does *NOT* return an option, when the command was not found + +## V 0.2.1 +*30 Dec 2020* +- Hashmap bootstrapping fixes. +- Array decomposition to varargs: `fn sum(i ...int) int` => `a := [2,3,4] println(sum(a...))` +- HTML module docs generated by vdoc now have global search. + +## V 0.2 +*22 Dec 2020* +- Compile-time memory management via `-autofree`. [Video demonstration](https://www.youtube.com/watch?v=gmB8ea8uLsM). +It will be enabled by default in 0.3 +- Channels and locks. +- Thread safe typed arrays via keyword `shared`. +- Struct embedding. +- IO streams. +- A powerful websocket module that conforms to RFC 6455 and passes the Autobahn test suite (498 client tests and 249 server tests). +- The `net` module is now non blocking and is more feature complete providing similar API to Go. +- V's graphics module now uses Metal/DirectX/OpenGL instead of just OpenGL. +- V can now run in the browser via WASM and execute V code by translating it to JavaScript: +https://v-wasm.now.sh +- V binaries for Linux/Windows/macOS are now built and deployed automatically via GitHub Actions. +- Smart casting for sumtypes and interfaces, including complex expressions: `if x.expr is int { println(x.expr + 1) }`. +- Clean and easy way to sort arrays: `users.sort(a.name > b.name)`. +- A huge amount of `vfmt` fixes and improvements. It has now reached a point where it can be safely used on any V source file. +- A new CI job that runs `v fmt -verify` on the entire code base, a new command that makes sure the file/directory +has been vfmt'ed. This ensures that all code submitted to the V project is formatted. +- A new tool `v vet` for analyzing the project and finding potential bugs and errors. +- A new `term.ui` module for building dynamic terminal UIs with an example editor written in it. +- Early iOS and Android support. +- All missing ORM features from the old backend were brought back. +- Magic `it` variable has been replaced with smart casts (the change is completely handled by vfmt). +- Cross-compiling to Windows and Linux brought back. +- C2V can now generate wrappers. Example: https://github.com/medvednikov/libsodium. (C2V will be released by 0.3) +- C++ compiler support: code, generated by the C backend can now by compiled by C++ compilers. +- Short generics syntax: `foo(5)` instead of `foo(5)`. +- Cached modules via `-usecache`. Faster compilation due to not needing to rebuild the entire vlib for +each program. Will be enabled by default in 0.2.1. +- New improved sum types implementation. +- Lots of errors that happen often during the development cycle were turned into warnings to increase + development speed. They are still errors in production builds. +- Labeled `break` and `continue`. +- Lots of documentation. The official language documentation grew 3 times in size. +- `modules.vlang.io` is now generated automatically on every commit. +- Builtin compile-time JSON serializer now supports `time.Time`. +- Fixes in type aliases, to make them behave just like the types they alias. +- `array.contains(element)` is now generic. +- Lots of improvements in the JS backend and its type system. +- Simpler and more constinent function arg syntax: `foo(a int, b int, c string)` instead of `foo(a, b int, c string)` +- Lots of fixes and optimizations in the hashmap. +- Lots of missing checks in the type checker were added (for example, checking the correct usage of public struct fields). +- Mutability bug fixes +- Taking the address of a map value is no longer allowed, like in Go. +- Matrix multiplication. +- A new `#pkgconfig` flag to provide platform independent way to get compilation flags for C libraries/packages. +- Explicit parentheses requirement in complex boolean expressions. +- `println` was made even smarter, and can now handle complex types. +- Precompiled text templates can now be used outside of vweb via `$tmpl()`. +- Gitly, a big web application written in vweb has been released: https://github.com/vlang/gitly +- `['/:arg1/:arg2/action']` vweb action attribute for easily getting query parameters assigned to method arguments. +- Improved performance of text rendering, `gg.text_width()`. +- Webview module in V UI. +- Binary enum flags. +- `[export]` attribute to change exported function name (for example for calling from a C library). +- `unsafe` fixes and improvements. +- Improvements to rand: `rand.ulid()`, `rand.uuid()`, a unified customizable PRNG API. +- Hundreds of other fixes, features, and tests (from now on the changelog will be updated +right away as the feature/bug fix lands). + + +## V 0.1.27 +*5 May 2020* + +- vfmt has been re-written from scratch using the new AST parser. + It's much faster, cleaner, and can format +files with compilation errors. +- `strconv`, `sprintf`, and `printf` in native V, without any libc calls. +- Interfaces are now a lot more stable and have all expected features. +- Lots of x64 backend improvements: function calls, if expressions, for loops, local variables. +- `map()` and `filter()` methods can now be chained. +- New `[]int{cap:cap, len:len}` syntax for initializing array length and capacity. +- New `is` keyword for checking the type of sum types and interfaces. +- `as` can now be used to cast interfaces and sum types. +- Profiling with `-profile`. Prints a nice table with details about every single function call: + number of calls, average time per call, total time per function +- `import(xxx)` syntax has been removed in favor of `import xxx` for simplicity and greppability. +- Lots of fixes and improvements in the type checker. +- `time.StopWatch` +- `dl` module for dynamic loading. +- Automatic `str()` method generation for every single type, including all arrays. +- Short struct initialization syntax for imitating named function args: `foo(bar:0, baz:1)`. +- New operator `!in`. +- Performance improvements in critical parts of the builtin data structures (array, map). +- High order functions improvements (functions can now be returned etc). +- Anonymous functions that can be defined inside other functions. +- Built-in JSON module is back. +- Lots and lots of new tests added, including output tests that test error messages. +- Multiple errors are now printed, the compiler no longer stops after the first error. +- The new JS backend using the AST parser (almost complete). +- Variadic functions. +- `net.websocket` module (early stage). +- `vlib` is now memory leak free, lots of `autofree` improvements. +- Simplified and cleaned up `cmd/v`, `v.builder`. +- V UI was updated to work with the new backend. + + +## V 0.1.25 +*1 Apr 2020* + +- The entire compiler has been re-written with an AST parser. + The code is now a lot cleaner and more maintainable. + ~15k lines of old compiler code were removed. + +## V 0.1.24 +*31 Dec 2019* + +- A new parser/generator built on top of an AST that simplifies code greatly + and allows to implement new backends much faster. +- Sum types (`type Expr = IfExpr | MatchExpr | IntegerLiteral`). +- B-tree map (sped up the V compiler by ~10%). +- `v fmt -w`. +- The entire code base has been formatted with vfmt. +- Generic structs. +- SDL module. +- Arrays of pointers. +- os: `is_link()`, `is_dir()`, `exists()`. +- Ranging through fixed size arrays. +- Lots of fixes in ORM and vweb. +- The first tutorial: [building a simple web application with vweb](https://github.com/vlang/v/blob/master/tutorials/building_a_simple_web_blog_with_vweb/README.md) +- Match expressions now must be exhaustive. +- freestanding: `malloc()`/`free()`. +- `++` is now required instead of `+= 1` for consistency. +- Interpolated strings now allow function calls: `println('val = $get_val()')`. +- `string.replace_each([])` for an efficient replacement of multiple values. +- More utf8 helper functions. +- `-prealloc` option for block allocations. +- `type` aliases. +- Running `v` with an unknown command will result in an error. +- `atof` implementation in pure V. +- Enums can now have negative values. +- New `filepath` module. +- `math.factorial`. +- `ftp` module. +- New syntax for casting: `val as Type`. +- Fewer libc functions used (soon V will have no dependency on libc). + + +## V 0.1.23 +*30 Nov 2019* + +- [Direct x64 machine code generation](https://github.com/vlang/v/issues/2849). + Hello world being built in 3 milliseconds. +- Bare metal support via the `-freestanding` flag, to build programs without linking to libc. +- Prebuilt V packages for Linux, macOS, and Windows. +- `string.index()` now returns `?int` instead of `int/-1`. +- Lots of fixes in Generics. +- vweb framework for developing web applications is back. +- Vorum, the forum/blogging software written in vweb, can now be compiled and has been added to CI. +- REPL, `v up` have been split up into separate applications to keep the core V compiler small. +- V now enforces short enum syntax (`.green` instead of `Color.green`) when it's enough. +- V UI for macOS. +- Interfaces have been rewritten. `[]interface` support. +- `os.cp()` for copying files and directores. +- Additional compile-time flags: `$if clang, msvc, mingw, x32, x64, big_endian, little_endian {`. +- All C functions now have to be declared, all missing C functions have been defined. +- Global variables (only with the `-enable-globals` flag) + for low level applications like kernels and drivers. +- Nothing can be cast to bool (previously code like `if bool(1) {` worked). +- `<<` and `>>` now work with all integer types. +- V detects Cygwin and shows an error (V supports Windows natively). +- Improved type checking of some operators (`%, |, &` etc). +- Windows 7 support. +- `println(true)` now prints `true` instead of `1`. +- `os.exec()` now uses `CreateProcess` on Windows. +- fast.vlang.io website for monitoring the performance of V after every commit. +- On Windows Visual Studio is now used automatically if GCC is not installed. +- vfmt! +- Lots of cleaning up in the compiler code. +- Multi-level pointers in unsafe code (`****int`). +- MSVC backtrace. +- `$if os {` blocks are now skipped on a different OS. +- C string literals (`c'hello'`). +- AlpineLinux/musl fixes + added to CI. +- Inline assembly. +- Clipboard module (Windows, macOS, X). +- `foo()?` syntax for error propagation. +- Docs have been migrated from HTML to `doc/docs.md`. +- `eventbus` module. +- Haiku OS support. +- `malloc/free` on bare metal. +- `utf8` helper functions (`to_lower()`, `to_upper()`, etc). +- Optimization of `for c in str {`. +- `string/array.left/right/slice/substr` were removed (use `[a..b]` slicing syntax instead). + + + +## V 0.1.22 +*28 Oct 2019* + +- Generic functions (`fn foo(bar T) T {`) with varargs support. +- `array[start..end]` and `string[start..end]` slicing syntax. +- Optimized `array.filter()` and `array.map()`. +- `sqlite` module. +- Cached modules for faster compilation. +- Dramatic compilation optimizations: [V now compiles itself in 0.10 - 0.30 seconds](https://github.com/vlang/v/wiki/The-V-language-now-compiles-itself-in-0.09-seconds) +- V scripts (simpler and cross-platform alternative to Bash). +- Infinite multi-dimensional arrays (`[][][]int`). +- `unsafe`. +- `[deprecated]` attribute. +- `[if]` function attributes for compile time function exclusion for performance. +- `switch` has been completely removed from the language and replaced by +`match` everywhere. +- `pub struct` and `pub const`, previously all structs and consts were public +by default. +- `musl` support (V can now run on, for example, Alpine Linux). +- Module header generation. V now supports closed source modules, which are still +used in some industries. +- Constants were added to typo suggestions. +- `color in [.green, .red, .blue]` now works without specifying `Color.green`. +- V compiler is now a module that can be used by other programs. +- Backtraces now have source lines on Linux. +- `runtime.nr_cpus()`. +- `fn init()` for module initialization. +- `a in [1, 2, 3]` optimization: no array gets allocated. +- Raw strings: `s := r'hello\nworld'`. +- `if a := func() { }` syntax for handling optionals. +- f32/f64 comparison now uses machine epsilon by default. + + +## V 0.1.21 +*30 Sep 2019* + +- `none` keyword for optionals. +- Solaris support. +- All table lookup functions now use `none`. +- varargs: `fn foo(bar int, params ...string) {`. +- Double quotes (`"`) can now also be used to denote strings. +- GitHub Actions CI in addition to Travis. +- `-compress` option. The V binary built with `-compress` is only ~90 KB! +- More memory management. +- Unused modules result in an error. +- "Unused variable/module" errors are now warnings in non-production builds. +- Duplicate methods with the same name can no longer be defined. +- Struct names must be capitalized, variable/function names must use snake_case. +- Error messages are now even nicer! +- Lots of fixes in automatic `.str()` method generation for structs and arrays. +- ~30% faster parser (files are no longer parsed separately for each pass). +- `_` is no longer a variable, but an actual syntax construct to skip unused values, like in Go. +- Multiple returns (`fn foo() (int, string) {`). +- `!` can now only be used with booleans. + + +## V 0.1.20 +*17 Sep 2019* + +- JavaScript backend! +- Hundreds of C warnings were fixed. `gcc v.c` now builds without +any warnings. +- The mutability check now applies to function args (mutable +receivers that are not modified result in a compilation error). +- V tests now show how long each test took. +- Official Android support (only console applications via Termux for now). +- Typo check. If a variable/function/module etc is misspelled, +V will suggest the correct name. +- Lots of Microsoft C fixes, and a separate Travis instance for +this backend. +- Bitwise operators `|`, `^`, `&` no longer work with booleans. + + +## V 0.1.19 +*12 Sep 2019* + +- Lots of refactoring, simplifications, and optimizations in the compiler. +- Experimental memory management at compilation (only for the V compiler itself for now). +- Lots of ORM fixes. +- Functions can now be inlined via the `[inline]` attribute. +- New `mysql` module. +- Better error format that is supported by all major editors (go to error). +- Error messages now point to the actual place where the error happened. +- Custom json field names: `struct User { last_name string [json:lastName] }`. +- Raw json fields via the `[raw]` attribute. +- All C code was removed from the `freetype` module. +- `gg` module can now render all Unicode characters. +- `[typedef]` attribute for imported C struct typedefs. +- Support of Objective C interfaces (primarily for using Cocoa). +- REPL: clear command and custom functions. +- REPL tests (which are also used for testing certain compiler errors). +- Syntax bug fixed: `foo[0] += 10` is now possible. +- http: support plain HTTP protocol and follow redirects. +- http: header data is now processed correctly. +- net: basic UDP support. +- `import const` was removed from the language. +- `array.contains()` was removed from the language (`in` should be used instead). +- `[0; len]` syntax was removed (replaced with a simpler `[0].repeat(len)`) +- Primitive aliases were removed to simplify the language. +- GitHub supports V now! +- Backtraces are now printed on panics. +- A new awesome `readline` module. +- V.c is now regenerated automatically after every commit. +- A bug with struct ordering was fixed, now structs can be declared in any order. +- V modules can now be built with `v build module`. +- `@FILE, @LINE, @FN, @COLUMN` for debugging. + + +## V 0.1.18 +*16 Aug 2019* + +- Built-in ORM (`uk_customers = db.select from Customer where country == 'uk' && nr_orders > 0`). +- Map initialization syntax: `m := { ‘foo’: ‘bar’, ‘baz’: ‘foo’ }`. +- `map.delete(key)`. +- `libcurl` dependency was removed from the `http` module. +- All function arguments are now immutable by default (previously they could be + modifed inside the function). +- `http` functions now return optionals. +- `sync.WaitGroup`. +- `vweb` static files serving. +- `crypto.rand` module. +- `v up` to update V. +- SChannel support on Windows. +- `net.urllib` module. +- vpm package manager, `v install`. +- `()` are now required in complex bool expressions: `(a && b) || c` instead of `a && b || c`. +- All arrays now have a default `.str()` method. +- Bootstrapping V with MSVC. +- Experimental `≠` etc support. +- `encoding.csv` module. +- `$if debug {` for running code in debug mode only. +- Map struct fields are now initialized automatically, just like arrays. +- Maps now support array values. +- `json` functions can no longer be used if the `json` module is not imported. + + +## V 0.1.17 +*29 Jul 2019* +- `vweb` module for developing web apps in V. +- vtalk, open source V forum software. +- Generics (very limited right now, but they will be gradually improved). +- Comptime codegen (`foo.$method()` where `method` is a string). +- @ for escaping keywords (e.g. `struct Foo { @type string }`). +- Windows Unicode fixes (V can now work with non-ASCII paths etc on Windows). +- Fix mutable args bugs + don't allow primitive arguments to be modified. +- Declaring a mutable variable and never modifying it results in a compilation error. +- Interactive debugging support. +- `sync` module for Windows. +- `#!` support on Unix systems (V scripts). +- Lots of Visual Studio fixes. +- `crypto.aes` and `crypto.rc4` modules. +- Internal modules. + + +## V 0.1.16 +*23 Jul 2019* +- V can now be used with Visual Studio! +- Hot code reloading now works with graphical applications (e.g. graph.v, bounce.v). +- Compile time memory management for arrays. +- High order functions. +- `match` expression (replacing `switch`). +- Import cycle detection. +- `crypto/md5`, `crypto/sha256`, and `crypro/sha512` modules. +- `os.executable()` - a cross platform function that returns full path to current executable. +- `~/.vlang` and `VROOT` were removed entirely. The installation is a lot cleaner now. +- V can now be packaged for all Linux distros. +- Arch Linux package. +- `string(bytes_buffer, len)`, `string(bytes_array)` casts. +- Multiple `defer`s. +- `key in map` syntax (replacing `map.exists(key)`). + + +## V 0.1.15 +*15 Jul 2019* +- FreeBSD, OpenBSD, NetBSD, DragonFly support. +- Hot reloading now works with graphical applications: [bounce.v](examples/hot_reload/bounce.v) +- VROOT was removed, the installation process is now much simpler. +- `defer` statement. +- map.v was re-written. It's now much faster. +- `for key, val in map` syntax. +- `flag` module for parsing command line arguments. +- `zip` module. +- `crypto/sha1` module. +- Submodules and module aliases (`import encoding.base64 as b64`). + + +## V 0.1.14 +*12 Jul 2019* +- `gg` module Windows support, V Tetris runs on Windows. +- Compile `glad` and `cJSON` only once. Programs using `gg` or `json` compile a bit faster. +- `v.c` has been cleaned up and minimized (~16k => ~10k lines of code). +- `type` aliases can now have methods. +- Const overflow check during compilation (`byte(1000)` will no longer compile). + + +## V 0.1.13 +*10 Jul 2019* +- New enum syntax (`token == .name`), enum values are no longer global consts. +- Submodules (`import encoding.base64`). +- Hot code reloading. +- Special `err` variable for getting error values. +- Complex numbers. +- `<<` can now append arrays (`numbers << [1, 2, 3]`). +- Lots of Windows fixes (Windows still needs some work). +- Lots of REPL improvements (e.g. `>> 2 + 3` works now, no `println` required). +- The website was made easily translatable, it's now partially available in several languages. + + +## V 0.1.12 +*4 Jul 2019* +- V can finally compile itself on Windows (https://github.com/vlang/v#mingw-w64). +- `os` module now uses optionals in all functions that return `File`. +- Lots of bugs with optionals were fixed. +- `println` was optimized. It no longer results in allocations. + Now it also works correctly with all integer types. +- Lots of `vfmt` fixes, it will be enabled tomorrow. +- New `strings` module. +- Lots of other fixes and improvements, thanks to all the contributors. + + +## V 0.1.11 +*1 Jul 2019* +- Cross compilation for Windows! +- Lots of Windows fixes. +- socket.v. +- maps fixed. + + +## V 0.1.9 - 0.1.10 +*29 Jun 2019* +- Windows support via MinGW-w64. Pre-built Windows binary. +- File structure has been simplified: all vlib modules were moved to the vlib/ directory, + makefile was moved to the root. +- One single archive with pre-built binaries for all operating systems. +- `mut var := val` was fixed (previously `mut var = val` was allowed as well). + + +## V 0.1.8 +*28 Jun 2019* +- Single file programs without `fn main` now work as expected. +- REPL has been fixed: it now supports imports, consts, function definitions, etc. + + +## V 0.1.7 +*27 Jun 2019* +- All C code in the compiler and vlib has been replaced with V. +- `#` syntax for embedding C code has been removed. +- Exported functions now need to be marked with `pub`, all public vlib functions have been updated. +- CI has been set up (Travis + Azure). On every commit and PR it is made sure that V + can compile itself, all tests pass, and all examples compile. +- More tests have been uploaded. +- Cleaner bytes to string conversion: `tos2(bytes)` => `string(bytes)`. +- The home page has 3 more examples next to 'hello world' that show the features of the language. +- Lots of bugs and issues fixed. diff --git a/v_windows/v/old/CODE_OF_CONDUCT.md b/v_windows/v/old/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..8fedba4 --- /dev/null +++ b/v_windows/v/old/CODE_OF_CONDUCT.md @@ -0,0 +1,4 @@ +# Code of Conduct + +Be nice and respectful. + diff --git a/v_windows/v/old/CONTRIBUTING.md b/v_windows/v/old/CONTRIBUTING.md new file mode 100644 index 0000000..afd4d00 --- /dev/null +++ b/v_windows/v/old/CONTRIBUTING.md @@ -0,0 +1,199 @@ +## Code Structure + +I tried to make the code of the compiler and vlib as simple and readable as +possible. One of V's goals is to be open to developers with different levels +of experience in compiler development. Compilers don't need to be black boxes +full of magic that only few people understand. + +The V compiler is modular, and can be used by other applications. It is located +in `cmd/v/` and `vlib/v/`. + +The most important and useful command to remember when working on the V compiler +is `v self`. +It rebuilds the V compiler. + +Be careful, if you introduce a breaking change and rebuild V, you will no longer +be able to use V to build itself. So it's a good idea to make a backup copy of a +working compiler executable. + +But don't worry, you can always simply run `make` (or `make.bat`), it will +download the C version of the compiler and rebuild it from scratch. + +The architecture of the compiler is very simple and has three distinct steps: + +Parse/generate AST (`v.parser`) => Check types (`v.checker`) +=> Generate C/JavaScript/machine code (`v.gen`) + + +The main files are: + +1. `cmd/v/v.v` The entry point. + +- V figures out the build mode. +- Constructs the compiler object (`struct V`). +- Creates a list of .v files that need to be parsed. +- Creates a parser object for each file and runs `parse()` on them. +- The correct backend is called (C, JS, native), and a binary is compiled. + +2. `v/scanner` The scanner's job is to parse a list of characters and convert +them to tokens. + +3. `v/token` This is simply a list of all tokens, their string values, and a +couple of helper functions. + +4. `v/parser` The parser. It converts a list of tokens into an AST. +In V, objects can be used before declaration, so unknown types are marked as +unresolved. They are resolved later in the type checker. + +5. `v/table` V creates one table object that is shared by all parsers. It +contains all types, consts, and functions, as well as several helpers to search +for objects by name, register new objects, modify types' fields, etc. + +6. `v/checker` Type checker and resolver. It processes the AST and makes sure +the types are correct. Unresolved types are resolved, type information is added +to the AST. + +7. `v/gen/c` C backend. It simply walks the AST and generates C code that can be +compiled with Clang, GCC, Visual Studio, and TCC. + +8. `json.v` defines the json code generation. This file will be removed once V +supports comptime code generation, and it will be possible to do this using the +language's tools. + +9. `v/gen/native` is the directory with all the machine code generation logic. It +defines a set of functions that translate assembly instructions to machine code +and build the binary from scratch byte by byte. It manually builds all headers, +segments, sections, symtable, relocations, etc. Right now it only has basic +support of the native platform (ELF, MACHO format). + +The rest of the directories are vlib modules: `builtin/` (strings, arrays, +maps), `time/`, `os/`, etc. Their documentation is pretty clear. + +## Example Workflow for Contributing +(provided by [@spytheman](https://github.com/spytheman)) + +(If you don't already have a GitHub account, please create one. Your GitHub +username will be referred to later as 'YOUR_GITHUB_USERNAME'. Change it +accordingly in the steps below.) + +1. Fork https://github.com/vlang/v using GitHub's interface to your own account. +Let's say that the forked repository is at +`https://github.com/YOUR_GITHUB_USERNAME/v` . +2. Clone the main v repository https://github.com/vlang/v to a local folder on +your computer, say named nv/ (`git clone https://github.com/vlang/v nv`) +3. `cd nv` +4. `git remote add pullrequest https://github.com/YOUR_GITHUB_USERNAME/v` +NB: the remote named `pullrequest` should point to YOUR own forked repo, not the +main v repository! After this, your local cloned repository is prepared for +making pullrequests, and you can just do normal git operations such as: +`git pull` `git status` and so on. + +5. When finished with a feature/bugfix/change, you can: +`git checkout -b fix_alabala` +6. `git push pullrequest` # (NOTE: the `pullrequest` remote was setup on step 4) +7. On GitHub's web interface, go to: https://github.com/vlang/v/pulls + + Here the UI shows a dialog with a button to make a new pull request based on + the new pushed branch. + (Example dialog: https://url4e.com/gyazo/images/364edc04.png) + +8. After making your pullrequest (aka, PR), you can continue to work on the +branch `fix_alabala` ... just do again `git push pullrequest` when you have more +commits. + +9. If there are merge conflicts, or a branch lags too much behind V's master, +you can do the following: + + 1. `git pull --rebase origin master` # solve conflicts and do + `git rebase --continue` + 2. `git push pullrequest -f` # this will overwrite your current remote branch + with the updated version of your changes. + +The point of doing the above steps, is to never directly push to the main V +repository, *only to your own fork*. Since your local `master` branch tracks the +main V repository's master, then `git checkout master`, as well as +`git pull --rebase origin master` will continue to work as expected +(these are actually used by `v up`) and git can always do it cleanly. + +Git is very flexible, so there are other ways to accomplish the same thing. +See the [GitHub flow](https://guides.github.com/introduction/git-handbook/#github), for more information. + +## Using Github's hub CLI tool + +You can download the `hub` tool from https://hub.github.com/ . Using +`hub`, you will not need to go through the (sometimes) slow website +to make PRs. Most remote operations can be done through the `hub` CLI +command. + +NB: You still need to have a GitHub account. + +### Preparation: +(steps 1..3 need to be done just *once*): + +1. `hub clone vlang/v my_v` +2. `cd my_v` +3. `hub fork --remote-name pullrequest` + +4. `git checkout -b my_cool_feature` # Step 4 is better done *once per each new +feature/bugfix* that you make. + +### Improve V by making commits: + +5. `git commit -am "math: add a new function copysign"` + +### Testing your commits locally: +You can test locally whether your changes have not broken something by +running: `v test-all`. See `TESTS.md` for more details. + +### Publishing your commits to GitHub: + +6. `git push pullrequest` + +### Making a PR with `hub`: +(so that your changes can be merged to the main V repository) + +7. `hub pull-request` + +Optionally, you can track the status of your PR CI tests with: + +8. `hub ci-status --verbose` + +### Fixing failing tests: +If everything is OK, after 5-10 minutes, the CI tests should pass for +all platforms. If not, visit the URLs for the failing CI jobs, see +which tests have failed and then fix them by making more changes. Just use +`git push pullrequest` to publish your changes. The CI tests will +run with your updated code. Use `hub ci-status --verbose` to monitor +their status. + +## Flags + +V allows you to pass custom flags using `-d my_flag` that can then be checked +at compile time (see the documentation about +[compile-time if](https://github.com/vlang/v/blob/master/doc/docs.md#compile-time-if)). +There are numerous flags that can be passed when building the compiler +with `v self` or when creating a copy of the compiler, that will help +you when debugging. + +Beware that the flags must be passed when building the compiler, +not the program, so do for example: `v -d time_parsing cmd/v` or +`v -d trace_checker self`. +Some flags can make the compiler very verbose, so it is recommended +to create a copy of the compiler rather than replacing it with `v self`. + +| Flag | Usage | +|------|-------| +| `debugautostr` | Prints informations about `.str()` method auto-generated by the compiler during C generation | +| `debugscanner` | Prints debug information during the scanning phase | +| `debug_codegen` | Prints automatically generated V code during the scanning phase | +| `debug_interface_table` | Prints generated interfaces during C generation | +| `debug_interface_type_implements` | Prints debug information when checking that a type implements in interface | +| `print_vweb_template_expansions` | Prints vweb compiled HTML files | +| `time_checking` | Prints the time spent checking files and other related informations | +| `time_parsing` | Prints the time spent parsing files and other related informations | +| `trace_ccoptions` | Prints options passed down to the C compiler | +| `trace_checker` | Prints informations about the statements being checked | +| `trace_gen` | Prints strings written to the generated C file. Beware, this flag is very verbose | +| `trace_parser` | Prints informations about parsed statements and expressions | +| `trace_thirdparty_obj_files` | Prints informations about built thirdparty obj files | +| `trace_use_cache` | Prints informations about cache use | diff --git a/v_windows/v/old/Dockerfile b/v_windows/v/old/Dockerfile new file mode 100644 index 0000000..ada540f --- /dev/null +++ b/v_windows/v/old/Dockerfile @@ -0,0 +1,27 @@ +#same container that golang use +FROM buildpack-deps:buster-curl + +LABEL maintainer="ANAGO Ronnel " +WORKDIR /opt/vlang + +ARG USE_LOCAL + +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends gcc clang make git && \ + apt-get clean && rm -rf /var/cache/apt/archives/* && \ + rm -rf /var/lib/apt/lists/* + +COPY . /vlang-local + +RUN if [ -z "${USE_LOCAL}" ] ; then \ + git clone https://github.com/vlang/v/ /opt/vlang && \ + rm -rf /vlang-local ; \ + else \ + mv /vlang-local/* . && \ + rm -rf /vlang-local ; \ + fi + +RUN make && \ + ln -s /opt/vlang/v /usr/local/bin/v + +CMD [ "v" ] diff --git a/v_windows/v/old/Dockerfile.alpine b/v_windows/v/old/Dockerfile.alpine new file mode 100644 index 0000000..a4bc1cb --- /dev/null +++ b/v_windows/v/old/Dockerfile.alpine @@ -0,0 +1,34 @@ +FROM alpine:3.12 + +LABEL maintainer="spytheman " + +WORKDIR /opt/vlang + +ENV VVV /opt/vlang +ENV PATH /opt/vlang:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin +ENV VFLAGS -cc gcc + +RUN mkdir -p /opt/vlang && ln -s /opt/vlang/v /usr/bin/v + +ARG USE_LOCAL + +RUN apk --no-cache add \ + git make upx gcc bash \ + musl-dev \ + openssl-dev sqlite-dev \ + libx11-dev glfw-dev freetype-dev + +## RUN apk --no-cache add --virtual sdl2deps sdl2-dev sdl2_ttf-dev sdl2_mixer-dev sdl2_image-dev +COPY . /vlang-local + +RUN if [[ -z "${USE_LOCAL}" ]] ; then \ + git clone https://github.com/vlang/v/ /opt/vlang && \ + rm -rf /vlang-local ; \ + else \ + mv /vlang-local/* . && \ + rm -rf /vlang-local ; \ + fi + +RUN make && v -version + +CMD ["v"] diff --git a/v_windows/v/old/Dockerfile.cross b/v_windows/v/old/Dockerfile.cross new file mode 100644 index 0000000..22a0ff0 --- /dev/null +++ b/v_windows/v/old/Dockerfile.cross @@ -0,0 +1,10 @@ +FROM mstorsjo/llvm-mingw + +LABEL maintainer="Vitaly Takmazov " +COPY . . +RUN make +RUN ./v -os windows -o v.c cmd/v +RUN x86_64-w64-mingw32-gcc v.c -std=c99 -w -municode -o v.exe +RUN file v.exe + +CMD [ "bash" ] diff --git a/v_windows/v/old/LICENSE b/v_windows/v/old/LICENSE new file mode 100644 index 0000000..ea31d17 --- /dev/null +++ b/v_windows/v/old/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019-2021 Alexander Medvednikov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/v_windows/v/old/Makefile b/v_windows/v/old/Makefile new file mode 100644 index 0000000..fd87662 --- /dev/null +++ b/v_windows/v/old/Makefile @@ -0,0 +1,154 @@ +CC ?= cc +CFLAGS ?= +LDFLAGS ?= +TMPDIR ?= /tmp +VROOT ?= . +VC ?= ./vc +V ?= ./v +VCREPO ?= https://github.com/vlang/vc +TCCREPO ?= https://github.com/vlang/tccbin + +VCFILE := v.c +TMPTCC := $(VROOT)/thirdparty/tcc +TCCOS := unknown +TCCARCH := unknown +GITCLEANPULL := git clean -xf && git pull --quiet +GITFASTCLONE := git clone --depth 1 --quiet --single-branch + +#### Platform detections and overrides: +_SYS := $(shell uname 2>/dev/null || echo Unknown) +_SYS := $(patsubst MSYS%,MSYS,$(_SYS)) +_SYS := $(patsubst MINGW%,MinGW,$(_SYS)) + +ifneq ($(filter $(_SYS),MSYS MinGW),) +WIN32 := 1 +V:=./v.exe +endif + +ifeq ($(_SYS),Linux) +LINUX := 1 +TCCOS := linux +endif + +ifeq ($(_SYS),Darwin) +MAC := 1 +TCCOS := macos +endif + +ifeq ($(_SYS),FreeBSD) +TCCOS := freebsd +LDFLAGS += -lexecinfo +endif + +ifeq ($(_SYS),NetBSD) +TCCOS := netbsd +LDFLAGS += -lexecinfo +endif + +ifdef ANDROID_ROOT +ANDROID := 1 +undefine LINUX +TCCOS := android +endif +##### + +ifdef WIN32 +TCCOS := windows +VCFILE := v_win.c +endif + +TCCARCH := $(shell uname -m 2>/dev/null || echo unknown) + +ifeq ($(TCCARCH),x86_64) + TCCARCH := amd64 +else +ifneq ($(filter x86%,$(TCCARCH)),) + TCCARCH := i386 +else +ifeq ($(TCCARCH),arm64) + TCCARCH := arm64 +else +ifneq ($(filter arm%,$(TCCARCH)),) + TCCARCH := arm +# otherwise, just use the arch name +endif +endif +endif +endif + +.PHONY: all clean fresh_vc fresh_tcc + +ifdef prod +VFLAGS+=-prod +endif + +all: latest_vc latest_tcc +ifdef WIN32 + $(CC) $(CFLAGS) -std=c99 -municode -w -o $(V) $(VC)/$(VCFILE) $(LDFLAGS) + $(V) -o v2.exe $(VFLAGS) cmd/v + move /y v2.exe v.exe +else + $(CC) $(CFLAGS) -std=gnu99 -w -o $(V) $(VC)/$(VCFILE) -lm -lpthread $(LDFLAGS) + $(V) -o v2.exe $(VFLAGS) cmd/v + mv -f v2.exe v +endif + @echo "V has been successfully built" + @$(V) -version + +clean: + rm -rf $(TMPTCC) + rm -rf $(VC) + +ifndef local +latest_vc: $(VC)/.git/config + cd $(VC) && $(GITCLEANPULL) +else +latest_vc: + @echo "Using local vc" +endif + +fresh_vc: + rm -rf $(VC) + $(GITFASTCLONE) $(VCREPO) $(VC) + +ifndef local +latest_tcc: $(TMPTCC)/.git/config + cd $(TMPTCC) && $(GITCLEANPULL) +else +latest_tcc: + @echo "Using local tcc" +endif + +fresh_tcc: + rm -rf $(TMPTCC) +ifndef local +# Check wether a TCC branch exists for the user's system configuration. +ifneq (,$(findstring thirdparty-$(TCCOS)-$(TCCARCH), $(shell git ls-remote --heads $(TCCREPO) | sed 's/^[a-z0-9]*\trefs.heads.//'))) + $(GITFASTCLONE) --branch thirdparty-$(TCCOS)-$(TCCARCH) $(TCCREPO) $(TMPTCC) +else + @echo 'Pre-built TCC not available for thirdparty-$(TCCOS)-$(TCCARCH) at $(TCCREPO), will use the system compiler: $(CC)' + $(GITFASTCLONE) --branch thirdparty-unknown-unknown $(TCCREPO) $(TMPTCC) +endif +else + @echo "Using local tccbin" +endif + +$(TMPTCC)/.git/config: + $(MAKE) fresh_tcc + +$(VC)/.git/config: + $(MAKE) fresh_vc + +asan: + $(MAKE) all CFLAGS='-fsanitize=address,undefined' + +selfcompile: + $(V) -cg -o v cmd/v + +selfcompile-static: + $(V) -cg -cflags '--static' -o v-static cmd/v + +### NB: Please keep this Makefile and make.bat simple. +install: + @echo 'Please use `sudo ./v symlink` instead.' + diff --git a/v_windows/v/old/README.md b/v_windows/v/old/README.md new file mode 100644 index 0000000..418fce5 --- /dev/null +++ b/v_windows/v/old/README.md @@ -0,0 +1,280 @@ +
+

+ +

+

The V Programming Language

+ +[vlang.io](https://vlang.io) | +[Docs](https://github.com/vlang/v/blob/master/doc/docs.md) | +[Changelog](https://github.com/vlang/v/blob/master/CHANGELOG.md) | +[Speed](https://fast.vlang.io/) | +[Contributing & compiler design](https://github.com/vlang/v/blob/master/CONTRIBUTING.md) + +
+
+ + +[![Sponsor][SponsorBadge]][SponsorUrl] +[![Patreon][PatreonBadge]][PatreonUrl] +[![Discord][DiscordBadge]][DiscordUrl] +[![Twitter][TwitterUrl]][TwitterBadge] + +
+ +## Key Features of V + +- Simplicity: the language can be learned in less than an hour +- Fast compilation: ≈110k loc/s with a Clang backend, + ≈1 million loc/s with native and tcc backends *(Intel i5-7500, SSD, no optimization)* ([demo video](https://www.youtube.com/watch?v=pvP6wmcl_Sc)) +- Easy to develop: V compiles itself in less than a second +- Performance: as fast as C (V's main backend compiles to human readable C) +- Safety: no null, no globals, no undefined behavior, immutability by default +- C to V translation +- Hot code reloading +- [Innovative memory management](https://vlang.io/#memory) ([demo video](https://www.youtube.com/watch?v=gmB8ea8uLsM)) +- [Cross-platform UI library](https://github.com/vlang/ui) +- Built-in graphics library +- Easy cross compilation +- REPL +- [Built-in ORM](https://github.com/vlang/v/blob/master/doc/docs.md#orm) +- [Built-in web framework](https://github.com/vlang/v/blob/master/vlib/vweb/README.md) +- C and JavaScript backends + +## Stability guarantee and future changes + +Despite being at an early development stage, the V language is relatively stable and has +backwards compatibility guarantee, meaning that the code you write today is guaranteed +to work a month, a year, or five years from now. + +There still may be minor syntax changes before the 1.0 release, but they will be handled +automatically via `vfmt`, as has been done in the past. + +The V core APIs (primarily the `os` module) will still have minor changes until +they are stabilized in V 1.0. Of course the APIs will grow after that, but without breaking +existing code. + +Unlike many other languages, V is not going to be always changing, with new features +being introduced and old features modified. It is always going to be a small and simple +language, very similar to the way it is right now. + +## Installing V from source + +### Linux, macOS, Windows, *BSD, Solaris, WSL, Android, Raspbian + +```bash +git clone https://github.com/vlang/v +cd v +make +``` + +That's it! Now you have a V executable at `[path to V repo]/v`. +`[path to V repo]` can be anywhere. + +(On Windows `make` means running `make.bat`, so make sure you use `cmd.exe`) + +Now you can try `./v run examples/hello_world.v` (`v.exe` on Windows). + +V is constantly being updated. To update V, simply run: + +```bash +v up +``` + +### C compiler + +It's recommended to use Clang, GCC, or Visual Studio. +If you are doing development, you most likely already have one of those installed. + +Otherwise, follow these instructions: + +- [Installing a C compiler on Linux and macOS](https://github.com/vlang/v/wiki/Installing-a-C-compiler-on-Linux-and-macOS) + +- [Installing a C compiler on Windows](https://github.com/vlang/v/wiki/Installing-a-C-compiler-on-Windows) + +However, if none is found when running `make` on Linux or Windows, +TCC is downloaded as the default C backend. +It's very lightweight (several MB) so this shouldn't take too long. + +### Symlinking + +NB: it is *highly recommended*, that you put V on your PATH. That saves +you the effort to type in the full path to your v executable every time. +V provides a convenience `v symlink` command to do that more easily. + +On Unix systems, it creates a `/usr/local/bin/v` symlink to your +executable. To do that, run: + +```bash +sudo ./v symlink +``` + +On Windows, start a new shell with administrative privileges, for +example by Windows Key, then type `cmd.exe`, right click on its menu +entry, and choose `Run as administrator`. In the new administrative +shell, cd to the path, where you have compiled v.exe, then type: + +```bat +.\v.exe symlink +``` + +That will make V available everywhere, by adding it to your PATH. +Please restart your shell/editor after that, so that it can pick +the new PATH variable. + +NB: there is no need to run `v symlink` more than once - v will +continue to be available, even after `v up`, restarts and so on. +You only need to run it again, if you decide to move the V repo +folder somewhere else. + +### Docker + +
Expand Docker instructions + +```bash +git clone https://github.com/vlang/v +cd v +docker build -t vlang . +docker run --rm -it vlang:latest +``` + +### Docker with Alpine/musl + +```bash +git clone https://github.com/vlang/v +cd v +docker build -t vlang --file=Dockerfile.alpine . +docker run --rm -it vlang:latest +``` + +
+ +### Testing and running the examples + +Make sure V can compile itself: + +```bash +v self +``` + +```bash +$ v +V 0.2.x +Use Ctrl-C or `exit` to exit + +>>> println('hello world') +hello world +>>> +``` + +```bash +cd examples +v hello_world.v && ./hello_world # or simply +v run hello_world.v # this builds the program and runs it right away + +v run word_counter/word_counter.v word_counter/cinderella.txt +v run news_fetcher.v +v run tetris/tetris.v +``` + + + +NB: In order to build Tetris or 2048 (or anything else using `sokol` or `gg` graphics modules) +on some Linux systems, you need to install `libxi-dev` and `libxcursor-dev` . + +If you plan to use the http package, you also need to install OpenSSL on non-Windows systems. + +```bash +macOS: +brew install openssl + +Debian/Ubuntu: +sudo apt install libssl-dev + +Arch/Manjaro: +openssl is installed by default + +Fedora: +sudo dnf install openssl-devel +``` + +## V sync +V's `sync` module and channel implementation uses libatomic. +It is most likely already installed on your system, but if not, +you can install it, by doing the following: +```bash +MacOS: already installed + +Debian/Ubuntu: +sudo apt install libatomic1 + +Fedora/CentOS/RH: +sudo dnf install libatomic-static +``` + +## V UI + + + + + +https://github.com/vlang/ui + + + +## Android graphical apps + +With V's `vab` tool, building V UI and graphical apps for Android can become as easy as: + +``` +./vab /path/to/v/examples/2048 +``` + +[https://github.com/vlang/vab](https://github.com/vlang/vab). + + + +## Developing web applications + +Check out the [Building a simple web blog](https://github.com/vlang/v/blob/master/tutorials/building_a_simple_web_blog_with_vweb/README.md) +tutorial and Gitly, a light and fast alternative to GitHub/GitLab: + +https://github.com/vlang/gitly + + + +## Troubleshooting + +Please see the [Troubleshooting](https://github.com/vlang/v/wiki/Troubleshooting) section on our [wiki page](https://github.com/vlang/v/wiki) + +[WorkflowBadge]: https://github.com/vlang/v/workflows/CI/badge.svg +[DiscordBadge]: https://img.shields.io/discord/592103645835821068?label=Discord&logo=discord&logoColor=white +[PatreonBadge]: https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fshieldsio-patreon.vercel.app%2Fapi%3Fusername%3Dvlang%26type%3Dpledges +[SponsorBadge]: https://camo.githubusercontent.com/da8bc40db5ed31e4b12660245535b5db67aa03ce/68747470733a2f2f696d672e736869656c64732e696f2f7374617469632f76313f6c6162656c3d53706f6e736f72266d6573736167653d254532253944254134266c6f676f3d476974487562 +[TwitterBadge]: https://twitter.com/v_language + +[WorkflowUrl]: https://github.com/vlang/v/commits/master +[DiscordUrl]: https://discord.gg/vlang +[PatreonUrl]: https://patreon.com/vlang +[SponsorUrl]: https://github.com/sponsors/medvednikov +[TwitterUrl]: https://img.shields.io/twitter/follow/v_language.svg?style=flatl&label=Follow&logo=twitter&logoColor=white&color=1da1f2 diff --git a/v_windows/v/old/ROADMAP.md b/v_windows/v/old/ROADMAP.md new file mode 100644 index 0000000..ae3300f --- /dev/null +++ b/v_windows/v/old/ROADMAP.md @@ -0,0 +1,32 @@ +## [Version 0.3](https://github.com/vlang/v/projects/5) +- [ ] [make `-autofree` the default](https://github.com/vlang/v/issues/6989) +- [x] [gc option] +- [ ] [coroutines](https://github.com/vlang/v/issues/561) +- [x] channels +- [x] lock{} +- [x] thread safe arrays +- [ ] [thread safe maps](https://github.com/vlang/v/issues/6992) +- [ ] [C2V translator](https://github.com/vlang/v/issues/6985) +- [ ] doom.v +- [x] rune type +- [x] replace `ustring` with `[]rune` +- [x] fix `byte.str()` +- [x] maps with non-string keys +- [x] iOS/Android support +- [ ] parallel parser +- [ ] parallel checker +- [ ] parallel cgen +- [ ] `recover()` from panics +- [x] IO streams +- [x] struct embedding +- [ ] interface embedding +- [x] interfaces: allow struct fields (not just methods) +- [ ] vfmt: fix common errors automatically (make vars mutable and vice versa, add missing imports) +- [ ] method expressions with an explicit receiver as the first argument +- [x] short generics syntax (`foo(5)` instead of `foo(5)`) +- [ ] fix all remaining generics issues +- [ ] merge v.c and v_win.c +- [x] more advanced errors, not just `error('message')` +- [ ] VLS for autocomplete, refactoring, go to definition etc +- [ ] Recursive structs via optionals: `struct Node { next ?Node }` +- [ ] Remove `foo = 0` for `&Foo` diff --git a/v_windows/v/old/TESTS.md b/v_windows/v/old/TESTS.md new file mode 100644 index 0000000..92b4db2 --- /dev/null +++ b/v_windows/v/old/TESTS.md @@ -0,0 +1,115 @@ +# Automated tests + +TLDR: run `v test-all` locally, after making your changes, +and before submitting PRs. + +## Notes +In the `v` repo there are several different tests. The main types are: + +* `_test.v` tests - check that `test_` functions succeed. These can be +run per directory or individually. +* `.out` tests - run a `.vv` file and check the output matches the +contents of the `.out` file with the same base name. This is +particularly useful for checking that errors are printed. + +Tip: use `v -cc tcc` when compiling tests for speed. + +## `vlib/v/tests` + +General runnable tests for different features of the V compiler. + +* `vlib/v/tests/inout/compiler_test.v` + +Test output of running a V program matches an expected .out file. +Check the source for how to test panics. + +* `vlib/v/gen/c/coutput_test.v` + +This tests whether the generated C source code matches all expectations, +specified in *.c.must_have files, in the folder vlib/v/gen/c/testdata/ . + +Each `.c.must_have` file has to have a corresponding .vv file. + +Each `.c.must_have` file, consists of multiple lines. Each of these +lines, *should* be present *at least once* in the output, when the .vv +file is compiled with `-o -` . + +* `vlib/v/tests/run_project_folders_test.v` +Tests whether whole project folders can be compiled, and run. +NB: Each project in these folders, should finish with exit code 0, +and it should output `OK` as its last stdout line. + +## Test building of actual V programs (examples, tools, V itself) + +* `v build-tools` +* `v build-examples` +* `v build-vbinaries` + +## vfmt tests + +In `vlib/v/fmt/` there are:: + +* `v vlib/v/fmt/fmt_test.v` + +This checks `.out` tests. + +* `v vlib/v/fmt/fmt_keep_test.v` + +This verifies that `_keep.v` files would be unchanged by `vfmt -w`. + +* `v vlib/v/fmt/fmt_vlib_test.v` + +This checks all source files are formatted and prints a summary. +This is not required. + +* `v test-fmt` + +Test all files in the current directory are formatted. + +## Markdown + +* `v check-md -hide-warnings .` + +Ensure that all .md files in the project are formatted properly, +and that the V code block examples in them can be compiled/formatted too. + +## `.github/workflows/ci.yml` + +This runs various CI tests, e.g.: + +* `v vet vlib/v` - style checker +* `v fmt -verify` on certain source files + +## `v test-cleancode` + +Check that most .v files, are invariant of `v fmt` runs. + +## `v test-self` + +Run `vlib` module tests, *including* the compiler tests. + +## `v vlib/v/compiler_errors_test.v` + +This runs tests for: +* `vlib/v/checker/tests/*.vv` +* `vlib/v/parser/tests/*.vv` + +### Special folders that compiler_errors_test.v will try to +run/compile with specific options: + +vlib/v/checker/tests/globals_run/ - `-enable-globals run`; +results stored in `.run.out` files, matching the .vv ones. + +## `v test-all` + +Test and build *everything*. Usefull to verify *locally*, that the CI will +most likely pass. Slowest, but most comprehensive. + +It works, by running these in succession: +* `v test-cleancode` +* `v test-self` +* `v test-fmt` +* `v build-tools` +* `v build-examples` +* `v check-md -hide-warnings .` +* `v install nedpals.args` diff --git a/v_windows/v/old/cmd/tools/bench/wyhash.v b/v_windows/v/old/cmd/tools/bench/wyhash.v new file mode 100644 index 0000000..b760ad6 --- /dev/null +++ b/v_windows/v/old/cmd/tools/bench/wyhash.v @@ -0,0 +1,56 @@ +module main + +import hash.fnv1a +import hash as wyhash +import rand +import benchmark + +fn main() { + rand.seed([u32(42), 0]) + sample_size := 10000000 + min_str_len := 20 + max_str_len := 40 + println('Generating $sample_size strings between $min_str_len - $max_str_len chars long...') + mut checksum := u64(0) + mut start_pos := 0 + mut bgenerating := benchmark.start() + mut bytepile := []byte{} + for _ in 0 .. sample_size * max_str_len { + bytepile << byte(rand.int_in_range(40, 125)) + } + mut str_lens := []int{} + for _ in 0 .. sample_size { + str_lens << rand.int_in_range(min_str_len, max_str_len) + } + bgenerating.measure('generating strings') + println('Hashing each of the generated strings...') + // + mut bhashing_1 := benchmark.start() + start_pos = 0 + checksum = 0 + for len in str_lens { + end_pos := start_pos + len + checksum ^= wyhash.wyhash_c(unsafe { &byte(bytepile.data) + start_pos }, u64(len), + 1) + start_pos = end_pos + } + bhashing_1.measure('wyhash.wyhash_c | checksum: ${checksum:22}') + mut bhashing_2 := benchmark.start() + start_pos = 0 + checksum = 0 + for len in str_lens { + end_pos := start_pos + len + checksum ^= wyhash.sum64(bytepile[start_pos..end_pos], 1) + start_pos = end_pos + } + bhashing_2.measure('wyhash.sum64 | checksum: ${checksum:22}') + mut bhashing_3 := benchmark.start() + start_pos = 0 + checksum = 0 + for len in str_lens { + end_pos := start_pos + len + checksum ^= fnv1a.sum64(bytepile[start_pos..end_pos]) + start_pos = end_pos + } + bhashing_3.measure('fnv1a.sum64 | checksum: ${checksum:22}') +} diff --git a/v_windows/v/old/cmd/tools/check_os_api_parity.v b/v_windows/v/old/cmd/tools/check_os_api_parity.v new file mode 100644 index 0000000..d4b925c --- /dev/null +++ b/v_windows/v/old/cmd/tools/check_os_api_parity.v @@ -0,0 +1,130 @@ +module main + +import os +import v.util +import v.util.diff +import v.pref +import v.builder +import v.ast +import rand +import term + +const ( + base_os = 'linux' + os_names = ['linux', 'macos', 'windows'] + skip_modules = [ + 'builtin.bare', + 'builtin.linux_bare.old', + 'builtin.js', + 'strconv', + 'strconv.ftoa', + 'hash', + 'strings', + 'crypto.rand', + 'os.bare', + 'os2', + 'picohttpparser', + 'picoev', + 'szip', + 'v.eval', + ] +) + +struct App { + diff_cmd string + is_verbose bool + modules []string +mut: + api_differences map[string]int +} + +fn main() { + vexe := pref.vexe_path() + vroot := os.dir(vexe) + util.set_vroot_folder(vroot) + os.chdir(vroot) + cmd := diff.find_working_diff_command() or { '' } + mut app := App{ + diff_cmd: cmd + is_verbose: os.getenv('VERBOSE').len > 0 + modules: if os.args.len > 1 { os.args[1..] } else { all_vlib_modules() } + } + for mname in app.modules { + if !app.is_verbose { + eprintln('Checking module: $mname ...') + } + api_base := app.gen_api_for_module_in_os(mname, base_os) + for oname in os_names { + if oname == base_os { + continue + } + api_os := app.gen_api_for_module_in_os(mname, oname) + app.compare_api(api_base, api_os, mname, base_os, oname) + } + } + howmany := app.api_differences.len + if howmany > 0 { + eprintln(term.header('Found $howmany modules with different APIs', '=')) + for m in app.api_differences.keys() { + eprintln('Module: $m') + } + exit(1) + } +} + +fn all_vlib_modules() []string { + mut vlib_v_files := os.walk_ext('vlib', '.v') + mut vmodulesmap := map[string]int{} + for f in vlib_v_files { + if f.contains('/tests/') || f.ends_with('_test.v') { + continue + } + vmodulename := os.dir(f).replace('/', '.').replace('vlib.', '') + if vmodulename in skip_modules { + continue + } + vmodulesmap[vmodulename] = vmodulesmap[vmodulename] + 1 + } + mut modules := vmodulesmap.keys() + modules.sort() + return modules +} + +fn (app App) gen_api_for_module_in_os(mod_name string, os_name string) string { + if app.is_verbose { + eprintln('Checking module: ${mod_name:-30} for OS: ${os_name:-10} ...') + } + mpath := os.join_path('vlib', mod_name.replace('.', '/')) + tmpname := '/tmp/${mod_name}_${os_name}.c' + prefs, _ := pref.parse_args([], ['-os', os_name, '-o', tmpname, '-shared', mpath]) + mut b := builder.new_builder(prefs) + b.compile_c() + mut res := []string{} + for f in b.parsed_files { + for s in f.stmts { + if s is ast.FnDecl { + if s.is_pub { + fn_signature := s.stringify(b.table, mod_name, map[string]string{}) + fn_mod := s.modname() + if fn_mod == mod_name { + fline := '$fn_mod: $fn_signature' + res << fline + } + } + } + } + } + res.sort() + return res.join('\n') +} + +fn (mut app App) compare_api(api_base string, api_os string, mod_name string, os_base string, os_target string) { + res := diff.color_compare_strings(app.diff_cmd, rand.ulid(), api_base, api_os) + if res.len > 0 { + summary := 'Different APIs found for module: `$mod_name`, between OS base: `$os_base` and OS: `$os_target`' + eprintln(term.header(summary, '-')) + eprintln(res) + eprintln(term.h_divider('-')) + app.api_differences[mod_name] = 1 + } +} diff --git a/v_windows/v/old/cmd/tools/fast/.gitignore b/v_windows/v/old/cmd/tools/fast/.gitignore new file mode 100644 index 0000000..1efae28 --- /dev/null +++ b/v_windows/v/old/cmd/tools/fast/.gitignore @@ -0,0 +1,5 @@ +fast +index.html +table.html +v.c +v2 diff --git a/v_windows/v/old/cmd/tools/fast/fast.v b/v_windows/v/old/cmd/tools/fast/fast.v new file mode 100644 index 0000000..0b9f05a --- /dev/null +++ b/v_windows/v/old/cmd/tools/fast/fast.v @@ -0,0 +1,178 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +import os +import time + +// TODO -usecache +const voptions = ' -skip-unused -show-timings -stats ' + +const exe = os.executable() + +const fast_dir = os.dir(exe) + +const vdir = @VEXEROOT + +fn main() { + dump(fast_dir) + dump(vdir) + os.chdir(fast_dir) + if !os.exists('$vdir/v') && !os.is_dir('$vdir/vlib') { + println('fast.html generator needs to be located in `v/cmd/tools/fast`') + } + println('fast.html generator\n') + println('Fetching updates...') + ret := os.system('$vdir/v up') + if ret != 0 { + println('failed to update V') + return + } + // Fetch the last commit's hash + commit := exec('git rev-parse HEAD')[..8] + if !os.exists('table.html') { + os.create('table.html') ? + } + mut table := os.read_file('table.html') ? + if table.contains('>$commit<') { + println('nothing to benchmark') + exit(1) + return + } + // for i, commit in commits { + message := exec('git log --pretty=format:"%s" -n1 $commit') + // println('\n${i + 1}/$commits.len Benchmarking commit $commit "$message"') + println('\nBenchmarking commit $commit "$message"') + // Build an optimized V + // println('Checking out ${commit}...') + // exec('git checkout $commit') + println(' Building vprod...') + os.chdir(vdir) + exec('./v -o vprod -prod -prealloc cmd/v') + // println('cur vdir="$vdir"') + // exec('v -o vprod cmd/v') // for faster debugging + // cache vlib modules + exec('$vdir/v wipe-cache') + exec('$vdir/v -o v2 -prod cmd/v') + // measure + diff1 := measure('$vdir/vprod $voptions -o v.c cmd/v', 'v.c') + mut tcc_path := 'tcc' + $if freebsd { + tcc_path = '/usr/local/bin/tcc' + } + diff2 := measure('$vdir/vprod $voptions -cc $tcc_path -o v2 cmd/v', 'v2') + diff3 := 0 // measure('$vdir/vprod -native $vdir/cmd/tools/1mil.v', 'native 1mil') + diff4 := measure('$vdir/vprod -usecache $voptions -cc clang examples/hello_world.v', + 'hello.v') + vc_size := os.file_size('v.c') / 1000 + // scan/parse/check/cgen + scan, parse, check, cgen, vlines := measure_steps(vdir) + // println('Building V took ${diff}ms') + commit_date := exec('git log -n1 --pretty="format:%at" $commit') + date := time.unix(commit_date.int()) + // + os.chdir(fast_dir) + mut out := os.create('table.html') ? + // Place the new row on top + html_message := message.replace_each(['<', '<', '>', '>']) + table = + ' + $date.format() + $commit + $html_message + ${diff1}ms + ${diff2}ms + ${diff3}ms + ${diff4}ms + $vc_size KB + ${parse}ms + ${check}ms + ${cgen}ms + ${scan}ms + $vlines + ${int(f64(vlines) / f64(diff1) * 1000.0)} + \n' + + table.trim_space() + out.writeln(table) ? + out.close() + // Regenerate index.html + header := os.read_file('header.html') ? + footer := os.read_file('footer.html') ? + mut res := os.create('index.html') ? + res.writeln(header) ? + res.writeln(table) ? + res.writeln(footer) ? + res.close() + //} + // exec('git checkout master') + // os.write_file('last_commit.txt', commits[commits.len - 1]) ? +} + +fn exec(s string) string { + e := os.execute_or_exit(s) + return e.output.trim_right('\r\n') +} + +// returns milliseconds +fn measure(cmd string, description string) int { + println(' Measuring $description') + println(' Warming up...') + println(cmd) + for _ in 0 .. 3 { + exec(cmd) + } + println(' Building...') + mut runs := []int{} + for r in 0 .. 5 { + println(' Sample ${r + 1}/5') + sw := time.new_stopwatch() + exec(cmd) + runs << int(sw.elapsed().milliseconds()) + } + // discard lowest and highest time + runs.sort() + runs = runs[1..4] + mut sum := 0 + for run in runs { + sum += run + } + return int(sum / 3) +} + +fn measure_steps(vdir string) (int, int, int, int, int) { + resp := os.execute_or_exit('$vdir/vprod $voptions -o v.c cmd/v') + + mut scan, mut parse, mut check, mut cgen, mut vlines := 0, 0, 0, 0, 0 + lines := resp.output.split_into_lines() + if lines.len == 3 { + parse = lines[0].before('.').int() + check = lines[1].before('.').int() + cgen = lines[2].before('.').int() + } else { + ms_lines := lines.map(it.split(' ms ')) + for line in ms_lines { + if line.len == 2 { + if line[1] == 'SCAN' { + scan = line[0].int() + } + if line[1] == 'PARSE' { + parse = line[0].int() + } + if line[1] == 'CHECK' { + check = line[0].int() + } + if line[1] == 'C GEN' { + cgen = line[0].int() + } + } else { + // Fetch number of V lines + if line[0].contains('V') && line[0].contains('source') && line[0].contains('size') { + start := line[0].index(':') or { 0 } + end := line[0].index('lines,') or { 0 } + s := line[0][start + 1..end] + vlines = s.trim_space().int() + } + } + } + } + return scan, parse, check, cgen, vlines +} diff --git a/v_windows/v/old/cmd/tools/fast/fast_job.v b/v_windows/v/old/cmd/tools/fast/fast_job.v new file mode 100644 index 0000000..d4d8fc3 --- /dev/null +++ b/v_windows/v/old/cmd/tools/fast/fast_job.v @@ -0,0 +1,43 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +import os +import time + +// A job that runs in the background, checks for repo updates, +// runs fast.v, pushes the HTML result to the fast.vlang.io GH pages repo. +fn main() { + println(time.now()) + if !os.exists('website') { + println('cloning the website repo...') + os.system('git clone git@github.com:/vlang/website.git') + } + if !os.exists('fast') { + println('"fast" binary (built with `v fast.v`) was not found') + return + } + for { + res_pull := os.execute('git pull --rebase') + if res_pull.exit_code != 0 { + println('failed to git pull. uncommitted changes?') + return + } + // println('running ./fast') + resp := os.execute('./fast') + if resp.exit_code < 0 { + println(resp.output) + return + } + if resp.exit_code != 0 { + println('resp != 0, skipping') + } else { + os.chdir('website') + os.execute_or_exit('git checkout gh-pages') + os.cp('../index.html', 'index.html') ? + os.system('git commit -am "update benchmark"') + os.system('git push origin gh-pages') + os.chdir('..') + } + time.sleep(180 * time.second) + } +} diff --git a/v_windows/v/old/cmd/tools/fast/fast_main.js b/v_windows/v/old/cmd/tools/fast/fast_main.js new file mode 100644 index 0000000..d6b2d19 --- /dev/null +++ b/v_windows/v/old/cmd/tools/fast/fast_main.js @@ -0,0 +1,67 @@ +const delta = 18; + +(function () { + var table = document.querySelector("table"); + var isTbody = table.children[0].nodeName == "TBODY"; + var trs = isTbody + ? table.children[0].querySelectorAll("tr") + : table.querySelectorAll("tr"); + trs.forEach(function (tr, idx) { + if (idx != 0 && idx + 1 < trs.length) { + var vc = 3, vv = 4, vf = 5, vh = 6; + var textContent = { + vc: tr.children[vc].textContent, + vv: tr.children[vv].textContent, + vf: tr.children[vf].textContent, + vh: tr.children[vh].textContent + }; + var currentData = { + vc: int(textContent.vc.slice(0, -2)), + vv: int(textContent.vv.slice(0, -2)), + vf: int(textContent.vf.slice(0, -2)), + vh: int(textContent.vh.slice(0, -2)) + }; + var prevData = { + vc: int(trs[idx + 1].children[vc].textContent.slice(0, -2)), + vv: int(trs[idx + 1].children[vv].textContent.slice(0, -2)), + vf: int(trs[idx + 1].children[vf].textContent.slice(0, -2)), + vh: int(trs[idx + 1].children[vh].textContent.slice(0, -2)) + }; + var result = { + vc: currentData.vc - prevData.vc, + vv: currentData.vv - prevData.vv, + vf: currentData.vf - prevData.vf, + vh: currentData.vh - prevData.vh + }; + if (Math.abs(result.vc) > delta) + tr.children[vc].appendChild(createElement(result.vc)); + if (Math.abs(result.vv) > delta * 2) + tr.children[vv].appendChild(createElement(result.vv)); + if (Math.abs(result.vf) > delta * 2) + tr.children[vf].appendChild(createElement(result.vf)); + if (Math.abs(result.vh) > delta * 2) + tr.children[vh].appendChild(createElement(result.vh)); + } + }); + function int(src) { + return src - 0; + } + function getClassName(x) { + if (x == 0) + return "equal"; + return x < 0 ? "plus" : "minus"; + } + function createElement(result) { + var el = document.createElement("span"); + var parsedResult = parseResult(result); + el.classList.add("diff"); + el.classList.add(getClassName(result)); + el.textContent = parsedResult; + return el; + } + function parseResult(x) { + if (x == 0) + return "0"; + return x > 0 ? "+" + x : x; + } +})(); diff --git a/v_windows/v/old/cmd/tools/fast/footer.html b/v_windows/v/old/cmd/tools/fast/footer.html new file mode 100644 index 0000000..37f5c0f --- /dev/null +++ b/v_windows/v/old/cmd/tools/fast/footer.html @@ -0,0 +1,4 @@ + + + + diff --git a/v_windows/v/old/cmd/tools/fast/header.html b/v_windows/v/old/cmd/tools/fast/header.html new file mode 100644 index 0000000..8f4ee5c --- /dev/null +++ b/v_windows/v/old/cmd/tools/fast/header.html @@ -0,0 +1,65 @@ + + + + + +Is V still fast? + + + +

Is V still fast?

+ +Monitoring compilation speed for each commit.

+Running on a free tier AWS t2.micro instance (1 vCPU). Typical desktop hardware is 2-3 times faster.

+Source code: fast.v

+ + + + + + + + + + + + + + + + + + + + diff --git a/v_windows/v/old/cmd/tools/fuzz/fuzz.sh b/v_windows/v/old/cmd/tools/fuzz/fuzz.sh new file mode 100644 index 0000000..cbba7d7 --- /dev/null +++ b/v_windows/v/old/cmd/tools/fuzz/fuzz.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +cores=$(nproc --all) + +echo Number of cores: $cores +echo Compiling... +./v -cc clang -o cmd/tools/fuzz/map_fuzz cmd/tools/fuzz/map_fuzz.v -prod -cflags "-fsanitize=memory" + +echo Fuzzing: +while true +do + for ((i=1;i<=cores;++i)) + do + sleep 0.001 + ./cmd/tools/fuzz/map_fuzz & + done + wait +done diff --git a/v_windows/v/old/cmd/tools/fuzz/map_fuzz.v b/v_windows/v/old/cmd/tools/fuzz/map_fuzz.v new file mode 100644 index 0000000..f1c62f2 --- /dev/null +++ b/v_windows/v/old/cmd/tools/fuzz/map_fuzz.v @@ -0,0 +1,144 @@ +import rand +import time + +fn generate_strings(str_len int, arr_len int) []string { + mut arr := []string{len: arr_len} + for i in 0 .. arr_len { + arr[i] = rand.string(str_len) + } + return arr +} + +fn fuzz1() { + amount := 200000 - rand.intn(100000) + amount2 := 200000 - rand.intn(100000) + len := 25 - rand.intn(10) + arr := generate_strings(len, amount) + arr2 := generate_strings(len, amount2) + mut m := map[string]int{} + for i in 0 .. amount { + m[arr[i]] = i + assert i == m[arr[i]] + } + for i in 0 .. amount { + assert i == m[arr[i]] + } + for i in 0 .. amount2 { + assert 0 == m[arr2[i]] + } + unsafe { + m.free() + arr.free() + } +} + +fn fuzz2() { + mut m := map[string]int{} + amount := rand.intn(500000) + 1 + len := 25 - rand.intn(10) + arr := generate_strings(len, amount) + for i, x in arr { + m[x] = i + } + mut i := 0 + for key, val in m { + assert key == arr[i] + assert val == i + i++ + } + unsafe { + m.free() + arr.free() + } +} + +fn fuzz3() { + mut m := map[string]int{} + amount := rand.intn(500000) + 1 + len := 25 - rand.intn(10) + arr := generate_strings(len, amount) + for i, x in arr { + if (i % 10000) == 0 { + keys := m.keys() + assert keys.len == i + assert keys == arr[0..i] + } + m[x] = i + } + assert m.keys() == arr + assert m.keys().len == amount + unsafe { + m.free() + arr.free() + } +} + +fn fuzz4() { + amount := rand.intn(500000) + len := 25 - rand.intn(10) + arr := generate_strings(len, amount) + mut m := map[string]int{} + for i in 0 .. amount { + m[arr[i]] = i + } + for i in 0 .. amount { + m.delete(arr[i]) + assert m[arr[i]] == 0 + } + assert m.len == 0 + unsafe { + m.free() + arr.free() + } +} + +fn fuzz5() { + amount := rand.intn(500000) + 1 + arr := generate_strings(20, amount) + mut m := map[string]int{} + for i in 0 .. amount { + m[arr[i]] = i + assert (arr[i] in m) == true + } + for i in 0 .. amount { + m.delete(arr[i]) + assert (arr[i] !in m) == true + assert m.len == amount - i - 1 + } + assert m.len == 0 + unsafe { + m.free() + arr.free() + } +} + +fn fuzz6() { + mut m := map[string]int{} + amount := rand.intn(500000) + 1 + len := 25 - rand.intn(10) + arr := generate_strings(len, amount) + for i, x in arr { + m[x]++ + m[x] += i + assert m[x] == i + 1 + } + for i, x in arr { + assert m[x] == i + 1 + } + unsafe { + m.free() + arr.free() + } +} + +fn main() { + seed := u32(time.ticks()) + println('seed: $seed.hex()') + rand.seed([seed, seed]) + fuzz1() + fuzz2() + fuzz3() + fuzz4() + fuzz5() + fuzz6() +} diff --git a/v_windows/v/old/cmd/tools/gen1m.v b/v_windows/v/old/cmd/tools/gen1m.v new file mode 100644 index 0000000..2352c9f --- /dev/null +++ b/v_windows/v/old/cmd/tools/gen1m.v @@ -0,0 +1,16 @@ +fn main() { + for i in 0 .. 100000 { + println(' +fn foo${i}() { + x := $i + mut a := 1 + a += 2 + print(a) + a = 0 + a = 1 +} +') + } + // println('fn main() {foo1()} ') + println('fn main() { println("1m DONE") } ') +} diff --git a/v_windows/v/old/cmd/tools/gen_vc.v b/v_windows/v/old/cmd/tools/gen_vc.v new file mode 100644 index 0000000..c2e8746 --- /dev/null +++ b/v_windows/v/old/cmd/tools/gen_vc.v @@ -0,0 +1,370 @@ +module main + +import os +import log +import flag +import time +import vweb +import net.urllib + +// This tool regenerates V's bootstrap .c files +// every time the V master branch is updated. +// if run with the --serve flag it will run in webhook +// server mode awaiting a request to http://host:port/genhook +// available command line flags: +// --work-dir gen_vc's working directory +// --purge force purge the local repositories +// --serve run in webhook server mode +// --port port for http server to listen on +// --log-to either 'file' or 'terminal' +// --log-file path to log file used when --log-to is 'file' +// --dry-run dont push anything to remote repo +// --force force update even if already up to date + +// git credentials +const ( + git_username = os.getenv('GITUSER') + git_password = os.getenv('GITPASS') +) + +// repository +const ( + // git repo + git_repo_v = 'github.com/vlang/v' + git_repo_vc = 'github.com/vlang/vc' + // local repo directories + git_repo_dir_v = 'v' + git_repo_dir_vc = 'vc' +) + +// gen_vc +const ( + // name + app_name = 'gen_vc' + // version + app_version = '0.1.2' + // description + app_description = "This tool regenerates V's bootstrap .c files every time the V master branch is updated." + // assume something went wrong if file size less than this + too_short_file_limit = 5000 + // create a .c file for these os's + vc_build_oses = [ + 'nix', + // all nix based os + 'windows', + ] +) + +// default options (overridden by flags) +const ( + // gen_vc working directory + work_dir = '/tmp/gen_vc' + // dont push anything to remote repo + dry_run = false + // server port + server_port = 7171 + // log file + log_file = '$work_dir/log.txt' + // log_to is either 'file' or 'terminal' + log_to = 'terminal' +) + +// errors +const ( + err_msg_build = 'error building' + err_msg_make = 'make failed' + err_msg_gen_c = 'failed to generate .c file' + err_msg_cmd_x = 'error running cmd' +) + +struct GenVC { + // logger + // flag options + options FlagOptions +mut: + logger &log.Log + // true if error was experienced running generate + gen_error bool +} + +// webhook server +struct WebhookServer { + vweb.Context +mut: + gen_vc &GenVC = 0 // initialized in init_server +} + +// storage for flag options +struct FlagOptions { + work_dir string + purge bool + serve bool + port int + log_to string + log_file string + dry_run bool + force bool +} + +fn main() { + mut fp := flag.new_flag_parser(os.args.clone()) + fp.application(app_name) + fp.version(app_version) + fp.description(app_description) + fp.skip_executable() + show_help := fp.bool('help', 0, false, 'Show this help screen\n') + flag_options := parse_flags(mut fp) + if show_help { + println(fp.usage()) + exit(0) + } + fp.finalize() or { + eprintln(err) + println(fp.usage()) + return + } + // webhook server mode + if flag_options.serve { + vweb.run(&WebhookServer{}, flag_options.port) + } else { + // cmd mode + mut gen_vc := new_gen_vc(flag_options) + gen_vc.init() + gen_vc.generate() + } +} + +// new GenVC +fn new_gen_vc(flag_options FlagOptions) &GenVC { + mut logger := &log.Log{} + logger.set_level(.debug) + if flag_options.log_to == 'file' { + logger.set_full_logpath(flag_options.log_file) + } + return &GenVC{ + options: flag_options + logger: logger + } +} + +// WebhookServer init +pub fn (mut ws WebhookServer) init_server() { + mut fp := flag.new_flag_parser(os.args.clone()) + flag_options := parse_flags(mut fp) + ws.gen_vc = new_gen_vc(flag_options) + ws.gen_vc.init() + // ws.gen_vc = new_gen_vc(flag_options) +} + +pub fn (mut ws WebhookServer) index() { + eprintln('WebhookServer.index() called') +} + +// gen webhook +pub fn (mut ws WebhookServer) genhook() { + // request data + // println(ws.vweb.req.data) + // TODO: parse request. json or urlencoded + // json.decode or net.urllib.parse + ws.gen_vc.generate() + // error in generate + if ws.gen_vc.gen_error { + ws.json('{status: "failed"}') + return + } + ws.json('{status: "ok"}') +} + +pub fn (ws &WebhookServer) reset() { +} + +// parse flags to FlagOptions struct +fn parse_flags(mut fp flag.FlagParser) FlagOptions { + return FlagOptions{ + serve: fp.bool('serve', 0, false, 'run in webhook server mode') + work_dir: fp.string('work-dir', 0, work_dir, 'gen_vc working directory') + purge: fp.bool('purge', 0, false, 'force purge the local repositories') + port: fp.int('port', 0, server_port, 'port for web server to listen on') + log_to: fp.string('log-to', 0, log_to, "log to is 'file' or 'terminal'") + log_file: fp.string('log-file', 0, log_file, "log file to use when log-to is 'file'") + dry_run: fp.bool('dry-run', 0, dry_run, 'when specified dont push anything to remote repo') + force: fp.bool('force', 0, false, 'force update even if already up to date') + } +} + +fn (mut gen_vc GenVC) init() { + // purge repos if flag is passed + if gen_vc.options.purge { + gen_vc.purge_repos() + } +} + +// regenerate +fn (mut gen_vc GenVC) generate() { + // set errors to false + gen_vc.gen_error = false + // check if gen_vc dir exists + if !os.is_dir(gen_vc.options.work_dir) { + // try create + os.mkdir(gen_vc.options.work_dir) or { panic(err) } + // still dosen't exist... we have a problem + if !os.is_dir(gen_vc.options.work_dir) { + gen_vc.logger.error('error creating directory: $gen_vc.options.work_dir') + gen_vc.gen_error = true + return + } + } + // cd to gen_vc dir + os.chdir(gen_vc.options.work_dir) + // if we are not running with the --serve flag (webhook server) + // rather than deleting and re-downloading the repo each time + // first check to see if the local v repo is behind master + // if it isn't behind theres no point continuing further + if !gen_vc.options.serve && os.is_dir(git_repo_dir_v) { + gen_vc.cmd_exec('git -C $git_repo_dir_v checkout master') + // fetch the remote repo just in case there are newer commits there + gen_vc.cmd_exec('git -C $git_repo_dir_v fetch') + git_status := gen_vc.cmd_exec('git -C $git_repo_dir_v status') + if !git_status.contains('behind') && !gen_vc.options.force { + gen_vc.logger.warn('v repository is already up to date.') + return + } + } + // delete repos + gen_vc.purge_repos() + // clone repos + gen_vc.cmd_exec('git clone --depth 1 https://$git_repo_v $git_repo_dir_v') + gen_vc.cmd_exec('git clone --depth 1 https://$git_repo_vc $git_repo_dir_vc') + // get output of git log -1 (last commit) + git_log_v := gen_vc.cmd_exec('git -C $git_repo_dir_v log -1 --format="commit %H%nDate: %ci%nDate Unix: %ct%nSubject: %s"') + git_log_vc := gen_vc.cmd_exec('git -C $git_repo_dir_vc log -1 --format="Commit %H%nDate: %ci%nDate Unix: %ct%nSubject: %s"') + // date of last commit in each repo + ts_v := git_log_v.find_between('Date:', '\n').trim_space() + ts_vc := git_log_vc.find_between('Date:', '\n').trim_space() + // parse time as string to time.Time + last_commit_time_v := time.parse(ts_v) or { panic(err) } + last_commit_time_vc := time.parse(ts_vc) or { panic(err) } + // git dates are in users local timezone and v time.parse does not parse + // timezones at the moment, so for now get unix timestamp from output also + t_unix_v := git_log_v.find_between('Date Unix:', '\n').trim_space().int() + t_unix_vc := git_log_vc.find_between('Date Unix:', '\n').trim_space().int() + // last commit hash in v repo + last_commit_hash_v := git_log_v.find_between('commit', '\n').trim_space() + last_commit_hash_v_short := last_commit_hash_v[..7] + // subject + last_commit_subject := git_log_v.find_between('Subject:', '\n').trim_space().replace("'", + '"') + // log some info + gen_vc.logger.debug('last commit time ($git_repo_v): ' + last_commit_time_v.format_ss()) + gen_vc.logger.debug('last commit time ($git_repo_vc): ' + last_commit_time_vc.format_ss()) + gen_vc.logger.debug('last commit hash ($git_repo_v): $last_commit_hash_v') + gen_vc.logger.debug('last commit subject ($git_repo_v): $last_commit_subject') + // if vc repo already has a newer commit than the v repo, assume it's up to date + if t_unix_vc >= t_unix_v && !gen_vc.options.force { + gen_vc.logger.warn('vc repository is already up to date.') + return + } + // try build v for current os (linux in this case) + gen_vc.cmd_exec('make -C $git_repo_dir_v') + v_exec := '$git_repo_dir_v/v' + // check if make was successful + gen_vc.assert_file_exists_and_is_not_too_short(v_exec, err_msg_make) + // build v.c for each os + for os_name in vc_build_oses { + c_file := if os_name == 'nix' { 'v.c' } else { 'v_win.c' } + v_flags := if os_name == 'nix' { '-os cross' } else { '-os $os_name' } + // try generate .c file + gen_vc.cmd_exec('$v_exec $v_flags -o $c_file $git_repo_dir_v/cmd/v') + // check if the c file seems ok + gen_vc.assert_file_exists_and_is_not_too_short(c_file, err_msg_gen_c) + // embed the latest v commit hash into the c file + gen_vc.cmd_exec('sed -i \'1s/^/#define V_COMMIT_HASH "$last_commit_hash_v_short"\\n/\' $c_file') + // move to vc repo + gen_vc.cmd_exec('mv $c_file $git_repo_dir_vc/$c_file') + // add new .c file to local vc repo + gen_vc.cmd_exec('git -C $git_repo_dir_vc add $c_file') + } + // check if the vc repo actually changed + git_status := gen_vc.cmd_exec('git -C $git_repo_dir_vc status') + if git_status.contains('nothing to commit') { + gen_vc.logger.error('no changes to vc repo: something went wrong.') + gen_vc.gen_error = true + } + // commit changes to local vc repo + gen_vc.cmd_exec_safe("git -C $git_repo_dir_vc commit -m '[v:master] $last_commit_hash_v_short - $last_commit_subject'") + // push changes to remote vc repo + gen_vc.cmd_exec_safe('git -C $git_repo_dir_vc push https://${urllib.query_escape(git_username)}:${urllib.query_escape(git_password)}@$git_repo_vc master') +} + +// only execute when dry_run option is false, otherwise just log +fn (mut gen_vc GenVC) cmd_exec_safe(cmd string) string { + return gen_vc.command_execute(cmd, gen_vc.options.dry_run) +} + +// always execute command +fn (mut gen_vc GenVC) cmd_exec(cmd string) string { + return gen_vc.command_execute(cmd, false) +} + +// execute command +fn (mut gen_vc GenVC) command_execute(cmd string, dry bool) string { + // if dry is true then dont execute, just log + if dry { + return gen_vc.command_execute_dry(cmd) + } + gen_vc.logger.info('cmd: $cmd') + r := os.execute(cmd) + if r.exit_code < 0 { + gen_vc.logger.error('$err_msg_cmd_x: "$cmd" could not start.') + gen_vc.logger.error(r.output) + // something went wrong, better start fresh next time + gen_vc.purge_repos() + gen_vc.gen_error = true + return '' + } + if r.exit_code != 0 { + gen_vc.logger.error('$err_msg_cmd_x: "$cmd" failed.') + gen_vc.logger.error(r.output) + // something went wrong, better start fresh next time + gen_vc.purge_repos() + gen_vc.gen_error = true + return '' + } + return r.output +} + +// just log cmd, dont execute +fn (mut gen_vc GenVC) command_execute_dry(cmd string) string { + gen_vc.logger.info('cmd (dry): "$cmd"') + return '' +} + +// delete repo directories +fn (mut gen_vc GenVC) purge_repos() { + // delete old repos (better to be fully explicit here, since these are destructive operations) + mut repo_dir := '$gen_vc.options.work_dir/$git_repo_dir_v' + if os.is_dir(repo_dir) { + gen_vc.logger.info('purging local repo: "$repo_dir"') + gen_vc.cmd_exec('rm -rf $repo_dir') + } + repo_dir = '$gen_vc.options.work_dir/$git_repo_dir_vc' + if os.is_dir(repo_dir) { + gen_vc.logger.info('purging local repo: "$repo_dir"') + gen_vc.cmd_exec('rm -rf $repo_dir') + } +} + +// check if file size is too short +fn (mut gen_vc GenVC) assert_file_exists_and_is_not_too_short(f string, emsg string) { + if !os.exists(f) { + gen_vc.logger.error('$err_msg_build: $emsg .') + gen_vc.gen_error = true + return + } + fsize := os.file_size(f) + if fsize < too_short_file_limit { + gen_vc.logger.error('$err_msg_build: $f exists, but is too short: only $fsize bytes.') + gen_vc.gen_error = true + return + } +} diff --git a/v_windows/v/old/cmd/tools/missdoc.v b/v_windows/v/old/cmd/tools/missdoc.v new file mode 100644 index 0000000..188fca1 --- /dev/null +++ b/v_windows/v/old/cmd/tools/missdoc.v @@ -0,0 +1,141 @@ +// Copyright (c) 2020 Lars Pontoppidan. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +import os +import flag + +const ( + tool_name = os.file_name(os.executable()) + tool_version = '0.0.2' + tool_description = 'Prints all V functions in .v files under PATH/, that do not yet have documentation comments.' +) + +struct UndocumentedFN { + line int + signature string + tags []string +} + +struct Options { + show_help bool + collect_tags bool + deprecated bool +} + +fn collect(path string, mut l []string, f fn (string, mut []string)) { + if !os.is_dir(path) { + return + } + mut files := os.ls(path) or { return } + for file in files { + p := path + os.path_separator + file + if os.is_dir(p) && !os.is_link(p) { + collect(p, mut l, f) + } else if os.exists(p) { + f(p, mut l) + } + } + return +} + +fn report_undocumented_functions_in_path(opt Options, path string) { + mut files := []string{} + collect_fn := fn (path string, mut l []string) { + if os.file_ext(path) == '.v' { + l << os.real_path(path) + } + } + collect(path, mut files, collect_fn) + for file in files { + if file.ends_with('_test.v') { + continue + } + report_undocumented_functions_in_file(opt, file) + } +} + +fn report_undocumented_functions_in_file(opt Options, file string) { + contents := os.read_file(file) or { panic(err) } + lines := contents.split('\n') + mut info := []UndocumentedFN{} + for i, line in lines { + if line.starts_with('pub fn') || (line.starts_with('fn ') && !(line.starts_with('fn C.') + || line.starts_with('fn main'))) { + // println('Match: $line') + if i > 0 && lines.len > 0 { + mut line_above := lines[i - 1] + if !line_above.starts_with('//') { + mut tags := []string{} + mut grab := true + for j := i - 1; j >= 0; j-- { + prev_line := lines[j] + if prev_line.contains('}') { // We've looked back to the above scope, stop here + break + } else if prev_line.starts_with('[') { + tags << collect_tags(prev_line) + continue + } else if prev_line.starts_with('//') { // Single-line comment + grab = false + break + } + } + if grab { + clean_line := line.all_before_last(' {') + info << UndocumentedFN{i + 1, clean_line, tags} + } + } + } + } + } + if info.len > 0 { + for undocumented_fn in info { + tags_str := if opt.collect_tags && undocumented_fn.tags.len > 0 { + '$undocumented_fn.tags' + } else { + '' + } + if opt.deprecated { + println('$file:$undocumented_fn.line:0:$undocumented_fn.signature $tags_str') + } else { + if 'deprecated' !in undocumented_fn.tags { + println('$file:$undocumented_fn.line:0:$undocumented_fn.signature $tags_str') + } + } + } + } +} + +fn collect_tags(line string) []string { + mut cleaned := line.all_before('/') + cleaned = cleaned.replace_each(['[', '', ']', '', ' ', '']) + return cleaned.split(',') +} + +fn main() { + if os.args.len == 1 { + println('Usage: $tool_name PATH \n$tool_description\n$tool_name -h for more help...') + exit(1) + } + mut fp := flag.new_flag_parser(os.args[1..]) + fp.application(tool_name) + fp.version(tool_version) + fp.description(tool_description) + fp.arguments_description('PATH [PATH]...') + // Collect tool options + opt := Options{ + show_help: fp.bool('help', `h`, false, 'Show this help text.') + deprecated: fp.bool('deprecated', `d`, false, 'Include deprecated functions in output.') + collect_tags: fp.bool('tags', `t`, false, 'Also print function tags if any is found.') + } + if opt.show_help { + println(fp.usage()) + exit(0) + } + for path in os.args[1..] { + if os.is_file(path) { + report_undocumented_functions_in_file(opt, path) + } else { + report_undocumented_functions_in_path(opt, path) + } + } +} diff --git a/v_windows/v/old/cmd/tools/modules/scripting/scripting.v b/v_windows/v/old/cmd/tools/modules/scripting/scripting.v new file mode 100644 index 0000000..edf6a0a --- /dev/null +++ b/v_windows/v/old/cmd/tools/modules/scripting/scripting.v @@ -0,0 +1,180 @@ +module scripting + +import os +import term +import time + +const ( + term_colors = term.can_show_color_on_stdout() +) + +pub fn set_verbose(on bool) { + // setting a global here would be the obvious solution, + // but V does not have globals normally. + if on { + os.setenv('VERBOSE', '1', true) + } else { + os.unsetenv('VERBOSE') + } +} + +pub fn cprint(omessage string) { + mut message := omessage + if scripting.term_colors { + message = term.cyan(message) + } + print(message) +} + +pub fn cprint_strong(omessage string) { + mut message := omessage + if scripting.term_colors { + message = term.bright_green(message) + } + print(message) +} + +pub fn cprintln(omessage string) { + cprint(omessage) + println('') +} + +pub fn cprintln_strong(omessage string) { + cprint_strong(omessage) + println('') +} + +pub fn verbose_trace(label string, message string) { + if os.getenv('VERBOSE').len > 0 { + slabel := '$time.now().format_ss_milli() $label' + cprintln('# ${slabel:-43s} : $message') + } +} + +pub fn verbose_trace_strong(label string, omessage string) { + if os.getenv('VERBOSE').len > 0 { + slabel := '$time.now().format_ss_milli() $label' + mut message := omessage + if scripting.term_colors { + message = term.bright_green(message) + } + cprintln('# ${slabel:-43s} : $message') + } +} + +pub fn verbose_trace_exec_result(x os.Result) { + if os.getenv('VERBOSE').len > 0 { + cprintln('# cmd.exit_code : ${x.exit_code.str():-4s} cmd.output:') + mut lnum := 1 + lines := x.output.split_into_lines() + for oline in lines { + mut line := oline + if scripting.term_colors { + line = term.bright_green(line) + } + cprintln('# ${lnum:3d}: $line') + lnum++ + } + cprintln('# ----------------------------------------------------------------------') + } +} + +fn modfn(mname string, fname string) string { + return '${mname}.$fname' +} + +pub fn chdir(path string) { + verbose_trace_strong(modfn(@MOD, @FN), 'cd $path') + os.chdir(path) +} + +pub fn mkdir(path string) ? { + verbose_trace_strong(modfn(@MOD, @FN), 'mkdir $path') + os.mkdir(path) or { + verbose_trace(modfn(@MOD, @FN), '## failed.') + return err + } +} + +pub fn mkdir_all(path string) ? { + verbose_trace_strong(modfn(@MOD, @FN), 'mkdir -p $path') + os.mkdir_all(path) or { + verbose_trace(modfn(@MOD, @FN), '## failed.') + return err + } +} + +pub fn rmrf(path string) { + verbose_trace_strong(modfn(@MOD, @FN), 'rm -rf $path') + if os.exists(path) { + if os.is_dir(path) { + os.rmdir_all(path) or { panic(err) } + } else { + os.rm(path) or { panic(err) } + } + } +} + +// execute a command, and return a result, or an error, if it failed in any way. +pub fn exec(cmd string) ?os.Result { + verbose_trace_strong(modfn(@MOD, @FN), cmd) + x := os.execute(cmd) + if x.exit_code != 0 { + verbose_trace(modfn(@MOD, @FN), '## failed.') + return error(x.output) + } + verbose_trace_exec_result(x) + return x +} + +// run a command, tracing its results, and returning ONLY its output +pub fn run(cmd string) string { + verbose_trace_strong(modfn(@MOD, @FN), cmd) + x := os.execute(cmd) + if x.exit_code < 0 { + verbose_trace(modfn(@MOD, @FN), '## failed.') + return '' + } + verbose_trace_exec_result(x) + if x.exit_code == 0 { + return x.output.trim_right('\r\n') + } + return '' +} + +pub fn exit_0_status(cmd string) bool { + verbose_trace_strong(modfn(@MOD, @FN), cmd) + x := os.execute(cmd) + if x.exit_code < 0 { + verbose_trace(modfn(@MOD, @FN), '## failed.') + return false + } + verbose_trace_exec_result(x) + if x.exit_code == 0 { + return true + } + return false +} + +pub fn tool_must_exist(toolcmd string) { + verbose_trace(modfn(@MOD, @FN), toolcmd) + if exit_0_status('type $toolcmd') { + return + } + eprintln('Missing tool: $toolcmd') + eprintln('Please try again after you install it.') + exit(1) +} + +pub fn used_tools_must_exist(tools []string) { + for t in tools { + tool_must_exist(t) + } +} + +pub fn show_sizes_of_files(files []string) { + for f in files { + size := os.file_size(f) + println('$size $f') // println('${size:10d} $f') + } +} diff --git a/v_windows/v/old/cmd/tools/modules/testing/common.v b/v_windows/v/old/cmd/tools/modules/testing/common.v new file mode 100644 index 0000000..f37a22a --- /dev/null +++ b/v_windows/v/old/cmd/tools/modules/testing/common.v @@ -0,0 +1,488 @@ +module testing + +import os +import time +import term +import benchmark +import sync.pool +import v.pref +import v.util.vtest + +const github_job = os.getenv('GITHUB_JOB') + +const show_start = os.getenv('VTEST_SHOW_START') == '1' + +const hide_skips = os.getenv('VTEST_HIDE_SKIP') == '1' + +const hide_oks = os.getenv('VTEST_HIDE_OK') == '1' + +pub struct TestSession { +pub mut: + files []string + skip_files []string + vexe string + vroot string + vtmp_dir string + vargs string + failed bool + benchmark benchmark.Benchmark + rm_binaries bool = true + silent_mode bool + progress_mode bool + root_relative bool // used by CI runs, so that the output is stable everywhere + nmessages chan LogMessage // many publishers, single consumer/printer + nmessage_idx int // currently printed message index + nprint_ended chan int // read to block till printing ends, 1:1 + failed_cmds shared []string +} + +enum MessageKind { + ok + fail + skip + info + sentinel +} + +struct LogMessage { + message string + kind MessageKind +} + +pub fn (mut ts TestSession) add_failed_cmd(cmd string) { + lock ts.failed_cmds { + ts.failed_cmds << cmd + } +} + +pub fn (mut ts TestSession) show_list_of_failed_tests() { + for i, cmd in ts.failed_cmds { + eprintln(term.failed('Failed command ${i + 1}:') + ' $cmd') + } +} + +pub fn (mut ts TestSession) append_message(kind MessageKind, msg string) { + ts.nmessages <- LogMessage{ + message: msg + kind: kind + } +} + +pub fn (mut ts TestSession) print_messages() { + empty := term.header(' ', ' ') + mut print_msg_time := time.new_stopwatch() + for { + // get a message from the channel of messages to be printed: + mut rmessage := <-ts.nmessages + if rmessage.kind == .sentinel { + // a sentinel for stopping the printing thread + if !ts.silent_mode && ts.progress_mode { + eprintln('') + } + ts.nprint_ended <- 0 + return + } + if rmessage.kind != .info { + ts.nmessage_idx++ + } + msg := rmessage.message.replace_each([ + 'TMP1', + '${ts.nmessage_idx:1d}', + 'TMP2', + '${ts.nmessage_idx:2d}', + 'TMP3', + '${ts.nmessage_idx:3d}', + 'TMP4', + '${ts.nmessage_idx:4d}', + ]) + is_ok := rmessage.kind == .ok + // + time_passed := print_msg_time.elapsed().seconds() + if time_passed > 10 && ts.silent_mode && is_ok { + // Even if OK tests are suppressed, + // show *at least* 1 result every 10 seconds, + // otherwise the CI can seem stuck ... + eprintln(msg) + print_msg_time.restart() + continue + } + if ts.progress_mode { + // progress mode, the last line is rewritten many times: + if is_ok && !ts.silent_mode { + print('\r$empty\r$msg') + } else { + // the last \n is needed, so SKIP/FAIL messages + // will not get overwritten by the OK ones + eprint('\r$empty\r$msg\n') + } + continue + } + if !ts.silent_mode || !is_ok { + // normal expanded mode, or failures in -silent mode + eprintln(msg) + continue + } + } +} + +pub fn new_test_session(_vargs string, will_compile bool) TestSession { + mut skip_files := []string{} + if will_compile { + $if msvc { + skip_files << 'vlib/v/tests/const_comptime_eval_before_vinit_test.v' // _constructor used + } + $if solaris { + skip_files << 'examples/gg/gg2.v' + skip_files << 'examples/pico/pico.v' + skip_files << 'examples/sokol/fonts.v' + skip_files << 'examples/sokol/drawing.v' + } + $if macos { + skip_files << 'examples/database/mysql.v' + skip_files << 'examples/database/orm.v' + skip_files << 'examples/database/psql/customer.v' + } + $if windows { + skip_files << 'examples/database/mysql.v' + skip_files << 'examples/database/orm.v' + skip_files << 'examples/websocket/ping.v' // requires OpenSSL + skip_files << 'examples/websocket/client-server/client.v' // requires OpenSSL + skip_files << 'examples/websocket/client-server/server.v' // requires OpenSSL + $if tinyc { + skip_files << 'examples/database/orm.v' // try fix it + } + } + if testing.github_job != 'sokol-shaders-can-be-compiled' { + // These examples need .h files that are produced from the supplied .glsl files, + // using by the shader compiler tools in https://github.com/floooh/sokol-tools-bin/archive/pre-feb2021-api-changes.tar.gz + skip_files << 'examples/sokol/02_cubes_glsl/cube_glsl.v' + skip_files << 'examples/sokol/03_march_tracing_glsl/rt_glsl.v' + skip_files << 'examples/sokol/04_multi_shader_glsl/rt_glsl.v' + skip_files << 'examples/sokol/05_instancing_glsl/rt_glsl.v' + // Skip obj_viewer code in the CI + skip_files << 'examples/sokol/06_obj_viewer/show_obj.v' + } + if testing.github_job != 'ubuntu-tcc' { + skip_files << 'examples/c_interop_wkhtmltopdf.v' // needs installation of wkhtmltopdf from https://github.com/wkhtmltopdf/packaging/releases + // the ttf_test.v is not interactive, but needs X11 headers to be installed, which is done only on ubuntu-tcc for now + skip_files << 'vlib/x/ttf/ttf_test.v' + skip_files << 'vlib/vweb/vweb_app_test.v' // imports the `sqlite` module, which in turn includes sqlite3.h + } + if testing.github_job != 'audio-examples' { + skip_files << 'examples/sokol/sounds/melody.v' + skip_files << 'examples/sokol/sounds/wav_player.v' + skip_files << 'examples/sokol/sounds/simple_sin_tones.v' + } + } + vargs := _vargs.replace('-progress', '').replace('-progress', '') + vexe := pref.vexe_path() + vroot := os.dir(vexe) + new_vtmp_dir := setup_new_vtmp_folder() + if term.can_show_color_on_stderr() { + os.setenv('VCOLORS', 'always', true) + } + return TestSession{ + vexe: vexe + vroot: vroot + skip_files: skip_files + vargs: vargs + vtmp_dir: new_vtmp_dir + silent_mode: _vargs.contains('-silent') + progress_mode: _vargs.contains('-progress') + } +} + +pub fn (mut ts TestSession) init() { + ts.files.sort() + ts.benchmark = benchmark.new_benchmark_no_cstep() +} + +pub fn (mut ts TestSession) add(file string) { + ts.files << file +} + +pub fn (mut ts TestSession) test() { + // Ensure that .tmp.c files generated from compiling _test.v files, + // are easy to delete at the end, *without* affecting the existing ones. + current_wd := os.getwd() + if current_wd == os.wd_at_startup && current_wd == ts.vroot { + ts.root_relative = true + } + // + ts.init() + mut remaining_files := []string{} + for dot_relative_file in ts.files { + relative_file := dot_relative_file.replace('./', '') + file := os.real_path(relative_file) + $if windows { + if file.contains('sqlite') || file.contains('httpbin') { + continue + } + } + $if !macos { + if file.contains('customer') { + continue + } + } + $if msvc { + if file.contains('asm') { + continue + } + } + remaining_files << dot_relative_file + } + remaining_files = vtest.filter_vtest_only(remaining_files, fix_slashes: false) + ts.files = remaining_files + ts.benchmark.set_total_expected_steps(remaining_files.len) + mut pool_of_test_runners := pool.new_pool_processor(callback: worker_trunner) + // for handling messages across threads + ts.nmessages = chan LogMessage{cap: 10000} + ts.nprint_ended = chan int{cap: 0} + ts.nmessage_idx = 0 + go ts.print_messages() + pool_of_test_runners.set_shared_context(ts) + pool_of_test_runners.work_on_pointers(unsafe { remaining_files.pointers() }) + ts.benchmark.stop() + ts.append_message(.sentinel, '') // send the sentinel + _ := <-ts.nprint_ended // wait for the stop of the printing thread + eprintln(term.h_divider('-')) + // cleanup generated .tmp.c files after successfull tests: + if ts.benchmark.nfail == 0 { + if ts.rm_binaries { + os.rmdir_all(ts.vtmp_dir) or { panic(err) } + } + } + ts.show_list_of_failed_tests() +} + +fn worker_trunner(mut p pool.PoolProcessor, idx int, thread_id int) voidptr { + mut ts := &TestSession(p.get_shared_context()) + tmpd := ts.vtmp_dir + show_stats := '-stats' in ts.vargs.split(' ') + // tls_bench is used to format the step messages/timings + mut tls_bench := &benchmark.Benchmark(p.get_thread_context(idx)) + if isnil(tls_bench) { + tls_bench = benchmark.new_benchmark_pointer() + tls_bench.set_total_expected_steps(ts.benchmark.nexpected_steps) + p.set_thread_context(idx, tls_bench) + } + tls_bench.no_cstep = true + dot_relative_file := p.get_item(idx) + mut relative_file := dot_relative_file.replace('./', '') + mut cmd_options := [ts.vargs] + if relative_file.contains('global') && !ts.vargs.contains('fmt') { + cmd_options << ' -enable-globals' + } + if ts.root_relative { + relative_file = relative_file.replace(ts.vroot + os.path_separator, '') + } + file := os.real_path(relative_file) + normalised_relative_file := relative_file.replace('\\', '/') + // Ensure that the generated binaries will be stored in the temporary folder. + // Remove them after a test passes/fails. + fname := os.file_name(file) + generated_binary_fname := if os.user_os() == 'windows' { + fname.replace('.v', '.exe') + } else { + fname.replace('.v', '') + } + generated_binary_fpath := os.join_path(tmpd, generated_binary_fname) + if os.exists(generated_binary_fpath) { + if ts.rm_binaries { + os.rm(generated_binary_fpath) or { panic(err) } + } + } + if !ts.vargs.contains('fmt') { + cmd_options << ' -o "$generated_binary_fpath"' + } + cmd := '"$ts.vexe" ' + cmd_options.join(' ') + ' "$file"' + ts.benchmark.step() + tls_bench.step() + if relative_file.replace('\\', '/') in ts.skip_files { + ts.benchmark.skip() + tls_bench.skip() + if !testing.hide_skips { + ts.append_message(.skip, tls_bench.step_message_skip(normalised_relative_file)) + } + return pool.no_result + } + if show_stats { + ts.append_message(.ok, term.h_divider('-')) + status := os.system(cmd) + if status == 0 { + ts.benchmark.ok() + tls_bench.ok() + } else { + ts.failed = true + ts.benchmark.fail() + tls_bench.fail() + ts.add_failed_cmd(cmd) + return pool.no_result + } + } else { + if testing.show_start { + ts.append_message(.info, ' starting $relative_file ...') + } + r := os.execute(cmd) + if r.exit_code < 0 { + ts.failed = true + ts.benchmark.fail() + tls_bench.fail() + ts.append_message(.fail, tls_bench.step_message_fail(normalised_relative_file)) + ts.add_failed_cmd(cmd) + return pool.no_result + } + if r.exit_code != 0 { + ts.failed = true + ts.benchmark.fail() + tls_bench.fail() + ending_newline := if r.output.ends_with('\n') { '\n' } else { '' } + ts.append_message(.fail, tls_bench.step_message_fail('$normalised_relative_file\n$r.output.trim_space()$ending_newline')) + ts.add_failed_cmd(cmd) + } else { + ts.benchmark.ok() + tls_bench.ok() + if !testing.hide_oks { + ts.append_message(.ok, tls_bench.step_message_ok(normalised_relative_file)) + } + } + } + if os.exists(generated_binary_fpath) { + if ts.rm_binaries { + os.rm(generated_binary_fpath) or { panic(err) } + } + } + return pool.no_result +} + +pub fn vlib_should_be_present(parent_dir string) { + vlib_dir := os.join_path(parent_dir, 'vlib') + if !os.is_dir(vlib_dir) { + eprintln('$vlib_dir is missing, it must be next to the V executable') + exit(1) + } +} + +pub fn v_build_failing(zargs string, folder string) bool { + return v_build_failing_skipped(zargs, folder, []) +} + +pub fn prepare_test_session(zargs string, folder string, oskipped []string, main_label string) TestSession { + vexe := pref.vexe_path() + parent_dir := os.dir(vexe) + vlib_should_be_present(parent_dir) + vargs := zargs.replace(vexe, '') + eheader(main_label) + if vargs.len > 0 { + eprintln('v compiler args: "$vargs"') + } + mut session := new_test_session(vargs, true) + files := os.walk_ext(os.join_path(parent_dir, folder), '.v') + mut mains := []string{} + mut skipped := oskipped.clone() + next_file: for f in files { + fnormalised := f.replace('\\', '/') + // NB: a `testdata` folder, is the preferred name of a folder, containing V code, + // that you *do not want* the test framework to find incidentally for various reasons, + // for example module import tests, or subtests, that are compiled/run by other parent tests + // in specific configurations, etc. + if fnormalised.contains('testdata/') || fnormalised.contains('modules/') + || f.contains('preludes/') { + continue + } + $if windows { + // skip pico and process/command examples on windows + if fnormalised.ends_with('examples/pico/pico.v') + || fnormalised.ends_with('examples/process/command.v') { + continue + } + } + c := os.read_file(f) or { panic(err) } + maxc := if c.len > 300 { 300 } else { c.len } + start := c[0..maxc] + if start.contains('module ') && !start.contains('module main') { + skipped_f := f.replace(os.join_path(parent_dir, ''), '') + skipped << skipped_f + } + for skip_prefix in oskipped { + if f.starts_with(skip_prefix) { + continue next_file + } + } + mains << f + } + session.files << mains + session.skip_files << skipped + return session +} + +pub fn v_build_failing_skipped(zargs string, folder string, oskipped []string) bool { + main_label := 'Building $folder ...' + finish_label := 'building $folder' + mut session := prepare_test_session(zargs, folder, oskipped, main_label) + session.test() + eprintln(session.benchmark.total_message(finish_label)) + return session.failed +} + +pub fn build_v_cmd_failed(cmd string) bool { + res := os.execute(cmd) + if res.exit_code < 0 { + return true + } + if res.exit_code != 0 { + eprintln('') + eprintln(res.output) + return true + } + return false +} + +pub fn building_any_v_binaries_failed() bool { + eheader('Building V binaries...') + eprintln('VFLAGS is: "' + os.getenv('VFLAGS') + '"') + vexe := pref.vexe_path() + parent_dir := os.dir(vexe) + vlib_should_be_present(parent_dir) + os.chdir(parent_dir) + mut failed := false + v_build_commands := ['$vexe -o v_g -g cmd/v', '$vexe -o v_prod_g -prod -g cmd/v', + '$vexe -o v_cg -cg cmd/v', '$vexe -o v_prod_cg -prod -cg cmd/v', + '$vexe -o v_prod -prod cmd/v', + ] + mut bmark := benchmark.new_benchmark() + for cmd in v_build_commands { + bmark.step() + if build_v_cmd_failed(cmd) { + bmark.fail() + failed = true + eprintln(bmark.step_message_fail('command: $cmd . See details above ^^^^^^^')) + eprintln('') + continue + } + bmark.ok() + if !testing.hide_oks { + eprintln(bmark.step_message_ok('command: $cmd')) + } + } + bmark.stop() + eprintln(term.h_divider('-')) + eprintln(bmark.total_message('building v binaries')) + return failed +} + +pub fn eheader(msg string) { + eprintln(term.header_left(msg, '-')) +} + +pub fn header(msg string) { + println(term.header_left(msg, '-')) +} + +pub fn setup_new_vtmp_folder() string { + now := time.sys_mono_now() + new_vtmp_dir := os.join_path(os.temp_dir(), 'v', 'test_session_$now') + os.mkdir_all(new_vtmp_dir) or { panic(err) } + os.setenv('VTMP', new_vtmp_dir, true) + return new_vtmp_dir +} diff --git a/v_windows/v/old/cmd/tools/modules/vgit/vgit.v b/v_windows/v/old/cmd/tools/modules/vgit/vgit.v new file mode 100644 index 0000000..efa2e8a --- /dev/null +++ b/v_windows/v/old/cmd/tools/modules/vgit/vgit.v @@ -0,0 +1,197 @@ +module vgit + +import os +import flag +import scripting + +pub fn check_v_commit_timestamp_before_self_rebuilding(v_timestamp int) { + if v_timestamp >= 1561805697 { + return + } + eprintln('##################################################################') + eprintln('# WARNING: v self rebuilding, before 5b7a1e8 (2019-06-29 12:21) #') + eprintln('# required the v executable to be built *inside* #') + eprintln('# the toplevel compiler/ folder. #') + eprintln('# #') + eprintln('# That is not supported by this tool. #') + eprintln('# You will have to build it manually there. #') + eprintln('##################################################################') +} + +pub fn validate_commit_exists(commit string) { + if commit.len == 0 { + return + } + cmd := "git cat-file -t '$commit' " + if !scripting.exit_0_status(cmd) { + eprintln('Commit: "$commit" does not exist in the current repository.') + exit(3) + } +} + +pub fn line_to_timestamp_and_commit(line string) (int, string) { + parts := line.split(' ') + return parts[0].int(), parts[1] +} + +pub fn normalized_workpath_for_commit(workdir string, commit string) string { + nc := 'v_at_' + commit.replace('^', '_').replace('-', '_').replace('/', '_') + return os.real_path(workdir + os.path_separator + nc) +} + +fn get_current_folder_commit_hash() string { + vline := scripting.run('git rev-list -n1 --timestamp HEAD') + _, v_commithash := line_to_timestamp_and_commit(vline) + return v_commithash +} + +pub fn prepare_vc_source(vcdir string, cdir string, commit string) (string, string) { + scripting.chdir(cdir) + // Building a historic v with the latest vc is not always possible ... + // It is more likely, that the vc *at the time of the v commit*, + // or slightly before that time will be able to build the historic v: + vline := scripting.run('git rev-list -n1 --timestamp "$commit" ') + v_timestamp, v_commithash := line_to_timestamp_and_commit(vline) + scripting.verbose_trace(@FN, 'v_timestamp: $v_timestamp | v_commithash: $v_commithash') + check_v_commit_timestamp_before_self_rebuilding(v_timestamp) + scripting.chdir(vcdir) + scripting.run('git checkout --quiet master') + // + mut vccommit := '' + vcbefore_subject_match := scripting.run('git rev-list HEAD -n1 --timestamp --grep=${v_commithash[0..7]} ') + scripting.verbose_trace(@FN, 'vcbefore_subject_match: $vcbefore_subject_match') + if vcbefore_subject_match.len > 3 { + _, vccommit = line_to_timestamp_and_commit(vcbefore_subject_match) + } else { + scripting.verbose_trace(@FN, 'the v commit did not match anything in the vc log; try --timestamp instead.') + vcbefore := scripting.run('git rev-list HEAD -n1 --timestamp --before=$v_timestamp ') + _, vccommit = line_to_timestamp_and_commit(vcbefore) + } + scripting.verbose_trace(@FN, 'vccommit: $vccommit') + scripting.run('git checkout --quiet "$vccommit" ') + scripting.run('wc *.c') + scripting.chdir(cdir) + return v_commithash, vccommit +} + +pub fn clone_or_pull(remote_git_url string, local_worktree_path string) { + // NB: after clone_or_pull, the current repo branch is === HEAD === master + if os.is_dir(local_worktree_path) && os.is_dir(os.join_path(local_worktree_path, '.git')) { + // Already existing ... Just pulling in this case is faster usually. + scripting.run('git -C "$local_worktree_path" checkout --quiet master') + scripting.run('git -C "$local_worktree_path" pull --quiet ') + } else { + // Clone a fresh + scripting.run('git clone --quiet "$remote_git_url" "$local_worktree_path" ') + } +} + +pub struct VGitContext { +pub: + cc string = 'cc' // what compiler to use + workdir string = '/tmp' // the base working folder + commit_v string = 'master' // the commit-ish that needs to be prepared + path_v string // where is the local working copy v repo + path_vc string // where is the local working copy vc repo + v_repo_url string // the remote v repo URL + vc_repo_url string // the remote vc repo URL +pub mut: + // these will be filled by vgitcontext.compile_oldv_if_needed() + commit_v__hash string // the git commit of the v repo that should be prepared + commit_vc_hash string // the git commit of the vc repo, corresponding to commit_v__hash + vexename string // v or v.exe + vexepath string // the full absolute path to the prepared v/v.exe + vvlocation string // v.v or compiler/ or cmd/v, depending on v version + make_fresh_tcc bool // whether to do 'make fresh_tcc' before compiling an old V. +} + +pub fn (mut vgit_context VGitContext) compile_oldv_if_needed() { + vgit_context.vexename = if os.user_os() == 'windows' { 'v.exe' } else { 'v' } + vgit_context.vexepath = os.real_path(os.join_path(vgit_context.path_v, vgit_context.vexename)) + mut command_for_building_v_from_c_source := '' + mut command_for_selfbuilding := '' + if 'windows' == os.user_os() { + command_for_building_v_from_c_source = '$vgit_context.cc -std=c99 -municode -w -o cv.exe "$vgit_context.path_vc/v_win.c" ' + command_for_selfbuilding = './cv.exe -o $vgit_context.vexename {SOURCE}' + } else { + command_for_building_v_from_c_source = '$vgit_context.cc -std=gnu11 -w -o cv "$vgit_context.path_vc/v.c" -lm -lpthread' + command_for_selfbuilding = './cv -o $vgit_context.vexename {SOURCE}' + } + scripting.chdir(vgit_context.workdir) + clone_or_pull(vgit_context.v_repo_url, vgit_context.path_v) + clone_or_pull(vgit_context.vc_repo_url, vgit_context.path_vc) + scripting.chdir(vgit_context.path_v) + scripting.run('git checkout --quiet $vgit_context.commit_v') + if os.is_dir(vgit_context.path_v) && os.exists(vgit_context.vexepath) { + // already compiled, so no need to compile v again + vgit_context.commit_v__hash = get_current_folder_commit_hash() + return + } + v_commithash, vccommit_before := prepare_vc_source(vgit_context.path_vc, vgit_context.path_v, + 'HEAD') + vgit_context.commit_v__hash = v_commithash + vgit_context.commit_vc_hash = vccommit_before + if os.exists('cmd/v') { + vgit_context.vvlocation = 'cmd/v' + } else { + vgit_context.vvlocation = if os.exists('v.v') { 'v.v' } else { 'compiler' } + } + if os.is_dir(vgit_context.path_v) && os.exists(vgit_context.vexepath) { + // already compiled, so no need to compile v again + return + } + // Recompilation is needed. Just to be sure, clean up everything first. + scripting.run('git clean -xf') + if vgit_context.make_fresh_tcc { + scripting.run('make fresh_tcc') + } + scripting.run(command_for_building_v_from_c_source) + build_cmd := command_for_selfbuilding.replace('{SOURCE}', vgit_context.vvlocation) + scripting.run(build_cmd) + // At this point, there exists a file vgit_context.vexepath + // which should be a valid working V executable. +} + +pub struct VGitOptions { +pub mut: + workdir string // the working folder (typically /tmp), where the tool will write + v_repo_url string // the url of the V repository. It can be a local folder path, if you want to eliminate network operations... + vc_repo_url string // the url of the vc repository. It can be a local folder path, if you want to eliminate network operations... + show_help bool // whether to show the usage screen + verbose bool // should the tool be much more verbose +} + +pub fn add_common_tool_options(mut context VGitOptions, mut fp flag.FlagParser) []string { + tdir := os.temp_dir() + context.workdir = os.real_path(fp.string('workdir', `w`, context.workdir, 'A writable base folder. Default: $tdir')) + context.v_repo_url = fp.string('vrepo', 0, context.v_repo_url, 'The url of the V repository. You can clone it locally too. See also --vcrepo below.') + context.vc_repo_url = fp.string('vcrepo', 0, context.vc_repo_url, 'The url of the vc repository. You can clone it +${flag.space}beforehand, and then just give the local folder +${flag.space}path here. That will eliminate the network ops +${flag.space}done by this tool, which is useful, if you want +${flag.space}to script it/run it in a restrictive vps/docker. +') + context.show_help = fp.bool('help', `h`, false, 'Show this help screen.') + context.verbose = fp.bool('verbose', `v`, false, 'Be more verbose.') + if context.show_help { + println(fp.usage()) + exit(0) + } + if context.verbose { + scripting.set_verbose(true) + } + if os.is_dir(context.v_repo_url) { + context.v_repo_url = os.real_path(context.v_repo_url) + } + if os.is_dir(context.vc_repo_url) { + context.vc_repo_url = os.real_path(context.vc_repo_url) + } + commits := fp.finalize() or { + eprintln('Error: $err') + exit(1) + } + for commit in commits { + validate_commit_exists(commit) + } + return commits +} diff --git a/v_windows/v/old/cmd/tools/modules/vhelp/vhelp.v b/v_windows/v/old/cmd/tools/modules/vhelp/vhelp.v new file mode 100644 index 0000000..347ba75 --- /dev/null +++ b/v_windows/v/old/cmd/tools/modules/vhelp/vhelp.v @@ -0,0 +1,14 @@ +module vhelp + +import os + +pub fn show_topic(topic string) { + vexe := os.real_path(os.getenv('VEXE')) + vroot := os.dir(vexe) + target_topic := os.join_path(vroot, 'cmd', 'v', 'help', '${topic}.txt') + content := os.read_file(target_topic) or { + eprintln('Unknown topic: $topic') + exit(1) + } + println(content) +} diff --git a/v_windows/v/old/cmd/tools/oldv.v b/v_windows/v/old/cmd/tools/oldv.v new file mode 100644 index 0000000..03eaf83 --- /dev/null +++ b/v_windows/v/old/cmd/tools/oldv.v @@ -0,0 +1,176 @@ +import os +import flag +import scripting +import vgit + +const ( + tool_version = '0.0.3' + tool_description = ' Checkout an old V and compile it as it was on specific commit. +| This tool is useful, when you want to discover when something broke. +| It is also useful, when you just want to experiment with an older historic V. +| +| The VCOMMIT argument can be a git commitish like HEAD or master and so on. +| When oldv is used with git bisect, you probably want to give HEAD. For example: +| git bisect start +| git bisect bad +| git checkout known_good_commit +| git bisect good +| ## Now git will automatically checkout a middle commit between the bad and the good +| cmd/tools/oldv HEAD --command="run commands in oldv folder, to verify if the commit is good or bad" +| ## See what the result is, and either do: ... +| git bisect good +| ## ... or do: +| git bisect bad +| ## Now you just repeat the above steps, each time running oldv with the same command, then mark the result as good or bad, +| ## until you find the commit, where the problem first occurred. +| ## When you finish, do not forget to do: +| git bisect reset'.strip_margin() +) + +struct Context { +mut: + vgo vgit.VGitOptions + vgcontext vgit.VGitContext + commit_v string = 'master' // the commit from which you want to produce a working v compiler (this may be a commit-ish too) + commit_v_hash string // this will be filled from the commit-ish commit_v using rev-list. It IS a commit hash. + path_v string // the full path to the v folder inside workdir. + path_vc string // the full path to the vc folder inside workdir. + cmd_to_run string // the command that you want to run *in* the oldv repo + cc string = 'cc' // the C compiler to use for bootstrapping. + cleanup bool // should the tool run a cleanup first + use_cache bool // use local cached copies for --vrepo and --vcrepo in + fresh_tcc bool // do use `make fresh_tcc` +} + +fn (mut c Context) compile_oldv_if_needed() { + c.vgcontext = vgit.VGitContext{ + workdir: c.vgo.workdir + v_repo_url: c.vgo.v_repo_url + vc_repo_url: c.vgo.vc_repo_url + cc: c.cc + commit_v: c.commit_v + path_v: c.path_v + path_vc: c.path_vc + make_fresh_tcc: c.fresh_tcc + } + c.vgcontext.compile_oldv_if_needed() + c.commit_v_hash = c.vgcontext.commit_v__hash + if !os.exists(c.vgcontext.vexepath) && c.cmd_to_run.len > 0 { + // NB: 125 is a special code, that git bisect understands as 'skip this commit'. + // it is used to inform git bisect that the current commit leads to a build failure. + exit(125) + } +} + +const cache_oldv_folder = os.join_path(os.cache_dir(), 'oldv') + +const cache_oldv_folder_v = os.join_path(cache_oldv_folder, 'v') + +const cache_oldv_folder_vc = os.join_path(cache_oldv_folder, 'vc') + +fn sync_cache() { + scripting.verbose_trace(@FN, 'start') + if !os.exists(cache_oldv_folder) { + scripting.verbose_trace(@FN, 'creating $cache_oldv_folder') + scripting.mkdir_all(cache_oldv_folder) or { + scripting.verbose_trace(@FN, '## failed.') + exit(1) + } + } + scripting.chdir(cache_oldv_folder) + for reponame in ['v', 'vc'] { + repofolder := os.join_path(cache_oldv_folder, reponame) + if !os.exists(repofolder) { + scripting.verbose_trace(@FN, 'cloning to $repofolder') + scripting.exec('git clone --quiet https://github.com/vlang/$reponame $repofolder') or { + scripting.verbose_trace(@FN, '## error during clone: $err') + exit(1) + } + } + scripting.chdir(repofolder) + scripting.exec('git pull --quiet') or { + scripting.verbose_trace(@FN, 'pulling to $repofolder') + scripting.verbose_trace(@FN, '## error during pull: $err') + exit(1) + } + } + scripting.verbose_trace(@FN, 'done') +} + +fn main() { + scripting.used_tools_must_exist(['git', 'cc']) + // + // Resetting VEXE here allows for `v run cmd/tools/oldv.v'. + // the parent V would have set VEXE, which later will + // affect the V's run from the tool itself. + os.setenv('VEXE', '', true) + // + mut context := Context{} + context.vgo.workdir = cache_oldv_folder + mut fp := flag.new_flag_parser(os.args) + fp.application(os.file_name(os.executable())) + fp.version(tool_version) + fp.description(tool_description) + fp.arguments_description('VCOMMIT') + fp.skip_executable() + context.use_cache = fp.bool('cache', `u`, true, 'Use a cache of local repositories for --vrepo and --vcrepo in \$HOME/.cache/oldv/') + if context.use_cache { + context.vgo.v_repo_url = cache_oldv_folder_v + context.vgo.vc_repo_url = cache_oldv_folder_vc + } else { + context.vgo.v_repo_url = 'https://github.com/vlang/v' + context.vgo.vc_repo_url = 'https://github.com/vlang/vc' + } + should_sync := fp.bool('cache-sync', `s`, false, 'Update the local cache') + if !should_sync { + fp.limit_free_args(1, 1) + } + //// + context.cleanup = fp.bool('clean', 0, false, 'Clean before running (slower).') + context.fresh_tcc = fp.bool('fresh_tcc', 0, true, 'Do `make fresh_tcc` when preparing a V compiler.') + context.cmd_to_run = fp.string('command', `c`, '', 'Command to run in the old V repo.\n') + commits := vgit.add_common_tool_options(mut context.vgo, mut fp) + if should_sync { + sync_cache() + exit(0) + } + if context.use_cache { + if !os.is_dir(cache_oldv_folder_v) || !os.is_dir(cache_oldv_folder_vc) { + sync_cache() + } + } + if commits.len > 0 { + context.commit_v = commits[0] + } else { + context.commit_v = scripting.run('git rev-list -n1 HEAD') + } + scripting.cprintln('################# context.commit_v: $context.commit_v #####################') + context.path_v = vgit.normalized_workpath_for_commit(context.vgo.workdir, context.commit_v) + context.path_vc = vgit.normalized_workpath_for_commit(context.vgo.workdir, 'vc') + if !os.is_dir(context.vgo.workdir) { + eprintln('Work folder: $context.vgo.workdir , does not exist.') + exit(2) + } + ecc := os.getenv('CC') + if ecc != '' { + context.cc = ecc + } + if context.cleanup { + scripting.rmrf(context.path_v) + scripting.rmrf(context.path_vc) + } + context.compile_oldv_if_needed() + scripting.chdir(context.path_v) + shorter_hash := context.commit_v_hash[0..10] + scripting.cprintln('# v commit hash: $shorter_hash | folder: $context.path_v') + if context.cmd_to_run.len > 0 { + scripting.cprintln_strong('# command: ${context.cmd_to_run:-34s}') + cmdres := os.execute_or_exit(context.cmd_to_run) + if cmdres.exit_code != 0 { + scripting.cprintln_strong('# exit code: ${cmdres.exit_code:-4d}') + } + scripting.cprint_strong('# result: ') + print(cmdres.output) + exit(cmdres.exit_code) + } +} diff --git a/v_windows/v/old/cmd/tools/performance_compare.v b/v_windows/v/old/cmd/tools/performance_compare.v new file mode 100644 index 0000000..6723231 --- /dev/null +++ b/v_windows/v/old/cmd/tools/performance_compare.v @@ -0,0 +1,215 @@ +import os +import flag +import scripting +import vgit + +const ( + tool_version = '0.0.5' + tool_description = " Compares V executable size and performance, +| between 2 commits from V's local git history. +| When only one commit is given, it is compared to master. +| ".strip_margin() +) + +struct Context { + cwd string // current working folder +mut: + vgo vgit.VGitOptions + a string // the full path to the 'after' folder inside workdir + b string // the full path to the 'before' folder inside workdir + vc string // the full path to the vc folder inside workdir. It is used during bootstrapping v from the C source. + commit_before string // the git commit for the 'before' state + commit_after string // the git commit for the 'after' state + warmups int // how many times to execute a command before gathering stats + hyperfineopts string // use for additional CLI options that will be given to the hyperfine command + vflags string // other v options to pass to compared v commands +} + +fn new_context() Context { + return Context{ + cwd: os.getwd() + commit_after: 'master' + warmups: 4 + } +} + +fn (c Context) compare_versions() { + // Input is validated at this point... + // Cleanup artifacts from previous runs of this tool: + scripting.chdir(c.vgo.workdir) + scripting.run('rm -rf "$c.a" "$c.b" "$c.vc" ') + // clone the VC source *just once per comparison*, and reuse it: + scripting.run('git clone --quiet "$c.vgo.vc_repo_url" "$c.vc" ') + println('Comparing V performance of commit $c.commit_before (before) vs commit $c.commit_after (after) ...') + c.prepare_v(c.b, c.commit_before) + c.prepare_v(c.a, c.commit_after) + scripting.chdir(c.vgo.workdir) + if c.vflags.len > 0 { + os.setenv('VFLAGS', c.vflags, true) + } + // The first is the baseline, against which all the others will be compared. + // It is the fastest, since hello_world.v has only a single println in it, + mut perf_files := []string{} + perf_files << c.compare_v_performance('source_hello', [ + 'vprod @DEBUG@ -o source.c examples/hello_world.v', + 'vprod -o source.c examples/hello_world.v', + 'v @DEBUG@ -o source.c examples/hello_world.v', + 'v -o source.c examples/hello_world.v', + ]) + perf_files << c.compare_v_performance('source_v', ['vprod @DEBUG@ -o source.c @COMPILER@', + 'vprod -o source.c @COMPILER@', 'v @DEBUG@ -o source.c @COMPILER@', + 'v -o source.c @COMPILER@', + ]) + perf_files << c.compare_v_performance('binary_hello', [ + 'vprod -o hello examples/hello_world.v', + 'v -o hello examples/hello_world.v', + ]) + perf_files << c.compare_v_performance('binary_v', ['vprod -o binary @COMPILER@', + 'v -o binary @COMPILER@', + ]) + println('All performance files:') + for f in perf_files { + println(' $f') + } +} + +fn (c &Context) prepare_v(cdir string, commit string) { + mut cc := os.getenv('CC') + if cc == '' { + cc = 'cc' + } + mut vgit_context := vgit.VGitContext{ + cc: cc + commit_v: commit + path_v: cdir + path_vc: c.vc + workdir: c.vgo.workdir + v_repo_url: c.vgo.v_repo_url + vc_repo_url: c.vgo.vc_repo_url + } + vgit_context.compile_oldv_if_needed() + scripting.chdir(cdir) + println('Making a v compiler in $cdir') + scripting.run('./v -cc $cc -o v $vgit_context.vvlocation') + println('Making a vprod compiler in $cdir') + scripting.run('./v -cc $cc -prod -o vprod $vgit_context.vvlocation') + println('Stripping and compressing cv v and vprod binaries in $cdir') + scripting.run('cp cv cv_stripped') + scripting.run('cp v v_stripped') + scripting.run('cp vprod vprod_stripped') + scripting.run('strip *_stripped') + scripting.run('cp cv_stripped cv_stripped_upxed') + scripting.run('cp v_stripped v_stripped_upxed') + scripting.run('cp vprod_stripped vprod_stripped_upxed') + scripting.run('upx -qqq --lzma cv_stripped_upxed') + scripting.run('upx -qqq --lzma v_stripped_upxed') + scripting.run('upx -qqq --lzma vprod_stripped_upxed') + scripting.show_sizes_of_files(['$cdir/cv', '$cdir/cv_stripped', '$cdir/cv_stripped_upxed']) + scripting.show_sizes_of_files(['$cdir/v', '$cdir/v_stripped', '$cdir/v_stripped_upxed']) + scripting.show_sizes_of_files(['$cdir/vprod', '$cdir/vprod_stripped', + '$cdir/vprod_stripped_upxed', + ]) + vversion := scripting.run('$cdir/v -version') + vcommit := scripting.run('git rev-parse --short --verify HEAD') + println('V version is: $vversion , local source commit: $vcommit') + if vgit_context.vvlocation == 'cmd/v' { + if os.exists('vlib/v/ast/ast.v') { + println('Source lines of the compiler: ' + + scripting.run('find cmd/v/ vlib/v/ -name "*.v" | grep -v /tests/ | xargs wc | tail -n -1')) + } else { + println('Source lines of the compiler: ' + + scripting.run('wc cmd/v/*.v vlib/compiler/*.v | tail -n -1')) + } + } else if vgit_context.vvlocation == 'v.v' { + println('Source lines of the compiler: ' + + scripting.run('wc v.v vlib/compiler/*.v | tail -n -1')) + } else { + println('Source lines of the compiler: ' + scripting.run('wc compiler/*.v | tail -n -1')) + } +} + +fn (c Context) compare_v_performance(label string, commands []string) string { + println('---------------------------------------------------------------------------------') + println('Compare v performance when doing the following commands ($label):') + mut source_location_a := '' + mut source_location_b := '' + if os.exists('$c.a/cmd/v') { + source_location_a = 'cmd/v' + } else { + source_location_a = if os.exists('$c.a/v.v') { 'v.v ' } else { 'compiler/ ' } + } + if os.exists('$c.b/cmd/v') { + source_location_b = 'cmd/v' + } else { + source_location_b = if os.exists('$c.b/v.v') { 'v.v ' } else { 'compiler/ ' } + } + timestamp_a, _ := vgit.line_to_timestamp_and_commit(scripting.run('cd $c.a/ ; git rev-list -n1 --timestamp HEAD')) + timestamp_b, _ := vgit.line_to_timestamp_and_commit(scripting.run('cd $c.b/ ; git rev-list -n1 --timestamp HEAD')) + debug_option_a := if timestamp_a > 1570877641 { '-cg ' } else { '-debug ' } + debug_option_b := if timestamp_b > 1570877641 { '-cg ' } else { '-debug ' } + mut hyperfine_commands_arguments := []string{} + for cmd in commands { + println(cmd) + } + for cmd in commands { + hyperfine_commands_arguments << ' \'cd ${c.b:-34s} ; ./$cmd \' '.replace_each([ + '@COMPILER@', + source_location_b, + '@DEBUG@', + debug_option_b, + ]) + } + for cmd in commands { + hyperfine_commands_arguments << ' \'cd ${c.a:-34s} ; ./$cmd \' '.replace_each([ + '@COMPILER@', + source_location_a, + '@DEBUG@', + debug_option_a, + ]) + } + // ///////////////////////////////////////////////////////////////////////////// + cmd_stats_file := os.real_path([c.vgo.workdir, 'v_performance_stats_${label}.json'].join(os.path_separator)) + comparison_cmd := 'hyperfine $c.hyperfineopts ' + '--export-json $cmd_stats_file ' + + '--time-unit millisecond ' + '--style full --warmup $c.warmups ' + + hyperfine_commands_arguments.join(' ') + // ///////////////////////////////////////////////////////////////////////////// + if c.vgo.verbose { + println(comparison_cmd) + } + os.system(comparison_cmd) + println('The detailed performance comparison report was saved to: $cmd_stats_file .') + println('') + return cmd_stats_file +} + +fn main() { + scripting.used_tools_must_exist(['cp', 'rm', 'strip', 'make', 'git', 'upx', 'cc', 'wc', 'tail', + 'find', 'xargs', 'hyperfine']) + mut context := new_context() + mut fp := flag.new_flag_parser(os.args) + fp.application(os.file_name(os.executable())) + fp.version(tool_version) + fp.description(tool_description) + fp.arguments_description('COMMIT_BEFORE [COMMIT_AFTER]') + fp.skip_executable() + fp.limit_free_args(1, 2) + context.vflags = fp.string('vflags', 0, '', 'Additional options to pass to the v commands, for example "-cc tcc"') + context.hyperfineopts = fp.string('hyperfine_options', 0, '', 'Additional options passed to hyperfine. +${flag.space}For example on linux, you may want to pass: +$flag.space--hyperfine_options "--prepare \'sync; echo 3 | sudo tee /proc/sys/vm/drop_caches\'" +') + commits := vgit.add_common_tool_options(mut context.vgo, mut fp) + context.commit_before = commits[0] + if commits.len > 1 { + context.commit_after = commits[1] + } + context.b = vgit.normalized_workpath_for_commit(context.vgo.workdir, context.commit_before) + context.a = vgit.normalized_workpath_for_commit(context.vgo.workdir, context.commit_after) + context.vc = vgit.normalized_workpath_for_commit(context.vgo.workdir, 'vc') + if !os.is_dir(context.vgo.workdir) { + msg := 'Work folder: ' + context.vgo.workdir + ' , does not exist.' + eprintln(msg) + exit(2) + } + context.compare_versions() +} diff --git a/v_windows/v/old/cmd/tools/repeat.v b/v_windows/v/old/cmd/tools/repeat.v new file mode 100644 index 0000000..e69cfdf --- /dev/null +++ b/v_windows/v/old/cmd/tools/repeat.v @@ -0,0 +1,374 @@ +module main + +import os +import flag +import time +import term +import math +import scripting + +struct CmdResult { +mut: + runs int + cmd string + icmd int + outputs []string + oms map[string][]int + summary map[string]Aints + timings []int + atiming Aints +} + +struct Context { +mut: + count int + series int + warmup int + show_help bool + show_output bool + use_newline bool // use \n instead of \r, so the last line is not overwritten + fail_on_regress_percent int + fail_on_maxtime int // in ms + verbose bool + commands []string + results []CmdResult + cmd_template string // {T} will be substituted with the current command + cmd_params map[string][]string + cline string // a terminal clearing line + cgoback string + nmins int // number of minimums to discard + nmaxs int // number of maximums to discard +} + +[unsafe] +fn (mut result CmdResult) free() { + unsafe { + result.cmd.free() + result.outputs.free() + result.oms.free() + result.summary.free() + result.timings.free() + result.atiming.free() + } +} + +[unsafe] +fn (mut context Context) free() { + unsafe { + context.commands.free() + context.results.free() + context.cmd_template.free() + context.cmd_params.free() + context.cline.free() + context.cgoback.free() + } +} + +struct Aints { + values []int +mut: + imin int + imax int + average f64 + stddev f64 + nmins int // number of discarded fastest results + nmaxs int // number of discarded slowest results +} + +[unsafe] +fn (mut a Aints) free() { + unsafe { a.values.free() } +} + +fn new_aints(ovals []int, extreme_mins int, extreme_maxs int) Aints { + mut res := Aints{ + values: ovals // remember the original values + nmins: extreme_mins + nmaxs: extreme_maxs + } + mut sum := i64(0) + mut imin := math.max_i32 + mut imax := -math.max_i32 + // discard the extremes: + mut vals := []int{} + for x in ovals { + vals << x + } + vals.sort() + if vals.len > extreme_mins + extreme_maxs { + vals = vals[extreme_mins..vals.len - extreme_maxs].clone() + } else { + vals = [] + } + // statistical processing of the remaining values: + for i in vals { + sum += i + if i < imin { + imin = i + } + if i > imax { + imax = i + } + } + res.imin = imin + res.imax = imax + if vals.len > 0 { + res.average = sum / f64(vals.len) + } + // + mut devsum := f64(0.0) + for i in vals { + x := f64(i) - res.average + devsum += (x * x) + } + res.stddev = math.sqrt(devsum / f64(vals.len)) + // eprintln('\novals: $ovals\n vals: $vals\n vals.len: $vals.len | res.imin: $res.imin | res.imax: $res.imax | res.average: $res.average | res.stddev: $res.stddev') + return res +} + +fn bold(s string) string { + return term.colorize(term.bold, s) +} + +fn (a Aints) str() string { + return bold('${a.average:6.2f}') + + 'ms ± σ: ${a.stddev:4.1f}ms, min: ${a.imin:4}ms, max: ${a.imax:4}ms, runs:${a.values.len:3}, nmins:${a.nmins:2}, nmaxs:${a.nmaxs:2}' +} + +const ( + max_fail_percent = 100 * 1000 + max_time = 60 * 1000 // ms + performance_regression_label = 'Performance regression detected, failing since ' +) + +fn main() { + mut context := Context{} + context.parse_options() + context.run() + context.show_diff_summary() +} + +fn (mut context Context) parse_options() { + mut fp := flag.new_flag_parser(os.args) + fp.application(os.file_name(os.executable())) + fp.version('0.0.1') + fp.description('Repeat command(s) and collect statistics. NB: you have to quote each command, if it contains spaces.') + fp.arguments_description('CMD1 CMD2 ...') + fp.skip_executable() + fp.limit_free_args_to_at_least(1) + context.count = fp.int('count', `c`, 10, 'Repetition count.') + context.series = fp.int('series', `s`, 2, 'Series count. `-s 2 -c 4 a b` => aaaabbbbaaaabbbb, while `-s 3 -c 2 a b` => aabbaabbaabb.') + context.warmup = fp.int('warmup', `w`, 2, 'Warmup runs. These are done *only at the start*, and are ignored.') + context.show_help = fp.bool('help', `h`, false, 'Show this help screen.') + context.use_newline = fp.bool('newline', `n`, false, 'Use \\n, do not overwrite the last line. Produces more output, but easier to diagnose.') + context.show_output = fp.bool('output', `O`, false, 'Show command stdout/stderr in the progress indicator for each command. NB: slower, for verbose commands.') + context.verbose = fp.bool('verbose', `v`, false, 'Be more verbose.') + context.fail_on_maxtime = fp.int('max_time', `m`, max_time, 'Fail with exit code 2, when first cmd takes above M milliseconds (regression).') + context.fail_on_regress_percent = fp.int('fail_percent', `f`, max_fail_percent, 'Fail with exit code 3, when first cmd is X% slower than the rest (regression).') + context.cmd_template = fp.string('template', `t`, '{T}', 'Command template. {T} will be substituted with the current command.') + cmd_params := fp.string_multi('parameter', `p`, 'A parameter substitution list. `{p}=val1,val2,val2` means that {p} in the template, will be substituted with each of val1, val2, val3.') + context.nmins = fp.int('nmins', `i`, 0, 'Ignore the BOTTOM X results (minimum execution time). Makes the results more robust to performance flukes.') + context.nmaxs = fp.int('nmaxs', `a`, 1, 'Ignore the TOP X results (maximum execution time). Makes the results more robust to performance flukes.') + for p in cmd_params { + parts := p.split(':') + if parts.len > 1 { + context.cmd_params[parts[0]] = parts[1].split(',') + } + } + if context.show_help { + println(fp.usage()) + exit(0) + } + if context.verbose { + scripting.set_verbose(true) + } + commands := fp.finalize() or { + eprintln('Error: $err') + exit(1) + } + context.commands = context.expand_all_commands(commands) + context.results = []CmdResult{len: context.commands.len, cap: 20, init: CmdResult{ + outputs: []string{cap: 500} + timings: []int{cap: 500} + }} + if context.use_newline { + context.cline = '\n' + context.cgoback = '\n' + } else { + context.cline = '\r' + term.h_divider('') + context.cgoback = '\r' + } +} + +fn (mut context Context) clear_line() { + print(context.cline) +} + +fn (mut context Context) expand_all_commands(commands []string) []string { + mut all_commands := []string{} + for cmd in commands { + maincmd := context.cmd_template.replace('{T}', cmd) + mut substituted_commands := []string{} + substituted_commands << maincmd + for paramk, paramlist in context.cmd_params { + for paramv in paramlist { + mut new_substituted_commands := []string{} + for cscmd in substituted_commands { + scmd := cscmd.replace(paramk, paramv) + new_substituted_commands << scmd + } + for sc in new_substituted_commands { + substituted_commands << sc + } + } + } + for sc in substituted_commands { + all_commands << sc + } + } + mut unique := map[string]int{} + for x in all_commands { + if x.contains('{') && x.contains('}') { + continue + } + unique[x] = 1 + } + return unique.keys() +} + +fn (mut context Context) run() { + mut run_warmups := 0 + for si in 1 .. context.series + 1 { + for icmd, cmd in context.commands { + mut runs := 0 + mut duration := 0 + mut sum := 0 + mut oldres := '' + println('Series: ${si:4}/${context.series:-4}, command: $cmd') + if context.warmup > 0 && run_warmups < context.commands.len { + for i in 1 .. context.warmup + 1 { + print('${context.cgoback}warming up run: ${i:4}/${context.warmup:-4} for ${cmd:-50s} took ${duration:6} ms ...') + mut sw := time.new_stopwatch() + res := os.execute(cmd) + if res.exit_code != 0 { + continue + } + duration = int(sw.elapsed().milliseconds()) + } + run_warmups++ + } + context.clear_line() + for i in 1 .. (context.count + 1) { + avg := f64(sum) / f64(i) + print('${context.cgoback}Average: ${avg:9.3f}ms | run: ${i:4}/${context.count:-4} | took ${duration:6} ms') + if context.show_output { + print(' | result: ${oldres:s}') + } + mut sw := time.new_stopwatch() + res := scripting.exec(cmd) or { continue } + duration = int(sw.elapsed().milliseconds()) + if res.exit_code != 0 { + eprintln('${i:10} non 0 exit code for cmd: $cmd') + continue + } + trimed_output := res.output.trim_right('\r\n') + trimed_normalized := trimed_output.replace('\r\n', '\n') + lines := trimed_normalized.split('\n') + for line in lines { + context.results[icmd].outputs << line + } + context.results[icmd].timings << duration + sum += duration + runs++ + oldres = res.output.replace('\n', ' ') + } + context.results[icmd].cmd = cmd + context.results[icmd].icmd = icmd + context.results[icmd].runs += runs + context.results[icmd].atiming = new_aints(context.results[icmd].timings, context.nmins, + context.nmaxs) + context.clear_line() + print(context.cgoback) + mut m := map[string][]int{} + ioutputs := context.results[icmd].outputs + for o in ioutputs { + x := o.split(':') + if x.len > 1 { + k := x[0] + v := x[1].trim_left(' ').int() + m[k] << v + } + } + mut summary := map[string]Aints{} + for k, v in m { + // show a temporary summary for the current series/cmd cycle + s := new_aints(v, context.nmins, context.nmaxs) + println(' $k: $s') + summary[k] = s + } + // merge current raw results to the previous ones + old_oms := context.results[icmd].oms.move() + mut new_oms := map[string][]int{} + for k, v in m { + if old_oms[k].len == 0 { + new_oms[k] = v + } else { + new_oms[k] << old_oms[k] + new_oms[k] << v + } + } + context.results[icmd].oms = new_oms.move() + // println('') + } + } + // create full summaries, taking account of all runs + for icmd in 0 .. context.results.len { + mut new_full_summary := map[string]Aints{} + for k, v in context.results[icmd].oms { + new_full_summary[k] = new_aints(v, context.nmins, context.nmaxs) + } + context.results[icmd].summary = new_full_summary.move() + } +} + +fn (mut context Context) show_diff_summary() { + context.results.sort_with_compare(fn (a &CmdResult, b &CmdResult) int { + if a.atiming.average < b.atiming.average { + return -1 + } + if a.atiming.average > b.atiming.average { + return 1 + } + return 0 + }) + println('Summary (commands are ordered by ascending mean time), after $context.series series of $context.count repetitions:') + base := context.results[0].atiming.average + mut first_cmd_percentage := f64(100.0) + mut first_marker := '' + for i, r in context.results { + first_marker = ' ' + cpercent := (r.atiming.average / base) * 100 - 100 + if r.icmd == 0 { + first_marker = bold('>') + first_cmd_percentage = cpercent + } + println(' $first_marker${(i + 1):3} | ${cpercent:5.1f}% slower | ${r.cmd:-57s} | $r.atiming') + } + $if debugcontext ? { + println('context: $context') + } + if int(base) > context.fail_on_maxtime { + print(performance_regression_label) + println('average time: ${base:6.1f} ms > $context.fail_on_maxtime ms threshold.') + exit(2) + } + if context.fail_on_regress_percent == max_fail_percent || context.results.len < 2 { + return + } + fail_threshold_max := f64(context.fail_on_regress_percent) + if first_cmd_percentage > fail_threshold_max { + print(performance_regression_label) + println('${first_cmd_percentage:5.1f}% > ${fail_threshold_max:5.1f}% threshold.') + exit(3) + } +} diff --git a/v_windows/v/old/cmd/tools/test_if_v_test_system_works.v b/v_windows/v/old/cmd/tools/test_if_v_test_system_works.v new file mode 100644 index 0000000..a86abd8 --- /dev/null +++ b/v_windows/v/old/cmd/tools/test_if_v_test_system_works.v @@ -0,0 +1,74 @@ +module main + +// This program verifies that `v test` propagates errors +// and that it exits with code 1, when at least 1 FAIL happen. +import os +import rand + +const ( + vexe = get_vexe_path() + vroot = os.dir(vexe) + tdir = new_tdir() +) + +fn get_vexe_path() string { + env_vexe := os.getenv('VEXE') + if env_vexe != '' { + return env_vexe + } + me := os.executable() + eprintln('me: $me') + mut vexe_ := os.join_path(os.dir(os.dir(os.dir(me))), 'v') + if os.user_os() == 'windows' { + vexe_ += '.exe' + } + return vexe_ +} + +fn new_tdir() string { + tdir_ := os.join_path(os.temp_dir(), rand.ulid()) + if os.exists(tdir_) { + os.rmdir(tdir_) or { panic(err) } + } + os.mkdir(tdir_) or { panic(err) } + C.atexit(cleanup_tdir) + return tdir_ +} + +fn cleanup_tdir() { + println('... removing tdir: $tdir') + os.rmdir_all(tdir) or { panic(err) } +} + +fn main() { + println('> vroot: $vroot | vexe: $vexe | tdir: $tdir') + ok_fpath := os.join_path(tdir, 'single_test.v') + os.write_file(ok_fpath, 'fn test_ok(){ assert true }') ? + check_ok('"$vexe" $ok_fpath') + check_ok('"$vexe" test $ok_fpath') + fail_fpath := os.join_path(tdir, 'failing_test.v') + os.write_file(fail_fpath, 'fn test_fail(){ assert 1 == 2 }') ? + check_fail('"$vexe" $fail_fpath') + check_fail('"$vexe" test $fail_fpath') + check_fail('"$vexe" test $tdir') +} + +fn check_ok(cmd string) string { + println('> check_ok cmd: $cmd') + res := os.execute(cmd) + if res.exit_code != 0 { + eprintln('> check_ok failed.\n$res.output') + exit(1) + } + return res.output +} + +fn check_fail(cmd string) string { + println('> check_fail cmd: $cmd') + res := os.execute(cmd) + if res.exit_code == 0 { + eprintln('> check_fail succeeded, but it should have failed.\n$res.output') + exit(1) + } + return res.output +} diff --git a/v_windows/v/old/cmd/tools/test_os_process.v b/v_windows/v/old/cmd/tools/test_os_process.v new file mode 100644 index 0000000..bc275dd --- /dev/null +++ b/v_windows/v/old/cmd/tools/test_os_process.v @@ -0,0 +1,82 @@ +module main + +import os +import time +import os.cmdline + +enum Target { + both + stderr + stdout + alternate +} + +fn s2target(s string) Target { + return match s { + 'both' { Target.both } + 'stderr' { Target.stderr } + 'alternate' { Target.alternate } + else { Target.stdout } + } +} + +struct Context { +mut: + timeout_ms int + period_ms int + exitcode int + target Target + omode Target + is_verbose bool +} + +fn (mut ctx Context) println(s string) { + if ctx.target == .alternate { + ctx.omode = if ctx.omode == .stderr { Target.stdout } else { Target.stderr } + } + if ctx.target in [.both, .stdout] || ctx.omode == .stdout { + println('stdout, $s') + } + if ctx.target in [.both, .stderr] || ctx.omode == .stderr { + eprintln('stderr, $s') + } +} + +fn do_timeout(c &Context) { + mut ctx := unsafe { c } + time.sleep(ctx.timeout_ms * time.millisecond) + exit(ctx.exitcode) +} + +fn main() { + mut ctx := Context{} + args := os.args[1..] + if '-h' in args || '--help' in args { + println("Usage: + test_os_process [-v] [-h] [-target stderr/stdout/both/alternate] [-exitcode 0] [-timeout_ms 200] [-period_ms 50] + Prints lines periodically (-period_ms), to stdout/stderr (-target). + After a while (-timeout_ms), exit with (-exitcode). + This program is useful for platform independent testing + of child process/standart input/output control. + It is used in V's `os` module tests. +") + return + } + ctx.is_verbose = '-v' in args + ctx.target = s2target(cmdline.option(args, '-target', 'both')) + ctx.exitcode = cmdline.option(args, '-exitcode', '0').int() + ctx.timeout_ms = cmdline.option(args, '-timeout_ms', '200').int() + ctx.period_ms = cmdline.option(args, '-period_ms', '50').int() + if ctx.target == .alternate { + ctx.omode = .stdout + } + if ctx.is_verbose { + eprintln('> args: $args | context: $ctx') + } + go do_timeout(&ctx) + for i := 1; true; i++ { + ctx.println('$i') + time.sleep(ctx.period_ms * time.millisecond) + } + time.sleep(100 * time.second) +} diff --git a/v_windows/v/old/cmd/tools/vast/cjson.v b/v_windows/v/old/cmd/tools/vast/cjson.v new file mode 100644 index 0000000..63e9f5d --- /dev/null +++ b/v_windows/v/old/cmd/tools/vast/cjson.v @@ -0,0 +1,114 @@ +module main + +import json + +struct UseJson { + x int +} + +fn suppress_json_warning() { + json.encode(UseJson{}) +} + +// struct C.cJSON {} +fn C.cJSON_CreateObject() &C.cJSON + +fn C.cJSON_CreateArray() &C.cJSON + +// fn C.cJSON_CreateBool(bool) &C.cJSON +fn C.cJSON_CreateTrue() &C.cJSON + +fn C.cJSON_CreateFalse() &C.cJSON + +fn C.cJSON_CreateNull() &C.cJSON + +// fn C.cJSON_CreateNumber() &C.cJSON +// fn C.cJSON_CreateString() &C.cJSON +fn C.cJSON_CreateRaw(&byte) &C.cJSON + +fn C.cJSON_IsInvalid(voidptr) bool + +fn C.cJSON_IsFalse(voidptr) bool + +// fn C.cJSON_IsTrue(voidptr) bool +fn C.cJSON_IsBool(voidptr) bool + +fn C.cJSON_IsNull(voidptr) bool + +fn C.cJSON_IsNumber(voidptr) bool + +fn C.cJSON_IsString(voidptr) bool + +fn C.cJSON_IsArray(voidptr) bool + +fn C.cJSON_IsObject(voidptr) bool + +fn C.cJSON_IsRaw(voidptr) bool + +fn C.cJSON_AddItemToObject(voidptr, &byte, voidptr) + +fn C.cJSON_AddItemToArray(voidptr, voidptr) + +fn C.cJSON_Delete(voidptr) + +fn C.cJSON_Print(voidptr) &byte + +[inline] +fn create_object() &C.cJSON { + return C.cJSON_CreateObject() +} + +[inline] +fn create_array() &C.cJSON { + return C.cJSON_CreateArray() +} + +[inline] +fn create_string(val string) &C.cJSON { + return C.cJSON_CreateString(val.str) +} + +[inline] +fn create_number(val f64) &C.cJSON { + return C.cJSON_CreateNumber(val) +} + +[inline] +fn create_bool(val bool) &C.cJSON { + return C.cJSON_CreateBool(val) +} + +[inline] +fn create_true() &C.cJSON { + return C.cJSON_CreateTrue() +} + +[inline] +fn create_false() &C.cJSON { + return C.cJSON_CreateFalse() +} + +[inline] +fn create_null() &C.cJSON { + return C.cJSON_CreateNull() +} + +[inline] +fn delete(b voidptr) { + C.cJSON_Delete(b) +} + +[inline] +fn add_item_to_object(obj &C.cJSON, key string, item &C.cJSON) { + C.cJSON_AddItemToObject(obj, key.str, item) +} + +[inline] +fn add_item_to_array(obj &C.cJSON, item &C.cJSON) { + C.cJSON_AddItemToArray(obj, item) +} + +fn json_print(json &C.cJSON) string { + s := C.cJSON_Print(json) + return unsafe { tos3(s) } +} diff --git a/v_windows/v/old/cmd/tools/vast/test/.gitignore b/v_windows/v/old/cmd/tools/vast/test/.gitignore new file mode 100644 index 0000000..857cade --- /dev/null +++ b/v_windows/v/old/cmd/tools/vast/test/.gitignore @@ -0,0 +1 @@ +demo.json diff --git a/v_windows/v/old/cmd/tools/vast/test/demo.v b/v_windows/v/old/cmd/tools/vast/test/demo.v new file mode 100644 index 0000000..a2176b1 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vast/test/demo.v @@ -0,0 +1,121 @@ +// usage test: v ast path_to_v/cmd/tools/vast/test/demo.v +// will generate demo.json + +// comment for module +module main + +// import module +import os +import math +import time { Time, now } + +// const decl +const ( + a = 1 + b = 3 + c = 'c' +) + +// struct decl +struct Point { + x int +mut: + y int +pub: + z int +pub mut: + name string +} + +// method of Point +pub fn (p Point) get_x() int { + return p.x +} + +// embed struct +struct MyPoint { + Point + title string +} + +// enum type +enum Color { + red + green + blue +} + +// type alias +type Myint = int + +// sum type +type MySumType = bool | int | string + +// function type +type Myfn = fn (int) int + +// interface type +interface Myinterfacer { + add(int, int) int + sub(int, int) int +} + +// main funciton +fn main() { + add(1, 3) + println(add(1, 2)) + println('ok') // comment println + arr := [1, 3, 5, 7] + for a in arr { + println(a) + add(1, 3) + } + color := Color.red + println(color) + println(os.args) + m := math.max(1, 3) + println(m) + println(now()) + t := Time{} + println(t) + p := Point{ + x: 1 + y: 2 + z: 3 + } + println(p) + my_point := MyPoint{ + // x: 1 + // y: 3 + // z: 5 + } + println(my_point.get_x()) +} + +// normal function +fn add(x int, y int) int { + return x + y +} + +// function with defer stmt +fn defer_fn() { + mut x := 1 + println('start fn') + defer { + println('in defer block') + println(x) + } + println('end fn') +} + +// generic function +fn g_fn(p T) T { + return p +} + +// generic struct +struct GenericStruct { + point Point +mut: + model T +} diff --git a/v_windows/v/old/cmd/tools/vast/vast.v b/v_windows/v/old/cmd/tools/vast/vast.v new file mode 100644 index 0000000..fe2d5da --- /dev/null +++ b/v_windows/v/old/cmd/tools/vast/vast.v @@ -0,0 +1,2246 @@ +module main + +import os +import time +import flag +import v.token +import v.parser +import v.ast +import v.pref +import v.errors + +struct Context { +mut: + is_watch bool + is_compile bool + is_print bool +} + +struct HideFields { +mut: + names map[string]bool +} + +const hide_fields = &HideFields{} + +fn main() { + if os.args.len < 2 { + eprintln('not enough parameters') + exit(1) + } + mut ctx := Context{} + mut fp := flag.new_flag_parser(os.args[2..]) + fp.application('v ast') + fp.usage_example('demo.v generate demo.json file.') + fp.usage_example('-w demo.v generate demo.json file, and watch for changes.') + fp.usage_example('-c demo.v generate demo.json *and* a demo.c file, and watch for changes.') + fp.usage_example('-p demo.v print the json output to stdout.') + fp.description('Dump a JSON representation of the V AST for a given .v or .vsh file.') + fp.description('By default, `v ast` will save the JSON to a .json file, named after the .v file.') + fp.description('Pass -p to see it instead.') + ctx.is_watch = fp.bool('watch', `w`, false, 'watch a .v file for changes, rewrite the .json file, when a change is detected') + ctx.is_print = fp.bool('print', `p`, false, 'print the AST to stdout') + ctx.is_compile = fp.bool('compile', `c`, false, 'watch the .v file for changes, rewrite the .json file, *AND* generate a .c file too on any change') + hfields := fp.string_multi('hide', 0, 'hide the specified fields. You can give several, by separating them with `,`').join(',') + mut mhf := unsafe { hide_fields } + for hf in hfields.split(',') { + mhf.names[hf] = true + } + fp.limit_free_args_to_at_least(1) + rest_of_args := fp.remaining_parameters() + for vfile in rest_of_args { + file := get_abs_path(vfile) + check_file(file) + ctx.write_file_or_print(file) + if ctx.is_watch || ctx.is_compile { + ctx.watch_for_changes(file) + } + } +} + +fn (ctx Context) write_file_or_print(file string) { + if ctx.is_print { + println(json(file)) + } else { + println('$time.now(): AST written to: ' + json_file(file)) + } +} + +// generate ast json file and c source code file +fn (ctx Context) watch_for_changes(file string) { + println('start watching...') + mut timestamp := 0 + for { + new_timestamp := os.file_last_mod_unix(file) + if timestamp != new_timestamp { + ctx.write_file_or_print(file) + if ctx.is_compile { + file_name := file[0..(file.len - os.file_ext(file).len)] + os.system('v -o ${file_name}.c $file') + } + } + timestamp = new_timestamp + time.sleep(500 * time.millisecond) + } +} + +// get absolute path for file +fn get_abs_path(path string) string { + if os.is_abs_path(path) { + return path + } else if path.starts_with('./') { + return os.join_path(os.getwd(), path[2..]) + } else { + return os.join_path(os.getwd(), path) + } +} + +// check file is v file and exists +fn check_file(file string) { + if os.file_ext(file) !in ['.v', '.vv', '.vsh'] { + eprintln('the file `$file` must be a v file or vsh file') + exit(1) + } + if !os.exists(file) { + eprintln('the v file `$file` does not exist') + exit(1) + } +} + +// generate json file with the same file name +fn json_file(file string) string { + ast_json := json(file) + // support .v and .vsh file + file_name := file[0..(file.len - os.file_ext(file).len)] + json_file := file_name + '.json' + os.write_file(json_file, ast_json) or { panic(err) } + return json_file +} + +// generate json string +fn json(file string) string { + // use as permissive preferences as possible, so that `v ast` + // can print the AST of arbitrary V files, even .vsh or ones + // that require globals: + mut pref := &pref.Preferences{} + pref.fill_with_defaults() + pref.enable_globals = true + pref.is_fmt = true + // + mut t := Tree{ + root: new_object() + table: ast.new_table() + pref: pref + } + // parse file with comment + ast_file := parser.parse_file(file, t.table, .parse_comments, t.pref) + t.root = t.ast_file(ast_file) + // generate the ast string + s := json_print(t.root) + return s +} + +// the ast tree +struct Tree { + table &ast.Table + pref &pref.Preferences +mut: + root Node // the root of tree +} + +// tree node +pub type Node = C.cJSON + +// create an object node +[inline] +fn new_object() &Node { + return C.cJSON_CreateObject() +} + +// add item to object node +[inline] +fn (node &Node) add(key string, child &Node) { + if hide_fields.names.len > 0 && key in hide_fields.names { + return + } + add_item_to_object(node, key, child) +} + +// create an array node +[inline] +fn new_array() &Node { + return C.cJSON_CreateArray() +} + +// add item to array node +[inline] +fn (node &Node) add_item(child &Node) { + add_item_to_array(node, child) +} + +// string type node +fn (t Tree) string_node(val string) &Node { + return create_string(val) +} + +// number type node +fn (t Tree) number_node(val int) &Node { + return create_number(val) +} + +// bool type node +fn (t Tree) bool_node(val bool) &Node { + if val { + return create_true() + } else { + return create_false() + } +} + +// null type node +fn (t Tree) null_node() &Node { + return create_null() +} + +// type node +fn (t Tree) type_node(typ ast.Type) &Node { + if typ == 0 { + return create_null() + } else { + type_name := t.table.get_type_name(typ) + return create_string(type_name) + } +} + +// token type node +fn (t Tree) token_node(tok_kind token.Kind) &Node { + return t.string_node('token:${int(tok_kind)}($tok_kind.str())') +} + +// enum type node +fn (t Tree) enum_node(value T) &Node { + return t.string_node('enum:${int(value)}($value)') +} + +// for [][]comment +fn (t Tree) two_dimension_comment(node [][]ast.Comment) &Node { + mut comments := new_array() + for n in node { + mut comment_array := new_array() + for c in n { + comment_array.add_item(t.comment(c)) + } + comments.add_item(comment_array) + } + return comments +} + +// ast file root node +fn (t Tree) ast_file(node ast.File) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('ast.File')) + obj.add('path', t.string_node(node.path)) + obj.add('path_base', t.string_node(node.path_base)) + obj.add('nr_lines', t.number_node(node.nr_lines)) + obj.add('nr_bytes', t.number_node(node.nr_bytes)) + obj.add('mod', t.mod(node.mod)) + obj.add('imports', t.imports(node.imports)) + obj.add('global_scope', t.scope(node.global_scope)) + obj.add('scope', t.scope(node.scope)) + obj.add('errors', t.errors(node.errors)) + obj.add('warnings', t.warnings(node.warnings)) + obj.add('notices', t.notices(node.notices)) + obj.add('auto_imports', t.array_node_string(node.auto_imports)) + symbol_obj := new_object() + for key, val in node.imported_symbols { + symbol_obj.add(key, t.string_node(val)) + } + obj.add('imported_symbols', symbol_obj) + obj.add('generic_fns', t.array_node_generic_fns(node.generic_fns)) + obj.add('embedded_files', t.array_node_embed_file(node.embedded_files)) + obj.add('global_labels', t.array_node_string(node.global_labels)) + obj.add('is_test', t.bool_node(node.is_test)) + obj.add('stmts', t.stmts(node.stmts)) + return obj +} + +// embed files +fn (t Tree) embed_file(node ast.EmbeddedFile) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('EmbeddedFile')) + obj.add('rpath', t.string_node(node.rpath)) + obj.add('apath', t.string_node(node.apath)) + return obj +} + +// ast module node +fn (t Tree) mod(node ast.Module) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('Module')) + obj.add('name', t.string_node(node.name)) + obj.add('short_name', t.string_node(node.short_name)) + obj.add('attrs', t.array_node_attr(node.attrs)) + obj.add('pos', t.position(node.pos)) + obj.add('name_pos', t.position(node.name_pos)) + obj.add('is_skipped', t.bool_node(node.is_skipped)) + return obj +} + +fn (t Tree) scope(scope ast.Scope) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('Scope')) + obj.add('parent', t.string_node(ptr_str(scope.parent))) + children_arr := new_array() + for s in scope.children { + mut children_obj := new_object() + children_obj.add('parent', t.string_node(ptr_str(s.parent))) + children_obj.add('start_pos', t.number_node(s.start_pos)) + children_obj.add('end_pos', t.number_node(s.end_pos)) + children_arr.add_item(children_obj) + } + obj.add('children', children_arr) + obj.add('start_pos', t.number_node(scope.start_pos)) + obj.add('end_pos', t.number_node(scope.end_pos)) + obj.add('objects', t.objects(scope.objects)) + obj.add('struct_fields', t.array_node_scope_struct_field(scope.struct_fields)) + return obj +} + +fn (t Tree) scope_struct_field(node ast.ScopeStructField) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('ScopeStructField')) + obj.add('struct_type', t.type_node(node.struct_type)) + obj.add('name', t.string_node(node.name)) + obj.add('typ', t.type_node(node.typ)) + obj.add('orig_type', t.type_node(node.orig_type)) + obj.add('pos', t.position(node.pos)) + obj.add('smartcasts', t.array_node_type(node.smartcasts)) + return obj +} + +fn (t Tree) objects(so map[string]ast.ScopeObject) &Node { + mut obj := new_object() + for key, val in so { + obj.add(key, t.scope_object(val)) + } + return obj +} + +fn (t Tree) scope_object(node ast.ScopeObject) &Node { + mut obj := new_object() + match node { + ast.ConstField { t.const_field(node) } + ast.GlobalField { t.global_field(node) } + ast.Var { t.var(node) } + ast.AsmRegister { t.asm_register(node) } + } + return obj +} + +fn (t Tree) imports(nodes []ast.Import) &Node { + mut import_array := new_array() + for node in nodes { + import_array.add_item(t.import_module(node)) + } + return import_array +} + +fn (t Tree) errors(errors []errors.Error) &Node { + mut errs := new_array() + for e in errors { + obj := new_object() + obj.add('message', t.string_node(e.message)) + obj.add('file_path', t.string_node(e.file_path)) + obj.add('pos', t.position(e.pos)) + obj.add('backtrace', t.string_node(e.backtrace)) + obj.add('reporter', t.enum_node(e.reporter)) + errs.add_item(obj) + } + return errs +} + +fn (t Tree) warnings(warnings []errors.Warning) &Node { + mut warns := new_array() + for w in warnings { + mut obj := new_object() + obj.add('message', t.string_node(w.message)) + obj.add('file_path', t.string_node(w.file_path)) + obj.add('pos', t.position(w.pos)) + obj.add('reporter', t.enum_node(w.reporter)) + warns.add_item(obj) + } + return warns +} + +fn (t Tree) notices(notices []errors.Notice) &Node { + mut notice_array := new_array() + for n in notices { + mut obj := new_object() + obj.add('message', t.string_node(n.message)) + obj.add('file_path', t.string_node(n.file_path)) + obj.add('pos', t.position(n.pos)) + obj.add('reporter', t.enum_node(n.reporter)) + notice_array.add_item(obj) + } + return notice_array +} + +// stmt array node +fn (t Tree) stmts(stmts []ast.Stmt) &Node { + mut stmt_array := new_array() + for s in stmts { + stmt_array.add_item(t.stmt(s)) + } + return stmt_array +} + +fn (t Tree) stmt(node ast.Stmt) &Node { + match node { + ast.Module { return t.mod(node) } + ast.Import { return t.import_module(node) } + ast.ConstDecl { return t.const_decl(node) } + ast.FnDecl { return t.fn_decl(node) } + ast.StructDecl { return t.struct_decl(node) } + ast.EnumDecl { return t.enum_decl(node) } + ast.InterfaceDecl { return t.interface_decl(node) } + ast.HashStmt { return t.hash_stmt(node) } + ast.CompFor { return t.comp_for(node) } + ast.GlobalDecl { return t.global_decl(node) } + ast.DeferStmt { return t.defer_stmt(node) } + ast.TypeDecl { return t.type_decl(node) } + ast.GotoLabel { return t.goto_label(node) } + ast.GotoStmt { return t.goto_stmt(node) } + ast.AssignStmt { return t.assign_stmt(node) } + ast.Return { return t.return_(node) } + ast.ForCStmt { return t.for_c_stmt(node) } + ast.ForStmt { return t.for_stmt(node) } + ast.ForInStmt { return t.for_in_stmt(node) } + ast.BranchStmt { return t.branch_stmt(node) } + ast.AssertStmt { return t.assert_stmt(node) } + ast.ExprStmt { return t.expr_stmt(node) } + ast.Block { return t.block(node) } + ast.SqlStmt { return t.sql_stmt(node) } + ast.AsmStmt { return t.asm_stmt(node) } + ast.NodeError { return t.node_error(node) } + ast.EmptyStmt { return t.empty_stmt(node) } + } + return t.null_node() +} + +fn (t Tree) import_module(node ast.Import) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('Import')) + obj.add('mod', t.string_node(node.mod)) + obj.add('alias', t.string_node(node.alias)) + obj.add('syms', t.array_node_import_symbol(node.syms)) + obj.add('comments', t.array_node_comment(node.comments)) + obj.add('next_comments', t.array_node_comment(node.next_comments)) + obj.add('pos', t.position(node.pos)) + obj.add('mod_pos', t.position(node.mod_pos)) + obj.add('alias_pos', t.position(node.alias_pos)) + obj.add('syms_pos', t.position(node.syms_pos)) + return obj +} + +fn (t Tree) import_symbol(node ast.ImportSymbol) &Node { + mut obj := new_object() + obj.add('name', t.string_node(node.name)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) position(p token.Position) &Node { + mut obj := new_object() + obj.add('line_nr', t.number_node(p.line_nr)) + obj.add('last_line', t.number_node(p.last_line)) + obj.add('pos', t.number_node(p.pos)) + obj.add('len', t.number_node(p.len)) + return obj +} + +fn (t Tree) comment(node ast.Comment) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('Comment')) + obj.add('text', t.string_node(node.text)) + obj.add('is_multi', t.bool_node(node.is_multi)) + obj.add('is_inline', t.bool_node(node.is_inline)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) const_decl(node ast.ConstDecl) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('ConstDecl')) + obj.add('is_pub', t.bool_node(node.is_pub)) + obj.add('is_block', t.bool_node(node.is_block)) + obj.add('fields', t.array_node_const_field(node.fields)) + obj.add('end_comments', t.array_node_comment(node.end_comments)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) const_field(node ast.ConstField) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('ConstField')) + obj.add('mod', t.string_node(node.mod)) + obj.add('name', t.string_node(node.name)) + obj.add('expr', t.expr(node.expr)) + obj.add('is_pub', t.bool_node(node.is_pub)) + obj.add('typ', t.type_node(node.typ)) + obj.add('comments', t.array_node_comment(node.comments)) + obj.add('pos', t.position(node.pos)) + return obj +} + +// function declaration +fn (t Tree) fn_decl(node ast.FnDecl) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('FnDecl')) + obj.add('name', t.string_node(node.name)) + obj.add('mod', t.string_node(node.mod)) + obj.add('is_deprecated', t.bool_node(node.is_deprecated)) + obj.add('is_pub', t.bool_node(node.is_pub)) + obj.add('is_variadic', t.bool_node(node.is_variadic)) + obj.add('is_anon', t.bool_node(node.is_anon)) + obj.add('is_noreturn', t.bool_node(node.is_noreturn)) + obj.add('is_manualfree', t.bool_node(node.is_manualfree)) + obj.add('is_main', t.bool_node(node.is_main)) + obj.add('is_test', t.bool_node(node.is_test)) + obj.add('is_conditional', t.bool_node(node.is_conditional)) + obj.add('is_exported', t.bool_node(node.is_exported)) + obj.add('is_keep_alive', t.bool_node(node.is_keep_alive)) + obj.add('receiver', t.struct_field(node.receiver)) + obj.add('receiver_pos', t.position(node.receiver_pos)) + obj.add('is_method', t.bool_node(node.is_method)) + obj.add('method_idx', t.number_node(node.method_idx)) + obj.add('rec_mut', t.bool_node(node.rec_mut)) + obj.add('rec_share', t.enum_node(node.rec_share)) + obj.add('language', t.enum_node(node.language)) + obj.add('no_body', t.bool_node(node.no_body)) + obj.add('is_builtin', t.bool_node(node.is_builtin)) + obj.add('is_direct_arr', t.bool_node(node.is_direct_arr)) + obj.add('ctdefine_idx', t.number_node(node.ctdefine_idx)) + obj.add('pos', t.position(node.pos)) + obj.add('body_pos', t.position(node.body_pos)) + obj.add('return_type_pos', t.position(node.return_type_pos)) + obj.add('file', t.string_node(node.file)) + obj.add('has_return', t.bool_node(node.has_return)) + obj.add('return_type', t.type_node(node.return_type)) + obj.add('source_file', t.number_node(int(node.source_file))) + obj.add('scope', t.number_node(int(node.scope))) + obj.add('attrs', t.array_node_attr(node.attrs)) + obj.add('params', t.array_node_arg(node.params)) + obj.add('generic_names', t.array_node_string(node.generic_names)) + obj.add('stmts', t.array_node_stmt(node.stmts)) + obj.add('comments', t.array_node_comment(node.comments)) + obj.add('next_comments', t.array_node_comment(node.next_comments)) + obj.add('label_names', t.array_node_string(node.label_names)) + obj.add('defer_stmts', t.array_node_defer_stmt(node.defer_stmts)) + return obj +} + +fn (t Tree) anon_fn(node ast.AnonFn) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('AnonFn')) + obj.add('decl', t.fn_decl(node.decl)) + obj.add('typ', t.type_node(node.typ)) + obj.add('has_gen', t.bool_node(node.has_gen)) + return obj +} + +fn (t Tree) struct_decl(node ast.StructDecl) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('StructDecl')) + obj.add('name', t.string_node(node.name)) + obj.add('is_pub', t.bool_node(node.is_pub)) + obj.add('pub_pos', t.number_node(node.pub_pos)) + obj.add('mut_pos', t.number_node(node.mut_pos)) + obj.add('pub_mut_pos', t.number_node(node.pub_mut_pos)) + obj.add('global_pos', t.number_node(node.global_pos)) + obj.add('module_pos', t.number_node(node.module_pos)) + obj.add('language', t.enum_node(node.language)) + obj.add('is_union', t.bool_node(node.is_union)) + obj.add('pos', t.position(node.pos)) + obj.add('fields', t.array_node_struct_field(node.fields)) + obj.add('generic_types', t.array_node_type(node.generic_types)) + obj.add('attrs', t.array_node_attr(node.attrs)) + obj.add('end_comments', t.array_node_comment(node.end_comments)) + obj.add('embeds', t.array_node_embed(node.embeds)) + return obj +} + +fn (t Tree) struct_field(node ast.StructField) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('StructField')) + obj.add('name', t.string_node(node.name)) + obj.add('typ', t.type_node(node.typ)) + obj.add('type_pos', t.position(node.type_pos)) + obj.add('has_default_expr', t.bool_node(node.has_default_expr)) + obj.add('default_expr_typ', t.type_node(node.default_expr_typ)) + obj.add('default_expr', t.expr(node.default_expr)) + obj.add('is_pub', t.bool_node(node.is_pub)) + obj.add('is_mut', t.bool_node(node.is_mut)) + obj.add('is_global', t.bool_node(node.is_global)) + obj.add('attrs', t.array_node_attr(node.attrs)) + obj.add('comments', t.array_node_comment(node.comments)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) embed(node ast.Embed) &Node { + mut obj := new_object() + obj.add('typ', t.type_node(node.typ)) + obj.add('pos', t.position(node.pos)) + obj.add('comments', t.array_node_comment(node.comments)) + return obj +} + +fn (t Tree) enum_decl(node ast.EnumDecl) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('EnumDecl')) + obj.add('name', t.string_node(node.name)) + obj.add('is_pub', t.bool_node(node.is_pub)) + obj.add('is_flag', t.bool_node(node.is_flag)) + obj.add('is_multi_allowed', t.bool_node(node.is_multi_allowed)) + obj.add('pos', t.position(node.pos)) + obj.add('fields', t.array_node_enum_field(node.fields)) + obj.add('comments', t.array_node_comment(node.comments)) + obj.add('attrs', t.array_node_attr(node.attrs)) + return obj +} + +fn (t Tree) enum_field(node ast.EnumField) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('EnumField')) + obj.add('name', t.string_node(node.name)) + obj.add('has_expr', t.bool_node(node.has_expr)) + obj.add('expr', t.expr(node.expr)) + obj.add('pos', t.position(node.pos)) + obj.add('comments', t.array_node_comment(node.comments)) + obj.add('next_comments', t.array_node_comment(node.next_comments)) + return obj +} + +fn (t Tree) interface_decl(node ast.InterfaceDecl) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('InterfaceDecl')) + obj.add('name', t.string_node(node.name)) + obj.add('typ', t.type_node(node.typ)) + obj.add('is_pub', t.bool_node(node.is_pub)) + obj.add('mut_pos', t.number_node(node.mut_pos)) + obj.add('field_names', t.array_node_string(node.field_names)) + obj.add('methods', t.array_node_fn_decl(node.methods)) + obj.add('fields', t.array_node_struct_field(node.fields)) + obj.add('pre_comments', t.array_node_comment(node.pre_comments)) + obj.add('name_pos', t.position(node.name_pos)) + obj.add('language', t.enum_node(node.language)) + obj.add('pos', t.position(node.pos)) + obj.add('are_ifaces_expanded', t.bool_node(node.are_ifaces_expanded)) + obj.add('ifaces', t.array_node_interface_embedding(node.ifaces)) + return obj +} + +fn (t Tree) interface_embedding(node ast.InterfaceEmbedding) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('InterfaceEmbedding')) + obj.add('name', t.string_node(node.name)) + obj.add('typ', t.type_node(node.typ)) + obj.add('pos', t.position(node.pos)) + obj.add('comments', t.array_node_comment(node.comments)) + return obj +} + +fn (t Tree) attr(node ast.Attr) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('Attr')) + obj.add('name', t.string_node(node.name)) + obj.add('has_arg', t.bool_node(node.has_arg)) + obj.add('kind', t.enum_node(node.kind)) + obj.add('ct_expr', t.expr(node.ct_expr)) + obj.add('ct_opt', t.bool_node(node.ct_opt)) + obj.add('ct_evaled', t.bool_node(node.ct_evaled)) + obj.add('ct_skip', t.bool_node(node.ct_skip)) + obj.add('arg', t.string_node(node.arg)) + return obj +} + +fn (t Tree) hash_stmt(node ast.HashStmt) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('HashStmt')) + obj.add('mod', t.string_node(node.mod)) + obj.add('val', t.string_node(node.val)) + obj.add('kind', t.string_node(node.kind)) + obj.add('main', t.string_node(node.main)) + obj.add('msg', t.string_node(node.msg)) + obj.add('source_file', t.string_node(node.source_file)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) comp_for(node ast.CompFor) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('CompFor')) + obj.add('val_var', t.string_node(node.val_var)) + obj.add('typ', t.type_node(node.typ)) + obj.add('kind', t.enum_node(node.kind)) + obj.add('pos', t.position(node.pos)) + obj.add('typ_pos', t.position(node.pos)) + obj.add('stmts', t.array_node_stmt(node.stmts)) + return obj +} + +fn (t Tree) global_decl(node ast.GlobalDecl) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('GlobalDecl')) + obj.add('pos', t.position(node.pos)) + obj.add('is_block', t.bool_node(node.is_block)) + obj.add('fields', t.array_node_global_field(node.fields)) + obj.add('end_comments', t.array_node_comment(node.end_comments)) + return obj +} + +fn (t Tree) global_field(node ast.GlobalField) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('GlobalField')) + obj.add('name', t.string_node(node.name)) + obj.add('expr', t.expr(node.expr)) + obj.add('typ', t.type_node(node.typ)) + obj.add('has_expr', t.bool_node(node.has_expr)) + obj.add('comments', t.array_node_comment(node.comments)) + obj.add('pos', t.position(node.pos)) + obj.add('typ_pos', t.position(node.typ_pos)) + return obj +} + +fn (t Tree) defer_stmt(node ast.DeferStmt) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('DeferStmt')) + obj.add('stmts', t.array_node_stmt(node.stmts)) + obj.add('defer_vars', t.array_node_ident(node.defer_vars)) + obj.add('ifdef', t.string_node(node.ifdef)) + obj.add('idx_in_fn', t.number_node(node.idx_in_fn)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) type_decl(node ast.TypeDecl) &Node { + match node { + ast.AliasTypeDecl { return t.alias_type_decl(node) } + ast.FnTypeDecl { return t.fn_type_decl(node) } + ast.SumTypeDecl { return t.sum_type_decl(node) } + } +} + +fn (t Tree) alias_type_decl(node ast.AliasTypeDecl) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('AliasTypeDecl')) + obj.add('name', t.string_node(node.name)) + obj.add('is_pub', t.bool_node(node.is_pub)) + obj.add('parent_type', t.type_node(node.parent_type)) + obj.add('comments', t.array_node_comment(node.comments)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) sum_type_decl(node ast.SumTypeDecl) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('SumTypeDecl')) + obj.add('name', t.string_node(node.name)) + obj.add('is_pub', t.bool_node(node.is_pub)) + obj.add('pos', t.position(node.pos)) + obj.add('typ', t.type_node(node.typ)) + obj.add('comments', t.array_node_comment(node.comments)) + // variants + t_array := new_array() + for s in node.variants { + variants_obj := new_object() + variants_obj.add('typ', t.type_node(s.typ)) + variants_obj.add('pos', t.position(s.pos)) + t_array.add_item(variants_obj) + } + obj.add('variants', t_array) + return obj +} + +fn (t Tree) fn_type_decl(node ast.FnTypeDecl) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('FnTypeDecl')) + obj.add('name', t.string_node(node.name)) + obj.add('is_pub', t.bool_node(node.is_pub)) + obj.add('typ', t.type_node(node.typ)) + obj.add('pos', t.position(node.pos)) + obj.add('comments', t.array_node_comment(node.comments)) + return obj +} + +fn (t Tree) arg(node ast.Param) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('Param')) + obj.add('name', t.string_node(node.name)) + obj.add('typ', t.type_node(node.typ)) + obj.add('is_mut', t.bool_node(node.is_mut)) + return obj +} + +fn (t Tree) goto_label(node ast.GotoLabel) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('GotoLabel')) + obj.add('name', t.string_node(node.name)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) goto_stmt(node ast.GotoStmt) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('GotoStmt')) + obj.add('name', t.string_node(node.name)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) assign_stmt(node ast.AssignStmt) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('AssignStmt')) + obj.add('left', t.array_node_expr(node.left)) + obj.add('left_types', t.array_node_type(node.left_types)) + obj.add('right', t.array_node_expr(node.right)) + obj.add('right_types', t.array_node_type(node.left_types)) + obj.add('op', t.token_node(node.op)) + obj.add('is_static', t.bool_node(node.is_static)) + obj.add('is_simple', t.bool_node(node.is_simple)) + obj.add('has_cross_var', t.bool_node(node.has_cross_var)) + obj.add('pos', t.position(node.pos)) + obj.add('comments', t.array_node_comment(node.comments)) + obj.add('end_comments', t.array_node_comment(node.end_comments)) + return obj +} + +fn (t Tree) var(node ast.Var) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('Var')) + obj.add('name', t.string_node(node.name)) + obj.add('typ', t.type_node(node.typ)) + obj.add('orig_type', t.type_node(node.orig_type)) + obj.add('expr', t.expr(node.expr)) + obj.add('is_arg', t.bool_node(node.is_arg)) + obj.add('is_mut', t.bool_node(node.is_mut)) + obj.add('is_used', t.bool_node(node.is_used)) + obj.add('is_changed', t.bool_node(node.is_changed)) + obj.add('is_or', t.bool_node(node.is_or)) + obj.add('is_tmp', t.bool_node(node.is_tmp)) + obj.add('is_autofree_tmp', t.bool_node(node.is_autofree_tmp)) + obj.add('is_auto_deref', t.bool_node(node.is_auto_deref)) + obj.add('is_auto_heap', t.bool_node(node.is_auto_heap)) + obj.add('is_stack_obj', t.bool_node(node.is_stack_obj)) + obj.add('share', t.enum_node(node.share)) + obj.add('pos', t.position(node.pos)) + obj.add('smartcasts', t.array_node_type(node.smartcasts)) + return obj +} + +fn (t Tree) return_(node ast.Return) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('Return')) + obj.add('exprs', t.array_node_expr(node.exprs)) + obj.add('types', t.array_node_type(node.types)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) for_c_stmt(node ast.ForCStmt) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('ForCStmt')) + obj.add('init', t.stmt(node.init)) + obj.add('has_init', t.bool_node(node.has_init)) + obj.add('cond', t.expr(node.cond)) + obj.add('has_cond', t.bool_node(node.has_cond)) + obj.add('inc', t.stmt(node.inc)) + obj.add('has_inc', t.bool_node(node.has_inc)) + obj.add('is_multi', t.bool_node(node.is_multi)) + obj.add('label', t.string_node(node.label)) + obj.add('pos', t.position(node.pos)) + obj.add('scope', t.number_node(int(node.scope))) + obj.add('stmts', t.array_node_stmt(node.stmts)) + return obj +} + +fn (t Tree) for_stmt(node ast.ForStmt) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('ForStmt')) + obj.add('cond', t.expr(node.cond)) + obj.add('is_inf', t.bool_node(node.is_inf)) + obj.add('label', t.string_node(node.label)) + obj.add('pos', t.position(node.pos)) + obj.add('scope', t.number_node(int(node.scope))) + obj.add('stmts', t.array_node_stmt(node.stmts)) + return obj +} + +fn (t Tree) for_in_stmt(node ast.ForInStmt) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('ForInStmt')) + obj.add('key_var', t.string_node(node.key_var)) + obj.add('val_var', t.string_node(node.val_var)) + obj.add('cond', t.expr(node.cond)) + obj.add('is_range', t.bool_node(node.is_range)) + obj.add('high', t.expr(node.high)) + obj.add('key_type', t.type_node(node.key_type)) + obj.add('val_type', t.type_node(node.val_type)) + obj.add('cond_type', t.type_node(node.cond_type)) + obj.add('kind', t.enum_node(node.kind)) + obj.add('val_is_mut', t.bool_node(node.val_is_mut)) + obj.add('label', t.string_node(node.label)) + obj.add('pos', t.position(node.pos)) + obj.add('scope', t.number_node(int(node.scope))) + obj.add('stmts', t.array_node_stmt(node.stmts)) + return obj +} + +fn (t Tree) branch_stmt(node ast.BranchStmt) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('BranchStmt')) + obj.add('kind', t.token_node(node.kind)) + obj.add('label', t.string_node(node.label)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) assert_stmt(node ast.AssertStmt) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('AssertStmt')) + obj.add('expr', t.expr(node.expr)) + obj.add('is_used', t.bool_node(node.is_used)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) block(node ast.Block) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('Block')) + obj.add('stmts', t.array_node_stmt(node.stmts)) + obj.add('is_unsafe', t.bool_node(node.is_unsafe)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) comptime_call(node ast.ComptimeCall) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('ComptimeCall')) + obj.add('method_name', t.string_node(node.method_name)) + obj.add('left', t.expr(node.left)) + obj.add('is_vweb', t.bool_node(node.is_vweb)) + obj.add('vweb_tmpl', t.string_node(node.vweb_tmpl.path)) + obj.add('args_var', t.string_node(node.args_var)) + obj.add('sym', t.string_node(node.sym.name)) + obj.add('has_parens', t.bool_node(node.has_parens)) + obj.add('is_embed', t.bool_node(node.is_embed)) + obj.add('embed_file', t.embed_file(node.embed_file)) + obj.add('method_pos', t.position(node.method_pos)) + obj.add('result_type', t.type_node(node.result_type)) + obj.add('scope', t.scope(node.scope)) + obj.add('env_value', t.string_node(node.env_value)) + obj.add('pos', t.position(node.pos)) + obj.add('args', t.array_node_call_arg(node.args)) + return obj +} + +fn (t Tree) comptime_selector(node ast.ComptimeSelector) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('ComptimeSelector')) + obj.add('has_parens', t.bool_node(node.has_parens)) + obj.add('left', t.expr(node.left)) + obj.add('field_expr', t.expr(node.field_expr)) + obj.add('left_type', t.type_node(node.left_type)) + obj.add('typ', t.type_node(node.typ)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) expr_stmt(node ast.ExprStmt) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('ExprStmt')) + obj.add('typ', t.type_node(node.typ)) + obj.add('is_expr', t.bool_node(node.is_expr)) + obj.add('expr', t.expr(node.expr)) + obj.add('pos', t.position(node.pos)) + obj.add('comments', t.array_node_comment(node.comments)) + return obj +} + +// expr +fn (t Tree) expr(expr ast.Expr) &Node { + match expr { + ast.IntegerLiteral { + return t.integer_literal(expr) + } + ast.FloatLiteral { + return t.float_literal(expr) + } + ast.StringLiteral { + return t.string_literal(expr) + } + ast.CharLiteral { + return t.char_literal(expr) + } + ast.BoolLiteral { + return t.bool_literal(expr) + } + ast.StringInterLiteral { + return t.string_inter_literal(expr) + } + ast.EnumVal { + return t.enum_val(expr) + } + ast.Assoc { + return t.assoc(expr) + } + ast.AtExpr { + return t.at_expr(expr) + } + ast.CastExpr { + return t.cast_expr(expr) + } + ast.AsCast { + return t.as_cast(expr) + } + ast.TypeNode { + return t.type_expr(expr) + } + ast.SizeOf { + return t.size_of(expr) + } + ast.IsRefType { + return t.is_ref_type(expr) + } + ast.PrefixExpr { + return t.prefix_expr(expr) + } + ast.InfixExpr { + return t.infix_expr(expr) + } + ast.IndexExpr { + return t.index_expr(expr) + } + ast.PostfixExpr { + return t.postfix_expr(expr) + } + ast.SelectorExpr { + return t.selector_expr(expr) + } + ast.RangeExpr { + return t.range_expr(expr) + } + ast.IfExpr { + return t.if_expr(expr) + } + ast.Ident { + return t.ident(expr) + } + ast.CallExpr { + return t.call_expr(expr) + } + ast.OrExpr { + return t.or_expr(expr) + } + ast.StructInit { + return t.struct_init(expr) + } + ast.ArrayInit { + return t.array_init(expr) + } + ast.MapInit { + return t.map_init(expr) + } + ast.None { + return t.none_expr(expr) + } + ast.ParExpr { + return t.par_expr(expr) + } + ast.IfGuardExpr { + return t.if_guard_expr(expr) + } + ast.MatchExpr { + return t.match_expr(expr) + } + ast.ConcatExpr { + return t.concat_expr(expr) + } + ast.TypeOf { + return t.type_of(expr) + } + ast.Likely { + return t.likely(expr) + } + ast.SqlExpr { + return t.sql_expr(expr) + } + ast.ComptimeCall { + return t.comptime_call(expr) + } + ast.ComptimeSelector { + return t.comptime_selector(expr) + } + ast.LockExpr { + return t.lock_expr(expr) + } + ast.UnsafeExpr { + return t.unsafe_expr(expr) + } + ast.ChanInit { + return t.chan_init(expr) + } + ast.SelectExpr { + return t.select_expr(expr) + } + ast.Comment { + return t.comment(expr) + } + ast.AnonFn { + return t.anon_fn(expr) + } + ast.ArrayDecompose { + return t.array_decompose(expr) + } + ast.GoExpr { + return t.go_expr(expr) + } + ast.OffsetOf { + return t.offset_of(expr) + } + ast.DumpExpr { + return t.dump_expr(expr) + } + ast.NodeError { + return t.node_error(expr) + } + ast.EmptyExpr { + return t.empty_expr(expr) + } + else { + // println('unknown expr') + return t.null_node() + } + } +} + +fn (t Tree) integer_literal(node ast.IntegerLiteral) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('IntegerLiteral')) + obj.add('val', t.string_node(node.val)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) float_literal(node ast.FloatLiteral) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('FloatLiteral')) + obj.add('val', t.string_node(node.val)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) string_literal(node ast.StringLiteral) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('StringLiteral')) + obj.add('val', t.string_node(node.val)) + obj.add('is_raw', t.bool_node(node.is_raw)) + obj.add('language', t.enum_node(node.language)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) char_literal(node ast.CharLiteral) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('CharLiteral')) + obj.add('val', t.string_node(node.val)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) bool_literal(node ast.BoolLiteral) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('BoolLiteral')) + obj.add('val', t.bool_node(node.val)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) string_inter_literal(node ast.StringInterLiteral) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('StringInterLiteral')) + obj.add('vals', t.array_node_string(node.vals)) + obj.add('exprs', t.array_node_expr(node.exprs)) + obj.add('expr_types', t.array_node_type(node.expr_types)) + obj.add('fwidths', t.array_node_int(node.fwidths)) + obj.add('precisions', t.array_node_int(node.precisions)) + obj.add('pluss', t.array_node_bool(node.pluss)) + obj.add('fills', t.array_node_bool(node.fills)) + obj.add('fmt_poss', t.array_node_position(node.fmt_poss)) + obj.add('fmts', t.array_node_byte(node.fmts)) + obj.add('need_fmts', t.array_node_bool(node.need_fmts)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) enum_val(node ast.EnumVal) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('EnumVal')) + obj.add('enum_name', t.string_node(node.enum_name)) + obj.add('mod', t.string_node(node.mod)) + obj.add('val', t.string_node(node.val)) + obj.add('typ', t.type_node(node.typ)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) assoc(node ast.Assoc) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('Assoc')) + obj.add('var_name', t.string_node(node.var_name)) + obj.add('fields', t.array_node_string(node.fields)) + obj.add('exprs', t.array_node_expr(node.exprs)) + obj.add('typ', t.type_node(node.typ)) + obj.add('pos', t.position(node.pos)) + obj.add('scope', t.number_node(int(node.scope))) + return obj +} + +fn (t Tree) at_expr(node ast.AtExpr) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('AtExpr')) + obj.add('name', t.string_node(node.name)) + obj.add('pos', t.position(node.pos)) + obj.add('kind', t.enum_node(node.kind)) + obj.add('val', t.string_node(node.val)) + return obj +} + +fn (t Tree) cast_expr(node ast.CastExpr) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('CastExpr')) + obj.add('typ', t.type_node(node.typ)) + obj.add('ityp', t.number_node(int(node.typ))) + obj.add('typname', t.string_node(node.typname)) + obj.add('has_arg', t.bool_node(node.has_arg)) + obj.add('arg', t.expr(node.arg)) + obj.add('expr_type', t.type_node(node.expr_type)) + obj.add('expr', t.expr(node.expr)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) as_cast(node ast.AsCast) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('AsCast')) + obj.add('expr', t.expr(node.expr)) + obj.add('typ', t.type_node(node.typ)) + obj.add('expr_type', t.type_node(node.expr_type)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) type_expr(node ast.TypeNode) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('TypeNode')) + obj.add('typ', t.type_node(node.typ)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) size_of(node ast.SizeOf) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('SizeOf')) + obj.add('is_type', t.bool_node(node.is_type)) + obj.add('typ', t.type_node(node.typ)) + obj.add('expr', t.expr(node.expr)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) is_ref_type(node ast.IsRefType) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('IsRefType')) + obj.add('is_type', t.bool_node(node.is_type)) + obj.add('typ', t.type_node(node.typ)) + obj.add('expr', t.expr(node.expr)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) prefix_expr(node ast.PrefixExpr) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('PrefixExpr')) + obj.add('op', t.token_node(node.op)) + obj.add('right', t.expr(node.right)) + obj.add('right_type', t.type_node(node.right_type)) + obj.add('or_block', t.or_expr(node.or_block)) + obj.add('is_option', t.bool_node(node.is_option)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) infix_expr(node ast.InfixExpr) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('InfixExpr')) + obj.add('op', t.token_node(node.op)) + obj.add('left', t.expr(node.left)) + obj.add('left_type', t.type_node(node.left_type)) + obj.add('right', t.expr(node.right)) + obj.add('right_type', t.type_node(node.right_type)) + obj.add('auto_locked', t.string_node(node.auto_locked)) + obj.add('or_block', t.or_expr(node.or_block)) + obj.add('is_stmt', t.bool_node(node.is_stmt)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) index_expr(node ast.IndexExpr) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('IndexExpr')) + obj.add('left', t.expr(node.left)) + obj.add('left_type', t.type_node(node.left_type)) + obj.add('index', t.expr(node.index)) + obj.add('is_setter', t.bool_node(node.is_setter)) + obj.add('or_expr', t.or_expr(node.or_expr)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) postfix_expr(node ast.PostfixExpr) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('PostfixExpr')) + obj.add('op', t.token_node(node.op)) + obj.add('expr', t.expr(node.expr)) + obj.add('auto_locked', t.string_node(node.auto_locked)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) selector_expr(node ast.SelectorExpr) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('SelectorExpr')) + obj.add('expr', t.expr(node.expr)) + obj.add('expr_type', t.type_node(node.expr_type)) + obj.add('field_name', t.string_node(node.field_name)) + obj.add('typ', t.type_node(node.typ)) + obj.add('name_type', t.type_node(node.name_type)) + obj.add('from_embed_type', t.type_node(node.from_embed_type)) + obj.add('next_token', t.token_node(node.next_token)) + obj.add('pos', t.position(node.pos)) + obj.add('scope', t.number_node(int(node.scope))) + return obj +} + +fn (t Tree) range_expr(node ast.RangeExpr) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('RangeExpr')) + obj.add('low', t.expr(node.low)) + obj.add('high', t.expr(node.high)) + obj.add('has_high', t.bool_node(node.has_high)) + obj.add('has_low', t.bool_node(node.has_low)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) if_expr(node ast.IfExpr) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('IfExpr')) + obj.add('is_comptime', t.bool_node(node.is_comptime)) + obj.add('tok_kind', t.token_node(node.tok_kind)) + obj.add('branches', t.array_node_if_branch(node.branches)) + obj.add('left', t.expr(node.left)) + obj.add('typ', t.type_node(node.typ)) + obj.add('has_else', t.bool_node(node.has_else)) + obj.add('is_expr', t.bool_node(node.is_expr)) + obj.add('pos', t.position(node.pos)) + obj.add('post_comments', t.array_node_comment(node.post_comments)) + return obj +} + +fn (t Tree) if_branch(node ast.IfBranch) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('IfBranch')) + obj.add('cond', t.expr(node.cond)) + obj.add('pos', t.position(node.pos)) + obj.add('body_pos', t.position(node.body_pos)) + obj.add('scope', t.number_node(int(node.scope))) + obj.add('stmts', t.array_node_stmt(node.stmts)) + obj.add('comments', t.array_node_comment(node.comments)) + return obj +} + +fn (t Tree) ident(node ast.Ident) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('Ident')) + obj.add('mod', t.string_node(node.mod)) + obj.add('name', t.string_node(node.name)) + obj.add('language', t.enum_node(node.language)) + obj.add('is_mut', t.bool_node(node.is_mut)) + obj.add('comptime', t.bool_node(node.comptime)) + obj.add('tok_kind', t.token_node(node.tok_kind)) + obj.add('kind', t.enum_node(node.kind)) + obj.add('info', t.ident_info(node.info)) + obj.add('pos', t.position(node.pos)) + obj.add('mut_pos', t.position(node.mut_pos)) + obj.add('obj', t.scope_object(node.obj)) + obj.add('scope', t.number_node(int(node.scope))) + return obj +} + +fn (t Tree) ident_info(node ast.IdentInfo) &Node { + match node { + ast.IdentVar { return t.ident_var(node) } + ast.IdentFn { return t.ident_fn(node) } + } +} + +fn (t Tree) ident_var(node ast.IdentVar) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('IdentVar')) + obj.add('typ', t.type_node(node.typ)) + obj.add('is_mut', t.bool_node(node.is_mut)) + obj.add('is_static', t.bool_node(node.is_static)) + obj.add('is_optional', t.bool_node(node.is_optional)) + obj.add('share', t.enum_node(node.share)) + return obj +} + +fn (t Tree) ident_fn(node ast.IdentFn) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('IdentFn')) + obj.add('typ', t.type_node(node.typ)) + return obj +} + +fn (t Tree) call_expr(node ast.CallExpr) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('CallExpr')) + obj.add('mod', t.string_node(node.mod)) + obj.add('name', t.string_node(node.name)) + obj.add('language', t.enum_node(node.language)) + obj.add('left_type', t.type_node(node.left_type)) + obj.add('receiver_type', t.type_node(node.receiver_type)) + obj.add('return_type', t.type_node(node.return_type)) + obj.add('left', t.expr(node.left)) + obj.add('is_method', t.bool_node(node.is_method)) + obj.add('is_keep_alive', t.bool_node(node.is_keep_alive)) + obj.add('is_noreturn', t.bool_node(node.is_noreturn)) + obj.add('should_be_skipped', t.bool_node(node.should_be_skipped)) + obj.add('free_receiver', t.bool_node(node.free_receiver)) + obj.add('scope', t.number_node(int(node.scope))) + obj.add('args', t.array_node_call_arg(node.args)) + obj.add('expected_arg_types', t.array_node_type(node.expected_arg_types)) + obj.add('concrete_types', t.array_node_type(node.concrete_types)) + obj.add('or_block', t.or_expr(node.or_block)) + obj.add('concrete_list_pos', t.position(node.concrete_list_pos)) + obj.add('from_embed_type', t.type_node(node.from_embed_type)) + obj.add('comments', t.array_node_comment(node.comments)) + obj.add('pos', t.position(node.pos)) + obj.add('name_pos', t.position(node.name_pos)) + return obj +} + +fn (t Tree) call_arg(node ast.CallArg) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('CallArg')) + obj.add('typ', t.type_node(node.typ)) + obj.add('is_mut', t.bool_node(node.is_mut)) + obj.add('share', t.enum_node(node.share)) + obj.add('expr', t.expr(node.expr)) + obj.add('is_tmp_autofree', t.bool_node(node.is_tmp_autofree)) + obj.add('pos', t.position(node.pos)) + obj.add('comments', t.array_node_comment(node.comments)) + return obj +} + +fn (t Tree) or_expr(node ast.OrExpr) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('OrExpr')) + obj.add('stmts', t.array_node_stmt(node.stmts)) + obj.add('kind', t.enum_node(node.kind)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) struct_init(node ast.StructInit) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('StructInit')) + obj.add('typ', t.type_node(node.typ)) + obj.add('is_short', t.bool_node(node.is_short)) + obj.add('unresolved', t.bool_node(node.unresolved)) + obj.add('has_update_expr', t.bool_node(node.has_update_expr)) + obj.add('update_expr', t.expr(node.update_expr)) + obj.add('update_expr_type', t.type_node(node.update_expr_type)) + obj.add('pos', t.position(node.pos)) + obj.add('name_pos', t.position(node.name_pos)) + obj.add('update_expr_comments', t.array_node_comment(node.update_expr_comments)) + obj.add('fields', t.array_node_struct_init_field(node.fields)) + obj.add('embeds', t.array_node_struct_init_embed(node.embeds)) + obj.add('pre_comments', t.array_node_comment(node.pre_comments)) + return obj +} + +fn (t Tree) struct_init_field(node ast.StructInitField) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('StructInitField')) + obj.add('name', t.string_node(node.name)) + obj.add('expr', t.expr(node.expr)) + obj.add('typ', t.type_node(node.typ)) + obj.add('expected_type', t.type_node(node.expected_type)) + obj.add('parent_type', t.type_node(node.parent_type)) + obj.add('comments', t.array_node_comment(node.comments)) + obj.add('next_comments', t.array_node_comment(node.next_comments)) + obj.add('pos', t.position(node.pos)) + obj.add('name_pos', t.position(node.name_pos)) + return obj +} + +fn (t Tree) struct_init_embed(node ast.StructInitEmbed) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('StructInitEmbed')) + obj.add('name', t.string_node(node.name)) + obj.add('expr', t.expr(node.expr)) + obj.add('typ', t.type_node(node.typ)) + obj.add('expected_type', t.type_node(node.expected_type)) + obj.add('comments', t.array_node_comment(node.comments)) + obj.add('next_comments', t.array_node_comment(node.next_comments)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) array_init(node ast.ArrayInit) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('ArrayInit')) + obj.add('typ', t.type_node(node.typ)) + obj.add('elem_type', t.type_node(node.elem_type)) + obj.add('exprs', t.array_node_expr(node.exprs)) + obj.add('ecmnts', t.two_dimension_comment(node.ecmnts)) + obj.add('pre_cmnts', t.array_node_comment(node.pre_cmnts)) + obj.add('elem_type_pos', t.position(node.elem_type_pos)) + obj.add('is_fixed', t.bool_node(node.is_fixed)) + obj.add('has_val', t.bool_node(node.has_val)) + obj.add('mod', t.string_node(node.mod)) + obj.add('len_expr', t.expr(node.len_expr)) + obj.add('cap_expr', t.expr(node.cap_expr)) + obj.add('default_expr', t.expr(node.default_expr)) + obj.add('has_len', t.bool_node(node.has_len)) + obj.add('has_cap', t.bool_node(node.has_cap)) + obj.add('has_default', t.bool_node(node.has_default)) + obj.add('expr_types', t.array_node_type(node.expr_types)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) map_init(node ast.MapInit) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('MapInit')) + obj.add('typ', t.type_node(node.typ)) + obj.add('key_type', t.type_node(node.key_type)) + obj.add('value_type', t.type_node(node.value_type)) + obj.add('keys', t.array_node_expr(node.keys)) + obj.add('vals', t.array_node_expr(node.vals)) + obj.add('comments', t.two_dimension_comment(node.comments)) + obj.add('pre_cmnts', t.array_node_comment(node.pre_cmnts)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) none_expr(node ast.None) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('None')) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) par_expr(node ast.ParExpr) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('ParExpr')) + obj.add('expr', t.expr(node.expr)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) if_guard_expr(node ast.IfGuardExpr) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('IfGuardExpr')) + obj.add('var_name', t.string_node(node.var_name)) + obj.add('expr', t.expr(node.expr)) + obj.add('expr_type', t.type_node(node.expr_type)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) match_expr(node ast.MatchExpr) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('MatchExpr')) + obj.add('tok_kind', t.token_node(node.tok_kind)) + obj.add('cond', t.expr(node.cond)) + obj.add('cond_type', t.type_node(node.cond_type)) + obj.add('return_type', t.type_node(node.return_type)) + obj.add('expected_type', t.type_node(node.expected_type)) + obj.add('is_sum_type', t.bool_node(node.is_sum_type)) + obj.add('is_expr', t.bool_node(node.is_expr)) + obj.add('pos', t.position(node.pos)) + obj.add('branches', t.array_node_match_branch(node.branches)) + obj.add('comments', t.array_node_comment(node.comments)) + return obj +} + +fn (t Tree) match_branch(node ast.MatchBranch) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('MatchBranch')) + obj.add('exprs', t.array_node_expr(node.exprs)) + obj.add('ecmnts', t.two_dimension_comment(node.ecmnts)) + obj.add('stmts', t.array_node_stmt(node.stmts)) + obj.add('is_else', t.bool_node(node.is_else)) + obj.add('pos', t.position(node.pos)) + obj.add('post_comments', t.array_node_comment(node.post_comments)) + obj.add('scope', t.number_node(int(node.scope))) + return obj +} + +fn (t Tree) concat_expr(node ast.ConcatExpr) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('ConcatExpr')) + obj.add('vals', t.array_node_expr(node.vals)) + obj.add('return_type', t.type_node(node.return_type)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) type_of(node ast.TypeOf) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('TypeOf')) + obj.add('expr', t.expr(node.expr)) + obj.add('expr_type', t.type_node(node.expr_type)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) likely(node ast.Likely) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('Likely')) + obj.add('expr', t.expr(node.expr)) + obj.add('is_likely', t.bool_node(node.is_likely)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) sql_expr(node ast.SqlExpr) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('SqlExpr')) + obj.add('type', t.type_node(node.typ)) + obj.add('is_count', t.bool_node(node.is_count)) + obj.add('db_expr', t.expr(node.db_expr)) + obj.add('table_expr', t.type_expr(node.table_expr)) + obj.add('has_where', t.bool_node(node.has_where)) + obj.add('where_expr', t.expr(node.where_expr)) + obj.add('has_order', t.bool_node(node.has_order)) + obj.add('order_expr', t.expr(node.order_expr)) + obj.add('has_desc', t.bool_node(node.has_desc)) + obj.add('is_array', t.bool_node(node.is_array)) + obj.add('pos', t.position(node.pos)) + obj.add('has_limit', t.bool_node(node.has_limit)) + obj.add('limit_expr', t.expr(node.limit_expr)) + obj.add('has_offset', t.bool_node(node.has_offset)) + obj.add('offset_expr', t.expr(node.offset_expr)) + obj.add('fields', t.array_node_struct_field(node.fields)) + sub_struct_map := new_object() + for key, val in node.sub_structs { + sub_struct_map.add(key.str(), t.sql_expr(val)) + } + obj.add('sub_structs', sub_struct_map) + return obj +} + +fn (t Tree) sql_stmt(node ast.SqlStmt) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('SqlStmt')) + obj.add('db_expr', t.expr(node.db_expr)) + obj.add('pos', t.position(node.pos)) + obj.add('lines', t.array_node_sql_stmt_line(node.lines)) + return obj +} + +fn (t Tree) sql_stmt_line(node ast.SqlStmtLine) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('SqlStmtLine')) + obj.add('kind', t.enum_node(node.kind)) + obj.add('table_expr', t.type_expr(node.table_expr)) + obj.add('object_var_name', t.string_node(node.object_var_name)) + obj.add('where_expr', t.expr(node.where_expr)) + obj.add('fields', t.array_node_struct_field(node.fields)) + obj.add('updated_columns', t.array_node_string(node.updated_columns)) + obj.add('update_exprs', t.array_node_expr(node.update_exprs)) + obj.add('pos', t.position(node.pos)) + + sub_struct_map := new_object() + for key, val in node.sub_structs { + sub_struct_map.add(key.str(), t.sql_stmt_line(val)) + } + obj.add('sub_structs', sub_struct_map) + return obj +} + +fn (t Tree) lock_expr(expr ast.LockExpr) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('LockExpr')) + obj.add('is_expr', t.bool_node(expr.is_expr)) + obj.add('typ', t.type_node(expr.typ)) + obj.add('pos', t.position(expr.pos)) + obj.add('stmts', t.array_node_stmt(expr.stmts)) + obj.add('lockeds', t.array_node_expr(expr.lockeds)) + obj.add('r_lock', t.array_node_bool(expr.is_rlock)) + return obj +} + +fn (t Tree) unsafe_expr(expr ast.UnsafeExpr) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('UnsafeExpr')) + obj.add('expr', t.expr(expr.expr)) + obj.add('pos', t.position(expr.pos)) + return obj +} + +fn (t Tree) chan_init(expr ast.ChanInit) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('ChanInit')) + obj.add('has_cap', t.bool_node(expr.has_cap)) + obj.add('cap_expr', t.expr(expr.cap_expr)) + obj.add('typ', t.type_node(expr.typ)) + obj.add('elem_type', t.type_node(expr.elem_type)) + obj.add('pos', t.position(expr.pos)) + return obj +} + +fn (t Tree) select_expr(expr ast.SelectExpr) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('SelectExpr')) + obj.add('branches', t.array_node_select_branch(expr.branches)) + obj.add('is_expr', t.bool_node(expr.is_expr)) + obj.add('has_exception', t.bool_node(expr.has_exception)) + obj.add('expected_type', t.type_node(expr.expected_type)) + obj.add('pos', t.position(expr.pos)) + return obj +} + +fn (t Tree) select_branch(expr ast.SelectBranch) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('SelectBranch')) + obj.add('stmt', t.stmt(expr.stmt)) + obj.add('stmts', t.array_node_stmt(expr.stmts)) + obj.add('pos', t.position(expr.pos)) + obj.add('comment', t.comment(expr.comment)) + obj.add('is_else', t.bool_node(expr.is_else)) + obj.add('is_timeout', t.bool_node(expr.is_timeout)) + obj.add('post_comments', t.array_node_comment(expr.post_comments)) + return obj +} + +fn (t Tree) array_decompose(expr ast.ArrayDecompose) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('ArrayDecompose')) + obj.add('expr', t.expr(expr.expr)) + obj.add('expr_type', t.type_node(expr.expr_type)) + obj.add('arg_type', t.type_node(expr.arg_type)) + obj.add('pos', t.position(expr.pos)) + return obj +} + +fn (t Tree) go_expr(expr ast.GoExpr) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('GoExpr')) + obj.add('call_expr', t.call_expr(expr.call_expr)) + obj.add('is_expr', t.bool_node(expr.is_expr)) + obj.add('pos', t.position(expr.pos)) + return obj +} + +fn (t Tree) offset_of(expr ast.OffsetOf) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('OffsetOf')) + obj.add('struct_type', t.type_node(expr.struct_type)) + obj.add('field', t.string_node('field')) + obj.add('pos', t.position(expr.pos)) + return obj +} + +fn (t Tree) dump_expr(expr ast.DumpExpr) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('DumpExpr')) + obj.add('expr', t.expr(expr.expr)) + obj.add('expr_type', t.type_node(expr.expr_type)) + obj.add('pos', t.position(expr.pos)) + return obj +} + +fn (t Tree) node_error(expr ast.NodeError) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('NodeError')) + obj.add('idx', t.number_node(expr.idx)) + obj.add('pos', t.position(expr.pos)) + return obj +} + +fn (t Tree) empty_expr(expr ast.EmptyExpr) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('EmptyExpr')) + // obj.add('x', t.number_node(expr.x)) + return obj +} + +fn (t Tree) empty_stmt(node ast.EmptyStmt) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('EmptyStmt')) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) asm_stmt(node ast.AsmStmt) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('AsmStmt')) + obj.add('arch', t.enum_node(node.arch)) + obj.add('is_basic', t.bool_node(node.is_basic)) + obj.add('is_volatile', t.bool_node(node.is_volatile)) + obj.add('is_goto', t.bool_node(node.is_goto)) + obj.add('scope', t.scope(node.scope)) + // obj.add('scope', t.number_node(int(node.scope))) + obj.add('pos', t.position(node.pos)) + obj.add('clobbered', t.array_node_asm_clobbered(node.clobbered)) + obj.add('templates', t.array_node_asm_template(node.templates)) + obj.add('output', t.array_node_asm_io(node.output)) + obj.add('input', t.array_node_asm_io(node.input)) + obj.add('global_labels', t.array_node_string(node.global_labels)) + obj.add('local_labels', t.array_node_string(node.local_labels)) + return obj +} + +fn (t Tree) asm_register(node ast.AsmRegister) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('AsmRegister')) + obj.add('name', t.string_node(node.name)) + obj.add('typ', t.type_node(node.typ)) + obj.add('size', t.number_node(node.size)) + return obj +} + +fn (t Tree) asm_template(node ast.AsmTemplate) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('AsmTemplate')) + obj.add('name', t.string_node(node.name)) + obj.add('is_label', t.bool_node(node.is_label)) + obj.add('is_directive', t.bool_node(node.is_directive)) + obj.add('args', t.array_node_asm_arg(node.args)) + obj.add('comments', t.array_node_comment(node.comments)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) asm_addressing(node ast.AsmAddressing) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('AsmAddressing')) + obj.add('scale', t.number_node(node.scale)) + obj.add('mode', t.enum_node(node.mode)) + obj.add('displacement', t.asm_arg(node.displacement)) + obj.add('base', t.asm_arg(node.base)) + obj.add('index', t.asm_arg(node.index)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) asm_arg(node ast.AsmArg) &Node { + match node { + ast.AsmAddressing { + return t.asm_addressing(node) + } + ast.AsmAlias { + return t.asm_alias(node) + } + ast.AsmDisp { + return t.asm_disp(node) + } + ast.AsmRegister { + return t.asm_register(node) + } + ast.BoolLiteral { + return t.bool_literal(node) + } + ast.CharLiteral { + return t.char_literal(node) + } + ast.FloatLiteral { + return t.float_literal(node) + } + ast.IntegerLiteral { + return t.integer_literal(node) + } + string { + return t.string_node(node) + } + } +} + +fn (t Tree) asm_alias(node ast.AsmAlias) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('AsmAlias')) + obj.add('name', t.string_node(node.name)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) asm_disp(node ast.AsmDisp) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('AsmDisp')) + obj.add('val', t.string_node(node.val)) + obj.add('pos', t.position(node.pos)) + return obj +} + +fn (t Tree) asm_clobbered(node ast.AsmClobbered) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('AsmClobbered')) + obj.add('reg', t.asm_register(node.reg)) + obj.add('comments', t.array_node_comment(node.comments)) + return obj +} + +fn (t Tree) asm_io(node ast.AsmIO) &Node { + mut obj := new_object() + obj.add('ast_type', t.string_node('AsmIO')) + obj.add('alias', t.string_node(node.alias)) + obj.add('constraint', t.string_node(node.constraint)) + obj.add('expr', t.expr(node.expr)) + obj.add('typ', t.type_node(node.typ)) + obj.add('comments', t.array_node_comment(node.comments)) + obj.add('pos', t.position(node.pos)) + return obj +} + +// do not support yet by vlang +// fn (t Tree) array_node1(nodes []T, method_name string) &Node { +// mut arr := new_array() + +// // call method dynamically, V do not support yet +// // error: todo: not a string literal + +// // for node in nodes { +// // arr.add_item(t.$method_name(node)) +// // } + +// // temp +// $for method in Tree.methods { +// if method.name == method_name { +// for node in nodes { +// res := t.$method(node) +// arr.add_item(res) // TODO,waiting for bug fixed +// } +// } +// } +// return arr +// } + +// do not support yet by vlang +// fn (t Tree) array_node2(nodes []T) &Node { +// mut arr := new_array() + +// for node in nodes { +// match node { +// string { +// arr.add_item(t.string_node(node)) +// } +// ast.Comment { +// arr.add_item(t.comment(node)) +// } +// ast.ConstField { +// arr.add_item(t.const_field(node)) +// } +// else { +// panic('unknown array type') +// } +// } +// } + +// return arr +// } + +// list all the different type of array node,temporarily +fn (t Tree) array_node_string(nodes []string) &Node { + mut arr := new_array() + for node in nodes { + arr.add_item(t.string_node(node)) + } + return arr +} + +fn (t Tree) array_node_position(nodes []token.Position) &Node { + mut arr := new_array() + for node in nodes { + arr.add_item(t.position(node)) + } + return arr +} + +fn (t Tree) array_node_if_branch(nodes []ast.IfBranch) &Node { + mut arr := new_array() + for node in nodes { + arr.add_item(t.if_branch(node)) + } + return arr +} + +fn (t Tree) array_node_fn_decl(nodes []ast.FnDecl) &Node { + mut arr := new_array() + for node in nodes { + arr.add_item(t.fn_decl(node)) + } + return arr +} + +fn (t Tree) array_node_generic_fns(nodes []&ast.FnDecl) &Node { + mut arr := new_array() + for node in nodes { + arr.add_item(t.fn_decl(node)) + } + return arr +} + +fn (t Tree) array_node_embed_file(nodes []ast.EmbeddedFile) &Node { + mut arr := new_array() + for node in nodes { + arr.add_item(t.embed_file(node)) + } + return arr +} + +fn (t Tree) array_node_attr(nodes []ast.Attr) &Node { + mut arr := new_array() + for node in nodes { + arr.add_item(t.attr(node)) + } + return arr +} + +fn (t Tree) array_node_scope_struct_field(nodes map[string]ast.ScopeStructField) &Node { + mut arr := new_array() + for _, node in nodes { + arr.add_item(t.scope_struct_field(node)) + } + return arr +} + +fn (t Tree) array_node_type(nodes []ast.Type) &Node { + mut arr := new_array() + for node in nodes { + arr.add_item(t.type_node(node)) + } + return arr +} + +fn (t Tree) array_node_import_symbol(nodes []ast.ImportSymbol) &Node { + mut arr := new_array() + for node in nodes { + arr.add_item(t.import_symbol(node)) + } + return arr +} + +fn (t Tree) array_node_comment(nodes []ast.Comment) &Node { + mut arr := new_array() + for node in nodes { + arr.add_item(t.comment(node)) + } + return arr +} + +fn (t Tree) array_node_const_field(nodes []ast.ConstField) &Node { + mut arr := new_array() + for node in nodes { + arr.add_item(t.const_field(node)) + } + return arr +} + +fn (t Tree) array_node_arg(nodes []ast.Param) &Node { + mut arr := new_array() + for node in nodes { + arr.add_item(t.arg(node)) + } + return arr +} + +fn (t Tree) array_node_stmt(nodes []ast.Stmt) &Node { + mut arr := new_array() + for node in nodes { + arr.add_item(t.stmt(node)) + } + return arr +} + +fn (t Tree) array_node_defer_stmt(nodes []ast.DeferStmt) &Node { + mut arr := new_array() + for node in nodes { + arr.add_item(t.defer_stmt(node)) + } + return arr +} + +fn (t Tree) array_node_struct_field(nodes []ast.StructField) &Node { + mut arr := new_array() + for node in nodes { + arr.add_item(t.struct_field(node)) + } + return arr +} + +fn (t Tree) array_node_embed(nodes []ast.Embed) &Node { + mut arr := new_array() + for node in nodes { + arr.add_item(t.embed(node)) + } + return arr +} + +fn (t Tree) array_node_enum_field(nodes []ast.EnumField) &Node { + mut arr := new_array() + for node in nodes { + arr.add_item(t.enum_field(node)) + } + return arr +} + +fn (t Tree) array_node_global_field(nodes []ast.GlobalField) &Node { + mut arr := new_array() + for node in nodes { + arr.add_item(t.global_field(node)) + } + return arr +} + +fn (t Tree) array_node_expr(nodes []ast.Expr) &Node { + mut arr := new_array() + for node in nodes { + arr.add_item(t.expr(node)) + } + return arr +} + +fn (t Tree) array_node_call_arg(nodes []ast.CallArg) &Node { + mut arr := new_array() + for node in nodes { + arr.add_item(t.call_arg(node)) + } + return arr +} + +fn (t Tree) array_node_int(nodes []int) &Node { + mut arr := new_array() + for node in nodes { + arr.add_item(t.number_node(node)) + } + return arr +} + +fn (t Tree) array_node_byte(nodes []byte) &Node { + mut arr := new_array() + for node in nodes { + arr.add_item(t.number_node(node)) + } + return arr +} + +fn (t Tree) array_node_bool(nodes []bool) &Node { + mut arr := new_array() + for node in nodes { + arr.add_item(t.bool_node(node)) + } + return arr +} + +fn (t Tree) array_node_struct_init_field(nodes []ast.StructInitField) &Node { + mut arr := new_array() + for node in nodes { + arr.add_item(t.struct_init_field(node)) + } + return arr +} + +fn (t Tree) array_node_struct_init_embed(nodes []ast.StructInitEmbed) &Node { + mut arr := new_array() + for node in nodes { + arr.add_item(t.struct_init_embed(node)) + } + return arr +} + +fn (t Tree) array_node_match_branch(nodes []ast.MatchBranch) &Node { + mut arr := new_array() + for node in nodes { + arr.add_item(t.match_branch(node)) + } + return arr +} + +fn (t Tree) array_node_ident(nodes []ast.Ident) &Node { + mut arr := new_array() + for node in nodes { + arr.add_item(t.ident(node)) + } + return arr +} + +fn (t Tree) array_node_select_branch(nodes []ast.SelectBranch) &Node { + mut arr := new_array() + for node in nodes { + arr.add_item(t.select_branch(node)) + } + return arr +} + +fn (t Tree) array_node_asm_clobbered(nodes []ast.AsmClobbered) &Node { + mut arr := new_array() + for node in nodes { + arr.add_item(t.asm_clobbered(node)) + } + return arr +} + +fn (t Tree) array_node_asm_template(nodes []ast.AsmTemplate) &Node { + mut arr := new_array() + for node in nodes { + arr.add_item(t.asm_template(node)) + } + return arr +} + +fn (t Tree) array_node_asm_io(nodes []ast.AsmIO) &Node { + mut arr := new_array() + for node in nodes { + arr.add_item(t.asm_io(node)) + } + return arr +} + +fn (t Tree) array_node_asm_arg(nodes []ast.AsmArg) &Node { + mut arr := new_array() + for node in nodes { + arr.add_item(t.asm_arg(node)) + } + return arr +} + +fn (t Tree) array_node_sql_stmt_line(nodes []ast.SqlStmtLine) &Node { + mut arr := new_array() + for node in nodes { + arr.add_item(t.sql_stmt_line(node)) + } + return arr +} + +fn (t Tree) array_node_interface_embedding(nodes []ast.InterfaceEmbedding) &Node { + mut arr := new_array() + for node in nodes { + arr.add_item(t.interface_embedding(node)) + } + return arr +} diff --git a/v_windows/v/old/cmd/tools/vbin2v.v b/v_windows/v/old/cmd/tools/vbin2v.v new file mode 100644 index 0000000..c1ab6e0 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vbin2v.v @@ -0,0 +1,146 @@ +module main + +import os +import flag +import strings + +const ( + tool_version = '0.0.4' + tool_description = 'Converts a list of arbitrary files into a single v module file.' +) + +struct Context { +mut: + files []string + prefix string + show_help bool + module_name string + write_file string +} + +fn (context Context) header() string { + mut header_s := '' + header_s += 'module $context.module_name\n' + header_s += '\n' + allfiles := context.files.join(' ') + mut options := []string{} + if context.prefix.len > 0 { + options << '-p $context.prefix' + } + if context.module_name.len > 0 { + options << '-m $context.module_name' + } + if context.write_file.len > 0 { + options << '-w $context.write_file' + } + soptions := options.join(' ') + header_s += '// File generated by:\n' + header_s += '// v bin2v $allfiles $soptions\n' + header_s += '// Please, do not edit this file.\n' + header_s += '// Your changes may be overwritten.\n' + header_s += 'const (\n' + return header_s +} + +fn (context Context) footer() string { + return ')\n' +} + +fn (context Context) file2v(bname string, fbytes []byte, bn_max int) string { + mut sb := strings.new_builder(1000) + bn_diff_len := bn_max - bname.len + sb.write_string('\t${bname}_len' + ' '.repeat(bn_diff_len - 4) + ' = $fbytes.len\n') + fbyte := fbytes[0] + bnmae_line := '\t$bname' + ' '.repeat(bn_diff_len) + ' = [byte($fbyte), ' + sb.write_string(bnmae_line) + mut line_len := bnmae_line.len + 3 + for i := 1; i < fbytes.len; i++ { + b := int(fbytes[i]).str() + if line_len > 94 { + sb.go_back(1) + sb.write_string('\n\t\t') + line_len = 8 + } + if i == fbytes.len - 1 { + sb.write_string(b) + line_len += b.len + } else { + sb.write_string('$b, ') + line_len += b.len + 2 + } + } + sb.write_string(']!\n') + return sb.str() +} + +fn (context Context) bname_and_bytes(file string) ?(string, []byte) { + fname := os.file_name(file) + fname_escaped := fname.replace_each(['.', '_', '-', '_']) + byte_name := '$context.prefix$fname_escaped'.to_lower() + fbytes := os.read_bytes(file) or { return error('Error: $err.msg') } + return byte_name, fbytes +} + +fn (context Context) max_bname_len(bnames []string) int { + mut max := 0 + for n in bnames { + if n.len > max { + max = n.len + } + } + // Add 4 to max due to "_len" suffix + return max + 4 +} + +fn main() { + mut context := Context{} + mut fp := flag.new_flag_parser(os.args[1..]) + fp.application('v bin2v') + fp.version(tool_version) + fp.description(tool_description) + fp.arguments_description('FILE [FILE]...') + context.show_help = fp.bool('help', `h`, false, 'Show this help screen.') + context.module_name = fp.string('module', `m`, 'binary', 'Name of the generated module.') + context.prefix = fp.string('prefix', `p`, '', 'A prefix put before each resource name.') + context.write_file = fp.string('write', `w`, '', 'Write directly to a file with the given name.') + if context.show_help { + println(fp.usage()) + exit(0) + } + files := fp.finalize() or { + eprintln('Error: $err.msg') + exit(1) + } + real_files := files.filter(it != 'bin2v') + if real_files.len == 0 { + println(fp.usage()) + exit(0) + } + context.files = real_files + if context.write_file != '' && os.file_ext(context.write_file) !in ['.vv', '.v'] { + context.write_file += '.v' + } + mut file_byte_map := map[string][]byte{} + for file in real_files { + bname, fbytes := context.bname_and_bytes(file) or { + eprintln(err.msg) + exit(1) + } + file_byte_map[bname] = fbytes + } + max_bname := context.max_bname_len(file_byte_map.keys()) + if context.write_file.len > 0 { + mut out_file := os.create(context.write_file) ? + out_file.write_string(context.header()) ? + for bname, fbytes in file_byte_map { + out_file.write_string(context.file2v(bname, fbytes, max_bname)) ? + } + out_file.write_string(context.footer()) ? + } else { + print(context.header()) + for bname, fbytes in file_byte_map { + print(context.file2v(bname, fbytes, max_bname)) + } + print(context.footer()) + } +} diff --git a/v_windows/v/old/cmd/tools/vbug.v b/v_windows/v/old/cmd/tools/vbug.v new file mode 100644 index 0000000..6baf890 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vbug.v @@ -0,0 +1,208 @@ +import dl +import net.urllib +import os +import readline + +const vroot = @VMODROOT + +// get output from `v doctor` +fn get_vdoctor_output(is_verbose bool) string { + vexe := os.getenv('VEXE') + verbose_flag := if is_verbose { '-v' } else { '' } + result := os.execute('$vexe $verbose_flag doctor') + if result.exit_code != 0 { + eprintln('unable to get `v doctor` output: $result.output') + return '' + } + return result.output +} + +// get ouput from `v -g -o vdbg cmd/v && vdbg file.v` +fn get_v_build_output(is_verbose bool, is_yes bool, file_path string) string { + mut vexe := os.getenv('VEXE') + // prepare a V compiler with -g to have better backtraces if possible + wd := os.getwd() + os.chdir(vroot) + verbose_flag := if is_verbose { '-v' } else { '' } + vdbg_path := $if windows { '$vroot/vdbg.exe' } $else { '$vroot/vdbg' } + vdbg_compilation_cmd := '"$vexe" $verbose_flag -g -o "$vdbg_path" cmd/v' + vdbg_result := os.execute(vdbg_compilation_cmd) + os.chdir(wd) + if vdbg_result.exit_code == 0 { + vexe = vdbg_path + } else { + eprintln('unable to compile V in debug mode: $vdbg_result.output\ncommand: $vdbg_compilation_cmd\n') + } + // + mut result := os.execute('"$vexe" $verbose_flag "$file_path"') + defer { + os.rm(vdbg_path) or { + if is_verbose { + eprintln('unable to delete `vdbg`: $err') + } + } + } + if result.exit_code == 0 { + defer { + mut generated_file := file_path.all_before_last('.') + $if windows { + generated_file += '.exe' + } + os.rm(generated_file) or { + if is_verbose { + eprintln('unable to delete generated file: $err') + } + } + } + run := is_yes + || ask('It looks like the compilation went well, do you want to run the file?') + if run { + result = os.execute('"$vexe" $verbose_flag run "$file_path"') + if result.exit_code == 0 && !is_yes { + confirm_or_exit('It looks like the file ran correctly as well, are you sure you want to continue?') + } + } + } + return result.output +} + +type ShellExecuteWin = fn (voidptr, &u16, &u16, &u16, &u16, int) + +// open a uri using the default associated application +fn open_browser(uri string) ? { + $if macos { + result := os.execute('open "$uri"') + if result.exit_code != 0 { + return error('unable to open url: $result.output') + } + } $else $if freebsd || openbsd { + result := os.execute('xdg-open "$uri"') + if result.exit_code != 0 { + return error('unable to open url: $result.output') + } + } $else $if linux { + providers := ['xdg-open', 'x-www-browser', 'www-browser', 'wslview'] + + // There are multiple possible providers to open a browser on linux + // One of them is xdg-open, another is x-www-browser, then there's www-browser, etc. + // Look for one that exists and run it + for provider in providers { + if os.exists_in_system_path(provider) { + result := os.execute('$provider "$uri"') + if result.exit_code != 0 { + return error('unable to open url: $result.output') + } + break + } + } + } $else $if windows { + handle := dl.open_opt('shell32', dl.rtld_now) ? + // https://docs.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shellexecutew + func := ShellExecuteWin(dl.sym_opt(handle, 'ShellExecuteW') ?) + func(C.NULL, 'open'.to_wide(), uri.to_wide(), C.NULL, C.NULL, C.SW_SHOWNORMAL) + dl.close(handle) + } $else { + return error('unsupported platform') + } +} + +fn ask(msg string) bool { + prompt := os.input_opt('$msg [Y/n] ') or { 'y' } + return prompt == '' || prompt[0].ascii_str().to_lower() != 'n' +} + +fn confirm_or_exit(msg string) { + if !ask(msg) { + exit(1) + } +} + +fn main() { + mut file_path := '' + mut is_verbose := false + mut is_yes := false + for arg in os.args[2..] { + match arg { + '-v' { + is_verbose = true + } + '-y' { + is_yes = true + } + else { + if !arg.ends_with('.v') && !arg.ends_with('.vsh') && !arg.ends_with('.vv') { + eprintln('unknown argument: `$arg`') + exit(1) + } + if file_path != '' { + eprintln('only one V file can be submitted') + exit(1) + } + file_path = arg + } + } + } + if file_path == '' { + eprintln('v bug: no v file listed to report') + exit(1) + } + // collect error information + // output from `v doctor` + vdoctor_output := get_vdoctor_output(is_verbose) + // file content + file_content := os.read_file(file_path) or { + eprintln('unable to get file "$file_path" content: $err') + '' + } + // output from `v -g -o vdbg cmd/v && vdbg file.v` + build_output := get_v_build_output(is_verbose, is_yes, file_path) + // ask the user if he wants to submit even after an error + if !is_yes && (vdoctor_output == '' || file_content == '' || build_output == '') { + confirm_or_exit('An error occured retrieving the information, do you want to continue?') + } + + expected_result := readline.read_line('What did you expect to see? ') or { + // Ctrl-C was pressed + eprintln('\nCanceled') + exit(1) + } + // open prefilled issue creation page, or print link as a fallback + + if !is_yes && vdoctor_output.contains('behind V master') { + confirm_or_exit('It looks like your installation of V is outdated, we advise you to run `v up` before submitting an issue. Are you sure you want to continue?') + } + + // When updating this template, make sure to update `.github/ISSUE_TEMPLATE/bug_report.md` too + raw_body := ' +**V doctor:** +``` +$vdoctor_output``` + +**What did you do?** +`v -g -o vdbg cmd/v && vdbg $file_path` +{file_content} + +**What did you expect to see?** + +$expected_result + +**What did you see instead?** +``` +$build_output```' + mut encoded_body := urllib.query_escape(raw_body.replace_once('{file_content}', '```v\n$file_content\n```')) + mut generated_uri := 'https://github.com/vlang/v/issues/new?labels=Bug&body=$encoded_body' + if generated_uri.len > 8192 { + // GitHub doesn't support URLs longer than 8192 characters + encoded_body = urllib.query_escape(raw_body.replace_once('{file_content}', 'See attached file `$file_path`')) + generated_uri = 'https://github.com/vlang/v/issues/new?labels=Bug&body=$encoded_body' + println('Your file is too big to be submitted. Head over to the following URL and attach your file.') + println(generated_uri) + } else { + open_browser(generated_uri) or { + if is_verbose { + eprintln(err) + } + println(generated_uri) + } + } +} diff --git a/v_windows/v/old/cmd/tools/vbuild-examples.v b/v_windows/v/old/cmd/tools/vbuild-examples.v new file mode 100644 index 0000000..82ddc1e --- /dev/null +++ b/v_windows/v/old/cmd/tools/vbuild-examples.v @@ -0,0 +1,15 @@ +module main + +import os +import testing + +fn main() { + args_string := os.args[1..].join(' ') + params := args_string.all_before('build-examples') + if testing.v_build_failing(params, 'examples') { + exit(1) + } + if testing.v_build_failing(params + '-live', os.join_path('examples', 'hot_reload')) { + exit(1) + } +} diff --git a/v_windows/v/old/cmd/tools/vbuild-tools.v b/v_windows/v/old/cmd/tools/vbuild-tools.v new file mode 100644 index 0000000..19905ab --- /dev/null +++ b/v_windows/v/old/cmd/tools/vbuild-tools.v @@ -0,0 +1,71 @@ +module main + +import os +import testing +import v.util + +// NB: tools like vdoc are compiled in their own subfolder +// => cmd/tools/vdoc/vdoc.exe +// Usually, they have several top level .v files in the subfolder, +// that cannot be compiled separately, but instead, the whole folder, +// should be compiled (v folder). +// To implement that, these folders are initially skipped, then added +// as a whole *after the testing.prepare_test_session call*. +const tools_in_subfolders = ['vdoc', 'vvet', 'vast'] + +// non_packaged_tools are tools that should not be packaged with +// prebuild versions of V, to keep the size smaller. +// They are mainly usefull for the V project itself, not to end users. +const non_packaged_tools = ['gen1m', 'gen_vc', 'fast', 'wyhash'] + +fn main() { + util.ensure_modules_for_all_tools_are_installed('-v' in os.args) + args_string := os.args[1..].join(' ') + vexe := os.getenv('VEXE') + vroot := os.dir(vexe) + os.chdir(vroot) + folder := os.join_path('cmd', 'tools') + tfolder := os.join_path(vroot, 'cmd', 'tools') + main_label := 'Building $folder ...' + finish_label := 'building $folder' + // + mut skips := []string{} + for stool in tools_in_subfolders { + skips << os.join_path(tfolder, stool) + } + buildopts := args_string.all_before('build-tools') + mut session := testing.prepare_test_session(buildopts, folder, skips, main_label) + session.rm_binaries = false + for stool in tools_in_subfolders { + session.add(os.join_path(tfolder, stool)) + } + // eprintln('> session.files: $session.files') + // eprintln('> session.skip_files: $session.skip_files') + session.test() + eprintln(session.benchmark.total_message(finish_label)) + if session.failed { + exit(1) + } + // + mut executables := os.ls(session.vtmp_dir) ? + executables.sort() + for texe in executables { + tname := texe.replace(os.file_ext(texe), '') + if tname in non_packaged_tools { + continue + } + // + tpath := os.join_path(session.vtmp_dir, texe) + if tname in tools_in_subfolders { + os.mv_by_cp(tpath, os.join_path(tfolder, tname, texe)) or { panic(err) } + continue + } + target_path := os.join_path(tfolder, texe) + os.mv_by_cp(tpath, target_path) or { + if !err.msg.contains('vbuild-tools') && !err.msg.contains('vtest-all') { + eprintln('error while moving $tpath to $target_path: $err.msg') + } + continue + } + } +} diff --git a/v_windows/v/old/cmd/tools/vbuild-vbinaries.v b/v_windows/v/old/cmd/tools/vbuild-vbinaries.v new file mode 100644 index 0000000..49e297e --- /dev/null +++ b/v_windows/v/old/cmd/tools/vbuild-vbinaries.v @@ -0,0 +1,9 @@ +module main + +import testing + +fn main() { + if testing.building_any_v_binaries_failed() { + exit(1) + } +} diff --git a/v_windows/v/old/cmd/tools/vcheck-md.v b/v_windows/v/old/cmd/tools/vcheck-md.v new file mode 100644 index 0000000..f5cd5ea --- /dev/null +++ b/v_windows/v/old/cmd/tools/vcheck-md.v @@ -0,0 +1,540 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module main + +import os +import os.cmdline +import rand +import term +import vhelp +import v.pref +import regex + +const ( + too_long_line_length = 100 + term_colors = term.can_show_color_on_stderr() + hide_warnings = '-hide-warnings' in os.args || '-w' in os.args + show_progress = os.getenv('GITHUB_JOB') == '' && '-silent' !in os.args + non_option_args = cmdline.only_non_options(os.args[2..]) +) + +struct CheckResult { +pub mut: + warnings int + errors int + oks int +} + +fn (v1 CheckResult) + (v2 CheckResult) CheckResult { + return CheckResult{ + warnings: v1.warnings + v2.warnings + errors: v1.errors + v2.errors + oks: v1.oks + v2.oks + } +} + +fn main() { + if non_option_args.len == 0 || '-help' in os.args { + vhelp.show_topic('check-md') + exit(0) + } + if '-all' in os.args { + println('´-all´ flag is deprecated. Please use ´v check-md .´ instead.') + exit(1) + } + if show_progress { + // this is intended to be replaced by the progress lines + println('') + } + mut files_paths := non_option_args.clone() + mut res := CheckResult{} + if term_colors { + os.setenv('VCOLORS', 'always', true) + } + for i := 0; i < files_paths.len; i++ { + file_path := files_paths[i] + if os.is_dir(file_path) { + files_paths << md_file_paths(file_path) + continue + } + real_path := os.real_path(file_path) + lines := os.read_lines(real_path) or { + println('"$file_path" does not exist') + res.warnings++ + continue + } + mut mdfile := MDFile{ + path: file_path + lines: lines + } + res += mdfile.check() + } + if res.errors == 0 && show_progress { + term.clear_previous_line() + } + if res.warnings > 0 || res.errors > 0 || res.oks > 0 { + println('\nWarnings: $res.warnings | Errors: $res.errors | OKs: $res.oks') + } + if res.errors > 0 { + exit(1) + } +} + +fn md_file_paths(dir string) []string { + mut files_to_check := []string{} + md_files := os.walk_ext(dir, '.md') + for file in md_files { + if file.contains_any_substr(['/thirdparty/', 'CHANGELOG']) { + continue + } + files_to_check << file + } + return files_to_check +} + +fn wprintln(s string) { + if !hide_warnings { + println(s) + } +} + +fn ftext(s string, cb fn (string) string) string { + if term_colors { + return cb(s) + } + return s +} + +fn btext(s string) string { + return ftext(s, term.bold) +} + +fn mtext(s string) string { + return ftext(s, term.magenta) +} + +fn rtext(s string) string { + return ftext(s, term.red) +} + +fn wline(file_path string, lnumber int, column int, message string) string { + return btext('$file_path:${lnumber + 1}:${column + 1}:') + btext(mtext(' warn:')) + + rtext(' $message') +} + +fn eline(file_path string, lnumber int, column int, message string) string { + return btext('$file_path:${lnumber + 1}:${column + 1}:') + btext(rtext(' error: $message')) +} + +const ( + default_command = 'compile' +) + +struct VCodeExample { +mut: + text []string + command string + sline int + eline int +} + +enum MDFileParserState { + markdown + vexample + codeblock +} + +struct MDFile { + path string + lines []string +mut: + examples []VCodeExample + current VCodeExample + state MDFileParserState = .markdown +} + +fn (mut f MDFile) progress(message string) { + if show_progress { + term.clear_previous_line() + println('File: ${f.path:-30s}, Lines: ${f.lines.len:5}, $message') + } +} + +fn (mut f MDFile) check() CheckResult { + mut res := CheckResult{} + mut anchor_data := AnchorData{} + for j, line in f.lines { + // f.progress('line: $j') + if line.len > too_long_line_length { + if f.state == .vexample { + wprintln(wline(f.path, j, line.len, 'long V example line')) + wprintln(line) + res.warnings++ + } else if f.state == .codeblock { + wprintln(wline(f.path, j, line.len, 'long code block line')) + wprintln(line) + res.warnings++ + } else if line.starts_with('|') { + wprintln(wline(f.path, j, line.len, 'long table')) + wprintln(line) + res.warnings++ + } else if line.contains('https') { + wprintln(wline(f.path, j, line.len, 'long link')) + wprintln(line) + res.warnings++ + } else { + eprintln(eline(f.path, j, line.len, 'line too long')) + eprintln(line) + res.errors++ + } + } + if f.state == .markdown { + anchor_data.add_links(j, line) + anchor_data.add_link_targets(j, line) + } + + f.parse_line(j, line) + } + anchor_data.check_link_target_match(f.path, mut res) + res += f.check_examples() + return res +} + +fn (mut f MDFile) parse_line(lnumber int, line string) { + if line.starts_with('```v') { + if f.state == .markdown { + f.state = .vexample + mut command := line.replace('```v', '').trim_space() + if command == '' { + command = default_command + } else if command == 'nofmt' { + command += ' $default_command' + } + f.current = VCodeExample{ + sline: lnumber + command: command + } + } + return + } + if line.starts_with('```') { + match f.state { + .vexample { + f.state = .markdown + f.current.eline = lnumber + f.examples << f.current + f.current = VCodeExample{} + return + } + .codeblock { + f.state = .markdown + return + } + .markdown { + f.state = .codeblock + return + } + } + } + if f.state == .vexample { + f.current.text << line + } +} + +struct Headline { + line int + lable string + level int +} + +struct Anchor { + line int +} + +type AnchorTarget = Anchor | Headline + +struct AnchorLink { + line int + lable string +} + +struct AnchorData { +mut: + links map[string][]AnchorLink + anchors map[string][]AnchorTarget +} + +fn (mut ad AnchorData) add_links(line_number int, line string) { + query := r'\[(?P[^\]]+)\]\(\s*#(?P[a-z0-9\-\_\x7f-\uffff]+)\)' + mut re := regex.regex_opt(query) or { panic(err) } + res := re.find_all_str(line) + + for elem in res { + re.match_string(elem) + link := re.get_group_by_name(elem, 'link') + ad.links[link] << AnchorLink{ + line: line_number + lable: re.get_group_by_name(elem, 'lable') + } + } +} + +fn (mut ad AnchorData) add_link_targets(line_number int, line string) { + if line.trim_space().starts_with('#') { + if headline_start_pos := line.index(' ') { + headline := line.substr(headline_start_pos + 1, line.len) + link := create_ref_link(headline) + ad.anchors[link] << Headline{ + line: line_number + lable: headline + level: headline_start_pos + } + } + } else { + query := '[a-z0-9\\-\\_\\x7f-\\uffff]+)["\']\\s*/>' + mut re := regex.regex_opt(query) or { panic(err) } + res := re.find_all_str(line) + + for elem in res { + re.match_string(elem) + link := re.get_group_by_name(elem, 'link') + ad.anchors[link] << Anchor{ + line: line_number + } + } + } +} + +fn (mut ad AnchorData) check_link_target_match(fpath string, mut res CheckResult) { + mut checked_headlines := []string{} + mut found_error_warning := false + for link, linkdata in ad.links { + if link in ad.anchors { + checked_headlines << link + if ad.anchors[link].len > 1 { + found_error_warning = true + res.errors++ + for anchordata in ad.anchors[link] { + eprintln(eline(fpath, anchordata.line, 0, 'multiple link targets of existing link (#$link)')) + } + } + } else { + found_error_warning = true + res.errors++ + for brokenlink in linkdata { + eprintln(eline(fpath, brokenlink.line, 0, 'no link target found for existing link [$brokenlink.lable](#$link)')) + } + } + } + for link, anchor_lists in ad.anchors { + if !(link in checked_headlines) { + if anchor_lists.len > 1 { + for anchor in anchor_lists { + line := match anchor { + Headline { + anchor.line + } + Anchor { + anchor.line + } + } + wprintln(wline(fpath, line, 0, 'multiple link target for non existing link (#$link)')) + found_error_warning = true + res.warnings++ + } + } + } + } + if found_error_warning { + eprintln('') // fix suppressed last error output + } +} + +// based on a reference sample md doc +// https://github.com/aheissenberger/vlang-markdown-module/blob/master/test.md +fn create_ref_link(s string) string { + mut result := '' + for c in s.trim_space() { + result += match c { + `a`...`z`, `0`...`9` { + c.ascii_str() + } + `A`...`Z` { + c.ascii_str().to_lower() + } + ` `, `-` { + '-' + } + `_` { + '_' + } + else { + if c > 127 { c.ascii_str() } else { '' } + } + } + } + return result +} + +fn (mut f MDFile) debug() { + for e in f.examples { + eprintln('f.path: $f.path | example: $e') + } +} + +fn cmdexecute(cmd string) int { + res := os.execute(cmd) + if res.exit_code < 0 { + return 1 + } + if res.exit_code != 0 { + eprint(res.output) + } + return res.exit_code +} + +fn silent_cmdexecute(cmd string) int { + res := os.execute(cmd) + return res.exit_code +} + +fn get_fmt_exit_code(vfile string, vexe string) int { + return silent_cmdexecute('"$vexe" fmt -verify $vfile') +} + +fn (mut f MDFile) check_examples() CheckResult { + mut errors := 0 + mut oks := 0 + vexe := pref.vexe_path() + for e in f.examples { + if e.command == 'ignore' { + continue + } + if e.command == 'wip' { + continue + } + fname := os.base(f.path).replace('.md', '_md') + uid := rand.ulid() + vfile := os.join_path(os.temp_dir(), 'check_${fname}_example_${e.sline}__${e.eline}__${uid}.v') + mut should_cleanup_vfile := true + // eprintln('>>> checking example $vfile ...') + vcontent := e.text.join('\n') + '\n' + os.write_file(vfile, vcontent) or { panic(err) } + mut acommands := e.command.split(' ') + nofmt := 'nofmt' in acommands + for command in acommands { + f.progress('example from $e.sline to $e.eline, command: $command') + fmt_res := if nofmt { 0 } else { get_fmt_exit_code(vfile, vexe) } + match command { + 'compile' { + res := cmdexecute('"$vexe" -w -Wfatal-errors -o x.c $vfile') + os.rm('x.c') or {} + if res != 0 || fmt_res != 0 { + if res != 0 { + eprintln(eline(f.path, e.sline, 0, 'example failed to compile')) + } + if fmt_res != 0 { + eprintln(eline(f.path, e.sline, 0, 'example is not formatted')) + } + eprintln(vcontent) + should_cleanup_vfile = false + errors++ + continue + } + oks++ + } + 'globals' { + res := cmdexecute('"$vexe" -w -Wfatal-errors -enable-globals -o x.c $vfile') + os.rm('x.c') or {} + if res != 0 || fmt_res != 0 { + if res != 0 { + eprintln(eline(f.path, e.sline, 0, '`example failed to compile with -enable-globals')) + } + if fmt_res != 0 { + eprintln(eline(f.path, e.sline, 0, '`example is not formatted')) + } + eprintln(vcontent) + should_cleanup_vfile = false + errors++ + continue + } + oks++ + } + 'live' { + res := cmdexecute('"$vexe" -w -Wfatal-errors -live -o x.c $vfile') + if res != 0 || fmt_res != 0 { + if res != 0 { + eprintln(eline(f.path, e.sline, 0, 'example failed to compile with -live')) + } + if fmt_res != 0 { + eprintln(eline(f.path, e.sline, 0, 'example is not formatted')) + } + eprintln(vcontent) + should_cleanup_vfile = false + errors++ + continue + } + oks++ + } + 'failcompile' { + res := silent_cmdexecute('"$vexe" -w -Wfatal-errors -o x.c $vfile') + os.rm('x.c') or {} + if res == 0 || fmt_res != 0 { + if res == 0 { + eprintln(eline(f.path, e.sline, 0, '`failcompile` example compiled')) + } + if fmt_res != 0 { + eprintln(eline(f.path, e.sline, 0, 'example is not formatted')) + } + eprintln(vcontent) + should_cleanup_vfile = false + errors++ + continue + } + oks++ + } + 'oksyntax' { + res := cmdexecute('"$vexe" -w -Wfatal-errors -check-syntax $vfile') + if res != 0 || fmt_res != 0 { + if res != 0 { + eprintln(eline(f.path, e.sline, 0, '`oksyntax` example with invalid syntax')) + } + if fmt_res != 0 { + eprintln(eline(f.path, e.sline, 0, '`oksyntax` example is not formatted')) + } + eprintln(vcontent) + should_cleanup_vfile = false + errors++ + continue + } + oks++ + } + 'badsyntax' { + res := silent_cmdexecute('"$vexe" -w -Wfatal-errors -check-syntax $vfile') + if res == 0 { + eprintln(eline(f.path, e.sline, 0, '`badsyntax` example can be parsed fine')) + eprintln(vcontent) + should_cleanup_vfile = false + errors++ + continue + } + oks++ + } + 'nofmt' {} + else { + eprintln(eline(f.path, e.sline, 0, 'unrecognized command: "$command", use one of: wip/ignore/compile/failcompile/oksyntax/badsyntax')) + should_cleanup_vfile = false + errors++ + } + } + } + if should_cleanup_vfile { + os.rm(vfile) or { panic(err) } + } + } + return CheckResult{ + errors: errors + oks: oks + } +} diff --git a/v_windows/v/old/cmd/tools/vcomplete.v b/v_windows/v/old/cmd/tools/vcomplete.v new file mode 100644 index 0000000..8e61755 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vcomplete.v @@ -0,0 +1,451 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +// +// Utility functions helping integrate with various shell auto-completion systems. +// The install process and communication is inspired from that of [kitty](https://sw.kovidgoyal.net/kitty/#completion-for-kitty) +// This method avoids writing and maintaining external files on the user's file system. +// The user will be responsible for adding a small line to their .*rc - that will ensure *live* (i.e. not-static) +// auto-completion features. +// +// # bash +// To install auto-completion for V in bash, simply add this code to your `~/.bashrc`: +// `source /dev/stdin <<<"$(v complete setup bash)"` +// On more recent versions of bash (>3.2) this should suffice: +// `source <(v complete setup bash)` +// +// # fish +// For versions of fish <3.0.0, add the following to your `~/.config/fish/config.fish` +// `v complete setup fish | source` +// Later versions of fish source completions by default. +// +// # zsh +// To install auto-completion for V in zsh - please add the following to your `~/.zshrc`: +// ``` +// autoload -Uz compinit +// compinit +// # Completion for v +// v complete setup zsh | source /dev/stdin +// ``` +// Please note that you should let v load the zsh completions after the call to compinit +// +// # powershell +// To install auto-complete for V in PowerShell, simply do this +// `v complete setup powershell >> $PROFILE` +// and reload profile +// `& $PROFILE` +// If `$PROFILE` didn't exist yet, create it before +// `New-Item -Type File -Force $PROFILE` +// +module main + +import os + +const ( + auto_complete_shells = ['bash', 'fish', 'zsh', 'powershell'] // list of supported shells + vexe = os.getenv('VEXE') +) + +// Snooped from cmd/v/v.v, vlib/v/pref/pref.v +const ( + auto_complete_commands = [ + // simple_cmd + 'fmt', + 'up', + 'vet', + 'self', + 'tracev', + 'symlink', + 'bin2v', + 'test', + 'test-fmt', + 'test-self', + 'test-cleancode', + 'repl', + 'complete', + 'build-tools', + 'build-examples', + 'build-vbinaries', + 'setup-freetype', + 'doc', + 'doctor', + // commands + 'help', + 'new', + 'init', + 'complete', + 'translate', + 'self', + 'search', + 'install', + 'update', + 'upgrade', + 'outdated', + 'list', + 'remove', + 'vlib-docs', + 'get', + 'version', + 'run', + 'build', + 'build-module', + ] + auto_complete_flags = [ + '-apk', + '-show-timings', + '-check-syntax', + '-v', + '-progress', + '-silent', + '-g', + '-cg', + '-repl', + '-live', + '-sharedlive', + '-shared', + '--enable-globals', + '-enable-globals', + '-autofree', + '-compress', + '-freestanding', + '-no-preludes', + '-prof', + '-profile', + '-profile-no-inline', + '-prod', + '-simulator', + '-stats', + '-obfuscate', + '-translated', + '-color', + '-nocolor', + '-showcc', + '-show-c-output', + '-experimental', + '-usecache', + '-prealloc', + '-parallel', + '-native', + '-W', + '-keepc', + '-w', + '-print-v-files', + '-error-limit', + '-os', + '-printfn', + '-cflags', + '-define', + '-d', + '-cc', + '-o', + '-b', + '-path', + '-custom-prelude', + '-name', + '-bundle', + '-V', + '-version', + '--version', + ] + auto_complete_flags_doc = [ + '-all', + '-f', + '-h', + '-help', + '-m', + '-o', + '-readme', + '-v', + '-filename', + '-pos', + '-no-timestamp', + '-inline-assets', + '-open', + '-p', + '-s', + '-l', + ] + auto_complete_flags_fmt = [ + '-c', + '-diff', + '-l', + '-w', + '-debug', + '-verify', + ] + auto_complete_flags_bin2v = [ + '-h', + '--help', + '-m', + '--module', + '-p', + '--prefix', + '-w', + '--write', + ] + auto_complete_flags_self = [ + '-prod', + ] + auto_complete_compilers = [ + 'cc', + 'gcc', + 'tcc', + 'tinyc', + 'clang', + 'mingw', + 'msvc', + ] +) + +// auto_complete prints auto completion results back to the calling shell's completion system. +// auto_complete acts as communication bridge between the calling shell and V's completions. +fn auto_complete(args []string) { + if args.len <= 1 || args[0] != 'complete' { + if args.len == 1 { + eprintln('auto completion require arguments to work.') + } else { + eprintln('auto completion failed for "$args".') + } + exit(1) + } + sub := args[1] + sub_args := args[1..] + match sub { + 'setup' { + if sub_args.len <= 1 || sub_args[1] !in auto_complete_shells { + eprintln('please specify a shell to setup auto completion for ($auto_complete_shells).') + exit(1) + } + shell := sub_args[1] + mut setup := '' + match shell { + 'bash' { + setup = ' +_v_completions() { + local src + local limit + # Send all words up to the word the cursor is currently on + let limit=1+\$COMP_CWORD + src=\$($vexe complete bash \$(printf "%s\\n" \${COMP_WORDS[@]: 0:\$limit})) + if [[ \$? == 0 ]]; then + eval \${src} + #echo \${src} + fi +} + +complete -o nospace -F _v_completions v +' + } + 'fish' { + setup = ' +function __v_completions + # Send all words up to the one before the cursor + $vexe complete fish (commandline -cop) +end +complete -f -c v -a "(__v_completions)" +' + } + 'zsh' { + setup = ' +#compdef v +_v() { + local src + # Send all words up to the word the cursor is currently on + src=\$($vexe complete zsh \$(printf "%s\\n" \${(@)words[1,\$CURRENT]})) + if [[ \$? == 0 ]]; then + eval \${src} + #echo \${src} + fi +} +compdef _v v +' + } + 'powershell' { + setup = ' +Register-ArgumentCompleter -Native -CommandName v -ScriptBlock { + param(\$commandName, \$wordToComplete, \$cursorPosition) + $vexe complete powershell "\$wordToComplete" | ForEach-Object { + [System.Management.Automation.CompletionResult]::new(\$_, \$_, \'ParameterValue\', \$_) + } +} +' + } + else {} + } + println(setup) + } + 'bash' { + if sub_args.len <= 1 { + exit(0) + } + mut lines := []string{} + list := auto_complete_request(sub_args[1..]) + for entry in list { + lines << "COMPREPLY+=('$entry')" + } + println(lines.join('\n')) + } + 'fish', 'powershell' { + if sub_args.len <= 1 { + exit(0) + } + mut lines := []string{} + list := auto_complete_request(sub_args[1..]) + for entry in list { + lines << '$entry' + } + println(lines.join('\n')) + } + 'zsh' { + if sub_args.len <= 1 { + exit(0) + } + mut lines := []string{} + list := auto_complete_request(sub_args[1..]) + for entry in list { + lines << 'compadd -U -S' + '""' + ' -- ' + "'$entry';" + } + println(lines.join('\n')) + } + else {} + } + exit(0) +} + +// append_separator_if_dir is a utility function.that returns the input `path` appended an +// OS dependant path separator if the `path` is a directory. +fn append_separator_if_dir(path string) string { + if os.is_dir(path) && !path.ends_with(os.path_separator) { + return path + os.path_separator + } + return path +} + +// auto_complete_request retuns a list of completions resolved from a full argument list. +fn auto_complete_request(args []string) []string { + // Using space will ensure a uniform input in cases where the shell + // returns the completion input as a string (['v','run'] vs. ['v run']). + split_by := ' ' + request := args.join(split_by) + mut list := []string{} + // new_part := request.ends_with('\n\n') + mut parts := request.trim_right(' ').split(split_by) + if parts.len <= 1 { // 'v ' -> top level commands. + for command in auto_complete_commands { + list << command + } + } else { + part := parts.last().trim(' ') + mut parent_command := '' + for i := parts.len - 1; i >= 0; i-- { + if parts[i].starts_with('-') { + continue + } + parent_command = parts[i] + break + } + get_flags := fn (base []string, flag string) []string { + if flag.len == 1 { return base + } else { return base.filter(it.starts_with(flag)) + } + } + if part.starts_with('-') { // 'v -' -> flags. + match parent_command { + 'bin2v' { // 'v bin2v -' + list = get_flags(auto_complete_flags_bin2v, part) + } + 'build' { // 'v build -' -> flags. + list = get_flags(auto_complete_flags, part) + } + 'doc' { // 'v doc -' -> flags. + list = get_flags(auto_complete_flags_doc, part) + } + 'fmt' { // 'v fmt -' -> flags. + list = get_flags(auto_complete_flags_fmt, part) + } + 'self' { // 'v self -' -> flags. + list = get_flags(auto_complete_flags_self, part) + } + else { + for flag in auto_complete_flags { + if flag == part { + if flag == '-cc' { // 'v -cc ' -> list of available compilers. + for compiler in auto_complete_compilers { + path := os.find_abs_path_of_executable(compiler) or { '' } + if path != '' { + list << compiler + } + } + } + } else if flag.starts_with(part) { // 'v -' -> flags matching "". + list << flag + } + } + } + } + } else { + match part { + 'help' { // 'v help ' -> top level commands except "help". + list = auto_complete_commands.filter(it != part && it != 'complete') + } + else { + // 'v ' -> commands matching "". + // Don't include if part matches a full command - instead go to path completion below. + for command in auto_complete_commands { + if part != command && command.starts_with(part) { + list << command + } + } + } + } + } + // Nothing of value was found. + // Mimic shell dir and file completion + if list.len == 0 { + mut ls_path := '.' + mut collect_all := part in auto_complete_commands + mut path_complete := false + if part.ends_with(os.path_separator) || part == '.' || part == '..' { + // 'v (.*/$|.|..)' -> output full directory list + ls_path = '.' + os.path_separator + part + collect_all = true + } else if !collect_all && part.contains(os.path_separator) && os.is_dir(os.dir(part)) { + // 'v (.*/.* && os.is_dir)' -> output completion friendly directory list + ls_path = os.dir(part) + path_complete = true + } + entries := os.ls(ls_path) or { return list } + last := part.all_after_last(os.path_separator) + if path_complete { + path := part.all_before_last(os.path_separator) + for entry in entries { + if entry.starts_with(last) { + list << append_separator_if_dir(os.join_path(path, entry)) + } + } + // If only one possible file - send full path to completion system. + // Please note that this might be bash specific - needs more testing. + if list.len == 1 { + list = [list[0]] + } + } else { + for entry in entries { + if collect_all { + list << append_separator_if_dir(entry) + } else { + if entry.starts_with(last) { + list << append_separator_if_dir(entry) + } + } + } + } + } + } + return list +} + +fn main() { + args := os.args[1..] + // println('"$args"') + auto_complete(args) +} diff --git a/v_windows/v/old/cmd/tools/vcreate.v b/v_windows/v/old/cmd/tools/vcreate.v new file mode 100644 index 0000000..e147979 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vcreate.v @@ -0,0 +1,186 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license that can be found in the LICENSE file. +module main + +// This module follows a similar convention to Rust: `init` makes the +// structure of the program in the _current_ directory, while `new` +// makes the program structure in a _sub_ directory. Besides that, the +// functionality is essentially the same. +import os + +struct Create { +mut: + name string + description string + version string + license string +} + +fn cerror(e string) { + eprintln('\nerror: $e') +} + +fn check_name(name string) string { + if name.trim_space().len == 0 { + cerror('project name cannot be empty') + exit(1) + } + if name.is_title() { + mut cname := name.to_lower() + if cname.contains(' ') { + cname = cname.replace(' ', '_') + } + eprintln('warning: the project name cannot be capitalized, the name will be changed to `$cname`') + return cname + } + if name.contains(' ') { + cname := name.replace(' ', '_') + eprintln('warning: the project name cannot contain spaces, the name will be changed to `$cname`') + return cname + } + return name +} + +fn vmod_content(c Create) string { + return [ + 'Module {', + " name: '$c.name'", + " description: '$c.description'", + " version: '$c.version'", + " license: '$c.license'", + ' dependencies: []', + '}', + '', + ].join('\n') +} + +fn main_content() string { + return [ + 'module main\n', + 'fn main() {', + " println('Hello World!')", + '}', + '', + ].join('\n') +} + +fn gen_gitignore(name string) string { + return [ + '# Binaries for programs and plugins', + 'main', + '$name', + '*.exe', + '*.exe~', + '*.so', + '*.dylib', + '*.dll', + '', + ].join('\n') +} + +fn (c &Create) write_vmod(new bool) { + vmod_path := if new { '$c.name/v.mod' } else { 'v.mod' } + mut vmod := os.create(vmod_path) or { + cerror(err.msg) + exit(1) + } + vmod.write_string(vmod_content(c)) or { panic(err) } + vmod.close() +} + +fn (c &Create) write_main(new bool) { + if !new && (os.exists('${c.name}.v') || os.exists('src/${c.name}.v')) { + return + } + main_path := if new { '$c.name/${c.name}.v' } else { '${c.name}.v' } + mut mainfile := os.create(main_path) or { + cerror(err.msg) + exit(2) + } + mainfile.write_string(main_content()) or { panic(err) } + mainfile.close() +} + +fn (c &Create) create_git_repo(dir string) { + // Create Git Repo and .gitignore file + if !os.is_dir('$dir/.git') { + res := os.execute('git init $dir') + if res.exit_code != 0 { + cerror('Unable to create git repo') + exit(4) + } + } + if !os.exists('$dir/.gitignore') { + mut fl := os.create('$dir/.gitignore') or { + // We don't really need a .gitignore, it's just a nice-to-have + return + } + fl.write_string(gen_gitignore(c.name)) or { panic(err) } + fl.close() + } +} + +fn create(args []string) { + mut c := Create{} + c.name = check_name(if args.len > 0 { args[0] } else { os.input('Input your project name: ') }) + if c.name == '' { + cerror('project name cannot be empty') + exit(1) + } + if c.name.contains('-') { + cerror('"$c.name" should not contain hyphens') + exit(1) + } + if os.is_dir(c.name) { + cerror('$c.name folder already exists') + exit(3) + } + c.description = if args.len > 1 { args[1] } else { os.input('Input your project description: ') } + default_version := '0.0.0' + c.version = os.input('Input your project version: ($default_version) ') + if c.version == '' { + c.version = default_version + } + default_license := 'MIT' + c.license = os.input('Input your project license: ($default_license) ') + if c.license == '' { + c.license = default_license + } + println('Initialising ...') + os.mkdir(c.name) or { panic(err) } + c.write_vmod(true) + c.write_main(true) + c.create_git_repo(c.name) +} + +fn init_project() { + if os.exists('v.mod') { + cerror('`v init` cannot be run on existing v modules') + exit(3) + } + mut c := Create{} + c.name = check_name(os.file_name(os.getwd())) + c.description = '' + c.write_vmod(false) + c.write_main(false) + c.create_git_repo('.') + + println('Change the description of your project in `v.mod`') +} + +fn main() { + cmd := os.args[1] + match cmd { + 'new' { + create(os.args[2..]) + } + 'init' { + init_project() + } + else { + cerror('unknown command: $cmd') + exit(1) + } + } + println('Complete!') +} diff --git a/v_windows/v/old/cmd/tools/vcreate_test.v b/v_windows/v/old/cmd/tools/vcreate_test.v new file mode 100644 index 0000000..fe829e6 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vcreate_test.v @@ -0,0 +1,79 @@ +import os + +const test_path = 'vcreate_test' + +fn init_and_check() ? { + vexe := @VEXE + os.execute_or_exit('$vexe init') + + assert os.read_file('vcreate_test.v') ? == [ + 'module main\n', + 'fn main() {', + " println('Hello World!')", + '}', + '', + ].join('\n') + + assert os.read_file('v.mod') ? == [ + 'Module {', + " name: 'vcreate_test'", + " description: ''", + " version: ''", + " license: ''", + ' dependencies: []', + '}', + '', + ].join('\n') + + assert os.read_file('.gitignore') ? == [ + '# Binaries for programs and plugins', + 'main', + 'vcreate_test', + '*.exe', + '*.exe~', + '*.so', + '*.dylib', + '*.dll', + '', + ].join('\n') +} + +fn test_v_init() ? { + dir := os.join_path(os.temp_dir(), test_path) + os.rmdir_all(dir) or {} + os.mkdir(dir) or {} + defer { + os.rmdir_all(dir) or {} + } + os.chdir(dir) + + init_and_check() ? +} + +fn test_v_init_in_git_dir() ? { + dir := os.join_path(os.temp_dir(), test_path) + os.rmdir_all(dir) or {} + os.mkdir(dir) or {} + defer { + os.rmdir_all(dir) or {} + } + os.chdir(dir) + os.execute_or_exit('git init .') + init_and_check() ? +} + +fn test_v_init_no_overwrite_gitignore() ? { + dir := os.join_path(os.temp_dir(), test_path) + os.rmdir_all(dir) or {} + os.mkdir(dir) or {} + os.write_file('$dir/.gitignore', 'blah') ? + defer { + os.rmdir_all(dir) or {} + } + os.chdir(dir) + + vexe := @VEXE + os.execute_or_exit('$vexe init') + + assert os.read_file('.gitignore') ? == 'blah' +} diff --git a/v_windows/v/old/cmd/tools/vdoc/html.v b/v_windows/v/old/cmd/tools/vdoc/html.v new file mode 100644 index 0000000..e253898 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vdoc/html.v @@ -0,0 +1,553 @@ +module main + +import os +import net.urllib +import strings +import markdown +import regex +import v.scanner +import v.ast +import v.token +import v.doc +import v.pref + +const ( + css_js_assets = ['doc.css', 'normalize.css', 'doc.js', 'dark-mode.js'] + res_path = os.resource_abs_path('resources') + favicons_path = os.join_path(res_path, 'favicons') + link_svg = '' + html_content = ' + + + + + + {{ title }} | vdoc + + + + + + + + + + + {{ head_assets }} + + +
+ +
+
+
+{{ contents }} + +
+ {{ right_content }} +
+
+
+ {{ footer_assets }} + + +' +) + +enum HighlightTokenTyp { + unone + boolean + builtin + char + comment + function + keyword + name + number + operator + punctuation + string + symbol + none_ + module_ + prefix +} + +struct SearchModuleResult { + description string + link string +} + +struct SearchResult { + prefix string + badge string + description string + link string +} + +fn (vd VDoc) render_search_index(out Output) { + mut js_search_index := strings.new_builder(200) + mut js_search_data := strings.new_builder(200) + js_search_index.write_string('var searchModuleIndex = [') + js_search_data.write_string('var searchModuleData = [') + for i, title in vd.search_module_index { + data := vd.search_module_data[i] + js_search_index.write_string('"$title",') + js_search_data.write_string('["$data.description","$data.link"],') + } + js_search_index.writeln('];') + js_search_index.write_string('var searchIndex = [') + js_search_data.writeln('];') + js_search_data.write_string('var searchData = [') + for i, title in vd.search_index { + data := vd.search_data[i] + js_search_index.write_string('"$title",') + // array instead of object to reduce file size + js_search_data.write_string('["$data.badge","$data.description","$data.link","$data.prefix"],') + } + js_search_index.writeln('];') + js_search_data.writeln('];') + out_file_path := os.join_path(out.path, 'search_index.js') + os.write_file(out_file_path, js_search_index.str() + js_search_data.str()) or { panic(err) } +} + +fn (mut vd VDoc) render_static_html(out Output) { + vd.assets = map{ + 'doc_css': vd.get_resource(css_js_assets[0], out) + 'normalize_css': vd.get_resource(css_js_assets[1], out) + 'doc_js': vd.get_resource(css_js_assets[2], out) + 'dark_mode_js': vd.get_resource(css_js_assets[3], out) + 'light_icon': vd.get_resource('light.svg', out) + 'dark_icon': vd.get_resource('dark.svg', out) + 'menu_icon': vd.get_resource('menu.svg', out) + 'arrow_icon': vd.get_resource('arrow.svg', out) + } +} + +fn (vd VDoc) get_resource(name string, out Output) string { + cfg := vd.cfg + path := os.join_path(res_path, name) + mut res := os.read_file(path) or { panic('vdoc: could not read $path') } + /* + if minify { + if name.ends_with('.js') { + res = js_compress(res) + } else { + res = res.split_into_lines().map(it.trim_space()).join('') + } + } + */ + // TODO: Make SVG inline for now + if cfg.inline_assets || path.ends_with('.svg') { + return res + } else { + output_path := os.join_path(out.path, name) + if !os.exists(output_path) { + println('Generating $out.typ in "$output_path"') + os.write_file(output_path, res) or { panic(err) } + } + return name + } +} + +fn (mut vd VDoc) collect_search_index(out Output) { + cfg := vd.cfg + for doc in vd.docs { + mod := doc.head.name + vd.search_module_index << mod + comments := if cfg.include_examples { + doc.head.merge_comments() + } else { + doc.head.merge_comments_without_examples() + } + vd.search_module_data << SearchModuleResult{ + description: trim_doc_node_description(comments) + link: vd.get_file_name(mod, out) + } + for _, dn in doc.contents { + vd.create_search_results(mod, dn, out) + } + } +} + +fn (mut vd VDoc) create_search_results(mod string, dn doc.DocNode, out Output) { + cfg := vd.cfg + if dn.kind == .const_group { + return + } + comments := if cfg.include_examples { + dn.merge_comments() + } else { + dn.merge_comments_without_examples() + } + dn_description := trim_doc_node_description(comments) + vd.search_index << dn.name + vd.search_data << SearchResult{ + prefix: if dn.parent_name != '' { '$dn.kind ($dn.parent_name)' } else { '$dn.kind ' } + description: dn_description + badge: mod + link: vd.get_file_name(mod, out) + '#' + get_node_id(dn) + } + for child in dn.children { + vd.create_search_results(mod, child, out) + } +} + +fn (vd VDoc) write_content(cn &doc.DocNode, d &doc.Doc, mut hw strings.Builder) { + cfg := vd.cfg + base_dir := os.dir(os.real_path(cfg.input_path)) + file_path_name := if cfg.is_multi { + cn.file_path.replace('$base_dir/', '') + } else { + os.file_name(cn.file_path) + } + src_link := get_src_link(vd.manifest.repo_url, file_path_name, cn.pos.line_nr + 1) + if cn.content.len != 0 || (cn.name == 'Constants') { + hw.write_string(doc_node_html(cn, src_link, false, cfg.include_examples, d.table)) + } + for child in cn.children { + child_file_path_name := child.file_path.replace('$base_dir/', '') + child_src_link := get_src_link(vd.manifest.repo_url, child_file_path_name, + child.pos.line_nr + 1) + hw.write_string(doc_node_html(child, child_src_link, false, cfg.include_examples, + d.table)) + } +} + +fn (vd VDoc) gen_html(d doc.Doc) string { + cfg := vd.cfg + mut symbols_toc := strings.new_builder(200) + mut modules_toc := strings.new_builder(200) + mut contents := strings.new_builder(200) + dcs_contents := d.contents.arr() + // generate toc first + contents.writeln(doc_node_html(d.head, '', true, cfg.include_examples, d.table)) + if is_module_readme(d.head) { + write_toc(d.head, mut symbols_toc) + } + for cn in dcs_contents { + vd.write_content(&cn, &d, mut contents) + write_toc(cn, mut symbols_toc) + } // write head + // write css + version := if vd.manifest.version.len != 0 { vd.manifest.version } else { '' } + header_name := if cfg.is_multi && vd.docs.len > 1 { + os.file_name(os.real_path(cfg.input_path)) + } else { + d.head.name + } + // write nav1 + if cfg.is_multi || vd.docs.len > 1 { + mut submod_prefix := '' + for i, dc in vd.docs { + if i - 1 >= 0 && dc.head.name.starts_with(submod_prefix + '.') { + continue + } + names := dc.head.name.split('.') + submod_prefix = if names.len > 1 { names[0] } else { dc.head.name } + mut href_name := './${dc.head.name}.html' + if (cfg.is_vlib && dc.head.name == 'builtin' && !cfg.include_readme) + || dc.head.name == 'README' { + href_name = './index.html' + } else if submod_prefix !in vd.docs.map(it.head.name) { + href_name = '#' + } + submodules := vd.docs.filter(it.head.name.starts_with(submod_prefix + '.')) + dropdown := if submodules.len > 0 { vd.assets['arrow_icon'] } else { '' } + active_class := if dc.head.name == d.head.name { ' active' } else { '' } + modules_toc.write_string('
  • ') + for j, cdoc in submodules { + if j == 0 { + modules_toc.write_string('
      ') + } + submod_name := cdoc.head.name.all_after(submod_prefix + '.') + sub_selected_classes := if cdoc.head.name == d.head.name { + ' class="active"' + } else { + '' + } + modules_toc.write_string('$submod_name') + if j == submodules.len - 1 { + modules_toc.write_string('
    ') + } + } + modules_toc.write_string('
  • ') + } + } + modules_toc_str := modules_toc.str() + symbols_toc_str := symbols_toc.str() + result := html_content.replace('{{ title }}', d.head.name).replace('{{ head_name }}', + header_name).replace('{{ version }}', version).replace('{{ light_icon }}', vd.assets['light_icon']).replace('{{ dark_icon }}', + vd.assets['dark_icon']).replace('{{ menu_icon }}', vd.assets['menu_icon']).replace('{{ head_assets }}', + if cfg.inline_assets { + '\n${tabs[0]}\n${tabs[0]}\n${tabs[0]}' + } else { + '\n${tabs[0]}\n${tabs[0]}\n${tabs[0]}' + }).replace('{{ toc_links }}', if cfg.is_multi || vd.docs.len > 1 { + modules_toc_str + } else { + symbols_toc_str + }).replace('{{ contents }}', contents.str()).replace('{{ right_content }}', if cfg.is_multi + && vd.docs.len > 1 && d.head.name != 'README' { + '
      ' + symbols_toc_str + '
    ' + } else { + '' + }).replace('{{ footer_content }}', gen_footer_text(d, !cfg.no_timestamp)).replace('{{ footer_assets }}', + if cfg.inline_assets { + '' + } else { + '' + }) + return result +} + +fn get_src_link(repo_url string, file_name string, line_nr int) string { + mut url := urllib.parse(repo_url) or { return '' } + if url.path.len <= 1 || file_name.len == 0 { + return '' + } + url.path = url.path.trim_right('/') + match url.host { + 'github.com' { '/blob/master/$file_name' } + 'gitlab.com' { '/-/blob/master/$file_name' } + 'git.sir.ht' { '/tree/master/$file_name' } + else { '' } + } + if url.path == '/' { + return '' + } + url.fragment = 'L$line_nr' + return url.str() +} + +fn html_highlight(code string, tb &ast.Table) string { + builtin := ['bool', 'string', 'i8', 'i16', 'int', 'i64', 'i128', 'byte', 'u16', 'u32', 'u64', + 'u128', 'rune', 'f32', 'f64', 'int_literal', 'float_literal', 'byteptr', 'voidptr', 'any'] + highlight_code := fn (tok token.Token, typ HighlightTokenTyp) string { + lit := if typ in [.unone, .operator, .punctuation] { + tok.kind.str() + } else if typ == .string { + "'$tok.lit'" + } else if typ == .char { + '`$tok.lit`' + } else { + tok.lit + } + if typ in [.unone, .name] { + return lit + } + return '$lit' + } + mut s := scanner.new_scanner(code, .parse_comments, &pref.Preferences{}) + mut tok := s.scan() + mut next_tok := s.scan() + mut buf := strings.new_builder(200) + mut i := 0 + for i < code.len { + if i == tok.pos { + mut tok_typ := HighlightTokenTyp.unone + match tok.kind { + .name { + if tok.lit in builtin || tb.known_type(tok.lit) { + tok_typ = .builtin + } else if next_tok.kind == .lcbr { + tok_typ = .symbol + } else if next_tok.kind == .lpar { + tok_typ = .function + } else { + tok_typ = .name + } + } + .comment { + tok_typ = .comment + } + .chartoken { + tok_typ = .char + } + .string { + tok_typ = .string + } + .number { + tok_typ = .number + } + .key_true, .key_false { + tok_typ = .boolean + } + .lpar, .lcbr, .rpar, .rcbr, .lsbr, .rsbr, .semicolon, .colon, .comma, .dot { + tok_typ = .punctuation + } + else { + if token.is_key(tok.lit) || token.is_decl(tok.kind) { + tok_typ = .keyword + } else if tok.kind == .decl_assign || tok.kind.is_assign() || tok.is_unary() + || tok.kind.is_relational() || tok.kind.is_infix() { + tok_typ = .operator + } + } + } + buf.write_string(highlight_code(tok, tok_typ)) + if next_tok.kind != .eof { + i = tok.pos + tok.len + tok = next_tok + next_tok = s.scan() + } else { + break + } + } else { + buf.write_b(code[i]) + i++ + } + } + return buf.str() +} + +fn doc_node_html(dn doc.DocNode, link string, head bool, include_examples bool, tb &ast.Table) string { + mut dnw := strings.new_builder(200) + head_tag := if head { 'h1' } else { 'h2' } + comments := dn.merge_comments_without_examples() + // Allow README.md to go through unescaped except for script tags + escaped_html := if head && is_module_readme(dn) { + // Strip markdown [TOC] directives, since we generate our own. + stripped := comments.replace('[TOC]', '') + markdown_escape_script_tags(stripped) + } else { + html_tag_escape(comments) + } + md_content := markdown.to_html(escaped_html) + highlighted_code := html_highlight(dn.content, tb) + node_class := if dn.kind == .const_group { ' const' } else { '' } + sym_name := get_sym_name(dn) + has_deprecated := 'deprecated' in dn.tags + mut tags := dn.tags.filter(it != 'deprecated') + tags.sort() + mut node_id := get_node_id(dn) + mut hash_link := if !head { ' #' } else { '' } + if head && is_module_readme(dn) { + node_id = 'readme_$node_id' + hash_link = ' #' + } + dnw.writeln('${tabs[1]}
    ') + if dn.name.len > 0 { + if dn.kind == .const_group { + dnw.write_string('${tabs[2]}
    <$head_tag>$sym_name$hash_link') + } else { + dnw.write_string('${tabs[2]}
    <$head_tag>$dn.kind $sym_name$hash_link') + } + if link.len != 0 { + dnw.write_string('$link_svg') + } + dnw.write_string('
    ') + } + if tags.len > 0 || has_deprecated { + mut attributes := if has_deprecated { + '
    deprecated
    ' + } else { + '' + } + attributes += tags.map('
    $it
    ').join('') + dnw.writeln('
    $attributes
    ') + } + if !head && dn.content.len > 0 { + dnw.writeln('
    $highlighted_code
    ') + } + // do not mess with md_content further, its formatting is important, just output it 1:1 ! + dnw.writeln('$md_content\n') + // Write examples if any found + examples := dn.examples() + if include_examples && examples.len > 0 { + example_title := if examples.len > 1 { 'Examples' } else { 'Example' } + dnw.writeln('

    $example_title

    ') + for example in examples { + // hl_example := html_highlight(example, tb) + dnw.writeln('
    $example
    ') + } + dnw.writeln('
    ') + } + dnw.writeln('
    ') + dnw_str := dnw.str() + return dnw_str +} + +fn html_tag_escape(str string) string { + excaped_string := str.replace_each(['<', '<', '>', '>']) + mut re := regex.regex_opt(r'`.+[(<)(>)].+`') or { regex.RE{} } + if re.find_all_str(excaped_string).len > 0 { + return str + } + return excaped_string +} + +/* +fn js_compress(str string) string { + mut js := strings.new_builder(200) + lines := str.split_into_lines() + rules := [') {', ' = ', ', ', '{ ', ' }', ' (', '; ', ' + ', ' < ', ' - ', ' || ', ' var', + ': ', ' >= ', ' && ', ' else if', ' === ', ' !== ', ' else '] + clean := ['){', '=', ',', '{', '}', '(', ';', '+', '<', '-', '||', 'var', ':', '>=', '&&', + 'else if', '===', '!==', 'else'] + for line in lines { + mut trimmed := line.trim_space() + if trimmed.starts_with('//') || (trimmed.starts_with('/*') && trimmed.ends_with('*/')) { + continue + } + for i in 0 .. rules.len - 1 { + trimmed = trimmed.replace(rules[i], clean[i]) + } + js.write_string(trimmed) + } + js_str := js.str() + return js_str +} +*/ +fn write_toc(dn doc.DocNode, mut toc strings.Builder) { + mut toc_slug := if dn.name.len == 0 || dn.content.len == 0 { '' } else { slug(dn.name) } + if toc_slug == '' && dn.children.len > 0 { + if dn.children[0].name == '' { + toc_slug = slug(dn.name) + } else { + toc_slug = slug(dn.name + '.' + dn.children[0].name) + } + } + if is_module_readme(dn) { + if dn.comments.len == 0 || (dn.comments.len > 0 && dn.comments[0].text.len == 0) { + return + } + toc.write_string('
  • README') + } else if dn.name != 'Constants' { + toc.write_string('
  • $dn.kind $dn.name') + toc.writeln('
      ') + for child in dn.children { + cname := dn.name + '.' + child.name + toc.writeln('
    • $child.kind $child.name
    • ') + } + toc.writeln('
    ') + } else { + toc.write_string('
  • $dn.name') + } + toc.writeln('
  • ') +} diff --git a/v_windows/v/old/cmd/tools/vdoc/html_tag_escape_test.v b/v_windows/v/old/cmd/tools/vdoc/html_tag_escape_test.v new file mode 100644 index 0000000..64a7d1c --- /dev/null +++ b/v_windows/v/old/cmd/tools/vdoc/html_tag_escape_test.v @@ -0,0 +1,6 @@ +module main + +fn test_html_tag_escape() { + assert html_tag_escape('') == '<abc>' + assert html_tag_escape('``') == '``' +} diff --git a/v_windows/v/old/cmd/tools/vdoc/markdown.v b/v_windows/v/old/cmd/tools/vdoc/markdown.v new file mode 100644 index 0000000..cc24ad2 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vdoc/markdown.v @@ -0,0 +1,55 @@ +module main + +import strings +import v.doc + +fn markdown_escape_script_tags(str string) string { + return str.replace_each(['', '`']) +} + +fn (vd VDoc) gen_markdown(d doc.Doc, with_toc bool) string { + mut hw := strings.new_builder(200) + mut cw := strings.new_builder(200) + hw.writeln('# $d.head.content\n') + if d.head.comments.len > 0 { + comments := if vd.cfg.include_examples { + d.head.merge_comments() + } else { + d.head.merge_comments_without_examples() + } + hw.writeln('$comments\n') + } + if with_toc { + hw.writeln('## Contents') + } + vd.write_markdown_content(d.contents.arr(), mut cw, mut hw, 0, with_toc) + footer_text := gen_footer_text(d, !vd.cfg.no_timestamp) + cw.writeln('#### $footer_text') + return hw.str() + '\n' + cw.str() +} + +fn (vd VDoc) write_markdown_content(contents []doc.DocNode, mut cw strings.Builder, mut hw strings.Builder, indent int, with_toc bool) { + for cn in contents { + if with_toc && cn.name.len > 0 { + hw.writeln(' '.repeat(2 * indent) + '- [${slug(cn.name)}](#$cn.name)') + cw.writeln('## $cn.name') + } + if cn.content.len > 0 { + comments := cn.merge_comments_without_examples() + cw.writeln('```v\n$cn.content\n```\n$comments\n') + // Write examples if any found + examples := cn.examples() + if vd.cfg.include_examples && examples.len > 0 { + example_title := if examples.len > 1 { 'Examples' } else { 'Example' } + cw.writeln('$example_title\n```v\n') + for example in examples { + cw.writeln('$example\n') + } + cw.writeln('```\n') + } + cw.writeln(r'[[Return to contents]](#Contents)') + cw.writeln('') + } + vd.write_markdown_content(cn.children, mut cw, mut hw, indent + 1, with_toc) + } +} diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/arrow.svg b/v_windows/v/old/cmd/tools/vdoc/resources/arrow.svg new file mode 100644 index 0000000..2a0456f --- /dev/null +++ b/v_windows/v/old/cmd/tools/vdoc/resources/arrow.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/dark-mode.js b/v_windows/v/old/cmd/tools/vdoc/resources/dark-mode.js new file mode 100644 index 0000000..075dbb5 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vdoc/resources/dark-mode.js @@ -0,0 +1,6 @@ +(function() { + var html = document.getElementsByTagName('html')[0]; + if (localStorage.getItem('dark-mode') === 'true') { + html.classList.add('dark'); + } +})(); diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/dark.svg b/v_windows/v/old/cmd/tools/vdoc/resources/dark.svg new file mode 100644 index 0000000..2067d05 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vdoc/resources/dark.svg @@ -0,0 +1 @@ + diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/doc.css b/v_windows/v/old/cmd/tools/vdoc/resources/doc.css new file mode 100644 index 0000000..a63d357 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vdoc/resources/doc.css @@ -0,0 +1,725 @@ +:root { + --background-color: #fff; + --link-color: #2779bd; + --text-color: #000; + --ref-symbol-color: #dae1e7; + --ref-symbol-hover-color: #b8c2cc; + --title-bottom-line-color: #f1f5f8; + --footer-top-line-color: #f1f5f8; + --footer-text-color: #616161; + --code-signature-border-color: #a0aec0; + --menu-background-color: #4b6c88; + --menu-text-color: #fff; + --menu-indent-line-color: #3b3b3b66; + --menu-indent-line-active-color: #00000066; + --menu-scrollbar-color: #a0aec0; + --menu-toggle-icon-color: #fff; + --menu-toggle-icon-hover-color: #00000044; + --menu-search-background-color: #00000044; + --menu-search-font-color: #fff; + --menu-search-result-background-hover-color: #00000021; + --menu-search-separator-color: #00000044; + --menu-search-title-text-color: #d5efff; + --menu-search-badge-background-color: #00000044; + --menu-search-badge-background-hover-color: #0000004d; + --toc-text-color: #2779bd; + --toc-indicator-color: #4299e1; + --code-default-text-color: #5c6e74; + --code-background-color: #edf2f7; + --code-keyword-text-color: #2b6cb0; + --code-builtin-text-color: #0a0a0a; + --code-function-text-color: #319795; + --code-comment-text-color: #93a1a1; + --code-punctuation-text-color: #999999; + --code-symbol-text-color: #702459; + --code-operator-text-color: #a67f59; + --attribute-deprecated-background-color: #f59f0b48; + --attribute-deprecated-text-color: #92400e; + --attribute-text-color: #000000af; +} +:root.dark .dark-icon { + display: none; +} +:root:not(.dark) .light-icon { + display: none; +} + +.dark body { + --background-color: #1a202c; + --text-color: #fff; + --link-color: #90cdf4; + --ref-symbol-color: #2d3748; + --ref-symbol-hover-color: #4a5568; + --title-bottom-line-color: #2d3748; + --footer-top-line-color: #2d3748; + --footer-text-color: #bbd3e1; + --code-signature-border-color: #4a5568; + --menu-background-color: #2d3748; + --menu-text-color: #fff; + --menu-indent-line-color: #4a5568; + --menu-indent-line-active-color: #90cdf4; /*#4a5568*/ + --menu-scrollbar-color: #4a5568; + --menu-toggle-icon-color: #fff; + --menu-search-background-color: #4a5568; + --menu-search-font-color: #fff; + --menu-search-separator-color: #4a5568; + --menu-search-title-text-color: #90cdf4; + --menu-search-badge-background-color: #4a5568; + --menu-search-badge-background-hover-color: #4a5568; + --toc-text-color: #90cdf4; + --toc-indicator-color: #4299e1; + --code-default-text-color: #cbd5e0; + --code-background-color: #2d3748; + --code-builtin-text-color: #68d391; + --code-keyword-text-color: #63b3ed; + --code-function-text-color: #4fd1c5; + --code-comment-text-color: #a0aec0; + --code-punctuation-text-color: #a0aec0; + --code-symbol-text-color: #ed64a6; + --attribute-background-color: #ffffff20; + --attribute-text-color: #ffffffaf; + --attribute-deprecated-text-color: #fef3c7; +} +html { + height: 100%; +} +body { + margin: 0; + font-family: Roboto, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, + Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; + background-color: #fff; + background-color: var(--background-color); + color: #000; + color: var(--text-color); + height: 100%; +} +#page { + height: 100%; + padding-top: 56px; + box-sizing: border-box; + overflow: hidden; +} + +/** Reset for menus */ +.doc-nav ul, +.doc-toc ul { + list-style: none; + padding: 0; + margin: 0; +} + +/* Left nav */ +.doc-nav { + position: fixed; + width: 100%; + left: 0; + right: 0; + top: 0; + display: flex; + background-color: #4b6c88; + background-color: var(--menu-background-color); + color: #fff; + color: var(--menu-text-color); + flex-direction: column; + overflow-y: auto; + height: 100vh; + z-index: 10; + scrollbar-width: thin; + scrollbar-color: #a0aec0 transparent; + scrollbar-color: var(--menu-scrollbar-color) transparent; + font-family: "Work Sans", sans-serif; +} +*::-webkit-scrollbar { + width: 4px; + height: 4px; +} +*::-webkit-scrollbar-track { + background: transparent; +} +*::-webkit-scrollbar-thumb { + background-color: #a0aec0; + background-color: var(--menu-scrollbar-color); + border: 3px solid transparent; +} +.doc-nav .content li { + line-height: 1.8; +} +.doc-nav .content.show { + display: flex; +} +.doc-nav .content.hidden { + display: none; +} +.doc-nav #toggle-menu { + cursor: pointer; + padding: 0.3rem; + fill: #fff; + fill: var(--menu-toggle-icon-color); +} +.doc-nav > .heading-container { + position: relative; + /* IE11 */ + position: sticky; + position: -webkit-sticky; + top: 0; + background-color: #4b6c88; + background-color: var(--menu-background-color); + z-index: 10; +} +.doc-nav > .heading-container > .heading { + display: flex; + flex-direction: column; +} +.doc-nav > .heading-container > .heading > .info { + display: flex; + padding: 0 1rem; + height: 56px; +} +.doc-nav > .heading-container > .heading > .info > .module { + font-size: 1.6rem; + font-weight: 500; + margin: 0; +} +.doc-nav > .heading-container > .heading > .info > .toggle-version-container { + display: flex; + align-items: center; +} +.doc-nav + > .heading-container + > .heading + > .info + > .toggle-version-container + > #dark-mode-toggle { + cursor: pointer; + fill: #fff; + display: flex; + visibility: hidden; +} +.doc-nav + > .heading-container + > .heading + > .info + > .toggle-version-container + > #dark-mode-toggle + > svg { + width: 1.2rem; + height: 1.2rem; +} +.doc-nav > .heading-container > .heading > #search { + margin: 0.6rem 1.2rem 1rem 1.2rem; + border: none; + border-radius: 0.2rem; + padding: 0.5rem 1rem; + outline: none; + background-color: #00000044; + background-color: var(--menu-search-background-color); + color: #fff; + color: var(--menu-search-text-color); +} +.doc-nav > .heading-container > .heading > #search::placeholder { + color: #edf2f7; + text-transform: uppercase; + font-size: 12px; + font-weight: 600; +} +.doc-nav > .heading-container > .heading > #search:-ms-input-placeholder { + color: #edf2f7; + text-transform: uppercase; + font-size: 12px; + font-weight: 600; +} +.doc-nav > .content { + padding: 0 2rem 2rem 2rem; + display: flex; + flex-direction: column; +} +.doc-nav > .content > ul > li.active { + font-weight: 600; +} +.doc-nav > .content > ul > li.open ul { + display: initial; +} +.doc-nav > .content > ul > li.open > .menu-row > .dropdown-arrow { + transform: initial; +} +.doc-nav > .content > ul > li > .menu-row { + display: flex; + align-items: center; +} +.doc-nav > .content > ul > li > .menu-row > .dropdown-arrow { + transform: rotate(-90deg); + height: 18px; + width: 18px; + margin-left: calc(-18px - 0.3rem); + margin-right: 0.3rem; + cursor: pointer; + fill: #fff; + pointer-events: all; +} +.doc-nav > .content > ul > li > ul { + margin: 0.4rem 0; + display: none; +} +.doc-nav > .content > ul > li > ul > li { + border-color: #ffffff66; + border-color: var(--menu-indent-line-color); + border-left-width: 1.7px; + border-left-style: solid; + padding-left: 0.7rem; +} +.doc-nav > .content > ul > li > ul > li.active { + border-color: #00000066; + border-color: var(--menu-search-result-hover-background-color); +} +.doc-nav > .content a { + color: #fff; + color: var(--menu-text-color); + text-decoration: none; + user-select: none; +} +.doc-nav > .content a:hover { + text-decoration: underline; +} +.doc-nav .search.hidden { + display: none; +} +.doc-nav .search li { + line-height: 1.5; +} +.doc-nav > .search .result:hover { + background-color: #00000021; + background-color: var(--menu-search-result-background-hover-color); +} +.doc-nav > .search .result:hover > .link > .definition > .badge { + background-color: #0000004d; + background-color: var(--menu-search-badge-background-hover-color); +} +.doc-nav > .search .result > .link { + padding: 0.5rem 1.4rem; + text-decoration: none; + color: #fff; + color: var(--menu-text-color); + display: block; +} +.doc-nav > .search .result > .link > .definition { + display: flex; +} +.doc-nav > .search .result > .link > .definition > .title { + color: #90cdf4; + color: var(--menu-search-title-text-color); + font-size: 0.875rem; + font-weight: 500; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} +.doc-nav > .search .result > .link > .definition > .badge { + font-size: 0.75rem; + display: inline-flex; + padding: 0 0.5rem; + background-color: #00000044; + background-color: var(--menu-search-badge-background-color); + margin-left: auto; + align-items: center; + border-radius: 9999px; + font-weight: 500; +} +.doc-nav > .search .result > .link > .description { + font-family: Roboto, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, + Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; + font-size: 0.75rem; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + margin-top: 0.25rem; +} +.doc-nav > .search > hr.separator { + margin: 0.5rem 0; + border-color: #00000044; + border-color: var(--menu-search-separator-color); + box-sizing: content-box; + height: 0; + border-width: 0; + border-top-width: 1px; + border-style: solid; + overflow: visible; +} + +/* Main content */ +.doc-scrollview { + height: 100%; + overflow-y: scroll; +} +.doc-container { + display: flex; + flex-direction: column-reverse; +} +.doc-content { + display: flex; + flex-direction: column; + padding: 1rem; + overflow: hidden; +} +.doc-content img { + width: auto; + max-width: 100%; +} +.doc-content p { + line-height: 1.4; +} +.doc-content a { + color: #2779bd; + color: var(--link-color); +} +.doc-content > .doc-node { + padding: 5rem 0 2rem 0; + margin-top: -4rem; + overflow: hidden; + word-break: break-all; /* IE11 */ + word-break: break-word; +} +.doc-content > .doc-node.const:nth-child(2) { + padding-bottom: 0 !important; +} +.doc-content > .doc-node.const:not(:first-child) { + padding-top: 4rem; +} +.doc-content > .doc-node.const:not(:last-child) { + padding-bottom: 2rem; +} +.doc-content > .timestamp { + font-size: 0.8rem; + color: #b8c2cc; + color: var(--timestamp-color); +} +.doc-content > .doc-node > .title { + display: flex; + font-family: "Work Sans", sans-serif; + font-weight: 500; + padding: 0.3rem; + align-items: center; + margin-bottom: 1rem; + border-bottom: 1px solid #f1f5f8; + border-bottom: 1px solid var(--title-bottom-line-color); +} +.doc-content > .doc-node > .attributes { + margin-bottom: 0.6rem; +} +.doc-content > .doc-node > .attributes > .attribute { + display: inline-block; + border-radius: 100px; + padding: 0.3rem 0.6rem; + background-color: var(--code-background-color); + color: var(--attribute-text-color); + margin-right: 0.8rem; +} +.doc-content > .doc-node > .attributes > .attribute-deprecated { + background-color: var(--attribute-deprecated-background-color); + color: var(--attribute-deprecated-text-color); +} +.doc-content > .doc-node > .title > .link { + display: flex; + margin-left: auto; + fill: #dae1e7; + fill: var(--ref-symbol-color); +} +.doc-content > .doc-node > .title > .link:hover { + fill: #b8c2cc; + fill: var(--ref-symbol-hover-color); +} +.doc-content > .doc-node h1 { + font-size: 2rem; +} +.doc-content > .doc-node h2 { + font-size: 1.3rem; +} +.doc-content > .doc-node .signature { + border-color: #a0aec0; + border-color: var(--code-signature-border-color); + border-left-width: 3px; + border-left-style: solid; +} +.doc-content > .doc-node > ul > li .task-list-item-checkbox { + margin-right: 0.5rem; +} +.doc-content > .doc-node > .title h1, +.doc-content > .doc-node > .title h2, +.doc-content > .doc-node > .title h3, +.doc-content > .doc-node > .title h4, +.doc-content > .doc-node > .title h5, +.doc-content > .doc-node > .title h6 { + font-weight: 500; + margin: 0; +} +.doc-content > .doc-node > .title h1 a, +.doc-content > .doc-node > .title h2 a, +.doc-content > .doc-node > .title h3 a, +.doc-content > .doc-node > .title h4 a, +.doc-content > .doc-node > .title h5 a, +.doc-content > .doc-node > .title h6 a { + text-decoration: none; + color: #dae1e7; + color: var(--ref-symbol-color); +} +.doc-content > .doc-node > .title h1 a:hover, +.doc-content > .doc-node > .title h2 a:hover, +.doc-content > .doc-node > .title h3 a:hover, +.doc-content > .doc-node > .title h4 a:hover, +.doc-content > .doc-node > .title h5 a:hover, +.doc-content > .doc-node > .title h6 a:hover { + color: #b8c2cc; + color: var(--ref-symbol-hover-color); +} +.doc-content > .footer { + padding-top: 1rem; + margin-top: auto; + bottom: 1rem; + color: 616161; + color: var(--footer-text-color); + border-color: #f1f5f8; + border-color: var(--footer-top-line-color); + border-top-style: solid; + border-top-width: 1px; + font-size: 0.8rem; + font-weight: 500; +} + +/* Right menu */ +.doc-toc { + right: 0; + top: 0; + height: 100%; + overflow-y: auto; + padding: 1rem 1rem 0 1rem; + width: 100%; + box-sizing: border-box; + -ms-overflow-style: none; + scrollbar-width: none; + font-family: "Work Sans", sans-serif; +} +.doc-toc::-webkit-scrollbar { + display: none; +} +.doc-toc li { + line-height: 1.5; +} +.doc-toc a { + color: #2779bd; + color: var(--toc-text-color); + font-size: 0.9rem; + font-weight: 600; + overflow: hidden; + text-overflow: ellipsis; + display: block; + text-decoration: none; + border-left-width: 2px; + border-left-style: solid; + border-color: transparent; + padding-left: 0.4rem; +} +.doc-toc a:hover { + text-decoration: underline; +} +.doc-toc a.active { + border-color: #4299e1; + border-color: var(--toc-indicator-color); +} +.doc-toc li ul { + margin: 0.2rem 0 0.2rem; + font-size: 0.7rem; + list-style: none; +} +.doc-toc li ul a { + font-weight: 400; + padding-left: 0.8rem; +} + +/* Code highlight */ +pre, +code, +pre code { + color: #5c6e74; + color: var(--code-default-text-color); + font-size: 0.948em; + text-shadow: none; + font-family: monospace; + background-color: #edf2f7; + background-color: var(--code-background-color); + border-radius: 0.25rem; +} +pre code { + direction: ltr; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + line-height: 1.5; + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; + display: block; + overflow-x: auto; + padding: 1rem; +} +code { + padding: 0.2rem; + vertical-align: middle; +} +pre { + overflow: auto; + margin: 0; +} +.namespace { + opacity: 0.7; +} +.token.comment { + color: #93a1a1; + color: var(--code-comment-text-color); +} +.token.punctuation { + color: #999999; + color: var(--code-punctuation-text-color); +} +.token.number, +.token.symbol { + color: #702459; + color: var(--code-symbol-text-color); +} +.token.string, +.token.char, +.token.builtin { + color: #38a169; + color: var(--code-builtin-text-color); +} +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #a67f59; + color: var(--code-operator-text-color); + background: transparent; +} +.token.boolean, +.token.keyword { + color: #2b6cb0; + color: var(--code-keyword-text-color); +} +.token.function { + color: #319795; + color: var(--code-function-text-color); +} + +/* Medium screen and up */ +@media (min-width: 768px) { + *::-webkit-scrollbar { + width: 8px; + height: 8px; + } + *::-webkit-scrollbar-thumb { + border: 3px solid transparent; + } + .doc-container { + flex-direction: row; + } + .doc-content { + font-size: 0.95rem; + flex: 1; + padding: 0rem 2rem 1rem 2rem; + } + .doc-toc { + position: relative; + /* IE11 */ + position: sticky; + position: -webkit-sticky; + align-self: flex-start; + top: 56px; + height: auto; + height: 100vh; + min-width: 200px; + width: auto; + max-width: 300px; + } + .doc-toc > ul { + padding-bottom: 1rem; + } +} + +@media (max-width: 1023px) { + .doc-nav.hidden { + height: auto; + } + .doc-nav.hidden #search { + display: none; + } + .doc-nav .search.mobile-hidden { + display: none; + } + .doc-nav > .heading-container > .heading > .info { + align-items: center; + } + .doc-nav > .heading-container > .heading > .info > .toggle-version-container { + flex-grow: 1; + padding: 0 1rem; + justify-content: space-between; + } +} + +@media (min-width: 1024px) { + #page { + padding-top: 0; + } + .doc-nav { + width: 300px; + } + .doc-nav #toggle-menu { + display: none; + } + .doc-nav > .heading-container > .heading > .info { + height: auto; + padding: 1rem 2rem 0 2rem; + flex-direction: column-reverse; + justify-content: center; + } + .doc-nav > .heading-container > .heading > .info > .toggle-version-container { + align-items: center; + margin-bottom: 0.2rem; + display: flex; + flex-direction: row-reverse; + } + .doc-nav + > .heading-container + > .heading + > .info + > .toggle-version-container + > #dark-mode-toggle { + margin-right: auto; + } + .doc-nav .content.show, + .doc-nav .content.hidden { + display: flex; + } + .doc-content > .doc-node.const:nth-child(2) { + padding-bottom: 0 !important; + } + .doc-content > .doc-node.const:not(:first-child) { + padding-top: 0; + } + .doc-content > .doc-node.const:not(:last-child) { + padding-bottom: 1rem; + } + .doc-container { + margin-left: 300px; + } + .doc-node { + padding-top: 1rem !important; + margin-top: 0 !important; + } + .doc-toc { + top: 0; + } +} diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/doc.js b/v_windows/v/old/cmd/tools/vdoc/resources/doc.js new file mode 100644 index 0000000..c355d7e --- /dev/null +++ b/v_windows/v/old/cmd/tools/vdoc/resources/doc.js @@ -0,0 +1,235 @@ +(function () { + if (document.body.scrollIntoView) { + var docnav = document.querySelector('.doc-nav'); + var active = docnav.querySelector('li.active'); + if (active) { + active.scrollIntoView({ block: 'center', inline: 'nearest' }); + } + } + setupScrollSpy(); + setupMobileToggle(); + setupDarkMode(); + setupSearch(); + setupCollapse(); +})(); + +function setupScrollSpy() { + var sectionPositions = []; + var sections = document.querySelectorAll('section'); + sections.forEach(function (section) { + sectionPositions.push(section.offsetTop); + }); + var scrollPos = 0; + window.addEventListener('scroll', function (_) { + // Reset classes + document.querySelectorAll('.doc-toc a[class="active"]').forEach(function (link) { + link.classList.remove('active'); + }); + // Set current menu link as active + var scrollPosition = document.documentElement.scrollTop || document.body.scrollTop; + for (var i = 0; i < sectionPositions.length; i++) { + var section = sections[i]; + var position = sectionPositions[i]; + if (position >= scrollPosition) { + var link = document.querySelector('.doc-toc a[href="#' + section.id + '"]'); + if (link) { + link.classList.add('active'); + var docToc = document.querySelector('.doc-toc'); + var tocHeight = docToc.clientHeight; + var scrollTop = docToc.scrollTop; + if ((document.body.getBoundingClientRect()).top < scrollPos && scrollTop < link.offsetTop - 10) { + docToc.scrollTop = link.clientHeight + link.offsetTop - tocHeight + 10; + } else if (scrollTop > link.offsetTop - 10) { + docToc.scrollTop = link.offsetTop - 10; + } + } + break; + } + } + scrollPos = (document.body.getBoundingClientRect()).top; + }); +} + +function setupMobileToggle() { + var toggle = document.getElementById('toggle-menu'); + toggle.addEventListener('click', function (_) { + var docNav = document.querySelector('.doc-nav'); + var isHidden = docNav.classList.contains('hidden'); + docNav.classList.toggle('hidden'); + var search = document.querySelector('.doc-nav > .search'); + console.log(search); + var searchHasResults = search.classList.contains('has-results'); + if (isHidden && searchHasResults) { + search.classList.remove('mobile-hidden'); + } else { + search.classList.add('mobile-hidden'); + } + var content = document.querySelector('.doc-nav .content'); + content.classList.toggle('hidden'); + content.classList.toggle('show'); + }); +} + +function setupDarkMode() { + var html = document.getElementsByTagName('html')[0]; + var darkModeToggle = document.getElementById('dark-mode-toggle'); + darkModeToggle.addEventListener('click', function () { + html.classList.toggle('dark'); + var isDarkModeEnabled = html.classList.contains('dark'); + localStorage.setItem('dark-mode', isDarkModeEnabled); + darkModeToggle.setAttribute('aria-checked', isDarkModeEnabled) + }); + // Check if css var() is supported and enable dark mode toggle + if (window.CSS && CSS.supports('color', 'var(--fake-var)')) { + darkModeToggle.style.visibility = 'unset'; + } +} + +function setupSearch() { + var searchInput = document.getElementById('search'); + var onInputChange = debounce(function (e) { + var searchValue = e.target.value.toLowerCase(); + var menu = document.querySelector('.doc-nav > .content'); + var search = document.querySelector('.doc-nav > .search'); + if (searchValue === '') { + // reset to default + menu.style.display = ''; + if (!search.classList.contains('hidden')) { + search.classList.add('hidden'); + search.classList.remove('has-results'); + } + } else if (searchValue.length >= 2) { + // search for less than 2 characters can display too much results + search.innerHTML = ''; + menu.style.display = 'none'; + if (search.classList.contains('hidden')) { + search.classList.remove('hidden'); + search.classList.remove('mobile-hidden'); + search.classList.add('has-results'); + } + // cache length for performance + var foundModule = false; + var searchModuleIndexLength = searchModuleIndex.length; + var ul = document.createElement('ul'); + search.appendChild(ul); + for (var i = 0; i < searchModuleIndexLength; i++) { + // no toLowerCase needed because modules are always lowercase + var title = searchModuleIndex[i]; + if (title.indexOf(searchValue) === -1) { + continue + } + foundModule = true; + // [description, link] + var data = searchModuleData[i]; + var description = data[0]; + var link = data[1]; + var el = createSearchResult({ + link: link, + title: title, + description: description, + badge: 'module', + }); + ul.appendChild(el); + } + if (foundModule) { + var hr = document.createElement('hr'); + hr.classList.add('separator'); + search.appendChild(hr); + } + var searchIndexLength = searchIndex.length; + var results = []; + for (var i = 0; i < searchIndexLength; i++) { + var title = searchIndex[i]; + if (title.toLowerCase().indexOf(searchValue) === -1) { + continue + } + // [badge, description, link] + var data = searchData[i]; + var badge = data[0]; + var description = data[1]; + var link = data[2]; + var prefix = data[3]; + results.push({ + badge: badge, + description: description, + link: link, + title: prefix + ' ' + title, + }); + } + results.sort(function (a, b) { + if (a.title < b.title) { + return -1; + } + if (a.title > b.title) { + return 1; + } + return 0; + }); + var ul = document.createElement('ul'); + search.appendChild(ul); + for (var i = 0; i < results.length; i++) { + var result = results[i]; + var el = createSearchResult(result); + ul.appendChild(el); + } + } + }); + searchInput.addEventListener('input', onInputChange); +} + +function createSearchResult(data) { + var li = document.createElement('li'); + li.classList.add('result'); + var a = document.createElement('a'); + a.href = data.link; + a.classList.add('link'); + li.appendChild(a); + var defintion = document.createElement('div'); + defintion.classList.add('definition'); + a.appendChild(defintion); + if (data.description) { + var description = document.createElement('div'); + description.classList.add('description'); + description.textContent = data.description; + a.appendChild(description); + } + var title = document.createElement('span'); + title.classList.add('title'); + title.textContent = data.title; + defintion.appendChild(title); + var badge = document.createElement('badge'); + badge.classList.add('badge'); + badge.textContent = data.badge; + defintion.appendChild(badge); + return li; +} + +function setupCollapse() { + var dropdownArrows = document.querySelectorAll('.dropdown-arrow'); + for (var i = 0; i < dropdownArrows.length; i++) { + var dropdownArrow = dropdownArrows[i]; + dropdownArrow.addEventListener('click', function (e) { + var parent = e.target.parentElement.parentElement.parentElement; + parent.classList.toggle('open'); + }); + } +} + +function debounce(func, timeout) { + var timer; + return (...args) => { + const next = () => func(...args); + if (timer) { + clearTimeout(timer); + } + timer = setTimeout(next, timeout > 0 ? timeout : 300); + } +} + +document.addEventListener('keypress', (ev) => { + if (ev.key == '/') { + let search = document.getElementById('search'); + ev.preventDefault(); + search.focus(); + } +}); diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/favicons/android-chrome-192x192.png b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/android-chrome-192x192.png new file mode 100644 index 0000000..a674500 Binary files /dev/null and b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/android-chrome-192x192.png differ diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/favicons/android-chrome-512x512.png b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/android-chrome-512x512.png new file mode 100644 index 0000000..fe7294e Binary files /dev/null and b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/android-chrome-512x512.png differ diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/favicons/apple-touch-icon.png b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/apple-touch-icon.png new file mode 100644 index 0000000..d2bedd5 Binary files /dev/null and b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/apple-touch-icon.png differ diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/favicons/browserconfig.xml b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/browserconfig.xml new file mode 100644 index 0000000..b3930d0 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/browserconfig.xml @@ -0,0 +1,9 @@ + + + + + + #da532c + + + diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/favicons/favicon-16x16.png b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/favicon-16x16.png new file mode 100644 index 0000000..ed11964 Binary files /dev/null and b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/favicon-16x16.png differ diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/favicons/favicon-32x32.png b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/favicon-32x32.png new file mode 100644 index 0000000..083808f Binary files /dev/null and b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/favicon-32x32.png differ diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/favicons/favicon.ico b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/favicon.ico new file mode 100644 index 0000000..5123c5e Binary files /dev/null and b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/favicon.ico differ diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-144x144.png b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-144x144.png new file mode 100644 index 0000000..f34f872 Binary files /dev/null and b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-144x144.png differ diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-150x150.png b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-150x150.png new file mode 100644 index 0000000..d511595 Binary files /dev/null and b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-150x150.png differ diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-310x150.png b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-310x150.png new file mode 100644 index 0000000..ec8e25f Binary files /dev/null and b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-310x150.png differ diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-310x310.png b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-310x310.png new file mode 100644 index 0000000..8b98e30 Binary files /dev/null and b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-310x310.png differ diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-70x70.png b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-70x70.png new file mode 100644 index 0000000..4740338 Binary files /dev/null and b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/mstile-70x70.png differ diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/favicons/safari-pinned-tab.svg b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/safari-pinned-tab.svg new file mode 100644 index 0000000..8580c38 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/safari-pinned-tab.svg @@ -0,0 +1,39 @@ + + + + +Created by potrace 1.11, written by Peter Selinger 2001-2013 + + + + + diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/favicons/site.webmanifest b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/site.webmanifest new file mode 100644 index 0000000..b20abb7 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vdoc/resources/favicons/site.webmanifest @@ -0,0 +1,19 @@ +{ + "name": "", + "short_name": "", + "icons": [ + { + "src": "/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/light.svg b/v_windows/v/old/cmd/tools/vdoc/resources/light.svg new file mode 100644 index 0000000..21606c9 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vdoc/resources/light.svg @@ -0,0 +1 @@ + diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/link.svg b/v_windows/v/old/cmd/tools/vdoc/resources/link.svg new file mode 100644 index 0000000..60d93bb --- /dev/null +++ b/v_windows/v/old/cmd/tools/vdoc/resources/link.svg @@ -0,0 +1 @@ + diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/menu.svg b/v_windows/v/old/cmd/tools/vdoc/resources/menu.svg new file mode 100644 index 0000000..c069b00 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vdoc/resources/menu.svg @@ -0,0 +1 @@ + diff --git a/v_windows/v/old/cmd/tools/vdoc/resources/normalize.css b/v_windows/v/old/cmd/tools/vdoc/resources/normalize.css new file mode 100644 index 0000000..d9fbe67 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vdoc/resources/normalize.css @@ -0,0 +1,171 @@ +/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */ +html { + line-height: 1.15; + -webkit-text-size-adjust: 100%; +} + +main { + display: block; +} + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +hr { + box-sizing: content-box; + height: 0; + overflow: visible; +} + +pre { + font-family: monospace, monospace; + font-size: 1em; +} + +a { + background-color: transparent; +} + +abbr[title] { + border-bottom: none; + text-decoration: underline; + text-decoration: underline dotted; +} + +b, +strong { + font-weight: bolder; +} + +kbd, +samp { + font-family: monospace, monospace; + font-size: 1em; +} + +small { + font-size: 80%; +} + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +img { + border-style: none; +} + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + font-size: 100%; + line-height: 1.15; + margin: 0; +} + +button, +input { + overflow: visible; +} + +button, +select { + text-transform: none; +} + +button, +[type="button"], +[type="reset"], +[type="submit"] { + -webkit-appearance: button; +} + +button::-moz-focus-inner, +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0; +} + +button:-moz-focusring, +[type="button"]:-moz-focusring, +[type="reset"]:-moz-focusring, +[type="submit"]:-moz-focusring { + outline: 1px dotted ButtonText; +} + +fieldset { + padding: 0.35em 0.75em 0.625em; +} + +legend { + box-sizing: border-box; + color: inherit; + display: table; + max-width: 100%; + padding: 0; + white-space: normal; +} + +progress { + vertical-align: baseline; +} + +textarea { + overflow: auto; +} + +[type="checkbox"], +[type="radio"] { + box-sizing: border-box; + padding: 0; +} + +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +[type="search"] { + -webkit-appearance: textfield; + outline-offset: -2px; +} + +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +::-webkit-file-upload-button { + -webkit-appearance: button; + font: inherit; +} + +summary { + display: list-item; +} + +template { + display: none; +} + +[hidden] { + display: none; +} diff --git a/v_windows/v/old/cmd/tools/vdoc/tests/testdata/project1/main.out b/v_windows/v/old/cmd/tools/vdoc/tests/testdata/project1/main.out new file mode 100644 index 0000000..41b75a2 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vdoc/tests/testdata/project1/main.out @@ -0,0 +1 @@ +vdoc: No documentation found for /v/vmaster/cmd/tools/vdoc/tests/testdata/project1/main.v diff --git a/v_windows/v/old/cmd/tools/vdoc/tests/testdata/project1/main.v b/v_windows/v/old/cmd/tools/vdoc/tests/testdata/project1/main.v new file mode 100644 index 0000000..1a1b527 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vdoc/tests/testdata/project1/main.v @@ -0,0 +1,8 @@ +const ( + source_root = 'temp' +) + +// funky - comment for function below +fn funky() { + println('hi') +} diff --git a/v_windows/v/old/cmd/tools/vdoc/tests/vdoc_file_test.v b/v_windows/v/old/cmd/tools/vdoc/tests/vdoc_file_test.v new file mode 100644 index 0000000..49a0130 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vdoc/tests/vdoc_file_test.v @@ -0,0 +1,72 @@ +import os +import rand +import term +import v.util.vtest +import v.util.diff + +const vexe = @VEXE + +const vroot = @VMODROOT + +const diff_cmd = find_diff_cmd() + +fn find_diff_cmd() string { + return diff.find_working_diff_command() or { '' } +} + +fn test_vet() { + os.setenv('VCOLORS', 'never', true) + os.chdir(vroot) + test_dir := 'cmd/tools/vdoc/tests/testdata' + main_files := get_main_files_in_dir(test_dir) + fails := check_path(vexe, test_dir, main_files) + assert fails == 0 +} + +fn get_main_files_in_dir(dir string) []string { + mut mfiles := os.walk_ext(dir, '.v') + mfiles.sort() + return mfiles +} + +fn check_path(vexe string, dir string, tests []string) int { + mut nb_fail := 0 + paths := vtest.filter_vtest_only(tests, basepath: vroot) + for path in paths { + program := path + print(path + ' ') + res := os.execute('$vexe doc $program') + if res.exit_code < 0 { + panic(res.output) + } + mut expected := os.read_file(program.replace('main.v', 'main.out')) or { panic(err) } + expected = clean_line_endings(expected) + found := clean_line_endings(res.output) + if expected != found { + println(term.red('FAIL')) + println('============') + println('expected:') + println(expected) + println('============') + println('found:') + println(found) + println('============\n') + println('diff:') + println(diff.color_compare_strings(diff_cmd, rand.ulid(), found, expected)) + println('============\n') + nb_fail++ + } else { + println(term.green('OK')) + } + } + return nb_fail +} + +fn clean_line_endings(s string) string { + mut res := s.trim_space() + res = res.replace(' \n', '\n') + res = res.replace(' \r\n', '\n') + res = res.replace('\r\n', '\n') + res = res.trim('\n') + return res +} diff --git a/v_windows/v/old/cmd/tools/vdoc/utils.v b/v_windows/v/old/cmd/tools/vdoc/utils.v new file mode 100644 index 0000000..4b07373 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vdoc/utils.v @@ -0,0 +1,275 @@ +module main + +import os +import v.doc +import term +import v.ast +import v.scanner +import v.token +import strings +import v.pref + +[inline] +fn slug(title string) string { + return title.replace(' ', '-') +} + +fn escape(str string) string { + return str.replace_each(['"', '\\"', '\r\n', '\\n', '\n', '\\n', '\t', '\\t']) +} + +fn get_sym_name(dn doc.DocNode) string { + sym_name := if dn.parent_name.len > 0 && dn.parent_name != 'void' { + '($dn.parent_name) $dn.name' + } else { + dn.name + } + return sym_name +} + +fn get_node_id(dn doc.DocNode) string { + tag := if dn.parent_name.len > 0 && dn.parent_name != 'void' { + '${dn.parent_name}.$dn.name' + } else { + dn.name + } + return slug(tag) +} + +fn is_module_readme(dn doc.DocNode) bool { + if dn.comments.len > 0 && dn.content == 'module $dn.name' { + return true + } + return false +} + +fn trim_doc_node_description(description string) string { + mut dn_description := description.replace_each(['\r\n', '\n', '"', '\\"']) + // 80 is enough to fill one line + if dn_description.len > 80 { + dn_description = dn_description[..80] + } + if dn_description.contains('\n') { + dn_description = dn_description.split('\n')[0] + } + // if \ is last character, it ends with \" which leads to a JS error + if dn_description.ends_with('\\') { + dn_description = dn_description.trim_right('\\') + } + return dn_description +} + +fn set_output_type_from_str(format string) OutputType { + output_type := match format { + 'htm', 'html' { OutputType.html } + 'md', 'markdown' { OutputType.markdown } + 'json' { OutputType.json } + 'stdout' { OutputType.stdout } + else { OutputType.plaintext } + } + return output_type +} + +fn get_ignore_paths(path string) ?[]string { + ignore_file_path := os.join_path(path, '.vdocignore') + ignore_content := os.read_file(ignore_file_path) or { + return error_with_code('ignore file not found.', 1) + } + mut res := []string{} + if ignore_content.trim_space().len > 0 { + rules := ignore_content.split_into_lines().map(it.trim_space()) + mut final := []string{} + for rule in rules { + if rule.contains('*.') || rule.contains('**') { + println('vdoc: Wildcards in ignore rules are not allowed for now.') + continue + } + final << rule + } + res = final.map(os.join_path(path, it.trim_right('/'))) + } else { + mut dirs := os.ls(path) or { return []string{} } + res = dirs.map(os.join_path(path, it)).filter(os.is_dir(it)) + } + return res.map(it.replace('/', os.path_separator)) +} + +fn is_included(path string, ignore_paths []string) bool { + if path.len == 0 { + return true + } + for ignore_path in ignore_paths { + if !path.contains(ignore_path) { + continue + } + return false + } + return true +} + +fn get_modules_list(path string, ignore_paths2 []string) []string { + files := os.ls(path) or { return []string{} } + mut ignore_paths := get_ignore_paths(path) or { []string{} } + ignore_paths << ignore_paths2 + mut dirs := []string{} + for file in files { + fpath := os.join_path(path, file) + if os.is_dir(fpath) && is_included(fpath, ignore_paths) && !os.is_link(path) { + dirs << get_modules_list(fpath, ignore_paths.filter(it.starts_with(fpath))) + } else if fpath.ends_with('.v') && !fpath.ends_with('_test.v') { + if path in dirs { + continue + } + dirs << path + } + } + dirs.sort() + return dirs +} + +fn gen_footer_text(d &doc.Doc, include_timestamp bool) string { + footer_text := 'Powered by vdoc.' + if !include_timestamp { + return footer_text + } + generated_time := d.time_generated + time_str := '$generated_time.day $generated_time.smonth() $generated_time.year $generated_time.hhmmss()' + return '$footer_text Generated on: $time_str' +} + +fn color_highlight(code string, tb &ast.Table) string { + builtin := ['bool', 'string', 'i8', 'i16', 'int', 'i64', 'i128', 'byte', 'u16', 'u32', 'u64', + 'u128', 'rune', 'f32', 'f64', 'int_literal', 'float_literal', 'byteptr', 'voidptr', 'any'] + highlight_code := fn (tok token.Token, typ HighlightTokenTyp) string { + mut lit := '' + match typ { + .unone, .operator, .punctuation { + lit = tok.kind.str() + } + .string { + use_double_quote := tok.lit.contains("'") && !tok.lit.contains('"') + unescaped_val := tok.lit.replace('\\\\', '\x01').replace_each(["\\'", "'", '\\"', + '"', + ]) + if use_double_quote { + s := unescaped_val.replace_each(['\x01', '\\\\', '"', '\\"']) + lit = term.yellow('"$s"') + } else { + s := unescaped_val.replace_each(['\x01', '\\\\', "'", "\\'"]) + lit = term.yellow("'$s'") + } + } + .char { + lit = term.yellow('`$tok.lit`') + } + .keyword { + lit = term.bright_blue(tok.lit) + } + .builtin, .symbol { + lit = term.green(tok.lit) + } + .function { + lit = term.cyan(tok.lit) + } + .number, .module_ { + lit = term.bright_blue(tok.lit) + } + .boolean { + lit = term.bright_magenta(tok.lit) + } + .none_ { + lit = term.red(tok.lit) + } + .prefix { + lit = term.magenta(tok.lit) + } + else { + lit = tok.lit + } + } + return lit + } + mut s := scanner.new_scanner(code, .parse_comments, &pref.Preferences{ is_fmt: true }) + mut prev_prev := token.Token{} + mut prev := token.Token{} + mut tok := s.scan() + mut next_tok := s.scan() + mut buf := strings.new_builder(200) + mut i := 0 + for i < code.len { + if i == tok.pos { + mut tok_typ := HighlightTokenTyp.unone + match tok.kind { + .name { + if (tok.lit in builtin || tb.known_type(tok.lit)) + && (next_tok.kind != .lpar || prev.kind !in [.key_fn, .rpar]) { + tok_typ = .builtin + } else if + next_tok.kind in [.lcbr, .rpar, .eof, .comma, .pipe, .name, .rcbr, .assign, .key_pub, .key_mut, .pipe, .comma] + && prev.kind in [.name, .amp, .rsbr, .key_type, .assign, .dot, .question, .rpar, .key_struct, .key_enum, .pipe, .key_interface] + && (tok.lit[0].ascii_str().is_upper() || prev_prev.lit in ['C', 'JS']) { + tok_typ = .symbol + } else if next_tok.kind in [.lpar, .lt] { + tok_typ = .function + } else if next_tok.kind == .dot { + if tok.lit in ['C', 'JS'] { + tok_typ = .prefix + } else { + if tok.lit[0].ascii_str().is_upper() { + tok_typ = .symbol + } else { + tok_typ = .module_ + } + } + } else if tok.lit in ['r', 'c'] && next_tok.kind == .string { + tok_typ = .prefix + } else { + tok_typ = .name + } + } + .comment { + tok_typ = .comment + } + .chartoken { + tok_typ = .char + } + .string { + tok_typ = .string + } + .number { + tok_typ = .number + } + .key_true, .key_false { + tok_typ = .boolean + } + .lpar, .lcbr, .rpar, .rcbr, .lsbr, .rsbr, .semicolon, .colon, .comma, .dot { + tok_typ = .punctuation + } + .key_none { + tok_typ = .none_ + } + else { + if token.is_key(tok.lit) || token.is_decl(tok.kind) { + tok_typ = .keyword + } else if tok.kind == .decl_assign || tok.kind.is_assign() || tok.is_unary() + || tok.kind.is_relational() || tok.kind.is_infix() { + tok_typ = .operator + } + } + } + buf.write_string(highlight_code(tok, tok_typ)) + if prev_prev.kind == .eof || prev.kind == .eof || next_tok.kind == .eof { + break + } + prev_prev = prev + prev = tok + i = tok.pos + tok.len + tok = next_tok + next_tok = s.scan() + } else { + buf.write_b(code[i]) + i++ + } + } + return buf.str() +} diff --git a/v_windows/v/old/cmd/tools/vdoc/vdoc.v b/v_windows/v/old/cmd/tools/vdoc/vdoc.v new file mode 100644 index 0000000..f4f0264 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vdoc/vdoc.v @@ -0,0 +1,511 @@ +module main + +import markdown +import os +import os.cmdline +import time +import strings +import sync +import runtime +import v.doc +import v.pref +import v.vmod +import json +import term + +const ( + allowed_formats = ['md', 'markdown', 'json', 'text', 'stdout', 'html', 'htm'] + vexe = pref.vexe_path() + vroot = os.dir(vexe) + tabs = ['\t\t', '\t\t\t\t\t\t', '\t\t\t\t\t\t\t'] +) + +enum OutputType { + unset + html + markdown + json + plaintext + stdout +} + +struct VDoc { + cfg Config [required] +mut: + docs []doc.Doc + assets map[string]string + manifest vmod.Manifest + search_index []string + search_data []SearchResult + search_module_index []string // search results are split into a module part and the rest + search_module_data []SearchModuleResult +} + +struct Config { +mut: + pub_only bool = true + show_loc bool // for plaintext + is_color bool + is_multi bool + is_vlib bool + is_verbose bool + include_readme bool + include_examples bool = true + inline_assets bool + no_timestamp bool + output_path string + output_type OutputType = .unset + input_path string + symbol_name string + platform doc.Platform +} + +// +struct Output { +mut: + path string + typ OutputType = .unset +} + +struct ParallelDoc { + d doc.Doc + out Output +} + +fn (vd VDoc) gen_json(d doc.Doc) string { + cfg := vd.cfg + mut jw := strings.new_builder(200) + comments := if cfg.include_examples { + d.head.merge_comments() + } else { + d.head.merge_comments_without_examples() + } + jw.write_string('{"module_name":"$d.head.name","description":"${escape(comments)}","contents":') + jw.write_string(json.encode(d.contents.keys().map(d.contents[it]))) + jw.write_string(',"generator":"vdoc","time_generated":"$d.time_generated.str()"}') + return jw.str() +} + +fn (vd VDoc) gen_plaintext(d doc.Doc) string { + cfg := vd.cfg + mut pw := strings.new_builder(200) + if cfg.is_color { + content_arr := d.head.content.split(' ') + pw.writeln('${term.bright_blue(content_arr[0])} ${term.green(content_arr[1])}\n') + } else { + pw.writeln('$d.head.content\n') + } + comments := if cfg.include_examples { + d.head.merge_comments() + } else { + d.head.merge_comments_without_examples() + } + if comments.trim_space().len > 0 && !cfg.pub_only { + pw.writeln(comments.split_into_lines().map(' ' + it).join('\n')) + } + vd.write_plaintext_content(d.contents.arr(), mut pw) + return pw.str() +} + +fn (vd VDoc) write_plaintext_content(contents []doc.DocNode, mut pw strings.Builder) { + cfg := vd.cfg + for cn in contents { + if cn.content.len > 0 { + if cfg.is_color { + pw.writeln(color_highlight(cn.content, vd.docs[0].table)) + } else { + pw.writeln(cn.content) + } + if cn.comments.len > 0 && !cfg.pub_only { + comments := if cfg.include_examples { + cn.merge_comments() + } else { + cn.merge_comments_without_examples() + } + pw.writeln(comments.trim_space().split_into_lines().map(' ' + it).join('\n')) + } + if cfg.show_loc { + pw.writeln('Location: $cn.file_path:${cn.pos.line_nr + 1}\n') + } + } + vd.write_plaintext_content(cn.children, mut pw) + } +} + +fn (vd VDoc) render_doc(d doc.Doc, out Output) (string, string) { + name := vd.get_file_name(d.head.name, out) + output := match out.typ { + .html { vd.gen_html(d) } + .markdown { vd.gen_markdown(d, true) } + .json { vd.gen_json(d) } + else { vd.gen_plaintext(d) } + } + return name, output +} + +// get_file_name returns the final file name from a module name +fn (vd VDoc) get_file_name(mod string, out Output) string { + cfg := vd.cfg + mut name := mod + // since builtin is generated first, ignore it + if (cfg.is_vlib && mod == 'builtin' && !cfg.include_readme) || mod == 'README' { + name = 'index' + } else if !cfg.is_multi && !os.is_dir(out.path) { + name = os.file_name(out.path) + } + name = name + match out.typ { + .html { '.html' } + .markdown { '.md' } + .json { '.json' } + else { '.txt' } + } + return name +} + +fn (vd VDoc) work_processor(mut work sync.Channel, mut wg sync.WaitGroup) { + for { + mut pdoc := ParallelDoc{} + if !work.pop(&pdoc) { + break + } + file_name, content := vd.render_doc(pdoc.d, pdoc.out) + output_path := os.join_path(pdoc.out.path, file_name) + println('Generating $pdoc.out.typ in "$output_path"') + os.write_file(output_path, content) or { panic(err) } + } + wg.done() +} + +fn (vd VDoc) render_parallel(out Output) { + vjobs := runtime.nr_jobs() + mut work := sync.new_channel(u32(vd.docs.len)) + mut wg := sync.new_waitgroup() + for i in 0 .. vd.docs.len { + p_doc := ParallelDoc{vd.docs[i], out} + work.push(&p_doc) + } + work.close() + wg.add(vjobs) + for _ in 0 .. vjobs { + go vd.work_processor(mut work, mut wg) + } + wg.wait() +} + +fn (vd VDoc) render(out Output) map[string]string { + mut docs := map[string]string{} + for doc in vd.docs { + name, output := vd.render_doc(doc, out) + docs[name] = output.trim_space() + } + vd.vprintln('Rendered: ' + docs.keys().str()) + return docs +} + +fn (vd VDoc) get_readme(path string) string { + mut fname := '' + for name in ['readme', 'README'] { + if os.exists(os.join_path(path, '${name}.md')) { + fname = name + break + } + } + if fname == '' { + return '' + } + readme_path := os.join_path(path, '${fname}.md') + vd.vprintln('Reading README file from $readme_path') + readme_contents := os.read_file(readme_path) or { '' } + return readme_contents +} + +fn (vd VDoc) emit_generate_err(err IError) { + cfg := vd.cfg + mut err_msg := err.msg + if err.code == 1 { + mod_list := get_modules_list(cfg.input_path, []string{}) + println('Available modules:\n==================') + for mod in mod_list { + println(mod.all_after('vlib/').all_after('modules/').replace('/', '.')) + } + err_msg += ' Use the `-m` flag when generating docs from a directory that has multiple modules.' + } + eprintln(err_msg) +} + +fn (mut vd VDoc) generate_docs_from_file() { + cfg := vd.cfg + mut out := Output{ + path: cfg.output_path + typ: cfg.output_type + } + if out.path.len == 0 { + if cfg.output_type == .unset { + out.typ = .stdout + } else { + vd.vprintln('No output path has detected. Using input path instead.') + out.path = cfg.input_path + } + } else if out.typ == .unset { + vd.vprintln('Output path detected. Identifying output type..') + ext := os.file_ext(out.path) + out.typ = set_output_type_from_str(ext.all_after('.')) + } + if cfg.include_readme && out.typ !in [.html, .stdout] { + eprintln('vdoc: Including README.md for doc generation is supported on HTML output, or when running directly in the terminal.') + exit(1) + } + dir_path := if cfg.is_vlib { + vroot + } else if os.is_dir(cfg.input_path) { + cfg.input_path + } else { + os.dir(cfg.input_path) + } + manifest_path := os.join_path(dir_path, 'v.mod') + if os.exists(manifest_path) { + vd.vprintln('Reading v.mod info from $manifest_path') + if manifest := vmod.from_file(manifest_path) { + vd.manifest = manifest + } + } + if cfg.include_readme { + readme_contents := vd.get_readme(dir_path) + comment := doc.DocComment{ + text: readme_contents + } + if out.typ == .stdout { + println(markdown.to_plain(readme_contents)) + } else if out.typ == .html && cfg.is_multi { + vd.docs << doc.Doc{ + head: doc.DocNode{ + name: 'README' + comments: [comment] + } + time_generated: time.now() + } + } + } + dirs := if cfg.is_multi { + get_modules_list(cfg.input_path, []string{}) + } else { + [cfg.input_path] + } + for dirpath in dirs { + vd.vprintln('Generating $out.typ docs for "$dirpath"') + mut dcs := doc.generate(dirpath, cfg.pub_only, true, cfg.platform, cfg.symbol_name) or { + vd.emit_generate_err(err) + exit(1) + } + if dcs.contents.len == 0 { + continue + } + if cfg.is_multi || (!cfg.is_multi && cfg.include_readme) { + readme_contents := vd.get_readme(dirpath) + comment := doc.DocComment{ + text: readme_contents + } + dcs.head.comments = [comment] + } + if cfg.pub_only { + for name, dc in dcs.contents { + dcs.contents[name].content = dc.content.all_after('pub ') + for i, cc in dc.children { + dcs.contents[name].children[i].content = cc.content.all_after('pub ') + } + } + } + vd.docs << dcs + } + // Important. Let builtin be in the top of the module list + // if we are generating docs for vlib. + if cfg.is_vlib { + mut docs := vd.docs.filter(it.head.name == 'builtin') + docs << vd.docs.filter(it.head.name != 'builtin') + vd.docs = docs + } + vd.vprintln('Rendering docs...') + if out.path.len == 0 || out.path == 'stdout' { + if out.typ == .html { + vd.render_static_html(out) + } + outputs := vd.render(out) + if outputs.len == 0 { + eprintln('vdoc: No documentation found for ${dirs[0]}') + exit(1) + } else { + first := outputs.keys()[0] + println(outputs[first]) + } + } else { + if !os.exists(out.path) { + os.mkdir_all(out.path) or { panic(err) } + } else if !os.is_dir(out.path) { + out.path = os.real_path('.') + } + if cfg.is_multi { + out.path = os.join_path(out.path, '_docs') + if !os.exists(out.path) { + os.mkdir(out.path) or { panic(err) } + } else { + for fname in css_js_assets { + existing_asset_path := os.join_path(out.path, fname) + if os.exists(existing_asset_path) { + os.rm(existing_asset_path) or { panic(err) } + } + } + } + } + if out.typ == .html { + vd.render_static_html(out) + } + vd.render_parallel(out) + if out.typ == .html { + println('Creating search index...') + vd.collect_search_index(out) + vd.render_search_index(out) + // move favicons to target directory + println('Copying favicons...') + favicons := os.ls(favicons_path) or { panic(err) } + for favicon in favicons { + favicon_path := os.join_path(favicons_path, favicon) + destination_path := os.join_path(out.path, favicon) + os.cp(favicon_path, destination_path) or { panic(err) } + } + } + } +} + +fn (vd VDoc) vprintln(str string) { + if vd.cfg.is_verbose { + println('vdoc: $str') + } +} + +fn parse_arguments(args []string) Config { + mut cfg := Config{} + cfg.is_color = term.can_show_color_on_stdout() + for i := 0; i < args.len; i++ { + arg := args[i] + current_args := args[i..] + match arg { + '-all' { + cfg.pub_only = false + } + '-f' { + format := cmdline.option(current_args, '-f', '') + if format !in allowed_formats { + allowed_str := allowed_formats.join(', ') + eprintln('vdoc: "$format" is not a valid format. Only $allowed_str are allowed.') + exit(1) + } + cfg.output_type = set_output_type_from_str(format) + i++ + } + '-color' { + cfg.is_color = true + } + '-no-color' { + cfg.is_color = false + } + '-inline-assets' { + cfg.inline_assets = true + } + '-l' { + cfg.show_loc = true + } + '-m' { + cfg.is_multi = true + } + '-o' { + opath := cmdline.option(current_args, '-o', '') + cfg.output_path = if opath == 'stdout' { opath } else { os.real_path(opath) } + i++ + } + '-os' { + platform_str := cmdline.option(current_args, '-os', '') + if platform_str == 'cross' { + eprintln('`v doc -os cross` is not supported yet.') + exit(1) + } + selected_platform := doc.platform_from_string(platform_str) or { + eprintln(err.msg) + exit(1) + } + cfg.platform = selected_platform + i++ + } + '-no-timestamp' { + cfg.no_timestamp = true + } + '-no-examples' { + cfg.include_examples = false + } + '-readme' { + cfg.include_readme = true + } + '-v' { + cfg.is_verbose = true + } + else { + if cfg.input_path.len < 1 { + cfg.input_path = arg + } else if !cfg.is_multi { + // Symbol name filtering should not be enabled + // in multi-module documentation mode. + cfg.symbol_name = arg + } + if i == args.len - 1 { + break + } + } + } + } + // Correct from configuration from user input + if cfg.output_path == 'stdout' && cfg.output_type == .html { + cfg.inline_assets = true + } + $if windows { + cfg.input_path = cfg.input_path.replace('/', os.path_separator) + } $else { + cfg.input_path = cfg.input_path.replace('\\', os.path_separator) + } + is_path := cfg.input_path.ends_with('.v') || cfg.input_path.split(os.path_separator).len > 1 + || cfg.input_path == '.' + if cfg.input_path.trim_right('/') == 'vlib' { + cfg.is_vlib = true + cfg.is_multi = true + cfg.input_path = os.join_path(vroot, 'vlib') + } else if !is_path { + // TODO vd.vprintln('Input "$cfg.input_path" is not a valid path. Looking for modules named "$cfg.input_path"...') + mod_path := doc.lookup_module(cfg.input_path) or { + eprintln('vdoc: $err') + exit(1) + } + cfg.input_path = mod_path + } + return cfg +} + +fn main() { + if os.args.len < 2 || '-h' in os.args || '-help' in os.args || '--help' in os.args + || os.args[1..] == ['doc', 'help'] { + os.system('$vexe help doc') + exit(0) + } + args := os.args[2..].clone() + cfg := parse_arguments(args) + if cfg.input_path.len == 0 { + eprintln('vdoc: No input path found.') + exit(1) + } + // Config is immutable from this point on + mut vd := VDoc{ + cfg: cfg + manifest: vmod.Manifest{ + repo_url: '' + } + } + vd.vprintln('Setting output type to "$cfg.output_type"') + vd.generate_docs_from_file() +} diff --git a/v_windows/v/old/cmd/tools/vdoctor.exe b/v_windows/v/old/cmd/tools/vdoctor.exe new file mode 100644 index 0000000..b023ce4 Binary files /dev/null and b/v_windows/v/old/cmd/tools/vdoctor.exe differ diff --git a/v_windows/v/old/cmd/tools/vdoctor.v b/v_windows/v/old/cmd/tools/vdoctor.v new file mode 100644 index 0000000..7eb8901 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vdoctor.v @@ -0,0 +1,264 @@ +import os +import time +import term +import v.util.version +import runtime + +struct App { +mut: + report_lines []string + cached_cpuinfo map[string]string +} + +fn (mut a App) println(s string) { + a.report_lines << s +} + +fn (mut a App) collect_info() { + mut os_kind := os.user_os() + mut arch_details := []string{} + arch_details << '$runtime.nr_cpus() cpus' + if runtime.is_32bit() { + arch_details << '32bit' + } + if runtime.is_64bit() { + arch_details << '64bit' + } + if runtime.is_big_endian() { + arch_details << 'big endian' + } + if runtime.is_little_endian() { + arch_details << 'little endian' + } + if os_kind == 'macos' { + arch_details << a.cmd(command: 'sysctl -n machdep.cpu.brand_string') + } + if os_kind == 'linux' { + mut cpu_details := '' + if cpu_details == '' { + cpu_details = a.cpu_info('model name') + } + if cpu_details == '' { + cpu_details = a.cpu_info('hardware') + } + if cpu_details == '' { + cpu_details = os.uname().machine + } + arch_details << cpu_details + } + if os_kind == 'windows' { + arch_details << a.cmd( + command: 'wmic cpu get name /format:table' + line: 1 + ) + } + // + mut os_details := '' + wsl_check := a.cmd(command: 'cat /proc/sys/kernel/osrelease') + if os_kind == 'linux' { + os_details = a.get_linux_os_name() + if a.cpu_info('flags').contains('hypervisor') { + if wsl_check.contains('microsoft') { + // WSL 2 is a Managed VM and Full Linux Kernel + // See https://docs.microsoft.com/en-us/windows/wsl/compare-versions + os_details += ' (WSL 2)' + } else { + os_details += ' (VM)' + } + } + // WSL 1 is NOT a Managed VM and Full Linux Kernel + // See https://docs.microsoft.com/en-us/windows/wsl/compare-versions + if wsl_check.contains('Microsoft') { + os_details += ' (WSL)' + } + // From https://unix.stackexchange.com/a/14346 + awk_cmd := '[ "$(awk \'\$5=="/" {print \$1}\' 0 && output.len > c.line { + return output[c.line] + } + } + return 'Error: $x.output' +} + +fn (mut a App) line(label string, value string) { + a.println('$label: ${term.colorize(term.bold, value)}') +} + +fn (app &App) parse(config string, sep string) map[string]string { + mut m := map[string]string{} + lines := config.split_into_lines() + for line in lines { + sline := line.trim_space() + if sline.len == 0 || sline[0] == `#` { + continue + } + x := sline.split(sep) + if x.len < 2 { + continue + } + m[x[0].trim_space().to_lower()] = x[1].trim_space().trim('"') + } + return m +} + +fn (mut a App) get_linux_os_name() string { + mut os_details := '' + linux_os_methods := ['os-release', 'lsb_release', 'kernel', 'uname'] + for m in linux_os_methods { + match m { + 'os-release' { + if !os.is_file('/etc/os-release') { + continue + } + lines := os.read_file('/etc/os-release') or { continue } + vals := a.parse(lines, '=') + if vals['PRETTY_NAME'] == '' { + continue + } + os_details = vals['PRETTY_NAME'] + break + } + 'lsb_release' { + exists := a.cmd(command: 'type lsb_release') + if exists.starts_with('Error') { + continue + } + os_details = a.cmd(command: 'lsb_release -d -s') + break + } + 'kernel' { + if !os.is_file('/proc/version') { + continue + } + os_details = a.cmd(command: 'cat /proc/version') + break + } + 'uname' { + ouname := os.uname() + os_details = '$ouname.release, $ouname.version' + break + } + else {} + } + } + return os_details +} + +fn (mut a App) cpu_info(key string) string { + if a.cached_cpuinfo.len > 0 { + return a.cached_cpuinfo[key] + } + info := os.execute('cat /proc/cpuinfo') + if info.exit_code != 0 { + return '`cat /proc/cpuinfo` could not run' + } + a.cached_cpuinfo = a.parse(info.output, ':') + return a.cached_cpuinfo[key] +} + +fn (mut a App) git_info() string { + mut out := a.cmd(command: 'git -C . describe --abbrev=8 --dirty --always --tags').trim_space() + os.execute('git -C . remote add V_REPO https://github.com/vlang/v') // ignore failure (i.e. remote exists) + os.execute('git -C . fetch V_REPO') + commit_count := a.cmd(command: 'git rev-list @{0}...V_REPO/master --right-only --count').int() + if commit_count > 0 { + out += ' ($commit_count commit(s) behind V master)' + } + return out +} + +fn (mut a App) report_tcc_version(tccfolder string) { + if !os.is_file(os.join_path(tccfolder, '.git', 'config')) { + a.line(tccfolder, 'N/A') + return + } + tcc_branch_name := a.cmd(command: 'git -C $tccfolder rev-parse --abbrev-ref HEAD') + tcc_commit := a.cmd(command: 'git -C $tccfolder describe --abbrev=8 --dirty --always --tags') + a.line('$tccfolder status', '$tcc_branch_name $tcc_commit') +} + +fn (mut a App) report_info() { + for x in a.report_lines { + println(x) + } +} + +fn is_writable_dir(path string) bool { + res := os.is_writable_folder(path) or { false } + return res +} + +fn main() { + mut app := App{} + app.collect_info() + app.report_info() +} diff --git a/v_windows/v/old/cmd/tools/vfmt.v b/v_windows/v/old/cmd/tools/vfmt.v new file mode 100644 index 0000000..ae10f1a --- /dev/null +++ b/v_windows/v/old/cmd/tools/vfmt.v @@ -0,0 +1,334 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module main + +import os +import os.cmdline +import rand +import term +import v.ast +import v.pref +import v.fmt +import v.util +import v.util.diff +import v.parser +import vhelp + +struct FormatOptions { + is_l bool + is_c bool // NB: This refers to the '-c' fmt flag, NOT the C backend + is_w bool + is_diff bool + is_verbose bool + is_all bool + is_debug bool + is_noerror bool + is_verify bool // exit(1) if the file is not vfmt'ed + is_worker bool // true *only* in the worker processes. NB: workers can crash. +} + +const ( + formatted_file_token = '\@\@\@' + 'FORMATTED_FILE: ' + vtmp_folder = util.get_vtmp_folder() + term_colors = term.can_show_color_on_stderr() +) + +fn main() { + // if os.getenv('VFMT_ENABLE') == '' { + // eprintln('v fmt is disabled for now') + // exit(1) + // } + toolexe := os.executable() + util.set_vroot_folder(os.dir(os.dir(os.dir(toolexe)))) + args := util.join_env_vflags_and_os_args() + mut foptions := FormatOptions{ + is_c: '-c' in args + is_l: '-l' in args + is_w: '-w' in args + is_diff: '-diff' in args + is_verbose: '-verbose' in args || '--verbose' in args + is_all: '-all' in args || '--all' in args + is_worker: '-worker' in args + is_debug: '-debug' in args + is_noerror: '-noerror' in args + is_verify: '-verify' in args + } + if term_colors { + os.setenv('VCOLORS', 'always', true) + } + if foptions.is_verbose { + eprintln('vfmt foptions: $foptions') + } + if foptions.is_worker { + // -worker should be added by a parent vfmt process. + // We launch a sub process for each file because + // the v compiler can do an early exit if it detects + // a syntax error, but we want to process ALL passed + // files if possible. + foptions.format_file(cmdline.option(args, '-worker', '')) + exit(0) + } + // we are NOT a worker at this stage, i.e. we are a parent vfmt process + possible_files := cmdline.only_non_options(cmdline.options_after(args, ['fmt'])) + if foptions.is_verbose { + eprintln('vfmt toolexe: $toolexe') + eprintln('vfmt args: ' + os.args.str()) + eprintln('vfmt env_vflags_and_os_args: ' + args.str()) + eprintln('vfmt possible_files: ' + possible_files.str()) + } + files := util.find_all_v_files(possible_files) or { + verror(err.msg) + return + } + if os.is_atty(0) == 0 && files.len == 0 { + foptions.format_pipe() + exit(0) + } + if files.len == 0 || '-help' in args || '--help' in args { + vhelp.show_topic('fmt') + exit(0) + } + mut cli_args_no_files := []string{} + for a in os.args { + if a !in files { + cli_args_no_files << a + } + } + mut errors := 0 + for file in files { + fpath := os.real_path(file) + mut worker_command_array := cli_args_no_files.clone() + worker_command_array << ['-worker', util.quote_path(fpath)] + worker_cmd := worker_command_array.join(' ') + if foptions.is_verbose { + eprintln('vfmt worker_cmd: $worker_cmd') + } + worker_result := os.execute(worker_cmd) + // Guard against a possibly crashing worker process. + if worker_result.exit_code != 0 { + eprintln(worker_result.output) + if worker_result.exit_code == 1 { + eprintln('Internal vfmt error while formatting file: ${file}.') + } + errors++ + continue + } + if worker_result.output.len > 0 { + if worker_result.output.contains(formatted_file_token) { + wresult := worker_result.output.split(formatted_file_token) + formatted_warn_errs := wresult[0] + formatted_file_path := wresult[1].trim_right('\n\r') + foptions.post_process_file(fpath, formatted_file_path) or { errors = errors + 1 } + if formatted_warn_errs.len > 0 { + eprintln(formatted_warn_errs) + } + continue + } + } + errors++ + } + if errors > 0 { + eprintln('Encountered a total of: $errors errors.') + if foptions.is_noerror { + exit(0) + } + if foptions.is_verify { + exit(1) + } + if foptions.is_c { + exit(2) + } + exit(1) + } +} + +fn (foptions &FormatOptions) format_file(file string) { + mut prefs := pref.new_preferences() + prefs.is_fmt = true + if foptions.is_verbose { + eprintln('vfmt2 running fmt.fmt over file: $file') + } + table := ast.new_table() + // checker := checker.new_checker(table, prefs) + file_ast := parser.parse_file(file, table, .parse_comments, prefs) + // checker.check(file_ast) + formatted_content := fmt.fmt(file_ast, table, prefs, foptions.is_debug) + file_name := os.file_name(file) + ulid := rand.ulid() + vfmt_output_path := os.join_path(vtmp_folder, 'vfmt_${ulid}_$file_name') + os.write_file(vfmt_output_path, formatted_content) or { panic(err) } + if foptions.is_verbose { + eprintln('fmt.fmt worked and $formatted_content.len bytes were written to $vfmt_output_path .') + } + eprintln('$formatted_file_token$vfmt_output_path') +} + +fn (foptions &FormatOptions) format_pipe() { + mut prefs := pref.new_preferences() + prefs.is_fmt = true + if foptions.is_verbose { + eprintln('vfmt2 running fmt.fmt over stdin') + } + input_text := os.get_raw_lines_joined() + table := ast.new_table() + // checker := checker.new_checker(table, prefs) + file_ast := parser.parse_text(input_text, '', table, .parse_comments, prefs) + // checker.check(file_ast) + formatted_content := fmt.fmt(file_ast, table, prefs, foptions.is_debug) + print(formatted_content) + if foptions.is_verbose { + eprintln('fmt.fmt worked and $formatted_content.len bytes were written to stdout.') + } +} + +fn print_compiler_options(compiler_params &pref.Preferences) { + eprintln(' os: ' + compiler_params.os.str()) + eprintln(' ccompiler: $compiler_params.ccompiler') + eprintln(' path: $compiler_params.path ') + eprintln(' out_name: $compiler_params.out_name ') + eprintln(' vroot: $compiler_params.vroot ') + eprintln('lookup_path: $compiler_params.lookup_path ') + eprintln(' out_name: $compiler_params.out_name ') + eprintln(' cflags: $compiler_params.cflags ') + eprintln(' is_test: $compiler_params.is_test ') + eprintln(' is_script: $compiler_params.is_script ') +} + +fn (foptions &FormatOptions) post_process_file(file string, formatted_file_path string) ? { + if formatted_file_path.len == 0 { + return + } + if foptions.is_diff { + diff_cmd := diff.find_working_diff_command() or { + eprintln(err) + return + } + if foptions.is_verbose { + eprintln('Using diff command: $diff_cmd') + } + diff := diff.color_compare_files(diff_cmd, file, formatted_file_path) + if diff.len > 0 { + println(diff) + } + return + } + if foptions.is_verify { + diff_cmd := diff.find_working_diff_command() or { + eprintln(err) + return + } + x := diff.color_compare_files(diff_cmd, file, formatted_file_path) + if x.len != 0 { + println("$file is not vfmt'ed") + return error('') + } + return + } + fc := os.read_file(file) or { + eprintln('File $file could not be read') + return + } + formatted_fc := os.read_file(formatted_file_path) or { + eprintln('File $formatted_file_path could not be read') + return + } + is_formatted_different := fc != formatted_fc + if foptions.is_c { + if is_formatted_different { + eprintln('File is not formatted: $file') + return error('') + } + return + } + if foptions.is_l { + if is_formatted_different { + eprintln('File needs formatting: $file') + } + return + } + if foptions.is_w { + if is_formatted_different { + os.mv_by_cp(formatted_file_path, file) or { panic(err) } + eprintln('Reformatted file: $file') + } else { + eprintln('Already formatted file: $file') + } + return + } + print(formatted_fc) +} + +fn (f FormatOptions) str() string { + return + 'FormatOptions{ is_l: $f.is_l, is_w: $f.is_w, is_diff: $f.is_diff, is_verbose: $f.is_verbose,' + + ' is_all: $f.is_all, is_worker: $f.is_worker, is_debug: $f.is_debug, is_noerror: $f.is_noerror,' + + ' is_verify: $f.is_verify" }' +} + +fn file_to_mod_name_and_is_module_file(file string) (string, bool) { + mut mod_name := 'main' + mut is_module_file := false + flines := read_source_lines(file) or { return mod_name, is_module_file } + for fline in flines { + line := fline.trim_space() + if line.starts_with('module ') { + if !line.starts_with('module main') { + is_module_file = true + mod_name = line.replace('module ', ' ').trim_space() + } + break + } + } + return mod_name, is_module_file +} + +fn read_source_lines(file string) ?[]string { + source_lines := os.read_lines(file) or { return error('can not read $file') } + return source_lines +} + +fn get_compile_name_of_potential_v_project(file string) string { + // This function get_compile_name_of_potential_v_project returns: + // a) the file's folder, if file is part of a v project + // b) the file itself, if the file is a standalone v program + pfolder := os.real_path(os.dir(file)) + // a .v project has many 'module main' files in one folder + // if there is only one .v file, then it must be a standalone + all_files_in_pfolder := os.ls(pfolder) or { panic(err) } + mut vfiles := []string{} + for f in all_files_in_pfolder { + vf := os.join_path(pfolder, f) + if f.starts_with('.') || !f.ends_with('.v') || os.is_dir(vf) { + continue + } + vfiles << vf + } + if vfiles.len == 1 { + return file + } + // ///////////////////////////////////////////////////////////// + // At this point, we know there are many .v files in the folder + // We will have to read them all, and if there are more than one + // containing `fn main` then the folder contains multiple standalone + // v programs. If only one contains `fn main` then the folder is + // a project folder, that should be compiled with `v pfolder`. + mut main_fns := 0 + for f in vfiles { + slines := read_source_lines(f) or { panic(err) } + for line in slines { + if line.contains('fn main()') { + main_fns++ + if main_fns > 1 { + return file + } + } + } + } + return pfolder +} + +[noreturn] +fn verror(s string) { + util.verror('vfmt error', s) +} diff --git a/v_windows/v/old/cmd/tools/vpm.exe b/v_windows/v/old/cmd/tools/vpm.exe new file mode 100644 index 0000000..2f4b9c9 Binary files /dev/null and b/v_windows/v/old/cmd/tools/vpm.exe differ diff --git a/v_windows/v/old/cmd/tools/vpm.v b/v_windows/v/old/cmd/tools/vpm.v new file mode 100644 index 0000000..a503d24 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vpm.v @@ -0,0 +1,601 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module main + +import os +import os.cmdline +import net.http +import json +import vhelp +import v.vmod + +const ( + default_vpm_server_urls = ['https://vpm.vlang.io'] + valid_vpm_commands = ['help', 'search', 'install', 'update', 'upgrade', 'outdated', + 'list', 'remove', 'show'] + excluded_dirs = ['cache', 'vlib'] + supported_vcs_systems = ['git', 'hg'] + supported_vcs_folders = ['.git', '.hg'] + supported_vcs_update_cmds = map{ + 'git': 'git pull' + 'hg': 'hg pull --update' + } + supported_vcs_install_cmds = map{ + 'git': 'git clone --depth=1' + 'hg': 'hg clone' + } + supported_vcs_outdated_steps = map{ + 'git': ['git fetch', 'git rev-parse @', 'git rev-parse @{u}'] + 'hg': ['hg incoming'] + } +) + +struct Mod { + id int + name string + url string + nr_downloads int + vcs string +} + +struct Vmod { +mut: + name string + version string + deps []string +} + +fn main() { + init_settings() + // This tool is intended to be launched by the v frontend, + // which provides the path to V inside os.getenv('VEXE') + // args are: vpm [options] SUBCOMMAND module names + params := cmdline.only_non_options(os.args[1..]) + verbose_println('cli params: $params') + if params.len < 1 { + vpm_help() + exit(5) + } + vpm_command := params[0] + mut module_names := params[1..] + ensure_vmodules_dir_exist() + // println('module names: ') println(module_names) + match vpm_command { + 'help' { + vpm_help() + } + 'search' { + vpm_search(module_names) + } + 'install' { + if module_names.len == 0 && os.exists('./v.mod') { + println('Detected v.mod file inside the project directory. Using it...') + manifest := vmod.from_file('./v.mod') or { panic(err) } + module_names = manifest.dependencies + } + vpm_install(module_names) + } + 'update' { + vpm_update(module_names) + } + 'upgrade' { + vpm_upgrade() + } + 'outdated' { + vpm_outdated() + } + 'list' { + vpm_list() + } + 'remove' { + vpm_remove(module_names) + } + 'show' { + vpm_show(module_names) + } + else { + println('Error: you tried to run "v $vpm_command"') + println('... but the v package management tool vpm only knows about these commands:') + for validcmd in valid_vpm_commands { + println(' v $validcmd') + } + exit(3) + } + } +} + +fn vpm_search(keywords []string) { + search_keys := keywords.map(it.replace('_', '-')) + if settings.is_help { + vhelp.show_topic('search') + exit(0) + } + if search_keys.len == 0 { + println('´v search´ requires *at least one* keyword.') + exit(2) + } + modules := get_all_modules() + installed_modules := get_installed_modules() + joined := search_keys.join(', ') + mut index := 0 + for mod in modules { + // TODO for some reason .filter results in substr error, so do it manually + for k in search_keys { + if !mod.contains(k) { + continue + } + if index == 0 { + println('Search results for "$joined":\n') + } + index++ + mut parts := mod.split('.') + // in case the author isn't present + if parts.len == 1 { + parts << parts[0] + parts[0] = ' ' + } else { + parts[0] = ' by ${parts[0]} ' + } + installed := if mod in installed_modules { ' (installed)' } else { '' } + println('${index}. ${parts[1]}${parts[0]}[$mod]$installed') + break + } + } + if index == 0 { + vexe := os.getenv('VEXE') + vroot := os.real_path(os.dir(vexe)) + mut messages := ['No module(s) found for `$joined` .'] + for vlibmod in search_keys { + if os.is_dir(os.join_path(vroot, 'vlib', vlibmod)) { + messages << 'There is already an existing "$vlibmod" module in vlib, so you can just `import $vlibmod` .' + } + } + for m in messages { + println(m) + } + } else { + println('\nUse "v install author_name.module_name" to install the module.') + } +} + +fn vpm_install(module_names []string) { + if settings.is_help { + vhelp.show_topic('install') + exit(0) + } + if module_names.len == 0 { + println('´v install´ requires *at least one* module name.') + exit(2) + } + mut errors := 0 + for n in module_names { + name := n.trim_space().replace('_', '-') + mod := get_module_meta_info(name) or { + errors++ + println('Errors while retrieving meta data for module $name:') + println(err) + continue + } + mut vcs := mod.vcs + if vcs == '' { + vcs = supported_vcs_systems[0] + } + if vcs !in supported_vcs_systems { + errors++ + println('Skipping module "$name", since it uses an unsupported VCS {$vcs} .') + continue + } + mod_name_as_path := mod.name.replace('.', os.path_separator).replace('-', '_').to_lower() + final_module_path := os.real_path(os.join_path(settings.vmodules_path, mod_name_as_path)) + if os.exists(final_module_path) { + vpm_update([name]) + continue + } + println('Installing module "$name" from $mod.url to $final_module_path ...') + vcs_install_cmd := supported_vcs_install_cmds[vcs] + cmd := '$vcs_install_cmd "$mod.url" "$final_module_path"' + verbose_println(' command: $cmd') + cmdres := os.execute(cmd) + if cmdres.exit_code != 0 { + errors++ + println('Failed installing module "$name" to "$final_module_path" .') + verbose_println('Failed command: $cmd') + verbose_println('Failed command output:\n$cmdres.output') + continue + } + resolve_dependencies(name, final_module_path, module_names) + } + if errors > 0 { + exit(1) + } +} + +fn vpm_update(m []string) { + mut module_names := m.clone() + if settings.is_help { + vhelp.show_topic('update') + exit(0) + } + if module_names.len == 0 { + module_names = get_installed_modules() + } + mut errors := 0 + for name in module_names { + final_module_path := valid_final_path_of_existing_module(name) or { continue } + os.chdir(final_module_path) + println('Updating module "$name"...') + verbose_println(' work folder: $final_module_path') + vcs := vcs_used_in_dir(final_module_path) or { continue } + vcs_cmd := supported_vcs_update_cmds[vcs[0]] + verbose_println(' command: $vcs_cmd') + vcs_res := os.execute('$vcs_cmd') + if vcs_res.exit_code != 0 { + errors++ + println('Failed updating module "$name".') + verbose_println('Failed command: $vcs_cmd') + verbose_println('Failed details:\n$vcs_res.output') + continue + } else { + verbose_println(' $vcs_res.output.trim_space()') + } + resolve_dependencies(name, final_module_path, module_names) + } + if errors > 0 { + exit(1) + } +} + +fn get_outdated() ?[]string { + module_names := get_installed_modules() + mut outdated := []string{} + for name in module_names { + final_module_path := valid_final_path_of_existing_module(name) or { continue } + os.chdir(final_module_path) + vcs := vcs_used_in_dir(final_module_path) or { continue } + vcs_cmd_steps := supported_vcs_outdated_steps[vcs[0]] + mut outputs := []string{} + for step in vcs_cmd_steps { + res := os.execute(step) + if res.exit_code < 0 { + verbose_println('Error command: $step') + verbose_println('Error details:\n$res.output') + return error('Error while checking latest commits for "$name".') + } + if vcs[0] == 'hg' { + if res.exit_code == 1 { + outdated << name + } + } else { + outputs << res.output + } + } + if vcs[0] == 'git' && outputs[1] != outputs[2] { + outdated << name + } + } + return outdated +} + +fn vpm_upgrade() { + outdated := get_outdated() or { exit(1) } + if outdated.len > 0 { + vpm_update(outdated) + } else { + println('Modules are up to date.') + } +} + +fn vpm_outdated() { + outdated := get_outdated() or { exit(1) } + if outdated.len > 0 { + println('Outdated modules:') + for m in outdated { + println(' $m') + } + } else { + println('Modules are up to date.') + } +} + +fn vpm_list() { + module_names := get_installed_modules() + if module_names.len == 0 { + println('You have no modules installed.') + exit(0) + } + println('Installed modules:') + for mod in module_names { + println(' $mod') + } +} + +fn vpm_remove(module_names []string) { + if settings.is_help { + vhelp.show_topic('remove') + exit(0) + } + if module_names.len == 0 { + println('´v remove´ requires *at least one* module name.') + exit(2) + } + for name in module_names { + final_module_path := valid_final_path_of_existing_module(name) or { continue } + println('Removing module "$name"...') + verbose_println('removing folder $final_module_path') + os.rmdir_all(final_module_path) or { + verbose_println('error while removing "$final_module_path": $err.msg') + } + // delete author directory if it is empty + author := name.split('.')[0] + author_dir := os.real_path(os.join_path(settings.vmodules_path, author)) + if !os.exists(author_dir) { + continue + } + if os.is_dir_empty(author_dir) { + verbose_println('removing author folder $author_dir') + os.rmdir(author_dir) or { + verbose_println('error while removing "$author_dir": $err.msg') + } + } + } +} + +fn valid_final_path_of_existing_module(name string) ?string { + mod_name_as_path := name.replace('.', os.path_separator).replace('-', '_').to_lower() + name_of_vmodules_folder := os.join_path(settings.vmodules_path, mod_name_as_path) + final_module_path := os.real_path(name_of_vmodules_folder) + if !os.exists(final_module_path) { + println('No module with name "$name" exists at $name_of_vmodules_folder') + return none + } + if !os.is_dir(final_module_path) { + println('Skipping "$name_of_vmodules_folder", since it is not a folder.') + return none + } + vcs_used_in_dir(final_module_path) or { + println('Skipping "$name_of_vmodules_folder", since it does not use a supported vcs.') + return none + } + return final_module_path +} + +fn ensure_vmodules_dir_exist() { + if !os.is_dir(settings.vmodules_path) { + println('Creating $settings.vmodules_path/ ...') + os.mkdir(settings.vmodules_path) or { panic(err) } + } +} + +fn vpm_help() { + vhelp.show_topic('vpm') +} + +fn vcs_used_in_dir(dir string) ?[]string { + mut vcs := []string{} + for repo_subfolder in supported_vcs_folders { + checked_folder := os.real_path(os.join_path(dir, repo_subfolder)) + if os.is_dir(checked_folder) { + vcs << repo_subfolder.replace('.', '') + } + } + if vcs.len == 0 { + return none + } + return vcs +} + +fn get_installed_modules() []string { + dirs := os.ls(settings.vmodules_path) or { return [] } + mut modules := []string{} + for dir in dirs { + adir := os.join_path(settings.vmodules_path, dir) + if dir in excluded_dirs || !os.is_dir(adir) { + continue + } + if os.exists(os.join_path(adir, 'v.mod')) && os.exists(os.join_path(adir, '.git', 'config')) { + // an official vlang module with a short module name, like `vsl`, `ui` or `markdown` + modules << dir + continue + } + author := dir + mods := os.ls(adir) or { continue } + for m in mods { + vcs_used_in_dir(os.join_path(adir, m)) or { continue } + modules << '${author}.$m' + } + } + return modules +} + +fn get_all_modules() []string { + url := get_working_server_url() + r := http.get(url) or { panic(err) } + if r.status_code != 200 { + println('Failed to search vpm.vlang.io. Status code: $r.status_code') + exit(1) + } + s := r.text + mut read_len := 0 + mut modules := []string{} + for read_len < s.len { + mut start_token := '' + start_index = s.index_after(start_token, start_index) + start_token.len + // get the index of the end of module entry + end_index := s.index_after(end_token, start_index) + if end_index == -1 { + break + } + modules << s[start_index..end_index] + read_len = end_index + if read_len >= s.len { + break + } + } + return modules +} + +fn resolve_dependencies(name string, module_path string, module_names []string) { + vmod_path := os.join_path(module_path, 'v.mod') + if !os.exists(vmod_path) { + return + } + data := os.read_file(vmod_path) or { return } + vmod := parse_vmod(data) + mut deps := []string{} + // filter out dependencies that were already specified by the user + for d in vmod.deps { + if d !in module_names { + deps << d + } + } + if deps.len > 0 { + println('Resolving $deps.len dependencies for module "$name"...') + verbose_println('Found dependencies: $deps') + vpm_install(deps) + } +} + +fn parse_vmod(data string) Vmod { + keys := ['name', 'version', 'deps'] + mut m := map{ + 'name': '' + 'version': '' + 'deps': '' + } + for key in keys { + mut key_index := data.index('$key:') or { continue } + key_index += key.len + 1 + m[key] = data[key_index..data.index_after('\n', key_index)].trim_space().replace("'", + '').replace('[', '').replace(']', '') + } + mut vmod := Vmod{} + vmod.name = m['name'] + vmod.version = m['version'] + if m['deps'].len > 0 { + vmod.deps = m['deps'].split(',') + } + return vmod +} + +fn get_working_server_url() string { + server_urls := if settings.server_urls.len > 0 { + settings.server_urls + } else { + default_vpm_server_urls + } + for url in server_urls { + verbose_println('Trying server url: $url') + http.head(url) or { + verbose_println(' $url failed.') + continue + } + return url + } + panic('No responding vpm server found. Please check your network connectivity and try again later.') +} + +// settings context: +struct VpmSettings { +mut: + is_help bool + is_verbose bool + server_urls []string + vmodules_path string +} + +const ( + settings = &VpmSettings{} +) + +fn init_settings() { + mut s := &VpmSettings(0) + unsafe { + s = settings + } + s.is_help = '-h' in os.args || '--help' in os.args || 'help' in os.args + s.is_verbose = '-v' in os.args + s.server_urls = cmdline.options(os.args, '-server-url') + s.vmodules_path = os.vmodules_dir() +} + +fn verbose_println(s string) { + if settings.is_verbose { + println(s) + } +} + +fn get_module_meta_info(name string) ?Mod { + mut errors := []string{} + for server_url in default_vpm_server_urls { + modurl := server_url + '/jsmod/$name' + verbose_println('Retrieving module metadata from: $modurl ...') + r := http.get(modurl) or { + errors << 'Http server did not respond to our request for ${modurl}.' + errors << 'Error details: $err' + continue + } + if r.status_code == 404 || r.text.trim_space() == '404' { + errors << 'Skipping module "$name", since $server_url reported that "$name" does not exist.' + continue + } + if r.status_code != 200 { + errors << 'Skipping module "$name", since $server_url responded with $r.status_code http status code. Please try again later.' + continue + } + s := r.text + if s.len > 0 && s[0] != `{` { + errors << 'Invalid json data' + errors << s.trim_space().limit(100) + '...' + continue + } + mod := json.decode(Mod, s) or { + errors << 'Skipping module "$name", since its information is not in json format.' + continue + } + if '' == mod.url || '' == mod.name { + errors << 'Skipping module "$name", since it is missing name or url information.' + continue + } + return mod + } + return error(errors.join_lines()) +} + +fn vpm_show(module_names []string) { + installed_modules := get_installed_modules() + for module_name in module_names { + if module_name !in installed_modules { + module_meta_info := get_module_meta_info(module_name) or { continue } + print(' +Name: $module_meta_info.name +Homepage: $module_meta_info.url +Downloads: $module_meta_info.nr_downloads +Installed: False +-------- +') + continue + } + path := os.join_path(os.vmodules_dir(), module_name) + mod := vmod.from_file(os.join_path(path, 'v.mod')) or { continue } + print('Name: $mod.name +Version: $mod.version +Description: $mod.description +Homepage: $mod.repo_url +Author: $mod.author +License: $mod.license +Location: $path +Requires: ${mod.dependencies.join(', ')} +-------- +') + } +} diff --git a/v_windows/v/old/cmd/tools/vrepl.exe b/v_windows/v/old/cmd/tools/vrepl.exe new file mode 100644 index 0000000..356bd4d Binary files /dev/null and b/v_windows/v/old/cmd/tools/vrepl.exe differ diff --git a/v_windows/v/old/cmd/tools/vrepl.v b/v_windows/v/old/cmd/tools/vrepl.v new file mode 100644 index 0000000..99ea224 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vrepl.v @@ -0,0 +1,390 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module main + +import os +import term +import rand +import readline +import os.cmdline +import v.util.version + +struct Repl { +mut: + readline readline.Readline + indent int // indentation level + in_func bool // are we inside a new custom user function + line string // the current line entered by the user + // + modules []string // all the import modules + includes []string // all the #include statements + functions []string // all the user function declarations + functions_name []string // all the user function names + lines []string // all the other lines/statements + temp_lines []string // all the temporary expressions/printlns + vstartup_lines []string // lines in the `VSTARTUP` file +} + +const is_stdin_a_pipe = (os.is_atty(0) == 0) + +const vexe = os.getenv('VEXE') + +const vstartup = os.getenv('VSTARTUP') + +fn new_repl() Repl { + return Repl{ + readline: readline.Readline{} + modules: ['os', 'time', 'math'] + vstartup_lines: os.read_file(vstartup) or { '' }.trim_right('\n\r').split_into_lines() + } +} + +fn (mut r Repl) checks() bool { + mut in_string := false + was_indent := r.indent > 0 + for i := 0; i < r.line.len; i++ { + if r.line[i] == `\'` && (i == 0 || r.line[i - 1] != `\\`) { + in_string = !in_string + } + if r.line[i] == `{` && !in_string { + r.line = r.line[..i + 1] + '\n' + r.line[i + 1..] + i++ + r.indent++ + } + if r.line[i] == `}` && !in_string { + r.line = r.line[..i] + '\n' + r.line[i..] + i++ + r.indent-- + if r.indent == 0 { + r.in_func = false + } + } + if i + 2 < r.line.len && r.indent == 0 && r.line[i + 1] == `f` && r.line[i + 2] == `n` { + r.in_func = true + } + } + return r.in_func || (was_indent && r.indent <= 0) || r.indent > 0 +} + +fn (r &Repl) function_call(line string) bool { + for function in r.functions_name { + is_function_definition := line.replace(' ', '').starts_with('$function:=') + if line.starts_with(function) && !is_function_definition { + return true + } + } + return false +} + +fn (r &Repl) current_source_code(should_add_temp_lines bool, not_add_print bool) string { + mut all_lines := []string{} + for mod in r.modules { + all_lines << 'import $mod\n' + } + if vstartup != '' { + mut lines := []string{} + if !not_add_print { + lines = r.vstartup_lines.filter(!it.starts_with('print')) + } else { + lines = r.vstartup_lines + } + all_lines << lines + } + all_lines << r.includes + all_lines << r.functions + all_lines << r.lines + + if should_add_temp_lines { + all_lines << r.temp_lines + } + return all_lines.join('\n') +} + +fn repl_help() { + println(version.full_v_version(false)) + println(' + |help Displays this information. + |list Show the program so far. + |reset Clears the accumulated program, so you can start a fresh. + |Ctrl-C, Ctrl-D, exit Exits the REPL. + |clear Clears the screen. +'.strip_margin()) +} + +fn run_repl(workdir string, vrepl_prefix string) { + if !is_stdin_a_pipe { + println(version.full_v_version(false)) + println('Use Ctrl-C or ${term.highlight_command('exit')} to exit, or ${term.highlight_command('help')} to see other available commands') + } + + if vstartup != '' { + result := repl_run_vfile(vstartup) or { + os.Result{ + output: '$vstartup file not found' + } + } + print('\n') + print_output(result) + } + + file := os.join_path(workdir, '.${vrepl_prefix}vrepl.v') + temp_file := os.join_path(workdir, '.${vrepl_prefix}vrepl_temp.v') + mut prompt := '>>> ' + defer { + if !is_stdin_a_pipe { + println('') + } + cleanup_files([file, temp_file]) + } + mut r := new_repl() + for { + if r.indent == 0 { + prompt = '>>> ' + } else { + prompt = '... ' + } + oline := r.get_one_line(prompt) or { break } + line := oline.trim_space() + if line == '' && oline.ends_with('\n') { + continue + } + if line.len <= -1 || line == '' || line == 'exit' { + break + } + r.line = line + if r.line == '\n' { + continue + } + if r.line == 'clear' { + term.erase_clear() + continue + } + if r.line == 'help' { + repl_help() + continue + } + if r.line.contains(':=') && r.line.contains('fn(') { + r.in_func = true + r.functions_name << r.line.all_before(':= fn(').trim_space() + } + if r.line.starts_with('fn') { + r.in_func = true + r.functions_name << r.line.all_after('fn').all_before('(').trim_space() + } + was_func := r.in_func + if r.checks() { + for rline in r.line.split('\n') { + if r.in_func || was_func { + r.functions << rline + } else { + r.temp_lines << rline + } + } + if r.indent > 0 { + continue + } + r.line = '' + } + if r.line == 'debug_repl' { + eprintln('repl: $r') + continue + } + if r.line == 'reset' { + r = new_repl() + continue + } + if r.line == 'list' { + source_code := r.current_source_code(true, true) + println('//////////////////////////////////////////////////////////////////////////////////////') + println(source_code) + println('//////////////////////////////////////////////////////////////////////////////////////') + continue + } + // Save the source only if the user is printing something, + // but don't add this print call to the `lines` array, + // so that it doesn't get called during the next print. + if r.line.starts_with('=') { + r.line = 'println(' + r.line[1..] + ')' + } + if r.line.starts_with('print') { + source_code := r.current_source_code(false, false) + '\n$r.line\n' + os.write_file(file, source_code) or { panic(err) } + s := repl_run_vfile(file) or { return } + print_output(s) + } else { + mut temp_line := r.line + mut temp_flag := false + func_call := r.function_call(r.line) + filter_line := r.line.replace(r.line.find_between("'", "'"), '').replace(r.line.find_between('"', + '"'), '') + possible_statement_patterns := [ + '++', + '--', + '<<', + '//', + '/*', + 'fn ', + 'pub ', + 'mut ', + 'enum ', + 'const ', + 'struct ', + 'interface ', + 'import ', + '#include ', + 'for ', + 'or ', + 'insert', + 'delete', + 'prepend', + 'sort', + 'clear', + 'trim', + ] + mut is_statement := false + if filter_line.count('=') % 2 == 1 { + is_statement = true + } else { + for pattern in possible_statement_patterns { + if filter_line.contains(pattern) { + is_statement = true + break + } + } + } + // NB: starting a line with 2 spaces escapes the println heuristic + if oline.starts_with(' ') { + is_statement = true + } + if !is_statement && !func_call && r.line != '' { + temp_line = 'println($r.line)' + temp_flag = true + } + mut temp_source_code := '' + if temp_line.starts_with('import ') { + mod := r.line.fields()[1] + if mod !in r.modules { + temp_source_code = '$temp_line\n' + r.current_source_code(false, true) + } + } else if temp_line.starts_with('#include ') { + temp_source_code = '$temp_line\n' + r.current_source_code(false, false) + } else { + for i, l in r.lines { + if (l.starts_with('for ') || l.starts_with('if ')) && l.contains('println') { + r.lines.delete(i) + break + } + } + temp_source_code = r.current_source_code(true, false) + '\n$temp_line\n' + } + os.write_file(temp_file, temp_source_code) or { panic(err) } + s := repl_run_vfile(temp_file) or { return } + if !func_call && s.exit_code == 0 && !temp_flag { + for r.temp_lines.len > 0 { + if !r.temp_lines[0].starts_with('print') { + r.lines << r.temp_lines[0] + } + r.temp_lines.delete(0) + } + if r.line.starts_with('import ') { + mod := r.line.fields()[1] + if mod !in r.modules { + r.modules << mod + } + } else if r.line.starts_with('#include ') { + r.includes << r.line + } else { + r.lines << r.line + } + } else { + for r.temp_lines.len > 0 { + r.temp_lines.delete(0) + } + } + print_output(s) + } + } +} + +fn print_output(s os.Result) { + lines := s.output.trim_right('\n\r').split_into_lines() + for line in lines { + if line.contains('.vrepl_temp.v:') { + // Hide the temporary file name + sline := line.all_after('.vrepl_temp.v:') + idx := sline.index(' ') or { + println(sline) + return + } + println(sline[idx + 1..]) + } else if line.contains('.vrepl.v:') { + // Ensure that .vrepl.v: is at the start, ignore the path + // This is needed to have stable .repl tests. + idx := line.index('.vrepl.v:') or { return } + println(line[idx..]) + } else { + println(line) + } + } +} + +fn main() { + // Support for the parameters replfolder and replprefix is needed + // so that the repl can be launched in parallel by several different + // threads by the REPL test runner. + args := cmdline.options_after(os.args, ['repl']) + replfolder := os.real_path(cmdline.option(args, '-replfolder', os.temp_dir())) + replprefix := cmdline.option(args, '-replprefix', 'noprefix.${rand.ulid()}.') + if !os.exists(os.getenv('VEXE')) { + println('Usage:') + println(' VEXE=vexepath vrepl\n') + println(' ... where vexepath is the full path to the v executable file') + return + } + run_repl(replfolder, replprefix) +} + +fn rerror(s string) { + println('V repl error: $s') + os.flush() +} + +fn (mut r Repl) get_one_line(prompt string) ?string { + if is_stdin_a_pipe { + iline := os.get_raw_line() + if iline.len == 0 { + return none + } + return iline + } + rline := r.readline.read_line(prompt) or { return none } + return rline +} + +fn cleanup_files(files []string) { + for file in files { + os.rm(file) or {} + $if windows { + os.rm(file[..file.len - 2] + '.exe') or {} + $if msvc { + os.rm(file[..file.len - 2] + '.ilk') or {} + os.rm(file[..file.len - 2] + '.pdb') or {} + } + } $else { + os.rm(file[..file.len - 2]) or {} + } + } +} + +fn repl_run_vfile(file string) ?os.Result { + $if trace_repl_temp_files ? { + eprintln('>> repl_run_vfile file: $file') + } + s := os.execute('"$vexe" -repl run "$file"') + if s.exit_code < 0 { + rerror(s.output) + return error(s.output) + } + return s +} diff --git a/v_windows/v/old/cmd/tools/vself.exe b/v_windows/v/old/cmd/tools/vself.exe new file mode 100644 index 0000000..0ca7946 Binary files /dev/null and b/v_windows/v/old/cmd/tools/vself.exe differ diff --git a/v_windows/v/old/cmd/tools/vself.v b/v_windows/v/old/cmd/tools/vself.v new file mode 100644 index 0000000..06d2189 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vself.v @@ -0,0 +1,89 @@ +module main + +import os +import os.cmdline +import v.pref +import v.util.recompilation + +const is_debug = os.args.contains('-debug') + +fn main() { + vexe := pref.vexe_path() + vroot := os.dir(vexe) + recompilation.must_be_enabled(vroot, 'Please install V from source, to use `v self` .') + os.chdir(vroot) + os.setenv('VCOLORS', 'always', true) + self_idx := os.args.index('self') + args := os.args[1..self_idx] + jargs := args.join(' ') + obinary := cmdline.option(args, '-o', '') + sargs := if obinary != '' { jargs } else { '$jargs -o v2' } + cmd := '$vexe $sargs cmd/v' + options := if args.len > 0 { '($sargs)' } else { '' } + println('V self compiling ${options}...') + compile(vroot, cmd) + if obinary != '' { + // When -o was given, there is no need to backup/rename the original. + // The user just wants an independent copy of v, and so we are done. + return + } + backup_old_version_and_rename_newer() or { panic(err.msg) } + println('V built successfully!') +} + +fn compile(vroot string, cmd string) { + result := os.execute_or_exit(cmd) + if result.exit_code != 0 { + eprintln('cannot compile to `$vroot`: \n$result.output') + exit(1) + } + if result.output.len > 0 { + println(result.output.trim_space()) + } +} + +fn list_folder(bmessage string, message string) { + if !is_debug { + return + } + if bmessage != '' { + println(bmessage) + } + if os.user_os() == 'windows' { + os.system('dir v*.exe') + } else { + os.system('ls -lartd v*') + } + println(message) +} + +fn backup_old_version_and_rename_newer() ?bool { + mut errors := []string{} + short_v_file := if os.user_os() == 'windows' { 'v.exe' } else { 'v' } + short_v2_file := if os.user_os() == 'windows' { 'v2.exe' } else { 'v2' } + short_bak_file := if os.user_os() == 'windows' { 'v_old.exe' } else { 'v_old' } + v_file := os.real_path(short_v_file) + v2_file := os.real_path(short_v2_file) + bak_file := os.real_path(short_bak_file) + + list_folder('before:', 'removing $bak_file ...') + if os.exists(bak_file) { + os.rm(bak_file) or { errors << 'failed removing $bak_file: $err.msg' } + } + + list_folder('', 'moving $v_file to $bak_file ...') + os.mv(v_file, bak_file) or { errors << err.msg } + + list_folder('', 'removing $v_file ...') + os.rm(v_file) or {} + + list_folder('', 'moving $v2_file to $v_file ...') + os.mv_by_cp(v2_file, v_file) or { panic(err.msg) } + + list_folder('after:', '') + + if errors.len > 0 { + eprintln('backup errors:\n >> ' + errors.join('\n >> ')) + } + return true +} diff --git a/v_windows/v/old/cmd/tools/vsetup-freetype.v b/v_windows/v/old/cmd/tools/vsetup-freetype.v new file mode 100644 index 0000000..c17ee67 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vsetup-freetype.v @@ -0,0 +1,28 @@ +module main + +import os +import v.pref + +const freetype_repo_url = 'https://github.com/ubawurinna/freetype-windows-binaries' + +const freetype_folder = os.join_path('thirdparty', 'freetype') + +fn main() { + $if windows { + println('Setup freetype...') + vroot := os.dir(pref.vexe_path()) + os.chdir(vroot) + if os.is_dir(freetype_folder) { + println('Thirdparty "freetype" is already installed.') + } else { + s := os.execute('git clone --depth=1 $freetype_repo_url $freetype_folder') + if s.exit_code != 0 { + panic(s.output) + } + println(s.output) + println('Thirdparty "freetype" installed successfully.') + } + } $else { + println('It is only for Windows to setup thirdparty "freetype".') + } +} diff --git a/v_windows/v/old/cmd/tools/vsymlink.v b/v_windows/v/old/cmd/tools/vsymlink.v new file mode 100644 index 0000000..c5d7d8a --- /dev/null +++ b/v_windows/v/old/cmd/tools/vsymlink.v @@ -0,0 +1,182 @@ +import os +import v.pref +import v.util + +$if windows { + $if tinyc { + #flag -ladvapi32 + #flag -luser32 + } +} +fn main() { + C.atexit(cleanup_vtmp_folder) + vexe := os.real_path(pref.vexe_path()) + $if windows { + setup_symlink_windows(vexe) + } $else { + setup_symlink_unix(vexe) + } +} + +fn cleanup_vtmp_folder() { + os.rmdir_all(util.get_vtmp_folder()) or {} +} + +fn setup_symlink_unix(vexe string) { + mut link_path := '/data/data/com.termux/files/usr/bin/v' + if !os.is_dir('/data/data/com.termux/files') { + link_dir := '/usr/local/bin' + if !os.exists(link_dir) { + os.mkdir_all(link_dir) or { panic(err) } + } + link_path = link_dir + '/v' + } + os.rm(link_path) or {} + os.symlink(vexe, link_path) or { + eprintln('Failed to create symlink "$link_path". Try again with sudo.') + exit(1) + } +} + +fn setup_symlink_windows(vexe string) { + $if windows { + // Create a symlink in a new local folder (.\.bin\.v.exe) + // Puts `v` in %PATH% without polluting it with anything else (like make.bat). + // This will make `v` available on cmd.exe, PowerShell, and MinGW(MSYS)/WSL/Cygwin + vdir := os.real_path(os.dir(vexe)) + vsymlinkdir := os.join_path(vdir, '.bin') + mut vsymlink := os.join_path(vsymlinkdir, 'v.exe') + // Remove old symlink first (v could have been moved, symlink rerun) + if !os.exists(vsymlinkdir) { + os.mkdir(vsymlinkdir) or { panic(err) } + } else { + if os.exists(vsymlink) { + os.rm(vsymlink) or { panic(err) } + } else { + vsymlink = os.join_path(vsymlinkdir, 'v.bat') + if os.exists(vsymlink) { + os.rm(vsymlink) or { panic(err) } + } + vsymlink = os.join_path(vsymlinkdir, 'v.exe') + } + } + // First, try to create a native symlink at .\.bin\v.exe + os.symlink(vsymlink, vexe) or { + // typically only fails if you're on a network drive (VirtualBox) + // do batch file creation instead + eprintln('Could not create a native symlink: $err') + eprintln('Creating a batch file instead...') + vsymlink = os.join_path(vsymlinkdir, 'v.bat') + if os.exists(vsymlink) { + os.rm(vsymlink) or { panic(err) } + } + os.write_file(vsymlink, '@echo off\n$vexe %*') or { panic(err) } + eprintln('$vsymlink file written.') + } + if !os.exists(vsymlink) { + warn_and_exit('Could not create $vsymlink') + } + println('Symlink $vsymlink to $vexe created.') + println('Checking system %PATH%...') + reg_sys_env_handle := get_reg_sys_env_handle() or { + warn_and_exit(err.msg) + return + } + // TODO: Fix defers inside ifs + // defer { + // C.RegCloseKey(reg_sys_env_handle) + // } + // if the above succeeded, and we cannot get the value, it may simply be empty + sys_env_path := get_reg_value(reg_sys_env_handle, 'Path') or { '' } + current_sys_paths := sys_env_path.split(os.path_delimiter).map(it.trim('/$os.path_separator')) + mut new_paths := [vsymlinkdir] + for p in current_sys_paths { + if p == '' { + continue + } + if p !in new_paths { + new_paths << p + } + } + new_sys_env_path := new_paths.join(';') + if new_sys_env_path == sys_env_path { + println('System %PATH% was already configured.') + } else { + println('System %PATH% was not configured.') + println('Adding symlink directory to system %PATH%...') + set_reg_value(reg_sys_env_handle, 'Path', new_sys_env_path) or { + C.RegCloseKey(reg_sys_env_handle) + warn_and_exit(err.msg) + } + println('Done.') + } + println('Notifying running processes to update their Environment...') + send_setting_change_msg('Environment') or { + eprintln(err) + C.RegCloseKey(reg_sys_env_handle) + warn_and_exit('You might need to run this again to have the `v` command in your %PATH%') + } + C.RegCloseKey(reg_sys_env_handle) + println('Done.') + println('Note: Restart your shell/IDE to load the new %PATH%.') + println('After restarting your shell/IDE, give `v version` a try in another directory!') + } +} + +fn warn_and_exit(err string) { + eprintln(err) + exit(1) +} + +// get the system environment registry handle +fn get_reg_sys_env_handle() ?voidptr { + $if windows { // wrap for cross-compile compat + // open the registry key + reg_key_path := 'Environment' + reg_env_key := voidptr(0) // or HKEY (HANDLE) + if C.RegOpenKeyEx(os.hkey_current_user, reg_key_path.to_wide(), 0, 1 | 2, ®_env_key) != 0 { + return error('Could not open "$reg_key_path" in the registry') + } + return reg_env_key + } + return error('not on windows') +} + +// get a value from a given $key +fn get_reg_value(reg_env_key voidptr, key string) ?string { + $if windows { + // query the value (shortcut the sizing step) + reg_value_size := u32(4095) // this is the max length (not for the registry, but for the system %PATH%) + mut reg_value := unsafe { &u16(malloc(int(reg_value_size))) } + if C.RegQueryValueExW(reg_env_key, key.to_wide(), 0, 0, reg_value, ®_value_size) != 0 { + return error('Unable to get registry value for "$key".') + } + return unsafe { string_from_wide(reg_value) } + } + return error('not on windows') +} + +// sets the value for the given $key to the given $value +fn set_reg_value(reg_key voidptr, key string, value string) ?bool { + $if windows { + if C.RegSetValueExW(reg_key, key.to_wide(), 0, C.REG_EXPAND_SZ, value.to_wide(), + value.len * 2) != 0 { + return error('Unable to set registry value for "$key". %PATH% may be too long.') + } + return true + } + return error('not on windows') +} + +// Broadcasts a message to all listening windows (explorer.exe in particular) +// letting them know that the system environment has changed and should be reloaded +fn send_setting_change_msg(message_data string) ?bool { + $if windows { + if C.SendMessageTimeoutW(os.hwnd_broadcast, os.wm_settingchange, 0, unsafe { &u32(message_data.to_wide()) }, + os.smto_abortifhung, 5000, 0) == 0 { + return error('Could not broadcast WM_SETTINGCHANGE') + } + return true + } + return error('not on windows') +} diff --git a/v_windows/v/old/cmd/tools/vtest-all.v b/v_windows/v/old/cmd/tools/vtest-all.v new file mode 100644 index 0000000..d865872 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vtest-all.v @@ -0,0 +1,187 @@ +module main + +import os +import term +import time + +const vexe = os.getenv('VEXE') + +const vroot = os.dir(vexe) + +const args_string = os.args[1..].join(' ') + +const vargs = args_string.all_before('test-all') + +const vtest_nocleanup = os.getenv('VTEST_NOCLEANUP').bool() + +fn main() { + mut commands := get_all_commands() + // summary + sw := time.new_stopwatch() + for mut cmd in commands { + cmd.run() + } + spent := sw.elapsed().milliseconds() + oks := commands.filter(it.ecode == 0) + fails := commands.filter(it.ecode != 0) + println('') + println(term.header_left(term_highlight('Summary of `v test-all`:'), '-')) + println(term_highlight('Total runtime: $spent ms')) + for ocmd in oks { + msg := if ocmd.okmsg != '' { ocmd.okmsg } else { ocmd.line } + println(term.colorize(term.green, '> OK: $msg ')) + } + for fcmd in fails { + msg := if fcmd.errmsg != '' { fcmd.errmsg } else { fcmd.line } + println(term.failed('> Failed:') + ' $msg') + } + if fails.len > 0 { + exit(1) + } +} + +struct Command { +mut: + line string + label string // when set, the label will be printed *before* cmd.line is executed + ecode int + okmsg string + errmsg string + rmfile string +} + +fn get_all_commands() []Command { + mut res := []Command{} + res << Command{ + line: '$vexe examples/hello_world.v' + okmsg: 'V can compile hello world.' + rmfile: 'examples/hello_world' + } + res << Command{ + line: '$vexe -o hhww.c examples/hello_world.v' + okmsg: 'V can output a .c file, without compiling further.' + rmfile: 'hhww.c' + } + $if linux || macos { + res << Command{ + line: '$vexe -o - examples/hello_world.v | grep "#define V_COMMIT_HASH" > /dev/null' + okmsg: 'V prints the generated source code to stdout with `-o -` .' + } + } + res << Command{ + line: '$vexe -o vtmp cmd/v' + okmsg: 'V can compile itself.' + rmfile: 'vtmp' + } + res << Command{ + line: '$vexe -o vtmp_werror -cstrict cmd/v' + okmsg: 'V can compile itself with -cstrict.' + rmfile: 'vtmp_werror' + } + res << Command{ + line: '$vexe -o vtmp_autofree -autofree cmd/v' + okmsg: 'V can compile itself with -autofree.' + rmfile: 'vtmp_autofree' + } + res << Command{ + line: '$vexe -o vtmp_prealloc -prealloc cmd/v' + okmsg: 'V can compile itself with -prealloc.' + rmfile: 'vtmp_prealloc' + } + res << Command{ + line: '$vexe -o vtmp_unused -skip-unused cmd/v' + okmsg: 'V can compile itself with -skip-unused.' + rmfile: 'vtmp_unused' + } + $if linux { + res << Command{ + line: '$vexe -cc gcc -keepc -freestanding -o bel vlib/os/bare/bare_example_linux.v' + okmsg: 'V can compile with -freestanding on Linux with GCC.' + rmfile: 'bel' + } + } + res << Command{ + line: '$vexe $vargs -progress test-cleancode' + okmsg: 'All .v files are invariant when processed with `v fmt`' + } + res << Command{ + line: '$vexe $vargs -progress test-fmt' + okmsg: 'All .v files can be processed with `v fmt`. NB: the result may not always be compilable, but `v fmt` should not crash.' + } + res << Command{ + line: '$vexe $vargs -progress test-self' + okmsg: 'There are no _test.v file regressions.' + } + res << Command{ + line: '$vexe $vargs -progress -W build-tools' + okmsg: 'All tools can be compiled.' + } + res << Command{ + line: '$vexe $vargs -progress -W build-examples' + okmsg: 'All examples can be compiled.' + } + res << Command{ + line: '$vexe check-md -hide-warnings .' + label: 'Check ```v ``` code examples and formatting of .MD files...' + okmsg: 'All .md files look good.' + } + res << Command{ + line: '$vexe install nedpals.args' + okmsg: '`v install` works.' + } + // NB: test that a program that depends on thirdparty libraries with its + // own #flags (tetris depends on gg, which uses sokol) can be compiled + // with -usecache: + res << Command{ + line: '$vexe -usecache examples/tetris/tetris.v' + okmsg: '`v -usecache` works.' + rmfile: 'examples/tetris/tetris' + } + $if macos || linux { + res << Command{ + line: '$vexe -o v.c cmd/v && cc -Werror v.c && rm -rf a.out' + label: 'v.c should be buildable with no warnings...' + okmsg: 'v.c can be compiled without warnings. This is good :)' + rmfile: 'v.c' + } + } + return res +} + +fn (mut cmd Command) run() { + // Changing the current directory is needed for some of the compiler tests, + // vlib/v/tests/local_test.v and vlib/v/tests/repl/repl_test.v + os.chdir(vroot) + if cmd.label != '' { + println(term.header_left(cmd.label, '*')) + } + sw := time.new_stopwatch() + cmd.ecode = os.system(cmd.line) + spent := sw.elapsed().milliseconds() + println('> Running: "$cmd.line" took: $spent ms ... ' + + if cmd.ecode != 0 { term.failed('FAILED') } else { term_highlight('OK') }) + if vtest_nocleanup { + return + } + if cmd.rmfile != '' { + mut file_existed := rm_existing(cmd.rmfile) + if os.user_os() == 'windows' { + file_existed = file_existed || rm_existing(cmd.rmfile + '.exe') + } + if !file_existed { + eprintln('Expected file did not exist: $cmd.rmfile') + cmd.ecode = 999 + } + } +} + +// try to remove a file, return if it existed before the removal attempt +fn rm_existing(path string) bool { + existed := os.exists(path) + os.rm(path) or {} + return existed +} + +fn term_highlight(s string) string { + return term.colorize(term.yellow, term.colorize(term.bold, s)) +} diff --git a/v_windows/v/old/cmd/tools/vtest-cleancode.v b/v_windows/v/old/cmd/tools/vtest-cleancode.v new file mode 100644 index 0000000..3c849b7 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vtest-cleancode.v @@ -0,0 +1,102 @@ +module main + +import os +import testing +import v.util +import arrays + +const ( + vet_known_failing_exceptions = []string{} + vet_folders = [ + 'vlib/sqlite', + 'vlib/v', + 'vlib/x/json2', + 'vlib/x/ttf', + 'cmd/v', + 'cmd/tools', + 'examples/2048', + 'examples/tetris', + 'examples/term.ui', + ] + verify_known_failing_exceptions = [ + // Handcrafted meaningful formatting of code parts (mostly arrays) + 'examples/sokol/02_cubes_glsl/cube_glsl.v', + 'examples/sokol/03_march_tracing_glsl/rt_glsl.v', + 'examples/sokol/04_multi_shader_glsl/rt_glsl.v', + 'examples/sokol/05_instancing_glsl/rt_glsl.v', + 'examples/sokol/06_obj_viewer/show_obj.v', + 'vlib/gg/m4/graphic.v', + 'vlib/gg/m4/m4_test.v', + 'vlib/gg/m4/matrix.v', + 'vlib/sqlite/orm.v' /* mut c &int -> mut c int */, + 'vlib/builtin/int_test.v' /* special number formatting that should be tested */, + // TODOs and unfixed vfmt bugs + 'vlib/builtin/int.v' /* TODO byteptr: vfmt converts `pub fn (nn byteptr) str() string {` to `nn &byte` and that conflicts with `nn byte` */, + 'vlib/builtin/string_charptr_byteptr_helpers.v' /* TODO byteptr: a temporary shim to ease the byteptr=>&byte transition */, + 'vlib/v/tests/interop_test.v', /* bad comment formatting */ + 'vlib/v/gen/js/tests/js.v', /* local `hello` fn, gets replaced with module `hello` aliased as `hl` */ + ] + vfmt_verify_list = [ + 'cmd/', + 'examples/', + 'tutorials/', + 'vlib/', + ] + vfmt_known_failing_exceptions = arrays.merge(verify_known_failing_exceptions, [ + 'vlib/regex/regex_test.v' /* contains meaningfull formatting of the test case data */, + 'vlib/readline/readline_test.v' /* vfmt eats `{ Readline }` from `import readline { Readline }` */, + 'vlib/glm/glm.v' /* `mut res &f32` => `mut res f32`, which then fails to compile */, + 'vlib/fontstash/fontstash_structs.v' /* eats fn arg names for inline callback types in struct field declarations */, + 'vlib/crypto/sha512/sha512block_generic.v' /* formatting of large constant arrays wraps to too many lines */, + 'vlib/crypto/aes/const.v' /* formatting of large constant arrays wraps to too many lines */, + ]) +) + +const ( + vexe = os.getenv('VEXE') + vroot = os.dir(vexe) + is_fix = '-fix' in os.args +) + +fn main() { + args_string := os.args[1..].join(' ') + pass_args := args_string.all_before('test-cleancode') + v_test_vetting(pass_args) +} + +fn tsession(vargs string, tool_source string, tool_cmd string, tool_args string, flist []string, slist []string) testing.TestSession { + os.chdir(vroot) + title_message := 'running $tool_cmd over most .v files' + testing.eheader(title_message) + mut test_session := testing.new_test_session('$vargs $tool_args', false) + test_session.files << flist + test_session.skip_files << slist + util.prepare_tool_when_needed(tool_source) + // note that util.prepare_tool_when_needed will put its temporary files + // in the VTMP from the test session too, so they will be cleaned up + // at the end + test_session.test() + eprintln(test_session.benchmark.total_message(title_message)) + return test_session +} + +fn v_test_vetting(vargs string) { + expanded_vet_list := util.find_all_v_files(vet_folders) or { return } + vet_session := tsession(vargs, 'vvet', 'v vet', 'vet', expanded_vet_list, vet_known_failing_exceptions) + // + fmt_cmd, fmt_args := if is_fix { 'v fmt -w', 'fmt -w' } else { 'v fmt -verify', 'fmt -verify' } + vfmt_list := util.find_all_v_files(vfmt_verify_list) or { return } + exceptions := util.find_all_v_files(vfmt_known_failing_exceptions) or { return } + verify_session := tsession(vargs, 'vfmt.v', fmt_cmd, fmt_args, vfmt_list, exceptions) + // + if vet_session.benchmark.nfail > 0 || verify_session.benchmark.nfail > 0 { + eprintln('\n') + if vet_session.benchmark.nfail > 0 { + eprintln('WARNING: `v vet` failed $vet_session.benchmark.nfail times.') + } + if verify_session.benchmark.nfail > 0 { + eprintln('WARNING: `v fmt -verify` failed $verify_session.benchmark.nfail times.') + } + exit(1) + } +} diff --git a/v_windows/v/old/cmd/tools/vtest-fmt.v b/v_windows/v/old/cmd/tools/vtest-fmt.v new file mode 100644 index 0000000..b1ebb81 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vtest-fmt.v @@ -0,0 +1,43 @@ +module main + +import os +import testing +import v.util + +const ( + known_failing_exceptions = [ + 'vlib/crypto/aes/const.v' /* const array wrapped in too many lines */, + ] +) + +fn main() { + args_string := os.args[1..].join(' ') + v_test_formatting(args_string.all_before('test-fmt')) +} + +fn v_test_formatting(vargs string) { + all_v_files := v_files() + util.prepare_tool_when_needed('vfmt.v') + testing.eheader('Run "v fmt" over all .v files') + mut vfmt_test_session := testing.new_test_session('$vargs fmt -worker', false) + vfmt_test_session.files << all_v_files + vfmt_test_session.skip_files << known_failing_exceptions + vfmt_test_session.test() + eprintln(vfmt_test_session.benchmark.total_message('running vfmt over V files')) + if vfmt_test_session.benchmark.nfail > 0 { + eprintln('\nWARNING: v fmt failed $vfmt_test_session.benchmark.nfail times.\n') + exit(1) + } +} + +fn v_files() []string { + mut files_that_can_be_formatted := []string{} + all_test_files := os.walk_ext('.', '.v') + for tfile in all_test_files { + if tfile.starts_with('./vlib/v/cgen/tests') { + continue + } + files_that_can_be_formatted << tfile + } + return files_that_can_be_formatted +} diff --git a/v_windows/v/old/cmd/tools/vtest-parser.v b/v_windows/v/old/cmd/tools/vtest-parser.v new file mode 100644 index 0000000..e1e829a --- /dev/null +++ b/v_windows/v/old/cmd/tools/vtest-parser.v @@ -0,0 +1,289 @@ +import os +import flag +import term +import time +import v.parser +import v.ast +import v.pref + +const ( + vexe = pref.vexe_path() + vroot = os.dir(vexe) + support_color = term.can_show_color_on_stderr() && term.can_show_color_on_stdout() + ecode_timeout = 101 + ecode_memout = 102 + ecode_details = map{ + -1: 'worker executable not found' + 101: 'too slow' + 102: 'too memory hungry' + } +) + +struct Context { +mut: + is_help bool + is_worker bool + is_verbose bool + is_silent bool // do not print any status/progress during processing, just failures. + is_linear bool // print linear progress log, without trying to do term cursor up + \r msg. Easier to use in a CI job + timeout_ms int + myself string // path to this executable, so the supervisor can launch worker processes + all_paths []string // all files given to the supervisor process + path string // the current path, given to a worker process + cut_index int // the cut position in the source from context.path + max_index int // the maximum index (equivalent to the file content length) + // parser context in the worker processes: + table ast.Table + scope ast.Scope + pref &pref.Preferences + period_ms int // print periodic progress + stop_print bool // stop printing the periodic progress +} + +fn main() { + mut context := process_cli_args() + if context.is_worker { + pid := os.getpid() + context.log('> worker ${pid:5} starts parsing at cut_index: ${context.cut_index:5} | $context.path') + // A worker's process job is to try to parse a single given file in context.path. + // It can crash/panic freely. + context.table = ast.new_table() + context.scope = &ast.Scope{ + parent: 0 + } + context.pref = &pref.Preferences{ + output_mode: .silent + } + mut source := os.read_file(context.path) ? + source = source[..context.cut_index] + + go fn (ms int) { + time.sleep(ms * time.millisecond) + exit(ecode_timeout) + }(context.timeout_ms) + _ := parser.parse_text(source, context.path, context.table, .skip_comments, context.pref) + context.log('> worker ${pid:5} finished parsing $context.path') + exit(0) + } else { + // The process supervisor should NOT crash/panic, unlike the workers. + // It's job, is to: + // 1) start workers + // 2) accumulate results + // 3) produce a summary at the end + context.expand_all_paths() + mut fails := 0 + mut panics := 0 + sw := time.new_stopwatch() + for path in context.all_paths { + filesw := time.new_stopwatch() + context.start_printing() + new_fails, new_panics := context.process_whole_file_in_worker(path) + fails += new_fails + panics += new_panics + context.stop_printing() + context.info('File: ${path:-30} | new_fails: ${new_fails:5} | new_panics: ${new_panics:5} | Elapsed time: ${filesw.elapsed().milliseconds()}ms') + } + non_panics := fails - panics + context.info('Total files processed: ${context.all_paths.len:5} | Errors found: ${fails:5} | Panics: ${panics:5} | Non panics: ${non_panics:5} | Elapsed time: ${sw.elapsed().milliseconds()}ms') + if fails > 0 { + exit(1) + } + exit(0) + } +} + +fn process_cli_args() &Context { + mut context := &Context{ + pref: pref.new_preferences() + } + context.myself = os.executable() + mut fp := flag.new_flag_parser(os.args_after('test-parser')) + fp.application(os.file_name(context.myself)) + fp.version('0.0.1') + fp.description('Test the V parser, by parsing each .v file in each PATH,\n' + + 'as if it was typed character by character by the user.\n' + + 'A PATH can be either a folder, or a specific .v file.\n' + + 'NB: you *have to quote* the PATH, if it contains spaces/punctuation.') + fp.arguments_description('PATH1 PATH2 ...') + fp.skip_executable() + context.is_help = fp.bool('help', `h`, false, 'Show help/usage screen.') + context.is_verbose = fp.bool('verbose', `v`, false, 'Be more verbose.') + context.is_silent = fp.bool('silent', `S`, false, 'Do not print progress at all.') + context.is_linear = fp.bool('linear', `L`, false, 'Print linear progress log. Suitable for CI.') + context.period_ms = fp.int('progress_ms', `s`, 500, 'print a status report periodically, the period is given in milliseconds.') + context.is_worker = fp.bool('worker', `w`, false, 'worker specific flag - is this a worker process, that can crash/panic.') + context.cut_index = fp.int('cut_index', `c`, 1, 'worker specific flag - cut index in the source file, everything before that will be parsed, the rest - ignored.') + context.timeout_ms = fp.int('timeout_ms', `t`, 250, 'worker specific flag - timeout in ms; a worker taking longer, will self terminate.') + context.path = fp.string('path', `p`, '', 'worker specific flag - path to the current source file, which will be parsed.') + // + if context.is_help { + println(fp.usage()) + exit(0) + } + context.all_paths = fp.finalize() or { + context.error(err.msg) + exit(1) + } + if !context.is_worker && context.all_paths.len == 0 { + println(fp.usage()) + exit(0) + } + return context +} + +// //////////////// +fn bold(msg string) string { + if !support_color { + return msg + } + return term.bold(msg) +} + +fn red(msg string) string { + if !support_color { + return msg + } + return term.red(msg) +} + +fn yellow(msg string) string { + if !support_color { + return msg + } + return term.yellow(msg) +} + +fn (mut context Context) info(msg string) { + println(msg) +} + +fn (mut context Context) log(msg string) { + if context.is_verbose { + label := yellow('info') + ts := time.now().format_ss_micro() + eprintln('$label: $ts | $msg') + } +} + +fn (mut context Context) error(msg string) { + label := red('error') + eprintln('$label: $msg') +} + +fn (mut context Context) expand_all_paths() { + context.log('> context.all_paths before: $context.all_paths') + mut files := []string{} + for path in context.all_paths { + if os.is_dir(path) { + files << os.walk_ext(path, '.v') + files << os.walk_ext(path, '.vsh') + continue + } + if !path.ends_with('.v') && !path.ends_with('.vv') && !path.ends_with('.vsh') { + context.error('`v test-parser` can only be used on .v/.vv/.vsh files.\nOffending file: "$path".') + continue + } + if !os.exists(path) { + context.error('"$path" does not exist.') + continue + } + files << path + } + context.all_paths = files + context.log('> context.all_paths after: $context.all_paths') +} + +fn (mut context Context) process_whole_file_in_worker(path string) (int, int) { + context.path = path // needed for the progress bar + context.log('> context.process_whole_file_in_worker path: $path') + if !(os.is_file(path) && os.is_readable(path)) { + context.error('$path is not readable') + return 1, 0 + } + source := os.read_file(path) or { '' } + if source == '' { + // an empty file is a valid .v file + return 0, 0 + } + len := source.len - 1 + mut fails := 0 + mut panics := 0 + context.max_index = len + for i in 0 .. len { + verbosity := if context.is_verbose { '-v' } else { '' } + context.cut_index = i // needed for the progress bar + cmd := '"$context.myself" $verbosity --worker --timeout_ms ${context.timeout_ms:5} --cut_index ${i:5} --path "$path" ' + context.log(cmd) + mut res := os.execute(cmd) + context.log('worker exit_code: $res.exit_code | worker output:\n$res.output') + if res.exit_code != 0 { + fails++ + mut is_panic := false + if res.output.contains('V panic:') { + is_panic = true + panics++ + } + part := source[..i] + line := part.count('\n') + 1 + last_line := part.all_after_last('\n') + col := last_line.len + err := if is_panic { + red('parser failure: panic') + } else { + red('parser failure: crash, ${ecode_details[res.exit_code]}') + } + path_to_line := bold('$path:$line:$col:') + err_line := last_line.trim_left('\t') + println('$path_to_line $err') + println('\t$line | $err_line') + println('') + eprintln(res.output) + } + } + return fails, panics +} + +fn (mut context Context) start_printing() { + context.stop_print = false + if !context.is_linear && !context.is_silent { + println('\n') + } + go context.print_periodic_status() +} + +fn (mut context Context) stop_printing() { + context.stop_print = true + time.sleep(time.millisecond * context.period_ms / 5) +} + +fn (mut context Context) print_status() { + if context.is_silent { + return + } + if (context.cut_index == 1) && (context.max_index == 0) { + return + } + msg := '> ${context.path:-30} | index: ${context.cut_index:5}/${context.max_index - 1:5}' + if context.is_linear { + eprintln(msg) + return + } + term.cursor_up(1) + eprint('\r $msg\n') +} + +fn (mut context Context) print_periodic_status() { + context.print_status() + mut printed_at_least_once := false + for !context.stop_print { + context.print_status() + for i := 0; i < 10 && !context.stop_print; i++ { + time.sleep(time.millisecond * context.period_ms / 10) + if context.cut_index > 50 && !printed_at_least_once { + context.print_status() + printed_at_least_once = true + } + } + } + context.print_status() +} diff --git a/v_windows/v/old/cmd/tools/vtest-self.v b/v_windows/v/old/cmd/tools/vtest-self.v new file mode 100644 index 0000000..99cc83c --- /dev/null +++ b/v_windows/v/old/cmd/tools/vtest-self.v @@ -0,0 +1,220 @@ +module main + +import os +import testing +import v.pref + +const github_job = os.getenv('GITHUB_JOB') + +const ( + skip_test_files = [ + 'vlib/context/deadline_test.v' /* sometimes blocks */, + 'vlib/mysql/mysql_orm_test.v' /* mysql not installed */, + 'vlib/pg/pg_orm_test.v' /* pg not installed */, + ] + skip_fsanitize_too_slow = [ + // These tests are too slow to be run in the CI on each PR/commit + // in the sanitized modes: + 'vlib/v/compiler_errors_test.v', + 'vlib/v/doc/doc_test.v', + 'vlib/v/fmt/fmt_test.v', + 'vlib/v/fmt/fmt_keep_test.v', + 'vlib/v/fmt/fmt_vlib_test.v', + 'vlib/v/live/live_test.v', + 'vlib/v/parser/v_parser_test.v', + 'vlib/v/scanner/scanner_test.v', + 'vlib/v/tests/inout/compiler_test.v', + 'vlib/v/tests/prod_test.v', + 'vlib/v/tests/profile/profile_test.v', + 'vlib/v/tests/repl/repl_test.v', + 'vlib/v/tests/valgrind/valgrind_test.v', + ] + skip_with_fsanitize_memory = [ + 'vlib/net/tcp_simple_client_server_test.v', + 'vlib/net/http/cookie_test.v', + 'vlib/net/http/http_test.v', + 'vlib/net/http/status_test.v', + 'vlib/net/http/http_httpbin_test.v', + 'vlib/net/http/header_test.v', + 'vlib/net/udp_test.v', + 'vlib/net/tcp_test.v', + 'vlib/orm/orm_test.v', + 'vlib/sqlite/sqlite_test.v', + 'vlib/sqlite/sqlite_orm_test.v', + 'vlib/v/tests/orm_sub_struct_test.v', + 'vlib/v/tests/orm_sub_array_struct_test.v', + 'vlib/vweb/tests/vweb_test.v', + 'vlib/vweb/request_test.v', + 'vlib/net/http/request_test.v', + 'vlib/vweb/route_test.v', + 'vlib/net/websocket/websocket_test.v', + 'vlib/crypto/rand/crypto_rand_read_test.v', + ] + skip_with_fsanitize_address = [ + 'vlib/net/websocket/websocket_test.v', + ] + skip_with_fsanitize_undefined = [ + 'do_not_remove', + ] + skip_with_werror = [ + 'do_not_remove', + ] + skip_with_asan_compiler = [ + 'do_not_remove', + ] + skip_with_msan_compiler = [ + 'do_not_remove', + ] + skip_on_musl = [ + 'vlib/v/tests/profile/profile_test.v', + ] + skip_on_ubuntu_musl = [ + //'vlib/v/gen/js/jsgen_test.v', + 'vlib/net/http/cookie_test.v', + 'vlib/net/http/http_test.v', + 'vlib/net/http/status_test.v', + 'vlib/net/websocket/ws_test.v', + 'vlib/sqlite/sqlite_test.v', + 'vlib/sqlite/sqlite_orm_test.v', + 'vlib/orm/orm_test.v', + 'vlib/v/tests/orm_sub_struct_test.v', + 'vlib/v/tests/orm_sub_array_struct_test.v', + 'vlib/clipboard/clipboard_test.v', + 'vlib/vweb/tests/vweb_test.v', + 'vlib/vweb/request_test.v', + 'vlib/net/http/request_test.v', + 'vlib/vweb/route_test.v', + 'vlib/net/websocket/websocket_test.v', + 'vlib/net/http/http_httpbin_test.v', + 'vlib/net/http/header_test.v', + ] + skip_on_linux = [ + 'do_not_remove', + ] + skip_on_non_linux = [ + 'do_not_remove', + ] + skip_on_windows = [ + 'vlib/orm/orm_test.v', + 'vlib/v/tests/orm_sub_struct_test.v', + 'vlib/net/websocket/ws_test.v', + 'vlib/net/unix/unix_test.v', + 'vlib/net/websocket/websocket_test.v', + 'vlib/vweb/tests/vweb_test.v', + 'vlib/vweb/request_test.v', + 'vlib/net/http/request_test.v', + 'vlib/vweb/route_test.v', + ] + skip_on_non_windows = [ + 'do_not_remove', + ] + skip_on_macos = [ + 'do_not_remove', + ] + skip_on_non_macos = [ + 'do_not_remove', + ] +) + +// NB: musl misses openssl, thus the http tests can not be done there +// NB: http_httpbin_test.v: fails with 'cgen error: json: map_string_string is not struct' +fn main() { + vexe := pref.vexe_path() + vroot := os.dir(vexe) + os.chdir(vroot) + args := os.args.clone() + args_string := args[1..].join(' ') + cmd_prefix := args_string.all_before('test-self') + title := 'testing vlib' + all_test_files := os.walk_ext(os.join_path(vroot, 'vlib'), '_test.v') + testing.eheader(title) + mut tsession := testing.new_test_session(cmd_prefix, true) + tsession.files << all_test_files.filter(!it.contains('testdata' + os.path_separator)) + tsession.skip_files << skip_test_files + + if github_job == 'windows-tcc' { + // TODO: fix these ASAP + tsession.skip_files << 'vlib/net/tcp_test.v' + tsession.skip_files << 'vlib/net/udp_test.v' + } + + mut werror := false + mut sanitize_memory := false + mut sanitize_address := false + mut sanitize_undefined := false + mut asan_compiler := false + mut msan_compiler := false + for arg in args { + if arg.contains('-asan-compiler') { + asan_compiler = true + } + if arg.contains('-msan-compiler') { + msan_compiler = true + } + if arg.contains('-Werror') || arg.contains('-cstrict') { + werror = true + } + if arg.contains('-fsanitize=memory') { + sanitize_memory = true + } + if arg.contains('-fsanitize=address') { + sanitize_address = true + } + if arg.contains('-fsanitize=undefined') { + sanitize_undefined = true + } + } + if os.getenv('VTEST_RUN_FSANITIZE_TOO_SLOW').len == 0 + && (sanitize_undefined || sanitize_memory || sanitize_address) { + tsession.skip_files << skip_fsanitize_too_slow + } + if werror { + tsession.skip_files << skip_with_werror + } + if sanitize_memory { + tsession.skip_files << skip_with_fsanitize_memory + } + if sanitize_address { + tsession.skip_files << skip_with_fsanitize_address + } + if sanitize_undefined { + tsession.skip_files << skip_with_fsanitize_undefined + } + if asan_compiler { + tsession.skip_files << skip_with_asan_compiler + } + if msan_compiler { + tsession.skip_files << skip_with_msan_compiler + } + // println(tsession.skip_files) + if os.getenv('V_CI_MUSL').len > 0 { + tsession.skip_files << skip_on_musl + } + if os.getenv('V_CI_UBUNTU_MUSL').len > 0 { + tsession.skip_files << skip_on_ubuntu_musl + } + $if !linux { + tsession.skip_files << skip_on_non_linux + } + $if linux { + tsession.skip_files << skip_on_linux + } + $if windows { + tsession.skip_files << skip_on_windows + } + $if !windows { + tsession.skip_files << skip_on_non_windows + } + $if macos { + tsession.skip_files << skip_on_macos + } + $if !macos { + tsession.skip_files << skip_on_non_macos + } + tsession.test() + eprintln(tsession.benchmark.total_message(title)) + if tsession.benchmark.nfail > 0 { + eprintln('\nWARNING: failed $tsession.benchmark.nfail times.\n') + exit(1) + } +} diff --git a/v_windows/v/old/cmd/tools/vtest.v b/v_windows/v/old/cmd/tools/vtest.v new file mode 100644 index 0000000..d116d29 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vtest.v @@ -0,0 +1,135 @@ +module main + +import os +import os.cmdline +import testing +import v.pref + +fn main() { + args := os.args.clone() + if os.args.last() == 'test' { + show_usage() + return + } + args_to_executable := args[1..] + args_before := cmdline.options_before(args_to_executable, ['test']) + args_after := cmdline.options_after(args_to_executable, ['test']) + if args_after.join(' ') == 'v' { + eprintln('`v test v` has been deprecated.') + eprintln('Use `v test-all` instead.') + exit(1) + } + backend_pos := args_before.index('-b') + backend := if backend_pos == -1 { '.c' } else { args_before[backend_pos + 1] } // this giant mess because closures are not implemented + + mut ts := testing.new_test_session(args_before.join(' '), true) + for targ in args_after { + if os.is_dir(targ) { + // Fetch all tests from the directory + files, skip_files := should_test_dir(targ.trim_right(os.path_separator), backend) + ts.files << files + ts.skip_files << skip_files + continue + } else if os.exists(targ) { + match should_test(targ, backend) { + .test { + ts.files << targ + continue + } + .skip { + ts.files << targ + ts.skip_files << targ + continue + } + .ignore {} + } + } else { + eprintln('\nUnrecognized test file `$targ`.\n `v test` can only be used with folders and/or _test.v files.\n') + show_usage() + exit(1) + } + } + testing.header('Testing...') + ts.test() + println(ts.benchmark.total_message('all V _test.v files')) + if ts.failed { + exit(1) + } +} + +fn show_usage() { + println('Usage:') + println(' A)') + println(' v test folder/ : run all v tests in the given folder.') + println(' v -stats test folder/ : the same, but print more stats.') + println(' B)') + println(' v test file_test.v : run test functions in a given test file.') + println(' v -stats test file_test.v : as above, but with more stats.') + println(' NB: you can also give many and mixed folder/ file_test.v arguments after `v test` .') + println('') +} + +pub fn should_test_dir(path string, backend string) ([]string, []string) { // return is (files, skip_files) + mut files := os.ls(path) or { return []string{}, []string{} } + mut local_path_separator := os.path_separator + if path.ends_with(os.path_separator) { + local_path_separator = '' + } + mut res_files := []string{} + mut skip_files := []string{} + for file in files { + p := path + local_path_separator + file + if os.is_dir(p) && !os.is_link(p) { + if file == 'testdata' { + continue + } + ret_files, ret_skip_files := should_test_dir(p, backend) + res_files << ret_files + skip_files << ret_skip_files + } else if os.exists(p) { + match should_test(p, backend) { + .test { + res_files << p + } + .skip { + res_files << p + skip_files << p + } + .ignore {} + } + } + } + return res_files, skip_files +} + +enum ShouldTestStatus { + test // do test + skip + ignore +} + +fn should_test(path string, backend string) ShouldTestStatus { + if path.ends_with('_test.v') { + return .test + } + if path.ends_with('.v') && path.count('.') == 2 { + if !path.all_before_last('.v').all_before_last('.').ends_with('_test') { + return .ignore + } + backend_arg := path.all_before_last('.v').all_after_last('.') + arch := pref.arch_from_string(backend_arg) or { pref.Arch._auto } + if arch == pref.get_host_arch() { + return .test + } else if arch == ._auto { + if backend_arg == 'c' { // .c.v + return if backend == 'c' { ShouldTestStatus.test } else { ShouldTestStatus.skip } + } + if backend_arg == 'js' { + return if backend == 'js' { ShouldTestStatus.test } else { ShouldTestStatus.skip } + } + } else { + return .skip + } + } + return .ignore +} diff --git a/v_windows/v/old/cmd/tools/vtracev.v b/v_windows/v/old/cmd/tools/vtracev.v new file mode 100644 index 0000000..1666188 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vtracev.v @@ -0,0 +1,17 @@ +module main + +import os +import v.pref + +fn main() { + vexe := pref.vexe_path() + vroot := os.dir(vexe) + os.chdir(vroot) + os.setenv('VCOLORS', 'always', true) + self_idx := os.args.index('tracev') + args := os.args[1..self_idx] + args_str := args.join(' ') + options := if args.len > 0 { '($args_str)' } else { '' } + println('Compiling a `tracev` executable ${options}...') + os.system('$vexe -cg -d trace_parser -d trace_checker -d trace_gen -o tracev $args_str cmd/v') +} diff --git a/v_windows/v/old/cmd/tools/vup.exe b/v_windows/v/old/cmd/tools/vup.exe new file mode 100644 index 0000000..cdaa765 Binary files /dev/null and b/v_windows/v/old/cmd/tools/vup.exe differ diff --git a/v_windows/v/old/cmd/tools/vup.v b/v_windows/v/old/cmd/tools/vup.v new file mode 100644 index 0000000..db6684a --- /dev/null +++ b/v_windows/v/old/cmd/tools/vup.v @@ -0,0 +1,164 @@ +module main + +import os +import v.pref +import v.util.version +import v.util.recompilation + +struct App { + is_verbose bool + is_prod bool + vexe string + vroot string +} + +fn new_app() App { + vexe := os.real_path(pref.vexe_path()) + vroot := os.dir(vexe) + return App{ + is_verbose: '-v' in os.args + is_prod: '-prod' in os.args + vexe: vexe + vroot: vroot + } +} + +fn main() { + app := new_app() + recompilation.must_be_enabled(app.vroot, 'Please install V from source, to use `v up` .') + os.chdir(app.vroot) + println('Updating V...') + app.update_from_master() + v_hash := version.githash(false) + current_hash := version.githash(true) + // println(v_hash) + // println(current_hash) + if v_hash == current_hash { + app.show_current_v_version() + return + } + $if windows { + app.backup('cmd/tools/vup.exe') + } + app.recompile_v() + app.recompile_vup() + app.show_current_v_version() +} + +fn (app App) vprintln(s string) { + if app.is_verbose { + println(s) + } +} + +fn (app App) update_from_master() { + app.vprintln('> updating from master ...') + if !os.exists('.git') { + // initialize as if it had been cloned + app.git_command('init') + app.git_command('remote add origin https://github.com/vlang/v') + app.git_command('fetch') + app.git_command('reset --hard origin/master') + app.git_command('clean --quiet -xdf --exclude v.exe --exclude cmd/tools/vup.exe') + } else { + // pull latest + app.git_command('pull https://github.com/vlang/v master') + } +} + +fn (app App) recompile_v() { + // NB: app.vexe is more reliable than just v (which may be a symlink) + opts := if app.is_prod { '-prod' } else { '' } + vself := '"$app.vexe" $opts self' + app.vprintln('> recompiling v itself with `$vself` ...') + self_result := os.execute(vself) + if self_result.exit_code == 0 { + println(self_result.output.trim_space()) + return + } else { + app.vprintln('`$vself` failed, running `make`...') + app.vprintln(self_result.output.trim_space()) + } + app.make(vself) +} + +fn (app App) recompile_vup() { + vup_result := os.execute('"$app.vexe" -g cmd/tools/vup.v') + if vup_result.exit_code != 0 { + eprintln('recompiling vup.v failed:') + eprintln(vup_result.output) + } +} + +fn (app App) make(vself string) { + mut make := 'make' + $if windows { + make = 'make.bat' + } + make_result := os.execute(make) + if make_result.exit_code != 0 { + eprintln('> $make failed:') + eprintln('> make output:') + eprintln(make_result.output) + return + } + app.vprintln(make_result.output) +} + +fn (app App) show_current_v_version() { + vout := os.execute('"$app.vexe" version') + if vout.exit_code >= 0 { + mut vversion := vout.output.trim_space() + if vout.exit_code == 0 { + latest_v_commit := vversion.split(' ').last().all_after('.') + latest_v_commit_time := os.execute('git show -s --format=%ci $latest_v_commit') + if latest_v_commit_time.exit_code == 0 { + vversion += ', timestamp: ' + latest_v_commit_time.output.trim_space() + } + } + println('Current V version:') + println(vversion) + } +} + +fn (app App) backup(file string) { + backup_file := '${file}_old.exe' + if os.exists(backup_file) { + os.rm(backup_file) or { eprintln('failed removing $backup_file: $err.msg') } + } + os.mv(file, backup_file) or { eprintln('failed moving $file: $err.msg') } +} + +fn (app App) git_command(command string) { + app.vprintln('git_command: git $command') + git_result := os.execute('git $command') + if git_result.exit_code < 0 { + app.get_git() + // Try it again with (maybe) git installed + os.execute_or_exit('git $command') + } + if git_result.exit_code != 0 { + eprintln(git_result.output) + exit(1) + } + app.vprintln(git_result.output) +} + +fn (app App) get_git() { + $if windows { + println('Downloading git 32 bit for Windows, please wait.') + // We'll use 32 bit because maybe someone out there is using 32-bit windows + res_download := os.execute('bitsadmin.exe /transfer "vgit" https://github.com/git-for-windows/git/releases/download/v2.30.0.windows.2/Git-2.30.0.2-32-bit.exe "$os.getwd()/git32.exe"') + if res_download.exit_code != 0 { + eprintln('Unable to install git automatically: please install git manually') + panic(res_download.output) + } + res_git32 := os.execute('$os.getwd()/git32.exe') + if res_git32.exit_code != 0 { + eprintln('Unable to install git automatically: please install git manually') + panic(res_git32.output) + } + } $else { // Probably some kind of *nix, usually need to get using a package manager. + eprintln("error: Install `git` using your system's package manager") + } +} diff --git a/v_windows/v/old/cmd/tools/vvet/tests/array_init_one_val.out b/v_windows/v/old/cmd/tools/vvet/tests/array_init_one_val.out new file mode 100644 index 0000000..e10d511 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vvet/tests/array_init_one_val.out @@ -0,0 +1,2 @@ +cmd/tools/vvet/tests/array_init_one_val.vv:2: error: Use `var == value` instead of `var in [value]` +NB: You can run `v fmt -w file.v` to fix these errors automatically diff --git a/v_windows/v/old/cmd/tools/vvet/tests/array_init_one_val.vv b/v_windows/v/old/cmd/tools/vvet/tests/array_init_one_val.vv new file mode 100644 index 0000000..2aa3514 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vvet/tests/array_init_one_val.vv @@ -0,0 +1,5 @@ +fn main() { + if 1 in [1] { + println('hello world') + } +} diff --git a/v_windows/v/old/cmd/tools/vvet/tests/indent_with_space.out b/v_windows/v/old/cmd/tools/vvet/tests/indent_with_space.out new file mode 100644 index 0000000..b307e20 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vvet/tests/indent_with_space.out @@ -0,0 +1,6 @@ +cmd/tools/vvet/tests/indent_with_space.vv:2: error: Looks like you are using spaces for indentation. +cmd/tools/vvet/tests/indent_with_space.vv:10: error: Looks like you are using spaces for indentation. +cmd/tools/vvet/tests/indent_with_space.vv:17: error: Looks like you are using spaces for indentation. +cmd/tools/vvet/tests/indent_with_space.vv:20: error: Looks like you are using spaces for indentation. +cmd/tools/vvet/tests/indent_with_space.vv:22: error: Looks like you are using spaces for indentation. +NB: You can run `v fmt -w file.v` to fix these errors automatically diff --git a/v_windows/v/old/cmd/tools/vvet/tests/indent_with_space.vv b/v_windows/v/old/cmd/tools/vvet/tests/indent_with_space.vv new file mode 100644 index 0000000..9b466ef --- /dev/null +++ b/v_windows/v/old/cmd/tools/vvet/tests/indent_with_space.vv @@ -0,0 +1,24 @@ +fn main() { + _ = 1 == 2 +} + +fn block_comments() { + /* tab to indent the comment + spaces before + also spaces before + same here */ + /* spaces for comment indentation (ouch) + and inside too + */ +} + +fn space_inside_strings() { + // Plain strings + str := "Bad space usage for variable indentation. + Here it's fine. + Here too." + str2 := 'linebreak and space\n inside' + // String interpolation + si1 := 'Error here $foo + and not here' +} diff --git a/v_windows/v/old/cmd/tools/vvet/tests/module_file_test.out b/v_windows/v/old/cmd/tools/vvet/tests/module_file_test.out new file mode 100644 index 0000000..b033e71 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vvet/tests/module_file_test.out @@ -0,0 +1,5 @@ +cmd/tools/vvet/tests/module_file_test.vv:7: warning: Function documentation seems to be missing for "pub fn foo() string". +cmd/tools/vvet/tests/module_file_test.vv:13: warning: A function name is missing from the documentation of "pub fn bar() string". +cmd/tools/vvet/tests/module_file_test.vv:35: warning: Function documentation seems to be missing for "pub fn (f Foo) foo() string". +cmd/tools/vvet/tests/module_file_test.vv:46: warning: A function name is missing from the documentation of "pub fn (f Foo) fooo() string". +cmd/tools/vvet/tests/module_file_test.vv:52: warning: The documentation for "pub fn (f Foo) boo() string" seems incomplete. \ No newline at end of file diff --git a/v_windows/v/old/cmd/tools/vvet/tests/module_file_test.vv b/v_windows/v/old/cmd/tools/vvet/tests/module_file_test.vv new file mode 100644 index 0000000..f0f5b24 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vvet/tests/module_file_test.vv @@ -0,0 +1,55 @@ +module foo + +struct Foo { + foo int +} + +pub fn foo() string { + // Missing doc + return 'foo' +} + +// foo does bar +pub fn bar() string { + // not using convention style: '// ' + return 'bar' +} + +// fooo does x +pub fn fooo() string { + // Documented + return 'fooo' +} + +// booo does x +fn booo() string { + // Documented, but not pub + return 'booo' +} + +fn boo() string { + // Missing doc + return 'boo' +} + +pub fn (f Foo) foo() string { + // Missing doc + return f.fo() +} + +fn (f Foo) fo() string { + // Missing doc, but not pub + return 'foo' +} + +// wrong doc +pub fn (f Foo) fooo() string { + // not using convention + return f.fo() +} + +// boo +pub fn (f Foo) boo() string { + // Incomplete doc + return f.fo() +} diff --git a/v_windows/v/old/cmd/tools/vvet/tests/parens_space_a.out b/v_windows/v/old/cmd/tools/vvet/tests/parens_space_a.out new file mode 100644 index 0000000..dbda99a --- /dev/null +++ b/v_windows/v/old/cmd/tools/vvet/tests/parens_space_a.out @@ -0,0 +1,2 @@ +cmd/tools/vvet/tests/parens_space_a.vv:1: error: Looks like you are adding a space after `(` +NB: You can run `v fmt -w file.v` to fix these errors automatically diff --git a/v_windows/v/old/cmd/tools/vvet/tests/parens_space_a.vv b/v_windows/v/old/cmd/tools/vvet/tests/parens_space_a.vv new file mode 100644 index 0000000..2b3b508 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vvet/tests/parens_space_a.vv @@ -0,0 +1,4 @@ +fn main() { + _ = 1 + ( 1 + 2) +} + diff --git a/v_windows/v/old/cmd/tools/vvet/tests/parens_space_b.out b/v_windows/v/old/cmd/tools/vvet/tests/parens_space_b.out new file mode 100644 index 0000000..d1d8791 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vvet/tests/parens_space_b.out @@ -0,0 +1,2 @@ +cmd/tools/vvet/tests/parens_space_b.vv:1: error: Looks like you are adding a space before `)` +NB: You can run `v fmt -w file.v` to fix these errors automatically diff --git a/v_windows/v/old/cmd/tools/vvet/tests/parens_space_b.vv b/v_windows/v/old/cmd/tools/vvet/tests/parens_space_b.vv new file mode 100644 index 0000000..9ea8932 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vvet/tests/parens_space_b.vv @@ -0,0 +1,4 @@ +fn main() { + _ = 1 + (1 + 2 ) +} + diff --git a/v_windows/v/old/cmd/tools/vvet/tests/trailing_space.out b/v_windows/v/old/cmd/tools/vvet/tests/trailing_space.out new file mode 100644 index 0000000..1899a21 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vvet/tests/trailing_space.out @@ -0,0 +1,7 @@ +cmd/tools/vvet/tests/trailing_space.vv:5: error: Looks like you have trailing whitespace. +cmd/tools/vvet/tests/trailing_space.vv:6: error: Looks like you have trailing whitespace. +cmd/tools/vvet/tests/trailing_space.vv:7: error: Looks like you have trailing whitespace. +cmd/tools/vvet/tests/trailing_space.vv:8: error: Looks like you have trailing whitespace. +cmd/tools/vvet/tests/trailing_space.vv:9: error: Looks like you have trailing whitespace. +cmd/tools/vvet/tests/trailing_space.vv:13: error: Looks like you have trailing whitespace. +cmd/tools/vvet/tests/trailing_space.vv:15: error: Looks like you have trailing whitespace. diff --git a/v_windows/v/old/cmd/tools/vvet/tests/trailing_space.vv b/v_windows/v/old/cmd/tools/vvet/tests/trailing_space.vv new file mode 100644 index 0000000..4fe733e --- /dev/null +++ b/v_windows/v/old/cmd/tools/vvet/tests/trailing_space.vv @@ -0,0 +1,16 @@ +// NB: This file has and *should* have trailing spaces. +// When making changes, please ensure they are not removed. + +fn after_comments() { + // spaces after line comments give errors + /* + in block comments + too + */ +} + +fn main() { + var := 'error about the spaces right there' + no_err := "inside multi line strings it's fine. +but not after" +} diff --git a/v_windows/v/old/cmd/tools/vvet/vet_test.v b/v_windows/v/old/cmd/tools/vvet/vet_test.v new file mode 100644 index 0000000..3291f20 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vvet/vet_test.v @@ -0,0 +1,72 @@ +import os +import rand +import term +import v.util.vtest +import v.util.diff + +const diff_cmd = find_diff_cmd() + +fn find_diff_cmd() string { + res := diff.find_working_diff_command() or { '' } + return res +} + +fn test_vet() { + vexe := os.getenv('VEXE') + vroot := os.dir(vexe) + os.chdir(vroot) + test_dir := 'cmd/tools/vvet/tests' + tests := get_tests_in_dir(test_dir) + fails := check_path(vexe, test_dir, tests) + assert fails == 0 +} + +fn get_tests_in_dir(dir string) []string { + files := os.ls(dir) or { panic(err) } + mut tests := files.filter(it.ends_with('.vv')) + tests.sort() + return tests +} + +fn check_path(vexe string, dir string, tests []string) int { + mut nb_fail := 0 + paths := vtest.filter_vtest_only(tests, basepath: dir) + for path in paths { + program := path + print(path + ' ') + // -force is needed so that `v vet` would not skip the regression files + res := os.execute('$vexe vet -force -nocolor $program') + if res.exit_code < 0 { + panic(res.output) + } + mut expected := os.read_file(program.replace('.vv', '') + '.out') or { panic(err) } + expected = clean_line_endings(expected) + found := clean_line_endings(res.output) + if expected != found { + println(term.red('FAIL')) + println('============') + println('expected:') + println(expected) + println('============') + println('found:') + println(found) + println('============\n') + println('diff:') + println(diff.color_compare_strings(diff_cmd, rand.ulid(), found, expected)) + println('============\n') + nb_fail++ + } else { + println(term.green('OK')) + } + } + return nb_fail +} + +fn clean_line_endings(s string) string { + mut res := s.trim_space() + res = res.replace(' \n', '\n') + res = res.replace(' \r\n', '\n') + res = res.replace('\r\n', '\n') + res = res.trim('\n') + return res +} diff --git a/v_windows/v/old/cmd/tools/vvet/vvet.v b/v_windows/v/old/cmd/tools/vvet/vvet.v new file mode 100644 index 0000000..fd04b40 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vvet/vvet.v @@ -0,0 +1,256 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license that can be found in the LICENSE file. +module main + +import os +import os.cmdline +import v.vet +import v.pref +import v.parser +import v.token +import v.ast +import term + +struct Vet { + opt Options +mut: + errors []vet.Error + warns []vet.Error + file string +} + +struct Options { + is_force bool + is_werror bool + is_verbose bool + show_warnings bool + use_color bool +} + +const term_colors = term.can_show_color_on_stderr() + +fn main() { + vet_options := cmdline.options_after(os.args, ['vet']) + mut vt := Vet{ + opt: Options{ + is_force: '-force' in vet_options + is_werror: '-W' in vet_options + is_verbose: '-verbose' in vet_options || '-v' in vet_options + show_warnings: '-hide-warnings' !in vet_options && '-w' !in vet_options + use_color: '-color' in vet_options || (term_colors && '-nocolor' !in vet_options) + } + } + mut paths := cmdline.only_non_options(vet_options) + vtmp := os.getenv('VTMP') + if vtmp != '' { + // `v test-cleancode` passes also `-o tmpfolder` as well as all options in VFLAGS + paths = paths.filter(!it.starts_with(vtmp)) + } + for path in paths { + if !os.exists(path) { + eprintln('File/folder $path does not exist') + continue + } + if os.is_file(path) { + vt.vet_file(path) + } + if os.is_dir(path) { + vt.vprintln("vetting folder: '$path' ...") + vfiles := os.walk_ext(path, '.v') + vvfiles := os.walk_ext(path, '.vv') + mut files := []string{} + files << vfiles + files << vvfiles + for file in files { + vt.vet_file(file) + } + } + } + vfmt_err_count := vt.errors.filter(it.fix == .vfmt).len + if vt.opt.show_warnings { + for w in vt.warns { + eprintln(vt.e2string(w)) + } + } + for err in vt.errors { + eprintln(vt.e2string(err)) + } + if vfmt_err_count > 0 { + eprintln('NB: You can run `v fmt -w file.v` to fix these errors automatically') + } + if vt.errors.len > 0 { + exit(1) + } +} + +// vet_file vets the file read from `path`. +fn (mut vt Vet) vet_file(path string) { + if path.contains('/tests/') && !vt.opt.is_force { + // skip all /tests/ files, since usually their content is not + // important enough to be documented/vetted, and they may even + // contain intentionally invalid code. + vt.vprintln("skipping test file: '$path' ...") + return + } + vt.file = path + mut prefs := pref.new_preferences() + prefs.is_vet = true + prefs.is_vsh = path.ends_with('.vsh') + table := ast.new_table() + vt.vprintln("vetting file '$path'...") + _, errors := parser.parse_vet_file(path, table, prefs) + // Transfer errors from scanner and parser + vt.errors << errors + // Scan each line in file for things to improve + source_lines := os.read_lines(vt.file) or { []string{} } + for lnumber, line in source_lines { + vt.vet_line(source_lines, line, lnumber) + } +} + +// vet_line vets the contents of `line` from `vet.file`. +fn (mut vt Vet) vet_line(lines []string, line string, lnumber int) { + // Vet public functions + if line.starts_with('pub fn') || (line.starts_with('fn ') && !(line.starts_with('fn C.') + || line.starts_with('fn main'))) { + // Scan function declarations for missing documentation + is_pub_fn := line.starts_with('pub fn') + if lnumber > 0 { + collect_tags := fn (line string) []string { + mut cleaned := line.all_before('/') + cleaned = cleaned.replace_each(['[', '', ']', '', ' ', '']) + return cleaned.split(',') + } + ident_fn_name := fn (line string) string { + mut fn_idx := line.index(' fn ') or { return '' } + if line.len < fn_idx + 5 { + return '' + } + mut tokens := line[fn_idx + 4..].split(' ') + // Skip struct identifier + if tokens.first().starts_with('(') { + fn_idx = line.index(')') or { return '' } + tokens = line[fn_idx..].split(' ') + if tokens.len > 1 { + tokens = [tokens[1]] + } + } + if tokens.len > 0 { + return tokens[0].all_before('(') + } + return '' + } + mut line_above := lines[lnumber - 1] + mut tags := []string{} + if !line_above.starts_with('//') { + mut grab := true + for j := lnumber - 1; j >= 0; j-- { + prev_line := lines[j] + if prev_line.contains('}') { // We've looked back to the above scope, stop here + break + } else if prev_line.starts_with('[') { + tags << collect_tags(prev_line) + continue + } else if prev_line.starts_with('//') { // Single-line comment + grab = false + break + } + } + if grab { + clean_line := line.all_before_last('{').trim(' ') + if is_pub_fn { + vt.warn('Function documentation seems to be missing for "$clean_line".', + lnumber, .doc) + } + } + } else { + fn_name := ident_fn_name(line) + mut grab := true + for j := lnumber - 1; j >= 0; j-- { + prev_line := lines[j] + if prev_line.contains('}') { // We've looked back to the above scope, stop here + break + } else if prev_line.starts_with('// $fn_name ') { + grab = false + break + } else if prev_line.starts_with('// $fn_name') { + grab = false + if is_pub_fn { + clean_line := line.all_before_last('{').trim(' ') + vt.warn('The documentation for "$clean_line" seems incomplete.', + lnumber, .doc) + } + break + } else if prev_line.starts_with('[') { + tags << collect_tags(prev_line) + continue + } else if prev_line.starts_with('//') { // Single-line comment + continue + } + } + if grab { + clean_line := line.all_before_last('{').trim(' ') + if is_pub_fn { + vt.warn('A function name is missing from the documentation of "$clean_line".', + lnumber, .doc) + } + } + } + } + } +} + +fn (vt &Vet) vprintln(s string) { + if !vt.opt.is_verbose { + return + } + println(s) +} + +fn (vt &Vet) e2string(err vet.Error) string { + mut kind := '$err.kind:' + mut location := '$err.file_path:$err.pos.line_nr:' + if vt.opt.use_color { + kind = match err.kind { + .warning { term.magenta(kind) } + .error { term.red(kind) } + } + kind = term.bold(kind) + location = term.bold(location) + } + return '$location $kind $err.message' +} + +fn (mut vt Vet) error(msg string, line int, fix vet.FixKind) { + pos := token.Position{ + line_nr: line + 1 + } + vt.errors << vet.Error{ + message: msg + file_path: vt.file + pos: pos + kind: .error + fix: fix + typ: .default + } +} + +fn (mut vt Vet) warn(msg string, line int, fix vet.FixKind) { + pos := token.Position{ + line_nr: line + 1 + } + mut w := vet.Error{ + message: msg + file_path: vt.file + pos: pos + kind: .warning + fix: fix + typ: .default + } + if vt.opt.is_werror { + w.kind = .error + vt.errors << w + } else { + vt.warns << w + } +} diff --git a/v_windows/v/old/cmd/tools/vwatch.exe b/v_windows/v/old/cmd/tools/vwatch.exe new file mode 100644 index 0000000..ade1298 Binary files /dev/null and b/v_windows/v/old/cmd/tools/vwatch.exe differ diff --git a/v_windows/v/old/cmd/tools/vwatch.v b/v_windows/v/old/cmd/tools/vwatch.v new file mode 100644 index 0000000..0ac243d --- /dev/null +++ b/v_windows/v/old/cmd/tools/vwatch.v @@ -0,0 +1,381 @@ +module main + +import os +import time +import term +import flag + +const scan_timeout_s = get_scan_timeout_seconds() + +const max_v_cycles = 1000 + +const scan_frequency_hz = 4 + +const scan_period_ms = 1000 / scan_frequency_hz + +const max_scan_cycles = scan_timeout_s * scan_frequency_hz + +fn get_scan_timeout_seconds() int { + env_vw_timeout := os.getenv('VWATCH_TIMEOUT').int() + if env_vw_timeout == 0 { + $if gcboehm ? { + return 35000000 // over 1 year + } $else { + return 5 * 60 + } + } + return env_vw_timeout +} + +// +// Implements `v watch file.v` , `v watch run file.v` etc. +// With this command, V will collect all .v files that are needed for the +// compilation, then it will enter an infinite loop, monitoring them for +// changes. +// +// When a change is detected, it will stop the current process, if it is +// still running, then rerun/recompile/etc. +// +// In effect, this makes it easy to have an editor session and a separate +// terminal, running just `v watch run file.v`, and you will see your +// changes right after you save your .v file in your editor. +// +// +// Since -gc boehm is not available on all platforms yet, +// and this program leaks ~8MB/minute without it, the implementation here +// is done similarly to vfmt in 2 modes, in the same executable: +// +// a) A parent/manager process that only manages a single worker +// process. The parent process does mostly nothing except restarting +// workers, thus it does not leak much. +// +// b) A worker process, doing the actual monitoring/polling. +// NB: *workers are started with the --vwatchworker option* +// +// Worker processes will run for a limited number of iterations, then +// they will do exit(255), and then the parent will start a new worker. +// Exiting by any other code will cause the parent to also exit with the +// same error code. This limits the potential leak that a worker process +// can do, even without using the garbage collection mode. +// + +struct VFileStat { + path string + mtime int +} + +[unsafe] +fn (mut vfs VFileStat) free() { + unsafe { vfs.path.free() } +} + +enum RerunCommand { + restart + quit +} + +struct Context { +mut: + pid int // the pid of the current process; useful while debugging manager/worker interactions + is_worker bool // true in the workers, false in the manager process + check_period_ms int = scan_period_ms + vexe string + affected_paths []string + vfiles []VFileStat + opts []string + rerun_channel chan RerunCommand + child_process &os.Process + is_exiting bool // set by SIGINT/Ctrl-C + v_cycles int // how many times the worker has restarted the V compiler + scan_cycles int // how many times the worker has scanned for source file changes + clear_terminal bool // whether to clear the terminal before each re-run + silent bool // when true, watch will not print a timestamp line before each re-run + add_files []string // path to additional files that have to be watched for changes + ignore_exts []string // extensions of files that will be ignored, even if they change (useful for sqlite.db files for example) + cmd_before_run string // a command to run before each re-run + cmd_after_run string // a command to run after each re-run +} + +[if debug_vwatch ?] +fn (mut context Context) elog(msg string) { + eprintln('> vwatch $context.pid, $msg') +} + +fn (context &Context) str() string { + return 'Context{ pid: $context.pid, is_worker: $context.is_worker, check_period_ms: $context.check_period_ms, vexe: $context.vexe, opts: $context.opts, is_exiting: $context.is_exiting, vfiles: $context.vfiles' +} + +fn (mut context Context) get_stats_for_affected_vfiles() []VFileStat { + if context.affected_paths.len == 0 { + mut apaths := map[string]bool{} + // The next command will make V parse the program, and print all .v files, + // needed for its compilation, without actually compiling it. + copts := context.opts.join(' ') + cmd := '"$context.vexe" -silent -print-v-files $copts' + // context.elog('> cmd: $cmd') + mut paths := []string{} + if context.add_files.len > 0 && context.add_files[0] != '' { + paths << context.add_files + } + vfiles := os.execute(cmd) + if vfiles.exit_code == 0 { + paths_trimmed := vfiles.output.trim_space() + paths << paths_trimmed.split('\n') + } + for vf in paths { + apaths[os.real_path(os.dir(vf))] = true + } + context.affected_paths = apaths.keys() + // context.elog('vfiles paths to be scanned: $context.affected_paths') + } + // scan all files in the found folders + mut newstats := []VFileStat{} + for path in context.affected_paths { + mut files := os.ls(path) or { []string{} } + for pf in files { + pf_ext := os.file_ext(pf).to_lower() + if pf_ext in ['', '.bak', '.exe', '.dll', '.so', '.def'] { + continue + } + if pf_ext in context.ignore_exts { + continue + } + if pf.starts_with('.#') { + continue + } + if pf.ends_with('~') { + continue + } + f := os.join_path(path, pf) + fullpath := os.real_path(f) + mtime := os.file_last_mod_unix(fullpath) + newstats << VFileStat{fullpath, mtime} + } + } + // always add the v compiler itself, so that if it is recompiled with `v self` + // the watcher will rerun the compilation too + newstats << VFileStat{context.vexe, os.file_last_mod_unix(context.vexe)} + return newstats +} + +fn (mut context Context) get_changed_vfiles() int { + mut changed := 0 + newfiles := context.get_stats_for_affected_vfiles() + for vfs in newfiles { + mut found := false + for existing_vfs in context.vfiles { + if existing_vfs.path == vfs.path { + found = true + if existing_vfs.mtime != vfs.mtime { + context.elog('> new updates for file: $vfs') + changed++ + } + break + } + } + if !found { + changed++ + continue + } + } + context.vfiles = newfiles + if changed > 0 { + context.elog('> get_changed_vfiles: $changed') + } + return changed +} + +fn change_detection_loop(ocontext &Context) { + mut context := unsafe { ocontext } + for { + if context.v_cycles >= max_v_cycles || context.scan_cycles >= max_scan_cycles { + context.is_exiting = true + context.kill_pgroup() + time.sleep(50 * time.millisecond) + exit(255) + } + if context.is_exiting { + return + } + changes := context.get_changed_vfiles() + if changes > 0 { + context.rerun_channel <- RerunCommand.restart + } + time.sleep(context.check_period_ms * time.millisecond) + context.scan_cycles++ + } +} + +fn (mut context Context) kill_pgroup() { + if context.child_process == 0 { + return + } + if context.child_process.is_alive() { + context.child_process.signal_pgkill() + } + context.child_process.wait() +} + +fn (mut context Context) run_before_cmd() { + if context.cmd_before_run != '' { + context.elog('> run_before_cmd: "$context.cmd_before_run"') + os.system(context.cmd_before_run) + } +} + +fn (mut context Context) run_after_cmd() { + if context.cmd_after_run != '' { + context.elog('> run_after_cmd: "$context.cmd_after_run"') + os.system(context.cmd_after_run) + } +} + +fn (mut context Context) compilation_runner_loop() { + cmd := '"$context.vexe" ${context.opts.join(' ')}' + _ := <-context.rerun_channel + for { + context.elog('>> loop: v_cycles: $context.v_cycles') + if context.clear_terminal { + term.clear() + } + context.run_before_cmd() + timestamp := time.now().format_ss_milli() + context.child_process = os.new_process(context.vexe) + context.child_process.use_pgroup = true + context.child_process.set_args(context.opts) + context.child_process.run() + if !context.silent { + eprintln('$timestamp: $cmd | pid: ${context.child_process.pid:7d} | reload cycle: ${context.v_cycles:5d}') + } + for { + mut notalive_count := 0 + mut cmds := []RerunCommand{} + for { + if context.is_exiting { + return + } + if !context.child_process.is_alive() { + context.child_process.wait() + notalive_count++ + if notalive_count == 1 { + // a short lived process finished, do cleanup: + context.run_after_cmd() + } + } + select { + action := <-context.rerun_channel { + cmds << action + if action == .quit { + context.kill_pgroup() + return + } + } + 100 * time.millisecond { + should_restart := RerunCommand.restart in cmds + cmds = [] + if should_restart { + // context.elog('>>>>>>>> KILLING $context.child_process.pid') + context.kill_pgroup() + break + } + } + } + } + if !context.child_process.is_alive() { + context.child_process.wait() + context.child_process.close() + if notalive_count == 0 { + // a long running process was killed, do cleanup: + context.run_after_cmd() + } + break + } + } + context.v_cycles++ + } +} + +const ccontext = Context{ + child_process: 0 +} + +fn main() { + dump(scan_timeout_s) + mut context := unsafe { &Context(voidptr(&ccontext)) } + context.pid = os.getpid() + context.vexe = os.getenv('VEXE') + + mut fp := flag.new_flag_parser(os.args[1..]) + fp.application('v watch') + if os.args[1] == 'watch' { + fp.skip_executable() + } + fp.version('0.0.2') + fp.description('Collect all .v files needed for a compilation, then re-run the compilation when any of the source changes.') + fp.arguments_description('[--silent] [--clear] [--ignore .db] [--add /path/to/a/file.v] [run] program.v') + fp.allow_unknown_args() + fp.limit_free_args_to_at_least(1) + context.is_worker = fp.bool('vwatchworker', 0, false, 'Internal flag. Used to distinguish vwatch manager and worker processes.') + context.silent = fp.bool('silent', `s`, false, 'Be more silent; do not print the watch timestamp before each re-run.') + context.clear_terminal = fp.bool('clear', `c`, false, 'Clears the terminal before each re-run.') + context.add_files = fp.string('add', `a`, '', 'Add more files to be watched. Useful with `v watch -add=/tmp/feature.v run cmd/v /tmp/feature.v`, if you change *both* the compiler, and the feature.v file.').split(',') + context.ignore_exts = fp.string('ignore', `i`, '', 'Ignore files having these extensions. Useful with `v watch -ignore=.db run server.v`, if your server writes to an sqlite.db file in the same folder.').split(',') + show_help := fp.bool('help', `h`, false, 'Show this help screen.') + context.cmd_before_run = fp.string('before', 0, '', 'A command to execute *before* each re-run.') + context.cmd_after_run = fp.string('after', 0, '', 'A command to execute *after* each re-run.') + if show_help { + println(fp.usage()) + exit(0) + } + remaining_options := fp.finalize() or { + eprintln('Error: $err') + exit(1) + } + context.opts = remaining_options + context.elog('>>> context.pid: $context.pid') + context.elog('>>> context.vexe: $context.vexe') + context.elog('>>> context.opts: $context.opts') + context.elog('>>> context.is_worker: $context.is_worker') + context.elog('>>> context.clear_terminal: $context.clear_terminal') + context.elog('>>> context.add_files: $context.add_files') + context.elog('>>> context.ignore_exts: $context.ignore_exts') + if context.is_worker { + context.worker_main() + } else { + context.manager_main() + } +} + +fn (mut context Context) manager_main() { + myexecutable := os.executable() + mut worker_opts := ['--vwatchworker'] + worker_opts << os.args[2..] + for { + mut worker_process := os.new_process(myexecutable) + worker_process.set_args(worker_opts) + worker_process.run() + for { + if !worker_process.is_alive() { + worker_process.wait() + break + } + time.sleep(200 * time.millisecond) + } + if !(worker_process.code == 255 && worker_process.status == .exited) { + worker_process.close() + break + } + worker_process.close() + } +} + +fn (mut context Context) worker_main() { + context.rerun_channel = chan RerunCommand{cap: 10} + os.signal_opt(.int, fn (_ os.Signal) { + mut context := unsafe { &Context(voidptr(&ccontext)) } + context.is_exiting = true + context.kill_pgroup() + }) or { panic(err) } + go context.compilation_runner_loop() + change_detection_loop(context) +} diff --git a/v_windows/v/old/cmd/tools/vwipe-cache.v b/v_windows/v/old/cmd/tools/vwipe-cache.v new file mode 100644 index 0000000..988b7c2 --- /dev/null +++ b/v_windows/v/old/cmd/tools/vwipe-cache.v @@ -0,0 +1,13 @@ +module main + +import os +import v.vcache + +fn main() { + mut cm := vcache.new_cache_manager([]) + cpath := cm.basepath + if os.exists(cpath) && os.is_dir(cpath) { + os.rmdir_all(cpath) or {} + } + println('V cache folder $cpath was wiped.') +} diff --git a/v_windows/v/old/cmd/v/help/ast.txt b/v_windows/v/old/cmd/v/help/ast.txt new file mode 100644 index 0000000..4ce4637 --- /dev/null +++ b/v_windows/v/old/cmd/v/help/ast.txt @@ -0,0 +1,12 @@ +Usage: + v ast [options] path_to_source.v + + Generate AST json file from v source file. + +Options: + -w + generate ast json file, and watch for changes. + -c + generate ast json file and c file, and watch for changes. + -p + print the json string to stdout. \ No newline at end of file diff --git a/v_windows/v/old/cmd/v/help/bin2v.txt b/v_windows/v/old/cmd/v/help/bin2v.txt new file mode 100644 index 0000000..bb55c66 --- /dev/null +++ b/v_windows/v/old/cmd/v/help/bin2v.txt @@ -0,0 +1,14 @@ +Usage: + v bin2v [options] FILE[...] + + Converts a list of arbitrary files into a single v module file. + + You can use this tool to embed binary files, like pictures or fonts inside + the executable of a v program, such that it would not need access to external + resources during runtime, and it would be as self-contained as possible. + +Options: + -h, --help Show this help screen. + -m, --module Name of the generated module. + -p, --prefix A prefix put before each resource name. + -w, --write Write directly to a file with the given name. diff --git a/v_windows/v/old/cmd/v/help/bug.txt b/v_windows/v/old/cmd/v/help/bug.txt new file mode 100644 index 0000000..6eae2ee --- /dev/null +++ b/v_windows/v/old/cmd/v/help/bug.txt @@ -0,0 +1,8 @@ +Usage: + v bug [options] FILE + + Open a prefilled bug report on GitHub. + +Options: + -v Enable verbosity while gathering various information + -y Force the submission of the issue, even if an error occured diff --git a/v_windows/v/old/cmd/v/help/build-c.txt b/v_windows/v/old/cmd/v/help/build-c.txt new file mode 100644 index 0000000..53aa0bc --- /dev/null +++ b/v_windows/v/old/cmd/v/help/build-c.txt @@ -0,0 +1,240 @@ +Usage: v [C build flags] ['run'] [run options] + +This command compiles the given target, along with their dependencies, into an executable. + +This help topic explores the C-backend specific build flags. For more general build help, +see also `v help build`. + +# Interfacing the C compiler, passing options to it: + -cc + Change the C compiler V invokes to the specified compiler. + The C compiler is required to support C99. + Officially supported/tested C compilers include: + `clang`, `gcc`, `tcc`, `mingw-w64` and `msvc`. + + -cflags + Pass the provided flag as is to the C compiler. + Can be specified multiple times to provide multiple flags. + Use quotes to wrap the flag argument if it contains spaces. + + V also supports the environment variables CFLAGS and LDFLAGS. + The contents of the CFLAGS variable will be prepended as is, at the start + of the C backend command, right after the name of the compiler. + The contents of the LDFLAGS variable will be appended as is, at the end + of the C backend command, after all other options. + + -cstrict + Turn on additional C warnings. This slows down compilation + slightly (~10-10% for gcc), but sometimes provides better diagnosis. + + -showcc + Prints the C command that is used to build the program. + + -freestanding + Build the executable without dependency on libc. + Supported only on `linux` targets currently. + + -bare-builtin-dir + Use with `-freestanding`. This specifies the directory to the + implementation of some basic builtin functions. The list is as follows: + bare_print(buf &byte, len u64) + Print len charecters from the buffer pointed to by buf to stdout. + bare_eprint(buf &byte, len u64) + Print len charecters from the buffer pointed to by buf to stderr. + bare_panic(msg string) + Print "V panic: " + msg, along with an optional backtrace and/or the V commit hash, and then exit. + [export: 'malloc'] + __malloc(n size_t) &C.void + Allocates n bytes of memory and returns the pointer to the first byte + [export: 'free'] + __free(ptr &C.void) + Free the block of memory ptr allocated by malloc. + realloc(old_area &C.void, new_size size_t) &C.void + Allocates a new area of size new_size, copies old_area + to the new area, and returns a pointer to the new area. + [export: 'calloc'] + __calloc(nmemb size_t, size size_t) &C.void + Like malloc, but sets all the bytes to `0` first. + memcpy(dest &C.void, src &C.void, n size_t) &C.void + Moves n bytes from dest to src, and returns dest + memmove(dest &C.void, src &C.void, n size_t) &C.void + Like memcpy, but guaranteed to work if dest and src overlap. + memcmp(a &C.void, b &C.void, n size_t) int + Compare two buffers of length n. If a and b are equal, return 0. + Otherwise, return the difference between the first different letter. + strlen(s &C.void) size_t + Returns the amount of bytes until the first `0`, starting at s + memset(s &C.void, c int, n size_t) &C.void + Sets n bytes starting at s to c (c is casted to a char) + and returns s. + getchar() int + Read one character from stdin and return it. + Print "V Panic" + msg, along with an optional backtrace, and then exit. + vsprintf(str &char, format &char, ap va_list) int + See `man vsprintf`. + vsnprintf(str &char, size size_t, format &char, ap va_list) int + See `man vsnprintf`. + bare_backtrace() string + Return a backtrace that can be printed. If backtraces are not + supported, return a message stating that backtraces do not work. + [export: 'exit'] + __exit(code int) + Exit with code code. code is allowed to be ignored. + + The module decleration should be `builtin`. The default Linux + implementation can be found in `vlib/builtin/linux_bare`. + + -os , -target-os + Change the target OS that V tries to compile for. + By default, the target OS is the host system. + When OS is `cross`, V will attempt to output cross-platform C code. + + Here is a list of the operating systems, supported by V: + (CI tests runs on every commit/PR for each of these): + `windows`, `linux`, `macos` + + The compiler is known to also work, and has support for these operating + systems also (although we do not test it as regularly as for the above): + `android`, `ios`, + `freebsd`, `openbsd`, `netbsd`, `dragonfly`, + `solaris`, `serenity`, `haiku`, `vinix` + + Note that V has the concept of platform files, i.e. files ending + with `_platform.c.v`, and usually only the matching files are used in + a compilation, and also it supports a `_default.c.v` file, that will + be used, when no other more specific `_platform.c.v` file is found. + The default is mainly useful for writing shims for new platforms, + until a more specialized _platform.c.v is written instead. + + For example, suppose you have these 3 files: + x_default.c.v + x_windows.c.v + x_linux.c.v + If you compile with `-os freebsd`, then x_default.c.v will be used. + If you compile with `-os linux`, then x_linux.c.v will be used. + If you compile with `-os windows`, then x_windows.c.v will be used. + If you compile with `-os cross`, then all, *except x_default.c.v* + will be used, wrapped in conditional compilation guards, so that + the generated C source code will be larger, but will compile on all + explicitly supported platforms without source changes. + + -m32, -m64 + Specify whether 32-bit or 64-bit machine code is generated. + + -sanitize + Pass flags related to sanitization to the C compiler. + + -shared + Tell V to compile a shared object instead of an executable. + The resulting file extension will be `.dll` on Windows and `.so` on Unix systems + +# Memory management + -autofree + Free memory used in functions automatically. + + -manualfree + Do not free memory used in functions (the developer has to put x.free() + and unsafe{free(x)} calls manually in this mode). + Some short lived applications, like compilers and other CLI tools are + more performant without autofree. + + -gc + Use and link an optional garbage collector. Only the Boehm–Demers–Weiser + garbage collector is supported currently with the following sub-options: + + `-gc boehm` ........... selects the default mode for the architecture + `-gc boehm_full` ...... full garbage collection mode + `-gc boehm_incr` ...... incremental/generational garbage collection mode + `-gc boehm_full_opt` .. optimized full garbage collection mode + `-gc boehm_incr_opt` .. optimized incremental/generational garbage collection mode + `-gc boehm_leak` ...... leak detection mode + + You need to install a `libgc-dev` package first, or install it manually from: + + https://github.com/ivmai/bdwgc + + Note, `-gc boehm` is complementary to -autofree. The Boehm garbage + collector is conservative, and it may make your program significantly + slower if it does many small allocations in a loop. This option + is intended *mainly* for reducing the memory usage of programs, that + process large amounts of text in *batch mode* on low/limited memory + environments like small VPSes, and for which a few ms of garbage + collection pauses from time to time *do not matter much*. + + The option `-gc boehm_leak` is intended for leak detection in + manual memory management. The function `gc_check_leaks()` + can be called to get detection results. This function is a no-op + when `-gc boehm_leak` is not supplied. + +# Miscellaneous: + -printfn + Print the content of the generated C function named fn_name. + You can repeat that many times with different function names. + This is useful when you just want to quickly tweak the generated + C code, without opening the generated .c file in a text editor, + i.e. it enables this workflow: + + 1) change vlib/v/gen/cgen.v + 2) ./v -o v2 cmd/v && ./v2 -printfn main__main bug.v + 3) inspect the produced C, and goto 1) till the bug is fixed. + + Since V compiles itself very fast (especially with tcc), + this loop is very short usually. + + -compress + Strip the compiled executable to compress it. + + -live + Build the executable with live capabilities (`[live]`). + + -no-prelude + Prevents V from generating a prelude in generated .c files, useful for freestanding targets + where eg. you replace C standard library with your own, or some definitions/headers break something. + + -custom-prelude + Useful for similar use-case as above option, except it replaces V-generated prelude with + your custom one loaded from specified . + +# Debugging: + -g + Generate more debug information in the compiled executable. + This makes program backtraces more useful. + Using debuggers like gdb/lldb with such executables is easier too. + Unlike `-cg` (described below), `-g` will enforce V source line numbers + so that your debugger and the stacktraces will show you directly + what .v file is responsible for each call/panic. + + -cg + Like -g, but do not use V source line numbers. + When debugging code that wraps C libraries, this option may be + more useful than -g, since it will reduce the amount of context + switching, that you need to do, while looking at .v and .c sources. + This option is usually used in combination with `-keepc`. + + -keepc + Do not remove the temporary .tmp.c and .tmp.c.rsp files. + Also do not use a random prefix for them, so they would be fixed and predictable. + + NB: when writing low level code that interfaces/wraps an existing C library, + it is frequently helpful to use these together: -keepc -cg -showcc -show-c-output + + -showcc + Prints the C command that is used to build the program. + + -show-c-output + Prints the output, that your C compiler produced, while compiling your program. + + -dump-c-flags file.txt + Write all C flags into `file.txt`, one flag per line. + If `file.txt` is `-`, then write the flags to stdout, one flag per line. + + -assert aborts + Call abort() after an assertion failure. Debuggers usually + install signal handlers for SIGABRT, so your program will stop and you + will get a backtrace. If you are running your program outside of a + debugger, you will most likely get a core dump file. + + -assert backtraces + Call print_backtrace() after an assertion failure. Note that + backtraces are not implemented yet on all combinations of + platform/compiler. diff --git a/v_windows/v/old/cmd/v/help/build-js.txt b/v_windows/v/old/cmd/v/help/build-js.txt new file mode 100644 index 0000000..2696658 --- /dev/null +++ b/v_windows/v/old/cmd/v/help/build-js.txt @@ -0,0 +1,22 @@ +Usage: v -b js [-options] ['run'] [run options] + +This command compiles the given target, along with their dependencies, into an Javascript source file. + +For more general build help, see also `v help build`. + +# Interfacing the Javascript Backend code generation, passing options to it: + -prod + Do not create any JS Doc comments + + -sourcemap + Create a source map for debugging + + -sourcemap-inline + Embed the source map directly into the JavaScript source file + (currently default, external source map files not implemented) + + -sourcemap-src-include + Include the orginal V source files into the generated source map + (default false, all files in the source map are currently referenced by their absolute system file path) + + The supported targets for the JS backend are: ES5 strict diff --git a/v_windows/v/old/cmd/v/help/build-native.txt b/v_windows/v/old/cmd/v/help/build-native.txt new file mode 100644 index 0000000..8522912 --- /dev/null +++ b/v_windows/v/old/cmd/v/help/build-native.txt @@ -0,0 +1,17 @@ +Usage: v -native [-options] ['run'] [run options] + +This command compiles the given target, along with their dependencies, into an executable using the Native code generation backend. + +For more general build help, see also `v help build`. + +# Interfacing the Native code generation, passing options to it: + -v + Display the assembly code generated (that may change to `-showasm` in the future) + + -arch + Select target architecture, right now only `arm64` and `amd64` are supported + + -os , -target-os + Change the target OS that V compiles for. + + The supported targets for the native backend are: `macos`, `linux` diff --git a/v_windows/v/old/cmd/v/help/build.txt b/v_windows/v/old/cmd/v/help/build.txt new file mode 100644 index 0000000..a36ecec --- /dev/null +++ b/v_windows/v/old/cmd/v/help/build.txt @@ -0,0 +1,157 @@ +Usage: v [build flags] ['run'] [run options] + +This command compiles the given target, along with their dependencies, into an executable. + +Note that these build flags also work with `run` too, but you need to +pass them *before* `run` . The argument directly after `run` is assumed +to be a .v source file or folder containing .v source files. +Everything after that, is assumed to be flags, that V will ignore itself, +but will pass to the executable after it is compiled. + +This enables you to do for example: `v -cc gcc -g myfile.v run -param1 abcde` +... which means for V: "compile using gcc, produce debugging information, +then run `./myfile -param1 abcde` and exit with its exit code". + +When compiling packages, V ignores files that end in '_test.v'. + +When compiling a single main package, V writes the resulting executable to an output file +named after the build target. ('v abc.v' and 'v abc/' both write either 'abc' or 'abc.exe') +The '.exe' suffix is added automatically, when writing a Windows executable. +By default, the executable is stored in the same directory as the compiled source code. + +The -o flag forces V to write the resulting executable or object to the d output file or directory, +instead of the default behavior described in the last two paragraphs. + +You can put common options inside an environment variable named VFLAGS, so that +you don't have to repeat them. + +You can set it like this: `export VFLAGS="-cc clang -g"` on *nix, +`set VFLAGS=-cc msvc` on Windows. + +V respects the TMPDIR environment variable, and will put .tmp.c files in TMPDIR/v/ . +If you have not set it, a suitable platform specific folder (like /tmp) will be used. + +NB: the build flags are shared with the run command too: + + -b , -backend + Specify the backend to use while building the executable. + Current list of supported backends: + * `c` (default) - V outputs C source code which is passed to a C compiler to be compiled. + * `js` - V outputs JS source code which can be passed to NodeJS to be ran. + * `native` - V outputs native executable (see -arch x64|arm64 and -os linux|macos) (EXPERIMENTAL). + + -d [=], -define [=] + Define the provided flag. + If value is not provided, it is assumed to be set to `true`. + `value` should be `1` or `0` to indicate `true` and `false` respectively otherwise. + + -g + Compile the executable in debug mode, allowing code to be debugged more easily. + + -o , -output + Force V to output the executable in a specific location + (relative to the current working directory if not absolute). + + -obf, -obfuscate + Turn on obfuscation for the code being built. Currently only renames symbols. + + -path + Specify the order of path V looks up in while searching for imported dependencies, + separated by pipes (`|`). In addition to absolute paths, you can + also use these special strings too: + @vmodules - replaced with the location of the global ~/.vmodules/ folder + (modules installed with `v install` are there). You can change + its location by setting the environment variable VMODULES. + @vlib - replaced with the location of the v's vlib folder. + Using these, you can arrange for very flexible search orders for you project, for example: + -path "/v/my_project_private_modules|@vlib|@vmodules" + By default, -path is just "@vlib|@vmodules" . + + -prod + Compile the executable in production mode, where most optimizations are enabled. + Note that most V warnings turn to errors, if you pass -prod, so you will have + to fix them first. + + -prof, -profile + Compile the executable with all functions profiled. + The profile results will be stored in `file.txt`. + The format is 4 fields, separated by a space, for each v function: + a) how many times it was called + b) how much *nanoseconds in total* it took + c) an average for each function (i.e. (b) / (a) ) + d) the function name + NB: if you want to output the profile info to stdout, use `-profile -`. + + -profile-no-inline + Skip [inline] functions when profiling. + + -stats + Enable more detailed statistics reporting, while compiling test files. + You can use that with `v test` too, for example: + v -stats test vlib/ + ... will run test_ functions inside all _test.v files inside vlib/ , + and will report detailed statistics about how much time each test_ function took, how many + assertions were made, how many tests passed/failed and so on. + + -translated + Enable features that are discouraged in regular V code but required for translated V code. + + -v + Enable verbosity in the V compiler while compiling + + -print-v-files + Just print the list of all parsed .v files, then stop processing further. + This is useful for running external processing tools: + ./v -print-v-files cmd/v | etags -L - + ... will generate a TAGS file, that emacs can then use to jump + to the definition of functions used by v itself. For vim: + ./v -print-v-files cmd/v | ctags -L - + ... will generate a simillar tags file, that vi compatible editors can use. + NB: an useful, although not entirely accurate regexp based Universal Ctags options file + for V is located in `.ctags.d/v.ctags` . If you use https://ctags.io/ , it will be used + up automatically, or you can specify it explicitly with --options=.ctags.d/v.ctags . + + -color, -nocolor + Force the use of ANSI colors for the V error/warning messages, or disable them completely. + By default, the V compiler tries to show its errors/warnings in ANSI color. The heuristic + that it uses to detect whether or not to use ANSI colors may not work in all cases. + These options allow you to override the default detection. + + -check-syntax + Only scan and parse the files, but then stop. Useful for very quick syntax checks. + + -show-timings + Print a summary about how long each compiler stage took, for example: + PARSE: 152ms + CHECK: 62ms + C GEN: 103ms + C tcc: 95ms + + Related to -show-timings, is the ability to compile a special instrumented + v compiler with this command: + `v -d time_parsing -d time_checking -d time_cgening -d time_v self` + The instrumented version will print detailed timing stats while processing + each .v file. + + -w + Hide all warnings. + + -W + Treat *all V warnings* as errors, even in development builds. + + -Wfatal-errors + Unconditionally exit with exit(1) after the first error. + Useful for scripts/tooling that calls V. + + -Wimpure-v + Warn about using C. or JS. symbols in plain .v files. + These should be moved in .c.v and .js.v . + NB: in the future, this will be turned ON by default, + and will become an error, after vlib modules are cleaned up. + +For C-specific build flags, use `v help build-c`. +For JS-specific build flags, use `v help build-js`. +For Native-specific build flags, use `v help build-native`. + +See also: + `v help run` for documentation regarding `v run`. diff --git a/v_windows/v/old/cmd/v/help/check-md.txt b/v_windows/v/old/cmd/v/help/check-md.txt new file mode 100644 index 0000000..145ffee --- /dev/null +++ b/v_windows/v/old/cmd/v/help/check-md.txt @@ -0,0 +1,22 @@ +check-md is a tool to check the passed markdown files for correct ```v ``` code blocks +and other style violations like too long lines/links etc... + +Usage: + a) `v check-md [flags] <...files>` - Check the given .md files. + b) `v check-md [flags] <...dirs>` - Check *all* files in the given directories. + Note: You can also combine files and directories. + +Flags: + -silent Do not show a progress bar. + -w, -hide-warnings Do not print warnings, only errors. + +NB: There are several special keywords, which you can put after the code fences for v. +These are: + compile - Default, can be omitted. The example will be compiled and formatting is verified. + live - Compile hot reload examples with the ´-live´ flag set and verify formatting. + ignore - Ignore the example, useful for examples that just use the syntax highlighting + failcompile - Known failing compilation. Useful for examples demonstrating compiler errors. + oksyntax - Should parse and be formatted but may not compile. Useful for partial examples. + badsyntax - Known bad syntax, it should not even parse. + wip - Like ignore; a planned feature; easy to search. + nofmt - Disable fmt verification for individual code blocks. diff --git a/v_windows/v/old/cmd/v/help/default.txt b/v_windows/v/old/cmd/v/help/default.txt new file mode 100644 index 0000000..77d3b57 --- /dev/null +++ b/v_windows/v/old/cmd/v/help/default.txt @@ -0,0 +1,60 @@ +V is a tool for managing V source code. + +Usage: + v [options] [command] [arguments] + +Examples: + v hello.v Compile the file `hello.v` and output it as `hello` or `hello.exe`. + v run hello.v Same as above but also run the produced executable immediately after compilation. + v -cg run hello.v Same as above, but make debugging easier (in case your program crashes). + v -o h.c hello.v Translate `hello.v` to `h.c`. Do not compile further. + v -o - hello.v Translate `hello.v` and output the C source code to stdout. Do not compile further. + + v watch hello.v Re-does the same compilation, when a source code change is detected. + The program is only compiled, not run. + v watch run hello.v Re-runs the same `hello.v` file, when a source code change is detected. + +V supports the following commands: +* New project scaffolding: + new Setup the file structure for a V project (in a sub folder). + init Setup the file structure for an already existing V project. + +* Ordinary development: + run Compile and run a V program. + test Run all test files in the provided directory. + fmt Format the V code provided. + vet Report suspicious code constructs. + doc Generate the documentation for a V module. + vlib-docs Generate and open the documentation of all the vlib modules. + repl Run the REPL. + watch Re-compile/re-run a source file, each time it is changed. + +* Installation/self updating: + symlink Create a symbolic link for V. + up Run the V self-updater. + self [-prod] Run the V self-compiler, use -prod to optimize compilation. + version Print the version text and exits. + +* Module/package management: + install Install a module from VPM. + remove Remove a module that was installed from VPM. + search Search for a module from VPM. + update Update an installed module from VPM. + upgrade Upgrade all the outdated modules. + list List all installed modules. + outdated List installed modules that need updates. + show Display information about a module on vpm + +* Others: + doctor Display some useful info about your system to help reporting bugs. + translate Translate C code to V (coming soon in 0.3). + tracev Produce a tracing version of the v compiler. + Use `tracev yourfile.v` when the compiler panics. + NB: `tracev` is much slower and more verbose than ordinary `v` + +Use "v help " for more information about a command, example: `v help build`, `v help build-c`, `v help build-native` +Use "v help other" to see less frequently used commands. +Use "v help topics" to see a list of all known help topics. + +Note: Help is required to write more help topics. +Only build, new, init, doc, fmt, vet, run, test, watch, search, install, remove, update, bin2v, check-md are properly documented currently. diff --git a/v_windows/v/old/cmd/v/help/doc.txt b/v_windows/v/old/cmd/v/help/doc.txt new file mode 100644 index 0000000..71f113a --- /dev/null +++ b/v_windows/v/old/cmd/v/help/doc.txt @@ -0,0 +1,34 @@ +Usage: + v doc [options] [MODULE / DIRECTORY / FILE] [symbol name] + +Examples: + v doc os + v doc os File + v doc -no-color os + v doc -o math.html math + v doc -m -f html vlib/ + + Generates the documentation of a given MODULE, DIRECTORY, or FILE + and prints or saves them to its desired format. It can generate HTML, JSON, + or Markdown format. + +Options: + -all Includes private and public functions/methods/structs/consts/enums. + -f Specifies the output format to be used. Available formats are: + md/markdown, json, text, stdout and html/htm + -h, -help Prints this help text. + -m Generate docs for modules listed in that folder. + -o Specifies the output file/folder path where to store the generated docs. + Set it to "stdout" to print the output instead of saving the contents + to a file. + -color Force stdout colorize output. + -no-color Force plain text output, without ANSI colors. + -readme Include README.md to docs if present. + -v Enables verbose logging. For debugging purposes. + -no-timestamp Omits the timestamp in the output file. + +For HTML mode: + -inline-assets Embeds the contents of the CSS and JS assets into the webpage directly. + +For plain text mode: + -l Show the locations of the generated signatures. diff --git a/v_windows/v/old/cmd/v/help/doctor.txt b/v_windows/v/old/cmd/v/help/doctor.txt new file mode 100644 index 0000000..05ed7ec --- /dev/null +++ b/v_windows/v/old/cmd/v/help/doctor.txt @@ -0,0 +1,3 @@ +Usage: v doctor + +Display some useful info about your system necessary for bug reports. diff --git a/v_windows/v/old/cmd/v/help/fmt.txt b/v_windows/v/old/cmd/v/help/fmt.txt new file mode 100644 index 0000000..cdfc8f4 --- /dev/null +++ b/v_windows/v/old/cmd/v/help/fmt.txt @@ -0,0 +1,35 @@ +Usage: + v fmt [options] path_to_source.v [path_to_other_source.v] + v fmt [options] path/to/dir [path/to/other_dir] + cat source.v | v fmt + Read source code from stdin, output formatted file to stdout. + +Formats the given V source files or recursively formats all files in the directory, +then prints their formatted source to stdout. + +Options: + -c Check if a file is already formatted. If not, print the filepath and exit with code 2. + Compared to -verify it is quicker but has a small trade-off in precision. + + -diff Display the differences between the formatted source(s) and the original source(s). + This will attempt to find a working `diff` command automatically unless you + specify one with the VDIFF_TOOL environment variable. + + -l List files whose formatting differs from vfmt. + + -w Write result to (source) file(s) instead of to stdout. + + -debug Print the kinds of encountered AST statements/expressions on stderr. + + -verify Make sure the provided file is already formatted. Useful for checking code contributions + in CI for example. + +Environment Variables: + VDIFF_TOOL A command-line tool that will be executed with the original file path + and a temporary formatted file path as arguments. e.g. + `VDIFF_TOOL=opendiff v fmt -diff path/to/file.v` will become: + opendiff path/to/file.v /tmp/v/vfmt_file.v + + VDIFF_OPTIONS A set of command-line options to be sent immediately after the + `diff` command. e.g. + VDIFF_OPTIONS="-W 80 -y" v fmt -diff path/to/file.v /tmp/v/vfmt_file.v diff --git a/v_windows/v/old/cmd/v/help/help.v b/v_windows/v/old/cmd/v/help/help.v new file mode 100644 index 0000000..d54bf06 --- /dev/null +++ b/v_windows/v/old/cmd/v/help/help.v @@ -0,0 +1,47 @@ +module help + +// TODO: move this file outside internal, and merge it with cmd/tools/modules/vhelp/vhelp.v . +import os +import v.pref + +const ( + unknown_topic = '`v help`: unknown help topic provided. Use `v help` for usage information.' +) + +pub fn print_and_exit(topic string) { + vexe := pref.vexe_path() + vroot := os.dir(vexe) + topicdir := os.join_path(vroot, 'cmd', 'v', 'help') + + for b in topic { + if (b >= `a` && b <= `z`) || b == `-` || (b >= `0` && b <= `9`) { + continue + } + eprintln(help.unknown_topic) + exit(1) + } + // `init` has the same help topic as `new` + name := if topic == 'init' { 'new' } else { topic } + if topic == 'topics' { + println(known_topics(topicdir)) + exit(0) + } + target_topic := os.join_path(topicdir, '${name}.txt') + content := os.read_file(target_topic) or { + eprintln(help.unknown_topic) + eprintln(known_topics(topicdir)) + exit(1) + } + println(content) + exit(0) +} + +fn known_topics(topicdir string) string { + mut res := []string{} + res << 'Known help topics:' + topic_files := os.glob(os.join_path(topicdir, '*.txt')) or { [] } + mut topics := topic_files.map(os.file_name(it).replace('.txt', '')) + topics.sort() + res << topics.join(', ') + return res.join('') +} diff --git a/v_windows/v/old/cmd/v/help/help_test.v b/v_windows/v/old/cmd/v/help/help_test.v new file mode 100644 index 0000000..ac395b6 --- /dev/null +++ b/v_windows/v/old/cmd/v/help/help_test.v @@ -0,0 +1,22 @@ +import os + +fn test_help() { + vexe := os.getenv('VEXE') + res := os.execute('"$vexe" help') + assert res.exit_code == 0 + assert res.output.starts_with('V is a tool for managing V source code.') +} + +fn test_help_as_short_option() { + vexe := os.getenv('VEXE') + res := os.execute('"$vexe" -h') + assert res.exit_code == 0 + assert res.output.starts_with('V is a tool for managing V source code.') +} + +fn test_help_as_long_option() { + vexe := os.getenv('VEXE') + res := os.execute('"$vexe" --help') + assert res.exit_code == 0 + assert res.output.starts_with('V is a tool for managing V source code.') +} diff --git a/v_windows/v/old/cmd/v/help/init.txt b/v_windows/v/old/cmd/v/help/init.txt new file mode 100644 index 0000000..235cb0b --- /dev/null +++ b/v_windows/v/old/cmd/v/help/init.txt @@ -0,0 +1,4 @@ +Usage: + v init + +Setup the file structure for an already existing V project. diff --git a/v_windows/v/old/cmd/v/help/install.txt b/v_windows/v/old/cmd/v/help/install.txt new file mode 100644 index 0000000..595d976 --- /dev/null +++ b/v_windows/v/old/cmd/v/help/install.txt @@ -0,0 +1,10 @@ +Usage: + v install [MODULE...] + Installs each MODULE. + + If no MODULEs, the modules listed in the `v.mod` file are installed instead. + +Options: + -help - Show usage info. + -v - Print more details about the performed operation. + -server-url - When doing network operations, use this vpm server. Can be given multiple times. diff --git a/v_windows/v/old/cmd/v/help/list.txt b/v_windows/v/old/cmd/v/help/list.txt new file mode 100644 index 0000000..33e28f1 --- /dev/null +++ b/v_windows/v/old/cmd/v/help/list.txt @@ -0,0 +1,3 @@ +Usage: v list + +List all installed modules. diff --git a/v_windows/v/old/cmd/v/help/new.txt b/v_windows/v/old/cmd/v/help/new.txt new file mode 100644 index 0000000..2c09e86 --- /dev/null +++ b/v_windows/v/old/cmd/v/help/new.txt @@ -0,0 +1,18 @@ +Usage: + v new [NAME] [DESCRIPTION] + Sets up a new V project with a 'v.mod' file, and a 'main.v' "Hello World" + file, and performs 'git init' (if git is installed on the system). + + If NAME is given, the project will be setup in a new directory with that + name, and that name will be added to the 'v.mod' file. If no name is given, + the user will be prompted for a name. + + If DESCRIPTION is given, the 'v.mod' file is updated with said description. + + v init + Sets up a V project within the current directory. + + If no '.v' file exists, then will create a 'main.v' file. + If no 'v.mod' file exists, one will be created. + If the current directory is not already controlled with 'git', will perform + 'git init' (if git is installed on the system). diff --git a/v_windows/v/old/cmd/v/help/other.txt b/v_windows/v/old/cmd/v/help/other.txt new file mode 100644 index 0000000..4958f7e --- /dev/null +++ b/v_windows/v/old/cmd/v/help/other.txt @@ -0,0 +1,33 @@ +These are utility commands that you can also launch through v, +but which are used less frequently by users: + + ast Generate a json representation of the AST for a given .v file. + + bug Post an issue on the V's issue tracker, including the failing program, and some diagnostic information. + + bin2v Convert a binary file to a v source file, + that can be later embedded in a module or program. + + build-examples Test if all examples can be built. + build-tools Test if all tools can be built. + build-vbinaries Test if V can be built with different configuration. + + check-md Check that V examples in markdown files are formatted and can compile. + + setup-freetype Setup thirdparty freetype on Windows. + + test-all Run most checks, that the CI does locally. + It may take over 2 minutes, and it needs internet connectivity too, + because it tries to also verify that `v install` works. + + test-fmt Test if all files in the current directory are formatted properly. + + test-parser Test that the V parser works with the given files, as if + they were typed by a human programmer, one character at a time. + NB: *very slow* for longer files (tens of seconds for 1KB .v file). + Mainly useful as a parser bug finder for the V Language Server project. + + test-self Test if V is working properly by running all tests, including the compiler ones. + NB: this can 1-2 minutes to run. + + wipe-cache Remove the V cache folder. Useful for cleaning the cache, and guaranteeing a clean build. diff --git a/v_windows/v/old/cmd/v/help/outdated.txt b/v_windows/v/old/cmd/v/help/outdated.txt new file mode 100644 index 0000000..82e7ce4 --- /dev/null +++ b/v_windows/v/old/cmd/v/help/outdated.txt @@ -0,0 +1,3 @@ +Usage: v outdated + +List all installed modules that need updates. diff --git a/v_windows/v/old/cmd/v/help/remove.txt b/v_windows/v/old/cmd/v/help/remove.txt new file mode 100644 index 0000000..0c02841 --- /dev/null +++ b/v_windows/v/old/cmd/v/help/remove.txt @@ -0,0 +1,9 @@ +Usage: + v remove [MODULE...] + Removes all listed MODULEs. + If no MODULE is provided, removes ALL installed modules. + +Options: + -help - Show usage info. + -v - Print more details about the performed operation. + -server-url - When doing network operations, use this vpm server. Can be given multiple times. diff --git a/v_windows/v/old/cmd/v/help/repl.txt b/v_windows/v/old/cmd/v/help/repl.txt new file mode 100644 index 0000000..08abb6a --- /dev/null +++ b/v_windows/v/old/cmd/v/help/repl.txt @@ -0,0 +1,3 @@ +Usage: v repl + +This command runs the V REPL. diff --git a/v_windows/v/old/cmd/v/help/run.txt b/v_windows/v/old/cmd/v/help/run.txt new file mode 100644 index 0000000..d214cee --- /dev/null +++ b/v_windows/v/old/cmd/v/help/run.txt @@ -0,0 +1,13 @@ +Usage: v [build flags] run [arguments...] + +This command is equivalent to running `v build` and running the compiled executable. +The executable is passed the arguments as provided in [arguments...]. + +If the target is '-', it means that the V source code to build comes from stdin. +If the '-o' option is not specified, and the target is '-', a temporary base name for the executable will be used. + +The exit status of run will be: +* `1` if the compilation failed. +* The exit code of the compiled executable otherwise. + +For more about build flags, see `v help build`. diff --git a/v_windows/v/old/cmd/v/help/search.txt b/v_windows/v/old/cmd/v/help/search.txt new file mode 100644 index 0000000..a405168 --- /dev/null +++ b/v_windows/v/old/cmd/v/help/search.txt @@ -0,0 +1,10 @@ +Usage: + v search KEYWORD[...] + Searches https://vpm.vlang.io/ for matching KEYWORDs and displays the + details + +Options: + -help - Show usage info. + -v - Print more details about the performed operation. + -server-url - When doing network operations, use this vpm server. Can be + given multiple times. diff --git a/v_windows/v/old/cmd/v/help/self.txt b/v_windows/v/old/cmd/v/help/self.txt new file mode 100644 index 0000000..3e0ce49 --- /dev/null +++ b/v_windows/v/old/cmd/v/help/self.txt @@ -0,0 +1,5 @@ +Usage: v self + +Rebuild V with the passed options. + +Options: All other options are passed to the build command. (e.g. -prod) diff --git a/v_windows/v/old/cmd/v/help/show.txt b/v_windows/v/old/cmd/v/help/show.txt new file mode 100644 index 0000000..c2c0fff --- /dev/null +++ b/v_windows/v/old/cmd/v/help/show.txt @@ -0,0 +1,3 @@ +Usage: v show + +Display information about a module on vpm. diff --git a/v_windows/v/old/cmd/v/help/symlink.txt b/v_windows/v/old/cmd/v/help/symlink.txt new file mode 100644 index 0000000..1059003 --- /dev/null +++ b/v_windows/v/old/cmd/v/help/symlink.txt @@ -0,0 +1,5 @@ +Usage: v symlink + +This command adds a symlink for the V compiler executable. + +Note that on Unix systems this command requires write permissions to /usr/local/bin to work. diff --git a/v_windows/v/old/cmd/v/help/test.txt b/v_windows/v/old/cmd/v/help/test.txt new file mode 100644 index 0000000..a49da91 --- /dev/null +++ b/v_windows/v/old/cmd/v/help/test.txt @@ -0,0 +1,18 @@ +Usage: + v [-stats] test FILE|DIRECTORY[...] + Runs test functions in the given FILEs and DIRECTORYs + + If '-stats' is given, more statistics about the tests are printed along + with a report of passes/failures + +NB 1: very frequently, when you work on a module you can cd into its folder, +and then you can perform: + v test . +... to run all the module's '_test.v' files. + +NB 2: V builtin testing requires you to name your files with a _test.v +suffix, and to name your test functions with test_ prefix. Each 'test_' +function in a '_test.v' file will be called automatically by the test +framework. You can use `assert condition` inside each 'test_' function. +If the asserted condition fails, then v will record that and produce a +more detailed error message about where the failure was. diff --git a/v_windows/v/old/cmd/v/help/tracev.txt b/v_windows/v/old/cmd/v/help/tracev.txt new file mode 100644 index 0000000..2d4b5a5 --- /dev/null +++ b/v_windows/v/old/cmd/v/help/tracev.txt @@ -0,0 +1,3 @@ +Usage: v tracev + +Produce a tracing version of the V compiler. diff --git a/v_windows/v/old/cmd/v/help/up.txt b/v_windows/v/old/cmd/v/help/up.txt new file mode 100644 index 0000000..7ae9447 --- /dev/null +++ b/v_windows/v/old/cmd/v/help/up.txt @@ -0,0 +1,7 @@ +Usage: v up + +Update the V compiler to the latest version from https://github.com/vlang/v. + +Options: + -v - Print more details about the update. + -prod - Compile the updated V with the -prod flag. diff --git a/v_windows/v/old/cmd/v/help/update.txt b/v_windows/v/old/cmd/v/help/update.txt new file mode 100644 index 0000000..4fa9a91 --- /dev/null +++ b/v_windows/v/old/cmd/v/help/update.txt @@ -0,0 +1,10 @@ +Usage: + v update [MODULE]... + Updates each MODULE. + + With no MODULE, ALL installed modules are updated to their latest versions. + +Options: + -help - Show usage info. + -v - Print more details about the update. + -server-url - When doing network operations, use this vpm server. Can be given multiple times. diff --git a/v_windows/v/old/cmd/v/help/upgrade.txt b/v_windows/v/old/cmd/v/help/upgrade.txt new file mode 100644 index 0000000..7aa62fb --- /dev/null +++ b/v_windows/v/old/cmd/v/help/upgrade.txt @@ -0,0 +1,7 @@ +Usage: v upgrade + +Upgrade all outdated modules. + +Options: + -help - Show usage info. + -v - Print more details about the upgrade. diff --git a/v_windows/v/old/cmd/v/help/version.txt b/v_windows/v/old/cmd/v/help/version.txt new file mode 100644 index 0000000..7c1a4bd --- /dev/null +++ b/v_windows/v/old/cmd/v/help/version.txt @@ -0,0 +1,3 @@ +Usage: v version + +Print the version of V installed. diff --git a/v_windows/v/old/cmd/v/help/vet.txt b/v_windows/v/old/cmd/v/help/vet.txt new file mode 100644 index 0000000..7a0d2bf --- /dev/null +++ b/v_windows/v/old/cmd/v/help/vet.txt @@ -0,0 +1,18 @@ +Usage: + v vet [options] path_to_source.v [path_to_other_source.v] + v vet [options] path/to/dir [path/to/other_dir] + +Reports suspicious code constructs. + +Options: + -W + Exit with code 1, even if vet only reported warnings. Useful for checks in CI. + + -w, -hide-warnings + Do not print warnings to stderr. + + -v, -verbose + Enable verbose logging. + + -force + (NB: vet development only!) Do not skip the vet regression tests. diff --git a/v_windows/v/old/cmd/v/help/vpm.txt b/v_windows/v/old/cmd/v/help/vpm.txt new file mode 100644 index 0000000..037084d --- /dev/null +++ b/v_windows/v/old/cmd/v/help/vpm.txt @@ -0,0 +1,8 @@ +Usage: + a) v install [MODULE...] + b) v update [MODULE...] + c) v remove [MODULE...] + d) v search KEYWORD[...] + e) v show [MODULE...] + + You can also pass -h or --help after each vpm command from the above, to see more details about it. diff --git a/v_windows/v/old/cmd/v/help/watch.txt b/v_windows/v/old/cmd/v/help/watch.txt new file mode 100644 index 0000000..9b69207 --- /dev/null +++ b/v_windows/v/old/cmd/v/help/watch.txt @@ -0,0 +1,28 @@ +v watch [--clear] [--ignore .db] [--add /path/to/a/file.v] [other V options] [run] program.v + +Description: +`v watch` collects all .v files needed for a compilation, then when +any of the .v source files changes, it re-runs the compilation. + +Options: + -s, --silent Be more silent; do not print the watch timestamp before each re-run. + + -c, --clear Clears the terminal before each re-run. + + -a, --add Add more files to be watched. + Useful with `v watch -add=feature.v run cmd/v feature.v`, + when you want to change *both* the V compiler, + and the `feature.v` file. + + -i, --ignore Ignore files having these extensions. + Useful with `v watch -ignore=.db run vwebserver.v`, + if your `vwebserver` writes to an sqlite.db file in the + same folder. + + --before A command to execute *before* each re-run. Example: --before 'v wipe-cache' + + --after A command to execute *after* each re-run. Example: --after 'rm -rf /tmp/v/' + +You can also customise the timeout, after `v watch` will re-start a monitored +program automatically, even if it was not changed by setting the enviroment +variable VWATCH_TIMEOUT (in seconds). By default, it is 5 min. (300 seconds). diff --git a/v_windows/v/old/cmd/v/v.v b/v_windows/v/old/cmd/v/v.v new file mode 100644 index 0000000..dde303d --- /dev/null +++ b/v_windows/v/old/cmd/v/v.v @@ -0,0 +1,146 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module main + +import help +import os +import term +import v.pref +import v.util +import v.util.version +import v.builder + +const ( + external_tools = [ + 'bin2v', + 'bug', + 'build-examples', + 'build-tools', + 'build-vbinaries', + 'check-md', + 'complete', + 'doc', + 'doctor', + 'fmt', + 'repl', + 'self', + 'setup-freetype', + 'symlink', + 'test', + 'test-all', /* runs most of the tests and other checking tools, that will be run by the CI */ + 'test-cleancode', + 'test-fmt', + 'test-parser', + 'test-self', + 'tracev', + 'up', + 'vet', + 'wipe-cache', + 'watch', + 'ast', + ] + list_of_flags_that_allow_duplicates = ['cc', 'd', 'define', 'cf', 'cflags'] +) + +fn main() { + mut timers_should_print := false + $if time_v ? { + timers_should_print = true + } + mut timers := util.new_timers(timers_should_print) + timers.start('v total') + defer { + timers.show('v total') + } + timers.start('v start') + timers.show('v start') + timers.start('parse_CLI_args') + args := os.args[1..] + // args = 123 + if args.len == 0 || args[0] in ['-', 'repl'] { + // Running `./v` without args launches repl + if args.len == 0 { + if os.is_atty(0) != 0 { + cmd_exit := term.highlight_command('exit') + cmd_help := term.highlight_command('v help') + file_main := term.highlight_command('main.v') + cmd_run := term.highlight_command('v run main.v') + println('Welcome to the V REPL (for help with V itself, type $cmd_exit, then run $cmd_help).') + eprintln(' NB: the REPL is highly experimental. For best V experience, use a text editor,') + eprintln(' save your code in a $file_main file and execute: $cmd_run') + } else { + mut args_and_flags := util.join_env_vflags_and_os_args()[1..].clone() + args_and_flags << ['run', '-'] + pref.parse_args(external_tools, args_and_flags) + } + } + util.launch_tool(false, 'vrepl', os.args[1..]) + return + } + args_and_flags := util.join_env_vflags_and_os_args()[1..] + prefs, command := pref.parse_args(external_tools, args_and_flags) + if prefs.use_cache && os.user_os() == 'windows' { + eprintln('-usecache is currently disabled on windows') + exit(1) + } + timers.show('parse_CLI_args') + // Start calling the correct functions/external tools + // Note for future contributors: Please add new subcommands in the `match` block below. + if command in external_tools { + // External tools + util.launch_tool(prefs.is_verbose, 'v' + command, os.args[1..]) + return + } + match command { + 'help' { + invoke_help_and_exit(args) + } + 'new', 'init' { + util.launch_tool(prefs.is_verbose, 'vcreate', os.args[1..]) + return + } + 'translate' { + eprintln('Translating C to V will be available in V 0.3') + exit(1) + } + 'install', 'list', 'outdated', 'remove', 'search', 'show', 'update', 'upgrade' { + util.launch_tool(prefs.is_verbose, 'vpm', os.args[1..]) + return + } + 'vlib-docs' { + util.launch_tool(prefs.is_verbose, 'vdoc', ['doc', 'vlib']) + } + 'get' { + eprintln('V Error: Use `v install` to install modules from vpm.vlang.io') + exit(1) + } + 'version' { + println(version.full_v_version(prefs.is_verbose)) + return + } + else {} + } + if command in ['run', 'build', 'build-module'] || command.ends_with('.v') || os.exists(command) { + // println('command') + // println(prefs.path) + builder.compile(command, prefs) + return + } + if prefs.is_help { + invoke_help_and_exit(args) + } + eprintln('v $command: unknown command\nRun ${term.highlight_command('v help')} for usage.') + exit(1) +} + +fn invoke_help_and_exit(remaining []string) { + match remaining.len { + 0, 1 { help.print_and_exit('default') } + 2 { help.print_and_exit(remaining[1]) } + else {} + } + println('${term.highlight_command('v help')}: provide only one help topic.') + println('For usage information, use ${term.highlight_command('v help')}.') + exit(1) +} diff --git a/v_windows/v/old/doc/docs.md b/v_windows/v/old/doc/docs.md new file mode 100644 index 0000000..8fbaa2d --- /dev/null +++ b/v_windows/v/old/doc/docs.md @@ -0,0 +1,5421 @@ +# V Documentation + +## Introduction + +V is a statically typed compiled programming language designed for building maintainable software. + +It's similar to Go and its design has also been influenced by Oberon, Rust, Swift, +Kotlin, and Python. + +V is a very simple language. Going through this documentation will take you about an hour, +and by the end of it you will have pretty much learned the entire language. + +The language promotes writing simple and clear code with minimal abstraction. + +Despite being simple, V gives the developer a lot of power. +Anything you can do in other languages, you can do in V. + +## Install from source +The major way to get the latest and greatest V, is to __install it from source__. +It is __easy__, and it usually takes __only a few seconds__. + +### Linux, macOS, FreeBSD, etc: +You need `git`, and a C compiler like `tcc`, `gcc` or `clang`, and `make`: +```bash +git clone https://github.com/vlang/v +cd v +make +``` + +### Windows: +You need `git`, and a C compiler like `tcc`, `gcc`, `clang` or `msvc`: +```bash +git clone https://github.com/vlang/v +cd v +make.bat -tcc +``` +NB: You can also pass one of `-gcc`, `-msvc`, `-clang` to `make.bat` instead, +if you do prefer to use a different C compiler, but -tcc is small, fast, and +easy to install (V will download a prebuilt binary automatically). + +It is recommended to add this folder to the PATH of your environment variables. +This can be done with the command `v.exe symlink`. + +### Android +Running V graphical apps on Android is also possible via [vab](https://github.com/vlang/vab). + +V Android dependencies: **V**, **Java JDK** >= 8, Android **SDK + NDK**. + + 1. Install dependencies (see [vab](https://github.com/vlang/vab)) + 2. Connect your Android device + 3. Run: + ```bash + git clone https://github.com/vlang/vab && cd vab && v vab.v + ./vab --device auto run /path/to/v/examples/sokol/particles + ``` +For more details and troubleshooting, please visit the [vab GitHub repository](https://github.com/vlang/vab). + +## Table of Contents + +
    timestampcommitcommit messagev -o v.cv -o vv -native 1mil.vv hello.vv.c sizeparsecheckcgenscanV linesV lines/s
    + +
    + +* [Hello world](#hello-world) +* [Running a project folder](#running-a-project-folder-with-several-files) +* [Comments](#comments) +* [Functions](#functions) + * [Returning multiple values](#returning-multiple-values) +* [Symbol visibility](#symbol-visibility) +* [Variables](#variables) +* [V types](#v-types) + * [Strings](#strings) + * [Numbers](#numbers) + * [Arrays](#arrays) + * [Fixed size arrays](#fixed-size-arrays) + * [Maps](#maps) +* [Module imports](#module-imports) +* [Statements & expressions](#statements--expressions) + * [If](#if) + * [In operator](#in-operator) + * [For loop](#for-loop) + * [Match](#match) + * [Defer](#defer) +* [Structs](#structs) + * [Embedded structs](#embedded-structs) + * [Default field values](#default-field-values) + * [Short struct literal syntax](#short-struct-literal-syntax) + * [Access modifiers](#access-modifiers) + * [Methods](#methods) +* [Unions](#unions) + + + +* [Functions 2](#functions-2) + * [Pure functions by default](#pure-functions-by-default) + * [Mutable arguments](#mutable-arguments) + * [Variable number of arguments](#variable-number-of-arguments) + * [Anonymous & higher-order functions](#anonymous--higher-order-functions) +* [References](#references) +* [Constants](#constants) +* [Builtin functions](#builtin-functions) +* [Printing custom types](#printing-custom-types) +* [Modules](#modules) + * [Manage Packages](#manage-packages) + * [Publish package](#publish-package) +* [Type Declarations](#type-declarations) + * [Interfaces](#interfaces) + * [Enums](#enums) + * [Sum types](#sum-types) + * [Type aliases](#type-aliases) + * [Option/Result types & error handling](#optionresult-types-and-error-handling) +* [Generics](#generics) +* [Concurrency](#concurrency) + * [Spawning Concurrent Tasks](#spawning-concurrent-tasks) + * [Channels](#channels) + * [Shared Objects](#shared-objects) +* [Decoding JSON](#decoding-json) +* [Testing](#testing) +* [Memory management](#memory-management) + * [Stack and Heap](#stack-and-heap) +* [ORM](#orm) + + + +* [Writing documentation](#writing-documentation) +* [Tools](#tools) + * [v fmt](#v-fmt) + * [Profiling](#profiling) +* [Advanced Topics](#advanced-topics) + * [Dumping expressions at runtime](#dumping-expressions-at-runtime) + * [Memory-unsafe code](#memory-unsafe-code) + * [Structs with reference fields](#structs-with-reference-fields) + * [sizeof and __offsetof](#sizeof-and-__offsetof) + * [Calling C from V](#calling-c-from-v) + * [Calling V from C](#calling-v-from-c) + * [Atomics](#atomics) + * [Global Variables](#global-variables) + * [Debugging](#debugging) + * [Conditional compilation](#conditional-compilation) + * [Compile time pseudo variables](#compile-time-pseudo-variables) + * [Compile-time reflection](#compile-time-reflection) + * [Limited operator overloading](#limited-operator-overloading) + * [Inline assembly](#inline-assembly) + * [Translating C to V](#translating-c-to-v) + * [Hot code reloading](#hot-code-reloading) + * [Cross compilation](#cross-compilation) + * [Cross-platform shell scripts in V](#cross-platform-shell-scripts-in-v) + * [Attributes](#attributes) + * [Goto](#goto) +* [Appendices](#appendices) + * [Keywords](#appendix-i-keywords) + * [Operators](#appendix-ii-operators) + +
    + + + +## Hello World + + +```v +fn main() { + println('hello world') +} +``` + +Save this snippet into a file named `hello.v`. Now do: `v run hello.v`. + +> That is assuming you have symlinked your V with `v symlink`, as described +[here](https://github.com/vlang/v/blob/master/README.md#symlinking). +If you haven't yet, you have to type the path to V manually. + +Congratulations - you just wrote and executed your first V program! + +You can compile a program without execution with `v hello.v`. +See `v help` for all supported commands. + +From the example above, you can see that functions are declared with the `fn` keyword. +The return type is specified after the function name. +In this case `main` doesn't return anything, so there is no return type. + +As in many other languages (such as C, Go, and Rust), `main` is the entry point of your program. + +`println` is one of the few built-in functions. +It prints the value passed to it to standard output. + +`fn main()` declaration can be skipped in one file programs. +This is useful when writing small programs, "scripts", or just learning the language. +For brevity, `fn main()` will be skipped in this tutorial. + +This means that a "hello world" program in V is as simple as + +```v +println('hello world') +``` + +## Running a project folder with several files + +Suppose you have a folder with several .v files in it, where one of them +contains your `main()` function, and the other files have other helper +functions. They may be organized by topic, but still *not yet* structured +enough to be their own separate reusable modules, and you want to compile +them all into one program. + +In other languages, you would have to use includes or a build system +to enumerate all files, compile them separately to object files, +then link them into one final executable. + +In V however, you can compile and run the whole folder of .v files together, +using just `v run .`. Passing parameters also works, so you can +do: `v run . --yourparam some_other_stuff` + +The above will first compile your files into a single program (named +after your folder/project), and then it will execute the program with +`--yourparam some_other_stuff` passed to it as CLI parameters. + +Your program can then use the CLI parameters like this: +```v +import os + +println(os.args) +``` +NB: after a successful run, V will delete the generated executable. +If you want to keep it, use `v -keepc run .` instead, or just compile +manually with `v .` . + +NB: any V compiler flags should be passed *before* the `run` command. +Everything after the source file/folder, will be passed to the program +as is - it will not be processed by V. + +## Comments + +```v +// This is a single line comment. +/* +This is a multiline comment. + /* It can be nested. */ +*/ +``` + +## Functions + +```v +fn main() { + println(add(77, 33)) + println(sub(100, 50)) +} + +fn add(x int, y int) int { + return x + y +} + +fn sub(x int, y int) int { + return x - y +} +``` + +Again, the type comes after the argument's name. + +Just like in Go and C, functions cannot be overloaded. +This simplifies the code and improves maintainability and readability. + +Functions can be used before their declaration: +`add` and `sub` are declared after `main`, but can still be called from `main`. +This is true for all declarations in V and eliminates the need for header files +or thinking about the order of files and declarations. + +### Returning multiple values + +```v +fn foo() (int, int) { + return 2, 3 +} + +a, b := foo() +println(a) // 2 +println(b) // 3 +c, _ := foo() // ignore values using `_` +``` + +## Symbol visibility + +```v +pub fn public_function() { +} + +fn private_function() { +} +``` + +Functions are private (not exported) by default. +To allow other modules to use them, prepend `pub`. The same applies +to constants and types. + +Note: `pub` can only be used from a named module. +For information about creating a module, see [Modules](#modules). + +## Variables + +```v +name := 'Bob' +age := 20 +large_number := i64(9999999999) +println(name) +println(age) +println(large_number) +``` + +Variables are declared and initialized with `:=`. This is the only +way to declare variables in V. This means that variables always have an initial +value. + +The variable's type is inferred from the value on the right hand side. +To choose a different type, use type conversion: +the expression `T(v)` converts the value `v` to the +type `T`. + +Unlike most other languages, V only allows defining variables in functions. +Global (module level) variables are not allowed. There's no global state in V +(see [Pure functions by default](#pure-functions-by-default) for details). + +For consistency across different code bases, all variable and function names +must use the `snake_case` style, as opposed to type names, which must use `PascalCase`. + +### Mutable variables + +```v +mut age := 20 +println(age) +age = 21 +println(age) +``` + +To change the value of the variable use `=`. In V, variables are +immutable by default. +To be able to change the value of the variable, you have to declare it with `mut`. + +Try compiling the program above after removing `mut` from the first line. + +### Initialization vs assignment + +Note the (important) difference between `:=` and `=`. +`:=` is used for declaring and initializing, `=` is used for assigning. + +```v failcompile +fn main() { + age = 21 +} +``` + +This code will not compile, because the variable `age` is not declared. +All variables need to be declared in V. + +```v +fn main() { + age := 21 +} +``` + +The values of multiple variables can be changed in one line. +In this way, their values can be swapped without an intermediary variable. + +```v +mut a := 0 +mut b := 1 +println('$a, $b') // 0, 1 +a, b = b, a +println('$a, $b') // 1, 0 +``` + +### Declaration errors + +In development mode the compiler will warn you that you haven't used the variable +(you'll get an "unused variable" warning). +In production mode (enabled by passing the `-prod` flag to v – `v -prod foo.v`) +it will not compile at all (like in Go). + +```v failcompile nofmt +fn main() { + a := 10 + if true { + a := 20 // error: redefinition of `a` + } + // warning: unused variable `a` +} +``` + +Unlike most languages, variable shadowing is not allowed. Declaring a variable with a name +that is already used in a parent scope will cause a compilation error. + +You can shadow imported modules though, as it is very useful in some situations: +```v ignore +import ui +import gg + +fn draw(ctx &gg.Context) { + gg := ctx.parent.get_ui().gg + gg.draw_rect(10, 10, 100, 50) +} +``` + +## V Types + +### Primitive types + +```v ignore +bool + +string + +i8 i16 int i64 i128 (soon) +byte u16 u32 u64 u128 (soon) + +rune // represents a Unicode code point + +f32 f64 + +voidptr, size_t // these are mostly used for C interoperability + +any // similar to C's void* and Go's interface{} +``` + +Please note that unlike C and Go, `int` is always a 32 bit integer. + +There is an exception to the rule that all operators +in V must have values of the same type on both sides. A small primitive type +on one side can be automatically promoted if it fits +completely into the data range of the type on the other side. +These are the allowed possibilities: + +```v ignore + i8 → i16 → int → i64 + ↘ ↘ + f32 → f64 + ↗ ↗ + byte → u16 → u32 → u64 ⬎ + ↘ ↘ ↘ ptr + i8 → i16 → int → i64 ⬏ +``` +An `int` value for example can be automatically promoted to `f64` +or `i64` but not to `u32`. (`u32` would mean loss of the sign for +negative values). +Promotion from `int` to `f32`, however, is currently done automatically +(but can lead to precision loss for large values). + +Literals like `123` or `4.56` are treated in a special way. They do +not lead to type promotions, however they default to `int` and `f64` +respectively, when their type has to be decided: + +```v nofmt +u := u16(12) +v := 13 + u // v is of type `u16` - no promotion +x := f32(45.6) +y := x + 3.14 // x is of type `f32` - no promotion +a := 75 // a is of type `int` - default for int literal +b := 14.7 // b is of type `f64` - default for float literal +c := u + a // c is of type `int` - automatic promotion of `u`'s value +d := b + x // d is of type `f64` - automatic promotion of `x`'s value +``` + +### Strings + +```v +name := 'Bob' +println(name.len) +println(name[0]) // indexing gives a byte B +println(name[1..3]) // slicing gives a string 'ob' +windows_newline := '\r\n' // escape special characters like in C +assert windows_newline.len == 2 +``` + +In V, a string is a read-only array of bytes. String data is encoded using UTF-8. +String values are immutable. You cannot mutate elements: + +```v failcompile +mut s := 'hello 🌎' +s[0] = `H` // not allowed +``` +> error: cannot assign to `s[i]` since V strings are immutable + +Note that indexing a string will produce a `byte`, not a `rune`. Indexes correspond +to bytes in the string, not Unicode code points. + +Character literals have type `rune`. To denote them, use ` + +```v +rocket := `🚀` +assert 'aloha!'[0] == `a` +``` + +Both single and double quotes can be used to denote strings. For consistency, +`vfmt` converts double quotes to single quotes unless the string contains a single quote character. + +For raw strings, prepend `r`. Raw strings are not escaped: + +```v +s := r'hello\nworld' +println(s) // "hello\nworld" +``` + +Strings can be easily converted to integers: + +```v +s := '42' +n := s.int() // 42 +``` + +### Runes +A `rune` represents a unicode character and is an alias for `u32`. Runes can be created like this: +```v +x := `🚀` +``` + +A string can be converted to runes by the `.runes()` method. +```v +hello := 'Hello World 👋' +hello_runes := hello.runes() // [`H`, `e`, `l`, `l`, `o`, ` `, `W`, `o`, `r`, `l`, `d`, ` `, `👋`] +``` + +### String interpolation + +Basic interpolation syntax is pretty simple - use `$` before a variable name. +The variable will be converted to a string and embedded into the literal: +```v +name := 'Bob' +println('Hello, $name!') // Hello, Bob! +``` +It also works with fields: `'age = $user.age'`. +If you need more complex expressions, use `${}`: `'can register = ${user.age > 13}'`. + +Format specifiers similar to those in C's `printf()` are also supported. +`f`, `g`, `x`, etc. are optional and specify the output format. +The compiler takes care of the storage size, so there is no `hd` or `llu`. + +```v +x := 123.4567 +println('x = ${x:4.2f}') +println('[${x:10}]') // pad with spaces on the left => [ 123.457] +println('[${int(x):-10}]') // pad with spaces on the right => [123 ] +println('[${int(x):010}]') // pad with zeros on the left => [0000000123] +``` + +### String operators + +```v +name := 'Bob' +bobby := name + 'by' // + is used to concatenate strings +println(bobby) // "Bobby" +mut s := 'hello ' +s += 'world' // `+=` is used to append to a string +println(s) // "hello world" +``` + +All operators in V must have values of the same type on both sides. +You cannot concatenate an integer to a string: + +```v failcompile +age := 10 +println('age = ' + age) // not allowed +``` +> error: infix expr: cannot use `int` (right expression) as `string` + +We have to either convert `age` to a `string`: + +```v +age := 11 +println('age = ' + age.str()) +``` + +or use string interpolation (preferred): + +```v +age := 12 +println('age = $age') +``` + +### Numbers + +```v +a := 123 +``` + +This will assign the value of 123 to `a`. By default `a` will have the +type `int`. + +You can also use hexadecimal, binary or octal notation for integer literals: + +```v +a := 0x7B +b := 0b01111011 +c := 0o173 +``` + +All of these will be assigned the same value, 123. They will all have type +`int`, no matter what notation you used. + +V also supports writing numbers with `_` as separator: + +```v +num := 1_000_000 // same as 1000000 +three := 0b0_11 // same as 0b11 +float_num := 3_122.55 // same as 3122.55 +hexa := 0xF_F // same as 255 +oct := 0o17_3 // same as 0o173 +``` + +If you want a different type of integer, you can use casting: + +```v +a := i64(123) +b := byte(42) +c := i16(12345) +``` + +Assigning floating point numbers works the same way: + +```v +f := 1.0 +f1 := f64(3.14) +f2 := f32(3.14) +``` +If you do not specify the type explicitly, by default float literals +will have the type of `f64`. + +Float literals can also be declared as a power of ten: +```v +f0 := 42e1 // 420 +f1 := 123e-2 // 1.23 +f2 := 456e+2 // 45600 +``` + +### Arrays +#### Basic Array Concepts +Arrays are collections of data elements of the same type. They can be represented by +a list of elements surrounded by brackets. The elements can be accessed by appending +an *index* (starting with `0`) in brackets to the array variable: +```v +mut nums := [1, 2, 3] +println(nums) // `[1, 2, 3]` +println(nums[0]) // `1` +println(nums[1]) // `2` +nums[1] = 5 +println(nums) // `[1, 5, 3]` +``` +#### Array Properties +There are two properties that control the "size" of an array: +* `len`: *length* - the number of pre-allocated and initialized elements in the array +* `cap`: *capacity* - the amount of memory space which has been reserved for elements, +but not initialized or counted as elements. The array can grow up to this size without +being reallocated. Usually, V takes care of this property automatically but there are +cases where the user may want to do manual optimizations (see [below](#array-initialization)). + +```v +mut nums := [1, 2, 3] +println(nums.len) // "3" +println(nums.cap) // "3" or greater +nums = [] // The array is now empty +println(nums.len) // "0" +``` + +Note that the properties are read-only fields and can't be modified by the user. + +#### Array Initialization +The basic initialization syntax is as described [above](#basic-array-concepts). +The type of an array is determined by the first element: +* `[1, 2, 3]` is an array of ints (`[]int`). +* `['a', 'b']` is an array of strings (`[]string`). + +The user can explicitly specify the type for the first element: `[byte(16), 32, 64, 128]`. +V arrays are homogeneous (all elements must have the same type). +This means that code like `[1, 'a']` will not compile. + +The above syntax is fine for a small number of known elements but for very large or empty +arrays there is a second initialization syntax: +```v +mut a := []int{len: 10000, cap: 30000, init: 3} +``` +This creates an array of 10000 `int` elements that are all initialized with `3`. Memory +space is reserved for 30000 elements. The parameters `len`, `cap` and `init` are optional; +`len` defaults to `0` and `init` to the default initialization of the element type (`0` +for numerical type, `''` for `string`, etc). The run time system makes sure that the +capacity is not smaller than `len` (even if a smaller value is specified explicitly): + +```v +arr := []int{len: 5, init: -1} +// `arr == [-1, -1, -1, -1, -1]`, arr.cap == 5 + +// Declare an empty array: +users := []int{} +``` + + +Setting the capacity improves performance of pushing elements to the array +as reallocations can be avoided: + +```v +mut numbers := []int{cap: 1000} +println(numbers.len) // 0 +// Now appending elements won't reallocate +for i in 0 .. 1000 { + numbers << i +} +``` +Note: The above code uses a [range `for`](#range-for) statement and a +[push operator (`<<`)](#array-operations). + +#### Array Types + +An array can be of these types: +| Types | Example Definition | +| ------------ | ------------------------------------ | +| Number | `[]int,[]i64` | +| String | `[]string` | +| Rune | `[]rune` | +| Boolean | `[]bool` | +| Array | `[][]int` | +| Struct | `[]MyStructName` | +| Channel | `[]chan f64` | +| Function | `[]MyFunctionType` `[]fn (int) bool` | +| Interface | `[]MyInterfaceName` | +| Sum Type | `[]MySumTypeName` | +| Generic Type | `[]T` | +| Map | `[]map[string]f64` | +| Enum | `[]MyEnumType` | +| Alias | `[]MyAliasTypeName` | +| Thread | `[]thread int` | +| Reference | `[]&f64` | +| Shared | `[]shared MyStructType` | + +**Example Code:** + +This example uses [Structs](#structs) and [Sum Types](#sum-types) to create an array +which can handle different types (e.g. Points, Lines) of data elements. + +```v +struct Point { + x int + y int +} + +struct Line { + p1 Point + p2 Point +} + +type ObjectSumType = Line | Point + +mut object_list := []ObjectSumType{} +object_list << Point{1, 1} +object_list << Line{ + p1: Point{3, 3} + p2: Point{4, 4} +} +dump(object_list) +/* +object_list: [ObjectSumType(Point{ + x: 1 + y: 1 +}), ObjectSumType(Line{ + p1: Point{ + x: 3 + y: 3 + } + p2: Point{ + x: 4 + y: 4 + } +})] +*/ +``` + +#### Multidimensional Arrays + +Arrays can have more than one dimension. + +2d array example: +```v +mut a := [][]int{len: 2, init: []int{len: 3}} +a[0][1] = 2 +println(a) // [[0, 2, 0], [0, 0, 0]] +``` + +3d array example: +```v +mut a := [][][]int{len: 2, init: [][]int{len: 3, init: []int{len: 2}}} +a[0][1][1] = 2 +println(a) // [[[0, 0], [0, 2], [0, 0]], [[0, 0], [0, 0], [0, 0]]] +``` + +#### Array Operations + +Elements can be appended to the end of an array using the push operator `<<`. +It can also append an entire array. + +```v +mut nums := [1, 2, 3] +nums << 4 +println(nums) // "[1, 2, 3, 4]" +// append array +nums << [5, 6, 7] +println(nums) // "[1, 2, 3, 4, 5, 6, 7]" +mut names := ['John'] +names << 'Peter' +names << 'Sam' +// names << 10 <-- This will not compile. `names` is an array of strings. +``` + +`val in array` returns true if the array contains `val`. See [`in` operator](#in-operator). + +```v +names := ['John', 'Peter', 'Sam'] +println(names.len) // "3" +println('Alex' in names) // "false" +``` + + +#### Array methods + +All arrays can be easily printed with `println(arr)` and converted to a string +with `s := arr.str()`. + +Copying the data from the array is done with `.clone()`: + +```v +nums := [1, 2, 3] +nums_copy := nums.clone() +``` + +Arrays can be efficiently filtered and mapped with the `.filter()` and +`.map()` methods: + +```v +nums := [1, 2, 3, 4, 5, 6] +even := nums.filter(it % 2 == 0) +println(even) // [2, 4, 6] +// filter can accept anonymous functions +even_fn := nums.filter(fn (x int) bool { + return x % 2 == 0 +}) +println(even_fn) +words := ['hello', 'world'] +upper := words.map(it.to_upper()) +println(upper) // ['HELLO', 'WORLD'] +// map can also accept anonymous functions +upper_fn := words.map(fn (w string) string { + return w.to_upper() +}) +println(upper_fn) // ['HELLO', 'WORLD'] +``` + +`it` is a builtin variable which refers to element currently being processed in filter/map methods. + +Additionally, `.any()` and `.all()` can be used to conveniently test +for elements that satisfy a condition. + +```v +nums := [1, 2, 3] +println(nums.any(it == 2)) // true +println(nums.all(it >= 2)) // false +``` + +There are further built in methods for arrays: +* `b := a.repeat(n)` concatenate `n` times the elements of `a` +* `a.insert(i, val)` insert new element `val` at index `i` and move all following elements upwards +* `a.insert(i, [3, 4, 5])` insert several elements +* `a.prepend(val)` insert value at beginning, equivalent to `a.insert(0, val)` +* `a.prepend(arr)` insert elements of array `arr` at beginning +* `a.trim(new_len)` truncate the length (if `new_length < a.len`, otherwise do nothing) +* `a.clear()` empty the array (without changing `cap`, equivalent to `a.trim(0)`) +* `a.delete_many(start, size)` removes `size` consecutive elements beginning with index `start` + – triggers reallocation +* `a.delete(index)` equivalent to `a.delete_many(index, 1)` +* `v := a.first()` equivalent to `v := a[0]` +* `v := a.last()` equivalent to `v := a[a.len - 1]` +* `v := a.pop()` get last element and remove it from array +* `a.delete_last()` remove last element from array +* `b := a.reverse()` make `b` contain the elements of `a` in reversed order +* `a.reverse_in_place()` reverse the order of elements in `a` +* `a.join(joiner)` concatenate array of strings into a string using `joiner` string as a separator + +#### Sorting Arrays + +Sorting arrays of all kinds is very simple and intuitive. Special variables `a` and `b` +are used when providing a custom sorting condition. + +```v +mut numbers := [1, 3, 2] +numbers.sort() // 1, 2, 3 +numbers.sort(a > b) // 3, 2, 1 +``` + +```v +struct User { + age int + name string +} + +mut users := [User{21, 'Bob'}, User{20, 'Zarkon'}, User{25, 'Alice'}] +users.sort(a.age < b.age) // sort by User.age int field +users.sort(a.name > b.name) // reverse sort by User.name string field +``` +V also supports custom sorting, through the `sort_with_compare` array method. +Which expects a comparing function which will define the sort order. +Useful for sorting on multiple fields at the same time by custom sorting rules. +The code below sorts the array ascending on `name` and descending `age`. +```v +struct User { + age int + name string +} + +mut users := [User{21, 'Bob'}, User{65, 'Bob'}, User{25, 'Alice'}] + +custom_sort_fn := fn (a &User, b &User) int { + // return -1 when a comes before b + // return 0, when both are in same order + // return 1 when b comes before a + if a.name == b.name { + if a.age < b.age { + return 1 + } + if a.age > b.age { + return -1 + } + return 0 + } + if a.name < b.name { + return -1 + } else if a.name > b.name { + return 1 + } + return 0 +} +users.sort_with_compare(custom_sort_fn) +``` + +#### Array Slices + +A slice is a part of a parent array. Initially it refers to the elements +between two indices separated by a `..` operator. The right-side index must +be greater than or equal to the left side index. + +If a right-side index is absent, it is assumed to be the array length. If a +left-side index is absent, it is assumed to be 0. + +```v +nums := [0, 10, 20, 30, 40] +println(nums[1..4]) // [10, 20, 30] +println(nums[..4]) // [0, 10, 20, 30] +println(nums[1..]) // [10, 20, 30, 40] +``` + +In V slices are arrays themselves (they are no distinct types). As a result +all array operations may be performed on them. E.g. they can be pushed onto an +array of the same type: + +```v +array_1 := [3, 5, 4, 7, 6] +mut array_2 := [0, 1] +array_2 << array_1[..3] +println(array_2) // `[0, 1, 3, 5, 4]` +``` + +A slice is always created with the smallest possible capacity `cap == len` (see +[`cap` above](#array-initialization)) no matter what the capacity or length +of the parent array is. As a result it is immediately reallocated and copied to another +memory location when the size increases thus becoming independent from the +parent array (*copy on grow*). In particular pushing elements to a slice +does not alter the parent: +```v +mut a := [0, 1, 2, 3, 4, 5] +mut b := a[2..4] +b[0] = 7 // `b[0]` is referring to `a[2]` +println(a) // `[0, 1, 7, 3, 4, 5]` +b << 9 +// `b` has been reallocated and is now independent from `a` +println(a) // `[0, 1, 7, 3, 4, 5]` - no change +println(b) // `[7, 3, 9]` +``` + +Appending to the parent array may or may not make it independent from its child slices. +The behaviour depends on the parent's capacity and is predictable: +```v +mut a := []int{len: 5, cap: 6, init: 2} +mut b := a[1..4] +a << 3 +// no reallocation - fits in `cap` +b[2] = 13 // `a[3]` is modified +a << 4 +// a has been reallocated and is now independent from `b` (`cap` was exceeded) +b[1] = 3 // no change in `a` +println(a) // `[2, 2, 2, 13, 2, 3, 4]` +println(b) // `[2, 3, 13]` +``` + +### Fixed size arrays + +V also supports arrays with fixed size. Unlike ordinary arrays, their +length is constant. You cannot append elements to them, nor shrink them. +You can only modify their elements in place. + +However, access to the elements of fixed size arrays is more efficient, +they need less memory than ordinary arrays, and unlike ordinary arrays, +their data is on the stack, so you may want to use them as buffers if you +do not want additional heap allocations. + +Most methods are defined to work on ordinary arrays, not on fixed size arrays. +You can convert a fixed size array to an ordinary array with slicing: +```v +mut fnums := [3]int{} // fnums is a fixed size array with 3 elements. +fnums[0] = 1 +fnums[1] = 10 +fnums[2] = 100 +println(fnums) // => [1, 10, 100] +println(typeof(fnums).name) // => [3]int + +fnums2 := [1, 10, 100]! // short init syntax that does the same (the syntax will probably change) + +anums := fnums[0..fnums.len] +println(anums) // => [1, 10, 100] +println(typeof(anums).name) // => []int +``` +Note that slicing will cause the data of the fixed size array to be copied to +the newly created ordinary array. + +### Maps + +```v +mut m := map[string]int{} // a map with `string` keys and `int` values +m['one'] = 1 +m['two'] = 2 +println(m['one']) // "1" +println(m['bad_key']) // "0" +println('bad_key' in m) // Use `in` to detect whether such key exists +m.delete('two') +``` +Maps can have keys of type string, rune, integer, float or voidptr. + +The whole map can be initialized using this short syntax: +```v +numbers := map{ + 'one': 1 + 'two': 2 +} +println(numbers) +``` + +If a key is not found, a zero value is returned by default: + +```v +sm := map{ + 'abc': 'xyz' +} +val := sm['bad_key'] +println(val) // '' +``` +```v +intm := map{ + 1: 1234 + 2: 5678 +} +s := intm[3] +println(s) // 0 +``` + +It's also possible to use an `or {}` block to handle missing keys: + +```v +mm := map[string]int{} +val := mm['bad_key'] or { panic('key not found') } +``` + +The same optional check applies to arrays: + +```v +arr := [1, 2, 3] +large_index := 999 +val := arr[large_index] or { panic('out of bounds') } +``` + +## Module imports + +For information about creating a module, see [Modules](#modules). + +Modules can be imported using the `import` keyword: + +```v +import os + +fn main() { + // read text from stdin + name := os.input('Enter your name: ') + println('Hello, $name!') +} +``` +This program can use any public definitions from the `os` module, such +as the `input` function. See the [standard library](https://modules.vlang.io/) +documentation for a list of common modules and their public symbols. + +By default, you have to specify the module prefix every time you call an external function. +This may seem verbose at first, but it makes code much more readable +and easier to understand - it's always clear which function from +which module is being called. This is especially useful in large code bases. + +Cyclic module imports are not allowed, like in Go. + +### Selective imports + +You can also import specific functions and types from modules directly: + +```v +import os { input } + +fn main() { + // read text from stdin + name := input('Enter your name: ') + println('Hello, $name!') +} +``` +Note: This will import the module as well. Also, this is not allowed for +constants - they must always be prefixed. + +You can import several specific symbols at once: + +```v +import os { input, user_os } + +name := input('Enter your name: ') +println('Name: $name') +os := user_os() +println('Your OS is ${os}.') +``` + +### Module import aliasing + +Any imported module name can be aliased using the `as` keyword: + +NOTE: this example will not compile unless you have created `mymod/sha256.v` +```v failcompile +import crypto.sha256 +import mymod.sha256 as mysha256 + +fn main() { + v_hash := sha256.sum('hi'.bytes()).hex() + my_hash := mysha256.sum('hi'.bytes()).hex() + assert my_hash == v_hash +} +``` + +You cannot alias an imported function or type. +However, you _can_ redeclare a type. + +```v +import time +import math + +type MyTime = time.Time + +fn (mut t MyTime) century() int { + return int(1.0 + math.trunc(f64(t.year) * 0.009999794661191)) +} + +fn main() { + mut my_time := MyTime{ + year: 2020 + month: 12 + day: 25 + } + println(time.new_time(my_time).utc_string()) + println('Century: $my_time.century()') +} +``` + +## Statements & expressions + +### If + +```v +a := 10 +b := 20 +if a < b { + println('$a < $b') +} else if a > b { + println('$a > $b') +} else { + println('$a == $b') +} +``` + +`if` statements are pretty straightforward and similar to most other languages. +Unlike other C-like languages, +there are no parentheses surrounding the condition and the braces are always required. + +`if` can be used as an expression: + +```v +num := 777 +s := if num % 2 == 0 { 'even' } else { 'odd' } +println(s) +// "odd" +``` + +#### Type checks and casts +You can check the current type of a sum type using `is` and its negated form `!is`. + +You can do it either in an `if`: +```v +struct Abc { + val string +} + +struct Xyz { + foo string +} + +type Alphabet = Abc | Xyz + +x := Alphabet(Abc{'test'}) // sum type +if x is Abc { + // x is automatically casted to Abc and can be used here + println(x) +} +if x !is Abc { + println('Not Abc') +} +``` +or using `match`: +```v oksyntax +match x { + Abc { + // x is automatically casted to Abc and can be used here + println(x) + } + Xyz { + // x is automatically casted to Xyz and can be used here + println(x) + } +} +``` + +This works also with struct fields: +```v +struct MyStruct { + x int +} + +struct MyStruct2 { + y string +} + +type MySumType = MyStruct | MyStruct2 + +struct Abc { + bar MySumType +} + +x := Abc{ + bar: MyStruct{123} // MyStruct will be converted to MySumType type automatically +} +if x.bar is MyStruct { + // x.bar is automatically casted + println(x.bar) +} +match x.bar { + MyStruct { + // x.bar is automatically casted + println(x.bar) + } + else {} +} +``` + +Mutable variables can change, and doing a cast would be unsafe. +However, sometimes it's useful to type cast despite mutability. +In such cases the developer must mark the expression with the `mut` keyword +to tell the compiler that they know what they're doing. + +It works like this: +```v oksyntax +mut x := MySumType(MyStruct{123}) +if mut x is MyStruct { + // x is casted to MyStruct even if it's mutable + // without the mut keyword that wouldn't work + println(x) +} +// same with match +match mut x { + MyStruct { + // x is casted to MyStruct even it's mutable + // without the mut keyword that wouldn't work + println(x) + } +} +``` + +### In operator + +`in` allows to check whether an array or a map contains an element. +To do the opposite, use `!in`. + +```v +nums := [1, 2, 3] +println(1 in nums) // true +println(4 !in nums) // true +m := map{ + 'one': 1 + 'two': 2 +} +println('one' in m) // true +println('three' !in m) // true +``` + +It's also useful for writing boolean expressions that are clearer and more compact: + +```v +enum Token { + plus + minus + div + mult +} + +struct Parser { + token Token +} + +parser := Parser{} +if parser.token == .plus || parser.token == .minus || parser.token == .div || parser.token == .mult { + // ... +} +if parser.token in [.plus, .minus, .div, .mult] { + // ... +} +``` + +V optimizes such expressions, +so both `if` statements above produce the same machine code and no arrays are created. + +### For loop + +V has only one looping keyword: `for`, with several forms. + +#### `for`/`in` + +This is the most common form. You can use it with an array, map or +numeric range. + +##### Array `for` + +```v +numbers := [1, 2, 3, 4, 5] +for num in numbers { + println(num) +} +names := ['Sam', 'Peter'] +for i, name in names { + println('$i) $name') + // Output: 0) Sam + // 1) Peter +} +``` + +The `for value in arr` form is used for going through elements of an array. +If an index is required, an alternative form `for index, value in arr` can be used. + +Note, that the value is read-only. +If you need to modify the array while looping, you need to declare the element as mutable: + +```v +mut numbers := [0, 1, 2] +for mut num in numbers { + num++ +} +println(numbers) // [1, 2, 3] +``` +When an identifier is just a single underscore, it is ignored. + +##### Custom iterators +Types that implement a `next` method returning an `Option` can be iterated +with a `for` loop. + +```v +struct SquareIterator { + arr []int +mut: + idx int +} + +fn (mut iter SquareIterator) next() ?int { + if iter.idx >= iter.arr.len { + return error('') + } + defer { + iter.idx++ + } + return iter.arr[iter.idx] * iter.arr[iter.idx] +} + +nums := [1, 2, 3, 4, 5] +iter := SquareIterator{ + arr: nums +} +for squared in iter { + println(squared) +} +``` + +The code above prints: +``` +1 +4 +9 +16 +25 +``` + +##### Map `for` + +```v +m := map{ + 'one': 1 + 'two': 2 +} +for key, value in m { + println('$key -> $value') + // Output: one -> 1 + // two -> 2 +} +``` + +Either key or value can be ignored by using a single underscore as the identifier. +```v +m := map{ + 'one': 1 + 'two': 2 +} +// iterate over keys +for key, _ in m { + println(key) + // Output: one + // two +} +// iterate over values +for _, value in m { + println(value) + // Output: 1 + // 2 +} +``` + +##### Range `for` + +```v +// Prints '01234' +for i in 0 .. 5 { + print(i) +} +``` +`low..high` means an *exclusive* range, which represents all values +from `low` up to *but not including* `high`. + +#### Condition `for` + +```v +mut sum := 0 +mut i := 0 +for i <= 100 { + sum += i + i++ +} +println(sum) // "5050" +``` + +This form of the loop is similar to `while` loops in other languages. +The loop will stop iterating once the boolean condition evaluates to false. +Again, there are no parentheses surrounding the condition, and the braces are always required. + +#### Bare `for` + +```v +mut num := 0 +for { + num += 2 + if num >= 10 { + break + } +} +println(num) // "10" +``` + +The condition can be omitted, resulting in an infinite loop. + +#### C `for` + +```v +for i := 0; i < 10; i += 2 { + // Don't print 6 + if i == 6 { + continue + } + println(i) +} +``` + +Finally, there's the traditional C style `for` loop. It's safer than the `while` form +because with the latter it's easy to forget to update the counter and get +stuck in an infinite loop. + +Here `i` doesn't need to be declared with `mut` since it's always going to be mutable by definition. + +#### Labelled break & continue + +`break` and `continue` control the innermost `for` loop by default. +You can also use `break` and `continue` followed by a label name to refer to an outer `for` +loop: + +```v +outer: for i := 4; true; i++ { + println(i) + for { + if i < 7 { + continue outer + } else { + break outer + } + } +} +``` +The label must immediately precede the outer loop. +The above code prints: +``` +4 +5 +6 +7 +``` + +### Match + +```v +os := 'windows' +print('V is running on ') +match os { + 'darwin' { println('macOS.') } + 'linux' { println('Linux.') } + else { println(os) } +} +``` + +A match statement is a shorter way to write a sequence of `if - else` statements. +When a matching branch is found, the following statement block will be run. +The else branch will be run when no other branches match. + +```v +number := 2 +s := match number { + 1 { 'one' } + 2 { 'two' } + else { 'many' } +} +``` + +A match expression returns the value of the final expression from the matching branch. + +```v +enum Color { + red + blue + green +} + +fn is_red_or_blue(c Color) bool { + return match c { + .red, .blue { true } // comma can be used to test multiple values + .green { false } + } +} +``` + +A match statement can also be used to branch on the variants of an `enum` +by using the shorthand `.variant_here` syntax. An `else` branch is not allowed +when all the branches are exhaustive. + +```v +c := `v` +typ := match c { + `0`...`9` { 'digit' } + `A`...`Z` { 'uppercase' } + `a`...`z` { 'lowercase' } + else { 'other' } +} +println(typ) +// 'lowercase' +``` + +You can also use ranges as `match` patterns. If the value falls within the range +of a branch, that branch will be executed. + +Note that the ranges use `...` (three dots) rather than `..` (two dots). This is +because the range is *inclusive* of the last element, rather than exclusive +(as `..` ranges are). Using `..` in a match branch will throw an error. + +Note: `match` as an expression is not usable in `for` loop and `if` statements. + +### Defer + +A defer statement defers the execution of a block of statements +until the surrounding function returns. + +```v +import os + +fn read_log() { + mut ok := false + mut f := os.open('log.txt') or { panic(err.msg) } + defer { + f.close() + } + // ... + if !ok { + // defer statement will be called here, the file will be closed + return + } + // ... + // defer statement will be called here, the file will be closed +} +``` + +If the function returns a value the `defer` block is executed *after* the return +expression is evaluated: + +```v +import os + +enum State { + normal + write_log + return_error +} + +// write log file and return number of bytes written +fn write_log(s State) ?int { + mut f := os.create('log.txt') ? + defer { + f.close() + } + if s == .write_log { + // `f.close()` will be called after `f.write()` has been + // executed, but before `write_log()` finally returns the + // number of bytes written to `main()` + return f.writeln('This is a log file') + } else if s == .return_error { + // the file will be closed after the `error()` function + // has returned - so the error message will still report + // it as open + return error('nothing written; file open: $f.is_opened') + } + // the file will be closed here, too + return 0 +} + +fn main() { + n := write_log(.return_error) or { + println('Error: $err') + 0 + } + println('$n bytes written') +} +``` + +## Structs + +```v +struct Point { + x int + y int +} + +mut p := Point{ + x: 10 + y: 20 +} +println(p.x) // Struct fields are accessed using a dot +// Alternative literal syntax for structs with 3 fields or fewer +p = Point{10, 20} +assert p.x == 10 +``` + +### Heap structs + +Structs are allocated on the stack. To allocate a struct on the heap +and get a reference to it, use the `&` prefix: + +```v +struct Point { + x int + y int +} + +p := &Point{10, 10} +// References have the same syntax for accessing fields +println(p.x) +``` + +The type of `p` is `&Point`. It's a [reference](#references) to `Point`. +References are similar to Go pointers and C++ references. + +### Embedded structs + +V doesn't allow subclassing, but it supports embedded structs: + +```v +struct Widget { +mut: + x int + y int +} + +struct Button { + Widget + title string +} + +mut button := Button{ + title: 'Click me' +} +button.x = 3 +``` +Without embedding we'd have to name the `Widget` field and do: + +```v oksyntax +button.widget.x = 3 +``` + +### Default field values + +```v +struct Foo { + n int // n is 0 by default + s string // s is '' by default + a []int // a is `[]int{}` by default + pos int = -1 // custom default value +} +``` + +All struct fields are zeroed by default during the creation of the struct. +Array and map fields are allocated. + +It's also possible to define custom default values. + +### Required fields + +```v +struct Foo { + n int [required] +} +``` + +You can mark a struct field with the `[required]` attribute, to tell V that +that field must be initialized when creating an instance of that struct. + +This example will not compile, since the field `n` isn't explicitly initialized: +```v failcompile +_ = Foo{} +``` + + + +### Short struct literal syntax + +```v +struct Point { + x int + y int +} + +mut p := Point{ + x: 10 + y: 20 +} +// you can omit the struct name when it's already known +p = { + x: 30 + y: 4 +} +assert p.y == 4 +// +// array: first element defines type of array +points := [Point{10, 20}, Point{20, 30}, Point{40, 50}] +println(points) // [Point{x: 10, y: 20}, Point{x: 20, y: 30}, Point{x: 40,y: 50}] +``` + +Omitting the struct name also works for returning a struct literal or passing one +as a function argument. + +#### Trailing struct literal arguments + +V doesn't have default function arguments or named arguments, for that trailing struct +literal syntax can be used instead: + +```v +struct ButtonConfig { + text string + is_disabled bool + width int = 70 + height int = 20 +} + +struct Button { + text string + width int + height int +} + +fn new_button(c ButtonConfig) &Button { + return &Button{ + width: c.width + height: c.height + text: c.text + } +} + +button := new_button(text: 'Click me', width: 100) +// the height is unset, so it's the default value +assert button.height == 20 +``` + +As you can see, both the struct name and braces can be omitted, instead of: + +```v oksyntax nofmt +new_button(ButtonConfig{text:'Click me', width:100}) +``` + +This only works for functions that take a struct for the last argument. + +### Access modifiers + +Struct fields are private and immutable by default (making structs immutable as well). +Their access modifiers can be changed with +`pub` and `mut`. In total, there are 5 possible options: + +```v +struct Foo { + a int // private immutable (default) +mut: + b int // private mutable + c int // (you can list multiple fields with the same access modifier) +pub: + d int // public immutable (readonly) +pub mut: + e int // public, but mutable only in parent module +__global: + // (not recommended to use, that's why the 'global' keyword starts with __) + f int // public and mutable both inside and outside parent module +} +``` + +For example, here's the `string` type defined in the `builtin` module: + +```v ignore +struct string { + str &byte +pub: + len int +} +``` + +It's easy to see from this definition that `string` is an immutable type. +The byte pointer with the string data is not accessible outside `builtin` at all. +The `len` field is public, but immutable: +```v failcompile +fn main() { + str := 'hello' + len := str.len // OK + str.len++ // Compilation error +} +``` + +This means that defining public readonly fields is very easy in V, +no need in getters/setters or properties. + +## Methods + +```v +struct User { + age int +} + +fn (u User) can_register() bool { + return u.age > 16 +} + +user := User{ + age: 10 +} +println(user.can_register()) // "false" +user2 := User{ + age: 20 +} +println(user2.can_register()) // "true" +``` + +V doesn't have classes, but you can define methods on types. +A method is a function with a special receiver argument. +The receiver appears in its own argument list between the `fn` keyword and the method name. +Methods must be in the same module as the receiver type. + +In this example, the `can_register` method has a receiver of type `User` named `u`. +The convention is not to use receiver names like `self` or `this`, +but a short, preferably one letter long, name. + +## Unions + +Just like structs, unions support embedding. + +```v +struct Rgba32_Component { + r byte + g byte + b byte + a byte +} + +union Rgba32 { + Rgba32_Component + value u32 +} + +clr1 := Rgba32{ + value: 0x008811FF +} + +clr2 := Rgba32{ + Rgba32_Component: { + a: 128 + } +} + +sz := sizeof(Rgba32) +unsafe { + println('Size: ${sz}B,clr1.b: $clr1.b,clr2.b: $clr2.b') +} +``` + +Output: `Size: 4B, clr1.b: 136, clr2.b: 0` + +Union member access must be performed in an `unsafe` block. + +Note that the embedded struct arguments are not necessarily stored in the order listed. + +## Functions 2 + +### Pure functions by default + +V functions are pure by default, meaning that their return values are a function of their +arguments only, and their evaluation has no side effects (besides I/O). + +This is achieved by a lack of global variables and all function arguments being +immutable by default, even when [references](#references) are passed. + +V is not a purely functional language however. + +There is a compiler flag to enable global variables (`-enable-globals`), but this is +intended for low-level applications like kernels and drivers. + +### Mutable arguments + +It is possible to modify function arguments by using the keyword `mut`: + +```v +struct User { + name string +mut: + is_registered bool +} + +fn (mut u User) register() { + u.is_registered = true +} + +mut user := User{} +println(user.is_registered) // "false" +user.register() +println(user.is_registered) // "true" +``` + +In this example, the receiver (which is simply the first argument) is marked as mutable, +so `register()` can change the user object. The same works with non-receiver arguments: + +```v +fn multiply_by_2(mut arr []int) { + for i in 0 .. arr.len { + arr[i] *= 2 + } +} + +mut nums := [1, 2, 3] +multiply_by_2(mut nums) +println(nums) +// "[2, 4, 6]" +``` + +Note, that you have to add `mut` before `nums` when calling this function. This makes +it clear that the function being called will modify the value. + +It is preferable to return values instead of modifying arguments. +Modifying arguments should only be done in performance-critical parts of your application +to reduce allocations and copying. + +For this reason V doesn't allow the modification of arguments with primitive types (e.g. integers). +Only more complex types such as arrays and maps may be modified. + +Use `user.register()` or `user = register(user)` +instead of `register(mut user)`. + +#### Struct update syntax + +V makes it easy to return a modified version of an object: + +```v +struct User { + name string + age int + is_registered bool +} + +fn register(u User) User { + return { + ...u + is_registered: true + } +} + +mut user := User{ + name: 'abc' + age: 23 +} +user = register(user) +println(user) +``` + +### Variable number of arguments + +```v +fn sum(a ...int) int { + mut total := 0 + for x in a { + total += x + } + return total +} + +println(sum()) // 0 +println(sum(1)) // 1 +println(sum(2, 3)) // 5 +// using array decomposition +a := [2, 3, 4] +println(sum(...a)) // <-- using prefix ... here. output: 9 +b := [5, 6, 7] +println(sum(...b)) // output: 18 +``` + +### Anonymous & higher order functions + +```v +fn sqr(n int) int { + return n * n +} + +fn cube(n int) int { + return n * n * n +} + +fn run(value int, op fn (int) int) int { + return op(value) +} + +fn main() { + // Functions can be passed to other functions + println(run(5, sqr)) // "25" + // Anonymous functions can be declared inside other functions: + double_fn := fn (n int) int { + return n + n + } + println(run(5, double_fn)) // "10" + // Functions can be passed around without assigning them to variables: + res := run(5, fn (n int) int { + return n + n + }) + println(res) // "10" + // You can even have an array/map of functions: + fns := [sqr, cube] + println(fns[0](10)) // "100" + fns_map := map{ + 'sqr': sqr + 'cube': cube + } + println(fns_map['cube'](2)) // "8" +} +``` + +## References + +```v +struct Foo {} + +fn (foo Foo) bar_method() { + // ... +} + +fn bar_function(foo Foo) { + // ... +} +``` + +If a function argument is immutable (like `foo` in the examples above) +V can pass it either by value or by reference. The compiler will decide, +and the developer doesn't need to think about it. + +You no longer need to remember whether you should pass the struct by value +or by reference. + +You can ensure that the struct is always passed by reference by +adding `&`: + +```v +struct Foo { + abc int +} + +fn (foo &Foo) bar() { + println(foo.abc) +} +``` + +`foo` is still immutable and can't be changed. For that, +`(mut foo Foo)` must be used. + +In general, V's references are similar to Go pointers and C++ references. +For example, a generic tree structure definition would look like this: + +```v +struct Node { + val T + left &Node + right &Node +} +``` + +## Constants + +```v +const ( + pi = 3.14 + world = '世界' +) + +println(pi) +println(world) +``` + +Constants are declared with `const`. They can only be defined +at the module level (outside of functions). +Constant values can never be changed. You can also declare a single +constant separately: + +```v +const e = 2.71828 +``` + +V constants are more flexible than in most languages. You can assign more complex values: + +```v +struct Color { + r int + g int + b int +} + +fn rgb(r int, g int, b int) Color { + return Color{ + r: r + g: g + b: b + } +} + +const ( + numbers = [1, 2, 3] + red = Color{ + r: 255 + g: 0 + b: 0 + } + // evaluate function call at compile-time* + blue = rgb(0, 0, 255) +) + +println(numbers) +println(red) +println(blue) +``` +\* WIP - for now function calls are evaluated at program start-up + +Global variables are not normally allowed, so this can be really useful. + +**Modules** + +Constants can be made public with `pub const`: +```v oksyntax +module mymodule + +pub const golden_ratio = 1.61803 + +fn calc() { + println(mymodule.golden_ratio) +} +``` +The `pub` keyword is only allowed before the `const` keyword and cannot be used inside +a `const ( )` block. + +Outside from module main all constants need to be prefixed with the module name. + +### Required module prefix + +When naming constants, `snake_case` must be used. In order to distinguish consts +from local variables, the full path to consts must be specified. For example, +to access the PI const, full `math.pi` name must be used both outside the `math` +module, and inside it. That restriction is relaxed only for the `main` module +(the one containing your `fn main()`), where you can use the unqualified name of +constants defined there, i.e. `numbers`, rather than `main.numbers`. + +vfmt takes care of this rule, so you can type `println(pi)` inside the `math` module, +and vfmt will automatically update it to `println(math.pi)`. + + + +## Builtin functions + +Some functions are builtin like `println`. Here is the complete list: + +```v ignore +fn print(s string) // print anything on sdtout +fn println(s string) // print anything and a newline on sdtout + +fn eprint(s string) // same as print(), but use stderr +fn eprintln(s string) // same as println(), but use stderr + +fn exit(code int) // terminate the program with a custom error code +fn panic(s string) // print a message and backtraces on stderr, and terminate the program with error code 1 +fn print_backtrace() // print backtraces on stderr +``` + +`println` is a simple yet powerful builtin function, that can print anything: +strings, numbers, arrays, maps, structs. + +```v +struct User { + name string + age int +} + +println(1) // "1" +println('hi') // "hi" +println([1, 2, 3]) // "[1, 2, 3]" +println(User{ name: 'Bob', age: 20 }) // "User{name:'Bob', age:20}" +``` + + + +## Printing custom types + +If you want to define a custom print value for your type, simply define a +`.str() string` method: + +```v +struct Color { + r int + g int + b int +} + +pub fn (c Color) str() string { + return '{$c.r, $c.g, $c.b}' +} + +red := Color{ + r: 255 + g: 0 + b: 0 +} +println(red) +``` + +## Modules + +Every file in the root of a folder is part of the same module. +Simple programs don't need to specify module name, in which case it defaults to 'main'. + +V is a very modular language. Creating reusable modules is encouraged and is +quite easy to do. +To create a new module, create a directory with your module's name containing +.v files with code: + +```shell +cd ~/code/modules +mkdir mymodule +vim mymodule/myfile.v +``` +```v failcompile +// myfile.v +module mymodule + +// To export a function we have to use `pub` +pub fn say_hi() { + println('hello from mymodule!') +} +``` + +You can now use `mymodule` in your code: + +```v failcompile +import mymodule + +fn main() { + mymodule.say_hi() +} +``` + +* Module names should be short, under 10 characters. +* Module names must use `snake_case`. +* Circular imports are not allowed. +* You can have as many .v files in a module as you want. +* You can create modules anywhere. +* All modules are compiled statically into a single executable. + +### `init` functions + +If you want a module to automatically call some setup/initialization code when it is imported, +you can use a module `init` function: + +```v +fn init() { + // your setup code here ... +} +``` + +The `init` function cannot be public - it will be called automatically. This feature is +particularly useful for initializing a C library. + +### Manage Packages + +Briefly: + +```powershell +v [module option] [param] +``` + +###### module options: + +``` + install Install a module from VPM. + remove Remove a module that was installed from VPM. + search Search for a module from VPM. + update Update an installed module from VPM. + upgrade Upgrade all the outdated modules. + list List all installed modules. + outdated Show installed modules that need updates. +``` + +Read more: + +You can also install modules already created by someone else with [VPM](https://vpm.vlang.io/): +```powershell +v install [module] +``` +**Example:** +```powershell +v install ui +``` + +Removing a module with v: + +```powershell +v remove [module] +``` +**Example:** +```powershell +v remove ui +``` + +Updating an installed module from [VPM](https://vpm.vlang.io/): + +```powershell +v update [module] +``` +**Example:** +```powershell +v update ui +``` + +Or you can update all your modules: +```powershell +v update +``` + +To see all the modules you have installed, you can use: + +```powershell +v list +``` +**Example:** +```powershell +> v list +Installed modules: + markdown + ui +``` + +To see all the modules you have installed, you can use: +outdated Show installed modules that need updates. +```powershell +v outdated +``` +**Example:** +```powershell +> v outdated +Modules are up to date. +``` + +### Publish package + +1. Put a `v.mod` file inside the toplevel folder of your module (if you + created your module with the command `v new mymodule` or `v init` you already have a v.mod file). + + ```sh + v new mymodule + Input your project description: My nice module. + Input your project version: (0.0.0) 0.0.1 + Input your project license: (MIT) + Initialising ... + Complete! + ``` + + Example `v.mod`: + ```v ignore + Module { + name: 'mymodule' + description: 'My nice module.' + version: '0.0.1' + license: 'MIT' + dependencies: [] + } + ``` + + Minimal file structure: + ``` + v.mod + mymodule.v + ``` + + Check that your module name is used in `mymodule.v`: + ```v + module mymodule + + pub fn hello_world() { + println('Hello World!') + } + ``` + +2. Create a git repository in the folder with the `v.mod` file + (this is not required if you used `v new` or `v init`): + ```sh + git init + git add . + git commit -m "INIT" + ```` + +3. Create a public repository on github.com. +4. Connect your local repository to the remote repository and push the changes. +5. Add your module to the public V module registry VPM: + https://vpm.vlang.io/new + + You will have to login with your Github account to register the module. + **Warning:** _Currently it is not possibility to edit your entry after submiting. + Check your module name and github url twice as this cannot be changed by you later._ +6. The final module name is a combination of your github account and + the module name you provided e.g. `mygithubname.mymodule`. + +**Optional:** tag your V module with `vlang` and `vlang-module` on github.com +to allow a better search experiance. + +## Type Declarations + +### Interfaces + +```v +struct Dog { + breed string +} + +struct Cat { + breed string +} + +fn (d Dog) speak() string { + return 'woof' +} + +fn (c Cat) speak() string { + return 'meow' +} + +// unlike Go and like TypeScript, V's interfaces can define fields, not just methods. +interface Speaker { + breed string + speak() string +} + +dog := Dog{'Leonberger'} +cat := Cat{'Siamese'} + +mut arr := []Speaker{} +arr << dog +arr << cat +for item in arr { + println('a $item.breed says: $item.speak()') +} +``` + +A type implements an interface by implementing its methods and fields. +There is no explicit declaration of intent, no "implements" keyword. + +#### Casting an interface + +We can test the underlying type of an interface using dynamic cast operators: +```v oksyntax +interface Something {} + +fn announce(s Something) { + if s is Dog { + println('a $s.breed dog') // `s` is automatically cast to `Dog` (smart cast) + } else if s is Cat { + println('a $s.breed cat') + } else { + println('something else') + } +} +``` +For more information, see [Dynamic casts](#dynamic-casts). + +#### Interface method definitions + +Also unlike Go, an interface may implement a method. +These methods are not implemented by structs which implement that interface. + +When a struct is wrapped in an interface that has implemented a method +with the same name as one implemented by this struct, only the method +implemented on the interface is called. + +```v +struct Cat {} + +fn (c Cat) speak() string { + return 'meow!' +} + +interface Adoptable {} + +fn (a Adoptable) speak() string { + return 'adopt me!' +} + +fn new_adoptable() Adoptable { + return Cat{} +} + +fn main() { + cat := Cat{} + assert cat.speak() == 'meow!' + a := new_adoptable() + assert a.speak() == 'adopt me!' + if a is Cat { + println(a.speak()) // meow! + } +} +``` + +### Function Types + +You can use type aliases for naming specific function signatures - for +example: + +```v +type Filter = fn (string) string +``` + +This works like any other type - for example, a function can accept an +argument of a function type: + +```v +type Filter = fn (string) string + +fn filter(s string, f Filter) string { + return f(s) +} +``` + +V has duck-typing, so functions don't need to declare compatibility with +a function type - they just have to be compatible: + +```v +fn uppercase(s string) string { + return s.to_upper() +} + +// now `uppercase` can be used everywhere where Filter is expected +``` + +Compatible functions can also be explicitly cast to a function type: + +```v oksyntax +my_filter := Filter(uppercase) +``` + +The cast here is purely informational - again, duck-typing means that the +resulting type is the same without an explicit cast: + +```v oksyntax +my_filter := uppercase +``` + +You can pass the assigned function as an argument: + +```v oksyntax +println(filter('Hello world', my_filter)) // prints `HELLO WORLD` +``` + +And you could of course have passed it directly as well, without using a +local variable: + +```v oksyntax +println(filter('Hello world', uppercase)) +``` + +And this works with anonymous functions as well: + +```v oksyntax +println(filter('Hello world', fn (s string) string { + return s.to_upper() +})) +``` + +You can see the complete +[example here](https://github.com/vlang/v/tree/master/examples/function_types.v). + +### Enums + +```v +enum Color { + red + green + blue +} + +mut color := Color.red +// V knows that `color` is a `Color`. No need to use `color = Color.green` here. +color = .green +println(color) // "green" +match color { + .red { println('the color was red') } + .green { println('the color was green') } + .blue { println('the color was blue') } +} +``` + +Enum match must be exhaustive or have an `else` branch. +This ensures that if a new enum field is added, it's handled everywhere in the code. + +Enum fields cannot re-use reserved keywords. However, reserved keywords may be escaped +with an @. + +```v +enum Color { + @none + red + green + blue +} + +color := Color.@none +println(color) +``` + +Integers may be assigned to enum fields. + +```v +enum Grocery { + apple + orange = 5 + pear +} + +g1 := int(Grocery.apple) +g2 := int(Grocery.orange) +g3 := int(Grocery.pear) +println('Grocery IDs: $g1, $g2, $g3') +``` + +Output: `Grocery IDs: 0, 5, 6`. + +Operations are not allowed on enum variables; they must be explicity cast to `int`. + +### Sum types + +A sum type instance can hold a value of several different types. Use the `type` +keyword to declare a sum type: + +```v +struct Moon {} + +struct Mars {} + +struct Venus {} + +type World = Mars | Moon | Venus + +sum := World(Moon{}) +assert sum.type_name() == 'Moon' +println(sum) +``` +The built-in method `type_name` returns the name of the currently held +type. + +With sum types you could build recursive structures and write concise but powerful code on them. +```v +// V's binary tree +struct Empty {} + +struct Node { + value f64 + left Tree + right Tree +} + +type Tree = Empty | Node + +// sum up all node values +fn sum(tree Tree) f64 { + return match tree { + Empty { 0 } + Node { tree.value + sum(tree.left) + sum(tree.right) } + } +} + +fn main() { + left := Node{0.2, Empty{}, Empty{}} + right := Node{0.3, Empty{}, Node{0.4, Empty{}, Empty{}}} + tree := Node{0.5, left, right} + println(sum(tree)) // 0.2 + 0.3 + 0.4 + 0.5 = 1.4 +} +``` + +Enums can have methods, just like structs + +```v +enum Cycle { + one + two + three +} + +fn (c Cycle) next() Cycle { + match c { + .one { + return .two + } + .two { + return .three + } + .three { + return .one + } + } +} + +mut c := Cycle.one +for _ in 0 .. 10 { + println(c) + c = c.next() +} +``` + +Output: +``` +one +two +three +one +two +three +one +two +three +one +``` + +#### Dynamic casts + +To check whether a sum type instance holds a certain type, use `sum is Type`. +To cast a sum type to one of its variants you can use `sum as Type`: + +```v +struct Moon {} + +struct Mars {} + +struct Venus {} + +type World = Mars | Moon | Venus + +fn (m Mars) dust_storm() bool { + return true +} + +fn main() { + mut w := World(Moon{}) + assert w is Moon + w = Mars{} + // use `as` to access the Mars instance + mars := w as Mars + if mars.dust_storm() { + println('bad weather!') + } +} +``` + +`as` will panic if `w` doesn't hold a `Mars` instance. +A safer way is to use a smart cast. + +#### Smart casting + +```v oksyntax +if w is Mars { + assert typeof(w).name == 'Mars' + if w.dust_storm() { + println('bad weather!') + } +} +``` +`w` has type `Mars` inside the body of the `if` statement. This is +known as *flow-sensitive typing*. +If `w` is a mutable identifier, it would be unsafe if the compiler smart casts it without a warning. +That's why you have to declare a `mut` before the `is` expression: + +```v ignore +if mut w is Mars { + assert typeof(w).name == 'Mars' + if w.dust_storm() { + println('bad weather!') + } +} +``` +Otherwise `w` would keep its original type. +> This works for both, simple variables and complex expressions like `user.name` + +#### Matching sum types + +You can also use `match` to determine the variant: + +```v +struct Moon {} + +struct Mars {} + +struct Venus {} + +type World = Mars | Moon | Venus + +fn open_parachutes(n int) { + println(n) +} + +fn land(w World) { + match w { + Moon {} // no atmosphere + Mars { + // light atmosphere + open_parachutes(3) + } + Venus { + // heavy atmosphere + open_parachutes(1) + } + } +} +``` + +`match` must have a pattern for each variant or have an `else` branch. + +```v ignore +struct Moon {} +struct Mars {} +struct Venus {} + +type World = Moon | Mars | Venus + +fn (m Moon) moon_walk() {} +fn (m Mars) shiver() {} +fn (v Venus) sweat() {} + +fn pass_time(w World) { + match w { + // using the shadowed match variable, in this case `w` (smart cast) + Moon { w.moon_walk() } + Mars { w.shiver() } + else {} + } +} +``` + +### Type aliases + +To define a new type `NewType` as an alias for `ExistingType`, +do `type NewType = ExistingType`.
    +This is a special case of a [sum type](#sum-types) declaration. + +### Option/Result types and error handling + +Option types are declared with `?Type`: +```v +struct User { + id int + name string +} + +struct Repo { + users []User +} + +fn (r Repo) find_user_by_id(id int) ?User { + for user in r.users { + if user.id == id { + // V automatically wraps this into an option type + return user + } + } + return error('User $id not found') +} + +fn main() { + repo := Repo{ + users: [User{1, 'Andrew'}, User{2, 'Bob'}, User{10, 'Charles'}] + } + user := repo.find_user_by_id(10) or { // Option types must be handled by `or` blocks + return + } + println(user.id) // "10" + println(user.name) // "Charles" +} +``` + +V combines `Option` and `Result` into one type, so you don't need to decide which one to use. + +The amount of work required to "upgrade" a function to an optional function is minimal; +you have to add a `?` to the return type and return an error when something goes wrong. + +If you don't need to return an error message, you can simply `return none` +(this is a more efficient equivalent of `return error("")`). + +This is the primary mechanism for error handling in V. They are still values, like in Go, +but the advantage is that errors can't be unhandled, and handling them is a lot less verbose. +Unlike other languages, V does not handle exceptions with `throw/try/catch` blocks. + +`err` is defined inside an `or` block and is set to the string message passed +to the `error()` function. `err` is empty if `none` was returned. + +```v oksyntax +user := repo.find_user_by_id(7) or { + println(err) // "User 7 not found" + return +} +``` + +### Handling optionals + +There are four ways of handling an optional. The first method is to +propagate the error: + +```v +import net.http + +fn f(url string) ?string { + resp := http.get(url) ? + return resp.text +} +``` + +`http.get` returns `?http.Response`. Because `?` follows the call, the +error will be propagated to the caller of `f`. When using `?` after a +function call producing an optional, the enclosing function must return +an optional as well. If error propagation is used in the `main()` +function it will `panic` instead, since the error cannot be propagated +any further. + +The body of `f` is essentially a condensed version of: + +```v ignore + resp := http.get(url) or { return err } + return resp.text +``` + +--- +The second method is to break from execution early: + +```v oksyntax +user := repo.find_user_by_id(7) or { return } +``` + +Here, you can either call `panic()` or `exit()`, which will stop the execution of the +entire program, or use a control flow statement (`return`, `break`, `continue`, etc) +to break from the current block. +Note that `break` and `continue` can only be used inside a `for` loop. + +V does not have a way to forcibly "unwrap" an optional (as other languages do, +for instance Rust's `unwrap()` or Swift's `!`). To do this, use `or { panic(err.msg) }` instead. + +--- +The third method is to provide a default value at the end of the `or` block. +In case of an error, that value would be assigned instead, +so it must have the same type as the content of the `Option` being handled. + +```v +fn do_something(s string) ?string { + if s == 'foo' { + return 'foo' + } + return error('invalid string') // Could be `return none` as well +} + +a := do_something('foo') or { 'default' } // a will be 'foo' +b := do_something('bar') or { 'default' } // b will be 'default' +println(a) +println(b) +``` + +--- +The fourth method is to use `if` unwrapping: + +```v +import net.http + +if resp := http.get('https://google.com') { + println(resp.text) // resp is a http.Response, not an optional +} else { + println(err) +} +``` +Above, `http.get` returns a `?http.Response`. `resp` is only in scope for the first +`if` branch. `err` is only in scope for the `else` branch. + +## Generics + +```v wip + +struct Repo { + db DB +} + +struct User { + id int + name string +} + +struct Post { + id int + user_id int + title string + body string +} + +fn new_repo(db DB) Repo { + return Repo{db: db} +} + +// This is a generic function. V will generate it for every type it's used with. +fn (r Repo) find_by_id(id int) ?T { + table_name := T.name // in this example getting the name of the type gives us the table name + return r.db.query_one('select * from $table_name where id = ?', id) +} + +db := new_db() +users_repo := new_repo(db) // returns Repo +posts_repo := new_repo(db) // returns Repo +user := users_repo.find_by_id(1)? // find_by_id +post := posts_repo.find_by_id(1)? // find_by_id +``` + +Currently generic function definitions must declare their type parameters, but in +future V will infer generic type parameters from single-letter type names in +runtime parameter types. This is why `find_by_id` can omit ``, because the +receiver argument `r` uses a generic type `T`. + +Another example: +```v +fn compare(a T, b T) int { + if a < b { + return -1 + } + if a > b { + return 1 + } + return 0 +} + +// compare +println(compare(1, 0)) // Outputs: 1 +println(compare(1, 1)) // 0 +println(compare(1, 2)) // -1 +// compare +println(compare('1', '0')) // Outputs: 1 +println(compare('1', '1')) // 0 +println(compare('1', '2')) // -1 +// compare +println(compare(1.1, 1.0)) // Outputs: 1 +println(compare(1.1, 1.1)) // 0 +println(compare(1.1, 1.2)) // -1 +``` + + +## Concurrency +### Spawning Concurrent Tasks +V's model of concurrency is very similar to Go's. To run `foo()` concurrently in +a different thread, just call it with `go foo()`: + +```v +import math + +fn p(a f64, b f64) { // ordinary function without return value + c := math.sqrt(a * a + b * b) + println(c) +} + +fn main() { + go p(3, 4) + // p will be run in parallel thread +} +``` + +Sometimes it is necessary to wait until a parallel thread has finished. This can +be done by assigning a *handle* to the started thread and calling the `wait()` method +to this handle later: + +```v +import math + +fn p(a f64, b f64) { // ordinary function without return value + c := math.sqrt(a * a + b * b) + println(c) // prints `5` +} + +fn main() { + h := go p(3, 4) + // p() runs in parallel thread + h.wait() + // p() has definitely finished +} +``` + +This approach can also be used to get a return value from a function that is run in a +parallel thread. There is no need to modify the function itself to be able to call it +concurrently. + +```v +import math { sqrt } + +fn get_hypot(a f64, b f64) f64 { // ordinary function returning a value + c := sqrt(a * a + b * b) + return c +} + +fn main() { + g := go get_hypot(54.06, 2.08) // spawn thread and get handle to it + h1 := get_hypot(2.32, 16.74) // do some other calculation here + h2 := g.wait() // get result from spawned thread + println('Results: $h1, $h2') // prints `Results: 16.9, 54.1` +} +``` + +If there is a large number of tasks, it might be easier to manage them +using an array of threads. + +```v +import time + +fn task(id int, duration int) { + println('task $id begin') + time.sleep(duration * time.millisecond) + println('task $id end') +} + +fn main() { + mut threads := []thread{} + threads << go task(1, 500) + threads << go task(2, 900) + threads << go task(3, 100) + threads.wait() + println('done') +} + +// Output: +// task 1 begin +// task 2 begin +// task 3 begin +// task 3 end +// task 1 end +// task 2 end +// done +``` + +Additionally for threads that return the same type, calling `wait()` +on the thread array will return all computed values. + +```v +fn expensive_computing(i int) int { + return i * i +} + +fn main() { + mut threads := []thread int{} + for i in 1 .. 10 { + threads << go expensive_computing(i) + } + // Join all tasks + r := threads.wait() + println('All jobs finished: $r') +} + +// Output: All jobs finished: [1, 4, 9, 16, 25, 36, 49, 64, 81] +``` + +### Channels +Channels are the preferred way to communicate between coroutines. V's channels work basically like +those in Go. You can push objects into a channel on one end and pop objects from the other end. +Channels can be buffered or unbuffered and it is possible to `select` from multiple channels. + +#### Syntax and Usage +Channels have the type `chan objtype`. An optional buffer length can specified as the `cap` property +in the declaration: + +```v +ch := chan int{} // unbuffered - "synchronous" +ch2 := chan f64{cap: 100} // buffer length 100 +``` + +Channels do not have to be declared as `mut`. The buffer length is not part of the type but +a property of the individual channel object. Channels can be passed to coroutines like normal +variables: + +```v +fn f(ch chan int) { + // ... +} + +fn main() { + ch := chan int{} + go f(ch) + // ... +} +``` + +Objects can be pushed to channels using the arrow operator. The same operator can be used to +pop objects from the other end: + +```v +// make buffered channels so pushing does not block (if there is room in the buffer) +ch := chan int{cap: 1} +ch2 := chan f64{cap: 1} +n := 5 +// push +ch <- n +ch2 <- 7.3 +mut y := f64(0.0) +m := <-ch // pop creating new variable +y = <-ch2 // pop into existing variable +``` + +A channel can be closed to indicate that no further objects can be pushed. Any attempt +to do so will then result in a runtime panic (with the exception of `select` and +`try_push()` - see below). Attempts to pop will return immediately if the +associated channel has been closed and the buffer is empty. This situation can be +handled using an or branch (see [Handling Optionals](#handling-optionals)). + +```v wip +ch := chan int{} +ch2 := chan f64{} +// ... +ch.close() +// ... +m := <-ch or { + println('channel has been closed') +} + +// propagate error +y := <-ch2 ? +``` + +#### Channel Select + +The `select` command allows monitoring several channels at the same time +without noticeable CPU load. It consists of a list of possible transfers and associated branches +of statements - similar to the [match](#match) command: +```v +import time + +fn main() { + ch := chan f64{} + ch2 := chan f64{} + ch3 := chan f64{} + mut b := 0.0 + c := 1.0 + // ... setup go threads that will send on ch/ch2 + go fn (the_channel chan f64) { + time.sleep(5 * time.millisecond) + the_channel <- 1.0 + }(ch) + go fn (the_channel chan f64) { + time.sleep(1 * time.millisecond) + the_channel <- 1.0 + }(ch2) + go fn (the_channel chan f64) { + _ := <-the_channel + }(ch3) + // + select { + a := <-ch { + // do something with `a` + eprintln('> a: $a') + } + b = <-ch2 { + // do something with predeclared variable `b` + eprintln('> b: $b') + } + ch3 <- c { + // do something if `c` was sent + time.sleep(5 * time.millisecond) + eprintln('> c: $c was send on channel ch3') + } + 500 * time.millisecond { + // do something if no channel has become ready within 0.5s + eprintln('> more than 0.5s passed without a channel being ready') + } + } + eprintln('> done') +} +``` + +The timeout branch is optional. If it is absent `select` waits for an unlimited amount of time. +It is also possible to proceed immediately if no channel is ready in the moment `select` is called +by adding an `else { ... }` branch. `else` and `> timeout` are mutually exclusive. + +The `select` command can be used as an *expression* of type `bool` +that becomes `false` if all channels are closed: +```v wip +if select { + ch <- a { + // ... + } +} { + // channel was open +} else { + // channel is closed +} +``` + +#### Special Channel Features + +For special purposes there are some builtin properties and methods: +```v +struct Abc { + x int +} + +a := 2.13 +ch := chan f64{} +res := ch.try_push(a) // try to perform `ch <- a` +println(res) +l := ch.len // number of elements in queue +c := ch.cap // maximum queue length +is_closed := ch.closed // bool flag - has `ch` been closed +println(l) +println(c) +mut b := Abc{} +ch2 := chan Abc{} +res2 := ch2.try_pop(mut b) // try to perform `b = <-ch2` +``` + +The `try_push/pop()` methods will return immediately with one of the results +`.success`, `.not_ready` or `.closed` - dependent on whether the object has been transferred or +the reason why not. +Usage of these methods and properties in production is not recommended - +algorithms based on them are often subject to race conditions. Especially `.len` and +`.closed` should not be used to make decisions. +Use `or` branches, error propagation or `select` instead (see [Syntax and Usage](#syntax-and-usage) +and [Channel Select](#channel-select) above). + +### Shared Objects + +Data can be exchanged between a coroutine and the calling thread via a shared variable. +Such variables should be created as `shared` and passed to the coroutine as such, too. +The underlying `struct` contains a hidden *mutex* that allows locking concurrent access +using `rlock` for read-only and `lock` for read/write access. + +```v +struct St { +mut: + x int // data to shared +} + +fn (shared b St) g() { + lock b { + // read/modify/write b.x + } +} + +fn main() { + shared a := St{ + x: 10 + } + go a.g() + // ... + rlock a { + // read a.x + } +} +``` +Shared variables must be structs, arrays or maps. + +## Decoding JSON + +```v +import json + +struct Foo { + x int +} + +struct User { + // Adding a [required] attribute will make decoding fail, if that + // field is not present in the input. + // If a field is not [required], but is missing, it will be assumed + // to have its default value, like 0 for numbers, or '' for strings, + // and decoding will not fail. + name string [required] + age int + // Use the `skip` attribute to skip certain fields + foo Foo [skip] + // If the field name is different in JSON, it can be specified + last_name string [json: lastName] +} + +data := '{ "name": "Frodo", "lastName": "Baggins", "age": 25 }' +user := json.decode(User, data) or { + eprintln('Failed to decode json, error: $err') + return +} +println(user.name) +println(user.last_name) +println(user.age) +// You can also decode JSON arrays: +sfoos := '[{"x":123},{"x":456}]' +foos := json.decode([]Foo, sfoos) ? +println(foos[0].x) +println(foos[1].x) +``` + +Because of the ubiquitous nature of JSON, support for it is built directly into V. + +The `json.decode` function takes two arguments: +the first is the type into which the JSON value should be decoded and +the second is a string containing the JSON data. + +V generates code for JSON encoding and decoding. +No runtime reflection is used. This results in much better performance. + +## Testing + +### Asserts + +```v +fn foo(mut v []int) { + v[0] = 1 +} + +mut v := [20] +foo(mut v) +assert v[0] < 4 +``` +An `assert` statement checks that its expression evaluates to `true`. If an assert fails, +the program will abort. Asserts should only be used to detect programming errors. When an +assert fails it is reported to *stderr*, and the values on each side of a comparison operator +(such as `<`, `==`) will be printed when possible. This is useful to easily find an +unexpected value. Assert statements can be used in any function. + +### Test files + +```v +// hello.v +module main + +fn hello() string { + return 'Hello world' +} + +fn main() { + println(hello()) +} +``` + +```v failcompile +module main + +// hello_test.v +fn test_hello() { + assert hello() == 'Hello world' +} +``` +To run the test above, use `v hello_test.v`. This will check that the function `hello` is +producing the correct output. V executes all test functions in the file. + +* All test functions have to be inside a test file whose name ends in `_test.v`. +* Test function names must begin with `test_` to mark them for execution. +* Normal functions can also be defined in test files, and should be called manually. Other + symbols can also be defined in test files e.g. types. +* There are two kinds of tests: external and internal. +* Internal tests must *declare* their module, just like all other .v +files from the same module. Internal tests can even call private functions in +the same module. +* External tests must *import* the modules which they test. They do not +have access to the private functions/types of the modules. They can test only +the external/public API that a module provides. + +In the example above, `test_hello` is an internal test, that can call +the private function `hello()` because `hello_test.v` has `module main`, +just like `hello.v`, i.e. both are part of the same module. Note also that +since `module main` is a regular module like the others, internal tests can +be used to test private functions in your main program .v files too. + +You can also define special test functions in a test file: +* `testsuite_begin` which will be run *before* all other test functions. +* `testsuite_end` which will be run *after* all other test functions. + +If a test function has an error return type, any propagated errors will fail the test: + +```v +import strconv + +fn test_atoi() ? { + assert strconv.atoi('1') ? == 1 + assert strconv.atoi('one') ? == 1 // test will fail +} +``` + +#### Running tests + +To run test functions in an individual test file, use `v foo_test.v`. + +To test an entire module, use `v test mymodule`. You can also use `v test .` to test +everything inside your current folder (and subfolders). You can pass the `-stats` +option to see more details about the individual tests run. + +You can put additional test data, including .v source files in a folder, named +`testdata`, right next to your _test.v files. V's test framework will *ignore* +such folders, while scanning for tests to run. This is usefull, if you want to +put .v files with invalid V source code, or other tests, including known +failing ones, that should be run in a specific way/options by a parent _test.v +file. + +NB: the path to the V compiler, is available through @VEXE, so a _test.v +file, can easily run *other* test files like this: +```v oksyntax +import os + +fn test_subtest() { + res := os.execute('${@VEXE} other_test.v') + assert res.exit_code == 1 + assert res.output.contains('other_test.v does not exist') +} +``` + +## Memory management + +V avoids doing unnecessary allocations in the first place by using value types, +string buffers, promoting a simple abstraction-free code style. + +Most objects (~90-100%) are freed by V's autofree engine: the compiler inserts +necessary free calls automatically during compilation. Remaining small percentage +of objects is freed via reference counting. + +The developer doesn't need to change anything in their code. "It just works", like in +Python, Go, or Java, except there's no heavy GC tracing everything or expensive RC for +each object. + +### Control + +You can take advantage of V's autofree engine and define a `free()` method on custom +data types: + +```v +struct MyType {} + +[unsafe] +fn (data &MyType) free() { + // ... +} +``` + +Just as the compiler frees C data types with C's `free()`, it will statically insert +`free()` calls for your data type at the end of each variable's lifetime. + +For developers willing to have more low level control, autofree can be disabled with +`-manualfree`, or by adding a `[manualfree]` on each function that wants manage its +memory manually. (See [attributes](#attributes)). + +_Note: right now autofree is hidden behind the -autofree flag. It will be enabled by +default in V 0.3. If autofree is not used, V programs will leak memory._ + +### Examples + +```v +import strings + +fn draw_text(s string, x int, y int) { + // ... +} + +fn draw_scene() { + // ... + name1 := 'abc' + name2 := 'def ghi' + draw_text('hello $name1', 10, 10) + draw_text('hello $name2', 100, 10) + draw_text(strings.repeat(`X`, 10000), 10, 50) + // ... +} +``` + +The strings don't escape `draw_text`, so they are cleaned up when +the function exits. + +In fact, with the `-prealloc` flag, the first two calls won't result in any allocations at all. +These two strings are small, so V will use a preallocated buffer for them. + +```v +struct User { + name string +} + +fn test() []int { + number := 7 // stack variable + user := User{} // struct allocated on stack + numbers := [1, 2, 3] // array allocated on heap, will be freed as the function exits + println(number) + println(user) + println(numbers) + numbers2 := [4, 5, 6] // array that's being returned, won't be freed here + return numbers2 +} +``` + +### Stack and Heap +#### Stack and Heap Basics + +Like with most other programming languages there are two locations where data can +be stored: + +* The *stack* allows fast allocations with almost zero administrative overhead. The + stack grows and shrinks with the function call depth – so every called + function has its stack segment that remains valid until the function returns. + No freeing is necessary, however, this also means that a reference to a stack + object becomes invalid on function return. Furthermore stack space is + limited (typically to a few Megabytes per thread). +* The *heap* is a large memory area (typically some Gigabytes) that is administrated + by the operating system. Heap objects are allocated and freed by special function + calls that delegate the administrative tasks to the OS. This means that they can + remain valid across several function calls, however, the administration is + expensive. + +#### V's default approach + +Due to performance considerations V tries to put objects on the stack if possible +but allocates them on the heap when obviously necessary. Example: + +```v +struct MyStruct { + n int +} + +struct RefStruct { + r &MyStruct +} + +fn main() { + q, w := f() + println('q: $q.r.n, w: $w.n') +} + +fn f() (RefStruct, &MyStruct) { + a := MyStruct{ + n: 1 + } + b := MyStruct{ + n: 2 + } + c := MyStruct{ + n: 3 + } + e := RefStruct{ + r: &b + } + x := a.n + c.n + println('x: $x') + return e, &c +} +``` + +Here `a` is stored on the stack since it's address never leaves the function `f()`. +However a reference to `b` is part of `e` which is returned. Also a reference to +`c` is returned. For this reason `b` and `c` will be heap allocated. + +Things become less obvious when a reference to an object is passed as function argument: + +```v +struct MyStruct { +mut: + n int +} + +fn main() { + mut q := MyStruct{ + n: 7 + } + w := MyStruct{ + n: 13 + } + x := q.f(&w) // references of `q` and `w` are passed + println('q: $q\nx: $x') +} + +fn (mut a MyStruct) f(b &MyStruct) int { + a.n += b.n + x := a.n * b.n + return x +} +``` +Here the call `q.f(&w)` passes references to `q` and `w` because `a` is +`mut` and `b` is of type `&MyStruct` in `f()`'s declaration, so technically +these references are leaving `main()`. However the *lifetime* of these +references lies inside the scope of `main()` so `q` and `w` are allocated +on the stack. + +#### Manual Control for Stack and Heap + +In the last example the V compiler could put `q` and `w` on the stack +because it assumed that in the call `q.f(&w)` these references were only +used for reading and modifying the referred values – and not to pass the +references themselves somewhere else. This can be seen in a way that the +references to `q` and `w` are only *borrowed* to `f()`. + +Things become different if `f()` is doing something with a reference itself: + +```v +struct RefStruct { +mut: + r &MyStruct +} + +// see discussion below +[heap] +struct MyStruct { + n int +} + +fn main() { + m := MyStruct{} + mut r := RefStruct{ + r: &m + } + r.g() + println('r: $r') +} + +fn (mut r RefStruct) g() { + s := MyStruct{ + n: 7 + } + r.f(&s) // reference to `s` inside `r` is passed back to `main() ` +} + +fn (mut r RefStruct) f(s &MyStruct) { + r.r = s // would trigger error without `[heap]` +} +``` + +Here `f()` looks quite innocent but is doing nasty things – it inserts a +reference to `s` into `r`. The problem with this is that `s` lives only as long +as `g()` is running but `r` is used in `main()` after that. For this reason +the compiler would complain about the assignment in `f()` because `s` *"might +refer to an object stored on stack"*. The assumption made in `g()` that the call +`r.f(&s)` would only borrow the reference to `s` is wrong. + +A solution to this dilemma is the `[heap]` attribute at the declaration of +`struct MyStruct`. It instructs the compiler to *always* allocate `MyStruct`-objects +on the heap. This way the reference to `s` remains valid even after `g()` returns. +The compiler takes into consideration that `MyStruct` objects are always heap +allocated when checking `f()` and allows assigning the reference to `s` to the +`r.r` field. + +There is a pattern often seen in other programming languages: + +```v failcompile +fn (mut a MyStruct) f() &MyStruct { + // do something with a + return &a // would return address of borrowed object +} +``` + +Here `f()` is passed a reference `a` as receiver that is passed back to the caller and returned +as result at the same time. The intention behind such a declaration is method chaining like +`y = x.f().g()`. However, the problem with this approach is that a second reference +to `a` is created – so it is not only borrowed and `MyStruct` has to be +declared as `[heap]`. + +In V the better approach is: + +```v +struct MyStruct { +mut: + n int +} + +fn (mut a MyStruct) f() { + // do something with `a` +} + +fn (mut a MyStruct) g() { + // do something else with `a` +} + +fn main() { + x := MyStruct{} // stack allocated + mut y := x + y.f() + y.g() + // instead of `mut y := x.f().g() +} +``` + +This way the `[heap]` attribute can be avoided – resulting in better performance. + +However, stack space is very limited as mentioned above. For this reason the `[heap]` +attribute might be suitable for very large structures even if not required by use cases +like those mentioned above. + +There is an alternative way to manually control allocation on a case to case basis. This +approach is not recommended but shown here for the sake of completeness: + +```v +struct MyStruct { + n int +} + +struct RefStruct { +mut: + r &MyStruct +} + +// simple function - just to overwrite stack segment previously used by `g()` +fn use_stack() { + x := 7.5 + y := 3.25 + z := x + y + println('$x $y $z') +} + +fn main() { + m := MyStruct{} + mut r := RefStruct{ + r: &m + } + r.g() + use_stack() // to erase invalid stack contents + println('r: $r') +} + +fn (mut r RefStruct) g() { + s := &MyStruct{ // `s` explicitly refers to a heap object + n: 7 + } + // change `&MyStruct` -> `MyStruct` above and `r.f(s)` -> `r.f(&s)` below + // to see data in stack segment being overwritten + r.f(s) +} + +fn (mut r RefStruct) f(s &MyStruct) { + r.r = unsafe { s } // override compiler check +} +``` + +Here the compiler check is suppressed by the `unsafe` block. To make `s` be heap +allocated even without `[heap]` attribute the `struct` literal is prefixed with +an ampersand: `&MyStruct{...}`. + +This last step would not be required by the compiler but without it the reference +inside `r` becomes invalid (the memory area pointed to will be overwritten by +`use_stack()`) and the program might crash (or at least produce an unpredictable +final output). That's why this approach is *unsafe* and should be avoided! + +## ORM + +(This is still in an alpha state) + +V has a built-in ORM (object-relational mapping) which supports SQLite, MySQL and Postgres, +but soon it will support MS SQL and Oracle. + +V's ORM provides a number of benefits: + +- One syntax for all SQL dialects. (Migrating between databases becomes much easier.) +- Queries are constructed using V's syntax. (There's no need to learn another syntax.) +- Safety. (All queries are automatically sanitised to prevent SQL injection.) +- Compile time checks. (This prevents typos which can only be caught during runtime.) +- Readability and simplicity. (You don't need to manually parse the results of a query and + then manually construct objects from the parsed results.) + +```v +import sqlite + +struct Customer { + // struct name has to be the same as the table name (for now) + id int [primary; sql: serial] // a field named `id` of integer type must be the first field + name string [nonull] + nr_orders int + country string [nonull] +} + +db := sqlite.connect('customers.db') ? + +// you can create tables +// CREATE TABLE IF NOT EXISTS `Customer` (`id` INTEGER PRIMARY KEY, `name` TEXT NOT NULL, `nr_orders` INTEGER, `country` TEXT NOT NULL) +sql db { + create table Customer +} + +// select count(*) from Customer +nr_customers := sql db { + select count from Customer +} +println('number of all customers: $nr_customers') +// V syntax can be used to build queries +uk_customers := sql db { + select from Customer where country == 'uk' && nr_orders > 0 +} +println(uk_customers.len) +for customer in uk_customers { + println('$customer.id - $customer.name') +} +// by adding `limit 1` we tell V that there will be only one object +customer := sql db { + select from Customer where id == 1 limit 1 +} +println('$customer.id - $customer.name') +// insert a new customer +new_customer := Customer{ + name: 'Bob' + nr_orders: 10 +} +sql db { + insert new_customer into Customer +} +``` + +For more examples and the docs, see
    vlib/orm. + +## Writing Documentation + +The way it works is very similar to Go. It's very simple: there's no need to +write documentation separately for your code, +vdoc will generate it from docstrings in the source code. + +Documentation for each function/type/const must be placed right before the declaration: + +```v +// clearall clears all bits in the array +fn clearall() { +} +``` + +The comment must start with the name of the definition. + +Sometimes one line isn't enough to explain what a function does, in that case comments should +span to the documented function using single line comments: + +```v +// copy_all recursively copies all elements of the array by their value, +// if `dupes` is false all duplicate values are eliminated in the process. +fn copy_all(dupes bool) { + // ... +} +``` + +By convention it is preferred that comments are written in *present tense*. + +An overview of the module must be placed in the first comment right after the module's name. + +To generate documentation use vdoc, for example `v doc net.http`. + +## Tools + +### v fmt + +You don't need to worry about formatting your code or setting style guidelines. +`v fmt` takes care of that: + +```shell +v fmt file.v +``` + +It's recommended to set up your editor, so that `v fmt -w` runs on every save. +A vfmt run is usually pretty cheap (takes <30ms). + +Always run `v fmt -w file.v` before pushing your code. + +### Profiling + +V has good support for profiling your programs: `v -profile profile.txt run file.v` +That will produce a profile.txt file, which you can then analyze. + +The generated profile.txt file will have lines with 4 columns: +a) how many times a function was called +b) how much time in total a function took (in ms) +c) how much time on average, a call to a function took (in ns) +d) the name of the v function + +You can sort on column 3 (average time per function) using: +`sort -n -k3 profile.txt|tail` + +You can also use stopwatches to measure just portions of your code explicitly: +```v +import time + +fn main() { + sw := time.new_stopwatch() + println('Hello world') + println('Greeting the world took: ${sw.elapsed().nanoseconds()}ns') +} +``` + +# Advanced Topics + +## Dumping expressions at runtime +You can dump/trace the value of any V expression using `dump(expr)`. +For example, save this code sample as `factorial.v`, then run it with +`v run factorial.v`: +```v +fn factorial(n u32) u32 { + if dump(n <= 1) { + return dump(1) + } + return dump(n * factorial(n - 1)) +} + +fn main() { + println(factorial(5)) +} +``` +You will get: +``` +[factorial.v:2] n <= 1: false +[factorial.v:2] n <= 1: false +[factorial.v:2] n <= 1: false +[factorial.v:2] n <= 1: false +[factorial.v:2] n <= 1: true +[factorial.v:3] 1: 1 +[factorial.v:5] n * factorial(n - 1): 2 +[factorial.v:5] n * factorial(n - 1): 6 +[factorial.v:5] n * factorial(n - 1): 24 +[factorial.v:5] n * factorial(n - 1): 120 +120 +``` +Note that `dump(expr)` will trace both the source location, +the expression itself, and the expression value. + +## Memory-unsafe code + +Sometimes for efficiency you may want to write low-level code that can potentially +corrupt memory or be vulnerable to security exploits. V supports writing such code, +but not by default. + +V requires that any potentially memory-unsafe operations are marked intentionally. +Marking them also indicates to anyone reading the code that there could be +memory-safety violations if there was a mistake. + +Examples of potentially memory-unsafe operations are: + +* Pointer arithmetic +* Pointer indexing +* Conversion to pointer from an incompatible type +* Calling certain C functions, e.g. `free`, `strlen` and `strncmp`. + +To mark potentially memory-unsafe operations, enclose them in an `unsafe` block: + +```v wip +// allocate 2 uninitialized bytes & return a reference to them +mut p := unsafe { malloc(2) } +p[0] = `h` // Error: pointer indexing is only allowed in `unsafe` blocks +unsafe { + p[0] = `h` // OK + p[1] = `i` +} +p++ // Error: pointer arithmetic is only allowed in `unsafe` blocks +unsafe { + p++ // OK +} +assert *p == `i` +``` + +Best practice is to avoid putting memory-safe expressions inside an `unsafe` block, +so that the reason for using `unsafe` is as clear as possible. Generally any code +you think is memory-safe should not be inside an `unsafe` block, so the compiler +can verify it. + +If you suspect your program does violate memory-safety, you have a head start on +finding the cause: look at the `unsafe` blocks (and how they interact with +surrounding code). + +* Note: This is work in progress. + +### Structs with reference fields + +Structs with references require explicitly setting the initial value to a +reference value unless the struct already defines its own initial value. + +Zero-value references, or nil pointers, will **NOT** be supported in the future, +for now data structures such as Linked Lists or Binary Trees that rely on reference +fields that can use the value `0`, understanding that it is unsafe, and that it can +cause a panic. + +```v +struct Node { + a &Node + b &Node = 0 // Auto-initialized to nil, use with caution! +} + +// Reference fields must be initialized unless an initial value is declared. +// Zero (0) is OK but use with caution, it's a nil pointer. +foo := Node{ + a: 0 +} +bar := Node{ + a: &foo +} +baz := Node{ + a: 0 + b: 0 +} +qux := Node{ + a: &foo + b: &bar +} +println(baz) +println(qux) +``` + +## sizeof and __offsetof + +* `sizeof(Type)` gives the size of a type in bytes. +* `__offsetof(Struct, field_name)` gives the offset in bytes of a struct field. + +```v +struct Foo { + a int + b int +} + +assert sizeof(Foo) == 8 +assert __offsetof(Foo, a) == 0 +assert __offsetof(Foo, b) == 4 +``` + +## Calling C from V + +### Example + +```v +#flag -lsqlite3 +#include "sqlite3.h" +// See also the example from https://www.sqlite.org/quickstart.html +struct C.sqlite3 { +} + +struct C.sqlite3_stmt { +} + +type FnSqlite3Callback = fn (voidptr, int, &&char, &&char) int + +fn C.sqlite3_open(&char, &&C.sqlite3) int + +fn C.sqlite3_close(&C.sqlite3) int + +fn C.sqlite3_column_int(stmt &C.sqlite3_stmt, n int) int + +// ... you can also just define the type of parameter and leave out the C. prefix +fn C.sqlite3_prepare_v2(&C.sqlite3, &char, int, &&C.sqlite3_stmt, &&char) int + +fn C.sqlite3_step(&C.sqlite3_stmt) + +fn C.sqlite3_finalize(&C.sqlite3_stmt) + +fn C.sqlite3_exec(db &C.sqlite3, sql &char, cb FnSqlite3Callback, cb_arg voidptr, emsg &&char) int + +fn C.sqlite3_free(voidptr) + +fn my_callback(arg voidptr, howmany int, cvalues &&char, cnames &&char) int { + unsafe { + for i in 0 .. howmany { + print('| ${cstring_to_vstring(cnames[i])}: ${cstring_to_vstring(cvalues[i]):20} ') + } + } + println('|') + return 0 +} + +fn main() { + db := &C.sqlite3(0) // this means `sqlite3* db = 0` + // passing a string literal to a C function call results in a C string, not a V string + C.sqlite3_open(c'users.db', &db) + // C.sqlite3_open(db_path.str, &db) + query := 'select count(*) from users' + stmt := &C.sqlite3_stmt(0) + // NB: you can also use the `.str` field of a V string, + // to get its C style zero terminated representation + C.sqlite3_prepare_v2(db, &char(query.str), -1, &stmt, 0) + C.sqlite3_step(stmt) + nr_users := C.sqlite3_column_int(stmt, 0) + C.sqlite3_finalize(stmt) + println('There are $nr_users users in the database.') + // + error_msg := &char(0) + query_all_users := 'select * from users' + rc := C.sqlite3_exec(db, &char(query_all_users.str), my_callback, voidptr(7), &error_msg) + if rc != C.SQLITE_OK { + eprintln(unsafe { cstring_to_vstring(error_msg) }) + C.sqlite3_free(error_msg) + } + C.sqlite3_close(db) +} +``` + +## Calling V from C + +Since V can compile to C, calling V code from C is very easy. + +By default all V functions have the following naming scheme in C: `[module name]__[fn_name]`. + +For example, `fn foo() {}` in module `bar` will result in `bar__foo()`. + +To use a custom export name, use the `[export]` attribute: + +``` +[export: 'my_custom_c_name'] +fn foo() { +} +``` + + +## Atomics + +V has no special support for atomics, yet, nevertheless it's possible to treat variables as atomics +by calling C functions from V. The standard C11 atomic functions like `atomic_store()` are usually +defined with the help of macros and C compiler magic to provide a kind of *overloaded C functions*. +Since V does not support overloading functions by intention there are wrapper functions defined in +C headers named `atomic.h` that are part of the V compiler infrastructure. + +There are dedicated wrappers for all unsigned integer types and for pointers. +(`byte` is not fully supported on Windows) – the function names include the type name +as suffix. e.g. `C.atomic_load_ptr()` or `C.atomic_fetch_add_u64()`. + +To use these functions the C header for the used OS has to be included and the functions +that are intended to be used have to be declared. Example: + +```v globals +$if windows { + #include "@VEXEROOT/thirdparty/stdatomic/win/atomic.h" +} $else { + #include "@VEXEROOT/thirdparty/stdatomic/nix/atomic.h" +} + +// declare functions we want to use - V does not parse the C header +fn C.atomic_store_u32(&u32, u32) +fn C.atomic_load_u32(&u32) u32 +fn C.atomic_compare_exchange_weak_u32(&u32, &u32, u32) bool +fn C.atomic_compare_exchange_strong_u32(&u32, &u32, u32) bool + +const num_iterations = 10000000 + +// see section "Global Variables" below +__global ( + atom u32 // ordinary variable but used as atomic +) + +fn change() int { + mut races_won_by_change := 0 + for { + mut cmp := u32(17) // addressable value to compare with and to store the found value + // atomic version of `if atom == 17 { atom = 23 races_won_by_change++ } else { cmp = atom }` + if C.atomic_compare_exchange_strong_u32(&atom, &cmp, 23) { + races_won_by_change++ + } else { + if cmp == 31 { + break + } + cmp = 17 // re-assign because overwritten with value of atom + } + } + return races_won_by_change +} + +fn main() { + C.atomic_store_u32(&atom, 17) + t := go change() + mut races_won_by_main := 0 + mut cmp17 := u32(17) + mut cmp23 := u32(23) + for i in 0 .. num_iterations { + // atomic version of `if atom == 17 { atom = 23 races_won_by_main++ }` + if C.atomic_compare_exchange_strong_u32(&atom, &cmp17, 23) { + races_won_by_main++ + } else { + cmp17 = 17 + } + desir := if i == num_iterations - 1 { u32(31) } else { u32(17) } + // atomic version of `for atom != 23 {} atom = desir` + for !C.atomic_compare_exchange_weak_u32(&atom, &cmp23, desir) { + cmp23 = 23 + } + } + races_won_by_change := t.wait() + atom_new := C.atomic_load_u32(&atom) + println('atom: $atom_new, #exchanges: ${races_won_by_main + races_won_by_change}') + // prints `atom: 31, #exchanges: 10000000`) + println('races won by\n- `main()`: $races_won_by_main\n- `change()`: $races_won_by_change') +} +``` + +In this example both `main()` and the spawned thread `change()` try to replace a value of `17` +in the global `atom` with a value of `23`. The replacement in the opposite direction is +done exactly 10000000 times. The last replacement will be with `31` which makes the spawned +thread finish. + +It is not predictable how many replacements occur in which thread, but the sum will always +be 10000000. (With the non-atomic commands from the comments the value will be higher or the program +will hang – dependent on the compiler optimization used.) + +## Global Variables + +By default V does not allow global variables. However, in low level applications they have their +place so their usage can be enabled with the compiler flag `-enable-globals`. +Declarations of global variables must be surrounded with a `__global ( ... )` +specification – as in the example [above](#atomics). + +An initializer for global variables must be explicitly converted to the +desired target type. If no initializer is given a default initialization is done. +Some objects like semaphores and mutexes require an explicit initialization *in place*, i.e. +not with a value returned from a function call but with a method call by reference. +A separate `init()` function can be used for this purpose – it will be called before `main()`: + +```v globals +import sync + +__global ( + sem sync.Semaphore // needs initialization in `init()` + mtx sync.RwMutex // needs initialization in `init()` + f1 = f64(34.0625) // explicily initialized + shmap shared map[string]f64 // initialized as empty `shared` map + f2 f64 // initialized to `0.0` +) + +fn init() { + sem.init(0) + mtx.init() +} +``` +Be aware that in multi threaded applications the access to global variables is subject +to race conditions. There are several approaches to deal with these: + +- use `shared` types for the variable declarations and use `lock` blocks for access. + This is most appropriate for larger objects like structs, arrays or maps. +- handle primitive data types as "atomics" using special C-functions (see [above](#atomics)). +- use explicit synchronization primitives like mutexes to control access. The compiler + cannot really help in this case, so you have to know what you are doing. +- don't care – this approach is possible but makes only sense if the exact values + of global variables do not really matter. An example can be found in the `rand` module + where global variables are used to generate (non cryptographic) pseudo random numbers. + In this case data races lead to random numbers in different threads becoming somewhat + correlated, which is acceptable considering the performance penalty that using + synchonization primitives would represent. + +### Passing C compilation flags + +Add `#flag` directives to the top of your V files to provide C compilation flags like: + +- `-I` for adding C include files search paths +- `-l` for adding C library names that you want to get linked +- `-L` for adding C library files search paths +- `-D` for setting compile time variables + +You can (optionally) use different flags for different targets. +Currently the `linux`, `darwin` , `freebsd`, and `windows` flags are supported. + +NB: Each flag must go on its own line (for now) + +```v oksyntax +#flag linux -lsdl2 +#flag linux -Ivig +#flag linux -DCIMGUI_DEFINE_ENUMS_AND_STRUCTS=1 +#flag linux -DIMGUI_DISABLE_OBSOLETE_FUNCTIONS=1 +#flag linux -DIMGUI_IMPL_API= +``` + +In the console build command, you can use: +* `-cflags` to pass custom flags to the backend C compiler. +* `-cc` to change the default C backend compiler. +* For example: `-cc gcc-9 -cflags -fsanitize=thread`. + +You can define a `VFLAGS` environment variable in your terminal to store your `-cc` +and `-cflags` settings, rather than including them in the build command each time. + +### #pkgconfig + +Add `#pkgconfig` directive is used to tell the compiler which modules should be used for compiling +and linking using the pkg-config files provided by the respective dependencies. + +As long as backticks can't be used in `#flag` and spawning processes is not desirable for security +and portability reasons, V uses its own pkgconfig library that is compatible with the standard +freedesktop one. + +If no flags are passed it will add `--cflags` and `--libs`, both lines below do the same: + +```v oksyntax +#pkgconfig r_core +#pkgconfig --cflags --libs r_core +``` + +The `.pc` files are looked up into a hardcoded list of default pkg-config paths, the user can add +extra paths by using the `PKG_CONFIG_PATH` environment variable. Multiple modules can be passed. + +To check the existance of a pkg-config use `$pkgconfig('pkg')` as a compile time if condition to +check if a pkg-config exists. If it exists the branch will be created. Use `$else` or `$else $if` +to handle other cases. + +```v ignore +$if $pkgconfig('mysqlclient') { + #pkgconfig mysqlclient +} $else $if $pkgconfig('mariadb') { + #pkgconfig mariadb +} +``` + +### Including C code + +You can also include C code directly in your V module. +For example, let's say that your C code is located in a folder named 'c' inside your module folder. +Then: + +* Put a v.mod file inside the toplevel folder of your module (if you +created your module with `v new` you already have v.mod file). For +example: +```v ignore +Module { + name: 'mymodule', + description: 'My nice module wraps a simple C library.', + version: '0.0.1' + dependencies: [] +} +``` + + +* Add these lines to the top of your module: +```v oksyntax +#flag -I @VMODROOT/c +#flag @VMODROOT/c/implementation.o +#include "header.h" +``` +NB: @VMODROOT will be replaced by V with the *nearest parent folder, where there is a v.mod file*. +Any .v file beside or below the folder where the v.mod file is, +can use `#flag @VMODROOT/abc` to refer to this folder. +The @VMODROOT folder is also *prepended* to the module lookup path, +so you can *import* other modules under your @VMODROOT, by just naming them. + +The instructions above will make V look for an compiled .o file in +your module `folder/c/implementation.o`. +If V finds it, the .o file will get linked to the main executable, that used the module. +If it does not find it, V assumes that there is a `@VMODROOT/c/implementation.c` file, +and tries to compile it to a .o file, then will use that. + +This allows you to have C code, that is contained in a V module, so that its distribution is easier. +You can see a complete minimal example for using C code in a V wrapper module here: +[project_with_c_code](https://github.com/vlang/v/tree/master/vlib/v/tests/project_with_c_code). +Another example, demonstrating passing structs from C to V and back again: +[interoperate between C to V to C](https://github.com/vlang/v/tree/master/vlib/v/tests/project_with_c_code_2). + +### C types + +Ordinary zero terminated C strings can be converted to V strings with +`unsafe { &char(cstring).vstring() }` or if you know their length already with +`unsafe { &char(cstring).vstring_with_len(len) }`. + +NB: The .vstring() and .vstring_with_len() methods do NOT create a copy of the `cstring`, +so you should NOT free it after calling the method `.vstring()`. +If you need to make a copy of the C string (some libc APIs like `getenv` pretty much require that, +since they return pointers to internal libc memory), you can use `cstring_to_vstring(cstring)`. + +On Windows, C APIs often return so called `wide` strings (utf16 encoding). +These can be converted to V strings with `string_from_wide(&u16(cwidestring))` . + +V has these types for easier interoperability with C: + +- `voidptr` for C's `void*`, +- `&byte` for C's `byte*` and +- `&char` for C's `char*`. +- `&&char` for C's `char**` + +To cast a `voidptr` to a V reference, use `user := &User(user_void_ptr)`. + +`voidptr` can also be dereferenced into a V struct through casting: `user := User(user_void_ptr)`. + +[an example of a module that calls C code from V](https://github.com/vlang/v/blob/master/vlib/v/tests/project_with_c_code/mod1/wrapper.v) + +### C Declarations + +C identifiers are accessed with the `C` prefix similarly to how module-specific +identifiers are accessed. Functions must be redeclared in V before they can be used. +Any C types may be used behind the `C` prefix, but types must be redeclared in V in +order to access type members. + +To redeclare complex types, such as in the following C code: + +```c +struct SomeCStruct { + uint8_t implTraits; + uint16_t memPoolData; + union { + struct { + void* data; + size_t size; + }; + + DataView view; + }; +}; +``` + +members of sub-data-structures may be directly declared in the containing struct as below: + +```v +struct C.SomeCStruct { + implTraits byte + memPoolData u16 + // These members are part of sub data structures that can't currently be represented in V. + // Declaring them directly like this is sufficient for access. + // union { + // struct { + data voidptr + size size_t + // } + view C.DataView + // } +} +``` + +The existence of the data members is made known to V, and they may be used without +re-creating the original structure exactly. + +Alternatively, you may [embed](#embedded-structs) the sub-data-structures to maintain +a parallel code structure. + +## Debugging + +### C Backend binaries (Default) + +To debug issues in the generated binary (flag: `-b c`), you can pass these flags: + +- `-g` - produces a less optimized executable with more debug information in it. + V will enforce line numbers from the .v files in the stacktraces, that the + executable will produce on panic. It is usually better to pass -g, unless + you are writing low level code, in which case use the next option `-cg`. +- `-cg` - produces a less optimized executable with more debug information in it. + The executable will use C source line numbers in this case. It is frequently + used in combination with `-keepc`, so that you can inspect the generated + C program in case of panic, or so that your debugger (`gdb`, `lldb` etc.) + can show you the generated C source code. +- `-showcc` - prints the C command that is used to build the program. +- `-show-c-output` - prints the output, that your C compiler produced + while compiling your program. +- `-keepc` - do not delete the generated C source code file after a successful + compilation. Also keep using the same file path, so it is more stable, + and easier to keep opened in an editor/IDE. + +For best debugging experience if you are writing a low level wrapper for an existing +C library, you can pass several of these flags at the same time: +`v -keepc -cg -showcc yourprogram.v`, then just run your debugger (gdb/lldb) or IDE +on the produced executable `yourprogram`. + +If you just want to inspect the generated C code, +without further compilation, you can also use the `-o` flag (e.g. `-o file.c`). +This will make V produce the `file.c` then stop. + +If you want to see the generated C source code for *just* a single C function, +for example `main`, you can use: `-printfn main -o file.c`. + +To debug the V executable itself you need to compile from src with `./v -g -o v cmd/v`. + +You can debug tests with for example `v -g -keepc prog_test.v`. The `-keepc` flag is needed, +so that the executable is not deleted, after it was created and ran. + +To see a detailed list of all flags that V supports, +use `v help`, `v help build` and `v help build-c`. + +**Commandline Debugging** + +1. compile your binary with debugging info `v -g hello.v` +2. debug with [lldb](https://lldb.llvm.org) or [GDB](https://www.gnu.org/software/gdb/) e.g. `lldb hello` + +Troubleshooting (debugging) executables [created with V in GDB](https://github.com/vlang/v/wiki/Troubleshooting-(debugging)-executables-created-with-V-in-GDB) + +**Visual debugging Setup:** +* [Visual Studio Code](vscode.md) + +### Native Backend binaries + +Currently there is no debugging support for binaries, created by the +native backend (flag: `-b native`). + +### Javascript Backend + +To debug the generated Javascript output you can active source maps: +`v -b js -sourcemap hello.v -o hello.js` + +For all supported options check the latest help: +`v help build-js` + +## Conditional compilation + +### Compile time code + +`$` is used as a prefix for compile-time operations. + +#### `$if` condition +```v +// Support for multiple conditions in one branch +$if ios || android { + println('Running on a mobile device!') +} +$if linux && x64 { + println('64-bit Linux.') +} +// Usage as expression +os := $if windows { 'Windows' } $else { 'UNIX' } +println('Using $os') +// $else-$if branches +$if tinyc { + println('tinyc') +} $else $if clang { + println('clang') +} $else $if gcc { + println('gcc') +} $else { + println('different compiler') +} +$if test { + println('testing') +} +// v -cg ... +$if debug { + println('debugging') +} +// v -prod ... +$if prod { + println('production build') +} +// v -d option ... +$if option ? { + println('custom option') +} +``` + +If you want an `if` to be evaluated at compile time it must be prefixed with a `$` sign. +Right now it can be used to detect an OS, compiler, platform or compilation options. +`$if debug` is a special option like `$if windows` or `$if x32`. +If you're using a custom ifdef, then you do need `$if option ? {}` and compile with`v -d option`. +Full list of builtin options: +| OS | Compilers | Platforms | Other | +| --- | --- | --- | --- | +| `windows`, `linux`, `macos` | `gcc`, `tinyc` | `amd64`, `arm64` | `debug`, `prod`, `test` | +| `mac`, `darwin`, `ios`, | `clang`, `mingw` | `x64`, `x32` | `js`, `glibc`, `prealloc` | +| `android`,`mach`, `dragonfly` | `msvc` | `little_endian` | `no_bounds_checking`, `freestanding` | +| `gnu`, `hpux`, `haiku`, `qnx` | `cplusplus` | `big_endian` | +| `solaris` | | | | + +#### `$embed_file` + +```v ignore +import os +fn main() { + embedded_file := $embed_file('v.png') + os.write_file('exported.png', embedded_file.to_string()) ? +} +``` + +V can embed arbitrary files into the executable with the `$embed_file()` +compile time call. Paths can be absolute or relative to the source file. + +When you do not use `-prod`, the file will not be embedded. Instead, it will +be loaded *the first time* your program calls `f.data()` at runtime, making +it easier to change in external editor programs, without needing to recompile +your executable. + +When you compile with `-prod`, the file *will be embedded inside* your +executable, increasing your binary size, but making it more self contained +and thus easier to distribute. In this case, `f.data()` will cause *no IO*, +and it will always return the same data. + +#### `$tmpl` for embedding and parsing V template files + +V has a simple template language for text and html templates, and they can easily +be embedded via `$tmpl('path/to/template.txt')`: + + +```v ignore +fn build() string { + name := 'Peter' + age := 25 + numbers := [1, 2, 3] + return $tmpl('1.txt') +} + +fn main() { + println(build()) +} +``` + +1.txt: + +``` +name: @name + +age: @age + +numbers: @numbers + +@for number in numbers + @number +@end +``` + +output: + +``` +name: Peter + +age: 25 + +numbers: [1, 2, 3] + +1 +2 +3 +``` + + + + +#### `$env` + +```v +module main + +fn main() { + compile_time_env := $env('ENV_VAR') + println(compile_time_env) +} +``` + +V can bring in values at compile time from environment variables. +`$env('ENV_VAR')` can also be used in top-level `#flag` and `#include` statements: +`#flag linux -I $env('JAVA_HOME')/include`. + +### Environment specific files + +If a file has an environment-specific suffix, it will only be compiled for that environment. + +- `.js.v` => will be used only by the JS backend. These files can contain JS. code. +- `.c.v` => will be used only by the C backend. These files can contain C. code. +- `.native.v` => will be used only by V's native backend. +- `_nix.c.v` => will be used only on Unix systems (non Windows). +- `_${os}.c.v` => will be used only on the specific `os` system. +For example, `_windows.c.v` will be used only when compiling on Windows, or with `-os windows`. +- `_default.c.v` => will be used only if there is NOT a more specific platform file. +For example, if you have both `file_linux.c.v` and `file_default.c.v`, +and you are compiling for linux, then only `file_linux.c.v` will be used, +and `file_default.c.v` will be ignored. + +Here is a more complete example: +main.v: +```v ignore +module main +fn main() { println(message) } +``` + +main_default.c.v: +```v ignore +module main +const ( message = 'Hello world' ) +``` + +main_linux.c.v: +```v ignore +module main +const ( message = 'Hello linux' ) +``` + +main_windows.c.v: +```v ignore +module main +const ( message = 'Hello windows' ) +``` + +With the example above: +- when you compile for windows, you will get 'Hello windows' +- when you compile for linux, you will get 'Hello linux' +- when you compile for any other platform, you will get the +non specific 'Hello world' message. + +- `_d_customflag.v` => will be used *only* if you pass `-d customflag` to V. +That corresponds to `$if customflag ? {}`, but for a whole file, not just a +single block. `customflag` should be a snake_case identifier, it can not +contain arbitrary characters (only lower case latin letters + numbers + `_`). +NB: a combinatorial `_d_customflag_linux.c.v` postfix will not work. +If you do need a custom flag file, that has platform dependent code, use the +postfix `_d_customflag.v`, and then use plaftorm dependent compile time +conditional blocks inside it, i.e. `$if linux {}` etc. + +- `_notd_customflag.v` => similar to _d_customflag.v, but will be used +*only* if you do NOT pass `-d customflag` to V. + +## Compile time pseudo variables + +V also gives your code access to a set of pseudo string variables, +that are substituted at compile time: + +- `@FN` => replaced with the name of the current V function +- `@METHOD` => replaced with ReceiverType.MethodName +- `@MOD` => replaced with the name of the current V module +- `@STRUCT` => replaced with the name of the current V struct +- `@FILE` => replaced with the path of the V source file +- `@LINE` => replaced with the V line number where it appears (as a string). +- `@COLUMN` => replaced with the column where it appears (as a string). +- `@VEXE` => replaced with the path to the V compiler +- `@VEXEROOT` => will be substituted with the *folder*, + where the V executable is (as a string). +- `@VHASH` => replaced with the shortened commit hash of the V compiler (as a string). +- `@VMOD_FILE` => replaced with the contents of the nearest v.mod file (as a string). +- `@VMODROOT` => will be substituted with the *folder*, + where the nearest v.mod file is (as a string). + +That allows you to do the following example, useful while debugging/logging/tracing your code: +```v +eprintln('file: ' + @FILE + ' | line: ' + @LINE + ' | fn: ' + @MOD + '.' + @FN) +``` + +Another example, is if you want to embed the version/name from v.mod *inside* your executable: +```v ignore +import v.vmod +vm := vmod.decode( @VMOD_FILE ) or { panic(err.msg) } +eprintln('$vm.name $vm.version\n $vm.description') +``` + +## Performance tuning + +The generated C code is usually fast enough, when you compile your code +with `-prod`. There are some situations though, where you may want to give +additional hints to the compiler, so that it can further optimize some +blocks of code. + +NB: These are *rarely* needed, and should not be used, unless you +*profile your code*, and then see that there are significant benefits for them. +To cite gcc's documentation: "programmers are notoriously bad at predicting +how their programs actually perform". + +`[inline]` - you can tag functions with `[inline]`, so the C compiler will +try to inline them, which in some cases, may be beneficial for performance, +but may impact the size of your executable. + +`[direct_array_access]` - in functions tagged with `[direct_array_access]` +the compiler will translate array operations directly into C array operations - +omiting bounds checking. This may save a lot of time in a function that iterates +over an array but at the cost of making the function unsafe - unless +the boundaries will be checked by the user. + +`if _likely_(bool expression) {` this hints the C compiler, that the passed +boolean expression is very likely to be true, so it can generate assembly +code, with less chance of branch misprediction. In the JS backend, +that does nothing. + +`if _unlikely_(bool expression) {` similar to `_likely_(x)`, but it hints that +the boolean expression is highly improbable. In the JS backend, that does nothing. + + + +## Compile-time reflection + +Having built-in JSON support is nice, but V also allows you to create efficient +serializers for any data format. V has compile-time `if` and `for` constructs: + +```v wip +// TODO: not fully implemented + +struct User { + name string + age int +} + +// Note: T should be passed a struct name only +fn decode(data string) T { + mut result := T{} + // compile-time `for` loop + // T.fields gives an array of a field metadata type + $for field in T.fields { + $if field.typ is string { + // $(string_expr) produces an identifier + result.$(field.name) = get_string(data, field.name) + } $else $if field.typ is int { + result.$(field.name) = get_int(data, field.name) + } + } + return result +} + +// `decode` generates: +fn decode_User(data string) User { + mut result := User{} + result.name = get_string(data, 'name') + result.age = get_int(data, 'age') + return result +} +``` + +## Limited operator overloading + +```v +struct Vec { + x int + y int +} + +fn (a Vec) str() string { + return '{$a.x, $a.y}' +} + +fn (a Vec) + (b Vec) Vec { + return Vec{a.x + b.x, a.y + b.y} +} + +fn (a Vec) - (b Vec) Vec { + return Vec{a.x - b.x, a.y - b.y} +} + +fn main() { + a := Vec{2, 3} + b := Vec{4, 5} + mut c := Vec{1, 2} + println(a + b) // "{6, 8}" + println(a - b) // "{-2, -2}" + c += a + println(c) // "{3, 5}" +} +``` + +Operator overloading goes against V's philosophy of simplicity and predictability. +But since scientific and graphical applications are among V's domains, +operator overloading is an important feature to have in order to improve readability: + +`a.add(b).add(c.mul(d))` is a lot less readable than `a + b + c * d`. + +To improve safety and maintainability, operator overloading is limited: + +- It's only possible to overload `+, -, *, /, %, <, >, ==, !=, <=, >=` operators. +- `==` and `!=` are self generated by the compiler but can be overriden. +- Calling other functions inside operator functions is not allowed. +- Operator functions can't modify their arguments. +- When using `<` and `==` operators, the return type must be `bool`. +- `!=`, `>`, `<=` and `>=` are auto generated when `==` and `<` are defined. +- Both arguments must have the same type (just like with all operators in V). +- Assignment operators (`*=`, `+=`, `/=`, etc) +are auto generated when the operators are defined though they must return the same type. + +## Inline assembly + +```v ignore +a := 100 +b := 20 +mut c := 0 +asm amd64 { + mov eax, a + add eax, b + mov c, eax + ; =r (c) as c // output + ; r (a) as a // input + r (b) as b +} +println('a: $a') // 100 +println('b: $b') // 20 +println('c: $c') // 120 +``` + +For more examples, see [github.com/vlang/v/tree/master/vlib/v/tests/assembly/asm_test.amd64.v](https://github.com/vlang/v/tree/master/vlib/v/tests/assembly/asm_test.amd64.v) + +## Translating C to V + +TODO: translating C to V will be available in V 0.3. + +V can translate your C code to human readable V code and generate V wrappers on top of C libraries. + + +Let's create a simple program `test.c` first: + +```c +#include "stdio.h" + +int main() { + for (int i = 0; i < 10; i++) { + printf("hello world\n"); + } + return 0; +} +``` + +Run `v translate test.c`, and V will generate `test.v`: + +```v +fn main() { + for i := 0; i < 10; i++ { + println('hello world') + } +} +``` + +To generate a wrapper on top of a C library use this command: + +```bash +v wrapper c_code/libsodium/src/libsodium +``` + +This will generate a directory `libsodium` with a V module. + +Example of a C2V generated libsodium wrapper: + +https://github.com/medvednikov/libsodium + +
    + +When should you translate C code and when should you simply call C code from V? + +If you have well-written, well-tested C code, +then of course you can always simply call this C code from V. + +Translating it to V gives you several advantages: + +- If you plan to develop that code base, you now have everything in one language, + which is much safer and easier to develop in than C. +- Cross-compilation becomes a lot easier. You don't have to worry about it at all. +- No more build flags and include files either. + +## Hot code reloading + +```v live +module main + +import time + +[live] +fn print_message() { + println('Hello! Modify this message while the program is running.') +} + +fn main() { + for { + print_message() + time.sleep(500 * time.millisecond) + } +} +``` + +Build this example with `v -live message.v`. + +Functions that you want to be reloaded must have `[live]` attribute +before their definition. + +Right now it's not possible to modify types while the program is running. + +More examples, including a graphical application: +[github.com/vlang/v/tree/master/examples/hot_code_reload](https://github.com/vlang/v/tree/master/examples/hot_reload). + +## Cross compilation + +To cross compile your project simply run + +```shell +v -os windows . +``` + +or + +```shell +v -os linux . +``` + +(Cross compiling for macOS is temporarily not possible.) + +If you don't have any C dependencies, that's all you need to do. This works even +when compiling GUI apps using the `ui` module or graphical apps using `gg`. + +You will need to install Clang, LLD linker, and download a zip file with +libraries and include files for Windows and Linux. V will provide you with a link. + +## Cross-platform shell scripts in V + +V can be used as an alternative to Bash to write deployment scripts, build scripts, etc. + +The advantage of using V for this is the simplicity and predictability of the language, and +cross-platform support. "V scripts" run on Unix-like systems as well as on Windows. + +Use the `.vsh` file extension. It will make all functions in the `os` +module global (so that you can use `mkdir()` instead of `os.mkdir()`, for example). + +An example `deploy.vsh`: +```v wip +#!/usr/bin/env -S v run +// The shebang above associates the file to V on Unix-like systems, +// so it can be run just by specifying the path to the file +// once it's made executable using `chmod +x`. + +// Remove if build/ exits, ignore any errors if it doesn't +rmdir_all('build') or { } + +// Create build/, never fails as build/ does not exist +mkdir('build') ? + +// Move *.v files to build/ +result := exec('mv *.v build/') ? +if result.exit_code != 0 { + println(result.output) +} +// Similar to: +// files := ls('.') ? +// mut count := 0 +// if files.len > 0 { +// for file in files { +// if file.ends_with('.v') { +// mv(file, 'build/') or { +// println('err: $err') +// return +// } +// } +// count++ +// } +// } +// if count == 0 { +// println('No files') +// } +``` + +Now you can either compile this like a normal V program and get an executable you can deploy and run +anywhere: +`v deploy.vsh && ./deploy` + +Or just run it more like a traditional Bash script: +`v run deploy.vsh` + +On Unix-like platforms, the file can be run directly after making it executable using `chmod +x`: +`./deploy.vsh` + +## Attributes + +V has several attributes that modify the behavior of functions and structs. + +An attribute is a compiler instruction specified inside `[]` right before a +function/struct/enum declaration and applies only to the following declaration. + +```v +// Calling this function will result in a deprecation warning +[deprecated] +fn old_function() { +} + +// It can also display a custom deprecation message +[deprecated: 'use new_function() instead'] +fn legacy_function() {} + +// This function's calls will be inlined. +[inline] +fn inlined_function() { +} + +// This function's calls will NOT be inlined. +[noinline] +fn function() { +} + +// This function will NOT return to its callers. +// Such functions can be used at the end of or blocks, +// just like exit/1 or panic/1. Such functions can not +// have return types, and should end either in for{}, or +// by calling other `[noreturn]` functions. +[noreturn] +fn forever() { + for {} +} + +// The following struct must be allocated on the heap. Therefore, it can only be used as a +// reference (`&Window`) or inside another reference (`&OuterStruct{ Window{...} }`). +// See section "Stack and Heap" +[heap] +struct Window { +} + +// V will not generate this function and all its calls if the provided flag is false. +// To use a flag, use `v -d flag` +[if debug] +fn foo() { +} + +fn bar() { + foo() // will not be called if `-d debug` is not passed +} + +// The memory pointed to by the pointer arguments of this function will not be +// freed by the garbage collector (if in use) before the function returns +[keep_args_alive] +fn C.my_external_function(voidptr, int, voidptr) int + +// Calls to following function must be in unsafe{} blocks. +// Note that the code in the body of `risky_business()` will still be +// checked, unless you also wrap it in `unsafe {}` blocks. +// This is usefull, when you want to have an `[unsafe]` function that +// has checks before/after a certain unsafe operation, that will still +// benefit from V's safety features. +[unsafe] +fn risky_business() { + // code that will be checked, perhaps checking pre conditions + unsafe { + // code that *will not be* checked, like pointer arithmetic, + // accessing union fields, calling other `[unsafe]` fns, etc... + // Usually, it is a good idea to try minimizing code wrapped + // in unsafe{} as much as possible. + // See also [Memory-unsafe code](#memory-unsafe-code) + } + // code that will be checked, perhaps checking post conditions and/or + // keeping invariants +} + +// V's autofree engine will not take care of memory management in this function. +// You will have the responsibility to free memory manually yourself in it. +[manualfree] +fn custom_allocations() { +} + +// For C interop only, tells V that the following struct is defined with `typedef struct` in C +[typedef] +struct C.Foo { +} + +// Used in Win32 API code when you need to pass callback function +[windows_stdcall] +fn C.DefWindowProc(hwnd int, msg int, lparam int, wparam int) + +// Windows only: +// If a default graphics library is imported (ex. gg, ui), then the graphical window takes +// priority and no console window is created, effectively disabling println() statements. +// Use to explicity create console window. Valid before main() only. +[console] +fn main() { +} +``` + +## Goto + +V allows unconditionally jumping to a label with `goto`. The label name must be contained +within the same function as the `goto` statement. A program may `goto` a label outside +or deeper than the current scope. `goto` allows jumping past variable initialization or +jumping back to code that accesses memory that has already been freed, so it requires +`unsafe`. + +```v ignore +if x { + // ... + if y { + unsafe { + goto my_label + } + } + // ... +} +my_label: +``` +`goto` should be avoided, particularly when `for` can be used instead. +[Labelled break/continue](#labelled-break--continue) can be used to break out of +a nested loop, and those do not risk violating memory-safety. + +# Appendices + +## Appendix I: Keywords + +V has 41 reserved keywords (3 are literals): + +```v ignore +as +asm +assert +atomic +break +const +continue +defer +else +embed +enum +false +fn +for +go +goto +if +import +in +interface +is +lock +match +module +mut +none +or +pub +return +rlock +select +shared +sizeof +static +struct +true +type +typeof +union +unsafe +__offsetof +``` +See also [V Types](#v-types). + +## Appendix II: Operators + +This lists operators for [primitive types](#primitive-types) only. + +```v ignore ++ sum integers, floats, strings +- difference integers, floats +* product integers, floats +/ quotient integers, floats +% remainder integers + +~ bitwise NOT integers +& bitwise AND integers +| bitwise OR integers +^ bitwise XOR integers + +! logical NOT bools +&& logical AND bools +|| logical OR bools +!= logical XOR bools + +<< left shift integer << unsigned integer +>> right shift integer >> unsigned integer + + +Precedence Operator + 5 * / % << >> & + 4 + - | ^ + 3 == != < <= > >= + 2 && + 1 || + + +Assignment Operators ++= -= *= /= %= +&= |= ^= +>>= <<= +``` diff --git a/v_windows/v/old/doc/img/vscode-debugger.png b/v_windows/v/old/doc/img/vscode-debugger.png new file mode 100644 index 0000000..ff3b799 Binary files /dev/null and b/v_windows/v/old/doc/img/vscode-debugger.png differ diff --git a/v_windows/v/old/doc/upcoming.md b/v_windows/v/old/doc/upcoming.md new file mode 100644 index 0000000..45a1d98 --- /dev/null +++ b/v_windows/v/old/doc/upcoming.md @@ -0,0 +1,195 @@ +# V Work In Progress + +***This document describes features that are not implemented, yet. +Please refer to [docs.md](https://github.com/vlang/v/blob/master/doc/docs.md) +for the current state of V*** + +## Table of Contents + +* [Concurrency](#concurrency) + * [Variable Declarations](#variable-declarations) + * [Strengths](#strengths) + * [Weaknesses](#weaknesses) + * [Compatibility](#compatibility) + * [Automatic Lock](#automatic-lock) + * [Channels](#channels) + +## Concurrency + +### Variable Declarations + +Objects that are supposed to be used to exchange data between +coroutines have to be declared with special care. Exactly one of the following +4 kinds of declaration has to be chosen: + +```v ignore +a := ... +mut b := ... +shared c := ... +atomic d := ... +``` + +- `a` is declared as *constant* that can be passed to + other coroutines and read without limitations. However + it cannot be changed. +- `b` can be accessed reading and writing but only from one + coroutine. That coroutine *owns* the object. A `mut` variable can + be passed to another coroutine (as receiver or function argument in + the `go` statement or via a channel) but then ownership is passed, + too, and only the other coroutine can access the object.1 +- `c` can be passed to coroutines an accessed + *concurrently*.2 In order to avoid data races it has to + be locked before access can occur and unlocked to allow access to + other coroutines. This is done by one the following block structures: + ```v ignore + lock c { + // read, modify, write c + ... + } + ``` + + ```v ignore + rlock c { + // read c + ... + } + ``` + Several variables may be specified: `lock x, y, z { ... }`. + They are unlocked in the opposite order. +- `d` can be passed to coroutines and accessed *concurrently*, + too.3 No lock is needed in this case, however + `atomic` variables can only be 32/64 bit integers (or pointers) + and access is limited to a small set of predefined idioms that have + native hardware support. + +To help making the correct decision the following table summarizes the +different capabilities: + +| | *default* | `mut` | `shared` | `atomic` | +| :--- | :---: | :---: | :---: | :---: | +| write access | | + | + | + | +| concurrent access | + | | + | + | +| performance | ++ | ++ | | + | +| sophisticated operations | + | + | + | | +| structured data types | + | + | + | | + +### Strengths +**default** +- very fast +- unlimited access from different coroutines +- easy to handle + +**`mut`** +- very fast +- easy to handle + +**`shared`** +- concurrent access from different coroutines +- data type may be complex structure +- sophisticated access possible (several statements within one `lock` + block) + +**`atomic`** +- concurrent access from different coroutines +- reasonably fast + +### Weaknesses +**default** +- read only + +**`mut`** +- access only from one coroutine at a time + +**`shared`** +- lock/unlock are slow +- moderately difficult to handle (needs `lock` block) + +**`atomic`** +- limited to single (max. 64 bit) integers (and pointers) +- only a small set of predefined operations possible +- very difficult to handle correctly + +1 The owning coroutine will also free the memory space used +for the object when it is no longer needed. +2 For `shared` objects the compiler adds code for reference +counting. Once the counter reaches 0 the object is automatically freed. +3 Since an `atomic` variable is only a few bytes in size +allocation would be an unnecessary overhead. Instead the compiler +creates a global. + +### Compatibility +Outside of `lock`/`rlock` blocks function arguments must in general +match - with the familiar exception that objects declared `mut` can be +used to call functions expecting immutable arguments: + +```v ignore +fn f(x St) {...} +fn g(mut x St) {...} +fn h(shared x St) {...} +fn i(atomic x u64) {...} + +a := St{...} +f(a) + +mut b := St{...} +f(b) +go g(mut b) +// `b` should not be accessed here any more + +shared c := St{...} +h(shared c) + +atomic d &u64 +i(atomic d) +``` + +Inside a `lock c {...}` block `c` behaves like a `mut`, +inside an `rlock c {...}` block like an immutable: +```v ignore +shared c := St{...} +lock c { + g(mut c) + f(c) + // call to h() not allowed inside `lock` block + // since h() will lock `c` itself +} +rlock c { + f(c) + // call to g() or h() not allowed +} +``` + +### Automatic Lock +In general the compiler will generate an error message when a `shared` +object is accessed outside of any corresponding `lock`/`rlock` +block. However in simple and obvious cases the necessary lock/unlock +can be generated automatically for `array`/`map` operations: + +```v ignore +shared a := []int{cap: 5} +go h2(shared a) +a << 3 +// keep in mind that `h2()` could change `a` between these statements +a << 4 +x := a[1] // not necessarily `4` + +shared b := map[string]int{} +go h3(shared b) +b['apple'] = 3 +c['plume'] = 7 +y := b['apple'] // not necesarily `3` + +// iteration over elements +for k, v in b { + // concurrently changed k/v pairs may or my not be included +} +``` + +This is handy, but since other coroutines might access the `array`/`map` +concurrently between the automatically locked statements, the results +are sometimes surprising. Each statement should be seen as a single +transaction that is unrelated to the previous or following +statement. Therefore - but also for performance reasons - it's often +better to group consecutive coherent statements in an explicit `lock` block. + +### Channels diff --git a/v_windows/v/old/doc/vscode.md b/v_windows/v/old/doc/vscode.md new file mode 100644 index 0000000..e1e1b35 --- /dev/null +++ b/v_windows/v/old/doc/vscode.md @@ -0,0 +1,107 @@ +# Visual Studio Code Setup + +## Table of Contents + +* [V language support](#v-language-support) +* [Visual Debugging](#visual-debugging) + +## V language support + +The [V VS Code Extention](https://marketplace.visualstudio.com/items?itemName=vlanguage.vscode-vlang) +provides V language support for Visual Studio Code. + +![Screenshot Code with activated extention](https://github.com/vlang/vscode-vlang/raw/HEAD/images/demo.png) + +**Features:** +* Syntax Highlighting. +* Code Snippets for quick coding. +* Format code on file save as well as format manually (using v fmt). +* Linter (Workspace files only). +[more](https://marketplace.visualstudio.com/items?itemName=vlanguage.vscode-vlang) + +**Hint:** This extention will not add the V compiler! Information on how to +[install V compiler](https://github.com/vlang/v/blob/master/doc/docs.md#install-from-source) +on your operating system. + +### Setup Extention + +Install [V VS Code Extention](https://marketplace.visualstudio.com/items?itemName=vlanguage.vscode-vlang). + +## Visual Debugging + +![screenshot visual debugger](https://github.com/vlang/v/blob/master/doc/img/vscode-debugger.png?raw=true) + +The [C/C++ Extention](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) +for Visual Studio Code provides visual conditional debugging. + +**Features:** +* Conditional breakpoints +* Function breakpoints +* Expression evaluation +* Change Values +[more Features & Documentation](https://code.visualstudio.com/docs/cpp/cpp-debug) + +**Hint:** Not all types (e.g. Array) in V currently create the required +[DWARF](https://en.wikipedia.org/wiki/DWARF) information to show and +edit the variable. + +### Setup Debugging + +1. Install the [C/C++ Extention](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) +2. Open `RUN AND DEBUG` panel (Debug Icon in left panel). +3. Click on `Show` all automatic debug configurations. +4. Select `Add config`. +5. Select environment `C++ (GDB/LLDB)`. +6. Change the line `"program": "Enter the program name, e.g. \"${workspaceFolder}/a.out\"",` +to point to your compiled application e.g. `"program": "${workspaceFolder}/hello",`. + +This will add a block to your `.workspace` file, +or create the file `.vscode/launch.json`: +```json +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: + // https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "(lldb) Start", + "type": "cppdbg", + "request": "launch", + "program": "Enter the program name, e.g. \"${workspaceFolder}/a.out\"", + "args": [], + "stopAtEntry": false, + "cwd": "${fileDirname}", + "environment": [], + "externalConsole": false, + "MIMode": "lldb" + } + ] +} +``` + +**Optional:** use `"program": "${fileDirname}/${fileBasenameNoExtension}"` to debug +any current open source file with an existing binary with the same name but without any extension. + +### Usage + +To allow your compiled application to be debugged. +The application needs to include additional debugging information +([DWARF](https://en.wikipedia.org/wiki/DWARF)). + +**1. Compile with debugging information:** +`v -b c -g hello.v -o hello` or short `v -g hello.v` + +The `-g` option will add the needed debugging informations. +More Options are explained in the [docs](docs.md#debugging). + + +**2. Start Debugging** + +1. Open your source code and set the required break points +2. Click on the Debug Icon in the left Icon panel and click +`> (lldb) Start`, or use `F5` to launch your application in debug mode. + +For all options look at the official +[C/C++ Extention documentation](https://code.visualstudio.com/docs/cpp/cpp-debug). diff --git a/v_windows/v/old/examples/.gitignore b/v_windows/v/old/examples/.gitignore new file mode 100644 index 0000000..8299fd7 --- /dev/null +++ b/v_windows/v/old/examples/.gitignore @@ -0,0 +1,2 @@ +*.ppm +*.js \ No newline at end of file diff --git a/v_windows/v/old/examples/2048/.gitignore b/v_windows/v/old/examples/2048/.gitignore new file mode 100644 index 0000000..e324642 --- /dev/null +++ b/v_windows/v/old/examples/2048/.gitignore @@ -0,0 +1,2 @@ +2048 +main diff --git a/v_windows/v/old/examples/2048/2048.v b/v_windows/v/old/examples/2048/2048.v new file mode 100644 index 0000000..d237226 --- /dev/null +++ b/v_windows/v/old/examples/2048/2048.v @@ -0,0 +1,939 @@ +import gg +import gx +import math +import math.mathutil as mu +import os +import rand +import time + +struct App { +mut: + gg &gg.Context = 0 + touch TouchInfo + ui Ui + theme &Theme = themes[0] + theme_idx int + board Board + undo []Undo + atickers [4][4]int + state GameState = .play + tile_format TileFormat = .normal + moves int + perf &Perf = 0 + is_ai_mode bool +} + +struct Ui { +mut: + dpi_scale f32 + tile_size int + border_size int + padding_size int + header_size int + font_size int + window_width int + window_height int + x_padding int + y_padding int +} + +struct Theme { + bg_color gx.Color + padding_color gx.Color + text_color gx.Color + game_over_color gx.Color + victory_color gx.Color + tile_colors []gx.Color +} + +const ( + themes = [ + &Theme{ + bg_color: gx.rgb(250, 248, 239) + padding_color: gx.rgb(143, 130, 119) + victory_color: gx.rgb(100, 160, 100) + game_over_color: gx.rgb(190, 50, 50) + text_color: gx.black + tile_colors: [ + gx.rgb(205, 193, 180), /* Empty / 0 tile */ + gx.rgb(238, 228, 218), /* 2 */ + gx.rgb(237, 224, 200), /* 4 */ + gx.rgb(242, 177, 121), /* 8 */ + gx.rgb(245, 149, 99), /* 16 */ + gx.rgb(246, 124, 95), /* 32 */ + gx.rgb(246, 94, 59), /* 64 */ + gx.rgb(237, 207, 114), /* 128 */ + gx.rgb(237, 204, 97), /* 256 */ + gx.rgb(237, 200, 80), /* 512 */ + gx.rgb(237, 197, 63), /* 1024 */ + gx.rgb(237, 194, 46), + ] + }, + &Theme{ + bg_color: gx.rgb(55, 55, 55) + padding_color: gx.rgb(68, 60, 59) + victory_color: gx.rgb(100, 160, 100) + game_over_color: gx.rgb(190, 50, 50) + text_color: gx.white + tile_colors: [ + gx.rgb(123, 115, 108), + gx.rgb(142, 136, 130), + gx.rgb(142, 134, 120), + gx.rgb(145, 106, 72), + gx.rgb(147, 89, 59), + gx.rgb(147, 74, 57), + gx.rgb(147, 56, 35), + gx.rgb(142, 124, 68), + gx.rgb(142, 122, 58), + gx.rgb(142, 120, 48), + gx.rgb(142, 118, 37), + gx.rgb(142, 116, 27), + ] + }, + &Theme{ + bg_color: gx.rgb(38, 38, 66) + padding_color: gx.rgb(58, 50, 74) + victory_color: gx.rgb(100, 160, 100) + game_over_color: gx.rgb(190, 50, 50) + text_color: gx.white + tile_colors: [ + gx.rgb(92, 86, 140), + gx.rgb(106, 99, 169), + gx.rgb(106, 97, 156), + gx.rgb(108, 79, 93), + gx.rgb(110, 66, 76), + gx.rgb(110, 55, 74), + gx.rgb(110, 42, 45), + gx.rgb(106, 93, 88), + gx.rgb(106, 91, 75), + gx.rgb(106, 90, 62), + gx.rgb(106, 88, 48), + gx.rgb(106, 87, 35), + ] + }, + ] + window_title = 'V 2048' + default_window_width = 544 + default_window_height = 560 + animation_length = 10 // frames + frames_per_ai_move = 8 + possible_moves = [Direction.up, .right, .down, .left] + predictions_per_move = 200 + prediction_depth = 8 +) + +// Used for performance monitoring when `-d showfps` is passed, unused / optimized out otherwise +struct Perf { +mut: + frame int + frame_old int + frame_sw time.StopWatch = time.new_stopwatch() + second_sw time.StopWatch = time.new_stopwatch() +} + +struct Pos { + x int = -1 + y int = -1 +} + +struct Board { +mut: + field [4][4]int + points int + shifts int +} + +struct Undo { + board Board + state GameState +} + +struct TileLine { + ypos int +mut: + field [5]int + points int + shifts int +} + +struct TouchInfo { +mut: + start Touch + end Touch +} + +struct Touch { +mut: + pos Pos + time time.Time +} + +enum TileFormat { + normal + log + exponent + shifts + none_ + end_ // To know when to wrap around +} + +enum GameState { + play + over + victory + freeplay +} + +enum LabelKind { + points + moves + tile + victory + game_over + score_end +} + +enum Direction { + up + down + left + right +} + +// Utility functions +[inline] +fn avg(a int, b int) int { + return (a + b) / 2 +} + +fn (b Board) transpose() Board { + mut res := b + for y in 0 .. 4 { + for x in 0 .. 4 { + res.field[y][x] = b.field[x][y] + } + } + return res +} + +fn (b Board) hmirror() Board { + mut res := b + for y in 0 .. 4 { + for x in 0 .. 4 { + res.field[y][x] = b.field[y][3 - x] + } + } + return res +} + +fn (t TileLine) to_left() TileLine { + right_border_idx := 4 + mut res := t + mut zeros := 0 + mut nonzeros := 0 + // gather meta info about the line: + for x in res.field { + if x == 0 { + zeros++ + } else { + nonzeros++ + } + } + if nonzeros == 0 { + // when all the tiles are empty, there is nothing left to do + return res + } + if zeros > 0 { + // we have some 0s, do shifts to compact them: + mut remaining_zeros := zeros + for x := 0; x < right_border_idx - 1; x++ { + for res.field[x] == 0 && remaining_zeros > 0 { + res.shifts++ + for k := x; k < right_border_idx; k++ { + res.field[k] = res.field[k + 1] + } + remaining_zeros-- + } + } + } + // At this point, the non 0 tiles are all on the left, with no empty spaces + // between them. we can safely merge them, when they have the same value: + for x := 0; x < right_border_idx - 1; x++ { + if res.field[x] == 0 { + break + } + if res.field[x] == res.field[x + 1] { + for k := x; k < right_border_idx; k++ { + res.field[k] = res.field[k + 1] + } + res.shifts++ + res.field[x]++ + res.points += 1 << res.field[x] + } + } + return res +} + +fn (b Board) to_left() Board { + mut res := b + for y in 0 .. 4 { + mut hline := TileLine{ + ypos: y + } + for x in 0 .. 4 { + hline.field[x] = b.field[y][x] + } + reshline := hline.to_left() + res.shifts += reshline.shifts + res.points += reshline.points + for x in 0 .. 4 { + res.field[y][x] = reshline.field[x] + } + } + return res +} + +fn (b Board) move(d Direction) (Board, bool) { + new := match d { + .left { b.to_left() } + .right { b.hmirror().to_left().hmirror() } + .up { b.transpose().to_left().transpose() } + .down { b.transpose().hmirror().to_left().hmirror().transpose() } + } + // If the board hasn't changed, it's an illegal move, don't allow it. + for x in 0 .. 4 { + for y in 0 .. 4 { + if b.field[x][y] != new.field[x][y] { + return new, true + } + } + } + return new, false +} + +fn (mut b Board) is_game_over() bool { + for y in 0 .. 4 { + for x in 0 .. 4 { + fidx := b.field[y][x] + if fidx == 0 { + // there are remaining zeros + return false + } + if (x > 0 && fidx == b.field[y][x - 1]) + || (x < 4 - 1 && fidx == b.field[y][x + 1]) + || (y > 0 && fidx == b.field[y - 1][x]) + || (y < 4 - 1 && fidx == b.field[y + 1][x]) { + // there are remaining merges + return false + } + } + } + return true +} + +fn (mut app App) update_tickers() { + for y in 0 .. 4 { + for x in 0 .. 4 { + mut old := app.atickers[y][x] + if old > 0 { + old-- + app.atickers[y][x] = old + } + } + } +} + +fn (mut app App) new_game() { + app.board = Board{} + for y in 0 .. 4 { + for x in 0 .. 4 { + app.board.field[y][x] = 0 + app.atickers[y][x] = 0 + } + } + app.state = .play + app.undo = []Undo{cap: 4096} + app.moves = 0 + app.new_random_tile() + app.new_random_tile() +} + +[inline] +fn (mut app App) check_for_victory() { + for y in 0 .. 4 { + for x in 0 .. 4 { + fidx := app.board.field[y][x] + if fidx == 11 { + app.state = .victory + return + } + } + } +} + +[inline] +fn (mut app App) check_for_game_over() { + if app.board.is_game_over() { + app.state = .over + } +} + +fn (mut b Board) place_random_tile() (Pos, int) { + mut etiles := [16]Pos{} + mut empty_tiles_max := 0 + for y in 0 .. 4 { + for x in 0 .. 4 { + fidx := b.field[y][x] + if fidx == 0 { + etiles[empty_tiles_max] = Pos{x, y} + empty_tiles_max++ + } + } + } + if empty_tiles_max > 0 { + new_random_tile_index := rand.intn(empty_tiles_max) + empty_pos := etiles[new_random_tile_index] + // 10% chance of getting a `4` tile + random_value := if rand.f64n(1.0) < 0.9 { 1 } else { 2 } + b.field[empty_pos.y][empty_pos.x] = random_value + return empty_pos, random_value + } + return Pos{}, 0 +} + +fn (mut app App) new_random_tile() { + for y in 0 .. 4 { + for x in 0 .. 4 { + fidx := app.board.field[y][x] + if fidx == 0 { + app.atickers[y][x] = 0 + } + } + } + empty_pos, random_value := app.board.place_random_tile() + if random_value > 0 { + app.atickers[empty_pos.y][empty_pos.x] = animation_length + } + if app.state != .freeplay { + app.check_for_victory() + } + app.check_for_game_over() +} + +fn (mut app App) apply_new_board(new Board) { + old := app.board + app.moves++ + app.board = new + app.undo << Undo{old, app.state} + app.new_random_tile() +} + +fn (mut app App) move(d Direction) { + new, is_valid := app.board.move(d) + if !is_valid { + return + } + app.apply_new_board(new) +} + +struct Prediction { +mut: + move Direction + mpoints f64 + mcmoves f64 +} + +fn (p Prediction) str() string { + return '{ move: ${p.move:5}, mpoints: ${p.mpoints:6.2f}, mcmoves: ${p.mcmoves:6.2f} }' +} + +fn (mut app App) ai_move() { + mut predictions := [4]Prediction{} + mut is_valid := false + think_watch := time.new_stopwatch() + for move in possible_moves { + move_idx := int(move) + predictions[move_idx].move = move + mut mpoints := 0 + mut mcmoves := 0 + for _ in 0 .. predictions_per_move { + mut cboard := app.board + cboard, is_valid = cboard.move(move) + if !is_valid || cboard.is_game_over() { + continue + } + mpoints += cboard.points + cboard.place_random_tile() + mut cmoves := 0 + for !cboard.is_game_over() { + nmove := possible_moves[rand.intn(possible_moves.len)] + cboard, is_valid = cboard.move(nmove) + if !is_valid { + continue + } + cboard.place_random_tile() + cmoves++ + if cmoves > prediction_depth { + break + } + } + mpoints += cboard.points + mcmoves += cmoves + } + predictions[move_idx].mpoints = f64(mpoints) / predictions_per_move + predictions[move_idx].mcmoves = f64(mcmoves) / predictions_per_move + } + think_time := think_watch.elapsed().milliseconds() + mut bestprediction := Prediction{ + mpoints: -1 + } + for move_idx in 0 .. possible_moves.len { + if bestprediction.mpoints < predictions[move_idx].mpoints { + bestprediction = predictions[move_idx] + } + } + eprintln('Simulation time: ${think_time:4}ms | best $bestprediction') + app.move(bestprediction.move) +} + +fn (app &App) label_format(kind LabelKind) gx.TextCfg { + match kind { + .points { + return gx.TextCfg{ + color: if app.state in [.over, .victory] { gx.white } else { app.theme.text_color } + align: .left + size: app.ui.font_size / 2 + } + } + .moves { + return gx.TextCfg{ + color: if app.state in [.over, .victory] { gx.white } else { app.theme.text_color } + align: .right + size: app.ui.font_size / 2 + } + } + .tile { + return gx.TextCfg{ + color: app.theme.text_color + align: .center + vertical_align: .middle + size: app.ui.font_size + } + } + .victory { + return gx.TextCfg{ + color: app.theme.victory_color + align: .center + vertical_align: .middle + size: app.ui.font_size * 2 + } + } + .game_over { + return gx.TextCfg{ + color: app.theme.game_over_color + align: .center + vertical_align: .middle + size: app.ui.font_size * 2 + } + } + .score_end { + return gx.TextCfg{ + color: gx.white + align: .center + vertical_align: .middle + size: app.ui.font_size * 3 / 4 + } + } + } +} + +[inline] +fn (mut app App) set_theme(idx int) { + theme := themes[idx] + app.theme_idx = idx + app.theme = theme + app.gg.set_bg_color(theme.bg_color) +} + +fn (mut app App) resize() { + mut s := gg.dpi_scale() + if s == 0.0 { + s = 1.0 + } + window_size := gg.window_size() + w := window_size.width + h := window_size.height + m := f32(mu.min(w, h)) + app.ui.dpi_scale = s + app.ui.window_width = w + app.ui.window_height = h + app.ui.padding_size = int(m / 38) + app.ui.header_size = app.ui.padding_size + app.ui.border_size = app.ui.padding_size * 2 + app.ui.tile_size = int((m - app.ui.padding_size * 5 - app.ui.border_size * 2) / 4) + app.ui.font_size = int(m / 10) + // If the window's height is greater than its width, center the board vertically. + // If not, center it horizontally + if w > h { + app.ui.y_padding = 0 + app.ui.x_padding = (app.ui.window_width - app.ui.window_height) / 2 + } else { + app.ui.y_padding = (app.ui.window_height - app.ui.window_width - app.ui.header_size) / 2 + app.ui.x_padding = 0 + } +} + +fn (app &App) draw() { + xpad, ypad := app.ui.x_padding, app.ui.y_padding + ww := app.ui.window_width + wh := app.ui.window_height + m := mu.min(ww, wh) + labelx := xpad + app.ui.border_size + labely := ypad + app.ui.border_size / 2 + app.draw_tiles() + // TODO: Make transparency work in `gg` + if app.state == .over { + app.gg.draw_rect(0, 0, ww, wh, gx.rgba(10, 0, 0, 180)) + app.gg.draw_text(ww / 2, (m * 4 / 10) + ypad, 'Game Over', app.label_format(.game_over)) + f := app.label_format(.tile) + msg := $if android { 'Tap to restart' } $else { 'Press `r` to restart' } + app.gg.draw_text(ww / 2, (m * 6 / 10) + ypad, msg, gx.TextCfg{ + ...f + color: gx.white + size: f.size * 3 / 4 + }) + } + if app.state == .victory { + app.gg.draw_rect(0, 0, ww, wh, gx.rgba(0, 10, 0, 180)) + app.gg.draw_text(ww / 2, (m * 4 / 10) + ypad, 'Victory!', app.label_format(.victory)) + // f := app.label_format(.tile) + msg1 := $if android { 'Tap to continue' } $else { 'Press `space` to continue' } + msg2 := $if android { 'Tap to restart' } $else { 'Press `r` to restart' } + app.gg.draw_text(ww / 2, (m * 6 / 10) + ypad, msg1, app.label_format(.score_end)) + app.gg.draw_text(ww / 2, (m * 8 / 10) + ypad, msg2, app.label_format(.score_end)) + } + // Draw at the end, so that it's on top of the victory / game over overlays + app.gg.draw_text(labelx, labely, 'Points: $app.board.points', app.label_format(.points)) + app.gg.draw_text(ww - labelx, labely, 'Moves: $app.moves', app.label_format(.moves)) +} + +fn (app &App) draw_tiles() { + xstart := app.ui.x_padding + app.ui.border_size + ystart := app.ui.y_padding + app.ui.border_size + app.ui.header_size + toffset := app.ui.tile_size + app.ui.padding_size + tiles_size := mu.min(app.ui.window_width, app.ui.window_height) - app.ui.border_size * 2 + // Draw the padding around the tiles + app.gg.draw_rounded_rect(xstart, ystart, tiles_size, tiles_size, tiles_size / 24, + app.theme.padding_color) + // Draw the actual tiles + for y in 0 .. 4 { + for x in 0 .. 4 { + tidx := app.board.field[y][x] + tile_color := if tidx < app.theme.tile_colors.len { + app.theme.tile_colors[tidx] + } else { + // If there isn't a specific color for this tile, reuse the last color available + app.theme.tile_colors.last() + } + anim_size := animation_length - app.atickers[y][x] + tw := int(f64(app.ui.tile_size) / animation_length * anim_size) + th := tw // square tiles, w == h + xoffset := xstart + app.ui.padding_size + x * toffset + (app.ui.tile_size - tw) / 2 + yoffset := ystart + app.ui.padding_size + y * toffset + (app.ui.tile_size - th) / 2 + app.gg.draw_rounded_rect(xoffset, yoffset, tw, th, tw / 8, tile_color) + if tidx != 0 { // 0 == blank spot + xpos := xoffset + tw / 2 + ypos := yoffset + th / 2 + mut fmt := app.label_format(.tile) + fmt = gx.TextCfg{ + ...fmt + size: int(f32(fmt.size - 1) / animation_length * anim_size) + } + match app.tile_format { + .normal { + app.gg.draw_text(xpos, ypos, '${1 << tidx}', fmt) + } + .log { + app.gg.draw_text(xpos, ypos, '$tidx', fmt) + } + .exponent { + app.gg.draw_text(xpos, ypos, '2', fmt) + fs2 := int(f32(fmt.size) * 0.67) + app.gg.draw_text(xpos + app.ui.tile_size / 10, ypos - app.ui.tile_size / 8, + '$tidx', gx.TextCfg{ + ...fmt + size: fs2 + align: gx.HorizontalAlign.left + }) + } + .shifts { + fs2 := int(f32(fmt.size) * 0.6) + app.gg.draw_text(xpos, ypos, '2<<${tidx - 1}', gx.TextCfg{ + ...fmt + size: fs2 + }) + } + .none_ {} // Don't draw any text here, colors only + .end_ {} // Should never get here + } + } + } + } +} + +fn (mut app App) handle_touches() { + s, e := app.touch.start, app.touch.end + adx, ady := mu.abs(e.pos.x - s.pos.x), mu.abs(e.pos.y - s.pos.y) + if mu.max(adx, ady) < 10 { + app.handle_tap() + } else { + app.handle_swipe() + } +} + +fn (mut app App) handle_tap() { + _, ypad := app.ui.x_padding, app.ui.y_padding + w, h := app.ui.window_width, app.ui.window_height + m := mu.min(w, h) + s, e := app.touch.start, app.touch.end + avgx, avgy := avg(s.pos.x, e.pos.x), avg(s.pos.y, e.pos.y) + // TODO: Replace "touch spots" with actual buttons + // bottom left -> change theme + if avgx < 50 && h - avgy < 50 { + app.next_theme() + } + // bottom right -> change tile format + if w - avgx < 50 && h - avgy < 50 { + app.next_tile_format() + } + if app.state == .victory { + if avgy > (m / 2) + ypad { + if avgy < (m * 7 / 10) + ypad { + app.state = .freeplay + } else if avgy < (m * 9 / 10) + ypad { + app.new_game() + } else { + // TODO remove and implement an actual way to toggle themes on mobile + } + } + } else if app.state == .over { + if avgy > (m / 2) + ypad && avgy < (m * 7 / 10) + ypad { + app.new_game() + } + } +} + +fn (mut app App) handle_swipe() { + // Currently, swipes are only used to move the tiles. + // If the user's not playing, exit early to avoid all the unnecessary calculations + if app.state !in [.play, .freeplay] { + return + } + s, e := app.touch.start, app.touch.end + w, h := app.ui.window_width, app.ui.window_height + dx, dy := e.pos.x - s.pos.x, e.pos.y - s.pos.y + adx, ady := mu.abs(dx), mu.abs(dy) + dmin := if mu.min(adx, ady) > 0 { mu.min(adx, ady) } else { 1 } + dmax := if mu.max(adx, ady) > 0 { mu.max(adx, ady) } else { 1 } + tdiff := int(e.time.unix_time_milli() - s.time.unix_time_milli()) + // TODO: make this calculation more accurate (don't use arbitrary numbers) + min_swipe_distance := int(math.sqrt(mu.min(w, h) * tdiff / 100)) + 20 + if dmax < min_swipe_distance { + return + } + // Swipe was too short + if dmax / dmin < 2 { + return + } + // Swiped diagonally + if adx > ady { + if dx < 0 { + app.move(.left) + } else { + app.move(.right) + } + } else { + if dy < 0 { + app.move(.up) + } else { + app.move(.down) + } + } +} + +[inline] +fn (mut app App) next_theme() { + app.set_theme(if app.theme_idx == themes.len - 1 { 0 } else { app.theme_idx + 1 }) +} + +[inline] +fn (mut app App) next_tile_format() { + app.tile_format = TileFormat(int(app.tile_format) + 1) + if app.tile_format == .end_ { + app.tile_format = .normal + } +} + +[inline] +fn (mut app App) undo() { + if app.undo.len > 0 { + undo := app.undo.pop() + app.board = undo.board + app.state = undo.state + app.moves-- + } +} + +fn (mut app App) on_key_down(key gg.KeyCode) { + // these keys are independent from the game state: + match key { + .a { app.is_ai_mode = !app.is_ai_mode } + .escape { exit(0) } + .n, .r { app.new_game() } + .backspace { app.undo() } + .enter { app.next_tile_format() } + .j { app.state = .over } + .t { app.next_theme() } + else {} + } + if app.state in [.play, .freeplay] { + match key { + .w, .up { app.move(.up) } + .a, .left { app.move(.left) } + .s, .down { app.move(.down) } + .d, .right { app.move(.right) } + else {} + } + } + if app.state == .victory { + if key == .space { + app.state = .freeplay + } + } +} + +fn on_event(e &gg.Event, mut app App) { + match e.typ { + .key_down { + app.on_key_down(e.key_code) + } + .resized, .restored, .resumed { + app.resize() + } + .touches_began { + if e.num_touches > 0 { + t := e.touches[0] + app.touch.start = Touch{ + pos: Pos{ + x: int(t.pos_x / app.ui.dpi_scale) + y: int(t.pos_y / app.ui.dpi_scale) + } + time: time.now() + } + } + } + .touches_ended { + if e.num_touches > 0 { + t := e.touches[0] + app.touch.end = Touch{ + pos: Pos{ + x: int(t.pos_x / app.ui.dpi_scale) + y: int(t.pos_y / app.ui.dpi_scale) + } + time: time.now() + } + app.handle_touches() + } + } + .mouse_down { + app.touch.start = Touch{ + pos: Pos{ + x: int(e.mouse_x / app.ui.dpi_scale) + y: int(e.mouse_y / app.ui.dpi_scale) + } + time: time.now() + } + } + .mouse_up { + app.touch.end = Touch{ + pos: Pos{ + x: int(e.mouse_x / app.ui.dpi_scale) + y: int(e.mouse_y / app.ui.dpi_scale) + } + time: time.now() + } + app.handle_touches() + } + else {} + } +} + +fn frame(mut app App) { + $if showfps ? { + app.perf.frame_sw.restart() + } + app.gg.begin() + app.update_tickers() + app.draw() + app.perf.frame++ + if app.is_ai_mode && app.state in [.play, .freeplay] && app.perf.frame % frames_per_ai_move == 0 { + app.ai_move() + } + $if showfps ? { + app.showfps() + } + app.gg.end() +} + +fn init(mut app App) { + app.resize() + $if showfps ? { + app.perf.frame_sw.restart() + app.perf.second_sw.restart() + } +} + +fn (mut app App) showfps() { + println(app.perf.frame_sw.elapsed().microseconds()) + f := app.perf.frame + if (f & 127) == 0 { + last_frame_us := app.perf.frame_sw.elapsed().microseconds() + ticks := f64(app.perf.second_sw.elapsed().milliseconds()) + fps := f64(app.perf.frame - app.perf.frame_old) * ticks / 1000 / 4.5 + last_fps := 128000.0 / ticks + eprintln('frame ${f:-5} | avg. fps: ${fps:-5.1f} | avg. last 128 fps: ${last_fps:-5.1f} | last frame time: ${last_frame_us:-4}µs') + app.perf.second_sw.restart() + app.perf.frame_old = f + } +} + +fn main() { + mut app := &App{} + app.new_game() + mut font_path := os.resource_abs_path(os.join_path('../assets/fonts/', 'RobotoMono-Regular.ttf')) + $if android { + font_path = 'fonts/RobotoMono-Regular.ttf' + } + mut window_title_ := 'V 2048' + // TODO: Make emcc a real platform ifdef + $if emscripten ? { + // in emscripten, sokol uses `window_title` as the selector to the canvas it'll render to, + // and since `document.querySelector('V 2048')` isn't valid JS, we use `canvas` instead + window_title_ = 'canvas' + } + app.perf = &Perf{} + app.gg = gg.new_context( + bg_color: app.theme.bg_color + width: default_window_width + height: default_window_height + sample_count: 4 // higher quality curves + create_window: true + window_title: window_title_ + frame_fn: frame + event_fn: on_event + init_fn: init + user_data: app + font_path: font_path + ) + app.gg.run() +} diff --git a/v_windows/v/old/examples/2048/LICENSE b/v_windows/v/old/examples/2048/LICENSE new file mode 100644 index 0000000..7897d4d --- /dev/null +++ b/v_windows/v/old/examples/2048/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Delyan Angelov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/v_windows/v/old/examples/2048/README.md b/v_windows/v/old/examples/2048/README.md new file mode 100644 index 0000000..15516fb --- /dev/null +++ b/v_windows/v/old/examples/2048/README.md @@ -0,0 +1,25 @@ +# V 2048 + +This is a simple 2048 game, written in [the V programming language](https://vlang.io/). + +WebAssembly demo: https://v2048.vercel.app + +![screenshot](demo.png) + +## Description: +Merge tiles by moving them. +After each move, a new random tile is added (2 or 4). +The goal of the game is to create a tile with a value of 2048. + +## Keys: +Escape - exit the game +Backspace - undo last move +n - restart the game +t - toggle the UI theme +Enter - toggle the tile text format + +UP,LEFT,DOWN,RIGHT / W,A,S,D / touchscreen swipes - move the tiles + +## Running instructions: +Compile & run the game with `./v run examples/2048` + diff --git a/v_windows/v/old/examples/2048/demo.png b/v_windows/v/old/examples/2048/demo.png new file mode 100644 index 0000000..fcd4684 Binary files /dev/null and b/v_windows/v/old/examples/2048/demo.png differ diff --git a/v_windows/v/old/examples/2048/v.mod b/v_windows/v/old/examples/2048/v.mod new file mode 100644 index 0000000..5c146f8 --- /dev/null +++ b/v_windows/v/old/examples/2048/v.mod @@ -0,0 +1,7 @@ +Module { + name: 'v2048', + description: 'A simple 2048 game written in V.', + version: '0.0.2', + repo_url: 'https://github.com/spytheman/v2048', + dependencies: [] +} diff --git a/v_windows/v/old/examples/asm.v b/v_windows/v/old/examples/asm.v new file mode 100644 index 0000000..88c75ec --- /dev/null +++ b/v_windows/v/old/examples/asm.v @@ -0,0 +1,18 @@ +fn main() { + a := 100 + b := 20 + mut c := 0 + $if amd64 { + asm amd64 { + mov eax, a + add eax, b + mov c, eax + ; =r (c) // output + ; r (a) // input + r (b) + } + } + println('a: $a') // 100 + println('b: $b') // 20 + println('c: $c') // 120 +} diff --git a/v_windows/v/old/examples/assets/fonts/LICENSE b/v_windows/v/old/examples/assets/fonts/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/v_windows/v/old/examples/assets/fonts/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/v_windows/v/old/examples/assets/fonts/RobotoMono-Regular.ttf b/v_windows/v/old/examples/assets/fonts/RobotoMono-Regular.ttf new file mode 100644 index 0000000..b158a33 Binary files /dev/null and b/v_windows/v/old/examples/assets/fonts/RobotoMono-Regular.ttf differ diff --git a/v_windows/v/old/examples/bfs.v b/v_windows/v/old/examples/bfs.v new file mode 100644 index 0000000..b92763e --- /dev/null +++ b/v_windows/v/old/examples/bfs.v @@ -0,0 +1,41 @@ +// Breadth-First Search (BFS) allows you to find the shortest distance between two nodes in the graph. +fn breadth_first_search_path(graph map[string][]string, vertex string, target string) []string { + mut path := []string{} + mut visited := []string{init: vertex} + mut queue := [][][]string{} + queue << [[vertex], path] + for queue.len > 0 { + mut idx := queue.len - 1 + node := queue[idx][0][0] + path = queue[idx][1] + queue.delete(idx) + if node == target { + path << node + return path + } + for child in graph[node] { + mut tmp := path.clone() + if child !in visited { + visited << child + tmp << node + queue << [[child], tmp] + } + } + } + return path +} + +fn main() { + graph := map{ + 'A': ['B', 'C'] + 'B': ['A', 'D', 'E'] + 'C': ['A', 'F'] + 'D': ['B'] + 'E': ['B', 'F'] + 'F': ['C', 'E'] + } + println('Graph: $graph') + path := breadth_first_search_path(graph, 'A', 'F') + println('The shortest path from node A to node F is: $path') + assert path == ['A', 'C', 'F'] +} diff --git a/v_windows/v/old/examples/binary_search_tree.v b/v_windows/v/old/examples/binary_search_tree.v new file mode 100644 index 0000000..514426a --- /dev/null +++ b/v_windows/v/old/examples/binary_search_tree.v @@ -0,0 +1,171 @@ +// Binary Search Tree example by @SleepyRoy + +// TODO: make Node.value generic once it's robust enough +struct Empty {} + +struct Node { + value f64 + left Tree + right Tree +} + +type Tree = Empty | Node + +// return size(number of nodes) of BST +fn size(tree Tree) int { + return match tree { + Empty { 0 } + Node { 1 + size(tree.left) + size(tree.right) } + } +} + +// insert a value to BST +fn insert(tree Tree, x f64) Tree { + return match tree { + Empty { + Node{x, tree, tree} + } + Node { + if x == tree.value { + tree + } else if x < tree.value { + Node{ + ...tree + left: insert(tree.left, x) + } + } else { + Node{ + ...tree + right: insert(tree.right, x) + } + } + } + } +} + +// whether able to find a value in BST +fn search(tree Tree, x f64) bool { + return match tree { + Empty { + false + } + Node { + if x == tree.value { + true + } else if x < tree.value { + search(tree.left, x) + } else { + search(tree.right, x) + } + } + } +} + +// find the minimal value of a BST +fn min(tree Tree) f64 { + return match tree { + Empty { + 1e100 + } + Node { + if tree.value < min(tree.left) { + tree.value + } else { + min(tree.left) + } + } + } +} + +// delete a value in BST (if nonexistant do nothing) +fn delete(tree Tree, x f64) Tree { + return match tree { + Empty { + tree + } + Node { + if tree.left is Node && tree.right is Node { + if x < tree.value { + Node{ + ...tree + left: delete(tree.left, x) + } + } else if x > tree.value { + Node{ + ...tree + right: delete(tree.right, x) + } + } else { + Node{ + ...tree + value: min(tree.right) + right: delete(tree.right, min(tree.right)) + } + } + } else if tree.left is Node { + if x == tree.value { + tree.left + } else { + Node{ + ...tree + left: delete(tree.left, x) + } + } + } else { + if x == tree.value { + tree.right + } else { + Node{ + ...tree + right: delete(tree.right, x) + } + } + } + } + } +} + +fn main() { + $if !freestanding { + mut tree := Tree(Empty{}) + input := [0.3, 0.2, 0.5, 0.0, 0.6, 0.8, 0.9, 1.0, 0.1, 0.4, 0.7] + for i in input { + tree = insert(tree, i) + } + println('[1] after insertion tree size is ${size(tree)}') // 11 + del := [-0.3, 0.0, 0.3, 0.6, 1.0, 1.5] + for i in del { + tree = delete(tree, i) + } + print('[2] after deletion tree size is ${size(tree)}, ') // 7 + print('and these elements were deleted: ') // 0.0 0.3 0.6 1.0 + for i in input { + if !search(tree, i) { + print('$i ') + } + } + println('') + } $else { + mut tree := Tree(Empty{}) + input := [0.3, 0.2, 0.5, 0.0, 0.6, 0.8, 0.9, 1.0, 0.1, 0.4, 0.7] + for i in input { + tree = insert(tree, i) + } + print('[1] after insertion tree size is ') // 11 + println(size(tree)) + del := [-0.3, 0.0, 0.3, 0.6, 1.0, 1.5] + for i in del { + tree = delete(tree, i) + } + print('[2] after deletion tree size is ') // 7 + print(size(tree)) + print(', and these elements were deleted: ') // 0.0 0.3 0.6 1.0 + for i in input { + if !search(tree, i) { + print(i) + print(' ') + } + } + println('') + } +} diff --git a/v_windows/v/old/examples/buf_reader.v b/v_windows/v/old/examples/buf_reader.v new file mode 100644 index 0000000..e754f61 --- /dev/null +++ b/v_windows/v/old/examples/buf_reader.v @@ -0,0 +1,19 @@ +// Simple raw HTTP head request +import net +import time +import io + +fn main() { + // Make a new connection + mut conn := net.dial_tcp('google.com:80') ? + // Simple http HEAD request for a file + conn.write_string('GET /index.html HTTP/1.0\r\n\r\n') ? + // Wrap in a buffered reader + mut r := io.new_buffered_reader(reader: conn) + for { + l := r.read_line() or { break } + println('$l') + // Make it nice and obvious that we are doing this line by line + time.sleep(100 * time.millisecond) + } +} diff --git a/v_windows/v/old/examples/c_interop_wkhtmltopdf.v b/v_windows/v/old/examples/c_interop_wkhtmltopdf.v new file mode 100644 index 0000000..ddd56b5 --- /dev/null +++ b/v_windows/v/old/examples/c_interop_wkhtmltopdf.v @@ -0,0 +1,96 @@ +import os + +// Example of C interop for a very handy task. +// +// wkhtmltopdf and wkhtmltoimage are open source (LGPLv3) command line tools to +// render HTML into PDF and various image formats using the Qt WebKit rendering +// engine. These run entirely "headless" and do not require a display or display +// service. +// +// https://github.com/wkhtmltopdf/wkhtmltopdf +// https://wkhtmltopdf.org/downloads.html +// https://wkhtmltopdf.org/libwkhtmltox/ +#flag -lwkhtmltox +#include "wkhtmltox/pdf.h" # You can install the C package for your system from the wkhtmltopdf.org/downloads.html page + +struct C.wkhtmltopdf_global_settings {} + +struct C.wkhtmltopdf_object_settings {} + +struct C.wkhtmltopdf_converter {} + +fn C.wkhtmltopdf_init(use_graphics bool) int + +fn C.wkhtmltopdf_deinit() int + +fn C.wkhtmltopdf_version() &char + +fn C.wkhtmltopdf_create_global_settings() &C.wkhtmltopdf_global_settings + +fn C.wkhtmltopdf_destroy_global_settings(global_settings &C.wkhtmltopdf_global_settings) + +fn wkhtmltopdf_set_global_setting(global_settings &C.wkhtmltopdf_global_settings, name &char, value &char) bool + +fn C.wkhtmltopdf_create_object_settings() &C.wkhtmltopdf_object_settings + +fn C.wkhtmltopdf_destroy_object_settings(object_settings &C.wkhtmltopdf_object_settings) + +fn C.wkhtmltopdf_set_object_setting(object_settings &C.wkhtmltopdf_object_settings, name &char, value &char) bool + +fn C.wkhtmltopdf_create_converter(global_settings &C.wkhtmltopdf_global_settings) &C.wkhtmltopdf_converter + +fn C.wkhtmltopdf_destroy_converter(converter &C.wkhtmltopdf_converter) + +fn C.wkhtmltopdf_add_object(converter &C.wkhtmltopdf_converter, object_settings &C.wkhtmltopdf_object_settings, data &char) + +fn C.wkhtmltopdf_convert(converter &C.wkhtmltopdf_converter) bool + +fn C.wkhtmltopdf_http_error_code(converter &C.wkhtmltopdf_converter) int + +fn C.wkhtmltopdf_get_output(converter &C.wkhtmltopdf_converter, data &&char) int + +fn main() { + // init + init := C.wkhtmltopdf_init(0) + println('wkhtmltopdf_init: $init') + version := unsafe { cstring_to_vstring(&char(C.wkhtmltopdf_version())) } + println('wkhtmltopdf_version: $version') + global_settings := C.wkhtmltopdf_create_global_settings() + println('wkhtmltopdf_create_global_settings: ${voidptr(global_settings)}') + object_settings := C.wkhtmltopdf_create_object_settings() + println('wkhtmltopdf_create_object_settings') + converter := C.wkhtmltopdf_create_converter(global_settings) + println('wkhtmltopdf_create_converter: ${voidptr(converter)}') + // convert + mut result := C.wkhtmltopdf_set_object_setting(object_settings, c'page', c'http://www.google.com.br') + println('wkhtmltopdf_set_object_setting: $result [page = http://www.google.com.br]') + C.wkhtmltopdf_add_object(converter, object_settings, 0) + println('wkhtmltopdf_add_object') + result = C.wkhtmltopdf_convert(converter) + println('wkhtmltopdf_convert: $result') + error_code := C.wkhtmltopdf_http_error_code(converter) + println('wkhtmltopdf_http_error_code: $error_code') + if result { + pdata := &char(0) + ppdata := &pdata + size := C.wkhtmltopdf_get_output(converter, voidptr(ppdata)) + println('wkhtmltopdf_get_output: $size bytes') + mut file := os.open_file('./google.pdf', 'w+', 0o666) or { + println('ERR: $err') + return + } + wrote := unsafe { file.write_ptr(pdata, size) } + println('write_bytes: $wrote [./google.pdf]') + file.flush() + file.close() + } + // destroy + C.wkhtmltopdf_destroy_converter(converter) + println('wkhtmltopdf_destroy_converter') + C.wkhtmltopdf_destroy_object_settings(object_settings) + println('wkhtmltopdf_destroy_object_settings: ${voidptr(object_settings)}') + C.wkhtmltopdf_destroy_global_settings(global_settings) + println('wkhtmltopdf_destroy_global_settings') + deinit := C.wkhtmltopdf_deinit() + println('wkhtmltopdf_deinit: $deinit') +} diff --git a/v_windows/v/old/examples/cli.v b/v_windows/v/old/examples/cli.v new file mode 100644 index 0000000..9232aac --- /dev/null +++ b/v_windows/v/old/examples/cli.v @@ -0,0 +1,78 @@ +module main + +import cli { Command, Flag } +import os + +fn main() { + mut cmd := Command{ + name: 'cli' + description: 'An example of the cli library.' + version: '1.0.0' + } + mut greet_cmd := Command{ + name: 'greet' + description: 'Prints greeting in different languages.' + usage: '' + required_args: 1 + pre_execute: greet_pre_func + execute: greet_func + post_execute: greet_post_func + } + greet_cmd.add_flag(Flag{ + flag: .string + required: true + name: 'language' + abbrev: 'l' + description: 'Language of the message.' + }) + greet_cmd.add_flag(Flag{ + flag: .int + name: 'times' + default_value: ['3'] + description: 'Number of times the message gets printed.' + }) + greet_cmd.add_flag(Flag{ + flag: .string_array + name: 'fun' + description: 'Just a dumby flags to show multiple.' + }) + cmd.add_command(greet_cmd) + cmd.setup() + cmd.parse(os.args) +} + +fn greet_func(cmd Command) ? { + language := cmd.flags.get_string('language') or { panic('Failed to get `language` flag: $err') } + times := cmd.flags.get_int('times') or { panic('Failed to get `times` flag: $err') } + name := cmd.args[0] + for _ in 0 .. times { + match language { + 'english', 'en' { + println('Welcome $name') + } + 'german', 'de' { + println('Willkommen $name') + } + 'dutch', 'nl' { + println('Welkom $name') + } + else { + println('Unsupported language') + println('Supported languages are `english`, `german` and `dutch`.') + break + } + } + } + fun := cmd.flags.get_strings('fun') or { panic('Failed to get `fun` flag: $err') } + for f in fun { + println('fun: $f') + } +} + +fn greet_pre_func(cmd Command) ? { + println('This is a function running before the main function.\n') +} + +fn greet_post_func(cmd Command) ? { + println('\nThis is a function running after the main function.') +} diff --git a/v_windows/v/old/examples/compiletime/compile-time-for.v b/v_windows/v/old/examples/compiletime/compile-time-for.v new file mode 100644 index 0000000..f3f90fa --- /dev/null +++ b/v_windows/v/old/examples/compiletime/compile-time-for.v @@ -0,0 +1,38 @@ +struct App {} + +fn (mut app App) method_one() {} + +fn (mut app App) method_two() int { + return 0 +} + +fn (mut app App) method_three(s string) string { + return s +} + +fn main() { + $for method in App.methods { + $if method.typ is fn (string) string { + println('$method.name IS `fn(string) string`') + } $else { + println('$method.name is NOT `fn(string) string`') + } + $if method.return_type !is int { + println('$method.name does NOT return `int`') + } $else { + println('$method.name DOES return `int`') + } + $if method.args[0].typ !is string { + println("$method.name's first arg is NOT `string`") + } $else { + println("$method.name's first arg IS `string`") + } + // TODO: Double inversion, should this even be allowed? + $if method.typ is fn () { + println('$method.name IS a void method') + } $else { + println('$method.name is NOT a void method') + } + println('') + } +} diff --git a/v_windows/v/old/examples/concurrency/concurrency.v b/v_windows/v/old/examples/concurrency/concurrency.v new file mode 100644 index 0000000..92a5517 --- /dev/null +++ b/v_windows/v/old/examples/concurrency/concurrency.v @@ -0,0 +1,18 @@ +import time + +// Simulate expensive computing using sleep function +fn expensive_computing(id int, duration int) { + println('Executing expensive computing task ($id)...') + time.sleep(duration * time.millisecond) + println('Finish task $id on $duration ms') +} + +fn main() { + mut threads := []thread{} + threads << go expensive_computing(1, 100) + threads << go expensive_computing(2, 500) + threads << go expensive_computing(3, 1000) + // Join all tasks + threads.wait() + println('All jobs finished!') +} diff --git a/v_windows/v/old/examples/concurrency/concurrency_http.v b/v_windows/v/old/examples/concurrency/concurrency_http.v new file mode 100644 index 0000000..b5b0b58 --- /dev/null +++ b/v_windows/v/old/examples/concurrency/concurrency_http.v @@ -0,0 +1,32 @@ +import net.http +import sync +import time + +fn vlang_time(mut wg sync.WaitGroup) ?string { + start := time.ticks() + data := http.get('https://vlang.io/utc_now') ? + finish := time.ticks() + println('Finish getting time ${finish - start} ms') + println(data.text) + wg.done() + return data.text +} + +fn remote_ip(mut wg sync.WaitGroup) ?string { + start := time.ticks() + data := http.get('https://api.ipify.org') ? + finish := time.ticks() + println('Finish getting ip ${finish - start} ms') + println(data.text) + wg.done() + return data.text +} + +fn main() { + mut wg := sync.new_waitgroup() + wg.add(2) + // Run tasks async + go vlang_time(mut wg) + go remote_ip(mut wg) + wg.wait() +} diff --git a/v_windows/v/old/examples/concurrency/concurrency_returns.v b/v_windows/v/old/examples/concurrency/concurrency_returns.v new file mode 100644 index 0000000..6d687e4 --- /dev/null +++ b/v_windows/v/old/examples/concurrency/concurrency_returns.v @@ -0,0 +1,13 @@ +fn expensive_computing(i int) int { + return i * i +} + +fn main() { + mut threads := []thread int{} + for i in 1 .. 10 { + threads << go expensive_computing(i) + } + // Join all tasks + r := threads.wait() + println('All jobs finished: $r') +} diff --git a/v_windows/v/old/examples/database/mysql.v b/v_windows/v/old/examples/database/mysql.v new file mode 100644 index 0000000..97e0888 --- /dev/null +++ b/v_windows/v/old/examples/database/mysql.v @@ -0,0 +1,17 @@ +import mysql + +fn main() { + mut conn := mysql.Connection{ + host: 'localhost' + port: 3306 + username: 'root' + password: '' + dbname: 'mysql' + } + conn.connect() ? + res := conn.query('show tables') ? + for row in res.rows() { + println(row.vals.join(', ')) + } + conn.close() +} diff --git a/v_windows/v/old/examples/database/orm.v b/v_windows/v/old/examples/database/orm.v new file mode 100644 index 0000000..a7db00c --- /dev/null +++ b/v_windows/v/old/examples/database/orm.v @@ -0,0 +1,260 @@ +import sqlite +import mysql +import pg + +[table: 'modules'] +struct Module { + id int [primary; sql: serial] + name string + nr_downloads int [sql: u64] + creator User +} + +struct User { + id int [primary; sql: serial] + age int [unique: 'user'] + name string [sql: 'username'; unique] + is_customer bool [sql: 'abc'; unique: 'user'] + skipped_string string [skip] +} + +struct Parent { + id int [primary; sql: serial] + name string + children []Child [fkey: 'parent_id'] +} + +struct Child { + id int [primary; sql: serial] + parent_id int + name string +} + +fn main() { + sqlite3_array() + mysql_array() + psql_array() + + sqlite3() + mysql() + psql() +} + +fn sqlite3_array() { + mut db := sqlite.connect(':memory:') or { panic(err) } + sql db { + create table Parent + } + + par := Parent{ + name: 'test' + children: [ + Child{ + name: 'abc' + }, + Child{ + name: 'def' + }, + ] + } + + sql db { + insert par into Parent + } + + parent := sql db { + select from Parent where id == 1 + } + + sql db { + drop table Child + drop table Parent + } + + eprintln(parent) +} + +fn mysql_array() { + mut db := mysql.Connection{ + host: 'localhost' + port: 3306 + username: 'root' + password: 'abc' + dbname: 'test' + } + db.connect() or { panic(err) } + + sql db { + create table Parent + } + + par := Parent{ + name: 'test' + children: [ + Child{ + name: 'abc' + }, + Child{ + name: 'def' + }, + ] + } + + sql db { + insert par into Parent + } + + parent := sql db { + select from Parent where id == 1 + } + + eprintln(parent) + + sql db { + drop table Child + drop table Parent + } + + db.close() +} + +fn psql_array() { + mut db := pg.connect(host: 'localhost', user: 'test', password: 'abc', dbname: 'test') or { + panic(err) + } + + sql db { + create table Parent + } + + par := Parent{ + name: 'test' + children: [ + Child{ + name: 'abc' + }, + Child{ + name: 'def' + }, + ] + } + + sql db { + insert par into Parent + } + + parent := sql db { + select from Parent where id == 1 + } + + eprintln(parent) + + sql db { + drop table Child + drop table Parent + } + + db.close() +} + +fn sqlite3() { + mut db := sqlite.connect(':memory:') or { panic(err) } + sql db { + create table Module + } + + mod := Module{ + name: 'test' + nr_downloads: 10 + creator: User{ + age: 21 + name: 'VUser' + is_customer: true + } + } + sql db { + insert mod into Module + } + + modul := sql db { + select from Module where id == 1 + } + + sql db { + drop table Module + } + + eprintln(modul) + db.close() or { panic(err) } +} + +fn mysql() { + mut conn := mysql.Connection{ + host: 'localhost' + port: 3306 + username: 'root' + password: 'abc' + dbname: 'test' + } + conn.connect() or { panic(err) } + + sql conn { + create table Module + } + + mod := Module{ + name: 'test' + nr_downloads: 10 + creator: User{ + age: 21 + name: 'VUser' + is_customer: true + } + } + + sql conn { + insert mod into Module + } + + m := sql conn { + select from Module where id == 1 + } + + eprintln(m) + conn.close() +} + +fn psql() { + mut db := pg.connect(host: 'localhost', user: 'test', password: 'abc', dbname: 'test') or { + panic(err) + } + + mod := Module{ + name: 'test' + nr_downloads: 10 + creator: User{ + age: 21 + name: 'VUser' + is_customer: true + } + } + + sql db { + create table Module + } + + sql db { + insert mod into Module + } + + modul := sql db { + select from Module where id == 1 + } + + sql db { + drop table Module + } + + eprintln(modul) + db.close() +} diff --git a/v_windows/v/old/examples/database/psql/.gitignore b/v_windows/v/old/examples/database/psql/.gitignore new file mode 100644 index 0000000..23830aa --- /dev/null +++ b/v_windows/v/old/examples/database/psql/.gitignore @@ -0,0 +1 @@ +customer diff --git a/v_windows/v/old/examples/database/psql/customer.v b/v_windows/v/old/examples/database/psql/customer.v new file mode 100644 index 0000000..4538a94 --- /dev/null +++ b/v_windows/v/old/examples/database/psql/customer.v @@ -0,0 +1,63 @@ +module main + +import pg + +const dash = '----------------------------------------------------------------' + +struct Customer { + id int + name string + nr_orders int + country string +} + +fn main() { + /* + db := pg.connect(pg.Config{ + host: 'localhost' //'127.0.0.1' + user: 'postgres' + dbname: 'customerdb' + }) or { + println('failed to connect') + println(err) + return + } + + nr_customers := db.select count from Customer + println('Total customers: $nr_customers') + + // V syntax can be used to build queries + println(dash) + bg_country := 'Bulgaria' + bg_customers := db.select from Customer where country == bg_country && id != 2 + for customer in bg_customers { + println('$customer.country | $customer.id - $customer.name') + } + + println(dash) + ru_customers := db.select from Customer where country == 'Russia' + for customer in ru_customers { + println('$customer.country | $customer.id - $customer.name') + } + + // by adding `limit 1` we tell V that there will be only one object + println(dash) + existing := db.select from Customer where id == 1 limit 1 or { panic(err) } + println('Existing customer name: $existing.name') + println('Existing customer full information:') + println(existing) + + println(dash) + q := Customer{} + // It's easy to handle queries that don't return any data + if anon := db.select from Customer where id == 12345 && name == q.name && + nr_orders > q.nr_orders limit 1 { + println('Non existing customer name: $anon.name') + } + // Insert a new customer + nc := Customer{ + name: 'John Doe' + nr_orders: 10 + } + db.insert(nc)*/ +} diff --git a/v_windows/v/old/examples/database/psql/mydb.sql b/v_windows/v/old/examples/database/psql/mydb.sql new file mode 100644 index 0000000..a7cbb39 --- /dev/null +++ b/v_windows/v/old/examples/database/psql/mydb.sql @@ -0,0 +1,122 @@ +-- +-- PostgreSQL database dump +-- + +-- Dumped from database version 9.5.19 +-- Dumped by pg_dump version 9.5.19 + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); +SET check_function_bodies = false; +SET xmloption = content; +SET client_min_messages = warning; +SET row_security = off; + +-- +-- Name: plpgsql; Type: EXTENSION; Schema: -; Owner: +-- + +CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog; + + +-- +-- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner: +-- + +COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language'; + + +SET default_tablespace = ''; + +SET default_with_oids = false; + +-- +-- Name: customers; Type: TABLE; Schema: public; Owner: myuser +-- + +CREATE TABLE public.customers ( + id integer NOT NULL, + name text DEFAULT ''::text, + nr_orders integer DEFAULT 0, + country text DEFAULT 'England'::text, + created_at timestamp without time zone DEFAULT now() +); + + +ALTER TABLE public.customers OWNER TO myuser; + +-- +-- Name: customers_id_seq; Type: SEQUENCE; Schema: public; Owner: myuser +-- + +CREATE SEQUENCE public.customers_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE public.customers_id_seq OWNER TO myuser; + +-- +-- Name: customers_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: myuser +-- + +ALTER SEQUENCE public.customers_id_seq OWNED BY public.customers.id; + + +-- +-- Name: id; Type: DEFAULT; Schema: public; Owner: myuser +-- + +ALTER TABLE ONLY public.customers ALTER COLUMN id SET DEFAULT nextval('public.customers_id_seq'::regclass); + + +-- +-- Data for Name: customers; Type: TABLE DATA; Schema: public; Owner: myuser +-- + +COPY public.customers (id, name, nr_orders, country, created_at) FROM stdin; +2 Pippi Långstrump 3 Bulgaria 2019-08-19 09:41:30.78888 +1 Bilbo Begins 11 Bulgaria 2019-08-19 09:40:31.396807 +3 Viktualia Rullgardina 0 Bulgaria 2019-08-19 09:42:52.723223 +4 Krusmynta Efraimsdotter 5 Bulgaria 2019-08-19 09:43:04.083209 +5 Ana Karenina 0 Russia 2019-08-20 15:41:50.244971 +7 Jiji Lolobridgida 0 Italy 2019-08-20 15:42:26.020113 +6 Viktor Savashkin 8 Russia 2019-08-20 15:42:07.213557 +\. + + +-- +-- Name: customers_id_seq; Type: SEQUENCE SET; Schema: public; Owner: myuser +-- + +SELECT pg_catalog.setval('public.customers_id_seq', 1, true); + + +-- +-- Name: customers_pkey; Type: CONSTRAINT; Schema: public; Owner: myuser +-- + +ALTER TABLE ONLY public.customers + ADD CONSTRAINT customers_pkey PRIMARY KEY (id); + + +-- +-- Name: SCHEMA public; Type: ACL; Schema: -; Owner: postgres +-- + +REVOKE ALL ON SCHEMA public FROM PUBLIC; +REVOKE ALL ON SCHEMA public FROM postgres; +GRANT ALL ON SCHEMA public TO postgres; +GRANT ALL ON SCHEMA public TO PUBLIC; + + +-- +-- PostgreSQL database dump complete +-- + diff --git a/v_windows/v/old/examples/database/sqlite.v b/v_windows/v/old/examples/database/sqlite.v new file mode 100644 index 0000000..a3c7176 --- /dev/null +++ b/v_windows/v/old/examples/database/sqlite.v @@ -0,0 +1,22 @@ +import sqlite + +fn main() { + db := sqlite.connect(':memory:') ? + db.exec("create table users (id integer primary key, name text default '');") + + db.exec("insert into users (name) values ('Sam')") + db.exec("insert into users (name) values ('Peter')") + db.exec("insert into users (name) values ('Kate')") + + nr_users := db.q_int('select count(*) from users') + println('nr users = $nr_users') + + name := db.q_string('select name from users where id = 1') + assert name == 'Sam' + + users, code := db.exec('select * from users') + println('SQL Result code: $code') + for row in users { + println(row.vals) + } +} diff --git a/v_windows/v/old/examples/dump_factorial.v b/v_windows/v/old/examples/dump_factorial.v new file mode 100644 index 0000000..9b5fc0f --- /dev/null +++ b/v_windows/v/old/examples/dump_factorial.v @@ -0,0 +1,10 @@ +fn factorial(n u32) u32 { + if dump(n <= 1) { + return dump(1) + } + return dump(n * factorial(n - 1)) +} + +fn main() { + println(factorial(5)) +} diff --git a/v_windows/v/old/examples/dynamic_library_loading/modules/library/library.v b/v_windows/v/old/examples/dynamic_library_loading/modules/library/library.v new file mode 100644 index 0000000..ed3658c --- /dev/null +++ b/v_windows/v/old/examples/dynamic_library_loading/modules/library/library.v @@ -0,0 +1,19 @@ +module library + +// add_1 is exported with the C name `add_1`. +// It can be called by external programs, when the module is compiled +// as a shared library. +// It is exported, because the function is declared as public with `pub`. +// The exported C name is `add_1`, because of the export: tag. +// (Normally, the exported name is a V mangled version based on the module +// name followed by __, followed by the fn name, i.e. it would have been +// `library__add_1`, if not for the export: tag). +[export: 'add_1'] +pub fn add_1(x int, y int) int { + return my_private_function(x + y) +} + +// this function is not exported and will not be visible to external programs. +fn my_private_function(x int) int { + return 1 + x +} diff --git a/v_windows/v/old/examples/dynamic_library_loading/use.v b/v_windows/v/old/examples/dynamic_library_loading/use.v new file mode 100644 index 0000000..71a0099 --- /dev/null +++ b/v_windows/v/old/examples/dynamic_library_loading/use.v @@ -0,0 +1,17 @@ +module main + +import os +import dl + +type FNAdder = fn (int, int) int + +fn main() { + library_file_path := os.join_path(os.getwd(), dl.get_libname('library')) + handle := dl.open_opt(library_file_path, dl.rtld_lazy) ? + eprintln('handle: ${ptr_str(handle)}') + mut f := FNAdder(0) + f = dl.sym_opt(handle, 'add_1') ? + eprintln('f: ${ptr_str(f)}') + res := f(1, 2) + eprintln('res: $res') +} diff --git a/v_windows/v/old/examples/dynamic_library_loading/use_test.v b/v_windows/v/old/examples/dynamic_library_loading/use_test.v new file mode 100644 index 0000000..9a93d66 --- /dev/null +++ b/v_windows/v/old/examples/dynamic_library_loading/use_test.v @@ -0,0 +1,56 @@ +module main + +import os +import dl + +const ( + vexe = os.real_path(os.getenv('VEXE')) + cfolder = os.dir(@FILE) + so_ext = dl.dl_ext + library_file_name = os.join_path(cfolder, dl.get_libname('library')) +) + +fn test_vexe() { + // dump(vexe) + assert vexe != '' + // dump(os.executable()) + // dump(@FILE) + // dump(cfolder) + // dump(so_ext) + // dump(library_file_name) +} + +fn test_can_compile_library() { + os.chdir(cfolder) + os.rm(library_file_name) or {} + v_compile('-d no_backtrace -o library -shared modules/library/library.v') + assert os.is_file(library_file_name) +} + +fn test_can_compile_main_program() { + os.chdir(cfolder) + assert os.is_file(library_file_name) + result := v_compile('run use.v') + // dump(result) + assert result.output.contains('res: 4') + os.rm(library_file_name) or {} +} + +fn test_can_compile_and_use_library_with_skip_unused() { + os.chdir(cfolder) + os.rm(library_file_name) or {} + v_compile('-skip-unused -d no_backtrace -o library -shared modules/library/library.v') + assert os.is_file(library_file_name) + result := v_compile('run use.v') + assert result.output.contains('res: 4') + os.rm(library_file_name) or {} +} + +fn v_compile(vopts string) os.Result { + cmd := '"$vexe" -showcc $vopts' + // dump(cmd) + res := os.execute_or_exit(cmd) + // dump(res) + assert res.exit_code == 0 + return res +} diff --git a/v_windows/v/old/examples/errors.v b/v_windows/v/old/examples/errors.v new file mode 100644 index 0000000..e763fcf --- /dev/null +++ b/v_windows/v/old/examples/errors.v @@ -0,0 +1,20 @@ +import semver + +fn main() { + semver.from('asd') or { check_error(err) } + semver.from('') or { check_error(err) } +} + +fn check_error(err IError) { + match err { + semver.InvalidVersionFormatError { + println('wrong format') + } + semver.EmptyInputError { + println('empty input') + } + else { + println('unknown error') + } + } +} diff --git a/v_windows/v/old/examples/eventbus/eventbus.v b/v_windows/v/old/examples/eventbus/eventbus.v new file mode 100644 index 0000000..042feb8 --- /dev/null +++ b/v_windows/v/old/examples/eventbus/eventbus.v @@ -0,0 +1,13 @@ +module main + +import some_module + +fn main() { + mut sub := some_module.get_subscriber() + sub.subscribe('error', on_error) + some_module.do_work() +} + +fn on_error(sender voidptr, e &some_module.MyError, x voidptr) { + println(e.message) +} diff --git a/v_windows/v/old/examples/eventbus/modules/some_module/some_module.v b/v_windows/v/old/examples/eventbus/modules/some_module/some_module.v new file mode 100644 index 0000000..01231fb --- /dev/null +++ b/v_windows/v/old/examples/eventbus/modules/some_module/some_module.v @@ -0,0 +1,34 @@ +module some_module + +import eventbus + +const ( + eb = eventbus.new() +) + +pub struct Work { +pub: + hours int +} + +pub struct MyError { +pub: + message string +} + +pub fn do_work() { + work := Work{20} + for i in 0 .. 20 { + println('working...') + if i == 15 { + error := &MyError{'There was an error.'} + some_module.eb.publish('error', work, error) + some_module.eb.publish('error', work, error) + return + } + } +} + +pub fn get_subscriber() eventbus.Subscriber { + return *some_module.eb.subscriber +} diff --git a/v_windows/v/old/examples/fetch.v b/v_windows/v/old/examples/fetch.v new file mode 100644 index 0000000..e1a7132 --- /dev/null +++ b/v_windows/v/old/examples/fetch.v @@ -0,0 +1,12 @@ +import time +import net.http + +fn main() { + resp := http.get('https://vlang.io/utc_now') or { + println('failed to fetch data from the server') + return + } + + t := time.unix(resp.text.int()) + println(t.format()) +} diff --git a/v_windows/v/old/examples/fibonacci.v b/v_windows/v/old/examples/fibonacci.v new file mode 100644 index 0000000..13056d2 --- /dev/null +++ b/v_windows/v/old/examples/fibonacci.v @@ -0,0 +1,37 @@ +// This program displays the fibonacci sequence +// import os + +fn main() { + // Check for user input + // if os.args.len != 2 { + // println('usage: fibonacci [rank]') + + // Exit + // return + // } + + // Parse first argument and cast it to int + // stop := os.args[1].int() + stop := 23 + // Can only calculate correctly until rank 92 + if stop > 92 { + println('rank must be 92 or less') + return + } + + // Three consecutive terms of the sequence + mut a := 0 + mut b := 0 + mut c := 1 + println(a + c + c) + for _ in 0 .. stop { + // Set a and b to the next term + a = b + b = c + // Compute the new term + c = a + b + + // Print the new term + println(c) + } +} diff --git a/v_windows/v/old/examples/file_list.v b/v_windows/v/old/examples/file_list.v new file mode 100644 index 0000000..b1dc295 --- /dev/null +++ b/v_windows/v/old/examples/file_list.v @@ -0,0 +1,18 @@ +import os + +fn main() { + files := os.ls('.') or { + println(err) + return + } + mut f := os.create('file_list.txt') or { + println(err) + return + } + for file in files { + if os.is_file(file) { + f.write_string(file + '\r\n') or { println(err) } + } + } + f.close() +} diff --git a/v_windows/v/old/examples/fireworks/fireworks.v b/v_windows/v/old/examples/fireworks/fireworks.v new file mode 100644 index 0000000..9581cc5 --- /dev/null +++ b/v_windows/v/old/examples/fireworks/fireworks.v @@ -0,0 +1,119 @@ +import os +import objects +import gg +import gx +import rand + +struct App { +mut: + gg &gg.Context = 0 + ui &objects.UIParams = 0 + rockets []objects.Rocket + frames [][]objects.Rocket + // i thought about using a fixed fifo queue for the frames but the array + // seemed to work fine, if you'd like a challenge try implementing it with the queue :) + draw_flag bool = true +} + +fn on_frame(mut app App) { + if !app.draw_flag { + return + } + app.gg.begin() + + // drawing previous frames + for mut frame in app.frames { + for mut rocket in frame { + if !rocket.exploded { + rocket.color.a = byte(f32_max(rocket.color.a - 8, 0)) + rocket.draw(mut app.gg) + } + } + } + + // chance of firing new rocket + if rand.intn(30) == 0 { + app.rockets << objects.new_rocket() + } + // simulating rockets + app.rockets = app.rockets.filter(!it.dead) + + for mut rocket in app.rockets { + rocket.tick(mut app.gg) + } + + // adding frame + mut frame := app.rockets.clone() + + for mut rocket in frame { + rocket.particles = [] + } + + app.frames << frame + + // trimming out frames + if app.frames.len > 30 { + app.frames.delete(0) + } + + app.gg.end() +} + +fn on_event(e &gg.Event, mut app App) { + match e.typ { + .resized, .resumed { + app.resize() + } + .iconified { + app.draw_flag = false + } + .restored { + app.draw_flag = true + app.resize() + } + else { + // println("Type ${e.typ}") + } + } +} + +fn (mut app App) resize() { + size := gg.window_size() + // avoid calls when minimized + if size.width < 2 && size.height < 2 { + return + } + mut s := gg.dpi_scale() + + if s == 0.0 { + s = 1.0 + } + app.ui.dpi_scale = s + app.ui.width = size.width + app.ui.height = size.height +} + +// is needed for easier diagnostics on windows +[console] +fn main() { + mut font_path := os.resource_abs_path(os.join_path('../assets/fonts/', 'RobotoMono-Regular.ttf')) + $if android { + font_path = 'fonts/RobotoMono-Regular.ttf' + } + + mut app := &App{} + app.ui = objects.get_params() + + app.gg = gg.new_context( + width: app.ui.width + height: app.ui.height + window_title: 'Fireworks!' + bg_color: gx.black + user_data: app + frame_fn: on_frame + event_fn: on_event + font_path: font_path + ) + + app.gg.run() +} diff --git a/v_windows/v/old/examples/fireworks/modules/objects/color.v b/v_windows/v/old/examples/fireworks/modules/objects/color.v new file mode 100644 index 0000000..6761f26 --- /dev/null +++ b/v_windows/v/old/examples/fireworks/modules/objects/color.v @@ -0,0 +1,12 @@ +module objects + +import gx +import rand + +pub fn random_color() gx.Color { + return gx.Color{ + r: byte(rand.int_in_range(0, 256)) + g: byte(rand.int_in_range(0, 256)) + b: byte(rand.int_in_range(0, 256)) + } +} diff --git a/v_windows/v/old/examples/fireworks/modules/objects/constants.v b/v_windows/v/old/examples/fireworks/modules/objects/constants.v new file mode 100644 index 0000000..a8e5abd --- /dev/null +++ b/v_windows/v/old/examples/fireworks/modules/objects/constants.v @@ -0,0 +1,20 @@ +module objects + +pub struct UIParams { +pub mut: + dpi_scale f32 = 1.0 + width int = 800 + height int = 800 + gravity Vector = Vector{0, -0.03} + age_rate int = 1 + offspring_count int = 100 + rocket_radius int = 5 + particle_radius f32 = 2.5 + drag f32 = 0.98 +} + +const params = &UIParams{} + +pub fn get_params() &UIParams { + return objects.params +} diff --git a/v_windows/v/old/examples/fireworks/modules/objects/particle.v b/v_windows/v/old/examples/fireworks/modules/objects/particle.v new file mode 100644 index 0000000..8f84a39 --- /dev/null +++ b/v_windows/v/old/examples/fireworks/modules/objects/particle.v @@ -0,0 +1,36 @@ +module objects + +import gg +import gx + +pub struct Particle { +pub mut: + color gx.Color + pos Vector + vel Vector + accel Vector + lifespan f32 = 255 +} + +pub fn (particle Particle) draw(mut ctx gg.Context) { + ctx.draw_circle(particle.pos.x, get_params().height - particle.pos.y, get_params().particle_radius, + particle.color) +} + +pub fn (mut particle Particle) tick(mut rocket Rocket, mut ctx gg.Context) { + particle.lifespan -= get_params().age_rate + particle.color.a = byte(particle.lifespan) + + if particle.lifespan <= 0 { + rocket.dead = true + return + } + + particle.accel += get_params().gravity + particle.vel += particle.accel + particle.vel = particle.vel.mult(get_params().drag) + particle.pos += particle.vel + particle.draw(mut ctx) + + particle.accel = Vector{} +} diff --git a/v_windows/v/old/examples/fireworks/modules/objects/rocket.v b/v_windows/v/old/examples/fireworks/modules/objects/rocket.v new file mode 100644 index 0000000..ebdce0a --- /dev/null +++ b/v_windows/v/old/examples/fireworks/modules/objects/rocket.v @@ -0,0 +1,69 @@ +module objects + +import gg +import gx +import rand + +pub struct Rocket { +pub mut: + color gx.Color + pos Vector + vel Vector + accel Vector + exploded bool + particles []Particle + dead bool +} + +pub fn (rocket Rocket) draw(mut ctx gg.Context) { + ctx.draw_circle(rocket.pos.x, get_params().height - rocket.pos.y, get_params().rocket_radius, + rocket.color) +} + +pub fn (mut rocket Rocket) explode() { + rocket.exploded = true + + for _ in 0 .. get_params().offspring_count { + rocket.spawn_particle() + } +} + +pub fn (mut rocket Rocket) tick(mut ctx gg.Context) { + if !rocket.exploded { + if rocket.vel.y <= 1 { + rocket.explode() + } + + rocket.accel += get_params().gravity + rocket.vel += rocket.accel + rocket.pos += rocket.vel + rocket.draw(mut ctx) + + rocket.accel = Vector{} + } + + for mut particle in rocket.particles { + particle.tick(mut rocket, mut ctx) + } +} + +pub fn new_rocket() Rocket { + return Rocket{ + color: random_color() + pos: Vector{ + x: rand.f32_in_range(50, get_params().width - 50) + } + vel: Vector{ + x: rand.f32_in_range(-1.5, 1.5) + y: rand.f32_in_range(5, 7) + } + } +} + +pub fn (mut rocket Rocket) spawn_particle() { + rocket.particles << Particle{ + color: rocket.color + pos: rocket.pos + accel: random_vector_in_circle().mult(2) + } +} diff --git a/v_windows/v/old/examples/fireworks/modules/objects/vector.v b/v_windows/v/old/examples/fireworks/modules/objects/vector.v new file mode 100644 index 0000000..0e29283 --- /dev/null +++ b/v_windows/v/old/examples/fireworks/modules/objects/vector.v @@ -0,0 +1,28 @@ +module objects + +import math +import rand + +pub struct Vector { +pub mut: + x f32 + y f32 +} + +pub fn (a Vector) + (b Vector) Vector { + return Vector{a.x + b.x, a.y + b.y} +} + +pub fn (vector Vector) mult(scalar f32) Vector { + return Vector{vector.x * scalar, vector.y * scalar} +} + +pub fn random_vector_in_circle() Vector { + theta := rand.f32n(2 * math.pi) + y := rand.f32() + + return Vector{ + x: f32(y * math.sin(theta)) + y: f32(y * math.cos(theta)) + } +} diff --git a/v_windows/v/old/examples/fizz_buzz.v b/v_windows/v/old/examples/fizz_buzz.v new file mode 100644 index 0000000..3310460 --- /dev/null +++ b/v_windows/v/old/examples/fizz_buzz.v @@ -0,0 +1,17 @@ +fn main() { + mut s := '' + for n in 1 .. 101 { + if n % 3 == 0 { + s += 'Fizz' + } + if n % 5 == 0 { + s += 'Buzz' + } + if s == '' { + println(n) + } else { + println(s) + } + s = '' + } +} diff --git a/v_windows/v/old/examples/flappylearning/.gitignore b/v_windows/v/old/examples/flappylearning/.gitignore new file mode 100644 index 0000000..dc22e61 --- /dev/null +++ b/v_windows/v/old/examples/flappylearning/.gitignore @@ -0,0 +1 @@ +game diff --git a/v_windows/v/old/examples/flappylearning/LICENSE b/v_windows/v/old/examples/flappylearning/LICENSE new file mode 100644 index 0000000..87cfb6f --- /dev/null +++ b/v_windows/v/old/examples/flappylearning/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 uxnow + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/v_windows/v/old/examples/flappylearning/README.md b/v_windows/v/old/examples/flappylearning/README.md new file mode 100644 index 0000000..57e2889 --- /dev/null +++ b/v_windows/v/old/examples/flappylearning/README.md @@ -0,0 +1,16 @@ +# flappylearning-v +flappy learning implemented by vlang + +## get started + +```sh +v run game.v +``` + +![flappy.png](img/flappy.png) + +## thanks +https://github.com/xviniette/FlappyLearning + +## license +MIT diff --git a/v_windows/v/old/examples/flappylearning/assets/img/background.png b/v_windows/v/old/examples/flappylearning/assets/img/background.png new file mode 100644 index 0000000..22b3070 Binary files /dev/null and b/v_windows/v/old/examples/flappylearning/assets/img/background.png differ diff --git a/v_windows/v/old/examples/flappylearning/assets/img/bird.png b/v_windows/v/old/examples/flappylearning/assets/img/bird.png new file mode 100644 index 0000000..ca3a159 Binary files /dev/null and b/v_windows/v/old/examples/flappylearning/assets/img/bird.png differ diff --git a/v_windows/v/old/examples/flappylearning/assets/img/flappy.png b/v_windows/v/old/examples/flappylearning/assets/img/flappy.png new file mode 100644 index 0000000..c7085aa Binary files /dev/null and b/v_windows/v/old/examples/flappylearning/assets/img/flappy.png differ diff --git a/v_windows/v/old/examples/flappylearning/assets/img/pipebottom.png b/v_windows/v/old/examples/flappylearning/assets/img/pipebottom.png new file mode 100644 index 0000000..99d27b2 Binary files /dev/null and b/v_windows/v/old/examples/flappylearning/assets/img/pipebottom.png differ diff --git a/v_windows/v/old/examples/flappylearning/assets/img/pipetop.png b/v_windows/v/old/examples/flappylearning/assets/img/pipetop.png new file mode 100644 index 0000000..1fcd52b Binary files /dev/null and b/v_windows/v/old/examples/flappylearning/assets/img/pipetop.png differ diff --git a/v_windows/v/old/examples/flappylearning/game.v b/v_windows/v/old/examples/flappylearning/game.v new file mode 100644 index 0000000..15cdc28 --- /dev/null +++ b/v_windows/v/old/examples/flappylearning/game.v @@ -0,0 +1,291 @@ +module main + +import gg +import gx +import os +import time +import math +import rand +import neuroevolution + +const ( + win_width = 500 + win_height = 512 +) + +struct Bird { +mut: + x f64 = 80 + y f64 = 250 + width f64 = 40 + height f64 = 30 + alive bool = true + gravity f64 + velocity f64 = 0.3 + jump f64 = -6 +} + +fn (mut b Bird) flap() { + b.gravity = b.jump +} + +fn (mut b Bird) update() { + b.gravity += b.velocity + b.y += b.gravity +} + +fn (b Bird) is_dead(height f64, pipes []Pipe) bool { + if b.y >= height || b.y + b.height <= 0 { + return true + } + for pipe in pipes { + if !(b.x > pipe.x + pipe.width || b.x + b.width < pipe.x || b.y > pipe.y + pipe.height + || b.y + b.height < pipe.y) { + return true + } + } + return false +} + +struct Pipe { +mut: + x f64 = 80 + y f64 = 250 + width f64 = 40 + height f64 = 30 + speed f64 = 3 +} + +fn (mut p Pipe) update() { + p.x -= p.speed +} + +fn (p Pipe) is_out() bool { + return p.x + p.width < 0 +} + +struct App { +mut: + gg &gg.Context + background gg.Image + bird gg.Image + pipetop gg.Image + pipebottom gg.Image + pipes []Pipe + birds []Bird + score int + max_score int + width f64 = win_width + height f64 = win_height + spawn_interval f64 = 90 + interval f64 + nv neuroevolution.Generations + gen []neuroevolution.Network + alives int + generation int + background_speed f64 = 0.5 + background_x f64 + timer_period_ms int = 24 +} + +fn (mut app App) start() { + app.interval = 0 + app.score = 0 + app.pipes = [] + app.birds = [] + app.gen = app.nv.generate() + for _ in 0 .. app.gen.len { + app.birds << Bird{} + } + app.generation++ + app.alives = app.birds.len +} + +fn (app &App) is_it_end() bool { + for i in 0 .. app.birds.len { + if app.birds[i].alive { + return false + } + } + return true +} + +fn (mut app App) update() { + app.background_x += app.background_speed + mut next_holl := f64(0) + if app.birds.len > 0 { + for i := 0; i < app.pipes.len; i += 2 { + if app.pipes[i].x + app.pipes[i].width > app.birds[0].x { + next_holl = app.pipes[i].height / app.height + break + } + } + } + for j, mut bird in app.birds { + if bird.alive { + inputs := [ + bird.y / app.height, + next_holl, + ] + res := app.gen[j].compute(inputs) + if res[0] > 0.5 { + bird.flap() + } + bird.update() + if bird.is_dead(app.height, app.pipes) { + bird.alive = false + app.alives-- + app.nv.network_score(app.gen[j], app.score) + if app.is_it_end() { + app.start() + } + } + } + } + for k := 0; k < app.pipes.len; k++ { + app.pipes[k].update() + if app.pipes[k].is_out() { + app.pipes.delete(k) + k-- + } + } + if app.interval == 0 { + delta_bord := f64(50) + pipe_holl := f64(120) + holl_position := math.round(rand.f64() * (app.height - delta_bord * 2.0 - pipe_holl)) + + delta_bord + app.pipes << Pipe{ + x: app.width + y: 0 + height: holl_position + } + app.pipes << Pipe{ + x: app.width + y: holl_position + pipe_holl + height: app.height + } + } + app.interval++ + if app.interval == app.spawn_interval { + app.interval = 0 + } + app.score++ + app.max_score = if app.score > app.max_score { app.score } else { app.max_score } +} + +fn main() { + mut app := &App{ + gg: 0 + } + mut font_path := os.resource_abs_path(os.join_path('../assets/fonts/', 'RobotoMono-Regular.ttf')) + $if android { + font_path = 'fonts/RobotoMono-Regular.ttf' + } + app.gg = gg.new_context( + bg_color: gx.white + width: win_width + height: win_height + create_window: true + window_title: 'flappylearning-v' + frame_fn: frame + event_fn: on_event + user_data: app + init_fn: init_images + font_path: font_path + ) + app.nv = neuroevolution.Generations{ + population: 50 + network: [2, 2, 1] + } + app.start() + go app.run() + app.gg.run() +} + +fn (mut app App) run() { + for { + app.update() + time.sleep(app.timer_period_ms * time.millisecond) + } +} + +fn init_images(mut app App) { + $if android { + background := os.read_apk_asset('img/background.png') or { panic(err) } + app.background = app.gg.create_image_from_byte_array(background) + bird := os.read_apk_asset('img/bird.png') or { panic(err) } + app.bird = app.gg.create_image_from_byte_array(bird) + pipetop := os.read_apk_asset('img/pipetop.png') or { panic(err) } + app.pipetop = app.gg.create_image_from_byte_array(pipetop) + pipebottom := os.read_apk_asset('img/pipebottom.png') or { panic(err) } + app.pipebottom = app.gg.create_image_from_byte_array(pipebottom) + } $else { + app.background = app.gg.create_image(os.resource_abs_path('assets/img/background.png')) + app.bird = app.gg.create_image(os.resource_abs_path('assets/img/bird.png')) + app.pipetop = app.gg.create_image(os.resource_abs_path('assets/img/pipetop.png')) + app.pipebottom = app.gg.create_image(os.resource_abs_path('assets/img/pipebottom.png')) + } +} + +fn frame(app &App) { + app.gg.begin() + app.draw() + app.gg.end() +} + +fn (app &App) display() { + for i := 0; i < int(math.ceil(app.width / app.background.width) + 1.0); i++ { + background_x := i * app.background.width - math.floor(int(app.background_x) % int(app.background.width)) + app.gg.draw_image(f32(background_x), 0, app.background.width, app.background.height, + app.background) + } + for i, pipe in app.pipes { + if i % 2 == 0 { + app.gg.draw_image(f32(pipe.x), f32(pipe.y + pipe.height - app.pipetop.height), + app.pipetop.width, app.pipetop.height, app.pipetop) + } else { + app.gg.draw_image(f32(pipe.x), f32(pipe.y), app.pipebottom.width, app.pipebottom.height, + app.pipebottom) + } + } + for bird in app.birds { + if bird.alive { + app.gg.draw_image(f32(bird.x), f32(bird.y), app.bird.width, app.bird.height, + app.bird) + } + } + app.gg.draw_text_def(10, 25, 'Score: $app.score') + app.gg.draw_text_def(10, 50, 'Max Score: $app.max_score') + app.gg.draw_text_def(10, 75, 'Generation: $app.generation') + app.gg.draw_text_def(10, 100, 'Alive: $app.alives / $app.nv.population') +} + +fn (app &App) draw() { + app.display() +} + +fn on_event(e &gg.Event, mut app App) { + if e.typ == .key_down { + app.key_down(e.key_code) + } +} + +fn (mut app App) key_down(key gg.KeyCode) { + // global keys + match key { + .escape { + exit(0) + } + ._0 { + app.timer_period_ms = 0 + } + .space { + if app.timer_period_ms == 24 { + app.timer_period_ms = 4 + } else { + app.timer_period_ms = 24 + } + } + else {} + } +} diff --git a/v_windows/v/old/examples/flappylearning/modules/neuroevolution/neuronevolution.v b/v_windows/v/old/examples/flappylearning/modules/neuroevolution/neuronevolution.v new file mode 100644 index 0000000..f3839ee --- /dev/null +++ b/v_windows/v/old/examples/flappylearning/modules/neuroevolution/neuronevolution.v @@ -0,0 +1,287 @@ +module neuroevolution + +import rand +import math + +fn random_clamped() f64 { + return rand.f64() * 2 - 1 +} + +pub fn activation(a f64) f64 { + ap := (-a) / 1 + return (1 / (1 + math.exp(ap))) +} + +fn round(a int, b f64) int { + return int(math.round(f64(a) * b)) +} + +struct Neuron { +mut: + value f64 + weights []f64 +} + +fn (mut n Neuron) populate(nb int) { + for _ in 0 .. nb { + n.weights << random_clamped() + } +} + +struct Layer { + id int +mut: + neurons []Neuron +} + +fn (mut l Layer) populate(nb_neurons int, nb_inputs int) { + for _ in 0 .. nb_neurons { + mut n := Neuron{} + n.populate(nb_inputs) + l.neurons << n + } +} + +struct Network { +mut: + layers []Layer +} + +fn (mut n Network) populate(network []int) { + assert network.len >= 2 + input := network[0] + hiddens := network[1..network.len - 1] + output := network[network.len - 1] + mut index := 0 + mut previous_neurons := 0 + mut input_layer := Layer{ + id: index + } + input_layer.populate(input, previous_neurons) + n.layers << input_layer + previous_neurons = input + index++ + for hidden in hiddens { + mut hidden_layer := Layer{ + id: index + } + hidden_layer.populate(hidden, previous_neurons) + previous_neurons = hidden + n.layers << hidden_layer + index++ + } + mut output_layer := Layer{ + id: index + } + output_layer.populate(output, previous_neurons) + n.layers << output_layer +} + +fn (n Network) get_save() Save { + mut save := Save{} + for layer in n.layers { + save.neurons << layer.neurons.len + for neuron in layer.neurons { + for weight in neuron.weights { + save.weights << weight + } + } + } + return save +} + +fn (mut n Network) set_save(save Save) { + mut previous_neurons := 0 + mut index := 0 + mut index_weights := 0 + n.layers = [] + for save_neuron in save.neurons { + mut layer := Layer{ + id: index + } + layer.populate(save_neuron, previous_neurons) + for mut neuron in layer.neurons { + for i in 0 .. neuron.weights.len { + neuron.weights[i] = save.weights[index_weights] + index_weights++ + } + } + previous_neurons = save_neuron + index++ + n.layers << layer + } +} + +pub fn (mut n Network) compute(inputs []f64) []f64 { + assert n.layers.len > 0 + assert inputs.len == n.layers[0].neurons.len + for i, input in inputs { + n.layers[0].neurons[i].value = input + } + mut prev_layer := n.layers[0] + for i in 1 .. n.layers.len { + for j, neuron in n.layers[i].neurons { + mut sum := f64(0) + for k, prev_layer_neuron in prev_layer.neurons { + sum += prev_layer_neuron.value * neuron.weights[k] + } + n.layers[i].neurons[j].value = activation(sum) + } + prev_layer = n.layers[i] + } + mut outputs := []f64{} + mut last_layer := n.layers[n.layers.len - 1] + for neuron in last_layer.neurons { + outputs << neuron.value + } + return outputs +} + +struct Save { +mut: + neurons []int + weights []f64 +} + +fn (s Save) clone() Save { + mut save := Save{} + save.neurons << s.neurons + save.weights << s.weights + return save +} + +struct Genome { + score int + network Save +} + +struct Generation { +mut: + genomes []Genome +} + +fn (mut g Generation) add_genome(genome Genome) { + mut i := 0 + for gg in g.genomes { + if genome.score > gg.score { + break + } + i++ + } + g.genomes.insert(i, genome) +} + +fn (g1 Genome) breed(g2 Genome, nb_child int) []Save { + mut datas := []Save{} + for _ in 0 .. nb_child { + mut data := g1.network.clone() + for i, weight in g2.network.weights { + if rand.f64() <= 0.5 { + data.weights[i] = weight + } + } + for i, _ in data.weights { + if rand.f64() <= 0.1 { + data.weights[i] += (rand.f64() * 2 - 1) * 0.5 + } + } + datas << data + } + return datas +} + +fn (g Generation) next(population int) []Save { + mut nexts := []Save{} + if population == 0 { + return nexts + } + keep := round(population, 0.2) + for i in 0 .. keep { + if nexts.len < population { + nexts << g.genomes[i].network.clone() + } + } + random := round(population, 0.2) + for _ in 0 .. random { + if nexts.len < population { + mut n := g.genomes[0].network.clone() + for k, _ in n.weights { + n.weights[k] = random_clamped() + } + nexts << n + } + } + mut max := 0 + out: for { + for i in 0 .. max { + mut childs := g.genomes[i].breed(g.genomes[max], 1) + for c in childs { + nexts << c + if nexts.len >= population { + break out + } + } + } + max++ + if max >= g.genomes.len - 1 { + max = 0 + } + } + return nexts +} + +pub struct Generations { +pub: + population int + network []int +mut: + generations []Generation +} + +fn (mut gs Generations) first() []Save { + mut out := []Save{} + for _ in 0 .. gs.population { + mut nn := Network{} + nn.populate(gs.network) + out << nn.get_save() + } + gs.generations << Generation{} + return out +} + +fn (mut gs Generations) next() []Save { + assert gs.generations.len > 0 + gen := gs.generations[gs.generations.len - 1].next(gs.population) + gs.generations << Generation{} + return gen +} + +fn (mut gs Generations) add_genome(genome Genome) { + assert gs.generations.len > 0 + gs.generations[gs.generations.len - 1].add_genome(genome) +} + +fn (mut gs Generations) restart() { + gs.generations = [] +} + +pub fn (mut gs Generations) generate() []Network { + saves := if gs.generations.len == 0 { gs.first() } else { gs.next() } + mut nns := []Network{} + for save in saves { + mut nn := Network{} + nn.set_save(save) + nns << nn + } + if gs.generations.len >= 2 { + gs.generations.delete(0) + } + return nns +} + +pub fn (mut gs Generations) network_score(network Network, score int) { + gs.add_genome(Genome{ + score: score + network: network.get_save() + }) +} diff --git a/v_windows/v/old/examples/function_types.v b/v_windows/v/old/examples/function_types.v new file mode 100644 index 0000000..fa924cb --- /dev/null +++ b/v_windows/v/old/examples/function_types.v @@ -0,0 +1,32 @@ +// Function signatures can be declared as types: + +type Filter = fn (string) string + +// Functions can accept function types as arguments: + +fn filter(s string, f Filter) string { + return f(s) +} + +// Declare a function with a matching signature: + +fn uppercase(s string) string { + return s.to_upper() +} + +fn main() { + // A function can be assigned to a matching type: + + my_filter := Filter(uppercase) + + // You don't strictly need the `Filter` cast - it's only used + // here to illustrate how these types are compatible. + + // All of the following prints "HELLO WORLD": + + println(filter('Hello world', my_filter)) + println(filter('Hello world', uppercase)) + println(filter('Hello world', fn (s string) string { + return s.to_upper() + })) +} diff --git a/v_windows/v/old/examples/game_of_life/README.md b/v_windows/v/old/examples/game_of_life/README.md new file mode 100644 index 0000000..02969ff --- /dev/null +++ b/v_windows/v/old/examples/game_of_life/README.md @@ -0,0 +1,10 @@ +# Conway's Game of Life + +![](https://github.com/fuyutarow/Conways-Game-of-Life-with-Vlang/raw/master/v-gun.gif) + + +``` +v run life.v +``` + +Created by fuyutarow: https://github.com/fuyutarow/Conways-Game-of-Life-with-Vlang diff --git a/v_windows/v/old/examples/game_of_life/life.v b/v_windows/v/old/examples/game_of_life/life.v new file mode 100644 index 0000000..0f3b7c7 --- /dev/null +++ b/v_windows/v/old/examples/game_of_life/life.v @@ -0,0 +1,25 @@ +module main + +import time +import automaton + +fn print_automaton(a &automaton.Automaton) { + for y := 1; y < a.field.maxy; y++ { + mut s := ' ' + for x := 1; x < a.field.maxx; x++ { + cell := a.field.get(x, y) + s += if cell == 1 { '@' } else { '.' } + } + println(s) + } + println('') +} + +fn main() { + mut a := automaton.gun() + for { + a.update() + print_automaton(a) + time.sleep(100 * time.millisecond) + } +} diff --git a/v_windows/v/old/examples/game_of_life/life_gg.v b/v_windows/v/old/examples/game_of_life/life_gg.v new file mode 100644 index 0000000..2f45017 --- /dev/null +++ b/v_windows/v/old/examples/game_of_life/life_gg.v @@ -0,0 +1,56 @@ +module main + +import gg +import gx +import automaton + +const ( + screen_width = 800 + screen_height = 600 + filled_color = gx.blue +) + +[live] +fn print_automaton(app &App) { + square_size := 18 + for y := 1; y < app.a.field.maxy; y++ { + for x := 1; x < app.a.field.maxx; x++ { + cell := app.a.field.get(x, y) + if cell == 1 { + app.gg.draw_rect(f32(square_size * x), f32(square_size * y), f32(square_size), + f32(square_size), filled_color) + } + } + } +} + +struct App { +mut: + gg &gg.Context + a automaton.Automaton +} + +fn frame(mut app App) { + app.gg.begin() + app.a.update() + print_automaton(app) + app.gg.end() +} + +fn main() { + mut app := App{ + gg: 0 + a: automaton.gun() + } + app.gg = gg.new_context( + bg_color: gx.white + frame_fn: frame + user_data: &app + width: screen_width + height: screen_height + create_window: true + resizable: false + window_title: 'v life (with gg, gx)' + ) + app.gg.run() +} diff --git a/v_windows/v/old/examples/game_of_life/modules/automaton/automaton.v b/v_windows/v/old/examples/game_of_life/modules/automaton/automaton.v new file mode 100644 index 0000000..5c0bf06 --- /dev/null +++ b/v_windows/v/old/examples/game_of_life/modules/automaton/automaton.v @@ -0,0 +1,132 @@ +module automaton + +// /////////////////////////////////////////////////////////// +pub struct A2D { +pub mut: + maxx int + maxy int + data &int +} + +fn new_a2d(maxx int, maxy int) &A2D { + size := int(sizeof(int)) * (maxx * maxy) + return &A2D{ + maxx: maxx + maxy: maxy + data: unsafe { &int(vcalloc(size)) } + } +} + +[inline] +pub fn (a &A2D) set(x int, y int, newval int) { + unsafe { + mut e := &int(0) + e = a.data + y * a.maxx + x + *e = newval + _ = e // TODO compiler bug, this is not necessary + } +} + +[inline] +pub fn (a &A2D) get(x int, y int) int { + unsafe { + mut e := &int(0) + e = a.data + y * a.maxx + x + _ = e + return *e + } +} + +[inline] +pub fn (a &A2D) clear() { + for y := 0; y < a.maxy; y++ { + for x := 0; x < a.maxx; x++ { + a.set(x, y, 0) + } + } +} + +// /////////////////////////////////////////////////////////// +pub struct Automaton { +pub mut: + field &A2D + new_field &A2D +} + +fn new_automaton(ftext string) Automaton { + f := ftext.split('\n').map(it.trim_space()).filter(it.len > 0) + maxy := f.len + mut maxx := 0 + for y := 0; y < f.len; y++ { + if maxx < f[y].len { + maxx = f[y].len + } + } + field := new_a2d(maxx, maxy) + new_field := new_a2d(maxx, maxy) + for y in 0 .. field.maxy { + for x in 0 .. field.maxx { + val := if x < f[y].len && f[y][x] == `#` { 1 } else { 0 } + field.set(x, y, val) + } + } + return Automaton{ + field: field + new_field: new_field + } +} + +pub fn (mut aa Automaton) update() { + aa.new_field.clear() + for y := 1; y < aa.field.maxy; y++ { + for x := 1; x < aa.field.maxx; x++ { + moore_sum := (0 + aa.field.get(x - 1, y - 1) + aa.field.get(x, y - 1) + aa.field.get(x + + 1, y - 1) + aa.field.get(x - 1, y) + 0 + aa.field.get(x + 1, y) + + aa.field.get(x - 1, y + 1) + aa.field.get(x, y + 1) + aa.field.get(x + 1, y + 1)) + cell := aa.field.get(x, y) + v := if cell == 1 { moore_sum in [2, 3] } else { moore_sum == 3 } + aa.new_field.set(x, y, if v { 1 } else { 0 }) + } + } + tmp := aa.field + aa.field = aa.new_field + aa.new_field = tmp +} + +pub fn gun() Automaton { + field := ' +******************************************* +* * +* A shooting gun: * +* # * +* # # * +* ## ## ## * +* # # ## ## * +* ## # # ## * +* ## # # ## # # * +* # # # * +* # # * +* ## * +* * +* Tetris Life: * +* * +* ## #### * +* ## * +* * +* * +* * +* # ## * +* ### ## * +* * +* * +* * +* # * +* ### * +* * +* * +* * +* * +******************************************* +' + return new_automaton(field) +} diff --git a/v_windows/v/old/examples/get_weather/README.md b/v_windows/v/old/examples/get_weather/README.md new file mode 100644 index 0000000..be2695d --- /dev/null +++ b/v_windows/v/old/examples/get_weather/README.md @@ -0,0 +1,23 @@ +# get_weather +get_weather is a very simple web crawler. +Its goal is to get a weather forecast from caiyunapp.com. + +# Compile and Run + +Use this to generate an executable and then launch the web crawler. +```bash +v get_weather.v +./get_weather +``` + +As a convenience, you can also compile and launch the web crawler directly. +```bash +v run get_weather.v +``` + +In this project we use http.fetch() to get a http.Response, with a +custom user-agent and then we use json.decode() to decode the json +response to struct. +We also use a `[skip]` attribute to skip certain fields in the response, +that we don't need and use a `[json: result]` attribute to specify that +our struct field is named differently from the incoming json response. diff --git a/v_windows/v/old/examples/get_weather/get_weather.v b/v_windows/v/old/examples/get_weather/get_weather.v new file mode 100644 index 0000000..3156590 --- /dev/null +++ b/v_windows/v/old/examples/get_weather/get_weather.v @@ -0,0 +1,55 @@ +import json +import rand +import net.http + +struct Weather { + status string [skip] // drop this field + api_version string [skip] + api_status string [skip] + lang string [skip] + unit string [skip] + tzshift int [skip] + timezone string [skip] + server_time u32 [skip] + location []f32 [skip] + result Result //[json: result] if the field name is different in JSON, it can be specified +} + +struct Result { + realtime Realtime [skip] + minutely Minutely [skip] + hourly Hourly [skip] + daily Daily [skip] + primary int [skip] + forecast_keypoint string +} + +struct Realtime {} + +struct Minutely {} + +struct Hourly {} + +struct Daily {} + +fn main() { + config := http.FetchConfig{ + user_agent: 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0' + } + + rnd := rand.f32() + url := 'https://api.caiyunapp.com/v2.5/96Ly7wgKGq6FhllM/116.391912,40.010711/weather.jsonp?hourlysteps=120&random=$rnd' + // println(url) + + resp := http.fetch(url, config) or { + println('failed to fetch data from the server') + return + } + + weather := json.decode(Weather, resp.text) or { + println('failed to decode weather json') + return + } + + println('未来两小时天气:\n${weather.result.forecast_keypoint}.') +} diff --git a/v_windows/v/old/examples/gg/logo.png b/v_windows/v/old/examples/gg/logo.png new file mode 100644 index 0000000..98ebfb1 Binary files /dev/null and b/v_windows/v/old/examples/gg/logo.png differ diff --git a/v_windows/v/old/examples/gg/polygons.v b/v_windows/v/old/examples/gg/polygons.v new file mode 100644 index 0000000..9ae2037 --- /dev/null +++ b/v_windows/v/old/examples/gg/polygons.v @@ -0,0 +1,34 @@ +module main + +import gg +import gx + +struct App { +mut: + gg &gg.Context +} + +fn main() { + mut app := &App{ + gg: 0 + } + app.gg = gg.new_context( + bg_color: gx.rgb(174, 198, 255) + width: 600 + height: 400 + window_title: 'Polygons' + frame_fn: frame + user_data: app + ) + app.gg.run() +} + +fn frame(mut app App) { + app.gg.begin() + app.gg.draw_convex_poly([f32(100.0), 100.0, 200.0, 100.0, 300.0, 200.0, 200.0, 300.0, 100.0, + 300.0, + ], gx.blue) + app.gg.draw_empty_poly([f32(50.0), 50.0, 70.0, 60.0, 90.0, 80.0, 70.0, 110.0], gx.black) + app.gg.draw_triangle(450, 142, 530, 280, 370, 280, gx.red) + app.gg.end() +} diff --git a/v_windows/v/old/examples/gg/random.v b/v_windows/v/old/examples/gg/random.v new file mode 100644 index 0000000..f9e963b --- /dev/null +++ b/v_windows/v/old/examples/gg/random.v @@ -0,0 +1,63 @@ +import gg +import time + +const pwidth = 800 + +const pheight = 600 + +const pbytes = 4 + +struct AppState { +mut: + gg &gg.Context = 0 + istream_idx int + pixels [pheight][pwidth]u32 +} + +[direct_array_access] +fn (mut state AppState) update() { + mut rcolor := u64(state.gg.frame) + for { + for y in 0 .. pheight { + for x in 0 .. pwidth { + rcolor = rcolor * 1664525 + 1013904223 + state.pixels[y][x] = u32(rcolor & 0x0000_0000_FFFF_FFFF) | 0x1010AFFF + } + } + time.sleep(33 * time.millisecond) + } +} + +fn (mut state AppState) draw() { + mut istream_image := state.gg.get_cached_image_by_idx(state.istream_idx) + istream_image.update_pixel_data(&state.pixels) + size := gg.window_size() + state.gg.draw_image(0, 0, size.width, size.height, istream_image) +} + +// gg callbacks: + +fn graphics_init(mut state AppState) { + state.istream_idx = state.gg.new_streaming_image(pwidth, pheight, pbytes, pixel_format: .rgba8) +} + +fn graphics_frame(mut state AppState) { + state.gg.begin() + state.draw() + state.gg.end() +} + +fn main() { + mut state := &AppState{} + state.gg = gg.new_context( + width: 800 + height: 600 + create_window: true + window_title: 'Random Static' + init_fn: graphics_init + frame_fn: graphics_frame + user_data: state + ) + go state.update() + state.gg.run() +} diff --git a/v_windows/v/old/examples/gg/raven_text_rendering.v b/v_windows/v/old/examples/gg/raven_text_rendering.v new file mode 100644 index 0000000..2007787 --- /dev/null +++ b/v_windows/v/old/examples/gg/raven_text_rendering.v @@ -0,0 +1,104 @@ +module main + +import gg +import gx +import os +import math + +const ( + win_width = 600 + win_height = 700 + bg_color = gx.white +) + +const ( + text = ' +Once upon a midnight dreary, while I pondered, weak and weary, +Over many a quaint and curious volume of forgotten lore— + While I nodded, nearly napping, suddenly there came a tapping, +As of some one gently rapping, rapping at my chamber door. +“’Tis some visitor,” I muttered, “tapping at my chamber door— + Only this and nothing more.” + + Ah, distinctly I remember it was in the bleak December; +And each separate dying ember wrought its ghost upon the floor. + Eagerly I wished the morrow;—vainly I had sought to borrow + From my books surcease of sorrow—sorrow for the lost Lenore— +For the rare and radiant maiden whom the angels name Lenore— + Nameless here for evermore. + + And the silken, sad, uncertain rustling of each purple curtain +Thrilled me—filled me with fantastic terrors never felt before; + So that now, to still the beating of my heart, I stood repeating + “’Tis some visitor entreating entrance at my chamber door— +Some late visitor entreating entrance at my chamber door;— + This it is and nothing more.” + + Presently my soul grew stronger; hesitating then no longer, +“Sir,” said I, “or Madam, truly your forgiveness I implore; + But the fact is I was napping, and so gently you came rapping, + And so faintly you came tapping, tapping at my chamber door, +That I scarce was sure I heard you”—here I opened wide the door;— + Darkness there and nothing more. + +Deep into that darkness peering, long I stood there wondering, fearing, +Doubting, dreaming dreams no mortal ever dared to dream before; + But the silence was unbroken, and the stillness gave no token, + And the only word there spoken was the whispered word, “Lenore?” +This I whispered, and an echo murmured back the word, “Lenore!”— + Merely this and nothing more. + + Back into the chamber turning, all my soul within me burning, +Soon again I heard a tapping somewhat louder than before. + “Surely,” said I, “surely that is something at my window lattice; + Let me see, then, what thereat is, and this mystery explore— +Let my heart be still a moment and this mystery explore;— + ’Tis the wind and nothing more!” +' + lines = text.split('\n') +) + +struct App { +mut: + gg &gg.Context +} + +fn main() { + mut app := &App{ + gg: 0 + } + mut font_path := os.resource_abs_path(os.join_path('..', 'assets', 'fonts', 'RobotoMono-Regular.ttf')) + $if android { + font_path = 'fonts/RobotoMono-Regular.ttf' + } + app.gg = gg.new_context( + width: win_width + height: win_height + create_window: true + window_title: 'Empty window' + user_data: app + bg_color: bg_color + frame_fn: frame + font_path: font_path // window_user_ptr: ctx + // native_rendering: true + ) + app.gg.run() +} + +fn frame(mut app App) { + app.gg.begin() + width := gg.window_size().width + mut scale_factor := math.round(f32(width) / win_width) + if scale_factor <= 0 { + scale_factor = 1 + } + text_cfg := gx.TextCfg{ + size: 16 * int(scale_factor) + } + mut y := 10 + for line in lines { + app.gg.draw_text(10, y, line, text_cfg) + y += 30 + } + app.gg.end() +} diff --git a/v_windows/v/old/examples/gg/rectangles.v b/v_windows/v/old/examples/gg/rectangles.v new file mode 100644 index 0000000..73229fd --- /dev/null +++ b/v_windows/v/old/examples/gg/rectangles.v @@ -0,0 +1,52 @@ +module main + +import gg +import gx +import os + +const ( + win_width = 600 + win_height = 300 +) + +struct App { +mut: + gg &gg.Context + image gg.Image +} + +fn main() { + mut app := &App{ + gg: 0 + } + app.gg = gg.new_context( + bg_color: gx.white + width: win_width + height: win_height + create_window: true + window_title: 'Rectangles' + frame_fn: frame + user_data: app + init_fn: init_images + ) + app.image = app.gg.create_image(os.resource_abs_path('logo.png')) + app.gg.run() +} + +fn init_images(mut app App) { + // app.image = gg.create_image('logo.png') +} + +fn frame(app &App) { + app.gg.begin() + app.draw() + app.gg.end() +} + +fn (app &App) draw() { + // app.gg.draw_text_def(200,20, 'hello world!') + // app.gg.draw_text_def(300,300, 'привет') + app.gg.draw_rect(10, 10, 100, 30, gx.blue) + app.gg.draw_empty_rect(110, 150, 80, 40, gx.black) + app.gg.draw_image(230, 30, app.image.width, app.image.height, app.image) +} diff --git a/v_windows/v/old/examples/gg/stars.v b/v_windows/v/old/examples/gg/stars.v new file mode 100644 index 0000000..815cfeb --- /dev/null +++ b/v_windows/v/old/examples/gg/stars.v @@ -0,0 +1,134 @@ +module main + +import os +import gg +import gx +import rand +import sokol.sgl + +const ( + win_width = 800 + win_height = 600 + max_stars = 5000 + max_v_letters = 5 +) + +struct Star { +mut: + x f32 + y f32 + z f32 + r f32 + g f32 + b f32 +} + +struct VLetter { +mut: + x f32 + y f32 + z f32 + w f32 + h f32 + angle f32 + dz f32 + dangle f32 +} + +struct App { +mut: + gg &gg.Context + image gg.Image + stars []Star + v_letters []VLetter +} + +fn main() { + mut app := &App{ + gg: 0 + stars: []Star{len: max_stars} + v_letters: []VLetter{len: max_v_letters} + } + app.gg = gg.new_context( + bg_color: gx.black + width: win_width + height: win_height + create_window: true + window_title: 'Star Vield' + frame_fn: frame + init_fn: init_images + user_data: app + ) + for i in 0 .. max_stars { + app.stars[i].x = rand.f32_in_range(-200.0, 200.0) + app.stars[i].y = rand.f32_in_range(-200.0, 200.0) + app.stars[i].z = rand.f32_in_range(-200.0, -100.0) + app.stars[i].r = rand.f32_in_range(0.1, 1.0) + app.stars[i].g = rand.f32_in_range(0.1, 1.0) + app.stars[i].b = rand.f32_in_range(0.1, 1.0) + } + for i in 0 .. max_v_letters { + app.v_letters[i].x = rand.f32_in_range(-20.0, 20.0) + app.v_letters[i].y = rand.f32_in_range(-20.0, 20.0) + app.v_letters[i].z = rand.f32_in_range(-5.0, -1.0) + app.v_letters[i].w = rand.f32_in_range(5, 20) + app.v_letters[i].h = app.v_letters[i].w + app.v_letters[i].angle = rand.f32_in_range(0, 6.283184) + app.v_letters[i].dangle = rand.f32_in_range(-0.05, 0.05) + app.v_letters[i].dz = rand.f32_in_range(-0.1, -0.01) + } + app.gg.run() +} + +fn init_images(mut app App) { + app.image = app.gg.create_image(os.resource_abs_path('logo.png')) +} + +fn frame(mut app App) { + app.gg.begin() + app.draw() + app.gg.end() +} + +// fn C.glPointSize(size f32) +fn (mut app App) draw() { + sgl.defaults() + sgl.perspective(sgl.rad(90), 1.0, 1.0, 100.0) + // C.glPointSize(3.0) + sgl.begin_points() + for i in 0 .. app.stars.len { + s := app.stars[i] + sgl.v3f_c3f(s.x, s.y, s.z, s.r, s.g, s.b) + app.stars[i].z += 0.3 + if app.stars[i].z > -1.0 { + app.stars[i].x = rand.f32_in_range(-200.0, 200.0) + app.stars[i].y = rand.f32_in_range(-200.0, 200.0) + app.stars[i].z = rand.f32_in_range(-200.0, -100.0) + } + } + sgl.end() + // //// + for i in 0 .. app.v_letters.len { + v := app.v_letters[i] + sgl.defaults() + sgl.perspective(sgl.rad(90), 1.0, 1.0, 100.0) + sgl.rotate(v.angle, 0, 0, 1) + app.gg.draw_image_3d(v.x, v.y, v.z, v.w, v.h, app.image) + // + app.v_letters[i].z += app.v_letters[i].dz + app.v_letters[i].angle += app.v_letters[i].dangle + if app.v_letters[i].z > -60.0 { + app.v_letters[i].x += rand.f32_in_range(-0.05, 0.05) + app.v_letters[i].y += rand.f32_in_range(-0.05, 0.05) + } + if app.v_letters[i].z < -95.0 { + app.v_letters[i].h *= 0.8 + app.v_letters[i].w *= 0.8 + } + if app.v_letters[i].z < -100.0 { + app.v_letters[i].z = rand.f32_in_range(-5.0, -1.0) + app.v_letters[i].h = 10.0 + app.v_letters[i].w = 10.0 + } + } +} diff --git a/v_windows/v/old/examples/gg/worker_thread.v b/v_windows/v/old/examples/gg/worker_thread.v new file mode 100644 index 0000000..a2c9852 --- /dev/null +++ b/v_windows/v/old/examples/gg/worker_thread.v @@ -0,0 +1,90 @@ +// Copyright(C) 2019 Lars Pontoppidan. All rights reserved. +// Use of this source code is governed by an MIT license file distributed with this software package. +module main + +// Example of how to send a value through a channel from a worker thread to the main/rendering thread. +// This can be useful to do long running computations while keeping your framerate high (60 fps in this example). +import gg +import gx +import math +import time + +const ( + win_width = 600 + win_height = 700 + bg_color = gx.white + count_color = gx.black +) + +struct App { +mut: + gg &gg.Context + ch chan i64 + counter i64 +} + +fn main() { + mut app := &App{ + gg: 0 + } + app.gg = gg.new_context( + width: win_width + height: win_height + create_window: true + window_title: 'Counter' + user_data: app + bg_color: bg_color + frame_fn: frame + init_fn: init + font_path: gg.system_font_path() + ) + app.gg.run() +} + +fn init(mut app App) { + // Spawn a new worker thread. + go worker(mut app) +} + +// worker simulates a workload. This should be run in a separate thread. +fn worker(mut app App) { + stopwatch := time.new_stopwatch() + mut elapsed := stopwatch.elapsed() + // Do heavy operations here - like invoking a path finding algorithm, load an image or similar. + for { + now := stopwatch.elapsed() + // When done - send the result through a channel to the main/rendering thread. + app.ch <- (now - elapsed) + elapsed = now + time.sleep(1 * time.second) + } +} + +fn frame(mut app App) { + app.gg.begin() + size := gg.window_size() + mut scale_factor := math.round(f32(size.width) / win_width) + if scale_factor <= 0 { + scale_factor = 1 + } + text_cfg := gx.TextCfg{ + size: 64 * int(scale_factor) + } + + // Try a pop from the channel + mut count := i64(0) + if app.ch.try_pop(mut count) == .success { + // A value was assigned - increase the counter + app.counter += i64(f64(count) / time.second) + } + + label := '$app.counter' + label_width := (f64(label.len * text_cfg.size) / 4.0) + label_height := (f64(1 * text_cfg.size) / 2.0) + mut x := f32(size.width) * 0.5 - label_width + mut y := f32(size.height) * 0.5 - label_height + + app.gg.draw_text(int(x), int(y), label, text_cfg) + + app.gg.end() +} diff --git a/v_windows/v/old/examples/hanoi.v b/v_windows/v/old/examples/hanoi.v new file mode 100644 index 0000000..0851ba0 --- /dev/null +++ b/v_windows/v/old/examples/hanoi.v @@ -0,0 +1,22 @@ +// hanoi tower +const ( + num = 7 +) + +fn main() { + hanoi(num, 'A', 'B', 'C') +} + +fn move(n int, a string, b string) { + println('Disc $n from $a to ${b}...') +} + +fn hanoi(n int, a string, b string, c string) { + if n == 1 { + move(1, a, c) + } else { + hanoi(n - 1, a, c, b) + move(n, a, c) + hanoi(n - 1, b, a, c) + } +} diff --git a/v_windows/v/old/examples/hello_v_js.v b/v_windows/v/old/examples/hello_v_js.v new file mode 100644 index 0000000..2286719 --- /dev/null +++ b/v_windows/v/old/examples/hello_v_js.v @@ -0,0 +1,5 @@ +fn main() { + for i in 0 .. 3 { + println('Hello from V.js ($i)') + } +} diff --git a/v_windows/v/old/examples/hello_world.v b/v_windows/v/old/examples/hello_world.v new file mode 100644 index 0000000..fb96416 --- /dev/null +++ b/v_windows/v/old/examples/hello_world.v @@ -0,0 +1 @@ +println('Hello, World!') diff --git a/v_windows/v/old/examples/hot_reload/.gitignore b/v_windows/v/old/examples/hot_reload/.gitignore new file mode 100644 index 0000000..fc656b9 --- /dev/null +++ b/v_windows/v/old/examples/hot_reload/.gitignore @@ -0,0 +1 @@ +!glfw3.dll diff --git a/v_windows/v/old/examples/hot_reload/bounce.v b/v_windows/v/old/examples/hot_reload/bounce.v new file mode 100644 index 0000000..135ad52 --- /dev/null +++ b/v_windows/v/old/examples/hot_reload/bounce.v @@ -0,0 +1,84 @@ +// Build this example with +// v -live bounce.v +module main + +import gx +import gg +import time + +struct Game { +mut: + gg &gg.Context + x int + y int + dy int + dx int + height int + width int + draw_fn voidptr +} + +const ( + window_width = 400 + window_height = 300 + width = 50 +) + +fn main() { + mut game := &Game{ + gg: 0 + dx: 2 + dy: 2 + height: window_height + width: window_width + draw_fn: 0 + } + game.gg = gg.new_context( + width: window_width + height: window_height + font_size: 20 + user_data: game + window_title: 'Hot code reloading demo' + create_window: true + frame_fn: frame + bg_color: gx.white + font_path: gg.system_font_path() + ) + // window.onkeydown(key_down) + println('Starting the game loop...') + go game.run() + game.gg.run() +} + +// Try uncommenting or changing the lines inside the live functions. +// Guess what will happen: +[live] +fn frame(mut game Game) { + game.gg.begin() + game.gg.draw_text_def(10, 5, 'Modify examples/hot_reload/bounce.v to get instant updates') + game.gg.draw_rect(game.x, game.y, width, width, gx.blue) + game.gg.draw_rect(window_width - width - game.x + 10, 200 - game.y + width, width, + width, gx.rgb(228, 10, 55)) + game.gg.draw_rect(game.x - 25, 250 - game.y, width, width, gx.rgb(28, 240, 55)) + game.gg.end() +} + +[live] +fn (mut game Game) update_model() { + speed := 2 + game.x += speed * game.dx + game.y += speed * game.dy + if game.y >= game.height - width || game.y <= 0 { + game.dy = -game.dy + } + if game.x >= game.width - width || game.x <= 0 { + game.dx = -game.dx + } +} + +fn (mut game Game) run() { + for { + game.update_model() + time.sleep(16 * time.millisecond) // 60fps + } +} diff --git a/v_windows/v/old/examples/hot_reload/graph.v b/v_windows/v/old/examples/hot_reload/graph.v new file mode 100644 index 0000000..53228f1 --- /dev/null +++ b/v_windows/v/old/examples/hot_reload/graph.v @@ -0,0 +1,86 @@ +module main + +import gx +import gg +import time +import math + +const ( + size = 700 + scale = 50.0 +) + +struct Context { +mut: + gg &gg.Context +} + +fn main() { + mut context := &Context{ + gg: 0 + } + context.gg = gg.new_context( + width: size + height: size + font_size: 20 + user_data: context + window_title: 'Graph builder' + create_window: true + frame_fn: frame + resizable: true + bg_color: gx.white + font_path: gg.system_font_path() + ) + context.gg.run() +} + +fn frame(mut ctx Context) { + ctx.gg.begin() + ctx.draw() + ctx.gg.end() +} + +[live] +fn (ctx &Context) draw() { + s := gg.window_size() + mut w := s.width + mut h := s.height + if gg.high_dpi() { + w /= 2 + h /= 2 + } + ctx.gg.draw_line(0, h / 2, w, h / 2, gx.gray) // x axis + ctx.gg.draw_line(w / 2, 0, w / 2, h, gx.gray) // y axis + atime := f64(time.ticks() / 10) + stime := math.sin(2.0 * math.pi * f64(time.ticks() % 6000) / 6000) + mut y := 0.0 + blue := gx.Color{ + r: 100 + g: 100 + b: 200 + } + red := gx.Color{ + r: 200 + g: 100 + b: 100 + } + y = 1.0 + max := f32(w) / (2 * scale) + min := -max + for x := min; x <= max; x += 0.01 { + // y = x*x + 2 + // y = x * x + stime * stime + // y = stime + // y = stime * h + y = stime * 1.0 * math.sin(x + stime + atime / 32) * ((h / 256) + x) + // y = (stime * x) * x + stime + // y = (x + 3) * (x + 3) / stime + stime*2.5 + // y = math.sqrt(30.0 - x * x) * stime + // y -= (stime-0.5) + stime + // ctx.gg.draw_rect(f32((w/2) + x * scale), f32((h/2) - y * scale), 2, 2, blue) + ctx.gg.draw_rect(f32((w / 2) + x * scale), f32((h / 2) - y * scale), 2, (f32(y) * scale), + blue) + ctx.gg.draw_rect(f32((w / 2) + x * scale), f32((h / 2) + y * scale), 2, (f32(y) * scale) + + 32, red) + } +} diff --git a/v_windows/v/old/examples/hot_reload/message.v b/v_windows/v/old/examples/hot_reload/message.v new file mode 100644 index 0000000..1f9ec2a --- /dev/null +++ b/v_windows/v/old/examples/hot_reload/message.v @@ -0,0 +1,18 @@ +module main + +// Build this example with `v -live message.v` +import time +import v.live + +[live] +fn print_message() { + info := live.info() + println('OK reloads: ${info.reloads_ok:4d} | Total reloads: ${info.reloads:4d} | Hello! Modify this message while the program is running.') +} + +fn main() { + for { + print_message() + time.sleep(500 * time.millisecond) + } +} diff --git a/v_windows/v/old/examples/json.v b/v_windows/v/old/examples/json.v new file mode 100644 index 0000000..841d9e2 --- /dev/null +++ b/v_windows/v/old/examples/json.v @@ -0,0 +1,40 @@ +import json + +struct User { + name string + age int +mut: + is_registered bool +} + +fn main() { + s := '[{ "name":"Frodo", "age":25}, {"name":"Bobby", "age":10}]' + mut users := json.decode([]User, s) or { + eprintln('Failed to parse json') + return + } + for user in users { + println('$user.name: $user.age') + } + println('') + for i, user in users { + println('$i) $user.name') + if !user.can_register() { + println('Cannot register $user.name, they are too young') + } else { + users[i].register() + println('$user.name is registered') + } + } + // Let's encode users again just for fun + println('') + println(json.encode(users)) +} + +fn (u User) can_register() bool { + return u.age >= 16 +} + +fn (mut u User) register() { + u.is_registered = true +} diff --git a/v_windows/v/old/examples/lander.v b/v_windows/v/old/examples/lander.v new file mode 100644 index 0000000..ad2600d --- /dev/null +++ b/v_windows/v/old/examples/lander.v @@ -0,0 +1,62 @@ +// Example of sum types +// Models a landing craft leaving orbit and landing on a world +import rand +import time + +struct Moon { +} + +struct Mars { +} + +fn (m Mars) dust_storm() bool { + return rand.int() >= 0 +} + +struct Venus { +} + +type World = Mars | Moon | Venus + +struct Lander { +} + +fn (l Lander) deorbit() { + println('leaving orbit') +} + +fn (l Lander) open_parachutes(n int) { + println('opening $n parachutes') +} + +fn wait() { + println('waiting...') + time.sleep(1 * time.second) +} + +fn (l Lander) land(w World) { + if w is Mars { + for w.dust_storm() { + wait() + } + } + l.deorbit() + match w { + Moon {} // no atmosphere + Mars { + // light atmosphere + l.open_parachutes(3) + } + Venus { + // heavy atmosphere + l.open_parachutes(1) + } + } + println('landed') +} + +fn main() { + l := Lander{} + l.land(Venus{}) + l.land(Mars{}) +} diff --git a/v_windows/v/old/examples/linear_regression/simple_linear_regression.v b/v_windows/v/old/examples/linear_regression/simple_linear_regression.v new file mode 100644 index 0000000..5bce1dc --- /dev/null +++ b/v_windows/v/old/examples/linear_regression/simple_linear_regression.v @@ -0,0 +1,55 @@ +import math + +struct LinearResult { + r2 f64 + intercept f64 + slope f64 + dependent_variable_means f64 + independent_variable_means f64 +} + +fn linearrelationship(independent_variable []int, dependent_variable []int) LinearResult { + // Objective : + // Find what is the linear relationship between two dataset X and Y? + // x := independent variable + // y := dependent variable + mut sum_r2_x := 0 + mut sum_r2_y := 0 + mut sum_xy := 0 + mut sum_x := 0 + mut sum_y := 0 + for i in independent_variable { + sum_x += i + sum_r2_x += i * i + } + for yi in dependent_variable { + sum_y += yi + sum_r2_y += yi * yi + } + x_means := sum_x / independent_variable.len + y_means := sum_y / dependent_variable.len + for index, x_value in independent_variable { + sum_xy += x_value * dependent_variable[index] + } + // /Slope = (∑y)(∑x²) - (∑x)(∑xy) / n(∑x²) - (∑x)² + slope_value := f64((sum_y * sum_r2_x) - (sum_x * sum_xy)) / f64((sum_r2_x * independent_variable.len) - (sum_x * sum_x)) + // /Intercept = n(∑xy) - (∑x)(∑y) / n(∑x²) - (∑x)² + intercept_value := f64((independent_variable.len * sum_xy) - (sum_x * sum_y)) / f64((independent_variable.len * sum_r2_x) - (sum_x * sum_x)) + // Regression equation = Intercept + Slope x + // R2 = n(∑xy) - (∑x)(∑y) / sqrt([n(∑x²)-(∑x)²][n(∑y²)-(∑y)²] + r2_value := f64((independent_variable.len * sum_xy) - (sum_x * sum_y)) / math.sqrt(f64((sum_r2_x * independent_variable.len) - (sum_x * sum_x)) * f64((sum_r2_y * dependent_variable.len) - (sum_y * sum_y))) + return LinearResult{ + r2: r2_value + intercept: intercept_value + slope: slope_value + independent_variable_means: x_means + dependent_variable_means: y_means + } +} + +fn main() { + independent_variable := [4, 5, 6, 7, 10] + dependent_variable := [3, 8, 20, 30, 12] + result := linearrelationship(independent_variable, dependent_variable) + println(result) +} diff --git a/v_windows/v/old/examples/links_scraper.v b/v_windows/v/old/examples/links_scraper.v new file mode 100644 index 0000000..2a0b775 --- /dev/null +++ b/v_windows/v/old/examples/links_scraper.v @@ -0,0 +1,14 @@ +import net.http + +fn main() { + html := http.get_text('https://news.ycombinator.com') + mut pos := 0 + for { + pos = html.index_after('https://', pos + 1) + if pos == -1 { + break + } + end := html.index_after('"', pos) + println(html[pos..end]) + } +} diff --git a/v_windows/v/old/examples/log.v b/v_windows/v/old/examples/log.v new file mode 100644 index 0000000..a4c28e1 --- /dev/null +++ b/v_windows/v/old/examples/log.v @@ -0,0 +1,18 @@ +import log + +fn main() { + mut l := log.Log{} + l.set_level(.info) + // Make a new file called info.log in the current folder + l.set_full_logpath('./info.log') + l.log_to_console_too() + println('Please check the file: $l.output_file_name after this example crashes.') + + l.info('info') + l.warn('warn') + l.error('error') + l.debug('no debug') + l.set_level(.debug) + l.debug('debug') + l.fatal('fatal') +} diff --git a/v_windows/v/old/examples/mini_calculator.v b/v_windows/v/old/examples/mini_calculator.v new file mode 100644 index 0000000..4663f4c --- /dev/null +++ b/v_windows/v/old/examples/mini_calculator.v @@ -0,0 +1,134 @@ +// Q: What's this? +// A: This is a mini "home-made" calculator. You may also regard it as a very elementary version of "interpreter". +import os + +const ( + numeric_char = [`0`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `.`, `e`, `E`] +) + +// Convert expression to Reverse Polish Notation. +fn expr_to_rev_pol(expr string) ?[]string { + if expr == '' { + return error('err: empty expression') + } + mut stack := []string{} + mut rev_pol := []string{} + mut pos := 0 + for pos < expr.len { + mut end_pos := pos + for end_pos < expr.len && expr[end_pos] in numeric_char { + end_pos++ + } + if end_pos > pos { + stack << expr[pos..end_pos] + pos = end_pos + } else if end_pos == pos { + op := expr[pos].ascii_str() + match op { + '(' { + stack << op + } + '*', '/' { + for stack.len > 0 && stack.last() !in ['(', '+', '-'] { + rev_pol << stack.last() + stack.delete(stack.len - 1) + } + stack << op + } + '+', '-' { + for stack.len > 0 && stack.last() != '(' { + rev_pol << stack.last() + stack.delete(stack.len - 1) + } + stack << op + } + ')' { + for stack.len > 0 && stack.last() != '(' { + rev_pol << stack.last() + stack.delete(stack.len - 1) + } + stack.delete(stack.len - 1) + } + else { + return error('err: invalid character `$op`') + } + } + pos++ + } + } + for stack.len > 0 { + top := stack.last() + rev_pol << top + stack.delete(stack.len - 1) + } + return rev_pol +} + +// Evaluate the result of Reverse Polish Notation. +fn eval_rev_pol(rev_pol []string) ?f64 { + mut stack := []f64{} + for item in rev_pol { + if is_num_string(item) { + stack << item.f64() + } else { + if stack.len >= 2 { + oprand_r := stack.last() + stack.delete(stack.len - 1) + oprand_l := stack.last() + stack.delete(stack.len - 1) + match item { + '+' { + stack << oprand_l + oprand_r + } + '-' { + stack << oprand_l - oprand_r + } + '*' { + stack << oprand_l * oprand_r + } + '/' { + if oprand_r == 0 { + return error('err: divide by zero') + } + stack << oprand_l / oprand_r + } + else {} + } + } else { + return error('err: invalid expression') + } + } + } + return stack[0] +} + +fn is_num_string(str string) bool { + for c in str { + if c !in numeric_char { + return false + } + } + return true +} + +fn main() { + println('Please enter the expression you want to calculate, e.g. 1e2+(3-2.5)*6/1.5 .') + println("Enter 'exit' or 'EXIT' to quit.") + mut expr_count := 0 + for { + expr_count++ + expr := os.input('[$expr_count] ').trim_space() + if expr in ['exit', 'EXIT'] { + break + } + rev_pol := expr_to_rev_pol(expr) or { + eprintln(err) + continue + } + res := eval_rev_pol(rev_pol) or { + eprintln(err) + continue + } + println(res) + } +} diff --git a/v_windows/v/old/examples/native/hello_world.v b/v_windows/v/old/examples/native/hello_world.v new file mode 100644 index 0000000..f2be959 --- /dev/null +++ b/v_windows/v/old/examples/native/hello_world.v @@ -0,0 +1,20 @@ +// fn println(s string) { } + +// fn test_fn() { +// println('test fn') +//} + +fn main() { + println('native test') + // i := 0 + // for i < 5 { + for _ in 1 .. 5 { + println('Hello world from V native machine code generator!') + // i++ + } + /* + println('Hello again!') + //test_fn() + println('done') + */ +} diff --git a/v_windows/v/old/examples/nbody.v b/v_windows/v/old/examples/nbody.v new file mode 100644 index 0000000..0f719e8 --- /dev/null +++ b/v_windows/v/old/examples/nbody.v @@ -0,0 +1,129 @@ +// Ported based on https://benchmarksgame-team.pages.debian.net/benchmarksgame/program/nbody-go-3.html +// Output: +// -0.169075164 +// -0.169059907 +import math + +const ( + solar_mass = 39.47841760435743197 // 4.0 * math.Pi * math.Pi + days_per_year = 365.24 + c_n = 5 +) + +struct Position { +pub mut: + x f64 + y f64 + z f64 +} + +struct Momentum { +pub mut: + x f64 + y f64 + z f64 + m f64 +} + +struct System { +pub mut: + v []Momentum + s []Position +} + +fn advance(mut sys System, dt f64) { + for i in 0 .. c_n - 1 { + mut vx := sys.v[i].x + mut vy := sys.v[i].y + mut vz := sys.v[i].z + + for j := i + 1; j < c_n; j++ { + dx := sys.s[i].x - sys.s[j].x + dy := sys.s[i].y - sys.s[j].y + dz := sys.s[i].z - sys.s[j].z + + dsquared := dx * dx + dy * dy + dz * dz + distance := math.sqrt(dsquared) + mag := (dt / (dsquared * distance)) + mi := sys.v[i].m + + vx -= dx * sys.v[j].m * mag + vy -= dy * sys.v[j].m * mag + vz -= dz * sys.v[j].m * mag + + sys.v[j].x += dx * mi * mag + sys.v[j].y += dy * mi * mag + sys.v[j].z += dz * mi * mag + } + sys.v[i].x = vx + sys.v[i].y = vy + sys.v[i].z = vz + } + + for i in 0 .. c_n { + sys.s[i].x += dt * sys.v[i].x + sys.s[i].y += dt * sys.v[i].y + sys.s[i].z += dt * sys.v[i].z + } +} + +fn offsetmomentum(mut sys System) { + mut px := f64(0) + mut py := f64(0) + mut pz := f64(0) + + for i in 0 .. c_n { + px += sys.v[i].x * sys.v[i].m + py += sys.v[i].y * sys.v[i].m + pz += sys.v[i].z * sys.v[i].m + } + sys.v[0].x = -px / solar_mass + sys.v[0].y = -py / solar_mass + sys.v[0].z = -pz / solar_mass +} + +fn energy(sys System) f64 { + mut e := f64(0) + for i in 0 .. c_n { + e += 0.5 * sys.v[i].m * (sys.v[i].x * sys.v[i].x + sys.v[i].y * sys.v[i].y + + sys.v[i].z * sys.v[i].z) + for j := i + 1; j < c_n; j++ { + dx := sys.s[i].x - sys.s[j].x + dy := sys.s[i].y - sys.s[j].y + dz := sys.s[i].z - sys.s[j].z + distance := math.sqrt(dx * dx + dy * dy + dz * dz) + e -= (sys.v[i].m * sys.v[j].m) / distance + } + } + return e +} + +fn arr_momentum() []Momentum { + return [ + Momentum{0.0, 0.0, 0.0, solar_mass}, + Momentum{1.66007664274403694e-03 * days_per_year, 7.69901118419740425e-03 * days_per_year, -6.90460016972063023e-05 * days_per_year, 9.54791938424326609e-04 * solar_mass}, + Momentum{-2.76742510726862411e-03 * days_per_year, 4.99852801234917238e-03 * days_per_year, 2.30417297573763929e-05 * days_per_year, 2.85885980666130812e-04 * solar_mass}, + Momentum{2.96460137564761618e-03 * days_per_year, 2.37847173959480950e-03 * days_per_year, -2.96589568540237556e-05 * days_per_year, 4.36624404335156298e-05 * solar_mass}, + Momentum{2.68067772490389322e-03 * days_per_year, 1.62824170038242295e-03 * days_per_year, -9.51592254519715870e-05 * days_per_year, 5.15138902046611451e-05 * solar_mass}, + ] +} + +fn arr_position() []Position { + return [ + Position{0.0, 0.0, 0.0}, + Position{4.84143144246472090e+00, -1.16032004402742839e+00, -1.03622044471123109e-01}, + Position{8.34336671824457987e+00, 4.12479856412430479e+00, -4.03523417114321381e-01}, + Position{1.28943695621391310e+01, -1.51111514016986312e+01, -2.23307578892655734e-01}, + Position{1.53796971148509165e+01, -2.59193146099879641e+01, 1.79258772950371181e-01}, + ] +} + +fn main() { + mut sys := &System{arr_momentum(), arr_position()} + offsetmomentum(mut sys) + println('${energy(sys):.9f}') //-0.169075164 + for _ in 0 .. 50_000_000 { + advance(mut sys, 0.01) + } + println('${energy(sys):.9f}') //-0.169059907 +} diff --git a/v_windows/v/old/examples/net_peer_ip.v b/v_windows/v/old/examples/net_peer_ip.v new file mode 100644 index 0000000..7f84deb --- /dev/null +++ b/v_windows/v/old/examples/net_peer_ip.v @@ -0,0 +1,5 @@ +import net + +conn := net.dial_tcp('google.com:80') ? +peer_addr := conn.peer_addr() ? +println('$peer_addr') diff --git a/v_windows/v/old/examples/net_raw_http.v b/v_windows/v/old/examples/net_raw_http.v new file mode 100644 index 0000000..85c88a9 --- /dev/null +++ b/v_windows/v/old/examples/net_raw_http.v @@ -0,0 +1,20 @@ +import net +import io + +fn main() { + // Make a new connection + mut conn := net.dial_tcp('google.com:80') ? + defer { + conn.close() or {} + } + + println(' peer: $conn.peer_addr()') + println('local: $conn.addr()') + + // Simple http HEAD request for a file + conn.write_string('HEAD /index.html HTTP/1.0\r\n\r\n') ? + // Read all the data that is waiting + result := io.read_all(reader: conn) ? + // Cast to string and print result + println(result.bytestr()) +} diff --git a/v_windows/v/old/examples/net_resolve.v b/v_windows/v/old/examples/net_resolve.v new file mode 100644 index 0000000..ddc058d --- /dev/null +++ b/v_windows/v/old/examples/net_resolve.v @@ -0,0 +1,24 @@ +import net + +for addr in [ + 'vlang.io:80', + 'google.com:80', + 'steampowered.com:80', + 'api.steampowered.com:80', +] { + println('$addr') + + for @type in [net.SocketType.tcp, .udp] { + family := net.AddrFamily.unspec + + addrs := net.resolve_addrs(addr, family, @type) or { + println('> None') + continue + } + + for a in addrs { + f := a.family() + println('> $a $f ${@type}') + } + } +} diff --git a/v_windows/v/old/examples/net_t.v b/v_windows/v/old/examples/net_t.v new file mode 100644 index 0000000..8e8255a --- /dev/null +++ b/v_windows/v/old/examples/net_t.v @@ -0,0 +1,21 @@ +import net.http +import sync +import time + +fn send_request(mut wg sync.WaitGroup) ?string { + start := time.ticks() + data := http.get('https://google.com') ? + finish := time.ticks() + println('Finish getting time ${finish - start} ms') + wg.done() + return data.text +} + +fn main() { + mut wg := sync.new_waitgroup() + for i := 0; i < 50; i++ { + wg.add(1) + go send_request(mut wg) + } + wg.wait() +} diff --git a/v_windows/v/old/examples/net_udp_server_and_client.v b/v_windows/v/old/examples/net_udp_server_and_client.v new file mode 100644 index 0000000..a69d178 --- /dev/null +++ b/v_windows/v/old/examples/net_udp_server_and_client.v @@ -0,0 +1,44 @@ +import os +import os.cmdline +import net + +fn main() { + println('Usage: net_udp_server_and_client [-l] [-p 5000]') + println(' -l - act as a server and listen') + println(' -p XXXX - custom port number') + println('------------------------------------------') + is_server := '-l' in os.args + port := cmdline.option(os.args, '-p', '40001').int() + mut buf := []byte{len: 100} + if is_server { + println('UDP echo server, listening for udp packets on port: $port') + mut c := net.listen_udp(':$port') ? + for { + read, addr := c.read(mut buf) or { continue } + println('received $read bytes from $addr') + c.write_to(addr, buf[..read]) or { + println('Server: connection dropped') + continue + } + } + } else { + println('UDP client, sending packets to port: ${port}.\nType `exit` to exit.') + mut c := net.dial_udp('localhost:$port') ? + for { + mut line := os.input('client > ') + match line { + '' { + line = '\n' + } + 'exit' { + println('goodbye.') + exit(0) + } + else {} + } + c.write_string(line) ? + read, _ := c.read(mut buf) ? + println('server : ' + buf[0..read].bytestr()) + } + } +} diff --git a/v_windows/v/old/examples/news_fetcher.v b/v_windows/v/old/examples/news_fetcher.v new file mode 100644 index 0000000..e724fba --- /dev/null +++ b/v_windows/v/old/examples/news_fetcher.v @@ -0,0 +1,49 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +import net.http +import json +import sync.pool + +struct Story { + title string + url string +} + +fn worker_fetch(p &pool.PoolProcessor, cursor int, worker_id int) voidptr { + id := p.get_item(cursor) + resp := http.get('https://hacker-news.firebaseio.com/v0/item/${id}.json') or { + println('failed to fetch data from /v0/item/${id}.json') + return pool.no_result + } + story := json.decode(Story, resp.text) or { + println('failed to decode a story') + return pool.no_result + } + println('# $cursor) $story.title | $story.url') + return pool.no_result +} + +// Fetches top HN stories in parallel, depending on how many cores you have +fn main() { + resp := http.get('https://hacker-news.firebaseio.com/v0/topstories.json') or { + println('failed to fetch data from /v0/topstories.json') + return + } + mut ids := json.decode([]int, resp.text) or { + println('failed to decode topstories.json') + return + } + if ids.len > 10 { + ids = ids[0..10] + } + mut fetcher_pool := pool.new_pool_processor( + callback: worker_fetch + ) + // NB: if you do not call set_max_jobs, the pool will try to use an optimal + // number of threads, one per each core in your system, which in most + // cases is what you want anyway... You can override the automatic choice + // by setting the VJOBS environment variable too. + // fetcher_pool.set_max_jobs( 4 ) + fetcher_pool.work_on_items(ids) +} diff --git a/v_windows/v/old/examples/path_tracing.v b/v_windows/v/old/examples/path_tracing.v new file mode 100644 index 0000000..8546c01 --- /dev/null +++ b/v_windows/v/old/examples/path_tracing.v @@ -0,0 +1,583 @@ +/********************************************************************** +* path tracing demo +* +* Copyright (c) 2019-2021 Dario Deledda. All rights reserved. +* Use of this source code is governed by an MIT license +* that can be found in the LICENSE file. +* +* This file contains a path tracer example in less of 500 line of codes +* 3 demo scenes included +* +* This code is inspired by: +* - "Realistic Ray Tracing" by Peter Shirley 2000 ISBN-13: 978-1568814612 +* - https://www.kevinbeason.com/smallpt/ +* +* Known limitations: +* - there are some approximation errors in the calculations +* - to speed-up the code a cos/sin table is used +* - the full precision code is present but commented, can be restored very easily +* - an higher number of samples ( > 60) can block the program on higher resolutions +* without a stack size increase +* - as a recursive program this code depend on the stack size, +* for higher number of samples increase the stack size +* in linux: ulimit -s byte_size_of_the_stack +* example: ulimit -s 16000000 +* - No OpenMP support +**********************************************************************/ +import os +import math +import rand +import time +import term + +const ( + inf = 1e+10 + eps = 1e-4 + f_0 = 0.0 +) + +//**************************** 3D Vector utility struct ********************* +struct Vec { +mut: + x f64 = 0.0 + y f64 = 0.0 + z f64 = 0.0 +} + +[inline] +fn (v Vec) + (b Vec) Vec { + return Vec{v.x + b.x, v.y + b.y, v.z + b.z} +} + +[inline] +fn (v Vec) - (b Vec) Vec { + return Vec{v.x - b.x, v.y - b.y, v.z - b.z} +} + +[inline] +fn (v Vec) * (b Vec) Vec { + return Vec{v.x * b.x, v.y * b.y, v.z * b.z} +} + +[inline] +fn (v Vec) dot(b Vec) f64 { + return v.x * b.x + v.y * b.y + v.z * b.z +} + +[inline] +fn (v Vec) mult_s(b f64) Vec { + return Vec{v.x * b, v.y * b, v.z * b} +} + +[inline] +fn (v Vec) cross(b Vec) Vec { + return Vec{v.y * b.z - v.z * b.y, v.z * b.x - v.x * b.z, v.x * b.y - v.y * b.x} +} + +[inline] +fn (v Vec) norm() Vec { + tmp_norm := 1.0 / math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z) + return Vec{v.x * tmp_norm, v.y * tmp_norm, v.z * tmp_norm} +} + +//********************************Image************************************** +struct Image { + width int + height int + data &Vec +} + +fn new_image(w int, h int) Image { + vecsize := int(sizeof(Vec)) + return Image{ + width: w + height: h + data: unsafe { &Vec(vcalloc(vecsize * w * h)) } + } +} + +// write out a .ppm file +fn (image Image) save_as_ppm(file_name string) { + npixels := image.width * image.height + mut f_out := os.create(file_name) or { panic(err) } + f_out.writeln('P3') or { panic(err) } + f_out.writeln('$image.width $image.height') or { panic(err) } + f_out.writeln('255') or { panic(err) } + for i in 0 .. npixels { + c_r := to_int(unsafe { image.data[i] }.x) + c_g := to_int(unsafe { image.data[i] }.y) + c_b := to_int(unsafe { image.data[i] }.z) + f_out.write_string('$c_r $c_g $c_b ') or { panic(err) } + } + f_out.close() +} + +//********************************** Ray ************************************ +struct Ray { + o Vec + d Vec +} + +// material types, used in radiance() +enum Refl_t { + diff + spec + refr +} + +//******************************** Sphere *********************************** +struct Sphere { + rad f64 = 0.0 // radius + p Vec // position + e Vec // emission + c Vec // color + refl Refl_t // reflection type => [diffuse, specular, refractive] +} + +fn (sp Sphere) intersect(r Ray) f64 { + op := sp.p - r.o // Solve t^2*d.d + 2*t*(o-p).d + (o-p).(o-p)-R^2 = 0 + b := op.dot(r.d) + mut det := b * b - op.dot(op) + sp.rad * sp.rad + + if det < 0 { + return 0 + } + + det = math.sqrt(det) + + mut t := b - det + if t > eps { + return t + } + + t = b + det + if t > eps { + return t + } + return 0 +} + +/*********************************** Scenes ********************************** +* 0) Cornell Box with 2 spheres +* 1) Sunset +* 2) Psychedelic +* The sphere fileds are: Sphere{radius, position, emission, color, material} +******************************************************************************/ +const ( + cen = Vec{50, 40.8, -860} // used by scene 1 + spheres = [ + [/* scene 0 cornnel box */ Sphere{ + rad: 1e+5 + p: Vec{1e+5 + 1, 40.8, 81.6} + e: Vec{} + c: Vec{.75, .25, .25} + refl: .diff + }, /* Left */ Sphere{ + rad: 1e+5 + p: Vec{-1e+5 + 99, 40.8, 81.6} + e: Vec{} + c: Vec{.25, .25, .75} + refl: .diff + }, /* Rght */ Sphere{ + rad: 1e+5 + p: Vec{50, 40.8, 1e+5} + e: Vec{} + c: Vec{.75, .75, .75} + refl: .diff + }, /* Back */ Sphere{ + rad: 1e+5 + p: Vec{50, 40.8, -1e+5 + 170} + e: Vec{} + c: Vec{} + refl: .diff + }, /* Frnt */ Sphere{ + rad: 1e+5 + p: Vec{50, 1e+5, 81.6} + e: Vec{} + c: Vec{.75, .75, .75} + refl: .diff + }, /* Botm */ Sphere{ + rad: 1e+5 + p: Vec{50, -1e+5 + 81.6, 81.6} + e: Vec{} + c: Vec{.75, .75, .75} + refl: .diff + }, /* Top */ Sphere{ + rad: 16.5 + p: Vec{27, 16.5, 47} + e: Vec{} + c: Vec{1, 1, 1}.mult_s(.999) + refl: .spec + }, /* Mirr */ Sphere{ + rad: 16.5 + p: Vec{73, 16.5, 78} + e: Vec{} + c: Vec{1, 1, 1}.mult_s(.999) + refl: .refr + }, /* Glas */ Sphere{ + rad: 600 + p: Vec{50, 681.6 - .27, 81.6} + e: Vec{12, 12, 12} + c: Vec{} + refl: .diff + } /* Lite */], + [/* scene 1 sunset */ Sphere{ + rad: 1600 + p: Vec{1.0, 0.0, 2.0}.mult_s(3000) + e: Vec{1.0, .9, .8}.mult_s(1.2e+1 * 1.56 * 2) + c: Vec{} + refl: .diff + }, /* sun */ Sphere{ + rad: 1560 + p: Vec{1, 0, 2}.mult_s(3500) + e: Vec{1.0, .5, .05}.mult_s(4.8e+1 * 1.56 * 2) + c: Vec{} + refl: .diff + }, /* horizon sun2 */ Sphere{ + rad: 10000 + p: cen + Vec{0, 0, -200} + e: Vec{0.00063842, 0.02001478, 0.28923243}.mult_s(6e-2 * 8) + c: Vec{.7, .7, 1}.mult_s(.25) + refl: .diff + }, /* sky */ Sphere{ + rad: 100000 + p: Vec{50, -100000, 0} + e: Vec{} + c: Vec{.3, .3, .3} + refl: .diff + }, /* grnd */ Sphere{ + rad: 110000 + p: Vec{50, -110048.5, 0} + e: Vec{.9, .5, .05}.mult_s(4) + c: Vec{} + refl: .diff + }, /* horizon brightener */ Sphere{ + rad: 4e+4 + p: Vec{50, -4e+4 - 30, -3000} + e: Vec{} + c: Vec{.2, .2, .2} + refl: .diff + }, /* mountains */ Sphere{ + rad: 26.5 + p: Vec{22, 26.5, 42} + e: Vec{} + c: Vec{1, 1, 1}.mult_s(.596) + refl: .spec + }, /* white Mirr */ Sphere{ + rad: 13 + p: Vec{75, 13, 82} + e: Vec{} + c: Vec{.96, .96, .96}.mult_s(.96) + refl: .refr + }, /* Glas */ Sphere{ + rad: 22 + p: Vec{87, 22, 24} + e: Vec{} + c: Vec{.6, .6, .6}.mult_s(.696) + refl: .refr + } /* Glas2 */], + [/* scene 3 Psychedelic */ Sphere{ + rad: 150 + p: Vec{50 + 75, 28, 62} + e: Vec{1, 1, 1}.mult_s(0e-3) + c: Vec{1, .9, .8}.mult_s(.93) + refl: .refr + }, Sphere{ + rad: 28 + p: Vec{50 + 5, -28, 62} + e: Vec{1, 1, 1}.mult_s(1e+1) + c: Vec{1, 1, 1}.mult_s(0) + refl: .diff + }, Sphere{ + rad: 300 + p: Vec{50, 28, 62} + e: Vec{1, 1, 1}.mult_s(0e-3) + c: Vec{1, 1, 1}.mult_s(.93) + refl: .spec + }], + ] // end of scene array +) + +//********************************** Utilities ****************************** +[inline] +fn clamp(x f64) f64 { + if x < 0 { + return 0 + } + if x > 1 { + return 1 + } + return x +} + +[inline] +fn to_int(x f64) int { + p := math.pow(clamp(x), 1.0 / 2.2) + return int(p * 255.0 + 0.5) +} + +fn intersect(r Ray, spheres &Sphere, nspheres int) (bool, f64, int) { + mut d := 0.0 + mut t := inf + mut id := 0 + for i := nspheres - 1; i >= 0; i-- { + d = unsafe { spheres[i] }.intersect(r) + if d > 0 && d < t { + t = d + id = i + } + } + return (t < inf), t, id +} + +// some casual random function, try to avoid the 0 +fn rand_f64() f64 { + x := rand.u32() & 0x3FFF_FFFF + return f64(x) / f64(0x3FFF_FFFF) +} + +const ( + cache_len = 65536 // the 2*pi angle will be splitted in 65536 part + cache_mask = cache_len - 1 // mask to speed-up the module process +) + +struct Cache { +mut: + sin_tab [65536]f64 + cos_tab [65536]f64 +} + +fn new_tabs() Cache { + mut c := Cache{} + inv_len := 1.0 / f64(cache_len) + for i in 0 .. cache_len { + x := f64(i) * math.pi * 2.0 * inv_len + c.sin_tab[i] = math.sin(x) + c.cos_tab[i] = math.cos(x) + } + return c +} + +//************ Cache for sin/cos speed-up table and scene selector ********** +const ( + tabs = new_tabs() +) + +//****************** main function for the radiance calculation ************* +fn radiance(r Ray, depthi int, scene_id int) Vec { + if depthi > 1024 { + eprintln('depthi: $depthi') + eprintln('') + return Vec{} + } + mut depth := depthi // actual depth in the reflection tree + mut t := 0.0 // distance to intersection + mut id := 0 // id of intersected object + mut res := false // result of intersect + + v_1 := 1.0 + // v_2 := f64(2.0) + + scene := spheres[scene_id] + // res, t, id = intersect(r, id, tb.scene) + res, t, id = intersect(r, scene.data, scene.len) + if !res { + return Vec{} + } + // if miss, return black + + obj := scene[id] // the hit object + + x := r.o + r.d.mult_s(t) + n := (x - obj.p).norm() + + nl := if n.dot(r.d) < 0.0 { n } else { n.mult_s(-1) } + + mut f := obj.c + + // max reflection + mut p := f.z + if f.x > f.y && f.x > f.z { + p = f.x + } else { + if f.y > f.z { + p = f.y + } + } + + depth++ + if depth > 5 { + if rand_f64() < p { + f = f.mult_s(f64(1.0) / p) + } else { + return obj.e // R.R. + } + } + + if obj.refl == .diff { // Ideal DIFFUSE reflection + // **Full Precision** + // r1 := f64(2.0 * math.pi) * rand_f64() + + // tabbed speed-up + r1 := rand.u32() & cache_mask + + r2 := rand_f64() + r2s := math.sqrt(r2) + + w := nl + + mut u := if math.abs(w.x) > f64(0.1) { Vec{0, 1, 0} } else { Vec{1, 0, 0} } + u = u.cross(w).norm() + + v := w.cross(u) + + // **Full Precision** + // d := (u.mult_s(math.cos(r1) * r2s) + v.mult_s(math.sin(r1) * r2s) + w.mult_s(1.0 - r2)).norm() + + // tabbed speed-up + d := (u.mult_s(tabs.cos_tab[r1] * r2s) + v.mult_s(tabs.sin_tab[r1] * r2s) + + w.mult_s(math.sqrt(f64(1.0) - r2))).norm() + + return obj.e + f * radiance(Ray{x, d}, depth, scene_id) + } else { + if obj.refl == .spec { // Ideal SPECULAR reflection + return obj.e + f * radiance(Ray{x, r.d - n.mult_s(2.0 * n.dot(r.d))}, depth, scene_id) + } + } + + refl_ray := Ray{x, r.d - n.mult_s(2.0 * n.dot(r.d))} // Ideal dielectric REFRACTION + into := n.dot(nl) > 0 // Ray from outside going in? + + nc := f64(1.0) + nt := f64(1.5) + + nnt := if into { nc / nt } else { nt / nc } + + ddn := r.d.dot(nl) + cos2t := v_1 - nnt * nnt * (v_1 - ddn * ddn) + if cos2t < 0.0 { // Total internal reflection + return obj.e + f * radiance(refl_ray, depth, scene_id) + } + + dirc := if into { f64(1) } else { f64(-1) } + tdir := (r.d.mult_s(nnt) - n.mult_s(dirc * (ddn * nnt + math.sqrt(cos2t)))).norm() + + a := nt - nc + b := nt + nc + r0 := a * a / (b * b) + c := if into { v_1 + ddn } else { v_1 - tdir.dot(n) } + + re := r0 + (v_1 - r0) * c * c * c * c * c + tr := v_1 - re + pp := f64(.25) + f64(.5) * re + rp := re / pp + tp := tr / (v_1 - pp) + + mut tmp := Vec{} + if depth > 2 { + // Russian roulette + tmp = if rand_f64() < pp { + radiance(refl_ray, depth, scene_id).mult_s(rp) + } else { + radiance(Ray{x, tdir}, depth, scene_id).mult_s(tp) + } + } else { + tmp = (radiance(refl_ray, depth, scene_id).mult_s(re)) + + (radiance(Ray{x, tdir}, depth, scene_id).mult_s(tr)) + } + return obj.e + (f * tmp) +} + +//*********************** beam scan routine ********************************* +fn ray_trace(w int, h int, samps int, file_name string, scene_id int) Image { + image := new_image(w, h) + + // inverse costants + w1 := f64(1.0 / f64(w)) + h1 := f64(1.0 / f64(h)) + samps1 := f64(1.0 / f64(samps)) + + cam := Ray{Vec{50, 52, 295.6}, Vec{0, -0.042612, -1}.norm()} // cam position, direction + cx := Vec{f64(w) * 0.5135 / f64(h), 0, 0} + cy := cx.cross(cam.d).norm().mult_s(0.5135) + mut r := Vec{} + + // speed-up constants + v_1 := f64(1.0) + v_2 := f64(2.0) + + // OpenMP injection point! #pragma omp parallel for schedule(dynamic, 1) shared(c) + for y := 0; y < h; y++ { + term.cursor_up(1) + eprintln('Rendering (${samps * 4} spp) ${(100.0 * f64(y)) / (f64(h) - 1.0):5.2f}%') + for x in 0 .. w { + i := (h - y - 1) * w + x + mut ivec := unsafe { &image.data[i] } + // we use sx and sy to perform a square subsampling of 4 samples + for sy := 0; sy < 2; sy++ { + for sx := 0; sx < 2; sx++ { + r = Vec{0, 0, 0} + for _ in 0 .. samps { + r1 := v_2 * rand_f64() + dx := if r1 < v_1 { math.sqrt(r1) - v_1 } else { v_1 - math.sqrt(v_2 - r1) } + + r2 := v_2 * rand_f64() + dy := if r2 < v_1 { math.sqrt(r2) - v_1 } else { v_1 - math.sqrt(v_2 - r2) } + + d := cx.mult_s(((f64(sx) + 0.5 + dx) * 0.5 + f64(x)) * w1 - .5) + + cy.mult_s(((f64(sy) + 0.5 + dy) * 0.5 + f64(y)) * h1 - .5) + cam.d + r = r + radiance(Ray{cam.o + + d.mult_s(140.0), d.norm()}, 0, scene_id).mult_s(samps1) + } + tmp_vec := Vec{clamp(r.x), clamp(r.y), clamp(r.z)}.mult_s(.25) + (*ivec) = *ivec + tmp_vec + } + } + } + } + return image +} + +fn main() { + if os.args.len > 6 { + eprintln('Usage:\n path_tracing [samples] [image.ppm] [scene_n] [width] [height]') + exit(1) + } + mut width := 320 // width of the rendering in pixels + mut height := 200 // height of the rendering in pixels + mut samples := 4 // number of samples per pixel, increase for better quality + mut scene_id := 0 // scene to render [0 cornell box,1 sunset,2 psyco] + mut file_name := 'image.ppm' // name of the output file in .ppm format + + if os.args.len >= 2 { + samples = os.args[1].int() / 4 + } + if os.args.len >= 3 { + file_name = os.args[2] + } + if os.args.len >= 4 { + scene_id = os.args[3].int() + } + if os.args.len >= 5 { + width = os.args[4].int() + } + if os.args.len == 6 { + height = os.args[5].int() + } + // change the seed for a different result + rand.seed([u32(2020), 0]) + + t1 := time.ticks() + + eprintln('Path tracing samples: $samples, file_name: $file_name, scene_id: $scene_id, width: $width, height: $height') + eprintln('') + image := ray_trace(width, height, samples, file_name, scene_id) + t2 := time.ticks() + + eprintln('Rendering finished. Took: ${(t2 - t1):5}ms') + + image.save_as_ppm(file_name) + t3 := time.ticks() + + eprintln('Image saved as [$file_name]. Took: ${(t3 - t2):5}ms') +} diff --git a/v_windows/v/old/examples/pendulum_sim/sim.v b/v_windows/v/old/examples/pendulum_sim/sim.v new file mode 100644 index 0000000..f8ef11d --- /dev/null +++ b/v_windows/v/old/examples/pendulum_sim/sim.v @@ -0,0 +1,366 @@ +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +// sim.v * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +// created by: jordan bonecutter * * * * * * * * * * * * * * * * * * * +// jpbonecutter@gmail.com * * * * * * * * * * * * * * * * * * * * * * +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +// +// I wrote the pendulum simulator to learn V, I think it could be a +// good addition to the examples directory. +// Essentially, the pendulum sim runs a simulation of a pendulum with +// a metallic tip swinging over three magnets. +// I run this simulation with the initial position at each pixel in an +// image and color the pixel according to the magnet over which it +// finally rests. +// I used some fun features in V like coroutines, channels, +// struct embedding, mutability, methods, and the like. +import math +import os +import term +import runtime + +// customisable through setting VJOBS +const parallel_workers = runtime.nr_jobs() + +const width = 800 + +const height = 600 + +struct Vec3D { + x f64 + y f64 + z f64 +} + +fn (v Vec3D) add(v2 Vec3D) Vec3D { + return Vec3D{ + x: v.x + v2.x + y: v.y + v2.y + z: v.z + v2.z + } +} + +fn (v Vec3D) dot(v2 Vec3D) f64 { + return (v.x * v2.x) + (v.y * v2.y) + (v.z * v2.z) +} + +fn (v Vec3D) scale(scalar f64) Vec3D { + return Vec3D{ + x: v.x * scalar + y: v.y * scalar + z: v.z * scalar + } +} + +fn (v Vec3D) norm_squared() f64 { + return v.dot(v) +} + +fn (v Vec3D) norm() f64 { + return math.sqrt(v.norm_squared()) +} + +struct SimState { +mut: + position Vec3D + velocity Vec3D + accel Vec3D +} + +// magnets lie at [ +// math.cos(index * 2 * math.pi / 3) * magnet_spacing +// math.sin(index * 2 * math.pi / 3) * magnet_spacing +// -magnet_height +// ] +struct SimParams { + rope_length f64 + bearing_mass f64 + magnet_spacing f64 + magnet_height f64 + magnet_strength f64 + gravity f64 +} + +fn (params SimParams) get_rope_vector(state SimState) Vec3D { + rope_origin := Vec3D{ + x: 0 + y: 0 + z: params.rope_length + } + + return state.position.add(rope_origin.scale(-1)) +} + +fn (mut state SimState) satisfy_rope_constraint(params SimParams) { + mut rope_vector := params.get_rope_vector(state) + rope_vector = rope_vector.scale(params.rope_length / rope_vector.norm()) + state.position = Vec3D{ + x: 0 + y: 0 + z: params.rope_length + }.add(rope_vector) +} + +fn (params SimParams) get_grav_force(state SimState) Vec3D { + return Vec3D{ + x: 0 + y: 0 + z: -params.bearing_mass * params.gravity + } +} + +fn (params SimParams) get_magnet_position(theta f64) Vec3D { + return Vec3D{ + x: math.cos(theta) * params.magnet_spacing + y: math.sin(theta) * params.magnet_spacing + z: -params.magnet_height + } +} + +fn (params SimParams) get_magnet_force(theta f64, state SimState) Vec3D { + magnet_position := params.get_magnet_position(theta) + mut diff := magnet_position.add(state.position.scale(-1)) + distance_squared := diff.norm_squared() + diff = diff.scale(1.0 / math.sqrt(distance_squared)) + return diff.scale(params.magnet_strength / distance_squared) +} + +fn (params SimParams) get_magnet_dist(theta f64, state SimState) f64 { + return params.get_magnet_position(theta).add(state.position.scale(-1)).norm() +} + +fn (params SimParams) get_magnet1_force(state SimState) Vec3D { + return params.get_magnet_force(0.0 * math.pi / 3.0, state) +} + +fn (params SimParams) get_magnet2_force(state SimState) Vec3D { + return params.get_magnet_force(2.0 * math.pi / 3.0, state) +} + +fn (params SimParams) get_magnet3_force(state SimState) Vec3D { + return params.get_magnet_force(4.0 * math.pi / 3.0, state) +} + +fn (params SimParams) get_tension_force(state SimState, f_passive Vec3D) Vec3D { + rope_vector := params.get_rope_vector(state) + rope_vector_norm := rope_vector.scale(1.0 / rope_vector.norm()) + return rope_vector_norm.scale(-1.0 * rope_vector_norm.dot(f_passive)) +} + +fn (mut state SimState) increment(delta_t f64, params SimParams) { + // basically just add up all forces => + // get an accelleration => + // add to velocity => + // ensure rope constraint is satisfied + + // force due to gravity + f_gravity := params.get_grav_force(state) + + // force due to each magnet + f_magnet1 := params.get_magnet1_force(state) + + // force due to each magnet + f_magnet2 := params.get_magnet2_force(state) + + // force due to each magnet + f_magnet3 := params.get_magnet3_force(state) + + // passive forces + f_passive := f_gravity.add(f_magnet1.add(f_magnet2.add(f_magnet3))) + + // force due to tension of the rope + f_tension := params.get_tension_force(state, f_passive) + + // sum up all the fores + f_sum := f_tension.add(f_passive) + + // get the acceleration + accel := f_sum.scale(1.0 / params.bearing_mass) + state.accel = accel + + // update the velocity + state.velocity = state.velocity.add(accel.scale(delta_t)) + + // update the position + state.position = state.position.add(state.velocity.scale(delta_t)) + + // ensure the position satisfies rope constraint + state.satisfy_rope_constraint(params) +} + +fn (state SimState) done() bool { + return state.velocity.norm() < 0.05 && state.accel.norm() < 0.01 +} + +struct PPMWriter { +mut: + file os.File +} + +struct ImageSettings { + width int + height int +} + +struct Pixel { + r byte + g byte + b byte +} + +fn (mut writer PPMWriter) start_for_file(fname string, settings ImageSettings) { + writer.file = os.create(fname) or { panic("can't create file $fname") } + writer.file.writeln('P6 $settings.width $settings.height 255') or {} +} + +fn (mut writer PPMWriter) next_pixel(p Pixel) { + writer.file.write([p.r, p.g, p.b]) or {} +} + +fn (mut writer PPMWriter) finish() { + writer.file.close() +} + +fn sim_runner(mut state SimState, params SimParams) Pixel { + // do the simulation! + for _ in 0 .. 1000 { + state.increment(0.0005, params) + if state.done() { + println('done!') + break + } + } + + // find the closest magnet + m1_dist := params.get_magnet_dist(0, state) + m2_dist := params.get_magnet_dist(2.0 * math.pi / 3.0, state) + m3_dist := params.get_magnet_dist(4.0 * math.pi / 3.0, state) + + if m1_dist < m2_dist && m1_dist < m3_dist { + return Pixel{ + r: 255 + g: 0 + b: 0 + } + } else if m2_dist < m1_dist && m2_dist < m3_dist { + return Pixel{ + r: 0 + g: 255 + b: 0 + } + } else { + return Pixel{ + r: 0 + g: 0 + b: 255 + } + } +} + +struct SimResult { + id u64 + p Pixel +} + +struct SimRequest { + id u64 + params SimParams +mut: + initial SimState +} + +fn sim_worker(request_chan chan SimRequest, result_chan chan SimResult) { + // serve sim requests as they come in + for { + mut request := <-request_chan or { break } + + result_chan <- SimResult{ + id: request.id + p: sim_runner(mut request.initial, request.params) + } + } +} + +struct ValidPixel { + Pixel +mut: + valid bool +} + +fn image_worker(mut writer PPMWriter, result_chan chan SimResult, total_pixels u64) { + // as new pixels come in, write them to the image file + mut current_index := u64(0) + mut pixel_buf := []ValidPixel{len: int(total_pixels), init: ValidPixel{ + valid: false + }} + for { + result := <-result_chan or { break } + pixel_buf[result.id].Pixel = result.p + pixel_buf[result.id].valid = true + + for current_index < total_pixels && pixel_buf[current_index].valid { + writer.next_pixel(pixel_buf[current_index].Pixel) + current_index++ + } + + if current_index >= total_pixels { + break + } + } +} + +fn main() { + params := SimParams{ + rope_length: 0.25 + bearing_mass: 0.03 + magnet_spacing: 0.05 + magnet_height: 0.03 + magnet_strength: 10.0 + gravity: 4.9 + } + + mut writer := PPMWriter{} + writer.start_for_file('test.ppm', ImageSettings{ + width: width + height: height + }) + defer { + writer.finish() + } + + result_chan := chan SimResult{} + request_chan := chan SimRequest{} + + // start a worker on each core + for _ in 0 .. parallel_workers { + go sim_worker(request_chan, result_chan) + } + + go fn (request_chan chan SimRequest, params SimParams) { + mut index := u64(0) + println('') + for y in 0 .. height { + term.clear_previous_line() + println('Line: $y') + for x in 0 .. width { + // setup initial conditions + mut state := SimState{} + state.position = Vec3D{ + x: 0.1 * ((f64(x) - 0.5 * f64(width - 1)) / f64(width - 1)) + y: 0.1 * ((f64(y) - 0.5 * f64(height - 1)) / f64(height - 1)) + z: 0.0 + } + state.velocity = Vec3D{} + state.satisfy_rope_constraint(params) + request_chan <- SimRequest{ + id: index + initial: state + params: params + } + index++ + } + } + request_chan.close() + }(request_chan, params) + + image_worker(mut writer, result_chan, width * height) +} diff --git a/v_windows/v/old/examples/pico/pico.v b/v_windows/v/old/examples/pico/pico.v new file mode 100644 index 0000000..8a8a636 --- /dev/null +++ b/v_windows/v/old/examples/pico/pico.v @@ -0,0 +1,52 @@ +import json +import picoev +import picohttpparser + +const ( + port = 8088 +) + +struct Message { + message string +} + +[inline] +fn json_response() string { + msg := Message{ + message: 'Hello, World!' + } + return json.encode(msg) +} + +[inline] +fn hello_response() string { + return 'Hello, World!' +} + +fn callback(data voidptr, req picohttpparser.Request, mut res picohttpparser.Response) { + if picohttpparser.cmpn(req.method, 'GET ', 4) { + if picohttpparser.cmp(req.path, '/t') { + res.http_ok() + res.header_server() + res.header_date() + res.plain() + res.body(hello_response()) + } else if picohttpparser.cmp(req.path, '/j') { + res.http_ok() + res.header_server() + res.header_date() + res.json() + res.body(json_response()) + } else { + res.http_404() + } + } else { + res.http_405() + } + res.end() +} + +fn main() { + println('Starting webserver on http://127.0.0.1:$port/ ...') + picoev.new(port: port, cb: &callback).serve() +} diff --git a/v_windows/v/old/examples/process/.ignore b/v_windows/v/old/examples/process/.ignore new file mode 100644 index 0000000..e42252a --- /dev/null +++ b/v_windows/v/old/examples/process/.ignore @@ -0,0 +1 @@ +command \ No newline at end of file diff --git a/v_windows/v/old/examples/process/command.v b/v_windows/v/old/examples/process/command.v new file mode 100644 index 0000000..718ce96 --- /dev/null +++ b/v_windows/v/old/examples/process/command.v @@ -0,0 +1,34 @@ +module main + +import os + +// basic example which shows how to use the Command function + +fn exec(path string) string { + mut out := '' + mut line := '' + mut cmd := os.Command{ + path: path + } + cmd.start() or { panic(err) } + + for { + line = cmd.read_line() + println(line) + out += line + if cmd.eof { + return out + } + } + return out +} + +fn main() { + mut out := '' + exec("bash -c 'find /tmp/'") + out = exec('echo to stdout') + out = exec('echo to stderr 1>&2') + println("'$out'") + // THIS DOES NOT WORK, is error, it goes to stderror of the command I run + assert out == 'to stderr' +} diff --git a/v_windows/v/old/examples/process/execve.v b/v_windows/v/old/examples/process/execve.v new file mode 100644 index 0000000..f840c8d --- /dev/null +++ b/v_windows/v/old/examples/process/execve.v @@ -0,0 +1,17 @@ +module main + +import os + +fn exec(args []string) { + os.execve('/bin/bash', args, []) or { + // eprintln(err) + panic(err) + } +} + +fn main() { + // exec(["-c","find /"]) //works + exec(['-c', 'find /tmp/']) // here it works as well + + // exec(["-c","find","/tmp/"]) // does not work I guess is normal +} diff --git a/v_windows/v/old/examples/process/process_script.v b/v_windows/v/old/examples/process/process_script.v new file mode 100644 index 0000000..fa415e7 --- /dev/null +++ b/v_windows/v/old/examples/process/process_script.v @@ -0,0 +1,59 @@ +module main + +import os + +// a test where we execute a bash script but work around where we put script in bash inside bash + +fn exec(path string, redirect bool) { + mut line := '' + mut line_err := '' + mut cmd := os.new_process('/bin/bash') + + if redirect { + cmd.set_args(['-c', '/bin/bash /tmp/test.sh 2>&1']) + } else { + cmd.set_args([path]) + } + + cmd.set_redirect_stdio() + cmd.run() + if cmd.is_alive() { + for { + line = cmd.stdout_read() + println('STDOUT: $line') + + if !redirect { + line_err = cmd.stderr_read() + println('STDERR: $line_err') + } + + if !cmd.is_alive() { + break + } + } + } + if cmd.code > 0 { + println('ERROR:') + println(cmd) + // println(cmd.stderr_read()) + } +} + +fn main() { + script := ' +echo line 1 +#will use some stderr now +echo redirect 1 to 2 1>&2 +echo line 3 +' + + os.write_file('/tmp/test.sh', script) or { panic(err) } + // os.chmod("/tmp/test.sh",0o700) //make executable + + // this will work because stderr/stdout are smaller than 4096 chars, once larger there can be deadlocks + // in other words this can never work reliably without being able to check if there is data on stderr or stdout + exec('/tmp/test.sh', false) + + // this will always work + exec('/tmp/test.sh', true) +} diff --git a/v_windows/v/old/examples/process/process_stdin_trick.v b/v_windows/v/old/examples/process/process_stdin_trick.v new file mode 100644 index 0000000..7a1455d --- /dev/null +++ b/v_windows/v/old/examples/process/process_stdin_trick.v @@ -0,0 +1,83 @@ +module main + +import os + +// this is a example script to show you stdin can be used and keep a process open + +fn exec(cmd string) (string, int) { + mut cmd2 := cmd + mut out := '' + mut line := '' + mut rc := 0 + mut p := os.new_process('/bin/bash') + + // there are methods missing to know if stderr/stdout has data as such its better to redirect bot on same FD + // not so nice trick to run bash in bash and redirect stderr, maybe someone has a better solution + p.set_args(['-c', 'bash 2>&1']) + p.set_redirect_stdio() + p.run() + + if !cmd2.ends_with('\n') { + cmd2 += '\n' + } + + p.stdin_write(cmd2) + p.stdin_write('\necho **OK**\n') + + for { + if !p.is_alive() { + break + } + line = p.stdout_read() + println(line) + // line_err = p.stderr_read() //IF WE CALL STDERR_READ will block + // we need a mechanism which allows us to check if stderr/stdout has data or it should never block + // is not a good way, need to use a string buffer, is slow like this + out += line + if out.ends_with('**OK**\n') { + out = out[0..(out.len - 7)] + break + } + } + + // println("read from stdout, should not block") + // is not really needed but good test to see behaviour + // out += p.stdout_read() + // println("read done") + + // println(cmd.stderr_read()) + + if p.code > 0 { + rc = 1 + println('ERROR:') + println(cmd2) + print(out) + } + // documentation says we need to call p.wait(), but this does not seem to work, will be process stop or become zombie? + // p.wait() + + return out, rc +} + +fn main() { + mut out := '' + mut rc := 0 + + // the following does not work, not sure why not + // out,rc = exec("find /tmp/ && echo '******'") + + out, rc = exec("find /tmp/ ; echo '******'") + println(out) + assert out.ends_with('******\n') + + out, rc = exec('echo to stdout') + assert out.contains('to stdout') + + out, rc = exec('echo to stderr 1>&2') + assert out.contains('to stderr') + + out, rc = exec('ls /sssss') + assert rc > 0 // THIS STILL GIVES AN ERROR ! + + println('test ok stderr & stdout is indeed redirected') +} diff --git a/v_windows/v/old/examples/quick_sort.v b/v_windows/v/old/examples/quick_sort.v new file mode 100644 index 0000000..f890749 --- /dev/null +++ b/v_windows/v/old/examples/quick_sort.v @@ -0,0 +1,42 @@ +import rand + +const ( + gen_len = 1000 // how many random numbers to generate + gen_max = 10000 // max of the generated numbers +) + +fn main() { + mut arr := []int{} + for _ in 0 .. gen_len { + arr << rand.intn(gen_max) + } + println('length of random array is $arr.len') + println('before quick sort whether array is sorted: ${is_sorted(arr)}') + quick_sort(mut arr, 0, arr.len - 1) + println('after quick sort whether array is sorted: ${is_sorted(arr)}') +} + +fn quick_sort(mut arr []T, l int, r int) { + if l >= r { + return + } + mut sep := l // what is sep: [...all_value=arr[sep]...] + for i in l + 1 .. r + 1 { + if arr[i] < arr[l] { + sep++ + arr[i], arr[sep] = arr[sep], arr[i] + } + } + arr[l], arr[sep] = arr[sep], arr[l] + quick_sort(mut arr, l, sep - 1) + quick_sort(mut arr, sep + 1, r) +} + +fn is_sorted(arr []T) bool { + for i in 0 .. arr.len - 1 { + if arr[i] > arr[i + 1] { + return false + } + } + return true +} diff --git a/v_windows/v/old/examples/random_ips.v b/v_windows/v/old/examples/random_ips.v new file mode 100644 index 0000000..59c4eff --- /dev/null +++ b/v_windows/v/old/examples/random_ips.v @@ -0,0 +1,7 @@ +import rand + +fn main() { + for _ in 0 .. 10 { + println('${rand.intn(255)}.${rand.intn(255)}.${rand.intn(255)}.${rand.intn(255)}') + } +} diff --git a/v_windows/v/old/examples/regex/pcre.vv b/v_windows/v/old/examples/regex/pcre.vv new file mode 100644 index 0000000..72beaf5 --- /dev/null +++ b/v_windows/v/old/examples/regex/pcre.vv @@ -0,0 +1,69 @@ +module main + +// NB: you need to `v install pcre` to be able to compile this example. + +import pcre + +fn example() { + r := pcre.new_regex('Match everything after this: (.+)', 0) or { + println('An error occured!') + return + } + + m := r.match_str('Match everything after this: "I ❤️ VLang!"', 0, 0) or { + println('No match!') + return + } + + // m.get(0) -> Match everything after this: "I ❤️ VLang!" + // m.get(1) -> "I ❤️ VLang!"' + // m.get(2) -> Error! + whole_match := m.get(0) or { + println('We matched nothing...') + return + } + + matched_str := m.get(1) or { + println('We matched nothing...') + return + } + + println(whole_match) // Match everything after this: "I ❤️ VLang!" + println(matched_str) // "I ❤️ VLang!" +} + +fn main() { + example() + + mut text := '[ an s. s! ]( wi4ki:something ) + [ an s. s! ]( wi4ki:something ) + [ an s. s! ](wiki:something) + [ an s. s! ](something)dd + d [ an s. s! ](something ) d + [ more text ]( something ) s [ something b ](something)dd + + ' + + // check the regex on https://regex101.com/r/HdYya8/1/ + + regex := r'(\[[a-z\.\! ]*\]\( *\w*\:*\w* *\))*' + + r := pcre.new_regex(regex, 0) or { + println('An error occured!') + return + } + + m := r.match_str(text, 0, 0) or { + println('No match!') + return + } + + whole_match1 := m.get(0) or { + println('We matched nothing 0...') + return + } + + println(whole_match1) + + println(m.get_all()) +} diff --git a/v_windows/v/old/examples/regex/readme.md b/v_windows/v/old/examples/regex/readme.md new file mode 100644 index 0000000..3559564 --- /dev/null +++ b/v_windows/v/old/examples/regex/readme.md @@ -0,0 +1,8 @@ +# regex + +There are 2 ways to do regex: +a) using the native module called `regex` +b) using an exteranl module called `pcre`, which wraps the C library pcre. +NB: you need to first do: `v install pcre`, for the `pcre` module to work. + +You can find examples of both in this directory. diff --git a/v_windows/v/old/examples/regex/regex_example.v b/v_windows/v/old/examples/regex/regex_example.v new file mode 100644 index 0000000..7469ef5 --- /dev/null +++ b/v_windows/v/old/examples/regex/regex_example.v @@ -0,0 +1,80 @@ +/********************************************************************** +* regex samples +* +* Copyright (c) 2019-2021 Dario Deledda. All rights reserved. +* Use of this source code is governed by an MIT license +* that can be found in the LICENSE file. +* +* This file contains a collection of regex samples +* +**********************************************************************/ +import regex + +/* +This simple function converts an HTML RGB value with 3 or 6 hex digits to a u32 value, +this function is not optimized and it is only for didatical purpose +example: #A0B0CC #A9F +*/ +fn convert_html_rgb(in_col string) u32 { + mut n_digit := if in_col.len == 4 { 1 } else { 2 } + mut col_mul := if in_col.len == 4 { 4 } else { 0 } + + // this is the regex query, it uses V string interpolation to customize the regex query + // NOTE: if you want use escaped code you must use the r"" (raw) strings, + // *** please remember that V interpoaltion doesn't work on raw strings. *** + + query := '#([a-fA-F0-9]{$n_digit})([a-fA-F0-9]{$n_digit})([a-fA-F0-9]{$n_digit})' + + mut re := regex.regex_opt(query) or { panic(err) } + start, end := re.match_string(in_col) + println('start: $start, end: $end') + mut res := u32(0) + if start >= 0 { + group_list := re.get_group_list() + r := ('0x' + in_col[group_list[0].start..group_list[0].end]).int() << col_mul + g := ('0x' + in_col[group_list[1].start..group_list[1].end]).int() << col_mul + b := ('0x' + in_col[group_list[2].start..group_list[2].end]).int() << col_mul + println('r: $r g: $g b: $b') + res = u32(r) << 16 | u32(g) << 8 | u32(b) + } + return res +} + +/* +This function demonstrates the use of the named groups +*/ +fn convert_html_rgb_n(in_col string) u32 { + mut n_digit := if in_col.len == 4 { 1 } else { 2 } + mut col_mul := if in_col.len == 4 { 4 } else { 0 } + + query := '#(?P[a-fA-F0-9]{$n_digit})(?P[a-fA-F0-9]{$n_digit})(?P[a-fA-F0-9]{$n_digit})' + + mut re := regex.regex_opt(query) or { panic(err) } + start, end := re.match_string(in_col) + println('start: $start, end: $end') + mut res := u32(0) + if start >= 0 { + red_s, red_e := re.get_group_bounds_by_name('red') + r := ('0x' + in_col[red_s..red_e]).int() << col_mul + + green_s, green_e := re.get_group_bounds_by_name('green') + g := ('0x' + in_col[green_s..green_e]).int() << col_mul + + blue_s, blue_e := re.get_group_bounds_by_name('blue') + b := ('0x' + in_col[blue_s..blue_e]).int() << col_mul + + println('r: $r g: $g b: $b') + res = u32(r) << 16 | u32(g) << 8 | u32(b) + } + return res +} + +fn main() { + // convert HTML rgb color using groups + println(convert_html_rgb('#A0b0Cc').hex()) + println(convert_html_rgb('#ABC').hex()) + + // convert HTML rgb color using named groups + println(convert_html_rgb_n('#A0B0CC').hex()) + println(convert_html_rgb_n('#ABC').hex()) +} diff --git a/v_windows/v/old/examples/regex/regex_with_memoization.v b/v_windows/v/old/examples/regex/regex_with_memoization.v new file mode 100644 index 0000000..28e346b --- /dev/null +++ b/v_windows/v/old/examples/regex/regex_with_memoization.v @@ -0,0 +1,127 @@ +import os + +fn regex_match(src string, pat string) bool { + src_size := src.len + 1 + pat_size := pat.len + 1 + mut memo := [][]int{len: src_size, init: []int{len: pat_size, init: -1}} + return regex_match_core(src, pat, 0, 0, mut memo) +} + +fn regex_match_core(src string, pat string, src_pos int, pat_pos int, mut memo [][]int) bool { + if memo[src_pos][pat_pos] != -1 { + return memo[src_pos][pat_pos] == 1 + } + mut spos := src_pos + mut ppos := pat_pos + if spos >= src.len && ppos >= pat.len { + memo[src_pos][pat_pos] = 1 + return true + } else if spos < src.len && ppos >= pat.len { + memo[src_pos][pat_pos] = 0 + return false + } else if spos >= src.len && ppos < pat.len { + if pat[ppos] == `\\` { + ppos++ + } + res := ppos + 1 < pat.len && pat[ppos + 1] in [`*`, `?`] + && regex_match_core(src, pat, spos, ppos + 2, mut memo) + memo[src_pos][pat_pos] = if res { 1 } else { 0 } + return res + } else { + first_is_bslash := pat[ppos] == `\\` + if first_is_bslash { + ppos++ + } + first_bslash_and_match := first_is_bslash && ppos < pat.len + && (((pat[ppos] == `d` && src[spos].is_digit()) + || (pat[ppos] == `D` && !src[spos].is_digit()) + || (pat[ppos] == `s` && src[spos].is_space()) + || (pat[ppos] == `S` && !src[spos].is_space()) + || (pat[ppos] == `w` && (src[spos].is_digit() || src[spos].is_letter() + || src[spos] == `_`)) || (pat[ppos] == `W` && !(src[spos].is_digit() + || src[spos].is_letter() || src[spos] == `_`))) + || (pat[ppos] in [`d`, `D`, `s`, `S`, `w`, `W`] && ppos + 1 < pat.len + && pat[ppos + 1] in [`*`, `?`, `+`]) + || (pat[ppos] !in [`d`, `D`, `s`, `S`, `w`, `W`] && src[spos] == pat[ppos])) + if ppos + 1 < pat.len { + match pat[ppos + 1] { + `*` { + if first_bslash_and_match { + res := regex_match_core(src, pat, spos + 1, ppos - 1, mut memo) + || regex_match_core(src, pat, spos, ppos + 2, mut memo) + memo[src_pos][pat_pos] = if res { 1 } else { 0 } + return res + } else if src[spos] == pat[ppos] || pat[ppos] == `.` { + res := regex_match_core(src, pat, spos + 1, ppos, mut memo) + || regex_match_core(src, pat, spos, ppos + 2, mut memo) + memo[src_pos][pat_pos] = if res { 1 } else { 0 } + return res + } else { + res := regex_match_core(src, pat, spos, ppos + 2, mut memo) + memo[src_pos][pat_pos] = if res { 1 } else { 0 } + return res + } + } + `+` { + if first_bslash_and_match { + res := regex_match_core(src, pat, spos + 1, ppos - 1, mut memo) + || regex_match_core(src, pat, spos + 1, ppos + 2, mut memo) + memo[src_pos][pat_pos] = if res { 1 } else { 0 } + return res + } else if src[spos] == pat[ppos] || pat[ppos] == `.` { + res := regex_match_core(src, pat, spos + 1, ppos, mut memo) + || regex_match_core(src, pat, spos + 1, ppos + 2, mut memo) + memo[src_pos][pat_pos] = if res { 1 } else { 0 } + return res + } else { + memo[src_pos][pat_pos] = 0 + return false + } + } + `?` { + if first_bslash_and_match || src[spos] == pat[ppos] || pat[ppos] == `.` { + res := regex_match_core(src, pat, spos + 1, ppos + 2, mut memo) + || regex_match_core(src, pat, spos, ppos + 2, mut memo) + memo[src_pos][pat_pos] = if res { 1 } else { 0 } + return res + } else { + res := regex_match_core(src, pat, spos, ppos + 2, mut memo) + memo[src_pos][pat_pos] = if res { 1 } else { 0 } + return res + } + } + else {} + } + } + if first_is_bslash { + res := first_bslash_and_match + && regex_match_core(src, pat, spos + 1, ppos + 1, mut memo) + memo[src_pos][pat_pos] = if res { 1 } else { 0 } + return res + } else { + res := (src[spos] == pat[ppos] || pat[ppos] == `.`) && pat[ppos] != `\\` + && regex_match_core(src, pat, spos + 1, ppos + 1, mut memo) + memo[src_pos][pat_pos] = if res { 1 } else { 0 } + return res + } + } +} + +fn main() { + mut cnt := 0 + println('currently supported patterns: . ? + * \\ \\d \\D \\s \\S \\w \\W') + println('example: source `address@domain.net` matches pattern `\\w+@domain\\.net`') + println('enter `exit` to quit\n') + for { + cnt++ + src := os.input('[$cnt] enter source string: ') + if src == 'exit' { + break + } + pat := os.input('[$cnt] enter pattern string: ') + if pat == 'exit' { + break + } + println('[$cnt] whether `$src` matches `$pat`: ${regex_match(src, pat)}') + } +} diff --git a/v_windows/v/old/examples/rune.v b/v_windows/v/old/examples/rune.v new file mode 100644 index 0000000..f30b671 --- /dev/null +++ b/v_windows/v/old/examples/rune.v @@ -0,0 +1,9 @@ +fn main() { + // GRINNING FACE😀 => f0 09 98 80 + grinning_face := rune(0xf09f9880) + println(grinning_face) + + // COMMERCIAL AT@ => 0x40 + commercial_at := rune(0x40000000) + println(commercial_at) +} diff --git a/v_windows/v/old/examples/smtp/mail.v b/v_windows/v/old/examples/smtp/mail.v new file mode 100644 index 0000000..c26be33 --- /dev/null +++ b/v_windows/v/old/examples/smtp/mail.v @@ -0,0 +1,36 @@ +// Creator: nedimf (07/2020) +import os +import net.smtp + +fn main() { + println('Hi, this is sample of how to send email trough net.smtp library in V, which is really easy using the net.smtp module.') + println('We are going to create a simple email client, that takes some arguments. and then sends email with an HTML body.') + println('To fully test email sending, I suggest using the mailtrap.io service, which is free and acts like a really nice mail server sandbox.') + println('') + println('V Email client') + println('') + mailserver := os.input('Mail server: ') + mailport := os.input('Mail server port: ').int() + println('Login') + username := os.input('Username: ') + password := os.input('Password: ') + from := os.input('From: ') + to := os.input('To: ') + subject := os.input('Subject: ') + body := os.input('Body: ') + client_cfg := smtp.Client{ + server: mailserver + from: from + port: mailport + username: username + password: password + } + send_cfg := smtp.Mail{ + to: to + subject: subject + body_type: .html + body: body + } + mut client := smtp.new_client(client_cfg) or { panic('Error configuring smtp') } + client.send(send_cfg) or { panic('Error resolving email address') } +} diff --git a/v_windows/v/old/examples/snek/snek.v b/v_windows/v/old/examples/snek/snek.v new file mode 100644 index 0000000..1d66fb1 --- /dev/null +++ b/v_windows/v/old/examples/snek/snek.v @@ -0,0 +1,221 @@ +import os +import gg +import gx +// import sokol.sapp +import time +import rand + +// constants +const ( + top_height = 100 + canvas_size = 700 + game_size = 17 + tile_size = canvas_size / game_size + tick_rate_ms = 100 +) + +const high_score_file_path = os.join_path(os.cache_dir(), 'v', 'examples', 'snek') + +// types +struct Pos { + x int + y int +} + +fn (a Pos) + (b Pos) Pos { + return Pos{a.x + b.x, a.y + b.y} +} + +fn (a Pos) - (b Pos) Pos { + return Pos{a.x - b.x, a.y - b.y} +} + +enum Direction { + up + down + left + right +} + +type HighScore = int + +fn (mut h HighScore) save() { + os.mkdir_all(os.dir(high_score_file_path)) or { return } + os.write_file(high_score_file_path, (*h).str()) or { return } +} + +fn (mut h HighScore) load() { + h = (os.read_file(high_score_file_path) or { '' }).int() +} + +struct App { +mut: + gg &gg.Context + score int + best HighScore + snake []Pos + dir Direction + food Pos + start_time i64 + last_tick i64 +} + +// utility +fn (mut app App) reset_game() { + app.score = 0 + app.snake = [ + Pos{3, 8}, + Pos{2, 8}, + Pos{1, 8}, + Pos{0, 8}, + ] + app.dir = .right + app.food = Pos{10, 8} + app.start_time = time.ticks() + app.last_tick = time.ticks() +} + +fn (mut app App) move_food() { + for { + x := rand.int_in_range(0, game_size) + y := rand.int_in_range(0, game_size) + app.food = Pos{x, y} + + if app.food !in app.snake { + return + } + } +} + +// events +fn on_keydown(key gg.KeyCode, mod gg.Modifier, mut app App) { + match key { + .w, .up { + if app.dir != .down { + app.dir = .up + } + } + .s, .down { + if app.dir != .up { + app.dir = .down + } + } + .a, .left { + if app.dir != .right { + app.dir = .left + } + } + .d, .right { + if app.dir != .left { + app.dir = .right + } + } + else {} + } +} + +fn on_frame(mut app App) { + app.gg.begin() + + now := time.ticks() + + if now - app.last_tick >= tick_rate_ms { + app.last_tick = now + + // finding delta direction + delta_dir := match app.dir { + .up { Pos{0, -1} } + .down { Pos{0, 1} } + .left { Pos{-1, 0} } + .right { Pos{1, 0} } + } + + // "snaking" along + mut prev := app.snake[0] + app.snake[0] = app.snake[0] + delta_dir + + for i in 1 .. app.snake.len { + tmp := app.snake[i] + app.snake[i] = prev + prev = tmp + } + + // adding last segment + if app.snake[0] == app.food { + app.move_food() + app.score++ + if app.score > app.best { + app.best = app.score + app.best.save() + } + app.snake << app.snake.last() + app.snake.last() - app.snake[app.snake.len - 2] + } + } + // drawing snake + for pos in app.snake { + app.gg.draw_rect(tile_size * pos.x, tile_size * pos.y + top_height, tile_size, + tile_size, gx.blue) + } + + // drawing food + app.gg.draw_rect(tile_size * app.food.x, tile_size * app.food.y + top_height, tile_size, + tile_size, gx.red) + + // drawing top + app.gg.draw_rect(0, 0, canvas_size, top_height, gx.black) + app.gg.draw_text(150, top_height / 2, 'Score: $app.score', gx.TextCfg{ + color: gx.white + align: .center + vertical_align: .middle + size: 65 + }) + app.gg.draw_text(canvas_size - 150, top_height / 2, 'Best: $app.best', gx.TextCfg{ + color: gx.white + align: .center + vertical_align: .middle + size: 65 + }) + + // checking if snake bit itself + if app.snake[0] in app.snake[1..] { + app.reset_game() + } + // checking if snake hit a wall + if app.snake[0].x < 0 || app.snake[0].x >= game_size || app.snake[0].y < 0 + || app.snake[0].y >= game_size { + app.reset_game() + } + + app.gg.end() +} + +const font = $embed_file('../assets/fonts/RobotoMono-Regular.ttf') + +// setup +fn main() { + mut app := App{ + gg: 0 + } + app.reset_game() + app.best.load() + + mut font_copy := font + font_bytes := unsafe { + font_copy.data().vbytes(font_copy.len) + } + + app.gg = gg.new_context( + bg_color: gx.white + frame_fn: on_frame + keydown_fn: on_keydown + user_data: &app + width: canvas_size + height: top_height + canvas_size + create_window: true + resizable: false + window_title: 'snek' + font_bytes_normal: font_bytes + ) + + app.gg.run() +} diff --git a/v_windows/v/old/examples/sokol/01_cubes/cube.v b/v_windows/v/old/examples/sokol/01_cubes/cube.v new file mode 100644 index 0000000..c933dbf --- /dev/null +++ b/v_windows/v/old/examples/sokol/01_cubes/cube.v @@ -0,0 +1,432 @@ +/********************************************************************** +* +* Sokol 3d cube demo +* +* Copyright (c) 2021 Dario Deledda. All rights reserved. +* Use of this source code is governed by an MIT license +* that can be found in the LICENSE file. +* +* TODO: +* - add instancing +* - add an exampel with shaders +**********************************************************************/ +import gg +import gx +import math +import sokol.sapp +import sokol.gfx +import sokol.sgl + +const ( + win_width = 800 + win_height = 800 + bg_color = gx.white +) + +struct App { +mut: + gg &gg.Context + pip_3d C.sgl_pipeline + texture C.sg_image + init_flag bool + frame_count int + mouse_x int = -1 + mouse_y int = -1 +} + +/****************************************************************************** +* +* Texture functions +* +******************************************************************************/ +fn create_texture(w int, h int, buf &u8) C.sg_image { + sz := w * h * 4 + mut img_desc := C.sg_image_desc{ + width: w + height: h + num_mipmaps: 0 + min_filter: .linear + mag_filter: .linear + // usage: .dynamic + wrap_u: .clamp_to_edge + wrap_v: .clamp_to_edge + label: &byte(0) + d3d11_texture: 0 + } + // commen if .dynamic is enabled + img_desc.data.subimage[0][0] = C.sg_range{ + ptr: buf + size: size_t(sz) + } + + sg_img := C.sg_make_image(&img_desc) + return sg_img +} + +fn destroy_texture(sg_img C.sg_image) { + C.sg_destroy_image(sg_img) +} + +// Use only if usage: .dynamic is enabled +fn update_text_texture(sg_img C.sg_image, w int, h int, buf &byte) { + sz := w * h * 4 + mut tmp_sbc := C.sg_image_data{} + tmp_sbc.subimage[0][0] = C.sg_range{ + ptr: buf + size: size_t(sz) + } + C.sg_update_image(sg_img, &tmp_sbc) +} + +/****************************************************************************** +* +* Draw functions +* +******************************************************************************/ +fn draw_triangle() { + sgl.defaults() + sgl.begin_triangles() + sgl.v2f_c3b(0.0, 0.5, 255, 0, 0) + sgl.v2f_c3b(-0.5, -0.5, 0, 0, 255) + sgl.v2f_c3b(0.5, -0.5, 0, 255, 0) + sgl.end() +} + +// vertex specification for a cube with colored sides and texture coords +fn cube() { + sgl.begin_quads() + // edge color + sgl.c3f(1.0, 0.0, 0.0) + // edge coord + // x,y,z, texture cord: u,v + sgl.v3f_t2f(-1.0, 1.0, -1.0, -1.0, 1.0) + sgl.v3f_t2f(1.0, 1.0, -1.0, 1.0, 1.0) + sgl.v3f_t2f(1.0, -1.0, -1.0, 1.0, -1.0) + sgl.v3f_t2f(-1.0, -1.0, -1.0, -1.0, -1.0) + sgl.c3f(0.0, 1.0, 0.0) + sgl.v3f_t2f(-1.0, -1.0, 1.0, -1.0, 1.0) + sgl.v3f_t2f(1.0, -1.0, 1.0, 1.0, 1.0) + sgl.v3f_t2f(1.0, 1.0, 1.0, 1.0, -1.0) + sgl.v3f_t2f(-1.0, 1.0, 1.0, -1.0, -1.0) + sgl.c3f(0.0, 0.0, 1.0) + sgl.v3f_t2f(-1.0, -1.0, 1.0, -1.0, 1.0) + sgl.v3f_t2f(-1.0, 1.0, 1.0, 1.0, 1.0) + sgl.v3f_t2f(-1.0, 1.0, -1.0, 1.0, -1.0) + sgl.v3f_t2f(-1.0, -1.0, -1.0, -1.0, -1.0) + sgl.c3f(1.0, 0.5, 0.0) + sgl.v3f_t2f(1.0, -1.0, 1.0, -1.0, 1.0) + sgl.v3f_t2f(1.0, -1.0, -1.0, 1.0, 1.0) + sgl.v3f_t2f(1.0, 1.0, -1.0, 1.0, -1.0) + sgl.v3f_t2f(1.0, 1.0, 1.0, -1.0, -1.0) + sgl.c3f(0.0, 0.5, 1.0) + sgl.v3f_t2f(1.0, -1.0, -1.0, -1.0, 1.0) + sgl.v3f_t2f(1.0, -1.0, 1.0, 1.0, 1.0) + sgl.v3f_t2f(-1.0, -1.0, 1.0, 1.0, -1.0) + sgl.v3f_t2f(-1.0, -1.0, -1.0, -1.0, -1.0) + sgl.c3f(1.0, 0.0, 0.5) + sgl.v3f_t2f(-1.0, 1.0, -1.0, -1.0, 1.0) + sgl.v3f_t2f(-1.0, 1.0, 1.0, 1.0, 1.0) + sgl.v3f_t2f(1.0, 1.0, 1.0, 1.0, -1.0) + sgl.v3f_t2f(1.0, 1.0, -1.0, -1.0, -1.0) + sgl.end() +} + +fn draw_cubes(app App) { + rot := [f32(1.0) * (app.frame_count % 360), 0.5 * f32(app.frame_count % 360)] + // rot := [f32(app.mouse_x), f32(app.mouse_y)] + + sgl.defaults() + sgl.load_pipeline(app.pip_3d) + + sgl.matrix_mode_projection() + sgl.perspective(sgl.rad(45.0), 1.0, 0.1, 100.0) + + sgl.matrix_mode_modelview() + sgl.translate(0.0, 0.0, -12.0) + sgl.rotate(sgl.rad(rot[0]), 1.0, 0.0, 0.0) + sgl.rotate(sgl.rad(rot[1]), 0.0, 1.0, 0.0) + cube() + sgl.push_matrix() + sgl.translate(0.0, 0.0, 3.0) + sgl.scale(0.5, 0.5, 0.5) + sgl.rotate(-2.0 * sgl.rad(rot[0]), 1.0, 0.0, 0.0) + sgl.rotate(-2.0 * sgl.rad(rot[1]), 0.0, 1.0, 0.0) + cube() + sgl.push_matrix() + sgl.translate(0.0, 0.0, 3.0) + sgl.scale(0.5, 0.5, 0.5) + sgl.rotate(-3.0 * sgl.rad(2 * rot[0]), 1.0, 0.0, 0.0) + sgl.rotate(3.0 * sgl.rad(2 * rot[1]), 0.0, 0.0, 1.0) + cube() + sgl.pop_matrix() + sgl.pop_matrix() +} + +fn cube_t(r f32, g f32, b f32) { + sgl.begin_quads() + // edge color + sgl.c3f(r, g, b) + // edge coord + // x,y,z, texture cord: u,v + sgl.v3f_t2f(-1.0, 1.0, -1.0, 0.0, 0.25) + sgl.v3f_t2f(1.0, 1.0, -1.0, 0.25, 0.25) + sgl.v3f_t2f(1.0, -1.0, -1.0, 0.25, 0.0) + sgl.v3f_t2f(-1.0, -1.0, -1.0, 0.0, 0.0) + sgl.c3f(r, g, b) + sgl.v3f_t2f(-1.0, -1.0, 1.0, 0.0, 0.25) + sgl.v3f_t2f(1.0, -1.0, 1.0, 0.25, 0.25) + sgl.v3f_t2f(1.0, 1.0, 1.0, 0.25, 0.0) + sgl.v3f_t2f(-1.0, 1.0, 1.0, 0.0, 0.0) + sgl.c3f(r, g, b) + sgl.v3f_t2f(-1.0, -1.0, 1.0, 0.0, 0.25) + sgl.v3f_t2f(-1.0, 1.0, 1.0, 0.25, 0.25) + sgl.v3f_t2f(-1.0, 1.0, -1.0, 0.25, 0.0) + sgl.v3f_t2f(-1.0, -1.0, -1.0, 0.0, 0.0) + sgl.c3f(r, g, b) + sgl.v3f_t2f(1.0, -1.0, 1.0, 0.0, 0.25) + sgl.v3f_t2f(1.0, -1.0, -1.0, 0.25, 0.25) + sgl.v3f_t2f(1.0, 1.0, -1.0, 0.25, 0.0) + sgl.v3f_t2f(1.0, 1.0, 1.0, 0.0, 0.0) + sgl.c3f(r, g, b) + sgl.v3f_t2f(1.0, -1.0, -1.0, 0.0, 0.25) + sgl.v3f_t2f(1.0, -1.0, 1.0, 0.25, 0.25) + sgl.v3f_t2f(-1.0, -1.0, 1.0, 0.25, 0.0) + sgl.v3f_t2f(-1.0, -1.0, -1.0, 0.0, 0.0) + sgl.c3f(r, g, b) + sgl.v3f_t2f(-1.0, 1.0, -1.0, 0.0, 0.25) + sgl.v3f_t2f(-1.0, 1.0, 1.0, 0.25, 0.25) + sgl.v3f_t2f(1.0, 1.0, 1.0, 0.25, 0.0) + sgl.v3f_t2f(1.0, 1.0, -1.0, 0.0, 0.0) + sgl.end() +} + +fn draw_texture_cubes(app App) { + rot := [f32(app.mouse_x), f32(app.mouse_y)] + sgl.defaults() + sgl.load_pipeline(app.pip_3d) + + sgl.enable_texture() + sgl.texture(app.texture) + + sgl.matrix_mode_projection() + sgl.perspective(sgl.rad(45.0), 1.0, 0.1, 100.0) + + sgl.matrix_mode_modelview() + sgl.translate(0.0, 0.0, -12.0) + sgl.rotate(sgl.rad(rot[0]), 1.0, 0.0, 0.0) + sgl.rotate(sgl.rad(rot[1]), 0.0, 1.0, 0.0) + cube_t(1, 1, 1) + sgl.push_matrix() + sgl.translate(0.0, 0.0, 3.0) + sgl.scale(0.5, 0.5, 0.5) + sgl.rotate(-2.0 * sgl.rad(rot[0]), 1.0, 0.0, 0.0) + sgl.rotate(-2.0 * sgl.rad(rot[1]), 0.0, 1.0, 0.0) + cube_t(1, 1, 1) + sgl.push_matrix() + sgl.translate(0.0, 0.0, 3.0) + sgl.scale(0.5, 0.5, 0.5) + sgl.rotate(-3.0 * sgl.rad(2 * rot[0]), 1.0, 0.0, 0.0) + sgl.rotate(3.0 * sgl.rad(2 * rot[1]), 0.0, 0.0, 1.0) + cube_t(1, 1, 1) + sgl.pop_matrix() + sgl.pop_matrix() + + sgl.disable_texture() +} + +fn cube_field(app App) { + rot := [f32(app.mouse_x), f32(app.mouse_y)] + xyz_sz := f32(2.0) + field_size := 20 + + sgl.defaults() + sgl.load_pipeline(app.pip_3d) + + sgl.enable_texture() + sgl.texture(app.texture) + + sgl.matrix_mode_projection() + sgl.perspective(sgl.rad(45.0), 1.0, 0.1, 200.0) + + sgl.matrix_mode_modelview() + + sgl.translate(field_size, 0.0, -120.0) + sgl.rotate(sgl.rad(rot[0]), 0.0, 1.0, 0.0) + sgl.rotate(sgl.rad(rot[1]), 1.0, 0.0, 0.0) + + // draw field_size*field_size cubes + for y in 0 .. field_size { + for x in 0 .. field_size { + sgl.push_matrix() + z := f32(math.cos(f32(x * 2) / field_size) * math.sin(f32(y * 2) / field_size) * xyz_sz) * (xyz_sz * 5) + sgl.translate(x * xyz_sz, z, y * xyz_sz) + cube_t(f32(f32(x) / field_size), f32(f32(y) / field_size), 1) + sgl.pop_matrix() + } + } + sgl.disable_texture() +} + +fn frame(mut app App) { + ws := gg.window_size_real_pixels() + ratio := f32(ws.width) / ws.height + dw := ws.width + dh := ws.height + ww := int(dh / 3) // not a bug + hh := int(dh / 3) + x0 := int(f32(dw) * 0.05) + // x1 := dw/2 + y0 := 0 + y1 := int(f32(dh) * 0.5) + + app.gg.begin() + // sgl.defaults() + + // 2d triangle + sgl.viewport(x0, y0, ww, hh, true) + draw_triangle() + + // colored cubes with viewport + sgl.viewport(x0, y1, ww, hh, true) + draw_cubes(app) + + // textured cubed with viewport + sgl.viewport(0, int(dh / 5), dw, int(dh * ratio), true) + draw_texture_cubes(app) + + // textured field of cubes with viewport + sgl.viewport(0, int(dh / 5), dw, int(dh * ratio), true) + cube_field(app) + + app.frame_count++ + + app.gg.end() +} + +/****************************************************************************** +* +* Init / Cleanup +* +******************************************************************************/ +fn my_init(mut app App) { + app.init_flag = true + + // set max vertices, + // for a large number of the same type of object it is better use the instances!! + desc := sapp.create_desc() + gfx.setup(&desc) + sgl_desc := C.sgl_desc_t{ + max_vertices: 50 * 65536 + } + sgl.setup(&sgl_desc) + + // 3d pipeline + mut pipdesc := C.sg_pipeline_desc{} + unsafe { C.memset(&pipdesc, 0, sizeof(pipdesc)) } + + color_state := C.sg_color_state{ + blend: C.sg_blend_state{ + enabled: true + src_factor_rgb: gfx.BlendFactor(C.SG_BLENDFACTOR_SRC_ALPHA) + dst_factor_rgb: gfx.BlendFactor(C.SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA) + } + } + pipdesc.colors[0] = color_state + + pipdesc.depth = C.sg_depth_state{ + write_enabled: true + compare: gfx.CompareFunc(C.SG_COMPAREFUNC_LESS_EQUAL) + } + pipdesc.cull_mode = .back + app.pip_3d = sgl.make_pipeline(&pipdesc) + + // create chessboard texture 256*256 RGBA + w := 256 + h := 256 + sz := w * h * 4 + tmp_txt := unsafe { malloc(sz) } + mut i := 0 + for i < sz { + unsafe { + y := (i >> 0x8) >> 5 // 8 cell + x := (i & 0xFF) >> 5 // 8 cell + // upper left corner + if x == 0 && y == 0 { + tmp_txt[i] = byte(0xFF) + tmp_txt[i + 1] = byte(0) + tmp_txt[i + 2] = byte(0) + tmp_txt[i + 3] = byte(0xFF) + } + // low right corner + else if x == 7 && y == 7 { + tmp_txt[i] = byte(0) + tmp_txt[i + 1] = byte(0xFF) + tmp_txt[i + 2] = byte(0) + tmp_txt[i + 3] = byte(0xFF) + } else { + col := if ((x + y) & 1) == 1 { 0xFF } else { 0 } + tmp_txt[i] = byte(col) // red + tmp_txt[i + 1] = byte(col) // green + tmp_txt[i + 2] = byte(col) // blue + tmp_txt[i + 3] = byte(0xFF) // alpha + } + i += 4 + } + } + unsafe { + app.texture = create_texture(w, h, tmp_txt) + free(tmp_txt) + } +} + +fn cleanup(mut app App) { + gfx.shutdown() +} + +/****************************************************************************** +* +* event +* +******************************************************************************/ +fn my_event_manager(mut ev gg.Event, mut app App) { + if ev.typ == .mouse_move { + app.mouse_x = int(ev.mouse_x) + app.mouse_y = int(ev.mouse_y) + } + if ev.typ == .touches_began || ev.typ == .touches_moved { + if ev.num_touches > 0 { + touch_point := ev.touches[0] + app.mouse_x = int(touch_point.pos_x) + app.mouse_y = int(touch_point.pos_y) + } + } +} + +/****************************************************************************** +* +* Main +* +******************************************************************************/ +// is needed for easier diagnostics on windows +[console] +fn main() { + // App init + mut app := &App{ + gg: 0 + } + + app.gg = gg.new_context( + width: win_width + height: win_height + create_window: true + window_title: '3D Cube Demo' + user_data: app + bg_color: bg_color + frame_fn: frame + init_fn: my_init + cleanup_fn: cleanup + event_fn: my_event_manager + ) + + app.gg.run() +} diff --git a/v_windows/v/old/examples/sokol/02_cubes_glsl/cube_glsl.glsl b/v_windows/v/old/examples/sokol/02_cubes_glsl/cube_glsl.glsl new file mode 100644 index 0000000..e19e936 --- /dev/null +++ b/v_windows/v/old/examples/sokol/02_cubes_glsl/cube_glsl.glsl @@ -0,0 +1,95 @@ +//------------------------------------------------------------------------------ +// Shader code for texcube-sapp sample. +// +// NOTE: This source file also uses the '#pragma sokol' form of the +// custom tags. +//------------------------------------------------------------------------------ +//#pragma sokol @ctype mat4 my_mat4 + +#pragma sokol @vs vs +uniform vs_params { + mat4 mvp; +}; + +in vec4 pos; +in vec4 color0; +in vec2 texcoord0; + +out vec4 color; +out vec2 uv; + +void main() { + gl_Position = mvp * pos; + color = color0; + uv = texcoord0; +} +#pragma sokol @end + +#pragma sokol @fs fs +uniform sampler2D tex; +uniform fs_params { + vec2 text_res; + float iTime; +}; + +in vec4 color; +in vec2 uv; +out vec4 frag_color; + +//********************************************************* +// RAY TRACE +// original code from: https://www.shadertoy.com/view/ldS3DW +//********************************************************* +float sphere(vec3 ray, vec3 dir, vec3 center, float radius) +{ + vec3 rc = ray-center; + float c = dot(rc, rc) - (radius*radius); + float b = dot(dir, rc); + float d = b*b - c; + float t = -b - sqrt(abs(d)); + float st = step(0.0, min(t,d)); + return mix(-1.0, t, st); +} + +vec3 background(float t, vec3 rd) +{ + vec3 light = normalize(vec3(sin(t), 0.6, cos(t))); + float sun = max(0.0, dot(rd, light)); + float sky = max(0.0, dot(rd, vec3(0.0, 1.0, 0.0))); + float ground = max(0.0, -dot(rd, vec3(0.0, 1.0, 0.0))); + return (pow(sun, 256.0)+0.2*pow(sun, 2.0))*vec3(2.0, 1.6, 1.0) + + pow(ground, 0.5)*vec3(0.4, 0.3, 0.2) + + pow(sky, 1.0)*vec3(0.5, 0.6, 0.7); +} + +vec4 mainImage(vec2 fragCoord) +{ + vec2 uv = (fragCoord-vec2(0.4,0.4))*2.0; + + //vec2 uv = (-1.0 + 2.0*fc.xy / text_res.xy) * vec2(text_res.x/text_res.y, 1.0); + vec3 ro = vec3(0.0, 0.0, -3.0); + vec3 rd = normalize(vec3(uv, 1.0)); + vec3 p = vec3(0.0, 0.0, 0.0); + float t = sphere(ro, rd, p, 1.0); + vec3 nml = normalize(p - (ro+rd*t)); + vec3 bgCol = background(iTime, rd); + rd = reflect(rd, nml); + vec3 col = background(iTime, rd) * vec3(0.9, 0.8, 1.0); + vec4 fragColor = vec4( mix(bgCol, col, step(0.0, t)), 1.0 ); + return fragColor; +} +//********************************************************* +//********************************************************* + +void main() { + vec4 c = color; + vec4 txt = texture(tex, uv/4.0); + c = txt * c; + vec4 col_ray = mainImage(uv); + float txt_mix = mod(iTime,5); + frag_color = c*txt_mix*0.1 + col_ray ; +} + +#pragma sokol @end + +#pragma sokol @program cube vs fs diff --git a/v_windows/v/old/examples/sokol/02_cubes_glsl/cube_glsl.v b/v_windows/v/old/examples/sokol/02_cubes_glsl/cube_glsl.v new file mode 100644 index 0000000..0be09a4 --- /dev/null +++ b/v_windows/v/old/examples/sokol/02_cubes_glsl/cube_glsl.v @@ -0,0 +1,627 @@ +/********************************************************************** +* +* Sokol 3d cube demo +* +* Copyright (c) 2021 Dario Deledda. All rights reserved. +* Use of this source code is governed by an MIT license +* that can be found in the LICENSE file. +* +* HOW TO COMPILE SHADERS: +* - download the sokol shader convertor tool from https://github.com/floooh/sokol-tools-bin +* +* - compile the .glsl shader with: +* linux : sokol-shdc --input cube_glsl.glsl --output cube_glsl.h --slang glsl330 +* windows: sokol-shdc.exe --input cube_glsl.glsl --output cube_glsl.h --slang glsl330 +* +* --slang parameter can be: +* - glsl330: desktop GL +* - glsl100: GLES2 / WebGL +* - glsl300es: GLES3 / WebGL2 +* - hlsl4: D3D11 +* - hlsl5: D3D11 +* - metal_macos: Metal on macOS +* - metal_ios: Metal on iOS device +* - metal_sim: Metal on iOS simulator +* - wgpu: WebGPU +* +* you can have multiple platforms at the same time passing prameter like this: --slang glsl330:hlsl5:metal_macos +* for further infos have a look at the sokol shader tool docs. +* +* TODO: +* - add instancing +**********************************************************************/ +import gg +import gx +// import math +import sokol.sapp +import sokol.gfx +import sokol.sgl +import time +import gg.m4 + +// GLSL Include and functions +#flag -I @VMODROOT/. +#include "cube_glsl.h" #Please use sokol-shdc to generate the necessary cube_glsl.h file from cube_glsl.glsl (see the instructions at the top of this file) + +fn C.cube_shader_desc(gfx.Backend) &C.sg_shader_desc + +const ( + win_width = 800 + win_height = 800 + bg_color = gx.white +) + +struct App { +mut: + gg &gg.Context + pip_3d C.sgl_pipeline + texture C.sg_image + init_flag bool + frame_count int + mouse_x int = -1 + mouse_y int = -1 + // glsl + cube_pip_glsl C.sg_pipeline + cube_bind C.sg_bindings + // time + ticks i64 +} + +/****************************************************************************** +* +* Texture functions +* +******************************************************************************/ +fn create_texture(w int, h int, buf &byte) C.sg_image { + sz := w * h * 4 + mut img_desc := C.sg_image_desc{ + width: w + height: h + num_mipmaps: 0 + min_filter: .linear + mag_filter: .linear + // usage: .dynamic + wrap_u: .clamp_to_edge + wrap_v: .clamp_to_edge + label: &byte(0) + d3d11_texture: 0 + } + // comment if .dynamic is enabled + img_desc.data.subimage[0][0] = C.sg_range{ + ptr: buf + size: size_t(sz) + } + + sg_img := C.sg_make_image(&img_desc) + return sg_img +} + +fn destroy_texture(sg_img C.sg_image) { + C.sg_destroy_image(sg_img) +} + +// Use only if usage: .dynamic is enabled +fn update_text_texture(sg_img C.sg_image, w int, h int, buf &byte) { + sz := w * h * 4 + mut tmp_sbc := C.sg_image_data{} + tmp_sbc.subimage[0][0] = C.sg_range{ + ptr: buf + size: size_t(sz) + } + C.sg_update_image(sg_img, &tmp_sbc) +} + +/****************************************************************************** +* +* Draw functions +* +******************************************************************************/ +fn draw_triangle() { + sgl.defaults() + sgl.begin_triangles() + sgl.v2f_c3b( 0.0, 0.5, 255, 0 , 0 ) + sgl.v2f_c3b(-0.5, -0.5, 0, 0 , 255) + sgl.v2f_c3b( 0.5, -0.5, 0, 255, 0 ) + sgl.end() +} + +// vertex specification for a cube with colored sides and texture coords +fn cube() { + sgl.begin_quads() + // edge color + sgl.c3f(1.0, 0.0, 0.0) + // edge coord + // x,y,z, texture cord: u,v + sgl.v3f_t2f(-1.0, 1.0, -1.0, -1.0, 1.0) + sgl.v3f_t2f( 1.0, 1.0, -1.0, 1.0, 1.0) + sgl.v3f_t2f( 1.0, -1.0, -1.0, 1.0, -1.0) + sgl.v3f_t2f(-1.0, -1.0, -1.0, -1.0, -1.0) + sgl.c3f(0.0, 1.0, 0.0) + sgl.v3f_t2f(-1.0, -1.0, 1.0, -1.0, 1.0) + sgl.v3f_t2f( 1.0, -1.0, 1.0, 1.0, 1.0) + sgl.v3f_t2f( 1.0, 1.0, 1.0, 1.0, -1.0) + sgl.v3f_t2f(-1.0, 1.0, 1.0, -1.0, -1.0) + sgl.c3f(0.0, 0.0, 1.0) + sgl.v3f_t2f(-1.0, -1.0, 1.0, -1.0, 1.0) + sgl.v3f_t2f(-1.0, 1.0, 1.0, 1.0, 1.0) + sgl.v3f_t2f(-1.0, 1.0, -1.0, 1.0, -1.0) + sgl.v3f_t2f(-1.0, -1.0, -1.0, -1.0, -1.0) + sgl.c3f(1.0, 0.5, 0.0) + sgl.v3f_t2f(1.0, -1.0, 1.0, -1.0, 1.0) + sgl.v3f_t2f(1.0, -1.0, -1.0, 1.0, 1.0) + sgl.v3f_t2f(1.0, 1.0, -1.0, 1.0, -1.0) + sgl.v3f_t2f(1.0, 1.0, 1.0, -1.0, -1.0) + sgl.c3f(0.0, 0.5, 1.0) + sgl.v3f_t2f( 1.0, -1.0, -1.0, -1.0, 1.0) + sgl.v3f_t2f( 1.0, -1.0, 1.0, 1.0, 1.0) + sgl.v3f_t2f(-1.0, -1.0, 1.0, 1.0, -1.0) + sgl.v3f_t2f(-1.0, -1.0, -1.0, -1.0, -1.0) + sgl.c3f(1.0, 0.0, 0.5) + sgl.v3f_t2f(-1.0, 1.0, -1.0, -1.0, 1.0) + sgl.v3f_t2f(-1.0, 1.0, 1.0, 1.0, 1.0) + sgl.v3f_t2f( 1.0, 1.0, 1.0, 1.0, -1.0) + sgl.v3f_t2f( 1.0, 1.0, -1.0, -1.0, -1.0) + sgl.end() +} + +fn draw_cubes(app App) { + rot := [f32(1.0) * (app.frame_count % 360), 0.5 * f32(app.frame_count % 360)] + // rot := [f32(app.mouse_x), f32(app.mouse_y)] + + sgl.defaults() + sgl.load_pipeline(app.pip_3d) + + sgl.matrix_mode_projection() + sgl.perspective(sgl.rad(45.0), 1.0, 0.1, 100.0) + + sgl.matrix_mode_modelview() + sgl.translate(0.0, 0.0, -12.0) + sgl.rotate(sgl.rad(rot[0]), 1.0, 0.0, 0.0) + sgl.rotate(sgl.rad(rot[1]), 0.0, 1.0, 0.0) + cube() + sgl.push_matrix() + sgl.translate(0.0, 0.0, 3.0) + sgl.scale(0.5, 0.5, 0.5) + sgl.rotate(-2.0 * sgl.rad(rot[0]), 1.0, 0.0, 0.0) + sgl.rotate(-2.0 * sgl.rad(rot[1]), 0.0, 1.0, 0.0) + cube() + sgl.push_matrix() + sgl.translate(0.0, 0.0, 3.0) + sgl.scale(0.5, 0.5, 0.5) + sgl.rotate(-3.0 * sgl.rad(2 * rot[0]), 1.0, 0.0, 0.0) + sgl.rotate( 3.0 * sgl.rad(2 * rot[1]), 0.0, 0.0, 1.0) + cube() + sgl.pop_matrix() + sgl.pop_matrix() +} + +fn cube_texture(r f32, g f32, b f32) { + sgl.begin_quads() + // edge color + sgl.c3f(r, g, b) + // edge coord + // x,y,z, texture cord: u,v + sgl.v3f_t2f(-1.0, 1.0, -1.0, 0.0 , 0.25) + sgl.v3f_t2f( 1.0, 1.0, -1.0, 0.25, 0.25) + sgl.v3f_t2f( 1.0, -1.0, -1.0, 0.25, 0.0 ) + sgl.v3f_t2f(-1.0, -1.0, -1.0, 0.0 , 0.0 ) + sgl.c3f(r, g, b) + sgl.v3f_t2f(-1.0, -1.0, 1.0, 0.0 , 0.25) + sgl.v3f_t2f( 1.0, -1.0, 1.0, 0.25, 0.25) + sgl.v3f_t2f( 1.0, 1.0, 1.0, 0.25, 0.0 ) + sgl.v3f_t2f(-1.0, 1.0, 1.0, 0.0 , 0.0 ) + sgl.c3f(r, g, b) + sgl.v3f_t2f(-1.0, -1.0, 1.0, 0.0 , 0.25) + sgl.v3f_t2f(-1.0, 1.0, 1.0, 0.25, 0.25) + sgl.v3f_t2f(-1.0, 1.0, -1.0, 0.25, 0.0 ) + sgl.v3f_t2f(-1.0, -1.0, -1.0, 0.0 , 0.0 ) + sgl.c3f(r, g, b) + sgl.v3f_t2f(1.0, -1.0, 1.0, 0.0 , 0.25) + sgl.v3f_t2f(1.0, -1.0, -1.0, 0.25, 0.25) + sgl.v3f_t2f(1.0, 1.0, -1.0, 0.25, 0.0 ) + sgl.v3f_t2f(1.0, 1.0, 1.0, 0.0 , 0.0 ) + sgl.c3f(r, g, b) + sgl.v3f_t2f( 1.0, -1.0, -1.0, 0.0 , 0.25) + sgl.v3f_t2f( 1.0, -1.0, 1.0, 0.25, 0.25) + sgl.v3f_t2f(-1.0, -1.0, 1.0, 0.25, 0.0 ) + sgl.v3f_t2f(-1.0, -1.0, -1.0, 0.0 , 0.0 ) + sgl.c3f(r, g, b) + sgl.v3f_t2f(-1.0, 1.0, -1.0, 0.0 , 0.25) + sgl.v3f_t2f(-1.0, 1.0, 1.0, 0.25, 0.25) + sgl.v3f_t2f( 1.0, 1.0, 1.0, 0.25, 0.0 ) + sgl.v3f_t2f( 1.0, 1.0, -1.0, 0.0 , 0.0 ) + sgl.end() +} + +/* +Cube vertex buffer with packed vertex formats for color and texture coords. + Note that a vertex format which must be portable across all + backends must only use the normalized integer formats + (BYTE4N, UBYTE4N, SHORT2N, SHORT4N), which can be converted + to floating point formats in the vertex shader inputs. + The reason is that D3D11 cannot convert from non-normalized + formats to floating point inputs (only to integer inputs), + and WebGL2 / GLES2 don't support integer vertex shader inputs. +*/ + +struct Vertex_t { + x f32 + y f32 + z f32 + color u32 + // u u16 + // v u16 + u f32 + v f32 +} + +fn init_cube_glsl(mut app App) { + // cube vertex buffer + // d := u16(32767/8) // for compatibility with D3D11, 32767 stand for 1 + d := f32(1.0) // 0.05) + c := u32(0xFFFFFF_FF) // color RGBA8 + vertices := [ + // Face 0 + Vertex_t{-1.0, -1.0, -1.0, c, 0, 0}, + Vertex_t{ 1.0, -1.0, -1.0, c, d, 0}, + Vertex_t{ 1.0, 1.0, -1.0, c, d, d}, + Vertex_t{-1.0, 1.0, -1.0, c, 0, d}, + // Face 1 + Vertex_t{-1.0, -1.0, 1.0, c, 0, 0}, + Vertex_t{ 1.0, -1.0, 1.0, c, d, 0}, + Vertex_t{ 1.0, 1.0, 1.0, c, d, d}, + Vertex_t{-1.0, 1.0, 1.0, c, 0, d}, + // Face 2 + Vertex_t{-1.0, -1.0, -1.0, c, 0, 0}, + Vertex_t{-1.0, 1.0, -1.0, c, d, 0}, + Vertex_t{-1.0, 1.0, 1.0, c, d, d}, + Vertex_t{-1.0, -1.0, 1.0, c, 0, d}, + // Face 3 + Vertex_t{ 1.0, -1.0, -1.0, c, 0, 0}, + Vertex_t{ 1.0, 1.0, -1.0, c, d, 0}, + Vertex_t{ 1.0, 1.0, 1.0, c, d, d}, + Vertex_t{ 1.0, -1.0, 1.0, c, 0, d}, + // Face 4 + Vertex_t{-1.0, -1.0, -1.0, c, 0, 0}, + Vertex_t{-1.0, -1.0, 1.0, c, d, 0}, + Vertex_t{ 1.0, -1.0, 1.0, c, d, d}, + Vertex_t{ 1.0, -1.0, -1.0, c, 0, d}, + // Face 5 + Vertex_t{-1.0, 1.0, -1.0, c, 0, 0}, + Vertex_t{-1.0, 1.0, 1.0, c, d, 0}, + Vertex_t{ 1.0, 1.0, 1.0, c, d, d}, + Vertex_t{ 1.0, 1.0, -1.0, c, 0, d}, + ] + + mut vert_buffer_desc := C.sg_buffer_desc{label: c'cube-vertices'} + unsafe { C.memset(&vert_buffer_desc, 0, sizeof(vert_buffer_desc)) } + + vert_buffer_desc.size = size_t(vertices.len * int(sizeof(Vertex_t))) + vert_buffer_desc.data = C.sg_range{ + ptr: vertices.data + size: size_t(vertices.len * int(sizeof(Vertex_t))) + } + + vert_buffer_desc.@type = .vertexbuffer + // vert_buffer_desc.usage = .immutable + vbuf := gfx.make_buffer(&vert_buffer_desc) + + /* create an index buffer for the cube */ + indices := [ + u16(0), 1, 2, 0, 2, 3, + 6, 5, 4, 7, 6, 4, + 8, 9, 10, 8, 10, 11, + 14, 13, 12, 15, 14, 12, + 16, 17, 18, 16, 18, 19, + 22, 21, 20, 23, 22, 20 + ] + + mut index_buffer_desc := C.sg_buffer_desc{label: c'cube-indices'} + unsafe { C.memset(&index_buffer_desc, 0, sizeof(index_buffer_desc)) } + + index_buffer_desc.size = size_t(indices.len * int(sizeof(u16))) + index_buffer_desc.data = C.sg_range{ + ptr: indices.data + size: size_t(indices.len * int(sizeof(u16))) + } + + index_buffer_desc.@type = .indexbuffer + ibuf := gfx.make_buffer(&index_buffer_desc) + + // create shader + shader := gfx.make_shader(C.cube_shader_desc(C.sg_query_backend())) + + mut pipdesc := C.sg_pipeline_desc{} + unsafe { C.memset(&pipdesc, 0, sizeof(pipdesc)) } + + pipdesc.layout.buffers[0].stride = int(sizeof(Vertex_t)) + // the constants [C.ATTR_vs_pos, C.ATTR_vs_color0, C.ATTR_vs_texcoord0] are generated bysokol-shdc + pipdesc.layout.attrs[C.ATTR_vs_pos ].format = .float3 // x,y,z as f32 + pipdesc.layout.attrs[C.ATTR_vs_color0 ].format = .ubyte4n // color as u32 + pipdesc.layout.attrs[C.ATTR_vs_texcoord0].format = .float2 // u,v as f32 + // pipdesc.layout.attrs[C.ATTR_vs_texcoord0].format = .short2n // u,v as u16 + + pipdesc.shader = shader + pipdesc.index_type = .uint16 + + pipdesc.depth = C.sg_depth_state{ + write_enabled: true + compare: gfx.CompareFunc(C.SG_COMPAREFUNC_LESS_EQUAL) + } + pipdesc.cull_mode = .back + + pipdesc.label = 'glsl_shader pipeline'.str + + app.cube_bind.vertex_buffers[0] = vbuf + app.cube_bind.index_buffer = ibuf + app.cube_bind.fs_images[C.SLOT_tex] = app.texture + app.cube_pip_glsl = gfx.make_pipeline(&pipdesc) + println('GLSL init DONE!') +} + +fn draw_cube_glsl(app App) { + if app.init_flag == false { + return + } + + rot := [f32(app.mouse_y), f32(app.mouse_x)] + + ws := gg.window_size_real_pixels() + // ratio := f32(ws.width)/ws.height + dw := f32(ws.width / 2) + dh := f32(ws.height / 2) + + tr_matrix := m4.calc_tr_matrices(dw, dh, rot[0], rot[1], 2.0) + gfx.apply_viewport(ws.width / 2, 0, ws.width / 2, ws.height / 2, true) + + // apply the pipline and bindings + gfx.apply_pipeline(app.cube_pip_glsl) + gfx.apply_bindings(app.cube_bind) + + //*************** + // Uniforms + //*************** + // passing the view matrix as uniform + // res is a 4x4 matrix of f32 thus: 4*16 byte of size + vs_uniforms_range := C.sg_range{ + ptr: &tr_matrix + size: size_t(4 * 16) + } + gfx.apply_uniforms(C.SG_SHADERSTAGE_VS, C.SLOT_vs_params, &vs_uniforms_range) + + // fs uniforms + time_ticks := f32(time.ticks() - app.ticks) / 1000 + mut text_res := [ + f32(512), + 512, /* x,y resolution to pass to FS */ + time_ticks, /* time as f32 */ + 0 /* padding 4 Bytes == 1 f32 */, + ]! + fs_uniforms_range := C.sg_range{ + ptr: unsafe { &text_res } + size: size_t(4 * 4) + } + gfx.apply_uniforms(C.SG_SHADERSTAGE_FS, C.SLOT_fs_params, &fs_uniforms_range) + + gfx.draw(0, (3 * 2) * 6, 1) + gfx.end_pass() + gfx.commit() +} + +fn draw_texture_cubes(app App) { + rot := [f32(app.mouse_x), f32(app.mouse_y)] + sgl.defaults() + sgl.load_pipeline(app.pip_3d) + + sgl.enable_texture() + sgl.texture(app.texture) + + sgl.matrix_mode_projection() + sgl.perspective(sgl.rad(45.0), 1.0, 0.1, 100.0) + + sgl.matrix_mode_modelview() + sgl.translate(0.0, 0.0, -12.0) + sgl.rotate(sgl.rad(rot[0]), 1.0, 0.0, 0.0) + sgl.rotate(sgl.rad(rot[1]), 0.0, 1.0, 0.0) + cube_texture(1, 1, 1) + sgl.push_matrix() + sgl.translate(0.0, 0.0, 3.0) + sgl.scale(0.5, 0.5, 0.5) + sgl.rotate(-2.0 * sgl.rad(rot[0]), 1.0, 0.0, 0.0) + sgl.rotate(-2.0 * sgl.rad(rot[1]), 0.0, 1.0, 0.0) + cube_texture(1,1,1) + sgl.push_matrix() + sgl.translate(0.0, 0.0, 3.0) + sgl.scale(0.5, 0.5, 0.5) + sgl.rotate(-3.0 * sgl.rad(2*rot[0]), 1.0, 0.0, 0.0) + sgl.rotate(3.0 * sgl.rad(2*rot[1]), 0.0, 0.0, 1.0) + cube_texture(1,1,1) + sgl.pop_matrix() + sgl.pop_matrix() + + sgl.disable_texture() +} + +fn frame(mut app App) { + ws := gg.window_size_real_pixels() + ratio := f32(ws.width) / ws.height + dw := ws.width + dh := ws.height + ww := int(dh / 3) // not a bug + hh := int(dh / 3) + x0 := int(f32(dw) * 0.05) + // x1 := dw/2 + y0 := 0 + y1 := int(f32(dh) * 0.5) + + // app.gg.begin() + + app.gg.begin() + sgl.defaults() + + // 2d triangle + sgl.viewport(x0, y0, ww, hh, true) + draw_triangle() + + // colored cubes with viewport + sgl.viewport(x0, y1, ww, hh, true) + draw_cubes(app) + + // textured cubed with viewport + sgl.viewport(0, int(dh / 5), dw, int(dh * ratio), true) + draw_texture_cubes(app) + + app.gg.end() + + // clear + mut color_action := C.sg_color_attachment_action{ + action: gfx.Action(C.SG_ACTION_DONTCARE) // C.SG_ACTION_CLEAR) + value: C.sg_color{ + r: 1.0 + g: 1.0 + b: 1.0 + a: 1.0 + } + } + mut pass_action := C.sg_pass_action{} + pass_action.colors[0] = color_action + gfx.begin_default_pass(&pass_action, ws.width, ws.height) + + // glsl cube + draw_cube_glsl(app) + + app.frame_count++ +} + +/****************************************************************************** +* +* Init / Cleanup +* +******************************************************************************/ +fn my_init(mut app App) { + // set max vertices, + // for a large number of the same type of object it is better use the instances!! + desc := sapp.create_desc() + gfx.setup(&desc) + sgl_desc := C.sgl_desc_t{ + max_vertices: 50 * 65536 + } + sgl.setup(&sgl_desc) + + // 3d pipeline + mut pipdesc := C.sg_pipeline_desc{} + unsafe { C.memset(&pipdesc, 0, sizeof(pipdesc)) } + + color_state := C.sg_color_state{ + blend: C.sg_blend_state{ + enabled: true + src_factor_rgb: gfx.BlendFactor(C.SG_BLENDFACTOR_SRC_ALPHA) + dst_factor_rgb: gfx.BlendFactor(C.SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA) + } + } + pipdesc.colors[0] = color_state + + pipdesc.depth = C.sg_depth_state{ + write_enabled: true + compare: gfx.CompareFunc(C.SG_COMPAREFUNC_LESS_EQUAL) + } + pipdesc.cull_mode = .back + + app.pip_3d = sgl.make_pipeline(&pipdesc) + + // create chessboard texture 256*256 RGBA + w := 256 + h := 256 + sz := w * h * 4 + tmp_txt := unsafe { malloc(sz) } + mut i := 0 + for i < sz { + unsafe { + y := (i >> 0x8) >> 5 // 8 cell + x := (i & 0xFF) >> 5 // 8 cell + // upper left corner + if x == 0 && y == 0 { + tmp_txt[i] = byte(0xFF) + tmp_txt[i + 1] = byte(0) + tmp_txt[i + 2] = byte(0) + tmp_txt[i + 3] = byte(0xFF) + } + // low right corner + else if x == 7 && y == 7 { + tmp_txt[i + 0] = byte(0) + tmp_txt[i + 1] = byte(0xFF) + tmp_txt[i + 2] = byte(0) + tmp_txt[i + 3] = byte(0xFF) + } else { + col := if ((x + y) & 1) == 1 { 0xFF } else { 128 } + tmp_txt[i + 0] = byte(col) // red + tmp_txt[i + 1] = byte(col) // green + tmp_txt[i + 2] = byte(col) // blue + tmp_txt[i + 3] = byte(0xFF) // alpha + } + i += 4 + } + } + app.texture = create_texture(w, h, tmp_txt) + unsafe { free(tmp_txt) } + + // glsl + init_cube_glsl(mut app) + app.init_flag = true +} + +fn cleanup(mut app App) { + gfx.shutdown() +} + +/****************************************************************************** +* +* event +* +******************************************************************************/ +fn my_event_manager(mut ev gg.Event, mut app App) { + if ev.typ == .mouse_move { + app.mouse_x = int(ev.mouse_x) + app.mouse_y = int(ev.mouse_y) + } + if ev.typ == .touches_began || ev.typ == .touches_moved { + if ev.num_touches > 0 { + touch_point := ev.touches[0] + app.mouse_x = int(touch_point.pos_x) + app.mouse_y = int(touch_point.pos_y) + } + } +} + +/****************************************************************************** +* +* Main +* +******************************************************************************/ +[console] // is needed for easier diagnostics on windows +fn main() { + // App init + mut app := &App{ + gg: 0 + } + + mut a := [5]int{} + a[0] = 2 + println(a) + + app.gg = gg.new_context( + width: win_width + height: win_height + create_window: true + window_title: '3D Cube Demo' + user_data: app + bg_color: bg_color + frame_fn: frame + init_fn: my_init + cleanup_fn: cleanup + event_fn: my_event_manager + ) + + app.ticks = time.ticks() + app.gg.run() +} diff --git a/v_windows/v/old/examples/sokol/02_cubes_glsl/v.mod b/v_windows/v/old/examples/sokol/02_cubes_glsl/v.mod new file mode 100644 index 0000000..e69de29 diff --git a/v_windows/v/old/examples/sokol/03_march_tracing_glsl/rt_glsl.glsl b/v_windows/v/old/examples/sokol/03_march_tracing_glsl/rt_glsl.glsl new file mode 100644 index 0000000..3490873 --- /dev/null +++ b/v_windows/v/old/examples/sokol/03_march_tracing_glsl/rt_glsl.glsl @@ -0,0 +1,695 @@ +//------------------------------------------------------------------------------ +// Shader code for texcube-sapp sample. +// +// NOTE: This source file also uses the '#pragma sokol' form of the +// custom tags. +//------------------------------------------------------------------------------ +//#pragma sokol @ctype mat4 hmm_mat4 + +#pragma sokol @vs vs +uniform vs_params { + mat4 mvp; +}; + +in vec4 pos; +in vec4 color0; +in vec2 texcoord0; + +out vec4 color; +out vec2 uv; + +void main() { + gl_Position = mvp * pos; + color = color0; + uv = texcoord0; +} +#pragma sokol @end + +#pragma sokol @fs fs +uniform sampler2D tex; +uniform fs_params { + vec2 iResolution; + vec2 iMouse; + float iTime; + float iFrame; +}; + +in vec4 color; +in vec2 uv; +out vec4 frag_color; + +// change to 0 to 4 to increment the AntiAliasing, +// increase AA will SLOW the rendering!! +#define AA 1 + +//********************************************************* +// Ray Marching +// original code from: https://www.shadertoy.com/view/Xds3zN +//********************************************************* +// The MIT License +// Copyright © 2013 Inigo Quilez +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +// A list of useful distance function to simple primitives. All +// these functions (except for ellipsoid) return an exact +// euclidean distance, meaning they produce a better SDF than +// what you'd get if you were constructing them from boolean +// operations. +// +// More info here: +// +// https://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm + +//------------------------------------------------------------------ +float dot2( in vec2 v ) { return dot(v,v); } +float dot2( in vec3 v ) { return dot(v,v); } +float ndot( in vec2 a, in vec2 b ) { return a.x*b.x - a.y*b.y; } + +float sdPlane( vec3 p ) +{ + return p.y; +} + +float sdSphere( vec3 p, float s ) +{ + return length(p)-s; +} + +float sdBox( vec3 p, vec3 b ) +{ + vec3 d = abs(p) - b; + return min(max(d.x,max(d.y,d.z)),0.0) + length(max(d,0.0)); +} + +float sdBoundingBox( vec3 p, vec3 b, float e ) +{ + p = abs(p )-b; + vec3 q = abs(p+e)-e; + + return min(min( + length(max(vec3(p.x,q.y,q.z),0.0))+min(max(p.x,max(q.y,q.z)),0.0), + length(max(vec3(q.x,p.y,q.z),0.0))+min(max(q.x,max(p.y,q.z)),0.0)), + length(max(vec3(q.x,q.y,p.z),0.0))+min(max(q.x,max(q.y,p.z)),0.0)); +} +float sdEllipsoid( in vec3 p, in vec3 r ) // approximated +{ + float k0 = length(p/r); + float k1 = length(p/(r*r)); + return k0*(k0-1.0)/k1; +} + +float sdTorus( vec3 p, vec2 t ) +{ + return length( vec2(length(p.xz)-t.x,p.y) )-t.y; +} + +float sdCappedTorus(in vec3 p, in vec2 sc, in float ra, in float rb) +{ + p.x = abs(p.x); + float k = (sc.y*p.x>sc.x*p.y) ? dot(p.xy,sc) : length(p.xy); + return sqrt( dot(p,p) + ra*ra - 2.0*ra*k ) - rb; +} + +float sdHexPrism( vec3 p, vec2 h ) +{ + vec3 q = abs(p); + + const vec3 k = vec3(-0.8660254, 0.5, 0.57735); + p = abs(p); + p.xy -= 2.0*min(dot(k.xy, p.xy), 0.0)*k.xy; + vec2 d = vec2( + length(p.xy - vec2(clamp(p.x, -k.z*h.x, k.z*h.x), h.x))*sign(p.y - h.x), + p.z-h.y ); + return min(max(d.x,d.y),0.0) + length(max(d,0.0)); +} + +float sdOctogonPrism( in vec3 p, in float r, float h ) +{ + const vec3 k = vec3(-0.9238795325, // sqrt(2+sqrt(2))/2 + 0.3826834323, // sqrt(2-sqrt(2))/2 + 0.4142135623 ); // sqrt(2)-1 + // reflections + p = abs(p); + p.xy -= 2.0*min(dot(vec2( k.x,k.y),p.xy),0.0)*vec2( k.x,k.y); + p.xy -= 2.0*min(dot(vec2(-k.x,k.y),p.xy),0.0)*vec2(-k.x,k.y); + // polygon side + p.xy -= vec2(clamp(p.x, -k.z*r, k.z*r), r); + vec2 d = vec2( length(p.xy)*sign(p.y), p.z-h ); + return min(max(d.x,d.y),0.0) + length(max(d,0.0)); +} + +float sdCapsule( vec3 p, vec3 a, vec3 b, float r ) +{ + vec3 pa = p-a, ba = b-a; + float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 ); + return length( pa - ba*h ) - r; +} + +float sdRoundCone( in vec3 p, in float r1, float r2, float h ) +{ + vec2 q = vec2( length(p.xz), p.y ); + + float b = (r1-r2)/h; + float a = sqrt(1.0-b*b); + float k = dot(q,vec2(-b,a)); + + if( k < 0.0 ) return length(q) - r1; + if( k > a*h ) return length(q-vec2(0.0,h)) - r2; + + return dot(q, vec2(a,b) ) - r1; +} + +float sdRoundCone(vec3 p, vec3 a, vec3 b, float r1, float r2) +{ + // sampling independent computations (only depend on shape) + vec3 ba = b - a; + float l2 = dot(ba,ba); + float rr = r1 - r2; + float a2 = l2 - rr*rr; + float il2 = 1.0/l2; + + // sampling dependant computations + vec3 pa = p - a; + float y = dot(pa,ba); + float z = y - l2; + float x2 = dot2( pa*l2 - ba*y ); + float y2 = y*y*l2; + float z2 = z*z*l2; + + // single square root! + float k = sign(rr)*rr*rr*x2; + if( sign(z)*a2*z2 > k ) return sqrt(x2 + z2) *il2 - r2; + if( sign(y)*a2*y2 < k ) return sqrt(x2 + y2) *il2 - r1; + return (sqrt(x2*a2*il2)+y*rr)*il2 - r1; +} + +float sdTriPrism( vec3 p, vec2 h ) +{ + const float k = sqrt(3.0); + h.x *= 0.5*k; + p.xy /= h.x; + p.x = abs(p.x) - 1.0; + p.y = p.y + 1.0/k; + if( p.x+k*p.y>0.0 ) p.xy=vec2(p.x-k*p.y,-k*p.x-p.y)/2.0; + p.x -= clamp( p.x, -2.0, 0.0 ); + float d1 = length(p.xy)*sign(-p.y)*h.x; + float d2 = abs(p.z)-h.y; + return length(max(vec2(d1,d2),0.0)) + min(max(d1,d2), 0.); +} + +// vertical +float sdCylinder( vec3 p, vec2 h ) +{ + vec2 d = abs(vec2(length(p.xz),p.y)) - h; + return min(max(d.x,d.y),0.0) + length(max(d,0.0)); +} + +// arbitrary orientation +float sdCylinder(vec3 p, vec3 a, vec3 b, float r) +{ + vec3 pa = p - a; + vec3 ba = b - a; + float baba = dot(ba,ba); + float paba = dot(pa,ba); + + float x = length(pa*baba-ba*paba) - r*baba; + float y = abs(paba-baba*0.5)-baba*0.5; + float x2 = x*x; + float y2 = y*y*baba; + float d = (max(x,y)<0.0)?-min(x2,y2):(((x>0.0)?x2:0.0)+((y>0.0)?y2:0.0)); + return sign(d)*sqrt(abs(d))/baba; +} + +// vertical +float sdCone( in vec3 p, in vec2 c, float h ) +{ + vec2 q = h*vec2(c.x,-c.y)/c.y; + vec2 w = vec2( length(p.xz), p.y ); + + vec2 a = w - q*clamp( dot(w,q)/dot(q,q), 0.0, 1.0 ); + vec2 b = w - q*vec2( clamp( w.x/q.x, 0.0, 1.0 ), 1.0 ); + float k = sign( q.y ); + float d = min(dot( a, a ),dot(b, b)); + float s = max( k*(w.x*q.y-w.y*q.x),k*(w.y-q.y) ); + return sqrt(d)*sign(s); +} + +float sdCappedCone( in vec3 p, in float h, in float r1, in float r2 ) +{ + vec2 q = vec2( length(p.xz), p.y ); + + vec2 k1 = vec2(r2,h); + vec2 k2 = vec2(r2-r1,2.0*h); + vec2 ca = vec2(q.x-min(q.x,(q.y < 0.0)?r1:r2), abs(q.y)-h); + vec2 cb = q - k1 + k2*clamp( dot(k1-q,k2)/dot2(k2), 0.0, 1.0 ); + float s = (cb.x < 0.0 && ca.y < 0.0) ? -1.0 : 1.0; + return s*sqrt( min(dot2(ca),dot2(cb)) ); +} + +float sdCappedCone(vec3 p, vec3 a, vec3 b, float ra, float rb) +{ + float rba = rb-ra; + float baba = dot(b-a,b-a); + float papa = dot(p-a,p-a); + float paba = dot(p-a,b-a)/baba; + + float x = sqrt( papa - paba*paba*baba ); + + float cax = max(0.0,x-((paba<0.5)?ra:rb)); + float cay = abs(paba-0.5)-0.5; + + float k = rba*rba + baba; + float f = clamp( (rba*(x-ra)+paba*baba)/k, 0.0, 1.0 ); + + float cbx = x-ra - f*rba; + float cby = paba - f; + + float s = (cbx < 0.0 && cay < 0.0) ? -1.0 : 1.0; + + return s*sqrt( min(cax*cax + cay*cay*baba, + cbx*cbx + cby*cby*baba) ); +} + +// c is the sin/cos of the desired cone angle +float sdSolidAngle(vec3 pos, vec2 c, float ra) +{ + vec2 p = vec2( length(pos.xz), pos.y ); + float l = length(p) - ra; + float m = length(p - c*clamp(dot(p,c),0.0,ra) ); + return max(l,m*sign(c.y*p.x-c.x*p.y)); +} + +float sdOctahedron(vec3 p, float s) +{ + p = abs(p); + float m = p.x + p.y + p.z - s; + +// exact distance +#if 0 + vec3 o = min(3.0*p - m, 0.0); + o = max(6.0*p - m*2.0 - o*3.0 + (o.x+o.y+o.z), 0.0); + return length(p - s*o/(o.x+o.y+o.z)); +#endif + +// exact distance +#if 1 + vec3 q; + if( 3.0*p.x < m ) q = p.xyz; + else if( 3.0*p.y < m ) q = p.yzx; + else if( 3.0*p.z < m ) q = p.zxy; + else return m*0.57735027; + float k = clamp(0.5*(q.z-q.y+s),0.0,s); + return length(vec3(q.x,q.y-s+k,q.z-k)); +#endif + +// bound, not exact +#if 0 + return m*0.57735027; +#endif +} + +float sdPyramid( in vec3 p, in float h ) +{ + float m2 = h*h + 0.25; + + // symmetry + p.xz = abs(p.xz); + p.xz = (p.z>p.x) ? p.zx : p.xz; + p.xz -= 0.5; + + // project into face plane (2D) + vec3 q = vec3( p.z, h*p.y - 0.5*p.x, h*p.x + 0.5*p.y); + + float s = max(-q.x,0.0); + float t = clamp( (q.y-0.5*p.z)/(m2+0.25), 0.0, 1.0 ); + + float a = m2*(q.x+s)*(q.x+s) + q.y*q.y; + float b = m2*(q.x+0.5*t)*(q.x+0.5*t) + (q.y-m2*t)*(q.y-m2*t); + + float d2 = min(q.y,-q.x*m2-q.y*0.5) > 0.0 ? 0.0 : min(a,b); + + // recover 3D and scale, and add sign + return sqrt( (d2+q.z*q.z)/m2 ) * sign(max(q.z,-p.y)); +} + +// la,lb=semi axis, h=height, ra=corner +float sdRhombus(vec3 p, float la, float lb, float h, float ra) +{ + p = abs(p); + vec2 b = vec2(la,lb); + float f = clamp( (ndot(b,b-2.0*p.xz))/dot(b,b), -1.0, 1.0 ); + vec2 q = vec2(length(p.xz-0.5*b*vec2(1.0-f,1.0+f))*sign(p.x*b.y+p.z*b.x-b.x*b.y)-ra, p.y-h); + return min(max(q.x,q.y),0.0) + length(max(q,0.0)); +} + +//------------------------------------------------------------------ + +vec2 opU( vec2 d1, vec2 d2 ) +{ + return (d1.x0.0 ) + { + tmax = min( tmax, tp1 ); + res = vec2( tp1, 1.0 ); + } + //else return res; + + // raymarch primitives + vec2 tb = iBox( ro-vec3(0.0,0.4,-0.5), rd, vec3(2.5,0.41,3.0) ); + if( tb.x0.0 && tb.x0.0 ) tmax = min( tmax, tp ); + + float res = 1.0; + float t = mint; + for( int i=ZERO; i<24; i++ ) + { + float h = map( ro + rd*t ).x; + float s = clamp(8.0*h/t,0.0,1.0); + res = min( res, s*s*(3.0-2.0*s) ); + t += clamp( h, 0.02, 0.2 ); + if( res<0.004 || t>tmax ) break; + } + return clamp( res, 0.0, 1.0 ); +} + +// http://iquilezles.org/www/articles/normalsSDF/normalsSDF.htm +vec3 calcNormal( in vec3 pos ) +{ +#if 0 + vec2 e = vec2(1.0,-1.0)*0.5773*0.0005; + return normalize( e.xyy*map( pos + e.xyy ).x + + e.yyx*map( pos + e.yyx ).x + + e.yxy*map( pos + e.yxy ).x + + e.xxx*map( pos + e.xxx ).x ); +#else + // inspired by tdhooper and klems - a way to prevent the compiler from inlining map() 4 times + vec3 n = vec3(0.0); + for( int i=ZERO; i<4; i++ ) + { + vec3 e = 0.5773*(2.0*vec3((((i+3)>>1)&1),((i>>1)&1),(i&1))-1.0); + n += e*map(pos+0.0005*e).x; + //if( n.x+n.y+n.z>100.0 ) break; + } + return normalize(n); +#endif +} + +float calcAO( in vec3 pos, in vec3 nor ) +{ + float occ = 0.0; + float sca = 1.0; + for( int i=ZERO; i<5; i++ ) + { + float h = 0.01 + 0.12*float(i)/4.0; + float d = map( pos + h*nor ).x; + occ += (h-d)*sca; + sca *= 0.95; + if( occ>0.35 ) break; + } + return clamp( 1.0 - 3.0*occ, 0.0, 1.0 ) * (0.5+0.5*nor.y); +} + +// http://iquilezles.org/www/articles/checkerfiltering/checkerfiltering.htm +float checkersGradBox( in vec2 p, in vec2 dpdx, in vec2 dpdy ) +{ + // filter kernel + vec2 w = abs(dpdx)+abs(dpdy) + 0.001; + // analytical integral (box filter) + vec2 i = 2.0*(abs(fract((p-0.5*w)*0.5)-0.5)-abs(fract((p+0.5*w)*0.5)-0.5))/w; + // xor pattern + return 0.5 - 0.5*i.x*i.y; +} + +vec3 render( in vec3 ro, in vec3 rd, in vec3 rdx, in vec3 rdy ) +{ + // background + vec3 col = vec3(0.7, 0.7, 0.9) - max(rd.y,0.0)*0.3; + + // raycast scene + vec2 res = raycast(ro,rd); + float t = res.x; + float m = res.y; + if( m>-0.5 ) + { + vec3 pos = ro + t*rd; + vec3 nor = (m<1.5) ? vec3(0.0,1.0,0.0) : calcNormal( pos ); + vec3 ref = reflect( rd, nor ); + + // material + col = 0.2 + 0.2*sin( m*2.0 + vec3(0.0,1.0,2.0) ); + float ks = 1.0; + + if( m<1.5 ) + { + // project pixel footprint into the plane + vec3 dpdx = ro.y*(rd/rd.y-rdx/rdx.y); + vec3 dpdy = ro.y*(rd/rd.y-rdy/rdy.y); + + float f = checkersGradBox( 3.0*pos.xz, 3.0*dpdx.xz, 3.0*dpdy.xz ); + col = 0.15 + f*vec3(0.05); + ks = 0.4; + } + + // lighting + float occ = calcAO( pos, nor ); + + vec3 lin = vec3(0.0); + + // sun + { + vec3 lig = normalize( vec3(-0.5, 0.4, -0.6) ); + vec3 hal = normalize( lig-rd ); + float dif = clamp( dot( nor, lig ), 0.0, 1.0 ); + //if( dif>0.0001 ) + dif *= calcSoftshadow( pos, lig, 0.02, 2.5 ); + float spe = pow( clamp( dot( nor, hal ), 0.0, 1.0 ),16.0); + spe *= dif; + spe *= 0.04+0.96*pow(clamp(1.0-dot(hal,lig),0.0,1.0),5.0); + lin += col*2.20*dif*vec3(1.30,1.00,0.70); + lin += 5.00*spe*vec3(1.30,1.00,0.70)*ks; + } + // sky + { + float dif = sqrt(clamp( 0.5+0.5*nor.y, 0.0, 1.0 )); + dif *= occ; + float spe = smoothstep( -0.2, 0.2, ref.y ); + spe *= dif; + spe *= 0.04+0.96*pow(clamp(1.0+dot(nor,rd),0.0,1.0), 5.0 ); + //if( spe>0.001 ) + spe *= calcSoftshadow( pos, ref, 0.02, 2.5 ); + lin += col*0.60*dif*vec3(0.40,0.60,1.15); + lin += 2.00*spe*vec3(0.40,0.60,1.30)*ks; + } + // back + { + float dif = clamp( dot( nor, normalize(vec3(0.5,0.0,0.6))), 0.0, 1.0 )*clamp( 1.0-pos.y,0.0,1.0); + dif *= occ; + lin += col*0.55*dif*vec3(0.25,0.25,0.25); + } + // sss + { + float dif = pow(clamp(1.0+dot(nor,rd),0.0,1.0),2.0); + dif *= occ; + lin += col*0.25*dif*vec3(1.00,1.00,1.00); + } + + col = lin; + + col = mix( col, vec3(0.7,0.7,0.9), 1.0-exp( -0.0001*t*t*t ) ); + } + + return vec3( clamp(col,0.0,1.0) ); +} + +mat3 setCamera( in vec3 ro, in vec3 ta, float cr ) +{ + vec3 cw = normalize(ta-ro); + vec3 cp = vec3(sin(cr), cos(cr),0.0); + vec3 cu = normalize( cross(cw,cp) ); + vec3 cv = ( cross(cu,cw) ); + return mat3( cu, cv, cw ); +} + +vec4 mainImage( vec2 fragCoord ) +{ + vec2 mo = iMouse.xy/iResolution.xy; + float time = 32.0 + iTime*1.5; + + // camera + vec3 ta = vec3( 0.5, -0.5, -0.6 ); + vec3 ro = ta + vec3( 4.5*cos(0.1*time + 7.0*mo.x), 1.3 + 2.0*mo.y, 4.5*sin(0.1*time + 7.0*mo.x) ); + // camera-to-world transformation + mat3 ca = setCamera( ro, ta, 0.0 ); + + vec3 tot = vec3(0.0); +#if AA>1 + for( int m=ZERO; m1 + } + tot /= float(AA*AA); +#endif + + //fragColor = vec4( tot, 1.0 ); + return vec4( tot, 1.0 ); +} + +//********************************************************* +// END Ray Marching +//********************************************************* + +void main() { + vec4 c = color; + vec4 txt = texture(tex, uv); + c = txt * c; + vec2 uv1 = uv * iResolution; + vec4 col_ray = mainImage(uv1); + + // use this to mix the chessboart texture with the ray marching + //frag_color = clamp(c*iMouse.y/512.0,0.0,1.0) * col_ray ; + + frag_color = c*0.00001 + col_ray ; +} + +#pragma sokol @end + +#pragma sokol @program rt vs fs diff --git a/v_windows/v/old/examples/sokol/03_march_tracing_glsl/rt_glsl.v b/v_windows/v/old/examples/sokol/03_march_tracing_glsl/rt_glsl.v new file mode 100644 index 0000000..73a85a3 --- /dev/null +++ b/v_windows/v/old/examples/sokol/03_march_tracing_glsl/rt_glsl.v @@ -0,0 +1,438 @@ +/********************************************************************** +* +* Sokol 3d cube demo +* +* Copyright (c) 2021 Dario Deledda. All rights reserved. +* Use of this source code is governed by an MIT license +* that can be found in the LICENSE file. +* +* HOW TO COMPILE SHADERS: +* - download the sokol shader convertor tool from https://github.com/floooh/sokol-tools-bin +* +* - compile the .glsl shader with: +* linux : sokol-shdc --input rt_glsl.glsl --output rt_glsl.h --slang glsl330 +* windows: sokol-shdc.exe --input rt_glsl.glsl --output rt_glsl.h --slang glsl330 +* +* --slang parameter can be: +* - glsl330: desktop GL +* - glsl100: GLES2 / WebGL +* - glsl300es: GLES3 / WebGL2 +* - hlsl4: D3D11 +* - hlsl5: D3D11 +* - metal_macos: Metal on macOS +* - metal_ios: Metal on iOS device +* - metal_sim: Metal on iOS simulator +* - wgpu: WebGPU +* +* you can have multiple platforms at the same time passing parameters like this: --slang glsl330:hlsl5:metal_macos +* for further infos have a look at the sokol shader tool docs. +* +* TODO: +* - frame counter +**********************************************************************/ +import gg +import gg.m4 +import gx +// import math +import sokol.sapp +import sokol.gfx +import sokol.sgl +import time + +// GLSL Include and functions + +#flag -I @VMODROOT/. +#include "rt_glsl.h" #Please use sokol-shdc to generate the necessary rt_glsl.h file from rt_glsl.glsl (see the instructions at the top of this file) + +fn C.rt_shader_desc(gfx.Backend) &C.sg_shader_desc + +const ( + win_width = 800 + win_height = 800 + bg_color = gx.white +) + +struct App { +mut: + gg &gg.Context + texture C.sg_image + init_flag bool + frame_count int + + mouse_x int = -1 + mouse_y int = -1 + // glsl + cube_pip_glsl C.sg_pipeline + cube_bind C.sg_bindings + // time + ticks i64 +} + +/****************************************************************************** +* Texture functions +******************************************************************************/ +fn create_texture(w int, h int, buf &byte) C.sg_image { + sz := w * h * 4 + mut img_desc := C.sg_image_desc{ + width: w + height: h + num_mipmaps: 0 + min_filter: .linear + mag_filter: .linear + // usage: .dynamic + wrap_u: .clamp_to_edge + wrap_v: .clamp_to_edge + label: &byte(0) + d3d11_texture: 0 + } + // comment if .dynamic is enabled + img_desc.data.subimage[0][0] = C.sg_range{ + ptr: buf + size: size_t(sz) + } + + sg_img := C.sg_make_image(&img_desc) + return sg_img +} + +fn destroy_texture(sg_img C.sg_image) { + C.sg_destroy_image(sg_img) +} + +// Use only if usage: .dynamic is enabled +fn update_text_texture(sg_img C.sg_image, w int, h int, buf &byte) { + sz := w * h * 4 + mut tmp_sbc := C.sg_image_data{} + tmp_sbc.subimage[0][0] = C.sg_range{ + ptr: buf + size: size_t(sz) + } + C.sg_update_image(sg_img, &tmp_sbc) +} + +/****************************************************************************** +* Draw functions +****************************************************************************** +Cube vertex buffer with packed vertex formats for color and texture coords. + Note that a vertex format which must be portable across all + backends must only use the normalized integer formats + (BYTE4N, UBYTE4N, SHORT2N, SHORT4N), which can be converted + to floating point formats in the vertex shader inputs. + The reason is that D3D11 cannot convert from non-normalized + formats to floating point inputs (only to integer inputs), + and WebGL2 / GLES2 don't support integer vertex shader inputs. +*/ + +struct Vertex_t { + x f32 + y f32 + z f32 + color u32 + u f32 + v f32 + // u u16 // for compatibility with D3D11 + // v u16 // for compatibility with D3D11 +} + +fn init_cube_glsl(mut app App) { + // cube vertex buffer + // d := u16(32767) // for compatibility with D3D11, 32767 stand for 1 + d := f32(1.0) + c := u32(0xFFFFFF_FF) // color RGBA8 + vertices := [ + // Face 0 + Vertex_t{-1.0, -1.0, -1.0, c, 0, 0}, + Vertex_t{ 1.0, -1.0, -1.0, c, d, 0}, + Vertex_t{ 1.0, 1.0, -1.0, c, d, d}, + Vertex_t{-1.0, 1.0, -1.0, c, 0, d}, + // Face 1 + Vertex_t{-1.0, -1.0, 1.0, c, 0, 0}, + Vertex_t{ 1.0, -1.0, 1.0, c, d, 0}, + Vertex_t{ 1.0, 1.0, 1.0, c, d, d}, + Vertex_t{-1.0, 1.0, 1.0, c, 0, d}, + // Face 2 + Vertex_t{-1.0, -1.0, -1.0, c, 0, 0}, + Vertex_t{-1.0, 1.0, -1.0, c, d, 0}, + Vertex_t{-1.0, 1.0, 1.0, c, d, d}, + Vertex_t{-1.0, -1.0, 1.0, c, 0, d}, + // Face 3 + Vertex_t{ 1.0, -1.0, -1.0, c, 0, 0}, + Vertex_t{ 1.0, 1.0, -1.0, c, d, 0}, + Vertex_t{ 1.0, 1.0, 1.0, c, d, d}, + Vertex_t{ 1.0, -1.0, 1.0, c, 0, d}, + // Face 4 + Vertex_t{-1.0, -1.0, -1.0, c, 0, 0}, + Vertex_t{-1.0, -1.0, 1.0, c, d, 0}, + Vertex_t{ 1.0, -1.0, 1.0, c, d, d}, + Vertex_t{ 1.0, -1.0, -1.0, c, 0, d}, + // Face 5 + Vertex_t{-1.0, 1.0, -1.0, c, 0, 0}, + Vertex_t{-1.0, 1.0, 1.0, c, d, 0}, + Vertex_t{ 1.0, 1.0, 1.0, c, d, d}, + Vertex_t{ 1.0, 1.0, -1.0, c, 0, d}, + ] + + mut vert_buffer_desc := C.sg_buffer_desc{label: c'cube-vertices'} + unsafe { C.memset(&vert_buffer_desc, 0, sizeof(vert_buffer_desc)) } + + vert_buffer_desc.size = size_t(vertices.len * int(sizeof(Vertex_t))) + vert_buffer_desc.data = C.sg_range{ + ptr: vertices.data + size: size_t(vertices.len * int(sizeof(Vertex_t))) + } + + vert_buffer_desc.@type = .vertexbuffer + vbuf := gfx.make_buffer(&vert_buffer_desc) + + // create an index buffer for the cube + indices := [ + u16(0), 1, 2, 0, 2, 3, + 6, 5, 4, 7, 6, 4, + 8, 9, 10, 8, 10, 11, + 14, 13, 12, 15, 14, 12, + 16, 17, 18, 16, 18, 19, + 22, 21, 20, 23, 22, 20, + ] + + mut index_buffer_desc := C.sg_buffer_desc{label: c'cube-indices'} + unsafe {C.memset(&index_buffer_desc, 0, sizeof(index_buffer_desc))} + + index_buffer_desc.size = size_t(indices.len * int(sizeof(u16))) + index_buffer_desc.data = C.sg_range{ + ptr: indices.data + size: size_t(indices.len * int(sizeof(u16))) + } + + index_buffer_desc.@type = .indexbuffer + ibuf := gfx.make_buffer(&index_buffer_desc) + + // create shader + shader := gfx.make_shader(C.rt_shader_desc(C.sg_query_backend())) + + mut pipdesc := C.sg_pipeline_desc{} + unsafe { C.memset(&pipdesc, 0, sizeof(pipdesc)) } + pipdesc.layout.buffers[0].stride = int(sizeof(Vertex_t)) + + // the constants [C.ATTR_vs_pos, C.ATTR_vs_color0, C.ATTR_vs_texcoord0] are generated by sokol-shdc + pipdesc.layout.attrs[C.ATTR_vs_pos ].format = .float3 // x,y,z as f32 + pipdesc.layout.attrs[C.ATTR_vs_color0 ].format = .ubyte4n // color as u32 + pipdesc.layout.attrs[C.ATTR_vs_texcoord0].format = .float2 // u,v as f32 + // pipdesc.layout.attrs[C.ATTR_vs_texcoord0].format = .short2n // u,v as u16 + + pipdesc.shader = shader + pipdesc.index_type = .uint16 + + pipdesc.depth = C.sg_depth_state{ + write_enabled: true + compare: gfx.CompareFunc(C.SG_COMPAREFUNC_LESS_EQUAL) + } + pipdesc.cull_mode = .back + + pipdesc.label = 'glsl_shader pipeline'.str + + app.cube_bind.vertex_buffers[0] = vbuf + app.cube_bind.index_buffer = ibuf + app.cube_bind.fs_images[C.SLOT_tex] = app.texture + app.cube_pip_glsl = gfx.make_pipeline(&pipdesc) + println('GLSL init DONE!') +} + +[inline] +fn vec4(x f32, y f32, z f32, w f32) m4.Vec4 { + return m4.Vec4{e:[x, y, z, w]!} +} + +fn calc_tr_matrices(w f32, h f32, rx f32, ry f32, in_scale f32) m4.Mat4 { + proj := m4.perspective(60, w/h, 0.01, 10.0) + view := m4.look_at(vec4(f32(0.0) ,0 , 6, 0), vec4(f32(0), 0, 0, 0), vec4(f32(0), 1, 0, 0)) + view_proj := view * proj + + rxm := m4.rotate(m4.rad(rx), vec4(f32(1), 0, 0, 0)) + rym := m4.rotate(m4.rad(ry), vec4(f32(0), 1, 0, 0)) + + model := rym * rxm + scale_m := m4.scale(vec4(in_scale, in_scale, in_scale, 1)) + + res := (scale_m * model) * view_proj + return res +} + +fn draw_cube_glsl(app App) { + if app.init_flag == false { + return + } + + ws := gg.window_size_real_pixels() + ratio := f32(ws.width) / ws.height + dw := f32(ws.width / 2) + dh := f32(ws.height / 2) + + // use the following commented lines to rotate the 3d glsl cube + // rot := [f32(app.mouse_y), f32(app.mouse_x)] + // calc_tr_matrices(dw, dh, rot[0], rot[1] ,2.3) + tr_matrix := calc_tr_matrices(dw, dh, 0, 0, 2.3) + gfx.apply_viewport(0, 0, ws.width, ws.height, true) + + // apply the pipline and bindings + gfx.apply_pipeline(app.cube_pip_glsl) + gfx.apply_bindings(app.cube_bind) + + // Uniforms + // *** vertex shadeer uniforms *** + // passing the view matrix as uniform + // res is a 4x4 matrix of f32 thus: 4*16 byte of size + vs_uniforms_range := C.sg_range{ + ptr: &tr_matrix + size: size_t(4 * 16) + } + gfx.apply_uniforms(C.SG_SHADERSTAGE_VS, C.SLOT_vs_params, &vs_uniforms_range) + + // *** fragment shader uniforms *** + time_ticks := f32(time.ticks() - app.ticks) / 1000 + mut tmp_fs_params := [ + f32(ws.width), + ws.height * ratio, // x,y resolution to pass to FS + app.mouse_x, // mouse x + ws.height - app.mouse_y * 2, // mouse y scaled + time_ticks, // time as f32 + app.frame_count, // frame count + 0, + 0 // padding bytes , see "fs_params" struct paddings in rt_glsl.h + ]! + fs_uniforms_range := C.sg_range{ + ptr: unsafe { &tmp_fs_params } + size: size_t(sizeof(tmp_fs_params)) + } + gfx.apply_uniforms(C.SG_SHADERSTAGE_FS, C.SLOT_fs_params, &fs_uniforms_range) + + // 3 vertices for triangle * 2 triangles per face * 6 faces = 36 vertices to draw + gfx.draw(0, (3 * 2) * 6, 1) + gfx.end_pass() + gfx.commit() +} + +fn frame(mut app App) { + ws := gg.window_size_real_pixels() + + // clear + mut color_action := C.sg_color_attachment_action{ + action: gfx.Action(C.SG_ACTION_CLEAR) + value: C.sg_color{ + r: 0.0 + g: 0.0 + b: 0.0 + a: 1.0 + } + } + + mut pass_action := C.sg_pass_action{} + pass_action.colors[0] = color_action + gfx.begin_default_pass(&pass_action, ws.width, ws.height) + + // glsl cube + draw_cube_glsl(app) + app.frame_count++ +} + +/****************************************************************************** +* Init / Cleanup +******************************************************************************/ +fn my_init(mut app App) { + // set max vertices, + // for a large number of the same type of object it is better use the instances!! + desc := sapp.create_desc() + gfx.setup(&desc) + sgl_desc := C.sgl_desc_t{ + max_vertices: 50 * 65536 + } + sgl.setup(&sgl_desc) + + // create chessboard texture 256*256 RGBA + w := 256 + h := 256 + sz := w * h * 4 + tmp_txt := unsafe { malloc(sz) } + mut i := 0 + for i < sz { + unsafe { + y := (i >> 0x8) >> 5 // 8 cell + x := (i & 0xFF) >> 5 // 8 cell + // upper left corner + if x == 0 && y == 0 { + tmp_txt[i + 0] = byte(0xFF) + tmp_txt[i + 1] = byte(0) + tmp_txt[i + 2] = byte(0) + tmp_txt[i + 3] = byte(0xFF) + } + // low right corner + else if x == 7 && y == 7 { + tmp_txt[i + 0] = byte(0) + tmp_txt[i + 1] = byte(0xFF) + tmp_txt[i + 2] = byte(0) + tmp_txt[i + 3] = byte(0xFF) + } else { + col := if ((x + y) & 1) == 1 { 0xFF } else { 128 } + tmp_txt[i + 0] = byte(col) // red + tmp_txt[i + 1] = byte(col) // green + tmp_txt[i + 2] = byte(col) // blue + tmp_txt[i + 3] = byte(0xFF) // alpha + } + i += 4 + } + } + unsafe { + app.texture = create_texture(w, h, tmp_txt) + free(tmp_txt) + } + // glsl + init_cube_glsl(mut app) + app.init_flag = true +} + +fn cleanup(mut app App) { + gfx.shutdown() +} + +/****************************************************************************** +* events handling +******************************************************************************/ +fn my_event_manager(mut ev gg.Event, mut app App) { + if ev.typ == .mouse_move { + app.mouse_x = int(ev.mouse_x) + app.mouse_y = int(ev.mouse_y) + } + if ev.typ == .touches_began || ev.typ == .touches_moved { + if ev.num_touches > 0 { + touch_point := ev.touches[0] + app.mouse_x = int(touch_point.pos_x) + app.mouse_y = int(touch_point.pos_y) + } + } +} + +/****************************************************************************** +* Main +******************************************************************************/ +[console] // is needed for easier diagnostics on windows +fn main() { + // App init + mut app := &App{ + gg: 0 + } + + app.gg = gg.new_context( + width: win_width + height: win_height + create_window: true + window_title: '3D Ray Marching Cube' + user_data: app + bg_color: bg_color + frame_fn: frame + init_fn: my_init + cleanup_fn: cleanup + event_fn: my_event_manager + ) + + app.ticks = time.ticks() + app.gg.run() +} diff --git a/v_windows/v/old/examples/sokol/03_march_tracing_glsl/v.mod b/v_windows/v/old/examples/sokol/03_march_tracing_glsl/v.mod new file mode 100644 index 0000000..e69de29 diff --git a/v_windows/v/old/examples/sokol/04_multi_shader_glsl/rt_glsl.v b/v_windows/v/old/examples/sokol/04_multi_shader_glsl/rt_glsl.v new file mode 100644 index 0000000..d19d199 --- /dev/null +++ b/v_windows/v/old/examples/sokol/04_multi_shader_glsl/rt_glsl.v @@ -0,0 +1,634 @@ +/********************************************************************** +* +* Sokol 3d cube multishader demo +* +* Copyright (c) 2021 Dario Deledda. All rights reserved. +* Use of this source code is governed by an MIT license +* that can be found in the LICENSE file. +* +* HOW TO COMPILE SHADERS: +* - download the sokol shader convertor tool from https://github.com/floooh/sokol-tools-bin +* +* - compile the .glsl shared file with: +* linux : sokol-shdc --input rt_glsl_puppy.glsl --output rt_glsl_puppy.h --slang glsl330 + sokol-shdc --input rt_glsl_march.glsl --output rt_glsl_march.h --slang glsl330 +* windows: sokol-shdc.exe --input rt_glsl_puppy.glsl --output rt_glsl_puppy.h --slang glsl330 +* sokol-shdc.exe --input rt_glsl_march.glsl --output rt_glsl_march.h --slang glsl330 +* +* --slang parameter can be: +* - glsl330: desktop GL +* - glsl100: GLES2 / WebGL +* - glsl300es: GLES3 / WebGL2 +* - hlsl4: D3D11 +* - hlsl5: D3D11 +* - metal_macos: Metal on macOS +* - metal_ios: Metal on iOS device +* - metal_sim: Metal on iOS simulator +* - wgpu: WebGPU +* +* you can have multiple platforms at the same time passing parameters like this: --slang glsl330:hlsl5:metal_macos +* for further infos have a look at the sokol shader tool docs. +* +* TODO: +* - frame counter +**********************************************************************/ +import gg +import gg.m4 +import gx +// import math +import sokol.sapp +import sokol.gfx +import sokol.sgl +import time + +// GLSL Include and functions +#flag -I @VMODROOT/. +#include "rt_glsl_march.h" #Please use sokol-shdc to generate the necessary rt_glsl_march.h file from rt_glsl_march.glsl (see the instructions at the top of this file) +#include "rt_glsl_puppy.h" #Please use sokol-shdc to generate the necessary rt_glsl_puppy.h file from rt_glsl_puppy.glsl (see the instructions at the top of this file) +fn C.rt_march_shader_desc(gfx.Backend) &C.sg_shader_desc +fn C.rt_puppy_shader_desc(gfx.Backend) &C.sg_shader_desc + +const ( + win_width = 800 + win_height = 800 + bg_color = gx.white +) + +struct App { +mut: + gg &gg.Context + texture C.sg_image + init_flag bool + frame_count int + mouse_x int = -1 + mouse_y int = -1 + mouse_down bool + // glsl + cube_pip_glsl C.sg_pipeline + cube_bind C.sg_bindings + pipe map[string]C.sg_pipeline + bind map[string]C.sg_bindings + // time + ticks i64 +} + +/****************************************************************************** +* Texture functions +******************************************************************************/ +fn create_texture(w int, h int, buf byteptr) C.sg_image { + sz := w * h * 4 + mut img_desc := C.sg_image_desc{ + width: w + height: h + num_mipmaps: 0 + min_filter: .linear + mag_filter: .linear + // usage: .dynamic + wrap_u: .clamp_to_edge + wrap_v: .clamp_to_edge + label: &byte(0) + d3d11_texture: 0 + } + // comment if .dynamic is enabled + img_desc.data.subimage[0][0] = C.sg_range{ + ptr: buf + size: size_t(sz) + } + + sg_img := C.sg_make_image(&img_desc) + return sg_img +} + +fn destroy_texture(sg_img C.sg_image) { + C.sg_destroy_image(sg_img) +} + +// Use only if usage: .dynamic is enabled +fn update_text_texture(sg_img C.sg_image, w int, h int, buf byteptr) { + sz := w * h * 4 + mut tmp_sbc := C.sg_image_data{} + tmp_sbc.subimage[0][0] = C.sg_range{ + ptr: buf + size: size_t(sz) + } + C.sg_update_image(sg_img, &tmp_sbc) +} + +/****************************************************************************** +* Draw functions +****************************************************************************** +Cube vertex buffer with packed vertex formats for color and texture coords. + Note that a vertex format which must be portable across all + backends must only use the normalized integer formats + (BYTE4N, UBYTE4N, SHORT2N, SHORT4N), which can be converted + to floating point formats in the vertex shader inputs. + The reason is that D3D11 cannot convert from non-normalized + formats to floating point inputs (only to integer inputs), + and WebGL2 / GLES2 don't support integer vertex shader inputs. +*/ +struct Vertex_t { + x f32 + y f32 + z f32 + color u32 + // u u16 // for compatibility with D3D11 + // v u16 // for compatibility with D3D11 + u f32 + v f32 +} + +// march shader init +fn init_cube_glsl_m(mut app App) { + // cube vertex buffer + // d := u16(32767) // for compatibility with D3D11, 32767 stand for 1 + d := f32(1.0) + c := u32(0xFFFFFF_FF) // color RGBA8 + vertices := [ + // Face 0 + Vertex_t{-1.0, -1.0, -1.0, c, 0, 0}, + Vertex_t{ 1.0, -1.0, -1.0, c, d, 0}, + Vertex_t{ 1.0, 1.0, -1.0, c, d, d}, + Vertex_t{-1.0, 1.0, -1.0, c, 0, d}, + // Face 1 + Vertex_t{-1.0, -1.0, 1.0, c, 0, 0}, + Vertex_t{ 1.0, -1.0, 1.0, c, d, 0}, + Vertex_t{ 1.0, 1.0, 1.0, c, d, d}, + Vertex_t{-1.0, 1.0, 1.0, c, 0, d}, + // Face 2 + Vertex_t{-1.0, -1.0, -1.0, c, 0, 0}, + Vertex_t{-1.0, 1.0, -1.0, c, d, 0}, + Vertex_t{-1.0, 1.0, 1.0, c, d, d}, + Vertex_t{-1.0, -1.0, 1.0, c, 0, d}, + // Face 3 + Vertex_t{ 1.0, -1.0, -1.0, c, 0, 0}, + Vertex_t{ 1.0, 1.0, -1.0, c, d, 0}, + Vertex_t{ 1.0, 1.0, 1.0, c, d, d}, + Vertex_t{ 1.0, -1.0, 1.0, c, 0, d}, + // Face 4 + Vertex_t{-1.0, -1.0, -1.0, c, 0, 0}, + Vertex_t{-1.0, -1.0, 1.0, c, d, 0}, + Vertex_t{ 1.0, -1.0, 1.0, c, d, d}, + Vertex_t{ 1.0, -1.0, -1.0, c, 0, d}, + // Face 5 + Vertex_t{-1.0, 1.0, -1.0, c, 0, 0}, + Vertex_t{-1.0, 1.0, 1.0, c, d, 0}, + Vertex_t{ 1.0, 1.0, 1.0, c, d, d}, + Vertex_t{ 1.0, 1.0, -1.0, c, 0, d}, + ] + + mut vert_buffer_desc := C.sg_buffer_desc{label: c'cube-vertices'} + unsafe { C.memset(&vert_buffer_desc, 0, sizeof(vert_buffer_desc)) } + vert_buffer_desc.size = size_t(vertices.len * int(sizeof(Vertex_t))) + vert_buffer_desc.data = C.sg_range{ + ptr: vertices.data + size: size_t(vertices.len * int(sizeof(Vertex_t))) + } + vert_buffer_desc.@type = .vertexbuffer + vbuf := gfx.make_buffer(&vert_buffer_desc) + + /* create an index buffer for the cube */ + indices := [ + u16(0), 1, 2, 0, 2, 3, + 6, 5, 4, 7, 6, 4, + 8, 9, 10, 8, 10, 11, +/* + u16(14), 13, 12, 15, 14, 12, + 16, 17, 18, 16, 18, 19, + 22, 21, 20, 23, 22, 20 +*/ + ] + + mut index_buffer_desc := C.sg_buffer_desc{label: c'cube-indices'} + unsafe { C.memset(&index_buffer_desc, 0, sizeof(index_buffer_desc)) } + index_buffer_desc.size = size_t(indices.len * int(sizeof(u16))) + index_buffer_desc.data = C.sg_range{ + ptr: indices.data + size: size_t(indices.len * int(sizeof(u16))) + } + index_buffer_desc.@type = .indexbuffer + ibuf := gfx.make_buffer(&index_buffer_desc) + + // create shader + shader := gfx.make_shader(C.rt_march_shader_desc(C.sg_query_backend())) + + mut pipdesc := C.sg_pipeline_desc{} + unsafe { C.memset(&pipdesc, 0, sizeof(pipdesc)) } + pipdesc.layout.buffers[0].stride = int(sizeof(Vertex_t)) + + // the constants [C.ATTR_vs_m_pos, C.ATTR_vs_m_color0, C.ATTR_vs_m_texcoord0] are generated by sokol-shdc + pipdesc.layout.attrs[C.ATTR_vs_m_pos ].format = .float3 // x,y,z as f32 + pipdesc.layout.attrs[C.ATTR_vs_m_color0 ].format = .ubyte4n // color as u32 + pipdesc.layout.attrs[C.ATTR_vs_m_texcoord0].format = .float2 // u,v as f32 + // pipdesc.layout.attrs[C.ATTR_vs_m_texcoord0].format = .short2n // u,v as u16 + + pipdesc.shader = shader + pipdesc.index_type = .uint16 + + pipdesc.depth = C.sg_depth_state{ + write_enabled: true + compare: gfx.CompareFunc(C.SG_COMPAREFUNC_LESS_EQUAL) + } + pipdesc.cull_mode = .back + pipdesc.label = 'glsl_shader pipeline'.str + + mut bind := C.sg_bindings{} + unsafe { C.memset(&bind, 0, sizeof(bind)) } + bind.vertex_buffers[0] = vbuf + bind.index_buffer = ibuf + bind.fs_images[C.SLOT_tex] = app.texture + app.bind['march'] = bind + + app.pipe['march'] = gfx.make_pipeline(&pipdesc) + + println('GLSL March init DONE!') +} + +// putty shader init +fn init_cube_glsl_p(mut app App) { + // cube vertex buffer + // d := u16(32767) // for compatibility with D3D11, 32767 stand for 1 + d := f32(1.0) + c := u32(0xFFFFFF_FF) // color RGBA8 + vertices := [ + // Face 0 + Vertex_t{-1.0, -1.0, -1.0, c, 0, 0}, + Vertex_t{ 1.0, -1.0, -1.0, c, d, 0}, + Vertex_t{ 1.0, 1.0, -1.0, c, d, d}, + Vertex_t{-1.0, 1.0, -1.0, c, 0, d}, + // Face 1 + Vertex_t{-1.0, -1.0, 1.0, c, 0, 0}, + Vertex_t{ 1.0, -1.0, 1.0, c, d, 0}, + Vertex_t{ 1.0, 1.0, 1.0, c, d, d}, + Vertex_t{-1.0, 1.0, 1.0, c, 0, d}, + // Face 2 + Vertex_t{-1.0, -1.0, -1.0, c, 0, 0}, + Vertex_t{-1.0, 1.0, -1.0, c, d, 0}, + Vertex_t{-1.0, 1.0, 1.0, c, d, d}, + Vertex_t{-1.0, -1.0, 1.0, c, 0, d}, + // Face 3 + Vertex_t{ 1.0, -1.0, -1.0, c, 0, 0}, + Vertex_t{ 1.0, 1.0, -1.0, c, d, 0}, + Vertex_t{ 1.0, 1.0, 1.0, c, d, d}, + Vertex_t{ 1.0, -1.0, 1.0, c, 0, d}, + // Face 4 + Vertex_t{-1.0, -1.0, -1.0, c, 0, 0}, + Vertex_t{-1.0, -1.0, 1.0, c, d, 0}, + Vertex_t{ 1.0, -1.0, 1.0, c, d, d}, + Vertex_t{ 1.0, -1.0, -1.0, c, 0, d}, + // Face 5 + Vertex_t{-1.0, 1.0, -1.0, c, 0, 0}, + Vertex_t{-1.0, 1.0, 1.0, c, d, 0}, + Vertex_t{ 1.0, 1.0, 1.0, c, d, d}, + Vertex_t{ 1.0, 1.0, -1.0, c, 0, d}, + ] + + mut vert_buffer_desc := C.sg_buffer_desc{label: c'cube-vertices'} + unsafe { C.memset(&vert_buffer_desc, 0, sizeof(vert_buffer_desc)) } + vert_buffer_desc.size = size_t(vertices.len * int(sizeof(Vertex_t))) + vert_buffer_desc.data = C.sg_range{ + ptr: vertices.data + size: size_t(vertices.len * int(sizeof(Vertex_t))) + } + vert_buffer_desc.@type = .vertexbuffer + vbuf := gfx.make_buffer(&vert_buffer_desc) + + /* create an index buffer for the cube */ + indices := [ +/* + u16(0), 1, 2, 0, 2, 3, + 6, 5, 4, 7, 6, 4, + 8, 9, 10, 8, 10, 11, +*/ + u16(14), 13, 12, 15, 14, 12, + 16, 17, 18, 16, 18, 19, + 22, 21, 20, 23, 22, 20 + + ] + + mut index_buffer_desc := C.sg_buffer_desc{label: c'cube-indices'} + unsafe { C.memset(&index_buffer_desc, 0, sizeof(index_buffer_desc)) } + index_buffer_desc.size = size_t(indices.len * int(sizeof(u16))) + index_buffer_desc.data = C.sg_range{ + ptr: indices.data + size: size_t(indices.len * int(sizeof(u16))) + } + index_buffer_desc.@type = .indexbuffer + ibuf := gfx.make_buffer(&index_buffer_desc) + + // create shader + shader := gfx.make_shader(C.rt_puppy_shader_desc(C.sg_query_backend())) + + mut pipdesc := C.sg_pipeline_desc{} + unsafe { C.memset(&pipdesc, 0, sizeof(pipdesc)) } + pipdesc.layout.buffers[0].stride = int(sizeof(Vertex_t)) + + // the constants [C.ATTR_vs_p_pos, C.ATTR_vs_p_color0, C.ATTR_vs_p_texcoord0] are generated by sokol-shdc + pipdesc.layout.attrs[C.ATTR_vs_p_pos ].format = .float3 // x,y,z as f32 + pipdesc.layout.attrs[C.ATTR_vs_p_color0 ].format = .ubyte4n // color as u32 + pipdesc.layout.attrs[C.ATTR_vs_p_texcoord0].format = .float2 // u,v as f32 + // pipdesc.layout.attrs[C.ATTR_vs_p_texcoord0].format = .short2n // u,v as u16 + + pipdesc.shader = shader + pipdesc.index_type = .uint16 + + pipdesc.depth = C.sg_depth_state{ + write_enabled: true + compare: gfx.CompareFunc(C.SG_COMPAREFUNC_LESS_EQUAL) + } + pipdesc.cull_mode = .back + + pipdesc.label = 'glsl_shader pipeline'.str + + mut bind := C.sg_bindings{} + unsafe { C.memset(&bind, 0, sizeof(bind)) } + bind.vertex_buffers[0] = vbuf + bind.index_buffer = ibuf + bind.fs_images[C.SLOT_tex] = app.texture + app.bind['puppy'] = bind + + app.pipe['puppy'] = gfx.make_pipeline(&pipdesc) + + println('GLSL Puppy init DONE!') +} + +[inline] +fn vec4(x f32, y f32, z f32, w f32) m4.Vec4 { + return m4.Vec4{e:[x, y, z, w]!} +} + +fn calc_tr_matrices(w f32, h f32, rx f32, ry f32, in_scale f32) m4.Mat4 { + proj := m4.perspective(60, w/h, 0.01, 10.0) + view := m4.look_at(vec4(f32(0.0) ,0 , 6, 0), vec4(f32(0), 0, 0, 0), vec4(f32(0), 1, 0, 0)) + view_proj := view * proj + + rxm := m4.rotate(m4.rad(rx), vec4(f32(1), 0, 0, 0)) + rym := m4.rotate(m4.rad(ry), vec4(f32(0), 1, 0, 0)) + + model := rym * rxm + scale_m := m4.scale(vec4(in_scale, in_scale, in_scale, 1)) + + res := (scale_m * model) * view_proj + return res +} + +// march triangles draw +fn draw_cube_glsl_m(app App) { + if app.init_flag == false { + return + } + + ws := gg.window_size_real_pixels() + ratio := f32(ws.width) / ws.height + dw := f32(ws.width / 2) + dh := f32(ws.height / 2) + + rot := [f32(app.mouse_y), f32(app.mouse_x)] + tr_matrix := calc_tr_matrices(dw, dh, rot[0], rot[1], 2.3) + + gfx.apply_pipeline(app.pipe['march']) + gfx.apply_bindings(app.bind['march']) + + // Uniforms + // *** vertex shadeer uniforms *** + // passing the view matrix as uniform + // res is a 4x4 matrix of f32 thus: 4*16 byte of size + vs_uniforms_range := C.sg_range{ + ptr: &tr_matrix + size: size_t(4 * 16) + } + gfx.apply_uniforms(C.SG_SHADERSTAGE_VS, C.SLOT_vs_params_m, &vs_uniforms_range) + + // *** fragment shader uniforms *** + time_ticks := f32(time.ticks() - app.ticks) / 1000 + mut tmp_fs_params := [ + f32(ws.width), + ws.height * ratio, // x,y resolution to pass to FS + 0, + 0, // dont send mouse position + /* app.mouse_x, // mouse x */ + /* ws.height - app.mouse_y*2, // mouse y scaled */ + time_ticks, // time as f32 + app.frame_count, // frame count + 0, + 0 // padding bytes , see "fs_params" struct paddings in rt_glsl.h + ]! + fs_uniforms_range := C.sg_range{ + ptr: unsafe { &tmp_fs_params } + size: size_t(sizeof(tmp_fs_params)) + } + gfx.apply_uniforms(C.SG_SHADERSTAGE_FS, C.SLOT_fs_params_p, &fs_uniforms_range) + + // 3 vertices for triangle * 2 triangles per face * 6 faces = 36 vertices to draw + gfx.draw(0, (3 * 2) * 3, 1) +} + +// puppy triangles draw +fn draw_cube_glsl_p(app App) { + if app.init_flag == false { + return + } + + ws := gg.window_size_real_pixels() + ratio := f32(ws.width) / ws.height + dw := f32(ws.width / 2) + dh := f32(ws.height / 2) + + rot := [f32(app.mouse_y), f32(app.mouse_x)] + tr_matrix := calc_tr_matrices(dw, dh, rot[0], rot[1], 2.3) + + // apply the pipline and bindings + gfx.apply_pipeline(app.pipe['puppy']) + gfx.apply_bindings(app.bind['puppy']) + + // Uniforms + // *** vertex shadeer uniforms *** + // passing the view matrix as uniform + // res is a 4x4 matrix of f32 thus: 4*16 byte of size + vs_uniforms_range := C.sg_range{ + ptr: &tr_matrix + size: size_t(4 * 16) + } + gfx.apply_uniforms(C.SG_SHADERSTAGE_VS, C.SLOT_vs_params_p, &vs_uniforms_range) + + // *** fragment shader uniforms *** + time_ticks := f32(time.ticks() - app.ticks) / 1000 + mut tmp_fs_params := [ + f32(ws.width), + ws.height * ratio, // x,y resolution to pass to FS + 0, + 0, // dont send mouse position + /* app.mouse_x, // mouse x */ + /* ws.height - app.mouse_y*2, // mouse y scaled */ + time_ticks, // time as f32 + app.frame_count, // frame count + 0, + 0 // padding bytes , see "fs_params" struct paddings in rt_glsl.h + ]! + fs_uniforms_range := C.sg_range{ + ptr: unsafe { &tmp_fs_params } + size: size_t(sizeof(tmp_fs_params)) + } + gfx.apply_uniforms(C.SG_SHADERSTAGE_FS, C.SLOT_fs_params_p, &fs_uniforms_range) + + // 3 vertices for triangle * 2 triangles per face * 6 faces = 36 vertices to draw + gfx.draw(0, (3 * 2) * 3, 1) +} + +fn draw_start_glsl(app App) { + if app.init_flag == false { + return + } + + ws := gg.window_size_real_pixels() + // ratio := f32(ws.width) / ws.height + // dw := f32(ws.width / 2) + // dh := f32(ws.height / 2) + + gfx.apply_viewport(0, 0, ws.width, ws.height, true) +} + +fn draw_end_glsl(app App) { + gfx.end_pass() + gfx.commit() +} + +fn frame(mut app App) { + ws := gg.window_size_real_pixels() + + // clear + mut color_action := C.sg_color_attachment_action{ + action: gfx.Action(C.SG_ACTION_CLEAR) + value: C.sg_color{ + r: 0.0 + g: 0.0 + b: 0.0 + a: 1.0 + } + } + mut pass_action := C.sg_pass_action{} + pass_action.colors[0] = color_action + gfx.begin_default_pass(&pass_action, ws.width, ws.height) + + /* + // glsl cube + if app.frame_count % 1 == 1{ + draw_cube_glsl_m(app) + } else { + draw_cube_glsl_p(app) + } + */ + draw_start_glsl(app) + draw_cube_glsl_m(app) + draw_cube_glsl_p(app) + draw_end_glsl(app) + app.frame_count++ +} + +/****************************************************************************** +* Init / Cleanup +******************************************************************************/ +fn my_init(mut app App) { + // set max vertices, + // for a large number of the same type of object it is better use the instances!! + desc := sapp.create_desc() + gfx.setup(&desc) + sgl_desc := C.sgl_desc_t{ + max_vertices: 50 * 65536 + } + sgl.setup(&sgl_desc) + + // create chessboard texture 256*256 RGBA + w := 256 + h := 256 + sz := w * h * 4 + tmp_txt := unsafe { malloc(sz) } + mut i := 0 + for i < sz { + unsafe { + y := (i >> 0x8) >> 5 // 8 cell + x := (i & 0xFF) >> 5 // 8 cell + // upper left corner + if x == 0 && y == 0 { + tmp_txt[i + 0] = byte(0xFF) + tmp_txt[i + 1] = byte(0) + tmp_txt[i + 2] = byte(0) + tmp_txt[i + 3] = byte(0xFF) + } + // low right corner + else if x == 7 && y == 7 { + tmp_txt[i + 0] = byte(0) + tmp_txt[i + 1] = byte(0xFF) + tmp_txt[i + 2] = byte(0) + tmp_txt[i + 3] = byte(0xFF) + } else { + col := if ((x + y) & 1) == 1 { 0xFF } else { 128 } + tmp_txt[i + 0] = byte(col) // red + tmp_txt[i + 1] = byte(col) // green + tmp_txt[i + 2] = byte(col) // blue + tmp_txt[i + 3] = byte(0xFF) // alpha + } + i += 4 + } + } + app.texture = create_texture(w, h, tmp_txt) + unsafe { free(tmp_txt) } + + // glsl + init_cube_glsl_m(mut app) + init_cube_glsl_p(mut app) + app.init_flag = true +} + +fn cleanup(mut app App) { + gfx.shutdown() +} + +/****************************************************************************** +* events handling +******************************************************************************/ +fn my_event_manager(mut ev gg.Event, mut app App) { + if ev.typ == .mouse_down { + app.mouse_down = true + } + if ev.typ == .mouse_up { + app.mouse_down = false + } + if app.mouse_down == true && ev.typ == .mouse_move { + app.mouse_x = int(ev.mouse_x) + app.mouse_y = int(ev.mouse_y) + } + if ev.typ == .touches_began || ev.typ == .touches_moved { + if ev.num_touches > 0 { + touch_point := ev.touches[0] + app.mouse_x = int(touch_point.pos_x) + app.mouse_y = int(touch_point.pos_y) + } + } +} + +/****************************************************************************** +* Main +******************************************************************************/ +[console] // is needed for easier diagnostics on windows +fn main() { + // App init + mut app := &App{ + gg: 0 + } + + app.gg = gg.new_context( + width: win_width + height: win_height + create_window: true + window_title: '3D Dual shader Cube - click and rotate with the mouse' + user_data: app + bg_color: bg_color + frame_fn: frame + init_fn: my_init + cleanup_fn: cleanup + event_fn: my_event_manager + ) + + app.ticks = time.ticks() + app.gg.run() +} diff --git a/v_windows/v/old/examples/sokol/04_multi_shader_glsl/rt_glsl_march.glsl b/v_windows/v/old/examples/sokol/04_multi_shader_glsl/rt_glsl_march.glsl new file mode 100644 index 0000000..ed76e25 --- /dev/null +++ b/v_windows/v/old/examples/sokol/04_multi_shader_glsl/rt_glsl_march.glsl @@ -0,0 +1,695 @@ +//------------------------------------------------------------------------------ +// Shader code for texcube-sapp sample. +// +// NOTE: This source file also uses the '#pragma sokol' form of the +// custom tags. +//------------------------------------------------------------------------------ +//#pragma sokol @ctype mat4 hmm_mat4 + +#pragma sokol @vs vs_m +uniform vs_params_m { + mat4 mvp; +}; + +in vec4 pos; +in vec4 color0; +in vec2 texcoord0; + +out vec4 color; +out vec2 uv; + +void main() { + gl_Position = mvp * pos; + color = color0; + uv = texcoord0; +} +#pragma sokol @end + +#pragma sokol @fs fs_m +uniform sampler2D tex; +uniform fs_params_m { + vec2 iResolution; + vec2 iMouse; + float iTime; + float iFrame; +}; + +in vec4 color; +in vec2 uv; +out vec4 frag_color; + +// change to 0 to 4 to increment the AntiAliasing, +// increase AA will SLOW the rendering!! +#define AA 1 + +//********************************************************* +// Ray Marching +// original code from: https://www.shadertoy.com/view/Xds3zN +//********************************************************* +// The MIT License +// Copyright © 2013 Inigo Quilez +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +// A list of useful distance function to simple primitives. All +// these functions (except for ellipsoid) return an exact +// euclidean distance, meaning they produce a better SDF than +// what you'd get if you were constructing them from boolean +// operations. +// +// More info here: +// +// https://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm + +//------------------------------------------------------------------ +float dot2( in vec2 v ) { return dot(v,v); } +float dot2( in vec3 v ) { return dot(v,v); } +float ndot( in vec2 a, in vec2 b ) { return a.x*b.x - a.y*b.y; } + +float sdPlane( vec3 p ) +{ + return p.y; +} + +float sdSphere( vec3 p, float s ) +{ + return length(p)-s; +} + +float sdBox( vec3 p, vec3 b ) +{ + vec3 d = abs(p) - b; + return min(max(d.x,max(d.y,d.z)),0.0) + length(max(d,0.0)); +} + +float sdBoundingBox( vec3 p, vec3 b, float e ) +{ + p = abs(p )-b; + vec3 q = abs(p+e)-e; + + return min(min( + length(max(vec3(p.x,q.y,q.z),0.0))+min(max(p.x,max(q.y,q.z)),0.0), + length(max(vec3(q.x,p.y,q.z),0.0))+min(max(q.x,max(p.y,q.z)),0.0)), + length(max(vec3(q.x,q.y,p.z),0.0))+min(max(q.x,max(q.y,p.z)),0.0)); +} +float sdEllipsoid( in vec3 p, in vec3 r ) // approximated +{ + float k0 = length(p/r); + float k1 = length(p/(r*r)); + return k0*(k0-1.0)/k1; +} + +float sdTorus( vec3 p, vec2 t ) +{ + return length( vec2(length(p.xz)-t.x,p.y) )-t.y; +} + +float sdCappedTorus(in vec3 p, in vec2 sc, in float ra, in float rb) +{ + p.x = abs(p.x); + float k = (sc.y*p.x>sc.x*p.y) ? dot(p.xy,sc) : length(p.xy); + return sqrt( dot(p,p) + ra*ra - 2.0*ra*k ) - rb; +} + +float sdHexPrism( vec3 p, vec2 h ) +{ + vec3 q = abs(p); + + const vec3 k = vec3(-0.8660254, 0.5, 0.57735); + p = abs(p); + p.xy -= 2.0*min(dot(k.xy, p.xy), 0.0)*k.xy; + vec2 d = vec2( + length(p.xy - vec2(clamp(p.x, -k.z*h.x, k.z*h.x), h.x))*sign(p.y - h.x), + p.z-h.y ); + return min(max(d.x,d.y),0.0) + length(max(d,0.0)); +} + +float sdOctogonPrism( in vec3 p, in float r, float h ) +{ + const vec3 k = vec3(-0.9238795325, // sqrt(2+sqrt(2))/2 + 0.3826834323, // sqrt(2-sqrt(2))/2 + 0.4142135623 ); // sqrt(2)-1 + // reflections + p = abs(p); + p.xy -= 2.0*min(dot(vec2( k.x,k.y),p.xy),0.0)*vec2( k.x,k.y); + p.xy -= 2.0*min(dot(vec2(-k.x,k.y),p.xy),0.0)*vec2(-k.x,k.y); + // polygon side + p.xy -= vec2(clamp(p.x, -k.z*r, k.z*r), r); + vec2 d = vec2( length(p.xy)*sign(p.y), p.z-h ); + return min(max(d.x,d.y),0.0) + length(max(d,0.0)); +} + +float sdCapsule( vec3 p, vec3 a, vec3 b, float r ) +{ + vec3 pa = p-a, ba = b-a; + float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 ); + return length( pa - ba*h ) - r; +} + +float sdRoundCone( in vec3 p, in float r1, float r2, float h ) +{ + vec2 q = vec2( length(p.xz), p.y ); + + float b = (r1-r2)/h; + float a = sqrt(1.0-b*b); + float k = dot(q,vec2(-b,a)); + + if( k < 0.0 ) return length(q) - r1; + if( k > a*h ) return length(q-vec2(0.0,h)) - r2; + + return dot(q, vec2(a,b) ) - r1; +} + +float sdRoundCone(vec3 p, vec3 a, vec3 b, float r1, float r2) +{ + // sampling independent computations (only depend on shape) + vec3 ba = b - a; + float l2 = dot(ba,ba); + float rr = r1 - r2; + float a2 = l2 - rr*rr; + float il2 = 1.0/l2; + + // sampling dependant computations + vec3 pa = p - a; + float y = dot(pa,ba); + float z = y - l2; + float x2 = dot2( pa*l2 - ba*y ); + float y2 = y*y*l2; + float z2 = z*z*l2; + + // single square root! + float k = sign(rr)*rr*rr*x2; + if( sign(z)*a2*z2 > k ) return sqrt(x2 + z2) *il2 - r2; + if( sign(y)*a2*y2 < k ) return sqrt(x2 + y2) *il2 - r1; + return (sqrt(x2*a2*il2)+y*rr)*il2 - r1; +} + +float sdTriPrism( vec3 p, vec2 h ) +{ + const float k = sqrt(3.0); + h.x *= 0.5*k; + p.xy /= h.x; + p.x = abs(p.x) - 1.0; + p.y = p.y + 1.0/k; + if( p.x+k*p.y>0.0 ) p.xy=vec2(p.x-k*p.y,-k*p.x-p.y)/2.0; + p.x -= clamp( p.x, -2.0, 0.0 ); + float d1 = length(p.xy)*sign(-p.y)*h.x; + float d2 = abs(p.z)-h.y; + return length(max(vec2(d1,d2),0.0)) + min(max(d1,d2), 0.); +} + +// vertical +float sdCylinder( vec3 p, vec2 h ) +{ + vec2 d = abs(vec2(length(p.xz),p.y)) - h; + return min(max(d.x,d.y),0.0) + length(max(d,0.0)); +} + +// arbitrary orientation +float sdCylinder(vec3 p, vec3 a, vec3 b, float r) +{ + vec3 pa = p - a; + vec3 ba = b - a; + float baba = dot(ba,ba); + float paba = dot(pa,ba); + + float x = length(pa*baba-ba*paba) - r*baba; + float y = abs(paba-baba*0.5)-baba*0.5; + float x2 = x*x; + float y2 = y*y*baba; + float d = (max(x,y)<0.0)?-min(x2,y2):(((x>0.0)?x2:0.0)+((y>0.0)?y2:0.0)); + return sign(d)*sqrt(abs(d))/baba; +} + +// vertical +float sdCone( in vec3 p, in vec2 c, float h ) +{ + vec2 q = h*vec2(c.x,-c.y)/c.y; + vec2 w = vec2( length(p.xz), p.y ); + + vec2 a = w - q*clamp( dot(w,q)/dot(q,q), 0.0, 1.0 ); + vec2 b = w - q*vec2( clamp( w.x/q.x, 0.0, 1.0 ), 1.0 ); + float k = sign( q.y ); + float d = min(dot( a, a ),dot(b, b)); + float s = max( k*(w.x*q.y-w.y*q.x),k*(w.y-q.y) ); + return sqrt(d)*sign(s); +} + +float sdCappedCone( in vec3 p, in float h, in float r1, in float r2 ) +{ + vec2 q = vec2( length(p.xz), p.y ); + + vec2 k1 = vec2(r2,h); + vec2 k2 = vec2(r2-r1,2.0*h); + vec2 ca = vec2(q.x-min(q.x,(q.y < 0.0)?r1:r2), abs(q.y)-h); + vec2 cb = q - k1 + k2*clamp( dot(k1-q,k2)/dot2(k2), 0.0, 1.0 ); + float s = (cb.x < 0.0 && ca.y < 0.0) ? -1.0 : 1.0; + return s*sqrt( min(dot2(ca),dot2(cb)) ); +} + +float sdCappedCone(vec3 p, vec3 a, vec3 b, float ra, float rb) +{ + float rba = rb-ra; + float baba = dot(b-a,b-a); + float papa = dot(p-a,p-a); + float paba = dot(p-a,b-a)/baba; + + float x = sqrt( papa - paba*paba*baba ); + + float cax = max(0.0,x-((paba<0.5)?ra:rb)); + float cay = abs(paba-0.5)-0.5; + + float k = rba*rba + baba; + float f = clamp( (rba*(x-ra)+paba*baba)/k, 0.0, 1.0 ); + + float cbx = x-ra - f*rba; + float cby = paba - f; + + float s = (cbx < 0.0 && cay < 0.0) ? -1.0 : 1.0; + + return s*sqrt( min(cax*cax + cay*cay*baba, + cbx*cbx + cby*cby*baba) ); +} + +// c is the sin/cos of the desired cone angle +float sdSolidAngle(vec3 pos, vec2 c, float ra) +{ + vec2 p = vec2( length(pos.xz), pos.y ); + float l = length(p) - ra; + float m = length(p - c*clamp(dot(p,c),0.0,ra) ); + return max(l,m*sign(c.y*p.x-c.x*p.y)); +} + +float sdOctahedron(vec3 p, float s) +{ + p = abs(p); + float m = p.x + p.y + p.z - s; + +// exact distance +#if 0 + vec3 o = min(3.0*p - m, 0.0); + o = max(6.0*p - m*2.0 - o*3.0 + (o.x+o.y+o.z), 0.0); + return length(p - s*o/(o.x+o.y+o.z)); +#endif + +// exact distance +#if 1 + vec3 q; + if( 3.0*p.x < m ) q = p.xyz; + else if( 3.0*p.y < m ) q = p.yzx; + else if( 3.0*p.z < m ) q = p.zxy; + else return m*0.57735027; + float k = clamp(0.5*(q.z-q.y+s),0.0,s); + return length(vec3(q.x,q.y-s+k,q.z-k)); +#endif + +// bound, not exact +#if 0 + return m*0.57735027; +#endif +} + +float sdPyramid( in vec3 p, in float h ) +{ + float m2 = h*h + 0.25; + + // symmetry + p.xz = abs(p.xz); + p.xz = (p.z>p.x) ? p.zx : p.xz; + p.xz -= 0.5; + + // project into face plane (2D) + vec3 q = vec3( p.z, h*p.y - 0.5*p.x, h*p.x + 0.5*p.y); + + float s = max(-q.x,0.0); + float t = clamp( (q.y-0.5*p.z)/(m2+0.25), 0.0, 1.0 ); + + float a = m2*(q.x+s)*(q.x+s) + q.y*q.y; + float b = m2*(q.x+0.5*t)*(q.x+0.5*t) + (q.y-m2*t)*(q.y-m2*t); + + float d2 = min(q.y,-q.x*m2-q.y*0.5) > 0.0 ? 0.0 : min(a,b); + + // recover 3D and scale, and add sign + return sqrt( (d2+q.z*q.z)/m2 ) * sign(max(q.z,-p.y)); +} + +// la,lb=semi axis, h=height, ra=corner +float sdRhombus(vec3 p, float la, float lb, float h, float ra) +{ + p = abs(p); + vec2 b = vec2(la,lb); + float f = clamp( (ndot(b,b-2.0*p.xz))/dot(b,b), -1.0, 1.0 ); + vec2 q = vec2(length(p.xz-0.5*b*vec2(1.0-f,1.0+f))*sign(p.x*b.y+p.z*b.x-b.x*b.y)-ra, p.y-h); + return min(max(q.x,q.y),0.0) + length(max(q,0.0)); +} + +//------------------------------------------------------------------ + +vec2 opU( vec2 d1, vec2 d2 ) +{ + return (d1.x0.0 ) + { + tmax = min( tmax, tp1 ); + res = vec2( tp1, 1.0 ); + } + //else return res; + + // raymarch primitives + vec2 tb = iBox( ro-vec3(0.0,0.4,-0.5), rd, vec3(2.5,0.41,3.0) ); + if( tb.x0.0 && tb.x0.0 ) tmax = min( tmax, tp ); + + float res = 1.0; + float t = mint; + for( int i=ZERO; i<24; i++ ) + { + float h = map( ro + rd*t ).x; + float s = clamp(8.0*h/t,0.0,1.0); + res = min( res, s*s*(3.0-2.0*s) ); + t += clamp( h, 0.02, 0.2 ); + if( res<0.004 || t>tmax ) break; + } + return clamp( res, 0.0, 1.0 ); +} + +// http://iquilezles.org/www/articles/normalsSDF/normalsSDF.htm +vec3 calcNormal( in vec3 pos ) +{ +#if 0 + vec2 e = vec2(1.0,-1.0)*0.5773*0.0005; + return normalize( e.xyy*map( pos + e.xyy ).x + + e.yyx*map( pos + e.yyx ).x + + e.yxy*map( pos + e.yxy ).x + + e.xxx*map( pos + e.xxx ).x ); +#else + // inspired by tdhooper and klems - a way to prevent the compiler from inlining map() 4 times + vec3 n = vec3(0.0); + for( int i=ZERO; i<4; i++ ) + { + vec3 e = 0.5773*(2.0*vec3((((i+3)>>1)&1),((i>>1)&1),(i&1))-1.0); + n += e*map(pos+0.0005*e).x; + //if( n.x+n.y+n.z>100.0 ) break; + } + return normalize(n); +#endif +} + +float calcAO( in vec3 pos, in vec3 nor ) +{ + float occ = 0.0; + float sca = 1.0; + for( int i=ZERO; i<5; i++ ) + { + float h = 0.01 + 0.12*float(i)/4.0; + float d = map( pos + h*nor ).x; + occ += (h-d)*sca; + sca *= 0.95; + if( occ>0.35 ) break; + } + return clamp( 1.0 - 3.0*occ, 0.0, 1.0 ) * (0.5+0.5*nor.y); +} + +// http://iquilezles.org/www/articles/checkerfiltering/checkerfiltering.htm +float checkersGradBox( in vec2 p, in vec2 dpdx, in vec2 dpdy ) +{ + // filter kernel + vec2 w = abs(dpdx)+abs(dpdy) + 0.001; + // analytical integral (box filter) + vec2 i = 2.0*(abs(fract((p-0.5*w)*0.5)-0.5)-abs(fract((p+0.5*w)*0.5)-0.5))/w; + // xor pattern + return 0.5 - 0.5*i.x*i.y; +} + +vec3 render( in vec3 ro, in vec3 rd, in vec3 rdx, in vec3 rdy ) +{ + // background + vec3 col = vec3(0.7, 0.7, 0.9) - max(rd.y,0.0)*0.3; + + // raycast scene + vec2 res = raycast(ro,rd); + float t = res.x; + float m = res.y; + if( m>-0.5 ) + { + vec3 pos = ro + t*rd; + vec3 nor = (m<1.5) ? vec3(0.0,1.0,0.0) : calcNormal( pos ); + vec3 ref = reflect( rd, nor ); + + // material + col = 0.2 + 0.2*sin( m*2.0 + vec3(0.0,1.0,2.0) ); + float ks = 1.0; + + if( m<1.5 ) + { + // project pixel footprint into the plane + vec3 dpdx = ro.y*(rd/rd.y-rdx/rdx.y); + vec3 dpdy = ro.y*(rd/rd.y-rdy/rdy.y); + + float f = checkersGradBox( 3.0*pos.xz, 3.0*dpdx.xz, 3.0*dpdy.xz ); + col = 0.15 + f*vec3(0.05); + ks = 0.4; + } + + // lighting + float occ = calcAO( pos, nor ); + + vec3 lin = vec3(0.0); + + // sun + { + vec3 lig = normalize( vec3(-0.5, 0.4, -0.6) ); + vec3 hal = normalize( lig-rd ); + float dif = clamp( dot( nor, lig ), 0.0, 1.0 ); + //if( dif>0.0001 ) + dif *= calcSoftshadow( pos, lig, 0.02, 2.5 ); + float spe = pow( clamp( dot( nor, hal ), 0.0, 1.0 ),16.0); + spe *= dif; + spe *= 0.04+0.96*pow(clamp(1.0-dot(hal,lig),0.0,1.0),5.0); + lin += col*2.20*dif*vec3(1.30,1.00,0.70); + lin += 5.00*spe*vec3(1.30,1.00,0.70)*ks; + } + // sky + { + float dif = sqrt(clamp( 0.5+0.5*nor.y, 0.0, 1.0 )); + dif *= occ; + float spe = smoothstep( -0.2, 0.2, ref.y ); + spe *= dif; + spe *= 0.04+0.96*pow(clamp(1.0+dot(nor,rd),0.0,1.0), 5.0 ); + //if( spe>0.001 ) + spe *= calcSoftshadow( pos, ref, 0.02, 2.5 ); + lin += col*0.60*dif*vec3(0.40,0.60,1.15); + lin += 2.00*spe*vec3(0.40,0.60,1.30)*ks; + } + // back + { + float dif = clamp( dot( nor, normalize(vec3(0.5,0.0,0.6))), 0.0, 1.0 )*clamp( 1.0-pos.y,0.0,1.0); + dif *= occ; + lin += col*0.55*dif*vec3(0.25,0.25,0.25); + } + // sss + { + float dif = pow(clamp(1.0+dot(nor,rd),0.0,1.0),2.0); + dif *= occ; + lin += col*0.25*dif*vec3(1.00,1.00,1.00); + } + + col = lin; + + col = mix( col, vec3(0.7,0.7,0.9), 1.0-exp( -0.0001*t*t*t ) ); + } + + return vec3( clamp(col,0.0,1.0) ); +} + +mat3 setCamera( in vec3 ro, in vec3 ta, float cr ) +{ + vec3 cw = normalize(ta-ro); + vec3 cp = vec3(sin(cr), cos(cr),0.0); + vec3 cu = normalize( cross(cw,cp) ); + vec3 cv = ( cross(cu,cw) ); + return mat3( cu, cv, cw ); +} + +vec4 mainImage( vec2 fragCoord ) +{ + vec2 mo = iMouse.xy/iResolution.xy; + float time = 32.0 + iTime*1.5; + + // camera + vec3 ta = vec3( 0.5, -0.5, -0.6 ); + vec3 ro = ta + vec3( 4.5*cos(0.1*time + 7.0*mo.x), 1.3 + 2.0*mo.y, 4.5*sin(0.1*time + 7.0*mo.x) ); + // camera-to-world transformation + mat3 ca = setCamera( ro, ta, 0.0 ); + + vec3 tot = vec3(0.0); +#if AA>1 + for( int m=ZERO; m1 + } + tot /= float(AA*AA); +#endif + + //fragColor = vec4( tot, 1.0 ); + return vec4( tot, 1.0 ); +} + +//********************************************************* +// END Ray Marching +//********************************************************* + +void main() { + vec4 c = color; + vec4 txt = texture(tex, uv); + c = txt * c; + vec2 uv1 = uv * iResolution; + vec4 col_ray = mainImage(uv1); + + // use this to mix the chessboart texture with the ray marching + //frag_color = clamp(c*iMouse.y/512.0,0.0,1.0) * col_ray ; + + frag_color = c*0.00001 + col_ray ; +} + +#pragma sokol @end + +#pragma sokol @program rt_march vs_m fs_m diff --git a/v_windows/v/old/examples/sokol/04_multi_shader_glsl/rt_glsl_puppy.glsl b/v_windows/v/old/examples/sokol/04_multi_shader_glsl/rt_glsl_puppy.glsl new file mode 100644 index 0000000..bea60d5 --- /dev/null +++ b/v_windows/v/old/examples/sokol/04_multi_shader_glsl/rt_glsl_puppy.glsl @@ -0,0 +1,568 @@ +//------------------------------------------------------------------------------ +// Shader code for texcube-sapp sample. +// +// NOTE: This source file also uses the '#pragma sokol' form of the +// custom tags. +//------------------------------------------------------------------------------ +//#pragma sokol @ctype mat4 hmm_mat4 + +#pragma sokol @vs vs_p +uniform vs_params_p { + mat4 mvp; +}; + +in vec4 pos; +in vec4 color0; +in vec2 texcoord0; + +out vec4 color; +out vec2 uv; + +void main() { + gl_Position = mvp * pos; + color = color0; + uv = texcoord0; +} +#pragma sokol @end + +#pragma sokol @fs fs_p +uniform sampler2D tex; +uniform fs_params_p { + vec2 iResolution; + vec2 iMouse; + float iTime; + float iFrame; +}; + +in vec4 color; +in vec2 uv; +out vec4 frag_color; + +// change to 0 to 4 to increment the AntiAliasing, +// increase AA will SLOW the rendering!! +#define AA 1 + +//********************************************************* +// Ray Marching +// original code from: https://www.shadertoy.com/view/Xds3zN +//********************************************************* +// Created by inigo quilez - iq/2019 +// I share this piece (art and code) here in Shadertoy and through its Public API, only for educational purposes. +// You cannot use, share or host this piece or modifications of it as part of your own commercial or non-commercial product, website or project. +// You can share a link to it or an unmodified screenshot of it provided you attribute "by Inigo Quilez, @iquilezles and iquilezles.org". +// If you are a teacher, lecturer, educator or similar and these conditions are too restrictive for your needs, please contact me and we'll work it out. + + +// An animation test - a happy and blobby creature +// jumping and looking around. It gets off-model very +// often, but it looks good enough I think. +// +// Making-of with math/shader/art explanations (6 hours +// long): https://www.youtube.com/watch?v=Cfe5UQ-1L9Q +// +// Video capture: https://www.youtube.com/watch?v=s_UOFo2IULQ +// +// Buy a metal print here: https://www.redbubble.com/i/metal-print/Happy-Jumping-by-InigoQuilez/43594745.0JXQP + +//------------------------------------------------------------------ + + +// http://iquilezles.org/www/articles/smin/smin.htm +float smin( float a, float b, float k ) +{ + float h = max(k-abs(a-b),0.0); + return min(a, b) - h*h*0.25/k; +} + +// http://iquilezles.org/www/articles/smin/smin.htm +vec2 smin( vec2 a, vec2 b, float k ) +{ + float h = clamp( 0.5+0.5*(b.x-a.x)/k, 0.0, 1.0 ); + return mix( b, a, h ) - k*h*(1.0-h); +} + +// http://iquilezles.org/www/articles/smin/smin.htm +float smax( float a, float b, float k ) +{ + float h = max(k-abs(a-b),0.0); + return max(a, b) + h*h*0.25/k; +} + +// http://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm +float sdSphere( vec3 p, float s ) +{ + return length(p)-s; +} + +// http://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm +float sdEllipsoid( in vec3 p, in vec3 r ) // approximated +{ + float k0 = length(p/r); + float k1 = length(p/(r*r)); + return k0*(k0-1.0)/k1; +} + +vec2 sdStick(vec3 p, vec3 a, vec3 b, float r1, float r2) // approximated +{ + vec3 pa = p-a, ba = b-a; + float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 ); + return vec2( length( pa - ba*h ) - mix(r1,r2,h*h*(3.0-2.0*h)), h ); +} + +// http://iquilezles.org/www/articles/smin/smin.htm +vec4 opU( vec4 d1, vec4 d2 ) +{ + return (d1.x0.0 ) tmax = min( tmax, tp ); + #endif + + // raymarch scene + float t = tmin; + for( int i=0; i<256 && t0.0 ) tmax = min( tmax, tp ); + #endif + + float t = 0.02; + for( int i=0; i<50; i++ ) + { + float h = map( ro + rd*t, time ).x; + res = min( res, mix(1.0,16.0*h/t, hsha) ); + t += clamp( h, 0.05, 0.40 ); + if( res<0.005 || t>tmax ) break; + } + return clamp( res, 0.0, 1.0 ); +} + +// http://iquilezles.org/www/articles/normalsSDF/normalsSDF.htm +vec3 calcNormal( in vec3 pos, float time ) +{ + +#if 0 + vec2 e = vec2(1.0,-1.0)*0.5773*0.001; + return normalize( e.xyy*map( pos + e.xyy, time ).x + + e.yyx*map( pos + e.yyx, time ).x + + e.yxy*map( pos + e.yxy, time ).x + + e.xxx*map( pos + e.xxx, time ).x ); +#else + // inspired by tdhooper and klems - a way to prevent the compiler from inlining map() 4 times + vec3 n = vec3(0.0); + for( int i=ZERO; i<4; i++ ) + { + vec3 e = 0.5773*(2.0*vec3((((i+3)>>1)&1),((i>>1)&1),(i&1))-1.0); + n += e*map(pos+0.001*e,time).x; + } + return normalize(n); +#endif +} + +float calcOcclusion( in vec3 pos, in vec3 nor, float time ) +{ + float occ = 0.0; + float sca = 1.0; + for( int i=ZERO; i<5; i++ ) + { + float h = 0.01 + 0.11*float(i)/4.0; + vec3 opos = pos + h*nor; + float d = map( opos, time ).x; + occ += (h-d)*sca; + sca *= 0.95; + } + return clamp( 1.0 - 2.0*occ, 0.0, 1.0 ); +} + +vec3 render( in vec3 ro, in vec3 rd, float time ) +{ + // sky dome + vec3 col = vec3(0.5, 0.8, 0.9) - max(rd.y,0.0)*0.5; + // sky clouds + vec2 uv = 1.5*rd.xz/rd.y; + float cl = 1.0*(sin(uv.x)+sin(uv.y)); uv *= mat2(0.8,0.6,-0.6,0.8)*2.1; + cl += 0.5*(sin(uv.x)+sin(uv.y)); + col += 0.1*(-1.0+2.0*smoothstep(-0.1,0.1,cl-0.4)); + // sky horizon + col = mix( col, vec3(0.5, 0.7, .9), exp(-10.0*max(rd.y,0.0)) ); + + + // scene geometry + vec4 res = raycast(ro,rd, time); + if( res.y>-0.5 ) + { + float t = res.x; + vec3 pos = ro + t*rd; + vec3 nor = calcNormal( pos, time ); + vec3 ref = reflect( rd, nor ); + float focc = res.w; + + // material + col = vec3(0.2); + float ks = 1.0; + + if( res.y>4.5 ) // candy + { + col = vec3(0.14,0.048,0.0); + vec2 id = floor(5.0*pos.xz+0.5); + col += 0.036*cos((id.x*11.1+id.y*37.341) + vec3(0.0,1.0,2.0) ); + col = max(col,0.0); + focc = clamp(4.0*res.z,0.0,1.0); + } + else if( res.y>3.5 ) // eyeball + { + col = vec3(0.0); + } + else if( res.y>2.5 ) // iris + { + col = vec3(0.4); + } + else if( res.y>1.5 ) // body + { + col = mix(vec3(0.144,0.09,0.0036),vec3(0.36,0.1,0.04),res.z*res.z); + col = mix(col,vec3(0.14,0.09,0.06)*2.0, (1.0-res.z)*smoothstep(-0.15, 0.15, -href)); + } + else // terrain + { + // base green + col = vec3(0.05,0.09,0.02); + float f = 0.2*(-1.0+2.0*smoothstep(-0.2,0.2,sin(18.0*pos.x)+sin(18.0*pos.y)+sin(18.0*pos.z))); + col += f*vec3(0.06,0.06,0.02); + ks = 0.5 + pos.y*0.15; + + // footprints + vec2 mp = vec2(pos.x-0.5*(mod(floor(pos.z+0.5),2.0)*2.0-1.0), fract(pos.z+0.5)-0.5 ); + float mark = 1.0-smoothstep(0.1, 0.5, length(mp)); + mark *= smoothstep(0.0, 0.1, floor(time) - floor(pos.z+0.5) ); + col *= mix( vec3(1.0), vec3(0.5,0.5,0.4), mark ); + ks *= 1.0-0.5*mark; + } + + // lighting (sun, sky, bounce, back, sss) + float occ = calcOcclusion( pos, nor, time )*focc; + float fre = clamp(1.0+dot(nor,rd),0.0,1.0); + + vec3 sun_lig = normalize( vec3(0.6, 0.35, 0.5) ); + float sun_dif = clamp(dot( nor, sun_lig ), 0.0, 1.0 ); + vec3 sun_hal = normalize( sun_lig-rd ); + float sun_sha = calcSoftshadow( pos, sun_lig, time ); + float sun_spe = ks*pow(clamp(dot(nor,sun_hal),0.0,1.0),8.0)*sun_dif*(0.04+0.96*pow(clamp(1.0+dot(sun_hal,rd),0.0,1.0),5.0)); + float sky_dif = sqrt(clamp( 0.5+0.5*nor.y, 0.0, 1.0 )); + float sky_spe = ks*smoothstep( 0.0, 0.5, ref.y )*(0.04+0.96*pow(fre,4.0)); + float bou_dif = sqrt(clamp( 0.1-0.9*nor.y, 0.0, 1.0 ))*clamp(1.0-0.1*pos.y,0.0,1.0); + float bac_dif = clamp(0.1+0.9*dot( nor, normalize(vec3(-sun_lig.x,0.0,-sun_lig.z))), 0.0, 1.0 ); + float sss_dif = fre*sky_dif*(0.25+0.75*sun_dif*sun_sha); + + vec3 lin = vec3(0.0); + lin += sun_dif*vec3(8.10,6.00,4.20)*vec3(sun_sha,sun_sha*sun_sha*0.5+0.5*sun_sha,sun_sha*sun_sha); + lin += sky_dif*vec3(0.50,0.70,1.00)*occ; + lin += bou_dif*vec3(0.20,0.70,0.10)*occ; + lin += bac_dif*vec3(0.45,0.35,0.25)*occ; + lin += sss_dif*vec3(3.25,2.75,2.50)*occ; + col = col*lin; + col += sun_spe*vec3(9.90,8.10,6.30)*sun_sha; + col += sky_spe*vec3(0.20,0.30,0.65)*occ*occ; + + col = pow(col,vec3(0.8,0.9,1.0) ); + + // fog + col = mix( col, vec3(0.5,0.7,0.9), 1.0-exp( -0.0001*t*t*t ) ); + } + + return col; +} + +mat3 setCamera( in vec3 ro, in vec3 ta, float cr ) +{ + vec3 cw = normalize(ta-ro); + vec3 cp = vec3(sin(cr), cos(cr),0.0); + vec3 cu = normalize( cross(cw,cp) ); + vec3 cv = ( cross(cu,cw) ); + return mat3( cu, cv, cw ); +} + +//void mainImage( out vec4 fragColor, in vec2 fragCoord ) +vec4 mainImage( vec2 fragCoord ) +{ + vec3 tot = vec3(0.0); +#if AA>1 + for( int m=ZERO; m1 + } + tot /= float(AA*AA); +#endif + + // s-surve + tot = clamp(tot,0.0,1.0); + tot = tot*tot*(3.0-2.0*tot); + + // vignetting + vec2 q = fragCoord/iResolution.xy; + tot *= 0.5 + 0.5*pow(16.0*q.x*q.y*(1.0-q.x)*(1.0-q.y),0.25); + + // output + //fragColor = vec4( tot, 1.0 ); + return vec4( tot, 1.0 ); +} + +//********************************************************* +// END Ray Marching +//********************************************************* + +void main() { + vec4 c = color; + vec4 txt = texture(tex, uv); + c = txt * c; + vec2 uv1 = uv * iResolution; + vec4 col_ray = mainImage(uv1); + + // use this to mix the chessboart texture with the ray marching + //frag_color = clamp(c*iMouse.y/512.0,0.0,1.0) * col_ray ; + + frag_color = c*0.00001 + col_ray ; +} + +#pragma sokol @end + +#pragma sokol @program rt_puppy vs_p fs_p diff --git a/v_windows/v/old/examples/sokol/04_multi_shader_glsl/v.mod b/v_windows/v/old/examples/sokol/04_multi_shader_glsl/v.mod new file mode 100644 index 0000000..e69de29 diff --git a/v_windows/v/old/examples/sokol/05_instancing_glsl/rt_glsl.v b/v_windows/v/old/examples/sokol/05_instancing_glsl/rt_glsl.v new file mode 100644 index 0000000..f06a8e3 --- /dev/null +++ b/v_windows/v/old/examples/sokol/05_instancing_glsl/rt_glsl.v @@ -0,0 +1,521 @@ +/********************************************************************** +* +* Sokol 3d cube multishader demo +* +* Copyright (c) 2021 Dario Deledda. All rights reserved. +* Use of this source code is governed by an MIT license +* that can be found in the LICENSE file. +* +* HOW TO COMPILE SHADERS: +* - download the sokol shader convertor tool from https://github.com/floooh/sokol-tools-bin +* +* - compile the .glsl shared file with: +* linux : sokol-shdc --input rt_glsl_instancing.glsl --output rt_glsl_instancing.h --slang glsl330 +* windows: sokol-shdc.exe --input rt_glsl_instancing.glsl --output rt_glsl_instancing.h --slang glsl330 +* +* --slang parameter can be: +* - glsl330: desktop GL +* - glsl100: GLES2 / WebGL +* - glsl300es: GLES3 / WebGL2 +* - hlsl4: D3D11 +* - hlsl5: D3D11 +* - metal_macos: Metal on macOS +* - metal_ios: Metal on iOS device +* - metal_sim: Metal on iOS simulator +* - wgpu: WebGPU +* +* you can have multiple platforms at the same time passing parameters like this: --slang glsl330:hlsl5:metal_macos +* for further infos have a look at the sokol shader tool docs. +* +* TODO: +* - frame counter +**********************************************************************/ +import gg +import gg.m4 +import gx +import math + +import sokol.gfx +//import sokol.sgl + +import time + +const ( + win_width = 800 + win_height = 800 + bg_color = gx.white + num_inst = 16384 +) + +struct App { +mut: + gg &gg.Context + texture C.sg_image + init_flag bool + frame_count int + + mouse_x int = -1 + mouse_y int = -1 + mouse_down bool + + // glsl + cube_pip_glsl C.sg_pipeline + cube_bind C.sg_bindings + + pipe map[string]C.sg_pipeline + bind map[string]C.sg_bindings + + // time + ticks i64 + + // instances + inst_pos [num_inst]m4.Vec4 + + // camera + camera_x f32 + camera_z f32 +} + +/****************************************************************************** +* GLSL Include and functions +******************************************************************************/ +#flag -I @VMODROOT/. +#include "rt_glsl_instancing.h" #Please use sokol-shdc to generate the necessary rt_glsl_march.h file from rt_glsl_march.glsl (see the instructions at the top of this file) +fn C.instancing_shader_desc(gfx.Backend) &C.sg_shader_desc + +/****************************************************************************** +* Texture functions +******************************************************************************/ +fn create_texture(w int, h int, buf byteptr) C.sg_image{ + sz := w * h * 4 + mut img_desc := C.sg_image_desc{ + width: w + height: h + num_mipmaps: 0 + min_filter: .linear + mag_filter: .linear + //usage: .dynamic + wrap_u: .clamp_to_edge + wrap_v: .clamp_to_edge + label: &byte(0) + d3d11_texture: 0 + } + // comment if .dynamic is enabled + img_desc.data.subimage[0][0] = C.sg_range{ + ptr: buf + size: size_t(sz) + } + + sg_img := C.sg_make_image(&img_desc) + return sg_img +} + +fn destroy_texture(sg_img C.sg_image){ + C.sg_destroy_image(sg_img) +} + +// Use only if usage: .dynamic is enabled +fn update_text_texture(sg_img C.sg_image, w int, h int, buf byteptr){ + sz := w * h * 4 + mut tmp_sbc := C.sg_image_data{} + tmp_sbc.subimage[0][0] = C.sg_range{ + ptr: buf + size: size_t(sz) + } + C.sg_update_image(sg_img, &tmp_sbc) +} + +/****************************************************************************** +* Draw functions +****************************************************************************** + Cube vertex buffer with packed vertex formats for color and texture coords. + Note that a vertex format which must be portable across all + backends must only use the normalized integer formats + (BYTE4N, UBYTE4N, SHORT2N, SHORT4N), which can be converted + to floating point formats in the vertex shader inputs. + The reason is that D3D11 cannot convert from non-normalized + formats to floating point inputs (only to integer inputs), + and WebGL2 / GLES2 don't support integer vertex shader inputs. +*/ + +struct Vertex_t { + x f32 + y f32 + z f32 + color u32 + + //u u16 // for compatibility with D3D11 + //v u16 // for compatibility with D3D11 + u f32 + v f32 +} + +// march shader init +fn init_cube_glsl_i(mut app App) { + /* cube vertex buffer */ + //d := u16(32767) // for compatibility with D3D11, 32767 stand for 1 + d := f32(1.0) + c := u32(0xFFFFFF_FF) // color RGBA8 + vertices := [ + // Face 0 + Vertex_t{-1.0, -1.0, -1.0, c, 0, 0}, + Vertex_t{ 1.0, -1.0, -1.0, c, d, 0}, + Vertex_t{ 1.0, 1.0, -1.0, c, d, d}, + Vertex_t{-1.0, 1.0, -1.0, c, 0, d}, + // Face 1 + Vertex_t{-1.0, -1.0, 1.0, c, 0, 0}, + Vertex_t{ 1.0, -1.0, 1.0, c, d, 0}, + Vertex_t{ 1.0, 1.0, 1.0, c, d, d}, + Vertex_t{-1.0, 1.0, 1.0, c, 0, d}, + // Face 2 + Vertex_t{-1.0, -1.0, -1.0, c, 0, 0}, + Vertex_t{-1.0, 1.0, -1.0, c, d, 0}, + Vertex_t{-1.0, 1.0, 1.0, c, d, d}, + Vertex_t{-1.0, -1.0, 1.0, c, 0, d}, + // Face 3 + Vertex_t{ 1.0, -1.0, -1.0, c, 0, 0}, + Vertex_t{ 1.0, 1.0, -1.0, c, d, 0}, + Vertex_t{ 1.0, 1.0, 1.0, c, d, d}, + Vertex_t{ 1.0, -1.0, 1.0, c, 0, d}, + // Face 4 + Vertex_t{-1.0, -1.0, -1.0, c, 0, 0}, + Vertex_t{-1.0, -1.0, 1.0, c, d, 0}, + Vertex_t{ 1.0, -1.0, 1.0, c, d, d}, + Vertex_t{ 1.0, -1.0, -1.0, c, 0, d}, + // Face 5 + Vertex_t{-1.0, 1.0, -1.0, c, 0, 0}, + Vertex_t{-1.0, 1.0, 1.0, c, d, 0}, + Vertex_t{ 1.0, 1.0, 1.0, c, d, d}, + Vertex_t{ 1.0, 1.0, -1.0, c, 0, d}, + ] + + mut vert_buffer_desc := C.sg_buffer_desc{label: c'cube-vertices'} + unsafe {C.memset(&vert_buffer_desc, 0, sizeof(vert_buffer_desc))} + vert_buffer_desc.size = size_t(vertices.len * int(sizeof(Vertex_t))) + vert_buffer_desc.data = C.sg_range{ + ptr: vertices.data + size: size_t(vertices.len * int(sizeof(Vertex_t))) + } + vert_buffer_desc.@type = .vertexbuffer + vbuf := gfx.make_buffer(&vert_buffer_desc) + + /* create an instance buffer for the cube */ + mut inst_buffer_desc := C.sg_buffer_desc{label: c'instance-data'} + unsafe {C.memset(&inst_buffer_desc, 0, sizeof(inst_buffer_desc))} + + inst_buffer_desc.size = size_t(num_inst * int(sizeof(m4.Vec4))) + inst_buffer_desc.@type = .vertexbuffer + inst_buffer_desc.usage = .stream + inst_buf := gfx.make_buffer(&inst_buffer_desc) + + + /* create an index buffer for the cube */ + indices := [ + u16(0), 1, 2, 0, 2, 3, + 6, 5, 4, 7, 6, 4, + 8, 9, 10, 8, 10, 11, + 14, 13, 12, 15, 14, 12, + 16, 17, 18, 16, 18, 19, + 22, 21, 20, 23, 22, 20 + ] + + mut index_buffer_desc := C.sg_buffer_desc{label: c'cube-indices'} + unsafe {C.memset(&index_buffer_desc, 0, sizeof(index_buffer_desc))} + index_buffer_desc.size = size_t(indices.len * int(sizeof(u16))) + index_buffer_desc.data = C.sg_range{ + ptr: indices.data + size: size_t(indices.len * int(sizeof(u16))) + } + index_buffer_desc.@type = .indexbuffer + ibuf := gfx.make_buffer(&index_buffer_desc) + + /* create shader */ + shader := gfx.make_shader(C.instancing_shader_desc(C.sg_query_backend())) + + mut pipdesc := C.sg_pipeline_desc{} + unsafe {C.memset(&pipdesc, 0, sizeof(pipdesc))} + pipdesc.layout.buffers[0].stride = int(sizeof(Vertex_t)) + + // the constants [C.ATTR_vs_m_pos, C.ATTR_vs_m_color0, C.ATTR_vs_m_texcoord0] are generated by sokol-shdc + pipdesc.layout.attrs[C.ATTR_vs_i_pos ].format = .float3 // x,y,z as f32 + pipdesc.layout.attrs[C.ATTR_vs_i_pos ].buffer_index = 0 + pipdesc.layout.attrs[C.ATTR_vs_i_color0 ].format = .ubyte4n // color as u32 + pipdesc.layout.attrs[C.ATTR_vs_i_pos ].buffer_index = 0 + pipdesc.layout.attrs[C.ATTR_vs_i_texcoord0].format = .float2 // u,v as f32 + pipdesc.layout.attrs[C.ATTR_vs_i_pos ].buffer_index = 0 + + // instancing + // the constant ATTR_vs_i_inst_pos is generated by sokol-shdc + pipdesc.layout.buffers[1].stride = int(sizeof(m4.Vec4)) + pipdesc.layout.buffers[1].step_func = .per_instance // we will pass a single parameter for each instance!! + pipdesc.layout.attrs[C.ATTR_vs_i_inst_pos ].format = .float4 + pipdesc.layout.attrs[C.ATTR_vs_i_inst_pos ].buffer_index = 1 + + pipdesc.shader = shader + pipdesc.index_type = .uint16 + + pipdesc.depth = C.sg_depth_state{ + write_enabled: true + compare: gfx.CompareFunc(C.SG_COMPAREFUNC_LESS_EQUAL) + } + pipdesc.cull_mode = .back + + pipdesc.label = "glsl_shader pipeline".str + + mut bind := C.sg_bindings{} + unsafe {C.memset(&bind, 0, sizeof(bind))} + bind.vertex_buffers[0] = vbuf // vertex buffer + bind.vertex_buffers[1] = inst_buf // instance buffer + bind.index_buffer = ibuf + bind.fs_images[C.SLOT_tex] = app.texture + app.bind['inst'] = bind + app.pipe['inst'] = gfx.make_pipeline(&pipdesc) + + println("GLSL March init DONE!") +} + +fn calc_tr_matrices(w f32, h f32, rx f32, ry f32, in_scale f32) m4.Mat4{ + proj := m4.perspective(60, w/h, 0.01, 4000.0) + view := m4.look_at(m4.Vec4{e:[f32(0.0),100,6,0]!}, m4.Vec4{e:[f32(0),0,0,0]!}, m4.Vec4{e:[f32(0),1.0,0,0]!}) + view_proj := view * proj + + rxm := m4.rotate(m4.rad(rx), m4.Vec4{e:[f32(1),0,0,0]!}) + rym := m4.rotate(m4.rad(ry), m4.Vec4{e:[f32(0),1,0,0]!}) + + model := rym * rxm + scale_m := m4.scale(m4.Vec4{e:[in_scale, in_scale, in_scale, 1]!}) + + res := (scale_m * model)* view_proj + return res +} + +// triangles draw +fn draw_cube_glsl_i(mut app App){ + if app.init_flag == false { + return + } + + ws := gg.window_size_real_pixels() + //ratio := f32(ws.width) / ws.height + dw := f32(ws.width / 2) + dh := f32(ws.height / 2) + + rot := [f32(app.mouse_y), f32(app.mouse_x)] + tr_matrix := calc_tr_matrices(dw, dh, rot[0], rot[1], 2.3) + + gfx.apply_pipeline(app.pipe['inst']) + gfx.apply_bindings(app.bind['inst']) + + //*************** + // Instancing + //*************** + // passing the instancing to the vs + time_ticks := f32(time.ticks() - app.ticks) / 1000 + cube_size := 2 + sz := 128 // field size dimension + cx := 64 // x center for the cubes + cz := 64 // z center for the cubes + //frame := (app.frame_count/4) % 100 + for index in 0..num_inst { + x := f32(index % sz) + z := f32(index / sz) + // simply waves + y := f32(math.cos((x+time_ticks)/2.0)*math.sin(z/2.0))*2 + // sombrero function + //r := ((x-cx)*(x-cx)+(z-cz)*(z-cz))/(sz/2) + //y := f32(math.sin(r+time_ticks)*4.0) + spare_param := f32(index % 10) + app.inst_pos[index] = m4.Vec4{e:[f32((x - cx - app.camera_x) * cube_size),y ,f32( (z - cz - app.camera_z) * cube_size),spare_param]!} + } + range := C.sg_range{ + ptr: unsafe { &app.inst_pos } + size: size_t(num_inst * int(sizeof(m4.Vec4))) + } + gfx.update_buffer(app.bind['inst'].vertex_buffers[1], &range ) + + // Uniforms + // *** vertex shadeer uniforms *** + // passing the view matrix as uniform + // res is a 4x4 matrix of f32 thus: 4*16 byte of size + vs_uniforms_range := C.sg_range{ + ptr: unsafe { &tr_matrix } + size: size_t(4 * 16) + } + gfx.apply_uniforms(C.SG_SHADERSTAGE_VS, C.SLOT_vs_params_i, &vs_uniforms_range) + +/* + // *** fragment shader uniforms *** + time_ticks := f32(time.ticks() - app.ticks) / 1000 + mut tmp_fs_params := [ + f32(ws.width), ws.height * ratio, // x,y resolution to pass to FS + 0,0, // dont send mouse position + //app.mouse_x, // mouse x + //ws.height - app.mouse_y*2, // mouse y scaled + time_ticks, // time as f32 + app.frame_count, // frame count + 0,0 // padding bytes , see "fs_params" struct paddings in rt_glsl.h + ]! + fs_uniforms_range := C.sg_range{ + ptr: unsafe { &tmp_fs_params } + size: size_t(sizeof(tmp_fs_params)) + } + gfx.apply_uniforms(C.SG_SHADERSTAGE_FS, C.SLOT_fs_params, &fs_uniforms_range) +*/ + // 3 vertices for triangle * 2 triangles per face * 6 faces = 36 vertices to draw for num_inst times + gfx.draw(0, (3 * 2) * 6, num_inst) +} + + +fn draw_start_glsl(app App){ + if app.init_flag == false { + return + } + + ws := gg.window_size_real_pixels() + //ratio := f32(ws.width) / ws.height + //dw := f32(ws.width / 2) + //dh := f32(ws.height / 2) + + gfx.apply_viewport(0, 0, ws.width, ws.height, true) +} + +fn draw_end_glsl(app App){ + gfx.end_pass() + gfx.commit() +} + +fn frame(mut app App) { + ws := gg.window_size_real_pixels() + + // clear + mut color_action := C.sg_color_attachment_action{ + action: gfx.Action(C.SG_ACTION_CLEAR) + value: C.sg_color{ + r: 0.0 + g: 0.0 + b: 0.0 + a: 1.0 + } + } + mut pass_action := C.sg_pass_action{} + pass_action.colors[0] = color_action + gfx.begin_default_pass(&pass_action, ws.width, ws.height) + + draw_start_glsl(app) + draw_cube_glsl_i(mut app) + draw_end_glsl(app) + app.frame_count++ +} + +/****************************************************************************** +* Init / Cleanup +******************************************************************************/ +fn my_init(mut app App) { + // create chessboard texture 256*256 RGBA + w := 256 + h := 256 + sz := w * h * 4 + tmp_txt := unsafe { malloc(sz) } + mut i := 0 + for i < sz { + unsafe { + y := (i >> 0x8) >> 5 // 8 cell + x := (i & 0xFF) >> 5 // 8 cell + // upper left corner + if x == 0 && y == 0 { + tmp_txt[i + 0] = byte(0xFF) + tmp_txt[i + 1] = byte(0) + tmp_txt[i + 2] = byte(0) + tmp_txt[i + 3] = byte(0xFF) + } + // low right corner + else if x == 7 && y == 7 { + tmp_txt[i + 0] = byte(0) + tmp_txt[i + 1] = byte(0xFF) + tmp_txt[i + 2] = byte(0) + tmp_txt[i + 3] = byte(0xFF) + } else { + col := if ((x + y) & 1) == 1 { 0xFF } else { 128 } + tmp_txt[i + 0] = byte(col) // red + tmp_txt[i + 1] = byte(col) // green + tmp_txt[i + 2] = byte(col) // blue + tmp_txt[i + 3] = byte(0xFF) // alpha + } + i += 4 + } + } + unsafe { + app.texture = create_texture(w, h, tmp_txt) + free(tmp_txt) + } + + // glsl + init_cube_glsl_i(mut app) + app.init_flag = true +} + +fn cleanup(mut app App) { + gfx.shutdown() +} + +/****************************************************************************** +* events handling +******************************************************************************/ +fn my_event_manager(mut ev gg.Event, mut app App) { + if ev.typ == .mouse_down{ + app.mouse_down = true + } + if ev.typ == .mouse_up{ + app.mouse_down = false + } + if app.mouse_down == true && ev.typ == .mouse_move { + app.mouse_x = int(ev.mouse_x) + app.mouse_y = int(ev.mouse_y) + } + if ev.typ == .touches_began || ev.typ == .touches_moved { + if ev.num_touches > 0 { + touch_point := ev.touches[0] + app.mouse_x = int(touch_point.pos_x) + app.mouse_y = int(touch_point.pos_y) + } + } + + // keyboard + if ev.typ == .key_down { + step := f32(1.0) + match ev.key_code { + .w { app.camera_z += step } + .s { app.camera_z -= step } + .a { app.camera_x -= step } + .d { app.camera_x += step } + else{} + } + } +} + +/****************************************************************************** +* Main +******************************************************************************/ +[console] // is needed for easier diagnostics on windows +fn main(){ + // App init + mut app := &App{ + gg: 0 + } + + app.gg = gg.new_context({ + width: win_width + height: win_height + create_window: true + window_title: 'Instancing Cube' + user_data: app + bg_color: bg_color + frame_fn: frame + init_fn: my_init + cleanup_fn: cleanup + event_fn: my_event_manager + }) + + app.ticks = time.ticks() + app.gg.run() +} diff --git a/v_windows/v/old/examples/sokol/05_instancing_glsl/rt_glsl_instancing.glsl b/v_windows/v/old/examples/sokol/05_instancing_glsl/rt_glsl_instancing.glsl new file mode 100644 index 0000000..0e780b3 --- /dev/null +++ b/v_windows/v/old/examples/sokol/05_instancing_glsl/rt_glsl_instancing.glsl @@ -0,0 +1,64 @@ +//------------------------------------------------------------------------------ +// Shader code for texcube-sapp sample. +// +// NOTE: This source file also uses the '#pragma sokol' form of the +// custom tags. +//------------------------------------------------------------------------------ +//#pragma sokol @ctype mat4 hmm_mat4 + +#pragma sokol @vs vs_i +uniform vs_params_i { + mat4 mvp; +}; + +in vec4 pos; +in vec4 color0; +in vec2 texcoord0; + +in vec4 inst_pos; + +out vec4 color; +out vec4 color_inst; +out vec2 uv; + +const vec4 palette[10] = vec4[10]( + vec4(1,0,0,1), + vec4(0,1,0,1), + vec4(0,0,1,1), + vec4(1,1,0,1), + vec4(0,1,1,1), + vec4(1,1,1,1), + vec4(0,0,0,1), + vec4(0.2,0.2,0.2,1), + vec4(0.3,0.3,0.3,1), + vec4(0.9,0.9,0.9,1) +); + +void main() { + vec4 delta_pos = vec4(inst_pos.xyz,0); + float w = inst_pos.w; + color_inst = palette[int(w)]; + gl_Position = mvp * (pos + delta_pos); + color = color0; + uv = texcoord0/4; +} +#pragma sokol @end + +#pragma sokol @fs fs_i +uniform sampler2D tex; + +in vec4 color; +in vec4 color_inst; +in vec2 uv; +out vec4 frag_color; + +void main() { + vec4 c = color; + vec4 txt = texture(tex, uv); + c = txt * c * color_inst; + frag_color = c ; +} + +#pragma sokol @end + +#pragma sokol @program instancing vs_i fs_i diff --git a/v_windows/v/old/examples/sokol/05_instancing_glsl/v.mod b/v_windows/v/old/examples/sokol/05_instancing_glsl/v.mod new file mode 100644 index 0000000..e69de29 diff --git a/v_windows/v/old/examples/sokol/06_obj_viewer/assets/models/v.mtl b/v_windows/v/old/examples/sokol/06_obj_viewer/assets/models/v.mtl new file mode 100644 index 0000000..47d0381 --- /dev/null +++ b/v_windows/v/old/examples/sokol/06_obj_viewer/assets/models/v.mtl @@ -0,0 +1,20 @@ +# Blender MTL File: 'v-logo.blend' +# Material Count: 2 +newmtl Material.001 +Ns 96.078431 +Ka 0.2 0.2 0.2 +Kd 0.365 0.529 0.749 +Ks 0.85 0.85 0.85 +Ke 0 0 0 +Ni 1 +d 1 +illum 1 +newmtl Material.002 +Ns 96.078431 +Ka 0.1 0.1 0.1 +Kd 0.325 0.42 0.541 +Ks 0.85 0.85 0.85 +Ke 0 0 0 +Ni 1 +d 1 +illum 1 \ No newline at end of file diff --git a/v_windows/v/old/examples/sokol/06_obj_viewer/assets/models/v.obj_ b/v_windows/v/old/examples/sokol/06_obj_viewer/assets/models/v.obj_ new file mode 100644 index 0000000..d25cc0a --- /dev/null +++ b/v_windows/v/old/examples/sokol/06_obj_viewer/assets/models/v.obj_ @@ -0,0 +1,194 @@ +# Blender v2.92.0 V Logo +mtllib v.mtl +o Vleft.020_Plane.030 +v -1.379 6.295 0.625 +v -1.389 6.019 -3.177 +v -1.442 6.063 -3.177 +v -1.433 6.339 0.625 +v -4.038 6.499 0.662 +v -4.037 6.537 0.661 +v -4.047 6.261 -3.141 +v -4.048 6.222 -3.140 +v -3.900 6.637 0.658 +v -3.910 6.360 -3.144 +v -3.974 6.342 -3.142 +v -3.964 6.618 0.660 +v -4.012 6.582 0.661 +v -4.022 6.306 -3.142 +v -1.273 6.135 0.624 +v -1.283 5.859 -3.178 +v -1.321 5.933 -3.178 +v -1.311 6.209 0.624 +v 1.288 -1.652 0.018 +v 1.282 -1.628 -3.759 +v -1.510 6.387 0.625 +v -1.520 6.111 -3.177 +v -1.559 6.128 -3.176 +v -1.550 6.404 0.626 +v -1.103 -1.603 0.043 +v -1.113 -1.579 -3.759 +v -1.082 -1.597 -3.759 +v -1.072 -1.620 0.043 +v -1.127 -1.583 0.043 +v -1.137 -1.560 -3.759 +v -1.042 -1.631 0.043 +v -1.052 -1.608 -3.759 +v -1.490 6.095 -3.177 +v -1.480 6.372 0.625 +v -1.148 -1.563 0.043 +v -1.158 -1.540 -3.759 +v -1.164 -1.541 0.043 +v -1.174 -1.518 -3.759 +v -1.171 -1.528 0.043 +v -1.181 -1.504 -3.759 +v -1.347 5.974 -3.178 +v -1.337 6.250 0.624 +v -4.034 6.459 0.662 +v -4.044 6.183 -3.140 +vn 0.634 0.771 -0.057 +vn -0.999 0.034 0.000 +vn -0.278 0.958 -0.068 +vn -0.868 0.494 -0.033 +vn 0.889 0.455 -0.035 +vn 0.947 0.319 -0.012 +vn 0.399 0.914 -0.067 +vn -0.502 -0.864 -0.004 +vn 0.097 0.992 -0.072 +vn -0.630 -0.776 -0.003 +vn -0.009 -0.999 -0.006 +vn 0.564 0.822 -0.061 +vn -0.693 -0.720 -0.002 +vn -0.805 -0.592 -0.001 +vn -0.888 -0.458 -0.000 +vn 0.843 0.535 -0.041 +vn -0.993 -0.116 0.011 +vn 0.731 0.680 -0.051 +vn -0.938 -0.344 0.013 +vn 0.450 0.890 -0.065 +vn -0.604 0.794 -0.056 +vn 0.005 -0.072 0.997 +vn -0.000 0.075 -0.997 +vn -0.329 -0.944 -0.005 +usemtl Material.001 +s 1 +f 1//1 2//1 3//1 4//1 +f 5//2 6//2 7//2 8//2 +f 9//3 10//3 11//3 12//3 +f 6//4 13//4 14//4 7//4 +f 15//5 16//5 17//5 18//5 +f 19//6 20//6 16//6 15//6 +f 21//7 22//7 23//7 24//7 +f 25//8 26//8 27//8 28//8 +f 24//9 23//9 10//9 9//9 +f 29//10 30//10 26//10 25//10 +f 19//11 31//11 32//11 20//11 +f 4//12 3//12 33//12 34//12 +f 35//13 36//13 30//13 29//13 +f 37//14 38//14 36//14 35//14 +f 39//15 40//15 38//15 37//15 +f 18//16 17//16 41//16 42//16 +f 43//17 5//17 8//17 44//17 +f 42//18 41//18 2//18 1//18 +f 39//19 43//19 44//19 40//19 +f 34//20 33//20 22//20 21//20 +f 13//21 12//21 11//21 14//21 +f 15//22 18//22 42//22 1//22 4//22 34//22 21//22 24//22 9//22 12//22 13//22 6//22 5//22 43//22 39//22 37//22 35//22 29//22 25//22 28//22 31//22 19//22 +f 22//23 33//23 3//23 2//23 41//23 17//23 16//23 20//23 32//23 27//23 26//23 30//23 36//23 38//23 40//23 44//23 8//23 7//23 14//23 11//23 10//23 23//23 +f 28//24 27//24 32//24 31//24 +o Vleft.019_Plane.029 +v 1.617 6.547 -4.385 +v 1.627 6.270 -0.582 +v 1.681 6.314 -0.582 +v 1.671 6.591 -4.385 +v 4.277 6.750 -4.422 +v 4.276 6.789 -4.421 +v 4.286 6.513 -0.618 +v 4.287 6.474 -0.618 +v 1.280 -1.685 -3.803 +v 1.290 -1.656 -0.000 +v -1.044 -1.625 -0.000 +v -1.059 -1.615 -3.788 +v 4.250 6.834 -4.420 +v 4.260 6.557 -0.617 +v 1.512 6.387 -4.384 +v 1.522 6.110 -0.581 +v 1.559 6.184 -0.581 +v 1.550 6.461 -4.384 +v 1.410 -1.581 -3.803 +v 4.273 6.711 -4.422 +v 4.282 6.434 -0.619 +v 1.419 -1.552 -0.000 +v 1.749 6.639 -4.385 +v 1.758 6.362 -0.582 +v 1.798 6.380 -0.582 +v 1.788 6.656 -4.386 +v 1.342 -1.657 -3.803 +v 1.351 -1.628 -0.000 +v 1.321 -1.645 -0.000 +v 1.311 -1.674 -3.803 +v 4.148 6.612 -0.615 +v 4.139 6.888 -4.418 +v 4.213 6.593 -0.617 +v 4.203 6.870 -4.420 +v 1.366 -1.637 -3.803 +v 1.375 -1.608 -0.000 +v 1.728 6.347 -0.582 +v 1.719 6.623 -4.385 +v 1.386 -1.617 -3.803 +v 1.396 -1.588 -0.000 +v 1.402 -1.595 -3.803 +v 1.412 -1.566 -0.000 +v 1.585 6.225 -0.581 +v 1.575 6.501 -4.384 +vn -0.634 0.771 0.057 +vn 0.999 0.034 -0.000 +vn -0.021 -0.999 0.002 +vn 0.868 0.494 0.033 +vn -0.889 0.455 0.035 +vn 0.943 -0.332 -0.013 +vn -0.399 0.914 0.067 +vn 0.502 -0.864 0.005 +vn -0.097 0.992 0.072 +vn 0.278 0.958 0.068 +vn 0.630 -0.776 0.004 +vn -0.564 0.822 0.061 +vn 0.693 -0.720 0.003 +vn -0.950 0.311 0.014 +vn 0.805 -0.592 0.002 +vn 0.888 -0.458 0.001 +vn -0.843 0.535 0.041 +vn 0.993 -0.116 -0.011 +vn -0.731 0.680 0.051 +vn -0.450 0.890 0.065 +vn 0.604 0.794 0.056 +vn -0.005 -0.070 -0.997 +vn 0.000 -0.000 1.000 +vn 0.001 0.072 0.997 +vn 0.329 -0.944 0.006 +usemtl Material.002 +s 1 +f 45//25 46//25 47//25 48//25 +f 49//26 50//26 51//26 52//26 +f 53//27 54//27 55//27 56//27 +f 50//28 57//28 58//28 51//28 +f 59//29 60//29 61//29 62//29 +f 63//30 64//30 65//30 66//30 +f 67//31 68//31 69//31 70//31 +f 71//32 72//32 73//32 74//32 +f 70//33 69//33 75//33 76//33 +f 76//34 75//34 77//34 78//34 +f 79//35 80//35 72//35 71//35 +f 48//36 47//36 81//36 82//36 +f 83//37 84//37 80//37 79//37 +f 56//38 55//38 60//38 59//38 +f 85//39 86//39 84//39 83//39 +f 63//40 66//40 86//40 85//40 +f 62//41 61//41 87//41 88//41 +f 64//42 49//42 52//42 65//42 +f 88//43 87//43 46//43 45//43 +f 82//44 81//44 68//44 67//44 +f 57//45 78//45 77//45 58//45 +f 63//46 85//46 83//46 79//46 71//46 74//46 53//46 56//46 59//46 62//46 88//46 45//46 48//46 82//46 67//46 70//46 76//46 78//46 57//46 50//46 49//46 64//46 +f 66//47 84//47 86//47 +f 66//48 65//48 52//48 51//48 58//48 77//48 75//48 69//48 68//48 81//48 47//48 46//48 87//48 61//48 60//48 55//48 54//48 73//48 72//48 80//48 84//48 +f 74//49 73//49 54//49 53//49 diff --git a/v_windows/v/old/examples/sokol/06_obj_viewer/gouraud.glsl b/v_windows/v/old/examples/sokol/06_obj_viewer/gouraud.glsl new file mode 100644 index 0000000..7f36499 --- /dev/null +++ b/v_windows/v/old/examples/sokol/06_obj_viewer/gouraud.glsl @@ -0,0 +1,108 @@ +//#pragma sokol @ctype mat4 hmm_mat4 + +#pragma sokol @vs vs + +uniform vs_params { + mat4 u_MVMatrix; // A constant representing the combined model/view matrix. + mat4 u_MVPMatrix; // A constant representing the combined model/view/projection matrix. + mat4 u_NMatrix; // A constant representing the Normal Matrix +}; + +in vec4 a_Position; // Per-vertex position information we will pass in. +in vec3 a_Normal; // Per-vertex normal information we will pass in. +in vec4 a_Color; // Per-vertex color information we will pass in. +in vec2 a_Texcoord0; + +out vec3 v_Position; // This will be passed into the fragment shader. +out vec4 v_Color; // This will be passed into the fragment shader. +out vec3 v_Normal; // This will be passed into the fragment shader. +out vec3 v_Normal1; +out vec2 uv; // This will be passed into the fragment shader. + +// The entry point for our vertex shader. +void main() +{ + // Transform the vertex into eye space. + v_Position = vec3(u_MVMatrix * a_Position); + // Pass through the color. + v_Color = a_Color; + // calc eye space normal + v_Normal = vec3(u_NMatrix * vec4(a_Normal, 1.0)); + // texture coord + uv = a_Texcoord0; + + v_Normal1 = normalize(vec3(u_MVMatrix * vec4(a_Normal, 1.0))); + + // gl_Position is a special variable used to store the final position. + // Multiply the vertex by the matrix to get the final point in normalized screen coordinates. + gl_Position = u_MVPMatrix * a_Position; +} + +#pragma sokol @end + +#pragma sokol @fs fs +//precision mediump float; // Set the default precision to medium. We don't need as high of a precision in the fragment shader +uniform sampler2D tex; +uniform fs_params { + vec4 u_LightPos; // The position of the light in eye space. + vec4 ambientColor; + vec4 diffuseColor; + vec4 specularColor; + +}; +in vec3 v_Position; // Interpolated position for this fragment. +in vec4 v_Color; // This is the color from the vertex shader interpolated across the triangle per fragment. +in vec3 v_Normal; // Interpolated normal for this fragment. +in vec3 v_Normal1; +in vec2 uv; +out vec4 frag_color; + +vec3 lightDirection = -u_LightPos.xyz;// vec3(0.0, -0.5, 0.5); +//const vec4 ambientColor = vec4(0.094, 0.0, 0.0, 1.0); +//const vec4 diffuseColor = vec4(0.5, 0.0, 0.0, 1.0); +//const vec4 specularColor = vec4(1.0, 1.0, 1.0, 1.0); +//const float shininess = 10.0; +const vec4 lightColor = vec4(1.0, 1.0, 1.0, 1.0); + +vec3 phongBRDF(vec3 lightDir, vec3 viewDir, vec3 normal, vec3 phongDiffuseCol, vec3 phongSpecularCol, float phongShininess) { + vec3 color = phongDiffuseCol; + vec3 reflectDir = reflect(-lightDir, normal); + float specDot = max(dot(reflectDir, viewDir), 0.0); + color += pow(specDot, phongShininess) * phongSpecularCol; + return color; +} + +vec4 getPhong(in vec4 diffuseColor) { + vec3 lightDir = normalize(-lightDirection); + vec3 viewDir = normalize(-v_Position); + vec3 n = normalize(v_Normal); + + vec3 luminance = ambientColor.rgb * 0.5; + + float illuminance = dot(lightDir, n); + if(illuminance > 0.0) { + // we save specular shiness in specularColor.a + vec3 brdf = phongBRDF(lightDir, viewDir, n, diffuseColor.rgb, specularColor.rgb, specularColor.a * 1000); + luminance += brdf * illuminance * lightColor.rgb; + } + + vec4 outColor = vec4(luminance,1.0); + return outColor; +} + +// The entry point for our fragment shader. +void main() +{ + vec4 txt = texture(tex, uv); + + // Directional light + float directional = dot(normalize(v_Normal1), normalize(vec3(0,0.5,1))) ; + directional = directional * 0.15; + + // Multiply the color by the diffuse illumination level to get final output color. + frag_color = vec4(clamp(directional + txt.rgb * getPhong(diffuseColor).rgb,0,1), txt.a * diffuseColor.a); + +} +#pragma sokol @end + +#pragma sokol @program gouraud vs fs \ No newline at end of file diff --git a/v_windows/v/old/examples/sokol/06_obj_viewer/modules/obj/obj.v b/v_windows/v/old/examples/sokol/06_obj_viewer/modules/obj/obj.v new file mode 100644 index 0000000..1d239a0 --- /dev/null +++ b/v_windows/v/old/examples/sokol/06_obj_viewer/modules/obj/obj.v @@ -0,0 +1,595 @@ +module obj + +/********************************************************************** +* +* .obj loader +* +* Copyright (c) 2021 Dario Deledda. All rights reserved. +* Use of this source code is governed by an MIT license +* that can be found in the LICENSE file. +* +* TODO: +**********************************************************************/ +import gg.m4 +import strconv + +enum F_state { + start + first + ints + decimals + exp_start + exp_sign + exp_int +} + +// read a int from a string +fn get_int(s string, start_index int) (int, int) { + mut i := start_index + mut res := 0 + mut sgn := 1 + + mut state := F_state.start + for true { + if i >= s.len { + break + } + c := s[i] + if state == .start { + match c { + `+` { + i++ + state = .ints + continue + } + `-` { + sgn = -1 + i++ + state = .ints + continue + } + `0`...`9` { + state = .ints + } + ` `, `\t` { + i++ + continue + } + else { // no number found + break + } + } + } + + if state == .ints { + match c { + `0`...`9` { + // println("$res => ${(int(c) - 48)}") + res = res * 10 + (int(c) - 48) + i++ + continue + } + else { + break + } + } + } + } + // println("---") + return res * sgn, i +} + +// reas a float number from a string +fn get_float(s string, start_index int) (f64, int) { + mut i1 := start_index //+ 1 + for i1 < s.len && s[i1] in [` `, `\t`] { + i1++ + } + mut i := i1 + for i < s.len { + if s[i] in [` `, `\t`] { + break + } + i++ + } + // println(" get_float: ($start_index,$i) [${s[start_index..i]}]") + // f_res := strconv.atof_quick(s[start_index..i]) + f_res := strconv.atof_quick(s[i1..i]) + return f_res, i +} + +// read 3 f32 in sequence from a string +fn parse_3f(row string, start_index int) m4.Vec4 { + // println(row) + mut i := start_index //+ 1 + mut f1 := f64(0) + mut f2 := f64(0) + f0, mut p := get_float(row, i) + // print("Here f0: $f0 $p ") + f1, p = get_float(row, p + 1) + // print("Here f1: $f1 $p ") + f2, p = get_float(row, p + 1) + // print("Here f2: $f2 $p ") + return m4.Vec4{ + e: [f32(f0), f32(f1), f32(f2), 1]! + } +} + +// reas a sequence of f32 from a string +fn (mut m ObjPart) parse_floats(row string, start_index int) m4.Vec4 { + mut i := start_index //+ 1 + mut res_f := f64(0) + mut res := m4.Vec4{ + e: [f32(0), 0, 0, 1]! + } + mut c := 0 + for true { + res_f, i = get_float(row, i) + unsafe { + res.e[c] = f32(res_f) + } + c++ + i++ + if i >= row.len { + break + } + } + return res +} + +// read and manage all the faes from an .obj file data +fn (mut p Part) parse_faces(row string, start_index int, obj ObjPart) { + mut i := start_index + 1 + mut res := [][3]int{} + mut v := 0 + mut t := 0 + mut n := 0 + // println("row: ${row[i..]}") + for true { + t = 0 + n = 0 + if i >= row.len { + break + } + mut c := row[i] + if (c > `9` || c < `0`) && c != `-` { + i++ + continue + } + v, i = get_int(row, i) + if i < row.len && row[i] == `/` { + if row[i + 1] != `/` { + t, i = get_int(row, i + 1) + if i < row.len && row[i] == `/` { + n, i = get_int(row, i + 1) + } + } else { + i++ + n, i = get_int(row, i + 1) + } + } + // manage negative indexes + // NOTE: not well suporeted now + if v < 0 { + // println("${obj.v.len} ${obj.v.len-c}") + v = obj.v.len - v + 1 + // exit(0) + } + if n < 0 { + n = obj.vn.len - n + 1 + } + if t < 0 { + t = obj.vt.len - t + 1 + } + res << [v - 1, n - 1, t - 1]! + } + // println("ok res: ${res}") + // println(p.faces.len) + p.faces << res +} + +// parse the obj file, if single_material is true it use only one default material +pub fn (mut obj_part ObjPart) parse_obj_buffer(rows []string, single_material bool) { + mut mat_count := 0 + mut row_count := 0 + default_part := Part{ + name: 'default part' + } + obj_part.part << default_part + // println("OBJ file has ${rows.len} rows") + for c, row in rows { + // println("$c $row") + mut i := 0 + row_count++ + for true { + if i >= row.len { + break + } + match row[i] { + `s` { + break + } + `m` { + if row[i..i + 6] == 'mtllib' { + obj_part.material_file = row[i + 7..].trim_space() + obj_part.load_materials() + } + break + } + `o`, `g` { + mut part := Part{} + part.name = row[i + 1..].trim_space() + obj_part.part << part + mat_count = 0 + break + } + `u` { + if single_material == false && row[i..i + 6] == 'usemtl' { + material := row[i + 7..].trim_space() + // println("material: $material") + // manage multiple materials in an part + if obj_part.part[obj_part.part.len - 1].material.len > 0 { + mat_count++ + mut part := Part{} + if mat_count > 1 { + li := obj_part.part[obj_part.part.len - 1].name.last_index('_m') or { + obj_part.part[obj_part.part.len - 1].name.len - 1 + } + part.name = obj_part.part[obj_part.part.len - 1].name[..li] + + '_m${mat_count:02}' + } else { + part.name = obj_part.part[obj_part.part.len - 1].name + '_m01' + } + obj_part.part << part + } + obj_part.part[obj_part.part.len - 1].material = material + } + break + } + `v` { + i++ + match row[i] { + // normals + `n` { + obj_part.vn << parse_3f(row, i + 2) + // println("Vertex line: $c") + break + } + // parameteres uvw + `p` { + obj_part.vp << parse_3f(row, i + 2) + // println("Vertex line: ${obj_part.vp.len}") + break + } + // texture uvw + `t` { + obj_part.vt << obj_part.parse_floats(row, i + 2) + // println("Vertex line: $c") + break + } + else { + obj_part.v << parse_3f(row, i + 1) + // println("$row => ${obj_part.v[obj_part.v.len-1]}") + break + } + } + } + `f` { + // println("$c $row") + obj_part.part[obj_part.part.len - 1].parse_faces(row, i, obj_part) + // println(obj_part.part[obj_part.part.len - 1].faces.len) + // println("Faces line: $c") + break + } + // end of the line, comments + `\n`, `#` { + break + } + else {} + } + i++ + } + // if c == 2 { break } + if c % 100000 == 0 && c > 0 { + println('$c rows parsed') + } + } + println('$row_count .obj Rows parsed') + // remove default part if empty + if obj_part.part.len > 1 && obj_part.part[0].faces.len == 0 { + obj_part.part = obj_part.part[1..] + } +} + +// load the materials if found the .mtl file +fn (mut obj_part ObjPart) load_materials() { + rows := read_lines_from_file(obj_part.material_file) + println('Material file [$obj_part.material_file] $rows.len Rows.') + for row in rows { + // println("$row") + mut i := 0 + for true { + if i >= row.len { + break + } + match row[i] { + `n` { + if row[i..i + 6] == 'newmtl' { + name := row[i + 6..].trim_space() + mut mat := Material{ + name: name + } + obj_part.mat << mat + break + } + } + `K` { + if row[i + 1] !in [`a`, `d`, `e`, `s`] { + break + } + k_name := row[i..i + 2] + i += 3 + value := parse_3f(row, i) + obj_part.mat[obj_part.mat.len - 1].ks[k_name] = value + break + } + `N` { + n_name := row[i..i + 2] + i += 3 + value, _ := get_float(row, i) + obj_part.mat[obj_part.mat.len - 1].ns[n_name] = f32(value) + break + } + `m` { + if row[i..i + 4] == 'map_' { + name := row[i..i + 6] + if (i + 7) < row.len { + file_name := row[i + 7..].trim_space() + obj_part.mat[obj_part.mat.len - 1].maps[name] = file_name + } + break + } + } + // trasparency + `d` { + if row[i + 1] == ` ` { + value, _ := get_float(row, i + 2) + obj_part.mat[obj_part.mat.len - 1].ns['Tr'] = f32(value) + } + } + `T` { + if row[i + 1] == `r` { + value, _ := get_float(row, i + 3) + obj_part.mat[obj_part.mat.len - 1].ns['Tr'] = f32(1.0 - value) + } + } + // end of the line, comments + `\n`, `#` { + break + } + ` `, `\t` { + i++ + continue + } + else { + break + } + } + i++ + } + } + + // create map material name => material index + for i, m in obj_part.mat { + if m.name !in obj_part.mat_map { + obj_part.mat_map[m.name] = i + } + } + + println('Material Loading Done!') +} + +//============================================================================== +// Sokol data +//============================================================================== + +// vertex data struct +pub struct Vertex_pnct { +pub mut: + x f32 // poistion + y f32 + z f32 + nx f32 // normal + ny f32 + nz f32 + color u32 = 0xFFFFFFFF // color + u f32 // uv + v f32 + // u u16 // for compatibility with D3D11 + // v u16 // for compatibility with D3D11 +} + +// struct used to pass the data to the sokol calls +pub struct Skl_buffer { +pub mut: + vbuf []Vertex_pnct + ibuf []u32 + n_vertex u32 +} + +// transforms data from .obj format to buffer ready to be used in the render +pub fn (mut obj_part ObjPart) get_buffer(in_part_list []int) Skl_buffer { + // in_part := 0 + mut v_count_index := 0 + mut out_buf := Skl_buffer{} + + mut cache := map[string]int{} + mut cache_hit := 0 + + // has_normals := obj_part.vn.len > 0 + // has_uvs := obj_part.vt.len > 0 + + for in_part in in_part_list { + part := obj_part.part[in_part] + for fc, face in part.faces { + // println("$fc $face") + // default 3 faces + mut v_seq := [0, 1, 2] + if face.len == 4 { + v_seq = [0, 1, 2, 0, 2, 3] + } + + // if big faces => use the fan of triangles as solution + // Note: this trick doesn't work with concave faces + if face.len > 4 { + v_seq = [] + mut i := 1 + for i < (face.len - 1) { + v_seq << 0 + v_seq << i + v_seq << (i + 1) + i++ + } + // println("BIG FACES! ${fc} ${face.len} v_seq:${v_seq.len}") + } + + // no vertex index, generate normals + if face[0][1] == -1 && face.len >= 3 { + mut v_count := 0 + v0 := face[v_count + 0][0] + v1 := face[v_count + 1][0] + v2 := face[v_count + 2][0] + + vec0 := obj_part.v[v2] - obj_part.v[v1] + vec1 := obj_part.v[v0] - obj_part.v[v1] + tmp_normal := vec0 % vec1 + + for v_count < face.len { + obj_part.vn << tmp_normal + obj_part.part[in_part].faces[fc][v_count][1] = obj_part.vn.len - 1 + v_count++ + } + } + + for vertex_index in v_seq { + // position + if vertex_index >= face.len { + continue + } + v_index := face[vertex_index][0] // vertex index + n_index := face[vertex_index][1] // normal index + t_index := face[vertex_index][2] // uv texture index + key := '${v_index}_${n_index}_$t_index' + if key !in cache { + cache[key] = v_count_index + mut pnct := Vertex_pnct{ + x: obj_part.v[v_index].e[0] + y: obj_part.v[v_index].e[1] + z: obj_part.v[v_index].e[2] + } + // normal + if n_index >= 0 { + pnct.nx = obj_part.vn[n_index].e[0] + pnct.ny = obj_part.vn[n_index].e[1] + pnct.nz = obj_part.vn[n_index].e[2] + } + // texture uv + if t_index >= 0 { + pnct.u = obj_part.vt[t_index].e[0] + pnct.v = obj_part.vt[t_index].e[1] + } + + out_buf.vbuf << pnct + out_buf.ibuf << u32(v_count_index) + v_count_index++ + } else { + // println("Cache used! $key") + out_buf.ibuf << u32(cache[key]) + cache_hit++ + } + } + } + } + + /* + println("------------") + for c1, x1 in out_buf.vbuf[..10] { + println("$c1 $x1") + } + println(out_buf.ibuf[..10]) + */ + // println("vbuf size: ${out_buf.vbuf.len} ibuf size: ${out_buf.ibuf.len} Cache hit: $cache_hit") + out_buf.n_vertex = u32(out_buf.ibuf.len) + return out_buf +} + +//============================================================================== +// Utility +//============================================================================== +// print on the console the summary of the .obj model loaded +pub fn (obj_part ObjPart) summary() { + println('---- Stats ----') + println('vertices: $obj_part.v.len') + println('normals : $obj_part.vn.len') + println('uv : $obj_part.vt.len') + println('parts : $obj_part.part.len') + // Parts + println('---- Parts ----') + for c, x in obj_part.part { + println('${c:3} [${x.name:-16}] mat:[${x.material:-10}] ${x.faces.len:7} faces') + } + // Materials + println('---- Materials ----') + println('Material dict: $obj_part.mat_map.keys()') + for c, mat in obj_part.mat { + println('${c:3} [${mat.name:-16}]') + for k, v in mat.ks { + print('$k = $v') + } + for k, v in mat.ns { + println('$k = $v') + } + for k, v in mat.maps { + println('$k = $v') + } + } +} + +// debug test function, do not remove. +pub fn tst() { + /* + //fname := "capsule.obj" + //fname := "Forklift.obj" + fname := "cube.obj" + //fname := "Orange Robot 3D ObjPart.obj" + + mut obj := ObjPart{} + buf := os.read_lines(fname) or { panic(err.msg) } + obj.parse_obj_buffer(buf) + obj.summary() + */ + /* + a :="f 7048 7070 7071 7072 7073 7074 7075 7076 7077 7078 7079 7080 7003" + mut f1 := 0 + mut f2 := 0 + f0,mut p := get_int(a,1) + f1, p = get_int(a,p) + f2, p = get_int(a,p) + println("res: ${f0} ${f1} ${f2}") + */ + /* + a :="v -0 0.107769 -0.755914" + println("${parse_3f(a,1)}") + */ + /* + ort := m4.ortho(0,300,0,200,0,0) + println(ort) + a := m4.vec3(0,0,0) + println("a: $a") + res := m4.mul_vec(ort, a) + println("res:\n${res}") + */ + s := 'K 1 1 1' + r := strconv.atof_quick(s[1..s.len - 1]) + println(r) +} diff --git a/v_windows/v/old/examples/sokol/06_obj_viewer/modules/obj/rend.v b/v_windows/v/old/examples/sokol/06_obj_viewer/modules/obj/rend.v new file mode 100644 index 0000000..d4ea3bc --- /dev/null +++ b/v_windows/v/old/examples/sokol/06_obj_viewer/modules/obj/rend.v @@ -0,0 +1,297 @@ +/********************************************************************** +* +* .obj loader +* +* Copyright (c) 2021 Dario Deledda. All rights reserved. +* Use of this source code is governed by an MIT license +* that can be found in the LICENSE file. +* +* TODO: +**********************************************************************/ +module obj + +import sokol.gfx +import gg.m4 +import math +import stbi + +/****************************************************************************** +* Texture functions +******************************************************************************/ +pub fn create_texture(w int, h int, buf &byte) C.sg_image { + sz := w * h * 4 + mut img_desc := C.sg_image_desc{ + width: w + height: h + num_mipmaps: 0 + min_filter: .linear + mag_filter: .linear + // usage: .dynamic + wrap_u: .clamp_to_edge + wrap_v: .clamp_to_edge + label: &byte(0) + d3d11_texture: 0 + } + // comment if .dynamic is enabled + img_desc.data.subimage[0][0] = C.sg_range{ + ptr: buf + size: size_t(sz) + } + + sg_img := C.sg_make_image(&img_desc) + return sg_img +} + +pub fn destroy_texture(sg_img C.sg_image) { + C.sg_destroy_image(sg_img) +} + +pub fn load_texture(file_name string) C.sg_image { + buffer := read_bytes_from_file(file_name) + stbi.set_flip_vertically_on_load(true) + img := stbi.load_from_memory(buffer.data, buffer.len) or { + eprintln('Texure file: [$file_name] ERROR!') + exit(0) + } + res := create_texture(int(img.width), int(img.height), img.data) + img.free() + return res +} + +/****************************************************************************** +* Pipeline +******************************************************************************/ +pub fn (mut obj_part ObjPart) create_pipeline(in_part []int, shader C.sg_shader, texture C.sg_image) Render_data { + mut res := Render_data{} + obj_buf := obj_part.get_buffer(in_part) + res.n_vert = obj_buf.n_vertex + res.material = obj_part.part[in_part[0]].material + + // vertex buffer + mut vert_buffer_desc := C.sg_buffer_desc{ + label: 0 + } + unsafe { C.memset(&vert_buffer_desc, 0, sizeof(vert_buffer_desc)) } + + vert_buffer_desc.size = size_t(obj_buf.vbuf.len * int(sizeof(Vertex_pnct))) + vert_buffer_desc.data = C.sg_range{ + ptr: obj_buf.vbuf.data + size: size_t(obj_buf.vbuf.len * int(sizeof(Vertex_pnct))) + } + + vert_buffer_desc.@type = .vertexbuffer + vert_buffer_desc.label = 'vertbuf_part_${in_part:03}'.str + vbuf := gfx.make_buffer(&vert_buffer_desc) + + // index buffer + mut index_buffer_desc := C.sg_buffer_desc{ + label: 0 + } + unsafe { C.memset(&index_buffer_desc, 0, sizeof(index_buffer_desc)) } + + index_buffer_desc.size = size_t(obj_buf.ibuf.len * int(sizeof(u32))) + index_buffer_desc.data = C.sg_range{ + ptr: obj_buf.ibuf.data + size: size_t(obj_buf.ibuf.len * int(sizeof(u32))) + } + + index_buffer_desc.@type = .indexbuffer + index_buffer_desc.label = 'indbuf_part_${in_part:03}'.str + ibuf := gfx.make_buffer(&index_buffer_desc) + + mut pipdesc := C.sg_pipeline_desc{} + unsafe { C.memset(&pipdesc, 0, sizeof(pipdesc)) } + pipdesc.layout.buffers[0].stride = int(sizeof(Vertex_pnct)) + + // the constants [C.ATTR_vs_a_Position, C.ATTR_vs_a_Color, C.ATTR_vs_a_Texcoord0] are generated by sokol-shdc + pipdesc.layout.attrs[C.ATTR_vs_a_Position].format = .float3 // x,y,z as f32 + pipdesc.layout.attrs[C.ATTR_vs_a_Normal].format = .float3 // x,y,z as f32 + pipdesc.layout.attrs[C.ATTR_vs_a_Color].format = .ubyte4n // color as u32 + pipdesc.layout.attrs[C.ATTR_vs_a_Texcoord0].format = .float2 // u,v as f32 + // pipdesc.layout.attrs[C.ATTR_vs_a_Texcoord0].format = .short2n // u,v as u16 + pipdesc.index_type = .uint32 + + color_state := C.sg_color_state{ + blend: C.sg_blend_state{ + enabled: true + src_factor_rgb: gfx.BlendFactor(C.SG_BLENDFACTOR_SRC_ALPHA) + dst_factor_rgb: gfx.BlendFactor(C.SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA) + } + } + pipdesc.colors[0] = color_state + + pipdesc.depth = C.sg_depth_state{ + write_enabled: true + compare: gfx.CompareFunc(C.SG_COMPAREFUNC_LESS_EQUAL) + } + pipdesc.cull_mode = .front + + pipdesc.label = 'pip_part_${in_part:03}'.str + + // shader + pipdesc.shader = shader + + res.bind.vertex_buffers[0] = vbuf + res.bind.index_buffer = ibuf + res.bind.fs_images[C.SLOT_tex] = texture + res.pipeline = gfx.make_pipeline(&pipdesc) + // println('Buffers part [$in_part] init done!') + + return res +} + +/****************************************************************************** +* Render functions +******************************************************************************/ +// agregate all the part by materials +pub fn (mut obj_part ObjPart) init_render_data(texture C.sg_image) { + // create shader + // One shader for all the model + shader := gfx.make_shader(C.gouraud_shader_desc(gfx.query_backend())) + + mut part_dict := map[string][]int{} + for i, p in obj_part.part { + if p.faces.len > 0 { + part_dict[p.material] << i + } + } + obj_part.rend_data.clear() + // println("Material dict: ${obj_part.mat_map.keys()}") + + for k, v in part_dict { + // println("$k => Parts $v") + + mut txt := texture + + if k in obj_part.mat_map { + mat_map := obj_part.mat[obj_part.mat_map[k]] + if 'map_Kd' in mat_map.maps { + file_name := mat_map.maps['map_Kd'] + if file_name in obj_part.texture { + txt = obj_part.texture[file_name] + // println("Texture [${file_name}] => from CACHE") + } else { + txt = load_texture(file_name) + obj_part.texture[file_name] = txt + // println("Texture [${file_name}] => LOADED") + } + } + } + // key := obj_part.texture.keys()[0] + // obj_part.rend_data << obj_part.create_pipeline(v, shader, obj_part.texture[key]) + obj_part.rend_data << obj_part.create_pipeline(v, shader, txt) + } + // println("Texture array len: ${obj_part.texture.len}") + // println("Calc bounding box.") + obj_part.calc_bbox() + println('init_render_data DONE!') +} + +pub fn (obj_part ObjPart) bind_and_draw(rend_data_index int, in_data Shader_data) u32 { + // apply the pipline and bindings + mut part_render_data := obj_part.rend_data[rend_data_index] + + // pass light position + mut tmp_fs_params := Tmp_fs_param{} + tmp_fs_params.ligth = in_data.fs_data.ligth + + if part_render_data.material in obj_part.mat_map { + mat_index := obj_part.mat_map[part_render_data.material] + mat := obj_part.mat[mat_index] + + // ambient + tmp_fs_params.ka = in_data.fs_data.ka + if 'Ka' in mat.ks { + tmp_fs_params.ka = mat.ks['Ka'] + } + + // specular + tmp_fs_params.ks = in_data.fs_data.ks + if 'Ks' in mat.ks { + tmp_fs_params.ks = mat.ks['Ks'] + } + + // specular exponent Ns + if 'Ns' in mat.ns { + tmp_fs_params.ks.e[3] = mat.ns['Ns'] / 1000.0 + } else { + // defautl value is 10 + tmp_fs_params.ks.e[3] = f32(10) / 1000.0 + } + + // diffuse + tmp_fs_params.kd = in_data.fs_data.kd + if 'Kd' in mat.ks { + tmp_fs_params.kd = mat.ks['Kd'] + } + + // alpha/transparency + if 'Tr' in mat.ns { + tmp_fs_params.kd.e[3] = mat.ns['Tr'] + } + } + + gfx.apply_pipeline(part_render_data.pipeline) + gfx.apply_bindings(part_render_data.bind) + + vs_uniforms_range := C.sg_range{ + ptr: in_data.vs_data + size: size_t(in_data.vs_len) + } + fs_uniforms_range := C.sg_range{ + ptr: unsafe { &tmp_fs_params } + size: size_t(in_data.fs_len) + } + + gfx.apply_uniforms(C.SG_SHADERSTAGE_VS, C.SLOT_vs_params, &vs_uniforms_range) + gfx.apply_uniforms(C.SG_SHADERSTAGE_FS, C.SLOT_fs_params, &fs_uniforms_range) + gfx.draw(0, int(part_render_data.n_vert), 1) + return part_render_data.n_vert +} + +pub fn (obj_part ObjPart) bind_and_draw_all(in_data Shader_data) u32 { + mut n_vert := u32(0) + // println("Parts: ${obj_part.rend_data.len}") + for i, _ in obj_part.rend_data { + n_vert += obj_part.bind_and_draw(i, in_data) + } + return n_vert +} + +pub fn (mut obj_part ObjPart) calc_bbox() { + obj_part.max = m4.Vec4{ + e: [f32(-math.max_f32), -math.max_f32, -math.max_f32, 0]! + } + obj_part.min = m4.Vec4{ + e: [f32(math.max_f32), math.max_f32, math.max_f32, 0]! + } + for v in obj_part.v { + if v.e[0] > obj_part.max.e[0] { + obj_part.max.e[0] = v.e[0] + } + if v.e[1] > obj_part.max.e[1] { + obj_part.max.e[1] = v.e[1] + } + if v.e[2] > obj_part.max.e[2] { + obj_part.max.e[2] = v.e[2] + } + + if v.e[0] < obj_part.min.e[0] { + obj_part.min.e[0] = v.e[0] + } + if v.e[1] < obj_part.min.e[1] { + obj_part.min.e[1] = v.e[1] + } + if v.e[2] < obj_part.min.e[2] { + obj_part.min.e[2] = v.e[2] + } + } + val1 := obj_part.max.mod3() + val2 := obj_part.min.mod3() + if val1 > val2 { + obj_part.radius = f32(val1) + } else { + obj_part.radius = f32(val2) + } + // println("BBox: ${obj_part.min} <=> ${obj_part.max}\nRadius: ${obj_part.radius}") +} diff --git a/v_windows/v/old/examples/sokol/06_obj_viewer/modules/obj/struct.v b/v_windows/v/old/examples/sokol/06_obj_viewer/modules/obj/struct.v new file mode 100644 index 0000000..55a528b --- /dev/null +++ b/v_windows/v/old/examples/sokol/06_obj_viewer/modules/obj/struct.v @@ -0,0 +1,104 @@ +/********************************************************************** +* +* .obj loader +* +* Copyright (c) 2021 Dario Deledda. All rights reserved. +* Use of this source code is governed by an MIT license +* that can be found in the LICENSE file. +* +* TODO: +**********************************************************************/ +module obj + +import gg.m4 + +// part struct mantain the fae indexes list +pub struct Part { +pub mut: + faces [][][3]int // v n t index order, if -1 not available + name string + material string +} + +// materias struct, all Ks and Ns are stored as maps of string +pub struct Material { +pub mut: + name string + ks map[string]m4.Vec4 + ns map[string]f32 + maps map[string]string +} + +// render data used for the rendering +pub struct Render_data { +pub mut: + pipeline C.sg_pipeline + bind C.sg_bindings + n_vert u32 + material string +} + +// base object parts struct +pub struct ObjPart { +pub mut: + v []m4.Vec4 // position + vn []m4.Vec4 // normals + vp []m4.Vec4 // vertex params + vt []m4.Vec4 // textures + + name string + part []Part // parts of the ObjPart + mat []Material // list of the materials of the ObjPart + mat_map map[string]int // maping material name to its material index + texture map[string]C.sg_image // GPU loaded texture map + material_file string // .mtl file name for the .obj + + rend_data []Render_data // render data used for the rendering + + t_m m4.Mat4 = m4.unit_m4() // transform matrix for this ObjPart + // child []ObjPart // childs + // stats + min m4.Vec4 // min 3d position in the ObjPart + max m4.Vec4 // max 3d position in the ObjPart + radius f32 // bounding circle radius of the ObjPart +} + +// used in to pass the matrices to the shader +pub struct Mats { +pub mut: + mv m4.Mat4 + mvp m4.Mat4 + nm m4.Mat4 +} + +// data passed to the vertex shader +pub struct Tmp_vs_param { +pub mut: + mv m4.Mat4 + mvp m4.Mat4 + nm m4.Mat4 +} + +// data passed to the pixel shader +pub struct Tmp_fs_param { +pub mut: + ligth m4.Vec4 + ka m4.Vec4 = m4.Vec4{ + e: [f32(0.1), 0.0, 0.0, 1.0]! + } + kd m4.Vec4 = m4.Vec4{ + e: [f32(0.5), 0.5, 0.5, 1.0]! + } + ks m4.Vec4 = m4.Vec4{ + e: [f32(1.0), 1.0, 1.0, 1.0]! + } +} + +// shader data for the rendering +pub struct Shader_data { +pub mut: + vs_data &Tmp_vs_param + vs_len int + fs_data &Tmp_fs_param + fs_len int +} diff --git a/v_windows/v/old/examples/sokol/06_obj_viewer/modules/obj/util.v b/v_windows/v/old/examples/sokol/06_obj_viewer/modules/obj/util.v new file mode 100644 index 0000000..a1e596a --- /dev/null +++ b/v_windows/v/old/examples/sokol/06_obj_viewer/modules/obj/util.v @@ -0,0 +1,44 @@ +module obj + +import os + +// read a file as single lines +pub fn read_lines_from_file(file_path string) []string { + mut path := '' + mut rows := []string{} + $if android { + path = 'models/' + file_path + bts := os.read_apk_asset(path) or { + eprintln('File [$path] NOT FOUND!') + return rows + } + rows = bts.bytestr().split_into_lines() + } $else { + path = os.resource_abs_path('assets/models/' + file_path) + rows = os.read_lines(path) or { + eprintln('File [$path] NOT FOUND! file_path: $file_path') + return rows + } + } + return rows +} + +// read a file as []byte +pub fn read_bytes_from_file(file_path string) []byte { + mut path := '' + mut buffer := []byte{} + $if android { + path = 'models/' + file_path + buffer = os.read_apk_asset(path) or { + eprintln('Texure file: [$path] NOT FOUND!') + exit(0) + } + } $else { + path = os.resource_abs_path('assets/models/' + file_path) + buffer = os.read_bytes(path) or { + eprintln('Texure file: [$path] NOT FOUND!') + exit(0) + } + } + return buffer +} diff --git a/v_windows/v/old/examples/sokol/06_obj_viewer/show_obj.v b/v_windows/v/old/examples/sokol/06_obj_viewer/show_obj.v new file mode 100644 index 0000000..e2e0be3 --- /dev/null +++ b/v_windows/v/old/examples/sokol/06_obj_viewer/show_obj.v @@ -0,0 +1,338 @@ +/********************************************************************** +* +* .obj viewer +* +* Copyright (c) 2021 Dario Deledda. All rights reserved. +* Use of this source code is governed by an MIT license +* that can be found in the LICENSE file. +* +* Example .obj model of V from SurmanPP +* +* HOW TO COMPILE SHADERS: +* - download the sokol shader convertor tool from https://github.com/floooh/sokol-tools-bin +* +* - compile the .glsl shader with: +* linux : sokol-shdc --input gouraud.glsl --output gouraud.h --slang glsl330 +* windows: sokol-shdc.exe --input gouraud.glsl --output gouraud.h --slang glsl330 +* +* --slang parameter can be: +* - glsl330: desktop GL +* - glsl100: GLES2 / WebGL +* - glsl300es: GLES3 / WebGL2 +* - hlsl4: D3D11 +* - hlsl5: D3D11 +* - metal_macos: Metal on macOS +* - metal_ios: Metal on iOS device +* - metal_sim: Metal on iOS simulator +* - wgpu: WebGPU +* +* you can have multiple platforms at the same time passing parameters like this: --slang glsl330:hlsl5:metal_macos +* for further infos have a look at the sokol shader tool docs. +* +* ALTERNATIVE .OBJ MODELS: +* you can load alternative models putting them in the "assets/model" folder with or without their .mtl file. +* use the program help for further instructions. +* +* TODO: +* - frame counter +**********************************************************************/ +import gg +import gg.m4 +import gx +import math +import sokol.sapp +import sokol.gfx +import sokol.sgl +import time +import os +import obj + +// GLSL Include and functions + +#flag -I @VMODROOT/. +#include "gouraud.h" #Please use sokol-shdc to generate the necessary rt_glsl.h file from rt_glsl.glsl (see the instructions at the top of this file) + +fn C.gouraud_shader_desc(gfx.Backend) &C.sg_shader_desc + +const ( + win_width = 600 + win_height = 600 + bg_color = gx.white +) + +struct App { +mut: + gg &gg.Context + texture C.sg_image + init_flag bool + frame_count int + + mouse_x int = -1 + mouse_y int = -1 + scroll_y int // mouse wheel value + // time + ticks i64 + // model + obj_part &obj.ObjPart + n_vertex u32 + // init parameters + file_name string + single_material_flag bool +} + +/****************************************************************************** +* Draw functions +******************************************************************************/ +[inline] +fn vec4(x f32, y f32, z f32, w f32) m4.Vec4 { + return m4.Vec4{ + e: [x, y, z, w]! + } +} + +fn calc_matrices(w f32, h f32, rx f32, ry f32, in_scale f32, pos m4.Vec4) obj.Mats { + proj := m4.perspective(60, w / h, 0.01, 100.0) // set far plane to 100 fro the zoom function + view := m4.look_at(vec4(f32(0.0), 0, 6, 0), vec4(f32(0), 0, 0, 0), vec4(f32(0), 1, + 0, 0)) + view_proj := view * proj + + rxm := m4.rotate(m4.rad(rx), vec4(f32(1), 0, 0, 0)) + rym := m4.rotate(m4.rad(ry), vec4(f32(0), 1, 0, 0)) + + model_pos := m4.unit_m4().translate(pos) + + model_m := (rym * rxm) * model_pos + scale_m := m4.scale(vec4(in_scale, in_scale, in_scale, 1)) + + mv := scale_m * model_m // model view + nm := mv.inverse().transpose() // normal matrix + mvp := mv * view_proj // model view projection + + return obj.Mats{ + mv: mv + mvp: mvp + nm: nm + } +} + +fn draw_model(app App, model_pos m4.Vec4) u32 { + if app.init_flag == false { + return 0 + } + + ws := gg.window_size_real_pixels() + dw := ws.width / 2 + dh := ws.height / 2 + + mut scale := f32(1) + if app.obj_part.radius > 1 { + scale = 1 / (app.obj_part.radius) + } else { + scale = app.obj_part.radius + } + scale *= 3 + + // *** vertex shader uniforms *** + rot := [f32(app.mouse_y), f32(app.mouse_x)] + mut zoom_scale := scale + f32(app.scroll_y) / (app.obj_part.radius * 4) + mats := calc_matrices(dw, dh, rot[0], rot[1], zoom_scale, model_pos) + + mut tmp_vs_param := obj.Tmp_vs_param{ + mv: mats.mv + mvp: mats.mvp + nm: mats.nm + } + + // *** fragment shader uniforms *** + time_ticks := f32(time.ticks() - app.ticks) / 1000 + radius_light := f32(app.obj_part.radius) + x_light := f32(math.cos(time_ticks) * radius_light) + z_light := f32(math.sin(time_ticks) * radius_light) + + mut tmp_fs_params := obj.Tmp_fs_param{} + tmp_fs_params.ligth = m4.vec3(x_light, radius_light, z_light) + + sd := obj.Shader_data{ + vs_data: unsafe { &tmp_vs_param } + vs_len: int(sizeof(tmp_vs_param)) + fs_data: unsafe { &tmp_fs_params } + fs_len: int(sizeof(tmp_fs_params)) + } + + return app.obj_part.bind_and_draw_all(sd) +} + +fn frame(mut app App) { + ws := gg.window_size_real_pixels() + + // clear + mut color_action := C.sg_color_attachment_action{ + action: gfx.Action(C.SG_ACTION_CLEAR) + value: C.sg_color{ + r: 0.0 + g: 0.0 + b: 0.0 + a: 1.0 + } + } + + mut pass_action := C.sg_pass_action{} + pass_action.colors[0] = color_action + gfx.begin_default_pass(&pass_action, ws.width, ws.height) + + // render the data + draw_start_glsl(app) + draw_model(app, m4.Vec4{}) + // uncoment if you want a raw benchmark mode + /* + mut n_vertex_drawn := u32(0) + n_x_obj := 20 + + for x in 0..n_x_obj { + for z in 0..30 { + for y in 0..4 { + n_vertex_drawn += draw_model(app, m4.Vec4{e:[f32((x-(n_x_obj>>1))*3),-3 + y*3,f32(-6*z),1]!}) + } + } + } + */ + draw_end_glsl(app) + + // println("v:$n_vertex_drawn") + app.frame_count++ +} + +fn draw_start_glsl(app App) { + if app.init_flag == false { + return + } + ws := gg.window_size_real_pixels() + gfx.apply_viewport(0, 0, ws.width, ws.height, true) +} + +fn draw_end_glsl(app App) { + gfx.end_pass() + gfx.commit() +} + +/****************************************************************************** +* Init / Cleanup +******************************************************************************/ +fn my_init(mut app App) { + mut object := &obj.ObjPart{} + obj_file_lines := obj.read_lines_from_file(app.file_name) + object.parse_obj_buffer(obj_file_lines, app.single_material_flag) + object.summary() + app.obj_part = object + + // set max vertices, + // for a large number of the same type of object it is better use the instances!! + desc := sapp.create_desc() + gfx.setup(&desc) + sgl_desc := C.sgl_desc_t{ + max_vertices: 128 * 65536 + } + sgl.setup(&sgl_desc) + + // 1x1 pixel white, default texture + unsafe { + tmp_txt := malloc(4) + tmp_txt[0] = byte(0xFF) + tmp_txt[1] = byte(0xFF) + tmp_txt[2] = byte(0xFF) + tmp_txt[3] = byte(0xFF) + app.texture = obj.create_texture(1, 1, tmp_txt) + free(tmp_txt) + } + // glsl + app.obj_part.init_render_data(app.texture) + app.init_flag = true +} + +fn cleanup(mut app App) { + gfx.shutdown() + /* + for _, mat in app.obj_part.texture { + obj.destroy_texture(mat) + } + */ +} + +/****************************************************************************** +* events handling +******************************************************************************/ +fn my_event_manager(mut ev gg.Event, mut app App) { + if ev.typ == .mouse_move { + app.mouse_x = int(ev.mouse_x) + app.mouse_y = int(ev.mouse_y) + } + + if ev.scroll_y != 0 { + app.scroll_y += int(ev.scroll_y) + } + + if ev.typ == .touches_began || ev.typ == .touches_moved { + if ev.num_touches > 0 { + touch_point := ev.touches[0] + app.mouse_x = int(touch_point.pos_x) + app.mouse_y = int(touch_point.pos_y) + } + } +} + +/****************************************************************************** +* Main +******************************************************************************/ +// is needed for easier diagnostics on windows +[console] +fn main() { + /* + obj.tst() + exit(0) + */ + + // App init + mut app := &App{ + gg: 0 + obj_part: 0 + } + + app.file_name = 'v.obj_' // default object is the v logo + + app.single_material_flag = false + $if !android { + if os.args.len > 3 || (os.args.len >= 2 && os.args[1] in ['-h', '--help', '\\?', '-?']) { + eprintln('Usage:\nshow_obj [file_name:string] [single_material_flag:(true|false)]\n') + eprintln('file_name : name of the .obj file, it must be in the folder "assets/models"') + eprintln(' if no file name is passed the default V logo will be showed.') + eprintln(' if you want custom models you can put them in the folder "assets/models".') + eprintln("single_material_flag: if true the viewer use for all the model's parts the default material\n") + exit(0) + } + + if os.args.len >= 2 { + app.file_name = os.args[1] + } + if os.args.len >= 3 { + app.single_material_flag = os.args[2].bool() + } + println('Loading model: $app.file_name') + println('Using single material: $app.single_material_flag') + } + + app.gg = gg.new_context( + width: win_width + height: win_height + create_window: true + window_title: 'V Wavefront OBJ viewer - Use the mouse wheel to zoom' + user_data: app + bg_color: bg_color + frame_fn: frame + init_fn: my_init + cleanup_fn: cleanup + event_fn: my_event_manager + ) + + app.ticks = time.ticks() + app.gg.run() +} diff --git a/v_windows/v/old/examples/sokol/06_obj_viewer/v.mod b/v_windows/v/old/examples/sokol/06_obj_viewer/v.mod new file mode 100644 index 0000000..e69de29 diff --git a/v_windows/v/old/examples/sokol/drawing.v b/v_windows/v/old/examples/sokol/drawing.v new file mode 100644 index 0000000..e1b779b --- /dev/null +++ b/v_windows/v/old/examples/sokol/drawing.v @@ -0,0 +1,75 @@ +import sokol +import sokol.sapp +import sokol.gfx +import sokol.sgl + +struct AppState { + pass_action C.sg_pass_action +} + +const ( + used_import = sokol.used_import +) + +fn main() { + state := &AppState{ + pass_action: gfx.create_clear_pass(0.1, 0.1, 0.1, 1.0) + } + title := 'Sokol Drawing Template' + desc := C.sapp_desc{ + user_data: state + init_userdata_cb: init + frame_userdata_cb: frame + window_title: title.str + html5_canvas_name: title.str + } + sapp.run(&desc) +} + +fn init(user_data voidptr) { + desc := sapp.create_desc() // C.sg_desc{ + gfx.setup(&desc) + sgl_desc := C.sgl_desc_t{} + sgl.setup(&sgl_desc) +} + +fn frame(user_data voidptr) { + // println('frame') + state := &AppState(user_data) + draw() + gfx.begin_default_pass(&state.pass_action, sapp.width(), sapp.height()) + sgl.draw() + gfx.end_pass() + gfx.commit() +} + +fn draw() { + // first, reset and setup ortho projection + sgl.defaults() + sgl.matrix_mode_projection() + sgl.ortho(0.0, f32(sapp.width()), f32(sapp.height()), 0.0, -1.0, 1.0) + sgl.c4b(255, 0, 0, 128) + draw_hollow_rect(10, 10, 100, 30) + sgl.c4b(25, 150, 0, 128) + draw_filled_rect(10, 150, 80, 40) + // line(0, 0, 500, 500) +} + +fn draw_hollow_rect(x f32, y f32, w f32, h f32) { + sgl.begin_line_strip() + sgl.v2f(x, y) + sgl.v2f(x + w, y) + sgl.v2f(x + w, y + h) + sgl.v2f(x, y + h) + sgl.v2f(x, y) + sgl.end() +} + +fn draw_filled_rect(x f32, y f32, w f32, h f32) { + sgl.begin_quads() + sgl.v2f(x, y) + sgl.v2f(x + w, y) + sgl.v2f(x + w, y + h) + sgl.v2f(x, y + h) + sgl.end() +} diff --git a/v_windows/v/old/examples/sokol/fonts.v b/v_windows/v/old/examples/sokol/fonts.v new file mode 100644 index 0000000..1cf2170 --- /dev/null +++ b/v_windows/v/old/examples/sokol/fonts.v @@ -0,0 +1,164 @@ +import sokol +import sokol.sapp +import sokol.gfx +import sokol.sgl +import sokol.sfons +import os + +struct AppState { +mut: + pass_action C.sg_pass_action + fons &C.FONScontext + font_normal int +} + +fn main() { + mut color_action := C.sg_color_attachment_action{ + action: gfx.Action(C.SG_ACTION_CLEAR) + value: C.sg_color{ + r: 0.3 + g: 0.3 + b: 0.32 + a: 1.0 + } + } + mut pass_action := C.sg_pass_action{} + pass_action.colors[0] = color_action + state := &AppState{ + pass_action: pass_action + fons: &C.FONScontext(0) + } + title := 'V Metal/GL Text Rendering' + desc := C.sapp_desc{ + user_data: state + init_userdata_cb: init + frame_userdata_cb: frame + window_title: title.str + html5_canvas_name: title.str + } + sapp.run(&desc) +} + +fn init(mut state AppState) { + desc := sapp.create_desc() + gfx.setup(&desc) + s := &C.sgl_desc_t{} + C.sgl_setup(s) + state.fons = sfons.create(512, 512, 1) + // or use DroidSerif-Regular.ttf + if bytes := os.read_bytes(os.resource_abs_path('../assets/fonts/RobotoMono-Regular.ttf')) { + println('loaded font: $bytes.len') + state.font_normal = C.fonsAddFontMem(state.fons, c'sans', bytes.data, bytes.len, + false) + } +} + +fn frame(user_data voidptr) { + mut state := &AppState(user_data) + state.render_font() + gfx.begin_default_pass(&state.pass_action, sapp.width(), sapp.height()) + sgl.draw() + gfx.end_pass() + gfx.commit() +} + +fn (state &AppState) render_font() { + mut sx := 0.0 + mut sy := 0.0 + mut dx := 0.0 + mut dy := 0.0 + lh := f32(0.0) + white := C.sfons_rgba(255, 255, 255, 255) + black := C.sfons_rgba(0, 0, 0, 255) + brown := C.sfons_rgba(192, 128, 0, 128) + blue := C.sfons_rgba(0, 192, 255, 255) + state.fons.clear_state() + sgl.defaults() + sgl.matrix_mode_projection() + sgl.ortho(0.0, f32(C.sapp_width()), f32(C.sapp_height()), 0.0, -1.0, 1.0) + sx = 0 + sy = 50 + dx = sx + dy = sy + state.fons.set_font(state.font_normal) + state.fons.set_size(100.0) + ascender := f32(0.0) + descender := f32(0.0) + state.fons.vert_metrics(&ascender, &descender, &lh) + dx = sx + dy += lh + C.fonsSetColor(state.fons, white) + dx = C.fonsDrawText(state.fons, dx, dy, c'The quick ', C.NULL) + C.fonsSetFont(state.fons, state.font_normal) + C.fonsSetSize(state.fons, 48.0) + C.fonsSetColor(state.fons, brown) + dx = C.fonsDrawText(state.fons, dx, dy, c'brown ', C.NULL) + C.fonsSetFont(state.fons, state.font_normal) + C.fonsSetSize(state.fons, 24.0) + C.fonsSetColor(state.fons, white) + dx = C.fonsDrawText(state.fons, dx, dy, c'fox ', C.NULL) + dx = sx + dy += lh * 1.2 + C.fonsSetSize(state.fons, 20.0) + C.fonsSetFont(state.fons, state.font_normal) + C.fonsSetColor(state.fons, blue) + C.fonsDrawText(state.fons, dx, dy, c'Now is the time for all good men to come to the aid of the party.', + C.NULL) + dx = 300 + dy = 350 + C.fonsSetAlign(state.fons, C.FONS_ALIGN_LEFT | C.FONS_ALIGN_BASELINE) + C.fonsSetSize(state.fons, 60.0) + C.fonsSetFont(state.fons, state.font_normal) + C.fonsSetColor(state.fons, white) + C.fonsSetSpacing(state.fons, 5.0) + C.fonsSetBlur(state.fons, 6.0) + C.fonsDrawText(state.fons, dx, dy, c'Blurry...', C.NULL) + dx = 300 + dy += 50.0 + C.fonsSetSize(state.fons, 28.0) + C.fonsSetFont(state.fons, state.font_normal) + C.fonsSetColor(state.fons, white) + C.fonsSetSpacing(state.fons, 0.0) + C.fonsSetBlur(state.fons, 3.0) + C.fonsDrawText(state.fons, dx, dy + 2, c'DROP SHADOW', C.NULL) + C.fonsSetColor(state.fons, black) + C.fonsSetBlur(state.fons, 0) + C.fonsDrawText(state.fons, dx, dy, c'DROP SHADOW', C.NULL) + C.fonsSetSize(state.fons, 18.0) + C.fonsSetFont(state.fons, state.font_normal) + C.fonsSetColor(state.fons, white) + dx = 50 + dy = 350 + line(f32(dx - 10), f32(dy), f32(dx + 250), f32(dy)) + C.fonsSetAlign(state.fons, C.FONS_ALIGN_LEFT | C.FONS_ALIGN_TOP) + dx = C.fonsDrawText(state.fons, dx, dy, c'Top', C.NULL) + dx += 10 + C.fonsSetAlign(state.fons, C.FONS_ALIGN_LEFT | C.FONS_ALIGN_MIDDLE) + dx = C.fonsDrawText(state.fons, dx, dy, c'Middle', C.NULL) + dx += 10 + C.fonsSetAlign(state.fons, C.FONS_ALIGN_LEFT | C.FONS_ALIGN_BASELINE) + dx = C.fonsDrawText(state.fons, dx, dy, c'Baseline', C.NULL) + dx += 10 + C.fonsSetAlign(state.fons, C.FONS_ALIGN_LEFT | C.FONS_ALIGN_BOTTOM) + C.fonsDrawText(state.fons, dx, dy, c'Bottom', C.NULL) + dx = 150 + dy = 400 + line(f32(dx), f32(dy - 30), f32(dx), f32(dy + 80.0)) + C.fonsSetAlign(state.fons, C.FONS_ALIGN_LEFT | C.FONS_ALIGN_BASELINE) + C.fonsDrawText(state.fons, dx, dy, c'Left', C.NULL) + dy += 30 + C.fonsSetAlign(state.fons, C.FONS_ALIGN_CENTER | C.FONS_ALIGN_BASELINE) + C.fonsDrawText(state.fons, dx, dy, c'Center', C.NULL) + dy += 30 + C.fonsSetAlign(state.fons, C.FONS_ALIGN_RIGHT | C.FONS_ALIGN_BASELINE) + C.fonsDrawText(state.fons, dx, dy, c'Right', C.NULL) + C.sfons_flush(state.fons) +} + +fn line(sx f32, sy f32, ex f32, ey f32) { + sgl.begin_lines() + sgl.c4b(255, 255, 0, 128) + sgl.v2f(sx, sy) + sgl.v2f(ex, ey) + sgl.end() +} diff --git a/v_windows/v/old/examples/sokol/freetype_raven.v b/v_windows/v/old/examples/sokol/freetype_raven.v new file mode 100644 index 0000000..d65b01f --- /dev/null +++ b/v_windows/v/old/examples/sokol/freetype_raven.v @@ -0,0 +1,153 @@ +import sokol +import sokol.sapp +import sokol.gfx +import sokol.sgl +import sokol.sfons +import os +import time + +const ( + text = ' +Once upon a midnight dreary, while I pondered, weak and weary, +Over many a quaint and curious volume of forgotten lore— + While I nodded, nearly napping, suddenly there came a tapping, +As of some one gently rapping, rapping at my chamber door. +“’Tis some visitor,” I muttered, “tapping at my chamber door— + Only this and nothing more.” + + Ah, distinctly I remember it was in the bleak December; +And each separate dying ember wrought its ghost upon the floor. + Eagerly I wished the morrow;—vainly I had sought to borrow + From my books surcease of sorrow—sorrow for the lost Lenore— +For the rare and radiant maiden whom the angels name Lenore— + Nameless here for evermore. + + And the silken, sad, uncertain rustling of each purple curtain +Thrilled me—filled me with fantastic terrors never felt before; + So that now, to still the beating of my heart, I stood repeating + “’Tis some visitor entreating entrance at my chamber door— +Some late visitor entreating entrance at my chamber door;— + This it is and nothing more.” + + Presently my soul grew stronger; hesitating then no longer, +“Sir,” said I, “or Madam, truly your forgiveness I implore; + But the fact is I was napping, and so gently you came rapping, + And so faintly you came tapping, tapping at my chamber door, +That I scarce was sure I heard you”—here I opened wide the door;— + Darkness there and nothing more. + +Deep into that darkness peering, long I stood there wondering, fearing, +Doubting, dreaming dreams no mortal ever dared to dream before; + But the silence was unbroken, and the stillness gave no token, + And the only word there spoken was the whispered word, “Lenore?” +This I whispered, and an echo murmured back the word, “Lenore!”— + Merely this and nothing more. + + Back into the chamber turning, all my soul within me burning, +Soon again I heard a tapping somewhat louder than before. + “Surely,” said I, “surely that is something at my window lattice; + Let me see, then, what thereat is, and this mystery explore— +Let my heart be still a moment and this mystery explore;— + ’Tis the wind and nothing more!” +' + lines = text.split('\n') +) + +struct AppState { +mut: + pass_action C.sg_pass_action + fons &C.FONScontext + font_normal int + inited bool +} + +fn main() { + mut color_action := C.sg_color_attachment_action{ + action: gfx.Action(C.SG_ACTION_CLEAR) + value: C.sg_color{ + r: 1.0 + g: 1.0 + b: 1.0 + a: 1.0 + } + } + mut pass_action := C.sg_pass_action{} + pass_action.colors[0] = color_action + state := &AppState{ + pass_action: pass_action + fons: &C.FONScontext(0) + } + title := 'V Metal/GL Text Rendering' + desc := C.sapp_desc{ + user_data: state + init_userdata_cb: init + frame_userdata_cb: frame + window_title: title.str + html5_canvas_name: title.str + width: 600 + height: 700 + high_dpi: true + } + sapp.run(&desc) +} + +fn init(user_data voidptr) { + mut state := &AppState(user_data) + desc := sapp.create_desc() + gfx.setup(&desc) + s := &C.sgl_desc_t{} + C.sgl_setup(s) + state.fons = sfons.create(512, 512, 1) + // or use DroidSerif-Regular.ttf + if bytes := os.read_bytes(os.resource_abs_path('../assets/fonts/RobotoMono-Regular.ttf')) { + println('loaded font: $bytes.len') + state.font_normal = C.fonsAddFontMem(state.fons, c'sans', bytes.data, bytes.len, + false) + } +} + +fn frame(user_data voidptr) { + t := time.ticks() + mut state := &AppState(user_data) + state.render_font() + gfx.begin_default_pass(&state.pass_action, sapp.width(), sapp.height()) + sgl.draw() + gfx.end_pass() + gfx.commit() + println(time.ticks() - t) +} + +const ( + black = C.sfons_rgba(0, 0, 0, 255) +) + +fn (mut state AppState) render_font() { + lh := 30 + mut dy := lh + if !state.inited { + state.fons.clear_state() + sgl.defaults() + sgl.matrix_mode_projection() + sgl.ortho(0.0, f32(C.sapp_width()), f32(C.sapp_height()), 0.0, -1.0, 1.0) + state.fons.set_font(state.font_normal) + state.fons.set_size(100.0) + C.fonsSetColor(state.fons, black) + C.fonsSetFont(state.fons, state.font_normal) + C.fonsSetSize(state.fons, 35.0) + state.inited = true + } + + for line in lines { + C.fonsDrawText(state.fons, 40, dy, line.str, C.NULL) + dy += lh + } + C.sfons_flush(state.fons) +} + +fn line(sx f32, sy f32, ex f32, ey f32) { + sgl.begin_lines() + sgl.c4b(255, 255, 0, 128) + sgl.v2f(sx, sy) + sgl.v2f(ex, ey) + sgl.end() +} diff --git a/v_windows/v/old/examples/sokol/particles/modules/particle/LICENSE b/v_windows/v/old/examples/sokol/particles/modules/particle/LICENSE new file mode 100644 index 0000000..1595a57 --- /dev/null +++ b/v_windows/v/old/examples/sokol/particles/modules/particle/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Lars Pontoppidan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/v_windows/v/old/examples/sokol/particles/modules/particle/color.v b/v_windows/v/old/examples/sokol/particles/modules/particle/color.v new file mode 100644 index 0000000..535e48a --- /dev/null +++ b/v_windows/v/old/examples/sokol/particles/modules/particle/color.v @@ -0,0 +1,12 @@ +// Copyright(C) 2019 Lars Pontoppidan. All rights reserved. +// Use of this source code is governed by an MIT license file distributed with this software package +module particle + +// * Color +pub struct Color { +mut: + r byte + g byte + b byte + a byte +} diff --git a/v_windows/v/old/examples/sokol/particles/modules/particle/particle.v b/v_windows/v/old/examples/sokol/particles/modules/particle/particle.v new file mode 100644 index 0000000..ac172b9 --- /dev/null +++ b/v_windows/v/old/examples/sokol/particles/modules/particle/particle.v @@ -0,0 +1,81 @@ +// Copyright(C) 2019 Lars Pontoppidan. All rights reserved. +// Use of this source code is governed by an MIT license file distributed with this software package +module particle + +import particle.vec2 +import sokol.sgl + +const ( + default_life_time = 1000 + default_v_color = Color{93, 136, 193, 255} +) + +// * Module public +pub fn new(location vec2.Vec2) &Particle { + p := &Particle{ + location: location + velocity: vec2.Vec2{0, 0} + acceleration: vec2.Vec2{0, 0} + color: particle.default_v_color + life_time: particle.default_life_time + life_time_init: particle.default_life_time + } + return p +} + +fn remap(v f64, min f64, max f64, new_min f64, new_max f64) f64 { + return (((v - min) * (new_max - new_min)) / (max - min)) + new_min +} + +// Particle +pub struct Particle { +mut: + location vec2.Vec2 + velocity vec2.Vec2 + acceleration vec2.Vec2 + color Color + life_time f64 + life_time_init f64 +} + +pub fn (mut p Particle) update(dt f64) { + mut acc := p.acceleration + acc.multiply_f64(dt) + p.velocity = p.velocity.add(acc) + p.location = p.location.add(p.velocity) + lt := p.life_time - (1000 * dt) + if lt > 0 { + p.life_time = lt + p.color.r = p.color.r - 1 // byte(remap(p.life_time,0.0,p.life_time_init,0,p.color.r)) + p.color.g = p.color.g - 1 // byte(remap(p.life_time,0.0,p.life_time_init,0,p.color.g)) + p.color.b = p.color.b - 1 // byte(remap(p.life_time,0.0,p.life_time_init,0,p.color.b)) + p.color.a = byte(int(remap(p.life_time, 0.0, p.life_time_init, 0, 255))) - 10 + } else { + p.life_time = 0 + } +} + +pub fn (p Particle) is_dead() bool { + return p.life_time <= 0.0 +} + +pub fn (p Particle) draw() { + l := p.location + sgl.c4b(p.color.r, p.color.g, p.color.b, p.color.a) + lx := f32(l.x) + ly := f32(l.y) + sgl.v2f(lx, ly) + sgl.v2f(lx + 2, ly) + sgl.v2f(lx + 2, ly + 2) + sgl.v2f(lx, ly + 2) +} + +pub fn (mut p Particle) reset() { + p.location.zero() + p.acceleration.zero() + p.velocity.zero() + // p.color = Color{93, 136, 193, 255} + p.color = particle.default_v_color + p.life_time = particle.default_life_time + p.life_time_init = p.life_time +} diff --git a/v_windows/v/old/examples/sokol/particles/modules/particle/system.v b/v_windows/v/old/examples/sokol/particles/modules/particle/system.v new file mode 100644 index 0000000..4f0382b --- /dev/null +++ b/v_windows/v/old/examples/sokol/particles/modules/particle/system.v @@ -0,0 +1,99 @@ +// Copyright(C) 2019 Lars Pontoppidan. All rights reserved. +// Use of this source code is governed by an MIT license file distributed with this software package +module particle + +import particle.vec2 +import rand +import sokol.sgl + +pub struct SystemConfig { + pool int +} + +pub struct System { + width int + height int +mut: + pool []&Particle + bin []&Particle +} + +pub fn (mut s System) init(sc SystemConfig) { + for i := 0; i < sc.pool; i++ { + p := new(vec2.Vec2{f32(s.width) * 0.5, f32(s.height) * 0.5}) + s.bin << p + } +} + +pub fn (mut s System) update(dt f64) { + mut p := &Particle(0) + for i := 0; i < s.pool.len; i++ { + p = s.pool[i] + p.update(dt) + if p.is_dead() { + s.bin << p + s.pool.delete(i) + } + } +} + +pub fn (s System) draw() { + sgl.begin_quads() + for p in s.pool { + p.draw() + } + sgl.end() +} + +pub fn (mut s System) reset() { + for i in 0 .. s.pool.len { + mut p := s.pool[i] + p.reset() + p.life_time = 0 + } + for i in 0 .. s.bin.len { + mut p := s.pool[i] + p.reset() + p.life_time = 0 + } +} + +pub fn (mut s System) explode(x f32, y f32) { + mut reserve := 500 + center := vec2.Vec2{x, y} + mut p := &Particle(0) + for i := 0; i < s.bin.len && reserve > 0; i++ { + p = s.bin[i] + p.reset() + p.location.from(center) + p.acceleration = vec2.Vec2{rand.f32_in_range(-0.5, 0.5), rand.f32_in_range(-0.5, + 0.5)} + p.velocity = vec2.Vec2{rand.f32_in_range(-0.5, 0.5), rand.f32_in_range(-0.5, 0.5)} + p.life_time = rand.f64_in_range(500, 2000) + s.pool << p + s.bin.delete(i) + reserve-- + } +} + +pub fn (mut s System) free() { + for p in s.pool { + if p == 0 { + print(ptr_str(p) + ' ouch') + continue + } + unsafe { free(p) } + } + s.pool.clear() + for p in s.bin { + if p == 0 { + print(ptr_str(p) + ' ouch') + continue + } + unsafe { + // println('Freeing from bin') + free(p) + } + } + s.bin.clear() +} diff --git a/v_windows/v/old/examples/sokol/particles/modules/particle/v.mod b/v_windows/v/old/examples/sokol/particles/modules/particle/v.mod new file mode 100644 index 0000000..e69de29 diff --git a/v_windows/v/old/examples/sokol/particles/modules/particle/vec2/v.mod b/v_windows/v/old/examples/sokol/particles/modules/particle/vec2/v.mod new file mode 100644 index 0000000..e69de29 diff --git a/v_windows/v/old/examples/sokol/particles/modules/particle/vec2/vec2.v b/v_windows/v/old/examples/sokol/particles/modules/particle/vec2/vec2.v new file mode 100644 index 0000000..ec41485 --- /dev/null +++ b/v_windows/v/old/examples/sokol/particles/modules/particle/vec2/vec2.v @@ -0,0 +1,89 @@ +// Copyright(C) 2019 Lars Pontoppidan. All rights reserved. +// Use of this source code is governed by an MIT license file distributed with this software package +module vec2 + +pub struct Vec2 { +pub mut: + x f64 + y f64 +} + +pub fn (mut v Vec2) zero() { + v.x = 0.0 + v.y = 0.0 +} + +pub fn (mut v Vec2) from(src Vec2) { + v.x = src.x + v.y = src.y +} + +// * Addition +// + operator overload. Adds two vectors +pub fn (v1 Vec2) + (v2 Vec2) Vec2 { + return Vec2{v1.x + v2.x, v1.y + v2.y} +} + +pub fn (v Vec2) add(vector Vec2) Vec2 { + return Vec2{v.x + vector.x, v.y + vector.y} +} + +pub fn (v Vec2) add_f64(scalar f64) Vec2 { + return Vec2{v.x + scalar, v.y + scalar} +} + +pub fn (mut v Vec2) plus(vector Vec2) { + v.x += vector.x + v.y += vector.y +} + +pub fn (mut v Vec2) plus_f64(scalar f64) { + v.x += scalar + v.y += scalar +} + +// * Subtraction +pub fn (v1 Vec2) - (v2 Vec2) Vec2 { + return Vec2{v1.x - v2.x, v1.y - v2.y} +} + +pub fn (v Vec2) sub(vector Vec2) Vec2 { + return Vec2{v.x - vector.x, v.y - vector.y} +} + +pub fn (v Vec2) sub_f64(scalar f64) Vec2 { + return Vec2{v.x - scalar, v.y - scalar} +} + +pub fn (mut v Vec2) subtract(vector Vec2) { + v.x -= vector.x + v.y -= vector.y +} + +pub fn (mut v Vec2) subtract_f64(scalar f64) { + v.x -= scalar + v.y -= scalar +} + +// * Multiplication +pub fn (v1 Vec2) * (v2 Vec2) Vec2 { + return Vec2{v1.x * v2.x, v1.y * v2.y} +} + +pub fn (v Vec2) mul(vector Vec2) Vec2 { + return Vec2{v.x * vector.x, v.y * vector.y} +} + +pub fn (v Vec2) mul_f64(scalar f64) Vec2 { + return Vec2{v.x * scalar, v.y * scalar} +} + +pub fn (mut v Vec2) multiply(vector Vec2) { + v.x *= vector.x + v.y *= vector.y +} + +pub fn (mut v Vec2) multiply_f64(scalar f64) { + v.x *= scalar + v.y *= scalar +} diff --git a/v_windows/v/old/examples/sokol/particles/particles.v b/v_windows/v/old/examples/sokol/particles/particles.v new file mode 100644 index 0000000..05b0cb3 --- /dev/null +++ b/v_windows/v/old/examples/sokol/particles/particles.v @@ -0,0 +1,155 @@ +// Copyright(C) 2019 Lars Pontoppidan. All rights reserved. +// Use of this source code is governed by an MIT license file distributed with this software package +module main + +import time +import sokol +import sokol.sapp +import sokol.gfx +import sokol.sgl +import particle + +const ( + used_import = sokol.used_import +) + +fn main() { + mut app := &App{ + width: 800 + height: 400 + pass_action: gfx.create_clear_pass(0.1, 0.1, 0.1, 1.0) + } + app.init() + app.run() +} + +struct App { + pass_action C.sg_pass_action +mut: + width int + height int + frame i64 + last i64 + ps particle.System + alpha_pip C.sgl_pipeline +} + +fn (mut a App) init() { + a.frame = 0 + a.last = time.ticks() + a.ps = particle.System{ + width: a.width + height: a.height + } + a.ps.init(particle.SystemConfig{ + pool: 20000 + }) +} + +fn (mut a App) cleanup() { + a.ps.free() +} + +fn (mut a App) run() { + title := 'V Particle Example' + desc := C.sapp_desc{ + width: a.width + height: a.height + user_data: a + init_userdata_cb: init + frame_userdata_cb: frame + event_userdata_cb: event + window_title: title.str + html5_canvas_name: title.str + cleanup_userdata_cb: cleanup + } + sapp.run(&desc) +} + +fn (a App) draw() { + sgl.load_pipeline(a.alpha_pip) + a.ps.draw() +} + +fn init(user_data voidptr) { + mut app := &App(user_data) + desc := sapp.create_desc() + gfx.setup(&desc) + sgl_desc := C.sgl_desc_t{ + max_vertices: 50 * 65536 + } + sgl.setup(&sgl_desc) + mut pipdesc := C.sg_pipeline_desc{} + unsafe { C.memset(&pipdesc, 0, sizeof(pipdesc)) } + + color_state := C.sg_color_state{ + blend: C.sg_blend_state{ + enabled: true + src_factor_rgb: gfx.BlendFactor(C.SG_BLENDFACTOR_SRC_ALPHA) + dst_factor_rgb: gfx.BlendFactor(C.SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA) + } + } + pipdesc.colors[0] = color_state + + app.alpha_pip = sgl.make_pipeline(&pipdesc) +} + +fn cleanup(user_data voidptr) { + mut app := &App(user_data) + app.cleanup() + gfx.shutdown() +} + +fn frame(user_data voidptr) { + mut app := &App(user_data) + app.width = sapp.width() + app.height = sapp.height() + t := time.ticks() + dt := f64(t - app.last) / 1000.0 + app.ps.update(dt) + draw(app) + gfx.begin_default_pass(&app.pass_action, app.width, app.height) + sgl.default_pipeline() + sgl.draw() + gfx.end_pass() + gfx.commit() + app.frame++ + app.last = t +} + +fn event(ev &C.sapp_event, mut app App) { + if ev.@type == .mouse_move { + app.ps.explode(ev.mouse_x, ev.mouse_y) + } + if ev.@type == .mouse_up || ev.@type == .mouse_down { + if ev.mouse_button == .left { + is_pressed := ev.@type == .mouse_down + if is_pressed { + app.ps.explode(ev.mouse_x, ev.mouse_y) + } + } + } + if ev.@type == .key_up || ev.@type == .key_down { + if ev.key_code == .r { + is_pressed := ev.@type == .key_down + if is_pressed { + app.ps.reset() + } + } + } + if ev.@type == .touches_began || ev.@type == .touches_moved { + if ev.num_touches > 0 { + touch_point := ev.touches[0] + app.ps.explode(touch_point.pos_x, touch_point.pos_y) + } + } +} + +fn draw(a &App) { + sgl.defaults() + sgl.matrix_mode_projection() + sgl.ortho(0, f32(sapp.width()), f32(sapp.height()), 0.0, -1.0, 1.0) + sgl.push_matrix() + a.draw() + sgl.pop_matrix() +} diff --git a/v_windows/v/old/examples/sokol/sounds/melody.v b/v_windows/v/old/examples/sokol/sounds/melody.v new file mode 100644 index 0000000..aa5ebc4 --- /dev/null +++ b/v_windows/v/old/examples/sokol/sounds/melody.v @@ -0,0 +1,75 @@ +import gg +import gx +import sokol.audio + +const credits = 'Based on the ByteBeat formula from: https://www.youtube.com/watch?v=V4GfkFbDojc \n "Techno" by Gabriel Miceli' + +struct AppState { +mut: + gframe int // the current graphical frame + frame_0 int // offset of the current audio frames, relative to the start of the music + frames [2048]f32 // a copy of the last rendered audio frames + gg &gg.Context // used for drawing +} + +fn my_audio_stream_callback(buffer &f32, num_frames int, num_channels int, mut acontext AppState) { + mut soundbuffer := unsafe { buffer } + for frame := 0; frame < num_frames; frame++ { + t := int(f32(acontext.frame_0 + frame) * 0.245) + // "Techno" by Gabriel Miceli + y := (t * (((t / 10 | 0) ^ ((t / 10 | 0) - 1280)) % 11) / 2 & 127) + + (t * (((t / 640 | 0) ^ ((t / 640 | 0) - 2)) % 13) / 2 & 127) + for ch := 0; ch < num_channels; ch++ { + idx := frame * num_channels + ch + unsafe { + a := f32(byte(y) - 127) / 255.0 + soundbuffer[idx] = a + acontext.frames[idx & 2047] = a + } + } + } + acontext.frame_0 += num_frames +} + +fn main() { + println(credits) + mut state := &AppState{ + gg: 0 + } + audio.setup( + stream_userdata_cb: my_audio_stream_callback + user_data: state + ) + state.gg = gg.new_context( + bg_color: gx.rgb(50, 50, 50) + width: 1024 + height: 400 + create_window: true + window_title: 'ByteBeat Music' + frame_fn: graphics_frame + user_data: state + ) + state.gg.run() + audio.shutdown() +} + +fn graphics_frame(mut state AppState) { + state.gframe++ + state.gg.begin() + state.draw() + state.gg.end() +} + +[inline] +fn (mut state AppState) bsample(idx int) byte { + return byte(127 + state.frames[(state.gframe + idx) & 2047] * 128) +} + +fn (mut state AppState) draw() { + // first, reset and setup ortho projection + for x in 0 .. 1024 { + mut y := 100 * (state.frames[2 * x] + state.frames[2 * x + 1]) + state.gg.draw_line(x, 200, x, 200 + y, gx.rgba(state.bsample(x), state.bsample(x + 300), + state.bsample(x + 700), 255)) + } +} diff --git a/v_windows/v/old/examples/sokol/sounds/simple_sin_tones.v b/v_windows/v/old/examples/sokol/sounds/simple_sin_tones.v new file mode 100644 index 0000000..c327117 --- /dev/null +++ b/v_windows/v/old/examples/sokol/sounds/simple_sin_tones.v @@ -0,0 +1,42 @@ +import time +import math +import sokol.audio + +const ( + sw = time.new_stopwatch() + sw_start_ms = sw.elapsed().milliseconds() +) + +[inline] +fn sintone(periods int, frame int, num_frames int) f32 { + return math.sinf(f32(periods) * (2 * math.pi) * f32(frame) / f32(num_frames)) +} + +fn my_audio_stream_callback(buffer &f32, num_frames int, num_channels int) { + ms := sw.elapsed().milliseconds() - sw_start_ms + unsafe { + mut soundbuffer := buffer + for frame := 0; frame < num_frames; frame++ { + for ch := 0; ch < num_channels; ch++ { + idx := frame * num_channels + ch + if ms < 250 { + soundbuffer[idx] = 0.5 * sintone(20, frame, num_frames) + } else if ms < 300 { + soundbuffer[idx] = 0.5 * sintone(25, frame, num_frames) + } else if ms < 1500 { + soundbuffer[idx] *= sintone(22, frame, num_frames) + } else { + soundbuffer[idx] = 0.5 * sintone(25, frame, num_frames) + } + } + } + } +} + +fn main() { + audio.setup( + stream_cb: my_audio_stream_callback + ) + time.sleep(2000 * time.millisecond) + audio.shutdown() +} diff --git a/v_windows/v/old/examples/sokol/sounds/uhoh.wav b/v_windows/v/old/examples/sokol/sounds/uhoh.wav new file mode 100644 index 0000000..cff9e3c Binary files /dev/null and b/v_windows/v/old/examples/sokol/sounds/uhoh.wav differ diff --git a/v_windows/v/old/examples/sokol/sounds/wav_player.v b/v_windows/v/old/examples/sokol/sounds/wav_player.v new file mode 100644 index 0000000..3c9419b --- /dev/null +++ b/v_windows/v/old/examples/sokol/sounds/wav_player.v @@ -0,0 +1,209 @@ +import os +import time +import sokol.audio + +struct Player { +mut: + samples []f32 + pos int + finished bool +} + +fn main() { + if os.args.len < 2 { + eprintln('Usage: play_wav file1.wav file2.wav ...') + play_sounds([os.resource_abs_path('uhoh.wav')]) ? + exit(1) + } + play_sounds(os.args[1..]) ? +} + +fn play_sounds(files []string) ? { + mut player := Player{} + player.init() + for f in files { + if !os.exists(f) || os.is_dir(f) { + eprintln('skipping "$f" (does not exist)') + continue + } + fext := os.file_ext(f).to_lower() + if fext != '.wav' { + eprintln('skipping "$f" (not a .wav file)') + continue + } + player.play_wav_file(f) ? + } + player.stop() +} + +// +fn audio_player_callback(buffer &f32, num_frames int, num_channels int, mut p Player) { + if p.finished { + return + } + ntotal := num_channels * num_frames + nremaining := p.samples.len - p.pos + nsamples := if nremaining < ntotal { nremaining } else { ntotal } + if nsamples <= 0 { + p.finished = true + return + } + unsafe { C.memcpy(buffer, &p.samples[p.pos], nsamples * int(sizeof(f32))) } + p.pos += nsamples +} + +fn (mut p Player) init() { + audio.setup( + num_channels: 2 + stream_userdata_cb: audio_player_callback + user_data: p + ) +} + +fn (mut p Player) stop() { + audio.shutdown() + p.free() +} + +fn (mut p Player) play_wav_file(fpath string) ? { + println('> play_wav_file: $fpath') + samples := read_wav_file_samples(fpath) ? + p.finished = true + p.samples << samples + p.finished = false + for !p.finished { + time.sleep(16 * time.millisecond) + } + p.free() +} + +fn (mut p Player) free() { + p.finished = false + p.samples = []f32{} + p.pos = 0 +} + +// The read_wav_file_samples function below is based on the following sources: +// http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html +// http://www.lightlink.com/tjweber/StripWav/WAVE.html +// http://www.lightlink.com/tjweber/StripWav/Canon.html +// https://tools.ietf.org/html/draft-ema-vpim-wav-00 +// NB: > The chunks MAY appear in any order except that the Format chunk +// > MUST be placed before the Sound data chunk (but not necessarily +// > contiguous to the Sound data chunk). +struct RIFFHeader { + riff [4]byte + file_size u32 + form_type [4]byte +} + +struct RIFFChunkHeader { + chunk_type [4]byte + chunk_size u32 + chunk_data voidptr +} + +struct RIFFFormat { + format_tag u16 // PCM = 1; Values other than 1 indicate some form of compression. + nchannels u16 // Nc ; 1 = mono ; 2 = stereo + sample_rate u32 // F + avg_bytes_per_second u32 // F * M*Nc + nblock_align u16 // M*Nc + bits_per_sample u16 // 8 * M + cbsize u16 // Size of the extension: 22 + valid_bits_per_sample u16 // at most 8*M + channel_mask u32 // Speaker position mask + sub_format [16]byte // GUID +} + +fn read_wav_file_samples(fpath string) ?[]f32 { + mut res := []f32{} + // eprintln('> read_wav_file_samples: $fpath -------------------------------------------------') + mut bytes := os.read_bytes(fpath) ? + mut pbytes := &byte(bytes.data) + mut offset := u32(0) + rh := unsafe { &RIFFHeader(pbytes) } + // eprintln('rh: $rh') + if rh.riff != [byte(`R`), `I`, `F`, `F`]! { + return error('WAV should start with `RIFF`') + } + if rh.form_type != [byte(`W`), `A`, `V`, `E`]! { + return error('WAV should have `WAVE` form type') + } + if rh.file_size + 8 != bytes.len { + return error('WAV should have valid lenght') + } + offset += sizeof(RIFFHeader) + mut rf := &RIFFFormat(0) + for { + if offset >= bytes.len { + break + } + // + ch := unsafe { &RIFFChunkHeader(pbytes + offset) } + offset += 8 + ch.chunk_size + // eprintln('ch: $ch') + // eprintln('p: $pbytes | offset: $offset | bytes.len: $bytes.len') + // //////// + if ch.chunk_type == [byte(`L`), `I`, `S`, `T`]! { + continue + } + // + if ch.chunk_type == [byte(`i`), `d`, `3`, ` `]! { + continue + } + // + if ch.chunk_type == [byte(`f`), `m`, `t`, ` `]! { + // eprintln('`fmt ` chunk') + rf = unsafe { &RIFFFormat(&ch.chunk_data) } + // eprintln('fmt riff format: $rf') + if rf.format_tag != 1 { + return error('only PCM encoded WAVs are supported') + } + if rf.nchannels < 1 || rf.nchannels > 2 { + return error('only mono or stereo WAVs are supported') + } + if rf.bits_per_sample !in [u16(8), 16] { + return error('only 8 or 16 bits per sample WAVs are supported') + } + continue + } + // + if ch.chunk_type == [byte(`d`), `a`, `t`, `a`]! { + if rf == 0 { + return error('`data` chunk should be after `fmt ` chunk') + } + // eprintln('`fmt ` chunk: $rf\n`data` chunk: $ch') + mut doffset := 0 + mut dp := unsafe { &byte(&ch.chunk_data) } + for doffset < ch.chunk_size { + for c := 0; c < rf.nchannels; c++ { + mut x := f32(0.0) + mut step := 0 + ppos := unsafe { dp + doffset } + if rf.bits_per_sample == 8 { + d8 := unsafe { &byte(ppos) } + x = (f32(*d8) - 128) / 128.0 + step = 1 + doffset++ + } + if rf.bits_per_sample == 16 { + d16 := unsafe { &i16(ppos) } + x = f32(*d16) / 32768.0 + step = 2 + } + doffset += step + if doffset < ch.chunk_size { + res << x + if rf.nchannels == 1 { + // Duplicating single channel mono sounds, + // produces a stereo sound, simplifying further processing: + res << x + } + } + } + } + } + } + return res +} diff --git a/v_windows/v/old/examples/spectral.v b/v_windows/v/old/examples/spectral.v new file mode 100644 index 0000000..88451af --- /dev/null +++ b/v_windows/v/old/examples/spectral.v @@ -0,0 +1,68 @@ +/* +https://benchmarksgame-team.pages.debian.net/benchmarksgame/performance/spectralnorm.html +Added: Pradeep Verghese +Benchmarks: +Used v -prod spectral.v +Command: time ./spectral 5500 +Output: 1.274224153 + +Time: 11.67s user 0.02s system 99% cpu 11.721 total +*/ +module main + +import math +import os +import strconv + +fn evala(i int, j int) int { + return ((i + j) * (i + j + 1) / 2 + i + 1) +} + +fn times(mut v []f64, u []f64) { + for i in 0 .. v.len { + mut a := f64(0) + for j in 0 .. u.len { + a += u[j] / f64(evala(i, j)) + } + v[i] = a + } +} + +fn times_trans(mut v []f64, u []f64) { + for i in 0 .. v.len { + mut a := f64(0) + for j in 0 .. u.len { + a += u[j] / f64(evala(j, i)) + } + v[i] = a + } +} + +fn a_times_transp(mut v []f64, u []f64) { + mut x := []f64{len: u.len, init: 0} + times(mut x, u) + times_trans(mut v, x) +} + +fn main() { + mut n := 0 + if os.args.len == 2 { + n = strconv.atoi(os.args[1]) or { 0 } + } else { + n = 0 + } + mut u := []f64{len: n, init: 1} + mut v := []f64{len: n, init: 1} + for _ in 0 .. 10 { + a_times_transp(mut v, u) + a_times_transp(mut u, v) + } + mut vbv := f64(0) + mut vv := f64(0) + for i in 0 .. n { + vbv += u[i] * v[i] + vv += v[i] * v[i] + } + ans := math.sqrt(vbv / vv) + println('${ans:0.9f}') +} diff --git a/v_windows/v/old/examples/submodule/main.v b/v_windows/v/old/examples/submodule/main.v new file mode 100644 index 0000000..e1ae1db --- /dev/null +++ b/v_windows/v/old/examples/submodule/main.v @@ -0,0 +1,7 @@ +import mymodules { add_xy } +import mymodules.submodule { sub_xy } + +fn main() { + println(add_xy(2, 3)) // expected: 5 + println(sub_xy(10, 7)) // expected: 3 +} diff --git a/v_windows/v/old/examples/submodule/mymodules/main_functions.v b/v_windows/v/old/examples/submodule/mymodules/main_functions.v new file mode 100644 index 0000000..0697d36 --- /dev/null +++ b/v_windows/v/old/examples/submodule/mymodules/main_functions.v @@ -0,0 +1,5 @@ +module mymodule + +pub fn add_xy(x int, y int) int { + return x + y +} diff --git a/v_windows/v/old/examples/submodule/mymodules/submodule/sub_functions.v b/v_windows/v/old/examples/submodule/mymodules/submodule/sub_functions.v new file mode 100644 index 0000000..15448ec --- /dev/null +++ b/v_windows/v/old/examples/submodule/mymodules/submodule/sub_functions.v @@ -0,0 +1,5 @@ +module submodule + +pub fn sub_xy(x int, y int) int { + return x - y +} diff --git a/v_windows/v/old/examples/tcp_echo_server.v b/v_windows/v/old/examples/tcp_echo_server.v new file mode 100644 index 0000000..e5a0973 --- /dev/null +++ b/v_windows/v/old/examples/tcp_echo_server.v @@ -0,0 +1,40 @@ +import io +import net + +// This file shows how a basic TCP echo server can be implemented using +// the net module. You can connect to the server by using netcat or telnet, +// in separate shells, for example: +// nc 127.0.0.1 12345 +// or +// telnet 127.0.0.1 12345 + +fn main() { + mut server := net.listen_tcp(.ip6, ':12345') ? + laddr := server.addr() ? + eprintln('Listen on $laddr ...') + for { + mut socket := server.accept() ? + go handle_client(mut socket) + } +} + +fn handle_client(mut socket net.TcpConn) { + defer { + socket.close() or { panic(err) } + } + client_addr := socket.peer_addr() or { return } + eprintln('> new client: $client_addr') + mut reader := io.new_buffered_reader(reader: socket) + defer { + reader.free() + } + socket.write_string('server: hello\n') or { return } + for { + received_line := reader.read_line() or { return } + if received_line == '' { + return + } + println('client $client_addr: $received_line') + socket.write_string('server: $received_line\n') or { return } + } +} diff --git a/v_windows/v/old/examples/tcp_notify_echo_server.v b/v_windows/v/old/examples/tcp_notify_echo_server.v new file mode 100644 index 0000000..0e2d0bf --- /dev/null +++ b/v_windows/v/old/examples/tcp_notify_echo_server.v @@ -0,0 +1,73 @@ +import os +import os.notify +import net +import time + +// This example demonstrates a single threaded TCP server using os.notify to be +// notified of events on file descriptors. You can connect to the server using +// netcat in separate shells, for example: `nc localhost 9001` + +fn main() { + $if !linux { + eprintln('This example only works on Linux') + exit(1) + } + + // create TCP listener + mut listener := net.listen_tcp(.ip, 'localhost:9001') ? + defer { + listener.close() or {} + } + addr := listener.addr() ? + eprintln('Listening on $addr') + eprintln('Type `stop` to stop the server') + + // create file descriptor notifier + mut notifier := notify.new() ? + defer { + notifier.close() or {} + } + notifier.add(os.stdin().fd, .read) ? + notifier.add(listener.sock.handle, .read) ? + + for { + for event in notifier.wait(time.infinite) { + match event.fd { + listener.sock.handle { + // someone is trying to connect + eprint('trying to connect.. ') + if conn := listener.accept() { + if _ := notifier.add(conn.sock.handle, .read | .peer_hangup) { + eprintln('connected') + } else { + eprintln('error adding to notifier: $err') + } + } else { + eprintln('unable to accept: $err') + } + } + 0 { + // stdin + s, _ := os.fd_read(event.fd, 10) + if s == 'stop\n' { + eprintln('stopping') + return + } + } + else { + // remote connection + if event.kind.has(.peer_hangup) { + if _ := notifier.remove(event.fd) { + eprintln('remote disconnected') + } else { + eprintln('error removing from notifier: $err') + } + } else { + s, _ := os.fd_read(event.fd, 10) + os.fd_write(event.fd, s) + } + } + } + } + } +} diff --git a/v_windows/v/old/examples/templates/data.json b/v_windows/v/old/examples/templates/data.json new file mode 100644 index 0000000..a6765cf --- /dev/null +++ b/v_windows/v/old/examples/templates/data.json @@ -0,0 +1,197 @@ +[ + { + "name": "www_threefold_io", + "url": "https://github.com/threefoldfoundation/www_threefold_io", + "branch": "default", + "pull": false, + "cat": 2, + "alias": "tf", + "path_code": "/Users/despiegk/codewww/github/threefoldfoundation/www_threefold_io", + "domains": [ + "www.threefold.io", + "www.threefold.me" + ], + "descr": "is our entry point for everyone, redirect to the detailed websites underneith." + }, + { + "name": "www_threefold_cloud", + "url": "https://github.com/threefoldfoundation/www_threefold_cloud", + "branch": "default", + "pull": false, + "cat": 2, + "alias": "cloud", + "path_code": "/Users/despiegk/codewww/github/threefoldfoundation/www_threefold_cloud", + "domains": [ + "cloud.threefold.io", + "cloud.threefold.me" + ], + "descr": "for people looking to deploy solutions on top of a cloud, alternative to e.g. digital ocean" + }, + { + "name": "www_threefold_farming", + "url": "https://github.com/threefoldfoundation/www_threefold_farming", + "branch": "default", + "pull": false, + "cat": 2, + "alias": "farming", + "path_code": "/Users/despiegk/codewww/github/threefoldfoundation/www_threefold_farming", + "domains": [ + "farming.threefold.io", + "farming.threefold.me" + ], + "descr": "crypto & minining enthusiasts, be the internet, know about farming & tokens." + }, + { + "name": "www_threefold_twin", + "url": "https://github.com/threefoldfoundation/www_threefold_twin", + "branch": "default", + "pull": false, + "cat": 2, + "alias": "twin", + "path_code": "/Users/despiegk/codewww/github/threefoldfoundation/www_threefold_twin", + "domains": [ + "twin.threefold.io", + "twin.threefold.me" + ], + "descr": "you digital life" + }, + { + "name": "www_threefold_marketplace", + "url": "https://github.com/threefoldfoundation/www_threefold_marketplace", + "branch": "default", + "pull": false, + "cat": 2, + "alias": "marketplace", + "path_code": "/Users/despiegk/codewww/github/threefoldfoundation/www_threefold_marketplace", + "domains": [ + "now.threefold.io", + "marketplace.threefold.io", + "now.threefold.me", + "marketplace.threefold.me" + ], + "descr": "apps for community builders, runs on top of evdc" + }, + { + "name": "www_conscious_internet", + "url": "https://github.com/threefoldfoundation/www_conscious_internet", + "branch": "default", + "pull": false, + "cat": 2, + "alias": "conscious_internet", + "path_code": "/Users/despiegk/codewww/github/threefoldfoundation/www_conscious_internet", + "domains": [ + "www.consciousinternet.org", + "eco.threefold.io", + "community.threefold.io", + "eco.threefold.me", + "community.threefold.me" + ], + "descr": "community around threefold, partners, friends, ..." + }, + { + "name": "www_threefold_tech", + "url": "https://github.com/threefoldtech/www_threefold_tech", + "branch": "default", + "pull": false, + "cat": 2, + "alias": "tech", + "path_code": "/Users/despiegk/codewww/github/threefoldtech/www_threefold_tech", + "domains": [ + "www.threefold.tech" + ], + "descr": "cyberpandemic, use the tech to build your own solutions with, certification for TFGrid" + }, + { + "name": "www_examplesite", + "url": "https://github.com/threefoldfoundation/www_examplesite", + "branch": "default", + "pull": false, + "cat": 2, + "alias": "example", + "path_code": "/Users/despiegk/codewww/github/threefoldfoundation/www_examplesite", + "domains": [ + "example.threefold.io" + ], + "descr": "" + }, + { + "name": "info_threefold", + "url": "https://github.com/threefoldfoundation/info_foundation_archive", + "branch": "default", + "pull": false, + "cat": 0, + "alias": "threefold", + "path_code": "/Users/despiegk/codewww/github/threefoldfoundation/info_foundation_archive", + "domains": [ + "info.threefold.io" + ], + "descr": "wiki for foundation, collaborate, what if farmings, tokens" + }, + { + "name": "info_sdk", + "url": "https://github.com/threefoldfoundation/info_sdk", + "branch": "default", + "pull": false, + "cat": 0, + "alias": "sdk", + "path_code": "/Users/despiegk/codewww/github/threefoldfoundation/info_sdk", + "domains": [ + "sdk.threefold.io", + "sdk_info.threefold.io" + ], + "descr": "for IAC, devops, how to do Infrastruture As Code, 3bot, Ansible, tfgrid-sdk, ..." + }, + { + "name": "info_legal", + "url": "https://github.com/threefoldfoundation/info_legal", + "branch": "default", + "pull": false, + "cat": 0, + "alias": "legal", + "path_code": "/Users/despiegk/codewww/github/threefoldfoundation/info_legal", + "domains": [ + "legal.threefold.io", + "legal_info.threefold.io" + ], + "descr": "" + }, + { + "name": "info_cloud", + "url": "https://github.com/threefoldfoundation/info_cloud", + "branch": "default", + "pull": false, + "cat": 0, + "alias": "cloud", + "path_code": "/Users/despiegk/codewww/github/threefoldfoundation/info_cloud", + "domains": [ + "cloud_info.threefold.io" + ], + "descr": "how to use the cloud for deploying apps: evdc, kubernetes, planetary fs, ... + marketplace solutions " + }, + { + "name": "info_tftech", + "url": "https://github.com/threefoldtech/info_tftech", + "branch": "default", + "pull": false, + "cat": 0, + "alias": "tftech", + "path_code": "/Users/despiegk/codewww/github/threefoldtech/info_tftech", + "domains": [ + "info.threefold.tech" + ], + "descr": "" + }, + { + "name": "info_digitaltwin", + "url": "https://github.com/threefoldfoundation/info_digitaltwin.git", + "branch": "default", + "pull": false, + "cat": 0, + "alias": "twin", + "path_code": "/Users/despiegk/codewww/github/threefoldfoundation/info_digitaltwin", + "domains": [ + "twin_info.threefold.io" + ], + "descr": "" + } +] \ No newline at end of file diff --git a/v_windows/v/old/examples/templates/readme.md b/v_windows/v/old/examples/templates/readme.md new file mode 100644 index 0000000..3129068 --- /dev/null +++ b/v_windows/v/old/examples/templates/readme.md @@ -0,0 +1,14 @@ +# example how to work with templates + +- templates support + - if + - for loops + - even nesting of for loops + - syntax checking + - embed the template into the binary + +Its a very cool way how to do also do e.g. system administration, fill in +config files, etc... + +The example there is also a good demonstration of how to use json on a more +complex object and read/write to file. diff --git a/v_windows/v/old/examples/templates/template.md b/v_windows/v/old/examples/templates/template.md new file mode 100644 index 0000000..7bbdc0d --- /dev/null +++ b/v_windows/v/old/examples/templates/template.md @@ -0,0 +1,16 @@ +# WIKIs + +@for site in sites +@if site.cat == .wiki + +## @{site.domains[0]}@port_str + +- [errors]("//@{site.domains[0]}@port_str/errors") + +### domains + +@for dom in site.domains +- @dom +@end +@end +@end diff --git a/v_windows/v/old/examples/templates/templates.v b/v_windows/v/old/examples/templates/templates.v new file mode 100644 index 0000000..65ec459 --- /dev/null +++ b/v_windows/v/old/examples/templates/templates.v @@ -0,0 +1,196 @@ +module main + +import os +import json + +pub struct SiteConfig { +pub mut: + name string + url string + branch string = 'default' // means is the default branch + pull bool + cat SiteCat + alias string + path_code string + domains []string + descr string +} + +pub enum SiteCat { + wiki + data + web +} + +fn data_get() []SiteConfig { + data := [SiteConfig{ + name: 'www_threefold_io' + url: 'https://github.com/threefoldfoundation/www_threefold_io' + branch: 'default' + pull: false + cat: .web + alias: 'tf' + path_code: '/Users/despiegk/codewww/github/threefoldfoundation/www_threefold_io' + domains: ['www.threefold.io', 'www.threefold.me'] + descr: 'is our entry point for everyone, redirect to the detailed websites underneith.' + }, SiteConfig{ + name: 'www_threefold_cloud' + url: 'https://github.com/threefoldfoundation/www_threefold_cloud' + branch: 'default' + pull: false + cat: .web + alias: 'cloud' + path_code: '/Users/despiegk/codewww/github/threefoldfoundation/www_threefold_cloud' + domains: ['cloud.threefold.io', 'cloud.threefold.me'] + descr: 'for people looking to deploy solutions on top of a cloud, alternative to e.g. digital ocean' + }, SiteConfig{ + name: 'www_threefold_farming' + url: 'https://github.com/threefoldfoundation/www_threefold_farming' + branch: 'default' + pull: false + cat: .web + alias: 'farming' + path_code: '/Users/despiegk/codewww/github/threefoldfoundation/www_threefold_farming' + domains: ['farming.threefold.io', 'farming.threefold.me'] + descr: 'crypto & minining enthusiasts, be the internet, know about farming & tokens.' + }, SiteConfig{ + name: 'www_threefold_twin' + url: 'https://github.com/threefoldfoundation/www_threefold_twin' + branch: 'default' + pull: false + cat: .web + alias: 'twin' + path_code: '/Users/despiegk/codewww/github/threefoldfoundation/www_threefold_twin' + domains: ['twin.threefold.io', 'twin.threefold.me'] + descr: 'you digital life' + }, SiteConfig{ + name: 'www_threefold_marketplace' + url: 'https://github.com/threefoldfoundation/www_threefold_marketplace' + branch: 'default' + pull: false + cat: .web + alias: 'marketplace' + path_code: '/Users/despiegk/codewww/github/threefoldfoundation/www_threefold_marketplace' + domains: ['now.threefold.io', 'marketplace.threefold.io', 'now.threefold.me', + 'marketplace.threefold.me', + ] + descr: 'apps for community builders, runs on top of evdc' + }, SiteConfig{ + name: 'www_conscious_internet' + url: 'https://github.com/threefoldfoundation/www_conscious_internet' + branch: 'default' + pull: false + cat: .web + alias: 'conscious_internet' + path_code: '/Users/despiegk/codewww/github/threefoldfoundation/www_conscious_internet' + domains: ['www.consciousinternet.org', 'eco.threefold.io', 'community.threefold.io', + 'eco.threefold.me', 'community.threefold.me'] + descr: 'community around threefold, partners, friends, ...' + }, SiteConfig{ + name: 'www_threefold_tech' + url: 'https://github.com/threefoldtech/www_threefold_tech' + branch: 'default' + pull: false + cat: .web + alias: 'tech' + path_code: '/Users/despiegk/codewww/github/threefoldtech/www_threefold_tech' + domains: ['www.threefold.tech'] + descr: 'cyberpandemic, use the tech to build your own solutions with, certification for TFGrid' + }, SiteConfig{ + name: 'www_examplesite' + url: 'https://github.com/threefoldfoundation/www_examplesite' + branch: 'default' + pull: false + cat: .web + alias: 'example' + path_code: '/Users/despiegk/codewww/github/threefoldfoundation/www_examplesite' + domains: ['example.threefold.io'] + descr: '' + }, SiteConfig{ + name: 'info_threefold' + url: 'https://github.com/threefoldfoundation/info_foundation_archive' + branch: 'default' + pull: false + cat: .wiki + alias: 'threefold' + path_code: '/Users/despiegk/codewww/github/threefoldfoundation/info_foundation_archive' + domains: ['info.threefold.io'] + descr: 'wiki for foundation, collaborate, what if farmings, tokens' + }, SiteConfig{ + name: 'info_sdk' + url: 'https://github.com/threefoldfoundation/info_sdk' + branch: 'default' + pull: false + cat: .wiki + alias: 'sdk' + path_code: '/Users/despiegk/codewww/github/threefoldfoundation/info_sdk' + domains: ['sdk.threefold.io', 'sdk_info.threefold.io'] + descr: 'for IAC, devops, how to do Infrastruture As Code, 3bot, Ansible, tfgrid-sdk, ...' + }, SiteConfig{ + name: 'info_legal' + url: 'https://github.com/threefoldfoundation/info_legal' + branch: 'default' + pull: false + cat: .wiki + alias: 'legal' + path_code: '/Users/despiegk/codewww/github/threefoldfoundation/info_legal' + domains: ['legal.threefold.io', 'legal_info.threefold.io'] + descr: '' + }, SiteConfig{ + name: 'info_cloud' + url: 'https://github.com/threefoldfoundation/info_cloud' + branch: 'default' + pull: false + cat: .wiki + alias: 'cloud' + path_code: '/Users/despiegk/codewww/github/threefoldfoundation/info_cloud' + domains: ['cloud_info.threefold.io'] + descr: 'how to use the cloud for deploying apps: evdc, kubernetes, planetary fs, ... + marketplace solutions ' + }, SiteConfig{ + name: 'info_tftech' + url: 'https://github.com/threefoldtech/info_tftech' + branch: 'default' + pull: false + cat: .wiki + alias: 'tftech' + path_code: '/Users/despiegk/codewww/github/threefoldtech/info_tftech' + domains: ['info.threefold.tech'] + descr: '' + }, SiteConfig{ + name: 'info_digitaltwin' + url: 'https://github.com/threefoldfoundation/info_digitaltwin.git' + branch: 'default' + pull: false + cat: .wiki + alias: 'twin' + path_code: '/Users/despiegk/codewww/github/threefoldfoundation/info_digitaltwin' + domains: ['twin_info.threefold.io'] + descr: '' + }] + return data +} + +fn data_dump(data []SiteConfig) { + a := json.encode_pretty(data) + os.write_file(os.resource_abs_path('data.json'), a) or { panic(err) } +} + +fn data_load() []SiteConfig { + data := os.read_file(os.resource_abs_path('data.json')) or { panic(err) } + a := json.decode([]SiteConfig, data) or { panic(err) } + return a +} + +fn filled_in_template() string { + port_str := '80' + sites := data_load() + return $tmpl('template.md') +} + +fn main() { + // A NICE test how to work with the json module + // data := data_get() + // data_dump(data) + b := filled_in_template() + println(b) +} diff --git a/v_windows/v/old/examples/term.ui/README.md b/v_windows/v/old/examples/term.ui/README.md new file mode 100644 index 0000000..409a55e --- /dev/null +++ b/v_windows/v/old/examples/term.ui/README.md @@ -0,0 +1 @@ + diff --git a/v_windows/v/old/examples/term.ui/cursor_chaser.v b/v_windows/v/old/examples/term.ui/cursor_chaser.v new file mode 100644 index 0000000..dd987e6 --- /dev/null +++ b/v_windows/v/old/examples/term.ui/cursor_chaser.v @@ -0,0 +1,100 @@ +import term.ui as tui + +const ( + colors = [ + tui.Color{33, 150, 243}, + tui.Color{0, 150, 136}, + tui.Color{205, 220, 57}, + tui.Color{255, 152, 0}, + tui.Color{244, 67, 54}, + tui.Color{156, 39, 176}, + ] +) + +struct Point { + x int + y int +} + +struct App { +mut: + tui &tui.Context = 0 + points []Point + color tui.Color = colors[0] + color_idx int + cut_rate f64 = 5 +} + +fn frame(x voidptr) { + mut app := &App(x) + + app.tui.clear() + + if app.points.len > 0 { + app.tui.set_bg_color(app.color) + mut last := app.points[0] + for point in app.points { + // if the cursor moves quickly enough, different events are not + // necessarily touching, so we need to draw a line between them + app.tui.draw_line(last.x, last.y, point.x, point.y) + last = point + } + app.tui.reset() + + l := int(app.points.len / app.cut_rate) + 1 + app.points = app.points[l..].clone() + } + + ww := app.tui.window_width + + app.tui.bold() + app.tui.draw_text(ww / 6, 2, 'V term.input: cursor chaser demo') + app.tui.draw_text((ww - ww / 6) - 14, 2, 'cut rate: ${(100 / app.cut_rate):3.0f}%') + app.tui.horizontal_separator(3) + app.tui.reset() + app.tui.flush() +} + +fn event(e &tui.Event, x voidptr) { + mut app := &App(x) + + match e.typ { + .key_down { + match e.code { + .escape { + exit(0) + } + .space, .enter { + app.color_idx++ + if app.color_idx == colors.len { + app.color_idx = 0 + } + app.color = colors[app.color_idx] + } + else {} + } + } + .mouse_move, .mouse_drag, .mouse_down { + app.points << Point{e.x, e.y} + } + .mouse_scroll { + d := if e.direction == .up { 0.1 } else { -0.1 } + app.cut_rate += d + if app.cut_rate < 1 { + app.cut_rate = 1 + } + } + else {} + } +} + +fn main() { + mut app := &App{} + app.tui = tui.init( + user_data: app + frame_fn: frame + event_fn: event + hide_cursor: true + ) + app.tui.run() ? +} diff --git a/v_windows/v/old/examples/term.ui/event_viewer.v b/v_windows/v/old/examples/term.ui/event_viewer.v new file mode 100644 index 0000000..f9c443f --- /dev/null +++ b/v_windows/v/old/examples/term.ui/event_viewer.v @@ -0,0 +1,47 @@ +import term.ui as tui + +struct App { +mut: + tui &tui.Context = 0 +} + +fn event(e &tui.Event, x voidptr) { + mut app := &App(x) + app.tui.clear() + app.tui.set_cursor_position(0, 0) + app.tui.write('V term.input event viewer (press `esc` to exit)\n\n') + app.tui.write('$e') + app.tui.write('\n\nRaw event bytes: "$e.utf8.bytes().hex()" = $e.utf8.bytes()') + if !e.modifiers.is_empty() { + app.tui.write('\nModifiers: $e.modifiers = ') + if e.modifiers.has(.ctrl) { + app.tui.write('ctrl. ') + } + if e.modifiers.has(.shift) { + app.tui.write('shift ') + } + if e.modifiers.has(.alt) { + app.tui.write('alt. ') + } + } + app.tui.flush() + + if e.typ == .key_down && e.code == .escape { + exit(0) + } +} + +fn main() { + mut app := &App{} + app.tui = tui.init( + user_data: app + event_fn: event + window_title: 'V term.ui event viewer' + hide_cursor: true + capture_events: true + frame_rate: 60 + use_alternate_buffer: false + ) + println('V term.ui event viewer (press `esc` to exit)\n\n') + app.tui.run() ? +} diff --git a/v_windows/v/old/examples/term.ui/pong.v b/v_windows/v/old/examples/term.ui/pong.v new file mode 100644 index 0000000..375261f --- /dev/null +++ b/v_windows/v/old/examples/term.ui/pong.v @@ -0,0 +1,499 @@ +// Copyright (c) 2020 Lars Pontoppidan. All rights reserved. +// Use of this source code is governed by the MIT license distributed with this software. +import term +import term.ui +import time + +enum Mode { + menu + game +} + +const ( + player_one = 1 // Human control this racket + player_two = 0 // Take over this AI controller + white = ui.Color{255, 255, 255} + orange = ui.Color{255, 140, 0} +) + +[heap] +struct App { +mut: + tui &ui.Context = 0 + mode Mode = Mode.menu + width int + height int + game &Game = 0 + dt f32 + ticks i64 +} + +fn (mut a App) init() { + a.game = &Game{ + app: a + } + w, h := a.tui.window_width, a.tui.window_height + a.width = w + a.height = h + term.erase_del_clear() + term.set_cursor_position( + x: 0 + y: 0 + ) +} + +fn (mut a App) start_game() { + if a.mode != .game { + a.mode = .game + } + a.game.init() +} + +fn (mut a App) frame() { + ticks := time.ticks() + a.dt = f32(ticks - a.ticks) / 1000.0 + a.width, a.height = a.tui.window_width, a.tui.window_height + if a.mode == .game { + a.game.update() + } + a.tui.clear() + a.render() + a.tui.flush() + a.ticks = ticks +} + +fn (mut a App) quit() { + if a.mode != .menu { + a.game.quit() + return + } + term.set_cursor_position( + x: 0 + y: 0 + ) + exit(0) +} + +fn (mut a App) event(e &ui.Event) { + match e.typ { + .mouse_move { + if a.mode != .game { + return + } + // TODO mouse movement for real Pong sharks + // a.game.move_player(player_one, 0, -1) + } + .key_down { + match e.code { + .escape, .q { + a.quit() + } + .w { + if a.mode != .game { + return + } + a.game.move_player(player_one, 0, -1) + } + .a { + if a.mode != .game { + return + } + a.game.move_player(player_one, 0, -1) + } + .s { + if a.mode != .game { + return + } + a.game.move_player(player_one, 0, 1) + } + .d { + if a.mode != .game { + return + } + a.game.move_player(player_one, 0, 1) + } + .left { + if a.mode != .game { + return + } + a.game.move_player(player_two, 0, -1) + } + .right { + if a.mode != .game { + return + } + a.game.move_player(player_two, 0, 1) + } + .up { + if a.mode != .game { + return + } + a.game.move_player(player_two, 0, -1) + } + .down { + if a.mode != .game { + return + } + a.game.move_player(player_two, 0, 1) + } + .enter, .space { + if a.mode == .menu { + a.start_game() + } + } + else {} + } + } + else {} + } +} + +fn (mut a App) free() { + unsafe { + a.game.free() + free(a.game) + } +} + +fn (mut a App) render() { + match a.mode { + .menu { a.draw_menu() } + else { a.draw_game() } + } +} + +fn (mut a App) draw_menu() { + cx := int(f32(a.width) * 0.5) + y025 := int(f32(a.height) * 0.25) + y075 := int(f32(a.height) * 0.75) + cy := int(f32(a.height) * 0.5) + // + a.tui.set_color(white) + a.tui.bold() + a.tui.draw_text(cx - 2, y025, 'VONG') + a.tui.reset() + a.tui.draw_text(cx - 13, y025 + 1, '(A game of Pong written in V)') + // + a.tui.set_color(white) + a.tui.bold() + a.tui.draw_text(cx - 3, cy + 1, 'START') + a.tui.reset() + // + a.tui.draw_text(cx - 9, y075 + 1, 'Press SPACE to start') + a.tui.reset() + a.tui.draw_text(cx - 5, y075 + 3, 'ESC to Quit') + a.tui.reset() +} + +fn (mut a App) draw_game() { + a.game.draw() +} + +struct Player { +mut: + game &Game + pos Vec + racket_size int = 4 + score int + ai bool +} + +fn (mut p Player) move(x f32, y f32) { + p.pos.x += x + p.pos.y += y +} + +fn (mut p Player) update() { + if !p.ai { + return + } + if isnil(p.game) { + return + } + // dt := p.game.app.dt + ball := unsafe { &p.game.ball } + // Evil AI that eventually will take over the world + p.pos.y = ball.pos.y - int(f32(p.racket_size) * 0.5) +} + +struct Vec { +mut: + x f32 + y f32 +} + +fn (mut v Vec) set(x f32, y f32) { + v.x = x + v.y = y +} + +struct Ball { +mut: + pos Vec + vel Vec + acc Vec +} + +fn (mut b Ball) update(dt f32) { + b.pos.x += b.vel.x * b.acc.x * dt + b.pos.y += b.vel.y * b.acc.y * dt +} + +[heap] +struct Game { +mut: + app &App = 0 + players []Player + ball Ball +} + +fn (mut g Game) move_player(id int, x int, y int) { + mut p := unsafe { &g.players[id] } + if p.ai { // disable AI when moved + p.ai = false + } + p.move(x, y) +} + +fn (mut g Game) init() { + if g.players.len == 0 { + g.players = []Player{len: 2, init: Player{ // <- BUG omitting the init will result in smaller racket sizes??? + game: g + }} + } + g.reset() +} + +fn (mut g Game) reset() { + mut i := 0 + for mut p in g.players { + p.score = 0 + if i != player_one { + p.ai = true + } + i++ + } + g.new_round() +} + +fn (mut g Game) new_round() { + mut i := 0 + for mut p in g.players { + p.pos.x = if i == 0 { 3 } else { g.app.width - 2 } + p.pos.y = f32(g.app.height) * 0.5 - f32(p.racket_size) * 0.5 + i++ + } + g.ball.pos.set(f32(g.app.width) * 0.5, f32(g.app.height) * 0.5) + g.ball.vel.set(-8, -15) + g.ball.acc.set(2.0, 1.0) +} + +fn (mut g Game) update() { + dt := g.app.dt + mut b := unsafe { &g.ball } + for mut p in g.players { + p.update() + // Keep rackets within the game area + if p.pos.y <= 0 { + p.pos.y = 1 + } + if p.pos.y + p.racket_size >= g.app.height { + p.pos.y = g.app.height - p.racket_size - 1 + } + // Check ball collision + // Player left side + if p.pos.x < f32(g.app.width) * 0.5 { + // Racket collision + if b.pos.x <= p.pos.x + 1 { + if b.pos.y >= p.pos.y && b.pos.y <= p.pos.y + p.racket_size { + b.vel.x *= -1 + } + } + // Behind racket + if b.pos.x < p.pos.x { + g.players[1].score++ + g.new_round() + } + } else { + // Player right side + if b.pos.x >= p.pos.x - 1 { + if b.pos.y >= p.pos.y && b.pos.y <= p.pos.y + p.racket_size { + b.vel.x *= -1 + } + } + if b.pos.x > p.pos.x { + g.players[0].score++ + g.new_round() + } + } + } + if b.pos.x <= 1 || b.pos.x >= g.app.width { + b.vel.x *= -1 + } + if b.pos.y <= 2 || b.pos.y >= g.app.height { + b.vel.y *= -1 + } + b.update(dt) +} + +fn (mut g Game) quit() { + if g.app.mode != .game { + return + } + g.app.mode = .menu +} + +fn (mut g Game) draw_big_digit(px f32, py f32, digit int) { + // TODO use draw_line or draw_point to fix tearing with non-monospaced terminal fonts + mut gfx := g.app.tui + x, y := int(px), int(py) + match digit { + 0 { + gfx.draw_text(x, y + 0, '█████') + gfx.draw_text(x, y + 1, '█ █') + gfx.draw_text(x, y + 2, '█ █') + gfx.draw_text(x, y + 3, '█ █') + gfx.draw_text(x, y + 4, '█████') + } + 1 { + gfx.draw_text(x + 3, y + 0, '█') + gfx.draw_text(x + 3, y + 1, '█') + gfx.draw_text(x + 3, y + 2, '█') + gfx.draw_text(x + 3, y + 3, '█') + gfx.draw_text(x + 3, y + 4, '█') + } + 2 { + gfx.draw_text(x, y + 0, '█████') + gfx.draw_text(x, y + 1, ' █') + gfx.draw_text(x, y + 2, '█████') + gfx.draw_text(x, y + 3, '█') + gfx.draw_text(x, y + 4, '█████') + } + 3 { + gfx.draw_text(x, y + 0, '█████') + gfx.draw_text(x, y + 1, ' ██') + gfx.draw_text(x, y + 2, ' ████') + gfx.draw_text(x, y + 3, ' ██') + gfx.draw_text(x, y + 4, '█████') + } + 4 { + gfx.draw_text(x, y + 0, '█ █') + gfx.draw_text(x, y + 1, '█ █') + gfx.draw_text(x, y + 2, '█████') + gfx.draw_text(x, y + 3, ' █') + gfx.draw_text(x, y + 4, ' █') + } + 5 { + gfx.draw_text(x, y + 0, '█████') + gfx.draw_text(x, y + 1, '█') + gfx.draw_text(x, y + 2, '█████') + gfx.draw_text(x, y + 3, ' █') + gfx.draw_text(x, y + 4, '█████') + } + 6 { + gfx.draw_text(x, y + 0, '█████') + gfx.draw_text(x, y + 1, '█') + gfx.draw_text(x, y + 2, '█████') + gfx.draw_text(x, y + 3, '█ █') + gfx.draw_text(x, y + 4, '█████') + } + 7 { + gfx.draw_text(x, y + 0, '█████') + gfx.draw_text(x, y + 1, ' █') + gfx.draw_text(x, y + 2, ' █') + gfx.draw_text(x, y + 3, ' █') + gfx.draw_text(x, y + 4, ' █') + } + 8 { + gfx.draw_text(x, y + 0, '█████') + gfx.draw_text(x, y + 1, '█ █') + gfx.draw_text(x, y + 2, '█████') + gfx.draw_text(x, y + 3, '█ █') + gfx.draw_text(x, y + 4, '█████') + } + 9 { + gfx.draw_text(x, y + 0, '█████') + gfx.draw_text(x, y + 1, '█ █') + gfx.draw_text(x, y + 2, '█████') + gfx.draw_text(x, y + 3, ' █') + gfx.draw_text(x, y + 4, '█████') + } + else {} + } +} + +fn (mut g Game) draw() { + mut gfx := g.app.tui + gfx.set_bg_color(white) + // Border + gfx.draw_empty_rect(1, 1, g.app.width, g.app.height) + // Center line + gfx.draw_dashed_line(int(f32(g.app.width) * 0.5), 0, int(f32(g.app.width) * 0.5), + int(g.app.height)) + border := 1 + mut y, mut x := 0, 0 + for p in g.players { + x = int(p.pos.x) + y = int(p.pos.y) + gfx.reset_bg_color() + gfx.set_color(white) + if x < f32(g.app.width) * 0.5 { + g.draw_big_digit(f32(g.app.width) * 0.25, 3, p.score) + } else { + g.draw_big_digit(f32(g.app.width) * 0.75, 3, p.score) + } + gfx.reset_color() + gfx.set_bg_color(white) + // Racket + gfx.draw_line(x, y + border, x, y + p.racket_size) + } + // Ball + gfx.draw_point(int(g.ball.pos.x), int(g.ball.pos.y)) + // gfx.draw_text(22,2,'$g.ball.pos') + gfx.reset_bg_color() +} + +fn (mut g Game) free() { + g.players.clear() +} + +// TODO Remove these wrapper functions when we can assign methods as callbacks +fn init(x voidptr) { + mut app := &App(x) + app.init() +} + +fn frame(x voidptr) { + mut app := &App(x) + app.frame() +} + +fn cleanup(x voidptr) { + mut app := &App(x) + app.free() +} + +fn fail(error string) { + eprintln(error) +} + +fn event(e &ui.Event, x voidptr) { + mut app := &App(x) + app.event(e) +} + +fn main() { + mut app := &App{} + app.tui = ui.init( + user_data: app + init_fn: init + frame_fn: frame + cleanup_fn: cleanup + event_fn: event + fail_fn: fail + capture_events: true + hide_cursor: true + frame_rate: 60 + ) + app.tui.run() ? +} diff --git a/v_windows/v/old/examples/term.ui/rectangles.v b/v_windows/v/old/examples/term.ui/rectangles.v new file mode 100644 index 0000000..09e9d6a --- /dev/null +++ b/v_windows/v/old/examples/term.ui/rectangles.v @@ -0,0 +1,97 @@ +import term.ui as tui +import rand + +struct Rect { +mut: + c tui.Color + x int + y int + x2 int + y2 int +} + +struct App { +mut: + tui &tui.Context = 0 + rects []Rect + cur_rect Rect + is_drag bool + redraw bool +} + +fn random_color() tui.Color { + return tui.Color{ + r: byte(rand.intn(256)) + g: byte(rand.intn(256)) + b: byte(rand.intn(256)) + } +} + +fn event(e &tui.Event, x voidptr) { + mut app := &App(x) + match e.typ { + .mouse_down { + app.is_drag = true + app.cur_rect = Rect{ + c: random_color() + x: e.x + y: e.y + x2: e.x + y2: e.y + } + } + .mouse_drag { + app.cur_rect.x2 = e.x + app.cur_rect.y2 = e.y + } + .mouse_up { + app.rects << app.cur_rect + app.is_drag = false + } + .key_down { + if e.code == .c { + app.rects.clear() + } else if e.code == .escape { + exit(0) + } + } + else {} + } + app.redraw = true +} + +fn frame(x voidptr) { + mut app := &App(x) + if !app.redraw { + return + } + + app.tui.clear() + + for rect in app.rects { + app.tui.set_bg_color(rect.c) + app.tui.draw_rect(rect.x, rect.y, rect.x2, rect.y2) + } + + if app.is_drag { + r := app.cur_rect + app.tui.set_bg_color(r.c) + app.tui.draw_empty_rect(r.x, r.y, r.x2, r.y2) + } + + app.tui.reset_bg_color() + app.tui.flush() + app.redraw = false +} + +fn main() { + mut app := &App{} + app.tui = tui.init( + user_data: app + event_fn: event + frame_fn: frame + hide_cursor: true + frame_rate: 60 + ) + app.tui.run() ? +} diff --git a/v_windows/v/old/examples/term.ui/screenshot_pong.png b/v_windows/v/old/examples/term.ui/screenshot_pong.png new file mode 100644 index 0000000..9127d7b Binary files /dev/null and b/v_windows/v/old/examples/term.ui/screenshot_pong.png differ diff --git a/v_windows/v/old/examples/term.ui/term_drawing.v b/v_windows/v/old/examples/term.ui/term_drawing.v new file mode 100644 index 0000000..df08cb7 --- /dev/null +++ b/v_windows/v/old/examples/term.ui/term_drawing.v @@ -0,0 +1,510 @@ +// Copyright (c) 2020 Raúl Hernández. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module main + +import term.ui + +// The color palette, taken from Google's Material design +const ( + colors = [ + [ + ui.Color{239, 154, 154}, + ui.Color{244, 143, 177}, + ui.Color{206, 147, 216}, + ui.Color{179, 157, 219}, + ui.Color{159, 168, 218}, + ui.Color{144, 202, 249}, + ui.Color{129, 212, 250}, + ui.Color{128, 222, 234}, + ui.Color{128, 203, 196}, + ui.Color{165, 214, 167}, + ui.Color{197, 225, 165}, + ui.Color{230, 238, 156}, + ui.Color{255, 245, 157}, + ui.Color{255, 224, 130}, + ui.Color{255, 204, 128}, + ui.Color{255, 171, 145}, + ui.Color{188, 170, 164}, + ui.Color{238, 238, 238}, + ui.Color{176, 190, 197}, + ], + [ + ui.Color{244, 67, 54}, + ui.Color{233, 30, 99}, + ui.Color{156, 39, 176}, + ui.Color{103, 58, 183}, + ui.Color{63, 81, 181}, + ui.Color{33, 150, 243}, + ui.Color{3, 169, 244}, + ui.Color{0, 188, 212}, + ui.Color{0, 150, 136}, + ui.Color{76, 175, 80}, + ui.Color{139, 195, 74}, + ui.Color{205, 220, 57}, + ui.Color{255, 235, 59}, + ui.Color{255, 193, 7}, + ui.Color{255, 152, 0}, + ui.Color{255, 87, 34}, + ui.Color{121, 85, 72}, + ui.Color{120, 120, 120}, + ui.Color{96, 125, 139}, + ], + [ + ui.Color{198, 40, 40}, + ui.Color{173, 20, 87}, + ui.Color{106, 27, 154}, + ui.Color{69, 39, 160}, + ui.Color{40, 53, 147}, + ui.Color{21, 101, 192}, + ui.Color{2, 119, 189}, + ui.Color{0, 131, 143}, + ui.Color{0, 105, 92}, + ui.Color{46, 125, 50}, + ui.Color{85, 139, 47}, + ui.Color{158, 157, 36}, + ui.Color{249, 168, 37}, + ui.Color{255, 143, 0}, + ui.Color{239, 108, 0}, + ui.Color{216, 67, 21}, + ui.Color{78, 52, 46}, + ui.Color{33, 33, 33}, + ui.Color{55, 71, 79}, + ], + ] +) + +const ( + frame_rate = 30 // fps + msg_display_time = 5 * frame_rate + w = 200 + h = 100 + space = ' ' + spaces = ' ' + select_color = 'Select color: ' + select_size = 'Size: + -' + help_1 = '╭────────╮' + help_2 = '│ HELP │' + help_3 = '╰────────╯' +) + +struct App { +mut: + ui &ui.Context = 0 + header_text []string + mouse_pos Point + msg string + msg_hide_tick int + primary_color ui.Color = colors[1][6] + secondary_color ui.Color = colors[1][9] + primary_color_idx int = 25 + secondary_color_idx int = 28 + bg_color ui.Color = ui.Color{0, 0, 0} + drawing [][]ui.Color = [][]ui.Color{len: h, init: []ui.Color{len: w}} + size int = 1 + should_redraw bool = true + is_dragging bool +} + +struct Point { +mut: + x int + y int +} + +fn main() { + mut app := &App{} + app.ui = ui.init( + user_data: app + frame_fn: frame + event_fn: event + frame_rate: frame_rate + hide_cursor: true + window_title: 'V terminal pixelart drawing app' + ) + app.mouse_pos.x = 40 + app.mouse_pos.y = 15 + app.ui.clear() + app.ui.run() ? +} + +fn frame(x voidptr) { + mut app := &App(x) + mut redraw := app.should_redraw + if app.msg != '' && app.ui.frame_count >= app.msg_hide_tick { + app.msg = '' + redraw = true + } + if redraw { + app.render(false) + app.should_redraw = false + } +} + +fn event(event &ui.Event, x voidptr) { + mut app := &App(x) + match event.typ { + .mouse_down { + app.is_dragging = true + if app.ui.window_height - event.y < 5 { + app.footer_click(event) + } else { + app.paint(event) + } + } + .mouse_up { + app.is_dragging = false + } + .mouse_drag { + app.mouse_pos = Point{ + x: event.x + y: event.y + } + app.paint(event) + } + .mouse_move { + app.mouse_pos = Point{ + x: event.x + y: event.y + } + } + .mouse_scroll { + app.mouse_pos = Point{ + x: event.x + y: event.y + } + d := event.direction == .down + if event.modifiers.has(.ctrl) { + p := !event.modifiers.has(.shift) + c := if d { + if p { app.primary_color_idx - 1 } else { app.secondary_color_idx - 1 } + } else { + if p { app.primary_color_idx + 1 } else { app.secondary_color_idx + 1 } + } + app.select_color(p, c) + } else { + if d { + app.inc_size() + } else { + app.dec_size() + } + } + } + .key_down { + match event.code { + .f1, ._1 { + oevent := *event + nevent := ui.Event{ + ...oevent + button: ui.MouseButton.left + x: app.mouse_pos.x + y: app.mouse_pos.y + } + app.paint(nevent) + } + .f2, ._2 { + oevent := *event + nevent := ui.Event{ + ...oevent + button: ui.MouseButton.right + x: app.mouse_pos.x + y: app.mouse_pos.y + } + app.paint(nevent) + } + .space, .enter { + oevent := *event + nevent := ui.Event{ + ...oevent + button: .left + x: app.mouse_pos.x + y: app.mouse_pos.y + } + app.paint(nevent) + } + .delete, .backspace { + oevent := *event + nevent := ui.Event{ + ...oevent + button: .middle + x: app.mouse_pos.x + y: app.mouse_pos.y + } + app.paint(nevent) + } + .j, .down { + if event.modifiers.has(.shift) { + app.set_pixel((1 + app.mouse_pos.x) / 2, app.mouse_pos.y, app.primary_color) + } + app.mouse_pos.y++ + } + .k, .up { + if event.modifiers.has(.shift) { + app.set_pixel((1 + app.mouse_pos.x) / 2, app.mouse_pos.y, app.primary_color) + } + app.mouse_pos.y-- + } + .h, .left { + if event.modifiers.has(.shift) { + app.set_pixel((1 + app.mouse_pos.x) / 2, app.mouse_pos.y, app.primary_color) + } + app.mouse_pos.x -= 2 + } + .l, .right { + if event.modifiers.has(.shift) { + app.set_pixel((1 + app.mouse_pos.x) / 2, app.mouse_pos.y, app.primary_color) + } + app.mouse_pos.x += 2 + } + .t { + p := !event.modifiers.has(.alt) + c := if event.modifiers.has(.shift) { + if p { app.primary_color_idx - 19 } else { app.secondary_color_idx - 19 } + } else { + if p { app.primary_color_idx + 19 } else { app.secondary_color_idx + 19 } + } + app.select_color(p, c) + } + .r { + p := !event.modifiers.has(.alt) + c := if event.modifiers.has(.shift) { + if p { app.primary_color_idx - 1 } else { app.secondary_color_idx - 1 } + } else { + if p { app.primary_color_idx + 1 } else { app.secondary_color_idx + 1 } + } + app.select_color(p, c) + } + .plus { + app.inc_size() + } + .minus { + app.dec_size() + } + .c { + app.drawing = [][]ui.Color{len: h, init: []ui.Color{len: w}} + } + .q, .escape { + app.render(true) + exit(0) + } + else {} + } + } + else {} + } + app.should_redraw = true +} + +fn (mut app App) render(paint_only bool) { + app.ui.clear() + app.draw_header() + app.draw_content() + if !paint_only { + app.draw_footer() + app.draw_cursor() + } + app.ui.flush() +} + +fn (mut app App) select_color(primary bool, idx int) { + c := (idx + 57) % 57 + cx := c % 19 + cy := (c / 19) % 3 + color := colors[cy][cx] + if primary { + app.primary_color_idx = c % (19 * 3) + app.primary_color = color + } else { + app.secondary_color_idx = c % (19 * 3) + app.secondary_color = color + } + c_str := if primary { 'primary' } else { 'secondary' } + app.show_msg('set $c_str color idx: $idx', 1) +} + +fn (mut app App) set_pixel(x_ int, y_ int, c ui.Color) { + // Term coords start at 1, and adjust for the header + x, y := x_ - 1, y_ - 4 + if y < 0 || app.ui.window_height - y < 3 { + return + } + if y >= app.drawing.len || x < 0 || x >= app.drawing[0].len { + return + } + app.drawing[y][x] = c +} + +fn (mut app App) paint(event &ui.Event) { + if event.y < 4 || app.ui.window_height - event.y < 4 { + return + } + x_start, y_start := int(f32((event.x - 1) / 2) - app.size / 2 + 1), event.y - app.size / 2 + color := match event.button { + .left { app.primary_color } + .right { app.secondary_color } + else { app.bg_color } + } + for x in x_start .. x_start + app.size { + for y in y_start .. y_start + app.size { + app.set_pixel(x, y, color) + } + } +} + +fn (mut app App) draw_content() { + w_, mut h_ := app.ui.window_width / 2, app.ui.window_height - 8 + if h_ > app.drawing.len { + h_ = app.drawing.len + } + for row_idx, row in app.drawing[..h_] { + app.ui.set_cursor_position(0, row_idx + 4) + mut last := ui.Color{0, 0, 0} + for cell in row[..w_] { + if cell.r == 0 && cell.g == 0 && cell.b == 0 { + if !(cell.r == last.r && cell.g == last.g && cell.b == last.b) { + app.ui.reset() + } + } else { + if !(cell.r == last.r && cell.g == last.g && cell.b == last.b) { + app.ui.set_bg_color(cell) + } + } + app.ui.write(spaces) + last = cell + } + app.ui.reset() + } +} + +fn (mut app App) draw_cursor() { + if app.mouse_pos.y in [3, app.ui.window_height - 5] { + // inside the horizontal separators + return + } + cursor_color := if app.is_dragging { ui.Color{220, 220, 220} } else { ui.Color{160, 160, 160} } + app.ui.set_bg_color(cursor_color) + if app.mouse_pos.y >= 3 && app.mouse_pos.y <= app.ui.window_height - 4 { + // inside the main content + mut x_start := int(f32((app.mouse_pos.x - 1) / 2) - app.size / 2 + 1) * 2 - 1 + mut y_start := app.mouse_pos.y - app.size / 2 + mut x_end := x_start + app.size * 2 - 1 + mut y_end := y_start + app.size - 1 + if x_start < 1 { + x_start = 1 + } + if y_start < 4 { + y_start = 4 + } + if x_end > app.ui.window_width { + x_end = app.ui.window_width + } + if y_end > app.ui.window_height - 5 { + y_end = app.ui.window_height - 5 + } + app.ui.draw_rect(x_start, y_start, x_end, y_end) + } else { + app.ui.draw_text(app.mouse_pos.x, app.mouse_pos.y, space) + } + app.ui.reset() +} + +fn (mut app App) draw_header() { + if app.msg != '' { + app.ui.set_color( + r: 0 + g: 0 + b: 0 + ) + app.ui.set_bg_color( + r: 220 + g: 220 + b: 220 + ) + app.ui.draw_text(0, 0, ' $app.msg ') + app.ui.reset() + } + //'tick: $app.ui.frame_count | ' + + app.ui.draw_text(3, 2, 'terminal size: ($app.ui.window_width, $app.ui.window_height) | primary color: $app.primary_color.hex() | secondary color: $app.secondary_color.hex()') + app.ui.horizontal_separator(3) +} + +fn (mut app App) draw_footer() { + _, wh := app.ui.window_width, app.ui.window_height + app.ui.horizontal_separator(wh - 4) + for i, color_row in colors { + for j, color in color_row { + x := j * 3 + 19 + y := wh - 3 + i + app.ui.set_bg_color(color) + if app.primary_color_idx == j + (i * 19) { + app.ui.set_color(r: 0, g: 0, b: 0) + app.ui.draw_text(x, y, '><') + app.ui.reset_color() + } else if app.secondary_color_idx == j + (i * 19) { + app.ui.set_color(r: 255, g: 255, b: 255) + app.ui.draw_text(x, y, '><') + app.ui.reset_color() + } else { + app.ui.draw_rect(x, y, x + 1, y) + } + } + } + app.ui.reset_bg_color() + app.ui.draw_text(3, wh - 3, select_color) + app.ui.bold() + app.ui.draw_text(3, wh - 1, '$select_size $app.size') + app.ui.reset() + + // TODO: help button + // if ww >= 90 { + // app.ui.draw_text(80, wh - 3, help_1) + // app.ui.draw_text(80, wh - 2, help_2) + // app.ui.draw_text(80, wh - 1, help_3) + // } +} + +[inline] +fn (mut app App) inc_size() { + if app.size < 30 { + app.size++ + } + app.show_msg('inc. size: $app.size', 1) +} + +[inline] +fn (mut app App) dec_size() { + if app.size > 1 { + app.size-- + } + app.show_msg('dec. size: $app.size', 1) +} + +fn (mut app App) footer_click(event &ui.Event) { + footer_y := 3 - (app.ui.window_height - event.y) + match event.x { + 8...11 { + app.inc_size() + } + 12...15 { + app.dec_size() + } + 18...75 { + if (event.x % 3) == 0 { + // Inside the gap between tiles + return + } + idx := footer_y * 19 - 6 + event.x / 3 + if idx < 0 || idx > 56 { + return + } + app.select_color(event.button == .left, idx) + } + else {} + } +} + +fn (mut app App) show_msg(text string, time int) { + frames := time * frame_rate + app.msg_hide_tick = if time > 0 { int(app.ui.frame_count) + frames } else { -1 } + app.msg = text +} diff --git a/v_windows/v/old/examples/term.ui/text_editor.v b/v_windows/v/old/examples/term.ui/text_editor.v new file mode 100644 index 0000000..78e6e40 --- /dev/null +++ b/v_windows/v/old/examples/term.ui/text_editor.v @@ -0,0 +1,583 @@ +// Copyright (c) 2020 Lars Pontoppidan. All rights reserved. +// Use of this source code is governed by the MIT license distributed with this software. +// Don't use this editor for any serious work. +// A lot of funtionality is missing compared to your favourite editor :) +import strings +import math.mathutil as mu +import os +import term.ui as tui + +enum Movement { + up + down + left + right + home + end + page_up + page_down +} + +struct View { +pub: + raw string + cursor Cursor +} + +struct App { +mut: + tui &tui.Context = 0 + ed &Buffer = 0 + current_file int + files []string + status string + t int + magnet_x int + footer_height int = 2 + viewport int +} + +fn (mut a App) set_status(msg string, duration_ms int) { + a.status = msg + a.t = duration_ms +} + +fn (mut a App) save() { + if a.cfile().len > 0 { + b := a.ed + os.write_file(a.cfile(), b.raw()) or { panic(err) } + a.set_status('Saved', 2000) + } else { + a.set_status('No file loaded', 4000) + } +} + +fn (mut a App) cfile() string { + if a.files.len == 0 { + return '' + } + if a.current_file >= a.files.len { + return '' + } + return a.files[a.current_file] +} + +fn (mut a App) visit_prev_file() { + if a.files.len == 0 { + a.current_file = 0 + } else { + a.current_file = (a.current_file + a.files.len - 1) % a.files.len + } + a.init_file() +} + +fn (mut a App) visit_next_file() { + if a.files.len == 0 { + a.current_file = 0 + } else { + a.current_file = (a.current_file + a.files.len + 1) % a.files.len + } + a.init_file() +} + +fn (mut a App) footer() { + w, h := a.tui.window_width, a.tui.window_height + mut b := a.ed + // flat := b.flat() + // snip := if flat.len > 19 { flat[..20] } else { flat } + finfo := if a.cfile().len > 0 { ' (' + os.file_name(a.cfile()) + ')' } else { '' } + mut status := a.status + a.tui.draw_text(0, h - 1, '─'.repeat(w)) + footer := '$finfo Line ${b.cursor.pos_y + 1:4}/${b.lines.len:-4}, Column ${b.cursor.pos_x + 1:3}/${b.cur_line().len:-3} index: ${b.cursor_index():5} (ESC = quit, Ctrl+s = save)' + if footer.len < w { + a.tui.draw_text((w - footer.len) / 2, h, footer) + } else if footer.len == w { + a.tui.draw_text(0, h, footer) + } else { + a.tui.draw_text(0, h, footer[..w]) + } + if a.t <= 0 { + status = '' + } else { + a.tui.set_bg_color( + r: 200 + g: 200 + b: 200 + ) + a.tui.set_color( + r: 0 + g: 0 + b: 0 + ) + a.tui.draw_text((w + 4 - status.len) / 2, h - 1, ' $status ') + a.tui.reset() + a.t -= 33 + } +} + +struct Buffer { + tab_width int = 4 +pub mut: + lines []string + cursor Cursor +} + +fn (b Buffer) flat() string { + return b.raw().replace_each(['\n', r'\n', '\t', r'\t']) +} + +fn (b Buffer) raw() string { + return b.lines.join('\n') +} + +fn (b Buffer) view(from int, to int) View { + l := b.cur_line() + mut x := 0 + for i := 0; i < b.cursor.pos_x && i < l.len; i++ { + if l[i] == `\t` { + x += b.tab_width + continue + } + x++ + } + mut lines := []string{} + for i, line in b.lines { + if i >= from && i <= to { + lines << line + } + } + raw := lines.join('\n') + return View{ + raw: raw.replace('\t', strings.repeat(` `, b.tab_width)) + cursor: Cursor{ + pos_x: x + pos_y: b.cursor.pos_y + } + } +} + +fn (b Buffer) line(i int) string { + if i < 0 || i >= b.lines.len { + return '' + } + return b.lines[i] +} + +fn (b Buffer) cur_line() string { + return b.line(b.cursor.pos_y) +} + +fn (b Buffer) cursor_index() int { + mut i := 0 + for y, line in b.lines { + if b.cursor.pos_y == y { + i += b.cursor.pos_x + break + } + i += line.len + 1 + } + return i +} + +fn (mut b Buffer) put(s string) { + has_line_ending := s.contains('\n') + x, y := b.cursor.xy() + if b.lines.len == 0 { + b.lines.prepend('') + } + line := b.lines[y] + l, r := line[..x], line[x..] + if has_line_ending { + mut lines := s.split('\n') + lines[0] = l + lines[0] + lines[lines.len - 1] += r + b.lines.delete(y) + b.lines.insert(y, lines) + last := lines[lines.len - 1] + b.cursor.set(last.len, y + lines.len - 1) + if s == '\n' { + b.cursor.set(0, b.cursor.pos_y) + } + } else { + b.lines[y] = l + s + r + b.cursor.set(x + s.len, y) + } + $if debug { + flat := s.replace('\n', r'\n') + eprintln(@MOD + '.' + @STRUCT + '::' + @FN + ' "$flat"') + } +} + +fn (mut b Buffer) del(amount int) string { + if amount == 0 { + return '' + } + x, y := b.cursor.xy() + if amount < 0 { // don't delete left if we're at 0,0 + if x == 0 && y == 0 { + return '' + } + } else if x >= b.cur_line().len && y >= b.lines.len - 1 { + return '' + } + mut removed := '' + if amount < 0 { // backspace (backward) + i := b.cursor_index() + removed = b.raw()[i + amount..i] + mut left := amount * -1 + for li := y; li >= 0 && left > 0; li-- { + ln := b.lines[li] + if left > ln.len { + b.lines.delete(li) + if ln.len == 0 { // line break delimiter + left-- + if y == 0 { + return '' + } + line_above := b.lines[li - 1] + b.cursor.pos_x = line_above.len + } else { + left -= ln.len + } + b.cursor.pos_y-- + } else { + if x == 0 { + if y == 0 { + return '' + } + line_above := b.lines[li - 1] + if ln.len == 0 { // at line break + b.lines.delete(li) + b.cursor.pos_y-- + b.cursor.pos_x = line_above.len + } else { + b.lines[li - 1] = line_above + ln + b.lines.delete(li) + b.cursor.pos_y-- + b.cursor.pos_x = line_above.len + } + } else if x == 1 { + b.lines[li] = b.lines[li][left..] + b.cursor.pos_x = 0 + } else { + b.lines[li] = ln[..x - left] + ln[x..] + b.cursor.pos_x -= left + } + left = 0 + break + } + } + } else { // delete (forward) + i := b.cursor_index() + 1 + removed = b.raw()[i - amount..i] + mut left := amount + for li := y; li >= 0 && left > 0; li++ { + ln := b.lines[li] + if x == ln.len { // at line end + if y + 1 <= b.lines.len { + b.lines[li] = ln + b.lines[y + 1] + b.lines.delete(y + 1) + left-- + b.del(left) + } + } else if left > ln.len { + b.lines.delete(li) + left -= ln.len + } else { + b.lines[li] = ln[..x] + ln[x + left..] + left = 0 + } + } + } + $if debug { + flat := removed.replace('\n', r'\n') + eprintln(@MOD + '.' + @STRUCT + '::' + @FN + ' "$flat"') + } + return removed +} + +fn (mut b Buffer) free() { + $if debug { + eprintln(@MOD + '.' + @STRUCT + '::' + @FN) + } + for line in b.lines { + unsafe { line.free() } + } + unsafe { b.lines.free() } +} + +fn (mut b Buffer) move_updown(amount int) { + b.cursor.move(0, amount) + // Check the move + line := b.cur_line() + if b.cursor.pos_x > line.len { + b.cursor.set(line.len, b.cursor.pos_y) + } +} + +// move_cursor will navigate the cursor within the buffer bounds +fn (mut b Buffer) move_cursor(amount int, movement Movement) { + cur_line := b.cur_line() + match movement { + .up { + if b.cursor.pos_y - amount >= 0 { + b.move_updown(-amount) + } + } + .down { + if b.cursor.pos_y + amount < b.lines.len { + b.move_updown(amount) + } + } + .page_up { + dlines := mu.min(b.cursor.pos_y, amount) + b.move_updown(-dlines) + } + .page_down { + dlines := mu.min(b.lines.len - 1, b.cursor.pos_y + amount) - b.cursor.pos_y + b.move_updown(dlines) + } + .left { + if b.cursor.pos_x - amount >= 0 { + b.cursor.move(-amount, 0) + } else if b.cursor.pos_y > 0 { + b.cursor.set(b.line(b.cursor.pos_y - 1).len, b.cursor.pos_y - 1) + } + } + .right { + if b.cursor.pos_x + amount <= cur_line.len { + b.cursor.move(amount, 0) + } else if b.cursor.pos_y + 1 < b.lines.len { + b.cursor.set(0, b.cursor.pos_y + 1) + } + } + .home { + b.cursor.set(0, b.cursor.pos_y) + } + .end { + b.cursor.set(cur_line.len, b.cursor.pos_y) + } + } +} + +fn (mut b Buffer) move_to_word(movement Movement) { + a := if movement == .left { -1 } else { 1 } + mut line := b.cur_line() + mut x, mut y := b.cursor.pos_x, b.cursor.pos_y + if x + a < 0 && y > 0 { + y-- + line = b.line(b.cursor.pos_y - 1) + x = line.len + } else if x + a >= line.len && y + 1 < b.lines.len { + y++ + line = b.line(b.cursor.pos_y + 1) + x = 0 + } + // first, move past all non-`a-zA-Z0-9_` characters + for x + a >= 0 && x + a < line.len && !(line[x + a].is_letter() + || line[x + a].is_digit() || line[x + a] == `_`) { + x += a + } + // then, move past all the letters and numbers + for x + a >= 0 && x + a < line.len && (line[x + a].is_letter() + || line[x + a].is_digit() || line[x + a] == `_`) { + x += a + } + // if the cursor is out of bounds, move it to the next/previous line + if x + a >= 0 && x + a <= line.len { + x += a + } else if a < 0 && y + 1 > b.lines.len && y - 1 >= 0 { + y += a + x = 0 + } + b.cursor.set(x, y) +} + +struct Cursor { +pub mut: + pos_x int + pos_y int +} + +fn (mut c Cursor) set(x int, y int) { + c.pos_x = x + c.pos_y = y +} + +fn (mut c Cursor) move(x int, y int) { + c.pos_x += x + c.pos_y += y +} + +fn (c Cursor) xy() (int, int) { + return c.pos_x, c.pos_y +} + +// App callbacks +fn init(x voidptr) { + mut a := &App(x) + a.init_file() +} + +fn (mut a App) init_file() { + a.ed = &Buffer{} + mut init_y := 0 + mut init_x := 0 + if a.files.len > 0 && a.current_file < a.files.len && a.files[a.current_file].len > 0 { + if !os.is_file(a.files[a.current_file]) && a.files[a.current_file].contains(':') { + // support the file:line:col: format + fparts := a.files[a.current_file].split(':') + if fparts.len > 0 { + a.files[a.current_file] = fparts[0] + } + if fparts.len > 1 { + init_y = fparts[1].int() - 1 + } + if fparts.len > 2 { + init_x = fparts[2].int() - 1 + } + } + if os.is_file(a.files[a.current_file]) { + // 'vico: ' + + a.tui.set_window_title(a.files[a.current_file]) + mut b := a.ed + content := os.read_file(a.files[a.current_file]) or { panic(err) } + b.put(content) + a.ed.cursor.pos_x = init_x + a.ed.cursor.pos_y = init_y + } + } +} + +fn (a &App) view_height() int { + return a.tui.window_height - a.footer_height - 1 +} + +// magnet_cursor_x will place the cursor as close to it's last move left or right as possible +fn (mut a App) magnet_cursor_x() { + mut buffer := a.ed + if buffer.cursor.pos_x < a.magnet_x { + if a.magnet_x < buffer.cur_line().len { + move_x := a.magnet_x - buffer.cursor.pos_x + buffer.move_cursor(move_x, .right) + } + } +} + +fn frame(x voidptr) { + mut a := &App(x) + mut ed := a.ed + a.tui.clear() + scroll_limit := a.view_height() + // scroll down + if ed.cursor.pos_y > a.viewport + scroll_limit { // scroll down + a.viewport = ed.cursor.pos_y - scroll_limit + } else if ed.cursor.pos_y < a.viewport { // scroll up + a.viewport = ed.cursor.pos_y + } + view := ed.view(a.viewport, scroll_limit + a.viewport) + a.tui.draw_text(0, 0, view.raw) + a.footer() + a.tui.set_cursor_position(view.cursor.pos_x + 1, ed.cursor.pos_y + 1 - a.viewport) + a.tui.flush() +} + +fn event(e &tui.Event, x voidptr) { + mut a := &App(x) + mut buffer := a.ed + if e.typ == .key_down { + match e.code { + .escape { + exit(0) + } + .enter { + buffer.put('\n') + } + .backspace { + buffer.del(-1) + } + .delete { + buffer.del(1) + } + .left { + if e.modifiers == .ctrl { + buffer.move_to_word(.left) + } else if e.modifiers.is_empty() { + buffer.move_cursor(1, .left) + } + a.magnet_x = buffer.cursor.pos_x + } + .right { + if e.modifiers == .ctrl { + buffer.move_to_word(.right) + } else if e.modifiers.is_empty() { + buffer.move_cursor(1, .right) + } + a.magnet_x = buffer.cursor.pos_x + } + .up { + buffer.move_cursor(1, .up) + a.magnet_cursor_x() + } + .down { + buffer.move_cursor(1, .down) + a.magnet_cursor_x() + } + .page_up { + buffer.move_cursor(a.view_height(), .page_up) + } + .page_down { + buffer.move_cursor(a.view_height(), .page_down) + } + .home { + buffer.move_cursor(1, .home) + } + .end { + buffer.move_cursor(1, .end) + } + 48...57, 97...122 { // 0-9a-zA-Z + if e.modifiers == .ctrl { + if e.code == .s { + a.save() + } + } else if !(e.modifiers.has(.ctrl | .alt) || e.code == .null) { + buffer.put(e.ascii.ascii_str()) + } + } + else { + if e.modifiers == .alt { + if e.code == .comma { + a.visit_prev_file() + return + } + if e.code == .period { + a.visit_next_file() + return + } + } + buffer.put(e.utf8.bytes().bytestr()) + } + } + } else if e.typ == .mouse_scroll { + direction := if e.direction == .up { Movement.down } else { Movement.up } + buffer.move_cursor(1, direction) + } +} + +fn main() { + mut files := []string{} + if os.args.len > 1 { + files << os.args[1..] + } + mut a := &App{ + files: files + } + a.tui = tui.init( + user_data: a + init_fn: init + frame_fn: frame + event_fn: event + capture_events: true + ) + a.tui.run() ? +} diff --git a/v_windows/v/old/examples/term.ui/vyper.v b/v_windows/v/old/examples/term.ui/vyper.v new file mode 100644 index 0000000..cf41e60 --- /dev/null +++ b/v_windows/v/old/examples/term.ui/vyper.v @@ -0,0 +1,475 @@ +// import modules for use in app +import term.ui as termui +import rand + +// define some global constants +const ( + block_size = 1 + buffer = 10 + green = termui.Color{0, 255, 0} + grey = termui.Color{150, 150, 150} + white = termui.Color{255, 255, 255} + blue = termui.Color{0, 0, 255} + red = termui.Color{255, 0, 0} + black = termui.Color{0, 0, 0} +) + +// what edge of the screen are you facing +enum Orientation { + top + right + bottom + left +} + +// what's the current state of the game +enum GameState { + pause + gameover + game + oob // snake out-of-bounds +} + +// simple 2d vector representation +struct Vec { +mut: + x int + y int +} + +// determine orientation from vector (hacky way to set facing from velocity) +fn (v Vec) facing() Orientation { + result := if v.x >= 0 { + Orientation.right + } else if v.x < 0 { + Orientation.left + } else if v.y >= 0 { + Orientation.bottom + } else { + Orientation.top + } + return result +} + +// generate a random vector with x in [min_x, max_x] and y in [min_y, max_y] +fn (mut v Vec) randomize(min_x int, min_y int, max_x int, max_y int) { + v.x = rand.int_in_range(min_x, max_x) + v.y = rand.int_in_range(min_y, max_y) +} + +// part of snake's body representation +struct BodyPart { +mut: + pos Vec = Vec{ + x: block_size + y: block_size + } + color termui.Color = green + facing Orientation = .top +} + +// snake representation +struct Snake { +mut: + app &App + direction Orientation + body []BodyPart + velocity Vec = Vec{ + x: 0 + y: 0 + } +} + +// length returns the snake's current length +fn (s Snake) length() int { + return s.body.len +} + +// impulse provides a impulse to change the snake's direction +fn (mut s Snake) impulse(direction Orientation) { + mut vec := Vec{} + match direction { + .top { + vec.x = 0 + vec.y = -1 * block_size + } + .right { + vec.x = 2 * block_size + vec.y = 0 + } + .bottom { + vec.x = 0 + vec.y = block_size + } + .left { + vec.x = -2 * block_size + vec.y = 0 + } + } + s.direction = direction + s.velocity = vec +} + +// move performs the calculations for the snake's movements +fn (mut s Snake) move() { + mut i := s.body.len - 1 + width := s.app.width + height := s.app.height + // move the parts of the snake as appropriate + for i = s.body.len - 1; i >= 0; i-- { + mut piece := s.body[i] + if i > 0 { // just move the body of the snake up one position + piece.pos = s.body[i - 1].pos + piece.facing = s.body[i - 1].facing + } else { // verify that the move is valid and move the head if so + piece.facing = s.direction + new_x := piece.pos.x + s.velocity.x + new_y := piece.pos.y + s.velocity.y + piece.pos.x += if new_x > block_size && new_x < width - block_size { + s.velocity.x + } else { + 0 + } + piece.pos.y += if new_y > block_size && new_y < height - block_size { + s.velocity.y + } else { + 0 + } + } + s.body[i] = piece + } +} + +// grow add another part to the snake when it catches the rat +fn (mut s Snake) grow() { + head := s.get_tail() + mut pos := Vec{} + // add the segment on the opposite side of the previous tail + match head.facing { + .bottom { + pos.x = head.pos.x + pos.y = head.pos.y - block_size + } + .left { + pos.x = head.pos.x + block_size + pos.y = head.pos.y + } + .top { + pos.x = head.pos.x + pos.y = head.pos.y + block_size + } + .right { + pos.x = head.pos.x - block_size + pos.y = head.pos.y + } + } + s.body << BodyPart{ + pos: pos + facing: head.facing + } +} + +// get_body gets the parts of the snakes body +fn (s Snake) get_body() []BodyPart { + return s.body +} + +// get_head get snake's head +fn (s Snake) get_head() BodyPart { + return s.body[0] +} + +// get_tail get snake's tail +fn (s Snake) get_tail() BodyPart { + return s.body[s.body.len - 1] +} + +// randomize randomizes position and veolcity of snake +fn (mut s Snake) randomize() { + speeds := [-2, 0, 2] + mut pos := s.get_head().pos + pos.randomize(buffer, buffer, s.app.width - buffer, s.app.height - buffer) + for pos.x % 2 != 0 || (pos.x < buffer && pos.x > s.app.width - buffer) { + pos.randomize(buffer, buffer, s.app.width - buffer, s.app.height - buffer) + } + s.velocity.y = rand.int_in_range(-1 * block_size, block_size) + s.velocity.x = speeds[rand.intn(speeds.len)] + s.direction = s.velocity.facing() + s.body[0].pos = pos +} + +// check_overlap determine if the snake's looped onto itself +fn (s Snake) check_overlap() bool { + h := s.get_head() + head_pos := h.pos + for i in 2 .. s.length() { + piece_pos := s.body[i].pos + if head_pos.x == piece_pos.x && head_pos.y == piece_pos.y { + return true + } + } + return false +} + +fn (s Snake) check_out_of_bounds() bool { + h := s.get_head() + return h.pos.x + s.velocity.x <= block_size + || h.pos.x + s.velocity.x > s.app.width - s.velocity.x + || h.pos.y + s.velocity.y <= block_size + || h.pos.y + s.velocity.y > s.app.height - block_size - s.velocity.y +} + +// draw draws the parts of the snake +fn (s Snake) draw() { + mut a := s.app + for part in s.get_body() { + a.termui.set_bg_color(part.color) + a.termui.draw_rect(part.pos.x, part.pos.y, part.pos.x + block_size, part.pos.y + block_size) + $if verbose ? { + text := match part.facing { + .top { '^' } + .bottom { 'v' } + .right { '>' } + .left { '<' } + } + a.termui.set_color(white) + a.termui.draw_text(part.pos.x, part.pos.y, text) + } + } +} + +// rat representation +struct Rat { +mut: + pos Vec = Vec{ + x: block_size + y: block_size + } + captured bool + color termui.Color = grey + app &App +} + +// randomize spawn the rat in a new spot within the playable field +fn (mut r Rat) randomize() { + r.pos.randomize(2 * block_size + buffer, 2 * block_size + buffer, r.app.width - block_size - buffer, + r.app.height - block_size - buffer) +} + +[heap] +struct App { +mut: + termui &termui.Context = 0 + snake Snake + rat Rat + width int + height int + redraw bool = true + state GameState = .game +} + +// new_game setups the rat and snake for play +fn (mut a App) new_game() { + mut snake := Snake{ + body: []BodyPart{len: 1, init: BodyPart{}} + app: a + } + snake.randomize() + mut rat := Rat{ + app: a + } + rat.randomize() + a.snake = snake + a.rat = rat + a.state = .game + a.redraw = true +} + +// initialize the app and record the width and height of the window +fn init(x voidptr) { + mut app := &App(x) + w, h := app.termui.window_width, app.termui.window_height + app.width = w + app.height = h + app.new_game() +} + +// event handles different events for the app as they occur +fn event(e &termui.Event, x voidptr) { + mut app := &App(x) + match e.typ { + .mouse_down {} + .mouse_drag {} + .mouse_up {} + .key_down { + match e.code { + .up, .w { app.move_snake(.top) } + .down, .s { app.move_snake(.bottom) } + .left, .a { app.move_snake(.left) } + .right, .d { app.move_snake(.right) } + .r { app.new_game() } + .c {} + .p { app.state = if app.state == .game { GameState.pause } else { GameState.game } } + .escape, .q { exit(0) } + else { exit(0) } + } + if e.code == .c { + } else if e.code == .escape { + exit(0) + } + } + else {} + } + app.redraw = true +} + +// frame perform actions on every tick +fn frame(x voidptr) { + mut app := &App(x) + app.update() + app.draw() +} + +// update perform any calculations that are needed before drawing +fn (mut a App) update() { + if a.state == .game { + a.snake.move() + if a.snake.check_out_of_bounds() { + $if verbose ? { + a.snake.body[0].color = red + } $else { + a.state = .oob + } + } + if a.snake.check_overlap() { + a.state = .gameover + return + } + if a.check_capture() { + a.rat.randomize() + a.snake.grow() + } + } +} + +// draw write to the screen +fn (mut a App) draw() { + // reset screen + a.termui.clear() + a.termui.set_bg_color(white) + a.termui.draw_empty_rect(1, 1, a.width, a.height) + // determine if a special screen needs to be draw + match a.state { + .gameover { + a.draw_gameover() + a.redraw = false + } + .pause { + a.draw_pause() + } + else { + a.redraw = true + } + } + a.termui.set_color(blue) + a.termui.set_bg_color(white) + a.termui.draw_text(3 * block_size, a.height - (2 * block_size), 'p - (un)pause r - reset q - quit') + // draw the snake, rat, and score if appropriate + if a.redraw { + a.termui.set_bg_color(black) + a.draw_gamescreen() + if a.state == .oob { + a.state = .gameover + } + } + // write to the screen + a.termui.reset_bg_color() + a.termui.flush() +} + +// move_snake move the snake in specified direction +fn (mut a App) move_snake(direction Orientation) { + a.snake.impulse(direction) +} + +// check_capture determine if the snake overlaps with the rat +fn (a App) check_capture() bool { + snake_pos := a.snake.get_head().pos + rat_pos := a.rat.pos + return snake_pos.x <= rat_pos.x + block_size && snake_pos.x + block_size >= rat_pos.x + && snake_pos.y <= rat_pos.y + block_size && snake_pos.y + block_size >= rat_pos.y +} + +fn (mut a App) draw_snake() { + a.snake.draw() +} + +fn (mut a App) draw_rat() { + a.termui.set_bg_color(a.rat.color) + a.termui.draw_rect(a.rat.pos.x, a.rat.pos.y, a.rat.pos.x + block_size, a.rat.pos.y + block_size) +} + +fn (mut a App) draw_gamescreen() { + $if verbose ? { + a.draw_debug() + } + a.draw_score() + a.draw_rat() + a.draw_snake() +} + +fn (mut a App) draw_score() { + a.termui.set_color(blue) + a.termui.set_bg_color(white) + score := a.snake.length() - 1 + a.termui.draw_text(a.width - (2 * block_size), block_size, '${score:03d}') +} + +fn (mut a App) draw_pause() { + a.termui.set_color(blue) + a.termui.draw_text((a.width / 2) - block_size, 3 * block_size, 'Paused!') +} + +fn (mut a App) draw_debug() { + a.termui.set_color(blue) + a.termui.set_bg_color(white) + snake := a.snake + a.termui.draw_text(block_size, 1 * block_size, 'Display_width: ${a.width:04d} Display_height: ${a.height:04d}') + a.termui.draw_text(block_size, 2 * block_size, 'Vx: ${snake.velocity.x:+02d} Vy: ${snake.velocity.y:+02d}') + a.termui.draw_text(block_size, 3 * block_size, 'F: $snake.direction') + snake_head := snake.get_head() + rat := a.rat + a.termui.draw_text(block_size, 4 * block_size, 'Sx: ${snake_head.pos.x:+03d} Sy: ${snake_head.pos.y:+03d}') + a.termui.draw_text(block_size, 5 * block_size, 'Rx: ${rat.pos.x:+03d} Ry: ${rat.pos.y:+03d}') +} + +fn (mut a App) draw_gameover() { + a.termui.set_bg_color(white) + a.termui.set_color(red) + a.rat.pos = Vec{ + x: -1 + y: -1 + } + x_offset := ' ##### '.len // take half of a line from the game over text and store the length + start_x := (a.width / 2) - x_offset + a.termui.draw_text(start_x, (a.height / 2) - 3 * block_size, ' ##### ####### ') + a.termui.draw_text(start_x, (a.height / 2) - 2 * block_size, ' # # ## # # ###### # # # # ###### ##### ') + a.termui.draw_text(start_x, (a.height / 2) - 1 * block_size, ' # # # ## ## # # # # # # # # ') + a.termui.draw_text(start_x, (a.height / 2) - 0 * block_size, ' # #### # # # ## # ##### # # # # ##### # # ') + a.termui.draw_text(start_x, (a.height / 2) + 1 * block_size, ' # # ###### # # # # # # # # ##### ') + a.termui.draw_text(start_x, (a.height / 2) + 2 * block_size, ' # # # # # # # # # # # # # # ') + a.termui.draw_text(start_x, (a.height / 2) + 3 * block_size, ' ##### # # # # ###### ####### ## ###### # # ') +} + +fn main() { + mut app := &App{} + app.termui = termui.init( + user_data: app + event_fn: event + frame_fn: frame + init_fn: init + hide_cursor: true + frame_rate: 10 + ) + app.termui.run() ? +} diff --git a/v_windows/v/old/examples/terminal_control.v b/v_windows/v/old/examples/terminal_control.v new file mode 100644 index 0000000..404f290 --- /dev/null +++ b/v_windows/v/old/examples/terminal_control.v @@ -0,0 +1,36 @@ +import term + +fn main() { + term.erase_clear() + sleeping_line(5, 5, 5, '*') + standing_line(5, 5, 5, '*') + sleeping_line(5, 10, 5, '*') + standing_line(9, 5, 5, '*') + term.cursor_down(5) + print('\n') + println(term.bold(term.red('It Worked!'))) +} + +fn sleeping_line(x int, y int, size int, ch string) { + mut i := 0 + for i < size { + term.set_cursor_position( + x: x + i + y: y + ) + print(term.bold(term.yellow(ch))) + i++ + } +} + +fn standing_line(x int, y int, size int, ch string) { + mut i := 0 + for i < size { + term.set_cursor_position( + x: x + y: y + i + ) + print(term.bold(term.yellow(ch))) + i++ + } +} diff --git a/v_windows/v/old/examples/tetris/README.md b/v_windows/v/old/examples/tetris/README.md new file mode 100644 index 0000000..353db1e --- /dev/null +++ b/v_windows/v/old/examples/tetris/README.md @@ -0,0 +1,9 @@ + + +### Dependencies (Ubuntu) +```sh +sudo apt install libx11-dev +sudo apt install libxi-dev +sudo apt install libxcursor-dev +sudo apt install libgl-dev +``` diff --git a/v_windows/v/old/examples/tetris/screenshot.png b/v_windows/v/old/examples/tetris/screenshot.png new file mode 100644 index 0000000..8b457a1 Binary files /dev/null and b/v_windows/v/old/examples/tetris/screenshot.png differ diff --git a/v_windows/v/old/examples/tetris/tetris.v b/v_windows/v/old/examples/tetris/tetris.v new file mode 100644 index 0000000..01589a3 --- /dev/null +++ b/v_windows/v/old/examples/tetris/tetris.v @@ -0,0 +1,518 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module main + +import os +import rand +import time +import gx +import gg +// import sokol.sapp + +const ( + block_size = 20 // virtual pixels + field_height = 20 // # of blocks + field_width = 10 + tetro_size = 4 + win_width = block_size * field_width + win_height = block_size * field_height + timer_period = 250 // ms + text_size = 24 + limit_thickness = 3 +) + +const ( + text_cfg = gx.TextCfg{ + align: .left + size: text_size + color: gx.rgb(0, 0, 0) + } + over_cfg = gx.TextCfg{ + align: .left + size: text_size + color: gx.white + } +) + +const ( + // Tetros' 4 possible states are encoded in binaries + // 0000 0 0000 0 0000 0 0000 0 0000 0 0000 0 + // 0000 0 0000 0 0000 0 0000 0 0011 3 0011 3 + // 0110 6 0010 2 0011 3 0110 6 0001 1 0010 2 + // 0110 6 0111 7 0110 6 0011 3 0001 1 0010 2 + // There is a special case 1111, since 15 can't be used. + b_tetros = [ + [66, 66, 66, 66], + [27, 131, 72, 232], + [36, 231, 36, 231], + [63, 132, 63, 132], + [311, 17, 223, 74], + [322, 71, 113, 47], + [1111, 9, 1111, 9], + ] + // Each tetro has its unique color + colors = [ + gx.rgb(0, 0, 0), /* unused ? */ + gx.rgb(255, 242, 0), /* yellow quad */ + gx.rgb(174, 0, 255), /* purple triple */ + gx.rgb(60, 255, 0), /* green short topright */ + gx.rgb(255, 0, 0), /* red short topleft */ + gx.rgb(255, 180, 31), /* orange long topleft */ + gx.rgb(33, 66, 255), /* blue long topright */ + gx.rgb(74, 198, 255), /* lightblue longest */ + gx.rgb(0, 170, 170), + ] + background_color = gx.white + ui_color = gx.rgba(255, 0, 0, 210) +) + +// TODO: type Tetro [tetro_size]struct{ x, y int } +struct Block { +mut: + x int + y int +} + +enum GameState { + paused + running + gameover +} + +struct Game { +mut: + // Score of the current game + score int + // Lines of the current game + lines int + // State of the current game + state GameState + // Block size in screen dimensions + block_size int = block_size + // Field margin + margin int + // Position of the current tetro + pos_x int + pos_y int + // field[y][x] contains the color of the block with (x,y) coordinates + // "-1" border is to avoid bounds checking. + // -1 -1 -1 -1 + // -1 0 0 -1 + // -1 0 0 -1 + // -1 -1 -1 -1 + field [][]int + // TODO: tetro Tetro + tetro []Block + // TODO: tetros_cache []Tetro + tetros_cache []Block + // Index of the current tetro. Refers to its color. + tetro_idx int + // Idem for the next tetro + next_tetro_idx int + // Index of the rotation (0-3) + rotation_idx int + // gg context for drawing + gg &gg.Context = voidptr(0) + font_loaded bool + show_ghost bool = true + // frame/time counters: + frame int + frame_old int + frame_sw time.StopWatch = time.new_stopwatch() + second_sw time.StopWatch = time.new_stopwatch() +} + +fn remap(v f32, min f32, max f32, new_min f32, new_max f32) f32 { + return (((v - min) * (new_max - new_min)) / (max - min)) + new_min +} + +[if showfps ?] +fn (mut game Game) showfps() { + game.frame++ + last_frame_ms := f64(game.frame_sw.elapsed().microseconds()) / 1000.0 + ticks := f64(game.second_sw.elapsed().microseconds()) / 1000.0 + if ticks > 999.0 { + fps := f64(game.frame - game.frame_old) * ticks / 1000.0 + $if debug { + eprintln('fps: ${fps:5.1f} | last frame took: ${last_frame_ms:6.3f}ms | frame: ${game.frame:6} ') + } + game.second_sw.restart() + game.frame_old = game.frame + } +} + +fn frame(mut game Game) { + ws := gg.window_size() + bs := remap(block_size, 0, win_height, 0, ws.height) + m := (f32(ws.width) - bs * field_width) * 0.5 + game.block_size = int(bs) + game.margin = int(m) + game.frame_sw.restart() + game.gg.begin() + game.draw_scene() + game.showfps() + game.gg.end() +} + +fn main() { + mut game := &Game{ + gg: 0 + } + mut fpath := os.resource_abs_path(os.join_path('..', 'assets', 'fonts', 'RobotoMono-Regular.ttf')) + $if android { + fpath = 'fonts/RobotoMono-Regular.ttf' + } + game.gg = gg.new_context( + bg_color: gx.white + width: win_width + height: win_height + create_window: true + window_title: 'V Tetris' // + user_data: game + frame_fn: frame + event_fn: on_event + font_path: fpath // wait_events: true + ) + game.init_game() + go game.run() // Run the game loop in a new thread + game.gg.run() // Run the render loop in the main thread +} + +fn (mut g Game) init_game() { + g.parse_tetros() + g.next_tetro_idx = rand.intn(b_tetros.len) // generate initial "next" + g.generate_tetro() + g.field = [] + // Generate the field, fill it with 0's, add -1's on each edge + for _ in 0 .. field_height + 2 { + mut row := [0].repeat(field_width + 2) + row[0] = -1 + row[field_width + 1] = -1 + g.field << row.clone() + } + for j in 0 .. field_width + 2 { + g.field[0][j] = -1 + g.field[field_height + 1][j] = -1 + } + g.score = 0 + g.lines = 0 + g.state = .running +} + +fn (mut g Game) parse_tetros() { + for b_tetros0 in b_tetros { + for b_tetro in b_tetros0 { + for t in parse_binary_tetro(b_tetro) { + g.tetros_cache << t + } + } + } +} + +fn (mut g Game) run() { + for { + if g.state == .running { + g.move_tetro() + g.delete_completed_lines() + } + // glfw.post_empty_event() // force window redraw + time.sleep(timer_period * time.millisecond) + } +} + +fn (g &Game) draw_ghost() { + if g.state != .gameover && g.show_ghost { + pos_y := g.move_ghost() + for i in 0 .. tetro_size { + tetro := g.tetro[i] + g.draw_block_color(pos_y + tetro.y, g.pos_x + tetro.x, gx.rgba(125, 125, 225, + 40)) + } + } +} + +fn (g Game) move_ghost() int { + mut pos_y := g.pos_y + mut end := false + for !end { + for block in g.tetro { + y := block.y + pos_y + 1 + x := block.x + g.pos_x + if g.field[y][x] != 0 { + end = true + break + } + } + pos_y++ + } + return pos_y - 1 +} + +fn (mut g Game) move_tetro() bool { + // Check each block in current tetro + for block in g.tetro { + y := block.y + g.pos_y + 1 + x := block.x + g.pos_x + // Reached the bottom of the screen or another block? + if g.field[y][x] != 0 { + // The new tetro has no space to drop => end of the game + if g.pos_y < 2 { + g.state = .gameover + return false + } + // Drop it and generate a new one + g.drop_tetro() + g.generate_tetro() + return false + } + } + g.pos_y++ + return true +} + +fn (mut g Game) move_right(dx int) bool { + // Reached left/right edge or another tetro? + for i in 0 .. tetro_size { + tetro := g.tetro[i] + y := tetro.y + g.pos_y + x := tetro.x + g.pos_x + dx + if g.field[y][x] != 0 { + // Do not move + return false + } + } + g.pos_x += dx + return true +} + +fn (mut g Game) delete_completed_lines() { + for y := field_height; y >= 1; y-- { + g.delete_completed_line(y) + } +} + +fn (mut g Game) delete_completed_line(y int) { + for x := 1; x <= field_width; x++ { + if g.field[y][x] == 0 { + return + } + } + g.score += 10 + g.lines++ + // Move everything down by 1 position + for yy := y - 1; yy >= 1; yy-- { + for x := 1; x <= field_width; x++ { + g.field[yy + 1][x] = g.field[yy][x] + } + } +} + +// Place a new tetro on top +fn (mut g Game) generate_tetro() { + g.pos_y = 0 + g.pos_x = field_width / 2 - tetro_size / 2 + g.tetro_idx = g.next_tetro_idx + g.next_tetro_idx = rand.intn(b_tetros.len) + g.rotation_idx = 0 + g.get_tetro() +} + +// Get the right tetro from cache +fn (mut g Game) get_tetro() { + idx := g.tetro_idx * tetro_size * tetro_size + g.rotation_idx * tetro_size + g.tetro = g.tetros_cache[idx..idx + tetro_size].clone() +} + +// TODO mut +fn (mut g Game) drop_tetro() { + for i in 0 .. tetro_size { + tetro := g.tetro[i] + x := tetro.x + g.pos_x + y := tetro.y + g.pos_y + // Remember the color of each block + g.field[y][x] = g.tetro_idx + 1 + } +} + +fn (g &Game) draw_tetro() { + for i in 0 .. tetro_size { + tetro := g.tetro[i] + g.draw_block(g.pos_y + tetro.y, g.pos_x + tetro.x, g.tetro_idx + 1) + } +} + +fn (g &Game) draw_next_tetro() { + if g.state != .gameover { + idx := g.next_tetro_idx * tetro_size * tetro_size + next_tetro := g.tetros_cache[idx..idx + tetro_size].clone() + pos_y := 0 + pos_x := field_width / 2 - tetro_size / 2 + for i in 0 .. tetro_size { + block := next_tetro[i] + g.draw_block_color(pos_y + block.y, pos_x + block.x, gx.rgb(220, 220, 220)) + } + } +} + +fn (g &Game) draw_block_color(i int, j int, color gx.Color) { + g.gg.draw_rect(f32((j - 1) * g.block_size) + g.margin, f32((i - 1) * g.block_size), + f32(g.block_size - 1), f32(g.block_size - 1), color) +} + +fn (g &Game) draw_block(i int, j int, color_idx int) { + color := if g.state == .gameover { gx.gray } else { colors[color_idx] } + g.draw_block_color(i, j, color) +} + +fn (g &Game) draw_field() { + for i := 1; i < field_height + 1; i++ { + for j := 1; j < field_width + 1; j++ { + if g.field[i][j] > 0 { + g.draw_block(i, j, g.field[i][j]) + } + } + } +} + +fn (mut g Game) draw_ui() { + ws := gg.window_size() + textsize := int(remap(text_size, 0, win_width, 0, ws.width)) + g.gg.draw_text(1, 3, g.score.str(), text_cfg) + lines := g.lines.str() + g.gg.draw_text(ws.width - lines.len * textsize, 3, lines, text_cfg) + if g.state == .gameover { + g.gg.draw_rect(0, ws.height / 2 - textsize, ws.width, 5 * textsize, ui_color) + g.gg.draw_text(1, ws.height / 2 + 0 * textsize, 'Game Over', over_cfg) + g.gg.draw_text(1, ws.height / 2 + 2 * textsize, 'Space to restart', over_cfg) + } else if g.state == .paused { + g.gg.draw_rect(0, ws.height / 2 - textsize, ws.width, 5 * textsize, ui_color) + g.gg.draw_text(1, ws.height / 2 + 0 * textsize, 'Game Paused', text_cfg) + g.gg.draw_text(1, ws.height / 2 + 2 * textsize, 'SPACE to resume', text_cfg) + } + // g.gg.draw_rect(0, block_size, win_width, limit_thickness, ui_color) +} + +fn (mut g Game) draw_scene() { + g.draw_ghost() + g.draw_next_tetro() + g.draw_tetro() + g.draw_field() + g.draw_ui() +} + +fn parse_binary_tetro(t_ int) []Block { + mut t := t_ + mut res := [Block{}].repeat(4) + mut cnt := 0 + horizontal := t == 9 // special case for the horizontal line + ten_powers := [1000, 100, 10, 1] + for i := 0; i <= 3; i++ { + // Get ith digit of t + p := ten_powers[i] + mut digit := t / p + t %= p + // Convert the digit to binary + for j := 3; j >= 0; j-- { + bin := digit % 2 + digit /= 2 + if bin == 1 || (horizontal && i == tetro_size - 1) { + res[cnt].x = j + res[cnt].y = i + cnt++ + } + } + } + return res +} + +fn on_event(e &gg.Event, mut game Game) { + // println('code=$e.char_code') + if e.typ == .key_down { + game.key_down(e.key_code) + } + if e.typ == .touches_began || e.typ == .touches_moved { + if e.num_touches > 0 { + touch_point := e.touches[0] + game.touch_event(touch_point) + } + } +} + +fn (mut game Game) rotate_tetro() { + old_rotation_idx := game.rotation_idx + game.rotation_idx++ + if game.rotation_idx == tetro_size { + game.rotation_idx = 0 + } + game.get_tetro() + if !game.move_right(0) { + game.rotation_idx = old_rotation_idx + game.get_tetro() + } + if game.pos_x < 0 { + // game.pos_x = 1 + } +} + +fn (mut game Game) key_down(key gg.KeyCode) { + // global keys + match key { + .escape { + exit(0) + } + .space { + if game.state == .running { + game.state = .paused + } else if game.state == .paused { + game.state = .running + } else if game.state == .gameover { + game.init_game() + game.state = .running + } + } + else {} + } + if game.state != .running { + return + } + // keys while game is running + match key { + .up { + // Rotate the tetro + game.rotate_tetro() + } + .left { + game.move_right(-1) + } + .right { + game.move_right(1) + } + .down { + game.move_tetro() // drop faster when the player presses + } + .d { + for game.move_tetro() { + } + } + .g { + game.show_ghost = !game.show_ghost + } + else {} + } +} + +fn (mut game Game) touch_event(touch_point C.sapp_touchpoint) { + ws := gg.window_size() + tx := touch_point.pos_x + ty := touch_point.pos_y + if ty < f32(ws.height) * 0.5 { + game.rotate_tetro() + } else { + if tx <= f32(ws.width) * 0.5 { + game.move_right(-1) + } else { + game.move_right(1) + } + } +} diff --git a/v_windows/v/old/examples/tree_of_nodes.v b/v_windows/v/old/examples/tree_of_nodes.v new file mode 100644 index 0000000..76d69a2 --- /dev/null +++ b/v_windows/v/old/examples/tree_of_nodes.v @@ -0,0 +1,27 @@ +type Tree = Empty | Node + +struct Empty {} + +struct Node { + value int + left Tree + right Tree +} + +// NB: a match expression, infers the type of its result +// from the type of the return value in the first branch, +// => it needs an explicit int(0) cast here: +fn size(tree Tree) int { + return match tree { + Empty { int(0) } + Node { 1 + size(tree.left) + size(tree.right) } + } +} + +fn main() { + node1 := Node{30, Empty{}, Empty{}} + node2 := Node{20, Empty{}, Empty{}} + tree := Node{10, node1, node2} + println('tree structure:\n $tree') + println('tree size: ${size(tree)}') +} diff --git a/v_windows/v/old/examples/ttf_font/Graduate-Regular.ttf b/v_windows/v/old/examples/ttf_font/Graduate-Regular.ttf new file mode 100644 index 0000000..0ce68b7 Binary files /dev/null and b/v_windows/v/old/examples/ttf_font/Graduate-Regular.ttf differ diff --git a/v_windows/v/old/examples/ttf_font/Imprima-Regular.ttf b/v_windows/v/old/examples/ttf_font/Imprima-Regular.ttf new file mode 100644 index 0000000..9c701e1 Binary files /dev/null and b/v_windows/v/old/examples/ttf_font/Imprima-Regular.ttf differ diff --git a/v_windows/v/old/examples/ttf_font/OFL.txt b/v_windows/v/old/examples/ttf_font/OFL.txt new file mode 100644 index 0000000..3a5f6c1 --- /dev/null +++ b/v_windows/v/old/examples/ttf_font/OFL.txt @@ -0,0 +1,93 @@ +Copyright (c) 2012, Eduardo Tunni (http://www.tipo.net.ar), with Reserved Font Name "Imprima" + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +http://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/v_windows/v/old/examples/ttf_font/example_ttf.v b/v_windows/v/old/examples/ttf_font/example_ttf.v new file mode 100644 index 0000000..02e43cd --- /dev/null +++ b/v_windows/v/old/examples/ttf_font/example_ttf.v @@ -0,0 +1,171 @@ +import gg +import gx +import sokol.sapp +import sokol.sgl +import x.ttf +import os + +// import math +const ( + win_width = 600 + win_height = 700 + bg_color = gx.white + font_paths = [ + os.resource_abs_path('Imprima-Regular.ttf'), + os.resource_abs_path('Graduate-Regular.ttf'), + ] +) + +// UI +struct App_data { +pub mut: + gg &gg.Context + sg_img C.sg_image + init_flag bool + frame_c int + tf []ttf.TTF_File + ttf_render []ttf.TTF_render_Sokol + text_ready_flag bool + mouse_x int = -1 + mouse_y int = -1 +} + +fn my_init(mut app App_data) { + app.init_flag = true +} + +fn draw_frame(mut app App_data) { + cframe_txt := 'Current Frame: $app.frame_c' + app.gg.begin() + sgl.defaults() + sgl.matrix_mode_projection() + sgl.ortho(0.0, f32(sapp.width()), f32(sapp.height()), 0.0, -1.0, 1.0) + sgl.c4b(0, 0, 0, 255) // black + // draw a line as background + sgl.begin_line_strip() + sgl.v2f(10, 10) + sgl.v2f(100, 100) + sgl.end() + // draw text only if the app is already initialized + if app.init_flag == true { + sgl.begin_line_strip() + sgl.v2f(410, 400) + sgl.v2f(510, 400) + sgl.end() + // update the text + mut txt1 := unsafe { &app.ttf_render[0] } + if app.frame_c % 2 == 0 { + txt1.destroy_texture() + txt1.create_text(cframe_txt, 43) + txt1.create_texture() + } + // ----- decomment if you want text rotation ---- + // txt1.bmp.angle = 3.141592 / 180 * f32(app.frame_c % 360) + // txt1.draw_text_bmp(app.gg, 300, 350) + // txt1.bmp.angle = 0 + txt1.draw_text_bmp(app.gg, 30, 60) + // block test + block_txt := "Today it is a good day! +Tommorow I'm not so sure :( +Frame: $app.frame_c +But Vwill prevail for sure, V is the way!! +òàèì@ò!£$%& +" + txt1 = unsafe { &app.ttf_render[1] } + if app.frame_c % 2 == 0 { + txt1.bmp.justify = false + if (app.frame_c >> 6) % 2 == 0 { + // txt1.align = .left + txt1.bmp.justify = true + } + txt1.bmp.align = .left + if (app.frame_c >> 6) % 3 == 0 { + txt1.bmp.align = .right + } + txt1.destroy_texture() + txt1.create_text_block(block_txt, 500, 500, 32) + txt1.create_texture() + } + // decomment if want block color change + // txt1.bmp.color = ttf.color_multiply(0xFF00FFFF, f32(app.frame_c % 255)/255.0) + // decomment if want block rotation wanted + // txt1.bmp.angle = 3.141592/180 * f32(app.frame_c % 45) + txt1.draw_text_bmp(app.gg, 30 + (app.frame_c >> 1) & 0xFF, 200) + // draw mouse position + if app.mouse_x >= 0 { + txt1 = unsafe { &app.ttf_render[2] } + txt1.destroy_texture() + txt1.create_text('$app.mouse_x,$app.mouse_y', 25) + txt1.create_texture() + r := app.mouse_x % 255 + g := app.mouse_y % 255 + color := u32(r << 24) | u32(g << 16) | 0xFF + txt1.bmp.color = color + txt1.draw_text_bmp(app.gg, app.mouse_x, app.mouse_y) + } + app.frame_c++ + } + app.gg.end() +} + +fn my_event_manager(mut ev gg.Event, mut app App_data) { + if ev.typ == .mouse_move { + app.mouse_x = int(ev.mouse_x) + app.mouse_y = int(ev.mouse_y) + } +} + +[console] +fn main() { + mut app := &App_data{ + gg: 0 + } + app.gg = gg.new_context( + width: win_width + height: win_height + create_window: true + window_title: 'Test TTF module' + user_data: app + bg_color: bg_color + frame_fn: draw_frame + event_fn: my_event_manager + init_fn: my_init + ) + // load TTF fonts + for font_path in font_paths { + mut tf := ttf.TTF_File{} + tf.buf = os.read_bytes(font_path) or { panic(err) } + println('TrueTypeFont file [$font_path] len: $tf.buf.len') + tf.init() + println('Unit per EM: $tf.units_per_em') + app.tf << tf + } + // TTF render 0 Frame counter + app.ttf_render << &ttf.TTF_render_Sokol{ + bmp: &ttf.BitMap{ + tf: &(app.tf[0]) + buf: unsafe { malloc_noscan(32000000) } + buf_size: (32000000) + color: 0xFF0000FF + // style: .raw + // use_font_metrics: true + } + } + // TTF render 1 Text Block + app.ttf_render << &ttf.TTF_render_Sokol{ + bmp: &ttf.BitMap{ + tf: &(app.tf[1]) + // color : 0xFF0000_10 + // style: .raw + // use_font_metrics: true + } + } + // TTF mouse position render + app.ttf_render << &ttf.TTF_render_Sokol{ + bmp: &ttf.BitMap{ + tf: &(app.tf[0]) + } + } + // setup sokol_gfx + app.gg.run() +} diff --git a/v_windows/v/old/examples/v_script.vsh b/v_windows/v/old/examples/v_script.vsh new file mode 100644 index 0000000..1cdf910 --- /dev/null +++ b/v_windows/v/old/examples/v_script.vsh @@ -0,0 +1,32 @@ +#!/usr/local/bin/v run + +// The shebang above associates the file to V on Unix-like systems, +// so it can be run just by specifying the path to the file +// once it's made executable using `chmod +x`. + +for _ in 0 .. 3 { + println('V script') +} + +println('\nMaking dir "v_script_dir".') +mkdir('v_script_dir') + +println("\nEntering into v_script_dir and listing it's files.") +chdir('v_script_dir') +files := ls('.') or { panic(err.msg) } +println(files) + +println('\nCreating foo.txt') +create('foo.txt') ? + +println('\nFiles:') +again_ls := ls('.') or { panic(err.msg) } +println(again_ls) + +println('\nRemoving foo.txt and v_script_dir') +rm('foo.txt') +chdir('../') +rmdir('v_script_dir') + +print('\nDoes v_script_dir still exist? ') +println(exists('v_script_dir')) diff --git a/v_windows/v/old/examples/vcasino/README.md b/v_windows/v/old/examples/vcasino/README.md new file mode 100644 index 0000000..a2a6a3d --- /dev/null +++ b/v_windows/v/old/examples/vcasino/README.md @@ -0,0 +1,17 @@ +# VCasino +VCasino is a very simple game made to learn V. + +# Compile and Run + +Use this to generate a binary and then launch the game. +```bash +v VCasino.v +./VCasino +``` + +And this to compile and launch the game directly. +```bash +v run VCasino.v +``` + +Created by Thomas Senechal : https://github.com/thomas-senechal/VCasino diff --git a/v_windows/v/old/examples/vcasino/vcasino.v b/v_windows/v/old/examples/vcasino/vcasino.v new file mode 100644 index 0000000..e6fd87c --- /dev/null +++ b/v_windows/v/old/examples/vcasino/vcasino.v @@ -0,0 +1,146 @@ +import rand +import os + +const ( + help_text = ' Usage:\t./VCasino\n + Description:\n VCasino is a little game only made to learn V.\n' + g_desc = " The object of Roulette is to pick the number where the spinning ball will land on the wheel. + If your number is the good one, you'll get your bet x3. + If your number is the same color as the ball one, you'll get your bet /2. + Otherwise, you will lose your bet.\n" + odd = 'red' + even = 'black' +) + +struct Options { + long_opt string + short_opt string +} + +fn display_help() { + println(help_text + g_desc) +} + +fn option_parser() bool { + help := Options{'--help', '-h'} + for i in 0 .. os.args.len { + if os.args[i] == help.long_opt || os.args[i] == help.short_opt { + display_help() + return true + } + } + return false +} + +fn str_is_nbr(s string) bool { + for i in 0 .. s.len { + if !s[i].is_digit() { + return false + } + } + return true +} + +fn get_bet_nbr() int { + mut bet_nbr := -1 + for bet_nbr < 0 || bet_nbr > 49 { + println('Reminder: odd numbers are red and even are black.') + println('Type the number you want to bet on (between 0 and 49):') + line := os.get_line().trim_space() + if line.len < 1 { + println('error: empty line.') + continue + } + if !str_is_nbr(line) { + println('error: $line is not a number.') + continue + } + bet_nbr = line.int() + if bet_nbr < 0 || bet_nbr > 49 { + println('error: $line is not between 0 and 49.') + bet_nbr = -1 + continue + } + } + return bet_nbr +} + +fn get_bet(money int) int { + mut bet := -1 + for bet <= 0 || bet > money { + println('You have $money V. Type in the amount of your bet:') + line := os.get_line().trim_space() + if line.len < 1 { + println('error: empty line.') + continue + } + if !str_is_nbr(line) { + println('error: $line is not a number.') + continue + } + bet = line.int() + if bet <= 0 { + println('error: $line is not higher than 1.') + continue + } else if bet > money { + println('error: $line is more money than you have.') + } + } + return bet +} + +fn run_wheel(bet_nbr int, _bet int) int { + mut bet := _bet + winning_nbr := rand.intn(50) + print('Roulette Wheel spinning... and stops on the number $winning_nbr which is a ') + if winning_nbr % 2 == 1 { + println(odd) + } else { + println(even) + } + if winning_nbr == bet_nbr { + bet *= 3 + println('Congratulations! You get $bet V!') + } else if winning_nbr % 2 == bet_nbr % 2 { + bet /= 2 + println('You bet the right color. You get $bet V!') + } else { + println('Sorry buddy. You lost $bet V!') + bet *= -1 + } + return bet +} + +fn is_broke(money int) bool { + if money <= 0 { + println("You're broke, the game is over..") + return false + } + quit := Options{'yes', 'y'} + println('You have $money V. Do you want to quit the casino with your winnings? (y/n)') + line := os.get_line().trim_space().to_lower() + if line == quit.long_opt || line == quit.short_opt { + return false + } + return true +} + +fn game_loop() { + mut can_play := true + mut money := 1000 + println(g_desc) + println('You start the game with $money V.\n') + for can_play { + bet_nbr := get_bet_nbr() + bet := get_bet(money) + money += run_wheel(bet_nbr, bet) + can_play = is_broke(money) + } +} + +fn main() { + if os.args.len >= 2 && option_parser() { + return + } + game_loop() +} diff --git a/v_windows/v/old/examples/vmod.v b/v_windows/v/old/examples/vmod.v new file mode 100644 index 0000000..9e34b32 --- /dev/null +++ b/v_windows/v/old/examples/vmod.v @@ -0,0 +1,9 @@ +module main + +import v.vmod + +fn main() { + mod := vmod.decode(@VMOD_FILE) or { panic('Error decoding v.mod') } + println('$mod.name has version $mod.version') + println('\nThe full mod struct: \n$mod') +} diff --git a/v_windows/v/old/examples/vpwgen.v b/v_windows/v/old/examples/vpwgen.v new file mode 100644 index 0000000..2f803e4 --- /dev/null +++ b/v_windows/v/old/examples/vpwgen.v @@ -0,0 +1,25 @@ +import os +import os.cmdline +import crypto.rand +import strings + +fn main() { + blocksize := 256 + size := cmdline.option(os.args, '-size', '80').int() + repeats := cmdline.option(os.args, '-repeats', '4').int() + for _ in 0 .. repeats { + mut sb := strings.new_builder(blocksize) + for { + x := rand.read(blocksize) ? + for c in x { + if c >= `0` && c <= `~` { + sb.write_b(c) + } + } + if sb.len > size { + println(sb.str()[0..size]) + break + } + } + } +} diff --git a/v_windows/v/old/examples/vweb/file_upload/index.html b/v_windows/v/old/examples/vweb/file_upload/index.html new file mode 100644 index 0000000..a2bffad --- /dev/null +++ b/v_windows/v/old/examples/vweb/file_upload/index.html @@ -0,0 +1,6 @@ +

    File Upload

    + +
    + + +
    diff --git a/v_windows/v/old/examples/vweb/file_upload/upload.html b/v_windows/v/old/examples/vweb/file_upload/upload.html new file mode 100644 index 0000000..e80359d --- /dev/null +++ b/v_windows/v/old/examples/vweb/file_upload/upload.html @@ -0,0 +1,14 @@ + + +File amount: @fdata.len + +@for i, data in fdata + +

    Filename: @data.filename

    +

    Type: @data.content_type

    + +

    @{files[i]}

    + +@end + +
    Back diff --git a/v_windows/v/old/examples/vweb/file_upload/vweb_example.v b/v_windows/v/old/examples/vweb/file_upload/vweb_example.v new file mode 100644 index 0000000..bde55c3 --- /dev/null +++ b/v_windows/v/old/examples/vweb/file_upload/vweb_example.v @@ -0,0 +1,32 @@ +module main + +import vweb + +const ( + port = 8082 +) + +struct App { + vweb.Context +} + +fn main() { + vweb.run(&App{}, port) +} + +pub fn (mut app App) index() vweb.Result { + return $vweb.html() +} + +['/upload'; post] +pub fn (mut app App) upload() vweb.Result { + fdata := app.files['upfile'] + + mut files := []vweb.RawHtml{} + + for d in fdata { + files << d.data.replace_each(['\n', '
    ', '\n\r', '
    ', '\t', ' ', ' ', ' ']) + } + + return $vweb.html() +} diff --git a/v_windows/v/old/examples/vweb/index.html b/v_windows/v/old/examples/vweb/index.html new file mode 100644 index 0000000..d69ff78 --- /dev/null +++ b/v_windows/v/old/examples/vweb/index.html @@ -0,0 +1,15 @@ +Test app +
    +

    @hello

    +
    +@if show + show = true +@end + +@for number in numbers + @number
    +@end + + +
    +End. diff --git a/v_windows/v/old/examples/vweb/server_sent_events/assets/site.css b/v_windows/v/old/examples/vweb/server_sent_events/assets/site.css new file mode 100644 index 0000000..4ad9eb8 --- /dev/null +++ b/v_windows/v/old/examples/vweb/server_sent_events/assets/site.css @@ -0,0 +1,19 @@ +body { + font-family: Arial, Helvetica, sans-serif; + color: #eee; + background-color: #333; + background-image: url("v-logo.svg"); + background-repeat: no-repeat; + background-size: 10em; + margin: 0; + padding-left: 11em; +} + +h1 { + color: #6699CC; +} + +img.logo { + float: left; + width: 10em; +} diff --git a/v_windows/v/old/examples/vweb/server_sent_events/assets/v-logo.svg b/v_windows/v/old/examples/vweb/server_sent_events/assets/v-logo.svg new file mode 100644 index 0000000..9a4ec60 --- /dev/null +++ b/v_windows/v/old/examples/vweb/server_sent_events/assets/v-logo.svg @@ -0,0 +1 @@ + diff --git a/v_windows/v/old/examples/vweb/server_sent_events/favicon.ico b/v_windows/v/old/examples/vweb/server_sent_events/favicon.ico new file mode 100644 index 0000000..fa834c3 Binary files /dev/null and b/v_windows/v/old/examples/vweb/server_sent_events/favicon.ico differ diff --git a/v_windows/v/old/examples/vweb/server_sent_events/index.html b/v_windows/v/old/examples/vweb/server_sent_events/index.html new file mode 100644 index 0000000..7e500c9 --- /dev/null +++ b/v_windows/v/old/examples/vweb/server_sent_events/index.html @@ -0,0 +1,38 @@ + +
    + @title + + @css 'assets/site.css' +
    + +

    @title

    + +
      + + + diff --git a/v_windows/v/old/examples/vweb/server_sent_events/server.v b/v_windows/v/old/examples/vweb/server_sent_events/server.v new file mode 100644 index 0000000..01e63cb --- /dev/null +++ b/v_windows/v/old/examples/vweb/server_sent_events/server.v @@ -0,0 +1,39 @@ +module main + +import os +import rand +import time +import vweb +import vweb.sse + +struct App { + vweb.Context +} + +fn main() { + vweb.run(&App{}, 8081) +} + +pub fn (mut app App) init_server() { + app.serve_static('/favicon.ico', 'favicon.ico') + app.mount_static_folder_at(os.resource_abs_path('.'), '/') +} + +pub fn (mut app App) index() vweb.Result { + title := 'SSE Example' + return $vweb.html() +} + +fn (mut app App) sse() vweb.Result { + mut session := sse.new_connection(app.conn) + // NB: you can setup session.write_timeout and session.headers here + session.start() or { return app.server_error(501) } + session.send_message(data: 'ok') or { return app.server_error(501) } + for { + data := '{"time": "$time.now().str()", "random_id": "$rand.ulid()"}' + session.send_message(event: 'ping', data: data) or { return app.server_error(501) } + println('> sent event: $data') + time.sleep(1 * time.second) + } + return app.server_error(501) +} diff --git a/v_windows/v/old/examples/vweb/vweb_assets/assets/index.css b/v_windows/v/old/examples/vweb/vweb_assets/assets/index.css new file mode 100644 index 0000000..4ad9eb8 --- /dev/null +++ b/v_windows/v/old/examples/vweb/vweb_assets/assets/index.css @@ -0,0 +1,19 @@ +body { + font-family: Arial, Helvetica, sans-serif; + color: #eee; + background-color: #333; + background-image: url("v-logo.svg"); + background-repeat: no-repeat; + background-size: 10em; + margin: 0; + padding-left: 11em; +} + +h1 { + color: #6699CC; +} + +img.logo { + float: left; + width: 10em; +} diff --git a/v_windows/v/old/examples/vweb/vweb_assets/assets/v-logo.svg b/v_windows/v/old/examples/vweb/vweb_assets/assets/v-logo.svg new file mode 100644 index 0000000..9a4ec60 --- /dev/null +++ b/v_windows/v/old/examples/vweb/vweb_assets/assets/v-logo.svg @@ -0,0 +1 @@ + diff --git a/v_windows/v/old/examples/vweb/vweb_assets/favicon.ico b/v_windows/v/old/examples/vweb/vweb_assets/favicon.ico new file mode 100644 index 0000000..fa834c3 Binary files /dev/null and b/v_windows/v/old/examples/vweb/vweb_assets/favicon.ico differ diff --git a/v_windows/v/old/examples/vweb/vweb_assets/index.html b/v_windows/v/old/examples/vweb/vweb_assets/index.html new file mode 100644 index 0000000..6635329 --- /dev/null +++ b/v_windows/v/old/examples/vweb/vweb_assets/index.html @@ -0,0 +1,12 @@ + +
      + @title + @css 'index.css' +
      + +

      @title

      +

      @subtitle

      +

      @message

      + + + diff --git a/v_windows/v/old/examples/vweb/vweb_assets/vweb_assets.v b/v_windows/v/old/examples/vweb/vweb_assets/vweb_assets.v new file mode 100644 index 0000000..058f459 --- /dev/null +++ b/v_windows/v/old/examples/vweb/vweb_assets/vweb_assets.v @@ -0,0 +1,40 @@ +module main + +import vweb +// import vweb.assets +import time + +const ( + port = 8081 +) + +struct App { + vweb.Context +} + +fn main() { + mut app := &App{} + app.serve_static('/favicon.ico', 'favicon.ico') + // Automatically make available known static mime types found in given directory. + app.handle_static('assets', true) + vweb.run(app, port) +} + +pub fn (mut app App) index() vweb.Result { + // We can dynamically specify which assets are to be used in template. + // mut am := assets.new_manager() + // am.add_css('assets/index.css') + // css := am.include_css(false) + title := 'VWeb Assets Example' + subtitle := 'VWeb can serve static assets too!' + message := 'It also has an Assets Manager that allows dynamically specifying which CSS and JS files to be used.' + return $vweb.html() +} + +fn (mut app App) text() vweb.Result { + return app.Context.text('Hello, world from vweb!') +} + +fn (mut app App) time() vweb.Result { + return app.Context.text(time.now().format()) +} diff --git a/v_windows/v/old/examples/vweb/vweb_example.v b/v_windows/v/old/examples/vweb/vweb_example.v new file mode 100644 index 0000000..020a2d8 --- /dev/null +++ b/v_windows/v/old/examples/vweb/vweb_example.v @@ -0,0 +1,58 @@ +module main + +import vweb +import rand + +const ( + port = 8082 +) + +struct App { + vweb.Context +mut: + state shared State +} + +struct State { +mut: + cnt int +} + +fn main() { + println('vweb example') + vweb.run(&App{}, port) +} + +pub fn (mut app App) init_server() { + app.handle_static('.', false) +} + +['/users/:user'] +pub fn (mut app App) user_endpoint(user string) vweb.Result { + id := rand.intn(100) + return app.json('{"$user": $id}') +} + +pub fn (mut app App) index() vweb.Result { + lock app.state { + app.state.cnt++ + } + show := true + hello := 'Hello world from vweb' + numbers := [1, 2, 3] + return $vweb.html() +} + +pub fn (mut app App) show_text() vweb.Result { + return app.text('Hello world from vweb') +} + +pub fn (mut app App) cookie() vweb.Result { + app.set_cookie(name: 'cookie', value: 'test') + return app.text('Response Headers\n$app.header') +} + +[post] +pub fn (mut app App) post() vweb.Result { + return app.text('Post body: $app.req.data') +} diff --git a/v_windows/v/old/examples/web_crawler/README.md b/v_windows/v/old/examples/web_crawler/README.md new file mode 100644 index 0000000..c8a741f --- /dev/null +++ b/v_windows/v/old/examples/web_crawler/README.md @@ -0,0 +1,22 @@ +# web_crawler +web_crawler is a very simple web crawler. +This web crawler fetches news from tuicool.com, +(a chinese site similar to hacker-news.firebaseio.com). + +# Compile and Run + +Use this to generate an executable, and then launch the web crawler: +```bash +v web_crawler.v +./web_crawler +``` + +And this to compile and launch the web crawler directly: +```bash +v run web_crawler.v +``` + +This project shows how to use http.fetch() to get http.Response, +and then html.parse() to parse the returned html. + +It's easy, isn't it? diff --git a/v_windows/v/old/examples/web_crawler/web_crawler.v b/v_windows/v/old/examples/web_crawler/web_crawler.v new file mode 100644 index 0000000..e32de54 --- /dev/null +++ b/v_windows/v/old/examples/web_crawler/web_crawler.v @@ -0,0 +1,24 @@ +import net.http +import net.html + +fn main() { + // http.fetch() sends an HTTP request to the URL with the given method and configurations. + config := http.FetchConfig{ + user_agent: 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0' + } + resp := http.fetch('https://tuicool.com', config) or { + println('failed to fetch data from the server') + return + } + // html.parse() parses and returns the DOM from the given text. + mut doc := html.parse(resp.text) + // html.DocumentObjectModel.get_tag_by_attribute_value() retrieves all the tags in the document that has the given attribute name and value. + tags := doc.get_tag_by_attribute_value('class', 'list_article_item') + for tag in tags { + href := tag.children[0].attributes['href'] or { panic('key not found') } + title := tag.children[0].attributes['title'] or { panic('key not found') } + println('href: $href') + println('title: $title') + println('') + } +} diff --git a/v_windows/v/old/examples/websocket/client-server/client.v b/v_windows/v/old/examples/websocket/client-server/client.v new file mode 100644 index 0000000..b039415 --- /dev/null +++ b/v_windows/v/old/examples/websocket/client-server/client.v @@ -0,0 +1,54 @@ +module main + +import os +import net.websocket +import term + +// This client should be compiled an run in different consoles +// it connects to the server who will broadcast your messages +// to all other connected clients +fn main() { + mut ws := start_client() ? + println(term.green('client $ws.id ready')) + println('Write message and enter to send...') + for { + line := os.get_line() + if line == '' { + break + } + ws.write_string(line) ? + } + ws.close(1000, 'normal') or { println(term.red('panicing $err')) } + unsafe { + ws.free() + } +} + +fn start_client() ?&websocket.Client { + mut ws := websocket.new_client('ws://localhost:30000') ? + // mut ws := websocket.new_client('wss://echo.websocket.org:443')? + // use on_open_ref if you want to send any reference object + ws.on_open(fn (mut ws websocket.Client) ? { + println(term.green('websocket connected to the server and ready to send messages...')) + }) + // use on_error_ref if you want to send any reference object + ws.on_error(fn (mut ws websocket.Client, err string) ? { + println(term.red('error: $err')) + }) + // use on_close_ref if you want to send any reference object + ws.on_close(fn (mut ws websocket.Client, code int, reason string) ? { + println(term.green('the connection to the server successfully closed')) + }) + // on new messages from other clients, display them in blue text + ws.on_message(fn (mut ws websocket.Client, msg &websocket.Message) ? { + if msg.payload.len > 0 { + message := msg.payload.bytestr() + println(term.blue('$message')) + } + }) + + ws.connect() or { println(term.red('error on connect: $err')) } + + go ws.listen() // or { println(term.red('error on listen $err')) } + return ws +} diff --git a/v_windows/v/old/examples/websocket/client-server/server.v b/v_windows/v/old/examples/websocket/client-server/server.v new file mode 100644 index 0000000..db41913 --- /dev/null +++ b/v_windows/v/old/examples/websocket/client-server/server.v @@ -0,0 +1,43 @@ +module main + +import net.websocket +import term + +// this server accepts client connections and broadcast all messages to other connected clients +fn main() { + println('press ctrl-c to quit...') + start_server() ? +} + +fn start_server() ? { + mut s := websocket.new_server(.ip6, 30000, '') + // Make that in execution test time give time to execute at least one time + s.ping_interval = 100 + s.on_connect(fn (mut s websocket.ServerClient) ?bool { + // Here you can look att the client info and accept or not accept + // just returning a true/false + if s.resource_name != '/' { + return false + } + return true + }) ? + + // on_message_ref, broadcast all incoming messages to all clients except the one sent it + s.on_message_ref(fn (mut ws websocket.Client, msg &websocket.Message, mut m websocket.Server) ? { + // for _, cli in m.clients { + for i, _ in m.clients { + mut c := m.clients[i] + if c.client.state == .open && c.client.id != ws.id { + c.client.write(msg.payload, websocket.OPCode.text_frame) or { panic(err) } + } + } + }, s) + + s.on_close(fn (mut ws websocket.Client, code int, reason string) ? { + println(term.green('client ($ws.id) closed connection')) + }) + s.listen() or { println(term.red('error on server listen: $err')) } + unsafe { + s.free() + } +} diff --git a/v_windows/v/old/examples/websocket/ping.v b/v_windows/v/old/examples/websocket/ping.v new file mode 100644 index 0000000..3599ec3 --- /dev/null +++ b/v_windows/v/old/examples/websocket/ping.v @@ -0,0 +1,86 @@ +module main + +import time +import os +import net.websocket + +fn main() { + println('press enter to quit...\n') + go start_server() + time.sleep(100 * time.millisecond) + go start_client() + os.get_line() +} + +// start_server starts the websocket server, it receives messages +// and send it back to the client that sent it +fn start_server() ? { + mut s := websocket.new_server(.ip6, 30000, '') + // Make that in execution test time give time to execute at least one time + s.ping_interval = 100 + s.on_connect(fn (mut s websocket.ServerClient) ?bool { + // Here you can look att the client info and accept or not accept + // just returning a true/false + if s.resource_name != '/' { + return false + } + return true + }) ? + s.on_message(fn (mut ws websocket.Client, msg &websocket.Message) ? { + ws.write(msg.payload, msg.opcode) or { panic(err) } + }) + s.on_close(fn (mut ws websocket.Client, code int, reason string) ? { + // println('client ($ws.id) closed connection') + }) + s.listen() or { println('error on server listen: $err') } + unsafe { + s.free() + } +} + +// start_client starts the websocket client, it writes a message to +// the server and prints all the messages received +fn start_client() ? { + mut ws := websocket.new_client('ws://localhost:30000') ? + // mut ws := websocket.new_client('wss://echo.websocket.org:443')? + // use on_open_ref if you want to send any reference object + ws.on_open(fn (mut ws websocket.Client) ? { + println('open!') + }) + // use on_error_ref if you want to send any reference object + ws.on_error(fn (mut ws websocket.Client, err string) ? { + println('error: $err') + }) + // use on_close_ref if you want to send any reference object + ws.on_close(fn (mut ws websocket.Client, code int, reason string) ? { + println('closed') + }) + // use on_message_ref if you want to send any reference object + ws.on_message(fn (mut ws websocket.Client, msg &websocket.Message) ? { + if msg.payload.len > 0 { + message := msg.payload.bytestr() + println('client got type: $msg.opcode payload:\n$message') + } + }) + // you can add any pointer reference to use in callback + // t := TestRef{count: 10} + // ws.on_message_ref(fn (mut ws websocket.Client, msg &websocket.Message, r &SomeRef)? { + // // println('type: $msg.opcode payload:\n$msg.payload ref: $r') + // }, &r) + ws.connect() or { println('error on connect: $err') } + go write_echo(mut ws) // or { println('error on write_echo $err') } + ws.listen() or { println('error on listen $err') } + unsafe { + ws.free() + } +} + +fn write_echo(mut ws websocket.Client) ? { + message := 'echo this' + for i := 0; i <= 10; i++ { + // Server will send pings every 30 seconds + ws.write_string(message) or { println('panicing writing $err') } + time.sleep(100 * time.millisecond) + } + ws.close(1000, 'normal') or { println('panicing $err') } +} diff --git a/v_windows/v/old/examples/word_counter/README.md b/v_windows/v/old/examples/word_counter/README.md new file mode 100644 index 0000000..1316ad8 --- /dev/null +++ b/v_windows/v/old/examples/word_counter/README.md @@ -0,0 +1,25 @@ +``` +usage: word_counter [text_file] +using cinderella.txt +a => 25 +able => 2 +after => 1 +afterwards => 1 +again => 10 +against => 2 +all => 12 +allow => 1 +allowed => 2 +along => 1 +also => 2 +always => 2 +an => 4 +and => 140 +anew => 1 +anger => 1 +another => 2 +answered => 1 +any => 1 +anyone => 2 +... +``` diff --git a/v_windows/v/old/examples/word_counter/cinderella.txt b/v_windows/v/old/examples/word_counter/cinderella.txt new file mode 100644 index 0000000..7399d01 --- /dev/null +++ b/v_windows/v/old/examples/word_counter/cinderella.txt @@ -0,0 +1,250 @@ +A rich man's wife became sick, and when she felt that her end was drawing near, +she called her only daughter to her bedside and said, "Dear child, remain pious +and good, and then our dear God will always protect you, and I will look down +on you from heaven and be near you." With this she closed her eyes and died. + +The girl went out to her mother's grave every day and wept, and she remained +pious and good. When winter came the snow spread a white cloth over the grave, +and when the spring sun had removed it again, the man took himself another +wife. + +This wife brought two daughters into the house with her. They were beautiful, +with fair faces, but evil and dark hearts. Times soon grew very bad for the +poor stepchild. + +"Why should that stupid goose sit in the parlor with us?" they said. "If she +wants to eat bread, then she will have to earn it. Out with this kitchen maid!" + +They took her beautiful clothes away from her, dressed her in an old gray +smock, and gave her wooden shoes. "Just look at the proud princess! How decked +out she is!" they shouted and laughed as they led her into the kitchen. + +There she had to do hard work from morning until evening, get up before +daybreak, carry water, make the fires, cook, and wash. Besides this, the +sisters did everything imaginable to hurt her. They made fun of her, scattered +peas and lentils into the ashes for her, so that she had to sit and pick them +out again. In the evening when she had worked herself weary, there was no bed +for her. Instead she had to sleep by the hearth in the ashes. And because she +always looked dusty and dirty, they called her Cinderella. + +One day it happened that the father was going to the fair, and he asked his two +stepdaughters what he should bring back for them. + +"Beautiful dresses," said the one. + +"Pearls and jewels," said the other. + +"And you, Cinderella," he said, "what do you want?" + +"Father, break off for me the first twig that brushes against your hat on your +way home." + +So he bought beautiful dresses, pearls, and jewels for his two stepdaughters. +On his way home, as he was riding through a green thicket, a hazel twig brushed +against him and knocked off his hat. Then he broke off the twig and took it +with him. Arriving home, he gave his stepdaughters the things that they had +asked for, and he gave Cinderella the twig from the hazel bush. + +Cinderella thanked him, went to her mother's grave, and planted the branch on +it, and she wept so much that her tears fell upon it and watered it. It grew +and became a beautiful tree. + +Cinderella went to this tree three times every day, and beneath it she wept and +prayed. A white bird came to the tree every time, and whenever she expressed a +wish, the bird would throw down to her what she had wished for. + +Now it happened that the king proclaimed a festival that was to last three +days. All the beautiful young girls in the land were invited, so that his son +could select a bride for himself. When the two stepsisters heard that they too +had been invited, they were in high spirits. + +They called Cinderella, saying, "Comb our hair for us. Brush our shoes and +fasten our buckles. We are going to the festival at the king's castle." + +Cinderella obeyed, but wept, because she too would have liked to go to the +dance with them. She begged her stepmother to allow her to go. + +"You, Cinderella?" she said. "You, all covered with dust and dirt, and you want +to go to the festival?. You have neither clothes nor shoes, and yet you want to +dance!" + +However, because Cinderella kept asking, the stepmother finally said, "I have +scattered a bowl of lentils into the ashes for you. If you can pick them out +again in two hours, then you may go with us." + +The girl went through the back door into the garden, and called out, "You tame +pigeons, you turtledoves, and all you birds beneath the sky, come and help me +to gather: + +The good ones go into the pot, The bad ones go into your crop." Two white +pigeons came in through the kitchen window, and then the turtledoves, and +finally all the birds beneath the sky came whirring and swarming in, and lit +around the ashes. The pigeons nodded their heads and began to pick, pick, pick, +pick. And the others also began to pick, pick, pick, pick. They gathered all +the good grains into the bowl. Hardly one hour had passed before they were +finished, and they all flew out again. The girl took the bowl to her +stepmother, and was happy, thinking that now she would be allowed to go to the +festival with them. + +But the stepmother said, "No, Cinderella, you have no clothes, and you don't +know how to dance. Everyone would only laugh at you." + +Cinderella began to cry, and then the stepmother said, "You may go if you are +able to pick two bowls of lentils out of the ashes for me in one hour," +thinking to herself, "She will never be able to do that." + +The girl went through the back door into the garden, and called out, "You tame +pigeons, you turtledoves, and all you birds beneath the sky, come and help me +to gather: + +The good ones go into the pot, The bad ones go into your crop." Two white +pigeons came in through the kitchen window, and then the turtledoves, and +finally all the birds beneath the sky came whirring and swarming in, and lit +around the ashes. The pigeons nodded their heads and began to pick, pick, pick, +pick. And the others also began to pick, pick, pick, pick. They gathered all +the good grains into the bowls. Before a half hour had passed they were +finished, and they all flew out again. The girl took the bowls to her +stepmother, and was happy, thinking that now she would be allowed to go to the +festival with them. + +But the stepmother said, "It's no use. You are not coming with us, for you have +no clothes, and you don't know how to dance. We would be ashamed of you." With +this she turned her back on Cinderella, and hurried away with her two proud +daughters. + +Now that no one else was at home, Cinderella went to her mother's grave beneath +the hazel tree, and cried out: + +Shake and quiver, little tree, Throw gold and silver down to me. Then the bird +threw a gold and silver dress down to her, and slippers embroidered with silk +and silver. She quickly put on the dress and went to the festival. Her +stepsisters and her stepmother did not recognize her. They thought she must be +a foreign princess, for she looked so beautiful in the golden dress. They never +once thought it was Cinderella, for they thought that she was sitting at home +in the dirt, looking for lentils in the ashes. + +The prince approached her, took her by the hand, and danced with her. +Furthermore, he would dance with no one else. He never let go of her hand, and +whenever anyone else came and asked her to dance, he would say, "She is my +dance partner." + +She danced until evening, and then she wanted to go home. But the prince said, +"I will go along and escort you," for he wanted to see to whom the beautiful +girl belonged. However, she eluded him and jumped into the pigeon coop. The +prince waited until her father came, and then he told him that the unknown girl +had jumped into the pigeon coop. + +The old man thought, "Could it be Cinderella?" + +He had them bring him an ax and a pick so that he could break the pigeon coop +apart, but no one was inside. When they got home Cinderella was lying in the +ashes, dressed in her dirty clothes. A dim little oil-lamp was burning in the +fireplace. Cinderella had quickly jumped down from the back of the pigeon coop +and had run to the hazel tree. There she had taken off her beautiful clothes +and laid them on the grave, and the bird had taken them away again. Then, +dressed in her gray smock, she had returned to the ashes in the kitchen. + +The next day when the festival began anew, and her parents and her stepsisters +had gone again, Cinderella went to the hazel tree and said: + +Shake and quiver, little tree, Throw gold and silver down to me. Then the bird +threw down an even more magnificent dress than on the preceding day. When +Cinderella appeared at the festival in this dress, everyone was astonished at +her beauty. The prince had waited until she came, then immediately took her by +the hand, and danced only with her. When others came and asked her to dance +with them, he said, "She is my dance partner." When evening came she wanted to +leave, and the prince followed her, wanting to see into which house she went. +But she ran away from him and into the garden behind the house. A beautiful +tall tree stood there, on which hung the most magnificent pears. She climbed as +nimbly as a squirrel into the branches, and the prince did not know where she +had gone. He waited until her father came, then said to him, "The unknown girl +has eluded me, and I believe she has climbed up the pear tree. + +The father thought, "Could it be Cinderella?" He had an ax brought to him and +cut down the tree, but no one was in it. When they came to the kitchen, +Cinderella was lying there in the ashes as usual, for she had jumped down from +the other side of the tree, had taken the beautiful dress back to the bird in +the hazel tree, and had put on her gray smock. + +On the third day, when her parents and sisters had gone away, Cinderella went +again to her mother's grave and said to the tree: + +Shake and quiver, little tree, Throw gold and silver down to me. This time the +bird threw down to her a dress that was more splendid and magnificent than any +she had yet had, and the slippers were of pure gold. When she arrived at the +festival in this dress, everyone was so astonished that they did not know what +to say. The prince danced only with her, and whenever anyone else asked her to +dance, he would say, "She is my dance partner." When evening came Cinderella +wanted to leave, and the prince tried to escort her, but she ran away from him +so quickly that he could not follow her. The prince, however, had set a trap. +He had had the entire stairway smeared with pitch. When she ran down the +stairs, her left slipper stuck in the pitch. The prince picked it up. It was +small and dainty, and of pure gold. + +The next morning, he went with it to the man, and said to him, "No one shall be +my wife except for the one whose foot fits this golden shoe." + +The two sisters were happy to hear this, for they had pretty feet. With her +mother standing by, the older one took the shoe into her bedroom to try it on. +She could not get her big toe into it, for the shoe was too small for her. Then +her mother gave her a knife and said, "Cut off your toe. When you are queen you +will no longer have to go on foot." + +The girl cut off her toe, forced her foot into the shoe, swallowed the pain, +and went out to the prince. He took her on his horse as his bride and rode away +with her. However, they had to ride past the grave, and there, on the hazel +tree, sat the two pigeons, crying out: + +Rook di goo, rook di goo! There's blood in the shoe. The shoe is too tight, +This bride is not right! Then he looked at her foot and saw how the blood was +running from it. He turned his horse around and took the false bride home +again, saying that she was not the right one, and that the other sister should +try on the shoe. She went into her bedroom, and got her toes into the shoe all +right, but her heel was too large. Then her mother gave her a knife, and said, +"Cut a piece off your heel. When you are queen you will no longer have to go on +foot." + +The girl cut a piece off her heel, forced her foot into the shoe, swallowed the +pain, and went out to the prince. He took her on his horse as his bride and +rode away with her. When they passed the hazel tree, the two pigeons were +sitting in it, and they cried out: + +Rook di goo, rook di goo! There's blood in the shoe. The shoe is too tight, +This bride is not right! He looked down at her foot and saw how the blood was +running out of her shoe, and how it had stained her white stocking all red. +Then he turned his horse around and took the false bride home again. "This is +not the right one, either," he said. "Don't you have another daughter?" + +"No," said the man. "There is only a deformed little Cinderella from my first +wife, but she cannot possibly be the bride." + +The prince told him to send her to him, but the mother answered, "Oh, no, she +is much too dirty. She cannot be seen." + +But the prince insisted on it, and they had to call Cinderella. She first +washed her hands and face clean, and then went and bowed down before the +prince, who gave her the golden shoe. She sat down on a stool, pulled her foot +out of the heavy wooden shoe, and put it into the slipper, and it fitted her +perfectly. + +When she stood up the prince looked into her face, and he recognized the +beautiful girl who had danced with him. He cried out, "She is my true bride." + +The stepmother and the two sisters were horrified and turned pale with anger. +The prince, however, took Cinderella onto his horse and rode away with her. As +they passed by the hazel tree, the two white pigeons cried out: + +Rook di goo, rook di goo! No blood's in the shoe. The shoe's not too tight, +This bride is right! After they had cried this out, they both flew down and +lit on Cinderella's shoulders, one on the right, the other on the left, and +remained sitting there. When the wedding with the prince was to be held, the +two false sisters came, wanting to gain favor with Cinderella and to share her +good fortune. When the bridal couple walked into the church, the older sister +walked on their right side and the younger on their left side, and the pigeons +pecked out one eye from each of them. Afterwards, as they came out of the +church, the older one was on the left side, and the younger one on the right +side, and then the pigeons pecked out the other eye from each of them. And +thus, for their wickedness and falsehood, they were punished with blindness as +long as they lived. + + diff --git a/v_windows/v/old/examples/word_counter/word_counter.v b/v_windows/v/old/examples/word_counter/word_counter.v new file mode 100644 index 0000000..fc60a6f --- /dev/null +++ b/v_windows/v/old/examples/word_counter/word_counter.v @@ -0,0 +1,70 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +import os + +fn main() { + mut path := 'cinderella.txt' + if os.args.len != 2 { + println('usage: word_counter [text_file]') + println('using $path') + } else { + path = os.args[1] + } + contents := os.read_file(path.trim_space()) or { + println('failed to open $path') + return + } + mut m := map[string]int{} + for word in extract_words(contents) { + m[word]++ + } + // Sort the keys + mut keys := m.keys() + keys.sort() + // Print the map + for key in keys { + val := m[key] + println('$key => $val') + } +} + +// Creates an array of words from a given string +fn extract_words(contents string) []string { + mut splitted := []string{} + for space_splitted in contents.to_lower().split(' ') { + if space_splitted.contains('\n') { + splitted << space_splitted.split('\n') + } else { + splitted << space_splitted + } + } + + mut results := []string{} + for s in splitted { + result := filter_word(s) + if result == '' { + continue + } + results << result + } + + return results +} + +// Removes punctuation +fn filter_word(word string) string { + if word == '' || word == ' ' { + return '' + } + mut i := 0 + for i < word.len && !word[i].is_letter() { + i++ + } + start := i + for i < word.len && word[i].is_letter() { + i++ + } + end := i + return word[start..end] +} diff --git a/v_windows/v/old/thirdparty/.gitignore b/v_windows/v/old/thirdparty/.gitignore new file mode 100644 index 0000000..c964467 --- /dev/null +++ b/v_windows/v/old/thirdparty/.gitignore @@ -0,0 +1,11 @@ +!glfw/glfw3.dll +!glfw/msvc/glfw3.lib + +freetype/ +sdl2/ +SDL2_image/ +SDL2_mixer/ +SDL2_ttf/ +pg/ +tcc/ +sqlite/ diff --git a/v_windows/v/old/thirdparty/bignum/README.md b/v_windows/v/old/thirdparty/bignum/README.md new file mode 100644 index 0000000..04eb555 --- /dev/null +++ b/v_windows/v/old/thirdparty/bignum/README.md @@ -0,0 +1,2 @@ +This folder contains bn.h and bn.c files +from https://github.com/kokke/tiny-bignum-c diff --git a/v_windows/v/old/thirdparty/bignum/bn.c b/v_windows/v/old/thirdparty/bignum/bn.c new file mode 100644 index 0000000..7ec0895 --- /dev/null +++ b/v_windows/v/old/thirdparty/bignum/bn.c @@ -0,0 +1,664 @@ +/* + +Big number library - arithmetic on multiple-precision unsigned integers. + +This library is an implementation of arithmetic on arbitrarily large integers. + +The difference between this and other implementations, is that the data structure +has optimal memory utilization (i.e. a 1024 bit integer takes up 128 bytes RAM), +and all memory is allocated statically: no dynamic allocation for better or worse. + +Primary goals are correctness, clarity of code and clean, portable implementation. +Secondary goal is a memory footprint small enough to make it suitable for use in +embedded applications. + + +The current state is correct functionality and adequate performance. +There may well be room for performance-optimizations and improvements. + +*/ + +#include +#include +#include +#include "bn.h" + + + +/* Functions for shifting number in-place. */ +static void _lshift_one_bit(struct bn* a); +static void _rshift_one_bit(struct bn* a); +static void _lshift_word(struct bn* a, int nwords); +static void _rshift_word(struct bn* a, int nwords); + + + +/* Public / Exported functions. */ +void bignum_init(struct bn* n) +{ + require(n, "n is null"); + + int i; + for (i = 0; i < BN_ARRAY_SIZE; ++i) + { + n->array[i] = 0; + } +} + + +void bignum_from_int(struct bn* n, DTYPE_TMP i) +{ + require(n, "n is null"); + + bignum_init(n); + + /* Endianness issue if machine is not little-endian? */ +#ifdef WORD_SIZE + #if (WORD_SIZE == 1) + n->array[0] = (i & 0x000000ff); + n->array[1] = (i & 0x0000ff00) >> 8; + n->array[2] = (i & 0x00ff0000) >> 16; + n->array[3] = (i & 0xff000000) >> 24; + #elif (WORD_SIZE == 2) + n->array[0] = (i & 0x0000ffff); + n->array[1] = (i & 0xffff0000) >> 16; + #elif (WORD_SIZE == 4) + n->array[0] = i; + DTYPE_TMP num_32 = 32; + DTYPE_TMP tmp = i >> num_32; /* bit-shift with U64 operands to force 64-bit results */ + n->array[1] = tmp; + #endif +#endif +} + + +int bignum_to_int(struct bn* n) +{ + require(n, "n is null"); + + int ret = 0; + + /* Endianness issue if machine is not little-endian? */ +#if (WORD_SIZE == 1) + ret += n->array[0]; + ret += n->array[1] << 8; + ret += n->array[2] << 16; + ret += n->array[3] << 24; +#elif (WORD_SIZE == 2) + ret += n->array[0]; + ret += n->array[1] << 16; +#elif (WORD_SIZE == 4) + ret += n->array[0]; +#endif + + return ret; +} + + +void bignum_from_string(struct bn* n, char* str, int nbytes) +{ + require(n, "n is null"); + require(str, "str is null"); + require(nbytes > 0, "nbytes must be positive"); + require((nbytes & 1) == 0, "string format must be in hex -> equal number of bytes"); + + bignum_init(n); + + DTYPE tmp; /* DTYPE is defined in bn.h - uint{8,16,32,64}_t */ + int i = nbytes - (2 * WORD_SIZE); /* index into string */ + int j = 0; /* index into array */ + + /* reading last hex-byte "MSB" from string first -> big endian */ + /* MSB ~= most significant byte / block ? :) */ + while (i >= 0) + { + tmp = 0; + sscanf(&str[i], SSCANF_FORMAT_STR, &tmp); + n->array[j] = tmp; + i -= (2 * WORD_SIZE); /* step WORD_SIZE hex-byte(s) back in the string. */ + j += 1; /* step one element forward in the array. */ + } +} + + +void bignum_to_string(struct bn* n, char* str, int nbytes) +{ + require(n, "n is null"); + require(str, "str is null"); + require(nbytes > 0, "nbytes must be positive"); + require((nbytes & 1) == 0, "string format must be in hex -> equal number of bytes"); + + int j = BN_ARRAY_SIZE - 1; /* index into array - reading "MSB" first -> big-endian */ + int i = 0; /* index into string representation. */ + + /* reading last array-element "MSB" first -> big endian */ + while ((j >= 0) && (nbytes > (i + 1))) + { + sprintf(&str[i], SPRINTF_FORMAT_STR, n->array[j]); + i += (2 * WORD_SIZE); /* step WORD_SIZE hex-byte(s) forward in the string. */ + j -= 1; /* step one element back in the array. */ + } + + /* Count leading zeros: */ + j = 0; + while (str[j] == '0') + { + j += 1; + } + + /* Move string j places ahead, effectively skipping leading zeros */ + for (i = 0; i < (nbytes - j); ++i) + { + str[i] = str[i + j]; + } + + /* Zero-terminate string */ + str[i] = 0; +} + + +void bignum_dec(struct bn* n) +{ + require(n, "n is null"); + + DTYPE tmp; /* copy of n */ + DTYPE res; + + int i; + for (i = 0; i < BN_ARRAY_SIZE; ++i) + { + tmp = n->array[i]; + res = tmp - 1; + n->array[i] = res; + + if (!(res > tmp)) + { + break; + } + } +} + + +void bignum_inc(struct bn* n) +{ + require(n, "n is null"); + + DTYPE res; + DTYPE_TMP tmp; /* copy of n */ + + int i; + for (i = 0; i < BN_ARRAY_SIZE; ++i) + { + tmp = n->array[i]; + res = tmp + 1; + n->array[i] = res; + + if (res > tmp) + { + break; + } + } +} + + +void bignum_add(struct bn* a, struct bn* b, struct bn* c) +{ + require(a, "a is null"); + require(b, "b is null"); + require(c, "c is null"); + + DTYPE_TMP tmp; + int carry = 0; + int i; + for (i = 0; i < BN_ARRAY_SIZE; ++i) + { + tmp = (DTYPE_TMP)a->array[i] + b->array[i] + carry; + carry = (tmp > MAX_VAL); + c->array[i] = (tmp & MAX_VAL); + } +} + + +void bignum_sub(struct bn* a, struct bn* b, struct bn* c) +{ + require(a, "a is null"); + require(b, "b is null"); + require(c, "c is null"); + + DTYPE_TMP res; + DTYPE_TMP tmp1; + DTYPE_TMP tmp2; + int borrow = 0; + int i; + for (i = 0; i < BN_ARRAY_SIZE; ++i) + { + tmp1 = (DTYPE_TMP)a->array[i] + (MAX_VAL + 1); /* + number_base */ + tmp2 = (DTYPE_TMP)b->array[i] + borrow;; + res = (tmp1 - tmp2); + c->array[i] = (DTYPE)(res & MAX_VAL); /* "modulo number_base" == "% (number_base - 1)" if number_base is 2^N */ + borrow = (res <= MAX_VAL); + } +} + + +void bignum_mul(struct bn* a, struct bn* b, struct bn* c) +{ + require(a, "a is null"); + require(b, "b is null"); + require(c, "c is null"); + + struct bn row; + struct bn tmp; + int i, j; + + bignum_init(c); + + for (i = 0; i < BN_ARRAY_SIZE; ++i) + { + bignum_init(&row); + + for (j = 0; j < BN_ARRAY_SIZE; ++j) + { + if (i + j < BN_ARRAY_SIZE) + { + bignum_init(&tmp); + DTYPE_TMP intermediate = ((DTYPE_TMP)a->array[i] * (DTYPE_TMP)b->array[j]); + bignum_from_int(&tmp, intermediate); + _lshift_word(&tmp, i + j); + bignum_add(&tmp, &row, &row); + } + } + bignum_add(c, &row, c); + } +} + + +void bignum_div(struct bn* a, struct bn* b, struct bn* c) +{ + require(a, "a is null"); + require(b, "b is null"); + require(c, "c is null"); + + struct bn current; + struct bn denom; + struct bn tmp; + + bignum_from_int(¤t, 1); // int current = 1; + bignum_assign(&denom, b); // denom = b + bignum_assign(&tmp, a); // tmp = a + + const DTYPE_TMP half_max = 1 + (DTYPE_TMP)(MAX_VAL / 2); + bool overflow = false; + while (bignum_cmp(&denom, a) != LARGER) // while (denom <= a) { + { + if (denom.array[BN_ARRAY_SIZE - 1] >= half_max) + { + overflow = true; + break; + } + _lshift_one_bit(¤t); // current <<= 1; + _lshift_one_bit(&denom); // denom <<= 1; + } + if (!overflow) + { + _rshift_one_bit(&denom); // denom >>= 1; + _rshift_one_bit(¤t); // current >>= 1; + } + bignum_init(c); // int answer = 0; + + while (!bignum_is_zero(¤t)) // while (current != 0) + { + if (bignum_cmp(&tmp, &denom) != SMALLER) // if (dividend >= denom) + { + bignum_sub(&tmp, &denom, &tmp); // dividend -= denom; + bignum_or(c, ¤t, c); // answer |= current; + } + _rshift_one_bit(¤t); // current >>= 1; + _rshift_one_bit(&denom); // denom >>= 1; + } // return answer; +} + + +void bignum_lshift(struct bn* a, struct bn* b, int nbits) +{ + require(a, "a is null"); + require(b, "b is null"); + require(nbits >= 0, "no negative shifts"); + + bignum_assign(b, a); + /* Handle shift in multiples of word-size */ + const int nbits_pr_word = (WORD_SIZE * 8); + int nwords = nbits / nbits_pr_word; + if (nwords != 0) + { + _lshift_word(b, nwords); + nbits -= (nwords * nbits_pr_word); + } + + if (nbits != 0) + { + int i; + for (i = (BN_ARRAY_SIZE - 1); i > 0; --i) + { + b->array[i] = (b->array[i] << nbits) | (b->array[i - 1] >> ((8 * WORD_SIZE) - nbits)); + } + b->array[i] <<= nbits; + } +} + + +void bignum_rshift(struct bn* a, struct bn* b, int nbits) +{ + require(a, "a is null"); + require(b, "b is null"); + require(nbits >= 0, "no negative shifts"); + + bignum_assign(b, a); + /* Handle shift in multiples of word-size */ + const int nbits_pr_word = (WORD_SIZE * 8); + int nwords = nbits / nbits_pr_word; + if (nwords != 0) + { + _rshift_word(b, nwords); + nbits -= (nwords * nbits_pr_word); + } + + if (nbits != 0) + { + int i; + for (i = 0; i < (BN_ARRAY_SIZE - 1); ++i) + { + b->array[i] = (b->array[i] >> nbits) | (b->array[i + 1] << ((8 * WORD_SIZE) - nbits)); + } + b->array[i] >>= nbits; + } + +} + + +void bignum_mod(struct bn* a, struct bn* b, struct bn* c) +{ + /* + Take divmod and throw away div part + */ + require(a, "a is null"); + require(b, "b is null"); + require(c, "c is null"); + + struct bn tmp; + + bignum_divmod(a,b,&tmp,c); +} + +void bignum_divmod(struct bn* a, struct bn* b, struct bn* c, struct bn* d) +{ + /* + Puts a%b in d + and a/b in c + + mod(a,b) = a - ((a / b) * b) + + example: + mod(8, 3) = 8 - ((8 / 3) * 3) = 2 + */ + require(a, "a is null"); + require(b, "b is null"); + require(c, "c is null"); + + struct bn tmp; + + /* c = (a / b) */ + bignum_div(a, b, c); + + /* tmp = (c * b) */ + bignum_mul(c, b, &tmp); + + /* c = a - tmp */ + bignum_sub(a, &tmp, d); +} + + +void bignum_and(struct bn* a, struct bn* b, struct bn* c) +{ + require(a, "a is null"); + require(b, "b is null"); + require(c, "c is null"); + + int i; + for (i = 0; i < BN_ARRAY_SIZE; ++i) + { + c->array[i] = (a->array[i] & b->array[i]); + } +} + + +void bignum_or(struct bn* a, struct bn* b, struct bn* c) +{ + require(a, "a is null"); + require(b, "b is null"); + require(c, "c is null"); + + int i; + for (i = 0; i < BN_ARRAY_SIZE; ++i) + { + c->array[i] = (a->array[i] | b->array[i]); + } +} + + +void bignum_xor(struct bn* a, struct bn* b, struct bn* c) +{ + require(a, "a is null"); + require(b, "b is null"); + require(c, "c is null"); + + int i; + for (i = 0; i < BN_ARRAY_SIZE; ++i) + { + c->array[i] = (a->array[i] ^ b->array[i]); + } +} + + +int bignum_cmp(struct bn* a, struct bn* b) +{ + require(a, "a is null"); + require(b, "b is null"); + + int i = BN_ARRAY_SIZE; + do + { + i -= 1; /* Decrement first, to start with last array element */ + if (a->array[i] > b->array[i]) + { + return LARGER; + } + else if (a->array[i] < b->array[i]) + { + return SMALLER; + } + } + while (i != 0); + + return EQUAL; +} + + +int bignum_is_zero(struct bn* n) +{ + require(n, "n is null"); + + int i; + for (i = 0; i < BN_ARRAY_SIZE; ++i) + { + if (n->array[i]) + { + return 0; + } + } + + return 1; +} + + +void bignum_pow(struct bn* a, struct bn* b, struct bn* c) +{ + require(a, "a is null"); + require(b, "b is null"); + require(c, "c is null"); + + struct bn tmp; + + bignum_init(c); + + if (bignum_cmp(b, c) == EQUAL) + { + /* Return 1 when exponent is 0 -- n^0 = 1 */ + bignum_inc(c); + } + else + { + struct bn bcopy; + bignum_assign(&bcopy, b); + + /* Copy a -> tmp */ + bignum_assign(&tmp, a); + + bignum_dec(&bcopy); + + /* Begin summing products: */ + while (!bignum_is_zero(&bcopy)) + { + + /* c = tmp * tmp */ + bignum_mul(&tmp, a, c); + /* Decrement b by one */ + bignum_dec(&bcopy); + + bignum_assign(&tmp, c); + } + + /* c = tmp */ + bignum_assign(c, &tmp); + } +} + +void bignum_isqrt(struct bn *a, struct bn* b) +{ + require(a, "a is null"); + require(b, "b is null"); + + struct bn low, high, mid, tmp; + + bignum_init(&low); + bignum_assign(&high, a); + bignum_rshift(&high, &mid, 1); + bignum_inc(&mid); + + while (bignum_cmp(&high, &low) > 0) + { + bignum_mul(&mid, &mid, &tmp); + if (bignum_cmp(&tmp, a) > 0) + { + bignum_assign(&high, &mid); + bignum_dec(&high); + } + else + { + bignum_assign(&low, &mid); + } + bignum_sub(&high,&low,&mid); + _rshift_one_bit(&mid); + bignum_add(&low,&mid,&mid); + bignum_inc(&mid); + } + bignum_assign(b,&low); +} + + +void bignum_assign(struct bn* dst, struct bn* src) +{ + require(dst, "dst is null"); + require(src, "src is null"); + + int i; + for (i = 0; i < BN_ARRAY_SIZE; ++i) + { + dst->array[i] = src->array[i]; + } +} + + +/* Private / Static functions. */ +static void _rshift_word(struct bn* a, int nwords) +{ + /* Naive method: */ + require(a, "a is null"); + require(nwords >= 0, "no negative shifts"); + + int i; + if (nwords >= BN_ARRAY_SIZE) + { + for (i = 0; i < BN_ARRAY_SIZE; ++i) + { + a->array[i] = 0; + } + return; + } + + for (i = 0; i < BN_ARRAY_SIZE - nwords; ++i) + { + a->array[i] = a->array[i + nwords]; + } + for (; i < BN_ARRAY_SIZE; ++i) + { + a->array[i] = 0; + } +} + + +static void _lshift_word(struct bn* a, int nwords) +{ + require(a, "a is null"); + require(nwords >= 0, "no negative shifts"); + + int i; + /* Shift whole words */ + for (i = (BN_ARRAY_SIZE - 1); i >= nwords; --i) + { + a->array[i] = a->array[i - nwords]; + } + /* Zero pad shifted words. */ + for (; i >= 0; --i) + { + a->array[i] = 0; + } +} + + +static void _lshift_one_bit(struct bn* a) +{ + require(a, "a is null"); + + int i; + for (i = (BN_ARRAY_SIZE - 1); i > 0; --i) + { + a->array[i] = (a->array[i] << 1) | (a->array[i - 1] >> ((8 * WORD_SIZE) - 1)); + } + a->array[0] <<= 1; +} + + +static void _rshift_one_bit(struct bn* a) +{ + require(a, "a is null"); + + int i; + for (i = 0; i < (BN_ARRAY_SIZE - 1); ++i) + { + a->array[i] = (a->array[i] >> 1) | (a->array[i + 1] << ((8 * WORD_SIZE) - 1)); + } + a->array[BN_ARRAY_SIZE - 1] >>= 1; +} + + diff --git a/v_windows/v/old/thirdparty/bignum/bn.h b/v_windows/v/old/thirdparty/bignum/bn.h new file mode 100644 index 0000000..f82676f --- /dev/null +++ b/v_windows/v/old/thirdparty/bignum/bn.h @@ -0,0 +1,123 @@ +#ifndef __BIGNUM_H__ +#define __BIGNUM_H__ +/* + +Big number library - arithmetic on multiple-precision unsigned integers. + +This library is an implementation of arithmetic on arbitrarily large integers. + +The difference between this and other implementations, is that the data structure +has optimal memory utilization (i.e. a 1024 bit integer takes up 128 bytes RAM), +and all memory is allocated statically: no dynamic allocation for better or worse. + +Primary goals are correctness, clarity of code and clean, portable implementation. +Secondary goal is a memory footprint small enough to make it suitable for use in +embedded applications. + + +The current state is correct functionality and adequate performance. +There may well be room for performance-optimizations and improvements. + +*/ + +#include +#include + + +/* This macro defines the word size in bytes of the array that constitues the big-number data structure. */ +#ifndef WORD_SIZE + #define WORD_SIZE 4 +#endif + +/* Size of big-numbers in bytes */ +#define BN_ARRAY_SIZE (128 / WORD_SIZE) + + +/* Here comes the compile-time specialization for how large the underlying array size should be. */ +/* The choices are 1, 2 and 4 bytes in size with uint32, uint64 for WORD_SIZE==4, as temporary. */ +#ifndef WORD_SIZE + #error Must define WORD_SIZE to be 1, 2, 4 +#elif (WORD_SIZE == 1) + /* Data type of array in structure */ + #define DTYPE uint8_t + /* bitmask for getting MSB */ + #define DTYPE_MSB ((DTYPE_TMP)(0x80)) + /* Data-type larger than DTYPE, for holding intermediate results of calculations */ + #define DTYPE_TMP uint32_t + /* sprintf format string */ + #define SPRINTF_FORMAT_STR "%.02x" + #define SSCANF_FORMAT_STR "%2hhx" + /* Max value of integer type */ + #define MAX_VAL ((DTYPE_TMP)0xFF) +#elif (WORD_SIZE == 2) + #define DTYPE uint16_t + #define DTYPE_TMP uint32_t + #define DTYPE_MSB ((DTYPE_TMP)(0x8000)) + #define SPRINTF_FORMAT_STR "%.04x" + #define SSCANF_FORMAT_STR "%4hx" + #define MAX_VAL ((DTYPE_TMP)0xFFFF) +#elif (WORD_SIZE == 4) + #define DTYPE uint32_t + #define DTYPE_TMP uint64_t + #define DTYPE_MSB ((DTYPE_TMP)(0x80000000)) + #define SPRINTF_FORMAT_STR "%.08x" + #define SSCANF_FORMAT_STR "%8x" + #define MAX_VAL ((DTYPE_TMP)0xFFFFFFFF) +#endif +#ifndef DTYPE + #error DTYPE must be defined to uint8_t, uint16_t uint32_t or whatever +#endif + + +/* Custom assert macro - easy to disable */ +#define require(p, msg) assert(p && #msg) + + +/* Data-holding structure: array of DTYPEs */ +struct bn +{ + DTYPE array[BN_ARRAY_SIZE]; +}; + + + +/* Tokens returned by bignum_cmp() for value comparison */ +enum { SMALLER = -1, EQUAL = 0, LARGER = 1 }; + + + +/* Initialization functions: */ +void bignum_init(struct bn* n); +void bignum_from_int(struct bn* n, DTYPE_TMP i); +int bignum_to_int(struct bn* n); +void bignum_from_string(struct bn* n, char* str, int nbytes); +void bignum_to_string(struct bn* n, char* str, int maxsize); + +/* Basic arithmetic operations: */ +void bignum_add(struct bn* a, struct bn* b, struct bn* c); /* c = a + b */ +void bignum_sub(struct bn* a, struct bn* b, struct bn* c); /* c = a - b */ +void bignum_mul(struct bn* a, struct bn* b, struct bn* c); /* c = a * b */ +void bignum_div(struct bn* a, struct bn* b, struct bn* c); /* c = a / b */ +void bignum_mod(struct bn* a, struct bn* b, struct bn* c); /* c = a % b */ +void bignum_divmod(struct bn* a, struct bn* b, struct bn* c, struct bn* d); /* c = a/b, d = a%b */ + +/* Bitwise operations: */ +void bignum_and(struct bn* a, struct bn* b, struct bn* c); /* c = a & b */ +void bignum_or(struct bn* a, struct bn* b, struct bn* c); /* c = a | b */ +void bignum_xor(struct bn* a, struct bn* b, struct bn* c); /* c = a ^ b */ +void bignum_lshift(struct bn* a, struct bn* b, int nbits); /* b = a << nbits */ +void bignum_rshift(struct bn* a, struct bn* b, int nbits); /* b = a >> nbits */ + +/* Special operators and comparison */ +int bignum_cmp(struct bn* a, struct bn* b); /* Compare: returns LARGER, EQUAL or SMALLER */ +int bignum_is_zero(struct bn* n); /* For comparison with zero */ +void bignum_inc(struct bn* n); /* Increment: add one to n */ +void bignum_dec(struct bn* n); /* Decrement: subtract one from n */ +void bignum_pow(struct bn* a, struct bn* b, struct bn* c); /* Calculate a^b -- e.g. 2^10 => 1024 */ +void bignum_isqrt(struct bn* a, struct bn* b); /* Integer square root -- e.g. isqrt(5) => 2*/ +void bignum_assign(struct bn* dst, struct bn* src); /* Copy src into dst -- dst := src */ + + +#endif /* #ifndef __BIGNUM_H__ */ + + diff --git a/v_windows/v/old/thirdparty/cJSON/cJSON.c b/v_windows/v/old/thirdparty/cJSON/cJSON.c new file mode 100644 index 0000000..60b72c0 --- /dev/null +++ b/v_windows/v/old/thirdparty/cJSON/cJSON.c @@ -0,0 +1,2973 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +/* cJSON */ +/* JSON parser in C. */ + +/* disable warnings about old C89 functions in MSVC */ +#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) +#define _CRT_SECURE_NO_DEPRECATE +#endif + +#ifdef __GNUC__ +#pragma GCC visibility push(default) +#endif +#if defined(_MSC_VER) +#pragma warning (push) +/* disable warning about single line comments in system headers */ +#pragma warning (disable : 4001) +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef ENABLE_LOCALES +#include +#endif + +#if defined(_MSC_VER) +#pragma warning (pop) +#endif +#ifdef __GNUC__ +#pragma GCC visibility pop +#endif + +#include "cJSON.h" + +/* define our own boolean type */ +#ifdef true +#undef true +#endif +#define true ((cJSON_bool)1) + +#ifdef false +#undef false +#endif +#define false ((cJSON_bool)0) + +typedef struct { + const unsigned char *json; + size_t position; +} error; +static error global_error = { NULL, 0 }; + +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) +{ + return (const char*) (global_error.json + global_error.position); +} + +CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item) { + if (!cJSON_IsString(item)) { + return NULL; + } + + return item->valuestring; +} + +/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ +#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 12) + #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. +#endif + +CJSON_PUBLIC(const char*) cJSON_Version(void) +{ + static char version[15]; + sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); + + return version; +} + +/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ +static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) +{ + if ((string1 == NULL) || (string2 == NULL)) + { + return 1; + } + + if (string1 == string2) + { + return 0; + } + + for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) + { + if (*string1 == '\0') + { + return 0; + } + } + + return tolower(*string1) - tolower(*string2); +} + +typedef struct internal_hooks +{ + void *(CJSON_CDECL *allocate)(size_t size); + void (CJSON_CDECL *deallocate)(void *pointer); + void *(CJSON_CDECL *reallocate)(void *pointer, size_t size); +} internal_hooks; + +#if defined(_MSC_VER) +/* work around MSVC error C2322: '...' address of dillimport '...' is not static */ +static void * CJSON_CDECL internal_malloc(size_t size) +{ + return malloc(size); +} +static void CJSON_CDECL internal_free(void *pointer) +{ + free(pointer); +} +static void * CJSON_CDECL internal_realloc(void *pointer, size_t size) +{ + return realloc(pointer, size); +} +#else +#define internal_malloc malloc +#define internal_free free +#define internal_realloc realloc +#endif + +/* strlen of character literals resolved at compile time */ +#define static_strlen(string_literal) (sizeof(string_literal) - sizeof("")) + +static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc }; + +static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) +{ + size_t length = 0; + unsigned char *copy = NULL; + + if (string == NULL) + { + return NULL; + } + + length = strlen((const char*)string) + sizeof(""); + copy = (unsigned char*)hooks->allocate(length); + if (copy == NULL) + { + return NULL; + } + memcpy(copy, string, length); + + return copy; +} + +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (hooks == NULL) + { + /* Reset hooks */ + global_hooks.allocate = malloc; + global_hooks.deallocate = free; + global_hooks.reallocate = realloc; + return; + } + + global_hooks.allocate = malloc; + if (hooks->malloc_fn != NULL) + { + global_hooks.allocate = hooks->malloc_fn; + } + + global_hooks.deallocate = free; + if (hooks->free_fn != NULL) + { + global_hooks.deallocate = hooks->free_fn; + } + + /* use realloc only if both free and malloc are used */ + global_hooks.reallocate = NULL; + if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) + { + global_hooks.reallocate = realloc; + } +} + +/* Internal constructor. */ +static cJSON *cJSON_New_Item(const internal_hooks * const hooks) +{ + cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); + if (node) + { + memset(node, '\0', sizeof(cJSON)); + } + + return node; +} + +/* Delete a cJSON structure. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) +{ + cJSON *next = NULL; + while (item != NULL) + { + next = item->next; + if (!(item->type & cJSON_IsReference) && (item->child != NULL)) + { + cJSON_Delete(item->child); + } + if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) + { + global_hooks.deallocate(item->valuestring); + } + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + global_hooks.deallocate(item->string); + } + global_hooks.deallocate(item); + item = next; + } +} + +/* get the decimal point character of the current locale */ +static unsigned char get_decimal_point(void) +{ +#ifdef ENABLE_LOCALES + struct lconv *lconv = localeconv(); + return (unsigned char) lconv->decimal_point[0]; +#else + return '.'; +#endif +} + +typedef struct +{ + const unsigned char *content; + size_t length; + size_t offset; + size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ + internal_hooks hooks; +} parse_buffer; + +/* check if the given size is left to read in a given parse buffer (starting with 1) */ +#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) +/* check if the buffer can be accessed at the given index (starting with 0) */ +#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) +#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) +/* get a pointer to the buffer at the position */ +#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) + +/* Parse the input text to generate a number, and populate the result into item. */ +static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) +{ + double number = 0; + unsigned char *after_end = NULL; + unsigned char number_c_string[64]; + unsigned char decimal_point = get_decimal_point(); + size_t i = 0; + + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; + } + + /* copy the number into a temporary buffer and replace '.' with the decimal point + * of the current locale (for strtod) + * This also takes care of '\0' not necessarily being available for marking the end of the input */ + for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) + { + switch (buffer_at_offset(input_buffer)[i]) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + case '-': + case 'e': + case 'E': + number_c_string[i] = buffer_at_offset(input_buffer)[i]; + break; + + case '.': + number_c_string[i] = decimal_point; + break; + + default: + goto loop_end; + } + } +loop_end: + number_c_string[i] = '\0'; + + number = strtod((const char*)number_c_string, (char**)&after_end); + if (number_c_string == after_end) + { + return false; /* parse_error */ + } + + item->valuedouble = number; + + /* use saturation in case of overflow */ + if (number >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (number <= (double)INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)number; + } + + item->type = cJSON_Number; + + input_buffer->offset += (size_t)(after_end - number_c_string); + return true; +} + +/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) +{ + if (number >= INT_MAX) + { + object->valueint = INT_MAX; + } + else if (number <= (double)INT_MIN) + { + object->valueint = INT_MIN; + } + else + { + object->valueint = (int)number; + } + + return object->valuedouble = number; +} + +typedef struct +{ + unsigned char *buffer; + size_t length; + size_t offset; + size_t depth; /* current nesting depth (for formatted printing) */ + cJSON_bool noalloc; + cJSON_bool format; /* is this print a formatted print */ + internal_hooks hooks; +} printbuffer; + +/* realloc printbuffer if necessary to have at least "needed" bytes more */ +static unsigned char* ensure(printbuffer * const p, size_t needed) +{ + unsigned char *newbuffer = NULL; + size_t newsize = 0; + + if ((p == NULL) || (p->buffer == NULL)) + { + return NULL; + } + + if ((p->length > 0) && (p->offset >= p->length)) + { + /* make sure that offset is valid */ + return NULL; + } + + if (needed > INT_MAX) + { + /* sizes bigger than INT_MAX are currently not supported */ + return NULL; + } + + needed += p->offset + 1; + if (needed <= p->length) + { + return p->buffer + p->offset; + } + + if (p->noalloc) { + return NULL; + } + + /* calculate new buffer size */ + if (needed > (INT_MAX / 2)) + { + /* overflow of int, use INT_MAX if possible */ + if (needed <= INT_MAX) + { + newsize = INT_MAX; + } + else + { + return NULL; + } + } + else + { + newsize = needed * 2; + } + + if (p->hooks.reallocate != NULL) + { + /* reallocate with realloc if available */ + newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); + if (newbuffer == NULL) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + } + else + { + /* otherwise reallocate manually */ + newbuffer = (unsigned char*)p->hooks.allocate(newsize); + if (!newbuffer) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + if (newbuffer) + { + memcpy(newbuffer, p->buffer, p->offset + 1); + } + p->hooks.deallocate(p->buffer); + } + p->length = newsize; + p->buffer = newbuffer; + + return newbuffer + p->offset; +} + +/* calculate the new length of the string in a printbuffer and update the offset */ +static void update_offset(printbuffer * const buffer) +{ + const unsigned char *buffer_pointer = NULL; + if ((buffer == NULL) || (buffer->buffer == NULL)) + { + return; + } + buffer_pointer = buffer->buffer + buffer->offset; + + buffer->offset += strlen((const char*)buffer_pointer); +} + +/* Render the number nicely from the given item into a string. */ +static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + double d = item->valuedouble; + int length = 0; + size_t i = 0; + unsigned char number_buffer[26]; /* temporary buffer to print the number into */ + unsigned char decimal_point = get_decimal_point(); + double test; + + if (output_buffer == NULL) + { + return false; + } + + /* This checks for NaN and Infinity */ + if ((d * 0) != 0) + { + length = sprintf((char*)number_buffer, "null"); + } + else + { + /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ + length = sprintf((char*)number_buffer, "%1.15g", d); + + /* Check whether the original double can be recovered */ + if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || ((double)test != d)) + { + /* If not, print with 17 decimal places of precision */ + length = sprintf((char*)number_buffer, "%1.17g", d); + } + } + + /* sprintf failed or buffer overrun occurred */ + if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) + { + return false; + } + + /* reserve appropriate space in the output */ + output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); + if (output_pointer == NULL) + { + return false; + } + + /* copy the printed number to the output and replace locale + * dependent decimal point with '.' */ + for (i = 0; i < ((size_t)length); i++) + { + if (number_buffer[i] == decimal_point) + { + output_pointer[i] = '.'; + continue; + } + + output_pointer[i] = number_buffer[i]; + } + output_pointer[i] = '\0'; + + output_buffer->offset += (size_t)length; + + return true; +} + +/* parse 4 digit hexadecimal number */ +static unsigned parse_hex4(const unsigned char * const input) +{ + unsigned int h = 0; + size_t i = 0; + + for (i = 0; i < 4; i++) + { + /* parse digit */ + if ((input[i] >= '0') && (input[i] <= '9')) + { + h += (unsigned int) input[i] - '0'; + } + else if ((input[i] >= 'A') && (input[i] <= 'F')) + { + h += (unsigned int) 10 + input[i] - 'A'; + } + else if ((input[i] >= 'a') && (input[i] <= 'f')) + { + h += (unsigned int) 10 + input[i] - 'a'; + } + else /* invalid */ + { + return 0; + } + + if (i < 3) + { + /* shift left to make place for the next nibble */ + h = h << 4; + } + } + + return h; +} + +/* converts a UTF-16 literal to UTF-8 + * A literal can be one or two sequences of the form \uXXXX */ +static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer) +{ + long unsigned int codepoint = 0; + unsigned int first_code = 0; + const unsigned char *first_sequence = input_pointer; + unsigned char utf8_length = 0; + unsigned char utf8_position = 0; + unsigned char sequence_length = 0; + unsigned char first_byte_mark = 0; + + if ((input_end - first_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + /* get the first utf16 sequence */ + first_code = parse_hex4(first_sequence + 2); + + /* check that the code is valid */ + if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) + { + goto fail; + } + + /* UTF16 surrogate pair */ + if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) + { + const unsigned char *second_sequence = first_sequence + 6; + unsigned int second_code = 0; + sequence_length = 12; /* \uXXXX\uXXXX */ + + if ((input_end - second_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) + { + /* missing second half of the surrogate pair */ + goto fail; + } + + /* get the second utf16 sequence */ + second_code = parse_hex4(second_sequence + 2); + /* check that the code is valid */ + if ((second_code < 0xDC00) || (second_code > 0xDFFF)) + { + /* invalid second half of the surrogate pair */ + goto fail; + } + + + /* calculate the unicode codepoint from the surrogate pair */ + codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); + } + else + { + sequence_length = 6; /* \uXXXX */ + codepoint = first_code; + } + + /* encode as UTF-8 + * takes at maximum 4 bytes to encode: + * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if (codepoint < 0x80) + { + /* normal ascii, encoding 0xxxxxxx */ + utf8_length = 1; + } + else if (codepoint < 0x800) + { + /* two bytes, encoding 110xxxxx 10xxxxxx */ + utf8_length = 2; + first_byte_mark = 0xC0; /* 11000000 */ + } + else if (codepoint < 0x10000) + { + /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ + utf8_length = 3; + first_byte_mark = 0xE0; /* 11100000 */ + } + else if (codepoint <= 0x10FFFF) + { + /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + utf8_length = 4; + first_byte_mark = 0xF0; /* 11110000 */ + } + else + { + /* invalid unicode codepoint */ + goto fail; + } + + /* encode as utf8 */ + for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) + { + /* 10xxxxxx */ + (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); + codepoint >>= 6; + } + /* encode first byte */ + if (utf8_length > 1) + { + (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); + } + else + { + (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); + } + + *output_pointer += utf8_length; + + return sequence_length; + +fail: + return 0; +} + +/* Parse the input text into an unescaped cinput, and populate item. */ +static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer) +{ + const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; + const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; + unsigned char *output_pointer = NULL; + unsigned char *output = NULL; + + /* not a string */ + if (buffer_at_offset(input_buffer)[0] != '\"') + { + goto fail; + } + + { + /* calculate approximate size of the output (overestimate) */ + size_t allocation_length = 0; + size_t skipped_bytes = 0; + while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) + { + /* is escape sequence */ + if (input_end[0] == '\\') + { + if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) + { + /* prevent buffer overflow when last input character is a backslash */ + goto fail; + } + skipped_bytes++; + input_end++; + } + input_end++; + } + if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) + { + goto fail; /* string ended unexpectedly */ + } + + /* This is at most how much we need for the output */ + allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; + output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); + if (output == NULL) + { + goto fail; /* allocation failure */ + } + } + + output_pointer = output; + /* loop through the string literal */ + while (input_pointer < input_end) + { + if (*input_pointer != '\\') + { + *output_pointer++ = *input_pointer++; + } + /* escape sequence */ + else + { + unsigned char sequence_length = 2; + if ((input_end - input_pointer) < 1) + { + goto fail; + } + + switch (input_pointer[1]) + { + case 'b': + *output_pointer++ = '\b'; + break; + case 'f': + *output_pointer++ = '\f'; + break; + case 'n': + *output_pointer++ = '\n'; + break; + case 'r': + *output_pointer++ = '\r'; + break; + case 't': + *output_pointer++ = '\t'; + break; + case '\"': + case '\\': + case '/': + *output_pointer++ = input_pointer[1]; + break; + + /* UTF-16 literal */ + case 'u': + sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); + if (sequence_length == 0) + { + /* failed to convert UTF16-literal to UTF-8 */ + goto fail; + } + break; + + default: + goto fail; + } + input_pointer += sequence_length; + } + } + + /* zero terminate the output */ + *output_pointer = '\0'; + + item->type = cJSON_String; + item->valuestring = (char*)output; + + input_buffer->offset = (size_t) (input_end - input_buffer->content); + input_buffer->offset++; + + return true; + +fail: + if (output != NULL) + { + input_buffer->hooks.deallocate(output); + } + + if (input_pointer != NULL) + { + input_buffer->offset = (size_t)(input_pointer - input_buffer->content); + } + + return false; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer) +{ + const unsigned char *input_pointer = NULL; + unsigned char *output = NULL; + unsigned char *output_pointer = NULL; + size_t output_length = 0; + /* numbers of additional characters needed for escaping */ + size_t escape_characters = 0; + + if (output_buffer == NULL) + { + return false; + } + + /* empty string */ + if (input == NULL) + { + output = ensure(output_buffer, sizeof("\"\"")); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "\"\""); + + return true; + } + + /* set "flag" to 1 if something needs to be escaped */ + for (input_pointer = input; *input_pointer; input_pointer++) + { + switch (*input_pointer) + { + case '\"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + /* one character escape sequence */ + escape_characters++; + break; + default: + if (*input_pointer < 32) + { + /* UTF-16 escape sequence uXXXX */ + escape_characters += 5; + } + break; + } + } + output_length = (size_t)(input_pointer - input) + escape_characters; + + output = ensure(output_buffer, output_length + sizeof("\"\"")); + if (output == NULL) + { + return false; + } + + /* no characters have to be escaped */ + if (escape_characters == 0) + { + output[0] = '\"'; + memcpy(output + 1, input, output_length); + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; + } + + output[0] = '\"'; + output_pointer = output + 1; + /* copy the string */ + for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) + { + if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) + { + /* normal character, copy */ + *output_pointer = *input_pointer; + } + else + { + /* character needs to be escaped */ + *output_pointer++ = '\\'; + switch (*input_pointer) + { + case '\\': + *output_pointer = '\\'; + break; + case '\"': + *output_pointer = '\"'; + break; + case '\b': + *output_pointer = 'b'; + break; + case '\f': + *output_pointer = 'f'; + break; + case '\n': + *output_pointer = 'n'; + break; + case '\r': + *output_pointer = 'r'; + break; + case '\t': + *output_pointer = 't'; + break; + default: + /* escape and print as unicode codepoint */ + sprintf((char*)output_pointer, "u%04x", *input_pointer); + output_pointer += 4; + break; + } + } + } + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; +} + +/* Invoke print_string_ptr (which is useful) on an item. */ +static cJSON_bool print_string(const cJSON * const item, printbuffer * const p) +{ + return print_string_ptr((unsigned char*)item->valuestring, p); +} + +/* Predeclare these prototypes. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer); + +/* Utility to jump whitespace and cr/lf */ +static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL)) + { + return NULL; + } + + while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) + { + buffer->offset++; + } + + if (buffer->offset == buffer->length) + { + buffer->offset--; + } + + return buffer; +} + +/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ +static parse_buffer *skip_utf8_bom(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) + { + return NULL; + } + + if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) + { + buffer->offset += 3; + } + + return buffer; +} + +/* Parse an object - create a new root, and populate. */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) +{ + parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + cJSON *item = NULL; + + /* reset error position */ + global_error.json = NULL; + global_error.position = 0; + + if (value == NULL) + { + goto fail; + } + + buffer.content = (const unsigned char*)value; + buffer.length = strlen((const char*)value) + sizeof(""); + buffer.offset = 0; + buffer.hooks = global_hooks; + + item = cJSON_New_Item(&global_hooks); + if (item == NULL) /* memory fail */ + { + goto fail; + } + + if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) + { + /* parse failure. ep is set. */ + goto fail; + } + + /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ + if (require_null_terminated) + { + buffer_skip_whitespace(&buffer); + if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') + { + goto fail; + } + } + if (return_parse_end) + { + *return_parse_end = (const char*)buffer_at_offset(&buffer); + } + + return item; + +fail: + if (item != NULL) + { + cJSON_Delete(item); + } + + if (value != NULL) + { + error local_error; + local_error.json = (const unsigned char*)value; + local_error.position = 0; + + if (buffer.offset < buffer.length) + { + local_error.position = buffer.offset; + } + else if (buffer.length > 0) + { + local_error.position = buffer.length - 1; + } + + if (return_parse_end != NULL) + { + *return_parse_end = (const char*)local_error.json + local_error.position; + } + + global_error = local_error; + } + + return NULL; +} + +/* Default options for cJSON_Parse */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) +{ + return cJSON_ParseWithOpts(value, 0, 0); +} + +#define cjson_min(a, b) ((a < b) ? a : b) + +static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) +{ + static const size_t default_buffer_size = 256; + printbuffer buffer[1]; + unsigned char *printed = NULL; + + memset(buffer, 0, sizeof(buffer)); + + /* create buffer */ + buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size); + buffer->length = default_buffer_size; + buffer->format = format; + buffer->hooks = *hooks; + if (buffer->buffer == NULL) + { + goto fail; + } + + /* print the value */ + if (!print_value(item, buffer)) + { + goto fail; + } + update_offset(buffer); + + /* check if reallocate is available */ + if (hooks->reallocate != NULL) + { + printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1); + if (printed == NULL) { + goto fail; + } + buffer->buffer = NULL; + } + else /* otherwise copy the JSON over to a new buffer */ + { + printed = (unsigned char*) hooks->allocate(buffer->offset + 1); + if (printed == NULL) + { + goto fail; + } + memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); + printed[buffer->offset] = '\0'; /* just to be sure */ + + /* free the buffer */ + hooks->deallocate(buffer->buffer); + } + + return printed; + +fail: + if (buffer->buffer != NULL) + { + hooks->deallocate(buffer->buffer); + } + + if (printed != NULL) + { + hooks->deallocate(printed); + } + + return NULL; +} + +/* Render a cJSON item/entity/structure to text. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) +{ + return (char*)print(item, true, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) +{ + return (char*)print(item, false, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if (prebuffer < 0) + { + return NULL; + } + + p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); + if (!p.buffer) + { + return NULL; + } + + p.length = (size_t)prebuffer; + p.offset = 0; + p.noalloc = false; + p.format = fmt; + p.hooks = global_hooks; + + if (!print_value(item, &p)) + { + global_hooks.deallocate(p.buffer); + return NULL; + } + + return (char*)p.buffer; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buf, const int len, const cJSON_bool fmt) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if ((len < 0) || (buf == NULL)) + { + return false; + } + + p.buffer = (unsigned char*)buf; + p.length = (size_t)len; + p.offset = 0; + p.noalloc = true; + p.format = fmt; + p.hooks = global_hooks; + + return print_value(item, &p); +} + +/* Parser core - when encountering text, process appropriately. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer) +{ + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; /* no input */ + } + + /* parse the different types of values */ + /* null */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) + { + item->type = cJSON_NULL; + input_buffer->offset += 4; + return true; + } + /* false */ + if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) + { + item->type = cJSON_False; + input_buffer->offset += 5; + return true; + } + /* true */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) + { + item->type = cJSON_True; + item->valueint = 1; + input_buffer->offset += 4; + return true; + } + /* string */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) + { + return parse_string(item, input_buffer); + } + /* number */ + if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) + { + return parse_number(item, input_buffer); + } + /* array */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) + { + return parse_array(item, input_buffer); + } + /* object */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) + { + return parse_object(item, input_buffer); + } + + return false; +} + +/* Render a value to text. */ +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output = NULL; + + if ((item == NULL) || (output_buffer == NULL)) + { + return false; + } + + switch ((item->type) & 0xFF) + { + case cJSON_NULL: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "null"); + return true; + + case cJSON_False: + output = ensure(output_buffer, 6); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "false"); + return true; + + case cJSON_True: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "true"); + return true; + + case cJSON_Number: + return print_number(item, output_buffer); + + case cJSON_Raw: + { + size_t raw_length = 0; + if (item->valuestring == NULL) + { + return false; + } + + raw_length = strlen(item->valuestring) + sizeof(""); + output = ensure(output_buffer, raw_length); + if (output == NULL) + { + return false; + } + memcpy(output, item->valuestring, raw_length); + return true; + } + + case cJSON_String: + return print_string(item, output_buffer); + + case cJSON_Array: + return print_array(item, output_buffer); + + case cJSON_Object: + return print_object(item, output_buffer); + + default: + return false; + } +} + +/* Build an array from input text. */ +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* head of the linked list */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (buffer_at_offset(input_buffer)[0] != '[') + { + /* not an array */ + goto fail; + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) + { + /* empty array */ + goto success; + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse next value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') + { + goto fail; /* expected end of array */ + } + +success: + input_buffer->depth--; + + item->type = cJSON_Array; + item->child = head; + + input_buffer->offset++; + + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an array to text */ +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_element = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output array. */ + /* opening square bracket */ + output_pointer = ensure(output_buffer, 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer = '['; + output_buffer->offset++; + output_buffer->depth++; + + while (current_element != NULL) + { + if (!print_value(current_element, output_buffer)) + { + return false; + } + update_offset(output_buffer); + if (current_element->next) + { + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ','; + if(output_buffer->format) + { + *output_pointer++ = ' '; + } + *output_pointer = '\0'; + output_buffer->offset += length; + } + current_element = current_element->next; + } + + output_pointer = ensure(output_buffer, 2); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ']'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Build an object from the text. */ +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* linked list head */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) + { + goto fail; /* not an object */ + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) + { + goto success; /* empty object */ + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse the name of the child */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_string(current_item, input_buffer)) + { + goto fail; /* failed to parse name */ + } + buffer_skip_whitespace(input_buffer); + + /* swap valuestring and string, because we parsed the name */ + current_item->string = current_item->valuestring; + current_item->valuestring = NULL; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) + { + goto fail; /* invalid object */ + } + + /* parse the value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) + { + goto fail; /* expected end of object */ + } + +success: + input_buffer->depth--; + + item->type = cJSON_Object; + item->child = head; + + input_buffer->offset++; + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an object to text. */ +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_item = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output: */ + length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */ + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer++ = '{'; + output_buffer->depth++; + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + output_buffer->offset += length; + + while (current_item) + { + if (output_buffer->format) + { + size_t i; + output_pointer = ensure(output_buffer, output_buffer->depth); + if (output_pointer == NULL) + { + return false; + } + for (i = 0; i < output_buffer->depth; i++) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += output_buffer->depth; + } + + /* print key */ + if (!print_string_ptr((unsigned char*)current_item->string, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ':'; + if (output_buffer->format) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += length; + + /* print value */ + if (!print_value(current_item, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + /* print comma if not last */ + length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + if (current_item->next) + { + *output_pointer++ = ','; + } + + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + *output_pointer = '\0'; + output_buffer->offset += length; + + current_item = current_item->next; + } + + output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); + if (output_pointer == NULL) + { + return false; + } + if (output_buffer->format) + { + size_t i; + for (i = 0; i < (output_buffer->depth - 1); i++) + { + *output_pointer++ = '\t'; + } + } + *output_pointer++ = '}'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Get Array size/item / object item. */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) +{ + cJSON *child = NULL; + size_t size = 0; + + if (array == NULL) + { + return 0; + } + + child = array->child; + + while(child != NULL) + { + size++; + child = child->next; + } + + /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ + + return (int)size; +} + +static cJSON* get_array_item(const cJSON *array, size_t index) +{ + cJSON *current_child = NULL; + + if (array == NULL) + { + return NULL; + } + + current_child = array->child; + while ((current_child != NULL) && (index > 0)) + { + index--; + current_child = current_child->next; + } + + return current_child; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) +{ + if (index < 0) + { + return NULL; + } + + return get_array_item(array, (size_t)index); +} + +static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive) +{ + cJSON *current_element = NULL; + + if ((object == NULL) || (name == NULL)) + { + return NULL; + } + + current_element = object->child; + if (case_sensitive) + { + while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0)) + { + current_element = current_element->next; + } + } + else + { + while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0)) + { + current_element = current_element->next; + } + } + + if ((current_element == NULL) || (current_element->string == NULL)) { + return NULL; + } + + return current_element; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, false); +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) +{ + return cJSON_GetObjectItem(object, string) ? 1 : 0; +} + +/* Utility for array list handling. */ +static void suffix_object(cJSON *prev, cJSON *item) +{ + prev->next = item; + item->prev = prev; +} + +/* Utility for handling references. */ +static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks) +{ + cJSON *reference = NULL; + if (item == NULL) + { + return NULL; + } + + reference = cJSON_New_Item(hooks); + if (reference == NULL) + { + return NULL; + } + + memcpy(reference, item, sizeof(cJSON)); + reference->string = NULL; + reference->type |= cJSON_IsReference; + reference->next = reference->prev = NULL; + return reference; +} + +static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) +{ + cJSON *child = NULL; + + if ((item == NULL) || (array == NULL)) + { + return false; + } + + child = array->child; + + if (child == NULL) + { + /* list is empty, start new one */ + array->child = item; + } + else + { + /* append to the end */ + while (child->next) + { + child = child->next; + } + suffix_object(child, item); + } + + return true; +} + +/* Add item to array/object. */ +CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item) +{ + add_item_to_array(array, item); +} + +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic push +#endif +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif +/* helper function to cast away const */ +static void* cast_away_const(const void* string) +{ + return (void*)string; +} +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic pop +#endif + + +static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key) +{ + char *new_key = NULL; + int new_type = cJSON_Invalid; + + if ((object == NULL) || (string == NULL) || (item == NULL)) + { + return false; + } + + if (constant_key) + { + new_key = (char*)cast_away_const(string); + new_type = item->type | cJSON_StringIsConst; + } + else + { + new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks); + if (new_key == NULL) + { + return false; + } + + new_type = item->type & ~cJSON_StringIsConst; + } + + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + hooks->deallocate(item->string); + } + + item->string = new_key; + item->type = new_type; + + return add_item_to_array(object, item); +} + +CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) +{ + add_item_to_object(object, string, item, &global_hooks, false); +} + +/* Add an item to an object with constant string as key */ +CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) +{ + add_item_to_object(object, string, item, &global_hooks, true); +} + +CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) +{ + if (array == NULL) + { + return; + } + + add_item_to_array(array, create_reference(item, &global_hooks)); +} + +CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) +{ + if ((object == NULL) || (string == NULL)) + { + return; + } + + add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name) +{ + cJSON *null = cJSON_CreateNull(); + if (add_item_to_object(object, name, null, &global_hooks, false)) + { + return null; + } + + cJSON_Delete(null); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name) +{ + cJSON *true_item = cJSON_CreateTrue(); + if (add_item_to_object(object, name, true_item, &global_hooks, false)) + { + return true_item; + } + + cJSON_Delete(true_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name) +{ + cJSON *false_item = cJSON_CreateFalse(); + if (add_item_to_object(object, name, false_item, &global_hooks, false)) + { + return false_item; + } + + cJSON_Delete(false_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean) +{ + cJSON *bool_item = cJSON_CreateBool(boolean); + if (add_item_to_object(object, name, bool_item, &global_hooks, false)) + { + return bool_item; + } + + cJSON_Delete(bool_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number) +{ + cJSON *number_item = cJSON_CreateNumber(number); + if (add_item_to_object(object, name, number_item, &global_hooks, false)) + { + return number_item; + } + + cJSON_Delete(number_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string) +{ + cJSON *string_item = cJSON_CreateString(string); + if (add_item_to_object(object, name, string_item, &global_hooks, false)) + { + return string_item; + } + + cJSON_Delete(string_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw) +{ + cJSON *raw_item = cJSON_CreateRaw(raw); + if (add_item_to_object(object, name, raw_item, &global_hooks, false)) + { + return raw_item; + } + + cJSON_Delete(raw_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name) +{ + cJSON *object_item = cJSON_CreateObject(); + if (add_item_to_object(object, name, object_item, &global_hooks, false)) + { + return object_item; + } + + cJSON_Delete(object_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name) +{ + cJSON *array = cJSON_CreateArray(); + if (add_item_to_object(object, name, array, &global_hooks, false)) + { + return array; + } + + cJSON_Delete(array); + return NULL; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) +{ + if ((parent == NULL) || (item == NULL)) + { + return NULL; + } + + if (item->prev != NULL) + { + /* not the first element */ + item->prev->next = item->next; + } + if (item->next != NULL) + { + /* not the last element */ + item->next->prev = item->prev; + } + + if (item == parent->child) + { + /* first element */ + parent->child = item->next; + } + /* make sure the detached item doesn't point anywhere anymore */ + item->prev = NULL; + item->next = NULL; + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) +{ + if (which < 0) + { + return NULL; + } + + return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) +{ + cJSON_Delete(cJSON_DetachItemFromArray(array, which)); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItem(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObject(object, string)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); +} + +/* Replace array/object items with new ones. */ +CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) +{ + cJSON *after_inserted = NULL; + + if (which < 0) + { + return; + } + + after_inserted = get_array_item(array, (size_t)which); + if (after_inserted == NULL) + { + add_item_to_array(array, newitem); + return; + } + + newitem->next = after_inserted; + newitem->prev = after_inserted->prev; + after_inserted->prev = newitem; + if (after_inserted == array->child) + { + array->child = newitem; + } + else + { + newitem->prev->next = newitem; + } +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) +{ + if ((parent == NULL) || (replacement == NULL) || (item == NULL)) + { + return false; + } + + if (replacement == item) + { + return true; + } + + replacement->next = item->next; + replacement->prev = item->prev; + + if (replacement->next != NULL) + { + replacement->next->prev = replacement; + } + if (replacement->prev != NULL) + { + replacement->prev->next = replacement; + } + if (parent->child == item) + { + parent->child = replacement; + } + + item->next = NULL; + item->prev = NULL; + cJSON_Delete(item); + + return true; +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) +{ + if (which < 0) + { + return; + } + + cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); +} + +static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) +{ + if ((replacement == NULL) || (string == NULL)) + { + return false; + } + + /* replace the name in the replacement */ + if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) + { + cJSON_free(replacement->string); + } + replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + replacement->type &= ~cJSON_StringIsConst; + + cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); + + return true; +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) +{ + replace_item_in_object(object, string, newitem, false); +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) +{ + replace_item_in_object(object, string, newitem, true); +} + +/* Create basic types: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_NULL; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_True; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool b) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = b ? cJSON_True : cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Number; + item->valuedouble = num; + + /* use saturation in case of overflow */ + if (num >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (num <= (double)INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)num; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_String; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) + { + item->type = cJSON_String | cJSON_IsReference; + item->valuestring = (char*)cast_away_const(string); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Object | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Array | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Raw; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type=cJSON_Array; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) + { + item->type = cJSON_Object; + } + + return item; +} + +/* Create Arrays: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if (!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber((double)numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0;a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (strings == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for (i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateString(strings[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p,n); + } + p = n; + } + + return a; +} + +/* Duplication */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) +{ + cJSON *newitem = NULL; + cJSON *child = NULL; + cJSON *next = NULL; + cJSON *newchild = NULL; + + /* Bail on bad ptr */ + if (!item) + { + goto fail; + } + /* Create new item */ + newitem = cJSON_New_Item(&global_hooks); + if (!newitem) + { + goto fail; + } + /* Copy over all vars */ + newitem->type = item->type & (~cJSON_IsReference); + newitem->valueint = item->valueint; + newitem->valuedouble = item->valuedouble; + if (item->valuestring) + { + newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); + if (!newitem->valuestring) + { + goto fail; + } + } + if (item->string) + { + newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); + if (!newitem->string) + { + goto fail; + } + } + /* If non-recursive, then we're done! */ + if (!recurse) + { + return newitem; + } + /* Walk the ->next chain for the child. */ + child = item->child; + while (child != NULL) + { + newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ + if (!newchild) + { + goto fail; + } + if (next != NULL) + { + /* If newitem->child already set, then crosswire ->prev and ->next and move on */ + next->next = newchild; + newchild->prev = next; + next = newchild; + } + else + { + /* Set newitem->child and move to it */ + newitem->child = newchild; + next = newchild; + } + child = child->next; + } + + return newitem; + +fail: + if (newitem != NULL) + { + cJSON_Delete(newitem); + } + + return NULL; +} + +static void skip_oneline_comment(char **input) +{ + *input += static_strlen("//"); + + for (; (*input)[0] != '\0'; ++(*input)) + { + if ((*input)[0] == '\n') { + *input += static_strlen("\n"); + return; + } + } +} + +static void skip_multiline_comment(char **input) +{ + *input += static_strlen("/*"); + + for (; (*input)[0] != '\0'; ++(*input)) + { + if (((*input)[0] == '*') && ((*input)[1] == '/')) + { + *input += static_strlen("*/"); + return; + } + } +} + +static void minify_string(char **input, char **output) { + (*output)[0] = (*input)[0]; + *input += static_strlen("\""); + *output += static_strlen("\""); + + + for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) { + (*output)[0] = (*input)[0]; + + if ((*input)[0] == '\"') { + (*output)[0] = '\"'; + *input += static_strlen("\""); + *output += static_strlen("\""); + return; + } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) { + (*output)[1] = (*input)[1]; + *input += static_strlen("\""); + *output += static_strlen("\""); + } + } +} + +CJSON_PUBLIC(void) cJSON_Minify(char *json) +{ + char *into = json; + + if (json == NULL) + { + return; + } + + while (json[0] != '\0') + { + switch (json[0]) + { + case ' ': + case '\t': + case '\r': + case '\n': + json++; + break; + + case '/': + if (json[1] == '/') + { + skip_oneline_comment(&json); + } + else if (json[1] == '*') + { + skip_multiline_comment(&json); + } else { + json++; + } + break; + + case '\"': + minify_string(&json, (char**)&into); + break; + + default: + into[0] = json[0]; + json++; + into++; + } + } + + /* and null-terminate. */ + *into = '\0'; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Invalid; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_False; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xff) == cJSON_True; +} + + +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & (cJSON_True | cJSON_False)) != 0; +} +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_NULL; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Number; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_String; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Array; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Object; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Raw; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) +{ + if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)) || cJSON_IsInvalid(a)) + { + return false; + } + + /* check if type is valid */ + switch (a->type & 0xFF) + { + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + case cJSON_Number: + case cJSON_String: + case cJSON_Raw: + case cJSON_Array: + case cJSON_Object: + break; + + default: + return false; + } + + /* identical objects are equal */ + if (a == b) + { + return true; + } + + switch (a->type & 0xFF) + { + /* in these cases and equal type is enough */ + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + return true; + + case cJSON_Number: + if (a->valuedouble == b->valuedouble) + { + return true; + } + return false; + + case cJSON_String: + case cJSON_Raw: + if ((a->valuestring == NULL) || (b->valuestring == NULL)) + { + return false; + } + if (strcmp(a->valuestring, b->valuestring) == 0) + { + return true; + } + + return false; + + case cJSON_Array: + { + cJSON *a_element = a->child; + cJSON *b_element = b->child; + + for (; (a_element != NULL) && (b_element != NULL);) + { + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + + a_element = a_element->next; + b_element = b_element->next; + } + + /* one of the arrays is longer than the other */ + if (a_element != b_element) { + return false; + } + + return true; + } + + case cJSON_Object: + { + cJSON *a_element = NULL; + cJSON *b_element = NULL; + cJSON_ArrayForEach(a_element, a) + { + /* TODO This has O(n^2) runtime, which is horrible! */ + b_element = get_object_item(b, a_element->string, case_sensitive); + if (b_element == NULL) + { + return false; + } + + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + } + + /* doing this twice, once on a and b to prevent true comparison if a subset of b + * TODO: Do this the proper way, this is just a fix for now */ + cJSON_ArrayForEach(b_element, b) + { + a_element = get_object_item(a, b_element->string, case_sensitive); + if (a_element == NULL) + { + return false; + } + + if (!cJSON_Compare(b_element, a_element, case_sensitive)) + { + return false; + } + } + + return true; + } + + default: + return false; + } +} + +CJSON_PUBLIC(void *) cJSON_malloc(size_t size) +{ + return global_hooks.allocate(size); +} + +CJSON_PUBLIC(void) cJSON_free(void *object) +{ + global_hooks.deallocate(object); +} diff --git a/v_windows/v/old/thirdparty/cJSON/cJSON.h b/v_windows/v/old/thirdparty/cJSON/cJSON.h new file mode 100644 index 0000000..592986b --- /dev/null +++ b/v_windows/v/old/thirdparty/cJSON/cJSON.h @@ -0,0 +1,285 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef cJSON__h +#define cJSON__h + +#ifdef __cplusplus +extern "C" +{ +#endif + +#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) +#define __WINDOWS__ +#endif + +#ifdef __WINDOWS__ + +/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options: + +CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols +CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) +CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol + +For *nix builds that support visibility attribute, you can define similar behavior by + +setting default visibility to hidden by adding +-fvisibility=hidden (for gcc) +or +-xldscope=hidden (for sun cc) +to CFLAGS + +then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does + +*/ + +#define CJSON_CDECL __cdecl +#define CJSON_STDCALL __stdcall + +/* export symbols by default, this is necessary for copy pasting the C and header file */ +#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_EXPORT_SYMBOLS +#endif + +#if defined(CJSON_HIDE_SYMBOLS) +#define CJSON_PUBLIC(type) type CJSON_STDCALL +#elif defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL +#elif defined(CJSON_IMPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL +#endif +#else /* !__WINDOWS__ */ +#define CJSON_CDECL +#define CJSON_STDCALL + +#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) +#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type +#else +#define CJSON_PUBLIC(type) type +#endif +#endif + +/* project version */ +#define CJSON_VERSION_MAJOR 1 +#define CJSON_VERSION_MINOR 7 +#define CJSON_VERSION_PATCH 12 + +#include + +/* cJSON Types: */ +#define cJSON_Invalid (0) +#define cJSON_False (1 << 0) +#define cJSON_True (1 << 1) +#define cJSON_NULL (1 << 2) +#define cJSON_Number (1 << 3) +#define cJSON_String (1 << 4) +#define cJSON_Array (1 << 5) +#define cJSON_Object (1 << 6) +#define cJSON_Raw (1 << 7) /* raw json */ + +#define cJSON_IsReference 256 +#define cJSON_StringIsConst 512 + +/* The cJSON structure: */ +typedef struct cJSON +{ + /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON *next; + struct cJSON *prev; + /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + struct cJSON *child; + + /* The type of the item, as above. */ + int type; + + /* The item's string, if type==cJSON_String and type == cJSON_Raw */ + char *valuestring; + /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ + int valueint; + /* The item's number, if type==cJSON_Number */ + double valuedouble; + + /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ + char *string; +} cJSON; + +typedef struct cJSON_Hooks +{ + /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ + void *(CJSON_CDECL *malloc_fn)(size_t sz); + void (CJSON_CDECL *free_fn)(void *ptr); +} cJSON_Hooks; + +typedef int cJSON_bool; + +/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. + * This is to prevent stack overflows. */ +#ifndef CJSON_NESTING_LIMIT +#define CJSON_NESTING_LIMIT 1000 +#endif + +/* returns the version of cJSON as a string */ +CJSON_PUBLIC(const char*) cJSON_Version(void); + +/* Supply malloc, realloc and free functions to cJSON */ +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); + +/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); +/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ +/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); + +/* Render a cJSON entity to text for transfer/storage. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); +/* Render a cJSON entity to text for transfer/storage without any formatting. */ +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); +/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); +/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ +/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); +/* Delete a cJSON entity and all subentities. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *c); + +/* Returns the number of items in an array (or object). */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); +/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); +/* Get item "string" from object. Case insensitive. */ +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); +/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); + +/* Check if the item is a string and return its valuestring */ +CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item); + +/* These functions check the type of an item */ +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item); + +/* These calls create a cJSON item of the appropriate type. */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); +/* raw json */ +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); + +/* Create a string where valuestring references a string so + * it will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string); +/* Create an object/arrray that only references it's elements so + * they will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child); +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); + +/* These utilities create an Array of count items. */ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count); + +/* Append item to the specified array/object. */ +CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); +/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. + * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before + * writing to `item->string` */ +CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); +/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ +CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); + +/* Remove/Detatch items from Arrays/Objects. */ +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); + +/* Update array items. */ +CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); +CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); +CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); +CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); + +/* Duplicate a cJSON item */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); +/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will +need to be released. With recurse!=0, it will duplicate any children connected to the item. +The item->next and ->prev pointers are always zero on return from Duplicate. */ +/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. + * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); + + +CJSON_PUBLIC(void) cJSON_Minify(char *json); + +/* Helper functions for creating and adding items to an object at the same time. + * They return the added item or NULL on failure. */ +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean); +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number); +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw); +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name); + +/* When assigning an integer value, it needs to be propagated to valuedouble too. */ +#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) +/* helper for the cJSON_SetNumberValue macro */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); +#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) + +/* Macro for iterating over an array or object */ +#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) + +/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ +CJSON_PUBLIC(void *) cJSON_malloc(size_t size); +CJSON_PUBLIC(void) cJSON_free(void *object); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/v_windows/v/old/thirdparty/cJSON/readme.txt b/v_windows/v/old/thirdparty/cJSON/readme.txt new file mode 100644 index 0000000..3a67b57 --- /dev/null +++ b/v_windows/v/old/thirdparty/cJSON/readme.txt @@ -0,0 +1,7 @@ +Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/v_windows/v/old/thirdparty/fontstash/fontstash.h b/v_windows/v/old/thirdparty/fontstash/fontstash.h new file mode 100644 index 0000000..9ae21c1 --- /dev/null +++ b/v_windows/v/old/thirdparty/fontstash/fontstash.h @@ -0,0 +1,1742 @@ +// +// NOTE sokol: all IO functions have been removed +// +// Copyright (c) 2009-2013 Mikko Mononen memon@inside.org +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. +// + +#ifndef FONS_H +#define FONS_H + +#ifdef __cplusplus +extern "C" { +#endif + +// To make the implementation private to the file that generates the implementation +#ifdef FONS_STATIC +#define FONS_DEF static +#else +#define FONS_DEF extern +#endif + +#define FONS_INVALID -1 + +#if !defined(FONTSTASH_MALLOC) +#define FONTSTASH_MALLOC malloc +#define FONTSTASH_REALLOC realloc +#define FONTSTASH_FREE free +#endif + +enum FONSflags { + FONS_ZERO_TOPLEFT = 1, + FONS_ZERO_BOTTOMLEFT = 2, +}; + +enum FONSalign { + // Horizontal align + FONS_ALIGN_LEFT = 1<<0, // Default + FONS_ALIGN_CENTER = 1<<1, + FONS_ALIGN_RIGHT = 1<<2, + // Vertical align + FONS_ALIGN_TOP = 1<<3, + FONS_ALIGN_MIDDLE = 1<<4, + FONS_ALIGN_BOTTOM = 1<<5, + FONS_ALIGN_BASELINE = 1<<6, // Default +}; + +enum FONSerrorCode { + // Font atlas is full. + FONS_ATLAS_FULL = 1, + // Scratch memory used to render glyphs is full, requested size reported in 'val', you may need to bump up FONS_SCRATCH_BUF_SIZE. + FONS_SCRATCH_FULL = 2, + // Calls to fonsPushState has created too large stack, if you need deep state stack bump up FONS_MAX_STATES. + FONS_STATES_OVERFLOW = 3, + // Trying to pop too many states fonsPopState(). + FONS_STATES_UNDERFLOW = 4, +}; + +struct FONSparams { + int width, height; + unsigned char flags; + void* userPtr; + int (*renderCreate)(void* uptr, int width, int height); + int (*renderResize)(void* uptr, int width, int height); + void (*renderUpdate)(void* uptr, int* rect, const unsigned char* data); + void (*renderDraw)(void* uptr, const float* verts, const float* tcoords, const unsigned int* colors, int nverts); + void (*renderDelete)(void* uptr); +}; +typedef struct FONSparams FONSparams; + +struct FONSquad +{ + float x0,y0,s0,t0; + float x1,y1,s1,t1; +}; +typedef struct FONSquad FONSquad; + +struct FONStextIter { + float x, y, nextx, nexty, scale, spacing; + unsigned int codepoint; + short isize, iblur; + struct FONSfont* font; + int prevGlyphIndex; + const char* str; + const char* next; + const char* end; + unsigned int utf8state; +}; +typedef struct FONStextIter FONStextIter; + +typedef struct FONScontext FONScontext; + +// Contructor and destructor. +FONS_DEF FONScontext* fonsCreateInternal(FONSparams* params); +FONS_DEF void fonsDeleteInternal(FONScontext* s); + +FONS_DEF void fonsSetErrorCallback(FONScontext* s, void (*callback)(void* uptr, int error, int val), void* uptr); +// Returns current atlas size. +FONS_DEF void fonsGetAtlasSize(FONScontext* s, int* width, int* height); +// Expands the atlas size. +FONS_DEF int fonsExpandAtlas(FONScontext* s, int width, int height); +// Resets the whole stash. +FONS_DEF int fonsResetAtlas(FONScontext* stash, int width, int height); + +// Add fonts +FONS_DEF int fonsGetFontByName(FONScontext* s, const char* name); +FONS_DEF int fonsAddFallbackFont(FONScontext* stash, int base, int fallback); + +// State handling +FONS_DEF void fonsPushState(FONScontext* s); +FONS_DEF void fonsPopState(FONScontext* s); +FONS_DEF void fonsClearState(FONScontext* s); + +// State setting +FONS_DEF void fonsSetSize(FONScontext* s, float size); +FONS_DEF void fonsSetColor(FONScontext* s, unsigned int color); +FONS_DEF void fonsSetSpacing(FONScontext* s, float spacing); +FONS_DEF void fonsSetBlur(FONScontext* s, float blur); +FONS_DEF void fonsSetAlign(FONScontext* s, int align); +FONS_DEF void fonsSetFont(FONScontext* s, int font); + +// Draw text +FONS_DEF float fonsDrawText(FONScontext* s, float x, float y, const char* string, const char* end); + +// Measure text +FONS_DEF float fonsTextBounds(FONScontext* s, float x, float y, const char* string, const char* end, float* bounds); +FONS_DEF void fonsLineBounds(FONScontext* s, float y, float* miny, float* maxy); +FONS_DEF void fonsVertMetrics(FONScontext* s, float* ascender, float* descender, float* lineh); + +// Text iterator +FONS_DEF int fonsTextIterInit(FONScontext* stash, FONStextIter* iter, float x, float y, const char* str, const char* end); +FONS_DEF int fonsTextIterNext(FONScontext* stash, FONStextIter* iter, struct FONSquad* quad); + +// Pull texture changes +FONS_DEF const unsigned char* fonsGetTextureData(FONScontext* stash, int* width, int* height); +FONS_DEF int fonsValidateTexture(FONScontext* s, int* dirty); + +// Draws the stash texture for debugging +FONS_DEF void fonsDrawDebug(FONScontext* s, float x, float y); + +#ifdef __cplusplus +} +#endif + +#endif // FONS_H + + +#ifdef FONTSTASH_IMPLEMENTATION + +#define FONS_NOTUSED(v) (void)sizeof(v) + + +// Use FreeType on non-Windows systems +#ifndef _WIN32 +//#define FONS_USE_FREETYPE +#endif + +#ifdef _WIN32 +#undef FONS_USE_FREETYPE +#endif + +#ifdef __APPLE__ + #include "TargetConditionals.h" + #if TARGET_OS_IPHONE + #undef FONS_USE_FREETYPE + #endif +#endif + +//#undef FONS_USE_FREETYPE + +//#define FONS_USE_FREETYPE 1 + +#ifdef FONS_USE_FREETYPE + +#include +#include FT_FREETYPE_H +#include FT_ADVANCES_H +#include + +struct FONSttFontImpl { + FT_Face font; +}; +typedef struct FONSttFontImpl FONSttFontImpl; + +static FT_Library ftLibrary; + +static int fons__tt_init() +{ + puts("free type fons init"); + FT_Error ftError; + //FONS_NOTUSED(context); + ftError = FT_Init_FreeType(&ftLibrary); + return ftError == 0; +} + +static int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, unsigned char *data, int dataSize) +{ + FT_Error ftError; + FONS_NOTUSED(context); + + //font->font.userdata = stash; + ftError = FT_New_Memory_Face(ftLibrary, (const FT_Byte*)data, dataSize, 0, &font->font); + return ftError == 0; +} + +static void fons__tt_getFontVMetrics(FONSttFontImpl *font, int *ascent, int *descent, int *lineGap) +{ + *ascent = font->font->ascender; + *descent = font->font->descender; + *lineGap = font->font->height - (*ascent - *descent); +} + +static float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size) +{ + return size / (font->font->ascender - font->font->descender); +} + +static int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint) +{ + return FT_Get_Char_Index(font->font, codepoint); +} + +static int fons__tt_buildGlyphBitmap(FONSttFontImpl *font, int glyph, float size, float scale, + int *advance, int *lsb, int *x0, int *y0, int *x1, int *y1) +{ + FT_Error ftError; + FT_GlyphSlot ftGlyph; + FT_Fixed advFixed; + FONS_NOTUSED(scale); + + ftError = FT_Set_Pixel_Sizes(font->font, 0, (FT_UInt)(size * (float)font->font->units_per_EM / (float)(font->font->ascender - font->font->descender))); + if (ftError) return 0; + ftError = FT_Load_Glyph(font->font, glyph, FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT); + if (ftError) return 0; + ftError = FT_Get_Advance(font->font, glyph, FT_LOAD_NO_SCALE, &advFixed); + if (ftError) return 0; + ftGlyph = font->font->glyph; + *advance = (int)advFixed; + *lsb = (int)ftGlyph->metrics.horiBearingX; + *x0 = ftGlyph->bitmap_left; + *x1 = *x0 + ftGlyph->bitmap.width; + *y0 = -ftGlyph->bitmap_top; + *y1 = *y0 + ftGlyph->bitmap.rows; + return 1; +} + +static void fons__tt_renderGlyphBitmap(FONSttFontImpl *font, unsigned char *output, int outWidth, int outHeight, int outStride, + float scaleX, float scaleY, int glyph) +{ + FT_GlyphSlot ftGlyph = font->font->glyph; + int ftGlyphOffset = 0; + int x, y; + FONS_NOTUSED(outWidth); + FONS_NOTUSED(outHeight); + FONS_NOTUSED(scaleX); + FONS_NOTUSED(scaleY); + FONS_NOTUSED(glyph); // glyph has already been loaded by fons__tt_buildGlyphBitmap + + for ( y = 0; y < ftGlyph->bitmap.rows; y++ ) { + for ( x = 0; x < ftGlyph->bitmap.width; x++ ) { + output[(y * outStride) + x] = ftGlyph->bitmap.buffer[ftGlyphOffset++]; + } + } +} + +static int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2) +{ + FT_Vector ftKerning; + FT_Get_Kerning(font->font, glyph1, glyph2, FT_KERNING_DEFAULT, &ftKerning); + return (int)((ftKerning.x + 32) >> 6); // Round up and convert to integer +} + +#else + +#define STB_TRUETYPE_IMPLEMENTATION +#define STBTT_STATIC +static void* fons__tmpalloc(size_t size, void* up); +static void fons__tmpfree(void* ptr, void* up); +#define STBTT_malloc(x,u) fons__tmpalloc(x,u) +#define STBTT_free(x,u) fons__tmpfree(x,u) +#include "stb_truetype.h" + +struct FONSttFontImpl { + stbtt_fontinfo font; +}; +typedef struct FONSttFontImpl FONSttFontImpl; + +static int fons__tt_init(FONScontext *context) +{ + FONS_NOTUSED(context); + return 1; +} + +static int fons__tt_loadFont(FONScontext *context, FONSttFontImpl *font, unsigned char *data, int dataSize) +{ + int stbError; + FONS_NOTUSED(dataSize); + + font->font.userdata = context; + stbError = stbtt_InitFont(&font->font, data, 0); + return stbError; +} + +static void fons__tt_getFontVMetrics(FONSttFontImpl *font, int *ascent, int *descent, int *lineGap) +{ + stbtt_GetFontVMetrics(&font->font, ascent, descent, lineGap); +} + +static float fons__tt_getPixelHeightScale(FONSttFontImpl *font, float size) +{ + return stbtt_ScaleForPixelHeight(&font->font, size); +} + +static int fons__tt_getGlyphIndex(FONSttFontImpl *font, int codepoint) +{ + return stbtt_FindGlyphIndex(&font->font, codepoint); +} + +static int fons__tt_buildGlyphBitmap(FONSttFontImpl *font, int glyph, float size, float scale, + int *advance, int *lsb, int *x0, int *y0, int *x1, int *y1) +{ + FONS_NOTUSED(size); + stbtt_GetGlyphHMetrics(&font->font, glyph, advance, lsb); + stbtt_GetGlyphBitmapBox(&font->font, glyph, scale, scale, x0, y0, x1, y1); + return 1; +} + +static void fons__tt_renderGlyphBitmap(FONSttFontImpl *font, unsigned char *output, int outWidth, int outHeight, int outStride, + float scaleX, float scaleY, int glyph) +{ + stbtt_MakeGlyphBitmap(&font->font, output, outWidth, outHeight, outStride, scaleX, scaleY, glyph); +} + +static int fons__tt_getGlyphKernAdvance(FONSttFontImpl *font, int glyph1, int glyph2) +{ + return stbtt_GetGlyphKernAdvance(&font->font, glyph1, glyph2); +} + +#endif + +#ifndef FONS_SCRATCH_BUF_SIZE +# define FONS_SCRATCH_BUF_SIZE 64000 +#endif +#ifndef FONS_HASH_LUT_SIZE +# define FONS_HASH_LUT_SIZE 256 +#endif +#ifndef FONS_INIT_FONTS +# define FONS_INIT_FONTS 4 +#endif +#ifndef FONS_INIT_GLYPHS +# define FONS_INIT_GLYPHS 256 +#endif +#ifndef FONS_INIT_ATLAS_NODES +# define FONS_INIT_ATLAS_NODES 256 +#endif +#ifndef FONS_VERTEX_COUNT +# define FONS_VERTEX_COUNT 1024 +#endif +#ifndef FONS_MAX_STATES +# define FONS_MAX_STATES 20 +#endif +#ifndef FONS_MAX_FALLBACKS +# define FONS_MAX_FALLBACKS 20 +#endif + +static unsigned int fons__hashint(unsigned int a) +{ + a += ~(a<<15); + a ^= (a>>10); + a += (a<<3); + a ^= (a>>6); + a += ~(a<<11); + a ^= (a>>16); + return a; +} + +static int fons__mini(int a, int b) +{ + return a < b ? a : b; +} + +static int fons__maxi(int a, int b) +{ + return a > b ? a : b; +} + +struct FONSglyph +{ + unsigned int codepoint; + int index; + int next; + short size, blur; + short x0,y0,x1,y1; + short xadv,xoff,yoff; +}; +typedef struct FONSglyph FONSglyph; + +struct FONSfont +{ + FONSttFontImpl font; + char name[64]; + unsigned char* data; + int dataSize; + unsigned char freeData; + float ascender; + float descender; + float lineh; + FONSglyph* glyphs; + int cglyphs; + int nglyphs; + int lut[FONS_HASH_LUT_SIZE]; + int fallbacks[FONS_MAX_FALLBACKS]; + int nfallbacks; +}; +typedef struct FONSfont FONSfont; + +struct FONSstate +{ + int font; + int align; + float size; + unsigned int color; + float blur; + float spacing; +}; +typedef struct FONSstate FONSstate; + +struct FONSatlasNode { + short x, y, width; +}; +typedef struct FONSatlasNode FONSatlasNode; + +struct FONSatlas +{ + int width, height; + FONSatlasNode* nodes; + int nnodes; + int cnodes; +}; +typedef struct FONSatlas FONSatlas; + +struct FONScontext +{ + FONSparams params; + float itw,ith; + unsigned char* texData; + int dirtyRect[4]; + FONSfont** fonts; + FONSatlas* atlas; + int cfonts; + int nfonts; + float verts[FONS_VERTEX_COUNT*2]; + float tcoords[FONS_VERTEX_COUNT*2]; + unsigned int colors[FONS_VERTEX_COUNT]; + int nverts; + unsigned char* scratch; + int nscratch; + FONSstate states[FONS_MAX_STATES]; + int nstates; + void (*handleError)(void* uptr, int error, int val); + void* errorUptr; +}; + +#ifdef STB_TRUETYPE_IMPLEMENTATION + +static void* fons__tmpalloc(size_t size, void* up) +{ + unsigned char* ptr; + FONScontext* stash = (FONScontext*)up; + + // 16-byte align the returned pointer + size = (size + 0xf) & ~0xf; + + if (stash->nscratch+(int)size > FONS_SCRATCH_BUF_SIZE) { + if (stash->handleError) + stash->handleError(stash->errorUptr, FONS_SCRATCH_FULL, stash->nscratch+(int)size); + return NULL; + } + ptr = stash->scratch + stash->nscratch; + stash->nscratch += (int)size; + return ptr; +} + +static void fons__tmpfree(void* ptr, void* up) +{ + (void)ptr; + (void)up; + // empty +} + +#endif // STB_TRUETYPE_IMPLEMENTATION + +// Copyright (c) 2008-2010 Bjoern Hoehrmann +// See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. + +#define FONS_UTF8_ACCEPT 0 +#define FONS_UTF8_REJECT 12 + +static unsigned int fons__decutf8(unsigned int* state, unsigned int* codep, unsigned int byte) +{ + static const unsigned char utf8d[] = { + // The first part of the table maps bytes to character classes that + // to reduce the size of the transition table and create bitmasks. + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, + + // The second part is a transition table that maps a combination + // of a state of the automaton and a character class to a state. + 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12, + 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12, + 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12, + 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12, + 12,36,12,12,12,12,12,12,12,12,12,12, + }; + + unsigned int type = utf8d[byte]; + + *codep = (*state != FONS_UTF8_ACCEPT) ? + (byte & 0x3fu) | (*codep << 6) : + (0xff >> type) & (byte); + + *state = utf8d[256 + *state + type]; + return *state; +} + +// Atlas based on Skyline Bin Packer by Jukka Jylänki + +static void fons__deleteAtlas(FONSatlas* atlas) +{ + if (atlas == NULL) return; + if (atlas->nodes != NULL) FONTSTASH_FREE(atlas->nodes); + FONTSTASH_FREE(atlas); +} + +static FONSatlas* fons__allocAtlas(int w, int h, int nnodes) +{ + FONSatlas* atlas = NULL; + + // Allocate memory for the font stash. + atlas = (FONSatlas*)FONTSTASH_MALLOC(sizeof(FONSatlas)); + if (atlas == NULL) goto error; + memset(atlas, 0, sizeof(FONSatlas)); + + atlas->width = w; + atlas->height = h; + + // Allocate space for skyline nodes + atlas->nodes = (FONSatlasNode*)FONTSTASH_MALLOC(sizeof(FONSatlasNode) * nnodes); + if (atlas->nodes == NULL) goto error; + memset(atlas->nodes, 0, sizeof(FONSatlasNode) * nnodes); + atlas->nnodes = 0; + atlas->cnodes = nnodes; + + // Init root node. + atlas->nodes[0].x = 0; + atlas->nodes[0].y = 0; + atlas->nodes[0].width = (short)w; + atlas->nnodes++; + + return atlas; + +error: + if (atlas) fons__deleteAtlas(atlas); + return NULL; +} + +static int fons__atlasInsertNode(FONSatlas* atlas, int idx, int x, int y, int w) +{ + int i; + // Insert node + if (atlas->nnodes+1 > atlas->cnodes) { + atlas->cnodes = atlas->cnodes == 0 ? 8 : atlas->cnodes * 2; + atlas->nodes = (FONSatlasNode*)FONTSTASH_REALLOC(atlas->nodes, sizeof(FONSatlasNode) * atlas->cnodes); + if (atlas->nodes == NULL) + return 0; + } + for (i = atlas->nnodes; i > idx; i--) + atlas->nodes[i] = atlas->nodes[i-1]; + atlas->nodes[idx].x = (short)x; + atlas->nodes[idx].y = (short)y; + atlas->nodes[idx].width = (short)w; + atlas->nnodes++; + + return 1; +} + +static void fons__atlasRemoveNode(FONSatlas* atlas, int idx) +{ + int i; + if (atlas->nnodes == 0) return; + for (i = idx; i < atlas->nnodes-1; i++) + atlas->nodes[i] = atlas->nodes[i+1]; + atlas->nnodes--; +} + +static void fons__atlasExpand(FONSatlas* atlas, int w, int h) +{ + // Insert node for empty space + if (w > atlas->width) + fons__atlasInsertNode(atlas, atlas->nnodes, atlas->width, 0, w - atlas->width); + atlas->width = w; + atlas->height = h; +} + +static void fons__atlasReset(FONSatlas* atlas, int w, int h) +{ + atlas->width = w; + atlas->height = h; + atlas->nnodes = 0; + + // Init root node. + atlas->nodes[0].x = 0; + atlas->nodes[0].y = 0; + atlas->nodes[0].width = (short)w; + atlas->nnodes++; +} + +static int fons__atlasAddSkylineLevel(FONSatlas* atlas, int idx, int x, int y, int w, int h) +{ + int i; + + // Insert new node + if (fons__atlasInsertNode(atlas, idx, x, y+h, w) == 0) + return 0; + + // Delete skyline segments that fall under the shadow of the new segment. + for (i = idx+1; i < atlas->nnodes; i++) { + if (atlas->nodes[i].x < atlas->nodes[i-1].x + atlas->nodes[i-1].width) { + int shrink = atlas->nodes[i-1].x + atlas->nodes[i-1].width - atlas->nodes[i].x; + atlas->nodes[i].x += (short)shrink; + atlas->nodes[i].width -= (short)shrink; + if (atlas->nodes[i].width <= 0) { + fons__atlasRemoveNode(atlas, i); + i--; + } else { + break; + } + } else { + break; + } + } + + // Merge same height skyline segments that are next to each other. + for (i = 0; i < atlas->nnodes-1; i++) { + if (atlas->nodes[i].y == atlas->nodes[i+1].y) { + atlas->nodes[i].width += atlas->nodes[i+1].width; + fons__atlasRemoveNode(atlas, i+1); + i--; + } + } + + return 1; +} + +static int fons__atlasRectFits(FONSatlas* atlas, int i, int w, int h) +{ + // Checks if there is enough space at the location of skyline span 'i', + // and return the max height of all skyline spans under that at that location, + // (think tetris block being dropped at that position). Or -1 if no space found. + int x = atlas->nodes[i].x; + int y = atlas->nodes[i].y; + int spaceLeft; + if (x + w > atlas->width) + return -1; + spaceLeft = w; + while (spaceLeft > 0) { + if (i == atlas->nnodes) return -1; + y = fons__maxi(y, atlas->nodes[i].y); + if (y + h > atlas->height) return -1; + spaceLeft -= atlas->nodes[i].width; + ++i; + } + return y; +} + +static int fons__atlasAddRect(FONSatlas* atlas, int rw, int rh, int* rx, int* ry) +{ + int besth = atlas->height, bestw = atlas->width, besti = -1; + int bestx = -1, besty = -1, i; + + // Bottom left fit heuristic. + for (i = 0; i < atlas->nnodes; i++) { + int y = fons__atlasRectFits(atlas, i, rw, rh); + if (y != -1) { + if (y + rh < besth || (y + rh == besth && atlas->nodes[i].width < bestw)) { + besti = i; + bestw = atlas->nodes[i].width; + besth = y + rh; + bestx = atlas->nodes[i].x; + besty = y; + } + } + } + + if (besti == -1) + return 0; + + // Perform the actual packing. + if (fons__atlasAddSkylineLevel(atlas, besti, bestx, besty, rw, rh) == 0) + return 0; + + *rx = bestx; + *ry = besty; + + return 1; +} + +static void fons__addWhiteRect(FONScontext* stash, int w, int h) +{ + int x, y, gx, gy; + unsigned char* dst; + if (fons__atlasAddRect(stash->atlas, w, h, &gx, &gy) == 0) + return; + + // Rasterize + dst = &stash->texData[gx + gy * stash->params.width]; + for (y = 0; y < h; y++) { + for (x = 0; x < w; x++) + dst[x] = 0xff; + dst += stash->params.width; + } + + stash->dirtyRect[0] = fons__mini(stash->dirtyRect[0], gx); + stash->dirtyRect[1] = fons__mini(stash->dirtyRect[1], gy); + stash->dirtyRect[2] = fons__maxi(stash->dirtyRect[2], gx+w); + stash->dirtyRect[3] = fons__maxi(stash->dirtyRect[3], gy+h); +} + +FONScontext* fonsCreateInternal(FONSparams* params) +{ + FONScontext* stash = NULL; + + // Allocate memory for the font stash. + stash = (FONScontext*)FONTSTASH_MALLOC(sizeof(FONScontext)); + if (stash == NULL) goto error; + memset(stash, 0, sizeof(FONScontext)); + + stash->params = *params; + + // Allocate scratch buffer. + stash->scratch = (unsigned char*)FONTSTASH_MALLOC(FONS_SCRATCH_BUF_SIZE); + if (stash->scratch == NULL) goto error; + + // Initialize implementation library + if (!fons__tt_init(stash)) goto error; + + if (stash->params.renderCreate != NULL) { + if (stash->params.renderCreate(stash->params.userPtr, stash->params.width, stash->params.height) == 0) + goto error; + } + + stash->atlas = fons__allocAtlas(stash->params.width, stash->params.height, FONS_INIT_ATLAS_NODES); + if (stash->atlas == NULL) goto error; + + // Allocate space for fonts. + stash->fonts = (FONSfont**)FONTSTASH_MALLOC(sizeof(FONSfont*) * FONS_INIT_FONTS); + if (stash->fonts == NULL) goto error; + memset(stash->fonts, 0, sizeof(FONSfont*) * FONS_INIT_FONTS); + stash->cfonts = FONS_INIT_FONTS; + stash->nfonts = 0; + + // Create texture for the cache. + stash->itw = 1.0f/stash->params.width; + stash->ith = 1.0f/stash->params.height; + stash->texData = (unsigned char*)FONTSTASH_MALLOC(stash->params.width * stash->params.height); + if (stash->texData == NULL) goto error; + memset(stash->texData, 0, stash->params.width * stash->params.height); + + stash->dirtyRect[0] = stash->params.width; + stash->dirtyRect[1] = stash->params.height; + stash->dirtyRect[2] = 0; + stash->dirtyRect[3] = 0; + + // Add white rect at 0,0 for debug drawing. + fons__addWhiteRect(stash, 2,2); + + fonsPushState(stash); + fonsClearState(stash); + + return stash; + +error: + fonsDeleteInternal(stash); + return NULL; +} + +static FONSstate* fons__getState(FONScontext* stash) +{ + return &stash->states[stash->nstates-1]; +} + +int fonsAddFallbackFont(FONScontext* stash, int base, int fallback) +{ + FONSfont* baseFont = stash->fonts[base]; + if (baseFont->nfallbacks < FONS_MAX_FALLBACKS) { + baseFont->fallbacks[baseFont->nfallbacks++] = fallback; + return 1; + } + return 0; +} + +void fonsSetSize(FONScontext* stash, float size) +{ + fons__getState(stash)->size = size; +} + +void fonsSetColor(FONScontext* stash, unsigned int color) +{ + fons__getState(stash)->color = color; +} + +void fonsSetSpacing(FONScontext* stash, float spacing) +{ + fons__getState(stash)->spacing = spacing; +} + +void fonsSetBlur(FONScontext* stash, float blur) +{ + fons__getState(stash)->blur = blur; +} + +void fonsSetAlign(FONScontext* stash, int align) +{ + fons__getState(stash)->align = align; +} + +void fonsSetFont(FONScontext* stash, int font) +{ + fons__getState(stash)->font = font; +} + +void fonsPushState(FONScontext* stash) +{ + if (stash->nstates >= FONS_MAX_STATES) { + if (stash->handleError) + stash->handleError(stash->errorUptr, FONS_STATES_OVERFLOW, 0); + return; + } + if (stash->nstates > 0) + memcpy(&stash->states[stash->nstates], &stash->states[stash->nstates-1], sizeof(FONSstate)); + stash->nstates++; +} + +void fonsPopState(FONScontext* stash) +{ + if (stash->nstates <= 1) { + if (stash->handleError) + stash->handleError(stash->errorUptr, FONS_STATES_UNDERFLOW, 0); + return; + } + stash->nstates--; +} + +void fonsClearState(FONScontext* stash) +{ + FONSstate* state = fons__getState(stash); + state->size = 12.0f; + state->color = 0xffffffff; + state->font = 0; + state->blur = 0; + state->spacing = 0; + state->align = FONS_ALIGN_LEFT | FONS_ALIGN_BASELINE; +} + +static void fons__freeFont(FONSfont* font) +{ + if (font == NULL) return; + if (font->glyphs) FONTSTASH_FREE(font->glyphs); + if (font->freeData && font->data) FONTSTASH_FREE(font->data); + FONTSTASH_FREE(font); +} + +static int fons__allocFont(FONScontext* stash) +{ + FONSfont* font = NULL; + if (stash->nfonts+1 > stash->cfonts) { + stash->cfonts = stash->cfonts == 0 ? 8 : stash->cfonts * 2; + stash->fonts = (FONSfont**)FONTSTASH_REALLOC(stash->fonts, sizeof(FONSfont*) * stash->cfonts); + if (stash->fonts == NULL) + return -1; + } + font = (FONSfont*)FONTSTASH_MALLOC(sizeof(FONSfont)); + if (font == NULL) goto error; + memset(font, 0, sizeof(FONSfont)); + + font->glyphs = (FONSglyph*)FONTSTASH_MALLOC(sizeof(FONSglyph) * FONS_INIT_GLYPHS); + if (font->glyphs == NULL) goto error; + font->cglyphs = FONS_INIT_GLYPHS; + font->nglyphs = 0; + + stash->fonts[stash->nfonts++] = font; + return stash->nfonts-1; + +error: + fons__freeFont(font); + + return FONS_INVALID; +} + +int fonsAddFontMem(FONScontext* stash, const char* name, unsigned char* data, int dataSize, int freeData) +{ + int i, ascent, descent, fh, lineGap; + FONSfont* font; + + int idx = fons__allocFont(stash); + if (idx == FONS_INVALID) + return FONS_INVALID; + + font = stash->fonts[idx]; + + strncpy(font->name, name, sizeof(font->name)); + font->name[sizeof(font->name)-1] = '\0'; + + // Init hash lookup. + for (i = 0; i < FONS_HASH_LUT_SIZE; ++i) + font->lut[i] = -1; + + // Read in the font data. + font->dataSize = dataSize; + font->data = data; + font->freeData = (unsigned char)freeData; + + // Init font + stash->nscratch = 0; + if (!fons__tt_loadFont(stash, &font->font, data, dataSize)) goto error; + + // Store normalized line height. The real line height is got + // by multiplying the lineh by font size. + fons__tt_getFontVMetrics( &font->font, &ascent, &descent, &lineGap); + fh = ascent - descent; + font->ascender = (float)ascent / (float)fh; + font->descender = (float)descent / (float)fh; + font->lineh = (float)(fh + lineGap) / (float)fh; + + return idx; + +error: + fons__freeFont(font); + stash->nfonts--; + return FONS_INVALID; +} + +int fonsGetFontByName(FONScontext* s, const char* name) +{ + int i; + for (i = 0; i < s->nfonts; i++) { + if (strcmp(s->fonts[i]->name, name) == 0) + return i; + } + return FONS_INVALID; +} + + +static FONSglyph* fons__allocGlyph(FONSfont* font) +{ + if (font->nglyphs+1 > font->cglyphs) { + font->cglyphs = font->cglyphs == 0 ? 8 : font->cglyphs * 2; + font->glyphs = (FONSglyph*)FONTSTASH_REALLOC(font->glyphs, sizeof(FONSglyph) * font->cglyphs); + if (font->glyphs == NULL) return NULL; + } + font->nglyphs++; + return &font->glyphs[font->nglyphs-1]; +} + + +// Based on Exponential blur, Jani Huhtanen, 2006 + +#define APREC 16 +#define ZPREC 7 + +static void fons__blurCols(unsigned char* dst, int w, int h, int dstStride, int alpha) +{ + int x, y; + for (y = 0; y < h; y++) { + int z = 0; // force zero border + for (x = 1; x < w; x++) { + z += (alpha * (((int)(dst[x]) << ZPREC) - z)) >> APREC; + dst[x] = (unsigned char)(z >> ZPREC); + } + dst[w-1] = 0; // force zero border + z = 0; + for (x = w-2; x >= 0; x--) { + z += (alpha * (((int)(dst[x]) << ZPREC) - z)) >> APREC; + dst[x] = (unsigned char)(z >> ZPREC); + } + dst[0] = 0; // force zero border + dst += dstStride; + } +} + +static void fons__blurRows(unsigned char* dst, int w, int h, int dstStride, int alpha) +{ + int x, y; + for (x = 0; x < w; x++) { + int z = 0; // force zero border + for (y = dstStride; y < h*dstStride; y += dstStride) { + z += (alpha * (((int)(dst[y]) << ZPREC) - z)) >> APREC; + dst[y] = (unsigned char)(z >> ZPREC); + } + dst[(h-1)*dstStride] = 0; // force zero border + z = 0; + for (y = (h-2)*dstStride; y >= 0; y -= dstStride) { + z += (alpha * (((int)(dst[y]) << ZPREC) - z)) >> APREC; + dst[y] = (unsigned char)(z >> ZPREC); + } + dst[0] = 0; // force zero border + dst++; + } +} + + +static void fons__blur(FONScontext* stash, unsigned char* dst, int w, int h, int dstStride, int blur) +{ + int alpha; + float sigma; + (void)stash; + + if (blur < 1) + return; + // Calculate the alpha such that 90% of the kernel is within the radius. (Kernel extends to infinity) + sigma = (float)blur * 0.57735f; // 1 / sqrt(3) + alpha = (int)((1< 20) iblur = 20; + pad = iblur+2; + + // Reset allocator. + stash->nscratch = 0; + + // Find code point and size. + h = fons__hashint(codepoint) & (FONS_HASH_LUT_SIZE-1); + i = font->lut[h]; + while (i != -1) { + if (font->glyphs[i].codepoint == codepoint && font->glyphs[i].size == isize && font->glyphs[i].blur == iblur) + return &font->glyphs[i]; + i = font->glyphs[i].next; + } + + // Could not find glyph, create it. + g = fons__tt_getGlyphIndex(&font->font, codepoint); + // Try to find the glyph in fallback fonts. + if (g == 0) { + for (i = 0; i < font->nfallbacks; ++i) { + FONSfont* fallbackFont = stash->fonts[font->fallbacks[i]]; + int fallbackIndex = fons__tt_getGlyphIndex(&fallbackFont->font, codepoint); + if (fallbackIndex != 0) { + g = fallbackIndex; + renderFont = fallbackFont; + break; + } + } + // It is possible that we did not find a fallback glyph. + // In that case the glyph index 'g' is 0, and we'll proceed below and cache empty glyph. + } + scale = fons__tt_getPixelHeightScale(&renderFont->font, size); + fons__tt_buildGlyphBitmap(&renderFont->font, g, size, scale, &advance, &lsb, &x0, &y0, &x1, &y1); + gw = x1-x0 + pad*2; + gh = y1-y0 + pad*2; + + // Find free spot for the rect in the atlas + added = fons__atlasAddRect(stash->atlas, gw, gh, &gx, &gy); + if (added == 0 && stash->handleError != NULL) { + // Atlas is full, let the user to resize the atlas (or not), and try again. + stash->handleError(stash->errorUptr, FONS_ATLAS_FULL, 0); + added = fons__atlasAddRect(stash->atlas, gw, gh, &gx, &gy); + } + if (added == 0) return NULL; + + // Init glyph. + glyph = fons__allocGlyph(font); + glyph->codepoint = codepoint; + glyph->size = isize; + glyph->blur = iblur; + glyph->index = g; + glyph->x0 = (short)gx; + glyph->y0 = (short)gy; + glyph->x1 = (short)(glyph->x0+gw); + glyph->y1 = (short)(glyph->y0+gh); + glyph->xadv = (short)(scale * advance * 10.0f); + glyph->xoff = (short)(x0 - pad); + glyph->yoff = (short)(y0 - pad); + glyph->next = 0; + + // Insert char to hash lookup. + glyph->next = font->lut[h]; + font->lut[h] = font->nglyphs-1; + + // Rasterize + dst = &stash->texData[(glyph->x0+pad) + (glyph->y0+pad) * stash->params.width]; + fons__tt_renderGlyphBitmap(&renderFont->font, dst, gw-pad*2,gh-pad*2, stash->params.width, scale,scale, g); + + // Make sure there is one pixel empty border. + dst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width]; + for (y = 0; y < gh; y++) { + dst[y*stash->params.width] = 0; + dst[gw-1 + y*stash->params.width] = 0; + } + for (x = 0; x < gw; x++) { + dst[x] = 0; + dst[x + (gh-1)*stash->params.width] = 0; + } + + // Debug code to color the glyph background +/* unsigned char* fdst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width]; + for (y = 0; y < gh; y++) { + for (x = 0; x < gw; x++) { + int a = (int)fdst[x+y*stash->params.width] + 20; + if (a > 255) a = 255; + fdst[x+y*stash->params.width] = a; + } + }*/ + + // Blur + if (iblur > 0) { + stash->nscratch = 0; + bdst = &stash->texData[glyph->x0 + glyph->y0 * stash->params.width]; + fons__blur(stash, bdst, gw,gh, stash->params.width, iblur); + } + + stash->dirtyRect[0] = fons__mini(stash->dirtyRect[0], glyph->x0); + stash->dirtyRect[1] = fons__mini(stash->dirtyRect[1], glyph->y0); + stash->dirtyRect[2] = fons__maxi(stash->dirtyRect[2], glyph->x1); + stash->dirtyRect[3] = fons__maxi(stash->dirtyRect[3], glyph->y1); + + return glyph; +} + +static void fons__getQuad(FONScontext* stash, FONSfont* font, + int prevGlyphIndex, FONSglyph* glyph, + float scale, float spacing, float* x, float* y, FONSquad* q) +{ + float rx,ry,xoff,yoff,x0,y0,x1,y1; + + if (prevGlyphIndex != -1) { + float adv = fons__tt_getGlyphKernAdvance(&font->font, prevGlyphIndex, glyph->index) * scale; + *x += (int)(adv + spacing + 0.5f); + } + + // Each glyph has 2px border to allow good interpolation, + // one pixel to prevent leaking, and one to allow good interpolation for rendering. + // Inset the texture region by one pixel for correct interpolation. + xoff = (short)(glyph->xoff+1); + yoff = (short)(glyph->yoff+1); + x0 = (float)(glyph->x0+1); + y0 = (float)(glyph->y0+1); + x1 = (float)(glyph->x1-1); + y1 = (float)(glyph->y1-1); + + if (stash->params.flags & FONS_ZERO_TOPLEFT) { + rx = (float)(int)(*x + xoff); + ry = (float)(int)(*y + yoff); + + q->x0 = rx; + q->y0 = ry; + q->x1 = rx + x1 - x0; + q->y1 = ry + y1 - y0; + + q->s0 = x0 * stash->itw; + q->t0 = y0 * stash->ith; + q->s1 = x1 * stash->itw; + q->t1 = y1 * stash->ith; + } else { + rx = (float)(int)(*x + xoff); + ry = (float)(int)(*y - yoff); + + q->x0 = rx; + q->y0 = ry; + q->x1 = rx + x1 - x0; + q->y1 = ry - y1 + y0; + + q->s0 = x0 * stash->itw; + q->t0 = y0 * stash->ith; + q->s1 = x1 * stash->itw; + q->t1 = y1 * stash->ith; + } + + *x += (int)(glyph->xadv / 10.0f + 0.5f); +} + +static void fons__flush(FONScontext* stash) +{ + // Flush texture + if (stash->dirtyRect[0] < stash->dirtyRect[2] && stash->dirtyRect[1] < stash->dirtyRect[3]) { + if (stash->params.renderUpdate != NULL) + stash->params.renderUpdate(stash->params.userPtr, stash->dirtyRect, stash->texData); + // Reset dirty rect + stash->dirtyRect[0] = stash->params.width; + stash->dirtyRect[1] = stash->params.height; + stash->dirtyRect[2] = 0; + stash->dirtyRect[3] = 0; + } + + // Flush triangles + if (stash->nverts > 0) { + if (stash->params.renderDraw != NULL) + stash->params.renderDraw(stash->params.userPtr, stash->verts, stash->tcoords, stash->colors, stash->nverts); + stash->nverts = 0; + } +} + +static __inline void fons__vertex(FONScontext* stash, float x, float y, float s, float t, unsigned int c) +{ + stash->verts[stash->nverts*2+0] = x; + stash->verts[stash->nverts*2+1] = y; + stash->tcoords[stash->nverts*2+0] = s; + stash->tcoords[stash->nverts*2+1] = t; + stash->colors[stash->nverts] = c; + stash->nverts++; +} + +static float fons__getVertAlign(FONScontext* stash, FONSfont* font, int align, short isize) +{ + if (stash->params.flags & FONS_ZERO_TOPLEFT) { + if (align & FONS_ALIGN_TOP) { + return font->ascender * (float)isize/10.0f; + } else if (align & FONS_ALIGN_MIDDLE) { + return (font->ascender + font->descender) / 2.0f * (float)isize/10.0f; + } else if (align & FONS_ALIGN_BASELINE) { + return 0.0f; + } else if (align & FONS_ALIGN_BOTTOM) { + return font->descender * (float)isize/10.0f; + } + } else { + if (align & FONS_ALIGN_TOP) { + return -font->ascender * (float)isize/10.0f; + } else if (align & FONS_ALIGN_MIDDLE) { + return -(font->ascender + font->descender) / 2.0f * (float)isize/10.0f; + } else if (align & FONS_ALIGN_BASELINE) { + return 0.0f; + } else if (align & FONS_ALIGN_BOTTOM) { + return -font->descender * (float)isize/10.0f; + } + } + return 0.0; +} + +FONS_DEF float fonsDrawText(FONScontext* stash, + float x, float y, + const char* str, const char* end) +{ + FONSstate* state = fons__getState(stash); + unsigned int codepoint; + unsigned int utf8state = 0; + FONSglyph* glyph = NULL; + FONSquad q; + int prevGlyphIndex = -1; + short isize = (short)(state->size*10.0f); + short iblur = (short)state->blur; + float scale; + FONSfont* font; + float width; + + if (stash == NULL) return x; + if (state->font < 0 || state->font >= stash->nfonts) return x; + font = stash->fonts[state->font]; + if (font->data == NULL) return x; + + scale = fons__tt_getPixelHeightScale(&font->font, (float)isize/10.0f); + + if (end == NULL) + end = str + strlen(str); + + // Align horizontally + if (state->align & FONS_ALIGN_LEFT) { + // empty + } else if (state->align & FONS_ALIGN_RIGHT) { + width = fonsTextBounds(stash, x,y, str, end, NULL); + x -= width; + } else if (state->align & FONS_ALIGN_CENTER) { + width = fonsTextBounds(stash, x,y, str, end, NULL); + x -= width * 0.5f; + } + // Align vertically. + y += fons__getVertAlign(stash, font, state->align, isize); + + for (; str != end; ++str) { + if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str)) + continue; + glyph = fons__getGlyph(stash, font, codepoint, isize, iblur); + if (glyph != NULL) { + fons__getQuad(stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q); + + if (stash->nverts+6 > FONS_VERTEX_COUNT) + fons__flush(stash); + + fons__vertex(stash, q.x0, q.y0, q.s0, q.t0, state->color); + fons__vertex(stash, q.x1, q.y1, q.s1, q.t1, state->color); + fons__vertex(stash, q.x1, q.y0, q.s1, q.t0, state->color); + + fons__vertex(stash, q.x0, q.y0, q.s0, q.t0, state->color); + fons__vertex(stash, q.x0, q.y1, q.s0, q.t1, state->color); + fons__vertex(stash, q.x1, q.y1, q.s1, q.t1, state->color); + } + prevGlyphIndex = glyph != NULL ? glyph->index : -1; + } + fons__flush(stash); + + return x; +} + +FONS_DEF int fonsTextIterInit(FONScontext* stash, FONStextIter* iter, + float x, float y, const char* str, const char* end) +{ + FONSstate* state = fons__getState(stash); + float width; + + memset(iter, 0, sizeof(*iter)); + + if (stash == NULL) return 0; + if (state->font < 0 || state->font >= stash->nfonts) return 0; + iter->font = stash->fonts[state->font]; + if (iter->font->data == NULL) return 0; + + iter->isize = (short)(state->size*10.0f); + iter->iblur = (short)state->blur; + iter->scale = fons__tt_getPixelHeightScale(&iter->font->font, (float)iter->isize/10.0f); + + // Align horizontally + if (state->align & FONS_ALIGN_LEFT) { + // empty + } else if (state->align & FONS_ALIGN_RIGHT) { + width = fonsTextBounds(stash, x,y, str, end, NULL); + x -= width; + } else if (state->align & FONS_ALIGN_CENTER) { + width = fonsTextBounds(stash, x,y, str, end, NULL); + x -= width * 0.5f; + } + // Align vertically. + y += fons__getVertAlign(stash, iter->font, state->align, iter->isize); + + if (end == NULL) + end = str + strlen(str); + + iter->x = iter->nextx = x; + iter->y = iter->nexty = y; + iter->spacing = state->spacing; + iter->str = str; + iter->next = str; + iter->end = end; + iter->codepoint = 0; + iter->prevGlyphIndex = -1; + + return 1; +} + +FONS_DEF int fonsTextIterNext(FONScontext* stash, FONStextIter* iter, FONSquad* quad) +{ + FONSglyph* glyph = NULL; + const char* str = iter->next; + iter->str = iter->next; + + if (str == iter->end) + return 0; + + for (; str != iter->end; str++) { + if (fons__decutf8(&iter->utf8state, &iter->codepoint, *(const unsigned char*)str)) + continue; + str++; + // Get glyph and quad + iter->x = iter->nextx; + iter->y = iter->nexty; + glyph = fons__getGlyph(stash, iter->font, iter->codepoint, iter->isize, iter->iblur); + if (glyph != NULL) + fons__getQuad(stash, iter->font, iter->prevGlyphIndex, glyph, iter->scale, iter->spacing, &iter->nextx, &iter->nexty, quad); + iter->prevGlyphIndex = glyph != NULL ? glyph->index : -1; + break; + } + iter->next = str; + + return 1; +} + +FONS_DEF void fonsDrawDebug(FONScontext* stash, float x, float y) +{ + int i; + int w = stash->params.width; + int h = stash->params.height; + float u = w == 0 ? 0 : (1.0f / w); + float v = h == 0 ? 0 : (1.0f / h); + + if (stash->nverts+6+6 > FONS_VERTEX_COUNT) + fons__flush(stash); + + // Draw background + fons__vertex(stash, x+0, y+0, u, v, 0x0fffffff); + fons__vertex(stash, x+w, y+h, u, v, 0x0fffffff); + fons__vertex(stash, x+w, y+0, u, v, 0x0fffffff); + + fons__vertex(stash, x+0, y+0, u, v, 0x0fffffff); + fons__vertex(stash, x+0, y+h, u, v, 0x0fffffff); + fons__vertex(stash, x+w, y+h, u, v, 0x0fffffff); + + // Draw texture + fons__vertex(stash, x+0, y+0, 0, 0, 0xffffffff); + fons__vertex(stash, x+w, y+h, 1, 1, 0xffffffff); + fons__vertex(stash, x+w, y+0, 1, 0, 0xffffffff); + + fons__vertex(stash, x+0, y+0, 0, 0, 0xffffffff); + fons__vertex(stash, x+0, y+h, 0, 1, 0xffffffff); + fons__vertex(stash, x+w, y+h, 1, 1, 0xffffffff); + + // Drawbug draw atlas + for (i = 0; i < stash->atlas->nnodes; i++) { + FONSatlasNode* n = &stash->atlas->nodes[i]; + + if (stash->nverts+6 > FONS_VERTEX_COUNT) + fons__flush(stash); + + fons__vertex(stash, x+n->x+0, y+n->y+0, u, v, 0xc00000ff); + fons__vertex(stash, x+n->x+n->width, y+n->y+1, u, v, 0xc00000ff); + fons__vertex(stash, x+n->x+n->width, y+n->y+0, u, v, 0xc00000ff); + + fons__vertex(stash, x+n->x+0, y+n->y+0, u, v, 0xc00000ff); + fons__vertex(stash, x+n->x+0, y+n->y+1, u, v, 0xc00000ff); + fons__vertex(stash, x+n->x+n->width, y+n->y+1, u, v, 0xc00000ff); + } + + fons__flush(stash); +} + +FONS_DEF float fonsTextBounds(FONScontext* stash, + float x, float y, + const char* str, const char* end, + float* bounds) +{ + FONSstate* state = fons__getState(stash); + unsigned int codepoint; + unsigned int utf8state = 0; + FONSquad q; + FONSglyph* glyph = NULL; + int prevGlyphIndex = -1; + short isize = (short)(state->size*10.0f); + short iblur = (short)state->blur; + float scale; + FONSfont* font; + float startx, advance; + float minx, miny, maxx, maxy; + + if (stash == NULL) return 0; + if (state->font < 0 || state->font >= stash->nfonts) return 0; + font = stash->fonts[state->font]; + if (font->data == NULL) return 0; + + scale = fons__tt_getPixelHeightScale(&font->font, (float)isize/10.0f); + + // Align vertically. + y += fons__getVertAlign(stash, font, state->align, isize); + + minx = maxx = x; + miny = maxy = y; + startx = x; + + if (end == NULL) + end = str + strlen(str); + + for (; str != end; ++str) { + if (fons__decutf8(&utf8state, &codepoint, *(const unsigned char*)str)) + continue; + glyph = fons__getGlyph(stash, font, codepoint, isize, iblur); + if (glyph != NULL) { + fons__getQuad(stash, font, prevGlyphIndex, glyph, scale, state->spacing, &x, &y, &q); + if (q.x0 < minx) minx = q.x0; + if (q.x1 > maxx) maxx = q.x1; + if (stash->params.flags & FONS_ZERO_TOPLEFT) { + if (q.y0 < miny) miny = q.y0; + if (q.y1 > maxy) maxy = q.y1; + } else { + if (q.y1 < miny) miny = q.y1; + if (q.y0 > maxy) maxy = q.y0; + } + } + prevGlyphIndex = glyph != NULL ? glyph->index : -1; + } + + advance = x - startx; + + // Align horizontally + if (state->align & FONS_ALIGN_LEFT) { + // empty + } else if (state->align & FONS_ALIGN_RIGHT) { + minx -= advance; + maxx -= advance; + } else if (state->align & FONS_ALIGN_CENTER) { + minx -= advance * 0.5f; + maxx -= advance * 0.5f; + } + + if (bounds) { + bounds[0] = minx; + bounds[1] = miny; + bounds[2] = maxx; + bounds[3] = maxy; + } + + return advance; +} + +FONS_DEF void fonsVertMetrics(FONScontext* stash, + float* ascender, float* descender, float* lineh) +{ + FONSfont* font; + FONSstate* state = fons__getState(stash); + short isize; + + if (stash == NULL) return; + if (state->font < 0 || state->font >= stash->nfonts) return; + font = stash->fonts[state->font]; + isize = (short)(state->size*10.0f); + if (font->data == NULL) return; + + if (ascender) + *ascender = font->ascender*isize/10.0f; + if (descender) + *descender = font->descender*isize/10.0f; + if (lineh) + *lineh = font->lineh*isize/10.0f; +} + +FONS_DEF void fonsLineBounds(FONScontext* stash, float y, float* miny, float* maxy) +{ + FONSfont* font; + FONSstate* state = fons__getState(stash); + short isize; + + if (stash == NULL) return; + if (state->font < 0 || state->font >= stash->nfonts) return; + font = stash->fonts[state->font]; + isize = (short)(state->size*10.0f); + if (font->data == NULL) return; + + y += fons__getVertAlign(stash, font, state->align, isize); + + if (stash->params.flags & FONS_ZERO_TOPLEFT) { + *miny = y - font->ascender * (float)isize/10.0f; + *maxy = *miny + font->lineh*isize/10.0f; + } else { + *maxy = y + font->descender * (float)isize/10.0f; + *miny = *maxy - font->lineh*isize/10.0f; + } +} + +FONS_DEF const unsigned char* fonsGetTextureData(FONScontext* stash, int* width, int* height) +{ + if (width != NULL) + *width = stash->params.width; + if (height != NULL) + *height = stash->params.height; + return stash->texData; +} + +FONS_DEF int fonsValidateTexture(FONScontext* stash, int* dirty) +{ + if (stash->dirtyRect[0] < stash->dirtyRect[2] && stash->dirtyRect[1] < stash->dirtyRect[3]) { + dirty[0] = stash->dirtyRect[0]; + dirty[1] = stash->dirtyRect[1]; + dirty[2] = stash->dirtyRect[2]; + dirty[3] = stash->dirtyRect[3]; + // Reset dirty rect + stash->dirtyRect[0] = stash->params.width; + stash->dirtyRect[1] = stash->params.height; + stash->dirtyRect[2] = 0; + stash->dirtyRect[3] = 0; + return 1; + } + return 0; +} + +FONS_DEF void fonsDeleteInternal(FONScontext* stash) +{ + int i; + if (stash == NULL) return; + + if (stash->params.renderDelete) + stash->params.renderDelete(stash->params.userPtr); + + for (i = 0; i < stash->nfonts; ++i) + fons__freeFont(stash->fonts[i]); + + if (stash->atlas) fons__deleteAtlas(stash->atlas); + if (stash->fonts) FONTSTASH_FREE(stash->fonts); + if (stash->texData) FONTSTASH_FREE(stash->texData); + if (stash->scratch) FONTSTASH_FREE(stash->scratch); + FONTSTASH_FREE(stash); +} + +FONS_DEF void fonsSetErrorCallback(FONScontext* stash, void (*callback)(void* uptr, int error, int val), void* uptr) +{ + if (stash == NULL) return; + stash->handleError = callback; + stash->errorUptr = uptr; +} + +FONS_DEF void fonsGetAtlasSize(FONScontext* stash, int* width, int* height) +{ + if (stash == NULL) return; + *width = stash->params.width; + *height = stash->params.height; +} + +FONS_DEF int fonsExpandAtlas(FONScontext* stash, int width, int height) +{ + int i, maxy = 0; + unsigned char* data = NULL; + if (stash == NULL) return 0; + + width = fons__maxi(width, stash->params.width); + height = fons__maxi(height, stash->params.height); + + if (width == stash->params.width && height == stash->params.height) + return 1; + + // Flush pending glyphs. + fons__flush(stash); + + // Create new texture + if (stash->params.renderResize != NULL) { + if (stash->params.renderResize(stash->params.userPtr, width, height) == 0) + return 0; + } + // Copy old texture data over. + data = (unsigned char*)FONTSTASH_MALLOC(width * height); + if (data == NULL) + return 0; + for (i = 0; i < stash->params.height; i++) { + unsigned char* dst = &data[i*width]; + unsigned char* src = &stash->texData[i*stash->params.width]; + memcpy(dst, src, stash->params.width); + if (width > stash->params.width) + memset(dst+stash->params.width, 0, width - stash->params.width); + } + if (height > stash->params.height) + memset(&data[stash->params.height * width], 0, (height - stash->params.height) * width); + + FONTSTASH_FREE(stash->texData); + stash->texData = data; + + // Increase atlas size + fons__atlasExpand(stash->atlas, width, height); + + // Add existing data as dirty. + for (i = 0; i < stash->atlas->nnodes; i++) + maxy = fons__maxi(maxy, stash->atlas->nodes[i].y); + stash->dirtyRect[0] = 0; + stash->dirtyRect[1] = 0; + stash->dirtyRect[2] = stash->params.width; + stash->dirtyRect[3] = maxy; + + stash->params.width = width; + stash->params.height = height; + stash->itw = 1.0f/stash->params.width; + stash->ith = 1.0f/stash->params.height; + + return 1; +} + +FONS_DEF int fonsResetAtlas(FONScontext* stash, int width, int height) +{ + int i, j; + if (stash == NULL) return 0; + + // Flush pending glyphs. + fons__flush(stash); + + // Create new texture + if (stash->params.renderResize != NULL) { + if (stash->params.renderResize(stash->params.userPtr, width, height) == 0) + return 0; + } + + // Reset atlas + fons__atlasReset(stash->atlas, width, height); + + // Clear texture data. + stash->texData = (unsigned char*)FONTSTASH_REALLOC(stash->texData, width * height); + if (stash->texData == NULL) return 0; + memset(stash->texData, 0, width * height); + + // Reset dirty rect + stash->dirtyRect[0] = width; + stash->dirtyRect[1] = height; + stash->dirtyRect[2] = 0; + stash->dirtyRect[3] = 0; + + // Reset cached glyphs + for (i = 0; i < stash->nfonts; i++) { + FONSfont* font = stash->fonts[i]; + font->nglyphs = 0; + for (j = 0; j < FONS_HASH_LUT_SIZE; j++) + font->lut[j] = -1; + } + + stash->params.width = width; + stash->params.height = height; + stash->itw = 1.0f/stash->params.width; + stash->ith = 1.0f/stash->params.height; + + // Add white rect at 0,0 for debug drawing. + fons__addWhiteRect(stash, 2,2); + + return 1; +} + +#endif // FONTSTASH_IMPLEMENTATION diff --git a/v_windows/v/old/thirdparty/fontstash/stb_truetype.h b/v_windows/v/old/thirdparty/fontstash/stb_truetype.h new file mode 100644 index 0000000..f0f98a4 --- /dev/null +++ b/v_windows/v/old/thirdparty/fontstash/stb_truetype.h @@ -0,0 +1,5011 @@ +// stb_truetype.h - v1.24 - public domain +// authored from 2009-2020 by Sean Barrett / RAD Game Tools +// +// ======================================================================= +// +// NO SECURITY GUARANTEE -- DO NOT USE THIS ON UNTRUSTED FONT FILES +// +// This library does no range checking of the offsets found in the file, +// meaning an attacker can use it to read arbitrary memory. +// +// ======================================================================= +// +// This library processes TrueType files: +// parse files +// extract glyph metrics +// extract glyph shapes +// render glyphs to one-channel bitmaps with antialiasing (box filter) +// render glyphs to one-channel SDF bitmaps (signed-distance field/function) +// +// Todo: +// non-MS cmaps +// crashproof on bad data +// hinting? (no longer patented) +// cleartype-style AA? +// optimize: use simple memory allocator for intermediates +// optimize: build edge-list directly from curves +// optimize: rasterize directly from curves? +// +// ADDITIONAL CONTRIBUTORS +// +// Mikko Mononen: compound shape support, more cmap formats +// Tor Andersson: kerning, subpixel rendering +// Dougall Johnson: OpenType / Type 2 font handling +// Daniel Ribeiro Maciel: basic GPOS-based kerning +// +// Misc other: +// Ryan Gordon +// Simon Glass +// github:IntellectualKitty +// Imanol Celaya +// Daniel Ribeiro Maciel +// +// Bug/warning reports/fixes: +// "Zer" on mollyrocket Fabian "ryg" Giesen github:NiLuJe +// Cass Everitt Martins Mozeiko github:aloucks +// stoiko (Haemimont Games) Cap Petschulat github:oyvindjam +// Brian Hook Omar Cornut github:vassvik +// Walter van Niftrik Ryan Griege +// David Gow Peter LaValle +// David Given Sergey Popov +// Ivan-Assen Ivanov Giumo X. Clanjor +// Anthony Pesch Higor Euripedes +// Johan Duparc Thomas Fields +// Hou Qiming Derek Vinyard +// Rob Loach Cort Stratton +// Kenney Phillis Jr. Brian Costabile +// Ken Voskuil (kaesve) +// +// VERSION HISTORY +// +// 1.24 (2020-02-05) fix warning +// 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS) +// 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined +// 1.21 (2019-02-25) fix warning +// 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics() +// 1.19 (2018-02-11) GPOS kerning, STBTT_fmod +// 1.18 (2018-01-29) add missing function +// 1.17 (2017-07-23) make more arguments const; doc fix +// 1.16 (2017-07-12) SDF support +// 1.15 (2017-03-03) make more arguments const +// 1.14 (2017-01-16) num-fonts-in-TTC function +// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts +// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual +// 1.11 (2016-04-02) fix unused-variable warning +// 1.10 (2016-04-02) user-defined fabs(); rare memory leak; remove duplicate typedef +// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use allocation userdata properly +// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges +// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; +// variant PackFontRanges to pack and render in separate phases; +// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); +// fixed an assert() bug in the new rasterizer +// replace assert() with STBTT_assert() in new rasterizer +// +// Full history can be found at the end of this file. +// +// LICENSE +// +// See end of file for license information. +// +// USAGE +// +// Include this file in whatever places need to refer to it. In ONE C/C++ +// file, write: +// #define STB_TRUETYPE_IMPLEMENTATION +// before the #include of this file. This expands out the actual +// implementation into that C/C++ file. +// +// To make the implementation private to the file that generates the implementation, +// #define STBTT_STATIC +// +// Simple 3D API (don't ship this, but it's fine for tools and quick start) +// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture +// stbtt_GetBakedQuad() -- compute quad to draw for a given char +// +// Improved 3D API (more shippable): +// #include "stb_rect_pack.h" -- optional, but you really want it +// stbtt_PackBegin() +// stbtt_PackSetOversampling() -- for improved quality on small fonts +// stbtt_PackFontRanges() -- pack and renders +// stbtt_PackEnd() +// stbtt_GetPackedQuad() +// +// "Load" a font file from a memory buffer (you have to keep the buffer loaded) +// stbtt_InitFont() +// stbtt_GetFontOffsetForIndex() -- indexing for TTC font collections +// stbtt_GetNumberOfFonts() -- number of fonts for TTC font collections +// +// Render a unicode codepoint to a bitmap +// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap +// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide +// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be +// +// Character advance/positioning +// stbtt_GetCodepointHMetrics() +// stbtt_GetFontVMetrics() +// stbtt_GetFontVMetricsOS2() +// stbtt_GetCodepointKernAdvance() +// +// Starting with version 1.06, the rasterizer was replaced with a new, +// faster and generally-more-precise rasterizer. The new rasterizer more +// accurately measures pixel coverage for anti-aliasing, except in the case +// where multiple shapes overlap, in which case it overestimates the AA pixel +// coverage. Thus, anti-aliasing of intersecting shapes may look wrong. If +// this turns out to be a problem, you can re-enable the old rasterizer with +// #define STBTT_RASTERIZER_VERSION 1 +// which will incur about a 15% speed hit. +// +// ADDITIONAL DOCUMENTATION +// +// Immediately after this block comment are a series of sample programs. +// +// After the sample programs is the "header file" section. This section +// includes documentation for each API function. +// +// Some important concepts to understand to use this library: +// +// Codepoint +// Characters are defined by unicode codepoints, e.g. 65 is +// uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is +// the hiragana for "ma". +// +// Glyph +// A visual character shape (every codepoint is rendered as +// some glyph) +// +// Glyph index +// A font-specific integer ID representing a glyph +// +// Baseline +// Glyph shapes are defined relative to a baseline, which is the +// bottom of uppercase characters. Characters extend both above +// and below the baseline. +// +// Current Point +// As you draw text to the screen, you keep track of a "current point" +// which is the origin of each character. The current point's vertical +// position is the baseline. Even "baked fonts" use this model. +// +// Vertical Font Metrics +// The vertical qualities of the font, used to vertically position +// and space the characters. See docs for stbtt_GetFontVMetrics. +// +// Font Size in Pixels or Points +// The preferred interface for specifying font sizes in stb_truetype +// is to specify how tall the font's vertical extent should be in pixels. +// If that sounds good enough, skip the next paragraph. +// +// Most font APIs instead use "points", which are a common typographic +// measurement for describing font size, defined as 72 points per inch. +// stb_truetype provides a point API for compatibility. However, true +// "per inch" conventions don't make much sense on computer displays +// since different monitors have different number of pixels per +// inch. For example, Windows traditionally uses a convention that +// there are 96 pixels per inch, thus making 'inch' measurements have +// nothing to do with inches, and thus effectively defining a point to +// be 1.333 pixels. Additionally, the TrueType font data provides +// an explicit scale factor to scale a given font's glyphs to points, +// but the author has observed that this scale factor is often wrong +// for non-commercial fonts, thus making fonts scaled in points +// according to the TrueType spec incoherently sized in practice. +// +// DETAILED USAGE: +// +// Scale: +// Select how high you want the font to be, in points or pixels. +// Call ScaleForPixelHeight or ScaleForMappingEmToPixels to compute +// a scale factor SF that will be used by all other functions. +// +// Baseline: +// You need to select a y-coordinate that is the baseline of where +// your text will appear. Call GetFontBoundingBox to get the baseline-relative +// bounding box for all characters. SF*-y0 will be the distance in pixels +// that the worst-case character could extend above the baseline, so if +// you want the top edge of characters to appear at the top of the +// screen where y=0, then you would set the baseline to SF*-y0. +// +// Current point: +// Set the current point where the first character will appear. The +// first character could extend left of the current point; this is font +// dependent. You can either choose a current point that is the leftmost +// point and hope, or add some padding, or check the bounding box or +// left-side-bearing of the first character to be displayed and set +// the current point based on that. +// +// Displaying a character: +// Compute the bounding box of the character. It will contain signed values +// relative to . I.e. if it returns x0,y0,x1,y1, +// then the character should be displayed in the rectangle from +// to = 32 && *text < 128) { + stbtt_aligned_quad q; + stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9 + glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0); + glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0); + glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1); + glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1); + } + ++text; + } + glEnd(); +} +#endif +// +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program (this compiles): get a single bitmap, print as ASCII art +// +#if 0 +#include +#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation +#include "stb_truetype.h" + +char ttf_buffer[1<<25]; + +int main(int argc, char **argv) +{ + stbtt_fontinfo font; + unsigned char *bitmap; + int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20); + + fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb")); + + stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); + bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0); + + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) + putchar(" .:ioVM@"[bitmap[j*w+i]>>5]); + putchar('\n'); + } + return 0; +} +#endif +// +// Output: +// +// .ii. +// @@@@@@. +// V@Mio@@o +// :i. V@V +// :oM@@M +// :@@@MM@M +// @@o o@M +// :@@. M@M +// @@@o@@@@ +// :M@@V:@@. +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program: print "Hello World!" banner, with bugs +// +#if 0 +char buffer[24<<20]; +unsigned char screen[20][79]; + +int main(int arg, char **argv) +{ + stbtt_fontinfo font; + int i,j,ascent,baseline,ch=0; + float scale, xpos=2; // leave a little padding in case the character extends left + char *text = "Heljo World!"; // intentionally misspelled to show 'lj' brokenness + + fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb")); + stbtt_InitFont(&font, buffer, 0); + + scale = stbtt_ScaleForPixelHeight(&font, 15); + stbtt_GetFontVMetrics(&font, &ascent,0,0); + baseline = (int) (ascent*scale); + + while (text[ch]) { + int advance,lsb,x0,y0,x1,y1; + float x_shift = xpos - (float) floor(xpos); + stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb); + stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1); + stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]); + // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong + // because this API is really for baking character bitmaps into textures. if you want to render + // a sequence of characters, you really need to render each bitmap to a temp buffer, then + // "alpha blend" that into the working buffer + xpos += (advance * scale); + if (text[ch+1]) + xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]); + ++ch; + } + + for (j=0; j < 20; ++j) { + for (i=0; i < 78; ++i) + putchar(" .:ioVM@"[screen[j][i]>>5]); + putchar('\n'); + } + + return 0; +} +#endif + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +//// +//// INTEGRATION WITH YOUR CODEBASE +//// +//// The following sections allow you to supply alternate definitions +//// of C library functions used by stb_truetype, e.g. if you don't +//// link with the C runtime library. + +#ifdef STB_TRUETYPE_IMPLEMENTATION + // #define your own (u)stbtt_int8/16/32 before including to override this + #ifndef stbtt_uint8 + typedef unsigned char stbtt_uint8; + typedef signed char stbtt_int8; + typedef unsigned short stbtt_uint16; + typedef signed short stbtt_int16; + typedef unsigned int stbtt_uint32; + typedef signed int stbtt_int32; + #endif + + typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1]; + typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1]; + + // e.g. #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h + #ifndef STBTT_ifloor + #include + #define STBTT_ifloor(x) ((int) floor(x)) + #define STBTT_iceil(x) ((int) ceil(x)) + #endif + + #ifndef STBTT_sqrt + #include + #define STBTT_sqrt(x) sqrt(x) + #define STBTT_pow(x,y) pow(x,y) + #endif + + #ifndef STBTT_fmod + #include + #define STBTT_fmod(x,y) fmod(x,y) + #endif + + #ifndef STBTT_cos + #include + #define STBTT_cos(x) cos(x) + #define STBTT_acos(x) acos(x) + #endif + + #ifndef STBTT_fabs + #include + #define STBTT_fabs(x) fabs(x) + #endif + + // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h + #ifndef STBTT_malloc + #include + #define STBTT_malloc(x,u) ((void)(u),FONTSTASH_MALLOC(x)) + #define STBTT_free(x,u) ((void)(u),FONTSTASH_FREE(x)) + #endif + + #ifndef STBTT_assert + #include + #define STBTT_assert(x) assert(x) + #endif + + #ifndef STBTT_strlen + #include + #define STBTT_strlen(x) strlen(x) + #endif + + #ifndef STBTT_memcpy + #include + #define STBTT_memcpy memcpy + #define STBTT_memset memset + #endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// INTERFACE +//// +//// + +#ifndef __STB_INCLUDE_STB_TRUETYPE_H__ +#define __STB_INCLUDE_STB_TRUETYPE_H__ + +#ifdef STBTT_STATIC +#define STBTT_DEF static +#else +#define STBTT_DEF extern +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// private structure +typedef struct +{ + unsigned char *data; + int cursor; + int size; +} stbtt__buf; + +////////////////////////////////////////////////////////////////////////////// +// +// TEXTURE BAKING API +// +// If you use this API, you only have to call two functions ever. +// + +typedef struct +{ + unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap + float xoff,yoff,xadvance; +} stbtt_bakedchar; + +STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata); // you allocate this, it's num_chars long +// if return is positive, the first unused row of the bitmap +// if return is negative, returns the negative of the number of characters that fit +// if return is 0, no characters fit and no rows were used +// This uses a very crappy packing. + +typedef struct +{ + float x0,y0,s0,t0; // top-left + float x1,y1,s1,t1; // bottom-right +} stbtt_aligned_quad; + +STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, // same data as above + int char_index, // character to display + float *xpos, float *ypos, // pointers to current position in screen pixel space + stbtt_aligned_quad *q, // output: quad to draw + int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier +// Call GetBakedQuad with char_index = 'character - first_char', and it +// creates the quad you need to draw and advances the current position. +// +// The coordinate system used assumes y increases downwards. +// +// Characters will extend both above and below the current position; +// see discussion of "BASELINE" above. +// +// It's inefficient; you might want to c&p it and optimize it. + +STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap); +// Query the font vertical metrics without having to create a font first. + + +////////////////////////////////////////////////////////////////////////////// +// +// NEW TEXTURE BAKING API +// +// This provides options for packing multiple fonts into one atlas, not +// perfectly but better than nothing. + +typedef struct +{ + unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap + float xoff,yoff,xadvance; + float xoff2,yoff2; +} stbtt_packedchar; + +typedef struct stbtt_pack_context stbtt_pack_context; +typedef struct stbtt_fontinfo stbtt_fontinfo; +#ifndef STB_RECT_PACK_VERSION +typedef struct stbrp_rect stbrp_rect; +#endif + +STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context); +// Initializes a packing context stored in the passed-in stbtt_pack_context. +// Future calls using this context will pack characters into the bitmap passed +// in here: a 1-channel bitmap that is width * height. stride_in_bytes is +// the distance from one row to the next (or 0 to mean they are packed tightly +// together). "padding" is the amount of padding to leave between each +// character (normally you want '1' for bitmaps you'll use as textures with +// bilinear filtering). +// +// Returns 0 on failure, 1 on success. + +STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc); +// Cleans up the packing context and frees all memory. + +#define STBTT_POINT_SIZE(x) (-(x)) + +STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size, + int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range); +// Creates character bitmaps from the font_index'th font found in fontdata (use +// font_index=0 if you don't know what that is). It creates num_chars_in_range +// bitmaps for characters with unicode values starting at first_unicode_char_in_range +// and increasing. Data for how to render them is stored in chardata_for_range; +// pass these to stbtt_GetPackedQuad to get back renderable quads. +// +// font_size is the full height of the character from ascender to descender, +// as computed by stbtt_ScaleForPixelHeight. To use a point size as computed +// by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE() +// and pass that result as 'font_size': +// ..., 20 , ... // font max minus min y is 20 pixels tall +// ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall + +typedef struct +{ + float font_size; + int first_unicode_codepoint_in_range; // if non-zero, then the chars are continuous, and this is the first codepoint + int *array_of_unicode_codepoints; // if non-zero, then this is an array of unicode codepoints + int num_chars; + stbtt_packedchar *chardata_for_range; // output + unsigned char h_oversample, v_oversample; // don't set these, they're used internally +} stbtt_pack_range; + +STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges); +// Creates character bitmaps from multiple ranges of characters stored in +// ranges. This will usually create a better-packed bitmap than multiple +// calls to stbtt_PackFontRange. Note that you can call this multiple +// times within a single PackBegin/PackEnd. + +STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample); +// Oversampling a font increases the quality by allowing higher-quality subpixel +// positioning, and is especially valuable at smaller text sizes. +// +// This function sets the amount of oversampling for all following calls to +// stbtt_PackFontRange(s) or stbtt_PackFontRangesGatherRects for a given +// pack context. The default (no oversampling) is achieved by h_oversample=1 +// and v_oversample=1. The total number of pixels required is +// h_oversample*v_oversample larger than the default; for example, 2x2 +// oversampling requires 4x the storage of 1x1. For best results, render +// oversampled textures with bilinear filtering. Look at the readme in +// stb/tests/oversample for information about oversampled fonts +// +// To use with PackFontRangesGather etc., you must set it before calls +// call to PackFontRangesGatherRects. + +STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip); +// If skip != 0, this tells stb_truetype to skip any codepoints for which +// there is no corresponding glyph. If skip=0, which is the default, then +// codepoints without a glyph recived the font's "missing character" glyph, +// typically an empty box by convention. + +STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, // same data as above + int char_index, // character to display + float *xpos, float *ypos, // pointers to current position in screen pixel space + stbtt_aligned_quad *q, // output: quad to draw + int align_to_integer); + +STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); +STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects); +STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); +// Calling these functions in sequence is roughly equivalent to calling +// stbtt_PackFontRanges(). If you more control over the packing of multiple +// fonts, or if you want to pack custom data into a font texture, take a look +// at the source to of stbtt_PackFontRanges() and create a custom version +// using these functions, e.g. call GatherRects multiple times, +// building up a single array of rects, then call PackRects once, +// then call RenderIntoRects repeatedly. This may result in a +// better packing than calling PackFontRanges multiple times +// (or it may not). + +// this is an opaque structure that you shouldn't mess with which holds +// all the context needed from PackBegin to PackEnd. +struct stbtt_pack_context { + void *user_allocator_context; + void *pack_info; + int width; + int height; + int stride_in_bytes; + int padding; + int skip_missing; + unsigned int h_oversample, v_oversample; + unsigned char *pixels; + void *nodes; +}; + +////////////////////////////////////////////////////////////////////////////// +// +// FONT LOADING +// +// + +STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data); +// This function will determine the number of fonts in a font file. TrueType +// collection (.ttc) files may contain multiple fonts, while TrueType font +// (.ttf) files only contain one font. The number of fonts can be used for +// indexing with the previous function where the index is between zero and one +// less than the total fonts. If an error occurs, -1 is returned. + +STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); +// Each .ttf/.ttc file may have more than one font. Each font has a sequential +// index number starting from 0. Call this function to get the font offset for +// a given index; it returns -1 if the index is out of range. A regular .ttf +// file will only define one font and it always be at offset 0, so it will +// return '0' for index 0, and -1 for all other indices. + +// The following structure is defined publicly so you can declare one on +// the stack or as a global or etc, but you should treat it as opaque. +struct stbtt_fontinfo +{ + void * userdata; + unsigned char * data; // pointer to .ttf file + int fontstart; // offset of start of font + + int numGlyphs; // number of glyphs, needed for range checking + + int loca,head,glyf,hhea,hmtx,kern,gpos,svg; // table locations as offset from start of .ttf + int index_map; // a cmap mapping for our chosen character encoding + int indexToLocFormat; // format needed to map from glyph index to glyph + + stbtt__buf cff; // cff font data + stbtt__buf charstrings; // the charstring index + stbtt__buf gsubrs; // global charstring subroutines index + stbtt__buf subrs; // private charstring subroutines index + stbtt__buf fontdicts; // array of font dicts + stbtt__buf fdselect; // map from glyph to fontdict +}; + +STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset); +// Given an offset into the file that defines a font, this function builds +// the necessary cached info for the rest of the system. You must allocate +// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't +// need to do anything special to free it, because the contents are pure +// value data with no additional data structures. Returns 0 on failure. + + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER TO GLYPH-INDEX CONVERSIOn + +STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint); +// If you're going to perform multiple operations on the same character +// and you want a speed-up, call this function with the character you're +// going to process, then use glyph-based functions instead of the +// codepoint-based functions. +// Returns 0 if the character codepoint is not defined in the font. + + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER PROPERTIES +// + +STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels); +// computes a scale factor to produce a font whose "height" is 'pixels' tall. +// Height is measured as the distance from the highest ascender to the lowest +// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics +// and computing: +// scale = pixels / (ascent - descent) +// so if you prefer to measure height by the ascent only, use a similar calculation. + +STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels); +// computes a scale factor to produce a font whose EM size is mapped to +// 'pixels' tall. This is probably what traditional APIs compute, but +// I'm not positive. + +STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap); +// ascent is the coordinate above the baseline the font extends; descent +// is the coordinate below the baseline the font extends (i.e. it is typically negative) +// lineGap is the spacing between one row's descent and the next row's ascent... +// so you should advance the vertical position by "*ascent - *descent + *lineGap" +// these are expressed in unscaled coordinates, so you must multiply by +// the scale factor for a given size + +STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap); +// analogous to GetFontVMetrics, but returns the "typographic" values from the OS/2 +// table (specific to MS/Windows TTF files). +// +// Returns 1 on success (table present), 0 on failure. + +STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1); +// the bounding box around all possible characters + +STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing); +// leftSideBearing is the offset from the current horizontal position to the left edge of the character +// advanceWidth is the offset from the current horizontal position to the next horizontal position +// these are expressed in unscaled coordinates + +STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2); +// an additional amount to add to the 'advance' value between ch1 and ch2 + +STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1); +// Gets the bounding box of the visible part of the glyph, in unscaled coordinates + +STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing); +STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2); +STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); +// as above, but takes one or more glyph indices for greater efficiency + +typedef struct stbtt_kerningentry +{ + int glyph1; // use stbtt_FindGlyphIndex + int glyph2; + int advance; +} stbtt_kerningentry; + +STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info); +STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length); +// Retrieves a complete list of all of the kerning pairs provided by the font +// stbtt_GetKerningTable never writes more than table_length entries and returns how many entries it did write. +// The table will be sorted by (a.glyph1 == b.glyph1)?(a.glyph2 < b.glyph2):(a.glyph1 < b.glyph1) + +////////////////////////////////////////////////////////////////////////////// +// +// GLYPH SHAPES (you probably don't need these, but they have to go before +// the bitmaps for C declaration-order reasons) +// + +#ifndef STBTT_vmove // you can predefine these to use different values (but why?) + enum { + STBTT_vmove=1, + STBTT_vline, + STBTT_vcurve, + STBTT_vcubic + }; +#endif + +#ifndef stbtt_vertex // you can predefine this to use different values + // (we share this with other code at RAD) + #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file + typedef struct + { + stbtt_vertex_type x,y,cx,cy,cx1,cy1; + unsigned char type,padding; + } stbtt_vertex; +#endif + +STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index); +// returns non-zero if nothing is drawn for this glyph + +STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices); +STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices); +// returns # of vertices and fills *vertices with the pointer to them +// these are expressed in "unscaled" coordinates +// +// The shape is a series of contours. Each one starts with +// a STBTT_moveto, then consists of a series of mixed +// STBTT_lineto and STBTT_curveto segments. A lineto +// draws a line from previous endpoint to its x,y; a curveto +// draws a quadratic bezier from previous endpoint to +// its x,y, using cx,cy as the bezier control point. + +STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); +// frees the data allocated above + +STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg); +STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg); +// fills svg with the character's SVG data. +// returns data size or 0 if SVG not found. + +////////////////////////////////////////////////////////////////////////////// +// +// BITMAP RENDERING +// + +STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata); +// frees the bitmap allocated below + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +// allocates a large-enough single-channel 8bpp bitmap and renders the +// specified character/glyph at the specified scale into it, with +// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque). +// *width & *height are filled out with the width & height of the bitmap, +// which is stored left-to-right, top-to-bottom. +// +// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +// the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel +// shift for the character + +STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint); +// the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap +// in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap +// is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the +// width and height and positioning info for it first. + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint); +// same as stbtt_MakeCodepointBitmap, but you can specify a subpixel +// shift for the character + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint); +// same as stbtt_MakeCodepointBitmapSubpixel, but prefiltering +// is performed (see stbtt_PackSetOversampling) + +STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +// get the bbox of the bitmap centered around the glyph origin; so the +// bitmap width is ix1-ix0, height is iy1-iy0, and location to place +// the bitmap top left is (leftSideBearing*scale,iy0). +// (Note that the bitmap uses y-increases-down, but the shape uses +// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.) + +STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); +// same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel +// shift for the character + +// the following functions are equivalent to the above functions, but operate +// on glyph indices instead of Unicode codepoints (for efficiency) +STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph); +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph); +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int glyph); +STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); + + +// @TODO: don't expose this structure +typedef struct +{ + int w,h,stride; + unsigned char *pixels; +} stbtt__bitmap; + +// rasterize a shape with quadratic beziers into a bitmap +STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, // 1-channel bitmap to draw into + float flatness_in_pixels, // allowable error of curve in pixels + stbtt_vertex *vertices, // array of vertices defining shape + int num_verts, // number of vertices in above array + float scale_x, float scale_y, // scale applied to input vertices + float shift_x, float shift_y, // translation applied to input vertices + int x_off, int y_off, // another translation applied to input + int invert, // if non-zero, vertically flip shape + void *userdata); // context for to STBTT_MALLOC + +////////////////////////////////////////////////////////////////////////////// +// +// Signed Distance Function (or Field) rendering + +STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata); +// frees the SDF bitmap allocated below + +STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); +// These functions compute a discretized SDF field for a single character, suitable for storing +// in a single-channel texture, sampling with bilinear filtering, and testing against +// larger than some threshold to produce scalable fonts. +// info -- the font +// scale -- controls the size of the resulting SDF bitmap, same as it would be creating a regular bitmap +// glyph/codepoint -- the character to generate the SDF for +// padding -- extra "pixels" around the character which are filled with the distance to the character (not 0), +// which allows effects like bit outlines +// onedge_value -- value 0-255 to test the SDF against to reconstruct the character (i.e. the isocontour of the character) +// pixel_dist_scale -- what value the SDF should increase by when moving one SDF "pixel" away from the edge (on the 0..255 scale) +// if positive, > onedge_value is inside; if negative, < onedge_value is inside +// width,height -- output height & width of the SDF bitmap (including padding) +// xoff,yoff -- output origin of the character +// return value -- a 2D array of bytes 0..255, width*height in size +// +// pixel_dist_scale & onedge_value are a scale & bias that allows you to make +// optimal use of the limited 0..255 for your application, trading off precision +// and special effects. SDF values outside the range 0..255 are clamped to 0..255. +// +// Example: +// scale = stbtt_ScaleForPixelHeight(22) +// padding = 5 +// onedge_value = 180 +// pixel_dist_scale = 180/5.0 = 36.0 +// +// This will create an SDF bitmap in which the character is about 22 pixels +// high but the whole bitmap is about 22+5+5=32 pixels high. To produce a filled +// shape, sample the SDF at each pixel and fill the pixel if the SDF value +// is greater than or equal to 180/255. (You'll actually want to antialias, +// which is beyond the scope of this example.) Additionally, you can compute +// offset outlines (e.g. to stroke the character border inside & outside, +// or only outside). For example, to fill outside the character up to 3 SDF +// pixels, you would compare against (180-36.0*3)/255 = 72/255. The above +// choice of variables maps a range from 5 pixels outside the shape to +// 2 pixels inside the shape to 0..255; this is intended primarily for apply +// outside effects only (the interior range is needed to allow proper +// antialiasing of the font at *smaller* sizes) +// +// The function computes the SDF analytically at each SDF pixel, not by e.g. +// building a higher-res bitmap and approximating it. In theory the quality +// should be as high as possible for an SDF of this size & representation, but +// unclear if this is true in practice (perhaps building a higher-res bitmap +// and computing from that can allow drop-out prevention). +// +// The algorithm has not been optimized at all, so expect it to be slow +// if computing lots of characters or very large sizes. + + + +////////////////////////////////////////////////////////////////////////////// +// +// Finding the right font... +// +// You should really just solve this offline, keep your own tables +// of what font is what, and don't try to get it out of the .ttf file. +// That's because getting it out of the .ttf file is really hard, because +// the names in the file can appear in many possible encodings, in many +// possible languages, and e.g. if you need a case-insensitive comparison, +// the details of that depend on the encoding & language in a complex way +// (actually underspecified in truetype, but also gigantic). +// +// But you can use the provided functions in two possible ways: +// stbtt_FindMatchingFont() will use *case-sensitive* comparisons on +// unicode-encoded names to try to find the font you want; +// you can run this before calling stbtt_InitFont() +// +// stbtt_GetFontNameString() lets you get any of the various strings +// from the file yourself and do your own comparisons on them. +// You have to have called stbtt_InitFont() first. + + +STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags); +// returns the offset (not index) of the font that matches, or -1 if none +// if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold". +// if you use any other flag, use a font name like "Arial"; this checks +// the 'macStyle' header field; i don't know if fonts set this consistently +#define STBTT_MACSTYLE_DONTCARE 0 +#define STBTT_MACSTYLE_BOLD 1 +#define STBTT_MACSTYLE_ITALIC 2 +#define STBTT_MACSTYLE_UNDERSCORE 4 +#define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0 + +STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2); +// returns 1/0 whether the first string interpreted as utf8 is identical to +// the second string interpreted as big-endian utf16... useful for strings from next func + +STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID); +// returns the string (which may be big-endian double byte, e.g. for unicode) +// and puts the length in bytes in *length. +// +// some of the values for the IDs are below; for more see the truetype spec: +// http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html +// http://www.microsoft.com/typography/otspec/name.htm + +enum { // platformID + STBTT_PLATFORM_ID_UNICODE =0, + STBTT_PLATFORM_ID_MAC =1, + STBTT_PLATFORM_ID_ISO =2, + STBTT_PLATFORM_ID_MICROSOFT =3 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_UNICODE + STBTT_UNICODE_EID_UNICODE_1_0 =0, + STBTT_UNICODE_EID_UNICODE_1_1 =1, + STBTT_UNICODE_EID_ISO_10646 =2, + STBTT_UNICODE_EID_UNICODE_2_0_BMP=3, + STBTT_UNICODE_EID_UNICODE_2_0_FULL=4 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT + STBTT_MS_EID_SYMBOL =0, + STBTT_MS_EID_UNICODE_BMP =1, + STBTT_MS_EID_SHIFTJIS =2, + STBTT_MS_EID_UNICODE_FULL =10 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes + STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4, + STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5, + STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6, + STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7 +}; + +enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID... + // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs + STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410, + STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411, + STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412, + STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419, + STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409, + STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D +}; + +enum { // languageID for STBTT_PLATFORM_ID_MAC + STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11, + STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23, + STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32, + STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 , + STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 , + STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33, + STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19 +}; + +#ifdef __cplusplus +} +#endif + +#endif // __STB_INCLUDE_STB_TRUETYPE_H__ + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// IMPLEMENTATION +//// +//// + +#ifdef STB_TRUETYPE_IMPLEMENTATION + +#ifndef STBTT_MAX_OVERSAMPLE +#define STBTT_MAX_OVERSAMPLE 8 +#endif + +#if STBTT_MAX_OVERSAMPLE > 255 +#error "STBTT_MAX_OVERSAMPLE cannot be > 255" +#endif + +typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1]; + +#ifndef STBTT_RASTERIZER_VERSION +#define STBTT_RASTERIZER_VERSION 2 +#endif + +#ifdef _MSC_VER +#define STBTT__NOTUSED(v) (void)(v) +#else +#define STBTT__NOTUSED(v) (void)sizeof(v) +#endif + +////////////////////////////////////////////////////////////////////////// +// +// stbtt__buf helpers to parse data from file +// + +static stbtt_uint8 stbtt__buf_get8(stbtt__buf *b) +{ + if (b->cursor >= b->size) + return 0; + return b->data[b->cursor++]; +} + +static stbtt_uint8 stbtt__buf_peek8(stbtt__buf *b) +{ + if (b->cursor >= b->size) + return 0; + return b->data[b->cursor]; +} + +static void stbtt__buf_seek(stbtt__buf *b, int o) +{ + STBTT_assert(!(o > b->size || o < 0)); + b->cursor = (o > b->size || o < 0) ? b->size : o; +} + +static void stbtt__buf_skip(stbtt__buf *b, int o) +{ + stbtt__buf_seek(b, b->cursor + o); +} + +static stbtt_uint32 stbtt__buf_get(stbtt__buf *b, int n) +{ + stbtt_uint32 v = 0; + int i; + STBTT_assert(n >= 1 && n <= 4); + for (i = 0; i < n; i++) + v = (v << 8) | stbtt__buf_get8(b); + return v; +} + +static stbtt__buf stbtt__new_buf(const void *p, size_t size) +{ + stbtt__buf r; + STBTT_assert(size < 0x40000000); + r.data = (stbtt_uint8*) p; + r.size = (int) size; + r.cursor = 0; + return r; +} + +#define stbtt__buf_get16(b) stbtt__buf_get((b), 2) +#define stbtt__buf_get32(b) stbtt__buf_get((b), 4) + +static stbtt__buf stbtt__buf_range(const stbtt__buf *b, int o, int s) +{ + stbtt__buf r = stbtt__new_buf(NULL, 0); + if (o < 0 || s < 0 || o > b->size || s > b->size - o) return r; + r.data = b->data + o; + r.size = s; + return r; +} + +static stbtt__buf stbtt__cff_get_index(stbtt__buf *b) +{ + int count, start, offsize; + start = b->cursor; + count = stbtt__buf_get16(b); + if (count) { + offsize = stbtt__buf_get8(b); + STBTT_assert(offsize >= 1 && offsize <= 4); + stbtt__buf_skip(b, offsize * count); + stbtt__buf_skip(b, stbtt__buf_get(b, offsize) - 1); + } + return stbtt__buf_range(b, start, b->cursor - start); +} + +static stbtt_uint32 stbtt__cff_int(stbtt__buf *b) +{ + int b0 = stbtt__buf_get8(b); + if (b0 >= 32 && b0 <= 246) return b0 - 139; + else if (b0 >= 247 && b0 <= 250) return (b0 - 247)*256 + stbtt__buf_get8(b) + 108; + else if (b0 >= 251 && b0 <= 254) return -(b0 - 251)*256 - stbtt__buf_get8(b) - 108; + else if (b0 == 28) return stbtt__buf_get16(b); + else if (b0 == 29) return stbtt__buf_get32(b); + STBTT_assert(0); + return 0; +} + +static void stbtt__cff_skip_operand(stbtt__buf *b) { + int v, b0 = stbtt__buf_peek8(b); + STBTT_assert(b0 >= 28); + if (b0 == 30) { + stbtt__buf_skip(b, 1); + while (b->cursor < b->size) { + v = stbtt__buf_get8(b); + if ((v & 0xF) == 0xF || (v >> 4) == 0xF) + break; + } + } else { + stbtt__cff_int(b); + } +} + +static stbtt__buf stbtt__dict_get(stbtt__buf *b, int key) +{ + stbtt__buf_seek(b, 0); + while (b->cursor < b->size) { + int start = b->cursor, end, op; + while (stbtt__buf_peek8(b) >= 28) + stbtt__cff_skip_operand(b); + end = b->cursor; + op = stbtt__buf_get8(b); + if (op == 12) op = stbtt__buf_get8(b) | 0x100; + if (op == key) return stbtt__buf_range(b, start, end-start); + } + return stbtt__buf_range(b, 0, 0); +} + +static void stbtt__dict_get_ints(stbtt__buf *b, int key, int outcount, stbtt_uint32 *out) +{ + int i; + stbtt__buf operands = stbtt__dict_get(b, key); + for (i = 0; i < outcount && operands.cursor < operands.size; i++) + out[i] = stbtt__cff_int(&operands); +} + +static int stbtt__cff_index_count(stbtt__buf *b) +{ + stbtt__buf_seek(b, 0); + return stbtt__buf_get16(b); +} + +static stbtt__buf stbtt__cff_index_get(stbtt__buf b, int i) +{ + int count, offsize, start, end; + stbtt__buf_seek(&b, 0); + count = stbtt__buf_get16(&b); + offsize = stbtt__buf_get8(&b); + STBTT_assert(i >= 0 && i < count); + STBTT_assert(offsize >= 1 && offsize <= 4); + stbtt__buf_skip(&b, i*offsize); + start = stbtt__buf_get(&b, offsize); + end = stbtt__buf_get(&b, offsize); + return stbtt__buf_range(&b, 2+(count+1)*offsize+start, end - start); +} + +////////////////////////////////////////////////////////////////////////// +// +// accessors to parse data from file +// + +// on platforms that don't allow misaligned reads, if we want to allow +// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE + +#define ttBYTE(p) (* (stbtt_uint8 *) (p)) +#define ttCHAR(p) (* (stbtt_int8 *) (p)) +#define ttFixed(p) ttLONG(p) + +static stbtt_uint16 ttUSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; } +static stbtt_int16 ttSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; } +static stbtt_uint32 ttULONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } +static stbtt_int32 ttLONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } + +#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) +#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3]) + +static int stbtt__isfont(stbtt_uint8 *font) +{ + // check the version number + if (stbtt_tag4(font, '1',0,0,0)) return 1; // TrueType 1 + if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this! + if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF + if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0 + if (stbtt_tag(font, "true")) return 1; // Apple specification for TrueType fonts + return 0; +} + +// @OPTIMIZE: binary search +static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag) +{ + stbtt_int32 num_tables = ttUSHORT(data+fontstart+4); + stbtt_uint32 tabledir = fontstart + 12; + stbtt_int32 i; + for (i=0; i < num_tables; ++i) { + stbtt_uint32 loc = tabledir + 16*i; + if (stbtt_tag(data+loc+0, tag)) + return ttULONG(data+loc+8); + } + return 0; +} + +static int stbtt_GetFontOffsetForIndex_internal(unsigned char *font_collection, int index) +{ + // if it's just a font, there's only one valid index + if (stbtt__isfont(font_collection)) + return index == 0 ? 0 : -1; + + // check if it's a TTC + if (stbtt_tag(font_collection, "ttcf")) { + // version 1? + if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { + stbtt_int32 n = ttLONG(font_collection+8); + if (index >= n) + return -1; + return ttULONG(font_collection+12+index*4); + } + } + return -1; +} + +static int stbtt_GetNumberOfFonts_internal(unsigned char *font_collection) +{ + // if it's just a font, there's only one valid font + if (stbtt__isfont(font_collection)) + return 1; + + // check if it's a TTC + if (stbtt_tag(font_collection, "ttcf")) { + // version 1? + if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { + return ttLONG(font_collection+8); + } + } + return 0; +} + +static stbtt__buf stbtt__get_subrs(stbtt__buf cff, stbtt__buf fontdict) +{ + stbtt_uint32 subrsoff = 0, private_loc[2] = { 0, 0 }; + stbtt__buf pdict; + stbtt__dict_get_ints(&fontdict, 18, 2, private_loc); + if (!private_loc[1] || !private_loc[0]) return stbtt__new_buf(NULL, 0); + pdict = stbtt__buf_range(&cff, private_loc[1], private_loc[0]); + stbtt__dict_get_ints(&pdict, 19, 1, &subrsoff); + if (!subrsoff) return stbtt__new_buf(NULL, 0); + stbtt__buf_seek(&cff, private_loc[1]+subrsoff); + return stbtt__cff_get_index(&cff); +} + +// since most people won't use this, find this table the first time it's needed +static int stbtt__get_svg(stbtt_fontinfo *info) +{ + stbtt_uint32 t; + if (info->svg < 0) { + t = stbtt__find_table(info->data, info->fontstart, "SVG "); + if (t) { + stbtt_uint32 offset = ttULONG(info->data + t + 2); + info->svg = t + offset; + } else { + info->svg = 0; + } + } + return info->svg; +} + +static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, int fontstart) +{ + stbtt_uint32 cmap, t; + stbtt_int32 i,numTables; + + info->data = data; + info->fontstart = fontstart; + info->cff = stbtt__new_buf(NULL, 0); + + cmap = stbtt__find_table(data, fontstart, "cmap"); // required + info->loca = stbtt__find_table(data, fontstart, "loca"); // required + info->head = stbtt__find_table(data, fontstart, "head"); // required + info->glyf = stbtt__find_table(data, fontstart, "glyf"); // required + info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required + info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required + info->kern = stbtt__find_table(data, fontstart, "kern"); // not required + info->gpos = stbtt__find_table(data, fontstart, "GPOS"); // not required + + if (!cmap || !info->head || !info->hhea || !info->hmtx) + return 0; + if (info->glyf) { + // required for truetype + if (!info->loca) return 0; + } else { + // initialization for CFF / Type2 fonts (OTF) + stbtt__buf b, topdict, topdictidx; + stbtt_uint32 cstype = 2, charstrings = 0, fdarrayoff = 0, fdselectoff = 0; + stbtt_uint32 cff; + + cff = stbtt__find_table(data, fontstart, "CFF "); + if (!cff) return 0; + + info->fontdicts = stbtt__new_buf(NULL, 0); + info->fdselect = stbtt__new_buf(NULL, 0); + + // @TODO this should use size from table (not 512MB) + info->cff = stbtt__new_buf(data+cff, 512*1024*1024); + b = info->cff; + + // read the header + stbtt__buf_skip(&b, 2); + stbtt__buf_seek(&b, stbtt__buf_get8(&b)); // hdrsize + + // @TODO the name INDEX could list multiple fonts, + // but we just use the first one. + stbtt__cff_get_index(&b); // name INDEX + topdictidx = stbtt__cff_get_index(&b); + topdict = stbtt__cff_index_get(topdictidx, 0); + stbtt__cff_get_index(&b); // string INDEX + info->gsubrs = stbtt__cff_get_index(&b); + + stbtt__dict_get_ints(&topdict, 17, 1, &charstrings); + stbtt__dict_get_ints(&topdict, 0x100 | 6, 1, &cstype); + stbtt__dict_get_ints(&topdict, 0x100 | 36, 1, &fdarrayoff); + stbtt__dict_get_ints(&topdict, 0x100 | 37, 1, &fdselectoff); + info->subrs = stbtt__get_subrs(b, topdict); + + // we only support Type 2 charstrings + if (cstype != 2) return 0; + if (charstrings == 0) return 0; + + if (fdarrayoff) { + // looks like a CID font + if (!fdselectoff) return 0; + stbtt__buf_seek(&b, fdarrayoff); + info->fontdicts = stbtt__cff_get_index(&b); + info->fdselect = stbtt__buf_range(&b, fdselectoff, b.size-fdselectoff); + } + + stbtt__buf_seek(&b, charstrings); + info->charstrings = stbtt__cff_get_index(&b); + } + + t = stbtt__find_table(data, fontstart, "maxp"); + if (t) + info->numGlyphs = ttUSHORT(data+t+4); + else + info->numGlyphs = 0xffff; + + info->svg = -1; + + // find a cmap encoding table we understand *now* to avoid searching + // later. (todo: could make this installable) + // the same regardless of glyph. + numTables = ttUSHORT(data + cmap + 2); + info->index_map = 0; + for (i=0; i < numTables; ++i) { + stbtt_uint32 encoding_record = cmap + 4 + 8 * i; + // find an encoding we understand: + switch(ttUSHORT(data+encoding_record)) { + case STBTT_PLATFORM_ID_MICROSOFT: + switch (ttUSHORT(data+encoding_record+2)) { + case STBTT_MS_EID_UNICODE_BMP: + case STBTT_MS_EID_UNICODE_FULL: + // MS/Unicode + info->index_map = cmap + ttULONG(data+encoding_record+4); + break; + } + break; + case STBTT_PLATFORM_ID_UNICODE: + // Mac/iOS has these + // all the encodingIDs are unicode, so we don't bother to check it + info->index_map = cmap + ttULONG(data+encoding_record+4); + break; + } + } + if (info->index_map == 0) + return 0; + + info->indexToLocFormat = ttUSHORT(data+info->head + 50); + return 1; +} + +STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) +{ + stbtt_uint8 *data = info->data; + stbtt_uint32 index_map = info->index_map; + + stbtt_uint16 format = ttUSHORT(data + index_map + 0); + if (format == 0) { // apple byte encoding + stbtt_int32 bytes = ttUSHORT(data + index_map + 2); + if (unicode_codepoint < bytes-6) + return ttBYTE(data + index_map + 6 + unicode_codepoint); + return 0; + } else if (format == 6) { + stbtt_uint32 first = ttUSHORT(data + index_map + 6); + stbtt_uint32 count = ttUSHORT(data + index_map + 8); + if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count) + return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2); + return 0; + } else if (format == 2) { + STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean + return 0; + } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges + stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1; + stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1; + stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10); + stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1; + + // do a binary search of the segments + stbtt_uint32 endCount = index_map + 14; + stbtt_uint32 search = endCount; + + if (unicode_codepoint > 0xffff) + return 0; + + // they lie from endCount .. endCount + segCount + // but searchRange is the nearest power of two, so... + if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2)) + search += rangeShift*2; + + // now decrement to bias correctly to find smallest + search -= 2; + while (entrySelector) { + stbtt_uint16 end; + searchRange >>= 1; + end = ttUSHORT(data + search + searchRange*2); + if (unicode_codepoint > end) + search += searchRange*2; + --entrySelector; + } + search += 2; + + { + stbtt_uint16 offset, start; + stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1); + + STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item)); + start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); + if (unicode_codepoint < start) + return 0; + + offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); + if (offset == 0) + return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item)); + + return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); + } + } else if (format == 12 || format == 13) { + stbtt_uint32 ngroups = ttULONG(data+index_map+12); + stbtt_int32 low,high; + low = 0; high = (stbtt_int32)ngroups; + // Binary search the right group. + while (low < high) { + stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high + stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12); + stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4); + if ((stbtt_uint32) unicode_codepoint < start_char) + high = mid; + else if ((stbtt_uint32) unicode_codepoint > end_char) + low = mid+1; + else { + stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8); + if (format == 12) + return start_glyph + unicode_codepoint-start_char; + else // format == 13 + return start_glyph; + } + } + return 0; // not found + } + // @TODO + STBTT_assert(0); + return 0; +} + +STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices) +{ + return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices); +} + +static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy) +{ + v->type = type; + v->x = (stbtt_int16) x; + v->y = (stbtt_int16) y; + v->cx = (stbtt_int16) cx; + v->cy = (stbtt_int16) cy; +} + +static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) +{ + int g1,g2; + + STBTT_assert(!info->cff.size); + + if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range + if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format + + if (info->indexToLocFormat == 0) { + g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2; + g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2; + } else { + g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4); + g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4); + } + + return g1==g2 ? -1 : g1; // if length is 0, return -1 +} + +static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); + +STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) +{ + if (info->cff.size) { + stbtt__GetGlyphInfoT2(info, glyph_index, x0, y0, x1, y1); + } else { + int g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 0; + + if (x0) *x0 = ttSHORT(info->data + g + 2); + if (y0) *y0 = ttSHORT(info->data + g + 4); + if (x1) *x1 = ttSHORT(info->data + g + 6); + if (y1) *y1 = ttSHORT(info->data + g + 8); + } + return 1; +} + +STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1) +{ + return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1); +} + +STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index) +{ + stbtt_int16 numberOfContours; + int g; + if (info->cff.size) + return stbtt__GetGlyphInfoT2(info, glyph_index, NULL, NULL, NULL, NULL) == 0; + g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 1; + numberOfContours = ttSHORT(info->data + g); + return numberOfContours == 0; +} + +static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off, + stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy) +{ + if (start_off) { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy); + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy); + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); + } + return num_vertices; +} + +static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + stbtt_int16 numberOfContours; + stbtt_uint8 *endPtsOfContours; + stbtt_uint8 *data = info->data; + stbtt_vertex *vertices=0; + int num_vertices=0; + int g = stbtt__GetGlyfOffset(info, glyph_index); + + *pvertices = NULL; + + if (g < 0) return 0; + + numberOfContours = ttSHORT(data + g); + + if (numberOfContours > 0) { + stbtt_uint8 flags=0,flagcount; + stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0; + stbtt_int32 x,y,cx,cy,sx,sy, scx,scy; + stbtt_uint8 *points; + endPtsOfContours = (data + g + 10); + ins = ttUSHORT(data + g + 10 + numberOfContours * 2); + points = data + g + 10 + numberOfContours * 2 + 2 + ins; + + n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2); + + m = n + 2*numberOfContours; // a loose bound on how many vertices we might need + vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata); + if (vertices == 0) + return 0; + + next_move = 0; + flagcount=0; + + // in first pass, we load uninterpreted data into the allocated array + // above, shifted to the end of the array so we won't overwrite it when + // we create our final data starting from the front + + off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated + + // first load flags + + for (i=0; i < n; ++i) { + if (flagcount == 0) { + flags = *points++; + if (flags & 8) + flagcount = *points++; + } else + --flagcount; + vertices[off+i].type = flags; + } + + // now load x coordinates + x=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 2) { + stbtt_int16 dx = *points++; + x += (flags & 16) ? dx : -dx; // ??? + } else { + if (!(flags & 16)) { + x = x + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].x = (stbtt_int16) x; + } + + // now load y coordinates + y=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 4) { + stbtt_int16 dy = *points++; + y += (flags & 32) ? dy : -dy; // ??? + } else { + if (!(flags & 32)) { + y = y + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].y = (stbtt_int16) y; + } + + // now convert them to our format + num_vertices=0; + sx = sy = cx = cy = scx = scy = 0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + x = (stbtt_int16) vertices[off+i].x; + y = (stbtt_int16) vertices[off+i].y; + + if (next_move == i) { + if (i != 0) + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + + // now start the new one + start_off = !(flags & 1); + if (start_off) { + // if we start off with an off-curve point, then when we need to find a point on the curve + // where we can start, and we need to save some state for when we wraparound. + scx = x; + scy = y; + if (!(vertices[off+i+1].type & 1)) { + // next point is also a curve point, so interpolate an on-point curve + sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1; + sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1; + } else { + // otherwise just use the next point as our start point + sx = (stbtt_int32) vertices[off+i+1].x; + sy = (stbtt_int32) vertices[off+i+1].y; + ++i; // we're using point i+1 as the starting point, so skip it + } + } else { + sx = x; + sy = y; + } + stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0); + was_off = 0; + next_move = 1 + ttUSHORT(endPtsOfContours+j*2); + ++j; + } else { + if (!(flags & 1)) { // if it's a curve + if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); + cx = x; + cy = y; + was_off = 1; + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0); + was_off = 0; + } + } + } + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + } else if (numberOfContours < 0) { + // Compound shapes. + int more = 1; + stbtt_uint8 *comp = data + g + 10; + num_vertices = 0; + vertices = 0; + while (more) { + stbtt_uint16 flags, gidx; + int comp_num_verts = 0, i; + stbtt_vertex *comp_verts = 0, *tmp = 0; + float mtx[6] = {1,0,0,1,0,0}, m, n; + + flags = ttSHORT(comp); comp+=2; + gidx = ttSHORT(comp); comp+=2; + + if (flags & 2) { // XY values + if (flags & 1) { // shorts + mtx[4] = ttSHORT(comp); comp+=2; + mtx[5] = ttSHORT(comp); comp+=2; + } else { + mtx[4] = ttCHAR(comp); comp+=1; + mtx[5] = ttCHAR(comp); comp+=1; + } + } + else { + // @TODO handle matching point + STBTT_assert(0); + } + if (flags & (1<<3)) { // WE_HAVE_A_SCALE + mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } + + // Find transformation scales. + m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); + n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); + + // Get indexed glyph. + comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts); + if (comp_num_verts > 0) { + // Transform vertices. + for (i = 0; i < comp_num_verts; ++i) { + stbtt_vertex* v = &comp_verts[i]; + stbtt_vertex_type x,y; + x=v->x; y=v->y; + v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + x=v->cx; y=v->cy; + v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + } + // Append vertices. + tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata); + if (!tmp) { + if (vertices) STBTT_free(vertices, info->userdata); + if (comp_verts) STBTT_free(comp_verts, info->userdata); + return 0; + } + if (num_vertices > 0) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); + STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex)); + if (vertices) STBTT_free(vertices, info->userdata); + vertices = tmp; + STBTT_free(comp_verts, info->userdata); + num_vertices += comp_num_verts; + } + // More components ? + more = flags & (1<<5); + } + } else { + // numberOfCounters == 0, do nothing + } + + *pvertices = vertices; + return num_vertices; +} + +typedef struct +{ + int bounds; + int started; + float first_x, first_y; + float x, y; + stbtt_int32 min_x, max_x, min_y, max_y; + + stbtt_vertex *pvertices; + int num_vertices; +} stbtt__csctx; + +#define STBTT__CSCTX_INIT(bounds) {bounds,0, 0,0, 0,0, 0,0,0,0, NULL, 0} + +static void stbtt__track_vertex(stbtt__csctx *c, stbtt_int32 x, stbtt_int32 y) +{ + if (x > c->max_x || !c->started) c->max_x = x; + if (y > c->max_y || !c->started) c->max_y = y; + if (x < c->min_x || !c->started) c->min_x = x; + if (y < c->min_y || !c->started) c->min_y = y; + c->started = 1; +} + +static void stbtt__csctx_v(stbtt__csctx *c, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy, stbtt_int32 cx1, stbtt_int32 cy1) +{ + if (c->bounds) { + stbtt__track_vertex(c, x, y); + if (type == STBTT_vcubic) { + stbtt__track_vertex(c, cx, cy); + stbtt__track_vertex(c, cx1, cy1); + } + } else { + stbtt_setvertex(&c->pvertices[c->num_vertices], type, x, y, cx, cy); + c->pvertices[c->num_vertices].cx1 = (stbtt_int16) cx1; + c->pvertices[c->num_vertices].cy1 = (stbtt_int16) cy1; + } + c->num_vertices++; +} + +static void stbtt__csctx_close_shape(stbtt__csctx *ctx) +{ + if (ctx->first_x != ctx->x || ctx->first_y != ctx->y) + stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->first_x, (int)ctx->first_y, 0, 0, 0, 0); +} + +static void stbtt__csctx_rmove_to(stbtt__csctx *ctx, float dx, float dy) +{ + stbtt__csctx_close_shape(ctx); + ctx->first_x = ctx->x = ctx->x + dx; + ctx->first_y = ctx->y = ctx->y + dy; + stbtt__csctx_v(ctx, STBTT_vmove, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); +} + +static void stbtt__csctx_rline_to(stbtt__csctx *ctx, float dx, float dy) +{ + ctx->x += dx; + ctx->y += dy; + stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); +} + +static void stbtt__csctx_rccurve_to(stbtt__csctx *ctx, float dx1, float dy1, float dx2, float dy2, float dx3, float dy3) +{ + float cx1 = ctx->x + dx1; + float cy1 = ctx->y + dy1; + float cx2 = cx1 + dx2; + float cy2 = cy1 + dy2; + ctx->x = cx2 + dx3; + ctx->y = cy2 + dy3; + stbtt__csctx_v(ctx, STBTT_vcubic, (int)ctx->x, (int)ctx->y, (int)cx1, (int)cy1, (int)cx2, (int)cy2); +} + +static stbtt__buf stbtt__get_subr(stbtt__buf idx, int n) +{ + int count = stbtt__cff_index_count(&idx); + int bias = 107; + if (count >= 33900) + bias = 32768; + else if (count >= 1240) + bias = 1131; + n += bias; + if (n < 0 || n >= count) + return stbtt__new_buf(NULL, 0); + return stbtt__cff_index_get(idx, n); +} + +static stbtt__buf stbtt__cid_get_glyph_subrs(const stbtt_fontinfo *info, int glyph_index) +{ + stbtt__buf fdselect = info->fdselect; + int nranges, start, end, v, fmt, fdselector = -1, i; + + stbtt__buf_seek(&fdselect, 0); + fmt = stbtt__buf_get8(&fdselect); + if (fmt == 0) { + // untested + stbtt__buf_skip(&fdselect, glyph_index); + fdselector = stbtt__buf_get8(&fdselect); + } else if (fmt == 3) { + nranges = stbtt__buf_get16(&fdselect); + start = stbtt__buf_get16(&fdselect); + for (i = 0; i < nranges; i++) { + v = stbtt__buf_get8(&fdselect); + end = stbtt__buf_get16(&fdselect); + if (glyph_index >= start && glyph_index < end) { + fdselector = v; + break; + } + start = end; + } + } + if (fdselector == -1) stbtt__new_buf(NULL, 0); + return stbtt__get_subrs(info->cff, stbtt__cff_index_get(info->fontdicts, fdselector)); +} + +static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, stbtt__csctx *c) +{ + int in_header = 1, maskbits = 0, subr_stack_height = 0, sp = 0, v, i, b0; + int has_subrs = 0, clear_stack; + float s[48]; + stbtt__buf subr_stack[10], subrs = info->subrs, b; + float f; + +#define STBTT__CSERR(s) (0) + + // this currently ignores the initial width value, which isn't needed if we have hmtx + b = stbtt__cff_index_get(info->charstrings, glyph_index); + while (b.cursor < b.size) { + i = 0; + clear_stack = 1; + b0 = stbtt__buf_get8(&b); + switch (b0) { + // @TODO implement hinting + case 0x13: // hintmask + case 0x14: // cntrmask + if (in_header) + maskbits += (sp / 2); // implicit "vstem" + in_header = 0; + stbtt__buf_skip(&b, (maskbits + 7) / 8); + break; + + case 0x01: // hstem + case 0x03: // vstem + case 0x12: // hstemhm + case 0x17: // vstemhm + maskbits += (sp / 2); + break; + + case 0x15: // rmoveto + in_header = 0; + if (sp < 2) return STBTT__CSERR("rmoveto stack"); + stbtt__csctx_rmove_to(c, s[sp-2], s[sp-1]); + break; + case 0x04: // vmoveto + in_header = 0; + if (sp < 1) return STBTT__CSERR("vmoveto stack"); + stbtt__csctx_rmove_to(c, 0, s[sp-1]); + break; + case 0x16: // hmoveto + in_header = 0; + if (sp < 1) return STBTT__CSERR("hmoveto stack"); + stbtt__csctx_rmove_to(c, s[sp-1], 0); + break; + + case 0x05: // rlineto + if (sp < 2) return STBTT__CSERR("rlineto stack"); + for (; i + 1 < sp; i += 2) + stbtt__csctx_rline_to(c, s[i], s[i+1]); + break; + + // hlineto/vlineto and vhcurveto/hvcurveto alternate horizontal and vertical + // starting from a different place. + + case 0x07: // vlineto + if (sp < 1) return STBTT__CSERR("vlineto stack"); + goto vlineto; + case 0x06: // hlineto + if (sp < 1) return STBTT__CSERR("hlineto stack"); + for (;;) { + if (i >= sp) break; + stbtt__csctx_rline_to(c, s[i], 0); + i++; + vlineto: + if (i >= sp) break; + stbtt__csctx_rline_to(c, 0, s[i]); + i++; + } + break; + + case 0x1F: // hvcurveto + if (sp < 4) return STBTT__CSERR("hvcurveto stack"); + goto hvcurveto; + case 0x1E: // vhcurveto + if (sp < 4) return STBTT__CSERR("vhcurveto stack"); + for (;;) { + if (i + 3 >= sp) break; + stbtt__csctx_rccurve_to(c, 0, s[i], s[i+1], s[i+2], s[i+3], (sp - i == 5) ? s[i + 4] : 0.0f); + i += 4; + hvcurveto: + if (i + 3 >= sp) break; + stbtt__csctx_rccurve_to(c, s[i], 0, s[i+1], s[i+2], (sp - i == 5) ? s[i+4] : 0.0f, s[i+3]); + i += 4; + } + break; + + case 0x08: // rrcurveto + if (sp < 6) return STBTT__CSERR("rcurveline stack"); + for (; i + 5 < sp; i += 6) + stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + break; + + case 0x18: // rcurveline + if (sp < 8) return STBTT__CSERR("rcurveline stack"); + for (; i + 5 < sp - 2; i += 6) + stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + if (i + 1 >= sp) return STBTT__CSERR("rcurveline stack"); + stbtt__csctx_rline_to(c, s[i], s[i+1]); + break; + + case 0x19: // rlinecurve + if (sp < 8) return STBTT__CSERR("rlinecurve stack"); + for (; i + 1 < sp - 6; i += 2) + stbtt__csctx_rline_to(c, s[i], s[i+1]); + if (i + 5 >= sp) return STBTT__CSERR("rlinecurve stack"); + stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + break; + + case 0x1A: // vvcurveto + case 0x1B: // hhcurveto + if (sp < 4) return STBTT__CSERR("(vv|hh)curveto stack"); + f = 0.0; + if (sp & 1) { f = s[i]; i++; } + for (; i + 3 < sp; i += 4) { + if (b0 == 0x1B) + stbtt__csctx_rccurve_to(c, s[i], f, s[i+1], s[i+2], s[i+3], 0.0); + else + stbtt__csctx_rccurve_to(c, f, s[i], s[i+1], s[i+2], 0.0, s[i+3]); + f = 0.0; + } + break; + + case 0x0A: // callsubr + if (!has_subrs) { + if (info->fdselect.size) + subrs = stbtt__cid_get_glyph_subrs(info, glyph_index); + has_subrs = 1; + } + // fallthrough + case 0x1D: // callgsubr + if (sp < 1) return STBTT__CSERR("call(g|)subr stack"); + v = (int) s[--sp]; + if (subr_stack_height >= 10) return STBTT__CSERR("recursion limit"); + subr_stack[subr_stack_height++] = b; + b = stbtt__get_subr(b0 == 0x0A ? subrs : info->gsubrs, v); + if (b.size == 0) return STBTT__CSERR("subr not found"); + b.cursor = 0; + clear_stack = 0; + break; + + case 0x0B: // return + if (subr_stack_height <= 0) return STBTT__CSERR("return outside subr"); + b = subr_stack[--subr_stack_height]; + clear_stack = 0; + break; + + case 0x0E: // endchar + stbtt__csctx_close_shape(c); + return 1; + + case 0x0C: { // two-byte escape + float dx1, dx2, dx3, dx4, dx5, dx6, dy1, dy2, dy3, dy4, dy5, dy6; + float dx, dy; + int b1 = stbtt__buf_get8(&b); + switch (b1) { + // @TODO These "flex" implementations ignore the flex-depth and resolution, + // and always draw beziers. + case 0x22: // hflex + if (sp < 7) return STBTT__CSERR("hflex stack"); + dx1 = s[0]; + dx2 = s[1]; + dy2 = s[2]; + dx3 = s[3]; + dx4 = s[4]; + dx5 = s[5]; + dx6 = s[6]; + stbtt__csctx_rccurve_to(c, dx1, 0, dx2, dy2, dx3, 0); + stbtt__csctx_rccurve_to(c, dx4, 0, dx5, -dy2, dx6, 0); + break; + + case 0x23: // flex + if (sp < 13) return STBTT__CSERR("flex stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dy3 = s[5]; + dx4 = s[6]; + dy4 = s[7]; + dx5 = s[8]; + dy5 = s[9]; + dx6 = s[10]; + dy6 = s[11]; + //fd is s[12] + stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); + stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); + break; + + case 0x24: // hflex1 + if (sp < 9) return STBTT__CSERR("hflex1 stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dx4 = s[5]; + dx5 = s[6]; + dy5 = s[7]; + dx6 = s[8]; + stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, 0); + stbtt__csctx_rccurve_to(c, dx4, 0, dx5, dy5, dx6, -(dy1+dy2+dy5)); + break; + + case 0x25: // flex1 + if (sp < 11) return STBTT__CSERR("flex1 stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dy3 = s[5]; + dx4 = s[6]; + dy4 = s[7]; + dx5 = s[8]; + dy5 = s[9]; + dx6 = dy6 = s[10]; + dx = dx1+dx2+dx3+dx4+dx5; + dy = dy1+dy2+dy3+dy4+dy5; + if (STBTT_fabs(dx) > STBTT_fabs(dy)) + dy6 = -dy; + else + dx6 = -dx; + stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); + stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); + break; + + default: + return STBTT__CSERR("unimplemented"); + } + } break; + + default: + if (b0 != 255 && b0 != 28 && (b0 < 32 || b0 > 254)) + return STBTT__CSERR("reserved operator"); + + // push immediate + if (b0 == 255) { + f = (float)(stbtt_int32)stbtt__buf_get32(&b) / 0x10000; + } else { + stbtt__buf_skip(&b, -1); + f = (float)(stbtt_int16)stbtt__cff_int(&b); + } + if (sp >= 48) return STBTT__CSERR("push stack overflow"); + s[sp++] = f; + clear_stack = 0; + break; + } + if (clear_stack) sp = 0; + } + return STBTT__CSERR("no endchar"); + +#undef STBTT__CSERR +} + +static int stbtt__GetGlyphShapeT2(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + // runs the charstring twice, once to count and once to output (to avoid realloc) + stbtt__csctx count_ctx = STBTT__CSCTX_INIT(1); + stbtt__csctx output_ctx = STBTT__CSCTX_INIT(0); + if (stbtt__run_charstring(info, glyph_index, &count_ctx)) { + *pvertices = (stbtt_vertex*)STBTT_malloc(count_ctx.num_vertices*sizeof(stbtt_vertex), info->userdata); + output_ctx.pvertices = *pvertices; + if (stbtt__run_charstring(info, glyph_index, &output_ctx)) { + STBTT_assert(output_ctx.num_vertices == count_ctx.num_vertices); + return output_ctx.num_vertices; + } + } + *pvertices = NULL; + return 0; +} + +static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) +{ + stbtt__csctx c = STBTT__CSCTX_INIT(1); + int r = stbtt__run_charstring(info, glyph_index, &c); + if (x0) *x0 = r ? c.min_x : 0; + if (y0) *y0 = r ? c.min_y : 0; + if (x1) *x1 = r ? c.max_x : 0; + if (y1) *y1 = r ? c.max_y : 0; + return r ? c.num_vertices : 0; +} + +STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + if (!info->cff.size) + return stbtt__GetGlyphShapeTT(info, glyph_index, pvertices); + else + return stbtt__GetGlyphShapeT2(info, glyph_index, pvertices); +} + +STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) +{ + stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34); + if (glyph_index < numOfLongHorMetrics) { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2); + } else { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1)); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); + } +} + +STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info) +{ + stbtt_uint8 *data = info->data + info->kern; + + // we only look at the first table. it must be 'horizontal' and format 0. + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return 0; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return 0; + + return ttUSHORT(data+10); +} + +STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length) +{ + stbtt_uint8 *data = info->data + info->kern; + int k, length; + + // we only look at the first table. it must be 'horizontal' and format 0. + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return 0; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return 0; + + length = ttUSHORT(data+10); + if (table_length < length) + length = table_length; + + for (k = 0; k < length; k++) + { + table[k].glyph1 = ttUSHORT(data+18+(k*6)); + table[k].glyph2 = ttUSHORT(data+20+(k*6)); + table[k].advance = ttSHORT(data+22+(k*6)); + } + + return length; +} + +static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) +{ + stbtt_uint8 *data = info->data + info->kern; + stbtt_uint32 needle, straw; + int l, r, m; + + // we only look at the first table. it must be 'horizontal' and format 0. + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return 0; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return 0; + + l = 0; + r = ttUSHORT(data+10) - 1; + needle = glyph1 << 16 | glyph2; + while (l <= r) { + m = (l + r) >> 1; + straw = ttULONG(data+18+(m*6)); // note: unaligned read + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else + return ttSHORT(data+22+(m*6)); + } + return 0; +} + +static stbtt_int32 stbtt__GetCoverageIndex(stbtt_uint8 *coverageTable, int glyph) +{ + stbtt_uint16 coverageFormat = ttUSHORT(coverageTable); + switch(coverageFormat) { + case 1: { + stbtt_uint16 glyphCount = ttUSHORT(coverageTable + 2); + + // Binary search. + stbtt_int32 l=0, r=glyphCount-1, m; + int straw, needle=glyph; + while (l <= r) { + stbtt_uint8 *glyphArray = coverageTable + 4; + stbtt_uint16 glyphID; + m = (l + r) >> 1; + glyphID = ttUSHORT(glyphArray + 2 * m); + straw = glyphID; + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else { + return m; + } + } + } break; + + case 2: { + stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2); + stbtt_uint8 *rangeArray = coverageTable + 4; + + // Binary search. + stbtt_int32 l=0, r=rangeCount-1, m; + int strawStart, strawEnd, needle=glyph; + while (l <= r) { + stbtt_uint8 *rangeRecord; + m = (l + r) >> 1; + rangeRecord = rangeArray + 6 * m; + strawStart = ttUSHORT(rangeRecord); + strawEnd = ttUSHORT(rangeRecord + 2); + if (needle < strawStart) + r = m - 1; + else if (needle > strawEnd) + l = m + 1; + else { + stbtt_uint16 startCoverageIndex = ttUSHORT(rangeRecord + 4); + return startCoverageIndex + glyph - strawStart; + } + } + } break; + + default: { + // There are no other cases. + STBTT_assert(0); + } break; + } + + return -1; +} + +static stbtt_int32 stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph) +{ + stbtt_uint16 classDefFormat = ttUSHORT(classDefTable); + switch(classDefFormat) + { + case 1: { + stbtt_uint16 startGlyphID = ttUSHORT(classDefTable + 2); + stbtt_uint16 glyphCount = ttUSHORT(classDefTable + 4); + stbtt_uint8 *classDef1ValueArray = classDefTable + 6; + + if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount) + return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID)); + + classDefTable = classDef1ValueArray + 2 * glyphCount; + } break; + + case 2: { + stbtt_uint16 classRangeCount = ttUSHORT(classDefTable + 2); + stbtt_uint8 *classRangeRecords = classDefTable + 4; + + // Binary search. + stbtt_int32 l=0, r=classRangeCount-1, m; + int strawStart, strawEnd, needle=glyph; + while (l <= r) { + stbtt_uint8 *classRangeRecord; + m = (l + r) >> 1; + classRangeRecord = classRangeRecords + 6 * m; + strawStart = ttUSHORT(classRangeRecord); + strawEnd = ttUSHORT(classRangeRecord + 2); + if (needle < strawStart) + r = m - 1; + else if (needle > strawEnd) + l = m + 1; + else + return (stbtt_int32)ttUSHORT(classRangeRecord + 4); + } + + classDefTable = classRangeRecords + 6 * classRangeCount; + } break; + + default: { + // There are no other cases. + STBTT_assert(0); + } break; + } + + return -1; +} + +// Define to STBTT_assert(x) if you want to break on unimplemented formats. +#define STBTT_GPOS_TODO_assert(x) + +static stbtt_int32 stbtt__GetGlyphGPOSInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) +{ + stbtt_uint16 lookupListOffset; + stbtt_uint8 *lookupList; + stbtt_uint16 lookupCount; + stbtt_uint8 *data; + stbtt_int32 i; + + if (!info->gpos) return 0; + + data = info->data + info->gpos; + + if (ttUSHORT(data+0) != 1) return 0; // Major version 1 + if (ttUSHORT(data+2) != 0) return 0; // Minor version 0 + + lookupListOffset = ttUSHORT(data+8); + lookupList = data + lookupListOffset; + lookupCount = ttUSHORT(lookupList); + + for (i=0; i> 1; + pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m; + secondGlyph = ttUSHORT(pairValue); + straw = secondGlyph; + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else { + stbtt_int16 xAdvance = ttSHORT(pairValue + 2); + return xAdvance; + } + } + } break; + + case 2: { + stbtt_uint16 valueFormat1 = ttUSHORT(table + 4); + stbtt_uint16 valueFormat2 = ttUSHORT(table + 6); + + stbtt_uint16 classDef1Offset = ttUSHORT(table + 8); + stbtt_uint16 classDef2Offset = ttUSHORT(table + 10); + int glyph1class = stbtt__GetGlyphClass(table + classDef1Offset, glyph1); + int glyph2class = stbtt__GetGlyphClass(table + classDef2Offset, glyph2); + + stbtt_uint16 class1Count = ttUSHORT(table + 12); + stbtt_uint16 class2Count = ttUSHORT(table + 14); + STBTT_assert(glyph1class < class1Count); + STBTT_assert(glyph2class < class2Count); + + // TODO: Support more formats. + STBTT_GPOS_TODO_assert(valueFormat1 == 4); + if (valueFormat1 != 4) return 0; + STBTT_GPOS_TODO_assert(valueFormat2 == 0); + if (valueFormat2 != 0) return 0; + + if (glyph1class >= 0 && glyph1class < class1Count && glyph2class >= 0 && glyph2class < class2Count) { + stbtt_uint8 *class1Records = table + 16; + stbtt_uint8 *class2Records = class1Records + 2 * (glyph1class * class2Count); + stbtt_int16 xAdvance = ttSHORT(class2Records + 2 * glyph2class); + return xAdvance; + } + } break; + + default: { + // There are no other cases. + STBTT_assert(0); + break; + }; + } + } + break; + }; + + default: + // TODO: Implement other stuff. + break; + } + } + + return 0; +} + +STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int g1, int g2) +{ + int xAdvance = 0; + + if (info->gpos) + xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2); + else if (info->kern) + xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2); + + return xAdvance; +} + +STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2) +{ + if (!info->kern && !info->gpos) // if no kerning table, don't waste time looking up both codepoint->glyphs + return 0; + return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2)); +} + +STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing) +{ + stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing); +} + +STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap) +{ + if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4); + if (descent) *descent = ttSHORT(info->data+info->hhea + 6); + if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8); +} + +STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap) +{ + int tab = stbtt__find_table(info->data, info->fontstart, "OS/2"); + if (!tab) + return 0; + if (typoAscent ) *typoAscent = ttSHORT(info->data+tab + 68); + if (typoDescent) *typoDescent = ttSHORT(info->data+tab + 70); + if (typoLineGap) *typoLineGap = ttSHORT(info->data+tab + 72); + return 1; +} + +STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1) +{ + *x0 = ttSHORT(info->data + info->head + 36); + *y0 = ttSHORT(info->data + info->head + 38); + *x1 = ttSHORT(info->data + info->head + 40); + *y1 = ttSHORT(info->data + info->head + 42); +} + +STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height) +{ + int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6); + return (float) height / fheight; +} + +STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels) +{ + int unitsPerEm = ttUSHORT(info->data + info->head + 18); + return pixels / unitsPerEm; +} + +STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) +{ + STBTT_free(v, info->userdata); +} + +STBTT_DEF stbtt_uint8 *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl) +{ + int i; + stbtt_uint8 *data = info->data; + stbtt_uint8 *svg_doc_list = data + stbtt__get_svg((stbtt_fontinfo *) info); + + int numEntries = ttUSHORT(svg_doc_list); + stbtt_uint8 *svg_docs = svg_doc_list + 2; + + for(i=0; i= ttUSHORT(svg_doc)) && (gl <= ttUSHORT(svg_doc + 2))) + return svg_doc; + } + return 0; +} + +STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg) +{ + stbtt_uint8 *data = info->data; + stbtt_uint8 *svg_doc; + + if (info->svg == 0) + return 0; + + svg_doc = stbtt_FindSVGDoc(info, gl); + if (svg_doc != NULL) { + *svg = (char *) data + info->svg + ttULONG(svg_doc + 4); + return ttULONG(svg_doc + 8); + } else { + return 0; + } +} + +STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg) +{ + return stbtt_GetGlyphSVG(info, stbtt_FindGlyphIndex(info, unicode_codepoint), svg); +} + +////////////////////////////////////////////////////////////////////////////// +// +// antialiasing software rasterizer +// + +STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + int x0=0,y0=0,x1,y1; // =0 suppresses compiler warning + if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) { + // e.g. space character + if (ix0) *ix0 = 0; + if (iy0) *iy0 = 0; + if (ix1) *ix1 = 0; + if (iy1) *iy1 = 0; + } else { + // move to integral bboxes (treating pixels as little squares, what pixels get touched)? + if (ix0) *ix0 = STBTT_ifloor( x0 * scale_x + shift_x); + if (iy0) *iy0 = STBTT_ifloor(-y1 * scale_y + shift_y); + if (ix1) *ix1 = STBTT_iceil ( x1 * scale_x + shift_x); + if (iy1) *iy1 = STBTT_iceil (-y0 * scale_y + shift_y); + } +} + +STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1); +} + +STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1); +} + +STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1); +} + +////////////////////////////////////////////////////////////////////////////// +// +// Rasterizer + +typedef struct stbtt__hheap_chunk +{ + struct stbtt__hheap_chunk *next; +} stbtt__hheap_chunk; + +typedef struct stbtt__hheap +{ + struct stbtt__hheap_chunk *head; + void *first_free; + int num_remaining_in_head_chunk; +} stbtt__hheap; + +static void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata) +{ + if (hh->first_free) { + void *p = hh->first_free; + hh->first_free = * (void **) p; + return p; + } else { + if (hh->num_remaining_in_head_chunk == 0) { + int count = (size < 32 ? 2000 : size < 128 ? 800 : 100); + stbtt__hheap_chunk *c = (stbtt__hheap_chunk *) STBTT_malloc(sizeof(stbtt__hheap_chunk) + size * count, userdata); + if (c == NULL) + return NULL; + c->next = hh->head; + hh->head = c; + hh->num_remaining_in_head_chunk = count; + } + --hh->num_remaining_in_head_chunk; + return (char *) (hh->head) + sizeof(stbtt__hheap_chunk) + size * hh->num_remaining_in_head_chunk; + } +} + +static void stbtt__hheap_free(stbtt__hheap *hh, void *p) +{ + *(void **) p = hh->first_free; + hh->first_free = p; +} + +static void stbtt__hheap_cleanup(stbtt__hheap *hh, void *userdata) +{ + stbtt__hheap_chunk *c = hh->head; + while (c) { + stbtt__hheap_chunk *n = c->next; + STBTT_free(c, userdata); + c = n; + } +} + +typedef struct stbtt__edge { + float x0,y0, x1,y1; + int invert; +} stbtt__edge; + + +typedef struct stbtt__active_edge +{ + struct stbtt__active_edge *next; + #if STBTT_RASTERIZER_VERSION==1 + int x,dx; + float ey; + int direction; + #elif STBTT_RASTERIZER_VERSION==2 + float fx,fdx,fdy; + float direction; + float sy; + float ey; + #else + #error "Unrecognized value of STBTT_RASTERIZER_VERSION" + #endif +} stbtt__active_edge; + +#if STBTT_RASTERIZER_VERSION == 1 +#define STBTT_FIXSHIFT 10 +#define STBTT_FIX (1 << STBTT_FIXSHIFT) +#define STBTT_FIXMASK (STBTT_FIX-1) + +static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) +{ + stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + STBTT_assert(z != NULL); + if (!z) return z; + + // round dx down to avoid overshooting + if (dxdy < 0) + z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy); + else + z->dx = STBTT_ifloor(STBTT_FIX * dxdy); + + z->x = STBTT_ifloor(STBTT_FIX * e->x0 + z->dx * (start_point - e->y0)); // use z->dx so when we offset later it's by the same amount + z->x -= off_x * STBTT_FIX; + + z->ey = e->y1; + z->next = 0; + z->direction = e->invert ? 1 : -1; + return z; +} +#elif STBTT_RASTERIZER_VERSION == 2 +static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) +{ + stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + STBTT_assert(z != NULL); + //STBTT_assert(e->y0 <= start_point); + if (!z) return z; + z->fdx = dxdy; + z->fdy = dxdy != 0.0f ? (1.0f/dxdy) : 0.0f; + z->fx = e->x0 + dxdy * (start_point - e->y0); + z->fx -= off_x; + z->direction = e->invert ? 1.0f : -1.0f; + z->sy = e->y0; + z->ey = e->y1; + z->next = 0; + return z; +} +#else +#error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + +#if STBTT_RASTERIZER_VERSION == 1 +// note: this routine clips fills that extend off the edges... ideally this +// wouldn't happen, but it could happen if the truetype glyph bounding boxes +// are wrong, or if the user supplies a too-small bitmap +static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight) +{ + // non-zero winding fill + int x0=0, w=0; + + while (e) { + if (w == 0) { + // if we're currently at zero, we need to record the edge start point + x0 = e->x; w += e->direction; + } else { + int x1 = e->x; w += e->direction; + // if we went to zero, we need to draw + if (w == 0) { + int i = x0 >> STBTT_FIXSHIFT; + int j = x1 >> STBTT_FIXSHIFT; + + if (i < len && j >= 0) { + if (i == j) { + // x0,x1 are the same pixel, so compute combined coverage + scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> STBTT_FIXSHIFT); + } else { + if (i >= 0) // add antialiasing for x0 + scanline[i] = scanline[i] + (stbtt_uint8) (((STBTT_FIX - (x0 & STBTT_FIXMASK)) * max_weight) >> STBTT_FIXSHIFT); + else + i = -1; // clip + + if (j < len) // add antialiasing for x1 + scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & STBTT_FIXMASK) * max_weight) >> STBTT_FIXSHIFT); + else + j = len; // clip + + for (++i; i < j; ++i) // fill pixels between x0 and x1 + scanline[i] = scanline[i] + (stbtt_uint8) max_weight; + } + } + } + } + + e = e->next; + } +} + +static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) +{ + stbtt__hheap hh = { 0, 0, 0 }; + stbtt__active_edge *active = NULL; + int y,j=0; + int max_weight = (255 / vsubsample); // weight per vertical scanline + int s; // vertical subsample index + unsigned char scanline_data[512], *scanline; + + if (result->w > 512) + scanline = (unsigned char *) STBTT_malloc(result->w, userdata); + else + scanline = scanline_data; + + y = off_y * vsubsample; + e[n].y0 = (off_y + result->h) * (float) vsubsample + 1; + + while (j < result->h) { + STBTT_memset(scanline, 0, result->w); + for (s=0; s < vsubsample; ++s) { + // find center of pixel for this scanline + float scan_y = y + 0.5f; + stbtt__active_edge **step = &active; + + // update all active edges; + // remove all active edges that terminate before the center of this scanline + while (*step) { + stbtt__active_edge * z = *step; + if (z->ey <= scan_y) { + *step = z->next; // delete from list + STBTT_assert(z->direction); + z->direction = 0; + stbtt__hheap_free(&hh, z); + } else { + z->x += z->dx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + } + + // resort the list if needed + for(;;) { + int changed=0; + step = &active; + while (*step && (*step)->next) { + if ((*step)->x > (*step)->next->x) { + stbtt__active_edge *t = *step; + stbtt__active_edge *q = t->next; + + t->next = q->next; + q->next = t; + *step = q; + changed = 1; + } + step = &(*step)->next; + } + if (!changed) break; + } + + // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline + while (e->y0 <= scan_y) { + if (e->y1 > scan_y) { + stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y, userdata); + if (z != NULL) { + // find insertion point + if (active == NULL) + active = z; + else if (z->x < active->x) { + // insert at front + z->next = active; + active = z; + } else { + // find thing to insert AFTER + stbtt__active_edge *p = active; + while (p->next && p->next->x < z->x) + p = p->next; + // at this point, p->next->x is NOT < z->x + z->next = p->next; + p->next = z; + } + } + } + ++e; + } + + // now process all active edges in XOR fashion + if (active) + stbtt__fill_active_edges(scanline, result->w, active, max_weight); + + ++y; + } + STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w); + ++j; + } + + stbtt__hheap_cleanup(&hh, userdata); + + if (scanline != scanline_data) + STBTT_free(scanline, userdata); +} + +#elif STBTT_RASTERIZER_VERSION == 2 + +// the edge passed in here does not cross the vertical line at x or the vertical line at x+1 +// (i.e. it has already been clipped to those) +static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edge *e, float x0, float y0, float x1, float y1) +{ + if (y0 == y1) return; + STBTT_assert(y0 < y1); + STBTT_assert(e->sy <= e->ey); + if (y0 > e->ey) return; + if (y1 < e->sy) return; + if (y0 < e->sy) { + x0 += (x1-x0) * (e->sy - y0) / (y1-y0); + y0 = e->sy; + } + if (y1 > e->ey) { + x1 += (x1-x0) * (e->ey - y1) / (y1-y0); + y1 = e->ey; + } + + if (x0 == x) + STBTT_assert(x1 <= x+1); + else if (x0 == x+1) + STBTT_assert(x1 >= x); + else if (x0 <= x) + STBTT_assert(x1 <= x); + else if (x0 >= x+1) + STBTT_assert(x1 >= x+1); + else + STBTT_assert(x1 >= x && x1 <= x+1); + + if (x0 <= x && x1 <= x) + scanline[x] += e->direction * (y1-y0); + else if (x0 >= x+1 && x1 >= x+1) + ; + else { + STBTT_assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1); + scanline[x] += e->direction * (y1-y0) * (1-((x0-x)+(x1-x))/2); // coverage = 1 - average x position + } +} + +static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top) +{ + float y_bottom = y_top+1; + + while (e) { + // brute force every pixel + + // compute intersection points with top & bottom + STBTT_assert(e->ey >= y_top); + + if (e->fdx == 0) { + float x0 = e->fx; + if (x0 < len) { + if (x0 >= 0) { + stbtt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom); + stbtt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom); + } else { + stbtt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom); + } + } + } else { + float x0 = e->fx; + float dx = e->fdx; + float xb = x0 + dx; + float x_top, x_bottom; + float sy0,sy1; + float dy = e->fdy; + STBTT_assert(e->sy <= y_bottom && e->ey >= y_top); + + // compute endpoints of line segment clipped to this scanline (if the + // line segment starts on this scanline. x0 is the intersection of the + // line with y_top, but that may be off the line segment. + if (e->sy > y_top) { + x_top = x0 + dx * (e->sy - y_top); + sy0 = e->sy; + } else { + x_top = x0; + sy0 = y_top; + } + if (e->ey < y_bottom) { + x_bottom = x0 + dx * (e->ey - y_top); + sy1 = e->ey; + } else { + x_bottom = xb; + sy1 = y_bottom; + } + + if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) { + // from here on, we don't have to range check x values + + if ((int) x_top == (int) x_bottom) { + float height; + // simple case, only spans one pixel + int x = (int) x_top; + height = sy1 - sy0; + STBTT_assert(x >= 0 && x < len); + scanline[x] += e->direction * (1-((x_top - x) + (x_bottom-x))/2) * height; + scanline_fill[x] += e->direction * height; // everything right of this pixel is filled + } else { + int x,x1,x2; + float y_crossing, step, sign, area; + // covers 2+ pixels + if (x_top > x_bottom) { + // flip scanline vertically; signed area is the same + float t; + sy0 = y_bottom - (sy0 - y_top); + sy1 = y_bottom - (sy1 - y_top); + t = sy0, sy0 = sy1, sy1 = t; + t = x_bottom, x_bottom = x_top, x_top = t; + dx = -dx; + dy = -dy; + t = x0, x0 = xb, xb = t; + } + + x1 = (int) x_top; + x2 = (int) x_bottom; + // compute intersection with y axis at x1+1 + y_crossing = (x1+1 - x0) * dy + y_top; + + sign = e->direction; + // area of the rectangle covered from y0..y_crossing + area = sign * (y_crossing-sy0); + // area of the triangle (x_top,y0), (x+1,y0), (x+1,y_crossing) + scanline[x1] += area * (1-((x_top - x1)+(x1+1-x1))/2); + + step = sign * dy; + for (x = x1+1; x < x2; ++x) { + scanline[x] += area + step/2; + area += step; + } + y_crossing += dy * (x2 - (x1+1)); + + STBTT_assert(STBTT_fabs(area) <= 1.01f); + + scanline[x2] += area + sign * (1-((x2-x2)+(x_bottom-x2))/2) * (sy1-y_crossing); + + scanline_fill[x2] += sign * (sy1-sy0); + } + } else { + // if edge goes outside of box we're drawing, we require + // clipping logic. since this does not match the intended use + // of this library, we use a different, very slow brute + // force implementation + int x; + for (x=0; x < len; ++x) { + // cases: + // + // there can be up to two intersections with the pixel. any intersection + // with left or right edges can be handled by splitting into two (or three) + // regions. intersections with top & bottom do not necessitate case-wise logic. + // + // the old way of doing this found the intersections with the left & right edges, + // then used some simple logic to produce up to three segments in sorted order + // from top-to-bottom. however, this had a problem: if an x edge was epsilon + // across the x border, then the corresponding y position might not be distinct + // from the other y segment, and it might ignored as an empty segment. to avoid + // that, we need to explicitly produce segments based on x positions. + + // rename variables to clearly-defined pairs + float y0 = y_top; + float x1 = (float) (x); + float x2 = (float) (x+1); + float x3 = xb; + float y3 = y_bottom; + + // x = e->x + e->dx * (y-y_top) + // (y-y_top) = (x - e->x) / e->dx + // y = (x - e->x) / e->dx + y_top + float y1 = (x - x0) / dx + y_top; + float y2 = (x+1 - x0) / dx + y_top; + + if (x0 < x1 && x3 > x2) { // three segments descending down-right + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else if (x3 < x1 && x0 > x2) { // three segments descending down-left + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x0 < x1 && x3 > x1) { // two segments across x, down-right + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x3 < x1 && x0 > x1) { // two segments across x, down-left + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x0 < x2 && x3 > x2) { // two segments across x+1, down-right + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else if (x3 < x2 && x0 > x2) { // two segments across x+1, down-left + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else { // one segment + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x3,y3); + } + } + } + } + e = e->next; + } +} + +// directly AA rasterize edges w/o supersampling +static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) +{ + stbtt__hheap hh = { 0, 0, 0 }; + stbtt__active_edge *active = NULL; + int y,j=0, i; + float scanline_data[129], *scanline, *scanline2; + + STBTT__NOTUSED(vsubsample); + + if (result->w > 64) + scanline = (float *) STBTT_malloc((result->w*2+1) * sizeof(float), userdata); + else + scanline = scanline_data; + + scanline2 = scanline + result->w; + + y = off_y; + e[n].y0 = (float) (off_y + result->h) + 1; + + while (j < result->h) { + // find center of pixel for this scanline + float scan_y_top = y + 0.0f; + float scan_y_bottom = y + 1.0f; + stbtt__active_edge **step = &active; + + STBTT_memset(scanline , 0, result->w*sizeof(scanline[0])); + STBTT_memset(scanline2, 0, (result->w+1)*sizeof(scanline[0])); + + // update all active edges; + // remove all active edges that terminate before the top of this scanline + while (*step) { + stbtt__active_edge * z = *step; + if (z->ey <= scan_y_top) { + *step = z->next; // delete from list + STBTT_assert(z->direction); + z->direction = 0; + stbtt__hheap_free(&hh, z); + } else { + step = &((*step)->next); // advance through list + } + } + + // insert all edges that start before the bottom of this scanline + while (e->y0 <= scan_y_bottom) { + if (e->y0 != e->y1) { + stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata); + if (z != NULL) { + if (j == 0 && off_y != 0) { + if (z->ey < scan_y_top) { + // this can happen due to subpixel positioning and some kind of fp rounding error i think + z->ey = scan_y_top; + } + } + STBTT_assert(z->ey >= scan_y_top); // if we get really unlucky a tiny bit of an edge can be out of bounds + // insert at front + z->next = active; + active = z; + } + } + ++e; + } + + // now process all active edges + if (active) + stbtt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top); + + { + float sum = 0; + for (i=0; i < result->w; ++i) { + float k; + int m; + sum += scanline2[i]; + k = scanline[i] + sum; + k = (float) STBTT_fabs(k)*255 + 0.5f; + m = (int) k; + if (m > 255) m = 255; + result->pixels[j*result->stride + i] = (unsigned char) m; + } + } + // advance all the edges + step = &active; + while (*step) { + stbtt__active_edge *z = *step; + z->fx += z->fdx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + + ++y; + ++j; + } + + stbtt__hheap_cleanup(&hh, userdata); + + if (scanline != scanline_data) + STBTT_free(scanline, userdata); +} +#else +#error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + +#define STBTT__COMPARE(a,b) ((a)->y0 < (b)->y0) + +static void stbtt__sort_edges_ins_sort(stbtt__edge *p, int n) +{ + int i,j; + for (i=1; i < n; ++i) { + stbtt__edge t = p[i], *a = &t; + j = i; + while (j > 0) { + stbtt__edge *b = &p[j-1]; + int c = STBTT__COMPARE(a,b); + if (!c) break; + p[j] = p[j-1]; + --j; + } + if (i != j) + p[j] = t; + } +} + +static void stbtt__sort_edges_quicksort(stbtt__edge *p, int n) +{ + /* threshold for transitioning to insertion sort */ + while (n > 12) { + stbtt__edge t; + int c01,c12,c,m,i,j; + + /* compute median of three */ + m = n >> 1; + c01 = STBTT__COMPARE(&p[0],&p[m]); + c12 = STBTT__COMPARE(&p[m],&p[n-1]); + /* if 0 >= mid >= end, or 0 < mid < end, then use mid */ + if (c01 != c12) { + /* otherwise, we'll need to swap something else to middle */ + int z; + c = STBTT__COMPARE(&p[0],&p[n-1]); + /* 0>mid && midn => n; 0 0 */ + /* 0n: 0>n => 0; 0 n */ + z = (c == c12) ? 0 : n-1; + t = p[z]; + p[z] = p[m]; + p[m] = t; + } + /* now p[m] is the median-of-three */ + /* swap it to the beginning so it won't move around */ + t = p[0]; + p[0] = p[m]; + p[m] = t; + + /* partition loop */ + i=1; + j=n-1; + for(;;) { + /* handling of equality is crucial here */ + /* for sentinels & efficiency with duplicates */ + for (;;++i) { + if (!STBTT__COMPARE(&p[i], &p[0])) break; + } + for (;;--j) { + if (!STBTT__COMPARE(&p[0], &p[j])) break; + } + /* make sure we haven't crossed */ + if (i >= j) break; + t = p[i]; + p[i] = p[j]; + p[j] = t; + + ++i; + --j; + } + /* recurse on smaller side, iterate on larger */ + if (j < (n-i)) { + stbtt__sort_edges_quicksort(p,j); + p = p+i; + n = n-i; + } else { + stbtt__sort_edges_quicksort(p+i, n-i); + n = j; + } + } +} + +static void stbtt__sort_edges(stbtt__edge *p, int n) +{ + stbtt__sort_edges_quicksort(p, n); + stbtt__sort_edges_ins_sort(p, n); +} + +typedef struct +{ + float x,y; +} stbtt__point; + +static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, void *userdata) +{ + float y_scale_inv = invert ? -scale_y : scale_y; + stbtt__edge *e; + int n,i,j,k,m; +#if STBTT_RASTERIZER_VERSION == 1 + int vsubsample = result->h < 8 ? 15 : 5; +#elif STBTT_RASTERIZER_VERSION == 2 + int vsubsample = 1; +#else + #error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + // vsubsample should divide 255 evenly; otherwise we won't reach full opacity + + // now we have to blow out the windings into explicit edge lists + n = 0; + for (i=0; i < windings; ++i) + n += wcount[i]; + + e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel + if (e == 0) return; + n = 0; + + m=0; + for (i=0; i < windings; ++i) { + stbtt__point *p = pts + m; + m += wcount[i]; + j = wcount[i]-1; + for (k=0; k < wcount[i]; j=k++) { + int a=k,b=j; + // skip the edge if horizontal + if (p[j].y == p[k].y) + continue; + // add edge from j to k to the list + e[n].invert = 0; + if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { + e[n].invert = 1; + a=j,b=k; + } + e[n].x0 = p[a].x * scale_x + shift_x; + e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample; + e[n].x1 = p[b].x * scale_x + shift_x; + e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample; + ++n; + } + } + + // now sort the edges by their highest point (should snap to integer, and then by x) + //STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); + stbtt__sort_edges(e, n); + + // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule + stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata); + + STBTT_free(e, userdata); +} + +static void stbtt__add_point(stbtt__point *points, int n, float x, float y) +{ + if (!points) return; // during first pass, it's unallocated + points[n].x = x; + points[n].y = y; +} + +// tessellate until threshold p is happy... @TODO warped to compensate for non-linear stretching +static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) +{ + // midpoint + float mx = (x0 + 2*x1 + x2)/4; + float my = (y0 + 2*y1 + y2)/4; + // versus directly drawn line + float dx = (x0+x2)/2 - mx; + float dy = (y0+y2)/2 - my; + if (n > 16) // 65536 segments on one curve better be enough! + return 1; + if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA + stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); + stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); + } else { + stbtt__add_point(points, *num_points,x2,y2); + *num_points = *num_points+1; + } + return 1; +} + +static void stbtt__tesselate_cubic(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float objspace_flatness_squared, int n) +{ + // @TODO this "flatness" calculation is just made-up nonsense that seems to work well enough + float dx0 = x1-x0; + float dy0 = y1-y0; + float dx1 = x2-x1; + float dy1 = y2-y1; + float dx2 = x3-x2; + float dy2 = y3-y2; + float dx = x3-x0; + float dy = y3-y0; + float longlen = (float) (STBTT_sqrt(dx0*dx0+dy0*dy0)+STBTT_sqrt(dx1*dx1+dy1*dy1)+STBTT_sqrt(dx2*dx2+dy2*dy2)); + float shortlen = (float) STBTT_sqrt(dx*dx+dy*dy); + float flatness_squared = longlen*longlen-shortlen*shortlen; + + if (n > 16) // 65536 segments on one curve better be enough! + return; + + if (flatness_squared > objspace_flatness_squared) { + float x01 = (x0+x1)/2; + float y01 = (y0+y1)/2; + float x12 = (x1+x2)/2; + float y12 = (y1+y2)/2; + float x23 = (x2+x3)/2; + float y23 = (y2+y3)/2; + + float xa = (x01+x12)/2; + float ya = (y01+y12)/2; + float xb = (x12+x23)/2; + float yb = (y12+y23)/2; + + float mx = (xa+xb)/2; + float my = (ya+yb)/2; + + stbtt__tesselate_cubic(points, num_points, x0,y0, x01,y01, xa,ya, mx,my, objspace_flatness_squared,n+1); + stbtt__tesselate_cubic(points, num_points, mx,my, xb,yb, x23,y23, x3,y3, objspace_flatness_squared,n+1); + } else { + stbtt__add_point(points, *num_points,x3,y3); + *num_points = *num_points+1; + } +} + +// returns number of contours +static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata) +{ + stbtt__point *points=0; + int num_points=0; + + float objspace_flatness_squared = objspace_flatness * objspace_flatness; + int i,n=0,start=0, pass; + + // count how many "moves" there are to get the contour count + for (i=0; i < num_verts; ++i) + if (vertices[i].type == STBTT_vmove) + ++n; + + *num_contours = n; + if (n == 0) return 0; + + *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata); + + if (*contour_lengths == 0) { + *num_contours = 0; + return 0; + } + + // make two passes through the points so we don't need to realloc + for (pass=0; pass < 2; ++pass) { + float x=0,y=0; + if (pass == 1) { + points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata); + if (points == NULL) goto error; + } + num_points = 0; + n= -1; + for (i=0; i < num_verts; ++i) { + switch (vertices[i].type) { + case STBTT_vmove: + // start the next contour + if (n >= 0) + (*contour_lengths)[n] = num_points - start; + ++n; + start = num_points; + + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x,y); + break; + case STBTT_vline: + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x, y); + break; + case STBTT_vcurve: + stbtt__tesselate_curve(points, &num_points, x,y, + vertices[i].cx, vertices[i].cy, + vertices[i].x, vertices[i].y, + objspace_flatness_squared, 0); + x = vertices[i].x, y = vertices[i].y; + break; + case STBTT_vcubic: + stbtt__tesselate_cubic(points, &num_points, x,y, + vertices[i].cx, vertices[i].cy, + vertices[i].cx1, vertices[i].cy1, + vertices[i].x, vertices[i].y, + objspace_flatness_squared, 0); + x = vertices[i].x, y = vertices[i].y; + break; + } + } + (*contour_lengths)[n] = num_points - start; + } + + return points; +error: + STBTT_free(points, userdata); + STBTT_free(*contour_lengths, userdata); + *contour_lengths = 0; + *num_contours = 0; + return NULL; +} + +STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata) +{ + float scale = scale_x > scale_y ? scale_y : scale_x; + int winding_count = 0; + int *winding_lengths = NULL; + stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata); + if (windings) { + stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata); + STBTT_free(winding_lengths, userdata); + STBTT_free(windings, userdata); + } +} + +STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata) +{ + STBTT_free(bitmap, userdata); +} + +STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + int ix0,iy0,ix1,iy1; + stbtt__bitmap gbm; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + + if (scale_x == 0) scale_x = scale_y; + if (scale_y == 0) { + if (scale_x == 0) { + STBTT_free(vertices, info->userdata); + return NULL; + } + scale_y = scale_x; + } + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1); + + // now we get the size + gbm.w = (ix1 - ix0); + gbm.h = (iy1 - iy0); + gbm.pixels = NULL; // in case we error + + if (width ) *width = gbm.w; + if (height) *height = gbm.h; + if (xoff ) *xoff = ix0; + if (yoff ) *yoff = iy0; + + if (gbm.w && gbm.h) { + gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); + if (gbm.pixels) { + gbm.stride = gbm.w; + + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata); + } + } + STBTT_free(vertices, info->userdata); + return gbm.pixels; +} + +STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff); +} + +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph) +{ + int ix0,iy0; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + stbtt__bitmap gbm; + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0); + gbm.pixels = output; + gbm.w = out_w; + gbm.h = out_h; + gbm.stride = out_stride; + + if (gbm.w && gbm.h) + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata); + + STBTT_free(vertices, info->userdata); +} + +STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph); +} + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); +} + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint) +{ + stbtt_MakeGlyphBitmapSubpixelPrefilter(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, oversample_x, oversample_y, sub_x, sub_y, stbtt_FindGlyphIndex(info,codepoint)); +} + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint)); +} + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff); +} + +STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) +{ + stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint); +} + +////////////////////////////////////////////////////////////////////////////// +// +// bitmap baking +// +// This is SUPER-CRAPPY packing to keep source code small + +static int stbtt_BakeFontBitmap_internal(unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata) +{ + float scale; + int x,y,bottom_y, i; + stbtt_fontinfo f; + f.userdata = NULL; + if (!stbtt_InitFont(&f, data, offset)) + return -1; + STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels + x=y=1; + bottom_y = 1; + + scale = stbtt_ScaleForPixelHeight(&f, pixel_height); + + for (i=0; i < num_chars; ++i) { + int advance, lsb, x0,y0,x1,y1,gw,gh; + int g = stbtt_FindGlyphIndex(&f, first_char + i); + stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb); + stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1); + gw = x1-x0; + gh = y1-y0; + if (x + gw + 1 >= pw) + y = bottom_y, x = 1; // advance to next row + if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row + return -i; + STBTT_assert(x+gw < pw); + STBTT_assert(y+gh < ph); + stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g); + chardata[i].x0 = (stbtt_int16) x; + chardata[i].y0 = (stbtt_int16) y; + chardata[i].x1 = (stbtt_int16) (x + gw); + chardata[i].y1 = (stbtt_int16) (y + gh); + chardata[i].xadvance = scale * advance; + chardata[i].xoff = (float) x0; + chardata[i].yoff = (float) y0; + x = x + gw + 1; + if (y+gh+1 > bottom_y) + bottom_y = y+gh+1; + } + return bottom_y; +} + +STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) +{ + float d3d_bias = opengl_fillrule ? 0 : -0.5f; + float ipw = 1.0f / pw, iph = 1.0f / ph; + const stbtt_bakedchar *b = chardata + char_index; + int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5f); + int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5f); + + q->x0 = round_x + d3d_bias; + q->y0 = round_y + d3d_bias; + q->x1 = round_x + b->x1 - b->x0 + d3d_bias; + q->y1 = round_y + b->y1 - b->y0 + d3d_bias; + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + + *xpos += b->xadvance; +} + +////////////////////////////////////////////////////////////////////////////// +// +// rectangle packing replacement routines if you don't have stb_rect_pack.h +// + +#ifndef STB_RECT_PACK_VERSION + +typedef int stbrp_coord; + +//////////////////////////////////////////////////////////////////////////////////// +// // +// // +// COMPILER WARNING ?!?!? // +// // +// // +// if you get a compile warning due to these symbols being defined more than // +// once, move #include "stb_rect_pack.h" before #include "stb_truetype.h" // +// // +//////////////////////////////////////////////////////////////////////////////////// + +typedef struct +{ + int width,height; + int x,y,bottom_y; +} stbrp_context; + +typedef struct +{ + unsigned char x; +} stbrp_node; + +struct stbrp_rect +{ + stbrp_coord x,y; + int id,w,h,was_packed; +}; + +static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes) +{ + con->width = pw; + con->height = ph; + con->x = 0; + con->y = 0; + con->bottom_y = 0; + STBTT__NOTUSED(nodes); + STBTT__NOTUSED(num_nodes); +} + +static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects) +{ + int i; + for (i=0; i < num_rects; ++i) { + if (con->x + rects[i].w > con->width) { + con->x = 0; + con->y = con->bottom_y; + } + if (con->y + rects[i].h > con->height) + break; + rects[i].x = con->x; + rects[i].y = con->y; + rects[i].was_packed = 1; + con->x += rects[i].w; + if (con->y + rects[i].h > con->bottom_y) + con->bottom_y = con->y + rects[i].h; + } + for ( ; i < num_rects; ++i) + rects[i].was_packed = 0; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// bitmap baking +// +// This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If +// stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy. + +STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context) +{ + stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context) ,alloc_context); + int num_nodes = pw - padding; + stbrp_node *nodes = (stbrp_node *) STBTT_malloc(sizeof(*nodes ) * num_nodes,alloc_context); + + if (context == NULL || nodes == NULL) { + if (context != NULL) STBTT_free(context, alloc_context); + if (nodes != NULL) STBTT_free(nodes , alloc_context); + return 0; + } + + spc->user_allocator_context = alloc_context; + spc->width = pw; + spc->height = ph; + spc->pixels = pixels; + spc->pack_info = context; + spc->nodes = nodes; + spc->padding = padding; + spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw; + spc->h_oversample = 1; + spc->v_oversample = 1; + spc->skip_missing = 0; + + stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes); + + if (pixels) + STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels + + return 1; +} + +STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc) +{ + STBTT_free(spc->nodes , spc->user_allocator_context); + STBTT_free(spc->pack_info, spc->user_allocator_context); +} + +STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample) +{ + STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE); + STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE); + if (h_oversample <= STBTT_MAX_OVERSAMPLE) + spc->h_oversample = h_oversample; + if (v_oversample <= STBTT_MAX_OVERSAMPLE) + spc->v_oversample = v_oversample; +} + +STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip) +{ + spc->skip_missing = skip; +} + +#define STBTT__OVER_MASK (STBTT_MAX_OVERSAMPLE-1) + +static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) +{ + unsigned char buffer[STBTT_MAX_OVERSAMPLE]; + int safe_w = w - kernel_width; + int j; + STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze + for (j=0; j < h; ++j) { + int i; + unsigned int total; + STBTT_memset(buffer, 0, kernel_width); + + total = 0; + + // make kernel_width a constant in common cases so compiler can optimize out the divide + switch (kernel_width) { + case 2: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 4); + } + break; + case 5: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 5); + } + break; + default: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / kernel_width); + } + break; + } + + for (; i < w; ++i) { + STBTT_assert(pixels[i] == 0); + total -= buffer[i & STBTT__OVER_MASK]; + pixels[i] = (unsigned char) (total / kernel_width); + } + + pixels += stride_in_bytes; + } +} + +static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) +{ + unsigned char buffer[STBTT_MAX_OVERSAMPLE]; + int safe_h = h - kernel_width; + int j; + STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze + for (j=0; j < w; ++j) { + int i; + unsigned int total; + STBTT_memset(buffer, 0, kernel_width); + + total = 0; + + // make kernel_width a constant in common cases so compiler can optimize out the divide + switch (kernel_width) { + case 2: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 4); + } + break; + case 5: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 5); + } + break; + default: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); + } + break; + } + + for (; i < h; ++i) { + STBTT_assert(pixels[i*stride_in_bytes] == 0); + total -= buffer[i & STBTT__OVER_MASK]; + pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); + } + + pixels += 1; + } +} + +static float stbtt__oversample_shift(int oversample) +{ + if (!oversample) + return 0.0f; + + // The prefilter is a box filter of width "oversample", + // which shifts phase by (oversample - 1)/2 pixels in + // oversampled space. We want to shift in the opposite + // direction to counter this. + return (float)-(oversample - 1) / (2.0f * (float)oversample); +} + +// rects array must be big enough to accommodate all characters in the given ranges +STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) +{ + int i,j,k; + int missing_glyph_added = 0; + + k=0; + for (i=0; i < num_ranges; ++i) { + float fh = ranges[i].font_size; + float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); + ranges[i].h_oversample = (unsigned char) spc->h_oversample; + ranges[i].v_oversample = (unsigned char) spc->v_oversample; + for (j=0; j < ranges[i].num_chars; ++j) { + int x0,y0,x1,y1; + int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; + int glyph = stbtt_FindGlyphIndex(info, codepoint); + if (glyph == 0 && (spc->skip_missing || missing_glyph_added)) { + rects[k].w = rects[k].h = 0; + } else { + stbtt_GetGlyphBitmapBoxSubpixel(info,glyph, + scale * spc->h_oversample, + scale * spc->v_oversample, + 0,0, + &x0,&y0,&x1,&y1); + rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1); + rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1); + if (glyph == 0) + missing_glyph_added = 1; + } + ++k; + } + } + + return k; +} + +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int prefilter_x, int prefilter_y, float *sub_x, float *sub_y, int glyph) +{ + stbtt_MakeGlyphBitmapSubpixel(info, + output, + out_w - (prefilter_x - 1), + out_h - (prefilter_y - 1), + out_stride, + scale_x, + scale_y, + shift_x, + shift_y, + glyph); + + if (prefilter_x > 1) + stbtt__h_prefilter(output, out_w, out_h, out_stride, prefilter_x); + + if (prefilter_y > 1) + stbtt__v_prefilter(output, out_w, out_h, out_stride, prefilter_y); + + *sub_x = stbtt__oversample_shift(prefilter_x); + *sub_y = stbtt__oversample_shift(prefilter_y); +} + +// rects array must be big enough to accommodate all characters in the given ranges +STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) +{ + int i,j,k, missing_glyph = -1, return_value = 1; + + // save current values + int old_h_over = spc->h_oversample; + int old_v_over = spc->v_oversample; + + k = 0; + for (i=0; i < num_ranges; ++i) { + float fh = ranges[i].font_size; + float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); + float recip_h,recip_v,sub_x,sub_y; + spc->h_oversample = ranges[i].h_oversample; + spc->v_oversample = ranges[i].v_oversample; + recip_h = 1.0f / spc->h_oversample; + recip_v = 1.0f / spc->v_oversample; + sub_x = stbtt__oversample_shift(spc->h_oversample); + sub_y = stbtt__oversample_shift(spc->v_oversample); + for (j=0; j < ranges[i].num_chars; ++j) { + stbrp_rect *r = &rects[k]; + if (r->was_packed && r->w != 0 && r->h != 0) { + stbtt_packedchar *bc = &ranges[i].chardata_for_range[j]; + int advance, lsb, x0,y0,x1,y1; + int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; + int glyph = stbtt_FindGlyphIndex(info, codepoint); + stbrp_coord pad = (stbrp_coord) spc->padding; + + // pad on left and top + r->x += pad; + r->y += pad; + r->w -= pad; + r->h -= pad; + stbtt_GetGlyphHMetrics(info, glyph, &advance, &lsb); + stbtt_GetGlyphBitmapBox(info, glyph, + scale * spc->h_oversample, + scale * spc->v_oversample, + &x0,&y0,&x1,&y1); + stbtt_MakeGlyphBitmapSubpixel(info, + spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w - spc->h_oversample+1, + r->h - spc->v_oversample+1, + spc->stride_in_bytes, + scale * spc->h_oversample, + scale * spc->v_oversample, + 0,0, + glyph); + + if (spc->h_oversample > 1) + stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, + spc->h_oversample); + + if (spc->v_oversample > 1) + stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, + spc->v_oversample); + + bc->x0 = (stbtt_int16) r->x; + bc->y0 = (stbtt_int16) r->y; + bc->x1 = (stbtt_int16) (r->x + r->w); + bc->y1 = (stbtt_int16) (r->y + r->h); + bc->xadvance = scale * advance; + bc->xoff = (float) x0 * recip_h + sub_x; + bc->yoff = (float) y0 * recip_v + sub_y; + bc->xoff2 = (x0 + r->w) * recip_h + sub_x; + bc->yoff2 = (y0 + r->h) * recip_v + sub_y; + + if (glyph == 0) + missing_glyph = j; + } else if (spc->skip_missing) { + return_value = 0; + } else if (r->was_packed && r->w == 0 && r->h == 0 && missing_glyph >= 0) { + ranges[i].chardata_for_range[j] = ranges[i].chardata_for_range[missing_glyph]; + } else { + return_value = 0; // if any fail, report failure + } + + ++k; + } + } + + // restore original values + spc->h_oversample = old_h_over; + spc->v_oversample = old_v_over; + + return return_value; +} + +STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects) +{ + stbrp_pack_rects((stbrp_context *) spc->pack_info, rects, num_rects); +} + +STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges) +{ + stbtt_fontinfo info; + int i,j,n, return_value = 1; + //stbrp_context *context = (stbrp_context *) spc->pack_info; + stbrp_rect *rects; + + // flag all characters as NOT packed + for (i=0; i < num_ranges; ++i) + for (j=0; j < ranges[i].num_chars; ++j) + ranges[i].chardata_for_range[j].x0 = + ranges[i].chardata_for_range[j].y0 = + ranges[i].chardata_for_range[j].x1 = + ranges[i].chardata_for_range[j].y1 = 0; + + n = 0; + for (i=0; i < num_ranges; ++i) + n += ranges[i].num_chars; + + rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context); + if (rects == NULL) + return 0; + + info.userdata = spc->user_allocator_context; + stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index)); + + n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects); + + stbtt_PackFontRangesPackRects(spc, rects, n); + + return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects); + + STBTT_free(rects, spc->user_allocator_context); + return return_value; +} + +STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size, + int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range) +{ + stbtt_pack_range range; + range.first_unicode_codepoint_in_range = first_unicode_codepoint_in_range; + range.array_of_unicode_codepoints = NULL; + range.num_chars = num_chars_in_range; + range.chardata_for_range = chardata_for_range; + range.font_size = font_size; + return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1); +} + +STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap) +{ + int i_ascent, i_descent, i_lineGap; + float scale; + stbtt_fontinfo info; + stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata, index)); + scale = size > 0 ? stbtt_ScaleForPixelHeight(&info, size) : stbtt_ScaleForMappingEmToPixels(&info, -size); + stbtt_GetFontVMetrics(&info, &i_ascent, &i_descent, &i_lineGap); + *ascent = (float) i_ascent * scale; + *descent = (float) i_descent * scale; + *lineGap = (float) i_lineGap * scale; +} + +STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer) +{ + float ipw = 1.0f / pw, iph = 1.0f / ph; + const stbtt_packedchar *b = chardata + char_index; + + if (align_to_integer) { + float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f); + float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5f); + q->x0 = x; + q->y0 = y; + q->x1 = x + b->xoff2 - b->xoff; + q->y1 = y + b->yoff2 - b->yoff; + } else { + q->x0 = *xpos + b->xoff; + q->y0 = *ypos + b->yoff; + q->x1 = *xpos + b->xoff2; + q->y1 = *ypos + b->yoff2; + } + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + + *xpos += b->xadvance; +} + +////////////////////////////////////////////////////////////////////////////// +// +// sdf computation +// + +#define STBTT_min(a,b) ((a) < (b) ? (a) : (b)) +#define STBTT_max(a,b) ((a) < (b) ? (b) : (a)) + +static int stbtt__ray_intersect_bezier(float orig[2], float ray[2], float q0[2], float q1[2], float q2[2], float hits[2][2]) +{ + float q0perp = q0[1]*ray[0] - q0[0]*ray[1]; + float q1perp = q1[1]*ray[0] - q1[0]*ray[1]; + float q2perp = q2[1]*ray[0] - q2[0]*ray[1]; + float roperp = orig[1]*ray[0] - orig[0]*ray[1]; + + float a = q0perp - 2*q1perp + q2perp; + float b = q1perp - q0perp; + float c = q0perp - roperp; + + float s0 = 0., s1 = 0.; + int num_s = 0; + + if (a != 0.0) { + float discr = b*b - a*c; + if (discr > 0.0) { + float rcpna = -1 / a; + float d = (float) STBTT_sqrt(discr); + s0 = (b+d) * rcpna; + s1 = (b-d) * rcpna; + if (s0 >= 0.0 && s0 <= 1.0) + num_s = 1; + if (d > 0.0 && s1 >= 0.0 && s1 <= 1.0) { + if (num_s == 0) s0 = s1; + ++num_s; + } + } + } else { + // 2*b*s + c = 0 + // s = -c / (2*b) + s0 = c / (-2 * b); + if (s0 >= 0.0 && s0 <= 1.0) + num_s = 1; + } + + if (num_s == 0) + return 0; + else { + float rcp_len2 = 1 / (ray[0]*ray[0] + ray[1]*ray[1]); + float rayn_x = ray[0] * rcp_len2, rayn_y = ray[1] * rcp_len2; + + float q0d = q0[0]*rayn_x + q0[1]*rayn_y; + float q1d = q1[0]*rayn_x + q1[1]*rayn_y; + float q2d = q2[0]*rayn_x + q2[1]*rayn_y; + float rod = orig[0]*rayn_x + orig[1]*rayn_y; + + float q10d = q1d - q0d; + float q20d = q2d - q0d; + float q0rd = q0d - rod; + + hits[0][0] = q0rd + s0*(2.0f - 2.0f*s0)*q10d + s0*s0*q20d; + hits[0][1] = a*s0+b; + + if (num_s > 1) { + hits[1][0] = q0rd + s1*(2.0f - 2.0f*s1)*q10d + s1*s1*q20d; + hits[1][1] = a*s1+b; + return 2; + } else { + return 1; + } + } +} + +static int equal(float *a, float *b) +{ + return (a[0] == b[0] && a[1] == b[1]); +} + +static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex *verts) +{ + int i; + float orig[2], ray[2] = { 1, 0 }; + float y_frac; + int winding = 0; + + orig[0] = x; + orig[1] = y; + + // make sure y never passes through a vertex of the shape + y_frac = (float) STBTT_fmod(y, 1.0f); + if (y_frac < 0.01f) + y += 0.01f; + else if (y_frac > 0.99f) + y -= 0.01f; + orig[1] = y; + + // test a ray from (-infinity,y) to (x,y) + for (i=0; i < nverts; ++i) { + if (verts[i].type == STBTT_vline) { + int x0 = (int) verts[i-1].x, y0 = (int) verts[i-1].y; + int x1 = (int) verts[i ].x, y1 = (int) verts[i ].y; + if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { + float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; + if (x_inter < x) + winding += (y0 < y1) ? 1 : -1; + } + } + if (verts[i].type == STBTT_vcurve) { + int x0 = (int) verts[i-1].x , y0 = (int) verts[i-1].y ; + int x1 = (int) verts[i ].cx, y1 = (int) verts[i ].cy; + int x2 = (int) verts[i ].x , y2 = (int) verts[i ].y ; + int ax = STBTT_min(x0,STBTT_min(x1,x2)), ay = STBTT_min(y0,STBTT_min(y1,y2)); + int by = STBTT_max(y0,STBTT_max(y1,y2)); + if (y > ay && y < by && x > ax) { + float q0[2],q1[2],q2[2]; + float hits[2][2]; + q0[0] = (float)x0; + q0[1] = (float)y0; + q1[0] = (float)x1; + q1[1] = (float)y1; + q2[0] = (float)x2; + q2[1] = (float)y2; + if (equal(q0,q1) || equal(q1,q2)) { + x0 = (int)verts[i-1].x; + y0 = (int)verts[i-1].y; + x1 = (int)verts[i ].x; + y1 = (int)verts[i ].y; + if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { + float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; + if (x_inter < x) + winding += (y0 < y1) ? 1 : -1; + } + } else { + int num_hits = stbtt__ray_intersect_bezier(orig, ray, q0, q1, q2, hits); + if (num_hits >= 1) + if (hits[0][0] < 0) + winding += (hits[0][1] < 0 ? -1 : 1); + if (num_hits >= 2) + if (hits[1][0] < 0) + winding += (hits[1][1] < 0 ? -1 : 1); + } + } + } + } + return winding; +} + +static float stbtt__cuberoot( float x ) +{ + if (x<0) + return -(float) STBTT_pow(-x,1.0f/3.0f); + else + return (float) STBTT_pow( x,1.0f/3.0f); +} + +// x^3 + c*x^2 + b*x + a = 0 +static int stbtt__solve_cubic(float a, float b, float c, float* r) +{ + float s = -a / 3; + float p = b - a*a / 3; + float q = a * (2*a*a - 9*b) / 27 + c; + float p3 = p*p*p; + float d = q*q + 4*p3 / 27; + if (d >= 0) { + float z = (float) STBTT_sqrt(d); + float u = (-q + z) / 2; + float v = (-q - z) / 2; + u = stbtt__cuberoot(u); + v = stbtt__cuberoot(v); + r[0] = s + u + v; + return 1; + } else { + float u = (float) STBTT_sqrt(-p/3); + float v = (float) STBTT_acos(-STBTT_sqrt(-27/p3) * q / 2) / 3; // p3 must be negative, since d is negative + float m = (float) STBTT_cos(v); + float n = (float) STBTT_cos(v-3.141592/2)*1.732050808f; + r[0] = s + u * 2 * m; + r[1] = s - u * (m + n); + r[2] = s - u * (m - n); + + //STBTT_assert( STBTT_fabs(((r[0]+a)*r[0]+b)*r[0]+c) < 0.05f); // these asserts may not be safe at all scales, though they're in bezier t parameter units so maybe? + //STBTT_assert( STBTT_fabs(((r[1]+a)*r[1]+b)*r[1]+c) < 0.05f); + //STBTT_assert( STBTT_fabs(((r[2]+a)*r[2]+b)*r[2]+c) < 0.05f); + return 3; + } +} + +STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) +{ + float scale_x = scale, scale_y = scale; + int ix0,iy0,ix1,iy1; + int w,h; + unsigned char *data; + + if (scale == 0) return NULL; + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.0f,0.0f, &ix0,&iy0,&ix1,&iy1); + + // if empty, return NULL + if (ix0 == ix1 || iy0 == iy1) + return NULL; + + ix0 -= padding; + iy0 -= padding; + ix1 += padding; + iy1 += padding; + + w = (ix1 - ix0); + h = (iy1 - iy0); + + if (width ) *width = w; + if (height) *height = h; + if (xoff ) *xoff = ix0; + if (yoff ) *yoff = iy0; + + // invert for y-downwards bitmaps + scale_y = -scale_y; + + { + int x,y,i,j; + float *precompute; + stbtt_vertex *verts; + int num_verts = stbtt_GetGlyphShape(info, glyph, &verts); + data = (unsigned char *) STBTT_malloc(w * h, info->userdata); + precompute = (float *) STBTT_malloc(num_verts * sizeof(float), info->userdata); + + for (i=0,j=num_verts-1; i < num_verts; j=i++) { + if (verts[i].type == STBTT_vline) { + float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; + float x1 = verts[j].x*scale_x, y1 = verts[j].y*scale_y; + float dist = (float) STBTT_sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0)); + precompute[i] = (dist == 0) ? 0.0f : 1.0f / dist; + } else if (verts[i].type == STBTT_vcurve) { + float x2 = verts[j].x *scale_x, y2 = verts[j].y *scale_y; + float x1 = verts[i].cx*scale_x, y1 = verts[i].cy*scale_y; + float x0 = verts[i].x *scale_x, y0 = verts[i].y *scale_y; + float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; + float len2 = bx*bx + by*by; + if (len2 != 0.0f) + precompute[i] = 1.0f / (bx*bx + by*by); + else + precompute[i] = 0.0f; + } else + precompute[i] = 0.0f; + } + + for (y=iy0; y < iy1; ++y) { + for (x=ix0; x < ix1; ++x) { + float val; + float min_dist = 999999.0f; + float sx = (float) x + 0.5f; + float sy = (float) y + 0.5f; + float x_gspace = (sx / scale_x); + float y_gspace = (sy / scale_y); + + int winding = stbtt__compute_crossings_x(x_gspace, y_gspace, num_verts, verts); // @OPTIMIZE: this could just be a rasterization, but needs to be line vs. non-tesselated curves so a new path + + for (i=0; i < num_verts; ++i) { + float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; + + // check against every point here rather than inside line/curve primitives -- @TODO: wrong if multiple 'moves' in a row produce a garbage point, and given culling, probably more efficient to do within line/curve + float dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy); + if (dist2 < min_dist*min_dist) + min_dist = (float) STBTT_sqrt(dist2); + + if (verts[i].type == STBTT_vline) { + float x1 = verts[i-1].x*scale_x, y1 = verts[i-1].y*scale_y; + + // coarse culling against bbox + //if (sx > STBTT_min(x0,x1)-min_dist && sx < STBTT_max(x0,x1)+min_dist && + // sy > STBTT_min(y0,y1)-min_dist && sy < STBTT_max(y0,y1)+min_dist) + float dist = (float) STBTT_fabs((x1-x0)*(y0-sy) - (y1-y0)*(x0-sx)) * precompute[i]; + STBTT_assert(i != 0); + if (dist < min_dist) { + // check position along line + // x' = x0 + t*(x1-x0), y' = y0 + t*(y1-y0) + // minimize (x'-sx)*(x'-sx)+(y'-sy)*(y'-sy) + float dx = x1-x0, dy = y1-y0; + float px = x0-sx, py = y0-sy; + // minimize (px+t*dx)^2 + (py+t*dy)^2 = px*px + 2*px*dx*t + t^2*dx*dx + py*py + 2*py*dy*t + t^2*dy*dy + // derivative: 2*px*dx + 2*py*dy + (2*dx*dx+2*dy*dy)*t, set to 0 and solve + float t = -(px*dx + py*dy) / (dx*dx + dy*dy); + if (t >= 0.0f && t <= 1.0f) + min_dist = dist; + } + } else if (verts[i].type == STBTT_vcurve) { + float x2 = verts[i-1].x *scale_x, y2 = verts[i-1].y *scale_y; + float x1 = verts[i ].cx*scale_x, y1 = verts[i ].cy*scale_y; + float box_x0 = STBTT_min(STBTT_min(x0,x1),x2); + float box_y0 = STBTT_min(STBTT_min(y0,y1),y2); + float box_x1 = STBTT_max(STBTT_max(x0,x1),x2); + float box_y1 = STBTT_max(STBTT_max(y0,y1),y2); + // coarse culling against bbox to avoid computing cubic unnecessarily + if (sx > box_x0-min_dist && sx < box_x1+min_dist && sy > box_y0-min_dist && sy < box_y1+min_dist) { + int num=0; + float ax = x1-x0, ay = y1-y0; + float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; + float mx = x0 - sx, my = y0 - sy; + float res[3],px,py,t,it; + float a_inv = precompute[i]; + if (a_inv == 0.0) { // if a_inv is 0, it's 2nd degree so use quadratic formula + float a = 3*(ax*bx + ay*by); + float b = 2*(ax*ax + ay*ay) + (mx*bx+my*by); + float c = mx*ax+my*ay; + if (a == 0.0) { // if a is 0, it's linear + if (b != 0.0) { + res[num++] = -c/b; + } + } else { + float discriminant = b*b - 4*a*c; + if (discriminant < 0) + num = 0; + else { + float root = (float) STBTT_sqrt(discriminant); + res[0] = (-b - root)/(2*a); + res[1] = (-b + root)/(2*a); + num = 2; // don't bother distinguishing 1-solution case, as code below will still work + } + } + } else { + float b = 3*(ax*bx + ay*by) * a_inv; // could precompute this as it doesn't depend on sample point + float c = (2*(ax*ax + ay*ay) + (mx*bx+my*by)) * a_inv; + float d = (mx*ax+my*ay) * a_inv; + num = stbtt__solve_cubic(b, c, d, res); + } + if (num >= 1 && res[0] >= 0.0f && res[0] <= 1.0f) { + t = res[0], it = 1.0f - t; + px = it*it*x0 + 2*t*it*x1 + t*t*x2; + py = it*it*y0 + 2*t*it*y1 + t*t*y2; + dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); + if (dist2 < min_dist * min_dist) + min_dist = (float) STBTT_sqrt(dist2); + } + if (num >= 2 && res[1] >= 0.0f && res[1] <= 1.0f) { + t = res[1], it = 1.0f - t; + px = it*it*x0 + 2*t*it*x1 + t*t*x2; + py = it*it*y0 + 2*t*it*y1 + t*t*y2; + dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); + if (dist2 < min_dist * min_dist) + min_dist = (float) STBTT_sqrt(dist2); + } + if (num >= 3 && res[2] >= 0.0f && res[2] <= 1.0f) { + t = res[2], it = 1.0f - t; + px = it*it*x0 + 2*t*it*x1 + t*t*x2; + py = it*it*y0 + 2*t*it*y1 + t*t*y2; + dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); + if (dist2 < min_dist * min_dist) + min_dist = (float) STBTT_sqrt(dist2); + } + } + } + } + if (winding == 0) + min_dist = -min_dist; // if outside the shape, value is negative + val = onedge_value + pixel_dist_scale * min_dist; + if (val < 0) + val = 0; + else if (val > 255) + val = 255; + data[(y-iy0)*w+(x-ix0)] = (unsigned char) val; + } + } + STBTT_free(precompute, info->userdata); + STBTT_free(verts, info->userdata); + } + return data; +} + +STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphSDF(info, scale, stbtt_FindGlyphIndex(info, codepoint), padding, onedge_value, pixel_dist_scale, width, height, xoff, yoff); +} + +STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata) +{ + STBTT_free(bitmap, userdata); +} + +////////////////////////////////////////////////////////////////////////////// +// +// font name matching -- recommended not to use this +// + +// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string +static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2) +{ + stbtt_int32 i=0; + + // convert utf16 to utf8 and compare the results while converting + while (len2) { + stbtt_uint16 ch = s2[0]*256 + s2[1]; + if (ch < 0x80) { + if (i >= len1) return -1; + if (s1[i++] != ch) return -1; + } else if (ch < 0x800) { + if (i+1 >= len1) return -1; + if (s1[i++] != 0xc0 + (ch >> 6)) return -1; + if (s1[i++] != 0x80 + (ch & 0x3f)) return -1; + } else if (ch >= 0xd800 && ch < 0xdc00) { + stbtt_uint32 c; + stbtt_uint16 ch2 = s2[2]*256 + s2[3]; + if (i+3 >= len1) return -1; + c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000; + if (s1[i++] != 0xf0 + (c >> 18)) return -1; + if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1; + s2 += 2; // plus another 2 below + len2 -= 2; + } else if (ch >= 0xdc00 && ch < 0xe000) { + return -1; + } else { + if (i+2 >= len1) return -1; + if (s1[i++] != 0xe0 + (ch >> 12)) return -1; + if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1; + } + s2 += 2; + len2 -= 2; + } + return i; +} + +static int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2) +{ + return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2); +} + +// returns results in whatever encoding you request... but note that 2-byte encodings +// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare +STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID) +{ + stbtt_int32 i,count,stringOffset; + stbtt_uint8 *fc = font->data; + stbtt_uint32 offset = font->fontstart; + stbtt_uint32 nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return NULL; + + count = ttUSHORT(fc+nm+2); + stringOffset = nm + ttUSHORT(fc+nm+4); + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2) + && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) { + *length = ttUSHORT(fc+loc+8); + return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10)); + } + } + return NULL; +} + +static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id) +{ + stbtt_int32 i; + stbtt_int32 count = ttUSHORT(fc+nm+2); + stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4); + + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + stbtt_int32 id = ttUSHORT(fc+loc+6); + if (id == target_id) { + // find the encoding + stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4); + + // is this a Unicode encoding? + if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) { + stbtt_int32 slen = ttUSHORT(fc+loc+8); + stbtt_int32 off = ttUSHORT(fc+loc+10); + + // check if there's a prefix match + stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen); + if (matchlen >= 0) { + // check for target_id+1 immediately following, with same encoding & language + if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) { + slen = ttUSHORT(fc+loc+12+8); + off = ttUSHORT(fc+loc+12+10); + if (slen == 0) { + if (matchlen == nlen) + return 1; + } else if (matchlen < nlen && name[matchlen] == ' ') { + ++matchlen; + if (stbtt_CompareUTF8toUTF16_bigendian_internal((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) + return 1; + } + } else { + // if nothing immediately following + if (matchlen == nlen) + return 1; + } + } + } + + // @TODO handle other encodings + } + } + return 0; +} + +static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags) +{ + stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name); + stbtt_uint32 nm,hd; + if (!stbtt__isfont(fc+offset)) return 0; + + // check italics/bold/underline flags in macStyle... + if (flags) { + hd = stbtt__find_table(fc, offset, "head"); + if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0; + } + + nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return 0; + + if (flags) { + // if we checked the macStyle flags, then just check the family and ignore the subfamily + if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } else { + if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } + + return 0; +} + +static int stbtt_FindMatchingFont_internal(unsigned char *font_collection, char *name_utf8, stbtt_int32 flags) +{ + stbtt_int32 i; + for (i=0;;++i) { + stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i); + if (off < 0) return off; + if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags)) + return off; + } +} + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif + +STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, + float pixel_height, unsigned char *pixels, int pw, int ph, + int first_char, int num_chars, stbtt_bakedchar *chardata) +{ + return stbtt_BakeFontBitmap_internal((unsigned char *) data, offset, pixel_height, pixels, pw, ph, first_char, num_chars, chardata); +} + +STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index) +{ + return stbtt_GetFontOffsetForIndex_internal((unsigned char *) data, index); +} + +STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data) +{ + return stbtt_GetNumberOfFonts_internal((unsigned char *) data); +} + +STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset) +{ + return stbtt_InitFont_internal(info, (unsigned char *) data, offset); +} + +STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags) +{ + return stbtt_FindMatchingFont_internal((unsigned char *) fontdata, (char *) name, flags); +} + +STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) +{ + return stbtt_CompareUTF8toUTF16_bigendian_internal((char *) s1, len1, (char *) s2, len2); +} + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic pop +#endif + +#endif // STB_TRUETYPE_IMPLEMENTATION + + +// FULL VERSION HISTORY +// +// 1.19 (2018-02-11) OpenType GPOS kerning (horizontal only), STBTT_fmod +// 1.18 (2018-01-29) add missing function +// 1.17 (2017-07-23) make more arguments const; doc fix +// 1.16 (2017-07-12) SDF support +// 1.15 (2017-03-03) make more arguments const +// 1.14 (2017-01-16) num-fonts-in-TTC function +// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts +// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual +// 1.11 (2016-04-02) fix unused-variable warning +// 1.10 (2016-04-02) allow user-defined fabs() replacement +// fix memory leak if fontsize=0.0 +// fix warning from duplicate typedef +// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use alloc userdata for PackFontRanges +// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges +// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; +// allow PackFontRanges to pack and render in separate phases; +// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); +// fixed an assert() bug in the new rasterizer +// replace assert() with STBTT_assert() in new rasterizer +// 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine) +// also more precise AA rasterizer, except if shapes overlap +// remove need for STBTT_sort +// 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC +// 1.04 (2015-04-15) typo in example +// 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes +// 1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++ +// 1.01 (2014-12-08) fix subpixel position when oversampling to exactly match +// non-oversampled; STBTT_POINT_SIZE for packed case only +// 1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling +// 0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg) +// 0.9 (2014-08-07) support certain mac/iOS fonts without an MS platformID +// 0.8b (2014-07-07) fix a warning +// 0.8 (2014-05-25) fix a few more warnings +// 0.7 (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back +// 0.6c (2012-07-24) improve documentation +// 0.6b (2012-07-20) fix a few more warnings +// 0.6 (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels, +// stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty +// 0.5 (2011-12-09) bugfixes: +// subpixel glyph renderer computed wrong bounding box +// first vertex of shape can be off-curve (FreeSans) +// 0.4b (2011-12-03) fixed an error in the font baking example +// 0.4 (2011-12-01) kerning, subpixel rendering (tor) +// bugfixes for: +// codepoint-to-glyph conversion using table fmt=12 +// codepoint-to-glyph conversion using table fmt=4 +// stbtt_GetBakedQuad with non-square texture (Zer) +// updated Hello World! sample to use kerning and subpixel +// fixed some warnings +// 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM) +// userdata, malloc-from-userdata, non-zero fill (stb) +// 0.2 (2009-03-11) Fix unsigned/signed char warnings +// 0.1 (2009-03-09) First public release +// + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/v_windows/v/old/thirdparty/ios/ios.m b/v_windows/v/old/thirdparty/ios/ios.m new file mode 100644 index 0000000..801df16 --- /dev/null +++ b/v_windows/v/old/thirdparty/ios/ios.m @@ -0,0 +1,8 @@ +#import + +void WrappedNSLog(const char *message,...) { + va_list args; + va_start(args, message); + NSLog(@"%@",[[NSString alloc] initWithFormat:[NSString stringWithUTF8String:message] arguments:args]); + va_end(args); +} diff --git a/v_windows/v/old/thirdparty/libgc/amalgamation.txt b/v_windows/v/old/thirdparty/libgc/amalgamation.txt new file mode 100644 index 0000000..de25a3d --- /dev/null +++ b/v_windows/v/old/thirdparty/libgc/amalgamation.txt @@ -0,0 +1,7 @@ +The libgc source is distributed here as an amalgamation (https://sqlite.org/amalgamation.html). +This means that, rather than mirroring the entire bdwgc repo here, +[this script](https://gist.github.com/spaceface777/34d25420f2dc4953fb7864f44a211105) was used +to bundle all local includes together into a single C file, which is much easier to handle. +Furthermore, the script above was also used to minify (i.e. remove comments and whitespace in) +the garbage collector source. Together, these details help keep the V source distribution small, +can reduce compile times by 3%-15%, and can help C compilers generate more optimized code. diff --git a/v_windows/v/old/thirdparty/libgc/gc.c b/v_windows/v/old/thirdparty/libgc/gc.c new file mode 100644 index 0000000..2459097 --- /dev/null +++ b/v_windows/v/old/thirdparty/libgc/gc.c @@ -0,0 +1,30266 @@ +/* + * Copyright (c) 1994 by Xerox Corporation. All rights reserved. + * Copyright (c) 1996 by Silicon Graphics. All rights reserved. + * Copyright (c) 1998 by Fergus Henderson. All rights reserved. + * Copyright (c) 2000-2009 by Hewlett-Packard Development Company. + * All rights reserved. + * Copyright (c) 2009-2018 Ivan Maidanski + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#ifndef __cplusplus +#define GC_INNER STATIC +#define GC_EXTERN GC_INNER +#endif +#ifndef GC_DBG_MLC_H +#define GC_DBG_MLC_H +#ifndef GC_PRIVATE_H +#define GC_PRIVATE_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#if!defined(GC_BUILD)&&!defined(NOT_GCBUILD) +#define GC_BUILD +#endif +#if (defined(__linux__)||defined(__GLIBC__)||defined(__GNU__)||(defined(__CYGWIN__)&&(defined(GC_THREADS)||!defined(USE_MMAP))))&&!defined(_GNU_SOURCE) +#define _GNU_SOURCE 1 +#endif +#if defined(__INTERIX)&&!defined(_ALL_SOURCE) +#define _ALL_SOURCE 1 +#endif +#if (defined(DGUX)&&defined(GC_THREADS)||defined(DGUX386_THREADS)||defined(GC_DGUX386_THREADS))&&!defined(_USING_POSIX4A_DRAFT10) +#define _USING_POSIX4A_DRAFT10 1 +#endif +#if defined(__MINGW32__)&&!defined(__MINGW_EXCPT_DEFINE_PSDK)&&defined(__i386__)&&defined(GC_EXTERN) +#define __MINGW_EXCPT_DEFINE_PSDK 1 +#endif +#if defined(NO_DEBUGGING)&&!defined(GC_ASSERTIONS)&&!defined(NDEBUG) +#define NDEBUG 1 +#endif +#ifndef GC_H +#ifndef GC_H +#define GC_H +#if (defined(WIN64)&&!defined(_WIN64))&&defined(_MSC_VER) +#pragma message("Warning:Expecting _WIN64 for x64 targets!Notice the leading underscore!") +#endif +#if defined(GC_H) +#define GC_TMP_VERSION_MAJOR 8 +#define GC_TMP_VERSION_MINOR 1 +#define GC_TMP_VERSION_MICRO 0 +#ifdef GC_VERSION_MAJOR +#if GC_TMP_VERSION_MAJOR!=GC_VERSION_MAJOR||GC_TMP_VERSION_MINOR!=GC_VERSION_MINOR||GC_TMP_VERSION_MICRO!=GC_VERSION_MICRO +#error Inconsistent version info. Check README.md,include/gc_version.h and configure.ac. +#endif +#else +#define GC_VERSION_MAJOR GC_TMP_VERSION_MAJOR +#define GC_VERSION_MINOR GC_TMP_VERSION_MINOR +#define GC_VERSION_MICRO GC_TMP_VERSION_MICRO +#endif +#endif +#if defined(GC_H) +#if defined(__GNUC__)&&defined(__GNUC_MINOR__) +#define GC_GNUC_PREREQ(major,minor)((__GNUC__<<16)+__GNUC_MINOR__>=((major)<<16)+(minor)) +#else +#define GC_GNUC_PREREQ(major,minor)0 +#endif +#if defined(SOLARIS_THREADS)||defined(_SOLARIS_THREADS)||defined(_SOLARIS_PTHREADS)||defined(GC_SOLARIS_PTHREADS) +#ifndef GC_SOLARIS_THREADS +#define GC_SOLARIS_THREADS +#endif +#endif +#if defined(IRIX_THREADS) +#define GC_IRIX_THREADS +#endif +#if defined(DGUX_THREADS)&&!defined(GC_DGUX386_THREADS) +#define GC_DGUX386_THREADS +#endif +#if defined(AIX_THREADS) +#define GC_AIX_THREADS +#endif +#if defined(HPUX_THREADS) +#define GC_HPUX_THREADS +#endif +#if defined(OSF1_THREADS) +#define GC_OSF1_THREADS +#endif +#if defined(LINUX_THREADS) +#define GC_LINUX_THREADS +#endif +#if defined(WIN32_THREADS) +#define GC_WIN32_THREADS +#endif +#if defined(RTEMS_THREADS) +#define GC_RTEMS_PTHREADS +#endif +#if defined(USE_LD_WRAP) +#define GC_USE_LD_WRAP +#endif +#if defined(GC_WIN32_PTHREADS)&&!defined(GC_WIN32_THREADS) +#define GC_WIN32_THREADS +#endif +#if defined(GC_AIX_THREADS)||defined(GC_DARWIN_THREADS)||defined(GC_DGUX386_THREADS)||defined(GC_FREEBSD_THREADS)||defined(GC_HPUX_THREADS)||defined(GC_IRIX_THREADS)||defined(GC_LINUX_THREADS)||defined(GC_NETBSD_THREADS)||defined(GC_OPENBSD_THREADS)||defined(GC_OSF1_THREADS)||defined(GC_SOLARIS_THREADS)||defined(GC_WIN32_THREADS)||defined(GC_RTEMS_PTHREADS) +#ifndef GC_THREADS +#define GC_THREADS +#endif +#elif defined(GC_THREADS) +#if defined(__linux__) +#define GC_LINUX_THREADS +#elif defined(__OpenBSD__) +#define GC_OPENBSD_THREADS +#elif defined(_PA_RISC1_1)||defined(_PA_RISC2_0)||defined(hppa)||defined(__HPPA)||(defined(__ia64)&&defined(_HPUX_SOURCE)) +#define GC_HPUX_THREADS +#elif defined(__HAIKU__) +#define GC_HAIKU_THREADS +#elif defined(__DragonFly__)||defined(__FreeBSD_kernel__)||(defined(__FreeBSD__)&&!defined(SN_TARGET_ORBIS)) +#define GC_FREEBSD_THREADS +#elif defined(__NetBSD__) +#define GC_NETBSD_THREADS +#elif defined(__alpha)||defined(__alpha__) +#define GC_OSF1_THREADS +#elif (defined(mips)||defined(__mips)||defined(_mips))&&!(defined(nec_ews)||defined(_nec_ews)||defined(ultrix)||defined(__ultrix)) +#define GC_IRIX_THREADS +#elif defined(__sparc)||((defined(sun)||defined(__sun))&&(defined(i386)||defined(__i386__)||defined(__amd64)||defined(__amd64__))) +#define GC_SOLARIS_THREADS +#elif defined(__APPLE__)&&defined(__MACH__) +#define GC_DARWIN_THREADS +#endif +#if defined(DGUX)&&(defined(i386)||defined(__i386__)) +#define GC_DGUX386_THREADS +#endif +#if defined(_AIX) +#define GC_AIX_THREADS +#endif +#if (defined(_WIN32)||defined(_MSC_VER)||defined(__BORLANDC__)||defined(__CYGWIN32__)||defined(__CYGWIN__)||defined(__CEGCC__)||defined(_WIN32_WCE)||defined(__MINGW32__))&&!defined(GC_WIN32_THREADS) +#define GC_WIN32_THREADS +#endif +#if defined(__rtems__)&&(defined(i386)||defined(__i386__)) +#define GC_RTEMS_PTHREADS +#endif +#endif +#undef GC_PTHREADS +#if (!defined(GC_WIN32_THREADS)||defined(GC_WIN32_PTHREADS)||defined(__CYGWIN32__)||defined(__CYGWIN__))&&defined(GC_THREADS)&&!defined(NN_PLATFORM_CTR)&&!defined(NN_BUILD_TARGET_PLATFORM_NX) +#define GC_PTHREADS +#endif +#if!defined(_PTHREADS)&&defined(GC_NETBSD_THREADS) +#define _PTHREADS +#endif +#if defined(GC_DGUX386_THREADS)&&!defined(_POSIX4A_DRAFT10_SOURCE) +#define _POSIX4A_DRAFT10_SOURCE 1 +#endif +#if!defined(_REENTRANT)&&defined(GC_PTHREADS)&&!defined(GC_WIN32_THREADS) +#define _REENTRANT 1 +#endif +#define __GC +#if!defined(_WIN32_WCE)||defined(__GNUC__) +#include +#if defined(__MINGW32__)&&!defined(_WIN32_WCE) +#include +#endif +#else +#include +#ifndef _PTRDIFF_T_DEFINED +#define _PTRDIFF_T_DEFINED +typedef long ptrdiff_t; +#endif +#endif +#if!defined(GC_NOT_DLL)&&!defined(GC_DLL)&&((defined(_DLL)&&!defined(__GNUC__))||(defined(DLL_EXPORT)&&defined(GC_BUILD))) +#define GC_DLL +#endif +#if defined(GC_DLL)&&!defined(GC_API) +#if defined(__CEGCC__) +#if defined(GC_BUILD) +#define GC_API __declspec(dllexport) +#else +#define GC_API __declspec(dllimport) +#endif +#elif defined(__MINGW32__) +#if defined(__cplusplus)&&defined(GC_BUILD) +#define GC_API extern __declspec(dllexport) +#elif defined(GC_BUILD)||defined(__MINGW32_DELAY_LOAD__) +#define GC_API __declspec(dllexport) +#else +#define GC_API extern __declspec(dllimport) +#endif +#elif defined(_MSC_VER)||defined(__DMC__)||defined(__BORLANDC__)||defined(__CYGWIN__) +#ifdef GC_BUILD +#define GC_API extern __declspec(dllexport) +#else +#define GC_API __declspec(dllimport) +#endif +#elif defined(__WATCOMC__) +#ifdef GC_BUILD +#define GC_API extern __declspec(dllexport) +#else +#define GC_API extern __declspec(dllimport) +#endif +#elif defined(__SYMBIAN32__) +#ifdef GC_BUILD +#define GC_API extern EXPORT_C +#else +#define GC_API extern IMPORT_C +#endif +#elif defined(__GNUC__) +#if defined(GC_BUILD)&&!defined(GC_NO_VISIBILITY)&&(GC_GNUC_PREREQ(4,0)||defined(GC_VISIBILITY_HIDDEN_SET)) +#define GC_API extern __attribute__((__visibility__("default"))) +#endif +#endif +#endif +#ifndef GC_API +#define GC_API extern +#endif +#ifndef GC_CALL +#define GC_CALL +#endif +#ifndef GC_CALLBACK +#define GC_CALLBACK GC_CALL +#endif +#ifndef GC_ATTR_MALLOC +#ifdef GC_OOM_FUNC_RETURNS_ALIAS +#define GC_ATTR_MALLOC +#elif GC_GNUC_PREREQ(3,1) +#define GC_ATTR_MALLOC __attribute__((__malloc__)) +#elif defined(_MSC_VER)&&(_MSC_VER>=1900)&&!defined(__EDG__) +#define GC_ATTR_MALLOC __declspec(allocator)__declspec(noalias)__declspec(restrict) +#elif defined(_MSC_VER)&&_MSC_VER>=1400 +#define GC_ATTR_MALLOC __declspec(noalias)__declspec(restrict) +#else +#define GC_ATTR_MALLOC +#endif +#endif +#ifndef GC_ATTR_ALLOC_SIZE +#undef GC_ATTR_CALLOC_SIZE +#ifdef __clang__ +#if __has_attribute(__alloc_size__) +#define GC_ATTR_ALLOC_SIZE(argnum)__attribute__((__alloc_size__(argnum))) +#define GC_ATTR_CALLOC_SIZE(n,s)__attribute__((__alloc_size__(n,s))) +#else +#define GC_ATTR_ALLOC_SIZE(argnum) +#endif +#elif GC_GNUC_PREREQ(4,3)&&!defined(__ICC) +#define GC_ATTR_ALLOC_SIZE(argnum)__attribute__((__alloc_size__(argnum))) +#define GC_ATTR_CALLOC_SIZE(n,s)__attribute__((__alloc_size__(n,s))) +#else +#define GC_ATTR_ALLOC_SIZE(argnum) +#endif +#endif +#ifndef GC_ATTR_CALLOC_SIZE +#define GC_ATTR_CALLOC_SIZE(n,s) +#endif +#ifndef GC_ATTR_NONNULL +#if GC_GNUC_PREREQ(4,0) +#define GC_ATTR_NONNULL(argnum)__attribute__((__nonnull__(argnum))) +#else +#define GC_ATTR_NONNULL(argnum) +#endif +#endif +#ifndef GC_ATTR_CONST +#if GC_GNUC_PREREQ(4,0) +#define GC_ATTR_CONST __attribute__((__const__)) +#else +#define GC_ATTR_CONST +#endif +#endif +#ifndef GC_ATTR_DEPRECATED +#ifdef GC_BUILD +#undef GC_ATTR_DEPRECATED +#define GC_ATTR_DEPRECATED +#elif GC_GNUC_PREREQ(4,0) +#define GC_ATTR_DEPRECATED __attribute__((__deprecated__)) +#elif defined(_MSC_VER)&&_MSC_VER>=1200 +#define GC_ATTR_DEPRECATED __declspec(deprecated) +#else +#define GC_ATTR_DEPRECATED +#endif +#endif +#if defined(__sgi)&&!defined(__GNUC__)&&_COMPILER_VERSION>=720 +#define GC_ADD_CALLER +#define GC_RETURN_ADDR (GC_word)__return_address +#endif +#if defined(__linux__)||defined(__GLIBC__) +#if!defined(__native_client__) +#include +#endif +#if (__GLIBC__==2&&__GLIBC_MINOR__>=1||__GLIBC__ > 2)&&!defined(__ia64__)&&!defined(GC_MISSING_EXECINFO_H)&&!defined(GC_HAVE_BUILTIN_BACKTRACE) +#define GC_HAVE_BUILTIN_BACKTRACE +#endif +#if defined(__i386__)||defined(__amd64__)||defined(__x86_64__) +#define GC_CAN_SAVE_CALL_STACKS +#endif +#endif +#if defined(_MSC_VER)&&_MSC_VER>=1200&&!defined(_AMD64_)&&!defined(_M_X64)&&!defined(_WIN32_WCE)&&!defined(GC_HAVE_NO_BUILTIN_BACKTRACE)&&!defined(GC_HAVE_BUILTIN_BACKTRACE) +#define GC_HAVE_BUILTIN_BACKTRACE +#endif +#if defined(GC_HAVE_BUILTIN_BACKTRACE)&&!defined(GC_CAN_SAVE_CALL_STACKS) +#define GC_CAN_SAVE_CALL_STACKS +#endif +#if defined(__sparc__) +#define GC_CAN_SAVE_CALL_STACKS +#endif +#if (defined(__linux__)||defined(__DragonFly__)||defined(__FreeBSD__)||defined(__FreeBSD_kernel__)||defined(__HAIKU__)||defined(__NetBSD__)||defined(__OpenBSD__)||defined(HOST_ANDROID)||defined(__ANDROID__))&&!defined(GC_CAN_SAVE_CALL_STACKS) +#define GC_ADD_CALLER +#if GC_GNUC_PREREQ(2,95) +#define GC_RETURN_ADDR (GC_word)__builtin_return_address(0) +#if GC_GNUC_PREREQ(4,0)&&(defined(__i386__)||defined(__amd64__)||defined(__x86_64__)) +#define GC_HAVE_RETURN_ADDR_PARENT +#define GC_RETURN_ADDR_PARENT (GC_word)__builtin_extract_return_addr(__builtin_return_address(1)) +#endif +#else +#define GC_RETURN_ADDR 0 +#endif +#endif +#ifdef GC_PTHREADS +#if (defined(GC_DARWIN_THREADS)||defined(GC_WIN32_PTHREADS)||defined(__native_client__)||defined(GC_RTEMS_PTHREADS))&&!defined(GC_NO_DLOPEN) +#define GC_NO_DLOPEN +#endif +#if (defined(GC_DARWIN_THREADS)||defined(GC_WIN32_PTHREADS)||defined(GC_OPENBSD_THREADS)||defined(__native_client__))&&!defined(GC_NO_PTHREAD_SIGMASK) +#define GC_NO_PTHREAD_SIGMASK +#endif +#if defined(__native_client__) +#ifndef GC_PTHREAD_CREATE_CONST +#define GC_PTHREAD_CREATE_CONST +#endif +#ifndef GC_HAVE_PTHREAD_EXIT +#define GC_HAVE_PTHREAD_EXIT +#define GC_PTHREAD_EXIT_ATTRIBUTE +#endif +#endif +#if!defined(GC_HAVE_PTHREAD_EXIT)&&!defined(HOST_ANDROID)&&!defined(__ANDROID__)&&(defined(GC_LINUX_THREADS)||defined(GC_SOLARIS_THREADS)) +#define GC_HAVE_PTHREAD_EXIT +#if GC_GNUC_PREREQ(2,7) +#define GC_PTHREAD_EXIT_ATTRIBUTE __attribute__((__noreturn__)) +#elif defined(__NORETURN) +#define GC_PTHREAD_EXIT_ATTRIBUTE __NORETURN +#else +#define GC_PTHREAD_EXIT_ATTRIBUTE +#endif +#endif +#if (!defined(GC_HAVE_PTHREAD_EXIT)||defined(__native_client__))&&!defined(GC_NO_PTHREAD_CANCEL) +#define GC_NO_PTHREAD_CANCEL +#endif +#endif +#ifdef __cplusplus +#ifndef GC_ATTR_EXPLICIT +#if __cplusplus>=201103L&&!defined(__clang__)||_MSVC_LANG>=201103L||defined(CPPCHECK) +#define GC_ATTR_EXPLICIT explicit +#else +#define GC_ATTR_EXPLICIT +#endif +#endif +#ifndef GC_NOEXCEPT +#if defined(__DMC__)||(defined(__BORLANDC__)&&(defined(_RWSTD_NO_EXCEPTIONS)||defined(_RWSTD_NO_EX_SPEC)))||(defined(_MSC_VER)&&defined(_HAS_EXCEPTIONS)&&!_HAS_EXCEPTIONS)||(defined(__WATCOMC__)&&!defined(_CPPUNWIND)) +#define GC_NOEXCEPT +#ifndef GC_NEW_ABORTS_ON_OOM +#define GC_NEW_ABORTS_ON_OOM +#endif +#elif __cplusplus>=201103L||_MSVC_LANG>=201103L +#define GC_NOEXCEPT noexcept +#else +#define GC_NOEXCEPT throw() +#endif +#endif +#endif +#endif +#ifdef __cplusplus +extern "C" { +#endif +typedef void*GC_PTR; +#ifdef _WIN64 +#if defined(__int64)&&!defined(CPPCHECK) +typedef unsigned __int64 GC_word; +typedef __int64 GC_signed_word; +#else +typedef unsigned long long GC_word; +typedef long long GC_signed_word; +#endif +#else +typedef unsigned long GC_word; +typedef long GC_signed_word; +#endif +GC_API unsigned GC_CALL GC_get_version(void); +GC_API GC_ATTR_DEPRECATED GC_word GC_gc_no; +GC_API GC_word GC_CALL GC_get_gc_no(void); +#ifdef GC_THREADS +GC_API GC_ATTR_DEPRECATED int GC_parallel; +GC_API int GC_CALL GC_get_parallel(void); +GC_API void GC_CALL GC_set_markers_count(unsigned); +#endif +typedef void*(GC_CALLBACK*GC_oom_func)(size_t); +GC_API GC_ATTR_DEPRECATED GC_oom_func GC_oom_fn; +GC_API void GC_CALL GC_set_oom_fn(GC_oom_func)GC_ATTR_NONNULL(1); +GC_API GC_oom_func GC_CALL GC_get_oom_fn(void); +typedef void (GC_CALLBACK*GC_on_heap_resize_proc)(GC_word); +GC_API GC_ATTR_DEPRECATED GC_on_heap_resize_proc GC_on_heap_resize; +GC_API void GC_CALL GC_set_on_heap_resize(GC_on_heap_resize_proc); +GC_API GC_on_heap_resize_proc GC_CALL GC_get_on_heap_resize(void); +typedef enum { +GC_EVENT_START, +GC_EVENT_MARK_START, +GC_EVENT_MARK_END, +GC_EVENT_RECLAIM_START, +GC_EVENT_RECLAIM_END, +GC_EVENT_END, +GC_EVENT_PRE_STOP_WORLD, +GC_EVENT_POST_STOP_WORLD, +GC_EVENT_PRE_START_WORLD, +GC_EVENT_POST_START_WORLD, +GC_EVENT_THREAD_SUSPENDED, +GC_EVENT_THREAD_UNSUSPENDED +} GC_EventType; +typedef void (GC_CALLBACK*GC_on_collection_event_proc)(GC_EventType); +GC_API void GC_CALL GC_set_on_collection_event(GC_on_collection_event_proc); +GC_API GC_on_collection_event_proc GC_CALL GC_get_on_collection_event(void); +#if defined(GC_THREADS)||(defined(GC_BUILD)&&defined(NN_PLATFORM_CTR)) +typedef void (GC_CALLBACK*GC_on_thread_event_proc)(GC_EventType, +void*); +GC_API void GC_CALL GC_set_on_thread_event(GC_on_thread_event_proc); +GC_API GC_on_thread_event_proc GC_CALL GC_get_on_thread_event(void); +#endif +GC_API GC_ATTR_DEPRECATED int GC_find_leak; +GC_API void GC_CALL GC_set_find_leak(int); +GC_API int GC_CALL GC_get_find_leak(void); +GC_API GC_ATTR_DEPRECATED int GC_all_interior_pointers; +GC_API void GC_CALL GC_set_all_interior_pointers(int); +GC_API int GC_CALL GC_get_all_interior_pointers(void); +GC_API GC_ATTR_DEPRECATED int GC_finalize_on_demand; +GC_API void GC_CALL GC_set_finalize_on_demand(int); +GC_API int GC_CALL GC_get_finalize_on_demand(void); +GC_API GC_ATTR_DEPRECATED int GC_java_finalization; +GC_API void GC_CALL GC_set_java_finalization(int); +GC_API int GC_CALL GC_get_java_finalization(void); +typedef void (GC_CALLBACK*GC_finalizer_notifier_proc)(void); +GC_API GC_ATTR_DEPRECATED GC_finalizer_notifier_proc GC_finalizer_notifier; +GC_API void GC_CALL GC_set_finalizer_notifier(GC_finalizer_notifier_proc); +GC_API GC_finalizer_notifier_proc GC_CALL GC_get_finalizer_notifier(void); +GC_API +#ifndef GC_DONT_GC +GC_ATTR_DEPRECATED +#endif +int GC_dont_gc; +GC_API GC_ATTR_DEPRECATED int GC_dont_expand; +GC_API void GC_CALL GC_set_dont_expand(int); +GC_API int GC_CALL GC_get_dont_expand(void); +GC_API GC_ATTR_DEPRECATED int GC_use_entire_heap; +GC_API GC_ATTR_DEPRECATED int GC_full_freq; +GC_API void GC_CALL GC_set_full_freq(int); +GC_API int GC_CALL GC_get_full_freq(void); +GC_API GC_ATTR_DEPRECATED GC_word GC_non_gc_bytes; +GC_API void GC_CALL GC_set_non_gc_bytes(GC_word); +GC_API GC_word GC_CALL GC_get_non_gc_bytes(void); +GC_API GC_ATTR_DEPRECATED int GC_no_dls; +GC_API void GC_CALL GC_set_no_dls(int); +GC_API int GC_CALL GC_get_no_dls(void); +GC_API GC_ATTR_DEPRECATED GC_word GC_free_space_divisor; +GC_API void GC_CALL GC_set_free_space_divisor(GC_word); +GC_API GC_word GC_CALL GC_get_free_space_divisor(void); +GC_API GC_ATTR_DEPRECATED GC_word GC_max_retries; +GC_API void GC_CALL GC_set_max_retries(GC_word); +GC_API GC_word GC_CALL GC_get_max_retries(void); +GC_API GC_ATTR_DEPRECATED char*GC_stackbottom; +GC_API GC_ATTR_DEPRECATED int GC_dont_precollect; +GC_API void GC_CALL GC_set_dont_precollect(int); +GC_API int GC_CALL GC_get_dont_precollect(void); +GC_API GC_ATTR_DEPRECATED unsigned long GC_time_limit; +#define GC_TIME_UNLIMITED 999999 +GC_API void GC_CALL GC_set_time_limit(unsigned long); +GC_API unsigned long GC_CALL GC_get_time_limit(void); +struct GC_timeval_s { +unsigned long tv_ms; +unsigned long tv_nsec; +}; +GC_API void GC_CALL GC_set_time_limit_tv(struct GC_timeval_s); +GC_API struct GC_timeval_s GC_CALL GC_get_time_limit_tv(void); +GC_API void GC_CALL GC_set_allocd_bytes_per_finalizer(GC_word); +GC_API GC_word GC_CALL GC_get_allocd_bytes_per_finalizer(void); +GC_API void GC_CALL GC_start_performance_measurement(void); +GC_API unsigned long GC_CALL GC_get_full_gc_total_time(void); +GC_API void GC_CALL GC_set_pages_executable(int); +GC_API int GC_CALL GC_get_pages_executable(void); +GC_API void GC_CALL GC_set_min_bytes_allocd(size_t); +GC_API size_t GC_CALL GC_get_min_bytes_allocd(void); +GC_API void GC_CALL GC_set_rate(int); +GC_API int GC_CALL GC_get_rate(void); +GC_API void GC_CALL GC_set_max_prior_attempts(int); +GC_API int GC_CALL GC_get_max_prior_attempts(void); +GC_API void GC_CALL GC_set_handle_fork(int); +GC_API void GC_CALL GC_atfork_prepare(void); +GC_API void GC_CALL GC_atfork_parent(void); +GC_API void GC_CALL GC_atfork_child(void); +GC_API void GC_CALL GC_init(void); +GC_API int GC_CALL GC_is_init_called(void); +GC_API void GC_CALL GC_deinit(void); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL +GC_malloc(size_t); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL +GC_malloc_atomic(size_t); +GC_API GC_ATTR_MALLOC char*GC_CALL GC_strdup(const char*); +GC_API GC_ATTR_MALLOC char*GC_CALL +GC_strndup(const char*,size_t)GC_ATTR_NONNULL(1); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL +GC_malloc_uncollectable(size_t); +GC_API GC_ATTR_DEPRECATED void*GC_CALL GC_malloc_stubborn(size_t); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(2)void*GC_CALL +GC_memalign(size_t,size_t); +GC_API int GC_CALL GC_posix_memalign(void**,size_t, +size_t)GC_ATTR_NONNULL(1); +GC_API void GC_CALL GC_free(void*); +#define GC_MALLOC_STUBBORN(sz)GC_MALLOC(sz) +#define GC_NEW_STUBBORN(t)GC_NEW(t) +#define GC_CHANGE_STUBBORN(p)GC_change_stubborn(p) +GC_API GC_ATTR_DEPRECATED void GC_CALL GC_change_stubborn(const void*); +GC_API void GC_CALL GC_end_stubborn_change(const void*)GC_ATTR_NONNULL(1); +GC_API void*GC_CALL GC_base(void*); +GC_API int GC_CALL GC_is_heap_ptr(const void*); +GC_API size_t GC_CALL GC_size(const void*)GC_ATTR_NONNULL(1); +GC_API void*GC_CALL GC_realloc(void*, +size_t) +GC_ATTR_ALLOC_SIZE(2); +GC_API int GC_CALL GC_expand_hp(size_t); +GC_API void GC_CALL GC_set_max_heap_size(GC_word); +GC_API void GC_CALL GC_exclude_static_roots(void*, +void*); +GC_API void GC_CALL GC_clear_exclusion_table(void); +GC_API void GC_CALL GC_clear_roots(void); +GC_API void GC_CALL GC_add_roots(void*, +void*); +GC_API void GC_CALL GC_remove_roots(void*, +void*); +GC_API void GC_CALL GC_register_displacement(size_t); +GC_API void GC_CALL GC_debug_register_displacement(size_t); +GC_API void GC_CALL GC_gcollect(void); +GC_API void GC_CALL GC_gcollect_and_unmap(void); +typedef int (GC_CALLBACK*GC_stop_func)(void); +GC_API int GC_CALL GC_try_to_collect(GC_stop_func) +GC_ATTR_NONNULL(1); +GC_API void GC_CALL GC_set_stop_func(GC_stop_func) +GC_ATTR_NONNULL(1); +GC_API GC_stop_func GC_CALL GC_get_stop_func(void); +GC_API size_t GC_CALL GC_get_heap_size(void); +GC_API size_t GC_CALL GC_get_free_bytes(void); +GC_API size_t GC_CALL GC_get_unmapped_bytes(void); +GC_API size_t GC_CALL GC_get_bytes_since_gc(void); +GC_API size_t GC_CALL GC_get_expl_freed_bytes_since_gc(void); +GC_API size_t GC_CALL GC_get_total_bytes(void); +GC_API void GC_CALL GC_get_heap_usage_safe(GC_word*, +GC_word*, +GC_word*, +GC_word*, +GC_word*); +struct GC_prof_stats_s { +GC_word heapsize_full; +GC_word free_bytes_full; +GC_word unmapped_bytes; +GC_word bytes_allocd_since_gc; +GC_word allocd_bytes_before_gc; +GC_word non_gc_bytes; +GC_word gc_no; +GC_word markers_m1; +GC_word bytes_reclaimed_since_gc; +GC_word reclaimed_bytes_before_gc; +GC_word expl_freed_bytes_since_gc; +}; +GC_API size_t GC_CALL GC_get_prof_stats(struct GC_prof_stats_s*, +size_t); +#ifdef GC_THREADS +GC_API size_t GC_CALL GC_get_prof_stats_unsafe(struct GC_prof_stats_s*, +size_t); +#endif +GC_API size_t GC_CALL GC_get_size_map_at(int i); +GC_API size_t GC_CALL GC_get_memory_use(void); +GC_API void GC_CALL GC_disable(void); +GC_API int GC_CALL GC_is_disabled(void); +GC_API void GC_CALL GC_enable(void); +GC_API void GC_CALL GC_set_manual_vdb_allowed(int); +GC_API int GC_CALL GC_get_manual_vdb_allowed(void); +GC_API void GC_CALL GC_enable_incremental(void); +GC_API int GC_CALL GC_is_incremental_mode(void); +#define GC_PROTECTS_POINTER_HEAP 1 +#define GC_PROTECTS_PTRFREE_HEAP 2 +#define GC_PROTECTS_STATIC_DATA 4 +#define GC_PROTECTS_STACK 8 +#define GC_PROTECTS_NONE 0 +GC_API int GC_CALL GC_incremental_protection_needs(void); +GC_API int GC_CALL GC_collect_a_little(void); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL +GC_malloc_ignore_off_page(size_t); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL +GC_malloc_atomic_ignore_off_page(size_t); +#ifdef GC_ADD_CALLER +#define GC_EXTRAS GC_RETURN_ADDR,__FILE__,__LINE__ +#define GC_EXTRA_PARAMS GC_word ra,const char*s,int i +#else +#define GC_EXTRAS __FILE__,__LINE__ +#define GC_EXTRA_PARAMS const char*s,int i +#endif +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL +GC_malloc_atomic_uncollectable(size_t); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL +GC_debug_malloc_atomic_uncollectable(size_t,GC_EXTRA_PARAMS); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL +GC_debug_malloc(size_t,GC_EXTRA_PARAMS); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL +GC_debug_malloc_atomic(size_t,GC_EXTRA_PARAMS); +GC_API GC_ATTR_MALLOC char*GC_CALL +GC_debug_strdup(const char*,GC_EXTRA_PARAMS); +GC_API GC_ATTR_MALLOC char*GC_CALL +GC_debug_strndup(const char*,size_t,GC_EXTRA_PARAMS) +GC_ATTR_NONNULL(1); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL +GC_debug_malloc_uncollectable(size_t, +GC_EXTRA_PARAMS); +GC_API GC_ATTR_DEPRECATED void*GC_CALL +GC_debug_malloc_stubborn(size_t,GC_EXTRA_PARAMS); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL +GC_debug_malloc_ignore_off_page(size_t, +GC_EXTRA_PARAMS); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL +GC_debug_malloc_atomic_ignore_off_page(size_t, +GC_EXTRA_PARAMS); +GC_API void GC_CALL GC_debug_free(void*); +GC_API void*GC_CALL GC_debug_realloc(void*, +size_t,GC_EXTRA_PARAMS) +GC_ATTR_ALLOC_SIZE(2); +GC_API GC_ATTR_DEPRECATED void GC_CALL GC_debug_change_stubborn(const void*); +GC_API void GC_CALL GC_debug_end_stubborn_change(const void*) +GC_ATTR_NONNULL(1); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL +GC_debug_malloc_replacement(size_t); +GC_API GC_ATTR_ALLOC_SIZE(2)void*GC_CALL +GC_debug_realloc_replacement(void*, +size_t); +#ifdef GC_DEBUG_REPLACEMENT +#define GC_MALLOC(sz)GC_debug_malloc_replacement(sz) +#define GC_REALLOC(old,sz)GC_debug_realloc_replacement(old,sz) +#elif defined(GC_DEBUG) +#define GC_MALLOC(sz)GC_debug_malloc(sz,GC_EXTRAS) +#define GC_REALLOC(old,sz)GC_debug_realloc(old,sz,GC_EXTRAS) +#else +#define GC_MALLOC(sz)GC_malloc(sz) +#define GC_REALLOC(old,sz)GC_realloc(old,sz) +#endif +#ifdef GC_DEBUG +#define GC_MALLOC_ATOMIC(sz)GC_debug_malloc_atomic(sz,GC_EXTRAS) +#define GC_STRDUP(s)GC_debug_strdup(s,GC_EXTRAS) +#define GC_STRNDUP(s,sz)GC_debug_strndup(s,sz,GC_EXTRAS) +#define GC_MALLOC_ATOMIC_UNCOLLECTABLE(sz)GC_debug_malloc_atomic_uncollectable(sz,GC_EXTRAS) +#define GC_MALLOC_UNCOLLECTABLE(sz)GC_debug_malloc_uncollectable(sz,GC_EXTRAS) +#define GC_MALLOC_IGNORE_OFF_PAGE(sz)GC_debug_malloc_ignore_off_page(sz,GC_EXTRAS) +#define GC_MALLOC_ATOMIC_IGNORE_OFF_PAGE(sz)GC_debug_malloc_atomic_ignore_off_page(sz,GC_EXTRAS) +#define GC_FREE(p)GC_debug_free(p) +#define GC_REGISTER_FINALIZER(p,f,d,of,od)GC_debug_register_finalizer(p,f,d,of,od) +#define GC_REGISTER_FINALIZER_IGNORE_SELF(p,f,d,of,od)GC_debug_register_finalizer_ignore_self(p,f,d,of,od) +#define GC_REGISTER_FINALIZER_NO_ORDER(p,f,d,of,od)GC_debug_register_finalizer_no_order(p,f,d,of,od) +#define GC_REGISTER_FINALIZER_UNREACHABLE(p,f,d,of,od)GC_debug_register_finalizer_unreachable(p,f,d,of,od) +#define GC_END_STUBBORN_CHANGE(p)GC_debug_end_stubborn_change(p) +#define GC_PTR_STORE_AND_DIRTY(p,q)GC_debug_ptr_store_and_dirty(p,q) +#define GC_GENERAL_REGISTER_DISAPPEARING_LINK(link,obj)GC_general_register_disappearing_link(link,GC_base(( void*)(obj))) +#define GC_REGISTER_LONG_LINK(link,obj)GC_register_long_link(link,GC_base(( void*)(obj))) +#define GC_REGISTER_DISPLACEMENT(n)GC_debug_register_displacement(n) +#else +#define GC_MALLOC_ATOMIC(sz)GC_malloc_atomic(sz) +#define GC_STRDUP(s)GC_strdup(s) +#define GC_STRNDUP(s,sz)GC_strndup(s,sz) +#define GC_MALLOC_ATOMIC_UNCOLLECTABLE(sz)GC_malloc_atomic_uncollectable(sz) +#define GC_MALLOC_UNCOLLECTABLE(sz)GC_malloc_uncollectable(sz) +#define GC_MALLOC_IGNORE_OFF_PAGE(sz)GC_malloc_ignore_off_page(sz) +#define GC_MALLOC_ATOMIC_IGNORE_OFF_PAGE(sz)GC_malloc_atomic_ignore_off_page(sz) +#define GC_FREE(p)GC_free(p) +#define GC_REGISTER_FINALIZER(p,f,d,of,od)GC_register_finalizer(p,f,d,of,od) +#define GC_REGISTER_FINALIZER_IGNORE_SELF(p,f,d,of,od)GC_register_finalizer_ignore_self(p,f,d,of,od) +#define GC_REGISTER_FINALIZER_NO_ORDER(p,f,d,of,od)GC_register_finalizer_no_order(p,f,d,of,od) +#define GC_REGISTER_FINALIZER_UNREACHABLE(p,f,d,of,od)GC_register_finalizer_unreachable(p,f,d,of,od) +#define GC_END_STUBBORN_CHANGE(p)GC_end_stubborn_change(p) +#define GC_PTR_STORE_AND_DIRTY(p,q)GC_ptr_store_and_dirty(p,q) +#define GC_GENERAL_REGISTER_DISAPPEARING_LINK(link,obj)GC_general_register_disappearing_link(link,obj) +#define GC_REGISTER_LONG_LINK(link,obj)GC_register_long_link(link,obj) +#define GC_REGISTER_DISPLACEMENT(n)GC_register_displacement(n) +#endif +#define GC_NEW(t)((t*)GC_MALLOC(sizeof(t))) +#define GC_NEW_ATOMIC(t)((t*)GC_MALLOC_ATOMIC(sizeof(t))) +#define GC_NEW_UNCOLLECTABLE(t)((t*)GC_MALLOC_UNCOLLECTABLE(sizeof(t))) +#ifdef GC_REQUIRE_WCSDUP +GC_API GC_ATTR_MALLOC wchar_t*GC_CALL +GC_wcsdup(const wchar_t*)GC_ATTR_NONNULL(1); +GC_API GC_ATTR_MALLOC wchar_t*GC_CALL +GC_debug_wcsdup(const wchar_t*,GC_EXTRA_PARAMS)GC_ATTR_NONNULL(1); +#ifdef GC_DEBUG +#define GC_WCSDUP(s)GC_debug_wcsdup(s,GC_EXTRAS) +#else +#define GC_WCSDUP(s)GC_wcsdup(s) +#endif +#endif +typedef void (GC_CALLBACK*GC_finalization_proc)(void*, +void*); +GC_API void GC_CALL GC_register_finalizer(void*, +GC_finalization_proc,void*, +GC_finalization_proc*,void**) +GC_ATTR_NONNULL(1); +GC_API void GC_CALL GC_debug_register_finalizer(void*, +GC_finalization_proc,void*, +GC_finalization_proc*,void**) +GC_ATTR_NONNULL(1); +GC_API void GC_CALL GC_register_finalizer_ignore_self(void*, +GC_finalization_proc,void*, +GC_finalization_proc*,void**) +GC_ATTR_NONNULL(1); +GC_API void GC_CALL GC_debug_register_finalizer_ignore_self(void*, +GC_finalization_proc,void*, +GC_finalization_proc*,void**) +GC_ATTR_NONNULL(1); +GC_API void GC_CALL GC_register_finalizer_no_order(void*, +GC_finalization_proc,void*, +GC_finalization_proc*,void**) +GC_ATTR_NONNULL(1); +GC_API void GC_CALL GC_debug_register_finalizer_no_order(void*, +GC_finalization_proc,void*, +GC_finalization_proc*,void**) +GC_ATTR_NONNULL(1); +GC_API void GC_CALL GC_register_finalizer_unreachable(void*, +GC_finalization_proc,void*, +GC_finalization_proc*,void**) +GC_ATTR_NONNULL(1); +GC_API void GC_CALL GC_debug_register_finalizer_unreachable(void*, +GC_finalization_proc,void*, +GC_finalization_proc*,void**) +GC_ATTR_NONNULL(1); +#define GC_NO_MEMORY 2 +GC_API int GC_CALL GC_register_disappearing_link(void**) +GC_ATTR_NONNULL(1); +GC_API int GC_CALL GC_general_register_disappearing_link(void**, +const void*) +GC_ATTR_NONNULL(1)GC_ATTR_NONNULL(2); +GC_API int GC_CALL GC_move_disappearing_link(void**, +void**) +GC_ATTR_NONNULL(2); +GC_API int GC_CALL GC_unregister_disappearing_link(void**); +GC_API int GC_CALL GC_register_long_link(void**, +const void*) +GC_ATTR_NONNULL(1)GC_ATTR_NONNULL(2); +GC_API int GC_CALL GC_move_long_link(void**, +void**) +GC_ATTR_NONNULL(2); +GC_API int GC_CALL GC_unregister_long_link(void**); +typedef enum { +GC_TOGGLE_REF_DROP, +GC_TOGGLE_REF_STRONG, +GC_TOGGLE_REF_WEAK +} GC_ToggleRefStatus; +typedef GC_ToggleRefStatus (GC_CALLBACK*GC_toggleref_func)(void*); +GC_API void GC_CALL GC_set_toggleref_func(GC_toggleref_func); +GC_API GC_toggleref_func GC_CALL GC_get_toggleref_func(void); +GC_API int GC_CALL GC_toggleref_add(void*,int) +GC_ATTR_NONNULL(1); +typedef void (GC_CALLBACK*GC_await_finalize_proc)(void*); +GC_API void GC_CALL GC_set_await_finalize_proc(GC_await_finalize_proc); +GC_API GC_await_finalize_proc GC_CALL GC_get_await_finalize_proc(void); +GC_API int GC_CALL GC_should_invoke_finalizers(void); +GC_API int GC_CALL GC_invoke_finalizers(void); +#if defined(__GNUC__)&&!defined(__INTEL_COMPILER) +#define GC_reachable_here(ptr)__asm__ __volatile__(" "::"X"(ptr):"memory") +#else +GC_API void GC_CALL GC_noop1(GC_word); +#ifdef LINT2 +#define GC_reachable_here(ptr)GC_noop1(~(GC_word)(ptr)^(~(GC_word)0)) +#else +#define GC_reachable_here(ptr)GC_noop1((GC_word)(ptr)) +#endif +#endif +typedef void (GC_CALLBACK*GC_warn_proc)(char*, +GC_word); +GC_API void GC_CALL GC_set_warn_proc(GC_warn_proc)GC_ATTR_NONNULL(1); +GC_API GC_warn_proc GC_CALL GC_get_warn_proc(void); +GC_API void GC_CALLBACK GC_ignore_warn_proc(char*,GC_word); +GC_API void GC_CALL GC_set_log_fd(int); +typedef void (GC_CALLBACK*GC_abort_func)(const char*); +GC_API void GC_CALL GC_set_abort_func(GC_abort_func)GC_ATTR_NONNULL(1); +GC_API GC_abort_func GC_CALL GC_get_abort_func(void); +GC_API void GC_CALL GC_abort_on_oom(void); +typedef GC_word GC_hidden_pointer; +#define GC_HIDE_POINTER(p)(~(GC_hidden_pointer)(p)) +#define GC_REVEAL_POINTER(p)((void*)GC_HIDE_POINTER(p)) +#if defined(I_HIDE_POINTERS)||defined(GC_I_HIDE_POINTERS) +#define HIDE_POINTER(p)GC_HIDE_POINTER(p) +#define REVEAL_POINTER(p)GC_REVEAL_POINTER(p) +#endif +#ifdef GC_THREADS +GC_API void GC_CALL GC_alloc_lock(void); +GC_API void GC_CALL GC_alloc_unlock(void); +#else +#define GC_alloc_lock()(void)0 +#define GC_alloc_unlock()(void)0 +#endif +typedef void*(GC_CALLBACK*GC_fn_type)(void*); +GC_API void*GC_CALL GC_call_with_alloc_lock(GC_fn_type, +void*)GC_ATTR_NONNULL(1); +struct GC_stack_base { +void*mem_base; +#if defined(__ia64)||defined(__ia64__)||defined(_M_IA64) +void*reg_base; +#endif +}; +typedef void*(GC_CALLBACK*GC_stack_base_func)( +struct GC_stack_base*,void*); +GC_API void*GC_CALL GC_call_with_stack_base(GC_stack_base_func, +void*)GC_ATTR_NONNULL(1); +#define GC_SUCCESS 0 +#define GC_DUPLICATE 1 +#define GC_NO_THREADS 2 +#define GC_UNIMPLEMENTED 3 +#define GC_NOT_FOUND 4 +#if defined(GC_DARWIN_THREADS)||defined(GC_WIN32_THREADS) +GC_API void GC_CALL GC_use_threads_discovery(void); +#endif +#ifdef GC_THREADS +GC_API void GC_CALL GC_set_suspend_signal(int); +GC_API void GC_CALL GC_set_thr_restart_signal(int); +GC_API int GC_CALL GC_get_suspend_signal(void); +GC_API int GC_CALL GC_get_thr_restart_signal(void); +GC_API void GC_CALL GC_start_mark_threads(void); +GC_API void GC_CALL GC_allow_register_threads(void); +GC_API int GC_CALL GC_register_my_thread(const struct GC_stack_base*) +GC_ATTR_NONNULL(1); +GC_API int GC_CALL GC_thread_is_registered(void); +GC_API void GC_CALL GC_register_altstack(void*, +GC_word, +void*, +GC_word); +GC_API int GC_CALL GC_unregister_my_thread(void); +GC_API void GC_CALL GC_stop_world_external(void); +GC_API void GC_CALL GC_start_world_external(void); +#endif +GC_API void*GC_CALL GC_do_blocking(GC_fn_type, +void*)GC_ATTR_NONNULL(1); +GC_API void*GC_CALL GC_call_with_gc_active(GC_fn_type, +void*)GC_ATTR_NONNULL(1); +GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base*) +GC_ATTR_NONNULL(1); +GC_API void*GC_CALL GC_get_my_stackbottom(struct GC_stack_base*) +GC_ATTR_NONNULL(1); +GC_API void GC_CALL GC_set_stackbottom(void*, +const struct GC_stack_base*) +GC_ATTR_NONNULL(2); +GC_API void*GC_CALL GC_same_obj(void*,void*); +GC_API void*GC_CALL GC_pre_incr(void**,ptrdiff_t) +GC_ATTR_NONNULL(1); +GC_API void*GC_CALL GC_post_incr(void**,ptrdiff_t) +GC_ATTR_NONNULL(1); +GC_API void*GC_CALL GC_is_visible(void*); +GC_API void*GC_CALL GC_is_valid_displacement(void*); +GC_API void GC_CALL GC_dump(void); +GC_API void GC_CALL GC_dump_named(const char*); +GC_API void GC_CALL GC_dump_regions(void); +GC_API void GC_CALL GC_dump_finalization(void); +#if defined(GC_DEBUG)&&defined(__GNUC__) +#define GC_PTR_ADD3(x,n,type_of_result)((type_of_result)GC_same_obj((x)+(n),(x))) +#define GC_PRE_INCR3(x,n,type_of_result)((type_of_result)GC_pre_incr((void**)(&(x)),(n)*sizeof(*x))) +#define GC_POST_INCR3(x,n,type_of_result)((type_of_result)GC_post_incr((void**)(&(x)),(n)*sizeof(*x))) +#define GC_PTR_ADD(x,n)GC_PTR_ADD3(x,n,__typeof__(x)) +#define GC_PRE_INCR(x,n)GC_PRE_INCR3(x,n,__typeof__(x)) +#define GC_POST_INCR(x)GC_POST_INCR3(x,1,__typeof__(x)) +#define GC_POST_DECR(x)GC_POST_INCR3(x,-1,__typeof__(x)) +#else +#define GC_PTR_ADD(x,n)((x)+(n)) +#define GC_PRE_INCR(x,n)((x)+=(n)) +#define GC_POST_INCR(x)((x)++) +#define GC_POST_DECR(x)((x)--) +#endif +#ifdef GC_DEBUG +#define GC_PTR_STORE(p,q)(*(void**)GC_is_visible((void*)(p))=GC_is_valid_displacement((void*)(q))) +#else +#define GC_PTR_STORE(p,q)(*(void**)(p)=(void*)(q)) +#endif +GC_API void GC_CALL GC_ptr_store_and_dirty(void*, +const void*); +GC_API void GC_CALL GC_debug_ptr_store_and_dirty(void*, +const void*); +GC_API void (GC_CALLBACK*GC_same_obj_print_proc)(void*, +void*); +GC_API void (GC_CALLBACK*GC_is_valid_displacement_print_proc)(void*); +GC_API void (GC_CALLBACK*GC_is_visible_print_proc)(void*); +#ifdef GC_PTHREADS +#ifdef __cplusplus +} +#endif +#ifndef GC_PTHREAD_REDIRECTS_H +#define GC_PTHREAD_REDIRECTS_H +#if defined(GC_H)&&defined(GC_PTHREADS) +#ifndef GC_PTHREAD_REDIRECTS_ONLY +#include +#ifndef GC_NO_DLOPEN +#include +#endif +#ifndef GC_NO_PTHREAD_SIGMASK +#include +#endif +#ifdef __cplusplus +extern "C" { +#endif +#ifndef GC_SUSPEND_THREAD_ID +#define GC_SUSPEND_THREAD_ID pthread_t +#endif +#ifndef GC_NO_DLOPEN +GC_API void*GC_dlopen(const char*,int); +#endif +#ifndef GC_NO_PTHREAD_SIGMASK +#if defined(GC_PTHREAD_SIGMASK_NEEDED)||defined(_BSD_SOURCE)||defined(_GNU_SOURCE)||(_POSIX_C_SOURCE>=199506L)||(_XOPEN_SOURCE>=500) +GC_API int GC_pthread_sigmask(int,const sigset_t*, +sigset_t*); +#endif +#endif +#ifndef GC_PTHREAD_CREATE_CONST +#define GC_PTHREAD_CREATE_CONST const +#endif +GC_API int GC_pthread_create(pthread_t*, +GC_PTHREAD_CREATE_CONST pthread_attr_t*, +void*(*)(void*),void*); +GC_API int GC_pthread_join(pthread_t,void**); +GC_API int GC_pthread_detach(pthread_t); +#ifndef GC_NO_PTHREAD_CANCEL +GC_API int GC_pthread_cancel(pthread_t); +#endif +#if defined(GC_HAVE_PTHREAD_EXIT)&&!defined(GC_PTHREAD_EXIT_DECLARED) +#define GC_PTHREAD_EXIT_DECLARED +GC_API void GC_pthread_exit(void*)GC_PTHREAD_EXIT_ATTRIBUTE; +#endif +#ifdef __cplusplus +} +#endif +#endif +#if!defined(GC_NO_THREAD_REDIRECTS)&&!defined(GC_USE_LD_WRAP) +#undef pthread_create +#undef pthread_join +#undef pthread_detach +#define pthread_create GC_pthread_create +#define pthread_join GC_pthread_join +#define pthread_detach GC_pthread_detach +#ifndef GC_NO_PTHREAD_SIGMASK +#undef pthread_sigmask +#define pthread_sigmask GC_pthread_sigmask +#endif +#ifndef GC_NO_DLOPEN +#undef dlopen +#define dlopen GC_dlopen +#endif +#ifndef GC_NO_PTHREAD_CANCEL +#undef pthread_cancel +#define pthread_cancel GC_pthread_cancel +#endif +#ifdef GC_HAVE_PTHREAD_EXIT +#undef pthread_exit +#define pthread_exit GC_pthread_exit +#endif +#endif +#endif +#endif +#ifdef __cplusplus +extern "C" { +#endif +#endif +GC_API GC_ATTR_MALLOC void*GC_CALL GC_malloc_many(size_t); +#define GC_NEXT(p)(*(void**)(p)) +typedef int (GC_CALLBACK*GC_has_static_roots_func)( +const char*, +void*, +size_t); +GC_API void GC_CALL GC_register_has_static_roots_callback( +GC_has_static_roots_func); +#if!defined(CPPCHECK)&&!defined(GC_WINDOWS_H_INCLUDED)&&defined(WINAPI) +#define GC_WINDOWS_H_INCLUDED +#endif +#if defined(GC_WIN32_THREADS)&&(!defined(GC_PTHREADS)||defined(GC_BUILD)||defined(GC_WINDOWS_H_INCLUDED)) +#if (!defined(GC_NO_THREAD_DECLS)||defined(GC_BUILD))&&!defined(GC_DONT_INCL_WINDOWS_H) +#ifdef __cplusplus +} +#endif +#if!defined(_WIN32_WCE)&&!defined(__CEGCC__) +#include +#endif +#if defined(GC_BUILD)||!defined(GC_DONT_INCLUDE_WINDOWS_H) +#include +#define GC_WINDOWS_H_INCLUDED +#endif +#ifdef __cplusplus +extern "C" { +#endif +#ifdef GC_UNDERSCORE_STDCALL +#define GC_CreateThread _GC_CreateThread +#define GC_ExitThread _GC_ExitThread +#endif +#ifndef DECLSPEC_NORETURN +#ifdef GC_WINDOWS_H_INCLUDED +#define DECLSPEC_NORETURN +#else +#define DECLSPEC_NORETURN __declspec(noreturn) +#endif +#endif +#if!defined(_UINTPTR_T)&&!defined(_UINTPTR_T_DEFINED)&&!defined(UINTPTR_MAX) +typedef GC_word GC_uintptr_t; +#else +typedef uintptr_t GC_uintptr_t; +#endif +#ifdef _WIN64 +#define GC_WIN32_SIZE_T GC_uintptr_t +#elif defined(GC_WINDOWS_H_INCLUDED) +#define GC_WIN32_SIZE_T DWORD +#else +#define GC_WIN32_SIZE_T unsigned long +#endif +#ifdef GC_INSIDE_DLL +#ifdef GC_UNDERSCORE_STDCALL +#define GC_DllMain _GC_DllMain +#endif +#ifdef GC_WINDOWS_H_INCLUDED +GC_API BOOL WINAPI GC_DllMain(HINSTANCE, +ULONG, +LPVOID); +#else +GC_API int __stdcall GC_DllMain(void*,unsigned long,void*); +#endif +#endif +#ifdef GC_WINDOWS_H_INCLUDED +GC_API HANDLE WINAPI GC_CreateThread( +LPSECURITY_ATTRIBUTES, +GC_WIN32_SIZE_T, +LPTHREAD_START_ROUTINE, +LPVOID,DWORD, +LPDWORD); +GC_API DECLSPEC_NORETURN void WINAPI GC_ExitThread( +DWORD); +#else +struct _SECURITY_ATTRIBUTES; +GC_API void*__stdcall GC_CreateThread(struct _SECURITY_ATTRIBUTES*, +GC_WIN32_SIZE_T, +unsigned long (__stdcall*)(void*), +void*,unsigned long,unsigned long*); +GC_API DECLSPEC_NORETURN void __stdcall GC_ExitThread(unsigned long); +#endif +#if!defined(_WIN32_WCE)&&!defined(__CEGCC__) +GC_API GC_uintptr_t GC_CALL GC_beginthreadex( +void*,unsigned, +unsigned (__stdcall*)(void*), +void*,unsigned, +unsigned*); +GC_API void GC_CALL GC_endthreadex(unsigned); +#endif +#endif +#ifdef GC_WINMAIN_REDIRECT +#define WinMain GC_WinMain +#endif +#define GC_use_DllMain GC_use_threads_discovery +#ifndef GC_NO_THREAD_REDIRECTS +#define CreateThread GC_CreateThread +#define ExitThread GC_ExitThread +#undef _beginthreadex +#define _beginthreadex GC_beginthreadex +#undef _endthreadex +#define _endthreadex GC_endthreadex +#endif +#endif +GC_API void GC_CALL GC_set_force_unmap_on_gcollect(int); +GC_API int GC_CALL GC_get_force_unmap_on_gcollect(void); +#if defined(__CYGWIN32__)||defined(__CYGWIN__) +#ifdef __x86_64__ +extern int __data_start__[],__data_end__[]; +extern int __bss_start__[],__bss_end__[]; +#define GC_DATASTART ((GC_word)__data_start__ < (GC_word)__bss_start__?(void*)__data_start__:(void*)__bss_start__) +#define GC_DATAEND ((GC_word)__data_end__ > (GC_word)__bss_end__?(void*)__data_end__:(void*)__bss_end__) +#else +extern int _data_start__[],_data_end__[],_bss_start__[],_bss_end__[]; +#define GC_DATASTART ((GC_word)_data_start__ < (GC_word)_bss_start__?(void*)_data_start__:(void*)_bss_start__) +#define GC_DATAEND ((GC_word)_data_end__ > (GC_word)_bss_end__?(void*)_data_end__:(void*)_bss_end__) +#endif +#define GC_INIT_CONF_ROOTS GC_add_roots(GC_DATASTART,GC_DATAEND);GC_gcollect() +#elif defined(_AIX) +extern int _data[],_end[]; +#define GC_DATASTART ((void*)_data) +#define GC_DATAEND ((void*)_end) +#define GC_INIT_CONF_ROOTS GC_add_roots(GC_DATASTART,GC_DATAEND) +#elif (defined(HOST_ANDROID)||defined(__ANDROID__))&&defined(IGNORE_DYNAMIC_LOADING) +#pragma weak __dso_handle +extern int __dso_handle[]; +GC_API void*GC_CALL GC_find_limit(void*,int); +#define GC_INIT_CONF_ROOTS (void)(__dso_handle!=0?(GC_add_roots(__dso_handle,GC_find_limit(__dso_handle,1)),0):0) +#else +#define GC_INIT_CONF_ROOTS +#endif +#ifdef GC_DONT_EXPAND +#define GC_INIT_CONF_DONT_EXPAND GC_set_dont_expand(1) +#else +#define GC_INIT_CONF_DONT_EXPAND +#endif +#ifdef GC_FORCE_UNMAP_ON_GCOLLECT +#define GC_INIT_CONF_FORCE_UNMAP_ON_GCOLLECT GC_set_force_unmap_on_gcollect(1) +#else +#define GC_INIT_CONF_FORCE_UNMAP_ON_GCOLLECT +#endif +#ifdef GC_DONT_GC +#define GC_INIT_CONF_MAX_RETRIES (void)(GC_dont_gc=1) +#elif defined(GC_MAX_RETRIES)&&!defined(CPPCHECK) +#define GC_INIT_CONF_MAX_RETRIES GC_set_max_retries(GC_MAX_RETRIES) +#else +#define GC_INIT_CONF_MAX_RETRIES +#endif +#if defined(GC_ALLOCD_BYTES_PER_FINALIZER)&&!defined(CPPCHECK) +#define GC_INIT_CONF_ALLOCD_BYTES_PER_FINALIZER GC_set_allocd_bytes_per_finalizer(GC_ALLOCD_BYTES_PER_FINALIZER) +#else +#define GC_INIT_CONF_ALLOCD_BYTES_PER_FINALIZER +#endif +#if defined(GC_FREE_SPACE_DIVISOR)&&!defined(CPPCHECK) +#define GC_INIT_CONF_FREE_SPACE_DIVISOR GC_set_free_space_divisor(GC_FREE_SPACE_DIVISOR) +#else +#define GC_INIT_CONF_FREE_SPACE_DIVISOR +#endif +#if defined(GC_FULL_FREQ)&&!defined(CPPCHECK) +#define GC_INIT_CONF_FULL_FREQ GC_set_full_freq(GC_FULL_FREQ) +#else +#define GC_INIT_CONF_FULL_FREQ +#endif +#if defined(GC_TIME_LIMIT)&&!defined(CPPCHECK) +#define GC_INIT_CONF_TIME_LIMIT GC_set_time_limit(GC_TIME_LIMIT) +#else +#define GC_INIT_CONF_TIME_LIMIT +#endif +#if defined(GC_MARKERS)&&defined(GC_THREADS)&&!defined(CPPCHECK) +#define GC_INIT_CONF_MARKERS GC_set_markers_count(GC_MARKERS) +#else +#define GC_INIT_CONF_MARKERS +#endif +#if defined(GC_SIG_SUSPEND)&&defined(GC_THREADS)&&!defined(CPPCHECK) +#define GC_INIT_CONF_SUSPEND_SIGNAL GC_set_suspend_signal(GC_SIG_SUSPEND) +#else +#define GC_INIT_CONF_SUSPEND_SIGNAL +#endif +#if defined(GC_SIG_THR_RESTART)&&defined(GC_THREADS)&&!defined(CPPCHECK) +#define GC_INIT_CONF_THR_RESTART_SIGNAL GC_set_thr_restart_signal(GC_SIG_THR_RESTART) +#else +#define GC_INIT_CONF_THR_RESTART_SIGNAL +#endif +#if defined(GC_MAXIMUM_HEAP_SIZE)&&!defined(CPPCHECK) +#define GC_INIT_CONF_MAXIMUM_HEAP_SIZE GC_set_max_heap_size(GC_MAXIMUM_HEAP_SIZE) +#else +#define GC_INIT_CONF_MAXIMUM_HEAP_SIZE +#endif +#ifdef GC_IGNORE_WARN +#define GC_INIT_CONF_IGNORE_WARN GC_set_warn_proc(GC_ignore_warn_proc) +#else +#define GC_INIT_CONF_IGNORE_WARN +#endif +#if defined(GC_INITIAL_HEAP_SIZE)&&!defined(CPPCHECK) +#define GC_INIT_CONF_INITIAL_HEAP_SIZE { size_t heap_size=GC_get_heap_size();if (heap_size < (GC_INITIAL_HEAP_SIZE))(void)GC_expand_hp((GC_INITIAL_HEAP_SIZE)- heap_size);} +#else +#define GC_INIT_CONF_INITIAL_HEAP_SIZE +#endif +#define GC_INIT(){ GC_INIT_CONF_DONT_EXPAND;GC_INIT_CONF_FORCE_UNMAP_ON_GCOLLECT;GC_INIT_CONF_MAX_RETRIES;GC_INIT_CONF_ALLOCD_BYTES_PER_FINALIZER;GC_INIT_CONF_FREE_SPACE_DIVISOR;GC_INIT_CONF_FULL_FREQ;GC_INIT_CONF_TIME_LIMIT;GC_INIT_CONF_MARKERS;GC_INIT_CONF_SUSPEND_SIGNAL;GC_INIT_CONF_THR_RESTART_SIGNAL;GC_INIT_CONF_MAXIMUM_HEAP_SIZE;GC_init();GC_INIT_CONF_ROOTS;GC_INIT_CONF_IGNORE_WARN;GC_INIT_CONF_INITIAL_HEAP_SIZE;} +GC_API void GC_CALL GC_win32_free_heap(void); +#if defined(__SYMBIAN32__) +void GC_init_global_static_roots(void); +#endif +#if defined(_AMIGA)&&!defined(GC_AMIGA_MAKINGLIB) +void*GC_amiga_realloc(void*,size_t); +#define GC_realloc(a,b)GC_amiga_realloc(a,b) +void GC_amiga_set_toany(void (*)(void)); +extern int GC_amiga_free_space_divisor_inc; +extern void*(*GC_amiga_allocwrapper_do)(size_t,void*(GC_CALL*)(size_t)); +#define GC_malloc(a)(*GC_amiga_allocwrapper_do)(a,GC_malloc) +#define GC_malloc_atomic(a)(*GC_amiga_allocwrapper_do)(a,GC_malloc_atomic) +#define GC_malloc_uncollectable(a)(*GC_amiga_allocwrapper_do)(a,GC_malloc_uncollectable) +#define GC_malloc_atomic_uncollectable(a)(*GC_amiga_allocwrapper_do)(a,GC_malloc_atomic_uncollectable) +#define GC_malloc_ignore_off_page(a)(*GC_amiga_allocwrapper_do)(a,GC_malloc_ignore_off_page) +#define GC_malloc_atomic_ignore_off_page(a)(*GC_amiga_allocwrapper_do)(a,GC_malloc_atomic_ignore_off_page) +#endif +#ifdef __cplusplus +} +#endif +#endif +#endif +#include +#if!defined(sony_news) +#include +#endif +#ifdef DGUX +#include +#include +#include +#endif +#ifdef BSD_TIME +#include +#include +#include +#endif +#ifdef PARALLEL_MARK +#define AO_REQUIRE_CAS +#if!defined(__GNUC__)&&!defined(AO_ASSUME_WINDOWS98) +#define AO_ASSUME_WINDOWS98 +#endif +#endif +#ifndef GC_TINY_FL_H +#define GC_TINY_FL_H +#ifndef GC_GRANULE_BYTES +#if defined(__LP64__)||defined (_LP64)||defined(_WIN64)||defined(__s390x__)||(defined(__x86_64__)&&!defined(__ILP32__))||defined(__alpha__)||defined(__powerpc64__)||defined(__arch64__) +#define GC_GRANULE_BYTES 16 +#define GC_GRANULE_WORDS 2 +#else +#define GC_GRANULE_BYTES 8 +#define GC_GRANULE_WORDS 2 +#endif +#endif +#if GC_GRANULE_WORDS==2 +#define GC_WORDS_TO_GRANULES(n)((n)>>1) +#else +#define GC_WORDS_TO_GRANULES(n)((n)*sizeof(void*)/GC_GRANULE_BYTES) +#endif +#ifndef GC_TINY_FREELISTS +#if GC_GRANULE_BYTES==16 +#define GC_TINY_FREELISTS 25 +#else +#define GC_TINY_FREELISTS 33 +#endif +#endif +#define GC_RAW_BYTES_FROM_INDEX(i)((i)*GC_GRANULE_BYTES) +#endif +#ifndef GC_MARK_H +#define GC_MARK_H +#ifndef GC_H +#endif +#ifdef __cplusplus +extern "C" { +#endif +#define GC_PROC_BYTES 100 +#if defined(GC_BUILD)||defined(NOT_GCBUILD) +struct GC_ms_entry; +#else +struct GC_ms_entry { void*opaque;}; +#endif +typedef struct GC_ms_entry*(*GC_mark_proc)(GC_word*, +struct GC_ms_entry*, +struct GC_ms_entry*, +GC_word); +#define GC_LOG_MAX_MARK_PROCS 6 +#define GC_MAX_MARK_PROCS (1<=(GC_word)GC_least_plausible_heap_addr&&(GC_word)(obj)<=(GC_word)GC_greatest_plausible_heap_addr?GC_mark_and_push(obj,msp,lim,src):(msp)) +GC_API GC_ATTR_CONST size_t GC_CALL GC_get_debug_header_size(void); +#define GC_USR_PTR_FROM_BASE(p)((void*)((char*)(p)+GC_get_debug_header_size())) +GC_API GC_ATTR_DEPRECATED +#ifdef GC_BUILD +const +#endif +size_t GC_debug_header_size; +GC_API void**GC_CALL GC_new_free_list(void); +GC_API void**GC_CALL GC_new_free_list_inner(void); +GC_API unsigned GC_CALL GC_new_kind(void**, +GC_word, +int, +int)GC_ATTR_NONNULL(1); +GC_API unsigned GC_CALL GC_new_kind_inner(void**, +GC_word, +int, +int)GC_ATTR_NONNULL(1); +GC_API unsigned GC_CALL GC_new_proc(GC_mark_proc); +GC_API unsigned GC_CALL GC_new_proc_inner(GC_mark_proc); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL GC_generic_malloc( +size_t, +int); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL +GC_generic_malloc_ignore_off_page( +size_t,int); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL +GC_generic_malloc_uncollectable( +size_t,int); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL +GC_generic_or_special_malloc( +size_t,int); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL +GC_debug_generic_or_special_malloc( +size_t,int, +GC_EXTRA_PARAMS); +#ifdef GC_DEBUG +#define GC_GENERIC_OR_SPECIAL_MALLOC(sz,knd)GC_debug_generic_or_special_malloc(sz,knd,GC_EXTRAS) +#else +#define GC_GENERIC_OR_SPECIAL_MALLOC(sz,knd)GC_generic_or_special_malloc(sz,knd) +#endif +GC_API int GC_CALL GC_get_kind_and_size(const void*,size_t*) +GC_ATTR_NONNULL(1); +typedef void (GC_CALLBACK*GC_describe_type_fn)(void*, +char*); +#define GC_TYPE_DESCR_LEN 40 +GC_API void GC_CALL GC_register_describe_type_fn(int, +GC_describe_type_fn); +GC_API void*GC_CALL GC_clear_stack(void*); +typedef void (GC_CALLBACK*GC_start_callback_proc)(void); +GC_API void GC_CALL GC_set_start_callback(GC_start_callback_proc); +GC_API GC_start_callback_proc GC_CALL GC_get_start_callback(void); +GC_API int GC_CALL GC_is_marked(const void*)GC_ATTR_NONNULL(1); +GC_API void GC_CALL GC_clear_mark_bit(const void*)GC_ATTR_NONNULL(1); +GC_API void GC_CALL GC_set_mark_bit(const void*)GC_ATTR_NONNULL(1); +GC_API void GC_CALL GC_push_all(void*,void*); +GC_API void GC_CALL GC_push_all_eager(void*,void*); +GC_API void GC_CALL GC_push_conditional(void*,void*, +int); +GC_API void GC_CALL GC_push_finalizer_structures(void); +typedef void (GC_CALLBACK*GC_push_other_roots_proc)(void); +GC_API void GC_CALL GC_set_push_other_roots(GC_push_other_roots_proc); +GC_API GC_push_other_roots_proc GC_CALL GC_get_push_other_roots(void); +typedef void (GC_CALLBACK*GC_reachable_object_proc)(void*, +size_t, +void*); +GC_API void GC_CALL GC_enumerate_reachable_objects_inner( +GC_reachable_object_proc, +void*)GC_ATTR_NONNULL(1); +GC_API int GC_CALL GC_is_tmp_root(void*); +GC_API void GC_CALL GC_print_trace(GC_word); +GC_API void GC_CALL GC_print_trace_inner(GC_word); +#ifdef __cplusplus +} +#endif +#endif +typedef GC_word word; +typedef GC_signed_word signed_word; +typedef unsigned int unsigned32; +typedef int GC_bool; +#define TRUE 1 +#define FALSE 0 +#ifndef PTR_T_DEFINED +typedef char*ptr_t; +#define PTR_T_DEFINED +#endif +#ifndef SIZE_MAX +#include +#endif +#if defined(SIZE_MAX)&&!defined(CPPCHECK) +#define GC_SIZE_MAX ((size_t)SIZE_MAX) +#else +#define GC_SIZE_MAX (~(size_t)0) +#endif +#if GC_GNUC_PREREQ(3,0)&&!defined(LINT2) +#define EXPECT(expr,outcome)__builtin_expect(expr,outcome) +#else +#define EXPECT(expr,outcome)(expr) +#endif +#define SIZET_SAT_ADD(a,b)(EXPECT((a)< GC_SIZE_MAX - (b),TRUE)?(a)+(b):GC_SIZE_MAX) +#ifndef GCCONFIG_H +#define GCCONFIG_H +#ifdef CPPCHECK +#undef CLOCKS_PER_SEC +#undef FIXUP_POINTER +#undef POINTER_MASK +#undef POINTER_SHIFT +#undef REDIRECT_REALLOC +#undef _MAX_PATH +#endif +#ifndef PTR_T_DEFINED +typedef char*ptr_t; +#define PTR_T_DEFINED +#endif +#if!defined(sony_news) +#include +#endif +#ifdef __cplusplus +#define EXTERN_C_BEGIN extern "C" { +#define EXTERN_C_END } +#else +#define EXTERN_C_BEGIN +#define EXTERN_C_END +#endif +EXTERN_C_BEGIN +#if defined(__clang__)&&defined(__clang_major__) +#define GC_CLANG_PREREQ(major,minor)((__clang_major__<<16)+__clang_minor__>=((major)<<16)+(minor)) +#define GC_CLANG_PREREQ_FULL(major,minor,patchlevel)(GC_CLANG_PREREQ(major,(minor)+1)||(__clang_major__==(major)&&__clang_minor__==(minor)&&__clang_patchlevel__>=(patchlevel))) +#else +#define GC_CLANG_PREREQ(major,minor)0 +#define GC_CLANG_PREREQ_FULL(major,minor,patchlevel)0 +#endif +#ifdef LINT2 +#define COVERT_DATAFLOW(w)(~(GC_word)(w)^(~(GC_word)0)) +#else +#define COVERT_DATAFLOW(w)((GC_word)(w)) +#endif +#if defined(__ANDROID__)&&!defined(HOST_ANDROID) +#define HOST_ANDROID 1 +#endif +#if defined(TIZEN)&&!defined(HOST_TIZEN) +#define HOST_TIZEN 1 +#endif +#if defined(__SYMBIAN32__)&&!defined(SYMBIAN) +#define SYMBIAN +#ifdef __WINS__ +#pragma data_seg(".data2") +#endif +#endif +#if (defined(linux)||defined(__linux__)||defined(HOST_ANDROID))&&!defined(LINUX)&&!defined(__native_client__) +#define LINUX +#endif +#if defined(__NetBSD__) +#define NETBSD +#endif +#if defined(__OpenBSD__) +#define OPENBSD +#endif +#if (defined(__FreeBSD__)||defined(__DragonFly__)||defined(__FreeBSD_kernel__))&&!defined(FREEBSD)&&!defined(SN_TARGET_ORBIS) +#define FREEBSD +#endif +#if defined(macosx)||(defined(__APPLE__)&&defined(__MACH__)) +#define DARWIN +EXTERN_C_END +#include +EXTERN_C_BEGIN +#endif +#if defined(__native_client__) +#define NACL +#if!defined(__portable_native_client__)&&!defined(__arm__) +#define I386 +#define mach_type_known +#else +#endif +#endif +#if defined(__aarch64__) +#define AARCH64 +#if!defined(LINUX)&&!defined(DARWIN)&&!defined(FREEBSD)&&!defined(NETBSD)&&!defined(NN_BUILD_TARGET_PLATFORM_NX)&&!defined(OPENBSD) +#define NOSYS +#define mach_type_known +#endif +#endif +#if defined(__arm)||defined(__arm__)||defined(__thumb__) +#define ARM32 +#if defined(NACL) +#define mach_type_known +#elif!defined(LINUX)&&!defined(NETBSD)&&!defined(FREEBSD)&&!defined(OPENBSD)&&!defined(DARWIN)&&!defined(_WIN32)&&!defined(__CEGCC__)&&!defined(NN_PLATFORM_CTR)&&!defined(SN_TARGET_ORBIS)&&!defined(SN_TARGET_PSP2)&&!defined(SYMBIAN) +#define NOSYS +#define mach_type_known +#endif +#endif +#if defined(sun)&&defined(mc68000)&&!defined(CPPCHECK) +#error SUNOS4 no longer supported +#endif +#if defined(hp9000s300)&&!defined(CPPCHECK) +#error M68K based HP machines no longer supported +#endif +#if defined(OPENBSD)&&defined(m68k) +#define M68K +#define mach_type_known +#endif +#if defined(OPENBSD)&&defined(__sparc__) +#define SPARC +#define mach_type_known +#endif +#if defined(OPENBSD)&&defined(__arm__) +#define ARM32 +#define mach_type_known +#endif +#if defined(OPENBSD)&&defined(__aarch64__) +#define AARCH64 +#define mach_type_known +#endif +#if defined(OPENBSD)&&defined(__sh__) +#define SH +#define mach_type_known +#endif +#if defined(NETBSD)&&(defined(m68k)||defined(__m68k__)) +#define M68K +#define mach_type_known +#endif +#if defined(NETBSD)&&defined(__powerpc__) +#define POWERPC +#define mach_type_known +#endif +#if defined(NETBSD)&&(defined(__arm32__)||defined(__arm__)) +#define ARM32 +#define mach_type_known +#endif +#if defined(NETBSD)&&defined(__aarch64__) +#define AARCH64 +#define mach_type_known +#endif +#if defined(NETBSD)&&defined(__sh__) +#define SH +#define mach_type_known +#endif +#if defined(vax)||defined(__vax__) +#define VAX +#ifdef ultrix +#define ULTRIX +#else +#define BSD +#endif +#define mach_type_known +#endif +#if defined(NETBSD)&&defined(__vax__) +#define VAX +#define mach_type_known +#endif +#if defined(mips)||defined(__mips)||defined(_mips) +#define MIPS +#if defined(nec_ews)||defined(_nec_ews) +#define EWS4800 +#endif +#if!defined(LINUX)&&!defined(EWS4800)&&!defined(NETBSD)&&!defined(OPENBSD) +#if defined(ultrix)||defined(__ultrix) +#define ULTRIX +#else +#define IRIX5 +#endif +#endif +#if defined(NETBSD)&&defined(__MIPSEL__) +#undef ULTRIX +#endif +#define mach_type_known +#endif +#if defined(__QNX__) +#define I386 +#define mach_type_known +#endif +#if defined(__NIOS2__)||defined(__NIOS2)||defined(__nios2__) +#define NIOS2 +#define mach_type_known +#endif +#if defined(__or1k__) +#define OR1K +#define mach_type_known +#endif +#if defined(DGUX)&&(defined(i386)||defined(__i386__)) +#define I386 +#ifndef _USING_DGUX +#define _USING_DGUX +#endif +#define mach_type_known +#endif +#if defined(sequent)&&(defined(i386)||defined(__i386__)) +#define I386 +#define SEQUENT +#define mach_type_known +#endif +#if (defined(sun)||defined(__sun))&&(defined(i386)||defined(__i386__)) +#define I386 +#define SOLARIS +#define mach_type_known +#endif +#if (defined(sun)||defined(__sun))&&defined(__amd64) +#define X86_64 +#define SOLARIS +#define mach_type_known +#endif +#if (defined(__OS2__)||defined(__EMX__))&&defined(__32BIT__) +#define I386 +#define OS2 +#define mach_type_known +#endif +#if defined(ibm032)&&!defined(CPPCHECK) +#error IBM PC/RT no longer supported +#endif +#if (defined(sun)||defined(__sun))&&(defined(sparc)||defined(__sparc)) +EXTERN_C_END +#include +EXTERN_C_BEGIN +#define SPARC +#define SOLARIS +#define mach_type_known +#elif defined(sparc)&&defined(unix)&&!defined(sun)&&!defined(linux)&&!defined(FREEBSD)&&!defined(NETBSD)&&!defined(OPENBSD) +#define SPARC +#define DRSNX +#define mach_type_known +#endif +#if defined(_IBMR2) +#define POWERPC +#define AIX +#define mach_type_known +#endif +#if defined(NETBSD)&&defined(__sparc__) +#define SPARC +#define mach_type_known +#endif +#if defined(_M_XENIX)&&defined(_M_SYSV)&&defined(_M_I386) +#define I386 +#if defined(_SCO_ELF) +#define SCO_ELF +#else +#define SCO +#endif +#define mach_type_known +#endif +#if defined(_AUX_SOURCE)&&!defined(CPPCHECK) +#error A/UX no longer supported +#endif +#if defined(_PA_RISC1_0)||defined(_PA_RISC1_1)||defined(_PA_RISC2_0)||defined(hppa)||defined(__hppa__) +#define HP_PA +#if!defined(LINUX)&&!defined(HPUX)&&!defined(OPENBSD) +#define HPUX +#endif +#define mach_type_known +#endif +#if defined(__ia64)&&(defined(_HPUX_SOURCE)||defined(__HP_aCC)) +#define IA64 +#ifndef HPUX +#define HPUX +#endif +#define mach_type_known +#endif +#if (defined(__BEOS__)||defined(__HAIKU__))&&defined(_X86_) +#define I386 +#define HAIKU +#define mach_type_known +#endif +#if defined(__HAIKU__)&&(defined(__amd64__)||defined(__x86_64__)) +#define X86_64 +#define HAIKU +#define mach_type_known +#endif +#if defined(OPENBSD)&&defined(__amd64__) +#define X86_64 +#define mach_type_known +#endif +#if defined(LINUX)&&(defined(i386)||defined(__i386__)) +#define I386 +#define mach_type_known +#endif +#if defined(LINUX)&&defined(__x86_64__) +#define X86_64 +#define mach_type_known +#endif +#if defined(LINUX)&&(defined(__ia64__)||defined(__ia64)) +#define IA64 +#define mach_type_known +#endif +#if defined(LINUX)&&defined(__aarch64__) +#define AARCH64 +#define mach_type_known +#endif +#if defined(LINUX)&&(defined(__arm)||defined(__arm__)) +#define ARM32 +#define mach_type_known +#endif +#if defined(LINUX)&&defined(__cris__) +#ifndef CRIS +#define CRIS +#endif +#define mach_type_known +#endif +#if defined(LINUX)&&(defined(powerpc)||defined(__powerpc__)||defined(powerpc64)||defined(__powerpc64__)) +#define POWERPC +#define mach_type_known +#endif +#if defined(LINUX)&&defined(__mc68000__) +#define M68K +#define mach_type_known +#endif +#if defined(LINUX)&&(defined(sparc)||defined(__sparc__)) +#define SPARC +#define mach_type_known +#endif +#if defined(LINUX)&&defined(__sh__) +#define SH +#define mach_type_known +#endif +#if defined(LINUX)&&defined(__avr32__) +#define AVR32 +#define mach_type_known +#endif +#if defined(LINUX)&&defined(__m32r__) +#define M32R +#define mach_type_known +#endif +#if defined(__alpha)||defined(__alpha__) +#define ALPHA +#if!defined(LINUX)&&!defined(NETBSD)&&!defined(OPENBSD)&&!defined(FREEBSD) +#define OSF1 +#endif +#define mach_type_known +#endif +#if defined(_AMIGA)&&!defined(AMIGA) +#define AMIGA +#endif +#ifdef AMIGA +#define M68K +#define mach_type_known +#endif +#if defined(THINK_C)||(defined(__MWERKS__)&&!defined(__powerc)&&!defined(SYMBIAN)) +#define M68K +#define MACOS +#define mach_type_known +#endif +#if defined(__MWERKS__)&&defined(__powerc)&&!defined(__MACH__)&&!defined(SYMBIAN) +#define POWERPC +#define MACOS +#define mach_type_known +#endif +#if defined(OPENBSD)&&defined(__powerpc__) +#define POWERPC +#define mach_type_known +#endif +#if defined(DARWIN) +#if defined(__ppc__)||defined(__ppc64__) +#define POWERPC +#define mach_type_known +#elif defined(__x86_64__)||defined(__x86_64) +#define X86_64 +#define mach_type_known +#elif defined(__i386__) +#define I386 +#define mach_type_known +#elif defined(__arm__) +#define ARM32 +#define mach_type_known +#elif defined(__aarch64__) +#define AARCH64 +#define mach_type_known +#endif +#endif +#if defined(__rtems__)&&(defined(i386)||defined(__i386__)) +#define I386 +#define RTEMS +#define mach_type_known +#endif +#if defined(NeXT)&&defined(mc68000) +#define M68K +#define NEXT +#define mach_type_known +#endif +#if defined(NeXT)&&(defined(i386)||defined(__i386__)) +#define I386 +#define NEXT +#define mach_type_known +#endif +#if defined(OPENBSD)&&(defined(i386)||defined(__i386__)) +#define I386 +#define mach_type_known +#endif +#if defined(NETBSD)&&(defined(i386)||defined(__i386__)) +#define I386 +#define mach_type_known +#endif +#if defined(NETBSD)&&defined(__x86_64__) +#define X86_64 +#define mach_type_known +#endif +#if defined(FREEBSD)&&(defined(i386)||defined(__i386__)) +#define I386 +#define mach_type_known +#endif +#if (defined(FREEBSD)||defined(SN_TARGET_ORBIS))&&(defined(__amd64__)||defined(__x86_64__)) +#define X86_64 +#define mach_type_known +#endif +#if defined(FREEBSD)&&defined(__sparc__) +#define SPARC +#define mach_type_known +#endif +#if defined(FREEBSD)&&(defined(powerpc)||defined(__powerpc__)) +#define POWERPC +#define mach_type_known +#endif +#if defined(FREEBSD)&&defined(__arm__) +#define ARM32 +#define mach_type_known +#endif +#if defined(FREEBSD)&&defined(__aarch64__) +#define AARCH64 +#define mach_type_known +#endif +#if defined(FREEBSD)&&(defined(mips)||defined(__mips)||defined(_mips)) +#define MIPS +#define mach_type_known +#endif +#if defined(bsdi)&&(defined(i386)||defined(__i386__)) +#define I386 +#define BSDI +#define mach_type_known +#endif +#if!defined(mach_type_known)&&defined(__386BSD__) +#define I386 +#define THREE86BSD +#define mach_type_known +#endif +#if defined(_CX_UX)&&defined(_M88K) +#define M88K +#define CX_UX +#define mach_type_known +#endif +#if defined(DGUX)&&defined(m88k) +#define M88K +#define mach_type_known +#endif +#if defined(_WIN32_WCE)||defined(__CEGCC__)||defined(__MINGW32CE__) +#if defined(SH3)||defined(SH4) +#define SH +#endif +#if defined(x86)||defined(__i386__) +#define I386 +#endif +#if defined(_M_ARM)||defined(ARM)||defined(_ARM_) +#define ARM32 +#endif +#define MSWINCE +#define mach_type_known +#else +#if ((defined(_MSDOS)||defined(_MSC_VER))&&(_M_IX86>=300))||(defined(_WIN32)&&!defined(__CYGWIN32__)&&!defined(__CYGWIN__)&&!defined(__INTERIX)&&!defined(SYMBIAN)) +#if defined(__LP64__)||defined(_M_X64) +#define X86_64 +#elif defined(_M_ARM) +#define ARM32 +#elif defined(_M_ARM64) +#define AARCH64 +#else +#define I386 +#endif +#ifdef _XBOX_ONE +#define MSWIN_XBOX1 +#else +#ifndef MSWIN32 +#define MSWIN32 +#endif +#if defined(WINAPI_FAMILY)&&(WINAPI_FAMILY==WINAPI_FAMILY_APP) +#define MSWINRT_FLAVOR +#endif +#endif +#define mach_type_known +#endif +#if defined(_MSC_VER)&&defined(_M_IA64) +#define IA64 +#define MSWIN32 +#endif +#endif +#if defined(__DJGPP__) +#define I386 +#ifndef DJGPP +#define DJGPP +#endif +#define mach_type_known +#endif +#if defined(__CYGWIN32__)||defined(__CYGWIN__) +#if defined(__LP64__) +#define X86_64 +#else +#define I386 +#endif +#define CYGWIN32 +#define mach_type_known +#endif +#if defined(__INTERIX) +#define I386 +#define INTERIX +#define mach_type_known +#endif +#if defined(__MINGW32__)&&!defined(mach_type_known) +#define I386 +#define MSWIN32 +#define mach_type_known +#endif +#if defined(__BORLANDC__) +#define I386 +#define MSWIN32 +#define mach_type_known +#endif +#if defined(_UTS)&&!defined(mach_type_known) +#define S370 +#define UTS4 +#define mach_type_known +#endif +#if defined(__pj__)&&!defined(CPPCHECK) +#error PicoJava no longer supported +#endif +#if defined(__embedded__)&&defined(PPC) +#define POWERPC +#define NOSYS +#define mach_type_known +#endif +#if defined(__WATCOMC__)&&defined(__386__) +#define I386 +#if!defined(OS2)&&!defined(MSWIN32)&&!defined(DOS4GW) +#if defined(__OS2__) +#define OS2 +#else +#if defined(__WINDOWS_386__)||defined(__NT__) +#define MSWIN32 +#else +#define DOS4GW +#endif +#endif +#endif +#define mach_type_known +#endif +#if defined(__s390__)&&defined(LINUX) +#define S390 +#define mach_type_known +#endif +#if defined(__GNU__) +#if defined(__i386__) +#define HURD +#define I386 +#define mach_type_known +#endif +#endif +#if defined(__TANDEM) +#define MIPS +#define NONSTOP +#define mach_type_known +#endif +#if defined(__arc__)&&defined(LINUX) +#define ARC +#define mach_type_known +#endif +#if defined(__hexagon__)&&defined(LINUX) +#define HEXAGON +#define mach_type_known +#endif +#if defined(__tile__)&&defined(LINUX) +#ifdef __tilegx__ +#define TILEGX +#else +#define TILEPRO +#endif +#define mach_type_known +#endif +#if defined(__riscv)&&(defined(FREEBSD)||defined(LINUX)) +#define RISCV +#define mach_type_known +#endif +#if defined(SN_TARGET_PSP2) +#define mach_type_known +#endif +#if defined(NN_PLATFORM_CTR) +#define mach_type_known +#endif +#if defined(NN_BUILD_TARGET_PLATFORM_NX) +#define NINTENDO_SWITCH +#define mach_type_known +#endif +#if defined(SYMBIAN) +#define mach_type_known +#endif +#if defined(__EMSCRIPTEN__) +#define I386 +#define mach_type_known +#endif +#if!defined(mach_type_known)&&!defined(CPPCHECK) +#error The collector has not been ported to this machine/OS combination +#endif +#if GC_GNUC_PREREQ(2,8)&&!defined(__INTEL_COMPILER)&&!defined(__PATHCC__)&&!defined(__FUJITSU)&&!(defined(POWERPC)&&defined(DARWIN))&&!defined(RTEMS)&&!defined(__ARMCC_VERSION)&&!defined(__clang__) +#define HAVE_BUILTIN_UNWIND_INIT +#endif +#ifdef SYMBIAN +#define MACH_TYPE "SYMBIAN" +#define OS_TYPE "SYMBIAN" +#define CPP_WORDSZ 32 +#define ALIGNMENT 4 +#define DATASTART (ptr_t)ALIGNMENT +#define DATAEND (ptr_t)ALIGNMENT +#endif +#define STACK_GRAN 0x1000000 +#ifdef M68K +#define MACH_TYPE "M68K" +#define ALIGNMENT 2 +#ifdef OPENBSD +#define OS_TYPE "OPENBSD" +#define HEURISTIC2 +#ifdef __ELF__ +extern ptr_t GC_data_start; +#define DATASTART GC_data_start +#define DYNAMIC_LOADING +#else +extern char etext[]; +#define DATASTART ((ptr_t)(etext)) +#endif +#endif +#ifdef NETBSD +#define OS_TYPE "NETBSD" +#define HEURISTIC2 +#ifdef __ELF__ +extern ptr_t GC_data_start; +#define DATASTART GC_data_start +#define DYNAMIC_LOADING +#else +extern char etext[]; +#define DATASTART ((ptr_t)(etext)) +#endif +#endif +#ifdef LINUX +#define OS_TYPE "LINUX" +#define LINUX_STACKBOTTOM +#define COUNT_UNMAPPED_REGIONS +#if!defined(REDIRECT_MALLOC) +#define MPROTECT_VDB +#endif +#ifdef __ELF__ +#define DYNAMIC_LOADING +EXTERN_C_END +#include +EXTERN_C_BEGIN +#if defined(__GLIBC__)&&__GLIBC__>=2 +#define SEARCH_FOR_DATA_START +#else +extern char**__environ; +#define DATASTART ((ptr_t)(&__environ)) +#endif +extern int _end[]; +#define DATAEND ((ptr_t)(_end)) +#else +extern int etext[]; +#define DATASTART ((ptr_t)((((word)(etext))+0xfff)&~0xfff)) +#endif +#endif +#ifdef AMIGA +#define OS_TYPE "AMIGA" +#define DATAEND +#define GETPAGESIZE()4096 +#endif +#ifdef MACOS +#ifndef __LOWMEM__ +EXTERN_C_END +#include +EXTERN_C_BEGIN +#endif +#define OS_TYPE "MACOS" +#define STACKBOTTOM ((ptr_t)LMGetCurStackBase()) +#define DATAEND +#define GETPAGESIZE()4096 +#endif +#ifdef NEXT +#define OS_TYPE "NEXT" +#define DATASTART ((ptr_t)get_etext()) +#define DATASTART_IS_FUNC +#define STACKBOTTOM ((ptr_t)0x4000000) +#define DATAEND +#endif +#endif +#ifdef POWERPC +#define MACH_TYPE "POWERPC" +#ifdef MACOS +#define ALIGNMENT 2 +#ifndef __LOWMEM__ +EXTERN_C_END +#include +EXTERN_C_BEGIN +#endif +#define OS_TYPE "MACOS" +#define STACKBOTTOM ((ptr_t)LMGetCurStackBase()) +#define DATAEND +#endif +#ifdef LINUX +#if defined(__powerpc64__) +#define ALIGNMENT 8 +#define CPP_WORDSZ 64 +#ifndef HBLKSIZE +#define HBLKSIZE 4096 +#endif +#else +#define ALIGNMENT 4 +#endif +#define OS_TYPE "LINUX" +#if defined(__bg__) +#define HEURISTIC2 +#define NO_PTHREAD_GETATTR_NP +#else +#define LINUX_STACKBOTTOM +#endif +#define COUNT_UNMAPPED_REGIONS +#define DYNAMIC_LOADING +#define SEARCH_FOR_DATA_START +extern int _end[]; +#define DATAEND ((ptr_t)(_end)) +#endif +#ifdef DARWIN +#define OS_TYPE "DARWIN" +#define DYNAMIC_LOADING +#if defined(__ppc64__) +#define ALIGNMENT 8 +#define CPP_WORDSZ 64 +#define STACKBOTTOM ((ptr_t)0x7fff5fc00000) +#define CACHE_LINE_SIZE 64 +#ifndef HBLKSIZE +#define HBLKSIZE 4096 +#endif +#else +#define ALIGNMENT 4 +#define STACKBOTTOM ((ptr_t)0xc0000000) +#endif +#define DATASTART ((ptr_t)get_etext()) +#define DATAEND ((ptr_t)get_end()) +#define USE_MMAP_ANON +#define MPROTECT_VDB +EXTERN_C_END +#include +EXTERN_C_BEGIN +#define GETPAGESIZE()(unsigned)getpagesize() +#if defined(USE_PPC_PREFETCH)&&defined(__GNUC__) +#define PREFETCH(x)__asm__ __volatile__ ("dcbt 0,%0"::"r" ((const void*)(x))) +#define GC_PREFETCH_FOR_WRITE(x)__asm__ __volatile__ ("dcbtst 0,%0"::"r" ((const void*)(x))) +#endif +#define NO_PTHREAD_TRYLOCK +#endif +#ifdef OPENBSD +#define OS_TYPE "OPENBSD" +#if defined(__powerpc64__) +#define ALIGNMENT 8 +#define CPP_WORDSZ 64 +#else +#define ALIGNMENT 4 +#endif +#ifndef GC_OPENBSD_THREADS +#define HEURISTIC2 +#endif +extern int __data_start[]; +#define DATASTART ((ptr_t)__data_start) +extern int _end[]; +#define DATAEND ((ptr_t)(&_end)) +#define DYNAMIC_LOADING +#endif +#ifdef FREEBSD +#if defined(__powerpc64__) +#define ALIGNMENT 8 +#define CPP_WORDSZ 64 +#ifndef HBLKSIZE +#define HBLKSIZE 4096 +#endif +#else +#define ALIGNMENT 4 +#endif +#define OS_TYPE "FREEBSD" +#ifndef GC_FREEBSD_THREADS +#define MPROTECT_VDB +#endif +#define SIG_SUSPEND SIGUSR1 +#define SIG_THR_RESTART SIGUSR2 +#define FREEBSD_STACKBOTTOM +#define DYNAMIC_LOADING +extern char etext[]; +#define DATASTART GC_FreeBSDGetDataStart(0x1000,(ptr_t)etext) +#define DATASTART_USES_BSDGETDATASTART +#endif +#ifdef NETBSD +#define ALIGNMENT 4 +#define OS_TYPE "NETBSD" +#define HEURISTIC2 +extern ptr_t GC_data_start; +#define DATASTART GC_data_start +#define DYNAMIC_LOADING +#endif +#ifdef SN_TARGET_PS3 +#define OS_TYPE "SN_TARGET_PS3" +#define NO_GETENV +#define CPP_WORDSZ 32 +#define ALIGNMENT 4 +extern int _end[]; +extern int __bss_start; +#define DATASTART ((ptr_t)(__bss_start)) +#define DATAEND ((ptr_t)(_end)) +#define STACKBOTTOM ((ptr_t)ps3_get_stack_bottom()) +#define NO_PTHREAD_TRYLOCK +#endif +#ifdef AIX +#define OS_TYPE "AIX" +#undef ALIGNMENT +#undef IA64 +#ifdef __64BIT__ +#define ALIGNMENT 8 +#define CPP_WORDSZ 64 +#define STACKBOTTOM ((ptr_t)0x1000000000000000) +#else +#define ALIGNMENT 4 +#define CPP_WORDSZ 32 +#define STACKBOTTOM ((ptr_t)((ulong)&errno)) +#endif +#define USE_MMAP_ANON +extern int _data[],_end[]; +#define DATASTART ((ptr_t)((ulong)_data)) +#define DATAEND ((ptr_t)((ulong)_end)) +extern int errno; +#define DYNAMIC_LOADING +#endif +#ifdef NOSYS +#define ALIGNMENT 4 +#define OS_TYPE "NOSYS" +extern void __end[],__dso_handle[]; +#define DATASTART ((ptr_t)__dso_handle) +#define DATAEND ((ptr_t)(__end)) +#undef STACK_GRAN +#define STACK_GRAN 0x10000000 +#define HEURISTIC1 +#endif +#endif +#ifdef NACL +#define OS_TYPE "NACL" +#if defined(__GLIBC__) +#define DYNAMIC_LOADING +#endif +#define DATASTART ((ptr_t)0x10020000) +extern int _end[]; +#define DATAEND ((ptr_t)_end) +#undef STACK_GRAN +#define STACK_GRAN 0x10000 +#define HEURISTIC1 +#define NO_PTHREAD_GETATTR_NP +#define USE_MMAP_ANON +#define GETPAGESIZE()65536 +#define MAX_NACL_GC_THREADS 1024 +#endif +#ifdef VAX +#define MACH_TYPE "VAX" +#define ALIGNMENT 4 +extern char etext[]; +#define DATASTART ((ptr_t)(etext)) +#ifdef BSD +#define OS_TYPE "BSD" +#define HEURISTIC1 +#endif +#ifdef ULTRIX +#define OS_TYPE "ULTRIX" +#define STACKBOTTOM ((ptr_t)0x7fffc800) +#endif +#endif +#ifdef SPARC +#define MACH_TYPE "SPARC" +#if defined(__arch64__)||defined(__sparcv9) +#define ALIGNMENT 8 +#define CPP_WORDSZ 64 +#define ELF_CLASS ELFCLASS64 +#else +#define ALIGNMENT 4 +#define CPP_WORDSZ 32 +#endif +#ifdef SOLARIS +#define OS_TYPE "SOLARIS" +extern int _etext[]; +extern int _end[]; +ptr_t GC_SysVGetDataStart(size_t,ptr_t); +#define DATASTART GC_SysVGetDataStart(0x10000,(ptr_t)_etext) +#define DATASTART_IS_FUNC +#define DATAEND ((ptr_t)(_end)) +#if!defined(USE_MMAP)&&defined(REDIRECT_MALLOC) +#define USE_MMAP 1 +#endif +#ifdef USE_MMAP +#define HEAP_START (ptr_t)0x40000000 +#else +#define HEAP_START DATAEND +#endif +#define PROC_VDB +EXTERN_C_END +#include +#include +EXTERN_C_BEGIN +#ifdef USERLIMIT +#define STACKBOTTOM ((ptr_t)USRSTACK) +#else +#define HEURISTIC2 +#endif +#define GETPAGESIZE()(unsigned)sysconf(_SC_PAGESIZE) +#define DYNAMIC_LOADING +#endif +#ifdef DRSNX +#define OS_TYPE "DRSNX" +extern int etext[]; +ptr_t GC_SysVGetDataStart(size_t,ptr_t); +#define DATASTART GC_SysVGetDataStart(0x10000,(ptr_t)etext) +#define DATASTART_IS_FUNC +#define MPROTECT_VDB +#define STACKBOTTOM ((ptr_t)0xdfff0000) +#define DYNAMIC_LOADING +#endif +#ifdef LINUX +#define OS_TYPE "LINUX" +#ifdef __ELF__ +#define DYNAMIC_LOADING +#elif!defined(CPPCHECK) +#error Linux SPARC a.out not supported +#endif +#define COUNT_UNMAPPED_REGIONS +extern int _end[]; +extern int _etext[]; +#define DATAEND ((ptr_t)(_end)) +#define SVR4 +ptr_t GC_SysVGetDataStart(size_t,ptr_t); +#ifdef __arch64__ +#define DATASTART GC_SysVGetDataStart(0x100000,(ptr_t)_etext) +#else +#define DATASTART GC_SysVGetDataStart(0x10000,(ptr_t)_etext) +#endif +#define DATASTART_IS_FUNC +#define LINUX_STACKBOTTOM +#endif +#ifdef OPENBSD +#define OS_TYPE "OPENBSD" +#ifndef GC_OPENBSD_THREADS +#define HEURISTIC2 +#endif +extern int __data_start[]; +#define DATASTART ((ptr_t)__data_start) +extern int _end[]; +#define DATAEND ((ptr_t)(&_end)) +#define DYNAMIC_LOADING +#endif +#ifdef NETBSD +#define OS_TYPE "NETBSD" +#define HEURISTIC2 +#ifdef __ELF__ +extern ptr_t GC_data_start; +#define DATASTART GC_data_start +#define DYNAMIC_LOADING +#else +extern char etext[]; +#define DATASTART ((ptr_t)(etext)) +#endif +#endif +#ifdef FREEBSD +#define OS_TYPE "FREEBSD" +#define SIG_SUSPEND SIGUSR1 +#define SIG_THR_RESTART SIGUSR2 +#define FREEBSD_STACKBOTTOM +#define DYNAMIC_LOADING +extern char etext[]; +extern char edata[]; +#if!defined(CPPCHECK) +extern char end[]; +#endif +#define NEED_FIND_LIMIT +#define DATASTART ((ptr_t)(&etext)) +void*GC_find_limit(void*,int); +#define DATAEND (ptr_t)GC_find_limit(DATASTART,TRUE) +#define DATAEND_IS_FUNC +#define GC_HAVE_DATAREGION2 +#define DATASTART2 ((ptr_t)(&edata)) +#define DATAEND2 ((ptr_t)(&end)) +#endif +#endif +#ifdef I386 +#define MACH_TYPE "I386" +#if (defined(__LP64__)||defined(_WIN64))&&!defined(CPPCHECK) +#error This should be handled as X86_64 +#else +#define CPP_WORDSZ 32 +#define ALIGNMENT 4 +#endif +#ifdef SEQUENT +#define OS_TYPE "SEQUENT" +extern int etext[]; +#define DATASTART ((ptr_t)((((word)(etext))+0xfff)&~0xfff)) +#define STACKBOTTOM ((ptr_t)0x3ffff000) +#endif +#if defined(__EMSCRIPTEN__) +#define OS_TYPE "EMSCRIPTEN" +#define DATASTART (ptr_t)ALIGNMENT +#define DATAEND (ptr_t)ALIGNMENT +#define USE_MMAP_ANON +#define STACK_GROWS_DOWN +#endif +#if defined(__QNX__) +#define OS_TYPE "QNX" +#define SA_RESTART 0 +#define HEURISTIC1 +extern char etext[]; +extern int _end[]; +#define DATASTART ((ptr_t)etext) +#define DATAEND ((ptr_t)_end) +#endif +#ifdef HAIKU +#define OS_TYPE "HAIKU" +EXTERN_C_END +#include +EXTERN_C_BEGIN +#define GETPAGESIZE()(unsigned)B_PAGE_SIZE +extern int etext[]; +#define DATASTART ((ptr_t)((((word)(etext))+0xfff)&~0xfff)) +#define DYNAMIC_LOADING +#define MPROTECT_VDB +#endif +#ifdef SOLARIS +#define OS_TYPE "SOLARIS" +extern int _etext[],_end[]; +ptr_t GC_SysVGetDataStart(size_t,ptr_t); +#define DATASTART GC_SysVGetDataStart(0x1000,(ptr_t)_etext) +#define DATASTART_IS_FUNC +#define DATAEND ((ptr_t)(_end)) +EXTERN_C_END +#include +EXTERN_C_BEGIN +#ifdef USERLIMIT +#define STACKBOTTOM ((ptr_t)USRSTACK) +#else +#define HEURISTIC2 +#endif +#ifdef SOLARIS25_PROC_VDB_BUG_FIXED +#define PROC_VDB +#endif +#ifndef GC_THREADS +#define MPROTECT_VDB +#endif +#define DYNAMIC_LOADING +#if!defined(USE_MMAP)&&defined(REDIRECT_MALLOC) +#define USE_MMAP 1 +#endif +#ifdef USE_MMAP +#define HEAP_START (ptr_t)0x40000000 +#else +#define HEAP_START DATAEND +#endif +#endif +#ifdef SCO +#define OS_TYPE "SCO" +extern int etext[]; +#define DATASTART ((ptr_t)((((word)(etext))+0x3fffff)&~0x3fffff)+((word)(etext)&0xfff)) +#define STACKBOTTOM ((ptr_t)0x7ffffffc) +#endif +#ifdef SCO_ELF +#define OS_TYPE "SCO_ELF" +extern int etext[]; +#define DATASTART ((ptr_t)(etext)) +#define STACKBOTTOM ((ptr_t)0x08048000) +#define DYNAMIC_LOADING +#define ELF_CLASS ELFCLASS32 +#endif +#ifdef DGUX +#define OS_TYPE "DGUX" +extern int _etext,_end; +ptr_t GC_SysVGetDataStart(size_t,ptr_t); +#define DATASTART GC_SysVGetDataStart(0x1000,(ptr_t)(&_etext)) +#define DATASTART_IS_FUNC +#define DATAEND ((ptr_t)(&_end)) +#define STACK_GROWS_DOWN +#define HEURISTIC2 +EXTERN_C_END +#include +EXTERN_C_BEGIN +#define GETPAGESIZE()(unsigned)sysconf(_SC_PAGESIZE) +#define DYNAMIC_LOADING +#ifndef USE_MMAP +#define USE_MMAP 1 +#endif +#define MAP_FAILED (void*)((word)-1) +#define HEAP_START (ptr_t)0x40000000 +#endif +#ifdef LINUX +#define OS_TYPE "LINUX" +#define LINUX_STACKBOTTOM +#define COUNT_UNMAPPED_REGIONS +#if!defined(REDIRECT_MALLOC) +#define MPROTECT_VDB +#else +#endif +#define HEAP_START (ptr_t)0x1000 +#ifdef __ELF__ +#define DYNAMIC_LOADING +EXTERN_C_END +#include +EXTERN_C_BEGIN +#if defined(__GLIBC__)&&__GLIBC__>=2||defined(HOST_ANDROID)||defined(HOST_TIZEN) +#define SEARCH_FOR_DATA_START +#else +extern char**__environ; +#define DATASTART ((ptr_t)(&__environ)) +#endif +extern int _end[]; +#define DATAEND ((ptr_t)(_end)) +#if!defined(GC_NO_SIGSETJMP)&&(defined(HOST_TIZEN)||(defined(HOST_ANDROID)&&!(GC_GNUC_PREREQ(4,8)||GC_CLANG_PREREQ(3,2)||__ANDROID_API__>=18))) +#define GC_NO_SIGSETJMP 1 +#endif +#else +extern int etext[]; +#define DATASTART ((ptr_t)((((word)(etext))+0xfff)&~0xfff)) +#endif +#ifdef USE_I686_PREFETCH +#define PREFETCH(x)__asm__ __volatile__ ("prefetchnta %0"::"m"(*(char*)(x))) +#ifdef FORCE_WRITE_PREFETCH +#define GC_PREFETCH_FOR_WRITE(x)__asm__ __volatile__ ("prefetcht0 %0"::"m"(*(char*)(x))) +#else +#define GC_NO_PREFETCH_FOR_WRITE +#endif +#elif defined(USE_3DNOW_PREFETCH) +#define PREFETCH(x)__asm__ __volatile__ ("prefetch %0"::"m"(*(char*)(x))) +#define GC_PREFETCH_FOR_WRITE(x)__asm__ __volatile__ ("prefetchw %0"::"m"(*(char*)(x))) +#endif +#if defined(__GLIBC__)&&!defined(__UCLIBC__) +#define GLIBC_2_19_TSX_BUG +EXTERN_C_END +#include +EXTERN_C_BEGIN +#endif +#endif +#ifdef CYGWIN32 +#define OS_TYPE "CYGWIN32" +#define WOW64_THREAD_CONTEXT_WORKAROUND +#define RETRY_GET_THREAD_CONTEXT +#define DATASTART ((ptr_t)GC_DATASTART) +#define DATAEND ((ptr_t)GC_DATAEND) +#ifdef USE_WINALLOC +#define GWW_VDB +#else +# +#ifdef USE_MMAP +#define NEED_FIND_LIMIT +#define USE_MMAP_ANON +#endif +#endif +#endif +#ifdef INTERIX +#define OS_TYPE "INTERIX" +extern int _data_start__[]; +extern int _bss_end__[]; +#define DATASTART ((ptr_t)_data_start__) +#define DATAEND ((ptr_t)_bss_end__) +#define STACKBOTTOM ({ ptr_t rv;__asm__ __volatile__ ("movl %%fs:4,%%eax":"=a" (rv));rv;}) +#define USE_MMAP_ANON +#endif +#ifdef OS2 +#define OS_TYPE "OS2" +#define DATAEND +#endif +#ifdef MSWIN32 +#define OS_TYPE "MSWIN32" +#define WOW64_THREAD_CONTEXT_WORKAROUND +#define RETRY_GET_THREAD_CONTEXT +#define MPROTECT_VDB +#define GWW_VDB +#define DATAEND +#endif +#ifdef MSWINCE +#define OS_TYPE "MSWINCE" +#define DATAEND +#endif +#ifdef DJGPP +#define OS_TYPE "DJGPP" +EXTERN_C_END +#include "stubinfo.h" +EXTERN_C_BEGIN +extern int etext[]; +extern int _stklen; +extern int __djgpp_stack_limit; +#define DATASTART ((ptr_t)((((word)(etext))+0x1ff)&~0x1ff)) +#define STACKBOTTOM ((ptr_t)((word)__djgpp_stack_limit+_stklen)) +#endif +#ifdef OPENBSD +#define OS_TYPE "OPENBSD" +#ifndef GC_OPENBSD_THREADS +#define HEURISTIC2 +#endif +extern int __data_start[]; +#define DATASTART ((ptr_t)__data_start) +extern int _end[]; +#define DATAEND ((ptr_t)(&_end)) +#define DYNAMIC_LOADING +#endif +#ifdef FREEBSD +#define OS_TYPE "FREEBSD" +#ifndef GC_FREEBSD_THREADS +#define MPROTECT_VDB +#endif +#ifdef __GLIBC__ +#define SIG_SUSPEND (32+6) +#define SIG_THR_RESTART (32+5) +extern int _end[]; +#define DATAEND ((ptr_t)(_end)) +#else +#define SIG_SUSPEND SIGUSR1 +#define SIG_THR_RESTART SIGUSR2 +#endif +#define FREEBSD_STACKBOTTOM +#ifdef __ELF__ +#define DYNAMIC_LOADING +#endif +extern char etext[]; +#define DATASTART GC_FreeBSDGetDataStart(0x1000,(ptr_t)etext) +#define DATASTART_USES_BSDGETDATASTART +#endif +#ifdef NETBSD +#define OS_TYPE "NETBSD" +#ifdef __ELF__ +#define DYNAMIC_LOADING +#endif +#endif +#ifdef THREE86BSD +#define OS_TYPE "THREE86BSD" +#endif +#ifdef BSDI +#define OS_TYPE "BSDI" +#endif +#if defined(NETBSD)||defined(THREE86BSD)||defined(BSDI) +#define HEURISTIC2 +extern char etext[]; +#define DATASTART ((ptr_t)(etext)) +#endif +#ifdef NEXT +#define OS_TYPE "NEXT" +#define DATASTART ((ptr_t)get_etext()) +#define DATASTART_IS_FUNC +#define STACKBOTTOM ((ptr_t)0xc0000000) +#define DATAEND +#endif +#ifdef RTEMS +#define OS_TYPE "RTEMS" +EXTERN_C_END +#include +EXTERN_C_BEGIN +extern int etext[]; +void*rtems_get_stack_bottom(void); +#define InitStackBottom rtems_get_stack_bottom() +#define DATASTART ((ptr_t)etext) +#define STACKBOTTOM ((ptr_t)InitStackBottom) +#define SIG_SUSPEND SIGUSR1 +#define SIG_THR_RESTART SIGUSR2 +#endif +#ifdef DOS4GW +#define OS_TYPE "DOS4GW" +extern long __nullarea; +extern char _end; +extern char*_STACKTOP; +#pragma aux __nullarea "*"; +#pragma aux _end "*"; +#define STACKBOTTOM ((ptr_t)_STACKTOP) +#define DATASTART ((ptr_t)(&__nullarea)) +#define DATAEND ((ptr_t)(&_end)) +#endif +#ifdef HURD +#define OS_TYPE "HURD" +#define STACK_GROWS_DOWN +#define HEURISTIC2 +#define SIG_SUSPEND SIGUSR1 +#define SIG_THR_RESTART SIGUSR2 +#define SEARCH_FOR_DATA_START +extern int _end[]; +#define DATAEND ((ptr_t)(_end)) +#define DYNAMIC_LOADING +#define USE_MMAP_ANON +#endif +#ifdef DARWIN +#define OS_TYPE "DARWIN" +#define DARWIN_DONT_PARSE_STACK 1 +#define DYNAMIC_LOADING +#define DATASTART ((ptr_t)get_etext()) +#define DATAEND ((ptr_t)get_end()) +#define STACKBOTTOM ((ptr_t)0xc0000000) +#define USE_MMAP_ANON +#define MPROTECT_VDB +EXTERN_C_END +#include +EXTERN_C_BEGIN +#define GETPAGESIZE()(unsigned)getpagesize() +#define NO_PTHREAD_TRYLOCK +#if TARGET_OS_IPHONE&&!defined(NO_DYLD_BIND_FULLY_IMAGE) +#define NO_DYLD_BIND_FULLY_IMAGE +#endif +#endif +#endif +#ifdef NS32K +#define MACH_TYPE "NS32K" +#define ALIGNMENT 4 +extern char**environ; +#define DATASTART ((ptr_t)(&environ)) +#define STACKBOTTOM ((ptr_t)0xfffff000) +#endif +#ifdef MIPS +#define MACH_TYPE "MIPS" +#ifdef LINUX +#define OS_TYPE "LINUX" +#define DYNAMIC_LOADING +#define COUNT_UNMAPPED_REGIONS +extern int _end[]; +#pragma weak __data_start +extern int __data_start[]; +#define DATASTART ((ptr_t)(__data_start)) +#define DATAEND ((ptr_t)(_end)) +#ifdef _MIPS_SZPTR +#define CPP_WORDSZ _MIPS_SZPTR +#define ALIGNMENT (_MIPS_SZPTR/8) +#else +#define ALIGNMENT 4 +#endif +#ifndef HBLKSIZE +#define HBLKSIZE 4096 +#endif +#if __GLIBC__==2&&__GLIBC_MINOR__>=2||__GLIBC__ > 2 +#define LINUX_STACKBOTTOM +#else +#define STACKBOTTOM ((ptr_t)0x7fff8000) +#endif +#endif +#ifdef EWS4800 +#define HEURISTIC2 +#if defined(_MIPS_SZPTR)&&(_MIPS_SZPTR==64) +extern int _fdata[],_end[]; +#define DATASTART ((ptr_t)_fdata) +#define DATAEND ((ptr_t)_end) +#define CPP_WORDSZ _MIPS_SZPTR +#define ALIGNMENT (_MIPS_SZPTR/8) +#else +extern int etext[],edata[]; +#if!defined(CPPCHECK) +extern int end[]; +#endif +extern int _DYNAMIC_LINKING[],_gp[]; +#define DATASTART ((ptr_t)((((word)(etext)+0x3ffff)&~0x3ffff)+((word)(etext)&0xffff))) +#define DATAEND ((ptr_t)(edata)) +#define GC_HAVE_DATAREGION2 +#define DATASTART2 (_DYNAMIC_LINKING?(ptr_t)(((word)_gp+0x8000+0x3ffff)&~0x3ffff):(ptr_t)edata) +#define DATAEND2 ((ptr_t)(end)) +#define ALIGNMENT 4 +#endif +#define OS_TYPE "EWS4800" +#endif +#ifdef ULTRIX +#define HEURISTIC2 +#define DATASTART ((ptr_t)0x10000000) +#define OS_TYPE "ULTRIX" +#define ALIGNMENT 4 +#endif +#ifdef IRIX5 +#define HEURISTIC2 +extern int _fdata[]; +#define DATASTART ((ptr_t)(_fdata)) +#ifdef USE_MMAP +#define HEAP_START (ptr_t)0x30000000 +#else +#define HEAP_START DATASTART +#endif +#define OS_TYPE "IRIX5" +#ifdef _MIPS_SZPTR +#define CPP_WORDSZ _MIPS_SZPTR +#define ALIGNMENT (_MIPS_SZPTR/8) +#else +#define ALIGNMENT 4 +#endif +#define DYNAMIC_LOADING +#endif +#ifdef MSWINCE +#define OS_TYPE "MSWINCE" +#define ALIGNMENT 4 +#define DATAEND +#endif +#ifdef NETBSD +#define OS_TYPE "NETBSD" +#define ALIGNMENT 4 +#define HEURISTIC2 +#ifdef __ELF__ +extern ptr_t GC_data_start; +#define DATASTART GC_data_start +#define NEED_FIND_LIMIT +#define DYNAMIC_LOADING +#else +#define DATASTART ((ptr_t)0x10000000) +#define STACKBOTTOM ((ptr_t)0x7ffff000) +#endif +#endif +#ifdef OPENBSD +#define OS_TYPE "OPENBSD" +#define CPP_WORDSZ 64 +#define ALIGNMENT 8 +#ifndef GC_OPENBSD_THREADS +#define HEURISTIC2 +#endif +extern int __data_start[]; +#define DATASTART ((ptr_t)__data_start) +extern int _end[]; +#define DATAEND ((ptr_t)(&_end)) +#define DYNAMIC_LOADING +#endif +#ifdef FREEBSD +#define OS_TYPE "FREEBSD" +#define ALIGNMENT 4 +#ifndef GC_FREEBSD_THREADS +#define MPROTECT_VDB +#endif +#define SIG_SUSPEND SIGUSR1 +#define SIG_THR_RESTART SIGUSR2 +#define FREEBSD_STACKBOTTOM +#define DYNAMIC_LOADING +extern char etext[]; +#define DATASTART GC_FreeBSDGetDataStart(0x1000,(ptr_t)etext) +#define DATASTART_USES_BSDGETDATASTART +#endif +#if defined(NONSTOP) +#define CPP_WORDSZ 32 +#define OS_TYPE "NONSTOP" +#define ALIGNMENT 4 +#define DATASTART ((ptr_t)0x08000000) +extern char**environ; +#define DATAEND ((ptr_t)(environ - 0x10)) +#define STACKBOTTOM ((ptr_t)0x4fffffff) +#endif +#endif +#ifdef NIOS2 +#define CPP_WORDSZ 32 +#define MACH_TYPE "NIOS2" +#ifdef LINUX +#define OS_TYPE "LINUX" +#define DYNAMIC_LOADING +#define COUNT_UNMAPPED_REGIONS +extern int _end[]; +extern int __data_start[]; +#define DATASTART ((ptr_t)(__data_start)) +#define DATAEND ((ptr_t)(_end)) +#define ALIGNMENT 4 +#ifndef HBLKSIZE +#define HBLKSIZE 4096 +#endif +#define LINUX_STACKBOTTOM +#endif +#endif +#ifdef OR1K +#define CPP_WORDSZ 32 +#define MACH_TYPE "OR1K" +#ifdef LINUX +#define OS_TYPE "LINUX" +#define DYNAMIC_LOADING +#define COUNT_UNMAPPED_REGIONS +extern int _end[]; +extern int __data_start[]; +#define DATASTART ((ptr_t)(__data_start)) +#define DATAEND ((ptr_t)(_end)) +#define ALIGNMENT 4 +#ifndef HBLKSIZE +#define HBLKSIZE 4096 +#endif +#define LINUX_STACKBOTTOM +#endif +#endif +#ifdef HP_PA +#define MACH_TYPE "HP_PA" +#ifdef __LP64__ +#define CPP_WORDSZ 64 +#define ALIGNMENT 8 +#else +#define CPP_WORDSZ 32 +#define ALIGNMENT 4 +#endif +#if!defined(GC_HPUX_THREADS)&&!defined(GC_LINUX_THREADS)&&!defined(OPENBSD)&&!defined(LINUX) +#define MPROTECT_VDB +#endif +#define STACK_GROWS_UP +#ifdef HPUX +#define OS_TYPE "HPUX" +extern int __data_start[]; +#define DATASTART ((ptr_t)(__data_start)) +#ifdef USE_MMAP +#define USE_MMAP_ANON +#endif +#ifdef USE_HPUX_FIXED_STACKBOTTOM +#define STACKBOTTOM ((ptr_t)0x7b033000) +#elif defined(USE_ENVIRON_POINTER) +extern char**environ; +#define STACKBOTTOM ((ptr_t)environ) +#elif!defined(HEURISTIC2) +#define HPUX_MAIN_STACKBOTTOM +#define NEED_FIND_LIMIT +#endif +#define DYNAMIC_LOADING +EXTERN_C_END +#include +EXTERN_C_BEGIN +#define GETPAGESIZE()(unsigned)sysconf(_SC_PAGE_SIZE) +#ifndef __GNUC__ +#define PREFETCH(x)do { register long addr=(long)(x);(void)_asm ("LDW",0,0,addr,0);} while (0) +#endif +#endif +#ifdef LINUX +#define OS_TYPE "LINUX" +#define LINUX_STACKBOTTOM +#define COUNT_UNMAPPED_REGIONS +#define DYNAMIC_LOADING +#define SEARCH_FOR_DATA_START +extern int _end[]; +#define DATAEND ((ptr_t)(&_end)) +#endif +#ifdef OPENBSD +#define OS_TYPE "OPENBSD" +#ifndef GC_OPENBSD_THREADS +#define HEURISTIC2 +#endif +extern int __data_start[]; +#define DATASTART ((ptr_t)__data_start) +extern int _end[]; +#define DATAEND ((ptr_t)(&_end)) +#define DYNAMIC_LOADING +#endif +#endif +#ifdef ALPHA +#define MACH_TYPE "ALPHA" +#define ALIGNMENT 8 +#define CPP_WORDSZ 64 +#ifdef NETBSD +#define OS_TYPE "NETBSD" +#define HEURISTIC2 +extern ptr_t GC_data_start; +#define DATASTART GC_data_start +#define ELFCLASS32 32 +#define ELFCLASS64 64 +#define ELF_CLASS ELFCLASS64 +#define DYNAMIC_LOADING +#endif +#ifdef OPENBSD +#define OS_TYPE "OPENBSD" +#ifndef GC_OPENBSD_THREADS +#define HEURISTIC2 +#endif +extern int __data_start[]; +#define DATASTART ((ptr_t)__data_start) +extern int _end[]; +#define DATAEND ((ptr_t)(&_end)) +#define DYNAMIC_LOADING +#endif +#ifdef FREEBSD +#define OS_TYPE "FREEBSD" +#define SIG_SUSPEND SIGUSR1 +#define SIG_THR_RESTART SIGUSR2 +#define FREEBSD_STACKBOTTOM +#define DYNAMIC_LOADING +extern char etext[]; +extern char edata[]; +#if!defined(CPPCHECK) +extern char end[]; +#endif +#define NEED_FIND_LIMIT +#define DATASTART ((ptr_t)(&etext)) +void*GC_find_limit(void*,int); +#define DATAEND (ptr_t)GC_find_limit(DATASTART,TRUE) +#define DATAEND_IS_FUNC +#define GC_HAVE_DATAREGION2 +#define DATASTART2 ((ptr_t)(&edata)) +#define DATAEND2 ((ptr_t)(&end)) +#endif +#ifdef OSF1 +#define OS_TYPE "OSF1" +#define DATASTART ((ptr_t)0x140000000) +extern int _end[]; +#define DATAEND ((ptr_t)(&_end)) +extern char**environ; +#define STACKBOTTOM ((ptr_t)(((word)(environ)|(getpagesize()-1))+1)) +extern int __start[]; +#define HEURISTIC2_LIMIT ((ptr_t)((word)(__start)&~(getpagesize()-1))) +#ifndef GC_OSF1_THREADS +#define MPROTECT_VDB +#endif +#define DYNAMIC_LOADING +#endif +#ifdef LINUX +#define OS_TYPE "LINUX" +#define LINUX_STACKBOTTOM +#define COUNT_UNMAPPED_REGIONS +#ifdef __ELF__ +#define SEARCH_FOR_DATA_START +#define DYNAMIC_LOADING +#else +#define DATASTART ((ptr_t)0x140000000) +#endif +extern int _end[]; +#define DATAEND ((ptr_t)(_end)) +#if!defined(REDIRECT_MALLOC) +#define MPROTECT_VDB +#endif +#endif +#endif +#ifdef IA64 +#define MACH_TYPE "IA64" +#ifdef HPUX +#ifdef _ILP32 +#define CPP_WORDSZ 32 +#define ALIGNMENT 4 +#else +#if!defined(_LP64)&&!defined(CPPCHECK) +#error Unknown ABI +#endif +#define CPP_WORDSZ 64 +#define ALIGNMENT 8 +#endif +#define OS_TYPE "HPUX" +extern int __data_start[]; +#define DATASTART ((ptr_t)(__data_start)) +#ifdef USE_MMAP +#define USE_MMAP_ANON +#endif +extern char**environ; +#define STACKBOTTOM ((ptr_t)environ) +#define HPUX_STACKBOTTOM +#define DYNAMIC_LOADING +EXTERN_C_END +#include +EXTERN_C_BEGIN +#define GETPAGESIZE()(unsigned)sysconf(_SC_PAGE_SIZE) +#define BACKING_STORE_DISPLACEMENT 0x1000000 +#define BACKING_STORE_ALIGNMENT 0x1000 +extern ptr_t GC_register_stackbottom; +#define BACKING_STORE_BASE GC_register_stackbottom +#endif +#ifdef LINUX +#define CPP_WORDSZ 64 +#define ALIGNMENT 8 +#define OS_TYPE "LINUX" +#define LINUX_STACKBOTTOM +extern ptr_t GC_register_stackbottom; +#define BACKING_STORE_BASE GC_register_stackbottom +#define COUNT_UNMAPPED_REGIONS +#define SEARCH_FOR_DATA_START +#ifdef __GNUC__ +#define DYNAMIC_LOADING +#else +#endif +#if!defined(REDIRECT_MALLOC) +#define MPROTECT_VDB +#endif +extern int _end[]; +#define DATAEND ((ptr_t)(_end)) +#ifdef __GNUC__ +#ifndef __INTEL_COMPILER +#define PREFETCH(x)__asm__ (" lfetch [%0]"::"r"(x)) +#define GC_PREFETCH_FOR_WRITE(x)__asm__ (" lfetch.excl [%0]"::"r"(x)) +#define CLEAR_DOUBLE(x)__asm__ (" stf.spill [%0]=f0"::"r"((void*)(x))) +#else +EXTERN_C_END +#include +EXTERN_C_BEGIN +#define PREFETCH(x)__lfetch(__lfhint_none,(x)) +#define GC_PREFETCH_FOR_WRITE(x)__lfetch(__lfhint_nta,(x)) +#define CLEAR_DOUBLE(x)__stf_spill((void*)(x),0) +#endif +#endif +#endif +#ifdef MSWIN32 +#define OS_TYPE "MSWIN32" +#define DATAEND +#if defined(_WIN64) +#define CPP_WORDSZ 64 +#else +#define CPP_WORDSZ 32 +#endif +#define ALIGNMENT 8 +#endif +#endif +#ifdef M88K +#define MACH_TYPE "M88K" +#define ALIGNMENT 4 +extern int etext[]; +#ifdef CX_UX +#define OS_TYPE "CX_UX" +#define DATASTART ((ptr_t)((((word)(etext)+0x3fffff)&~0x3fffff)+0x10000)) +#endif +#ifdef DGUX +#define OS_TYPE "DGUX" +ptr_t GC_SysVGetDataStart(size_t,ptr_t); +#define DATASTART GC_SysVGetDataStart(0x10000,(ptr_t)etext) +#define DATASTART_IS_FUNC +#endif +#define STACKBOTTOM ((char*)0xf0000000) +#endif +#ifdef S370 +#define MACH_TYPE "S370" +#define ALIGNMENT 4 +#ifdef UTS4 +#define OS_TYPE "UTS4" +extern int etext[]; +extern int _etext[]; +extern int _end[]; +ptr_t GC_SysVGetDataStart(size_t,ptr_t); +#define DATASTART GC_SysVGetDataStart(0x10000,(ptr_t)_etext) +#define DATASTART_IS_FUNC +#define DATAEND ((ptr_t)(_end)) +#define HEURISTIC2 +#endif +#endif +#ifdef S390 +#define MACH_TYPE "S390" +#ifndef __s390x__ +#define ALIGNMENT 4 +#define CPP_WORDSZ 32 +#else +#define ALIGNMENT 8 +#define CPP_WORDSZ 64 +#ifndef HBLKSIZE +#define HBLKSIZE 4096 +#endif +#endif +#ifdef LINUX +#define OS_TYPE "LINUX" +#define LINUX_STACKBOTTOM +#define DYNAMIC_LOADING +#define COUNT_UNMAPPED_REGIONS +extern int __data_start[] __attribute__((__weak__)); +#define DATASTART ((ptr_t)(__data_start)) +extern int _end[] __attribute__((__weak__)); +#define DATAEND ((ptr_t)(_end)) +#define CACHE_LINE_SIZE 256 +#define GETPAGESIZE()4096 +#endif +#endif +#ifdef AARCH64 +#define MACH_TYPE "AARCH64" +#ifdef __ILP32__ +#define CPP_WORDSZ 32 +#define ALIGNMENT 4 +#else +#define CPP_WORDSZ 64 +#define ALIGNMENT 8 +#endif +#ifndef HBLKSIZE +#define HBLKSIZE 4096 +#endif +#ifdef LINUX +#define OS_TYPE "LINUX" +#define LINUX_STACKBOTTOM +#define COUNT_UNMAPPED_REGIONS +#if!defined(REDIRECT_MALLOC) +#define MPROTECT_VDB +#endif +#define DYNAMIC_LOADING +#if defined(HOST_ANDROID) +#define SEARCH_FOR_DATA_START +#else +extern int __data_start[]; +#define DATASTART ((ptr_t)__data_start) +#endif +extern int _end[]; +#define DATAEND ((ptr_t)(&_end)) +#endif +#ifdef DARWIN +#define OS_TYPE "DARWIN" +#define DARWIN_DONT_PARSE_STACK 1 +#define DYNAMIC_LOADING +#define DATASTART ((ptr_t)get_etext()) +#define DATAEND ((ptr_t)get_end()) +#define STACKBOTTOM ((ptr_t)0x16fdfffff) +#define USE_MMAP_ANON +EXTERN_C_END +#include +EXTERN_C_BEGIN +#define GETPAGESIZE()(unsigned)getpagesize() +#define NO_PTHREAD_TRYLOCK +#if TARGET_OS_IPHONE&&!defined(NO_DYLD_BIND_FULLY_IMAGE) +#define NO_DYLD_BIND_FULLY_IMAGE +#endif +#endif +#ifdef FREEBSD +#define OS_TYPE "FREEBSD" +#ifndef GC_FREEBSD_THREADS +#define MPROTECT_VDB +#endif +#define FREEBSD_STACKBOTTOM +#define DYNAMIC_LOADING +extern char etext[]; +#define DATASTART GC_FreeBSDGetDataStart(0x1000,(ptr_t)etext) +#define DATASTART_USES_BSDGETDATASTART +#endif +#ifdef NETBSD +#define OS_TYPE "NETBSD" +#define HEURISTIC2 +extern ptr_t GC_data_start; +#define DATASTART GC_data_start +#define ELF_CLASS ELFCLASS64 +#define DYNAMIC_LOADING +#endif +#ifdef OPENBSD +#define OS_TYPE "OPENBSD" +#ifndef GC_OPENBSD_THREADS +#define HEURISTIC2 +#endif +extern int __data_start[]; +#define DATASTART ((ptr_t)__data_start) +extern int _end[]; +#define DATAEND ((ptr_t)(&_end)) +#define DYNAMIC_LOADING +#endif +#ifdef NINTENDO_SWITCH +#define OS_TYPE "NINTENDO_SWITCH" +extern int __bss_end[]; +#define NO_HANDLE_FORK 1 +#define DATASTART (ptr_t)ALIGNMENT +#define DATAEND (ptr_t)(&__bss_end) +void*switch_get_stack_bottom(void); +#define STACKBOTTOM ((ptr_t)switch_get_stack_bottom()) +#endif +#ifdef MSWIN32 +#define OS_TYPE "MSWIN32" +#ifndef DATAEND +#define DATAEND +#endif +#endif +#ifdef NOSYS +extern int __data_start[]; +#define DATASTART ((ptr_t)__data_start) +extern void*__stack_base__; +#define STACKBOTTOM ((ptr_t)__stack_base__) +#endif +#endif +#ifdef ARM32 +#if defined(NACL) +#define MACH_TYPE "NACL" +#else +#define MACH_TYPE "ARM32" +#endif +#define CPP_WORDSZ 32 +#define ALIGNMENT 4 +#ifdef NETBSD +#define OS_TYPE "NETBSD" +#define HEURISTIC2 +#ifdef __ELF__ +extern ptr_t GC_data_start; +#define DATASTART GC_data_start +#define DYNAMIC_LOADING +#else +extern char etext[]; +#define DATASTART ((ptr_t)(etext)) +#endif +#endif +#ifdef LINUX +#define OS_TYPE "LINUX" +#define LINUX_STACKBOTTOM +#define COUNT_UNMAPPED_REGIONS +#if!defined(REDIRECT_MALLOC) +#define MPROTECT_VDB +#endif +#define DYNAMIC_LOADING +EXTERN_C_END +#include +EXTERN_C_BEGIN +#if defined(__GLIBC__)&&__GLIBC__>=2||defined(HOST_ANDROID)||defined(HOST_TIZEN) +#define SEARCH_FOR_DATA_START +#else +extern char**__environ; +#define DATASTART ((ptr_t)(&__environ)) +#endif +extern int _end[]; +#define DATAEND ((ptr_t)(_end)) +#endif +#ifdef MSWINCE +#define OS_TYPE "MSWINCE" +#define DATAEND +#endif +#ifdef FREEBSD +#define OS_TYPE "FREEBSD" +#ifndef GC_FREEBSD_THREADS +#define MPROTECT_VDB +#endif +#define SIG_SUSPEND SIGUSR1 +#define SIG_THR_RESTART SIGUSR2 +#define FREEBSD_STACKBOTTOM +#define DYNAMIC_LOADING +extern char etext[]; +#define DATASTART GC_FreeBSDGetDataStart(0x1000,(ptr_t)etext) +#define DATASTART_USES_BSDGETDATASTART +#endif +#ifdef DARWIN +#define OS_TYPE "DARWIN" +#define DARWIN_DONT_PARSE_STACK 1 +#define DYNAMIC_LOADING +#define DATASTART ((ptr_t)get_etext()) +#define DATAEND ((ptr_t)get_end()) +#define STACKBOTTOM ((ptr_t)0x30000000) +#define USE_MMAP_ANON +EXTERN_C_END +#include +EXTERN_C_BEGIN +#define GETPAGESIZE()(unsigned)getpagesize() +#define NO_PTHREAD_TRYLOCK +#if TARGET_OS_IPHONE&&!defined(NO_DYLD_BIND_FULLY_IMAGE) +#define NO_DYLD_BIND_FULLY_IMAGE +#endif +#endif +#ifdef OPENBSD +#define OS_TYPE "OPENBSD" +#ifndef GC_OPENBSD_THREADS +#define HEURISTIC2 +#endif +extern int __data_start[]; +#define DATASTART ((ptr_t)__data_start) +extern int _end[]; +#define DATAEND ((ptr_t)(&_end)) +#define DYNAMIC_LOADING +#endif +#ifdef SN_TARGET_PSP2 +#define OS_TYPE "SN_TARGET_PSP2" +#define NO_HANDLE_FORK 1 +#define DATASTART (ptr_t)ALIGNMENT +#define DATAEND (ptr_t)ALIGNMENT +void*psp2_get_stack_bottom(void); +#define STACKBOTTOM ((ptr_t)psp2_get_stack_bottom()) +#endif +#ifdef NN_PLATFORM_CTR +#define OS_TYPE "NN_PLATFORM_CTR" +extern unsigned char Image$$ZI$$ZI$$Base[]; +#define DATASTART (ptr_t)(Image$$ZI$$ZI$$Base) +extern unsigned char Image$$ZI$$ZI$$Limit[]; +#define DATAEND (ptr_t)(Image$$ZI$$ZI$$Limit) +void*n3ds_get_stack_bottom(void); +#define STACKBOTTOM ((ptr_t)n3ds_get_stack_bottom()) +#endif +#ifdef MSWIN32 +#define OS_TYPE "MSWIN32" +#ifndef DATAEND +#define DATAEND +#endif +#endif +#ifdef NOSYS +extern int __data_start[]; +#define DATASTART ((ptr_t)(__data_start)) +extern void*__stack_base__; +#define STACKBOTTOM ((ptr_t)(__stack_base__)) +#endif +#endif +#ifdef CRIS +#define MACH_TYPE "CRIS" +#define CPP_WORDSZ 32 +#define ALIGNMENT 1 +#ifdef LINUX +#define OS_TYPE "LINUX" +#define DYNAMIC_LOADING +#define LINUX_STACKBOTTOM +#define COUNT_UNMAPPED_REGIONS +#define SEARCH_FOR_DATA_START +extern int _end[]; +#define DATAEND ((ptr_t)(_end)) +#endif +#endif +#if defined(SH)&&!defined(SH4) +#define MACH_TYPE "SH" +#define ALIGNMENT 4 +#ifdef MSWINCE +#define OS_TYPE "MSWINCE" +#define DATAEND +#endif +#ifdef LINUX +#define OS_TYPE "LINUX" +#define LINUX_STACKBOTTOM +#define COUNT_UNMAPPED_REGIONS +#define DYNAMIC_LOADING +#define SEARCH_FOR_DATA_START +extern int _end[]; +#define DATAEND ((ptr_t)(_end)) +#endif +#ifdef NETBSD +#define OS_TYPE "NETBSD" +#define HEURISTIC2 +extern ptr_t GC_data_start; +#define DATASTART GC_data_start +#define DYNAMIC_LOADING +#endif +#ifdef OPENBSD +#define OS_TYPE "OPENBSD" +#ifndef GC_OPENBSD_THREADS +#define HEURISTIC2 +#endif +extern int __data_start[]; +#define DATASTART ((ptr_t)__data_start) +extern int _end[]; +#define DATAEND ((ptr_t)(&_end)) +#define DYNAMIC_LOADING +#endif +#endif +#ifdef SH4 +#define MACH_TYPE "SH4" +#define ALIGNMENT 4 +#ifdef MSWINCE +#define OS_TYPE "MSWINCE" +#define DATAEND +#endif +#endif +#ifdef AVR32 +#define MACH_TYPE "AVR32" +#define CPP_WORDSZ 32 +#define ALIGNMENT 4 +#ifdef LINUX +#define OS_TYPE "LINUX" +#define DYNAMIC_LOADING +#define LINUX_STACKBOTTOM +#define COUNT_UNMAPPED_REGIONS +#define SEARCH_FOR_DATA_START +extern int _end[]; +#define DATAEND ((ptr_t)(_end)) +#endif +#endif +#ifdef M32R +#define CPP_WORDSZ 32 +#define MACH_TYPE "M32R" +#define ALIGNMENT 4 +#ifdef LINUX +#define OS_TYPE "LINUX" +#define LINUX_STACKBOTTOM +#define COUNT_UNMAPPED_REGIONS +#define DYNAMIC_LOADING +#define SEARCH_FOR_DATA_START +extern int _end[]; +#define DATAEND ((ptr_t)(_end)) +#endif +#endif +#ifdef X86_64 +#define MACH_TYPE "X86_64" +#ifdef __ILP32__ +#define ALIGNMENT 4 +#define CPP_WORDSZ 32 +#else +#define ALIGNMENT 8 +#define CPP_WORDSZ 64 +#endif +#ifndef HBLKSIZE +#define HBLKSIZE 4096 +#endif +#ifndef CACHE_LINE_SIZE +#define CACHE_LINE_SIZE 64 +#endif +#ifdef SN_TARGET_ORBIS +#define OS_TYPE "SN_TARGET_ORBIS" +#define DATASTART (ptr_t)ALIGNMENT +#define DATAEND (ptr_t)ALIGNMENT +void*ps4_get_stack_bottom(void); +#define STACKBOTTOM ((ptr_t)ps4_get_stack_bottom()) +#endif +#ifdef OPENBSD +#define OS_TYPE "OPENBSD" +#ifndef GC_OPENBSD_THREADS +#define HEURISTIC2 +#endif +extern int __data_start[]; +extern int _end[]; +#define DATASTART ((ptr_t)__data_start) +#define DATAEND ((ptr_t)(&_end)) +#define DYNAMIC_LOADING +#endif +#ifdef LINUX +#define OS_TYPE "LINUX" +#define LINUX_STACKBOTTOM +#if!defined(REDIRECT_MALLOC) +#define MPROTECT_VDB +#else +#endif +#define COUNT_UNMAPPED_REGIONS +#define DYNAMIC_LOADING +EXTERN_C_END +#include +EXTERN_C_BEGIN +#define SEARCH_FOR_DATA_START +extern int _end[]; +#define DATAEND ((ptr_t)(_end)) +#if defined(__GLIBC__)&&!defined(__UCLIBC__) +#define USE_MMAP_ANON +#define GETCONTEXT_FPU_EXCMASK_BUG +#define GLIBC_2_19_TSX_BUG +EXTERN_C_END +#include +EXTERN_C_BEGIN +#endif +#endif +#ifdef DARWIN +#define OS_TYPE "DARWIN" +#define DARWIN_DONT_PARSE_STACK 1 +#define DYNAMIC_LOADING +#define DATASTART ((ptr_t)get_etext()) +#define DATAEND ((ptr_t)get_end()) +#define STACKBOTTOM ((ptr_t)0x7fff5fc00000) +#define USE_MMAP_ANON +#define MPROTECT_VDB +EXTERN_C_END +#include +EXTERN_C_BEGIN +#define GETPAGESIZE()(unsigned)getpagesize() +#define NO_PTHREAD_TRYLOCK +#if TARGET_OS_IPHONE&&!defined(NO_DYLD_BIND_FULLY_IMAGE) +#define NO_DYLD_BIND_FULLY_IMAGE +#endif +#endif +#ifdef FREEBSD +#define OS_TYPE "FREEBSD" +#ifndef GC_FREEBSD_THREADS +#define MPROTECT_VDB +#endif +#ifdef __GLIBC__ +#define SIG_SUSPEND (32+6) +#define SIG_THR_RESTART (32+5) +extern int _end[]; +#define DATAEND ((ptr_t)(_end)) +#else +#define SIG_SUSPEND SIGUSR1 +#define SIG_THR_RESTART SIGUSR2 +#endif +#define FREEBSD_STACKBOTTOM +#if defined(__DragonFly__) +#define COUNT_UNMAPPED_REGIONS +#endif +#define DYNAMIC_LOADING +extern char etext[]; +#define DATASTART GC_FreeBSDGetDataStart(0x1000,(ptr_t)etext) +#define DATASTART_USES_BSDGETDATASTART +#endif +#ifdef NETBSD +#define OS_TYPE "NETBSD" +#define HEURISTIC2 +extern ptr_t GC_data_start; +#define DATASTART GC_data_start +#define DYNAMIC_LOADING +#endif +#ifdef HAIKU +#define OS_TYPE "HAIKU" +EXTERN_C_END +#include +EXTERN_C_BEGIN +#define GETPAGESIZE()(unsigned)B_PAGE_SIZE +#define HEURISTIC2 +#define SEARCH_FOR_DATA_START +#define DYNAMIC_LOADING +#define MPROTECT_VDB +#endif +#ifdef SOLARIS +#define OS_TYPE "SOLARIS" +#define ELF_CLASS ELFCLASS64 +extern int _etext[],_end[]; +ptr_t GC_SysVGetDataStart(size_t,ptr_t); +#define DATASTART GC_SysVGetDataStart(0x1000,(ptr_t)_etext) +#define DATASTART_IS_FUNC +#define DATAEND ((ptr_t)(_end)) +EXTERN_C_END +#include +EXTERN_C_BEGIN +#ifdef USERLIMIT +#define STACKBOTTOM ((ptr_t)USRSTACK) +#else +#define HEURISTIC2 +#endif +#ifdef SOLARIS25_PROC_VDB_BUG_FIXED +#define PROC_VDB +#endif +#ifndef GC_THREADS +#define MPROTECT_VDB +#endif +#define DYNAMIC_LOADING +#if!defined(USE_MMAP)&&defined(REDIRECT_MALLOC) +#define USE_MMAP 1 +#endif +#ifdef USE_MMAP +#define HEAP_START (ptr_t)0x40000000 +#else +#define HEAP_START DATAEND +#endif +#endif +#ifdef CYGWIN32 +#define OS_TYPE "CYGWIN32" +#define RETRY_GET_THREAD_CONTEXT +#ifdef USE_WINALLOC +#define GWW_VDB +#else +#if defined(THREAD_LOCAL_ALLOC) +#else +#define MPROTECT_VDB +#endif +#ifdef USE_MMAP +#define USE_MMAP_ANON +#endif +#endif +#endif +#ifdef MSWIN_XBOX1 +#define OS_TYPE "MSWIN_XBOX1" +#define NO_GETENV +#define DATASTART (ptr_t)ALIGNMENT +#define DATAEND (ptr_t)ALIGNMENT +LONG64 durango_get_stack_bottom(void); +#define STACKBOTTOM ((ptr_t)durango_get_stack_bottom()) +#define GETPAGESIZE()4096 +#ifndef USE_MMAP +#define USE_MMAP 1 +#endif +#define PROT_NONE 0 +#define PROT_READ 1 +#define PROT_WRITE 2 +#define PROT_EXEC 4 +#define MAP_PRIVATE 2 +#define MAP_FIXED 0x10 +#define MAP_FAILED ((void*)-1) +#endif +#ifdef MSWIN32 +#define OS_TYPE "MSWIN32" +#define RETRY_GET_THREAD_CONTEXT +#if!defined(__GNUC__)||defined(__INTEL_COMPILER)||GC_GNUC_PREREQ(4,7) +#define MPROTECT_VDB +#endif +#define GWW_VDB +#ifndef DATAEND +#define DATAEND +#endif +#endif +#endif +#ifdef ARC +#define CPP_WORDSZ 32 +#define MACH_TYPE "ARC" +#define ALIGNMENT 4 +#define CACHE_LINE_SIZE 64 +#ifdef LINUX +#define OS_TYPE "LINUX" +#define LINUX_STACKBOTTOM +#define COUNT_UNMAPPED_REGIONS +#define DYNAMIC_LOADING +extern int __data_start[] __attribute__((__weak__)); +#define DATASTART ((ptr_t)__data_start) +#endif +#endif +#ifdef HEXAGON +#define CPP_WORDSZ 32 +#define MACH_TYPE "HEXAGON" +#define ALIGNMENT 4 +#ifdef LINUX +#define OS_TYPE "LINUX" +#define LINUX_STACKBOTTOM +#if!defined(REDIRECT_MALLOC) +#define MPROTECT_VDB +#endif +#define COUNT_UNMAPPED_REGIONS +#define DYNAMIC_LOADING +EXTERN_C_END +#include +EXTERN_C_BEGIN +#if defined(__GLIBC__) +#define SEARCH_FOR_DATA_START +#elif!defined(CPPCHECK) +#error Unknown Hexagon libc configuration +#endif +extern int _end[]; +#define DATAEND ((ptr_t)(_end)) +#endif +#endif +#ifdef TILEPRO +#define CPP_WORDSZ 32 +#define MACH_TYPE "TILEPro" +#define ALIGNMENT 4 +#define PREFETCH(x)__insn_prefetch(x) +#define CACHE_LINE_SIZE 64 +#ifdef LINUX +#define OS_TYPE "LINUX" +extern int __data_start[]; +#define DATASTART ((ptr_t)__data_start) +#define LINUX_STACKBOTTOM +#define COUNT_UNMAPPED_REGIONS +#define DYNAMIC_LOADING +#endif +#endif +#ifdef TILEGX +#define CPP_WORDSZ (__SIZEOF_POINTER__*8) +#define MACH_TYPE "TILE-Gx" +#define ALIGNMENT __SIZEOF_POINTER__ +#if CPP_WORDSZ < 64 +#define CLEAR_DOUBLE(x)(*(long long*)(x)=0) +#endif +#define PREFETCH(x)__insn_prefetch_l1(x) +#define CACHE_LINE_SIZE 64 +#ifdef LINUX +#define OS_TYPE "LINUX" +extern int __data_start[]; +#define DATASTART ((ptr_t)__data_start) +#define LINUX_STACKBOTTOM +#define COUNT_UNMAPPED_REGIONS +#define DYNAMIC_LOADING +#endif +#endif +#ifdef RISCV +#define MACH_TYPE "RISC-V" +#define CPP_WORDSZ __riscv_xlen +#define ALIGNMENT (CPP_WORDSZ/8) +#ifdef FREEBSD +#define OS_TYPE "FREEBSD" +#ifndef GC_FREEBSD_THREADS +#define MPROTECT_VDB +#endif +#define SIG_SUSPEND SIGUSR1 +#define SIG_THR_RESTART SIGUSR2 +#define FREEBSD_STACKBOTTOM +#define DYNAMIC_LOADING +extern char etext[]; +#define DATASTART GC_FreeBSDGetDataStart(0x1000,(ptr_t)etext) +#define DATASTART_USES_BSDGETDATASTART +#endif +#ifdef LINUX +#define OS_TYPE "LINUX" +extern int __data_start[] __attribute__((__weak__)); +#define DATASTART ((ptr_t)__data_start) +#define LINUX_STACKBOTTOM +#define COUNT_UNMAPPED_REGIONS +#define DYNAMIC_LOADING +#endif +#endif +#if defined(__GLIBC__)&&!defined(DONT_USE_LIBC_PRIVATES) +#define USE_LIBC_PRIVATES +#endif +#ifdef NO_RETRY_GET_THREAD_CONTEXT +#undef RETRY_GET_THREAD_CONTEXT +#endif +#if defined(LINUX_STACKBOTTOM)&&defined(NO_PROC_STAT)&&!defined(USE_LIBC_PRIVATES) +#undef LINUX_STACKBOTTOM +#define HEURISTIC2 +#endif +#if defined(USE_MMAP_ANON)&&!defined(USE_MMAP) +#define USE_MMAP 1 +#elif (defined(LINUX)||defined(OPENBSD))&&defined(USE_MMAP) +#define USE_MMAP_ANON +#endif +#if defined(GC_LINUX_THREADS)&&defined(REDIRECT_MALLOC)&&!defined(USE_PROC_FOR_LIBRARIES) +#define USE_PROC_FOR_LIBRARIES +#endif +#ifndef STACK_GROWS_UP +#define STACK_GROWS_DOWN +#endif +#ifndef CPP_WORDSZ +#define CPP_WORDSZ 32 +#endif +#ifndef OS_TYPE +#define OS_TYPE "" +#endif +#ifndef DATAEND +#if!defined(CPPCHECK) +extern int end[]; +#endif +#define DATAEND ((ptr_t)(end)) +#endif +#if defined(HOST_ANDROID)&&defined(__clang__)&&!defined(BROKEN_UUENDUU_SYM) +#undef DATAEND +#pragma weak __end__ +extern int __end__[]; +#define DATAEND (__end__!=0?(ptr_t)__end__:(ptr_t)_end) +#endif +#if (defined(SVR4)||defined(HOST_ANDROID)||defined(HOST_TIZEN))&&!defined(GETPAGESIZE) +EXTERN_C_END +#include +EXTERN_C_BEGIN +#define GETPAGESIZE()(unsigned)sysconf(_SC_PAGESIZE) +#endif +#ifndef GETPAGESIZE +#if defined(SOLARIS)||defined(IRIX5)||defined(LINUX)||defined(NETBSD)||defined(FREEBSD)||defined(HPUX) +EXTERN_C_END +#include +EXTERN_C_BEGIN +#endif +#define GETPAGESIZE()(unsigned)getpagesize() +#endif +#if defined(HOST_ANDROID)&&!(__ANDROID_API__>=23)&&((defined(MIPS)&&(CPP_WORDSZ==32))||defined(ARM32)||defined(I386)) +#define USE_TKILL_ON_ANDROID +#endif +#if defined(SOLARIS)||defined(DRSNX)||defined(UTS4) +#define SVR4 +#endif +#if defined(SOLARIS)||defined(DRSNX) +#define SOLARISDL +#define SUNOS5SIGS +#endif +#if defined(HPUX) +#define SUNOS5SIGS +#endif +#if defined(FREEBSD)&&(defined(__DragonFly__)||__FreeBSD__>=4||(__FreeBSD_kernel__>=4)) +#define SUNOS5SIGS +#endif +#if!defined(GC_EXPLICIT_SIGNALS_UNBLOCK)&&defined(SUNOS5SIGS)&&!defined(GC_NO_PTHREAD_SIGMASK) +#define GC_EXPLICIT_SIGNALS_UNBLOCK +#endif +#if!defined(NO_SIGNALS_UNBLOCK_IN_MAIN)&&defined(GC_NO_PTHREAD_SIGMASK) +#define NO_SIGNALS_UNBLOCK_IN_MAIN +#endif +#if!defined(NO_MARKER_SPECIAL_SIGMASK)&&(defined(NACL)||defined(GC_WIN32_PTHREADS)) +#define NO_MARKER_SPECIAL_SIGMASK +#endif +#ifdef GC_NETBSD_THREADS +#define SIGRTMIN 33 +#define SIGRTMAX 63 +#define GC_NETBSD_THREADS_WORKAROUND +#endif +#ifdef GC_OPENBSD_THREADS +EXTERN_C_END +#include +EXTERN_C_BEGIN +#if OpenBSD < 201211 +#define GC_OPENBSD_UTHREADS 1 +#endif +#endif +#if defined(SVR4)||defined(LINUX)||defined(IRIX5)||defined(HPUX)||defined(OPENBSD)||defined(NETBSD)||defined(FREEBSD)||defined(DGUX)||defined(BSD)||defined(HAIKU)||defined(HURD)||defined(AIX)||defined(DARWIN)||defined(OSF1) +#define UNIX_LIKE +#endif +#if defined(CPPCHECK) +#undef CPP_WORDSZ +#define CPP_WORDSZ (__SIZEOF_POINTER__*8) +#elif CPP_WORDSZ!=32&&CPP_WORDSZ!=64 +#error Bad word size +#endif +#if!defined(ALIGNMENT)&&!defined(CPPCHECK) +#error Undefined ALIGNMENT +#endif +#ifdef PCR +#undef DYNAMIC_LOADING +#undef STACKBOTTOM +#undef HEURISTIC1 +#undef HEURISTIC2 +#undef PROC_VDB +#undef MPROTECT_VDB +#define PCR_VDB +#endif +#if!defined(STACKBOTTOM)&&(defined(ECOS)||defined(NOSYS))&&!defined(CPPCHECK) +#error Undefined STACKBOTTOM +#endif +#ifdef IGNORE_DYNAMIC_LOADING +#undef DYNAMIC_LOADING +#endif +#if defined(SMALL_CONFIG)&&!defined(GC_DISABLE_INCREMENTAL) +#define GC_DISABLE_INCREMENTAL +#endif +#if (defined(MSWIN32)||defined(MSWINCE))&&!defined(USE_WINALLOC) +#define USE_WINALLOC 1 +#endif +#ifdef USE_WINALLOC +#undef USE_MMAP +#endif +#if defined(DARWIN)||defined(FREEBSD)||defined(HAIKU)||defined(IRIX5)||defined(LINUX)||defined(NETBSD)||defined(OPENBSD)||defined(SOLARIS)||((defined(CYGWIN32)||defined(USE_MMAP)||defined(USE_MUNMAP))&&!defined(USE_WINALLOC)) +#define MMAP_SUPPORTED +#endif +#if defined(USE_MUNMAP)&&!defined(MUNMAP_THRESHOLD)&&(defined(SN_TARGET_ORBIS)||defined(SN_TARGET_PS3)||defined(SN_TARGET_PSP2)||defined(MSWIN_XBOX1)) +#define MUNMAP_THRESHOLD 2 +#endif +#if defined(USE_MUNMAP)&&defined(COUNT_UNMAPPED_REGIONS)&&!defined(GC_UNMAPPED_REGIONS_SOFT_LIMIT) +#if defined(__DragonFly__) +#define GC_UNMAPPED_REGIONS_SOFT_LIMIT (1000000/4) +#else +#define GC_UNMAPPED_REGIONS_SOFT_LIMIT 16384 +#endif +#endif +#if defined(GC_DISABLE_INCREMENTAL)||defined(DEFAULT_VDB) +#undef GWW_VDB +#undef MPROTECT_VDB +#undef PCR_VDB +#undef PROC_VDB +#endif +#ifdef GC_DISABLE_INCREMENTAL +#undef CHECKSUMS +#endif +#ifdef USE_GLOBAL_ALLOC +#undef GWW_VDB +#endif +#if defined(BASE_ATOMIC_OPS_EMULATED) +#undef MPROTECT_VDB +#endif +#if defined(USE_PROC_FOR_LIBRARIES)&&defined(GC_LINUX_THREADS) +#undef MPROTECT_VDB +#endif +#if defined(MPROTECT_VDB)&&defined(GC_PREFER_MPROTECT_VDB) +#undef PCR_VDB +#undef PROC_VDB +#endif +#ifdef PROC_VDB +#undef MPROTECT_VDB +#endif +#if defined(MPROTECT_VDB)&&!defined(MSWIN32)&&!defined(MSWINCE) +#include +#endif +#if defined(SIGBUS)&&!defined(HAVE_SIGBUS)&&!defined(CPPCHECK) +#define HAVE_SIGBUS +#endif +#ifndef SA_SIGINFO +#define NO_SA_SIGACTION +#endif +#if (defined(NO_SA_SIGACTION)||defined(GC_NO_SIGSETJMP))&&defined(MPROTECT_VDB)&&!defined(DARWIN)&&!defined(MSWIN32)&&!defined(MSWINCE) +#undef MPROTECT_VDB +#endif +#if!defined(PCR_VDB)&&!defined(PROC_VDB)&&!defined(MPROTECT_VDB)&&!defined(GWW_VDB)&&!defined(DEFAULT_VDB)&&!defined(GC_DISABLE_INCREMENTAL) +#define DEFAULT_VDB +#endif +#if ((defined(UNIX_LIKE)&&(defined(DARWIN)||defined(HAIKU)||defined(HURD)||defined(OPENBSD)||defined(ARM32)||defined(AVR32)||defined(MIPS)||defined(NIOS2)||defined(OR1K)))||(defined(LINUX)&&!defined(__gnu_linux__))||(defined(RTEMS)&&defined(I386))||defined(HOST_ANDROID))&&!defined(NO_GETCONTEXT) +#define NO_GETCONTEXT 1 +#endif +#ifndef PREFETCH +#if GC_GNUC_PREREQ(3,0)&&!defined(NO_PREFETCH) +#define PREFETCH(x)__builtin_prefetch((x),0,0) +#else +#define PREFETCH(x)(void)0 +#endif +#endif +#ifndef GC_PREFETCH_FOR_WRITE +#if GC_GNUC_PREREQ(3,0)&&!defined(GC_NO_PREFETCH_FOR_WRITE) +#define GC_PREFETCH_FOR_WRITE(x)__builtin_prefetch((x),1) +#else +#define GC_PREFETCH_FOR_WRITE(x)(void)0 +#endif +#endif +#ifndef CACHE_LINE_SIZE +#define CACHE_LINE_SIZE 32 +#endif +#ifndef STATIC +#ifdef GC_ASSERTIONS +#define STATIC +#else +#define STATIC static +#endif +#endif +#if defined(LINUX)&&(defined(USE_PROC_FOR_LIBRARIES)||defined(IA64)||!defined(SMALL_CONFIG)) +#define NEED_PROC_MAPS +#endif +#if defined(LINUX)||defined(HURD)||defined(__GLIBC__) +#define REGISTER_LIBRARIES_EARLY +#endif +#if defined(SEARCH_FOR_DATA_START) +extern ptr_t GC_data_start; +#define DATASTART GC_data_start +#endif +#ifndef HEAP_START +#define HEAP_START ((ptr_t)0) +#endif +#ifndef CLEAR_DOUBLE +#define CLEAR_DOUBLE(x)(((word*)(x))[0]=0,((word*)(x))[1]=0) +#endif +#if defined(GC_LINUX_THREADS)&&defined(REDIRECT_MALLOC)&&!defined(INCLUDE_LINUX_THREAD_DESCR) +#define INCLUDE_LINUX_THREAD_DESCR +#endif +#if!defined(CPPCHECK) +#if defined(GC_IRIX_THREADS)&&!defined(IRIX5) +#error Inconsistent configuration +#endif +#if defined(GC_LINUX_THREADS)&&!defined(LINUX)&&!defined(NACL) +#error Inconsistent configuration +#endif +#if defined(GC_NETBSD_THREADS)&&!defined(NETBSD) +#error Inconsistent configuration +#endif +#if defined(GC_FREEBSD_THREADS)&&!defined(FREEBSD) +#error Inconsistent configuration +#endif +#if defined(GC_SOLARIS_THREADS)&&!defined(SOLARIS) +#error Inconsistent configuration +#endif +#if defined(GC_HPUX_THREADS)&&!defined(HPUX) +#error Inconsistent configuration +#endif +#if defined(GC_AIX_THREADS)&&!defined(_AIX) +#error Inconsistent configuration +#endif +#if defined(GC_WIN32_THREADS)&&!defined(CYGWIN32)&&!defined(MSWIN32)&&!defined(MSWINCE)&&!defined(MSWIN_XBOX1) +#error Inconsistent configuration +#endif +#if defined(GC_WIN32_PTHREADS)&&defined(CYGWIN32) +#error Inconsistent configuration +#endif +#endif +#if defined(PCR)||defined(GC_WIN32_THREADS)||defined(GC_PTHREADS)||((defined(NN_PLATFORM_CTR)||defined(NINTENDO_SWITCH)||defined(SN_TARGET_ORBIS)||defined(SN_TARGET_PS3)||defined(SN_TARGET_PSP2))&&defined(GC_THREADS)) +#define THREADS +#endif +#if defined(PARALLEL_MARK)&&!defined(THREADS)&&!defined(CPPCHECK) +#error Invalid config:PARALLEL_MARK requires GC_THREADS +#endif +#if defined(GWW_VDB)&&!defined(USE_WINALLOC)&&!defined(CPPCHECK) +#error Invalid config:GWW_VDB requires USE_WINALLOC +#endif +#if (((defined(MSWIN32)||defined(MSWINCE))&&!defined(__GNUC__))||(defined(MSWIN32)&&defined(I386))||(defined(USE_PROC_FOR_LIBRARIES)&&defined(THREADS)))&&!defined(NO_CRT)&&!defined(NO_WRAP_MARK_SOME) +#define WRAP_MARK_SOME +#endif +#if defined(PARALLEL_MARK)&&!defined(DEFAULT_STACK_MAYBE_SMALL)&&(defined(HPUX)||defined(GC_DGUX386_THREADS)||defined(NO_GETCONTEXT)) +#define DEFAULT_STACK_MAYBE_SMALL +#endif +#ifdef PARALLEL_MARK +#define MIN_STACK_SIZE (8*HBLKSIZE*sizeof(word)) +#endif +#if defined(HOST_ANDROID)&&!defined(THREADS)&&!defined(USE_GET_STACKBASE_FOR_MAIN) +#define USE_GET_STACKBASE_FOR_MAIN +#endif +#if ((defined(FREEBSD)&&defined(__GLIBC__))||defined(LINUX)||defined(NETBSD)||defined(HOST_ANDROID))&&!defined(NO_PTHREAD_GETATTR_NP) +#define HAVE_PTHREAD_GETATTR_NP 1 +#elif defined(FREEBSD)&&!defined(__GLIBC__)&&!defined(NO_PTHREAD_ATTR_GET_NP) +#define HAVE_PTHREAD_NP_H 1 +#define HAVE_PTHREAD_ATTR_GET_NP 1 +#endif +#if defined(UNIX_LIKE)&&defined(THREADS)&&!defined(NO_CANCEL_SAFE)&&!defined(HOST_ANDROID) +#define CANCEL_SAFE +#endif +#ifdef CANCEL_SAFE +#define IF_CANCEL(x)x +#else +#define IF_CANCEL(x) +#endif +#if!defined(CAN_HANDLE_FORK)&&!defined(NO_HANDLE_FORK)&&!defined(HAVE_NO_FORK)&&((defined(GC_PTHREADS)&&!defined(NACL)&&!defined(GC_WIN32_PTHREADS)&&!defined(USE_WINALLOC))||(defined(DARWIN)&&defined(MPROTECT_VDB))||defined(HANDLE_FORK)) +#define CAN_HANDLE_FORK +#endif +#if defined(CAN_HANDLE_FORK)&&!defined(CAN_CALL_ATFORK)&&!defined(HURD)&&!defined(SN_TARGET_ORBIS)&&!defined(HOST_TIZEN)&&(!defined(HOST_ANDROID)||__ANDROID_API__>=21) +#define CAN_CALL_ATFORK +#endif +#if!defined(CAN_HANDLE_FORK)&&!defined(HAVE_NO_FORK)&&(defined(MSWIN32)||defined(MSWINCE)||defined(DOS4GW)||defined(OS2)||defined(SYMBIAN)) +#define HAVE_NO_FORK +#endif +#if!defined(USE_MARK_BITS)&&!defined(USE_MARK_BYTES)&&defined(PARALLEL_MARK) +#define USE_MARK_BYTES +#endif +#if (defined(MSWINCE)&&!defined(__CEGCC__)||defined(MSWINRT_FLAVOR))&&!defined(NO_GETENV) +#define NO_GETENV +#endif +#if (defined(NO_GETENV)||defined(MSWINCE))&&!defined(NO_GETENV_WIN32) +#define NO_GETENV_WIN32 +#endif +#if!defined(MSGBOX_ON_ERROR)&&!defined(NO_MSGBOX_ON_ERROR)&&!defined(SMALL_CONFIG)&&defined(MSWIN32)&&!defined(MSWINRT_FLAVOR)&&!defined(MSWIN_XBOX1) +#define MSGBOX_ON_ERROR +#endif +#ifndef STRTOULL +#if defined(_WIN64)&&!defined(__GNUC__) +#define STRTOULL _strtoui64 +#elif defined(_LLP64)||defined(__LLP64__)||defined(_WIN64) +#define STRTOULL strtoull +#else +#define STRTOULL strtoul +#endif +#endif +#ifndef GC_WORD_C +#if defined(_WIN64)&&!defined(__GNUC__) +#define GC_WORD_C(val)val##ui64 +#elif defined(_LLP64)||defined(__LLP64__)||defined(_WIN64) +#define GC_WORD_C(val)val##ULL +#else +#define GC_WORD_C(val)((word)val##UL) +#endif +#endif +#if defined(__has_feature) +#if __has_feature(address_sanitizer)&&!defined(ADDRESS_SANITIZER) +#define ADDRESS_SANITIZER +#endif +#if __has_feature(memory_sanitizer)&&!defined(MEMORY_SANITIZER) +#define MEMORY_SANITIZER +#endif +#if __has_feature(thread_sanitizer)&&!defined(THREAD_SANITIZER) +#define THREAD_SANITIZER +#endif +#else +#ifdef __SANITIZE_ADDRESS__ +#define ADDRESS_SANITIZER +#endif +#endif +#if defined(SPARC) +#define ASM_CLEAR_CODE +#endif +#if defined(SPARC) +#define CAN_SAVE_CALL_ARGS +#endif +#if (defined(I386)||defined(X86_64))&&(defined(LINUX)||defined(__GLIBC__)) +#define CAN_SAVE_CALL_ARGS +#endif +#if defined(SAVE_CALL_COUNT)&&!defined(GC_ADD_CALLER)&&defined(GC_CAN_SAVE_CALL_STACKS) +#define SAVE_CALL_CHAIN +#endif +#ifdef SAVE_CALL_CHAIN +#if defined(SAVE_CALL_NARGS)&&defined(CAN_SAVE_CALL_ARGS) +#define NARGS SAVE_CALL_NARGS +#else +#define NARGS 0 +#endif +#endif +#ifdef SAVE_CALL_CHAIN +#if!defined(SAVE_CALL_COUNT)||defined(CPPCHECK) +#define NFRAMES 6 +#else +#define NFRAMES ((SAVE_CALL_COUNT+1)&~1) +#endif +#define NEED_CALLINFO +#endif +#ifdef GC_ADD_CALLER +#define NFRAMES 1 +#define NARGS 0 +#define NEED_CALLINFO +#endif +#if (defined(FREEBSD)||(defined(DARWIN)&&!defined(_POSIX_C_SOURCE))||(defined(SOLARIS)&&(!defined(_XOPEN_SOURCE)||defined(__EXTENSIONS__)))||defined(LINUX))&&!defined(HAVE_DLADDR) +#define HAVE_DLADDR 1 +#endif +#if defined(MAKE_BACK_GRAPH)&&!defined(DBG_HDRS_ALL) +#define DBG_HDRS_ALL 1 +#endif +#if defined(POINTER_MASK)&&!defined(POINTER_SHIFT) +#define POINTER_SHIFT 0 +#endif +#if defined(POINTER_SHIFT)&&!defined(POINTER_MASK) +#define POINTER_MASK ((word)(-1)) +#endif +#if!defined(FIXUP_POINTER)&&defined(POINTER_MASK) +#define FIXUP_POINTER(p)(p=((p)&POINTER_MASK)< 32 +#define HASH_TL +#endif +#if defined(LARGE_CONFIG)||!defined(SMALL_CONFIG) +#define LOG_BOTTOM_SZ 10 +#else +#define LOG_BOTTOM_SZ 11 +#endif +#define BOTTOM_SZ (1<>LOG_HBLKSIZE)&(HDR_CACHE_SIZE-1))) +#define HCE_VALID_FOR(hce,h)((hce)->block_addr==((word)(h)>>LOG_HBLKSIZE)) +#define HCE_HDR(h)((hce)->hce_hdr) +#ifdef PRINT_BLACK_LIST +GC_INNER hdr*GC_header_cache_miss(ptr_t p,hdr_cache_entry*hce, +ptr_t source); +#define HEADER_CACHE_MISS(p,hce,source)GC_header_cache_miss(p,hce,source) +#else +GC_INNER hdr*GC_header_cache_miss(ptr_t p,hdr_cache_entry*hce); +#define HEADER_CACHE_MISS(p,hce,source)GC_header_cache_miss(p,hce) +#endif +#define HC_GET_HDR(p,hhdr,source){ hdr_cache_entry*hce=HCE(p);if (EXPECT(HCE_VALID_FOR(hce,p),TRUE)){ HC_HIT();hhdr=hce->hce_hdr;} else { hhdr=HEADER_CACHE_MISS(p,hce,source);if (NULL==hhdr)break;} } +typedef struct bi { +hdr*index[BOTTOM_SZ]; +struct bi*asc_link; +struct bi*desc_link; +word key; +#ifdef HASH_TL +struct bi*hash_link; +#endif +} bottom_index; +#define MAX_JUMP (HBLKSIZE - 1) +#define HDR_FROM_BI(bi,p)((bi)->index[((word)(p)>>LOG_HBLKSIZE)&(BOTTOM_SZ - 1)]) +#ifndef HASH_TL +#define BI(p)(GC_top_index [(word)(p)>>(LOG_BOTTOM_SZ+LOG_HBLKSIZE)]) +#define HDR_INNER(p)HDR_FROM_BI(BI(p),p) +#ifdef SMALL_CONFIG +#define HDR(p)GC_find_header((ptr_t)(p)) +#else +#define HDR(p)HDR_INNER(p) +#endif +#define GET_BI(p,bottom_indx)(void)((bottom_indx)=BI(p)) +#define GET_HDR(p,hhdr)(void)((hhdr)=HDR(p)) +#define SET_HDR(p,hhdr)(void)(HDR_INNER(p)=(hhdr)) +#define GET_HDR_ADDR(p,ha)(void)((ha)=&HDR_INNER(p)) +#else +#define TL_HASH(hi)((hi)&(TOP_SZ - 1)) +#define GET_BI(p,bottom_indx)do { REGISTER word hi=(word)(p)>>(LOG_BOTTOM_SZ+LOG_HBLKSIZE);REGISTER bottom_index*_bi=GC_top_index[TL_HASH(hi)];while (_bi->key!=hi&&_bi!=GC_all_nils)_bi=_bi->hash_link;(bottom_indx)=_bi;} while (0) +#define GET_HDR_ADDR(p,ha)do { REGISTER bottom_index*bi;GET_BI(p,bi);(ha)=&HDR_FROM_BI(bi,p);} while (0) +#define GET_HDR(p,hhdr)do { REGISTER hdr**_ha;GET_HDR_ADDR(p,_ha);(hhdr)=*_ha;} while (0) +#define SET_HDR(p,hhdr)do { REGISTER hdr**_ha;GET_HDR_ADDR(p,_ha);*_ha=(hhdr);} while (0) +#define HDR(p)GC_find_header((ptr_t)(p)) +#endif +#define IS_FORWARDING_ADDR_OR_NIL(hhdr)((size_t)(hhdr)<=MAX_JUMP) +#define FORWARDED_ADDR(h,hhdr)((struct hblk*)(h)- (size_t)(hhdr)) +EXTERN_C_END +#endif +#endif +#ifndef GC_ATTR_NO_SANITIZE_ADDR +#ifndef ADDRESS_SANITIZER +#define GC_ATTR_NO_SANITIZE_ADDR +#elif GC_CLANG_PREREQ(3,8) +#define GC_ATTR_NO_SANITIZE_ADDR __attribute__((no_sanitize("address"))) +#else +#define GC_ATTR_NO_SANITIZE_ADDR __attribute__((no_sanitize_address)) +#endif +#endif +#ifndef GC_ATTR_NO_SANITIZE_MEMORY +#ifndef MEMORY_SANITIZER +#define GC_ATTR_NO_SANITIZE_MEMORY +#elif GC_CLANG_PREREQ(3,8) +#define GC_ATTR_NO_SANITIZE_MEMORY __attribute__((no_sanitize("memory"))) +#else +#define GC_ATTR_NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory)) +#endif +#endif +#ifndef GC_ATTR_NO_SANITIZE_THREAD +#ifndef THREAD_SANITIZER +#define GC_ATTR_NO_SANITIZE_THREAD +#elif GC_CLANG_PREREQ(3,8) +#define GC_ATTR_NO_SANITIZE_THREAD __attribute__((no_sanitize("thread"))) +#else +#define GC_ATTR_NO_SANITIZE_THREAD __attribute__((no_sanitize_thread)) +#endif +#endif +#ifndef GC_ATTR_UNUSED +#if GC_GNUC_PREREQ(3,4) +#define GC_ATTR_UNUSED __attribute__((__unused__)) +#else +#define GC_ATTR_UNUSED +#endif +#endif +#ifdef HAVE_CONFIG_H +#define GC_INLINE static inline +#elif defined(_MSC_VER)||defined(__INTEL_COMPILER)||defined(__DMC__)||(GC_GNUC_PREREQ(3,0)&&defined(__STRICT_ANSI__))||defined(__WATCOMC__) +#define GC_INLINE static __inline +#elif GC_GNUC_PREREQ(3,0)||defined(__sun) +#define GC_INLINE static inline +#else +#define GC_INLINE static +#endif +#ifndef GC_ATTR_NOINLINE +#if GC_GNUC_PREREQ(4,0) +#define GC_ATTR_NOINLINE __attribute__((__noinline__)) +#elif _MSC_VER>=1400 +#define GC_ATTR_NOINLINE __declspec(noinline) +#else +#define GC_ATTR_NOINLINE +#endif +#endif +#ifndef GC_API_OSCALL +#if defined(__GNUC__) +#if GC_GNUC_PREREQ(4,0)&&!defined(GC_NO_VISIBILITY) +#define GC_API_OSCALL extern __attribute__((__visibility__("default"))) +#else +#define GC_API_OSCALL extern +#endif +#else +#define GC_API_OSCALL GC_API +#endif +#endif +#ifndef GC_API_PRIV +#define GC_API_PRIV GC_API +#endif +#if defined(THREADS)&&!defined(NN_PLATFORM_CTR) +#ifndef GC_ATOMIC_OPS_H +#define GC_ATOMIC_OPS_H +#ifdef GC_BUILTIN_ATOMIC +#ifdef __cplusplus +extern "C" { +#endif +typedef GC_word AO_t; +#ifdef GC_PRIVATE_H +#define AO_INLINE GC_INLINE +#else +#define AO_INLINE static __inline +#endif +typedef unsigned char AO_TS_t; +#define AO_TS_CLEAR 0 +#define AO_TS_INITIALIZER (AO_TS_t)AO_TS_CLEAR +#if defined(__GCC_ATOMIC_TEST_AND_SET_TRUEVAL)&&!defined(CPPCHECK) +#define AO_TS_SET __GCC_ATOMIC_TEST_AND_SET_TRUEVAL +#else +#define AO_TS_SET (AO_TS_t)1 +#endif +#define AO_CLEAR(p)__atomic_clear(p,__ATOMIC_RELEASE) +#define AO_test_and_set_acquire(p)__atomic_test_and_set(p,__ATOMIC_ACQUIRE) +#define AO_HAVE_test_and_set_acquire +#define AO_compiler_barrier()__atomic_signal_fence(__ATOMIC_SEQ_CST) +#define AO_nop_full()__atomic_thread_fence(__ATOMIC_SEQ_CST) +#define AO_HAVE_nop_full +#define AO_fetch_and_add(p,v)__atomic_fetch_add(p,v,__ATOMIC_RELAXED) +#define AO_HAVE_fetch_and_add +#define AO_fetch_and_add1(p)AO_fetch_and_add(p,1) +#define AO_HAVE_fetch_and_add1 +#define AO_or(p,v)(void)__atomic_or_fetch(p,v,__ATOMIC_RELAXED) +#define AO_HAVE_or +#define AO_load(p)__atomic_load_n(p,__ATOMIC_RELAXED) +#define AO_HAVE_load +#define AO_load_acquire(p)__atomic_load_n(p,__ATOMIC_ACQUIRE) +#define AO_HAVE_load_acquire +#define AO_load_acquire_read(p)AO_load_acquire(p) +#define AO_HAVE_load_acquire_read +#define AO_store(p,v)__atomic_store_n(p,v,__ATOMIC_RELAXED) +#define AO_HAVE_store +#define AO_store_release(p,v)__atomic_store_n(p,v,__ATOMIC_RELEASE) +#define AO_HAVE_store_release +#define AO_store_release_write(p,v)AO_store_release(p,v) +#define AO_HAVE_store_release_write +#define AO_char_load(p)__atomic_load_n(p,__ATOMIC_RELAXED) +#define AO_HAVE_char_load +#define AO_char_store(p,v)__atomic_store_n(p,v,__ATOMIC_RELAXED) +#define AO_HAVE_char_store +#ifdef AO_REQUIRE_CAS +AO_INLINE int +AO_compare_and_swap(volatile AO_t*p,AO_t ov,AO_t nv) +{ +return (int)__atomic_compare_exchange_n(p,&ov,nv,0, +__ATOMIC_RELAXED,__ATOMIC_RELAXED); +} +AO_INLINE int +AO_compare_and_swap_release(volatile AO_t*p,AO_t ov,AO_t nv) +{ +return (int)__atomic_compare_exchange_n(p,&ov,nv,0, +__ATOMIC_RELEASE,__ATOMIC_RELAXED); +} +#define AO_HAVE_compare_and_swap_release +#endif +#ifdef __cplusplus +} +#endif +#ifndef NO_LOCKFREE_AO_OR +#define HAVE_LOCKFREE_AO_OR 1 +#endif +#else +#include "atomic_ops.h" +#if (!defined(AO_HAVE_load)||!defined(AO_HAVE_store))&&!defined(CPPCHECK) +#error AO_load or AO_store is missing;probably old version of atomic_ops +#endif +#endif +#endif +#ifndef AO_HAVE_compiler_barrier +#define AO_HAVE_compiler_barrier 1 +#endif +#endif +#if defined(MSWIN32)||defined(MSWINCE)||defined(CYGWIN32) +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif +#define NOSERVICE +#include +#include +#endif +#ifndef GC_LOCKS_H +#define GC_LOCKS_H +#ifdef THREADS +#ifdef PCR +#include +#include +#endif +EXTERN_C_BEGIN +#ifdef PCR +GC_EXTERN PCR_Th_ML GC_allocate_ml; +#if defined(CPPCHECK) +#define DCL_LOCK_STATE +#else +#define DCL_LOCK_STATE PCR_ERes GC_fastLockRes;PCR_sigset_t GC_old_sig_mask +#endif +#define UNCOND_LOCK()PCR_Th_ML_Acquire(&GC_allocate_ml) +#define UNCOND_UNLOCK()PCR_Th_ML_Release(&GC_allocate_ml) +#elif defined(NN_PLATFORM_CTR)||defined(NINTENDO_SWITCH) +extern void GC_lock(void); +extern void GC_unlock(void); +#define UNCOND_LOCK()GC_lock() +#define UNCOND_UNLOCK()GC_unlock() +#endif +#if (!defined(AO_HAVE_test_and_set_acquire)||defined(GC_RTEMS_PTHREADS)||defined(SN_TARGET_ORBIS)||defined(SN_TARGET_PS3)||defined(GC_WIN32_THREADS)||defined(BASE_ATOMIC_OPS_EMULATED)||defined(LINT2))&&defined(GC_PTHREADS) +#define USE_PTHREAD_LOCKS +#undef USE_SPIN_LOCK +#endif +#if defined(GC_WIN32_THREADS)&&!defined(USE_PTHREAD_LOCKS) +#define NO_THREAD (DWORD)(-1) +GC_EXTERN CRITICAL_SECTION GC_allocate_ml; +#ifdef GC_ASSERTIONS +GC_EXTERN DWORD GC_lock_holder; +#define SET_LOCK_HOLDER()GC_lock_holder=GetCurrentThreadId() +#define UNSET_LOCK_HOLDER()GC_lock_holder=NO_THREAD +#define I_HOLD_LOCK()(!GC_need_to_lock||GC_lock_holder==GetCurrentThreadId()) +#ifdef THREAD_SANITIZER +#define I_DONT_HOLD_LOCK()TRUE +#else +#define I_DONT_HOLD_LOCK()(!GC_need_to_lock||GC_lock_holder!=GetCurrentThreadId()) +#endif +#define UNCOND_LOCK(){ GC_ASSERT(I_DONT_HOLD_LOCK());EnterCriticalSection(&GC_allocate_ml);SET_LOCK_HOLDER();} +#define UNCOND_UNLOCK(){ GC_ASSERT(I_HOLD_LOCK());UNSET_LOCK_HOLDER();LeaveCriticalSection(&GC_allocate_ml);} +#else +#define UNCOND_LOCK()EnterCriticalSection(&GC_allocate_ml) +#define UNCOND_UNLOCK()LeaveCriticalSection(&GC_allocate_ml) +#endif +#elif defined(GC_PTHREADS) +EXTERN_C_END +#include +EXTERN_C_BEGIN +#if!defined(GC_WIN32_PTHREADS) +#define NUMERIC_THREAD_ID(id)((unsigned long)(id)) +#define THREAD_EQUAL(id1,id2)((id1)==(id2)) +#define NUMERIC_THREAD_ID_UNIQUE +#elif defined(__WINPTHREADS_VERSION_MAJOR) +#define NUMERIC_THREAD_ID(id)((unsigned long)(id)) +#define THREAD_EQUAL(id1,id2)((id1)==(id2)) +#ifndef _WIN64 +#define NUMERIC_THREAD_ID_UNIQUE +#endif +#else +#define NUMERIC_THREAD_ID(id)((unsigned long)(word)(id.p)) +#define THREAD_EQUAL(id1,id2)((id1.p==id2.p)&&(id1.x==id2.x)) +#undef NUMERIC_THREAD_ID_UNIQUE +#endif +#define NO_THREAD ((unsigned long)(-1l)) +#ifdef SN_TARGET_PSP2 +EXTERN_C_END +#include "psp2-support.h" +EXTERN_C_BEGIN +GC_EXTERN WapiMutex GC_allocate_ml_PSP2; +#define UNCOND_LOCK(){ int res;GC_ASSERT(I_DONT_HOLD_LOCK());res=PSP2_MutexLock(&GC_allocate_ml_PSP2);GC_ASSERT(0==res);(void)res;SET_LOCK_HOLDER();} +#define UNCOND_UNLOCK(){ int res;GC_ASSERT(I_HOLD_LOCK());UNSET_LOCK_HOLDER();res=PSP2_MutexUnlock(&GC_allocate_ml_PSP2);GC_ASSERT(0==res);(void)res;} +#elif (!defined(THREAD_LOCAL_ALLOC)||defined(USE_SPIN_LOCK))&&!defined(USE_PTHREAD_LOCKS) +#undef USE_SPIN_LOCK +#define USE_SPIN_LOCK +GC_EXTERN volatile AO_TS_t GC_allocate_lock; +GC_INNER void GC_lock(void); +#ifdef GC_ASSERTIONS +#define UNCOND_LOCK(){ GC_ASSERT(I_DONT_HOLD_LOCK());if (AO_test_and_set_acquire(&GC_allocate_lock)==AO_TS_SET)GC_lock();SET_LOCK_HOLDER();} +#define UNCOND_UNLOCK(){ GC_ASSERT(I_HOLD_LOCK());UNSET_LOCK_HOLDER();AO_CLEAR(&GC_allocate_lock);} +#else +#define UNCOND_LOCK(){ if (AO_test_and_set_acquire(&GC_allocate_lock)==AO_TS_SET)GC_lock();} +#define UNCOND_UNLOCK()AO_CLEAR(&GC_allocate_lock) +#endif +#else +#ifndef USE_PTHREAD_LOCKS +#define USE_PTHREAD_LOCKS +#endif +#endif +#ifdef USE_PTHREAD_LOCKS +EXTERN_C_END +#include +EXTERN_C_BEGIN +GC_EXTERN pthread_mutex_t GC_allocate_ml; +#ifdef GC_ASSERTIONS +GC_INNER void GC_lock(void); +#define UNCOND_LOCK(){ GC_ASSERT(I_DONT_HOLD_LOCK());GC_lock();SET_LOCK_HOLDER();} +#define UNCOND_UNLOCK(){ GC_ASSERT(I_HOLD_LOCK());UNSET_LOCK_HOLDER();pthread_mutex_unlock(&GC_allocate_ml);} +#else +#if defined(NO_PTHREAD_TRYLOCK) +#define UNCOND_LOCK()pthread_mutex_lock(&GC_allocate_ml) +#else +GC_INNER void GC_lock(void); +#define UNCOND_LOCK(){ if (0!=pthread_mutex_trylock(&GC_allocate_ml))GC_lock();} +#endif +#define UNCOND_UNLOCK()pthread_mutex_unlock(&GC_allocate_ml) +#endif +#endif +#ifdef GC_ASSERTIONS +GC_EXTERN unsigned long GC_lock_holder; +#define SET_LOCK_HOLDER()GC_lock_holder=NUMERIC_THREAD_ID(pthread_self()) +#define UNSET_LOCK_HOLDER()GC_lock_holder=NO_THREAD +#define I_HOLD_LOCK()(!GC_need_to_lock||GC_lock_holder==NUMERIC_THREAD_ID(pthread_self())) +#if!defined(NUMERIC_THREAD_ID_UNIQUE)||defined(THREAD_SANITIZER) +#define I_DONT_HOLD_LOCK()TRUE +#else +#define I_DONT_HOLD_LOCK()(!GC_need_to_lock||GC_lock_holder!=NUMERIC_THREAD_ID(pthread_self())) +#endif +#endif +#ifndef GC_WIN32_THREADS +GC_EXTERN volatile GC_bool GC_collecting; +#ifdef AO_HAVE_char_store +#define ENTER_GC()AO_char_store((unsigned char*)&GC_collecting,TRUE) +#define EXIT_GC()AO_char_store((unsigned char*)&GC_collecting,FALSE) +#else +#define ENTER_GC()(void)(GC_collecting=TRUE) +#define EXIT_GC()(void)(GC_collecting=FALSE) +#endif +#endif +#endif +#if defined(GC_ALWAYS_MULTITHREADED)&&(defined(USE_PTHREAD_LOCKS)||defined(USE_SPIN_LOCK)) +#define GC_need_to_lock TRUE +#define set_need_to_lock()(void)0 +#else +#if defined(GC_ALWAYS_MULTITHREADED)&&!defined(CPPCHECK) +#error Runtime initialization of GC lock is needed! +#endif +#undef GC_ALWAYS_MULTITHREADED +GC_EXTERN GC_bool GC_need_to_lock; +#ifdef THREAD_SANITIZER +#define set_need_to_lock()(void)(*(GC_bool volatile*)&GC_need_to_lock?FALSE:(GC_need_to_lock=TRUE)) +#else +#define set_need_to_lock()(void)(GC_need_to_lock=TRUE) +#endif +#endif +EXTERN_C_END +#else +#define LOCK()(void)0 +#define UNLOCK()(void)0 +#ifdef GC_ASSERTIONS +#define I_HOLD_LOCK()TRUE +#define I_DONT_HOLD_LOCK()TRUE +#endif +#endif +#if defined(UNCOND_LOCK)&&!defined(LOCK) +#if (defined(LINT2)&&defined(USE_PTHREAD_LOCKS))||defined(GC_ALWAYS_MULTITHREADED) +#define LOCK()UNCOND_LOCK() +#define UNLOCK()UNCOND_UNLOCK() +#else +#define LOCK()do { if (GC_need_to_lock)UNCOND_LOCK();} while (0) +#define UNLOCK()do { if (GC_need_to_lock)UNCOND_UNLOCK();} while (0) +#endif +#endif +#ifndef ENTER_GC +#define ENTER_GC() +#define EXIT_GC() +#endif +#ifndef DCL_LOCK_STATE +#define DCL_LOCK_STATE +#endif +#endif +#define GC_WORD_MAX (~(word)0) +#ifdef STACK_GROWS_DOWN +#define COOLER_THAN > +#define HOTTER_THAN < +#define MAKE_COOLER(x,y)if ((word)((x)+(y))> (word)(x)){(x)+=(y);} else (x)=(ptr_t)GC_WORD_MAX +#define MAKE_HOTTER(x,y)(x)-=(y) +#else +#define COOLER_THAN < +#define HOTTER_THAN > +#define MAKE_COOLER(x,y)if ((word)((x)- (y))< (word)(x)){(x)-=(y);} else (x)=0 +#define MAKE_HOTTER(x,y)(x)+=(y) +#endif +#if defined(AMIGA)&&defined(__SASC) +#define GC_FAR __far +#else +#define GC_FAR +#endif +EXTERN_C_BEGIN +#ifndef GC_NO_FINALIZATION +#define GC_INVOKE_FINALIZERS()GC_notify_or_invoke_finalizers() +GC_INNER void GC_notify_or_invoke_finalizers(void); +GC_INNER void GC_finalize(void); +#ifndef GC_TOGGLE_REFS_NOT_NEEDED +GC_INNER void GC_process_togglerefs(void); +#endif +#ifndef SMALL_CONFIG +GC_INNER void GC_print_finalization_stats(void); +#endif +#else +#define GC_INVOKE_FINALIZERS()(void)0 +#endif +#if!defined(DONT_ADD_BYTE_AT_END) +#ifdef LINT2 +#define EXTRA_BYTES ((size_t)(GC_all_interior_pointers?1:0)) +#else +#define EXTRA_BYTES (size_t)GC_all_interior_pointers +#endif +#define MAX_EXTRA_BYTES 1 +#else +#define EXTRA_BYTES 0 +#define MAX_EXTRA_BYTES 0 +#endif +#ifndef LARGE_CONFIG +#define MINHINCR 16 +#define MAXHINCR 2048 +#else +#define MINHINCR 64 +#define MAXHINCR 4096 +#endif +#define BL_LIMIT GC_black_list_spacing +#ifdef NEED_CALLINFO +struct callinfo { +word ci_pc; +#if NARGS > 0 +word ci_arg[NARGS]; +#endif +#if (NFRAMES*(NARGS+1))% 2==1 +word ci_dummy; +#endif +}; +#endif +#ifdef SAVE_CALL_CHAIN +GC_INNER void GC_save_callers(struct callinfo info[NFRAMES]); +GC_INNER void GC_print_callers(struct callinfo info[NFRAMES]); +#endif +EXTERN_C_END +#ifndef NO_CLOCK +#ifdef BSD_TIME +#undef CLOCK_TYPE +#undef GET_TIME +#undef MS_TIME_DIFF +#define CLOCK_TYPE struct timeval +#define CLOCK_TYPE_INITIALIZER { 0,0 } +#define GET_TIME(x)do { struct rusage rusage;getrusage(RUSAGE_SELF,&rusage);x=rusage.ru_utime;} while (0) +#define MS_TIME_DIFF(a,b)((unsigned long)((long)(a.tv_sec-b.tv_sec)*1000+(long)(a.tv_usec - b.tv_usec)/1000 - (a.tv_usec < b.tv_usec&&(long)(a.tv_usec - b.tv_usec)% 1000!=0?1:0))) +#define NS_FRAC_TIME_DIFF(a,b)((unsigned long)((a.tv_usec < b.tv_usec&&(long)(a.tv_usec - b.tv_usec)% 1000!=0?1000L:0)+(long)(a.tv_usec - b.tv_usec)% 1000)*1000) +#elif defined(MSWIN32)||defined(MSWINCE)||defined(WINXP_USE_PERF_COUNTER) +#if defined(MSWINRT_FLAVOR)||defined(WINXP_USE_PERF_COUNTER) +#define CLOCK_TYPE ULONGLONG +#define GET_TIME(x)do { LARGE_INTEGER freq,tc;if (!QueryPerformanceFrequency(&freq)||!QueryPerformanceCounter(&tc))ABORT("QueryPerformanceCounter requires WinXP+");x=(CLOCK_TYPE)((double)tc.QuadPart/freq.QuadPart*1e9);} while (0) +#define MS_TIME_DIFF(a,b)((unsigned long)(((a)- (b))/1000000UL)) +#define NS_FRAC_TIME_DIFF(a,b)((unsigned long)(((a)- (b))% 1000000UL)) +#else +#define CLOCK_TYPE DWORD +#define GET_TIME(x)(void)(x=GetTickCount()) +#define MS_TIME_DIFF(a,b)((unsigned long)((a)- (b))) +#define NS_FRAC_TIME_DIFF(a,b)0UL +#endif +#elif defined(NN_PLATFORM_CTR) +#define CLOCK_TYPE long long +EXTERN_C_BEGIN +CLOCK_TYPE n3ds_get_system_tick(void); +CLOCK_TYPE n3ds_convert_tick_to_ms(CLOCK_TYPE tick); +EXTERN_C_END +#define GET_TIME(x)(void)(x=n3ds_get_system_tick()) +#define MS_TIME_DIFF(a,b)((unsigned long)n3ds_convert_tick_to_ms((a)-(b))) +#define NS_FRAC_TIME_DIFF(a,b)0UL +#elif defined(NINTENDO_SWITCH)||(((defined(LINUX)&&defined(__USE_POSIX199309))||defined(CYGWIN32))&&defined(_POSIX_TIMERS)) +#include +#define CLOCK_TYPE struct timespec +#define CLOCK_TYPE_INITIALIZER { 0,0 } +#if defined(_POSIX_MONOTONIC_CLOCK)&&!defined(NINTENDO_SWITCH) +#define GET_TIME(x)do { if (clock_gettime(CLOCK_MONOTONIC,&x)==-1)ABORT("clock_gettime failed");} while (0) +#else +#define GET_TIME(x)do { if (clock_gettime(CLOCK_REALTIME,&x)==-1)ABORT("clock_gettime failed");} while (0) +#endif +#define MS_TIME_DIFF(a,b)((unsigned long)((a).tv_nsec+(1000000L*1000 - (b).tv_nsec))/1000000UL+((unsigned long)((a).tv_sec - (b).tv_sec)*1000UL)- 1000UL) +#define NS_FRAC_TIME_DIFF(a,b)((unsigned long)((a).tv_nsec+(1000000L*1000 - (b).tv_nsec))% 1000000UL) +#else +#include +#if defined(FREEBSD)&&!defined(CLOCKS_PER_SEC) +#include +#define CLOCKS_PER_SEC CLK_TCK +#endif +#if!defined(CLOCKS_PER_SEC) +#define CLOCKS_PER_SEC 1000000 +#endif +#define CLOCK_TYPE clock_t +#define GET_TIME(x)(void)(x=clock()) +#define MS_TIME_DIFF(a,b)(CLOCKS_PER_SEC % 1000==0?(unsigned long)((a)- (b))/(unsigned long)(CLOCKS_PER_SEC/1000):((unsigned long)((a)- (b))*1000)/(unsigned long)CLOCKS_PER_SEC) +#define NS_FRAC_TIME_DIFF(a,b)(CLOCKS_PER_SEC<=1000?0UL:(unsigned long)(CLOCKS_PER_SEC<=(clock_t)1000000UL?(((a)- (b))*((clock_t)1000000UL/CLOCKS_PER_SEC)% 1000)*1000:(CLOCKS_PER_SEC<=(clock_t)1000000UL*1000?((a)- (b))*((clock_t)1000000UL*1000/CLOCKS_PER_SEC):(((a)- (b))*(clock_t)1000000UL*1000)/CLOCKS_PER_SEC)% (clock_t)1000000UL)) +#endif +#ifndef CLOCK_TYPE_INITIALIZER +#define CLOCK_TYPE_INITIALIZER 0 +#endif +#endif +#if defined(SPARC)&&defined(SUNOS4)||(defined(M68K)&&defined(NEXT))||defined(VAX) +#define BCOPY_EXISTS +#elif defined(AMIGA)||defined(DARWIN) +#include +#define BCOPY_EXISTS +#elif defined(MACOS)&&defined(POWERPC) +#include +#define bcopy(x,y,n)BlockMoveData(x,y,n) +#define bzero(x,n)BlockZero(x,n) +#define BCOPY_EXISTS +#endif +#if!defined(BCOPY_EXISTS)||defined(CPPCHECK) +#include +#define BCOPY(x,y,n)memcpy(y,x,(size_t)(n)) +#define BZERO(x,n)memset(x,0,(size_t)(n)) +#else +#define BCOPY(x,y,n)bcopy((void*)(x),(void*)(y),(size_t)(n)) +#define BZERO(x,n)bzero((void*)(x),(size_t)(n)) +#endif +#ifdef PCR +#include "th/PCR_ThCtl.h" +#endif +EXTERN_C_BEGIN +#ifdef PCR +#define STOP_WORLD()PCR_ThCtl_SetExclusiveMode(PCR_ThCtl_ExclusiveMode_stopNormal,PCR_allSigsBlocked,PCR_waitForever) +#define START_WORLD()PCR_ThCtl_SetExclusiveMode(PCR_ThCtl_ExclusiveMode_null,PCR_allSigsBlocked,PCR_waitForever) +#else +#if defined(NN_PLATFORM_CTR)||defined(NINTENDO_SWITCH)||defined(GC_WIN32_THREADS)||defined(GC_PTHREADS) +GC_INNER void GC_stop_world(void); +GC_INNER void GC_start_world(void); +#define STOP_WORLD()GC_stop_world() +#define START_WORLD()GC_start_world() +#else +#define STOP_WORLD()GC_ASSERT(GC_blocked_sp==NULL) +#define START_WORLD() +#endif +#endif +#ifdef THREADS +GC_EXTERN GC_on_thread_event_proc GC_on_thread_event; +#endif +#if defined(SMALL_CONFIG)||defined(PCR) +#define GC_on_abort(msg)(void)0 +#else +GC_API_PRIV GC_abort_func GC_on_abort; +#endif +#if defined(CPPCHECK) +#define ABORT(msg){ GC_on_abort(msg);abort();} +#elif defined(PCR) +#define ABORT(s)PCR_Base_Panic(s) +#else +#if defined(MSWIN_XBOX1)&&!defined(DebugBreak) +#define DebugBreak()__debugbreak() +#elif defined(MSWINCE)&&!defined(DebugBreak)&&(!defined(UNDER_CE)||(defined(__MINGW32CE__)&&!defined(ARM32))) +#define DebugBreak()_exit(-1) +#endif +#if defined(MSWIN32)&&(defined(NO_DEBUGGING)||defined(LINT2)) +#define ABORT(msg)(GC_on_abort(msg),_exit(-1)) +#elif defined(MSWINCE)&&defined(NO_DEBUGGING) +#define ABORT(msg)(GC_on_abort(msg),ExitProcess(-1)) +#elif defined(MSWIN32)||defined(MSWINCE) +#if defined(_CrtDbgBreak)&&defined(_DEBUG)&&defined(_MSC_VER) +#define ABORT(msg){ GC_on_abort(msg);_CrtDbgBreak();} +#else +#define ABORT(msg){ GC_on_abort(msg);DebugBreak();} +#endif +#else +#define ABORT(msg)(GC_on_abort(msg),abort()) +#endif +#endif +#define ABORT_ARG1(C_msg,C_fmt,arg1)MACRO_BLKSTMT_BEGIN GC_INFOLOG_PRINTF(C_msg C_fmt "\n",arg1);ABORT(C_msg);MACRO_BLKSTMT_END +#define ABORT_ARG2(C_msg,C_fmt,arg1,arg2)MACRO_BLKSTMT_BEGIN GC_INFOLOG_PRINTF(C_msg C_fmt "\n",arg1,arg2);ABORT(C_msg);MACRO_BLKSTMT_END +#define ABORT_ARG3(C_msg,C_fmt,arg1,arg2,arg3)MACRO_BLKSTMT_BEGIN GC_INFOLOG_PRINTF(C_msg C_fmt "\n",arg1,arg2,arg3);ABORT(C_msg);MACRO_BLKSTMT_END +#define ABORT_RET(msg)if ((signed_word)GC_current_warn_proc==-1){} else ABORT(msg) +#ifdef PCR +#define EXIT()PCR_Base_Exit(1,PCR_waitForever) +#else +#define EXIT()(GC_on_abort(NULL),exit(1)) +#endif +#define WARN(msg,arg)(*GC_current_warn_proc)(( char*)("GC Warning:" msg),(word)(arg)) +GC_EXTERN GC_warn_proc GC_current_warn_proc; +#ifndef WARN_PRIdPTR +#define WARN_PRIdPTR "ld" +#endif +#define TRUSTED_STRING(s)(char*)COVERT_DATAFLOW(s) +#ifdef GC_READ_ENV_FILE +GC_INNER char*GC_envfile_getenv(const char*name); +#define GETENV(name)GC_envfile_getenv(name) +#elif defined(NO_GETENV)&&!defined(CPPCHECK) +#define GETENV(name)NULL +#elif defined(EMPTY_GETENV_RESULTS) +GC_INLINE char*fixed_getenv(const char*name) +{ +char*value=getenv(name); +return value!=NULL&&*value!='\0'?value:NULL; +} +#define GETENV(name)fixed_getenv(name) +#else +#define GETENV(name)getenv(name) +#endif +EXTERN_C_END +#if defined(DARWIN) +#include +#ifndef MAC_OS_X_VERSION_MAX_ALLOWED +#include +#endif +#if defined(POWERPC) +#if CPP_WORDSZ==32 +#define GC_THREAD_STATE_T ppc_thread_state_t +#else +#define GC_THREAD_STATE_T ppc_thread_state64_t +#define GC_MACH_THREAD_STATE PPC_THREAD_STATE64 +#define GC_MACH_THREAD_STATE_COUNT PPC_THREAD_STATE64_COUNT +#endif +#elif defined(I386)||defined(X86_64) +#if CPP_WORDSZ==32 +#if defined(i386_THREAD_STATE_COUNT)&&!defined(x86_THREAD_STATE32_COUNT) +#define GC_THREAD_STATE_T i386_thread_state_t +#define GC_MACH_THREAD_STATE i386_THREAD_STATE +#define GC_MACH_THREAD_STATE_COUNT i386_THREAD_STATE_COUNT +#else +#define GC_THREAD_STATE_T x86_thread_state32_t +#define GC_MACH_THREAD_STATE x86_THREAD_STATE32 +#define GC_MACH_THREAD_STATE_COUNT x86_THREAD_STATE32_COUNT +#endif +#else +#define GC_THREAD_STATE_T x86_thread_state64_t +#define GC_MACH_THREAD_STATE x86_THREAD_STATE64 +#define GC_MACH_THREAD_STATE_COUNT x86_THREAD_STATE64_COUNT +#endif +#elif defined(ARM32)&&defined(ARM_UNIFIED_THREAD_STATE)&&!defined(CPPCHECK) +#define GC_THREAD_STATE_T arm_unified_thread_state_t +#define GC_MACH_THREAD_STATE ARM_UNIFIED_THREAD_STATE +#define GC_MACH_THREAD_STATE_COUNT ARM_UNIFIED_THREAD_STATE_COUNT +#elif defined(ARM32) +#define GC_THREAD_STATE_T arm_thread_state_t +#ifdef ARM_MACHINE_THREAD_STATE_COUNT +#define GC_MACH_THREAD_STATE ARM_MACHINE_THREAD_STATE +#define GC_MACH_THREAD_STATE_COUNT ARM_MACHINE_THREAD_STATE_COUNT +#endif +#elif defined(AARCH64) +#define GC_THREAD_STATE_T arm_thread_state64_t +#define GC_MACH_THREAD_STATE ARM_THREAD_STATE64 +#define GC_MACH_THREAD_STATE_COUNT ARM_THREAD_STATE64_COUNT +#elif!defined(CPPCHECK) +#error define GC_THREAD_STATE_T +#endif +#ifndef GC_MACH_THREAD_STATE +#define GC_MACH_THREAD_STATE MACHINE_THREAD_STATE +#define GC_MACH_THREAD_STATE_COUNT MACHINE_THREAD_STATE_COUNT +#endif +#if CPP_WORDSZ==32 +#define GC_MACH_HEADER mach_header +#define GC_MACH_SECTION section +#define GC_GETSECTBYNAME getsectbynamefromheader +#else +#define GC_MACH_HEADER mach_header_64 +#define GC_MACH_SECTION section_64 +#define GC_GETSECTBYNAME getsectbynamefromheader_64 +#endif +#if __DARWIN_UNIX03 +#define THREAD_FLD_NAME(x)__ ## x +#else +#define THREAD_FLD_NAME(x)x +#endif +#if defined(ARM32)&&defined(ARM_UNIFIED_THREAD_STATE) +#define THREAD_FLD(x)ts_32.THREAD_FLD_NAME(x) +#else +#define THREAD_FLD(x)THREAD_FLD_NAME(x) +#endif +#endif +#include +#if __STDC_VERSION__>=201112L +#include +#endif +EXTERN_C_BEGIN +#if CPP_WORDSZ==32 +#define WORDS_TO_BYTES(x)((x)<<2) +#define BYTES_TO_WORDS(x)((x)>>2) +#define LOGWL ((word)5) +#define modWORDSZ(n)((n)&0x1f) +#if ALIGNMENT!=4 +#define UNALIGNED_PTRS +#endif +#endif +#if CPP_WORDSZ==64 +#define WORDS_TO_BYTES(x)((x)<<3) +#define BYTES_TO_WORDS(x)((x)>>3) +#define LOGWL ((word)6) +#define modWORDSZ(n)((n)&0x3f) +#if ALIGNMENT!=8 +#define UNALIGNED_PTRS +#endif +#endif +#define GRANULE_BYTES GC_GRANULE_BYTES +#define TINY_FREELISTS GC_TINY_FREELISTS +#define WORDSZ ((word)CPP_WORDSZ) +#define SIGNB ((word)1<<(WORDSZ-1)) +#define BYTES_PER_WORD ((word)(sizeof (word))) +#define divWORDSZ(n)((n)>>LOGWL) +#if GRANULE_BYTES==8 +#define BYTES_TO_GRANULES(n)((n)>>3) +#define GRANULES_TO_BYTES(n)((n)<<3) +#if CPP_WORDSZ==64 +#define GRANULES_TO_WORDS(n)(n) +#elif CPP_WORDSZ==32 +#define GRANULES_TO_WORDS(n)((n)<<1) +#else +#define GRANULES_TO_WORDS(n)BYTES_TO_WORDS(GRANULES_TO_BYTES(n)) +#endif +#elif GRANULE_BYTES==16 +#define BYTES_TO_GRANULES(n)((n)>>4) +#define GRANULES_TO_BYTES(n)((n)<<4) +#if CPP_WORDSZ==64 +#define GRANULES_TO_WORDS(n)((n)<<1) +#elif CPP_WORDSZ==32 +#define GRANULES_TO_WORDS(n)((n)<<2) +#else +#define GRANULES_TO_WORDS(n)BYTES_TO_WORDS(GRANULES_TO_BYTES(n)) +#endif +#else +#error Bad GRANULE_BYTES value +#endif +#ifndef HBLKSIZE +#if defined(LARGE_CONFIG)||!defined(SMALL_CONFIG) +#ifdef ALPHA +#define CPP_LOG_HBLKSIZE 13 +#elif defined(SN_TARGET_ORBIS)||defined(SN_TARGET_PSP2) +#define CPP_LOG_HBLKSIZE 16 +#else +#define CPP_LOG_HBLKSIZE 12 +#endif +#else +#define CPP_LOG_HBLKSIZE 10 +#endif +#else +#if HBLKSIZE==512 +#define CPP_LOG_HBLKSIZE 9 +#elif HBLKSIZE==1024 +#define CPP_LOG_HBLKSIZE 10 +#elif HBLKSIZE==2048 +#define CPP_LOG_HBLKSIZE 11 +#elif HBLKSIZE==4096 +#define CPP_LOG_HBLKSIZE 12 +#elif HBLKSIZE==8192 +#define CPP_LOG_HBLKSIZE 13 +#elif HBLKSIZE==16384 +#define CPP_LOG_HBLKSIZE 14 +#elif!defined(CPPCHECK) +#error Bad HBLKSIZE value +#endif +#undef HBLKSIZE +#endif +#define CPP_HBLKSIZE (1<>LOG_HBLKSIZE) +#define HBLK_PTR_DIFF(p,q)divHBLKSZ((ptr_t)p - (ptr_t)q) +#define modHBLKSZ(n)((n)&(HBLKSIZE-1)) +#define HBLKPTR(objptr)((struct hblk*)(((word)(objptr))&~(word)(HBLKSIZE-1))) +#define HBLKDISPL(objptr)(((size_t)(objptr))&(HBLKSIZE-1)) +#define ROUNDUP_GRANULE_SIZE(lb)(SIZET_SAT_ADD(lb,GRANULE_BYTES - 1)&~(GRANULE_BYTES - 1)) +#define ROUNDED_UP_GRANULES(lb)BYTES_TO_GRANULES(SIZET_SAT_ADD(lb,GRANULE_BYTES - 1+EXTRA_BYTES)) +#if MAX_EXTRA_BYTES==0 +#define SMALL_OBJ(bytes)EXPECT((bytes)<=(MAXOBJBYTES),TRUE) +#else +#define SMALL_OBJ(bytes)(EXPECT((bytes)<=(MAXOBJBYTES - MAX_EXTRA_BYTES),TRUE)||(bytes)<=MAXOBJBYTES - EXTRA_BYTES) +#endif +#define ADD_SLOP(lb)SIZET_SAT_ADD(lb,EXTRA_BYTES) +#ifdef LARGE_CONFIG +#if CPP_WORDSZ==32 +#define LOG_PHT_ENTRIES 20 +#else +#define LOG_PHT_ENTRIES 21 +#endif +#elif!defined(SMALL_CONFIG) +#define LOG_PHT_ENTRIES 18 +#else +#define LOG_PHT_ENTRIES 15 +#endif +#define PHT_ENTRIES ((word)1<>LOGWL) +typedef word page_hash_table[PHT_SIZE]; +#define PHT_HASH(addr)((((word)(addr))>>LOG_HBLKSIZE)&(PHT_ENTRIES - 1)) +#define get_pht_entry_from_index(bl,index)(((bl)[divWORDSZ(index)]>>modWORDSZ(index))&1) +#define set_pht_entry_from_index(bl,index)(void)((bl)[divWORDSZ(index)]|=(word)1<hb_flags&FREE_BLK)!=0) +#define OBJ_SZ_TO_BLOCKS(lb)divHBLKSZ((lb)+HBLKSIZE-1) +#define OBJ_SZ_TO_BLOCKS_CHECKED(lb)divHBLKSZ(SIZET_SAT_ADD(lb,HBLKSIZE - 1)) +#define obj_link(p)(*(void**)(p)) +#define LOG_MAX_MARK_PROCS 6 +#define MAX_MARK_PROCS (1< 32 +#define MAX_HEAP_SECTS 81920 +#else +#define MAX_HEAP_SECTS 7680 +#endif +#elif defined(SMALL_CONFIG)&&!defined(USE_PROC_FOR_LIBRARIES) +#if defined(PARALLEL_MARK)&&(defined(MSWIN32)||defined(CYGWIN32)) +#define MAX_HEAP_SECTS 384 +#else +#define MAX_HEAP_SECTS 128 +#endif +#elif CPP_WORDSZ > 32 +#define MAX_HEAP_SECTS 1024 +#else +#define MAX_HEAP_SECTS 512 +#endif +#endif +typedef struct GC_ms_entry { +ptr_t mse_start; +union word_ptr_ao_u mse_descr; +} mse; +typedef int mark_state_t; +struct disappearing_link; +struct finalizable_object; +struct dl_hashtbl_s { +struct disappearing_link**head; +word entries; +unsigned log_size; +}; +struct fnlz_roots_s { +struct finalizable_object**fo_head; +struct finalizable_object*finalize_now; +}; +union toggle_ref_u { +void*strong_ref; +GC_hidden_pointer weak_ref; +}; +typedef struct { +word ed_bitmap; +GC_bool ed_continued; +} typed_ext_descr_t; +struct _GC_arrays { +word _heapsize; +word _requested_heapsize; +ptr_t _last_heap_addr; +ptr_t _prev_heap_addr; +word _large_free_bytes; +word _large_allocd_bytes; +word _max_large_allocd_bytes; +word _bytes_allocd_before_gc; +#ifndef SEPARATE_GLOBALS +#define GC_bytes_allocd GC_arrays._bytes_allocd +word _bytes_allocd; +#endif +word _bytes_dropped; +word _bytes_finalized; +word _bytes_freed; +word _finalizer_bytes_freed; +bottom_index*_all_bottom_indices; +bottom_index*_all_bottom_indices_end; +ptr_t _scratch_free_ptr; +hdr*_hdr_free_list; +ptr_t _scratch_end_ptr; +ptr_t _scratch_last_end_ptr; +mse*_mark_stack; +mse*_mark_stack_limit; +#ifdef PARALLEL_MARK +mse*volatile _mark_stack_top; +#else +mse*_mark_stack_top; +#endif +word _composite_in_use; +word _atomic_in_use; +#ifdef USE_MUNMAP +#define GC_unmapped_bytes GC_arrays._unmapped_bytes +word _unmapped_bytes; +#ifdef COUNT_UNMAPPED_REGIONS +#define GC_num_unmapped_regions GC_arrays._num_unmapped_regions +signed_word _num_unmapped_regions; +#endif +#else +#define GC_unmapped_bytes 0 +#endif +bottom_index*_all_nils; +#define GC_scan_ptr GC_arrays._scan_ptr +struct hblk*_scan_ptr; +#ifdef PARALLEL_MARK +#define GC_main_local_mark_stack GC_arrays._main_local_mark_stack +mse*_main_local_mark_stack; +#define GC_first_nonempty GC_arrays._first_nonempty +volatile AO_t _first_nonempty; +#endif +#define GC_mark_stack_size GC_arrays._mark_stack_size +size_t _mark_stack_size; +#define GC_mark_state GC_arrays._mark_state +mark_state_t _mark_state; +#define GC_mark_stack_too_small GC_arrays._mark_stack_too_small +GC_bool _mark_stack_too_small; +#define GC_objects_are_marked GC_arrays._objects_are_marked +GC_bool _objects_are_marked; +#ifdef ENABLE_TRACE +#define GC_trace_addr GC_arrays._trace_addr +ptr_t _trace_addr; +#endif +#define GC_n_heap_sects GC_arrays._n_heap_sects +word _n_heap_sects; +#if defined(MSWIN32)||defined(MSWINCE)||defined(CYGWIN32) +#define GC_n_heap_bases GC_arrays._n_heap_bases +word _n_heap_bases; +#endif +#ifdef USE_PROC_FOR_LIBRARIES +#define GC_n_memory GC_arrays._n_memory +word _n_memory; +#endif +#ifdef GC_GCJ_SUPPORT +#define GC_gcjobjfreelist GC_arrays._gcjobjfreelist +ptr_t*_gcjobjfreelist; +#endif +#define GC_fo_entries GC_arrays._fo_entries +word _fo_entries; +#ifndef GC_NO_FINALIZATION +#define GC_dl_hashtbl GC_arrays._dl_hashtbl +#define GC_fnlz_roots GC_arrays._fnlz_roots +#define GC_log_fo_table_size GC_arrays._log_fo_table_size +#ifndef GC_LONG_REFS_NOT_NEEDED +#define GC_ll_hashtbl GC_arrays._ll_hashtbl +struct dl_hashtbl_s _ll_hashtbl; +#endif +struct dl_hashtbl_s _dl_hashtbl; +struct fnlz_roots_s _fnlz_roots; +unsigned _log_fo_table_size; +#ifndef GC_TOGGLE_REFS_NOT_NEEDED +#define GC_toggleref_arr GC_arrays._toggleref_arr +#define GC_toggleref_array_size GC_arrays._toggleref_array_size +#define GC_toggleref_array_capacity GC_arrays._toggleref_array_capacity +union toggle_ref_u*_toggleref_arr; +size_t _toggleref_array_size; +size_t _toggleref_array_capacity; +#endif +#endif +#ifdef TRACE_BUF +#define GC_trace_buf_ptr GC_arrays._trace_buf_ptr +int _trace_buf_ptr; +#endif +#ifdef ENABLE_DISCLAIM +#define GC_finalized_kind GC_arrays._finalized_kind +int _finalized_kind; +#endif +#define n_root_sets GC_arrays._n_root_sets +#define GC_excl_table_entries GC_arrays._excl_table_entries +int _n_root_sets; +size_t _excl_table_entries; +#ifdef THREADS +#define GC_roots_were_cleared GC_arrays._roots_were_cleared +GC_bool _roots_were_cleared; +#endif +#define GC_explicit_typing_initialized GC_arrays._explicit_typing_initialized +#define GC_ed_size GC_arrays._ed_size +#define GC_avail_descr GC_arrays._avail_descr +#define GC_ext_descriptors GC_arrays._ext_descriptors +#ifdef AO_HAVE_load_acquire +volatile AO_t _explicit_typing_initialized; +#else +GC_bool _explicit_typing_initialized; +#endif +size_t _ed_size; +size_t _avail_descr; +typed_ext_descr_t*_ext_descriptors; +GC_mark_proc _mark_procs[MAX_MARK_PROCS]; +char _modws_valid_offsets[sizeof(word)]; +#if!defined(MSWIN32)&&!defined(MSWINCE)&&!defined(CYGWIN32) +#define GC_root_index GC_arrays._root_index +struct roots*_root_index[RT_SIZE]; +#endif +#ifdef SAVE_CALL_CHAIN +#define GC_last_stack GC_arrays._last_stack +struct callinfo _last_stack[NFRAMES]; +#endif +#ifndef SEPARATE_GLOBALS +#define GC_objfreelist GC_arrays._objfreelist +void*_objfreelist[MAXOBJGRANULES+1]; +#define GC_aobjfreelist GC_arrays._aobjfreelist +void*_aobjfreelist[MAXOBJGRANULES+1]; +#endif +void*_uobjfreelist[MAXOBJGRANULES+1]; +#ifdef GC_ATOMIC_UNCOLLECTABLE +#define GC_auobjfreelist GC_arrays._auobjfreelist +void*_auobjfreelist[MAXOBJGRANULES+1]; +#endif +size_t _size_map[MAXOBJBYTES+1]; +#ifdef MARK_BIT_PER_GRANULE +#define GC_obj_map GC_arrays._obj_map +unsigned short*_obj_map[MAXOBJGRANULES+1]; +#define MAP_LEN BYTES_TO_GRANULES(HBLKSIZE) +#endif +#define VALID_OFFSET_SZ HBLKSIZE +char _valid_offsets[VALID_OFFSET_SZ]; +#ifndef GC_DISABLE_INCREMENTAL +#define GC_grungy_pages GC_arrays._grungy_pages +page_hash_table _grungy_pages; +#define GC_dirty_pages GC_arrays._dirty_pages +volatile page_hash_table _dirty_pages; +#endif +#if (defined(CHECKSUMS)&&defined(GWW_VDB))||defined(PROC_VDB) +#define GC_written_pages GC_arrays._written_pages +page_hash_table _written_pages; +#endif +#define GC_heap_sects GC_arrays._heap_sects +struct HeapSect { +ptr_t hs_start; +size_t hs_bytes; +} _heap_sects[MAX_HEAP_SECTS]; +#if defined(USE_PROC_FOR_LIBRARIES) +#define GC_our_memory GC_arrays._our_memory +struct HeapSect _our_memory[MAX_HEAP_SECTS]; +#endif +#if defined(MSWIN32)||defined(MSWINCE)||defined(CYGWIN32) +#define GC_heap_bases GC_arrays._heap_bases +ptr_t _heap_bases[MAX_HEAP_SECTS]; +#endif +#ifdef MSWINCE +#define GC_heap_lengths GC_arrays._heap_lengths +word _heap_lengths[MAX_HEAP_SECTS]; +#endif +struct roots _static_roots[MAX_ROOT_SETS]; +struct exclusion _excl_table[MAX_EXCLUSIONS]; +bottom_index*_top_index[TOP_SZ]; +}; +GC_API_PRIV GC_FAR struct _GC_arrays GC_arrays; +#define GC_all_nils GC_arrays._all_nils +#define GC_atomic_in_use GC_arrays._atomic_in_use +#define GC_bytes_allocd_before_gc GC_arrays._bytes_allocd_before_gc +#define GC_bytes_dropped GC_arrays._bytes_dropped +#define GC_bytes_finalized GC_arrays._bytes_finalized +#define GC_bytes_freed GC_arrays._bytes_freed +#define GC_composite_in_use GC_arrays._composite_in_use +#define GC_excl_table GC_arrays._excl_table +#define GC_finalizer_bytes_freed GC_arrays._finalizer_bytes_freed +#define GC_heapsize GC_arrays._heapsize +#define GC_large_allocd_bytes GC_arrays._large_allocd_bytes +#define GC_large_free_bytes GC_arrays._large_free_bytes +#define GC_last_heap_addr GC_arrays._last_heap_addr +#define GC_mark_stack GC_arrays._mark_stack +#define GC_mark_stack_limit GC_arrays._mark_stack_limit +#define GC_mark_stack_top GC_arrays._mark_stack_top +#define GC_mark_procs GC_arrays._mark_procs +#define GC_max_large_allocd_bytes GC_arrays._max_large_allocd_bytes +#define GC_modws_valid_offsets GC_arrays._modws_valid_offsets +#define GC_prev_heap_addr GC_arrays._prev_heap_addr +#define GC_requested_heapsize GC_arrays._requested_heapsize +#define GC_all_bottom_indices GC_arrays._all_bottom_indices +#define GC_all_bottom_indices_end GC_arrays._all_bottom_indices_end +#define GC_scratch_free_ptr GC_arrays._scratch_free_ptr +#define GC_hdr_free_list GC_arrays._hdr_free_list +#define GC_scratch_end_ptr GC_arrays._scratch_end_ptr +#define GC_scratch_last_end_ptr GC_arrays._scratch_last_end_ptr +#define GC_size_map GC_arrays._size_map +#define GC_static_roots GC_arrays._static_roots +#define GC_top_index GC_arrays._top_index +#define GC_uobjfreelist GC_arrays._uobjfreelist +#define GC_valid_offsets GC_arrays._valid_offsets +#define beginGC_arrays ((ptr_t)(&GC_arrays)) +#define endGC_arrays (((ptr_t)(&GC_arrays))+(sizeof GC_arrays)) +#define USED_HEAP_SIZE (GC_heapsize - GC_large_free_bytes) +#ifndef MAXOBJKINDS +#define MAXOBJKINDS 16 +#endif +GC_EXTERN struct obj_kind { +void**ok_freelist; +struct hblk**ok_reclaim_list; +word ok_descriptor; +GC_bool ok_relocate_descr; +GC_bool ok_init; +#ifdef ENABLE_DISCLAIM +GC_bool ok_mark_unconditionally; +int (GC_CALLBACK*ok_disclaim_proc)(void*); +#define OK_DISCLAIM_INITZ,FALSE,0 +#else +#define OK_DISCLAIM_INITZ +#endif +} GC_obj_kinds[MAXOBJKINDS]; +#define beginGC_obj_kinds ((ptr_t)(&GC_obj_kinds)) +#define endGC_obj_kinds (beginGC_obj_kinds+(sizeof GC_obj_kinds)) +#ifdef SEPARATE_GLOBALS +extern word GC_bytes_allocd; +extern ptr_t GC_objfreelist[MAXOBJGRANULES+1]; +#define beginGC_objfreelist ((ptr_t)(&GC_objfreelist)) +#define endGC_objfreelist (beginGC_objfreelist+sizeof(GC_objfreelist)) +extern ptr_t GC_aobjfreelist[MAXOBJGRANULES+1]; +#define beginGC_aobjfreelist ((ptr_t)(&GC_aobjfreelist)) +#define endGC_aobjfreelist (beginGC_aobjfreelist+sizeof(GC_aobjfreelist)) +#endif +#define PTRFREE 0 +#define NORMAL 1 +#define UNCOLLECTABLE 2 +#ifdef GC_ATOMIC_UNCOLLECTABLE +#define AUNCOLLECTABLE 3 +#define IS_UNCOLLECTABLE(k)(((k)&~1)==UNCOLLECTABLE) +#define GC_N_KINDS_INITIAL_VALUE 4 +#else +#define IS_UNCOLLECTABLE(k)((k)==UNCOLLECTABLE) +#define GC_N_KINDS_INITIAL_VALUE 3 +#endif +GC_EXTERN unsigned GC_n_kinds; +GC_EXTERN size_t GC_page_size; +#define ROUNDUP_PAGESIZE(lb)(SIZET_SAT_ADD(lb,GC_page_size - 1)&~(GC_page_size - 1)) +#ifdef MMAP_SUPPORTED +#define ROUNDUP_PAGESIZE_IF_MMAP(lb)ROUNDUP_PAGESIZE(lb) +#else +#define ROUNDUP_PAGESIZE_IF_MMAP(lb)(lb) +#endif +#if defined(MSWIN32)||defined(MSWINCE)||defined(CYGWIN32) +GC_EXTERN SYSTEM_INFO GC_sysinfo; +GC_INNER GC_bool GC_is_heap_base(void*p); +#endif +GC_EXTERN word GC_black_list_spacing; +#ifdef GC_GCJ_SUPPORT +extern struct hblk*GC_hblkfreelist[]; +extern word GC_free_bytes[]; +#endif +GC_EXTERN word GC_root_size; +GC_EXTERN GC_bool GC_debugging_started; +struct blocking_data { +GC_fn_type fn; +void*client_data; +}; +struct GC_traced_stack_sect_s { +ptr_t saved_stack_ptr; +#ifdef IA64 +ptr_t saved_backing_store_ptr; +ptr_t backing_store_end; +#endif +struct GC_traced_stack_sect_s*prev; +}; +#ifdef THREADS +GC_INNER void GC_push_all_stack_sections(ptr_t lo,ptr_t hi, +struct GC_traced_stack_sect_s*traced_stack_sect); +GC_EXTERN word GC_total_stacksize; +#else +GC_EXTERN ptr_t GC_blocked_sp; +GC_EXTERN struct GC_traced_stack_sect_s*GC_traced_stack_sect; +#endif +#ifdef IA64 +GC_INNER void GC_push_all_register_sections(ptr_t bs_lo,ptr_t bs_hi, +int eager,struct GC_traced_stack_sect_s*traced_stack_sect); +#endif +#ifdef USE_MARK_BYTES +#define mark_bit_from_hdr(hhdr,n)((hhdr)->hb_marks[n]) +#define set_mark_bit_from_hdr(hhdr,n)((hhdr)->hb_marks[n]=1) +#define clear_mark_bit_from_hdr(hhdr,n)((hhdr)->hb_marks[n]=0) +#else +#if defined(PARALLEL_MARK)||(defined(THREAD_SANITIZER)&&defined(THREADS)) +#define OR_WORD(addr,bits)AO_or((volatile AO_t*)(addr),(AO_t)(bits)) +#else +#define OR_WORD(addr,bits)(void)(*(addr)|=(bits)) +#endif +#define mark_bit_from_hdr(hhdr,n)(((hhdr)->hb_marks[divWORDSZ(n)]>>modWORDSZ(n))&(word)1) +#define set_mark_bit_from_hdr(hhdr,n)OR_WORD((hhdr)->hb_marks+divWORDSZ(n),(word)1<hb_marks[divWORDSZ(n)]&=~((word)1< MAXOBJBYTES?1:HBLK_OBJS(sz)) +#else +#define MARK_BIT_NO(offset,sz)BYTES_TO_GRANULES((word)(offset)) +#define MARK_BIT_OFFSET(sz)BYTES_TO_GRANULES(sz) +#define IF_PER_OBJ(x) +#define FINAL_MARK_BIT(sz)((sz)> MAXOBJBYTES?MARK_BITS_PER_HBLK:BYTES_TO_GRANULES((sz)*HBLK_OBJS(sz))) +#endif +GC_INNER ptr_t GC_approx_sp(void); +GC_INNER GC_bool GC_should_collect(void); +void GC_apply_to_all_blocks(void (*fn)(struct hblk*h,word client_data), +word client_data); +GC_INNER struct hblk*GC_next_block(struct hblk*h,GC_bool allow_free); +GC_INNER struct hblk*GC_prev_block(struct hblk*h); +GC_INNER void GC_mark_init(void); +GC_INNER void GC_clear_marks(void); +GC_INNER void GC_invalidate_mark_state(void); +GC_INNER GC_bool GC_mark_some(ptr_t cold_gc_frame); +GC_INNER void GC_initiate_gc(void); +GC_INNER GC_bool GC_collection_in_progress(void); +#define GC_PUSH_ALL_SYM(sym)GC_push_all(( void*)&(sym),( void*)(&(sym)+1)) +GC_INNER void GC_push_all_stack(ptr_t b,ptr_t t); +#if defined(WRAP_MARK_SOME)&&defined(PARALLEL_MARK) +GC_INNER void GC_push_conditional_eager(void*bottom,void*top, +GC_bool all); +#endif +GC_INNER void GC_push_roots(GC_bool all,ptr_t cold_gc_frame); +GC_API_PRIV GC_push_other_roots_proc GC_push_other_roots; +#ifdef THREADS +void GC_push_thread_structures(void); +#endif +GC_EXTERN void (*GC_push_typed_structures)(void); +GC_INNER void GC_with_callee_saves_pushed(void (*fn)(ptr_t,void*), +volatile ptr_t arg); +#if defined(SPARC)||defined(IA64) +ptr_t GC_save_regs_in_stack(void); +#endif +#if defined(AMIGA)||defined(MACOS)||defined(GC_DARWIN_THREADS) +void GC_push_one(word p); +#endif +#ifdef GC_WIN32_THREADS +GC_INNER void GC_push_many_regs(const word*regs,unsigned count); +#endif +#if defined(PRINT_BLACK_LIST)||defined(KEEP_BACK_PTRS) +GC_INNER void GC_mark_and_push_stack(ptr_t p,ptr_t source); +#else +GC_INNER void GC_mark_and_push_stack(ptr_t p); +#endif +GC_INNER void GC_clear_hdr_marks(hdr*hhdr); +GC_INNER void GC_set_hdr_marks(hdr*hhdr); +GC_INNER void GC_set_fl_marks(ptr_t p); +#if defined(GC_ASSERTIONS)&&defined(THREAD_LOCAL_ALLOC) +void GC_check_fl_marks(void**); +#endif +void GC_add_roots_inner(ptr_t b,ptr_t e,GC_bool tmp); +#ifdef USE_PROC_FOR_LIBRARIES +GC_INNER void GC_remove_roots_subregion(ptr_t b,ptr_t e); +#endif +GC_INNER void GC_exclude_static_roots_inner(void*start,void*finish); +#if defined(DYNAMIC_LOADING)||defined(MSWIN32)||defined(MSWINCE)||defined(CYGWIN32)||defined(PCR) +GC_INNER void GC_register_dynamic_libraries(void); +#endif +GC_INNER void GC_cond_register_dynamic_libraries(void); +ptr_t GC_get_main_stack_base(void); +#ifdef IA64 +GC_INNER ptr_t GC_get_register_stack_base(void); +#endif +void GC_register_data_segments(void); +#ifdef THREADS +GC_INNER void GC_thr_init(void); +GC_INNER void GC_init_parallel(void); +#else +GC_INNER GC_bool GC_is_static_root(void*p); +#ifdef TRACE_BUF +void GC_add_trace_entry(char*kind,word arg1,word arg2); +#endif +#endif +#ifdef PRINT_BLACK_LIST +GC_INNER void GC_add_to_black_list_normal(word p,ptr_t source); +#define GC_ADD_TO_BLACK_LIST_NORMAL(bits,source)if (GC_all_interior_pointers){ GC_add_to_black_list_stack((word)(bits),(source));} else GC_add_to_black_list_normal((word)(bits),(source)) +GC_INNER void GC_add_to_black_list_stack(word p,ptr_t source); +#define GC_ADD_TO_BLACK_LIST_STACK(bits,source)GC_add_to_black_list_stack((word)(bits),(source)) +#else +GC_INNER void GC_add_to_black_list_normal(word p); +#define GC_ADD_TO_BLACK_LIST_NORMAL(bits,source)if (GC_all_interior_pointers){ GC_add_to_black_list_stack((word)(bits));} else GC_add_to_black_list_normal((word)(bits)) +GC_INNER void GC_add_to_black_list_stack(word p); +#define GC_ADD_TO_BLACK_LIST_STACK(bits,source)GC_add_to_black_list_stack((word)(bits)) +#endif +struct hblk*GC_is_black_listed(struct hblk*h,word len); +GC_INNER void GC_promote_black_lists(void); +GC_INNER void GC_unpromote_black_lists(void); +GC_INNER ptr_t GC_scratch_alloc(size_t bytes); +#ifdef GWW_VDB +#else +#define GC_scratch_recycle_no_gww GC_scratch_recycle_inner +#endif +GC_INNER void GC_scratch_recycle_inner(void*ptr,size_t bytes); +#ifdef MARK_BIT_PER_GRANULE +GC_INNER GC_bool GC_add_map_entry(size_t sz); +#endif +GC_INNER void GC_register_displacement_inner(size_t offset); +GC_INNER void GC_new_hblk(size_t size_in_granules,int kind); +GC_INNER ptr_t GC_build_fl(struct hblk*h,size_t words,GC_bool clear, +ptr_t list); +GC_INNER struct hblk*GC_allochblk(size_t size_in_bytes,int kind, +unsigned flags); +GC_INNER ptr_t GC_alloc_large(size_t lb,int k,unsigned flags); +GC_INNER void GC_freehblk(struct hblk*p); +GC_INNER GC_bool GC_expand_hp_inner(word n); +GC_INNER void GC_start_reclaim(GC_bool abort_if_found); +GC_INNER void GC_continue_reclaim(word sz,int kind); +GC_INNER GC_bool GC_reclaim_all(GC_stop_func stop_func,GC_bool ignore_old); +GC_INNER ptr_t GC_reclaim_generic(struct hblk*hbp,hdr*hhdr,size_t sz, +GC_bool init,ptr_t list, +signed_word*count); +GC_INNER GC_bool GC_block_empty(hdr*hhdr); +GC_INNER int GC_CALLBACK GC_never_stop_func(void); +GC_INNER GC_bool GC_try_to_collect_inner(GC_stop_func f); +#define GC_gcollect_inner()(void)GC_try_to_collect_inner(GC_never_stop_func) +#ifdef THREADS +GC_EXTERN GC_bool GC_in_thread_creation; +#endif +GC_EXTERN GC_bool GC_is_initialized; +GC_INNER void GC_collect_a_little_inner(int n); +GC_INNER void*GC_generic_malloc_inner(size_t lb,int k); +#if defined(DBG_HDRS_ALL)||defined(GC_GCJ_SUPPORT)||!defined(GC_NO_FINALIZATION) +GC_INNER void*GC_generic_malloc_inner_ignore_off_page(size_t lb,int k); +#endif +GC_INNER GC_bool GC_collect_or_expand(word needed_blocks, +GC_bool ignore_off_page,GC_bool retry); +GC_INNER ptr_t GC_allocobj(size_t sz,int kind); +#ifdef GC_ADD_CALLER +#ifdef GC_HAVE_RETURN_ADDR_PARENT +#define GC_DBG_EXTRAS GC_RETURN_ADDR_PARENT,NULL,0 +#else +#define GC_DBG_EXTRAS GC_RETURN_ADDR,NULL,0 +#endif +#else +#define GC_DBG_EXTRAS "unknown",0 +#endif +#ifdef GC_COLLECT_AT_MALLOC +extern size_t GC_dbg_collect_at_malloc_min_lb; +#define GC_DBG_COLLECT_AT_MALLOC(lb)(void)((lb)>=GC_dbg_collect_at_malloc_min_lb?(GC_gcollect(),0):0) +#else +#define GC_DBG_COLLECT_AT_MALLOC(lb)(void)0 +#endif +#if defined(THREAD_LOCAL_ALLOC)&&defined(GC_GCJ_SUPPORT) +GC_INNER void*GC_core_gcj_malloc(size_t,void*); +#endif +GC_INNER void GC_init_headers(void); +GC_INNER struct hblkhdr*GC_install_header(struct hblk*h); +GC_INNER GC_bool GC_install_counts(struct hblk*h,size_t sz); +GC_INNER void GC_remove_header(struct hblk*h); +GC_INNER void GC_remove_counts(struct hblk*h,size_t sz); +GC_INNER hdr*GC_find_header(ptr_t h); +GC_INNER void GC_add_to_heap(struct hblk*p,size_t bytes); +#ifdef USE_PROC_FOR_LIBRARIES +GC_INNER void GC_add_to_our_memory(ptr_t p,size_t bytes); +#else +#define GC_add_to_our_memory(p,bytes) +#endif +GC_INNER void GC_print_all_errors(void); +GC_EXTERN void (*GC_check_heap)(void); +GC_EXTERN void (*GC_print_all_smashed)(void); +GC_EXTERN void (*GC_print_heap_obj)(ptr_t p); +#if defined(LINUX)&&defined(__ELF__)&&!defined(SMALL_CONFIG) +void GC_print_address_map(void); +#endif +#ifndef SHORT_DBG_HDRS +GC_EXTERN GC_bool GC_findleak_delay_free; +GC_INNER GC_bool GC_check_leaked(ptr_t base); +#endif +GC_EXTERN GC_bool GC_have_errors; +#define VERBOSE 2 +#if!defined(NO_CLOCK)||!defined(SMALL_CONFIG) +extern int GC_print_stats; +#else +#define GC_print_stats 0 +#endif +#ifdef KEEP_BACK_PTRS +GC_EXTERN long GC_backtraces; +GC_INNER void GC_generate_random_backtrace_no_gc(void); +#endif +#ifdef LINT2 +#define GC_RAND_MAX (~0U>>1) +GC_API_PRIV long GC_random(void); +#endif +GC_EXTERN GC_bool GC_print_back_height; +#ifdef MAKE_BACK_GRAPH +void GC_print_back_graph_stats(void); +#endif +#ifdef THREADS +GC_INNER void GC_free_inner(void*p); +#endif +#ifdef DBG_HDRS_ALL +GC_INNER void*GC_debug_generic_malloc_inner(size_t lb,int k); +GC_INNER void*GC_debug_generic_malloc_inner_ignore_off_page(size_t lb, +int k); +#define GC_INTERNAL_MALLOC GC_debug_generic_malloc_inner +#define GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE GC_debug_generic_malloc_inner_ignore_off_page +#ifdef THREADS +GC_INNER void GC_debug_free_inner(void*p); +#define GC_INTERNAL_FREE GC_debug_free_inner +#else +#define GC_INTERNAL_FREE GC_debug_free +#endif +#else +#define GC_INTERNAL_MALLOC GC_generic_malloc_inner +#define GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE GC_generic_malloc_inner_ignore_off_page +#ifdef THREADS +#define GC_INTERNAL_FREE GC_free_inner +#else +#define GC_INTERNAL_FREE GC_free +#endif +#endif +#ifdef USE_MUNMAP +GC_INNER void GC_unmap_old(void); +GC_INNER void GC_merge_unmapped(void); +GC_INNER void GC_unmap(ptr_t start,size_t bytes); +GC_INNER void GC_remap(ptr_t start,size_t bytes); +GC_INNER void GC_unmap_gap(ptr_t start1,size_t bytes1,ptr_t start2, +size_t bytes2); +GC_INLINE ptr_t GC_unmap_end(ptr_t start,size_t bytes) +{ +return (ptr_t)((word)(start+bytes)&~(GC_page_size - 1)); +} +#endif +#ifdef CAN_HANDLE_FORK +GC_EXTERN int GC_handle_fork; +#endif +#ifdef GC_DISABLE_INCREMENTAL +#define GC_incremental FALSE +#define GC_auto_incremental FALSE +#define GC_manual_vdb FALSE +#define GC_dirty(p)(void)(p) +#define REACHABLE_AFTER_DIRTY(p)(void)(p) +#else +GC_EXTERN GC_bool GC_incremental; +GC_INNER void GC_read_dirty(GC_bool output_unneeded); +GC_INNER GC_bool GC_page_was_dirty(struct hblk*h); +GC_INNER void GC_remove_protection(struct hblk*h,word nblocks, +GC_bool pointerfree); +GC_INNER GC_bool GC_dirty_init(void); +GC_EXTERN GC_bool GC_manual_vdb; +#define GC_auto_incremental (GC_incremental&&!GC_manual_vdb) +GC_INNER void GC_dirty_inner(const void*p); +#define GC_dirty(p)(GC_manual_vdb?GC_dirty_inner(p):(void)0) +#define REACHABLE_AFTER_DIRTY(p)GC_reachable_here(p) +#endif +#define GC_base_C(p)((const void*)GC_base(( void*)(p))) +void GC_print_block_list(void); +void GC_print_hblkfreelist(void); +void GC_print_heap_sects(void); +void GC_print_static_roots(void); +#ifdef KEEP_BACK_PTRS +GC_INNER void GC_store_back_pointer(ptr_t source,ptr_t dest); +GC_INNER void GC_marked_for_finalization(ptr_t dest); +#define GC_STORE_BACK_PTR(source,dest)GC_store_back_pointer(source,dest) +#define GC_MARKED_FOR_FINALIZATION(dest)GC_marked_for_finalization(dest) +#else +#define GC_STORE_BACK_PTR(source,dest)(void)(source) +#define GC_MARKED_FOR_FINALIZATION(dest) +#endif +void GC_noop6(word,word,word,word,word,word); +GC_API void GC_CALL GC_noop1(word); +#ifndef GC_ATTR_FORMAT_PRINTF +#if GC_GNUC_PREREQ(3,0) +#define GC_ATTR_FORMAT_PRINTF(spec_argnum,first_checked)__attribute__((__format__(__printf__,spec_argnum,first_checked))) +#else +#define GC_ATTR_FORMAT_PRINTF(spec_argnum,first_checked) +#endif +#endif +GC_API_PRIV void GC_printf(const char*format,...) +GC_ATTR_FORMAT_PRINTF(1,2); +GC_API_PRIV void GC_err_printf(const char*format,...) +GC_ATTR_FORMAT_PRINTF(1,2); +GC_API_PRIV void GC_log_printf(const char*format,...) +GC_ATTR_FORMAT_PRINTF(1,2); +#ifndef GC_ANDROID_LOG +#define GC_PRINT_STATS_FLAG (GC_print_stats!=0) +#define GC_INFOLOG_PRINTF GC_COND_LOG_PRINTF +#define GC_verbose_log_printf GC_log_printf +#else +extern GC_bool GC_quiet; +#define GC_PRINT_STATS_FLAG (!GC_quiet) +#ifndef GC_INFOLOG_PRINTF +#define GC_INFOLOG_PRINTF if (GC_quiet){} else GC_info_log_printf +#endif +GC_INNER void GC_info_log_printf(const char*format,...) +GC_ATTR_FORMAT_PRINTF(1,2); +GC_INNER void GC_verbose_log_printf(const char*format,...) +GC_ATTR_FORMAT_PRINTF(1,2); +#endif +#define GC_COND_LOG_PRINTF if (EXPECT(!GC_print_stats,TRUE)){} else GC_log_printf +#define GC_VERBOSE_LOG_PRINTF if (EXPECT(GC_print_stats!=VERBOSE,TRUE)){} else GC_verbose_log_printf +#ifndef GC_DBGLOG_PRINTF +#define GC_DBGLOG_PRINTF if (!GC_PRINT_STATS_FLAG){} else GC_log_printf +#endif +void GC_err_puts(const char*s); +#define TO_KiB_UL(v)((unsigned long)(((v)+((1<<9)- 1))>>10)) +GC_EXTERN unsigned GC_fail_count; +GC_EXTERN long GC_large_alloc_warn_interval; +GC_EXTERN signed_word GC_bytes_found; +#ifndef GC_GET_HEAP_USAGE_NOT_NEEDED +GC_EXTERN word GC_reclaimed_bytes_before_gc; +#endif +#ifdef USE_MUNMAP +GC_EXTERN int GC_unmap_threshold; +GC_EXTERN GC_bool GC_force_unmap_on_gcollect; +#endif +#ifdef MSWIN32 +GC_EXTERN GC_bool GC_no_win32_dlls; +GC_EXTERN GC_bool GC_wnt; +#endif +#ifdef THREADS +#if (defined(MSWIN32)&&!defined(CONSOLE_LOG))||defined(MSWINCE) +GC_EXTERN CRITICAL_SECTION GC_write_cs; +#ifdef GC_ASSERTIONS +GC_EXTERN GC_bool GC_write_disabled; +#endif +#endif +#if defined(GC_DISABLE_INCREMENTAL)||defined(HAVE_LOCKFREE_AO_OR) +#define GC_acquire_dirty_lock()(void)0 +#define GC_release_dirty_lock()(void)0 +#else +#define GC_acquire_dirty_lock()do { } while (AO_test_and_set_acquire(&GC_fault_handler_lock)==AO_TS_SET) +#define GC_release_dirty_lock()AO_CLEAR(&GC_fault_handler_lock) +GC_EXTERN volatile AO_TS_t GC_fault_handler_lock; +#endif +#ifdef MSWINCE +GC_EXTERN GC_bool GC_dont_query_stack_min; +#endif +#elif defined(IA64) +GC_EXTERN ptr_t GC_save_regs_ret_val; +#endif +#ifdef THREAD_LOCAL_ALLOC +GC_EXTERN GC_bool GC_world_stopped; +GC_INNER void GC_mark_thread_local_free_lists(void); +#endif +#if defined(MPROTECT_VDB)&&defined(GWW_VDB) +GC_INNER GC_bool GC_gww_dirty_init(void); +#endif +#if defined(CHECKSUMS)||defined(PROC_VDB) +GC_INNER GC_bool GC_page_was_ever_dirty(struct hblk*h); +#endif +#ifdef CHECKSUMS +#if defined(MPROTECT_VDB)&&!defined(DARWIN) +void GC_record_fault(struct hblk*h); +#endif +void GC_check_dirty(void); +#endif +GC_INNER void GC_default_print_heap_obj_proc(ptr_t p); +GC_INNER void GC_setpagesize(void); +GC_INNER void GC_initialize_offsets(void); +GC_INNER void GC_bl_init(void); +GC_INNER void GC_bl_init_no_interiors(void); +GC_INNER void GC_start_debugging_inner(void); +GC_INNER void*GC_store_debug_info_inner(void*p,word sz,const char*str, +int linenum); +#ifdef REDIRECT_MALLOC +#ifdef GC_LINUX_THREADS +GC_INNER GC_bool GC_text_mapping(char*nm,ptr_t*startp,ptr_t*endp); +#endif +#elif defined(USE_WINALLOC) +GC_INNER void GC_add_current_malloc_heap(void); +#endif +#ifdef MAKE_BACK_GRAPH +GC_INNER void GC_build_back_graph(void); +GC_INNER void GC_traverse_back_graph(void); +#endif +#ifdef MSWIN32 +GC_INNER void GC_init_win32(void); +#endif +#if!defined(MSWIN32)&&!defined(MSWINCE)&&!defined(CYGWIN32) +GC_INNER void*GC_roots_present(ptr_t); +#endif +#ifdef GC_WIN32_THREADS +GC_INNER void GC_get_next_stack(char*start,char*limit,char**lo, +char**hi); +#if defined(MPROTECT_VDB)&&!defined(CYGWIN32) +GC_INNER void GC_set_write_fault_handler(void); +#endif +#if defined(WRAP_MARK_SOME)&&!defined(GC_PTHREADS) +GC_INNER GC_bool GC_started_thread_while_stopped(void); +#endif +#endif +#ifdef THREADS +GC_INNER void GC_reset_finalizer_nested(void); +GC_INNER unsigned char*GC_check_finalizer_nested(void); +GC_INNER void GC_do_blocking_inner(ptr_t data,void*context); +GC_INNER void GC_push_all_stacks(void); +#ifdef USE_PROC_FOR_LIBRARIES +GC_INNER GC_bool GC_segment_is_thread_stack(ptr_t lo,ptr_t hi); +#endif +#ifdef IA64 +GC_INNER ptr_t GC_greatest_stack_base_below(ptr_t bound); +#endif +#endif +#ifdef DYNAMIC_LOADING +GC_INNER GC_bool GC_register_main_static_data(void); +#ifdef DARWIN +GC_INNER void GC_init_dyld(void); +#endif +#endif +#ifdef SEARCH_FOR_DATA_START +GC_INNER void GC_init_linux_data_start(void); +void*GC_find_limit(void*,int); +#endif +#if defined(NETBSD)&&defined(__ELF__) +GC_INNER void GC_init_netbsd_elf(void); +void*GC_find_limit(void*,int); +#endif +#ifdef UNIX_LIKE +GC_INNER void GC_set_and_save_fault_handler(void (*handler)(int)); +#endif +#ifdef NEED_PROC_MAPS +#if defined(DYNAMIC_LOADING)&&defined(USE_PROC_FOR_LIBRARIES) +GC_INNER char*GC_parse_map_entry(char*buf_ptr,ptr_t*start,ptr_t*end, +char**prot,unsigned int*maj_dev, +char**mapping_name); +#endif +#if defined(IA64)||defined(INCLUDE_LINUX_THREAD_DESCR) +GC_INNER GC_bool GC_enclosing_mapping(ptr_t addr, +ptr_t*startp,ptr_t*endp); +#endif +GC_INNER char*GC_get_maps(void); +#endif +#ifdef GC_ASSERTIONS +#define GC_ASSERT(expr)do { if (!(expr)){ GC_err_printf("Assertion failure:%s:%d\n",__FILE__,__LINE__);ABORT("assertion failure");} } while (0) +GC_INNER word GC_compute_large_free_bytes(void); +GC_INNER word GC_compute_root_size(void); +#else +#define GC_ASSERT(expr) +#endif +#if _MSC_VER>=1700 +#define GC_STATIC_ASSERT(expr)static_assert(expr,"static assertion failed:" #expr) +#elif defined(static_assert)&&__STDC_VERSION__>=201112L +#define GC_STATIC_ASSERT(expr)static_assert(expr,#expr) +#elif defined(mips)&&!defined(__GNUC__) +#define GC_STATIC_ASSERT(expr)do { if (0){ char j[(expr)?1:-1];j[0]='\0';j[0]=j[0];} } while(0) +#else +#define GC_STATIC_ASSERT(expr)(void)sizeof(char[(expr)?1:-1]) +#endif +#if GC_GNUC_PREREQ(4,0) +#define NONNULL_ARG_NOT_NULL(arg)(*(volatile void**)&(arg)!=NULL) +#else +#define NONNULL_ARG_NOT_NULL(arg)(NULL!=(arg)) +#endif +#define COND_DUMP_CHECKS do { GC_ASSERT(GC_compute_large_free_bytes()==GC_large_free_bytes);GC_ASSERT(GC_compute_root_size()==GC_root_size);} while (0) +#ifndef NO_DEBUGGING +GC_EXTERN GC_bool GC_dump_regularly; +#define COND_DUMP if (EXPECT(GC_dump_regularly,FALSE)){ GC_dump_named(NULL);} else COND_DUMP_CHECKS +#else +#define COND_DUMP COND_DUMP_CHECKS +#endif +#if defined(PARALLEL_MARK) +#define GC_markers_m1 GC_parallel +GC_EXTERN GC_bool GC_parallel_mark_disabled; +GC_INNER void GC_wait_for_markers_init(void); +GC_INNER void GC_acquire_mark_lock(void); +GC_INNER void GC_release_mark_lock(void); +GC_INNER void GC_notify_all_builder(void); +GC_INNER void GC_wait_for_reclaim(void); +GC_EXTERN signed_word GC_fl_builder_count; +GC_INNER void GC_notify_all_marker(void); +GC_INNER void GC_wait_marker(void); +GC_EXTERN word GC_mark_no; +GC_INNER void GC_help_marker(word my_mark_no); +GC_INNER void GC_start_mark_threads_inner(void); +#endif +#if defined(GC_PTHREADS)&&!defined(GC_WIN32_THREADS)&&!defined(NACL)&&!defined(GC_DARWIN_THREADS)&&!defined(SIG_SUSPEND) +#if (defined(GC_LINUX_THREADS)||defined(GC_DGUX386_THREADS))&&!defined(GC_USESIGRT_SIGNALS) +#if defined(SPARC)&&!defined(SIGPWR) +#define SIG_SUSPEND SIGLOST +#else +#define SIG_SUSPEND SIGPWR +#endif +#elif defined(GC_OPENBSD_THREADS) +#ifndef GC_OPENBSD_UTHREADS +#define SIG_SUSPEND SIGXFSZ +#endif +#elif defined(_SIGRTMIN)&&!defined(CPPCHECK) +#define SIG_SUSPEND _SIGRTMIN+6 +#else +#define SIG_SUSPEND SIGRTMIN+6 +#endif +#endif +#if defined(GC_PTHREADS)&&!defined(GC_SEM_INIT_PSHARED) +#define GC_SEM_INIT_PSHARED 0 +#endif +#if (defined(UNIX_LIKE)||(defined(NEED_FIND_LIMIT)&&defined(CYGWIN32)))&&!defined(GC_NO_SIGSETJMP) +#if defined(SUNOS5SIGS)&&!defined(FREEBSD)&&!defined(LINUX) +EXTERN_C_END +#include +EXTERN_C_BEGIN +#endif +#define SETJMP(env)sigsetjmp(env,1) +#define LONGJMP(env,val)siglongjmp(env,val) +#define JMP_BUF sigjmp_buf +#else +#ifdef ECOS +#define SETJMP(env)hal_setjmp(env) +#else +#define SETJMP(env)setjmp(env) +#endif +#define LONGJMP(env,val)longjmp(env,val) +#define JMP_BUF jmp_buf +#endif +#if defined(HEURISTIC2)||defined(SEARCH_FOR_DATA_START)||((defined(SVR4)||defined(AIX)||defined(DGUX)||(defined(LINUX)&&defined(SPARC)))&&!defined(PCR)) +#define NEED_FIND_LIMIT +#endif +#if defined(DATASTART_USES_BSDGETDATASTART) +EXTERN_C_END +#include +EXTERN_C_BEGIN +#if!defined(PCR) +#define NEED_FIND_LIMIT +#endif +GC_INNER ptr_t GC_FreeBSDGetDataStart(size_t,ptr_t); +#define DATASTART_IS_FUNC +#endif +#if (defined(NETBSD)||defined(OPENBSD))&&defined(__ELF__)&&!defined(NEED_FIND_LIMIT) +#define NEED_FIND_LIMIT +#endif +#if defined(IA64)&&!defined(NEED_FIND_LIMIT) +#define NEED_FIND_LIMIT +#endif +#if defined(NEED_FIND_LIMIT)||(defined(USE_PROC_FOR_LIBRARIES)&&defined(THREADS)) +GC_EXTERN JMP_BUF GC_jmp_buf; +GC_INNER void GC_setup_temporary_fault_handler(void); +GC_INNER void GC_reset_fault_handler(void); +#endif +#if defined(CANCEL_SAFE) +#if defined(GC_ASSERTIONS)&&(defined(USE_COMPILER_TLS)||(defined(LINUX)&&!defined(ARM32)&&GC_GNUC_PREREQ(3,3)||defined(HPUX))) +extern __thread unsigned char GC_cancel_disable_count; +#define NEED_CANCEL_DISABLE_COUNT +#define INCR_CANCEL_DISABLE()++GC_cancel_disable_count +#define DECR_CANCEL_DISABLE()--GC_cancel_disable_count +#define ASSERT_CANCEL_DISABLED()GC_ASSERT(GC_cancel_disable_count > 0) +#else +#define INCR_CANCEL_DISABLE() +#define DECR_CANCEL_DISABLE() +#define ASSERT_CANCEL_DISABLED()(void)0 +#endif +#define DISABLE_CANCEL(state)do { pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,&state);INCR_CANCEL_DISABLE();} while (0) +#define RESTORE_CANCEL(state)do { ASSERT_CANCEL_DISABLED();pthread_setcancelstate(state,NULL);DECR_CANCEL_DISABLE();} while (0) +#else +#define DISABLE_CANCEL(state)(void)0 +#define RESTORE_CANCEL(state)(void)0 +#define ASSERT_CANCEL_DISABLED()(void)0 +#endif +EXTERN_C_END +#endif +#ifdef KEEP_BACK_PTRS +#ifndef GC_BACKPTR_H +#define GC_BACKPTR_H +#ifndef GC_H +#endif +#ifdef __cplusplus +extern "C" { +#endif +typedef enum { +GC_UNREFERENCED, +GC_NO_SPACE, +GC_REFD_FROM_ROOT, +GC_REFD_FROM_REG, +GC_REFD_FROM_HEAP, +GC_FINALIZER_REFD +} GC_ref_kind; +GC_API GC_ref_kind GC_CALL GC_get_back_ptr_info(void*, +void**,size_t*) +GC_ATTR_NONNULL(1); +GC_API void*GC_CALL GC_generate_random_heap_address(void); +GC_API void*GC_CALL GC_generate_random_valid_address(void); +GC_API void GC_CALL GC_generate_random_backtrace(void); +GC_API void GC_CALL GC_print_backtrace(void*)GC_ATTR_NONNULL(1); +#ifdef __cplusplus +} +#endif +#endif +#endif +EXTERN_C_BEGIN +#if CPP_WORDSZ==32 +#define START_FLAG (word)0xfedcedcb +#define END_FLAG (word)0xbcdecdef +#else +#define START_FLAG GC_WORD_C(0xFEDCEDCBfedcedcb) +#define END_FLAG GC_WORD_C(0xBCDECDEFbcdecdef) +#endif +#if defined(KEEP_BACK_PTRS)||defined(PRINT_BLACK_LIST)||defined(MAKE_BACK_GRAPH) +#define NOT_MARKED (ptr_t)0 +#define MARKED_FOR_FINALIZATION ((ptr_t)(word)2) +#define MARKED_FROM_REGISTER ((ptr_t)(word)4) +#endif +typedef struct { +#if defined(KEEP_BACK_PTRS)||defined(MAKE_BACK_GRAPH) +#if ALIGNMENT==1 +#define HIDE_BACK_PTR(p)GC_HIDE_POINTER(~1&(word)(p)) +#else +#define HIDE_BACK_PTR(p)GC_HIDE_POINTER(p) +#endif +#ifdef KEEP_BACK_PTRS +GC_hidden_pointer oh_back_ptr; +#endif +#ifdef MAKE_BACK_GRAPH +GC_hidden_pointer oh_bg_ptr; +#endif +#if defined(KEEP_BACK_PTRS)!=defined(MAKE_BACK_GRAPH) +word oh_dummy; +#endif +#endif +const char*oh_string; +signed_word oh_int; +#ifdef NEED_CALLINFO +struct callinfo oh_ci[NFRAMES]; +#endif +#ifndef SHORT_DBG_HDRS +word oh_sz; +word oh_sf; +#endif +} oh; +#ifdef SHORT_DBG_HDRS +#define DEBUG_BYTES (sizeof (oh)) +#define UNCOLLECTABLE_DEBUG_BYTES DEBUG_BYTES +#else +#define UNCOLLECTABLE_DEBUG_BYTES (sizeof (oh)+sizeof (word)) +#define DEBUG_BYTES (UNCOLLECTABLE_DEBUG_BYTES - EXTRA_BYTES) +#endif +#define SIMPLE_ROUNDED_UP_WORDS(n)BYTES_TO_WORDS((n)+WORDS_TO_BYTES(1)- 1) +#if defined(SAVE_CALL_CHAIN) +struct callinfo; +GC_INNER void GC_save_callers(struct callinfo info[NFRAMES]); +GC_INNER void GC_print_callers(struct callinfo info[NFRAMES]); +#define ADD_CALL_CHAIN(base,ra)GC_save_callers(((oh*)(base))->oh_ci) +#define PRINT_CALL_CHAIN(base)GC_print_callers(((oh*)(base))->oh_ci) +#elif defined(GC_ADD_CALLER) +struct callinfo; +GC_INNER void GC_print_callers(struct callinfo info[NFRAMES]); +#define ADD_CALL_CHAIN(base,ra)((oh*)(base))->oh_ci[0].ci_pc=(ra) +#define PRINT_CALL_CHAIN(base)GC_print_callers(((oh*)(base))->oh_ci) +#else +#define ADD_CALL_CHAIN(base,ra) +#define PRINT_CALL_CHAIN(base) +#endif +#ifdef GC_ADD_CALLER +#define OPT_RA ra, +#else +#define OPT_RA +#endif +#ifdef SHORT_DBG_HDRS +#define GC_has_other_debug_info(p)1 +#else +GC_INNER int GC_has_other_debug_info(ptr_t p); +#endif +#if defined(KEEP_BACK_PTRS)||defined(MAKE_BACK_GRAPH) +#if defined(SHORT_DBG_HDRS)&&!defined(CPPCHECK) +#error Non-ptr stored in object results in GC_HAS_DEBUG_INFO malfunction +#endif +#if defined(PARALLEL_MARK)&&defined(KEEP_BACK_PTRS) +#define GC_HAS_DEBUG_INFO(p)((AO_load((volatile AO_t*)(p))&1)!=0&&GC_has_other_debug_info(p)> 0) +#else +#define GC_HAS_DEBUG_INFO(p)((*(word*)(p)&1)&&GC_has_other_debug_info(p)> 0) +#endif +#else +#define GC_HAS_DEBUG_INFO(p)(GC_has_other_debug_info(p)> 0) +#endif +EXTERN_C_END +#endif +#ifdef MAKE_BACK_GRAPH +#define MAX_IN 10 +#if (!defined(DBG_HDRS_ALL)||(ALIGNMENT!=CPP_WORDSZ/8))&&!defined(CPPCHECK) +#error The configuration does not support MAKE_BACK_GRAPH +#endif +#define FLAG_MANY 2 +typedef struct back_edges_struct { +word n_edges; +unsigned short flags; +#define RETAIN 1 +unsigned short height_gc_no; +signed_word height; +#define HEIGHT_UNKNOWN (-2) +#define HEIGHT_IN_PROGRESS (-1) +ptr_t edges[MAX_IN]; +struct back_edges_struct*cont; +} back_edges; +#define MAX_BACK_EDGE_STRUCTS 100000 +static back_edges*back_edge_space=0; +STATIC int GC_n_back_edge_structs=0; +static back_edges*avail_back_edges=0; +static back_edges*new_back_edges(void) +{ +if (0==back_edge_space){ +size_t bytes_to_get=ROUNDUP_PAGESIZE_IF_MMAP(MAX_BACK_EDGE_STRUCTS +*sizeof(back_edges)); +GC_ASSERT(GC_page_size!=0); +back_edge_space=(back_edges*)GET_MEM(bytes_to_get); +if (NULL==back_edge_space) +ABORT("Insufficient memory for back edges"); +GC_add_to_our_memory((ptr_t)back_edge_space,bytes_to_get); +} +if (0!=avail_back_edges){ +back_edges*result=avail_back_edges; +avail_back_edges=result->cont; +result->cont=0; +return result; +} +if (GC_n_back_edge_structs>=MAX_BACK_EDGE_STRUCTS - 1){ +ABORT("Needed too much space for back edges:adjust " +"MAX_BACK_EDGE_STRUCTS"); +} +return back_edge_space+(GC_n_back_edge_structs++); +} +static void deallocate_back_edges(back_edges*p) +{ +back_edges*last=p; +while (0!=last->cont)last=last->cont; +last->cont=avail_back_edges; +avail_back_edges=p; +} +#define INITIAL_IN_PROGRESS 10000 +static ptr_t*in_progress_space=0; +static size_t in_progress_size=0; +static size_t n_in_progress=0; +static void push_in_progress(ptr_t p) +{ +if (n_in_progress>=in_progress_size){ +ptr_t*new_in_progress_space; +GC_ASSERT(GC_page_size!=0); +if (NULL==in_progress_space){ +in_progress_size=ROUNDUP_PAGESIZE_IF_MMAP(INITIAL_IN_PROGRESS +*sizeof(ptr_t)) +/sizeof(ptr_t); +new_in_progress_space= +(ptr_t*)GET_MEM(in_progress_size*sizeof(ptr_t)); +} else { +in_progress_size*=2; +new_in_progress_space=(ptr_t*) +GET_MEM(in_progress_size*sizeof(ptr_t)); +if (new_in_progress_space!=NULL) +BCOPY(in_progress_space,new_in_progress_space, +n_in_progress*sizeof(ptr_t)); +} +GC_add_to_our_memory((ptr_t)new_in_progress_space, +in_progress_size*sizeof(ptr_t)); +#ifndef GWW_VDB +GC_scratch_recycle_no_gww(in_progress_space, +n_in_progress*sizeof(ptr_t)); +#elif defined(LINT2) +GC_noop1((word)in_progress_space); +#endif +in_progress_space=new_in_progress_space; +} +if (in_progress_space==0) +ABORT("MAKE_BACK_GRAPH:Out of in-progress space:" +"Huge linear data structure?"); +in_progress_space[n_in_progress++]=p; +} +static GC_bool is_in_progress(ptr_t p) +{ +size_t i; +for (i=0;i < n_in_progress;++i){ +if (in_progress_space[i]==p)return TRUE; +} +return FALSE; +} +GC_INLINE void pop_in_progress(ptr_t p GC_ATTR_UNUSED) +{ +--n_in_progress; +GC_ASSERT(in_progress_space[n_in_progress]==p); +} +#define GET_OH_BG_PTR(p)(ptr_t)GC_REVEAL_POINTER(((oh*)(p))->oh_bg_ptr) +#define SET_OH_BG_PTR(p,q)(((oh*)(p))->oh_bg_ptr=GC_HIDE_POINTER(q)) +static void ensure_struct(ptr_t p) +{ +ptr_t old_back_ptr=GET_OH_BG_PTR(p); +if (!((word)old_back_ptr&FLAG_MANY)){ +back_edges*be=new_back_edges(); +be->flags=0; +if (0==old_back_ptr){ +be->n_edges=0; +} else { +be->n_edges=1; +be->edges[0]=old_back_ptr; +} +be->height=HEIGHT_UNKNOWN; +be->height_gc_no=(unsigned short)(GC_gc_no - 1); +GC_ASSERT((word)be>=(word)back_edge_space); +SET_OH_BG_PTR(p,(word)be|FLAG_MANY); +} +} +static void add_edge(ptr_t p,ptr_t q) +{ +ptr_t pred=GET_OH_BG_PTR(q); +back_edges*be,*be_cont; +word i; +GC_ASSERT(p==GC_base(p)&&q==GC_base(q)); +if (!GC_HAS_DEBUG_INFO(q)||!GC_HAS_DEBUG_INFO(p)){ +return; +} +if (NULL==pred){ +static unsigned random_number=13; +#define GOT_LUCKY_NUMBER (((++random_number)&0x7f)==0) +SET_OH_BG_PTR(q,p); +if (GOT_LUCKY_NUMBER)ensure_struct(q); +return; +} +{ +back_edges*e=(back_edges*)((word)pred&~FLAG_MANY); +word n_edges; +word total; +int local=0; +if (((word)pred&FLAG_MANY)!=0){ +n_edges=e->n_edges; +} else if (((word)COVERT_DATAFLOW(pred)&1)==0){ +n_edges=1; +local=-1; +} else { +n_edges=0; +} +for (total=0;total < n_edges;++total){ +if (local==MAX_IN){ +e=e->cont; +local=0; +} +if (local>=0) +pred=e->edges[local++]; +if (pred==p) +return; +} +} +ensure_struct(q); +be=(back_edges*)((word)GET_OH_BG_PTR(q)&~FLAG_MANY); +for (i=be->n_edges,be_cont=be;i > MAX_IN;i-=MAX_IN) +be_cont=be_cont->cont; +if (i==MAX_IN){ +be_cont->cont=new_back_edges(); +be_cont=be_cont->cont; +i=0; +} +be_cont->edges[i]=p; +be->n_edges++; +#ifdef DEBUG_PRINT_BIG_N_EDGES +if (GC_print_stats==VERBOSE&&be->n_edges==100){ +GC_err_printf("The following object has big in-degree:\n"); +GC_print_heap_obj(q); +} +#endif +} +typedef void (*per_object_func)(ptr_t p,size_t n_bytes,word gc_descr); +static void per_object_helper(struct hblk*h,word fn) +{ +hdr*hhdr=HDR(h); +size_t sz=(size_t)hhdr->hb_sz; +word descr=hhdr->hb_descr; +per_object_func f=(per_object_func)fn; +size_t i=0; +do { +f((ptr_t)(h->hb_body+i),sz,descr); +i+=sz; +} while (i+sz<=BYTES_TO_WORDS(HBLKSIZE)); +} +GC_INLINE void GC_apply_to_each_object(per_object_func f) +{ +GC_apply_to_all_blocks(per_object_helper,(word)f); +} +static void reset_back_edge(ptr_t p,size_t n_bytes GC_ATTR_UNUSED, +word gc_descr GC_ATTR_UNUSED) +{ +if (GC_HAS_DEBUG_INFO(p)){ +ptr_t old_back_ptr=GET_OH_BG_PTR(p); +if ((word)old_back_ptr&FLAG_MANY){ +back_edges*be=(back_edges*)((word)old_back_ptr&~FLAG_MANY); +if (!(be->flags&RETAIN)){ +deallocate_back_edges(be); +SET_OH_BG_PTR(p,0); +} else { +GC_ASSERT(GC_is_marked(p)); +be->n_edges=0; +if (0!=be->cont){ +deallocate_back_edges(be->cont); +be->cont=0; +} +GC_ASSERT(GC_is_marked(p)); +be->flags&=~RETAIN; +} +} else { +SET_OH_BG_PTR(p,0); +} +} +} +static void add_back_edges(ptr_t p,size_t n_bytes,word gc_descr) +{ +word*currentp=(word*)(p+sizeof(oh)); +if((gc_descr&GC_DS_TAGS)!=GC_DS_LENGTH){ +gc_descr=n_bytes; +} +while ((word)currentp < (word)(p+gc_descr)){ +word current=*currentp++; +FIXUP_POINTER(current); +if (current>=(word)GC_least_plausible_heap_addr&& +current<=(word)GC_greatest_plausible_heap_addr){ +ptr_t target=(ptr_t)GC_base((void*)current); +if (0!=target){ +add_edge(p,target); +} +} +} +} +GC_INNER void GC_build_back_graph(void) +{ +GC_ASSERT(I_HOLD_LOCK()); +GC_apply_to_each_object(add_back_edges); +} +static word backwards_height(ptr_t p) +{ +word result; +ptr_t pred=GET_OH_BG_PTR(p); +back_edges*be; +if (NULL==pred) +return 1; +if (((word)pred&FLAG_MANY)==0){ +if (is_in_progress(p))return 0; +push_in_progress(p); +result=backwards_height(pred)+1; +pop_in_progress(p); +return result; +} +be=(back_edges*)((word)pred&~FLAG_MANY); +if (be->height>=0&&be->height_gc_no==(unsigned short)GC_gc_no) +return be->height; +if (be->height==HEIGHT_IN_PROGRESS)return 0; +result=(be->height > 0?be->height:1); +be->height=HEIGHT_IN_PROGRESS; +{ +back_edges*e=be; +word n_edges; +word total; +int local=0; +if (((word)pred&FLAG_MANY)!=0){ +n_edges=e->n_edges; +} else if (((word)pred&1)==0){ +n_edges=1; +local=-1; +} else { +n_edges=0; +} +for (total=0;total < n_edges;++total){ +word this_height; +if (local==MAX_IN){ +e=e->cont; +local=0; +} +if (local>=0) +pred=e->edges[local++]; +if (GC_is_marked(pred)&&((word)GET_OH_BG_PTR(p)&FLAG_MANY)==0){ +GC_COND_LOG_PRINTF("Found bogus pointer from %p to %p\n", +(void*)pred,(void*)p); +this_height=1; +} else { +this_height=backwards_height(pred); +} +if (this_height>=result) +result=this_height+1; +} +} +be->height=result; +be->height_gc_no=(unsigned short)GC_gc_no; +return result; +} +STATIC word GC_max_height=0; +STATIC ptr_t GC_deepest_obj=NULL; +static void update_max_height(ptr_t p,size_t n_bytes GC_ATTR_UNUSED, +word gc_descr GC_ATTR_UNUSED) +{ +if (GC_is_marked(p)&&GC_HAS_DEBUG_INFO(p)){ +word p_height=0; +ptr_t p_deepest_obj=0; +ptr_t back_ptr; +back_edges*be=0; +back_ptr=GET_OH_BG_PTR(p); +if (0!=back_ptr&&((word)back_ptr&FLAG_MANY)){ +be=(back_edges*)((word)back_ptr&~FLAG_MANY); +if (be->height!=HEIGHT_UNKNOWN)p_height=be->height; +} +{ +ptr_t pred=GET_OH_BG_PTR(p); +back_edges*e=(back_edges*)((word)pred&~FLAG_MANY); +word n_edges; +word total; +int local=0; +if (((word)pred&FLAG_MANY)!=0){ +n_edges=e->n_edges; +} else if (pred!=NULL&&((word)pred&1)==0){ +n_edges=1; +local=-1; +} else { +n_edges=0; +} +for (total=0;total < n_edges;++total){ +if (local==MAX_IN){ +e=e->cont; +local=0; +} +if (local>=0) +pred=e->edges[local++]; +if (!GC_is_marked(pred)&&GC_HAS_DEBUG_INFO(pred)){ +word this_height=backwards_height(pred); +if (this_height > p_height){ +p_height=this_height; +p_deepest_obj=pred; +} +} +} +} +if (p_height > 0){ +if (be==0){ +ensure_struct(p); +back_ptr=GET_OH_BG_PTR(p); +be=(back_edges*)((word)back_ptr&~FLAG_MANY); +} +be->flags|=RETAIN; +be->height=p_height; +be->height_gc_no=(unsigned short)GC_gc_no; +} +if (p_height > GC_max_height){ +GC_max_height=p_height; +GC_deepest_obj=p_deepest_obj; +} +} +} +STATIC word GC_max_max_height=0; +GC_INNER void GC_traverse_back_graph(void) +{ +GC_ASSERT(I_HOLD_LOCK()); +GC_max_height=0; +GC_apply_to_each_object(update_max_height); +if (0!=GC_deepest_obj) +GC_set_mark_bit(GC_deepest_obj); +} +void GC_print_back_graph_stats(void) +{ +GC_ASSERT(I_HOLD_LOCK()); +GC_printf("Maximum backwards height of reachable objects at GC %lu is %lu\n", +(unsigned long)GC_gc_no,(unsigned long)GC_max_height); +if (GC_max_height > GC_max_max_height){ +ptr_t obj=GC_deepest_obj; +GC_max_max_height=GC_max_height; +UNLOCK(); +GC_err_printf( +"The following unreachable object is last in a longest chain " +"of unreachable objects:\n"); +GC_print_heap_obj(obj); +LOCK(); +} +GC_COND_LOG_PRINTF("Needed max total of %d back-edge structs\n", +GC_n_back_edge_structs); +GC_apply_to_each_object(reset_back_edge); +GC_deepest_obj=0; +} +#endif +STATIC word*GC_old_normal_bl=NULL; +STATIC word*GC_incomplete_normal_bl=NULL; +STATIC word*GC_old_stack_bl=NULL; +STATIC word*GC_incomplete_stack_bl=NULL; +STATIC word GC_total_stack_black_listed=0; +GC_INNER word GC_black_list_spacing=MINHINCR*HBLKSIZE; +STATIC void GC_clear_bl(word*); +GC_INNER void GC_default_print_heap_obj_proc(ptr_t p) +{ +ptr_t base=(ptr_t)GC_base(p); +int kind=HDR(base)->hb_obj_kind; +GC_err_printf("object at %p of appr. %lu bytes (%s)\n", +(void*)base,(unsigned long)GC_size(base), +kind==PTRFREE?"atomic": +IS_UNCOLLECTABLE(kind)?"uncollectable":"composite"); +} +GC_INNER void (*GC_print_heap_obj)(ptr_t p)=GC_default_print_heap_obj_proc; +#ifdef PRINT_BLACK_LIST +STATIC void GC_print_blacklisted_ptr(word p,ptr_t source, +const char*kind_str) +{ +ptr_t base=(ptr_t)GC_base(source); +if (0==base){ +GC_err_printf("Black listing (%s)%p referenced from %p in %s\n", +kind_str,(void*)p,(void*)source, +NULL!=source?"root set":"register"); +} else { +GC_err_printf("Black listing (%s)%p referenced from %p in" +" object at %p of appr. %lu bytes\n", +kind_str,(void*)p,(void*)source, +(void*)base,(unsigned long)GC_size(base)); +} +} +#endif +GC_INNER void GC_bl_init_no_interiors(void) +{ +if (GC_incomplete_normal_bl==0){ +GC_old_normal_bl=(word*)GC_scratch_alloc(sizeof(page_hash_table)); +GC_incomplete_normal_bl=(word*)GC_scratch_alloc( +sizeof(page_hash_table)); +if (GC_old_normal_bl==0||GC_incomplete_normal_bl==0){ +GC_err_printf("Insufficient memory for black list\n"); +EXIT(); +} +GC_clear_bl(GC_old_normal_bl); +GC_clear_bl(GC_incomplete_normal_bl); +} +} +GC_INNER void GC_bl_init(void) +{ +if (!GC_all_interior_pointers){ +GC_bl_init_no_interiors(); +} +GC_ASSERT(NULL==GC_old_stack_bl&&NULL==GC_incomplete_stack_bl); +GC_old_stack_bl=(word*)GC_scratch_alloc(sizeof(page_hash_table)); +GC_incomplete_stack_bl=(word*)GC_scratch_alloc(sizeof(page_hash_table)); +if (GC_old_stack_bl==0||GC_incomplete_stack_bl==0){ +GC_err_printf("Insufficient memory for black list\n"); +EXIT(); +} +GC_clear_bl(GC_old_stack_bl); +GC_clear_bl(GC_incomplete_stack_bl); +} +STATIC void GC_clear_bl(word*doomed) +{ +BZERO(doomed,sizeof(page_hash_table)); +} +STATIC void GC_copy_bl(word*old,word*dest) +{ +BCOPY(old,dest,sizeof(page_hash_table)); +} +static word total_stack_black_listed(void); +GC_INNER void GC_promote_black_lists(void) +{ +word*very_old_normal_bl=GC_old_normal_bl; +word*very_old_stack_bl=GC_old_stack_bl; +GC_old_normal_bl=GC_incomplete_normal_bl; +GC_old_stack_bl=GC_incomplete_stack_bl; +if (!GC_all_interior_pointers){ +GC_clear_bl(very_old_normal_bl); +} +GC_clear_bl(very_old_stack_bl); +GC_incomplete_normal_bl=very_old_normal_bl; +GC_incomplete_stack_bl=very_old_stack_bl; +GC_total_stack_black_listed=total_stack_black_listed(); +GC_VERBOSE_LOG_PRINTF( +"%lu bytes in heap blacklisted for interior pointers\n", +(unsigned long)GC_total_stack_black_listed); +if (GC_total_stack_black_listed!=0){ +GC_black_list_spacing= +HBLKSIZE*(GC_heapsize/GC_total_stack_black_listed); +} +if (GC_black_list_spacing < 3*HBLKSIZE){ +GC_black_list_spacing=3*HBLKSIZE; +} +if (GC_black_list_spacing > MAXHINCR*HBLKSIZE){ +GC_black_list_spacing=MAXHINCR*HBLKSIZE; +} +} +GC_INNER void GC_unpromote_black_lists(void) +{ +if (!GC_all_interior_pointers){ +GC_copy_bl(GC_old_normal_bl,GC_incomplete_normal_bl); +} +GC_copy_bl(GC_old_stack_bl,GC_incomplete_stack_bl); +} +#if defined(PARALLEL_MARK)&&defined(THREAD_SANITIZER) +#define backlist_set_pht_entry_from_index(db,index)set_pht_entry_from_index_concurrent(db,index) +#else +#define backlist_set_pht_entry_from_index(bl,index)set_pht_entry_from_index(bl,index) +#endif +#ifdef PRINT_BLACK_LIST +GC_INNER void GC_add_to_black_list_normal(word p,ptr_t source) +#else +GC_INNER void GC_add_to_black_list_normal(word p) +#endif +{ +if (GC_modws_valid_offsets[p&(sizeof(word)-1)]){ +word index=PHT_HASH((word)p); +if (HDR(p)==0||get_pht_entry_from_index(GC_old_normal_bl,index)){ +#ifdef PRINT_BLACK_LIST +if (!get_pht_entry_from_index(GC_incomplete_normal_bl,index)){ +GC_print_blacklisted_ptr(p,source,"normal"); +} +#endif +backlist_set_pht_entry_from_index(GC_incomplete_normal_bl,index); +} +} +} +#ifdef PRINT_BLACK_LIST +GC_INNER void GC_add_to_black_list_stack(word p,ptr_t source) +#else +GC_INNER void GC_add_to_black_list_stack(word p) +#endif +{ +word index=PHT_HASH((word)p); +if (HDR(p)==0||get_pht_entry_from_index(GC_old_stack_bl,index)){ +#ifdef PRINT_BLACK_LIST +if (!get_pht_entry_from_index(GC_incomplete_stack_bl,index)){ +GC_print_blacklisted_ptr(p,source,"stack"); +} +#endif +backlist_set_pht_entry_from_index(GC_incomplete_stack_bl,index); +} +} +struct hblk*GC_is_black_listed(struct hblk*h,word len) +{ +word index=PHT_HASH((word)h); +word i; +word nblocks; +if (!GC_all_interior_pointers +&&(get_pht_entry_from_index(GC_old_normal_bl,index) +||get_pht_entry_from_index(GC_incomplete_normal_bl,index))){ +return (h+1); +} +nblocks=divHBLKSZ(len); +for (i=0;;){ +if (GC_old_stack_bl[divWORDSZ(index)]==0 +&&GC_incomplete_stack_bl[divWORDSZ(index)]==0){ +i+=WORDSZ - modWORDSZ(index); +} else { +if (get_pht_entry_from_index(GC_old_stack_bl,index) +||get_pht_entry_from_index(GC_incomplete_stack_bl,index)){ +return(h+i+1); +} +i++; +} +if (i>=nblocks)break; +index=PHT_HASH((word)(h+i)); +} +return(0); +} +STATIC word GC_number_stack_black_listed(struct hblk*start, +struct hblk*endp1) +{ +struct hblk*h; +word result=0; +for (h=start;(word)h < (word)endp1;h++){ +word index=PHT_HASH((word)h); +if (get_pht_entry_from_index(GC_old_stack_bl,index))result++; +} +return(result); +} +static word total_stack_black_listed(void) +{ +unsigned i; +word total=0; +for (i=0;i < GC_n_heap_sects;i++){ +struct hblk*start=(struct hblk*)GC_heap_sects[i].hs_start; +struct hblk*endp1=start+divHBLKSZ(GC_heap_sects[i].hs_bytes); +total+=GC_number_stack_black_listed(start,endp1); +} +return(total*HBLKSIZE); +} +#ifdef CHECKSUMS +#define NSUMS 10000 +#define OFFSET 0x10000 +typedef struct { +GC_bool new_valid; +word old_sum; +word new_sum; +struct hblk*block; +} page_entry; +page_entry GC_sums[NSUMS]; +STATIC word GC_faulted[NSUMS]={ 0 }; +STATIC size_t GC_n_faulted=0; +#if defined(MPROTECT_VDB)&&!defined(DARWIN) +void GC_record_fault(struct hblk*h) +{ +word page=(word)h&~(GC_page_size - 1); +GC_ASSERT(GC_page_size!=0); +if (GC_n_faulted>=NSUMS)ABORT("write fault log overflowed"); +GC_faulted[GC_n_faulted++]=page; +} +#endif +STATIC GC_bool GC_was_faulted(struct hblk*h) +{ +size_t i; +word page=(word)h&~(GC_page_size - 1); +for (i=0;i < GC_n_faulted;++i){ +if (GC_faulted[i]==page)return TRUE; +} +return FALSE; +} +STATIC word GC_checksum(struct hblk*h) +{ +word*p=(word*)h; +word*lim=(word*)(h+1); +word result=0; +while ((word)p < (word)lim){ +result+=*p++; +} +return(result|0x80000000); +} +int GC_n_dirty_errors=0; +int GC_n_faulted_dirty_errors=0; +unsigned long GC_n_clean=0; +unsigned long GC_n_dirty=0; +STATIC void GC_update_check_page(struct hblk*h,int index) +{ +page_entry*pe=GC_sums+index; +hdr*hhdr=HDR(h); +struct hblk*b; +if (pe->block!=0&&pe->block!=h+OFFSET)ABORT("goofed"); +pe->old_sum=pe->new_sum; +pe->new_sum=GC_checksum(h); +#if!defined(MSWIN32)&&!defined(MSWINCE) +if (pe->new_sum!=0x80000000&&!GC_page_was_ever_dirty(h)){ +GC_err_printf("GC_page_was_ever_dirty(%p)is wrong\n",(void*)h); +} +#endif +if (GC_page_was_dirty(h)){ +GC_n_dirty++; +} else { +GC_n_clean++; +} +b=h; +while (IS_FORWARDING_ADDR_OR_NIL(hhdr)&&hhdr!=0){ +b-=(word)hhdr; +hhdr=HDR(b); +} +if (pe->new_valid +&&hhdr!=0&&hhdr->hb_descr!=0 +&&pe->old_sum!=pe->new_sum){ +if (!GC_page_was_dirty(h)||!GC_page_was_ever_dirty(h)){ +GC_bool was_faulted=GC_was_faulted(h); +GC_n_dirty_errors++; +if (was_faulted)GC_n_faulted_dirty_errors++; +} +} +pe->new_valid=TRUE; +pe->block=h+OFFSET; +} +word GC_bytes_in_used_blocks=0; +STATIC void GC_add_block(struct hblk*h,word dummy GC_ATTR_UNUSED) +{ +hdr*hhdr=HDR(h); +GC_bytes_in_used_blocks+=(hhdr->hb_sz+HBLKSIZE-1)&~(HBLKSIZE-1); +} +STATIC void GC_check_blocks(void) +{ +word bytes_in_free_blocks=GC_large_free_bytes; +GC_bytes_in_used_blocks=0; +GC_apply_to_all_blocks(GC_add_block,(word)0); +GC_COND_LOG_PRINTF("GC_bytes_in_used_blocks=%lu," +" bytes_in_free_blocks=%lu,heapsize=%lu\n", +(unsigned long)GC_bytes_in_used_blocks, +(unsigned long)bytes_in_free_blocks, +(unsigned long)GC_heapsize); +if (GC_bytes_in_used_blocks+bytes_in_free_blocks!=GC_heapsize){ +GC_err_printf("LOST SOME BLOCKS!!\n"); +} +} +void GC_check_dirty(void) +{ +int index; +unsigned i; +struct hblk*h; +ptr_t start; +GC_check_blocks(); +GC_n_dirty_errors=0; +GC_n_faulted_dirty_errors=0; +GC_n_clean=0; +GC_n_dirty=0; +index=0; +for (i=0;i < GC_n_heap_sects;i++){ +start=GC_heap_sects[i].hs_start; +for (h=(struct hblk*)start; +(word)h < (word)(start+GC_heap_sects[i].hs_bytes);h++){ +GC_update_check_page(h,index); +index++; +if (index>=NSUMS)goto out; +} +} +out: +GC_COND_LOG_PRINTF("Checked %lu clean and %lu dirty pages\n", +GC_n_clean,GC_n_dirty); +if (GC_n_dirty_errors > 0){ +GC_err_printf("Found %d dirty bit errors (%d were faulted)\n", +GC_n_dirty_errors,GC_n_faulted_dirty_errors); +} +for (i=0;i < GC_n_faulted;++i){ +GC_faulted[i]=0; +} +GC_n_faulted=0; +} +#endif +#ifndef GC_PMARK_H +#define GC_PMARK_H +#if defined(HAVE_CONFIG_H)&&!defined(GC_PRIVATE_H) +#endif +#ifndef GC_BUILD +#define GC_BUILD +#endif +#if (defined(__linux__)||defined(__GLIBC__)||defined(__GNU__))&&!defined(_GNU_SOURCE)&&defined(GC_PTHREADS)&&!defined(GC_NO_PTHREAD_SIGMASK) +#define _GNU_SOURCE 1 +#endif +#if defined(KEEP_BACK_PTRS)||defined(PRINT_BLACK_LIST) +#endif +EXTERN_C_BEGIN +#ifndef MARK_DESCR_OFFSET +#define MARK_DESCR_OFFSET sizeof(word) +#endif +#define BITMAP_BITS (WORDSZ - GC_DS_TAG_BITS) +#define PROC(descr)(GC_mark_procs[((descr)>>GC_DS_TAG_BITS)&(GC_MAX_MARK_PROCS-1)]) +#define ENV(descr)((descr)>>(GC_DS_TAG_BITS+GC_LOG_MAX_MARK_PROCS)) +#define MAX_ENV (((word)1<<(WORDSZ - GC_DS_TAG_BITS - GC_LOG_MAX_MARK_PROCS))- 1) +GC_EXTERN unsigned GC_n_mark_procs; +#define GC_MARK_STACK_DISCARDS (INITIAL_MARK_STACK_SIZE/8) +#ifdef PARALLEL_MARK +#endif +GC_INNER mse*GC_signal_mark_stack_overflow(mse*msp); +GC_INLINE mse*GC_push_obj(ptr_t obj,hdr*hhdr,mse*mark_stack_top, +mse*mark_stack_limit) +{ +word descr=hhdr->hb_descr; +GC_ASSERT(!HBLK_IS_FREE(hhdr)); +if (descr!=0){ +mark_stack_top++; +if ((word)mark_stack_top>=(word)mark_stack_limit){ +mark_stack_top=GC_signal_mark_stack_overflow(mark_stack_top); +} +mark_stack_top->mse_start=obj; +mark_stack_top->mse_descr.w=descr; +} +return mark_stack_top; +} +#define PUSH_CONTENTS(current,mark_stack_top,mark_stack_limit,source)do { hdr*my_hhdr;HC_GET_HDR(current,my_hhdr,source);mark_stack_top=GC_push_contents_hdr(current,mark_stack_top,mark_stack_limit,source,my_hhdr,TRUE);} while (0) +#ifdef USE_MARK_BYTES +#if defined(PARALLEL_MARK)&&defined(AO_HAVE_char_store)&&!defined(BASE_ATOMIC_OPS_EMULATED) +#define SET_MARK_BIT_EXIT_IF_SET(hhdr,bit_no){ volatile unsigned char*mark_byte_addr=(unsigned char*)(hhdr)->hb_marks+(bit_no);if (AO_char_load(mark_byte_addr)!=0)break;AO_char_store(mark_byte_addr,1);} +#else +#define SET_MARK_BIT_EXIT_IF_SET(hhdr,bit_no){ char*mark_byte_addr=(char*)(hhdr)->hb_marks+(bit_no);if (*mark_byte_addr!=0)break;*mark_byte_addr=1;} +#endif +#else +#ifdef PARALLEL_MARK +#ifdef THREAD_SANITIZER +#define OR_WORD_EXIT_IF_SET(addr,bits){ if (!((word)AO_load((volatile AO_t*)(addr))&(bits))){ AO_or((volatile AO_t*)(addr),(AO_t)(bits));} else { break;} } +#else +#define OR_WORD_EXIT_IF_SET(addr,bits){ if (!(*(addr)&(bits))){ AO_or((volatile AO_t*)(addr),(AO_t)(bits));} else { break;} } +#endif +#else +#define OR_WORD_EXIT_IF_SET(addr,bits){ word old=*(addr);word my_bits=(bits);if ((old&my_bits)!=0)break;*(addr)=old|my_bits;} +#endif +#define SET_MARK_BIT_EXIT_IF_SET(hhdr,bit_no){ word*mark_word_addr=(hhdr)->hb_marks+divWORDSZ(bit_no);OR_WORD_EXIT_IF_SET(mark_word_addr,(word)1<hb_n_marks,AO_load(&hhdr->hb_n_marks)+1) +#else +#define INCR_MARKS(hhdr)(void)(++hhdr->hb_n_marks) +#endif +#ifdef ENABLE_TRACE +#define TRACE(source,cmd)if (GC_trace_addr!=0&&(ptr_t)(source)==GC_trace_addr)cmd +#define TRACE_TARGET(target,cmd)if (GC_trace_addr!=0&&(target)==*(ptr_t*)GC_trace_addr)cmd +#else +#define TRACE(source,cmd) +#define TRACE_TARGET(source,cmd) +#endif +#if defined(I386)&&defined(__GNUC__)&&!defined(NACL) +#define LONG_MULT(hprod,lprod,x,y)do { __asm__ __volatile__("mull %2":"=a"(lprod),"=d"(hprod):"g"(y),"0"(x));} while (0) +#else +#if defined(__int64)&&!defined(__GNUC__)&&!defined(CPPCHECK) +#define ULONG_MULT_T unsigned __int64 +#else +#define ULONG_MULT_T unsigned long long +#endif +#define LONG_MULT(hprod,lprod,x,y)do { ULONG_MULT_T prod=(ULONG_MULT_T)(x)*(ULONG_MULT_T)(y);GC_STATIC_ASSERT(sizeof(x)+sizeof(y)<=sizeof(prod));hprod=prod>>32;lprod=(unsigned32)prod;} while (0) +#endif +GC_INLINE mse*GC_push_contents_hdr(ptr_t current,mse*mark_stack_top, +mse*mark_stack_limit,ptr_t source, +hdr*hhdr,GC_bool do_offset_check) +{ +do { +size_t displ=HBLKDISPL(current); +ptr_t base=current; +#ifdef MARK_BIT_PER_GRANULE +size_t gran_displ=BYTES_TO_GRANULES(displ); +size_t gran_offset=hhdr->hb_map[gran_displ]; +size_t byte_offset=displ&(GRANULE_BYTES - 1); +if (EXPECT((gran_offset|byte_offset)!=0,FALSE)) +#else +unsigned32 gran_displ; +unsigned32 inv_sz=hhdr->hb_inv_sz; +#endif +{ +#ifdef MARK_BIT_PER_GRANULE +if ((hhdr->hb_flags&LARGE_BLOCK)!=0) +#else +if (EXPECT(inv_sz==LARGE_INV_SZ,FALSE)) +#endif +{ +size_t obj_displ; +base=(ptr_t)hhdr->hb_block; +obj_displ=current - base; +if (obj_displ!=displ){ +GC_ASSERT(obj_displ < hhdr->hb_sz); +} else if (do_offset_check&&!GC_valid_offsets[obj_displ]){ +GC_ADD_TO_BLACK_LIST_NORMAL(current,source); +break; +} +GC_ASSERT(hhdr->hb_sz > HBLKSIZE +||hhdr->hb_block==HBLKPTR(current)); +GC_ASSERT((word)hhdr->hb_block<=(word)current); +gran_displ=0; +} else { +#ifdef MARK_BIT_PER_GRANULE +size_t obj_displ=GRANULES_TO_BYTES(gran_offset)+byte_offset; +#else +unsigned32 low_prod; +LONG_MULT(gran_displ,low_prod,(unsigned32)displ,inv_sz); +if ((low_prod>>16)!=0) +#endif +{ +#if defined(MARK_BIT_PER_OBJ)&&!defined(MARK_BIT_PER_GRANULE) +size_t obj_displ; +GC_STATIC_ASSERT(HBLKSIZE<=(1<<15)); +obj_displ=(((low_prod>>16)+1)*(size_t)hhdr->hb_sz)>>16; +#endif +if (do_offset_check&&!GC_valid_offsets[obj_displ]){ +GC_ADD_TO_BLACK_LIST_NORMAL(current,source); +break; +} +#ifdef MARK_BIT_PER_GRANULE +gran_displ-=gran_offset; +#endif +base-=obj_displ; +} +} +} +#ifdef MARK_BIT_PER_GRANULE +GC_ASSERT(hhdr==GC_find_header(base)); +GC_ASSERT(gran_displ % BYTES_TO_GRANULES(hhdr->hb_sz)==0); +#else +GC_ASSERT(gran_displ<=HBLK_OBJS(hhdr->hb_sz)); +#endif +TRACE(source,GC_log_printf("GC #%u:passed validity tests\n", +(unsigned)GC_gc_no)); +SET_MARK_BIT_EXIT_IF_SET(hhdr,gran_displ); +TRACE(source,GC_log_printf("GC #%u:previously unmarked\n", +(unsigned)GC_gc_no)); +TRACE_TARGET(base,GC_log_printf("GC #%u:marking %p from %p instead\n", +(unsigned)GC_gc_no,(void*)base, +(void*)source)); +INCR_MARKS(hhdr); +GC_STORE_BACK_PTR(source,base); +mark_stack_top=GC_push_obj(base,hhdr,mark_stack_top, +mark_stack_limit); +} while (0); +return mark_stack_top; +} +#if defined(PRINT_BLACK_LIST)||defined(KEEP_BACK_PTRS) +#define PUSH_ONE_CHECKED_STACK(p,source)GC_mark_and_push_stack((ptr_t)(p),(ptr_t)(source)) +#else +#define PUSH_ONE_CHECKED_STACK(p,source)GC_mark_and_push_stack((ptr_t)(p)) +#endif +#ifdef NEED_FIXUP_POINTER +#define GC_PUSH_ONE_STACK(p,source)do { if ((word)(p)>=(word)GC_least_plausible_heap_addr&&(word)(p)< (word)GC_greatest_plausible_heap_addr){ PUSH_ONE_CHECKED_STACK(p,source);} FIXUP_POINTER(p);if ((word)(p)>=(word)GC_least_plausible_heap_addr&&(word)(p)< (word)GC_greatest_plausible_heap_addr){ PUSH_ONE_CHECKED_STACK(p,source);} } while (0) +#else +#define GC_PUSH_ONE_STACK(p,source)do { if ((word)(p)>=(word)GC_least_plausible_heap_addr&&(word)(p)< (word)GC_greatest_plausible_heap_addr){ PUSH_ONE_CHECKED_STACK(p,source);} } while (0) +#endif +#define GC_PUSH_ONE_HEAP(p,source,mark_stack_top)do { FIXUP_POINTER(p);if ((word)(p)>=(word)GC_least_plausible_heap_addr&&(word)(p)< (word)GC_greatest_plausible_heap_addr)mark_stack_top=GC_mark_and_push((void*)(p),mark_stack_top,GC_mark_stack_limit,(void**)(source));} while (0) +GC_INNER mse*GC_mark_from(mse*top,mse*bottom,mse*limit); +#define MARK_FROM_MARK_STACK()GC_mark_stack_top=GC_mark_from(GC_mark_stack_top,GC_mark_stack,GC_mark_stack+GC_mark_stack_size); +#define GC_mark_stack_empty()((word)GC_mark_stack_top < (word)GC_mark_stack) +#define GC_MARK_FO(real_ptr,mark_proc)do { (*(mark_proc))(real_ptr);while (!GC_mark_stack_empty())MARK_FROM_MARK_STACK();if (GC_mark_state!=MS_NONE){ GC_set_mark_bit(real_ptr);while (!GC_mark_some((ptr_t)0)){ } } } while (0) +#define MS_NONE 0 +#define MS_PUSH_RESCUERS 1 +#define MS_PUSH_UNCOLLECTABLE 2 +#define MS_ROOTS_PUSHED 3 +#define MS_PARTIALLY_INVALID 4 +#define MS_INVALID 5 +EXTERN_C_END +#endif +#ifdef GC_GCJ_SUPPORT +#ifndef GC_GCJ_H +#define GC_GCJ_H +#ifndef GC_H +#endif +#ifdef __cplusplus +extern "C" { +#endif +GC_API void GC_CALL GC_init_gcj_malloc(int, +void*); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL +GC_gcj_malloc(size_t, +void*); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL +GC_debug_gcj_malloc(size_t, +void*, +GC_EXTRA_PARAMS); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL +GC_gcj_malloc_ignore_off_page(size_t, +void*); +GC_API int GC_gcj_kind; +GC_API int GC_gcj_debug_kind; +#ifdef GC_DEBUG +#define GC_GCJ_MALLOC(s,d)GC_debug_gcj_malloc(s,d,GC_EXTRAS) +#define GC_GCJ_MALLOC_IGNORE_OFF_PAGE(s,d)GC_debug_gcj_malloc(s,d,GC_EXTRAS) +#else +#define GC_GCJ_MALLOC(s,d)GC_gcj_malloc(s,d) +#define GC_GCJ_MALLOC_IGNORE_OFF_PAGE(s,d)GC_gcj_malloc_ignore_off_page(s,d) +#endif +#ifdef __cplusplus +} +#endif +#endif +int GC_gcj_kind=0; +int GC_gcj_debug_kind=0; +STATIC struct GC_ms_entry*GC_gcj_fake_mark_proc(word*addr GC_ATTR_UNUSED, +struct GC_ms_entry*mark_stack_ptr, +struct GC_ms_entry*mark_stack_limit GC_ATTR_UNUSED, +word env GC_ATTR_UNUSED) +{ +ABORT_RET("No client gcj mark proc is specified"); +return mark_stack_ptr; +} +GC_API void GC_CALL GC_init_gcj_malloc(int mp_index, +void*mp) +{ +#ifndef GC_IGNORE_GCJ_INFO +GC_bool ignore_gcj_info; +#endif +DCL_LOCK_STATE; +if (mp==0) +mp=(void*)(word)GC_gcj_fake_mark_proc; +GC_init(); +LOCK(); +if (GC_gcjobjfreelist!=NULL){ +UNLOCK(); +return; +} +#ifdef GC_IGNORE_GCJ_INFO +#define ignore_gcj_info TRUE +#else +ignore_gcj_info=(0!=GETENV("GC_IGNORE_GCJ_INFO")); +#endif +if (ignore_gcj_info){ +GC_COND_LOG_PRINTF("Gcj-style type information is disabled!\n"); +} +GC_ASSERT(GC_mark_procs[mp_index]==(GC_mark_proc)0); +GC_mark_procs[mp_index]=(GC_mark_proc)(word)mp; +if ((unsigned)mp_index>=GC_n_mark_procs) +ABORT("GC_init_gcj_malloc:bad index"); +GC_gcjobjfreelist=(ptr_t*)GC_new_free_list_inner(); +if (ignore_gcj_info){ +GC_gcj_kind=GC_new_kind_inner((void**)GC_gcjobjfreelist, +GC_DS_LENGTH, +TRUE,TRUE); +GC_gcj_debug_kind=GC_gcj_kind; +} else { +GC_gcj_kind=GC_new_kind_inner( +(void**)GC_gcjobjfreelist, +(((word)(-(signed_word)MARK_DESCR_OFFSET +- GC_INDIR_PER_OBJ_BIAS)) +|GC_DS_PER_OBJECT), +FALSE,TRUE); +GC_gcj_debug_kind=GC_new_kind_inner(GC_new_free_list_inner(), +GC_MAKE_PROC(mp_index, +1), +FALSE,TRUE); +} +UNLOCK(); +#undef ignore_gcj_info +} +#define GENERAL_MALLOC_INNER(lb,k)GC_clear_stack(GC_generic_malloc_inner(lb,k)) +#define GENERAL_MALLOC_INNER_IOP(lb,k)GC_clear_stack(GC_generic_malloc_inner_ignore_off_page(lb,k)) +static void maybe_finalize(void) +{ +static word last_finalized_no=0; +DCL_LOCK_STATE; +if (GC_gc_no==last_finalized_no|| +!EXPECT(GC_is_initialized,TRUE))return; +UNLOCK(); +GC_INVOKE_FINALIZERS(); +LOCK(); +last_finalized_no=GC_gc_no; +} +#ifdef THREAD_LOCAL_ALLOC +GC_INNER void*GC_core_gcj_malloc(size_t lb, +void*ptr_to_struct_containing_descr) +#else +GC_API GC_ATTR_MALLOC void*GC_CALL GC_gcj_malloc(size_t lb, +void*ptr_to_struct_containing_descr) +#endif +{ +ptr_t op; +DCL_LOCK_STATE; +GC_DBG_COLLECT_AT_MALLOC(lb); +if(SMALL_OBJ(lb)){ +word lg; +LOCK(); +lg=GC_size_map[lb]; +op=GC_gcjobjfreelist[lg]; +if(EXPECT(0==op,FALSE)){ +maybe_finalize(); +op=(ptr_t)GENERAL_MALLOC_INNER((word)lb,GC_gcj_kind); +if (0==op){ +GC_oom_func oom_fn=GC_oom_fn; +UNLOCK(); +return((*oom_fn)(lb)); +} +} else { +GC_gcjobjfreelist[lg]=(ptr_t)obj_link(op); +GC_bytes_allocd+=GRANULES_TO_BYTES((word)lg); +} +GC_ASSERT(((void**)op)[1]==0); +} else { +LOCK(); +maybe_finalize(); +op=(ptr_t)GENERAL_MALLOC_INNER((word)lb,GC_gcj_kind); +if (0==op){ +GC_oom_func oom_fn=GC_oom_fn; +UNLOCK(); +return((*oom_fn)(lb)); +} +} +*(void**)op=ptr_to_struct_containing_descr; +UNLOCK(); +GC_dirty(op); +REACHABLE_AFTER_DIRTY(ptr_to_struct_containing_descr); +return (void*)op; +} +GC_API GC_ATTR_MALLOC void*GC_CALL GC_debug_gcj_malloc(size_t lb, +void*ptr_to_struct_containing_descr,GC_EXTRA_PARAMS) +{ +void*result; +DCL_LOCK_STATE; +LOCK(); +maybe_finalize(); +result=GC_generic_malloc_inner(SIZET_SAT_ADD(lb,DEBUG_BYTES), +GC_gcj_debug_kind); +if (result==0){ +GC_oom_func oom_fn=GC_oom_fn; +UNLOCK(); +GC_err_printf("GC_debug_gcj_malloc(%lu,%p)returning NULL (%s:%d)\n", +(unsigned long)lb,ptr_to_struct_containing_descr,s,i); +return((*oom_fn)(lb)); +} +*((void**)((ptr_t)result+sizeof(oh)))=ptr_to_struct_containing_descr; +if (!GC_debugging_started){ +GC_start_debugging_inner(); +} +ADD_CALL_CHAIN(result,ra); +result=GC_store_debug_info_inner(result,(word)lb,s,i); +UNLOCK(); +GC_dirty(result); +REACHABLE_AFTER_DIRTY(ptr_to_struct_containing_descr); +return result; +} +GC_API GC_ATTR_MALLOC void*GC_CALL GC_gcj_malloc_ignore_off_page(size_t lb, +void*ptr_to_struct_containing_descr) +{ +ptr_t op; +DCL_LOCK_STATE; +GC_DBG_COLLECT_AT_MALLOC(lb); +if(SMALL_OBJ(lb)){ +word lg; +LOCK(); +lg=GC_size_map[lb]; +op=GC_gcjobjfreelist[lg]; +if (EXPECT(0==op,FALSE)){ +maybe_finalize(); +op=(ptr_t)GENERAL_MALLOC_INNER_IOP(lb,GC_gcj_kind); +if (0==op){ +GC_oom_func oom_fn=GC_oom_fn; +UNLOCK(); +return((*oom_fn)(lb)); +} +} else { +GC_gcjobjfreelist[lg]=(ptr_t)obj_link(op); +GC_bytes_allocd+=GRANULES_TO_BYTES((word)lg); +} +} else { +LOCK(); +maybe_finalize(); +op=(ptr_t)GENERAL_MALLOC_INNER_IOP(lb,GC_gcj_kind); +if (0==op){ +GC_oom_func oom_fn=GC_oom_fn; +UNLOCK(); +return((*oom_fn)(lb)); +} +} +*(void**)op=ptr_to_struct_containing_descr; +UNLOCK(); +GC_dirty(op); +REACHABLE_AFTER_DIRTY(ptr_to_struct_containing_descr); +return (void*)op; +} +#endif +GC_INNER hdr*GC_find_header(ptr_t h) +{ +#ifdef HASH_TL +hdr*result; +GET_HDR(h,result); +return(result); +#else +return(HDR_INNER(h)); +#endif +} +GC_INNER hdr* +#ifdef PRINT_BLACK_LIST +GC_header_cache_miss(ptr_t p,hdr_cache_entry*hce,ptr_t source) +#else +GC_header_cache_miss(ptr_t p,hdr_cache_entry*hce) +#endif +{ +hdr*hhdr; +HC_MISS(); +GET_HDR(p,hhdr); +if (IS_FORWARDING_ADDR_OR_NIL(hhdr)){ +if (GC_all_interior_pointers){ +if (hhdr!=0){ +ptr_t current=p; +current=(ptr_t)HBLKPTR(current); +do { +current=current - HBLKSIZE*(word)hhdr; +hhdr=HDR(current); +} while(IS_FORWARDING_ADDR_OR_NIL(hhdr)); +if (hhdr->hb_flags&IGNORE_OFF_PAGE) +return 0; +if (HBLK_IS_FREE(hhdr) +||p - current>=(ptrdiff_t)(hhdr->hb_sz)){ +GC_ADD_TO_BLACK_LIST_NORMAL(p,source); +return 0; +} +} else { +GC_ADD_TO_BLACK_LIST_NORMAL(p,source); +} +GC_ASSERT(hhdr==0||!HBLK_IS_FREE(hhdr)); +return hhdr; +} else { +if (hhdr==0){ +GC_ADD_TO_BLACK_LIST_NORMAL(p,source); +} +return 0; +} +} else { +if (HBLK_IS_FREE(hhdr)){ +GC_ADD_TO_BLACK_LIST_NORMAL(p,source); +return 0; +} else { +hce->block_addr=(word)(p)>>LOG_HBLKSIZE; +hce->hce_hdr=hhdr; +return hhdr; +} +} +} +GC_INNER ptr_t GC_scratch_alloc(size_t bytes) +{ +ptr_t result=GC_scratch_free_ptr; +size_t bytes_to_get; +bytes=ROUNDUP_GRANULE_SIZE(bytes); +for (;;){ +GC_scratch_free_ptr+=bytes; +if ((word)GC_scratch_free_ptr<=(word)GC_scratch_end_ptr){ +return result; +} +GC_ASSERT(GC_page_size!=0); +if (bytes>=MINHINCR*HBLKSIZE){ +bytes_to_get=ROUNDUP_PAGESIZE_IF_MMAP(bytes); +result=(ptr_t)GET_MEM(bytes_to_get); +GC_add_to_our_memory(result,bytes_to_get); +GC_scratch_free_ptr-=bytes; +if (result!=NULL){ +GC_scratch_last_end_ptr=result+bytes; +} +return result; +} +bytes_to_get=ROUNDUP_PAGESIZE_IF_MMAP(MINHINCR*HBLKSIZE); +result=(ptr_t)GET_MEM(bytes_to_get); +GC_add_to_our_memory(result,bytes_to_get); +if (NULL==result){ +WARN("Out of memory - trying to allocate requested amount" +" (%" WARN_PRIdPTR " bytes)...\n",(word)bytes); +GC_scratch_free_ptr-=bytes; +bytes_to_get=ROUNDUP_PAGESIZE_IF_MMAP(bytes); +result=(ptr_t)GET_MEM(bytes_to_get); +GC_add_to_our_memory(result,bytes_to_get); +return result; +} +GC_scratch_free_ptr=result; +GC_scratch_end_ptr=GC_scratch_free_ptr+bytes_to_get; +GC_scratch_last_end_ptr=GC_scratch_end_ptr; +} +} +static hdr*alloc_hdr(void) +{ +hdr*result; +if (NULL==GC_hdr_free_list){ +result=(hdr*)GC_scratch_alloc(sizeof(hdr)); +} else { +result=GC_hdr_free_list; +GC_hdr_free_list=(hdr*)result->hb_next; +} +return(result); +} +GC_INLINE void free_hdr(hdr*hhdr) +{ +hhdr->hb_next=(struct hblk*)GC_hdr_free_list; +GC_hdr_free_list=hhdr; +} +#ifdef COUNT_HDR_CACHE_HITS +word GC_hdr_cache_hits=0; +word GC_hdr_cache_misses=0; +#endif +GC_INNER void GC_init_headers(void) +{ +unsigned i; +GC_ASSERT(NULL==GC_all_nils); +GC_all_nils=(bottom_index*)GC_scratch_alloc(sizeof(bottom_index)); +if (GC_all_nils==NULL){ +GC_err_printf("Insufficient memory for GC_all_nils\n"); +EXIT(); +} +BZERO(GC_all_nils,sizeof(bottom_index)); +for (i=0;i < TOP_SZ;i++){ +GC_top_index[i]=GC_all_nils; +} +} +static GC_bool get_index(word addr) +{ +word hi=(word)(addr)>>(LOG_BOTTOM_SZ+LOG_HBLKSIZE); +bottom_index*r; +bottom_index*p; +bottom_index**prev; +bottom_index*pi; +word i; +GC_ASSERT(I_HOLD_LOCK()); +#ifdef HASH_TL +i=TL_HASH(hi); +pi=p=GC_top_index[i]; +while(p!=GC_all_nils){ +if (p->key==hi)return(TRUE); +p=p->hash_link; +} +#else +if (GC_top_index[hi]!=GC_all_nils) +return TRUE; +i=hi; +#endif +r=(bottom_index*)GC_scratch_alloc(sizeof(bottom_index)); +if (EXPECT(NULL==r,FALSE)) +return FALSE; +BZERO(r,sizeof(bottom_index)); +r->key=hi; +#ifdef HASH_TL +r->hash_link=pi; +#endif +prev=&GC_all_bottom_indices; +pi=0; +while ((p=*prev)!=0&&p->key < hi){ +pi=p; +prev=&(p->asc_link); +} +r->desc_link=pi; +if (0==p){ +GC_all_bottom_indices_end=r; +} else { +p->desc_link=r; +} +r->asc_link=p; +*prev=r; +GC_top_index[i]=r; +return(TRUE); +} +GC_INNER struct hblkhdr*GC_install_header(struct hblk*h) +{ +hdr*result; +if (!get_index((word)h))return(0); +result=alloc_hdr(); +if (result){ +SET_HDR(h,result); +#ifdef USE_MUNMAP +result->hb_last_reclaimed=(unsigned short)GC_gc_no; +#endif +} +return(result); +} +GC_INNER GC_bool GC_install_counts(struct hblk*h,size_t sz) +{ +struct hblk*hbp; +for (hbp=h;(word)hbp < (word)h+sz;hbp+=BOTTOM_SZ){ +if (!get_index((word)hbp)) +return FALSE; +if ((word)hbp > GC_WORD_MAX - (word)BOTTOM_SZ*HBLKSIZE) +break; +} +if (!get_index((word)h+sz - 1)) +return FALSE; +for (hbp=h+1;(word)hbp < (word)h+sz;hbp+=1){ +word i=HBLK_PTR_DIFF(hbp,h); +SET_HDR(hbp,(hdr*)(i > MAX_JUMP?MAX_JUMP:i)); +} +return TRUE; +} +GC_INNER void GC_remove_header(struct hblk*h) +{ +hdr**ha; +GET_HDR_ADDR(h,ha); +free_hdr(*ha); +*ha=0; +} +GC_INNER void GC_remove_counts(struct hblk*h,size_t sz) +{ +struct hblk*hbp; +for (hbp=h+1;(word)hbp < (word)h+sz;hbp+=1){ +SET_HDR(hbp,0); +} +} +void GC_apply_to_all_blocks(void (*fn)(struct hblk*h,word client_data), +word client_data) +{ +signed_word j; +bottom_index*index_p; +for (index_p=GC_all_bottom_indices;index_p!=0; +index_p=index_p->asc_link){ +for (j=BOTTOM_SZ-1;j>=0;){ +if (!IS_FORWARDING_ADDR_OR_NIL(index_p->index[j])){ +if (!HBLK_IS_FREE(index_p->index[j])){ +(*fn)(((struct hblk*) +(((index_p->key<index[j]==0){ +j--; +} else { +j-=(signed_word)(index_p->index[j]); +} +} +} +} +GC_INNER struct hblk*GC_next_block(struct hblk*h,GC_bool allow_free) +{ +REGISTER bottom_index*bi; +REGISTER word j=((word)h>>LOG_HBLKSIZE)&(BOTTOM_SZ-1); +GC_ASSERT(I_HOLD_LOCK()); +GET_BI(h,bi); +if (bi==GC_all_nils){ +REGISTER word hi=(word)h>>(LOG_BOTTOM_SZ+LOG_HBLKSIZE); +bi=GC_all_bottom_indices; +while (bi!=0&&bi->key < hi)bi=bi->asc_link; +j=0; +} +while (bi!=0){ +while (j < BOTTOM_SZ){ +hdr*hhdr=bi->index[j]; +if (IS_FORWARDING_ADDR_OR_NIL(hhdr)){ +j++; +} else { +if (allow_free||!HBLK_IS_FREE(hhdr)){ +return ((struct hblk*) +(((bi->key<hb_sz); +} +} +} +j=0; +bi=bi->asc_link; +} +return(0); +} +GC_INNER struct hblk*GC_prev_block(struct hblk*h) +{ +bottom_index*bi; +signed_word j=((word)h>>LOG_HBLKSIZE)&(BOTTOM_SZ-1); +GC_ASSERT(I_HOLD_LOCK()); +GET_BI(h,bi); +if (bi==GC_all_nils){ +word hi=(word)h>>(LOG_BOTTOM_SZ+LOG_HBLKSIZE); +bi=GC_all_bottom_indices_end; +while (bi!=0&&bi->key > hi)bi=bi->desc_link; +j=BOTTOM_SZ - 1; +} +while(bi!=0){ +while (j>=0){ +hdr*hhdr=bi->index[j]; +if (0==hhdr){ +--j; +} else if (IS_FORWARDING_ADDR_OR_NIL(hhdr)){ +j-=(signed_word)hhdr; +} else { +return((struct hblk*) +(((bi->key<desc_link; +} +return(0); +} +#include +#ifndef SMALL_CONFIG +STATIC ptr_t GC_build_fl_clear2(struct hblk*h,ptr_t ofl) +{ +word*p=(word*)(h->hb_body); +word*lim=(word*)(h+1); +p[0]=(word)ofl; +p[1]=0; +p[2]=(word)p; +p[3]=0; +p+=4; +for (;(word)p < (word)lim;p+=4){ +p[0]=(word)(p-2); +p[1]=0; +p[2]=(word)p; +p[3]=0; +}; +return((ptr_t)(p-2)); +} +STATIC ptr_t GC_build_fl_clear4(struct hblk*h,ptr_t ofl) +{ +word*p=(word*)(h->hb_body); +word*lim=(word*)(h+1); +p[0]=(word)ofl; +p[1]=0; +p[2]=0; +p[3]=0; +p+=4; +for (;(word)p < (word)lim;p+=4){ +GC_PREFETCH_FOR_WRITE((ptr_t)(p+64)); +p[0]=(word)(p-4); +p[1]=0; +CLEAR_DOUBLE(p+2); +}; +return((ptr_t)(p-4)); +} +STATIC ptr_t GC_build_fl2(struct hblk*h,ptr_t ofl) +{ +word*p=(word*)(h->hb_body); +word*lim=(word*)(h+1); +p[0]=(word)ofl; +p[2]=(word)p; +p+=4; +for (;(word)p < (word)lim;p+=4){ +p[0]=(word)(p-2); +p[2]=(word)p; +}; +return((ptr_t)(p-2)); +} +STATIC ptr_t GC_build_fl4(struct hblk*h,ptr_t ofl) +{ +word*p=(word*)(h->hb_body); +word*lim=(word*)(h+1); +p[0]=(word)ofl; +p[4]=(word)p; +p+=8; +for (;(word)p < (word)lim;p+=8){ +GC_PREFETCH_FOR_WRITE((ptr_t)(p+64)); +p[0]=(word)(p-4); +p[4]=(word)p; +}; +return((ptr_t)(p-4)); +} +#endif +GC_INNER ptr_t GC_build_fl(struct hblk*h,size_t sz,GC_bool clear, +ptr_t list) +{ +word*p,*prev; +word*last_object; +GC_PREFETCH_FOR_WRITE((ptr_t)h); +GC_PREFETCH_FOR_WRITE((ptr_t)h+128); +GC_PREFETCH_FOR_WRITE((ptr_t)h+256); +GC_PREFETCH_FOR_WRITE((ptr_t)h+378); +#ifndef SMALL_CONFIG +switch (sz){ +case 2:if (clear){ +return GC_build_fl_clear2(h,list); +} else { +return GC_build_fl2(h,list); +} +case 4:if (clear){ +return GC_build_fl_clear4(h,list); +} else { +return GC_build_fl4(h,list); +} +default: +break; +} +#endif +if (clear)BZERO(h,HBLKSIZE); +p=(word*)(h->hb_body)+sz; +prev=(word*)(h->hb_body); +last_object=(word*)((char*)h+HBLKSIZE); +last_object-=sz; +while ((word)p<=(word)last_object){ +obj_link(p)=(ptr_t)prev; +prev=p; +p+=sz; +} +p-=sz; +*(ptr_t*)h=list; +return ((ptr_t)p); +} +GC_INNER void GC_new_hblk(size_t gran,int kind) +{ +struct hblk*h; +GC_bool clear=GC_obj_kinds[kind].ok_init; +GC_STATIC_ASSERT((sizeof (struct hblk))==HBLKSIZE); +if (GC_debugging_started)clear=TRUE; +h=GC_allochblk(GRANULES_TO_BYTES(gran),kind,0); +if (h==0)return; +if (IS_UNCOLLECTABLE(kind))GC_set_hdr_marks(HDR(h)); +GC_obj_kinds[kind].ok_freelist[gran]= +GC_build_fl(h,GRANULES_TO_WORDS(gran),clear, +(ptr_t)GC_obj_kinds[kind].ok_freelist[gran]); +} +GC_API void GC_CALL GC_register_displacement(size_t offset) +{ +DCL_LOCK_STATE; +LOCK(); +GC_register_displacement_inner(offset); +UNLOCK(); +} +GC_INNER void GC_register_displacement_inner(size_t offset) +{ +GC_ASSERT(I_HOLD_LOCK()); +if (offset>=VALID_OFFSET_SZ){ +ABORT("Bad argument to GC_register_displacement"); +} +if (!GC_valid_offsets[offset]){ +GC_valid_offsets[offset]=TRUE; +GC_modws_valid_offsets[offset % sizeof(word)]=TRUE; +} +} +#ifdef MARK_BIT_PER_GRANULE +GC_INNER GC_bool GC_add_map_entry(size_t granules) +{ +unsigned displ; +unsigned short*new_map; +if (granules > BYTES_TO_GRANULES(MAXOBJBYTES))granules=0; +if (GC_obj_map[granules]!=0){ +return(TRUE); +} +new_map=(unsigned short*)GC_scratch_alloc(MAP_LEN*sizeof(short)); +if (new_map==0)return(FALSE); +GC_COND_LOG_PRINTF( +"Adding block map for size of %u granules (%u bytes)\n", +(unsigned)granules,(unsigned)GRANULES_TO_BYTES(granules)); +if (granules==0){ +for (displ=0;displ < BYTES_TO_GRANULES(HBLKSIZE);displ++){ +new_map[displ]=1; +} +} else { +for (displ=0;displ < BYTES_TO_GRANULES(HBLKSIZE);displ++){ +new_map[displ]=(unsigned short)(displ % granules); +} +} +GC_obj_map[granules]=new_map; +return(TRUE); +} +#endif +GC_INNER void GC_initialize_offsets(void) +{ +unsigned i; +if (GC_all_interior_pointers){ +for (i=0;i < VALID_OFFSET_SZ;++i) +GC_valid_offsets[i]=TRUE; +} else { +BZERO(GC_valid_offsets,sizeof(GC_valid_offsets)); +for (i=0;i < sizeof(word);++i) +GC_modws_valid_offsets[i]=FALSE; +} +} +STATIC void GC_CALLBACK GC_default_same_obj_print_proc(void*p,void*q) +{ +ABORT_ARG2("GC_same_obj test failed", +":%p and %p are not in the same object",p,q); +} +void (GC_CALLBACK*GC_same_obj_print_proc)(void*,void*) +=GC_default_same_obj_print_proc; +GC_API void*GC_CALL GC_same_obj(void*p,void*q) +{ +struct hblk*h; +hdr*hhdr; +ptr_t base,limit; +word sz; +if (!EXPECT(GC_is_initialized,TRUE))GC_init(); +hhdr=HDR((word)p); +if (hhdr==0){ +if (divHBLKSZ((word)p)!=divHBLKSZ((word)q) +&&HDR((word)q)!=0){ +goto fail; +} +return(p); +} +if (IS_FORWARDING_ADDR_OR_NIL(hhdr)){ +h=HBLKPTR(p)- (word)hhdr; +hhdr=HDR(h); +while (IS_FORWARDING_ADDR_OR_NIL(hhdr)){ +h=FORWARDED_ADDR(h,hhdr); +hhdr=HDR(h); +} +limit=(ptr_t)h+hhdr->hb_sz; +if ((word)p>=(word)limit||(word)q>=(word)limit +||(word)q < (word)h){ +goto fail; +} +return(p); +} +sz=hhdr->hb_sz; +if (sz > MAXOBJBYTES){ +base=(ptr_t)HBLKPTR(p); +limit=base+sz; +if ((word)p>=(word)limit){ +goto fail; +} +} else { +size_t offset; +size_t pdispl=HBLKDISPL(p); +offset=pdispl % sz; +if (HBLKPTR(p)!=HBLKPTR(q))goto fail; +base=(ptr_t)p - offset; +limit=base+sz; +} +if ((word)q>=(word)limit||(word)q < (word)base){ +goto fail; +} +return(p); +fail: +(*GC_same_obj_print_proc)((ptr_t)p,(ptr_t)q); +return(p); +} +STATIC void GC_CALLBACK GC_default_is_valid_displacement_print_proc (void*p) +{ +ABORT_ARG1("GC_is_valid_displacement test failed",":%p not valid",p); +} +void (GC_CALLBACK*GC_is_valid_displacement_print_proc)(void*)= +GC_default_is_valid_displacement_print_proc; +GC_API void*GC_CALL GC_is_valid_displacement(void*p) +{ +hdr*hhdr; +word pdispl; +word offset; +struct hblk*h; +word sz; +if (!EXPECT(GC_is_initialized,TRUE))GC_init(); +hhdr=HDR((word)p); +if (hhdr==0)return(p); +h=HBLKPTR(p); +if (GC_all_interior_pointers){ +while (IS_FORWARDING_ADDR_OR_NIL(hhdr)){ +h=FORWARDED_ADDR(h,hhdr); +hhdr=HDR(h); +} +} else if (IS_FORWARDING_ADDR_OR_NIL(hhdr)){ +goto fail; +} +sz=hhdr->hb_sz; +pdispl=HBLKDISPL(p); +offset=pdispl % sz; +if ((sz > MAXOBJBYTES&&(word)p>=(word)h+sz) +||!GC_valid_offsets[offset] +||((word)p+(sz - offset)> (word)(h+1) +&&!IS_FORWARDING_ADDR_OR_NIL(HDR(h+1)))){ +goto fail; +} +return(p); +fail: +(*GC_is_valid_displacement_print_proc)((ptr_t)p); +return(p); +} +STATIC void GC_CALLBACK GC_default_is_visible_print_proc(void*p) +{ +ABORT_ARG1("GC_is_visible test failed",":%p not GC-visible",p); +} +void (GC_CALLBACK*GC_is_visible_print_proc)(void*p)= +GC_default_is_visible_print_proc; +#ifndef THREADS +STATIC GC_bool GC_on_stack(void*p) +{ +#ifdef STACK_GROWS_DOWN +if ((word)p>=(word)GC_approx_sp() +&&(word)p < (word)GC_stackbottom){ +return(TRUE); +} +#else +if ((word)p<=(word)GC_approx_sp() +&&(word)p > (word)GC_stackbottom){ +return(TRUE); +} +#endif +return(FALSE); +} +#endif +GC_API void*GC_CALL GC_is_visible(void*p) +{ +hdr*hhdr; +if ((word)p&(ALIGNMENT - 1))goto fail; +if (!EXPECT(GC_is_initialized,TRUE))GC_init(); +#ifdef THREADS +hhdr=HDR((word)p); +if (hhdr!=0&&GC_base(p)==0){ +goto fail; +} else { +return(p); +} +#else +if (GC_on_stack(p))return(p); +hhdr=HDR((word)p); +if (hhdr==0){ +if (GC_is_static_root(p))return(p); +#if defined(DYNAMIC_LOADING)||defined(MSWIN32)||defined(MSWINCE)||defined(CYGWIN32)||defined(PCR) +GC_register_dynamic_libraries(); +if (GC_is_static_root(p)) +return(p); +#endif +goto fail; +} else { +word descr; +ptr_t base=(ptr_t)GC_base(p); +if (NULL==base)goto fail; +if (HBLKPTR(base)!=HBLKPTR(p)) +hhdr=HDR(base); +descr=hhdr->hb_descr; +retry: +switch(descr&GC_DS_TAGS){ +case GC_DS_LENGTH: +if ((word)p - (word)base > descr)goto fail; +break; +case GC_DS_BITMAP: +if ((word)p - (word)base>=WORDS_TO_BYTES(BITMAP_BITS) +||((word)p&(sizeof(word)- 1)))goto fail; +if (!(((word)1<<(WORDSZ - ((ptr_t)p - (ptr_t)base)- 1)) +&descr))goto fail; +break; +case GC_DS_PROC: +break; +case GC_DS_PER_OBJECT: +if ((signed_word)descr>=0){ +descr=*(word*)((ptr_t)base+(descr&~GC_DS_TAGS)); +} else { +ptr_t type_descr=*(ptr_t*)base; +descr=*(word*)(type_descr +- (descr - (word)(GC_DS_PER_OBJECT +- GC_INDIR_PER_OBJ_BIAS))); +} +goto retry; +} +return(p); +} +#endif +fail: +(*GC_is_visible_print_proc)((ptr_t)p); +return(p); +} +GC_API void*GC_CALL GC_pre_incr (void**p,ptrdiff_t how_much) +{ +void*initial=*p; +void*result=GC_same_obj((void*)((ptr_t)initial+how_much),initial); +if (!GC_all_interior_pointers){ +(void)GC_is_valid_displacement(result); +} +return (*p=result); +} +GC_API void*GC_CALL GC_post_incr (void**p,ptrdiff_t how_much) +{ +void*initial=*p; +void*result=GC_same_obj((void*)((ptr_t)initial+how_much),initial); +if (!GC_all_interior_pointers){ +(void)GC_is_valid_displacement(result); +} +*p=result; +return(initial); +} +#ifndef GC_INLINE_H +#define GC_INLINE_H +#if GC_GNUC_PREREQ(3,0) +#define GC_EXPECT(expr,outcome)__builtin_expect(expr,outcome) +#else +#define GC_EXPECT(expr,outcome)(expr) +#endif +#ifndef GC_ASSERT +#ifdef NDEBUG +#define GC_ASSERT(expr) +#else +#include +#define GC_ASSERT(expr)assert(expr) +#endif +#endif +#ifdef __cplusplus +extern "C" { +#endif +#ifndef GC_PREFETCH_FOR_WRITE +#if GC_GNUC_PREREQ(3,0)&&!defined(GC_NO_PREFETCH_FOR_WRITE) +#define GC_PREFETCH_FOR_WRITE(x)__builtin_prefetch((x),1) +#else +#define GC_PREFETCH_FOR_WRITE(x)(void)0 +#endif +#endif +#define GC_I_PTRFREE 0 +#define GC_I_NORMAL 1 +GC_API void GC_CALL GC_generic_malloc_many(size_t,int, +void**); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL +GC_malloc_kind(size_t,int); +#ifdef GC_THREADS +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL +GC_malloc_kind_global(size_t,int); +#else +#define GC_malloc_kind_global GC_malloc_kind +#endif +#if defined(GC_THREADS)&&defined(AO_HAVE_store) +#define GC_FAST_M_AO_STORE(my_fl,next)AO_store((volatile AO_t*)(my_fl),(AO_t)(next)) +#else +#define GC_FAST_M_AO_STORE(my_fl,next)(void)(*(my_fl)=(next)) +#endif +#define GC_FAST_MALLOC_GRANS(result,granules,tiny_fl,num_direct,kind,default_expr,init)do { if (GC_EXPECT((granules)>=GC_TINY_FREELISTS,0)){ result=(default_expr);} else { void**my_fl=(tiny_fl)+(granules);void*my_entry=*my_fl;void*next;for (;;){ if (GC_EXPECT((GC_word)my_entry > (num_direct)+GC_TINY_FREELISTS+1,1)){ next=*(void**)(my_entry);result=(void*)my_entry;GC_FAST_M_AO_STORE(my_fl,next);init;GC_PREFETCH_FOR_WRITE(next);if ((kind)!=GC_I_PTRFREE){ GC_end_stubborn_change(my_fl);GC_reachable_here(next);} GC_ASSERT(GC_size(result)>=(granules)*GC_GRANULE_BYTES);GC_ASSERT((kind)==GC_I_PTRFREE||((GC_word*)result)[1]==0);break;} if ((GC_signed_word)my_entry - (GC_signed_word)(num_direct)<=0&&my_entry!=0){ GC_FAST_M_AO_STORE(my_fl,(char*)my_entry+(granules)+1);result=(default_expr);break;} else { GC_generic_malloc_many(((granules)==0?GC_GRANULE_BYTES:GC_RAW_BYTES_FROM_INDEX(granules)),kind,my_fl);my_entry=*my_fl;if (my_entry==0){ result=(*GC_get_oom_fn())((granules)*GC_GRANULE_BYTES);break;} } } } } while (0) +#define GC_WORDS_TO_WHOLE_GRANULES(n)GC_WORDS_TO_GRANULES((n)+GC_GRANULE_WORDS - 1) +#define GC_MALLOC_WORDS_KIND(result,n,tiny_fl,kind,init)do { size_t granules=GC_WORDS_TO_WHOLE_GRANULES(n);GC_FAST_MALLOC_GRANS(result,granules,tiny_fl,0,kind,GC_malloc_kind(granules*GC_GRANULE_BYTES,kind),init);} while (0) +#define GC_MALLOC_WORDS(result,n,tiny_fl)GC_MALLOC_WORDS_KIND(result,n,tiny_fl,GC_I_NORMAL,*(void**)(result)=0) +#define GC_MALLOC_ATOMIC_WORDS(result,n,tiny_fl)GC_MALLOC_WORDS_KIND(result,n,tiny_fl,GC_I_PTRFREE,(void)0) +#define GC_CONS(result,first,second,tiny_fl)do { void*l=(void*)(first);void*r=(void*)(second);GC_MALLOC_WORDS_KIND(result,2,tiny_fl,GC_I_NORMAL,(void)0);if ((result)!=0){*(void**)(result)=l;GC_PTR_STORE_AND_DIRTY((void**)(result)+1,r);GC_reachable_here(l);} } while (0) +GC_API void GC_CALL GC_print_free_list(int, +size_t); +#ifdef __cplusplus +} +#endif +#endif +#include +#ifdef GC_USE_ENTIRE_HEAP +int GC_use_entire_heap=TRUE; +#else +int GC_use_entire_heap=FALSE; +#endif +#define MAX_BLACK_LIST_ALLOC (2*HBLKSIZE) +#define UNIQUE_THRESHOLD 32 +#define HUGE_THRESHOLD 256 +#define FL_COMPRESSION 8 +#define N_HBLK_FLS ((HUGE_THRESHOLD - UNIQUE_THRESHOLD)/FL_COMPRESSION+UNIQUE_THRESHOLD) +#ifndef GC_GCJ_SUPPORT +STATIC +#endif +struct hblk*GC_hblkfreelist[N_HBLK_FLS+1]={ 0 }; +#ifndef GC_GCJ_SUPPORT +STATIC +#endif +word GC_free_bytes[N_HBLK_FLS+1]={ 0 }; +GC_INLINE int GC_enough_large_bytes_left(void) +{ +int n; +word bytes=GC_large_allocd_bytes; +GC_ASSERT(GC_max_large_allocd_bytes<=GC_heapsize); +for (n=N_HBLK_FLS;n>=0;--n){ +bytes+=GC_free_bytes[n]; +if (bytes>=GC_max_large_allocd_bytes)return n; +} +return 0; +} +STATIC int GC_hblk_fl_from_blocks(word blocks_needed) +{ +if (blocks_needed<=UNIQUE_THRESHOLD)return (int)blocks_needed; +if (blocks_needed>=HUGE_THRESHOLD)return N_HBLK_FLS; +return (int)(blocks_needed - UNIQUE_THRESHOLD)/FL_COMPRESSION ++UNIQUE_THRESHOLD; +} +#define PHDR(hhdr)HDR((hhdr)->hb_prev) +#define NHDR(hhdr)HDR((hhdr)->hb_next) +#ifdef USE_MUNMAP +#define IS_MAPPED(hhdr)(((hhdr)->hb_flags&WAS_UNMAPPED)==0) +#else +#define IS_MAPPED(hhdr)TRUE +#endif +#if!defined(NO_DEBUGGING)||defined(GC_ASSERTIONS) +GC_INNER word GC_compute_large_free_bytes(void) +{ +word total_free=0; +unsigned i; +for (i=0;i<=N_HBLK_FLS;++i){ +struct hblk*h; +hdr*hhdr; +for (h=GC_hblkfreelist[i];h!=0;h=hhdr->hb_next){ +hhdr=HDR(h); +total_free+=hhdr->hb_sz; +} +} +return total_free; +} +#endif +#if!defined(NO_DEBUGGING) +void GC_print_hblkfreelist(void) +{ +unsigned i; +word total; +for (i=0;i<=N_HBLK_FLS;++i){ +struct hblk*h=GC_hblkfreelist[i]; +if (0!=h)GC_printf("Free list %u (total size %lu):\n", +i,(unsigned long)GC_free_bytes[i]); +while (h){ +hdr*hhdr=HDR(h); +GC_printf("\t%p size %lu %s black listed\n", +(void*)h,(unsigned long)hhdr->hb_sz, +GC_is_black_listed(h,HBLKSIZE)!=0?"start": +GC_is_black_listed(h,hhdr->hb_sz)!=0?"partially": +"not"); +h=hhdr->hb_next; +} +} +GC_printf("GC_large_free_bytes:%lu\n", +(unsigned long)GC_large_free_bytes); +if ((total=GC_compute_large_free_bytes())!=GC_large_free_bytes) +GC_err_printf("GC_large_free_bytes INCONSISTENT!!Should be:%lu\n", +(unsigned long)total); +} +static int free_list_index_of(hdr*wanted) +{ +int i; +for (i=0;i<=N_HBLK_FLS;++i){ +struct hblk*h; +hdr*hhdr; +for (h=GC_hblkfreelist[i];h!=0;h=hhdr->hb_next){ +hhdr=HDR(h); +if (hhdr==wanted)return i; +} +} +return -1; +} +GC_API void GC_CALL GC_dump_regions(void) +{ +unsigned i; +for (i=0;i < GC_n_heap_sects;++i){ +ptr_t start=GC_heap_sects[i].hs_start; +size_t bytes=GC_heap_sects[i].hs_bytes; +ptr_t end=start+bytes; +ptr_t p; +while (i+1 < GC_n_heap_sects&&GC_heap_sects[i+1].hs_start==end){ +++i; +end=GC_heap_sects[i].hs_start+GC_heap_sects[i].hs_bytes; +} +GC_printf("***Section from %p to %p\n",(void*)start,(void*)end); +for (p=start;(word)p < (word)end;){ +hdr*hhdr=HDR(p); +if (IS_FORWARDING_ADDR_OR_NIL(hhdr)){ +GC_printf("\t%p Missing header!!(%p)\n", +(void*)p,(void*)hhdr); +p+=HBLKSIZE; +continue; +} +if (HBLK_IS_FREE(hhdr)){ +int correct_index=GC_hblk_fl_from_blocks( +divHBLKSZ(hhdr->hb_sz)); +int actual_index; +GC_printf("\t%p\tfree block of size 0x%lx bytes%s\n", +(void*)p,(unsigned long)(hhdr->hb_sz), +IS_MAPPED(hhdr)?"":" (unmapped)"); +actual_index=free_list_index_of(hhdr); +if (-1==actual_index){ +GC_printf("\t\tBlock not on free list %d!!\n", +correct_index); +} else if (correct_index!=actual_index){ +GC_printf("\t\tBlock on list %d,should be on %d!!\n", +actual_index,correct_index); +} +p+=hhdr->hb_sz; +} else { +GC_printf("\t%p\tused for blocks of size 0x%lx bytes\n", +(void*)p,(unsigned long)(hhdr->hb_sz)); +p+=HBLKSIZE*OBJ_SZ_TO_BLOCKS(hhdr->hb_sz); +} +} +} +} +#endif +static GC_bool setup_header(hdr*hhdr,struct hblk*block,size_t byte_sz, +int kind,unsigned flags) +{ +word descr; +#ifdef MARK_BIT_PER_GRANULE +if (byte_sz > MAXOBJBYTES) +flags|=LARGE_BLOCK; +#endif +#ifdef ENABLE_DISCLAIM +if (GC_obj_kinds[kind].ok_disclaim_proc) +flags|=HAS_DISCLAIM; +if (GC_obj_kinds[kind].ok_mark_unconditionally) +flags|=MARK_UNCONDITIONALLY; +#endif +hhdr->hb_sz=byte_sz; +hhdr->hb_obj_kind=(unsigned char)kind; +hhdr->hb_flags=(unsigned char)flags; +hhdr->hb_block=block; +descr=GC_obj_kinds[kind].ok_descriptor; +if (GC_obj_kinds[kind].ok_relocate_descr)descr+=byte_sz; +hhdr->hb_descr=descr; +#ifdef MARK_BIT_PER_OBJ +if (byte_sz > MAXOBJBYTES){ +hhdr->hb_inv_sz=LARGE_INV_SZ; +} else { +word inv_sz; +#if CPP_WORDSZ==64 +inv_sz=((word)1<<32)/byte_sz; +if (((inv_sz*byte_sz)>>32)==0)++inv_sz; +#else +GC_ASSERT(byte_sz>=4); +inv_sz=((unsigned)1<<31)/byte_sz; +inv_sz*=2; +while (inv_sz*byte_sz > byte_sz)++inv_sz; +#endif +#ifdef INV_SZ_COMPUTATION_CHECK +GC_ASSERT(((1ULL<<32)+byte_sz - 1)/byte_sz==inv_sz); +#endif +hhdr->hb_inv_sz=inv_sz; +} +#endif +#ifdef MARK_BIT_PER_GRANULE +{ +size_t granules=BYTES_TO_GRANULES(byte_sz); +if (EXPECT(!GC_add_map_entry(granules),FALSE)){ +hhdr->hb_sz=HBLKSIZE; +hhdr->hb_descr=0; +hhdr->hb_flags|=LARGE_BLOCK; +hhdr->hb_map=0; +return FALSE; +} +hhdr->hb_map=GC_obj_map[(hhdr->hb_flags&LARGE_BLOCK)!=0? +0:granules]; +} +#endif +GC_clear_hdr_marks(hhdr); +hhdr->hb_last_reclaimed=(unsigned short)GC_gc_no; +return(TRUE); +} +STATIC void GC_remove_from_fl_at(hdr*hhdr,int index) +{ +GC_ASSERT(((hhdr->hb_sz)&(HBLKSIZE-1))==0); +if (hhdr->hb_prev==0){ +GC_ASSERT(HDR(GC_hblkfreelist[index])==hhdr); +GC_hblkfreelist[index]=hhdr->hb_next; +} else { +hdr*phdr; +GET_HDR(hhdr->hb_prev,phdr); +phdr->hb_next=hhdr->hb_next; +} +GC_ASSERT(GC_free_bytes[index]>=hhdr->hb_sz); +GC_free_bytes[index]-=hhdr->hb_sz; +if (0!=hhdr->hb_next){ +hdr*nhdr; +GC_ASSERT(!IS_FORWARDING_ADDR_OR_NIL(NHDR(hhdr))); +GET_HDR(hhdr->hb_next,nhdr); +nhdr->hb_prev=hhdr->hb_prev; +} +} +GC_INLINE void GC_remove_from_fl(hdr*hhdr) +{ +GC_remove_from_fl_at(hhdr,GC_hblk_fl_from_blocks(divHBLKSZ(hhdr->hb_sz))); +} +static struct hblk*get_block_ending_at(struct hblk*h) +{ +struct hblk*p=h - 1; +hdr*phdr; +GET_HDR(p,phdr); +while (0!=phdr&&IS_FORWARDING_ADDR_OR_NIL(phdr)){ +p=FORWARDED_ADDR(p,phdr); +phdr=HDR(p); +} +if (0!=phdr){ +return p; +} +p=GC_prev_block(h - 1); +if (p){ +phdr=HDR(p); +if ((ptr_t)p+phdr->hb_sz==(ptr_t)h){ +return p; +} +} +return NULL; +} +STATIC struct hblk*GC_free_block_ending_at(struct hblk*h) +{ +struct hblk*p=get_block_ending_at(h); +if (p){ +hdr*phdr=HDR(p); +if (HBLK_IS_FREE(phdr)){ +return p; +} +} +return 0; +} +STATIC void GC_add_to_fl(struct hblk*h,hdr*hhdr) +{ +int index=GC_hblk_fl_from_blocks(divHBLKSZ(hhdr->hb_sz)); +struct hblk*second=GC_hblkfreelist[index]; +#if defined(GC_ASSERTIONS)&&!defined(USE_MUNMAP) +struct hblk*next=(struct hblk*)((word)h+hhdr->hb_sz); +hdr*nexthdr=HDR(next); +struct hblk*prev=GC_free_block_ending_at(h); +hdr*prevhdr=HDR(prev); +GC_ASSERT(nexthdr==0||!HBLK_IS_FREE(nexthdr) +||(GC_heapsize&SIGNB)!=0); +GC_ASSERT(prev==0||!HBLK_IS_FREE(prevhdr) +||(GC_heapsize&SIGNB)!=0); +#endif +GC_ASSERT(((hhdr->hb_sz)&(HBLKSIZE-1))==0); +GC_hblkfreelist[index]=h; +GC_free_bytes[index]+=hhdr->hb_sz; +GC_ASSERT(GC_free_bytes[index]<=GC_large_free_bytes); +hhdr->hb_next=second; +hhdr->hb_prev=0; +if (second){ +hdr*second_hdr; +GET_HDR(second,second_hdr); +second_hdr->hb_prev=h; +} +hhdr->hb_flags|=FREE_BLK; +} +#ifdef USE_MUNMAP +#ifndef MUNMAP_THRESHOLD +#define MUNMAP_THRESHOLD 6 +#endif +GC_INNER int GC_unmap_threshold=MUNMAP_THRESHOLD; +#ifdef COUNT_UNMAPPED_REGIONS +static int calc_num_unmapped_regions_delta(struct hblk*h,hdr*hhdr) +{ +struct hblk*prev=get_block_ending_at(h); +struct hblk*next; +GC_bool prev_unmapped=FALSE; +GC_bool next_unmapped=FALSE; +next=GC_next_block((struct hblk*)((ptr_t)h+hhdr->hb_sz),TRUE); +if ((ptr_t)next!=GC_unmap_end((ptr_t)h,(size_t)hhdr->hb_sz)){ +next=NULL; +} +if (prev!=NULL){ +hdr*prevhdr=HDR(prev); +prev_unmapped=!IS_MAPPED(prevhdr); +} +if (next!=NULL){ +hdr*nexthdr=HDR(next); +next_unmapped=!IS_MAPPED(nexthdr); +} +if (prev_unmapped&&next_unmapped){ +return IS_MAPPED(hhdr)?-1:1; +} +if (!prev_unmapped&&!next_unmapped){ +return IS_MAPPED(hhdr)?1:-1; +} +return 0; +} +#endif +GC_INLINE void GC_adjust_num_unmapped(struct hblk*h GC_ATTR_UNUSED, +hdr*hhdr GC_ATTR_UNUSED) +{ +#ifdef COUNT_UNMAPPED_REGIONS +GC_num_unmapped_regions+=calc_num_unmapped_regions_delta(h,hhdr); +#endif +} +GC_INNER void GC_unmap_old(void) +{ +int i; +if (GC_unmap_threshold==0) +return; +#ifdef COUNT_UNMAPPED_REGIONS +if (GC_num_unmapped_regions>=GC_UNMAPPED_REGIONS_SOFT_LIMIT) +return; +#endif +for (i=0;i<=N_HBLK_FLS;++i){ +struct hblk*h; +hdr*hhdr; +for (h=GC_hblkfreelist[i];0!=h;h=hhdr->hb_next){ +hhdr=HDR(h); +if (!IS_MAPPED(hhdr))continue; +if ((unsigned short)(GC_gc_no - hhdr->hb_last_reclaimed)> +(unsigned short)GC_unmap_threshold){ +#ifdef COUNT_UNMAPPED_REGIONS +int delta=calc_num_unmapped_regions_delta(h,hhdr); +signed_word regions=GC_num_unmapped_regions+delta; +if (delta>=0&®ions>=GC_UNMAPPED_REGIONS_SOFT_LIMIT){ +GC_COND_LOG_PRINTF("Unmapped regions limit reached!\n"); +return; +} +GC_num_unmapped_regions=regions; +#endif +GC_unmap((ptr_t)h,(size_t)hhdr->hb_sz); +hhdr->hb_flags|=WAS_UNMAPPED; +} +} +} +} +GC_INNER void GC_merge_unmapped(void) +{ +int i; +for (i=0;i<=N_HBLK_FLS;++i){ +struct hblk*h=GC_hblkfreelist[i]; +while (h!=0){ +struct hblk*next; +hdr*hhdr,*nexthdr; +word size,nextsize; +GET_HDR(h,hhdr); +size=hhdr->hb_sz; +next=(struct hblk*)((word)h+size); +GET_HDR(next,nexthdr); +if (0!=nexthdr&&HBLK_IS_FREE(nexthdr) +&&(signed_word)(size+(nextsize=nexthdr->hb_sz))> 0 +){ +if (IS_MAPPED(hhdr)&&!IS_MAPPED(nexthdr)){ +if (size > nextsize){ +GC_adjust_num_unmapped(next,nexthdr); +GC_remap((ptr_t)next,nextsize); +} else { +GC_adjust_num_unmapped(h,hhdr); +GC_unmap((ptr_t)h,size); +GC_unmap_gap((ptr_t)h,size,(ptr_t)next,nextsize); +hhdr->hb_flags|=WAS_UNMAPPED; +} +} else if (IS_MAPPED(nexthdr)&&!IS_MAPPED(hhdr)){ +if (size > nextsize){ +GC_adjust_num_unmapped(next,nexthdr); +GC_unmap((ptr_t)next,nextsize); +GC_unmap_gap((ptr_t)h,size,(ptr_t)next,nextsize); +} else { +GC_adjust_num_unmapped(h,hhdr); +GC_remap((ptr_t)h,size); +hhdr->hb_flags&=~WAS_UNMAPPED; +hhdr->hb_last_reclaimed=nexthdr->hb_last_reclaimed; +} +} else if (!IS_MAPPED(hhdr)&&!IS_MAPPED(nexthdr)){ +GC_unmap_gap((ptr_t)h,size,(ptr_t)next,nextsize); +} +GC_remove_from_fl_at(hhdr,i); +GC_remove_from_fl(nexthdr); +hhdr->hb_sz+=nexthdr->hb_sz; +GC_remove_header(next); +GC_add_to_fl(h,hhdr); +h=GC_hblkfreelist[i]; +} else { +h=hhdr->hb_next; +} +} +} +} +#endif +STATIC struct hblk*GC_get_first_part(struct hblk*h,hdr*hhdr, +size_t bytes,int index) +{ +word total_size=hhdr->hb_sz; +struct hblk*rest; +hdr*rest_hdr; +GC_ASSERT((total_size&(HBLKSIZE-1))==0); +GC_remove_from_fl_at(hhdr,index); +if (total_size==bytes)return h; +rest=(struct hblk*)((word)h+bytes); +rest_hdr=GC_install_header(rest); +if (0==rest_hdr){ +WARN("Header allocation failed:dropping block\n",0); +return(0); +} +rest_hdr->hb_sz=total_size - bytes; +rest_hdr->hb_flags=0; +#ifdef GC_ASSERTIONS +hhdr->hb_flags&=~FREE_BLK; +#endif +GC_add_to_fl(rest,rest_hdr); +return h; +} +STATIC void GC_split_block(struct hblk*h,hdr*hhdr,struct hblk*n, +hdr*nhdr,int index) +{ +word total_size=hhdr->hb_sz; +word h_size=(word)n - (word)h; +struct hblk*prev=hhdr->hb_prev; +struct hblk*next=hhdr->hb_next; +nhdr->hb_prev=prev; +nhdr->hb_next=next; +nhdr->hb_sz=total_size - h_size; +nhdr->hb_flags=0; +if (prev){ +HDR(prev)->hb_next=n; +} else { +GC_hblkfreelist[index]=n; +} +if (next){ +HDR(next)->hb_prev=n; +} +GC_ASSERT(GC_free_bytes[index] > h_size); +GC_free_bytes[index]-=h_size; +#ifdef USE_MUNMAP +hhdr->hb_last_reclaimed=(unsigned short)GC_gc_no; +#endif +hhdr->hb_sz=h_size; +GC_add_to_fl(h,hhdr); +nhdr->hb_flags|=FREE_BLK; +} +STATIC struct hblk* +GC_allochblk_nth(size_t sz,int kind,unsigned flags,int n, +int may_split); +#define AVOID_SPLIT_REMAPPED 2 +GC_INNER struct hblk* +GC_allochblk(size_t sz,int kind,unsigned flags) +{ +word blocks; +int start_list; +struct hblk*result; +int may_split; +int split_limit; +GC_ASSERT((sz&(GRANULE_BYTES - 1))==0); +blocks=OBJ_SZ_TO_BLOCKS_CHECKED(sz); +if ((signed_word)(blocks*HBLKSIZE)< 0){ +return 0; +} +start_list=GC_hblk_fl_from_blocks(blocks); +result=GC_allochblk_nth(sz,kind,flags,start_list,FALSE); +if (0!=result)return result; +may_split=TRUE; +if (GC_use_entire_heap||GC_dont_gc +||USED_HEAP_SIZE < GC_requested_heapsize +||GC_incremental||!GC_should_collect()){ +split_limit=N_HBLK_FLS; +} else if (GC_finalizer_bytes_freed > (GC_heapsize>>4)){ +split_limit=0; +} else { +split_limit=GC_enough_large_bytes_left(); +#ifdef USE_MUNMAP +if (split_limit > 0) +may_split=AVOID_SPLIT_REMAPPED; +#endif +} +if (start_list < UNIQUE_THRESHOLD){ +++start_list; +} +for (;start_list<=split_limit;++start_list){ +result=GC_allochblk_nth(sz,kind,flags,start_list,may_split); +if (0!=result) +break; +} +return result; +} +STATIC long GC_large_alloc_warn_suppressed=0; +STATIC struct hblk* +GC_allochblk_nth(size_t sz,int kind,unsigned flags,int n,int may_split) +{ +struct hblk*hbp; +hdr*hhdr; +struct hblk*thishbp; +hdr*thishdr; +signed_word size_needed=HBLKSIZE*OBJ_SZ_TO_BLOCKS_CHECKED(sz); +for (hbp=GC_hblkfreelist[n];;hbp=hhdr->hb_next){ +signed_word size_avail; +if (hbp){ +} else { +return NULL; +} +GET_HDR(hbp,hhdr); +size_avail=(signed_word)hhdr->hb_sz; +if (size_avail < size_needed)continue; +if (size_avail!=size_needed){ +if (!may_split)continue; +thishbp=hhdr->hb_next; +if (thishbp){ +signed_word next_size; +GET_HDR(thishbp,thishdr); +next_size=(signed_word)(thishdr->hb_sz); +if (next_size < size_avail +&&next_size>=size_needed +&&!GC_is_black_listed(thishbp,(word)size_needed)){ +continue; +} +} +} +if (!IS_UNCOLLECTABLE(kind)&&(kind!=PTRFREE +||size_needed > (signed_word)MAX_BLACK_LIST_ALLOC)){ +struct hblk*lasthbp=hbp; +ptr_t search_end=(ptr_t)hbp+size_avail - size_needed; +signed_word orig_avail=size_avail; +signed_word eff_size_needed=(flags&IGNORE_OFF_PAGE)!=0? +(signed_word)HBLKSIZE +:size_needed; +while ((word)lasthbp<=(word)search_end +&&(thishbp=GC_is_black_listed(lasthbp, +(word)eff_size_needed))!=0){ +lasthbp=thishbp; +} +size_avail-=(ptr_t)lasthbp - (ptr_t)hbp; +thishbp=lasthbp; +if (size_avail>=size_needed){ +if (thishbp!=hbp){ +#ifdef USE_MUNMAP +if (may_split==AVOID_SPLIT_REMAPPED&&!IS_MAPPED(hhdr)) +continue; +#endif +thishdr=GC_install_header(thishbp); +if (0!=thishdr){ +#ifdef USE_MUNMAP +if (!IS_MAPPED(hhdr)){ +GC_adjust_num_unmapped(hbp,hhdr); +GC_remap((ptr_t)hbp,(size_t)hhdr->hb_sz); +hhdr->hb_flags&=~WAS_UNMAPPED; +} +#endif +GC_split_block(hbp,hhdr,thishbp,thishdr,n); +hbp=thishbp; +hhdr=thishdr; +} +} +} else if (size_needed > (signed_word)BL_LIMIT +&&orig_avail - size_needed +> (signed_word)BL_LIMIT){ +if (++GC_large_alloc_warn_suppressed +>=GC_large_alloc_warn_interval){ +WARN("Repeated allocation of very large block " +"(appr. size %" WARN_PRIdPTR "):\n" +"\tMay lead to memory leak and poor performance\n", +size_needed); +GC_large_alloc_warn_suppressed=0; +} +size_avail=orig_avail; +} else if (size_avail==0 +&&size_needed==(signed_word)HBLKSIZE +&&IS_MAPPED(hhdr)){ +if (!GC_find_leak){ +static unsigned count=0; +if ((++count&3)==0){ +word total_size=hhdr->hb_sz; +struct hblk*limit=hbp+divHBLKSZ(total_size); +struct hblk*h; +struct hblk*prev=hhdr->hb_prev; +GC_large_free_bytes-=total_size; +GC_bytes_dropped+=total_size; +GC_remove_from_fl_at(hhdr,n); +for (h=hbp;(word)h < (word)limit;h++){ +if (h!=hbp){ +hhdr=GC_install_header(h); +} +if (NULL!=hhdr){ +(void)setup_header(hhdr,h,HBLKSIZE,PTRFREE,0); +if (GC_debugging_started){ +BZERO(h,HBLKSIZE); +} +} +} +hbp=prev; +if (0==hbp){ +return GC_allochblk_nth(sz,kind,flags,n,may_split); +} +hhdr=HDR(hbp); +} +} +} +} +if( size_avail>=size_needed){ +#ifdef USE_MUNMAP +if (!IS_MAPPED(hhdr)){ +GC_adjust_num_unmapped(hbp,hhdr); +GC_remap((ptr_t)hbp,(size_t)hhdr->hb_sz); +hhdr->hb_flags&=~WAS_UNMAPPED; +} +#endif +hbp=GC_get_first_part(hbp,hhdr,size_needed,n); +break; +} +} +if (0==hbp)return 0; +if (!GC_install_counts(hbp,(word)size_needed))return(0); +if (!setup_header(hhdr,hbp,sz,kind,flags)){ +GC_remove_counts(hbp,(word)size_needed); +return(0); +} +#ifndef GC_DISABLE_INCREMENTAL +GC_ASSERT((size_needed&(HBLKSIZE-1))==0); +GC_remove_protection(hbp,divHBLKSZ(size_needed), +(hhdr->hb_descr==0)); +#endif +GC_fail_count=0; +GC_large_free_bytes-=size_needed; +GC_ASSERT(IS_MAPPED(hhdr)); +return( hbp); +} +GC_INNER void GC_freehblk(struct hblk*hbp) +{ +struct hblk*next,*prev; +hdr*hhdr,*prevhdr,*nexthdr; +word size; +GET_HDR(hbp,hhdr); +size=HBLKSIZE*OBJ_SZ_TO_BLOCKS(hhdr->hb_sz); +if ((size&SIGNB)!=0) +ABORT("Deallocating excessively large block. Too large an allocation?"); +GC_remove_counts(hbp,size); +hhdr->hb_sz=size; +#ifdef USE_MUNMAP +hhdr->hb_last_reclaimed=(unsigned short)GC_gc_no; +#endif +if (HBLK_IS_FREE(hhdr)){ +ABORT_ARG1("Duplicate large block deallocation", +" of %p",(void*)hbp); +} +GC_ASSERT(IS_MAPPED(hhdr)); +hhdr->hb_flags|=FREE_BLK; +next=(struct hblk*)((ptr_t)hbp+size); +GET_HDR(next,nexthdr); +prev=GC_free_block_ending_at(hbp); +if(0!=nexthdr&&HBLK_IS_FREE(nexthdr)&&IS_MAPPED(nexthdr) +&&(signed_word)(hhdr->hb_sz+nexthdr->hb_sz)> 0 +){ +GC_remove_from_fl(nexthdr); +hhdr->hb_sz+=nexthdr->hb_sz; +GC_remove_header(next); +} +if (prev){ +prevhdr=HDR(prev); +if (IS_MAPPED(prevhdr) +&&(signed_word)(hhdr->hb_sz+prevhdr->hb_sz)> 0){ +GC_remove_from_fl(prevhdr); +prevhdr->hb_sz+=hhdr->hb_sz; +#ifdef USE_MUNMAP +prevhdr->hb_last_reclaimed=(unsigned short)GC_gc_no; +#endif +GC_remove_header(hbp); +hbp=prev; +hhdr=prevhdr; +} +} +GC_large_free_bytes+=size; +GC_add_to_fl(hbp,hhdr); +} +#include +#if!defined(MACOS)&&!defined(MSWINCE) +#include +#if!defined(SN_TARGET_ORBIS)&&!defined(SN_TARGET_PSP2)&&!defined(__CC_ARM) +#include +#endif +#endif +word GC_non_gc_bytes=0; +word GC_gc_no=0; +#ifndef NO_CLOCK +static unsigned long full_gc_total_time=0; +static unsigned full_gc_total_ns_frac=0; +static GC_bool measure_performance=FALSE; +GC_API void GC_CALL GC_start_performance_measurement(void) +{ +measure_performance=TRUE; +} +GC_API unsigned long GC_CALL GC_get_full_gc_total_time(void) +{ +return full_gc_total_time; +} +#endif +#ifndef GC_DISABLE_INCREMENTAL +GC_INNER GC_bool GC_incremental=FALSE; +#endif +GC_API int GC_CALL GC_is_incremental_mode(void) +{ +return (int)GC_incremental; +} +#ifdef THREADS +int GC_parallel=FALSE; +#endif +#if defined(GC_FULL_FREQ)&&!defined(CPPCHECK) +int GC_full_freq=GC_FULL_FREQ; +#else +int GC_full_freq=19; +#endif +STATIC GC_bool GC_need_full_gc=FALSE; +#ifdef THREAD_LOCAL_ALLOC +GC_INNER GC_bool GC_world_stopped=FALSE; +#endif +STATIC word GC_used_heap_size_after_full=0; +EXTERN_C_BEGIN +extern const char*const GC_copyright[]; +EXTERN_C_END +const char*const GC_copyright[]= +{"Copyright 1988,1989 Hans-J. Boehm and Alan J. Demers ", +"Copyright (c)1991-1995 by Xerox Corporation. All rights reserved. ", +"Copyright (c)1996-1998 by Silicon Graphics. All rights reserved. ", +"Copyright (c)1999-2009 by Hewlett-Packard Company. All rights reserved. ", +"Copyright (c)2008-2020 Ivan Maidanski ", +"THIS MATERIAL IS PROVIDED AS IS,WITH ABSOLUTELY NO WARRANTY", +" EXPRESSED OR IMPLIED. ANY USE IS AT YOUR OWN RISK.", +"See source code for details." }; +#ifndef GC_NO_VERSION_VAR +EXTERN_C_BEGIN +extern const unsigned GC_version; +EXTERN_C_END +const unsigned GC_version=((GC_VERSION_MAJOR<<16)| +(GC_VERSION_MINOR<<8)|GC_VERSION_MICRO); +#endif +GC_API unsigned GC_CALL GC_get_version(void) +{ +return (GC_VERSION_MAJOR<<16)|(GC_VERSION_MINOR<<8)| +GC_VERSION_MICRO; +} +#ifdef GC_DONT_EXPAND +int GC_dont_expand=TRUE; +#else +int GC_dont_expand=FALSE; +#endif +#if defined(GC_FREE_SPACE_DIVISOR)&&!defined(CPPCHECK) +word GC_free_space_divisor=GC_FREE_SPACE_DIVISOR; +#else +word GC_free_space_divisor=3; +#endif +GC_INNER int GC_CALLBACK GC_never_stop_func(void) +{ +return(0); +} +#if defined(GC_TIME_LIMIT)&&!defined(CPPCHECK) +unsigned long GC_time_limit=GC_TIME_LIMIT; +#elif defined(PARALLEL_MARK) +unsigned long GC_time_limit=GC_TIME_UNLIMITED; +#else +unsigned long GC_time_limit=50; +#endif +#ifndef NO_CLOCK +STATIC unsigned long GC_time_lim_nsec=0; +#define TV_NSEC_LIMIT (1000UL*1000) +GC_API void GC_CALL GC_set_time_limit_tv(struct GC_timeval_s tv) +{ +GC_ASSERT(tv.tv_ms<=GC_TIME_UNLIMITED); +GC_ASSERT(tv.tv_nsec < TV_NSEC_LIMIT); +GC_time_limit=tv.tv_ms; +GC_time_lim_nsec=tv.tv_nsec; +} +GC_API struct GC_timeval_s GC_CALL GC_get_time_limit_tv(void) +{ +struct GC_timeval_s tv; +tv.tv_ms=GC_time_limit; +tv.tv_nsec=GC_time_lim_nsec; +return tv; +} +STATIC CLOCK_TYPE GC_start_time=CLOCK_TYPE_INITIALIZER; +#endif +STATIC int GC_n_attempts=0; +STATIC GC_stop_func GC_default_stop_func=GC_never_stop_func; +GC_API void GC_CALL GC_set_stop_func(GC_stop_func stop_func) +{ +DCL_LOCK_STATE; +GC_ASSERT(NONNULL_ARG_NOT_NULL(stop_func)); +LOCK(); +GC_default_stop_func=stop_func; +UNLOCK(); +} +GC_API GC_stop_func GC_CALL GC_get_stop_func(void) +{ +GC_stop_func stop_func; +DCL_LOCK_STATE; +LOCK(); +stop_func=GC_default_stop_func; +UNLOCK(); +return stop_func; +} +#if defined(GC_DISABLE_INCREMENTAL)||defined(NO_CLOCK) +#define GC_timeout_stop_func GC_default_stop_func +#else +STATIC int GC_CALLBACK GC_timeout_stop_func (void) +{ +CLOCK_TYPE current_time; +static unsigned count=0; +unsigned long time_diff,nsec_diff; +if ((*GC_default_stop_func)()) +return(1); +if ((count++&3)!=0)return(0); +GET_TIME(current_time); +time_diff=MS_TIME_DIFF(current_time,GC_start_time); +nsec_diff=NS_FRAC_TIME_DIFF(current_time,GC_start_time); +#if defined(CPPCHECK) +GC_noop1((word)&nsec_diff); +#endif +if (time_diff>=GC_time_limit +&&(time_diff > GC_time_limit||nsec_diff>=GC_time_lim_nsec)){ +GC_COND_LOG_PRINTF("Abandoning stopped marking after %lu ms %lu ns" +" (attempt %d)\n", +time_diff,nsec_diff,GC_n_attempts); +return 1; +} +return(0); +} +#endif +#ifdef THREADS +GC_INNER word GC_total_stacksize=0; +#endif +static size_t min_bytes_allocd_minimum=1; +GC_API void GC_CALL GC_set_min_bytes_allocd(size_t value) +{ +GC_ASSERT(value > 0); +min_bytes_allocd_minimum=value; +} +GC_API size_t GC_CALL GC_get_min_bytes_allocd(void) +{ +return min_bytes_allocd_minimum; +} +static word min_bytes_allocd(void) +{ +word result; +word stack_size; +word total_root_size; +word scan_size; +#ifdef THREADS +if (GC_need_to_lock){ +stack_size=GC_total_stacksize; +#ifdef DEBUG_THREADS +GC_log_printf("Total stacks size:%lu\n", +(unsigned long)stack_size); +#endif +} else +#endif +{ +#ifdef STACK_NOT_SCANNED +stack_size=0; +#elif defined(STACK_GROWS_UP) +stack_size=GC_approx_sp()- GC_stackbottom; +#else +stack_size=GC_stackbottom - GC_approx_sp(); +#endif +} +total_root_size=2*stack_size+GC_root_size; +scan_size=2*GC_composite_in_use+GC_atomic_in_use/4 ++total_root_size; +result=scan_size/GC_free_space_divisor; +if (GC_incremental){ +result/=2; +} +return result > min_bytes_allocd_minimum +?result:min_bytes_allocd_minimum; +} +STATIC word GC_non_gc_bytes_at_gc=0; +STATIC word GC_adj_bytes_allocd(void) +{ +signed_word result; +signed_word expl_managed=(signed_word)GC_non_gc_bytes +- (signed_word)GC_non_gc_bytes_at_gc; +result=(signed_word)GC_bytes_allocd ++(signed_word)GC_bytes_dropped +- (signed_word)GC_bytes_freed ++(signed_word)GC_finalizer_bytes_freed +- expl_managed; +if (result > (signed_word)GC_bytes_allocd){ +result=GC_bytes_allocd; +} +result+=GC_bytes_finalized; +if (result < (signed_word)(GC_bytes_allocd>>3)){ +return(GC_bytes_allocd>>3); +} else { +return(result); +} +} +STATIC void GC_clear_a_few_frames(void) +{ +#ifndef CLEAR_NWORDS +#define CLEAR_NWORDS 64 +#endif +volatile word frames[CLEAR_NWORDS]; +BZERO((word*)frames,CLEAR_NWORDS*sizeof(word)); +} +STATIC word GC_collect_at_heapsize=GC_WORD_MAX; +GC_INNER GC_bool GC_should_collect(void) +{ +static word last_min_bytes_allocd; +static word last_gc_no; +if (last_gc_no!=GC_gc_no){ +last_min_bytes_allocd=min_bytes_allocd(); +last_gc_no=GC_gc_no; +} +return(GC_adj_bytes_allocd()>=last_min_bytes_allocd +||GC_heapsize>=GC_collect_at_heapsize); +} +GC_start_callback_proc GC_start_call_back=0; +GC_API void GC_CALL GC_set_start_callback(GC_start_callback_proc fn) +{ +DCL_LOCK_STATE; +LOCK(); +GC_start_call_back=fn; +UNLOCK(); +} +GC_API GC_start_callback_proc GC_CALL GC_get_start_callback(void) +{ +GC_start_callback_proc fn; +DCL_LOCK_STATE; +LOCK(); +fn=GC_start_call_back; +UNLOCK(); +return fn; +} +GC_INLINE void GC_notify_full_gc(void) +{ +if (GC_start_call_back!=0){ +(*GC_start_call_back)(); +} +} +STATIC GC_bool GC_is_full_gc=FALSE; +STATIC GC_bool GC_stopped_mark(GC_stop_func stop_func); +STATIC void GC_finish_collection(void); +STATIC void GC_maybe_gc(void) +{ +GC_ASSERT(I_HOLD_LOCK()); +ASSERT_CANCEL_DISABLED(); +if (GC_should_collect()){ +static int n_partial_gcs=0; +if (!GC_incremental){ +GC_try_to_collect_inner(GC_never_stop_func); +n_partial_gcs=0; +return; +} else { +#ifdef PARALLEL_MARK +if (GC_parallel) +GC_wait_for_reclaim(); +#endif +if (GC_need_full_gc||n_partial_gcs>=GC_full_freq){ +GC_COND_LOG_PRINTF( +"***>Full mark for collection #%lu after %lu allocd bytes\n", +(unsigned long)GC_gc_no+1,(unsigned long)GC_bytes_allocd); +GC_promote_black_lists(); +(void)GC_reclaim_all((GC_stop_func)0,TRUE); +GC_notify_full_gc(); +GC_clear_marks(); +n_partial_gcs=0; +GC_is_full_gc=TRUE; +} else { +n_partial_gcs++; +} +} +#ifndef NO_CLOCK +if (GC_time_limit!=GC_TIME_UNLIMITED){ GET_TIME(GC_start_time);} +#endif +if (GC_stopped_mark(GC_time_limit==GC_TIME_UNLIMITED? +GC_never_stop_func:GC_timeout_stop_func)){ +#ifdef SAVE_CALL_CHAIN +GC_save_callers(GC_last_stack); +#endif +GC_finish_collection(); +} else { +if (!GC_is_full_gc){ +GC_n_attempts++; +} +} +} +} +STATIC GC_on_collection_event_proc GC_on_collection_event=0; +GC_API void GC_CALL GC_set_on_collection_event(GC_on_collection_event_proc fn) +{ +DCL_LOCK_STATE; +LOCK(); +GC_on_collection_event=fn; +UNLOCK(); +} +GC_API GC_on_collection_event_proc GC_CALL GC_get_on_collection_event(void) +{ +GC_on_collection_event_proc fn; +DCL_LOCK_STATE; +LOCK(); +fn=GC_on_collection_event; +UNLOCK(); +return fn; +} +GC_INNER GC_bool GC_try_to_collect_inner(GC_stop_func stop_func) +{ +#ifndef NO_CLOCK +CLOCK_TYPE start_time=CLOCK_TYPE_INITIALIZER; +GC_bool start_time_valid; +#endif +ASSERT_CANCEL_DISABLED(); +GC_ASSERT(I_HOLD_LOCK()); +if (GC_dont_gc||(*stop_func)())return FALSE; +if (GC_on_collection_event) +GC_on_collection_event(GC_EVENT_START); +if (GC_incremental&&GC_collection_in_progress()){ +GC_COND_LOG_PRINTF( +"GC_try_to_collect_inner:finishing collection in progress\n"); +while(GC_collection_in_progress()){ +if ((*stop_func)()){ +return(FALSE); +} +ENTER_GC(); +GC_collect_a_little_inner(1); +EXIT_GC(); +} +} +GC_notify_full_gc(); +#ifndef NO_CLOCK +start_time_valid=FALSE; +if ((GC_print_stats|(int)measure_performance)!=0){ +if (GC_print_stats) +GC_log_printf("Initiating full world-stop collection!\n"); +start_time_valid=TRUE; +GET_TIME(start_time); +} +#endif +GC_promote_black_lists(); +#ifdef PARALLEL_MARK +if (GC_parallel) +GC_wait_for_reclaim(); +#endif +if ((GC_find_leak||stop_func!=GC_never_stop_func) +&&!GC_reclaim_all(stop_func,FALSE)){ +return(FALSE); +} +GC_invalidate_mark_state(); +GC_clear_marks(); +#ifdef SAVE_CALL_CHAIN +GC_save_callers(GC_last_stack); +#endif +GC_is_full_gc=TRUE; +if (!GC_stopped_mark(stop_func)){ +if (!GC_incremental){ +GC_invalidate_mark_state(); +GC_unpromote_black_lists(); +} +return(FALSE); +} +GC_finish_collection(); +#ifndef NO_CLOCK +if (start_time_valid){ +CLOCK_TYPE current_time; +unsigned long time_diff,ns_frac_diff; +GET_TIME(current_time); +time_diff=MS_TIME_DIFF(current_time,start_time); +ns_frac_diff=NS_FRAC_TIME_DIFF(current_time,start_time); +if (measure_performance){ +full_gc_total_time+=time_diff; +full_gc_total_ns_frac+=(unsigned)ns_frac_diff; +if (full_gc_total_ns_frac>=1000000U){ +full_gc_total_ns_frac-=1000000U; +full_gc_total_time++; +} +} +if (GC_print_stats) +GC_log_printf("Complete collection took %lu ms %lu ns\n", +time_diff,ns_frac_diff); +} +#endif +if (GC_on_collection_event) +GC_on_collection_event(GC_EVENT_END); +return(TRUE); +} +#ifndef GC_RATE +#define GC_RATE 10 +#endif +#ifndef MAX_PRIOR_ATTEMPTS +#define MAX_PRIOR_ATTEMPTS 1 +#endif +STATIC int GC_deficit=0; +STATIC int GC_rate=GC_RATE; +GC_API void GC_CALL GC_set_rate(int value) +{ +GC_ASSERT(value > 0); +GC_rate=value; +} +GC_API int GC_CALL GC_get_rate(void) +{ +return GC_rate; +} +static int max_prior_attempts=MAX_PRIOR_ATTEMPTS; +GC_API void GC_CALL GC_set_max_prior_attempts(int value) +{ +GC_ASSERT(value>=0); +max_prior_attempts=value; +} +GC_API int GC_CALL GC_get_max_prior_attempts(void) +{ +return max_prior_attempts; +} +GC_INNER void GC_collect_a_little_inner(int n) +{ +IF_CANCEL(int cancel_state;) +GC_ASSERT(I_HOLD_LOCK()); +if (GC_dont_gc)return; +DISABLE_CANCEL(cancel_state); +if (GC_incremental&&GC_collection_in_progress()){ +int i; +int max_deficit=GC_rate*n; +#ifdef PARALLEL_MARK +if (GC_time_limit!=GC_TIME_UNLIMITED) +GC_parallel_mark_disabled=TRUE; +#endif +for (i=GC_deficit;i < max_deficit;i++){ +if (GC_mark_some(NULL)) +break; +} +#ifdef PARALLEL_MARK +GC_parallel_mark_disabled=FALSE; +#endif +if (i < max_deficit){ +#ifdef SAVE_CALL_CHAIN +GC_save_callers(GC_last_stack); +#endif +#ifdef PARALLEL_MARK +if (GC_parallel) +GC_wait_for_reclaim(); +#endif +if (GC_n_attempts < max_prior_attempts +&&GC_time_limit!=GC_TIME_UNLIMITED){ +#ifndef NO_CLOCK +GET_TIME(GC_start_time); +#endif +if (GC_stopped_mark(GC_timeout_stop_func)){ +GC_finish_collection(); +} else { +GC_n_attempts++; +} +} else { +(void)GC_stopped_mark(GC_never_stop_func); +GC_finish_collection(); +} +} +if (GC_deficit > 0){ +GC_deficit-=max_deficit; +if (GC_deficit < 0) +GC_deficit=0; +} +} else { +GC_maybe_gc(); +} +RESTORE_CANCEL(cancel_state); +} +GC_INNER void (*GC_check_heap)(void)=0; +GC_INNER void (*GC_print_all_smashed)(void)=0; +GC_API int GC_CALL GC_collect_a_little(void) +{ +int result; +DCL_LOCK_STATE; +LOCK(); +ENTER_GC(); +GC_collect_a_little_inner(1); +EXIT_GC(); +result=(int)GC_collection_in_progress(); +UNLOCK(); +if (!result&&GC_debugging_started)GC_print_all_smashed(); +return(result); +} +#ifndef NO_CLOCK +static unsigned world_stopped_total_time=0; +static unsigned world_stopped_total_divisor=0; +#ifndef MAX_TOTAL_TIME_DIVISOR +#define MAX_TOTAL_TIME_DIVISOR 1000 +#endif +#endif +#ifdef USE_MUNMAP +#define IF_USE_MUNMAP(x)x +#define COMMA_IF_USE_MUNMAP(x),x +#else +#define IF_USE_MUNMAP(x) +#define COMMA_IF_USE_MUNMAP(x) +#endif +STATIC GC_bool GC_stopped_mark(GC_stop_func stop_func) +{ +int i; +#ifndef NO_CLOCK +CLOCK_TYPE start_time=CLOCK_TYPE_INITIALIZER; +#endif +GC_ASSERT(I_HOLD_LOCK()); +#if!defined(REDIRECT_MALLOC)&&defined(USE_WINALLOC) +GC_add_current_malloc_heap(); +#endif +#if defined(REGISTER_LIBRARIES_EARLY) +GC_cond_register_dynamic_libraries(); +#endif +#ifndef NO_CLOCK +if (GC_PRINT_STATS_FLAG) +GET_TIME(start_time); +#endif +#if!defined(GC_NO_FINALIZATION)&&!defined(GC_TOGGLE_REFS_NOT_NEEDED) +GC_process_togglerefs(); +#endif +#ifdef THREADS +if (GC_on_collection_event) +GC_on_collection_event(GC_EVENT_PRE_STOP_WORLD); +#endif +STOP_WORLD(); +#ifdef THREADS +if (GC_on_collection_event) +GC_on_collection_event(GC_EVENT_POST_STOP_WORLD); +#endif +#ifdef THREAD_LOCAL_ALLOC +GC_world_stopped=TRUE; +#endif +GC_COND_LOG_PRINTF( +"\n--> Marking for collection #%lu after %lu allocated bytes\n", +(unsigned long)GC_gc_no+1,(unsigned long)GC_bytes_allocd); +#ifdef MAKE_BACK_GRAPH +if (GC_print_back_height){ +GC_build_back_graph(); +} +#endif +if (GC_on_collection_event) +GC_on_collection_event(GC_EVENT_MARK_START); +GC_clear_a_few_frames(); +GC_noop6(0,0,0,0,0,0); +GC_initiate_gc(); +#ifdef PARALLEL_MARK +if (stop_func!=GC_never_stop_func) +GC_parallel_mark_disabled=TRUE; +#endif +for (i=0;!(*stop_func)();i++){ +if (GC_mark_some(GC_approx_sp())){ +#ifdef PARALLEL_MARK +if (GC_parallel&&GC_parallel_mark_disabled){ +GC_COND_LOG_PRINTF("Stopped marking done after %d iterations" +" with disabled parallel marker\n",i); +} +#endif +i=-1; +break; +} +} +#ifdef PARALLEL_MARK +GC_parallel_mark_disabled=FALSE; +#endif +if (i>=0){ +GC_COND_LOG_PRINTF("Abandoned stopped marking after" +" %d iterations\n",i); +GC_deficit=i; +#ifdef THREAD_LOCAL_ALLOC +GC_world_stopped=FALSE; +#endif +#ifdef THREADS +if (GC_on_collection_event) +GC_on_collection_event(GC_EVENT_PRE_START_WORLD); +#endif +START_WORLD(); +#ifdef THREADS +if (GC_on_collection_event) +GC_on_collection_event(GC_EVENT_POST_START_WORLD); +#endif +return FALSE; +} +GC_gc_no++; +GC_DBGLOG_PRINTF("GC #%lu freed %ld bytes,heap %lu KiB" +IF_USE_MUNMAP(" (+%lu KiB unmapped)")"\n", +(unsigned long)GC_gc_no,(long)GC_bytes_found, +TO_KiB_UL(GC_heapsize - GC_unmapped_bytes) +COMMA_IF_USE_MUNMAP(TO_KiB_UL(GC_unmapped_bytes))); +if (GC_debugging_started){ +(*GC_check_heap)(); +} +if (GC_on_collection_event){ +GC_on_collection_event(GC_EVENT_MARK_END); +#ifdef THREADS +GC_on_collection_event(GC_EVENT_PRE_START_WORLD); +#endif +} +#ifdef THREAD_LOCAL_ALLOC +GC_world_stopped=FALSE; +#endif +START_WORLD(); +#ifdef THREADS +if (GC_on_collection_event) +GC_on_collection_event(GC_EVENT_POST_START_WORLD); +#endif +#ifndef NO_CLOCK +if (GC_PRINT_STATS_FLAG){ +unsigned long time_diff; +unsigned total_time,divisor; +CLOCK_TYPE current_time; +GET_TIME(current_time); +time_diff=MS_TIME_DIFF(current_time,start_time); +total_time=world_stopped_total_time; +divisor=world_stopped_total_divisor; +if ((int)total_time < 0||divisor>=MAX_TOTAL_TIME_DIVISOR){ +total_time>>=1; +divisor>>=1; +} +total_time+=time_diff < (((unsigned)-1)>>1)? +(unsigned)time_diff:((unsigned)-1)>>1; +world_stopped_total_time=total_time; +world_stopped_total_divisor=++divisor; +GC_ASSERT(divisor!=0); +GC_log_printf("World-stopped marking took %lu ms %lu ns" +" (%u ms in average)\n", +time_diff,NS_FRAC_TIME_DIFF(current_time,start_time), +total_time/divisor); +} +#endif +return(TRUE); +} +GC_INNER void GC_set_fl_marks(ptr_t q) +{ +if (q){ +struct hblk*h=HBLKPTR(q); +struct hblk*last_h=h; +hdr*hhdr=HDR(h); +IF_PER_OBJ(word sz=hhdr->hb_sz;) +for (;;){ +word bit_no=MARK_BIT_NO((ptr_t)q - (ptr_t)h,sz); +if (!mark_bit_from_hdr(hhdr,bit_no)){ +set_mark_bit_from_hdr(hhdr,bit_no); +++hhdr->hb_n_marks; +} +q=(ptr_t)obj_link(q); +if (q==NULL) +break; +h=HBLKPTR(q); +if (h!=last_h){ +last_h=h; +hhdr=HDR(h); +IF_PER_OBJ(sz=hhdr->hb_sz;) +} +} +} +} +#if defined(GC_ASSERTIONS)&&defined(THREAD_LOCAL_ALLOC) +void GC_check_fl_marks(void**pfreelist) +{ +#if defined(AO_HAVE_load_acquire_read)&&!defined(THREAD_SANITIZER) +AO_t*list=(AO_t*)AO_load_acquire_read((AO_t*)pfreelist); +AO_t*prev; +AO_t*p; +if ((word)list<=HBLKSIZE)return; +prev=(AO_t*)pfreelist; +for (p=list;p!=NULL;){ +AO_t*next; +if (!GC_is_marked(p)){ +ABORT_ARG2("Unmarked local free list entry", +":object %p on list %p",(void*)p,(void*)list); +} +next=(AO_t*)AO_load_acquire_read(p); +if (AO_load(prev)!=(AO_t)p) +break; +prev=p; +p=next; +} +#else +(void)pfreelist; +#endif +} +#endif +STATIC void GC_clear_fl_marks(ptr_t q) +{ +struct hblk*h=HBLKPTR(q); +struct hblk*last_h=h; +hdr*hhdr=HDR(h); +word sz=hhdr->hb_sz; +for (;;){ +word bit_no=MARK_BIT_NO((ptr_t)q - (ptr_t)h,sz); +if (mark_bit_from_hdr(hhdr,bit_no)){ +size_t n_marks=hhdr->hb_n_marks; +GC_ASSERT(n_marks!=0); +clear_mark_bit_from_hdr(hhdr,bit_no); +n_marks--; +#ifdef PARALLEL_MARK +if (0!=n_marks||!GC_parallel){ +hhdr->hb_n_marks=n_marks; +} +#else +hhdr->hb_n_marks=n_marks; +#endif +} +GC_bytes_found-=sz; +q=(ptr_t)obj_link(q); +if (q==NULL) +break; +h=HBLKPTR(q); +if (h!=last_h){ +last_h=h; +hhdr=HDR(h); +sz=hhdr->hb_sz; +} +} +} +#if defined(GC_ASSERTIONS)&&defined(THREAD_LOCAL_ALLOC) +void GC_check_tls(void); +#endif +GC_on_heap_resize_proc GC_on_heap_resize=0; +GC_INLINE int GC_compute_heap_usage_percent(void) +{ +word used=GC_composite_in_use+GC_atomic_in_use; +word heap_sz=GC_heapsize - GC_unmapped_bytes; +#if defined(CPPCHECK) +word limit=(GC_WORD_MAX>>1)/50; +#else +const word limit=GC_WORD_MAX/100; +#endif +return used>=heap_sz?0:used < limit? +(int)((used*100)/heap_sz):(int)(used/(heap_sz/100)); +} +STATIC void GC_finish_collection(void) +{ +#ifndef NO_CLOCK +CLOCK_TYPE start_time=CLOCK_TYPE_INITIALIZER; +CLOCK_TYPE finalize_time=CLOCK_TYPE_INITIALIZER; +#endif +GC_ASSERT(I_HOLD_LOCK()); +#if defined(GC_ASSERTIONS)&&defined(THREAD_LOCAL_ALLOC)&&!defined(DBG_HDRS_ALL) +GC_check_tls(); +#endif +#ifndef NO_CLOCK +if (GC_print_stats) +GET_TIME(start_time); +#endif +if (GC_on_collection_event) +GC_on_collection_event(GC_EVENT_RECLAIM_START); +#ifndef GC_GET_HEAP_USAGE_NOT_NEEDED +if (GC_bytes_found > 0) +GC_reclaimed_bytes_before_gc+=(word)GC_bytes_found; +#endif +GC_bytes_found=0; +#if defined(LINUX)&&defined(__ELF__)&&!defined(SMALL_CONFIG) +if (GETENV("GC_PRINT_ADDRESS_MAP")!=0){ +GC_print_address_map(); +} +#endif +COND_DUMP; +if (GC_find_leak){ +word size; +unsigned kind; +ptr_t q; +for (kind=0;kind < GC_n_kinds;kind++){ +for (size=1;size<=MAXOBJGRANULES;size++){ +q=(ptr_t)GC_obj_kinds[kind].ok_freelist[size]; +if (q!=NULL) +GC_set_fl_marks(q); +} +} +GC_start_reclaim(TRUE); +} +#ifndef GC_NO_FINALIZATION +GC_finalize(); +#endif +#ifndef NO_CLOCK +if (GC_print_stats) +GET_TIME(finalize_time); +#endif +if (GC_print_back_height){ +#ifdef MAKE_BACK_GRAPH +GC_traverse_back_graph(); +#elif!defined(SMALL_CONFIG) +GC_err_printf("Back height not available:" +"Rebuild collector with -DMAKE_BACK_GRAPH\n"); +#endif +} +{ +word size; +ptr_t q; +unsigned kind; +for (kind=0;kind < GC_n_kinds;kind++){ +for (size=1;size<=MAXOBJGRANULES;size++){ +q=(ptr_t)GC_obj_kinds[kind].ok_freelist[size]; +if (q!=NULL) +GC_clear_fl_marks(q); +} +} +} +GC_VERBOSE_LOG_PRINTF("Bytes recovered before sweep - f.l. count=%ld\n", +(long)GC_bytes_found); +GC_start_reclaim(FALSE); +GC_DBGLOG_PRINTF("In-use heap:%d%% (%lu KiB pointers+%lu KiB other)\n", +GC_compute_heap_usage_percent(), +TO_KiB_UL(GC_composite_in_use), +TO_KiB_UL(GC_atomic_in_use)); +if (GC_is_full_gc){ +GC_used_heap_size_after_full=USED_HEAP_SIZE; +GC_need_full_gc=FALSE; +} else { +GC_need_full_gc=USED_HEAP_SIZE - GC_used_heap_size_after_full +> min_bytes_allocd(); +} +GC_VERBOSE_LOG_PRINTF("Immediately reclaimed %ld bytes,heapsize:" +" %lu bytes" IF_USE_MUNMAP(" (%lu unmapped)")"\n", +(long)GC_bytes_found, +(unsigned long)GC_heapsize +COMMA_IF_USE_MUNMAP((unsigned long) +GC_unmapped_bytes)); +GC_n_attempts=0; +GC_is_full_gc=FALSE; +GC_bytes_allocd_before_gc+=GC_bytes_allocd; +GC_non_gc_bytes_at_gc=GC_non_gc_bytes; +GC_bytes_allocd=0; +GC_bytes_dropped=0; +GC_bytes_freed=0; +GC_finalizer_bytes_freed=0; +IF_USE_MUNMAP(GC_unmap_old()); +if (GC_on_collection_event) +GC_on_collection_event(GC_EVENT_RECLAIM_END); +#ifndef NO_CLOCK +if (GC_print_stats){ +CLOCK_TYPE done_time; +GET_TIME(done_time); +#if!defined(SMALL_CONFIG)&&!defined(GC_NO_FINALIZATION) +GC_print_finalization_stats(); +#endif +GC_log_printf("Finalize and initiate sweep took %lu ms %lu ns" +"+%lu ms %lu ns\n", +MS_TIME_DIFF(finalize_time,start_time), +NS_FRAC_TIME_DIFF(finalize_time,start_time), +MS_TIME_DIFF(done_time,finalize_time), +NS_FRAC_TIME_DIFF(done_time,finalize_time)); +} +#elif!defined(SMALL_CONFIG)&&!defined(GC_NO_FINALIZATION) +if (GC_print_stats) +GC_print_finalization_stats(); +#endif +} +STATIC GC_bool GC_try_to_collect_general(GC_stop_func stop_func, +GC_bool force_unmap GC_ATTR_UNUSED) +{ +GC_bool result; +IF_USE_MUNMAP(int old_unmap_threshold;) +IF_CANCEL(int cancel_state;) +DCL_LOCK_STATE; +if (!EXPECT(GC_is_initialized,TRUE))GC_init(); +if (GC_debugging_started)GC_print_all_smashed(); +GC_INVOKE_FINALIZERS(); +LOCK(); +DISABLE_CANCEL(cancel_state); +#ifdef USE_MUNMAP +old_unmap_threshold=GC_unmap_threshold; +if (force_unmap|| +(GC_force_unmap_on_gcollect&&old_unmap_threshold > 0)) +GC_unmap_threshold=1; +#endif +ENTER_GC(); +GC_noop6(0,0,0,0,0,0); +result=GC_try_to_collect_inner(stop_func!=0?stop_func: +GC_default_stop_func); +EXIT_GC(); +IF_USE_MUNMAP(GC_unmap_threshold=old_unmap_threshold); +RESTORE_CANCEL(cancel_state); +UNLOCK(); +if (result){ +if (GC_debugging_started)GC_print_all_smashed(); +GC_INVOKE_FINALIZERS(); +} +return(result); +} +GC_API int GC_CALL GC_try_to_collect(GC_stop_func stop_func) +{ +GC_ASSERT(NONNULL_ARG_NOT_NULL(stop_func)); +return (int)GC_try_to_collect_general(stop_func,FALSE); +} +GC_API void GC_CALL GC_gcollect(void) +{ +(void)GC_try_to_collect_general(0,FALSE); +if (GC_have_errors)GC_print_all_errors(); +} +STATIC word GC_heapsize_at_forced_unmap=0; +GC_API void GC_CALL GC_gcollect_and_unmap(void) +{ +GC_heapsize_at_forced_unmap=GC_heapsize; +(void)GC_try_to_collect_general(GC_never_stop_func,TRUE); +} +#ifdef USE_PROC_FOR_LIBRARIES +GC_INNER void GC_add_to_our_memory(ptr_t p,size_t bytes) +{ +if (0==p)return; +if (GC_n_memory>=MAX_HEAP_SECTS) +ABORT("Too many GC-allocated memory sections:Increase MAX_HEAP_SECTS"); +GC_our_memory[GC_n_memory].hs_start=p; +GC_our_memory[GC_n_memory].hs_bytes=bytes; +GC_n_memory++; +} +#endif +GC_INNER void GC_add_to_heap(struct hblk*p,size_t bytes) +{ +hdr*phdr; +word endp; +if (GC_n_heap_sects>=MAX_HEAP_SECTS){ +ABORT("Too many heap sections:Increase MAXHINCR or MAX_HEAP_SECTS"); +} +while ((word)p<=HBLKSIZE){ +++p; +bytes-=HBLKSIZE; +if (0==bytes)return; +} +endp=(word)p+bytes; +if (endp<=(word)p){ +bytes-=HBLKSIZE; +if (0==bytes)return; +endp-=HBLKSIZE; +} +phdr=GC_install_header(p); +if (0==phdr){ +return; +} +GC_ASSERT(endp > (word)p&&endp==(word)p+bytes); +GC_heap_sects[GC_n_heap_sects].hs_start=(ptr_t)p; +GC_heap_sects[GC_n_heap_sects].hs_bytes=bytes; +GC_n_heap_sects++; +phdr->hb_sz=bytes; +phdr->hb_flags=0; +GC_freehblk(p); +GC_heapsize+=bytes; +GC_collect_at_heapsize+=bytes; +if (GC_collect_at_heapsize < GC_heapsize) +GC_collect_at_heapsize=GC_WORD_MAX; +if ((word)p<=(word)GC_least_plausible_heap_addr +||GC_least_plausible_heap_addr==0){ +GC_least_plausible_heap_addr=(void*)((ptr_t)p - sizeof(word)); +} +if ((word)p+bytes>=(word)GC_greatest_plausible_heap_addr){ +GC_greatest_plausible_heap_addr=(void*)endp; +} +} +#if!defined(NO_DEBUGGING) +void GC_print_heap_sects(void) +{ +unsigned i; +GC_printf("Total heap size:%lu" IF_USE_MUNMAP(" (%lu unmapped)")"\n", +(unsigned long)GC_heapsize +COMMA_IF_USE_MUNMAP((unsigned long)GC_unmapped_bytes)); +for (i=0;i < GC_n_heap_sects;i++){ +ptr_t start=GC_heap_sects[i].hs_start; +size_t len=GC_heap_sects[i].hs_bytes; +struct hblk*h; +unsigned nbl=0; +for (h=(struct hblk*)start;(word)h < (word)(start+len);h++){ +if (GC_is_black_listed(h,HBLKSIZE))nbl++; +} +GC_printf("Section %d from %p to %p %u/%lu blacklisted\n", +i,(void*)start,(void*)&start[len], +nbl,(unsigned long)divHBLKSZ(len)); +} +} +#endif +void*GC_least_plausible_heap_addr=(void*)GC_WORD_MAX; +void*GC_greatest_plausible_heap_addr=0; +GC_INLINE word GC_max(word x,word y) +{ +return(x > y?x:y); +} +GC_INLINE word GC_min(word x,word y) +{ +return(x < y?x:y); +} +STATIC word GC_max_heapsize=0; +GC_API void GC_CALL GC_set_max_heap_size(GC_word n) +{ +GC_max_heapsize=n; +} +GC_word GC_max_retries=0; +GC_INNER GC_bool GC_expand_hp_inner(word n) +{ +size_t bytes; +struct hblk*space; +word expansion_slop; +GC_ASSERT(I_HOLD_LOCK()); +GC_ASSERT(GC_page_size!=0); +if (n < MINHINCR)n=MINHINCR; +bytes=ROUNDUP_PAGESIZE((size_t)n*HBLKSIZE); +if (GC_max_heapsize!=0 +&&(GC_max_heapsize < (word)bytes +||GC_heapsize > GC_max_heapsize - (word)bytes)){ +return(FALSE); +} +space=GET_MEM(bytes); +GC_add_to_our_memory((ptr_t)space,bytes); +if (space==0){ +WARN("Failed to expand heap by %" WARN_PRIdPTR " bytes\n", +(word)bytes); +return(FALSE); +} +GC_INFOLOG_PRINTF("Grow heap to %lu KiB after %lu bytes allocated\n", +TO_KiB_UL(GC_heapsize+(word)bytes), +(unsigned long)GC_bytes_allocd); +expansion_slop=min_bytes_allocd()+4*MAXHINCR*HBLKSIZE; +if ((GC_last_heap_addr==0&&!((word)space&SIGNB)) +||(GC_last_heap_addr!=0 +&&(word)GC_last_heap_addr < (word)space)){ +word new_limit=(word)space+(word)bytes+expansion_slop; +if (new_limit > (word)space){ +GC_greatest_plausible_heap_addr= +(void*)GC_max((word)GC_greatest_plausible_heap_addr, +(word)new_limit); +} +} else { +word new_limit=(word)space - expansion_slop; +if (new_limit < (word)space){ +GC_least_plausible_heap_addr= +(void*)GC_min((word)GC_least_plausible_heap_addr, +(word)space - expansion_slop); +} +} +GC_prev_heap_addr=GC_last_heap_addr; +GC_last_heap_addr=(ptr_t)space; +GC_add_to_heap(space,bytes); +GC_collect_at_heapsize= +GC_heapsize+expansion_slop - 2*MAXHINCR*HBLKSIZE; +if (GC_collect_at_heapsize < GC_heapsize) +GC_collect_at_heapsize=GC_WORD_MAX; +if (GC_on_heap_resize) +(*GC_on_heap_resize)(GC_heapsize); +return(TRUE); +} +GC_API int GC_CALL GC_expand_hp(size_t bytes) +{ +int result; +DCL_LOCK_STATE; +if (!EXPECT(GC_is_initialized,TRUE))GC_init(); +LOCK(); +result=(int)GC_expand_hp_inner(divHBLKSZ((word)bytes)); +if (result)GC_requested_heapsize+=bytes; +UNLOCK(); +return(result); +} +GC_INNER unsigned GC_fail_count=0; +#if defined(GC_ALLOCD_BYTES_PER_FINALIZER)&&!defined(CPPCHECK) +STATIC word GC_allocd_bytes_per_finalizer=GC_ALLOCD_BYTES_PER_FINALIZER; +#else +STATIC word GC_allocd_bytes_per_finalizer=10000; +#endif +GC_API void GC_CALL GC_set_allocd_bytes_per_finalizer(GC_word value) +{ +GC_allocd_bytes_per_finalizer=value; +} +GC_API GC_word GC_CALL GC_get_allocd_bytes_per_finalizer(void) +{ +return GC_allocd_bytes_per_finalizer; +} +static word last_fo_entries=0; +static word last_bytes_finalized=0; +GC_INNER GC_bool GC_collect_or_expand(word needed_blocks, +GC_bool ignore_off_page, +GC_bool retry) +{ +GC_bool gc_not_stopped=TRUE; +word blocks_to_get; +IF_CANCEL(int cancel_state;) +GC_ASSERT(I_HOLD_LOCK()); +DISABLE_CANCEL(cancel_state); +if (!GC_incremental&&!GC_dont_gc&& +((GC_dont_expand&&GC_bytes_allocd > 0) +||(GC_fo_entries > last_fo_entries +&&(last_bytes_finalized|GC_bytes_finalized)!=0 +&&(GC_fo_entries - last_fo_entries) +*GC_allocd_bytes_per_finalizer > GC_bytes_allocd) +||GC_should_collect())){ +gc_not_stopped=GC_try_to_collect_inner( +GC_bytes_allocd > 0&&(!GC_dont_expand||!retry)? +GC_default_stop_func:GC_never_stop_func); +if (gc_not_stopped==TRUE||!retry){ +last_fo_entries=GC_fo_entries; +last_bytes_finalized=GC_bytes_finalized; +RESTORE_CANCEL(cancel_state); +return(TRUE); +} +} +blocks_to_get=(GC_heapsize - GC_heapsize_at_forced_unmap) +/(HBLKSIZE*GC_free_space_divisor) ++needed_blocks; +if (blocks_to_get > MAXHINCR){ +word slop; +if (ignore_off_page){ +slop=4; +} else { +slop=2*divHBLKSZ(BL_LIMIT); +if (slop > needed_blocks)slop=needed_blocks; +} +if (needed_blocks+slop > MAXHINCR){ +blocks_to_get=needed_blocks+slop; +} else { +blocks_to_get=MAXHINCR; +} +if (blocks_to_get > divHBLKSZ(GC_WORD_MAX)) +blocks_to_get=divHBLKSZ(GC_WORD_MAX); +} +if (!GC_expand_hp_inner(blocks_to_get) +&&(blocks_to_get==needed_blocks +||!GC_expand_hp_inner(needed_blocks))){ +if (gc_not_stopped==FALSE){ +GC_gcollect_inner(); +GC_ASSERT(GC_bytes_allocd==0); +} else if (GC_fail_count++< GC_max_retries){ +WARN("Out of Memory!Trying to continue...\n",0); +GC_gcollect_inner(); +} else { +#if!defined(AMIGA)||!defined(GC_AMIGA_FASTALLOC) +WARN("Out of Memory!Heap size:%" WARN_PRIdPTR " MiB." +" Returning NULL!\n",(GC_heapsize - GC_unmapped_bytes)>>20); +#endif +RESTORE_CANCEL(cancel_state); +return(FALSE); +} +} else if (GC_fail_count){ +GC_COND_LOG_PRINTF("Memory available again...\n"); +} +RESTORE_CANCEL(cancel_state); +return(TRUE); +} +GC_INNER ptr_t GC_allocobj(size_t gran,int kind) +{ +void**flh=&(GC_obj_kinds[kind].ok_freelist[gran]); +GC_bool tried_minor=FALSE; +GC_bool retry=FALSE; +GC_ASSERT(I_HOLD_LOCK()); +if (gran==0)return(0); +while (*flh==0){ +ENTER_GC(); +#ifndef GC_DISABLE_INCREMENTAL +if (GC_incremental&&GC_time_limit!=GC_TIME_UNLIMITED){ +GC_collect_a_little_inner(1); +} +#endif +GC_ASSERT(!GC_is_full_gc +||NULL==GC_obj_kinds[kind].ok_reclaim_list +||NULL==GC_obj_kinds[kind].ok_reclaim_list[gran]); +GC_continue_reclaim(gran,kind); +EXIT_GC(); +#if defined(CPPCHECK) +GC_noop1((word)&flh); +#endif +if (NULL==*flh){ +GC_new_hblk(gran,kind); +#if defined(CPPCHECK) +GC_noop1((word)&flh); +#endif +if (NULL==*flh){ +ENTER_GC(); +if (GC_incremental&&GC_time_limit==GC_TIME_UNLIMITED +&&!tried_minor){ +GC_collect_a_little_inner(1); +tried_minor=TRUE; +} else { +if (!GC_collect_or_expand(1,FALSE,retry)){ +EXIT_GC(); +return(0); +} +retry=TRUE; +} +EXIT_GC(); +} +} +} +GC_fail_count=0; +return (ptr_t)(*flh); +} +#ifndef MSWINCE +#include +#endif +#include +#ifndef SHORT_DBG_HDRS +GC_INNER int GC_has_other_debug_info(ptr_t p) +{ +ptr_t body=(ptr_t)((oh*)p+1); +word sz=GC_size(p); +if (HBLKPTR(p)!=HBLKPTR((ptr_t)body) +||sz < DEBUG_BYTES+EXTRA_BYTES){ +return 0; +} +if (((oh*)p)->oh_sf!=(START_FLAG^(word)body) +&&((word*)p)[BYTES_TO_WORDS(sz)-1]!=(END_FLAG^(word)body)){ +return 0; +} +if (((oh*)p)->oh_sz==sz){ +return -1; +} +return 1; +} +#endif +#ifdef LINT2 +long GC_random(void) +{ +static unsigned seed=1; +seed=(seed*1103515245U+12345)&GC_RAND_MAX; +return (long)seed; +} +#endif +#ifdef KEEP_BACK_PTRS +#ifdef LINT2 +#define RANDOM()GC_random() +#else +#include +#define GC_RAND_MAX RAND_MAX +#if defined(__GLIBC__)||defined(SOLARIS)||defined(HPUX)||defined(IRIX5)||defined(OSF1) +#define RANDOM()random() +#else +#define RANDOM()(long)rand() +#endif +#endif +GC_INNER void GC_store_back_pointer(ptr_t source,ptr_t dest) +{ +if (GC_HAS_DEBUG_INFO(dest)){ +#ifdef PARALLEL_MARK +AO_store((volatile AO_t*)&((oh*)dest)->oh_back_ptr, +(AO_t)HIDE_BACK_PTR(source)); +#else +((oh*)dest)->oh_back_ptr=HIDE_BACK_PTR(source); +#endif +} +} +GC_INNER void GC_marked_for_finalization(ptr_t dest) +{ +GC_store_back_pointer(MARKED_FOR_FINALIZATION,dest); +} +GC_API GC_ref_kind GC_CALL GC_get_back_ptr_info(void*dest,void**base_p, +size_t*offset_p) +{ +oh*hdr=(oh*)GC_base(dest); +ptr_t bp; +ptr_t bp_base; +#ifdef LINT2 +if (!hdr)ABORT("Invalid GC_get_back_ptr_info argument"); +#endif +if (!GC_HAS_DEBUG_INFO((ptr_t)hdr))return GC_NO_SPACE; +bp=(ptr_t)GC_REVEAL_POINTER(hdr->oh_back_ptr); +if (MARKED_FOR_FINALIZATION==bp)return GC_FINALIZER_REFD; +if (MARKED_FROM_REGISTER==bp)return GC_REFD_FROM_REG; +if (NOT_MARKED==bp)return GC_UNREFERENCED; +#if ALIGNMENT==1 +{ +ptr_t alternate_ptr=bp+1; +ptr_t target=*(ptr_t*)bp; +ptr_t alternate_target=*(ptr_t*)alternate_ptr; +if ((word)alternate_target>=(word)GC_least_plausible_heap_addr +&&(word)alternate_target<=(word)GC_greatest_plausible_heap_addr +&&((word)target < (word)GC_least_plausible_heap_addr +||(word)target > (word)GC_greatest_plausible_heap_addr)){ +bp=alternate_ptr; +} +} +#endif +bp_base=(ptr_t)GC_base(bp); +if (NULL==bp_base){ +*base_p=bp; +*offset_p=0; +return GC_REFD_FROM_ROOT; +} else { +if (GC_HAS_DEBUG_INFO(bp_base))bp_base+=sizeof(oh); +*base_p=bp_base; +*offset_p=bp - bp_base; +return GC_REFD_FROM_HEAP; +} +} +GC_API void*GC_CALL GC_generate_random_heap_address(void) +{ +size_t i; +word heap_offset=RANDOM(); +if (GC_heapsize > GC_RAND_MAX){ +heap_offset*=GC_RAND_MAX; +heap_offset+=RANDOM(); +} +heap_offset%=GC_heapsize; +for (i=0;;++i){ +size_t size; +if (i>=GC_n_heap_sects) +ABORT("GC_generate_random_heap_address:size inconsistency"); +size=GC_heap_sects[i].hs_bytes; +if (heap_offset < size){ +break; +} else { +heap_offset-=size; +} +} +return GC_heap_sects[i].hs_start+heap_offset; +} +GC_API void*GC_CALL GC_generate_random_valid_address(void) +{ +ptr_t result; +ptr_t base; +do { +result=(ptr_t)GC_generate_random_heap_address(); +base=(ptr_t)GC_base(result); +} while (NULL==base||!GC_is_marked(base)); +return result; +} +GC_API void GC_CALL GC_print_backtrace(void*p) +{ +void*current=p; +int i; +GC_ref_kind source; +size_t offset; +void*base; +GC_print_heap_obj((ptr_t)GC_base(current)); +for (i=0;;++i){ +source=GC_get_back_ptr_info(current,&base,&offset); +if (GC_UNREFERENCED==source){ +GC_err_printf("Reference could not be found\n"); +goto out; +} +if (GC_NO_SPACE==source){ +GC_err_printf("No debug info in object:Can't find reference\n"); +goto out; +} +GC_err_printf("Reachable via %d levels of pointers from ",i); +switch(source){ +case GC_REFD_FROM_ROOT: +GC_err_printf("root at %p\n\n",base); +goto out; +case GC_REFD_FROM_REG: +GC_err_printf("root in register\n\n"); +goto out; +case GC_FINALIZER_REFD: +GC_err_printf("list of finalizable objects\n\n"); +goto out; +case GC_REFD_FROM_HEAP: +GC_err_printf("offset %ld in object:\n",(long)offset); +GC_print_heap_obj((ptr_t)GC_base(base)); +break; +default: +GC_err_printf("INTERNAL ERROR:UNEXPECTED SOURCE!!!!\n"); +goto out; +} +current=base; +} +out:; +} +GC_INNER void GC_generate_random_backtrace_no_gc(void) +{ +void*current; +current=GC_generate_random_valid_address(); +GC_printf("\n****Chosen address %p in object\n",current); +GC_print_backtrace(current); +} +GC_API void GC_CALL GC_generate_random_backtrace(void) +{ +if (GC_try_to_collect(GC_never_stop_func)==0){ +GC_err_printf("Cannot generate a backtrace:" +"garbage collection is disabled!\n"); +return; +} +GC_generate_random_backtrace_no_gc(); +} +#endif +#define CROSSES_HBLK(p,sz)(((word)((p)+sizeof(oh)+(sz)- 1)^(word)(p))>=HBLKSIZE) +GC_INNER void*GC_store_debug_info_inner(void*p,word sz GC_ATTR_UNUSED, +const char*string,int linenum) +{ +word*result=(word*)((oh*)p+1); +GC_ASSERT(I_HOLD_LOCK()); +GC_ASSERT(GC_size(p)>=sizeof(oh)+sz); +GC_ASSERT(!(SMALL_OBJ(sz)&&CROSSES_HBLK((ptr_t)p,sz))); +#ifdef KEEP_BACK_PTRS +((oh*)p)->oh_back_ptr=HIDE_BACK_PTR(NOT_MARKED); +#endif +#ifdef MAKE_BACK_GRAPH +((oh*)p)->oh_bg_ptr=HIDE_BACK_PTR((ptr_t)0); +#endif +((oh*)p)->oh_string=string; +((oh*)p)->oh_int=linenum; +#ifndef SHORT_DBG_HDRS +((oh*)p)->oh_sz=sz; +((oh*)p)->oh_sf=START_FLAG^(word)result; +((word*)p)[BYTES_TO_WORDS(GC_size(p))-1]= +result[SIMPLE_ROUNDED_UP_WORDS(sz)]=END_FLAG^(word)result; +#endif +return result; +} +static void*store_debug_info(void*p,size_t lb, +const char*fn,GC_EXTRA_PARAMS) +{ +void*result; +DCL_LOCK_STATE; +if (NULL==p){ +GC_err_printf("%s(%lu)returning NULL (%s:%d)\n", +fn,(unsigned long)lb,s,i); +return NULL; +} +LOCK(); +if (!GC_debugging_started) +GC_start_debugging_inner(); +ADD_CALL_CHAIN(p,ra); +result=GC_store_debug_info_inner(p,(word)lb,s,i); +UNLOCK(); +return result; +} +#ifndef SHORT_DBG_HDRS +STATIC ptr_t GC_check_annotated_obj(oh*ohdr) +{ +ptr_t body=(ptr_t)(ohdr+1); +word gc_sz=GC_size((ptr_t)ohdr); +if (ohdr->oh_sz+DEBUG_BYTES > gc_sz){ +return((ptr_t)(&(ohdr->oh_sz))); +} +if (ohdr->oh_sf!=(START_FLAG^(word)body)){ +return((ptr_t)(&(ohdr->oh_sf))); +} +if (((word*)ohdr)[BYTES_TO_WORDS(gc_sz)-1]!=(END_FLAG^(word)body)){ +return (ptr_t)(&((word*)ohdr)[BYTES_TO_WORDS(gc_sz)-1]); +} +if (((word*)body)[SIMPLE_ROUNDED_UP_WORDS(ohdr->oh_sz)] +!=(END_FLAG^(word)body)){ +return (ptr_t)(&((word*)body)[SIMPLE_ROUNDED_UP_WORDS(ohdr->oh_sz)]); +} +return(0); +} +#endif +STATIC GC_describe_type_fn GC_describe_type_fns[MAXOBJKINDS]={0}; +GC_API void GC_CALL GC_register_describe_type_fn(int kind, +GC_describe_type_fn fn) +{ +GC_describe_type_fns[kind]=fn; +} +#define GET_OH_LINENUM(ohdr)((int)(ohdr)->oh_int) +#ifndef SHORT_DBG_HDRS +#define IF_NOT_SHORTDBG_HDRS(x)x +#define COMMA_IFNOT_SHORTDBG_HDRS(x),x +#else +#define IF_NOT_SHORTDBG_HDRS(x) +#define COMMA_IFNOT_SHORTDBG_HDRS(x) +#endif +STATIC void GC_print_obj(ptr_t p) +{ +oh*ohdr=(oh*)GC_base(p); +ptr_t q; +hdr*hhdr; +int kind; +const char*kind_str; +char buffer[GC_TYPE_DESCR_LEN+1]; +GC_ASSERT(I_DONT_HOLD_LOCK()); +#ifdef LINT2 +if (!ohdr)ABORT("Invalid GC_print_obj argument"); +#endif +q=(ptr_t)(ohdr+1); +hhdr=GC_find_header(q); +kind=hhdr->hb_obj_kind; +if (0!=GC_describe_type_fns[kind]&&GC_is_marked(ohdr)){ +buffer[GC_TYPE_DESCR_LEN]=0; +(GC_describe_type_fns[kind])(q,buffer); +GC_ASSERT(buffer[GC_TYPE_DESCR_LEN]==0); +kind_str=buffer; +} else { +switch(kind){ +case PTRFREE: +kind_str="PTRFREE"; +break; +case NORMAL: +kind_str="NORMAL"; +break; +case UNCOLLECTABLE: +kind_str="UNCOLLECTABLE"; +break; +#ifdef GC_ATOMIC_UNCOLLECTABLE +case AUNCOLLECTABLE: +kind_str="ATOMIC_UNCOLLECTABLE"; +break; +#endif +default: +kind_str=NULL; +} +} +if (NULL!=kind_str){ +GC_err_printf("%p (%s:%d," IF_NOT_SHORTDBG_HDRS(" sz=%lu,")" %s)\n", +(void*)((ptr_t)ohdr+sizeof(oh)), +ohdr->oh_string,GET_OH_LINENUM(ohdr) +COMMA_IFNOT_SHORTDBG_HDRS((unsigned long)ohdr->oh_sz), +kind_str); +} else { +GC_err_printf("%p (%s:%d," IF_NOT_SHORTDBG_HDRS(" sz=%lu,") +" kind=%d descr=0x%lx)\n", +(void*)((ptr_t)ohdr+sizeof(oh)), +ohdr->oh_string,GET_OH_LINENUM(ohdr) +COMMA_IFNOT_SHORTDBG_HDRS((unsigned long)ohdr->oh_sz), +kind,(unsigned long)hhdr->hb_descr); +} +PRINT_CALL_CHAIN(ohdr); +} +STATIC void GC_debug_print_heap_obj_proc(ptr_t p) +{ +GC_ASSERT(I_DONT_HOLD_LOCK()); +if (GC_HAS_DEBUG_INFO(p)){ +GC_print_obj(p); +} else { +GC_default_print_heap_obj_proc(p); +} +} +#ifndef SHORT_DBG_HDRS +STATIC void GC_print_smashed_obj(const char*msg,void*p, +ptr_t clobbered_addr) +{ +oh*ohdr=(oh*)GC_base(p); +GC_ASSERT(I_DONT_HOLD_LOCK()); +#ifdef LINT2 +if (!ohdr)ABORT("Invalid GC_print_smashed_obj argument"); +#endif +if ((word)clobbered_addr<=(word)(&ohdr->oh_sz) +||ohdr->oh_string==0){ +GC_err_printf( +"%s %p in or near object at %p(,appr. sz=%lu)\n", +msg,(void*)clobbered_addr,p, +(unsigned long)(GC_size((ptr_t)ohdr)- DEBUG_BYTES)); +} else { +GC_err_printf("%s %p in or near object at %p (%s:%d,sz=%lu)\n", +msg,(void*)clobbered_addr,p, +(word)(ohdr->oh_string)< HBLKSIZE?"(smashed string)": +ohdr->oh_string[0]=='\0'?"EMPTY(smashed?)": +ohdr->oh_string, +GET_OH_LINENUM(ohdr),(unsigned long)(ohdr->oh_sz)); +PRINT_CALL_CHAIN(ohdr); +} +} +STATIC void GC_check_heap_proc (void); +STATIC void GC_print_all_smashed_proc (void); +#else +STATIC void GC_do_nothing(void){} +#endif +GC_INNER void GC_start_debugging_inner(void) +{ +GC_ASSERT(I_HOLD_LOCK()); +#ifndef SHORT_DBG_HDRS +GC_check_heap=GC_check_heap_proc; +GC_print_all_smashed=GC_print_all_smashed_proc; +#else +GC_check_heap=GC_do_nothing; +GC_print_all_smashed=GC_do_nothing; +#endif +GC_print_heap_obj=GC_debug_print_heap_obj_proc; +GC_debugging_started=TRUE; +GC_register_displacement_inner((word)sizeof(oh)); +#if defined(CPPCHECK) +GC_noop1(GC_debug_header_size); +#endif +} +const size_t GC_debug_header_size=sizeof(oh); +GC_API size_t GC_CALL GC_get_debug_header_size(void){ +return sizeof(oh); +} +GC_API void GC_CALL GC_debug_register_displacement(size_t offset) +{ +DCL_LOCK_STATE; +LOCK(); +GC_register_displacement_inner(offset); +GC_register_displacement_inner((word)sizeof(oh)+offset); +UNLOCK(); +} +#ifdef GC_ADD_CALLER +#if defined(HAVE_DLADDR)&&defined(GC_HAVE_RETURN_ADDR_PARENT) +#include +STATIC void GC_caller_func_offset(word ad,const char**symp,int*offp) +{ +Dl_info caller; +if (ad&&dladdr((void*)ad,&caller)&&caller.dli_sname!=NULL){ +*symp=caller.dli_sname; +*offp=(int)((char*)ad - (char*)caller.dli_saddr); +} +if (NULL==*symp){ +*symp="unknown"; +} +} +#else +#define GC_caller_func_offset(ad,symp,offp)(void)(*(symp)="unknown") +#endif +#endif +GC_API GC_ATTR_MALLOC void*GC_CALL GC_debug_malloc(size_t lb, +GC_EXTRA_PARAMS) +{ +void*result; +result=GC_malloc(SIZET_SAT_ADD(lb,DEBUG_BYTES)); +#ifdef GC_ADD_CALLER +if (s==NULL){ +GC_caller_func_offset(ra,&s,&i); +} +#endif +return store_debug_info(result,lb,"GC_debug_malloc",OPT_RA s,i); +} +GC_API GC_ATTR_MALLOC void*GC_CALL +GC_debug_malloc_ignore_off_page(size_t lb,GC_EXTRA_PARAMS) +{ +void*result=GC_malloc_ignore_off_page(SIZET_SAT_ADD(lb,DEBUG_BYTES)); +return store_debug_info(result,lb,"GC_debug_malloc_ignore_off_page", +OPT_RA s,i); +} +GC_API GC_ATTR_MALLOC void*GC_CALL +GC_debug_malloc_atomic_ignore_off_page(size_t lb,GC_EXTRA_PARAMS) +{ +void*result=GC_malloc_atomic_ignore_off_page( +SIZET_SAT_ADD(lb,DEBUG_BYTES)); +return store_debug_info(result,lb, +"GC_debug_malloc_atomic_ignore_off_page", +OPT_RA s,i); +} +STATIC void*GC_debug_generic_malloc(size_t lb,int knd,GC_EXTRA_PARAMS) +{ +void*result=GC_generic_malloc(SIZET_SAT_ADD(lb,DEBUG_BYTES),knd); +return store_debug_info(result,lb,"GC_debug_generic_malloc", +OPT_RA s,i); +} +#ifdef DBG_HDRS_ALL +GC_INNER void*GC_debug_generic_malloc_inner(size_t lb,int k) +{ +void*result; +GC_ASSERT(I_HOLD_LOCK()); +result=GC_generic_malloc_inner(SIZET_SAT_ADD(lb,DEBUG_BYTES),k); +if (NULL==result){ +GC_err_printf("GC internal allocation (%lu bytes)returning NULL\n", +(unsigned long)lb); +return(0); +} +if (!GC_debugging_started){ +GC_start_debugging_inner(); +} +ADD_CALL_CHAIN(result,GC_RETURN_ADDR); +return (GC_store_debug_info_inner(result,(word)lb,"INTERNAL",0)); +} +GC_INNER void*GC_debug_generic_malloc_inner_ignore_off_page(size_t lb, +int k) +{ +void*result; +GC_ASSERT(I_HOLD_LOCK()); +result=GC_generic_malloc_inner_ignore_off_page( +SIZET_SAT_ADD(lb,DEBUG_BYTES),k); +if (NULL==result){ +GC_err_printf("GC internal allocation (%lu bytes)returning NULL\n", +(unsigned long)lb); +return(0); +} +if (!GC_debugging_started){ +GC_start_debugging_inner(); +} +ADD_CALL_CHAIN(result,GC_RETURN_ADDR); +return (GC_store_debug_info_inner(result,(word)lb,"INTERNAL",0)); +} +#endif +#ifndef CPPCHECK +GC_API void*GC_CALL GC_debug_malloc_stubborn(size_t lb,GC_EXTRA_PARAMS) +{ +return GC_debug_malloc(lb,OPT_RA s,i); +} +GC_API void GC_CALL GC_debug_change_stubborn( +const void*p GC_ATTR_UNUSED){} +#endif +GC_API void GC_CALL GC_debug_end_stubborn_change(const void*p) +{ +const void*q=GC_base_C(p); +if (NULL==q){ +ABORT_ARG1("GC_debug_end_stubborn_change:bad arg",":%p",p); +} +GC_end_stubborn_change(q); +} +GC_API void GC_CALL GC_debug_ptr_store_and_dirty(void*p,const void*q) +{ +*(void**)GC_is_visible(p)=GC_is_valid_displacement((void*)q); +GC_debug_end_stubborn_change(p); +REACHABLE_AFTER_DIRTY(q); +} +GC_API GC_ATTR_MALLOC void*GC_CALL GC_debug_malloc_atomic(size_t lb, +GC_EXTRA_PARAMS) +{ +void*result=GC_malloc_atomic(SIZET_SAT_ADD(lb,DEBUG_BYTES)); +return store_debug_info(result,lb,"GC_debug_malloc_atomic", +OPT_RA s,i); +} +GC_API GC_ATTR_MALLOC char*GC_CALL GC_debug_strdup(const char*str, +GC_EXTRA_PARAMS) +{ +char*copy; +size_t lb; +if (str==NULL){ +if (GC_find_leak) +GC_err_printf("strdup(NULL)behavior is undefined\n"); +return NULL; +} +lb=strlen(str)+1; +copy=(char*)GC_debug_malloc_atomic(lb,OPT_RA s,i); +if (copy==NULL){ +#ifndef MSWINCE +errno=ENOMEM; +#endif +return NULL; +} +BCOPY(str,copy,lb); +return copy; +} +GC_API GC_ATTR_MALLOC char*GC_CALL GC_debug_strndup(const char*str, +size_t size,GC_EXTRA_PARAMS) +{ +char*copy; +size_t len=strlen(str); +if (len > size) +len=size; +copy=(char*)GC_debug_malloc_atomic(len+1,OPT_RA s,i); +if (copy==NULL){ +#ifndef MSWINCE +errno=ENOMEM; +#endif +return NULL; +} +if (len > 0) +BCOPY(str,copy,len); +copy[len]='\0'; +return copy; +} +#ifdef GC_REQUIRE_WCSDUP +#include +GC_API GC_ATTR_MALLOC wchar_t*GC_CALL GC_debug_wcsdup(const wchar_t*str, +GC_EXTRA_PARAMS) +{ +size_t lb=(wcslen(str)+1)*sizeof(wchar_t); +wchar_t*copy=(wchar_t*)GC_debug_malloc_atomic(lb,OPT_RA s,i); +if (copy==NULL){ +#ifndef MSWINCE +errno=ENOMEM; +#endif +return NULL; +} +BCOPY(str,copy,lb); +return copy; +} +#endif +GC_API GC_ATTR_MALLOC void*GC_CALL GC_debug_malloc_uncollectable(size_t lb, +GC_EXTRA_PARAMS) +{ +void*result=GC_malloc_uncollectable( +SIZET_SAT_ADD(lb,UNCOLLECTABLE_DEBUG_BYTES)); +return store_debug_info(result,lb,"GC_debug_malloc_uncollectable", +OPT_RA s,i); +} +#ifdef GC_ATOMIC_UNCOLLECTABLE +GC_API GC_ATTR_MALLOC void*GC_CALL +GC_debug_malloc_atomic_uncollectable(size_t lb,GC_EXTRA_PARAMS) +{ +void*result=GC_malloc_atomic_uncollectable( +SIZET_SAT_ADD(lb,UNCOLLECTABLE_DEBUG_BYTES)); +return store_debug_info(result,lb, +"GC_debug_malloc_atomic_uncollectable", +OPT_RA s,i); +} +#endif +#ifndef GC_FREED_MEM_MARKER +#if CPP_WORDSZ==32 +#define GC_FREED_MEM_MARKER 0xdeadbeef +#else +#define GC_FREED_MEM_MARKER GC_WORD_C(0xEFBEADDEdeadbeef) +#endif +#endif +GC_API void GC_CALL GC_debug_free(void*p) +{ +ptr_t base; +if (0==p)return; +base=(ptr_t)GC_base(p); +if (NULL==base){ +#if defined(REDIRECT_MALLOC)&&((defined(NEED_CALLINFO)&&defined(GC_HAVE_BUILTIN_BACKTRACE))||defined(GC_LINUX_THREADS)||defined(GC_SOLARIS_THREADS)||defined(MSWIN32)) +if (!GC_is_heap_ptr(p))return; +#endif +ABORT_ARG1("Invalid pointer passed to free()",":%p",p); +} +if ((ptr_t)p - (ptr_t)base!=sizeof(oh)){ +#if defined(REDIRECT_FREE)&&defined(USE_PROC_FOR_LIBRARIES) +#endif +GC_err_printf( +"GC_debug_free called on pointer %p w/o debugging info\n",p); +} else { +#ifndef SHORT_DBG_HDRS +ptr_t clobbered=GC_check_annotated_obj((oh*)base); +word sz=GC_size(base); +if (clobbered!=0){ +GC_have_errors=TRUE; +if (((oh*)base)->oh_sz==sz){ +GC_print_smashed_obj( +"GC_debug_free:found previously deallocated (?)object at", +p,clobbered); +return; +} else { +GC_print_smashed_obj("GC_debug_free:found smashed location at", +p,clobbered); +} +} +((oh*)base)->oh_sz=sz; +#endif +} +if (GC_find_leak +#ifndef SHORT_DBG_HDRS +&&((ptr_t)p - (ptr_t)base!=sizeof(oh)||!GC_findleak_delay_free) +#endif +){ +GC_free(base); +} else { +hdr*hhdr=HDR(p); +if (hhdr->hb_obj_kind==UNCOLLECTABLE +#ifdef GC_ATOMIC_UNCOLLECTABLE +||hhdr->hb_obj_kind==AUNCOLLECTABLE +#endif +){ +GC_free(base); +} else { +word i; +word sz=hhdr->hb_sz; +word obj_sz=BYTES_TO_WORDS(sz - sizeof(oh)); +for (i=0;i < obj_sz;++i) +((word*)p)[i]=GC_FREED_MEM_MARKER; +GC_ASSERT((word*)p+i==(word*)(base+sz)); +LOCK(); +GC_bytes_freed+=sz; +UNLOCK(); +} +} +} +#if defined(THREADS)&&defined(DBG_HDRS_ALL) +GC_INNER void GC_debug_free_inner(void*p) +{ +ptr_t base=(ptr_t)GC_base(p); +GC_ASSERT((ptr_t)p - (ptr_t)base==sizeof(oh)); +#ifdef LINT2 +if (!base)ABORT("Invalid GC_debug_free_inner argument"); +#endif +#ifndef SHORT_DBG_HDRS +((oh*)base)->oh_sz=GC_size(base); +#endif +GC_free_inner(base); +} +#endif +GC_API void*GC_CALL GC_debug_realloc(void*p,size_t lb,GC_EXTRA_PARAMS) +{ +void*base; +void*result; +hdr*hhdr; +if (p==0){ +return GC_debug_malloc(lb,OPT_RA s,i); +} +if (0==lb){ +GC_debug_free(p); +return NULL; +} +#ifdef GC_ADD_CALLER +if (s==NULL){ +GC_caller_func_offset(ra,&s,&i); +} +#endif +base=GC_base(p); +if (base==0){ +ABORT_ARG1("Invalid pointer passed to realloc()",":%p",p); +} +if ((ptr_t)p - (ptr_t)base!=sizeof(oh)){ +GC_err_printf( +"GC_debug_realloc called on pointer %p w/o debugging info\n",p); +return(GC_realloc(p,lb)); +} +hhdr=HDR(base); +switch (hhdr->hb_obj_kind){ +case NORMAL: +result=GC_debug_malloc(lb,OPT_RA s,i); +break; +case PTRFREE: +result=GC_debug_malloc_atomic(lb,OPT_RA s,i); +break; +case UNCOLLECTABLE: +result=GC_debug_malloc_uncollectable(lb,OPT_RA s,i); +break; +#ifdef GC_ATOMIC_UNCOLLECTABLE +case AUNCOLLECTABLE: +result=GC_debug_malloc_atomic_uncollectable(lb,OPT_RA s,i); +break; +#endif +default: +result=NULL; +ABORT_RET("GC_debug_realloc:encountered bad kind"); +} +if (result!=NULL){ +size_t old_sz; +#ifdef SHORT_DBG_HDRS +old_sz=GC_size(base)- sizeof(oh); +#else +old_sz=((oh*)base)->oh_sz; +#endif +if (old_sz > 0) +BCOPY(p,result,old_sz < lb?old_sz:lb); +GC_debug_free(p); +} +return(result); +} +GC_API GC_ATTR_MALLOC void*GC_CALL +GC_debug_generic_or_special_malloc(size_t lb,int knd,GC_EXTRA_PARAMS) +{ +switch (knd){ +case PTRFREE: +return GC_debug_malloc_atomic(lb,OPT_RA s,i); +case NORMAL: +return GC_debug_malloc(lb,OPT_RA s,i); +case UNCOLLECTABLE: +return GC_debug_malloc_uncollectable(lb,OPT_RA s,i); +#ifdef GC_ATOMIC_UNCOLLECTABLE +case AUNCOLLECTABLE: +return GC_debug_malloc_atomic_uncollectable(lb,OPT_RA s,i); +#endif +default: +return GC_debug_generic_malloc(lb,knd,OPT_RA s,i); +} +} +#ifndef SHORT_DBG_HDRS +#ifndef MAX_SMASHED +#define MAX_SMASHED 20 +#endif +STATIC ptr_t GC_smashed[MAX_SMASHED]={0}; +STATIC unsigned GC_n_smashed=0; +STATIC void GC_add_smashed(ptr_t smashed) +{ +GC_ASSERT(GC_is_marked(GC_base(smashed))); +GC_smashed[GC_n_smashed]=smashed; +if (GC_n_smashed < MAX_SMASHED - 1)++GC_n_smashed; +GC_have_errors=TRUE; +} +STATIC void GC_print_all_smashed_proc(void) +{ +unsigned i; +GC_ASSERT(I_DONT_HOLD_LOCK()); +if (GC_n_smashed==0)return; +GC_err_printf("GC_check_heap_block:found %u smashed heap objects:\n", +GC_n_smashed); +for (i=0;i < GC_n_smashed;++i){ +ptr_t base=(ptr_t)GC_base(GC_smashed[i]); +#ifdef LINT2 +if (!base)ABORT("Invalid GC_smashed element"); +#endif +GC_print_smashed_obj("",base+sizeof(oh),GC_smashed[i]); +GC_smashed[i]=0; +} +GC_n_smashed=0; +} +STATIC void GC_check_heap_block(struct hblk*hbp,word dummy GC_ATTR_UNUSED) +{ +struct hblkhdr*hhdr=HDR(hbp); +word sz=hhdr->hb_sz; +word bit_no; +char*p,*plim; +p=hbp->hb_body; +if (sz > MAXOBJBYTES){ +plim=p; +} else { +plim=hbp->hb_body+HBLKSIZE - sz; +} +for (bit_no=0;(word)p<=(word)plim; +bit_no+=MARK_BIT_OFFSET(sz),p+=sz){ +if (mark_bit_from_hdr(hhdr,bit_no)&&GC_HAS_DEBUG_INFO((ptr_t)p)){ +ptr_t clobbered=GC_check_annotated_obj((oh*)p); +if (clobbered!=0) +GC_add_smashed(clobbered); +} +} +} +STATIC void GC_check_heap_proc(void) +{ +GC_STATIC_ASSERT((sizeof(oh)&(GRANULE_BYTES - 1))==0); +GC_apply_to_all_blocks(GC_check_heap_block,0); +} +GC_INNER GC_bool GC_check_leaked(ptr_t base) +{ +word i; +word obj_sz; +word*p; +if ( +#if defined(KEEP_BACK_PTRS)||defined(MAKE_BACK_GRAPH) +(*(word*)base&1)!=0&& +#endif +GC_has_other_debug_info(base)>=0) +return TRUE; +p=(word*)(base+sizeof(oh)); +obj_sz=BYTES_TO_WORDS(HDR(base)->hb_sz - sizeof(oh)); +for (i=0;i < obj_sz;++i) +if (p[i]!=GC_FREED_MEM_MARKER){ +GC_set_mark_bit(base); +GC_add_smashed((ptr_t)(&p[i])); +break; +} +return FALSE; +} +#endif +#ifndef GC_NO_FINALIZATION +struct closure { +GC_finalization_proc cl_fn; +void*cl_data; +}; +STATIC void*GC_make_closure(GC_finalization_proc fn,void*data) +{ +struct closure*result= +#ifdef DBG_HDRS_ALL +(struct closure*)GC_debug_malloc(sizeof (struct closure), +GC_EXTRAS); +#else +(struct closure*)GC_malloc(sizeof (struct closure)); +#endif +if (result!=0){ +result->cl_fn=fn; +result->cl_data=data; +} +return((void*)result); +} +STATIC void GC_CALLBACK GC_debug_invoke_finalizer(void*obj,void*data) +{ +struct closure*cl=(struct closure*)data; +(*(cl->cl_fn))((void*)((char*)obj+sizeof(oh)),cl->cl_data); +} +#define OFN_UNSET ((GC_finalization_proc)~(signed_word)0) +static void store_old(void*obj,GC_finalization_proc my_old_fn, +struct closure*my_old_cd,GC_finalization_proc*ofn, +void**ocd) +{ +if (0!=my_old_fn){ +if (my_old_fn==OFN_UNSET){ +return; +} +if (my_old_fn!=GC_debug_invoke_finalizer){ +GC_err_printf("Debuggable object at %p had a non-debug finalizer\n", +obj); +} else { +if (ofn)*ofn=my_old_cd->cl_fn; +if (ocd)*ocd=my_old_cd->cl_data; +} +} else { +if (ofn)*ofn=0; +if (ocd)*ocd=0; +} +} +GC_API void GC_CALL GC_debug_register_finalizer(void*obj, +GC_finalization_proc fn, +void*cd,GC_finalization_proc*ofn, +void**ocd) +{ +GC_finalization_proc my_old_fn=OFN_UNSET; +void*my_old_cd; +ptr_t base=(ptr_t)GC_base(obj); +if (NULL==base){ +if (ocd)*ocd=0; +if (ofn)*ofn=0; +return; +} +if ((ptr_t)obj - base!=sizeof(oh)){ +GC_err_printf("GC_debug_register_finalizer called with" +" non-base-pointer %p\n",obj); +} +if (0==fn){ +GC_register_finalizer(base,0,0,&my_old_fn,&my_old_cd); +} else { +cd=GC_make_closure(fn,cd); +if (cd==0)return; +GC_register_finalizer(base,GC_debug_invoke_finalizer, +cd,&my_old_fn,&my_old_cd); +} +store_old(obj,my_old_fn,(struct closure*)my_old_cd,ofn,ocd); +} +GC_API void GC_CALL GC_debug_register_finalizer_no_order +(void*obj,GC_finalization_proc fn, +void*cd,GC_finalization_proc*ofn, +void**ocd) +{ +GC_finalization_proc my_old_fn=OFN_UNSET; +void*my_old_cd; +ptr_t base=(ptr_t)GC_base(obj); +if (NULL==base){ +if (ocd)*ocd=0; +if (ofn)*ofn=0; +return; +} +if ((ptr_t)obj - base!=sizeof(oh)){ +GC_err_printf("GC_debug_register_finalizer_no_order called with" +" non-base-pointer %p\n",obj); +} +if (0==fn){ +GC_register_finalizer_no_order(base,0,0,&my_old_fn,&my_old_cd); +} else { +cd=GC_make_closure(fn,cd); +if (cd==0)return; +GC_register_finalizer_no_order(base,GC_debug_invoke_finalizer, +cd,&my_old_fn,&my_old_cd); +} +store_old(obj,my_old_fn,(struct closure*)my_old_cd,ofn,ocd); +} +GC_API void GC_CALL GC_debug_register_finalizer_unreachable +(void*obj,GC_finalization_proc fn, +void*cd,GC_finalization_proc*ofn, +void**ocd) +{ +GC_finalization_proc my_old_fn=OFN_UNSET; +void*my_old_cd; +ptr_t base=(ptr_t)GC_base(obj); +if (NULL==base){ +if (ocd)*ocd=0; +if (ofn)*ofn=0; +return; +} +if ((ptr_t)obj - base!=sizeof(oh)){ +GC_err_printf("GC_debug_register_finalizer_unreachable called with" +" non-base-pointer %p\n",obj); +} +if (0==fn){ +GC_register_finalizer_unreachable(base,0,0,&my_old_fn,&my_old_cd); +} else { +cd=GC_make_closure(fn,cd); +if (cd==0)return; +GC_register_finalizer_unreachable(base,GC_debug_invoke_finalizer, +cd,&my_old_fn,&my_old_cd); +} +store_old(obj,my_old_fn,(struct closure*)my_old_cd,ofn,ocd); +} +GC_API void GC_CALL GC_debug_register_finalizer_ignore_self +(void*obj,GC_finalization_proc fn, +void*cd,GC_finalization_proc*ofn, +void**ocd) +{ +GC_finalization_proc my_old_fn=OFN_UNSET; +void*my_old_cd; +ptr_t base=(ptr_t)GC_base(obj); +if (NULL==base){ +if (ocd)*ocd=0; +if (ofn)*ofn=0; +return; +} +if ((ptr_t)obj - base!=sizeof(oh)){ +GC_err_printf("GC_debug_register_finalizer_ignore_self called with" +" non-base-pointer %p\n",obj); +} +if (0==fn){ +GC_register_finalizer_ignore_self(base,0,0,&my_old_fn,&my_old_cd); +} else { +cd=GC_make_closure(fn,cd); +if (cd==0)return; +GC_register_finalizer_ignore_self(base,GC_debug_invoke_finalizer, +cd,&my_old_fn,&my_old_cd); +} +store_old(obj,my_old_fn,(struct closure*)my_old_cd,ofn,ocd); +} +#endif +GC_API GC_ATTR_MALLOC void*GC_CALL GC_debug_malloc_replacement(size_t lb) +{ +return GC_debug_malloc(lb,GC_DBG_EXTRAS); +} +GC_API void*GC_CALL GC_debug_realloc_replacement(void*p,size_t lb) +{ +return GC_debug_realloc(p,lb,GC_DBG_EXTRAS); +} +#ifndef GC_NO_FINALIZATION +#ifndef GC_JAVAXFC_H +#define GC_JAVAXFC_H +#ifndef GC_H +#endif +#ifdef __cplusplus +extern "C" { +#endif +GC_API void GC_CALL GC_finalize_all(void); +#ifdef GC_THREADS +#ifndef GC_SUSPEND_THREAD_ID +#define GC_SUSPEND_THREAD_ID void* +#endif +GC_API void GC_CALL GC_suspend_thread(GC_SUSPEND_THREAD_ID); +GC_API void GC_CALL GC_resume_thread(GC_SUSPEND_THREAD_ID); +GC_API int GC_CALL GC_is_thread_suspended(GC_SUSPEND_THREAD_ID); +#endif +#ifdef __cplusplus +} +#endif +#endif +typedef void (*finalization_mark_proc)(ptr_t); +#define HASH3(addr,size,log_size)((((word)(addr)>>3)^((word)(addr)>>(3+(log_size))))&((size)- 1)) +#define HASH2(addr,log_size)HASH3(addr,(word)1<<(log_size),log_size) +struct hash_chain_entry { +word hidden_key; +struct hash_chain_entry*next; +}; +struct disappearing_link { +struct hash_chain_entry prolog; +#define dl_hidden_link prolog.hidden_key +#define dl_next(x)(struct disappearing_link*)((x)->prolog.next) +#define dl_set_next(x,y)(void)((x)->prolog.next=(struct hash_chain_entry*)(y)) +word dl_hidden_obj; +}; +struct finalizable_object { +struct hash_chain_entry prolog; +#define fo_hidden_base prolog.hidden_key +#define fo_next(x)(struct finalizable_object*)((x)->prolog.next) +#define fo_set_next(x,y)((x)->prolog.next=(struct hash_chain_entry*)(y)) +GC_finalization_proc fo_fn; +ptr_t fo_client_data; +word fo_object_size; +finalization_mark_proc fo_mark_proc; +}; +#ifdef AO_HAVE_store +#define SET_FINALIZE_NOW(fo)AO_store((volatile AO_t*)&GC_fnlz_roots.finalize_now,(AO_t)(fo)) +#else +#define SET_FINALIZE_NOW(fo)(void)(GC_fnlz_roots.finalize_now=(fo)) +#endif +GC_API void GC_CALL GC_push_finalizer_structures(void) +{ +GC_ASSERT((word)(&GC_dl_hashtbl.head)% sizeof(word)==0); +GC_ASSERT((word)(&GC_fnlz_roots)% sizeof(word)==0); +#ifndef GC_LONG_REFS_NOT_NEEDED +GC_ASSERT((word)(&GC_ll_hashtbl.head)% sizeof(word)==0); +GC_PUSH_ALL_SYM(GC_ll_hashtbl.head); +#endif +GC_PUSH_ALL_SYM(GC_dl_hashtbl.head); +GC_PUSH_ALL_SYM(GC_fnlz_roots); +} +#ifndef GC_ON_GROW_LOG_SIZE_MIN +#define GC_ON_GROW_LOG_SIZE_MIN CPP_LOG_HBLKSIZE +#endif +STATIC void GC_grow_table(struct hash_chain_entry***table, +unsigned*log_size_ptr,word*entries_ptr) +{ +word i; +struct hash_chain_entry*p; +unsigned log_old_size=*log_size_ptr; +unsigned log_new_size=log_old_size+1; +word old_size=*table==NULL?0:(word)1<=GC_ON_GROW_LOG_SIZE_MIN&&!GC_incremental){ +IF_CANCEL(int cancel_state;) +DISABLE_CANCEL(cancel_state); +(void)GC_try_to_collect_inner(GC_never_stop_func); +RESTORE_CANCEL(cancel_state); +if (*entries_ptr < ((word)1<>2)) +return; +} +new_table=(struct hash_chain_entry**) +GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE( +(size_t)new_size*sizeof(struct hash_chain_entry*), +NORMAL); +if (new_table==0){ +if (*table==0){ +ABORT("Insufficient space for initial table allocation"); +} else { +return; +} +} +for (i=0;i < old_size;i++){ +p=(*table)[i]; +while (p!=0){ +ptr_t real_key=(ptr_t)GC_REVEAL_POINTER(p->hidden_key); +struct hash_chain_entry*next=p->next; +size_t new_hash=HASH3(real_key,new_size,log_new_size); +p->next=new_table[new_hash]; +GC_dirty(p); +new_table[new_hash]=p; +p=next; +} +} +*log_size_ptr=log_new_size; +*table=new_table; +GC_dirty(new_table); +} +GC_API int GC_CALL GC_register_disappearing_link(void**link) +{ +ptr_t base; +base=(ptr_t)GC_base(link); +if (base==0) +ABORT("Bad arg to GC_register_disappearing_link"); +return(GC_general_register_disappearing_link(link,base)); +} +STATIC int GC_register_disappearing_link_inner( +struct dl_hashtbl_s*dl_hashtbl,void**link, +const void*obj,const char*tbl_log_name) +{ +struct disappearing_link*curr_dl; +size_t index; +struct disappearing_link*new_dl; +DCL_LOCK_STATE; +if (EXPECT(GC_find_leak,FALSE))return GC_UNIMPLEMENTED; +LOCK(); +GC_ASSERT(obj!=NULL&&GC_base_C(obj)==obj); +if (EXPECT(NULL==dl_hashtbl->head,FALSE) +||EXPECT(dl_hashtbl->entries +> ((word)1<log_size),FALSE)){ +GC_grow_table((struct hash_chain_entry***)&dl_hashtbl->head, +&dl_hashtbl->log_size,&dl_hashtbl->entries); +GC_COND_LOG_PRINTF("Grew %s table to %u entries\n",tbl_log_name, +1U<log_size); +} +index=HASH2(link,dl_hashtbl->log_size); +for (curr_dl=dl_hashtbl->head[index];curr_dl!=0; +curr_dl=dl_next(curr_dl)){ +if (curr_dl->dl_hidden_link==GC_HIDE_POINTER(link)){ +curr_dl->dl_hidden_obj=GC_HIDE_POINTER(obj); +UNLOCK(); +return GC_DUPLICATE; +} +} +new_dl=(struct disappearing_link*) +GC_INTERNAL_MALLOC(sizeof(struct disappearing_link),NORMAL); +if (0==new_dl){ +GC_oom_func oom_fn=GC_oom_fn; +UNLOCK(); +new_dl=(struct disappearing_link*) +(*oom_fn)(sizeof(struct disappearing_link)); +if (0==new_dl){ +return GC_NO_MEMORY; +} +LOCK(); +index=HASH2(link,dl_hashtbl->log_size); +for (curr_dl=dl_hashtbl->head[index];curr_dl!=0; +curr_dl=dl_next(curr_dl)){ +if (curr_dl->dl_hidden_link==GC_HIDE_POINTER(link)){ +curr_dl->dl_hidden_obj=GC_HIDE_POINTER(obj); +UNLOCK(); +#ifndef DBG_HDRS_ALL +GC_free((void*)new_dl); +#endif +return GC_DUPLICATE; +} +} +} +new_dl->dl_hidden_obj=GC_HIDE_POINTER(obj); +new_dl->dl_hidden_link=GC_HIDE_POINTER(link); +dl_set_next(new_dl,dl_hashtbl->head[index]); +GC_dirty(new_dl); +dl_hashtbl->head[index]=new_dl; +dl_hashtbl->entries++; +GC_dirty(dl_hashtbl->head+index); +UNLOCK(); +return GC_SUCCESS; +} +GC_API int GC_CALL GC_general_register_disappearing_link(void**link, +const void*obj) +{ +if (((word)link&(ALIGNMENT-1))!=0||!NONNULL_ARG_NOT_NULL(link)) +ABORT("Bad arg to GC_general_register_disappearing_link"); +return GC_register_disappearing_link_inner(&GC_dl_hashtbl,link,obj, +"dl"); +} +#ifdef DBG_HDRS_ALL +#define FREE_DL_ENTRY(curr_dl)dl_set_next(curr_dl,NULL) +#else +#define FREE_DL_ENTRY(curr_dl)GC_free(curr_dl) +#endif +GC_INLINE struct disappearing_link*GC_unregister_disappearing_link_inner( +struct dl_hashtbl_s*dl_hashtbl,void**link) +{ +struct disappearing_link*curr_dl; +struct disappearing_link*prev_dl=NULL; +size_t index; +GC_ASSERT(I_HOLD_LOCK()); +if (EXPECT(NULL==dl_hashtbl->head,FALSE))return NULL; +index=HASH2(link,dl_hashtbl->log_size); +for (curr_dl=dl_hashtbl->head[index];curr_dl; +curr_dl=dl_next(curr_dl)){ +if (curr_dl->dl_hidden_link==GC_HIDE_POINTER(link)){ +if (NULL==prev_dl){ +dl_hashtbl->head[index]=dl_next(curr_dl); +GC_dirty(dl_hashtbl->head+index); +} else { +dl_set_next(prev_dl,dl_next(curr_dl)); +GC_dirty(prev_dl); +} +dl_hashtbl->entries--; +break; +} +prev_dl=curr_dl; +} +return curr_dl; +} +GC_API int GC_CALL GC_unregister_disappearing_link(void**link) +{ +struct disappearing_link*curr_dl; +DCL_LOCK_STATE; +if (((word)link&(ALIGNMENT-1))!=0)return(0); +LOCK(); +curr_dl=GC_unregister_disappearing_link_inner(&GC_dl_hashtbl,link); +UNLOCK(); +if (NULL==curr_dl)return 0; +FREE_DL_ENTRY(curr_dl); +return 1; +} +#ifndef GC_TOGGLE_REFS_NOT_NEEDED +typedef union toggle_ref_u GCToggleRef; +STATIC GC_toggleref_func GC_toggleref_callback=0; +GC_INNER void GC_process_togglerefs(void) +{ +size_t i; +size_t new_size=0; +GC_bool needs_barrier=FALSE; +GC_ASSERT(I_HOLD_LOCK()); +for (i=0;i < GC_toggleref_array_size;++i){ +GCToggleRef r=GC_toggleref_arr[i]; +void*obj=r.strong_ref; +if (((word)obj&1)!=0){ +obj=GC_REVEAL_POINTER(r.weak_ref); +} +if (NULL==obj){ +continue; +} +switch (GC_toggleref_callback(obj)){ +case GC_TOGGLE_REF_DROP: +break; +case GC_TOGGLE_REF_STRONG: +GC_toggleref_arr[new_size++].strong_ref=obj; +needs_barrier=TRUE; +break; +case GC_TOGGLE_REF_WEAK: +GC_toggleref_arr[new_size++].weak_ref=GC_HIDE_POINTER(obj); +break; +default: +ABORT("Bad toggle-ref status returned by callback"); +} +} +if (new_size < GC_toggleref_array_size){ +BZERO(&GC_toggleref_arr[new_size], +(GC_toggleref_array_size - new_size)*sizeof(GCToggleRef)); +GC_toggleref_array_size=new_size; +} +if (needs_barrier) +GC_dirty(GC_toggleref_arr); +} +STATIC void GC_normal_finalize_mark_proc(ptr_t); +static void push_and_mark_object(void*p) +{ +GC_normal_finalize_mark_proc((ptr_t)p); +while (!GC_mark_stack_empty()){ +MARK_FROM_MARK_STACK(); +} +GC_set_mark_bit(p); +if (GC_mark_state!=MS_NONE){ +while (!GC_mark_some(0)){ +} +} +} +STATIC void GC_mark_togglerefs(void) +{ +size_t i; +if (NULL==GC_toggleref_arr) +return; +GC_set_mark_bit(GC_toggleref_arr); +for (i=0;i < GC_toggleref_array_size;++i){ +void*obj=GC_toggleref_arr[i].strong_ref; +if (obj!=NULL&&((word)obj&1)==0){ +push_and_mark_object(obj); +} +} +} +STATIC void GC_clear_togglerefs(void) +{ +size_t i; +for (i=0;i < GC_toggleref_array_size;++i){ +if ((GC_toggleref_arr[i].weak_ref&1)!=0){ +if (!GC_is_marked(GC_REVEAL_POINTER(GC_toggleref_arr[i].weak_ref))){ +GC_toggleref_arr[i].weak_ref=0; +} else { +} +} +} +} +GC_API void GC_CALL GC_set_toggleref_func(GC_toggleref_func fn) +{ +DCL_LOCK_STATE; +LOCK(); +GC_toggleref_callback=fn; +UNLOCK(); +} +GC_API GC_toggleref_func GC_CALL GC_get_toggleref_func(void) +{ +GC_toggleref_func fn; +DCL_LOCK_STATE; +LOCK(); +fn=GC_toggleref_callback; +UNLOCK(); +return fn; +} +static GC_bool ensure_toggleref_capacity(size_t capacity_inc) +{ +GC_ASSERT(I_HOLD_LOCK()); +if (NULL==GC_toggleref_arr){ +GC_toggleref_array_capacity=32; +GC_toggleref_arr=(GCToggleRef*)GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE( +GC_toggleref_array_capacity*sizeof(GCToggleRef), +NORMAL); +if (NULL==GC_toggleref_arr) +return FALSE; +} +if (GC_toggleref_array_size+capacity_inc +>=GC_toggleref_array_capacity){ +GCToggleRef*new_array; +while (GC_toggleref_array_capacity +< GC_toggleref_array_size+capacity_inc){ +GC_toggleref_array_capacity*=2; +if ((GC_toggleref_array_capacity +&((size_t)1<<(sizeof(size_t)*8 - 1)))!=0) +return FALSE; +} +new_array=(GCToggleRef*)GC_INTERNAL_MALLOC_IGNORE_OFF_PAGE( +GC_toggleref_array_capacity*sizeof(GCToggleRef), +NORMAL); +if (NULL==new_array) +return FALSE; +if (EXPECT(GC_toggleref_array_size > 0,TRUE)) +BCOPY(GC_toggleref_arr,new_array, +GC_toggleref_array_size*sizeof(GCToggleRef)); +GC_INTERNAL_FREE(GC_toggleref_arr); +GC_toggleref_arr=new_array; +} +return TRUE; +} +GC_API int GC_CALL GC_toggleref_add(void*obj,int is_strong_ref) +{ +int res=GC_SUCCESS; +DCL_LOCK_STATE; +GC_ASSERT(NONNULL_ARG_NOT_NULL(obj)); +LOCK(); +if (GC_toggleref_callback!=0){ +if (!ensure_toggleref_capacity(1)){ +res=GC_NO_MEMORY; +} else { +GC_toggleref_arr[GC_toggleref_array_size].strong_ref= +is_strong_ref?obj:(void*)GC_HIDE_POINTER(obj); +if (is_strong_ref) +GC_dirty(GC_toggleref_arr+GC_toggleref_array_size); +GC_toggleref_array_size++; +} +} +UNLOCK(); +return res; +} +#endif +STATIC GC_await_finalize_proc GC_object_finalized_proc=0; +GC_API void GC_CALL GC_set_await_finalize_proc(GC_await_finalize_proc fn) +{ +DCL_LOCK_STATE; +LOCK(); +GC_object_finalized_proc=fn; +UNLOCK(); +} +GC_API GC_await_finalize_proc GC_CALL GC_get_await_finalize_proc(void) +{ +GC_await_finalize_proc fn; +DCL_LOCK_STATE; +LOCK(); +fn=GC_object_finalized_proc; +UNLOCK(); +return fn; +} +#ifndef GC_LONG_REFS_NOT_NEEDED +GC_API int GC_CALL GC_register_long_link(void**link,const void*obj) +{ +if (((word)link&(ALIGNMENT-1))!=0||!NONNULL_ARG_NOT_NULL(link)) +ABORT("Bad arg to GC_register_long_link"); +return GC_register_disappearing_link_inner(&GC_ll_hashtbl,link,obj, +"long dl"); +} +GC_API int GC_CALL GC_unregister_long_link(void**link) +{ +struct disappearing_link*curr_dl; +DCL_LOCK_STATE; +if (((word)link&(ALIGNMENT-1))!=0)return(0); +LOCK(); +curr_dl=GC_unregister_disappearing_link_inner(&GC_ll_hashtbl,link); +UNLOCK(); +if (NULL==curr_dl)return 0; +FREE_DL_ENTRY(curr_dl); +return 1; +} +#endif +#ifndef GC_MOVE_DISAPPEARING_LINK_NOT_NEEDED +STATIC int GC_move_disappearing_link_inner( +struct dl_hashtbl_s*dl_hashtbl, +void**link,void**new_link) +{ +struct disappearing_link*curr_dl,*new_dl; +struct disappearing_link*prev_dl=NULL; +size_t curr_index,new_index; +word curr_hidden_link,new_hidden_link; +GC_ASSERT(I_HOLD_LOCK()); +if (EXPECT(NULL==dl_hashtbl->head,FALSE))return GC_NOT_FOUND; +curr_index=HASH2(link,dl_hashtbl->log_size); +curr_hidden_link=GC_HIDE_POINTER(link); +for (curr_dl=dl_hashtbl->head[curr_index];curr_dl; +curr_dl=dl_next(curr_dl)){ +if (curr_dl->dl_hidden_link==curr_hidden_link) +break; +prev_dl=curr_dl; +} +if (EXPECT(NULL==curr_dl,FALSE)){ +return GC_NOT_FOUND; +} else if (link==new_link){ +return GC_SUCCESS; +} +new_index=HASH2(new_link,dl_hashtbl->log_size); +new_hidden_link=GC_HIDE_POINTER(new_link); +for (new_dl=dl_hashtbl->head[new_index];new_dl; +new_dl=dl_next(new_dl)){ +if (new_dl->dl_hidden_link==new_hidden_link){ +return GC_DUPLICATE; +} +} +if (NULL==prev_dl){ +dl_hashtbl->head[curr_index]=dl_next(curr_dl); +} else { +dl_set_next(prev_dl,dl_next(curr_dl)); +GC_dirty(prev_dl); +} +curr_dl->dl_hidden_link=new_hidden_link; +dl_set_next(curr_dl,dl_hashtbl->head[new_index]); +dl_hashtbl->head[new_index]=curr_dl; +GC_dirty(curr_dl); +GC_dirty(dl_hashtbl->head); +return GC_SUCCESS; +} +GC_API int GC_CALL GC_move_disappearing_link(void**link,void**new_link) +{ +int result; +DCL_LOCK_STATE; +if (((word)new_link&(ALIGNMENT-1))!=0 +||!NONNULL_ARG_NOT_NULL(new_link)) +ABORT("Bad new_link arg to GC_move_disappearing_link"); +if (((word)link&(ALIGNMENT-1))!=0) +return GC_NOT_FOUND; +LOCK(); +result=GC_move_disappearing_link_inner(&GC_dl_hashtbl,link,new_link); +UNLOCK(); +return result; +} +#ifndef GC_LONG_REFS_NOT_NEEDED +GC_API int GC_CALL GC_move_long_link(void**link,void**new_link) +{ +int result; +DCL_LOCK_STATE; +if (((word)new_link&(ALIGNMENT-1))!=0 +||!NONNULL_ARG_NOT_NULL(new_link)) +ABORT("Bad new_link arg to GC_move_long_link"); +if (((word)link&(ALIGNMENT-1))!=0) +return GC_NOT_FOUND; +LOCK(); +result=GC_move_disappearing_link_inner(&GC_ll_hashtbl,link,new_link); +UNLOCK(); +return result; +} +#endif +#endif +STATIC void GC_normal_finalize_mark_proc(ptr_t p) +{ +#if defined(_MSC_VER)&&defined(I386) +hdr*hhdr=HDR(p); +#define mark_stack_top GC_mark_stack_top +mse*mark_stack_limit=GC_mark_stack+GC_mark_stack_size; +word descr=hhdr->hb_descr; +if (descr!=0){ +mark_stack_top++; +if ((word)mark_stack_top>=(word)mark_stack_limit){ +mark_stack_top=GC_signal_mark_stack_overflow(mark_stack_top); +} +mark_stack_top->mse_start=p; +mark_stack_top->mse_descr.w=descr; +} +#undef mark_stack_top +#else +GC_mark_stack_top=GC_push_obj(p,HDR(p),GC_mark_stack_top, +GC_mark_stack+GC_mark_stack_size); +#endif +} +STATIC void GC_ignore_self_finalize_mark_proc(ptr_t p) +{ +hdr*hhdr=HDR(p); +word descr=hhdr->hb_descr; +ptr_t q; +ptr_t scan_limit; +ptr_t target_limit=p+hhdr->hb_sz - 1; +if ((descr&GC_DS_TAGS)==GC_DS_LENGTH){ +scan_limit=p+descr - sizeof(word); +} else { +scan_limit=target_limit+1 - sizeof(word); +} +for (q=p;(word)q<=(word)scan_limit;q+=ALIGNMENT){ +word r=*(word*)q; +if (r < (word)p||r > (word)target_limit){ +GC_PUSH_ONE_HEAP(r,q,GC_mark_stack_top); +} +} +} +STATIC void GC_null_finalize_mark_proc(ptr_t p GC_ATTR_UNUSED){} +STATIC void GC_unreachable_finalize_mark_proc(ptr_t p) +{ +GC_normal_finalize_mark_proc(p); +} +STATIC void GC_register_finalizer_inner(void*obj, +GC_finalization_proc fn,void*cd, +GC_finalization_proc*ofn,void**ocd, +finalization_mark_proc mp) +{ +struct finalizable_object*curr_fo; +size_t index; +struct finalizable_object*new_fo=0; +hdr*hhdr=NULL; +DCL_LOCK_STATE; +if (EXPECT(GC_find_leak,FALSE))return; +LOCK(); +if (EXPECT(NULL==GC_fnlz_roots.fo_head,FALSE) +||EXPECT(GC_fo_entries > ((word)1<=sizeof(struct finalizable_object)); +if (curr_fo->fo_hidden_base==GC_HIDE_POINTER(obj)){ +if (ocd)*ocd=(void*)(curr_fo->fo_client_data); +if (ofn)*ofn=curr_fo->fo_fn; +if (prev_fo==0){ +GC_fnlz_roots.fo_head[index]=fo_next(curr_fo); +} else { +fo_set_next(prev_fo,fo_next(curr_fo)); +GC_dirty(prev_fo); +} +if (fn==0){ +GC_fo_entries--; +#if!defined(THREADS)&&!defined(DBG_HDRS_ALL) +GC_free((void*)curr_fo); +#endif +} else { +curr_fo->fo_fn=fn; +curr_fo->fo_client_data=(ptr_t)cd; +curr_fo->fo_mark_proc=mp; +GC_dirty(curr_fo); +if (prev_fo==0){ +GC_fnlz_roots.fo_head[index]=curr_fo; +} else { +fo_set_next(prev_fo,curr_fo); +GC_dirty(prev_fo); +} +} +if (NULL==prev_fo) +GC_dirty(GC_fnlz_roots.fo_head+index); +UNLOCK(); +#ifndef DBG_HDRS_ALL +GC_free((void*)new_fo); +#endif +return; +} +prev_fo=curr_fo; +curr_fo=fo_next(curr_fo); +} +if (EXPECT(new_fo!=0,FALSE)){ +GC_ASSERT(fn!=0); +#ifdef LINT2 +if (NULL==hhdr)ABORT("Bad hhdr in GC_register_finalizer_inner"); +#endif +break; +} +if (fn==0){ +if (ocd)*ocd=0; +if (ofn)*ofn=0; +UNLOCK(); +return; +} +GET_HDR(obj,hhdr); +if (EXPECT(0==hhdr,FALSE)){ +if (ocd)*ocd=0; +if (ofn)*ofn=0; +UNLOCK(); +return; +} +new_fo=(struct finalizable_object*) +GC_INTERNAL_MALLOC(sizeof(struct finalizable_object),NORMAL); +if (EXPECT(new_fo!=0,TRUE)) +break; +oom_fn=GC_oom_fn; +UNLOCK(); +new_fo=(struct finalizable_object*) +(*oom_fn)(sizeof(struct finalizable_object)); +if (0==new_fo){ +return; +} +LOCK(); +} +GC_ASSERT(GC_size(new_fo)>=sizeof(struct finalizable_object)); +if (ocd)*ocd=0; +if (ofn)*ofn=0; +new_fo->fo_hidden_base=GC_HIDE_POINTER(obj); +new_fo->fo_fn=fn; +new_fo->fo_client_data=(ptr_t)cd; +new_fo->fo_object_size=hhdr->hb_sz; +new_fo->fo_mark_proc=mp; +fo_set_next(new_fo,GC_fnlz_roots.fo_head[index]); +GC_dirty(new_fo); +GC_fo_entries++; +GC_fnlz_roots.fo_head[index]=new_fo; +GC_dirty(GC_fnlz_roots.fo_head+index); +UNLOCK(); +} +GC_API void GC_CALL GC_register_finalizer(void*obj, +GC_finalization_proc fn,void*cd, +GC_finalization_proc*ofn,void**ocd) +{ +GC_register_finalizer_inner(obj,fn,cd,ofn, +ocd,GC_normal_finalize_mark_proc); +} +GC_API void GC_CALL GC_register_finalizer_ignore_self(void*obj, +GC_finalization_proc fn,void*cd, +GC_finalization_proc*ofn,void**ocd) +{ +GC_register_finalizer_inner(obj,fn,cd,ofn, +ocd,GC_ignore_self_finalize_mark_proc); +} +GC_API void GC_CALL GC_register_finalizer_no_order(void*obj, +GC_finalization_proc fn,void*cd, +GC_finalization_proc*ofn,void**ocd) +{ +GC_register_finalizer_inner(obj,fn,cd,ofn, +ocd,GC_null_finalize_mark_proc); +} +static GC_bool need_unreachable_finalization=FALSE; +GC_API void GC_CALL GC_register_finalizer_unreachable(void*obj, +GC_finalization_proc fn,void*cd, +GC_finalization_proc*ofn,void**ocd) +{ +need_unreachable_finalization=TRUE; +GC_ASSERT(GC_java_finalization); +GC_register_finalizer_inner(obj,fn,cd,ofn, +ocd,GC_unreachable_finalize_mark_proc); +} +#ifndef NO_DEBUGGING +STATIC void GC_dump_finalization_links( +const struct dl_hashtbl_s*dl_hashtbl) +{ +size_t dl_size=(size_t)1<log_size; +size_t i; +if (NULL==dl_hashtbl->head)return; +for (i=0;i < dl_size;i++){ +struct disappearing_link*curr_dl; +for (curr_dl=dl_hashtbl->head[i];curr_dl!=0; +curr_dl=dl_next(curr_dl)){ +ptr_t real_ptr=(ptr_t)GC_REVEAL_POINTER(curr_dl->dl_hidden_obj); +ptr_t real_link=(ptr_t)GC_REVEAL_POINTER(curr_dl->dl_hidden_link); +GC_printf("Object:%p,link:%p\n", +(void*)real_ptr,(void*)real_link); +} +} +} +GC_API void GC_CALL GC_dump_finalization(void) +{ +struct finalizable_object*curr_fo; +size_t i; +size_t fo_size=GC_fnlz_roots.fo_head==NULL?0: +(size_t)1<fo_hidden_base); +GC_printf("Finalizable object:%p\n",(void*)real_ptr); +} +} +} +#endif +#ifndef SMALL_CONFIG +STATIC word GC_old_dl_entries=0; +#ifndef GC_LONG_REFS_NOT_NEEDED +STATIC word GC_old_ll_entries=0; +#endif +#endif +#ifndef THREADS +STATIC int GC_finalizer_nested=0; +STATIC unsigned GC_finalizer_skipped=0; +STATIC unsigned char*GC_check_finalizer_nested(void) +{ +unsigned nesting_level=*(unsigned char*)&GC_finalizer_nested; +if (nesting_level){ +if (++GC_finalizer_skipped < (1U<log_size; +GC_bool needs_barrier=FALSE; +GC_ASSERT(I_HOLD_LOCK()); +if (NULL==dl_hashtbl->head)return; +for (i=0;i < dl_size;i++){ +struct disappearing_link*curr_dl,*next_dl; +struct disappearing_link*prev_dl=NULL; +for (curr_dl=dl_hashtbl->head[i];curr_dl!=NULL;curr_dl=next_dl){ +next_dl=dl_next(curr_dl); +if (is_remove_dangling){ +ptr_t real_link=(ptr_t)GC_base(GC_REVEAL_POINTER( +curr_dl->dl_hidden_link)); +if (NULL==real_link||EXPECT(GC_is_marked(real_link),TRUE)){ +prev_dl=curr_dl; +continue; +} +} else { +if (EXPECT(GC_is_marked((ptr_t)GC_REVEAL_POINTER( +curr_dl->dl_hidden_obj)),TRUE)){ +prev_dl=curr_dl; +continue; +} +*(ptr_t*)GC_REVEAL_POINTER(curr_dl->dl_hidden_link)=NULL; +} +if (NULL==prev_dl){ +dl_hashtbl->head[i]=next_dl; +needs_barrier=TRUE; +} else { +dl_set_next(prev_dl,next_dl); +GC_dirty(prev_dl); +} +GC_clear_mark_bit(curr_dl); +dl_hashtbl->entries--; +} +} +if (needs_barrier) +GC_dirty(dl_hashtbl->head); +} +GC_INNER void GC_finalize(void) +{ +struct finalizable_object*curr_fo,*prev_fo,*next_fo; +ptr_t real_ptr; +size_t i; +size_t fo_size=GC_fnlz_roots.fo_head==NULL?0: +(size_t)1<=sizeof(struct finalizable_object)); +real_ptr=(ptr_t)GC_REVEAL_POINTER(curr_fo->fo_hidden_base); +if (!GC_is_marked(real_ptr)){ +GC_MARKED_FOR_FINALIZATION(real_ptr); +GC_MARK_FO(real_ptr,curr_fo->fo_mark_proc); +if (GC_is_marked(real_ptr)){ +WARN("Finalization cycle involving %p\n",real_ptr); +} +} +} +} +GC_bytes_finalized=0; +for (i=0;i < fo_size;i++){ +curr_fo=GC_fnlz_roots.fo_head[i]; +prev_fo=0; +while (curr_fo!=0){ +real_ptr=(ptr_t)GC_REVEAL_POINTER(curr_fo->fo_hidden_base); +if (!GC_is_marked(real_ptr)){ +if (!GC_java_finalization){ +GC_set_mark_bit(real_ptr); +} +next_fo=fo_next(curr_fo); +if (NULL==prev_fo){ +GC_fnlz_roots.fo_head[i]=next_fo; +if (GC_object_finalized_proc){ +GC_dirty(GC_fnlz_roots.fo_head+i); +} else { +needs_barrier=TRUE; +} +} else { +fo_set_next(prev_fo,next_fo); +GC_dirty(prev_fo); +} +GC_fo_entries--; +if (GC_object_finalized_proc) +GC_object_finalized_proc(real_ptr); +fo_set_next(curr_fo,GC_fnlz_roots.finalize_now); +GC_dirty(curr_fo); +SET_FINALIZE_NOW(curr_fo); +curr_fo->fo_hidden_base= +(word)GC_REVEAL_POINTER(curr_fo->fo_hidden_base); +GC_bytes_finalized+= +curr_fo->fo_object_size ++sizeof(struct finalizable_object); +GC_ASSERT(GC_is_marked(GC_base(curr_fo))); +curr_fo=next_fo; +} else { +prev_fo=curr_fo; +curr_fo=fo_next(curr_fo); +} +} +} +if (GC_java_finalization){ +for (curr_fo=GC_fnlz_roots.finalize_now; +curr_fo!=NULL;curr_fo=fo_next(curr_fo)){ +real_ptr=(ptr_t)curr_fo->fo_hidden_base; +if (!GC_is_marked(real_ptr)){ +if (curr_fo->fo_mark_proc==GC_null_finalize_mark_proc){ +GC_MARK_FO(real_ptr,GC_normal_finalize_mark_proc); +} +if (curr_fo->fo_mark_proc!=GC_unreachable_finalize_mark_proc){ +GC_set_mark_bit(real_ptr); +} +} +} +if (need_unreachable_finalization){ +curr_fo=GC_fnlz_roots.finalize_now; +GC_ASSERT(NULL==curr_fo||GC_fnlz_roots.fo_head!=NULL); +prev_fo=NULL; +while (curr_fo!=NULL){ +next_fo=fo_next(curr_fo); +if (curr_fo->fo_mark_proc==GC_unreachable_finalize_mark_proc){ +real_ptr=(ptr_t)curr_fo->fo_hidden_base; +if (!GC_is_marked(real_ptr)){ +GC_set_mark_bit(real_ptr); +} else { +if (NULL==prev_fo){ +SET_FINALIZE_NOW(next_fo); +} else { +fo_set_next(prev_fo,next_fo); +GC_dirty(prev_fo); +} +curr_fo->fo_hidden_base= +GC_HIDE_POINTER(curr_fo->fo_hidden_base); +GC_bytes_finalized-= +curr_fo->fo_object_size+sizeof(struct finalizable_object); +i=HASH2(real_ptr,GC_log_fo_table_size); +fo_set_next(curr_fo,GC_fnlz_roots.fo_head[i]); +GC_dirty(curr_fo); +GC_fo_entries++; +GC_fnlz_roots.fo_head[i]=curr_fo; +curr_fo=prev_fo; +needs_barrier=TRUE; +} +} +prev_fo=curr_fo; +curr_fo=next_fo; +} +} +} +if (needs_barrier) +GC_dirty(GC_fnlz_roots.fo_head); +GC_make_disappearing_links_disappear(&GC_dl_hashtbl,TRUE); +#ifndef GC_TOGGLE_REFS_NOT_NEEDED +GC_clear_togglerefs(); +#endif +#ifndef GC_LONG_REFS_NOT_NEEDED +GC_make_disappearing_links_disappear(&GC_ll_hashtbl,FALSE); +GC_make_disappearing_links_disappear(&GC_ll_hashtbl,TRUE); +#endif +if (GC_fail_count){ +#ifdef THREADS +GC_reset_finalizer_nested(); +#else +GC_finalizer_nested=0; +#endif +} +} +#ifndef JAVA_FINALIZATION_NOT_NEEDED +STATIC void GC_enqueue_all_finalizers(void) +{ +struct finalizable_object*next_fo; +size_t i; +size_t fo_size=GC_fnlz_roots.fo_head==NULL?0: +(size_t)1<fo_hidden_base); +GC_MARK_FO(real_ptr,GC_normal_finalize_mark_proc); +GC_set_mark_bit(real_ptr); +next_fo=fo_next(curr_fo); +fo_set_next(curr_fo,GC_fnlz_roots.finalize_now); +GC_dirty(curr_fo); +SET_FINALIZE_NOW(curr_fo); +curr_fo->fo_hidden_base= +(word)GC_REVEAL_POINTER(curr_fo->fo_hidden_base); +GC_bytes_finalized+= +curr_fo->fo_object_size+sizeof(struct finalizable_object); +curr_fo=next_fo; +} +} +GC_fo_entries=0; +} +GC_API void GC_CALL GC_finalize_all(void) +{ +DCL_LOCK_STATE; +LOCK(); +while (GC_fo_entries > 0){ +GC_enqueue_all_finalizers(); +UNLOCK(); +GC_invoke_finalizers(); +LOCK(); +} +UNLOCK(); +} +#endif +GC_API int GC_CALL GC_should_invoke_finalizers(void) +{ +#ifdef AO_HAVE_load +return AO_load((volatile AO_t*)&GC_fnlz_roots.finalize_now)!=0; +#else +return GC_fnlz_roots.finalize_now!=NULL; +#endif +} +GC_API int GC_CALL GC_invoke_finalizers(void) +{ +int count=0; +word bytes_freed_before=0; +DCL_LOCK_STATE; +while (GC_should_invoke_finalizers()){ +struct finalizable_object*curr_fo; +#ifdef THREADS +LOCK(); +#endif +if (count==0){ +bytes_freed_before=GC_bytes_freed; +} +curr_fo=GC_fnlz_roots.finalize_now; +#ifdef THREADS +if (curr_fo!=NULL) +SET_FINALIZE_NOW(fo_next(curr_fo)); +UNLOCK(); +if (curr_fo==0)break; +#else +GC_fnlz_roots.finalize_now=fo_next(curr_fo); +#endif +fo_set_next(curr_fo,0); +(*(curr_fo->fo_fn))((ptr_t)(curr_fo->fo_hidden_base), +curr_fo->fo_client_data); +curr_fo->fo_client_data=0; +++count; +} +if (count!=0 +#if defined(THREADS)&&!defined(THREAD_SANITIZER) +&&bytes_freed_before!=GC_bytes_freed +#endif +){ +LOCK(); +GC_finalizer_bytes_freed+=(GC_bytes_freed - bytes_freed_before); +UNLOCK(); +} +return count; +} +static word last_finalizer_notification=0; +GC_INNER void GC_notify_or_invoke_finalizers(void) +{ +GC_finalizer_notifier_proc notifier_fn=0; +#if defined(KEEP_BACK_PTRS)||defined(MAKE_BACK_GRAPH) +static word last_back_trace_gc_no=1; +#endif +DCL_LOCK_STATE; +#if defined(THREADS)&&!defined(KEEP_BACK_PTRS)&&!defined(MAKE_BACK_GRAPH) +if (!GC_should_invoke_finalizers()) +return; +#endif +LOCK(); +#if defined(KEEP_BACK_PTRS)||defined(MAKE_BACK_GRAPH) +if (GC_gc_no > last_back_trace_gc_no){ +#ifdef KEEP_BACK_PTRS +long i; +last_back_trace_gc_no=GC_WORD_MAX; +for (i=0;i < GC_backtraces;++i){ +UNLOCK(); +GC_generate_random_backtrace_no_gc(); +LOCK(); +} +last_back_trace_gc_no=GC_gc_no; +#endif +#ifdef MAKE_BACK_GRAPH +if (GC_print_back_height){ +GC_print_back_graph_stats(); +} +#endif +} +#endif +if (NULL==GC_fnlz_roots.finalize_now){ +UNLOCK(); +return; +} +if (!GC_finalize_on_demand){ +unsigned char*pnested=GC_check_finalizer_nested(); +UNLOCK(); +if (pnested!=NULL){ +(void)GC_invoke_finalizers(); +*pnested=0; +#ifndef THREADS +GC_ASSERT(NULL==GC_fnlz_roots.finalize_now); +#endif +} +return; +} +if (last_finalizer_notification!=GC_gc_no){ +notifier_fn=GC_finalizer_notifier; +last_finalizer_notification=GC_gc_no; +} +UNLOCK(); +if (notifier_fn!=0) +(*notifier_fn)(); +} +#ifndef SMALL_CONFIG +#ifndef GC_LONG_REFS_NOT_NEEDED +#define IF_LONG_REFS_PRESENT_ELSE(x,y)(x) +#else +#define IF_LONG_REFS_PRESENT_ELSE(x,y)(y) +#endif +GC_INNER void GC_print_finalization_stats(void) +{ +struct finalizable_object*fo; +unsigned long ready=0; +GC_log_printf("%lu finalization entries;" +" %lu/%lu short/long disappearing links alive\n", +(unsigned long)GC_fo_entries, +(unsigned long)GC_dl_hashtbl.entries, +(unsigned long)IF_LONG_REFS_PRESENT_ELSE( +GC_ll_hashtbl.entries,0)); +for (fo=GC_fnlz_roots.finalize_now;fo!=NULL;fo=fo_next(fo)) +++ready; +GC_log_printf("%lu finalization-ready objects;" +" %ld/%ld short/long links cleared\n", +ready, +(long)GC_old_dl_entries - (long)GC_dl_hashtbl.entries, +(long)IF_LONG_REFS_PRESENT_ELSE( +GC_old_ll_entries - GC_ll_hashtbl.entries,0)); +} +#endif +#endif +#ifdef ENABLE_DISCLAIM +#ifndef GC_DISCLAIM_H +#define GC_DISCLAIM_H +#ifdef __cplusplus +extern "C" { +#endif +GC_API void GC_CALL GC_init_finalized_malloc(void); +typedef int (GC_CALLBACK*GC_disclaim_proc)(void*); +GC_API void GC_CALL GC_register_disclaim_proc(int, +GC_disclaim_proc, +int)GC_ATTR_NONNULL(2); +struct GC_finalizer_closure { +GC_finalization_proc proc; +void*cd; +}; +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL +GC_finalized_malloc(size_t, +const struct GC_finalizer_closure*)GC_ATTR_NONNULL(2); +#ifdef __cplusplus +} +#endif +#endif +#if defined(KEEP_BACK_PTRS)||defined(MAKE_BACK_GRAPH) +#define FINALIZER_CLOSURE_FLAG 0x2 +#else +#define FINALIZER_CLOSURE_FLAG 0x1 +#endif +STATIC int GC_CALLBACK GC_finalized_disclaim(void*obj) +{ +word fc_word=*(word*)obj; +if ((fc_word&FINALIZER_CLOSURE_FLAG)!=0){ +const struct GC_finalizer_closure*fc +=(struct GC_finalizer_closure*)(fc_word +&~(word)FINALIZER_CLOSURE_FLAG); +GC_ASSERT(!GC_find_leak); +(*fc->proc)((word*)obj+1,fc->cd); +} +return 0; +} +GC_API void GC_CALL GC_init_finalized_malloc(void) +{ +DCL_LOCK_STATE; +GC_init(); +LOCK(); +if (GC_finalized_kind!=0){ +UNLOCK(); +return; +} +GC_register_displacement_inner(sizeof(word)); +GC_register_displacement_inner(FINALIZER_CLOSURE_FLAG); +GC_register_displacement_inner(sizeof(oh)+FINALIZER_CLOSURE_FLAG); +GC_finalized_kind=GC_new_kind_inner(GC_new_free_list_inner(), +GC_DS_LENGTH,TRUE,TRUE); +GC_ASSERT(GC_finalized_kind!=0); +GC_register_disclaim_proc(GC_finalized_kind,GC_finalized_disclaim,TRUE); +UNLOCK(); +} +GC_API void GC_CALL GC_register_disclaim_proc(int kind,GC_disclaim_proc proc, +int mark_unconditionally) +{ +GC_ASSERT((unsigned)kind < MAXOBJKINDS); +GC_ASSERT(NONNULL_ARG_NOT_NULL(proc)); +if (!EXPECT(GC_find_leak,FALSE)){ +GC_obj_kinds[kind].ok_disclaim_proc=proc; +GC_obj_kinds[kind].ok_mark_unconditionally= +(GC_bool)mark_unconditionally; +} +} +GC_API GC_ATTR_MALLOC void*GC_CALL GC_finalized_malloc(size_t lb, +const struct GC_finalizer_closure*fclos) +{ +word*op; +GC_ASSERT(GC_finalized_kind!=0); +GC_ASSERT(NONNULL_ARG_NOT_NULL(fclos)); +GC_ASSERT(((word)fclos&FINALIZER_CLOSURE_FLAG)==0); +op=(word*)GC_malloc_kind(SIZET_SAT_ADD(lb,sizeof(word)), +GC_finalized_kind); +if (EXPECT(NULL==op,FALSE)) +return NULL; +*op=(word)fclos|FINALIZER_CLOSURE_FLAG; +GC_dirty(op); +REACHABLE_AFTER_DIRTY(fclos); +return op+1; +} +#endif +#include +#include +STATIC GC_bool GC_alloc_reclaim_list(struct obj_kind*kind) +{ +struct hblk**result=(struct hblk**) +GC_scratch_alloc((MAXOBJGRANULES+1)*sizeof(struct hblk*)); +if (result==0)return(FALSE); +BZERO(result,(MAXOBJGRANULES+1)*sizeof(struct hblk*)); +kind->ok_reclaim_list=result; +return(TRUE); +} +GC_INNER ptr_t GC_alloc_large(size_t lb,int k,unsigned flags) +{ +struct hblk*h; +word n_blocks; +ptr_t result; +GC_bool retry=FALSE; +GC_ASSERT(I_HOLD_LOCK()); +lb=ROUNDUP_GRANULE_SIZE(lb); +n_blocks=OBJ_SZ_TO_BLOCKS_CHECKED(lb); +if (!EXPECT(GC_is_initialized,TRUE)){ +DCL_LOCK_STATE; +UNLOCK(); +GC_init(); +LOCK(); +} +if (GC_incremental&&!GC_dont_gc){ +ENTER_GC(); +GC_collect_a_little_inner((int)n_blocks); +EXIT_GC(); +} +h=GC_allochblk(lb,k,flags); +#ifdef USE_MUNMAP +if (0==h){ +GC_merge_unmapped(); +h=GC_allochblk(lb,k,flags); +} +#endif +while (0==h&&GC_collect_or_expand(n_blocks,flags!=0,retry)){ +h=GC_allochblk(lb,k,flags); +retry=TRUE; +} +if (h==0){ +result=0; +} else { +size_t total_bytes=n_blocks*HBLKSIZE; +if (n_blocks > 1){ +GC_large_allocd_bytes+=total_bytes; +if (GC_large_allocd_bytes > GC_max_large_allocd_bytes) +GC_max_large_allocd_bytes=GC_large_allocd_bytes; +} +result=h->hb_body; +} +return result; +} +STATIC ptr_t GC_alloc_large_and_clear(size_t lb,int k,unsigned flags) +{ +ptr_t result; +GC_ASSERT(I_HOLD_LOCK()); +result=GC_alloc_large(lb,k,flags); +if (result!=NULL +&&(GC_debugging_started||GC_obj_kinds[k].ok_init)){ +word n_blocks=OBJ_SZ_TO_BLOCKS(lb); +BZERO(result,n_blocks*HBLKSIZE); +} +return result; +} +STATIC void GC_extend_size_map(size_t i) +{ +size_t orig_granule_sz=ROUNDED_UP_GRANULES(i); +size_t granule_sz; +size_t byte_sz=GRANULES_TO_BYTES(orig_granule_sz); +size_t smaller_than_i=byte_sz - (byte_sz>>3); +size_t low_limit; +size_t number_of_objs; +GC_ASSERT(I_HOLD_LOCK()); +GC_ASSERT(0==GC_size_map[i]); +if (0==GC_size_map[smaller_than_i]){ +low_limit=byte_sz - (byte_sz>>2); +granule_sz=orig_granule_sz; +while (GC_size_map[low_limit]!=0) +low_limit++; +} else { +low_limit=smaller_than_i+1; +while (GC_size_map[low_limit]!=0) +low_limit++; +granule_sz=ROUNDED_UP_GRANULES(low_limit); +granule_sz+=granule_sz>>3; +if (granule_sz < orig_granule_sz) +granule_sz=orig_granule_sz; +} +granule_sz=(granule_sz+1)&~1; +if (granule_sz > MAXOBJGRANULES) +granule_sz=MAXOBJGRANULES; +number_of_objs=HBLK_GRANULES/granule_sz; +GC_ASSERT(number_of_objs!=0); +granule_sz=(HBLK_GRANULES/number_of_objs)&~1; +byte_sz=GRANULES_TO_BYTES(granule_sz)- EXTRA_BYTES; +for (;low_limit<=byte_sz;low_limit++) +GC_size_map[low_limit]=granule_sz; +} +GC_INNER void*GC_generic_malloc_inner(size_t lb,int k) +{ +void*op; +GC_ASSERT(I_HOLD_LOCK()); +GC_ASSERT(k < MAXOBJKINDS); +if (SMALL_OBJ(lb)){ +struct obj_kind*kind=GC_obj_kinds+k; +size_t lg=GC_size_map[lb]; +void**opp=&(kind->ok_freelist[lg]); +op=*opp; +if (EXPECT(0==op,FALSE)){ +if (lg==0){ +if (!EXPECT(GC_is_initialized,TRUE)){ +DCL_LOCK_STATE; +UNLOCK(); +GC_init(); +LOCK(); +lg=GC_size_map[lb]; +} +if (0==lg){ +GC_extend_size_map(lb); +lg=GC_size_map[lb]; +GC_ASSERT(lg!=0); +} +opp=&(kind->ok_freelist[lg]); +op=*opp; +} +if (0==op){ +if (0==kind->ok_reclaim_list&& +!GC_alloc_reclaim_list(kind)) +return NULL; +op=GC_allocobj(lg,k); +if (0==op) +return NULL; +} +} +*opp=obj_link(op); +obj_link(op)=0; +GC_bytes_allocd+=GRANULES_TO_BYTES((word)lg); +} else { +op=(ptr_t)GC_alloc_large_and_clear(ADD_SLOP(lb),k,0); +if (op!=NULL) +GC_bytes_allocd+=lb; +} +return op; +} +#if defined(DBG_HDRS_ALL)||defined(GC_GCJ_SUPPORT)||!defined(GC_NO_FINALIZATION) +GC_INNER void*GC_generic_malloc_inner_ignore_off_page(size_t lb,int k) +{ +word lb_adjusted; +void*op; +GC_ASSERT(I_HOLD_LOCK()); +if (lb<=HBLKSIZE) +return GC_generic_malloc_inner(lb,k); +GC_ASSERT(k < MAXOBJKINDS); +lb_adjusted=ADD_SLOP(lb); +op=GC_alloc_large_and_clear(lb_adjusted,k,IGNORE_OFF_PAGE); +if (op!=NULL) +GC_bytes_allocd+=lb_adjusted; +return op; +} +#endif +#ifdef GC_COLLECT_AT_MALLOC +#if defined(CPPCHECK) +size_t GC_dbg_collect_at_malloc_min_lb=16*1024; +#else +size_t GC_dbg_collect_at_malloc_min_lb=(GC_COLLECT_AT_MALLOC); +#endif +#endif +GC_API GC_ATTR_MALLOC void*GC_CALL GC_generic_malloc(size_t lb,int k) +{ +void*result; +DCL_LOCK_STATE; +GC_ASSERT(k < MAXOBJKINDS); +if (EXPECT(GC_have_errors,FALSE)) +GC_print_all_errors(); +GC_INVOKE_FINALIZERS(); +GC_DBG_COLLECT_AT_MALLOC(lb); +if (SMALL_OBJ(lb)){ +LOCK(); +result=GC_generic_malloc_inner(lb,k); +UNLOCK(); +} else { +size_t lg; +size_t lb_rounded; +word n_blocks; +GC_bool init; +lg=ROUNDED_UP_GRANULES(lb); +lb_rounded=GRANULES_TO_BYTES(lg); +n_blocks=OBJ_SZ_TO_BLOCKS(lb_rounded); +init=GC_obj_kinds[k].ok_init; +LOCK(); +result=(ptr_t)GC_alloc_large(lb_rounded,k,0); +if (0!=result){ +if (GC_debugging_started){ +BZERO(result,n_blocks*HBLKSIZE); +} else { +#ifdef THREADS +((word*)result)[0]=0; +((word*)result)[1]=0; +((word*)result)[GRANULES_TO_WORDS(lg)-1]=0; +((word*)result)[GRANULES_TO_WORDS(lg)-2]=0; +#endif +} +GC_bytes_allocd+=lb_rounded; +} +UNLOCK(); +if (init&&!GC_debugging_started&&0!=result){ +BZERO(result,n_blocks*HBLKSIZE); +} +} +if (0==result){ +return((*GC_get_oom_fn())(lb)); +} else { +return(result); +} +} +GC_API GC_ATTR_MALLOC void*GC_CALL GC_malloc_kind_global(size_t lb,int k) +{ +GC_ASSERT(k < MAXOBJKINDS); +if (SMALL_OBJ(lb)){ +void*op; +void**opp; +size_t lg; +DCL_LOCK_STATE; +GC_DBG_COLLECT_AT_MALLOC(lb); +LOCK(); +lg=GC_size_map[lb]; +opp=&GC_obj_kinds[k].ok_freelist[lg]; +op=*opp; +if (EXPECT(op!=NULL,TRUE)){ +if (k==PTRFREE){ +*opp=obj_link(op); +} else { +GC_ASSERT(0==obj_link(op) +||((word)obj_link(op) +<=(word)GC_greatest_plausible_heap_addr +&&(word)obj_link(op) +>=(word)GC_least_plausible_heap_addr)); +*opp=obj_link(op); +obj_link(op)=0; +} +GC_bytes_allocd+=GRANULES_TO_BYTES((word)lg); +UNLOCK(); +return op; +} +UNLOCK(); +} +return GC_clear_stack(GC_generic_malloc(lb,k)); +} +#if defined(THREADS)&&!defined(THREAD_LOCAL_ALLOC) +GC_API GC_ATTR_MALLOC void*GC_CALL GC_malloc_kind(size_t lb,int k) +{ +return GC_malloc_kind_global(lb,k); +} +#endif +GC_API GC_ATTR_MALLOC void*GC_CALL GC_malloc_atomic(size_t lb) +{ +return GC_malloc_kind(lb,PTRFREE); +} +GC_API GC_ATTR_MALLOC void*GC_CALL GC_malloc(size_t lb) +{ +return GC_malloc_kind(lb,NORMAL); +} +GC_API GC_ATTR_MALLOC void*GC_CALL GC_generic_malloc_uncollectable( +size_t lb,int k) +{ +void*op; +DCL_LOCK_STATE; +GC_ASSERT(k < MAXOBJKINDS); +if (SMALL_OBJ(lb)){ +void**opp; +size_t lg; +GC_DBG_COLLECT_AT_MALLOC(lb); +if (EXTRA_BYTES!=0&&lb!=0)lb--; +LOCK(); +lg=GC_size_map[lb]; +opp=&GC_obj_kinds[k].ok_freelist[lg]; +op=*opp; +if (EXPECT(op!=NULL,TRUE)){ +*opp=obj_link(op); +obj_link(op)=0; +GC_bytes_allocd+=GRANULES_TO_BYTES((word)lg); +GC_non_gc_bytes+=GRANULES_TO_BYTES((word)lg); +UNLOCK(); +} else { +UNLOCK(); +op=GC_generic_malloc(lb,k); +} +GC_ASSERT(0==op||GC_is_marked(op)); +} else { +op=GC_generic_malloc(lb,k); +if (op){ +hdr*hhdr=HDR(op); +GC_ASSERT(((word)op&(HBLKSIZE - 1))==0); +LOCK(); +set_mark_bit_from_hdr(hhdr,0); +#ifndef THREADS +GC_ASSERT(hhdr->hb_n_marks==0); +#endif +hhdr->hb_n_marks=1; +UNLOCK(); +} +} +return op; +} +GC_API GC_ATTR_MALLOC void*GC_CALL GC_malloc_uncollectable(size_t lb) +{ +return GC_generic_malloc_uncollectable(lb,UNCOLLECTABLE); +} +#ifdef GC_ATOMIC_UNCOLLECTABLE +GC_API GC_ATTR_MALLOC void*GC_CALL +GC_malloc_atomic_uncollectable(size_t lb) +{ +return GC_generic_malloc_uncollectable(lb,AUNCOLLECTABLE); +} +#endif +#if defined(REDIRECT_MALLOC)&&!defined(REDIRECT_MALLOC_IN_HEADER) +#ifndef MSWINCE +#include +#endif +#define GC_debug_malloc_replacement(lb)GC_debug_malloc(lb,GC_DBG_EXTRAS) +#if defined(CPPCHECK) +#define REDIRECT_MALLOC_F GC_malloc +#else +#define REDIRECT_MALLOC_F REDIRECT_MALLOC +#endif +void*malloc(size_t lb) +{ +#if defined(I386)&&defined(GC_SOLARIS_THREADS) +if (!EXPECT(GC_is_initialized,TRUE))return sbrk(lb); +#endif +return (void*)REDIRECT_MALLOC_F(lb); +} +#if defined(GC_LINUX_THREADS) +STATIC ptr_t GC_libpthread_start=0; +STATIC ptr_t GC_libpthread_end=0; +STATIC ptr_t GC_libld_start=0; +STATIC ptr_t GC_libld_end=0; +STATIC void GC_init_lib_bounds(void) +{ +IF_CANCEL(int cancel_state;) +if (GC_libpthread_start!=0)return; +DISABLE_CANCEL(cancel_state); +GC_init(); +if (!GC_text_mapping("libpthread-", +&GC_libpthread_start,&GC_libpthread_end)){ +WARN("Failed to find libpthread.so text mapping:Expect crash\n",0); +GC_libpthread_start=(ptr_t)1; +} +if (!GC_text_mapping("ld-",&GC_libld_start,&GC_libld_end)){ +WARN("Failed to find ld.so text mapping:Expect crash\n",0); +} +RESTORE_CANCEL(cancel_state); +} +#endif +void*calloc(size_t n,size_t lb) +{ +if ((lb|n)> GC_SQRT_SIZE_MAX +&&lb&&n > GC_SIZE_MAX/lb) +return (*GC_get_oom_fn())(GC_SIZE_MAX); +#if defined(GC_LINUX_THREADS) +{ +static GC_bool lib_bounds_set=FALSE; +ptr_t caller=(ptr_t)__builtin_return_address(0); +if (!EXPECT(lib_bounds_set,TRUE)){ +GC_init_lib_bounds(); +lib_bounds_set=TRUE; +} +if (((word)caller>=(word)GC_libpthread_start +&&(word)caller < (word)GC_libpthread_end) +||((word)caller>=(word)GC_libld_start +&&(word)caller < (word)GC_libld_end)) +return GC_generic_malloc_uncollectable(n*lb,UNCOLLECTABLE); +} +#endif +return (void*)REDIRECT_MALLOC_F(n*lb); +} +#ifndef strdup +char*strdup(const char*s) +{ +size_t lb=strlen(s)+1; +char*result=(char*)REDIRECT_MALLOC_F(lb); +if (result==0){ +errno=ENOMEM; +return 0; +} +BCOPY(s,result,lb); +return result; +} +#endif +#ifndef strndup +char*strndup(const char*str,size_t size) +{ +char*copy; +size_t len=strlen(str); +if (len > size) +len=size; +copy=(char*)REDIRECT_MALLOC_F(len+1); +if (copy==NULL){ +errno=ENOMEM; +return NULL; +} +if (EXPECT(len > 0,TRUE)) +BCOPY(str,copy,len); +copy[len]='\0'; +return copy; +} +#endif +#undef GC_debug_malloc_replacement +#endif +GC_API void GC_CALL GC_free(void*p) +{ +struct hblk*h; +hdr*hhdr; +size_t sz; +size_t ngranules; +int knd; +struct obj_kind*ok; +DCL_LOCK_STATE; +if (p){ +} else { +return; +} +#ifdef LOG_ALLOCS +GC_log_printf("GC_free(%p)after GC #%lu\n", +p,(unsigned long)GC_gc_no); +#endif +h=HBLKPTR(p); +hhdr=HDR(h); +#if defined(REDIRECT_MALLOC)&&((defined(NEED_CALLINFO)&&defined(GC_HAVE_BUILTIN_BACKTRACE))||defined(GC_SOLARIS_THREADS)||defined(GC_LINUX_THREADS)||defined(MSWIN32)) +if (0==hhdr)return; +#endif +GC_ASSERT(GC_base(p)==p); +sz=(size_t)hhdr->hb_sz; +ngranules=BYTES_TO_GRANULES(sz); +knd=hhdr->hb_obj_kind; +ok=&GC_obj_kinds[knd]; +if (EXPECT(ngranules<=MAXOBJGRANULES,TRUE)){ +void**flh; +LOCK(); +GC_bytes_freed+=sz; +if (IS_UNCOLLECTABLE(knd))GC_non_gc_bytes-=sz; +if (ok->ok_init&&EXPECT(sz > sizeof(word),TRUE)){ +BZERO((word*)p+1,sz-sizeof(word)); +} +flh=&(ok->ok_freelist[ngranules]); +obj_link(p)=*flh; +*flh=(ptr_t)p; +UNLOCK(); +} else { +size_t nblocks=OBJ_SZ_TO_BLOCKS(sz); +LOCK(); +GC_bytes_freed+=sz; +if (IS_UNCOLLECTABLE(knd))GC_non_gc_bytes-=sz; +if (nblocks > 1){ +GC_large_allocd_bytes-=nblocks*HBLKSIZE; +} +GC_freehblk(h); +UNLOCK(); +} +} +#ifdef THREADS +GC_INNER void GC_free_inner(void*p) +{ +struct hblk*h; +hdr*hhdr; +size_t sz; +size_t ngranules; +int knd; +struct obj_kind*ok; +h=HBLKPTR(p); +hhdr=HDR(h); +knd=hhdr->hb_obj_kind; +sz=(size_t)hhdr->hb_sz; +ngranules=BYTES_TO_GRANULES(sz); +ok=&GC_obj_kinds[knd]; +if (ngranules<=MAXOBJGRANULES){ +void**flh; +GC_bytes_freed+=sz; +if (IS_UNCOLLECTABLE(knd))GC_non_gc_bytes-=sz; +if (ok->ok_init&&EXPECT(sz > sizeof(word),TRUE)){ +BZERO((word*)p+1,sz-sizeof(word)); +} +flh=&(ok->ok_freelist[ngranules]); +obj_link(p)=*flh; +*flh=(ptr_t)p; +} else { +size_t nblocks=OBJ_SZ_TO_BLOCKS(sz); +GC_bytes_freed+=sz; +if (IS_UNCOLLECTABLE(knd))GC_non_gc_bytes-=sz; +if (nblocks > 1){ +GC_large_allocd_bytes-=nblocks*HBLKSIZE; +} +GC_freehblk(h); +} +} +#endif +#if defined(REDIRECT_MALLOC)&&!defined(REDIRECT_FREE) +#define REDIRECT_FREE GC_free +#endif +#if defined(REDIRECT_FREE)&&!defined(REDIRECT_MALLOC_IN_HEADER) +#if defined(CPPCHECK) +#define REDIRECT_FREE_F GC_free +#else +#define REDIRECT_FREE_F REDIRECT_FREE +#endif +void free(void*p) +{ +#ifndef IGNORE_FREE +#if defined(GC_LINUX_THREADS)&&!defined(USE_PROC_FOR_LIBRARIES) +ptr_t caller=(ptr_t)__builtin_return_address(0); +if (((word)caller>=(word)GC_libpthread_start +&&(word)caller < (word)GC_libpthread_end) +||((word)caller>=(word)GC_libld_start +&&(word)caller < (word)GC_libld_end)){ +GC_free(p); +return; +} +#endif +REDIRECT_FREE_F(p); +#endif +} +#endif +#include +#include +#ifndef MSWINCE +#include +#endif +#ifndef GC_ALLOC_PTRS_H +#define GC_ALLOC_PTRS_H +#ifdef __cplusplus +extern "C" { +#endif +GC_API void**const GC_objfreelist_ptr; +GC_API void**const GC_aobjfreelist_ptr; +GC_API void**const GC_uobjfreelist_ptr; +#ifdef GC_ATOMIC_UNCOLLECTABLE +GC_API void**const GC_auobjfreelist_ptr; +#endif +GC_API void GC_CALL GC_incr_bytes_allocd(size_t bytes); +GC_API void GC_CALL GC_incr_bytes_freed(size_t bytes); +#ifdef __cplusplus +} +#endif +#endif +void**const GC_objfreelist_ptr=GC_objfreelist; +void**const GC_aobjfreelist_ptr=GC_aobjfreelist; +void**const GC_uobjfreelist_ptr=GC_uobjfreelist; +#ifdef GC_ATOMIC_UNCOLLECTABLE +void**const GC_auobjfreelist_ptr=GC_auobjfreelist; +#endif +GC_API int GC_CALL GC_get_kind_and_size(const void*p,size_t*psize) +{ +hdr*hhdr=HDR(p); +if (psize!=NULL){ +*psize=(size_t)hhdr->hb_sz; +} +return hhdr->hb_obj_kind; +} +GC_API GC_ATTR_MALLOC void*GC_CALL GC_generic_or_special_malloc(size_t lb, +int knd) +{ +switch(knd){ +case PTRFREE: +case NORMAL: +return GC_malloc_kind(lb,knd); +case UNCOLLECTABLE: +#ifdef GC_ATOMIC_UNCOLLECTABLE +case AUNCOLLECTABLE: +#endif +return GC_generic_malloc_uncollectable(lb,knd); +default: +return GC_generic_malloc(lb,knd); +} +} +GC_API void*GC_CALL GC_realloc(void*p,size_t lb) +{ +struct hblk*h; +hdr*hhdr; +void*result; +size_t sz; +size_t orig_sz; +int obj_kind; +if (p==0)return(GC_malloc(lb)); +if (0==lb){ +#ifndef IGNORE_FREE +GC_free(p); +#endif +return NULL; +} +h=HBLKPTR(p); +hhdr=HDR(h); +sz=(size_t)hhdr->hb_sz; +obj_kind=hhdr->hb_obj_kind; +orig_sz=sz; +if (sz > MAXOBJBYTES){ +word descr=GC_obj_kinds[obj_kind].ok_descriptor; +sz=(sz+HBLKSIZE-1)&~HBLKMASK; +if (GC_obj_kinds[obj_kind].ok_relocate_descr) +descr+=sz; +#ifdef AO_HAVE_store +GC_STATIC_ASSERT(sizeof(hhdr->hb_sz)==sizeof(AO_t)); +AO_store((volatile AO_t*)&hhdr->hb_sz,(AO_t)sz); +AO_store((volatile AO_t*)&hhdr->hb_descr,(AO_t)descr); +#else +{ +DCL_LOCK_STATE; +LOCK(); +hhdr->hb_sz=sz; +hhdr->hb_descr=descr; +UNLOCK(); +} +#endif +#ifdef MARK_BIT_PER_OBJ +GC_ASSERT(hhdr->hb_inv_sz==LARGE_INV_SZ); +#endif +#ifdef MARK_BIT_PER_GRANULE +GC_ASSERT((hhdr->hb_flags&LARGE_BLOCK)!=0 +&&hhdr->hb_map[ANY_INDEX]==1); +#endif +if (IS_UNCOLLECTABLE(obj_kind))GC_non_gc_bytes+=(sz - orig_sz); +} +if (ADD_SLOP(lb)<=sz){ +if (lb>=(sz>>1)){ +if (orig_sz > lb){ +BZERO(((ptr_t)p)+lb,orig_sz - lb); +} +return(p); +} +sz=lb; +} +result=GC_generic_or_special_malloc((word)lb,obj_kind); +if (result!=NULL){ +BCOPY(p,result,sz); +#ifndef IGNORE_FREE +GC_free(p); +#endif +} +return result; +} +#if defined(REDIRECT_MALLOC)&&!defined(REDIRECT_REALLOC) +#define REDIRECT_REALLOC GC_realloc +#endif +#ifdef REDIRECT_REALLOC +#define GC_debug_realloc_replacement(p,lb)GC_debug_realloc(p,lb,GC_DBG_EXTRAS) +#if!defined(REDIRECT_MALLOC_IN_HEADER) +void*realloc(void*p,size_t lb) +{ +return(REDIRECT_REALLOC(p,lb)); +} +#endif +#undef GC_debug_realloc_replacement +#endif +GC_API GC_ATTR_MALLOC void*GC_CALL +GC_generic_malloc_ignore_off_page(size_t lb,int k) +{ +void*result; +size_t lg; +size_t lb_rounded; +word n_blocks; +GC_bool init; +DCL_LOCK_STATE; +if (SMALL_OBJ(lb)) +return GC_generic_malloc(lb,k); +GC_ASSERT(k < MAXOBJKINDS); +lg=ROUNDED_UP_GRANULES(lb); +lb_rounded=GRANULES_TO_BYTES(lg); +n_blocks=OBJ_SZ_TO_BLOCKS(lb_rounded); +init=GC_obj_kinds[k].ok_init; +if (EXPECT(GC_have_errors,FALSE)) +GC_print_all_errors(); +GC_INVOKE_FINALIZERS(); +GC_DBG_COLLECT_AT_MALLOC(lb); +LOCK(); +result=(ptr_t)GC_alloc_large(ADD_SLOP(lb),k,IGNORE_OFF_PAGE); +if (NULL==result){ +GC_oom_func oom_fn=GC_oom_fn; +UNLOCK(); +return (*oom_fn)(lb); +} +if (GC_debugging_started){ +BZERO(result,n_blocks*HBLKSIZE); +} else { +#ifdef THREADS +((word*)result)[0]=0; +((word*)result)[1]=0; +((word*)result)[GRANULES_TO_WORDS(lg)-1]=0; +((word*)result)[GRANULES_TO_WORDS(lg)-2]=0; +#endif +} +GC_bytes_allocd+=lb_rounded; +UNLOCK(); +if (init&&!GC_debugging_started){ +BZERO(result,n_blocks*HBLKSIZE); +} +return(result); +} +GC_API GC_ATTR_MALLOC void*GC_CALL GC_malloc_ignore_off_page(size_t lb) +{ +return GC_generic_malloc_ignore_off_page(lb,NORMAL); +} +GC_API GC_ATTR_MALLOC void*GC_CALL +GC_malloc_atomic_ignore_off_page(size_t lb) +{ +return GC_generic_malloc_ignore_off_page(lb,PTRFREE); +} +GC_API void GC_CALL GC_incr_bytes_allocd(size_t n) +{ +GC_bytes_allocd+=n; +} +GC_API void GC_CALL GC_incr_bytes_freed(size_t n) +{ +GC_bytes_freed+=n; +} +GC_API size_t GC_CALL GC_get_expl_freed_bytes_since_gc(void) +{ +return (size_t)GC_bytes_freed; +} +#ifdef PARALLEL_MARK +STATIC volatile AO_t GC_bytes_allocd_tmp=0; +#endif +GC_API void GC_CALL GC_generic_malloc_many(size_t lb,int k,void**result) +{ +void*op; +void*p; +void**opp; +size_t lw; +size_t lg; +signed_word my_bytes_allocd=0; +struct obj_kind*ok=&(GC_obj_kinds[k]); +struct hblk**rlh; +DCL_LOCK_STATE; +GC_ASSERT(lb!=0&&(lb&(GRANULE_BYTES-1))==0); +if (!SMALL_OBJ(lb)||GC_manual_vdb){ +op=GC_generic_malloc(lb,k); +if (EXPECT(0!=op,TRUE)) +obj_link(op)=0; +*result=op; +#ifndef GC_DISABLE_INCREMENTAL +if (GC_manual_vdb&&GC_is_heap_ptr(result)){ +GC_dirty_inner(result); +REACHABLE_AFTER_DIRTY(op); +} +#endif +return; +} +GC_ASSERT(k < MAXOBJKINDS); +lw=BYTES_TO_WORDS(lb); +lg=BYTES_TO_GRANULES(lb); +if (EXPECT(GC_have_errors,FALSE)) +GC_print_all_errors(); +GC_INVOKE_FINALIZERS(); +GC_DBG_COLLECT_AT_MALLOC(lb); +if (!EXPECT(GC_is_initialized,TRUE))GC_init(); +LOCK(); +if (GC_incremental&&!GC_dont_gc){ +ENTER_GC(); +GC_collect_a_little_inner(1); +EXIT_GC(); +} +rlh=ok->ok_reclaim_list; +if (rlh!=NULL){ +struct hblk*hbp; +hdr*hhdr; +for (rlh+=lg;(hbp=*rlh)!=NULL;){ +hhdr=HDR(hbp); +*rlh=hhdr->hb_next; +GC_ASSERT(hhdr->hb_sz==lb); +hhdr->hb_last_reclaimed=(unsigned short)GC_gc_no; +#ifdef PARALLEL_MARK +if (GC_parallel){ +signed_word my_bytes_allocd_tmp= +(signed_word)AO_load(&GC_bytes_allocd_tmp); +GC_ASSERT(my_bytes_allocd_tmp>=0); +if (my_bytes_allocd_tmp!=0){ +(void)AO_fetch_and_add(&GC_bytes_allocd_tmp, +(AO_t)(-my_bytes_allocd_tmp)); +GC_bytes_allocd+=my_bytes_allocd_tmp; +} +GC_acquire_mark_lock(); +++GC_fl_builder_count; +UNLOCK(); +GC_release_mark_lock(); +} +#endif +op=GC_reclaim_generic(hbp,hhdr,lb, +ok->ok_init,0,&my_bytes_allocd); +if (op!=0){ +#ifdef PARALLEL_MARK +if (GC_parallel){ +*result=op; +(void)AO_fetch_and_add(&GC_bytes_allocd_tmp, +(AO_t)my_bytes_allocd); +GC_acquire_mark_lock(); +--GC_fl_builder_count; +if (GC_fl_builder_count==0)GC_notify_all_builder(); +#ifdef THREAD_SANITIZER +GC_release_mark_lock(); +LOCK(); +GC_bytes_found+=my_bytes_allocd; +UNLOCK(); +#else +GC_bytes_found+=my_bytes_allocd; +GC_release_mark_lock(); +#endif +(void)GC_clear_stack(0); +return; +} +#endif +GC_bytes_found+=my_bytes_allocd; +GC_bytes_allocd+=my_bytes_allocd; +goto out; +} +#ifdef PARALLEL_MARK +if (GC_parallel){ +GC_acquire_mark_lock(); +--GC_fl_builder_count; +if (GC_fl_builder_count==0)GC_notify_all_builder(); +GC_release_mark_lock(); +LOCK(); +} +#endif +} +} +opp=&(GC_obj_kinds[k].ok_freelist[lg]); +if ( (op=*opp)!=0){ +*opp=0; +my_bytes_allocd=0; +for (p=op;p!=0;p=obj_link(p)){ +my_bytes_allocd+=lb; +if ((word)my_bytes_allocd>=HBLKSIZE){ +*opp=obj_link(p); +obj_link(p)=0; +break; +} +} +GC_bytes_allocd+=my_bytes_allocd; +goto out; +} +{ +struct hblk*h=GC_allochblk(lb,k,0); +if (h){ +if (IS_UNCOLLECTABLE(k))GC_set_hdr_marks(HDR(h)); +GC_bytes_allocd+=HBLKSIZE - HBLKSIZE % lb; +#ifdef PARALLEL_MARK +if (GC_parallel){ +GC_acquire_mark_lock(); +++GC_fl_builder_count; +UNLOCK(); +GC_release_mark_lock(); +op=GC_build_fl(h,lw, +(ok->ok_init||GC_debugging_started),0); +*result=op; +GC_acquire_mark_lock(); +--GC_fl_builder_count; +if (GC_fl_builder_count==0)GC_notify_all_builder(); +GC_release_mark_lock(); +(void)GC_clear_stack(0); +return; +} +#endif +op=GC_build_fl(h,lw,(ok->ok_init||GC_debugging_started),0); +goto out; +} +} +op=GC_generic_malloc_inner(lb,k); +if (0!=op)obj_link(op)=0; +out: +*result=op; +UNLOCK(); +(void)GC_clear_stack(0); +} +GC_API GC_ATTR_MALLOC void*GC_CALL GC_malloc_many(size_t lb) +{ +void*result; +lb=SIZET_SAT_ADD(lb,EXTRA_BYTES+GRANULE_BYTES - 1) +&~(GRANULE_BYTES - 1); +GC_generic_malloc_many(lb,NORMAL,&result); +return result; +} +#include +GC_API GC_ATTR_MALLOC void*GC_CALL GC_memalign(size_t align,size_t lb) +{ +size_t new_lb; +size_t offset; +ptr_t result; +if (align<=GRANULE_BYTES)return GC_malloc(lb); +if (align>=HBLKSIZE/2||lb>=HBLKSIZE/2){ +if (align > HBLKSIZE){ +return (*GC_get_oom_fn())(LONG_MAX-1024); +} +return GC_malloc(lb<=HBLKSIZE?HBLKSIZE:lb); +} +new_lb=SIZET_SAT_ADD(lb,align - 1); +result=(ptr_t)GC_malloc(new_lb); +offset=(word)result % align; +if (offset!=0){ +offset=align - offset; +if (!GC_all_interior_pointers){ +GC_STATIC_ASSERT(VALID_OFFSET_SZ<=HBLKSIZE); +GC_ASSERT(offset < VALID_OFFSET_SZ); +GC_register_displacement(offset); +} +} +result+=offset; +GC_ASSERT((word)result % align==0); +return result; +} +GC_API int GC_CALL GC_posix_memalign(void**memptr,size_t align,size_t lb) +{ +size_t align_minus_one=align - 1; +if (align < sizeof(void*)||(align_minus_one&align)!=0){ +#ifdef MSWINCE +return ERROR_INVALID_PARAMETER; +#else +return EINVAL; +#endif +} +if ((*memptr=GC_memalign(align,lb))==NULL){ +#ifdef MSWINCE +return ERROR_NOT_ENOUGH_MEMORY; +#else +return ENOMEM; +#endif +} +return 0; +} +GC_API GC_ATTR_MALLOC char*GC_CALL GC_strdup(const char*s) +{ +char*copy; +size_t lb; +if (s==NULL)return NULL; +lb=strlen(s)+1; +copy=(char*)GC_malloc_atomic(lb); +if (NULL==copy){ +#ifndef MSWINCE +errno=ENOMEM; +#endif +return NULL; +} +BCOPY(s,copy,lb); +return copy; +} +GC_API GC_ATTR_MALLOC char*GC_CALL GC_strndup(const char*str,size_t size) +{ +char*copy; +size_t len=strlen(str); +if (len > size) +len=size; +copy=(char*)GC_malloc_atomic(len+1); +if (copy==NULL){ +#ifndef MSWINCE +errno=ENOMEM; +#endif +return NULL; +} +if (EXPECT(len > 0,TRUE)) +BCOPY(str,copy,len); +copy[len]='\0'; +return copy; +} +#ifdef GC_REQUIRE_WCSDUP +#include +GC_API GC_ATTR_MALLOC wchar_t*GC_CALL GC_wcsdup(const wchar_t*str) +{ +size_t lb=(wcslen(str)+1)*sizeof(wchar_t); +wchar_t*copy=(wchar_t*)GC_malloc_atomic(lb); +if (copy==NULL){ +#ifndef MSWINCE +errno=ENOMEM; +#endif +return NULL; +} +BCOPY(str,copy,lb); +return copy; +} +#endif +#ifndef CPPCHECK +GC_API void*GC_CALL GC_malloc_stubborn(size_t lb) +{ +return GC_malloc(lb); +} +GC_API void GC_CALL GC_change_stubborn(const void*p GC_ATTR_UNUSED) +{ +} +#endif +GC_API void GC_CALL GC_end_stubborn_change(const void*p) +{ +GC_dirty(p); +} +GC_API void GC_CALL GC_ptr_store_and_dirty(void*p,const void*q) +{ +*(const void**)p=q; +GC_dirty(p); +REACHABLE_AFTER_DIRTY(q); +} +#if defined(__MINGW32__)&&!defined(__MINGW_EXCPT_DEFINE_PSDK)&&defined(__i386__) +#define __MINGW_EXCPT_DEFINE_PSDK 1 +#endif +#include +#if defined(MSWIN32)&&defined(__GNUC__) +#include +#endif +GC_ATTR_NOINLINE +void GC_noop6(word arg1 GC_ATTR_UNUSED,word arg2 GC_ATTR_UNUSED, +word arg3 GC_ATTR_UNUSED,word arg4 GC_ATTR_UNUSED, +word arg5 GC_ATTR_UNUSED,word arg6 GC_ATTR_UNUSED) +{ +#if defined(AO_HAVE_compiler_barrier)&&!defined(BASE_ATOMIC_OPS_EMULATED) +AO_compiler_barrier(); +#else +GC_noop1(0); +#endif +} +volatile word GC_noop_sink; +GC_ATTR_NO_SANITIZE_THREAD +GC_API void GC_CALL GC_noop1(word x) +{ +GC_noop_sink=x; +} +GC_INNER struct obj_kind GC_obj_kinds[MAXOBJKINDS]={ +{&GC_aobjfreelist[0],0, +GC_DS_LENGTH,FALSE,FALSE +OK_DISCLAIM_INITZ }, +{&GC_objfreelist[0],0, +GC_DS_LENGTH, +TRUE,TRUE +OK_DISCLAIM_INITZ }, +{&GC_uobjfreelist[0],0, +GC_DS_LENGTH,TRUE,TRUE +OK_DISCLAIM_INITZ }, +#ifdef GC_ATOMIC_UNCOLLECTABLE +{&GC_auobjfreelist[0],0, +GC_DS_LENGTH,FALSE,FALSE +OK_DISCLAIM_INITZ }, +#endif +}; +#ifndef INITIAL_MARK_STACK_SIZE +#define INITIAL_MARK_STACK_SIZE (1*HBLKSIZE) +#endif +#if!defined(GC_DISABLE_INCREMENTAL) +STATIC word GC_n_rescuing_pages=0; +#endif +#ifdef PARALLEL_MARK +GC_INNER GC_bool GC_parallel_mark_disabled=FALSE; +#endif +GC_INNER GC_bool GC_collection_in_progress(void) +{ +return(GC_mark_state!=MS_NONE); +} +GC_INNER void GC_clear_hdr_marks(hdr*hhdr) +{ +size_t last_bit; +#ifdef AO_HAVE_load +last_bit=FINAL_MARK_BIT((size_t)AO_load((volatile AO_t*)&hhdr->hb_sz)); +#else +last_bit=FINAL_MARK_BIT((size_t)hhdr->hb_sz); +#endif +BZERO(hhdr->hb_marks,sizeof(hhdr->hb_marks)); +set_mark_bit_from_hdr(hhdr,last_bit); +hhdr->hb_n_marks=0; +} +GC_INNER void GC_set_hdr_marks(hdr*hhdr) +{ +unsigned i; +size_t sz=(size_t)hhdr->hb_sz; +unsigned n_marks=(unsigned)FINAL_MARK_BIT(sz); +#ifdef USE_MARK_BYTES +for (i=0;i<=n_marks;i+=(unsigned)MARK_BIT_OFFSET(sz)){ +hhdr->hb_marks[i]=1; +} +#else +for (i=0;i < divWORDSZ(n_marks+WORDSZ);++i){ +hhdr->hb_marks[i]=GC_WORD_MAX; +} +#endif +#ifdef MARK_BIT_PER_OBJ +hhdr->hb_n_marks=n_marks; +#else +hhdr->hb_n_marks=HBLK_OBJS(sz); +#endif +} +static void clear_marks_for_block(struct hblk*h,word dummy GC_ATTR_UNUSED) +{ +hdr*hhdr=HDR(h); +if (IS_UNCOLLECTABLE(hhdr->hb_obj_kind))return; +GC_clear_hdr_marks(hhdr); +} +GC_API void GC_CALL GC_set_mark_bit(const void*p) +{ +struct hblk*h=HBLKPTR(p); +hdr*hhdr=HDR(h); +word bit_no=MARK_BIT_NO((ptr_t)p - (ptr_t)h,hhdr->hb_sz); +if (!mark_bit_from_hdr(hhdr,bit_no)){ +set_mark_bit_from_hdr(hhdr,bit_no); +++hhdr->hb_n_marks; +} +} +GC_API void GC_CALL GC_clear_mark_bit(const void*p) +{ +struct hblk*h=HBLKPTR(p); +hdr*hhdr=HDR(h); +word bit_no=MARK_BIT_NO((ptr_t)p - (ptr_t)h,hhdr->hb_sz); +if (mark_bit_from_hdr(hhdr,bit_no)){ +size_t n_marks=hhdr->hb_n_marks; +GC_ASSERT(n_marks!=0); +clear_mark_bit_from_hdr(hhdr,bit_no); +n_marks--; +#ifdef PARALLEL_MARK +if (n_marks!=0||!GC_parallel) +hhdr->hb_n_marks=n_marks; +#else +hhdr->hb_n_marks=n_marks; +#endif +} +} +GC_API int GC_CALL GC_is_marked(const void*p) +{ +struct hblk*h=HBLKPTR(p); +hdr*hhdr=HDR(h); +word bit_no=MARK_BIT_NO((ptr_t)p - (ptr_t)h,hhdr->hb_sz); +return (int)mark_bit_from_hdr(hhdr,bit_no); +} +GC_INNER void GC_clear_marks(void) +{ +GC_apply_to_all_blocks(clear_marks_for_block,(word)0); +GC_objects_are_marked=FALSE; +GC_mark_state=MS_INVALID; +GC_scan_ptr=NULL; +} +GC_INNER void GC_initiate_gc(void) +{ +GC_ASSERT(I_HOLD_LOCK()); +#ifndef GC_DISABLE_INCREMENTAL +if (GC_incremental){ +#ifdef CHECKSUMS +GC_read_dirty(FALSE); +GC_check_dirty(); +#else +GC_read_dirty(GC_mark_state==MS_INVALID); +#endif +} +GC_n_rescuing_pages=0; +#endif +if (GC_mark_state==MS_NONE){ +GC_mark_state=MS_PUSH_RESCUERS; +} else if (GC_mark_state!=MS_INVALID){ +ABORT("Unexpected state"); +} +GC_scan_ptr=NULL; +} +#ifdef PARALLEL_MARK +STATIC void GC_do_parallel_mark(void); +#endif +#ifdef GC_DISABLE_INCREMENTAL +#define GC_push_next_marked_dirty(h)GC_push_next_marked(h) +#else +STATIC struct hblk*GC_push_next_marked_dirty(struct hblk*h); +#endif +STATIC struct hblk*GC_push_next_marked(struct hblk*h); +STATIC struct hblk*GC_push_next_marked_uncollectable(struct hblk*h); +static void alloc_mark_stack(size_t); +#ifdef WRAP_MARK_SOME +STATIC GC_bool GC_mark_some_inner(ptr_t cold_gc_frame) +#else +GC_INNER GC_bool GC_mark_some(ptr_t cold_gc_frame) +#endif +{ +switch(GC_mark_state){ +case MS_NONE: +break; +case MS_PUSH_RESCUERS: +if ((word)GC_mark_stack_top +>=(word)(GC_mark_stack_limit - INITIAL_MARK_STACK_SIZE/2)){ +GC_mark_stack_too_small=TRUE; +MARK_FROM_MARK_STACK(); +break; +} else { +GC_scan_ptr=GC_push_next_marked_dirty(GC_scan_ptr); +if (NULL==GC_scan_ptr){ +#if!defined(GC_DISABLE_INCREMENTAL) +GC_COND_LOG_PRINTF("Marked from %lu dirty pages\n", +(unsigned long)GC_n_rescuing_pages); +#endif +GC_push_roots(FALSE,cold_gc_frame); +GC_objects_are_marked=TRUE; +if (GC_mark_state!=MS_INVALID){ +GC_mark_state=MS_ROOTS_PUSHED; +} +} +} +break; +case MS_PUSH_UNCOLLECTABLE: +if ((word)GC_mark_stack_top +>=(word)(GC_mark_stack+GC_mark_stack_size/4)){ +#ifdef PARALLEL_MARK +if (GC_parallel)GC_mark_stack_too_small=TRUE; +#endif +MARK_FROM_MARK_STACK(); +break; +} else { +GC_scan_ptr=GC_push_next_marked_uncollectable(GC_scan_ptr); +if (NULL==GC_scan_ptr){ +GC_push_roots(TRUE,cold_gc_frame); +GC_objects_are_marked=TRUE; +if (GC_mark_state!=MS_INVALID){ +GC_mark_state=MS_ROOTS_PUSHED; +} +} +} +break; +case MS_ROOTS_PUSHED: +#ifdef PARALLEL_MARK +if (GC_parallel&&!GC_parallel_mark_disabled){ +GC_do_parallel_mark(); +GC_ASSERT((word)GC_mark_stack_top < (word)GC_first_nonempty); +GC_mark_stack_top=GC_mark_stack - 1; +if (GC_mark_stack_too_small){ +alloc_mark_stack(2*GC_mark_stack_size); +} +if (GC_mark_state==MS_ROOTS_PUSHED){ +GC_mark_state=MS_NONE; +return(TRUE); +} +break; +} +#endif +if ((word)GC_mark_stack_top>=(word)GC_mark_stack){ +MARK_FROM_MARK_STACK(); +break; +} else { +GC_mark_state=MS_NONE; +if (GC_mark_stack_too_small){ +alloc_mark_stack(2*GC_mark_stack_size); +} +return(TRUE); +} +case MS_INVALID: +case MS_PARTIALLY_INVALID: +if (!GC_objects_are_marked){ +GC_mark_state=MS_PUSH_UNCOLLECTABLE; +break; +} +if ((word)GC_mark_stack_top>=(word)GC_mark_stack){ +MARK_FROM_MARK_STACK(); +break; +} +if (NULL==GC_scan_ptr&&GC_mark_state==MS_INVALID){ +if (GC_mark_stack_too_small){ +alloc_mark_stack(2*GC_mark_stack_size); +} +GC_mark_state=MS_PARTIALLY_INVALID; +} +GC_scan_ptr=GC_push_next_marked(GC_scan_ptr); +if (NULL==GC_scan_ptr&&GC_mark_state==MS_PARTIALLY_INVALID){ +GC_push_roots(TRUE,cold_gc_frame); +GC_objects_are_marked=TRUE; +if (GC_mark_state!=MS_INVALID){ +GC_mark_state=MS_ROOTS_PUSHED; +} +} +break; +default: +ABORT("GC_mark_some:bad state"); +} +return(FALSE); +} +#ifdef WRAP_MARK_SOME +#if (defined(MSWIN32)||defined(MSWINCE))&&defined(__GNUC__) +typedef struct { +EXCEPTION_REGISTRATION ex_reg; +void*alt_path; +} ext_ex_regn; +static EXCEPTION_DISPOSITION mark_ex_handler( +struct _EXCEPTION_RECORD*ex_rec, +void*est_frame, +struct _CONTEXT*context, +void*disp_ctxt GC_ATTR_UNUSED) +{ +if (ex_rec->ExceptionCode==STATUS_ACCESS_VIOLATION){ +ext_ex_regn*xer=(ext_ex_regn*)est_frame; +context->Esp=context->Ebp; +context->Ebp=*((DWORD*)context->Esp); +context->Esp=context->Esp - 8; +context->Eip=(DWORD)(xer->alt_path); +return ExceptionContinueExecution; +} else { +return ExceptionContinueSearch; +} +} +#endif +GC_INNER GC_bool GC_mark_some(ptr_t cold_gc_frame) +{ +GC_bool ret_val; +#if defined(MSWIN32)||defined(MSWINCE) +#ifndef __GNUC__ +__try { +ret_val=GC_mark_some_inner(cold_gc_frame); +} __except (GetExceptionCode()==EXCEPTION_ACCESS_VIOLATION? +EXCEPTION_EXECUTE_HANDLER:EXCEPTION_CONTINUE_SEARCH){ +goto handle_ex; +} +#if defined(GC_WIN32_THREADS)&&!defined(GC_PTHREADS) +if (GC_started_thread_while_stopped()) +goto handle_thr_start; +#endif +rm_handler: +return ret_val; +#else +ext_ex_regn er; +#if GC_GNUC_PREREQ(4,7)||GC_CLANG_PREREQ(3,3) +#pragma GCC diagnostic push +#if defined(__clang__)||GC_GNUC_PREREQ(6,4) +#pragma GCC diagnostic ignored "-Wpedantic" +#else +#pragma GCC diagnostic ignored "-pedantic" +#endif +er.alt_path=&&handle_ex; +#pragma GCC diagnostic pop +#elif!defined(CPPCHECK) +er.alt_path=&&handle_ex; +#endif +er.ex_reg.handler=mark_ex_handler; +__asm__ __volatile__ ("movl %%fs:0,%0":"=r" (er.ex_reg.prev)); +__asm__ __volatile__ ("movl %0,%%fs:0"::"r" (&er)); +ret_val=GC_mark_some_inner(cold_gc_frame); +if (er.alt_path==0) +goto handle_ex; +#if defined(GC_WIN32_THREADS)&&!defined(GC_PTHREADS) +if (GC_started_thread_while_stopped()) +goto handle_thr_start; +#endif +rm_handler: +__asm__ __volatile__ ("mov %0,%%fs:0"::"r" (er.ex_reg.prev)); +return ret_val; +#endif +#else +#ifndef DEFAULT_VDB +if (GC_auto_incremental){ +WARN("Incremental GC incompatible with/proc roots\n",0); +} +#endif +GC_setup_temporary_fault_handler(); +if(SETJMP(GC_jmp_buf)!=0)goto handle_ex; +ret_val=GC_mark_some_inner(cold_gc_frame); +rm_handler: +GC_reset_fault_handler(); +return ret_val; +#endif +handle_ex: +{ +static word warned_gc_no; +if (warned_gc_no!=GC_gc_no){ +WARN("Caught ACCESS_VIOLATION in marker;" +" memory mapping disappeared\n",0); +warned_gc_no=GC_gc_no; +} +} +#if (defined(MSWIN32)||defined(MSWINCE))&&defined(GC_WIN32_THREADS)&&!defined(GC_PTHREADS) +handle_thr_start: +#endif +#ifdef REGISTER_LIBRARIES_EARLY +START_WORLD(); +GC_cond_register_dynamic_libraries(); +STOP_WORLD(); +#endif +GC_invalidate_mark_state(); +GC_scan_ptr=NULL; +ret_val=FALSE; +goto rm_handler; +} +#endif +GC_INNER void GC_invalidate_mark_state(void) +{ +GC_mark_state=MS_INVALID; +GC_mark_stack_top=GC_mark_stack-1; +} +GC_INNER mse*GC_signal_mark_stack_overflow(mse*msp) +{ +GC_mark_state=MS_INVALID; +#ifdef PARALLEL_MARK +if (!GC_parallel) +GC_mark_stack_too_small=TRUE; +#else +GC_mark_stack_too_small=TRUE; +#endif +GC_COND_LOG_PRINTF("Mark stack overflow;current size=%lu entries\n", +(unsigned long)GC_mark_stack_size); +return(msp - GC_MARK_STACK_DISCARDS); +} +GC_ATTR_NO_SANITIZE_ADDR GC_ATTR_NO_SANITIZE_MEMORY GC_ATTR_NO_SANITIZE_THREAD +GC_INNER mse*GC_mark_from(mse*mark_stack_top,mse*mark_stack, +mse*mark_stack_limit) +{ +signed_word credit=HBLKSIZE; +ptr_t current_p; +word current; +ptr_t limit=0; +word descr; +ptr_t greatest_ha=(ptr_t)GC_greatest_plausible_heap_addr; +ptr_t least_ha=(ptr_t)GC_least_plausible_heap_addr; +DECLARE_HDR_CACHE; +#define SPLIT_RANGE_WORDS 128 +GC_objects_are_marked=TRUE; +INIT_HDR_CACHE; +#ifdef OS2 +while ((word)mark_stack_top>=(word)mark_stack&&credit>=0) +#else +while (((((word)mark_stack_top - (word)mark_stack)|(word)credit) +&SIGNB)==0) +#endif +{ +current_p=mark_stack_top->mse_start; +descr=mark_stack_top->mse_descr.w; +retry: +if (descr&((~(WORDS_TO_BYTES(SPLIT_RANGE_WORDS)- 1))|GC_DS_TAGS)){ +word tag=descr&GC_DS_TAGS; +GC_STATIC_ASSERT(GC_DS_TAGS==0x3); +switch(tag){ +case GC_DS_LENGTH: +GC_ASSERT(descr < (word)GC_greatest_plausible_heap_addr +- (word)GC_least_plausible_heap_addr +||(word)(current_p+descr) +<=(word)GC_least_plausible_heap_addr +||(word)current_p>=(word)GC_greatest_plausible_heap_addr); +#ifdef PARALLEL_MARK +#define SHARE_BYTES 2048 +if (descr > SHARE_BYTES&&GC_parallel +&&(word)mark_stack_top < (word)(mark_stack_limit - 1)){ +word new_size=(descr/2)&~(word)(sizeof(word)-1); +mark_stack_top->mse_start=current_p; +mark_stack_top->mse_descr.w=new_size+sizeof(word); +mark_stack_top++; +#ifdef ENABLE_TRACE +if ((word)GC_trace_addr>=(word)current_p +&&(word)GC_trace_addr < (word)(current_p+descr)){ +GC_log_printf("GC #%u:large section;start %p,len %lu," +" splitting (parallel)at %p\n", +(unsigned)GC_gc_no,(void*)current_p, +(unsigned long)descr, +(void*)(current_p+new_size)); +} +#endif +current_p+=new_size; +descr-=new_size; +goto retry; +} +#endif +mark_stack_top->mse_start= +limit=current_p+WORDS_TO_BYTES(SPLIT_RANGE_WORDS-1); +mark_stack_top->mse_descr.w= +descr - WORDS_TO_BYTES(SPLIT_RANGE_WORDS-1); +#ifdef ENABLE_TRACE +if ((word)GC_trace_addr>=(word)current_p +&&(word)GC_trace_addr < (word)(current_p+descr)){ +GC_log_printf("GC #%u:large section;start %p,len %lu," +" splitting at %p\n", +(unsigned)GC_gc_no,(void*)current_p, +(unsigned long)descr,(void*)limit); +} +#endif +limit+=sizeof(word)- ALIGNMENT; +break; +case GC_DS_BITMAP: +mark_stack_top--; +#ifdef ENABLE_TRACE +if ((word)GC_trace_addr>=(word)current_p +&&(word)GC_trace_addr < (word)(current_p ++WORDS_TO_BYTES(WORDSZ-2))){ +GC_log_printf("GC #%u:tracing from %p bitmap descr %lu\n", +(unsigned)GC_gc_no,(void*)current_p, +(unsigned long)descr); +} +#endif +descr&=~GC_DS_TAGS; +credit-=WORDS_TO_BYTES(WORDSZ/2); +while (descr!=0){ +if ((descr&SIGNB)!=0){ +current=*(word*)current_p; +FIXUP_POINTER(current); +if (current>=(word)least_ha&¤t < (word)greatest_ha){ +PREFETCH((ptr_t)current); +#ifdef ENABLE_TRACE +if (GC_trace_addr==current_p){ +GC_log_printf("GC #%u:considering(3)%p->%p\n", +(unsigned)GC_gc_no,(void*)current_p, +(void*)current); +} +#endif +PUSH_CONTENTS((ptr_t)current,mark_stack_top, +mark_stack_limit,current_p); +} +} +descr<<=1; +current_p+=sizeof(word); +} +continue; +case GC_DS_PROC: +mark_stack_top--; +#ifdef ENABLE_TRACE +if ((word)GC_trace_addr>=(word)current_p +&&GC_base(current_p)!=0 +&&GC_base(current_p)==GC_base(GC_trace_addr)){ +GC_log_printf("GC #%u:tracing from %p,proc descr %lu\n", +(unsigned)GC_gc_no,(void*)current_p, +(unsigned long)descr); +} +#endif +credit-=GC_PROC_BYTES; +mark_stack_top=(*PROC(descr))((word*)current_p,mark_stack_top, +mark_stack_limit,ENV(descr)); +continue; +case GC_DS_PER_OBJECT: +if ((signed_word)descr>=0){ +descr=*(word*)(current_p+descr - GC_DS_PER_OBJECT); +} else { +ptr_t type_descr=*(ptr_t*)current_p; +if (EXPECT(0==type_descr,FALSE)){ +mark_stack_top--; +continue; +} +descr=*(word*)(type_descr +- ((signed_word)descr+(GC_INDIR_PER_OBJ_BIAS +- GC_DS_PER_OBJECT))); +} +if (0==descr){ +mark_stack_top--; +continue; +} +goto retry; +} +} else { +mark_stack_top--; +#ifndef SMALL_CONFIG +if (descr < sizeof(word)) +continue; +#endif +#ifdef ENABLE_TRACE +if ((word)GC_trace_addr>=(word)current_p +&&(word)GC_trace_addr < (word)(current_p+descr)){ +GC_log_printf("GC #%u:small object;start %p,len %lu\n", +(unsigned)GC_gc_no,(void*)current_p, +(unsigned long)descr); +} +#endif +limit=current_p+(word)descr; +} +GC_ASSERT(!((word)current_p&(ALIGNMENT-1))); +credit-=limit - current_p; +limit-=sizeof(word); +{ +#define PREF_DIST 4 +#ifndef SMALL_CONFIG +word deferred; +for(;;){ +PREFETCH(limit - PREF_DIST*CACHE_LINE_SIZE); +GC_ASSERT((word)limit>=(word)current_p); +deferred=*(word*)limit; +FIXUP_POINTER(deferred); +limit-=ALIGNMENT; +if (deferred>=(word)least_ha&&deferred < (word)greatest_ha){ +PREFETCH((ptr_t)deferred); +break; +} +if ((word)current_p > (word)limit)goto next_object; +deferred=*(word*)limit; +FIXUP_POINTER(deferred); +limit-=ALIGNMENT; +if (deferred>=(word)least_ha&&deferred < (word)greatest_ha){ +PREFETCH((ptr_t)deferred); +break; +} +if ((word)current_p > (word)limit)goto next_object; +} +#endif +while ((word)current_p<=(word)limit){ +current=*(word*)current_p; +FIXUP_POINTER(current); +PREFETCH(current_p+PREF_DIST*CACHE_LINE_SIZE); +if (current>=(word)least_ha&¤t < (word)greatest_ha){ +PREFETCH((ptr_t)current); +#ifdef ENABLE_TRACE +if (GC_trace_addr==current_p){ +GC_log_printf("GC #%u:considering(1)%p->%p\n", +(unsigned)GC_gc_no,(void*)current_p, +(void*)current); +} +#endif +PUSH_CONTENTS((ptr_t)current,mark_stack_top, +mark_stack_limit,current_p); +} +current_p+=ALIGNMENT; +} +#ifndef SMALL_CONFIG +#ifdef ENABLE_TRACE +if (GC_trace_addr==current_p){ +GC_log_printf("GC #%u:considering(2)%p->%p\n", +(unsigned)GC_gc_no,(void*)current_p, +(void*)deferred); +} +#endif +PUSH_CONTENTS((ptr_t)deferred,mark_stack_top, +mark_stack_limit,current_p); +next_object:; +#endif +} +} +return mark_stack_top; +} +#ifdef PARALLEL_MARK +STATIC GC_bool GC_help_wanted=FALSE; +STATIC unsigned GC_helper_count=0; +STATIC unsigned GC_active_count=0; +GC_INNER word GC_mark_no=0; +#ifdef LINT2 +#define LOCAL_MARK_STACK_SIZE (HBLKSIZE/8) +#else +#define LOCAL_MARK_STACK_SIZE HBLKSIZE +#endif +GC_INNER void GC_wait_for_markers_init(void) +{ +signed_word count; +if (GC_markers_m1==0) +return; +#ifndef CAN_HANDLE_FORK +GC_ASSERT(NULL==GC_main_local_mark_stack); +#else +if (NULL==GC_main_local_mark_stack) +#endif +{ +size_t bytes_to_get= +ROUNDUP_PAGESIZE_IF_MMAP(LOCAL_MARK_STACK_SIZE*sizeof(mse)); +GC_ASSERT(GC_page_size!=0); +GC_main_local_mark_stack=(mse*)GET_MEM(bytes_to_get); +if (NULL==GC_main_local_mark_stack) +ABORT("Insufficient memory for main local_mark_stack"); +GC_add_to_our_memory((ptr_t)GC_main_local_mark_stack,bytes_to_get); +} +GC_acquire_mark_lock(); +GC_fl_builder_count+=GC_markers_m1; +count=GC_fl_builder_count; +GC_release_mark_lock(); +if (count!=0){ +GC_ASSERT(count > 0); +GC_wait_for_reclaim(); +} +} +STATIC mse*GC_steal_mark_stack(mse*low,mse*high,mse*local, +unsigned max,mse**next) +{ +mse*p; +mse*top=local - 1; +unsigned i=0; +GC_ASSERT((word)high>=(word)(low - 1) +&&(word)(high - low+1)<=GC_mark_stack_size); +for (p=low;(word)p<=(word)high&&i<=max;++p){ +word descr=(word)AO_load(&p->mse_descr.ao); +if (descr!=0){ +AO_store_release_write(&p->mse_descr.ao,0); +++top; +top->mse_descr.w=descr; +top->mse_start=p->mse_start; +GC_ASSERT((descr&GC_DS_TAGS)!=GC_DS_LENGTH +||descr < (word)GC_greatest_plausible_heap_addr +- (word)GC_least_plausible_heap_addr +||(word)(p->mse_start+descr) +<=(word)GC_least_plausible_heap_addr +||(word)p->mse_start +>=(word)GC_greatest_plausible_heap_addr); +++i; +if ((descr&GC_DS_TAGS)==GC_DS_LENGTH)i+=(int)(descr>>8); +} +} +*next=p; +return top; +} +STATIC void GC_return_mark_stack(mse*low,mse*high) +{ +mse*my_top; +mse*my_start; +size_t stack_size; +if ((word)high < (word)low)return; +stack_size=high - low+1; +GC_acquire_mark_lock(); +my_top=GC_mark_stack_top; +my_start=my_top+1; +if ((word)(my_start - GC_mark_stack+stack_size) +> (word)GC_mark_stack_size){ +GC_COND_LOG_PRINTF("No room to copy back mark stack\n"); +GC_mark_state=MS_INVALID; +GC_mark_stack_too_small=TRUE; +} else { +BCOPY(low,my_start,stack_size*sizeof(mse)); +GC_ASSERT((mse*)AO_load((volatile AO_t*)(&GC_mark_stack_top)) +==my_top); +AO_store_release_write((volatile AO_t*)(&GC_mark_stack_top), +(AO_t)(my_top+stack_size)); +} +GC_release_mark_lock(); +GC_notify_all_marker(); +} +#ifndef N_LOCAL_ITERS +#define N_LOCAL_ITERS 1 +#endif +static GC_bool has_inactive_helpers(void) +{ +GC_bool res; +GC_acquire_mark_lock(); +res=GC_active_count < GC_helper_count; +GC_release_mark_lock(); +return res; +} +STATIC void GC_do_local_mark(mse*local_mark_stack,mse*local_top) +{ +unsigned n; +for (;;){ +for (n=0;n < N_LOCAL_ITERS;++n){ +local_top=GC_mark_from(local_top,local_mark_stack, +local_mark_stack+LOCAL_MARK_STACK_SIZE); +if ((word)local_top < (word)local_mark_stack)return; +if ((word)(local_top - local_mark_stack) +>=LOCAL_MARK_STACK_SIZE/2){ +GC_return_mark_stack(local_mark_stack,local_top); +return; +} +} +if ((word)AO_load((volatile AO_t*)&GC_mark_stack_top) +< (word)AO_load(&GC_first_nonempty) +&&(word)local_top > (word)(local_mark_stack+1) +&&has_inactive_helpers()){ +mse*new_bottom=local_mark_stack ++(local_top - local_mark_stack)/2; +GC_ASSERT((word)new_bottom > (word)local_mark_stack +&&(word)new_bottom < (word)local_top); +GC_return_mark_stack(local_mark_stack,new_bottom - 1); +memmove(local_mark_stack,new_bottom, +(local_top - new_bottom+1)*sizeof(mse)); +local_top-=(new_bottom - local_mark_stack); +} +} +} +#ifndef ENTRIES_TO_GET +#define ENTRIES_TO_GET 5 +#endif +STATIC void GC_mark_local(mse*local_mark_stack,int id) +{ +mse*my_first_nonempty; +GC_active_count++; +my_first_nonempty=(mse*)AO_load(&GC_first_nonempty); +GC_ASSERT((word)GC_mark_stack<=(word)my_first_nonempty); +GC_ASSERT((word)my_first_nonempty +<=(word)AO_load((volatile AO_t*)&GC_mark_stack_top)+sizeof(mse)); +GC_VERBOSE_LOG_PRINTF("Starting mark helper %d\n",id); +GC_release_mark_lock(); +for (;;){ +size_t n_on_stack; +unsigned n_to_get; +mse*my_top; +mse*local_top; +mse*global_first_nonempty=(mse*)AO_load(&GC_first_nonempty); +GC_ASSERT((word)my_first_nonempty>=(word)GC_mark_stack&& +(word)my_first_nonempty<= +(word)AO_load((volatile AO_t*)&GC_mark_stack_top) ++sizeof(mse)); +GC_ASSERT((word)global_first_nonempty>=(word)GC_mark_stack); +if ((word)my_first_nonempty < (word)global_first_nonempty){ +my_first_nonempty=global_first_nonempty; +} else if ((word)global_first_nonempty < (word)my_first_nonempty){ +(void)AO_compare_and_swap(&GC_first_nonempty, +(AO_t)global_first_nonempty, +(AO_t)my_first_nonempty); +} +my_top=(mse*)AO_load_acquire((volatile AO_t*)(&GC_mark_stack_top)); +if ((word)my_top < (word)my_first_nonempty){ +GC_acquire_mark_lock(); +my_top=GC_mark_stack_top; +n_on_stack=my_top - my_first_nonempty+1; +if (0==n_on_stack){ +GC_active_count--; +GC_ASSERT(GC_active_count<=GC_helper_count); +if (0==GC_active_count)GC_notify_all_marker(); +while (GC_active_count > 0 +&&(word)AO_load(&GC_first_nonempty) +> (word)GC_mark_stack_top){ +GC_wait_marker(); +} +if (GC_active_count==0 +&&(word)AO_load(&GC_first_nonempty) +> (word)GC_mark_stack_top){ +GC_bool need_to_notify=FALSE; +GC_helper_count--; +if (0==GC_helper_count)need_to_notify=TRUE; +GC_VERBOSE_LOG_PRINTF("Finished mark helper %d\n",id); +if (need_to_notify)GC_notify_all_marker(); +return; +} +GC_active_count++; +GC_ASSERT(GC_active_count > 0); +GC_release_mark_lock(); +continue; +} else { +GC_release_mark_lock(); +} +} else { +n_on_stack=my_top - my_first_nonempty+1; +} +n_to_get=ENTRIES_TO_GET; +if (n_on_stack < 2*ENTRIES_TO_GET)n_to_get=1; +local_top=GC_steal_mark_stack(my_first_nonempty,my_top, +local_mark_stack,n_to_get, +&my_first_nonempty); +GC_ASSERT((word)my_first_nonempty>=(word)GC_mark_stack&& +(word)my_first_nonempty<= +(word)AO_load((volatile AO_t*)&GC_mark_stack_top) ++sizeof(mse)); +GC_do_local_mark(local_mark_stack,local_top); +} +} +STATIC void GC_do_parallel_mark(void) +{ +GC_acquire_mark_lock(); +GC_ASSERT(I_HOLD_LOCK()); +if (GC_help_wanted||GC_active_count!=0||GC_helper_count!=0) +ABORT("Tried to start parallel mark in bad state"); +GC_VERBOSE_LOG_PRINTF("Starting marking for mark phase number %lu\n", +(unsigned long)GC_mark_no); +GC_first_nonempty=(AO_t)GC_mark_stack; +GC_active_count=0; +GC_helper_count=1; +GC_help_wanted=TRUE; +GC_notify_all_marker(); +GC_mark_local(GC_main_local_mark_stack,0); +GC_help_wanted=FALSE; +while (GC_helper_count > 0){ +GC_wait_marker(); +} +GC_VERBOSE_LOG_PRINTF("Finished marking for mark phase number %lu\n", +(unsigned long)GC_mark_no); +GC_mark_no++; +GC_release_mark_lock(); +GC_notify_all_marker(); +} +GC_INNER void GC_help_marker(word my_mark_no) +{ +#define my_id my_id_mse.mse_descr.w +mse my_id_mse; +mse local_mark_stack[LOCAL_MARK_STACK_SIZE]; +GC_ASSERT(GC_parallel); +while (GC_mark_no < my_mark_no +||(!GC_help_wanted&&GC_mark_no==my_mark_no)){ +GC_wait_marker(); +} +my_id=GC_helper_count; +if (GC_mark_no!=my_mark_no||my_id > (unsigned)GC_markers_m1){ +return; +} +GC_helper_count=(unsigned)my_id+1; +GC_mark_local(local_mark_stack,(int)my_id); +#undef my_id +} +#endif +GC_INNER void GC_scratch_recycle_inner(void*ptr,size_t bytes) +{ +if (ptr!=NULL){ +size_t page_offset=(word)ptr&(GC_page_size - 1); +size_t displ=0; +size_t recycled_bytes; +GC_ASSERT(bytes!=0); +GC_ASSERT(GC_page_size!=0); +if (page_offset!=0) +displ=GC_page_size - page_offset; +recycled_bytes=(bytes - displ)&~(GC_page_size - 1); +GC_COND_LOG_PRINTF("Recycle %lu scratch-allocated bytes at %p\n", +(unsigned long)recycled_bytes,ptr); +if (recycled_bytes > 0) +GC_add_to_heap((struct hblk*)((word)ptr+displ),recycled_bytes); +} +} +static void alloc_mark_stack(size_t n) +{ +mse*new_stack=(mse*)GC_scratch_alloc(n*sizeof(struct GC_ms_entry)); +#ifdef GWW_VDB +static GC_bool GC_incremental_at_stack_alloc=FALSE; +GC_bool recycle_old=!GC_auto_incremental +||GC_incremental_at_stack_alloc; +GC_incremental_at_stack_alloc=GC_auto_incremental; +#else +#define recycle_old TRUE +#endif +GC_mark_stack_too_small=FALSE; +if (GC_mark_stack!=NULL){ +if (new_stack!=0){ +if (recycle_old){ +GC_scratch_recycle_inner(GC_mark_stack, +GC_mark_stack_size*sizeof(struct GC_ms_entry)); +} +GC_mark_stack=new_stack; +GC_mark_stack_size=n; +GC_mark_stack_limit=new_stack+n; +GC_COND_LOG_PRINTF("Grew mark stack to %lu frames\n", +(unsigned long)GC_mark_stack_size); +} else { +WARN("Failed to grow mark stack to %" WARN_PRIdPTR " frames\n",n); +} +} else if (NULL==new_stack){ +GC_err_printf("No space for mark stack\n"); +EXIT(); +} else { +GC_mark_stack=new_stack; +GC_mark_stack_size=n; +GC_mark_stack_limit=new_stack+n; +} +GC_mark_stack_top=GC_mark_stack-1; +} +GC_INNER void GC_mark_init(void) +{ +alloc_mark_stack(INITIAL_MARK_STACK_SIZE); +} +GC_API void GC_CALL GC_push_all(void*bottom,void*top) +{ +word length; +bottom=(void*)(((word)bottom+ALIGNMENT-1)&~(ALIGNMENT-1)); +top=(void*)((word)top&~(ALIGNMENT-1)); +if ((word)bottom>=(word)top)return; +GC_mark_stack_top++; +if ((word)GC_mark_stack_top>=(word)GC_mark_stack_limit){ +ABORT("Unexpected mark stack overflow"); +} +length=(word)top - (word)bottom; +#if GC_DS_TAGS > ALIGNMENT - 1 +length+=GC_DS_TAGS; +length&=~GC_DS_TAGS; +#endif +GC_mark_stack_top->mse_start=(ptr_t)bottom; +GC_mark_stack_top->mse_descr.w=length; +} +#ifndef GC_DISABLE_INCREMENTAL +STATIC void GC_push_selected(ptr_t bottom,ptr_t top, +GC_bool (*dirty_fn)(struct hblk*)) +{ +struct hblk*h; +bottom=(ptr_t)(((word)bottom+ALIGNMENT-1)&~(ALIGNMENT-1)); +top=(ptr_t)(((word)top)&~(ALIGNMENT-1)); +if ((word)bottom>=(word)top)return; +h=HBLKPTR(bottom+HBLKSIZE); +if ((word)top<=(word)h){ +if ((*dirty_fn)(h-1)){ +GC_push_all(bottom,top); +} +return; +} +if ((*dirty_fn)(h-1)){ +if ((word)(GC_mark_stack_top - GC_mark_stack) +> 3*GC_mark_stack_size/4){ +GC_push_all(bottom,top); +return; +} +GC_push_all(bottom,h); +} +while ((word)(h+1)<=(word)top){ +if ((*dirty_fn)(h)){ +if ((word)(GC_mark_stack_top - GC_mark_stack) +> 3*GC_mark_stack_size/4){ +GC_push_all(h,top); +return; +} else { +GC_push_all(h,h+1); +} +} +h++; +} +if ((ptr_t)h!=top&&(*dirty_fn)(h)){ +GC_push_all(h,top); +} +} +GC_API void GC_CALL GC_push_conditional(void*bottom,void*top,int all) +{ +if (!all){ +GC_push_selected((ptr_t)bottom,(ptr_t)top,GC_page_was_dirty); +} else { +#ifdef PROC_VDB +if (GC_auto_incremental){ +GC_push_selected((ptr_t)bottom,(ptr_t)top,GC_page_was_ever_dirty); +} else +#endif +{ +GC_push_all(bottom,top); +} +} +} +#else +GC_API void GC_CALL GC_push_conditional(void*bottom,void*top, +int all GC_ATTR_UNUSED) +{ +GC_push_all(bottom,top); +} +#endif +#if defined(AMIGA)||defined(MACOS)||defined(GC_DARWIN_THREADS) +void GC_push_one(word p) +{ +GC_PUSH_ONE_STACK(p,MARKED_FROM_REGISTER); +} +#endif +#ifdef GC_WIN32_THREADS +GC_INNER void GC_push_many_regs(const word*regs,unsigned count) +{ +unsigned i; +for (i=0;i < count;i++) +GC_PUSH_ONE_STACK(regs[i],MARKED_FROM_REGISTER); +} +#endif +GC_API struct GC_ms_entry*GC_CALL GC_mark_and_push(void*obj, +mse*mark_stack_ptr, +mse*mark_stack_limit, +void**src GC_ATTR_UNUSED) +{ +hdr*hhdr; +PREFETCH(obj); +GET_HDR(obj,hhdr); +if ((EXPECT(IS_FORWARDING_ADDR_OR_NIL(hhdr),FALSE) +&&(!GC_all_interior_pointers +||NULL==(hhdr=GC_find_header((ptr_t)GC_base(obj))))) +||EXPECT(HBLK_IS_FREE(hhdr),FALSE)){ +GC_ADD_TO_BLACK_LIST_NORMAL(obj,(ptr_t)src); +return mark_stack_ptr; +} +return GC_push_contents_hdr((ptr_t)obj,mark_stack_ptr,mark_stack_limit, +(ptr_t)src,hhdr,TRUE); +} +#if defined(PRINT_BLACK_LIST)||defined(KEEP_BACK_PTRS) +GC_INNER void GC_mark_and_push_stack(ptr_t p,ptr_t source) +#else +GC_INNER void GC_mark_and_push_stack(ptr_t p) +#define source ((ptr_t)0) +#endif +{ +hdr*hhdr; +ptr_t r=p; +PREFETCH(p); +GET_HDR(p,hhdr); +if (EXPECT(IS_FORWARDING_ADDR_OR_NIL(hhdr),FALSE)){ +if (NULL==hhdr +||(r=(ptr_t)GC_base(p))==NULL +||(hhdr=HDR(r))==NULL){ +GC_ADD_TO_BLACK_LIST_STACK(p,source); +return; +} +} +if (EXPECT(HBLK_IS_FREE(hhdr),FALSE)){ +GC_ADD_TO_BLACK_LIST_NORMAL(p,source); +return; +} +#ifdef THREADS +GC_dirty(p); +#endif +GC_mark_stack_top=GC_push_contents_hdr(r,GC_mark_stack_top, +GC_mark_stack_limit, +source,hhdr,FALSE); +} +#undef source +#ifdef TRACE_BUF +#ifndef TRACE_ENTRIES +#define TRACE_ENTRIES 1000 +#endif +struct trace_entry { +char*kind; +word gc_no; +word bytes_allocd; +word arg1; +word arg2; +} GC_trace_buf[TRACE_ENTRIES]={ { NULL,0,0,0,0 } }; +void GC_add_trace_entry(char*kind,word arg1,word arg2) +{ +GC_trace_buf[GC_trace_buf_ptr].kind=kind; +GC_trace_buf[GC_trace_buf_ptr].gc_no=GC_gc_no; +GC_trace_buf[GC_trace_buf_ptr].bytes_allocd=GC_bytes_allocd; +GC_trace_buf[GC_trace_buf_ptr].arg1=arg1^0x80000000; +GC_trace_buf[GC_trace_buf_ptr].arg2=arg2^0x80000000; +GC_trace_buf_ptr++; +if (GC_trace_buf_ptr>=TRACE_ENTRIES)GC_trace_buf_ptr=0; +} +GC_API void GC_CALL GC_print_trace_inner(word gc_no) +{ +int i; +for (i=GC_trace_buf_ptr-1;i!=GC_trace_buf_ptr;i--){ +struct trace_entry*p; +if (i < 0)i=TRACE_ENTRIES-1; +p=GC_trace_buf+i; +if (p->gc_no < gc_no||p->kind==0){ +return; +} +GC_printf("Trace:%s (gc:%u,bytes:%lu)0x%lX,0x%lX\n", +p->kind,(unsigned)p->gc_no, +(unsigned long)p->bytes_allocd, +(long)p->arg1^0x80000000L,(long)p->arg2^0x80000000L); +} +GC_printf("Trace incomplete\n"); +} +GC_API void GC_CALL GC_print_trace(word gc_no) +{ +DCL_LOCK_STATE; +LOCK(); +GC_print_trace_inner(gc_no); +UNLOCK(); +} +#endif +GC_ATTR_NO_SANITIZE_ADDR GC_ATTR_NO_SANITIZE_MEMORY GC_ATTR_NO_SANITIZE_THREAD +GC_API void GC_CALL GC_push_all_eager(void*bottom,void*top) +{ +word*b=(word*)(((word)bottom+ALIGNMENT-1)&~(ALIGNMENT-1)); +word*t=(word*)(((word)top)&~(ALIGNMENT-1)); +REGISTER word*p; +REGISTER word*lim; +REGISTER ptr_t greatest_ha=(ptr_t)GC_greatest_plausible_heap_addr; +REGISTER ptr_t least_ha=(ptr_t)GC_least_plausible_heap_addr; +#define GC_greatest_plausible_heap_addr greatest_ha +#define GC_least_plausible_heap_addr least_ha +if (top==0)return; +lim=t - 1; +for (p=b;(word)p<=(word)lim; +p=(word*)(((ptr_t)p)+ALIGNMENT)){ +REGISTER word q=*p; +GC_PUSH_ONE_STACK(q,p); +} +#undef GC_greatest_plausible_heap_addr +#undef GC_least_plausible_heap_addr +} +GC_INNER void GC_push_all_stack(ptr_t bottom,ptr_t top) +{ +#ifndef NEED_FIXUP_POINTER +if (GC_all_interior_pointers +#if defined(THREADS)&&defined(MPROTECT_VDB) +&&!GC_auto_incremental +#endif +&&(word)GC_mark_stack_top +< (word)(GC_mark_stack_limit - INITIAL_MARK_STACK_SIZE/8)){ +GC_push_all(bottom,top); +} else +#endif +{ +GC_push_all_eager(bottom,top); +} +} +#if defined(WRAP_MARK_SOME)&&defined(PARALLEL_MARK) +GC_ATTR_NO_SANITIZE_ADDR GC_ATTR_NO_SANITIZE_MEMORY +GC_ATTR_NO_SANITIZE_THREAD +GC_INNER void GC_push_conditional_eager(void*bottom,void*top, +GC_bool all) +{ +word*b=(word*)(((word)bottom+ALIGNMENT-1)&~(ALIGNMENT-1)); +word*t=(word*)(((word)top)&~(ALIGNMENT-1)); +REGISTER word*p; +REGISTER word*lim; +REGISTER ptr_t greatest_ha=(ptr_t)GC_greatest_plausible_heap_addr; +REGISTER ptr_t least_ha=(ptr_t)GC_least_plausible_heap_addr; +#define GC_greatest_plausible_heap_addr greatest_ha +#define GC_least_plausible_heap_addr least_ha +if (top==NULL) +return; +(void)all; +lim=t - 1; +for (p=b;(word)p<=(word)lim;p=(word*)((ptr_t)p+ALIGNMENT)){ +REGISTER word q=*p; +GC_PUSH_ONE_HEAP(q,p,GC_mark_stack_top); +} +#undef GC_greatest_plausible_heap_addr +#undef GC_least_plausible_heap_addr +} +#endif +#if!defined(SMALL_CONFIG)&&!defined(USE_MARK_BYTES)&&defined(MARK_BIT_PER_GRANULE) +#if GC_GRANULE_WORDS==1 +#define USE_PUSH_MARKED_ACCELERATORS +#define PUSH_GRANULE(q)do { word qcontents=(q)[0];GC_PUSH_ONE_HEAP(qcontents,q,GC_mark_stack_top);} while (0) +#elif GC_GRANULE_WORDS==2 +#define USE_PUSH_MARKED_ACCELERATORS +#define PUSH_GRANULE(q)do { word qcontents=(q)[0];GC_PUSH_ONE_HEAP(qcontents,q,GC_mark_stack_top);qcontents=(q)[1];GC_PUSH_ONE_HEAP(qcontents,(q)+1,GC_mark_stack_top);} while (0) +#elif GC_GRANULE_WORDS==4 +#define USE_PUSH_MARKED_ACCELERATORS +#define PUSH_GRANULE(q)do { word qcontents=(q)[0];GC_PUSH_ONE_HEAP(qcontents,q,GC_mark_stack_top);qcontents=(q)[1];GC_PUSH_ONE_HEAP(qcontents,(q)+1,GC_mark_stack_top);qcontents=(q)[2];GC_PUSH_ONE_HEAP(qcontents,(q)+2,GC_mark_stack_top);qcontents=(q)[3];GC_PUSH_ONE_HEAP(qcontents,(q)+3,GC_mark_stack_top);} while (0) +#endif +#endif +#ifdef USE_PUSH_MARKED_ACCELERATORS +STATIC void GC_push_marked1(struct hblk*h,hdr*hhdr) +{ +word*mark_word_addr=&(hhdr->hb_marks[0]); +word*p; +word*plim; +ptr_t greatest_ha=(ptr_t)GC_greatest_plausible_heap_addr; +ptr_t least_ha=(ptr_t)GC_least_plausible_heap_addr; +mse*mark_stack_top=GC_mark_stack_top; +mse*mark_stack_limit=GC_mark_stack_limit; +#undef GC_mark_stack_top +#undef GC_mark_stack_limit +#define GC_mark_stack_top mark_stack_top +#define GC_mark_stack_limit mark_stack_limit +#define GC_greatest_plausible_heap_addr greatest_ha +#define GC_least_plausible_heap_addr least_ha +p=(word*)(h->hb_body); +plim=(word*)(((word)h)+HBLKSIZE); +while ((word)p < (word)plim){ +word mark_word=*mark_word_addr++; +word*q=p; +while(mark_word!=0){ +if (mark_word&1){ +PUSH_GRANULE(q); +} +q+=GC_GRANULE_WORDS; +mark_word>>=1; +} +p+=WORDSZ*GC_GRANULE_WORDS; +} +#undef GC_greatest_plausible_heap_addr +#undef GC_least_plausible_heap_addr +#undef GC_mark_stack_top +#undef GC_mark_stack_limit +#define GC_mark_stack_limit GC_arrays._mark_stack_limit +#define GC_mark_stack_top GC_arrays._mark_stack_top +GC_mark_stack_top=mark_stack_top; +} +#ifndef UNALIGNED_PTRS +STATIC void GC_push_marked2(struct hblk*h,hdr*hhdr) +{ +word*mark_word_addr=&(hhdr->hb_marks[0]); +word*p; +word*plim; +ptr_t greatest_ha=(ptr_t)GC_greatest_plausible_heap_addr; +ptr_t least_ha=(ptr_t)GC_least_plausible_heap_addr; +mse*mark_stack_top=GC_mark_stack_top; +mse*mark_stack_limit=GC_mark_stack_limit; +#undef GC_mark_stack_top +#undef GC_mark_stack_limit +#define GC_mark_stack_top mark_stack_top +#define GC_mark_stack_limit mark_stack_limit +#define GC_greatest_plausible_heap_addr greatest_ha +#define GC_least_plausible_heap_addr least_ha +p=(word*)(h->hb_body); +plim=(word*)(((word)h)+HBLKSIZE); +while ((word)p < (word)plim){ +word mark_word=*mark_word_addr++; +word*q=p; +while(mark_word!=0){ +if (mark_word&1){ +PUSH_GRANULE(q); +PUSH_GRANULE(q+GC_GRANULE_WORDS); +} +q+=2*GC_GRANULE_WORDS; +mark_word>>=2; +} +p+=WORDSZ*GC_GRANULE_WORDS; +} +#undef GC_greatest_plausible_heap_addr +#undef GC_least_plausible_heap_addr +#undef GC_mark_stack_top +#undef GC_mark_stack_limit +#define GC_mark_stack_limit GC_arrays._mark_stack_limit +#define GC_mark_stack_top GC_arrays._mark_stack_top +GC_mark_stack_top=mark_stack_top; +} +#if GC_GRANULE_WORDS < 4 +STATIC void GC_push_marked4(struct hblk*h,hdr*hhdr) +{ +word*mark_word_addr=&(hhdr->hb_marks[0]); +word*p; +word*plim; +ptr_t greatest_ha=(ptr_t)GC_greatest_plausible_heap_addr; +ptr_t least_ha=(ptr_t)GC_least_plausible_heap_addr; +mse*mark_stack_top=GC_mark_stack_top; +mse*mark_stack_limit=GC_mark_stack_limit; +#undef GC_mark_stack_top +#undef GC_mark_stack_limit +#define GC_mark_stack_top mark_stack_top +#define GC_mark_stack_limit mark_stack_limit +#define GC_greatest_plausible_heap_addr greatest_ha +#define GC_least_plausible_heap_addr least_ha +p=(word*)(h->hb_body); +plim=(word*)(((word)h)+HBLKSIZE); +while ((word)p < (word)plim){ +word mark_word=*mark_word_addr++; +word*q=p; +while(mark_word!=0){ +if (mark_word&1){ +PUSH_GRANULE(q); +PUSH_GRANULE(q+GC_GRANULE_WORDS); +PUSH_GRANULE(q+2*GC_GRANULE_WORDS); +PUSH_GRANULE(q+3*GC_GRANULE_WORDS); +} +q+=4*GC_GRANULE_WORDS; +mark_word>>=4; +} +p+=WORDSZ*GC_GRANULE_WORDS; +} +#undef GC_greatest_plausible_heap_addr +#undef GC_least_plausible_heap_addr +#undef GC_mark_stack_top +#undef GC_mark_stack_limit +#define GC_mark_stack_limit GC_arrays._mark_stack_limit +#define GC_mark_stack_top GC_arrays._mark_stack_top +GC_mark_stack_top=mark_stack_top; +} +#endif +#endif +#endif +STATIC void GC_push_marked(struct hblk*h,hdr*hhdr) +{ +word sz=hhdr->hb_sz; +word descr=hhdr->hb_descr; +ptr_t p; +word bit_no; +ptr_t lim; +mse*GC_mark_stack_top_reg; +mse*mark_stack_limit=GC_mark_stack_limit; +if (( GC_DS_LENGTH)==descr)return; +if (GC_block_empty(hhdr))return; +#if!defined(GC_DISABLE_INCREMENTAL) +GC_n_rescuing_pages++; +#endif +GC_objects_are_marked=TRUE; +if (sz > MAXOBJBYTES){ +lim=h->hb_body; +} else { +lim=(ptr_t)((word)(h+1)->hb_body - sz); +} +switch(BYTES_TO_GRANULES(sz)){ +#if defined(USE_PUSH_MARKED_ACCELERATORS) +case 1: +GC_push_marked1(h,hhdr); +break; +#if!defined(UNALIGNED_PTRS) +case 2: +GC_push_marked2(h,hhdr); +break; +#if GC_GRANULE_WORDS < 4 +case 4: +GC_push_marked4(h,hhdr); +break; +#endif +#endif +#else +case 1: +#endif +default: +GC_mark_stack_top_reg=GC_mark_stack_top; +for (p=h->hb_body,bit_no=0;(word)p<=(word)lim; +p+=sz,bit_no+=MARK_BIT_OFFSET(sz)){ +if (mark_bit_from_hdr(hhdr,bit_no)){ +GC_mark_stack_top_reg=GC_push_obj(p,hhdr,GC_mark_stack_top_reg, +mark_stack_limit); +} +} +GC_mark_stack_top=GC_mark_stack_top_reg; +} +} +#ifdef ENABLE_DISCLAIM +STATIC void GC_push_unconditionally(struct hblk*h,hdr*hhdr) +{ +word sz=hhdr->hb_sz; +word descr=hhdr->hb_descr; +ptr_t p; +ptr_t lim; +mse*GC_mark_stack_top_reg; +mse*mark_stack_limit=GC_mark_stack_limit; +if (( GC_DS_LENGTH)==descr) +return; +#if!defined(GC_DISABLE_INCREMENTAL) +GC_n_rescuing_pages++; +#endif +GC_objects_are_marked=TRUE; +if (sz > MAXOBJBYTES) +lim=h->hb_body; +else +lim=(ptr_t)((word)(h+1)->hb_body - sz); +GC_mark_stack_top_reg=GC_mark_stack_top; +for (p=h->hb_body;(word)p<=(word)lim;p+=sz) +if ((*(word*)p&0x3)!=0) +GC_mark_stack_top_reg=GC_push_obj(p,hhdr,GC_mark_stack_top_reg, +mark_stack_limit); +GC_mark_stack_top=GC_mark_stack_top_reg; +} +#endif +#ifndef GC_DISABLE_INCREMENTAL +STATIC GC_bool GC_block_was_dirty(struct hblk*h,hdr*hhdr) +{ +word sz=hhdr->hb_sz; +if (sz<=MAXOBJBYTES){ +return(GC_page_was_dirty(h)); +} else { +ptr_t p=(ptr_t)h; +while ((word)p < (word)h+sz){ +if (GC_page_was_dirty((struct hblk*)p))return(TRUE); +p+=HBLKSIZE; +} +return(FALSE); +} +} +#endif +STATIC struct hblk*GC_push_next_marked(struct hblk*h) +{ +hdr*hhdr=HDR(h); +if (EXPECT(IS_FORWARDING_ADDR_OR_NIL(hhdr)||HBLK_IS_FREE(hhdr),FALSE)){ +h=GC_next_block(h,FALSE); +if (NULL==h)return NULL; +hhdr=GC_find_header((ptr_t)h); +} else { +#ifdef LINT2 +if (NULL==h)ABORT("Bad HDR()definition"); +#endif +} +GC_push_marked(h,hhdr); +return(h+OBJ_SZ_TO_BLOCKS(hhdr->hb_sz)); +} +#ifndef GC_DISABLE_INCREMENTAL +STATIC struct hblk*GC_push_next_marked_dirty(struct hblk*h) +{ +hdr*hhdr=HDR(h); +if (!GC_incremental)ABORT("Dirty bits not set up"); +for (;;){ +if (EXPECT(IS_FORWARDING_ADDR_OR_NIL(hhdr) +||HBLK_IS_FREE(hhdr),FALSE)){ +h=GC_next_block(h,FALSE); +if (NULL==h)return NULL; +hhdr=GC_find_header((ptr_t)h); +} else { +#ifdef LINT2 +if (NULL==h)ABORT("Bad HDR()definition"); +#endif +} +if (GC_block_was_dirty(h,hhdr)) +break; +h+=OBJ_SZ_TO_BLOCKS(hhdr->hb_sz); +hhdr=HDR(h); +} +#ifdef ENABLE_DISCLAIM +if ((hhdr->hb_flags&MARK_UNCONDITIONALLY)!=0){ +GC_push_unconditionally(h,hhdr); +} else +#endif +{ +GC_push_marked(h,hhdr); +} +return(h+OBJ_SZ_TO_BLOCKS(hhdr->hb_sz)); +} +#endif +STATIC struct hblk*GC_push_next_marked_uncollectable(struct hblk*h) +{ +hdr*hhdr=HDR(h); +for (;;){ +if (EXPECT(IS_FORWARDING_ADDR_OR_NIL(hhdr) +||HBLK_IS_FREE(hhdr),FALSE)){ +h=GC_next_block(h,FALSE); +if (NULL==h)return NULL; +hhdr=GC_find_header((ptr_t)h); +} else { +#ifdef LINT2 +if (NULL==h)ABORT("Bad HDR()definition"); +#endif +} +if (hhdr->hb_obj_kind==UNCOLLECTABLE){ +GC_push_marked(h,hhdr); +break; +} +#ifdef ENABLE_DISCLAIM +if ((hhdr->hb_flags&MARK_UNCONDITIONALLY)!=0){ +GC_push_unconditionally(h,hhdr); +break; +} +#endif +h+=OBJ_SZ_TO_BLOCKS(hhdr->hb_sz); +hhdr=HDR(h); +} +return(h+OBJ_SZ_TO_BLOCKS(hhdr->hb_sz)); +} +#include +int GC_no_dls=0; +#if!defined(NO_DEBUGGING)||defined(GC_ASSERTIONS) +GC_INNER word GC_compute_root_size(void) +{ +int i; +word size=0; +for (i=0;i < n_root_sets;i++){ +size+=GC_static_roots[i].r_end - GC_static_roots[i].r_start; +} +return size; +} +#endif +#if!defined(NO_DEBUGGING) +void GC_print_static_roots(void) +{ +int i; +word size; +for (i=0;i < n_root_sets;i++){ +GC_printf("From %p to %p%s\n", +(void*)GC_static_roots[i].r_start, +(void*)GC_static_roots[i].r_end, +GC_static_roots[i].r_tmp?" (temporary)":""); +} +GC_printf("GC_root_size:%lu\n",(unsigned long)GC_root_size); +if ((size=GC_compute_root_size())!=GC_root_size) +GC_err_printf("GC_root_size incorrect!!Should be:%lu\n", +(unsigned long)size); +} +#endif +#ifndef THREADS +GC_INNER GC_bool GC_is_static_root(void*p) +{ +static int last_root_set=MAX_ROOT_SETS; +int i; +if (last_root_set < n_root_sets +&&(word)p>=(word)GC_static_roots[last_root_set].r_start +&&(word)p < (word)GC_static_roots[last_root_set].r_end) +return(TRUE); +for (i=0;i < n_root_sets;i++){ +if ((word)p>=(word)GC_static_roots[i].r_start +&&(word)p < (word)GC_static_roots[i].r_end){ +last_root_set=i; +return(TRUE); +} +} +return(FALSE); +} +#endif +#if!defined(MSWIN32)&&!defined(MSWINCE)&&!defined(CYGWIN32) +GC_INLINE int rt_hash(ptr_t addr) +{ +word result=(word)addr; +#if CPP_WORDSZ > 8*LOG_RT_SIZE +result^=result>>8*LOG_RT_SIZE; +#endif +#if CPP_WORDSZ > 4*LOG_RT_SIZE +result^=result>>4*LOG_RT_SIZE; +#endif +result^=result>>2*LOG_RT_SIZE; +result^=result>>LOG_RT_SIZE; +result&=(RT_SIZE-1); +return(result); +} +GC_INNER void*GC_roots_present(ptr_t b) +{ +int h=rt_hash(b); +struct roots*p=GC_root_index[h]; +while (p!=0){ +if (p->r_start==(ptr_t)b)return(p); +p=p->r_next; +} +return NULL; +} +GC_INLINE void add_roots_to_index(struct roots*p) +{ +int h=rt_hash(p->r_start); +p->r_next=GC_root_index[h]; +GC_root_index[h]=p; +} +#endif +GC_INNER word GC_root_size=0; +GC_API void GC_CALL GC_add_roots(void*b,void*e) +{ +DCL_LOCK_STATE; +if (!EXPECT(GC_is_initialized,TRUE))GC_init(); +LOCK(); +GC_add_roots_inner((ptr_t)b,(ptr_t)e,FALSE); +UNLOCK(); +} +void GC_add_roots_inner(ptr_t b,ptr_t e,GC_bool tmp) +{ +GC_ASSERT((word)b<=(word)e); +b=(ptr_t)(((word)b+(sizeof(word)- 1))&~(word)(sizeof(word)- 1)); +e=(ptr_t)((word)e&~(word)(sizeof(word)- 1)); +if ((word)b>=(word)e)return; +#if defined(MSWIN32)||defined(MSWINCE)||defined(CYGWIN32) +{ +int i; +struct roots*old=NULL; +for (i=0;i < n_root_sets;i++){ +old=GC_static_roots+i; +if ((word)b<=(word)old->r_end +&&(word)e>=(word)old->r_start){ +if ((word)b < (word)old->r_start){ +GC_root_size+=old->r_start - b; +old->r_start=b; +} +if ((word)e > (word)old->r_end){ +GC_root_size+=e - old->r_end; +old->r_end=e; +} +old->r_tmp&=tmp; +break; +} +} +if (i < n_root_sets){ +struct roots*other; +for (i++;i < n_root_sets;i++){ +other=GC_static_roots+i; +b=other->r_start; +e=other->r_end; +if ((word)b<=(word)old->r_end +&&(word)e>=(word)old->r_start){ +if ((word)b < (word)old->r_start){ +GC_root_size+=old->r_start - b; +old->r_start=b; +} +if ((word)e > (word)old->r_end){ +GC_root_size+=e - old->r_end; +old->r_end=e; +} +old->r_tmp&=other->r_tmp; +GC_root_size-=(other->r_end - other->r_start); +other->r_start=GC_static_roots[n_root_sets-1].r_start; +other->r_end=GC_static_roots[n_root_sets-1].r_end; +n_root_sets--; +} +} +return; +} +} +#else +{ +struct roots*old=(struct roots*)GC_roots_present(b); +if (old!=0){ +if ((word)e<=(word)old->r_end){ +old->r_tmp&=tmp; +return; +} +if (old->r_tmp==tmp||!tmp){ +GC_root_size+=e - old->r_end; +old->r_end=e; +old->r_tmp=tmp; +return; +} +b=old->r_end; +} +} +#endif +if (n_root_sets==MAX_ROOT_SETS){ +ABORT("Too many root sets"); +} +#ifdef DEBUG_ADD_DEL_ROOTS +GC_log_printf("Adding data root section %d:%p .. %p%s\n", +n_root_sets,(void*)b,(void*)e, +tmp?" (temporary)":""); +#endif +GC_static_roots[n_root_sets].r_start=(ptr_t)b; +GC_static_roots[n_root_sets].r_end=(ptr_t)e; +GC_static_roots[n_root_sets].r_tmp=tmp; +#if!defined(MSWIN32)&&!defined(MSWINCE)&&!defined(CYGWIN32) +GC_static_roots[n_root_sets].r_next=0; +add_roots_to_index(GC_static_roots+n_root_sets); +#endif +GC_root_size+=e - b; +n_root_sets++; +} +GC_API void GC_CALL GC_clear_roots(void) +{ +DCL_LOCK_STATE; +if (!EXPECT(GC_is_initialized,TRUE))GC_init(); +LOCK(); +#ifdef THREADS +GC_roots_were_cleared=TRUE; +#endif +n_root_sets=0; +GC_root_size=0; +#if!defined(MSWIN32)&&!defined(MSWINCE)&&!defined(CYGWIN32) +BZERO(GC_root_index,RT_SIZE*sizeof(void*)); +#endif +#ifdef DEBUG_ADD_DEL_ROOTS +GC_log_printf("Clear all data root sections\n"); +#endif +UNLOCK(); +} +STATIC void GC_remove_root_at_pos(int i) +{ +#ifdef DEBUG_ADD_DEL_ROOTS +GC_log_printf("Remove data root section at %d:%p .. %p%s\n", +i,(void*)GC_static_roots[i].r_start, +(void*)GC_static_roots[i].r_end, +GC_static_roots[i].r_tmp?" (temporary)":""); +#endif +GC_root_size-=(GC_static_roots[i].r_end - GC_static_roots[i].r_start); +GC_static_roots[i].r_start=GC_static_roots[n_root_sets-1].r_start; +GC_static_roots[i].r_end=GC_static_roots[n_root_sets-1].r_end; +GC_static_roots[i].r_tmp=GC_static_roots[n_root_sets-1].r_tmp; +n_root_sets--; +} +#if!defined(MSWIN32)&&!defined(MSWINCE)&&!defined(CYGWIN32) +STATIC void GC_rebuild_root_index(void) +{ +int i; +BZERO(GC_root_index,RT_SIZE*sizeof(void*)); +for (i=0;i < n_root_sets;i++) +add_roots_to_index(GC_static_roots+i); +} +#endif +#if defined(DYNAMIC_LOADING)||defined(MSWIN32)||defined(MSWINCE)||defined(PCR)||defined(CYGWIN32) +STATIC void GC_remove_tmp_roots(void) +{ +int i; +#if!defined(MSWIN32)&&!defined(MSWINCE)&&!defined(CYGWIN32) +int old_n_roots=n_root_sets; +#endif +for (i=0;i < n_root_sets;){ +if (GC_static_roots[i].r_tmp){ +GC_remove_root_at_pos(i); +} else { +i++; +} +} +#if!defined(MSWIN32)&&!defined(MSWINCE)&&!defined(CYGWIN32) +if (n_root_sets < old_n_roots) +GC_rebuild_root_index(); +#endif +} +#endif +#if!defined(MSWIN32)&&!defined(MSWINCE)&&!defined(CYGWIN32) +STATIC void GC_remove_roots_inner(ptr_t b,ptr_t e); +GC_API void GC_CALL GC_remove_roots(void*b,void*e) +{ +DCL_LOCK_STATE; +if ((((word)b+(sizeof(word)- 1))&~(word)(sizeof(word)- 1))>= +((word)e&~(word)(sizeof(word)- 1))) +return; +LOCK(); +GC_remove_roots_inner((ptr_t)b,(ptr_t)e); +UNLOCK(); +} +STATIC void GC_remove_roots_inner(ptr_t b,ptr_t e) +{ +int i; +GC_bool rebuild=FALSE; +for (i=0;i < n_root_sets;){ +if ((word)GC_static_roots[i].r_start>=(word)b +&&(word)GC_static_roots[i].r_end<=(word)e){ +GC_remove_root_at_pos(i); +rebuild=TRUE; +} else { +i++; +} +} +if (rebuild) +GC_rebuild_root_index(); +} +#endif +#ifdef USE_PROC_FOR_LIBRARIES +GC_INLINE void swap_static_roots(int i,int j) +{ +ptr_t r_start=GC_static_roots[i].r_start; +ptr_t r_end=GC_static_roots[i].r_end; +GC_bool r_tmp=GC_static_roots[i].r_tmp; +GC_static_roots[i].r_start=GC_static_roots[j].r_start; +GC_static_roots[i].r_end=GC_static_roots[j].r_end; +GC_static_roots[i].r_tmp=GC_static_roots[j].r_tmp; +GC_static_roots[j].r_start=r_start; +GC_static_roots[j].r_end=r_end; +GC_static_roots[j].r_tmp=r_tmp; +} +GC_INNER void GC_remove_roots_subregion(ptr_t b,ptr_t e) +{ +int i; +GC_bool rebuild=FALSE; +GC_ASSERT(I_HOLD_LOCK()); +GC_ASSERT((word)b % sizeof(word)==0&&(word)e % sizeof(word)==0); +for (i=0;i < n_root_sets;i++){ +ptr_t r_start,r_end; +if (GC_static_roots[i].r_tmp){ +#ifdef GC_ASSERTIONS +int j; +for (j=i+1;j < n_root_sets;j++){ +GC_ASSERT(GC_static_roots[j].r_tmp); +} +#endif +break; +} +r_start=GC_static_roots[i].r_start; +r_end=GC_static_roots[i].r_end; +if (!EXPECT((word)e<=(word)r_start||(word)r_end<=(word)b,TRUE)){ +#ifdef DEBUG_ADD_DEL_ROOTS +GC_log_printf("Removing %p .. %p from root section %d (%p .. %p)\n", +(void*)b,(void*)e, +i,(void*)r_start,(void*)r_end); +#endif +if ((word)r_start < (word)b){ +GC_root_size-=r_end - b; +GC_static_roots[i].r_end=b; +if ((word)e < (word)r_end){ +int j; +if (rebuild){ +GC_rebuild_root_index(); +rebuild=FALSE; +} +GC_add_roots_inner(e,r_end,FALSE); +for (j=i+1;j < n_root_sets;j++) +if (GC_static_roots[j].r_tmp) +break; +if (j < n_root_sets-1&&!GC_static_roots[n_root_sets-1].r_tmp){ +swap_static_roots(j,n_root_sets - 1); +rebuild=TRUE; +} +} +} else { +if ((word)e < (word)r_end){ +GC_root_size-=e - r_start; +GC_static_roots[i].r_start=e; +} else { +GC_remove_root_at_pos(i); +if (i < n_root_sets - 1&&GC_static_roots[i].r_tmp +&&!GC_static_roots[i+1].r_tmp){ +int j; +for (j=i+2;j < n_root_sets;j++) +if (GC_static_roots[j].r_tmp) +break; +swap_static_roots(i,j - 1); +} +i--; +} +rebuild=TRUE; +} +} +} +if (rebuild) +GC_rebuild_root_index(); +} +#endif +#if!defined(NO_DEBUGGING) +GC_API int GC_CALL GC_is_tmp_root(void*p) +{ +static int last_root_set=MAX_ROOT_SETS; +int i; +if (last_root_set < n_root_sets +&&(word)p>=(word)GC_static_roots[last_root_set].r_start +&&(word)p < (word)GC_static_roots[last_root_set].r_end) +return GC_static_roots[last_root_set].r_tmp; +for (i=0;i < n_root_sets;i++){ +if ((word)p>=(word)GC_static_roots[i].r_start +&&(word)p < (word)GC_static_roots[i].r_end){ +last_root_set=i; +return GC_static_roots[i].r_tmp; +} +} +return(FALSE); +} +#endif +GC_INNER ptr_t GC_approx_sp(void) +{ +volatile word sp; +#if defined(S390)&&!defined(CPPCHECK)&&(__clang_major__ < 8) +sp=(word)&sp; +#elif defined(CPPCHECK)||(__GNUC__>=4&&!defined(STACK_NOT_SCANNED)) +sp=(word)__builtin_frame_address(0); +#else +sp=(word)&sp; +#endif +return((ptr_t)sp); +} +GC_API void GC_CALL GC_clear_exclusion_table(void) +{ +GC_excl_table_entries=0; +} +STATIC struct exclusion*GC_next_exclusion(ptr_t start_addr) +{ +size_t low=0; +size_t high; +GC_ASSERT(GC_excl_table_entries > 0); +high=GC_excl_table_entries - 1; +while (high > low){ +size_t mid=(low+high)>>1; +if ((word)GC_excl_table[mid].e_end<=(word)start_addr){ +low=mid+1; +} else { +high=mid; +} +} +if ((word)GC_excl_table[low].e_end<=(word)start_addr)return 0; +return GC_excl_table+low; +} +GC_INNER void GC_exclude_static_roots_inner(void*start,void*finish) +{ +struct exclusion*next; +size_t next_index; +GC_ASSERT((word)start % sizeof(word)==0); +GC_ASSERT((word)start < (word)finish); +if (0==GC_excl_table_entries){ +next=0; +} else { +next=GC_next_exclusion((ptr_t)start); +} +if (0!=next){ +size_t i; +if ((word)(next->e_start)< (word)finish){ +ABORT("Exclusion ranges overlap"); +} +if ((word)(next->e_start)==(word)finish){ +next->e_start=(ptr_t)start; +return; +} +next_index=next - GC_excl_table; +for (i=GC_excl_table_entries;i > next_index;--i){ +GC_excl_table[i]=GC_excl_table[i-1]; +} +} else { +next_index=GC_excl_table_entries; +} +if (GC_excl_table_entries==MAX_EXCLUSIONS)ABORT("Too many exclusions"); +GC_excl_table[next_index].e_start=(ptr_t)start; +GC_excl_table[next_index].e_end=(ptr_t)finish; +++GC_excl_table_entries; +} +GC_API void GC_CALL GC_exclude_static_roots(void*b,void*e) +{ +DCL_LOCK_STATE; +if (b==e)return; +b=(void*)((word)b&~(word)(sizeof(word)- 1)); +e=(void*)(((word)e+(sizeof(word)- 1))&~(word)(sizeof(word)- 1)); +if (NULL==e) +e=(void*)(~(word)(sizeof(word)- 1)); +LOCK(); +GC_exclude_static_roots_inner(b,e); +UNLOCK(); +} +#if defined(WRAP_MARK_SOME)&&defined(PARALLEL_MARK) +#define GC_PUSH_CONDITIONAL(b,t,all)(GC_parallel?GC_push_conditional_eager(b,t,all):GC_push_conditional(b,t,all)) +#elif defined(GC_DISABLE_INCREMENTAL) +#define GC_PUSH_CONDITIONAL(b,t,all)GC_push_all(b,t) +#else +#define GC_PUSH_CONDITIONAL(b,t,all)GC_push_conditional(b,t,all) +#endif +STATIC void GC_push_conditional_with_exclusions(ptr_t bottom,ptr_t top, +GC_bool all GC_ATTR_UNUSED) +{ +while ((word)bottom < (word)top){ +struct exclusion*next=GC_next_exclusion(bottom); +ptr_t excl_start; +if (0==next +||(word)(excl_start=next->e_start)>=(word)top){ +GC_PUSH_CONDITIONAL(bottom,top,all); +break; +} +if ((word)excl_start > (word)bottom) +GC_PUSH_CONDITIONAL(bottom,excl_start,all); +bottom=next->e_end; +} +} +#ifdef IA64 +GC_INNER void GC_push_all_register_sections(ptr_t bs_lo,ptr_t bs_hi, +int eager,struct GC_traced_stack_sect_s*traced_stack_sect) +{ +while (traced_stack_sect!=NULL){ +ptr_t frame_bs_lo=traced_stack_sect->backing_store_end; +GC_ASSERT((word)frame_bs_lo<=(word)bs_hi); +if (eager){ +GC_push_all_eager(frame_bs_lo,bs_hi); +} else { +GC_push_all_stack(frame_bs_lo,bs_hi); +} +bs_hi=traced_stack_sect->saved_backing_store_ptr; +traced_stack_sect=traced_stack_sect->prev; +} +GC_ASSERT((word)bs_lo<=(word)bs_hi); +if (eager){ +GC_push_all_eager(bs_lo,bs_hi); +} else { +GC_push_all_stack(bs_lo,bs_hi); +} +} +#endif +#ifdef THREADS +GC_INNER void GC_push_all_stack_sections(ptr_t lo,ptr_t hi, +struct GC_traced_stack_sect_s*traced_stack_sect) +{ +while (traced_stack_sect!=NULL){ +GC_ASSERT((word)lo HOTTER_THAN (word)traced_stack_sect); +#ifdef STACK_GROWS_UP +GC_push_all_stack((ptr_t)traced_stack_sect,lo); +#else +GC_push_all_stack(lo,(ptr_t)traced_stack_sect); +#endif +lo=traced_stack_sect->saved_stack_ptr; +GC_ASSERT(lo!=NULL); +traced_stack_sect=traced_stack_sect->prev; +} +GC_ASSERT(!((word)hi HOTTER_THAN (word)lo)); +#ifdef STACK_GROWS_UP +GC_push_all_stack(hi,lo); +#else +GC_push_all_stack(lo,hi); +#endif +} +#else +STATIC void GC_push_all_stack_partially_eager(ptr_t bottom,ptr_t top, +ptr_t cold_gc_frame) +{ +#ifndef NEED_FIXUP_POINTER +if (GC_all_interior_pointers){ +if (0==cold_gc_frame){ +GC_push_all_stack(bottom,top); +return; +} +GC_ASSERT((word)bottom<=(word)cold_gc_frame +&&(word)cold_gc_frame<=(word)top); +#ifdef STACK_GROWS_DOWN +GC_push_all(cold_gc_frame - sizeof(ptr_t),top); +GC_push_all_eager(bottom,cold_gc_frame); +#else +GC_push_all(bottom,cold_gc_frame+sizeof(ptr_t)); +GC_push_all_eager(cold_gc_frame,top); +#endif +} else +#endif +{ +GC_push_all_eager(bottom,top); +} +#ifdef TRACE_BUF +GC_add_trace_entry("GC_push_all_stack",(word)bottom,(word)top); +#endif +} +STATIC void GC_push_all_stack_part_eager_sections(ptr_t lo,ptr_t hi, +ptr_t cold_gc_frame,struct GC_traced_stack_sect_s*traced_stack_sect) +{ +GC_ASSERT(traced_stack_sect==NULL||cold_gc_frame==NULL|| +(word)cold_gc_frame HOTTER_THAN (word)traced_stack_sect); +while (traced_stack_sect!=NULL){ +GC_ASSERT((word)lo HOTTER_THAN (word)traced_stack_sect); +#ifdef STACK_GROWS_UP +GC_push_all_stack_partially_eager((ptr_t)traced_stack_sect,lo, +cold_gc_frame); +#else +GC_push_all_stack_partially_eager(lo,(ptr_t)traced_stack_sect, +cold_gc_frame); +#endif +lo=traced_stack_sect->saved_stack_ptr; +GC_ASSERT(lo!=NULL); +traced_stack_sect=traced_stack_sect->prev; +cold_gc_frame=NULL; +} +GC_ASSERT(!((word)hi HOTTER_THAN (word)lo)); +#ifdef STACK_GROWS_UP +GC_push_all_stack_partially_eager(hi,lo,cold_gc_frame); +#else +GC_push_all_stack_partially_eager(lo,hi,cold_gc_frame); +#endif +} +#endif +STATIC void GC_push_current_stack(ptr_t cold_gc_frame, +void*context GC_ATTR_UNUSED) +{ +#if defined(THREADS) +#ifdef STACK_GROWS_DOWN +GC_push_all_eager(GC_approx_sp(),cold_gc_frame); +#else +GC_push_all_eager(cold_gc_frame,GC_approx_sp()); +#endif +#else +GC_push_all_stack_part_eager_sections(GC_approx_sp(),GC_stackbottom, +cold_gc_frame,GC_traced_stack_sect); +#ifdef IA64 +{ +ptr_t bsp=GC_save_regs_ret_val; +ptr_t cold_gc_bs_pointer=bsp - 2048; +if (GC_all_interior_pointers +&&(word)cold_gc_bs_pointer > (word)BACKING_STORE_BASE){ +if (GC_traced_stack_sect!=NULL +&&(word)cold_gc_bs_pointer +< (word)GC_traced_stack_sect->backing_store_end) +cold_gc_bs_pointer= +GC_traced_stack_sect->backing_store_end; +GC_push_all_register_sections(BACKING_STORE_BASE, +cold_gc_bs_pointer,FALSE,GC_traced_stack_sect); +GC_push_all_eager(cold_gc_bs_pointer,bsp); +} else { +GC_push_all_register_sections(BACKING_STORE_BASE,bsp, +TRUE,GC_traced_stack_sect); +} +} +#endif +#endif +} +GC_INNER void (*GC_push_typed_structures)(void)=0; +GC_INNER void GC_cond_register_dynamic_libraries(void) +{ +#if (defined(DYNAMIC_LOADING)&&!defined(MSWIN_XBOX1))||defined(CYGWIN32)||defined(MSWIN32)||defined(MSWINCE)||defined(PCR) +GC_remove_tmp_roots(); +if (!GC_no_dls)GC_register_dynamic_libraries(); +#else +GC_no_dls=TRUE; +#endif +} +STATIC void GC_push_regs_and_stack(ptr_t cold_gc_frame) +{ +#ifdef THREADS +if (NULL==cold_gc_frame) +return; +#endif +GC_with_callee_saves_pushed(GC_push_current_stack,cold_gc_frame); +} +GC_INNER void GC_push_roots(GC_bool all,ptr_t cold_gc_frame GC_ATTR_UNUSED) +{ +int i; +unsigned kind; +#if!defined(REGISTER_LIBRARIES_EARLY) +GC_cond_register_dynamic_libraries(); +#endif +for (i=0;i < n_root_sets;i++){ +GC_push_conditional_with_exclusions( +GC_static_roots[i].r_start, +GC_static_roots[i].r_end,all); +} +for (kind=0;kind < GC_n_kinds;kind++){ +void*base=GC_base(GC_obj_kinds[kind].ok_freelist); +if (base!=NULL){ +GC_set_mark_bit(base); +} +} +#ifndef GC_NO_FINALIZATION +GC_push_finalizer_structures(); +#endif +#ifdef THREADS +if (GC_no_dls||GC_roots_were_cleared) +GC_push_thread_structures(); +#endif +if (GC_push_typed_structures) +GC_push_typed_structures(); +#if defined(THREAD_LOCAL_ALLOC) +if (GC_world_stopped) +GC_mark_thread_local_free_lists(); +#endif +#ifndef STACK_NOT_SCANNED +GC_push_regs_and_stack(cold_gc_frame); +#endif +if (GC_push_other_roots!=0){ +(*GC_push_other_roots)(); +} +} +#ifdef ENABLE_DISCLAIM +#endif +#include +GC_INNER signed_word GC_bytes_found=0; +#if defined(PARALLEL_MARK) +GC_INNER signed_word GC_fl_builder_count=0; +#endif +#ifndef MAX_LEAKED +#define MAX_LEAKED 40 +#endif +STATIC ptr_t GC_leaked[MAX_LEAKED]={ NULL }; +STATIC unsigned GC_n_leaked=0; +GC_INNER GC_bool GC_have_errors=FALSE; +#if!defined(EAGER_SWEEP)&&defined(ENABLE_DISCLAIM) +STATIC void GC_reclaim_unconditionally_marked(void); +#endif +GC_INLINE void GC_add_leaked(ptr_t leaked) +{ +#ifndef SHORT_DBG_HDRS +if (GC_findleak_delay_free&&!GC_check_leaked(leaked)) +return; +#endif +GC_have_errors=TRUE; +if (GC_n_leaked < MAX_LEAKED){ +GC_leaked[GC_n_leaked++]=leaked; +GC_set_mark_bit(leaked); +} +} +GC_INNER void GC_print_all_errors(void) +{ +static GC_bool printing_errors=FALSE; +GC_bool have_errors; +unsigned i,n_leaked; +ptr_t leaked[MAX_LEAKED]; +DCL_LOCK_STATE; +LOCK(); +if (printing_errors){ +UNLOCK(); +return; +} +have_errors=GC_have_errors; +printing_errors=TRUE; +n_leaked=GC_n_leaked; +if (n_leaked > 0){ +GC_ASSERT(n_leaked<=MAX_LEAKED); +BCOPY(GC_leaked,leaked,n_leaked*sizeof(ptr_t)); +GC_n_leaked=0; +BZERO(GC_leaked,n_leaked*sizeof(ptr_t)); +} +UNLOCK(); +if (GC_debugging_started){ +GC_print_all_smashed(); +} else { +have_errors=FALSE; +} +if (n_leaked > 0){ +GC_err_printf("Found %u leaked objects:\n",n_leaked); +have_errors=TRUE; +} +for (i=0;i < n_leaked;i++){ +ptr_t p=leaked[i]; +#ifndef SKIP_LEAKED_OBJECTS_PRINTING +GC_print_heap_obj(p); +#endif +GC_free(p); +} +if (have_errors +#ifndef GC_ABORT_ON_LEAK +&&GETENV("GC_ABORT_ON_LEAK")!=NULL +#endif +){ +ABORT("Leaked or smashed objects encountered"); +} +LOCK(); +printing_errors=FALSE; +UNLOCK(); +} +GC_INNER GC_bool GC_block_empty(hdr*hhdr) +{ +return (hhdr->hb_n_marks==0); +} +STATIC GC_bool GC_block_nearly_full(hdr*hhdr,word sz) +{ +return hhdr->hb_n_marks > HBLK_OBJS(sz)*7/8; +} +STATIC ptr_t GC_reclaim_clear(struct hblk*hbp,hdr*hhdr,word sz, +ptr_t list,signed_word*count) +{ +word bit_no=0; +word*p,*q,*plim; +signed_word n_bytes_found=0; +GC_ASSERT(hhdr==GC_find_header((ptr_t)hbp)); +#ifndef THREADS +GC_ASSERT(sz==hhdr->hb_sz); +#else +#endif +GC_ASSERT((sz&(BYTES_PER_WORD-1))==0); +p=(word*)(hbp->hb_body); +plim=(word*)(hbp->hb_body+HBLKSIZE - sz); +while ((word)p<=(word)plim){ +if (mark_bit_from_hdr(hhdr,bit_no)){ +p=(word*)((ptr_t)p+sz); +} else { +n_bytes_found+=sz; +obj_link(p)=list; +list=((ptr_t)p); +q=(word*)((ptr_t)p+sz); +#ifdef USE_MARK_BYTES +GC_ASSERT(!(sz&1) +&&!((word)p&(2*sizeof(word)- 1))); +p[1]=0; +p+=2; +while ((word)p < (word)q){ +CLEAR_DOUBLE(p); +p+=2; +} +#else +p++; +while ((word)p < (word)q){ +*p++=0; +} +#endif +} +bit_no+=MARK_BIT_OFFSET(sz); +} +*count+=n_bytes_found; +return(list); +} +STATIC ptr_t GC_reclaim_uninit(struct hblk*hbp,hdr*hhdr,word sz, +ptr_t list,signed_word*count) +{ +word bit_no=0; +word*p,*plim; +signed_word n_bytes_found=0; +#ifndef THREADS +GC_ASSERT(sz==hhdr->hb_sz); +#endif +p=(word*)(hbp->hb_body); +plim=(word*)((ptr_t)hbp+HBLKSIZE - sz); +while ((word)p<=(word)plim){ +if (!mark_bit_from_hdr(hhdr,bit_no)){ +n_bytes_found+=sz; +obj_link(p)=list; +list=((ptr_t)p); +} +p=(word*)((ptr_t)p+sz); +bit_no+=MARK_BIT_OFFSET(sz); +} +*count+=n_bytes_found; +return(list); +} +#ifdef ENABLE_DISCLAIM +STATIC ptr_t GC_disclaim_and_reclaim(struct hblk*hbp,hdr*hhdr,word sz, +ptr_t list,signed_word*count) +{ +word bit_no=0; +word*p,*q,*plim; +signed_word n_bytes_found=0; +struct obj_kind*ok=&GC_obj_kinds[hhdr->hb_obj_kind]; +int (GC_CALLBACK*disclaim)(void*)=ok->ok_disclaim_proc; +#ifndef THREADS +GC_ASSERT(sz==hhdr->hb_sz); +#endif +p=(word*)(hbp->hb_body); +plim=(word*)((ptr_t)p+HBLKSIZE - sz); +while ((word)p<=(word)plim){ +int marked=mark_bit_from_hdr(hhdr,bit_no); +if (!marked&&(*disclaim)(p)){ +set_mark_bit_from_hdr(hhdr,bit_no); +hhdr->hb_n_marks++; +marked=1; +} +if (marked) +p=(word*)((ptr_t)p+sz); +else { +n_bytes_found+=sz; +obj_link(p)=list; +list=((ptr_t)p); +q=(word*)((ptr_t)p+sz); +#ifdef USE_MARK_BYTES +GC_ASSERT((sz&1)==0); +GC_ASSERT(((word)p&(2*sizeof(word)- 1))==0); +p[1]=0; +p+=2; +while ((word)p < (word)q){ +CLEAR_DOUBLE(p); +p+=2; +} +#else +p++; +while ((word)p < (word)q){ +*p++=0; +} +#endif +} +bit_no+=MARK_BIT_OFFSET(sz); +} +*count+=n_bytes_found; +return list; +} +#endif +STATIC void GC_reclaim_check(struct hblk*hbp,hdr*hhdr,word sz) +{ +word bit_no; +ptr_t p,plim; +#ifndef THREADS +GC_ASSERT(sz==hhdr->hb_sz); +#endif +p=hbp->hb_body; +plim=p+HBLKSIZE - sz; +for (bit_no=0;(word)p<=(word)plim; +p+=sz,bit_no+=MARK_BIT_OFFSET(sz)){ +if (!mark_bit_from_hdr(hhdr,bit_no)){ +GC_add_leaked(p); +} +} +} +#ifdef AO_HAVE_load +#define IS_PTRFREE_SAFE(hhdr)(AO_load((volatile AO_t*)&(hhdr)->hb_descr)==0) +#else +#define IS_PTRFREE_SAFE(hhdr)((hhdr)->hb_descr==0) +#endif +GC_INNER ptr_t GC_reclaim_generic(struct hblk*hbp,hdr*hhdr,size_t sz, +GC_bool init,ptr_t list, +signed_word*count) +{ +ptr_t result; +GC_ASSERT(GC_find_header((ptr_t)hbp)==hhdr); +#ifndef GC_DISABLE_INCREMENTAL +GC_remove_protection(hbp,1,IS_PTRFREE_SAFE(hhdr)); +#endif +#ifdef ENABLE_DISCLAIM +if ((hhdr->hb_flags&HAS_DISCLAIM)!=0){ +result=GC_disclaim_and_reclaim(hbp,hhdr,sz,list,count); +} else +#endif +if (init||GC_debugging_started){ +result=GC_reclaim_clear(hbp,hhdr,sz,list,count); +} else { +GC_ASSERT(IS_PTRFREE_SAFE(hhdr)); +result=GC_reclaim_uninit(hbp,hhdr,sz,list,count); +} +if (IS_UNCOLLECTABLE(hhdr->hb_obj_kind))GC_set_hdr_marks(hhdr); +return result; +} +STATIC void GC_reclaim_small_nonempty_block(struct hblk*hbp,word sz, +GC_bool report_if_found) +{ +hdr*hhdr=HDR(hbp); +struct obj_kind*ok=&GC_obj_kinds[hhdr->hb_obj_kind]; +void**flh=&(ok->ok_freelist[BYTES_TO_GRANULES(sz)]); +hhdr->hb_last_reclaimed=(unsigned short)GC_gc_no; +if (report_if_found){ +GC_reclaim_check(hbp,hhdr,sz); +} else { +*flh=GC_reclaim_generic(hbp,hhdr,sz,ok->ok_init, +(ptr_t)(*flh),&GC_bytes_found); +} +} +#ifdef ENABLE_DISCLAIM +STATIC void GC_disclaim_and_reclaim_or_free_small_block(struct hblk*hbp) +{ +hdr*hhdr=HDR(hbp); +word sz=hhdr->hb_sz; +struct obj_kind*ok=&GC_obj_kinds[hhdr->hb_obj_kind]; +void**flh=&(ok->ok_freelist[BYTES_TO_GRANULES(sz)]); +void*flh_next; +hhdr->hb_last_reclaimed=(unsigned short)GC_gc_no; +flh_next=GC_reclaim_generic(hbp,hhdr,sz,ok->ok_init, +(ptr_t)(*flh),&GC_bytes_found); +if (hhdr->hb_n_marks) +*flh=flh_next; +else { +GC_bytes_found+=HBLKSIZE; +GC_freehblk(hbp); +} +} +#endif +STATIC void GC_reclaim_block(struct hblk*hbp,word report_if_found) +{ +hdr*hhdr=HDR(hbp); +word sz; +struct obj_kind*ok=&GC_obj_kinds[hhdr->hb_obj_kind]; +#ifdef AO_HAVE_load +sz=(word)AO_load((volatile AO_t*)&hhdr->hb_sz); +#else +sz=hhdr->hb_sz; +#endif +if( sz > MAXOBJBYTES){ +if(!mark_bit_from_hdr(hhdr,0)){ +if (report_if_found){ +GC_add_leaked((ptr_t)hbp); +} else { +word blocks; +#ifdef ENABLE_DISCLAIM +if (EXPECT(hhdr->hb_flags&HAS_DISCLAIM,0)){ +if ((*ok->ok_disclaim_proc)(hbp)){ +set_mark_bit_from_hdr(hhdr,0); +goto in_use; +} +} +#endif +blocks=OBJ_SZ_TO_BLOCKS(sz); +#if defined(CPPCHECK) +GC_noop1((word)&blocks); +#endif +if (blocks > 1){ +GC_large_allocd_bytes-=blocks*HBLKSIZE; +} +GC_bytes_found+=sz; +GC_freehblk(hbp); +} +} else { +#ifdef ENABLE_DISCLAIM +in_use: +#endif +if (IS_PTRFREE_SAFE(hhdr)){ +GC_atomic_in_use+=sz; +} else { +GC_composite_in_use+=sz; +} +} +} else { +GC_bool empty=GC_block_empty(hhdr); +#ifdef PARALLEL_MARK +GC_ASSERT(hhdr->hb_n_marks<=2*(HBLKSIZE/sz+1)+16); +#else +GC_ASSERT(sz*hhdr->hb_n_marks<=HBLKSIZE); +#endif +if (report_if_found){ +GC_reclaim_small_nonempty_block(hbp,sz, +TRUE); +} else if (empty){ +#ifdef ENABLE_DISCLAIM +if ((hhdr->hb_flags&HAS_DISCLAIM)!=0){ +GC_disclaim_and_reclaim_or_free_small_block(hbp); +} else +#endif +{ +GC_bytes_found+=HBLKSIZE; +GC_freehblk(hbp); +} +} else if (GC_find_leak||!GC_block_nearly_full(hhdr,sz)){ +struct hblk**rlh=ok->ok_reclaim_list; +if (rlh!=NULL){ +rlh+=BYTES_TO_GRANULES(sz); +hhdr->hb_next=*rlh; +*rlh=hbp; +} +} +if (IS_PTRFREE_SAFE(hhdr)){ +GC_atomic_in_use+=sz*hhdr->hb_n_marks; +} else { +GC_composite_in_use+=sz*hhdr->hb_n_marks; +} +} +} +#if!defined(NO_DEBUGGING) +struct Print_stats +{ +size_t number_of_blocks; +size_t total_bytes; +}; +#ifdef USE_MARK_BYTES +unsigned GC_n_set_marks(hdr*hhdr) +{ +unsigned result=0; +word i; +word sz=hhdr->hb_sz; +word offset=MARK_BIT_OFFSET(sz); +word limit=FINAL_MARK_BIT(sz); +for (i=0;i < limit;i+=offset){ +result+=hhdr->hb_marks[i]; +} +GC_ASSERT(hhdr->hb_marks[limit]); +return(result); +} +#else +static unsigned set_bits(word n) +{ +word m=n; +unsigned result=0; +while (m > 0){ +if (m&1)result++; +m>>=1; +} +return(result); +} +unsigned GC_n_set_marks(hdr*hhdr) +{ +unsigned result=0; +word i; +word n_mark_words; +#ifdef MARK_BIT_PER_OBJ +word n_objs=HBLK_OBJS(hhdr->hb_sz); +if (0==n_objs)n_objs=1; +n_mark_words=divWORDSZ(n_objs+WORDSZ - 1); +#else +n_mark_words=MARK_BITS_SZ; +#endif +for (i=0;i < n_mark_words - 1;i++){ +result+=set_bits(hhdr->hb_marks[i]); +} +#ifdef MARK_BIT_PER_OBJ +result+=set_bits((hhdr->hb_marks[n_mark_words - 1]) +<<(n_mark_words*WORDSZ - n_objs)); +#else +result+=set_bits(hhdr->hb_marks[n_mark_words - 1]); +#endif +return result; +} +#endif +STATIC void GC_print_block_descr(struct hblk*h, +word raw_ps) +{ +hdr*hhdr=HDR(h); +size_t bytes=hhdr->hb_sz; +struct Print_stats*ps; +unsigned n_marks=GC_n_set_marks(hhdr); +unsigned n_objs=(unsigned)HBLK_OBJS(bytes); +if (0==n_objs)n_objs=1; +if (hhdr->hb_n_marks!=n_marks){ +GC_printf("%u,%u,%u!=%u,%u\n",hhdr->hb_obj_kind,(unsigned)bytes, +(unsigned)hhdr->hb_n_marks,n_marks,n_objs); +} else { +GC_printf("%u,%u,%u,%u\n",hhdr->hb_obj_kind,(unsigned)bytes, +n_marks,n_objs); +} +ps=(struct Print_stats*)raw_ps; +ps->total_bytes+=(bytes+(HBLKSIZE-1))&~(HBLKSIZE-1); +ps->number_of_blocks++; +} +void GC_print_block_list(void) +{ +struct Print_stats pstats; +GC_printf("kind(0=ptrfree,1=normal,2=unc.)," +"size_in_bytes,#_marks_set,#objs\n"); +pstats.number_of_blocks=0; +pstats.total_bytes=0; +GC_apply_to_all_blocks(GC_print_block_descr,(word)&pstats); +GC_printf("blocks=%lu,bytes=%lu\n", +(unsigned long)pstats.number_of_blocks, +(unsigned long)pstats.total_bytes); +} +GC_API void GC_CALL GC_print_free_list(int kind,size_t sz_in_granules) +{ +void*flh_next; +int n; +GC_ASSERT(kind < MAXOBJKINDS); +GC_ASSERT(sz_in_granules<=MAXOBJGRANULES); +flh_next=GC_obj_kinds[kind].ok_freelist[sz_in_granules]; +for (n=0;flh_next;n++){ +GC_printf("Free object in heap block %p [%d]:%p\n", +(void*)HBLKPTR(flh_next),n,flh_next); +flh_next=obj_link(flh_next); +} +} +#endif +STATIC void GC_clear_fl_links(void**flp) +{ +void*next=*flp; +while (0!=next){ +*flp=0; +flp=&(obj_link(next)); +next=*flp; +} +} +GC_INNER void GC_start_reclaim(GC_bool report_if_found) +{ +unsigned kind; +#if defined(PARALLEL_MARK) +GC_ASSERT(0==GC_fl_builder_count); +#endif +GC_composite_in_use=0; +GC_atomic_in_use=0; +for (kind=0;kind < GC_n_kinds;kind++){ +struct hblk**rlist=GC_obj_kinds[kind].ok_reclaim_list; +GC_bool should_clobber=(GC_obj_kinds[kind].ok_descriptor!=0); +if (rlist==0)continue; +if (!report_if_found){ +void**fop; +void**lim=&(GC_obj_kinds[kind].ok_freelist[MAXOBJGRANULES+1]); +for (fop=GC_obj_kinds[kind].ok_freelist; +(word)fop < (word)lim;(*(word**)&fop)++){ +if (*fop!=0){ +if (should_clobber){ +GC_clear_fl_links(fop); +} else { +*fop=0; +} +} +} +} +BZERO(rlist,(MAXOBJGRANULES+1)*sizeof(void*)); +} +GC_apply_to_all_blocks(GC_reclaim_block,(word)report_if_found); +#ifdef EAGER_SWEEP +GC_reclaim_all((GC_stop_func)0,FALSE); +#elif defined(ENABLE_DISCLAIM) +GC_reclaim_unconditionally_marked(); +#endif +#if defined(PARALLEL_MARK) +GC_ASSERT(0==GC_fl_builder_count); +#endif +} +GC_INNER void GC_continue_reclaim(word sz,int kind) +{ +hdr*hhdr; +struct hblk*hbp; +struct obj_kind*ok=&(GC_obj_kinds[kind]); +struct hblk**rlh=ok->ok_reclaim_list; +void**flh=&(ok->ok_freelist[sz]); +if (NULL==rlh) +return; +for (rlh+=sz;(hbp=*rlh)!=NULL;){ +hhdr=HDR(hbp); +*rlh=hhdr->hb_next; +GC_reclaim_small_nonempty_block(hbp,hhdr->hb_sz,FALSE); +if (*flh!=0) +break; +} +} +GC_INNER GC_bool GC_reclaim_all(GC_stop_func stop_func,GC_bool ignore_old) +{ +word sz; +unsigned kind; +hdr*hhdr; +struct hblk*hbp; +struct obj_kind*ok; +struct hblk**rlp; +struct hblk**rlh; +#ifndef NO_CLOCK +CLOCK_TYPE start_time=CLOCK_TYPE_INITIALIZER; +if (GC_print_stats==VERBOSE) +GET_TIME(start_time); +#endif +for (kind=0;kind < GC_n_kinds;kind++){ +ok=&(GC_obj_kinds[kind]); +rlp=ok->ok_reclaim_list; +if (rlp==0)continue; +for (sz=1;sz<=MAXOBJGRANULES;sz++){ +for (rlh=rlp+sz;(hbp=*rlh)!=NULL;){ +if (stop_func!=(GC_stop_func)0&&(*stop_func)()){ +return(FALSE); +} +hhdr=HDR(hbp); +*rlh=hhdr->hb_next; +if (!ignore_old +||(word)hhdr->hb_last_reclaimed==GC_gc_no - 1){ +GC_reclaim_small_nonempty_block(hbp,hhdr->hb_sz,FALSE); +} +} +} +} +#ifndef NO_CLOCK +if (GC_print_stats==VERBOSE){ +CLOCK_TYPE done_time; +GET_TIME(done_time); +GC_verbose_log_printf( +"Disposing of reclaim lists took %lu ms %lu ns\n", +MS_TIME_DIFF(done_time,start_time), +NS_FRAC_TIME_DIFF(done_time,start_time)); +} +#endif +return(TRUE); +} +#if!defined(EAGER_SWEEP)&&defined(ENABLE_DISCLAIM) +STATIC void GC_reclaim_unconditionally_marked(void) +{ +word sz; +unsigned kind; +hdr*hhdr; +struct hblk*hbp; +struct obj_kind*ok; +struct hblk**rlp; +struct hblk**rlh; +for (kind=0;kind < GC_n_kinds;kind++){ +ok=&(GC_obj_kinds[kind]); +if (!ok->ok_mark_unconditionally) +continue; +rlp=ok->ok_reclaim_list; +if (rlp==0) +continue; +for (sz=1;sz<=MAXOBJGRANULES;sz++){ +rlh=rlp+sz; +while ((hbp=*rlh)!=0){ +hhdr=HDR(hbp); +*rlh=hhdr->hb_next; +GC_reclaim_small_nonempty_block(hbp,hhdr->hb_sz,FALSE); +} +} +} +} +#endif +struct enumerate_reachable_s { +GC_reachable_object_proc proc; +void*client_data; +}; +STATIC void GC_do_enumerate_reachable_objects(struct hblk*hbp,word ped) +{ +struct hblkhdr*hhdr=HDR(hbp); +size_t sz=(size_t)hhdr->hb_sz; +size_t bit_no; +char*p,*plim; +if (GC_block_empty(hhdr)){ +return; +} +p=hbp->hb_body; +if (sz > MAXOBJBYTES){ +plim=p; +} else { +plim=hbp->hb_body+HBLKSIZE - sz; +} +for (bit_no=0;p<=plim;bit_no+=MARK_BIT_OFFSET(sz),p+=sz){ +if (mark_bit_from_hdr(hhdr,bit_no)){ +((struct enumerate_reachable_s*)ped)->proc(p,sz, +((struct enumerate_reachable_s*)ped)->client_data); +} +} +} +GC_API void GC_CALL GC_enumerate_reachable_objects_inner( +GC_reachable_object_proc proc, +void*client_data) +{ +struct enumerate_reachable_s ed; +GC_ASSERT(I_HOLD_LOCK()); +ed.proc=proc; +ed.client_data=client_data; +GC_apply_to_all_blocks(GC_do_enumerate_reachable_objects,(word)&ed); +} +#ifndef GC_TYPED_H +#define GC_TYPED_H +#ifndef GC_H +#endif +#ifdef __cplusplus +extern "C" { +#endif +typedef GC_word*GC_bitmap; +#define GC_WORDSZ (8*sizeof(GC_word)) +#define GC_get_bit(bm,index)(((bm)[(index)/GC_WORDSZ]>>((index)% GC_WORDSZ))&1) +#define GC_set_bit(bm,index)((bm)[(index)/GC_WORDSZ]|=(GC_word)1<<((index)% GC_WORDSZ)) +#define GC_WORD_OFFSET(t,f)(offsetof(t,f)/sizeof(GC_word)) +#define GC_WORD_LEN(t)(sizeof(t)/sizeof(GC_word)) +#define GC_BITMAP_SIZE(t)((GC_WORD_LEN(t)+GC_WORDSZ - 1)/GC_WORDSZ) +typedef GC_word GC_descr; +GC_API GC_descr GC_CALL GC_make_descriptor(const GC_word*, +size_t); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL +GC_malloc_explicitly_typed(size_t, +GC_descr); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL +GC_malloc_explicitly_typed_ignore_off_page(size_t, +GC_descr); +GC_API GC_ATTR_MALLOC GC_ATTR_CALLOC_SIZE(1,2)void*GC_CALL +GC_calloc_explicitly_typed(size_t, +size_t, +GC_descr); +#ifdef GC_DEBUG +#define GC_MALLOC_EXPLICITLY_TYPED(bytes,d)((void)(d),GC_MALLOC(bytes)) +#define GC_CALLOC_EXPLICITLY_TYPED(n,bytes,d)((void)(d),GC_MALLOC((n)*(bytes))) +#else +#define GC_MALLOC_EXPLICITLY_TYPED(bytes,d)GC_malloc_explicitly_typed(bytes,d) +#define GC_CALLOC_EXPLICITLY_TYPED(n,bytes,d)GC_calloc_explicitly_typed(n,bytes,d) +#endif +#ifdef __cplusplus +} +#endif +#endif +#define TYPD_EXTRA_BYTES (sizeof(word)- EXTRA_BYTES) +STATIC int GC_explicit_kind=0; +STATIC int GC_array_kind=0; +struct LeafDescriptor { +word ld_tag; +#define LEAF_TAG 1 +size_t ld_size; +size_t ld_nelements; +GC_descr ld_descriptor; +}; +struct ComplexArrayDescriptor { +word ad_tag; +#define ARRAY_TAG 2 +size_t ad_nelements; +union ComplexDescriptor*ad_element_descr; +}; +struct SequenceDescriptor { +word sd_tag; +#define SEQUENCE_TAG 3 +union ComplexDescriptor*sd_first; +union ComplexDescriptor*sd_second; +}; +typedef union ComplexDescriptor { +struct LeafDescriptor ld; +struct ComplexArrayDescriptor ad; +struct SequenceDescriptor sd; +} complex_descriptor; +#define TAG ad.ad_tag +#define ED_INITIAL_SIZE 100 +STATIC int GC_typed_mark_proc_index=0; +STATIC int GC_array_mark_proc_index=0; +STATIC void GC_push_typed_structures_proc(void) +{ +GC_PUSH_ALL_SYM(GC_ext_descriptors); +} +STATIC signed_word GC_add_ext_descriptor(const word*bm,word nbits) +{ +size_t nwords=divWORDSZ(nbits+WORDSZ-1); +signed_word result; +size_t i; +word last_part; +size_t extra_bits; +DCL_LOCK_STATE; +LOCK(); +while (GC_avail_descr+nwords>=GC_ed_size){ +typed_ext_descr_t*newExtD; +size_t new_size; +word ed_size=GC_ed_size; +if (ed_size==0){ +GC_ASSERT((word)(&GC_ext_descriptors)% sizeof(word)==0); +GC_push_typed_structures=GC_push_typed_structures_proc; +UNLOCK(); +new_size=ED_INITIAL_SIZE; +} else { +UNLOCK(); +new_size=2*ed_size; +if (new_size > MAX_ENV)return(-1); +} +newExtD=(typed_ext_descr_t*)GC_malloc_atomic(new_size +*sizeof(typed_ext_descr_t)); +if (NULL==newExtD) +return -1; +LOCK(); +if (ed_size==GC_ed_size){ +if (GC_avail_descr!=0){ +BCOPY(GC_ext_descriptors,newExtD, +GC_avail_descr*sizeof(typed_ext_descr_t)); +} +GC_ed_size=new_size; +GC_ext_descriptors=newExtD; +} +} +result=GC_avail_descr; +for (i=0;i < nwords-1;i++){ +GC_ext_descriptors[result+i].ed_bitmap=bm[i]; +GC_ext_descriptors[result+i].ed_continued=TRUE; +} +last_part=bm[i]; +extra_bits=nwords*WORDSZ - nbits; +last_part<<=extra_bits; +last_part>>=extra_bits; +GC_ext_descriptors[result+i].ed_bitmap=last_part; +GC_ext_descriptors[result+i].ed_continued=FALSE; +GC_avail_descr+=nwords; +UNLOCK(); +return(result); +} +STATIC GC_descr GC_bm_table[WORDSZ/2]; +STATIC GC_descr GC_double_descr(GC_descr descriptor,word nwords) +{ +if ((descriptor&GC_DS_TAGS)==GC_DS_LENGTH){ +descriptor=GC_bm_table[BYTES_TO_WORDS((word)descriptor)]; +}; +descriptor|=(descriptor&~GC_DS_TAGS)>>nwords; +return(descriptor); +} +STATIC complex_descriptor* +GC_make_sequence_descriptor(complex_descriptor*first, +complex_descriptor*second); +#define COMPLEX 2 +#define LEAF 1 +#define SIMPLE 0 +#define NO_MEM (-1) +STATIC int GC_make_array_descriptor(size_t nelements,size_t size, +GC_descr descriptor,GC_descr*simple_d, +complex_descriptor**complex_d, +struct LeafDescriptor*leaf) +{ +#define OPT_THRESHOLD 50 +if ((descriptor&GC_DS_TAGS)==GC_DS_LENGTH){ +if (descriptor==(GC_descr)size){ +*simple_d=nelements*descriptor; +return(SIMPLE); +} else if ((word)descriptor==0){ +*simple_d=(GC_descr)0; +return(SIMPLE); +} +} +if (nelements<=OPT_THRESHOLD){ +if (nelements<=1){ +if (nelements==1){ +*simple_d=descriptor; +return(SIMPLE); +} else { +*simple_d=(GC_descr)0; +return(SIMPLE); +} +} +} else if (size<=BITMAP_BITS/2 +&&(descriptor&GC_DS_TAGS)!=GC_DS_PROC +&&(size&(sizeof(word)-1))==0){ +int result= +GC_make_array_descriptor(nelements/2,2*size, +GC_double_descr(descriptor, +BYTES_TO_WORDS(size)), +simple_d,complex_d,leaf); +if ((nelements&1)==0){ +return(result); +} else { +struct LeafDescriptor*one_element= +(struct LeafDescriptor*) +GC_malloc_atomic(sizeof(struct LeafDescriptor)); +if (result==NO_MEM||one_element==0)return(NO_MEM); +one_element->ld_tag=LEAF_TAG; +one_element->ld_size=size; +one_element->ld_nelements=1; +one_element->ld_descriptor=descriptor; +switch(result){ +case SIMPLE: +{ +struct LeafDescriptor*beginning= +(struct LeafDescriptor*) +GC_malloc_atomic(sizeof(struct LeafDescriptor)); +if (beginning==0)return(NO_MEM); +beginning->ld_tag=LEAF_TAG; +beginning->ld_size=size; +beginning->ld_nelements=1; +beginning->ld_descriptor=*simple_d; +*complex_d=GC_make_sequence_descriptor( +(complex_descriptor*)beginning, +(complex_descriptor*)one_element); +break; +} +case LEAF: +{ +struct LeafDescriptor*beginning= +(struct LeafDescriptor*) +GC_malloc_atomic(sizeof(struct LeafDescriptor)); +if (beginning==0)return(NO_MEM); +beginning->ld_tag=LEAF_TAG; +beginning->ld_size=leaf->ld_size; +beginning->ld_nelements=leaf->ld_nelements; +beginning->ld_descriptor=leaf->ld_descriptor; +*complex_d=GC_make_sequence_descriptor( +(complex_descriptor*)beginning, +(complex_descriptor*)one_element); +break; +} +case COMPLEX: +*complex_d=GC_make_sequence_descriptor( +*complex_d, +(complex_descriptor*)one_element); +break; +} +return(COMPLEX); +} +} +leaf->ld_size=size; +leaf->ld_nelements=nelements; +leaf->ld_descriptor=descriptor; +return(LEAF); +} +STATIC complex_descriptor* +GC_make_sequence_descriptor(complex_descriptor*first, +complex_descriptor*second) +{ +struct SequenceDescriptor*result= +(struct SequenceDescriptor*) +GC_malloc(sizeof(struct SequenceDescriptor)); +if (result!=0){ +result->sd_tag=SEQUENCE_TAG; +result->sd_first=first; +result->sd_second=second; +GC_dirty(result); +REACHABLE_AFTER_DIRTY(first); +REACHABLE_AFTER_DIRTY(second); +} +return((complex_descriptor*)result); +} +STATIC mse*GC_typed_mark_proc(word*addr,mse*mark_stack_ptr, +mse*mark_stack_limit,word env); +STATIC mse*GC_array_mark_proc(word*addr,mse*mark_stack_ptr, +mse*mark_stack_limit,word env); +STATIC void GC_init_explicit_typing(void) +{ +unsigned i; +GC_STATIC_ASSERT(sizeof(struct LeafDescriptor)% sizeof(word)==0); +GC_explicit_kind=GC_new_kind_inner(GC_new_free_list_inner(), +(WORDS_TO_BYTES((word)-1)|GC_DS_PER_OBJECT), +TRUE,TRUE); +GC_typed_mark_proc_index=GC_new_proc_inner(GC_typed_mark_proc); +GC_array_mark_proc_index=GC_new_proc_inner(GC_array_mark_proc); +GC_array_kind=GC_new_kind_inner(GC_new_free_list_inner(), +GC_MAKE_PROC(GC_array_mark_proc_index,0), +FALSE,TRUE); +GC_bm_table[0]=GC_DS_BITMAP; +for (i=1;i < WORDSZ/2;i++){ +GC_bm_table[i]=(((word)-1)<<(WORDSZ - i))|GC_DS_BITMAP; +} +} +STATIC mse*GC_typed_mark_proc(word*addr,mse*mark_stack_ptr, +mse*mark_stack_limit,word env) +{ +word bm=GC_ext_descriptors[env].ed_bitmap; +word*current_p=addr; +word current; +ptr_t greatest_ha=(ptr_t)GC_greatest_plausible_heap_addr; +ptr_t least_ha=(ptr_t)GC_least_plausible_heap_addr; +DECLARE_HDR_CACHE; +INIT_HDR_CACHE; +for (;bm!=0;bm>>=1,current_p++){ +if (bm&1){ +current=*current_p; +FIXUP_POINTER(current); +if (current>=(word)least_ha&¤t<=(word)greatest_ha){ +PUSH_CONTENTS((ptr_t)current,mark_stack_ptr, +mark_stack_limit,(ptr_t)current_p); +} +} +} +if (GC_ext_descriptors[env].ed_continued){ +mark_stack_ptr++; +if ((word)mark_stack_ptr>=(word)mark_stack_limit){ +mark_stack_ptr=GC_signal_mark_stack_overflow(mark_stack_ptr); +} +mark_stack_ptr->mse_start=(ptr_t)(addr+WORDSZ); +mark_stack_ptr->mse_descr.w= +GC_MAKE_PROC(GC_typed_mark_proc_index,env+1); +} +return(mark_stack_ptr); +} +STATIC word GC_descr_obj_size(complex_descriptor*d) +{ +switch(d->TAG){ +case LEAF_TAG: +return(d->ld.ld_nelements*d->ld.ld_size); +case ARRAY_TAG: +return(d->ad.ad_nelements +*GC_descr_obj_size(d->ad.ad_element_descr)); +case SEQUENCE_TAG: +return(GC_descr_obj_size(d->sd.sd_first) ++GC_descr_obj_size(d->sd.sd_second)); +default: +ABORT_RET("Bad complex descriptor"); +return 0; +} +} +STATIC mse*GC_push_complex_descriptor(word*addr,complex_descriptor*d, +mse*msp,mse*msl) +{ +ptr_t current=(ptr_t)addr; +word nelements; +word sz; +word i; +switch(d->TAG){ +case LEAF_TAG: +{ +GC_descr descr=d->ld.ld_descriptor; +nelements=d->ld.ld_nelements; +if (msl - msp<=(ptrdiff_t)nelements)return(0); +sz=d->ld.ld_size; +for (i=0;i < nelements;i++){ +msp++; +msp->mse_start=current; +msp->mse_descr.w=descr; +current+=sz; +} +return(msp); +} +case ARRAY_TAG: +{ +complex_descriptor*descr=d->ad.ad_element_descr; +nelements=d->ad.ad_nelements; +sz=GC_descr_obj_size(descr); +for (i=0;i < nelements;i++){ +msp=GC_push_complex_descriptor((word*)current,descr, +msp,msl); +if (msp==0)return(0); +current+=sz; +} +return(msp); +} +case SEQUENCE_TAG: +{ +sz=GC_descr_obj_size(d->sd.sd_first); +msp=GC_push_complex_descriptor((word*)current,d->sd.sd_first, +msp,msl); +if (msp==0)return(0); +current+=sz; +msp=GC_push_complex_descriptor((word*)current,d->sd.sd_second, +msp,msl); +return(msp); +} +default: +ABORT_RET("Bad complex descriptor"); +return 0; +} +} +STATIC mse*GC_array_mark_proc(word*addr,mse*mark_stack_ptr, +mse*mark_stack_limit, +word env GC_ATTR_UNUSED) +{ +hdr*hhdr=HDR(addr); +word sz=hhdr->hb_sz; +word nwords=BYTES_TO_WORDS(sz); +complex_descriptor*descr=(complex_descriptor*)(addr[nwords-1]); +mse*orig_mark_stack_ptr=mark_stack_ptr; +mse*new_mark_stack_ptr; +if (descr==0){ +return(orig_mark_stack_ptr); +} +new_mark_stack_ptr=GC_push_complex_descriptor(addr,descr, +mark_stack_ptr, +mark_stack_limit-1); +if (new_mark_stack_ptr==0){ +if (NULL==mark_stack_ptr)ABORT("Bad mark_stack_ptr"); +#ifdef PARALLEL_MARK +if (GC_mark_stack+GC_mark_stack_size==mark_stack_limit) +#endif +{ +GC_mark_stack_too_small=TRUE; +} +new_mark_stack_ptr=orig_mark_stack_ptr+1; +new_mark_stack_ptr->mse_start=(ptr_t)addr; +new_mark_stack_ptr->mse_descr.w=sz|GC_DS_LENGTH; +} else { +new_mark_stack_ptr++; +new_mark_stack_ptr->mse_start=(ptr_t)(addr+nwords - 1); +new_mark_stack_ptr->mse_descr.w=sizeof(word)|GC_DS_LENGTH; +} +return new_mark_stack_ptr; +} +GC_API GC_descr GC_CALL GC_make_descriptor(const GC_word*bm,size_t len) +{ +signed_word last_set_bit=len - 1; +GC_descr result; +DCL_LOCK_STATE; +#if defined(AO_HAVE_load_acquire)&&defined(AO_HAVE_store_release) +if (!EXPECT(AO_load_acquire(&GC_explicit_typing_initialized),TRUE)){ +LOCK(); +if (!GC_explicit_typing_initialized){ +GC_init_explicit_typing(); +AO_store_release(&GC_explicit_typing_initialized,TRUE); +} +UNLOCK(); +} +#else +LOCK(); +if (!EXPECT(GC_explicit_typing_initialized,TRUE)){ +GC_init_explicit_typing(); +GC_explicit_typing_initialized=TRUE; +} +UNLOCK(); +#endif +while (last_set_bit>=0&&!GC_get_bit(bm,last_set_bit)) +last_set_bit--; +if (last_set_bit < 0)return(0); +#if ALIGNMENT==CPP_WORDSZ/8 +{ +signed_word i; +for (i=0;i < last_set_bit;i++){ +if (!GC_get_bit(bm,i)){ +break; +} +} +if (i==last_set_bit){ +return (WORDS_TO_BYTES(last_set_bit+1)|GC_DS_LENGTH); +} +} +#endif +if ((word)last_set_bit < BITMAP_BITS){ +signed_word i; +result=SIGNB; +for (i=last_set_bit - 1;i>=0;i--){ +result>>=1; +if (GC_get_bit(bm,i))result|=SIGNB; +} +result|=GC_DS_BITMAP; +} else { +signed_word index=GC_add_ext_descriptor(bm,(word)last_set_bit+1); +if (index==-1)return(WORDS_TO_BYTES(last_set_bit+1)|GC_DS_LENGTH); +result=GC_MAKE_PROC(GC_typed_mark_proc_index,(word)index); +} +return result; +} +GC_API GC_ATTR_MALLOC void*GC_CALL GC_malloc_explicitly_typed(size_t lb, +GC_descr d) +{ +word*op; +size_t lg; +GC_ASSERT(GC_explicit_typing_initialized); +lb=SIZET_SAT_ADD(lb,TYPD_EXTRA_BYTES); +op=(word*)GC_malloc_kind(lb,GC_explicit_kind); +if (EXPECT(NULL==op,FALSE)) +return NULL; +lg=BYTES_TO_GRANULES(GC_size(op)); +op[GRANULES_TO_WORDS(lg)- 1]=d; +GC_dirty(op+GRANULES_TO_WORDS(lg)- 1); +REACHABLE_AFTER_DIRTY(d); +return op; +} +#define GENERAL_MALLOC_IOP(lb,k)GC_clear_stack(GC_generic_malloc_ignore_off_page(lb,k)) +GC_API GC_ATTR_MALLOC void*GC_CALL +GC_malloc_explicitly_typed_ignore_off_page(size_t lb,GC_descr d) +{ +ptr_t op; +size_t lg; +DCL_LOCK_STATE; +GC_ASSERT(GC_explicit_typing_initialized); +lb=SIZET_SAT_ADD(lb,TYPD_EXTRA_BYTES); +if (SMALL_OBJ(lb)){ +void**opp; +GC_DBG_COLLECT_AT_MALLOC(lb); +LOCK(); +lg=GC_size_map[lb]; +opp=&GC_obj_kinds[GC_explicit_kind].ok_freelist[lg]; +op=(ptr_t)(*opp); +if (EXPECT(0==op,FALSE)){ +UNLOCK(); +op=(ptr_t)GENERAL_MALLOC_IOP(lb,GC_explicit_kind); +if (0==op)return 0; +lg=BYTES_TO_GRANULES(GC_size(op)); +} else { +*opp=obj_link(op); +obj_link(op)=0; +GC_bytes_allocd+=GRANULES_TO_BYTES((word)lg); +UNLOCK(); +} +} else { +op=(ptr_t)GENERAL_MALLOC_IOP(lb,GC_explicit_kind); +if (NULL==op)return NULL; +lg=BYTES_TO_GRANULES(GC_size(op)); +} +((word*)op)[GRANULES_TO_WORDS(lg)- 1]=d; +GC_dirty(op+GRANULES_TO_WORDS(lg)- 1); +REACHABLE_AFTER_DIRTY(d); +return op; +} +GC_API GC_ATTR_MALLOC void*GC_CALL GC_calloc_explicitly_typed(size_t n, +size_t lb,GC_descr d) +{ +word*op; +size_t lg; +GC_descr simple_descr; +complex_descriptor*complex_descr; +int descr_type; +struct LeafDescriptor leaf; +GC_ASSERT(GC_explicit_typing_initialized); +descr_type=GC_make_array_descriptor((word)n,(word)lb,d,&simple_descr, +&complex_descr,&leaf); +if ((lb|n)> GC_SQRT_SIZE_MAX +&&lb > 0&&n > GC_SIZE_MAX/lb) +return (*GC_get_oom_fn())(GC_SIZE_MAX); +lb*=n; +switch(descr_type){ +case NO_MEM:return(0); +case SIMPLE: +return GC_malloc_explicitly_typed(lb,simple_descr); +case LEAF: +lb=SIZET_SAT_ADD(lb, +sizeof(struct LeafDescriptor)+TYPD_EXTRA_BYTES); +break; +case COMPLEX: +lb=SIZET_SAT_ADD(lb,TYPD_EXTRA_BYTES); +break; +} +op=(word*)GC_malloc_kind(lb,GC_array_kind); +if (EXPECT(NULL==op,FALSE)) +return NULL; +lg=BYTES_TO_GRANULES(GC_size(op)); +if (descr_type==LEAF){ +volatile struct LeafDescriptor*lp= +(struct LeafDescriptor*) +(op+GRANULES_TO_WORDS(lg) +- (BYTES_TO_WORDS(sizeof(struct LeafDescriptor))+1)); +lp->ld_tag=LEAF_TAG; +lp->ld_size=leaf.ld_size; +lp->ld_nelements=leaf.ld_nelements; +lp->ld_descriptor=leaf.ld_descriptor; +((volatile word*)op)[GRANULES_TO_WORDS(lg)- 1]=(word)lp; +} else { +#ifndef GC_NO_FINALIZATION +size_t lw=GRANULES_TO_WORDS(lg); +op[lw - 1]=(word)complex_descr; +GC_dirty(op+lw - 1); +REACHABLE_AFTER_DIRTY(complex_descr); +if (EXPECT(GC_general_register_disappearing_link( +(void**)(op+lw - 1),op) +==GC_NO_MEMORY,FALSE)) +#endif +{ +return (*GC_get_oom_fn())(lb); +} +} +return op; +} +#include +#include +#include +#ifndef MSWINCE +#include +#endif +#ifdef GC_SOLARIS_THREADS +#include +#endif +#if defined(UNIX_LIKE)||defined(CYGWIN32)||defined(SYMBIAN)||(defined(CONSOLE_LOG)&&defined(MSWIN32)) +#include +#include +#include +#endif +#if defined(CONSOLE_LOG)&&defined(MSWIN32)&&defined(_MSC_VER) +#include +#endif +#ifdef NONSTOP +#include +#endif +#ifdef THREADS +#ifdef PCR +#include "il/PCR_IL.h" +GC_INNER PCR_Th_ML GC_allocate_ml; +#elif defined(SN_TARGET_PSP2) +GC_INNER WapiMutex GC_allocate_ml_PSP2={ 0,NULL }; +#elif defined(SN_TARGET_ORBIS)||defined(SN_TARGET_PS3) +#include +GC_INNER pthread_mutex_t GC_allocate_ml; +#endif +#endif +#ifdef DYNAMIC_LOADING +#define GC_REGISTER_MAIN_STATIC_DATA()GC_register_main_static_data() +#elif defined(GC_DONT_REGISTER_MAIN_STATIC_DATA) +#define GC_REGISTER_MAIN_STATIC_DATA()FALSE +#else +#define GC_REGISTER_MAIN_STATIC_DATA()TRUE +#endif +#ifdef NEED_CANCEL_DISABLE_COUNT +__thread unsigned char GC_cancel_disable_count=0; +#endif +GC_FAR struct _GC_arrays GC_arrays; +GC_INNER unsigned GC_n_mark_procs=GC_RESERVED_MARK_PROCS; +GC_INNER unsigned GC_n_kinds=GC_N_KINDS_INITIAL_VALUE; +GC_INNER GC_bool GC_debugging_started=FALSE; +ptr_t GC_stackbottom=0; +#ifdef IA64 +ptr_t GC_register_stackbottom=0; +#endif +int GC_dont_gc=FALSE; +int GC_dont_precollect=FALSE; +GC_bool GC_quiet=0; +#if!defined(NO_CLOCK)||!defined(SMALL_CONFIG) +int GC_print_stats=0; +#endif +#ifdef GC_PRINT_BACK_HEIGHT +GC_INNER GC_bool GC_print_back_height=TRUE; +#else +GC_INNER GC_bool GC_print_back_height=FALSE; +#endif +#ifndef NO_DEBUGGING +#ifdef GC_DUMP_REGULARLY +GC_INNER GC_bool GC_dump_regularly=TRUE; +#else +GC_INNER GC_bool GC_dump_regularly=FALSE; +#endif +#ifndef NO_CLOCK +STATIC CLOCK_TYPE GC_init_time; +#endif +#endif +#ifdef KEEP_BACK_PTRS +GC_INNER long GC_backtraces=0; +#endif +#ifdef FIND_LEAK +int GC_find_leak=1; +#else +int GC_find_leak=0; +#endif +#ifndef SHORT_DBG_HDRS +#ifdef GC_FINDLEAK_DELAY_FREE +GC_INNER GC_bool GC_findleak_delay_free=TRUE; +#else +GC_INNER GC_bool GC_findleak_delay_free=FALSE; +#endif +#endif +#ifdef ALL_INTERIOR_POINTERS +int GC_all_interior_pointers=1; +#else +int GC_all_interior_pointers=0; +#endif +#ifdef FINALIZE_ON_DEMAND +int GC_finalize_on_demand=1; +#else +int GC_finalize_on_demand=0; +#endif +#ifdef JAVA_FINALIZATION +int GC_java_finalization=1; +#else +int GC_java_finalization=0; +#endif +GC_finalizer_notifier_proc GC_finalizer_notifier= +(GC_finalizer_notifier_proc)0; +#ifdef GC_FORCE_UNMAP_ON_GCOLLECT +GC_INNER GC_bool GC_force_unmap_on_gcollect=TRUE; +#else +GC_INNER GC_bool GC_force_unmap_on_gcollect=FALSE; +#endif +#ifndef GC_LARGE_ALLOC_WARN_INTERVAL +#define GC_LARGE_ALLOC_WARN_INTERVAL 5 +#endif +GC_INNER long GC_large_alloc_warn_interval=GC_LARGE_ALLOC_WARN_INTERVAL; +STATIC void*GC_CALLBACK GC_default_oom_fn( +size_t bytes_requested GC_ATTR_UNUSED) +{ +return(0); +} +GC_oom_func GC_oom_fn=GC_default_oom_fn; +#ifdef CAN_HANDLE_FORK +#ifdef HANDLE_FORK +GC_INNER int GC_handle_fork=1; +#else +GC_INNER int GC_handle_fork=FALSE; +#endif +#elif!defined(HAVE_NO_FORK) +GC_API void GC_CALL GC_atfork_prepare(void) +{ +#ifdef THREADS +ABORT("fork()handling unsupported"); +#endif +} +GC_API void GC_CALL GC_atfork_parent(void) +{ +} +GC_API void GC_CALL GC_atfork_child(void) +{ +} +#endif +GC_API void GC_CALL GC_set_handle_fork(int value GC_ATTR_UNUSED) +{ +#ifdef CAN_HANDLE_FORK +if (!GC_is_initialized) +GC_handle_fork=value>=-1?value:1; +#elif defined(THREADS)||(defined(DARWIN)&&defined(MPROTECT_VDB)) +if (!GC_is_initialized&&value){ +#ifndef SMALL_CONFIG +GC_init(); +#ifndef THREADS +if (GC_manual_vdb) +return; +#endif +#endif +ABORT("fork()handling unsupported"); +} +#else +#endif +} +STATIC void GC_init_size_map(void) +{ +size_t i; +GC_size_map[0]=1; +for (i=1;i<=GRANULES_TO_BYTES(TINY_FREELISTS-1)- EXTRA_BYTES;i++){ +GC_size_map[i]=ROUNDED_UP_GRANULES(i); +#ifndef _MSC_VER +GC_ASSERT(GC_size_map[i] < TINY_FREELISTS); +#endif +} +} +#ifndef SMALL_CLEAR_SIZE +#define SMALL_CLEAR_SIZE 256 +#endif +#if defined(ALWAYS_SMALL_CLEAR_STACK)||defined(STACK_NOT_SCANNED) +GC_API void*GC_CALL GC_clear_stack(void*arg) +{ +#ifndef STACK_NOT_SCANNED +word volatile dummy[SMALL_CLEAR_SIZE]; +BZERO(( void*)dummy,sizeof(dummy)); +#endif +return arg; +} +#else +#ifdef THREADS +#define BIG_CLEAR_SIZE 2048 +#else +STATIC word GC_stack_last_cleared=0; +STATIC ptr_t GC_min_sp=NULL; +STATIC ptr_t GC_high_water=NULL; +STATIC word GC_bytes_allocd_at_reset=0; +#define DEGRADE_RATE 50 +#endif +#if defined(ASM_CLEAR_CODE) +void*GC_clear_stack_inner(void*,ptr_t); +#else +void*GC_clear_stack_inner(void*arg, +#if defined(__APPLE_CC__)&&!GC_CLANG_PREREQ(6,0) +volatile +#endif +ptr_t limit) +{ +#define CLEAR_SIZE 213 +volatile word dummy[CLEAR_SIZE]; +BZERO(( void*)dummy,sizeof(dummy)); +if ((word)GC_approx_sp()COOLER_THAN (word)limit){ +(void)GC_clear_stack_inner(arg,limit); +} +#if defined(CPPCHECK) +GC_noop1(dummy[0]); +#else +GC_noop1(COVERT_DATAFLOW(dummy)); +#endif +return(arg); +} +#endif +#ifdef THREADS +GC_ATTR_NO_SANITIZE_THREAD +static unsigned next_random_no(void) +{ +static unsigned random_no=0; +return++random_no % 13; +} +#endif +GC_API void*GC_CALL GC_clear_stack(void*arg) +{ +ptr_t sp=GC_approx_sp(); +#ifdef THREADS +word volatile dummy[SMALL_CLEAR_SIZE]; +#endif +#define SLOP 400 +#define GC_SLOP 4000 +#define CLEAR_THRESHOLD 100000 +#ifdef THREADS +if (next_random_no()==0){ +ptr_t limit=sp; +MAKE_HOTTER(limit,BIG_CLEAR_SIZE*sizeof(word)); +limit=(ptr_t)((word)limit&~0xf); +return GC_clear_stack_inner(arg,limit); +} +BZERO((void*)dummy,SMALL_CLEAR_SIZE*sizeof(word)); +#else +if (GC_gc_no > GC_stack_last_cleared){ +if (GC_stack_last_cleared==0) +GC_high_water=(ptr_t)GC_stackbottom; +GC_min_sp=GC_high_water; +GC_stack_last_cleared=GC_gc_no; +GC_bytes_allocd_at_reset=GC_bytes_allocd; +} +MAKE_COOLER(GC_high_water,WORDS_TO_BYTES(DEGRADE_RATE)+GC_SLOP); +if ((word)sp HOTTER_THAN (word)GC_high_water){ +GC_high_water=sp; +} +MAKE_HOTTER(GC_high_water,GC_SLOP); +{ +ptr_t limit=GC_min_sp; +MAKE_HOTTER(limit,SLOP); +if ((word)sp COOLER_THAN (word)limit){ +limit=(ptr_t)((word)limit&~0xf); +GC_min_sp=sp; +return GC_clear_stack_inner(arg,limit); +} +} +if (GC_bytes_allocd - GC_bytes_allocd_at_reset > CLEAR_THRESHOLD){ +GC_min_sp=sp; +MAKE_HOTTER(GC_min_sp,CLEAR_THRESHOLD/4); +if ((word)GC_min_sp HOTTER_THAN (word)GC_high_water) +GC_min_sp=GC_high_water; +GC_bytes_allocd_at_reset=GC_bytes_allocd; +} +#endif +return arg; +} +#endif +GC_API void*GC_CALL GC_base(void*p) +{ +ptr_t r; +struct hblk*h; +bottom_index*bi; +hdr*candidate_hdr; +r=(ptr_t)p; +if (!EXPECT(GC_is_initialized,TRUE))return 0; +h=HBLKPTR(r); +GET_BI(r,bi); +candidate_hdr=HDR_FROM_BI(bi,r); +if (candidate_hdr==0)return(0); +while (IS_FORWARDING_ADDR_OR_NIL(candidate_hdr)){ +h=FORWARDED_ADDR(h,candidate_hdr); +r=(ptr_t)h; +candidate_hdr=HDR(h); +} +if (HBLK_IS_FREE(candidate_hdr))return(0); +r=(ptr_t)((word)r&~(WORDS_TO_BYTES(1)- 1)); +{ +size_t offset=HBLKDISPL(r); +word sz=candidate_hdr->hb_sz; +size_t obj_displ=offset % sz; +ptr_t limit; +r-=obj_displ; +limit=r+sz; +if ((word)limit > (word)(h+1)&&sz<=HBLKSIZE){ +return(0); +} +if ((word)p>=(word)limit)return(0); +} +return((void*)r); +} +GC_API int GC_CALL GC_is_heap_ptr(const void*p) +{ +bottom_index*bi; +GC_ASSERT(GC_is_initialized); +GET_BI(p,bi); +return HDR_FROM_BI(bi,p)!=0; +} +GC_API size_t GC_CALL GC_size(const void*p) +{ +hdr*hhdr=HDR(p); +return (size_t)hhdr->hb_sz; +} +GC_API size_t GC_CALL GC_get_heap_size(void) +{ +return (size_t)(GC_heapsize - GC_unmapped_bytes); +} +GC_API size_t GC_CALL GC_get_free_bytes(void) +{ +return (size_t)(GC_large_free_bytes - GC_unmapped_bytes); +} +GC_API size_t GC_CALL GC_get_unmapped_bytes(void) +{ +return (size_t)GC_unmapped_bytes; +} +GC_API size_t GC_CALL GC_get_bytes_since_gc(void) +{ +return (size_t)GC_bytes_allocd; +} +GC_API size_t GC_CALL GC_get_total_bytes(void) +{ +return (size_t)(GC_bytes_allocd+GC_bytes_allocd_before_gc); +} +#ifndef GC_GET_HEAP_USAGE_NOT_NEEDED +GC_API size_t GC_CALL GC_get_size_map_at(int i) +{ +if ((unsigned)i > MAXOBJBYTES) +return GC_SIZE_MAX; +return GRANULES_TO_BYTES(GC_size_map[i]); +} +GC_API void GC_CALL GC_get_heap_usage_safe(GC_word*pheap_size, +GC_word*pfree_bytes,GC_word*punmapped_bytes, +GC_word*pbytes_since_gc,GC_word*ptotal_bytes) +{ +DCL_LOCK_STATE; +LOCK(); +if (pheap_size!=NULL) +*pheap_size=GC_heapsize - GC_unmapped_bytes; +if (pfree_bytes!=NULL) +*pfree_bytes=GC_large_free_bytes - GC_unmapped_bytes; +if (punmapped_bytes!=NULL) +*punmapped_bytes=GC_unmapped_bytes; +if (pbytes_since_gc!=NULL) +*pbytes_since_gc=GC_bytes_allocd; +if (ptotal_bytes!=NULL) +*ptotal_bytes=GC_bytes_allocd+GC_bytes_allocd_before_gc; +UNLOCK(); +} +GC_INNER word GC_reclaimed_bytes_before_gc=0; +static void fill_prof_stats(struct GC_prof_stats_s*pstats) +{ +pstats->heapsize_full=GC_heapsize; +pstats->free_bytes_full=GC_large_free_bytes; +pstats->unmapped_bytes=GC_unmapped_bytes; +pstats->bytes_allocd_since_gc=GC_bytes_allocd; +pstats->allocd_bytes_before_gc=GC_bytes_allocd_before_gc; +pstats->non_gc_bytes=GC_non_gc_bytes; +pstats->gc_no=GC_gc_no; +#ifdef PARALLEL_MARK +pstats->markers_m1=(word)((signed_word)GC_markers_m1); +#else +pstats->markers_m1=0; +#endif +pstats->bytes_reclaimed_since_gc=GC_bytes_found > 0? +(word)GC_bytes_found:0; +pstats->reclaimed_bytes_before_gc=GC_reclaimed_bytes_before_gc; +pstats->expl_freed_bytes_since_gc=GC_bytes_freed; +} +#include +GC_API size_t GC_CALL GC_get_prof_stats(struct GC_prof_stats_s*pstats, +size_t stats_sz) +{ +struct GC_prof_stats_s stats; +DCL_LOCK_STATE; +LOCK(); +fill_prof_stats(stats_sz>=sizeof(stats)?pstats:&stats); +UNLOCK(); +if (stats_sz==sizeof(stats)){ +return sizeof(stats); +} else if (stats_sz > sizeof(stats)){ +memset((char*)pstats+sizeof(stats),0xff,stats_sz - sizeof(stats)); +return sizeof(stats); +} else { +if (EXPECT(stats_sz > 0,TRUE)) +BCOPY(&stats,pstats,stats_sz); +return stats_sz; +} +} +#ifdef THREADS +GC_API size_t GC_CALL GC_get_prof_stats_unsafe( +struct GC_prof_stats_s*pstats, +size_t stats_sz) +{ +struct GC_prof_stats_s stats; +if (stats_sz>=sizeof(stats)){ +fill_prof_stats(pstats); +if (stats_sz > sizeof(stats)) +memset((char*)pstats+sizeof(stats),0xff, +stats_sz - sizeof(stats)); +return sizeof(stats); +} else { +if (EXPECT(stats_sz > 0,TRUE)){ +fill_prof_stats(&stats); +BCOPY(&stats,pstats,stats_sz); +} +return stats_sz; +} +} +#endif +#endif +#if defined(GC_DARWIN_THREADS)||defined(GC_OPENBSD_UTHREADS)||defined(GC_WIN32_THREADS)||(defined(NACL)&&defined(THREADS)) +GC_API void GC_CALL GC_set_suspend_signal(int sig GC_ATTR_UNUSED) +{ +} +GC_API void GC_CALL GC_set_thr_restart_signal(int sig GC_ATTR_UNUSED) +{ +} +GC_API int GC_CALL GC_get_suspend_signal(void) +{ +return -1; +} +GC_API int GC_CALL GC_get_thr_restart_signal(void) +{ +return -1; +} +#endif +#if!defined(_MAX_PATH)&&(defined(MSWIN32)||defined(MSWINCE)||defined(CYGWIN32)) +#define _MAX_PATH MAX_PATH +#endif +#ifdef GC_READ_ENV_FILE +STATIC char*GC_envfile_content=NULL; +STATIC unsigned GC_envfile_length=0; +#ifndef GC_ENVFILE_MAXLEN +#define GC_ENVFILE_MAXLEN 0x4000 +#endif +#define GC_ENV_FILE_EXT ".gc.env" +STATIC void GC_envfile_init(void) +{ +#if defined(MSWIN32)||defined(MSWINCE)||defined(CYGWIN32) +HANDLE hFile; +char*content; +unsigned ofs; +unsigned len; +DWORD nBytesRead; +TCHAR path[_MAX_PATH+0x10]; +len=(unsigned)GetModuleFileName(NULL,path, +_MAX_PATH+1); +if (len > 4&&path[len - 4]==(TCHAR)'.'){ +len-=4; +} +BCOPY(TEXT(GC_ENV_FILE_EXT),&path[len],sizeof(TEXT(GC_ENV_FILE_EXT))); +hFile=CreateFile(path,GENERIC_READ, +FILE_SHARE_READ|FILE_SHARE_WRITE, +NULL,OPEN_EXISTING, +FILE_ATTRIBUTE_NORMAL,NULL); +if (hFile==INVALID_HANDLE_VALUE) +return; +len=(unsigned)GetFileSize(hFile,NULL); +if (len<=1||len>=GC_ENVFILE_MAXLEN){ +CloseHandle(hFile); +return; +} +GC_ASSERT(GC_page_size!=0); +content=(char*)GET_MEM(ROUNDUP_PAGESIZE_IF_MMAP((size_t)len+1)); +if (content==NULL){ +CloseHandle(hFile); +return; +} +ofs=0; +nBytesRead=(DWORD)-1L; +while (ReadFile(hFile,content+ofs,len - ofs+1,&nBytesRead, +NULL)&&nBytesRead!=0){ +if ((ofs+=nBytesRead)> len) +break; +} +CloseHandle(hFile); +if (ofs!=len||nBytesRead!=0) +return; +content[ofs]='\0'; +while (ofs--> 0){ +if (content[ofs]=='\r'||content[ofs]=='\n') +content[ofs]='\0'; +} +GC_ASSERT(NULL==GC_envfile_content); +GC_envfile_length=len+1; +GC_envfile_content=content; +#endif +} +GC_INNER char*GC_envfile_getenv(const char*name) +{ +char*p; +char*end_of_content; +unsigned namelen; +#ifndef NO_GETENV +p=getenv(name); +if (p!=NULL) +return*p!='\0'?p:NULL; +#endif +p=GC_envfile_content; +if (p==NULL) +return NULL; +namelen=strlen(name); +if (namelen==0) +return NULL; +for (end_of_content=p+GC_envfile_length; +p!=end_of_content;p+=strlen(p)+1){ +if (strncmp(p,name,namelen)==0&&*(p+=namelen)=='='){ +p++; +return*p!='\0'?p:NULL; +} +} +return NULL; +} +#endif +GC_INNER GC_bool GC_is_initialized=FALSE; +GC_API int GC_CALL GC_is_init_called(void) +{ +return GC_is_initialized; +} +#if defined(GC_WIN32_THREADS)&&((defined(MSWIN32)&&!defined(CONSOLE_LOG))||defined(MSWINCE)) +GC_INNER CRITICAL_SECTION GC_write_cs; +#endif +#ifndef DONT_USE_ATEXIT +#if!defined(PCR)&&!defined(SMALL_CONFIG) +static GC_bool skip_gc_atexit=FALSE; +#else +#define skip_gc_atexit FALSE +#endif +STATIC void GC_exit_check(void) +{ +if (GC_find_leak&&!skip_gc_atexit){ +#ifdef THREADS +GC_in_thread_creation=TRUE; +GC_gcollect(); +GC_in_thread_creation=FALSE; +#else +GC_gcollect(); +#endif +} +} +#endif +#if defined(UNIX_LIKE)&&!defined(NO_DEBUGGING) +static void looping_handler(int sig) +{ +GC_err_printf("Caught signal %d:looping in handler\n",sig); +for (;;){ +} +} +static GC_bool installed_looping_handler=FALSE; +static void maybe_install_looping_handler(void) +{ +if (!installed_looping_handler&&0!=GETENV("GC_LOOP_ON_ABORT")){ +GC_set_and_save_fault_handler(looping_handler); +installed_looping_handler=TRUE; +} +} +#else +#define maybe_install_looping_handler() +#endif +#define GC_DEFAULT_STDOUT_FD 1 +#define GC_DEFAULT_STDERR_FD 2 +#if!defined(OS2)&&!defined(MACOS)&&!defined(GC_ANDROID_LOG)&&!defined(NN_PLATFORM_CTR)&&!defined(NINTENDO_SWITCH)&&(!defined(MSWIN32)||defined(CONSOLE_LOG))&&!defined(MSWINCE) +STATIC int GC_stdout=GC_DEFAULT_STDOUT_FD; +STATIC int GC_stderr=GC_DEFAULT_STDERR_FD; +STATIC int GC_log=GC_DEFAULT_STDERR_FD; +#ifndef MSWIN32 +GC_API void GC_CALL GC_set_log_fd(int fd) +{ +GC_log=fd; +} +#endif +#endif +#ifdef MSGBOX_ON_ERROR +STATIC void GC_win32_MessageBoxA(const char*msg,const char*caption, +unsigned flags) +{ +#ifndef DONT_USE_USER32_DLL +(void)MessageBoxA(NULL,msg,caption,flags); +#else +HINSTANCE hU32=LoadLibrary(TEXT("user32.dll")); +if (hU32){ +FARPROC pfn=GetProcAddress(hU32,"MessageBoxA"); +if (pfn) +(void)(*(int (WINAPI*)(HWND,LPCSTR,LPCSTR,UINT))(word)pfn)( +NULL,msg,caption,flags); +(void)FreeLibrary(hU32); +} +#endif +} +#endif +#if defined(THREADS)&&defined(UNIX_LIKE)&&!defined(NO_GETCONTEXT) +static void callee_saves_pushed_dummy_fn(ptr_t data GC_ATTR_UNUSED, +void*context GC_ATTR_UNUSED){} +#endif +#ifndef SMALL_CONFIG +#ifdef MANUAL_VDB +static GC_bool manual_vdb_allowed=TRUE; +#else +static GC_bool manual_vdb_allowed=FALSE; +#endif +GC_API void GC_CALL GC_set_manual_vdb_allowed(int value) +{ +manual_vdb_allowed=(GC_bool)value; +} +GC_API int GC_CALL GC_get_manual_vdb_allowed(void) +{ +return (int)manual_vdb_allowed; +} +#endif +STATIC word GC_parse_mem_size_arg(const char*str) +{ +word result=0; +if (*str!='\0'){ +char*endptr; +char ch; +result=(word)STRTOULL(str,&endptr,10); +ch=*endptr; +if (ch!='\0'){ +if (*(endptr+1)!='\0') +return 0; +switch (ch){ +case 'K': +case 'k': +result<<=10; +break; +case 'M': +case 'm': +result<<=20; +break; +case 'G': +case 'g': +result<<=30; +break; +default: +result=0; +} +} +} +return result; +} +#define GC_LOG_STD_NAME "gc.log" +GC_API void GC_CALL GC_init(void) +{ +word initial_heap_sz; +IF_CANCEL(int cancel_state;) +#if defined(GC_ASSERTIONS)&&defined(GC_ALWAYS_MULTITHREADED) +DCL_LOCK_STATE; +#endif +if (EXPECT(GC_is_initialized,TRUE))return; +#ifdef REDIRECT_MALLOC +{ +static GC_bool init_started=FALSE; +if (init_started) +ABORT("Redirected malloc()called during GC init"); +init_started=TRUE; +} +#endif +#if defined(GC_INITIAL_HEAP_SIZE)&&!defined(CPPCHECK) +initial_heap_sz=GC_INITIAL_HEAP_SIZE; +#else +initial_heap_sz=MINHINCR*HBLKSIZE; +#endif +DISABLE_CANCEL(cancel_state); +#ifdef THREADS +#ifndef GC_ALWAYS_MULTITHREADED +GC_ASSERT(!GC_need_to_lock); +#endif +#ifdef SN_TARGET_PS3 +{ +pthread_mutexattr_t mattr; +if (0!=pthread_mutexattr_init(&mattr)){ +ABORT("pthread_mutexattr_init failed"); +} +if (0!=pthread_mutex_init(&GC_allocate_ml,&mattr)){ +ABORT("pthread_mutex_init failed"); +} +(void)pthread_mutexattr_destroy(&mattr); +} +#endif +#endif +#if defined(GC_WIN32_THREADS)&&!defined(GC_PTHREADS) +#ifndef SPIN_COUNT +#define SPIN_COUNT 4000 +#endif +#ifdef MSWINRT_FLAVOR +InitializeCriticalSectionAndSpinCount(&GC_allocate_ml,SPIN_COUNT); +#else +{ +#ifndef MSWINCE +FARPROC pfn=0; +HMODULE hK32=GetModuleHandle(TEXT("kernel32.dll")); +if (hK32) +pfn=GetProcAddress(hK32, +"InitializeCriticalSectionAndSpinCount"); +if (pfn){ +(*(BOOL (WINAPI*)(LPCRITICAL_SECTION,DWORD))(word)pfn)( +&GC_allocate_ml,SPIN_COUNT); +} else +#endif +InitializeCriticalSection(&GC_allocate_ml); +} +#endif +#endif +#if defined(GC_WIN32_THREADS)&&((defined(MSWIN32)&&!defined(CONSOLE_LOG))||defined(MSWINCE)) +InitializeCriticalSection(&GC_write_cs); +#endif +GC_setpagesize(); +#ifdef MSWIN32 +GC_init_win32(); +#endif +#ifdef GC_READ_ENV_FILE +GC_envfile_init(); +#endif +#if!defined(NO_CLOCK)||!defined(SMALL_CONFIG) +#ifdef GC_PRINT_VERBOSE_STATS +GC_print_stats=VERBOSE; +#else +if (0!=GETENV("GC_PRINT_VERBOSE_STATS")){ +GC_print_stats=VERBOSE; +} else if (0!=GETENV("GC_PRINT_STATS")){ +GC_print_stats=1; +} +#endif +#endif +#if ((defined(UNIX_LIKE)&&!defined(GC_ANDROID_LOG))||(defined(CONSOLE_LOG)&&defined(MSWIN32))||defined(CYGWIN32)||defined(SYMBIAN))&&!defined(SMALL_CONFIG) +{ +char*file_name=TRUSTED_STRING(GETENV("GC_LOG_FILE")); +#ifdef GC_LOG_TO_FILE_ALWAYS +if (NULL==file_name) +file_name=GC_LOG_STD_NAME; +#else +if (0!=file_name) +#endif +{ +#if defined(_MSC_VER) +int log_d=_open(file_name,O_CREAT|O_WRONLY|O_APPEND); +#else +int log_d=open(file_name,O_CREAT|O_WRONLY|O_APPEND,0644); +#endif +if (log_d < 0){ +GC_err_printf("Failed to open %s as log file\n",file_name); +} else { +char*str; +GC_log=log_d; +str=GETENV("GC_ONLY_LOG_TO_FILE"); +#ifdef GC_ONLY_LOG_TO_FILE +if (str!=NULL&&*str=='0'&&*(str+1)=='\0') +#else +if (str==NULL||(*str=='0'&&*(str+1)=='\0')) +#endif +{ +GC_stdout=log_d; +GC_stderr=log_d; +} +} +} +} +#endif +#if!defined(NO_DEBUGGING)&&!defined(GC_DUMP_REGULARLY) +if (0!=GETENV("GC_DUMP_REGULARLY")){ +GC_dump_regularly=TRUE; +} +#endif +#ifdef KEEP_BACK_PTRS +{ +char*backtraces_string=GETENV("GC_BACKTRACES"); +if (0!=backtraces_string){ +GC_backtraces=atol(backtraces_string); +if (backtraces_string[0]=='\0')GC_backtraces=1; +} +} +#endif +if (0!=GETENV("GC_FIND_LEAK")){ +GC_find_leak=1; +} +#ifndef SHORT_DBG_HDRS +if (0!=GETENV("GC_FINDLEAK_DELAY_FREE")){ +GC_findleak_delay_free=TRUE; +} +#endif +if (0!=GETENV("GC_ALL_INTERIOR_POINTERS")){ +GC_all_interior_pointers=1; +} +if (0!=GETENV("GC_DONT_GC")){ +GC_dont_gc=1; +} +if (0!=GETENV("GC_PRINT_BACK_HEIGHT")){ +GC_print_back_height=TRUE; +} +if (0!=GETENV("GC_NO_BLACKLIST_WARNING")){ +GC_large_alloc_warn_interval=LONG_MAX; +} +{ +char*addr_string=GETENV("GC_TRACE"); +if (0!=addr_string){ +#ifndef ENABLE_TRACE +WARN("Tracing not enabled:Ignoring GC_TRACE value\n",0); +#else +word addr=(word)STRTOULL(addr_string,NULL,16); +if (addr < 0x1000) +WARN("Unlikely trace address:%p\n",(void*)addr); +GC_trace_addr=(ptr_t)addr; +#endif +} +} +#ifdef GC_COLLECT_AT_MALLOC +{ +char*string=GETENV("GC_COLLECT_AT_MALLOC"); +if (0!=string){ +size_t min_lb=(size_t)STRTOULL(string,NULL,10); +if (min_lb > 0) +GC_dbg_collect_at_malloc_min_lb=min_lb; +} +} +#endif +#if!defined(GC_DISABLE_INCREMENTAL)&&!defined(NO_CLOCK) +{ +char*time_limit_string=GETENV("GC_PAUSE_TIME_TARGET"); +if (0!=time_limit_string){ +long time_limit=atol(time_limit_string); +if (time_limit > 0){ +GC_time_limit=time_limit; +} +} +} +#endif +#ifndef SMALL_CONFIG +{ +char*full_freq_string=GETENV("GC_FULL_FREQUENCY"); +if (full_freq_string!=NULL){ +int full_freq=atoi(full_freq_string); +if (full_freq > 0) +GC_full_freq=full_freq; +} +} +#endif +{ +char*interval_string=GETENV("GC_LARGE_ALLOC_WARN_INTERVAL"); +if (0!=interval_string){ +long interval=atol(interval_string); +if (interval<=0){ +WARN("GC_LARGE_ALLOC_WARN_INTERVAL environment variable has " +"bad value:Ignoring\n",0); +} else { +GC_large_alloc_warn_interval=interval; +} +} +} +{ +char*space_divisor_string=GETENV("GC_FREE_SPACE_DIVISOR"); +if (space_divisor_string!=NULL){ +int space_divisor=atoi(space_divisor_string); +if (space_divisor > 0) +GC_free_space_divisor=(unsigned)space_divisor; +} +} +#ifdef USE_MUNMAP +{ +char*string=GETENV("GC_UNMAP_THRESHOLD"); +if (string!=NULL){ +if (*string=='0'&&*(string+1)=='\0'){ +GC_unmap_threshold=0; +} else { +int unmap_threshold=atoi(string); +if (unmap_threshold > 0) +GC_unmap_threshold=unmap_threshold; +} +} +} +{ +char*string=GETENV("GC_FORCE_UNMAP_ON_GCOLLECT"); +if (string!=NULL){ +if (*string=='0'&&*(string+1)=='\0'){ +GC_force_unmap_on_gcollect=FALSE; +} else { +GC_force_unmap_on_gcollect=TRUE; +} +} +} +{ +char*string=GETENV("GC_USE_ENTIRE_HEAP"); +if (string!=NULL){ +if (*string=='0'&&*(string+1)=='\0'){ +GC_use_entire_heap=FALSE; +} else { +GC_use_entire_heap=TRUE; +} +} +} +#endif +#if!defined(NO_DEBUGGING)&&!defined(NO_CLOCK) +GET_TIME(GC_init_time); +#endif +maybe_install_looping_handler(); +#if ALIGNMENT > GC_DS_TAGS +if (EXTRA_BYTES!=0) +GC_obj_kinds[NORMAL].ok_descriptor=(word)(-ALIGNMENT)|GC_DS_LENGTH; +#endif +GC_exclude_static_roots_inner(beginGC_arrays,endGC_arrays); +GC_exclude_static_roots_inner(beginGC_obj_kinds,endGC_obj_kinds); +#ifdef SEPARATE_GLOBALS +GC_exclude_static_roots_inner(beginGC_objfreelist,endGC_objfreelist); +GC_exclude_static_roots_inner(beginGC_aobjfreelist,endGC_aobjfreelist); +#endif +#if defined(USE_PROC_FOR_LIBRARIES)&&defined(GC_LINUX_THREADS) +WARN("USE_PROC_FOR_LIBRARIES+GC_LINUX_THREADS performs poorly.\n",0); +#endif +#if defined(SEARCH_FOR_DATA_START) +GC_init_linux_data_start(); +#endif +#if defined(NETBSD)&&defined(__ELF__) +GC_init_netbsd_elf(); +#endif +#if!defined(THREADS)||defined(GC_PTHREADS)||defined(NN_PLATFORM_CTR)||defined(NINTENDO_SWITCH)||defined(GC_WIN32_THREADS)||defined(GC_SOLARIS_THREADS) +if (GC_stackbottom==0){ +GC_stackbottom=GC_get_main_stack_base(); +#if (defined(LINUX)||defined(HPUX))&&defined(IA64) +GC_register_stackbottom=GC_get_register_stack_base(); +#endif +} else { +#if (defined(LINUX)||defined(HPUX))&&defined(IA64) +if (GC_register_stackbottom==0){ +WARN("GC_register_stackbottom should be set with GC_stackbottom\n",0); +GC_register_stackbottom=GC_get_register_stack_base(); +} +#endif +} +#endif +#if!defined(CPPCHECK) +GC_STATIC_ASSERT(sizeof(ptr_t)==sizeof(word)); +GC_STATIC_ASSERT(sizeof(signed_word)==sizeof(word)); +#if!defined(_AUX_SOURCE)||defined(__GNUC__) +GC_STATIC_ASSERT((word)(-1)> (word)0); +#endif +GC_STATIC_ASSERT((signed_word)(-1)< (signed_word)0); +#endif +GC_STATIC_ASSERT(sizeof (struct hblk)==HBLKSIZE); +#ifndef THREADS +GC_ASSERT(!((word)GC_stackbottom HOTTER_THAN (word)GC_approx_sp())); +#endif +#ifndef GC_DISABLE_INCREMENTAL +if (GC_incremental||0!=GETENV("GC_ENABLE_INCREMENTAL")){ +#if defined(BASE_ATOMIC_OPS_EMULATED)||defined(CHECKSUMS)||defined(REDIRECT_MALLOC)||defined(REDIRECT_MALLOC_IN_HEADER)||defined(SMALL_CONFIG) +#else +if (manual_vdb_allowed){ +GC_manual_vdb=TRUE; +GC_incremental=TRUE; +} else +#endif +{ +GC_incremental=GC_dirty_init(); +GC_ASSERT(GC_bytes_allocd==0); +} +} +#endif +if (GC_REGISTER_MAIN_STATIC_DATA())GC_register_data_segments(); +GC_init_headers(); +GC_bl_init(); +GC_mark_init(); +{ +char*sz_str=GETENV("GC_INITIAL_HEAP_SIZE"); +if (sz_str!=NULL){ +initial_heap_sz=GC_parse_mem_size_arg(sz_str); +if (initial_heap_sz<=MINHINCR*HBLKSIZE){ +WARN("Bad initial heap size %s - ignoring it.\n",sz_str); +} +} +} +{ +char*sz_str=GETENV("GC_MAXIMUM_HEAP_SIZE"); +if (sz_str!=NULL){ +word max_heap_sz=GC_parse_mem_size_arg(sz_str); +if (max_heap_sz < initial_heap_sz){ +WARN("Bad maximum heap size %s - ignoring it.\n",sz_str); +} +if (0==GC_max_retries)GC_max_retries=2; +GC_set_max_heap_size(max_heap_sz); +} +} +#if defined(GC_ASSERTIONS)&&defined(GC_ALWAYS_MULTITHREADED) +LOCK(); +#endif +if (!GC_expand_hp_inner(divHBLKSZ(initial_heap_sz))){ +GC_err_printf("Can't start up:not enough memory\n"); +EXIT(); +} else { +GC_requested_heapsize+=initial_heap_sz; +} +if (GC_all_interior_pointers) +GC_initialize_offsets(); +GC_register_displacement_inner(0L); +#if defined(GC_LINUX_THREADS)&&defined(REDIRECT_MALLOC) +if (!GC_all_interior_pointers){ +GC_register_displacement_inner(sizeof(void*)); +} +#endif +GC_init_size_map(); +#ifdef PCR +if (PCR_IL_Lock(PCR_Bool_false,PCR_allSigsBlocked,PCR_waitForever) +!=PCR_ERes_okay){ +ABORT("Can't lock load state"); +} else if (PCR_IL_Unlock()!=PCR_ERes_okay){ +ABORT("Can't unlock load state"); +} +PCR_IL_Unlock(); +GC_pcr_install(); +#endif +GC_is_initialized=TRUE; +#if defined(GC_PTHREADS)||defined(GC_WIN32_THREADS) +GC_thr_init(); +#ifdef PARALLEL_MARK +#if defined(GC_ASSERTIONS)&&defined(GC_ALWAYS_MULTITHREADED) +UNLOCK(); +#endif +GC_start_mark_threads_inner(); +#if defined(GC_ASSERTIONS)&&defined(GC_ALWAYS_MULTITHREADED) +LOCK(); +#endif +#endif +#endif +COND_DUMP; +if (!GC_dont_precollect||GC_incremental){ +GC_gcollect_inner(); +} +#if defined(GC_ASSERTIONS)&&defined(GC_ALWAYS_MULTITHREADED) +UNLOCK(); +#endif +#if defined(THREADS)&&defined(UNIX_LIKE)&&!defined(NO_GETCONTEXT) +if (GC_dont_gc||GC_dont_precollect) +GC_with_callee_saves_pushed(callee_saves_pushed_dummy_fn,NULL); +#endif +#ifndef DONT_USE_ATEXIT +if (GC_find_leak){ +atexit(GC_exit_check); +} +#endif +#if defined(PARALLEL_MARK)||defined(THREAD_LOCAL_ALLOC)||(defined(GC_ALWAYS_MULTITHREADED)&&defined(GC_WIN32_THREADS)&&!defined(GC_NO_THREADS_DISCOVERY)) +GC_init_parallel(); +#endif +#if defined(DYNAMIC_LOADING)&&defined(DARWIN) +GC_init_dyld(); +#endif +RESTORE_CANCEL(cancel_state); +} +GC_API void GC_CALL GC_enable_incremental(void) +{ +#if!defined(GC_DISABLE_INCREMENTAL)&&!defined(KEEP_BACK_PTRS) +DCL_LOCK_STATE; +if (!GC_find_leak&&0==GETENV("GC_DISABLE_INCREMENTAL")){ +LOCK(); +if (!GC_incremental){ +GC_setpagesize(); +maybe_install_looping_handler(); +if (!GC_is_initialized){ +UNLOCK(); +GC_incremental=TRUE; +GC_init(); +LOCK(); +} else { +#if!defined(BASE_ATOMIC_OPS_EMULATED)&&!defined(CHECKSUMS)&&!defined(REDIRECT_MALLOC)&&!defined(REDIRECT_MALLOC_IN_HEADER)&&!defined(SMALL_CONFIG) +if (manual_vdb_allowed){ +GC_manual_vdb=TRUE; +GC_incremental=TRUE; +} else +#endif +{ +GC_incremental=GC_dirty_init(); +} +} +if (GC_incremental&&!GC_dont_gc){ +IF_CANCEL(int cancel_state;) +DISABLE_CANCEL(cancel_state); +if (GC_bytes_allocd > 0){ +GC_gcollect_inner(); +} +GC_read_dirty(FALSE); +RESTORE_CANCEL(cancel_state); +} +} +UNLOCK(); +return; +} +#endif +GC_init(); +} +#if defined(THREADS) +GC_API void GC_CALL GC_start_mark_threads(void) +{ +#if defined(PARALLEL_MARK)&&defined(CAN_HANDLE_FORK)&&!defined(THREAD_SANITIZER) +IF_CANCEL(int cancel_state;) +DISABLE_CANCEL(cancel_state); +GC_start_mark_threads_inner(); +RESTORE_CANCEL(cancel_state); +#else +GC_ASSERT(I_DONT_HOLD_LOCK()); +#endif +} +#endif +GC_API void GC_CALL GC_deinit(void) +{ +if (GC_is_initialized){ +GC_is_initialized=FALSE; +#if defined(GC_WIN32_THREADS)&&(defined(MSWIN32)||defined(MSWINCE)) +#if!defined(CONSOLE_LOG)||defined(MSWINCE) +DeleteCriticalSection(&GC_write_cs); +#endif +DeleteCriticalSection(&GC_allocate_ml); +#endif +} +} +#if (defined(MSWIN32)&&!defined(CONSOLE_LOG))||defined(MSWINCE) +#if defined(_MSC_VER)&&defined(_DEBUG)&&!defined(MSWINCE) +#include +#endif +STATIC HANDLE GC_log=0; +#ifdef THREADS +#if defined(PARALLEL_MARK)&&!defined(GC_ALWAYS_MULTITHREADED) +#define IF_NEED_TO_LOCK(x)if (GC_parallel||GC_need_to_lock)x +#else +#define IF_NEED_TO_LOCK(x)if (GC_need_to_lock)x +#endif +#else +#define IF_NEED_TO_LOCK(x) +#endif +#ifdef MSWINRT_FLAVOR +#include +DECLSPEC_IMPORT HRESULT WINAPI RoGetActivationFactory( +HSTRING activatableClassId, +REFIID iid,void**factory); +static GC_bool getWinRTLogPath(wchar_t*buf,size_t bufLen) +{ +static const GUID kIID_IApplicationDataStatics={ +0x5612147B,0xE843,0x45E3, +0x94,0xD8,0x06,0x16,0x9E,0x3C,0x8E,0x17 +}; +static const GUID kIID_IStorageItem={ +0x4207A996,0xCA2F,0x42F7, +0xBD,0xE8,0x8B,0x10,0x45,0x7A,0x7F,0x30 +}; +GC_bool result=FALSE; +HSTRING_HEADER appDataClassNameHeader; +HSTRING appDataClassName; +__x_ABI_CWindows_CStorage_CIApplicationDataStatics*appDataStatics=0; +GC_ASSERT(bufLen > 0); +if (SUCCEEDED(WindowsCreateStringReference( +RuntimeClass_Windows_Storage_ApplicationData, +(sizeof(RuntimeClass_Windows_Storage_ApplicationData)-1) +/sizeof(wchar_t), +&appDataClassNameHeader,&appDataClassName)) +&&SUCCEEDED(RoGetActivationFactory(appDataClassName, +&kIID_IApplicationDataStatics, +&appDataStatics))){ +__x_ABI_CWindows_CStorage_CIApplicationData*appData=NULL; +__x_ABI_CWindows_CStorage_CIStorageFolder*tempFolder=NULL; +__x_ABI_CWindows_CStorage_CIStorageItem*tempFolderItem=NULL; +HSTRING tempPath=NULL; +if (SUCCEEDED(appDataStatics->lpVtbl->get_Current(appDataStatics, +&appData)) +&&SUCCEEDED(appData->lpVtbl->get_TemporaryFolder(appData, +&tempFolder)) +&&SUCCEEDED(tempFolder->lpVtbl->QueryInterface(tempFolder, +&kIID_IStorageItem, +&tempFolderItem)) +&&SUCCEEDED(tempFolderItem->lpVtbl->get_Path(tempFolderItem, +&tempPath))){ +UINT32 tempPathLen; +const wchar_t*tempPathBuf= +WindowsGetStringRawBuffer(tempPath,&tempPathLen); +buf[0]='\0'; +if (wcsncat_s(buf,bufLen,tempPathBuf,tempPathLen)==0 +&&wcscat_s(buf,bufLen,L"\\")==0 +&&wcscat_s(buf,bufLen,TEXT(GC_LOG_STD_NAME))==0) +result=TRUE; +WindowsDeleteString(tempPath); +} +if (tempFolderItem!=NULL) +tempFolderItem->lpVtbl->Release(tempFolderItem); +if (tempFolder!=NULL) +tempFolder->lpVtbl->Release(tempFolder); +if (appData!=NULL) +appData->lpVtbl->Release(appData); +appDataStatics->lpVtbl->Release(appDataStatics); +} +return result; +} +#endif +STATIC HANDLE GC_CreateLogFile(void) +{ +HANDLE hFile; +#ifdef MSWINRT_FLAVOR +TCHAR pathBuf[_MAX_PATH+0x10]; +hFile=INVALID_HANDLE_VALUE; +if (getWinRTLogPath(pathBuf,_MAX_PATH+1)){ +CREATEFILE2_EXTENDED_PARAMETERS extParams; +BZERO(&extParams,sizeof(extParams)); +extParams.dwSize=sizeof(extParams); +extParams.dwFileAttributes=FILE_ATTRIBUTE_NORMAL; +extParams.dwFileFlags=GC_print_stats==VERBOSE?0 +:FILE_FLAG_WRITE_THROUGH; +hFile=CreateFile2(pathBuf,GENERIC_WRITE,FILE_SHARE_READ, +CREATE_ALWAYS,&extParams); +} +#else +TCHAR*logPath; +#if defined(NO_GETENV_WIN32)&&defined(CPPCHECK) +#define appendToFile FALSE +#else +BOOL appendToFile=FALSE; +#endif +#if!defined(NO_GETENV_WIN32)||!defined(OLD_WIN32_LOG_FILE) +TCHAR pathBuf[_MAX_PATH+0x10]; +logPath=pathBuf; +#endif +#ifndef NO_GETENV_WIN32 +if (GetEnvironmentVariable(TEXT("GC_LOG_FILE"),pathBuf, +_MAX_PATH+1)- 1U < (DWORD)_MAX_PATH){ +appendToFile=TRUE; +} else +#endif +{ +#ifdef OLD_WIN32_LOG_FILE +logPath=TEXT(GC_LOG_STD_NAME); +#else +int len=(int)GetModuleFileName(NULL,pathBuf, +_MAX_PATH+1); +if (len > 4&&pathBuf[len - 4]==(TCHAR)'.'){ +len-=4; +} +BCOPY(TEXT(".")TEXT(GC_LOG_STD_NAME),&pathBuf[len], +sizeof(TEXT(".")TEXT(GC_LOG_STD_NAME))); +#endif +} +hFile=CreateFile(logPath,GENERIC_WRITE,FILE_SHARE_READ, +NULL, +appendToFile?OPEN_ALWAYS:CREATE_ALWAYS, +GC_print_stats==VERBOSE?FILE_ATTRIBUTE_NORMAL: +FILE_ATTRIBUTE_NORMAL|FILE_FLAG_WRITE_THROUGH, +NULL); +#ifndef NO_GETENV_WIN32 +if (appendToFile&&hFile!=INVALID_HANDLE_VALUE){ +LONG posHigh=0; +(void)SetFilePointer(hFile,0,&posHigh,FILE_END); +} +#endif +#undef appendToFile +#endif +return hFile; +} +STATIC int GC_write(const char*buf,size_t len) +{ +BOOL res; +DWORD written; +#if defined(THREADS)&&defined(GC_ASSERTIONS) +static GC_bool inside_write=FALSE; +if (inside_write) +return -1; +#endif +if (len==0) +return 0; +IF_NEED_TO_LOCK(EnterCriticalSection(&GC_write_cs)); +#if defined(THREADS)&&defined(GC_ASSERTIONS) +if (GC_write_disabled){ +inside_write=TRUE; +ABORT("Assertion failure:GC_write called with write_disabled"); +} +#endif +if (GC_log==0){ +GC_log=GC_CreateLogFile(); +} +if (GC_log==INVALID_HANDLE_VALUE){ +IF_NEED_TO_LOCK(LeaveCriticalSection(&GC_write_cs)); +#ifdef NO_DEBUGGING +return 0; +#else +return -1; +#endif +} +res=WriteFile(GC_log,buf,(DWORD)len,&written,NULL); +#if defined(_MSC_VER)&&defined(_DEBUG)&&!defined(NO_CRT) +#ifdef MSWINCE +{ +WCHAR wbuf[1024]; +wbuf[MultiByteToWideChar(CP_ACP,0, +buf,len,wbuf, +sizeof(wbuf)/sizeof(wbuf[0])- 1)]=0; +OutputDebugStringW(wbuf); +} +#else +_CrtDbgReport(_CRT_WARN,NULL,0,NULL,"%.*s",len,buf); +#endif +#endif +IF_NEED_TO_LOCK(LeaveCriticalSection(&GC_write_cs)); +return res?(int)written:-1; +} +#define WRITE(f,buf,len)GC_write(buf,len) +#elif defined(OS2)||defined(MACOS) +STATIC FILE*GC_stdout=NULL; +STATIC FILE*GC_stderr=NULL; +STATIC FILE*GC_log=NULL; +STATIC void GC_set_files(void) +{ +if (GC_stdout==NULL){ +GC_stdout=stdout; +} +if (GC_stderr==NULL){ +GC_stderr=stderr; +} +if (GC_log==NULL){ +GC_log=stderr; +} +} +GC_INLINE int GC_write(FILE*f,const char*buf,size_t len) +{ +int res=fwrite(buf,1,len,f); +fflush(f); +return res; +} +#define WRITE(f,buf,len)(GC_set_files(),GC_write(f,buf,len)) +#elif defined(GC_ANDROID_LOG) +#include +#ifndef GC_ANDROID_LOG_TAG +#define GC_ANDROID_LOG_TAG "BDWGC" +#endif +#define GC_stdout ANDROID_LOG_DEBUG +#define GC_stderr ANDROID_LOG_ERROR +#define GC_log GC_stdout +#define WRITE(level,buf,unused_len)__android_log_write(level,GC_ANDROID_LOG_TAG,buf) +#elif defined(NN_PLATFORM_CTR) +int n3ds_log_write(const char*text,int length); +#define WRITE(level,buf,len)n3ds_log_write(buf,len) +#elif defined(NINTENDO_SWITCH) +int switch_log_write(const char*text,int length); +#define WRITE(level,buf,len)switch_log_write(buf,len) +#else +#if!defined(SN_TARGET_ORBIS)&&!defined(SN_TARGET_PSP2) +#if!defined(AMIGA)&&!defined(MSWIN32)&&!defined(MSWIN_XBOX1)&&!defined(__CC_ARM) +#include +#endif +#if!defined(ECOS)&&!defined(NOSYS) +#include +#endif +#endif +STATIC int GC_write(int fd,const char*buf,size_t len) +{ +#if defined(ECOS)||defined(SN_TARGET_ORBIS)||defined(SN_TARGET_PSP2)||defined(NOSYS) +#ifdef ECOS +#else +#endif +return len; +#else +int bytes_written=0; +IF_CANCEL(int cancel_state;) +DISABLE_CANCEL(cancel_state); +while ((unsigned)bytes_written < len){ +#ifdef GC_SOLARIS_THREADS +int result=syscall(SYS_write,fd,buf+bytes_written, +len - bytes_written); +#elif defined(_MSC_VER) +int result=_write(fd,buf+bytes_written, +(unsigned)(len - bytes_written)); +#else +int result=write(fd,buf+bytes_written,len - bytes_written); +#endif +if (-1==result){ +if (EAGAIN==errno) +continue; +RESTORE_CANCEL(cancel_state); +return(result); +} +bytes_written+=result; +} +RESTORE_CANCEL(cancel_state); +return(bytes_written); +#endif +} +#define WRITE(f,buf,len)GC_write(f,buf,len) +#endif +#define BUFSZ 1024 +#if defined(DJGPP)||defined(__STRICT_ANSI__) +#define GC_VSNPRINTF(buf,bufsz,format,args)vsprintf(buf,format,args) +#elif defined(_MSC_VER) +#ifdef MSWINCE +#define GC_VSNPRINTF StringCchVPrintfA +#else +#define GC_VSNPRINTF _vsnprintf +#endif +#else +#define GC_VSNPRINTF vsnprintf +#endif +#define GC_PRINTF_FILLBUF(buf,format)do { va_list args;va_start(args,format);(buf)[sizeof(buf)- 1]=0x15;(void)GC_VSNPRINTF(buf,sizeof(buf)- 1,format,args);va_end(args);if ((buf)[sizeof(buf)- 1]!=0x15)ABORT("GC_printf clobbered stack");} while (0) +void GC_printf(const char*format,...) +{ +if (!GC_quiet){ +char buf[BUFSZ+1]; +GC_PRINTF_FILLBUF(buf,format); +#ifdef NACL +(void)WRITE(GC_stdout,buf,strlen(buf)); +#else +if (WRITE(GC_stdout,buf,strlen(buf))< 0 +#if defined(CYGWIN32)||(defined(CONSOLE_LOG)&&defined(MSWIN32)) +&&GC_stdout!=GC_DEFAULT_STDOUT_FD +#endif +){ +ABORT("write to stdout failed"); +} +#endif +} +} +void GC_err_printf(const char*format,...) +{ +char buf[BUFSZ+1]; +GC_PRINTF_FILLBUF(buf,format); +GC_err_puts(buf); +} +void GC_log_printf(const char*format,...) +{ +char buf[BUFSZ+1]; +GC_PRINTF_FILLBUF(buf,format); +#ifdef NACL +(void)WRITE(GC_log,buf,strlen(buf)); +#else +if (WRITE(GC_log,buf,strlen(buf))< 0 +#if defined(CYGWIN32)||(defined(CONSOLE_LOG)&&defined(MSWIN32)) +&&GC_log!=GC_DEFAULT_STDERR_FD +#endif +){ +ABORT("write to GC log failed"); +} +#endif +} +#ifndef GC_ANDROID_LOG +#define GC_warn_printf GC_err_printf +#else +GC_INNER void GC_info_log_printf(const char*format,...) +{ +char buf[BUFSZ+1]; +GC_PRINTF_FILLBUF(buf,format); +(void)WRITE(ANDROID_LOG_INFO,buf,0); +} +GC_INNER void GC_verbose_log_printf(const char*format,...) +{ +char buf[BUFSZ+1]; +GC_PRINTF_FILLBUF(buf,format); +(void)WRITE(ANDROID_LOG_VERBOSE,buf,0); +} +STATIC void GC_warn_printf(const char*format,...) +{ +char buf[BUFSZ+1]; +GC_PRINTF_FILLBUF(buf,format); +(void)WRITE(ANDROID_LOG_WARN,buf,0); +} +#endif +void GC_err_puts(const char*s) +{ +(void)WRITE(GC_stderr,s,strlen(s)); +} +STATIC void GC_CALLBACK GC_default_warn_proc(char*msg,GC_word arg) +{ +GC_warn_printf(msg,arg); +} +GC_INNER GC_warn_proc GC_current_warn_proc=GC_default_warn_proc; +GC_API void GC_CALLBACK GC_ignore_warn_proc(char*msg,GC_word arg) +{ +if (GC_print_stats){ +GC_default_warn_proc(msg,arg); +} +} +GC_API void GC_CALL GC_set_warn_proc(GC_warn_proc p) +{ +DCL_LOCK_STATE; +GC_ASSERT(NONNULL_ARG_NOT_NULL(p)); +#ifdef GC_WIN32_THREADS +#ifdef CYGWIN32 +GC_ASSERT(GC_is_initialized); +#else +if (!GC_is_initialized)GC_init(); +#endif +#endif +LOCK(); +GC_current_warn_proc=p; +UNLOCK(); +} +GC_API GC_warn_proc GC_CALL GC_get_warn_proc(void) +{ +GC_warn_proc result; +DCL_LOCK_STATE; +LOCK(); +result=GC_current_warn_proc; +UNLOCK(); +return(result); +} +#if!defined(PCR)&&!defined(SMALL_CONFIG) +STATIC void GC_CALLBACK GC_default_on_abort(const char*msg) +{ +#ifndef DONT_USE_ATEXIT +skip_gc_atexit=TRUE; +#endif +if (msg!=NULL){ +#ifdef MSGBOX_ON_ERROR +GC_win32_MessageBoxA(msg,"Fatal error in GC",MB_ICONERROR|MB_OK); +#endif +#ifndef GC_ANDROID_LOG +#if defined(GC_WIN32_THREADS)&&defined(GC_ASSERTIONS)&&((defined(MSWIN32)&&!defined(CONSOLE_LOG))||defined(MSWINCE)) +if (!GC_write_disabled) +#endif +{ +if (WRITE(GC_stderr,msg,strlen(msg))>=0) +(void)WRITE(GC_stderr,"\n",1); +} +#else +__android_log_assert("*",GC_ANDROID_LOG_TAG,"%s\n",msg); +#endif +} +#if!defined(NO_DEBUGGING)&&!defined(GC_ANDROID_LOG) +if (GETENV("GC_LOOP_ON_ABORT")!=NULL){ +for(;;){ +} +} +#endif +} +GC_abort_func GC_on_abort=GC_default_on_abort; +GC_API void GC_CALL GC_set_abort_func(GC_abort_func fn) +{ +DCL_LOCK_STATE; +GC_ASSERT(NONNULL_ARG_NOT_NULL(fn)); +LOCK(); +GC_on_abort=fn; +UNLOCK(); +} +GC_API GC_abort_func GC_CALL GC_get_abort_func(void) +{ +GC_abort_func fn; +DCL_LOCK_STATE; +LOCK(); +fn=GC_on_abort; +UNLOCK(); +return fn; +} +#endif +GC_API void GC_CALL GC_enable(void) +{ +DCL_LOCK_STATE; +LOCK(); +GC_ASSERT(GC_dont_gc!=0); +GC_dont_gc--; +UNLOCK(); +} +GC_API void GC_CALL GC_disable(void) +{ +DCL_LOCK_STATE; +LOCK(); +GC_dont_gc++; +UNLOCK(); +} +GC_API int GC_CALL GC_is_disabled(void) +{ +return GC_dont_gc!=0; +} +GC_API void**GC_CALL GC_new_free_list_inner(void) +{ +void*result; +GC_ASSERT(I_HOLD_LOCK()); +result=GC_INTERNAL_MALLOC((MAXOBJGRANULES+1)*sizeof(ptr_t),PTRFREE); +if (NULL==result)ABORT("Failed to allocate freelist for new kind"); +BZERO(result,(MAXOBJGRANULES+1)*sizeof(ptr_t)); +return (void**)result; +} +GC_API void**GC_CALL GC_new_free_list(void) +{ +void**result; +DCL_LOCK_STATE; +LOCK(); +result=GC_new_free_list_inner(); +UNLOCK(); +return result; +} +GC_API unsigned GC_CALL GC_new_kind_inner(void**fl,GC_word descr, +int adjust,int clear) +{ +unsigned result=GC_n_kinds; +GC_ASSERT(NONNULL_ARG_NOT_NULL(fl)); +GC_ASSERT(adjust==FALSE||adjust==TRUE); +GC_ASSERT(clear==TRUE +||(descr==0&&adjust==FALSE&&clear==FALSE)); +if (result < MAXOBJKINDS){ +GC_ASSERT(result > 0); +GC_n_kinds++; +GC_obj_kinds[result].ok_freelist=fl; +GC_obj_kinds[result].ok_reclaim_list=0; +GC_obj_kinds[result].ok_descriptor=descr; +GC_obj_kinds[result].ok_relocate_descr=adjust; +GC_obj_kinds[result].ok_init=(GC_bool)clear; +#ifdef ENABLE_DISCLAIM +GC_obj_kinds[result].ok_mark_unconditionally=FALSE; +GC_obj_kinds[result].ok_disclaim_proc=0; +#endif +} else { +ABORT("Too many kinds"); +} +return result; +} +GC_API unsigned GC_CALL GC_new_kind(void**fl,GC_word descr,int adjust, +int clear) +{ +unsigned result; +DCL_LOCK_STATE; +LOCK(); +result=GC_new_kind_inner(fl,descr,adjust,clear); +UNLOCK(); +return result; +} +GC_API unsigned GC_CALL GC_new_proc_inner(GC_mark_proc proc) +{ +unsigned result=GC_n_mark_procs; +if (result < MAX_MARK_PROCS){ +GC_n_mark_procs++; +GC_mark_procs[result]=proc; +} else { +ABORT("Too many mark procedures"); +} +return result; +} +GC_API unsigned GC_CALL GC_new_proc(GC_mark_proc proc) +{ +unsigned result; +DCL_LOCK_STATE; +LOCK(); +result=GC_new_proc_inner(proc); +UNLOCK(); +return result; +} +GC_API void*GC_CALL GC_call_with_alloc_lock(GC_fn_type fn,void*client_data) +{ +void*result; +DCL_LOCK_STATE; +#ifdef THREADS +LOCK(); +#endif +result=(*fn)(client_data); +#ifdef THREADS +UNLOCK(); +#endif +return(result); +} +GC_API void*GC_CALL GC_call_with_stack_base(GC_stack_base_func fn,void*arg) +{ +struct GC_stack_base base; +void*result; +base.mem_base=(void*)&base; +#ifdef IA64 +base.reg_base=(void*)GC_save_regs_in_stack(); +#endif +result=fn(&base,arg); +GC_noop1(COVERT_DATAFLOW(&base)); +return result; +} +#ifndef THREADS +GC_INNER ptr_t GC_blocked_sp=NULL; +#ifdef IA64 +STATIC ptr_t GC_blocked_register_sp=NULL; +#endif +GC_INNER struct GC_traced_stack_sect_s*GC_traced_stack_sect=NULL; +GC_API void*GC_CALL GC_call_with_gc_active(GC_fn_type fn, +void*client_data) +{ +struct GC_traced_stack_sect_s stacksect; +GC_ASSERT(GC_is_initialized); +if ((word)GC_stackbottom HOTTER_THAN (word)(&stacksect)) +GC_stackbottom=(ptr_t)COVERT_DATAFLOW(&stacksect); +if (GC_blocked_sp==NULL){ +client_data=fn(client_data); +GC_noop1(COVERT_DATAFLOW(&stacksect)); +return client_data; +} +stacksect.saved_stack_ptr=GC_blocked_sp; +#ifdef IA64 +stacksect.backing_store_end=GC_save_regs_in_stack(); +stacksect.saved_backing_store_ptr=GC_blocked_register_sp; +#endif +stacksect.prev=GC_traced_stack_sect; +GC_blocked_sp=NULL; +GC_traced_stack_sect=&stacksect; +client_data=fn(client_data); +GC_ASSERT(GC_blocked_sp==NULL); +GC_ASSERT(GC_traced_stack_sect==&stacksect); +#if defined(CPPCHECK) +GC_noop1((word)GC_traced_stack_sect - (word)GC_blocked_sp); +#endif +GC_traced_stack_sect=stacksect.prev; +#ifdef IA64 +GC_blocked_register_sp=stacksect.saved_backing_store_ptr; +#endif +GC_blocked_sp=stacksect.saved_stack_ptr; +return client_data; +} +STATIC void GC_do_blocking_inner(ptr_t data,void*context GC_ATTR_UNUSED) +{ +struct blocking_data*d=(struct blocking_data*)data; +GC_ASSERT(GC_is_initialized); +GC_ASSERT(GC_blocked_sp==NULL); +#ifdef SPARC +GC_blocked_sp=GC_save_regs_in_stack(); +#else +GC_blocked_sp=(ptr_t)&d; +#endif +#ifdef IA64 +GC_blocked_register_sp=GC_save_regs_in_stack(); +#endif +d->client_data=(d->fn)(d->client_data); +#ifdef SPARC +GC_ASSERT(GC_blocked_sp!=NULL); +#else +GC_ASSERT(GC_blocked_sp==(ptr_t)(&d)); +#endif +#if defined(CPPCHECK) +GC_noop1((word)GC_blocked_sp); +#endif +GC_blocked_sp=NULL; +} +GC_API void GC_CALL GC_set_stackbottom(void*gc_thread_handle, +const struct GC_stack_base*sb) +{ +GC_ASSERT(sb->mem_base!=NULL); +GC_ASSERT(NULL==gc_thread_handle||&GC_stackbottom==gc_thread_handle); +GC_ASSERT(NULL==GC_blocked_sp +&&NULL==GC_traced_stack_sect); +(void)gc_thread_handle; +GC_stackbottom=(char*)sb->mem_base; +#ifdef IA64 +GC_register_stackbottom=(ptr_t)sb->reg_base; +#endif +} +GC_API void*GC_CALL GC_get_my_stackbottom(struct GC_stack_base*sb) +{ +GC_ASSERT(GC_is_initialized); +sb->mem_base=GC_stackbottom; +#ifdef IA64 +sb->reg_base=GC_register_stackbottom; +#endif +return&GC_stackbottom; +} +#endif +GC_API void*GC_CALL GC_do_blocking(GC_fn_type fn,void*client_data) +{ +struct blocking_data my_data; +my_data.fn=fn; +my_data.client_data=client_data; +GC_with_callee_saves_pushed(GC_do_blocking_inner,(ptr_t)(&my_data)); +return my_data.client_data; +} +#if!defined(NO_DEBUGGING) +GC_API void GC_CALL GC_dump(void) +{ +DCL_LOCK_STATE; +LOCK(); +GC_dump_named(NULL); +UNLOCK(); +} +GC_API void GC_CALL GC_dump_named(const char*name) +{ +#ifndef NO_CLOCK +CLOCK_TYPE current_time; +GET_TIME(current_time); +#endif +if (name!=NULL){ +GC_printf("***GC Dump %s\n",name); +} else { +GC_printf("***GC Dump collection #%lu\n",(unsigned long)GC_gc_no); +} +#ifndef NO_CLOCK +GC_printf("Time since GC init:%lu ms\n", +MS_TIME_DIFF(current_time,GC_init_time)); +#endif +GC_printf("\n***Static roots:\n"); +GC_print_static_roots(); +GC_printf("\n***Heap sections:\n"); +GC_print_heap_sects(); +GC_printf("\n***Free blocks:\n"); +GC_print_hblkfreelist(); +GC_printf("\n***Blocks in use:\n"); +GC_print_block_list(); +} +#endif +static void block_add_size(struct hblk*h,word pbytes) +{ +hdr*hhdr=HDR(h); +*(word*)pbytes+=(WORDS_TO_BYTES(hhdr->hb_sz)+(HBLKSIZE - 1)) +&~(word)(HBLKSIZE - 1); +} +GC_API size_t GC_CALL GC_get_memory_use(void) +{ +word bytes=0; +DCL_LOCK_STATE; +LOCK(); +GC_apply_to_all_blocks(block_add_size,(word)(&bytes)); +UNLOCK(); +return (size_t)bytes; +} +GC_API GC_word GC_CALL GC_get_gc_no(void) +{ +return GC_gc_no; +} +#ifdef THREADS +GC_API int GC_CALL GC_get_parallel(void) +{ +return GC_parallel; +} +GC_API void GC_CALL GC_alloc_lock(void) +{ +DCL_LOCK_STATE; +LOCK(); +} +GC_API void GC_CALL GC_alloc_unlock(void) +{ +UNLOCK(); +} +GC_INNER GC_on_thread_event_proc GC_on_thread_event=0; +GC_API void GC_CALL GC_set_on_thread_event(GC_on_thread_event_proc fn) +{ +DCL_LOCK_STATE; +LOCK(); +GC_on_thread_event=fn; +UNLOCK(); +} +GC_API GC_on_thread_event_proc GC_CALL GC_get_on_thread_event(void) +{ +GC_on_thread_event_proc fn; +DCL_LOCK_STATE; +LOCK(); +fn=GC_on_thread_event; +UNLOCK(); +return fn; +} +#endif +GC_API void GC_CALL GC_set_oom_fn(GC_oom_func fn) +{ +GC_ASSERT(NONNULL_ARG_NOT_NULL(fn)); +DCL_LOCK_STATE; +LOCK(); +GC_oom_fn=fn; +UNLOCK(); +} +GC_API GC_oom_func GC_CALL GC_get_oom_fn(void) +{ +GC_oom_func fn; +DCL_LOCK_STATE; +LOCK(); +fn=GC_oom_fn; +UNLOCK(); +return fn; +} +GC_API void GC_CALL GC_set_on_heap_resize(GC_on_heap_resize_proc fn) +{ +DCL_LOCK_STATE; +LOCK(); +GC_on_heap_resize=fn; +UNLOCK(); +} +GC_API GC_on_heap_resize_proc GC_CALL GC_get_on_heap_resize(void) +{ +GC_on_heap_resize_proc fn; +DCL_LOCK_STATE; +LOCK(); +fn=GC_on_heap_resize; +UNLOCK(); +return fn; +} +GC_API void GC_CALL GC_set_finalizer_notifier(GC_finalizer_notifier_proc fn) +{ +DCL_LOCK_STATE; +LOCK(); +GC_finalizer_notifier=fn; +UNLOCK(); +} +GC_API GC_finalizer_notifier_proc GC_CALL GC_get_finalizer_notifier(void) +{ +GC_finalizer_notifier_proc fn; +DCL_LOCK_STATE; +LOCK(); +fn=GC_finalizer_notifier; +UNLOCK(); +return fn; +} +GC_API void GC_CALL GC_set_find_leak(int value) +{ +GC_find_leak=value; +} +GC_API int GC_CALL GC_get_find_leak(void) +{ +return GC_find_leak; +} +GC_API void GC_CALL GC_set_all_interior_pointers(int value) +{ +DCL_LOCK_STATE; +GC_all_interior_pointers=value?1:0; +if (GC_is_initialized){ +LOCK(); +GC_initialize_offsets(); +if (!GC_all_interior_pointers) +GC_bl_init_no_interiors(); +UNLOCK(); +} +} +GC_API int GC_CALL GC_get_all_interior_pointers(void) +{ +return GC_all_interior_pointers; +} +GC_API void GC_CALL GC_set_finalize_on_demand(int value) +{ +GC_ASSERT(value!=-1); +GC_finalize_on_demand=value; +} +GC_API int GC_CALL GC_get_finalize_on_demand(void) +{ +return GC_finalize_on_demand; +} +GC_API void GC_CALL GC_set_java_finalization(int value) +{ +GC_ASSERT(value!=-1); +GC_java_finalization=value; +} +GC_API int GC_CALL GC_get_java_finalization(void) +{ +return GC_java_finalization; +} +GC_API void GC_CALL GC_set_dont_expand(int value) +{ +GC_ASSERT(value!=-1); +GC_dont_expand=value; +} +GC_API int GC_CALL GC_get_dont_expand(void) +{ +return GC_dont_expand; +} +GC_API void GC_CALL GC_set_no_dls(int value) +{ +GC_ASSERT(value!=-1); +GC_no_dls=value; +} +GC_API int GC_CALL GC_get_no_dls(void) +{ +return GC_no_dls; +} +GC_API void GC_CALL GC_set_non_gc_bytes(GC_word value) +{ +GC_non_gc_bytes=value; +} +GC_API GC_word GC_CALL GC_get_non_gc_bytes(void) +{ +return GC_non_gc_bytes; +} +GC_API void GC_CALL GC_set_free_space_divisor(GC_word value) +{ +GC_ASSERT(value > 0); +GC_free_space_divisor=value; +} +GC_API GC_word GC_CALL GC_get_free_space_divisor(void) +{ +return GC_free_space_divisor; +} +GC_API void GC_CALL GC_set_max_retries(GC_word value) +{ +GC_ASSERT((GC_signed_word)value!=-1); +GC_max_retries=value; +} +GC_API GC_word GC_CALL GC_get_max_retries(void) +{ +return GC_max_retries; +} +GC_API void GC_CALL GC_set_dont_precollect(int value) +{ +GC_ASSERT(value!=-1); +GC_dont_precollect=value; +} +GC_API int GC_CALL GC_get_dont_precollect(void) +{ +return GC_dont_precollect; +} +GC_API void GC_CALL GC_set_full_freq(int value) +{ +GC_ASSERT(value>=0); +GC_full_freq=value; +} +GC_API int GC_CALL GC_get_full_freq(void) +{ +return GC_full_freq; +} +GC_API void GC_CALL GC_set_time_limit(unsigned long value) +{ +GC_ASSERT((long)value!=-1L); +GC_time_limit=value; +} +GC_API unsigned long GC_CALL GC_get_time_limit(void) +{ +return GC_time_limit; +} +GC_API void GC_CALL GC_set_force_unmap_on_gcollect(int value) +{ +GC_force_unmap_on_gcollect=(GC_bool)value; +} +GC_API int GC_CALL GC_get_force_unmap_on_gcollect(void) +{ +return (int)GC_force_unmap_on_gcollect; +} +GC_API void GC_CALL GC_abort_on_oom(void) +{ +GC_err_printf("Insufficient memory for the allocation\n"); +EXIT(); +} +#ifdef THREADS +GC_API void GC_CALL GC_stop_world_external(void) +{ +GC_ASSERT(GC_is_initialized); +LOCK(); +#ifdef THREAD_LOCAL_ALLOC +GC_ASSERT(!GC_world_stopped); +#endif +STOP_WORLD(); +#ifdef THREAD_LOCAL_ALLOC +GC_world_stopped=TRUE; +#endif +} +GC_API void GC_CALL GC_start_world_external(void) +{ +#ifdef THREAD_LOCAL_ALLOC +GC_ASSERT(GC_world_stopped); +GC_world_stopped=FALSE; +#else +GC_ASSERT(GC_is_initialized); +#endif +START_WORLD(); +UNLOCK(); +} +#endif +#if!defined(OS2)&&!defined(PCR)&&!defined(AMIGA)&&!defined(MACOS)&&!defined(MSWINCE)&&!defined(SN_TARGET_ORBIS)&&!defined(SN_TARGET_PSP2)&&!defined(__CC_ARM) +#include +#if!defined(MSWIN32)&&!defined(MSWIN_XBOX1) +#include +#endif +#endif +#include +#if defined(MSWINCE)||defined(SN_TARGET_PS3) +#define SIGSEGV 0 +#else +#include +#endif +#if defined(UNIX_LIKE)||defined(CYGWIN32)||defined(NACL)||defined(SYMBIAN) +#include +#endif +#if defined(LINUX)||defined(LINUX_STACKBOTTOM) +#include +#endif +#ifdef AMIGA +#define GC_AMIGA_DEF +#if!defined(GC_AMIGA_DEF)&&!defined(GC_AMIGA_SB)&&!defined(GC_AMIGA_DS)&&!defined(GC_AMIGA_AM) +#include +#include +#define GC_AMIGA_DEF +#define GC_AMIGA_SB +#define GC_AMIGA_DS +#define GC_AMIGA_AM +#endif +#ifdef GC_AMIGA_DEF +#ifndef __GNUC__ +#include +#endif +#include +#include +#include +#include +#endif +#ifdef GC_AMIGA_SB +ptr_t GC_get_main_stack_base(void) +{ +struct Process*proc=(struct Process*)SysBase->ThisTask; +if (proc->pr_Task.tc_Node.ln_Type==NT_PROCESS +&&proc->pr_CLI!=NULL){ +return (char*)proc->pr_ReturnAddr+sizeof(ULONG); +} else { +return (char*)proc->pr_Task.tc_SPUpper; +} +} +#endif +#ifdef GC_AMIGA_DS +void GC_register_data_segments(void) +{ +struct Process*proc; +struct CommandLineInterface*cli; +BPTR myseglist; +ULONG*data; +#ifdef __GNUC__ +ULONG dataSegSize; +GC_bool found_segment=FALSE; +extern char __data_size[]; +dataSegSize=__data_size+8; +#endif +proc=(struct Process*)SysBase->ThisTask; +myseglist=proc->pr_SegList; +if (proc->pr_Task.tc_Node.ln_Type==NT_PROCESS){ +if (proc->pr_CLI!=NULL){ +cli=BADDR(proc->pr_CLI); +myseglist=cli->cli_Module; +} +} else { +ABORT("Not a Process."); +} +if (myseglist==NULL){ +ABORT("Arrrgh.. can't find segments,aborting"); +} +for (data=(ULONG*)BADDR(myseglist);data!=NULL; +data=(ULONG*)BADDR(data[0])){ +if ((ULONG)GC_register_data_segments < (ULONG)(&data[1]) +||(ULONG)GC_register_data_segments > (ULONG)(&data[1]) ++data[-1]){ +#ifdef __GNUC__ +if (dataSegSize==data[-1]){ +found_segment=TRUE; +} +#endif +GC_add_roots_inner((char*)&data[1], +((char*)&data[1])+data[-1],FALSE); +} +} +#ifdef __GNUC__ +if (!found_segment){ +ABORT("Can`t find correct Segments.\nSolution:Use an newer version of ixemul.library"); +} +#endif +} +#endif +#ifdef GC_AMIGA_AM +#ifndef GC_AMIGA_FASTALLOC +void*GC_amiga_allocwrapper(size_t size,void*(*AllocFunction)(size_t size2)){ +return (*AllocFunction)(size); +} +void*(*GC_amiga_allocwrapper_do)(size_t size,void*(*AllocFunction)(size_t size2)) +=GC_amiga_allocwrapper; +#else +void*GC_amiga_allocwrapper_firsttime(size_t size,void*(*AllocFunction)(size_t size2)); +void*(*GC_amiga_allocwrapper_do)(size_t size,void*(*AllocFunction)(size_t size2)) +=GC_amiga_allocwrapper_firsttime; +struct GC_Amiga_AllocedMemoryHeader{ +ULONG size; +struct GC_Amiga_AllocedMemoryHeader*next; +}; +struct GC_Amiga_AllocedMemoryHeader*GC_AMIGAMEM=(struct GC_Amiga_AllocedMemoryHeader*)(int)~(NULL); +ULONG GC_AMIGA_MEMF=MEMF_FAST|MEMF_CLEAR; +#ifndef GC_AMIGA_ONLYFAST +BOOL GC_amiga_dontalloc=FALSE; +#endif +#ifdef GC_AMIGA_PRINTSTATS +int succ=0,succ2=0; +int nsucc=0,nsucc2=0; +int nullretries=0; +int numcollects=0; +int chipa=0; +int allochip=0; +int allocfast=0; +int cur0=0; +int cur1=0; +int cur10=0; +int cur50=0; +int cur150=0; +int cur151=0; +int ncur0=0; +int ncur1=0; +int ncur10=0; +int ncur50=0; +int ncur150=0; +int ncur151=0; +#endif +void GC_amiga_free_all_mem(void){ +struct GC_Amiga_AllocedMemoryHeader*gc_am=(struct GC_Amiga_AllocedMemoryHeader*)(~(int)(GC_AMIGAMEM)); +#ifdef GC_AMIGA_PRINTSTATS +printf("\n\n" +"%d bytes of chip-mem,and %d bytes of fast-mem where allocated from the OS.\n", +allochip,allocfast +); +printf( +"%d bytes of chip-mem were returned from the GC_AMIGA_FASTALLOC supported allocating functions.\n", +chipa +); +printf("\n"); +printf("GC_gcollect was called %d times to avoid returning NULL or start allocating with the MEMF_ANY flag.\n",numcollects); +printf("%d of them was a success. (the others had to use allocation from the OS.)\n",nullretries); +printf("\n"); +printf("Succeeded forcing %d gc-allocations (%d bytes)of chip-mem to be fast-mem.\n",succ,succ2); +printf("Failed forcing %d gc-allocations (%d bytes)of chip-mem to be fast-mem.\n",nsucc,nsucc2); +printf("\n"); +printf( +"Number of retries before succeeding a chip->fast force:\n" +"0:%d,1:%d,2-9:%d,10-49:%d,50-149:%d,>150:%d\n", +cur0,cur1,cur10,cur50,cur150,cur151 +); +printf( +"Number of retries before giving up a chip->fast force:\n" +"0:%d,1:%d,2-9:%d,10-49:%d,50-149:%d,>150:%d\n", +ncur0,ncur1,ncur10,ncur50,ncur150,ncur151 +); +#endif +while(gc_am!=NULL){ +struct GC_Amiga_AllocedMemoryHeader*temp=gc_am->next; +FreeMem(gc_am,gc_am->size); +gc_am=(struct GC_Amiga_AllocedMemoryHeader*)(~(int)(temp)); +} +} +#ifndef GC_AMIGA_ONLYFAST +char*chipmax; +size_t latestsize; +#endif +#ifdef GC_AMIGA_FASTALLOC +void*GC_amiga_get_mem(size_t size){ +struct GC_Amiga_AllocedMemoryHeader*gc_am; +#ifndef GC_AMIGA_ONLYFAST +if(GC_amiga_dontalloc==TRUE){ +return NULL; +} +if(GC_AMIGA_MEMF==(MEMF_ANY|MEMF_CLEAR)&&size>100000&&latestsize<50000)return NULL; +#endif +gc_am=AllocMem((ULONG)(size+sizeof(struct GC_Amiga_AllocedMemoryHeader)),GC_AMIGA_MEMF); +if(gc_am==NULL)return NULL; +gc_am->next=GC_AMIGAMEM; +gc_am->size=size+sizeof(struct GC_Amiga_AllocedMemoryHeader); +GC_AMIGAMEM=(struct GC_Amiga_AllocedMemoryHeader*)(~(int)(gc_am)); +#ifdef GC_AMIGA_PRINTSTATS +if((char*)gc_amchipmax||ret==NULL){ +if(ret==NULL){ +nsucc++; +nsucc2+=size; +if(rec==0)ncur0++; +if(rec==1)ncur1++; +if(rec>1&&rec<10)ncur10++; +if(rec>=10&&rec<50)ncur50++; +if(rec>=50&&rec<150)ncur150++; +if(rec>=150)ncur151++; +}else{ +succ++; +succ2+=size; +if(rec==0)cur0++; +if(rec==1)cur1++; +if(rec>1&&rec<10)cur10++; +if(rec>=10&&rec<50)cur50++; +if(rec>=50&&rec<150)cur150++; +if(rec>=150)cur151++; +} +} +#endif +if (((char*)ret)<=chipmax&&ret!=NULL&&(rec<(size>500000?9:size/5000))){ +ret=GC_amiga_rec_alloc(size,AllocFunction,rec+1); +} +return ret; +} +#endif +void*GC_amiga_allocwrapper_any(size_t size,void*(*AllocFunction)(size_t size2)){ +void*ret; +GC_amiga_dontalloc=TRUE; +latestsize=size; +ret=(*AllocFunction)(size); +if(((char*)ret)<=chipmax){ +if(ret==NULL){ +#ifdef GC_AMIGA_GC +if(!GC_dont_gc){ +GC_gcollect(); +#ifdef GC_AMIGA_PRINTSTATS +numcollects++; +#endif +ret=(*AllocFunction)(size); +} +if(ret==NULL) +#endif +{ +GC_amiga_dontalloc=FALSE; +ret=(*AllocFunction)(size); +if(ret==NULL){ +WARN("Out of Memory!Returning NIL!\n",0); +} +} +#ifdef GC_AMIGA_PRINTSTATS +else{ +nullretries++; +} +if(ret!=NULL&&(char*)ret<=chipmax)chipa+=size; +#endif +} +#ifdef GC_AMIGA_RETRY +else{ +void*ret2; +if( +AllocFunction!=GC_malloc_uncollectable +#ifdef GC_ATOMIC_UNCOLLECTABLE +&&AllocFunction!=GC_malloc_atomic_uncollectable +#endif +){ +ret2=GC_amiga_rec_alloc(size,AllocFunction,0); +}else{ +ret2=(*AllocFunction)(size); +#ifdef GC_AMIGA_PRINTSTATS +if((char*)ret2chipmax){ +GC_free(ret); +ret=ret2; +}else{ +GC_free(ret2); +} +} +#endif +} +#if defined(CPPCHECK) +if (GC_amiga_dontalloc) +#endif +GC_amiga_dontalloc=FALSE; +return ret; +} +void (*GC_amiga_toany)(void)=NULL; +void GC_amiga_set_toany(void (*func)(void)){ +GC_amiga_toany=func; +} +#endif +void*GC_amiga_allocwrapper_fast(size_t size,void*(*AllocFunction)(size_t size2)){ +void*ret; +ret=(*AllocFunction)(size); +if(ret==NULL){ +#ifdef GC_AMIGA_GC +if(!GC_dont_gc){ +GC_gcollect(); +#ifdef GC_AMIGA_PRINTSTATS +numcollects++; +#endif +ret=(*AllocFunction)(size); +} +if(ret==NULL) +#endif +{ +#ifndef GC_AMIGA_ONLYFAST +GC_AMIGA_MEMF=MEMF_ANY|MEMF_CLEAR; +if(GC_amiga_toany!=NULL)(*GC_amiga_toany)(); +GC_amiga_allocwrapper_do=GC_amiga_allocwrapper_any; +return GC_amiga_allocwrapper_any(size,AllocFunction); +#endif +} +#ifdef GC_AMIGA_PRINTSTATS +else{ +nullretries++; +} +#endif +} +return ret; +} +void*GC_amiga_allocwrapper_firsttime(size_t size,void*(*AllocFunction)(size_t size2)){ +atexit(&GC_amiga_free_all_mem); +chipmax=(char*)SysBase->MaxLocMem; +GC_amiga_allocwrapper_do=GC_amiga_allocwrapper_fast; +return GC_amiga_allocwrapper_fast(size,AllocFunction); +} +#endif +void*GC_amiga_realloc(void*old_object,size_t new_size_in_bytes){ +#ifndef GC_AMIGA_FASTALLOC +return GC_realloc(old_object,new_size_in_bytes); +#else +void*ret; +latestsize=new_size_in_bytes; +ret=GC_realloc(old_object,new_size_in_bytes); +if(ret==NULL&&new_size_in_bytes!=0 +&&GC_AMIGA_MEMF==(MEMF_FAST|MEMF_CLEAR)){ +#ifdef GC_AMIGA_GC +if(!GC_dont_gc){ +GC_gcollect(); +#ifdef GC_AMIGA_PRINTSTATS +numcollects++; +#endif +ret=GC_realloc(old_object,new_size_in_bytes); +} +if(ret==NULL) +#endif +{ +#ifndef GC_AMIGA_ONLYFAST +GC_AMIGA_MEMF=MEMF_ANY|MEMF_CLEAR; +if(GC_amiga_toany!=NULL)(*GC_amiga_toany)(); +GC_amiga_allocwrapper_do=GC_amiga_allocwrapper_any; +ret=GC_realloc(old_object,new_size_in_bytes); +#endif +} +#ifdef GC_AMIGA_PRINTSTATS +else{ +nullretries++; +} +#endif +} +if(ret==NULL&&new_size_in_bytes!=0){ +WARN("Out of Memory!Returning NIL!\n",0); +} +#ifdef GC_AMIGA_PRINTSTATS +if(((char*)ret) +#endif +#ifdef IRIX5 +#include +#include +#endif +#if defined(MMAP_SUPPORTED)||defined(ADD_HEAP_GUARD_PAGES) +#if defined(USE_MUNMAP)&&!defined(USE_MMAP)&&!defined(CPPCHECK) +#error Invalid config:USE_MUNMAP requires USE_MMAP +#endif +#include +#include +#include +#include +#endif +#ifdef DARWIN +#include +#endif +#ifdef DJGPP +typedef long unsigned int caddr_t; +#endif +#ifdef PCR +#include "mm/PCR_MM.h" +#endif +#if defined(GC_DARWIN_THREADS)&&defined(MPROTECT_VDB) +#ifndef GC_DARWIN_STOP_WORLD_H +#define GC_DARWIN_STOP_WORLD_H +#if!defined(GC_DARWIN_THREADS) +#error darwin_stop_world.h included without GC_DARWIN_THREADS defined +#endif +#include +#include +EXTERN_C_BEGIN +struct thread_stop_info { +mach_port_t mach_thread; +ptr_t stack_ptr; +}; +#ifndef DARWIN_DONT_PARSE_STACK +GC_INNER ptr_t GC_FindTopOfStack(unsigned long); +#endif +#ifdef MPROTECT_VDB +GC_INNER void GC_mprotect_stop(void); +GC_INNER void GC_mprotect_resume(void); +#ifndef GC_NO_THREADS_DISCOVERY +GC_INNER void GC_darwin_register_mach_handler_thread(mach_port_t thread); +#endif +#endif +#if defined(PARALLEL_MARK)&&!defined(GC_NO_THREADS_DISCOVERY) +GC_INNER GC_bool GC_is_mach_marker(thread_act_t); +#endif +EXTERN_C_END +#endif +#endif +#if!defined(NO_EXECUTE_PERMISSION) +STATIC GC_bool GC_pages_executable=TRUE; +#else +STATIC GC_bool GC_pages_executable=FALSE; +#endif +#define IGNORE_PAGES_EXECUTABLE 1 +#ifdef NEED_PROC_MAPS +STATIC ssize_t GC_repeat_read(int fd,char*buf,size_t count) +{ +#define READ read +size_t num_read=0; +ASSERT_CANCEL_DISABLED(); +while (num_read < count){ +ssize_t result=READ(fd,buf+num_read,count - num_read); +if (result < 0)return result; +if (result==0)break; +num_read+=result; +} +#undef READ +return num_read; +} +#ifdef THREADS +STATIC size_t GC_get_file_len(int f) +{ +size_t total=0; +ssize_t result; +#define GET_FILE_LEN_BUF_SZ 500 +char buf[GET_FILE_LEN_BUF_SZ]; +do { +result=read(f,buf,GET_FILE_LEN_BUF_SZ); +if (result==-1)return 0; +total+=result; +} while (result > 0); +return total; +} +STATIC size_t GC_get_maps_len(void) +{ +int f=open("/proc/self/maps",O_RDONLY); +size_t result; +if (f < 0)return 0; +result=GC_get_file_len(f); +close(f); +return result; +} +#endif +GC_INNER char*GC_get_maps(void) +{ +ssize_t result; +static char*maps_buf=NULL; +static size_t maps_buf_sz=1; +size_t maps_size; +#ifdef THREADS +size_t old_maps_size=0; +#endif +GC_ASSERT(I_HOLD_LOCK()); +#ifdef THREADS +maps_size=GC_get_maps_len(); +if (0==maps_size)return 0; +#else +maps_size=4000; +#endif +do { +int f; +while (maps_size>=maps_buf_sz){ +#ifdef LINT2 +GC_noop1((word)maps_buf); +#else +GC_scratch_recycle_no_gww(maps_buf,maps_buf_sz); +#endif +while (maps_size>=maps_buf_sz)maps_buf_sz*=2; +maps_buf=GC_scratch_alloc(maps_buf_sz); +#ifdef THREADS +maps_size=GC_get_maps_len(); +if (0==maps_size)return 0; +#endif +if (maps_buf==0)return 0; +} +GC_ASSERT(maps_buf_sz>=maps_size+1); +f=open("/proc/self/maps",O_RDONLY); +if (-1==f)return 0; +#ifdef THREADS +old_maps_size=maps_size; +#endif +maps_size=0; +do { +result=GC_repeat_read(f,maps_buf,maps_buf_sz-1); +if (result<=0){ +close(f); +return 0; +} +maps_size+=result; +} while ((size_t)result==maps_buf_sz-1); +close(f); +#ifdef THREADS +if (maps_size > old_maps_size){ +WARN("Unexpected asynchronous/proc/self/maps growth" +" (to %" WARN_PRIdPTR " bytes)\n",maps_size); +} +#endif +} while (maps_size>=maps_buf_sz +#ifdef THREADS +||maps_size < old_maps_size +#endif +); +maps_buf[maps_size]='\0'; +return maps_buf; +} +#if (defined(DYNAMIC_LOADING)&&defined(USE_PROC_FOR_LIBRARIES))||defined(IA64)||defined(INCLUDE_LINUX_THREAD_DESCR)||defined(REDIRECT_MALLOC) +GC_INNER char*GC_parse_map_entry(char*buf_ptr,ptr_t*start,ptr_t*end, +char**prot,unsigned int*maj_dev, +char**mapping_name) +{ +unsigned char*start_start,*end_start,*maj_dev_start; +unsigned char*p; +if (buf_ptr==NULL||*buf_ptr=='\0'){ +return NULL; +} +p=(unsigned char*)buf_ptr; +while (isspace(*p))++p; +start_start=p; +GC_ASSERT(isxdigit(*start_start)); +*start=(ptr_t)strtoul((char*)start_start,(char**)&p,16); +GC_ASSERT(*p=='-'); +++p; +end_start=p; +GC_ASSERT(isxdigit(*end_start)); +*end=(ptr_t)strtoul((char*)end_start,(char**)&p,16); +GC_ASSERT(isspace(*p)); +while (isspace(*p))++p; +GC_ASSERT(*p=='r'||*p=='-'); +*prot=(char*)p; +while (!isspace(*p))++p; +while (isspace(*p))p++; +GC_ASSERT(isxdigit(*p)); +while (!isspace(*p))++p; +while (isspace(*p))p++; +maj_dev_start=p; +GC_ASSERT(isxdigit(*maj_dev_start)); +*maj_dev=strtoul((char*)maj_dev_start,NULL,16); +if (mapping_name==0){ +while (*p&&*p++!='\n'); +} else { +while (*p&&*p!='\n'&&*p!='/'&&*p!='[')p++; +*mapping_name=(char*)p; +while (*p&&*p++!='\n'); +} +return (char*)p; +} +#endif +#if defined(IA64)||defined(INCLUDE_LINUX_THREAD_DESCR) +GC_INNER GC_bool GC_enclosing_mapping(ptr_t addr,ptr_t*startp, +ptr_t*endp) +{ +char*prot; +ptr_t my_start,my_end; +unsigned int maj_dev; +char*maps=GC_get_maps(); +char*buf_ptr=maps; +if (0==maps)return(FALSE); +for (;;){ +buf_ptr=GC_parse_map_entry(buf_ptr,&my_start,&my_end, +&prot,&maj_dev,0); +if (buf_ptr==NULL)return FALSE; +if (prot[1]=='w'&&maj_dev==0){ +if ((word)my_end > (word)addr&&(word)my_start<=(word)addr){ +*startp=my_start; +*endp=my_end; +return TRUE; +} +} +} +return FALSE; +} +#endif +#if defined(REDIRECT_MALLOC) +GC_INNER GC_bool GC_text_mapping(char*nm,ptr_t*startp,ptr_t*endp) +{ +size_t nm_len=strlen(nm); +char*prot; +char*map_path; +ptr_t my_start,my_end; +unsigned int maj_dev; +char*maps=GC_get_maps(); +char*buf_ptr=maps; +if (0==maps)return(FALSE); +for (;;){ +buf_ptr=GC_parse_map_entry(buf_ptr,&my_start,&my_end, +&prot,&maj_dev,&map_path); +if (buf_ptr==NULL)return FALSE; +if (prot[0]=='r'&&prot[1]=='-'&&prot[2]=='x'){ +char*p=map_path; +while (*p!='\0'&&*p!='\n'&&*p!=' '&&*p!='\t')++p; +while (*p!='/'&&(word)p>=(word)map_path)--p; +++p; +if (strncmp(nm,p,nm_len)==0){ +*startp=my_start; +*endp=my_end; +return TRUE; +} +} +} +return FALSE; +} +#endif +#ifdef IA64 +static ptr_t backing_store_base_from_proc(void) +{ +ptr_t my_start,my_end; +if (!GC_enclosing_mapping(GC_save_regs_in_stack(),&my_start,&my_end)){ +GC_COND_LOG_PRINTF("Failed to find backing store base from/proc\n"); +return 0; +} +return my_start; +} +#endif +#endif +#if defined(SEARCH_FOR_DATA_START) +#if defined(LINUX)||defined(HURD) +EXTERN_C_BEGIN +#pragma weak __data_start +#pragma weak data_start +extern int __data_start[],data_start[]; +EXTERN_C_END +#endif +ptr_t GC_data_start=NULL; +GC_INNER void GC_init_linux_data_start(void) +{ +ptr_t data_end=DATAEND; +#if (defined(LINUX)||defined(HURD))&&defined(USE_PROG_DATA_START) +if (COVERT_DATAFLOW(__data_start)!=0){ +GC_data_start=(ptr_t)(__data_start); +} else { +GC_data_start=(ptr_t)(data_start); +} +if (COVERT_DATAFLOW(GC_data_start)!=0){ +if ((word)GC_data_start > (word)data_end) +ABORT_ARG2("Wrong __data_start/_end pair", +":%p .. %p",(void*)GC_data_start,(void*)data_end); +return; +} +#ifdef DEBUG_ADD_DEL_ROOTS +GC_log_printf("__data_start not provided\n"); +#endif +#endif +if (GC_no_dls){ +GC_data_start=data_end; +return; +} +GC_data_start=(ptr_t)GC_find_limit(data_end,FALSE); +} +#endif +#ifdef ECOS +#ifndef ECOS_GC_MEMORY_SIZE +#define ECOS_GC_MEMORY_SIZE (448*1024) +#endif +static char ecos_gc_memory[ECOS_GC_MEMORY_SIZE]; +static char*ecos_gc_brk=ecos_gc_memory; +static void*tiny_sbrk(ptrdiff_t increment) +{ +void*p=ecos_gc_brk; +ecos_gc_brk+=increment; +if ((word)ecos_gc_brk > (word)(ecos_gc_memory+sizeof(ecos_gc_memory))){ +ecos_gc_brk-=increment; +return NULL; +} +return p; +} +#define sbrk tiny_sbrk +#endif +#if defined(NETBSD)&&defined(__ELF__) +ptr_t GC_data_start=NULL; +EXTERN_C_BEGIN +extern char**environ; +EXTERN_C_END +GC_INNER void GC_init_netbsd_elf(void) +{ +GC_data_start=(ptr_t)GC_find_limit(&environ,FALSE); +} +#endif +#if defined(ADDRESS_SANITIZER)&&(defined(UNIX_LIKE)||defined(NEED_FIND_LIMIT)||defined(MPROTECT_VDB))&&!defined(CUSTOM_ASAN_DEF_OPTIONS) +GC_API const char*__asan_default_options(void) +{ +return "allow_user_segv_handler=1"; +} +#endif +#ifdef OPENBSD +static struct sigaction old_segv_act; +STATIC JMP_BUF GC_jmp_buf_openbsd; +STATIC void GC_fault_handler_openbsd(int sig GC_ATTR_UNUSED) +{ +LONGJMP(GC_jmp_buf_openbsd,1); +} +#ifdef GC_OPENBSD_UTHREADS +#include +EXTERN_C_BEGIN +extern sigset_t __syscall(quad_t,...); +EXTERN_C_END +STATIC ptr_t GC_find_limit_openbsd(ptr_t p,ptr_t bound) +{ +static volatile ptr_t result; +struct sigaction act; +word pgsz=(word)sysconf(_SC_PAGESIZE); +GC_ASSERT((word)bound>=pgsz); +GC_ASSERT(I_HOLD_LOCK()); +act.sa_handler=GC_fault_handler_openbsd; +sigemptyset(&act.sa_mask); +act.sa_flags=SA_NODEFER|SA_RESTART; +sigaction(SIGSEGV,&act,&old_segv_act); +if (SETJMP(GC_jmp_buf_openbsd)==0){ +result=(ptr_t)((word)p&~(pgsz-1)); +for (;;){ +if ((word)result>=(word)bound - pgsz){ +result=bound; +break; +} +result+=pgsz; +GC_noop1((word)(*result)); +} +} +#ifdef THREADS +__syscall(SYS_sigprocmask,SIG_UNBLOCK,sigmask(SIGPROF)); +#endif +sigaction(SIGSEGV,&old_segv_act,0); +return(result); +} +#endif +static volatile int firstpass; +STATIC ptr_t GC_skip_hole_openbsd(ptr_t p,ptr_t bound) +{ +static volatile ptr_t result; +struct sigaction act; +word pgsz=(word)sysconf(_SC_PAGESIZE); +GC_ASSERT((word)bound>=pgsz); +GC_ASSERT(I_HOLD_LOCK()); +act.sa_handler=GC_fault_handler_openbsd; +sigemptyset(&act.sa_mask); +act.sa_flags=SA_NODEFER|SA_RESTART; +sigaction(SIGSEGV,&act,&old_segv_act); +firstpass=1; +result=(ptr_t)((word)p&~(pgsz-1)); +if (SETJMP(GC_jmp_buf_openbsd)!=0||firstpass){ +firstpass=0; +if ((word)result>=(word)bound - pgsz){ +result=bound; +} else { +result+=pgsz; +GC_noop1((word)(*result)); +} +} +sigaction(SIGSEGV,&old_segv_act,0); +return(result); +} +#endif +#ifdef OS2 +#include +#if!defined(__IBMC__)&&!defined(__WATCOMC__) +struct exe_hdr { +unsigned short magic_number; +unsigned short padding[29]; +long new_exe_offset; +}; +#define E_MAGIC(x)(x).magic_number +#define EMAGIC 0x5A4D +#define E_LFANEW(x)(x).new_exe_offset +struct e32_exe { +unsigned char magic_number[2]; +unsigned char byte_order; +unsigned char word_order; +unsigned long exe_format_level; +unsigned short cpu; +unsigned short os; +unsigned long padding1[13]; +unsigned long object_table_offset; +unsigned long object_count; +unsigned long padding2[31]; +}; +#define E32_MAGIC1(x)(x).magic_number[0] +#define E32MAGIC1 'L' +#define E32_MAGIC2(x)(x).magic_number[1] +#define E32MAGIC2 'X' +#define E32_BORDER(x)(x).byte_order +#define E32LEBO 0 +#define E32_WORDER(x)(x).word_order +#define E32LEWO 0 +#define E32_CPU(x)(x).cpu +#define E32CPU286 1 +#define E32_OBJTAB(x)(x).object_table_offset +#define E32_OBJCNT(x)(x).object_count +struct o32_obj { +unsigned long size; +unsigned long base; +unsigned long flags; +unsigned long pagemap; +unsigned long mapsize; +unsigned long reserved; +}; +#define O32_FLAGS(x)(x).flags +#define OBJREAD 0x0001L +#define OBJWRITE 0x0002L +#define OBJINVALID 0x0080L +#define O32_SIZE(x)(x).size +#define O32_BASE(x)(x).base +#else +#ifndef WORD +#define WORD unsigned short +#endif +#ifndef DWORD +#define DWORD unsigned long +#endif +#define EXE386 1 +#include +#include +#endif +#define INCL_DOSEXCEPTIONS +#define INCL_DOSPROCESS +#define INCL_DOSERRORS +#define INCL_DOSMODULEMGR +#define INCL_DOSMEMMGR +#include +#endif +GC_INNER size_t GC_page_size=0; +#if defined(MSWIN32)||defined(MSWINCE)||defined(CYGWIN32) +#ifndef VER_PLATFORM_WIN32_CE +#define VER_PLATFORM_WIN32_CE 3 +#endif +#if defined(MSWINCE)&&defined(THREADS) +GC_INNER GC_bool GC_dont_query_stack_min=FALSE; +#endif +GC_INNER SYSTEM_INFO GC_sysinfo; +GC_INNER void GC_setpagesize(void) +{ +GetSystemInfo(&GC_sysinfo); +#if defined(CYGWIN32)&&(defined(MPROTECT_VDB)||defined(USE_MUNMAP)) +GC_page_size=(size_t)GC_sysinfo.dwAllocationGranularity; +GC_ASSERT(GC_page_size>=(size_t)GC_sysinfo.dwPageSize); +#else +GC_page_size=(size_t)GC_sysinfo.dwPageSize; +#endif +#if defined(MSWINCE)&&!defined(_WIN32_WCE_EMULATION) +{ +OSVERSIONINFO verInfo; +verInfo.dwOSVersionInfoSize=sizeof(OSVERSIONINFO); +if (!GetVersionEx(&verInfo)) +ABORT("GetVersionEx failed"); +if (verInfo.dwPlatformId==VER_PLATFORM_WIN32_CE&& +verInfo.dwMajorVersion < 6){ +GC_sysinfo.lpMaximumApplicationAddress=(LPVOID)((word)32<<20); +#ifdef THREADS +if (verInfo.dwMajorVersion < 5) +GC_dont_query_stack_min=TRUE; +#endif +} +} +#endif +} +#ifndef CYGWIN32 +#define is_writable(prot)((prot)==PAGE_READWRITE||(prot)==PAGE_WRITECOPY||(prot)==PAGE_EXECUTE_READWRITE||(prot)==PAGE_EXECUTE_WRITECOPY) +STATIC word GC_get_writable_length(ptr_t p,ptr_t*base) +{ +MEMORY_BASIC_INFORMATION buf; +word result; +word protect; +result=VirtualQuery(p,&buf,sizeof(buf)); +if (result!=sizeof(buf))ABORT("Weird VirtualQuery result"); +if (base!=0)*base=(ptr_t)(buf.AllocationBase); +protect=(buf.Protect&~(PAGE_GUARD|PAGE_NOCACHE)); +if (!is_writable(protect)){ +return(0); +} +if (buf.State!=MEM_COMMIT)return(0); +return(buf.RegionSize); +} +GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base*sb) +{ +ptr_t trunc_sp; +word size; +if (!GC_page_size)GC_setpagesize(); +trunc_sp=(ptr_t)((word)GC_approx_sp()&~(GC_page_size - 1)); +size=GC_get_writable_length(trunc_sp,0); +GC_ASSERT(size!=0); +sb->mem_base=trunc_sp+size; +return GC_SUCCESS; +} +#else +GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base*sb) +{ +#ifdef X86_64 +sb->mem_base=((NT_TIB*)NtCurrentTeb())->StackBase; +#else +void*_tlsbase; +__asm__ ("movl %%fs:4,%0" +:"=r" (_tlsbase)); +sb->mem_base=_tlsbase; +#endif +return GC_SUCCESS; +} +#endif +#define HAVE_GET_STACK_BASE +#else +GC_INNER void GC_setpagesize(void) +{ +#if defined(MPROTECT_VDB)||defined(PROC_VDB)||defined(USE_MMAP) +GC_page_size=(size_t)GETPAGESIZE(); +#if!defined(CPPCHECK) +if (0==GC_page_size) +ABORT("getpagesize failed"); +#endif +#else +GC_page_size=HBLKSIZE; +#endif +} +#endif +#ifdef HAIKU +#include +GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base*sb) +{ +thread_info th; +get_thread_info(find_thread(NULL),&th); +sb->mem_base=th.stack_end; +return GC_SUCCESS; +} +#define HAVE_GET_STACK_BASE +#endif +#ifdef OS2 +GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base*sb) +{ +PTIB ptib; +PPIB ppib; +if (DosGetInfoBlocks(&ptib,&ppib)!=NO_ERROR){ +WARN("DosGetInfoBlocks failed\n",0); +return GC_UNIMPLEMENTED; +} +sb->mem_base=ptib->tib_pstacklimit; +return GC_SUCCESS; +} +#define HAVE_GET_STACK_BASE +#endif +#ifdef AMIGA +#define GC_AMIGA_SB +#undef GC_AMIGA_SB +#define GET_MAIN_STACKBASE_SPECIAL +#endif +#if defined(NEED_FIND_LIMIT)||defined(UNIX_LIKE) +typedef void (*GC_fault_handler_t)(int); +#if defined(SUNOS5SIGS)||defined(IRIX5)||defined(OSF1)||defined(HAIKU)||defined(HURD)||defined(FREEBSD)||defined(NETBSD) +static struct sigaction old_segv_act; +#if defined(_sigargs)||defined(HURD)||defined(NETBSD)||defined(FREEBSD) +static struct sigaction old_bus_act; +#endif +#else +static GC_fault_handler_t old_segv_handler; +#ifdef HAVE_SIGBUS +static GC_fault_handler_t old_bus_handler; +#endif +#endif +GC_INNER void GC_set_and_save_fault_handler(GC_fault_handler_t h) +{ +#if defined(SUNOS5SIGS)||defined(IRIX5)||defined(OSF1)||defined(HAIKU)||defined(HURD)||defined(FREEBSD)||defined(NETBSD)||defined(OPENBSD) +struct sigaction act; +act.sa_handler=h; +#ifdef SIGACTION_FLAGS_NODEFER_HACK +act.sa_flags=SA_RESTART|SA_NODEFER; +#else +act.sa_flags=SA_RESTART; +#endif +(void)sigemptyset(&act.sa_mask); +#ifdef GC_IRIX_THREADS +(void)sigaction(SIGSEGV,0,&old_segv_act); +(void)sigaction(SIGSEGV,&act,0); +#else +(void)sigaction(SIGSEGV,&act,&old_segv_act); +#if defined(IRIX5)&&defined(_sigargs)||defined(HURD)||defined(NETBSD)||defined(FREEBSD) +(void)sigaction(SIGBUS,&act,&old_bus_act); +#endif +#endif +#else +old_segv_handler=signal(SIGSEGV,h); +#ifdef HAVE_SIGBUS +old_bus_handler=signal(SIGBUS,h); +#endif +#endif +#if defined(CPPCHECK)&&defined(ADDRESS_SANITIZER) +GC_noop1((word)&__asan_default_options); +#endif +} +#endif +#if defined(NEED_FIND_LIMIT)||(defined(USE_PROC_FOR_LIBRARIES)&&defined(THREADS)) +#define MIN_PAGE_SIZE 256 +GC_INNER JMP_BUF GC_jmp_buf; +STATIC void GC_fault_handler(int sig GC_ATTR_UNUSED) +{ +LONGJMP(GC_jmp_buf,1); +} +GC_INNER void GC_setup_temporary_fault_handler(void) +{ +GC_ASSERT(I_HOLD_LOCK()); +GC_set_and_save_fault_handler(GC_fault_handler); +} +GC_INNER void GC_reset_fault_handler(void) +{ +#if defined(SUNOS5SIGS)||defined(IRIX5)||defined(OSF1)||defined(HAIKU)||defined(HURD)||defined(FREEBSD)||defined(NETBSD)||defined(OPENBSD) +(void)sigaction(SIGSEGV,&old_segv_act,0); +#if defined(IRIX5)&&defined(_sigargs)||defined(HURD)||defined(NETBSD) +(void)sigaction(SIGBUS,&old_bus_act,0); +#endif +#else +(void)signal(SIGSEGV,old_segv_handler); +#ifdef HAVE_SIGBUS +(void)signal(SIGBUS,old_bus_handler); +#endif +#endif +} +GC_ATTR_NO_SANITIZE_ADDR +STATIC ptr_t GC_find_limit_with_bound(ptr_t p,GC_bool up,ptr_t bound) +{ +static volatile ptr_t result; +GC_ASSERT(up?(word)bound>=MIN_PAGE_SIZE +:(word)bound<=~(word)MIN_PAGE_SIZE); +GC_ASSERT(I_HOLD_LOCK()); +GC_setup_temporary_fault_handler(); +if (SETJMP(GC_jmp_buf)==0){ +result=(ptr_t)(((word)(p)) +&~(MIN_PAGE_SIZE-1)); +for (;;){ +if (up){ +if ((word)result>=(word)bound - MIN_PAGE_SIZE){ +result=bound; +break; +} +result+=MIN_PAGE_SIZE; +} else { +if ((word)result<=(word)bound+MIN_PAGE_SIZE){ +result=bound - MIN_PAGE_SIZE; +break; +} +result-=MIN_PAGE_SIZE; +} +GC_noop1((word)(*result)); +} +} +GC_reset_fault_handler(); +if (!up){ +result+=MIN_PAGE_SIZE; +} +return(result); +} +void*GC_find_limit(void*p,int up) +{ +return GC_find_limit_with_bound((ptr_t)p,(GC_bool)up, +up?(ptr_t)GC_WORD_MAX:0); +} +#endif +#ifdef HPUX_MAIN_STACKBOTTOM +#include +#include +STATIC ptr_t GC_hpux_main_stack_base(void) +{ +struct pst_vm_status vm_status; +int i=0; +while (pstat_getprocvm(&vm_status,sizeof(vm_status),0,i++)==1){ +if (vm_status.pst_type==PS_STACK) +return (ptr_t)vm_status.pst_vaddr; +} +#ifdef STACK_GROWS_UP +return (ptr_t)GC_find_limit(GC_approx_sp(),FALSE); +#else +return (ptr_t)GC_find_limit(GC_approx_sp(),TRUE); +#endif +} +#endif +#ifdef HPUX_STACKBOTTOM +#include +#include +GC_INNER ptr_t GC_get_register_stack_base(void) +{ +struct pst_vm_status vm_status; +int i=0; +while (pstat_getprocvm(&vm_status,sizeof(vm_status),0,i++)==1){ +if (vm_status.pst_type==PS_RSESTACK){ +return (ptr_t)vm_status.pst_vaddr; +} +} +return (ptr_t)(((word)GC_stackbottom - BACKING_STORE_DISPLACEMENT - 1) +&~(BACKING_STORE_ALIGNMENT - 1)); +} +#endif +#ifdef LINUX_STACKBOTTOM +#include +#include +#define STAT_SKIP 27 +#ifdef USE_LIBC_PRIVATES +EXTERN_C_BEGIN +#pragma weak __libc_stack_end +extern ptr_t __libc_stack_end; +#ifdef IA64 +#pragma weak __libc_ia64_register_backing_store_base +extern ptr_t __libc_ia64_register_backing_store_base; +#endif +EXTERN_C_END +#endif +#ifdef IA64 +GC_INNER ptr_t GC_get_register_stack_base(void) +{ +ptr_t result; +#ifdef USE_LIBC_PRIVATES +if (0!=&__libc_ia64_register_backing_store_base +&&0!=__libc_ia64_register_backing_store_base){ +return __libc_ia64_register_backing_store_base; +} +#endif +result=backing_store_base_from_proc(); +if (0==result){ +result=(ptr_t)GC_find_limit(GC_save_regs_in_stack(),FALSE); +} +return result; +} +#endif +STATIC ptr_t GC_linux_main_stack_base(void) +{ +#ifndef STAT_READ +#define STAT_BUF_SIZE 4096 +#define STAT_READ read +#endif +char stat_buf[STAT_BUF_SIZE]; +int f; +word result; +int i,buf_offset=0,len; +#ifdef USE_LIBC_PRIVATES +if (0!=&__libc_stack_end&&0!=__libc_stack_end){ +#if defined(IA64) +if (((word)__libc_stack_end&0xfff)+0x10 < 0x1000){ +return __libc_stack_end+0x10; +} +#elif defined(SPARC) +if (__libc_stack_end!=(ptr_t)(unsigned long)0x1) +return __libc_stack_end; +#else +return __libc_stack_end; +#endif +} +#endif +f=open("/proc/self/stat",O_RDONLY); +if (f < 0) +ABORT("Couldn't read/proc/self/stat"); +len=STAT_READ(f,stat_buf,STAT_BUF_SIZE); +close(f); +for (i=0;i < STAT_SKIP;++i){ +while (buf_offset < len&&isspace(stat_buf[buf_offset++])){ +} +while (buf_offset < len&&!isspace(stat_buf[buf_offset++])){ +} +} +while (buf_offset < len&&isspace(stat_buf[buf_offset])){ +buf_offset++; +} +for (i=0;buf_offset+i < len;i++){ +if (!isdigit(stat_buf[buf_offset+i]))break; +} +if (buf_offset+i>=len)ABORT("Could not parse/proc/self/stat"); +stat_buf[buf_offset+i]='\0'; +result=(word)STRTOULL(&stat_buf[buf_offset],NULL,10); +if (result < 0x100000||(result&(sizeof(word)- 1))!=0) +ABORT("Absurd stack bottom value"); +return (ptr_t)result; +} +#endif +#ifdef FREEBSD_STACKBOTTOM +#include +#include +#include +STATIC ptr_t GC_freebsd_main_stack_base(void) +{ +int nm[2]={CTL_KERN,KERN_USRSTACK}; +ptr_t base; +size_t len=sizeof(ptr_t); +int r=sysctl(nm,2,&base,&len,NULL,0); +if (r)ABORT("Error getting main stack base"); +return base; +} +#endif +#if defined(ECOS)||defined(NOSYS) +ptr_t GC_get_main_stack_base(void) +{ +return STACKBOTTOM; +} +#define GET_MAIN_STACKBASE_SPECIAL +#elif defined(SYMBIAN) +EXTERN_C_BEGIN +extern int GC_get_main_symbian_stack_base(void); +EXTERN_C_END +ptr_t GC_get_main_stack_base(void) +{ +return (ptr_t)GC_get_main_symbian_stack_base(); +} +#define GET_MAIN_STACKBASE_SPECIAL +#elif defined(__EMSCRIPTEN__) +#include +static void*emscripten_stack_base; +static void scan_stack_cb(void*begin,void*end) +{ +(void)begin; +emscripten_stack_base=end; +} +ptr_t GC_get_main_stack_base(void) +{ +emscripten_scan_stack(scan_stack_cb); +return (ptr_t)emscripten_stack_base; +} +#define GET_MAIN_STACKBASE_SPECIAL +#elif!defined(AMIGA)&&!defined(HAIKU)&&!defined(OS2)&&!defined(MSWIN32)&&!defined(MSWINCE)&&!defined(CYGWIN32)&&!defined(GC_OPENBSD_THREADS)&&(!defined(GC_SOLARIS_THREADS)||defined(_STRICT_STDC)) +#if (defined(HAVE_PTHREAD_ATTR_GET_NP)||defined(HAVE_PTHREAD_GETATTR_NP))&&(defined(THREADS)||defined(USE_GET_STACKBASE_FOR_MAIN)) +#include +#ifdef HAVE_PTHREAD_NP_H +#include +#endif +#elif defined(DARWIN)&&!defined(NO_PTHREAD_GET_STACKADDR_NP) +#include +#undef STACKBOTTOM +#define STACKBOTTOM (ptr_t)pthread_get_stackaddr_np(pthread_self()) +#endif +ptr_t GC_get_main_stack_base(void) +{ +ptr_t result; +#if (defined(HAVE_PTHREAD_ATTR_GET_NP)||defined(HAVE_PTHREAD_GETATTR_NP))&&(defined(USE_GET_STACKBASE_FOR_MAIN)||(defined(THREADS)&&!defined(REDIRECT_MALLOC))) +pthread_attr_t attr; +void*stackaddr; +size_t size; +#ifdef HAVE_PTHREAD_ATTR_GET_NP +if (pthread_attr_init(&attr)==0 +&&(pthread_attr_get_np(pthread_self(),&attr)==0 +?TRUE:(pthread_attr_destroy(&attr),FALSE))) +#else +if (pthread_getattr_np(pthread_self(),&attr)==0) +#endif +{ +if (pthread_attr_getstack(&attr,&stackaddr,&size)==0 +&&stackaddr!=NULL){ +(void)pthread_attr_destroy(&attr); +#ifdef STACK_GROWS_DOWN +stackaddr=(char*)stackaddr+size; +#endif +return (ptr_t)stackaddr; +} +(void)pthread_attr_destroy(&attr); +} +WARN("pthread_getattr_np or pthread_attr_getstack failed" +" for main thread\n",0); +#endif +#ifdef STACKBOTTOM +result=STACKBOTTOM; +#else +#ifdef HEURISTIC1 +#define STACKBOTTOM_ALIGNMENT_M1 ((word)STACK_GRAN - 1) +#ifdef STACK_GROWS_DOWN +result=(ptr_t)(((word)GC_approx_sp()+STACKBOTTOM_ALIGNMENT_M1) +&~STACKBOTTOM_ALIGNMENT_M1); +#else +result=(ptr_t)((word)GC_approx_sp()&~STACKBOTTOM_ALIGNMENT_M1); +#endif +#elif defined(HPUX_MAIN_STACKBOTTOM) +result=GC_hpux_main_stack_base(); +#elif defined(LINUX_STACKBOTTOM) +result=GC_linux_main_stack_base(); +#elif defined(FREEBSD_STACKBOTTOM) +result=GC_freebsd_main_stack_base(); +#elif defined(HEURISTIC2) +{ +ptr_t sp=GC_approx_sp(); +#ifdef STACK_GROWS_DOWN +result=(ptr_t)GC_find_limit(sp,TRUE); +#if defined(HEURISTIC2_LIMIT)&&!defined(CPPCHECK) +if ((word)result > (word)HEURISTIC2_LIMIT +&&(word)sp < (word)HEURISTIC2_LIMIT){ +result=HEURISTIC2_LIMIT; +} +#endif +#else +result=(ptr_t)GC_find_limit(sp,FALSE); +#if defined(HEURISTIC2_LIMIT)&&!defined(CPPCHECK) +if ((word)result < (word)HEURISTIC2_LIMIT +&&(word)sp > (word)HEURISTIC2_LIMIT){ +result=HEURISTIC2_LIMIT; +} +#endif +#endif +} +#elif defined(STACK_NOT_SCANNED)||defined(CPPCHECK) +result=NULL; +#else +#error None of HEURISTIC*and*STACKBOTTOM defined! +#endif +#if defined(STACK_GROWS_DOWN)&&!defined(CPPCHECK) +if (result==0) +result=(ptr_t)(signed_word)(-sizeof(ptr_t)); +#endif +#endif +#if!defined(CPPCHECK) +GC_ASSERT((word)GC_approx_sp()HOTTER_THAN (word)result); +#endif +return(result); +} +#define GET_MAIN_STACKBASE_SPECIAL +#endif +#if (defined(HAVE_PTHREAD_ATTR_GET_NP)||defined(HAVE_PTHREAD_GETATTR_NP))&&defined(THREADS)&&!defined(HAVE_GET_STACK_BASE) +#include +#ifdef HAVE_PTHREAD_NP_H +#include +#endif +GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base*b) +{ +pthread_attr_t attr; +size_t size; +#ifdef IA64 +DCL_LOCK_STATE; +#endif +#ifdef HAVE_PTHREAD_ATTR_GET_NP +if (pthread_attr_init(&attr)!=0) +ABORT("pthread_attr_init failed"); +if (pthread_attr_get_np(pthread_self(),&attr)!=0){ +WARN("pthread_attr_get_np failed\n",0); +(void)pthread_attr_destroy(&attr); +return GC_UNIMPLEMENTED; +} +#else +if (pthread_getattr_np(pthread_self(),&attr)!=0){ +WARN("pthread_getattr_np failed\n",0); +return GC_UNIMPLEMENTED; +} +#endif +if (pthread_attr_getstack(&attr,&(b->mem_base),&size)!=0){ +ABORT("pthread_attr_getstack failed"); +} +(void)pthread_attr_destroy(&attr); +#ifdef STACK_GROWS_DOWN +b->mem_base=(char*)(b->mem_base)+size; +#endif +#ifdef IA64 +LOCK(); +{ +IF_CANCEL(int cancel_state;) +ptr_t bsp; +ptr_t next_stack; +DISABLE_CANCEL(cancel_state); +bsp=GC_save_regs_in_stack(); +next_stack=GC_greatest_stack_base_below(bsp); +if (0==next_stack){ +b->reg_base=GC_find_limit(bsp,FALSE); +} else { +b->reg_base=GC_find_limit_with_bound(bsp,FALSE,next_stack); +} +RESTORE_CANCEL(cancel_state); +} +UNLOCK(); +#endif +return GC_SUCCESS; +} +#define HAVE_GET_STACK_BASE +#endif +#if defined(GC_DARWIN_THREADS)&&!defined(NO_PTHREAD_GET_STACKADDR_NP) +#include +GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base*b) +{ +b->mem_base=pthread_get_stackaddr_np(pthread_self()); +GC_ASSERT((word)GC_approx_sp()HOTTER_THAN (word)b->mem_base); +return GC_SUCCESS; +} +#define HAVE_GET_STACK_BASE +#endif +#ifdef GC_OPENBSD_THREADS +#include +#include +#include +GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base*sb) +{ +stack_t stack; +if (pthread_stackseg_np(pthread_self(),&stack)) +ABORT("pthread_stackseg_np(self)failed"); +sb->mem_base=stack.ss_sp; +return GC_SUCCESS; +} +#define HAVE_GET_STACK_BASE +#endif +#if defined(GC_SOLARIS_THREADS)&&!defined(_STRICT_STDC) +#include +#include +#include +static pthread_t stackbase_main_self=0; +static void*stackbase_main_ss_sp=NULL; +GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base*b) +{ +stack_t s; +pthread_t self=pthread_self(); +if (self==stackbase_main_self) +{ +b->mem_base=stackbase_main_ss_sp; +GC_ASSERT(b->mem_base!=NULL); +return GC_SUCCESS; +} +if (thr_stksegment(&s)){ +ABORT("thr_stksegment failed"); +} +GC_ASSERT((word)GC_approx_sp()HOTTER_THAN (word)s.ss_sp); +if (!stackbase_main_self&&thr_main()!=0) +{ +stackbase_main_ss_sp=s.ss_sp; +stackbase_main_self=self; +} +b->mem_base=s.ss_sp; +return GC_SUCCESS; +} +#define HAVE_GET_STACK_BASE +#endif +#ifdef GC_RTEMS_PTHREADS +GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base*sb) +{ +sb->mem_base=rtems_get_stack_bottom(); +return GC_SUCCESS; +} +#define HAVE_GET_STACK_BASE +#endif +#ifndef HAVE_GET_STACK_BASE +#ifdef NEED_FIND_LIMIT +GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base*b) +{ +IF_CANCEL(int cancel_state;) +DCL_LOCK_STATE; +LOCK(); +DISABLE_CANCEL(cancel_state); +#ifdef STACK_GROWS_DOWN +b->mem_base=GC_find_limit(GC_approx_sp(),TRUE); +#ifdef IA64 +b->reg_base=GC_find_limit(GC_save_regs_in_stack(),FALSE); +#endif +#else +b->mem_base=GC_find_limit(GC_approx_sp(),FALSE); +#endif +RESTORE_CANCEL(cancel_state); +UNLOCK(); +return GC_SUCCESS; +} +#else +GC_API int GC_CALL GC_get_stack_base( +struct GC_stack_base*b GC_ATTR_UNUSED) +{ +#if defined(GET_MAIN_STACKBASE_SPECIAL)&&!defined(THREADS)&&!defined(IA64) +b->mem_base=GC_get_main_stack_base(); +return GC_SUCCESS; +#else +return GC_UNIMPLEMENTED; +#endif +} +#endif +#endif +#ifndef GET_MAIN_STACKBASE_SPECIAL +ptr_t GC_get_main_stack_base(void) +{ +struct GC_stack_base sb; +if (GC_get_stack_base(&sb)!=GC_SUCCESS) +ABORT("GC_get_stack_base failed"); +GC_ASSERT((word)GC_approx_sp()HOTTER_THAN (word)sb.mem_base); +return (ptr_t)sb.mem_base; +} +#endif +#ifdef OS2 +void GC_register_data_segments(void) +{ +PTIB ptib; +PPIB ppib; +HMODULE module_handle; +#define PBUFSIZ 512 +UCHAR path[PBUFSIZ]; +FILE*myexefile; +struct exe_hdr hdrdos; +struct e32_exe hdr386; +struct o32_obj seg; +int nsegs; +#if defined(CPPCHECK) +hdrdos.padding[0]=0; +hdr386.exe_format_level=0; +hdr386.os=0; +hdr386.padding1[0]=0; +hdr386.padding2[0]=0; +seg.pagemap=0; +seg.mapsize=0; +seg.reserved=0; +#endif +if (DosGetInfoBlocks(&ptib,&ppib)!=NO_ERROR){ +ABORT("DosGetInfoBlocks failed"); +} +module_handle=ppib->pib_hmte; +if (DosQueryModuleName(module_handle,PBUFSIZ,path)!=NO_ERROR){ +ABORT("DosQueryModuleName failed"); +} +myexefile=fopen(path,"rb"); +if (myexefile==0){ +ABORT_ARG1("Failed to open executable",":%s",path); +} +if (fread((char*)(&hdrdos),1,sizeof(hdrdos),myexefile) +< sizeof(hdrdos)){ +ABORT_ARG1("Could not read MSDOS header"," from:%s",path); +} +if (E_MAGIC(hdrdos)!=EMAGIC){ +ABORT_ARG1("Bad DOS magic number"," in file:%s",path); +} +if (fseek(myexefile,E_LFANEW(hdrdos),SEEK_SET)!=0){ +ABORT_ARG1("Bad DOS magic number"," in file:%s",path); +} +if (fread((char*)(&hdr386),1,sizeof(hdr386),myexefile) +< sizeof(hdr386)){ +ABORT_ARG1("Could not read OS/2 header"," from:%s",path); +} +if (E32_MAGIC1(hdr386)!=E32MAGIC1||E32_MAGIC2(hdr386)!=E32MAGIC2){ +ABORT_ARG1("Bad OS/2 magic number"," in file:%s",path); +} +if (E32_BORDER(hdr386)!=E32LEBO||E32_WORDER(hdr386)!=E32LEWO){ +ABORT_ARG1("Bad byte order in executable"," file:%s",path); +} +if (E32_CPU(hdr386)==E32CPU286){ +ABORT_ARG1("GC cannot handle 80286 executables",":%s",path); +} +if (fseek(myexefile,E_LFANEW(hdrdos)+E32_OBJTAB(hdr386), +SEEK_SET)!=0){ +ABORT_ARG1("Seek to object table failed"," in file:%s",path); +} +for (nsegs=E32_OBJCNT(hdr386);nsegs > 0;nsegs--){ +int flags; +if (fread((char*)(&seg),1,sizeof(seg),myexefile)< sizeof(seg)){ +ABORT_ARG1("Could not read obj table entry"," from file:%s",path); +} +flags=O32_FLAGS(seg); +if (!(flags&OBJWRITE))continue; +if (!(flags&OBJREAD))continue; +if (flags&OBJINVALID){ +GC_err_printf("Object with invalid pages?\n"); +continue; +} +GC_add_roots_inner((ptr_t)O32_BASE(seg), +(ptr_t)(O32_BASE(seg)+O32_SIZE(seg)),FALSE); +} +(void)fclose(myexefile); +} +#else +#if defined(GWW_VDB) +#ifndef MEM_WRITE_WATCH +#define MEM_WRITE_WATCH 0x200000 +#endif +#ifndef WRITE_WATCH_FLAG_RESET +#define WRITE_WATCH_FLAG_RESET 1 +#endif +#define GC_ULONG_PTR word +typedef UINT (WINAPI*GetWriteWatch_type)( +DWORD,PVOID,GC_ULONG_PTR, +PVOID*,GC_ULONG_PTR*,PULONG); +static FARPROC GetWriteWatch_func; +static DWORD GetWriteWatch_alloc_flag; +#define GC_GWW_AVAILABLE()(GetWriteWatch_func!=0) +static void detect_GetWriteWatch(void) +{ +static GC_bool done; +HMODULE hK32; +if (done) +return; +#if defined(MPROTECT_VDB) +{ +char*str=GETENV("GC_USE_GETWRITEWATCH"); +#if defined(GC_PREFER_MPROTECT_VDB) +if (str==NULL||(*str=='0'&&*(str+1)=='\0')){ +done=TRUE; +return; +} +#else +if (str!=NULL&&*str=='0'&&*(str+1)=='\0'){ +done=TRUE; +return; +} +#endif +} +#endif +#ifdef MSWINRT_FLAVOR +{ +MEMORY_BASIC_INFORMATION memInfo; +SIZE_T result=VirtualQuery(GetProcAddress, +&memInfo,sizeof(memInfo)); +if (result!=sizeof(memInfo)) +ABORT("Weird VirtualQuery result"); +hK32=(HMODULE)memInfo.AllocationBase; +} +#else +hK32=GetModuleHandle(TEXT("kernel32.dll")); +#endif +if (hK32!=(HMODULE)0&& +(GetWriteWatch_func=GetProcAddress(hK32,"GetWriteWatch"))!=0){ +void*page; +GC_ASSERT(GC_page_size!=0); +page=VirtualAlloc(NULL,GC_page_size,MEM_WRITE_WATCH|MEM_RESERVE, +PAGE_READWRITE); +if (page!=NULL){ +PVOID pages[16]; +GC_ULONG_PTR count=16; +DWORD page_size; +if ((*(GetWriteWatch_type)(word)GetWriteWatch_func)( +WRITE_WATCH_FLAG_RESET,page, +GC_page_size,pages,&count, +&page_size)!=0){ +GetWriteWatch_func=0; +} else { +GetWriteWatch_alloc_flag=MEM_WRITE_WATCH; +} +VirtualFree(page,0,MEM_RELEASE); +} else { +GetWriteWatch_func=0; +} +} +#ifndef SMALL_CONFIG +if (!GetWriteWatch_func){ +GC_COND_LOG_PRINTF("Did not find a usable GetWriteWatch()\n"); +} else { +GC_COND_LOG_PRINTF("Using GetWriteWatch()\n"); +} +#endif +done=TRUE; +} +#else +#define GetWriteWatch_alloc_flag 0 +#endif +#if defined(MSWIN32)||defined(MSWINCE)||defined(CYGWIN32) +#ifdef MSWIN32 +GC_INNER GC_bool GC_no_win32_dlls=FALSE; +GC_INNER GC_bool GC_wnt=FALSE; +GC_INNER void GC_init_win32(void) +{ +#if defined(_WIN64)||(defined(_MSC_VER)&&_MSC_VER>=1800) +GC_wnt=TRUE; +#else +DWORD v=GetVersion(); +GC_wnt=!(v&0x80000000); +GC_no_win32_dlls|=((!GC_wnt)&&(v&0xff)<=3); +#endif +#ifdef USE_MUNMAP +if (GC_no_win32_dlls){ +GC_unmap_threshold=0; +} +#endif +} +STATIC ptr_t GC_least_described_address(ptr_t start) +{ +MEMORY_BASIC_INFORMATION buf; +LPVOID limit=GC_sysinfo.lpMinimumApplicationAddress; +ptr_t p=(ptr_t)((word)start&~(GC_page_size - 1)); +GC_ASSERT(GC_page_size!=0); +for (;;){ +size_t result; +LPVOID q=(LPVOID)(p - GC_page_size); +if ((word)q > (word)p||(word)q < (word)limit)break; +result=VirtualQuery(q,&buf,sizeof(buf)); +if (result!=sizeof(buf)||buf.AllocationBase==0)break; +p=(ptr_t)(buf.AllocationBase); +} +return p; +} +#endif +#if defined(USE_WINALLOC)&&!defined(REDIRECT_MALLOC) +STATIC size_t GC_max_root_size=100000; +STATIC struct GC_malloc_heap_list { +void*allocation_base; +struct GC_malloc_heap_list*next; +}*GC_malloc_heap_l=0; +STATIC GC_bool GC_is_malloc_heap_base(void*p) +{ +struct GC_malloc_heap_list*q=GC_malloc_heap_l; +while (0!=q){ +if (q->allocation_base==p)return TRUE; +q=q->next; +} +return FALSE; +} +STATIC void*GC_get_allocation_base(void*p) +{ +MEMORY_BASIC_INFORMATION buf; +size_t result=VirtualQuery(p,&buf,sizeof(buf)); +if (result!=sizeof(buf)){ +ABORT("Weird VirtualQuery result"); +} +return buf.AllocationBase; +} +GC_INNER void GC_add_current_malloc_heap(void) +{ +struct GC_malloc_heap_list*new_l=(struct GC_malloc_heap_list*) +malloc(sizeof(struct GC_malloc_heap_list)); +void*candidate; +if (NULL==new_l)return; +candidate=GC_get_allocation_base(new_l); +if (GC_is_malloc_heap_base(candidate)){ +size_t req_size=10000; +do { +void*p=malloc(req_size); +if (0==p){ +free(new_l); +return; +} +candidate=GC_get_allocation_base(p); +free(p); +req_size*=2; +} while (GC_is_malloc_heap_base(candidate) +&&req_size < GC_max_root_size/10&&req_size < 500000); +if (GC_is_malloc_heap_base(candidate)){ +free(new_l); +return; +} +} +GC_COND_LOG_PRINTF("Found new system malloc AllocationBase at %p\n", +candidate); +new_l->allocation_base=candidate; +new_l->next=GC_malloc_heap_l; +GC_malloc_heap_l=new_l; +} +STATIC void GC_free_malloc_heap_list(void) +{ +struct GC_malloc_heap_list*q=GC_malloc_heap_l; +GC_malloc_heap_l=NULL; +while (q!=NULL){ +struct GC_malloc_heap_list*next=q->next; +free(q); +q=next; +} +} +#endif +GC_INNER GC_bool GC_is_heap_base(void*p) +{ +int i; +#if defined(USE_WINALLOC)&&!defined(REDIRECT_MALLOC) +if (GC_root_size > GC_max_root_size) +GC_max_root_size=GC_root_size; +if (GC_is_malloc_heap_base(p)) +return TRUE; +#endif +for (i=0;i < (int)GC_n_heap_bases;i++){ +if (GC_heap_bases[i]==p)return TRUE; +} +return FALSE; +} +#ifdef MSWIN32 +STATIC void GC_register_root_section(ptr_t static_root) +{ +MEMORY_BASIC_INFORMATION buf; +LPVOID p; +char*base; +char*limit; +if (!GC_no_win32_dlls)return; +p=base=limit=GC_least_described_address(static_root); +while ((word)p < (word)GC_sysinfo.lpMaximumApplicationAddress){ +size_t result=VirtualQuery(p,&buf,sizeof(buf)); +char*new_limit; +DWORD protect; +if (result!=sizeof(buf)||buf.AllocationBase==0 +||GC_is_heap_base(buf.AllocationBase))break; +new_limit=(char*)p+buf.RegionSize; +protect=buf.Protect; +if (buf.State==MEM_COMMIT +&&is_writable(protect)){ +if ((char*)p==limit){ +limit=new_limit; +} else { +if (base!=limit)GC_add_roots_inner(base,limit,FALSE); +base=(char*)p; +limit=new_limit; +} +} +if ((word)p > (word)new_limit)break; +p=(LPVOID)new_limit; +} +if (base!=limit)GC_add_roots_inner(base,limit,FALSE); +} +#endif +void GC_register_data_segments(void) +{ +#ifdef MSWIN32 +GC_register_root_section((ptr_t)&GC_pages_executable); +#endif +} +#else +#if (defined(SVR4)||defined(AIX)||defined(DGUX)||(defined(LINUX)&&defined(SPARC)))&&!defined(PCR) +ptr_t GC_SysVGetDataStart(size_t max_page_size,ptr_t etext_addr) +{ +word text_end=((word)(etext_addr)+sizeof(word)- 1) +&~(word)(sizeof(word)- 1); +word next_page=((text_end+(word)max_page_size - 1) +&~((word)max_page_size - 1)); +word page_offset=(text_end&((word)max_page_size - 1)); +volatile ptr_t result=(char*)(next_page+page_offset); +GC_setup_temporary_fault_handler(); +if (SETJMP(GC_jmp_buf)==0){ +#ifdef AO_HAVE_fetch_and_add +volatile AO_t zero=0; +(void)AO_fetch_and_add((volatile AO_t*)result,zero); +#else +char v=*result; +#if defined(CPPCHECK) +GC_noop1((word)&v); +#endif +*result=v; +#endif +GC_reset_fault_handler(); +} else { +GC_reset_fault_handler(); +result=(char*)GC_find_limit(DATAEND,FALSE); +} +return ( ptr_t)result; +} +#endif +#ifdef DATASTART_USES_BSDGETDATASTART +GC_INNER ptr_t GC_FreeBSDGetDataStart(size_t max_page_size, +ptr_t etext_addr) +{ +word text_end=((word)(etext_addr)+sizeof(word)- 1) +&~(word)(sizeof(word)- 1); +volatile word next_page=(text_end+(word)max_page_size - 1) +&~((word)max_page_size - 1); +volatile ptr_t result=(ptr_t)text_end; +GC_setup_temporary_fault_handler(); +if (SETJMP(GC_jmp_buf)==0){ +for (;next_page < (word)DATAEND;next_page+=(word)max_page_size) +*(volatile char*)next_page; +GC_reset_fault_handler(); +} else { +GC_reset_fault_handler(); +result=(ptr_t)GC_find_limit(DATAEND,FALSE); +} +return(result); +} +#endif +#ifdef AMIGA +#define GC_AMIGA_DS +#undef GC_AMIGA_DS +#elif defined(OPENBSD) +void GC_register_data_segments(void) +{ +ptr_t region_start=DATASTART; +if ((word)region_start - 1U>=(word)DATAEND) +ABORT_ARG2("Wrong DATASTART/END pair", +":%p .. %p",(void*)region_start,(void*)DATAEND); +for (;;){ +#ifdef GC_OPENBSD_UTHREADS +ptr_t region_end=GC_find_limit_openbsd(region_start,DATAEND); +#else +ptr_t region_end=GC_find_limit_with_bound(region_start,TRUE,DATAEND); +#endif +GC_add_roots_inner(region_start,region_end,FALSE); +if ((word)region_end>=(word)DATAEND) +break; +region_start=GC_skip_hole_openbsd(region_end,DATAEND); +} +} +#else +#if!defined(PCR)&&!defined(MACOS)&&defined(REDIRECT_MALLOC)&&defined(GC_SOLARIS_THREADS) +EXTERN_C_BEGIN +extern caddr_t sbrk(int); +EXTERN_C_END +#endif +void GC_register_data_segments(void) +{ +#if!defined(PCR)&&!defined(MACOS) +#if defined(REDIRECT_MALLOC)&&defined(GC_SOLARIS_THREADS) +GC_ASSERT(DATASTART); +{ +ptr_t p=(ptr_t)sbrk(0); +if ((word)DATASTART < (word)p) +GC_add_roots_inner(DATASTART,p,FALSE); +} +#else +if ((word)DATASTART - 1U>=(word)DATAEND){ +ABORT_ARG2("Wrong DATASTART/END pair", +":%p .. %p",(void*)DATASTART,(void*)DATAEND); +} +GC_add_roots_inner(DATASTART,DATAEND,FALSE); +#ifdef GC_HAVE_DATAREGION2 +if ((word)DATASTART2 - 1U>=(word)DATAEND2) +ABORT_ARG2("Wrong DATASTART/END2 pair", +":%p .. %p",(void*)DATASTART2,(void*)DATAEND2); +GC_add_roots_inner(DATASTART2,DATAEND2,FALSE); +#endif +#endif +#endif +#if defined(MACOS) +{ +#if defined(THINK_C) +extern void*GC_MacGetDataStart(void); +GC_add_roots_inner((ptr_t)GC_MacGetDataStart(), +(ptr_t)LMGetCurrentA5(),FALSE); +#else +#if defined(__MWERKS__) +#if!__POWERPC__ +extern void*GC_MacGetDataStart(void); +#if __option(far_data) +extern void*GC_MacGetDataEnd(void); +#endif +GC_add_roots_inner((ptr_t)GC_MacGetDataStart(), +(ptr_t)LMGetCurrentA5(),FALSE); +#if __option(far_data) +GC_add_roots_inner((ptr_t)LMGetCurrentA5(), +(ptr_t)GC_MacGetDataEnd(),FALSE); +#endif +#else +extern char __data_start__[],__data_end__[]; +GC_add_roots_inner((ptr_t)&__data_start__, +(ptr_t)&__data_end__,FALSE); +#endif +#endif +#endif +} +#endif +} +#endif +#endif +#endif +#if!defined(OS2)&&!defined(PCR)&&!defined(AMIGA)&&!defined(USE_WINALLOC)&&!defined(MACOS)&&!defined(DOS4GW)&&!defined(NINTENDO_SWITCH)&&!defined(NONSTOP)&&!defined(SN_TARGET_ORBIS)&&!defined(SN_TARGET_PS3)&&!defined(SN_TARGET_PSP2)&&!defined(RTEMS)&&!defined(__CC_ARM) +#define SBRK_ARG_T ptrdiff_t +#if defined(MMAP_SUPPORTED) +#ifdef USE_MMAP_FIXED +#define GC_MMAP_FLAGS MAP_FIXED|MAP_PRIVATE +#else +#define GC_MMAP_FLAGS MAP_PRIVATE +#endif +#ifdef USE_MMAP_ANON +#define zero_fd -1 +#if defined(MAP_ANONYMOUS)&&!defined(CPPCHECK) +#define OPT_MAP_ANON MAP_ANONYMOUS +#else +#define OPT_MAP_ANON MAP_ANON +#endif +#else +static int zero_fd=-1; +#define OPT_MAP_ANON 0 +#endif +#ifndef MSWIN_XBOX1 +#if defined(SYMBIAN)&&!defined(USE_MMAP_ANON) +EXTERN_C_BEGIN +extern char*GC_get_private_path_and_zero_file(void); +EXTERN_C_END +#endif +STATIC ptr_t GC_unix_mmap_get_mem(size_t bytes) +{ +void*result; +static ptr_t last_addr=HEAP_START; +#ifndef USE_MMAP_ANON +static GC_bool initialized=FALSE; +if (!EXPECT(initialized,TRUE)){ +#ifdef SYMBIAN +char*path=GC_get_private_path_and_zero_file(); +if (path!=NULL){ +zero_fd=open(path,O_RDWR|O_CREAT,0644); +free(path); +} +#else +zero_fd=open("/dev/zero",O_RDONLY); +#endif +if (zero_fd==-1) +ABORT("Could not open/dev/zero"); +if (fcntl(zero_fd,F_SETFD,FD_CLOEXEC)==-1) +WARN("Could not set FD_CLOEXEC for/dev/zero\n",0); +initialized=TRUE; +} +#endif +GC_ASSERT(GC_page_size!=0); +if (bytes&(GC_page_size - 1))ABORT("Bad GET_MEM arg"); +result=mmap(last_addr,bytes,(PROT_READ|PROT_WRITE) +|(GC_pages_executable?PROT_EXEC:0), +GC_MMAP_FLAGS|OPT_MAP_ANON,zero_fd,0); +#undef IGNORE_PAGES_EXECUTABLE +if (EXPECT(MAP_FAILED==result,FALSE)){ +if (HEAP_START==last_addr&&GC_pages_executable&&EACCES==errno) +ABORT("Cannot allocate executable pages"); +return NULL; +} +last_addr=(ptr_t)(((word)result+bytes+GC_page_size - 1) +&~(GC_page_size - 1)); +#if!defined(LINUX) +if (last_addr==0){ +munmap(result,~GC_page_size - (size_t)result+1); +return GC_unix_mmap_get_mem(bytes); +} +#else +GC_ASSERT(last_addr!=0); +#endif +if (((word)result % HBLKSIZE)!=0) +ABORT( +"GC_unix_get_mem:Memory returned by mmap is not aligned to HBLKSIZE."); +return((ptr_t)result); +} +#endif +#endif +#if defined(USE_MMAP) +ptr_t GC_unix_get_mem(size_t bytes) +{ +return GC_unix_mmap_get_mem(bytes); +} +#else +STATIC ptr_t GC_unix_sbrk_get_mem(size_t bytes) +{ +ptr_t result; +#ifdef IRIX5 +__LOCK_MALLOC(); +#endif +{ +ptr_t cur_brk=(ptr_t)sbrk(0); +SBRK_ARG_T lsbs=(word)cur_brk&(GC_page_size-1); +GC_ASSERT(GC_page_size!=0); +if ((SBRK_ARG_T)bytes < 0){ +result=0; +goto out; +} +if (lsbs!=0){ +if((ptr_t)sbrk((SBRK_ARG_T)GC_page_size - lsbs)==(ptr_t)(-1)){ +result=0; +goto out; +} +} +#ifdef ADD_HEAP_GUARD_PAGES +{ +ptr_t guard=(ptr_t)sbrk((SBRK_ARG_T)GC_page_size); +if (mprotect(guard,GC_page_size,PROT_NONE)!=0) +ABORT("ADD_HEAP_GUARD_PAGES:mprotect failed"); +} +#endif +result=(ptr_t)sbrk((SBRK_ARG_T)bytes); +if (result==(ptr_t)(-1))result=0; +} +out: +#ifdef IRIX5 +__UNLOCK_MALLOC(); +#endif +return(result); +} +ptr_t GC_unix_get_mem(size_t bytes) +{ +#if defined(MMAP_SUPPORTED) +static GC_bool sbrk_failed=FALSE; +ptr_t result=0; +if (GC_pages_executable){ +return GC_unix_mmap_get_mem(bytes); +} +if (!sbrk_failed)result=GC_unix_sbrk_get_mem(bytes); +if (0==result){ +sbrk_failed=TRUE; +result=GC_unix_mmap_get_mem(bytes); +} +if (0==result){ +result=GC_unix_sbrk_get_mem(bytes); +} +return result; +#else +return GC_unix_sbrk_get_mem(bytes); +#endif +} +#endif +#endif +#ifdef OS2 +void*os2_alloc(size_t bytes) +{ +void*result; +if (DosAllocMem(&result,bytes,(PAG_READ|PAG_WRITE|PAG_COMMIT) +|(GC_pages_executable?PAG_EXECUTE:0)) +!=NO_ERROR){ +return(0); +} +if (result==0)return(os2_alloc(bytes)); +return(result); +} +#endif +#ifdef MSWIN_XBOX1 +ptr_t GC_durango_get_mem(size_t bytes) +{ +if (0==bytes)return NULL; +return (ptr_t)VirtualAlloc(NULL,bytes,MEM_COMMIT|MEM_TOP_DOWN, +PAGE_READWRITE); +} +#elif defined(MSWINCE) +ptr_t GC_wince_get_mem(size_t bytes) +{ +ptr_t result=0; +word i; +GC_ASSERT(GC_page_size!=0); +bytes=ROUNDUP_PAGESIZE(bytes); +for (i=0;i < GC_n_heap_bases;i++){ +if (((word)(-(signed_word)GC_heap_lengths[i]) +&(GC_sysinfo.dwAllocationGranularity-1)) +>=bytes){ +result=GC_heap_bases[i]+GC_heap_lengths[i]; +break; +} +} +if (i==GC_n_heap_bases){ +size_t res_bytes= +SIZET_SAT_ADD(bytes,(size_t)GC_sysinfo.dwAllocationGranularity-1) +&~((size_t)GC_sysinfo.dwAllocationGranularity-1); +result=(ptr_t)VirtualAlloc(NULL,res_bytes, +MEM_RESERVE|MEM_TOP_DOWN, +GC_pages_executable?PAGE_EXECUTE_READWRITE: +PAGE_READWRITE); +if (HBLKDISPL(result)!=0)ABORT("Bad VirtualAlloc result"); +if (GC_n_heap_bases>=MAX_HEAP_SECTS)ABORT("Too many heap sections"); +if (result==NULL)return NULL; +GC_heap_bases[GC_n_heap_bases]=result; +GC_heap_lengths[GC_n_heap_bases]=0; +GC_n_heap_bases++; +} +result=(ptr_t)VirtualAlloc(result,bytes,MEM_COMMIT, +GC_pages_executable?PAGE_EXECUTE_READWRITE: +PAGE_READWRITE); +#undef IGNORE_PAGES_EXECUTABLE +if (result!=NULL){ +if (HBLKDISPL(result)!=0)ABORT("Bad VirtualAlloc result"); +GC_heap_lengths[i]+=bytes; +} +return(result); +} +#elif (defined(USE_WINALLOC)&&!defined(MSWIN_XBOX1))||defined(CYGWIN32) +#ifdef USE_GLOBAL_ALLOC +#define GLOBAL_ALLOC_TEST 1 +#else +#define GLOBAL_ALLOC_TEST GC_no_win32_dlls +#endif +#if (defined(GC_USE_MEM_TOP_DOWN)&&defined(USE_WINALLOC))||defined(CPPCHECK) +DWORD GC_mem_top_down=MEM_TOP_DOWN; +#else +#define GC_mem_top_down 0 +#endif +ptr_t GC_win32_get_mem(size_t bytes) +{ +ptr_t result; +#ifndef USE_WINALLOC +result=GC_unix_get_mem(bytes); +#else +#if defined(MSWIN32)&&!defined(MSWINRT_FLAVOR) +if (GLOBAL_ALLOC_TEST){ +result=(ptr_t)GlobalAlloc(0,SIZET_SAT_ADD(bytes,HBLKSIZE)); +result=(ptr_t)(((word)result+HBLKSIZE - 1) +&~(word)(HBLKSIZE - 1)); +} else +#endif +{ +#ifdef MPROTECT_VDB +#ifdef GWW_VDB +#define VIRTUAL_ALLOC_PAD (GC_GWW_AVAILABLE()?0:1) +#else +#define VIRTUAL_ALLOC_PAD 1 +#endif +#else +#define VIRTUAL_ALLOC_PAD 0 +#endif +result=(ptr_t)VirtualAlloc(NULL, +SIZET_SAT_ADD(bytes,VIRTUAL_ALLOC_PAD), +GetWriteWatch_alloc_flag +|(MEM_COMMIT|MEM_RESERVE) +|GC_mem_top_down, +GC_pages_executable?PAGE_EXECUTE_READWRITE: +PAGE_READWRITE); +#undef IGNORE_PAGES_EXECUTABLE +} +#endif +if (HBLKDISPL(result)!=0)ABORT("Bad VirtualAlloc result"); +if (GC_n_heap_bases>=MAX_HEAP_SECTS)ABORT("Too many heap sections"); +if (0!=result)GC_heap_bases[GC_n_heap_bases++]=result; +return(result); +} +#endif +#if defined(MSWIN32)||defined(MSWINCE)||defined(CYGWIN32)||defined(MSWIN_XBOX1) +GC_API void GC_CALL GC_win32_free_heap(void) +{ +#if defined(USE_WINALLOC)&&!defined(REDIRECT_MALLOC)&&!defined(MSWIN_XBOX1) +GC_free_malloc_heap_list(); +#endif +#if (defined(USE_WINALLOC)&&!defined(MSWIN_XBOX1)&&!defined(MSWINCE))||defined(CYGWIN32) +#ifndef MSWINRT_FLAVOR +#ifndef CYGWIN32 +if (GLOBAL_ALLOC_TEST) +#endif +{ +while (GC_n_heap_bases--> 0){ +#ifdef CYGWIN32 +#else +GlobalFree(GC_heap_bases[GC_n_heap_bases]); +#endif +GC_heap_bases[GC_n_heap_bases]=0; +} +return; +} +#endif +#ifndef CYGWIN32 +while (GC_n_heap_bases > 0){ +VirtualFree(GC_heap_bases[--GC_n_heap_bases],0,MEM_RELEASE); +GC_heap_bases[GC_n_heap_bases]=0; +} +#endif +#endif +} +#endif +#ifdef AMIGA +#define GC_AMIGA_AM +#undef GC_AMIGA_AM +#endif +#if defined(HAIKU) +#include +ptr_t GC_haiku_get_mem(size_t bytes) +{ +void*mem; +GC_ASSERT(GC_page_size!=0); +if (posix_memalign(&mem,GC_page_size,bytes)==0) +return mem; +return NULL; +} +#endif +#ifdef USE_MUNMAP +#if!defined(NN_PLATFORM_CTR)&&!defined(MSWIN32)&&!defined(MSWINCE)&&!defined(MSWIN_XBOX1) +#include +#ifdef SN_TARGET_PS3 +#include +#else +#include +#endif +#include +#include +#endif +STATIC ptr_t GC_unmap_start(ptr_t start,size_t bytes) +{ +ptr_t result=(ptr_t)(((word)start+GC_page_size - 1) +&~(GC_page_size - 1)); +GC_ASSERT(GC_page_size!=0); +if ((word)(result+GC_page_size)> (word)(start+bytes))return 0; +return result; +} +GC_INNER void GC_unmap(ptr_t start,size_t bytes) +{ +ptr_t start_addr=GC_unmap_start(start,bytes); +ptr_t end_addr=GC_unmap_end(start,bytes); +word len=end_addr - start_addr; +if (0==start_addr)return; +#ifdef USE_WINALLOC +while (len!=0){ +MEMORY_BASIC_INFORMATION mem_info; +word free_len; +if (VirtualQuery(start_addr,&mem_info,sizeof(mem_info)) +!=sizeof(mem_info)) +ABORT("Weird VirtualQuery result"); +free_len=(len < mem_info.RegionSize)?len:mem_info.RegionSize; +if (!VirtualFree(start_addr,free_len,MEM_DECOMMIT)) +ABORT("VirtualFree failed"); +GC_unmapped_bytes+=free_len; +start_addr+=free_len; +len-=free_len; +} +#elif defined(SN_TARGET_PS3) +ps3_free_mem(start_addr,len); +#else +{ +#if defined(AIX)||defined(CYGWIN32)||defined(HAIKU)||defined(HPUX) +if (mprotect(start_addr,len,PROT_NONE)) +ABORT("mprotect(PROT_NONE)failed"); +#elif defined(__EMSCRIPTEN__) +#else +void*result=mmap(start_addr,len,PROT_NONE, +MAP_PRIVATE|MAP_FIXED|OPT_MAP_ANON, +zero_fd,0); +if (result!=(void*)start_addr) +ABORT("mmap(PROT_NONE)failed"); +#if defined(CPPCHECK)||defined(LINT2) +GC_noop1((word)result); +#endif +#endif +} +GC_unmapped_bytes+=len; +#endif +} +GC_INNER void GC_remap(ptr_t start,size_t bytes) +{ +ptr_t start_addr=GC_unmap_start(start,bytes); +ptr_t end_addr=GC_unmap_end(start,bytes); +word len=end_addr - start_addr; +if (0==start_addr)return; +#ifdef USE_WINALLOC +while (len!=0){ +MEMORY_BASIC_INFORMATION mem_info; +word alloc_len; +ptr_t result; +if (VirtualQuery(start_addr,&mem_info,sizeof(mem_info)) +!=sizeof(mem_info)) +ABORT("Weird VirtualQuery result"); +alloc_len=(len < mem_info.RegionSize)?len:mem_info.RegionSize; +result=(ptr_t)VirtualAlloc(start_addr,alloc_len,MEM_COMMIT, +GC_pages_executable +?PAGE_EXECUTE_READWRITE +:PAGE_READWRITE); +if (result!=start_addr){ +if (GetLastError()==ERROR_NOT_ENOUGH_MEMORY|| +GetLastError()==ERROR_OUTOFMEMORY){ +ABORT("Not enough memory to process remapping"); +} else { +ABORT("VirtualAlloc remapping failed"); +} +} +#ifdef LINT2 +GC_noop1((word)result); +#endif +GC_unmapped_bytes-=alloc_len; +start_addr+=alloc_len; +len-=alloc_len; +} +#else +{ +#if defined(NACL)||defined(NETBSD) +void*result=mmap(start_addr,len,(PROT_READ|PROT_WRITE) +|(GC_pages_executable?PROT_EXEC:0), +MAP_PRIVATE|MAP_FIXED|OPT_MAP_ANON, +zero_fd,0); +if (result!=(void*)start_addr) +ABORT("mmap as mprotect failed"); +#if defined(CPPCHECK)||defined(LINT2) +GC_noop1((word)result); +#endif +#else +if (mprotect(start_addr,len,(PROT_READ|PROT_WRITE) +|(GC_pages_executable?PROT_EXEC:0))!=0){ +ABORT_ARG3("mprotect remapping failed", +" at %p (length %lu),errcode=%d", +(void*)start_addr,(unsigned long)len,errno); +} +#endif +} +#undef IGNORE_PAGES_EXECUTABLE +GC_unmapped_bytes-=len; +#endif +} +GC_INNER void GC_unmap_gap(ptr_t start1,size_t bytes1,ptr_t start2, +size_t bytes2) +{ +ptr_t start1_addr=GC_unmap_start(start1,bytes1); +ptr_t end1_addr=GC_unmap_end(start1,bytes1); +ptr_t start2_addr=GC_unmap_start(start2,bytes2); +ptr_t start_addr=end1_addr; +ptr_t end_addr=start2_addr; +size_t len; +GC_ASSERT(start1+bytes1==start2); +if (0==start1_addr)start_addr=GC_unmap_start(start1,bytes1+bytes2); +if (0==start2_addr)end_addr=GC_unmap_end(start1,bytes1+bytes2); +if (0==start_addr)return; +len=end_addr - start_addr; +#ifdef USE_WINALLOC +while (len!=0){ +MEMORY_BASIC_INFORMATION mem_info; +word free_len; +if (VirtualQuery(start_addr,&mem_info,sizeof(mem_info)) +!=sizeof(mem_info)) +ABORT("Weird VirtualQuery result"); +free_len=(len < mem_info.RegionSize)?len:mem_info.RegionSize; +if (!VirtualFree(start_addr,free_len,MEM_DECOMMIT)) +ABORT("VirtualFree failed"); +GC_unmapped_bytes+=free_len; +start_addr+=free_len; +len-=free_len; +} +#else +if (len!=0){ +#if defined(AIX)||defined(CYGWIN32)||defined(HAIKU)||defined(HPUX) +if (mprotect(start_addr,len,PROT_NONE)) +ABORT("mprotect(PROT_NONE)failed"); +#else +void*result=mmap(start_addr,len,PROT_NONE, +MAP_PRIVATE|MAP_FIXED|OPT_MAP_ANON, +zero_fd,0); +if (result!=(void*)start_addr) +ABORT("mmap(PROT_NONE)failed"); +#if defined(CPPCHECK)||defined(LINT2) +GC_noop1((word)result); +#endif +#endif +GC_unmapped_bytes+=len; +} +#endif +} +#endif +#ifndef THREADS +#if defined(__EMSCRIPTEN__) +static void scan_regs_cb(void*begin,void*end) +{ +GC_push_all_stack((ptr_t)begin,(ptr_t)end); +} +STATIC void GC_CALLBACK GC_default_push_other_roots(void) +{ +emscripten_scan_registers(scan_regs_cb); +} +#else +#define GC_default_push_other_roots 0 +#endif +#else +#ifdef PCR +PCR_ERes GC_push_thread_stack(PCR_Th_T*t,PCR_Any dummy) +{ +struct PCR_ThCtl_TInfoRep info; +PCR_ERes result; +info.ti_stkLow=info.ti_stkHi=0; +result=PCR_ThCtl_GetInfo(t,&info); +GC_push_all_stack((ptr_t)(info.ti_stkLow),(ptr_t)(info.ti_stkHi)); +return(result); +} +PCR_ERes GC_push_old_obj(void*p,size_t size,PCR_Any data) +{ +GC_push_all_stack((ptr_t)p,(ptr_t)p+size); +return(PCR_ERes_okay); +} +extern struct PCR_MM_ProcsRep*GC_old_allocator; +STATIC void GC_CALLBACK GC_default_push_other_roots(void) +{ +if ((*(GC_old_allocator->mmp_enumerate))(PCR_Bool_false, +GC_push_old_obj,0) +!=PCR_ERes_okay){ +ABORT("Old object enumeration failed"); +} +if (PCR_ERes_IsErr( +PCR_ThCtl_ApplyToAllOtherThreads(GC_push_thread_stack,0)) +||PCR_ERes_IsErr(GC_push_thread_stack(PCR_Th_CurrThread(),0))){ +ABORT("Thread stack marking failed"); +} +} +#elif defined(SN_TARGET_PS3) +STATIC void GC_CALLBACK GC_default_push_other_roots(void) +{ +ABORT("GC_default_push_other_roots is not implemented"); +} +void GC_push_thread_structures(void) +{ +ABORT("GC_push_thread_structures is not implemented"); +} +#else +STATIC void GC_CALLBACK GC_default_push_other_roots(void) +{ +GC_push_all_stacks(); +} +#endif +#endif +GC_push_other_roots_proc GC_push_other_roots=GC_default_push_other_roots; +GC_API void GC_CALL GC_set_push_other_roots(GC_push_other_roots_proc fn) +{ +GC_push_other_roots=fn; +} +GC_API GC_push_other_roots_proc GC_CALL GC_get_push_other_roots(void) +{ +return GC_push_other_roots; +} +#if (defined(CHECKSUMS)&&defined(GWW_VDB))||defined(PROC_VDB) +STATIC void GC_or_pages(page_hash_table pht1,page_hash_table pht2) +{ +unsigned i; +for (i=0;i < PHT_SIZE;i++)pht1[i]|=pht2[i]; +} +#endif +#ifdef GWW_VDB +#define GC_GWW_BUF_LEN (MAXHINCR*HBLKSIZE/4096) +static PVOID gww_buf[GC_GWW_BUF_LEN]; +#ifndef MPROTECT_VDB +#define GC_gww_dirty_init GC_dirty_init +#endif +GC_INNER GC_bool GC_gww_dirty_init(void) +{ +detect_GetWriteWatch(); +return GC_GWW_AVAILABLE(); +} +GC_INLINE void GC_gww_read_dirty(GC_bool output_unneeded) +{ +word i; +if (!output_unneeded) +BZERO(GC_grungy_pages,sizeof(GC_grungy_pages)); +for (i=0;i!=GC_n_heap_sects;++i){ +GC_ULONG_PTR count; +do { +PVOID*pages=gww_buf; +DWORD page_size; +count=GC_GWW_BUF_LEN; +if ((*(GetWriteWatch_type)(word)GetWriteWatch_func)( +WRITE_WATCH_FLAG_RESET, +GC_heap_sects[i].hs_start, +GC_heap_sects[i].hs_bytes, +pages,&count,&page_size)!=0){ +static int warn_count=0; +struct hblk*start=(struct hblk*)GC_heap_sects[i].hs_start; +static struct hblk*last_warned=0; +size_t nblocks=divHBLKSZ(GC_heap_sects[i].hs_bytes); +if (i!=0&&last_warned!=start&&warn_count++< 5){ +last_warned=start; +WARN("GC_gww_read_dirty unexpectedly failed at %p:" +"Falling back to marking all pages dirty\n",start); +} +if (!output_unneeded){ +unsigned j; +for (j=0;j < nblocks;++j){ +word hash=PHT_HASH(start+j); +set_pht_entry_from_index(GC_grungy_pages,hash); +} +} +count=1; +} else if (!output_unneeded){ +PVOID*pages_end=pages+count; +while (pages!=pages_end){ +struct hblk*h=(struct hblk*)*pages++; +struct hblk*h_end=(struct hblk*)((char*)h+page_size); +do { +set_pht_entry_from_index(GC_grungy_pages,PHT_HASH(h)); +} while ((word)(++h)< (word)h_end); +} +} +} while (count==GC_GWW_BUF_LEN); +} +#ifdef CHECKSUMS +GC_ASSERT(!output_unneeded); +GC_or_pages(GC_written_pages,GC_grungy_pages); +#endif +} +#else +#define GC_GWW_AVAILABLE()FALSE +#endif +#ifdef DEFAULT_VDB +GC_INNER GC_bool GC_dirty_init(void) +{ +GC_VERBOSE_LOG_PRINTF("Initializing DEFAULT_VDB...\n"); +return TRUE; +} +#endif +#ifndef GC_DISABLE_INCREMENTAL +#if!defined(THREADS)||defined(HAVE_LOCKFREE_AO_OR) +#define async_set_pht_entry_from_index(db,index)set_pht_entry_from_index_concurrent(db,index) +#elif defined(AO_HAVE_test_and_set_acquire) +GC_INNER volatile AO_TS_t GC_fault_handler_lock=AO_TS_INITIALIZER; +static void async_set_pht_entry_from_index(volatile page_hash_table db, +size_t index) +{ +GC_acquire_dirty_lock(); +set_pht_entry_from_index(db,index); +GC_release_dirty_lock(); +} +#else +#error No test_and_set operation:Introduces a race. +#endif +#endif +#ifdef MPROTECT_VDB +#ifdef DARWIN +#include +STATIC mach_port_t GC_task_self=0; +#define PROTECT(addr,len)if (vm_protect(GC_task_self,(vm_address_t)(addr),(vm_size_t)(len),FALSE,VM_PROT_READ|(GC_pages_executable?VM_PROT_EXECUTE:0))==KERN_SUCCESS){} else ABORT("vm_protect(PROTECT)failed") +#define UNPROTECT(addr,len)if (vm_protect(GC_task_self,(vm_address_t)(addr),(vm_size_t)(len),FALSE,(VM_PROT_READ|VM_PROT_WRITE)|(GC_pages_executable?VM_PROT_EXECUTE:0))==KERN_SUCCESS){} else ABORT("vm_protect(UNPROTECT)failed") +#elif!defined(USE_WINALLOC) +#include +#include +#if!defined(CYGWIN32)&&!defined(HAIKU) +#include +#endif +#define PROTECT(addr,len)if (mprotect((caddr_t)(addr),(size_t)(len),PROT_READ|(GC_pages_executable?PROT_EXEC:0))>=0){ } else ABORT("mprotect failed") +#define UNPROTECT(addr,len)if (mprotect((caddr_t)(addr),(size_t)(len),(PROT_READ|PROT_WRITE)|(GC_pages_executable?PROT_EXEC:0))>=0){ } else ABORT(GC_pages_executable?"un-mprotect executable page failed" " (probably disabled by OS)":"un-mprotect failed") +#undef IGNORE_PAGES_EXECUTABLE +#else +#ifndef MSWINCE +#include +#endif +static DWORD protect_junk; +#define PROTECT(addr,len)if (VirtualProtect((addr),(len),GC_pages_executable?PAGE_EXECUTE_READ:PAGE_READONLY,&protect_junk)){ } else ABORT_ARG1("VirtualProtect failed",":errcode=0x%X",(unsigned)GetLastError()) +#define UNPROTECT(addr,len)if (VirtualProtect((addr),(len),GC_pages_executable?PAGE_EXECUTE_READWRITE:PAGE_READWRITE,&protect_junk)){ } else ABORT("un-VirtualProtect failed") +#endif +#if defined(MSWIN32) +typedef LPTOP_LEVEL_EXCEPTION_FILTER SIG_HNDLR_PTR; +#undef SIG_DFL +#define SIG_DFL (LPTOP_LEVEL_EXCEPTION_FILTER)((signed_word)-1) +#elif defined(MSWINCE) +typedef LONG (WINAPI*SIG_HNDLR_PTR)(struct _EXCEPTION_POINTERS*); +#undef SIG_DFL +#define SIG_DFL (SIG_HNDLR_PTR)(-1) +#elif defined(DARWIN) +typedef void (*SIG_HNDLR_PTR)(); +#else +typedef void (*SIG_HNDLR_PTR)(int,siginfo_t*,void*); +typedef void (*PLAIN_HNDLR_PTR)(int); +#endif +#if defined(__GLIBC__) +#if __GLIBC__ < 2||__GLIBC__==2&&__GLIBC_MINOR__ < 2 +#error glibc too old? +#endif +#endif +#ifndef DARWIN +STATIC SIG_HNDLR_PTR GC_old_segv_handler=0; +#if defined(FREEBSD)||defined(HPUX)||defined(HURD)||defined(LINUX) +STATIC SIG_HNDLR_PTR GC_old_bus_handler=0; +#ifndef LINUX +STATIC GC_bool GC_old_bus_handler_used_si=FALSE; +#endif +#endif +#if!defined(MSWIN32)&&!defined(MSWINCE) +STATIC GC_bool GC_old_segv_handler_used_si=FALSE; +#endif +#endif +#ifdef THREADS +GC_ATTR_NO_SANITIZE_THREAD +static GC_bool is_header_found_async(void*addr) +{ +#ifdef HASH_TL +hdr*result; +GET_HDR((ptr_t)addr,result); +return result!=NULL; +#else +return HDR_INNER(addr)!=NULL; +#endif +} +#else +#define is_header_found_async(addr)(HDR(addr)!=NULL) +#endif +#ifndef DARWIN +#if!defined(MSWIN32)&&!defined(MSWINCE) +#include +#if defined(FREEBSD)||defined(HURD)||defined(HPUX) +#define SIG_OK (sig==SIGBUS||sig==SIGSEGV) +#else +#define SIG_OK (sig==SIGSEGV) +#endif +#if defined(FREEBSD) +#ifndef SEGV_ACCERR +#define SEGV_ACCERR 2 +#endif +#if defined(AARCH64)||defined(ARM32)||defined(MIPS) +#define CODE_OK (si->si_code==SEGV_ACCERR) +#elif defined(POWERPC) +#define AIM +#include +#define CODE_OK (si->si_code==EXC_DSI||si->si_code==SEGV_ACCERR) +#else +#define CODE_OK (si->si_code==BUS_PAGE_FAULT||si->si_code==SEGV_ACCERR) +#endif +#elif defined(OSF1) +#define CODE_OK (si->si_code==2) +#elif defined(IRIX5) +#define CODE_OK (si->si_code==EACCES) +#elif defined(CYGWIN32)||defined(HAIKU)||defined(HURD) +#define CODE_OK TRUE +#elif defined(LINUX) +#define CODE_OK TRUE +#elif defined(HPUX) +#define CODE_OK (si->si_code==SEGV_ACCERR||si->si_code==BUS_ADRERR||si->si_code==BUS_UNKNOWN||si->si_code==SEGV_UNKNOWN||si->si_code==BUS_OBJERR) +#elif defined(SUNOS5SIGS) +#define CODE_OK (si->si_code==SEGV_ACCERR) +#endif +#ifndef NO_GETCONTEXT +#include +#endif +STATIC void GC_write_fault_handler(int sig,siginfo_t*si,void*raw_sc) +#else +#define SIG_OK (exc_info->ExceptionRecord->ExceptionCode==STATUS_ACCESS_VIOLATION) +#define CODE_OK (exc_info->ExceptionRecord->ExceptionInformation[0]==1) +STATIC LONG WINAPI GC_write_fault_handler( +struct _EXCEPTION_POINTERS*exc_info) +#endif +{ +#if!defined(MSWIN32)&&!defined(MSWINCE) +char*addr=(char*)si->si_addr; +#else +char*addr=(char*)(exc_info->ExceptionRecord +->ExceptionInformation[1]); +#endif +if (SIG_OK&&CODE_OK){ +struct hblk*h=(struct hblk*)((word)addr&~(GC_page_size-1)); +GC_bool in_allocd_block; +size_t i; +GC_ASSERT(GC_page_size!=0); +#ifdef CHECKSUMS +GC_record_fault(h); +#endif +#ifdef SUNOS5SIGS +in_allocd_block=FALSE; +for (i=0;i < divHBLKSZ(GC_page_size);i++){ +if (is_header_found_async(&h[i])){ +in_allocd_block=TRUE; +break; +} +} +#else +in_allocd_block=is_header_found_async(addr); +#endif +if (!in_allocd_block){ +SIG_HNDLR_PTR old_handler; +#if defined(MSWIN32)||defined(MSWINCE) +old_handler=GC_old_segv_handler; +#else +GC_bool used_si; +#if defined(FREEBSD)||defined(HURD)||defined(HPUX) +if (sig==SIGBUS){ +old_handler=GC_old_bus_handler; +used_si=GC_old_bus_handler_used_si; +} else +#endif +{ +old_handler=GC_old_segv_handler; +used_si=GC_old_segv_handler_used_si; +} +#endif +if (old_handler==(SIG_HNDLR_PTR)(signed_word)SIG_DFL){ +#if!defined(MSWIN32)&&!defined(MSWINCE) +ABORT_ARG1("Unexpected bus error or segmentation fault", +" at %p",(void*)addr); +#else +return(EXCEPTION_CONTINUE_SEARCH); +#endif +} else { +#if defined(MSWIN32)||defined(MSWINCE) +return((*old_handler)(exc_info)); +#else +if (used_si) +((SIG_HNDLR_PTR)old_handler)(sig,si,raw_sc); +else +((PLAIN_HNDLR_PTR)(signed_word)old_handler)(sig); +return; +#endif +} +} +UNPROTECT(h,GC_page_size); +for (i=0;i < divHBLKSZ(GC_page_size);i++){ +word index=PHT_HASH(h+i); +async_set_pht_entry_from_index(GC_dirty_pages,index); +} +#if defined(MSWIN32)||defined(MSWINCE) +return(EXCEPTION_CONTINUE_EXECUTION); +#else +return; +#endif +} +#if defined(MSWIN32)||defined(MSWINCE) +return EXCEPTION_CONTINUE_SEARCH; +#else +ABORT_ARG1("Unexpected bus error or segmentation fault", +" at %p",(void*)addr); +#endif +} +#if defined(GC_WIN32_THREADS)&&!defined(CYGWIN32) +GC_INNER void GC_set_write_fault_handler(void) +{ +SetUnhandledExceptionFilter(GC_write_fault_handler); +} +#endif +#endif +#if!defined(DARWIN) +GC_INNER GC_bool GC_dirty_init(void) +{ +#if!defined(MSWIN32)&&!defined(MSWINCE) +struct sigaction act,oldact; +act.sa_flags=SA_RESTART|SA_SIGINFO; +act.sa_sigaction=GC_write_fault_handler; +(void)sigemptyset(&act.sa_mask); +#if defined(THREADS)&&!defined(GC_OPENBSD_UTHREADS)&&!defined(GC_WIN32_THREADS)&&!defined(NACL) +(void)sigaddset(&act.sa_mask,GC_get_suspend_signal()); +#endif +#endif +GC_VERBOSE_LOG_PRINTF( +"Initializing mprotect virtual dirty bit implementation\n"); +if (GC_page_size % HBLKSIZE!=0){ +ABORT("Page size not multiple of HBLKSIZE"); +} +#if!defined(MSWIN32)&&!defined(MSWINCE) +#if defined(GC_IRIX_THREADS) +sigaction(SIGSEGV,0,&oldact); +sigaction(SIGSEGV,&act,0); +#else +{ +int res=sigaction(SIGSEGV,&act,&oldact); +if (res!=0)ABORT("Sigaction failed"); +} +#endif +if (oldact.sa_flags&SA_SIGINFO){ +GC_old_segv_handler=oldact.sa_sigaction; +GC_old_segv_handler_used_si=TRUE; +} else { +GC_old_segv_handler=(SIG_HNDLR_PTR)(signed_word)oldact.sa_handler; +GC_old_segv_handler_used_si=FALSE; +} +if (GC_old_segv_handler==(SIG_HNDLR_PTR)(signed_word)SIG_IGN){ +WARN("Previously ignored segmentation violation!?\n",0); +GC_old_segv_handler=(SIG_HNDLR_PTR)(signed_word)SIG_DFL; +} +if (GC_old_segv_handler!=(SIG_HNDLR_PTR)(signed_word)SIG_DFL){ +GC_VERBOSE_LOG_PRINTF("Replaced other SIGSEGV handler\n"); +} +#if defined(HPUX)||defined(LINUX)||defined(HURD)||(defined(FREEBSD)&&(defined(__GLIBC__)||defined(SUNOS5SIGS))) +sigaction(SIGBUS,&act,&oldact); +if ((oldact.sa_flags&SA_SIGINFO)!=0){ +GC_old_bus_handler=oldact.sa_sigaction; +#if!defined(LINUX) +GC_old_bus_handler_used_si=TRUE; +#endif +} else { +GC_old_bus_handler=(SIG_HNDLR_PTR)(signed_word)oldact.sa_handler; +} +if (GC_old_bus_handler==(SIG_HNDLR_PTR)(signed_word)SIG_IGN){ +WARN("Previously ignored bus error!?\n",0); +#if!defined(LINUX) +GC_old_bus_handler=(SIG_HNDLR_PTR)(signed_word)SIG_DFL; +#else +#endif +} else if (GC_old_bus_handler!=(SIG_HNDLR_PTR)(signed_word)SIG_DFL){ +GC_VERBOSE_LOG_PRINTF("Replaced other SIGBUS handler\n"); +} +#endif +#endif +#if defined(GWW_VDB) +if (GC_gww_dirty_init()) +return TRUE; +#endif +#if defined(MSWIN32) +GC_old_segv_handler=SetUnhandledExceptionFilter(GC_write_fault_handler); +if (GC_old_segv_handler!=NULL){ +GC_COND_LOG_PRINTF("Replaced other UnhandledExceptionFilter\n"); +} else { +GC_old_segv_handler=SIG_DFL; +} +#elif defined(MSWINCE) +#endif +#if defined(CPPCHECK)&&defined(ADDRESS_SANITIZER) +GC_noop1((word)&__asan_default_options); +#endif +return TRUE; +} +#endif +GC_API int GC_CALL GC_incremental_protection_needs(void) +{ +GC_ASSERT(GC_is_initialized); +if (GC_page_size==HBLKSIZE){ +return GC_PROTECTS_POINTER_HEAP; +} else { +return GC_PROTECTS_POINTER_HEAP|GC_PROTECTS_PTRFREE_HEAP; +} +} +#define HAVE_INCREMENTAL_PROTECTION_NEEDS +#define IS_PTRFREE(hhdr)((hhdr)->hb_descr==0) +#define PAGE_ALIGNED(x)!((word)(x)&(GC_page_size - 1)) +STATIC void GC_protect_heap(void) +{ +unsigned i; +GC_bool protect_all= +(0!=(GC_incremental_protection_needs()&GC_PROTECTS_PTRFREE_HEAP)); +GC_ASSERT(GC_page_size!=0); +for (i=0;i < GC_n_heap_sects;i++){ +ptr_t start=GC_heap_sects[i].hs_start; +size_t len=GC_heap_sects[i].hs_bytes; +if (protect_all){ +PROTECT(start,len); +} else { +struct hblk*current; +struct hblk*current_start; +struct hblk*limit; +GC_ASSERT(PAGE_ALIGNED(len)); +GC_ASSERT(PAGE_ALIGNED(start)); +current_start=current=(struct hblk*)start; +limit=(struct hblk*)(start+len); +while ((word)current < (word)limit){ +hdr*hhdr; +word nhblks; +GC_bool is_ptrfree; +GC_ASSERT(PAGE_ALIGNED(current)); +GET_HDR(current,hhdr); +if (IS_FORWARDING_ADDR_OR_NIL(hhdr)){ +GC_ASSERT(current_start==current); +current_start=++current; +continue; +} +if (HBLK_IS_FREE(hhdr)){ +GC_ASSERT(PAGE_ALIGNED(hhdr->hb_sz)); +nhblks=divHBLKSZ(hhdr->hb_sz); +is_ptrfree=TRUE; +} else { +nhblks=OBJ_SZ_TO_BLOCKS(hhdr->hb_sz); +is_ptrfree=IS_PTRFREE(hhdr); +} +if (is_ptrfree){ +if ((word)current_start < (word)current){ +PROTECT(current_start,(ptr_t)current - (ptr_t)current_start); +} +current_start=(current+=nhblks); +} else { +current+=nhblks; +} +} +if ((word)current_start < (word)current){ +PROTECT(current_start,(ptr_t)current - (ptr_t)current_start); +} +} +} +} +#endif +#ifdef PROC_VDB +#include +#include +#include +#include +#include +#ifdef GC_NO_SYS_FAULT_H +#define PG_MODIFIED 1 +struct prpageheader { +int dummy[2]; +unsigned long pr_nmap; +unsigned long pr_npage; +}; +struct prasmap { +char*pr_vaddr; +size_t pr_npage; +char dummy1[64+8]; +unsigned pr_mflags; +unsigned pr_pagesize; +int dummy2[2]; +}; +#else +#include +#include +#endif +#define INITIAL_BUF_SZ 16384 +STATIC size_t GC_proc_buf_size=INITIAL_BUF_SZ; +STATIC char*GC_proc_buf=NULL; +STATIC int GC_proc_fd=0; +GC_INNER GC_bool GC_dirty_init(void) +{ +char buf[40]; +if (GC_bytes_allocd!=0||GC_bytes_allocd_before_gc!=0){ +memset(GC_written_pages,0xff,sizeof(page_hash_table)); +GC_VERBOSE_LOG_PRINTF( +"Allocated %lu bytes:all pages may have been written\n", +(unsigned long)(GC_bytes_allocd+GC_bytes_allocd_before_gc)); +} +(void)snprintf(buf,sizeof(buf),"/proc/%ld/pagedata",(long)getpid()); +buf[sizeof(buf)- 1]='\0'; +GC_proc_fd=open(buf,O_RDONLY); +if (GC_proc_fd < 0){ +WARN("/proc open failed;cannot enable GC incremental mode\n",0); +return FALSE; +} +if (syscall(SYS_fcntl,GC_proc_fd,F_SETFD,FD_CLOEXEC)==-1) +WARN("Could not set FD_CLOEXEC for/proc\n",0); +GC_proc_buf=GC_scratch_alloc(GC_proc_buf_size); +if (GC_proc_buf==NULL) +ABORT("Insufficient space for/proc read"); +return TRUE; +} +GC_INLINE void GC_proc_read_dirty(GC_bool output_unneeded) +{ +#define READ read +int nmaps; +char*bufp=GC_proc_buf; +int i; +BZERO(GC_grungy_pages,sizeof(GC_grungy_pages)); +if (READ(GC_proc_fd,bufp,GC_proc_buf_size)<=0){ +size_t new_size=2*GC_proc_buf_size; +char*new_buf; +WARN("/proc read failed:GC_proc_buf_size=%" WARN_PRIdPTR "\n", +(signed_word)GC_proc_buf_size); +new_buf=GC_scratch_alloc(new_size); +if (new_buf!=0){ +GC_scratch_recycle_no_gww(bufp,GC_proc_buf_size); +GC_proc_buf=bufp=new_buf; +GC_proc_buf_size=new_size; +} +if (READ(GC_proc_fd,bufp,GC_proc_buf_size)<=0){ +WARN("Insufficient space for/proc read\n",0); +if (!output_unneeded) +memset(GC_grungy_pages,0xff,sizeof (page_hash_table)); +memset(GC_written_pages,0xff,sizeof(page_hash_table)); +return; +} +} +nmaps=((struct prpageheader*)bufp)->pr_nmap; +#ifdef DEBUG_DIRTY_BITS +GC_log_printf("Proc VDB read:pr_nmap=%u,pr_npage=%lu\n", +nmaps,((struct prpageheader*)bufp)->pr_npage); +#endif +#if defined(GC_NO_SYS_FAULT_H)&&defined(CPPCHECK) +GC_noop1(((struct prpageheader*)bufp)->dummy[0]); +#endif +bufp+=sizeof(struct prpageheader); +for (i=0;i < nmaps;i++){ +struct prasmap*map=(struct prasmap*)bufp; +ptr_t vaddr=(ptr_t)(map->pr_vaddr); +unsigned long npages=map->pr_npage; +unsigned pagesize=map->pr_pagesize; +ptr_t limit; +#if defined(GC_NO_SYS_FAULT_H)&&defined(CPPCHECK) +GC_noop1(map->dummy1[0]+map->dummy2[0]); +#endif +#ifdef DEBUG_DIRTY_BITS +GC_log_printf( +"pr_vaddr=%p,npage=%lu,mflags=0x%x,pagesize=0x%x\n", +(void*)vaddr,npages,map->pr_mflags,pagesize); +#endif +bufp+=sizeof(struct prasmap); +limit=vaddr+pagesize*npages; +for (;(word)vaddr < (word)limit;vaddr+=pagesize){ +if ((*bufp++)&PG_MODIFIED){ +struct hblk*h; +ptr_t next_vaddr=vaddr+pagesize; +#ifdef DEBUG_DIRTY_BITS +GC_log_printf("dirty page at:%p\n",(void*)vaddr); +#endif +for (h=(struct hblk*)vaddr; +(word)h < (word)next_vaddr;h++){ +word index=PHT_HASH(h); +set_pht_entry_from_index(GC_grungy_pages,index); +} +} +} +bufp=(char*)(((word)bufp+(sizeof(long)-1)) +&~(word)(sizeof(long)-1)); +} +#ifdef DEBUG_DIRTY_BITS +GC_log_printf("Proc VDB read done\n"); +#endif +GC_or_pages(GC_written_pages,GC_grungy_pages); +#undef READ +} +#endif +#ifdef PCR_VDB +#include "vd/PCR_VD.h" +#define NPAGES (32*1024) +PCR_VD_DB GC_grungy_bits[NPAGES]; +STATIC ptr_t GC_vd_base=NULL; +GC_INNER GC_bool GC_dirty_init(void) +{ +GC_vd_base=GC_heap_sects[0].hs_start; +if (GC_vd_base==0){ +ABORT("Bad initial heap segment"); +} +if (PCR_VD_Start(HBLKSIZE,GC_vd_base,NPAGES*HBLKSIZE) +!=PCR_ERes_okay){ +ABORT("Dirty bit initialization failed"); +} +return TRUE; +} +#endif +#ifndef GC_DISABLE_INCREMENTAL +GC_INNER GC_bool GC_manual_vdb=FALSE; +GC_INNER void GC_dirty_inner(const void*p) +{ +word index=PHT_HASH(p); +#if defined(MPROTECT_VDB) +GC_ASSERT(GC_manual_vdb); +#endif +async_set_pht_entry_from_index(GC_dirty_pages,index); +} +GC_INNER void GC_read_dirty(GC_bool output_unneeded) +{ +if (GC_manual_vdb +#if defined(MPROTECT_VDB) +||!GC_GWW_AVAILABLE() +#endif +){ +if (!output_unneeded) +BCOPY(( void*)GC_dirty_pages,GC_grungy_pages, +sizeof(GC_dirty_pages)); +BZERO(( void*)GC_dirty_pages, +sizeof(GC_dirty_pages)); +#ifdef MPROTECT_VDB +if (!GC_manual_vdb) +GC_protect_heap(); +#endif +return; +} +#ifdef GWW_VDB +GC_gww_read_dirty(output_unneeded); +#elif defined(PROC_VDB) +GC_proc_read_dirty(output_unneeded); +#elif defined(PCR_VDB) +{ +static int onhs=0; +int nhs=GC_n_heap_sects; +for (;onhs < nhs;onhs++){ +PCR_VD_WriteProtectEnable( +GC_heap_sects[onhs].hs_start, +GC_heap_sects[onhs].hs_bytes); +} +} +if (PCR_VD_Clear(GC_vd_base,NPAGES*HBLKSIZE,GC_grungy_bits) +!=PCR_ERes_okay){ +ABORT("Dirty bit read failed"); +} +#endif +} +GC_INNER GC_bool GC_page_was_dirty(struct hblk*h) +{ +#ifdef PCR_VDB +if (!GC_manual_vdb){ +if ((word)h < (word)GC_vd_base +||(word)h>=(word)(GC_vd_base+NPAGES*HBLKSIZE)){ +return TRUE; +} +return GC_grungy_bits[h-(struct hblk*)GC_vd_base]&PCR_VD_DB_dirtyBit; +} +#elif defined(DEFAULT_VDB) +if (!GC_manual_vdb) +return TRUE; +#endif +return NULL==HDR(h) +||get_pht_entry_from_index(GC_grungy_pages,PHT_HASH(h)); +} +#if defined(CHECKSUMS)||defined(PROC_VDB) +GC_INNER GC_bool GC_page_was_ever_dirty(struct hblk*h) +{ +#if defined(GWW_VDB)||defined(PROC_VDB) +#ifdef MPROTECT_VDB +if (!GC_GWW_AVAILABLE()) +return TRUE; +#endif +return NULL==HDR(h) +||get_pht_entry_from_index(GC_written_pages,PHT_HASH(h)); +#else +(void)h; +return TRUE; +#endif +} +#endif +GC_INNER void GC_remove_protection(struct hblk*h,word nblocks, +GC_bool is_ptrfree) +{ +#ifdef PCR_VDB +(void)is_ptrfree; +if (!GC_auto_incremental) +return; +PCR_VD_WriteProtectDisable(h,nblocks*HBLKSIZE); +PCR_VD_WriteProtectEnable(h,nblocks*HBLKSIZE); +#elif defined(MPROTECT_VDB) +struct hblk*h_trunc; +struct hblk*h_end; +struct hblk*current; +if (!GC_auto_incremental||GC_GWW_AVAILABLE()) +return; +GC_ASSERT(GC_page_size!=0); +h_trunc=(struct hblk*)((word)h&~(GC_page_size-1)); +h_end=(struct hblk*)(((word)(h+nblocks)+GC_page_size - 1) +&~(GC_page_size - 1)); +if (h_end==h_trunc+1&& +get_pht_entry_from_index(GC_dirty_pages,PHT_HASH(h_trunc))){ +return; +} +for (current=h_trunc;(word)current < (word)h_end;++current){ +word index=PHT_HASH(current); +if (!is_ptrfree||(word)current < (word)h +||(word)current>=(word)(h+nblocks)){ +async_set_pht_entry_from_index(GC_dirty_pages,index); +} +} +UNPROTECT(h_trunc,(ptr_t)h_end - (ptr_t)h_trunc); +#else +(void)h;(void)nblocks;(void)is_ptrfree; +#endif +} +#endif +#if defined(MPROTECT_VDB)&&defined(DARWIN) +#include +#include +#include +#include +#include +EXTERN_C_BEGIN +extern boolean_t +exc_server(mach_msg_header_t*,mach_msg_header_t*); +extern kern_return_t +exception_raise(mach_port_t,mach_port_t,mach_port_t,exception_type_t, +exception_data_t,mach_msg_type_number_t); +extern kern_return_t +exception_raise_state(mach_port_t,mach_port_t,mach_port_t,exception_type_t, +exception_data_t,mach_msg_type_number_t, +thread_state_flavor_t*,thread_state_t, +mach_msg_type_number_t,thread_state_t, +mach_msg_type_number_t*); +extern kern_return_t +exception_raise_state_identity(mach_port_t,mach_port_t,mach_port_t, +exception_type_t,exception_data_t, +mach_msg_type_number_t,thread_state_flavor_t*, +thread_state_t,mach_msg_type_number_t, +thread_state_t,mach_msg_type_number_t*); +GC_API_OSCALL kern_return_t +catch_exception_raise(mach_port_t exception_port,mach_port_t thread, +mach_port_t task,exception_type_t exception, +exception_data_t code, +mach_msg_type_number_t code_count); +GC_API_OSCALL kern_return_t +catch_exception_raise_state(mach_port_name_t exception_port, +int exception,exception_data_t code, +mach_msg_type_number_t codeCnt,int flavor, +thread_state_t old_state,int old_stateCnt, +thread_state_t new_state,int new_stateCnt); +GC_API_OSCALL kern_return_t +catch_exception_raise_state_identity(mach_port_name_t exception_port, +mach_port_t thread,mach_port_t task,int exception, +exception_data_t code,mach_msg_type_number_t codeCnt, +int flavor,thread_state_t old_state,int old_stateCnt, +thread_state_t new_state,int new_stateCnt); +EXTERN_C_END +GC_API_OSCALL kern_return_t +catch_exception_raise_state(mach_port_name_t exception_port GC_ATTR_UNUSED, +int exception GC_ATTR_UNUSED,exception_data_t code GC_ATTR_UNUSED, +mach_msg_type_number_t codeCnt GC_ATTR_UNUSED,int flavor GC_ATTR_UNUSED, +thread_state_t old_state GC_ATTR_UNUSED,int old_stateCnt GC_ATTR_UNUSED, +thread_state_t new_state GC_ATTR_UNUSED,int new_stateCnt GC_ATTR_UNUSED) +{ +ABORT_RET("Unexpected catch_exception_raise_state invocation"); +return(KERN_INVALID_ARGUMENT); +} +GC_API_OSCALL kern_return_t +catch_exception_raise_state_identity( +mach_port_name_t exception_port GC_ATTR_UNUSED, +mach_port_t thread GC_ATTR_UNUSED,mach_port_t task GC_ATTR_UNUSED, +int exception GC_ATTR_UNUSED,exception_data_t code GC_ATTR_UNUSED, +mach_msg_type_number_t codeCnt GC_ATTR_UNUSED,int flavor GC_ATTR_UNUSED, +thread_state_t old_state GC_ATTR_UNUSED,int old_stateCnt GC_ATTR_UNUSED, +thread_state_t new_state GC_ATTR_UNUSED,int new_stateCnt GC_ATTR_UNUSED) +{ +ABORT_RET("Unexpected catch_exception_raise_state_identity invocation"); +return(KERN_INVALID_ARGUMENT); +} +#define MAX_EXCEPTION_PORTS 16 +static struct { +mach_msg_type_number_t count; +exception_mask_t masks[MAX_EXCEPTION_PORTS]; +exception_handler_t ports[MAX_EXCEPTION_PORTS]; +exception_behavior_t behaviors[MAX_EXCEPTION_PORTS]; +thread_state_flavor_t flavors[MAX_EXCEPTION_PORTS]; +} GC_old_exc_ports; +STATIC struct ports_s { +void (*volatile os_callback[3])(void); +mach_port_t exception; +#if defined(THREADS) +mach_port_t reply; +#endif +} GC_ports={ +{ +(void (*)(void))catch_exception_raise, +(void (*)(void))catch_exception_raise_state, +(void (*)(void))catch_exception_raise_state_identity +}, +#ifdef THREADS +0, +#endif +0 +}; +typedef struct { +mach_msg_header_t head; +} GC_msg_t; +typedef enum { +GC_MP_NORMAL, +GC_MP_DISCARDING, +GC_MP_STOPPED +} GC_mprotect_state_t; +#ifdef THREADS +#define ID_STOP 1 +#define ID_RESUME 2 +#define ID_ACK 3 +STATIC GC_mprotect_state_t GC_mprotect_state=GC_MP_NORMAL; +STATIC void GC_mprotect_thread_notify(mach_msg_id_t id) +{ +struct buf_s { +GC_msg_t msg; +mach_msg_trailer_t trailer; +} buf; +mach_msg_return_t r; +buf.msg.head.msgh_bits=MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND,0); +buf.msg.head.msgh_size=sizeof(buf.msg); +buf.msg.head.msgh_remote_port=GC_ports.exception; +buf.msg.head.msgh_local_port=MACH_PORT_NULL; +buf.msg.head.msgh_id=id; +r=mach_msg(&buf.msg.head,MACH_SEND_MSG|MACH_RCV_MSG|MACH_RCV_LARGE, +sizeof(buf.msg),sizeof(buf),GC_ports.reply, +MACH_MSG_TIMEOUT_NONE,MACH_PORT_NULL); +if (r!=MACH_MSG_SUCCESS) +ABORT("mach_msg failed in GC_mprotect_thread_notify"); +if (buf.msg.head.msgh_id!=ID_ACK) +ABORT("Invalid ack in GC_mprotect_thread_notify"); +} +STATIC void GC_mprotect_thread_reply(void) +{ +GC_msg_t msg; +mach_msg_return_t r; +msg.head.msgh_bits=MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND,0); +msg.head.msgh_size=sizeof(msg); +msg.head.msgh_remote_port=GC_ports.reply; +msg.head.msgh_local_port=MACH_PORT_NULL; +msg.head.msgh_id=ID_ACK; +r=mach_msg(&msg.head,MACH_SEND_MSG,sizeof(msg),0,MACH_PORT_NULL, +MACH_MSG_TIMEOUT_NONE,MACH_PORT_NULL); +if (r!=MACH_MSG_SUCCESS) +ABORT("mach_msg failed in GC_mprotect_thread_reply"); +} +GC_INNER void GC_mprotect_stop(void) +{ +GC_mprotect_thread_notify(ID_STOP); +} +GC_INNER void GC_mprotect_resume(void) +{ +GC_mprotect_thread_notify(ID_RESUME); +} +#else +#define GC_mprotect_state GC_MP_NORMAL +#endif +struct mp_reply_s { +mach_msg_header_t head; +char data[256]; +}; +struct mp_msg_s { +mach_msg_header_t head; +mach_msg_body_t msgh_body; +char data[1024]; +}; +STATIC void*GC_mprotect_thread(void*arg) +{ +mach_msg_return_t r; +struct mp_reply_s reply; +struct mp_msg_s msg; +mach_msg_id_t id; +if ((word)arg==GC_WORD_MAX)return 0; +#if defined(CPPCHECK) +reply.data[0]=0; +msg.data[0]=0; +#endif +#if defined(HAVE_PTHREAD_SETNAME_NP_WITHOUT_TID) +(void)pthread_setname_np("GC-mprotect"); +#endif +#if defined(THREADS)&&!defined(GC_NO_THREADS_DISCOVERY) +GC_darwin_register_mach_handler_thread(mach_thread_self()); +#endif +for(;;){ +r=mach_msg(&msg.head,MACH_RCV_MSG|MACH_RCV_LARGE| +(GC_mprotect_state==GC_MP_DISCARDING?MACH_RCV_TIMEOUT:0), +0,sizeof(msg),GC_ports.exception, +GC_mprotect_state==GC_MP_DISCARDING?0 +:MACH_MSG_TIMEOUT_NONE,MACH_PORT_NULL); +id=r==MACH_MSG_SUCCESS?msg.head.msgh_id:-1; +#if defined(THREADS) +if(GC_mprotect_state==GC_MP_DISCARDING){ +if(r==MACH_RCV_TIMED_OUT){ +GC_mprotect_state=GC_MP_STOPPED; +GC_mprotect_thread_reply(); +continue; +} +if(r==MACH_MSG_SUCCESS&&(id==ID_STOP||id==ID_RESUME)) +ABORT("Out of order mprotect thread request"); +} +#endif +if (r!=MACH_MSG_SUCCESS){ +ABORT_ARG2("mach_msg failed", +":errcode=%d (%s)",(int)r,mach_error_string(r)); +} +switch(id){ +#if defined(THREADS) +case ID_STOP: +if(GC_mprotect_state!=GC_MP_NORMAL) +ABORT("Called mprotect_stop when state wasn't normal"); +GC_mprotect_state=GC_MP_DISCARDING; +break; +case ID_RESUME: +if(GC_mprotect_state!=GC_MP_STOPPED) +ABORT("Called mprotect_resume when state wasn't stopped"); +GC_mprotect_state=GC_MP_NORMAL; +GC_mprotect_thread_reply(); +break; +#endif +default: +if(!exc_server(&msg.head,&reply.head)) +ABORT("exc_server failed"); +r=mach_msg(&reply.head,MACH_SEND_MSG,reply.head.msgh_size,0, +MACH_PORT_NULL,MACH_MSG_TIMEOUT_NONE, +MACH_PORT_NULL); +if(r!=MACH_MSG_SUCCESS){ +#ifdef BROKEN_EXCEPTION_HANDLING +GC_err_printf("mach_msg failed with %d %s while sending " +"exc reply\n",(int)r,mach_error_string(r)); +#else +ABORT("mach_msg failed while sending exception reply"); +#endif +} +} +} +} +#ifdef BROKEN_EXCEPTION_HANDLING +STATIC int GC_sigbus_count=0; +STATIC void GC_darwin_sigbus(int num,siginfo_t*sip,void*context) +{ +if (num!=SIGBUS) +ABORT("Got a non-sigbus signal in the sigbus handler"); +if (GC_sigbus_count>=8){ +ABORT("Got more than 8 SIGBUSs in a row!"); +} else { +GC_sigbus_count++; +WARN("Ignoring SIGBUS\n",0); +} +} +#endif +GC_INNER GC_bool GC_dirty_init(void) +{ +kern_return_t r; +mach_port_t me; +pthread_t thread; +pthread_attr_t attr; +exception_mask_t mask; +#ifdef CAN_HANDLE_FORK +if (GC_handle_fork){ +WARN("Can't turn on GC incremental mode as fork()" +" handling requested\n",0); +return FALSE; +} +#endif +GC_VERBOSE_LOG_PRINTF("Initializing mach/darwin mprotect" +" virtual dirty bit implementation\n"); +#ifdef BROKEN_EXCEPTION_HANDLING +WARN("Enabling workarounds for various darwin " +"exception handling bugs\n",0); +#endif +if (GC_page_size % HBLKSIZE!=0){ +ABORT("Page size not multiple of HBLKSIZE"); +} +GC_task_self=me=mach_task_self(); +r=mach_port_allocate(me,MACH_PORT_RIGHT_RECEIVE,&GC_ports.exception); +if (r!=KERN_SUCCESS) +ABORT("mach_port_allocate failed (exception port)"); +r=mach_port_insert_right(me,GC_ports.exception,GC_ports.exception, +MACH_MSG_TYPE_MAKE_SEND); +if (r!=KERN_SUCCESS) +ABORT("mach_port_insert_right failed (exception port)"); +#if defined(THREADS) +r=mach_port_allocate(me,MACH_PORT_RIGHT_RECEIVE,&GC_ports.reply); +if(r!=KERN_SUCCESS) +ABORT("mach_port_allocate failed (reply port)"); +#endif +mask=EXC_MASK_BAD_ACCESS; +r=task_get_exception_ports(me,mask,GC_old_exc_ports.masks, +&GC_old_exc_ports.count,GC_old_exc_ports.ports, +GC_old_exc_ports.behaviors, +GC_old_exc_ports.flavors); +if (r!=KERN_SUCCESS) +ABORT("task_get_exception_ports failed"); +r=task_set_exception_ports(me,mask,GC_ports.exception,EXCEPTION_DEFAULT, +GC_MACH_THREAD_STATE); +if (r!=KERN_SUCCESS) +ABORT("task_set_exception_ports failed"); +if (pthread_attr_init(&attr)!=0) +ABORT("pthread_attr_init failed"); +if (pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED)!=0) +ABORT("pthread_attr_setdetachedstate failed"); +#undef pthread_create +if (pthread_create(&thread,&attr,GC_mprotect_thread,NULL)!=0) +ABORT("pthread_create failed"); +(void)pthread_attr_destroy(&attr); +#ifdef BROKEN_EXCEPTION_HANDLING +{ +struct sigaction sa,oldsa; +sa.sa_handler=(SIG_HNDLR_PTR)GC_darwin_sigbus; +sigemptyset(&sa.sa_mask); +sa.sa_flags=SA_RESTART|SA_SIGINFO; +if (sigaction(SIGBUS,&sa,&oldsa)< 0) +ABORT("sigaction failed"); +if (oldsa.sa_handler!=(SIG_HNDLR_PTR)(signed_word)SIG_DFL){ +GC_VERBOSE_LOG_PRINTF("Replaced other SIGBUS handler\n"); +} +} +#endif +#if defined(CPPCHECK) +GC_noop1((word)GC_ports.os_callback[0]); +#endif +return TRUE; +} +STATIC kern_return_t GC_forward_exception(mach_port_t thread,mach_port_t task, +exception_type_t exception, +exception_data_t data, +mach_msg_type_number_t data_count) +{ +unsigned int i; +kern_return_t r; +mach_port_t port; +exception_behavior_t behavior; +thread_state_flavor_t flavor; +thread_state_data_t thread_state; +mach_msg_type_number_t thread_state_count=THREAD_STATE_MAX; +for (i=0;i < GC_old_exc_ports.count;i++) +if (GC_old_exc_ports.masks[i]&(1< 0?code[0]:-1, +code_count > 1?code[1]:-1); +#endif +return FWD(); +} +r=thread_get_state(thread,flavor,(natural_t*)&exc_state, +&exc_state_count); +if(r!=KERN_SUCCESS){ +#ifdef BROKEN_EXCEPTION_HANDLING +GC_err_printf("thread_get_state failed in catch_exception_raise\n"); +return KERN_SUCCESS; +#else +ABORT("thread_get_state failed in catch_exception_raise"); +#endif +} +addr=(char*)exc_state.DARWIN_EXC_STATE_DAR; +if (!is_header_found_async(addr)){ +#ifdef BROKEN_EXCEPTION_HANDLING +static char*last_fault; +static int last_fault_count; +if(addr!=last_fault){ +last_fault=addr; +last_fault_count=0; +} +if(++last_fault_count < 32){ +if(last_fault_count==1) +WARN("Ignoring KERN_PROTECTION_FAILURE at %p\n",addr); +return KERN_SUCCESS; +} +GC_err_printf("Unexpected KERN_PROTECTION_FAILURE at %p;aborting...\n", +(void*)addr); +EXIT(); +#else +return FWD(); +#endif +} +#ifdef BROKEN_EXCEPTION_HANDLING +GC_sigbus_count=0; +#endif +GC_ASSERT(GC_page_size!=0); +if (GC_mprotect_state==GC_MP_NORMAL){ +struct hblk*h=(struct hblk*)((word)addr&~(GC_page_size-1)); +size_t i; +UNPROTECT(h,GC_page_size); +for (i=0;i < divHBLKSZ(GC_page_size);i++){ +word index=PHT_HASH(h+i); +async_set_pht_entry_from_index(GC_dirty_pages,index); +} +} else if (GC_mprotect_state==GC_MP_DISCARDING){ +} else { +GC_err_printf("KERN_PROTECTION_FAILURE while world is stopped\n"); +return FWD(); +} +return KERN_SUCCESS; +} +#undef FWD +#ifndef NO_DESC_CATCH_EXCEPTION_RAISE +__asm__(".desc _catch_exception_raise,0x10"); +__asm__(".desc _catch_exception_raise_state,0x10"); +__asm__(".desc _catch_exception_raise_state_identity,0x10"); +#endif +#endif +#ifndef HAVE_INCREMENTAL_PROTECTION_NEEDS +GC_API int GC_CALL GC_incremental_protection_needs(void) +{ +GC_ASSERT(GC_is_initialized); +return GC_PROTECTS_NONE; +} +#endif +#ifdef ECOS +#undef sbrk +#endif +GC_API void GC_CALL GC_set_pages_executable(int value) +{ +GC_ASSERT(!GC_is_initialized); +GC_pages_executable=(GC_bool)(value!=0); +} +GC_API int GC_CALL GC_get_pages_executable(void) +{ +#ifdef IGNORE_PAGES_EXECUTABLE +return 1; +#else +return (int)GC_pages_executable; +#endif +} +#if defined(I386)&&defined(LINUX)&&defined(SAVE_CALL_CHAIN) +#include +struct frame { +struct frame*fr_savfp; +long fr_savpc; +#if NARGS > 0 +long fr_arg[NARGS]; +#endif +}; +#endif +#if defined(SPARC) +#if defined(LINUX) +#include +#if defined(SAVE_CALL_CHAIN) +struct frame { +long fr_local[8]; +long fr_arg[6]; +struct frame*fr_savfp; +long fr_savpc; +#ifndef __arch64__ +char*fr_stret; +#endif +long fr_argd[6]; +long fr_argx[0]; +}; +#endif +#elif defined (DRSNX) +#include +#elif defined(OPENBSD) +#include +#elif defined(FREEBSD)||defined(NETBSD) +#include +#else +#include +#endif +#if NARGS > 6 +#error We only know how to get the first 6 arguments +#endif +#endif +#ifdef NEED_CALLINFO +#ifdef LINUX +#include +#endif +#endif +#if defined(GC_HAVE_BUILTIN_BACKTRACE) +#ifdef _MSC_VER +#ifndef GC_MSVC_DBG_H +#define GC_MSVC_DBG_H +#include +#ifdef __cplusplus +extern "C" { +#endif +#if!MSVC_DBG_DLL +#define MSVC_DBG_EXPORT +#elif MSVC_DBG_BUILD +#define MSVC_DBG_EXPORT __declspec(dllexport) +#else +#define MSVC_DBG_EXPORT __declspec(dllimport) +#endif +#ifndef MAX_SYM_NAME +#define MAX_SYM_NAME 2000 +#endif +typedef void*HANDLE; +typedef struct _CONTEXT CONTEXT; +MSVC_DBG_EXPORT size_t GetStackFrames(size_t skip,void*frames[],size_t maxFrames); +MSVC_DBG_EXPORT size_t GetStackFramesFromContext(HANDLE hProcess,HANDLE hThread,CONTEXT*context,size_t skip,void*frames[],size_t maxFrames); +MSVC_DBG_EXPORT size_t GetModuleNameFromAddress(void*address,char*moduleName,size_t size); +MSVC_DBG_EXPORT size_t GetModuleNameFromStack(size_t skip,char*moduleName,size_t size); +MSVC_DBG_EXPORT size_t GetSymbolNameFromAddress(void*address,char*symbolName,size_t size,size_t*offsetBytes); +MSVC_DBG_EXPORT size_t GetSymbolNameFromStack(size_t skip,char*symbolName,size_t size,size_t*offsetBytes); +MSVC_DBG_EXPORT size_t GetFileLineFromAddress(void*address,char*fileName,size_t size,size_t*lineNumber,size_t*offsetBytes); +MSVC_DBG_EXPORT size_t GetFileLineFromStack(size_t skip,char*fileName,size_t size,size_t*lineNumber,size_t*offsetBytes); +MSVC_DBG_EXPORT size_t GetDescriptionFromAddress(void*address,const char*format,char*description,size_t size); +MSVC_DBG_EXPORT size_t GetDescriptionFromStack(void*const frames[],size_t count,const char*format,char*description[],size_t size); +MSVC_DBG_EXPORT int backtrace(void*addresses[],int count); +MSVC_DBG_EXPORT char**backtrace_symbols(void*const addresses[],int count); +#ifdef __cplusplus +} +#endif +#endif +#else +#include +#endif +#endif +#ifdef SAVE_CALL_CHAIN +#if NARGS==0&&NFRAMES % 2==0&&defined(GC_HAVE_BUILTIN_BACKTRACE) +#ifdef REDIRECT_MALLOC +#ifdef THREADS +__thread +#endif +GC_bool GC_in_save_callers=FALSE; +#endif +GC_INNER void GC_save_callers(struct callinfo info[NFRAMES]) +{ +void*tmp_info[NFRAMES+1]; +int npcs,i; +#define IGNORE_FRAMES 1 +#ifdef REDIRECT_MALLOC +if (GC_in_save_callers){ +info[0].ci_pc=(word)(&GC_save_callers); +for (i=1;i < NFRAMES;++i)info[i].ci_pc=0; +return; +} +GC_in_save_callers=TRUE; +#endif +GC_ASSERT(I_HOLD_LOCK()); +GC_STATIC_ASSERT(sizeof(struct callinfo)==sizeof(void*)); +npcs=backtrace((void**)tmp_info,NFRAMES+IGNORE_FRAMES); +if (npcs > IGNORE_FRAMES) +BCOPY(&tmp_info[IGNORE_FRAMES],info, +(npcs - IGNORE_FRAMES)*sizeof(void*)); +for (i=npcs - IGNORE_FRAMES;i < NFRAMES;++i)info[i].ci_pc=0; +#ifdef REDIRECT_MALLOC +GC_in_save_callers=FALSE; +#endif +} +#else +#if (defined(OPENBSD)||defined(NETBSD)||defined(FREEBSD))&&defined(SPARC) +#define FR_SAVFP fr_fp +#define FR_SAVPC fr_pc +#else +#define FR_SAVFP fr_savfp +#define FR_SAVPC fr_savpc +#endif +#if defined(SPARC)&&(defined(__arch64__)||defined(__sparcv9)) +#define BIAS 2047 +#else +#define BIAS 0 +#endif +GC_INNER void GC_save_callers(struct callinfo info[NFRAMES]) +{ +struct frame*frame; +struct frame*fp; +int nframes=0; +#ifdef I386 +asm("movl %%ebp,%0":"=r"(frame)); +fp=frame; +#else +frame=(struct frame*)GC_save_regs_in_stack(); +fp=(struct frame*)((long)frame->FR_SAVFP+BIAS); +#endif +for (;!((word)fp HOTTER_THAN (word)frame) +#ifndef THREADS +&&!((word)GC_stackbottom HOTTER_THAN (word)fp) +#elif defined(STACK_GROWS_UP) +&&fp!=NULL +#endif +&&nframes < NFRAMES; +fp=(struct frame*)((long)fp->FR_SAVFP+BIAS),nframes++){ +#if NARGS > 0 +int i; +#endif +info[nframes].ci_pc=fp->FR_SAVPC; +#if NARGS > 0 +for (i=0;i < NARGS;i++){ +info[nframes].ci_arg[i]=~(fp->fr_arg[i]); +} +#endif +} +if (nframes < NFRAMES)info[nframes].ci_pc=0; +} +#endif +#endif +#ifdef NEED_CALLINFO +GC_INNER void GC_print_callers(struct callinfo info[NFRAMES]) +{ +int i; +static int reentry_count=0; +DCL_LOCK_STATE; +LOCK(); +++reentry_count; +UNLOCK(); +#if NFRAMES==1 +GC_err_printf("\tCaller at allocation:\n"); +#else +GC_err_printf("\tCall chain at allocation:\n"); +#endif +for (i=0;i < NFRAMES;i++){ +#if defined(LINUX)&&!defined(SMALL_CONFIG) +GC_bool stop=FALSE; +#endif +if (0==info[i].ci_pc) +break; +#if NARGS > 0 +{ +int j; +GC_err_printf("\t\targs:"); +for (j=0;j < NARGS;j++){ +if (j!=0)GC_err_printf(","); +GC_err_printf("%d (0x%X)",~(info[i].ci_arg[j]), +~(info[i].ci_arg[j])); +} +GC_err_printf("\n"); +} +#endif +if (reentry_count > 1){ +GC_err_printf("\t\t##PC##=0x%lx\n", +(unsigned long)info[i].ci_pc); +continue; +} +{ +char buf[40]; +char*name; +#if defined(GC_HAVE_BUILTIN_BACKTRACE)&&!defined(GC_BACKTRACE_SYMBOLS_BROKEN) +char**sym_name= +backtrace_symbols((void**)(&(info[i].ci_pc)),1); +if (sym_name!=NULL){ +name=sym_name[0]; +} else +#endif +{ +(void)snprintf(buf,sizeof(buf),"##PC##=0x%lx", +(unsigned long)info[i].ci_pc); +buf[sizeof(buf)- 1]='\0'; +name=buf; +} +#if defined(LINUX)&&!defined(SMALL_CONFIG) +do { +FILE*pipe; +#define EXE_SZ 100 +static char exe_name[EXE_SZ]; +#define CMD_SZ 200 +char cmd_buf[CMD_SZ]; +#define RESULT_SZ 200 +static char result_buf[RESULT_SZ]; +size_t result_len; +char*old_preload; +#define PRELOAD_SZ 200 +char preload_buf[PRELOAD_SZ]; +static GC_bool found_exe_name=FALSE; +static GC_bool will_fail=FALSE; +if (will_fail) +break; +if (!found_exe_name){ +int ret_code=readlink("/proc/self/exe",exe_name,EXE_SZ); +if (ret_code < 0||ret_code>=EXE_SZ +||exe_name[0]!='/'){ +will_fail=TRUE; +break; +} +exe_name[ret_code]='\0'; +found_exe_name=TRUE; +} +(void)snprintf(cmd_buf,sizeof(cmd_buf), +"/usr/bin/addr2line -f -e %s 0x%lx", +exe_name,(unsigned long)info[i].ci_pc); +cmd_buf[sizeof(cmd_buf)- 1]='\0'; +old_preload=GETENV("LD_PRELOAD"); +if (0!=old_preload){ +size_t old_len=strlen(old_preload); +if (old_len>=PRELOAD_SZ){ +will_fail=TRUE; +break; +} +BCOPY(old_preload,preload_buf,old_len+1); +unsetenv ("LD_PRELOAD"); +} +pipe=popen(cmd_buf,"r"); +if (0!=old_preload +&&0!=setenv ("LD_PRELOAD",preload_buf,0)){ +WARN("Failed to reset LD_PRELOAD\n",0); +} +if (NULL==pipe){ +will_fail=TRUE; +break; +} +result_len=fread(result_buf,1,RESULT_SZ - 1,pipe); +(void)pclose(pipe); +if (0==result_len){ +will_fail=TRUE; +break; +} +if (result_buf[result_len - 1]=='\n')--result_len; +result_buf[result_len]=0; +if (result_buf[0]=='?' +||(result_buf[result_len-2]==':' +&&result_buf[result_len-1]=='0')) +break; +{ +char*nl=strchr(result_buf,'\n'); +if (nl!=NULL +&&(word)nl < (word)(result_buf+result_len)){ +*nl=':'; +} +if (strncmp(result_buf,"main", +nl!=NULL +?(size_t)((word)nl +- COVERT_DATAFLOW(result_buf)) +:result_len)==0){ +stop=TRUE; +} +} +if (result_len < RESULT_SZ - 25){ +(void)snprintf(&result_buf[result_len], +sizeof(result_buf)- result_len, +" [0x%lx]",(unsigned long)info[i].ci_pc); +result_buf[sizeof(result_buf)- 1]='\0'; +} +#if defined(CPPCHECK) +GC_noop1((unsigned char)name[0]); +#endif +name=result_buf; +} while (0); +#endif +GC_err_printf("\t\t%s\n",name); +#if defined(GC_HAVE_BUILTIN_BACKTRACE)&&!defined(GC_BACKTRACE_SYMBOLS_BROKEN) +if (sym_name!=NULL) +free(sym_name); +#endif +} +#if defined(LINUX)&&!defined(SMALL_CONFIG) +if (stop) +break; +#endif +} +LOCK(); +--reentry_count; +UNLOCK(); +} +#endif +#if defined(LINUX)&&defined(__ELF__)&&!defined(SMALL_CONFIG) +void GC_print_address_map(void) +{ +char*maps; +GC_err_printf("----------Begin address map----------\n"); +maps=GC_get_maps(); +GC_err_puts(maps!=NULL?maps:"Failed to get map!\n"); +GC_err_printf("----------End address map----------\n"); +} +#endif +#if defined(THREAD_LOCAL_ALLOC) +#ifndef THREADS +#error "invalid config - THREAD_LOCAL_ALLOC requires GC_THREADS" +#endif +#ifndef GC_THREAD_LOCAL_ALLOC_H +#define GC_THREAD_LOCAL_ALLOC_H +#ifdef THREAD_LOCAL_ALLOC +#if defined(USE_HPUX_TLS) +#error USE_HPUX_TLS macro was replaced by USE_COMPILER_TLS +#endif +#include +EXTERN_C_BEGIN +#if!defined(USE_PTHREAD_SPECIFIC)&&!defined(USE_WIN32_SPECIFIC)&&!defined(USE_WIN32_COMPILER_TLS)&&!defined(USE_COMPILER_TLS)&&!defined(USE_CUSTOM_SPECIFIC) +#if defined(MSWIN32)||defined(MSWINCE)||defined(CYGWIN32) +#if defined(CYGWIN32)&&GC_GNUC_PREREQ(4,0) +#if defined(__clang__) +#define USE_PTHREAD_SPECIFIC +#else +#define USE_COMPILER_TLS +#endif +#elif defined(__GNUC__)||defined(MSWINCE) +#define USE_WIN32_SPECIFIC +#else +#define USE_WIN32_COMPILER_TLS +#endif +#elif (defined(LINUX)&&!defined(ARM32)&&!defined(AVR32)&&GC_GNUC_PREREQ(3,3)&&!(defined(__clang__)&&defined(HOST_ANDROID)))||(defined(FREEBSD)||(defined(NETBSD)&&__NetBSD_Version__>=600000000)&&(GC_GNUC_PREREQ(4,4)||GC_CLANG_PREREQ(3,9)))||(defined(HOST_ANDROID)&&defined(ARM32)&&(GC_GNUC_PREREQ(4,6)||GC_CLANG_PREREQ_FULL(3,8,256229))) +#define USE_COMPILER_TLS +#elif defined(GC_DGUX386_THREADS)||defined(GC_OSF1_THREADS)||defined(GC_AIX_THREADS)||defined(GC_DARWIN_THREADS)||defined(GC_FREEBSD_THREADS)||defined(GC_NETBSD_THREADS)||defined(GC_LINUX_THREADS)||defined(GC_HAIKU_THREADS)||defined(GC_RTEMS_PTHREADS) +#define USE_PTHREAD_SPECIFIC +#elif defined(GC_HPUX_THREADS) +#ifdef __GNUC__ +#define USE_PTHREAD_SPECIFIC +#else +#define USE_COMPILER_TLS +#endif +#else +#define USE_CUSTOM_SPECIFIC +#endif +#endif +#ifndef THREAD_FREELISTS_KINDS +#ifdef ENABLE_DISCLAIM +#define THREAD_FREELISTS_KINDS (NORMAL+2) +#else +#define THREAD_FREELISTS_KINDS (NORMAL+1) +#endif +#endif +typedef struct thread_local_freelists { +void*_freelists[THREAD_FREELISTS_KINDS][TINY_FREELISTS]; +#define ptrfree_freelists _freelists[PTRFREE] +#define normal_freelists _freelists[NORMAL] +#ifdef GC_GCJ_SUPPORT +void*gcj_freelists[TINY_FREELISTS]; +#define ERROR_FL ((void*)GC_WORD_MAX) +#endif +#define DIRECT_GRANULES (HBLKSIZE/GRANULE_BYTES) +}*GC_tlfs; +#if defined(USE_PTHREAD_SPECIFIC) +#define GC_getspecific pthread_getspecific +#define GC_setspecific pthread_setspecific +#define GC_key_create pthread_key_create +#define GC_remove_specific(key)pthread_setspecific(key,NULL) +#define GC_remove_specific_after_fork(key,t)(void)0 +typedef pthread_key_t GC_key_t; +#elif defined(USE_COMPILER_TLS)||defined(USE_WIN32_COMPILER_TLS) +#define GC_getspecific(x)(x) +#define GC_setspecific(key,v)((key)=(v),0) +#define GC_key_create(key,d)0 +#define GC_remove_specific(key) +#define GC_remove_specific_after_fork(key,t)(void)0 +typedef void*GC_key_t; +#elif defined(USE_WIN32_SPECIFIC) +#define GC_getspecific TlsGetValue +#define GC_setspecific(key,v)!TlsSetValue(key,v) +#ifndef TLS_OUT_OF_INDEXES +#define TLS_OUT_OF_INDEXES (DWORD)0xFFFFFFFF +#endif +#define GC_key_create(key,d)((d)!=0||(*(key)=TlsAlloc())==TLS_OUT_OF_INDEXES?-1:0) +#define GC_remove_specific(key) +#define GC_remove_specific_after_fork(key,t)(void)0 +typedef DWORD GC_key_t; +#elif defined(USE_CUSTOM_SPECIFIC) +EXTERN_C_END +#ifndef GC_SPECIFIC_H +#define GC_SPECIFIC_H +#include +EXTERN_C_BEGIN +#define MALLOC_CLEAR(n)GC_INTERNAL_MALLOC(n,NORMAL) +#define TS_CACHE_SIZE 1024 +#define CACHE_HASH(n)((((n)>>8)^(n))&(TS_CACHE_SIZE - 1)) +#define TS_HASH_SIZE 1024 +#define HASH(p)((unsigned)((((word)(p))>>8)^(word)(p))&(TS_HASH_SIZE - 1)) +#ifdef GC_ASSERTIONS +typedef GC_hidden_pointer ts_entry_value_t; +#define TS_HIDE_VALUE(p)GC_HIDE_POINTER(p) +#define TS_REVEAL_PTR(p)GC_REVEAL_POINTER(p) +#else +typedef void*ts_entry_value_t; +#define TS_HIDE_VALUE(p)(p) +#define TS_REVEAL_PTR(p)(p) +#endif +typedef struct thread_specific_entry { +volatile AO_t qtid; +ts_entry_value_t value; +struct thread_specific_entry*next; +pthread_t thread; +} tse; +#define quick_thread_id()(((word)GC_approx_sp())>>12) +#define INVALID_QTID ((word)0) +#define INVALID_THREADID ((pthread_t)0) +union ptse_ao_u { +tse*p; +volatile AO_t ao; +}; +typedef struct thread_specific_data { +tse*volatile cache[TS_CACHE_SIZE]; +union ptse_ao_u hash[TS_HASH_SIZE]; +pthread_mutex_t lock; +} tsd; +typedef tsd*GC_key_t; +#define GC_key_create(key,d)GC_key_create_inner(key) +GC_INNER int GC_key_create_inner(tsd**key_ptr); +GC_INNER int GC_setspecific(tsd*key,void*value); +#define GC_remove_specific(key)GC_remove_specific_after_fork(key,pthread_self()) +GC_INNER void GC_remove_specific_after_fork(tsd*key,pthread_t t); +GC_INNER void*GC_slow_getspecific(tsd*key,word qtid, +tse*volatile*cache_entry); +GC_INLINE void*GC_getspecific(tsd*key) +{ +word qtid=quick_thread_id(); +tse*volatile*entry_ptr=&key->cache[CACHE_HASH(qtid)]; +tse*entry=*entry_ptr; +GC_ASSERT(qtid!=INVALID_QTID); +if (EXPECT(entry->qtid==qtid,TRUE)){ +GC_ASSERT(entry->thread==pthread_self()); +return TS_REVEAL_PTR(entry->value); +} +return GC_slow_getspecific(key,qtid,entry_ptr); +} +EXTERN_C_END +#endif +EXTERN_C_BEGIN +#else +#error implement me +#endif +GC_INNER void GC_init_thread_local(GC_tlfs p); +GC_INNER void GC_destroy_thread_local(GC_tlfs p); +GC_INNER void GC_mark_thread_local_fls_for(GC_tlfs p); +#ifdef GC_ASSERTIONS +GC_bool GC_is_thread_tsd_valid(void*tsd); +void GC_check_tls_for(GC_tlfs p); +#if defined(USE_CUSTOM_SPECIFIC) +void GC_check_tsd_marks(tsd*key); +#endif +#endif +#ifndef GC_ATTR_TLS_FAST +#define GC_ATTR_TLS_FAST +#endif +extern +#if defined(USE_COMPILER_TLS) +__thread GC_ATTR_TLS_FAST +#elif defined(USE_WIN32_COMPILER_TLS) +__declspec(thread)GC_ATTR_TLS_FAST +#endif +GC_key_t GC_thread_key; +EXTERN_C_END +#endif +#endif +#include +#if defined(USE_COMPILER_TLS) +__thread GC_ATTR_TLS_FAST +#elif defined(USE_WIN32_COMPILER_TLS) +__declspec(thread)GC_ATTR_TLS_FAST +#endif +GC_key_t GC_thread_key; +static GC_bool keys_initialized; +static void return_single_freelist(void*fl,void**gfl) +{ +if (*gfl==0){ +*gfl=fl; +} else { +void*q,**qptr; +GC_ASSERT(GC_size(fl)==GC_size(*gfl)); +qptr=&(obj_link(fl)); +while ((word)(q=*qptr)>=HBLKSIZE) +qptr=&(obj_link(q)); +GC_ASSERT(0==q); +*qptr=*gfl; +*gfl=fl; +} +} +static void return_freelists(void**fl,void**gfl) +{ +int i; +for (i=1;i < TINY_FREELISTS;++i){ +if ((word)(fl[i])>=HBLKSIZE){ +return_single_freelist(fl[i],&gfl[i]); +} +fl[i]=(ptr_t)HBLKSIZE; +} +#ifdef GC_GCJ_SUPPORT +if (fl[0]==ERROR_FL)return; +#endif +if ((word)(fl[0])>=HBLKSIZE){ +return_single_freelist(fl[0],&gfl[1]); +} +} +#ifdef USE_PTHREAD_SPECIFIC +static void reset_thread_key(void*v){ +pthread_setspecific(GC_thread_key,v); +} +#else +#define reset_thread_key 0 +#endif +GC_INNER void GC_init_thread_local(GC_tlfs p) +{ +int i,j,res; +GC_ASSERT(I_HOLD_LOCK()); +if (!EXPECT(keys_initialized,TRUE)){ +GC_ASSERT((word)&GC_thread_key % sizeof(word)==0); +res=GC_key_create(&GC_thread_key,reset_thread_key); +if (COVERT_DATAFLOW(res)!=0){ +ABORT("Failed to create key for local allocator"); +} +keys_initialized=TRUE; +} +res=GC_setspecific(GC_thread_key,p); +if (COVERT_DATAFLOW(res)!=0){ +ABORT("Failed to set thread specific allocation pointers"); +} +for (j=0;j < TINY_FREELISTS;++j){ +for (i=0;i < THREAD_FREELISTS_KINDS;++i){ +p->_freelists[i][j]=(void*)(word)1; +} +#ifdef GC_GCJ_SUPPORT +p->gcj_freelists[j]=(void*)(word)1; +#endif +} +#ifdef GC_GCJ_SUPPORT +p->gcj_freelists[0]=ERROR_FL; +#endif +} +GC_INNER void GC_destroy_thread_local(GC_tlfs p) +{ +int k; +GC_STATIC_ASSERT(THREAD_FREELISTS_KINDS<=MAXOBJKINDS); +for (k=0;k < THREAD_FREELISTS_KINDS;++k){ +if (k==(int)GC_n_kinds) +break; +return_freelists(p->_freelists[k],GC_obj_kinds[k].ok_freelist); +} +#ifdef GC_GCJ_SUPPORT +return_freelists(p->gcj_freelists,(void**)GC_gcjobjfreelist); +#endif +} +GC_API GC_ATTR_MALLOC void*GC_CALL GC_malloc_kind(size_t bytes,int kind) +{ +size_t granules; +void*tsd; +void*result; +#if MAXOBJKINDS > THREAD_FREELISTS_KINDS +if (EXPECT(kind>=THREAD_FREELISTS_KINDS,FALSE)){ +return GC_malloc_kind_global(bytes,kind); +} +#endif +#if!defined(USE_PTHREAD_SPECIFIC)&&!defined(USE_WIN32_SPECIFIC) +{ +GC_key_t k=GC_thread_key; +if (EXPECT(0==k,FALSE)){ +return GC_malloc_kind_global(bytes,kind); +} +tsd=GC_getspecific(k); +} +#else +if (!EXPECT(keys_initialized,TRUE)) +return GC_malloc_kind_global(bytes,kind); +tsd=GC_getspecific(GC_thread_key); +#endif +#if!defined(USE_COMPILER_TLS)&&!defined(USE_WIN32_COMPILER_TLS) +if (EXPECT(0==tsd,FALSE)){ +return GC_malloc_kind_global(bytes,kind); +} +#endif +GC_ASSERT(GC_is_initialized); +GC_ASSERT(GC_is_thread_tsd_valid(tsd)); +granules=ROUNDED_UP_GRANULES(bytes); +#if defined(CPPCHECK) +#define MALLOC_KIND_PTRFREE_INIT (void*)1 +#else +#define MALLOC_KIND_PTRFREE_INIT NULL +#endif +GC_FAST_MALLOC_GRANS(result,granules, +((GC_tlfs)tsd)->_freelists[kind],DIRECT_GRANULES, +kind,GC_malloc_kind_global(bytes,kind), +(void)(kind==PTRFREE?MALLOC_KIND_PTRFREE_INIT +:(obj_link(result)=0))); +#ifdef LOG_ALLOCS +GC_log_printf("GC_malloc_kind(%lu,%d)returned %p,recent GC #%lu\n", +(unsigned long)bytes,kind,result, +(unsigned long)GC_gc_no); +#endif +return result; +} +#ifdef GC_GCJ_SUPPORT +GC_API GC_ATTR_MALLOC void*GC_CALL GC_gcj_malloc(size_t bytes, +void*ptr_to_struct_containing_descr) +{ +if (EXPECT(GC_incremental,FALSE)){ +return GC_core_gcj_malloc(bytes,ptr_to_struct_containing_descr); +} else { +size_t granules=ROUNDED_UP_GRANULES(bytes); +void*result; +void**tiny_fl; +GC_ASSERT(GC_gcjobjfreelist!=NULL); +tiny_fl=((GC_tlfs)GC_getspecific(GC_thread_key))->gcj_freelists; +GC_FAST_MALLOC_GRANS(result,granules,tiny_fl,DIRECT_GRANULES, +GC_gcj_kind, +GC_core_gcj_malloc(bytes, +ptr_to_struct_containing_descr), +{AO_compiler_barrier(); +*(void**)result=ptr_to_struct_containing_descr;}); +return result; +} +} +#endif +GC_INNER void GC_mark_thread_local_fls_for(GC_tlfs p) +{ +ptr_t q; +int i,j; +for (j=0;j < TINY_FREELISTS;++j){ +for (i=0;i < THREAD_FREELISTS_KINDS;++i){ +q=(ptr_t)AO_load((volatile AO_t*)&p->_freelists[i][j]); +if ((word)q > HBLKSIZE) +GC_set_fl_marks(q); +} +#ifdef GC_GCJ_SUPPORT +if (EXPECT(j > 0,TRUE)){ +q=(ptr_t)AO_load((volatile AO_t*)&p->gcj_freelists[j]); +if ((word)q > HBLKSIZE) +GC_set_fl_marks(q); +} +#endif +} +} +#if defined(GC_ASSERTIONS) +void GC_check_tls_for(GC_tlfs p) +{ +int i,j; +for (j=1;j < TINY_FREELISTS;++j){ +for (i=0;i < THREAD_FREELISTS_KINDS;++i){ +GC_check_fl_marks(&p->_freelists[i][j]); +} +#ifdef GC_GCJ_SUPPORT +GC_check_fl_marks(&p->gcj_freelists[j]); +#endif +} +} +#endif +#endif +#ifndef GC_PTHREAD_SUPPORT_H +#define GC_PTHREAD_SUPPORT_H +#if defined(GC_PTHREADS)&&!defined(GC_WIN32_THREADS) +#if defined(GC_DARWIN_THREADS) +#else +#ifndef GC_PTHREAD_STOP_WORLD_H +#define GC_PTHREAD_STOP_WORLD_H +EXTERN_C_BEGIN +struct thread_stop_info { +#if!defined(GC_OPENBSD_UTHREADS)&&!defined(NACL)&&!defined(SN_TARGET_ORBIS)&&!defined(SN_TARGET_PSP2) +volatile AO_t last_stop_count; +#endif +ptr_t stack_ptr; +#ifdef NACL +#ifdef ARM32 +#define NACL_GC_REG_STORAGE_SIZE 9 +#else +#define NACL_GC_REG_STORAGE_SIZE 20 +#endif +ptr_t reg_storage[NACL_GC_REG_STORAGE_SIZE]; +#elif defined(SN_TARGET_ORBIS) +#define ORBIS_GC_REG_STORAGE_SIZE 27 +word registers[ORBIS_GC_REG_STORAGE_SIZE]; +#endif +}; +GC_INNER void GC_stop_init(void); +EXTERN_C_END +#endif +#endif +#ifdef THREAD_LOCAL_ALLOC +#endif +#ifdef THREAD_SANITIZER +#endif +EXTERN_C_BEGIN +typedef struct GC_Thread_Rep { +#ifdef THREAD_SANITIZER +char dummy[sizeof(oh)]; +#endif +struct GC_Thread_Rep*next; +pthread_t id; +#ifdef USE_TKILL_ON_ANDROID +pid_t kernel_id; +#endif +struct thread_stop_info stop_info; +#if defined(GC_ENABLE_SUSPEND_THREAD)&&!defined(GC_DARWIN_THREADS)&&!defined(GC_OPENBSD_UTHREADS)&&!defined(NACL) +volatile AO_t suspended_ext; +#endif +unsigned char flags; +#define FINISHED 1 +#define DETACHED 2 +#define MAIN_THREAD 4 +#define DISABLED_GC 0x10 +unsigned char thread_blocked; +unsigned short finalizer_skipped; +unsigned char finalizer_nested; +ptr_t stack_end; +ptr_t altstack; +word altstack_size; +ptr_t stack; +word stack_size; +#if defined(GC_DARWIN_THREADS)&&!defined(DARWIN_DONT_PARSE_STACK) +ptr_t topOfStack; +#endif +#ifdef IA64 +ptr_t backing_store_end; +ptr_t backing_store_ptr; +#endif +struct GC_traced_stack_sect_s*traced_stack_sect; +void*status; +#ifdef THREAD_LOCAL_ALLOC +struct thread_local_freelists tlfs GC_ATTR_WORD_ALIGNED; +#endif +}*GC_thread; +#ifndef THREAD_TABLE_SZ +#define THREAD_TABLE_SZ 256 +#endif +#if CPP_WORDSZ==64 +#define THREAD_TABLE_INDEX(id)(int)(((((NUMERIC_THREAD_ID(id)>>8)^NUMERIC_THREAD_ID(id))>>16)^((NUMERIC_THREAD_ID(id)>>8)^NUMERIC_THREAD_ID(id)))% THREAD_TABLE_SZ) +#else +#define THREAD_TABLE_INDEX(id)(int)(((NUMERIC_THREAD_ID(id)>>16)^(NUMERIC_THREAD_ID(id)>>8)^NUMERIC_THREAD_ID(id))% THREAD_TABLE_SZ) +#endif +GC_EXTERN volatile GC_thread GC_threads[THREAD_TABLE_SZ]; +GC_EXTERN GC_bool GC_thr_initialized; +GC_INNER GC_thread GC_lookup_thread(pthread_t id); +#ifdef NACL +GC_EXTERN __thread GC_thread GC_nacl_gc_thread_self; +GC_INNER void GC_nacl_initialize_gc_thread(void); +GC_INNER void GC_nacl_shutdown_gc_thread(void); +#endif +#ifdef GC_EXPLICIT_SIGNALS_UNBLOCK +GC_INNER void GC_unblock_gc_signals(void); +#endif +#ifdef GC_PTHREAD_START_STANDALONE +#define GC_INNER_PTHRSTART +#else +#define GC_INNER_PTHRSTART GC_INNER +#endif +GC_INNER_PTHRSTART void*GC_CALLBACK GC_inner_start_routine( +struct GC_stack_base*sb,void*arg); +GC_INNER_PTHRSTART GC_thread GC_start_rtn_prepare_thread( +void*(**pstart)(void*), +void**pstart_arg, +struct GC_stack_base*sb,void*arg); +GC_INNER_PTHRSTART void GC_thread_exit_proc(void*); +EXTERN_C_END +#endif +#endif +#if defined(GC_DARWIN_THREADS) +#include +#include +#include +#ifdef POWERPC +#if CPP_WORDSZ==32 +#define PPC_RED_ZONE_SIZE 224 +#elif CPP_WORDSZ==64 +#define PPC_RED_ZONE_SIZE 320 +#endif +#endif +#ifndef DARWIN_DONT_PARSE_STACK +typedef struct StackFrame { +unsigned long savedSP; +unsigned long savedCR; +unsigned long savedLR; +unsigned long reserved[2]; +unsigned long savedRTOC; +} StackFrame; +GC_INNER ptr_t GC_FindTopOfStack(unsigned long stack_start) +{ +StackFrame*frame=(StackFrame*)stack_start; +if (stack_start==0){ +#ifdef POWERPC +#if CPP_WORDSZ==32 +__asm__ __volatile__ ("lwz %0,0(r1)":"=r" (frame)); +#else +__asm__ __volatile__ ("ld %0,0(r1)":"=r" (frame)); +#endif +#elif defined(ARM32) +volatile ptr_t sp_reg; +__asm__ __volatile__ ("mov %0,r7\n":"=r" (sp_reg)); +frame=(StackFrame*)sp_reg; +#elif defined(AARCH64) +volatile ptr_t sp_reg; +__asm__ __volatile__ ("mov %0,x29\n":"=r" (sp_reg)); +frame=(StackFrame*)sp_reg; +#else +#if defined(CPPCHECK) +GC_noop1((word)&frame); +#endif +ABORT("GC_FindTopOfStack(0)is not implemented"); +#endif +} +#ifdef DEBUG_THREADS_EXTRA +GC_log_printf("FindTopOfStack start at sp=%p\n",(void*)frame); +#endif +while (frame->savedSP!=0){ +frame=(StackFrame*)frame->savedSP; +if ((frame->savedLR&~0x3)==0||(frame->savedLR&~0x3)==~0x3UL) +break; +} +#ifdef DEBUG_THREADS_EXTRA +GC_log_printf("FindTopOfStack finish at sp=%p\n",(void*)frame); +#endif +return (ptr_t)frame; +} +#endif +#ifdef GC_NO_THREADS_DISCOVERY +#define GC_query_task_threads FALSE +#elif defined(GC_DISCOVER_TASK_THREADS) +#define GC_query_task_threads TRUE +#else +STATIC GC_bool GC_query_task_threads=FALSE; +#endif +GC_API void GC_CALL GC_use_threads_discovery(void) +{ +#if defined(GC_NO_THREADS_DISCOVERY)||defined(DARWIN_DONT_PARSE_STACK) +ABORT("Darwin task-threads-based stop and push unsupported"); +#else +#ifndef GC_ALWAYS_MULTITHREADED +GC_ASSERT(!GC_need_to_lock); +#endif +#ifndef GC_DISCOVER_TASK_THREADS +GC_query_task_threads=TRUE; +#endif +GC_init_parallel(); +#endif +} +#ifndef kCFCoreFoundationVersionNumber_iOS_8_0 +#define kCFCoreFoundationVersionNumber_iOS_8_0 1140.1 +#endif +STATIC ptr_t GC_stack_range_for(ptr_t*phi,thread_act_t thread,GC_thread p, +GC_bool thread_blocked,mach_port_t my_thread, +ptr_t*paltstack_lo, +ptr_t*paltstack_hi GC_ATTR_UNUSED) +{ +ptr_t lo; +if (thread==my_thread){ +GC_ASSERT(!thread_blocked); +lo=GC_approx_sp(); +#ifndef DARWIN_DONT_PARSE_STACK +*phi=GC_FindTopOfStack(0); +#endif +} else if (thread_blocked){ +#if defined(CPPCHECK) +if (NULL==p)ABORT("Invalid GC_thread passed to GC_stack_range_for"); +#endif +lo=p->stop_info.stack_ptr; +#ifndef DARWIN_DONT_PARSE_STACK +*phi=p->topOfStack; +#endif +} else { +kern_return_t kern_result; +GC_THREAD_STATE_T state; +#if defined(ARM32)&&defined(ARM_THREAD_STATE32) +size_t size; +static cpu_type_t cputype=0; +if (cputype==0){ +sysctlbyname("hw.cputype",&cputype,&size,NULL,0); +} +if (cputype==CPU_TYPE_ARM64 +||kCFCoreFoundationVersionNumber +>=kCFCoreFoundationVersionNumber_iOS_8_0){ +arm_unified_thread_state_t unified_state; +mach_msg_type_number_t unified_thread_state_count +=ARM_UNIFIED_THREAD_STATE_COUNT; +#if defined(CPPCHECK) +#define GC_ARM_UNIFIED_THREAD_STATE 1 +#else +#define GC_ARM_UNIFIED_THREAD_STATE ARM_UNIFIED_THREAD_STATE +#endif +kern_result=thread_get_state(thread,GC_ARM_UNIFIED_THREAD_STATE, +(natural_t*)&unified_state, +&unified_thread_state_count); +#if!defined(CPPCHECK) +if (unified_state.ash.flavor!=ARM_THREAD_STATE32){ +ABORT("unified_state flavor should be ARM_THREAD_STATE32"); +} +#endif +state=unified_state; +} else +#endif +{ +mach_msg_type_number_t thread_state_count=GC_MACH_THREAD_STATE_COUNT; +kern_result=thread_get_state(thread,GC_MACH_THREAD_STATE, +(natural_t*)&state, +&thread_state_count); +} +#ifdef DEBUG_THREADS +GC_log_printf("thread_get_state returns value=%d\n",kern_result); +#endif +if (kern_result!=KERN_SUCCESS) +ABORT("thread_get_state failed"); +#if defined(I386) +lo=(ptr_t)state.THREAD_FLD(esp); +#ifndef DARWIN_DONT_PARSE_STACK +*phi=GC_FindTopOfStack(state.THREAD_FLD(esp)); +#endif +GC_push_one(state.THREAD_FLD(eax)); +GC_push_one(state.THREAD_FLD(ebx)); +GC_push_one(state.THREAD_FLD(ecx)); +GC_push_one(state.THREAD_FLD(edx)); +GC_push_one(state.THREAD_FLD(edi)); +GC_push_one(state.THREAD_FLD(esi)); +GC_push_one(state.THREAD_FLD(ebp)); +#elif defined(X86_64) +lo=(ptr_t)state.THREAD_FLD(rsp); +#ifndef DARWIN_DONT_PARSE_STACK +*phi=GC_FindTopOfStack(state.THREAD_FLD(rsp)); +#endif +GC_push_one(state.THREAD_FLD(rax)); +GC_push_one(state.THREAD_FLD(rbx)); +GC_push_one(state.THREAD_FLD(rcx)); +GC_push_one(state.THREAD_FLD(rdx)); +GC_push_one(state.THREAD_FLD(rdi)); +GC_push_one(state.THREAD_FLD(rsi)); +GC_push_one(state.THREAD_FLD(rbp)); +GC_push_one(state.THREAD_FLD(r8)); +GC_push_one(state.THREAD_FLD(r9)); +GC_push_one(state.THREAD_FLD(r10)); +GC_push_one(state.THREAD_FLD(r11)); +GC_push_one(state.THREAD_FLD(r12)); +GC_push_one(state.THREAD_FLD(r13)); +GC_push_one(state.THREAD_FLD(r14)); +GC_push_one(state.THREAD_FLD(r15)); +#elif defined(POWERPC) +lo=(ptr_t)(state.THREAD_FLD(r1)- PPC_RED_ZONE_SIZE); +#ifndef DARWIN_DONT_PARSE_STACK +*phi=GC_FindTopOfStack(state.THREAD_FLD(r1)); +#endif +GC_push_one(state.THREAD_FLD(r0)); +GC_push_one(state.THREAD_FLD(r2)); +GC_push_one(state.THREAD_FLD(r3)); +GC_push_one(state.THREAD_FLD(r4)); +GC_push_one(state.THREAD_FLD(r5)); +GC_push_one(state.THREAD_FLD(r6)); +GC_push_one(state.THREAD_FLD(r7)); +GC_push_one(state.THREAD_FLD(r8)); +GC_push_one(state.THREAD_FLD(r9)); +GC_push_one(state.THREAD_FLD(r10)); +GC_push_one(state.THREAD_FLD(r11)); +GC_push_one(state.THREAD_FLD(r12)); +GC_push_one(state.THREAD_FLD(r13)); +GC_push_one(state.THREAD_FLD(r14)); +GC_push_one(state.THREAD_FLD(r15)); +GC_push_one(state.THREAD_FLD(r16)); +GC_push_one(state.THREAD_FLD(r17)); +GC_push_one(state.THREAD_FLD(r18)); +GC_push_one(state.THREAD_FLD(r19)); +GC_push_one(state.THREAD_FLD(r20)); +GC_push_one(state.THREAD_FLD(r21)); +GC_push_one(state.THREAD_FLD(r22)); +GC_push_one(state.THREAD_FLD(r23)); +GC_push_one(state.THREAD_FLD(r24)); +GC_push_one(state.THREAD_FLD(r25)); +GC_push_one(state.THREAD_FLD(r26)); +GC_push_one(state.THREAD_FLD(r27)); +GC_push_one(state.THREAD_FLD(r28)); +GC_push_one(state.THREAD_FLD(r29)); +GC_push_one(state.THREAD_FLD(r30)); +GC_push_one(state.THREAD_FLD(r31)); +#elif defined(ARM32) +lo=(ptr_t)state.THREAD_FLD(sp); +#ifndef DARWIN_DONT_PARSE_STACK +*phi=GC_FindTopOfStack(state.THREAD_FLD(r[7])); +#endif +{ +int j; +for (j=0;j < 7;j++) +GC_push_one(state.THREAD_FLD(r[j])); +j++; +for (;j<=12;j++) +GC_push_one(state.THREAD_FLD(r[j])); +} +GC_push_one(state.THREAD_FLD(lr)); +#elif defined(AARCH64) +lo=(ptr_t)state.THREAD_FLD(sp); +#ifndef DARWIN_DONT_PARSE_STACK +*phi=GC_FindTopOfStack(state.THREAD_FLD(fp)); +#endif +{ +int j; +for (j=0;j<=28;j++){ +GC_push_one(state.THREAD_FLD(x[j])); +} +} +GC_push_one(state.THREAD_FLD(lr)); +#elif defined(CPPCHECK) +lo=NULL; +#else +#error FIXME for non-arm/ppc/x86 architectures +#endif +} +#ifdef DARWIN_DONT_PARSE_STACK +*phi=(p->flags&MAIN_THREAD)!=0?GC_stackbottom:p->stack_end; +#endif +#ifdef DARWIN_DONT_PARSE_STACK +if (p->altstack!=NULL&&(word)p->altstack<=(word)lo +&&(word)lo<=(word)p->altstack+p->altstack_size){ +*paltstack_lo=lo; +*paltstack_hi=p->altstack+p->altstack_size; +lo=p->stack; +*phi=p->stack+p->stack_size; +} else +#endif +{ +*paltstack_lo=NULL; +} +#ifdef DEBUG_THREADS +GC_log_printf("Darwin:Stack for thread %p=[%p,%p)\n", +(void*)(word)thread,(void*)lo,(void*)(*phi)); +#endif +return lo; +} +GC_INNER void GC_push_all_stacks(void) +{ +ptr_t hi,altstack_lo,altstack_hi; +task_t my_task=current_task(); +mach_port_t my_thread=mach_thread_self(); +GC_bool found_me=FALSE; +int nthreads=0; +word total_size=0; +mach_msg_type_number_t listcount=(mach_msg_type_number_t)THREAD_TABLE_SZ; +if (!EXPECT(GC_thr_initialized,TRUE)) +GC_thr_init(); +#ifndef DARWIN_DONT_PARSE_STACK +if (GC_query_task_threads){ +int i; +kern_return_t kern_result; +thread_act_array_t act_list=0; +kern_result=task_threads(my_task,&act_list,&listcount); +if (kern_result!=KERN_SUCCESS) +ABORT("task_threads failed"); +for (i=0;i < (int)listcount;i++){ +thread_act_t thread=act_list[i]; +ptr_t lo=GC_stack_range_for(&hi,thread,NULL,FALSE,my_thread, +&altstack_lo,&altstack_hi); +if (lo){ +GC_ASSERT((word)lo<=(word)hi); +total_size+=hi - lo; +GC_push_all_stack(lo,hi); +} +nthreads++; +if (thread==my_thread) +found_me=TRUE; +mach_port_deallocate(my_task,thread); +} +vm_deallocate(my_task,(vm_address_t)act_list, +sizeof(thread_t)*listcount); +} else +#endif +{ +int i; +for (i=0;i < (int)listcount;i++){ +GC_thread p; +for (p=GC_threads[i];p!=NULL;p=p->next) +if ((p->flags&FINISHED)==0){ +thread_act_t thread=(thread_act_t)p->stop_info.mach_thread; +ptr_t lo=GC_stack_range_for(&hi,thread,p, +(GC_bool)p->thread_blocked, +my_thread,&altstack_lo, +&altstack_hi); +if (lo){ +GC_ASSERT((word)lo<=(word)hi); +total_size+=hi - lo; +GC_push_all_stack_sections(lo,hi,p->traced_stack_sect); +} +if (altstack_lo){ +total_size+=altstack_hi - altstack_lo; +GC_push_all_stack(altstack_lo,altstack_hi); +} +nthreads++; +if (thread==my_thread) +found_me=TRUE; +} +} +} +mach_port_deallocate(my_task,my_thread); +GC_VERBOSE_LOG_PRINTF("Pushed %d thread stacks\n",nthreads); +if (!found_me&&!GC_in_thread_creation) +ABORT("Collecting from unknown thread"); +GC_total_stacksize=total_size; +} +#ifndef GC_NO_THREADS_DISCOVERY +#ifdef MPROTECT_VDB +STATIC mach_port_t GC_mach_handler_thread=0; +STATIC GC_bool GC_use_mach_handler_thread=FALSE; +GC_INNER void GC_darwin_register_mach_handler_thread(mach_port_t thread) +{ +GC_mach_handler_thread=thread; +GC_use_mach_handler_thread=TRUE; +} +#endif +#ifndef GC_MAX_MACH_THREADS +#define GC_MAX_MACH_THREADS THREAD_TABLE_SZ +#endif +struct GC_mach_thread { +thread_act_t thread; +GC_bool suspended; +}; +struct GC_mach_thread GC_mach_threads[GC_MAX_MACH_THREADS]; +STATIC int GC_mach_threads_count=0; +STATIC GC_bool GC_suspend_thread_list(thread_act_array_t act_list,int count, +thread_act_array_t old_list, +int old_count,task_t my_task, +mach_port_t my_thread) +{ +int i; +int j=-1; +GC_bool changed=FALSE; +for (i=0;i < count;i++){ +thread_act_t thread=act_list[i]; +GC_bool found; +kern_return_t kern_result; +if (thread==my_thread +#ifdef MPROTECT_VDB +||(GC_mach_handler_thread==thread&&GC_use_mach_handler_thread) +#endif +#ifdef PARALLEL_MARK +||GC_is_mach_marker(thread) +#endif +){ +mach_port_deallocate(my_task,thread); +continue; +} +found=FALSE; +{ +int last_found=j; +while (++j < old_count) +if (old_list[j]==thread){ +found=TRUE; +break; +} +if (!found){ +for (j=0;j < last_found;j++) +if (old_list[j]==thread){ +found=TRUE; +break; +} +} +} +if (found){ +mach_port_deallocate(my_task,thread); +continue; +} +if (GC_mach_threads_count==GC_MAX_MACH_THREADS) +ABORT("Too many threads"); +GC_mach_threads[GC_mach_threads_count].thread=thread; +GC_mach_threads[GC_mach_threads_count].suspended=FALSE; +changed=TRUE; +#ifdef DEBUG_THREADS +GC_log_printf("Suspending %p\n",(void*)(word)thread); +#endif +GC_acquire_dirty_lock(); +do { +kern_result=thread_suspend(thread); +} while (kern_result==KERN_ABORTED); +GC_release_dirty_lock(); +if (kern_result!=KERN_SUCCESS){ +GC_mach_threads[GC_mach_threads_count].suspended=FALSE; +} else { +GC_mach_threads[GC_mach_threads_count].suspended=TRUE; +if (GC_on_thread_event) +GC_on_thread_event(GC_EVENT_THREAD_SUSPENDED,(void*)(word)thread); +} +GC_mach_threads_count++; +} +return changed; +} +#endif +GC_INNER void GC_stop_world(void) +{ +task_t my_task=current_task(); +mach_port_t my_thread=mach_thread_self(); +kern_return_t kern_result; +#ifdef DEBUG_THREADS +GC_log_printf("Stopping the world from thread %p\n", +(void*)(word)my_thread); +#endif +#ifdef PARALLEL_MARK +if (GC_parallel){ +GC_acquire_mark_lock(); +GC_ASSERT(GC_fl_builder_count==0); +} +#endif +if (GC_query_task_threads){ +#ifndef GC_NO_THREADS_DISCOVERY +GC_bool changed; +thread_act_array_t act_list,prev_list; +mach_msg_type_number_t listcount,prevcount; +GC_mach_threads_count=0; +changed=TRUE; +prev_list=NULL; +prevcount=0; +do { +kern_result=task_threads(my_task,&act_list,&listcount); +if (kern_result==KERN_SUCCESS){ +changed=GC_suspend_thread_list(act_list,listcount,prev_list, +prevcount,my_task,my_thread); +if (prev_list!=NULL){ +vm_deallocate(my_task,(vm_address_t)prev_list, +sizeof(thread_t)*prevcount); +} +prev_list=act_list; +prevcount=listcount; +} +} while (changed); +GC_ASSERT(prev_list!=0); +vm_deallocate(my_task,(vm_address_t)act_list, +sizeof(thread_t)*listcount); +#endif +} else { +unsigned i; +for (i=0;i < THREAD_TABLE_SZ;i++){ +GC_thread p; +for (p=GC_threads[i];p!=NULL;p=p->next){ +if ((p->flags&FINISHED)==0&&!p->thread_blocked&& +p->stop_info.mach_thread!=my_thread){ +GC_acquire_dirty_lock(); +do { +kern_result=thread_suspend(p->stop_info.mach_thread); +} while (kern_result==KERN_ABORTED); +GC_release_dirty_lock(); +if (kern_result!=KERN_SUCCESS) +ABORT("thread_suspend failed"); +if (GC_on_thread_event) +GC_on_thread_event(GC_EVENT_THREAD_SUSPENDED, +(void*)(word)p->stop_info.mach_thread); +} +} +} +} +#ifdef MPROTECT_VDB +if (GC_auto_incremental){ +GC_mprotect_stop(); +} +#endif +#ifdef PARALLEL_MARK +if (GC_parallel) +GC_release_mark_lock(); +#endif +#ifdef DEBUG_THREADS +GC_log_printf("World stopped from %p\n",(void*)(word)my_thread); +#endif +mach_port_deallocate(my_task,my_thread); +} +GC_INLINE void GC_thread_resume(thread_act_t thread) +{ +kern_return_t kern_result; +#if defined(DEBUG_THREADS)||defined(GC_ASSERTIONS) +struct thread_basic_info info; +mach_msg_type_number_t outCount=THREAD_BASIC_INFO_COUNT; +#if defined(CPPCHECK)&&defined(DEBUG_THREADS) +info.run_state=0; +#endif +kern_result=thread_info(thread,THREAD_BASIC_INFO, +(thread_info_t)&info,&outCount); +if (kern_result!=KERN_SUCCESS) +ABORT("thread_info failed"); +#endif +#ifdef DEBUG_THREADS +GC_log_printf("Resuming thread %p with state %d\n",(void*)(word)thread, +info.run_state); +#endif +kern_result=thread_resume(thread); +if (kern_result!=KERN_SUCCESS){ +WARN("thread_resume(%p)failed:mach port invalid\n",thread); +} else if (GC_on_thread_event){ +GC_on_thread_event(GC_EVENT_THREAD_UNSUSPENDED,(void*)(word)thread); +} +} +GC_INNER void GC_start_world(void) +{ +task_t my_task=current_task(); +#ifdef DEBUG_THREADS +GC_log_printf("World starting\n"); +#endif +#ifdef MPROTECT_VDB +if (GC_auto_incremental){ +GC_mprotect_resume(); +} +#endif +if (GC_query_task_threads){ +#ifndef GC_NO_THREADS_DISCOVERY +int i,j; +kern_return_t kern_result; +thread_act_array_t act_list; +mach_msg_type_number_t listcount; +kern_result=task_threads(my_task,&act_list,&listcount); +if (kern_result!=KERN_SUCCESS) +ABORT("task_threads failed"); +j=(int)listcount; +for (i=0;i < GC_mach_threads_count;i++){ +thread_act_t thread=GC_mach_threads[i].thread; +if (GC_mach_threads[i].suspended){ +int last_found=j; +while (++j < (int)listcount){ +if (act_list[j]==thread) +break; +} +if (j>=(int)listcount){ +for (j=0;j < last_found;j++){ +if (act_list[j]==thread) +break; +} +} +if (j!=last_found){ +GC_thread_resume(thread); +} +} else { +#ifdef DEBUG_THREADS +GC_log_printf("Not resuming thread %p as it is not suspended\n", +(void*)(word)thread); +#endif +} +mach_port_deallocate(my_task,thread); +} +for (i=0;i < (int)listcount;i++) +mach_port_deallocate(my_task,act_list[i]); +vm_deallocate(my_task,(vm_address_t)act_list, +sizeof(thread_t)*listcount); +#endif +} else { +int i; +mach_port_t my_thread=mach_thread_self(); +for (i=0;i < THREAD_TABLE_SZ;i++){ +GC_thread p; +for (p=GC_threads[i];p!=NULL;p=p->next){ +if ((p->flags&FINISHED)==0&&!p->thread_blocked&& +p->stop_info.mach_thread!=my_thread) +GC_thread_resume(p->stop_info.mach_thread); +} +} +mach_port_deallocate(my_task,my_thread); +} +#ifdef DEBUG_THREADS +GC_log_printf("World started\n"); +#endif +} +#endif +#if!defined(MACOS)&&!defined(SN_TARGET_ORBIS)&&!defined(SN_TARGET_PSP2)&&!defined(_WIN32_WCE)&&!defined(__CC_ARM) +#include +#endif +#undef GC_MUST_RESTORE_REDEFINED_DLOPEN +#if defined(GC_PTHREADS)&&!defined(GC_NO_DLOPEN)&&!defined(GC_NO_THREAD_REDIRECTS)&&!defined(GC_USE_LD_WRAP) +#undef dlopen +#define GC_MUST_RESTORE_REDEFINED_DLOPEN +#endif +STATIC GC_has_static_roots_func GC_has_static_roots=0; +#if (defined(DYNAMIC_LOADING)||defined(MSWIN32)||defined(MSWINCE)||defined(CYGWIN32))&&!defined(PCR) +#if!defined(DARWIN)&&!defined(SCO_ELF)&&!defined(SOLARISDL)&&!defined(AIX)&&!defined(DGUX)&&!defined(IRIX5)&&!defined(HPUX)&&!defined(CYGWIN32)&&!defined(MSWIN32)&&!defined(MSWINCE)&&!(defined(ALPHA)&&defined(OSF1))&&!(defined(FREEBSD)&&defined(__ELF__))&&!(defined(LINUX)&&defined(__ELF__))&&!(defined(NETBSD)&&defined(__ELF__))&&!(defined(OPENBSD)&&(defined(__ELF__)||defined(M68K)))&&!defined(HAIKU)&&!defined(HURD)&&!defined(NACL)&&!defined(CPPCHECK) +#error We only know how to find data segments of dynamic libraries for above. +#error Additional SVR4 variants might not be too hard to add. +#endif +#include +#ifdef SOLARISDL +#include +#include +#include +#endif +#if defined(NETBSD) +#include +#include +#include +#define ELFSIZE ARCH_ELFSIZE +#endif +#if defined(OPENBSD) +#include +#if (OpenBSD>=200519)&&!defined(HAVE_DL_ITERATE_PHDR) +#define HAVE_DL_ITERATE_PHDR +#endif +#endif +#if defined(SCO_ELF)||defined(DGUX)||defined(HURD)||defined(NACL)||(defined(__ELF__)&&(defined(LINUX)||defined(FREEBSD)||defined(NETBSD)||defined(OPENBSD))) +#include +#if!defined(OPENBSD)&&!defined(HOST_ANDROID) +#include +#endif +#ifdef HOST_ANDROID +#ifdef BIONIC_ELFDATA_REDEF_BUG +#include +#include +#undef ELF_DATA +#undef EM_ALPHA +#endif +#include +#if!defined(GC_DONT_DEFINE_LINK_MAP)&&!(__ANDROID_API__>=21) +struct link_map { +uintptr_t l_addr; +char*l_name; +uintptr_t l_ld; +struct link_map*l_next; +struct link_map*l_prev; +}; +struct r_debug { +int32_t r_version; +struct link_map*r_map; +void (*r_brk)(void); +int32_t r_state; +uintptr_t r_ldbase; +}; +#endif +#else +EXTERN_C_BEGIN +#include +EXTERN_C_END +#endif +#endif +#ifndef ElfW +#if defined(FREEBSD) +#if __ELF_WORD_SIZE==32 +#define ElfW(type)Elf32_##type +#else +#define ElfW(type)Elf64_##type +#endif +#elif defined(NETBSD)||defined(OPENBSD) +#if ELFSIZE==32 +#define ElfW(type)Elf32_##type +#elif ELFSIZE==64 +#define ElfW(type)Elf64_##type +#else +#error Missing ELFSIZE define +#endif +#else +#if!defined(ELF_CLASS)||ELF_CLASS==ELFCLASS32 +#define ElfW(type)Elf32_##type +#else +#define ElfW(type)Elf64_##type +#endif +#endif +#endif +#if defined(SOLARISDL)&&!defined(USE_PROC_FOR_LIBRARIES) +EXTERN_C_BEGIN +extern ElfW(Dyn)_DYNAMIC; +EXTERN_C_END +STATIC struct link_map* +GC_FirstDLOpenedLinkMap(void) +{ +ElfW(Dyn)*dp; +static struct link_map*cachedResult=0; +static ElfW(Dyn)*dynStructureAddr=0; +#ifdef SUNOS53_SHARED_LIB +if( dynStructureAddr==0){ +void*startupSyms=dlopen(0,RTLD_LAZY); +dynStructureAddr=(ElfW(Dyn)*)(word)dlsym(startupSyms,"_DYNAMIC"); +} +#else +dynStructureAddr=&_DYNAMIC; +#endif +if (0==COVERT_DATAFLOW(dynStructureAddr)){ +return(0); +} +if (cachedResult==0){ +int tag; +for( dp=((ElfW(Dyn)*)(&_DYNAMIC));(tag=dp->d_tag)!=0;dp++){ +if (tag==DT_DEBUG){ +struct r_debug*rd=(struct r_debug*)dp->d_un.d_ptr; +if (rd!=NULL){ +struct link_map*lm=rd->r_map; +if (lm!=NULL) +cachedResult=lm->l_next; +} +break; +} +} +} +return cachedResult; +} +#endif +#ifdef GC_MUST_RESTORE_REDEFINED_DLOPEN +#define dlopen GC_dlopen +#endif +#if defined(SOLARISDL) +#if!defined(PCR)&&!defined(GC_SOLARIS_THREADS)&&defined(THREADS)&&!defined(CPPCHECK) +#error Fix mutual exclusion with dlopen +#endif +#ifndef USE_PROC_FOR_LIBRARIES +GC_INNER void GC_register_dynamic_libraries(void) +{ +struct link_map*lm; +for (lm=GC_FirstDLOpenedLinkMap();lm!=0;lm=lm->l_next){ +ElfW(Ehdr)*e; +ElfW(Phdr)*p; +unsigned long offset; +char*start; +int i; +e=(ElfW(Ehdr)*)lm->l_addr; +p=((ElfW(Phdr)*)(((char*)(e))+e->e_phoff)); +offset=((unsigned long)(lm->l_addr)); +for( i=0;i < (int)e->e_phnum;i++,p++){ +switch( p->p_type){ +case PT_LOAD: +{ +if(!(p->p_flags&PF_W))break; +start=((char*)(p->p_vaddr))+offset; +GC_add_roots_inner(start,start+p->p_memsz,TRUE); +} +break; +default: +break; +} +} +} +} +#endif +#endif +#if defined(SCO_ELF)||defined(DGUX)||defined(HURD)||defined(NACL)||(defined(__ELF__)&&(defined(LINUX)||defined(FREEBSD)||defined(NETBSD)||defined(OPENBSD))) +#ifdef USE_PROC_FOR_LIBRARIES +#include +#include +#include +#include +#define MAPS_BUF_SIZE (32*1024) +static void sort_heap_sects(struct HeapSect*base,size_t number_of_elements) +{ +signed_word n=(signed_word)number_of_elements; +signed_word nsorted=1; +while (nsorted < n){ +signed_word i; +while (nsorted < n&& +(word)base[nsorted-1].hs_start < (word)base[nsorted].hs_start) +++nsorted; +if (nsorted==n)break; +GC_ASSERT((word)base[nsorted-1].hs_start > (word)base[nsorted].hs_start); +i=nsorted - 1; +while (i>=0&&(word)base[i].hs_start > (word)base[i+1].hs_start){ +struct HeapSect tmp=base[i]; +base[i]=base[i+1]; +base[i+1]=tmp; +--i; +} +GC_ASSERT((word)base[nsorted-1].hs_start < (word)base[nsorted].hs_start); +++nsorted; +} +} +STATIC void GC_register_map_entries(char*maps) +{ +char*prot; +char*buf_ptr=maps; +ptr_t start,end; +unsigned int maj_dev; +ptr_t least_ha,greatest_ha; +unsigned i; +GC_ASSERT(I_HOLD_LOCK()); +sort_heap_sects(GC_our_memory,GC_n_memory); +least_ha=GC_our_memory[0].hs_start; +greatest_ha=GC_our_memory[GC_n_memory-1].hs_start ++GC_our_memory[GC_n_memory-1].hs_bytes; +for (;;){ +buf_ptr=GC_parse_map_entry(buf_ptr,&start,&end,&prot, +&maj_dev,0); +if (NULL==buf_ptr) +break; +if (prot[1]=='w'){ +if ((word)start<=(word)GC_stackbottom +&&(word)end>=(word)GC_stackbottom){ +continue; +} +#ifdef THREADS +if (GC_segment_is_thread_stack(start,end))continue; +#endif +if ((word)end<=(word)least_ha +||(word)start>=(word)greatest_ha){ +GC_add_roots_inner(start,end,TRUE); +continue; +} +i=0; +while ((word)(GC_our_memory[i].hs_start ++GC_our_memory[i].hs_bytes)< (word)start) +++i; +GC_ASSERT(i < GC_n_memory); +if ((word)GC_our_memory[i].hs_start<=(word)start){ +start=GC_our_memory[i].hs_start ++GC_our_memory[i].hs_bytes; +++i; +} +while (i < GC_n_memory +&&(word)GC_our_memory[i].hs_start < (word)end +&&(word)start < (word)end){ +if ((word)start < (word)GC_our_memory[i].hs_start) +GC_add_roots_inner(start, +GC_our_memory[i].hs_start,TRUE); +start=GC_our_memory[i].hs_start ++GC_our_memory[i].hs_bytes; +++i; +} +if ((word)start < (word)end) +GC_add_roots_inner(start,end,TRUE); +} else if (prot[0]=='-'&&prot[1]=='-'&&prot[2]=='-'){ +GC_remove_roots_subregion(start,end); +} +} +} +GC_INNER void GC_register_dynamic_libraries(void) +{ +char*maps=GC_get_maps(); +if (NULL==maps) +ABORT("Failed to read/proc for library registration"); +GC_register_map_entries(maps); +} +GC_INNER GC_bool GC_register_main_static_data(void) +{ +return FALSE; +} +#define HAVE_REGISTER_MAIN_STATIC_DATA +#else +#if __GLIBC__ > 2||(__GLIBC__==2&&__GLIBC_MINOR__ > 2)||(__GLIBC__==2&&__GLIBC_MINOR__==2&&defined(DT_CONFIG))||defined(HOST_ANDROID) +#ifndef HAVE_DL_ITERATE_PHDR +#define HAVE_DL_ITERATE_PHDR +#endif +#ifdef HOST_ANDROID +EXTERN_C_BEGIN +extern int dl_iterate_phdr(int (*cb)(struct dl_phdr_info*, +size_t,void*), +void*data); +EXTERN_C_END +#endif +#endif +#if defined(__DragonFly__)||defined(__FreeBSD_kernel__)||(defined(FREEBSD)&&__FreeBSD__>=7) +#ifndef HAVE_DL_ITERATE_PHDR +#define HAVE_DL_ITERATE_PHDR +#endif +#define DL_ITERATE_PHDR_STRONG +#elif defined(HAVE_DL_ITERATE_PHDR) +EXTERN_C_BEGIN +#pragma weak dl_iterate_phdr +EXTERN_C_END +#endif +#if defined(HAVE_DL_ITERATE_PHDR) +#ifdef PT_GNU_RELRO +#define MAX_LOAD_SEGS MAX_ROOT_SETS +static struct load_segment { +ptr_t start; +ptr_t end; +ptr_t start2; +ptr_t end2; +} load_segs[MAX_LOAD_SEGS]; +static int n_load_segs; +static GC_bool load_segs_overflow; +#endif +STATIC int GC_register_dynlib_callback(struct dl_phdr_info*info, +size_t size,void*ptr) +{ +const ElfW(Phdr)*p; +ptr_t start,end; +int i; +if (size < offsetof (struct dl_phdr_info,dlpi_phnum) ++sizeof (info->dlpi_phnum)) +return -1; +p=info->dlpi_phdr; +for (i=0;i < (int)info->dlpi_phnum;i++,p++){ +if (p->p_type==PT_LOAD){ +GC_has_static_roots_func callback=GC_has_static_roots; +if ((p->p_flags&PF_W)==0)continue; +start=(ptr_t)p->p_vaddr+info->dlpi_addr; +end=start+p->p_memsz; +if (callback!=0&&!callback(info->dlpi_name,start,p->p_memsz)) +continue; +#ifdef PT_GNU_RELRO +#if CPP_WORDSZ==64 +start=(ptr_t)((word)start&~(word)(sizeof(word)- 1)); +#endif +if (n_load_segs>=MAX_LOAD_SEGS){ +if (!load_segs_overflow){ +WARN("Too many PT_LOAD segments;" +" registering as roots directly...\n",0); +load_segs_overflow=TRUE; +} +GC_add_roots_inner(start,end,TRUE); +} else { +load_segs[n_load_segs].start=start; +load_segs[n_load_segs].end=end; +load_segs[n_load_segs].start2=0; +load_segs[n_load_segs].end2=0; +++n_load_segs; +} +#else +GC_add_roots_inner(start,end,TRUE); +#endif +} +} +#ifdef PT_GNU_RELRO +p=info->dlpi_phdr; +for (i=0;i < (int)info->dlpi_phnum;i++,p++){ +if (p->p_type==PT_GNU_RELRO){ +int j; +start=(ptr_t)p->p_vaddr+info->dlpi_addr; +end=start+p->p_memsz; +for (j=n_load_segs;--j>=0;){ +if ((word)start>=(word)load_segs[j].start +&&(word)start < (word)load_segs[j].end){ +if (load_segs[j].start2!=0){ +WARN("More than one GNU_RELRO segment per load one\n",0); +} else { +GC_ASSERT((word)end<=(word)load_segs[j].end); +load_segs[j].end2=load_segs[j].end; +load_segs[j].end=start; +load_segs[j].start2=end; +} +break; +} +if (0==j&&0==GC_has_static_roots) +WARN("Failed to find PT_GNU_RELRO segment" +" inside PT_LOAD region\n",0); +} +} +} +#endif +*(int*)ptr=1; +return 0; +} +GC_INNER GC_bool GC_register_main_static_data(void) +{ +#ifdef DL_ITERATE_PHDR_STRONG +return FALSE; +#else +return 0==COVERT_DATAFLOW(dl_iterate_phdr); +#endif +} +STATIC GC_bool GC_register_dynamic_libraries_dl_iterate_phdr(void) +{ +int did_something; +if (GC_register_main_static_data()) +return FALSE; +#ifdef PT_GNU_RELRO +{ +static GC_bool excluded_segs=FALSE; +n_load_segs=0; +load_segs_overflow=FALSE; +if (!EXPECT(excluded_segs,TRUE)){ +GC_exclude_static_roots_inner((ptr_t)load_segs, +(ptr_t)load_segs+sizeof(load_segs)); +excluded_segs=TRUE; +} +} +#endif +did_something=0; +dl_iterate_phdr(GC_register_dynlib_callback,&did_something); +if (did_something){ +#ifdef PT_GNU_RELRO +int i; +for (i=0;i < n_load_segs;++i){ +if ((word)load_segs[i].end > (word)load_segs[i].start){ +GC_add_roots_inner(load_segs[i].start,load_segs[i].end,TRUE); +} +if ((word)load_segs[i].end2 > (word)load_segs[i].start2){ +GC_add_roots_inner(load_segs[i].start2,load_segs[i].end2,TRUE); +} +} +#endif +} else { +ptr_t datastart,dataend; +#ifdef DATASTART_IS_FUNC +static ptr_t datastart_cached=(ptr_t)GC_WORD_MAX; +if (datastart_cached==(ptr_t)GC_WORD_MAX){ +datastart_cached=DATASTART; +} +datastart=datastart_cached; +#else +datastart=DATASTART; +#endif +#ifdef DATAEND_IS_FUNC +{ +static ptr_t dataend_cached=0; +if (dataend_cached==0){ +dataend_cached=DATAEND; +} +dataend=dataend_cached; +} +#else +dataend=DATAEND; +#endif +if (NULL==*(char*volatile*)&datastart +||(word)datastart > (word)dataend) +ABORT_ARG2("Wrong DATASTART/END pair", +":%p .. %p",(void*)datastart,(void*)dataend); +GC_add_roots_inner(datastart,dataend,TRUE); +#ifdef GC_HAVE_DATAREGION2 +if ((word)DATASTART2 - 1U>=(word)DATAEND2){ +ABORT_ARG2("Wrong DATASTART/END2 pair", +":%p .. %p",(void*)DATASTART2,(void*)DATAEND2); +} +GC_add_roots_inner(DATASTART2,DATAEND2,TRUE); +#endif +} +return TRUE; +} +#define HAVE_REGISTER_MAIN_STATIC_DATA +#else +#if defined(NETBSD)||defined(OPENBSD) +#include +#ifndef DT_DEBUG +#define DT_DEBUG 21 +#endif +#ifndef PT_LOAD +#define PT_LOAD 1 +#endif +#ifndef PF_W +#define PF_W 2 +#endif +#elif!defined(HOST_ANDROID) +#include +#endif +#ifndef HOST_ANDROID +#include +#endif +#endif +EXTERN_C_BEGIN +#ifdef __GNUC__ +#pragma weak _DYNAMIC +#endif +extern ElfW(Dyn)_DYNAMIC[]; +EXTERN_C_END +STATIC struct link_map* +GC_FirstDLOpenedLinkMap(void) +{ +static struct link_map*cachedResult=0; +if (0==COVERT_DATAFLOW(_DYNAMIC)){ +return(0); +} +if( cachedResult==0){ +#if defined(NETBSD)&&defined(RTLD_DI_LINKMAP) +#if defined(CPPCHECK) +#define GC_RTLD_DI_LINKMAP 2 +#else +#define GC_RTLD_DI_LINKMAP RTLD_DI_LINKMAP +#endif +struct link_map*lm=NULL; +if (!dlinfo(RTLD_SELF,GC_RTLD_DI_LINKMAP,&lm)&&lm!=NULL){ +while (lm->l_prev!=NULL){ +lm=lm->l_prev; +} +cachedResult=lm->l_next; +} +#else +ElfW(Dyn)*dp; +int tag; +for( dp=_DYNAMIC;(tag=dp->d_tag)!=0;dp++){ +if (tag==DT_DEBUG){ +struct r_debug*rd=(struct r_debug*)dp->d_un.d_ptr; +if (rd!=NULL){ +struct link_map*lm=rd->r_map; +if (lm!=NULL) +cachedResult=lm->l_next; +} +break; +} +} +#endif +} +return cachedResult; +} +GC_INNER void GC_register_dynamic_libraries(void) +{ +struct link_map*lm; +#ifdef HAVE_DL_ITERATE_PHDR +if (GC_register_dynamic_libraries_dl_iterate_phdr()){ +return; +} +#endif +for (lm=GC_FirstDLOpenedLinkMap();lm!=0;lm=lm->l_next) +{ +ElfW(Ehdr)*e; +ElfW(Phdr)*p; +unsigned long offset; +char*start; +int i; +e=(ElfW(Ehdr)*)lm->l_addr; +#ifdef HOST_ANDROID +if (e==NULL) +continue; +#endif +p=((ElfW(Phdr)*)(((char*)(e))+e->e_phoff)); +offset=((unsigned long)(lm->l_addr)); +for( i=0;i < (int)e->e_phnum;i++,p++){ +switch( p->p_type){ +case PT_LOAD: +{ +if(!(p->p_flags&PF_W))break; +start=((char*)(p->p_vaddr))+offset; +GC_add_roots_inner(start,start+p->p_memsz,TRUE); +} +break; +default: +break; +} +} +} +} +#endif +#endif +#if defined(IRIX5)||(defined(USE_PROC_FOR_LIBRARIES)&&!defined(LINUX)) +#include +#include +#include +#include +#include +#include +#ifndef _sigargs +#define IRIX6 +#endif +GC_INNER void GC_register_dynamic_libraries(void) +{ +static int fd=-1; +char buf[30]; +static prmap_t*addr_map=0; +static int current_sz=0; +int needed_sz=0; +int i; +long flags; +ptr_t start; +ptr_t limit; +ptr_t heap_start=HEAP_START; +ptr_t heap_end=heap_start; +#ifdef SOLARISDL +#define MA_PHYS 0 +#endif +if (fd < 0){ +(void)snprintf(buf,sizeof(buf),"/proc/%ld",(long)getpid()); +buf[sizeof(buf)- 1]='\0'; +fd=open(buf,O_RDONLY); +if (fd < 0){ +ABORT("/proc open failed"); +} +} +if (ioctl(fd,PIOCNMAP,&needed_sz)< 0){ +ABORT_ARG2("/proc PIOCNMAP ioctl failed", +":fd=%d,errno=%d",fd,errno); +} +if (needed_sz>=current_sz){ +GC_scratch_recycle_no_gww(addr_map, +(size_t)current_sz*sizeof(prmap_t)); +current_sz=needed_sz*2+1; +addr_map=(prmap_t*)GC_scratch_alloc( +(size_t)current_sz*sizeof(prmap_t)); +if (addr_map==NULL) +ABORT("Insufficient memory for address map"); +} +if (ioctl(fd,PIOCMAP,addr_map)< 0){ +ABORT_ARG3("/proc PIOCMAP ioctl failed", +":errcode=%d,needed_sz=%d,addr_map=%p", +errno,needed_sz,(void*)addr_map); +}; +if (GC_n_heap_sects > 0){ +heap_end=GC_heap_sects[GC_n_heap_sects-1].hs_start ++GC_heap_sects[GC_n_heap_sects-1].hs_bytes; +if ((word)heap_end < (word)GC_scratch_last_end_ptr) +heap_end=GC_scratch_last_end_ptr; +} +for (i=0;i < needed_sz;i++){ +flags=addr_map[i].pr_mflags; +if ((flags&(MA_BREAK|MA_STACK|MA_PHYS +|MA_FETCHOP|MA_NOTCACHED))!=0)goto irrelevant; +if ((flags&(MA_READ|MA_WRITE))!=(MA_READ|MA_WRITE)) +goto irrelevant; +start=(ptr_t)(addr_map[i].pr_vaddr); +if (GC_roots_present(start))goto irrelevant; +if ((word)start < (word)heap_end&&(word)start>=(word)heap_start) +goto irrelevant; +limit=start+addr_map[i].pr_size; +#ifndef IRIX6 +if (addr_map[i].pr_off==0&&strncmp(start,ELFMAG,4)==0){ +caddr_t arg; +int obj; +#define MAP_IRR_SZ 10 +static ptr_t map_irr[MAP_IRR_SZ]; +static int n_irr=0; +struct stat buf; +int j; +for (j=0;j < n_irr;j++){ +if (map_irr[j]==start)goto irrelevant; +} +arg=(caddr_t)start; +obj=ioctl(fd,PIOCOPENM,&arg); +if (obj>=0){ +fstat(obj,&buf); +close(obj); +if ((buf.st_mode&0111)!=0){ +if (n_irr < MAP_IRR_SZ){ +map_irr[n_irr++]=start; +} +goto irrelevant; +} +} +} +#endif +GC_add_roots_inner(start,limit,TRUE); +irrelevant:; +} +if (close(fd)< 0)ABORT("Couldn't close/proc file"); +fd=-1; +} +#endif +#if defined(MSWIN32)||defined(MSWINCE)||defined(CYGWIN32) +#include +STATIC void GC_cond_add_roots(char*base,char*limit) +{ +#ifdef GC_WIN32_THREADS +char*curr_base=base; +char*next_stack_lo; +char*next_stack_hi; +if (base==limit)return; +for(;;){ +GC_get_next_stack(curr_base,limit,&next_stack_lo,&next_stack_hi); +if ((word)next_stack_lo>=(word)limit)break; +if ((word)next_stack_lo > (word)curr_base) +GC_add_roots_inner(curr_base,next_stack_lo,TRUE); +curr_base=next_stack_hi; +} +if ((word)curr_base < (word)limit) +GC_add_roots_inner(curr_base,limit,TRUE); +#else +char*stack_top +=(char*)((word)GC_approx_sp()& +~(word)(GC_sysinfo.dwAllocationGranularity - 1)); +if (base==limit)return; +if ((word)limit > (word)stack_top +&&(word)base < (word)GC_stackbottom){ +return; +} +GC_add_roots_inner(base,limit,TRUE); +#endif +} +#ifdef DYNAMIC_LOADING +GC_INNER GC_bool GC_register_main_static_data(void) +{ +#if defined(MSWINCE)||defined(CYGWIN32) +return FALSE; +#else +return GC_no_win32_dlls; +#endif +} +#define HAVE_REGISTER_MAIN_STATIC_DATA +#endif +#ifdef DEBUG_VIRTUALQUERY +void GC_dump_meminfo(MEMORY_BASIC_INFORMATION*buf) +{ +GC_printf("BaseAddress=0x%lx,AllocationBase=0x%lx," +" RegionSize=0x%lx(%lu)\n",buf->BaseAddress, +buf->AllocationBase,buf->RegionSize,buf->RegionSize); +GC_printf("\tAllocationProtect=0x%lx,State=0x%lx,Protect=0x%lx," +"Type=0x%lx\n",buf->AllocationProtect,buf->State, +buf->Protect,buf->Type); +} +#endif +#if defined(MSWINCE)||defined(CYGWIN32) +#define GC_wnt TRUE +#endif +GC_INNER void GC_register_dynamic_libraries(void) +{ +MEMORY_BASIC_INFORMATION buf; +DWORD protect; +LPVOID p; +char*base; +char*limit,*new_limit; +#ifdef MSWIN32 +if (GC_no_win32_dlls)return; +#endif +p=GC_sysinfo.lpMinimumApplicationAddress; +base=limit=(char*)p; +while ((word)p < (word)GC_sysinfo.lpMaximumApplicationAddress){ +size_t result=VirtualQuery(p,&buf,sizeof(buf)); +#ifdef MSWINCE +if (result==0){ +new_limit=(char*) +(((DWORD)p+GC_sysinfo.dwAllocationGranularity) +&~(GC_sysinfo.dwAllocationGranularity-1)); +} else +#endif +{ +if (result!=sizeof(buf)){ +ABORT("Weird VirtualQuery result"); +} +new_limit=(char*)p+buf.RegionSize; +protect=buf.Protect; +if (buf.State==MEM_COMMIT +&&(protect==PAGE_EXECUTE_READWRITE +||protect==PAGE_EXECUTE_WRITECOPY +||protect==PAGE_READWRITE +||protect==PAGE_WRITECOPY) +&&(buf.Type==MEM_IMAGE +#ifdef GC_REGISTER_MEM_PRIVATE +||(protect==PAGE_READWRITE&&buf.Type==MEM_PRIVATE) +#else +||(!GC_wnt&&buf.Type==MEM_PRIVATE) +#endif +) +&&!GC_is_heap_base(buf.AllocationBase)){ +#ifdef DEBUG_VIRTUALQUERY +GC_dump_meminfo(&buf); +#endif +if ((char*)p!=limit){ +GC_cond_add_roots(base,limit); +base=(char*)p; +} +limit=new_limit; +} +} +if ((word)p > (word)new_limit)break; +p=(LPVOID)new_limit; +} +GC_cond_add_roots(base,limit); +} +#endif +#if defined(ALPHA)&&defined(OSF1) +#include +EXTERN_C_BEGIN +extern char*sys_errlist[]; +extern int sys_nerr; +extern int errno; +EXTERN_C_END +GC_INNER void GC_register_dynamic_libraries(void) +{ +ldr_module_t moduleid=LDR_NULL_MODULE; +ldr_process_t mypid=ldr_my_process(); +while (TRUE){ +ldr_module_info_t moduleinfo; +size_t modulereturnsize; +ldr_region_t region; +ldr_region_info_t regioninfo; +size_t regionreturnsize; +int status=ldr_next_module(mypid,&moduleid); +if (moduleid==LDR_NULL_MODULE) +break; +if (status!=0){ +ABORT_ARG3("ldr_next_module failed", +":status=%d,errcode=%d (%s)",status,errno, +errno < sys_nerr?sys_errlist[errno]:""); +} +status=ldr_inq_module(mypid,moduleid,&moduleinfo, +sizeof(moduleinfo),&modulereturnsize); +if (status!=0) +ABORT("ldr_inq_module failed"); +if (moduleinfo.lmi_flags&LDR_MAIN) +continue; +#ifdef DL_VERBOSE +GC_log_printf("---Module---\n"); +GC_log_printf("Module ID\t=%16ld\n",moduleinfo.lmi_modid); +GC_log_printf("Count of regions=%16d\n",moduleinfo.lmi_nregion); +GC_log_printf("flags for module=%16lx\n",moduleinfo.lmi_flags); +GC_log_printf("module pathname\t=\"%s\"\n",moduleinfo.lmi_name); +#endif +for (region=0;region < moduleinfo.lmi_nregion;region++){ +status=ldr_inq_region(mypid,moduleid,region,®ioninfo, +sizeof(regioninfo),®ionreturnsize); +if (status!=0) +ABORT("ldr_inq_region failed"); +if (!(regioninfo.lri_prot&LDR_W)) +continue; +#ifdef DL_VERBOSE +GC_log_printf("--- Region---\n"); +GC_log_printf("Region number\t=%16ld\n", +regioninfo.lri_region_no); +GC_log_printf("Protection flags=%016x\n",regioninfo.lri_prot); +GC_log_printf("Virtual address\t=%16p\n",regioninfo.lri_vaddr); +GC_log_printf("Mapped address\t=%16p\n", +regioninfo.lri_mapaddr); +GC_log_printf("Region size\t=%16ld\n",regioninfo.lri_size); +GC_log_printf("Region name\t=\"%s\"\n",regioninfo.lri_name); +#endif +GC_add_roots_inner((char*)regioninfo.lri_mapaddr, +(char*)regioninfo.lri_mapaddr+regioninfo.lri_size, +TRUE); +} +} +} +#endif +#if defined(HPUX) +#include +#include +EXTERN_C_BEGIN +extern char*sys_errlist[]; +extern int sys_nerr; +EXTERN_C_END +GC_INNER void GC_register_dynamic_libraries(void) +{ +int index=1; +while (TRUE){ +struct shl_descriptor*shl_desc; +int status=shl_get(index,&shl_desc); +if (status!=0){ +#ifdef GC_HPUX_THREADS +break; +#else +if (errno==EINVAL){ +break; +} else { +ABORT_ARG3("shl_get failed", +":status=%d,errcode=%d (%s)",status,errno, +errno < sys_nerr?sys_errlist[errno]:""); +} +#endif +} +#ifdef DL_VERBOSE +GC_log_printf("---Shared library---\n"); +GC_log_printf("\tfilename\t=\"%s\"\n",shl_desc->filename); +GC_log_printf("\tindex\t\t=%d\n",index); +GC_log_printf("\thandle\t\t=%08x\n", +(unsigned long)shl_desc->handle); +GC_log_printf("\ttext seg.start\t=%08x\n",shl_desc->tstart); +GC_log_printf("\ttext seg.end\t=%08x\n",shl_desc->tend); +GC_log_printf("\tdata seg.start\t=%08x\n",shl_desc->dstart); +GC_log_printf("\tdata seg.end\t=%08x\n",shl_desc->dend); +GC_log_printf("\tref.count\t=%lu\n",shl_desc->ref_count); +#endif +GC_add_roots_inner((char*)shl_desc->dstart, +(char*)shl_desc->dend,TRUE); +index++; +} +} +#endif +#ifdef AIX +#include +#include +#include +GC_INNER void GC_register_dynamic_libraries(void) +{ +int ldibuflen=8192; +for (;;){ +int len; +struct ld_info*ldi; +#if defined(CPPCHECK) +char ldibuf[ldibuflen]; +#else +char*ldibuf=alloca(ldibuflen); +#endif +len=loadquery(L_GETINFO,ldibuf,ldibuflen); +if (len < 0){ +if (errno!=ENOMEM){ +ABORT("loadquery failed"); +} +ldibuflen*=2; +continue; +} +ldi=(struct ld_info*)ldibuf; +while (ldi){ +len=ldi->ldinfo_next; +GC_add_roots_inner( +ldi->ldinfo_dataorg, +(ptr_t)(unsigned long)ldi->ldinfo_dataorg ++ldi->ldinfo_datasize, +TRUE); +ldi=len?(struct ld_info*)((char*)ldi+len):0; +} +break; +} +} +#endif +#ifdef DARWIN +#ifndef __private_extern__ +#define __private_extern__ extern +#include +#undef __private_extern__ +#else +#include +#endif +#include +STATIC const struct dyld_sections_s { +const char*seg; +const char*sect; +} GC_dyld_sections[]={ +{ SEG_DATA,SECT_DATA }, +{ SEG_DATA,"__static_data" }, +{ SEG_DATA,SECT_BSS }, +{ SEG_DATA,SECT_COMMON }, +{ SEG_DATA,"__zobj_data" }, +{ SEG_DATA,"__zobj_bss" } +}; +STATIC const char*const GC_dyld_add_sect_fmts[]={ +"__bss%u", +"__pu_bss%u", +"__zo_bss%u", +"__zo_pu_bss%u" +}; +#ifndef L2_MAX_OFILE_ALIGNMENT +#define L2_MAX_OFILE_ALIGNMENT 15 +#endif +STATIC const char*GC_dyld_name_for_hdr(const struct GC_MACH_HEADER*hdr) +{ +unsigned long i,c; +c=_dyld_image_count(); +for (i=0;i < c;i++) +if ((const struct GC_MACH_HEADER*)_dyld_get_image_header(i)==hdr) +return _dyld_get_image_name(i); +return NULL; +} +STATIC void GC_dyld_image_add(const struct GC_MACH_HEADER*hdr, +intptr_t slide) +{ +unsigned long start,end; +unsigned i,j; +const struct GC_MACH_SECTION*sec; +const char*name; +GC_has_static_roots_func callback=GC_has_static_roots; +DCL_LOCK_STATE; +if (GC_no_dls)return; +#ifdef DARWIN_DEBUG +name=GC_dyld_name_for_hdr(hdr); +#else +name=callback!=0?GC_dyld_name_for_hdr(hdr):NULL; +#endif +for (i=0;i < sizeof(GC_dyld_sections)/sizeof(GC_dyld_sections[0]);i++){ +sec=GC_GETSECTBYNAME(hdr,GC_dyld_sections[i].seg, +GC_dyld_sections[i].sect); +if (sec==NULL||sec->size < sizeof(word)) +continue; +start=slide+sec->addr; +end=start+sec->size; +LOCK(); +if (callback==0||callback(name,(void*)start,(size_t)sec->size)){ +#ifdef DARWIN_DEBUG +GC_log_printf( +"Adding section __DATA,%s at %p-%p (%lu bytes)from image %s\n", +GC_dyld_sections[i].sect,(void*)start,(void*)end, +(unsigned long)sec->size,name); +#endif +GC_add_roots_inner((ptr_t)start,(ptr_t)end,FALSE); +} +UNLOCK(); +} +for (j=0;j < sizeof(GC_dyld_add_sect_fmts)/sizeof(char*);j++){ +const char*fmt=GC_dyld_add_sect_fmts[j]; +for (i=0;i<=L2_MAX_OFILE_ALIGNMENT;i++){ +char secnam[16]; +(void)snprintf(secnam,sizeof(secnam),fmt,(unsigned)i); +secnam[sizeof(secnam)- 1]='\0'; +sec=GC_GETSECTBYNAME(hdr,SEG_DATA,secnam); +if (sec==NULL||sec->size==0) +continue; +start=slide+sec->addr; +end=start+sec->size; +#ifdef DARWIN_DEBUG +GC_log_printf("Adding on-demand section __DATA,%s at" +" %p-%p (%lu bytes)from image %s\n", +secnam,(void*)start,(void*)end, +(unsigned long)sec->size,name); +#endif +GC_add_roots((char*)start,(char*)end); +} +} +#if defined(DARWIN_DEBUG)&&!defined(NO_DEBUGGING) +LOCK(); +GC_print_static_roots(); +UNLOCK(); +#endif +} +STATIC void GC_dyld_image_remove(const struct GC_MACH_HEADER*hdr, +intptr_t slide) +{ +unsigned long start,end; +unsigned i,j; +const struct GC_MACH_SECTION*sec; +#if defined(DARWIN_DEBUG)&&!defined(NO_DEBUGGING) +DCL_LOCK_STATE; +#endif +for (i=0;i < sizeof(GC_dyld_sections)/sizeof(GC_dyld_sections[0]);i++){ +sec=GC_GETSECTBYNAME(hdr,GC_dyld_sections[i].seg, +GC_dyld_sections[i].sect); +if (sec==NULL||sec->size==0) +continue; +start=slide+sec->addr; +end=start+sec->size; +#ifdef DARWIN_DEBUG +GC_log_printf( +"Removing section __DATA,%s at %p-%p (%lu bytes)from image %s\n", +GC_dyld_sections[i].sect,(void*)start,(void*)end, +(unsigned long)sec->size,GC_dyld_name_for_hdr(hdr)); +#endif +GC_remove_roots((char*)start,(char*)end); +} +for (j=0;j < sizeof(GC_dyld_add_sect_fmts)/sizeof(char*);j++){ +const char*fmt=GC_dyld_add_sect_fmts[j]; +for (i=0;i<=L2_MAX_OFILE_ALIGNMENT;i++){ +char secnam[16]; +(void)snprintf(secnam,sizeof(secnam),fmt,(unsigned)i); +secnam[sizeof(secnam)- 1]='\0'; +sec=GC_GETSECTBYNAME(hdr,SEG_DATA,secnam); +if (sec==NULL||sec->size==0) +continue; +start=slide+sec->addr; +end=start+sec->size; +#ifdef DARWIN_DEBUG +GC_log_printf("Removing on-demand section __DATA,%s at" +" %p-%p (%lu bytes)from image %s\n",secnam, +(void*)start,(void*)end,(unsigned long)sec->size, +GC_dyld_name_for_hdr(hdr)); +#endif +GC_remove_roots((char*)start,(char*)end); +} +} +#if defined(DARWIN_DEBUG)&&!defined(NO_DEBUGGING) +LOCK(); +GC_print_static_roots(); +UNLOCK(); +#endif +} +GC_INNER void GC_register_dynamic_libraries(void) +{ +} +GC_INNER void GC_init_dyld(void) +{ +static GC_bool initialized=FALSE; +if (initialized)return; +#ifdef DARWIN_DEBUG +GC_log_printf("Registering dyld callbacks...\n"); +#endif +_dyld_register_func_for_add_image( +(void (*)(const struct mach_header*,intptr_t))GC_dyld_image_add); +_dyld_register_func_for_remove_image( +(void (*)(const struct mach_header*,intptr_t))GC_dyld_image_remove); +initialized=TRUE; +#ifdef NO_DYLD_BIND_FULLY_IMAGE +#else +if (GC_no_dls)return; +if (GETENV("DYLD_BIND_AT_LAUNCH")==0){ +#ifdef DARWIN_DEBUG +GC_log_printf("Forcing full bind of GC code...\n"); +#endif +if (!_dyld_bind_fully_image_containing_address( +(unsigned long*)GC_malloc)) +ABORT("_dyld_bind_fully_image_containing_address failed"); +} +#endif +} +#define HAVE_REGISTER_MAIN_STATIC_DATA +GC_INNER GC_bool GC_register_main_static_data(void) +{ +return FALSE; +} +#endif +#if defined(HAIKU) +#include +GC_INNER void GC_register_dynamic_libraries(void) +{ +image_info info; +int32 cookie=0; +while (get_next_image_info(0,&cookie,&info)==B_OK){ +ptr_t data=(ptr_t)info.data; +GC_add_roots_inner(data,data+info.data_size,TRUE); +} +} +#endif +#elif defined(PCR) +GC_INNER void GC_register_dynamic_libraries(void) +{ +PCR_IL_LoadedFile*p=PCR_IL_GetLastLoadedFile(); +PCR_IL_LoadedSegment*q; +while (p!=NIL&&!(p->lf_commitPoint)){ +p=p->lf_prev; +} +for (;p!=NIL;p=p->lf_prev){ +for (q=p->lf_ls;q!=NIL;q=q->ls_next){ +if ((q->ls_flags&PCR_IL_SegFlags_Traced_MASK) +==PCR_IL_SegFlags_Traced_on){ +GC_add_roots_inner((ptr_t)q->ls_addr, +(ptr_t)q->ls_addr+q->ls_bytes,TRUE); +} +} +} +} +#endif +#if!defined(HAVE_REGISTER_MAIN_STATIC_DATA)&&defined(DYNAMIC_LOADING) +GC_INNER GC_bool GC_register_main_static_data(void) +{ +return TRUE; +} +#endif +GC_API void GC_CALL GC_register_has_static_roots_callback( +GC_has_static_roots_func callback) +{ +GC_has_static_roots=callback; +} +#if defined(GC_PTHREADS)&&!defined(GC_NO_DLOPEN) +#undef GC_MUST_RESTORE_REDEFINED_DLOPEN +#if defined(dlopen)&&!defined(GC_USE_LD_WRAP) +#undef dlopen +#define GC_MUST_RESTORE_REDEFINED_DLOPEN +#endif +#ifndef USE_PROC_FOR_LIBRARIES +static void disable_gc_for_dlopen(void) +{ +DCL_LOCK_STATE; +LOCK(); +while (GC_incremental&&GC_collection_in_progress()){ +ENTER_GC(); +GC_collect_a_little_inner(1000); +EXIT_GC(); +} +++GC_dont_gc; +UNLOCK(); +} +#endif +#ifdef GC_USE_LD_WRAP +#define WRAP_DLFUNC(f)__wrap_##f +#define REAL_DLFUNC(f)__real_##f +void*REAL_DLFUNC(dlopen)(const char*,int); +#else +#define WRAP_DLFUNC(f)GC_##f +#define REAL_DLFUNC(f)f +#endif +GC_API void*WRAP_DLFUNC(dlopen)(const char*path,int mode) +{ +void*result; +#ifndef USE_PROC_FOR_LIBRARIES +disable_gc_for_dlopen(); +#endif +result=REAL_DLFUNC(dlopen)(path,mode); +#ifndef USE_PROC_FOR_LIBRARIES +GC_enable(); +#endif +return(result); +} +#ifdef GC_USE_LD_WRAP +GC_API void*GC_dlopen(const char*path,int mode) +{ +return dlopen(path,mode); +} +#endif +#ifdef GC_MUST_RESTORE_REDEFINED_DLOPEN +#define dlopen GC_dlopen +#endif +#endif +#if!defined(SN_TARGET_ORBIS)&&!defined(SN_TARGET_PSP2) +#include +#ifdef AMIGA +#ifndef __GNUC__ +#include +#else +#include +#endif +#endif +#if defined(MACOS)&&defined(__MWERKS__) +#if defined(POWERPC) +#define NONVOLATILE_GPR_COUNT 19 +struct ppc_registers { +unsigned long gprs[NONVOLATILE_GPR_COUNT]; +}; +typedef struct ppc_registers ppc_registers; +#if defined(CPPCHECK) +void getRegisters(ppc_registers*regs); +#else +asm static void getRegisters(register ppc_registers*regs) +{ +stmw r13,regs->gprs +blr +} +#endif +static void PushMacRegisters(void) +{ +ppc_registers regs; +int i; +getRegisters(®s); +for (i=0;i < NONVOLATILE_GPR_COUNT;i++) +GC_push_one(regs.gprs[i]); +} +#else +asm static void PushMacRegisters(void) +{ +sub.w #4,sp +move.l a2,(sp) +jsr GC_push_one +move.l a3,(sp) +jsr GC_push_one +move.l a4,(sp) +jsr GC_push_one +#if!__option(a6frames) +move.l a6,(sp) +jsr GC_push_one +#endif +move.l d2,(sp) +jsr GC_push_one +move.l d3,(sp) +jsr GC_push_one +move.l d4,(sp) +jsr GC_push_one +move.l d5,(sp) +jsr GC_push_one +move.l d6,(sp) +jsr GC_push_one +move.l d7,(sp) +jsr GC_push_one +add.w #4,sp +rts +} +#endif +#endif +#if defined(SPARC)||defined(IA64) +GC_INNER ptr_t GC_save_regs_ret_val=NULL; +#endif +#undef HAVE_PUSH_REGS +#if defined(USE_ASM_PUSH_REGS) +#define HAVE_PUSH_REGS +#else +#ifdef STACK_NOT_SCANNED +void GC_push_regs(void) +{ +} +#define HAVE_PUSH_REGS +#elif defined(M68K)&&defined(AMIGA) +void GC_push_regs(void) +{ +#ifdef __GNUC__ +asm("subq.w&0x4,%sp"); +asm("mov.l %a2,(%sp)");asm("jsr _GC_push_one"); +asm("mov.l %a3,(%sp)");asm("jsr _GC_push_one"); +asm("mov.l %a4,(%sp)");asm("jsr _GC_push_one"); +asm("mov.l %a5,(%sp)");asm("jsr _GC_push_one"); +asm("mov.l %a6,(%sp)");asm("jsr _GC_push_one"); +asm("mov.l %d2,(%sp)");asm("jsr _GC_push_one"); +asm("mov.l %d3,(%sp)");asm("jsr _GC_push_one"); +asm("mov.l %d4,(%sp)");asm("jsr _GC_push_one"); +asm("mov.l %d5,(%sp)");asm("jsr _GC_push_one"); +asm("mov.l %d6,(%sp)");asm("jsr _GC_push_one"); +asm("mov.l %d7,(%sp)");asm("jsr _GC_push_one"); +asm("addq.w&0x4,%sp"); +#else +GC_push_one(getreg(REG_A2)); +GC_push_one(getreg(REG_A3)); +#ifndef __SASC +GC_push_one(getreg(REG_A4)); +#endif +GC_push_one(getreg(REG_A5)); +GC_push_one(getreg(REG_A6)); +GC_push_one(getreg(REG_D2)); +GC_push_one(getreg(REG_D3)); +GC_push_one(getreg(REG_D4)); +GC_push_one(getreg(REG_D5)); +GC_push_one(getreg(REG_D6)); +GC_push_one(getreg(REG_D7)); +#endif +} +#define HAVE_PUSH_REGS +#elif defined(MACOS) +#if defined(M68K)&&defined(THINK_C)&&!defined(CPPCHECK) +#define PushMacReg(reg)move.l reg,(sp)jsr GC_push_one +void GC_push_regs(void) +{ +asm { +sub.w #4,sp;reserve space for one parameter. +PushMacReg(a2); +PushMacReg(a3); +PushMacReg(a4); +;skip a5 (globals),a6 (frame pointer),and a7 (stack pointer) +PushMacReg(d2); +PushMacReg(d3); +PushMacReg(d4); +PushMacReg(d5); +PushMacReg(d6); +PushMacReg(d7); +add.w #4,sp;fix stack. +} +} +#define HAVE_PUSH_REGS +#undef PushMacReg +#elif defined(__MWERKS__) +void GC_push_regs(void) +{ +PushMacRegisters(); +} +#define HAVE_PUSH_REGS +#endif +#endif +#endif +#if defined(HAVE_PUSH_REGS)&&defined(THREADS) +#error GC_push_regs cannot be used with threads +#undef HAVE_PUSH_REGS +#endif +#if!defined(HAVE_PUSH_REGS)&&defined(UNIX_LIKE) +#include +#ifndef NO_GETCONTEXT +#if defined(DARWIN)&&(MAC_OS_X_VERSION_MAX_ALLOWED>=1060) +#include +#else +#include +#endif +#ifdef GETCONTEXT_FPU_EXCMASK_BUG +#include +#endif +#endif +#endif +GC_ATTR_NO_SANITIZE_ADDR +GC_INNER void GC_with_callee_saves_pushed(void (*fn)(ptr_t,void*), +volatile ptr_t arg) +{ +volatile int dummy; +volatile ptr_t context=0; +#if defined(HAVE_PUSH_REGS) +GC_push_regs(); +#elif defined(__EMSCRIPTEN__) +#else +#if defined(UNIX_LIKE)&&!defined(NO_GETCONTEXT) +static signed char getcontext_works=0; +ucontext_t ctxt; +#ifdef GETCONTEXT_FPU_EXCMASK_BUG +#ifdef X86_64 +unsigned short old_fcw; +#if defined(CPPCHECK) +GC_noop1((word)&old_fcw); +#endif +__asm__ __volatile__ ("fstcw %0":"=m" (*&old_fcw)); +#else +int except_mask=fegetexcept(); +#endif +#endif +if (getcontext_works>=0){ +if (getcontext(&ctxt)< 0){ +WARN("getcontext failed:" +" using another register retrieval method...\n",0); +} else { +context=(ptr_t)&ctxt; +} +if (EXPECT(0==getcontext_works,FALSE)) +getcontext_works=context!=NULL?1:-1; +} +#ifdef GETCONTEXT_FPU_EXCMASK_BUG +#ifdef X86_64 +__asm__ __volatile__ ("fldcw %0"::"m" (*&old_fcw)); +{ +unsigned mxcsr; +__asm__ __volatile__ ("stmxcsr %0":"=m" (*&mxcsr)); +mxcsr=(mxcsr&~(FE_ALL_EXCEPT<<7))| +((old_fcw&FE_ALL_EXCEPT)<<7); +__asm__ __volatile__ ("ldmxcsr %0"::"m" (*&mxcsr)); +} +#else +if (feenableexcept(except_mask)< 0) +ABORT("feenableexcept failed"); +#endif +#endif +#if defined(SPARC)||defined(IA64) +GC_save_regs_ret_val=GC_save_regs_in_stack(); +#endif +if (NULL==context) +#endif +{ +#if defined(HAVE_BUILTIN_UNWIND_INIT) +__builtin_unwind_init(); +#elif defined(NO_CRT)&&defined(MSWIN32) +CONTEXT ctx; +RtlCaptureContext(&ctx); +#else +jmp_buf regs; +word*i=(word*)®s; +ptr_t lim=(ptr_t)(®s)+sizeof(regs); +for (;(word)i < (word)lim;i++){ +*i=0; +} +#if defined(MSWIN32)||defined(MSWINCE)||defined(UTS4)||defined(OS2)||defined(CX_UX)||defined(__CC_ARM)||defined(LINUX)||defined(EWS4800)||defined(RTEMS) +(void)setjmp(regs); +#else +(void)_setjmp(regs); +#endif +#endif +} +#endif +fn(arg,( void*)context); +GC_noop1(COVERT_DATAFLOW(&dummy)); +} +#endif +#if defined(GC_PTHREADS)&&!defined(GC_WIN32_THREADS)&&!defined(GC_DARWIN_THREADS)&&!defined(SN_TARGET_ORBIS)&&!defined(SN_TARGET_PSP2) +#ifdef NACL +#include +#include +STATIC int GC_nacl_num_gc_threads=0; +STATIC __thread int GC_nacl_thread_idx=-1; +STATIC volatile int GC_nacl_park_threads_now=0; +STATIC volatile pthread_t GC_nacl_thread_parker=-1; +GC_INNER __thread GC_thread GC_nacl_gc_thread_self=NULL; +volatile int GC_nacl_thread_parked[MAX_NACL_GC_THREADS]; +int GC_nacl_thread_used[MAX_NACL_GC_THREADS]; +#elif defined(GC_OPENBSD_UTHREADS) +#include +#else +#include +#include +#include +#include +#include +#if (!defined(AO_HAVE_load_acquire)||!defined(AO_HAVE_store_release))&&!defined(CPPCHECK) +#error AO_load_acquire and/or AO_store_release are missing; +#error please define AO_REQUIRE_CAS manually +#endif +#undef pthread_sigmask +#ifdef GC_ENABLE_SUSPEND_THREAD +static void*GC_CALLBACK suspend_self_inner(void*client_data); +#endif +#ifdef DEBUG_THREADS +#ifndef NSIG +#if defined(MAXSIG) +#define NSIG (MAXSIG+1) +#elif defined(_NSIG) +#define NSIG _NSIG +#elif defined(__SIGRTMAX) +#define NSIG (__SIGRTMAX+1) +#else +#error define NSIG +#endif +#endif +void GC_print_sig_mask(void) +{ +sigset_t blocked; +int i; +if (pthread_sigmask(SIG_BLOCK,NULL,&blocked)!=0) +ABORT("pthread_sigmask failed"); +for (i=1;i < NSIG;i++){ +if (sigismember(&blocked,i)) +GC_printf("Signal blocked:%d\n",i); +} +} +#endif +STATIC void GC_remove_allowed_signals(sigset_t*set) +{ +if (sigdelset(set,SIGINT)!=0 +||sigdelset(set,SIGQUIT)!=0 +||sigdelset(set,SIGABRT)!=0 +||sigdelset(set,SIGTERM)!=0){ +ABORT("sigdelset failed"); +} +#ifdef MPROTECT_VDB +if (sigdelset(set,SIGSEGV)!=0 +#ifdef HAVE_SIGBUS +||sigdelset(set,SIGBUS)!=0 +#endif +){ +ABORT("sigdelset failed"); +} +#endif +} +static sigset_t suspend_handler_mask; +#define THREAD_RESTARTED 0x1 +STATIC volatile AO_t GC_stop_count=0; +STATIC volatile AO_t GC_world_is_stopped=FALSE; +#if defined(GC_OSF1_THREADS)||defined(THREAD_SANITIZER)||defined(ADDRESS_SANITIZER)||defined(MEMORY_SANITIZER) +STATIC GC_bool GC_retry_signals=TRUE; +#else +STATIC GC_bool GC_retry_signals=FALSE; +#endif +#ifndef SIG_THR_RESTART +#if defined(GC_HPUX_THREADS)||defined(GC_OSF1_THREADS)||defined(GC_NETBSD_THREADS)||defined(GC_USESIGRT_SIGNALS) +#if defined(_SIGRTMIN)&&!defined(CPPCHECK) +#define SIG_THR_RESTART _SIGRTMIN+5 +#else +#define SIG_THR_RESTART SIGRTMIN+5 +#endif +#else +#define SIG_THR_RESTART SIGXCPU +#endif +#endif +#define SIGNAL_UNSET (-1) +STATIC int GC_sig_suspend=SIGNAL_UNSET; +STATIC int GC_sig_thr_restart=SIGNAL_UNSET; +GC_API void GC_CALL GC_set_suspend_signal(int sig) +{ +if (GC_is_initialized)return; +GC_sig_suspend=sig; +} +GC_API void GC_CALL GC_set_thr_restart_signal(int sig) +{ +if (GC_is_initialized)return; +GC_sig_thr_restart=sig; +} +GC_API int GC_CALL GC_get_suspend_signal(void) +{ +return GC_sig_suspend!=SIGNAL_UNSET?GC_sig_suspend:SIG_SUSPEND; +} +GC_API int GC_CALL GC_get_thr_restart_signal(void) +{ +return GC_sig_thr_restart!=SIGNAL_UNSET +?GC_sig_thr_restart:SIG_THR_RESTART; +} +#if defined(GC_EXPLICIT_SIGNALS_UNBLOCK)||!defined(NO_SIGNALS_UNBLOCK_IN_MAIN) +GC_INNER void GC_unblock_gc_signals(void) +{ +sigset_t set; +sigemptyset(&set); +GC_ASSERT(GC_sig_suspend!=SIGNAL_UNSET); +GC_ASSERT(GC_sig_thr_restart!=SIGNAL_UNSET); +sigaddset(&set,GC_sig_suspend); +sigaddset(&set,GC_sig_thr_restart); +if (pthread_sigmask(SIG_UNBLOCK,&set,NULL)!=0) +ABORT("pthread_sigmask failed"); +} +#endif +STATIC sem_t GC_suspend_ack_sem; +STATIC void GC_suspend_handler_inner(ptr_t dummy,void*context); +#ifndef NO_SA_SIGACTION +STATIC void GC_suspend_handler(int sig,siginfo_t*info GC_ATTR_UNUSED, +void*context GC_ATTR_UNUSED) +#else +STATIC void GC_suspend_handler(int sig) +#endif +{ +int old_errno=errno; +if (sig!=GC_sig_suspend){ +#if defined(GC_FREEBSD_THREADS) +if (0==sig)return; +#endif +ABORT("Bad signal in suspend_handler"); +} +#if defined(IA64)||defined(HP_PA)||defined(M68K) +GC_with_callee_saves_pushed(GC_suspend_handler_inner,NULL); +#else +{ +#ifdef NO_SA_SIGACTION +void*context=0; +#endif +GC_suspend_handler_inner(NULL,context); +} +#endif +errno=old_errno; +} +#ifdef BASE_ATOMIC_OPS_EMULATED +#define ao_load_acquire_async(p)(*(p)) +#define ao_load_async(p)ao_load_acquire_async(p) +#define ao_store_release_async(p,v)(void)(*(p)=(v)) +#define ao_store_async(p,v)ao_store_release_async(p,v) +#else +#define ao_load_acquire_async(p)AO_load_acquire(p) +#define ao_load_async(p)AO_load(p) +#define ao_store_release_async(p,v)AO_store_release(p,v) +#define ao_store_async(p,v)AO_store(p,v) +#endif +#ifdef THREAD_SANITIZER +GC_ATTR_NO_SANITIZE_THREAD +static GC_thread GC_lookup_thread_async(pthread_t id) +{ +GC_thread p=GC_threads[THREAD_TABLE_INDEX(id)]; +while (p!=NULL&&!THREAD_EQUAL(p->id,id)) +p=p->next; +return p; +} +#else +#define GC_lookup_thread_async GC_lookup_thread +#endif +GC_INLINE void GC_store_stack_ptr(GC_thread me) +{ +#ifdef SPARC +ao_store_async((volatile AO_t*)&me->stop_info.stack_ptr, +(AO_t)GC_save_regs_in_stack()); +#else +#ifdef IA64 +me->backing_store_ptr=GC_save_regs_in_stack(); +#endif +ao_store_async((volatile AO_t*)&me->stop_info.stack_ptr, +(AO_t)GC_approx_sp()); +#endif +} +STATIC void GC_suspend_handler_inner(ptr_t dummy GC_ATTR_UNUSED, +void*context GC_ATTR_UNUSED) +{ +pthread_t self=pthread_self(); +GC_thread me; +IF_CANCEL(int cancel_state;) +AO_t my_stop_count=ao_load_acquire_async(&GC_stop_count); +DISABLE_CANCEL(cancel_state); +#ifdef DEBUG_THREADS +GC_log_printf("Suspending %p\n",(void*)self); +#endif +GC_ASSERT(((word)my_stop_count&THREAD_RESTARTED)==0); +me=GC_lookup_thread_async(self); +#ifdef GC_ENABLE_SUSPEND_THREAD +if (ao_load_async(&me->suspended_ext)){ +GC_store_stack_ptr(me); +sem_post(&GC_suspend_ack_sem); +suspend_self_inner(me); +#ifdef DEBUG_THREADS +GC_log_printf("Continuing %p on GC_resume_thread\n",(void*)self); +#endif +RESTORE_CANCEL(cancel_state); +return; +} +#endif +if (((word)me->stop_info.last_stop_count&~(word)THREAD_RESTARTED) +==(word)my_stop_count){ +if (!GC_retry_signals){ +WARN("Duplicate suspend signal in thread %p\n",self); +} +RESTORE_CANCEL(cancel_state); +return; +} +GC_store_stack_ptr(me); +#ifdef THREAD_SANITIZER +{ +sigset_t set; +sigemptyset(&set); +GC_ASSERT(GC_sig_suspend!=SIGNAL_UNSET); +GC_ASSERT(GC_sig_thr_restart!=SIGNAL_UNSET); +sigaddset(&set,GC_sig_suspend); +sigaddset(&set,GC_sig_thr_restart); +if (pthread_sigmask(SIG_UNBLOCK,&set,NULL)!=0) +ABORT("pthread_sigmask failed in suspend handler"); +} +#endif +sem_post(&GC_suspend_ack_sem); +ao_store_release_async(&me->stop_info.last_stop_count,my_stop_count); +do { +sigsuspend (&suspend_handler_mask); +} while (ao_load_acquire_async(&GC_world_is_stopped) +&&ao_load_async(&GC_stop_count)==my_stop_count); +#ifdef DEBUG_THREADS +GC_log_printf("Continuing %p\n",(void*)self); +#endif +#ifndef GC_NETBSD_THREADS_WORKAROUND +if (GC_retry_signals) +#endif +{ +sem_post(&GC_suspend_ack_sem); +#ifdef GC_NETBSD_THREADS_WORKAROUND +if (GC_retry_signals) +#endif +{ +ao_store_release_async(&me->stop_info.last_stop_count, +(AO_t)((word)my_stop_count|THREAD_RESTARTED)); +} +} +RESTORE_CANCEL(cancel_state); +} +static void suspend_restart_barrier(int n_live_threads) +{ +int i; +for (i=0;i < n_live_threads;i++){ +while (0!=sem_wait(&GC_suspend_ack_sem)){ +if (errno!=EINTR) +ABORT("sem_wait failed"); +} +} +#ifdef GC_ASSERTIONS +sem_getvalue(&GC_suspend_ack_sem,&i); +GC_ASSERT(0==i); +#endif +} +static int resend_lost_signals(int n_live_threads, +int (*suspend_restart_all)(void)) +{ +#define WAIT_UNIT 3000 +#define RETRY_INTERVAL 100000 +if (n_live_threads > 0){ +unsigned long wait_usecs=0; +for (;;){ +int ack_count; +sem_getvalue(&GC_suspend_ack_sem,&ack_count); +if (ack_count==n_live_threads) +break; +if (wait_usecs > RETRY_INTERVAL){ +int newly_sent=suspend_restart_all(); +GC_COND_LOG_PRINTF("Resent %d signals after timeout\n",newly_sent); +sem_getvalue(&GC_suspend_ack_sem,&ack_count); +if (newly_sent < n_live_threads - ack_count){ +WARN("Lost some threads while stopping or starting world?!\n",0); +n_live_threads=ack_count+newly_sent; +} +wait_usecs=0; +} +#ifdef LINT2 +#undef WAIT_UNIT +#define WAIT_UNIT 1 +sched_yield(); +#elif defined(CPPCHECK) +{ +struct timespec ts; +ts.tv_sec=0; +ts.tv_nsec=WAIT_UNIT*1000; +(void)nanosleep(&ts,NULL); +} +#else +usleep(WAIT_UNIT); +#endif +wait_usecs+=WAIT_UNIT; +} +} +return n_live_threads; +} +STATIC void GC_restart_handler(int sig) +{ +#if defined(DEBUG_THREADS) +int old_errno=errno; +#endif +if (sig!=GC_sig_thr_restart) +ABORT("Bad signal in restart handler"); +#ifdef DEBUG_THREADS +GC_log_printf("In GC_restart_handler for %p\n",(void*)pthread_self()); +errno=old_errno; +#endif +} +#ifdef USE_TKILL_ON_ANDROID +EXTERN_C_BEGIN +extern int tkill(pid_t tid,int sig); +EXTERN_C_END +static int android_thread_kill(pid_t tid,int sig) +{ +int ret; +int old_errno=errno; +ret=tkill(tid,sig); +if (ret < 0){ +ret=errno; +errno=old_errno; +} +return ret; +} +#define THREAD_SYSTEM_ID(t)(t)->kernel_id +#define RAISE_SIGNAL(t,sig)android_thread_kill(THREAD_SYSTEM_ID(t),sig) +#else +#define THREAD_SYSTEM_ID(t)(t)->id +#define RAISE_SIGNAL(t,sig)pthread_kill(THREAD_SYSTEM_ID(t),sig) +#endif +#ifdef GC_ENABLE_SUSPEND_THREAD +#include +STATIC void GC_brief_async_signal_safe_sleep(void) +{ +struct timeval tv; +tv.tv_sec=0; +#if defined(GC_TIME_LIMIT)&&!defined(CPPCHECK) +tv.tv_usec=1000*GC_TIME_LIMIT/2; +#else +tv.tv_usec=1000*50/2; +#endif +(void)select(0,0,0,0,&tv); +} +static void*GC_CALLBACK suspend_self_inner(void*client_data){ +GC_thread me=(GC_thread)client_data; +while (ao_load_acquire_async(&me->suspended_ext)){ +GC_brief_async_signal_safe_sleep(); +} +return NULL; +} +GC_API void GC_CALL GC_suspend_thread(GC_SUSPEND_THREAD_ID thread){ +GC_thread t; +IF_CANCEL(int cancel_state;) +DCL_LOCK_STATE; +LOCK(); +t=GC_lookup_thread((pthread_t)thread); +if (t==NULL||t->suspended_ext){ +UNLOCK(); +return; +} +AO_store_release(&t->suspended_ext,TRUE); +if (THREAD_EQUAL((pthread_t)thread,pthread_self())){ +UNLOCK(); +(void)GC_do_blocking(suspend_self_inner,t); +return; +} +if ((t->flags&FINISHED)!=0){ +UNLOCK(); +return; +} +DISABLE_CANCEL(cancel_state); +#ifdef PARALLEL_MARK +if (GC_parallel) +GC_wait_for_reclaim(); +#endif +if (GC_manual_vdb){ +GC_acquire_dirty_lock(); +} +switch (RAISE_SIGNAL(t,GC_sig_suspend)){ +case 0: +break; +default: +ABORT("pthread_kill failed"); +} +GC_ASSERT(GC_thr_initialized); +while (sem_wait(&GC_suspend_ack_sem)!=0){ +if (errno!=EINTR) +ABORT("sem_wait for handler failed (suspend_self)"); +} +if (GC_manual_vdb) +GC_release_dirty_lock(); +RESTORE_CANCEL(cancel_state); +UNLOCK(); +} +GC_API void GC_CALL GC_resume_thread(GC_SUSPEND_THREAD_ID thread){ +GC_thread t; +DCL_LOCK_STATE; +LOCK(); +t=GC_lookup_thread((pthread_t)thread); +if (t!=NULL) +AO_store(&t->suspended_ext,FALSE); +UNLOCK(); +} +GC_API int GC_CALL GC_is_thread_suspended(GC_SUSPEND_THREAD_ID thread){ +GC_thread t; +int is_suspended=0; +DCL_LOCK_STATE; +LOCK(); +t=GC_lookup_thread((pthread_t)thread); +if (t!=NULL&&t->suspended_ext) +is_suspended=(int)TRUE; +UNLOCK(); +return is_suspended; +} +#endif +#undef ao_load_acquire_async +#undef ao_load_async +#undef ao_store_async +#undef ao_store_release_async +#endif +#ifdef IA64 +#define IF_IA64(x)x +#else +#define IF_IA64(x) +#endif +GC_INNER void GC_push_all_stacks(void) +{ +GC_bool found_me=FALSE; +size_t nthreads=0; +int i; +GC_thread p; +ptr_t lo,hi; +IF_IA64(ptr_t bs_lo;ptr_t bs_hi;) +struct GC_traced_stack_sect_s*traced_stack_sect; +pthread_t self=pthread_self(); +word total_size=0; +if (!EXPECT(GC_thr_initialized,TRUE)) +GC_thr_init(); +#ifdef DEBUG_THREADS +GC_log_printf("Pushing stacks from thread %p\n",(void*)self); +#endif +for (i=0;i < THREAD_TABLE_SZ;i++){ +for (p=GC_threads[i];p!=0;p=p->next){ +if (p->flags&FINISHED)continue; +++nthreads; +traced_stack_sect=p->traced_stack_sect; +if (THREAD_EQUAL(p->id,self)){ +GC_ASSERT(!p->thread_blocked); +#ifdef SPARC +lo=(ptr_t)GC_save_regs_in_stack(); +#else +lo=GC_approx_sp(); +#endif +found_me=TRUE; +IF_IA64(bs_hi=(ptr_t)GC_save_regs_in_stack();) +} else { +lo=(ptr_t)AO_load((volatile AO_t*)&p->stop_info.stack_ptr); +IF_IA64(bs_hi=p->backing_store_ptr;) +if (traced_stack_sect!=NULL +&&traced_stack_sect->saved_stack_ptr==lo){ +traced_stack_sect=traced_stack_sect->prev; +} +} +if ((p->flags&MAIN_THREAD)==0){ +hi=p->stack_end; +IF_IA64(bs_lo=p->backing_store_end); +} else { +hi=GC_stackbottom; +IF_IA64(bs_lo=BACKING_STORE_BASE;) +} +#ifdef DEBUG_THREADS +GC_log_printf("Stack for thread %p=[%p,%p)\n", +(void*)p->id,(void*)lo,(void*)hi); +#endif +if (0==lo)ABORT("GC_push_all_stacks:sp not set!"); +if (p->altstack!=NULL&&(word)p->altstack<=(word)lo +&&(word)lo<=(word)p->altstack+p->altstack_size){ +hi=p->altstack+p->altstack_size; +} +GC_push_all_stack_sections(lo,hi,traced_stack_sect); +#ifdef STACK_GROWS_UP +total_size+=lo - hi; +#else +total_size+=hi - lo; +#endif +#ifdef NACL +GC_push_all_stack((ptr_t)p->stop_info.reg_storage, +(ptr_t)(p->stop_info.reg_storage+NACL_GC_REG_STORAGE_SIZE)); +total_size+=NACL_GC_REG_STORAGE_SIZE*sizeof(ptr_t); +#endif +#ifdef IA64 +#ifdef DEBUG_THREADS +GC_log_printf("Reg stack for thread %p=[%p,%p)\n", +(void*)p->id,(void*)bs_lo,(void*)bs_hi); +#endif +GC_push_all_register_sections(bs_lo,bs_hi, +THREAD_EQUAL(p->id,self), +traced_stack_sect); +total_size+=bs_hi - bs_lo; +#endif +} +} +GC_VERBOSE_LOG_PRINTF("Pushed %d thread stacks\n",(int)nthreads); +if (!found_me&&!GC_in_thread_creation) +ABORT("Collecting from unknown thread"); +GC_total_stacksize=total_size; +} +#ifdef DEBUG_THREADS +pthread_t GC_stopping_thread; +int GC_stopping_pid=0; +#endif +STATIC int GC_suspend_all(void) +{ +int n_live_threads=0; +int i; +#ifndef NACL +GC_thread p; +#ifndef GC_OPENBSD_UTHREADS +int result; +#endif +pthread_t self=pthread_self(); +for (i=0;i < THREAD_TABLE_SZ;i++){ +for (p=GC_threads[i];p!=0;p=p->next){ +if (!THREAD_EQUAL(p->id,self)){ +if ((p->flags&FINISHED)!=0)continue; +if (p->thread_blocked)continue; +#ifndef GC_OPENBSD_UTHREADS +#ifdef GC_ENABLE_SUSPEND_THREAD +if (p->suspended_ext)continue; +#endif +if (AO_load(&p->stop_info.last_stop_count)==GC_stop_count) +continue; +n_live_threads++; +#endif +#ifdef DEBUG_THREADS +GC_log_printf("Sending suspend signal to %p\n",(void*)p->id); +#endif +#ifdef GC_OPENBSD_UTHREADS +{ +stack_t stack; +GC_acquire_dirty_lock(); +if (pthread_suspend_np(p->id)!=0) +ABORT("pthread_suspend_np failed"); +GC_release_dirty_lock(); +if (pthread_stackseg_np(p->id,&stack)) +ABORT("pthread_stackseg_np failed"); +p->stop_info.stack_ptr=(ptr_t)stack.ss_sp - stack.ss_size; +if (GC_on_thread_event) +GC_on_thread_event(GC_EVENT_THREAD_SUSPENDED, +(void*)p->id); +} +#else +result=RAISE_SIGNAL(p,GC_sig_suspend); +switch(result){ +case ESRCH: +n_live_threads--; +break; +case 0: +if (GC_on_thread_event) +GC_on_thread_event(GC_EVENT_THREAD_SUSPENDED, +(void*)(word)THREAD_SYSTEM_ID(p)); +break; +default: +ABORT_ARG1("pthread_kill failed at suspend", +":errcode=%d",result); +} +#endif +} +} +} +#else +#ifndef NACL_PARK_WAIT_NANOSECONDS +#define NACL_PARK_WAIT_NANOSECONDS (100*1000) +#endif +#define NANOS_PER_SECOND (1000UL*1000*1000) +unsigned long num_sleeps=0; +#ifdef DEBUG_THREADS +GC_log_printf("pthread_stop_world:num_threads=%d\n", +GC_nacl_num_gc_threads - 1); +#endif +GC_nacl_thread_parker=pthread_self(); +GC_nacl_park_threads_now=1; +if (GC_manual_vdb) +GC_acquire_dirty_lock(); +while (1){ +int num_threads_parked=0; +struct timespec ts; +int num_used=0; +for (i=0;i < MAX_NACL_GC_THREADS +&&num_used < GC_nacl_num_gc_threads;i++){ +if (GC_nacl_thread_used[i]==1){ +num_used++; +if (GC_nacl_thread_parked[i]==1){ +num_threads_parked++; +if (GC_on_thread_event) +GC_on_thread_event(GC_EVENT_THREAD_SUSPENDED,(void*)(word)i); +} +} +} +if (num_threads_parked>=GC_nacl_num_gc_threads - 1) +break; +ts.tv_sec=0; +ts.tv_nsec=NACL_PARK_WAIT_NANOSECONDS; +#ifdef DEBUG_THREADS +GC_log_printf("Sleep waiting for %d threads to park...\n", +GC_nacl_num_gc_threads - num_threads_parked - 1); +#endif +nanosleep(&ts,0); +if (++num_sleeps > NANOS_PER_SECOND/NACL_PARK_WAIT_NANOSECONDS){ +WARN("GC appears stalled waiting for %" WARN_PRIdPTR +" threads to park...\n", +GC_nacl_num_gc_threads - num_threads_parked - 1); +num_sleeps=0; +} +} +if (GC_manual_vdb) +GC_release_dirty_lock(); +#endif +return n_live_threads; +} +GC_INNER void GC_stop_world(void) +{ +#if!defined(GC_OPENBSD_UTHREADS)&&!defined(NACL) +int n_live_threads; +#endif +GC_ASSERT(I_HOLD_LOCK()); +#ifdef DEBUG_THREADS +GC_stopping_thread=pthread_self(); +GC_stopping_pid=getpid(); +GC_log_printf("Stopping the world from %p\n",(void*)GC_stopping_thread); +#endif +#ifdef PARALLEL_MARK +if (GC_parallel){ +GC_acquire_mark_lock(); +GC_ASSERT(GC_fl_builder_count==0); +} +#endif +#if defined(GC_OPENBSD_UTHREADS)||defined(NACL) +(void)GC_suspend_all(); +#else +AO_store(&GC_stop_count, +(AO_t)((word)GC_stop_count+(THREAD_RESTARTED+1))); +if (GC_manual_vdb){ +GC_acquire_dirty_lock(); +} +AO_store_release(&GC_world_is_stopped,TRUE); +n_live_threads=GC_suspend_all(); +if (GC_retry_signals) +n_live_threads=resend_lost_signals(n_live_threads,GC_suspend_all); +suspend_restart_barrier(n_live_threads); +if (GC_manual_vdb) +GC_release_dirty_lock(); +#endif +#ifdef PARALLEL_MARK +if (GC_parallel) +GC_release_mark_lock(); +#endif +#ifdef DEBUG_THREADS +GC_log_printf("World stopped from %p\n",(void*)pthread_self()); +GC_stopping_thread=0; +#endif +} +#ifdef NACL +#if defined(__x86_64__) +#define NACL_STORE_REGS()do { __asm__ __volatile__ ("push %rbx");__asm__ __volatile__ ("push %rbp");__asm__ __volatile__ ("push %r12");__asm__ __volatile__ ("push %r13");__asm__ __volatile__ ("push %r14");__asm__ __volatile__ ("push %r15");__asm__ __volatile__ ("mov %%esp,%0":"=m" (GC_nacl_gc_thread_self->stop_info.stack_ptr));BCOPY(GC_nacl_gc_thread_self->stop_info.stack_ptr,GC_nacl_gc_thread_self->stop_info.reg_storage,NACL_GC_REG_STORAGE_SIZE*sizeof(ptr_t));__asm__ __volatile__ ("naclasp $48,%r15");} while (0) +#elif defined(__i386__) +#define NACL_STORE_REGS()do { __asm__ __volatile__ ("push %ebx");__asm__ __volatile__ ("push %ebp");__asm__ __volatile__ ("push %esi");__asm__ __volatile__ ("push %edi");__asm__ __volatile__ ("mov %%esp,%0":"=m" (GC_nacl_gc_thread_self->stop_info.stack_ptr));BCOPY(GC_nacl_gc_thread_self->stop_info.stack_ptr,GC_nacl_gc_thread_self->stop_info.reg_storage,NACL_GC_REG_STORAGE_SIZE*sizeof(ptr_t));__asm__ __volatile__ ("add $16,%esp");} while (0) +#elif defined(__arm__) +#define NACL_STORE_REGS()do { __asm__ __volatile__ ("push {r4-r8,r10-r12,lr}");__asm__ __volatile__ ("mov r0,%0"::"r" (&GC_nacl_gc_thread_self->stop_info.stack_ptr));__asm__ __volatile__ ("bic r0,r0,#0xc0000000");__asm__ __volatile__ ("str sp,[r0]");BCOPY(GC_nacl_gc_thread_self->stop_info.stack_ptr,GC_nacl_gc_thread_self->stop_info.reg_storage,NACL_GC_REG_STORAGE_SIZE*sizeof(ptr_t));__asm__ __volatile__ ("add sp,sp,#40");__asm__ __volatile__ ("bic sp,sp,#0xc0000000");} while (0) +#else +#error TODO Please port NACL_STORE_REGS +#endif +GC_API_OSCALL void nacl_pre_syscall_hook(void) +{ +if (GC_nacl_thread_idx!=-1){ +NACL_STORE_REGS(); +GC_nacl_gc_thread_self->stop_info.stack_ptr=GC_approx_sp(); +GC_nacl_thread_parked[GC_nacl_thread_idx]=1; +} +} +GC_API_OSCALL void __nacl_suspend_thread_if_needed(void) +{ +if (GC_nacl_park_threads_now){ +pthread_t self=pthread_self(); +if (GC_nacl_thread_parker==self) +return; +if (GC_nacl_thread_idx < 0) +return; +if (!GC_nacl_thread_parked[GC_nacl_thread_idx]){ +NACL_STORE_REGS(); +GC_nacl_gc_thread_self->stop_info.stack_ptr=GC_approx_sp(); +} +GC_nacl_thread_parked[GC_nacl_thread_idx]=1; +while (GC_nacl_park_threads_now){ +} +GC_nacl_thread_parked[GC_nacl_thread_idx]=0; +BZERO(GC_nacl_gc_thread_self->stop_info.reg_storage, +NACL_GC_REG_STORAGE_SIZE*sizeof(ptr_t)); +} +} +GC_API_OSCALL void nacl_post_syscall_hook(void) +{ +__nacl_suspend_thread_if_needed(); +if (GC_nacl_thread_idx!=-1){ +GC_nacl_thread_parked[GC_nacl_thread_idx]=0; +} +} +STATIC GC_bool GC_nacl_thread_parking_inited=FALSE; +STATIC pthread_mutex_t GC_nacl_thread_alloc_lock=PTHREAD_MUTEX_INITIALIZER; +struct nacl_irt_blockhook { +int (*register_block_hooks)(void (*pre)(void),void (*post)(void)); +}; +EXTERN_C_BEGIN +extern size_t nacl_interface_query(const char*interface_ident, +void*table,size_t tablesize); +EXTERN_C_END +GC_INNER void GC_nacl_initialize_gc_thread(void) +{ +int i; +static struct nacl_irt_blockhook gc_hook; +pthread_mutex_lock(&GC_nacl_thread_alloc_lock); +if (!EXPECT(GC_nacl_thread_parking_inited,TRUE)){ +BZERO(GC_nacl_thread_parked,sizeof(GC_nacl_thread_parked)); +BZERO(GC_nacl_thread_used,sizeof(GC_nacl_thread_used)); +nacl_interface_query("nacl-irt-blockhook-0.1", +&gc_hook,sizeof(gc_hook)); +gc_hook.register_block_hooks(nacl_pre_syscall_hook, +nacl_post_syscall_hook); +GC_nacl_thread_parking_inited=TRUE; +} +GC_ASSERT(GC_nacl_num_gc_threads<=MAX_NACL_GC_THREADS); +for (i=0;i < MAX_NACL_GC_THREADS;i++){ +if (GC_nacl_thread_used[i]==0){ +GC_nacl_thread_used[i]=1; +GC_nacl_thread_idx=i; +GC_nacl_num_gc_threads++; +break; +} +} +pthread_mutex_unlock(&GC_nacl_thread_alloc_lock); +} +GC_INNER void GC_nacl_shutdown_gc_thread(void) +{ +pthread_mutex_lock(&GC_nacl_thread_alloc_lock); +GC_ASSERT(GC_nacl_thread_idx>=0); +GC_ASSERT(GC_nacl_thread_idx < MAX_NACL_GC_THREADS); +GC_ASSERT(GC_nacl_thread_used[GC_nacl_thread_idx]!=0); +GC_nacl_thread_used[GC_nacl_thread_idx]=0; +GC_nacl_thread_idx=-1; +GC_nacl_num_gc_threads--; +pthread_mutex_unlock(&GC_nacl_thread_alloc_lock); +} +#else +STATIC int GC_restart_all(void) +{ +int n_live_threads=0; +int i; +pthread_t self=pthread_self(); +GC_thread p; +#ifndef GC_OPENBSD_UTHREADS +int result; +#endif +for (i=0;i < THREAD_TABLE_SZ;i++){ +for (p=GC_threads[i];p!=NULL;p=p->next){ +if (!THREAD_EQUAL(p->id,self)){ +if ((p->flags&FINISHED)!=0)continue; +if (p->thread_blocked)continue; +#ifndef GC_OPENBSD_UTHREADS +#ifdef GC_ENABLE_SUSPEND_THREAD +if (p->suspended_ext)continue; +#endif +if (GC_retry_signals +&&AO_load(&p->stop_info.last_stop_count) +==(AO_t)((word)GC_stop_count|THREAD_RESTARTED)) +continue; +n_live_threads++; +#endif +#ifdef DEBUG_THREADS +GC_log_printf("Sending restart signal to %p\n",(void*)p->id); +#endif +#ifdef GC_OPENBSD_UTHREADS +if (pthread_resume_np(p->id)!=0) +ABORT("pthread_resume_np failed"); +if (GC_on_thread_event) +GC_on_thread_event(GC_EVENT_THREAD_UNSUSPENDED,(void*)p->id); +#else +result=RAISE_SIGNAL(p,GC_sig_thr_restart); +switch(result){ +case ESRCH: +n_live_threads--; +break; +case 0: +if (GC_on_thread_event) +GC_on_thread_event(GC_EVENT_THREAD_UNSUSPENDED, +(void*)(word)THREAD_SYSTEM_ID(p)); +break; +default: +ABORT_ARG1("pthread_kill failed at resume", +":errcode=%d",result); +} +#endif +} +} +} +return n_live_threads; +} +#endif +GC_INNER void GC_start_world(void) +{ +#ifndef NACL +int n_live_threads; +GC_ASSERT(I_HOLD_LOCK()); +#ifdef DEBUG_THREADS +GC_log_printf("World starting\n"); +#endif +#ifndef GC_OPENBSD_UTHREADS +AO_store_release(&GC_world_is_stopped,FALSE); +#endif +n_live_threads=GC_restart_all(); +#ifdef GC_OPENBSD_UTHREADS +(void)n_live_threads; +#elif defined(GC_NETBSD_THREADS_WORKAROUND) +if (GC_retry_signals) +n_live_threads=resend_lost_signals(n_live_threads,GC_restart_all); +suspend_restart_barrier(n_live_threads); +#else +if (GC_retry_signals){ +n_live_threads=resend_lost_signals(n_live_threads,GC_restart_all); +suspend_restart_barrier(n_live_threads); +} +#endif +#ifdef DEBUG_THREADS +GC_log_printf("World started\n"); +#endif +#else +#ifdef DEBUG_THREADS +GC_log_printf("World starting...\n"); +#endif +GC_nacl_park_threads_now=0; +if (GC_on_thread_event) +GC_on_thread_event(GC_EVENT_THREAD_UNSUSPENDED,NULL); +#endif +} +GC_INNER void GC_stop_init(void) +{ +#if!defined(GC_OPENBSD_UTHREADS)&&!defined(NACL) +struct sigaction act; +char*str; +if (SIGNAL_UNSET==GC_sig_suspend) +GC_sig_suspend=SIG_SUSPEND; +if (SIGNAL_UNSET==GC_sig_thr_restart) +GC_sig_thr_restart=SIG_THR_RESTART; +if (GC_sig_suspend==GC_sig_thr_restart) +ABORT("Cannot use same signal for thread suspend and resume"); +if (sem_init(&GC_suspend_ack_sem,GC_SEM_INIT_PSHARED,0)!=0) +ABORT("sem_init failed"); +#ifdef SA_RESTART +act.sa_flags=SA_RESTART +#else +act.sa_flags=0 +#endif +#ifndef NO_SA_SIGACTION +|SA_SIGINFO +#endif +; +if (sigfillset(&act.sa_mask)!=0){ +ABORT("sigfillset failed"); +} +#ifdef GC_RTEMS_PTHREADS +if(sigprocmask(SIG_UNBLOCK,&act.sa_mask,NULL)!=0){ +ABORT("sigprocmask failed"); +} +#endif +GC_remove_allowed_signals(&act.sa_mask); +#ifndef NO_SA_SIGACTION +act.sa_sigaction=GC_suspend_handler; +#else +act.sa_handler=GC_suspend_handler; +#endif +if (sigaction(GC_sig_suspend,&act,NULL)!=0){ +ABORT("Cannot set SIG_SUSPEND handler"); +} +#ifndef NO_SA_SIGACTION +act.sa_flags&=~SA_SIGINFO; +#endif +act.sa_handler=GC_restart_handler; +if (sigaction(GC_sig_thr_restart,&act,NULL)!=0){ +ABORT("Cannot set SIG_THR_RESTART handler"); +} +if (sigfillset(&suspend_handler_mask)!=0)ABORT("sigfillset failed"); +GC_remove_allowed_signals(&suspend_handler_mask); +if (sigdelset(&suspend_handler_mask,GC_sig_thr_restart)!=0) +ABORT("sigdelset failed"); +str=GETENV("GC_RETRY_SIGNALS"); +if (str!=NULL){ +if (*str=='0'&&*(str+1)=='\0'){ +GC_retry_signals=FALSE; +} else { +GC_retry_signals=TRUE; +} +} +if (GC_retry_signals){ +GC_COND_LOG_PRINTF( +"Will retry suspend and restart signals if necessary\n"); +} +#ifndef NO_SIGNALS_UNBLOCK_IN_MAIN +GC_unblock_gc_signals(); +#endif +#endif +} +#endif +#if defined(GC_PTHREADS)&&!defined(GC_WIN32_THREADS) +#include +#include +#include +#include +#include +#include +#if!defined(SN_TARGET_ORBIS)&&!defined(SN_TARGET_PSP2) +#if!defined(GC_RTEMS_PTHREADS) +#include +#endif +#include +#include +#include +#include +#endif +#include +#if defined(GC_DARWIN_THREADS) +#ifndef GC_DARWIN_SEMAPHORE_H +#define GC_DARWIN_SEMAPHORE_H +#if!defined(GC_DARWIN_THREADS) +#error darwin_semaphore.h included with GC_DARWIN_THREADS not defined +#endif +#ifdef __cplusplus +extern "C" { +#endif +typedef struct { +pthread_mutex_t mutex; +pthread_cond_t cond; +int value; +} sem_t; +GC_INLINE int sem_init(sem_t*sem,int pshared,int value){ +if (pshared!=0){ +errno=EPERM; +return -1; +} +sem->value=value; +if (pthread_mutex_init(&sem->mutex,NULL)!=0) +return -1; +if (pthread_cond_init(&sem->cond,NULL)!=0){ +(void)pthread_mutex_destroy(&sem->mutex); +return -1; +} +return 0; +} +GC_INLINE int sem_post(sem_t*sem){ +if (pthread_mutex_lock(&sem->mutex)!=0) +return -1; +sem->value++; +if (pthread_cond_signal(&sem->cond)!=0){ +(void)pthread_mutex_unlock(&sem->mutex); +return -1; +} +return pthread_mutex_unlock(&sem->mutex)!=0?-1:0; +} +GC_INLINE int sem_wait(sem_t*sem){ +if (pthread_mutex_lock(&sem->mutex)!=0) +return -1; +while (sem->value==0){ +if (pthread_cond_wait(&sem->cond,&sem->mutex)!=0){ +(void)pthread_mutex_unlock(&sem->mutex); +return -1; +} +} +sem->value--; +return pthread_mutex_unlock(&sem->mutex)!=0?-1:0; +} +GC_INLINE int sem_destroy(sem_t*sem){ +return pthread_cond_destroy(&sem->cond)!=0 +||pthread_mutex_destroy(&sem->mutex)!=0?-1:0; +} +#ifdef __cplusplus +} +#endif +#endif +#else +#include +#endif +#if defined(GC_DARWIN_THREADS)||defined(GC_FREEBSD_THREADS) +#include +#endif +#if defined(GC_NETBSD_THREADS)||defined(GC_OPENBSD_THREADS) +#include +#include +#endif +#if!defined(USE_SPIN_LOCK) +GC_INNER pthread_mutex_t GC_allocate_ml=PTHREAD_MUTEX_INITIALIZER; +#endif +#ifdef GC_ASSERTIONS +GC_INNER unsigned long GC_lock_holder=NO_THREAD; +#endif +#if defined(GC_DGUX386_THREADS) +#include +#include +typedef unsigned int sem_t; +#endif +#undef pthread_create +#ifndef GC_NO_PTHREAD_SIGMASK +#undef pthread_sigmask +#endif +#ifndef GC_NO_PTHREAD_CANCEL +#undef pthread_cancel +#endif +#ifdef GC_HAVE_PTHREAD_EXIT +#undef pthread_exit +#endif +#undef pthread_join +#undef pthread_detach +#if defined(GC_OSF1_THREADS)&&defined(_PTHREAD_USE_MANGLED_NAMES_)&&!defined(_PTHREAD_USE_PTDNAM_) +#define pthread_create __pthread_create +#define pthread_join __pthread_join +#define pthread_detach __pthread_detach +#ifndef GC_NO_PTHREAD_CANCEL +#define pthread_cancel __pthread_cancel +#endif +#ifdef GC_HAVE_PTHREAD_EXIT +#define pthread_exit __pthread_exit +#endif +#endif +#ifdef GC_USE_LD_WRAP +#define WRAP_FUNC(f)__wrap_##f +#define REAL_FUNC(f)__real_##f +int REAL_FUNC(pthread_create)(pthread_t*, +GC_PTHREAD_CREATE_CONST pthread_attr_t*, +void*(*start_routine)(void*),void*); +int REAL_FUNC(pthread_join)(pthread_t,void**); +int REAL_FUNC(pthread_detach)(pthread_t); +#ifndef GC_NO_PTHREAD_SIGMASK +int REAL_FUNC(pthread_sigmask)(int,const sigset_t*,sigset_t*); +#endif +#ifndef GC_NO_PTHREAD_CANCEL +int REAL_FUNC(pthread_cancel)(pthread_t); +#endif +#ifdef GC_HAVE_PTHREAD_EXIT +void REAL_FUNC(pthread_exit)(void*)GC_PTHREAD_EXIT_ATTRIBUTE; +#endif +#else +#ifdef GC_USE_DLOPEN_WRAP +#include +#define WRAP_FUNC(f)f +#define REAL_FUNC(f)GC_real_##f +typedef int (*GC_pthread_create_t)(pthread_t*, +GC_PTHREAD_CREATE_CONST pthread_attr_t*, +void*(*)(void*),void*); +static GC_pthread_create_t REAL_FUNC(pthread_create); +#ifndef GC_NO_PTHREAD_SIGMASK +typedef int (*GC_pthread_sigmask_t)(int,const sigset_t*, +sigset_t*); +static GC_pthread_sigmask_t REAL_FUNC(pthread_sigmask); +#endif +typedef int (*GC_pthread_join_t)(pthread_t,void**); +static GC_pthread_join_t REAL_FUNC(pthread_join); +typedef int (*GC_pthread_detach_t)(pthread_t); +static GC_pthread_detach_t REAL_FUNC(pthread_detach); +#ifndef GC_NO_PTHREAD_CANCEL +typedef int (*GC_pthread_cancel_t)(pthread_t); +static GC_pthread_cancel_t REAL_FUNC(pthread_cancel); +#endif +#ifdef GC_HAVE_PTHREAD_EXIT +typedef void (*GC_pthread_exit_t)(void*)GC_PTHREAD_EXIT_ATTRIBUTE; +static GC_pthread_exit_t REAL_FUNC(pthread_exit); +#endif +#else +#define WRAP_FUNC(f)GC_##f +#if!defined(GC_DGUX386_THREADS) +#define REAL_FUNC(f)f +#else +#define REAL_FUNC(f)__d10_##f +#endif +#endif +#endif +#if defined(GC_USE_LD_WRAP)||defined(GC_USE_DLOPEN_WRAP) +GC_API int GC_pthread_create(pthread_t*t, +GC_PTHREAD_CREATE_CONST pthread_attr_t*a, +void*(*fn)(void*),void*arg) +{ +return pthread_create(t,a,fn,arg); +} +#ifndef GC_NO_PTHREAD_SIGMASK +GC_API int GC_pthread_sigmask(int how,const sigset_t*mask, +sigset_t*old) +{ +return pthread_sigmask(how,mask,old); +} +#endif +GC_API int GC_pthread_join(pthread_t t,void**res) +{ +return pthread_join(t,res); +} +GC_API int GC_pthread_detach(pthread_t t) +{ +return pthread_detach(t); +} +#ifndef GC_NO_PTHREAD_CANCEL +GC_API int GC_pthread_cancel(pthread_t t) +{ +return pthread_cancel(t); +} +#endif +#ifdef GC_HAVE_PTHREAD_EXIT +GC_API GC_PTHREAD_EXIT_ATTRIBUTE void GC_pthread_exit(void*retval) +{ +pthread_exit(retval); +} +#endif +#endif +#ifdef GC_USE_DLOPEN_WRAP +STATIC GC_bool GC_syms_initialized=FALSE; +STATIC void GC_init_real_syms(void) +{ +void*dl_handle; +if (GC_syms_initialized)return; +#ifdef RTLD_NEXT +dl_handle=RTLD_NEXT; +#else +dl_handle=dlopen("libpthread.so.0",RTLD_LAZY); +if (NULL==dl_handle){ +dl_handle=dlopen("libpthread.so",RTLD_LAZY); +} +if (NULL==dl_handle)ABORT("Couldn't open libpthread"); +#endif +REAL_FUNC(pthread_create)=(GC_pthread_create_t)(word) +dlsym(dl_handle,"pthread_create"); +#ifdef RTLD_NEXT +if (REAL_FUNC(pthread_create)==0) +ABORT("pthread_create not found" +" (probably -lgc is specified after -lpthread)"); +#endif +#ifndef GC_NO_PTHREAD_SIGMASK +REAL_FUNC(pthread_sigmask)=(GC_pthread_sigmask_t)(word) +dlsym(dl_handle,"pthread_sigmask"); +#endif +REAL_FUNC(pthread_join)=(GC_pthread_join_t)(word) +dlsym(dl_handle,"pthread_join"); +REAL_FUNC(pthread_detach)=(GC_pthread_detach_t)(word) +dlsym(dl_handle,"pthread_detach"); +#ifndef GC_NO_PTHREAD_CANCEL +REAL_FUNC(pthread_cancel)=(GC_pthread_cancel_t)(word) +dlsym(dl_handle,"pthread_cancel"); +#endif +#ifdef GC_HAVE_PTHREAD_EXIT +REAL_FUNC(pthread_exit)=(GC_pthread_exit_t)(word) +dlsym(dl_handle,"pthread_exit"); +#endif +GC_syms_initialized=TRUE; +} +#define INIT_REAL_SYMS()if (EXPECT(GC_syms_initialized,TRUE)){} else GC_init_real_syms() +#define ASSERT_SYMS_INITIALIZED()GC_ASSERT(GC_syms_initialized) +#else +#define INIT_REAL_SYMS()(void)0 +#define ASSERT_SYMS_INITIALIZED()GC_ASSERT(parallel_initialized) +#endif +static GC_bool parallel_initialized=FALSE; +#ifndef GC_ALWAYS_MULTITHREADED +GC_INNER GC_bool GC_need_to_lock=FALSE; +#endif +STATIC int GC_nprocs=1; +#ifdef THREAD_LOCAL_ALLOC +GC_INNER void GC_mark_thread_local_free_lists(void) +{ +int i; +GC_thread p; +for (i=0;i < THREAD_TABLE_SZ;++i){ +for (p=GC_threads[i];0!=p;p=p->next){ +if (!(p->flags&FINISHED)) +GC_mark_thread_local_fls_for(&(p->tlfs)); +} +} +} +#if defined(GC_ASSERTIONS) +void GC_check_tls(void) +{ +int i; +GC_thread p; +for (i=0;i < THREAD_TABLE_SZ;++i){ +for (p=GC_threads[i];0!=p;p=p->next){ +if (!(p->flags&FINISHED)) +GC_check_tls_for(&(p->tlfs)); +} +} +#if defined(USE_CUSTOM_SPECIFIC) +if (GC_thread_key!=0) +GC_check_tsd_marks(GC_thread_key); +#endif +} +#endif +#endif +#ifdef PARALLEL_MARK +#ifndef MAX_MARKERS +#define MAX_MARKERS 16 +#endif +static ptr_t marker_sp[MAX_MARKERS - 1]={0}; +#ifdef IA64 +static ptr_t marker_bsp[MAX_MARKERS - 1]={0}; +#endif +#if defined(GC_DARWIN_THREADS)&&!defined(GC_NO_THREADS_DISCOVERY) +static mach_port_t marker_mach_threads[MAX_MARKERS - 1]={0}; +GC_INNER GC_bool GC_is_mach_marker(thread_act_t thread) +{ +int i; +for (i=0;i < GC_markers_m1;i++){ +if (marker_mach_threads[i]==thread) +return TRUE; +} +return FALSE; +} +#endif +#ifdef HAVE_PTHREAD_SETNAME_NP_WITH_TID_AND_ARG +static void set_marker_thread_name(unsigned id) +{ +int err=pthread_setname_np(pthread_self(),"GC-marker-%zu", +(void*)(size_t)id); +if (err!=0) +WARN("pthread_setname_np failed,errno=%" WARN_PRIdPTR "\n",err); +} +#elif defined(HAVE_PTHREAD_SETNAME_NP_WITH_TID)||defined(HAVE_PTHREAD_SETNAME_NP_WITHOUT_TID) +static void set_marker_thread_name(unsigned id) +{ +char name_buf[16]; +int len=sizeof("GC-marker-")- 1; +BCOPY("GC-marker-",name_buf,len); +if (id>=10) +name_buf[len++]=(char)('0'+(id/10)% 10); +name_buf[len]=(char)('0'+id % 10); +name_buf[len+1]='\0'; +#ifdef HAVE_PTHREAD_SETNAME_NP_WITHOUT_TID +(void)pthread_setname_np(name_buf); +#else +if (pthread_setname_np(pthread_self(),name_buf)!=0) +WARN("pthread_setname_np failed\n",0); +#endif +} +#else +#define set_marker_thread_name(id)(void)(id) +#endif +STATIC void*GC_mark_thread(void*id) +{ +word my_mark_no=0; +IF_CANCEL(int cancel_state;) +if ((word)id==GC_WORD_MAX)return 0; +DISABLE_CANCEL(cancel_state); +set_marker_thread_name((unsigned)(word)id); +marker_sp[(word)id]=GC_approx_sp(); +#ifdef IA64 +marker_bsp[(word)id]=GC_save_regs_in_stack(); +#endif +#if defined(GC_DARWIN_THREADS)&&!defined(GC_NO_THREADS_DISCOVERY) +marker_mach_threads[(word)id]=mach_thread_self(); +#endif +GC_acquire_mark_lock(); +if (0==--GC_fl_builder_count) +GC_notify_all_builder(); +for (;;++my_mark_no){ +if (my_mark_no < GC_mark_no||my_mark_no > GC_mark_no+2){ +my_mark_no=GC_mark_no; +} +#ifdef DEBUG_THREADS +GC_log_printf("Starting mark helper for mark number %lu\n", +(unsigned long)my_mark_no); +#endif +GC_help_marker(my_mark_no); +} +} +STATIC pthread_t GC_mark_threads[MAX_MARKERS]; +#ifdef CAN_HANDLE_FORK +static int available_markers_m1=0; +static pthread_cond_t mark_cv; +#else +#define available_markers_m1 GC_markers_m1 +static pthread_cond_t mark_cv=PTHREAD_COND_INITIALIZER; +#endif +GC_INNER void GC_start_mark_threads_inner(void) +{ +int i; +pthread_attr_t attr; +#ifndef NO_MARKER_SPECIAL_SIGMASK +sigset_t set,oldset; +#endif +GC_ASSERT(I_DONT_HOLD_LOCK()); +if (available_markers_m1<=0)return; +#ifdef CAN_HANDLE_FORK +if (GC_parallel)return; +{ +pthread_cond_t mark_cv_local=PTHREAD_COND_INITIALIZER; +BCOPY(&mark_cv_local,&mark_cv,sizeof(mark_cv)); +} +#endif +GC_ASSERT(GC_fl_builder_count==0); +INIT_REAL_SYMS(); +if (0!=pthread_attr_init(&attr))ABORT("pthread_attr_init failed"); +if (0!=pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED)) +ABORT("pthread_attr_setdetachstate failed"); +#ifdef DEFAULT_STACK_MAYBE_SMALL +{ +size_t old_size; +if (pthread_attr_getstacksize(&attr,&old_size)!=0) +ABORT("pthread_attr_getstacksize failed"); +if (old_size < MIN_STACK_SIZE +&&old_size!=0){ +if (pthread_attr_setstacksize(&attr,MIN_STACK_SIZE)!=0) +ABORT("pthread_attr_setstacksize failed"); +} +} +#endif +#ifndef NO_MARKER_SPECIAL_SIGMASK +if (sigfillset(&set)!=0) +ABORT("sigfillset failed"); +#if!defined(GC_DARWIN_THREADS)&&!defined(GC_OPENBSD_UTHREADS)&&!defined(NACL) +if (sigdelset(&set,GC_get_suspend_signal())!=0 +||sigdelset(&set,GC_get_thr_restart_signal())!=0) +ABORT("sigdelset failed"); +#endif +if (REAL_FUNC(pthread_sigmask)(SIG_BLOCK,&set,&oldset)< 0){ +WARN("pthread_sigmask set failed,no markers started," +" errno=%" WARN_PRIdPTR "\n",errno); +GC_markers_m1=0; +(void)pthread_attr_destroy(&attr); +return; +} +#endif +#ifdef CAN_HANDLE_FORK +GC_markers_m1=available_markers_m1; +#endif +for (i=0;i < available_markers_m1;++i){ +if (0!=REAL_FUNC(pthread_create)(GC_mark_threads+i,&attr, +GC_mark_thread,(void*)(word)i)){ +WARN("Marker thread creation failed,errno=%" WARN_PRIdPTR "\n", +errno); +GC_markers_m1=i; +break; +} +} +#ifndef NO_MARKER_SPECIAL_SIGMASK +if (REAL_FUNC(pthread_sigmask)(SIG_SETMASK,&oldset,NULL)< 0){ +WARN("pthread_sigmask restore failed,errno=%" WARN_PRIdPTR "\n", +errno); +} +#endif +(void)pthread_attr_destroy(&attr); +GC_wait_for_markers_init(); +GC_COND_LOG_PRINTF("Started %d mark helper threads\n",GC_markers_m1); +} +#endif +GC_INNER GC_bool GC_thr_initialized=FALSE; +GC_INNER volatile GC_thread GC_threads[THREAD_TABLE_SZ]={0}; +void GC_push_thread_structures(void) +{ +GC_ASSERT(I_HOLD_LOCK()); +GC_PUSH_ALL_SYM(GC_threads); +#if defined(THREAD_LOCAL_ALLOC) +GC_PUSH_ALL_SYM(GC_thread_key); +#endif +} +#ifdef DEBUG_THREADS +STATIC int GC_count_threads(void) +{ +int i; +int count=0; +GC_ASSERT(I_HOLD_LOCK()); +for (i=0;i < THREAD_TABLE_SZ;++i){ +GC_thread th=GC_threads[i]; +while (th){ +if (!(th->flags&FINISHED)) +++count; +th=th->next; +} +} +return count; +} +#endif +static struct GC_Thread_Rep first_thread; +STATIC GC_thread GC_new_thread(pthread_t id) +{ +int hv=THREAD_TABLE_INDEX(id); +GC_thread result; +static GC_bool first_thread_used=FALSE; +#ifdef DEBUG_THREADS +GC_log_printf("Creating thread %p\n",(void*)id); +for (result=GC_threads[hv];result!=NULL;result=result->next) +if (!THREAD_EQUAL(result->id,id)){ +GC_log_printf("Hash collision at GC_threads[%d]\n",hv); +break; +} +#endif +GC_ASSERT(I_HOLD_LOCK()); +if (!EXPECT(first_thread_used,TRUE)){ +result=&first_thread; +first_thread_used=TRUE; +GC_ASSERT(NULL==GC_threads[hv]); +#if defined(THREAD_SANITIZER)&&defined(CPPCHECK) +GC_noop1(result->dummy[0]); +#endif +} else { +result=(struct GC_Thread_Rep*) +GC_INTERNAL_MALLOC(sizeof(struct GC_Thread_Rep),NORMAL); +if (result==0)return(0); +} +result->id=id; +#ifdef USE_TKILL_ON_ANDROID +result->kernel_id=gettid(); +#endif +result->next=GC_threads[hv]; +GC_threads[hv]=result; +#ifdef NACL +GC_nacl_gc_thread_self=result; +GC_nacl_initialize_gc_thread(); +#endif +GC_ASSERT(result->flags==0&&result->thread_blocked==0); +if (EXPECT(result!=&first_thread,TRUE)) +GC_dirty(result); +return(result); +} +STATIC void GC_delete_thread(pthread_t id) +{ +int hv=THREAD_TABLE_INDEX(id); +GC_thread p=GC_threads[hv]; +GC_thread prev=NULL; +#ifdef DEBUG_THREADS +GC_log_printf("Deleting thread %p,n_threads=%d\n", +(void*)id,GC_count_threads()); +#endif +#ifdef NACL +GC_nacl_shutdown_gc_thread(); +GC_nacl_gc_thread_self=NULL; +#endif +GC_ASSERT(I_HOLD_LOCK()); +while (!THREAD_EQUAL(p->id,id)){ +prev=p; +p=p->next; +} +if (prev==0){ +GC_threads[hv]=p->next; +} else { +GC_ASSERT(prev!=&first_thread); +prev->next=p->next; +GC_dirty(prev); +} +if (p!=&first_thread){ +#ifdef GC_DARWIN_THREADS +mach_port_deallocate(mach_task_self(),p->stop_info.mach_thread); +#endif +GC_INTERNAL_FREE(p); +} +} +STATIC void GC_delete_gc_thread(GC_thread t) +{ +pthread_t id=t->id; +int hv=THREAD_TABLE_INDEX(id); +GC_thread p=GC_threads[hv]; +GC_thread prev=NULL; +GC_ASSERT(I_HOLD_LOCK()); +while (p!=t){ +prev=p; +p=p->next; +} +if (prev==0){ +GC_threads[hv]=p->next; +} else { +GC_ASSERT(prev!=&first_thread); +prev->next=p->next; +GC_dirty(prev); +} +#ifdef GC_DARWIN_THREADS +mach_port_deallocate(mach_task_self(),p->stop_info.mach_thread); +#endif +GC_INTERNAL_FREE(p); +#ifdef DEBUG_THREADS +GC_log_printf("Deleted thread %p,n_threads=%d\n", +(void*)id,GC_count_threads()); +#endif +} +GC_INNER GC_thread GC_lookup_thread(pthread_t id) +{ +GC_thread p=GC_threads[THREAD_TABLE_INDEX(id)]; +while (p!=0&&!THREAD_EQUAL(p->id,id))p=p->next; +return(p); +} +GC_INNER void GC_reset_finalizer_nested(void) +{ +GC_thread me=GC_lookup_thread(pthread_self()); +me->finalizer_nested=0; +} +GC_INNER unsigned char*GC_check_finalizer_nested(void) +{ +GC_thread me=GC_lookup_thread(pthread_self()); +unsigned nesting_level=me->finalizer_nested; +if (nesting_level){ +if (++me->finalizer_skipped < (1U<finalizer_skipped=0; +} +me->finalizer_nested=(unsigned char)(nesting_level+1); +return&me->finalizer_nested; +} +#if defined(GC_ASSERTIONS)&&defined(THREAD_LOCAL_ALLOC) +GC_bool GC_is_thread_tsd_valid(void*tsd) +{ +GC_thread me; +DCL_LOCK_STATE; +LOCK(); +me=GC_lookup_thread(pthread_self()); +UNLOCK(); +return (word)tsd>=(word)(&me->tlfs) +&&(word)tsd < (word)(&me->tlfs)+sizeof(me->tlfs); +} +#endif +GC_API int GC_CALL GC_thread_is_registered(void) +{ +pthread_t self=pthread_self(); +GC_thread me; +DCL_LOCK_STATE; +LOCK(); +me=GC_lookup_thread(self); +UNLOCK(); +return me!=NULL; +} +static pthread_t main_pthread_id; +static void*main_stack,*main_altstack; +static word main_stack_size,main_altstack_size; +GC_API void GC_CALL GC_register_altstack(void*stack,GC_word stack_size, +void*altstack, +GC_word altstack_size) +{ +GC_thread me; +pthread_t self=pthread_self(); +DCL_LOCK_STATE; +LOCK(); +me=GC_lookup_thread(self); +if (me!=NULL){ +me->stack=(ptr_t)stack; +me->stack_size=stack_size; +me->altstack=(ptr_t)altstack; +me->altstack_size=altstack_size; +} else { +main_pthread_id=self; +main_stack=stack; +main_stack_size=stack_size; +main_altstack=altstack; +main_altstack_size=altstack_size; +} +UNLOCK(); +} +#ifdef CAN_HANDLE_FORK +#ifdef CAN_CALL_ATFORK +GC_ATTR_NO_SANITIZE_THREAD +#endif +static void store_to_threads_table(int hv,GC_thread me) +{ +GC_threads[hv]=me; +} +STATIC void GC_remove_all_threads_but_me(void) +{ +pthread_t self=pthread_self(); +int hv; +for (hv=0;hv < THREAD_TABLE_SZ;++hv){ +GC_thread p,next; +GC_thread me=NULL; +for (p=GC_threads[hv];0!=p;p=next){ +next=p->next; +if (THREAD_EQUAL(p->id,self) +&&me==NULL){ +me=p; +p->next=0; +#ifdef GC_DARWIN_THREADS +me->stop_info.mach_thread=mach_thread_self(); +#endif +#ifdef USE_TKILL_ON_ANDROID +me->kernel_id=gettid(); +#endif +#if defined(THREAD_LOCAL_ALLOC)&&!defined(USE_CUSTOM_SPECIFIC) +{ +int res; +res=GC_setspecific(GC_thread_key,&me->tlfs); +if (COVERT_DATAFLOW(res)!=0) +ABORT("GC_setspecific failed (in child)"); +} +#endif +} else { +#ifdef THREAD_LOCAL_ALLOC +if (!(p->flags&FINISHED)){ +GC_remove_specific_after_fork(GC_thread_key,p->id); +} +#endif +#if!defined(THREAD_SANITIZER)||!defined(CAN_CALL_ATFORK) +if (p!=&first_thread)GC_INTERNAL_FREE(p); +#endif +} +} +store_to_threads_table(hv,me); +} +} +#endif +#ifdef USE_PROC_FOR_LIBRARIES +GC_INNER GC_bool GC_segment_is_thread_stack(ptr_t lo,ptr_t hi) +{ +int i; +GC_thread p; +GC_ASSERT(I_HOLD_LOCK()); +#ifdef PARALLEL_MARK +for (i=0;i < GC_markers_m1;++i){ +if ((word)marker_sp[i] > (word)lo&&(word)marker_sp[i] < (word)hi) +return TRUE; +#ifdef IA64 +if ((word)marker_bsp[i] > (word)lo +&&(word)marker_bsp[i] < (word)hi) +return TRUE; +#endif +} +#endif +for (i=0;i < THREAD_TABLE_SZ;i++){ +for (p=GC_threads[i];p!=0;p=p->next){ +if (0!=p->stack_end){ +#ifdef STACK_GROWS_UP +if ((word)p->stack_end>=(word)lo +&&(word)p->stack_end < (word)hi) +return TRUE; +#else +if ((word)p->stack_end > (word)lo +&&(word)p->stack_end<=(word)hi) +return TRUE; +#endif +} +} +} +return FALSE; +} +#endif +#ifdef IA64 +GC_INNER ptr_t GC_greatest_stack_base_below(ptr_t bound) +{ +int i; +GC_thread p; +ptr_t result=0; +GC_ASSERT(I_HOLD_LOCK()); +#ifdef PARALLEL_MARK +for (i=0;i < GC_markers_m1;++i){ +if ((word)marker_sp[i] > (word)result +&&(word)marker_sp[i] < (word)bound) +result=marker_sp[i]; +} +#endif +for (i=0;i < THREAD_TABLE_SZ;i++){ +for (p=GC_threads[i];p!=0;p=p->next){ +if ((word)p->stack_end > (word)result +&&(word)p->stack_end < (word)bound){ +result=p->stack_end; +} +} +} +return result; +} +#endif +#ifndef STAT_READ +#define STAT_BUF_SIZE 4096 +#define STAT_READ read +#endif +#ifdef GC_HPUX_THREADS +#define GC_get_nprocs()pthread_num_processors_np() +#elif defined(GC_OSF1_THREADS)||defined(GC_AIX_THREADS)||defined(GC_HAIKU_THREADS)||defined(GC_SOLARIS_THREADS)||defined(HURD)||defined(HOST_ANDROID)||defined(NACL) +GC_INLINE int GC_get_nprocs(void) +{ +int nprocs=(int)sysconf(_SC_NPROCESSORS_ONLN); +return nprocs > 0?nprocs:1; +} +#elif defined(GC_IRIX_THREADS) +GC_INLINE int GC_get_nprocs(void) +{ +int nprocs=(int)sysconf(_SC_NPROC_ONLN); +return nprocs > 0?nprocs:1; +} +#elif defined(GC_LINUX_THREADS) +STATIC int GC_get_nprocs(void) +{ +char stat_buf[STAT_BUF_SIZE]; +int f; +int result,i,len; +f=open("/proc/stat",O_RDONLY); +if (f < 0){ +WARN("Couldn't read/proc/stat\n",0); +return 1; +} +len=STAT_READ(f,stat_buf,STAT_BUF_SIZE); +close(f); +result=1; +for (i=0;i < len - 100;++i){ +if (stat_buf[i]=='\n'&&stat_buf[i+1]=='c' +&&stat_buf[i+2]=='p'&&stat_buf[i+3]=='u'){ +int cpu_no=atoi(&stat_buf[i+4]); +if (cpu_no>=result) +result=cpu_no+1; +} +} +return result; +} +#elif defined(GC_DGUX386_THREADS) +STATIC int GC_get_nprocs(void) +{ +int numCpus; +struct dg_sys_info_pm_info pm_sysinfo; +int status=0; +status=dg_sys_info((long int*)&pm_sysinfo, +DG_SYS_INFO_PM_INFO_TYPE,DG_SYS_INFO_PM_CURRENT_VERSION); +if (status < 0) +numCpus=-1; +else +numCpus=pm_sysinfo.idle_vp_count; +return(numCpus); +} +#elif defined(GC_DARWIN_THREADS)||defined(GC_FREEBSD_THREADS)||defined(GC_NETBSD_THREADS)||defined(GC_OPENBSD_THREADS) +STATIC int GC_get_nprocs(void) +{ +int mib[]={CTL_HW,HW_NCPU}; +int res; +size_t len=sizeof(res); +sysctl(mib,sizeof(mib)/sizeof(int),&res,&len,NULL,0); +return res; +} +#else +#define GC_get_nprocs()1 +#endif +#if defined(ARM32)&&defined(GC_LINUX_THREADS)&&!defined(NACL) +STATIC int GC_get_nprocs_present(void) +{ +char stat_buf[16]; +int f; +int len; +f=open("/sys/devices/system/cpu/present",O_RDONLY); +if (f < 0) +return -1; +len=STAT_READ(f,stat_buf,sizeof(stat_buf)); +close(f); +if (len < 2||stat_buf[0]!='0'||stat_buf[len - 1]!='\n'){ +return 0; +} else if (len==2){ +return 1; +} else if (stat_buf[1]!='-'){ +return 0; +} +stat_buf[len - 1]='\0'; +return atoi(&stat_buf[2])+1; +} +#endif +STATIC void GC_wait_for_gc_completion(GC_bool wait_for_all) +{ +DCL_LOCK_STATE; +#if!defined(THREAD_SANITIZER)||!defined(CAN_CALL_ATFORK) +GC_ASSERT(I_HOLD_LOCK()); +#endif +ASSERT_CANCEL_DISABLED(); +if (GC_incremental&&GC_collection_in_progress()){ +word old_gc_no=GC_gc_no; +while (GC_incremental&&GC_collection_in_progress() +&&(wait_for_all||old_gc_no==GC_gc_no)){ +ENTER_GC(); +GC_in_thread_creation=TRUE; +GC_collect_a_little_inner(1); +GC_in_thread_creation=FALSE; +EXIT_GC(); +UNLOCK(); +sched_yield(); +LOCK(); +} +} +} +#ifdef CAN_HANDLE_FORK +IF_CANCEL(static int fork_cancel_state;) +#if defined(GC_ASSERTIONS)&&defined(CAN_CALL_ATFORK) +GC_ATTR_NO_SANITIZE_THREAD +#endif +static void fork_prepare_proc(void) +{ +LOCK(); +DISABLE_CANCEL(fork_cancel_state); +#if defined(PARALLEL_MARK) +if (GC_parallel) +GC_wait_for_reclaim(); +#endif +GC_wait_for_gc_completion(TRUE); +#if defined(PARALLEL_MARK) +if (GC_parallel) +GC_acquire_mark_lock(); +#endif +GC_acquire_dirty_lock(); +} +#if defined(GC_ASSERTIONS)&&defined(CAN_CALL_ATFORK) +GC_ATTR_NO_SANITIZE_THREAD +#endif +static void fork_parent_proc(void) +{ +GC_release_dirty_lock(); +#if defined(PARALLEL_MARK) +if (GC_parallel) +GC_release_mark_lock(); +#endif +RESTORE_CANCEL(fork_cancel_state); +UNLOCK(); +} +#if defined(GC_ASSERTIONS)&&defined(CAN_CALL_ATFORK) +GC_ATTR_NO_SANITIZE_THREAD +#endif +static void fork_child_proc(void) +{ +GC_release_dirty_lock(); +#if defined(PARALLEL_MARK) +if (GC_parallel) +GC_release_mark_lock(); +#endif +GC_remove_all_threads_but_me(); +#ifdef PARALLEL_MARK +GC_parallel=FALSE; +#endif +RESTORE_CANCEL(fork_cancel_state); +UNLOCK(); +#ifdef USE_PTHREAD_LOCKS +GC_ASSERT(I_DONT_HOLD_LOCK()); +(void)pthread_mutex_destroy(&GC_allocate_ml); +if (0!=pthread_mutex_init(&GC_allocate_ml,NULL)) +ABORT("pthread_mutex_init failed (in child)"); +#endif +} +GC_API void GC_CALL GC_atfork_prepare(void) +{ +if (!EXPECT(GC_is_initialized,TRUE))GC_init(); +#if defined(GC_DARWIN_THREADS)&&defined(MPROTECT_VDB) +if (GC_auto_incremental){ +GC_ASSERT(0==GC_handle_fork); +ABORT("Unable to fork while mprotect_thread is running"); +} +#endif +if (GC_handle_fork<=0) +fork_prepare_proc(); +} +GC_API void GC_CALL GC_atfork_parent(void) +{ +if (GC_handle_fork<=0) +fork_parent_proc(); +} +GC_API void GC_CALL GC_atfork_child(void) +{ +if (GC_handle_fork<=0) +fork_child_proc(); +} +#endif +#ifdef INCLUDE_LINUX_THREAD_DESCR +__thread int GC_dummy_thread_local; +#endif +#ifdef PARALLEL_MARK +static void setup_mark_lock(void); +static unsigned required_markers_cnt=0; +#endif +GC_API void GC_CALL GC_set_markers_count(unsigned markers GC_ATTR_UNUSED) +{ +#ifdef PARALLEL_MARK +required_markers_cnt=markers < MAX_MARKERS?markers:MAX_MARKERS; +#endif +} +GC_INNER void GC_thr_init(void) +{ +GC_ASSERT(I_HOLD_LOCK()); +if (GC_thr_initialized)return; +GC_thr_initialized=TRUE; +GC_ASSERT((word)&GC_threads % sizeof(word)==0); +#ifdef CAN_HANDLE_FORK +if (GC_handle_fork){ +#ifdef CAN_CALL_ATFORK +if (pthread_atfork(fork_prepare_proc,fork_parent_proc, +fork_child_proc)==0){ +GC_handle_fork=1; +} else +#endif +if (GC_handle_fork!=-1) +ABORT("pthread_atfork failed"); +} +#endif +#ifdef INCLUDE_LINUX_THREAD_DESCR +{ +ptr_t thread_local_addr=(ptr_t)(&GC_dummy_thread_local); +ptr_t main_thread_start,main_thread_end; +if (!GC_enclosing_mapping(thread_local_addr,&main_thread_start, +&main_thread_end)){ +ABORT("Failed to find mapping for main thread thread locals"); +} else { +GC_add_roots_inner(main_thread_start,main_thread_end,FALSE); +} +} +#endif +{ +pthread_t self=pthread_self(); +GC_thread t=GC_new_thread(self); +if (t==NULL) +ABORT("Failed to allocate memory for the initial thread"); +#ifdef GC_DARWIN_THREADS +t->stop_info.mach_thread=mach_thread_self(); +#else +t->stop_info.stack_ptr=GC_approx_sp(); +#endif +t->flags=DETACHED|MAIN_THREAD; +if (THREAD_EQUAL(self,main_pthread_id)){ +t->stack=(ptr_t)main_stack; +t->stack_size=main_stack_size; +t->altstack=(ptr_t)main_altstack; +t->altstack_size=main_altstack_size; +} +} +#ifndef GC_DARWIN_THREADS +GC_stop_init(); +#endif +{ +char*nprocs_string=GETENV("GC_NPROCS"); +GC_nprocs=-1; +if (nprocs_string!=NULL)GC_nprocs=atoi(nprocs_string); +} +if (GC_nprocs<=0 +#if defined(ARM32)&&defined(GC_LINUX_THREADS)&&!defined(NACL) +&&(GC_nprocs=GC_get_nprocs_present())<=1 +#endif +) +{ +GC_nprocs=GC_get_nprocs(); +} +if (GC_nprocs<=0){ +WARN("GC_get_nprocs()returned %" WARN_PRIdPTR "\n",GC_nprocs); +GC_nprocs=2; +#ifdef PARALLEL_MARK +available_markers_m1=0; +#endif +} else { +#ifdef PARALLEL_MARK +{ +char*markers_string=GETENV("GC_MARKERS"); +int markers=required_markers_cnt; +if (markers_string!=NULL){ +markers=atoi(markers_string); +if (markers<=0||markers > MAX_MARKERS){ +WARN("Too big or invalid number of mark threads:%" WARN_PRIdPTR +";using maximum threads\n",(signed_word)markers); +markers=MAX_MARKERS; +} +} else if (0==markers){ +markers=GC_nprocs; +#if defined(GC_MIN_MARKERS)&&!defined(CPPCHECK) +if (markers < GC_MIN_MARKERS) +markers=GC_MIN_MARKERS; +#endif +if (markers > MAX_MARKERS) +markers=MAX_MARKERS; +} +available_markers_m1=markers - 1; +} +#endif +} +GC_COND_LOG_PRINTF("Number of processors=%d\n",GC_nprocs); +#ifdef PARALLEL_MARK +if (available_markers_m1<=0){ +GC_parallel=FALSE; +GC_COND_LOG_PRINTF( +"Single marker thread,turning off parallel marking\n"); +} else { +setup_mark_lock(); +} +#endif +} +GC_INNER void GC_init_parallel(void) +{ +#if defined(THREAD_LOCAL_ALLOC) +DCL_LOCK_STATE; +#endif +if (parallel_initialized)return; +parallel_initialized=TRUE; +if (!GC_is_initialized)GC_init(); +#if defined(THREAD_LOCAL_ALLOC) +LOCK(); +GC_init_thread_local(&(GC_lookup_thread(pthread_self())->tlfs)); +UNLOCK(); +#endif +} +#ifndef GC_NO_PTHREAD_SIGMASK +GC_API int WRAP_FUNC(pthread_sigmask)(int how,const sigset_t*set, +sigset_t*oset) +{ +sigset_t fudged_set; +INIT_REAL_SYMS(); +if (set!=NULL&&(how==SIG_BLOCK||how==SIG_SETMASK)){ +int sig_suspend=GC_get_suspend_signal(); +fudged_set=*set; +GC_ASSERT(sig_suspend>=0); +if (sigdelset(&fudged_set,sig_suspend)!=0) +ABORT("sigdelset failed"); +set=&fudged_set; +} +return(REAL_FUNC(pthread_sigmask)(how,set,oset)); +} +#endif +GC_INNER void GC_do_blocking_inner(ptr_t data,void*context GC_ATTR_UNUSED) +{ +struct blocking_data*d=(struct blocking_data*)data; +pthread_t self=pthread_self(); +GC_thread me; +#if defined(SPARC)||defined(IA64) +ptr_t stack_ptr=GC_save_regs_in_stack(); +#endif +#if defined(GC_DARWIN_THREADS)&&!defined(DARWIN_DONT_PARSE_STACK) +GC_bool topOfStackUnset=FALSE; +#endif +DCL_LOCK_STATE; +LOCK(); +me=GC_lookup_thread(self); +GC_ASSERT(!(me->thread_blocked)); +#ifdef SPARC +me->stop_info.stack_ptr=stack_ptr; +#else +me->stop_info.stack_ptr=GC_approx_sp(); +#endif +#if defined(GC_DARWIN_THREADS)&&!defined(DARWIN_DONT_PARSE_STACK) +if (me->topOfStack==NULL){ +topOfStackUnset=TRUE; +me->topOfStack=GC_FindTopOfStack(0); +} +#endif +#ifdef IA64 +me->backing_store_ptr=stack_ptr; +#endif +me->thread_blocked=(unsigned char)TRUE; +UNLOCK(); +d->client_data=(d->fn)(d->client_data); +LOCK(); +#if defined(CPPCHECK) +GC_noop1((word)&me->thread_blocked); +#endif +me->thread_blocked=FALSE; +#if defined(GC_DARWIN_THREADS)&&!defined(DARWIN_DONT_PARSE_STACK) +if (topOfStackUnset) +me->topOfStack=NULL; +#endif +UNLOCK(); +} +GC_API void GC_CALL GC_set_stackbottom(void*gc_thread_handle, +const struct GC_stack_base*sb) +{ +GC_thread t=(GC_thread)gc_thread_handle; +GC_ASSERT(sb->mem_base!=NULL); +if (!EXPECT(GC_is_initialized,TRUE)){ +GC_ASSERT(NULL==t); +} else { +GC_ASSERT(I_HOLD_LOCK()); +if (NULL==t) +t=GC_lookup_thread(pthread_self()); +GC_ASSERT((t->flags&FINISHED)==0); +GC_ASSERT(!(t->thread_blocked) +&&NULL==t->traced_stack_sect); +if ((t->flags&MAIN_THREAD)==0){ +t->stack_end=(ptr_t)sb->mem_base; +#ifdef IA64 +t->backing_store_end=(ptr_t)sb->reg_base; +#endif +return; +} +} +GC_stackbottom=(char*)sb->mem_base; +#ifdef IA64 +GC_register_stackbottom=(ptr_t)sb->reg_base; +#endif +} +GC_API void*GC_CALL GC_get_my_stackbottom(struct GC_stack_base*sb) +{ +pthread_t self=pthread_self(); +GC_thread me; +DCL_LOCK_STATE; +LOCK(); +me=GC_lookup_thread(self); +if ((me->flags&MAIN_THREAD)==0){ +sb->mem_base=me->stack_end; +#ifdef IA64 +sb->reg_base=me->backing_store_end; +#endif +} else { +sb->mem_base=GC_stackbottom; +#ifdef IA64 +sb->reg_base=GC_register_stackbottom; +#endif +} +UNLOCK(); +return (void*)me; +} +GC_API void*GC_CALL GC_call_with_gc_active(GC_fn_type fn, +void*client_data) +{ +struct GC_traced_stack_sect_s stacksect; +pthread_t self=pthread_self(); +GC_thread me; +DCL_LOCK_STATE; +LOCK(); +me=GC_lookup_thread(self); +if ((me->flags&MAIN_THREAD)==0){ +GC_ASSERT(me->stack_end!=NULL); +if ((word)me->stack_end HOTTER_THAN (word)(&stacksect)) +me->stack_end=(ptr_t)(&stacksect); +} else { +if ((word)GC_stackbottom HOTTER_THAN (word)(&stacksect)) +GC_stackbottom=(ptr_t)COVERT_DATAFLOW(&stacksect); +} +if (!me->thread_blocked){ +UNLOCK(); +client_data=fn(client_data); +GC_noop1(COVERT_DATAFLOW(&stacksect)); +return client_data; +} +stacksect.saved_stack_ptr=me->stop_info.stack_ptr; +#ifdef IA64 +stacksect.backing_store_end=GC_save_regs_in_stack(); +stacksect.saved_backing_store_ptr=me->backing_store_ptr; +#endif +stacksect.prev=me->traced_stack_sect; +me->thread_blocked=FALSE; +me->traced_stack_sect=&stacksect; +UNLOCK(); +client_data=fn(client_data); +GC_ASSERT(me->thread_blocked==FALSE); +GC_ASSERT(me->traced_stack_sect==&stacksect); +#if defined(CPPCHECK) +GC_noop1((word)me->traced_stack_sect); +#endif +LOCK(); +me->traced_stack_sect=stacksect.prev; +#ifdef IA64 +me->backing_store_ptr=stacksect.saved_backing_store_ptr; +#endif +me->thread_blocked=(unsigned char)TRUE; +me->stop_info.stack_ptr=stacksect.saved_stack_ptr; +UNLOCK(); +return client_data; +} +STATIC void GC_unregister_my_thread_inner(GC_thread me) +{ +#ifdef DEBUG_THREADS +GC_log_printf( +"Unregistering thread %p,gc_thread=%p,n_threads=%d\n", +(void*)me->id,(void*)me,GC_count_threads()); +#endif +GC_ASSERT(!(me->flags&FINISHED)); +#if defined(THREAD_LOCAL_ALLOC) +GC_ASSERT(GC_getspecific(GC_thread_key)==&me->tlfs); +GC_destroy_thread_local(&(me->tlfs)); +#endif +#if defined(GC_HAVE_PTHREAD_EXIT)||!defined(GC_NO_PTHREAD_CANCEL) +if ((me->flags&DISABLED_GC)!=0){ +GC_dont_gc--; +} +#endif +if (me->flags&DETACHED){ +GC_delete_thread(pthread_self()); +} else { +me->flags|=FINISHED; +} +#if defined(THREAD_LOCAL_ALLOC) +GC_remove_specific(GC_thread_key); +#endif +} +GC_API int GC_CALL GC_unregister_my_thread(void) +{ +pthread_t self=pthread_self(); +GC_thread me; +IF_CANCEL(int cancel_state;) +DCL_LOCK_STATE; +LOCK(); +DISABLE_CANCEL(cancel_state); +GC_wait_for_gc_completion(FALSE); +me=GC_lookup_thread(self); +#ifdef DEBUG_THREADS +GC_log_printf( +"Called GC_unregister_my_thread on %p,gc_thread=%p\n", +(void*)self,(void*)me); +#endif +GC_ASSERT(THREAD_EQUAL(me->id,self)); +GC_unregister_my_thread_inner(me); +RESTORE_CANCEL(cancel_state); +UNLOCK(); +return GC_SUCCESS; +} +GC_INNER_PTHRSTART void GC_thread_exit_proc(void*arg) +{ +IF_CANCEL(int cancel_state;) +DCL_LOCK_STATE; +#ifdef DEBUG_THREADS +GC_log_printf("Called GC_thread_exit_proc on %p,gc_thread=%p\n", +(void*)((GC_thread)arg)->id,arg); +#endif +LOCK(); +DISABLE_CANCEL(cancel_state); +GC_wait_for_gc_completion(FALSE); +GC_unregister_my_thread_inner((GC_thread)arg); +RESTORE_CANCEL(cancel_state); +UNLOCK(); +} +#if!defined(SN_TARGET_ORBIS)&&!defined(SN_TARGET_PSP2) +GC_API int WRAP_FUNC(pthread_join)(pthread_t thread,void**retval) +{ +int result; +GC_thread t; +DCL_LOCK_STATE; +ASSERT_SYMS_INITIALIZED(); +LOCK(); +t=GC_lookup_thread(thread); +UNLOCK(); +result=REAL_FUNC(pthread_join)(thread,retval); +#if defined(GC_FREEBSD_THREADS) +if (result==EINTR)result=0; +#endif +if (result==0){ +LOCK(); +if ((t->flags&FINISHED)!=0) +GC_delete_gc_thread(t); +UNLOCK(); +} +return result; +} +GC_API int WRAP_FUNC(pthread_detach)(pthread_t thread) +{ +int result; +GC_thread t; +DCL_LOCK_STATE; +ASSERT_SYMS_INITIALIZED(); +LOCK(); +t=GC_lookup_thread(thread); +UNLOCK(); +result=REAL_FUNC(pthread_detach)(thread); +if (result==0){ +LOCK(); +t->flags|=DETACHED; +if ((t->flags&FINISHED)!=0){ +GC_delete_gc_thread(t); +} +UNLOCK(); +} +return result; +} +#endif +#ifndef GC_NO_PTHREAD_CANCEL +GC_API int WRAP_FUNC(pthread_cancel)(pthread_t thread) +{ +#ifdef CANCEL_SAFE +GC_thread t; +DCL_LOCK_STATE; +#endif +INIT_REAL_SYMS(); +#ifdef CANCEL_SAFE +LOCK(); +t=GC_lookup_thread(thread); +if (t!=NULL&&(t->flags&DISABLED_GC)==0){ +t->flags|=DISABLED_GC; +GC_dont_gc++; +} +UNLOCK(); +#endif +return REAL_FUNC(pthread_cancel)(thread); +} +#endif +#ifdef GC_HAVE_PTHREAD_EXIT +GC_API GC_PTHREAD_EXIT_ATTRIBUTE void WRAP_FUNC(pthread_exit)(void*retval) +{ +pthread_t self=pthread_self(); +GC_thread me; +DCL_LOCK_STATE; +INIT_REAL_SYMS(); +LOCK(); +me=GC_lookup_thread(self); +if (me!=0&&(me->flags&DISABLED_GC)==0){ +me->flags|=DISABLED_GC; +GC_dont_gc++; +} +UNLOCK(); +REAL_FUNC(pthread_exit)(retval); +} +#endif +GC_INNER GC_bool GC_in_thread_creation=FALSE; +GC_INLINE void GC_record_stack_base(GC_thread me, +const struct GC_stack_base*sb) +{ +#ifndef GC_DARWIN_THREADS +me->stop_info.stack_ptr=(ptr_t)sb->mem_base; +#endif +me->stack_end=(ptr_t)sb->mem_base; +if (me->stack_end==NULL) +ABORT("Bad stack base in GC_register_my_thread"); +#ifdef IA64 +me->backing_store_end=(ptr_t)sb->reg_base; +#endif +} +STATIC GC_thread GC_register_my_thread_inner(const struct GC_stack_base*sb, +pthread_t my_pthread) +{ +GC_thread me; +GC_in_thread_creation=TRUE; +me=GC_new_thread(my_pthread); +GC_in_thread_creation=FALSE; +if (me==0) +ABORT("Failed to allocate memory for thread registering"); +#ifdef GC_DARWIN_THREADS +me->stop_info.mach_thread=mach_thread_self(); +#endif +GC_record_stack_base(me,sb); +#ifdef GC_EXPLICIT_SIGNALS_UNBLOCK +GC_unblock_gc_signals(); +#endif +return me; +} +GC_API void GC_CALL GC_allow_register_threads(void) +{ +GC_ASSERT(GC_lookup_thread(pthread_self())!=0); +set_need_to_lock(); +} +GC_API int GC_CALL GC_register_my_thread(const struct GC_stack_base*sb) +{ +pthread_t self=pthread_self(); +GC_thread me; +DCL_LOCK_STATE; +if (GC_need_to_lock==FALSE) +ABORT("Threads explicit registering is not previously enabled"); +LOCK(); +me=GC_lookup_thread(self); +if (0==me){ +me=GC_register_my_thread_inner(sb,self); +#if defined(CPPCHECK) +GC_noop1(me->flags); +#endif +me->flags|=DETACHED; +#if defined(THREAD_LOCAL_ALLOC) +GC_init_thread_local(&(me->tlfs)); +#endif +UNLOCK(); +return GC_SUCCESS; +} else if ((me->flags&FINISHED)!=0){ +#ifdef GC_DARWIN_THREADS +me->stop_info.mach_thread=mach_thread_self(); +#endif +GC_record_stack_base(me,sb); +me->flags&=~FINISHED; +#ifdef GC_EXPLICIT_SIGNALS_UNBLOCK +GC_unblock_gc_signals(); +#endif +#if defined(THREAD_LOCAL_ALLOC) +GC_init_thread_local(&(me->tlfs)); +#endif +UNLOCK(); +return GC_SUCCESS; +} else { +UNLOCK(); +return GC_DUPLICATE; +} +} +struct start_info { +void*(*start_routine)(void*); +void*arg; +word flags; +sem_t registered; +}; +GC_INNER_PTHRSTART GC_thread GC_start_rtn_prepare_thread( +void*(**pstart)(void*), +void**pstart_arg, +struct GC_stack_base*sb,void*arg) +{ +struct start_info*si=(struct start_info*)arg; +pthread_t self=pthread_self(); +GC_thread me; +DCL_LOCK_STATE; +#ifdef DEBUG_THREADS +GC_log_printf("Starting thread %p,pid=%ld,sp=%p\n", +(void*)self,(long)getpid(),(void*)&arg); +#endif +LOCK(); +me=GC_register_my_thread_inner(sb,self); +me->flags=si->flags; +#if defined(THREAD_LOCAL_ALLOC) +GC_init_thread_local(&(me->tlfs)); +#endif +UNLOCK(); +*pstart=si->start_routine; +#ifdef DEBUG_THREADS +GC_log_printf("start_routine=%p\n",(void*)(signed_word)(*pstart)); +#endif +*pstart_arg=si->arg; +sem_post(&(si->registered)); +return me; +} +#if!defined(SN_TARGET_ORBIS)&&!defined(SN_TARGET_PSP2) +STATIC void*GC_start_routine(void*arg) +{ +#ifdef INCLUDE_LINUX_THREAD_DESCR +struct GC_stack_base sb; +#ifdef REDIRECT_MALLOC +GC_disable(); +#endif +if (GC_get_stack_base(&sb)!=GC_SUCCESS) +ABORT("Failed to get thread stack base"); +#ifdef REDIRECT_MALLOC +GC_enable(); +#endif +return GC_inner_start_routine(&sb,arg); +#else +return GC_call_with_stack_base(GC_inner_start_routine,arg); +#endif +} +GC_API int WRAP_FUNC(pthread_create)(pthread_t*new_thread, +GC_PTHREAD_CREATE_CONST pthread_attr_t*attr, +void*(*start_routine)(void*),void*arg) +{ +int result; +int detachstate; +word my_flags=0; +struct start_info si; +DCL_LOCK_STATE; +INIT_REAL_SYMS(); +if (!EXPECT(parallel_initialized,TRUE)) +GC_init_parallel(); +if (sem_init(&si.registered,GC_SEM_INIT_PSHARED,0)!=0) +ABORT("sem_init failed"); +si.start_routine=start_routine; +si.arg=arg; +LOCK(); +if (!EXPECT(GC_thr_initialized,TRUE)) +GC_thr_init(); +#ifdef GC_ASSERTIONS +{ +size_t stack_size=0; +if (NULL!=attr){ +if (pthread_attr_getstacksize(attr,&stack_size)!=0) +ABORT("pthread_attr_getstacksize failed"); +} +if (0==stack_size){ +pthread_attr_t my_attr; +if (pthread_attr_init(&my_attr)!=0) +ABORT("pthread_attr_init failed"); +if (pthread_attr_getstacksize(&my_attr,&stack_size)!=0) +ABORT("pthread_attr_getstacksize failed"); +(void)pthread_attr_destroy(&my_attr); +} +if (0==stack_size){ +#ifndef SOLARIS +WARN("Failed to get stack size for assertion checking\n",0); +#endif +stack_size=1000000; +} +GC_ASSERT(stack_size>=65536); +} +#endif +if (NULL==attr){ +detachstate=PTHREAD_CREATE_JOINABLE; +} else { +pthread_attr_getdetachstate(attr,&detachstate); +} +if (PTHREAD_CREATE_DETACHED==detachstate)my_flags|=DETACHED; +si.flags=my_flags; +UNLOCK(); +#ifdef DEBUG_THREADS +GC_log_printf("About to start new thread from thread %p\n", +(void*)pthread_self()); +#endif +set_need_to_lock(); +result=REAL_FUNC(pthread_create)(new_thread,attr,GC_start_routine, +&si); +if (0==result){ +IF_CANCEL(int cancel_state;) +#ifdef DEBUG_THREADS +GC_log_printf("Started thread %p\n",(void*)(*new_thread)); +#endif +DISABLE_CANCEL(cancel_state); +while (0!=sem_wait(&si.registered)){ +#if defined(GC_HAIKU_THREADS) +if (EACCES==errno)continue; +#endif +if (EINTR!=errno)ABORT("sem_wait failed"); +} +RESTORE_CANCEL(cancel_state); +} +sem_destroy(&si.registered); +return(result); +} +#endif +#if defined(USE_SPIN_LOCK)||!defined(NO_PTHREAD_TRYLOCK) +#define GC_PAUSE_SPIN_CYCLES 10 +STATIC void GC_pause(void) +{ +int i; +for (i=0;i < GC_PAUSE_SPIN_CYCLES;++i){ +#if defined(AO_HAVE_compiler_barrier)&&!defined(BASE_ATOMIC_OPS_EMULATED) +AO_compiler_barrier(); +#else +GC_noop1(i); +#endif +} +} +#endif +#ifndef SPIN_MAX +#define SPIN_MAX 128 +#endif +GC_INNER volatile GC_bool GC_collecting=FALSE; +#if (!defined(USE_SPIN_LOCK)&&!defined(NO_PTHREAD_TRYLOCK))||defined(PARALLEL_MARK) +#ifdef LOCK_STATS +volatile AO_t GC_spin_count=0; +volatile AO_t GC_block_count=0; +volatile AO_t GC_unlocked_count=0; +#endif +STATIC void GC_generic_lock(pthread_mutex_t*lock) +{ +#ifndef NO_PTHREAD_TRYLOCK +unsigned pause_length=1; +unsigned i; +if (0==pthread_mutex_trylock(lock)){ +#ifdef LOCK_STATS +(void)AO_fetch_and_add1(&GC_unlocked_count); +#endif +return; +} +for (;pause_length<=SPIN_MAX;pause_length<<=1){ +for (i=0;i < pause_length;++i){ +GC_pause(); +} +switch(pthread_mutex_trylock(lock)){ +case 0: +#ifdef LOCK_STATS +(void)AO_fetch_and_add1(&GC_spin_count); +#endif +return; +case EBUSY: +break; +default: +ABORT("Unexpected error from pthread_mutex_trylock"); +} +} +#endif +#ifdef LOCK_STATS +(void)AO_fetch_and_add1(&GC_block_count); +#endif +pthread_mutex_lock(lock); +} +#endif +#if defined(AO_HAVE_char_load)&&!defined(BASE_ATOMIC_OPS_EMULATED) +#define is_collecting()((GC_bool)AO_char_load((unsigned char*)&GC_collecting)) +#else +#define is_collecting()GC_collecting +#endif +#if defined(USE_SPIN_LOCK) +GC_INNER volatile AO_TS_t GC_allocate_lock=AO_TS_INITIALIZER; +#define low_spin_max 30 +#define high_spin_max SPIN_MAX +static volatile AO_t spin_max=low_spin_max; +static volatile AO_t last_spins=0; +GC_INNER void GC_lock(void) +{ +unsigned my_spin_max; +unsigned my_last_spins; +unsigned i; +if (AO_test_and_set_acquire(&GC_allocate_lock)==AO_TS_CLEAR){ +return; +} +my_spin_max=(unsigned)AO_load(&spin_max); +my_last_spins=(unsigned)AO_load(&last_spins); +for (i=0;i < my_spin_max;i++){ +if (is_collecting()||GC_nprocs==1) +goto yield; +if (i < my_last_spins/2){ +GC_pause(); +continue; +} +if (AO_test_and_set_acquire(&GC_allocate_lock)==AO_TS_CLEAR){ +AO_store(&last_spins,(AO_t)i); +AO_store(&spin_max,(AO_t)high_spin_max); +return; +} +} +AO_store(&spin_max,(AO_t)low_spin_max); +yield: +for (i=0;;++i){ +if (AO_test_and_set_acquire(&GC_allocate_lock)==AO_TS_CLEAR){ +return; +} +#define SLEEP_THRESHOLD 12 +if (i < SLEEP_THRESHOLD){ +sched_yield(); +} else { +struct timespec ts; +if (i > 24)i=24; +ts.tv_sec=0; +ts.tv_nsec=1< 2||(glibc_major==2&&glibc_minor>=19)){ +if (0!=pthread_mutexattr_init(&mattr)){ +ABORT("pthread_mutexattr_init failed"); +} +if (0!=pthread_mutexattr_settype(&mattr,PTHREAD_MUTEX_NORMAL)){ +ABORT("pthread_mutexattr_settype failed"); +} +if (0!=pthread_mutex_init(&mark_mutex,&mattr)){ +ABORT("pthread_mutex_init failed"); +} +(void)pthread_mutexattr_destroy(&mattr); +} +#endif +} +GC_INNER void GC_acquire_mark_lock(void) +{ +#if defined(NUMERIC_THREAD_ID_UNIQUE)&&!defined(THREAD_SANITIZER) +GC_ASSERT(GC_mark_lock_holder!=NUMERIC_THREAD_ID(pthread_self())); +#endif +GC_generic_lock(&mark_mutex); +SET_MARK_LOCK_HOLDER; +} +GC_INNER void GC_release_mark_lock(void) +{ +UNSET_MARK_LOCK_HOLDER; +if (pthread_mutex_unlock(&mark_mutex)!=0){ +ABORT("pthread_mutex_unlock failed"); +} +} +STATIC void GC_wait_builder(void) +{ +ASSERT_CANCEL_DISABLED(); +UNSET_MARK_LOCK_HOLDER; +if (pthread_cond_wait(&builder_cv,&mark_mutex)!=0){ +ABORT("pthread_cond_wait failed"); +} +GC_ASSERT(GC_mark_lock_holder==NO_THREAD); +SET_MARK_LOCK_HOLDER; +} +GC_INNER void GC_wait_for_reclaim(void) +{ +GC_acquire_mark_lock(); +while (GC_fl_builder_count > 0){ +GC_wait_builder(); +} +GC_release_mark_lock(); +} +GC_INNER void GC_notify_all_builder(void) +{ +GC_ASSERT(GC_mark_lock_holder==NUMERIC_THREAD_ID(pthread_self())); +if (pthread_cond_broadcast(&builder_cv)!=0){ +ABORT("pthread_cond_broadcast failed"); +} +} +GC_INNER void GC_wait_marker(void) +{ +ASSERT_CANCEL_DISABLED(); +GC_ASSERT(GC_parallel); +UNSET_MARK_LOCK_HOLDER; +if (pthread_cond_wait(&mark_cv,&mark_mutex)!=0){ +ABORT("pthread_cond_wait failed"); +} +GC_ASSERT(GC_mark_lock_holder==NO_THREAD); +SET_MARK_LOCK_HOLDER; +} +GC_INNER void GC_notify_all_marker(void) +{ +GC_ASSERT(GC_parallel); +if (pthread_cond_broadcast(&mark_cv)!=0){ +ABORT("pthread_cond_broadcast failed"); +} +} +#endif +#ifdef PTHREAD_REGISTER_CANCEL_WEAK_STUBS +EXTERN_C_BEGIN +extern void __pthread_register_cancel(void)__attribute__((__weak__)); +extern void __pthread_unregister_cancel(void)__attribute__((__weak__)); +EXTERN_C_END +void __pthread_register_cancel(void){} +void __pthread_unregister_cancel(void){} +#endif +#endif +#if defined(USE_CUSTOM_SPECIFIC) +static const tse invalid_tse={INVALID_QTID,0,0,INVALID_THREADID}; +GC_INNER int GC_key_create_inner(tsd**key_ptr) +{ +int i; +int ret; +tsd*result; +GC_ASSERT(I_HOLD_LOCK()); +GC_ASSERT((word)(&invalid_tse.next)% sizeof(tse*)==0); +result=(tsd*)MALLOC_CLEAR(sizeof(tsd)); +if (NULL==result)return ENOMEM; +ret=pthread_mutex_init(&result->lock,NULL); +if (ret!=0)return ret; +for (i=0;i < TS_CACHE_SIZE;++i){ +result->cache[i]=( tse*)&invalid_tse; +} +#ifdef GC_ASSERTIONS +for (i=0;i < TS_HASH_SIZE;++i){ +GC_ASSERT(result->hash[i].p==0); +} +#endif +*key_ptr=result; +return 0; +} +GC_INNER int GC_setspecific(tsd*key,void*value) +{ +pthread_t self=pthread_self(); +unsigned hash_val=HASH(self); +volatile tse*entry; +GC_ASSERT(I_HOLD_LOCK()); +GC_ASSERT(self!=INVALID_THREADID); +GC_dont_gc++; +entry=(volatile tse*)MALLOC_CLEAR(sizeof(tse)); +GC_dont_gc--; +if (0==entry)return ENOMEM; +pthread_mutex_lock(&(key->lock)); +entry->next=key->hash[hash_val].p; +entry->thread=self; +entry->value=TS_HIDE_VALUE(value); +GC_ASSERT(entry->qtid==INVALID_QTID); +AO_store_release(&key->hash[hash_val].ao,(AO_t)entry); +GC_dirty(( void*)entry); +GC_dirty(key->hash+hash_val); +if (pthread_mutex_unlock(&key->lock)!=0) +ABORT("pthread_mutex_unlock failed (setspecific)"); +return 0; +} +GC_INNER void GC_remove_specific_after_fork(tsd*key,pthread_t t) +{ +unsigned hash_val=HASH(t); +tse*entry; +tse*prev=NULL; +#ifdef CAN_HANDLE_FORK +GC_ASSERT(I_HOLD_LOCK()); +#endif +pthread_mutex_lock(&(key->lock)); +entry=key->hash[hash_val].p; +while (entry!=NULL&&!THREAD_EQUAL(entry->thread,t)){ +prev=entry; +entry=entry->next; +} +if (entry!=NULL){ +entry->qtid=INVALID_QTID; +if (NULL==prev){ +key->hash[hash_val].p=entry->next; +GC_dirty(key->hash+hash_val); +} else { +prev->next=entry->next; +GC_dirty(prev); +} +} +#ifdef LINT2 +GC_noop1((word)entry); +#endif +if (pthread_mutex_unlock(&key->lock)!=0) +ABORT("pthread_mutex_unlock failed (remove_specific after fork)"); +} +GC_INNER void*GC_slow_getspecific(tsd*key,word qtid, +tse*volatile*cache_ptr) +{ +pthread_t self=pthread_self(); +tse*entry=key->hash[HASH(self)].p; +GC_ASSERT(qtid!=INVALID_QTID); +while (entry!=NULL&&!THREAD_EQUAL(entry->thread,self)){ +entry=entry->next; +} +if (entry==NULL)return NULL; +entry->qtid=(AO_t)qtid; +*cache_ptr=entry; +return TS_REVEAL_PTR(entry->value); +} +#ifdef GC_ASSERTIONS +void GC_check_tsd_marks(tsd*key) +{ +int i; +tse*p; +if (!GC_is_marked(GC_base(key))){ +ABORT("Unmarked thread-specific-data table"); +} +for (i=0;i < TS_HASH_SIZE;++i){ +for (p=key->hash[i].p;p!=0;p=p->next){ +if (!GC_is_marked(GC_base(p))){ +ABORT_ARG1("Unmarked thread-specific-data entry", +" at %p",(void*)p); +} +} +} +for (i=0;i < TS_CACHE_SIZE;++i){ +p=key->cache[i]; +if (p!=&invalid_tse&&!GC_is_marked(GC_base(p))){ +ABORT_ARG1("Unmarked cached thread-specific-data entry", +" at %p",(void*)p); +} +} +} +#endif +#endif +#if defined(GC_WIN32_THREADS) +#ifdef THREAD_LOCAL_ALLOC +#endif +#if!defined(USE_PTHREAD_LOCKS) +GC_INNER CRITICAL_SECTION GC_allocate_ml; +#ifdef GC_ASSERTIONS +GC_INNER DWORD GC_lock_holder=NO_THREAD; +#endif +#else +GC_INNER pthread_mutex_t GC_allocate_ml=PTHREAD_MUTEX_INITIALIZER; +#ifdef GC_ASSERTIONS +GC_INNER unsigned long GC_lock_holder=NO_THREAD; +#endif +#endif +#undef CreateThread +#undef ExitThread +#undef _beginthreadex +#undef _endthreadex +#ifdef GC_PTHREADS +#include +#undef pthread_create +#undef pthread_join +#undef pthread_detach +#ifndef GC_NO_PTHREAD_SIGMASK +#undef pthread_sigmask +#endif +STATIC void*GC_pthread_start(void*arg); +STATIC void GC_thread_exit_proc(void*arg); +#include +#ifdef CAN_CALL_ATFORK +#include +#endif +#elif!defined(MSWINCE) +#include +#include +#endif +static ptr_t copy_ptr_regs(word*regs,const CONTEXT*pcontext); +#if defined(I386) +#ifdef WOW64_THREAD_CONTEXT_WORKAROUND +#define PUSHED_REGS_COUNT 9 +#else +#define PUSHED_REGS_COUNT 7 +#endif +#elif defined(X86_64)||defined(SHx) +#define PUSHED_REGS_COUNT 15 +#elif defined(ARM32) +#define PUSHED_REGS_COUNT 13 +#elif defined(AARCH64) +#define PUSHED_REGS_COUNT 30 +#elif defined(MIPS)||defined(ALPHA) +#define PUSHED_REGS_COUNT 28 +#elif defined(PPC) +#define PUSHED_REGS_COUNT 29 +#endif +#if (defined(GC_DLL)||defined(GC_INSIDE_DLL))&&!defined(NO_CRT)&&!defined(GC_NO_THREADS_DISCOVERY)&&!defined(MSWINCE)&&!defined(THREAD_LOCAL_ALLOC)&&!defined(GC_PTHREADS) +#ifdef GC_DISCOVER_TASK_THREADS +#define GC_win32_dll_threads TRUE +#else +STATIC GC_bool GC_win32_dll_threads=FALSE; +#endif +#else +#ifndef GC_NO_THREADS_DISCOVERY +#define GC_NO_THREADS_DISCOVERY +#endif +#define GC_win32_dll_threads FALSE +#undef MAX_THREADS +#define MAX_THREADS 1 +#endif +typedef LONG*IE_t; +STATIC GC_bool GC_thr_initialized=FALSE; +#ifndef GC_ALWAYS_MULTITHREADED +GC_INNER GC_bool GC_need_to_lock=FALSE; +#endif +static GC_bool parallel_initialized=FALSE; +GC_API void GC_CALL GC_use_threads_discovery(void) +{ +#ifdef GC_NO_THREADS_DISCOVERY +ABORT("GC DllMain-based thread registration unsupported"); +#else +GC_ASSERT(!parallel_initialized); +#ifndef GC_DISCOVER_TASK_THREADS +GC_win32_dll_threads=TRUE; +#endif +GC_init_parallel(); +#endif +} +#define ADDR_LIMIT ((ptr_t)GC_WORD_MAX) +struct GC_Thread_Rep { +union { +#ifndef GC_NO_THREADS_DISCOVERY +volatile AO_t in_use; +LONG long_in_use; +#endif +struct GC_Thread_Rep*next; +} tm; +DWORD id; +#ifdef MSWINCE +#define THREAD_HANDLE(t)(HANDLE)(word)(t)->id +#else +HANDLE handle; +#define THREAD_HANDLE(t)(t)->handle +#endif +ptr_t stack_base; +ptr_t last_stack_min; +#ifdef IA64 +ptr_t backing_store_end; +ptr_t backing_store_ptr; +#elif defined(I386) +ptr_t initial_stack_base; +#endif +ptr_t thread_blocked_sp; +struct GC_traced_stack_sect_s*traced_stack_sect; +unsigned short finalizer_skipped; +unsigned char finalizer_nested; +unsigned char suspended; +#ifdef GC_PTHREADS +unsigned char flags; +#define FINISHED 1 +#define DETACHED 2 +#define KNOWN_FINISHED(t)(((t)->flags)&FINISHED) +pthread_t pthread_id; +void*status; +#else +#define KNOWN_FINISHED(t)0 +#endif +#ifdef THREAD_LOCAL_ALLOC +struct thread_local_freelists tlfs; +#endif +#ifdef RETRY_GET_THREAD_CONTEXT +ptr_t context_sp; +word context_regs[PUSHED_REGS_COUNT]; +#endif +}; +typedef struct GC_Thread_Rep*GC_thread; +typedef volatile struct GC_Thread_Rep*GC_vthread; +#ifndef GC_NO_THREADS_DISCOVERY +STATIC DWORD GC_main_thread=0; +STATIC volatile AO_t GC_attached_thread=FALSE; +STATIC volatile GC_bool GC_please_stop=FALSE; +#elif defined(GC_ASSERTIONS) +STATIC GC_bool GC_please_stop=FALSE; +#endif +#if defined(WRAP_MARK_SOME)&&!defined(GC_PTHREADS) +GC_INNER GC_bool GC_started_thread_while_stopped(void) +{ +#ifndef GC_NO_THREADS_DISCOVERY +if (GC_win32_dll_threads){ +#ifdef AO_HAVE_compare_and_swap_release +if (AO_compare_and_swap_release(&GC_attached_thread,TRUE, +FALSE)) +return TRUE; +#else +AO_nop_full(); +if (AO_load(&GC_attached_thread)){ +AO_store(&GC_attached_thread,FALSE); +return TRUE; +} +#endif +} +#endif +return FALSE; +} +#endif +#ifndef MAX_THREADS +#define MAX_THREADS 512 +#endif +volatile struct GC_Thread_Rep dll_thread_table[MAX_THREADS]; +STATIC volatile LONG GC_max_thread_index=0; +#ifndef THREAD_TABLE_SZ +#define THREAD_TABLE_SZ 256 +#endif +#define THREAD_TABLE_INDEX(id)(int)((((id)>>8)^(id))% THREAD_TABLE_SZ) +STATIC GC_thread GC_threads[THREAD_TABLE_SZ]; +static struct GC_Thread_Rep first_thread; +static GC_bool first_thread_used=FALSE; +STATIC GC_thread GC_new_thread(DWORD id) +{ +int hv=THREAD_TABLE_INDEX(id); +GC_thread result; +#ifdef DEBUG_THREADS +GC_log_printf("Creating thread 0x%lx\n",(long)id); +if (GC_threads[hv]!=NULL) +GC_log_printf("Hash collision at GC_threads[%d]\n",hv); +#endif +GC_ASSERT(I_HOLD_LOCK()); +if (!EXPECT(first_thread_used,TRUE)){ +result=&first_thread; +first_thread_used=TRUE; +GC_ASSERT(NULL==GC_threads[hv]); +} else { +GC_ASSERT(!GC_win32_dll_threads); +result=(struct GC_Thread_Rep*) +GC_INTERNAL_MALLOC(sizeof(struct GC_Thread_Rep),NORMAL); +if (result==0)return(0); +} +result->tm.next=GC_threads[hv]; +GC_threads[hv]=result; +#ifdef GC_PTHREADS +GC_ASSERT(result->flags==0); +#endif +GC_ASSERT(result->thread_blocked_sp==NULL); +if (EXPECT(result!=&first_thread,TRUE)) +GC_dirty(result); +return(result); +} +GC_INNER GC_bool GC_in_thread_creation=FALSE; +GC_INLINE void GC_record_stack_base(GC_vthread me, +const struct GC_stack_base*sb) +{ +me->stack_base=(ptr_t)sb->mem_base; +#ifdef IA64 +me->backing_store_end=(ptr_t)sb->reg_base; +#elif defined(I386) +me->initial_stack_base=(ptr_t)sb->mem_base; +#endif +if (me->stack_base==NULL) +ABORT("Bad stack base in GC_register_my_thread"); +} +STATIC GC_thread GC_register_my_thread_inner(const struct GC_stack_base*sb, +DWORD thread_id) +{ +GC_vthread me; +#if defined(MPROTECT_VDB)&&!defined(CYGWIN32) +if (GC_auto_incremental +#ifdef GWW_VDB +&&!GC_gww_dirty_init() +#endif +) +GC_set_write_fault_handler(); +#endif +#ifndef GC_NO_THREADS_DISCOVERY +if (GC_win32_dll_threads){ +int i; +for (i=0; +InterlockedExchange(&dll_thread_table[i].tm.long_in_use,1)!=0; +i++){ +if (i==MAX_THREADS - 1) +ABORT("Too many threads"); +} +while (i > GC_max_thread_index){ +InterlockedIncrement((IE_t)&GC_max_thread_index); +} +if (GC_max_thread_index>=MAX_THREADS){ +GC_max_thread_index=MAX_THREADS - 1; +} +me=dll_thread_table+i; +} else +#endif +{ +GC_ASSERT(I_HOLD_LOCK()); +GC_in_thread_creation=TRUE; +me=GC_new_thread(thread_id); +GC_in_thread_creation=FALSE; +if (me==0) +ABORT("Failed to allocate memory for thread registering"); +} +#ifdef GC_PTHREADS +me->pthread_id=pthread_self(); +#endif +#ifndef MSWINCE +if (!DuplicateHandle(GetCurrentProcess(),GetCurrentThread(), +GetCurrentProcess(), +(HANDLE*)&(me->handle), +0,FALSE, +DUPLICATE_SAME_ACCESS)){ +ABORT_ARG1("DuplicateHandle failed", +":errcode=0x%X",(unsigned)GetLastError()); +} +#endif +me->last_stack_min=ADDR_LIMIT; +GC_record_stack_base(me,sb); +me->id=thread_id; +#if defined(THREAD_LOCAL_ALLOC) +GC_init_thread_local((GC_tlfs)(&(me->tlfs))); +#endif +#ifndef GC_NO_THREADS_DISCOVERY +if (GC_win32_dll_threads){ +if (GC_please_stop){ +AO_store(&GC_attached_thread,TRUE); +AO_nop_full(); +} +} else +#endif +{ +GC_ASSERT(!GC_please_stop); +} +return (GC_thread)(me); +} +GC_INLINE LONG GC_get_max_thread_index(void) +{ +LONG my_max=GC_max_thread_index; +if (my_max>=MAX_THREADS)return MAX_THREADS - 1; +return my_max; +} +STATIC GC_thread GC_lookup_thread_inner(DWORD thread_id) +{ +#ifndef GC_NO_THREADS_DISCOVERY +if (GC_win32_dll_threads){ +int i; +LONG my_max=GC_get_max_thread_index(); +for (i=0;i<=my_max&& +(!AO_load_acquire(&dll_thread_table[i].tm.in_use) +||dll_thread_table[i].id!=thread_id); +i++){ +} +return i<=my_max?(GC_thread)(dll_thread_table+i):NULL; +} else +#endif +{ +GC_thread p=GC_threads[THREAD_TABLE_INDEX(thread_id)]; +GC_ASSERT(I_HOLD_LOCK()); +while (p!=0&&p->id!=thread_id)p=p->tm.next; +return(p); +} +} +#ifdef LINT2 +#define CHECK_LOOKUP_MY_THREAD(me)if (!(me))ABORT("GC_lookup_thread_inner(GetCurrentThreadId)failed") +#else +#define CHECK_LOOKUP_MY_THREAD(me) +#endif +GC_INNER void GC_reset_finalizer_nested(void) +{ +GC_thread me=GC_lookup_thread_inner(GetCurrentThreadId()); +CHECK_LOOKUP_MY_THREAD(me); +me->finalizer_nested=0; +} +GC_INNER unsigned char*GC_check_finalizer_nested(void) +{ +GC_thread me=GC_lookup_thread_inner(GetCurrentThreadId()); +unsigned nesting_level; +CHECK_LOOKUP_MY_THREAD(me); +nesting_level=me->finalizer_nested; +if (nesting_level){ +if (++me->finalizer_skipped < (1U<finalizer_skipped=0; +} +me->finalizer_nested=(unsigned char)(nesting_level+1); +return&me->finalizer_nested; +} +#if defined(GC_ASSERTIONS)&&defined(THREAD_LOCAL_ALLOC) +GC_bool GC_is_thread_tsd_valid(void*tsd) +{ +GC_thread me; +DCL_LOCK_STATE; +LOCK(); +me=GC_lookup_thread_inner(GetCurrentThreadId()); +UNLOCK(); +return (word)tsd>=(word)(&me->tlfs) +&&(word)tsd < (word)(&me->tlfs)+sizeof(me->tlfs); +} +#endif +GC_API int GC_CALL GC_thread_is_registered(void) +{ +DWORD thread_id=GetCurrentThreadId(); +GC_thread me; +DCL_LOCK_STATE; +LOCK(); +me=GC_lookup_thread_inner(thread_id); +UNLOCK(); +return me!=NULL; +} +GC_API void GC_CALL GC_register_altstack(void*stack GC_ATTR_UNUSED, +GC_word stack_size GC_ATTR_UNUSED, +void*altstack GC_ATTR_UNUSED, +GC_word altstack_size GC_ATTR_UNUSED) +{ +} +#if defined(MPROTECT_VDB) +#define UNPROTECT_THREAD(t)if (!GC_win32_dll_threads&&GC_auto_incremental&&t!=&first_thread){ GC_ASSERT(SMALL_OBJ(GC_size(t)));GC_remove_protection(HBLKPTR(t),1,FALSE);} else (void)0 +#else +#define UNPROTECT_THREAD(t)(void)0 +#endif +#ifdef CYGWIN32 +#define GC_PTHREAD_PTRVAL(pthread_id)pthread_id +#elif defined(GC_WIN32_PTHREADS)||defined(GC_PTHREADS_PARAMARK) +#include +#if defined(__WINPTHREADS_VERSION_MAJOR) +#define GC_PTHREAD_PTRVAL(pthread_id)pthread_id +#else +#define GC_PTHREAD_PTRVAL(pthread_id)pthread_id.p +#endif +#endif +STATIC void GC_delete_gc_thread_no_free(GC_vthread t) +{ +#ifndef MSWINCE +CloseHandle(t->handle); +#endif +#ifndef GC_NO_THREADS_DISCOVERY +if (GC_win32_dll_threads){ +t->stack_base=0; +t->id=0; +t->suspended=FALSE; +#ifdef RETRY_GET_THREAD_CONTEXT +t->context_sp=NULL; +#endif +AO_store_release(&t->tm.in_use,FALSE); +} else +#endif +{ +DWORD id=((GC_thread)t)->id; +int hv=THREAD_TABLE_INDEX(id); +GC_thread p=GC_threads[hv]; +GC_thread prev=NULL; +GC_ASSERT(I_HOLD_LOCK()); +while (p!=(GC_thread)t){ +prev=p; +p=p->tm.next; +} +if (prev==0){ +GC_threads[hv]=p->tm.next; +} else { +GC_ASSERT(prev!=&first_thread); +prev->tm.next=p->tm.next; +GC_dirty(prev); +} +} +} +STATIC void GC_delete_thread(DWORD id) +{ +if (GC_win32_dll_threads){ +GC_vthread t=GC_lookup_thread_inner(id); +if (0==t){ +WARN("Removing nonexistent thread,id=%" WARN_PRIdPTR "\n",id); +} else { +GC_delete_gc_thread_no_free(t); +} +} else { +int hv=THREAD_TABLE_INDEX(id); +GC_thread p=GC_threads[hv]; +GC_thread prev=NULL; +GC_ASSERT(I_HOLD_LOCK()); +while (p->id!=id){ +prev=p; +p=p->tm.next; +} +#ifndef MSWINCE +CloseHandle(p->handle); +#endif +if (prev==0){ +GC_threads[hv]=p->tm.next; +} else { +GC_ASSERT(prev!=&first_thread); +prev->tm.next=p->tm.next; +GC_dirty(prev); +} +if (EXPECT(p!=&first_thread,TRUE)){ +GC_INTERNAL_FREE(p); +} +} +} +GC_API void GC_CALL GC_allow_register_threads(void) +{ +GC_ASSERT(GC_lookup_thread_inner(GetCurrentThreadId())!=0); +#if!defined(GC_ALWAYS_MULTITHREADED)&&!defined(PARALLEL_MARK)&&!defined(GC_NO_THREADS_DISCOVERY) +parallel_initialized=TRUE; +#endif +set_need_to_lock(); +} +GC_API int GC_CALL GC_register_my_thread(const struct GC_stack_base*sb) +{ +GC_thread me; +DWORD thread_id=GetCurrentThreadId(); +DCL_LOCK_STATE; +if (GC_need_to_lock==FALSE) +ABORT("Threads explicit registering is not previously enabled"); +LOCK(); +me=GC_lookup_thread_inner(thread_id); +if (me==0){ +#ifdef GC_PTHREADS +me=GC_register_my_thread_inner(sb,thread_id); +#if defined(CPPCHECK) +GC_noop1(me->flags); +#endif +me->flags|=DETACHED; +#else +GC_register_my_thread_inner(sb,thread_id); +#endif +UNLOCK(); +return GC_SUCCESS; +} else +#ifdef GC_PTHREADS +if ((me->flags&FINISHED)!=0){ +GC_record_stack_base(me,sb); +me->flags&=~FINISHED; +#ifdef THREAD_LOCAL_ALLOC +GC_init_thread_local((GC_tlfs)(&me->tlfs)); +#endif +UNLOCK(); +return GC_SUCCESS; +} else +#endif +{ +UNLOCK(); +return GC_DUPLICATE; +} +} +STATIC void GC_wait_for_gc_completion(GC_bool wait_for_all) +{ +GC_ASSERT(I_HOLD_LOCK()); +if (GC_incremental&&GC_collection_in_progress()){ +word old_gc_no=GC_gc_no; +do { +ENTER_GC(); +GC_in_thread_creation=TRUE; +GC_collect_a_little_inner(1); +GC_in_thread_creation=FALSE; +EXIT_GC(); +UNLOCK(); +Sleep(0); +LOCK(); +} while (GC_incremental&&GC_collection_in_progress() +&&(wait_for_all||old_gc_no==GC_gc_no)); +} +} +GC_API int GC_CALL GC_unregister_my_thread(void) +{ +DCL_LOCK_STATE; +#ifdef DEBUG_THREADS +GC_log_printf("Unregistering thread 0x%lx\n",(long)GetCurrentThreadId()); +#endif +if (GC_win32_dll_threads){ +#if defined(THREAD_LOCAL_ALLOC) +GC_ASSERT(FALSE); +#else +GC_delete_thread(GetCurrentThreadId()); +#endif +} else { +#if defined(THREAD_LOCAL_ALLOC)||defined(GC_PTHREADS) +GC_thread me; +#endif +DWORD thread_id=GetCurrentThreadId(); +LOCK(); +GC_wait_for_gc_completion(FALSE); +#if defined(THREAD_LOCAL_ALLOC)||defined(GC_PTHREADS) +me=GC_lookup_thread_inner(thread_id); +CHECK_LOOKUP_MY_THREAD(me); +GC_ASSERT(!KNOWN_FINISHED(me)); +#endif +#if defined(THREAD_LOCAL_ALLOC) +GC_ASSERT(GC_getspecific(GC_thread_key)==&me->tlfs); +GC_destroy_thread_local(&(me->tlfs)); +#endif +#ifdef GC_PTHREADS +if ((me->flags&DETACHED)==0){ +me->flags|=FINISHED; +} else +#endif +{ +GC_delete_thread(thread_id); +} +#if defined(THREAD_LOCAL_ALLOC) +GC_remove_specific(GC_thread_key); +#endif +UNLOCK(); +} +return GC_SUCCESS; +} +GC_INNER void GC_do_blocking_inner(ptr_t data,void*context GC_ATTR_UNUSED) +{ +struct blocking_data*d=(struct blocking_data*)data; +DWORD thread_id=GetCurrentThreadId(); +GC_thread me; +#ifdef IA64 +ptr_t stack_ptr=GC_save_regs_in_stack(); +#endif +DCL_LOCK_STATE; +LOCK(); +me=GC_lookup_thread_inner(thread_id); +CHECK_LOOKUP_MY_THREAD(me); +GC_ASSERT(me->thread_blocked_sp==NULL); +#ifdef IA64 +me->backing_store_ptr=stack_ptr; +#endif +me->thread_blocked_sp=(ptr_t)&d; +UNLOCK(); +d->client_data=(d->fn)(d->client_data); +LOCK(); +#if defined(CPPCHECK) +GC_noop1((word)me->thread_blocked_sp); +#endif +me->thread_blocked_sp=NULL; +UNLOCK(); +} +GC_API void*GC_CALL GC_call_with_gc_active(GC_fn_type fn, +void*client_data) +{ +struct GC_traced_stack_sect_s stacksect; +DWORD thread_id=GetCurrentThreadId(); +GC_thread me; +DCL_LOCK_STATE; +LOCK(); +me=GC_lookup_thread_inner(thread_id); +CHECK_LOOKUP_MY_THREAD(me); +GC_ASSERT(me->stack_base!=NULL); +if ((word)me->stack_base < (word)(&stacksect)){ +me->stack_base=(ptr_t)(&stacksect); +#if defined(I386) +me->initial_stack_base=me->stack_base; +#endif +} +if (me->thread_blocked_sp==NULL){ +UNLOCK(); +client_data=fn(client_data); +GC_noop1(COVERT_DATAFLOW(&stacksect)); +return client_data; +} +stacksect.saved_stack_ptr=me->thread_blocked_sp; +#ifdef IA64 +stacksect.backing_store_end=GC_save_regs_in_stack(); +stacksect.saved_backing_store_ptr=me->backing_store_ptr; +#endif +stacksect.prev=me->traced_stack_sect; +me->thread_blocked_sp=NULL; +me->traced_stack_sect=&stacksect; +UNLOCK(); +client_data=fn(client_data); +GC_ASSERT(me->thread_blocked_sp==NULL); +GC_ASSERT(me->traced_stack_sect==&stacksect); +LOCK(); +#if defined(CPPCHECK) +GC_noop1((word)me->traced_stack_sect); +#endif +me->traced_stack_sect=stacksect.prev; +#ifdef IA64 +me->backing_store_ptr=stacksect.saved_backing_store_ptr; +#endif +me->thread_blocked_sp=stacksect.saved_stack_ptr; +UNLOCK(); +return client_data; +} +GC_API void GC_CALL GC_set_stackbottom(void*gc_thread_handle, +const struct GC_stack_base*sb) +{ +GC_thread t=(GC_thread)gc_thread_handle; +GC_ASSERT(sb->mem_base!=NULL); +if (!EXPECT(GC_is_initialized,TRUE)){ +GC_ASSERT(NULL==t); +GC_stackbottom=(char*)sb->mem_base; +#ifdef IA64 +GC_register_stackbottom=(ptr_t)sb->reg_base; +#endif +return; +} +GC_ASSERT(I_HOLD_LOCK()); +if (NULL==t){ +t=GC_lookup_thread_inner(GetCurrentThreadId()); +CHECK_LOOKUP_MY_THREAD(t); +} +GC_ASSERT(!KNOWN_FINISHED(t)); +GC_ASSERT(NULL==t->thread_blocked_sp +&&NULL==t->traced_stack_sect); +t->stack_base=(ptr_t)sb->mem_base; +t->last_stack_min=ADDR_LIMIT; +#ifdef IA64 +t->backing_store_end=(ptr_t)sb->reg_base; +#endif +} +GC_API void*GC_CALL GC_get_my_stackbottom(struct GC_stack_base*sb) +{ +DWORD thread_id=GetCurrentThreadId(); +GC_thread me; +DCL_LOCK_STATE; +LOCK(); +me=GC_lookup_thread_inner(thread_id); +CHECK_LOOKUP_MY_THREAD(me); +sb->mem_base=me->stack_base; +#ifdef IA64 +sb->reg_base=me->backing_store_end; +#endif +UNLOCK(); +return (void*)me; +} +#ifdef GC_PTHREADS +#define PTHREAD_MAP_SIZE 512 +DWORD GC_pthread_map_cache[PTHREAD_MAP_SIZE]={0}; +#define PTHREAD_MAP_INDEX(pthread_id)((NUMERIC_THREAD_ID(pthread_id)>>5)% PTHREAD_MAP_SIZE) +#define SET_PTHREAD_MAP_CACHE(pthread_id,win32_id)(void)(GC_pthread_map_cache[PTHREAD_MAP_INDEX(pthread_id)]=(win32_id)) +#define GET_PTHREAD_MAP_CACHE(pthread_id)GC_pthread_map_cache[PTHREAD_MAP_INDEX(pthread_id)] +STATIC GC_thread GC_lookup_pthread(pthread_t id) +{ +#ifndef GC_NO_THREADS_DISCOVERY +if (GC_win32_dll_threads){ +int i; +LONG my_max=GC_get_max_thread_index(); +for (i=0;i<=my_max&& +(!AO_load_acquire(&dll_thread_table[i].tm.in_use) +||THREAD_EQUAL(dll_thread_table[i].pthread_id,id)); +i++){ +} +return i<=my_max?(GC_thread)(dll_thread_table+i):NULL; +} else +#endif +{ +DWORD win32_id=GET_PTHREAD_MAP_CACHE(id); +int hv_guess=THREAD_TABLE_INDEX(win32_id); +int hv; +GC_thread p; +DCL_LOCK_STATE; +LOCK(); +for (p=GC_threads[hv_guess];0!=p;p=p->tm.next){ +if (THREAD_EQUAL(p->pthread_id,id)) +goto foundit; +} +for (hv=0;hv < THREAD_TABLE_SZ;++hv){ +for (p=GC_threads[hv];0!=p;p=p->tm.next){ +if (THREAD_EQUAL(p->pthread_id,id)) +goto foundit; +} +} +p=0; +foundit: +UNLOCK(); +return p; +} +} +#endif +#ifdef CAN_HANDLE_FORK +STATIC void GC_remove_all_threads_but_me(void) +{ +int hv; +GC_thread me=NULL; +DWORD thread_id; +pthread_t pthread_id=pthread_self(); +GC_ASSERT(!GC_win32_dll_threads); +for (hv=0;hv < THREAD_TABLE_SZ;++hv){ +GC_thread p,next; +for (p=GC_threads[hv];0!=p;p=next){ +next=p->tm.next; +if (THREAD_EQUAL(p->pthread_id,pthread_id) +&&me==NULL){ +me=p; +p->tm.next=0; +} else { +#ifdef THREAD_LOCAL_ALLOC +if ((p->flags&FINISHED)==0){ +GC_remove_specific_after_fork(GC_thread_key,p->pthread_id); +} +#endif +if (&first_thread!=p) +GC_INTERNAL_FREE(p); +} +} +GC_threads[hv]=NULL; +} +GC_ASSERT(me!=NULL); +thread_id=GetCurrentThreadId(); +GC_threads[THREAD_TABLE_INDEX(thread_id)]=me; +me->id=thread_id; +#ifndef MSWINCE +if (!DuplicateHandle(GetCurrentProcess(),GetCurrentThread(), +GetCurrentProcess(),(HANDLE*)&me->handle, +0,FALSE, +DUPLICATE_SAME_ACCESS)) +ABORT("DuplicateHandle failed"); +#endif +#if defined(THREAD_LOCAL_ALLOC)&&!defined(USE_CUSTOM_SPECIFIC) +if (GC_setspecific(GC_thread_key,&me->tlfs)!=0) +ABORT("GC_setspecific failed (in child)"); +#endif +} +static void fork_prepare_proc(void) +{ +LOCK(); +#ifdef PARALLEL_MARK +if (GC_parallel) +GC_wait_for_reclaim(); +#endif +GC_wait_for_gc_completion(TRUE); +#ifdef PARALLEL_MARK +if (GC_parallel) +GC_acquire_mark_lock(); +#endif +} +static void fork_parent_proc(void) +{ +#ifdef PARALLEL_MARK +if (GC_parallel) +GC_release_mark_lock(); +#endif +UNLOCK(); +} +static void fork_child_proc(void) +{ +#ifdef PARALLEL_MARK +if (GC_parallel){ +GC_release_mark_lock(); +GC_parallel=FALSE; +} +#endif +GC_remove_all_threads_but_me(); +UNLOCK(); +} +GC_API void GC_CALL GC_atfork_prepare(void) +{ +if (!EXPECT(GC_is_initialized,TRUE))GC_init(); +if (GC_handle_fork<=0) +fork_prepare_proc(); +} +GC_API void GC_CALL GC_atfork_parent(void) +{ +if (GC_handle_fork<=0) +fork_parent_proc(); +} +GC_API void GC_CALL GC_atfork_child(void) +{ +if (GC_handle_fork<=0) +fork_child_proc(); +} +#endif +void GC_push_thread_structures(void) +{ +GC_ASSERT(I_HOLD_LOCK()); +#ifndef GC_NO_THREADS_DISCOVERY +if (GC_win32_dll_threads){ +} else +#endif +{ +GC_PUSH_ALL_SYM(GC_threads); +} +#if defined(THREAD_LOCAL_ALLOC) +GC_PUSH_ALL_SYM(GC_thread_key); +#endif +} +#ifdef WOW64_THREAD_CONTEXT_WORKAROUND +#ifndef CONTEXT_EXCEPTION_ACTIVE +#define CONTEXT_EXCEPTION_ACTIVE 0x08000000 +#define CONTEXT_EXCEPTION_REQUEST 0x40000000 +#define CONTEXT_EXCEPTION_REPORTING 0x80000000 +#endif +static BOOL isWow64; +#define GET_THREAD_CONTEXT_FLAGS (isWow64?CONTEXT_INTEGER|CONTEXT_CONTROL|CONTEXT_EXCEPTION_REQUEST|CONTEXT_SEGMENTS:CONTEXT_INTEGER|CONTEXT_CONTROL) +#else +#define GET_THREAD_CONTEXT_FLAGS (CONTEXT_INTEGER|CONTEXT_CONTROL) +#endif +STATIC void GC_suspend(GC_thread t) +{ +#ifndef MSWINCE +DWORD exitCode; +#endif +#ifdef RETRY_GET_THREAD_CONTEXT +int retry_cnt=0; +#define MAX_SUSPEND_THREAD_RETRIES (1000*1000) +#endif +#ifdef DEBUG_THREADS +GC_log_printf("Suspending 0x%x\n",(int)t->id); +#endif +UNPROTECT_THREAD(t); +GC_acquire_dirty_lock(); +#ifdef MSWINCE +while (SuspendThread(THREAD_HANDLE(t))==(DWORD)-1){ +GC_release_dirty_lock(); +Sleep(10); +GC_acquire_dirty_lock(); +} +#elif defined(RETRY_GET_THREAD_CONTEXT) +for (;;){ +if (GetExitCodeThread(t->handle,&exitCode) +&&exitCode!=STILL_ACTIVE){ +GC_release_dirty_lock(); +#ifdef GC_PTHREADS +t->stack_base=0; +#else +GC_ASSERT(GC_win32_dll_threads); +GC_delete_gc_thread_no_free(t); +#endif +return; +} +if (SuspendThread(t->handle)!=(DWORD)-1){ +CONTEXT context; +context.ContextFlags=GET_THREAD_CONTEXT_FLAGS; +if (GetThreadContext(t->handle,&context)){ +t->context_sp=copy_ptr_regs(t->context_regs,&context); +break; +} +if (ResumeThread(t->handle)==(DWORD)-1) +ABORT("ResumeThread failed in suspend loop"); +} +if (retry_cnt > 1){ +GC_release_dirty_lock(); +Sleep(0); +GC_acquire_dirty_lock(); +} +if (++retry_cnt>=MAX_SUSPEND_THREAD_RETRIES) +ABORT("SuspendThread loop failed"); +} +#else +if (GetExitCodeThread(t->handle,&exitCode) +&&exitCode!=STILL_ACTIVE){ +GC_release_dirty_lock(); +#ifdef GC_PTHREADS +t->stack_base=0; +#else +GC_ASSERT(GC_win32_dll_threads); +GC_delete_gc_thread_no_free(t); +#endif +return; +} +if (SuspendThread(t->handle)==(DWORD)-1) +ABORT("SuspendThread failed"); +#endif +t->suspended=(unsigned char)TRUE; +GC_release_dirty_lock(); +if (GC_on_thread_event) +GC_on_thread_event(GC_EVENT_THREAD_SUSPENDED,THREAD_HANDLE(t)); +} +#if defined(GC_ASSERTIONS)&&((defined(MSWIN32)&&!defined(CONSOLE_LOG))||defined(MSWINCE)) +GC_INNER GC_bool GC_write_disabled=FALSE; +#endif +GC_INNER void GC_stop_world(void) +{ +DWORD thread_id=GetCurrentThreadId(); +if (!GC_thr_initialized) +ABORT("GC_stop_world()called before GC_thr_init()"); +GC_ASSERT(I_HOLD_LOCK()); +#ifdef PARALLEL_MARK +if (GC_parallel){ +GC_acquire_mark_lock(); +GC_ASSERT(GC_fl_builder_count==0); +} +#endif +#if!defined(GC_NO_THREADS_DISCOVERY)||defined(GC_ASSERTIONS) +GC_please_stop=TRUE; +#endif +#if (defined(MSWIN32)&&!defined(CONSOLE_LOG))||defined(MSWINCE) +GC_ASSERT(!GC_write_disabled); +EnterCriticalSection(&GC_write_cs); +#ifdef GC_ASSERTIONS +GC_write_disabled=TRUE; +#endif +#endif +#ifndef GC_NO_THREADS_DISCOVERY +if (GC_win32_dll_threads){ +int i; +int my_max; +AO_store(&GC_attached_thread,FALSE); +my_max=(int)GC_get_max_thread_index(); +for (i=0;i<=my_max;i++){ +GC_vthread t=dll_thread_table+i; +if (t->stack_base!=0&&t->thread_blocked_sp==NULL +&&t->id!=thread_id){ +GC_suspend((GC_thread)t); +} +} +} else +#endif +{ +GC_thread t; +int i; +for (i=0;i < THREAD_TABLE_SZ;i++){ +for (t=GC_threads[i];t!=0;t=t->tm.next){ +if (t->stack_base!=0&&t->thread_blocked_sp==NULL +&&!KNOWN_FINISHED(t)&&t->id!=thread_id){ +GC_suspend(t); +} +} +} +} +#if (defined(MSWIN32)&&!defined(CONSOLE_LOG))||defined(MSWINCE) +#ifdef GC_ASSERTIONS +GC_write_disabled=FALSE; +#endif +LeaveCriticalSection(&GC_write_cs); +#endif +#ifdef PARALLEL_MARK +if (GC_parallel) +GC_release_mark_lock(); +#endif +} +GC_INNER void GC_start_world(void) +{ +#ifdef GC_ASSERTIONS +DWORD thread_id=GetCurrentThreadId(); +#endif +GC_ASSERT(I_HOLD_LOCK()); +if (GC_win32_dll_threads){ +LONG my_max=GC_get_max_thread_index(); +int i; +for (i=0;i<=my_max;i++){ +GC_thread t=(GC_thread)(dll_thread_table+i); +if (t->suspended){ +#ifdef DEBUG_THREADS +GC_log_printf("Resuming 0x%x\n",(int)t->id); +#endif +GC_ASSERT(t->stack_base!=0&&t->id!=thread_id); +if (ResumeThread(THREAD_HANDLE(t))==(DWORD)-1) +ABORT("ResumeThread failed"); +t->suspended=FALSE; +if (GC_on_thread_event) +GC_on_thread_event(GC_EVENT_THREAD_UNSUSPENDED,THREAD_HANDLE(t)); +} +} +} else { +GC_thread t; +int i; +for (i=0;i < THREAD_TABLE_SZ;i++){ +for (t=GC_threads[i];t!=0;t=t->tm.next){ +if (t->suspended){ +#ifdef DEBUG_THREADS +GC_log_printf("Resuming 0x%x\n",(int)t->id); +#endif +GC_ASSERT(t->stack_base!=0&&t->id!=thread_id); +if (ResumeThread(THREAD_HANDLE(t))==(DWORD)-1) +ABORT("ResumeThread failed"); +UNPROTECT_THREAD(t); +t->suspended=FALSE; +if (GC_on_thread_event) +GC_on_thread_event(GC_EVENT_THREAD_UNSUSPENDED,THREAD_HANDLE(t)); +} else { +#ifdef DEBUG_THREADS +GC_log_printf("Not resuming thread 0x%x as it is not suspended\n", +(int)t->id); +#endif +} +} +} +} +#if!defined(GC_NO_THREADS_DISCOVERY)||defined(GC_ASSERTIONS) +GC_please_stop=FALSE; +#endif +} +#ifdef MSWINCE +#define GC_wince_evaluate_stack_min(s)(ptr_t)(((word)(s)- 1)&~(word)0xFFFF) +#elif defined(GC_ASSERTIONS) +#define GC_dont_query_stack_min FALSE +#endif +static ptr_t last_address=0; +static MEMORY_BASIC_INFORMATION last_info; +STATIC ptr_t GC_get_stack_min(ptr_t s) +{ +ptr_t bottom; +GC_ASSERT(I_HOLD_LOCK()); +if (s!=last_address){ +VirtualQuery(s,&last_info,sizeof(last_info)); +last_address=s; +} +do { +bottom=(ptr_t)last_info.BaseAddress; +VirtualQuery(bottom - 1,&last_info,sizeof(last_info)); +last_address=bottom - 1; +} while ((last_info.Protect&PAGE_READWRITE) +&&!(last_info.Protect&PAGE_GUARD)); +return(bottom); +} +static GC_bool may_be_in_stack(ptr_t s) +{ +GC_ASSERT(I_HOLD_LOCK()); +if (s!=last_address){ +VirtualQuery(s,&last_info,sizeof(last_info)); +last_address=s; +} +return (last_info.Protect&PAGE_READWRITE) +&&!(last_info.Protect&PAGE_GUARD); +} +static ptr_t copy_ptr_regs(word*regs,const CONTEXT*pcontext){ +ptr_t sp; +int cnt=0; +#define context (*pcontext) +#define PUSH1(reg)(regs[cnt++]=(word)pcontext->reg) +#define PUSH2(r1,r2)(PUSH1(r1),PUSH1(r2)) +#define PUSH4(r1,r2,r3,r4)(PUSH2(r1,r2),PUSH2(r3,r4)) +#if defined(I386) +#ifdef WOW64_THREAD_CONTEXT_WORKAROUND +PUSH2(ContextFlags,SegFs); +#endif +PUSH4(Edi,Esi,Ebx,Edx),PUSH2(Ecx,Eax),PUSH1(Ebp); +sp=(ptr_t)context.Esp; +#elif defined(X86_64) +PUSH4(Rax,Rcx,Rdx,Rbx);PUSH2(Rbp,Rsi);PUSH1(Rdi); +PUSH4(R8,R9,R10,R11);PUSH4(R12,R13,R14,R15); +sp=(ptr_t)context.Rsp; +#elif defined(ARM32) +PUSH4(R0,R1,R2,R3),PUSH4(R4,R5,R6,R7),PUSH4(R8,R9,R10,R11); +PUSH1(R12); +sp=(ptr_t)context.Sp; +#elif defined(AARCH64) +PUSH4(X0,X1,X2,X3),PUSH4(X4,X5,X6,X7),PUSH4(X8,X9,X10,X11); +PUSH4(X12,X13,X14,X15),PUSH4(X16,X17,X18,X19),PUSH4(X20,X21,X22,X23); +PUSH4(X24,X25,X26,X27),PUSH1(X28); +PUSH1(Lr); +sp=(ptr_t)context.Sp; +#elif defined(SHx) +PUSH4(R0,R1,R2,R3),PUSH4(R4,R5,R6,R7),PUSH4(R8,R9,R10,R11); +PUSH2(R12,R13),PUSH1(R14); +sp=(ptr_t)context.R15; +#elif defined(MIPS) +PUSH4(IntAt,IntV0,IntV1,IntA0),PUSH4(IntA1,IntA2,IntA3,IntT0); +PUSH4(IntT1,IntT2,IntT3,IntT4),PUSH4(IntT5,IntT6,IntT7,IntS0); +PUSH4(IntS1,IntS2,IntS3,IntS4),PUSH4(IntS5,IntS6,IntS7,IntT8); +PUSH4(IntT9,IntK0,IntK1,IntS8); +sp=(ptr_t)context.IntSp; +#elif defined(PPC) +PUSH4(Gpr0,Gpr3,Gpr4,Gpr5),PUSH4(Gpr6,Gpr7,Gpr8,Gpr9); +PUSH4(Gpr10,Gpr11,Gpr12,Gpr14),PUSH4(Gpr15,Gpr16,Gpr17,Gpr18); +PUSH4(Gpr19,Gpr20,Gpr21,Gpr22),PUSH4(Gpr23,Gpr24,Gpr25,Gpr26); +PUSH4(Gpr27,Gpr28,Gpr29,Gpr30),PUSH1(Gpr31); +sp=(ptr_t)context.Gpr1; +#elif defined(ALPHA) +PUSH4(IntV0,IntT0,IntT1,IntT2),PUSH4(IntT3,IntT4,IntT5,IntT6); +PUSH4(IntT7,IntS0,IntS1,IntS2),PUSH4(IntS3,IntS4,IntS5,IntFp); +PUSH4(IntA0,IntA1,IntA2,IntA3),PUSH4(IntA4,IntA5,IntT8,IntT9); +PUSH4(IntT10,IntT11,IntT12,IntAt); +sp=(ptr_t)context.IntSp; +#elif defined(CPPCHECK) +sp=(ptr_t)(word)cnt; +#else +#error Architecture is not supported +#endif +#undef context +GC_ASSERT(cnt==PUSHED_REGS_COUNT); +return sp; +} +STATIC word GC_push_stack_for(GC_thread thread,DWORD me) +{ +ptr_t sp,stack_min; +struct GC_traced_stack_sect_s*traced_stack_sect= +thread->traced_stack_sect; +if (thread->id==me){ +GC_ASSERT(thread->thread_blocked_sp==NULL); +sp=GC_approx_sp(); +} else if ((sp=thread->thread_blocked_sp)==NULL){ +#ifdef RETRY_GET_THREAD_CONTEXT +word*regs=thread->context_regs; +if (thread->suspended){ +sp=thread->context_sp; +} else +#else +word regs[PUSHED_REGS_COUNT]; +#endif +{ +CONTEXT context; +context.ContextFlags=GET_THREAD_CONTEXT_FLAGS; +if (GetThreadContext(THREAD_HANDLE(thread),&context)){ +sp=copy_ptr_regs(regs,&context); +} else { +#ifdef RETRY_GET_THREAD_CONTEXT +sp=thread->context_sp; +if (NULL==sp){ +return 0; +} +#else +ABORT("GetThreadContext failed"); +#endif +} +} +#ifdef THREAD_LOCAL_ALLOC +GC_ASSERT(thread->suspended||!GC_world_stopped); +#endif +#ifndef WOW64_THREAD_CONTEXT_WORKAROUND +GC_push_many_regs(regs,PUSHED_REGS_COUNT); +#else +GC_push_many_regs(regs+2,PUSHED_REGS_COUNT - 2); +if (isWow64){ +DWORD ContextFlags=(DWORD)regs[0]; +WORD SegFs=(WORD)regs[1]; +if ((ContextFlags&CONTEXT_EXCEPTION_REPORTING)!=0 +&&(ContextFlags&(CONTEXT_EXCEPTION_ACTIVE +))!=0){ +LDT_ENTRY selector; +PNT_TIB tib; +if (!GetThreadSelectorEntry(THREAD_HANDLE(thread),SegFs,&selector)) +ABORT("GetThreadSelectorEntry failed"); +tib=(PNT_TIB)(selector.BaseLow +|(selector.HighWord.Bits.BaseMid<<16) +|(selector.HighWord.Bits.BaseHi<<24)); +#ifdef DEBUG_THREADS +GC_log_printf("TIB stack limit/base:%p .. %p\n", +(void*)tib->StackLimit,(void*)tib->StackBase); +#endif +GC_ASSERT(!((word)thread->stack_base +COOLER_THAN (word)tib->StackBase)); +if (thread->stack_base!=thread->initial_stack_base +&&((word)thread->stack_base<=(word)tib->StackLimit +||(word)tib->StackBase < (word)thread->stack_base)){ +WARN("GetThreadContext might return stale register values" +" including ESP=%p\n",sp); +} else { +sp=(ptr_t)tib->StackLimit; +} +} +#ifdef DEBUG_THREADS +else { +static GC_bool logged; +if (!logged +&&(ContextFlags&CONTEXT_EXCEPTION_REPORTING)==0){ +GC_log_printf("CONTEXT_EXCEPTION_REQUEST not supported\n"); +logged=TRUE; +} +} +#endif +} +#endif +} +if (thread->last_stack_min==ADDR_LIMIT){ +#ifdef MSWINCE +if (GC_dont_query_stack_min){ +stack_min=GC_wince_evaluate_stack_min(traced_stack_sect!=NULL? +(ptr_t)traced_stack_sect:thread->stack_base); +} else +#endif +{ +stack_min=GC_get_stack_min(traced_stack_sect!=NULL? +(ptr_t)traced_stack_sect:thread->stack_base); +UNPROTECT_THREAD(thread); +thread->last_stack_min=stack_min; +} +} else { +if (traced_stack_sect!=NULL&& +(word)thread->last_stack_min > (word)traced_stack_sect){ +UNPROTECT_THREAD(thread); +thread->last_stack_min=(ptr_t)traced_stack_sect; +} +if ((word)sp < (word)thread->stack_base +&&(word)sp>=(word)thread->last_stack_min){ +stack_min=sp; +} else { +if (may_be_in_stack(thread->id==me&& +(word)sp < (word)thread->last_stack_min? +sp:thread->last_stack_min)){ +stack_min=(ptr_t)last_info.BaseAddress; +if ((word)sp < (word)stack_min +||(word)sp>=(word)thread->stack_base) +stack_min=GC_get_stack_min(thread->last_stack_min); +} else { +stack_min=GC_get_stack_min(thread->stack_base); +} +UNPROTECT_THREAD(thread); +thread->last_stack_min=stack_min; +} +} +GC_ASSERT(GC_dont_query_stack_min +||stack_min==GC_get_stack_min(thread->stack_base) +||((word)sp>=(word)stack_min +&&(word)stack_min < (word)thread->stack_base +&&(word)stack_min +> (word)GC_get_stack_min(thread->stack_base))); +if ((word)sp>=(word)stack_min&&(word)sp < (word)thread->stack_base){ +#ifdef DEBUG_THREADS +GC_log_printf("Pushing stack for 0x%x from sp %p to %p from 0x%x\n", +(int)thread->id,(void*)sp,(void*)thread->stack_base, +(int)me); +#endif +GC_push_all_stack_sections(sp,thread->stack_base,traced_stack_sect); +} else { +if (thread->id==me||(word)sp>=(word)thread->stack_base +||(word)(sp+GC_page_size)< (word)stack_min) +WARN("Thread stack pointer %p out of range,pushing everything\n", +sp); +#ifdef DEBUG_THREADS +GC_log_printf("Pushing stack for 0x%x from (min)%p to %p from 0x%x\n", +(int)thread->id,(void*)stack_min, +(void*)thread->stack_base,(int)me); +#endif +GC_push_all_stack(stack_min,thread->stack_base); +} +return thread->stack_base - sp; +} +GC_INNER void GC_push_all_stacks(void) +{ +DWORD thread_id=GetCurrentThreadId(); +GC_bool found_me=FALSE; +#ifndef SMALL_CONFIG +unsigned nthreads=0; +#endif +word total_size=0; +#ifndef GC_NO_THREADS_DISCOVERY +if (GC_win32_dll_threads){ +int i; +LONG my_max=GC_get_max_thread_index(); +for (i=0;i<=my_max;i++){ +GC_thread t=(GC_thread)(dll_thread_table+i); +if (t->tm.in_use&&t->stack_base){ +#ifndef SMALL_CONFIG +++nthreads; +#endif +total_size+=GC_push_stack_for(t,thread_id); +if (t->id==thread_id)found_me=TRUE; +} +} +} else +#endif +{ +int i; +for (i=0;i < THREAD_TABLE_SZ;i++){ +GC_thread t; +for (t=GC_threads[i];t!=0;t=t->tm.next){ +if (!KNOWN_FINISHED(t)&&t->stack_base){ +#ifndef SMALL_CONFIG +++nthreads; +#endif +total_size+=GC_push_stack_for(t,thread_id); +if (t->id==thread_id)found_me=TRUE; +} +} +} +} +#ifndef SMALL_CONFIG +GC_VERBOSE_LOG_PRINTF("Pushed %d thread stacks%s\n",nthreads, +GC_win32_dll_threads? +" based on DllMain thread tracking":""); +#endif +if (!found_me&&!GC_in_thread_creation) +ABORT("Collecting from unknown thread"); +GC_total_stacksize=total_size; +} +#ifdef PARALLEL_MARK +#ifndef MAX_MARKERS +#define MAX_MARKERS 16 +#endif +static ptr_t marker_sp[MAX_MARKERS - 1]; +#ifdef IA64 +static ptr_t marker_bsp[MAX_MARKERS - 1]; +#endif +static ptr_t marker_last_stack_min[MAX_MARKERS - 1]; +#endif +GC_INNER void GC_get_next_stack(char*start,char*limit, +char**lo,char**hi) +{ +int i; +char*current_min=ADDR_LIMIT; +ptr_t*plast_stack_min=NULL; +GC_thread thread=NULL; +if (GC_win32_dll_threads){ +LONG my_max=GC_get_max_thread_index(); +for (i=0;i<=my_max;i++){ +ptr_t s=(ptr_t)(dll_thread_table[i].stack_base); +if ((word)s > (word)start&&(word)s < (word)current_min){ +plast_stack_min=(ptr_t*) +&dll_thread_table[i].last_stack_min; +current_min=s; +#if defined(CPPCHECK) +thread=(GC_thread)&dll_thread_table[i]; +#endif +} +} +} else { +for (i=0;i < THREAD_TABLE_SZ;i++){ +GC_thread t; +for (t=GC_threads[i];t!=0;t=t->tm.next){ +ptr_t s=t->stack_base; +if ((word)s > (word)start&&(word)s < (word)current_min){ +plast_stack_min=&t->last_stack_min; +thread=t; +current_min=s; +} +} +} +#ifdef PARALLEL_MARK +for (i=0;i < GC_markers_m1;++i){ +ptr_t s=marker_sp[i]; +#ifdef IA64 +#endif +if ((word)s > (word)start&&(word)s < (word)current_min){ +GC_ASSERT(marker_last_stack_min[i]!=NULL); +plast_stack_min=&marker_last_stack_min[i]; +current_min=s; +thread=NULL; +} +} +#endif +} +*hi=current_min; +if (current_min==ADDR_LIMIT){ +*lo=ADDR_LIMIT; +return; +} +GC_ASSERT((word)current_min > (word)start&&plast_stack_min!=NULL); +#ifdef MSWINCE +if (GC_dont_query_stack_min){ +*lo=GC_wince_evaluate_stack_min(current_min); +return; +} +#endif +if ((word)current_min > (word)limit&&!may_be_in_stack(limit)){ +*lo=ADDR_LIMIT; +return; +} +if (*plast_stack_min==ADDR_LIMIT +||!may_be_in_stack(*plast_stack_min)){ +*lo=GC_get_stack_min(current_min); +} else { +*lo=GC_get_stack_min(*plast_stack_min); +} +if (thread!=NULL){ +UNPROTECT_THREAD(thread); +} +*plast_stack_min=*lo; +} +#ifdef PARALLEL_MARK +#if defined(GC_PTHREADS)&&!defined(GC_PTHREADS_PARAMARK) +#if!defined(__MINGW32__) +#define GC_PTHREADS_PARAMARK +#endif +#endif +#if!defined(GC_PTHREADS_PARAMARK) +STATIC HANDLE GC_marker_cv[MAX_MARKERS - 1]={0}; +STATIC DWORD GC_marker_Id[MAX_MARKERS - 1]={0}; +#endif +#if defined(GC_PTHREADS)&&defined(HAVE_PTHREAD_SETNAME_NP_WITH_TID) +static void set_marker_thread_name(unsigned id) +{ +char name_buf[16]; +int len=sizeof("GC-marker-")- 1; +BCOPY("GC-marker-",name_buf,len); +if (id>=10) +name_buf[len++]=(char)('0'+(id/10)% 10); +name_buf[len]=(char)('0'+id % 10); +name_buf[len+1]='\0'; +if (pthread_setname_np(pthread_self(),name_buf)!=0) +WARN("pthread_setname_np failed\n",0); +} +#elif!defined(MSWINCE) +static FARPROC setThreadDescription_fn; +static void set_marker_thread_name(unsigned id) +{ +WCHAR name_buf[16]; +int len=sizeof(L"GC-marker-")/sizeof(WCHAR)- 1; +HRESULT hr; +if (!setThreadDescription_fn)return; +BCOPY(L"GC-marker-",name_buf,len*sizeof(WCHAR)); +if (id>=10) +name_buf[len++]=(WCHAR)('0'+(id/10)% 10); +name_buf[len]=(WCHAR)('0'+id % 10); +name_buf[len+1]=0; +hr=(*(HRESULT (WINAPI*)(HANDLE,const WCHAR*)) +(word)setThreadDescription_fn)(GetCurrentThread(),name_buf); +if (FAILED(hr)) +WARN("SetThreadDescription failed\n",0); +} +#else +#define set_marker_thread_name(id)(void)(id) +#endif +#ifdef GC_PTHREADS_PARAMARK +STATIC void*GC_mark_thread(void*id) +#elif defined(MSWINCE) +STATIC DWORD WINAPI GC_mark_thread(LPVOID id) +#else +STATIC unsigned __stdcall GC_mark_thread(void*id) +#endif +{ +word my_mark_no=0; +if ((word)id==GC_WORD_MAX)return 0; +set_marker_thread_name((unsigned)(word)id); +marker_sp[(word)id]=GC_approx_sp(); +#ifdef IA64 +marker_bsp[(word)id]=GC_save_regs_in_stack(); +#endif +#if!defined(GC_PTHREADS_PARAMARK) +GC_marker_Id[(word)id]=GetCurrentThreadId(); +#endif +GC_acquire_mark_lock(); +if (0==--GC_fl_builder_count) +GC_notify_all_builder(); +for (;;++my_mark_no){ +if (my_mark_no - GC_mark_no > (word)2){ +my_mark_no=GC_mark_no; +} +#ifdef DEBUG_THREADS +GC_log_printf("Starting mark helper for mark number %lu\n", +(unsigned long)my_mark_no); +#endif +GC_help_marker(my_mark_no); +} +} +#ifndef GC_ASSERTIONS +#define SET_MARK_LOCK_HOLDER (void)0 +#define UNSET_MARK_LOCK_HOLDER (void)0 +#endif +#ifdef CAN_HANDLE_FORK +static int available_markers_m1=0; +#else +#define available_markers_m1 GC_markers_m1 +#endif +#ifdef GC_PTHREADS_PARAMARK +#include +#if defined(GC_ASSERTIONS)&&!defined(NUMERIC_THREAD_ID) +#define NUMERIC_THREAD_ID(id)(unsigned long)(word)GC_PTHREAD_PTRVAL(id) +#endif +#ifdef CAN_HANDLE_FORK +static pthread_cond_t mark_cv; +#else +static pthread_cond_t mark_cv=PTHREAD_COND_INITIALIZER; +#endif +GC_INNER void GC_start_mark_threads_inner(void) +{ +int i; +pthread_attr_t attr; +pthread_t new_thread; +#ifndef NO_MARKER_SPECIAL_SIGMASK +sigset_t set,oldset; +#endif +GC_ASSERT(I_DONT_HOLD_LOCK()); +if (available_markers_m1<=0)return; +#ifdef CAN_HANDLE_FORK +if (GC_parallel)return; +{ +pthread_cond_t mark_cv_local=PTHREAD_COND_INITIALIZER; +BCOPY(&mark_cv_local,&mark_cv,sizeof(mark_cv)); +} +#endif +GC_ASSERT(GC_fl_builder_count==0); +if (0!=pthread_attr_init(&attr))ABORT("pthread_attr_init failed"); +if (0!=pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED)) +ABORT("pthread_attr_setdetachstate failed"); +#ifndef NO_MARKER_SPECIAL_SIGMASK +if (sigfillset(&set)!=0) +ABORT("sigfillset failed"); +if (pthread_sigmask(SIG_BLOCK,&set,&oldset)< 0){ +WARN("pthread_sigmask set failed,no markers started," +" errno=%" WARN_PRIdPTR "\n",errno); +GC_markers_m1=0; +(void)pthread_attr_destroy(&attr); +return; +} +#endif +#ifdef CAN_HANDLE_FORK +GC_markers_m1=available_markers_m1; +#endif +for (i=0;i < available_markers_m1;++i){ +marker_last_stack_min[i]=ADDR_LIMIT; +if (0!=pthread_create(&new_thread,&attr, +GC_mark_thread,(void*)(word)i)){ +WARN("Marker thread creation failed\n",0); +GC_markers_m1=i; +break; +} +} +#ifndef NO_MARKER_SPECIAL_SIGMASK +if (pthread_sigmask(SIG_SETMASK,&oldset,NULL)< 0){ +WARN("pthread_sigmask restore failed,errno=%" WARN_PRIdPTR "\n", +errno); +} +#endif +(void)pthread_attr_destroy(&attr); +GC_wait_for_markers_init(); +GC_COND_LOG_PRINTF("Started %d mark helper threads\n",GC_markers_m1); +} +#ifdef GC_ASSERTIONS +STATIC unsigned long GC_mark_lock_holder=NO_THREAD; +#define SET_MARK_LOCK_HOLDER (void)(GC_mark_lock_holder=NUMERIC_THREAD_ID(pthread_self())) +#define UNSET_MARK_LOCK_HOLDER do { GC_ASSERT(GC_mark_lock_holder==NUMERIC_THREAD_ID(pthread_self()));GC_mark_lock_holder=NO_THREAD;} while (0) +#endif +static pthread_mutex_t mark_mutex=PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t builder_cv=PTHREAD_COND_INITIALIZER; +#ifdef LOCK_STATS +volatile AO_t GC_block_count=0; +#endif +GC_INNER void GC_acquire_mark_lock(void) +{ +#if defined(NUMERIC_THREAD_ID_UNIQUE)&&!defined(THREAD_SANITIZER) +GC_ASSERT(GC_mark_lock_holder!=NUMERIC_THREAD_ID(pthread_self())); +#endif +if (pthread_mutex_lock(&mark_mutex)!=0){ +ABORT("pthread_mutex_lock failed"); +} +#ifdef LOCK_STATS +(void)AO_fetch_and_add1(&GC_block_count); +#endif +SET_MARK_LOCK_HOLDER; +} +GC_INNER void GC_release_mark_lock(void) +{ +UNSET_MARK_LOCK_HOLDER; +if (pthread_mutex_unlock(&mark_mutex)!=0){ +ABORT("pthread_mutex_unlock failed"); +} +} +STATIC void GC_wait_builder(void) +{ +UNSET_MARK_LOCK_HOLDER; +if (pthread_cond_wait(&builder_cv,&mark_mutex)!=0){ +ABORT("pthread_cond_wait failed"); +} +GC_ASSERT(GC_mark_lock_holder==NO_THREAD); +SET_MARK_LOCK_HOLDER; +} +GC_INNER void GC_wait_for_reclaim(void) +{ +GC_acquire_mark_lock(); +while (GC_fl_builder_count > 0){ +GC_wait_builder(); +} +GC_release_mark_lock(); +} +GC_INNER void GC_notify_all_builder(void) +{ +GC_ASSERT(GC_mark_lock_holder==NUMERIC_THREAD_ID(pthread_self())); +if (pthread_cond_broadcast(&builder_cv)!=0){ +ABORT("pthread_cond_broadcast failed"); +} +} +GC_INNER void GC_wait_marker(void) +{ +GC_ASSERT(GC_parallel); +UNSET_MARK_LOCK_HOLDER; +if (pthread_cond_wait(&mark_cv,&mark_mutex)!=0){ +ABORT("pthread_cond_wait failed"); +} +GC_ASSERT(GC_mark_lock_holder==NO_THREAD); +SET_MARK_LOCK_HOLDER; +} +GC_INNER void GC_notify_all_marker(void) +{ +GC_ASSERT(GC_parallel); +if (pthread_cond_broadcast(&mark_cv)!=0){ +ABORT("pthread_cond_broadcast failed"); +} +} +#else +#ifndef MARK_THREAD_STACK_SIZE +#define MARK_THREAD_STACK_SIZE 0 +#endif +static HANDLE mark_mutex_event=(HANDLE)0; +static HANDLE builder_cv=(HANDLE)0; +static HANDLE mark_cv=(HANDLE)0; +GC_INNER void GC_start_mark_threads_inner(void) +{ +int i; +GC_ASSERT(I_DONT_HOLD_LOCK()); +if (available_markers_m1<=0)return; +GC_ASSERT(GC_fl_builder_count==0); +for (i=0;i < GC_markers_m1;++i){ +if ((GC_marker_cv[i]=CreateEvent(NULL, +TRUE, +FALSE, +NULL))==(HANDLE)0) +ABORT("CreateEvent failed"); +} +for (i=0;i < GC_markers_m1;++i){ +#if defined(MSWINCE)||defined(MSWIN_XBOX1) +HANDLE handle; +DWORD thread_id; +marker_last_stack_min[i]=ADDR_LIMIT; +handle=CreateThread(NULL, +MARK_THREAD_STACK_SIZE, +GC_mark_thread,(LPVOID)(word)i, +0,&thread_id); +if (handle==NULL){ +WARN("Marker thread creation failed\n",0); +break; +} else { +CloseHandle(handle); +} +#else +GC_uintptr_t handle; +unsigned thread_id; +marker_last_stack_min[i]=ADDR_LIMIT; +handle=_beginthreadex(NULL, +MARK_THREAD_STACK_SIZE,GC_mark_thread, +(void*)(word)i,0,&thread_id); +if (!handle||handle==(GC_uintptr_t)-1L){ +WARN("Marker thread creation failed\n",0); +break; +} else { +} +#endif +} +while (GC_markers_m1 > i){ +GC_markers_m1--; +CloseHandle(GC_marker_cv[GC_markers_m1]); +} +GC_wait_for_markers_init(); +GC_COND_LOG_PRINTF("Started %d mark helper threads\n",GC_markers_m1); +if (i==0){ +CloseHandle(mark_cv); +CloseHandle(builder_cv); +CloseHandle(mark_mutex_event); +} +} +#ifdef GC_ASSERTIONS +STATIC DWORD GC_mark_lock_holder=NO_THREAD; +#define SET_MARK_LOCK_HOLDER (void)(GC_mark_lock_holder=GetCurrentThreadId()) +#define UNSET_MARK_LOCK_HOLDER do { GC_ASSERT(GC_mark_lock_holder==GetCurrentThreadId());GC_mark_lock_holder=NO_THREAD;} while (0) +#endif +STATIC LONG GC_mark_mutex_state=0; +#ifdef LOCK_STATS +volatile AO_t GC_block_count=0; +volatile AO_t GC_unlocked_count=0; +#endif +GC_INNER void GC_acquire_mark_lock(void) +{ +#ifndef THREAD_SANITIZER +GC_ASSERT(GC_mark_lock_holder!=GetCurrentThreadId()); +#endif +if (InterlockedExchange(&GC_mark_mutex_state,1)!=0){ +#ifdef LOCK_STATS +(void)AO_fetch_and_add1(&GC_block_count); +#endif +while (InterlockedExchange(&GC_mark_mutex_state, +-1)!=0){ +if (WaitForSingleObject(mark_mutex_event,INFINITE)==WAIT_FAILED) +ABORT("WaitForSingleObject failed"); +} +} +#ifdef LOCK_STATS +else { +(void)AO_fetch_and_add1(&GC_unlocked_count); +} +#endif +GC_ASSERT(GC_mark_lock_holder==NO_THREAD); +SET_MARK_LOCK_HOLDER; +} +GC_INNER void GC_release_mark_lock(void) +{ +UNSET_MARK_LOCK_HOLDER; +if (InterlockedExchange(&GC_mark_mutex_state,0)< 0){ +if (SetEvent(mark_mutex_event)==FALSE) +ABORT("SetEvent failed"); +} +} +GC_INNER void GC_wait_for_reclaim(void) +{ +GC_ASSERT(builder_cv!=0); +for (;;){ +GC_acquire_mark_lock(); +if (GC_fl_builder_count==0) +break; +if (ResetEvent(builder_cv)==FALSE) +ABORT("ResetEvent failed"); +GC_release_mark_lock(); +if (WaitForSingleObject(builder_cv,INFINITE)==WAIT_FAILED) +ABORT("WaitForSingleObject failed"); +} +GC_release_mark_lock(); +} +GC_INNER void GC_notify_all_builder(void) +{ +GC_ASSERT(GC_mark_lock_holder==GetCurrentThreadId()); +GC_ASSERT(builder_cv!=0); +GC_ASSERT(GC_fl_builder_count==0); +if (SetEvent(builder_cv)==FALSE) +ABORT("SetEvent failed"); +} +GC_INNER void GC_wait_marker(void) +{ +HANDLE event=mark_cv; +DWORD thread_id=GetCurrentThreadId(); +int i=GC_markers_m1; +while (i--> 0){ +if (GC_marker_Id[i]==thread_id){ +event=GC_marker_cv[i]; +break; +} +} +if (ResetEvent(event)==FALSE) +ABORT("ResetEvent failed"); +GC_release_mark_lock(); +if (WaitForSingleObject(event,INFINITE)==WAIT_FAILED) +ABORT("WaitForSingleObject failed"); +GC_acquire_mark_lock(); +} +GC_INNER void GC_notify_all_marker(void) +{ +DWORD thread_id=GetCurrentThreadId(); +int i=GC_markers_m1; +while (i--> 0){ +if (SetEvent(GC_marker_Id[i]!=thread_id?GC_marker_cv[i]: +mark_cv)==FALSE) +ABORT("SetEvent failed"); +} +} +#endif +static unsigned required_markers_cnt=0; +#endif +typedef struct { +LPTHREAD_START_ROUTINE start; +LPVOID param; +} thread_args; +STATIC void*GC_CALLBACK GC_win32_start_inner(struct GC_stack_base*sb, +void*arg) +{ +void*ret; +LPTHREAD_START_ROUTINE start=((thread_args*)arg)->start; +LPVOID param=((thread_args*)arg)->param; +GC_register_my_thread(sb); +#ifdef DEBUG_THREADS +GC_log_printf("thread 0x%lx starting...\n",(long)GetCurrentThreadId()); +#endif +GC_free(arg); +#if!defined(__GNUC__)&&!defined(NO_CRT) +ret=NULL; +__try +#endif +{ +ret=(void*)(word)(*start)(param); +} +#if!defined(__GNUC__)&&!defined(NO_CRT) +__finally +#endif +{ +GC_unregister_my_thread(); +} +#ifdef DEBUG_THREADS +GC_log_printf("thread 0x%lx returned from start routine\n", +(long)GetCurrentThreadId()); +#endif +return ret; +} +STATIC DWORD WINAPI GC_win32_start(LPVOID arg) +{ +return (DWORD)(word)GC_call_with_stack_base(GC_win32_start_inner,arg); +} +GC_API HANDLE WINAPI GC_CreateThread( +LPSECURITY_ATTRIBUTES lpThreadAttributes, +GC_WIN32_SIZE_T dwStackSize, +LPTHREAD_START_ROUTINE lpStartAddress, +LPVOID lpParameter,DWORD dwCreationFlags, +LPDWORD lpThreadId) +{ +if (!EXPECT(parallel_initialized,TRUE)) +GC_init_parallel(); +#ifdef DEBUG_THREADS +GC_log_printf("About to create a thread from 0x%lx\n", +(long)GetCurrentThreadId()); +#endif +if (GC_win32_dll_threads){ +return CreateThread(lpThreadAttributes,dwStackSize,lpStartAddress, +lpParameter,dwCreationFlags,lpThreadId); +} else { +thread_args*args= +(thread_args*)GC_malloc_uncollectable(sizeof(thread_args)); +HANDLE thread_h; +if (NULL==args){ +SetLastError(ERROR_NOT_ENOUGH_MEMORY); +return NULL; +} +args->start=lpStartAddress; +args->param=lpParameter; +GC_dirty(args); +REACHABLE_AFTER_DIRTY(lpParameter); +set_need_to_lock(); +thread_h=CreateThread(lpThreadAttributes,dwStackSize,GC_win32_start, +args,dwCreationFlags,lpThreadId); +if (thread_h==0)GC_free(args); +return thread_h; +} +} +GC_API DECLSPEC_NORETURN void WINAPI GC_ExitThread(DWORD dwExitCode) +{ +GC_unregister_my_thread(); +ExitThread(dwExitCode); +} +#if!defined(CYGWIN32)&&!defined(MSWINCE)&&!defined(MSWIN_XBOX1)&&!defined(NO_CRT) +GC_API GC_uintptr_t GC_CALL GC_beginthreadex( +void*security,unsigned stack_size, +unsigned (__stdcall*start_address)(void*), +void*arglist,unsigned initflag, +unsigned*thrdaddr) +{ +if (!EXPECT(parallel_initialized,TRUE)) +GC_init_parallel(); +#ifdef DEBUG_THREADS +GC_log_printf("About to create a thread from 0x%lx\n", +(long)GetCurrentThreadId()); +#endif +if (GC_win32_dll_threads){ +return _beginthreadex(security,stack_size,start_address, +arglist,initflag,thrdaddr); +} else { +GC_uintptr_t thread_h; +thread_args*args= +(thread_args*)GC_malloc_uncollectable(sizeof(thread_args)); +if (NULL==args){ +errno=EAGAIN; +return 0; +} +args->start=(LPTHREAD_START_ROUTINE)start_address; +args->param=arglist; +GC_dirty(args); +REACHABLE_AFTER_DIRTY(arglist); +set_need_to_lock(); +thread_h=_beginthreadex(security,stack_size, +(unsigned (__stdcall*)(void*))GC_win32_start, +args,initflag,thrdaddr); +if (thread_h==0)GC_free(args); +return thread_h; +} +} +GC_API void GC_CALL GC_endthreadex(unsigned retval) +{ +GC_unregister_my_thread(); +_endthreadex(retval); +} +#endif +#ifdef GC_WINMAIN_REDIRECT +#if defined(MSWINCE)&&defined(UNDER_CE) +#define WINMAIN_LPTSTR LPWSTR +#else +#define WINMAIN_LPTSTR LPSTR +#endif +#undef WinMain +int WINAPI GC_WinMain(HINSTANCE,HINSTANCE,WINMAIN_LPTSTR,int); +typedef struct { +HINSTANCE hInstance; +HINSTANCE hPrevInstance; +WINMAIN_LPTSTR lpCmdLine; +int nShowCmd; +} main_thread_args; +static DWORD WINAPI main_thread_start(LPVOID arg) +{ +main_thread_args*args=(main_thread_args*)arg; +return (DWORD)GC_WinMain(args->hInstance,args->hPrevInstance, +args->lpCmdLine,args->nShowCmd); +} +STATIC void*GC_waitForSingleObjectInfinite(void*handle) +{ +return (void*)(word)WaitForSingleObject((HANDLE)handle,INFINITE); +} +#ifndef WINMAIN_THREAD_STACK_SIZE +#define WINMAIN_THREAD_STACK_SIZE 0 +#endif +int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance, +WINMAIN_LPTSTR lpCmdLine,int nShowCmd) +{ +DWORD exit_code=1; +main_thread_args args={ +hInstance,hPrevInstance,lpCmdLine,nShowCmd +}; +HANDLE thread_h; +DWORD thread_id; +GC_INIT(); +thread_h=GC_CreateThread(NULL, +WINMAIN_THREAD_STACK_SIZE, +main_thread_start,&args,0, +&thread_id); +if (thread_h!=NULL){ +if ((DWORD)(word)GC_do_blocking(GC_waitForSingleObjectInfinite, +(void*)thread_h)==WAIT_FAILED) +ABORT("WaitForSingleObject(main_thread)failed"); +GetExitCodeThread (thread_h,&exit_code); +CloseHandle (thread_h); +} else { +ABORT("GC_CreateThread(main_thread)failed"); +} +#ifdef MSWINCE +GC_deinit(); +#endif +return (int)exit_code; +} +#endif +GC_API void GC_CALL GC_set_markers_count(unsigned markers GC_ATTR_UNUSED) +{ +#ifdef PARALLEL_MARK +required_markers_cnt=markers < MAX_MARKERS?markers:MAX_MARKERS; +#endif +} +GC_INNER void GC_thr_init(void) +{ +struct GC_stack_base sb; +#if (!defined(HAVE_PTHREAD_SETNAME_NP_WITH_TID)&&!defined(MSWINCE)&&defined(PARALLEL_MARK))||defined(WOW64_THREAD_CONTEXT_WORKAROUND) +HMODULE hK32=GetModuleHandle(TEXT("kernel32.dll")); +#endif +GC_ASSERT(I_HOLD_LOCK()); +if (GC_thr_initialized)return; +GC_ASSERT((word)&GC_threads % sizeof(word)==0); +#ifdef GC_NO_THREADS_DISCOVERY +#define GC_main_thread GetCurrentThreadId() +#else +GC_main_thread=GetCurrentThreadId(); +#endif +GC_thr_initialized=TRUE; +#ifdef CAN_HANDLE_FORK +if (GC_handle_fork){ +#ifdef CAN_CALL_ATFORK +if (pthread_atfork(fork_prepare_proc,fork_parent_proc, +fork_child_proc)==0){ +GC_handle_fork=1; +} else +#endif +if (GC_handle_fork!=-1) +ABORT("pthread_atfork failed"); +} +#endif +#ifdef WOW64_THREAD_CONTEXT_WORKAROUND +if (hK32){ +FARPROC pfn=GetProcAddress(hK32,"IsWow64Process"); +if (pfn +&&!(*(BOOL (WINAPI*)(HANDLE,BOOL*))(word)pfn)( +GetCurrentProcess(),&isWow64)) +isWow64=FALSE; +} +#endif +sb.mem_base=GC_stackbottom; +GC_ASSERT(sb.mem_base!=NULL); +#ifdef IA64 +sb.reg_base=GC_register_stackbottom; +#endif +#if defined(PARALLEL_MARK) +{ +char*markers_string=GETENV("GC_MARKERS"); +int markers=required_markers_cnt; +if (markers_string!=NULL){ +markers=atoi(markers_string); +if (markers<=0||markers > MAX_MARKERS){ +WARN("Too big or invalid number of mark threads:%" WARN_PRIdPTR +";using maximum threads\n",(signed_word)markers); +markers=MAX_MARKERS; +} +} else if (0==markers){ +#ifdef MSWINCE +markers=(int)GC_sysinfo.dwNumberOfProcessors; +#else +#ifdef _WIN64 +DWORD_PTR procMask=0; +DWORD_PTR sysMask; +#else +DWORD procMask=0; +DWORD sysMask; +#endif +int ncpu=0; +if ( +#ifdef __cplusplus +GetProcessAffinityMask(GetCurrentProcess(),&procMask,&sysMask) +#else +GetProcessAffinityMask(GetCurrentProcess(), +(void*)&procMask,(void*)&sysMask) +#endif +&&procMask){ +do { +ncpu++; +} while ((procMask&=procMask - 1)!=0); +} +markers=ncpu; +#endif +#if defined(GC_MIN_MARKERS)&&!defined(CPPCHECK) +if (markers < GC_MIN_MARKERS) +markers=GC_MIN_MARKERS; +#endif +if (markers > MAX_MARKERS) +markers=MAX_MARKERS; +} +available_markers_m1=markers - 1; +} +if (GC_win32_dll_threads||available_markers_m1<=0){ +GC_parallel=FALSE; +GC_COND_LOG_PRINTF( +"Single marker thread,turning off parallel marking\n"); +} else { +#ifndef GC_PTHREADS_PARAMARK +mark_mutex_event=CreateEvent(NULL, +FALSE, +FALSE,NULL); +builder_cv=CreateEvent(NULL, +TRUE, +FALSE,NULL); +mark_cv=CreateEvent(NULL,TRUE, +FALSE,NULL); +if (mark_mutex_event==(HANDLE)0||builder_cv==(HANDLE)0 +||mark_cv==(HANDLE)0) +ABORT("CreateEvent failed"); +#endif +#if!defined(HAVE_PTHREAD_SETNAME_NP_WITH_TID)&&!defined(MSWINCE) +if (hK32) +setThreadDescription_fn=GetProcAddress(hK32, +"SetThreadDescription"); +#endif +} +#endif +GC_ASSERT(0==GC_lookup_thread_inner(GC_main_thread)); +GC_register_my_thread_inner(&sb,GC_main_thread); +#undef GC_main_thread +} +#ifdef GC_PTHREADS +struct start_info { +void*(*start_routine)(void*); +void*arg; +GC_bool detached; +}; +GC_API int GC_pthread_join(pthread_t pthread_id,void**retval) +{ +int result; +#ifndef GC_WIN32_PTHREADS +GC_thread t; +#endif +DCL_LOCK_STATE; +GC_ASSERT(!GC_win32_dll_threads); +#ifdef DEBUG_THREADS +GC_log_printf("thread %p(0x%lx)is joining thread %p\n", +(void*)GC_PTHREAD_PTRVAL(pthread_self()), +(long)GetCurrentThreadId(), +(void*)GC_PTHREAD_PTRVAL(pthread_id)); +#endif +#ifndef GC_WIN32_PTHREADS +while ((t=GC_lookup_pthread(pthread_id))==0) +Sleep(10); +#endif +result=pthread_join(pthread_id,retval); +if (0==result){ +#ifdef GC_WIN32_PTHREADS +GC_thread t=GC_lookup_pthread(pthread_id); +if (NULL==t)ABORT("Thread not registered"); +#endif +LOCK(); +if ((t->flags&FINISHED)!=0){ +GC_delete_gc_thread_no_free(t); +GC_INTERNAL_FREE(t); +} +UNLOCK(); +} +#ifdef DEBUG_THREADS +GC_log_printf("thread %p(0x%lx)join with thread %p %s\n", +(void*)GC_PTHREAD_PTRVAL(pthread_self()), +(long)GetCurrentThreadId(), +(void*)GC_PTHREAD_PTRVAL(pthread_id), +result!=0?"failed":"succeeded"); +#endif +return result; +} +GC_API int GC_pthread_create(pthread_t*new_thread, +GC_PTHREAD_CREATE_CONST pthread_attr_t*attr, +void*(*start_routine)(void*),void*arg) +{ +int result; +struct start_info*si; +if (!EXPECT(parallel_initialized,TRUE)) +GC_init_parallel(); +GC_ASSERT(!GC_win32_dll_threads); +si=(struct start_info*)GC_malloc_uncollectable( +sizeof(struct start_info)); +if (NULL==si) +return EAGAIN; +si->start_routine=start_routine; +si->arg=arg; +GC_dirty(si); +REACHABLE_AFTER_DIRTY(arg); +if (attr!=0&& +pthread_attr_getdetachstate(attr,&si->detached) +==PTHREAD_CREATE_DETACHED){ +si->detached=TRUE; +} +#ifdef DEBUG_THREADS +GC_log_printf("About to create a thread from %p(0x%lx)\n", +(void*)GC_PTHREAD_PTRVAL(pthread_self()), +(long)GetCurrentThreadId()); +#endif +set_need_to_lock(); +result=pthread_create(new_thread,attr,GC_pthread_start,si); +if (result){ +GC_free(si); +} +return(result); +} +STATIC void*GC_CALLBACK GC_pthread_start_inner(struct GC_stack_base*sb, +void*arg) +{ +struct start_info*si=(struct start_info*)arg; +void*result; +void*(*start)(void*); +void*start_arg; +DWORD thread_id=GetCurrentThreadId(); +pthread_t pthread_id=pthread_self(); +GC_thread me; +DCL_LOCK_STATE; +#ifdef DEBUG_THREADS +GC_log_printf("thread %p(0x%x)starting...\n", +(void*)GC_PTHREAD_PTRVAL(pthread_id),(int)thread_id); +#endif +GC_ASSERT(!GC_win32_dll_threads); +LOCK(); +me=GC_register_my_thread_inner(sb,thread_id); +SET_PTHREAD_MAP_CACHE(pthread_id,thread_id); +GC_ASSERT(me!=&first_thread); +me->pthread_id=pthread_id; +if (si->detached)me->flags|=DETACHED; +UNLOCK(); +start=si->start_routine; +start_arg=si->arg; +GC_free(si); +pthread_cleanup_push(GC_thread_exit_proc,(void*)me); +result=(*start)(start_arg); +me->status=result; +GC_dirty(me); +pthread_cleanup_pop(1); +#ifdef DEBUG_THREADS +GC_log_printf("thread %p(0x%x)returned from start routine\n", +(void*)GC_PTHREAD_PTRVAL(pthread_id),(int)thread_id); +#endif +return(result); +} +STATIC void*GC_pthread_start(void*arg) +{ +return GC_call_with_stack_base(GC_pthread_start_inner,arg); +} +STATIC void GC_thread_exit_proc(void*arg) +{ +GC_thread me=(GC_thread)arg; +DCL_LOCK_STATE; +GC_ASSERT(!GC_win32_dll_threads); +#ifdef DEBUG_THREADS +GC_log_printf("thread %p(0x%lx)called pthread_exit()\n", +(void*)GC_PTHREAD_PTRVAL(pthread_self()), +(long)GetCurrentThreadId()); +#endif +LOCK(); +GC_wait_for_gc_completion(FALSE); +#if defined(THREAD_LOCAL_ALLOC) +GC_ASSERT(GC_getspecific(GC_thread_key)==&me->tlfs); +GC_destroy_thread_local(&(me->tlfs)); +#endif +if (me->flags&DETACHED){ +GC_delete_thread(GetCurrentThreadId()); +} else { +me->flags|=FINISHED; +} +#if defined(THREAD_LOCAL_ALLOC) +GC_remove_specific(GC_thread_key); +#endif +UNLOCK(); +} +#ifndef GC_NO_PTHREAD_SIGMASK +GC_API int GC_pthread_sigmask(int how,const sigset_t*set, +sigset_t*oset) +{ +return pthread_sigmask(how,set,oset); +} +#endif +GC_API int GC_pthread_detach(pthread_t thread) +{ +int result; +GC_thread t; +DCL_LOCK_STATE; +GC_ASSERT(!GC_win32_dll_threads); +while ((t=GC_lookup_pthread(thread))==NULL) +Sleep(10); +result=pthread_detach(thread); +if (result==0){ +LOCK(); +t->flags|=DETACHED; +if ((t->flags&FINISHED)!=0){ +GC_delete_gc_thread_no_free(t); +GC_INTERNAL_FREE(t); +} +UNLOCK(); +} +return result; +} +#elif!defined(GC_NO_THREADS_DISCOVERY) +#ifdef GC_INSIDE_DLL +GC_API +#else +#define GC_DllMain DllMain +#endif +BOOL WINAPI GC_DllMain(HINSTANCE inst GC_ATTR_UNUSED,ULONG reason, +LPVOID reserved GC_ATTR_UNUSED) +{ +DWORD thread_id; +if (!GC_win32_dll_threads&¶llel_initialized)return TRUE; +switch (reason){ +case DLL_THREAD_ATTACH: +#ifdef PARALLEL_MARK +if (GC_parallel){ +break; +} +#endif +case DLL_PROCESS_ATTACH: +thread_id=GetCurrentThreadId(); +if (parallel_initialized&&GC_main_thread!=thread_id){ +#ifdef PARALLEL_MARK +ABORT("Cannot initialize parallel marker from DllMain"); +#else +struct GC_stack_base sb; +#ifdef GC_ASSERTIONS +int sb_result= +#endif +GC_get_stack_base(&sb); +GC_ASSERT(sb_result==GC_SUCCESS); +GC_register_my_thread_inner(&sb,thread_id); +#endif +} +break; +case DLL_THREAD_DETACH: +GC_ASSERT(parallel_initialized); +if (GC_win32_dll_threads){ +GC_delete_thread(GetCurrentThreadId()); +} +break; +case DLL_PROCESS_DETACH: +if (GC_win32_dll_threads){ +int i; +int my_max=(int)GC_get_max_thread_index(); +for (i=0;i<=my_max;++i){ +if (AO_load(&(dll_thread_table[i].tm.in_use))) +GC_delete_gc_thread_no_free(&dll_thread_table[i]); +} +GC_deinit(); +} +break; +} +return TRUE; +} +#endif +GC_INNER void GC_init_parallel(void) +{ +#if defined(THREAD_LOCAL_ALLOC) +GC_thread me; +DCL_LOCK_STATE; +#endif +if (parallel_initialized)return; +parallel_initialized=TRUE; +if (!GC_is_initialized)GC_init(); +#if defined(CPPCHECK)&&!defined(GC_NO_THREADS_DISCOVERY) +GC_noop1((word)&GC_DllMain); +#endif +if (GC_win32_dll_threads){ +set_need_to_lock(); +} +#if defined(THREAD_LOCAL_ALLOC) +LOCK(); +me=GC_lookup_thread_inner(GetCurrentThreadId()); +CHECK_LOOKUP_MY_THREAD(me); +GC_init_thread_local(&me->tlfs); +UNLOCK(); +#endif +} +#if defined(USE_PTHREAD_LOCKS) +GC_INNER void GC_lock(void) +{ +pthread_mutex_lock(&GC_allocate_ml); +} +#endif +#if defined(THREAD_LOCAL_ALLOC) +GC_INNER void GC_mark_thread_local_free_lists(void) +{ +int i; +GC_thread p; +for (i=0;i < THREAD_TABLE_SZ;++i){ +for (p=GC_threads[i];0!=p;p=p->tm.next){ +if (!KNOWN_FINISHED(p)){ +#ifdef DEBUG_THREADS +GC_log_printf("Marking thread locals for 0x%x\n",(int)p->id); +#endif +GC_mark_thread_local_fls_for(&(p->tlfs)); +} +} +} +} +#if defined(GC_ASSERTIONS) +void GC_check_tls(void) +{ +int i; +GC_thread p; +for (i=0;i < THREAD_TABLE_SZ;++i){ +for (p=GC_threads[i];0!=p;p=p->tm.next){ +if (!KNOWN_FINISHED(p)) +GC_check_tls_for(&(p->tlfs)); +} +} +#if defined(USE_CUSTOM_SPECIFIC) +if (GC_thread_key!=0) +GC_check_tsd_marks(GC_thread_key); +#endif +} +#endif +#endif +#ifndef GC_NO_THREAD_REDIRECTS +#define CreateThread GC_CreateThread +#define ExitThread GC_ExitThread +#undef _beginthreadex +#define _beginthreadex GC_beginthreadex +#undef _endthreadex +#define _endthreadex GC_endthreadex +#endif +#endif +#ifndef GC_PTHREAD_START_STANDALONE +#if defined(__GNUC__)&&defined(__linux__) +#undef __EXCEPTIONS +#endif +#if defined(GC_PTHREADS)&&!defined(GC_WIN32_THREADS) +#include +#include +GC_INNER_PTHRSTART void*GC_CALLBACK GC_inner_start_routine( +struct GC_stack_base*sb,void*arg) +{ +void*(*start)(void*); +void*start_arg; +void*result; +volatile GC_thread me= +GC_start_rtn_prepare_thread(&start,&start_arg,sb,arg); +#ifndef NACL +pthread_cleanup_push(GC_thread_exit_proc,me); +#endif +result=(*start)(start_arg); +#if defined(DEBUG_THREADS)&&!defined(GC_PTHREAD_START_STANDALONE) +GC_log_printf("Finishing thread %p\n",(void*)pthread_self()); +#endif +me->status=result; +GC_end_stubborn_change(me); +#ifndef NACL +pthread_cleanup_pop(1); +#endif +return result; +} +#endif +#endif +#ifndef GC_NO_THREAD_REDIRECTS +#define GC_PTHREAD_REDIRECTS_ONLY +#ifndef GC_PTHREAD_REDIRECTS_H +#define GC_PTHREAD_REDIRECTS_H +#if defined(GC_H)&&defined(GC_PTHREADS) +#ifndef GC_PTHREAD_REDIRECTS_ONLY +#include +#ifndef GC_NO_DLOPEN +#include +#endif +#ifndef GC_NO_PTHREAD_SIGMASK +#include +#endif +#ifdef __cplusplus +extern "C" { +#endif +#ifndef GC_SUSPEND_THREAD_ID +#define GC_SUSPEND_THREAD_ID pthread_t +#endif +#ifndef GC_NO_DLOPEN +GC_API void*GC_dlopen(const char*,int); +#endif +#ifndef GC_NO_PTHREAD_SIGMASK +#if defined(GC_PTHREAD_SIGMASK_NEEDED)||defined(_BSD_SOURCE)||defined(_GNU_SOURCE)||(_POSIX_C_SOURCE>=199506L)||(_XOPEN_SOURCE>=500) +GC_API int GC_pthread_sigmask(int,const sigset_t*, +sigset_t*); +#endif +#endif +#ifndef GC_PTHREAD_CREATE_CONST +#define GC_PTHREAD_CREATE_CONST const +#endif +GC_API int GC_pthread_create(pthread_t*, +GC_PTHREAD_CREATE_CONST pthread_attr_t*, +void*(*)(void*),void*); +GC_API int GC_pthread_join(pthread_t,void**); +GC_API int GC_pthread_detach(pthread_t); +#ifndef GC_NO_PTHREAD_CANCEL +GC_API int GC_pthread_cancel(pthread_t); +#endif +#if defined(GC_HAVE_PTHREAD_EXIT)&&!defined(GC_PTHREAD_EXIT_DECLARED) +#define GC_PTHREAD_EXIT_DECLARED +GC_API void GC_pthread_exit(void*)GC_PTHREAD_EXIT_ATTRIBUTE; +#endif +#ifdef __cplusplus +} +#endif +#endif +#if!defined(GC_NO_THREAD_REDIRECTS)&&!defined(GC_USE_LD_WRAP) +#undef pthread_create +#undef pthread_join +#undef pthread_detach +#define pthread_create GC_pthread_create +#define pthread_join GC_pthread_join +#define pthread_detach GC_pthread_detach +#ifndef GC_NO_PTHREAD_SIGMASK +#undef pthread_sigmask +#define pthread_sigmask GC_pthread_sigmask +#endif +#ifndef GC_NO_DLOPEN +#undef dlopen +#define dlopen GC_dlopen +#endif +#ifndef GC_NO_PTHREAD_CANCEL +#undef pthread_cancel +#define pthread_cancel GC_pthread_cancel +#endif +#ifdef GC_HAVE_PTHREAD_EXIT +#undef pthread_exit +#define pthread_exit GC_pthread_exit +#endif +#endif +#endif +#endif +#endif diff --git a/v_windows/v/old/thirdparty/libgc/gc.h b/v_windows/v/old/thirdparty/libgc/gc.h new file mode 100644 index 0000000..d0646f5 --- /dev/null +++ b/v_windows/v/old/thirdparty/libgc/gc.h @@ -0,0 +1,1081 @@ +/* + * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers + * Copyright (c) 1991-1995 by Xerox Corporation. All rights reserved. + * Copyright 1996-1999 by Silicon Graphics. All rights reserved. + * Copyright 1999 by Hewlett-Packard Company. All rights reserved. + * Copyright (C) 2007 Free Software Foundation, Inc + * Copyright (c) 2000-2011 by Hewlett-Packard Development Company. + * Copyright (c) 2009-2020 Ivan Maidanski + * + * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED + * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. + * + * Permission is hereby granted to use or copy this program + * for any purpose, provided the above notices are retained on all copies. + * Permission to modify the code and to distribute modified code is granted, + * provided the above notices are retained, and a notice that the code was + * modified is included with the above copyright notice. + */ + +#ifndef GC_H +#define GC_H +#if (defined(WIN64)&&!defined(_WIN64))&&defined(_MSC_VER) +#pragma message("Warning:Expecting _WIN64 for x64 targets!Notice the leading underscore!") +#endif +#if defined(GC_H) +#define GC_TMP_VERSION_MAJOR 8 +#define GC_TMP_VERSION_MINOR 1 +#define GC_TMP_VERSION_MICRO 0 +#ifdef GC_VERSION_MAJOR +#if GC_TMP_VERSION_MAJOR!=GC_VERSION_MAJOR||GC_TMP_VERSION_MINOR!=GC_VERSION_MINOR||GC_TMP_VERSION_MICRO!=GC_VERSION_MICRO +#error Inconsistent version info. Check README.md,include/gc_version.h and configure.ac. +#endif +#else +#define GC_VERSION_MAJOR GC_TMP_VERSION_MAJOR +#define GC_VERSION_MINOR GC_TMP_VERSION_MINOR +#define GC_VERSION_MICRO GC_TMP_VERSION_MICRO +#endif +#endif +#if defined(GC_H) +#if defined(__GNUC__)&&defined(__GNUC_MINOR__) +#define GC_GNUC_PREREQ(major,minor)((__GNUC__<<16)+__GNUC_MINOR__>=((major)<<16)+(minor)) +#else +#define GC_GNUC_PREREQ(major,minor)0 +#endif +#if defined(SOLARIS_THREADS)||defined(_SOLARIS_THREADS)||defined(_SOLARIS_PTHREADS)||defined(GC_SOLARIS_PTHREADS) +#ifndef GC_SOLARIS_THREADS +#define GC_SOLARIS_THREADS +#endif +#endif +#if defined(IRIX_THREADS) +#define GC_IRIX_THREADS +#endif +#if defined(DGUX_THREADS)&&!defined(GC_DGUX386_THREADS) +#define GC_DGUX386_THREADS +#endif +#if defined(AIX_THREADS) +#define GC_AIX_THREADS +#endif +#if defined(HPUX_THREADS) +#define GC_HPUX_THREADS +#endif +#if defined(OSF1_THREADS) +#define GC_OSF1_THREADS +#endif +#if defined(LINUX_THREADS) +#define GC_LINUX_THREADS +#endif +#if defined(WIN32_THREADS) +#define GC_WIN32_THREADS +#endif +#if defined(RTEMS_THREADS) +#define GC_RTEMS_PTHREADS +#endif +#if defined(USE_LD_WRAP) +#define GC_USE_LD_WRAP +#endif +#if defined(GC_WIN32_PTHREADS)&&!defined(GC_WIN32_THREADS) +#define GC_WIN32_THREADS +#endif +#if defined(GC_AIX_THREADS)||defined(GC_DARWIN_THREADS)||defined(GC_DGUX386_THREADS)||defined(GC_FREEBSD_THREADS)||defined(GC_HPUX_THREADS)||defined(GC_IRIX_THREADS)||defined(GC_LINUX_THREADS)||defined(GC_NETBSD_THREADS)||defined(GC_OPENBSD_THREADS)||defined(GC_OSF1_THREADS)||defined(GC_SOLARIS_THREADS)||defined(GC_WIN32_THREADS)||defined(GC_RTEMS_PTHREADS) +#ifndef GC_THREADS +#define GC_THREADS +#endif +#elif defined(GC_THREADS) +#if defined(__linux__) +#define GC_LINUX_THREADS +#elif defined(__OpenBSD__) +#define GC_OPENBSD_THREADS +#elif defined(_PA_RISC1_1)||defined(_PA_RISC2_0)||defined(hppa)||defined(__HPPA)||(defined(__ia64)&&defined(_HPUX_SOURCE)) +#define GC_HPUX_THREADS +#elif defined(__HAIKU__) +#define GC_HAIKU_THREADS +#elif defined(__DragonFly__)||defined(__FreeBSD_kernel__)||(defined(__FreeBSD__)&&!defined(SN_TARGET_ORBIS)) +#define GC_FREEBSD_THREADS +#elif defined(__NetBSD__) +#define GC_NETBSD_THREADS +#elif defined(__alpha)||defined(__alpha__) +#define GC_OSF1_THREADS +#elif (defined(mips)||defined(__mips)||defined(_mips))&&!(defined(nec_ews)||defined(_nec_ews)||defined(ultrix)||defined(__ultrix)) +#define GC_IRIX_THREADS +#elif defined(__sparc)||((defined(sun)||defined(__sun))&&(defined(i386)||defined(__i386__)||defined(__amd64)||defined(__amd64__))) +#define GC_SOLARIS_THREADS +#elif defined(__APPLE__)&&defined(__MACH__) +#define GC_DARWIN_THREADS +#endif +#if defined(DGUX)&&(defined(i386)||defined(__i386__)) +#define GC_DGUX386_THREADS +#endif +#if defined(_AIX) +#define GC_AIX_THREADS +#endif +#if (defined(_WIN32)||defined(_MSC_VER)||defined(__BORLANDC__)||defined(__CYGWIN32__)||defined(__CYGWIN__)||defined(__CEGCC__)||defined(_WIN32_WCE)||defined(__MINGW32__))&&!defined(GC_WIN32_THREADS) +#define GC_WIN32_THREADS +#endif +#if defined(__rtems__)&&(defined(i386)||defined(__i386__)) +#define GC_RTEMS_PTHREADS +#endif +#endif +#undef GC_PTHREADS +#if (!defined(GC_WIN32_THREADS)||defined(GC_WIN32_PTHREADS)||defined(__CYGWIN32__)||defined(__CYGWIN__))&&defined(GC_THREADS)&&!defined(NN_PLATFORM_CTR)&&!defined(NN_BUILD_TARGET_PLATFORM_NX) +#define GC_PTHREADS +#endif +#if!defined(_PTHREADS)&&defined(GC_NETBSD_THREADS) +#define _PTHREADS +#endif +#if defined(GC_DGUX386_THREADS)&&!defined(_POSIX4A_DRAFT10_SOURCE) +#define _POSIX4A_DRAFT10_SOURCE 1 +#endif +#if!defined(_REENTRANT)&&defined(GC_PTHREADS)&&!defined(GC_WIN32_THREADS) +#define _REENTRANT 1 +#endif +#define __GC +#if!defined(_WIN32_WCE)||defined(__GNUC__) +#include +#if defined(__MINGW32__)&&!defined(_WIN32_WCE) +#include +#endif +#else +#include +#ifndef _PTRDIFF_T_DEFINED +#define _PTRDIFF_T_DEFINED +typedef long ptrdiff_t; +#endif +#endif +#if!defined(GC_NOT_DLL)&&!defined(GC_DLL)&&((defined(_DLL)&&!defined(__GNUC__))||(defined(DLL_EXPORT)&&defined(GC_BUILD))) +#define GC_DLL +#endif +#if defined(GC_DLL)&&!defined(GC_API) +#if defined(__CEGCC__) +#if defined(GC_BUILD) +#define GC_API __declspec(dllexport) +#else +#define GC_API __declspec(dllimport) +#endif +#elif defined(__MINGW32__) +#if defined(__cplusplus)&&defined(GC_BUILD) +#define GC_API extern __declspec(dllexport) +#elif defined(GC_BUILD)||defined(__MINGW32_DELAY_LOAD__) +#define GC_API __declspec(dllexport) +#else +#define GC_API extern __declspec(dllimport) +#endif +#elif defined(_MSC_VER)||defined(__DMC__)||defined(__BORLANDC__)||defined(__CYGWIN__) +#ifdef GC_BUILD +#define GC_API extern __declspec(dllexport) +#else +#define GC_API __declspec(dllimport) +#endif +#elif defined(__WATCOMC__) +#ifdef GC_BUILD +#define GC_API extern __declspec(dllexport) +#else +#define GC_API extern __declspec(dllimport) +#endif +#elif defined(__SYMBIAN32__) +#ifdef GC_BUILD +#define GC_API extern EXPORT_C +#else +#define GC_API extern IMPORT_C +#endif +#elif defined(__GNUC__) +#if defined(GC_BUILD)&&!defined(GC_NO_VISIBILITY)&&(GC_GNUC_PREREQ(4,0)||defined(GC_VISIBILITY_HIDDEN_SET)) +#define GC_API extern __attribute__((__visibility__("default"))) +#endif +#endif +#endif +#ifndef GC_API +#define GC_API extern +#endif +#ifndef GC_CALL +#define GC_CALL +#endif +#ifndef GC_CALLBACK +#define GC_CALLBACK GC_CALL +#endif +#ifndef GC_ATTR_MALLOC +#ifdef GC_OOM_FUNC_RETURNS_ALIAS +#define GC_ATTR_MALLOC +#elif GC_GNUC_PREREQ(3,1) +#define GC_ATTR_MALLOC __attribute__((__malloc__)) +#elif defined(_MSC_VER)&&(_MSC_VER>=1900)&&!defined(__EDG__) +#define GC_ATTR_MALLOC __declspec(allocator)__declspec(noalias)__declspec(restrict) +#elif defined(_MSC_VER)&&_MSC_VER>=1400 +#define GC_ATTR_MALLOC __declspec(noalias)__declspec(restrict) +#else +#define GC_ATTR_MALLOC +#endif +#endif +#ifndef GC_ATTR_ALLOC_SIZE +#undef GC_ATTR_CALLOC_SIZE +#ifdef __clang__ +#if __has_attribute(__alloc_size__) +#define GC_ATTR_ALLOC_SIZE(argnum)__attribute__((__alloc_size__(argnum))) +#define GC_ATTR_CALLOC_SIZE(n,s)__attribute__((__alloc_size__(n,s))) +#else +#define GC_ATTR_ALLOC_SIZE(argnum) +#endif +#elif GC_GNUC_PREREQ(4,3)&&!defined(__ICC) +#define GC_ATTR_ALLOC_SIZE(argnum)__attribute__((__alloc_size__(argnum))) +#define GC_ATTR_CALLOC_SIZE(n,s)__attribute__((__alloc_size__(n,s))) +#else +#define GC_ATTR_ALLOC_SIZE(argnum) +#endif +#endif +#ifndef GC_ATTR_CALLOC_SIZE +#define GC_ATTR_CALLOC_SIZE(n,s) +#endif +#ifndef GC_ATTR_NONNULL +#if GC_GNUC_PREREQ(4,0) +#define GC_ATTR_NONNULL(argnum)__attribute__((__nonnull__(argnum))) +#else +#define GC_ATTR_NONNULL(argnum) +#endif +#endif +#ifndef GC_ATTR_CONST +#if GC_GNUC_PREREQ(4,0) +#define GC_ATTR_CONST __attribute__((__const__)) +#else +#define GC_ATTR_CONST +#endif +#endif +#ifndef GC_ATTR_DEPRECATED +#ifdef GC_BUILD +#undef GC_ATTR_DEPRECATED +#define GC_ATTR_DEPRECATED +#elif GC_GNUC_PREREQ(4,0) +#define GC_ATTR_DEPRECATED __attribute__((__deprecated__)) +#elif defined(_MSC_VER)&&_MSC_VER>=1200 +#define GC_ATTR_DEPRECATED __declspec(deprecated) +#else +#define GC_ATTR_DEPRECATED +#endif +#endif +#if defined(__sgi)&&!defined(__GNUC__)&&_COMPILER_VERSION>=720 +#define GC_ADD_CALLER +#define GC_RETURN_ADDR (GC_word)__return_address +#endif +#if defined(__linux__)||defined(__GLIBC__) +#if!defined(__native_client__) +#include +#endif +#if (__GLIBC__==2&&__GLIBC_MINOR__>=1||__GLIBC__ > 2)&&!defined(__ia64__)&&!defined(GC_MISSING_EXECINFO_H)&&!defined(GC_HAVE_BUILTIN_BACKTRACE) +#define GC_HAVE_BUILTIN_BACKTRACE +#endif +#if defined(__i386__)||defined(__amd64__)||defined(__x86_64__) +#define GC_CAN_SAVE_CALL_STACKS +#endif +#endif +#if defined(_MSC_VER)&&_MSC_VER>=1200&&!defined(_AMD64_)&&!defined(_M_X64)&&!defined(_WIN32_WCE)&&!defined(GC_HAVE_NO_BUILTIN_BACKTRACE)&&!defined(GC_HAVE_BUILTIN_BACKTRACE) +#define GC_HAVE_BUILTIN_BACKTRACE +#endif +#if defined(GC_HAVE_BUILTIN_BACKTRACE)&&!defined(GC_CAN_SAVE_CALL_STACKS) +#define GC_CAN_SAVE_CALL_STACKS +#endif +#if defined(__sparc__) +#define GC_CAN_SAVE_CALL_STACKS +#endif +#if (defined(__linux__)||defined(__DragonFly__)||defined(__FreeBSD__)||defined(__FreeBSD_kernel__)||defined(__HAIKU__)||defined(__NetBSD__)||defined(__OpenBSD__)||defined(HOST_ANDROID)||defined(__ANDROID__))&&!defined(GC_CAN_SAVE_CALL_STACKS) +#define GC_ADD_CALLER +#if GC_GNUC_PREREQ(2,95) +#define GC_RETURN_ADDR (GC_word)__builtin_return_address(0) +#if GC_GNUC_PREREQ(4,0)&&(defined(__i386__)||defined(__amd64__)||defined(__x86_64__)) +#define GC_HAVE_RETURN_ADDR_PARENT +#define GC_RETURN_ADDR_PARENT (GC_word)__builtin_extract_return_addr(__builtin_return_address(1)) +#endif +#else +#define GC_RETURN_ADDR 0 +#endif +#endif +#ifdef GC_PTHREADS +#if (defined(GC_DARWIN_THREADS)||defined(GC_WIN32_PTHREADS)||defined(__native_client__)||defined(GC_RTEMS_PTHREADS))&&!defined(GC_NO_DLOPEN) +#define GC_NO_DLOPEN +#endif +#if (defined(GC_DARWIN_THREADS)||defined(GC_WIN32_PTHREADS)||defined(GC_OPENBSD_THREADS)||defined(__native_client__))&&!defined(GC_NO_PTHREAD_SIGMASK) +#define GC_NO_PTHREAD_SIGMASK +#endif +#if defined(__native_client__) +#ifndef GC_PTHREAD_CREATE_CONST +#define GC_PTHREAD_CREATE_CONST +#endif +#ifndef GC_HAVE_PTHREAD_EXIT +#define GC_HAVE_PTHREAD_EXIT +#define GC_PTHREAD_EXIT_ATTRIBUTE +#endif +#endif +#if!defined(GC_HAVE_PTHREAD_EXIT)&&!defined(HOST_ANDROID)&&!defined(__ANDROID__)&&(defined(GC_LINUX_THREADS)||defined(GC_SOLARIS_THREADS)) +#define GC_HAVE_PTHREAD_EXIT +#if GC_GNUC_PREREQ(2,7) +#define GC_PTHREAD_EXIT_ATTRIBUTE __attribute__((__noreturn__)) +#elif defined(__NORETURN) +#define GC_PTHREAD_EXIT_ATTRIBUTE __NORETURN +#else +#define GC_PTHREAD_EXIT_ATTRIBUTE +#endif +#endif +#if (!defined(GC_HAVE_PTHREAD_EXIT)||defined(__native_client__))&&!defined(GC_NO_PTHREAD_CANCEL) +#define GC_NO_PTHREAD_CANCEL +#endif +#endif +#ifdef __cplusplus +#ifndef GC_ATTR_EXPLICIT +#if __cplusplus>=201103L&&!defined(__clang__)||_MSVC_LANG>=201103L||defined(CPPCHECK) +#define GC_ATTR_EXPLICIT explicit +#else +#define GC_ATTR_EXPLICIT +#endif +#endif +#ifndef GC_NOEXCEPT +#if defined(__DMC__)||(defined(__BORLANDC__)&&(defined(_RWSTD_NO_EXCEPTIONS)||defined(_RWSTD_NO_EX_SPEC)))||(defined(_MSC_VER)&&defined(_HAS_EXCEPTIONS)&&!_HAS_EXCEPTIONS)||(defined(__WATCOMC__)&&!defined(_CPPUNWIND)) +#define GC_NOEXCEPT +#ifndef GC_NEW_ABORTS_ON_OOM +#define GC_NEW_ABORTS_ON_OOM +#endif +#elif __cplusplus>=201103L||_MSVC_LANG>=201103L +#define GC_NOEXCEPT noexcept +#else +#define GC_NOEXCEPT throw() +#endif +#endif +#endif +#endif +#ifdef __cplusplus +extern "C" { +#endif +typedef void*GC_PTR; +#ifdef _WIN64 +#if defined(__int64)&&!defined(CPPCHECK) +typedef unsigned __int64 GC_word; +typedef __int64 GC_signed_word; +#else +typedef unsigned long long GC_word; +typedef long long GC_signed_word; +#endif +#else +typedef unsigned long GC_word; +typedef long GC_signed_word; +#endif +GC_API unsigned GC_CALL GC_get_version(void); +GC_API GC_ATTR_DEPRECATED GC_word GC_gc_no; +GC_API GC_word GC_CALL GC_get_gc_no(void); +#ifdef GC_THREADS +GC_API GC_ATTR_DEPRECATED int GC_parallel; +GC_API int GC_CALL GC_get_parallel(void); +GC_API void GC_CALL GC_set_markers_count(unsigned); +#endif +typedef void*(GC_CALLBACK*GC_oom_func)(size_t); +GC_API GC_ATTR_DEPRECATED GC_oom_func GC_oom_fn; +GC_API void GC_CALL GC_set_oom_fn(GC_oom_func)GC_ATTR_NONNULL(1); +GC_API GC_oom_func GC_CALL GC_get_oom_fn(void); +typedef void (GC_CALLBACK*GC_on_heap_resize_proc)(GC_word); +GC_API GC_ATTR_DEPRECATED GC_on_heap_resize_proc GC_on_heap_resize; +GC_API void GC_CALL GC_set_on_heap_resize(GC_on_heap_resize_proc); +GC_API GC_on_heap_resize_proc GC_CALL GC_get_on_heap_resize(void); +typedef enum { +GC_EVENT_START, +GC_EVENT_MARK_START, +GC_EVENT_MARK_END, +GC_EVENT_RECLAIM_START, +GC_EVENT_RECLAIM_END, +GC_EVENT_END, +GC_EVENT_PRE_STOP_WORLD, +GC_EVENT_POST_STOP_WORLD, +GC_EVENT_PRE_START_WORLD, +GC_EVENT_POST_START_WORLD, +GC_EVENT_THREAD_SUSPENDED, +GC_EVENT_THREAD_UNSUSPENDED +} GC_EventType; +typedef void (GC_CALLBACK*GC_on_collection_event_proc)(GC_EventType); +GC_API void GC_CALL GC_set_on_collection_event(GC_on_collection_event_proc); +GC_API GC_on_collection_event_proc GC_CALL GC_get_on_collection_event(void); +#if defined(GC_THREADS)||(defined(GC_BUILD)&&defined(NN_PLATFORM_CTR)) +typedef void (GC_CALLBACK*GC_on_thread_event_proc)(GC_EventType, +void*); +GC_API void GC_CALL GC_set_on_thread_event(GC_on_thread_event_proc); +GC_API GC_on_thread_event_proc GC_CALL GC_get_on_thread_event(void); +#endif +GC_API GC_ATTR_DEPRECATED int GC_find_leak; +GC_API void GC_CALL GC_set_find_leak(int); +GC_API int GC_CALL GC_get_find_leak(void); +GC_API GC_ATTR_DEPRECATED int GC_all_interior_pointers; +GC_API void GC_CALL GC_set_all_interior_pointers(int); +GC_API int GC_CALL GC_get_all_interior_pointers(void); +GC_API GC_ATTR_DEPRECATED int GC_finalize_on_demand; +GC_API void GC_CALL GC_set_finalize_on_demand(int); +GC_API int GC_CALL GC_get_finalize_on_demand(void); +GC_API GC_ATTR_DEPRECATED int GC_java_finalization; +GC_API void GC_CALL GC_set_java_finalization(int); +GC_API int GC_CALL GC_get_java_finalization(void); +typedef void (GC_CALLBACK*GC_finalizer_notifier_proc)(void); +GC_API GC_ATTR_DEPRECATED GC_finalizer_notifier_proc GC_finalizer_notifier; +GC_API void GC_CALL GC_set_finalizer_notifier(GC_finalizer_notifier_proc); +GC_API GC_finalizer_notifier_proc GC_CALL GC_get_finalizer_notifier(void); +GC_API +#ifndef GC_DONT_GC +GC_ATTR_DEPRECATED +#endif +int GC_dont_gc; +GC_API GC_ATTR_DEPRECATED int GC_dont_expand; +GC_API void GC_CALL GC_set_dont_expand(int); +GC_API int GC_CALL GC_get_dont_expand(void); +GC_API GC_ATTR_DEPRECATED int GC_use_entire_heap; +GC_API GC_ATTR_DEPRECATED int GC_full_freq; +GC_API void GC_CALL GC_set_full_freq(int); +GC_API int GC_CALL GC_get_full_freq(void); +GC_API GC_ATTR_DEPRECATED GC_word GC_non_gc_bytes; +GC_API void GC_CALL GC_set_non_gc_bytes(GC_word); +GC_API GC_word GC_CALL GC_get_non_gc_bytes(void); +GC_API GC_ATTR_DEPRECATED int GC_no_dls; +GC_API void GC_CALL GC_set_no_dls(int); +GC_API int GC_CALL GC_get_no_dls(void); +GC_API GC_ATTR_DEPRECATED GC_word GC_free_space_divisor; +GC_API void GC_CALL GC_set_free_space_divisor(GC_word); +GC_API GC_word GC_CALL GC_get_free_space_divisor(void); +GC_API GC_ATTR_DEPRECATED GC_word GC_max_retries; +GC_API void GC_CALL GC_set_max_retries(GC_word); +GC_API GC_word GC_CALL GC_get_max_retries(void); +GC_API GC_ATTR_DEPRECATED char*GC_stackbottom; +GC_API GC_ATTR_DEPRECATED int GC_dont_precollect; +GC_API void GC_CALL GC_set_dont_precollect(int); +GC_API int GC_CALL GC_get_dont_precollect(void); +GC_API GC_ATTR_DEPRECATED unsigned long GC_time_limit; +#define GC_TIME_UNLIMITED 999999 +GC_API void GC_CALL GC_set_time_limit(unsigned long); +GC_API unsigned long GC_CALL GC_get_time_limit(void); +struct GC_timeval_s { +unsigned long tv_ms; +unsigned long tv_nsec; +}; +GC_API void GC_CALL GC_set_time_limit_tv(struct GC_timeval_s); +GC_API struct GC_timeval_s GC_CALL GC_get_time_limit_tv(void); +GC_API void GC_CALL GC_set_allocd_bytes_per_finalizer(GC_word); +GC_API GC_word GC_CALL GC_get_allocd_bytes_per_finalizer(void); +GC_API void GC_CALL GC_start_performance_measurement(void); +GC_API unsigned long GC_CALL GC_get_full_gc_total_time(void); +GC_API void GC_CALL GC_set_pages_executable(int); +GC_API int GC_CALL GC_get_pages_executable(void); +GC_API void GC_CALL GC_set_min_bytes_allocd(size_t); +GC_API size_t GC_CALL GC_get_min_bytes_allocd(void); +GC_API void GC_CALL GC_set_rate(int); +GC_API int GC_CALL GC_get_rate(void); +GC_API void GC_CALL GC_set_max_prior_attempts(int); +GC_API int GC_CALL GC_get_max_prior_attempts(void); +GC_API void GC_CALL GC_set_handle_fork(int); +GC_API void GC_CALL GC_atfork_prepare(void); +GC_API void GC_CALL GC_atfork_parent(void); +GC_API void GC_CALL GC_atfork_child(void); +GC_API void GC_CALL GC_init(void); +GC_API int GC_CALL GC_is_init_called(void); +GC_API void GC_CALL GC_deinit(void); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL +GC_malloc(size_t); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL +GC_malloc_atomic(size_t); +GC_API GC_ATTR_MALLOC char*GC_CALL GC_strdup(const char*); +GC_API GC_ATTR_MALLOC char*GC_CALL +GC_strndup(const char*,size_t)GC_ATTR_NONNULL(1); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL +GC_malloc_uncollectable(size_t); +GC_API GC_ATTR_DEPRECATED void*GC_CALL GC_malloc_stubborn(size_t); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(2)void*GC_CALL +GC_memalign(size_t,size_t); +GC_API int GC_CALL GC_posix_memalign(void**,size_t, +size_t)GC_ATTR_NONNULL(1); +GC_API void GC_CALL GC_free(void*); +#define GC_MALLOC_STUBBORN(sz)GC_MALLOC(sz) +#define GC_NEW_STUBBORN(t)GC_NEW(t) +#define GC_CHANGE_STUBBORN(p)GC_change_stubborn(p) +GC_API GC_ATTR_DEPRECATED void GC_CALL GC_change_stubborn(const void*); +GC_API void GC_CALL GC_end_stubborn_change(const void*)GC_ATTR_NONNULL(1); +GC_API void*GC_CALL GC_base(void*); +GC_API int GC_CALL GC_is_heap_ptr(const void*); +GC_API size_t GC_CALL GC_size(const void*)GC_ATTR_NONNULL(1); +GC_API void*GC_CALL GC_realloc(void*, +size_t) +GC_ATTR_ALLOC_SIZE(2); +GC_API int GC_CALL GC_expand_hp(size_t); +GC_API void GC_CALL GC_set_max_heap_size(GC_word); +GC_API void GC_CALL GC_exclude_static_roots(void*, +void*); +GC_API void GC_CALL GC_clear_exclusion_table(void); +GC_API void GC_CALL GC_clear_roots(void); +GC_API void GC_CALL GC_add_roots(void*, +void*); +GC_API void GC_CALL GC_remove_roots(void*, +void*); +GC_API void GC_CALL GC_register_displacement(size_t); +GC_API void GC_CALL GC_debug_register_displacement(size_t); +GC_API void GC_CALL GC_gcollect(void); +GC_API void GC_CALL GC_gcollect_and_unmap(void); +typedef int (GC_CALLBACK*GC_stop_func)(void); +GC_API int GC_CALL GC_try_to_collect(GC_stop_func) +GC_ATTR_NONNULL(1); +GC_API void GC_CALL GC_set_stop_func(GC_stop_func) +GC_ATTR_NONNULL(1); +GC_API GC_stop_func GC_CALL GC_get_stop_func(void); +GC_API size_t GC_CALL GC_get_heap_size(void); +GC_API size_t GC_CALL GC_get_free_bytes(void); +GC_API size_t GC_CALL GC_get_unmapped_bytes(void); +GC_API size_t GC_CALL GC_get_bytes_since_gc(void); +GC_API size_t GC_CALL GC_get_expl_freed_bytes_since_gc(void); +GC_API size_t GC_CALL GC_get_total_bytes(void); +GC_API void GC_CALL GC_get_heap_usage_safe(GC_word*, +GC_word*, +GC_word*, +GC_word*, +GC_word*); +struct GC_prof_stats_s { +GC_word heapsize_full; +GC_word free_bytes_full; +GC_word unmapped_bytes; +GC_word bytes_allocd_since_gc; +GC_word allocd_bytes_before_gc; +GC_word non_gc_bytes; +GC_word gc_no; +GC_word markers_m1; +GC_word bytes_reclaimed_since_gc; +GC_word reclaimed_bytes_before_gc; +GC_word expl_freed_bytes_since_gc; +}; +GC_API size_t GC_CALL GC_get_prof_stats(struct GC_prof_stats_s*, +size_t); +#ifdef GC_THREADS +GC_API size_t GC_CALL GC_get_prof_stats_unsafe(struct GC_prof_stats_s*, +size_t); +#endif +GC_API size_t GC_CALL GC_get_size_map_at(int i); +GC_API size_t GC_CALL GC_get_memory_use(void); +GC_API void GC_CALL GC_disable(void); +GC_API int GC_CALL GC_is_disabled(void); +GC_API void GC_CALL GC_enable(void); +GC_API void GC_CALL GC_set_manual_vdb_allowed(int); +GC_API int GC_CALL GC_get_manual_vdb_allowed(void); +GC_API void GC_CALL GC_enable_incremental(void); +GC_API int GC_CALL GC_is_incremental_mode(void); +#define GC_PROTECTS_POINTER_HEAP 1 +#define GC_PROTECTS_PTRFREE_HEAP 2 +#define GC_PROTECTS_STATIC_DATA 4 +#define GC_PROTECTS_STACK 8 +#define GC_PROTECTS_NONE 0 +GC_API int GC_CALL GC_incremental_protection_needs(void); +GC_API int GC_CALL GC_collect_a_little(void); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL +GC_malloc_ignore_off_page(size_t); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL +GC_malloc_atomic_ignore_off_page(size_t); +#ifdef GC_ADD_CALLER +#define GC_EXTRAS GC_RETURN_ADDR,__FILE__,__LINE__ +#define GC_EXTRA_PARAMS GC_word ra,const char*s,int i +#else +#define GC_EXTRAS __FILE__,__LINE__ +#define GC_EXTRA_PARAMS const char*s,int i +#endif +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL +GC_malloc_atomic_uncollectable(size_t); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL +GC_debug_malloc_atomic_uncollectable(size_t,GC_EXTRA_PARAMS); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL +GC_debug_malloc(size_t,GC_EXTRA_PARAMS); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL +GC_debug_malloc_atomic(size_t,GC_EXTRA_PARAMS); +GC_API GC_ATTR_MALLOC char*GC_CALL +GC_debug_strdup(const char*,GC_EXTRA_PARAMS); +GC_API GC_ATTR_MALLOC char*GC_CALL +GC_debug_strndup(const char*,size_t,GC_EXTRA_PARAMS) +GC_ATTR_NONNULL(1); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL +GC_debug_malloc_uncollectable(size_t, +GC_EXTRA_PARAMS); +GC_API GC_ATTR_DEPRECATED void*GC_CALL +GC_debug_malloc_stubborn(size_t,GC_EXTRA_PARAMS); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL +GC_debug_malloc_ignore_off_page(size_t, +GC_EXTRA_PARAMS); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL +GC_debug_malloc_atomic_ignore_off_page(size_t, +GC_EXTRA_PARAMS); +GC_API void GC_CALL GC_debug_free(void*); +GC_API void*GC_CALL GC_debug_realloc(void*, +size_t,GC_EXTRA_PARAMS) +GC_ATTR_ALLOC_SIZE(2); +GC_API GC_ATTR_DEPRECATED void GC_CALL GC_debug_change_stubborn(const void*); +GC_API void GC_CALL GC_debug_end_stubborn_change(const void*) +GC_ATTR_NONNULL(1); +GC_API GC_ATTR_MALLOC GC_ATTR_ALLOC_SIZE(1)void*GC_CALL +GC_debug_malloc_replacement(size_t); +GC_API GC_ATTR_ALLOC_SIZE(2)void*GC_CALL +GC_debug_realloc_replacement(void*, +size_t); +#ifdef GC_DEBUG_REPLACEMENT +#define GC_MALLOC(sz)GC_debug_malloc_replacement(sz) +#define GC_REALLOC(old,sz)GC_debug_realloc_replacement(old,sz) +#elif defined(GC_DEBUG) +#define GC_MALLOC(sz)GC_debug_malloc(sz,GC_EXTRAS) +#define GC_REALLOC(old,sz)GC_debug_realloc(old,sz,GC_EXTRAS) +#else +#define GC_MALLOC(sz)GC_malloc(sz) +#define GC_REALLOC(old,sz)GC_realloc(old,sz) +#endif +#ifdef GC_DEBUG +#define GC_MALLOC_ATOMIC(sz)GC_debug_malloc_atomic(sz,GC_EXTRAS) +#define GC_STRDUP(s)GC_debug_strdup(s,GC_EXTRAS) +#define GC_STRNDUP(s,sz)GC_debug_strndup(s,sz,GC_EXTRAS) +#define GC_MALLOC_ATOMIC_UNCOLLECTABLE(sz)GC_debug_malloc_atomic_uncollectable(sz,GC_EXTRAS) +#define GC_MALLOC_UNCOLLECTABLE(sz)GC_debug_malloc_uncollectable(sz,GC_EXTRAS) +#define GC_MALLOC_IGNORE_OFF_PAGE(sz)GC_debug_malloc_ignore_off_page(sz,GC_EXTRAS) +#define GC_MALLOC_ATOMIC_IGNORE_OFF_PAGE(sz)GC_debug_malloc_atomic_ignore_off_page(sz,GC_EXTRAS) +#define GC_FREE(p)GC_debug_free(p) +#define GC_REGISTER_FINALIZER(p,f,d,of,od)GC_debug_register_finalizer(p,f,d,of,od) +#define GC_REGISTER_FINALIZER_IGNORE_SELF(p,f,d,of,od)GC_debug_register_finalizer_ignore_self(p,f,d,of,od) +#define GC_REGISTER_FINALIZER_NO_ORDER(p,f,d,of,od)GC_debug_register_finalizer_no_order(p,f,d,of,od) +#define GC_REGISTER_FINALIZER_UNREACHABLE(p,f,d,of,od)GC_debug_register_finalizer_unreachable(p,f,d,of,od) +#define GC_END_STUBBORN_CHANGE(p)GC_debug_end_stubborn_change(p) +#define GC_PTR_STORE_AND_DIRTY(p,q)GC_debug_ptr_store_and_dirty(p,q) +#define GC_GENERAL_REGISTER_DISAPPEARING_LINK(link,obj)GC_general_register_disappearing_link(link,GC_base(( void*)(obj))) +#define GC_REGISTER_LONG_LINK(link,obj)GC_register_long_link(link,GC_base(( void*)(obj))) +#define GC_REGISTER_DISPLACEMENT(n)GC_debug_register_displacement(n) +#else +#define GC_MALLOC_ATOMIC(sz)GC_malloc_atomic(sz) +#define GC_STRDUP(s)GC_strdup(s) +#define GC_STRNDUP(s,sz)GC_strndup(s,sz) +#define GC_MALLOC_ATOMIC_UNCOLLECTABLE(sz)GC_malloc_atomic_uncollectable(sz) +#define GC_MALLOC_UNCOLLECTABLE(sz)GC_malloc_uncollectable(sz) +#define GC_MALLOC_IGNORE_OFF_PAGE(sz)GC_malloc_ignore_off_page(sz) +#define GC_MALLOC_ATOMIC_IGNORE_OFF_PAGE(sz)GC_malloc_atomic_ignore_off_page(sz) +#define GC_FREE(p)GC_free(p) +#define GC_REGISTER_FINALIZER(p,f,d,of,od)GC_register_finalizer(p,f,d,of,od) +#define GC_REGISTER_FINALIZER_IGNORE_SELF(p,f,d,of,od)GC_register_finalizer_ignore_self(p,f,d,of,od) +#define GC_REGISTER_FINALIZER_NO_ORDER(p,f,d,of,od)GC_register_finalizer_no_order(p,f,d,of,od) +#define GC_REGISTER_FINALIZER_UNREACHABLE(p,f,d,of,od)GC_register_finalizer_unreachable(p,f,d,of,od) +#define GC_END_STUBBORN_CHANGE(p)GC_end_stubborn_change(p) +#define GC_PTR_STORE_AND_DIRTY(p,q)GC_ptr_store_and_dirty(p,q) +#define GC_GENERAL_REGISTER_DISAPPEARING_LINK(link,obj)GC_general_register_disappearing_link(link,obj) +#define GC_REGISTER_LONG_LINK(link,obj)GC_register_long_link(link,obj) +#define GC_REGISTER_DISPLACEMENT(n)GC_register_displacement(n) +#endif +#define GC_NEW(t)((t*)GC_MALLOC(sizeof(t))) +#define GC_NEW_ATOMIC(t)((t*)GC_MALLOC_ATOMIC(sizeof(t))) +#define GC_NEW_UNCOLLECTABLE(t)((t*)GC_MALLOC_UNCOLLECTABLE(sizeof(t))) +#ifdef GC_REQUIRE_WCSDUP +GC_API GC_ATTR_MALLOC wchar_t*GC_CALL +GC_wcsdup(const wchar_t*)GC_ATTR_NONNULL(1); +GC_API GC_ATTR_MALLOC wchar_t*GC_CALL +GC_debug_wcsdup(const wchar_t*,GC_EXTRA_PARAMS)GC_ATTR_NONNULL(1); +#ifdef GC_DEBUG +#define GC_WCSDUP(s)GC_debug_wcsdup(s,GC_EXTRAS) +#else +#define GC_WCSDUP(s)GC_wcsdup(s) +#endif +#endif +typedef void (GC_CALLBACK*GC_finalization_proc)(void*, +void*); +GC_API void GC_CALL GC_register_finalizer(void*, +GC_finalization_proc,void*, +GC_finalization_proc*,void**) +GC_ATTR_NONNULL(1); +GC_API void GC_CALL GC_debug_register_finalizer(void*, +GC_finalization_proc,void*, +GC_finalization_proc*,void**) +GC_ATTR_NONNULL(1); +GC_API void GC_CALL GC_register_finalizer_ignore_self(void*, +GC_finalization_proc,void*, +GC_finalization_proc*,void**) +GC_ATTR_NONNULL(1); +GC_API void GC_CALL GC_debug_register_finalizer_ignore_self(void*, +GC_finalization_proc,void*, +GC_finalization_proc*,void**) +GC_ATTR_NONNULL(1); +GC_API void GC_CALL GC_register_finalizer_no_order(void*, +GC_finalization_proc,void*, +GC_finalization_proc*,void**) +GC_ATTR_NONNULL(1); +GC_API void GC_CALL GC_debug_register_finalizer_no_order(void*, +GC_finalization_proc,void*, +GC_finalization_proc*,void**) +GC_ATTR_NONNULL(1); +GC_API void GC_CALL GC_register_finalizer_unreachable(void*, +GC_finalization_proc,void*, +GC_finalization_proc*,void**) +GC_ATTR_NONNULL(1); +GC_API void GC_CALL GC_debug_register_finalizer_unreachable(void*, +GC_finalization_proc,void*, +GC_finalization_proc*,void**) +GC_ATTR_NONNULL(1); +#define GC_NO_MEMORY 2 +GC_API int GC_CALL GC_register_disappearing_link(void**) +GC_ATTR_NONNULL(1); +GC_API int GC_CALL GC_general_register_disappearing_link(void**, +const void*) +GC_ATTR_NONNULL(1)GC_ATTR_NONNULL(2); +GC_API int GC_CALL GC_move_disappearing_link(void**, +void**) +GC_ATTR_NONNULL(2); +GC_API int GC_CALL GC_unregister_disappearing_link(void**); +GC_API int GC_CALL GC_register_long_link(void**, +const void*) +GC_ATTR_NONNULL(1)GC_ATTR_NONNULL(2); +GC_API int GC_CALL GC_move_long_link(void**, +void**) +GC_ATTR_NONNULL(2); +GC_API int GC_CALL GC_unregister_long_link(void**); +typedef enum { +GC_TOGGLE_REF_DROP, +GC_TOGGLE_REF_STRONG, +GC_TOGGLE_REF_WEAK +} GC_ToggleRefStatus; +typedef GC_ToggleRefStatus (GC_CALLBACK*GC_toggleref_func)(void*); +GC_API void GC_CALL GC_set_toggleref_func(GC_toggleref_func); +GC_API GC_toggleref_func GC_CALL GC_get_toggleref_func(void); +GC_API int GC_CALL GC_toggleref_add(void*,int) +GC_ATTR_NONNULL(1); +typedef void (GC_CALLBACK*GC_await_finalize_proc)(void*); +GC_API void GC_CALL GC_set_await_finalize_proc(GC_await_finalize_proc); +GC_API GC_await_finalize_proc GC_CALL GC_get_await_finalize_proc(void); +GC_API int GC_CALL GC_should_invoke_finalizers(void); +GC_API int GC_CALL GC_invoke_finalizers(void); +#if defined(__GNUC__)&&!defined(__INTEL_COMPILER) +#define GC_reachable_here(ptr)__asm__ __volatile__(" "::"X"(ptr):"memory") +#else +GC_API void GC_CALL GC_noop1(GC_word); +#ifdef LINT2 +#define GC_reachable_here(ptr)GC_noop1(~(GC_word)(ptr)^(~(GC_word)0)) +#else +#define GC_reachable_here(ptr)GC_noop1((GC_word)(ptr)) +#endif +#endif +typedef void (GC_CALLBACK*GC_warn_proc)(char*, +GC_word); +GC_API void GC_CALL GC_set_warn_proc(GC_warn_proc)GC_ATTR_NONNULL(1); +GC_API GC_warn_proc GC_CALL GC_get_warn_proc(void); +GC_API void GC_CALLBACK GC_ignore_warn_proc(char*,GC_word); +GC_API void GC_CALL GC_set_log_fd(int); +typedef void (GC_CALLBACK*GC_abort_func)(const char*); +GC_API void GC_CALL GC_set_abort_func(GC_abort_func)GC_ATTR_NONNULL(1); +GC_API GC_abort_func GC_CALL GC_get_abort_func(void); +GC_API void GC_CALL GC_abort_on_oom(void); +typedef GC_word GC_hidden_pointer; +#define GC_HIDE_POINTER(p)(~(GC_hidden_pointer)(p)) +#define GC_REVEAL_POINTER(p)((void*)GC_HIDE_POINTER(p)) +#if defined(I_HIDE_POINTERS)||defined(GC_I_HIDE_POINTERS) +#define HIDE_POINTER(p)GC_HIDE_POINTER(p) +#define REVEAL_POINTER(p)GC_REVEAL_POINTER(p) +#endif +#ifdef GC_THREADS +GC_API void GC_CALL GC_alloc_lock(void); +GC_API void GC_CALL GC_alloc_unlock(void); +#else +#define GC_alloc_lock()(void)0 +#define GC_alloc_unlock()(void)0 +#endif +typedef void*(GC_CALLBACK*GC_fn_type)(void*); +GC_API void*GC_CALL GC_call_with_alloc_lock(GC_fn_type, +void*)GC_ATTR_NONNULL(1); +struct GC_stack_base { +void*mem_base; +#if defined(__ia64)||defined(__ia64__)||defined(_M_IA64) +void*reg_base; +#endif +}; +typedef void*(GC_CALLBACK*GC_stack_base_func)( +struct GC_stack_base*,void*); +GC_API void*GC_CALL GC_call_with_stack_base(GC_stack_base_func, +void*)GC_ATTR_NONNULL(1); +#define GC_SUCCESS 0 +#define GC_DUPLICATE 1 +#define GC_NO_THREADS 2 +#define GC_UNIMPLEMENTED 3 +#define GC_NOT_FOUND 4 +#if defined(GC_DARWIN_THREADS)||defined(GC_WIN32_THREADS) +GC_API void GC_CALL GC_use_threads_discovery(void); +#endif +#ifdef GC_THREADS +GC_API void GC_CALL GC_set_suspend_signal(int); +GC_API void GC_CALL GC_set_thr_restart_signal(int); +GC_API int GC_CALL GC_get_suspend_signal(void); +GC_API int GC_CALL GC_get_thr_restart_signal(void); +GC_API void GC_CALL GC_start_mark_threads(void); +GC_API void GC_CALL GC_allow_register_threads(void); +GC_API int GC_CALL GC_register_my_thread(const struct GC_stack_base*) +GC_ATTR_NONNULL(1); +GC_API int GC_CALL GC_thread_is_registered(void); +GC_API void GC_CALL GC_register_altstack(void*, +GC_word, +void*, +GC_word); +GC_API int GC_CALL GC_unregister_my_thread(void); +GC_API void GC_CALL GC_stop_world_external(void); +GC_API void GC_CALL GC_start_world_external(void); +#endif +GC_API void*GC_CALL GC_do_blocking(GC_fn_type, +void*)GC_ATTR_NONNULL(1); +GC_API void*GC_CALL GC_call_with_gc_active(GC_fn_type, +void*)GC_ATTR_NONNULL(1); +GC_API int GC_CALL GC_get_stack_base(struct GC_stack_base*) +GC_ATTR_NONNULL(1); +GC_API void*GC_CALL GC_get_my_stackbottom(struct GC_stack_base*) +GC_ATTR_NONNULL(1); +GC_API void GC_CALL GC_set_stackbottom(void*, +const struct GC_stack_base*) +GC_ATTR_NONNULL(2); +GC_API void*GC_CALL GC_same_obj(void*,void*); +GC_API void*GC_CALL GC_pre_incr(void**,ptrdiff_t) +GC_ATTR_NONNULL(1); +GC_API void*GC_CALL GC_post_incr(void**,ptrdiff_t) +GC_ATTR_NONNULL(1); +GC_API void*GC_CALL GC_is_visible(void*); +GC_API void*GC_CALL GC_is_valid_displacement(void*); +GC_API void GC_CALL GC_dump(void); +GC_API void GC_CALL GC_dump_named(const char*); +GC_API void GC_CALL GC_dump_regions(void); +GC_API void GC_CALL GC_dump_finalization(void); +#if defined(GC_DEBUG)&&defined(__GNUC__) +#define GC_PTR_ADD3(x,n,type_of_result)((type_of_result)GC_same_obj((x)+(n),(x))) +#define GC_PRE_INCR3(x,n,type_of_result)((type_of_result)GC_pre_incr((void**)(&(x)),(n)*sizeof(*x))) +#define GC_POST_INCR3(x,n,type_of_result)((type_of_result)GC_post_incr((void**)(&(x)),(n)*sizeof(*x))) +#define GC_PTR_ADD(x,n)GC_PTR_ADD3(x,n,__typeof__(x)) +#define GC_PRE_INCR(x,n)GC_PRE_INCR3(x,n,__typeof__(x)) +#define GC_POST_INCR(x)GC_POST_INCR3(x,1,__typeof__(x)) +#define GC_POST_DECR(x)GC_POST_INCR3(x,-1,__typeof__(x)) +#else +#define GC_PTR_ADD(x,n)((x)+(n)) +#define GC_PRE_INCR(x,n)((x)+=(n)) +#define GC_POST_INCR(x)((x)++) +#define GC_POST_DECR(x)((x)--) +#endif +#ifdef GC_DEBUG +#define GC_PTR_STORE(p,q)(*(void**)GC_is_visible((void*)(p))=GC_is_valid_displacement((void*)(q))) +#else +#define GC_PTR_STORE(p,q)(*(void**)(p)=(void*)(q)) +#endif +GC_API void GC_CALL GC_ptr_store_and_dirty(void*, +const void*); +GC_API void GC_CALL GC_debug_ptr_store_and_dirty(void*, +const void*); +GC_API void (GC_CALLBACK*GC_same_obj_print_proc)(void*, +void*); +GC_API void (GC_CALLBACK*GC_is_valid_displacement_print_proc)(void*); +GC_API void (GC_CALLBACK*GC_is_visible_print_proc)(void*); +#ifdef GC_PTHREADS +#ifdef __cplusplus +} +#endif +#ifdef __cplusplus +extern "C" { +#endif +#endif +GC_API GC_ATTR_MALLOC void*GC_CALL GC_malloc_many(size_t); +#define GC_NEXT(p)(*(void**)(p)) +typedef int (GC_CALLBACK*GC_has_static_roots_func)( +const char*, +void*, +size_t); +GC_API void GC_CALL GC_register_has_static_roots_callback( +GC_has_static_roots_func); +#if!defined(CPPCHECK)&&!defined(GC_WINDOWS_H_INCLUDED)&&defined(WINAPI) +#define GC_WINDOWS_H_INCLUDED +#endif +#if defined(GC_WIN32_THREADS)&&(!defined(GC_PTHREADS)||defined(GC_BUILD)||defined(GC_WINDOWS_H_INCLUDED)) +#if (!defined(GC_NO_THREAD_DECLS)||defined(GC_BUILD))&&!defined(GC_DONT_INCL_WINDOWS_H) +#ifdef __cplusplus +} +#endif +#if!defined(_WIN32_WCE)&&!defined(__CEGCC__) +#include +#endif +#if defined(GC_BUILD)||!defined(GC_DONT_INCLUDE_WINDOWS_H) +#include +#define GC_WINDOWS_H_INCLUDED +#endif +#ifdef __cplusplus +extern "C" { +#endif +#ifdef GC_UNDERSCORE_STDCALL +#define GC_CreateThread _GC_CreateThread +#define GC_ExitThread _GC_ExitThread +#endif +#ifndef DECLSPEC_NORETURN +#ifdef GC_WINDOWS_H_INCLUDED +#define DECLSPEC_NORETURN +#else +#define DECLSPEC_NORETURN __declspec(noreturn) +#endif +#endif +#if!defined(_UINTPTR_T)&&!defined(_UINTPTR_T_DEFINED)&&!defined(UINTPTR_MAX) +typedef GC_word GC_uintptr_t; +#else +typedef uintptr_t GC_uintptr_t; +#endif +#ifdef _WIN64 +#define GC_WIN32_SIZE_T GC_uintptr_t +#elif defined(GC_WINDOWS_H_INCLUDED) +#define GC_WIN32_SIZE_T DWORD +#else +#define GC_WIN32_SIZE_T unsigned long +#endif +#ifdef GC_INSIDE_DLL +#ifdef GC_UNDERSCORE_STDCALL +#define GC_DllMain _GC_DllMain +#endif +#ifdef GC_WINDOWS_H_INCLUDED +GC_API BOOL WINAPI GC_DllMain(HINSTANCE, +ULONG, +LPVOID); +#else +GC_API int __stdcall GC_DllMain(void*,unsigned long,void*); +#endif +#endif +#ifdef GC_WINDOWS_H_INCLUDED +GC_API HANDLE WINAPI GC_CreateThread( +LPSECURITY_ATTRIBUTES, +GC_WIN32_SIZE_T, +LPTHREAD_START_ROUTINE, +LPVOID,DWORD, +LPDWORD); +GC_API DECLSPEC_NORETURN void WINAPI GC_ExitThread( +DWORD); +#else +struct _SECURITY_ATTRIBUTES; +GC_API void*__stdcall GC_CreateThread(struct _SECURITY_ATTRIBUTES*, +GC_WIN32_SIZE_T, +unsigned long (__stdcall*)(void*), +void*,unsigned long,unsigned long*); +GC_API DECLSPEC_NORETURN void __stdcall GC_ExitThread(unsigned long); +#endif +#if!defined(_WIN32_WCE)&&!defined(__CEGCC__) +GC_API GC_uintptr_t GC_CALL GC_beginthreadex( +void*,unsigned, +unsigned (__stdcall*)(void*), +void*,unsigned, +unsigned*); +GC_API void GC_CALL GC_endthreadex(unsigned); +#endif +#endif +#ifdef GC_WINMAIN_REDIRECT +#define WinMain GC_WinMain +#endif +#define GC_use_DllMain GC_use_threads_discovery +#ifndef GC_NO_THREAD_REDIRECTS +#define CreateThread GC_CreateThread +#define ExitThread GC_ExitThread +#undef _beginthreadex +#define _beginthreadex GC_beginthreadex +#undef _endthreadex +#define _endthreadex GC_endthreadex +#endif +#endif +GC_API void GC_CALL GC_set_force_unmap_on_gcollect(int); +GC_API int GC_CALL GC_get_force_unmap_on_gcollect(void); +#if defined(__CYGWIN32__)||defined(__CYGWIN__) +#ifdef __x86_64__ +extern int __data_start__[],__data_end__[]; +extern int __bss_start__[],__bss_end__[]; +#define GC_DATASTART ((GC_word)__data_start__ < (GC_word)__bss_start__?(void*)__data_start__:(void*)__bss_start__) +#define GC_DATAEND ((GC_word)__data_end__ > (GC_word)__bss_end__?(void*)__data_end__:(void*)__bss_end__) +#else +extern int _data_start__[],_data_end__[],_bss_start__[],_bss_end__[]; +#define GC_DATASTART ((GC_word)_data_start__ < (GC_word)_bss_start__?(void*)_data_start__:(void*)_bss_start__) +#define GC_DATAEND ((GC_word)_data_end__ > (GC_word)_bss_end__?(void*)_data_end__:(void*)_bss_end__) +#endif +#define GC_INIT_CONF_ROOTS GC_add_roots(GC_DATASTART,GC_DATAEND);GC_gcollect() +#elif defined(_AIX) +extern int _data[],_end[]; +#define GC_DATASTART ((void*)_data) +#define GC_DATAEND ((void*)_end) +#define GC_INIT_CONF_ROOTS GC_add_roots(GC_DATASTART,GC_DATAEND) +#elif (defined(HOST_ANDROID)||defined(__ANDROID__))&&defined(IGNORE_DYNAMIC_LOADING) +#pragma weak __dso_handle +extern int __dso_handle[]; +GC_API void*GC_CALL GC_find_limit(void*,int); +#define GC_INIT_CONF_ROOTS (void)(__dso_handle!=0?(GC_add_roots(__dso_handle,GC_find_limit(__dso_handle,1)),0):0) +#else +#define GC_INIT_CONF_ROOTS +#endif +#ifdef GC_DONT_EXPAND +#define GC_INIT_CONF_DONT_EXPAND GC_set_dont_expand(1) +#else +#define GC_INIT_CONF_DONT_EXPAND +#endif +#ifdef GC_FORCE_UNMAP_ON_GCOLLECT +#define GC_INIT_CONF_FORCE_UNMAP_ON_GCOLLECT GC_set_force_unmap_on_gcollect(1) +#else +#define GC_INIT_CONF_FORCE_UNMAP_ON_GCOLLECT +#endif +#ifdef GC_DONT_GC +#define GC_INIT_CONF_MAX_RETRIES (void)(GC_dont_gc=1) +#elif defined(GC_MAX_RETRIES)&&!defined(CPPCHECK) +#define GC_INIT_CONF_MAX_RETRIES GC_set_max_retries(GC_MAX_RETRIES) +#else +#define GC_INIT_CONF_MAX_RETRIES +#endif +#if defined(GC_ALLOCD_BYTES_PER_FINALIZER)&&!defined(CPPCHECK) +#define GC_INIT_CONF_ALLOCD_BYTES_PER_FINALIZER GC_set_allocd_bytes_per_finalizer(GC_ALLOCD_BYTES_PER_FINALIZER) +#else +#define GC_INIT_CONF_ALLOCD_BYTES_PER_FINALIZER +#endif +#if defined(GC_FREE_SPACE_DIVISOR)&&!defined(CPPCHECK) +#define GC_INIT_CONF_FREE_SPACE_DIVISOR GC_set_free_space_divisor(GC_FREE_SPACE_DIVISOR) +#else +#define GC_INIT_CONF_FREE_SPACE_DIVISOR +#endif +#if defined(GC_FULL_FREQ)&&!defined(CPPCHECK) +#define GC_INIT_CONF_FULL_FREQ GC_set_full_freq(GC_FULL_FREQ) +#else +#define GC_INIT_CONF_FULL_FREQ +#endif +#if defined(GC_TIME_LIMIT)&&!defined(CPPCHECK) +#define GC_INIT_CONF_TIME_LIMIT GC_set_time_limit(GC_TIME_LIMIT) +#else +#define GC_INIT_CONF_TIME_LIMIT +#endif +#if defined(GC_MARKERS)&&defined(GC_THREADS)&&!defined(CPPCHECK) +#define GC_INIT_CONF_MARKERS GC_set_markers_count(GC_MARKERS) +#else +#define GC_INIT_CONF_MARKERS +#endif +#if defined(GC_SIG_SUSPEND)&&defined(GC_THREADS)&&!defined(CPPCHECK) +#define GC_INIT_CONF_SUSPEND_SIGNAL GC_set_suspend_signal(GC_SIG_SUSPEND) +#else +#define GC_INIT_CONF_SUSPEND_SIGNAL +#endif +#if defined(GC_SIG_THR_RESTART)&&defined(GC_THREADS)&&!defined(CPPCHECK) +#define GC_INIT_CONF_THR_RESTART_SIGNAL GC_set_thr_restart_signal(GC_SIG_THR_RESTART) +#else +#define GC_INIT_CONF_THR_RESTART_SIGNAL +#endif +#if defined(GC_MAXIMUM_HEAP_SIZE)&&!defined(CPPCHECK) +#define GC_INIT_CONF_MAXIMUM_HEAP_SIZE GC_set_max_heap_size(GC_MAXIMUM_HEAP_SIZE) +#else +#define GC_INIT_CONF_MAXIMUM_HEAP_SIZE +#endif +#ifdef GC_IGNORE_WARN +#define GC_INIT_CONF_IGNORE_WARN GC_set_warn_proc(GC_ignore_warn_proc) +#else +#define GC_INIT_CONF_IGNORE_WARN +#endif +#if defined(GC_INITIAL_HEAP_SIZE)&&!defined(CPPCHECK) +#define GC_INIT_CONF_INITIAL_HEAP_SIZE { size_t heap_size=GC_get_heap_size();if (heap_size < (GC_INITIAL_HEAP_SIZE))(void)GC_expand_hp((GC_INITIAL_HEAP_SIZE)- heap_size);} +#else +#define GC_INIT_CONF_INITIAL_HEAP_SIZE +#endif +#define GC_INIT(){ GC_INIT_CONF_DONT_EXPAND;GC_INIT_CONF_FORCE_UNMAP_ON_GCOLLECT;GC_INIT_CONF_MAX_RETRIES;GC_INIT_CONF_ALLOCD_BYTES_PER_FINALIZER;GC_INIT_CONF_FREE_SPACE_DIVISOR;GC_INIT_CONF_FULL_FREQ;GC_INIT_CONF_TIME_LIMIT;GC_INIT_CONF_MARKERS;GC_INIT_CONF_SUSPEND_SIGNAL;GC_INIT_CONF_THR_RESTART_SIGNAL;GC_INIT_CONF_MAXIMUM_HEAP_SIZE;GC_init();GC_INIT_CONF_ROOTS;GC_INIT_CONF_IGNORE_WARN;GC_INIT_CONF_INITIAL_HEAP_SIZE;} +GC_API void GC_CALL GC_win32_free_heap(void); +#if defined(__SYMBIAN32__) +void GC_init_global_static_roots(void); +#endif +#if defined(_AMIGA)&&!defined(GC_AMIGA_MAKINGLIB) +void*GC_amiga_realloc(void*,size_t); +#define GC_realloc(a,b)GC_amiga_realloc(a,b) +void GC_amiga_set_toany(void (*)(void)); +extern int GC_amiga_free_space_divisor_inc; +extern void*(*GC_amiga_allocwrapper_do)(size_t,void*(GC_CALL*)(size_t)); +#define GC_malloc(a)(*GC_amiga_allocwrapper_do)(a,GC_malloc) +#define GC_malloc_atomic(a)(*GC_amiga_allocwrapper_do)(a,GC_malloc_atomic) +#define GC_malloc_uncollectable(a)(*GC_amiga_allocwrapper_do)(a,GC_malloc_uncollectable) +#define GC_malloc_atomic_uncollectable(a)(*GC_amiga_allocwrapper_do)(a,GC_malloc_atomic_uncollectable) +#define GC_malloc_ignore_off_page(a)(*GC_amiga_allocwrapper_do)(a,GC_malloc_ignore_off_page) +#define GC_malloc_atomic_ignore_off_page(a)(*GC_amiga_allocwrapper_do)(a,GC_malloc_atomic_ignore_off_page) +#endif +#ifdef __cplusplus +} +#endif +#endif diff --git a/v_windows/v/old/thirdparty/mssql/include/.gitignore b/v_windows/v/old/thirdparty/mssql/include/.gitignore new file mode 100644 index 0000000..773ce27 --- /dev/null +++ b/v_windows/v/old/thirdparty/mssql/include/.gitignore @@ -0,0 +1,4 @@ +* + +!.gitignore +!mssql.h \ No newline at end of file diff --git a/v_windows/v/old/thirdparty/mssql/include/mssql.h b/v_windows/v/old/thirdparty/mssql/include/mssql.h new file mode 100644 index 0000000..d38768c --- /dev/null +++ b/v_windows/v/old/thirdparty/mssql/include/mssql.h @@ -0,0 +1,20 @@ +// Hacking some headers in windows. +// sql headers using UNICODE to change function signatures. +// Currently Linux bindings do not use unicode SQL C bindings, +// So we turn off the UNICODE to make it compile on windows. +// For future Unicode support, please raise a issue. + +#include +#include + +#ifdef UNICODE +// Turn off unicode macro and turn back on, so it only affects sql headers +#undef UNICODE +#include +#include +#define UNICODE + +#else +#include +#include +#endif \ No newline at end of file diff --git a/v_windows/v/old/thirdparty/picoev/picoev.c b/v_windows/v/old/thirdparty/picoev/picoev.c new file mode 100644 index 0000000..bd6691a --- /dev/null +++ b/v_windows/v/old/thirdparty/picoev/picoev.c @@ -0,0 +1,9 @@ +#ifdef __linux__ + #include "src/picoev_epoll.c" +#elif __APPLE__ + #include "src/picoev_kqueue.c" +#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) + #include "src/picoev_kqueue.c" +#else + #include "src/picoev_select.c" +#endif diff --git a/v_windows/v/old/thirdparty/picoev/src/README.md b/v_windows/v/old/thirdparty/picoev/src/README.md new file mode 100644 index 0000000..9619e08 --- /dev/null +++ b/v_windows/v/old/thirdparty/picoev/src/README.md @@ -0,0 +1,21 @@ +picoev +====== + +A *tiny*, *lightning fast* event loop for network applications + +The text below is copied from the [original publication](http://developer.cybozu.co.jp/archives/kazuho/2009/08/picoev-a-tiny-e.html) + +I am sure many programmers writing network applications have their own abstracting layers hiding the differences between various I/O multiplex APIs, like select(2), poll(2), epoll(2), ... And of course, I am one among them. While writing mycached ([see Mycached: memcached protocol support for MySQL for more information](http://developer.cybozu.co.jp/archives/kazuho/2009/08/mycached-memcac.html)), I was at first considering of using [libev](http://software.schmorp.de/pkg/libev.html) for multiplexing socket I/Os. [Libevent](http://www.monkey.org/~provos/libevent/) was not an option since it does not (yet) provide multithreading support. + +But it was a great pain for me to learn how to use libev. I do not mean that its is an ugly product. In fact, I think that it is a very well written, excellent library. However, for me it was too much a boring task to learn how the things are abstracted, already being familiar with the problems it tries to hide. + +So instead I thought it might be a good occasion to write my own library that could be used in any programs I may write in the future. The result is picoev, and it is faster than libevent or libev! The benchmark used is a re-modified version taken from libev.schmorp.de/bench.html and can be found [here](http://coderepos.org/share/browser/lang/c/picoev/trunk/example/bench.c). +![setup time](http://developer.cybozu.co.jp/archives/kazuho/files/picoev_setup.png) +![event processing time](http://developer.cybozu.co.jp/archives/kazuho/files/picoev_event.png) + +Why is it faster? It is because it uses an array and a ring buffer of bit vectors as its internal structure. Libevent and libev seem to use some kind of sorted tree to represent file descriptors. However, if we concentrate on Un*x systems, there is a guarantee that the descriptors will be a small positive integer. Picoev utilizes the fact and stores information related to file descriptors (such as pointers to callback functions or callback arguments) in an array, resulting in a faster manipulation of socket states. + +Another optimization technique used by picoev is not to use an ordered tree for keeping timeout information. Generally speaking, most network applications do not require accurate timeouts. Thus it is possible to use a ring buffer (a sliding array) of bit vectors for the purpose. Each bit vector represents a set of file descriptors that time-outs at a given time. Picoev uses 128 of bit vectors to represent timeouts, for example, the first bit vector represents the sockets that timeout a second after, the second bit vector representing them of two seconds after..., and the bit vectors slide every second. If the maximum timeout required by the web application is greater than 128, the minimum granurality of timeout becomes two seconds. + +I would like to reiterate that both libevent and libev are great libraries. Picoev is not at comparable to them especially in maturity and the number of features. It only supports select(2), epoll(2), and kqueue(2) for the time being. However the design is simple than the other two, and I think it will be a good starting point to write network applications, or to use as a basis for writing one's own network libraries. + diff --git a/v_windows/v/old/thirdparty/picoev/src/picoev.h b/v_windows/v/old/thirdparty/picoev/src/picoev.h new file mode 100644 index 0000000..b00b02b --- /dev/null +++ b/v_windows/v/old/thirdparty/picoev/src/picoev.h @@ -0,0 +1,404 @@ +/* + * Copyright (c) 2009, Cybozu Labs, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef picoev_h +#define picoev_h + +#ifdef __cplusplus +extern "C" { +# define PICOEV_INLINE inline +#else +# define PICOEV_INLINE static __inline__ +#endif + +#include +#include +#include +#include +#include + +#define PICOEV_IS_INITED (picoev.max_fd != 0) +#define PICOEV_IS_INITED_AND_FD_IN_RANGE(fd) \ + (((unsigned)fd) < (unsigned)picoev.max_fd) +#define PICOEV_TOO_MANY_LOOPS (picoev.num_loops != 0) /* use after ++ */ +#define PICOEV_FD_BELONGS_TO_LOOP(loop, fd) \ + ((loop)->loop_id == picoev.fds[fd].loop_id) + +#define PICOEV_TIMEOUT_VEC_OF(loop, idx) \ + ((loop)->timeout.vec + (idx) * picoev.timeout_vec_size) +#define PICOEV_TIMEOUT_VEC_OF_VEC_OF(loop, idx) \ + ((loop)->timeout.vec_of_vec + (idx) * picoev.timeout_vec_of_vec_size) +#define PICOEV_RND_UP(v, d) (((v) + (d) - 1) / (d) * (d)) + +#define PICOEV_PAGE_SIZE 4096 +#define PICOEV_CACHE_LINE_SIZE 32 /* in bytes, ok if greater than the actual */ +#define PICOEV_SIMD_BITS 128 +#define PICOEV_TIMEOUT_VEC_SIZE 128 +#define PICOEV_SHORT_BITS (sizeof(short) * 8) + +#define PICOEV_READ 1 +#define PICOEV_WRITE 2 +#define PICOEV_TIMEOUT 4 +#define PICOEV_ADD 0x40000000 +#define PICOEV_DEL 0x20000000 +#define PICOEV_READWRITE (PICOEV_READ | PICOEV_WRITE) + +#define PICOEV_TIMEOUT_IDX_UNUSED (UCHAR_MAX) + + typedef unsigned short picoev_loop_id_t; + + typedef struct picoev_loop_st picoev_loop; + + typedef void picoev_handler(picoev_loop* loop, int fd, int revents, + void* cb_arg); + + typedef struct picoev_fd_st { + /* use accessors! */ + /* TODO adjust the size to match that of a cache line */ + picoev_handler* callback; + void* cb_arg; + picoev_loop_id_t loop_id; + char events; + unsigned char timeout_idx; /* PICOEV_TIMEOUT_IDX_UNUSED if not used */ + int _backend; /* can be used by backends (never modified by core) */ + } picoev_fd; + + struct picoev_loop_st { + /* read only */ + picoev_loop_id_t loop_id; + struct { + short* vec; + short* vec_of_vec; + size_t base_idx; + time_t base_time; + int resolution; + void* _free_addr; + } timeout; + time_t now; + }; + + typedef struct picoev_globals_st { + /* read only */ + picoev_fd* fds; + void* _fds_free_addr; + int max_fd; + int num_loops; + size_t timeout_vec_size; /* # of elements in picoev_loop.timeout.vec[0] */ + size_t timeout_vec_of_vec_size; /* ... in timeout.vec_of_vec[0] */ + } picoev_globals; + + extern picoev_globals picoev; + + /* creates a new event loop (defined by each backend) */ + picoev_loop* picoev_create_loop(int max_timeout); + + /* destroys a loop (defined by each backend) */ + int picoev_destroy_loop(picoev_loop* loop); + + /* internal: updates events to be watched (defined by each backend) */ + int picoev_update_events_internal(picoev_loop* loop, int fd, int events); + + /* internal: poll once and call the handlers (defined by each backend) */ + int picoev_poll_once_internal(picoev_loop* loop, int max_wait); + + /* internal, aligned allocator with address scrambling to avoid cache + line contention */ + PICOEV_INLINE + void* picoev_memalign(size_t sz, void** orig_addr, int clear) { + sz = sz + PICOEV_PAGE_SIZE + PICOEV_CACHE_LINE_SIZE; + if ((*orig_addr = malloc(sz)) == NULL) { + return NULL; + } + if (clear != 0) { + memset(*orig_addr, 0, sz); + } + return + (void*)PICOEV_RND_UP((unsigned long)*orig_addr + + (rand() % PICOEV_PAGE_SIZE), + PICOEV_CACHE_LINE_SIZE); + } + + /* initializes picoev */ + PICOEV_INLINE + int picoev_init(int max_fd) { + assert(! PICOEV_IS_INITED); + assert(max_fd > 0); + if ((picoev.fds = (picoev_fd*)picoev_memalign(sizeof(picoev_fd) * max_fd, + &picoev._fds_free_addr, 1)) + == NULL) { + return -1; + } + picoev.max_fd = max_fd; + picoev.num_loops = 0; + picoev.timeout_vec_size + = PICOEV_RND_UP(picoev.max_fd, PICOEV_SIMD_BITS) / PICOEV_SHORT_BITS; + picoev.timeout_vec_of_vec_size + = PICOEV_RND_UP(picoev.timeout_vec_size, PICOEV_SIMD_BITS) + / PICOEV_SHORT_BITS; + return 0; + } + + /* deinitializes picoev */ + PICOEV_INLINE + int picoev_deinit(void) { + assert(PICOEV_IS_INITED); + free(picoev._fds_free_addr); + picoev.fds = NULL; + picoev._fds_free_addr = NULL; + picoev.max_fd = 0; + picoev.num_loops = 0; + return 0; + } + + /* updates timeout */ + PICOEV_INLINE + void picoev_set_timeout(picoev_loop* loop, int fd, int secs) { + picoev_fd* target; + short* vec, * vec_of_vec; + size_t vi = fd / PICOEV_SHORT_BITS, delta; + assert(PICOEV_IS_INITED_AND_FD_IN_RANGE(fd)); + assert(PICOEV_FD_BELONGS_TO_LOOP(loop, fd)); + target = picoev.fds + fd; + /* clear timeout */ + if (target->timeout_idx != PICOEV_TIMEOUT_IDX_UNUSED) { + vec = PICOEV_TIMEOUT_VEC_OF(loop, target->timeout_idx); + if ((vec[vi] &= ~((unsigned short)SHRT_MIN >> (fd % PICOEV_SHORT_BITS))) + == 0) { + vec_of_vec = PICOEV_TIMEOUT_VEC_OF_VEC_OF(loop, target->timeout_idx); + vec_of_vec[vi / PICOEV_SHORT_BITS] + &= ~((unsigned short)SHRT_MIN >> (vi % PICOEV_SHORT_BITS)); + } + target->timeout_idx = PICOEV_TIMEOUT_IDX_UNUSED; + } + if (secs != 0) { + delta = (loop->now + secs - loop->timeout.base_time) + / loop->timeout.resolution; + if (delta >= PICOEV_TIMEOUT_VEC_SIZE) { + delta = PICOEV_TIMEOUT_VEC_SIZE - 1; + } + target->timeout_idx = + (loop->timeout.base_idx + delta) % PICOEV_TIMEOUT_VEC_SIZE; + vec = PICOEV_TIMEOUT_VEC_OF(loop, target->timeout_idx); + vec[vi] |= (unsigned short)SHRT_MIN >> (fd % PICOEV_SHORT_BITS); + vec_of_vec = PICOEV_TIMEOUT_VEC_OF_VEC_OF(loop, target->timeout_idx); + vec_of_vec[vi / PICOEV_SHORT_BITS] + |= (unsigned short)SHRT_MIN >> (vi % PICOEV_SHORT_BITS); + } + } + + /* registers a file descriptor and callback argument to a event loop */ + PICOEV_INLINE + int picoev_add(picoev_loop* loop, int fd, int events, int timeout_in_secs, + picoev_handler* callback, void* cb_arg) { + picoev_fd* target; + if (!PICOEV_IS_INITED_AND_FD_IN_RANGE(fd)) { return -1; } + target = picoev.fds + fd; + assert(target->loop_id == 0); + target->callback = callback; + target->cb_arg = cb_arg; + target->loop_id = loop->loop_id; + target->events = 0; + target->timeout_idx = PICOEV_TIMEOUT_IDX_UNUSED; + if (picoev_update_events_internal(loop, fd, events | PICOEV_ADD) != 0) { + target->loop_id = 0; + return -1; + } + picoev_set_timeout(loop, fd, timeout_in_secs); + return 0; + } + + /* unregisters a file descriptor from event loop */ + PICOEV_INLINE + int picoev_del(picoev_loop* loop, int fd) { + picoev_fd* target; + assert(PICOEV_IS_INITED_AND_FD_IN_RANGE(fd)); + target = picoev.fds + fd; + if (picoev_update_events_internal(loop, fd, PICOEV_DEL) != 0) { + return -1; + } + picoev_set_timeout(loop, fd, 0); + target->loop_id = 0; + return 0; + } + + /* check if fd is registered (checks all loops if loop == NULL) */ + PICOEV_INLINE + int picoev_is_active(picoev_loop* loop, int fd) { + assert(PICOEV_IS_INITED_AND_FD_IN_RANGE(fd)); + return loop != NULL + ? picoev.fds[fd].loop_id == loop->loop_id + : picoev.fds[fd].loop_id != 0; + } + + /* returns events being watched for given descriptor */ + PICOEV_INLINE + int picoev_get_events(picoev_loop* loop __attribute__((unused)), int fd) { + assert(PICOEV_IS_INITED_AND_FD_IN_RANGE(fd)); + return picoev.fds[fd].events & PICOEV_READWRITE; + } + + /* sets events to be watched for given desriptor */ + PICOEV_INLINE + int picoev_set_events(picoev_loop* loop, int fd, int events) { + assert(PICOEV_IS_INITED_AND_FD_IN_RANGE(fd)); + if (picoev.fds[fd].events != events + && picoev_update_events_internal(loop, fd, events) != 0) { + return -1; + } + return 0; + } + + /* returns callback for given descriptor */ + PICOEV_INLINE + picoev_handler* picoev_get_callback(picoev_loop* loop __attribute__((unused)), + int fd, void** cb_arg) { + assert(PICOEV_IS_INITED_AND_FD_IN_RANGE(fd)); + if (cb_arg != NULL) { + *cb_arg = picoev.fds[fd].cb_arg; + } + return picoev.fds[fd].callback; + } + + /* sets callback for given descriptor */ + PICOEV_INLINE + void picoev_set_callback(picoev_loop* loop __attribute__((unused)), int fd, + picoev_handler* callback, void** cb_arg) { + assert(PICOEV_IS_INITED_AND_FD_IN_RANGE(fd)); + if (cb_arg != NULL) { + picoev.fds[fd].cb_arg = *cb_arg; + } + picoev.fds[fd].callback = callback; + } + + /* function to iterate registered information. To start iteration, set curfd + to -1 and call the function until -1 is returned */ + PICOEV_INLINE + int picoev_next_fd(picoev_loop* loop, int curfd) { + if (curfd != -1) { + assert(PICOEV_IS_INITED_AND_FD_IN_RANGE(curfd)); + } + while (++curfd < picoev.max_fd) { + if (loop->loop_id == picoev.fds[curfd].loop_id) { + return curfd; + } + } + return -1; + } + + /* internal function */ + PICOEV_INLINE + int picoev_init_loop_internal(picoev_loop* loop, int max_timeout) { + loop->loop_id = ++picoev.num_loops; + assert(PICOEV_TOO_MANY_LOOPS); + if ((loop->timeout.vec_of_vec + = (short*)picoev_memalign((picoev.timeout_vec_of_vec_size + + picoev.timeout_vec_size) + * sizeof(short) * PICOEV_TIMEOUT_VEC_SIZE, + &loop->timeout._free_addr, 1)) + == NULL) { + --picoev.num_loops; + return -1; + } + loop->timeout.vec = loop->timeout.vec_of_vec + + picoev.timeout_vec_of_vec_size * PICOEV_TIMEOUT_VEC_SIZE; + loop->timeout.base_idx = 0; + loop->timeout.base_time = time(NULL); + loop->timeout.resolution + = PICOEV_RND_UP(max_timeout, PICOEV_TIMEOUT_VEC_SIZE) + / PICOEV_TIMEOUT_VEC_SIZE; + return 0; + } + + /* internal function */ + PICOEV_INLINE + void picoev_deinit_loop_internal(picoev_loop* loop) { + free(loop->timeout._free_addr); + } + + /* internal function */ + PICOEV_INLINE + void picoev_handle_timeout_internal(picoev_loop* loop) { + size_t i, j, k; + for (; + loop->timeout.base_time <= loop->now - loop->timeout.resolution; + loop->timeout.base_idx + = (loop->timeout.base_idx + 1) % PICOEV_TIMEOUT_VEC_SIZE, + loop->timeout.base_time += loop->timeout.resolution) { + /* TODO use SIMD instructions */ + short* vec = PICOEV_TIMEOUT_VEC_OF(loop, loop->timeout.base_idx); + short* vec_of_vec + = PICOEV_TIMEOUT_VEC_OF_VEC_OF(loop, loop->timeout.base_idx); + for (i = 0; i < picoev.timeout_vec_of_vec_size; ++i) { + short vv = vec_of_vec[i]; + if (vv != 0) { + for (j = i * PICOEV_SHORT_BITS; vv != 0; j++, vv <<= 1) { + if (vv < 0) { + short v = vec[j]; + assert(v != 0); + for (k = j * PICOEV_SHORT_BITS; v != 0; k++, v <<= 1) { + if (v < 0) { + picoev_fd* fd = picoev.fds + k; + assert(fd->loop_id == loop->loop_id); + fd->timeout_idx = PICOEV_TIMEOUT_IDX_UNUSED; + (*fd->callback)(loop, k, PICOEV_TIMEOUT, fd->cb_arg); + } + } + vec[j] = 0; + } + } + vec_of_vec[i] = 0; + } + } + } + } + + /* loop once */ + PICOEV_INLINE + int picoev_loop_once(picoev_loop* loop, int max_wait) { + loop->now = time(NULL); + if (max_wait > loop->timeout.resolution) { + max_wait = loop->timeout.resolution; + } + if (picoev_poll_once_internal(loop, max_wait) != 0) { + return -1; + } + if (max_wait != 0) { + loop->now = time(NULL); + } + picoev_handle_timeout_internal(loop); + return 0; + } + +#undef PICOEV_INLINE + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/v_windows/v/old/thirdparty/picoev/src/picoev_epoll.c b/v_windows/v/old/thirdparty/picoev/src/picoev_epoll.c new file mode 100644 index 0000000..61bda4d --- /dev/null +++ b/v_windows/v/old/thirdparty/picoev/src/picoev_epoll.c @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2009, Cybozu Labs, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include "picoev.h" + +#ifndef PICOEV_EPOLL_DEFER_DELETES +# define PICOEV_EPOLL_DEFER_DELETES 1 +#endif + +typedef struct picoev_loop_epoll_st { + picoev_loop loop; + int epfd; + struct epoll_event events[1024]; +} picoev_loop_epoll; + +picoev_globals picoev; + +picoev_loop* picoev_create_loop(int max_timeout) +{ + picoev_loop_epoll* loop; + + /* init parent */ + assert(PICOEV_IS_INITED); + if ((loop = (picoev_loop_epoll*)malloc(sizeof(picoev_loop_epoll))) == NULL) { + return NULL; + } + if (picoev_init_loop_internal(&loop->loop, max_timeout) != 0) { + free(loop); + return NULL; + } + + /* init myself */ + if ((loop->epfd = epoll_create(picoev.max_fd)) == -1) { + picoev_deinit_loop_internal(&loop->loop); + free(loop); + return NULL; + } + + loop->loop.now = time(NULL); + return &loop->loop; +} + +int picoev_destroy_loop(picoev_loop* _loop) +{ + picoev_loop_epoll* loop = (picoev_loop_epoll*)_loop; + + if (close(loop->epfd) != 0) { + return -1; + } + picoev_deinit_loop_internal(&loop->loop); + free(loop); + return 0; +} + +int picoev_update_events_internal(picoev_loop* _loop, int fd, int events) +{ + picoev_loop_epoll* loop = (picoev_loop_epoll*)_loop; + picoev_fd* target = picoev.fds + fd; + struct epoll_event ev; + int epoll_ret; + + memset( &ev, 0, sizeof( ev ) ); + assert(PICOEV_FD_BELONGS_TO_LOOP(&loop->loop, fd)); + + if ((events & PICOEV_READWRITE) == target->events) { + return 0; + } + + ev.events = ((events & PICOEV_READ) != 0 ? EPOLLIN : 0) + | ((events & PICOEV_WRITE) != 0 ? EPOLLOUT : 0); + ev.data.fd = fd; + +#define SET(op, check_error) do { \ + epoll_ret = epoll_ctl(loop->epfd, op, fd, &ev); \ + assert(! check_error || epoll_ret == 0); \ + } while (0) + +#if PICOEV_EPOLL_DEFER_DELETES + + if ((events & PICOEV_DEL) != 0) { + /* nothing to do */ + } else if ((events & PICOEV_READWRITE) == 0) { + SET(EPOLL_CTL_DEL, 1); + } else { + SET(EPOLL_CTL_MOD, 0); + if (epoll_ret != 0) { + assert(errno == ENOENT); + SET(EPOLL_CTL_ADD, 1); + } + } + +#else + + if ((events & PICOEV_READWRITE) == 0) { + SET(EPOLL_CTL_DEL, 1); + } else { + SET(target->events == 0 ? EPOLL_CTL_ADD : EPOLL_CTL_MOD, 1); + } + +#endif + +#undef SET + + target->events = events; + + return 0; +} + +int picoev_poll_once_internal(picoev_loop* _loop, int max_wait) +{ + picoev_loop_epoll* loop = (picoev_loop_epoll*)_loop; + int i, nevents; + + nevents = epoll_wait(loop->epfd, loop->events, + sizeof(loop->events) / sizeof(loop->events[0]), + max_wait * 1000); + if (nevents == -1) { + return -1; + } + for (i = 0; i < nevents; ++i) { + struct epoll_event* event = loop->events + i; + picoev_fd* target = picoev.fds + event->data.fd; + if (loop->loop.loop_id == target->loop_id + && (target->events & PICOEV_READWRITE) != 0) { + int revents = ((event->events & EPOLLIN) != 0 ? PICOEV_READ : 0) + | ((event->events & EPOLLOUT) != 0 ? PICOEV_WRITE : 0); + if (revents != 0) { + (*target->callback)(&loop->loop, event->data.fd, revents, + target->cb_arg); + } + } else { +#if PICOEV_EPOLL_DEFER_DELETES + event->events = 0; + epoll_ctl(loop->epfd, EPOLL_CTL_DEL, event->data.fd, event); +#endif + } + } + return 0; +} diff --git a/v_windows/v/old/thirdparty/picoev/src/picoev_kqueue.c b/v_windows/v/old/thirdparty/picoev/src/picoev_kqueue.c new file mode 100644 index 0000000..a45a052 --- /dev/null +++ b/v_windows/v/old/thirdparty/picoev/src/picoev_kqueue.c @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2009, Cybozu Labs, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include "picoev.h" + +#define EV_QUEUE_SZ 128 + +#define BACKEND_BUILD(next_fd, events) \ + ((unsigned)((next_fd << 8) | (events & 0xff))) +#define BACKEND_GET_NEXT_FD(backend) ((int)(backend) >> 8) +#define BACKEND_GET_OLD_EVENTS(backend) ((int)(backend) & 0xff) + +typedef struct picoev_loop_kqueue_st { + picoev_loop loop; + int kq; + int changed_fds; /* link list using picoev_fd::_backend, -1 if not changed */ + struct kevent events[1024]; + struct kevent changelist[256]; +} picoev_loop_kqueue; + +picoev_globals picoev; + +static int apply_pending_changes(picoev_loop_kqueue* loop, int apply_all) +{ +#define SET(op, events) \ + EV_SET(loop->changelist + cl_off++, loop->changed_fds, \ + (((events) & PICOEV_READ) != 0 ? EVFILT_READ : 0) \ + | (((events) & PICOEV_WRITE) != 0 ? EVFILT_WRITE : 0), \ + (op), 0, 0, NULL) + + int cl_off = 0, nevents; + + while (loop->changed_fds != -1) { + picoev_fd* changed = picoev.fds + loop->changed_fds; + int old_events = BACKEND_GET_OLD_EVENTS(changed->_backend); + if (changed->events != old_events) { + if (old_events != 0) { + SET(EV_DISABLE, old_events); + } + if (changed->events != 0) { + SET(EV_ADD | EV_ENABLE, changed->events); + } + if ((size_t)cl_off + 1 + >= sizeof(loop->changelist) / sizeof(loop->changelist[0])) { + nevents = kevent(loop->kq, loop->changelist, cl_off, NULL, 0, NULL); + assert(nevents == 0); + cl_off = 0; + } + } + loop->changed_fds = BACKEND_GET_NEXT_FD(changed->_backend); + changed->_backend = -1; + } + + if (apply_all && cl_off != 0) { + nevents = kevent(loop->kq, loop->changelist, cl_off, NULL, 0, NULL); + assert(nevents == 0); + cl_off = 0; + } + + return cl_off; + +#undef SET +} + +picoev_loop* picoev_create_loop(int max_timeout) +{ + picoev_loop_kqueue* loop; + + /* init parent */ + assert(PICOEV_IS_INITED); + if ((loop = (picoev_loop_kqueue*)malloc(sizeof(picoev_loop_kqueue))) + == NULL) { + return NULL; + } + if (picoev_init_loop_internal(&loop->loop, max_timeout) != 0) { + free(loop); + return NULL; + } + + /* init kqueue */ + if ((loop->kq = kqueue()) == -1) { + picoev_deinit_loop_internal(&loop->loop); + free(loop); + return NULL; + } + loop->changed_fds = -1; + + loop->loop.now = time(NULL); + return &loop->loop; +} + +int picoev_destroy_loop(picoev_loop* _loop) +{ + picoev_loop_kqueue* loop = (picoev_loop_kqueue*)_loop; + + if (close(loop->kq) != 0) { + return -1; + } + picoev_deinit_loop_internal(&loop->loop); + free(loop); + return 0; +} + +int picoev_update_events_internal(picoev_loop* _loop, int fd, int events) +{ + picoev_loop_kqueue* loop = (picoev_loop_kqueue*)_loop; + picoev_fd* target = picoev.fds + fd; + + assert(PICOEV_FD_BELONGS_TO_LOOP(&loop->loop, fd)); + + /* initialize if adding the fd */ + if ((events & PICOEV_ADD) != 0) { + target->_backend = -1; + } + /* return if nothing to do */ + if (events == PICOEV_DEL + ? target->_backend == -1 + : (events & PICOEV_READWRITE) == target->events) { + return 0; + } + /* add to changed list if not yet being done */ + if (target->_backend == -1) { + target->_backend = BACKEND_BUILD(loop->changed_fds, target->events); + loop->changed_fds = fd; + } + /* update events */ + target->events = events & PICOEV_READWRITE; + /* apply immediately if is a DELETE */ + if ((events & PICOEV_DEL) != 0) { + apply_pending_changes(loop, 1); + } + + return 0; +} + +int picoev_poll_once_internal(picoev_loop* _loop, int max_wait) +{ + picoev_loop_kqueue* loop = (picoev_loop_kqueue*)_loop; + struct timespec ts; + int cl_off = 0, nevents, i; + + /* apply pending changes, with last changes stored to loop->changelist */ + cl_off = apply_pending_changes(loop, 0); + + ts.tv_sec = max_wait; + ts.tv_nsec = 0; + nevents = kevent(loop->kq, loop->changelist, cl_off, loop->events, + sizeof(loop->events) / sizeof(loop->events[0]), &ts); + if (nevents == -1) { + /* the errors we can only rescue */ + assert(errno == EACCES || errno == EFAULT || errno == EINTR); + return -1; + } + for (i = 0; i < nevents; ++i) { + struct kevent* event = loop->events + i; + picoev_fd* target = picoev.fds + event->ident; + assert((event->flags & EV_ERROR) == 0); /* changelist errors are fatal */ + if (loop->loop.loop_id == target->loop_id + && (event->filter & (EVFILT_READ | EVFILT_WRITE)) != 0) { + int revents; + switch (event->filter) { + case EVFILT_READ: + revents = PICOEV_READ; + break; + case EVFILT_WRITE: + revents = PICOEV_WRITE; + break; + default: + assert(0); + revents = 0; // suppress compiler warning + break; + } + (*target->callback)(&loop->loop, event->ident, revents, target->cb_arg); + } + } + + return 0; +} diff --git a/v_windows/v/old/thirdparty/picoev/src/picoev_select.c b/v_windows/v/old/thirdparty/picoev/src/picoev_select.c new file mode 100644 index 0000000..afbe140 --- /dev/null +++ b/v_windows/v/old/thirdparty/picoev/src/picoev_select.c @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2009, Cybozu Labs, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _WIN32 +# include +#else +# include +#endif + +#include "picoev.h" + +#ifdef _WIN32 +# define PICOEV_W32_INTERNAL +# include "picoev_w32.h" +# define PICOEV_FD_SET(x, y) FD_SET(picoev_w32_fd2sock(x), y) +# define PICOEV_FD_ISSET(x, y) FD_ISSET(picoev_w32_fd2sock(x), y) + +typedef struct picoev_w32_globals_st { + int* fds; + void* _fds_free_addr; +} picoev_w32_globals; + +picoev_w32_globals picoev_w32; + +int picoev_w32_sock2fd(int sock) { + int i; + for (i = 0; i < picoev.max_fd && picoev_w32.fds[i]; ++i) + if (picoev_w32.fds[i] == sock) return i; + assert(PICOEV_IS_INITED_AND_FD_IN_RANGE(i)); + picoev_w32.fds[i] = sock; + return i; +} + +int picoev_w32_fd2sock(int fd) { + assert(PICOEV_IS_INITED_AND_FD_IN_RANGE(fd)); + return picoev_w32.fds[fd]; +} + +extern int picoev_w32_deinit(void); + +int picoev_w32_init(int max_fd) { + int r = picoev_init(max_fd); + if ((picoev_w32.fds = (int*)picoev_memalign(sizeof(int) * max_fd, + &picoev_w32._fds_free_addr, 1)) + == NULL) { + picoev_deinit(); + return -1; + } +} + +int picoev_w32_deinit(void) { + free(picoev_w32._fds_free_addr); + picoev_w32.fds = NULL; + picoev_w32._fds_free_addr = NULL; + return picoev_deinit(); +} + +#else +# define PICOEV_FD_SET(x, y) FD_SET(x, y) +# define PICOEV_FD_ISSET(x, y) FD_ISSET(x, y) +#endif + +picoev_globals picoev; + +picoev_loop* picoev_create_loop(int max_timeout) +{ + picoev_loop* loop; + + assert(PICOEV_IS_INITED); + if ((loop = (picoev_loop*)malloc(sizeof(picoev_loop))) == NULL) { + return NULL; + } + if (picoev_init_loop_internal(loop, max_timeout) != 0) { + free(loop); + return NULL; + } + + loop->now = time(NULL); + return loop; +} + +int picoev_destroy_loop(picoev_loop* loop) +{ + picoev_deinit_loop_internal(loop); + free(loop); + return 0; +} + +int picoev_update_events_internal(picoev_loop* loop, int fd, int events) +{ + picoev.fds[fd].events = events & PICOEV_READWRITE; + return 0; +} + +int picoev_poll_once_internal(picoev_loop* loop, int max_wait) +{ + fd_set readfds, writefds, errorfds; + struct timeval tv; + int i, r, maxfd = 0; + + /* setup */ + FD_ZERO(&readfds); + FD_ZERO(&writefds); + FD_ZERO(&errorfds); + for (i = 0; i < picoev.max_fd; ++i) { + picoev_fd* fd = picoev.fds + i; + if (fd->loop_id == loop->loop_id) { + if ((fd->events & PICOEV_READ) != 0) { + PICOEV_FD_SET(i, &readfds); + if (maxfd < i) { + maxfd = i; + } + } + if ((fd->events & PICOEV_WRITE) != 0) { + PICOEV_FD_SET(i, &writefds); + if (maxfd < i) { + maxfd = i; + } + } + } + } + + /* select and handle if any */ + tv.tv_sec = max_wait; + tv.tv_usec = 0; + r = select(maxfd + 1, &readfds, &writefds, &errorfds, &tv); + if (r == -1) { + return -1; + } else if (r > 0) { + for (i = 0; i < picoev.max_fd; ++i) { + picoev_fd* target = picoev.fds + i; + if (target->loop_id == loop->loop_id) { + int revents = (PICOEV_FD_ISSET(i, &readfds) ? PICOEV_READ : 0) + | (PICOEV_FD_ISSET(i, &writefds) ? PICOEV_WRITE : 0); + if (revents != 0) { + (*target->callback)(loop, i, revents, target->cb_arg); + } + } + } + } + + return 0; +} diff --git a/v_windows/v/old/thirdparty/picoev/src/picoev_w32.h b/v_windows/v/old/thirdparty/picoev/src/picoev_w32.h new file mode 100644 index 0000000..b7e0cc4 --- /dev/null +++ b/v_windows/v/old/thirdparty/picoev/src/picoev_w32.h @@ -0,0 +1,15 @@ +#ifndef picoev_w32_h +#define picoev_w32_h + +#include "picoev.h" + +#ifndef PICOEV_W32_INTERNAL +extern int picoev_w32_init(int); +extern int picoev_w32_deinit(void); +extern int picoev_w32_sock2fd(int); +extern int picoev_w32_fd2sock(int); +# define picoev_init picoev_w32_init +# define picoev_deinit picoev_w32_deinit +#endif + +#endif diff --git a/v_windows/v/old/thirdparty/picohttpparser/picohttpparser.c b/v_windows/v/old/thirdparty/picohttpparser/picohttpparser.c new file mode 100644 index 0000000..7a6abd0 --- /dev/null +++ b/v_windows/v/old/thirdparty/picohttpparser/picohttpparser.c @@ -0,0 +1,28 @@ +#include "src/picohttpparser.c" + +#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) +#define __WINDOWS__ +#endif + +// date +#include + +const char* get_date() { + time_t t; + struct tm tm; + static const char *days[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + static const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + static char date[30] = "Thu, 01 Jan 1970 00:00:00 GMT"; + + time(&t); + #ifdef __WINDOWS__ + gmtime_s(&t, &tm); + #else + gmtime_r(&t, &tm); + #endif + strftime(date, 30, "---, %d --- %Y %H:%M:%S GMT", &tm); + memcpy(date, days[tm.tm_wday], 3); + memcpy(date + 8, months[tm.tm_mon], 3); + + return date; +} diff --git a/v_windows/v/old/thirdparty/picohttpparser/picohttpparser.h b/v_windows/v/old/thirdparty/picohttpparser/picohttpparser.h new file mode 100644 index 0000000..34faaf6 --- /dev/null +++ b/v_windows/v/old/thirdparty/picohttpparser/picohttpparser.h @@ -0,0 +1,112 @@ + +typedef struct phr_header phr_header_t; + +#include "src/picohttpparser.h" +#include + +#define ADVANCE_TOKEN2(buf, tok, toklen, max_len) \ + do {\ + for (int i = 0; i < max_len; i++) {\ + if (buf[i] == ' ') {\ + tok = buf;\ + toklen = i++;\ + while (buf[i] == ' ') i++;\ + buf += i;\ + break;\ + }\ + }\ + } while (0) + +static inline int phr_parse_request_path(const char *buf_start, size_t len, const char **method, size_t *method_len, const char **path, size_t *path_len) { + if (len < 14) return -2; + const char *buf = buf_start, *buf_end = buf_start + len; + + ADVANCE_TOKEN2(buf, *method, *method_len, 9); + len -= buf-buf_start; + ADVANCE_TOKEN2(buf, *path, *path_len, len); + if (*method_len == 0 || *path_len == 0) return -1; + + return buf-buf_start; +} + +static inline int phr_parse_request_path_pipeline(const char *buf_start, size_t len, const char **method, size_t *method_len, const char **path, size_t *path_len) { + if (len < 14) return -2; + const char *buf = buf_start, *buf_end = buf_start + len; + + ADVANCE_TOKEN2(buf, *method, *method_len, 9); + len -= buf-buf_start; + ADVANCE_TOKEN2(buf, *path, *path_len, len); + if (*method_len == 0 || *path_len == 0) return -1; + + while (buf < buf_end) { + ++buf; + if (*(uint32_t*)buf == 0x0a0d0a0d) { + buf += 4; + return (int)(buf - buf_start); + } + }; + + return -1; +} + +// date +const char* get_date(); + +// branchlut +const char gDigitsLut[200] = { + '0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9', + '1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9', + '2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9', + '3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9', + '4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9', + '5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9', + '6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9', + '7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9', + '8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9', + '9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9' +}; + +static inline int u64toa(char* buf, uint64_t value) { + const char* b = buf; + if (value < 100000000) { + uint32_t v = (uint32_t)(value); + if (v < 10000) { + const uint32_t d1 = (v / 100) << 1; + const uint32_t d2 = (v % 100) << 1; + + if (v >= 1000) + *buf++ = gDigitsLut[d1]; + if (v >= 100) + *buf++ = gDigitsLut[d1 + 1]; + if (v >= 10) + *buf++ = gDigitsLut[d2]; + *buf++ = gDigitsLut[d2 + 1]; + } + else { + // value = bbbbcccc + const uint32_t b = v / 10000; + const uint32_t c = v % 10000; + + const uint32_t d1 = (b / 100) << 1; + const uint32_t d2 = (b % 100) << 1; + + const uint32_t d3 = (c / 100) << 1; + const uint32_t d4 = (c % 100) << 1; + + if (value >= 10000000) + *buf++ = gDigitsLut[d1]; + if (value >= 1000000) + *buf++ = gDigitsLut[d1 + 1]; + if (value >= 100000) + *buf++ = gDigitsLut[d2]; + *buf++ = gDigitsLut[d2 + 1]; + + *buf++ = gDigitsLut[d3]; + *buf++ = gDigitsLut[d3 + 1]; + *buf++ = gDigitsLut[d4]; + *buf++ = gDigitsLut[d4 + 1]; + } + } + // *buf = '\0'; + return buf - b; +} diff --git a/v_windows/v/old/thirdparty/picohttpparser/src/README.md b/v_windows/v/old/thirdparty/picohttpparser/src/README.md new file mode 100644 index 0000000..cb32f58 --- /dev/null +++ b/v_windows/v/old/thirdparty/picohttpparser/src/README.md @@ -0,0 +1,116 @@ +PicoHTTPParser +============= + +Copyright (c) 2009-2014 [Kazuho Oku](https://github.com/kazuho), [Tokuhiro Matsuno](https://github.com/tokuhirom), [Daisuke Murase](https://github.com/typester), [Shigeo Mitsunari](https://github.com/herumi) + +PicoHTTPParser is a tiny, primitive, fast HTTP request/response parser. + +Unlike most parsers, it is stateless and does not allocate memory by itself. +All it does is accept pointer to buffer and the output structure, and setups the pointers in the latter to point at the necessary portions of the buffer. + +The code is widely deployed within Perl applications through popular modules that use it, including [Plack](https://metacpan.org/pod/Plack), [Starman](https://metacpan.org/pod/Starman), [Starlet](https://metacpan.org/pod/Starlet), [Furl](https://metacpan.org/pod/Furl). It is also the HTTP/1 parser of [H2O](https://github.com/h2o/h2o). + +Check out [test.c] to find out how to use the parser. + +The software is dual-licensed under the Perl License or the MIT License. + +Usage +----- + +The library exposes four functions: `phr_parse_request`, `phr_parse_response`, `phr_parse_headers`, `phr_decode_chunked`. + +### phr_parse_request + +The example below reads an HTTP request from socket `sock` using `read(2)`, parses it using `phr_parse_request`, and prints the details. + +```c +char buf[4096], *method, *path; +int pret, minor_version; +struct phr_header headers[100]; +size_t buflen = 0, prevbuflen = 0, method_len, path_len, num_headers; +ssize_t rret; + +while (1) { + /* read the request */ + while ((rret = read(sock, buf + buflen, sizeof(buf) - buflen)) == -1 && errno == EINTR) + ; + if (rret <= 0) + return IOError; + prevbuflen = buflen; + buflen += rret; + /* parse the request */ + num_headers = sizeof(headers) / sizeof(headers[0]); + pret = phr_parse_request(buf, buflen, &method, &method_len, &path, &path_len, + &minor_version, headers, &num_headers, prevbuflen); + if (pret > 0) + break; /* successfully parsed the request */ + else if (pret == -1) + return ParseError; + /* request is incomplete, continue the loop */ + assert(pret == -2); + if (buflen == sizeof(buf)) + return RequestIsTooLongError; +} + +printf("request is %d bytes long\n", pret); +printf("method is %.*s\n", (int)method_len, method); +printf("path is %.*s\n", (int)path_len, path); +printf("HTTP version is 1.%d\n", minor_version); +printf("headers:\n"); +for (i = 0; i != num_headers; ++i) { + printf("%.*s: %.*s\n", (int)headers[i].name_len, headers[i].name, + (int)headers[i].value_len, headers[i].value); +} +``` + +### phr_parse_response, phr_parse_headers + +`phr_parse_response` and `phr_parse_headers` provide similar interfaces as `phr_parse_request`. `phr_parse_response` parses an HTTP response, and `phr_parse_headers` parses the headers only. + +### phr_decode_chunked + +The example below decodes incoming data in chunked-encoding. The data is decoded in-place. + +```c +struct phr_chunked_decoder decoder = {}; /* zero-clear */ +char *buf = malloc(4096); +size_t size = 0, capacity = 4096, rsize; +ssize_t rret, pret; + +/* set consume_trailer to 1 to discard the trailing header, or the application + * should call phr_parse_headers to parse the trailing header */ +decoder.consume_trailer = 1; + +do { + /* expand the buffer if necessary */ + if (size == capacity) { + capacity *= 2; + buf = realloc(buf, capacity); + assert(buf != NULL); + } + /* read */ + while ((rret = read(sock, buf + size, capacity - size)) == -1 && errno == EINTR) + ; + if (rret <= 0) + return IOError; + /* decode */ + rsize = rret; + pret = phr_decode_chunked(&decoder, buf + size, &rsize); + if (pret == -1) + return ParseError; + size += rsize; +} while (pret == -2); + +/* successfully decoded the chunked data */ +assert(pret >= 0); +printf("decoded data is at %p (%zu bytes)\n", buf, size); +``` + +Benchmark +--------- + +![benchmark results](http://i.gyazo.com/a85c18d3162dfb46b485bb41e0ad443a.png) + +The benchmark code is from [fukamachi/fast-http@6b91103](https://github.com/fukamachi/fast-http/tree/6b9110347c7a3407310c08979aefd65078518478). + +The internals of picohttpparser has been described to some extent in [my blog entry]( http://blog.kazuhooku.com/2014/11/the-internals-h2o-or-how-to-write-fast.html). diff --git a/v_windows/v/old/thirdparty/picohttpparser/src/picohttpparser.c b/v_windows/v/old/thirdparty/picohttpparser/src/picohttpparser.c new file mode 100644 index 0000000..74ccc3e --- /dev/null +++ b/v_windows/v/old/thirdparty/picohttpparser/src/picohttpparser.c @@ -0,0 +1,645 @@ +/* + * Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase, + * Shigeo Mitsunari + * + * The software is licensed under either the MIT License (below) or the Perl + * license. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include +#include +#include +#ifdef __SSE4_2__ +#ifdef _MSC_VER +#include +#else +#include +#endif +#endif +#include "picohttpparser.h" + +#if __GNUC__ >= 3 +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) +#else +#define likely(x) (x) +#define unlikely(x) (x) +#endif + +#ifdef _MSC_VER +#define ALIGNED(n) _declspec(align(n)) +#else +#define ALIGNED(n) __attribute__((aligned(n))) +#endif + +#define IS_PRINTABLE_ASCII(c) ((unsigned char)(c)-040u < 0137u) + +#define CHECK_EOF() \ + if (buf == buf_end) { \ + *ret = -2; \ + return NULL; \ + } + +#define EXPECT_CHAR_NO_CHECK(ch) \ + if (*buf++ != ch) { \ + *ret = -1; \ + return NULL; \ + } + +#define EXPECT_CHAR(ch) \ + CHECK_EOF(); \ + EXPECT_CHAR_NO_CHECK(ch); + +#define ADVANCE_TOKEN(tok, toklen) \ + do { \ + const char *tok_start = buf; \ + static const char ALIGNED(16) ranges2[16] = "\000\040\177\177"; \ + int found2; \ + buf = findchar_fast(buf, buf_end, ranges2, 4, &found2); \ + if (!found2) { \ + CHECK_EOF(); \ + } \ + while (1) { \ + if (*buf == ' ') { \ + break; \ + } else if (unlikely(!IS_PRINTABLE_ASCII(*buf))) { \ + if ((unsigned char)*buf < '\040' || *buf == '\177') { \ + *ret = -1; \ + return NULL; \ + } \ + } \ + ++buf; \ + CHECK_EOF(); \ + } \ + tok = tok_start; \ + toklen = buf - tok_start; \ + } while (0) + +static const char *token_char_map = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\1\0\1\1\1\1\1\0\0\1\1\0\1\1\0\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0" + "\0\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\1\1" + "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\1\0\1\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; + +static const char *findchar_fast(const char *buf, const char *buf_end, const char *ranges, size_t ranges_size, int *found) +{ + *found = 0; +#if __SSE4_2__ + if (likely(buf_end - buf >= 16)) { + __m128i ranges16 = _mm_loadu_si128((const __m128i *)ranges); + + size_t left = (buf_end - buf) & ~15; + do { + __m128i b16 = _mm_loadu_si128((const __m128i *)buf); + int r = _mm_cmpestri(ranges16, ranges_size, b16, 16, _SIDD_LEAST_SIGNIFICANT | _SIDD_CMP_RANGES | _SIDD_UBYTE_OPS); + if (unlikely(r != 16)) { + buf += r; + *found = 1; + break; + } + buf += 16; + left -= 16; + } while (likely(left != 0)); + } +#else + /* suppress unused parameter warning */ + (void)buf_end; + (void)ranges; + (void)ranges_size; +#endif + return buf; +} + +static const char *get_token_to_eol(const char *buf, const char *buf_end, const char **token, size_t *token_len, int *ret) +{ + const char *token_start = buf; + +#ifdef __SSE4_2__ + static const char ALIGNED(16) ranges1[16] = "\0\010" /* allow HT */ + "\012\037" /* allow SP and up to but not including DEL */ + "\177\177"; /* allow chars w. MSB set */ + int found; + buf = findchar_fast(buf, buf_end, ranges1, 6, &found); + if (found) + goto FOUND_CTL; +#else + /* find non-printable char within the next 8 bytes, this is the hottest code; manually inlined */ + while (likely(buf_end - buf >= 8)) { +#define DOIT() \ + do { \ + if (unlikely(!IS_PRINTABLE_ASCII(*buf))) \ + goto NonPrintable; \ + ++buf; \ + } while (0) + DOIT(); + DOIT(); + DOIT(); + DOIT(); + DOIT(); + DOIT(); + DOIT(); + DOIT(); +#undef DOIT + continue; + NonPrintable: + if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || unlikely(*buf == '\177')) { + goto FOUND_CTL; + } + ++buf; + } +#endif + for (;; ++buf) { + CHECK_EOF(); + if (unlikely(!IS_PRINTABLE_ASCII(*buf))) { + if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || unlikely(*buf == '\177')) { + goto FOUND_CTL; + } + } + } +FOUND_CTL: + if (likely(*buf == '\015')) { + ++buf; + EXPECT_CHAR('\012'); + *token_len = buf - 2 - token_start; + } else if (*buf == '\012') { + *token_len = buf - token_start; + ++buf; + } else { + *ret = -1; + return NULL; + } + *token = token_start; + + return buf; +} + +static const char *is_complete(const char *buf, const char *buf_end, size_t last_len, int *ret) +{ + int ret_cnt = 0; + buf = last_len < 3 ? buf : buf + last_len - 3; + + while (1) { + CHECK_EOF(); + if (*buf == '\015') { + ++buf; + CHECK_EOF(); + EXPECT_CHAR('\012'); + ++ret_cnt; + } else if (*buf == '\012') { + ++buf; + ++ret_cnt; + } else { + ++buf; + ret_cnt = 0; + } + if (ret_cnt == 2) { + return buf; + } + } + + *ret = -2; + return NULL; +} + +#define PARSE_INT(valp_, mul_) \ + if (*buf < '0' || '9' < *buf) { \ + buf++; \ + *ret = -1; \ + return NULL; \ + } \ + *(valp_) = (mul_) * (*buf++ - '0'); + +#define PARSE_INT_3(valp_) \ + do { \ + int res_ = 0; \ + PARSE_INT(&res_, 100) \ + *valp_ = res_; \ + PARSE_INT(&res_, 10) \ + *valp_ += res_; \ + PARSE_INT(&res_, 1) \ + *valp_ += res_; \ + } while (0) + +/* returned pointer is always within [buf, buf_end), or null */ +static const char *parse_http_version(const char *buf, const char *buf_end, int *minor_version, int *ret) +{ + /* we want at least [HTTP/1.] to try to parse */ + if (buf_end - buf < 9) { + *ret = -2; + return NULL; + } + EXPECT_CHAR_NO_CHECK('H'); + EXPECT_CHAR_NO_CHECK('T'); + EXPECT_CHAR_NO_CHECK('T'); + EXPECT_CHAR_NO_CHECK('P'); + EXPECT_CHAR_NO_CHECK('/'); + EXPECT_CHAR_NO_CHECK('1'); + EXPECT_CHAR_NO_CHECK('.'); + PARSE_INT(minor_version, 1); + return buf; +} + +static const char *parse_headers(const char *buf, const char *buf_end, struct phr_header *headers, size_t *num_headers, + size_t max_headers, int *ret) +{ + for (;; ++*num_headers) { + CHECK_EOF(); + if (*buf == '\015') { + ++buf; + EXPECT_CHAR('\012'); + break; + } else if (*buf == '\012') { + ++buf; + break; + } + if (*num_headers == max_headers) { + *ret = -1; + return NULL; + } + if (!(*num_headers != 0 && (*buf == ' ' || *buf == '\t'))) { + /* parsing name, but do not discard SP before colon, see + * http://www.mozilla.org/security/announce/2006/mfsa2006-33.html */ + headers[*num_headers].name = buf; + static const char ALIGNED(16) ranges1[] = "\x00 " /* control chars and up to SP */ + "\"\"" /* 0x22 */ + "()" /* 0x28,0x29 */ + ",," /* 0x2c */ + "//" /* 0x2f */ + ":@" /* 0x3a-0x40 */ + "[]" /* 0x5b-0x5d */ + "{\377"; /* 0x7b-0xff */ + int found; + buf = findchar_fast(buf, buf_end, ranges1, sizeof(ranges1) - 1, &found); + if (!found) { + CHECK_EOF(); + } + while (1) { + if (*buf == ':') { + break; + } else if (!token_char_map[(unsigned char)*buf]) { + *ret = -1; + return NULL; + } + ++buf; + CHECK_EOF(); + } + if ((headers[*num_headers].name_len = buf - headers[*num_headers].name) == 0) { + *ret = -1; + return NULL; + } + ++buf; + for (;; ++buf) { + CHECK_EOF(); + if (!(*buf == ' ' || *buf == '\t')) { + break; + } + } + } else { + headers[*num_headers].name = NULL; + headers[*num_headers].name_len = 0; + } + const char *value; + size_t value_len; + if ((buf = get_token_to_eol(buf, buf_end, &value, &value_len, ret)) == NULL) { + return NULL; + } + /* remove trailing SPs and HTABs */ + const char *value_end = value + value_len; + for (; value_end != value; --value_end) { + const char c = *(value_end - 1); + if (!(c == ' ' || c == '\t')) { + break; + } + } + headers[*num_headers].value = value; + headers[*num_headers].value_len = value_end - value; + } + return buf; +} + +static const char *parse_request(const char *buf, const char *buf_end, const char **method, size_t *method_len, const char **path, + size_t *path_len, int *minor_version, struct phr_header *headers, size_t *num_headers, + size_t max_headers, int *ret) +{ + /* skip first empty line (some clients add CRLF after POST content) */ + CHECK_EOF(); + if (*buf == '\015') { + ++buf; + EXPECT_CHAR('\012'); + } else if (*buf == '\012') { + ++buf; + } + + /* parse request line */ + ADVANCE_TOKEN(*method, *method_len); + do { + ++buf; + } while (*buf == ' '); + ADVANCE_TOKEN(*path, *path_len); + do { + ++buf; + } while (*buf == ' '); + if (*method_len == 0 || *path_len == 0) { + *ret = -1; + return NULL; + } + if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) == NULL) { + return NULL; + } + if (*buf == '\015') { + ++buf; + EXPECT_CHAR('\012'); + } else if (*buf == '\012') { + ++buf; + } else { + *ret = -1; + return NULL; + } + + return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret); +} + +int phr_parse_request(const char *buf_start, size_t len, const char **method, size_t *method_len, const char **path, + size_t *path_len, int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len) +{ + const char *buf = buf_start, *buf_end = buf_start + len; + size_t max_headers = *num_headers; + int r; + + *method = NULL; + *method_len = 0; + *path = NULL; + *path_len = 0; + *minor_version = -1; + *num_headers = 0; + + /* if last_len != 0, check if the request is complete (a fast countermeasure + againt slowloris */ + if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) { + return r; + } + + if ((buf = parse_request(buf, buf_end, method, method_len, path, path_len, minor_version, headers, num_headers, max_headers, + &r)) == NULL) { + return r; + } + + return (int)(buf - buf_start); +} + +static const char *parse_response(const char *buf, const char *buf_end, int *minor_version, int *status, const char **msg, + size_t *msg_len, struct phr_header *headers, size_t *num_headers, size_t max_headers, int *ret) +{ + /* parse "HTTP/1.x" */ + if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) == NULL) { + return NULL; + } + /* skip space */ + if (*buf != ' ') { + *ret = -1; + return NULL; + } + do { + ++buf; + } while (*buf == ' '); + /* parse status code, we want at least [:digit:][:digit:][:digit:] to try to parse */ + if (buf_end - buf < 4) { + *ret = -2; + return NULL; + } + PARSE_INT_3(status); + + /* get message includig preceding space */ + if ((buf = get_token_to_eol(buf, buf_end, msg, msg_len, ret)) == NULL) { + return NULL; + } + if (*msg_len == 0) { + /* ok */ + } else if (**msg == ' ') { + /* remove preceding space */ + do { + ++*msg; + --*msg_len; + } while (**msg == ' '); + } else { + /* garbage found after status code */ + *ret = -1; + return NULL; + } + + return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret); +} + +int phr_parse_response(const char *buf_start, size_t len, int *minor_version, int *status, const char **msg, size_t *msg_len, + struct phr_header *headers, size_t *num_headers, size_t last_len) +{ + const char *buf = buf_start, *buf_end = buf + len; + size_t max_headers = *num_headers; + int r; + + *minor_version = -1; + *status = 0; + *msg = NULL; + *msg_len = 0; + *num_headers = 0; + + /* if last_len != 0, check if the response is complete (a fast countermeasure + against slowloris */ + if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) { + return r; + } + + if ((buf = parse_response(buf, buf_end, minor_version, status, msg, msg_len, headers, num_headers, max_headers, &r)) == NULL) { + return r; + } + + return (int)(buf - buf_start); +} + +int phr_parse_headers(const char *buf_start, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len) +{ + const char *buf = buf_start, *buf_end = buf + len; + size_t max_headers = *num_headers; + int r; + + *num_headers = 0; + + /* if last_len != 0, check if the response is complete (a fast countermeasure + against slowloris */ + if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) { + return r; + } + + if ((buf = parse_headers(buf, buf_end, headers, num_headers, max_headers, &r)) == NULL) { + return r; + } + + return (int)(buf - buf_start); +} + +enum { + CHUNKED_IN_CHUNK_SIZE, + CHUNKED_IN_CHUNK_EXT, + CHUNKED_IN_CHUNK_DATA, + CHUNKED_IN_CHUNK_CRLF, + CHUNKED_IN_TRAILERS_LINE_HEAD, + CHUNKED_IN_TRAILERS_LINE_MIDDLE +}; + +static int decode_hex(int ch) +{ + if ('0' <= ch && ch <= '9') { + return ch - '0'; + } else if ('A' <= ch && ch <= 'F') { + return ch - 'A' + 0xa; + } else if ('a' <= ch && ch <= 'f') { + return ch - 'a' + 0xa; + } else { + return -1; + } +} + +ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_t *_bufsz) +{ + size_t dst = 0, src = 0, bufsz = *_bufsz; + ssize_t ret = -2; /* incomplete */ + + while (1) { + switch (decoder->_state) { + case CHUNKED_IN_CHUNK_SIZE: + for (;; ++src) { + int v; + if (src == bufsz) + goto Exit; + if ((v = decode_hex(buf[src])) == -1) { + if (decoder->_hex_count == 0) { + ret = -1; + goto Exit; + } + break; + } + if (decoder->_hex_count == sizeof(size_t) * 2) { + ret = -1; + goto Exit; + } + decoder->bytes_left_in_chunk = decoder->bytes_left_in_chunk * 16 + v; + ++decoder->_hex_count; + } + decoder->_hex_count = 0; + decoder->_state = CHUNKED_IN_CHUNK_EXT; + /* fallthru */ + case CHUNKED_IN_CHUNK_EXT: + /* RFC 7230 A.2 "Line folding in chunk extensions is disallowed" */ + for (;; ++src) { + if (src == bufsz) + goto Exit; + if (buf[src] == '\012') + break; + } + ++src; + if (decoder->bytes_left_in_chunk == 0) { + if (decoder->consume_trailer) { + decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD; + break; + } else { + goto Complete; + } + } + decoder->_state = CHUNKED_IN_CHUNK_DATA; + /* fallthru */ + case CHUNKED_IN_CHUNK_DATA: { + size_t avail = bufsz - src; + if (avail < decoder->bytes_left_in_chunk) { + if (dst != src) + memmove(buf + dst, buf + src, avail); + src += avail; + dst += avail; + decoder->bytes_left_in_chunk -= avail; + goto Exit; + } + if (dst != src) + memmove(buf + dst, buf + src, decoder->bytes_left_in_chunk); + src += decoder->bytes_left_in_chunk; + dst += decoder->bytes_left_in_chunk; + decoder->bytes_left_in_chunk = 0; + decoder->_state = CHUNKED_IN_CHUNK_CRLF; + } + /* fallthru */ + case CHUNKED_IN_CHUNK_CRLF: + for (;; ++src) { + if (src == bufsz) + goto Exit; + if (buf[src] != '\015') + break; + } + if (buf[src] != '\012') { + ret = -1; + goto Exit; + } + ++src; + decoder->_state = CHUNKED_IN_CHUNK_SIZE; + break; + case CHUNKED_IN_TRAILERS_LINE_HEAD: + for (;; ++src) { + if (src == bufsz) + goto Exit; + if (buf[src] != '\015') + break; + } + if (buf[src++] == '\012') + goto Complete; + decoder->_state = CHUNKED_IN_TRAILERS_LINE_MIDDLE; + /* fallthru */ + case CHUNKED_IN_TRAILERS_LINE_MIDDLE: + for (;; ++src) { + if (src == bufsz) + goto Exit; + if (buf[src] == '\012') + break; + } + ++src; + decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD; + break; + default: + assert(!"decoder is corrupt"); + } + } + +Complete: + ret = bufsz - src; +Exit: + if (dst != src) + memmove(buf + dst, buf + src, bufsz - src); + *_bufsz = dst; + return ret; +} + +int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder) +{ + return decoder->_state == CHUNKED_IN_CHUNK_DATA; +} + +#undef CHECK_EOF +#undef EXPECT_CHAR +#undef ADVANCE_TOKEN diff --git a/v_windows/v/old/thirdparty/picohttpparser/src/picohttpparser.h b/v_windows/v/old/thirdparty/picohttpparser/src/picohttpparser.h new file mode 100644 index 0000000..8121b12 --- /dev/null +++ b/v_windows/v/old/thirdparty/picohttpparser/src/picohttpparser.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase, + * Shigeo Mitsunari + * + * The software is licensed under either the MIT License (below) or the Perl + * license. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef picohttpparser_h +#define picohttpparser_h + +#include + +#ifdef _MSC_VER +#define ssize_t intptr_t +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* contains name and value of a header (name == NULL if is a continuing line + * of a multiline header */ +typedef struct phr_header { + const char *name; + size_t name_len; + const char *value; + size_t value_len; +}phr_header; + +/* returns number of bytes consumed if successful, -2 if request is partial, + * -1 if failed */ +int phr_parse_request(const char *buf, size_t len, const char **method, size_t *method_len, const char **path, size_t *path_len, + int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len); + +/* ditto */ +int phr_parse_response(const char *_buf, size_t len, int *minor_version, int *status, const char **msg, size_t *msg_len, + struct phr_header *headers, size_t *num_headers, size_t last_len); + +/* ditto */ +int phr_parse_headers(const char *buf, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len); + +/* should be zero-filled before start */ +struct phr_chunked_decoder { + size_t bytes_left_in_chunk; /* number of bytes left in current chunk */ + char consume_trailer; /* if trailing headers should be consumed */ + char _hex_count; + char _state; +}; + +/* the function rewrites the buffer given as (buf, bufsz) removing the chunked- + * encoding headers. When the function returns without an error, bufsz is + * updated to the length of the decoded data available. Applications should + * repeatedly call the function while it returns -2 (incomplete) every time + * supplying newly arrived data. If the end of the chunked-encoded data is + * found, the function returns a non-negative number indicating the number of + * octets left undecoded at the tail of the supplied buffer. Returns -1 on + * error. + */ +ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_t *bufsz); + +/* returns if the chunked decoder is in middle of chunked data */ +int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/v_windows/v/old/thirdparty/sokol/sokol_app.h b/v_windows/v/old/thirdparty/sokol/sokol_app.h new file mode 100644 index 0000000..ef4ac70 --- /dev/null +++ b/v_windows/v/old/thirdparty/sokol/sokol_app.h @@ -0,0 +1,10475 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_APP_IMPL) +#define SOKOL_APP_IMPL +#endif +#ifndef SOKOL_APP_INCLUDED +/* + sokol_app.h -- cross-platform application wrapper + + Project URL: https://github.com/floooh/sokol + + Do this: + #define SOKOL_IMPL or + #define SOKOL_APP_IMPL + before you include this file in *one* C or C++ file to create the + implementation. + + In the same place define one of the following to select the 3D-API + which should be initialized by sokol_app.h (this must also match + the backend selected for sokol_gfx.h if both are used in the same + project): + + #define SOKOL_GLCORE33 + #define SOKOL_GLES2 + #define SOKOL_GLES3 + #define SOKOL_D3D11 + #define SOKOL_METAL + #define SOKOL_WGPU + + Optionally provide the following defines with your own implementations: + + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + SOKOL_LOG(msg) - your own logging function (default: puts(msg)) + SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) + SOKOL_ABORT() - called after an unrecoverable error (default: abort()) + SOKOL_WIN32_FORCE_MAIN - define this on Win32 to use a main() entry point instead of WinMain + SOKOL_NO_ENTRY - define this if sokol_app.h shouldn't "hijack" the main() function + SOKOL_APP_API_DECL - public function declaration prefix (default: extern) + SOKOL_API_DECL - same as SOKOL_APP_API_DECL + SOKOL_API_IMPL - public function implementation prefix (default: -) + SOKOL_CALLOC - your own calloc function (default: calloc(n, s)) + SOKOL_FREE - your own free function (default: free(p)) + + Optionally define the following to force debug checks and validations + even in release mode: + + SOKOL_DEBUG - by default this is defined if _DEBUG is defined + + If sokol_app.h is compiled as a DLL, define the following before + including the declaration or implementation: + + SOKOL_DLL + + On Windows, SOKOL_DLL will define SOKOL_APP_API_DECL as __declspec(dllexport) + or __declspec(dllimport) as needed. + + For example code, see https://github.com/floooh/sokol-samples/tree/master/sapp + + Portions of the Windows and Linux GL initialization and event code have been + taken from GLFW (http://www.glfw.org/) + + iOS onscreen keyboard support 'inspired' by libgdx. + + Link with the following system libraries: + + - on macOS with Metal: Cocoa, QuartzCore, Metal, MetalKit + - on macOS with GL: Cocoa, QuartzCore, OpenGL + - on iOS with Metal: Foundation, UIKit, Metal, MetalKit + - on iOS with GL: Foundation, UIKit, OpenGLES, GLKit + - on Linux: X11, Xi, Xcursor, GL, dl, pthread, m(?) + - on Android: GLESv3, EGL, log, android + - on Windows with the MSVC or Clang toolchains: no action needed, libs are defined in-source via pragma-comment-lib + - on Windows with MINGW/MSYS2 gcc: compile with '-mwin32' so that _WIN32 is defined + - link with the following libs: -lkernel32 -luser32 -lshell32 + - additionally with the GL backend: -lgdi32 + - additionally with the D3D11 backend: -ld3d11 -ldxgi -dxguid + + On Linux, you also need to use the -pthread compiler and linker option, otherwise weird + things will happen, see here for details: https://github.com/floooh/sokol/issues/376 + + Building for UWP requires a recent Visual Studio toolchain and Windows SDK + (at least VS2019 and Windows SDK 10.0.19041.0). When the UWP backend is + selected, the sokol_app.h implementation must be compiled as C++17. + + On macOS and iOS, the implementation must be compiled as Objective-C. + + FEATURE OVERVIEW + ================ + sokol_app.h provides a minimalistic cross-platform API which + implements the 'application-wrapper' parts of a 3D application: + + - a common application entry function + - creates a window and 3D-API context/device with a 'default framebuffer' + - makes the rendered frame visible + - provides keyboard-, mouse- and low-level touch-events + - platforms: MacOS, iOS, HTML5, Win32, Linux, Android (TODO: RaspberryPi) + - 3D-APIs: Metal, D3D11, GL3.2, GLES2, GLES3, WebGL, WebGL2 + + FEATURE/PLATFORM MATRIX + ======================= + | Windows | macOS | Linux | iOS | Android | UWP | Raspi | HTML5 + --------------------+---------+-------+-------+-------+---------+------+-------+------- + gl 3.x | YES | YES | YES | --- | --- | --- | --- | --- + gles2/webgl | --- | --- | --- | YES | YES | --- | TODO | YES + gles3/webgl2 | --- | --- | --- | YES | YES | --- | --- | YES + metal | --- | YES | --- | YES | --- | --- | --- | --- + d3d11 | YES | --- | --- | --- | --- | YES | --- | --- + KEY_DOWN | YES | YES | YES | SOME | TODO | YES | TODO | YES + KEY_UP | YES | YES | YES | SOME | TODO | YES | TODO | YES + CHAR | YES | YES | YES | YES | TODO | YES | TODO | YES + MOUSE_DOWN | YES | YES | YES | --- | --- | YES | TODO | YES + MOUSE_UP | YES | YES | YES | --- | --- | YES | TODO | YES + MOUSE_SCROLL | YES | YES | YES | --- | --- | YES | TODO | YES + MOUSE_MOVE | YES | YES | YES | --- | --- | YES | TODO | YES + MOUSE_ENTER | YES | YES | YES | --- | --- | YES | TODO | YES + MOUSE_LEAVE | YES | YES | YES | --- | --- | YES | TODO | YES + TOUCHES_BEGAN | --- | --- | --- | YES | YES | TODO | --- | YES + TOUCHES_MOVED | --- | --- | --- | YES | YES | TODO | --- | YES + TOUCHES_ENDED | --- | --- | --- | YES | YES | TODO | --- | YES + TOUCHES_CANCELLED | --- | --- | --- | YES | YES | TODO | --- | YES + RESIZED | YES | YES | YES | YES | YES | YES | --- | YES + ICONIFIED | YES | YES | YES | --- | --- | YES | --- | --- + RESTORED | YES | YES | YES | --- | --- | YES | --- | --- + SUSPENDED | --- | --- | --- | YES | YES | YES | --- | TODO + RESUMED | --- | --- | --- | YES | YES | YES | --- | TODO + QUIT_REQUESTED | YES | YES | YES | --- | --- | --- | TODO | YES + UPDATE_CURSOR | YES | YES | TODO | --- | --- | TODO | --- | TODO + IME | TODO | TODO? | TODO | ??? | TODO | --- | ??? | ??? + key repeat flag | YES | YES | YES | --- | --- | YES | TODO | YES + windowed | YES | YES | YES | --- | --- | YES | TODO | YES + fullscreen | YES | YES | YES | YES | YES | YES | TODO | --- + mouse hide | YES | YES | YES | --- | --- | YES | TODO | TODO + mouse lock | YES | YES | YES | --- | --- | TODO | TODO | YES + screen keyboard | --- | --- | --- | YES | TODO | TODO | --- | YES + swap interval | YES | YES | YES | YES | TODO | --- | TODO | YES + high-dpi | YES | YES | TODO | YES | YES | YES | TODO | YES + clipboard | YES | YES | TODO | --- | --- | TODO | --- | YES + MSAA | YES | YES | YES | YES | YES | TODO | TODO | YES + drag'n'drop | YES | YES | YES | --- | --- | TODO | TODO | YES + + TODO + ==== + - Linux: + - clipboard support + - UWP: + - clipboard, mouselock + - sapp_consume_event() on non-web platforms? + + STEP BY STEP + ============ + --- Add a sokol_main() function to your code which returns a sapp_desc structure + with initialization parameters and callback function pointers. This + function is called very early, usually at the start of the + platform's entry function (e.g. main or WinMain). You should do as + little as possible here, since the rest of your code might be called + from another thread (this depends on the platform): + + sapp_desc sokol_main(int argc, char* argv[]) { + return (sapp_desc) { + .width = 640, + .height = 480, + .init_cb = my_init_func, + .frame_cb = my_frame_func, + .cleanup_cb = my_cleanup_func, + .event_cb = my_event_func, + ... + }; + } + + There are many more setup parameters, but these are the most important. + For a complete list search for the sapp_desc structure declaration + below. + + DO NOT call any sokol-app function from inside sokol_main(), since + sokol-app will not be initialized at this point. + + The .width and .height parameters are the preferred size of the 3D + rendering canvas. The actual size may differ from this depending on + platform and other circumstances. Also the canvas size may change at + any time (for instance when the user resizes the application window, + or rotates the mobile device). + + All provided function callbacks will be called from the same thread, + but this may be different from the thread where sokol_main() was called. + + .init_cb (void (*)(void)) + This function is called once after the application window, + 3D rendering context and swap chain have been created. The + function takes no arguments and has no return value. + .frame_cb (void (*)(void)) + This is the per-frame callback, which is usually called 60 + times per second. This is where your application would update + most of its state and perform all rendering. + .cleanup_cb (void (*)(void)) + The cleanup callback is called once right before the application + quits. + .event_cb (void (*)(const sapp_event* event)) + The event callback is mainly for input handling, but is also + used to communicate other types of events to the application. Keep the + event_cb struct member zero-initialized if your application doesn't require + event handling. + .fail_cb (void (*)(const char* msg)) + The fail callback is called when a fatal error is encountered + during start which doesn't allow the program to continue. + Providing a callback here gives you a chance to show an error message + to the user. The default behaviour is SOKOL_LOG(msg) + + As you can see, those 'standard callbacks' don't have a user_data + argument, so any data that needs to be preserved between callbacks + must live in global variables. If keeping state in global variables + is not an option, there's an alternative set of callbacks with + an additional user_data pointer argument: + + .user_data (void*) + The user-data argument for the callbacks below + .init_userdata_cb (void (*)(void* user_data)) + .frame_userdata_cb (void (*)(void* user_data)) + .cleanup_userdata_cb (void (*)(void* user_data)) + .event_cb (void(*)(const sapp_event* event, void* user_data)) + .fail_cb (void(*)(const char* msg, void* user_data)) + These are the user-data versions of the callback functions. You + can mix those with the standard callbacks that don't have the + user_data argument. + + The function sapp_userdata() can be used to query the user_data + pointer provided in the sapp_desc struct. + + You can also call sapp_query_desc() to get a copy of the + original sapp_desc structure. + + NOTE that there's also an alternative compile mode where sokol_app.h + doesn't "hijack" the main() function. Search below for SOKOL_NO_ENTRY. + + --- Implement the initialization callback function (init_cb), this is called + once after the rendering surface, 3D API and swap chain have been + initialized by sokol_app. All sokol-app functions can be called + from inside the initialization callback, the most useful functions + at this point are: + + int sapp_width(void) + int sapp_height(void) + Returns the current width and height of the default framebuffer in pixels, + this may change from one frame to the next, and it may be different + from the initial size provided in the sapp_desc struct. + + float sapp_widthf(void) + float sapp_heightf(void) + These are alternatives to sapp_width() and sapp_height() which return + the default framebuffer size as float values instead of integer. This + may help to prevent casting back and forth between int and float + in more strongly typed languages than C and C++. + + int sapp_color_format(void) + int sapp_depth_format(void) + The color and depth-stencil pixelformats of the default framebuffer, + as integer values which are compatible with sokol-gfx's + sg_pixel_format enum (so that they can be plugged directly in places + where sg_pixel_format is expected). Possible values are: + + 23 == SG_PIXELFORMAT_RGBA8 + 27 == SG_PIXELFORMAT_BGRA8 + 41 == SG_PIXELFORMAT_DEPTH + 42 == SG_PIXELFORMAT_DEPTH_STENCIL + + int sapp_sample_count(void) + Return the MSAA sample count of the default framebuffer. + + bool sapp_gles2(void) + Returns true if a GLES2 or WebGL context has been created. This + is useful when a GLES3/WebGL2 context was requested but is not + available so that sokol_app.h had to fallback to GLES2/WebGL. + + const void* sapp_metal_get_device(void) + const void* sapp_metal_get_renderpass_descriptor(void) + const void* sapp_metal_get_drawable(void) + If the Metal backend has been selected, these functions return pointers + to various Metal API objects required for rendering, otherwise + they return a null pointer. These void pointers are actually + Objective-C ids converted with a (ARC) __bridge cast so that + the ids can be tunnel through C code. Also note that the returned + pointers to the renderpass-descriptor and drawable may change from one + frame to the next, only the Metal device object is guaranteed to + stay the same. + + const void* sapp_macos_get_window(void) + On macOS, get the NSWindow object pointer, otherwise a null pointer. + Before being used as Objective-C object, the void* must be converted + back with a (ARC) __bridge cast. + + const void* sapp_ios_get_window(void) + On iOS, get the UIWindow object pointer, otherwise a null pointer. + Before being used as Objective-C object, the void* must be converted + back with a (ARC) __bridge cast. + + const void* sapp_win32_get_hwnd(void) + On Windows, get the window's HWND, otherwise a null pointer. The + HWND has been cast to a void pointer in order to be tunneled + through code which doesn't include Windows.h. + + const void* sapp_d3d11_get_device(void) + const void* sapp_d3d11_get_device_context(void) + const void* sapp_d3d11_get_render_target_view(void) + const void* sapp_d3d11_get_depth_stencil_view(void) + Similar to the sapp_metal_* functions, the sapp_d3d11_* functions + return pointers to D3D11 API objects required for rendering, + only if the D3D11 backend has been selected. Otherwise they + return a null pointer. Note that the returned pointers to the + render-target-view and depth-stencil-view may change from one + frame to the next! + + const void* sapp_wgpu_get_device(void) + const void* sapp_wgpu_get_render_view(void) + const void* sapp_wgpu_get_resolve_view(void) + const void* sapp_wgpu_get_depth_stencil_view(void) + These are the WebGPU-specific functions to get the WebGPU + objects and values required for rendering. If sokol_app.h + is not compiled with SOKOL_WGPU, these functions return null. + + const void* sapp_android_get_native_activity(void); + On Android, get the native activity ANativeActivity pointer, otherwise + a null pointer. + + --- Implement the frame-callback function, this function will be called + on the same thread as the init callback, but might be on a different + thread than the sokol_main() function. Note that the size of + the rendering framebuffer might have changed since the frame callback + was called last. Call the functions sapp_width() and sapp_height() + each frame to get the current size. + + --- Optionally implement the event-callback to handle input events. + sokol-app provides the following type of input events: + - a 'virtual key' was pressed down or released + - a single text character was entered (provided as UTF-32 code point) + - a mouse button was pressed down or released (left, right, middle) + - mouse-wheel or 2D scrolling events + - the mouse was moved + - the mouse has entered or left the application window boundaries + - low-level, portable multi-touch events (began, moved, ended, cancelled) + - the application window was resized, iconified or restored + - the application was suspended or restored (on mobile platforms) + - the user or application code has asked to quit the application + - a string was pasted to the system clipboard + - one or more files have been dropped onto the application window + + To explicitly 'consume' an event and prevent that the event is + forwarded for further handling to the operating system, call + sapp_consume_event() from inside the event handler (NOTE that + this behaviour is currently only implemented for some HTML5 + events, support for other platforms and event types will + be added as needed, please open a github ticket and/or provide + a PR if needed). + + NOTE: Do *not* call any 3D API rendering functions in the event + callback function, since the 3D API context may not be active when the + event callback is called (it may work on some platforms and 3D APIs, + but not others, and the exact behaviour may change between + sokol-app versions). + + --- Implement the cleanup-callback function, this is called once + after the user quits the application (see the section + "APPLICATION QUIT" for detailed information on quitting + behaviour, and how to intercept a pending quit - for instance to show a + "Really Quit?" dialog box). Note that the cleanup-callback isn't + guaranteed to be called on the web and mobile platforms. + + MOUSE LOCK (AKA POINTER LOCK, AKA MOUSE CAPTURE) + ================================================ + In normal mouse mode, no mouse movement events are reported when the + mouse leaves the windows client area or hits the screen border (whether + it's one or the other depends on the platform), and the mouse move events + (SAPP_EVENTTYPE_MOUSE_MOVE) contain absolute mouse positions in + framebuffer pixels in the sapp_event items mouse_x and mouse_y, and + relative movement in framebuffer pixels in the sapp_event items mouse_dx + and mouse_dy. + + To get continuous mouse movement (also when the mouse leaves the window + client area or hits the screen border), activate mouse-lock mode + by calling: + + sapp_lock_mouse(true) + + When mouse lock is activated, the mouse pointer is hidden, the + reported absolute mouse position (sapp_event.mouse_x/y) appears + frozen, and the relative mouse movement in sapp_event.mouse_dx/dy + no longer has a direct relation to framebuffer pixels but instead + uses "raw mouse input" (what "raw mouse input" exactly means also + differs by platform). + + To deactivate mouse lock and return to normal mouse mode, call + + sapp_lock_mouse(false) + + And finally, to check if mouse lock is currently active, call + + if (sapp_mouse_locked()) { ... } + + On native platforms, the sapp_lock_mouse() and sapp_mouse_locked() + functions work as expected (mouse lock is activated or deactivated + immediately when sapp_lock_mouse() is called, and sapp_mouse_locked() + also immediately returns the new state after sapp_lock_mouse() + is called. + + On the web platform, sapp_lock_mouse() and sapp_mouse_locked() behave + differently, as dictated by the limitations of the HTML5 Pointer Lock API: + + - sapp_lock_mouse(true) can be called at any time, but it will + only take effect in a 'short-lived input event handler of a specific + type', meaning when one of the following events happens: + - SAPP_EVENTTYPE_MOUSE_DOWN + - SAPP_EVENTTYPE_MOUSE_UP + - SAPP_EVENTTYPE_MOUSE_SCROLL + - SAPP_EVENTYTPE_KEY_UP + - SAPP_EVENTTYPE_KEY_DOWN + - The mouse lock/unlock action on the web platform is asynchronous, + this means that sapp_mouse_locked() won't immediately return + the new status after calling sapp_lock_mouse(), instead the + reported status will only change when the pointer lock has actually + been activated or deactivated in the browser. + - On the web, mouse lock can be deactivated by the user at any time + by pressing the Esc key. When this happens, sokol_app.h behaves + the same as if sapp_lock_mouse(false) is called. + + For things like camera manipulation it's most straightforward to lock + and unlock the mouse right from the sokol_app.h event handler, for + instance the following code enters and leaves mouse lock when the + left mouse button is pressed and released, and then uses the relative + movement information to manipulate a camera (taken from the + cgltf-sapp.c sample in the sokol-samples repository + at https://github.com/floooh/sokol-samples): + + static void input(const sapp_event* ev) { + switch (ev->type) { + case SAPP_EVENTTYPE_MOUSE_DOWN: + if (ev->mouse_button == SAPP_MOUSEBUTTON_LEFT) { + sapp_lock_mouse(true); + } + break; + + case SAPP_EVENTTYPE_MOUSE_UP: + if (ev->mouse_button == SAPP_MOUSEBUTTON_LEFT) { + sapp_lock_mouse(false); + } + break; + + case SAPP_EVENTTYPE_MOUSE_MOVE: + if (sapp_mouse_locked()) { + cam_orbit(&state.camera, ev->mouse_dx * 0.25f, ev->mouse_dy * 0.25f); + } + break; + + default: + break; + } + } + + CLIPBOARD SUPPORT + ================= + Applications can send and receive UTF-8 encoded text data from and to the + system clipboard. By default, clipboard support is disabled and + must be enabled at startup via the following sapp_desc struct + members: + + sapp_desc.enable_clipboard - set to true to enable clipboard support + sapp_desc.clipboard_size - size of the internal clipboard buffer in bytes + + Enabling the clipboard will dynamically allocate a clipboard buffer + for UTF-8 encoded text data of the requested size in bytes, the default + size is 8 KBytes. Strings that don't fit into the clipboard buffer + (including the terminating zero) will be silently clipped, so it's + important that you provide a big enough clipboard size for your + use case. + + To send data to the clipboard, call sapp_set_clipboard_string() with + a pointer to an UTF-8 encoded, null-terminated C-string. + + NOTE that on the HTML5 platform, sapp_set_clipboard_string() must be + called from inside a 'short-lived event handler', and there are a few + other HTML5-specific caveats to workaround. You'll basically have to + tinker until it works in all browsers :/ (maybe the situation will + improve when all browsers agree on and implement the new + HTML5 navigator.clipboard API). + + To get data from the clipboard, check for the SAPP_EVENTTYPE_CLIPBOARD_PASTED + event in your event handler function, and then call sapp_get_clipboard_string() + to obtain the pasted UTF-8 encoded text. + + NOTE that behaviour of sapp_get_clipboard_string() is slightly different + depending on platform: + + - on the HTML5 platform, the internal clipboard buffer will only be updated + right before the SAPP_EVENTTYPE_CLIPBOARD_PASTED event is sent, + and sapp_get_clipboard_string() will simply return the current content + of the clipboard buffer + - on 'native' platforms, the call to sapp_get_clipboard_string() will + update the internal clipboard buffer with the most recent data + from the system clipboard + + Portable code should check for the SAPP_EVENTTYPE_CLIPBOARD_PASTED event, + and then call sapp_get_clipboard_string() right in the event handler. + + The SAPP_EVENTTYPE_CLIPBOARD_PASTED event will be generated by sokol-app + as follows: + + - on macOS: when the Cmd+V key is pressed down + - on HTML5: when the browser sends a 'paste' event to the global 'window' object + - on all other platforms: when the Ctrl+V key is pressed down + + DRAG AND DROP SUPPORT + ===================== + PLEASE NOTE: the drag'n'drop feature works differently on WASM/HTML5 + and on the native desktop platforms (Win32, Linux and macOS) because + of security-related restrictions in the HTML5 drag'n'drop API. The + WASM/HTML5 specifics are described at the end of this documentation + section: + + Like clipboard support, drag'n'drop support must be explicitly enabled + at startup in the sapp_desc struct. + + sapp_desc sokol_main() { + return (sapp_desc) { + .enable_dragndrop = true, // default is false + ... + }; + } + + You can also adjust the maximum number of files that are accepted + in a drop operation, and the maximum path length in bytes if needed: + + sapp_desc sokol_main() { + return (sapp_desc) { + .enable_dragndrop = true, // default is false + .max_dropped_files = 8, // default is 1 + .max_dropped_file_path_length = 8192, // in bytes, default is 2048 + ... + }; + } + + When drag'n'drop is enabled, the event callback will be invoked with an + event of type SAPP_EVENTTYPE_FILES_DROPPED whenever the user drops files on + the application window. + + After the SAPP_EVENTTYPE_FILES_DROPPED is received, you can query the + number of dropped files, and their absolute paths by calling separate + functions: + + void on_event(const sapp_event* ev) { + if (ev->type == SAPP_EVENTTYPE_FILES_DROPPED) { + + // the mouse position where the drop happened + float x = ev->mouse_x; + float y = ev->mouse_y; + + // get the number of files and their paths like this: + const int num_dropped_files = sapp_get_num_dropped_files(); + for (int i = 0; i < num_dropped_files; i++) { + const char* path = sapp_get_dropped_file_path(i); + ... + } + } + } + + The returned file paths are UTF-8 encoded strings. + + You can call sapp_get_num_dropped_files() and sapp_get_dropped_file_path() + anywhere, also outside the event handler callback, but be aware that the + file path strings will be overwritten with the next drop operation. + + In any case, sapp_get_dropped_file_path() will never return a null pointer, + instead an empty string "" will be returned if the drag'n'drop feature + hasn't been enabled, the last drop-operation failed, or the file path index + is out of range. + + Drag'n'drop caveats: + + - if more files are dropped in a single drop-action + than sapp_desc.max_dropped_files, the additional + files will be silently ignored + - if any of the file paths is longer than + sapp_desc.max_dropped_file_path_length (in number of bytes, after UTF-8 + encoding) the entire drop operation will be silently ignored (this + needs some sort of error feedback in the future) + - no mouse positions are reported while the drag is in + process, this may change in the future + + Drag'n'drop on HTML5/WASM: + + The HTML5 drag'n'drop API doesn't return file paths, but instead + black-box 'file objects' which must be used to load the content + of dropped files. This is the reason why sokol_app.h adds two + HTML5-specific functions to the drag'n'drop API: + + uint32_t sapp_html5_get_dropped_file_size(int index) + Returns the size in bytes of a dropped file. + + void sapp_html5_fetch_dropped_file(const sapp_html5_fetch_request* request) + Asynchronously loads the content of a dropped file into a + provided memory buffer (which must be big enough to hold + the file content) + + To start loading the first dropped file after an SAPP_EVENTTYPE_FILES_DROPPED + event is received: + + sapp_html5_fetch_dropped_file(&(sapp_html5_fetch_request){ + .dropped_file_index = 0, + .callback = fetch_cb + .buffer_ptr = buf, + .buffer_size = buf_size, + .user_data = ... + }); + + Make sure that the memory pointed to by 'buf' stays valid until the + callback function is called! + + As result of the asynchronous loading operation (no matter if succeeded or + failed) the 'fetch_cb' function will be called: + + void fetch_cb(const sapp_html5_fetch_response* response) { + // IMPORTANT: check if the loading operation actually succeeded: + if (response->succeeded) { + // the size of the loaded file: + const uint32_t num_bytes = response->fetched_size; + // and the pointer to the data (same as 'buf' in the fetch-call): + const void* ptr = response->buffer_ptr; + } + else { + // on error check the error code: + switch (response->error_code) { + case SAPP_HTML5_FETCH_ERROR_BUFFER_TOO_SMALL: + ... + break; + case SAPP_HTML5_FETCH_ERROR_OTHER: + ... + break; + } + } + } + + Check the droptest-sapp example for a real-world example which works + both on native platforms and the web: + + https://github.com/floooh/sokol-samples/blob/master/sapp/droptest-sapp.c + + HIGH-DPI RENDERING + ================== + You can set the sapp_desc.high_dpi flag during initialization to request + a full-resolution framebuffer on HighDPI displays. The default behaviour + is sapp_desc.high_dpi=false, this means that the application will + render to a lower-resolution framebuffer on HighDPI displays and the + rendered content will be upscaled by the window system composer. + + In a HighDPI scenario, you still request the same window size during + sokol_main(), but the framebuffer sizes returned by sapp_width() + and sapp_height() will be scaled up according to the DPI scaling + ratio. You can also get a DPI scaling factor with the function + sapp_dpi_scale(). + + Here's an example on a Mac with Retina display: + + sapp_desc sokol_main() { + return (sapp_desc) { + .width = 640, + .height = 480, + .high_dpi = true, + ... + }; + } + + The functions sapp_width(), sapp_height() and sapp_dpi_scale() will + return the following values: + + sapp_width -> 1280 + sapp_height -> 960 + sapp_dpi_scale -> 2.0 + + If the high_dpi flag is false, or you're not running on a Retina display, + the values would be: + + sapp_width -> 640 + sapp_height -> 480 + sapp_dpi_scale -> 1.0 + + APPLICATION QUIT + ================ + Without special quit handling, a sokol_app.h application will quit + 'gracefully' when the user clicks the window close-button unless a + platform's application model prevents this (e.g. on web or mobile). + 'Graceful exit' means that the application-provided cleanup callback will + be called before the application quits. + + On native desktop platforms sokol_app.h provides more control over the + application-quit-process. It's possible to initiate a 'programmatic quit' + from the application code, and a quit initiated by the application user can + be intercepted (for instance to show a custom dialog box). + + This 'programmatic quit protocol' is implemented through 3 functions + and 1 event: + + - sapp_quit(): This function simply quits the application without + giving the user a chance to intervene. Usually this might + be called when the user clicks the 'Ok' button in a 'Really Quit?' + dialog box + - sapp_request_quit(): Calling sapp_request_quit() will send the + event SAPP_EVENTTYPE_QUIT_REQUESTED to the applications event handler + callback, giving the user code a chance to intervene and cancel the + pending quit process (for instance to show a 'Really Quit?' dialog + box). If the event handler callback does nothing, the application + will be quit as usual. To prevent this, call the function + sapp_cancel_quit() from inside the event handler. + - sapp_cancel_quit(): Cancels a pending quit request, either initiated + by the user clicking the window close button, or programmatically + by calling sapp_request_quit(). The only place where calling this + function makes sense is from inside the event handler callback when + the SAPP_EVENTTYPE_QUIT_REQUESTED event has been received. + - SAPP_EVENTTYPE_QUIT_REQUESTED: this event is sent when the user + clicks the window's close button or application code calls the + sapp_request_quit() function. The event handler callback code can handle + this event by calling sapp_cancel_quit() to cancel the quit. + If the event is ignored, the application will quit as usual. + + On the web platform, the quit behaviour differs from native platforms, + because of web-specific restrictions: + + A `programmatic quit` initiated by calling sapp_quit() or + sapp_request_quit() will work as described above: the cleanup callback is + called, platform-specific cleanup is performed (on the web + this means that JS event handlers are unregisters), and then + the request-animation-loop will be exited. However that's all. The + web page itself will continue to exist (e.g. it's not possible to + programmatically close the browser tab). + + On the web it's also not possible to run custom code when the user + closes a brower tab, so it's not possible to prevent this with a + fancy custom dialog box. + + Instead the standard "Leave Site?" dialog box can be activated (or + deactivated) with the following function: + + sapp_html5_ask_leave_site(bool ask); + + The initial state of the associated internal flag can be provided + at startup via sapp_desc.html5_ask_leave_site. + + This feature should only be used sparingly in critical situations - for + instance when the user would loose data - since popping up modal dialog + boxes is considered quite rude in the web world. Note that there's no way + to customize the content of this dialog box or run any code as a result + of the user's decision. Also note that the user must have interacted with + the site before the dialog box will appear. These are all security measures + to prevent fishing. + + The Dear ImGui HighDPI sample contains example code of how to + implement a 'Really Quit?' dialog box with Dear ImGui (native desktop + platforms only), and for showing the hardwired "Leave Site?" dialog box + when running on the web platform: + + https://floooh.github.io/sokol-html5/wasm/imgui-highdpi-sapp.html + + FULLSCREEN + ========== + If the sapp_desc.fullscreen flag is true, sokol-app will try to create + a fullscreen window on platforms with a 'proper' window system + (mobile devices will always use fullscreen). The implementation details + depend on the target platform, in general sokol-app will use a + 'soft approach' which doesn't interfere too much with the platform's + window system (for instance borderless fullscreen window instead of + a 'real' fullscreen mode). Such details might change over time + as sokol-app is adapted for different needs. + + The most important effect of fullscreen mode to keep in mind is that + the requested canvas width and height will be ignored for the initial + window size, calling sapp_width() and sapp_height() will instead return + the resolution of the fullscreen canvas (however the provided size + might still be used for the non-fullscreen window, in case the user can + switch back from fullscreen- to windowed-mode). + + To toggle fullscreen mode programmatically, call sapp_toggle_fullscreen(). + + To check if the application window is currently in fullscreen mode, + call sapp_is_fullscreen(). + + ONSCREEN KEYBOARD + ================= + On some platforms which don't provide a physical keyboard, sokol-app + can display the platform's integrated onscreen keyboard for text + input. To request that the onscreen keyboard is shown, call + + sapp_show_keyboard(true); + + Likewise, to hide the keyboard call: + + sapp_show_keyboard(false); + + Note that on the web platform, the keyboard can only be shown from + inside an input handler. On such platforms, sapp_show_keyboard() + will only work as expected when it is called from inside the + sokol-app event callback function. When called from other places, + an internal flag will be set, and the onscreen keyboard will be + called at the next 'legal' opportunity (when the next input event + is handled). + + OPTIONAL: DON'T HIJACK main() (#define SOKOL_NO_ENTRY) + ====================================================== + In its default configuration, sokol_app.h "hijacks" the platform's + standard main() function. This was done because different platforms + have different main functions which are not compatible with + C's main() (for instance WinMain on Windows has completely different + arguments). However, this "main hijacking" posed a problem for + usage scenarios like integrating sokol_app.h with other languages than + C or C++, so an alternative SOKOL_NO_ENTRY mode has been added + in which the user code provides the platform's main function: + + - define SOKOL_NO_ENTRY before including the sokol_app.h implementation + - do *not* provide a sokol_main() function + - instead provide the standard main() function of the platform + - from the main function, call the function ```sapp_run()``` which + takes a pointer to an ```sapp_desc``` structure. + - ```sapp_run()``` takes over control and calls the provided init-, frame-, + shutdown- and event-callbacks just like in the default model, it + will only return when the application quits (or not at all on some + platforms, like emscripten) + + NOTE: SOKOL_NO_ENTRY is currently not supported on Android. + + WINDOWS CONSOLE OUTPUT + ====================== + On Windows, regular windowed applications don't show any stdout/stderr text + output, which can be a bit of a hassle for printf() debugging or generally + logging text to the console. Also, console output by default uses a local + codepage setting and thus international UTF-8 encoded text is printed + as garbage. + + To help with these issues, sokol_app.h can be configured at startup + via the following Windows-specific sapp_desc flags: + + sapp_desc.win32_console_utf8 (default: false) + When set to true, the output console codepage will be switched + to UTF-8 (and restored to the original codepage on exit) + + sapp_desc.win32_console_attach (default: false) + When set to true, stdout and stderr will be attached to the + console of the parent process (if the parent process actually + has a console). This means that if the application was started + in a command line window, stdout and stderr output will be printed + to the terminal, just like a regular command line program. But if + the application is started via double-click, it will behave like + a regular UI application, and stdout/stderr will not be visible. + + sapp_desc.win32_console_create (default: false) + When set to true, a new console window will be created and + stdout/stderr will be redirected to that console window. It + doesn't matter if the application is started from the command + line or via double-click. + + TEMP NOTE DUMP + ============== + - onscreen keyboard support on Android requires Java :(, should we even bother? + - sapp_desc needs a bool whether to initialize depth-stencil surface + - GL context initialization needs more control (at least what GL version to initialize) + - application icon + - the UPDATE_CURSOR event currently behaves differently between Win32 and OSX + (Win32 sends the event each frame when the mouse moves and is inside the window + client area, OSX sends it only once when the mouse enters the client area) + - the Android implementation calls cleanup_cb() and destroys the egl context in onDestroy + at the latest but should do it earlier, in onStop, as an app is "killable" after onStop + on Android Honeycomb and later (it can't be done at the moment as the app may be started + again after onStop and the sokol lifecycle does not yet handle context teardown/bringup) + + + LICENSE + ======= + zlib/libpng license + + Copyright (c) 2018 Andre Weissflog + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software in a + product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ +#define SOKOL_APP_INCLUDED (1) +#include +#include + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_APP_API_DECL) +#define SOKOL_APP_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_APP_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_APP_IMPL) +#define SOKOL_APP_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_APP_API_DECL __declspec(dllimport) +#else +#define SOKOL_APP_API_DECL extern +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + SAPP_MAX_TOUCHPOINTS = 8, + SAPP_MAX_MOUSEBUTTONS = 3, + SAPP_MAX_KEYCODES = 512, +}; + +typedef enum sapp_event_type { + SAPP_EVENTTYPE_INVALID, + SAPP_EVENTTYPE_KEY_DOWN, + SAPP_EVENTTYPE_KEY_UP, + SAPP_EVENTTYPE_CHAR, + SAPP_EVENTTYPE_MOUSE_DOWN, + SAPP_EVENTTYPE_MOUSE_UP, + SAPP_EVENTTYPE_MOUSE_SCROLL, + SAPP_EVENTTYPE_MOUSE_MOVE, + SAPP_EVENTTYPE_MOUSE_ENTER, + SAPP_EVENTTYPE_MOUSE_LEAVE, + SAPP_EVENTTYPE_TOUCHES_BEGAN, + SAPP_EVENTTYPE_TOUCHES_MOVED, + SAPP_EVENTTYPE_TOUCHES_ENDED, + SAPP_EVENTTYPE_TOUCHES_CANCELLED, + SAPP_EVENTTYPE_RESIZED, + SAPP_EVENTTYPE_ICONIFIED, + SAPP_EVENTTYPE_RESTORED, + SAPP_EVENTTYPE_SUSPENDED, + SAPP_EVENTTYPE_RESUMED, + SAPP_EVENTTYPE_UPDATE_CURSOR, + SAPP_EVENTTYPE_QUIT_REQUESTED, + SAPP_EVENTTYPE_CLIPBOARD_PASTED, + SAPP_EVENTTYPE_FILES_DROPPED, + _SAPP_EVENTTYPE_NUM, + _SAPP_EVENTTYPE_FORCE_U32 = 0x7FFFFFFF +} sapp_event_type; + +/* key codes are the same names and values as GLFW */ +typedef enum sapp_keycode { + SAPP_KEYCODE_INVALID = 0, + SAPP_KEYCODE_SPACE = 32, + SAPP_KEYCODE_APOSTROPHE = 39, /* ' */ + SAPP_KEYCODE_COMMA = 44, /* , */ + SAPP_KEYCODE_MINUS = 45, /* - */ + SAPP_KEYCODE_PERIOD = 46, /* . */ + SAPP_KEYCODE_SLASH = 47, /* / */ + SAPP_KEYCODE_0 = 48, + SAPP_KEYCODE_1 = 49, + SAPP_KEYCODE_2 = 50, + SAPP_KEYCODE_3 = 51, + SAPP_KEYCODE_4 = 52, + SAPP_KEYCODE_5 = 53, + SAPP_KEYCODE_6 = 54, + SAPP_KEYCODE_7 = 55, + SAPP_KEYCODE_8 = 56, + SAPP_KEYCODE_9 = 57, + SAPP_KEYCODE_SEMICOLON = 59, /* ; */ + SAPP_KEYCODE_EQUAL = 61, /* = */ + SAPP_KEYCODE_A = 65, + SAPP_KEYCODE_B = 66, + SAPP_KEYCODE_C = 67, + SAPP_KEYCODE_D = 68, + SAPP_KEYCODE_E = 69, + SAPP_KEYCODE_F = 70, + SAPP_KEYCODE_G = 71, + SAPP_KEYCODE_H = 72, + SAPP_KEYCODE_I = 73, + SAPP_KEYCODE_J = 74, + SAPP_KEYCODE_K = 75, + SAPP_KEYCODE_L = 76, + SAPP_KEYCODE_M = 77, + SAPP_KEYCODE_N = 78, + SAPP_KEYCODE_O = 79, + SAPP_KEYCODE_P = 80, + SAPP_KEYCODE_Q = 81, + SAPP_KEYCODE_R = 82, + SAPP_KEYCODE_S = 83, + SAPP_KEYCODE_T = 84, + SAPP_KEYCODE_U = 85, + SAPP_KEYCODE_V = 86, + SAPP_KEYCODE_W = 87, + SAPP_KEYCODE_X = 88, + SAPP_KEYCODE_Y = 89, + SAPP_KEYCODE_Z = 90, + SAPP_KEYCODE_LEFT_BRACKET = 91, /* [ */ + SAPP_KEYCODE_BACKSLASH = 92, /* \ */ + SAPP_KEYCODE_RIGHT_BRACKET = 93, /* ] */ + SAPP_KEYCODE_GRAVE_ACCENT = 96, /* ` */ + SAPP_KEYCODE_WORLD_1 = 161, /* non-US #1 */ + SAPP_KEYCODE_WORLD_2 = 162, /* non-US #2 */ + SAPP_KEYCODE_ESCAPE = 256, + SAPP_KEYCODE_ENTER = 257, + SAPP_KEYCODE_TAB = 258, + SAPP_KEYCODE_BACKSPACE = 259, + SAPP_KEYCODE_INSERT = 260, + SAPP_KEYCODE_DELETE = 261, + SAPP_KEYCODE_RIGHT = 262, + SAPP_KEYCODE_LEFT = 263, + SAPP_KEYCODE_DOWN = 264, + SAPP_KEYCODE_UP = 265, + SAPP_KEYCODE_PAGE_UP = 266, + SAPP_KEYCODE_PAGE_DOWN = 267, + SAPP_KEYCODE_HOME = 268, + SAPP_KEYCODE_END = 269, + SAPP_KEYCODE_CAPS_LOCK = 280, + SAPP_KEYCODE_SCROLL_LOCK = 281, + SAPP_KEYCODE_NUM_LOCK = 282, + SAPP_KEYCODE_PRINT_SCREEN = 283, + SAPP_KEYCODE_PAUSE = 284, + SAPP_KEYCODE_F1 = 290, + SAPP_KEYCODE_F2 = 291, + SAPP_KEYCODE_F3 = 292, + SAPP_KEYCODE_F4 = 293, + SAPP_KEYCODE_F5 = 294, + SAPP_KEYCODE_F6 = 295, + SAPP_KEYCODE_F7 = 296, + SAPP_KEYCODE_F8 = 297, + SAPP_KEYCODE_F9 = 298, + SAPP_KEYCODE_F10 = 299, + SAPP_KEYCODE_F11 = 300, + SAPP_KEYCODE_F12 = 301, + SAPP_KEYCODE_F13 = 302, + SAPP_KEYCODE_F14 = 303, + SAPP_KEYCODE_F15 = 304, + SAPP_KEYCODE_F16 = 305, + SAPP_KEYCODE_F17 = 306, + SAPP_KEYCODE_F18 = 307, + SAPP_KEYCODE_F19 = 308, + SAPP_KEYCODE_F20 = 309, + SAPP_KEYCODE_F21 = 310, + SAPP_KEYCODE_F22 = 311, + SAPP_KEYCODE_F23 = 312, + SAPP_KEYCODE_F24 = 313, + SAPP_KEYCODE_F25 = 314, + SAPP_KEYCODE_KP_0 = 320, + SAPP_KEYCODE_KP_1 = 321, + SAPP_KEYCODE_KP_2 = 322, + SAPP_KEYCODE_KP_3 = 323, + SAPP_KEYCODE_KP_4 = 324, + SAPP_KEYCODE_KP_5 = 325, + SAPP_KEYCODE_KP_6 = 326, + SAPP_KEYCODE_KP_7 = 327, + SAPP_KEYCODE_KP_8 = 328, + SAPP_KEYCODE_KP_9 = 329, + SAPP_KEYCODE_KP_DECIMAL = 330, + SAPP_KEYCODE_KP_DIVIDE = 331, + SAPP_KEYCODE_KP_MULTIPLY = 332, + SAPP_KEYCODE_KP_SUBTRACT = 333, + SAPP_KEYCODE_KP_ADD = 334, + SAPP_KEYCODE_KP_ENTER = 335, + SAPP_KEYCODE_KP_EQUAL = 336, + SAPP_KEYCODE_LEFT_SHIFT = 340, + SAPP_KEYCODE_LEFT_CONTROL = 341, + SAPP_KEYCODE_LEFT_ALT = 342, + SAPP_KEYCODE_LEFT_SUPER = 343, + SAPP_KEYCODE_RIGHT_SHIFT = 344, + SAPP_KEYCODE_RIGHT_CONTROL = 345, + SAPP_KEYCODE_RIGHT_ALT = 346, + SAPP_KEYCODE_RIGHT_SUPER = 347, + SAPP_KEYCODE_MENU = 348, +} sapp_keycode; + +typedef struct sapp_touchpoint { + uintptr_t identifier; + float pos_x; + float pos_y; + bool changed; +} sapp_touchpoint; + +typedef enum sapp_mousebutton { + SAPP_MOUSEBUTTON_LEFT = 0x0, + SAPP_MOUSEBUTTON_RIGHT = 0x1, + SAPP_MOUSEBUTTON_MIDDLE = 0x2, + SAPP_MOUSEBUTTON_INVALID = 0x100, +} sapp_mousebutton; + +enum { + SAPP_MODIFIER_SHIFT = 0x1, + SAPP_MODIFIER_CTRL = 0x2, + SAPP_MODIFIER_ALT = 0x4, + SAPP_MODIFIER_SUPER = 0x8 +}; + +typedef struct sapp_event { + uint64_t frame_count; + sapp_event_type type; + sapp_keycode key_code; + uint32_t char_code; + bool key_repeat; + uint32_t modifiers; + sapp_mousebutton mouse_button; + float mouse_x; + float mouse_y; + float mouse_dx; + float mouse_dy; + float scroll_x; + float scroll_y; + int num_touches; + sapp_touchpoint touches[SAPP_MAX_TOUCHPOINTS]; + int window_width; + int window_height; + int framebuffer_width; + int framebuffer_height; +} sapp_event; + +typedef struct sapp_desc { + void (*init_cb)(void); /* these are the user-provided callbacks without user data */ + void (*frame_cb)(void); + void (*cleanup_cb)(void); + void (*event_cb)(const sapp_event*); + void (*fail_cb)(const char*); + + void* user_data; /* these are the user-provided callbacks with user data */ + void (*init_userdata_cb)(void*); + void (*frame_userdata_cb)(void*); + void (*cleanup_userdata_cb)(void*); + void (*event_userdata_cb)(const sapp_event*, void*); + void (*fail_userdata_cb)(const char*, void*); + + int width; /* the preferred width of the window / canvas */ + int height; /* the preferred height of the window / canvas */ + int sample_count; /* MSAA sample count */ + int swap_interval; /* the preferred swap interval (ignored on some platforms) */ + bool high_dpi; /* whether the rendering canvas is full-resolution on HighDPI displays */ + bool fullscreen; /* whether the window should be created in fullscreen mode */ + bool alpha; /* whether the framebuffer should have an alpha channel (ignored on some platforms) */ + const char* window_title; /* the window title as UTF-8 encoded string */ + bool user_cursor; /* if true, user is expected to manage cursor image in SAPP_EVENTTYPE_UPDATE_CURSOR */ + bool enable_clipboard; /* enable clipboard access, default is false */ + int clipboard_size; /* max size of clipboard content in bytes */ + bool enable_dragndrop; /* enable file dropping (drag'n'drop), default is false */ + int max_dropped_files; /* max number of dropped files to process (default: 1) */ + int max_dropped_file_path_length; /* max length in bytes of a dropped UTF-8 file path (default: 2048) */ + + /* backend-specific options */ + bool gl_force_gles2; /* if true, setup GLES2/WebGL even if GLES3/WebGL2 is available */ + bool win32_console_utf8; /* if true, set the output console codepage to UTF-8 */ + bool win32_console_create; /* if true, attach stdout/stderr to a new console window */ + bool win32_console_attach; /* if true, attach stdout/stderr to parent process */ + const char* html5_canvas_name; /* the name (id) of the HTML5 canvas element, default is "canvas" */ + bool html5_canvas_resize; /* if true, the HTML5 canvas size is set to sapp_desc.width/height, otherwise canvas size is tracked */ + bool html5_preserve_drawing_buffer; /* HTML5 only: whether to preserve default framebuffer content between frames */ + bool html5_premultiplied_alpha; /* HTML5 only: whether the rendered pixels use premultiplied alpha convention */ + bool html5_ask_leave_site; /* initial state of the internal html5_ask_leave_site flag (see sapp_html5_ask_leave_site()) */ + bool ios_keyboard_resizes_canvas; /* if true, showing the iOS keyboard shrinks the canvas */ + + /* V patches */ + bool __v_native_render; /* V patch to allow for native rendering */ +} sapp_desc; + +/* HTML5 specific: request and response structs for + asynchronously loading dropped-file content. +*/ +typedef enum sapp_html5_fetch_error { + SAPP_HTML5_FETCH_ERROR_NO_ERROR, + SAPP_HTML5_FETCH_ERROR_BUFFER_TOO_SMALL, + SAPP_HTML5_FETCH_ERROR_OTHER, +} sapp_html5_fetch_error; + +typedef struct sapp_html5_fetch_response { + bool succeeded; /* true if the loading operation has succeeded */ + sapp_html5_fetch_error error_code; + int file_index; /* index of the dropped file (0..sapp_get_num_dropped_filed()-1) */ + uint32_t fetched_size; /* size in bytes of loaded data */ + void* buffer_ptr; /* pointer to user-provided buffer which contains the loaded data */ + uint32_t buffer_size; /* size of user-provided buffer (buffer_size >= fetched_size) */ + void* user_data; /* user-provided user data pointer */ +} sapp_html5_fetch_response; + +typedef struct sapp_html5_fetch_request { + int dropped_file_index; /* 0..sapp_get_num_dropped_files()-1 */ + void (*callback)(const sapp_html5_fetch_response*); /* response callback function pointer (required) */ + void* buffer_ptr; /* pointer to buffer to load data into */ + uint32_t buffer_size; /* size in bytes of buffer */ + void* user_data; /* optional userdata pointer */ +} sapp_html5_fetch_request; + +/* user-provided functions */ +extern sapp_desc sokol_main(int argc, char* argv[]); + +/* returns true after sokol-app has been initialized */ +SOKOL_APP_API_DECL bool sapp_isvalid(void); +/* returns the current framebuffer width in pixels */ +SOKOL_APP_API_DECL int sapp_width(void); +/* same as sapp_width(), but returns float */ +SOKOL_APP_API_DECL float sapp_widthf(void); +/* returns the current framebuffer height in pixels */ +SOKOL_APP_API_DECL int sapp_height(void); +/* same as sapp_height(), but returns float */ +SOKOL_APP_API_DECL float sapp_heightf(void); +/* get default framebuffer color pixel format */ +SOKOL_APP_API_DECL int sapp_color_format(void); +/* get default framebuffer depth pixel format */ +SOKOL_APP_API_DECL int sapp_depth_format(void); +/* get default framebuffer sample count */ +SOKOL_APP_API_DECL int sapp_sample_count(void); +/* returns true when high_dpi was requested and actually running in a high-dpi scenario */ +SOKOL_APP_API_DECL bool sapp_high_dpi(void); +/* returns the dpi scaling factor (window pixels to framebuffer pixels) */ +SOKOL_APP_API_DECL float sapp_dpi_scale(void); +/* show or hide the mobile device onscreen keyboard */ +SOKOL_APP_API_DECL void sapp_show_keyboard(bool show); +/* return true if the mobile device onscreen keyboard is currently shown */ +SOKOL_APP_API_DECL bool sapp_keyboard_shown(void); +/* query fullscreen mode */ +SOKOL_APP_API_DECL bool sapp_is_fullscreen(void); +/* toggle fullscreen mode */ +SOKOL_APP_API_DECL void sapp_toggle_fullscreen(void); +/* show or hide the mouse cursor */ +SOKOL_APP_API_DECL void sapp_show_mouse(bool show); +/* show or hide the mouse cursor */ +SOKOL_APP_API_DECL bool sapp_mouse_shown(); +/* enable/disable mouse-pointer-lock mode */ +SOKOL_APP_API_DECL void sapp_lock_mouse(bool lock); +/* return true if in mouse-pointer-lock mode (this may toggle a few frames later) */ +SOKOL_APP_API_DECL bool sapp_mouse_locked(void); +/* return the userdata pointer optionally provided in sapp_desc */ +SOKOL_APP_API_DECL void* sapp_userdata(void); +/* return a copy of the sapp_desc structure */ +SOKOL_APP_API_DECL sapp_desc sapp_query_desc(void); +/* initiate a "soft quit" (sends SAPP_EVENTTYPE_QUIT_REQUESTED) */ +SOKOL_APP_API_DECL void sapp_request_quit(void); +/* cancel a pending quit (when SAPP_EVENTTYPE_QUIT_REQUESTED has been received) */ +SOKOL_APP_API_DECL void sapp_cancel_quit(void); +/* initiate a "hard quit" (quit application without sending SAPP_EVENTTYPE_QUIT_REQUSTED) */ +SOKOL_APP_API_DECL void sapp_quit(void); +/* call from inside event callback to consume the current event (don't forward to platform) */ +SOKOL_APP_API_DECL void sapp_consume_event(void); +/* get the current frame counter (for comparison with sapp_event.frame_count) */ +SOKOL_APP_API_DECL uint64_t sapp_frame_count(void); +/* write string into clipboard */ +SOKOL_APP_API_DECL void sapp_set_clipboard_string(const char* str); +/* read string from clipboard (usually during SAPP_EVENTTYPE_CLIPBOARD_PASTED) */ +SOKOL_APP_API_DECL const char* sapp_get_clipboard_string(void); +/* set the window title (only on desktop platforms) */ +SOKOL_APP_API_DECL void sapp_set_window_title(const char* str); +/* gets the total number of dropped files (after an SAPP_EVENTTYPE_FILES_DROPPED event) */ +SOKOL_APP_API_DECL int sapp_get_num_dropped_files(void); +/* gets the dropped file paths */ +SOKOL_APP_API_DECL const char* sapp_get_dropped_file_path(int index); + +/* special run-function for SOKOL_NO_ENTRY (in standard mode this is an empty stub) */ +SOKOL_APP_API_DECL void sapp_run(const sapp_desc* desc); + +/* GL: return true when GLES2 fallback is active (to detect fallback from GLES3) */ +SOKOL_APP_API_DECL bool sapp_gles2(void); + +/* HTML5: enable or disable the hardwired "Leave Site?" dialog box */ +SOKOL_APP_API_DECL void sapp_html5_ask_leave_site(bool ask); +/* HTML5: get byte size of a dropped file */ +SOKOL_APP_API_DECL uint32_t sapp_html5_get_dropped_file_size(int index); +/* HTML5: asynchronously load the content of a dropped file */ +SOKOL_APP_API_DECL void sapp_html5_fetch_dropped_file(const sapp_html5_fetch_request* request); + +/* Metal: get bridged pointer to Metal device object */ +SOKOL_APP_API_DECL const void* sapp_metal_get_device(void); +/* Metal: get bridged pointer to this frame's renderpass descriptor */ +SOKOL_APP_API_DECL const void* sapp_metal_get_renderpass_descriptor(void); +/* Metal: get bridged pointer to current drawable */ +SOKOL_APP_API_DECL const void* sapp_metal_get_drawable(void); +/* macOS: get bridged pointer to macOS NSWindow */ +SOKOL_APP_API_DECL const void* sapp_macos_get_window(void); +/* iOS: get bridged pointer to iOS UIWindow */ +SOKOL_APP_API_DECL const void* sapp_ios_get_window(void); + +/* D3D11: get pointer to ID3D11Device object */ +SOKOL_APP_API_DECL const void* sapp_d3d11_get_device(void); +/* D3D11: get pointer to ID3D11DeviceContext object */ +SOKOL_APP_API_DECL const void* sapp_d3d11_get_device_context(void); +/* D3D11: get pointer to ID3D11RenderTargetView object */ +SOKOL_APP_API_DECL const void* sapp_d3d11_get_render_target_view(void); +/* D3D11: get pointer to ID3D11DepthStencilView */ +SOKOL_APP_API_DECL const void* sapp_d3d11_get_depth_stencil_view(void); +/* Win32: get the HWND window handle */ +SOKOL_APP_API_DECL const void* sapp_win32_get_hwnd(void); + +/* WebGPU: get WGPUDevice handle */ +SOKOL_APP_API_DECL const void* sapp_wgpu_get_device(void); +/* WebGPU: get swapchain's WGPUTextureView handle for rendering */ +SOKOL_APP_API_DECL const void* sapp_wgpu_get_render_view(void); +/* WebGPU: get swapchain's MSAA-resolve WGPUTextureView (may return null) */ +SOKOL_APP_API_DECL const void* sapp_wgpu_get_resolve_view(void); +/* WebGPU: get swapchain's WGPUTextureView for the depth-stencil surface */ +SOKOL_APP_API_DECL const void* sapp_wgpu_get_depth_stencil_view(void); + +/* Android: get native activity handle */ +SOKOL_APP_API_DECL const void* sapp_android_get_native_activity(void); + +#ifdef __cplusplus +} /* extern "C" */ + +/* reference-based equivalents for C++ */ +inline void sapp_run(const sapp_desc& desc) { return sapp_run(&desc); } + +#endif + +// this WinRT specific hack is required when wWinMain is in a static library +#if defined(_MSC_VER) && defined(UNICODE) +#include +#if defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) +#pragma comment(linker, "/include:wWinMain") +#endif +#endif + +#endif // SOKOL_APP_INCLUDED + +/*-- IMPLEMENTATION ----------------------------------------------------------*/ +#ifdef SOKOL_APP_IMPL +#define SOKOL_APP_IMPL_INCLUDED (1) + +#include // memset +#include // size_t + +/* check if the config defines are alright */ +#if defined(__APPLE__) + // see https://clang.llvm.org/docs/LanguageExtensions.html#automatic-reference-counting + #if !defined(__cplusplus) + #if __has_feature(objc_arc) && !__has_feature(objc_arc_fields) + #error "sokol_app.h requires __has_feature(objc_arc_field) if ARC is enabled (use a more recent compiler version)" + #endif + #endif + #define _SAPP_APPLE (1) + #include + #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE + /* MacOS */ + #define _SAPP_MACOS (1) + #if !defined(SOKOL_METAL) && !defined(SOKOL_GLCORE33) + #error("sokol_app.h: unknown 3D API selected for MacOS, must be SOKOL_METAL or SOKOL_GLCORE33") + #endif + #else + /* iOS or iOS Simulator */ + #define _SAPP_IOS (1) + #if !defined(SOKOL_METAL) && !defined(SOKOL_GLES3) + #error("sokol_app.h: unknown 3D API selected for iOS, must be SOKOL_METAL or SOKOL_GLES3") + #endif + #endif +#elif defined(__EMSCRIPTEN__) + /* emscripten (asm.js or wasm) */ + #define _SAPP_EMSCRIPTEN (1) + #if !defined(SOKOL_GLES3) && !defined(SOKOL_GLES2) && !defined(SOKOL_WGPU) + #error("sokol_app.h: unknown 3D API selected for emscripten, must be SOKOL_GLES3, SOKOL_GLES2 or SOKOL_WGPU") + #endif +#elif defined(_WIN32) + /* Windows (D3D11 or GL) */ + #include + #if (defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) + #define _SAPP_UWP (1) + #if !defined(SOKOL_D3D11) + #error("sokol_app.h: unknown 3D API selected for UWP, must be SOKOL_D3D11") + #endif + #if !defined(__cplusplus) + #error("sokol_app.h: UWP bindings require C++/17") + #endif + #else + #define _SAPP_WIN32 (1) + #if !defined(SOKOL_D3D11) && !defined(SOKOL_GLCORE33) + #error("sokol_app.h: unknown 3D API selected for Win32, must be SOKOL_D3D11 or SOKOL_GLCORE33") + #endif + #endif +#elif defined(__ANDROID__) + /* Android */ + #define _SAPP_ANDROID (1) + #if !defined(SOKOL_GLES3) && !defined(SOKOL_GLES2) + #error("sokol_app.h: unknown 3D API selected for Android, must be SOKOL_GLES3 or SOKOL_GLES2") + #endif + #if defined(SOKOL_NO_ENTRY) + #error("sokol_app.h: SOKOL_NO_ENTRY is not supported on Android") + #endif +#elif defined(__linux__) || defined(__unix__) + /* Linux */ + #define _SAPP_LINUX (1) + #if !defined(SOKOL_GLCORE33) + #error("sokol_app.h: unknown 3D API selected for Linux, must be SOKOL_GLCORE33") + #endif +#else +#error "sokol_app.h: Unknown platform" +#endif + +#ifndef SOKOL_API_IMPL + #define SOKOL_API_IMPL +#endif +#ifndef SOKOL_DEBUG + #ifndef NDEBUG + #define SOKOL_DEBUG (1) + #endif +#endif +#ifndef SOKOL_ASSERT + #include + #define SOKOL_ASSERT(c) assert(c) +#endif +#ifndef SOKOL_UNREACHABLE + #define SOKOL_UNREACHABLE SOKOL_ASSERT(false) +#endif +#if !defined(SOKOL_CALLOC) || !defined(SOKOL_FREE) + #include +#endif +#if !defined(SOKOL_CALLOC) + #define SOKOL_CALLOC(n,s) calloc(n,s) +#endif +#if !defined(SOKOL_FREE) + #define SOKOL_FREE(p) free(p) +#endif +#ifndef SOKOL_LOG + #ifdef SOKOL_DEBUG + #if defined(__ANDROID__) + #include + #define SOKOL_LOG(s) { SOKOL_ASSERT(s); __android_log_write(ANDROID_LOG_INFO, "SOKOL_APP", s); } + #else + #include + #define SOKOL_LOG(s) { SOKOL_ASSERT(s); puts(s); } + #endif + #else + #define SOKOL_LOG(s) + #endif +#endif +#ifndef SOKOL_ABORT + #include + #define SOKOL_ABORT() abort() +#endif +#ifndef _SOKOL_PRIVATE + #if defined(__GNUC__) || defined(__clang__) + #define _SOKOL_PRIVATE __attribute__((unused)) static + #else + #define _SOKOL_PRIVATE static + #endif +#endif +#ifndef _SOKOL_UNUSED + #define _SOKOL_UNUSED(x) (void)(x) +#endif + +/*== PLATFORM SPECIFIC INCLUDES AND DEFINES ==================================*/ +#if defined(_SAPP_APPLE) + #if defined(SOKOL_METAL) + #import + #import + #endif + #if defined(_SAPP_MACOS) + #if !defined(SOKOL_METAL) + #ifndef GL_SILENCE_DEPRECATION + #define GL_SILENCE_DEPRECATION + #endif + #include + #endif + #elif defined(_SAPP_IOS) + #import + #if !defined(SOKOL_METAL) + #import + #endif + #endif +#elif defined(_SAPP_EMSCRIPTEN) + #if defined(SOKOL_WGPU) + #include + #endif + #include + #include +#elif defined(_SAPP_WIN32) + #ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ + #pragma warning(disable:4204) /* nonstandard extension used: non-constant aggregate initializer */ + #pragma warning(disable:4054) /* 'type cast': from function pointer */ + #pragma warning(disable:4055) /* 'type cast': from data pointer */ + #pragma warning(disable:4505) /* unreferenced local function has been removed */ + #pragma warning(disable:4115) /* /W4: 'ID3D11ModuleInstance': named type definition in parentheses (in d3d11.h) */ + #endif + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #ifndef NOMINMAX + #define NOMINMAX + #endif + #include + #include + #include + #if !defined(SOKOL_NO_ENTRY) // if SOKOL_NO_ENTRY is defined, it's the applications' responsibility to use the right subsystem + #if defined(SOKOL_WIN32_FORCE_MAIN) + #pragma comment (linker, "/subsystem:console") + #else + #pragma comment (linker, "/subsystem:windows") + #endif + #endif + #include /* freopen_s() */ + #include /* wcslen() */ + + #pragma comment (lib, "kernel32") + #pragma comment (lib, "user32") + #pragma comment (lib, "shell32") /* CommandLineToArgvW, DragQueryFileW, DragFinished */ + #if defined(SOKOL_D3D11) + #pragma comment (lib, "dxgi") + #pragma comment (lib, "d3d11") + #pragma comment (lib, "dxguid") + #endif + #if defined(SOKOL_GLCORE33) + #pragma comment (lib, "gdi32") + #endif + + #if defined(SOKOL_D3D11) + #ifndef D3D11_NO_HELPERS + #define D3D11_NO_HELPERS + #endif + #include + #include + // DXGI_SWAP_EFFECT_FLIP_DISCARD is only defined in newer Windows SDKs, so don't depend on it + #define _SAPP_DXGI_SWAP_EFFECT_FLIP_DISCARD (4) + #endif + #ifndef WM_MOUSEHWHEEL /* see https://github.com/floooh/sokol/issues/138 */ + #define WM_MOUSEHWHEEL (0x020E) + #endif +#elif defined(_SAPP_UWP) + #ifndef NOMINMAX + #define NOMINMAX + #endif + #ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ + #pragma warning(disable:4054) /* 'type cast': from function pointer */ + #pragma warning(disable:4055) /* 'type cast': from data pointer */ + #pragma warning(disable:4505) /* unreferenced local function has been removed */ + #pragma warning(disable:4115) /* /W4: 'ID3D11ModuleInstance': named type definition in parentheses (in d3d11.h) */ + #endif + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #include + #include + #include + + #pragma comment (lib, "WindowsApp") + #pragma comment (lib, "dxguid") +#elif defined(_SAPP_ANDROID) + #include + #include + #include + #include + #include +#elif defined(_SAPP_LINUX) + #define GL_GLEXT_PROTOTYPES + #include + #include + #include + #include + #include + #include + #include + #include + #include /* CARD32 */ + #include /* dlopen, dlsym, dlclose */ + #include /* LONG_MAX */ + #include /* only used a linker-guard, search for _sapp_linux_run() and see first comment */ +#endif + +/*== MACOS DECLARATIONS ======================================================*/ +#if defined(_SAPP_MACOS) +// __v_ +@interface SokolWindow : NSWindow { +} +@end +@interface MyView2 : NSView +@end + +MyView2* g_view; + +// A custom NSWindow interface to handle events in borderless windows. +@implementation SokolWindow +- (BOOL)canBecomeKeyWindow { return YES; } // needed for NSWindowStyleMaskBorderless +- (BOOL)canBecomeMainWindow { return YES; } +@end +// __v_ + +@interface _sapp_macos_app_delegate : NSObject +@end +@interface _sapp_macos_window : NSWindow +@end +@interface _sapp_macos_window_delegate : NSObject +@end +#if defined(SOKOL_METAL) + @interface _sapp_macos_view : MTKView + @end +#elif defined(SOKOL_GLCORE33) + @interface _sapp_macos_view : NSOpenGLView + - (void)timerFired:(id)sender; + @end +#endif // SOKOL_GLCORE33 + +typedef struct { + uint32_t flags_changed_store; + uint8_t mouse_buttons; +// NSWindow* window; +// SokolWindow* window; // __v_ + _sapp_macos_window* window; // __v_ + NSTrackingArea* tracking_area; + _sapp_macos_app_delegate* app_dlg; + _sapp_macos_window_delegate* win_dlg; + _sapp_macos_view* view; + #if defined(SOKOL_METAL) + id mtl_device; + #endif +} _sapp_macos_t; + +#endif // _SAPP_MACOS + +/*== IOS DECLARATIONS ========================================================*/ +#if defined(_SAPP_IOS) + +@interface _sapp_app_delegate : NSObject +@end +@interface _sapp_textfield_dlg : NSObject +- (void)keyboardWasShown:(NSNotification*)notif; +- (void)keyboardWillBeHidden:(NSNotification*)notif; +- (void)keyboardDidChangeFrame:(NSNotification*)notif; +@end +#if defined(SOKOL_METAL) + @interface _sapp_ios_view : MTKView; + @end +#else + @interface _sapp_ios_view : GLKView + @end +#endif + +typedef struct { + UIWindow* window; + _sapp_ios_view* view; + UITextField* textfield; + _sapp_textfield_dlg* textfield_dlg; + #if defined(SOKOL_METAL) + UIViewController* view_ctrl; + id mtl_device; + #else + GLKViewController* view_ctrl; + EAGLContext* eagl_ctx; + #endif + bool suspended; +} _sapp_ios_t; + +#endif // _SAPP_IOS + +/*== EMSCRIPTEN DECLARATIONS =================================================*/ +#if defined(_SAPP_EMSCRIPTEN) + +#if defined(SOKOL_WGPU) +typedef struct { + int state; + WGPUDevice device; + WGPUSwapChain swapchain; + WGPUTextureFormat render_format; + WGPUTexture msaa_tex; + WGPUTexture depth_stencil_tex; + WGPUTextureView swapchain_view; + WGPUTextureView msaa_view; + WGPUTextureView depth_stencil_view; +} _sapp_wgpu_t; +#endif + +typedef struct { + bool textfield_created; + bool wants_show_keyboard; + bool wants_hide_keyboard; + bool mouse_lock_requested; + #if defined(SOKOL_WGPU) + _sapp_wgpu_t wgpu; + #endif +} _sapp_emsc_t; +#endif // _SAPP_EMSCRIPTEN + +/*== WIN32 DECLARATIONS ======================================================*/ +#if defined(SOKOL_D3D11) && (defined(_SAPP_WIN32) || defined(_SAPP_UWP)) +typedef struct { + ID3D11Device* device; + ID3D11DeviceContext* device_context; + ID3D11Texture2D* rt; + ID3D11RenderTargetView* rtv; + ID3D11Texture2D* msaa_rt; + ID3D11RenderTargetView* msaa_rtv; + ID3D11Texture2D* ds; + ID3D11DepthStencilView* dsv; + DXGI_SWAP_CHAIN_DESC swap_chain_desc; + IDXGISwapChain* swap_chain; +} _sapp_d3d11_t; +#endif + +/*== WIN32 DECLARATIONS ======================================================*/ +#if defined(_SAPP_WIN32) + +#ifndef DPI_ENUMS_DECLARED +typedef enum PROCESS_DPI_AWARENESS +{ + PROCESS_DPI_UNAWARE = 0, + PROCESS_SYSTEM_DPI_AWARE = 1, + PROCESS_PER_MONITOR_DPI_AWARE = 2 +} PROCESS_DPI_AWARENESS; +typedef enum MONITOR_DPI_TYPE { + MDT_EFFECTIVE_DPI = 0, + MDT_ANGULAR_DPI = 1, + MDT_RAW_DPI = 2, + MDT_DEFAULT = MDT_EFFECTIVE_DPI +} MONITOR_DPI_TYPE; +#endif /*DPI_ENUMS_DECLARED*/ + +typedef struct { + bool aware; + float content_scale; + float window_scale; + float mouse_scale; +} _sapp_win32_dpi_t; + +typedef struct { + HWND hwnd; + HDC dc; + UINT orig_codepage; + LONG mouse_locked_x, mouse_locked_y; + bool is_win10_or_greater; + bool in_create_window; + bool iconified; + bool mouse_tracked; + uint8_t mouse_capture_mask; + _sapp_win32_dpi_t dpi; + bool raw_input_mousepos_valid; + LONG raw_input_mousepos_x; + LONG raw_input_mousepos_y; + uint8_t raw_input_data[256]; +} _sapp_win32_t; + +#if defined(SOKOL_GLCORE33) +#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 +#define WGL_SUPPORT_OPENGL_ARB 0x2010 +#define WGL_DRAW_TO_WINDOW_ARB 0x2001 +#define WGL_PIXEL_TYPE_ARB 0x2013 +#define WGL_TYPE_RGBA_ARB 0x202b +#define WGL_ACCELERATION_ARB 0x2003 +#define WGL_NO_ACCELERATION_ARB 0x2025 +#define WGL_RED_BITS_ARB 0x2015 +#define WGL_GREEN_BITS_ARB 0x2017 +#define WGL_BLUE_BITS_ARB 0x2019 +#define WGL_ALPHA_BITS_ARB 0x201b +#define WGL_DEPTH_BITS_ARB 0x2022 +#define WGL_STENCIL_BITS_ARB 0x2023 +#define WGL_DOUBLE_BUFFER_ARB 0x2011 +#define WGL_SAMPLES_ARB 0x2042 +#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 +#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 +#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define WGL_CONTEXT_FLAGS_ARB 0x2094 +#define ERROR_INVALID_VERSION_ARB 0x2095 +#define ERROR_INVALID_PROFILE_ARB 0x2096 +#define ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB 0x2054 +typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC)(int); +typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBIVARBPROC)(HDC,int,int,UINT,const int*,int*); +typedef const char* (WINAPI * PFNWGLGETEXTENSIONSSTRINGEXTPROC)(void); +typedef const char* (WINAPI * PFNWGLGETEXTENSIONSSTRINGARBPROC)(HDC); +typedef HGLRC (WINAPI * PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC,HGLRC,const int*); +typedef HGLRC (WINAPI * PFN_wglCreateContext)(HDC); +typedef BOOL (WINAPI * PFN_wglDeleteContext)(HGLRC); +typedef PROC (WINAPI * PFN_wglGetProcAddress)(LPCSTR); +typedef HDC (WINAPI * PFN_wglGetCurrentDC)(void); +typedef BOOL (WINAPI * PFN_wglMakeCurrent)(HDC,HGLRC); + +typedef struct { + HINSTANCE opengl32; + HGLRC gl_ctx; + PFN_wglCreateContext CreateContext; + PFN_wglDeleteContext DeleteContext; + PFN_wglGetProcAddress GetProcAddress; + PFN_wglGetCurrentDC GetCurrentDC; + PFN_wglMakeCurrent MakeCurrent; + PFNWGLSWAPINTERVALEXTPROC SwapIntervalEXT; + PFNWGLGETPIXELFORMATATTRIBIVARBPROC GetPixelFormatAttribivARB; + PFNWGLGETEXTENSIONSSTRINGEXTPROC GetExtensionsStringEXT; + PFNWGLGETEXTENSIONSSTRINGARBPROC GetExtensionsStringARB; + PFNWGLCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB; + bool ext_swap_control; + bool arb_multisample; + bool arb_pixel_format; + bool arb_create_context; + bool arb_create_context_profile; + HWND msg_hwnd; + HDC msg_dc; +} _sapp_wgl_t; +#endif // SOKOL_GLCORE33 + +#endif // _SAPP_WIN32 + +/*== UWP DECLARATIONS ======================================================*/ +#if defined(_SAPP_UWP) + +typedef struct { + float content_scale; + float window_scale; + float mouse_scale; +} _sapp_uwp_dpi_t; + +typedef struct { + bool mouse_tracked; + uint8_t mouse_buttons; + _sapp_uwp_dpi_t dpi; +} _sapp_uwp_t; + +#endif // _SAPP_UWP + +/*== ANDROID DECLARATIONS ====================================================*/ + +#if defined(_SAPP_ANDROID) +typedef enum { + _SOKOL_ANDROID_MSG_CREATE, + _SOKOL_ANDROID_MSG_RESUME, + _SOKOL_ANDROID_MSG_PAUSE, + _SOKOL_ANDROID_MSG_FOCUS, + _SOKOL_ANDROID_MSG_NO_FOCUS, + _SOKOL_ANDROID_MSG_SET_NATIVE_WINDOW, + _SOKOL_ANDROID_MSG_SET_INPUT_QUEUE, + _SOKOL_ANDROID_MSG_DESTROY, +} _sapp_android_msg_t; + +typedef struct { + pthread_t thread; + pthread_mutex_t mutex; + pthread_cond_t cond; + int read_from_main_fd; + int write_from_main_fd; +} _sapp_android_pt_t; + +typedef struct { + ANativeWindow* window; + AInputQueue* input; +} _sapp_android_resources_t; + +typedef struct { + ANativeActivity* activity; + _sapp_android_pt_t pt; + _sapp_android_resources_t pending; + _sapp_android_resources_t current; + ALooper* looper; + bool is_thread_started; + bool is_thread_stopping; + bool is_thread_stopped; + bool has_created; + bool has_resumed; + bool has_focus; + EGLConfig config; + EGLDisplay display; + EGLContext context; + EGLSurface surface; +} _sapp_android_t; + +#endif // _SAPP_ANDROID + +/*== LINUX DECLARATIONS ======================================================*/ +#if defined(_SAPP_LINUX) + +#define _SAPP_X11_XDND_VERSION (5) + +#define GLX_VENDOR 1 +#define GLX_RGBA_BIT 0x00000001 +#define GLX_WINDOW_BIT 0x00000001 +#define GLX_DRAWABLE_TYPE 0x8010 +#define GLX_RENDER_TYPE 0x8011 +#define GLX_DOUBLEBUFFER 5 +#define GLX_RED_SIZE 8 +#define GLX_GREEN_SIZE 9 +#define GLX_BLUE_SIZE 10 +#define GLX_ALPHA_SIZE 11 +#define GLX_DEPTH_SIZE 12 +#define GLX_STENCIL_SIZE 13 +#define GLX_SAMPLES 0x186a1 +#define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126 +#define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 +#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define GLX_CONTEXT_FLAGS_ARB 0x2094 + +typedef XID GLXWindow; +typedef XID GLXDrawable; +typedef struct __GLXFBConfig* GLXFBConfig; +typedef struct __GLXcontext* GLXContext; +typedef void (*__GLXextproc)(void); + +typedef int (*PFNGLXGETFBCONFIGATTRIBPROC)(Display*,GLXFBConfig,int,int*); +typedef const char* (*PFNGLXGETCLIENTSTRINGPROC)(Display*,int); +typedef Bool (*PFNGLXQUERYEXTENSIONPROC)(Display*,int*,int*); +typedef Bool (*PFNGLXQUERYVERSIONPROC)(Display*,int*,int*); +typedef void (*PFNGLXDESTROYCONTEXTPROC)(Display*,GLXContext); +typedef Bool (*PFNGLXMAKECURRENTPROC)(Display*,GLXDrawable,GLXContext); +typedef void (*PFNGLXSWAPBUFFERSPROC)(Display*,GLXDrawable); +typedef const char* (*PFNGLXQUERYEXTENSIONSSTRINGPROC)(Display*,int); +typedef GLXFBConfig* (*PFNGLXGETFBCONFIGSPROC)(Display*,int,int*); +typedef __GLXextproc (* PFNGLXGETPROCADDRESSPROC)(const char *procName); +typedef void (*PFNGLXSWAPINTERVALEXTPROC)(Display*,GLXDrawable,int); +typedef XVisualInfo* (*PFNGLXGETVISUALFROMFBCONFIGPROC)(Display*,GLXFBConfig); +typedef GLXWindow (*PFNGLXCREATEWINDOWPROC)(Display*,GLXFBConfig,Window,const int*); +typedef void (*PFNGLXDESTROYWINDOWPROC)(Display*,GLXWindow); + +typedef int (*PFNGLXSWAPINTERVALMESAPROC)(int); +typedef GLXContext (*PFNGLXCREATECONTEXTATTRIBSARBPROC)(Display*,GLXFBConfig,GLXContext,Bool,const int*); + +typedef struct { + bool available; + int major_opcode; + int event_base; + int error_base; + int major; + int minor; +} _sapp_xi_t; + +typedef struct { + int version; + Window source; + Atom format; + Atom XdndAware; + Atom XdndEnter; + Atom XdndPosition; + Atom XdndStatus; + Atom XdndActionCopy; + Atom XdndDrop; + Atom XdndFinished; + Atom XdndSelection; + Atom XdndTypeList; + Atom text_uri_list; +} _sapp_xdnd_t; + +typedef struct { + uint8_t mouse_buttons; + Display* display; + int screen; + Window root; + Colormap colormap; + Window window; + Cursor hidden_cursor; + int window_state; + float dpi; + unsigned char error_code; + Atom UTF8_STRING; + Atom WM_PROTOCOLS; + Atom WM_DELETE_WINDOW; + Atom WM_STATE; + Atom NET_WM_NAME; + Atom NET_WM_ICON_NAME; + Atom NET_WM_STATE; + Atom NET_WM_STATE_FULLSCREEN; + _sapp_xi_t xi; + _sapp_xdnd_t xdnd; +} _sapp_x11_t; + +typedef struct { + void* libgl; + int major; + int minor; + int event_base; + int error_base; + GLXContext ctx; + GLXWindow window; + + // GLX 1.3 functions + PFNGLXGETFBCONFIGSPROC GetFBConfigs; + PFNGLXGETFBCONFIGATTRIBPROC GetFBConfigAttrib; + PFNGLXGETCLIENTSTRINGPROC GetClientString; + PFNGLXQUERYEXTENSIONPROC QueryExtension; + PFNGLXQUERYVERSIONPROC QueryVersion; + PFNGLXDESTROYCONTEXTPROC DestroyContext; + PFNGLXMAKECURRENTPROC MakeCurrent; + PFNGLXSWAPBUFFERSPROC SwapBuffers; + PFNGLXQUERYEXTENSIONSSTRINGPROC QueryExtensionsString; + PFNGLXGETVISUALFROMFBCONFIGPROC GetVisualFromFBConfig; + PFNGLXCREATEWINDOWPROC CreateWindow; + PFNGLXDESTROYWINDOWPROC DestroyWindow; + + // GLX 1.4 and extension functions + PFNGLXGETPROCADDRESSPROC GetProcAddress; + PFNGLXGETPROCADDRESSPROC GetProcAddressARB; + PFNGLXSWAPINTERVALEXTPROC SwapIntervalEXT; + PFNGLXSWAPINTERVALMESAPROC SwapIntervalMESA; + PFNGLXCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB; + + // extension availability + bool EXT_swap_control; + bool MESA_swap_control; + bool ARB_multisample; + bool ARB_create_context; + bool ARB_create_context_profile; +} _sapp_glx_t; + +#endif // _SAPP_LINUX + +/*== COMMON DECLARATIONS =====================================================*/ + +/* helper macros */ +#define _sapp_def(val, def) (((val) == 0) ? (def) : (val)) +#define _sapp_absf(a) (((a)<0.0f)?-(a):(a)) + +#define _SAPP_MAX_TITLE_LENGTH (128) +/* NOTE: the pixel format values *must* be compatible with sg_pixel_format */ +#define _SAPP_PIXELFORMAT_RGBA8 (23) +#define _SAPP_PIXELFORMAT_BGRA8 (27) +#define _SAPP_PIXELFORMAT_DEPTH (41) +#define _SAPP_PIXELFORMAT_DEPTH_STENCIL (42) + +#if defined(_SAPP_MACOS) || defined(_SAPP_IOS) + // this is ARC compatible + #if defined(__cplusplus) + #define _SAPP_CLEAR(type, item) { item = (type) { }; } + #else + #define _SAPP_CLEAR(type, item) { item = (type) { 0 }; } + #endif +#else + #define _SAPP_CLEAR(type, item) { memset(&item, 0, sizeof(item)); } +#endif + +typedef struct { + bool enabled; + int buf_size; + char* buffer; +} _sapp_clipboard_t; + +typedef struct { + bool enabled; + int max_files; + int max_path_length; + int num_files; + int buf_size; + char* buffer; +} _sapp_drop_t; + +typedef struct { + float x, y; + float dx, dy; + bool shown; + bool locked; + bool pos_valid; +} _sapp_mouse_t; + +typedef struct { + sapp_desc desc; + bool valid; + bool fullscreen; + bool gles2_fallback; + bool first_frame; + bool init_called; + bool cleanup_called; + bool quit_requested; + bool quit_ordered; + bool event_consumed; + bool html5_ask_leave_site; + bool onscreen_keyboard_shown; + int window_width; + int window_height; + int framebuffer_width; + int framebuffer_height; + int sample_count; + int swap_interval; + float dpi_scale; + uint64_t frame_count; + sapp_event event; + _sapp_mouse_t mouse; + _sapp_clipboard_t clipboard; + _sapp_drop_t drop; + #if defined(_SAPP_MACOS) + _sapp_macos_t macos; + #elif defined(_SAPP_IOS) + _sapp_ios_t ios; + #elif defined(_SAPP_EMSCRIPTEN) + _sapp_emsc_t emsc; + #elif defined(_SAPP_WIN32) + _sapp_win32_t win32; + #if defined(SOKOL_D3D11) + _sapp_d3d11_t d3d11; + #elif defined(SOKOL_GLCORE33) + _sapp_wgl_t wgl; + #endif + #elif defined(_SAPP_UWP) + _sapp_uwp_t uwp; + #if defined(SOKOL_D3D11) + _sapp_d3d11_t d3d11; + #endif + #elif defined(_SAPP_ANDROID) + _sapp_android_t android; + #elif defined(_SAPP_LINUX) + _sapp_x11_t x11; + _sapp_glx_t glx; + #endif + char html5_canvas_selector[_SAPP_MAX_TITLE_LENGTH]; + char window_title[_SAPP_MAX_TITLE_LENGTH]; /* UTF-8 */ + wchar_t window_title_wide[_SAPP_MAX_TITLE_LENGTH]; /* UTF-32 or UCS-2 */ + sapp_keycode keycodes[SAPP_MAX_KEYCODES]; + + /* V patches */ + bool __v_native_render; /* V patch to allow for native rendering */ +} _sapp_t; +static _sapp_t _sapp; + +/*=== PRIVATE HELPER FUNCTIONS ===============================================*/ +_SOKOL_PRIVATE void _sapp_fail(const char* msg) { + if (_sapp.desc.fail_cb) { + _sapp.desc.fail_cb(msg); + } + else if (_sapp.desc.fail_userdata_cb) { + _sapp.desc.fail_userdata_cb(msg, _sapp.desc.user_data); + } + else { + SOKOL_LOG(msg); + } + SOKOL_ABORT(); +} + +_SOKOL_PRIVATE void _sapp_call_init(void) { + if (_sapp.desc.init_cb) { + _sapp.desc.init_cb(); + } + else if (_sapp.desc.init_userdata_cb) { + _sapp.desc.init_userdata_cb(_sapp.desc.user_data); + } + _sapp.init_called = true; +} + +_SOKOL_PRIVATE void _sapp_call_frame(void) { + if (_sapp.__v_native_render) { + return; + } + if (_sapp.init_called && !_sapp.cleanup_called) { + if (_sapp.desc.frame_cb) { + _sapp.desc.frame_cb(); + } + else if (_sapp.desc.frame_userdata_cb) { + _sapp.desc.frame_userdata_cb(_sapp.desc.user_data); + } + } +} + +// __v_ +_SOKOL_PRIVATE void _sapp_call_frame_native(void) { +//puts("_sapp_call_frame_native()"); +//printf("init called=%d cleanup_called=%d\n", _sapp.init_called,_sapp.cleanup_called); + if (_sapp.init_called && !_sapp.cleanup_called) { + if (_sapp.desc.frame_cb) { + _sapp.desc.frame_cb(); + } + else if (_sapp.desc.frame_userdata_cb) { + _sapp.desc.frame_userdata_cb(_sapp.desc.user_data); + } + } +} + + +_SOKOL_PRIVATE void _sapp_call_cleanup(void) { + if (!_sapp.cleanup_called) { + if (_sapp.desc.cleanup_cb) { + _sapp.desc.cleanup_cb(); + } + else if (_sapp.desc.cleanup_userdata_cb) { + _sapp.desc.cleanup_userdata_cb(_sapp.desc.user_data); + } + _sapp.cleanup_called = true; + } +} + +_SOKOL_PRIVATE bool _sapp_call_event(const sapp_event* e) { + if (!_sapp.cleanup_called) { + if (_sapp.desc.event_cb) { + _sapp.desc.event_cb(e); + } + else if (_sapp.desc.event_userdata_cb) { + _sapp.desc.event_userdata_cb(e, _sapp.desc.user_data); + } + } + if (_sapp.event_consumed) { + _sapp.event_consumed = false; + return true; + } + else { + return false; + } +} + +_SOKOL_PRIVATE char* _sapp_dropped_file_path_ptr(int index) { + SOKOL_ASSERT(_sapp.drop.buffer); + SOKOL_ASSERT((index >= 0) && (index <= _sapp.drop.max_files)); + int offset = index * _sapp.drop.max_path_length; + SOKOL_ASSERT(offset < _sapp.drop.buf_size); + return &_sapp.drop.buffer[offset]; +} + +/* Copy a string into a fixed size buffer with guaranteed zero- + termination. + + Return false if the string didn't fit into the buffer and had to be clamped. + + FIXME: Currently UTF-8 strings might become invalid if the string + is clamped, because the last zero-byte might be written into + the middle of a multi-byte sequence. +*/ +_SOKOL_PRIVATE bool _sapp_strcpy(const char* src, char* dst, int max_len) { + SOKOL_ASSERT(src && dst && (max_len > 0)); + char* const end = &(dst[max_len-1]); + char c = 0; + for (int i = 0; i < max_len; i++) { + c = *src; + if (c != 0) { + src++; + } + *dst++ = c; + } + /* truncated? */ + if (c != 0) { + *end = 0; + return false; + } + else { + return true; + } +} + +_SOKOL_PRIVATE sapp_desc _sapp_desc_defaults(const sapp_desc* in_desc) { + sapp_desc desc = *in_desc; + desc.width = _sapp_def(desc.width, 640); + desc.height = _sapp_def(desc.height, 480); + desc.sample_count = _sapp_def(desc.sample_count, 1); + desc.swap_interval = _sapp_def(desc.swap_interval, 1); + desc.html5_canvas_name = _sapp_def(desc.html5_canvas_name, "canvas"); + desc.clipboard_size = _sapp_def(desc.clipboard_size, 8192); + desc.max_dropped_files = _sapp_def(desc.max_dropped_files, 1); + desc.max_dropped_file_path_length = _sapp_def(desc.max_dropped_file_path_length, 2048); + desc.window_title = _sapp_def(desc.window_title, "sokol_app"); + return desc; +} + +_SOKOL_PRIVATE void _sapp_init_state(const sapp_desc* desc) { + _SAPP_CLEAR(_sapp_t, _sapp); + _sapp.desc = _sapp_desc_defaults(desc); + _sapp.first_frame = true; + _sapp.window_width = _sapp.desc.width; + _sapp.window_height = _sapp.desc.height; + _sapp.framebuffer_width = _sapp.window_width; + _sapp.framebuffer_height = _sapp.window_height; + _sapp.sample_count = _sapp.desc.sample_count; + _sapp.swap_interval = _sapp.desc.swap_interval; + _sapp.html5_canvas_selector[0] = '#'; + _sapp_strcpy(_sapp.desc.html5_canvas_name, &_sapp.html5_canvas_selector[1], sizeof(_sapp.html5_canvas_selector) - 1); + _sapp.desc.html5_canvas_name = &_sapp.html5_canvas_selector[1]; + _sapp.html5_ask_leave_site = _sapp.desc.html5_ask_leave_site; + _sapp.clipboard.enabled = _sapp.desc.enable_clipboard; + if (_sapp.clipboard.enabled) { + _sapp.clipboard.buf_size = _sapp.desc.clipboard_size; + _sapp.clipboard.buffer = (char*) SOKOL_CALLOC(1, (size_t)_sapp.clipboard.buf_size); + } + _sapp.drop.enabled = _sapp.desc.enable_dragndrop; + if (_sapp.drop.enabled) { + _sapp.drop.max_files = _sapp.desc.max_dropped_files; + _sapp.drop.max_path_length = _sapp.desc.max_dropped_file_path_length; + _sapp.drop.buf_size = _sapp.drop.max_files * _sapp.drop.max_path_length; + _sapp.drop.buffer = (char*) SOKOL_CALLOC(1, (size_t)_sapp.drop.buf_size); + } + _sapp_strcpy(_sapp.desc.window_title, _sapp.window_title, sizeof(_sapp.window_title)); + _sapp.desc.window_title = _sapp.window_title; + _sapp.dpi_scale = 1.0f; + _sapp.fullscreen = _sapp.desc.fullscreen; + _sapp.mouse.shown = true; + _sapp.__v_native_render = _sapp.desc.__v_native_render; +} + +_SOKOL_PRIVATE void _sapp_discard_state(void) { + if (_sapp.clipboard.enabled) { + SOKOL_ASSERT(_sapp.clipboard.buffer); + SOKOL_FREE((void*)_sapp.clipboard.buffer); + } + if (_sapp.drop.enabled) { + SOKOL_ASSERT(_sapp.drop.buffer); + SOKOL_FREE((void*)_sapp.drop.buffer); + } + _SAPP_CLEAR(_sapp_t, _sapp); +} + +_SOKOL_PRIVATE void _sapp_init_event(sapp_event_type type) { + memset(&_sapp.event, 0, sizeof(_sapp.event)); + _sapp.event.type = type; + _sapp.event.frame_count = _sapp.frame_count; + _sapp.event.mouse_button = SAPP_MOUSEBUTTON_INVALID; + _sapp.event.window_width = _sapp.window_width; + _sapp.event.window_height = _sapp.window_height; + _sapp.event.framebuffer_width = _sapp.framebuffer_width; + _sapp.event.framebuffer_height = _sapp.framebuffer_height; + _sapp.event.mouse_x = _sapp.mouse.x; + _sapp.event.mouse_y = _sapp.mouse.y; + _sapp.event.mouse_dx = _sapp.mouse.dx; + _sapp.event.mouse_dy = _sapp.mouse.dy; +} + +_SOKOL_PRIVATE bool _sapp_events_enabled(void) { + /* only send events when an event callback is set, and the init function was called */ + return (_sapp.desc.event_cb || _sapp.desc.event_userdata_cb) && _sapp.init_called; +} + +_SOKOL_PRIVATE sapp_keycode _sapp_translate_key(int scan_code) { + if ((scan_code >= 0) && (scan_code < SAPP_MAX_KEYCODES)) { + return _sapp.keycodes[scan_code]; + } + else { + return SAPP_KEYCODE_INVALID; + } +} + +_SOKOL_PRIVATE void _sapp_clear_drop_buffer(void) { + if (_sapp.drop.enabled) { + SOKOL_ASSERT(_sapp.drop.buffer); + memset(_sapp.drop.buffer, 0, (size_t)_sapp.drop.buf_size); + } +} + +_SOKOL_PRIVATE void _sapp_frame(void) { + if (_sapp.first_frame) { + _sapp.first_frame = false; + _sapp_call_init(); + } + _sapp_call_frame(); + _sapp.frame_count++; +} + +/*== MacOS/iOS ===============================================================*/ +#if defined(_SAPP_APPLE) + +#if __has_feature(objc_arc) +#define _SAPP_OBJC_RELEASE(obj) { obj = nil; } +#else +#define _SAPP_OBJC_RELEASE(obj) { [obj release]; obj = nil; } +#endif + +/*== MacOS ===================================================================*/ +#if defined(_SAPP_MACOS) + +_SOKOL_PRIVATE void _sapp_macos_init_keytable(void) { + _sapp.keycodes[0x1D] = SAPP_KEYCODE_0; + _sapp.keycodes[0x12] = SAPP_KEYCODE_1; + _sapp.keycodes[0x13] = SAPP_KEYCODE_2; + _sapp.keycodes[0x14] = SAPP_KEYCODE_3; + _sapp.keycodes[0x15] = SAPP_KEYCODE_4; + _sapp.keycodes[0x17] = SAPP_KEYCODE_5; + _sapp.keycodes[0x16] = SAPP_KEYCODE_6; + _sapp.keycodes[0x1A] = SAPP_KEYCODE_7; + _sapp.keycodes[0x1C] = SAPP_KEYCODE_8; + _sapp.keycodes[0x19] = SAPP_KEYCODE_9; + _sapp.keycodes[0x00] = SAPP_KEYCODE_A; + _sapp.keycodes[0x0B] = SAPP_KEYCODE_B; + _sapp.keycodes[0x08] = SAPP_KEYCODE_C; + _sapp.keycodes[0x02] = SAPP_KEYCODE_D; + _sapp.keycodes[0x0E] = SAPP_KEYCODE_E; + _sapp.keycodes[0x03] = SAPP_KEYCODE_F; + _sapp.keycodes[0x05] = SAPP_KEYCODE_G; + _sapp.keycodes[0x04] = SAPP_KEYCODE_H; + _sapp.keycodes[0x22] = SAPP_KEYCODE_I; + _sapp.keycodes[0x26] = SAPP_KEYCODE_J; + _sapp.keycodes[0x28] = SAPP_KEYCODE_K; + _sapp.keycodes[0x25] = SAPP_KEYCODE_L; + _sapp.keycodes[0x2E] = SAPP_KEYCODE_M; + _sapp.keycodes[0x2D] = SAPP_KEYCODE_N; + _sapp.keycodes[0x1F] = SAPP_KEYCODE_O; + _sapp.keycodes[0x23] = SAPP_KEYCODE_P; + _sapp.keycodes[0x0C] = SAPP_KEYCODE_Q; + _sapp.keycodes[0x0F] = SAPP_KEYCODE_R; + _sapp.keycodes[0x01] = SAPP_KEYCODE_S; + _sapp.keycodes[0x11] = SAPP_KEYCODE_T; + _sapp.keycodes[0x20] = SAPP_KEYCODE_U; + _sapp.keycodes[0x09] = SAPP_KEYCODE_V; + _sapp.keycodes[0x0D] = SAPP_KEYCODE_W; + _sapp.keycodes[0x07] = SAPP_KEYCODE_X; + _sapp.keycodes[0x10] = SAPP_KEYCODE_Y; + _sapp.keycodes[0x06] = SAPP_KEYCODE_Z; + _sapp.keycodes[0x27] = SAPP_KEYCODE_APOSTROPHE; + _sapp.keycodes[0x2A] = SAPP_KEYCODE_BACKSLASH; + _sapp.keycodes[0x2B] = SAPP_KEYCODE_COMMA; + _sapp.keycodes[0x18] = SAPP_KEYCODE_EQUAL; + _sapp.keycodes[0x32] = SAPP_KEYCODE_GRAVE_ACCENT; + _sapp.keycodes[0x21] = SAPP_KEYCODE_LEFT_BRACKET; + _sapp.keycodes[0x1B] = SAPP_KEYCODE_MINUS; + _sapp.keycodes[0x2F] = SAPP_KEYCODE_PERIOD; + _sapp.keycodes[0x1E] = SAPP_KEYCODE_RIGHT_BRACKET; + _sapp.keycodes[0x29] = SAPP_KEYCODE_SEMICOLON; + _sapp.keycodes[0x2C] = SAPP_KEYCODE_SLASH; + _sapp.keycodes[0x0A] = SAPP_KEYCODE_WORLD_1; + _sapp.keycodes[0x33] = SAPP_KEYCODE_BACKSPACE; + _sapp.keycodes[0x39] = SAPP_KEYCODE_CAPS_LOCK; + _sapp.keycodes[0x75] = SAPP_KEYCODE_DELETE; + _sapp.keycodes[0x7D] = SAPP_KEYCODE_DOWN; + _sapp.keycodes[0x77] = SAPP_KEYCODE_END; + _sapp.keycodes[0x24] = SAPP_KEYCODE_ENTER; + _sapp.keycodes[0x35] = SAPP_KEYCODE_ESCAPE; + _sapp.keycodes[0x7A] = SAPP_KEYCODE_F1; + _sapp.keycodes[0x78] = SAPP_KEYCODE_F2; + _sapp.keycodes[0x63] = SAPP_KEYCODE_F3; + _sapp.keycodes[0x76] = SAPP_KEYCODE_F4; + _sapp.keycodes[0x60] = SAPP_KEYCODE_F5; + _sapp.keycodes[0x61] = SAPP_KEYCODE_F6; + _sapp.keycodes[0x62] = SAPP_KEYCODE_F7; + _sapp.keycodes[0x64] = SAPP_KEYCODE_F8; + _sapp.keycodes[0x65] = SAPP_KEYCODE_F9; + _sapp.keycodes[0x6D] = SAPP_KEYCODE_F10; + _sapp.keycodes[0x67] = SAPP_KEYCODE_F11; + _sapp.keycodes[0x6F] = SAPP_KEYCODE_F12; + _sapp.keycodes[0x69] = SAPP_KEYCODE_F13; + _sapp.keycodes[0x6B] = SAPP_KEYCODE_F14; + _sapp.keycodes[0x71] = SAPP_KEYCODE_F15; + _sapp.keycodes[0x6A] = SAPP_KEYCODE_F16; + _sapp.keycodes[0x40] = SAPP_KEYCODE_F17; + _sapp.keycodes[0x4F] = SAPP_KEYCODE_F18; + _sapp.keycodes[0x50] = SAPP_KEYCODE_F19; + _sapp.keycodes[0x5A] = SAPP_KEYCODE_F20; + _sapp.keycodes[0x73] = SAPP_KEYCODE_HOME; + _sapp.keycodes[0x72] = SAPP_KEYCODE_INSERT; + _sapp.keycodes[0x7B] = SAPP_KEYCODE_LEFT; + _sapp.keycodes[0x3A] = SAPP_KEYCODE_LEFT_ALT; + _sapp.keycodes[0x3B] = SAPP_KEYCODE_LEFT_CONTROL; + _sapp.keycodes[0x38] = SAPP_KEYCODE_LEFT_SHIFT; + _sapp.keycodes[0x37] = SAPP_KEYCODE_LEFT_SUPER; + _sapp.keycodes[0x6E] = SAPP_KEYCODE_MENU; + _sapp.keycodes[0x47] = SAPP_KEYCODE_NUM_LOCK; + _sapp.keycodes[0x79] = SAPP_KEYCODE_PAGE_DOWN; + _sapp.keycodes[0x74] = SAPP_KEYCODE_PAGE_UP; + _sapp.keycodes[0x7C] = SAPP_KEYCODE_RIGHT; + _sapp.keycodes[0x3D] = SAPP_KEYCODE_RIGHT_ALT; + _sapp.keycodes[0x3E] = SAPP_KEYCODE_RIGHT_CONTROL; + _sapp.keycodes[0x3C] = SAPP_KEYCODE_RIGHT_SHIFT; + _sapp.keycodes[0x36] = SAPP_KEYCODE_RIGHT_SUPER; + _sapp.keycodes[0x31] = SAPP_KEYCODE_SPACE; + _sapp.keycodes[0x30] = SAPP_KEYCODE_TAB; + _sapp.keycodes[0x7E] = SAPP_KEYCODE_UP; + _sapp.keycodes[0x52] = SAPP_KEYCODE_KP_0; + _sapp.keycodes[0x53] = SAPP_KEYCODE_KP_1; + _sapp.keycodes[0x54] = SAPP_KEYCODE_KP_2; + _sapp.keycodes[0x55] = SAPP_KEYCODE_KP_3; + _sapp.keycodes[0x56] = SAPP_KEYCODE_KP_4; + _sapp.keycodes[0x57] = SAPP_KEYCODE_KP_5; + _sapp.keycodes[0x58] = SAPP_KEYCODE_KP_6; + _sapp.keycodes[0x59] = SAPP_KEYCODE_KP_7; + _sapp.keycodes[0x5B] = SAPP_KEYCODE_KP_8; + _sapp.keycodes[0x5C] = SAPP_KEYCODE_KP_9; + _sapp.keycodes[0x45] = SAPP_KEYCODE_KP_ADD; + _sapp.keycodes[0x41] = SAPP_KEYCODE_KP_DECIMAL; + _sapp.keycodes[0x4B] = SAPP_KEYCODE_KP_DIVIDE; + _sapp.keycodes[0x4C] = SAPP_KEYCODE_KP_ENTER; + _sapp.keycodes[0x51] = SAPP_KEYCODE_KP_EQUAL; + _sapp.keycodes[0x43] = SAPP_KEYCODE_KP_MULTIPLY; + _sapp.keycodes[0x4E] = SAPP_KEYCODE_KP_SUBTRACT; +} + +_SOKOL_PRIVATE void _sapp_macos_discard_state(void) { + // NOTE: it's safe to call [release] on a nil object + _SAPP_OBJC_RELEASE(_sapp.macos.tracking_area); + _SAPP_OBJC_RELEASE(_sapp.macos.app_dlg); + _SAPP_OBJC_RELEASE(_sapp.macos.win_dlg); + _SAPP_OBJC_RELEASE(_sapp.macos.view); + #if defined(SOKOL_METAL) + _SAPP_OBJC_RELEASE(_sapp.macos.mtl_device); + #endif + _SAPP_OBJC_RELEASE(_sapp.macos.window); +} + +_SOKOL_PRIVATE void _sapp_macos_run(const sapp_desc* desc) { + _sapp_init_state(desc); + _sapp_macos_init_keytable(); + [NSApplication sharedApplication]; + NSApp.activationPolicy = NSApplicationActivationPolicyRegular; + _sapp.macos.app_dlg = [[_sapp_macos_app_delegate alloc] init]; + NSApp.delegate = _sapp.macos.app_dlg; + [NSApp activateIgnoringOtherApps:YES]; + [NSApp run]; + // NOTE: [NSApp run] never returns, instead cleanup code + // must be put into applicationWillTerminate +} + +/* MacOS entry function */ +#if !defined(SOKOL_NO_ENTRY) +int main(int argc, char* argv[]) { + sapp_desc desc = sokol_main(argc, argv); + _sapp_macos_run(&desc); + return 0; +} +#endif /* SOKOL_NO_ENTRY */ + +_SOKOL_PRIVATE uint32_t _sapp_macos_mod(NSEventModifierFlags f) { + uint32_t m = 0; + if (f & NSEventModifierFlagShift) { + m |= SAPP_MODIFIER_SHIFT; + } + if (f & NSEventModifierFlagControl) { + m |= SAPP_MODIFIER_CTRL; + } + if (f & NSEventModifierFlagOption) { + m |= SAPP_MODIFIER_ALT; + } + if (f & NSEventModifierFlagCommand) { + m |= SAPP_MODIFIER_SUPER; + } + return m; +} + +_SOKOL_PRIVATE void _sapp_macos_mouse_event(sapp_event_type type, sapp_mousebutton btn, uint32_t mod) { + if (_sapp_events_enabled()) { + _sapp_init_event(type); + _sapp.event.mouse_button = btn; + _sapp.event.modifiers = mod; + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE void _sapp_macos_key_event(sapp_event_type type, sapp_keycode key, bool repeat, uint32_t mod) { + if (_sapp_events_enabled()) { + _sapp_init_event(type); + _sapp.event.key_code = key; + _sapp.event.key_repeat = repeat; + _sapp.event.modifiers = mod; + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE void _sapp_macos_app_event(sapp_event_type type) { + if (_sapp_events_enabled()) { + _sapp_init_event(type); + _sapp_call_event(&_sapp.event); + } +} + +/* NOTE: unlike the iOS version of this function, the macOS version + can dynamically update the DPI scaling factor when a window is moved + between HighDPI / LowDPI screens. +*/ +_SOKOL_PRIVATE void _sapp_macos_update_dimensions(void) { + #if defined(SOKOL_METAL) + const NSRect fb_rect = [_sapp.macos.view bounds]; + _sapp.framebuffer_width = fb_rect.size.width * _sapp.dpi_scale; + _sapp.framebuffer_height = fb_rect.size.height * _sapp.dpi_scale; + #elif defined(SOKOL_GLCORE33) + const NSRect fb_rect = [_sapp.macos.view convertRectToBacking:[_sapp.macos.view frame]]; + _sapp.framebuffer_width = fb_rect.size.width; + _sapp.framebuffer_height = fb_rect.size.height; + #endif + const NSRect bounds = [_sapp.macos.view bounds]; + _sapp.window_width = bounds.size.width; + _sapp.window_height = bounds.size.height; + if (_sapp.framebuffer_width == 0) { + _sapp.framebuffer_width = 1; + } + if (_sapp.framebuffer_height == 0) { + _sapp.framebuffer_height = 1; + } + if (_sapp.window_width == 0) { + _sapp.window_width = 1; + } + if (_sapp.window_height == 0) { + _sapp.window_height = 1; + } + _sapp.dpi_scale = (float)_sapp.framebuffer_width / (float)_sapp.window_width; + + /* NOTE: _sapp_macos_update_dimensions() isn't called each frame, but only + when the window size actually changes, so resizing the MTKView's + in each call is fine even when MTKView doesn't ignore setting an + identical drawableSize. + */ + #if defined(SOKOL_METAL) + CGSize drawable_size = { (CGFloat) _sapp.framebuffer_width, (CGFloat) _sapp.framebuffer_height }; + _sapp.macos.view.drawableSize = drawable_size; + #endif +} + +_SOKOL_PRIVATE void _sapp_macos_toggle_fullscreen(void) { + /* NOTE: the _sapp.fullscreen flag is also notified by the + windowDidEnterFullscreen / windowDidExitFullscreen + event handlers + */ + _sapp.fullscreen = !_sapp.fullscreen; + [_sapp.macos.window toggleFullScreen:nil]; +} + +_SOKOL_PRIVATE void _sapp_macos_set_clipboard_string(const char* str) { + @autoreleasepool { + NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; + [pasteboard declareTypes:@[NSPasteboardTypeString] owner:nil]; + [pasteboard setString:@(str) forType:NSPasteboardTypeString]; + } +} + +_SOKOL_PRIVATE const char* _sapp_macos_get_clipboard_string(void) { + SOKOL_ASSERT(_sapp.clipboard.buffer); + @autoreleasepool { + _sapp.clipboard.buffer[0] = 0; + NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; + if (![[pasteboard types] containsObject:NSPasteboardTypeString]) { + return _sapp.clipboard.buffer; + } + NSString* str = [pasteboard stringForType:NSPasteboardTypeString]; + if (!str) { + return _sapp.clipboard.buffer; + } + _sapp_strcpy([str UTF8String], _sapp.clipboard.buffer, _sapp.clipboard.buf_size); + } + return _sapp.clipboard.buffer; +} + +_SOKOL_PRIVATE void _sapp_macos_update_window_title(void) { + [_sapp.macos.window setTitle: [NSString stringWithUTF8String:_sapp.window_title]]; +} + +_SOKOL_PRIVATE void _sapp_macos_update_mouse(NSEvent* event) { + if (!_sapp.mouse.locked) { + const NSPoint mouse_pos = event.locationInWindow; + float new_x = mouse_pos.x * _sapp.dpi_scale; + float new_y = _sapp.framebuffer_height - (mouse_pos.y * _sapp.dpi_scale) - 1; + /* don't update dx/dy in the very first update */ + if (_sapp.mouse.pos_valid) { + _sapp.mouse.dx = new_x - _sapp.mouse.x; + _sapp.mouse.dy = new_y - _sapp.mouse.y; + } + _sapp.mouse.x = new_x; + _sapp.mouse.y = new_y; + _sapp.mouse.pos_valid = true; + } +} + +_SOKOL_PRIVATE void _sapp_macos_show_mouse(bool visible) { + /* NOTE: this function is only called when the mouse visibility actually changes */ + if (visible) { + CGDisplayShowCursor(kCGDirectMainDisplay); + } + else { + CGDisplayHideCursor(kCGDirectMainDisplay); + } +} + +_SOKOL_PRIVATE void _sapp_macos_lock_mouse(bool lock) { + if (lock == _sapp.mouse.locked) { + return; + } + _sapp.mouse.dx = 0.0f; + _sapp.mouse.dy = 0.0f; + _sapp.mouse.locked = lock; + /* + NOTE that this code doesn't warp the mouse cursor to the window + center as everybody else does it. This lead to a spike in the + *second* mouse-moved event after the warp happened. The + mouse centering doesn't seem to be required (mouse-moved events + are reported correctly even when the cursor is at an edge of the screen). + + NOTE also that the hide/show of the mouse cursor should properly + stack with calls to sapp_show_mouse() + */ + if (_sapp.mouse.locked) { + CGAssociateMouseAndMouseCursorPosition(NO); + CGDisplayHideCursor(kCGDirectMainDisplay); + } + else { + CGDisplayShowCursor(kCGDirectMainDisplay); + CGAssociateMouseAndMouseCursorPosition(YES); + } +} + +_SOKOL_PRIVATE void _sapp_macos_frame(void) { + _sapp_frame(); + if (_sapp.quit_requested || _sapp.quit_ordered) { + [_sapp.macos.window performClose:nil]; + } +} + +#include "sokol_app2.h" // __v_ + +@implementation _sapp_macos_app_delegate +- (void)applicationDidFinishLaunching:(NSNotification*)aNotification { + _SOKOL_UNUSED(aNotification); + if (_sapp.fullscreen) { + NSRect screen_rect = NSScreen.mainScreen.frame; + _sapp.window_width = screen_rect.size.width; + _sapp.window_height = screen_rect.size.height; + } + if (_sapp.desc.high_dpi) { + _sapp.framebuffer_width = 2 * _sapp.window_width; + _sapp.framebuffer_height = 2 * _sapp.window_height; + } + else { + _sapp.framebuffer_width = _sapp.window_width; + _sapp.framebuffer_height = _sapp.window_height; + } + _sapp.dpi_scale = (float)_sapp.framebuffer_width / (float) _sapp.window_width; + const NSUInteger style = _sapp.desc.fullscreen ? NSWindowStyleMaskBorderless : // __v_ + NSWindowStyleMaskTitled | + NSWindowStyleMaskClosable | + NSWindowStyleMaskMiniaturizable | + NSWindowStyleMaskResizable; + NSRect window_rect = NSMakeRect(0, 0, _sapp.window_width, _sapp.window_height); + _sapp.macos.window = [[_sapp_macos_window alloc] + initWithContentRect:window_rect + styleMask:style + backing:NSBackingStoreBuffered + defer:NO]; + _sapp.macos.window.releasedWhenClosed = NO; // this is necessary for proper cleanup in applicationWillTerminate + _sapp.macos.window.title = [NSString stringWithUTF8String:_sapp.window_title]; + _sapp.macos.window.acceptsMouseMovedEvents = YES; + _sapp.macos.window.restorable = YES; + + + + // _v__ + _sapp.macos.window.backgroundColor = [NSColor whiteColor]; + + // Quit menu + NSMenu* menu_bar = [[NSMenu alloc] init]; +NSMenuItem* app_menu_item = [[NSMenuItem alloc] init]; +[menu_bar addItem:app_menu_item]; +NSApp.mainMenu = menu_bar; +NSMenu* app_menu = [[NSMenu alloc] init]; +NSString* window_title_as_nsstring = [NSString stringWithUTF8String:_sapp.window_title]; +// `quit_title` memory will be owned by the NSMenuItem, so no need to release it ourselves +NSString* quit_title = [@"Quit " stringByAppendingString:window_title_as_nsstring]; +NSMenuItem* quit_menu_item = [[NSMenuItem alloc] + initWithTitle:quit_title + action:@selector(terminate:) + keyEquivalent:@"q"]; +[app_menu addItem:quit_menu_item]; +app_menu_item.submenu = app_menu; +_SAPP_OBJC_RELEASE( window_title_as_nsstring ); +_SAPP_OBJC_RELEASE( app_menu ); +_SAPP_OBJC_RELEASE( app_menu_item ); +_SAPP_OBJC_RELEASE( menu_bar ); + + + // _v__ + + _sapp.macos.win_dlg = [[_sapp_macos_window_delegate alloc] init]; + _sapp.macos.window.delegate = _sapp.macos.win_dlg; + #if defined(SOKOL_METAL) + _sapp.macos.mtl_device = MTLCreateSystemDefaultDevice(); + _sapp.macos.view = [[_sapp_macos_view alloc] init]; + [_sapp.macos.view updateTrackingAreas]; + _sapp.macos.view.preferredFramesPerSecond = 60 / _sapp.swap_interval; + _sapp.macos.view.device = _sapp.macos.mtl_device; + _sapp.macos.view.colorPixelFormat = MTLPixelFormatBGRA8Unorm; + _sapp.macos.view.depthStencilPixelFormat = MTLPixelFormatDepth32Float_Stencil8; + _sapp.macos.view.sampleCount = (NSUInteger) _sapp.sample_count; + _sapp.macos.view.autoResizeDrawable = false; + _sapp.macos.window.contentView = _sapp.macos.view; + [_sapp.macos.window makeFirstResponder:_sapp.macos.view]; + _sapp.macos.view.layer.magnificationFilter = kCAFilterNearest; + #elif defined(SOKOL_GLCORE33) + NSOpenGLPixelFormatAttribute attrs[32]; + int i = 0; + attrs[i++] = NSOpenGLPFAAccelerated; + attrs[i++] = NSOpenGLPFADoubleBuffer; + attrs[i++] = NSOpenGLPFAOpenGLProfile; attrs[i++] = NSOpenGLProfileVersion3_2Core; + attrs[i++] = NSOpenGLPFAColorSize; attrs[i++] = 24; + attrs[i++] = NSOpenGLPFAAlphaSize; attrs[i++] = 8; + attrs[i++] = NSOpenGLPFADepthSize; attrs[i++] = 24; + attrs[i++] = NSOpenGLPFAStencilSize; attrs[i++] = 8; + if (_sapp.sample_count > 1) { + attrs[i++] = NSOpenGLPFAMultisample; + attrs[i++] = NSOpenGLPFASampleBuffers; attrs[i++] = 1; + attrs[i++] = NSOpenGLPFASamples; attrs[i++] = (NSOpenGLPixelFormatAttribute)_sapp.sample_count; + } + else { + attrs[i++] = NSOpenGLPFASampleBuffers; attrs[i++] = 0; + } + attrs[i++] = 0; + NSOpenGLPixelFormat* glpixelformat_obj = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs]; + SOKOL_ASSERT(glpixelformat_obj != nil); + + _sapp.macos.view = [[_sapp_macos_view alloc] + initWithFrame:window_rect + pixelFormat:glpixelformat_obj]; + _SAPP_OBJC_RELEASE(glpixelformat_obj); + [_sapp.macos.view updateTrackingAreas]; + if (_sapp.desc.high_dpi) { + [_sapp.macos.view setWantsBestResolutionOpenGLSurface:YES]; + } + else { + [_sapp.macos.view setWantsBestResolutionOpenGLSurface:NO]; + } + + _sapp.macos.window.contentView = _sapp.macos.view; + [_sapp.macos.window makeFirstResponder:_sapp.macos.view]; + + NSTimer* timer_obj = [NSTimer timerWithTimeInterval:0.001 + target:_sapp.macos.view + selector:@selector(timerFired:) + userInfo:nil + repeats:YES]; + [[NSRunLoop currentRunLoop] addTimer:timer_obj forMode:NSDefaultRunLoopMode]; + timer_obj = nil; + #endif + _sapp.valid = true; + // __v_ + if (!_sapp.__v_native_render) { + if (_sapp.fullscreen) { + // on GL, this already toggles a rendered frame, so set the valid flag before + [_sapp.macos.window toggleFullScreen:self]; + } + else { + [_sapp.macos.window center]; + } + } + // __v C + /////////////////////////////////////////////////////// + // Create a child view for native rendering + if (_sapp.__v_native_render) { + + CGRect wRect = _sapp.macos.window.frame; + NSView *contentView =_sapp.macos.window.contentView; + CGRect cRect = contentView.frame; + + CGRect rect = CGRectMake(wRect.origin.x, wRect.origin.y, cRect.size.width, cRect.size.height); + NSWindow *overlayWindow = [[NSWindow alloc]initWithContentRect:rect + styleMask:NSBorderlessWindowMask + backing:NSBackingStoreBuffered + defer:NO]; + //overlayWindow.backgroundColor = [NSColor whiteColor]; + + //overlayWindow.backgroundColor = [[NSColor whiteColor] colorWithAlphaComponent:0]; + [overlayWindow setOpaque:YES]; + [_sapp.macos.window setIgnoresMouseEvents:NO]; + g_view = [[MyView2 alloc] init]; + overlayWindow.contentView = g_view; + + [ contentView addSubview:g_view]; +//[ _sapp.macos.window addChildWindow:overlayWindow ordered:NSWindowAbove]; + [_sapp.macos.window center]; + +} + ////////////////////////////////// + + [_sapp.macos.window makeKeyAndOrderFront:nil]; + _sapp_macos_update_dimensions(); +// [NSEvent setMouseCoalescingEnabled:NO]; +} + +- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)sender { + _SOKOL_UNUSED(sender); + return YES; +} + +- (void)applicationWillTerminate:(NSNotification*)notification { + _SOKOL_UNUSED(notification); + _sapp_call_cleanup(); + _sapp_macos_discard_state(); + _sapp_discard_state(); +} +@end + +@implementation _sapp_macos_window_delegate +- (BOOL)windowShouldClose:(id)sender { + _SOKOL_UNUSED(sender); + /* only give user-code a chance to intervene when sapp_quit() wasn't already called */ + if (!_sapp.quit_ordered) { + /* if window should be closed and event handling is enabled, give user code + a chance to intervene via sapp_cancel_quit() + */ + _sapp.quit_requested = true; + _sapp_macos_app_event(SAPP_EVENTTYPE_QUIT_REQUESTED); + /* user code hasn't intervened, quit the app */ + if (_sapp.quit_requested) { + _sapp.quit_ordered = true; + } + } + if (_sapp.quit_ordered) { + return YES; + } + else { + return NO; + } +} + +- (void)windowDidResize:(NSNotification*)notification { + _SOKOL_UNUSED(notification); + _sapp_macos_update_dimensions(); + if (!_sapp.first_frame) { + _sapp_macos_app_event(SAPP_EVENTTYPE_RESIZED); + } +} + +- (void)windowDidMiniaturize:(NSNotification*)notification { + _SOKOL_UNUSED(notification); + _sapp_macos_app_event(SAPP_EVENTTYPE_ICONIFIED); +} + +- (void)windowDidDeminiaturize:(NSNotification*)notification { + _SOKOL_UNUSED(notification); + _sapp_macos_app_event(SAPP_EVENTTYPE_RESTORED); +} + +- (void)windowDidEnterFullScreen:(NSNotification*)notification { + _SOKOL_UNUSED(notification); + _sapp.fullscreen = true; +} + +- (void)windowDidExitFullScreen:(NSNotification*)notification { + _SOKOL_UNUSED(notification); + _sapp.fullscreen = false; +} +@end + +@implementation _sapp_macos_window + +// __v_ +- (BOOL)canBecomeKeyWindow { return YES; } // needed for NSWindowStyleMaskBorderless +- (BOOL)canBecomeMainWindow { return YES; } +// __v_ + +- (instancetype)initWithContentRect:(NSRect)contentRect + styleMask:(NSWindowStyleMask)style + backing:(NSBackingStoreType)backingStoreType + defer:(BOOL)flag { + if (self = [super initWithContentRect:contentRect styleMask:style backing:backingStoreType defer:flag]) { + #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101300 + [self registerForDraggedTypes:[NSArray arrayWithObject:NSPasteboardTypeFileURL]]; + #endif + } + return self; +} + +- (NSDragOperation)draggingEntered:(id)sender { + return NSDragOperationCopy; +} + +- (NSDragOperation)draggingUpdated:(id)sender { + return NSDragOperationCopy; +} + +- (BOOL)performDragOperation:(id)sender { + #if __MAC_OS_X_VERSION_MAX_ALLOWED >= 101300 + NSPasteboard *pboard = [sender draggingPasteboard]; + if ([pboard.types containsObject:NSPasteboardTypeFileURL]) { + _sapp_clear_drop_buffer(); + _sapp.drop.num_files = ((int)pboard.pasteboardItems.count > _sapp.drop.max_files) ? _sapp.drop.max_files : pboard.pasteboardItems.count; + bool drop_failed = false; + for (int i = 0; i < _sapp.drop.num_files; i++) { + NSURL *fileUrl = [NSURL fileURLWithPath:[pboard.pasteboardItems[(NSUInteger)i] stringForType:NSPasteboardTypeFileURL]]; + if (!_sapp_strcpy(fileUrl.standardizedURL.path.UTF8String, _sapp_dropped_file_path_ptr(i), _sapp.drop.max_path_length)) { + SOKOL_LOG("sokol_app.h: dropped file path too long (sapp_desc.max_dropped_file_path_length)\n"); + drop_failed = true; + break; + } + } + if (!drop_failed) { + if (_sapp_events_enabled()) { + _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED); + _sapp_call_event(&_sapp.event); + } + } + else { + _sapp_clear_drop_buffer(); + _sapp.drop.num_files = 0; + } + return YES; + } + #endif + return NO; +} +@end + +@implementation _sapp_macos_view +#if defined(SOKOL_GLCORE33) +/* NOTE: this is a hack/fix when the initial window size has been clipped by + macOS because it didn't fit on the screen, in that case the + frame size of the window is reported wrong if low-dpi rendering + was requested (instead the high-dpi dimensions are returned) + until the window is resized for the first time. + + Hooking into reshape and getting the frame dimensions seems to report + the correct dimensions. +*/ +- (void)reshape { + _sapp_macos_update_dimensions(); + [super reshape]; +} +- (void)timerFired:(id)sender { + _SOKOL_UNUSED(sender); + [self setNeedsDisplay:YES]; +} +- (void)prepareOpenGL { + [super prepareOpenGL]; + GLint swapInt = 1; + NSOpenGLContext* ctx = [_sapp.macos.view openGLContext]; + [ctx setValues:&swapInt forParameter:NSOpenGLContextParameterSwapInterval]; + [ctx makeCurrentContext]; +} +#endif + +_SOKOL_PRIVATE void _sapp_macos_poll_input_events() { + /* + + NOTE: late event polling temporarily out-commented to check if this + causes infrequent and almost impossible to reproduce probelms with the + window close events, see: + https://github.com/floooh/sokol/pull/483#issuecomment-805148815 + + + const NSEventMask mask = NSEventMaskLeftMouseDown | + NSEventMaskLeftMouseUp| + NSEventMaskRightMouseDown | + NSEventMaskRightMouseUp | + NSEventMaskMouseMoved | + NSEventMaskLeftMouseDragged | + NSEventMaskRightMouseDragged | + NSEventMaskMouseEntered | + NSEventMaskMouseExited | + NSEventMaskKeyDown | + NSEventMaskKeyUp | + NSEventMaskCursorUpdate | + NSEventMaskScrollWheel | + NSEventMaskTabletPoint | + NSEventMaskTabletProximity | + NSEventMaskOtherMouseDown | + NSEventMaskOtherMouseUp | + NSEventMaskOtherMouseDragged | + NSEventMaskPressure | + NSEventMaskDirectTouch; + @autoreleasepool { + for (;;) { + // NOTE: using NSDefaultRunLoopMode here causes stuttering in the GL backend, + // see: https://github.com/floooh/sokol/issues/486 + NSEvent* event = [NSApp nextEventMatchingMask:mask untilDate:nil inMode:NSEventTrackingRunLoopMode dequeue:YES]; + if (event == nil) { + break; + } + [NSApp sendEvent:event]; + } + } + */ +} + +- (void)drawRect:(NSRect)rect { + _SOKOL_UNUSED(rect); + /* Catch any last-moment input events */ + _sapp_macos_poll_input_events(); + _sapp_macos_frame(); + #if !defined(SOKOL_METAL) + [[_sapp.macos.view openGLContext] flushBuffer]; + #endif +} + +- (BOOL)isOpaque { + return YES; +} +- (BOOL)canBecomeKeyView { + return YES; +} +- (BOOL)acceptsFirstResponder { + return YES; +} +- (BOOL)acceptsFirstMouse:(NSEvent *)event { + return YES; +} + + +- (void)updateTrackingAreas { + if (_sapp.macos.tracking_area != nil) { + [self removeTrackingArea:_sapp.macos.tracking_area]; + _SAPP_OBJC_RELEASE(_sapp.macos.tracking_area); + } + const NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited | + NSTrackingActiveInKeyWindow | + NSTrackingEnabledDuringMouseDrag | + NSTrackingCursorUpdate | + NSTrackingInVisibleRect | + NSTrackingAssumeInside; + _sapp.macos.tracking_area = [[NSTrackingArea alloc] initWithRect:[self bounds] options:options owner:self userInfo:nil]; + [self addTrackingArea:_sapp.macos.tracking_area]; + [super updateTrackingAreas]; +} +- (void)mouseEntered:(NSEvent*)event { + _sapp_macos_update_mouse(event); + /* don't send mouse enter/leave while dragging (so that it behaves the same as + on Windows while SetCapture is active + */ + if (0 == _sapp.macos.mouse_buttons) { + _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mod(event.modifierFlags)); + } +} +- (void)mouseExited:(NSEvent*)event { + _sapp_macos_update_mouse(event); + if (0 == _sapp.macos.mouse_buttons) { + _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mod(event.modifierFlags)); + } +} +- (void)mouseDown:(NSEvent*)event { + _sapp_macos_update_mouse(event); + _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_LEFT, _sapp_macos_mod(event.modifierFlags)); + _sapp.macos.mouse_buttons |= (1< 0.0f) || (_sapp_absf(dy) > 0.0f)) { + _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL); + _sapp.event.modifiers = _sapp_macos_mod(event.modifierFlags); + _sapp.event.scroll_x = dx; + _sapp.event.scroll_y = dy; + _sapp_call_event(&_sapp.event); + } + } +} +- (void)keyDown:(NSEvent*)event { + if (_sapp_events_enabled()) { + const uint32_t mods = _sapp_macos_mod(event.modifierFlags); + /* NOTE: macOS doesn't send keyUp events while the Cmd key is pressed, + as a workaround, to prevent key presses from sticking we'll send + a keyup event following right after the keydown if SUPER is also pressed + */ + const sapp_keycode key_code = _sapp_translate_key(event.keyCode); + _sapp_macos_key_event(SAPP_EVENTTYPE_KEY_DOWN, key_code, event.isARepeat, mods); + if (0 != (mods & SAPP_MODIFIER_SUPER)) { + _sapp_macos_key_event(SAPP_EVENTTYPE_KEY_UP, key_code, event.isARepeat, mods); + } + const NSString* chars = event.characters; + const NSUInteger len = chars.length; + if (len > 0) { + _sapp_init_event(SAPP_EVENTTYPE_CHAR); + _sapp.event.modifiers = mods; + for (NSUInteger i = 0; i < len; i++) { + const unichar codepoint = [chars characterAtIndex:i]; + if ((codepoint & 0xFF00) == 0xF700) { + continue; + } + _sapp.event.char_code = codepoint; + _sapp.event.key_repeat = event.isARepeat; + _sapp_call_event(&_sapp.event); + } + } + /* if this is a Cmd+V (paste), also send a CLIPBOARD_PASTE event */ + if (_sapp.clipboard.enabled && (mods == SAPP_MODIFIER_SUPER) && (key_code == SAPP_KEYCODE_V)) { + _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED); + _sapp_call_event(&_sapp.event); + } + } +} +- (void)keyUp:(NSEvent*)event { + _sapp_macos_key_event(SAPP_EVENTTYPE_KEY_UP, + _sapp_translate_key(event.keyCode), + event.isARepeat, + _sapp_macos_mod(event.modifierFlags)); +} +- (void)flagsChanged:(NSEvent*)event { + const uint32_t old_f = _sapp.macos.flags_changed_store; + const uint32_t new_f = event.modifierFlags; + _sapp.macos.flags_changed_store = new_f; + sapp_keycode key_code = SAPP_KEYCODE_INVALID; + bool down = false; + if ((new_f ^ old_f) & NSEventModifierFlagShift) { + key_code = SAPP_KEYCODE_LEFT_SHIFT; + down = 0 != (new_f & NSEventModifierFlagShift); + } + if ((new_f ^ old_f) & NSEventModifierFlagControl) { + key_code = SAPP_KEYCODE_LEFT_CONTROL; + down = 0 != (new_f & NSEventModifierFlagControl); + } + if ((new_f ^ old_f) & NSEventModifierFlagOption) { + key_code = SAPP_KEYCODE_LEFT_ALT; + down = 0 != (new_f & NSEventModifierFlagOption); + } + if ((new_f ^ old_f) & NSEventModifierFlagCommand) { + key_code = SAPP_KEYCODE_LEFT_SUPER; + down = 0 != (new_f & NSEventModifierFlagCommand); + } + if (key_code != SAPP_KEYCODE_INVALID) { + _sapp_macos_key_event(down ? SAPP_EVENTTYPE_KEY_DOWN : SAPP_EVENTTYPE_KEY_UP, + key_code, + false, + _sapp_macos_mod(event.modifierFlags)); + } +} +- (void)cursorUpdate:(NSEvent*)event { + _SOKOL_UNUSED(event); + if (_sapp.desc.user_cursor) { + _sapp_macos_app_event(SAPP_EVENTTYPE_UPDATE_CURSOR); + } +} +@end + +#endif /* MacOS */ + +/*== iOS =====================================================================*/ +#if defined(_SAPP_IOS) + +_SOKOL_PRIVATE void _sapp_ios_discard_state(void) { + // NOTE: it's safe to call [release] on a nil object + _SAPP_OBJC_RELEASE(_sapp.ios.textfield_dlg); + _SAPP_OBJC_RELEASE(_sapp.ios.textfield); + #if defined(SOKOL_METAL) + _SAPP_OBJC_RELEASE(_sapp.ios.view_ctrl); + _SAPP_OBJC_RELEASE(_sapp.ios.mtl_device); + #else + _SAPP_OBJC_RELEASE(_sapp.ios.view_ctrl); + _SAPP_OBJC_RELEASE(_sapp.ios.eagl_ctx); + #endif + _SAPP_OBJC_RELEASE(_sapp.ios.view); + _SAPP_OBJC_RELEASE(_sapp.ios.window); +} + +_SOKOL_PRIVATE void _sapp_ios_run(const sapp_desc* desc) { + _sapp_init_state(desc); + static int argc = 1; + static char* argv[] = { (char*)"sokol_app" }; + UIApplicationMain(argc, argv, nil, NSStringFromClass([_sapp_app_delegate class])); +} + +/* iOS entry function */ +#if !defined(SOKOL_NO_ENTRY) +int main(int argc, char* argv[]) { + sapp_desc desc = sokol_main(argc, argv); + _sapp_ios_run(&desc); + return 0; +} +#endif /* SOKOL_NO_ENTRY */ + +_SOKOL_PRIVATE void _sapp_ios_app_event(sapp_event_type type) { + if (_sapp_events_enabled()) { + _sapp_init_event(type); + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE void _sapp_ios_touch_event(sapp_event_type type, NSSet* touches, UIEvent* event) { + if (_sapp_events_enabled()) { + _sapp_init_event(type); + NSEnumerator* enumerator = event.allTouches.objectEnumerator; + UITouch* ios_touch; + while ((ios_touch = [enumerator nextObject])) { + if ((_sapp.event.num_touches + 1) < SAPP_MAX_TOUCHPOINTS) { + CGPoint ios_pos = [ios_touch locationInView:_sapp.ios.view]; + sapp_touchpoint* cur_point = &_sapp.event.touches[_sapp.event.num_touches++]; + cur_point->identifier = (uintptr_t) ios_touch; + cur_point->pos_x = ios_pos.x * _sapp.dpi_scale; + cur_point->pos_y = ios_pos.y * _sapp.dpi_scale; + cur_point->changed = [touches containsObject:ios_touch]; + } + } + if (_sapp.event.num_touches > 0) { + _sapp_call_event(&_sapp.event); + } + } +} + +_SOKOL_PRIVATE void _sapp_ios_update_dimensions(void) { + CGRect screen_rect = UIScreen.mainScreen.bounds; + _sapp.framebuffer_width = (int)(screen_rect.size.width * _sapp.dpi_scale); + _sapp.framebuffer_height = (int)(screen_rect.size.height * _sapp.dpi_scale); + _sapp.window_width = (int)screen_rect.size.width; + _sapp.window_height = (int)screen_rect.size.height; + int cur_fb_width, cur_fb_height; + #if defined(SOKOL_METAL) + const CGSize fb_size = _sapp.ios.view.drawableSize; + cur_fb_width = (int) fb_size.width; + cur_fb_height = (int) fb_size.height; + #else + cur_fb_width = (int) _sapp.ios.view.drawableWidth; + cur_fb_height = (int) _sapp.ios.view.drawableHeight; + #endif + const bool dim_changed = (_sapp.framebuffer_width != cur_fb_width) || + (_sapp.framebuffer_height != cur_fb_height); + if (dim_changed) { + #if defined(SOKOL_METAL) + const CGSize drawable_size = { (CGFloat) _sapp.framebuffer_width, (CGFloat) _sapp.framebuffer_height }; + _sapp.ios.view.drawableSize = drawable_size; + #else + // nothing to do here, GLKView correctly respects the view's contentScaleFactor + #endif + if (!_sapp.first_frame) { + _sapp_ios_app_event(SAPP_EVENTTYPE_RESIZED); + } + } +} + +_SOKOL_PRIVATE void _sapp_ios_frame(void) { + _sapp_ios_update_dimensions(); + _sapp_frame(); +} + +_SOKOL_PRIVATE void _sapp_ios_show_keyboard(bool shown) { + /* if not happened yet, create an invisible text field */ + if (nil == _sapp.ios.textfield) { + _sapp.ios.textfield_dlg = [[_sapp_textfield_dlg alloc] init]; + _sapp.ios.textfield = [[UITextField alloc] initWithFrame:CGRectMake(10, 10, 100, 50)]; + _sapp.ios.textfield.keyboardType = UIKeyboardTypeDefault; + _sapp.ios.textfield.returnKeyType = UIReturnKeyDefault; + _sapp.ios.textfield.autocapitalizationType = UITextAutocapitalizationTypeNone; + _sapp.ios.textfield.autocorrectionType = UITextAutocorrectionTypeNo; + _sapp.ios.textfield.spellCheckingType = UITextSpellCheckingTypeNo; + _sapp.ios.textfield.hidden = YES; + _sapp.ios.textfield.text = @"x"; + _sapp.ios.textfield.delegate = _sapp.ios.textfield_dlg; + [_sapp.ios.view_ctrl.view addSubview:_sapp.ios.textfield]; + + [[NSNotificationCenter defaultCenter] addObserver:_sapp.ios.textfield_dlg + selector:@selector(keyboardWasShown:) + name:UIKeyboardDidShowNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:_sapp.ios.textfield_dlg + selector:@selector(keyboardWillBeHidden:) + name:UIKeyboardWillHideNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:_sapp.ios.textfield_dlg + selector:@selector(keyboardDidChangeFrame:) + name:UIKeyboardDidChangeFrameNotification object:nil]; + } + if (shown) { + /* setting the text field as first responder brings up the onscreen keyboard */ + [_sapp.ios.textfield becomeFirstResponder]; + } + else { + [_sapp.ios.textfield resignFirstResponder]; + } +} + +@implementation _sapp_app_delegate +- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { + CGRect screen_rect = UIScreen.mainScreen.bounds; + _sapp.ios.window = [[UIWindow alloc] initWithFrame:screen_rect]; + _sapp.window_width = screen_rect.size.width; + _sapp.window_height = screen_rect.size.height; + if (_sapp.desc.high_dpi) { + _sapp.dpi_scale = (float) UIScreen.mainScreen.nativeScale; + } + else { + _sapp.dpi_scale = 1.0f; + } + _sapp.framebuffer_width = _sapp.window_width * _sapp.dpi_scale; + _sapp.framebuffer_height = _sapp.window_height * _sapp.dpi_scale; + #if defined(SOKOL_METAL) + _sapp.ios.mtl_device = MTLCreateSystemDefaultDevice(); + _sapp.ios.view = [[_sapp_ios_view alloc] init]; + _sapp.ios.view.preferredFramesPerSecond = 60 / _sapp.swap_interval; + _sapp.ios.view.device = _sapp.ios.mtl_device; + _sapp.ios.view.colorPixelFormat = MTLPixelFormatBGRA8Unorm; + _sapp.ios.view.depthStencilPixelFormat = MTLPixelFormatDepth32Float_Stencil8; + _sapp.ios.view.sampleCount = (NSUInteger)_sapp.sample_count; + /* NOTE: iOS MTKView seems to ignore thew view's contentScaleFactor + and automatically renders at Retina resolution. We'll disable + autoResize and instead do the resizing in _sapp_ios_update_dimensions() + */ + _sapp.ios.view.autoResizeDrawable = false; + _sapp.ios.view.userInteractionEnabled = YES; + _sapp.ios.view.multipleTouchEnabled = YES; + _sapp.ios.view_ctrl = [[UIViewController alloc] init]; + _sapp.ios.view_ctrl.modalPresentationStyle = UIModalPresentationFullScreen; + _sapp.ios.view_ctrl.view = _sapp.ios.view; + _sapp.ios.window.rootViewController = _sapp.ios.view_ctrl; + #else + if (_sapp.desc.gl_force_gles2) { + _sapp.ios.eagl_ctx = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; + _sapp.gles2_fallback = true; + } + else { + _sapp.ios.eagl_ctx = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; + if (_sapp.ios.eagl_ctx == nil) { + _sapp.ios.eagl_ctx = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; + _sapp.gles2_fallback = true; + } + } + _sapp.ios.view = [[_sapp_ios_view alloc] initWithFrame:screen_rect]; + _sapp.ios.view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888; + _sapp.ios.view.drawableDepthFormat = GLKViewDrawableDepthFormat24; + _sapp.ios.view.drawableStencilFormat = GLKViewDrawableStencilFormatNone; + GLKViewDrawableMultisample msaa = _sapp.sample_count > 1 ? GLKViewDrawableMultisample4X : GLKViewDrawableMultisampleNone; + _sapp.ios.view.drawableMultisample = msaa; + _sapp.ios.view.context = _sapp.ios.eagl_ctx; + _sapp.ios.view.enableSetNeedsDisplay = NO; + _sapp.ios.view.userInteractionEnabled = YES; + _sapp.ios.view.multipleTouchEnabled = YES; + // on GLKView, contentScaleFactor appears to work just fine! + if (_sapp.desc.high_dpi) { + _sapp.ios.view.contentScaleFactor = 2.0; + } + else { + _sapp.ios.view.contentScaleFactor = 1.0; + } + _sapp.ios.view_ctrl = [[GLKViewController alloc] init]; + _sapp.ios.view_ctrl.view = _sapp.ios.view; + _sapp.ios.view_ctrl.preferredFramesPerSecond = 60 / _sapp.swap_interval; + _sapp.ios.window.rootViewController = _sapp.ios.view_ctrl; + #endif + [_sapp.ios.window makeKeyAndVisible]; + + _sapp.valid = true; + return YES; +} + +- (void)applicationWillResignActive:(UIApplication *)application { + if (!_sapp.ios.suspended) { + _sapp.ios.suspended = true; + _sapp_ios_app_event(SAPP_EVENTTYPE_SUSPENDED); + } +} + +- (void)applicationDidBecomeActive:(UIApplication *)application { + if (_sapp.ios.suspended) { + _sapp.ios.suspended = false; + _sapp_ios_app_event(SAPP_EVENTTYPE_RESUMED); + } +} + +/* NOTE: this method will rarely ever be called, iOS application + which are terminated by the user are usually killed via signal 9 + by the operating system. +*/ +- (void)applicationWillTerminate:(UIApplication *)application { + _SOKOL_UNUSED(application); + _sapp_call_cleanup(); + _sapp_ios_discard_state(); + _sapp_discard_state(); +} +@end + +@implementation _sapp_textfield_dlg +- (void)keyboardWasShown:(NSNotification*)notif { + _sapp.onscreen_keyboard_shown = true; + /* query the keyboard's size, and modify the content view's size */ + if (_sapp.desc.ios_keyboard_resizes_canvas) { + NSDictionary* info = notif.userInfo; + CGFloat kbd_h = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height; + CGRect view_frame = UIScreen.mainScreen.bounds; + view_frame.size.height -= kbd_h; + _sapp.ios.view.frame = view_frame; + } +} +- (void)keyboardWillBeHidden:(NSNotification*)notif { + _sapp.onscreen_keyboard_shown = false; + if (_sapp.desc.ios_keyboard_resizes_canvas) { + _sapp.ios.view.frame = UIScreen.mainScreen.bounds; + } +} +- (void)keyboardDidChangeFrame:(NSNotification*)notif { + /* this is for the case when the screen rotation changes while the keyboard is open */ + if (_sapp.onscreen_keyboard_shown && _sapp.desc.ios_keyboard_resizes_canvas) { + NSDictionary* info = notif.userInfo; + CGFloat kbd_h = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue].size.height; + CGRect view_frame = UIScreen.mainScreen.bounds; + view_frame.size.height -= kbd_h; + _sapp.ios.view.frame = view_frame; + } +} +- (BOOL)textField:(UITextField*)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString*)string { + if (_sapp_events_enabled()) { + const NSUInteger len = string.length; + if (len > 0) { + for (NSUInteger i = 0; i < len; i++) { + unichar c = [string characterAtIndex:i]; + if (c >= 32) { + /* ignore surrogates for now */ + if ((c < 0xD800) || (c > 0xDFFF)) { + _sapp_init_event(SAPP_EVENTTYPE_CHAR); + _sapp.event.char_code = c; + _sapp_call_event(&_sapp.event); + } + } + if (c <= 32) { + sapp_keycode k = SAPP_KEYCODE_INVALID; + switch (c) { + case 10: k = SAPP_KEYCODE_ENTER; break; + case 32: k = SAPP_KEYCODE_SPACE; break; + default: break; + } + if (k != SAPP_KEYCODE_INVALID) { + _sapp_init_event(SAPP_EVENTTYPE_KEY_DOWN); + _sapp.event.key_code = k; + _sapp_call_event(&_sapp.event); + _sapp_init_event(SAPP_EVENTTYPE_KEY_UP); + _sapp.event.key_code = k; + _sapp_call_event(&_sapp.event); + } + } + } + } + else { + /* this was a backspace */ + _sapp_init_event(SAPP_EVENTTYPE_KEY_DOWN); + _sapp.event.key_code = SAPP_KEYCODE_BACKSPACE; + _sapp_call_event(&_sapp.event); + _sapp_init_event(SAPP_EVENTTYPE_KEY_UP); + _sapp.event.key_code = SAPP_KEYCODE_BACKSPACE; + _sapp_call_event(&_sapp.event); + } + } + return NO; +} +@end + +@implementation _sapp_ios_view +- (void)drawRect:(CGRect)rect { + _SOKOL_UNUSED(rect); + _sapp_ios_frame(); +} +- (BOOL)isOpaque { + return YES; +} +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent*)event { + _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_BEGAN, touches, event); +} +- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent*)event { + _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_MOVED, touches, event); +} +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent*)event { + _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_ENDED, touches, event); +} +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent*)event { + _sapp_ios_touch_event(SAPP_EVENTTYPE_TOUCHES_CANCELLED, touches, event); +} +@end +#endif /* TARGET_OS_IPHONE */ + +#endif /* _SAPP_APPLE */ + +/*== EMSCRIPTEN ==============================================================*/ +#if defined(_SAPP_EMSCRIPTEN) + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*_sapp_html5_fetch_callback) (const sapp_html5_fetch_response*); + +/* this function is called from a JS event handler when the user hides + the onscreen keyboard pressing the 'dismiss keyboard key' +*/ +EMSCRIPTEN_KEEPALIVE void _sapp_emsc_notify_keyboard_hidden(void) { + _sapp.onscreen_keyboard_shown = false; +} + +EMSCRIPTEN_KEEPALIVE void _sapp_emsc_onpaste(const char* str) { + if (_sapp.clipboard.enabled) { + _sapp_strcpy(str, _sapp.clipboard.buffer, _sapp.clipboard.buf_size); + if (_sapp_events_enabled()) { + _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED); + _sapp_call_event(&_sapp.event); + } + } +} + +/* https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onbeforeunload */ +EMSCRIPTEN_KEEPALIVE int _sapp_html5_get_ask_leave_site(void) { + return _sapp.html5_ask_leave_site ? 1 : 0; +} + +EMSCRIPTEN_KEEPALIVE void _sapp_emsc_begin_drop(int num) { + if (!_sapp.drop.enabled) { + return; + } + if (num < 0) { + num = 0; + } + if (num > _sapp.drop.max_files) { + num = _sapp.drop.max_files; + } + _sapp.drop.num_files = num; + _sapp_clear_drop_buffer(); +} + +EMSCRIPTEN_KEEPALIVE void _sapp_emsc_drop(int i, const char* name) { + /* NOTE: name is only the filename part, not a path */ + if (!_sapp.drop.enabled) { + return; + } + if (0 == name) { + return; + } + SOKOL_ASSERT(_sapp.drop.num_files <= _sapp.drop.max_files); + if ((i < 0) || (i >= _sapp.drop.num_files)) { + return; + } + if (!_sapp_strcpy(name, _sapp_dropped_file_path_ptr(i), _sapp.drop.max_path_length)) { + SOKOL_LOG("sokol_app.h: dropped file path too long!\n"); + _sapp.drop.num_files = 0; + } +} + +EMSCRIPTEN_KEEPALIVE void _sapp_emsc_end_drop(int x, int y) { + if (!_sapp.drop.enabled) { + return; + } + if (0 == _sapp.drop.num_files) { + /* there was an error copying the filenames */ + _sapp_clear_drop_buffer(); + return; + + } + if (_sapp_events_enabled()) { + _sapp.mouse.x = (float)x * _sapp.dpi_scale; + _sapp.mouse.y = (float)y * _sapp.dpi_scale; + _sapp.mouse.dx = 0.0f; + _sapp.mouse.dy = 0.0f; + _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED); + _sapp_call_event(&_sapp.event); + } +} + +EMSCRIPTEN_KEEPALIVE void _sapp_emsc_invoke_fetch_cb(int index, int success, int error_code, _sapp_html5_fetch_callback callback, uint32_t fetched_size, void* buf_ptr, uint32_t buf_size, void* user_data) { + sapp_html5_fetch_response response; + memset(&response, 0, sizeof(response)); + response.succeeded = (0 != success); + response.error_code = (sapp_html5_fetch_error) error_code; + response.file_index = index; + response.fetched_size = fetched_size; + response.buffer_ptr = buf_ptr; + response.buffer_size = buf_size; + response.user_data = user_data; + callback(&response); +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/* Javascript helper functions for mobile virtual keyboard input */ +EM_JS(void, sapp_js_create_textfield, (void), { + var _sapp_inp = document.createElement("input"); + _sapp_inp.type = "text"; + _sapp_inp.id = "_sokol_app_input_element"; + _sapp_inp.autocapitalize = "none"; + _sapp_inp.addEventListener("focusout", function(_sapp_event) { + __sapp_emsc_notify_keyboard_hidden() + + }); + document.body.append(_sapp_inp); +}); + +EM_JS(void, sapp_js_focus_textfield, (void), { + document.getElementById("_sokol_app_input_element").focus(); +}); + +EM_JS(void, sapp_js_unfocus_textfield, (void), { + document.getElementById("_sokol_app_input_element").blur(); +}); + +EM_JS(void, sapp_js_add_beforeunload_listener, (void), { + Module.sokol_beforeunload = function(event) { + if (__sapp_html5_get_ask_leave_site() != 0) { + event.preventDefault(); + event.returnValue = ' '; + } + }; + window.addEventListener('beforeunload', Module.sokol_beforeunload); +}); + +EM_JS(void, sapp_js_remove_beforeunload_listener, (void), { + window.removeEventListener('beforeunload', Module.sokol_beforeunload); +}); + +EM_JS(void, sapp_js_add_clipboard_listener, (void), { + Module.sokol_paste = function(event) { + var pasted_str = event.clipboardData.getData('text'); + ccall('_sapp_emsc_onpaste', 'void', ['string'], [pasted_str]); + }; + window.addEventListener('paste', Module.sokol_paste); +}); + +EM_JS(void, sapp_js_remove_clipboard_listener, (void), { + window.removeEventListener('paste', Module.sokol_paste); +}); + +EM_JS(void, sapp_js_write_clipboard, (const char* c_str), { + var str = UTF8ToString(c_str); + var ta = document.createElement('textarea'); + ta.setAttribute('autocomplete', 'off'); + ta.setAttribute('autocorrect', 'off'); + ta.setAttribute('autocapitalize', 'off'); + ta.setAttribute('spellcheck', 'false'); + ta.style.left = -100 + 'px'; + ta.style.top = -100 + 'px'; + ta.style.height = 1; + ta.style.width = 1; + ta.value = str; + document.body.appendChild(ta); + ta.select(); + document.execCommand('copy'); + document.body.removeChild(ta); +}); + +_SOKOL_PRIVATE void _sapp_emsc_set_clipboard_string(const char* str) { + sapp_js_write_clipboard(str); +} + +EM_JS(void, sapp_js_add_dragndrop_listeners, (const char* canvas_name_cstr), { + Module.sokol_drop_files = []; + var canvas_name = UTF8ToString(canvas_name_cstr); + var canvas = document.getElementById(canvas_name); + Module.sokol_dragenter = function(event) { + event.stopPropagation(); + event.preventDefault(); + }; + Module.sokol_dragleave = function(event) { + event.stopPropagation(); + event.preventDefault(); + }; + Module.sokol_dragover = function(event) { + event.stopPropagation(); + event.preventDefault(); + }; + Module.sokol_drop = function(event) { + event.stopPropagation(); + event.preventDefault(); + var files = event.dataTransfer.files; + Module.sokol_dropped_files = files; + __sapp_emsc_begin_drop(files.length); + var i; + for (i = 0; i < files.length; i++) { + ccall('_sapp_emsc_drop', 'void', ['number', 'string'], [i, files[i].name]); + } + // FIXME? see computation of targetX/targetY in emscripten via getClientBoundingRect + __sapp_emsc_end_drop(event.clientX, event.clientY); + }; + canvas.addEventListener('dragenter', Module.sokol_dragenter, false); + canvas.addEventListener('dragleave', Module.sokol_dragleave, false); + canvas.addEventListener('dragover', Module.sokol_dragover, false); + canvas.addEventListener('drop', Module.sokol_drop, false); +}); + +EM_JS(uint32_t, sapp_js_dropped_file_size, (int index), { + if ((index < 0) || (index >= Module.sokol_dropped_files.length)) { + return 0; + } + else { + return Module.sokol_dropped_files[index].size; + } +}); + +EM_JS(void, sapp_js_fetch_dropped_file, (int index, _sapp_html5_fetch_callback callback, void* buf_ptr, uint32_t buf_size, void* user_data), { + var reader = new FileReader(); + reader.onload = function(loadEvent) { + var content = loadEvent.target.result; + if (content.byteLength > buf_size) { + // SAPP_HTML5_FETCH_ERROR_BUFFER_TOO_SMALL + __sapp_emsc_invoke_fetch_cb(index, 0, 1, callback, 0, buf_ptr, buf_size, user_data); + } + else { + HEAPU8.set(new Uint8Array(content), buf_ptr); + __sapp_emsc_invoke_fetch_cb(index, 1, 0, callback, content.byteLength, buf_ptr, buf_size, user_data); + } + }; + reader.onerror = function() { + // SAPP_HTML5_FETCH_ERROR_OTHER + __sapp_emsc_invoke_fetch_cb(index, 0, 2, callback, 0, buf_ptr, buf_size, user_data); + }; + reader.readAsArrayBuffer(Module.sokol_dropped_files[index]); +}); + +EM_JS(void, sapp_js_remove_dragndrop_listeners, (const char* canvas_name_cstr), { + var canvas_name = UTF8ToString(canvas_name_cstr); + var canvas = document.getElementById(canvas_name); + canvas.removeEventListener('dragenter', Module.sokol_dragenter); + canvas.removeEventListener('dragleave', Module.sokol_dragleave); + canvas.removeEventListener('dragover', Module.sokol_dragover); + canvas.removeEventListener('drop', Module.sokol_drop); +}); + +/* called from the emscripten event handler to update the keyboard visibility + state, this must happen from an JS input event handler, otherwise + the request will be ignored by the browser +*/ +_SOKOL_PRIVATE void _sapp_emsc_update_keyboard_state(void) { + if (_sapp.emsc.wants_show_keyboard) { + /* create input text field on demand */ + if (!_sapp.emsc.textfield_created) { + _sapp.emsc.textfield_created = true; + sapp_js_create_textfield(); + } + /* focus the text input field, this will bring up the keyboard */ + _sapp.onscreen_keyboard_shown = true; + _sapp.emsc.wants_show_keyboard = false; + sapp_js_focus_textfield(); + } + if (_sapp.emsc.wants_hide_keyboard) { + /* unfocus the text input field */ + if (_sapp.emsc.textfield_created) { + _sapp.onscreen_keyboard_shown = false; + _sapp.emsc.wants_hide_keyboard = false; + sapp_js_unfocus_textfield(); + } + } +} + +/* actually showing the onscreen keyboard must be initiated from a JS + input event handler, so we'll just keep track of the desired + state, and the actual state change will happen with the next input event +*/ +_SOKOL_PRIVATE void _sapp_emsc_show_keyboard(bool show) { + if (show) { + _sapp.emsc.wants_show_keyboard = true; + } + else { + _sapp.emsc.wants_hide_keyboard = true; + } +} + +EM_JS(void, sapp_js_pointer_init, (const char* c_str_target), { + // lookup and store canvas object by name + var target_str = UTF8ToString(c_str_target); + Module.sapp_emsc_target = document.getElementById(target_str); + if (!Module.sapp_emsc_target) { + console.log("sokol_app.h: invalid target:" + target_str); + } + if (!Module.sapp_emsc_target.requestPointerLock) { + console.log("sokol_app.h: target doesn't support requestPointerLock:" + target_str); + } +}); + +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_pointerlockchange_cb(int emsc_type, const EmscriptenPointerlockChangeEvent* emsc_event, void* user_data) { + _SOKOL_UNUSED(emsc_type); + _SOKOL_UNUSED(user_data); + _sapp.mouse.locked = emsc_event->isActive; + return EM_TRUE; +} + +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_pointerlockerror_cb(int emsc_type, const void* reserved, void* user_data) { + _SOKOL_UNUSED(emsc_type); + _SOKOL_UNUSED(reserved); + _SOKOL_UNUSED(user_data); + _sapp.mouse.locked = false; + _sapp.emsc.mouse_lock_requested = false; + return true; +} + +EM_JS(void, sapp_js_request_pointerlock, (void), { + if (Module.sapp_emsc_target) { + if (Module.sapp_emsc_target.requestPointerLock) { + Module.sapp_emsc_target.requestPointerLock(); + } + } +}); + +EM_JS(void, sapp_js_exit_pointerlock, (void), { + if (document.exitPointerLock) { + document.exitPointerLock(); + } +}); + +_SOKOL_PRIVATE void _sapp_emsc_lock_mouse(bool lock) { + if (lock) { + /* request mouse-lock during event handler invocation (see _sapp_emsc_update_mouse_lock_state) */ + _sapp.emsc.mouse_lock_requested = true; + } + else { + /* NOTE: the _sapp.mouse_locked state will be set in the pointerlockchange callback */ + _sapp.emsc.mouse_lock_requested = false; + sapp_js_exit_pointerlock(); + } +} + +/* called from inside event handlers to check if mouse lock had been requested, + and if yes, actually enter mouse lock. +*/ +_SOKOL_PRIVATE void _sapp_emsc_update_mouse_lock_state(void) { + if (_sapp.emsc.mouse_lock_requested) { + _sapp.emsc.mouse_lock_requested = false; + sapp_js_request_pointerlock(); + } +} + +#if defined(SOKOL_WGPU) +_SOKOL_PRIVATE void _sapp_emsc_wgpu_surfaces_create(void); +_SOKOL_PRIVATE void _sapp_emsc_wgpu_surfaces_discard(void); +#endif + +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_size_changed(int event_type, const EmscriptenUiEvent* ui_event, void* user_data) { + _SOKOL_UNUSED(event_type); + _SOKOL_UNUSED(user_data); + double w, h; + emscripten_get_element_css_size(_sapp.html5_canvas_selector, &w, &h); + /* The above method might report zero when toggling HTML5 fullscreen, + in that case use the window's inner width reported by the + emscripten event. This works ok when toggling *into* fullscreen + but doesn't properly restore the previous canvas size when switching + back from fullscreen. + + In general, due to the HTML5's fullscreen API's flaky nature it is + recommended to use 'soft fullscreen' (stretching the WebGL canvas + over the browser windows client rect) with a CSS definition like this: + + position: absolute; + top: 0px; + left: 0px; + margin: 0px; + border: 0; + width: 100%; + height: 100%; + overflow: hidden; + display: block; + */ + if (w < 1.0) { + w = ui_event->windowInnerWidth; + } + else { + _sapp.window_width = (int) w; + } + if (h < 1.0) { + h = ui_event->windowInnerHeight; + } + else { + _sapp.window_height = (int) h; + } + if (_sapp.desc.high_dpi) { + _sapp.dpi_scale = emscripten_get_device_pixel_ratio(); + } + _sapp.framebuffer_width = (int) (w * _sapp.dpi_scale); + _sapp.framebuffer_height = (int) (h * _sapp.dpi_scale); + SOKOL_ASSERT((_sapp.framebuffer_width > 0) && (_sapp.framebuffer_height > 0)); + emscripten_set_canvas_element_size(_sapp.html5_canvas_selector, _sapp.framebuffer_width, _sapp.framebuffer_height); + #if defined(SOKOL_WGPU) + /* on WebGPU: recreate size-dependent rendering surfaces */ + _sapp_emsc_wgpu_surfaces_discard(); + _sapp_emsc_wgpu_surfaces_create(); + #endif + if (_sapp_events_enabled()) { + _sapp_init_event(SAPP_EVENTTYPE_RESIZED); + _sapp_call_event(&_sapp.event); + } + return true; +} + +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_mouse_cb(int emsc_type, const EmscriptenMouseEvent* emsc_event, void* user_data) { + _SOKOL_UNUSED(user_data); + if (_sapp.mouse.locked) { + _sapp.mouse.dx = (float) emsc_event->movementX; + _sapp.mouse.dy = (float) emsc_event->movementY; + } + else { + float new_x = emsc_event->targetX * _sapp.dpi_scale; + float new_y = emsc_event->targetY * _sapp.dpi_scale; + if (_sapp.mouse.pos_valid) { + _sapp.mouse.dx = new_x - _sapp.mouse.x; + _sapp.mouse.dy = new_y - _sapp.mouse.y; + } + _sapp.mouse.x = new_x; + _sapp.mouse.y = new_y; + _sapp.mouse.pos_valid = true; + } + if (_sapp_events_enabled() && (emsc_event->button >= 0) && (emsc_event->button < SAPP_MAX_MOUSEBUTTONS)) { + sapp_event_type type; + bool is_button_event = false; + switch (emsc_type) { + case EMSCRIPTEN_EVENT_MOUSEDOWN: + type = SAPP_EVENTTYPE_MOUSE_DOWN; + is_button_event = true; + break; + case EMSCRIPTEN_EVENT_MOUSEUP: + type = SAPP_EVENTTYPE_MOUSE_UP; + is_button_event = true; + break; + case EMSCRIPTEN_EVENT_MOUSEMOVE: + type = SAPP_EVENTTYPE_MOUSE_MOVE; + break; + case EMSCRIPTEN_EVENT_MOUSEENTER: + type = SAPP_EVENTTYPE_MOUSE_ENTER; + break; + case EMSCRIPTEN_EVENT_MOUSELEAVE: + type = SAPP_EVENTTYPE_MOUSE_LEAVE; + break; + default: + type = SAPP_EVENTTYPE_INVALID; + break; + } + if (type != SAPP_EVENTTYPE_INVALID) { + _sapp_init_event(type); + if (emsc_event->ctrlKey) { + _sapp.event.modifiers |= SAPP_MODIFIER_CTRL; + } + if (emsc_event->shiftKey) { + _sapp.event.modifiers |= SAPP_MODIFIER_SHIFT; + } + if (emsc_event->altKey) { + _sapp.event.modifiers |= SAPP_MODIFIER_ALT; + } + if (emsc_event->metaKey) { + _sapp.event.modifiers |= SAPP_MODIFIER_SUPER; + } + if (is_button_event) { + switch (emsc_event->button) { + case 0: _sapp.event.mouse_button = SAPP_MOUSEBUTTON_LEFT; break; + case 1: _sapp.event.mouse_button = SAPP_MOUSEBUTTON_MIDDLE; break; + case 2: _sapp.event.mouse_button = SAPP_MOUSEBUTTON_RIGHT; break; + default: _sapp.event.mouse_button = (sapp_mousebutton)emsc_event->button; break; + } + } + else { + _sapp.event.mouse_button = SAPP_MOUSEBUTTON_INVALID; + } + _sapp_call_event(&_sapp.event); + } + /* mouse lock can only be activated in mouse button events (not in move, enter or leave) */ + if (is_button_event) { + _sapp_emsc_update_mouse_lock_state(); + } + } + _sapp_emsc_update_keyboard_state(); + return true; +} + +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_wheel_cb(int emsc_type, const EmscriptenWheelEvent* emsc_event, void* user_data) { + _SOKOL_UNUSED(emsc_type); + _SOKOL_UNUSED(user_data); + if (_sapp_events_enabled()) { + _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL); + if (emsc_event->mouse.ctrlKey) { + _sapp.event.modifiers |= SAPP_MODIFIER_CTRL; + } + if (emsc_event->mouse.shiftKey) { + _sapp.event.modifiers |= SAPP_MODIFIER_SHIFT; + } + if (emsc_event->mouse.altKey) { + _sapp.event.modifiers |= SAPP_MODIFIER_ALT; + } + if (emsc_event->mouse.metaKey) { + _sapp.event.modifiers |= SAPP_MODIFIER_SUPER; + } + /* see https://github.com/floooh/sokol/issues/339 */ + float scale; + switch (emsc_event->deltaMode) { + case DOM_DELTA_PIXEL: scale = -0.04f; break; + case DOM_DELTA_LINE: scale = -1.33f; break; + case DOM_DELTA_PAGE: scale = -10.0f; break; // FIXME: this is a guess + default: scale = -0.1f; break; // shouldn't happen + } + _sapp.event.scroll_x = scale * (float)emsc_event->deltaX; + _sapp.event.scroll_y = scale * (float)emsc_event->deltaY; + _sapp_call_event(&_sapp.event); + } + _sapp_emsc_update_keyboard_state(); + _sapp_emsc_update_mouse_lock_state(); + return true; +} + +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_key_cb(int emsc_type, const EmscriptenKeyboardEvent* emsc_event, void* user_data) { + _SOKOL_UNUSED(user_data); + bool retval = true; + if (_sapp_events_enabled()) { + sapp_event_type type; + switch (emsc_type) { + case EMSCRIPTEN_EVENT_KEYDOWN: + type = SAPP_EVENTTYPE_KEY_DOWN; + break; + case EMSCRIPTEN_EVENT_KEYUP: + type = SAPP_EVENTTYPE_KEY_UP; + break; + case EMSCRIPTEN_EVENT_KEYPRESS: + type = SAPP_EVENTTYPE_CHAR; + break; + default: + type = SAPP_EVENTTYPE_INVALID; + break; + } + if (type != SAPP_EVENTTYPE_INVALID) { + bool send_keyup_followup = false; + _sapp_init_event(type); + _sapp.event.key_repeat = emsc_event->repeat; + if (emsc_event->ctrlKey) { + _sapp.event.modifiers |= SAPP_MODIFIER_CTRL; + } + if (emsc_event->shiftKey) { + _sapp.event.modifiers |= SAPP_MODIFIER_SHIFT; + } + if (emsc_event->altKey) { + _sapp.event.modifiers |= SAPP_MODIFIER_ALT; + } + if (emsc_event->metaKey) { + _sapp.event.modifiers |= SAPP_MODIFIER_SUPER; + } + if (type == SAPP_EVENTTYPE_CHAR) { + _sapp.event.char_code = emsc_event->charCode; + /* workaround to make Cmd+V work on Safari */ + if ((emsc_event->metaKey) && (emsc_event->charCode == 118)) { + retval = false; + } + } + else { + _sapp.event.key_code = _sapp_translate_key((int)emsc_event->keyCode); + /* Special hack for macOS: if the Super key is pressed, macOS doesn't + send keyUp events. As a workaround, to prevent keys from + "sticking", we'll send a keyup event following a keydown + when the SUPER key is pressed + */ + if ((type == SAPP_EVENTTYPE_KEY_DOWN) && + (_sapp.event.key_code != SAPP_KEYCODE_LEFT_SUPER) && + (_sapp.event.key_code != SAPP_KEYCODE_RIGHT_SUPER) && + (_sapp.event.modifiers & SAPP_MODIFIER_SUPER)) + { + send_keyup_followup = true; + } + /* only forward a certain key ranges to the browser */ + switch (_sapp.event.key_code) { + case SAPP_KEYCODE_WORLD_1: + case SAPP_KEYCODE_WORLD_2: + case SAPP_KEYCODE_ESCAPE: + case SAPP_KEYCODE_ENTER: + case SAPP_KEYCODE_TAB: + case SAPP_KEYCODE_BACKSPACE: + case SAPP_KEYCODE_INSERT: + case SAPP_KEYCODE_DELETE: + case SAPP_KEYCODE_RIGHT: + case SAPP_KEYCODE_LEFT: + case SAPP_KEYCODE_DOWN: + case SAPP_KEYCODE_UP: + case SAPP_KEYCODE_PAGE_UP: + case SAPP_KEYCODE_PAGE_DOWN: + case SAPP_KEYCODE_HOME: + case SAPP_KEYCODE_END: + case SAPP_KEYCODE_CAPS_LOCK: + case SAPP_KEYCODE_SCROLL_LOCK: + case SAPP_KEYCODE_NUM_LOCK: + case SAPP_KEYCODE_PRINT_SCREEN: + case SAPP_KEYCODE_PAUSE: + case SAPP_KEYCODE_F1: + case SAPP_KEYCODE_F2: + case SAPP_KEYCODE_F3: + case SAPP_KEYCODE_F4: + case SAPP_KEYCODE_F5: + case SAPP_KEYCODE_F6: + case SAPP_KEYCODE_F7: + case SAPP_KEYCODE_F8: + case SAPP_KEYCODE_F9: + case SAPP_KEYCODE_F10: + case SAPP_KEYCODE_F11: + case SAPP_KEYCODE_F12: + case SAPP_KEYCODE_F13: + case SAPP_KEYCODE_F14: + case SAPP_KEYCODE_F15: + case SAPP_KEYCODE_F16: + case SAPP_KEYCODE_F17: + case SAPP_KEYCODE_F18: + case SAPP_KEYCODE_F19: + case SAPP_KEYCODE_F20: + case SAPP_KEYCODE_F21: + case SAPP_KEYCODE_F22: + case SAPP_KEYCODE_F23: + case SAPP_KEYCODE_F24: + case SAPP_KEYCODE_F25: + case SAPP_KEYCODE_LEFT_SHIFT: + case SAPP_KEYCODE_LEFT_CONTROL: + case SAPP_KEYCODE_LEFT_ALT: + case SAPP_KEYCODE_LEFT_SUPER: + case SAPP_KEYCODE_RIGHT_SHIFT: + case SAPP_KEYCODE_RIGHT_CONTROL: + case SAPP_KEYCODE_RIGHT_ALT: + case SAPP_KEYCODE_RIGHT_SUPER: + case SAPP_KEYCODE_MENU: + /* consume the event */ + break; + default: + /* forward key to browser */ + retval = false; + break; + } + } + if (_sapp_call_event(&_sapp.event)) { + /* consume event via sapp_consume_event() */ + retval = true; + } + if (send_keyup_followup) { + _sapp.event.type = SAPP_EVENTTYPE_KEY_UP; + if (_sapp_call_event(&_sapp.event)) { + retval = true; + } + } + } + } + _sapp_emsc_update_keyboard_state(); + _sapp_emsc_update_mouse_lock_state(); + return retval; +} + +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_touch_cb(int emsc_type, const EmscriptenTouchEvent* emsc_event, void* user_data) { + _SOKOL_UNUSED(user_data); + bool retval = true; + if (_sapp_events_enabled()) { + sapp_event_type type; + switch (emsc_type) { + case EMSCRIPTEN_EVENT_TOUCHSTART: + type = SAPP_EVENTTYPE_TOUCHES_BEGAN; + break; + case EMSCRIPTEN_EVENT_TOUCHMOVE: + type = SAPP_EVENTTYPE_TOUCHES_MOVED; + break; + case EMSCRIPTEN_EVENT_TOUCHEND: + type = SAPP_EVENTTYPE_TOUCHES_ENDED; + break; + case EMSCRIPTEN_EVENT_TOUCHCANCEL: + type = SAPP_EVENTTYPE_TOUCHES_CANCELLED; + break; + default: + type = SAPP_EVENTTYPE_INVALID; + retval = false; + break; + } + if (type != SAPP_EVENTTYPE_INVALID) { + _sapp_init_event(type); + if (emsc_event->ctrlKey) { + _sapp.event.modifiers |= SAPP_MODIFIER_CTRL; + } + if (emsc_event->shiftKey) { + _sapp.event.modifiers |= SAPP_MODIFIER_SHIFT; + } + if (emsc_event->altKey) { + _sapp.event.modifiers |= SAPP_MODIFIER_ALT; + } + if (emsc_event->metaKey) { + _sapp.event.modifiers |= SAPP_MODIFIER_SUPER; + } + _sapp.event.num_touches = emsc_event->numTouches; + if (_sapp.event.num_touches > SAPP_MAX_TOUCHPOINTS) { + _sapp.event.num_touches = SAPP_MAX_TOUCHPOINTS; + } + for (int i = 0; i < _sapp.event.num_touches; i++) { + const EmscriptenTouchPoint* src = &emsc_event->touches[i]; + sapp_touchpoint* dst = &_sapp.event.touches[i]; + dst->identifier = (uintptr_t)src->identifier; + dst->pos_x = src->targetX * _sapp.dpi_scale; + dst->pos_y = src->targetY * _sapp.dpi_scale; + dst->changed = src->isChanged; + } + _sapp_call_event(&_sapp.event); + } + } + _sapp_emsc_update_keyboard_state(); + return retval; +} + +_SOKOL_PRIVATE void _sapp_emsc_keytable_init(void) { + _sapp.keycodes[8] = SAPP_KEYCODE_BACKSPACE; + _sapp.keycodes[9] = SAPP_KEYCODE_TAB; + _sapp.keycodes[13] = SAPP_KEYCODE_ENTER; + _sapp.keycodes[16] = SAPP_KEYCODE_LEFT_SHIFT; + _sapp.keycodes[17] = SAPP_KEYCODE_LEFT_CONTROL; + _sapp.keycodes[18] = SAPP_KEYCODE_LEFT_ALT; + _sapp.keycodes[19] = SAPP_KEYCODE_PAUSE; + _sapp.keycodes[27] = SAPP_KEYCODE_ESCAPE; + _sapp.keycodes[32] = SAPP_KEYCODE_SPACE; + _sapp.keycodes[33] = SAPP_KEYCODE_PAGE_UP; + _sapp.keycodes[34] = SAPP_KEYCODE_PAGE_DOWN; + _sapp.keycodes[35] = SAPP_KEYCODE_END; + _sapp.keycodes[36] = SAPP_KEYCODE_HOME; + _sapp.keycodes[37] = SAPP_KEYCODE_LEFT; + _sapp.keycodes[38] = SAPP_KEYCODE_UP; + _sapp.keycodes[39] = SAPP_KEYCODE_RIGHT; + _sapp.keycodes[40] = SAPP_KEYCODE_DOWN; + _sapp.keycodes[45] = SAPP_KEYCODE_INSERT; + _sapp.keycodes[46] = SAPP_KEYCODE_DELETE; + _sapp.keycodes[48] = SAPP_KEYCODE_0; + _sapp.keycodes[49] = SAPP_KEYCODE_1; + _sapp.keycodes[50] = SAPP_KEYCODE_2; + _sapp.keycodes[51] = SAPP_KEYCODE_3; + _sapp.keycodes[52] = SAPP_KEYCODE_4; + _sapp.keycodes[53] = SAPP_KEYCODE_5; + _sapp.keycodes[54] = SAPP_KEYCODE_6; + _sapp.keycodes[55] = SAPP_KEYCODE_7; + _sapp.keycodes[56] = SAPP_KEYCODE_8; + _sapp.keycodes[57] = SAPP_KEYCODE_9; + _sapp.keycodes[59] = SAPP_KEYCODE_SEMICOLON; + _sapp.keycodes[64] = SAPP_KEYCODE_EQUAL; + _sapp.keycodes[65] = SAPP_KEYCODE_A; + _sapp.keycodes[66] = SAPP_KEYCODE_B; + _sapp.keycodes[67] = SAPP_KEYCODE_C; + _sapp.keycodes[68] = SAPP_KEYCODE_D; + _sapp.keycodes[69] = SAPP_KEYCODE_E; + _sapp.keycodes[70] = SAPP_KEYCODE_F; + _sapp.keycodes[71] = SAPP_KEYCODE_G; + _sapp.keycodes[72] = SAPP_KEYCODE_H; + _sapp.keycodes[73] = SAPP_KEYCODE_I; + _sapp.keycodes[74] = SAPP_KEYCODE_J; + _sapp.keycodes[75] = SAPP_KEYCODE_K; + _sapp.keycodes[76] = SAPP_KEYCODE_L; + _sapp.keycodes[77] = SAPP_KEYCODE_M; + _sapp.keycodes[78] = SAPP_KEYCODE_N; + _sapp.keycodes[79] = SAPP_KEYCODE_O; + _sapp.keycodes[80] = SAPP_KEYCODE_P; + _sapp.keycodes[81] = SAPP_KEYCODE_Q; + _sapp.keycodes[82] = SAPP_KEYCODE_R; + _sapp.keycodes[83] = SAPP_KEYCODE_S; + _sapp.keycodes[84] = SAPP_KEYCODE_T; + _sapp.keycodes[85] = SAPP_KEYCODE_U; + _sapp.keycodes[86] = SAPP_KEYCODE_V; + _sapp.keycodes[87] = SAPP_KEYCODE_W; + _sapp.keycodes[88] = SAPP_KEYCODE_X; + _sapp.keycodes[89] = SAPP_KEYCODE_Y; + _sapp.keycodes[90] = SAPP_KEYCODE_Z; + _sapp.keycodes[91] = SAPP_KEYCODE_LEFT_SUPER; + _sapp.keycodes[93] = SAPP_KEYCODE_MENU; + _sapp.keycodes[96] = SAPP_KEYCODE_KP_0; + _sapp.keycodes[97] = SAPP_KEYCODE_KP_1; + _sapp.keycodes[98] = SAPP_KEYCODE_KP_2; + _sapp.keycodes[99] = SAPP_KEYCODE_KP_3; + _sapp.keycodes[100] = SAPP_KEYCODE_KP_4; + _sapp.keycodes[101] = SAPP_KEYCODE_KP_5; + _sapp.keycodes[102] = SAPP_KEYCODE_KP_6; + _sapp.keycodes[103] = SAPP_KEYCODE_KP_7; + _sapp.keycodes[104] = SAPP_KEYCODE_KP_8; + _sapp.keycodes[105] = SAPP_KEYCODE_KP_9; + _sapp.keycodes[106] = SAPP_KEYCODE_KP_MULTIPLY; + _sapp.keycodes[107] = SAPP_KEYCODE_KP_ADD; + _sapp.keycodes[109] = SAPP_KEYCODE_KP_SUBTRACT; + _sapp.keycodes[110] = SAPP_KEYCODE_KP_DECIMAL; + _sapp.keycodes[111] = SAPP_KEYCODE_KP_DIVIDE; + _sapp.keycodes[112] = SAPP_KEYCODE_F1; + _sapp.keycodes[113] = SAPP_KEYCODE_F2; + _sapp.keycodes[114] = SAPP_KEYCODE_F3; + _sapp.keycodes[115] = SAPP_KEYCODE_F4; + _sapp.keycodes[116] = SAPP_KEYCODE_F5; + _sapp.keycodes[117] = SAPP_KEYCODE_F6; + _sapp.keycodes[118] = SAPP_KEYCODE_F7; + _sapp.keycodes[119] = SAPP_KEYCODE_F8; + _sapp.keycodes[120] = SAPP_KEYCODE_F9; + _sapp.keycodes[121] = SAPP_KEYCODE_F10; + _sapp.keycodes[122] = SAPP_KEYCODE_F11; + _sapp.keycodes[123] = SAPP_KEYCODE_F12; + _sapp.keycodes[144] = SAPP_KEYCODE_NUM_LOCK; + _sapp.keycodes[145] = SAPP_KEYCODE_SCROLL_LOCK; + _sapp.keycodes[173] = SAPP_KEYCODE_MINUS; + _sapp.keycodes[186] = SAPP_KEYCODE_SEMICOLON; + _sapp.keycodes[187] = SAPP_KEYCODE_EQUAL; + _sapp.keycodes[188] = SAPP_KEYCODE_COMMA; + _sapp.keycodes[189] = SAPP_KEYCODE_MINUS; + _sapp.keycodes[190] = SAPP_KEYCODE_PERIOD; + _sapp.keycodes[191] = SAPP_KEYCODE_SLASH; + _sapp.keycodes[192] = SAPP_KEYCODE_GRAVE_ACCENT; + _sapp.keycodes[219] = SAPP_KEYCODE_LEFT_BRACKET; + _sapp.keycodes[220] = SAPP_KEYCODE_BACKSLASH; + _sapp.keycodes[221] = SAPP_KEYCODE_RIGHT_BRACKET; + _sapp.keycodes[222] = SAPP_KEYCODE_APOSTROPHE; + _sapp.keycodes[224] = SAPP_KEYCODE_LEFT_SUPER; +} + +#if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_webgl_context_cb(int emsc_type, const void* reserved, void* user_data) { + _SOKOL_UNUSED(reserved); + _SOKOL_UNUSED(user_data); + sapp_event_type type; + switch (emsc_type) { + case EMSCRIPTEN_EVENT_WEBGLCONTEXTLOST: type = SAPP_EVENTTYPE_SUSPENDED; break; + case EMSCRIPTEN_EVENT_WEBGLCONTEXTRESTORED: type = SAPP_EVENTTYPE_RESUMED; break; + default: type = SAPP_EVENTTYPE_INVALID; break; + } + if (_sapp_events_enabled() && (SAPP_EVENTTYPE_INVALID != type)) { + _sapp_init_event(type); + _sapp_call_event(&_sapp.event); + } + return true; +} + +_SOKOL_PRIVATE void _sapp_emsc_webgl_init(void) { + EmscriptenWebGLContextAttributes attrs; + emscripten_webgl_init_context_attributes(&attrs); + attrs.alpha = _sapp.desc.alpha; + attrs.depth = true; + attrs.stencil = true; + attrs.antialias = _sapp.sample_count > 1; + attrs.premultipliedAlpha = _sapp.desc.html5_premultiplied_alpha; + attrs.preserveDrawingBuffer = _sapp.desc.html5_preserve_drawing_buffer; + attrs.enableExtensionsByDefault = true; + #if defined(SOKOL_GLES3) + if (_sapp.desc.gl_force_gles2) { + attrs.majorVersion = 1; + _sapp.gles2_fallback = true; + } + else { + attrs.majorVersion = 2; + } + #endif + EMSCRIPTEN_WEBGL_CONTEXT_HANDLE ctx = emscripten_webgl_create_context(_sapp.html5_canvas_selector, &attrs); + if (!ctx) { + attrs.majorVersion = 1; + ctx = emscripten_webgl_create_context(_sapp.html5_canvas_selector, &attrs); + _sapp.gles2_fallback = true; + } + emscripten_webgl_make_context_current(ctx); + + /* some WebGL extension are not enabled automatically by emscripten */ + emscripten_webgl_enable_extension(ctx, "WEBKIT_WEBGL_compressed_texture_pvrtc"); +} +#endif + +#if defined(SOKOL_WGPU) +#define _SAPP_EMSC_WGPU_STATE_INITIAL (0) +#define _SAPP_EMSC_WGPU_STATE_READY (1) +#define _SAPP_EMSC_WGPU_STATE_RUNNING (2) + +#if defined(__cplusplus) +extern "C" { +#endif +/* called when the asynchronous WebGPU device + swapchain init code in JS has finished */ +EMSCRIPTEN_KEEPALIVE void _sapp_emsc_wgpu_ready(int device_id, int swapchain_id, int swapchain_fmt) { + SOKOL_ASSERT(0 == _sapp.emsc.wgpu.device); + _sapp.emsc.wgpu.device = (WGPUDevice) device_id; + _sapp.emsc.wgpu.swapchain = (WGPUSwapChain) swapchain_id; + _sapp.emsc.wgpu.render_format = (WGPUTextureFormat) swapchain_fmt; + _sapp.emsc.wgpu.state = _SAPP_EMSC_WGPU_STATE_READY; +} +#if defined(__cplusplus) +} // extern "C" +#endif + +/* embedded JS function to handle all the asynchronous WebGPU setup */ +EM_JS(void, sapp_js_wgpu_init, (), { + WebGPU.initManagers(); + // FIXME: the extension activation must be more clever here + navigator.gpu.requestAdapter().then(function(adapter) { + console.log("wgpu adapter extensions: " + adapter.extensions); + adapter.requestDevice({ extensions: ["textureCompressionBC"]}).then(function(device) { + var gpuContext = document.getElementById("canvas").getContext("gpupresent"); + console.log("wgpu device extensions: " + adapter.extensions); + gpuContext.getSwapChainPreferredFormat(device).then(function(fmt) { + var swapChainDescriptor = { device: device, format: fmt }; + var swapChain = gpuContext.configureSwapChain(swapChainDescriptor); + var deviceId = WebGPU.mgrDevice.create(device); + var swapChainId = WebGPU.mgrSwapChain.create(swapChain); + var fmtId = WebGPU.TextureFormat.findIndex(function(elm) { return elm==fmt; }); + console.log("wgpu device: " + device); + console.log("wgpu swap chain: " + swapChain); + console.log("wgpu preferred format: " + fmt + " (" + fmtId + ")"); + __sapp_emsc_wgpu_ready(deviceId, swapChainId, fmtId); + }); + }); + }); +}); + +_SOKOL_PRIVATE void _sapp_emsc_wgpu_surfaces_create(void) { + SOKOL_ASSERT(_sapp.emsc.wgpu.device); + SOKOL_ASSERT(_sapp.emsc.wgpu.swapchain); + SOKOL_ASSERT(0 == _sapp.emsc.wgpu.depth_stencil_tex); + SOKOL_ASSERT(0 == _sapp.emsc.wgpu.depth_stencil_view); + SOKOL_ASSERT(0 == _sapp.emsc.wgpu.msaa_tex); + SOKOL_ASSERT(0 == _sapp.emsc.wgpu.msaa_view); + + WGPUTextureDescriptor ds_desc; + memset(&ds_desc, 0, sizeof(ds_desc)); + ds_desc.usage = WGPUTextureUsage_OutputAttachment; + ds_desc.dimension = WGPUTextureDimension_2D; + ds_desc.size.width = (uint32_t) _sapp.framebuffer_width; + ds_desc.size.height = (uint32_t) _sapp.framebuffer_height; + ds_desc.size.depth = 1; + ds_desc.arrayLayerCount = 1; + ds_desc.format = WGPUTextureFormat_Depth24PlusStencil8; + ds_desc.mipLevelCount = 1; + ds_desc.sampleCount = _sapp.sample_count; + _sapp.emsc.wgpu.depth_stencil_tex = wgpuDeviceCreateTexture(_sapp.emsc.wgpu.device, &ds_desc); + _sapp.emsc.wgpu.depth_stencil_view = wgpuTextureCreateView(_sapp.emsc.wgpu.depth_stencil_tex, 0); + + if (_sapp.sample_count > 1) { + WGPUTextureDescriptor msaa_desc; + memset(&msaa_desc, 0, sizeof(msaa_desc)); + msaa_desc.usage = WGPUTextureUsage_OutputAttachment; + msaa_desc.dimension = WGPUTextureDimension_2D; + msaa_desc.size.width = (uint32_t) _sapp.framebuffer_width; + msaa_desc.size.height = (uint32_t) _sapp.framebuffer_height; + msaa_desc.size.depth = 1; + msaa_desc.arrayLayerCount = 1; + msaa_desc.format = _sapp.emsc.wgpu.render_format; + msaa_desc.mipLevelCount = 1; + msaa_desc.sampleCount = _sapp.sample_count; + _sapp.emsc.wgpu.msaa_tex = wgpuDeviceCreateTexture(_sapp.emsc.wgpu.device, &msaa_desc); + _sapp.emsc.wgpu.msaa_view = wgpuTextureCreateView(_sapp.emsc.wgpu.msaa_tex, 0); + } +} + +_SOKOL_PRIVATE void _sapp_emsc_wgpu_surfaces_discard(void) { + if (_sapp.emsc.wgpu.msaa_tex) { + wgpuTextureRelease(_sapp.emsc.wgpu.msaa_tex); + _sapp.emsc.wgpu.msaa_tex = 0; + } + if (_sapp.emsc.wgpu.msaa_view) { + wgpuTextureViewRelease(_sapp.emsc.wgpu.msaa_view); + _sapp.emsc.wgpu.msaa_view = 0; + } + if (_sapp.emsc.wgpu.depth_stencil_tex) { + wgpuTextureRelease(_sapp.emsc.wgpu.depth_stencil_tex); + _sapp.emsc.wgpu.depth_stencil_tex = 0; + } + if (_sapp.emsc.wgpu.depth_stencil_view) { + wgpuTextureViewRelease(_sapp.emsc.wgpu.depth_stencil_view); + _sapp.emsc.wgpu.depth_stencil_view = 0; + } +} + +_SOKOL_PRIVATE void _sapp_emsc_wgpu_next_frame(void) { + if (_sapp.emsc.wgpu.swapchain_view) { + wgpuTextureViewRelease(_sapp.emsc.wgpu.swapchain_view); + } + _sapp.emsc.wgpu.swapchain_view = wgpuSwapChainGetCurrentTextureView(_sapp.emsc.wgpu.swapchain); +} +#endif + +_SOKOL_PRIVATE void _sapp_emsc_register_eventhandlers(void) { + emscripten_set_mousedown_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb); + emscripten_set_mouseup_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb); + emscripten_set_mousemove_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb); + emscripten_set_mouseenter_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb); + emscripten_set_mouseleave_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_mouse_cb); + emscripten_set_wheel_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_wheel_cb); + emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_key_cb); + emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_key_cb); + emscripten_set_keypress_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, _sapp_emsc_key_cb); + emscripten_set_touchstart_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_touch_cb); + emscripten_set_touchmove_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_touch_cb); + emscripten_set_touchend_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_touch_cb); + emscripten_set_touchcancel_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_touch_cb); + emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, _sapp_emsc_pointerlockchange_cb); + emscripten_set_pointerlockerror_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, _sapp_emsc_pointerlockerror_cb); + sapp_js_add_beforeunload_listener(); + if (_sapp.clipboard.enabled) { + sapp_js_add_clipboard_listener(); + } + if (_sapp.drop.enabled) { + sapp_js_add_dragndrop_listeners(&_sapp.html5_canvas_selector[1]); + } + #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + emscripten_set_webglcontextlost_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_webgl_context_cb); + emscripten_set_webglcontextrestored_callback(_sapp.html5_canvas_selector, 0, true, _sapp_emsc_webgl_context_cb); + #endif +} + +_SOKOL_PRIVATE void _sapp_emsc_unregister_eventhandlers() { + emscripten_set_mousedown_callback(_sapp.html5_canvas_selector, 0, true, 0); + emscripten_set_mouseup_callback(_sapp.html5_canvas_selector, 0, true, 0); + emscripten_set_mousemove_callback(_sapp.html5_canvas_selector, 0, true, 0); + emscripten_set_mouseenter_callback(_sapp.html5_canvas_selector, 0, true, 0); + emscripten_set_mouseleave_callback(_sapp.html5_canvas_selector, 0, true, 0); + emscripten_set_wheel_callback(_sapp.html5_canvas_selector, 0, true, 0); + emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0); + emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0); + emscripten_set_keypress_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, true, 0); + emscripten_set_touchstart_callback(_sapp.html5_canvas_selector, 0, true, 0); + emscripten_set_touchmove_callback(_sapp.html5_canvas_selector, 0, true, 0); + emscripten_set_touchend_callback(_sapp.html5_canvas_selector, 0, true, 0); + emscripten_set_touchcancel_callback(_sapp.html5_canvas_selector, 0, true, 0); + emscripten_set_pointerlockchange_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, 0); + emscripten_set_pointerlockerror_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, 0, true, 0); + sapp_js_remove_beforeunload_listener(); + if (_sapp.clipboard.enabled) { + sapp_js_remove_clipboard_listener(); + } + if (_sapp.drop.enabled) { + sapp_js_remove_dragndrop_listeners(&_sapp.html5_canvas_selector[1]); + } + #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + emscripten_set_webglcontextlost_callback(_sapp.html5_canvas_selector, 0, true, 0); + emscripten_set_webglcontextrestored_callback(_sapp.html5_canvas_selector, 0, true, 0); + #endif +} + +_SOKOL_PRIVATE EM_BOOL _sapp_emsc_frame(double time, void* userData) { + _SOKOL_UNUSED(time); + _SOKOL_UNUSED(userData); + + #if defined(SOKOL_WGPU) + /* + on WebGPU, the emscripten frame callback will already be called while + the asynchronous WebGPU device and swapchain initialization is still + in progress + */ + switch (_sapp.emsc.wgpu.state) { + case _SAPP_EMSC_WGPU_STATE_INITIAL: + /* async JS init hasn't finished yet */ + break; + case _SAPP_EMSC_WGPU_STATE_READY: + /* perform post-async init stuff */ + _sapp_emsc_wgpu_surfaces_create(); + _sapp.emsc.wgpu.state = _SAPP_EMSC_WGPU_STATE_RUNNING; + break; + case _SAPP_EMSC_WGPU_STATE_RUNNING: + /* a regular frame */ + _sapp_emsc_wgpu_next_frame(); + _sapp_frame(); + break; + } + #else + /* WebGL code path */ + _sapp_frame(); + #endif + + /* quit-handling */ + if (_sapp.quit_requested) { + _sapp_init_event(SAPP_EVENTTYPE_QUIT_REQUESTED); + _sapp_call_event(&_sapp.event); + if (_sapp.quit_requested) { + _sapp.quit_ordered = true; + } + } + if (_sapp.quit_ordered) { + _sapp_emsc_unregister_eventhandlers(); + _sapp_call_cleanup(); + _sapp_discard_state(); + return EM_FALSE; + } + return EM_TRUE; +} + +_SOKOL_PRIVATE void _sapp_emsc_run(const sapp_desc* desc) { + _sapp_init_state(desc); + sapp_js_pointer_init(&_sapp.html5_canvas_selector[1]); + _sapp_emsc_keytable_init(); + double w, h; + if (_sapp.desc.html5_canvas_resize) { + w = (double) _sapp.desc.width; + h = (double) _sapp.desc.height; + } + else { + emscripten_get_element_css_size(_sapp.html5_canvas_selector, &w, &h); + emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, false, _sapp_emsc_size_changed); + } + if (_sapp.desc.high_dpi) { + _sapp.dpi_scale = emscripten_get_device_pixel_ratio(); + } + _sapp.window_width = (int) w; + _sapp.window_height = (int) h; + _sapp.framebuffer_width = (int) (w * _sapp.dpi_scale); + _sapp.framebuffer_height = (int) (h * _sapp.dpi_scale); + emscripten_set_canvas_element_size(_sapp.html5_canvas_selector, _sapp.framebuffer_width, _sapp.framebuffer_height); + #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + _sapp_emsc_webgl_init(); + #elif defined(SOKOL_WGPU) + sapp_js_wgpu_init(); + #endif + _sapp.valid = true; + _sapp_emsc_register_eventhandlers(); + + /* start the frame loop */ + emscripten_request_animation_frame_loop(_sapp_emsc_frame, 0); + + /* NOT A BUG: do not call _sapp_discard_state() here, instead this is + called in _sapp_emsc_frame() when the application is ordered to quit + */ +} + +#if !defined(SOKOL_NO_ENTRY) +int main(int argc, char* argv[]) { + sapp_desc desc = sokol_main(argc, argv); + _sapp_emsc_run(&desc); + return 0; +} +#endif /* SOKOL_NO_ENTRY */ +#endif /* _SAPP_EMSCRIPTEN */ + +/*== MISC GL SUPPORT FUNCTIONS ================================================*/ +#if defined(SOKOL_GLCORE33) +typedef struct { + int red_bits; + int green_bits; + int blue_bits; + int alpha_bits; + int depth_bits; + int stencil_bits; + int samples; + bool doublebuffer; + uintptr_t handle; +} _sapp_gl_fbconfig; + +_SOKOL_PRIVATE void _sapp_gl_init_fbconfig(_sapp_gl_fbconfig* fbconfig) { + memset(fbconfig, 0, sizeof(_sapp_gl_fbconfig)); + /* -1 means "don't care" */ + fbconfig->red_bits = -1; + fbconfig->green_bits = -1; + fbconfig->blue_bits = -1; + fbconfig->alpha_bits = -1; + fbconfig->depth_bits = -1; + fbconfig->stencil_bits = -1; + fbconfig->samples = -1; +} + +_SOKOL_PRIVATE const _sapp_gl_fbconfig* _sapp_gl_choose_fbconfig(const _sapp_gl_fbconfig* desired, const _sapp_gl_fbconfig* alternatives, int count) { + int missing, least_missing = 1000000; + int color_diff, least_color_diff = 10000000; + int extra_diff, least_extra_diff = 10000000; + const _sapp_gl_fbconfig* current; + const _sapp_gl_fbconfig* closest = 0; + for (int i = 0; i < count; i++) { + current = alternatives + i; + if (desired->doublebuffer != current->doublebuffer) { + continue; + } + missing = 0; + if (desired->alpha_bits > 0 && current->alpha_bits == 0) { + missing++; + } + if (desired->depth_bits > 0 && current->depth_bits == 0) { + missing++; + } + if (desired->stencil_bits > 0 && current->stencil_bits == 0) { + missing++; + } + if (desired->samples > 0 && current->samples == 0) { + /* Technically, several multisampling buffers could be + involved, but that's a lower level implementation detail and + not important to us here, so we count them as one + */ + missing++; + } + + /* These polynomials make many small channel size differences matter + less than one large channel size difference + Calculate color channel size difference value + */ + color_diff = 0; + if (desired->red_bits != -1) { + color_diff += (desired->red_bits - current->red_bits) * (desired->red_bits - current->red_bits); + } + if (desired->green_bits != -1) { + color_diff += (desired->green_bits - current->green_bits) * (desired->green_bits - current->green_bits); + } + if (desired->blue_bits != -1) { + color_diff += (desired->blue_bits - current->blue_bits) * (desired->blue_bits - current->blue_bits); + } + + /* Calculate non-color channel size difference value */ + extra_diff = 0; + if (desired->alpha_bits != -1) { + extra_diff += (desired->alpha_bits - current->alpha_bits) * (desired->alpha_bits - current->alpha_bits); + } + if (desired->depth_bits != -1) { + extra_diff += (desired->depth_bits - current->depth_bits) * (desired->depth_bits - current->depth_bits); + } + if (desired->stencil_bits != -1) { + extra_diff += (desired->stencil_bits - current->stencil_bits) * (desired->stencil_bits - current->stencil_bits); + } + if (desired->samples != -1) { + extra_diff += (desired->samples - current->samples) * (desired->samples - current->samples); + } + + /* Figure out if the current one is better than the best one found so far + Least number of missing buffers is the most important heuristic, + then color buffer size match and lastly size match for other buffers + */ + if (missing < least_missing) { + closest = current; + } + else if (missing == least_missing) { + if ((color_diff < least_color_diff) || + (color_diff == least_color_diff && extra_diff < least_extra_diff)) + { + closest = current; + } + } + if (current == closest) { + least_missing = missing; + least_color_diff = color_diff; + least_extra_diff = extra_diff; + } + } + return closest; +} +#endif + +/*== WINDOWS DESKTOP and UWP====================================================*/ +#if defined(_SAPP_WIN32) || defined(_SAPP_UWP) +_SOKOL_PRIVATE bool _sapp_win32_uwp_utf8_to_wide(const char* src, wchar_t* dst, int dst_num_bytes) { + SOKOL_ASSERT(src && dst && (dst_num_bytes > 1)); + memset(dst, 0, (size_t)dst_num_bytes); + const int dst_chars = dst_num_bytes / (int)sizeof(wchar_t); + const int dst_needed = MultiByteToWideChar(CP_UTF8, 0, src, -1, 0, 0); + if ((dst_needed > 0) && (dst_needed < dst_chars)) { + MultiByteToWideChar(CP_UTF8, 0, src, -1, dst, dst_chars); + return true; + } + else { + /* input string doesn't fit into destination buffer */ + return false; + } +} + +_SOKOL_PRIVATE void _sapp_win32_uwp_app_event(sapp_event_type type) { + if (_sapp_events_enabled()) { + _sapp_init_event(type); + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE void _sapp_win32_uwp_init_keytable(void) { + /* same as GLFW */ + _sapp.keycodes[0x00B] = SAPP_KEYCODE_0; + _sapp.keycodes[0x002] = SAPP_KEYCODE_1; + _sapp.keycodes[0x003] = SAPP_KEYCODE_2; + _sapp.keycodes[0x004] = SAPP_KEYCODE_3; + _sapp.keycodes[0x005] = SAPP_KEYCODE_4; + _sapp.keycodes[0x006] = SAPP_KEYCODE_5; + _sapp.keycodes[0x007] = SAPP_KEYCODE_6; + _sapp.keycodes[0x008] = SAPP_KEYCODE_7; + _sapp.keycodes[0x009] = SAPP_KEYCODE_8; + _sapp.keycodes[0x00A] = SAPP_KEYCODE_9; + _sapp.keycodes[0x01E] = SAPP_KEYCODE_A; + _sapp.keycodes[0x030] = SAPP_KEYCODE_B; + _sapp.keycodes[0x02E] = SAPP_KEYCODE_C; + _sapp.keycodes[0x020] = SAPP_KEYCODE_D; + _sapp.keycodes[0x012] = SAPP_KEYCODE_E; + _sapp.keycodes[0x021] = SAPP_KEYCODE_F; + _sapp.keycodes[0x022] = SAPP_KEYCODE_G; + _sapp.keycodes[0x023] = SAPP_KEYCODE_H; + _sapp.keycodes[0x017] = SAPP_KEYCODE_I; + _sapp.keycodes[0x024] = SAPP_KEYCODE_J; + _sapp.keycodes[0x025] = SAPP_KEYCODE_K; + _sapp.keycodes[0x026] = SAPP_KEYCODE_L; + _sapp.keycodes[0x032] = SAPP_KEYCODE_M; + _sapp.keycodes[0x031] = SAPP_KEYCODE_N; + _sapp.keycodes[0x018] = SAPP_KEYCODE_O; + _sapp.keycodes[0x019] = SAPP_KEYCODE_P; + _sapp.keycodes[0x010] = SAPP_KEYCODE_Q; + _sapp.keycodes[0x013] = SAPP_KEYCODE_R; + _sapp.keycodes[0x01F] = SAPP_KEYCODE_S; + _sapp.keycodes[0x014] = SAPP_KEYCODE_T; + _sapp.keycodes[0x016] = SAPP_KEYCODE_U; + _sapp.keycodes[0x02F] = SAPP_KEYCODE_V; + _sapp.keycodes[0x011] = SAPP_KEYCODE_W; + _sapp.keycodes[0x02D] = SAPP_KEYCODE_X; + _sapp.keycodes[0x015] = SAPP_KEYCODE_Y; + _sapp.keycodes[0x02C] = SAPP_KEYCODE_Z; + _sapp.keycodes[0x028] = SAPP_KEYCODE_APOSTROPHE; + _sapp.keycodes[0x02B] = SAPP_KEYCODE_BACKSLASH; + _sapp.keycodes[0x033] = SAPP_KEYCODE_COMMA; + _sapp.keycodes[0x00D] = SAPP_KEYCODE_EQUAL; + _sapp.keycodes[0x029] = SAPP_KEYCODE_GRAVE_ACCENT; + _sapp.keycodes[0x01A] = SAPP_KEYCODE_LEFT_BRACKET; + _sapp.keycodes[0x00C] = SAPP_KEYCODE_MINUS; + _sapp.keycodes[0x034] = SAPP_KEYCODE_PERIOD; + _sapp.keycodes[0x01B] = SAPP_KEYCODE_RIGHT_BRACKET; + _sapp.keycodes[0x027] = SAPP_KEYCODE_SEMICOLON; + _sapp.keycodes[0x035] = SAPP_KEYCODE_SLASH; + _sapp.keycodes[0x056] = SAPP_KEYCODE_WORLD_2; + _sapp.keycodes[0x00E] = SAPP_KEYCODE_BACKSPACE; + _sapp.keycodes[0x153] = SAPP_KEYCODE_DELETE; + _sapp.keycodes[0x14F] = SAPP_KEYCODE_END; + _sapp.keycodes[0x01C] = SAPP_KEYCODE_ENTER; + _sapp.keycodes[0x001] = SAPP_KEYCODE_ESCAPE; + _sapp.keycodes[0x147] = SAPP_KEYCODE_HOME; + _sapp.keycodes[0x152] = SAPP_KEYCODE_INSERT; + _sapp.keycodes[0x15D] = SAPP_KEYCODE_MENU; + _sapp.keycodes[0x151] = SAPP_KEYCODE_PAGE_DOWN; + _sapp.keycodes[0x149] = SAPP_KEYCODE_PAGE_UP; + _sapp.keycodes[0x045] = SAPP_KEYCODE_PAUSE; + _sapp.keycodes[0x146] = SAPP_KEYCODE_PAUSE; + _sapp.keycodes[0x039] = SAPP_KEYCODE_SPACE; + _sapp.keycodes[0x00F] = SAPP_KEYCODE_TAB; + _sapp.keycodes[0x03A] = SAPP_KEYCODE_CAPS_LOCK; + _sapp.keycodes[0x145] = SAPP_KEYCODE_NUM_LOCK; + _sapp.keycodes[0x046] = SAPP_KEYCODE_SCROLL_LOCK; + _sapp.keycodes[0x03B] = SAPP_KEYCODE_F1; + _sapp.keycodes[0x03C] = SAPP_KEYCODE_F2; + _sapp.keycodes[0x03D] = SAPP_KEYCODE_F3; + _sapp.keycodes[0x03E] = SAPP_KEYCODE_F4; + _sapp.keycodes[0x03F] = SAPP_KEYCODE_F5; + _sapp.keycodes[0x040] = SAPP_KEYCODE_F6; + _sapp.keycodes[0x041] = SAPP_KEYCODE_F7; + _sapp.keycodes[0x042] = SAPP_KEYCODE_F8; + _sapp.keycodes[0x043] = SAPP_KEYCODE_F9; + _sapp.keycodes[0x044] = SAPP_KEYCODE_F10; + _sapp.keycodes[0x057] = SAPP_KEYCODE_F11; + _sapp.keycodes[0x058] = SAPP_KEYCODE_F12; + _sapp.keycodes[0x064] = SAPP_KEYCODE_F13; + _sapp.keycodes[0x065] = SAPP_KEYCODE_F14; + _sapp.keycodes[0x066] = SAPP_KEYCODE_F15; + _sapp.keycodes[0x067] = SAPP_KEYCODE_F16; + _sapp.keycodes[0x068] = SAPP_KEYCODE_F17; + _sapp.keycodes[0x069] = SAPP_KEYCODE_F18; + _sapp.keycodes[0x06A] = SAPP_KEYCODE_F19; + _sapp.keycodes[0x06B] = SAPP_KEYCODE_F20; + _sapp.keycodes[0x06C] = SAPP_KEYCODE_F21; + _sapp.keycodes[0x06D] = SAPP_KEYCODE_F22; + _sapp.keycodes[0x06E] = SAPP_KEYCODE_F23; + _sapp.keycodes[0x076] = SAPP_KEYCODE_F24; + _sapp.keycodes[0x038] = SAPP_KEYCODE_LEFT_ALT; + _sapp.keycodes[0x01D] = SAPP_KEYCODE_LEFT_CONTROL; + _sapp.keycodes[0x02A] = SAPP_KEYCODE_LEFT_SHIFT; + _sapp.keycodes[0x15B] = SAPP_KEYCODE_LEFT_SUPER; + _sapp.keycodes[0x137] = SAPP_KEYCODE_PRINT_SCREEN; + _sapp.keycodes[0x138] = SAPP_KEYCODE_RIGHT_ALT; + _sapp.keycodes[0x11D] = SAPP_KEYCODE_RIGHT_CONTROL; + _sapp.keycodes[0x036] = SAPP_KEYCODE_RIGHT_SHIFT; + _sapp.keycodes[0x15C] = SAPP_KEYCODE_RIGHT_SUPER; + _sapp.keycodes[0x150] = SAPP_KEYCODE_DOWN; + _sapp.keycodes[0x14B] = SAPP_KEYCODE_LEFT; + _sapp.keycodes[0x14D] = SAPP_KEYCODE_RIGHT; + _sapp.keycodes[0x148] = SAPP_KEYCODE_UP; + _sapp.keycodes[0x052] = SAPP_KEYCODE_KP_0; + _sapp.keycodes[0x04F] = SAPP_KEYCODE_KP_1; + _sapp.keycodes[0x050] = SAPP_KEYCODE_KP_2; + _sapp.keycodes[0x051] = SAPP_KEYCODE_KP_3; + _sapp.keycodes[0x04B] = SAPP_KEYCODE_KP_4; + _sapp.keycodes[0x04C] = SAPP_KEYCODE_KP_5; + _sapp.keycodes[0x04D] = SAPP_KEYCODE_KP_6; + _sapp.keycodes[0x047] = SAPP_KEYCODE_KP_7; + _sapp.keycodes[0x048] = SAPP_KEYCODE_KP_8; + _sapp.keycodes[0x049] = SAPP_KEYCODE_KP_9; + _sapp.keycodes[0x04E] = SAPP_KEYCODE_KP_ADD; + _sapp.keycodes[0x053] = SAPP_KEYCODE_KP_DECIMAL; + _sapp.keycodes[0x135] = SAPP_KEYCODE_KP_DIVIDE; + _sapp.keycodes[0x11C] = SAPP_KEYCODE_KP_ENTER; + _sapp.keycodes[0x037] = SAPP_KEYCODE_KP_MULTIPLY; + _sapp.keycodes[0x04A] = SAPP_KEYCODE_KP_SUBTRACT; +} +#endif // _SAPP_WIN32 || _SAPP_UWP + +/*== WINDOWS DESKTOP===========================================================*/ +#if defined(_SAPP_WIN32) + +#if defined(SOKOL_D3D11) + +#if defined(__cplusplus) +#define _sapp_d3d11_Release(self) (self)->Release() +#else +#define _sapp_d3d11_Release(self) (self)->lpVtbl->Release(self) +#endif + +#define _SAPP_SAFE_RELEASE(obj) if (obj) { _sapp_d3d11_Release(obj); obj=0; } + +static inline HRESULT _sapp_dxgi_GetBuffer(IDXGISwapChain* self, UINT Buffer, REFIID riid, void** ppSurface) { + #if defined(__cplusplus) + return self->GetBuffer(Buffer, riid, ppSurface); + #else + return self->lpVtbl->GetBuffer(self, Buffer, riid, ppSurface); + #endif +} + +static inline HRESULT _sapp_d3d11_CreateRenderTargetView(ID3D11Device* self, ID3D11Resource *pResource, const D3D11_RENDER_TARGET_VIEW_DESC* pDesc, ID3D11RenderTargetView** ppRTView) { + #if defined(__cplusplus) + return self->CreateRenderTargetView(pResource, pDesc, ppRTView); + #else + return self->lpVtbl->CreateRenderTargetView(self, pResource, pDesc, ppRTView); + #endif +} + +static inline HRESULT _sapp_d3d11_CreateTexture2D(ID3D11Device* self, const D3D11_TEXTURE2D_DESC* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Texture2D** ppTexture2D) { + #if defined(__cplusplus) + return self->CreateTexture2D(pDesc, pInitialData, ppTexture2D); + #else + return self->lpVtbl->CreateTexture2D(self, pDesc, pInitialData, ppTexture2D); + #endif +} + +static inline HRESULT _sapp_d3d11_CreateDepthStencilView(ID3D11Device* self, ID3D11Resource* pResource, const D3D11_DEPTH_STENCIL_VIEW_DESC* pDesc, ID3D11DepthStencilView** ppDepthStencilView) { + #if defined(__cplusplus) + return self->CreateDepthStencilView(pResource, pDesc, ppDepthStencilView); + #else + return self->lpVtbl->CreateDepthStencilView(self, pResource, pDesc, ppDepthStencilView); + #endif +} + +static inline void _sapp_d3d11_ResolveSubresource(ID3D11DeviceContext* self, ID3D11Resource* pDstResource, UINT DstSubresource, ID3D11Resource* pSrcResource, UINT SrcSubresource, DXGI_FORMAT Format) { + #if defined(__cplusplus) + self->ResolveSubresource(pDstResource, DstSubresource, pSrcResource, SrcSubresource, Format); + #else + self->lpVtbl->ResolveSubresource(self, pDstResource, DstSubresource, pSrcResource, SrcSubresource, Format); + #endif +} + +static inline HRESULT _sapp_dxgi_ResizeBuffers(IDXGISwapChain* self, UINT BufferCount, UINT Width, UINT Height, DXGI_FORMAT NewFormat, UINT SwapChainFlags) { + #if defined(__cplusplus) + return self->ResizeBuffers(BufferCount, Width, Height, NewFormat, SwapChainFlags); + #else + return self->lpVtbl->ResizeBuffers(self, BufferCount, Width, Height, NewFormat, SwapChainFlags); + #endif +} + +static inline HRESULT _sapp_dxgi_Present(IDXGISwapChain* self, UINT SyncInterval, UINT Flags) { + #if defined(__cplusplus) + return self->Present(SyncInterval, Flags); + #else + return self->lpVtbl->Present(self, SyncInterval, Flags); + #endif +} + +_SOKOL_PRIVATE void _sapp_d3d11_create_device_and_swapchain(void) { + DXGI_SWAP_CHAIN_DESC* sc_desc = &_sapp.d3d11.swap_chain_desc; + sc_desc->BufferDesc.Width = (UINT)_sapp.framebuffer_width; + sc_desc->BufferDesc.Height = (UINT)_sapp.framebuffer_height; + sc_desc->BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + sc_desc->BufferDesc.RefreshRate.Numerator = 60; + sc_desc->BufferDesc.RefreshRate.Denominator = 1; + sc_desc->OutputWindow = _sapp.win32.hwnd; + sc_desc->Windowed = true; + if (_sapp.win32.is_win10_or_greater) { + sc_desc->BufferCount = 2; + sc_desc->SwapEffect = (DXGI_SWAP_EFFECT) _SAPP_DXGI_SWAP_EFFECT_FLIP_DISCARD; + } + else { + sc_desc->BufferCount = 1; + sc_desc->SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + } + sc_desc->SampleDesc.Count = 1; + sc_desc->SampleDesc.Quality = 0; + sc_desc->BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + UINT create_flags = D3D11_CREATE_DEVICE_SINGLETHREADED | D3D11_CREATE_DEVICE_BGRA_SUPPORT; + #if defined(SOKOL_DEBUG) + create_flags |= D3D11_CREATE_DEVICE_DEBUG; + #endif + D3D_FEATURE_LEVEL feature_level; + HRESULT hr = D3D11CreateDeviceAndSwapChain( + NULL, /* pAdapter (use default) */ + D3D_DRIVER_TYPE_HARDWARE, /* DriverType */ + NULL, /* Software */ + create_flags, /* Flags */ + NULL, /* pFeatureLevels */ + 0, /* FeatureLevels */ + D3D11_SDK_VERSION, /* SDKVersion */ + sc_desc, /* pSwapChainDesc */ + &_sapp.d3d11.swap_chain, /* ppSwapChain */ + &_sapp.d3d11.device, /* ppDevice */ + &feature_level, /* pFeatureLevel */ + &_sapp.d3d11.device_context); /* ppImmediateContext */ + _SOKOL_UNUSED(hr); + SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.swap_chain && _sapp.d3d11.device && _sapp.d3d11.device_context); +} + +_SOKOL_PRIVATE void _sapp_d3d11_destroy_device_and_swapchain(void) { + _SAPP_SAFE_RELEASE(_sapp.d3d11.swap_chain); + _SAPP_SAFE_RELEASE(_sapp.d3d11.device_context); + _SAPP_SAFE_RELEASE(_sapp.d3d11.device); +} + +_SOKOL_PRIVATE void _sapp_d3d11_create_default_render_target(void) { + SOKOL_ASSERT(0 == _sapp.d3d11.rt); + SOKOL_ASSERT(0 == _sapp.d3d11.rtv); + SOKOL_ASSERT(0 == _sapp.d3d11.msaa_rt); + SOKOL_ASSERT(0 == _sapp.d3d11.msaa_rtv); + SOKOL_ASSERT(0 == _sapp.d3d11.ds); + SOKOL_ASSERT(0 == _sapp.d3d11.dsv); + + HRESULT hr; + + /* view for the swapchain-created framebuffer */ + #ifdef __cplusplus + hr = _sapp_dxgi_GetBuffer(_sapp.d3d11.swap_chain, 0, IID_ID3D11Texture2D, (void**)&_sapp.d3d11.rt); + #else + hr = _sapp_dxgi_GetBuffer(_sapp.d3d11.swap_chain, 0, &IID_ID3D11Texture2D, (void**)&_sapp.d3d11.rt); + #endif + SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.rt); + hr = _sapp_d3d11_CreateRenderTargetView(_sapp.d3d11.device, (ID3D11Resource*)_sapp.d3d11.rt, NULL, &_sapp.d3d11.rtv); + SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.rtv); + + /* common desc for MSAA and depth-stencil texture */ + D3D11_TEXTURE2D_DESC tex_desc; + memset(&tex_desc, 0, sizeof(tex_desc)); + tex_desc.Width = (UINT)_sapp.framebuffer_width; + tex_desc.Height = (UINT)_sapp.framebuffer_height; + tex_desc.MipLevels = 1; + tex_desc.ArraySize = 1; + tex_desc.Usage = D3D11_USAGE_DEFAULT; + tex_desc.BindFlags = D3D11_BIND_RENDER_TARGET; + tex_desc.SampleDesc.Count = (UINT) _sapp.sample_count; + tex_desc.SampleDesc.Quality = (UINT) (_sapp.sample_count > 1 ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0); + + /* create MSAA texture and view if antialiasing requested */ + if (_sapp.sample_count > 1) { + tex_desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + hr = _sapp_d3d11_CreateTexture2D(_sapp.d3d11.device, &tex_desc, NULL, &_sapp.d3d11.msaa_rt); + SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.msaa_rt); + hr = _sapp_d3d11_CreateRenderTargetView(_sapp.d3d11.device, (ID3D11Resource*)_sapp.d3d11.msaa_rt, NULL, &_sapp.d3d11.msaa_rtv); + SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.msaa_rtv); + } + + /* texture and view for the depth-stencil-surface */ + tex_desc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; + tex_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL; + hr = _sapp_d3d11_CreateTexture2D(_sapp.d3d11.device, &tex_desc, NULL, &_sapp.d3d11.ds); + SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.ds); + hr = _sapp_d3d11_CreateDepthStencilView(_sapp.d3d11.device, (ID3D11Resource*)_sapp.d3d11.ds, NULL, &_sapp.d3d11.dsv); + SOKOL_ASSERT(SUCCEEDED(hr) && _sapp.d3d11.dsv); +} + +_SOKOL_PRIVATE void _sapp_d3d11_destroy_default_render_target(void) { + _SAPP_SAFE_RELEASE(_sapp.d3d11.rt); + _SAPP_SAFE_RELEASE(_sapp.d3d11.rtv); + _SAPP_SAFE_RELEASE(_sapp.d3d11.msaa_rt); + _SAPP_SAFE_RELEASE(_sapp.d3d11.msaa_rtv); + _SAPP_SAFE_RELEASE(_sapp.d3d11.ds); + _SAPP_SAFE_RELEASE(_sapp.d3d11.dsv); +} + +_SOKOL_PRIVATE void _sapp_d3d11_resize_default_render_target(void) { + if (_sapp.d3d11.swap_chain) { + _sapp_d3d11_destroy_default_render_target(); + _sapp_dxgi_ResizeBuffers(_sapp.d3d11.swap_chain, _sapp.d3d11.swap_chain_desc.BufferCount, (UINT)_sapp.framebuffer_width, (UINT)_sapp.framebuffer_height, DXGI_FORMAT_B8G8R8A8_UNORM, 0); + _sapp_d3d11_create_default_render_target(); + } +} + +_SOKOL_PRIVATE void _sapp_d3d11_present(void) { + /* do MSAA resolve if needed */ + if (_sapp.sample_count > 1) { + SOKOL_ASSERT(_sapp.d3d11.rt); + SOKOL_ASSERT(_sapp.d3d11.msaa_rt); + _sapp_d3d11_ResolveSubresource(_sapp.d3d11.device_context, (ID3D11Resource*)_sapp.d3d11.rt, 0, (ID3D11Resource*)_sapp.d3d11.msaa_rt, 0, DXGI_FORMAT_B8G8R8A8_UNORM); + } + _sapp_dxgi_Present(_sapp.d3d11.swap_chain, (UINT)_sapp.swap_interval, 0); +} + +#endif /* SOKOL_D3D11 */ + +#if defined(SOKOL_GLCORE33) +_SOKOL_PRIVATE void _sapp_wgl_init(void) { + _sapp.wgl.opengl32 = LoadLibraryA("opengl32.dll"); + if (!_sapp.wgl.opengl32) { + _sapp_fail("Failed to load opengl32.dll\n"); + } + SOKOL_ASSERT(_sapp.wgl.opengl32); + _sapp.wgl.CreateContext = (PFN_wglCreateContext)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglCreateContext"); + SOKOL_ASSERT(_sapp.wgl.CreateContext); + _sapp.wgl.DeleteContext = (PFN_wglDeleteContext)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglDeleteContext"); + SOKOL_ASSERT(_sapp.wgl.DeleteContext); + _sapp.wgl.GetProcAddress = (PFN_wglGetProcAddress)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglGetProcAddress"); + SOKOL_ASSERT(_sapp.wgl.GetProcAddress); + _sapp.wgl.GetCurrentDC = (PFN_wglGetCurrentDC)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglGetCurrentDC"); + SOKOL_ASSERT(_sapp.wgl.GetCurrentDC); + _sapp.wgl.MakeCurrent = (PFN_wglMakeCurrent)(void*) GetProcAddress(_sapp.wgl.opengl32, "wglMakeCurrent"); + SOKOL_ASSERT(_sapp.wgl.MakeCurrent); + + _sapp.wgl.msg_hwnd = CreateWindowExW(WS_EX_OVERLAPPEDWINDOW, + L"SOKOLAPP", + L"sokol-app message window", + WS_CLIPSIBLINGS|WS_CLIPCHILDREN, + 0, 0, 1, 1, + NULL, NULL, + GetModuleHandleW(NULL), + NULL); + if (!_sapp.wgl.msg_hwnd) { + _sapp_fail("Win32: failed to create helper window!\n"); + } + SOKOL_ASSERT(_sapp.wgl.msg_hwnd); + ShowWindow(_sapp.wgl.msg_hwnd, SW_HIDE); + MSG msg; + while (PeekMessageW(&msg, _sapp.wgl.msg_hwnd, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + _sapp.wgl.msg_dc = GetDC(_sapp.wgl.msg_hwnd); + if (!_sapp.wgl.msg_dc) { + _sapp_fail("Win32: failed to obtain helper window DC!\n"); + } +} + +_SOKOL_PRIVATE void _sapp_wgl_shutdown(void) { + SOKOL_ASSERT(_sapp.wgl.opengl32 && _sapp.wgl.msg_hwnd); + DestroyWindow(_sapp.wgl.msg_hwnd); _sapp.wgl.msg_hwnd = 0; + FreeLibrary(_sapp.wgl.opengl32); _sapp.wgl.opengl32 = 0; +} + +_SOKOL_PRIVATE bool _sapp_wgl_has_ext(const char* ext, const char* extensions) { + SOKOL_ASSERT(ext && extensions); + const char* start = extensions; + while (true) { + const char* where = strstr(start, ext); + if (!where) { + return false; + } + const char* terminator = where + strlen(ext); + if ((where == start) || (*(where - 1) == ' ')) { + if (*terminator == ' ' || *terminator == '\0') { + break; + } + } + start = terminator; + } + return true; +} + +_SOKOL_PRIVATE bool _sapp_wgl_ext_supported(const char* ext) { + SOKOL_ASSERT(ext); + if (_sapp.wgl.GetExtensionsStringEXT) { + const char* extensions = _sapp.wgl.GetExtensionsStringEXT(); + if (extensions) { + if (_sapp_wgl_has_ext(ext, extensions)) { + return true; + } + } + } + if (_sapp.wgl.GetExtensionsStringARB) { + const char* extensions = _sapp.wgl.GetExtensionsStringARB(_sapp.wgl.GetCurrentDC()); + if (extensions) { + if (_sapp_wgl_has_ext(ext, extensions)) { + return true; + } + } + } + return false; +} + +_SOKOL_PRIVATE void _sapp_wgl_load_extensions(void) { + SOKOL_ASSERT(_sapp.wgl.msg_dc); + PIXELFORMATDESCRIPTOR pfd; + memset(&pfd, 0, sizeof(pfd)); + pfd.nSize = sizeof(pfd); + pfd.nVersion = 1; + pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.cColorBits = 24; + if (!SetPixelFormat(_sapp.wgl.msg_dc, ChoosePixelFormat(_sapp.wgl.msg_dc, &pfd), &pfd)) { + _sapp_fail("WGL: failed to set pixel format for dummy context\n"); + } + HGLRC rc = _sapp.wgl.CreateContext(_sapp.wgl.msg_dc); + if (!rc) { + _sapp_fail("WGL: Failed to create dummy context\n"); + } + if (!_sapp.wgl.MakeCurrent(_sapp.wgl.msg_dc, rc)) { + _sapp_fail("WGL: Failed to make context current\n"); + } + _sapp.wgl.GetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC)(void*) _sapp.wgl.GetProcAddress("wglGetExtensionsStringEXT"); + _sapp.wgl.GetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)(void*) _sapp.wgl.GetProcAddress("wglGetExtensionsStringARB"); + _sapp.wgl.CreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)(void*) _sapp.wgl.GetProcAddress("wglCreateContextAttribsARB"); + _sapp.wgl.SwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)(void*) _sapp.wgl.GetProcAddress("wglSwapIntervalEXT"); + _sapp.wgl.GetPixelFormatAttribivARB = (PFNWGLGETPIXELFORMATATTRIBIVARBPROC)(void*) _sapp.wgl.GetProcAddress("wglGetPixelFormatAttribivARB"); + _sapp.wgl.arb_multisample = _sapp_wgl_ext_supported("WGL_ARB_multisample"); + _sapp.wgl.arb_create_context = _sapp_wgl_ext_supported("WGL_ARB_create_context"); + _sapp.wgl.arb_create_context_profile = _sapp_wgl_ext_supported("WGL_ARB_create_context_profile"); + _sapp.wgl.ext_swap_control = _sapp_wgl_ext_supported("WGL_EXT_swap_control"); + _sapp.wgl.arb_pixel_format = _sapp_wgl_ext_supported("WGL_ARB_pixel_format"); + _sapp.wgl.MakeCurrent(_sapp.wgl.msg_dc, 0); + _sapp.wgl.DeleteContext(rc); +} + +_SOKOL_PRIVATE int _sapp_wgl_attrib(int pixel_format, int attrib) { + SOKOL_ASSERT(_sapp.wgl.arb_pixel_format); + int value = 0; + if (!_sapp.wgl.GetPixelFormatAttribivARB(_sapp.win32.dc, pixel_format, 0, 1, &attrib, &value)) { + _sapp_fail("WGL: Failed to retrieve pixel format attribute\n"); + } + return value; +} + +_SOKOL_PRIVATE int _sapp_wgl_find_pixel_format(void) { + SOKOL_ASSERT(_sapp.win32.dc); + SOKOL_ASSERT(_sapp.wgl.arb_pixel_format); + const _sapp_gl_fbconfig* closest; + + int native_count = _sapp_wgl_attrib(1, WGL_NUMBER_PIXEL_FORMATS_ARB); + _sapp_gl_fbconfig* usable_configs = (_sapp_gl_fbconfig*) SOKOL_CALLOC((size_t)native_count, sizeof(_sapp_gl_fbconfig)); + SOKOL_ASSERT(usable_configs); + int usable_count = 0; + for (int i = 0; i < native_count; i++) { + const int n = i + 1; + _sapp_gl_fbconfig* u = usable_configs + usable_count; + _sapp_gl_init_fbconfig(u); + if (!_sapp_wgl_attrib(n, WGL_SUPPORT_OPENGL_ARB) || !_sapp_wgl_attrib(n, WGL_DRAW_TO_WINDOW_ARB)) { + continue; + } + if (_sapp_wgl_attrib(n, WGL_PIXEL_TYPE_ARB) != WGL_TYPE_RGBA_ARB) { + continue; + } + if (_sapp_wgl_attrib(n, WGL_ACCELERATION_ARB) == WGL_NO_ACCELERATION_ARB) { + continue; + } + u->red_bits = _sapp_wgl_attrib(n, WGL_RED_BITS_ARB); + u->green_bits = _sapp_wgl_attrib(n, WGL_GREEN_BITS_ARB); + u->blue_bits = _sapp_wgl_attrib(n, WGL_BLUE_BITS_ARB); + u->alpha_bits = _sapp_wgl_attrib(n, WGL_ALPHA_BITS_ARB); + u->depth_bits = _sapp_wgl_attrib(n, WGL_DEPTH_BITS_ARB); + u->stencil_bits = _sapp_wgl_attrib(n, WGL_STENCIL_BITS_ARB); + if (_sapp_wgl_attrib(n, WGL_DOUBLE_BUFFER_ARB)) { + u->doublebuffer = true; + } + if (_sapp.wgl.arb_multisample) { + u->samples = _sapp_wgl_attrib(n, WGL_SAMPLES_ARB); + } + u->handle = (uintptr_t)n; + usable_count++; + } + SOKOL_ASSERT(usable_count > 0); + _sapp_gl_fbconfig desired; + _sapp_gl_init_fbconfig(&desired); + desired.red_bits = 8; + desired.green_bits = 8; + desired.blue_bits = 8; + desired.alpha_bits = 8; + desired.depth_bits = 24; + desired.stencil_bits = 8; + desired.doublebuffer = true; + desired.samples = _sapp.sample_count > 1 ? _sapp.sample_count : 0; + closest = _sapp_gl_choose_fbconfig(&desired, usable_configs, usable_count); + int pixel_format = 0; + if (closest) { + pixel_format = (int) closest->handle; + } + SOKOL_FREE(usable_configs); + return pixel_format; +} + +_SOKOL_PRIVATE void _sapp_wgl_create_context(void) { + int pixel_format = _sapp_wgl_find_pixel_format(); + if (0 == pixel_format) { + _sapp_fail("WGL: Didn't find matching pixel format.\n"); + } + PIXELFORMATDESCRIPTOR pfd; + if (!DescribePixelFormat(_sapp.win32.dc, pixel_format, sizeof(pfd), &pfd)) { + _sapp_fail("WGL: Failed to retrieve PFD for selected pixel format!\n"); + } + if (!SetPixelFormat(_sapp.win32.dc, pixel_format, &pfd)) { + _sapp_fail("WGL: Failed to set selected pixel format!\n"); + } + if (!_sapp.wgl.arb_create_context) { + _sapp_fail("WGL: ARB_create_context required!\n"); + } + if (!_sapp.wgl.arb_create_context_profile) { + _sapp_fail("WGL: ARB_create_context_profile required!\n"); + } + const int attrs[] = { + WGL_CONTEXT_MAJOR_VERSION_ARB, 3, + WGL_CONTEXT_MINOR_VERSION_ARB, 3, + WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, + WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, + 0, 0 + }; + _sapp.wgl.gl_ctx = _sapp.wgl.CreateContextAttribsARB(_sapp.win32.dc, 0, attrs); + if (!_sapp.wgl.gl_ctx) { + const DWORD err = GetLastError(); + if (err == (0xc0070000 | ERROR_INVALID_VERSION_ARB)) { + _sapp_fail("WGL: Driver does not support OpenGL version 3.3\n"); + } + else if (err == (0xc0070000 | ERROR_INVALID_PROFILE_ARB)) { + _sapp_fail("WGL: Driver does not support the requested OpenGL profile"); + } + else if (err == (0xc0070000 | ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB)) { + _sapp_fail("WGL: The share context is not compatible with the requested context"); + } + else { + _sapp_fail("WGL: Failed to create OpenGL context"); + } + } + _sapp.wgl.MakeCurrent(_sapp.win32.dc, _sapp.wgl.gl_ctx); + if (_sapp.wgl.ext_swap_control) { + /* FIXME: DwmIsCompositionEnabled() (see GLFW) */ + _sapp.wgl.SwapIntervalEXT(_sapp.swap_interval); + } +} + +_SOKOL_PRIVATE void _sapp_wgl_destroy_context(void) { + SOKOL_ASSERT(_sapp.wgl.gl_ctx); + _sapp.wgl.DeleteContext(_sapp.wgl.gl_ctx); + _sapp.wgl.gl_ctx = 0; +} + +_SOKOL_PRIVATE void _sapp_wgl_swap_buffers(void) { + SOKOL_ASSERT(_sapp.win32.dc); + /* FIXME: DwmIsCompositionEnabled? (see GLFW) */ + SwapBuffers(_sapp.win32.dc); +} +#endif /* SOKOL_GLCORE33 */ + +_SOKOL_PRIVATE bool _sapp_win32_wide_to_utf8(const wchar_t* src, char* dst, int dst_num_bytes) { + SOKOL_ASSERT(src && dst && (dst_num_bytes > 1)); + memset(dst, 0, (size_t)dst_num_bytes); + const int bytes_needed = WideCharToMultiByte(CP_UTF8, 0, src, -1, NULL, 0, NULL, NULL); + if (bytes_needed <= dst_num_bytes) { + WideCharToMultiByte(CP_UTF8, 0, src, -1, dst, dst_num_bytes, NULL, NULL); + return true; + } + else { + return false; + } +} + +_SOKOL_PRIVATE void _sapp_win32_toggle_fullscreen(void) { + HMONITOR monitor = MonitorFromWindow(_sapp.win32.hwnd, MONITOR_DEFAULTTONEAREST); + MONITORINFO minfo; + memset(&minfo, 0, sizeof(minfo)); + minfo.cbSize = sizeof(MONITORINFO); + GetMonitorInfo(monitor, &minfo); + const RECT mr = minfo.rcMonitor; + const int monitor_w = mr.right - mr.left; + const int monitor_h = mr.bottom - mr.top; + + const DWORD win_ex_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; + DWORD win_style; + RECT rect = { 0, 0, 0, 0 }; + + _sapp.fullscreen = !_sapp.fullscreen; + if (!_sapp.fullscreen) { + win_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SIZEBOX; + rect.right = (int) ((float)_sapp.desc.width * _sapp.win32.dpi.window_scale); + rect.bottom = (int) ((float)_sapp.desc.height * _sapp.win32.dpi.window_scale); + } + else { + win_style = WS_POPUP | WS_SYSMENU | WS_VISIBLE; + rect.right = monitor_w; + rect.bottom = monitor_h; + } + AdjustWindowRectEx(&rect, win_style, FALSE, win_ex_style); + int win_width = rect.right - rect.left; + int win_height = rect.bottom - rect.top; + if (!_sapp.fullscreen) { + rect.left = (monitor_w - win_width) / 2; + rect.top = (monitor_h - win_height) / 2; + } + + SetWindowLongPtr(_sapp.win32.hwnd, GWL_STYLE, win_style); + SetWindowPos(_sapp.win32.hwnd, HWND_TOP, mr.left + rect.left, mr.top + rect.top, win_width, win_height, SWP_SHOWWINDOW | SWP_FRAMECHANGED); +} + +_SOKOL_PRIVATE void _sapp_win32_show_mouse(bool visible) { + /* NOTE: this function is only called when the mouse visibility actually changes */ + ShowCursor((BOOL)visible); +} + +_SOKOL_PRIVATE void _sapp_win32_capture_mouse(uint8_t btn_mask) { + if (0 == _sapp.win32.mouse_capture_mask) { + SetCapture(_sapp.win32.hwnd); + } + _sapp.win32.mouse_capture_mask |= btn_mask; +} + +_SOKOL_PRIVATE void _sapp_win32_release_mouse(uint8_t btn_mask) { + if (0 != _sapp.win32.mouse_capture_mask) { + _sapp.win32.mouse_capture_mask &= ~btn_mask; + if (0 == _sapp.win32.mouse_capture_mask) { + ReleaseCapture(); + } + } +} + +_SOKOL_PRIVATE void _sapp_win32_lock_mouse(bool lock) { + if (lock == _sapp.mouse.locked) { + return; + } + _sapp.mouse.dx = 0.0f; + _sapp.mouse.dy = 0.0f; + _sapp.mouse.locked = lock; + _sapp_win32_release_mouse(0xFF); + if (_sapp.mouse.locked) { + /* store the current mouse position, so it can be restored when unlocked */ + POINT pos; + BOOL res = GetCursorPos(&pos); + SOKOL_ASSERT(res); _SOKOL_UNUSED(res); + _sapp.win32.mouse_locked_x = pos.x; + _sapp.win32.mouse_locked_y = pos.y; + + /* while the mouse is locked, make the mouse cursor invisible and + confine the mouse movement to a small rectangle inside our window + (so that we dont miss any mouse up events) + */ + RECT client_rect = { + _sapp.win32.mouse_locked_x, + _sapp.win32.mouse_locked_y, + _sapp.win32.mouse_locked_x, + _sapp.win32.mouse_locked_y + }; + ClipCursor(&client_rect); + + /* make the mouse cursor invisible, this will stack with sapp_show_mouse() */ + ShowCursor(FALSE); + + /* enable raw input for mouse, starts sending WM_INPUT messages to WinProc (see GLFW) */ + const RAWINPUTDEVICE rid = { + 0x01, // usUsagePage: HID_USAGE_PAGE_GENERIC + 0x02, // usUsage: HID_USAGE_GENERIC_MOUSE + 0, // dwFlags + _sapp.win32.hwnd // hwndTarget + }; + if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) { + SOKOL_LOG("RegisterRawInputDevices() failed (on mouse lock).\n"); + } + /* in case the raw mouse device only supports absolute position reporting, + we need to skip the dx/dy compution for the first WM_INPUT event + */ + _sapp.win32.raw_input_mousepos_valid = false; + } + else { + /* disable raw input for mouse */ + const RAWINPUTDEVICE rid = { 0x01, 0x02, RIDEV_REMOVE, NULL }; + if (!RegisterRawInputDevices(&rid, 1, sizeof(rid))) { + SOKOL_LOG("RegisterRawInputDevices() failed (on mouse unlock).\n"); + } + + /* let the mouse roam freely again */ + ClipCursor(NULL); + ShowCursor(TRUE); + + /* restore the 'pre-locked' mouse position */ + BOOL res = SetCursorPos(_sapp.win32.mouse_locked_x, _sapp.win32.mouse_locked_y); + SOKOL_ASSERT(res); _SOKOL_UNUSED(res); + } +} + +/* updates current window and framebuffer size from the window's client rect, returns true if size has changed */ +_SOKOL_PRIVATE bool _sapp_win32_update_dimensions(void) { + RECT rect; + if (GetClientRect(_sapp.win32.hwnd, &rect)) { + _sapp.window_width = (int)((float)(rect.right - rect.left) / _sapp.win32.dpi.window_scale); + _sapp.window_height = (int)((float)(rect.bottom - rect.top) / _sapp.win32.dpi.window_scale); + const int fb_width = (int)((float)_sapp.window_width * _sapp.win32.dpi.content_scale); + const int fb_height = (int)((float)_sapp.window_height * _sapp.win32.dpi.content_scale); + if ((fb_width != _sapp.framebuffer_width) || (fb_height != _sapp.framebuffer_height)) { + _sapp.framebuffer_width = fb_width; + _sapp.framebuffer_height = fb_height; + /* prevent a framebuffer size of 0 when window is minimized */ + if (_sapp.framebuffer_width == 0) { + _sapp.framebuffer_width = 1; + } + if (_sapp.framebuffer_height == 0) { + _sapp.framebuffer_height = 1; + } + return true; + } + } + else { + _sapp.window_width = _sapp.window_height = 1; + _sapp.framebuffer_width = _sapp.framebuffer_height = 1; + } + return false; +} + +_SOKOL_PRIVATE uint32_t _sapp_win32_mods(void) { + uint32_t mods = 0; + if (GetKeyState(VK_SHIFT) & (1<<15)) { + mods |= SAPP_MODIFIER_SHIFT; + } + if (GetKeyState(VK_CONTROL) & (1<<15)) { + mods |= SAPP_MODIFIER_CTRL; + } + if (GetKeyState(VK_MENU) & (1<<15)) { + mods |= SAPP_MODIFIER_ALT; + } + if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & (1<<15)) { + mods |= SAPP_MODIFIER_SUPER; + } + return mods; +} + +_SOKOL_PRIVATE void _sapp_win32_mouse_event(sapp_event_type type, sapp_mousebutton btn) { + if (_sapp_events_enabled()) { + _sapp_init_event(type); + _sapp.event.modifiers = _sapp_win32_mods(); + _sapp.event.mouse_button = btn; + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE void _sapp_win32_scroll_event(float x, float y) { + if (_sapp_events_enabled()) { + _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL); + _sapp.event.modifiers = _sapp_win32_mods(); + _sapp.event.scroll_x = -x / 30.0f; + _sapp.event.scroll_y = y / 30.0f; + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE void _sapp_win32_key_event(sapp_event_type type, int vk, bool repeat) { + if (_sapp_events_enabled() && (vk < SAPP_MAX_KEYCODES)) { + _sapp_init_event(type); + _sapp.event.modifiers = _sapp_win32_mods(); + _sapp.event.key_code = _sapp.keycodes[vk]; + _sapp.event.key_repeat = repeat; + _sapp_call_event(&_sapp.event); + /* check if a CLIPBOARD_PASTED event must be sent too */ + if (_sapp.clipboard.enabled && + (type == SAPP_EVENTTYPE_KEY_DOWN) && + (_sapp.event.modifiers == SAPP_MODIFIER_CTRL) && + (_sapp.event.key_code == SAPP_KEYCODE_V)) + { + _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED); + _sapp_call_event(&_sapp.event); + } + } +} + +_SOKOL_PRIVATE void _sapp_win32_char_event(uint32_t c, bool repeat) { + if (_sapp_events_enabled() && (c >= 32)) { + _sapp_init_event(SAPP_EVENTTYPE_CHAR); + _sapp.event.modifiers = _sapp_win32_mods(); + _sapp.event.char_code = c; + _sapp.event.key_repeat = repeat; + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE void _sapp_win32_files_dropped(HDROP hdrop) { + if (!_sapp.drop.enabled) { + return; + } + _sapp_clear_drop_buffer(); + bool drop_failed = false; + const int count = (int) DragQueryFileW(hdrop, 0xffffffff, NULL, 0); + _sapp.drop.num_files = (count > _sapp.drop.max_files) ? _sapp.drop.max_files : count; + for (UINT i = 0; i < (UINT)_sapp.drop.num_files; i++) { + const UINT num_chars = DragQueryFileW(hdrop, i, NULL, 0) + 1; + WCHAR* buffer = (WCHAR*) SOKOL_CALLOC(num_chars, sizeof(WCHAR)); + DragQueryFileW(hdrop, i, buffer, num_chars); + if (!_sapp_win32_wide_to_utf8(buffer, _sapp_dropped_file_path_ptr((int)i), _sapp.drop.max_path_length)) { + SOKOL_LOG("sokol_app.h: dropped file path too long (sapp_desc.max_dropped_file_path_length)\n"); + drop_failed = true; + } + SOKOL_FREE(buffer); + } + DragFinish(hdrop); + if (!drop_failed) { + if (_sapp_events_enabled()) { + _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED); + _sapp_call_event(&_sapp.event); + } + } + else { + _sapp_clear_drop_buffer(); + _sapp.drop.num_files = 0; + } +} + +_SOKOL_PRIVATE LRESULT CALLBACK _sapp_win32_wndproc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + if (!_sapp.win32.in_create_window) { + switch (uMsg) { + case WM_CLOSE: + /* only give user a chance to intervene when sapp_quit() wasn't already called */ + if (!_sapp.quit_ordered) { + /* if window should be closed and event handling is enabled, give user code + a change to intervene via sapp_cancel_quit() + */ + _sapp.quit_requested = true; + _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_QUIT_REQUESTED); + /* if user code hasn't intervened, quit the app */ + if (_sapp.quit_requested) { + _sapp.quit_ordered = true; + } + } + if (_sapp.quit_ordered) { + PostQuitMessage(0); + } + return 0; + case WM_SYSCOMMAND: + switch (wParam & 0xFFF0) { + case SC_SCREENSAVE: + case SC_MONITORPOWER: + if (_sapp.fullscreen) { + /* disable screen saver and blanking in fullscreen mode */ + return 0; + } + break; + case SC_KEYMENU: + /* user trying to access menu via ALT */ + return 0; + } + break; + case WM_ERASEBKGND: + return 1; + case WM_SIZE: + { + const bool iconified = wParam == SIZE_MINIMIZED; + if (iconified != _sapp.win32.iconified) { + _sapp.win32.iconified = iconified; + if (iconified) { + _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_ICONIFIED); + } + else { + _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESTORED); + } + } + } + break; + case WM_SETCURSOR: + if (_sapp.desc.user_cursor) { + if (LOWORD(lParam) == HTCLIENT) { + _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_UPDATE_CURSOR); + return 1; + } + } + break; + case WM_LBUTTONDOWN: + _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_LEFT); + _sapp_win32_capture_mouse(1<data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) { + /* mouse only reports absolute position + NOTE: THIS IS UNTESTED, it's unclear from reading the + Win32 RawInput docs under which circumstances absolute + positions are sent. + */ + if (_sapp.win32.raw_input_mousepos_valid) { + LONG new_x = raw_mouse_data->data.mouse.lLastX; + LONG new_y = raw_mouse_data->data.mouse.lLastY; + _sapp.mouse.dx = (float) (new_x - _sapp.win32.raw_input_mousepos_x); + _sapp.mouse.dy = (float) (new_y - _sapp.win32.raw_input_mousepos_y); + _sapp.win32.raw_input_mousepos_x = new_x; + _sapp.win32.raw_input_mousepos_y = new_y; + _sapp.win32.raw_input_mousepos_valid = true; + } + } + else { + /* mouse reports movement delta (this seems to be the common case) */ + _sapp.mouse.dx = (float) raw_mouse_data->data.mouse.lLastX; + _sapp.mouse.dy = (float) raw_mouse_data->data.mouse.lLastY; + } + _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID); + } + break; + + case WM_MOUSELEAVE: + if (!_sapp.mouse.locked) { + _sapp.win32.mouse_tracked = false; + _sapp_win32_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID); + } + break; + case WM_MOUSEWHEEL: + _sapp_win32_scroll_event(0.0f, (float)((SHORT)HIWORD(wParam))); + break; + case WM_MOUSEHWHEEL: + _sapp_win32_scroll_event((float)((SHORT)HIWORD(wParam)), 0.0f); + break; + case WM_CHAR: + _sapp_win32_char_event((uint32_t)wParam, !!(lParam&0x40000000)); + break; + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + _sapp_win32_key_event(SAPP_EVENTTYPE_KEY_DOWN, (int)(HIWORD(lParam)&0x1FF), !!(lParam&0x40000000)); + break; + case WM_KEYUP: + case WM_SYSKEYUP: + _sapp_win32_key_event(SAPP_EVENTTYPE_KEY_UP, (int)(HIWORD(lParam)&0x1FF), false); + break; + case WM_ENTERSIZEMOVE: + SetTimer(_sapp.win32.hwnd, 1, USER_TIMER_MINIMUM, NULL); + break; + case WM_EXITSIZEMOVE: + KillTimer(_sapp.win32.hwnd, 1); + break; + case WM_TIMER: + _sapp_frame(); + #if defined(SOKOL_D3D11) + _sapp_d3d11_present(); + #endif + #if defined(SOKOL_GLCORE33) + _sapp_wgl_swap_buffers(); + #endif + /* NOTE: resizing the swap-chain during resize leads to a substantial + memory spike (hundreds of megabytes for a few seconds). + + if (_sapp_win32_update_dimensions()) { + #if defined(SOKOL_D3D11) + _sapp_d3d11_resize_default_render_target(); + #endif + _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED); + } + */ + break; + case WM_DROPFILES: + _sapp_win32_files_dropped((HDROP)wParam); + break; + default: + break; + } + } + return DefWindowProcW(hWnd, uMsg, wParam, lParam); +} + +_SOKOL_PRIVATE void _sapp_win32_create_window(void) { + WNDCLASSW wndclassw; + memset(&wndclassw, 0, sizeof(wndclassw)); + wndclassw.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + wndclassw.lpfnWndProc = (WNDPROC) _sapp_win32_wndproc; + wndclassw.hInstance = GetModuleHandleW(NULL); + wndclassw.hCursor = LoadCursor(NULL, IDC_ARROW); + wndclassw.hIcon = LoadIcon(NULL, IDI_WINLOGO); + wndclassw.lpszClassName = L"SOKOLAPP"; + RegisterClassW(&wndclassw); + + DWORD win_style; + const DWORD win_ex_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; + RECT rect = { 0, 0, 0, 0 }; + if (_sapp.fullscreen) { + win_style = WS_POPUP | WS_SYSMENU | WS_VISIBLE; + rect.right = GetSystemMetrics(SM_CXSCREEN); + rect.bottom = GetSystemMetrics(SM_CYSCREEN); + } + else { + win_style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SIZEBOX; + rect.right = (int) ((float)_sapp.window_width * _sapp.win32.dpi.window_scale); + rect.bottom = (int) ((float)_sapp.window_height * _sapp.win32.dpi.window_scale); + } + AdjustWindowRectEx(&rect, win_style, FALSE, win_ex_style); + const int win_width = rect.right - rect.left; + const int win_height = rect.bottom - rect.top; + _sapp.win32.in_create_window = true; + _sapp.win32.hwnd = CreateWindowExW( + win_ex_style, /* dwExStyle */ + L"SOKOLAPP", /* lpClassName */ + _sapp.window_title_wide, /* lpWindowName */ + win_style, /* dwStyle */ + CW_USEDEFAULT, /* X */ + CW_USEDEFAULT, /* Y */ + win_width, /* nWidth */ + win_height, /* nHeight */ + NULL, /* hWndParent */ + NULL, /* hMenu */ + GetModuleHandle(NULL), /* hInstance */ + NULL); /* lParam */ + ShowWindow(_sapp.win32.hwnd, SW_SHOW); + _sapp.win32.in_create_window = false; + _sapp.win32.dc = GetDC(_sapp.win32.hwnd); + SOKOL_ASSERT(_sapp.win32.dc); + _sapp_win32_update_dimensions(); + + DragAcceptFiles(_sapp.win32.hwnd, 1); +} + +_SOKOL_PRIVATE void _sapp_win32_destroy_window(void) { + DestroyWindow(_sapp.win32.hwnd); _sapp.win32.hwnd = 0; + UnregisterClassW(L"SOKOLAPP", GetModuleHandleW(NULL)); +} + +_SOKOL_PRIVATE void _sapp_win32_init_console(void) { + if (_sapp.desc.win32_console_create || _sapp.desc.win32_console_attach) { + BOOL con_valid = FALSE; + if (_sapp.desc.win32_console_create) { + con_valid = AllocConsole(); + } + else if (_sapp.desc.win32_console_attach) { + con_valid = AttachConsole(ATTACH_PARENT_PROCESS); + } + if (con_valid) { + FILE* res_fp = 0; + errno_t err; + err = freopen_s(&res_fp, "CON", "w", stdout); + err = freopen_s(&res_fp, "CON", "w", stderr); + (void)err; + } + } + if (_sapp.desc.win32_console_utf8) { + _sapp.win32.orig_codepage = GetConsoleOutputCP(); + SetConsoleOutputCP(CP_UTF8); + } +} + +_SOKOL_PRIVATE void _sapp_win32_restore_console(void) { + if (_sapp.desc.win32_console_utf8) { + SetConsoleOutputCP(_sapp.win32.orig_codepage); + } +} + +_SOKOL_PRIVATE void _sapp_win32_init_dpi(void) { + + typedef BOOL(WINAPI * SETPROCESSDPIAWARE_T)(void); + typedef HRESULT(WINAPI * SETPROCESSDPIAWARENESS_T)(PROCESS_DPI_AWARENESS); + typedef HRESULT(WINAPI * GETDPIFORMONITOR_T)(HMONITOR, MONITOR_DPI_TYPE, UINT*, UINT*); + + SETPROCESSDPIAWARE_T fn_setprocessdpiaware = 0; + SETPROCESSDPIAWARENESS_T fn_setprocessdpiawareness = 0; + GETDPIFORMONITOR_T fn_getdpiformonitor = 0; + HINSTANCE user32 = LoadLibraryA("user32.dll"); + if (user32) { + fn_setprocessdpiaware = (SETPROCESSDPIAWARE_T)(void*) GetProcAddress(user32, "SetProcessDPIAware"); + } + HINSTANCE shcore = LoadLibraryA("shcore.dll"); + if (shcore) { + fn_setprocessdpiawareness = (SETPROCESSDPIAWARENESS_T)(void*) GetProcAddress(shcore, "SetProcessDpiAwareness"); + fn_getdpiformonitor = (GETDPIFORMONITOR_T)(void*) GetProcAddress(shcore, "GetDpiForMonitor"); + } + if (fn_setprocessdpiawareness) { + /* if the app didn't request HighDPI rendering, let Windows do the upscaling */ + PROCESS_DPI_AWARENESS process_dpi_awareness = PROCESS_SYSTEM_DPI_AWARE; + _sapp.win32.dpi.aware = true; + if (!_sapp.desc.high_dpi) { + process_dpi_awareness = PROCESS_DPI_UNAWARE; + _sapp.win32.dpi.aware = false; + } + fn_setprocessdpiawareness(process_dpi_awareness); + } + else if (fn_setprocessdpiaware) { + fn_setprocessdpiaware(); + _sapp.win32.dpi.aware = true; + } + /* get dpi scale factor for main monitor */ + if (fn_getdpiformonitor && _sapp.win32.dpi.aware) { + POINT pt = { 1, 1 }; + HMONITOR hm = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST); + UINT dpix, dpiy; + HRESULT hr = fn_getdpiformonitor(hm, MDT_EFFECTIVE_DPI, &dpix, &dpiy); + _SOKOL_UNUSED(hr); + SOKOL_ASSERT(SUCCEEDED(hr)); + /* clamp window scale to an integer factor */ + _sapp.win32.dpi.window_scale = (float)dpix / 96.0f; + } + else { + _sapp.win32.dpi.window_scale = 1.0f; + } + if (_sapp.desc.high_dpi) { + _sapp.win32.dpi.content_scale = _sapp.win32.dpi.window_scale; + _sapp.win32.dpi.mouse_scale = 1.0f; + } + else { + _sapp.win32.dpi.content_scale = 1.0f; + _sapp.win32.dpi.mouse_scale = 1.0f / _sapp.win32.dpi.window_scale; + } + _sapp.dpi_scale = _sapp.win32.dpi.content_scale; + if (user32) { + FreeLibrary(user32); + } + if (shcore) { + FreeLibrary(shcore); + } +} + +_SOKOL_PRIVATE bool _sapp_win32_set_clipboard_string(const char* str) { + SOKOL_ASSERT(str); + SOKOL_ASSERT(_sapp.win32.hwnd); + SOKOL_ASSERT(_sapp.clipboard.enabled && (_sapp.clipboard.buf_size > 0)); + + wchar_t* wchar_buf = 0; + const SIZE_T wchar_buf_size = (SIZE_T)_sapp.clipboard.buf_size * sizeof(wchar_t); + HANDLE object = GlobalAlloc(GMEM_MOVEABLE, wchar_buf_size); + if (!object) { + goto error; + } + wchar_buf = (wchar_t*) GlobalLock(object); + if (!wchar_buf) { + goto error; + } + if (!_sapp_win32_uwp_utf8_to_wide(str, wchar_buf, (int)wchar_buf_size)) { + goto error; + } + GlobalUnlock(wchar_buf); + wchar_buf = 0; + if (!OpenClipboard(_sapp.win32.hwnd)) { + goto error; + } + EmptyClipboard(); + SetClipboardData(CF_UNICODETEXT, object); + CloseClipboard(); + return true; + +error: + if (wchar_buf) { + GlobalUnlock(object); + } + if (object) { + GlobalFree(object); + } + return false; +} + +_SOKOL_PRIVATE const char* _sapp_win32_get_clipboard_string(void) { + SOKOL_ASSERT(_sapp.clipboard.enabled && _sapp.clipboard.buffer); + SOKOL_ASSERT(_sapp.win32.hwnd); + if (!OpenClipboard(_sapp.win32.hwnd)) { + /* silently ignore any errors and just return the current + content of the local clipboard buffer + */ + return _sapp.clipboard.buffer; + } + HANDLE object = GetClipboardData(CF_UNICODETEXT); + if (!object) { + CloseClipboard(); + return _sapp.clipboard.buffer; + } + const wchar_t* wchar_buf = (const wchar_t*) GlobalLock(object); + if (!wchar_buf) { + CloseClipboard(); + return _sapp.clipboard.buffer; + } + if (!_sapp_win32_wide_to_utf8(wchar_buf, _sapp.clipboard.buffer, _sapp.clipboard.buf_size)) { + SOKOL_LOG("sokol_app.h: clipboard string didn't fit into clipboard buffer\n"); + } + GlobalUnlock(object); + CloseClipboard(); + return _sapp.clipboard.buffer; +} + +_SOKOL_PRIVATE void _sapp_win32_update_window_title(void) { + _sapp_win32_uwp_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide)); + SetWindowTextW(_sapp.win32.hwnd, _sapp.window_title_wide); +} + +/* don't laugh, but this seems to be the easiest and most robust + way to check if we're running on Win10 + + From: https://github.com/videolan/vlc/blob/232fb13b0d6110c4d1b683cde24cf9a7f2c5c2ea/modules/video_output/win32/d3d11_swapchain.c#L263 +*/ +_SOKOL_PRIVATE bool _sapp_win32_is_win10_or_greater(void) { + HMODULE h = GetModuleHandleW(L"kernel32.dll"); + if (NULL != h) { + return (NULL != GetProcAddress(h, "GetSystemCpuSetInformation")); + } + else { + return false; + } +} + +_SOKOL_PRIVATE void _sapp_win32_run(const sapp_desc* desc) { + _sapp_init_state(desc); + _sapp_win32_init_console(); + _sapp.win32.is_win10_or_greater = _sapp_win32_is_win10_or_greater(); + _sapp_win32_uwp_init_keytable(); + _sapp_win32_uwp_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide)); + _sapp_win32_init_dpi(); + _sapp_win32_create_window(); + #if defined(SOKOL_D3D11) + _sapp_d3d11_create_device_and_swapchain(); + _sapp_d3d11_create_default_render_target(); + #endif + #if defined(SOKOL_GLCORE33) + _sapp_wgl_init(); + _sapp_wgl_load_extensions(); + _sapp_wgl_create_context(); + #endif + _sapp.valid = true; + + bool done = false; + while (!(done || _sapp.quit_ordered)) { + MSG msg; + while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) { + if (WM_QUIT == msg.message) { + done = true; + continue; + } + else { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + _sapp_frame(); + #if defined(SOKOL_D3D11) + _sapp_d3d11_present(); + if (IsIconic(_sapp.win32.hwnd)) { + Sleep((DWORD)(16 * _sapp.swap_interval)); + } + #endif + #if defined(SOKOL_GLCORE33) + _sapp_wgl_swap_buffers(); + #endif + /* check for window resized, this cannot happen in WM_SIZE as it explodes memory usage */ + if (_sapp_win32_update_dimensions()) { + #if defined(SOKOL_D3D11) + _sapp_d3d11_resize_default_render_target(); + #endif + _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED); + } + if (_sapp.quit_requested) { + PostMessage(_sapp.win32.hwnd, WM_CLOSE, 0, 0); + } + } + _sapp_call_cleanup(); + + #if defined(SOKOL_D3D11) + _sapp_d3d11_destroy_default_render_target(); + _sapp_d3d11_destroy_device_and_swapchain(); + #else + _sapp_wgl_destroy_context(); + _sapp_wgl_shutdown(); + #endif + _sapp_win32_destroy_window(); + _sapp_win32_restore_console(); + _sapp_discard_state(); +} + +_SOKOL_PRIVATE char** _sapp_win32_command_line_to_utf8_argv(LPWSTR w_command_line, int* o_argc) { + int argc = 0; + char** argv = 0; + char* args; + + LPWSTR* w_argv = CommandLineToArgvW(w_command_line, &argc); + if (w_argv == NULL) { + _sapp_fail("Win32: failed to parse command line"); + } else { + size_t size = wcslen(w_command_line) * 4; + argv = (char**) SOKOL_CALLOC(1, ((size_t)argc + 1) * sizeof(char*) + size); + SOKOL_ASSERT(argv); + args = (char*) &argv[argc + 1]; + int n; + for (int i = 0; i < argc; ++i) { + n = WideCharToMultiByte(CP_UTF8, 0, w_argv[i], -1, args, (int)size, NULL, NULL); + if (n == 0) { + _sapp_fail("Win32: failed to convert all arguments to utf8"); + break; + } + argv[i] = args; + size -= (size_t)n; + args += n; + } + LocalFree(w_argv); + } + *o_argc = argc; + return argv; +} + +#if !defined(SOKOL_NO_ENTRY) +#if defined(SOKOL_WIN32_FORCE_MAIN) +int main(int argc, char* argv[]) { + sapp_desc desc = sokol_main(argc, argv); + _sapp_win32_run(&desc); + return 0; +} +#else +int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) { + _SOKOL_UNUSED(hInstance); + _SOKOL_UNUSED(hPrevInstance); + _SOKOL_UNUSED(lpCmdLine); + _SOKOL_UNUSED(nCmdShow); + int argc_utf8 = 0; + char** argv_utf8 = _sapp_win32_command_line_to_utf8_argv(GetCommandLineW(), &argc_utf8); + sapp_desc desc = sokol_main(argc_utf8, argv_utf8); + _sapp_win32_run(&desc); + SOKOL_FREE(argv_utf8); + return 0; +} +#endif /* SOKOL_WIN32_FORCE_MAIN */ +#endif /* SOKOL_NO_ENTRY */ + +#ifdef _MSC_VER + #pragma warning(pop) +#endif + +#endif /* _SAPP_WIN32 */ + +/*== UWP ================================================================*/ +#if defined(_SAPP_UWP) + +// Helper functions +_SOKOL_PRIVATE void _sapp_uwp_configure_dpi(float monitor_dpi) { + _sapp.uwp.dpi.window_scale = monitor_dpi / 96.0f; + if (_sapp.desc.high_dpi) { + _sapp.uwp.dpi.content_scale = _sapp.uwp.dpi.window_scale; + _sapp.uwp.dpi.mouse_scale = 1.0f * _sapp.uwp.dpi.window_scale; + } + else { + _sapp.uwp.dpi.content_scale = 1.0f; + _sapp.uwp.dpi.mouse_scale = 1.0f; + } + _sapp.dpi_scale = _sapp.uwp.dpi.content_scale; +} + +_SOKOL_PRIVATE void _sapp_uwp_show_mouse(bool visible) { + using namespace winrt::Windows::UI::Core; + + /* NOTE: this function is only called when the mouse visibility actually changes */ + CoreWindow::GetForCurrentThread().PointerCursor(visible ? + CoreCursor(CoreCursorType::Arrow, 0) : + CoreCursor(nullptr)); +} + +_SOKOL_PRIVATE uint32_t _sapp_uwp_mods(winrt::Windows::UI::Core::CoreWindow const& sender_window) { + using namespace winrt::Windows::System; + using namespace winrt::Windows::UI::Core; + + uint32_t mods = 0; + if ((sender_window.GetKeyState(VirtualKey::Shift) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down) { + mods |= SAPP_MODIFIER_SHIFT; + } + if ((sender_window.GetKeyState(VirtualKey::Control) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down) { + mods |= SAPP_MODIFIER_CTRL; + } + if ((sender_window.GetKeyState(VirtualKey::Menu) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down) { + mods |= SAPP_MODIFIER_ALT; + } + if (((sender_window.GetKeyState(VirtualKey::LeftWindows) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down) || + ((sender_window.GetKeyState(VirtualKey::RightWindows) & CoreVirtualKeyStates::Down) == CoreVirtualKeyStates::Down)) + { + mods |= SAPP_MODIFIER_SUPER; + } + return mods; +} + +_SOKOL_PRIVATE void _sapp_uwp_mouse_event(sapp_event_type type, sapp_mousebutton btn, winrt::Windows::UI::Core::CoreWindow const& sender_window) { + if (_sapp_events_enabled()) { + _sapp_init_event(type); + _sapp.event.modifiers = _sapp_uwp_mods(sender_window); + _sapp.event.mouse_button = btn; + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE void _sapp_uwp_scroll_event(float delta, bool horizontal, winrt::Windows::UI::Core::CoreWindow const& sender_window) { + if (_sapp_events_enabled()) { + _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL); + _sapp.event.modifiers = _sapp_uwp_mods(sender_window); + _sapp.event.scroll_x = horizontal ? (-delta / 30.0f) : 0.0f; + _sapp.event.scroll_y = horizontal ? 0.0f : (delta / 30.0f); + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE void _sapp_uwp_extract_mouse_button_events(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) { + + // we need to figure out ourselves what mouse buttons have been pressed and released, + // because UWP doesn't properly send down/up mouse button events when multiple buttons + // are pressed down, so we also need to check the mouse button state in other mouse events + // to track what buttons have been pressed down and released + // + auto properties = args.CurrentPoint().Properties(); + const uint8_t lmb_bit = (1 << SAPP_MOUSEBUTTON_LEFT); + const uint8_t rmb_bit = (1 << SAPP_MOUSEBUTTON_RIGHT); + const uint8_t mmb_bit = (1 << SAPP_MOUSEBUTTON_MIDDLE); + uint8_t new_btns = 0; + if (properties.IsLeftButtonPressed()) { + new_btns |= lmb_bit; + } + if (properties.IsRightButtonPressed()) { + new_btns |= rmb_bit; + } + if (properties.IsMiddleButtonPressed()) { + new_btns |= mmb_bit; + } + const uint8_t old_btns = _sapp.uwp.mouse_buttons; + const uint8_t chg_btns = new_btns ^ old_btns; + + _sapp.uwp.mouse_buttons = new_btns; + + sapp_event_type type = SAPP_EVENTTYPE_INVALID; + sapp_mousebutton btn = SAPP_MOUSEBUTTON_INVALID; + if (chg_btns & lmb_bit) { + btn = SAPP_MOUSEBUTTON_LEFT; + type = (new_btns & lmb_bit) ? SAPP_EVENTTYPE_MOUSE_DOWN : SAPP_EVENTTYPE_MOUSE_UP; + } + if (chg_btns & rmb_bit) { + btn = SAPP_MOUSEBUTTON_RIGHT; + type = (new_btns & rmb_bit) ? SAPP_EVENTTYPE_MOUSE_DOWN : SAPP_EVENTTYPE_MOUSE_UP; + } + if (chg_btns & mmb_bit) { + btn = SAPP_MOUSEBUTTON_MIDDLE; + type = (new_btns & mmb_bit) ? SAPP_EVENTTYPE_MOUSE_DOWN : SAPP_EVENTTYPE_MOUSE_UP; + } + if (type != SAPP_EVENTTYPE_INVALID) { + _sapp_uwp_mouse_event(type, btn, sender); + } +} + +_SOKOL_PRIVATE void _sapp_uwp_key_event(sapp_event_type type, winrt::Windows::UI::Core::CoreWindow const& sender_window, winrt::Windows::UI::Core::KeyEventArgs const& args) { + auto key_status = args.KeyStatus(); + uint32_t ext_scan_code = key_status.ScanCode | (key_status.IsExtendedKey ? 0x100 : 0); + if (_sapp_events_enabled() && (ext_scan_code < SAPP_MAX_KEYCODES)) { + _sapp_init_event(type); + _sapp.event.modifiers = _sapp_uwp_mods(sender_window); + _sapp.event.key_code = _sapp.keycodes[ext_scan_code]; + _sapp.event.key_repeat = type == SAPP_EVENTTYPE_KEY_UP ? false : key_status.WasKeyDown; + _sapp_call_event(&_sapp.event); + /* check if a CLIPBOARD_PASTED event must be sent too */ + if (_sapp.clipboard.enabled && + (type == SAPP_EVENTTYPE_KEY_DOWN) && + (_sapp.event.modifiers == SAPP_MODIFIER_CTRL) && + (_sapp.event.key_code == SAPP_KEYCODE_V)) + { + _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED); + _sapp_call_event(&_sapp.event); + } + } +} + +_SOKOL_PRIVATE void _sapp_uwp_char_event(uint32_t c, bool repeat, winrt::Windows::UI::Core::CoreWindow const& sender_window) { + if (_sapp_events_enabled() && (c >= 32)) { + _sapp_init_event(SAPP_EVENTTYPE_CHAR); + _sapp.event.modifiers = _sapp_uwp_mods(sender_window); + _sapp.event.char_code = c; + _sapp.event.key_repeat = repeat; + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE void _sapp_uwp_toggle_fullscreen(void) { + auto appView = winrt::Windows::UI::ViewManagement::ApplicationView::GetForCurrentView(); + _sapp.fullscreen = appView.IsFullScreenMode(); + if (!_sapp.fullscreen) { + appView.TryEnterFullScreenMode(); + } + else { + appView.ExitFullScreenMode(); + } + _sapp.fullscreen = appView.IsFullScreenMode(); +} + +namespace {/* Empty namespace to ensure internal linkage (same as _SOKOL_PRIVATE) */ + +// Controls all the DirectX device resources. +class DeviceResources { +public: + // Provides an interface for an application that owns DeviceResources to be notified of the device being lost or created. + interface IDeviceNotify { + virtual void OnDeviceLost() = 0; + virtual void OnDeviceRestored() = 0; + }; + + DeviceResources(); + ~DeviceResources(); + void SetWindow(winrt::Windows::UI::Core::CoreWindow const& window); + void SetLogicalSize(winrt::Windows::Foundation::Size logicalSize); + void SetCurrentOrientation(winrt::Windows::Graphics::Display::DisplayOrientations currentOrientation); + void SetDpi(float dpi); + void ValidateDevice(); + void HandleDeviceLost(); + void RegisterDeviceNotify(IDeviceNotify* deviceNotify); + void Trim(); + void Present(); + +private: + + // Swapchain Rotation Matrices (Z-rotation) + static inline const DirectX::XMFLOAT4X4 DeviceResources::m_rotation0 = { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + static inline const DirectX::XMFLOAT4X4 DeviceResources::m_rotation90 = { + 0.0f, 1.0f, 0.0f, 0.0f, + -1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + static inline const DirectX::XMFLOAT4X4 DeviceResources::m_rotation180 = { + -1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, -1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + static inline const DirectX::XMFLOAT4X4 DeviceResources::m_rotation270 = { + 0.0f, -1.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + + void CreateDeviceResources(); + void CreateWindowSizeDependentResources(); + void UpdateRenderTargetSize(); + DXGI_MODE_ROTATION ComputeDisplayRotation(); + bool SdkLayersAvailable(); + + // Direct3D objects. + winrt::com_ptr m_d3dDevice; + winrt::com_ptr m_d3dContext; + winrt::com_ptr m_swapChain; + + // Direct3D rendering objects. Required for 3D. + winrt::com_ptr m_d3dRenderTarget; + winrt::com_ptr m_d3dRenderTargetView; + winrt::com_ptr m_d3dMSAARenderTarget; + winrt::com_ptr m_d3dMSAARenderTargetView; + winrt::com_ptr m_d3dDepthStencil; + winrt::com_ptr m_d3dDepthStencilView; + D3D11_VIEWPORT m_screenViewport = { }; + + // Cached reference to the Window. + winrt::agile_ref< winrt::Windows::UI::Core::CoreWindow> m_window; + + // Cached device properties. + D3D_FEATURE_LEVEL m_d3dFeatureLevel = D3D_FEATURE_LEVEL_9_1; + winrt::Windows::Foundation::Size m_d3dRenderTargetSize = { }; + winrt::Windows::Foundation::Size m_outputSize = { }; + winrt::Windows::Foundation::Size m_logicalSize = { }; + winrt::Windows::Graphics::Display::DisplayOrientations m_nativeOrientation = winrt::Windows::Graphics::Display::DisplayOrientations::None; + winrt::Windows::Graphics::Display::DisplayOrientations m_currentOrientation = winrt::Windows::Graphics::Display::DisplayOrientations::None; + float m_dpi = -1.0f; + + // Transforms used for display orientation. + DirectX::XMFLOAT4X4 m_orientationTransform3D; + + // The IDeviceNotify can be held directly as it owns the DeviceResources. + IDeviceNotify* m_deviceNotify = nullptr; +}; + +// Main entry point for our app. Connects the app with the Windows shell and handles application lifecycle events. +struct App : winrt::implements { +public: + // IFrameworkViewSource Methods + winrt::Windows::ApplicationModel::Core::IFrameworkView CreateView() { return *this; } + + // IFrameworkView Methods. + virtual void Initialize(winrt::Windows::ApplicationModel::Core::CoreApplicationView const& applicationView); + virtual void SetWindow(winrt::Windows::UI::Core::CoreWindow const& window); + virtual void Load(winrt::hstring const& entryPoint); + virtual void Run(); + virtual void Uninitialize(); + +protected: + // Application lifecycle event handlers + void OnActivated(winrt::Windows::ApplicationModel::Core::CoreApplicationView const& applicationView, winrt::Windows::ApplicationModel::Activation::IActivatedEventArgs const& args); + void OnSuspending(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::ApplicationModel::SuspendingEventArgs const& args); + void OnResuming(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::Foundation::IInspectable const& args); + + // Window event handlers + void OnWindowSizeChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::WindowSizeChangedEventArgs const& args); + void OnVisibilityChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::VisibilityChangedEventArgs const& args); + + // Navigation event handlers + void OnBackRequested(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Core::BackRequestedEventArgs const& args); + + // Input event handlers + void OnKeyDown(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::KeyEventArgs const& args); + void OnKeyUp(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::KeyEventArgs const& args); + void OnCharacterReceived(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::CharacterReceivedEventArgs const& args); + + // Pointer event handlers + void OnPointerEntered(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args); + void OnPointerExited(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args); + void OnPointerPressed(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args); + void OnPointerReleased(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args); + void OnPointerMoved(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args); + void OnPointerWheelChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args); + + // DisplayInformation event handlers. + void OnDpiChanged(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args); + void OnOrientationChanged(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args); + void OnDisplayContentsInvalidated(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args); + +private: + std::unique_ptr m_deviceResources; + bool m_windowVisible = true; +}; + +DeviceResources::DeviceResources() { + CreateDeviceResources(); +} + +DeviceResources::~DeviceResources() { + // Cleanup Sokol Context + _sapp.d3d11.device = nullptr; + _sapp.d3d11.device_context = nullptr; +} + +void DeviceResources::CreateDeviceResources() { + // This flag adds support for surfaces with a different color channel ordering + // than the API default. It is required for compatibility with Direct2D. + UINT creationFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; + + #if defined(_DEBUG) + if (SdkLayersAvailable()) { + // If the project is in a debug build, enable debugging via SDK Layers with this flag. + creationFlags |= D3D11_CREATE_DEVICE_DEBUG; + } + #endif + + // This array defines the set of DirectX hardware feature levels this app will support. + // Note the ordering should be preserved. + // Don't forget to declare your application's minimum required feature level in its + // description. All applications are assumed to support 9.1 unless otherwise stated. + D3D_FEATURE_LEVEL featureLevels[] = { + D3D_FEATURE_LEVEL_12_1, + D3D_FEATURE_LEVEL_12_0, + D3D_FEATURE_LEVEL_11_1, + D3D_FEATURE_LEVEL_11_0, + D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0, + D3D_FEATURE_LEVEL_9_3, + D3D_FEATURE_LEVEL_9_2, + D3D_FEATURE_LEVEL_9_1 + }; + + // Create the Direct3D 11 API device object and a corresponding context. + winrt::com_ptr device; + winrt::com_ptr context; + + HRESULT hr = D3D11CreateDevice( + nullptr, // Specify nullptr to use the default adapter. + D3D_DRIVER_TYPE_HARDWARE, // Create a device using the hardware graphics driver. + 0, // Should be 0 unless the driver is D3D_DRIVER_TYPE_SOFTWARE. + creationFlags, // Set debug and Direct2D compatibility flags. + featureLevels, // List of feature levels this app can support. + ARRAYSIZE(featureLevels), // Size of the list above. + D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for Microsoft Store apps. + device.put(), // Returns the Direct3D device created. + &m_d3dFeatureLevel, // Returns feature level of device created. + context.put() // Returns the device immediate context. + ); + + if (FAILED(hr)) { + // If the initialization fails, fall back to the WARP device. + // For more information on WARP, see: + // https://go.microsoft.com/fwlink/?LinkId=286690 + winrt::check_hresult( + D3D11CreateDevice( + nullptr, + D3D_DRIVER_TYPE_WARP, // Create a WARP device instead of a hardware device. + 0, + creationFlags, + featureLevels, + ARRAYSIZE(featureLevels), + D3D11_SDK_VERSION, + device.put(), + &m_d3dFeatureLevel, + context.put() + ) + ); + } + + // Store pointers to the Direct3D 11.3 API device and immediate context. + m_d3dDevice = device.as(); + m_d3dContext = context.as(); + + // Setup Sokol Context + _sapp.d3d11.device = m_d3dDevice.get(); + _sapp.d3d11.device_context = m_d3dContext.get(); +} + +void DeviceResources::CreateWindowSizeDependentResources() { + // Cleanup Sokol Context (these are non-owning raw pointers) + _sapp.d3d11.rt = nullptr; + _sapp.d3d11.rtv = nullptr; + _sapp.d3d11.msaa_rt = nullptr; + _sapp.d3d11.msaa_rtv = nullptr; + _sapp.d3d11.ds = nullptr; + _sapp.d3d11.dsv = nullptr; + + // Clear the previous window size specific context. + ID3D11RenderTargetView* nullViews[] = { nullptr }; + m_d3dContext->OMSetRenderTargets(ARRAYSIZE(nullViews), nullViews, nullptr); + // these are smart pointers, setting to nullptr will delete the objects + m_d3dRenderTarget = nullptr; + m_d3dRenderTargetView = nullptr; + m_d3dMSAARenderTarget = nullptr; + m_d3dMSAARenderTargetView = nullptr; + m_d3dDepthStencilView = nullptr; + m_d3dDepthStencil = nullptr; + m_d3dContext->Flush1(D3D11_CONTEXT_TYPE_ALL, nullptr); + + UpdateRenderTargetSize(); + + // The width and height of the swap chain must be based on the window's + // natively-oriented width and height. If the window is not in the native + // orientation, the dimensions must be reversed. + DXGI_MODE_ROTATION displayRotation = ComputeDisplayRotation(); + + bool swapDimensions = displayRotation == DXGI_MODE_ROTATION_ROTATE90 || displayRotation == DXGI_MODE_ROTATION_ROTATE270; + m_d3dRenderTargetSize.Width = swapDimensions ? m_outputSize.Height : m_outputSize.Width; + m_d3dRenderTargetSize.Height = swapDimensions ? m_outputSize.Width : m_outputSize.Height; + + if (m_swapChain != nullptr) { + // If the swap chain already exists, resize it. + HRESULT hr = m_swapChain->ResizeBuffers( + 2, // Double-buffered swap chain. + lround(m_d3dRenderTargetSize.Width), + lround(m_d3dRenderTargetSize.Height), + DXGI_FORMAT_B8G8R8A8_UNORM, + 0 + ); + + if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) { + // If the device was removed for any reason, a new device and swap chain will need to be created. + HandleDeviceLost(); + + // Everything is set up now. Do not continue execution of this method. HandleDeviceLost will reenter this method + // and correctly set up the new device. + return; + } + else { + winrt::check_hresult(hr); + } + } + else { + // Otherwise, create a new one using the same adapter as the existing Direct3D device. + DXGI_SCALING scaling = (_sapp.uwp.dpi.content_scale == _sapp.uwp.dpi.window_scale) ? DXGI_SCALING_NONE : DXGI_SCALING_STRETCH; + DXGI_SWAP_CHAIN_DESC1 swapChainDesc = { 0 }; + + swapChainDesc.Width = lround(m_d3dRenderTargetSize.Width); // Match the size of the window. + swapChainDesc.Height = lround(m_d3dRenderTargetSize.Height); + swapChainDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; // This is the most common swap chain format. + swapChainDesc.Stereo = false; + swapChainDesc.SampleDesc.Count = 1; // Don't use multi-sampling. + swapChainDesc.SampleDesc.Quality = 0; + swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapChainDesc.BufferCount = 2; // Use double-buffering to minimize latency. + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; // All Microsoft Store apps must use this SwapEffect. + swapChainDesc.Flags = 0; + swapChainDesc.Scaling = scaling; + swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_IGNORE; + + // This sequence obtains the DXGI factory that was used to create the Direct3D device above. + winrt::com_ptr dxgiDevice = m_d3dDevice.as(); + winrt::com_ptr dxgiAdapter; + winrt::check_hresult(dxgiDevice->GetAdapter(dxgiAdapter.put())); + winrt::com_ptr dxgiFactory; + winrt::check_hresult(dxgiAdapter->GetParent(__uuidof(IDXGIFactory4), dxgiFactory.put_void())); + winrt::com_ptr swapChain; + winrt::check_hresult(dxgiFactory->CreateSwapChainForCoreWindow(m_d3dDevice.get(), m_window.get().as<::IUnknown>().get(), &swapChainDesc, nullptr, swapChain.put())); + m_swapChain = swapChain.as(); + + // Ensure that DXGI does not queue more than one frame at a time. This both reduces latency and + // ensures that the application will only render after each VSync, minimizing power consumption. + winrt::check_hresult(dxgiDevice->SetMaximumFrameLatency(1)); + + // Setup Sokol Context + winrt::check_hresult(swapChain->GetDesc(&_sapp.d3d11.swap_chain_desc)); + _sapp.d3d11.swap_chain = m_swapChain.as().detach(); + } + + // Set the proper orientation for the swap chain, and generate 2D and + // 3D matrix transformations for rendering to the rotated swap chain. + // Note the rotation angle for the 2D and 3D transforms are different. + // This is due to the difference in coordinate spaces. Additionally, + // the 3D matrix is specified explicitly to avoid rounding errors. + switch (displayRotation) { + case DXGI_MODE_ROTATION_IDENTITY: + m_orientationTransform3D = m_rotation0; + break; + + case DXGI_MODE_ROTATION_ROTATE90: + m_orientationTransform3D = m_rotation270; + break; + + case DXGI_MODE_ROTATION_ROTATE180: + m_orientationTransform3D = m_rotation180; + break; + + case DXGI_MODE_ROTATION_ROTATE270: + m_orientationTransform3D = m_rotation90; + break; + } + winrt::check_hresult(m_swapChain->SetRotation(displayRotation)); + + // Create a render target view of the swap chain back buffer. + winrt::check_hresult(m_swapChain->GetBuffer(0, IID_PPV_ARGS(&m_d3dRenderTarget))); + winrt::check_hresult(m_d3dDevice->CreateRenderTargetView1(m_d3dRenderTarget.get(), nullptr, m_d3dRenderTargetView.put())); + + // Create MSAA texture and view if needed + if (_sapp.sample_count > 1) { + CD3D11_TEXTURE2D_DESC1 msaaTexDesc( + DXGI_FORMAT_B8G8R8A8_UNORM, + lround(m_d3dRenderTargetSize.Width), + lround(m_d3dRenderTargetSize.Height), + 1, // arraySize + 1, // mipLevels + D3D11_BIND_RENDER_TARGET, + D3D11_USAGE_DEFAULT, + 0, // cpuAccessFlags + _sapp.sample_count, + _sapp.sample_count > 1 ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0 + ); + winrt::check_hresult(m_d3dDevice->CreateTexture2D1(&msaaTexDesc, nullptr, m_d3dMSAARenderTarget.put())); + winrt::check_hresult(m_d3dDevice->CreateRenderTargetView1(m_d3dMSAARenderTarget.get(), nullptr, m_d3dMSAARenderTargetView.put())); + } + + // Create a depth stencil view for use with 3D rendering if needed. + CD3D11_TEXTURE2D_DESC1 depthStencilDesc( + DXGI_FORMAT_D24_UNORM_S8_UINT, + lround(m_d3dRenderTargetSize.Width), + lround(m_d3dRenderTargetSize.Height), + 1, // This depth stencil view has only one texture. + 1, // Use a single mipmap level. + D3D11_BIND_DEPTH_STENCIL, + D3D11_USAGE_DEFAULT, + 0, // cpuAccessFlag + _sapp.sample_count, + _sapp.sample_count > 1 ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0 + ); + winrt::check_hresult(m_d3dDevice->CreateTexture2D1(&depthStencilDesc, nullptr, m_d3dDepthStencil.put())); + + CD3D11_DEPTH_STENCIL_VIEW_DESC depthStencilViewDesc(D3D11_DSV_DIMENSION_TEXTURE2D); + winrt::check_hresult(m_d3dDevice->CreateDepthStencilView(m_d3dDepthStencil.get(), nullptr, m_d3dDepthStencilView.put())); + + // Set sokol window and framebuffer sizes + _sapp.window_width = (int) m_logicalSize.Width; + _sapp.window_height = (int) m_logicalSize.Height; + _sapp.framebuffer_width = lround(m_d3dRenderTargetSize.Width); + _sapp.framebuffer_height = lround(m_d3dRenderTargetSize.Height); + + // Setup Sokol Context + _sapp.d3d11.rt = m_d3dRenderTarget.as().get(); + _sapp.d3d11.rtv = m_d3dRenderTargetView.as().get(); + _sapp.d3d11.ds = m_d3dDepthStencil.as().get(); + _sapp.d3d11.dsv = m_d3dDepthStencilView.get(); + if (_sapp.sample_count > 1) { + _sapp.d3d11.msaa_rt = m_d3dMSAARenderTarget.as().get(); + _sapp.d3d11.msaa_rtv = m_d3dMSAARenderTargetView.as().get(); + } + + // Sokol app is now valid + _sapp.valid = true; +} + +// Determine the dimensions of the render target and whether it will be scaled down. +void DeviceResources::UpdateRenderTargetSize() { + // Calculate the necessary render target size in pixels. + m_outputSize.Width = m_logicalSize.Width * _sapp.uwp.dpi.content_scale; + m_outputSize.Height = m_logicalSize.Height * _sapp.uwp.dpi.content_scale; + + // Prevent zero size DirectX content from being created. + m_outputSize.Width = std::max(m_outputSize.Width, 1.0f); + m_outputSize.Height = std::max(m_outputSize.Height, 1.0f); +} + +// This method is called when the CoreWindow is created (or re-created). +void DeviceResources::SetWindow(winrt::Windows::UI::Core::CoreWindow const& window) { + auto currentDisplayInformation = winrt::Windows::Graphics::Display::DisplayInformation::GetForCurrentView(); + m_window = window; + m_logicalSize = winrt::Windows::Foundation::Size(window.Bounds().Width, window.Bounds().Height); + m_nativeOrientation = currentDisplayInformation.NativeOrientation(); + m_currentOrientation = currentDisplayInformation.CurrentOrientation(); + m_dpi = currentDisplayInformation.LogicalDpi(); + _sapp_uwp_configure_dpi(m_dpi); + CreateWindowSizeDependentResources(); +} + +// This method is called in the event handler for the SizeChanged event. +void DeviceResources::SetLogicalSize(winrt::Windows::Foundation::Size logicalSize) { + if (m_logicalSize != logicalSize) { + m_logicalSize = logicalSize; + CreateWindowSizeDependentResources(); + } +} + +// This method is called in the event handler for the DpiChanged event. +void DeviceResources::SetDpi(float dpi) { + if (dpi != m_dpi) { + m_dpi = dpi; + _sapp_uwp_configure_dpi(m_dpi); + // When the display DPI changes, the logical size of the window (measured in Dips) also changes and needs to be updated. + auto window = m_window.get(); + m_logicalSize = winrt::Windows::Foundation::Size(window.Bounds().Width, window.Bounds().Height); + CreateWindowSizeDependentResources(); + } +} + +// This method is called in the event handler for the OrientationChanged event. +void DeviceResources::SetCurrentOrientation(winrt::Windows::Graphics::Display::DisplayOrientations currentOrientation) { + if (m_currentOrientation != currentOrientation) { + m_currentOrientation = currentOrientation; + CreateWindowSizeDependentResources(); + } +} + +// This method is called in the event handler for the DisplayContentsInvalidated event. +void DeviceResources::ValidateDevice() { + // The D3D Device is no longer valid if the default adapter changed since the device + // was created or if the device has been removed. + + // First, get the information for the default adapter from when the device was created. + winrt::com_ptr dxgiDevice = m_d3dDevice.as< IDXGIDevice3>(); + winrt::com_ptr deviceAdapter; + winrt::check_hresult(dxgiDevice->GetAdapter(deviceAdapter.put())); + winrt::com_ptr deviceFactory; + winrt::check_hresult(deviceAdapter->GetParent(IID_PPV_ARGS(&deviceFactory))); + winrt::com_ptr previousDefaultAdapter; + winrt::check_hresult(deviceFactory->EnumAdapters1(0, previousDefaultAdapter.put())); + DXGI_ADAPTER_DESC1 previousDesc; + winrt::check_hresult(previousDefaultAdapter->GetDesc1(&previousDesc)); + + // Next, get the information for the current default adapter. + winrt::com_ptr currentFactory; + winrt::check_hresult(CreateDXGIFactory1(IID_PPV_ARGS(¤tFactory))); + winrt::com_ptr currentDefaultAdapter; + winrt::check_hresult(currentFactory->EnumAdapters1(0, currentDefaultAdapter.put())); + DXGI_ADAPTER_DESC1 currentDesc; + winrt::check_hresult(currentDefaultAdapter->GetDesc1(¤tDesc)); + + // If the adapter LUIDs don't match, or if the device reports that it has been removed, + // a new D3D device must be created. + if (previousDesc.AdapterLuid.LowPart != currentDesc.AdapterLuid.LowPart || + previousDesc.AdapterLuid.HighPart != currentDesc.AdapterLuid.HighPart || + FAILED(m_d3dDevice->GetDeviceRemovedReason())) + { + // Release references to resources related to the old device. + dxgiDevice = nullptr; + deviceAdapter = nullptr; + deviceFactory = nullptr; + previousDefaultAdapter = nullptr; + + // Create a new device and swap chain. + HandleDeviceLost(); + } +} + +// Recreate all device resources and set them back to the current state. +void DeviceResources::HandleDeviceLost() { + m_swapChain = nullptr; + if (m_deviceNotify != nullptr) { + m_deviceNotify->OnDeviceLost(); + } + CreateDeviceResources(); + CreateWindowSizeDependentResources(); + if (m_deviceNotify != nullptr) { + m_deviceNotify->OnDeviceRestored(); + } +} + +// Register our DeviceNotify to be informed on device lost and creation. +void DeviceResources::RegisterDeviceNotify(IDeviceNotify* deviceNotify) { + m_deviceNotify = deviceNotify; +} + +// Call this method when the app suspends. It provides a hint to the driver that the app +// is entering an idle state and that temporary buffers can be reclaimed for use by other apps. +void DeviceResources::Trim() { + m_d3dDevice.as()->Trim(); +} + +// Present the contents of the swap chain to the screen. +void DeviceResources::Present() { + + // MSAA resolve if needed + if (_sapp.sample_count > 1) { + m_d3dContext->ResolveSubresource(m_d3dRenderTarget.get(), 0, m_d3dMSAARenderTarget.get(), 0, DXGI_FORMAT_B8G8R8A8_UNORM); + m_d3dContext->DiscardView1(m_d3dMSAARenderTargetView.get(), nullptr, 0); + } + + // The first argument instructs DXGI to block until VSync, putting the application + // to sleep until the next VSync. This ensures we don't waste any cycles rendering + // frames that will never be displayed to the screen. + DXGI_PRESENT_PARAMETERS parameters = { 0 }; + HRESULT hr = m_swapChain->Present1(1, 0, ¶meters); + + // Discard the contents of the render target. + // This is a valid operation only when the existing contents will be entirely + // overwritten. If dirty or scroll rects are used, this call should be removed. + m_d3dContext->DiscardView1(m_d3dRenderTargetView.get(), nullptr, 0); + + // Discard the contents of the depth stencil. + m_d3dContext->DiscardView1(m_d3dDepthStencilView.get(), nullptr, 0); + + // If the device was removed either by a disconnection or a driver upgrade, we + // must recreate all device resources. + if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) { + HandleDeviceLost(); + } + else { + winrt::check_hresult(hr); + } +} + +// This method determines the rotation between the display device's native orientation and the +// current display orientation. +DXGI_MODE_ROTATION DeviceResources::ComputeDisplayRotation() { + DXGI_MODE_ROTATION rotation = DXGI_MODE_ROTATION_UNSPECIFIED; + + // Note: NativeOrientation can only be Landscape or Portrait even though + // the DisplayOrientations enum has other values. + switch (m_nativeOrientation) { + case winrt::Windows::Graphics::Display::DisplayOrientations::Landscape: + switch (m_currentOrientation) { + case winrt::Windows::Graphics::Display::DisplayOrientations::Landscape: + rotation = DXGI_MODE_ROTATION_IDENTITY; + break; + + case winrt::Windows::Graphics::Display::DisplayOrientations::Portrait: + rotation = DXGI_MODE_ROTATION_ROTATE270; + break; + + case winrt::Windows::Graphics::Display::DisplayOrientations::LandscapeFlipped: + rotation = DXGI_MODE_ROTATION_ROTATE180; + break; + + case winrt::Windows::Graphics::Display::DisplayOrientations::PortraitFlipped: + rotation = DXGI_MODE_ROTATION_ROTATE90; + break; + } + break; + + case winrt::Windows::Graphics::Display::DisplayOrientations::Portrait: + switch (m_currentOrientation) { + case winrt::Windows::Graphics::Display::DisplayOrientations::Landscape: + rotation = DXGI_MODE_ROTATION_ROTATE90; + break; + + case winrt::Windows::Graphics::Display::DisplayOrientations::Portrait: + rotation = DXGI_MODE_ROTATION_IDENTITY; + break; + + case winrt::Windows::Graphics::Display::DisplayOrientations::LandscapeFlipped: + rotation = DXGI_MODE_ROTATION_ROTATE270; + break; + + case winrt::Windows::Graphics::Display::DisplayOrientations::PortraitFlipped: + rotation = DXGI_MODE_ROTATION_ROTATE180; + break; + } + break; + } + return rotation; +} + +// Check for SDK Layer support. +bool DeviceResources::SdkLayersAvailable() { + #if defined(_DEBUG) + HRESULT hr = D3D11CreateDevice( + nullptr, + D3D_DRIVER_TYPE_NULL, // There is no need to create a real hardware device. + 0, + D3D11_CREATE_DEVICE_DEBUG, // Check for the SDK layers. + nullptr, // Any feature level will do. + 0, + D3D11_SDK_VERSION, // Always set this to D3D11_SDK_VERSION for Microsoft Store apps. + nullptr, // No need to keep the D3D device reference. + nullptr, // No need to know the feature level. + nullptr // No need to keep the D3D device context reference. + ); + return SUCCEEDED(hr); + #else + return false; + #endif +} + +// The first method called when the IFrameworkView is being created. +void App::Initialize(winrt::Windows::ApplicationModel::Core::CoreApplicationView const& applicationView) { + // Register event handlers for app lifecycle. This example includes Activated, so that we + // can make the CoreWindow active and start rendering on the window. + applicationView.Activated({ this, &App::OnActivated }); + + winrt::Windows::ApplicationModel::Core::CoreApplication::Suspending({ this, &App::OnSuspending }); + winrt::Windows::ApplicationModel::Core::CoreApplication::Resuming({ this, &App::OnResuming }); + + // At this point we have access to the device. + // We can create the device-dependent resources. + m_deviceResources = std::make_unique(); +} + +// Called when the CoreWindow object is created (or re-created). +void App::SetWindow(winrt::Windows::UI::Core::CoreWindow const& window) { + window.SizeChanged({ this, &App::OnWindowSizeChanged }); + window.VisibilityChanged({ this, &App::OnVisibilityChanged }); + + window.KeyDown({ this, &App::OnKeyDown }); + window.KeyUp({ this, &App::OnKeyUp }); + window.CharacterReceived({ this, &App::OnCharacterReceived }); + + window.PointerEntered({ this, &App::OnPointerEntered }); + window.PointerExited({ this, &App::OnPointerExited }); + window.PointerPressed({ this, &App::OnPointerPressed }); + window.PointerReleased({ this, &App::OnPointerReleased }); + window.PointerMoved({ this, &App::OnPointerMoved }); + window.PointerWheelChanged({ this, &App::OnPointerWheelChanged }); + + auto currentDisplayInformation = winrt::Windows::Graphics::Display::DisplayInformation::GetForCurrentView(); + + currentDisplayInformation.DpiChanged({ this, &App::OnDpiChanged }); + currentDisplayInformation.OrientationChanged({ this, &App::OnOrientationChanged }); + winrt::Windows::Graphics::Display::DisplayInformation::DisplayContentsInvalidated({ this, &App::OnDisplayContentsInvalidated }); + + winrt::Windows::UI::Core::SystemNavigationManager::GetForCurrentView().BackRequested({ this, &App::OnBackRequested }); + + m_deviceResources->SetWindow(window); +} + +// Initializes scene resources, or loads a previously saved app state. +void App::Load(winrt::hstring const& entryPoint) { + _SOKOL_UNUSED(entryPoint); +} + +// This method is called after the window becomes active. +void App::Run() { + // NOTE: UWP will simply terminate an application, it's not possible to detect when an application is being closed + while (true) { + if (m_windowVisible) { + winrt::Windows::UI::Core::CoreWindow::GetForCurrentThread().Dispatcher().ProcessEvents(winrt::Windows::UI::Core::CoreProcessEventsOption::ProcessAllIfPresent); + _sapp_frame(); + m_deviceResources->Present(); + } + else { + winrt::Windows::UI::Core::CoreWindow::GetForCurrentThread().Dispatcher().ProcessEvents(winrt::Windows::UI::Core::CoreProcessEventsOption::ProcessOneAndAllPending); + } + } +} + +// Required for IFrameworkView. +// Terminate events do not cause Uninitialize to be called. It will be called if your IFrameworkView +// class is torn down while the app is in the foreground. +void App::Uninitialize() { + // empty +} + +// Application lifecycle event handlers. +void App::OnActivated(winrt::Windows::ApplicationModel::Core::CoreApplicationView const& applicationView, winrt::Windows::ApplicationModel::Activation::IActivatedEventArgs const& args) { + _SOKOL_UNUSED(args); + _SOKOL_UNUSED(applicationView); + auto appView = winrt::Windows::UI::ViewManagement::ApplicationView::GetForCurrentView(); + auto targetSize = winrt::Windows::Foundation::Size((float)_sapp.desc.width, (float)_sapp.desc.height); + appView.SetPreferredMinSize(targetSize); + appView.TryResizeView(targetSize); + + // Disabling this since it can only append the title to the app name (Title - Appname). + // There's no way of just setting a string to be the window title. + //appView.Title(_sapp.window_title_wide); + + // Run() won't start until the CoreWindow is activated. + winrt::Windows::UI::Core::CoreWindow::GetForCurrentThread().Activate(); + if (_sapp.desc.fullscreen) { + appView.TryEnterFullScreenMode(); + } + _sapp.fullscreen = appView.IsFullScreenMode(); +} + +void App::OnSuspending(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::ApplicationModel::SuspendingEventArgs const& args) { + _SOKOL_UNUSED(sender); + _SOKOL_UNUSED(args); + _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_SUSPENDED); +} + +void App::OnResuming(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::Foundation::IInspectable const& args) { + _SOKOL_UNUSED(args); + _SOKOL_UNUSED(sender); + _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESUMED); +} + +void App::OnWindowSizeChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::WindowSizeChangedEventArgs const& args) { + _SOKOL_UNUSED(args); + m_deviceResources->SetLogicalSize(winrt::Windows::Foundation::Size(sender.Bounds().Width, sender.Bounds().Height)); + _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED); +} + +void App::OnVisibilityChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::VisibilityChangedEventArgs const& args) { + _SOKOL_UNUSED(sender); + m_windowVisible = args.Visible(); + _sapp_win32_uwp_app_event(m_windowVisible ? SAPP_EVENTTYPE_RESTORED : SAPP_EVENTTYPE_ICONIFIED); +} + +void App::OnBackRequested(winrt::Windows::Foundation::IInspectable const& sender, winrt::Windows::UI::Core::BackRequestedEventArgs const& args) { + _SOKOL_UNUSED(sender); + args.Handled(true); +} + +void App::OnKeyDown(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::KeyEventArgs const& args) { + auto status = args.KeyStatus(); + _sapp_uwp_key_event(SAPP_EVENTTYPE_KEY_DOWN, sender, args); +} + +void App::OnKeyUp(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::KeyEventArgs const& args) { + auto status = args.KeyStatus(); + _sapp_uwp_key_event(SAPP_EVENTTYPE_KEY_UP, sender, args); +} + +void App::OnCharacterReceived(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::CharacterReceivedEventArgs const& args) { + _sapp_uwp_char_event(args.KeyCode(), args.KeyStatus().WasKeyDown, sender); +} + +void App::OnPointerEntered(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) { + _SOKOL_UNUSED(args); + _sapp.uwp.mouse_tracked = true; + _sapp_uwp_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, sender); +} + +void App::OnPointerExited(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) { + _SOKOL_UNUSED(args); + _sapp.uwp.mouse_tracked = false; + _sapp_uwp_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, sender); +} + +void App::OnPointerPressed(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) { + _sapp_uwp_extract_mouse_button_events(sender, args); +} + +// NOTE: for some reason this event handler is never called?? +void App::OnPointerReleased(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) { + _sapp_uwp_extract_mouse_button_events(sender, args); +} + +void App::OnPointerMoved(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) { + auto position = args.CurrentPoint().Position(); + const float new_x = (float)(int)(position.X * _sapp.uwp.dpi.mouse_scale + 0.5f); + const float new_y = (float)(int)(position.Y * _sapp.uwp.dpi.mouse_scale + 0.5f); + // don't update dx/dy in the very first event + if (_sapp.mouse.pos_valid) { + _sapp.mouse.dx = new_x - _sapp.mouse.x; + _sapp.mouse.dy = new_y - _sapp.mouse.y; + } + _sapp.mouse.x = new_x; + _sapp.mouse.y = new_y; + _sapp.mouse.pos_valid = true; + if (!_sapp.uwp.mouse_tracked) { + _sapp.uwp.mouse_tracked = true; + _sapp_uwp_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, sender); + } + _sapp_uwp_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, sender); + + // HACK for detecting multiple mouse button presses + _sapp_uwp_extract_mouse_button_events(sender, args); +} + +void App::OnPointerWheelChanged(winrt::Windows::UI::Core::CoreWindow const& sender, winrt::Windows::UI::Core::PointerEventArgs const& args) { + auto properties = args.CurrentPoint().Properties(); + _sapp_uwp_scroll_event((float)properties.MouseWheelDelta(), properties.IsHorizontalMouseWheel(), sender); +} + +void App::OnDpiChanged(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args) { + // NOTE: UNTESTED + _SOKOL_UNUSED(args); + m_deviceResources->SetDpi(sender.LogicalDpi()); + _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED); +} + +void App::OnOrientationChanged(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args) { + // NOTE: UNTESTED + _SOKOL_UNUSED(args); + m_deviceResources->SetCurrentOrientation(sender.CurrentOrientation()); + _sapp_win32_uwp_app_event(SAPP_EVENTTYPE_RESIZED); +} + +void App::OnDisplayContentsInvalidated(winrt::Windows::Graphics::Display::DisplayInformation const& sender, winrt::Windows::Foundation::IInspectable const& args) { + // NOTE: UNTESTED + _SOKOL_UNUSED(args); + _SOKOL_UNUSED(sender); + m_deviceResources->ValidateDevice(); +} + +} /* End empty namespace */ + +_SOKOL_PRIVATE void _sapp_uwp_run(const sapp_desc* desc) { + _sapp_init_state(desc); + _sapp_win32_uwp_init_keytable(); + _sapp_win32_uwp_utf8_to_wide(_sapp.window_title, _sapp.window_title_wide, sizeof(_sapp.window_title_wide)); + winrt::Windows::ApplicationModel::Core::CoreApplication::Run(winrt::make()); +} + +#if !defined(SOKOL_NO_ENTRY) +#if defined(UNICODE) +int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nCmdShow) { +#else +int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) { +#endif + _SOKOL_UNUSED(hInstance); + _SOKOL_UNUSED(hPrevInstance); + _SOKOL_UNUSED(lpCmdLine); + _SOKOL_UNUSED(nCmdShow); + sapp_desc desc = sokol_main(0, nullptr); + _sapp_uwp_run(&desc); + return 0; +} +#endif /* SOKOL_NO_ENTRY */ +#endif /* _SAPP_UWP */ + +/*== Android ================================================================*/ +#if defined(_SAPP_ANDROID) + +/* android loop thread */ +_SOKOL_PRIVATE bool _sapp_android_init_egl(void) { + SOKOL_ASSERT(_sapp.android.display == EGL_NO_DISPLAY); + SOKOL_ASSERT(_sapp.android.context == EGL_NO_CONTEXT); + + EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (display == EGL_NO_DISPLAY) { + return false; + } + if (eglInitialize(display, NULL, NULL) == EGL_FALSE) { + return false; + } + + EGLint alpha_size = _sapp.desc.alpha ? 8 : 0; + const EGLint cfg_attributes[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, alpha_size, + EGL_DEPTH_SIZE, 16, + EGL_STENCIL_SIZE, 0, + EGL_NONE, + }; + EGLConfig available_cfgs[32]; + EGLint cfg_count; + eglChooseConfig(display, cfg_attributes, available_cfgs, 32, &cfg_count); + SOKOL_ASSERT(cfg_count > 0); + SOKOL_ASSERT(cfg_count <= 32); + + /* find config with 8-bit rgb buffer if available, ndk sample does not trust egl spec */ + EGLConfig config; + bool exact_cfg_found = false; + for (int i = 0; i < cfg_count; ++i) { + EGLConfig c = available_cfgs[i]; + EGLint r, g, b, a, d; + if (eglGetConfigAttrib(display, c, EGL_RED_SIZE, &r) == EGL_TRUE && + eglGetConfigAttrib(display, c, EGL_GREEN_SIZE, &g) == EGL_TRUE && + eglGetConfigAttrib(display, c, EGL_BLUE_SIZE, &b) == EGL_TRUE && + eglGetConfigAttrib(display, c, EGL_ALPHA_SIZE, &a) == EGL_TRUE && + eglGetConfigAttrib(display, c, EGL_DEPTH_SIZE, &d) == EGL_TRUE && + r == 8 && g == 8 && b == 8 && (alpha_size == 0 || a == alpha_size) && d == 16) { + exact_cfg_found = true; + config = c; + break; + } + } + if (!exact_cfg_found) { + config = available_cfgs[0]; + } + + EGLint ctx_attributes[] = { + #if defined(SOKOL_GLES3) + EGL_CONTEXT_CLIENT_VERSION, _sapp.desc.gl_force_gles2 ? 2 : 3, + #else + EGL_CONTEXT_CLIENT_VERSION, 2, + #endif + EGL_NONE, + }; + EGLContext context = eglCreateContext(display, config, EGL_NO_CONTEXT, ctx_attributes); + if (context == EGL_NO_CONTEXT) { + return false; + } + + _sapp.android.config = config; + _sapp.android.display = display; + _sapp.android.context = context; + return true; +} + +_SOKOL_PRIVATE void _sapp_android_cleanup_egl(void) { + if (_sapp.android.display != EGL_NO_DISPLAY) { + eglMakeCurrent(_sapp.android.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + if (_sapp.android.surface != EGL_NO_SURFACE) { + SOKOL_LOG("Destroying egl surface"); + eglDestroySurface(_sapp.android.display, _sapp.android.surface); + _sapp.android.surface = EGL_NO_SURFACE; + } + if (_sapp.android.context != EGL_NO_CONTEXT) { + SOKOL_LOG("Destroying egl context"); + eglDestroyContext(_sapp.android.display, _sapp.android.context); + _sapp.android.context = EGL_NO_CONTEXT; + } + SOKOL_LOG("Terminating egl display"); + eglTerminate(_sapp.android.display); + _sapp.android.display = EGL_NO_DISPLAY; + } +} + +_SOKOL_PRIVATE bool _sapp_android_init_egl_surface(ANativeWindow* window) { + SOKOL_ASSERT(_sapp.android.display != EGL_NO_DISPLAY); + SOKOL_ASSERT(_sapp.android.context != EGL_NO_CONTEXT); + SOKOL_ASSERT(_sapp.android.surface == EGL_NO_SURFACE); + SOKOL_ASSERT(window); + + /* TODO: set window flags */ + /* ANativeActivity_setWindowFlags(activity, AWINDOW_FLAG_KEEP_SCREEN_ON, 0); */ + + /* create egl surface and make it current */ + EGLSurface surface = eglCreateWindowSurface(_sapp.android.display, _sapp.android.config, window, NULL); + if (surface == EGL_NO_SURFACE) { + return false; + } + if (eglMakeCurrent(_sapp.android.display, surface, surface, _sapp.android.context) == EGL_FALSE) { + return false; + } + _sapp.android.surface = surface; + return true; +} + +_SOKOL_PRIVATE void _sapp_android_cleanup_egl_surface(void) { + if (_sapp.android.display == EGL_NO_DISPLAY) { + return; + } + eglMakeCurrent(_sapp.android.display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + if (_sapp.android.surface != EGL_NO_SURFACE) { + eglDestroySurface(_sapp.android.display, _sapp.android.surface); + _sapp.android.surface = EGL_NO_SURFACE; + } +} + +_SOKOL_PRIVATE void _sapp_android_app_event(sapp_event_type type) { + if (_sapp_events_enabled()) { + _sapp_init_event(type); + SOKOL_LOG("event_cb()"); + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE void _sapp_android_update_dimensions(ANativeWindow* window, bool force_update) { + SOKOL_ASSERT(_sapp.android.display != EGL_NO_DISPLAY); + SOKOL_ASSERT(_sapp.android.context != EGL_NO_CONTEXT); + SOKOL_ASSERT(_sapp.android.surface != EGL_NO_SURFACE); + SOKOL_ASSERT(window); + + const int32_t win_w = ANativeWindow_getWidth(window); + const int32_t win_h = ANativeWindow_getHeight(window); + SOKOL_ASSERT(win_w >= 0 && win_h >= 0); + const bool win_changed = (win_w != _sapp.window_width) || (win_h != _sapp.window_height); + _sapp.window_width = win_w; + _sapp.window_height = win_h; + if (win_changed || force_update) { + if (!_sapp.desc.high_dpi) { + const int32_t buf_w = win_w / 2; + const int32_t buf_h = win_h / 2; + EGLint format; + EGLBoolean egl_result = eglGetConfigAttrib(_sapp.android.display, _sapp.android.config, EGL_NATIVE_VISUAL_ID, &format); + SOKOL_ASSERT(egl_result == EGL_TRUE); + /* NOTE: calling ANativeWindow_setBuffersGeometry() with the same dimensions + as the ANativeWindow size results in weird display artefacts, that's + why it's only called when the buffer geometry is different from + the window size + */ + int32_t result = ANativeWindow_setBuffersGeometry(window, buf_w, buf_h, format); + SOKOL_ASSERT(result == 0); + } + } + + /* query surface size */ + EGLint fb_w, fb_h; + EGLBoolean egl_result_w = eglQuerySurface(_sapp.android.display, _sapp.android.surface, EGL_WIDTH, &fb_w); + EGLBoolean egl_result_h = eglQuerySurface(_sapp.android.display, _sapp.android.surface, EGL_HEIGHT, &fb_h); + SOKOL_ASSERT(egl_result_w == EGL_TRUE); + SOKOL_ASSERT(egl_result_h == EGL_TRUE); + const bool fb_changed = (fb_w != _sapp.framebuffer_width) || (fb_h != _sapp.framebuffer_height); + _sapp.framebuffer_width = fb_w; + _sapp.framebuffer_height = fb_h; + _sapp.dpi_scale = (float)_sapp.framebuffer_width / (float)_sapp.window_width; + if (win_changed || fb_changed || force_update) { + if (!_sapp.first_frame) { + SOKOL_LOG("SAPP_EVENTTYPE_RESIZED"); + _sapp_android_app_event(SAPP_EVENTTYPE_RESIZED); + } + } +} + +_SOKOL_PRIVATE void _sapp_android_cleanup(void) { + SOKOL_LOG("Cleaning up"); + if (_sapp.android.surface != EGL_NO_SURFACE) { + /* egl context is bound, cleanup gracefully */ + if (_sapp.init_called && !_sapp.cleanup_called) { + SOKOL_LOG("cleanup_cb()"); + _sapp_call_cleanup(); + } + } + /* always try to cleanup by destroying egl context */ + _sapp_android_cleanup_egl(); +} + +_SOKOL_PRIVATE void _sapp_android_shutdown(void) { + /* try to cleanup while we still have a surface and can call cleanup_cb() */ + _sapp_android_cleanup(); + /* request exit */ + ANativeActivity_finish(_sapp.android.activity); +} + +_SOKOL_PRIVATE void _sapp_android_frame(void) { + SOKOL_ASSERT(_sapp.android.display != EGL_NO_DISPLAY); + SOKOL_ASSERT(_sapp.android.context != EGL_NO_CONTEXT); + SOKOL_ASSERT(_sapp.android.surface != EGL_NO_SURFACE); + _sapp_android_update_dimensions(_sapp.android.current.window, false); + _sapp_frame(); + eglSwapBuffers(_sapp.android.display, _sapp.android.surface); +} + +_SOKOL_PRIVATE bool _sapp_android_touch_event(const AInputEvent* e) { + if (AInputEvent_getType(e) != AINPUT_EVENT_TYPE_MOTION) { + return false; + } + if (!_sapp_events_enabled()) { + return false; + } + int32_t action_idx = AMotionEvent_getAction(e); + int32_t action = action_idx & AMOTION_EVENT_ACTION_MASK; + sapp_event_type type = SAPP_EVENTTYPE_INVALID; + switch (action) { + case AMOTION_EVENT_ACTION_DOWN: + SOKOL_LOG("Touch: down"); + case AMOTION_EVENT_ACTION_POINTER_DOWN: + SOKOL_LOG("Touch: ptr down"); + type = SAPP_EVENTTYPE_TOUCHES_BEGAN; + break; + case AMOTION_EVENT_ACTION_MOVE: + type = SAPP_EVENTTYPE_TOUCHES_MOVED; + break; + case AMOTION_EVENT_ACTION_UP: + SOKOL_LOG("Touch: up"); + case AMOTION_EVENT_ACTION_POINTER_UP: + SOKOL_LOG("Touch: ptr up"); + type = SAPP_EVENTTYPE_TOUCHES_ENDED; + break; + case AMOTION_EVENT_ACTION_CANCEL: + SOKOL_LOG("Touch: cancel"); + type = SAPP_EVENTTYPE_TOUCHES_CANCELLED; + break; + default: + break; + } + if (type == SAPP_EVENTTYPE_INVALID) { + return false; + } + int32_t idx = action_idx >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; + _sapp_init_event(type); + _sapp.event.num_touches = (int)AMotionEvent_getPointerCount(e); + if (_sapp.event.num_touches > SAPP_MAX_TOUCHPOINTS) { + _sapp.event.num_touches = SAPP_MAX_TOUCHPOINTS; + } + for (int32_t i = 0; i < _sapp.event.num_touches; i++) { + sapp_touchpoint* dst = &_sapp.event.touches[i]; + dst->identifier = (uintptr_t)AMotionEvent_getPointerId(e, (size_t)i); + dst->pos_x = (AMotionEvent_getRawX(e, (size_t)i) / _sapp.window_width) * _sapp.framebuffer_width; + dst->pos_y = (AMotionEvent_getRawY(e, (size_t)i) / _sapp.window_height) * _sapp.framebuffer_height; + + if (action == AMOTION_EVENT_ACTION_POINTER_DOWN || + action == AMOTION_EVENT_ACTION_POINTER_UP) { + dst->changed = (i == idx); + } else { + dst->changed = true; + } + } + _sapp_call_event(&_sapp.event); + return true; +} + +_SOKOL_PRIVATE bool _sapp_android_key_event(const AInputEvent* e) { + if (AInputEvent_getType(e) != AINPUT_EVENT_TYPE_KEY) { + return false; + } + if (AKeyEvent_getKeyCode(e) == AKEYCODE_BACK) { + /* FIXME: this should be hooked into a "really quit?" mechanism + so the app can ask the user for confirmation, this is currently + generally missing in sokol_app.h + */ + _sapp_android_shutdown(); + return true; + } + return false; +} + +_SOKOL_PRIVATE int _sapp_android_input_cb(int fd, int events, void* data) { + if ((events & ALOOPER_EVENT_INPUT) == 0) { + SOKOL_LOG("_sapp_android_input_cb() encountered unsupported event"); + return 1; + } + SOKOL_ASSERT(_sapp.android.current.input); + AInputEvent* event = NULL; + while (AInputQueue_getEvent(_sapp.android.current.input, &event) >= 0) { + if (AInputQueue_preDispatchEvent(_sapp.android.current.input, event) != 0) { + continue; + } + int32_t handled = 0; + if (_sapp_android_touch_event(event) || _sapp_android_key_event(event)) { + handled = 1; + } + AInputQueue_finishEvent(_sapp.android.current.input, event, handled); + } + return 1; +} + +_SOKOL_PRIVATE int _sapp_android_main_cb(int fd, int events, void* data) { + if ((events & ALOOPER_EVENT_INPUT) == 0) { + SOKOL_LOG("_sapp_android_main_cb() encountered unsupported event"); + return 1; + } + + _sapp_android_msg_t msg; + if (read(fd, &msg, sizeof(msg)) != sizeof(msg)) { + SOKOL_LOG("Could not write to read_from_main_fd"); + return 1; + } + + pthread_mutex_lock(&_sapp.android.pt.mutex); + switch (msg) { + case _SOKOL_ANDROID_MSG_CREATE: + { + SOKOL_LOG("MSG_CREATE"); + SOKOL_ASSERT(!_sapp.valid); + bool result = _sapp_android_init_egl(); + SOKOL_ASSERT(result); + _sapp.valid = true; + _sapp.android.has_created = true; + } + break; + case _SOKOL_ANDROID_MSG_RESUME: + SOKOL_LOG("MSG_RESUME"); + _sapp.android.has_resumed = true; + _sapp_android_app_event(SAPP_EVENTTYPE_RESUMED); + break; + case _SOKOL_ANDROID_MSG_PAUSE: + SOKOL_LOG("MSG_PAUSE"); + _sapp.android.has_resumed = false; + _sapp_android_app_event(SAPP_EVENTTYPE_SUSPENDED); + break; + case _SOKOL_ANDROID_MSG_FOCUS: + SOKOL_LOG("MSG_FOCUS"); + _sapp.android.has_focus = true; + break; + case _SOKOL_ANDROID_MSG_NO_FOCUS: + SOKOL_LOG("MSG_NO_FOCUS"); + _sapp.android.has_focus = false; + break; + case _SOKOL_ANDROID_MSG_SET_NATIVE_WINDOW: + SOKOL_LOG("MSG_SET_NATIVE_WINDOW"); + if (_sapp.android.current.window != _sapp.android.pending.window) { + if (_sapp.android.current.window != NULL) { + _sapp_android_cleanup_egl_surface(); + } + if (_sapp.android.pending.window != NULL) { + SOKOL_LOG("Creating egl surface ..."); + if (_sapp_android_init_egl_surface(_sapp.android.pending.window)) { + SOKOL_LOG("... ok!"); + _sapp_android_update_dimensions(_sapp.android.pending.window, true); + } else { + SOKOL_LOG("... failed!"); + _sapp_android_shutdown(); + } + } + } + _sapp.android.current.window = _sapp.android.pending.window; + break; + case _SOKOL_ANDROID_MSG_SET_INPUT_QUEUE: + SOKOL_LOG("MSG_SET_INPUT_QUEUE"); + if (_sapp.android.current.input != _sapp.android.pending.input) { + if (_sapp.android.current.input != NULL) { + AInputQueue_detachLooper(_sapp.android.current.input); + } + if (_sapp.android.pending.input != NULL) { + AInputQueue_attachLooper( + _sapp.android.pending.input, + _sapp.android.looper, + ALOOPER_POLL_CALLBACK, + _sapp_android_input_cb, + NULL); /* data */ + } + } + _sapp.android.current.input = _sapp.android.pending.input; + break; + case _SOKOL_ANDROID_MSG_DESTROY: + SOKOL_LOG("MSG_DESTROY"); + _sapp_android_cleanup(); + _sapp.valid = false; + _sapp.android.is_thread_stopping = true; + break; + default: + SOKOL_LOG("Unknown msg type received"); + break; + } + pthread_cond_broadcast(&_sapp.android.pt.cond); /* signal "received" */ + pthread_mutex_unlock(&_sapp.android.pt.mutex); + return 1; +} + +_SOKOL_PRIVATE bool _sapp_android_should_update(void) { + bool is_in_front = _sapp.android.has_resumed && _sapp.android.has_focus; + bool has_surface = _sapp.android.surface != EGL_NO_SURFACE; + return is_in_front && has_surface; +} + +_SOKOL_PRIVATE void _sapp_android_show_keyboard(bool shown) { + SOKOL_ASSERT(_sapp.valid); + /* This seems to be broken in the NDK, but there is (a very cumbersome) workaround... */ + if (shown) { + SOKOL_LOG("Showing keyboard"); + ANativeActivity_showSoftInput(_sapp.android.activity, ANATIVEACTIVITY_SHOW_SOFT_INPUT_FORCED); + } else { + SOKOL_LOG("Hiding keyboard"); + ANativeActivity_hideSoftInput(_sapp.android.activity, ANATIVEACTIVITY_HIDE_SOFT_INPUT_NOT_ALWAYS); + } +} + +_SOKOL_PRIVATE void* _sapp_android_loop(void* arg) { + _SOKOL_UNUSED(arg); + SOKOL_LOG("Loop thread started"); + + _sapp.android.looper = ALooper_prepare(0 /* or ALOOPER_PREPARE_ALLOW_NON_CALLBACKS*/); + ALooper_addFd(_sapp.android.looper, + _sapp.android.pt.read_from_main_fd, + ALOOPER_POLL_CALLBACK, + ALOOPER_EVENT_INPUT, + _sapp_android_main_cb, + NULL); /* data */ + + /* signal start to main thread */ + pthread_mutex_lock(&_sapp.android.pt.mutex); + _sapp.android.is_thread_started = true; + pthread_cond_broadcast(&_sapp.android.pt.cond); + pthread_mutex_unlock(&_sapp.android.pt.mutex); + + /* main loop */ + while (!_sapp.android.is_thread_stopping) { + /* sokol frame */ + if (_sapp_android_should_update()) { + _sapp_android_frame(); + } + + /* process all events (or stop early if app is requested to quit) */ + bool process_events = true; + while (process_events && !_sapp.android.is_thread_stopping) { + bool block_until_event = !_sapp.android.is_thread_stopping && !_sapp_android_should_update(); + process_events = ALooper_pollOnce(block_until_event ? -1 : 0, NULL, NULL, NULL) == ALOOPER_POLL_CALLBACK; + } + } + + /* cleanup thread */ + if (_sapp.android.current.input != NULL) { + AInputQueue_detachLooper(_sapp.android.current.input); + } + + /* the following causes heap corruption on exit, why?? + ALooper_removeFd(_sapp.android.looper, _sapp.android.pt.read_from_main_fd); + ALooper_release(_sapp.android.looper);*/ + + /* signal "destroyed" */ + pthread_mutex_lock(&_sapp.android.pt.mutex); + _sapp.android.is_thread_stopped = true; + pthread_cond_broadcast(&_sapp.android.pt.cond); + pthread_mutex_unlock(&_sapp.android.pt.mutex); + SOKOL_LOG("Loop thread done"); + return NULL; +} + +/* android main/ui thread */ +_SOKOL_PRIVATE void _sapp_android_msg(_sapp_android_msg_t msg) { + if (write(_sapp.android.pt.write_from_main_fd, &msg, sizeof(msg)) != sizeof(msg)) { + SOKOL_LOG("Could not write to write_from_main_fd"); + } +} + +_SOKOL_PRIVATE void _sapp_android_on_start(ANativeActivity* activity) { + SOKOL_LOG("NativeActivity onStart()"); +} + +_SOKOL_PRIVATE void _sapp_android_on_resume(ANativeActivity* activity) { + SOKOL_LOG("NativeActivity onResume()"); + _sapp_android_msg(_SOKOL_ANDROID_MSG_RESUME); +} + +_SOKOL_PRIVATE void* _sapp_android_on_save_instance_state(ANativeActivity* activity, size_t* out_size) { + SOKOL_LOG("NativeActivity onSaveInstanceState()"); + *out_size = 0; + return NULL; +} + +_SOKOL_PRIVATE void _sapp_android_on_window_focus_changed(ANativeActivity* activity, int has_focus) { + SOKOL_LOG("NativeActivity onWindowFocusChanged()"); + if (has_focus) { + _sapp_android_msg(_SOKOL_ANDROID_MSG_FOCUS); + } else { + _sapp_android_msg(_SOKOL_ANDROID_MSG_NO_FOCUS); + } +} + +_SOKOL_PRIVATE void _sapp_android_on_pause(ANativeActivity* activity) { + SOKOL_LOG("NativeActivity onPause()"); + _sapp_android_msg(_SOKOL_ANDROID_MSG_PAUSE); +} + +_SOKOL_PRIVATE void _sapp_android_on_stop(ANativeActivity* activity) { + SOKOL_LOG("NativeActivity onStop()"); +} + +_SOKOL_PRIVATE void _sapp_android_msg_set_native_window(ANativeWindow* window) { + pthread_mutex_lock(&_sapp.android.pt.mutex); + _sapp.android.pending.window = window; + _sapp_android_msg(_SOKOL_ANDROID_MSG_SET_NATIVE_WINDOW); + while (_sapp.android.current.window != window) { + pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex); + } + pthread_mutex_unlock(&_sapp.android.pt.mutex); +} + +_SOKOL_PRIVATE void _sapp_android_on_native_window_created(ANativeActivity* activity, ANativeWindow* window) { + SOKOL_LOG("NativeActivity onNativeWindowCreated()"); + _sapp_android_msg_set_native_window(window); +} + +_SOKOL_PRIVATE void _sapp_android_on_native_window_destroyed(ANativeActivity* activity, ANativeWindow* window) { + SOKOL_LOG("NativeActivity onNativeWindowDestroyed()"); + _sapp_android_msg_set_native_window(NULL); +} + +_SOKOL_PRIVATE void _sapp_android_msg_set_input_queue(AInputQueue* input) { + pthread_mutex_lock(&_sapp.android.pt.mutex); + _sapp.android.pending.input = input; + _sapp_android_msg(_SOKOL_ANDROID_MSG_SET_INPUT_QUEUE); + while (_sapp.android.current.input != input) { + pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex); + } + pthread_mutex_unlock(&_sapp.android.pt.mutex); +} + +_SOKOL_PRIVATE void _sapp_android_on_input_queue_created(ANativeActivity* activity, AInputQueue* queue) { + SOKOL_LOG("NativeActivity onInputQueueCreated()"); + _sapp_android_msg_set_input_queue(queue); +} + +_SOKOL_PRIVATE void _sapp_android_on_input_queue_destroyed(ANativeActivity* activity, AInputQueue* queue) { + SOKOL_LOG("NativeActivity onInputQueueDestroyed()"); + _sapp_android_msg_set_input_queue(NULL); +} + +_SOKOL_PRIVATE void _sapp_android_on_config_changed(ANativeActivity* activity) { + SOKOL_LOG("NativeActivity onConfigurationChanged()"); + /* see android:configChanges in manifest */ +} + +_SOKOL_PRIVATE void _sapp_android_on_low_memory(ANativeActivity* activity) { + SOKOL_LOG("NativeActivity onLowMemory()"); +} + +_SOKOL_PRIVATE void _sapp_android_on_destroy(ANativeActivity* activity) { + /* + * For some reason even an empty app using nativeactivity.h will crash (WIN DEATH) + * on my device (Moto X 2nd gen) when the app is removed from the task view + * (TaskStackView: onTaskViewDismissed). + * + * However, if ANativeActivity_finish() is explicitly called from for example + * _sapp_android_on_stop(), the crash disappears. Is this a bug in NativeActivity? + */ + SOKOL_LOG("NativeActivity onDestroy()"); + + /* send destroy msg */ + pthread_mutex_lock(&_sapp.android.pt.mutex); + _sapp_android_msg(_SOKOL_ANDROID_MSG_DESTROY); + while (!_sapp.android.is_thread_stopped) { + pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex); + } + pthread_mutex_unlock(&_sapp.android.pt.mutex); + + /* clean up main thread */ + pthread_cond_destroy(&_sapp.android.pt.cond); + pthread_mutex_destroy(&_sapp.android.pt.mutex); + + close(_sapp.android.pt.read_from_main_fd); + close(_sapp.android.pt.write_from_main_fd); + + SOKOL_LOG("NativeActivity done"); + + /* this is a bit naughty, but causes a clean restart of the app (static globals are reset) */ + exit(0); +} + +JNIEXPORT +void ANativeActivity_onCreate(ANativeActivity* activity, void* saved_state, size_t saved_state_size) { + SOKOL_LOG("NativeActivity onCreate()"); + + sapp_desc desc = sokol_main(0, NULL); + _sapp_init_state(&desc); + + /* start loop thread */ + _sapp.android.activity = activity; + + int pipe_fd[2]; + if (pipe(pipe_fd) != 0) { + SOKOL_LOG("Could not create thread pipe"); + return; + } + _sapp.android.pt.read_from_main_fd = pipe_fd[0]; + _sapp.android.pt.write_from_main_fd = pipe_fd[1]; + + pthread_mutex_init(&_sapp.android.pt.mutex, NULL); + pthread_cond_init(&_sapp.android.pt.cond, NULL); + + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_create(&_sapp.android.pt.thread, &attr, _sapp_android_loop, 0); + pthread_attr_destroy(&attr); + + /* wait until main loop has started */ + pthread_mutex_lock(&_sapp.android.pt.mutex); + while (!_sapp.android.is_thread_started) { + pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex); + } + pthread_mutex_unlock(&_sapp.android.pt.mutex); + + /* send create msg */ + pthread_mutex_lock(&_sapp.android.pt.mutex); + _sapp_android_msg(_SOKOL_ANDROID_MSG_CREATE); + while (!_sapp.android.has_created) { + pthread_cond_wait(&_sapp.android.pt.cond, &_sapp.android.pt.mutex); + } + pthread_mutex_unlock(&_sapp.android.pt.mutex); + + /* register for callbacks */ + activity->callbacks->onStart = _sapp_android_on_start; + activity->callbacks->onResume = _sapp_android_on_resume; + activity->callbacks->onSaveInstanceState = _sapp_android_on_save_instance_state; + activity->callbacks->onWindowFocusChanged = _sapp_android_on_window_focus_changed; + activity->callbacks->onPause = _sapp_android_on_pause; + activity->callbacks->onStop = _sapp_android_on_stop; + activity->callbacks->onDestroy = _sapp_android_on_destroy; + activity->callbacks->onNativeWindowCreated = _sapp_android_on_native_window_created; + /* activity->callbacks->onNativeWindowResized = _sapp_android_on_native_window_resized; */ + /* activity->callbacks->onNativeWindowRedrawNeeded = _sapp_android_on_native_window_redraw_needed; */ + activity->callbacks->onNativeWindowDestroyed = _sapp_android_on_native_window_destroyed; + activity->callbacks->onInputQueueCreated = _sapp_android_on_input_queue_created; + activity->callbacks->onInputQueueDestroyed = _sapp_android_on_input_queue_destroyed; + /* activity->callbacks->onContentRectChanged = _sapp_android_on_content_rect_changed; */ + activity->callbacks->onConfigurationChanged = _sapp_android_on_config_changed; + activity->callbacks->onLowMemory = _sapp_android_on_low_memory; + + SOKOL_LOG("NativeActivity successfully created"); + + /* NOT A BUG: do NOT call sapp_discard_state() */ +} + +#endif /* _SAPP_ANDROID */ + +/*== LINUX ==================================================================*/ +#if defined(_SAPP_LINUX) + +/* see GLFW's xkb_unicode.c */ +static const struct _sapp_x11_codepair { + uint16_t keysym; + uint16_t ucs; +} _sapp_x11_keysymtab[] = { + { 0x01a1, 0x0104 }, + { 0x01a2, 0x02d8 }, + { 0x01a3, 0x0141 }, + { 0x01a5, 0x013d }, + { 0x01a6, 0x015a }, + { 0x01a9, 0x0160 }, + { 0x01aa, 0x015e }, + { 0x01ab, 0x0164 }, + { 0x01ac, 0x0179 }, + { 0x01ae, 0x017d }, + { 0x01af, 0x017b }, + { 0x01b1, 0x0105 }, + { 0x01b2, 0x02db }, + { 0x01b3, 0x0142 }, + { 0x01b5, 0x013e }, + { 0x01b6, 0x015b }, + { 0x01b7, 0x02c7 }, + { 0x01b9, 0x0161 }, + { 0x01ba, 0x015f }, + { 0x01bb, 0x0165 }, + { 0x01bc, 0x017a }, + { 0x01bd, 0x02dd }, + { 0x01be, 0x017e }, + { 0x01bf, 0x017c }, + { 0x01c0, 0x0154 }, + { 0x01c3, 0x0102 }, + { 0x01c5, 0x0139 }, + { 0x01c6, 0x0106 }, + { 0x01c8, 0x010c }, + { 0x01ca, 0x0118 }, + { 0x01cc, 0x011a }, + { 0x01cf, 0x010e }, + { 0x01d0, 0x0110 }, + { 0x01d1, 0x0143 }, + { 0x01d2, 0x0147 }, + { 0x01d5, 0x0150 }, + { 0x01d8, 0x0158 }, + { 0x01d9, 0x016e }, + { 0x01db, 0x0170 }, + { 0x01de, 0x0162 }, + { 0x01e0, 0x0155 }, + { 0x01e3, 0x0103 }, + { 0x01e5, 0x013a }, + { 0x01e6, 0x0107 }, + { 0x01e8, 0x010d }, + { 0x01ea, 0x0119 }, + { 0x01ec, 0x011b }, + { 0x01ef, 0x010f }, + { 0x01f0, 0x0111 }, + { 0x01f1, 0x0144 }, + { 0x01f2, 0x0148 }, + { 0x01f5, 0x0151 }, + { 0x01f8, 0x0159 }, + { 0x01f9, 0x016f }, + { 0x01fb, 0x0171 }, + { 0x01fe, 0x0163 }, + { 0x01ff, 0x02d9 }, + { 0x02a1, 0x0126 }, + { 0x02a6, 0x0124 }, + { 0x02a9, 0x0130 }, + { 0x02ab, 0x011e }, + { 0x02ac, 0x0134 }, + { 0x02b1, 0x0127 }, + { 0x02b6, 0x0125 }, + { 0x02b9, 0x0131 }, + { 0x02bb, 0x011f }, + { 0x02bc, 0x0135 }, + { 0x02c5, 0x010a }, + { 0x02c6, 0x0108 }, + { 0x02d5, 0x0120 }, + { 0x02d8, 0x011c }, + { 0x02dd, 0x016c }, + { 0x02de, 0x015c }, + { 0x02e5, 0x010b }, + { 0x02e6, 0x0109 }, + { 0x02f5, 0x0121 }, + { 0x02f8, 0x011d }, + { 0x02fd, 0x016d }, + { 0x02fe, 0x015d }, + { 0x03a2, 0x0138 }, + { 0x03a3, 0x0156 }, + { 0x03a5, 0x0128 }, + { 0x03a6, 0x013b }, + { 0x03aa, 0x0112 }, + { 0x03ab, 0x0122 }, + { 0x03ac, 0x0166 }, + { 0x03b3, 0x0157 }, + { 0x03b5, 0x0129 }, + { 0x03b6, 0x013c }, + { 0x03ba, 0x0113 }, + { 0x03bb, 0x0123 }, + { 0x03bc, 0x0167 }, + { 0x03bd, 0x014a }, + { 0x03bf, 0x014b }, + { 0x03c0, 0x0100 }, + { 0x03c7, 0x012e }, + { 0x03cc, 0x0116 }, + { 0x03cf, 0x012a }, + { 0x03d1, 0x0145 }, + { 0x03d2, 0x014c }, + { 0x03d3, 0x0136 }, + { 0x03d9, 0x0172 }, + { 0x03dd, 0x0168 }, + { 0x03de, 0x016a }, + { 0x03e0, 0x0101 }, + { 0x03e7, 0x012f }, + { 0x03ec, 0x0117 }, + { 0x03ef, 0x012b }, + { 0x03f1, 0x0146 }, + { 0x03f2, 0x014d }, + { 0x03f3, 0x0137 }, + { 0x03f9, 0x0173 }, + { 0x03fd, 0x0169 }, + { 0x03fe, 0x016b }, + { 0x047e, 0x203e }, + { 0x04a1, 0x3002 }, + { 0x04a2, 0x300c }, + { 0x04a3, 0x300d }, + { 0x04a4, 0x3001 }, + { 0x04a5, 0x30fb }, + { 0x04a6, 0x30f2 }, + { 0x04a7, 0x30a1 }, + { 0x04a8, 0x30a3 }, + { 0x04a9, 0x30a5 }, + { 0x04aa, 0x30a7 }, + { 0x04ab, 0x30a9 }, + { 0x04ac, 0x30e3 }, + { 0x04ad, 0x30e5 }, + { 0x04ae, 0x30e7 }, + { 0x04af, 0x30c3 }, + { 0x04b0, 0x30fc }, + { 0x04b1, 0x30a2 }, + { 0x04b2, 0x30a4 }, + { 0x04b3, 0x30a6 }, + { 0x04b4, 0x30a8 }, + { 0x04b5, 0x30aa }, + { 0x04b6, 0x30ab }, + { 0x04b7, 0x30ad }, + { 0x04b8, 0x30af }, + { 0x04b9, 0x30b1 }, + { 0x04ba, 0x30b3 }, + { 0x04bb, 0x30b5 }, + { 0x04bc, 0x30b7 }, + { 0x04bd, 0x30b9 }, + { 0x04be, 0x30bb }, + { 0x04bf, 0x30bd }, + { 0x04c0, 0x30bf }, + { 0x04c1, 0x30c1 }, + { 0x04c2, 0x30c4 }, + { 0x04c3, 0x30c6 }, + { 0x04c4, 0x30c8 }, + { 0x04c5, 0x30ca }, + { 0x04c6, 0x30cb }, + { 0x04c7, 0x30cc }, + { 0x04c8, 0x30cd }, + { 0x04c9, 0x30ce }, + { 0x04ca, 0x30cf }, + { 0x04cb, 0x30d2 }, + { 0x04cc, 0x30d5 }, + { 0x04cd, 0x30d8 }, + { 0x04ce, 0x30db }, + { 0x04cf, 0x30de }, + { 0x04d0, 0x30df }, + { 0x04d1, 0x30e0 }, + { 0x04d2, 0x30e1 }, + { 0x04d3, 0x30e2 }, + { 0x04d4, 0x30e4 }, + { 0x04d5, 0x30e6 }, + { 0x04d6, 0x30e8 }, + { 0x04d7, 0x30e9 }, + { 0x04d8, 0x30ea }, + { 0x04d9, 0x30eb }, + { 0x04da, 0x30ec }, + { 0x04db, 0x30ed }, + { 0x04dc, 0x30ef }, + { 0x04dd, 0x30f3 }, + { 0x04de, 0x309b }, + { 0x04df, 0x309c }, + { 0x05ac, 0x060c }, + { 0x05bb, 0x061b }, + { 0x05bf, 0x061f }, + { 0x05c1, 0x0621 }, + { 0x05c2, 0x0622 }, + { 0x05c3, 0x0623 }, + { 0x05c4, 0x0624 }, + { 0x05c5, 0x0625 }, + { 0x05c6, 0x0626 }, + { 0x05c7, 0x0627 }, + { 0x05c8, 0x0628 }, + { 0x05c9, 0x0629 }, + { 0x05ca, 0x062a }, + { 0x05cb, 0x062b }, + { 0x05cc, 0x062c }, + { 0x05cd, 0x062d }, + { 0x05ce, 0x062e }, + { 0x05cf, 0x062f }, + { 0x05d0, 0x0630 }, + { 0x05d1, 0x0631 }, + { 0x05d2, 0x0632 }, + { 0x05d3, 0x0633 }, + { 0x05d4, 0x0634 }, + { 0x05d5, 0x0635 }, + { 0x05d6, 0x0636 }, + { 0x05d7, 0x0637 }, + { 0x05d8, 0x0638 }, + { 0x05d9, 0x0639 }, + { 0x05da, 0x063a }, + { 0x05e0, 0x0640 }, + { 0x05e1, 0x0641 }, + { 0x05e2, 0x0642 }, + { 0x05e3, 0x0643 }, + { 0x05e4, 0x0644 }, + { 0x05e5, 0x0645 }, + { 0x05e6, 0x0646 }, + { 0x05e7, 0x0647 }, + { 0x05e8, 0x0648 }, + { 0x05e9, 0x0649 }, + { 0x05ea, 0x064a }, + { 0x05eb, 0x064b }, + { 0x05ec, 0x064c }, + { 0x05ed, 0x064d }, + { 0x05ee, 0x064e }, + { 0x05ef, 0x064f }, + { 0x05f0, 0x0650 }, + { 0x05f1, 0x0651 }, + { 0x05f2, 0x0652 }, + { 0x06a1, 0x0452 }, + { 0x06a2, 0x0453 }, + { 0x06a3, 0x0451 }, + { 0x06a4, 0x0454 }, + { 0x06a5, 0x0455 }, + { 0x06a6, 0x0456 }, + { 0x06a7, 0x0457 }, + { 0x06a8, 0x0458 }, + { 0x06a9, 0x0459 }, + { 0x06aa, 0x045a }, + { 0x06ab, 0x045b }, + { 0x06ac, 0x045c }, + { 0x06ae, 0x045e }, + { 0x06af, 0x045f }, + { 0x06b0, 0x2116 }, + { 0x06b1, 0x0402 }, + { 0x06b2, 0x0403 }, + { 0x06b3, 0x0401 }, + { 0x06b4, 0x0404 }, + { 0x06b5, 0x0405 }, + { 0x06b6, 0x0406 }, + { 0x06b7, 0x0407 }, + { 0x06b8, 0x0408 }, + { 0x06b9, 0x0409 }, + { 0x06ba, 0x040a }, + { 0x06bb, 0x040b }, + { 0x06bc, 0x040c }, + { 0x06be, 0x040e }, + { 0x06bf, 0x040f }, + { 0x06c0, 0x044e }, + { 0x06c1, 0x0430 }, + { 0x06c2, 0x0431 }, + { 0x06c3, 0x0446 }, + { 0x06c4, 0x0434 }, + { 0x06c5, 0x0435 }, + { 0x06c6, 0x0444 }, + { 0x06c7, 0x0433 }, + { 0x06c8, 0x0445 }, + { 0x06c9, 0x0438 }, + { 0x06ca, 0x0439 }, + { 0x06cb, 0x043a }, + { 0x06cc, 0x043b }, + { 0x06cd, 0x043c }, + { 0x06ce, 0x043d }, + { 0x06cf, 0x043e }, + { 0x06d0, 0x043f }, + { 0x06d1, 0x044f }, + { 0x06d2, 0x0440 }, + { 0x06d3, 0x0441 }, + { 0x06d4, 0x0442 }, + { 0x06d5, 0x0443 }, + { 0x06d6, 0x0436 }, + { 0x06d7, 0x0432 }, + { 0x06d8, 0x044c }, + { 0x06d9, 0x044b }, + { 0x06da, 0x0437 }, + { 0x06db, 0x0448 }, + { 0x06dc, 0x044d }, + { 0x06dd, 0x0449 }, + { 0x06de, 0x0447 }, + { 0x06df, 0x044a }, + { 0x06e0, 0x042e }, + { 0x06e1, 0x0410 }, + { 0x06e2, 0x0411 }, + { 0x06e3, 0x0426 }, + { 0x06e4, 0x0414 }, + { 0x06e5, 0x0415 }, + { 0x06e6, 0x0424 }, + { 0x06e7, 0x0413 }, + { 0x06e8, 0x0425 }, + { 0x06e9, 0x0418 }, + { 0x06ea, 0x0419 }, + { 0x06eb, 0x041a }, + { 0x06ec, 0x041b }, + { 0x06ed, 0x041c }, + { 0x06ee, 0x041d }, + { 0x06ef, 0x041e }, + { 0x06f0, 0x041f }, + { 0x06f1, 0x042f }, + { 0x06f2, 0x0420 }, + { 0x06f3, 0x0421 }, + { 0x06f4, 0x0422 }, + { 0x06f5, 0x0423 }, + { 0x06f6, 0x0416 }, + { 0x06f7, 0x0412 }, + { 0x06f8, 0x042c }, + { 0x06f9, 0x042b }, + { 0x06fa, 0x0417 }, + { 0x06fb, 0x0428 }, + { 0x06fc, 0x042d }, + { 0x06fd, 0x0429 }, + { 0x06fe, 0x0427 }, + { 0x06ff, 0x042a }, + { 0x07a1, 0x0386 }, + { 0x07a2, 0x0388 }, + { 0x07a3, 0x0389 }, + { 0x07a4, 0x038a }, + { 0x07a5, 0x03aa }, + { 0x07a7, 0x038c }, + { 0x07a8, 0x038e }, + { 0x07a9, 0x03ab }, + { 0x07ab, 0x038f }, + { 0x07ae, 0x0385 }, + { 0x07af, 0x2015 }, + { 0x07b1, 0x03ac }, + { 0x07b2, 0x03ad }, + { 0x07b3, 0x03ae }, + { 0x07b4, 0x03af }, + { 0x07b5, 0x03ca }, + { 0x07b6, 0x0390 }, + { 0x07b7, 0x03cc }, + { 0x07b8, 0x03cd }, + { 0x07b9, 0x03cb }, + { 0x07ba, 0x03b0 }, + { 0x07bb, 0x03ce }, + { 0x07c1, 0x0391 }, + { 0x07c2, 0x0392 }, + { 0x07c3, 0x0393 }, + { 0x07c4, 0x0394 }, + { 0x07c5, 0x0395 }, + { 0x07c6, 0x0396 }, + { 0x07c7, 0x0397 }, + { 0x07c8, 0x0398 }, + { 0x07c9, 0x0399 }, + { 0x07ca, 0x039a }, + { 0x07cb, 0x039b }, + { 0x07cc, 0x039c }, + { 0x07cd, 0x039d }, + { 0x07ce, 0x039e }, + { 0x07cf, 0x039f }, + { 0x07d0, 0x03a0 }, + { 0x07d1, 0x03a1 }, + { 0x07d2, 0x03a3 }, + { 0x07d4, 0x03a4 }, + { 0x07d5, 0x03a5 }, + { 0x07d6, 0x03a6 }, + { 0x07d7, 0x03a7 }, + { 0x07d8, 0x03a8 }, + { 0x07d9, 0x03a9 }, + { 0x07e1, 0x03b1 }, + { 0x07e2, 0x03b2 }, + { 0x07e3, 0x03b3 }, + { 0x07e4, 0x03b4 }, + { 0x07e5, 0x03b5 }, + { 0x07e6, 0x03b6 }, + { 0x07e7, 0x03b7 }, + { 0x07e8, 0x03b8 }, + { 0x07e9, 0x03b9 }, + { 0x07ea, 0x03ba }, + { 0x07eb, 0x03bb }, + { 0x07ec, 0x03bc }, + { 0x07ed, 0x03bd }, + { 0x07ee, 0x03be }, + { 0x07ef, 0x03bf }, + { 0x07f0, 0x03c0 }, + { 0x07f1, 0x03c1 }, + { 0x07f2, 0x03c3 }, + { 0x07f3, 0x03c2 }, + { 0x07f4, 0x03c4 }, + { 0x07f5, 0x03c5 }, + { 0x07f6, 0x03c6 }, + { 0x07f7, 0x03c7 }, + { 0x07f8, 0x03c8 }, + { 0x07f9, 0x03c9 }, + { 0x08a1, 0x23b7 }, + { 0x08a2, 0x250c }, + { 0x08a3, 0x2500 }, + { 0x08a4, 0x2320 }, + { 0x08a5, 0x2321 }, + { 0x08a6, 0x2502 }, + { 0x08a7, 0x23a1 }, + { 0x08a8, 0x23a3 }, + { 0x08a9, 0x23a4 }, + { 0x08aa, 0x23a6 }, + { 0x08ab, 0x239b }, + { 0x08ac, 0x239d }, + { 0x08ad, 0x239e }, + { 0x08ae, 0x23a0 }, + { 0x08af, 0x23a8 }, + { 0x08b0, 0x23ac }, + { 0x08bc, 0x2264 }, + { 0x08bd, 0x2260 }, + { 0x08be, 0x2265 }, + { 0x08bf, 0x222b }, + { 0x08c0, 0x2234 }, + { 0x08c1, 0x221d }, + { 0x08c2, 0x221e }, + { 0x08c5, 0x2207 }, + { 0x08c8, 0x223c }, + { 0x08c9, 0x2243 }, + { 0x08cd, 0x21d4 }, + { 0x08ce, 0x21d2 }, + { 0x08cf, 0x2261 }, + { 0x08d6, 0x221a }, + { 0x08da, 0x2282 }, + { 0x08db, 0x2283 }, + { 0x08dc, 0x2229 }, + { 0x08dd, 0x222a }, + { 0x08de, 0x2227 }, + { 0x08df, 0x2228 }, + { 0x08ef, 0x2202 }, + { 0x08f6, 0x0192 }, + { 0x08fb, 0x2190 }, + { 0x08fc, 0x2191 }, + { 0x08fd, 0x2192 }, + { 0x08fe, 0x2193 }, + { 0x09e0, 0x25c6 }, + { 0x09e1, 0x2592 }, + { 0x09e2, 0x2409 }, + { 0x09e3, 0x240c }, + { 0x09e4, 0x240d }, + { 0x09e5, 0x240a }, + { 0x09e8, 0x2424 }, + { 0x09e9, 0x240b }, + { 0x09ea, 0x2518 }, + { 0x09eb, 0x2510 }, + { 0x09ec, 0x250c }, + { 0x09ed, 0x2514 }, + { 0x09ee, 0x253c }, + { 0x09ef, 0x23ba }, + { 0x09f0, 0x23bb }, + { 0x09f1, 0x2500 }, + { 0x09f2, 0x23bc }, + { 0x09f3, 0x23bd }, + { 0x09f4, 0x251c }, + { 0x09f5, 0x2524 }, + { 0x09f6, 0x2534 }, + { 0x09f7, 0x252c }, + { 0x09f8, 0x2502 }, + { 0x0aa1, 0x2003 }, + { 0x0aa2, 0x2002 }, + { 0x0aa3, 0x2004 }, + { 0x0aa4, 0x2005 }, + { 0x0aa5, 0x2007 }, + { 0x0aa6, 0x2008 }, + { 0x0aa7, 0x2009 }, + { 0x0aa8, 0x200a }, + { 0x0aa9, 0x2014 }, + { 0x0aaa, 0x2013 }, + { 0x0aae, 0x2026 }, + { 0x0aaf, 0x2025 }, + { 0x0ab0, 0x2153 }, + { 0x0ab1, 0x2154 }, + { 0x0ab2, 0x2155 }, + { 0x0ab3, 0x2156 }, + { 0x0ab4, 0x2157 }, + { 0x0ab5, 0x2158 }, + { 0x0ab6, 0x2159 }, + { 0x0ab7, 0x215a }, + { 0x0ab8, 0x2105 }, + { 0x0abb, 0x2012 }, + { 0x0abc, 0x2329 }, + { 0x0abe, 0x232a }, + { 0x0ac3, 0x215b }, + { 0x0ac4, 0x215c }, + { 0x0ac5, 0x215d }, + { 0x0ac6, 0x215e }, + { 0x0ac9, 0x2122 }, + { 0x0aca, 0x2613 }, + { 0x0acc, 0x25c1 }, + { 0x0acd, 0x25b7 }, + { 0x0ace, 0x25cb }, + { 0x0acf, 0x25af }, + { 0x0ad0, 0x2018 }, + { 0x0ad1, 0x2019 }, + { 0x0ad2, 0x201c }, + { 0x0ad3, 0x201d }, + { 0x0ad4, 0x211e }, + { 0x0ad6, 0x2032 }, + { 0x0ad7, 0x2033 }, + { 0x0ad9, 0x271d }, + { 0x0adb, 0x25ac }, + { 0x0adc, 0x25c0 }, + { 0x0add, 0x25b6 }, + { 0x0ade, 0x25cf }, + { 0x0adf, 0x25ae }, + { 0x0ae0, 0x25e6 }, + { 0x0ae1, 0x25ab }, + { 0x0ae2, 0x25ad }, + { 0x0ae3, 0x25b3 }, + { 0x0ae4, 0x25bd }, + { 0x0ae5, 0x2606 }, + { 0x0ae6, 0x2022 }, + { 0x0ae7, 0x25aa }, + { 0x0ae8, 0x25b2 }, + { 0x0ae9, 0x25bc }, + { 0x0aea, 0x261c }, + { 0x0aeb, 0x261e }, + { 0x0aec, 0x2663 }, + { 0x0aed, 0x2666 }, + { 0x0aee, 0x2665 }, + { 0x0af0, 0x2720 }, + { 0x0af1, 0x2020 }, + { 0x0af2, 0x2021 }, + { 0x0af3, 0x2713 }, + { 0x0af4, 0x2717 }, + { 0x0af5, 0x266f }, + { 0x0af6, 0x266d }, + { 0x0af7, 0x2642 }, + { 0x0af8, 0x2640 }, + { 0x0af9, 0x260e }, + { 0x0afa, 0x2315 }, + { 0x0afb, 0x2117 }, + { 0x0afc, 0x2038 }, + { 0x0afd, 0x201a }, + { 0x0afe, 0x201e }, + { 0x0ba3, 0x003c }, + { 0x0ba6, 0x003e }, + { 0x0ba8, 0x2228 }, + { 0x0ba9, 0x2227 }, + { 0x0bc0, 0x00af }, + { 0x0bc2, 0x22a5 }, + { 0x0bc3, 0x2229 }, + { 0x0bc4, 0x230a }, + { 0x0bc6, 0x005f }, + { 0x0bca, 0x2218 }, + { 0x0bcc, 0x2395 }, + { 0x0bce, 0x22a4 }, + { 0x0bcf, 0x25cb }, + { 0x0bd3, 0x2308 }, + { 0x0bd6, 0x222a }, + { 0x0bd8, 0x2283 }, + { 0x0bda, 0x2282 }, + { 0x0bdc, 0x22a2 }, + { 0x0bfc, 0x22a3 }, + { 0x0cdf, 0x2017 }, + { 0x0ce0, 0x05d0 }, + { 0x0ce1, 0x05d1 }, + { 0x0ce2, 0x05d2 }, + { 0x0ce3, 0x05d3 }, + { 0x0ce4, 0x05d4 }, + { 0x0ce5, 0x05d5 }, + { 0x0ce6, 0x05d6 }, + { 0x0ce7, 0x05d7 }, + { 0x0ce8, 0x05d8 }, + { 0x0ce9, 0x05d9 }, + { 0x0cea, 0x05da }, + { 0x0ceb, 0x05db }, + { 0x0cec, 0x05dc }, + { 0x0ced, 0x05dd }, + { 0x0cee, 0x05de }, + { 0x0cef, 0x05df }, + { 0x0cf0, 0x05e0 }, + { 0x0cf1, 0x05e1 }, + { 0x0cf2, 0x05e2 }, + { 0x0cf3, 0x05e3 }, + { 0x0cf4, 0x05e4 }, + { 0x0cf5, 0x05e5 }, + { 0x0cf6, 0x05e6 }, + { 0x0cf7, 0x05e7 }, + { 0x0cf8, 0x05e8 }, + { 0x0cf9, 0x05e9 }, + { 0x0cfa, 0x05ea }, + { 0x0da1, 0x0e01 }, + { 0x0da2, 0x0e02 }, + { 0x0da3, 0x0e03 }, + { 0x0da4, 0x0e04 }, + { 0x0da5, 0x0e05 }, + { 0x0da6, 0x0e06 }, + { 0x0da7, 0x0e07 }, + { 0x0da8, 0x0e08 }, + { 0x0da9, 0x0e09 }, + { 0x0daa, 0x0e0a }, + { 0x0dab, 0x0e0b }, + { 0x0dac, 0x0e0c }, + { 0x0dad, 0x0e0d }, + { 0x0dae, 0x0e0e }, + { 0x0daf, 0x0e0f }, + { 0x0db0, 0x0e10 }, + { 0x0db1, 0x0e11 }, + { 0x0db2, 0x0e12 }, + { 0x0db3, 0x0e13 }, + { 0x0db4, 0x0e14 }, + { 0x0db5, 0x0e15 }, + { 0x0db6, 0x0e16 }, + { 0x0db7, 0x0e17 }, + { 0x0db8, 0x0e18 }, + { 0x0db9, 0x0e19 }, + { 0x0dba, 0x0e1a }, + { 0x0dbb, 0x0e1b }, + { 0x0dbc, 0x0e1c }, + { 0x0dbd, 0x0e1d }, + { 0x0dbe, 0x0e1e }, + { 0x0dbf, 0x0e1f }, + { 0x0dc0, 0x0e20 }, + { 0x0dc1, 0x0e21 }, + { 0x0dc2, 0x0e22 }, + { 0x0dc3, 0x0e23 }, + { 0x0dc4, 0x0e24 }, + { 0x0dc5, 0x0e25 }, + { 0x0dc6, 0x0e26 }, + { 0x0dc7, 0x0e27 }, + { 0x0dc8, 0x0e28 }, + { 0x0dc9, 0x0e29 }, + { 0x0dca, 0x0e2a }, + { 0x0dcb, 0x0e2b }, + { 0x0dcc, 0x0e2c }, + { 0x0dcd, 0x0e2d }, + { 0x0dce, 0x0e2e }, + { 0x0dcf, 0x0e2f }, + { 0x0dd0, 0x0e30 }, + { 0x0dd1, 0x0e31 }, + { 0x0dd2, 0x0e32 }, + { 0x0dd3, 0x0e33 }, + { 0x0dd4, 0x0e34 }, + { 0x0dd5, 0x0e35 }, + { 0x0dd6, 0x0e36 }, + { 0x0dd7, 0x0e37 }, + { 0x0dd8, 0x0e38 }, + { 0x0dd9, 0x0e39 }, + { 0x0dda, 0x0e3a }, + { 0x0ddf, 0x0e3f }, + { 0x0de0, 0x0e40 }, + { 0x0de1, 0x0e41 }, + { 0x0de2, 0x0e42 }, + { 0x0de3, 0x0e43 }, + { 0x0de4, 0x0e44 }, + { 0x0de5, 0x0e45 }, + { 0x0de6, 0x0e46 }, + { 0x0de7, 0x0e47 }, + { 0x0de8, 0x0e48 }, + { 0x0de9, 0x0e49 }, + { 0x0dea, 0x0e4a }, + { 0x0deb, 0x0e4b }, + { 0x0dec, 0x0e4c }, + { 0x0ded, 0x0e4d }, + { 0x0df0, 0x0e50 }, + { 0x0df1, 0x0e51 }, + { 0x0df2, 0x0e52 }, + { 0x0df3, 0x0e53 }, + { 0x0df4, 0x0e54 }, + { 0x0df5, 0x0e55 }, + { 0x0df6, 0x0e56 }, + { 0x0df7, 0x0e57 }, + { 0x0df8, 0x0e58 }, + { 0x0df9, 0x0e59 }, + { 0x0ea1, 0x3131 }, + { 0x0ea2, 0x3132 }, + { 0x0ea3, 0x3133 }, + { 0x0ea4, 0x3134 }, + { 0x0ea5, 0x3135 }, + { 0x0ea6, 0x3136 }, + { 0x0ea7, 0x3137 }, + { 0x0ea8, 0x3138 }, + { 0x0ea9, 0x3139 }, + { 0x0eaa, 0x313a }, + { 0x0eab, 0x313b }, + { 0x0eac, 0x313c }, + { 0x0ead, 0x313d }, + { 0x0eae, 0x313e }, + { 0x0eaf, 0x313f }, + { 0x0eb0, 0x3140 }, + { 0x0eb1, 0x3141 }, + { 0x0eb2, 0x3142 }, + { 0x0eb3, 0x3143 }, + { 0x0eb4, 0x3144 }, + { 0x0eb5, 0x3145 }, + { 0x0eb6, 0x3146 }, + { 0x0eb7, 0x3147 }, + { 0x0eb8, 0x3148 }, + { 0x0eb9, 0x3149 }, + { 0x0eba, 0x314a }, + { 0x0ebb, 0x314b }, + { 0x0ebc, 0x314c }, + { 0x0ebd, 0x314d }, + { 0x0ebe, 0x314e }, + { 0x0ebf, 0x314f }, + { 0x0ec0, 0x3150 }, + { 0x0ec1, 0x3151 }, + { 0x0ec2, 0x3152 }, + { 0x0ec3, 0x3153 }, + { 0x0ec4, 0x3154 }, + { 0x0ec5, 0x3155 }, + { 0x0ec6, 0x3156 }, + { 0x0ec7, 0x3157 }, + { 0x0ec8, 0x3158 }, + { 0x0ec9, 0x3159 }, + { 0x0eca, 0x315a }, + { 0x0ecb, 0x315b }, + { 0x0ecc, 0x315c }, + { 0x0ecd, 0x315d }, + { 0x0ece, 0x315e }, + { 0x0ecf, 0x315f }, + { 0x0ed0, 0x3160 }, + { 0x0ed1, 0x3161 }, + { 0x0ed2, 0x3162 }, + { 0x0ed3, 0x3163 }, + { 0x0ed4, 0x11a8 }, + { 0x0ed5, 0x11a9 }, + { 0x0ed6, 0x11aa }, + { 0x0ed7, 0x11ab }, + { 0x0ed8, 0x11ac }, + { 0x0ed9, 0x11ad }, + { 0x0eda, 0x11ae }, + { 0x0edb, 0x11af }, + { 0x0edc, 0x11b0 }, + { 0x0edd, 0x11b1 }, + { 0x0ede, 0x11b2 }, + { 0x0edf, 0x11b3 }, + { 0x0ee0, 0x11b4 }, + { 0x0ee1, 0x11b5 }, + { 0x0ee2, 0x11b6 }, + { 0x0ee3, 0x11b7 }, + { 0x0ee4, 0x11b8 }, + { 0x0ee5, 0x11b9 }, + { 0x0ee6, 0x11ba }, + { 0x0ee7, 0x11bb }, + { 0x0ee8, 0x11bc }, + { 0x0ee9, 0x11bd }, + { 0x0eea, 0x11be }, + { 0x0eeb, 0x11bf }, + { 0x0eec, 0x11c0 }, + { 0x0eed, 0x11c1 }, + { 0x0eee, 0x11c2 }, + { 0x0eef, 0x316d }, + { 0x0ef0, 0x3171 }, + { 0x0ef1, 0x3178 }, + { 0x0ef2, 0x317f }, + { 0x0ef3, 0x3181 }, + { 0x0ef4, 0x3184 }, + { 0x0ef5, 0x3186 }, + { 0x0ef6, 0x318d }, + { 0x0ef7, 0x318e }, + { 0x0ef8, 0x11eb }, + { 0x0ef9, 0x11f0 }, + { 0x0efa, 0x11f9 }, + { 0x0eff, 0x20a9 }, + { 0x13a4, 0x20ac }, + { 0x13bc, 0x0152 }, + { 0x13bd, 0x0153 }, + { 0x13be, 0x0178 }, + { 0x20ac, 0x20ac }, + { 0xfe50, '`' }, + { 0xfe51, 0x00b4 }, + { 0xfe52, '^' }, + { 0xfe53, '~' }, + { 0xfe54, 0x00af }, + { 0xfe55, 0x02d8 }, + { 0xfe56, 0x02d9 }, + { 0xfe57, 0x00a8 }, + { 0xfe58, 0x02da }, + { 0xfe59, 0x02dd }, + { 0xfe5a, 0x02c7 }, + { 0xfe5b, 0x00b8 }, + { 0xfe5c, 0x02db }, + { 0xfe5d, 0x037a }, + { 0xfe5e, 0x309b }, + { 0xfe5f, 0x309c }, + { 0xfe63, '/' }, + { 0xfe64, 0x02bc }, + { 0xfe65, 0x02bd }, + { 0xfe66, 0x02f5 }, + { 0xfe67, 0x02f3 }, + { 0xfe68, 0x02cd }, + { 0xfe69, 0xa788 }, + { 0xfe6a, 0x02f7 }, + { 0xfe6e, ',' }, + { 0xfe6f, 0x00a4 }, + { 0xfe80, 'a' }, /* XK_dead_a */ + { 0xfe81, 'A' }, /* XK_dead_A */ + { 0xfe82, 'e' }, /* XK_dead_e */ + { 0xfe83, 'E' }, /* XK_dead_E */ + { 0xfe84, 'i' }, /* XK_dead_i */ + { 0xfe85, 'I' }, /* XK_dead_I */ + { 0xfe86, 'o' }, /* XK_dead_o */ + { 0xfe87, 'O' }, /* XK_dead_O */ + { 0xfe88, 'u' }, /* XK_dead_u */ + { 0xfe89, 'U' }, /* XK_dead_U */ + { 0xfe8a, 0x0259 }, + { 0xfe8b, 0x018f }, + { 0xfe8c, 0x00b5 }, + { 0xfe90, '_' }, + { 0xfe91, 0x02c8 }, + { 0xfe92, 0x02cc }, + { 0xff80 /*XKB_KEY_KP_Space*/, ' ' }, + { 0xff95 /*XKB_KEY_KP_7*/, 0x0037 }, + { 0xff96 /*XKB_KEY_KP_4*/, 0x0034 }, + { 0xff97 /*XKB_KEY_KP_8*/, 0x0038 }, + { 0xff98 /*XKB_KEY_KP_6*/, 0x0036 }, + { 0xff99 /*XKB_KEY_KP_2*/, 0x0032 }, + { 0xff9a /*XKB_KEY_KP_9*/, 0x0039 }, + { 0xff9b /*XKB_KEY_KP_3*/, 0x0033 }, + { 0xff9c /*XKB_KEY_KP_1*/, 0x0031 }, + { 0xff9d /*XKB_KEY_KP_5*/, 0x0035 }, + { 0xff9e /*XKB_KEY_KP_0*/, 0x0030 }, + { 0xffaa /*XKB_KEY_KP_Multiply*/, '*' }, + { 0xffab /*XKB_KEY_KP_Add*/, '+' }, + { 0xffac /*XKB_KEY_KP_Separator*/, ',' }, + { 0xffad /*XKB_KEY_KP_Subtract*/, '-' }, + { 0xffae /*XKB_KEY_KP_Decimal*/, '.' }, + { 0xffaf /*XKB_KEY_KP_Divide*/, '/' }, + { 0xffb0 /*XKB_KEY_KP_0*/, 0x0030 }, + { 0xffb1 /*XKB_KEY_KP_1*/, 0x0031 }, + { 0xffb2 /*XKB_KEY_KP_2*/, 0x0032 }, + { 0xffb3 /*XKB_KEY_KP_3*/, 0x0033 }, + { 0xffb4 /*XKB_KEY_KP_4*/, 0x0034 }, + { 0xffb5 /*XKB_KEY_KP_5*/, 0x0035 }, + { 0xffb6 /*XKB_KEY_KP_6*/, 0x0036 }, + { 0xffb7 /*XKB_KEY_KP_7*/, 0x0037 }, + { 0xffb8 /*XKB_KEY_KP_8*/, 0x0038 }, + { 0xffb9 /*XKB_KEY_KP_9*/, 0x0039 }, + { 0xffbd /*XKB_KEY_KP_Equal*/, '=' } +}; + +_SOKOL_PRIVATE int _sapp_x11_error_handler(Display* display, XErrorEvent* event) { + _SOKOL_UNUSED(display); + _sapp.x11.error_code = event->error_code; + return 0; +} + +_SOKOL_PRIVATE void _sapp_x11_grab_error_handler(void) { + _sapp.x11.error_code = Success; + XSetErrorHandler(_sapp_x11_error_handler); +} + +_SOKOL_PRIVATE void _sapp_x11_release_error_handler(void) { + XSync(_sapp.x11.display, False); + XSetErrorHandler(NULL); +} + +_SOKOL_PRIVATE void _sapp_x11_init_extensions(void) { + _sapp.x11.UTF8_STRING = XInternAtom(_sapp.x11.display, "UTF8_STRING", False); + _sapp.x11.WM_PROTOCOLS = XInternAtom(_sapp.x11.display, "WM_PROTOCOLS", False); + _sapp.x11.WM_DELETE_WINDOW = XInternAtom(_sapp.x11.display, "WM_DELETE_WINDOW", False); + _sapp.x11.WM_STATE = XInternAtom(_sapp.x11.display, "WM_STATE", False); + _sapp.x11.NET_WM_NAME = XInternAtom(_sapp.x11.display, "_NET_WM_NAME", False); + _sapp.x11.NET_WM_ICON_NAME = XInternAtom(_sapp.x11.display, "_NET_WM_ICON_NAME", False); + _sapp.x11.NET_WM_STATE = XInternAtom(_sapp.x11.display, "_NET_WM_STATE", False); + _sapp.x11.NET_WM_STATE_FULLSCREEN = XInternAtom(_sapp.x11.display, "_NET_WM_STATE_FULLSCREEN", False); + if (_sapp.drop.enabled) { + _sapp.x11.xdnd.XdndAware = XInternAtom(_sapp.x11.display, "XdndAware", False); + _sapp.x11.xdnd.XdndEnter = XInternAtom(_sapp.x11.display, "XdndEnter", False); + _sapp.x11.xdnd.XdndPosition = XInternAtom(_sapp.x11.display, "XdndPosition", False); + _sapp.x11.xdnd.XdndStatus = XInternAtom(_sapp.x11.display, "XdndStatus", False); + _sapp.x11.xdnd.XdndActionCopy = XInternAtom(_sapp.x11.display, "XdndActionCopy", False); + _sapp.x11.xdnd.XdndDrop = XInternAtom(_sapp.x11.display, "XdndDrop", False); + _sapp.x11.xdnd.XdndFinished = XInternAtom(_sapp.x11.display, "XdndFinished", False); + _sapp.x11.xdnd.XdndSelection = XInternAtom(_sapp.x11.display, "XdndSelection", False); + _sapp.x11.xdnd.XdndTypeList = XInternAtom(_sapp.x11.display, "XdndTypeList", False); + _sapp.x11.xdnd.text_uri_list = XInternAtom(_sapp.x11.display, "text/uri-list", False); + } + + /* check Xi extension for raw mouse input */ + if (XQueryExtension(_sapp.x11.display, "XInputExtension", &_sapp.x11.xi.major_opcode, &_sapp.x11.xi.event_base, &_sapp.x11.xi.error_base)) { + _sapp.x11.xi.major = 2; + _sapp.x11.xi.minor = 0; + if (XIQueryVersion(_sapp.x11.display, &_sapp.x11.xi.major, &_sapp.x11.xi.minor) == Success) { + _sapp.x11.xi.available = true; + } + } +} + +_SOKOL_PRIVATE void _sapp_x11_query_system_dpi(void) { + /* from GLFW: + + NOTE: Default to the display-wide DPI as we don't currently have a policy + for which monitor a window is considered to be on + + _sapp.x11.dpi = DisplayWidth(_sapp.x11.display, _sapp.x11.screen) * + 25.4f / DisplayWidthMM(_sapp.x11.display, _sapp.x11.screen); + + NOTE: Basing the scale on Xft.dpi where available should provide the most + consistent user experience (matches Qt, Gtk, etc), although not + always the most accurate one + */ + char* rms = XResourceManagerString(_sapp.x11.display); + if (rms) { + XrmDatabase db = XrmGetStringDatabase(rms); + if (db) { + XrmValue value; + char* type = NULL; + if (XrmGetResource(db, "Xft.dpi", "Xft.Dpi", &type, &value)) { + if (type && strcmp(type, "String") == 0) { + _sapp.x11.dpi = atof(value.addr); + } + } + XrmDestroyDatabase(db); + } + } +} + +_SOKOL_PRIVATE bool _sapp_glx_has_ext(const char* ext, const char* extensions) { + SOKOL_ASSERT(ext); + const char* start = extensions; + while (true) { + const char* where = strstr(start, ext); + if (!where) { + return false; + } + const char* terminator = where + strlen(ext); + if ((where == start) || (*(where - 1) == ' ')) { + if (*terminator == ' ' || *terminator == '\0') { + break; + } + } + start = terminator; + } + return true; +} + +_SOKOL_PRIVATE bool _sapp_glx_extsupported(const char* ext, const char* extensions) { + if (extensions) { + return _sapp_glx_has_ext(ext, extensions); + } + else { + return false; + } +} + +_SOKOL_PRIVATE void* _sapp_glx_getprocaddr(const char* procname) +{ + if (_sapp.glx.GetProcAddress) { + return (void*) _sapp.glx.GetProcAddress(procname); + } + else if (_sapp.glx.GetProcAddressARB) { + return (void*) _sapp.glx.GetProcAddressARB(procname); + } + else { + return dlsym(_sapp.glx.libgl, procname); + } +} + +_SOKOL_PRIVATE void _sapp_glx_init() { + const char* sonames[] = { "libGL.so.1", "libGL.so", 0 }; + for (int i = 0; sonames[i]; i++) { + _sapp.glx.libgl = dlopen(sonames[i], RTLD_LAZY|RTLD_GLOBAL); + if (_sapp.glx.libgl) { + break; + } + } + if (!_sapp.glx.libgl) { + _sapp_fail("GLX: failed to load libGL"); + } + _sapp.glx.GetFBConfigs = (PFNGLXGETFBCONFIGSPROC) dlsym(_sapp.glx.libgl, "glXGetFBConfigs"); + _sapp.glx.GetFBConfigAttrib = (PFNGLXGETFBCONFIGATTRIBPROC) dlsym(_sapp.glx.libgl, "glXGetFBConfigAttrib"); + _sapp.glx.GetClientString = (PFNGLXGETCLIENTSTRINGPROC) dlsym(_sapp.glx.libgl, "glXGetClientString"); + _sapp.glx.QueryExtension = (PFNGLXQUERYEXTENSIONPROC) dlsym(_sapp.glx.libgl, "glXQueryExtension"); + _sapp.glx.QueryVersion = (PFNGLXQUERYVERSIONPROC) dlsym(_sapp.glx.libgl, "glXQueryVersion"); + _sapp.glx.DestroyContext = (PFNGLXDESTROYCONTEXTPROC) dlsym(_sapp.glx.libgl, "glXDestroyContext"); + _sapp.glx.MakeCurrent = (PFNGLXMAKECURRENTPROC) dlsym(_sapp.glx.libgl, "glXMakeCurrent"); + _sapp.glx.SwapBuffers = (PFNGLXSWAPBUFFERSPROC) dlsym(_sapp.glx.libgl, "glXSwapBuffers"); + _sapp.glx.QueryExtensionsString = (PFNGLXQUERYEXTENSIONSSTRINGPROC) dlsym(_sapp.glx.libgl, "glXQueryExtensionsString"); + _sapp.glx.CreateWindow = (PFNGLXCREATEWINDOWPROC) dlsym(_sapp.glx.libgl, "glXCreateWindow"); + _sapp.glx.DestroyWindow = (PFNGLXDESTROYWINDOWPROC) dlsym(_sapp.glx.libgl, "glXDestroyWindow"); + _sapp.glx.GetProcAddress = (PFNGLXGETPROCADDRESSPROC) dlsym(_sapp.glx.libgl, "glXGetProcAddress"); + _sapp.glx.GetProcAddressARB = (PFNGLXGETPROCADDRESSPROC) dlsym(_sapp.glx.libgl, "glXGetProcAddressARB"); + _sapp.glx.GetVisualFromFBConfig = (PFNGLXGETVISUALFROMFBCONFIGPROC) dlsym(_sapp.glx.libgl, "glXGetVisualFromFBConfig"); + if (!_sapp.glx.GetFBConfigs || + !_sapp.glx.GetFBConfigAttrib || + !_sapp.glx.GetClientString || + !_sapp.glx.QueryExtension || + !_sapp.glx.QueryVersion || + !_sapp.glx.DestroyContext || + !_sapp.glx.MakeCurrent || + !_sapp.glx.SwapBuffers || + !_sapp.glx.QueryExtensionsString || + !_sapp.glx.CreateWindow || + !_sapp.glx.DestroyWindow || + !_sapp.glx.GetProcAddress || + !_sapp.glx.GetProcAddressARB || + !_sapp.glx.GetVisualFromFBConfig) + { + _sapp_fail("GLX: failed to load required entry points"); + } + + if (!_sapp.glx.QueryExtension(_sapp.x11.display, &_sapp.glx.error_base, &_sapp.glx.event_base)) { + _sapp_fail("GLX: GLX extension not found"); + } + if (!_sapp.glx.QueryVersion(_sapp.x11.display, &_sapp.glx.major, &_sapp.glx.minor)) { + _sapp_fail("GLX: Failed to query GLX version"); + } + if (_sapp.glx.major == 1 && _sapp.glx.minor < 3) { + _sapp_fail("GLX: GLX version 1.3 is required"); + } + const char* exts = _sapp.glx.QueryExtensionsString(_sapp.x11.display, _sapp.x11.screen); + if (_sapp_glx_extsupported("GLX_EXT_swap_control", exts)) { + _sapp.glx.SwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC) _sapp_glx_getprocaddr("glXSwapIntervalEXT"); + _sapp.glx.EXT_swap_control = 0 != _sapp.glx.SwapIntervalEXT; + } + if (_sapp_glx_extsupported("GLX_MESA_swap_control", exts)) { + _sapp.glx.SwapIntervalMESA = (PFNGLXSWAPINTERVALMESAPROC) _sapp_glx_getprocaddr("glXSwapIntervalMESA"); + _sapp.glx.MESA_swap_control = 0 != _sapp.glx.SwapIntervalMESA; + } + _sapp.glx.ARB_multisample = _sapp_glx_extsupported("GLX_ARB_multisample", exts); + if (_sapp_glx_extsupported("GLX_ARB_create_context", exts)) { + _sapp.glx.CreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC) _sapp_glx_getprocaddr("glXCreateContextAttribsARB"); + _sapp.glx.ARB_create_context = 0 != _sapp.glx.CreateContextAttribsARB; + } + _sapp.glx.ARB_create_context_profile = _sapp_glx_extsupported("GLX_ARB_create_context_profile", exts); +} + +_SOKOL_PRIVATE int _sapp_glx_attrib(GLXFBConfig fbconfig, int attrib) { + int value; + _sapp.glx.GetFBConfigAttrib(_sapp.x11.display, fbconfig, attrib, &value); + return value; +} + +_SOKOL_PRIVATE GLXFBConfig _sapp_glx_choosefbconfig() { + GLXFBConfig* native_configs; + _sapp_gl_fbconfig* usable_configs; + const _sapp_gl_fbconfig* closest; + int i, native_count, usable_count; + const char* vendor; + bool trust_window_bit = true; + + /* HACK: This is a (hopefully temporary) workaround for Chromium + (VirtualBox GL) not setting the window bit on any GLXFBConfigs + */ + vendor = _sapp.glx.GetClientString(_sapp.x11.display, GLX_VENDOR); + if (vendor && strcmp(vendor, "Chromium") == 0) { + trust_window_bit = false; + } + + native_configs = _sapp.glx.GetFBConfigs(_sapp.x11.display, _sapp.x11.screen, &native_count); + if (!native_configs || !native_count) { + _sapp_fail("GLX: No GLXFBConfigs returned"); + } + + usable_configs = (_sapp_gl_fbconfig*) SOKOL_CALLOC((size_t)native_count, sizeof(_sapp_gl_fbconfig)); + usable_count = 0; + for (i = 0; i < native_count; i++) { + const GLXFBConfig n = native_configs[i]; + _sapp_gl_fbconfig* u = usable_configs + usable_count; + _sapp_gl_init_fbconfig(u); + + /* Only consider RGBA GLXFBConfigs */ + if (0 == (_sapp_glx_attrib(n, GLX_RENDER_TYPE) & GLX_RGBA_BIT)) { + continue; + } + /* Only consider window GLXFBConfigs */ + if (0 == (_sapp_glx_attrib(n, GLX_DRAWABLE_TYPE) & GLX_WINDOW_BIT)) { + if (trust_window_bit) { + continue; + } + } + u->red_bits = _sapp_glx_attrib(n, GLX_RED_SIZE); + u->green_bits = _sapp_glx_attrib(n, GLX_GREEN_SIZE); + u->blue_bits = _sapp_glx_attrib(n, GLX_BLUE_SIZE); + u->alpha_bits = _sapp_glx_attrib(n, GLX_ALPHA_SIZE); + u->depth_bits = _sapp_glx_attrib(n, GLX_DEPTH_SIZE); + u->stencil_bits = _sapp_glx_attrib(n, GLX_STENCIL_SIZE); + if (_sapp_glx_attrib(n, GLX_DOUBLEBUFFER)) { + u->doublebuffer = true; + } + if (_sapp.glx.ARB_multisample) { + u->samples = _sapp_glx_attrib(n, GLX_SAMPLES); + } + u->handle = (uintptr_t) n; + usable_count++; + } + _sapp_gl_fbconfig desired; + _sapp_gl_init_fbconfig(&desired); + desired.red_bits = 8; + desired.green_bits = 8; + desired.blue_bits = 8; + desired.alpha_bits = 8; + desired.depth_bits = 24; + desired.stencil_bits = 8; + desired.doublebuffer = true; + desired.samples = _sapp.sample_count > 1 ? _sapp.sample_count : 0; + closest = _sapp_gl_choose_fbconfig(&desired, usable_configs, usable_count); + GLXFBConfig result = 0; + if (closest) { + result = (GLXFBConfig) closest->handle; + } + XFree(native_configs); + SOKOL_FREE(usable_configs); + return result; +} + +_SOKOL_PRIVATE void _sapp_glx_choose_visual(Visual** visual, int* depth) { + GLXFBConfig native = _sapp_glx_choosefbconfig(); + if (0 == native) { + _sapp_fail("GLX: Failed to find a suitable GLXFBConfig"); + } + XVisualInfo* result = _sapp.glx.GetVisualFromFBConfig(_sapp.x11.display, native); + if (!result) { + _sapp_fail("GLX: Failed to retrieve Visual for GLXFBConfig"); + } + *visual = result->visual; + *depth = result->depth; + XFree(result); +} + +_SOKOL_PRIVATE void _sapp_glx_create_context(void) { + GLXFBConfig native = _sapp_glx_choosefbconfig(); + if (0 == native){ + _sapp_fail("GLX: Failed to find a suitable GLXFBConfig (2)"); + } + if (!(_sapp.glx.ARB_create_context && _sapp.glx.ARB_create_context_profile)) { + _sapp_fail("GLX: ARB_create_context and ARB_create_context_profile required"); + } + _sapp_x11_grab_error_handler(); + const int attribs[] = { + GLX_CONTEXT_MAJOR_VERSION_ARB, 3, + GLX_CONTEXT_MINOR_VERSION_ARB, 3, + GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, + GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, + 0, 0 + }; + _sapp.glx.ctx = _sapp.glx.CreateContextAttribsARB(_sapp.x11.display, native, NULL, True, attribs); + if (!_sapp.glx.ctx) { + _sapp_fail("GLX: failed to create GL context"); + } + _sapp_x11_release_error_handler(); + _sapp.glx.window = _sapp.glx.CreateWindow(_sapp.x11.display, native, _sapp.x11.window, NULL); + if (!_sapp.glx.window) { + _sapp_fail("GLX: failed to create window"); + } +} + +_SOKOL_PRIVATE void _sapp_glx_destroy_context(void) { + if (_sapp.glx.window) { + _sapp.glx.DestroyWindow(_sapp.x11.display, _sapp.glx.window); + _sapp.glx.window = 0; + } + if (_sapp.glx.ctx) { + _sapp.glx.DestroyContext(_sapp.x11.display, _sapp.glx.ctx); + _sapp.glx.ctx = 0; + } +} + +_SOKOL_PRIVATE void _sapp_glx_make_current(void) { + _sapp.glx.MakeCurrent(_sapp.x11.display, _sapp.glx.window, _sapp.glx.ctx); +} + +_SOKOL_PRIVATE void _sapp_glx_swap_buffers(void) { + _sapp.glx.SwapBuffers(_sapp.x11.display, _sapp.glx.window); +} + +_SOKOL_PRIVATE void _sapp_glx_swapinterval(int interval) { + _sapp_glx_make_current(); + if (_sapp.glx.EXT_swap_control) { + _sapp.glx.SwapIntervalEXT(_sapp.x11.display, _sapp.glx.window, interval); + } + else if (_sapp.glx.MESA_swap_control) { + _sapp.glx.SwapIntervalMESA(interval); + } +} + +_SOKOL_PRIVATE void _sapp_x11_send_event(Atom type, int a, int b, int c, int d, int e) { + XEvent event; + memset(&event, 0, sizeof(event)); + + event.type = ClientMessage; + event.xclient.window = _sapp.x11.window; + event.xclient.format = 32; + event.xclient.message_type = type; + event.xclient.data.l[0] = a; + event.xclient.data.l[1] = b; + event.xclient.data.l[2] = c; + event.xclient.data.l[3] = d; + event.xclient.data.l[4] = e; + + XSendEvent(_sapp.x11.display, _sapp.x11.root, + False, + SubstructureNotifyMask | SubstructureRedirectMask, + &event); +} + +_SOKOL_PRIVATE void _sapp_x11_query_window_size(void) { + XWindowAttributes attribs; + XGetWindowAttributes(_sapp.x11.display, _sapp.x11.window, &attribs); + _sapp.window_width = attribs.width; + _sapp.window_height = attribs.height; + _sapp.framebuffer_width = _sapp.window_width; + _sapp.framebuffer_height = _sapp.window_height; +} + +_SOKOL_PRIVATE void _sapp_x11_set_fullscreen(bool enable) { + /* NOTE: this function must be called after XMapWindow (which happens in _sapp_x11_show_window()) */ + if (_sapp.x11.NET_WM_STATE && _sapp.x11.NET_WM_STATE_FULLSCREEN) { + if (enable) { + const int _NET_WM_STATE_ADD = 1; + _sapp_x11_send_event(_sapp.x11.NET_WM_STATE, + _NET_WM_STATE_ADD, + _sapp.x11.NET_WM_STATE_FULLSCREEN, + 0, 1, 0); + } + else { + const int _NET_WM_STATE_REMOVE = 0; + _sapp_x11_send_event(_sapp.x11.NET_WM_STATE, + _NET_WM_STATE_REMOVE, + _sapp.x11.NET_WM_STATE_FULLSCREEN, + 0, 1, 0); + } + } + XFlush(_sapp.x11.display); +} + +_SOKOL_PRIVATE void _sapp_x11_create_hidden_cursor(void) { + SOKOL_ASSERT(0 == _sapp.x11.hidden_cursor); + const int w = 16; + const int h = 16; + XcursorImage* img = XcursorImageCreate(w, h); + SOKOL_ASSERT(img && (img->width == 16) && (img->height == 16) && img->pixels); + img->xhot = 0; + img->yhot = 0; + const size_t num_bytes = (size_t)(w * h) * sizeof(XcursorPixel); + memset(img->pixels, 0, num_bytes); + _sapp.x11.hidden_cursor = XcursorImageLoadCursor(_sapp.x11.display, img); + XcursorImageDestroy(img); +} + +_SOKOL_PRIVATE void _sapp_x11_toggle_fullscreen(void) { + _sapp.fullscreen = !_sapp.fullscreen; + _sapp_x11_set_fullscreen(_sapp.fullscreen); + _sapp_x11_query_window_size(); +} + +_SOKOL_PRIVATE void _sapp_x11_show_mouse(bool show) { + if (show) { + XUndefineCursor(_sapp.x11.display, _sapp.x11.window); + } + else { + XDefineCursor(_sapp.x11.display, _sapp.x11.window, _sapp.x11.hidden_cursor); + } +} + +_SOKOL_PRIVATE void _sapp_x11_lock_mouse(bool lock) { + if (lock == _sapp.mouse.locked) { + return; + } + _sapp.mouse.dx = 0.0f; + _sapp.mouse.dy = 0.0f; + _sapp.mouse.locked = lock; + if (_sapp.mouse.locked) { + if (_sapp.x11.xi.available) { + XIEventMask em; + unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 }; // XIMaskLen is a macro + em.deviceid = XIAllMasterDevices; + em.mask_len = sizeof(mask); + em.mask = mask; + XISetMask(mask, XI_RawMotion); + XISelectEvents(_sapp.x11.display, _sapp.x11.root, &em, 1); + } + XGrabPointer(_sapp.x11.display, // display + _sapp.x11.window, // grab_window + True, // owner_events + ButtonPressMask | ButtonReleaseMask | PointerMotionMask, // event_mask + GrabModeAsync, // pointer_mode + GrabModeAsync, // keyboard_mode + _sapp.x11.window, // confine_to + _sapp.x11.hidden_cursor, // cursor + CurrentTime); // time + } + else { + if (_sapp.x11.xi.available) { + XIEventMask em; + unsigned char mask[] = { 0 }; + em.deviceid = XIAllMasterDevices; + em.mask_len = sizeof(mask); + em.mask = mask; + XISelectEvents(_sapp.x11.display, _sapp.x11.root, &em, 1); + } + XWarpPointer(_sapp.x11.display, None, _sapp.x11.window, 0, 0, 0, 0, (int) _sapp.mouse.x, _sapp.mouse.y); + XUngrabPointer(_sapp.x11.display, CurrentTime); + } + XFlush(_sapp.x11.display); +} + +_SOKOL_PRIVATE void _sapp_x11_update_window_title(void) { + Xutf8SetWMProperties(_sapp.x11.display, + _sapp.x11.window, + _sapp.window_title, _sapp.window_title, + NULL, 0, NULL, NULL, NULL); + XChangeProperty(_sapp.x11.display, _sapp.x11.window, + _sapp.x11.NET_WM_NAME, _sapp.x11.UTF8_STRING, 8, + PropModeReplace, + (unsigned char*)_sapp.window_title, + strlen(_sapp.window_title)); + XChangeProperty(_sapp.x11.display, _sapp.x11.window, + _sapp.x11.NET_WM_ICON_NAME, _sapp.x11.UTF8_STRING, 8, + PropModeReplace, + (unsigned char*)_sapp.window_title, + strlen(_sapp.window_title)); + XFlush(_sapp.x11.display); +} + +_SOKOL_PRIVATE void _sapp_x11_create_window(Visual* visual, int depth) { + _sapp.x11.colormap = XCreateColormap(_sapp.x11.display, _sapp.x11.root, visual, AllocNone); + XSetWindowAttributes wa; + memset(&wa, 0, sizeof(wa)); + const uint32_t wamask = CWBorderPixel | CWColormap | CWEventMask; + wa.colormap = _sapp.x11.colormap; + wa.border_pixel = 0; + wa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask | + PointerMotionMask | ButtonPressMask | ButtonReleaseMask | + ExposureMask | FocusChangeMask | VisibilityChangeMask | + EnterWindowMask | LeaveWindowMask | PropertyChangeMask; + _sapp_x11_grab_error_handler(); + _sapp.x11.window = XCreateWindow(_sapp.x11.display, + _sapp.x11.root, + 0, 0, + (uint32_t)_sapp.window_width, + (uint32_t)_sapp.window_height, + 0, /* border width */ + depth, /* color depth */ + InputOutput, + visual, + wamask, + &wa); + _sapp_x11_release_error_handler(); + if (!_sapp.x11.window) { + _sapp_fail("X11: Failed to create window"); + } + Atom protocols[] = { + _sapp.x11.WM_DELETE_WINDOW + }; + XSetWMProtocols(_sapp.x11.display, _sapp.x11.window, protocols, 1); + + XSizeHints* hints = XAllocSizeHints(); + hints->flags |= PWinGravity; + hints->win_gravity = StaticGravity; + XSetWMNormalHints(_sapp.x11.display, _sapp.x11.window, hints); + XFree(hints); + + /* announce support for drag'n'drop */ + if (_sapp.drop.enabled) { + const Atom version = _SAPP_X11_XDND_VERSION; + XChangeProperty(_sapp.x11.display, _sapp.x11.window, _sapp.x11.xdnd.XdndAware, XA_ATOM, 32, PropModeReplace, (unsigned char*) &version, 1); + } + + _sapp_x11_update_window_title(); +} + +_SOKOL_PRIVATE void _sapp_x11_destroy_window(void) { + if (_sapp.x11.window) { + XUnmapWindow(_sapp.x11.display, _sapp.x11.window); + XDestroyWindow(_sapp.x11.display, _sapp.x11.window); + _sapp.x11.window = 0; + } + if (_sapp.x11.colormap) { + XFreeColormap(_sapp.x11.display, _sapp.x11.colormap); + _sapp.x11.colormap = 0; + } + XFlush(_sapp.x11.display); +} + +_SOKOL_PRIVATE bool _sapp_x11_window_visible(void) { + XWindowAttributes wa; + XGetWindowAttributes(_sapp.x11.display, _sapp.x11.window, &wa); + return wa.map_state == IsViewable; +} + +_SOKOL_PRIVATE void _sapp_x11_show_window(void) { + if (!_sapp_x11_window_visible()) { + XMapWindow(_sapp.x11.display, _sapp.x11.window); + XRaiseWindow(_sapp.x11.display, _sapp.x11.window); + XFlush(_sapp.x11.display); + } +} + +_SOKOL_PRIVATE void _sapp_x11_hide_window(void) { + XUnmapWindow(_sapp.x11.display, _sapp.x11.window); + XFlush(_sapp.x11.display); +} + +_SOKOL_PRIVATE unsigned long _sapp_x11_get_window_property(Window window, Atom property, Atom type, unsigned char** value) { + Atom actualType; + int actualFormat; + unsigned long itemCount, bytesAfter; + XGetWindowProperty(_sapp.x11.display, + window, + property, + 0, + LONG_MAX, + False, + type, + &actualType, + &actualFormat, + &itemCount, + &bytesAfter, + value); + return itemCount; +} + +_SOKOL_PRIVATE int _sapp_x11_get_window_state(void) { + int result = WithdrawnState; + struct { + CARD32 state; + Window icon; + } *state = NULL; + + if (_sapp_x11_get_window_property(_sapp.x11.window, _sapp.x11.WM_STATE, _sapp.x11.WM_STATE, (unsigned char**)&state) >= 2) { + result = (int)state->state; + } + if (state) { + XFree(state); + } + return result; +} + +_SOKOL_PRIVATE uint32_t _sapp_x11_mod(uint32_t x11_mods) { + uint32_t mods = 0; + if (x11_mods & ShiftMask) { + mods |= SAPP_MODIFIER_SHIFT; + } + if (x11_mods & ControlMask) { + mods |= SAPP_MODIFIER_CTRL; + } + if (x11_mods & Mod1Mask) { + mods |= SAPP_MODIFIER_ALT; + } + if (x11_mods & Mod4Mask) { + mods |= SAPP_MODIFIER_SUPER; + } + return mods; +} + +_SOKOL_PRIVATE void _sapp_x11_app_event(sapp_event_type type) { + if (_sapp_events_enabled()) { + _sapp_init_event(type); + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE sapp_mousebutton _sapp_x11_translate_button(const XEvent* event) { + switch (event->xbutton.button) { + case Button1: return SAPP_MOUSEBUTTON_LEFT; + case Button2: return SAPP_MOUSEBUTTON_MIDDLE; + case Button3: return SAPP_MOUSEBUTTON_RIGHT; + default: return SAPP_MOUSEBUTTON_INVALID; + } +} + +_SOKOL_PRIVATE void _sapp_x11_mouse_event(sapp_event_type type, sapp_mousebutton btn, uint32_t mods) { + if (_sapp_events_enabled()) { + _sapp_init_event(type); + _sapp.event.mouse_button = btn; + _sapp.event.modifiers = mods; + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE void _sapp_x11_scroll_event(float x, float y, uint32_t mods) { + if (_sapp_events_enabled()) { + _sapp_init_event(SAPP_EVENTTYPE_MOUSE_SCROLL); + _sapp.event.modifiers = mods; + _sapp.event.scroll_x = x; + _sapp.event.scroll_y = y; + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE void _sapp_x11_key_event(sapp_event_type type, sapp_keycode key, bool repeat, uint32_t mods) { + if (_sapp_events_enabled()) { + _sapp_init_event(type); + _sapp.event.key_code = key; + _sapp.event.key_repeat = repeat; + _sapp.event.modifiers = mods; + _sapp_call_event(&_sapp.event); + /* check if a CLIPBOARD_PASTED event must be sent too */ + if (_sapp.clipboard.enabled && + (type == SAPP_EVENTTYPE_KEY_DOWN) && + (_sapp.event.modifiers == SAPP_MODIFIER_CTRL) && + (_sapp.event.key_code == SAPP_KEYCODE_V)) + { + _sapp_init_event(SAPP_EVENTTYPE_CLIPBOARD_PASTED); + _sapp_call_event(&_sapp.event); + } + } +} + +_SOKOL_PRIVATE void _sapp_x11_char_event(uint32_t chr, bool repeat, uint32_t mods) { + if (_sapp_events_enabled()) { + _sapp_init_event(SAPP_EVENTTYPE_CHAR); + _sapp.event.char_code = chr; + _sapp.event.key_repeat = repeat; + _sapp.event.modifiers = mods; + _sapp_call_event(&_sapp.event); + } +} + +_SOKOL_PRIVATE sapp_keycode _sapp_x11_translate_key(int scancode) { + int dummy; + KeySym* keysyms = XGetKeyboardMapping(_sapp.x11.display, scancode, 1, &dummy); + SOKOL_ASSERT(keysyms); + KeySym keysym = keysyms[0]; + XFree(keysyms); + switch (keysym) { + case XK_Escape: return SAPP_KEYCODE_ESCAPE; + case XK_Tab: return SAPP_KEYCODE_TAB; + case XK_Shift_L: return SAPP_KEYCODE_LEFT_SHIFT; + case XK_Shift_R: return SAPP_KEYCODE_RIGHT_SHIFT; + case XK_Control_L: return SAPP_KEYCODE_LEFT_CONTROL; + case XK_Control_R: return SAPP_KEYCODE_RIGHT_CONTROL; + case XK_Meta_L: + case XK_Alt_L: return SAPP_KEYCODE_LEFT_ALT; + case XK_Mode_switch: /* Mapped to Alt_R on many keyboards */ + case XK_ISO_Level3_Shift: /* AltGr on at least some machines */ + case XK_Meta_R: + case XK_Alt_R: return SAPP_KEYCODE_RIGHT_ALT; + case XK_Super_L: return SAPP_KEYCODE_LEFT_SUPER; + case XK_Super_R: return SAPP_KEYCODE_RIGHT_SUPER; + case XK_Menu: return SAPP_KEYCODE_MENU; + case XK_Num_Lock: return SAPP_KEYCODE_NUM_LOCK; + case XK_Caps_Lock: return SAPP_KEYCODE_CAPS_LOCK; + case XK_Print: return SAPP_KEYCODE_PRINT_SCREEN; + case XK_Scroll_Lock: return SAPP_KEYCODE_SCROLL_LOCK; + case XK_Pause: return SAPP_KEYCODE_PAUSE; + case XK_Delete: return SAPP_KEYCODE_DELETE; + case XK_BackSpace: return SAPP_KEYCODE_BACKSPACE; + case XK_Return: return SAPP_KEYCODE_ENTER; + case XK_Home: return SAPP_KEYCODE_HOME; + case XK_End: return SAPP_KEYCODE_END; + case XK_Page_Up: return SAPP_KEYCODE_PAGE_UP; + case XK_Page_Down: return SAPP_KEYCODE_PAGE_DOWN; + case XK_Insert: return SAPP_KEYCODE_INSERT; + case XK_Left: return SAPP_KEYCODE_LEFT; + case XK_Right: return SAPP_KEYCODE_RIGHT; + case XK_Down: return SAPP_KEYCODE_DOWN; + case XK_Up: return SAPP_KEYCODE_UP; + case XK_F1: return SAPP_KEYCODE_F1; + case XK_F2: return SAPP_KEYCODE_F2; + case XK_F3: return SAPP_KEYCODE_F3; + case XK_F4: return SAPP_KEYCODE_F4; + case XK_F5: return SAPP_KEYCODE_F5; + case XK_F6: return SAPP_KEYCODE_F6; + case XK_F7: return SAPP_KEYCODE_F7; + case XK_F8: return SAPP_KEYCODE_F8; + case XK_F9: return SAPP_KEYCODE_F9; + case XK_F10: return SAPP_KEYCODE_F10; + case XK_F11: return SAPP_KEYCODE_F11; + case XK_F12: return SAPP_KEYCODE_F12; + case XK_F13: return SAPP_KEYCODE_F13; + case XK_F14: return SAPP_KEYCODE_F14; + case XK_F15: return SAPP_KEYCODE_F15; + case XK_F16: return SAPP_KEYCODE_F16; + case XK_F17: return SAPP_KEYCODE_F17; + case XK_F18: return SAPP_KEYCODE_F18; + case XK_F19: return SAPP_KEYCODE_F19; + case XK_F20: return SAPP_KEYCODE_F20; + case XK_F21: return SAPP_KEYCODE_F21; + case XK_F22: return SAPP_KEYCODE_F22; + case XK_F23: return SAPP_KEYCODE_F23; + case XK_F24: return SAPP_KEYCODE_F24; + case XK_F25: return SAPP_KEYCODE_F25; + + case XK_KP_Divide: return SAPP_KEYCODE_KP_DIVIDE; + case XK_KP_Multiply: return SAPP_KEYCODE_KP_MULTIPLY; + case XK_KP_Subtract: return SAPP_KEYCODE_KP_SUBTRACT; + case XK_KP_Add: return SAPP_KEYCODE_KP_ADD; + + case XK_KP_Insert: return SAPP_KEYCODE_KP_0; + case XK_KP_End: return SAPP_KEYCODE_KP_1; + case XK_KP_Down: return SAPP_KEYCODE_KP_2; + case XK_KP_Page_Down: return SAPP_KEYCODE_KP_3; + case XK_KP_Left: return SAPP_KEYCODE_KP_4; + case XK_KP_Begin: return SAPP_KEYCODE_KP_5; + case XK_KP_Right: return SAPP_KEYCODE_KP_6; + case XK_KP_Home: return SAPP_KEYCODE_KP_7; + case XK_KP_Up: return SAPP_KEYCODE_KP_8; + case XK_KP_Page_Up: return SAPP_KEYCODE_KP_9; + case XK_KP_Delete: return SAPP_KEYCODE_KP_DECIMAL; + case XK_KP_Equal: return SAPP_KEYCODE_KP_EQUAL; + case XK_KP_Enter: return SAPP_KEYCODE_KP_ENTER; + + case XK_a: return SAPP_KEYCODE_A; + case XK_b: return SAPP_KEYCODE_B; + case XK_c: return SAPP_KEYCODE_C; + case XK_d: return SAPP_KEYCODE_D; + case XK_e: return SAPP_KEYCODE_E; + case XK_f: return SAPP_KEYCODE_F; + case XK_g: return SAPP_KEYCODE_G; + case XK_h: return SAPP_KEYCODE_H; + case XK_i: return SAPP_KEYCODE_I; + case XK_j: return SAPP_KEYCODE_J; + case XK_k: return SAPP_KEYCODE_K; + case XK_l: return SAPP_KEYCODE_L; + case XK_m: return SAPP_KEYCODE_M; + case XK_n: return SAPP_KEYCODE_N; + case XK_o: return SAPP_KEYCODE_O; + case XK_p: return SAPP_KEYCODE_P; + case XK_q: return SAPP_KEYCODE_Q; + case XK_r: return SAPP_KEYCODE_R; + case XK_s: return SAPP_KEYCODE_S; + case XK_t: return SAPP_KEYCODE_T; + case XK_u: return SAPP_KEYCODE_U; + case XK_v: return SAPP_KEYCODE_V; + case XK_w: return SAPP_KEYCODE_W; + case XK_x: return SAPP_KEYCODE_X; + case XK_y: return SAPP_KEYCODE_Y; + case XK_z: return SAPP_KEYCODE_Z; + case XK_1: return SAPP_KEYCODE_1; + case XK_2: return SAPP_KEYCODE_2; + case XK_3: return SAPP_KEYCODE_3; + case XK_4: return SAPP_KEYCODE_4; + case XK_5: return SAPP_KEYCODE_5; + case XK_6: return SAPP_KEYCODE_6; + case XK_7: return SAPP_KEYCODE_7; + case XK_8: return SAPP_KEYCODE_8; + case XK_9: return SAPP_KEYCODE_9; + case XK_0: return SAPP_KEYCODE_0; + case XK_space: return SAPP_KEYCODE_SPACE; + case XK_minus: return SAPP_KEYCODE_MINUS; + case XK_equal: return SAPP_KEYCODE_EQUAL; + case XK_bracketleft: return SAPP_KEYCODE_LEFT_BRACKET; + case XK_bracketright: return SAPP_KEYCODE_RIGHT_BRACKET; + case XK_backslash: return SAPP_KEYCODE_BACKSLASH; + case XK_semicolon: return SAPP_KEYCODE_SEMICOLON; + case XK_apostrophe: return SAPP_KEYCODE_APOSTROPHE; + case XK_grave: return SAPP_KEYCODE_GRAVE_ACCENT; + case XK_comma: return SAPP_KEYCODE_COMMA; + case XK_period: return SAPP_KEYCODE_PERIOD; + case XK_slash: return SAPP_KEYCODE_SLASH; + case XK_less: return SAPP_KEYCODE_WORLD_1; /* At least in some layouts... */ + default: return SAPP_KEYCODE_INVALID; + } +} + +_SOKOL_PRIVATE int32_t _sapp_x11_keysym_to_unicode(KeySym keysym) { + int min = 0; + int max = sizeof(_sapp_x11_keysymtab) / sizeof(struct _sapp_x11_codepair) - 1; + int mid; + + /* First check for Latin-1 characters (1:1 mapping) */ + if ((keysym >= 0x0020 && keysym <= 0x007e) || + (keysym >= 0x00a0 && keysym <= 0x00ff)) + { + return keysym; + } + + /* Also check for directly encoded 24-bit UCS characters */ + if ((keysym & 0xff000000) == 0x01000000) { + return keysym & 0x00ffffff; + } + + /* Binary search in table */ + while (max >= min) { + mid = (min + max) / 2; + if (_sapp_x11_keysymtab[mid].keysym < keysym) { + min = mid + 1; + } + else if (_sapp_x11_keysymtab[mid].keysym > keysym) { + max = mid - 1; + } + else { + return _sapp_x11_keysymtab[mid].ucs; + } + } + + /* No matching Unicode value found */ + return -1; +} + +_SOKOL_PRIVATE bool _sapp_x11_parse_dropped_files_list(const char* src) { + SOKOL_ASSERT(src); + SOKOL_ASSERT(_sapp.drop.buffer); + + _sapp_clear_drop_buffer(); + _sapp.drop.num_files = 0; + + /* + src is (potentially percent-encoded) string made of one or multiple paths + separated by \r\n, each path starting with 'file://' + */ + bool err = false; + int src_count = 0; + char src_chr = 0; + char* dst_ptr = _sapp.drop.buffer; + const char* dst_end_ptr = dst_ptr + (_sapp.drop.max_path_length - 1); // room for terminating 0 + while (0 != (src_chr = *src++)) { + src_count++; + char dst_chr = 0; + /* check leading 'file://' */ + if (src_count <= 7) { + if (((src_count == 1) && (src_chr != 'f')) || + ((src_count == 2) && (src_chr != 'i')) || + ((src_count == 3) && (src_chr != 'l')) || + ((src_count == 4) && (src_chr != 'e')) || + ((src_count == 5) && (src_chr != ':')) || + ((src_count == 6) && (src_chr != '/')) || + ((src_count == 7) && (src_chr != '/'))) + { + SOKOL_LOG("sokol_app.h: dropped file URI doesn't start with file://"); + err = true; + break; + } + } + else if (src_chr == '\r') { + // skip + } + else if (src_chr == '\n') { + src_chr = 0; + src_count = 0; + _sapp.drop.num_files++; + // too many files is not an error + if (_sapp.drop.num_files >= _sapp.drop.max_files) { + break; + } + dst_ptr = _sapp.drop.buffer + _sapp.drop.num_files * _sapp.drop.max_path_length; + dst_end_ptr = dst_ptr + (_sapp.drop.max_path_length - 1); + } + else if ((src_chr == '%') && src[0] && src[1]) { + // a percent-encoded byte (most like UTF-8 multibyte sequence) + const char digits[3] = { src[0], src[1], 0 }; + src += 2; + dst_chr = (char) strtol(digits, 0, 16); + } + else { + dst_chr = src_chr; + } + if (dst_chr) { + // dst_end_ptr already has adjustment for terminating zero + if (dst_ptr < dst_end_ptr) { + *dst_ptr++ = dst_chr; + } + else { + SOKOL_LOG("sokol_app.h: dropped file path too long (sapp_desc.max_dropped_file_path_length)"); + err = true; + break; + } + } + } + if (err) { + _sapp_clear_drop_buffer(); + _sapp.drop.num_files = 0; + return false; + } + else { + return true; + } +} + +// XLib manual says keycodes are in the range [8, 255] inclusive. +// https://tronche.com/gui/x/xlib/input/keyboard-encoding.html +static bool _sapp_x11_keycodes[256]; + +_SOKOL_PRIVATE void _sapp_x11_process_event(XEvent* event) { + Bool filtered = XFilterEvent(event, None); + switch (event->type) { + case GenericEvent: + if (_sapp.mouse.locked && _sapp.x11.xi.available) { + if (event->xcookie.extension == _sapp.x11.xi.major_opcode) { + if (XGetEventData(_sapp.x11.display, &event->xcookie)) { + if (event->xcookie.evtype == XI_RawMotion) { + XIRawEvent* re = (XIRawEvent*) event->xcookie.data; + if (re->valuators.mask_len) { + const double* values = re->raw_values; + if (XIMaskIsSet(re->valuators.mask, 0)) { + _sapp.mouse.dx = (float) *values; + values++; + } + if (XIMaskIsSet(re->valuators.mask, 1)) { + _sapp.mouse.dy = (float) *values; + } + _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mod(event->xmotion.state)); + } + } + XFreeEventData(_sapp.x11.display, &event->xcookie); + } + } + } + break; + case FocusOut: + /* if focus is lost for any reason, and we're in mouse locked mode, disable mouse lock */ + if (_sapp.mouse.locked) { + _sapp_x11_lock_mouse(false); + } + break; + case KeyPress: + { + int keycode = (int)event->xkey.keycode; + const sapp_keycode key = _sapp_x11_translate_key(keycode); + bool repeat = _sapp_x11_keycodes[keycode & 0xFF]; + _sapp_x11_keycodes[keycode & 0xFF] = true; + const uint32_t mods = _sapp_x11_mod(event->xkey.state); + if (key != SAPP_KEYCODE_INVALID) { + _sapp_x11_key_event(SAPP_EVENTTYPE_KEY_DOWN, key, repeat, mods); + } + KeySym keysym; + XLookupString(&event->xkey, NULL, 0, &keysym, NULL); + int32_t chr = _sapp_x11_keysym_to_unicode(keysym); + if (chr > 0) { + _sapp_x11_char_event((uint32_t)chr, repeat, mods); + } + } + break; + case KeyRelease: + { + int keycode = (int)event->xkey.keycode; + const sapp_keycode key = _sapp_x11_translate_key(keycode); + _sapp_x11_keycodes[keycode & 0xFF] = false; + if (key != SAPP_KEYCODE_INVALID) { + const uint32_t mods = _sapp_x11_mod(event->xkey.state); + _sapp_x11_key_event(SAPP_EVENTTYPE_KEY_UP, key, false, mods); + } + } + break; + case ButtonPress: + { + const sapp_mousebutton btn = _sapp_x11_translate_button(event); + const uint32_t mods = _sapp_x11_mod(event->xbutton.state); + if (btn != SAPP_MOUSEBUTTON_INVALID) { + _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, btn, mods); + _sapp.x11.mouse_buttons |= (1 << btn); + } + else { + /* might be a scroll event */ + switch (event->xbutton.button) { + case 4: _sapp_x11_scroll_event(0.0f, 1.0f, mods); break; + case 5: _sapp_x11_scroll_event(0.0f, -1.0f, mods); break; + case 6: _sapp_x11_scroll_event(1.0f, 0.0f, mods); break; + case 7: _sapp_x11_scroll_event(-1.0f, 0.0f, mods); break; + } + } + } + break; + case ButtonRelease: + { + const sapp_mousebutton btn = _sapp_x11_translate_button(event); + if (btn != SAPP_MOUSEBUTTON_INVALID) { + _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, btn, _sapp_x11_mod(event->xbutton.state)); + _sapp.x11.mouse_buttons &= ~(1 << btn); + } + } + break; + case EnterNotify: + /* don't send enter/leave events while mouse button held down */ + if (0 == _sapp.x11.mouse_buttons) { + _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_ENTER, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mod(event->xcrossing.state)); + } + break; + case LeaveNotify: + if (0 == _sapp.x11.mouse_buttons) { + _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mod(event->xcrossing.state)); + } + break; + case MotionNotify: + if (!_sapp.mouse.locked) { + const float new_x = (float) event->xmotion.x; + const float new_y = (float) event->xmotion.y; + if (_sapp.mouse.pos_valid) { + _sapp.mouse.dx = new_x - _sapp.mouse.x; + _sapp.mouse.dy = new_y - _sapp.mouse.y; + } + _sapp.mouse.x = new_x; + _sapp.mouse.y = new_y; + _sapp.mouse.pos_valid = true; + _sapp_x11_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_x11_mod(event->xmotion.state)); + } + break; + case ConfigureNotify: + if ((event->xconfigure.width != _sapp.window_width) || (event->xconfigure.height != _sapp.window_height)) { + _sapp.window_width = event->xconfigure.width; + _sapp.window_height = event->xconfigure.height; + _sapp.framebuffer_width = _sapp.window_width; + _sapp.framebuffer_height = _sapp.window_height; + _sapp_x11_app_event(SAPP_EVENTTYPE_RESIZED); + } + break; + case PropertyNotify: + if (event->xproperty.state == PropertyNewValue) { + if (event->xproperty.atom == _sapp.x11.WM_STATE) { + const int state = _sapp_x11_get_window_state(); + if (state != _sapp.x11.window_state) { + _sapp.x11.window_state = state; + if (state == IconicState) { + _sapp_x11_app_event(SAPP_EVENTTYPE_ICONIFIED); + } + else if (state == NormalState) { + _sapp_x11_app_event(SAPP_EVENTTYPE_RESTORED); + } + } + } + } + break; + case ClientMessage: + if (filtered) { + return; + } + if (event->xclient.message_type == _sapp.x11.WM_PROTOCOLS) { + const Atom protocol = (Atom)event->xclient.data.l[0]; + if (protocol == _sapp.x11.WM_DELETE_WINDOW) { + _sapp.quit_requested = true; + } + } + else if (event->xclient.message_type == _sapp.x11.xdnd.XdndEnter) { + const bool is_list = 0 != (event->xclient.data.l[1] & 1); + _sapp.x11.xdnd.source = (Window)event->xclient.data.l[0]; + _sapp.x11.xdnd.version = event->xclient.data.l[1] >> 24; + _sapp.x11.xdnd.format = None; + if (_sapp.x11.xdnd.version > _SAPP_X11_XDND_VERSION) { + return; + } + uint32_t count = 0; + Atom* formats = 0; + if (is_list) { + count = _sapp_x11_get_window_property(_sapp.x11.xdnd.source, _sapp.x11.xdnd.XdndTypeList, XA_ATOM, (unsigned char**)&formats); + } + else { + count = 3; + formats = (Atom*) event->xclient.data.l + 2; + } + for (uint32_t i = 0; i < count; i++) { + if (formats[i] == _sapp.x11.xdnd.text_uri_list) { + _sapp.x11.xdnd.format = _sapp.x11.xdnd.text_uri_list; + break; + } + } + if (is_list && formats) { + XFree(formats); + } + } + else if (event->xclient.message_type == _sapp.x11.xdnd.XdndDrop) { + if (_sapp.x11.xdnd.version > _SAPP_X11_XDND_VERSION) { + return; + } + Time time = CurrentTime; + if (_sapp.x11.xdnd.format) { + if (_sapp.x11.xdnd.version >= 1) { + time = (Time)event->xclient.data.l[2]; + } + XConvertSelection(_sapp.x11.display, + _sapp.x11.xdnd.XdndSelection, + _sapp.x11.xdnd.format, + _sapp.x11.xdnd.XdndSelection, + _sapp.x11.window, + time); + } + else if (_sapp.x11.xdnd.version >= 2) { + XEvent reply; + memset(&reply, 0, sizeof(reply)); + reply.type = ClientMessage; + reply.xclient.window = _sapp.x11.window; + reply.xclient.message_type = _sapp.x11.xdnd.XdndFinished; + reply.xclient.format = 32; + reply.xclient.data.l[0] = (long)_sapp.x11.window; + reply.xclient.data.l[1] = 0; // drag was rejected + reply.xclient.data.l[2] = None; + XSendEvent(_sapp.x11.display, _sapp.x11.xdnd.source, False, NoEventMask, &reply); + XFlush(_sapp.x11.display); + } + } + else if (event->xclient.message_type == _sapp.x11.xdnd.XdndPosition) { + /* drag operation has moved over the window + FIXME: we could track the mouse position here, but + this isn't implemented on other platforms either so far + */ + if (_sapp.x11.xdnd.version > _SAPP_X11_XDND_VERSION) { + return; + } + XEvent reply; + memset(&reply, 0, sizeof(reply)); + reply.type = ClientMessage; + reply.xclient.window = _sapp.x11.xdnd.source; + reply.xclient.message_type = _sapp.x11.xdnd.XdndStatus; + reply.xclient.format = 32; + reply.xclient.data.l[0] = (long)_sapp.x11.window; + if (_sapp.x11.xdnd.format) { + /* reply that we are ready to copy the dragged data */ + reply.xclient.data.l[1] = 1; // accept with no rectangle + if (_sapp.x11.xdnd.version >= 2) { + reply.xclient.data.l[4] = (long)_sapp.x11.xdnd.XdndActionCopy; + } + } + XSendEvent(_sapp.x11.display, _sapp.x11.xdnd.source, False, NoEventMask, &reply); + XFlush(_sapp.x11.display); + } + break; + case SelectionNotify: + if (event->xselection.property == _sapp.x11.xdnd.XdndSelection) { + char* data = 0; + uint32_t result = _sapp_x11_get_window_property(event->xselection.requestor, + event->xselection.property, + event->xselection.target, + (unsigned char**) &data); + if (_sapp.drop.enabled && result) { + if (_sapp_x11_parse_dropped_files_list(data)) { + if (_sapp_events_enabled()) { + _sapp_init_event(SAPP_EVENTTYPE_FILES_DROPPED); + _sapp_call_event(&_sapp.event); + } + } + } + if (_sapp.x11.xdnd.version >= 2) { + XEvent reply; + memset(&reply, 0, sizeof(reply)); + reply.type = ClientMessage; + reply.xclient.window = _sapp.x11.window; + reply.xclient.message_type = _sapp.x11.xdnd.XdndFinished; + reply.xclient.format = 32; + reply.xclient.data.l[0] = (long)_sapp.x11.window; + reply.xclient.data.l[1] = result; + reply.xclient.data.l[2] = (long)_sapp.x11.xdnd.XdndActionCopy; + XSendEvent(_sapp.x11.display, _sapp.x11.xdnd.source, False, NoEventMask, &reply); + XFlush(_sapp.x11.display); + } + } + break; + case DestroyNotify: + break; + } +} + +_SOKOL_PRIVATE void _sapp_linux_run(const sapp_desc* desc) { + /* The following lines are here to trigger a linker error instead of an + obscure runtime error if the user has forgotten to add -pthread to + the compiler or linker options. They have no other purpose. + */ + pthread_attr_t pthread_attr; + pthread_attr_init(&pthread_attr); + pthread_attr_destroy(&pthread_attr); + + _sapp_init_state(desc); + _sapp.x11.window_state = NormalState; + + XInitThreads(); + XrmInitialize(); + _sapp.x11.display = XOpenDisplay(NULL); + if (!_sapp.x11.display) { + _sapp_fail("XOpenDisplay() failed!\n"); + } + _sapp.x11.screen = DefaultScreen(_sapp.x11.display); + _sapp.x11.root = DefaultRootWindow(_sapp.x11.display); + XkbSetDetectableAutoRepeat(_sapp.x11.display, true, NULL); + _sapp_x11_query_system_dpi(); + _sapp.dpi_scale = _sapp.x11.dpi / 96.0f; + _sapp_x11_init_extensions(); + _sapp_x11_create_hidden_cursor(); + _sapp_glx_init(); + Visual* visual = 0; + int depth = 0; + _sapp_glx_choose_visual(&visual, &depth); + _sapp_x11_create_window(visual, depth); + _sapp_glx_create_context(); + _sapp.valid = true; + _sapp_x11_show_window(); + if (_sapp.fullscreen) { + _sapp_x11_set_fullscreen(true); + } + _sapp_x11_query_window_size(); + _sapp_glx_swapinterval(_sapp.swap_interval); + XFlush(_sapp.x11.display); + while (!_sapp.quit_ordered) { + _sapp_glx_make_current(); + int count = XPending(_sapp.x11.display); + while (count--) { + XEvent event; + XNextEvent(_sapp.x11.display, &event); + _sapp_x11_process_event(&event); + } + _sapp_frame(); + _sapp_glx_swap_buffers(); + XFlush(_sapp.x11.display); + /* handle quit-requested, either from window or from sapp_request_quit() */ + if (_sapp.quit_requested && !_sapp.quit_ordered) { + /* give user code a chance to intervene */ + _sapp_x11_app_event(SAPP_EVENTTYPE_QUIT_REQUESTED); + /* if user code hasn't intervened, quit the app */ + if (_sapp.quit_requested) { + _sapp.quit_ordered = true; + } + } + } + _sapp_call_cleanup(); + _sapp_glx_destroy_context(); + _sapp_x11_destroy_window(); + XCloseDisplay(_sapp.x11.display); + _sapp_discard_state(); +} + +#if !defined(SOKOL_NO_ENTRY) +int main(int argc, char* argv[]) { + sapp_desc desc = sokol_main(argc, argv); + _sapp_linux_run(&desc); + return 0; +} +#endif /* SOKOL_NO_ENTRY */ +#endif /* _SAPP_LINUX */ + +/*== PUBLIC API FUNCTIONS ====================================================*/ +#if defined(SOKOL_NO_ENTRY) +SOKOL_API_IMPL void sapp_run(const sapp_desc* desc) { + SOKOL_ASSERT(desc); + #if defined(_SAPP_MACOS) + _sapp_macos_run(desc); + #elif defined(_SAPP_IOS) + _sapp_ios_run(desc); + #elif defined(_SAPP_EMSCRIPTEN) + _sapp_emsc_run(desc); + #elif defined(_SAPP_WIN32) + _sapp_win32_run(desc); + #elif defined(_SAPP_UWP) + _sapp_uwp_run(desc); + #elif defined(_SAPP_LINUX) + _sapp_linux_run(desc); + #else + // calling sapp_run() directly is not supported on Android) + _sapp_fail("sapp_run() not supported on this platform!"); + #endif +} + +/* this is just a stub so the linker doesn't complain */ +sapp_desc sokol_main(int argc, char* argv[]) { + _SOKOL_UNUSED(argc); + _SOKOL_UNUSED(argv); + sapp_desc desc; + memset(&desc, 0, sizeof(desc)); + return desc; +} +#else +/* likewise, in normal mode, sapp_run() is just an empty stub */ +SOKOL_API_IMPL void sapp_run(const sapp_desc* desc) { + _SOKOL_UNUSED(desc); +} +#endif + +SOKOL_API_IMPL bool sapp_isvalid(void) { + return _sapp.valid; +} + +SOKOL_API_IMPL void* sapp_userdata(void) { + return _sapp.desc.user_data; +} + +SOKOL_API_IMPL sapp_desc sapp_query_desc(void) { + return _sapp.desc; +} + +SOKOL_API_IMPL uint64_t sapp_frame_count(void) { + return _sapp.frame_count; +} + +SOKOL_API_IMPL int sapp_width(void) { + return (_sapp.framebuffer_width > 0) ? _sapp.framebuffer_width : 1; +} + +SOKOL_API_IMPL float sapp_widthf(void) { + return (float)sapp_width(); +} + +SOKOL_API_IMPL int sapp_height(void) { + return (_sapp.framebuffer_height > 0) ? _sapp.framebuffer_height : 1; +} + +SOKOL_API_IMPL float sapp_heightf(void) { + return (float)sapp_height(); +} + +SOKOL_API_IMPL int sapp_color_format(void) { + #if defined(_SAPP_EMSCRIPTEN) && defined(SOKOL_WGPU) + switch (_sapp.emsc.wgpu.render_format) { + case WGPUTextureFormat_RGBA8Unorm: + return _SAPP_PIXELFORMAT_RGBA8; + case WGPUTextureFormat_BGRA8Unorm: + return _SAPP_PIXELFORMAT_BGRA8; + default: + SOKOL_UNREACHABLE; + return 0; + } + #elif defined(SOKOL_METAL) || defined(SOKOL_D3D11) + return _SAPP_PIXELFORMAT_BGRA8; + #else + return _SAPP_PIXELFORMAT_RGBA8; + #endif +} + +SOKOL_API_IMPL int sapp_depth_format(void) { + return _SAPP_PIXELFORMAT_DEPTH_STENCIL; +} + +SOKOL_API_IMPL int sapp_sample_count(void) { + return _sapp.sample_count; +} + +SOKOL_API_IMPL bool sapp_high_dpi(void) { + return _sapp.desc.high_dpi && (_sapp.dpi_scale >= 1.5f); +} + +SOKOL_API_IMPL float sapp_dpi_scale(void) { + return _sapp.dpi_scale; +} + +SOKOL_API_IMPL bool sapp_gles2(void) { + return _sapp.gles2_fallback; +} + +SOKOL_API_IMPL void sapp_show_keyboard(bool show) { + #if defined(_SAPP_IOS) + _sapp_ios_show_keyboard(show); + #elif defined(_SAPP_EMSCRIPTEN) + _sapp_emsc_show_keyboard(show); + #elif defined(_SAPP_ANDROID) + _sapp_android_show_keyboard(show); + #else + _SOKOL_UNUSED(show); + #endif +} + +SOKOL_API_IMPL bool sapp_keyboard_shown(void) { + return _sapp.onscreen_keyboard_shown; +} + +SOKOL_APP_API_DECL bool sapp_is_fullscreen(void) { + return _sapp.fullscreen; +} + +SOKOL_APP_API_DECL void sapp_toggle_fullscreen(void) { + #if defined(_SAPP_MACOS) + _sapp_macos_toggle_fullscreen(); + #elif defined(_SAPP_WIN32) + _sapp_win32_toggle_fullscreen(); + #elif defined(_SAPP_UWP) + _sapp_uwp_toggle_fullscreen(); + #elif defined(_SAPP_LINUX) + _sapp_x11_toggle_fullscreen(); + #endif +} + +/* NOTE that sapp_show_mouse() does not "stack" like the Win32 or macOS API functions! */ +SOKOL_API_IMPL void sapp_show_mouse(bool show) { + if (_sapp.mouse.shown != show) { + #if defined(_SAPP_MACOS) + _sapp_macos_show_mouse(show); + #elif defined(_SAPP_WIN32) + _sapp_win32_show_mouse(show); + #elif defined(_SAPP_LINUX) + _sapp_x11_show_mouse(show); + #elif defined(_SAPP_UWP) + _sapp_uwp_show_mouse(show); + #endif + _sapp.mouse.shown = show; + } +} + +SOKOL_API_IMPL bool sapp_mouse_shown(void) { + return _sapp.mouse.shown; +} + +SOKOL_API_IMPL void sapp_lock_mouse(bool lock) { + #if defined(_SAPP_MACOS) + _sapp_macos_lock_mouse(lock); + #elif defined(_SAPP_EMSCRIPTEN) + _sapp_emsc_lock_mouse(lock); + #elif defined(_SAPP_WIN32) + _sapp_win32_lock_mouse(lock); + #elif defined(_SAPP_LINUX) + _sapp_x11_lock_mouse(lock); + #else + _sapp.mouse.locked = lock; + #endif +} + +SOKOL_API_IMPL bool sapp_mouse_locked(void) { + return _sapp.mouse.locked; +} + +SOKOL_API_IMPL void sapp_request_quit(void) { + _sapp.quit_requested = true; +} + +SOKOL_API_IMPL void sapp_cancel_quit(void) { + _sapp.quit_requested = false; +} + +SOKOL_API_IMPL void sapp_quit(void) { + _sapp.quit_ordered = true; +} + +SOKOL_API_IMPL void sapp_consume_event(void) { + _sapp.event_consumed = true; +} + +/* NOTE: on HTML5, sapp_set_clipboard_string() must be called from within event handler! */ +SOKOL_API_IMPL void sapp_set_clipboard_string(const char* str) { + SOKOL_ASSERT(_sapp.clipboard.enabled); + if (!_sapp.clipboard.enabled) { + return; + } + SOKOL_ASSERT(str); + #if defined(_SAPP_MACOS) + _sapp_macos_set_clipboard_string(str); + #elif defined(_SAPP_EMSCRIPTEN) + _sapp_emsc_set_clipboard_string(str); + #elif defined(_SAPP_WIN32) + _sapp_win32_set_clipboard_string(str); + #else + /* not implemented */ + #endif + _sapp_strcpy(str, _sapp.clipboard.buffer, _sapp.clipboard.buf_size); +} + +SOKOL_API_IMPL const char* sapp_get_clipboard_string(void) { + SOKOL_ASSERT(_sapp.clipboard.enabled); + if (!_sapp.clipboard.enabled) { + return ""; + } + #if defined(_SAPP_MACOS) + return _sapp_macos_get_clipboard_string(); + #elif defined(_SAPP_EMSCRIPTEN) + return _sapp.clipboard.buffer; + #elif defined(_SAPP_WIN32) + return _sapp_win32_get_clipboard_string(); + #else + /* not implemented */ + return _sapp.clipboard.buffer; + #endif +} + +SOKOL_API_IMPL void sapp_set_window_title(const char* title) { + SOKOL_ASSERT(title); + _sapp_strcpy(title, _sapp.window_title, sizeof(_sapp.window_title)); + #if defined(_SAPP_MACOS) + _sapp_macos_update_window_title(); + #elif defined(_SAPP_WIN32) + _sapp_win32_update_window_title(); + #elif defined(_SAPP_LINUX) + _sapp_x11_update_window_title(); + #endif +} + +SOKOL_API_IMPL int sapp_get_num_dropped_files(void) { + SOKOL_ASSERT(_sapp.drop.enabled); + return _sapp.drop.num_files; +} + +SOKOL_API_IMPL const char* sapp_get_dropped_file_path(int index) { + SOKOL_ASSERT(_sapp.drop.enabled); + SOKOL_ASSERT((index >= 0) && (index < _sapp.drop.num_files)); + SOKOL_ASSERT(_sapp.drop.buffer); + if (!_sapp.drop.enabled) { + return ""; + } + if ((index < 0) || (index >= _sapp.drop.max_files)) { + return ""; + } + return (const char*) _sapp_dropped_file_path_ptr(index); +} + +SOKOL_API_IMPL uint32_t sapp_html5_get_dropped_file_size(int index) { + SOKOL_ASSERT(_sapp.drop.enabled); + SOKOL_ASSERT((index >= 0) && (index < _sapp.drop.num_files)); + #if defined(_SAPP_EMSCRIPTEN) + if (!_sapp.drop.enabled) { + return 0; + } + return sapp_js_dropped_file_size(index); + #else + (void)index; + return 0; + #endif +} + +SOKOL_API_IMPL void sapp_html5_fetch_dropped_file(const sapp_html5_fetch_request* request) { + SOKOL_ASSERT(_sapp.drop.enabled); + SOKOL_ASSERT(request); + SOKOL_ASSERT(request->callback); + SOKOL_ASSERT(request->buffer_ptr); + SOKOL_ASSERT(request->buffer_size > 0); + #if defined(_SAPP_EMSCRIPTEN) + const int index = request->dropped_file_index; + sapp_html5_fetch_error error_code = SAPP_HTML5_FETCH_ERROR_NO_ERROR; + if ((index < 0) || (index >= _sapp.drop.num_files)) { + error_code = SAPP_HTML5_FETCH_ERROR_OTHER; + } + if (sapp_html5_get_dropped_file_size(index) > request->buffer_size) { + error_code = SAPP_HTML5_FETCH_ERROR_BUFFER_TOO_SMALL; + } + if (SAPP_HTML5_FETCH_ERROR_NO_ERROR != error_code) { + _sapp_emsc_invoke_fetch_cb(index, + false, // success + (int)error_code, + request->callback, + 0, // fetched_size + request->buffer_ptr, + request->buffer_size, + request->user_data); + } + else { + sapp_js_fetch_dropped_file(index, + request->callback, + request->buffer_ptr, + request->buffer_size, + request->user_data); + } + #else + (void)request; + #endif +} + +SOKOL_API_IMPL const void* sapp_metal_get_device(void) { + SOKOL_ASSERT(_sapp.valid); + #if defined(SOKOL_METAL) + #if defined(_SAPP_MACOS) + const void* obj = (__bridge const void*) _sapp.macos.mtl_device; + #else + const void* obj = (__bridge const void*) _sapp.ios.mtl_device; + #endif + SOKOL_ASSERT(obj); + return obj; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sapp_metal_get_renderpass_descriptor(void) { + SOKOL_ASSERT(_sapp.valid); + #if defined(SOKOL_METAL) + #if defined(_SAPP_MACOS) + const void* obj = (__bridge const void*) [_sapp.macos.view currentRenderPassDescriptor]; + #else + const void* obj = (__bridge const void*) [_sapp.ios.view currentRenderPassDescriptor]; + #endif + SOKOL_ASSERT(obj); + return obj; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sapp_metal_get_drawable(void) { + SOKOL_ASSERT(_sapp.valid); + #if defined(SOKOL_METAL) + #if defined(_SAPP_MACOS) + const void* obj = (__bridge const void*) [_sapp.macos.view currentDrawable]; + #else + const void* obj = (__bridge const void*) [_sapp.ios.view currentDrawable]; + #endif + SOKOL_ASSERT(obj); + return obj; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sapp_macos_get_window(void) { + #if defined(_SAPP_MACOS) + const void* obj = (__bridge const void*) _sapp.macos.window; + SOKOL_ASSERT(obj); + return obj; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sapp_ios_get_window(void) { + #if defined(_SAPP_IOS) + const void* obj = (__bridge const void*) _sapp.ios.window; + SOKOL_ASSERT(obj); + return obj; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sapp_d3d11_get_device(void) { + SOKOL_ASSERT(_sapp.valid); + #if defined(SOKOL_D3D11) + return _sapp.d3d11.device; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sapp_d3d11_get_device_context(void) { + SOKOL_ASSERT(_sapp.valid); + #if defined(SOKOL_D3D11) + return _sapp.d3d11.device_context; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sapp_d3d11_get_render_target_view(void) { + SOKOL_ASSERT(_sapp.valid); + #if defined(SOKOL_D3D11) + if (_sapp.d3d11.msaa_rtv) { + return _sapp.d3d11.msaa_rtv; + } + else { + return _sapp.d3d11.rtv; + } + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sapp_d3d11_get_depth_stencil_view(void) { + SOKOL_ASSERT(_sapp.valid); + #if defined(SOKOL_D3D11) + return _sapp.d3d11.dsv; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sapp_win32_get_hwnd(void) { + SOKOL_ASSERT(_sapp.valid); + #if defined(_SAPP_WIN32) + return _sapp.win32.hwnd; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sapp_wgpu_get_device(void) { + SOKOL_ASSERT(_sapp.valid); + #if defined(_SAPP_EMSCRIPTEN) && defined(SOKOL_WGPU) + return (const void*) _sapp.emsc.wgpu.device; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sapp_wgpu_get_render_view(void) { + SOKOL_ASSERT(_sapp.valid); + #if defined(_SAPP_EMSCRIPTEN) && defined(SOKOL_WGPU) + if (_sapp.sample_count > 1) { + return (const void*) _sapp.emsc.wgpu.msaa_view; + } + else { + return (const void*) _sapp.emsc.wgpu.swapchain_view; + } + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sapp_wgpu_get_resolve_view(void) { + SOKOL_ASSERT(_sapp.valid); + #if defined(_SAPP_EMSCRIPTEN) && defined(SOKOL_WGPU) + if (_sapp.sample_count > 1) { + return (const void*) _sapp.emsc.wgpu.swapchain_view; + } + else { + return 0; + } + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sapp_wgpu_get_depth_stencil_view(void) { + SOKOL_ASSERT(_sapp.valid); + #if defined(_SAPP_EMSCRIPTEN) && defined(SOKOL_WGPU) + return (const void*) _sapp.emsc.wgpu.depth_stencil_view; + #else + return 0; + #endif +} + +SOKOL_API_IMPL const void* sapp_android_get_native_activity(void) { + SOKOL_ASSERT(_sapp.valid); + #if defined(_SAPP_ANDROID) + return (void*)_sapp.android.activity; + #else + return 0; + #endif +} + +SOKOL_API_IMPL void sapp_html5_ask_leave_site(bool ask) { + _sapp.html5_ask_leave_site = ask; +} + +#endif /* SOKOL_APP_IMPL */ diff --git a/v_windows/v/old/thirdparty/sokol/sokol_app2.h b/v_windows/v/old/thirdparty/sokol/sokol_app2.h new file mode 100644 index 0000000..aa6d654 --- /dev/null +++ b/v_windows/v/old/thirdparty/sokol/sokol_app2.h @@ -0,0 +1,40 @@ + +@implementation MyView2 + +int __v_sokol_inited = 0; + +// Alternative drawRect which calls a frame function with native Cocoa calls +- (void)drawRect:(NSRect)rect { + //puts("drawRect()"); + if (__v_sokol_inited == 0) { + _sapp_call_init(); + __v_sokol_inited = 1; + } + _sapp_call_frame_native(); +} + +//- (BOOL)isOpaque { +// return NO; +//} + +- (BOOL)canBecomeKeyView { + return YES; +} +- (BOOL)acceptsFirstResponder { + return YES; +} + +// - (void)mouseExited:(NSEvent*)event { +// } + +// - (void)mouseDown:(NSEvent*)event { +// } + +- (BOOL)acceptsFirstMouse:(NSEvent *)event { + return YES; +} + + +@end + + diff --git a/v_windows/v/old/thirdparty/sokol/sokol_audio.h b/v_windows/v/old/thirdparty/sokol/sokol_audio.h new file mode 100644 index 0000000..faea817 --- /dev/null +++ b/v_windows/v/old/thirdparty/sokol/sokol_audio.h @@ -0,0 +1,2147 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_AUDIO_IMPL) +#define SOKOL_AUDIO_IMPL +#endif +#ifndef SOKOL_AUDIO_INCLUDED +/* + sokol_audio.h -- cross-platform audio-streaming API + + Project URL: https://github.com/floooh/sokol + + Do this: + #define SOKOL_IMPL or + #define SOKOL_AUDIO_IMPL + before you include this file in *one* C or C++ file to create the + implementation. + + Optionally provide the following defines with your own implementations: + + SOKOL_DUMMY_BACKEND - use a dummy backend + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + SOKOL_LOG(msg) - your own logging function (default: puts(msg)) + SOKOL_MALLOC(s) - your own malloc() implementation (default: malloc(s)) + SOKOL_FREE(p) - your own free() implementation (default: free(p)) + SOKOL_AUDIO_API_DECL- public function declaration prefix (default: extern) + SOKOL_API_DECL - same as SOKOL_AUDIO_API_DECL + SOKOL_API_IMPL - public function implementation prefix (default: -) + + SAUDIO_RING_MAX_SLOTS - max number of slots in the push-audio ring buffer (default 1024) + SAUDIO_OSX_USE_SYSTEM_HEADERS - define this to force inclusion of system headers on + macOS instead of using embedded CoreAudio declarations + + If sokol_audio.h is compiled as a DLL, define the following before + including the declaration or implementation: + + SOKOL_DLL + + On Windows, SOKOL_DLL will define SOKOL_AUDIO_API_DECL as __declspec(dllexport) + or __declspec(dllimport) as needed. + + Link with the following libraries: + + - on macOS: AudioToolbox + - on iOS: AudioToolbox, AVFoundation + - on Linux: asound + - on Android: link with OpenSLES + - on Windows with MSVC or Clang toolchain: no action needed, libs are defined in-source via pragma-comment-lib + - on Windows with MINGW/MSYS2 gcc: compile with '-mwin32' and link with -lole32 + + FEATURE OVERVIEW + ================ + You provide a mono- or stereo-stream of 32-bit float samples, which + Sokol Audio feeds into platform-specific audio backends: + + - Windows: WASAPI + - Linux: ALSA + - macOS: CoreAudio + - iOS: CoreAudio+AVAudioSession + - emscripten: WebAudio with ScriptProcessorNode + - Android: OpenSLES + + Sokol Audio will not do any buffer mixing or volume control, if you have + multiple independent input streams of sample data you need to perform the + mixing yourself before forwarding the data to Sokol Audio. + + There are two mutually exclusive ways to provide the sample data: + + 1. Callback model: You provide a callback function, which will be called + when Sokol Audio needs new samples. On all platforms except emscripten, + this function is called from a separate thread. + 2. Push model: Your code pushes small blocks of sample data from your + main loop or a thread you created. The pushed data is stored in + a ring buffer where it is pulled by the backend code when + needed. + + The callback model is preferred because it is the most direct way to + feed sample data into the audio backends and also has less moving parts + (there is no ring buffer between your code and the audio backend). + + Sometimes it is not possible to generate the audio stream directly in a + callback function running in a separate thread, for such cases Sokol Audio + provides the push-model as a convenience. + + SOKOL AUDIO, SOLOUD AND MINIAUDIO + ================================= + The WASAPI, ALSA, OpenSLES and CoreAudio backend code has been taken from the + SoLoud library (with some modifications, so any bugs in there are most + likely my fault). If you need a more fully-featured audio solution, check + out SoLoud, it's excellent: + + https://github.com/jarikomppa/soloud + + Another alternative which feature-wise is somewhere inbetween SoLoud and + sokol-audio might be MiniAudio: + + https://github.com/mackron/miniaudio + + GLOSSARY + ======== + - stream buffer: + The internal audio data buffer, usually provided by the backend API. The + size of the stream buffer defines the base latency, smaller buffers have + lower latency but may cause audio glitches. Bigger buffers reduce or + eliminate glitches, but have a higher base latency. + + - stream callback: + Optional callback function which is called by Sokol Audio when it + needs new samples. On Windows, macOS/iOS and Linux, this is called in + a separate thread, on WebAudio, this is called per-frame in the + browser thread. + + - channel: + A discrete track of audio data, currently 1-channel (mono) and + 2-channel (stereo) is supported and tested. + + - sample: + The magnitude of an audio signal on one channel at a given time. In + Sokol Audio, samples are 32-bit float numbers in the range -1.0 to + +1.0. + + - frame: + The tightly packed set of samples for all channels at a given time. + For mono 1 frame is 1 sample. For stereo, 1 frame is 2 samples. + + - packet: + In Sokol Audio, a small chunk of audio data that is moved from the + main thread to the audio streaming thread in order to decouple the + rate at which the main thread provides new audio data, and the + streaming thread consuming audio data. + + WORKING WITH SOKOL AUDIO + ======================== + First call saudio_setup() with your preferred audio playback options. + In most cases you can stick with the default values, these provide + a good balance between low-latency and glitch-free playback + on all audio backends. + + If you want to use the callback-model, you need to provide a stream + callback function either in saudio_desc.stream_cb or saudio_desc.stream_userdata_cb, + otherwise keep both function pointers zero-initialized. + + Use push model and default playback parameters: + + saudio_setup(&(saudio_desc){0}); + + Use stream callback model and default playback parameters: + + saudio_setup(&(saudio_desc){ + .stream_cb = my_stream_callback + }); + + The standard stream callback doesn't have a user data argument, if you want + that, use the alternative stream_userdata_cb and also set the user_data pointer: + + saudio_setup(&(saudio_desc){ + .stream_userdata_cb = my_stream_callback, + .user_data = &my_data + }); + + The following playback parameters can be provided through the + saudio_desc struct: + + General parameters (both for stream-callback and push-model): + + int sample_rate -- the sample rate in Hz, default: 44100 + int num_channels -- number of channels, default: 1 (mono) + int buffer_frames -- number of frames in streaming buffer, default: 2048 + + The stream callback prototype (either with or without userdata): + + void (*stream_cb)(float* buffer, int num_frames, int num_channels) + void (*stream_userdata_cb)(float* buffer, int num_frames, int num_channels, void* user_data) + Function pointer to the user-provide stream callback. + + Push-model parameters: + + int packet_frames -- number of frames in a packet, default: 128 + int num_packets -- number of packets in ring buffer, default: 64 + + The sample_rate and num_channels parameters are only hints for the audio + backend, it isn't guaranteed that those are the values used for actual + playback. + + To get the actual parameters, call the following functions after + saudio_setup(): + + int saudio_sample_rate(void) + int saudio_channels(void); + + It's unlikely that the number of channels will be different than requested, + but a different sample rate isn't uncommon. + + (NOTE: there's an yet unsolved issue when an audio backend might switch + to a different sample rate when switching output devices, for instance + plugging in a bluetooth headset, this case is currently not handled in + Sokol Audio). + + You can check if audio initialization was successful with + saudio_isvalid(). If backend initialization failed for some reason + (for instance when there's no audio device in the machine), this + will return false. Not checking for success won't do any harm, all + Sokol Audio function will silently fail when called after initialization + has failed, so apart from missing audio output, nothing bad will happen. + + Before your application exits, you should call + + saudio_shutdown(); + + This stops the audio thread (on Linux, Windows and macOS/iOS) and + properly shuts down the audio backend. + + THE STREAM CALLBACK MODEL + ========================= + To use Sokol Audio in stream-callback-mode, provide a callback function + like this in the saudio_desc struct when calling saudio_setup(): + + void stream_cb(float* buffer, int num_frames, int num_channels) { + ... + } + + Or the alternative version with a user-data argument: + + void stream_userdata_cb(float* buffer, int num_frames, int num_channels, void* user_data) { + my_data_t* my_data = (my_data_t*) user_data; + ... + } + + The job of the callback function is to fill the *buffer* with 32-bit + float sample values. + + To output silence, fill the buffer with zeros: + + void stream_cb(float* buffer, int num_frames, int num_channels) { + const int num_samples = num_frames * num_channels; + for (int i = 0; i < num_samples; i++) { + buffer[i] = 0.0f; + } + } + + For stereo output (num_channels == 2), the samples for the left + and right channel are interleaved: + + void stream_cb(float* buffer, int num_frames, int num_channels) { + assert(2 == num_channels); + for (int i = 0; i < num_frames; i++) { + buffer[2*i + 0] = ...; // left channel + buffer[2*i + 1] = ...; // right channel + } + } + + Please keep in mind that the stream callback function is running in a + separate thread, if you need to share data with the main thread you need + to take care yourself to make the access to the shared data thread-safe! + + THE PUSH MODEL + ============== + To use the push-model for providing audio data, simply don't set (keep + zero-initialized) the stream_cb field in the saudio_desc struct when + calling saudio_setup(). + + To provide sample data with the push model, call the saudio_push() + function at regular intervals (for instance once per frame). You can + call the saudio_expect() function to ask Sokol Audio how much room is + in the ring buffer, but if you provide a continuous stream of data + at the right sample rate, saudio_expect() isn't required (it's a simple + way to sync/throttle your sample generation code with the playback + rate though). + + With saudio_push() you may need to maintain your own intermediate sample + buffer, since pushing individual sample values isn't very efficient. + The following example is from the MOD player sample in + sokol-samples (https://github.com/floooh/sokol-samples): + + const int num_frames = saudio_expect(); + if (num_frames > 0) { + const int num_samples = num_frames * saudio_channels(); + read_samples(flt_buf, num_samples); + saudio_push(flt_buf, num_frames); + } + + Another option is to ignore saudio_expect(), and just push samples as they + are generated in small batches. In this case you *need* to generate the + samples at the right sample rate: + + The following example is taken from the Tiny Emulators project + (https://github.com/floooh/chips-test), this is for mono playback, + so (num_samples == num_frames): + + // tick the sound generator + if (ay38910_tick(&sys->psg)) { + // new sample is ready + sys->sample_buffer[sys->sample_pos++] = sys->psg.sample; + if (sys->sample_pos == sys->num_samples) { + // new sample packet is ready + saudio_push(sys->sample_buffer, sys->num_samples); + sys->sample_pos = 0; + } + } + + THE WEBAUDIO BACKEND + ==================== + The WebAudio backend is currently using a ScriptProcessorNode callback to + feed the sample data into WebAudio. ScriptProcessorNode has been + deprecated for a while because it is running from the main thread, with + the default initialization parameters it works 'pretty well' though. + Ultimately Sokol Audio will use Audio Worklets, but this requires a few + more things to fall into place (Audio Worklets implemented everywhere, + SharedArrayBuffers enabled again, and I need to figure out a 'low-cost' + solution in terms of implementation effort, since Audio Worklets are + a lot more complex than ScriptProcessorNode if the audio data needs to come + from the main thread). + + The WebAudio backend is automatically selected when compiling for + emscripten (__EMSCRIPTEN__ define exists). + + https://developers.google.com/web/updates/2017/12/audio-worklet + https://developers.google.com/web/updates/2018/06/audio-worklet-design-pattern + + "Blob URLs": https://www.html5rocks.com/en/tutorials/workers/basics/ + + THE COREAUDIO BACKEND + ===================== + The CoreAudio backend is selected on macOS and iOS (__APPLE__ is defined). + Since the CoreAudio API is implemented in C (not Objective-C) on macOS the + implementation part of Sokol Audio can be included into a C source file. + + However on iOS, Sokol Audio must be compiled as Objective-C due to it's + reliance on the AVAudioSession object. The iOS code path support both + being compiled with or without ARC (Automatic Reference Counting). + + For thread synchronisation, the CoreAudio backend will use the + pthread_mutex_* functions. + + The incoming floating point samples will be directly forwarded to + CoreAudio without further conversion. + + macOS and iOS applications that use Sokol Audio need to link with + the AudioToolbox framework. + + THE WASAPI BACKEND + ================== + The WASAPI backend is automatically selected when compiling on Windows + (_WIN32 is defined). + + For thread synchronisation a Win32 critical section is used. + + WASAPI may use a different size for its own streaming buffer then requested, + so the base latency may be slightly bigger. The current backend implementation + converts the incoming floating point sample values to signed 16-bit + integers. + + The required Windows system DLLs are linked with #pragma comment(lib, ...), + so you shouldn't need to add additional linker libs in the build process + (otherwise this is a bug which should be fixed in sokol_audio.h). + + THE ALSA BACKEND + ================ + The ALSA backend is automatically selected when compiling on Linux + ('linux' is defined). + + For thread synchronisation, the pthread_mutex_* functions are used. + + Samples are directly forwarded to ALSA in 32-bit float format, no + further conversion is taking place. + + You need to link with the 'asound' library, and the + header must be present (usually both are installed with some sort + of ALSA development package). + + LICENSE + ======= + + zlib/libpng license + + Copyright (c) 2018 Andre Weissflog + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software in a + product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ +#define SOKOL_AUDIO_INCLUDED (1) +#include +#include + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_AUDIO_API_DECL) +#define SOKOL_AUDIO_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_AUDIO_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_AUDIO_IMPL) +#define SOKOL_AUDIO_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_AUDIO_API_DECL __declspec(dllimport) +#else +#define SOKOL_AUDIO_API_DECL extern +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct saudio_desc { + int sample_rate; /* requested sample rate */ + int num_channels; /* number of channels, default: 1 (mono) */ + int buffer_frames; /* number of frames in streaming buffer */ + int packet_frames; /* number of frames in a packet */ + int num_packets; /* number of packets in packet queue */ + void (*stream_cb)(float* buffer, int num_frames, int num_channels); /* optional streaming callback (no user data) */ + void (*stream_userdata_cb)(float* buffer, int num_frames, int num_channels, void* user_data); /*... and with user data */ + void* user_data; /* optional user data argument for stream_userdata_cb */ +} saudio_desc; + +/* setup sokol-audio */ +SOKOL_AUDIO_API_DECL void saudio_setup(const saudio_desc* desc); +/* shutdown sokol-audio */ +SOKOL_AUDIO_API_DECL void saudio_shutdown(void); +/* true after setup if audio backend was successfully initialized */ +SOKOL_AUDIO_API_DECL bool saudio_isvalid(void); +/* return the saudio_desc.user_data pointer */ +SOKOL_AUDIO_API_DECL void* saudio_userdata(void); +/* return a copy of the original saudio_desc struct */ +SOKOL_AUDIO_API_DECL saudio_desc saudio_query_desc(void); +/* actual sample rate */ +SOKOL_AUDIO_API_DECL int saudio_sample_rate(void); +/* return actual backend buffer size in number of frames */ +SOKOL_AUDIO_API_DECL int saudio_buffer_frames(void); +/* actual number of channels */ +SOKOL_AUDIO_API_DECL int saudio_channels(void); +/* get current number of frames to fill packet queue */ +SOKOL_AUDIO_API_DECL int saudio_expect(void); +/* push sample frames from main thread, returns number of frames actually pushed */ +SOKOL_AUDIO_API_DECL int saudio_push(const float* frames, int num_frames); + +#ifdef __cplusplus +} /* extern "C" */ + +/* reference-based equivalents for c++ */ +inline void saudio_setup(const saudio_desc& desc) { return saudio_setup(&desc); } + +#endif +#endif // SOKOL_AUDIO_INCLUDED + +/*=== IMPLEMENTATION =========================================================*/ +#ifdef SOKOL_AUDIO_IMPL +#define SOKOL_AUDIO_IMPL_INCLUDED (1) +#include // memset, memcpy +#include // size_t + +#ifndef SOKOL_API_IMPL + #define SOKOL_API_IMPL +#endif +#ifndef SOKOL_DEBUG + #ifndef NDEBUG + #define SOKOL_DEBUG (1) + #endif +#endif +#ifndef SOKOL_ASSERT + #include + #define SOKOL_ASSERT(c) assert(c) +#endif +#ifndef SOKOL_MALLOC + #include + #define SOKOL_MALLOC(s) malloc(s) + #define SOKOL_FREE(p) free(p) +#endif +#ifndef SOKOL_LOG + #ifdef SOKOL_DEBUG + #include + #define SOKOL_LOG(s) { SOKOL_ASSERT(s); puts(s); } + #else + #define SOKOL_LOG(s) + #endif +#endif + +#ifndef _SOKOL_PRIVATE + #if defined(__GNUC__) || defined(__clang__) + #define _SOKOL_PRIVATE __attribute__((unused)) static + #else + #define _SOKOL_PRIVATE static + #endif +#endif + +#ifndef _SOKOL_UNUSED + #define _SOKOL_UNUSED(x) (void)(x) +#endif + +// platform detection defines +#if defined(SOKOL_DUMMY_BACKEND) + // nothing +#elif defined(__APPLE__) + #define _SAUDIO_APPLE (1) + #include + #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE + #define _SAUDIO_IOS (1) + #else + #define _SAUDIO_MACOS (1) + #endif +#elif defined(__EMSCRIPTEN__) + #define _SAUDIO_EMSCRIPTEN +#elif defined(_WIN32) + #define _SAUDIO_WINDOWS (1) + #include + #if (defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) + #define _SAUDIO_UWP (1) + #else + #define _SAUDIO_WIN32 (1) + #endif +#elif defined(__ANDROID__) + #define _SAUDIO_ANDROID (1) +#elif defined(__linux__) || defined(__unix__) + #define _SAUDIO_LINUX (1) +#else +#error "sokol_audio.h: Unknown platform" +#endif + +// platform-specific headers and definitions +#if defined(SOKOL_DUMMY_BACKEND) + #define _SAUDIO_NOTHREADS (1) +#elif defined(_SAUDIO_WINDOWS) + #define _SAUDIO_WINTHREADS (1) + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #ifndef NOMINMAX + #define NOMINMAX + #endif + #include + #include + #if defined(_SAUDIO_UWP) + #pragma comment (lib, "WindowsApp") + #else + #pragma comment (lib, "kernel32") + #pragma comment (lib, "ole32") + #endif + #ifndef CINTERFACE + #define CINTERFACE + #endif + #ifndef COBJMACROS + #define COBJMACROS + #endif + #ifndef CONST_VTABLE + #define CONST_VTABLE + #endif + #include + #include + static const IID _saudio_IID_IAudioClient = { 0x1cb9ad4c, 0xdbfa, 0x4c32, { 0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03, 0xb2 } }; + static const IID _saudio_IID_IMMDeviceEnumerator = { 0xa95664d2, 0x9614, 0x4f35, { 0xa7, 0x46, 0xde, 0x8d, 0xb6, 0x36, 0x17, 0xe6 } }; + static const CLSID _saudio_CLSID_IMMDeviceEnumerator = { 0xbcde0395, 0xe52f, 0x467c, { 0x8e, 0x3d, 0xc4, 0x57, 0x92, 0x91, 0x69, 0x2e } }; + static const IID _saudio_IID_IAudioRenderClient = { 0xf294acfc, 0x3146, 0x4483,{ 0xa7, 0xbf, 0xad, 0xdc, 0xa7, 0xc2, 0x60, 0xe2 } }; + static const IID _saudio_IID_Devinterface_Audio_Render = { 0xe6327cad, 0xdcec, 0x4949, {0xae, 0x8a, 0x99, 0x1e, 0x97, 0x6a, 0x79, 0xd2 } }; + static const IID _saudio_IID_IActivateAudioInterface_Completion_Handler = { 0x94ea2b94, 0xe9cc, 0x49e0, {0xc0, 0xff, 0xee, 0x64, 0xca, 0x8f, 0x5b, 0x90} }; + #if defined(__cplusplus) + #define _SOKOL_AUDIO_WIN32COM_ID(x) (x) + #else + #define _SOKOL_AUDIO_WIN32COM_ID(x) (&x) + #endif + /* fix for Visual Studio 2015 SDKs */ + #ifndef AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM + #define AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000 + #endif + #ifndef AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY + #define AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY 0x08000000 + #endif + #ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable:4505) /* unreferenced local function has been removed */ + #endif +#elif defined(_SAUDIO_APPLE) + #define _SAUDIO_PTHREADS (1) + #include + #if defined(_SAUDIO_IOS) + // always use system headers on iOS (for now at least) + #if !defined(SAUDIO_OSX_USE_SYSTEM_HEADERS) + #define SAUDIO_OSX_USE_SYSTEM_HEADERS (1) + #endif + #if !defined(__cplusplus) + #if __has_feature(objc_arc) && !__has_feature(objc_arc_fields) + #error "sokol_audio.h on iOS requires __has_feature(objc_arc_field) if ARC is enabled (use a more recent compiler version)" + #endif + #endif + #include + #include + #else + #if defined(SAUDIO_OSX_USE_SYSTEM_HEADERS) + #include + #endif + #endif +#elif defined(_SAUDIO_ANDROID) + #define _SAUDIO_PTHREADS (1) + #include + #include "SLES/OpenSLES_Android.h" +#elif defined(_SAUDIO_LINUX) + #define _SAUDIO_PTHREADS (1) + #include + #define ALSA_PCM_NEW_HW_PARAMS_API + #include +#elif defined(__EMSCRIPTEN__) + #define _SAUDIO_NOTHREADS (1) + #include +#endif + +#define _saudio_def(val, def) (((val) == 0) ? (def) : (val)) +#define _saudio_def_flt(val, def) (((val) == 0.0f) ? (def) : (val)) + +#define _SAUDIO_DEFAULT_SAMPLE_RATE (44100) +#define _SAUDIO_DEFAULT_BUFFER_FRAMES (2048) +#define _SAUDIO_DEFAULT_PACKET_FRAMES (128) +#define _SAUDIO_DEFAULT_NUM_PACKETS ((_SAUDIO_DEFAULT_BUFFER_FRAMES/_SAUDIO_DEFAULT_PACKET_FRAMES)*4) + +#ifndef SAUDIO_RING_MAX_SLOTS +#define SAUDIO_RING_MAX_SLOTS (1024) +#endif + +/*=== MUTEX WRAPPER DECLARATIONS =============================================*/ +#if defined(_SAUDIO_PTHREADS) + +typedef struct { + pthread_mutex_t mutex; +} _saudio_mutex_t; + +#elif defined(_SAUDIO_WINTHREADS) + +typedef struct { + CRITICAL_SECTION critsec; +} _saudio_mutex_t; + +#elif defined(_SAUDIO_NOTHREADS) + +typedef struct { + int dummy_mutex; +} _saudio_mutex_t; + +#endif + +/*=== DUMMY BACKEND DECLARATIONS =============================================*/ +#if defined(SOKOL_DUMMY_BACKEND) + +typedef struct { + int dummy_backend; +} _saudio_backend_t; + +/*=== COREAUDIO BACKEND DECLARATIONS =========================================*/ +#elif defined(_SAUDIO_APPLE) + +#if defined(SAUDIO_OSX_USE_SYSTEM_HEADERS) + +typedef AudioQueueRef _saudio_AudioQueueRef; +typedef AudioQueueBufferRef _saudio_AudioQueueBufferRef; +typedef AudioStreamBasicDescription _saudio_AudioStreamBasicDescription; +typedef OSStatus _saudio_OSStatus; + +#define _saudio_kAudioFormatLinearPCM (kAudioFormatLinearPCM) +#define _saudio_kLinearPCMFormatFlagIsFloat (kLinearPCMFormatFlagIsFloat) +#define _saudio_kAudioFormatFlagIsPacked (kAudioFormatFlagIsPacked) + +#else + +// embedded AudioToolbox declarations +typedef uint32_t _saudio_AudioFormatID; +typedef uint32_t _saudio_AudioFormatFlags; +typedef int32_t _saudio_OSStatus; +typedef uint32_t _saudio_SMPTETimeType; +typedef uint32_t _saudio_SMPTETimeFlags; +typedef uint32_t _saudio_AudioTimeStampFlags; +typedef void* _saudio_CFRunLoopRef; +typedef void* _saudio_CFStringRef; +typedef void* _saudio_AudioQueueRef; + +#define _saudio_kAudioFormatLinearPCM ('lpcm') +#define _saudio_kLinearPCMFormatFlagIsFloat (1U << 0) +#define _saudio_kAudioFormatFlagIsPacked (1U << 3) + +typedef struct _saudio_AudioStreamBasicDescription { + double mSampleRate; + _saudio_AudioFormatID mFormatID; + _saudio_AudioFormatFlags mFormatFlags; + uint32_t mBytesPerPacket; + uint32_t mFramesPerPacket; + uint32_t mBytesPerFrame; + uint32_t mChannelsPerFrame; + uint32_t mBitsPerChannel; + uint32_t mReserved; +} _saudio_AudioStreamBasicDescription; + +typedef struct _saudio_AudioStreamPacketDescription { + int64_t mStartOffset; + uint32_t mVariableFramesInPacket; + uint32_t mDataByteSize; +} _saudio_AudioStreamPacketDescription; + +typedef struct _saudio_SMPTETime { + int16_t mSubframes; + int16_t mSubframeDivisor; + uint32_t mCounter; + _saudio_SMPTETimeType mType; + _saudio_SMPTETimeFlags mFlags; + int16_t mHours; + int16_t mMinutes; + int16_t mSeconds; + int16_t mFrames; +} _saudio_SMPTETime; + +typedef struct _saudio_AudioTimeStamp { + double mSampleTime; + uint64_t mHostTime; + double mRateScalar; + uint64_t mWordClockTime; + _saudio_SMPTETime mSMPTETime; + _saudio_AudioTimeStampFlags mFlags; + uint32_t mReserved; +} _saudio_AudioTimeStamp; + +typedef struct _saudio_AudioQueueBuffer { + const uint32_t mAudioDataBytesCapacity; + void* const mAudioData; + uint32_t mAudioDataByteSize; + void * mUserData; + const uint32_t mPacketDescriptionCapacity; + _saudio_AudioStreamPacketDescription* const mPacketDescriptions; + uint32_t mPacketDescriptionCount; +} _saudio_AudioQueueBuffer; +typedef _saudio_AudioQueueBuffer* _saudio_AudioQueueBufferRef; + +typedef void (*_saudio_AudioQueueOutputCallback)(void* user_data, _saudio_AudioQueueRef inAQ, _saudio_AudioQueueBufferRef inBuffer); + +extern _saudio_OSStatus AudioQueueNewOutput(const _saudio_AudioStreamBasicDescription* inFormat, _saudio_AudioQueueOutputCallback inCallbackProc, void* inUserData, _saudio_CFRunLoopRef inCallbackRunLoop, _saudio_CFStringRef inCallbackRunLoopMode, uint32_t inFlags, _saudio_AudioQueueRef* outAQ); +extern _saudio_OSStatus AudioQueueDispose(_saudio_AudioQueueRef inAQ, bool inImmediate); +extern _saudio_OSStatus AudioQueueAllocateBuffer(_saudio_AudioQueueRef inAQ, uint32_t inBufferByteSize, _saudio_AudioQueueBufferRef* outBuffer); +extern _saudio_OSStatus AudioQueueEnqueueBuffer(_saudio_AudioQueueRef inAQ, _saudio_AudioQueueBufferRef inBuffer, uint32_t inNumPacketDescs, const _saudio_AudioStreamPacketDescription* inPacketDescs); +extern _saudio_OSStatus AudioQueueStart(_saudio_AudioQueueRef inAQ, const _saudio_AudioTimeStamp * inStartTime); +extern _saudio_OSStatus AudioQueueStop(_saudio_AudioQueueRef inAQ, bool inImmediate); +#endif // SAUDIO_OSX_USE_SYSTEM_HEADERS + +typedef struct { + _saudio_AudioQueueRef ca_audio_queue; + #if defined(_SAUDIO_IOS) + id ca_interruption_handler; + #endif +} _saudio_backend_t; + +/*=== ALSA BACKEND DECLARATIONS ==============================================*/ +#elif defined(_SAUDIO_LINUX) + +typedef struct { + snd_pcm_t* device; + float* buffer; + int buffer_byte_size; + int buffer_frames; + pthread_t thread; + bool thread_stop; +} _saudio_backend_t; + +/*=== OpenSLES BACKEND DECLARATIONS ==============================================*/ +#elif defined(_SAUDIO_ANDROID) + +#define SAUDIO_NUM_BUFFERS 2 + +typedef struct { + pthread_mutex_t mutex; + pthread_cond_t cond; + int count; +} _saudio_semaphore_t; + +typedef struct { + SLObjectItf engine_obj; + SLEngineItf engine; + SLObjectItf output_mix_obj; + SLVolumeItf output_mix_vol; + SLDataLocator_OutputMix out_locator; + SLDataSink dst_data_sink; + SLObjectItf player_obj; + SLPlayItf player; + SLVolumeItf player_vol; + SLAndroidSimpleBufferQueueItf player_buffer_queue; + + int16_t* output_buffers[SAUDIO_NUM_BUFFERS]; + float* src_buffer; + int active_buffer; + _saudio_semaphore_t buffer_sem; + pthread_t thread; + volatile int thread_stop; + SLDataLocator_AndroidSimpleBufferQueue in_locator; +} _saudio_backend_t; + +/*=== WASAPI BACKEND DECLARATIONS ============================================*/ +#elif defined(_SAUDIO_WINDOWS) + +typedef struct { + HANDLE thread_handle; + HANDLE buffer_end_event; + bool stop; + UINT32 dst_buffer_frames; + int src_buffer_frames; + int src_buffer_byte_size; + int src_buffer_pos; + float* src_buffer; +} _saudio_wasapi_thread_data_t; + +typedef struct { + #if defined(_SAUDIO_UWP) + LPOLESTR interface_activation_audio_interface_uid_string; + IActivateAudioInterfaceAsyncOperation* interface_activation_operation; + BOOL interface_activation_success; + HANDLE interface_activation_mutex; + #else + IMMDeviceEnumerator* device_enumerator; + IMMDevice* device; + #endif + IAudioClient* audio_client; + IAudioRenderClient* render_client; + int si16_bytes_per_frame; + _saudio_wasapi_thread_data_t thread; +} _saudio_backend_t; + +/*=== WEBAUDIO BACKEND DECLARATIONS ==========================================*/ +#elif defined(_SAUDIO_EMSCRIPTEN) + +typedef struct { + uint8_t* buffer; +} _saudio_backend_t; + +#else +#error "unknown platform" +#endif + +/*=== GENERAL DECLARATIONS ===================================================*/ + +/* a ringbuffer structure */ +typedef struct { + int head; // next slot to write to + int tail; // next slot to read from + int num; // number of slots in queue + int queue[SAUDIO_RING_MAX_SLOTS]; +} _saudio_ring_t; + +/* a packet FIFO structure */ +typedef struct { + bool valid; + int packet_size; /* size of a single packets in bytes(!) */ + int num_packets; /* number of packet in fifo */ + uint8_t* base_ptr; /* packet memory chunk base pointer (dynamically allocated) */ + int cur_packet; /* current write-packet */ + int cur_offset; /* current byte-offset into current write packet */ + _saudio_mutex_t mutex; /* mutex for thread-safe access */ + _saudio_ring_t read_queue; /* buffers with data, ready to be streamed */ + _saudio_ring_t write_queue; /* empty buffers, ready to be pushed to */ +} _saudio_fifo_t; + +/* sokol-audio state */ +typedef struct { + bool valid; + void (*stream_cb)(float* buffer, int num_frames, int num_channels); + void (*stream_userdata_cb)(float* buffer, int num_frames, int num_channels, void* user_data); + void* user_data; + int sample_rate; /* sample rate */ + int buffer_frames; /* number of frames in streaming buffer */ + int bytes_per_frame; /* filled by backend */ + int packet_frames; /* number of frames in a packet */ + int num_packets; /* number of packets in packet queue */ + int num_channels; /* actual number of channels */ + saudio_desc desc; + _saudio_fifo_t fifo; + _saudio_backend_t backend; +} _saudio_state_t; + +static _saudio_state_t _saudio; + +_SOKOL_PRIVATE bool _saudio_has_callback(void) { + return (_saudio.stream_cb || _saudio.stream_userdata_cb); +} + +_SOKOL_PRIVATE void _saudio_stream_callback(float* buffer, int num_frames, int num_channels) { + if (_saudio.stream_cb) { + _saudio.stream_cb(buffer, num_frames, num_channels); + } + else if (_saudio.stream_userdata_cb) { + _saudio.stream_userdata_cb(buffer, num_frames, num_channels, _saudio.user_data); + } +} + +/*=== MUTEX IMPLEMENTATION ===================================================*/ +#if defined(_SAUDIO_NOTHREADS) + +_SOKOL_PRIVATE void _saudio_mutex_init(_saudio_mutex_t* m) { (void)m; } +_SOKOL_PRIVATE void _saudio_mutex_destroy(_saudio_mutex_t* m) { (void)m; } +_SOKOL_PRIVATE void _saudio_mutex_lock(_saudio_mutex_t* m) { (void)m; } +_SOKOL_PRIVATE void _saudio_mutex_unlock(_saudio_mutex_t* m) { (void)m; } + +#elif defined(_SAUDIO_PTHREADS) + +_SOKOL_PRIVATE void _saudio_mutex_init(_saudio_mutex_t* m) { + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutex_init(&m->mutex, &attr); +} + +_SOKOL_PRIVATE void _saudio_mutex_destroy(_saudio_mutex_t* m) { + pthread_mutex_destroy(&m->mutex); +} + +_SOKOL_PRIVATE void _saudio_mutex_lock(_saudio_mutex_t* m) { + pthread_mutex_lock(&m->mutex); +} + +_SOKOL_PRIVATE void _saudio_mutex_unlock(_saudio_mutex_t* m) { + pthread_mutex_unlock(&m->mutex); +} + +#elif defined(_SAUDIO_WINTHREADS) + +_SOKOL_PRIVATE void _saudio_mutex_init(_saudio_mutex_t* m) { + InitializeCriticalSection(&m->critsec); +} + +_SOKOL_PRIVATE void _saudio_mutex_destroy(_saudio_mutex_t* m) { + DeleteCriticalSection(&m->critsec); +} + +_SOKOL_PRIVATE void _saudio_mutex_lock(_saudio_mutex_t* m) { + EnterCriticalSection(&m->critsec); +} + +_SOKOL_PRIVATE void _saudio_mutex_unlock(_saudio_mutex_t* m) { + LeaveCriticalSection(&m->critsec); +} +#else +#error "unknown platform!" +#endif + +/*=== RING-BUFFER QUEUE IMPLEMENTATION =======================================*/ +_SOKOL_PRIVATE int _saudio_ring_idx(_saudio_ring_t* ring, int i) { + return (i % ring->num); +} + +_SOKOL_PRIVATE void _saudio_ring_init(_saudio_ring_t* ring, int num_slots) { + SOKOL_ASSERT((num_slots + 1) <= SAUDIO_RING_MAX_SLOTS); + ring->head = 0; + ring->tail = 0; + /* one slot reserved to detect 'full' vs 'empty' */ + ring->num = num_slots + 1; +} + +_SOKOL_PRIVATE bool _saudio_ring_full(_saudio_ring_t* ring) { + return _saudio_ring_idx(ring, ring->head + 1) == ring->tail; +} + +_SOKOL_PRIVATE bool _saudio_ring_empty(_saudio_ring_t* ring) { + return ring->head == ring->tail; +} + +_SOKOL_PRIVATE int _saudio_ring_count(_saudio_ring_t* ring) { + int count; + if (ring->head >= ring->tail) { + count = ring->head - ring->tail; + } + else { + count = (ring->head + ring->num) - ring->tail; + } + SOKOL_ASSERT(count < ring->num); + return count; +} + +_SOKOL_PRIVATE void _saudio_ring_enqueue(_saudio_ring_t* ring, int val) { + SOKOL_ASSERT(!_saudio_ring_full(ring)); + ring->queue[ring->head] = val; + ring->head = _saudio_ring_idx(ring, ring->head + 1); +} + +_SOKOL_PRIVATE int _saudio_ring_dequeue(_saudio_ring_t* ring) { + SOKOL_ASSERT(!_saudio_ring_empty(ring)); + int val = ring->queue[ring->tail]; + ring->tail = _saudio_ring_idx(ring, ring->tail + 1); + return val; +} + +/*--- a packet fifo for queueing audio data from main thread ----------------*/ +_SOKOL_PRIVATE void _saudio_fifo_init_mutex(_saudio_fifo_t* fifo) { + /* this must be called before initializing both the backend and the fifo itself! */ + _saudio_mutex_init(&fifo->mutex); +} + +_SOKOL_PRIVATE void _saudio_fifo_init(_saudio_fifo_t* fifo, int packet_size, int num_packets) { + /* NOTE: there's a chicken-egg situation during the init phase where the + streaming thread must be started before the fifo is actually initialized, + thus the fifo init must already be protected from access by the fifo_read() func. + */ + _saudio_mutex_lock(&fifo->mutex); + SOKOL_ASSERT((packet_size > 0) && (num_packets > 0)); + fifo->packet_size = packet_size; + fifo->num_packets = num_packets; + fifo->base_ptr = (uint8_t*) SOKOL_MALLOC((size_t)(packet_size * num_packets)); + SOKOL_ASSERT(fifo->base_ptr); + fifo->cur_packet = -1; + fifo->cur_offset = 0; + _saudio_ring_init(&fifo->read_queue, num_packets); + _saudio_ring_init(&fifo->write_queue, num_packets); + for (int i = 0; i < num_packets; i++) { + _saudio_ring_enqueue(&fifo->write_queue, i); + } + SOKOL_ASSERT(_saudio_ring_full(&fifo->write_queue)); + SOKOL_ASSERT(_saudio_ring_count(&fifo->write_queue) == num_packets); + SOKOL_ASSERT(_saudio_ring_empty(&fifo->read_queue)); + SOKOL_ASSERT(_saudio_ring_count(&fifo->read_queue) == 0); + fifo->valid = true; + _saudio_mutex_unlock(&fifo->mutex); +} + +_SOKOL_PRIVATE void _saudio_fifo_shutdown(_saudio_fifo_t* fifo) { + SOKOL_ASSERT(fifo->base_ptr); + SOKOL_FREE(fifo->base_ptr); + fifo->base_ptr = 0; + fifo->valid = false; + _saudio_mutex_destroy(&fifo->mutex); +} + +_SOKOL_PRIVATE int _saudio_fifo_writable_bytes(_saudio_fifo_t* fifo) { + _saudio_mutex_lock(&fifo->mutex); + int num_bytes = (_saudio_ring_count(&fifo->write_queue) * fifo->packet_size); + if (fifo->cur_packet != -1) { + num_bytes += fifo->packet_size - fifo->cur_offset; + } + _saudio_mutex_unlock(&fifo->mutex); + SOKOL_ASSERT((num_bytes >= 0) && (num_bytes <= (fifo->num_packets * fifo->packet_size))); + return num_bytes; +} + +/* write new data to the write queue, this is called from main thread */ +_SOKOL_PRIVATE int _saudio_fifo_write(_saudio_fifo_t* fifo, const uint8_t* ptr, int num_bytes) { + /* returns the number of bytes written, this will be smaller then requested + if the write queue runs full + */ + int all_to_copy = num_bytes; + while (all_to_copy > 0) { + /* need to grab a new packet? */ + if (fifo->cur_packet == -1) { + _saudio_mutex_lock(&fifo->mutex); + if (!_saudio_ring_empty(&fifo->write_queue)) { + fifo->cur_packet = _saudio_ring_dequeue(&fifo->write_queue); + } + _saudio_mutex_unlock(&fifo->mutex); + SOKOL_ASSERT(fifo->cur_offset == 0); + } + /* append data to current write packet */ + if (fifo->cur_packet != -1) { + int to_copy = all_to_copy; + const int max_copy = fifo->packet_size - fifo->cur_offset; + if (to_copy > max_copy) { + to_copy = max_copy; + } + uint8_t* dst = fifo->base_ptr + fifo->cur_packet * fifo->packet_size + fifo->cur_offset; + memcpy(dst, ptr, (size_t)to_copy); + ptr += to_copy; + fifo->cur_offset += to_copy; + all_to_copy -= to_copy; + SOKOL_ASSERT(fifo->cur_offset <= fifo->packet_size); + SOKOL_ASSERT(all_to_copy >= 0); + } + else { + /* early out if we're starving */ + int bytes_copied = num_bytes - all_to_copy; + SOKOL_ASSERT((bytes_copied >= 0) && (bytes_copied < num_bytes)); + return bytes_copied; + } + /* if write packet is full, push to read queue */ + if (fifo->cur_offset == fifo->packet_size) { + _saudio_mutex_lock(&fifo->mutex); + _saudio_ring_enqueue(&fifo->read_queue, fifo->cur_packet); + _saudio_mutex_unlock(&fifo->mutex); + fifo->cur_packet = -1; + fifo->cur_offset = 0; + } + } + SOKOL_ASSERT(all_to_copy == 0); + return num_bytes; +} + +/* read queued data, this is called form the stream callback (maybe separate thread) */ +_SOKOL_PRIVATE int _saudio_fifo_read(_saudio_fifo_t* fifo, uint8_t* ptr, int num_bytes) { + /* NOTE: fifo_read might be called before the fifo is properly initialized */ + _saudio_mutex_lock(&fifo->mutex); + int num_bytes_copied = 0; + if (fifo->valid) { + SOKOL_ASSERT(0 == (num_bytes % fifo->packet_size)); + SOKOL_ASSERT(num_bytes <= (fifo->packet_size * fifo->num_packets)); + const int num_packets_needed = num_bytes / fifo->packet_size; + uint8_t* dst = ptr; + /* either pull a full buffer worth of data, or nothing */ + if (_saudio_ring_count(&fifo->read_queue) >= num_packets_needed) { + for (int i = 0; i < num_packets_needed; i++) { + int packet_index = _saudio_ring_dequeue(&fifo->read_queue); + _saudio_ring_enqueue(&fifo->write_queue, packet_index); + const uint8_t* src = fifo->base_ptr + packet_index * fifo->packet_size; + memcpy(dst, src, (size_t)fifo->packet_size); + dst += fifo->packet_size; + num_bytes_copied += fifo->packet_size; + } + SOKOL_ASSERT(num_bytes == num_bytes_copied); + } + } + _saudio_mutex_unlock(&fifo->mutex); + return num_bytes_copied; +} + +/*=== DUMMY BACKEND IMPLEMENTATION ===========================================*/ +#if defined(SOKOL_DUMMY_BACKEND) +_SOKOL_PRIVATE bool _saudio_backend_init(void) { + _saudio.bytes_per_frame = _saudio.num_channels * (int)sizeof(float); + return true; +}; +_SOKOL_PRIVATE void _saudio_backend_shutdown(void) { }; + +/*=== COREAUDIO BACKEND IMPLEMENTATION =======================================*/ +#elif defined(_SAUDIO_APPLE) + +#if defined(_SAUDIO_IOS) +#if __has_feature(objc_arc) +#define _SAUDIO_OBJC_RELEASE(obj) { obj = nil; } +#else +#define _SAUDIO_OBJC_RELEASE(obj) { [obj release]; obj = nil; } +#endif + +@interface _saudio_interruption_handler : NSObject { } +@end + +@implementation _saudio_interruption_handler +-(id)init { + self = [super init]; + AVAudioSession* session = [AVAudioSession sharedInstance]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handle_interruption:) name:AVAudioSessionInterruptionNotification object:session]; + return self; +} + +-(void)dealloc { + [self remove_handler]; + #if !__has_feature(objc_arc) + [super dealloc]; + #endif +} + +-(void)remove_handler { + [[NSNotificationCenter defaultCenter] removeObserver:self name:@"AVAudioSessionInterruptionNotification" object:nil]; +} + +-(void)handle_interruption:(NSNotification*)notification { + AVAudioSession* session = [AVAudioSession sharedInstance]; + SOKOL_ASSERT(session); + NSDictionary* dict = notification.userInfo; + SOKOL_ASSERT(dict); + NSInteger type = [[dict valueForKey:AVAudioSessionInterruptionTypeKey] integerValue]; + switch (type) { + case AVAudioSessionInterruptionTypeBegan: + AudioQueuePause(_saudio.backend.ca_audio_queue); + [session setActive:false error:nil]; + break; + case AVAudioSessionInterruptionTypeEnded: + [session setActive:true error:nil]; + AudioQueueStart(_saudio.backend.ca_audio_queue, NULL); + break; + default: + break; + } +} +@end +#endif // _SAUDIO_IOS + +/* NOTE: the buffer data callback is called on a separate thread! */ +_SOKOL_PRIVATE void _saudio_coreaudio_callback(void* user_data, _saudio_AudioQueueRef queue, _saudio_AudioQueueBufferRef buffer) { + _SOKOL_UNUSED(user_data); + if (_saudio_has_callback()) { + const int num_frames = (int)buffer->mAudioDataByteSize / _saudio.bytes_per_frame; + const int num_channels = _saudio.num_channels; + _saudio_stream_callback((float*)buffer->mAudioData, num_frames, num_channels); + } + else { + uint8_t* ptr = (uint8_t*)buffer->mAudioData; + int num_bytes = (int) buffer->mAudioDataByteSize; + if (0 == _saudio_fifo_read(&_saudio.fifo, ptr, num_bytes)) { + /* not enough read data available, fill the entire buffer with silence */ + memset(ptr, 0, (size_t)num_bytes); + } + } + AudioQueueEnqueueBuffer(queue, buffer, 0, NULL); +} + +_SOKOL_PRIVATE bool _saudio_backend_init(void) { + SOKOL_ASSERT(0 == _saudio.backend.ca_audio_queue); + + #if defined(_SAUDIO_IOS) + /* activate audio session */ + AVAudioSession* session = [AVAudioSession sharedInstance]; + SOKOL_ASSERT(session != nil); + [session setCategory: AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionDefaultToSpeaker error:nil]; + [session setActive:true error:nil]; + + /* create interruption handler */ + _saudio.backend.ca_interruption_handler = [[_saudio_interruption_handler alloc] init]; + #endif // _SAUDIO_IOS + + /* create an audio queue with fp32 samples */ + _saudio_AudioStreamBasicDescription fmt; + memset(&fmt, 0, sizeof(fmt)); + fmt.mSampleRate = (double) _saudio.sample_rate; + fmt.mFormatID = _saudio_kAudioFormatLinearPCM; + fmt.mFormatFlags = _saudio_kLinearPCMFormatFlagIsFloat | _saudio_kAudioFormatFlagIsPacked; + fmt.mFramesPerPacket = 1; + fmt.mChannelsPerFrame = (uint32_t) _saudio.num_channels; + fmt.mBytesPerFrame = (uint32_t)sizeof(float) * (uint32_t)_saudio.num_channels; + fmt.mBytesPerPacket = fmt.mBytesPerFrame; + fmt.mBitsPerChannel = 32; + _saudio_OSStatus res = AudioQueueNewOutput(&fmt, _saudio_coreaudio_callback, 0, NULL, NULL, 0, &_saudio.backend.ca_audio_queue); + SOKOL_ASSERT((res == 0) && _saudio.backend.ca_audio_queue); + + /* create 2 audio buffers */ + for (int i = 0; i < 2; i++) { + _saudio_AudioQueueBufferRef buf = NULL; + const uint32_t buf_byte_size = (uint32_t)_saudio.buffer_frames * fmt.mBytesPerFrame; + res = AudioQueueAllocateBuffer(_saudio.backend.ca_audio_queue, buf_byte_size, &buf); + SOKOL_ASSERT((res == 0) && buf); + buf->mAudioDataByteSize = buf_byte_size; + memset(buf->mAudioData, 0, buf->mAudioDataByteSize); + AudioQueueEnqueueBuffer(_saudio.backend.ca_audio_queue, buf, 0, NULL); + } + + /* init or modify actual playback parameters */ + _saudio.bytes_per_frame = (int)fmt.mBytesPerFrame; + + /* ...and start playback */ + res = AudioQueueStart(_saudio.backend.ca_audio_queue, NULL); + SOKOL_ASSERT(0 == res); + + return true; +} + +_SOKOL_PRIVATE void _saudio_backend_shutdown(void) { + AudioQueueStop(_saudio.backend.ca_audio_queue, true); + AudioQueueDispose(_saudio.backend.ca_audio_queue, false); + _saudio.backend.ca_audio_queue = NULL; + #if defined(_SAUDIO_IOS) + /* remove interruption handler */ + if (_saudio.backend.ca_interruption_handler != nil) { + [_saudio.backend.ca_interruption_handler remove_handler]; + _SAUDIO_OBJC_RELEASE(_saudio.backend.ca_interruption_handler); + } + /* deactivate audio session */ + AVAudioSession* session = [AVAudioSession sharedInstance]; + SOKOL_ASSERT(session); + [session setActive:false error:nil];; + #endif // _SAUDIO_IOS +} + +/*=== ALSA BACKEND IMPLEMENTATION ============================================*/ +#elif defined(_SAUDIO_LINUX) + +/* the streaming callback runs in a separate thread */ +_SOKOL_PRIVATE void* _saudio_alsa_cb(void* param) { + _SOKOL_UNUSED(param); + while (!_saudio.backend.thread_stop) { + /* snd_pcm_writei() will be blocking until it needs data */ + int write_res = snd_pcm_writei(_saudio.backend.device, _saudio.backend.buffer, (snd_pcm_uframes_t)_saudio.backend.buffer_frames); + if (write_res < 0) { + /* underrun occurred */ + snd_pcm_prepare(_saudio.backend.device); + } + else { + /* fill the streaming buffer with new data */ + if (_saudio_has_callback()) { + _saudio_stream_callback(_saudio.backend.buffer, _saudio.backend.buffer_frames, _saudio.num_channels); + } + else { + if (0 == _saudio_fifo_read(&_saudio.fifo, (uint8_t*)_saudio.backend.buffer, _saudio.backend.buffer_byte_size)) { + /* not enough read data available, fill the entire buffer with silence */ + memset(_saudio.backend.buffer, 0, (size_t)_saudio.backend.buffer_byte_size); + } + } + } + } + return 0; +} + +_SOKOL_PRIVATE bool _saudio_backend_init(void) { + int dir; uint32_t rate; + int rc = snd_pcm_open(&_saudio.backend.device, "default", SND_PCM_STREAM_PLAYBACK, 0); + if (rc < 0) { + SOKOL_LOG("sokol_audio.h: snd_pcm_open() failed"); + return false; + } + + /* configuration works by restricting the 'configuration space' step + by step, we require all parameters except the sample rate to + match perfectly + */ + snd_pcm_hw_params_t* params = 0; + snd_pcm_hw_params_alloca(¶ms); + snd_pcm_hw_params_any(_saudio.backend.device, params); + snd_pcm_hw_params_set_access(_saudio.backend.device, params, SND_PCM_ACCESS_RW_INTERLEAVED); + if (0 > snd_pcm_hw_params_set_format(_saudio.backend.device, params, SND_PCM_FORMAT_FLOAT_LE)) { + SOKOL_LOG("sokol_audio.h: float samples not supported"); + goto error; + } + if (0 > snd_pcm_hw_params_set_buffer_size(_saudio.backend.device, params, (snd_pcm_uframes_t)_saudio.buffer_frames)) { + SOKOL_LOG("sokol_audio.h: requested buffer size not supported"); + goto error; + } + if (0 > snd_pcm_hw_params_set_channels(_saudio.backend.device, params, (uint32_t)_saudio.num_channels)) { + SOKOL_LOG("sokol_audio.h: requested channel count not supported"); + goto error; + } + /* let ALSA pick a nearby sampling rate */ + rate = (uint32_t) _saudio.sample_rate; + dir = 0; + if (0 > snd_pcm_hw_params_set_rate_near(_saudio.backend.device, params, &rate, &dir)) { + SOKOL_LOG("sokol_audio.h: snd_pcm_hw_params_set_rate_near() failed"); + goto error; + } + if (0 > snd_pcm_hw_params(_saudio.backend.device, params)) { + SOKOL_LOG("sokol_audio.h: snd_pcm_hw_params() failed"); + goto error; + } + + /* read back actual sample rate and channels */ + _saudio.sample_rate = (int)rate; + _saudio.bytes_per_frame = _saudio.num_channels * (int)sizeof(float); + + /* allocate the streaming buffer */ + _saudio.backend.buffer_byte_size = _saudio.buffer_frames * _saudio.bytes_per_frame; + _saudio.backend.buffer_frames = _saudio.buffer_frames; + _saudio.backend.buffer = (float*) SOKOL_MALLOC((size_t)_saudio.backend.buffer_byte_size); + memset(_saudio.backend.buffer, 0, (size_t)_saudio.backend.buffer_byte_size); + + /* create the buffer-streaming start thread */ + if (0 != pthread_create(&_saudio.backend.thread, 0, _saudio_alsa_cb, 0)) { + SOKOL_LOG("sokol_audio.h: pthread_create() failed"); + goto error; + } + + return true; +error: + if (_saudio.backend.device) { + snd_pcm_close(_saudio.backend.device); + _saudio.backend.device = 0; + } + return false; +}; + +_SOKOL_PRIVATE void _saudio_backend_shutdown(void) { + SOKOL_ASSERT(_saudio.backend.device); + _saudio.backend.thread_stop = true; + pthread_join(_saudio.backend.thread, 0); + snd_pcm_drain(_saudio.backend.device); + snd_pcm_close(_saudio.backend.device); + SOKOL_FREE(_saudio.backend.buffer); +}; + +/*=== WASAPI BACKEND IMPLEMENTATION ==========================================*/ +#elif defined(_SAUDIO_WINDOWS) + +#if defined(_SAUDIO_UWP) +/* Minimal implementation of an IActivateAudioInterfaceCompletionHandler COM object in plain C. + Meant to be a static singleton (always one reference when add/remove reference) + and implements IUnknown and IActivateAudioInterfaceCompletionHandler when queryinterface'd + + Do not know why but IActivateAudioInterfaceCompletionHandler's GUID is not the one system queries for, + so I'm advertising the one actually requested. +*/ +_SOKOL_PRIVATE HRESULT STDMETHODCALLTYPE _saudio_interface_completion_handler_queryinterface(IActivateAudioInterfaceCompletionHandler* instance, REFIID riid, void** ppvObject) { + if (!ppvObject) { + return E_POINTER; + } + + if (IsEqualIID(riid, _SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_IActivateAudioInterface_Completion_Handler)) || IsEqualIID(riid, _SOKOL_AUDIO_WIN32COM_ID(IID_IUnknown))) + { + *ppvObject = (void*)instance; + return S_OK; + } + + *ppvObject = NULL; + return E_NOINTERFACE; +} + +_SOKOL_PRIVATE ULONG STDMETHODCALLTYPE _saudio_interface_completion_handler_addref_release(IActivateAudioInterfaceCompletionHandler* instance) { + _SOKOL_UNUSED(instance); + return 1; +} + +_SOKOL_PRIVATE HRESULT STDMETHODCALLTYPE _saudio_backend_activate_audio_interface_cb(IActivateAudioInterfaceCompletionHandler* instance, IActivateAudioInterfaceAsyncOperation* activateOperation) { + _SOKOL_UNUSED(instance); + WaitForSingleObject(_saudio.backend.interface_activation_mutex, INFINITE); + _saudio.backend.interface_activation_success = TRUE; + HRESULT activation_result; + if (FAILED(activateOperation->lpVtbl->GetActivateResult(activateOperation, &activation_result, (IUnknown**)(&_saudio.backend.audio_client))) || FAILED(activation_result)) { + _saudio.backend.interface_activation_success = FALSE; + } + + ReleaseMutex(_saudio.backend.interface_activation_mutex); + return S_OK; +} +#endif // _SAUDIO_UWP + +/* fill intermediate buffer with new data and reset buffer_pos */ +_SOKOL_PRIVATE void _saudio_wasapi_fill_buffer(void) { + if (_saudio_has_callback()) { + _saudio_stream_callback(_saudio.backend.thread.src_buffer, _saudio.backend.thread.src_buffer_frames, _saudio.num_channels); + } + else { + if (0 == _saudio_fifo_read(&_saudio.fifo, (uint8_t*)_saudio.backend.thread.src_buffer, _saudio.backend.thread.src_buffer_byte_size)) { + /* not enough read data available, fill the entire buffer with silence */ + memset(_saudio.backend.thread.src_buffer, 0, (size_t)_saudio.backend.thread.src_buffer_byte_size); + } + } +} + +_SOKOL_PRIVATE void _saudio_wasapi_submit_buffer(int num_frames) { + BYTE* wasapi_buffer = 0; + if (FAILED(IAudioRenderClient_GetBuffer(_saudio.backend.render_client, num_frames, &wasapi_buffer))) { + return; + } + SOKOL_ASSERT(wasapi_buffer); + + /* convert float samples to int16_t, refill float buffer if needed */ + const int num_samples = num_frames * _saudio.num_channels; + int16_t* dst = (int16_t*) wasapi_buffer; + int buffer_pos = _saudio.backend.thread.src_buffer_pos; + const int buffer_float_size = _saudio.backend.thread.src_buffer_byte_size / (int)sizeof(float); + float* src = _saudio.backend.thread.src_buffer; + for (int i = 0; i < num_samples; i++) { + if (0 == buffer_pos) { + _saudio_wasapi_fill_buffer(); + } + dst[i] = (int16_t) (src[buffer_pos] * 0x7FFF); + buffer_pos += 1; + if (buffer_pos == buffer_float_size) { + buffer_pos = 0; + } + } + _saudio.backend.thread.src_buffer_pos = buffer_pos; + + IAudioRenderClient_ReleaseBuffer(_saudio.backend.render_client, num_frames, 0); +} + +_SOKOL_PRIVATE DWORD WINAPI _saudio_wasapi_thread_fn(LPVOID param) { + (void)param; + _saudio_wasapi_submit_buffer(_saudio.backend.thread.src_buffer_frames); + IAudioClient_Start(_saudio.backend.audio_client); + while (!_saudio.backend.thread.stop) { + WaitForSingleObject(_saudio.backend.thread.buffer_end_event, INFINITE); + UINT32 padding = 0; + if (FAILED(IAudioClient_GetCurrentPadding(_saudio.backend.audio_client, &padding))) { + continue; + } + SOKOL_ASSERT(_saudio.backend.thread.dst_buffer_frames >= padding); + int num_frames = (int)_saudio.backend.thread.dst_buffer_frames - (int)padding; + if (num_frames > 0) { + _saudio_wasapi_submit_buffer(num_frames); + } + } + return 0; +} + +_SOKOL_PRIVATE void _saudio_wasapi_release(void) { + if (_saudio.backend.thread.src_buffer) { + SOKOL_FREE(_saudio.backend.thread.src_buffer); + _saudio.backend.thread.src_buffer = 0; + } + if (_saudio.backend.render_client) { + IAudioRenderClient_Release(_saudio.backend.render_client); + _saudio.backend.render_client = 0; + } + if (_saudio.backend.audio_client) { + IAudioClient_Release(_saudio.backend.audio_client); + _saudio.backend.audio_client = 0; + } + #if defined(_SAUDIO_UWP) + if (_saudio.backend.interface_activation_audio_interface_uid_string) { + CoTaskMemFree(_saudio.backend.interface_activation_audio_interface_uid_string); + _saudio.backend.interface_activation_audio_interface_uid_string = 0; + } + if (_saudio.backend.interface_activation_operation) { + IActivateAudioInterfaceAsyncOperation_Release(_saudio.backend.interface_activation_operation); + _saudio.backend.interface_activation_operation = 0; + } + #else + if (_saudio.backend.device) { + IMMDevice_Release(_saudio.backend.device); + _saudio.backend.device = 0; + } + if (_saudio.backend.device_enumerator) { + IMMDeviceEnumerator_Release(_saudio.backend.device_enumerator); + _saudio.backend.device_enumerator = 0; + } + #endif + if (0 != _saudio.backend.thread.buffer_end_event) { + CloseHandle(_saudio.backend.thread.buffer_end_event); + _saudio.backend.thread.buffer_end_event = 0; + } +} + +_SOKOL_PRIVATE bool _saudio_backend_init(void) { + REFERENCE_TIME dur; + /* UWP Threads are CoInitialized by default with a different threading model, and this call fails + See https://github.com/Microsoft/cppwinrt/issues/6#issuecomment-253930637 */ + #if defined(_SAUDIO_WIN32) + /* CoInitializeEx could have been called elsewhere already, in which + case the function returns with S_FALSE (thus it does not make much + sense to check the result) + */ + HRESULT hr = CoInitializeEx(0, COINIT_MULTITHREADED); + _SOKOL_UNUSED(hr); + #endif + _saudio.backend.thread.buffer_end_event = CreateEvent(0, FALSE, FALSE, 0); + if (0 == _saudio.backend.thread.buffer_end_event) { + SOKOL_LOG("sokol_audio wasapi: failed to create buffer_end_event"); + goto error; + } + #if defined(_SAUDIO_UWP) + _saudio.backend.interface_activation_mutex = CreateMutexA(NULL, FALSE, "interface_activation_mutex"); + if (_saudio.backend.interface_activation_mutex == NULL) { + SOKOL_LOG("sokol_audio wasapi: failed to create interface activation mutex"); + goto error; + } + if (FAILED(StringFromIID(_SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_Devinterface_Audio_Render), &_saudio.backend.interface_activation_audio_interface_uid_string))) { + SOKOL_LOG("sokol_audio wasapi: failed to get default audio device ID string"); + goto error; + } + + /* static instance of the fake COM object */ + static IActivateAudioInterfaceCompletionHandlerVtbl completion_handler_interface_vtable = { + _saudio_interface_completion_handler_queryinterface, + _saudio_interface_completion_handler_addref_release, + _saudio_interface_completion_handler_addref_release, + _saudio_backend_activate_audio_interface_cb + }; + static IActivateAudioInterfaceCompletionHandler completion_handler_interface = { &completion_handler_interface_vtable }; + + if (FAILED(ActivateAudioInterfaceAsync(_saudio.backend.interface_activation_audio_interface_uid_string, _SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_IAudioClient), NULL, &completion_handler_interface, &_saudio.backend.interface_activation_operation))) { + SOKOL_LOG("sokol_audio wasapi: failed to get default audio device ID string"); + goto error; + } + while (!(_saudio.backend.audio_client)) { + if (WaitForSingleObject(_saudio.backend.interface_activation_mutex, 10) != WAIT_TIMEOUT) { + ReleaseMutex(_saudio.backend.interface_activation_mutex); + } + } + + if (!(_saudio.backend.interface_activation_success)) { + SOKOL_LOG("sokol_audio wasapi: interface activation failed. Unable to get audio client"); + goto error; + } + + #else + if (FAILED(CoCreateInstance(_SOKOL_AUDIO_WIN32COM_ID(_saudio_CLSID_IMMDeviceEnumerator), + 0, CLSCTX_ALL, + _SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_IMMDeviceEnumerator), + (void**)&_saudio.backend.device_enumerator))) + { + SOKOL_LOG("sokol_audio wasapi: failed to create device enumerator"); + goto error; + } + if (FAILED(IMMDeviceEnumerator_GetDefaultAudioEndpoint(_saudio.backend.device_enumerator, + eRender, eConsole, + &_saudio.backend.device))) + { + SOKOL_LOG("sokol_audio wasapi: GetDefaultAudioEndPoint failed"); + goto error; + } + if (FAILED(IMMDevice_Activate(_saudio.backend.device, + _SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_IAudioClient), + CLSCTX_ALL, 0, + (void**)&_saudio.backend.audio_client))) + { + SOKOL_LOG("sokol_audio wasapi: device activate failed"); + goto error; + } + #endif + WAVEFORMATEX fmt; + memset(&fmt, 0, sizeof(fmt)); + fmt.nChannels = (WORD)_saudio.num_channels; + fmt.nSamplesPerSec = (DWORD)_saudio.sample_rate; + fmt.wFormatTag = WAVE_FORMAT_PCM; + fmt.wBitsPerSample = 16; + fmt.nBlockAlign = (fmt.nChannels * fmt.wBitsPerSample) / 8; + fmt.nAvgBytesPerSec = fmt.nSamplesPerSec * fmt.nBlockAlign; + dur = (REFERENCE_TIME) + (((double)_saudio.buffer_frames) / (((double)_saudio.sample_rate) * (1.0/10000000.0))); + if (FAILED(IAudioClient_Initialize(_saudio.backend.audio_client, + AUDCLNT_SHAREMODE_SHARED, + AUDCLNT_STREAMFLAGS_EVENTCALLBACK|AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM|AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY, + dur, 0, &fmt, 0))) + { + SOKOL_LOG("sokol_audio wasapi: audio client initialize failed"); + goto error; + } + if (FAILED(IAudioClient_GetBufferSize(_saudio.backend.audio_client, &_saudio.backend.thread.dst_buffer_frames))) { + SOKOL_LOG("sokol_audio wasapi: audio client get buffer size failed"); + goto error; + } + if (FAILED(IAudioClient_GetService(_saudio.backend.audio_client, + _SOKOL_AUDIO_WIN32COM_ID(_saudio_IID_IAudioRenderClient), + (void**)&_saudio.backend.render_client))) + { + SOKOL_LOG("sokol_audio wasapi: audio client GetService failed"); + goto error; + } + if (FAILED(IAudioClient_SetEventHandle(_saudio.backend.audio_client, _saudio.backend.thread.buffer_end_event))) { + SOKOL_LOG("sokol_audio wasapi: audio client SetEventHandle failed"); + goto error; + } + _saudio.backend.si16_bytes_per_frame = _saudio.num_channels * (int)sizeof(int16_t); + _saudio.bytes_per_frame = _saudio.num_channels * (int)sizeof(float); + _saudio.backend.thread.src_buffer_frames = _saudio.buffer_frames; + _saudio.backend.thread.src_buffer_byte_size = _saudio.backend.thread.src_buffer_frames * _saudio.bytes_per_frame; + + /* allocate an intermediate buffer for sample format conversion */ + _saudio.backend.thread.src_buffer = (float*) SOKOL_MALLOC((size_t)_saudio.backend.thread.src_buffer_byte_size); + SOKOL_ASSERT(_saudio.backend.thread.src_buffer); + + /* create streaming thread */ + _saudio.backend.thread.thread_handle = CreateThread(NULL, 0, _saudio_wasapi_thread_fn, 0, 0, 0); + if (0 == _saudio.backend.thread.thread_handle) { + SOKOL_LOG("sokol_audio wasapi: CreateThread failed"); + goto error; + } + return true; +error: + _saudio_wasapi_release(); + return false; +} + +_SOKOL_PRIVATE void _saudio_backend_shutdown(void) { + if (_saudio.backend.thread.thread_handle) { + _saudio.backend.thread.stop = true; + SetEvent(_saudio.backend.thread.buffer_end_event); + WaitForSingleObject(_saudio.backend.thread.thread_handle, INFINITE); + CloseHandle(_saudio.backend.thread.thread_handle); + _saudio.backend.thread.thread_handle = 0; + } + if (_saudio.backend.audio_client) { + IAudioClient_Stop(_saudio.backend.audio_client); + } + _saudio_wasapi_release(); + + #if defined(_SAUDIO_WIN32) + CoUninitialize(); + #endif +} + +/*=== EMSCRIPTEN BACKEND IMPLEMENTATION ======================================*/ +#elif defined(_SAUDIO_EMSCRIPTEN) + +#ifdef __cplusplus +extern "C" { +#endif + +EMSCRIPTEN_KEEPALIVE int _saudio_emsc_pull(int num_frames) { + SOKOL_ASSERT(_saudio.backend.buffer); + if (num_frames == _saudio.buffer_frames) { + if (_saudio_has_callback()) { + _saudio_stream_callback((float*)_saudio.backend.buffer, num_frames, _saudio.num_channels); + } + else { + const int num_bytes = num_frames * _saudio.bytes_per_frame; + if (0 == _saudio_fifo_read(&_saudio.fifo, _saudio.backend.buffer, num_bytes)) { + /* not enough read data available, fill the entire buffer with silence */ + memset(_saudio.backend.buffer, 0, (size_t)num_bytes); + } + } + int res = (int) _saudio.backend.buffer; + return res; + } + else { + return 0; + } +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +/* setup the WebAudio context and attach a ScriptProcessorNode */ +EM_JS(int, saudio_js_init, (int sample_rate, int num_channels, int buffer_size), { + Module._saudio_context = null; + Module._saudio_node = null; + if (typeof AudioContext !== 'undefined') { + Module._saudio_context = new AudioContext({ + sampleRate: sample_rate, + latencyHint: 'interactive', + }); + } + else if (typeof webkitAudioContext !== 'undefined') { + Module._saudio_context = new webkitAudioContext({ + sampleRate: sample_rate, + latencyHint: 'interactive', + }); + } + else { + Module._saudio_context = null; + console.log('sokol_audio.h: no WebAudio support'); + } + if (Module._saudio_context) { + console.log('sokol_audio.h: sample rate ', Module._saudio_context.sampleRate); + Module._saudio_node = Module._saudio_context.createScriptProcessor(buffer_size, 0, num_channels); + Module._saudio_node.onaudioprocess = function pump_audio(event) { + var num_frames = event.outputBuffer.length; + var ptr = __saudio_emsc_pull(num_frames); + if (ptr) { + var num_channels = event.outputBuffer.numberOfChannels; + for (var chn = 0; chn < num_channels; chn++) { + var chan = event.outputBuffer.getChannelData(chn); + for (var i = 0; i < num_frames; i++) { + chan[i] = HEAPF32[(ptr>>2) + ((num_channels*i)+chn)] + } + } + } + }; + Module._saudio_node.connect(Module._saudio_context.destination); + + // in some browsers, WebAudio needs to be activated on a user action + var resume_webaudio = function() { + if (Module._saudio_context) { + if (Module._saudio_context.state === 'suspended') { + Module._saudio_context.resume(); + } + } + }; + document.addEventListener('click', resume_webaudio, {once:true}); + document.addEventListener('touchstart', resume_webaudio, {once:true}); + document.addEventListener('keydown', resume_webaudio, {once:true}); + return 1; + } + else { + return 0; + } +}); + +/* shutdown the WebAudioContext and ScriptProcessorNode */ +EM_JS(void, saudio_js_shutdown, (void), { + if (Module._saudio_context !== null) { + if (Module._saudio_node) { + Module._saudio_node.disconnect(); + } + Module._saudio_context.close(); + Module._saudio_context = null; + Module._saudio_node = null; + } +}); + +/* get the actual sample rate back from the WebAudio context */ +EM_JS(int, saudio_js_sample_rate, (void), { + if (Module._saudio_context) { + return Module._saudio_context.sampleRate; + } + else { + return 0; + } +}); + +/* get the actual buffer size in number of frames */ +EM_JS(int, saudio_js_buffer_frames, (void), { + if (Module._saudio_node) { + return Module._saudio_node.bufferSize; + } + else { + return 0; + } +}); + +_SOKOL_PRIVATE bool _saudio_backend_init(void) { + if (saudio_js_init(_saudio.sample_rate, _saudio.num_channels, _saudio.buffer_frames)) { + _saudio.bytes_per_frame = (int)sizeof(float) * _saudio.num_channels; + _saudio.sample_rate = saudio_js_sample_rate(); + _saudio.buffer_frames = saudio_js_buffer_frames(); + const size_t buf_size = (size_t) (_saudio.buffer_frames * _saudio.bytes_per_frame); + _saudio.backend.buffer = (uint8_t*) SOKOL_MALLOC(buf_size); + return true; + } + else { + return false; + } +} + +_SOKOL_PRIVATE void _saudio_backend_shutdown(void) { + saudio_js_shutdown(); + if (_saudio.backend.buffer) { + SOKOL_FREE(_saudio.backend.buffer); + _saudio.backend.buffer = 0; + } +} + +/*=== ANDROID BACKEND IMPLEMENTATION ======================================*/ +#elif defined(_SAUDIO_ANDROID) + +#ifdef __cplusplus +extern "C" { +#endif + +_SOKOL_PRIVATE void _saudio_semaphore_init(_saudio_semaphore_t* sem) { + sem->count = 0; + int r = pthread_mutex_init(&sem->mutex, NULL); + SOKOL_ASSERT(r == 0); + + r = pthread_cond_init(&sem->cond, NULL); + SOKOL_ASSERT(r == 0); + + (void)(r); +} + +_SOKOL_PRIVATE void _saudio_semaphore_destroy(_saudio_semaphore_t* sem) +{ + pthread_cond_destroy(&sem->cond); + pthread_mutex_destroy(&sem->mutex); +} + +_SOKOL_PRIVATE void _saudio_semaphore_post(_saudio_semaphore_t* sem, int count) +{ + int r = pthread_mutex_lock(&sem->mutex); + SOKOL_ASSERT(r == 0); + + for (int ii = 0; ii < count; ii++) { + r = pthread_cond_signal(&sem->cond); + SOKOL_ASSERT(r == 0); + } + + sem->count += count; + r = pthread_mutex_unlock(&sem->mutex); + SOKOL_ASSERT(r == 0); + + (void)(r); +} + +_SOKOL_PRIVATE bool _saudio_semaphore_wait(_saudio_semaphore_t* sem) +{ + int r = pthread_mutex_lock(&sem->mutex); + SOKOL_ASSERT(r == 0); + + while (r == 0 && sem->count <= 0) { + r = pthread_cond_wait(&sem->cond, &sem->mutex); + } + + bool ok = (r == 0); + if (ok) { + --sem->count; + } + r = pthread_mutex_unlock(&sem->mutex); + (void)(r); + return ok; +} + +/* fill intermediate buffer with new data and reset buffer_pos */ +_SOKOL_PRIVATE void _saudio_opensles_fill_buffer(void) { + int src_buffer_frames = _saudio.buffer_frames; + if (_saudio_has_callback()) { + _saudio_stream_callback(_saudio.backend.src_buffer, src_buffer_frames, _saudio.num_channels); + } + else { + const int src_buffer_byte_size = src_buffer_frames * _saudio.num_channels * (int)sizeof(float); + if (0 == _saudio_fifo_read(&_saudio.fifo, (uint8_t*)_saudio.backend.src_buffer, src_buffer_byte_size)) { + /* not enough read data available, fill the entire buffer with silence */ + memset(_saudio.backend.src_buffer, 0x0, (size_t)src_buffer_byte_size); + } + } +} + +_SOKOL_PRIVATE void SLAPIENTRY _saudio_opensles_play_cb(SLPlayItf player, void *context, SLuint32 event) { + (void)(context); + (void)(player); + + if (event & SL_PLAYEVENT_HEADATEND) { + _saudio_semaphore_post(&_saudio.backend.buffer_sem, 1); + } +} + +_SOKOL_PRIVATE void* _saudio_opensles_thread_fn(void* param) { + while (!_saudio.backend.thread_stop) { + /* get next output buffer, advance, next buffer. */ + int16_t* out_buffer = _saudio.backend.output_buffers[_saudio.backend.active_buffer]; + _saudio.backend.active_buffer = (_saudio.backend.active_buffer + 1) % SAUDIO_NUM_BUFFERS; + int16_t* next_buffer = _saudio.backend.output_buffers[_saudio.backend.active_buffer]; + + /* queue this buffer */ + const int buffer_size_bytes = _saudio.buffer_frames * _saudio.num_channels * (int)sizeof(short); + (*_saudio.backend.player_buffer_queue)->Enqueue(_saudio.backend.player_buffer_queue, out_buffer, (SLuint32)buffer_size_bytes); + + /* fill the next buffer */ + _saudio_opensles_fill_buffer(); + const int num_samples = _saudio.num_channels * _saudio.buffer_frames; + for (int i = 0; i < num_samples; ++i) { + next_buffer[i] = (int16_t) (_saudio.backend.src_buffer[i] * 0x7FFF); + } + + _saudio_semaphore_wait(&_saudio.backend.buffer_sem); + } + + return 0; +} + +_SOKOL_PRIVATE void _saudio_backend_shutdown(void) { + _saudio.backend.thread_stop = 1; + pthread_join(_saudio.backend.thread, 0); + + if (_saudio.backend.player_obj) { + (*_saudio.backend.player_obj)->Destroy(_saudio.backend.player_obj); + } + + if (_saudio.backend.output_mix_obj) { + (*_saudio.backend.output_mix_obj)->Destroy(_saudio.backend.output_mix_obj); + } + + if (_saudio.backend.engine_obj) { + (*_saudio.backend.engine_obj)->Destroy(_saudio.backend.engine_obj); + } + + for (int i = 0; i < SAUDIO_NUM_BUFFERS; i++) { + SOKOL_FREE(_saudio.backend.output_buffers[i]); + } + SOKOL_FREE(_saudio.backend.src_buffer); +} + +_SOKOL_PRIVATE bool _saudio_backend_init(void) { + _saudio.bytes_per_frame = (int)sizeof(float) * _saudio.num_channels; + + for (int i = 0; i < SAUDIO_NUM_BUFFERS; ++i) { + const int buffer_size_bytes = (int)sizeof(int16_t) * _saudio.num_channels * _saudio.buffer_frames; + _saudio.backend.output_buffers[i] = (int16_t*) SOKOL_MALLOC((size_t)buffer_size_bytes); + SOKOL_ASSERT(_saudio.backend.output_buffers[i]); + memset(_saudio.backend.output_buffers[i], 0x0, (size_t)buffer_size_bytes); + } + + { + const int buffer_size_bytes = _saudio.bytes_per_frame * _saudio.buffer_frames; + _saudio.backend.src_buffer = (float*) SOKOL_MALLOC((size_t)buffer_size_bytes); + SOKOL_ASSERT(_saudio.backend.src_buffer); + memset(_saudio.backend.src_buffer, 0x0, (size_t)buffer_size_bytes); + } + + /* Create engine */ + const SLEngineOption opts[] = { SL_ENGINEOPTION_THREADSAFE, SL_BOOLEAN_TRUE }; + if (slCreateEngine(&_saudio.backend.engine_obj, 1, opts, 0, NULL, NULL ) != SL_RESULT_SUCCESS) { + SOKOL_LOG("sokol_audio opensles: slCreateEngine failed"); + _saudio_backend_shutdown(); + return false; + } + + (*_saudio.backend.engine_obj)->Realize(_saudio.backend.engine_obj, SL_BOOLEAN_FALSE); + if ((*_saudio.backend.engine_obj)->GetInterface(_saudio.backend.engine_obj, SL_IID_ENGINE, &_saudio.backend.engine) != SL_RESULT_SUCCESS) { + SOKOL_LOG("sokol_audio opensles: GetInterface->Engine failed"); + _saudio_backend_shutdown(); + return false; + } + + /* Create output mix. */ + { + const SLInterfaceID ids[] = { SL_IID_VOLUME }; + const SLboolean req[] = { SL_BOOLEAN_FALSE }; + + if( (*_saudio.backend.engine)->CreateOutputMix(_saudio.backend.engine, &_saudio.backend.output_mix_obj, 1, ids, req) != SL_RESULT_SUCCESS) + { + SOKOL_LOG("sokol_audio opensles: CreateOutputMix failed"); + _saudio_backend_shutdown(); + return false; + } + (*_saudio.backend.output_mix_obj)->Realize(_saudio.backend.output_mix_obj, SL_BOOLEAN_FALSE); + + if((*_saudio.backend.output_mix_obj)->GetInterface(_saudio.backend.output_mix_obj, SL_IID_VOLUME, &_saudio.backend.output_mix_vol) != SL_RESULT_SUCCESS) { + SOKOL_LOG("sokol_audio opensles: GetInterface->OutputMixVol failed"); + } + } + + /* android buffer queue */ + _saudio.backend.in_locator.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE; + _saudio.backend.in_locator.numBuffers = SAUDIO_NUM_BUFFERS; + + /* data format */ + SLDataFormat_PCM format; + format.formatType = SL_DATAFORMAT_PCM; + format.numChannels = (SLuint32)_saudio.num_channels; + format.samplesPerSec = (SLuint32) (_saudio.sample_rate * 1000); + format.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; + format.containerSize = 16; + format.endianness = SL_BYTEORDER_LITTLEENDIAN; + + if (_saudio.num_channels == 2) { + format.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; + } else { + format.channelMask = SL_SPEAKER_FRONT_CENTER; + } + + SLDataSource src; + src.pLocator = &_saudio.backend.in_locator; + src.pFormat = &format; + + /* Output mix. */ + _saudio.backend.out_locator.locatorType = SL_DATALOCATOR_OUTPUTMIX; + _saudio.backend.out_locator.outputMix = _saudio.backend.output_mix_obj; + + _saudio.backend.dst_data_sink.pLocator = &_saudio.backend.out_locator; + _saudio.backend.dst_data_sink.pFormat = NULL; + + /* setup player */ + { + const SLInterfaceID ids[] = { SL_IID_VOLUME, SL_IID_ANDROIDSIMPLEBUFFERQUEUE }; + const SLboolean req[] = { SL_BOOLEAN_FALSE, SL_BOOLEAN_TRUE }; + + (*_saudio.backend.engine)->CreateAudioPlayer(_saudio.backend.engine, &_saudio.backend.player_obj, &src, &_saudio.backend.dst_data_sink, sizeof(ids) / sizeof(ids[0]), ids, req); + + (*_saudio.backend.player_obj)->Realize(_saudio.backend.player_obj, SL_BOOLEAN_FALSE); + + (*_saudio.backend.player_obj)->GetInterface(_saudio.backend.player_obj, SL_IID_PLAY, &_saudio.backend.player); + (*_saudio.backend.player_obj)->GetInterface(_saudio.backend.player_obj, SL_IID_VOLUME, &_saudio.backend.player_vol); + + (*_saudio.backend.player_obj)->GetInterface(_saudio.backend.player_obj, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &_saudio.backend.player_buffer_queue); + } + + /* begin */ + { + const int buffer_size_bytes = (int)sizeof(int16_t) * _saudio.num_channels * _saudio.buffer_frames; + (*_saudio.backend.player_buffer_queue)->Enqueue(_saudio.backend.player_buffer_queue, _saudio.backend.output_buffers[0], (SLuint32)buffer_size_bytes); + _saudio.backend.active_buffer = (_saudio.backend.active_buffer + 1) % SAUDIO_NUM_BUFFERS; + + (*_saudio.backend.player)->RegisterCallback(_saudio.backend.player, _saudio_opensles_play_cb, NULL); + (*_saudio.backend.player)->SetCallbackEventsMask(_saudio.backend.player, SL_PLAYEVENT_HEADATEND); + (*_saudio.backend.player)->SetPlayState(_saudio.backend.player, SL_PLAYSTATE_PLAYING); + } + + /* create the buffer-streaming start thread */ + if (0 != pthread_create(&_saudio.backend.thread, 0, _saudio_opensles_thread_fn, 0)) { + _saudio_backend_shutdown(); + return false; + } + + return true; +} + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#else +#error "unsupported platform" +#endif + +/*=== PUBLIC API FUNCTIONS ===================================================*/ +SOKOL_API_IMPL void saudio_setup(const saudio_desc* desc) { + SOKOL_ASSERT(!_saudio.valid); + SOKOL_ASSERT(desc); + memset(&_saudio, 0, sizeof(_saudio)); + _saudio.desc = *desc; + _saudio.stream_cb = desc->stream_cb; + _saudio.stream_userdata_cb = desc->stream_userdata_cb; + _saudio.user_data = desc->user_data; + _saudio.sample_rate = _saudio_def(_saudio.desc.sample_rate, _SAUDIO_DEFAULT_SAMPLE_RATE); + _saudio.buffer_frames = _saudio_def(_saudio.desc.buffer_frames, _SAUDIO_DEFAULT_BUFFER_FRAMES); + _saudio.packet_frames = _saudio_def(_saudio.desc.packet_frames, _SAUDIO_DEFAULT_PACKET_FRAMES); + _saudio.num_packets = _saudio_def(_saudio.desc.num_packets, _SAUDIO_DEFAULT_NUM_PACKETS); + _saudio.num_channels = _saudio_def(_saudio.desc.num_channels, 1); + _saudio_fifo_init_mutex(&_saudio.fifo); + if (_saudio_backend_init()) { + /* the backend might not support the requested exact buffer size, + make sure the actual buffer size is still a multiple of + the requested packet size + */ + if (0 != (_saudio.buffer_frames % _saudio.packet_frames)) { + SOKOL_LOG("sokol_audio.h: actual backend buffer size isn't multiple of requested packet size"); + _saudio_backend_shutdown(); + return; + } + SOKOL_ASSERT(_saudio.bytes_per_frame > 0); + _saudio_fifo_init(&_saudio.fifo, _saudio.packet_frames * _saudio.bytes_per_frame, _saudio.num_packets); + _saudio.valid = true; + } +} + +SOKOL_API_IMPL void saudio_shutdown(void) { + if (_saudio.valid) { + _saudio_backend_shutdown(); + _saudio_fifo_shutdown(&_saudio.fifo); + _saudio.valid = false; + } +} + +SOKOL_API_IMPL bool saudio_isvalid(void) { + return _saudio.valid; +} + +SOKOL_API_IMPL void* saudio_userdata(void) { + return _saudio.desc.user_data; +} + +SOKOL_API_IMPL saudio_desc saudio_query_desc(void) { + return _saudio.desc; +} + +SOKOL_API_IMPL int saudio_sample_rate(void) { + return _saudio.sample_rate; +} + +SOKOL_API_IMPL int saudio_buffer_frames(void) { + return _saudio.buffer_frames; +} + +SOKOL_API_IMPL int saudio_channels(void) { + return _saudio.num_channels; +} + +SOKOL_API_IMPL int saudio_expect(void) { + if (_saudio.valid) { + const int num_frames = _saudio_fifo_writable_bytes(&_saudio.fifo) / _saudio.bytes_per_frame; + return num_frames; + } + else { + return 0; + } +} + +SOKOL_API_IMPL int saudio_push(const float* frames, int num_frames) { + SOKOL_ASSERT(frames && (num_frames > 0)); + if (_saudio.valid) { + const int num_bytes = num_frames * _saudio.bytes_per_frame; + const int num_written = _saudio_fifo_write(&_saudio.fifo, (const uint8_t*)frames, num_bytes); + return num_written / _saudio.bytes_per_frame; + } + else { + return 0; + } +} + +#undef _saudio_def +#undef _saudio_def_flt + +#if defined(_SAUDIO_WINDOWS) +#ifdef _MSC_VER +#pragma warning(pop) +#endif +#endif + +#endif /* SOKOL_AUDIO_IMPL */ diff --git a/v_windows/v/old/thirdparty/sokol/sokol_gfx.h b/v_windows/v/old/thirdparty/sokol/sokol_gfx.h new file mode 100644 index 0000000..9049ae2 --- /dev/null +++ b/v_windows/v/old/thirdparty/sokol/sokol_gfx.h @@ -0,0 +1,15672 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_GFX_IMPL) +#define SOKOL_GFX_IMPL +#endif +#ifndef SOKOL_GFX_INCLUDED +/* + sokol_gfx.h -- simple 3D API wrapper + + Project URL: https://github.com/floooh/sokol + + Example code: https://github.com/floooh/sokol-samples + + Do this: + #define SOKOL_IMPL or + #define SOKOL_GFX_IMPL + before you include this file in *one* C or C++ file to create the + implementation. + + In the same place define one of the following to select the rendering + backend: + #define SOKOL_GLCORE33 + #define SOKOL_GLES2 + #define SOKOL_GLES3 + #define SOKOL_D3D11 + #define SOKOL_METAL + #define SOKOL_WGPU + #define SOKOL_DUMMY_BACKEND + + I.e. for the GL 3.3 Core Profile it should look like this: + + #include ... + #include ... + #define SOKOL_IMPL + #define SOKOL_GLCORE33 + #include "sokol_gfx.h" + + The dummy backend replaces the platform-specific backend code with empty + stub functions. This is useful for writing tests that need to run on the + command line. + + Optionally provide the following defines with your own implementations: + + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + SOKOL_MALLOC(s) - your own malloc function (default: malloc(s)) + SOKOL_FREE(p) - your own free function (default: free(p)) + SOKOL_LOG(msg) - your own logging function (default: puts(msg)) + SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) + SOKOL_GFX_API_DECL - public function declaration prefix (default: extern) + SOKOL_API_DECL - same as SOKOL_GFX_API_DECL + SOKOL_API_IMPL - public function implementation prefix (default: -) + SOKOL_TRACE_HOOKS - enable trace hook callbacks (search below for TRACE HOOKS) + SOKOL_EXTERNAL_GL_LOADER - indicates that you're using your own GL loader, in this case + sokol_gfx.h will not include any platform GL headers and disable + the integrated Win32 GL loader + + If sokol_gfx.h is compiled as a DLL, define the following before + including the declaration or implementation: + + SOKOL_DLL + + On Windows, SOKOL_DLL will define SOKOL_GFX_API_DECL as __declspec(dllexport) + or __declspec(dllimport) as needed. + + If you want to compile without deprecated structs and functions, + define: + + SOKOL_NO_DEPRECATED + + Optionally define the following to force debug checks and validations + even in release mode: + + SOKOL_DEBUG - by default this is defined if _DEBUG is defined + + sokol_gfx DOES NOT: + =================== + - create a window or the 3D-API context/device, you must do this + before sokol_gfx is initialized, and pass any required information + (like 3D device pointers) to the sokol_gfx initialization call + + - present the rendered frame, how this is done exactly usually depends + on how the window and 3D-API context/device was created + + - provide a unified shader language, instead 3D-API-specific shader + source-code or shader-bytecode must be provided (for the "official" + offline shader cross-compiler, see here: + https://github.com/floooh/sokol-tools/blob/master/docs/sokol-shdc.md) + + STEP BY STEP + ============ + --- to initialize sokol_gfx, after creating a window and a 3D-API + context/device, call: + + sg_setup(const sg_desc*) + + --- create resource objects (at least buffers, shaders and pipelines, + and optionally images and passes): + + sg_buffer sg_make_buffer(const sg_buffer_desc*) + sg_image sg_make_image(const sg_image_desc*) + sg_shader sg_make_shader(const sg_shader_desc*) + sg_pipeline sg_make_pipeline(const sg_pipeline_desc*) + sg_pass sg_make_pass(const sg_pass_desc*) + + --- start rendering to the default frame buffer with: + + sg_begin_default_pass(const sg_pass_action* action, int width, int height) + + ...or alternatively with: + + sg_begin_default_passf(const sg_pass_action* action, float width, float height) + + ...which takes the framebuffer width and height as float values. + + --- or start rendering to an offscreen framebuffer with: + + sg_begin_pass(sg_pass pass, const sg_pass_action* action) + + --- set the pipeline state for the next draw call with: + + sg_apply_pipeline(sg_pipeline pip) + + --- fill an sg_bindings struct with the resource bindings for the next + draw call (1..N vertex buffers, 0 or 1 index buffer, 0..N image objects + to use as textures each on the vertex-shader- and fragment-shader-stage + and then call + + sg_apply_bindings(const sg_bindings* bindings) + + to update the resource bindings + + --- optionally update shader uniform data with: + + sg_apply_uniforms(sg_shader_stage stage, int ub_index, const void* data, int num_bytes) + + --- kick off a draw call with: + + sg_draw(int base_element, int num_elements, int num_instances) + + The sg_draw() function unifies all the different ways to render primitives + in a single call (indexed vs non-indexed rendering, and instanced vs non-instanced + rendering). In case of indexed rendering, base_element and num_element specify + indices in the currently bound index buffer. In case of non-indexed rendering + base_element and num_elements specify vertices in the currently bound + vertex-buffer(s). To perform instanced rendering, the rendering pipeline + must be setup for instancing (see sg_pipeline_desc below), a separate vertex buffer + containing per-instance data must be bound, and the num_instances parameter + must be > 1. + + --- finish the current rendering pass with: + + sg_end_pass() + + --- when done with the current frame, call + + sg_commit() + + --- at the end of your program, shutdown sokol_gfx with: + + sg_shutdown() + + --- if you need to destroy resources before sg_shutdown(), call: + + sg_destroy_buffer(sg_buffer buf) + sg_destroy_image(sg_image img) + sg_destroy_shader(sg_shader shd) + sg_destroy_pipeline(sg_pipeline pip) + sg_destroy_pass(sg_pass pass) + + --- to set a new viewport rectangle, call + + sg_apply_viewport(int x, int y, int width, int height, bool origin_top_left) + + ...or if you want to specifiy the viewport rectangle with float values: + + sg_apply_viewportf(float x, float y, float width, float height, bool origin_top_left) + + --- to set a new scissor rect, call: + + sg_apply_scissor_rect(int x, int y, int width, int height, bool origin_top_left) + + ...or with float values: + + sg_apply_scissor_rectf(float x, float y, float width, float height, bool origin_top_left) + + Both sg_apply_viewport() and sg_apply_scissor_rect() must be called + inside a rendering pass + + Note that sg_begin_default_pass() and sg_begin_pass() will reset both the + viewport and scissor rectangles to cover the entire framebuffer. + + --- to update (overwrite) the content of buffer and image resources, call: + + sg_update_buffer(sg_buffer buf, const sg_range* data) + sg_update_image(sg_image img, const sg_image_data* data) + + Buffers and images to be updated must have been created with + SG_USAGE_DYNAMIC or SG_USAGE_STREAM + + Only one update per frame is allowed for buffer and image resources when + using the sg_update_*() functions. The rationale is to have a simple + countermeasure to avoid the CPU scribbling over data the GPU is currently + using, or the CPU having to wait for the GPU + + Buffer and image updates can be partial, as long as a rendering + operation only references the valid (updated) data in the + buffer or image. + + --- to append a chunk of data to a buffer resource, call: + + int sg_append_buffer(sg_buffer buf, const sg_range* data) + + The difference to sg_update_buffer() is that sg_append_buffer() + can be called multiple times per frame to append new data to the + buffer piece by piece, optionally interleaved with draw calls referencing + the previously written data. + + sg_append_buffer() returns a byte offset to the start of the + written data, this offset can be assigned to + sg_bindings.vertex_buffer_offsets[n] or + sg_bindings.index_buffer_offset + + Code example: + + for (...) { + const void* data = ...; + const int num_bytes = ...; + int offset = sg_append_buffer(buf, &(sg_range) { .ptr=data, .size=num_bytes }); + bindings.vertex_buffer_offsets[0] = offset; + sg_apply_pipeline(pip); + sg_apply_bindings(&bindings); + sg_apply_uniforms(...); + sg_draw(...); + } + + A buffer to be used with sg_append_buffer() must have been created + with SG_USAGE_DYNAMIC or SG_USAGE_STREAM. + + If the application appends more data to the buffer then fits into + the buffer, the buffer will go into the "overflow" state for the + rest of the frame. + + Any draw calls attempting to render an overflown buffer will be + silently dropped (in debug mode this will also result in a + validation error). + + You can also check manually if a buffer is in overflow-state by calling + + bool sg_query_buffer_overflow(sg_buffer buf) + + NOTE: Due to restrictions in underlying 3D-APIs, appended chunks of + data will be 4-byte aligned in the destination buffer. This means + that there will be gaps in index buffers containing 16-bit indices + when the number of indices in a call to sg_append_buffer() is + odd. This isn't a problem when each call to sg_append_buffer() + is associated with one draw call, but will be problematic when + a single indexed draw call spans several appended chunks of indices. + + --- to check at runtime for optional features, limits and pixelformat support, + call: + + sg_features sg_query_features() + sg_limits sg_query_limits() + sg_pixelformat_info sg_query_pixelformat(sg_pixel_format fmt) + + --- if you need to call into the underlying 3D-API directly, you must call: + + sg_reset_state_cache() + + ...before calling sokol_gfx functions again + + --- you can inspect the original sg_desc structure handed to sg_setup() + by calling sg_query_desc(). This will return an sg_desc struct with + the default values patched in instead of any zero-initialized values + + --- you can inspect various internal resource attributes via: + + sg_buffer_info sg_query_buffer_info(sg_buffer buf) + sg_image_info sg_query_image_info(sg_image img) + sg_shader_info sg_query_shader_info(sg_shader shd) + sg_pipeline_info sg_query_pipeline_info(sg_pipeline pip) + sg_pass_info sg_query_pass_info(sg_pass pass) + + ...please note that the returned info-structs are tied quite closely + to sokol_gfx.h internals, and may change more often than other + public API functions and structs. + + --- you can ask at runtime what backend sokol_gfx.h has been compiled + for, or whether the GLES3 backend had to fall back to GLES2 with: + + sg_backend sg_query_backend(void) + + --- you can query the default resource creation parameters through the functions + + sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc* desc) + sg_image_desc sg_query_image_defaults(const sg_image_desc* desc) + sg_shader_desc sg_query_shader_defaults(const sg_shader_desc* desc) + sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_desc* desc) + sg_pass_desc sg_query_pass_defaults(const sg_pass_desc* desc) + + These functions take a pointer to a desc structure which may contain + zero-initialized items for default values. These zero-init values + will be replaced with their concrete values in the returned desc + struct. + + ON INITIALIZATION: + ================== + When calling sg_setup(), a pointer to an sg_desc struct must be provided + which contains initialization options. These options provide two types + of information to sokol-gfx: + + (1) upper bounds and limits needed to allocate various internal + data structures: + - the max number of resources of each type that can + be alive at the same time, this is used for allocating + internal pools + - the max overall size of uniform data that can be + updated per frame, including a worst-case alignment + per uniform update (this worst-case alignment is 256 bytes) + - the max size of all dynamic resource updates (sg_update_buffer, + sg_append_buffer and sg_update_image) per frame + - the max number of entries in the texture sampler cache + (how many unique texture sampler can exist at the same time) + Not all of those limit values are used by all backends, but it is + good practice to provide them none-the-less. + + (2) 3D-API "context information" (sometimes also called "bindings"): + sokol_gfx.h doesn't create or initialize 3D API objects which are + closely related to the presentation layer (this includes the "rendering + device", the swapchain, and any objects which depend on the + swapchain). These API objects (or callback functions to obtain + them, if those objects might change between frames), must + be provided in a nested sg_context_desc struct inside the + sg_desc struct. If sokol_gfx.h is used together with + sokol_app.h, have a look at the sokol_glue.h header which provides + a convenience function to get a sg_context_desc struct filled out + with context information provided by sokol_app.h + + See the documention block of the sg_desc struct below for more information. + + BACKEND-SPECIFIC TOPICS: + ======================== + --- the GL backends need to know about the internal structure of uniform + blocks, and the texture sampler-name and -type: + + typedef struct { + float mvp[16]; // model-view-projection matrix + float offset0[2]; // some 2D vectors + float offset1[2]; + float offset2[2]; + } params_t; + + // uniform block structure and texture image definition in sg_shader_desc: + sg_shader_desc desc = { + // uniform block description (size and internal structure) + .vs.uniform_blocks[0] = { + .size = sizeof(params_t), + .uniforms = { + [0] = { .name="mvp", .type=SG_UNIFORMTYPE_MAT4 }, + [1] = { .name="offset0", .type=SG_UNIFORMTYPE_VEC2 }, + ... + } + }, + // one texture on the fragment-shader-stage, GLES2/WebGL needs name and image type + .fs.images[0] = { .name="tex", .type=SG_IMAGETYPE_ARRAY } + ... + }; + + --- the Metal and D3D11 backends only need to know the size of uniform blocks, + not their internal member structure, and they only need to know + the type of a texture sampler, not its name: + + sg_shader_desc desc = { + .vs.uniform_blocks[0].size = sizeof(params_t), + .fs.images[0].type = SG_IMAGETYPE_ARRAY, + ... + }; + + --- when creating a shader object, GLES2/WebGL need to know the vertex + attribute names as used in the vertex shader: + + sg_shader_desc desc = { + .attrs = { + [0] = { .name="position" }, + [1] = { .name="color1" } + } + }; + + The vertex attribute names provided when creating a shader will be + used later in sg_create_pipeline() for matching the vertex layout + to vertex shader inputs. + + --- on D3D11 you need to provide a semantic name and semantic index in the + shader description struct instead (see the D3D11 documentation on + D3D11_INPUT_ELEMENT_DESC for details): + + sg_shader_desc desc = { + .attrs = { + [0] = { .sem_name="POSITION", .sem_index=0 } + [1] = { .sem_name="COLOR", .sem_index=1 } + } + }; + + The provided semantic information will be used later in sg_create_pipeline() + to match the vertex layout to vertex shader inputs. + + --- on D3D11, and when passing HLSL source code (instead of byte code) to shader + creation, you can optionally define the shader model targets on the vertex + stage: + + sg_shader_Desc desc = { + .vs = { + ... + .d3d11_target = "vs_5_0" + }, + .fs = { + ... + .d3d11_target = "ps_5_0" + } + }; + + The default targets are "ps_4_0" and "fs_4_0". Note that those target names + are only used when compiling shaders from source. They are ignored when + creating a shader from bytecode. + + --- on Metal, GL 3.3 or GLES3/WebGL2, you don't need to provide an attribute + name or semantic name, since vertex attributes can be bound by their slot index + (this is mandatory in Metal, and optional in GL): + + sg_pipeline_desc desc = { + .layout = { + .attrs = { + [0] = { .format=SG_VERTEXFORMAT_FLOAT3 }, + [1] = { .format=SG_VERTEXFORMAT_FLOAT4 } + } + } + }; + + WORKING WITH CONTEXTS + ===================== + sokol-gfx allows to switch between different rendering contexts and + associate resource objects with contexts. This is useful to + create GL applications that render into multiple windows. + + A rendering context keeps track of all resources created while + the context is active. When the context is destroyed, all resources + "belonging to the context" are destroyed as well. + + A default context will be created and activated implicitly in + sg_setup(), and destroyed in sg_shutdown(). So for a typical application + which *doesn't* use multiple contexts, nothing changes, and calling + the context functions isn't necessary. + + Three functions have been added to work with contexts: + + --- sg_context sg_setup_context(): + This must be called once after a GL context has been created and + made active. + + --- void sg_activate_context(sg_context ctx) + This must be called after making a different GL context active. + Apart from 3D-API-specific actions, the call to sg_activate_context() + will internally call sg_reset_state_cache(). + + --- void sg_discard_context(sg_context ctx) + This must be called right before a GL context is destroyed and + will destroy all resources associated with the context (that + have been created while the context was active) The GL context must be + active at the time sg_discard_context(sg_context ctx) is called. + + Also note that resources (buffers, images, shaders and pipelines) must + only be used or destroyed while the same GL context is active that + was also active while the resource was created (an exception is + resource sharing on GL, such resources can be used while + another context is active, but must still be destroyed under + the same context that was active during creation). + + For more information, check out the multiwindow-glfw sample: + + https://github.com/floooh/sokol-samples/blob/master/glfw/multiwindow-glfw.c + + TRACE HOOKS: + ============ + sokol_gfx.h optionally allows to install "trace hook" callbacks for + each public API functions. When a public API function is called, and + a trace hook callback has been installed for this function, the + callback will be invoked with the parameters and result of the function. + This is useful for things like debugging- and profiling-tools, or + keeping track of resource creation and destruction. + + To use the trace hook feature: + + --- Define SOKOL_TRACE_HOOKS before including the implementation. + + --- Setup an sg_trace_hooks structure with your callback function + pointers (keep all function pointers you're not interested + in zero-initialized), optionally set the user_data member + in the sg_trace_hooks struct. + + --- Install the trace hooks by calling sg_install_trace_hooks(), + the return value of this function is another sg_trace_hooks + struct which contains the previously set of trace hooks. + You should keep this struct around, and call those previous + functions pointers from your own trace callbacks for proper + chaining. + + As an example of how trace hooks are used, have a look at the + imgui/sokol_gfx_imgui.h header which implements a realtime + debugging UI for sokol_gfx.h on top of Dear ImGui. + + A NOTE ON PORTABLE PACKED VERTEX FORMATS: + ========================================= + There are two things to consider when using packed + vertex formats like UBYTE4, SHORT2, etc which need to work + across all backends: + + - D3D11 can only convert *normalized* vertex formats to + floating point during vertex fetch, normalized formats + have a trailing 'N', and are "normalized" to a range + -1.0..+1.0 (for the signed formats) or 0.0..1.0 (for the + unsigned formats): + + - SG_VERTEXFORMAT_BYTE4N + - SG_VERTEXFORMAT_UBYTE4N + - SG_VERTEXFORMAT_SHORT2N + - SG_VERTEXFORMAT_USHORT2N + - SG_VERTEXFORMAT_SHORT4N + - SG_VERTEXFORMAT_USHORT4N + + D3D11 will not convert *non-normalized* vertex formats to floating point + vertex shader inputs, those can only be uses with the *ivecn* vertex shader + input types when D3D11 is used as backend (GL and Metal can use both formats) + + - SG_VERTEXFORMAT_BYTE4, + - SG_VERTEXFORMAT_UBYTE4 + - SG_VERTEXFORMAT_SHORT2 + - SG_VERTEXFORMAT_SHORT4 + + - WebGL/GLES2 cannot use integer vertex shader inputs (int or ivecn) + + - SG_VERTEXFORMAT_UINT10_N2 is not supported on WebGL/GLES2 + + So for a vertex input layout which works on all platforms, only use the following + vertex formats, and if needed "expand" the normalized vertex shader + inputs in the vertex shader by multiplying with 127.0, 255.0, 32767.0 or + 65535.0: + + - SG_VERTEXFORMAT_FLOAT, + - SG_VERTEXFORMAT_FLOAT2, + - SG_VERTEXFORMAT_FLOAT3, + - SG_VERTEXFORMAT_FLOAT4, + - SG_VERTEXFORMAT_BYTE4N, + - SG_VERTEXFORMAT_UBYTE4N, + - SG_VERTEXFORMAT_SHORT2N, + - SG_VERTEXFORMAT_USHORT2N + - SG_VERTEXFORMAT_SHORT4N, + - SG_VERTEXFORMAT_USHORT4N + + TODO: + ==== + - talk about asynchronous resource creation + + zlib/libpng license + + Copyright (c) 2018 Andre Weissflog + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software in a + product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ +#define SOKOL_GFX_INCLUDED (1) +#include // size_t +#include +#include + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_GFX_API_DECL) +#define SOKOL_GFX_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_GFX_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_GFX_IMPL) +#define SOKOL_GFX_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_GFX_API_DECL __declspec(dllimport) +#else +#define SOKOL_GFX_API_DECL extern +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* + Resource id typedefs: + + sg_buffer: vertex- and index-buffers + sg_image: textures and render targets + sg_shader: vertex- and fragment-shaders, uniform blocks + sg_pipeline: associated shader and vertex-layouts, and render states + sg_pass: a bundle of render targets and actions on them + sg_context: a 'context handle' for switching between 3D-API contexts + + Instead of pointers, resource creation functions return a 32-bit + number which uniquely identifies the resource object. + + The 32-bit resource id is split into a 16-bit pool index in the lower bits, + and a 16-bit 'unique counter' in the upper bits. The index allows fast + pool lookups, and combined with the unique-mask it allows to detect + 'dangling accesses' (trying to use an object which no longer exists, and + its pool slot has been reused for a new object) + + The resource ids are wrapped into a struct so that the compiler + can complain when the wrong resource type is used. +*/ +typedef struct sg_buffer { uint32_t id; } sg_buffer; +typedef struct sg_image { uint32_t id; } sg_image; +typedef struct sg_shader { uint32_t id; } sg_shader; +typedef struct sg_pipeline { uint32_t id; } sg_pipeline; +typedef struct sg_pass { uint32_t id; } sg_pass; +typedef struct sg_context { uint32_t id; } sg_context; + +/* + sg_range is a pointer-size-pair struct used to pass memory blobs into + sokol-gfx. When initialized from a value type (array or struct), you can + use the SG_RANGE() macro to build an sg_range struct. For functions which + take either a sg_range pointer, or a (C++) sg_range reference, use the + SG_RANGE_REF macro as a solution which compiles both in C and C++. +*/ +typedef struct sg_range { + const void* ptr; + size_t size; +} sg_range; + +// disabling this for every includer isn't great, but the warnings are also quite pointless +#if defined(_MSC_VER) +#pragma warning(disable:4221) /* /W4 only: nonstandard extension used: 'x': cannot be initialized using address of automatic variable 'y' */ +#pragma warning(disable:4204) /* VS2015: nonstandard extension used: non-constant aggregate initializer */ +#endif +#if defined(__cplusplus) +#define SG_RANGE(x) sg_range{ &x, sizeof(x) } +#define SG_RANGE_REF(x) sg_range{ &x, sizeof(x) } +#else +#define SG_RANGE(x) (sg_range){ &x, sizeof(x) } +#define SG_RANGE_REF(x) &(sg_range){ &x, sizeof(x) } +#endif + +// various compile-time constants +enum { + SG_INVALID_ID = 0, + SG_NUM_SHADER_STAGES = 2, + SG_NUM_INFLIGHT_FRAMES = 2, + SG_MAX_COLOR_ATTACHMENTS = 4, + SG_MAX_SHADERSTAGE_BUFFERS = 8, + SG_MAX_SHADERSTAGE_IMAGES = 12, + SG_MAX_SHADERSTAGE_UBS = 4, + SG_MAX_UB_MEMBERS = 16, + SG_MAX_VERTEX_ATTRIBUTES = 16, /* NOTE: actual max vertex attrs can be less on GLES2, see sg_limits! */ + SG_MAX_MIPMAPS = 16, + SG_MAX_TEXTUREARRAY_LAYERS = 128 +}; + +/* + sg_color + + An RGBA color value. +*/ +typedef struct sg_color { float r, g, b, a; } sg_color; + +/* + sg_backend + + The active 3D-API backend, use the function sg_query_backend() + to get the currently active backend. + + NOTE that SG_BACKEND_GLES2 will be returned if sokol-gfx was + compiled with SOKOL_GLES3, but the runtime platform doesn't support + GLES3/WebGL2 and sokol-gfx had to fallback to GLES2/WebGL. +*/ +typedef enum sg_backend { + SG_BACKEND_GLCORE33, + SG_BACKEND_GLES2, + SG_BACKEND_GLES3, + SG_BACKEND_D3D11, + SG_BACKEND_METAL_IOS, + SG_BACKEND_METAL_MACOS, + SG_BACKEND_METAL_SIMULATOR, + SG_BACKEND_WGPU, + SG_BACKEND_DUMMY, +} sg_backend; + +/* + sg_pixel_format + + sokol_gfx.h basically uses the same pixel formats as WebGPU, since these + are supported on most newer GPUs. GLES2 and WebGL only supports a much + smaller subset of actually available pixel formats. Call + sg_query_pixelformat() to check at runtime if a pixel format supports the + desired features. + + A pixelformat name consist of three parts: + + - components (R, RG, RGB or RGBA) + - bit width per component (8, 16 or 32) + - component data type: + - unsigned normalized (no postfix) + - signed normalized (SN postfix) + - unsigned integer (UI postfix) + - signed integer (SI postfix) + - float (F postfix) + + Not all pixel formats can be used for everything, call sg_query_pixelformat() + to inspect the capabilities of a given pixelformat. The function returns + an sg_pixelformat_info struct with the following bool members: + + - sample: the pixelformat can be sampled as texture at least with + nearest filtering + - filter: the pixelformat can be samples as texture with linear + filtering + - render: the pixelformat can be used for render targets + - blend: blending is supported when using the pixelformat for + render targets + - msaa: multisample-antialiasing is supported when using the + pixelformat for render targets + - depth: the pixelformat can be used for depth-stencil attachments + + When targeting GLES2/WebGL, the only safe formats to use + as texture are SG_PIXELFORMAT_R8 and SG_PIXELFORMAT_RGBA8. For rendering + in GLES2/WebGL, only SG_PIXELFORMAT_RGBA8 is safe. All other formats + must be checked via sg_query_pixelformats(). + + The default pixel format for texture images is SG_PIXELFORMAT_RGBA8. + + The default pixel format for render target images is platform-dependent: + - for Metal and D3D11 it is SG_PIXELFORMAT_BGRA8 + - for GL backends it is SG_PIXELFORMAT_RGBA8 + + This is mainly because of the default framebuffer which is setup outside + of sokol_gfx.h. On some backends, using BGRA for the default frame buffer + allows more efficient frame flips. For your own offscreen-render-targets, + use whatever renderable pixel format is convenient for you. +*/ +typedef enum sg_pixel_format { + _SG_PIXELFORMAT_DEFAULT, /* value 0 reserved for default-init */ + SG_PIXELFORMAT_NONE, + + SG_PIXELFORMAT_R8, + SG_PIXELFORMAT_R8SN, + SG_PIXELFORMAT_R8UI, + SG_PIXELFORMAT_R8SI, + + SG_PIXELFORMAT_R16, + SG_PIXELFORMAT_R16SN, + SG_PIXELFORMAT_R16UI, + SG_PIXELFORMAT_R16SI, + SG_PIXELFORMAT_R16F, + SG_PIXELFORMAT_RG8, + SG_PIXELFORMAT_RG8SN, + SG_PIXELFORMAT_RG8UI, + SG_PIXELFORMAT_RG8SI, + + SG_PIXELFORMAT_R32UI, + SG_PIXELFORMAT_R32SI, + SG_PIXELFORMAT_R32F, + SG_PIXELFORMAT_RG16, + SG_PIXELFORMAT_RG16SN, + SG_PIXELFORMAT_RG16UI, + SG_PIXELFORMAT_RG16SI, + SG_PIXELFORMAT_RG16F, + SG_PIXELFORMAT_RGBA8, + SG_PIXELFORMAT_RGBA8SN, + SG_PIXELFORMAT_RGBA8UI, + SG_PIXELFORMAT_RGBA8SI, + SG_PIXELFORMAT_BGRA8, + SG_PIXELFORMAT_RGB10A2, + SG_PIXELFORMAT_RG11B10F, + + SG_PIXELFORMAT_RG32UI, + SG_PIXELFORMAT_RG32SI, + SG_PIXELFORMAT_RG32F, + SG_PIXELFORMAT_RGBA16, + SG_PIXELFORMAT_RGBA16SN, + SG_PIXELFORMAT_RGBA16UI, + SG_PIXELFORMAT_RGBA16SI, + SG_PIXELFORMAT_RGBA16F, + + SG_PIXELFORMAT_RGBA32UI, + SG_PIXELFORMAT_RGBA32SI, + SG_PIXELFORMAT_RGBA32F, + + SG_PIXELFORMAT_DEPTH, + SG_PIXELFORMAT_DEPTH_STENCIL, + + SG_PIXELFORMAT_BC1_RGBA, + SG_PIXELFORMAT_BC2_RGBA, + SG_PIXELFORMAT_BC3_RGBA, + SG_PIXELFORMAT_BC4_R, + SG_PIXELFORMAT_BC4_RSN, + SG_PIXELFORMAT_BC5_RG, + SG_PIXELFORMAT_BC5_RGSN, + SG_PIXELFORMAT_BC6H_RGBF, + SG_PIXELFORMAT_BC6H_RGBUF, + SG_PIXELFORMAT_BC7_RGBA, + SG_PIXELFORMAT_PVRTC_RGB_2BPP, + SG_PIXELFORMAT_PVRTC_RGB_4BPP, + SG_PIXELFORMAT_PVRTC_RGBA_2BPP, + SG_PIXELFORMAT_PVRTC_RGBA_4BPP, + SG_PIXELFORMAT_ETC2_RGB8, + SG_PIXELFORMAT_ETC2_RGB8A1, + SG_PIXELFORMAT_ETC2_RGBA8, + SG_PIXELFORMAT_ETC2_RG11, + SG_PIXELFORMAT_ETC2_RG11SN, + + _SG_PIXELFORMAT_NUM, + _SG_PIXELFORMAT_FORCE_U32 = 0x7FFFFFFF +} sg_pixel_format; + +/* + Runtime information about a pixel format, returned + by sg_query_pixelformat(). +*/ +typedef struct sg_pixelformat_info { + bool sample; // pixel format can be sampled in shaders + bool filter; // pixel format can be sampled with filtering + bool render; // pixel format can be used as render target + bool blend; // alpha-blending is supported + bool msaa; // pixel format can be used as MSAA render target + bool depth; // pixel format is a depth format + #if defined(SOKOL_ZIG_BINDINGS) + uint32_t __pad[3]; + #endif +} sg_pixelformat_info; + +/* + Runtime information about available optional features, + returned by sg_query_features() +*/ +typedef struct sg_features { + bool instancing; // hardware instancing supported + bool origin_top_left; // framebuffer and texture origin is in top left corner + bool multiple_render_targets; // offscreen render passes can have multiple render targets attached + bool msaa_render_targets; // offscreen render passes support MSAA antialiasing + bool imagetype_3d; // creation of SG_IMAGETYPE_3D images is supported + bool imagetype_array; // creation of SG_IMAGETYPE_ARRAY images is supported + bool image_clamp_to_border; // border color and clamp-to-border UV-wrap mode is supported + bool mrt_independent_blend_state; // multiple-render-target rendering can use per-render-target blend state + bool mrt_independent_write_mask; // multiple-render-target rendering can use per-render-target color write masks + #if defined(SOKOL_ZIG_BINDINGS) + uint32_t __pad[3]; + #endif +} sg_features; + +/* + Runtime information about resource limits, returned by sg_query_limit() +*/ +typedef struct sg_limits { + int max_image_size_2d; // max width/height of SG_IMAGETYPE_2D images + int max_image_size_cube; // max width/height of SG_IMAGETYPE_CUBE images + int max_image_size_3d; // max width/height/depth of SG_IMAGETYPE_3D images + int max_image_size_array; // max width/height of SG_IMAGETYPE_ARRAY images + int max_image_array_layers; // max number of layers in SG_IMAGETYPE_ARRAY images + int max_vertex_attrs; // <= SG_MAX_VERTEX_ATTRIBUTES (only on some GLES2 impls) +} sg_limits; + +/* + sg_resource_state + + The current state of a resource in its resource pool. + Resources start in the INITIAL state, which means the + pool slot is unoccupied and can be allocated. When a resource is + created, first an id is allocated, and the resource pool slot + is set to state ALLOC. After allocation, the resource is + initialized, which may result in the VALID or FAILED state. The + reason why allocation and initialization are separate is because + some resource types (e.g. buffers and images) might be asynchronously + initialized by the user application. If a resource which is not + in the VALID state is attempted to be used for rendering, rendering + operations will silently be dropped. + + The special INVALID state is returned in sg_query_xxx_state() if no + resource object exists for the provided resource id. +*/ +typedef enum sg_resource_state { + SG_RESOURCESTATE_INITIAL, + SG_RESOURCESTATE_ALLOC, + SG_RESOURCESTATE_VALID, + SG_RESOURCESTATE_FAILED, + SG_RESOURCESTATE_INVALID, + _SG_RESOURCESTATE_FORCE_U32 = 0x7FFFFFFF +} sg_resource_state; + +/* + sg_usage + + A resource usage hint describing the update strategy of + buffers and images. This is used in the sg_buffer_desc.usage + and sg_image_desc.usage members when creating buffers + and images: + + SG_USAGE_IMMUTABLE: the resource will never be updated with + new data, instead the content of the + resource must be provided on creation + SG_USAGE_DYNAMIC: the resource will be updated infrequently + with new data (this could range from "once + after creation", to "quite often but not + every frame") + SG_USAGE_STREAM: the resource will be updated each frame + with new content + + The rendering backends use this hint to prevent that the + CPU needs to wait for the GPU when attempting to update + a resource that might be currently accessed by the GPU. + + Resource content is updated with the functions sg_update_buffer() or + sg_append_buffer() for buffer objects, and sg_update_image() for image + objects. For the sg_update_*() functions, only one update is allowed per + frame and resource object, while sg_append_buffer() can be called + multiple times per frame on the same buffer. The application must update + all data required for rendering (this means that the update data can be + smaller than the resource size, if only a part of the overall resource + size is used for rendering, you only need to make sure that the data that + *is* used is valid). + + The default usage is SG_USAGE_IMMUTABLE. +*/ +typedef enum sg_usage { + _SG_USAGE_DEFAULT, /* value 0 reserved for default-init */ + SG_USAGE_IMMUTABLE, + SG_USAGE_DYNAMIC, + SG_USAGE_STREAM, + _SG_USAGE_NUM, + _SG_USAGE_FORCE_U32 = 0x7FFFFFFF +} sg_usage; + +/* + sg_buffer_type + + This indicates whether a buffer contains vertex- or index-data, + used in the sg_buffer_desc.type member when creating a buffer. + + The default value is SG_BUFFERTYPE_VERTEXBUFFER. +*/ +typedef enum sg_buffer_type { + _SG_BUFFERTYPE_DEFAULT, /* value 0 reserved for default-init */ + SG_BUFFERTYPE_VERTEXBUFFER, + SG_BUFFERTYPE_INDEXBUFFER, + _SG_BUFFERTYPE_NUM, + _SG_BUFFERTYPE_FORCE_U32 = 0x7FFFFFFF +} sg_buffer_type; + +/* + sg_index_type + + Indicates whether indexed rendering (fetching vertex-indices from an + index buffer) is used, and if yes, the index data type (16- or 32-bits). + This is used in the sg_pipeline_desc.index_type member when creating a + pipeline object. + + The default index type is SG_INDEXTYPE_NONE. +*/ +typedef enum sg_index_type { + _SG_INDEXTYPE_DEFAULT, /* value 0 reserved for default-init */ + SG_INDEXTYPE_NONE, + SG_INDEXTYPE_UINT16, + SG_INDEXTYPE_UINT32, + _SG_INDEXTYPE_NUM, + _SG_INDEXTYPE_FORCE_U32 = 0x7FFFFFFF +} sg_index_type; + +/* + sg_image_type + + Indicates the basic type of an image object (2D-texture, cubemap, + 3D-texture or 2D-array-texture). 3D- and array-textures are not supported + on the GLES2/WebGL backend (use sg_query_features().imagetype_3d and + sg_query_features().imagetype_array to check for support). The image type + is used in the sg_image_desc.type member when creating an image, and + in sg_shader_image_desc when describing a shader's texture sampler binding. + + The default image type when creating an image is SG_IMAGETYPE_2D. +*/ +typedef enum sg_image_type { + _SG_IMAGETYPE_DEFAULT, /* value 0 reserved for default-init */ + SG_IMAGETYPE_2D, + SG_IMAGETYPE_CUBE, + SG_IMAGETYPE_3D, + SG_IMAGETYPE_ARRAY, + _SG_IMAGETYPE_NUM, + _SG_IMAGETYPE_FORCE_U32 = 0x7FFFFFFF +} sg_image_type; + +/* + sg_sampler_type + + Indicates the basic data type of a shader's texture sampler which + can be float , unsigned integer or signed integer. The sampler + type is used in the sg_shader_image_desc to describe the + sampler type of a shader's texture sampler binding. + + The default sampler type is SG_SAMPLERTYPE_FLOAT. +*/ +typedef enum sg_sampler_type { + _SG_SAMPLERTYPE_DEFAULT, /* value 0 reserved for default-init */ + SG_SAMPLERTYPE_FLOAT, + SG_SAMPLERTYPE_SINT, + SG_SAMPLERTYPE_UINT, +} sg_sampler_type; + +/* + sg_cube_face + + The cubemap faces. Use these as indices in the sg_image_desc.content + array. +*/ +typedef enum sg_cube_face { + SG_CUBEFACE_POS_X, + SG_CUBEFACE_NEG_X, + SG_CUBEFACE_POS_Y, + SG_CUBEFACE_NEG_Y, + SG_CUBEFACE_POS_Z, + SG_CUBEFACE_NEG_Z, + SG_CUBEFACE_NUM, + _SG_CUBEFACE_FORCE_U32 = 0x7FFFFFFF +} sg_cube_face; + +/* + sg_shader_stage + + There are 2 shader stages: vertex- and fragment-shader-stage. + Each shader stage consists of: + + - one slot for a shader function (provided as source- or byte-code) + - SG_MAX_SHADERSTAGE_UBS slots for uniform blocks + - SG_MAX_SHADERSTAGE_IMAGES slots for images used as textures by + the shader function +*/ +typedef enum sg_shader_stage { + SG_SHADERSTAGE_VS, + SG_SHADERSTAGE_FS, + _SG_SHADERSTAGE_FORCE_U32 = 0x7FFFFFFF +} sg_shader_stage; + +/* + sg_primitive_type + + This is the common subset of 3D primitive types supported across all 3D + APIs. This is used in the sg_pipeline_desc.primitive_type member when + creating a pipeline object. + + The default primitive type is SG_PRIMITIVETYPE_TRIANGLES. +*/ +typedef enum sg_primitive_type { + _SG_PRIMITIVETYPE_DEFAULT, /* value 0 reserved for default-init */ + SG_PRIMITIVETYPE_POINTS, + SG_PRIMITIVETYPE_LINES, + SG_PRIMITIVETYPE_LINE_STRIP, + SG_PRIMITIVETYPE_TRIANGLES, + SG_PRIMITIVETYPE_TRIANGLE_STRIP, + _SG_PRIMITIVETYPE_NUM, + _SG_PRIMITIVETYPE_FORCE_U32 = 0x7FFFFFFF +} sg_primitive_type; + +/* + sg_filter + + The filtering mode when sampling a texture image. This is + used in the sg_image_desc.min_filter and sg_image_desc.mag_filter + members when creating an image object. + + The default filter mode is SG_FILTER_NEAREST. +*/ +typedef enum sg_filter { + _SG_FILTER_DEFAULT, /* value 0 reserved for default-init */ + SG_FILTER_NEAREST, + SG_FILTER_LINEAR, + SG_FILTER_NEAREST_MIPMAP_NEAREST, + SG_FILTER_NEAREST_MIPMAP_LINEAR, + SG_FILTER_LINEAR_MIPMAP_NEAREST, + SG_FILTER_LINEAR_MIPMAP_LINEAR, + _SG_FILTER_NUM, + _SG_FILTER_FORCE_U32 = 0x7FFFFFFF +} sg_filter; + +/* + sg_wrap + + The texture coordinates wrapping mode when sampling a texture + image. This is used in the sg_image_desc.wrap_u, .wrap_v + and .wrap_w members when creating an image. + + The default wrap mode is SG_WRAP_REPEAT. + + NOTE: SG_WRAP_CLAMP_TO_BORDER is not supported on all backends + and platforms. To check for support, call sg_query_features() + and check the "clamp_to_border" boolean in the returned + sg_features struct. + + Platforms which don't support SG_WRAP_CLAMP_TO_BORDER will silently fall back + to SG_WRAP_CLAMP_TO_EDGE without a validation error. + + Platforms which support clamp-to-border are: + + - all desktop GL platforms + - Metal on macOS + - D3D11 + + Platforms which do not support clamp-to-border: + + - GLES2/3 and WebGL/WebGL2 + - Metal on iOS +*/ +typedef enum sg_wrap { + _SG_WRAP_DEFAULT, /* value 0 reserved for default-init */ + SG_WRAP_REPEAT, + SG_WRAP_CLAMP_TO_EDGE, + SG_WRAP_CLAMP_TO_BORDER, + SG_WRAP_MIRRORED_REPEAT, + _SG_WRAP_NUM, + _SG_WRAP_FORCE_U32 = 0x7FFFFFFF +} sg_wrap; + +/* + sg_border_color + + The border color to use when sampling a texture, and the UV wrap + mode is SG_WRAP_CLAMP_TO_BORDER. + + The default border color is SG_BORDERCOLOR_OPAQUE_BLACK +*/ +typedef enum sg_border_color { + _SG_BORDERCOLOR_DEFAULT, /* value 0 reserved for default-init */ + SG_BORDERCOLOR_TRANSPARENT_BLACK, + SG_BORDERCOLOR_OPAQUE_BLACK, + SG_BORDERCOLOR_OPAQUE_WHITE, + _SG_BORDERCOLOR_NUM, + _SG_BORDERCOLOR_FORCE_U32 = 0x7FFFFFFF +} sg_border_color; + +/* + sg_vertex_format + + The data type of a vertex component. This is used to describe + the layout of vertex data when creating a pipeline object. +*/ +typedef enum sg_vertex_format { + SG_VERTEXFORMAT_INVALID, + SG_VERTEXFORMAT_FLOAT, + SG_VERTEXFORMAT_FLOAT2, + SG_VERTEXFORMAT_FLOAT3, + SG_VERTEXFORMAT_FLOAT4, + SG_VERTEXFORMAT_BYTE4, + SG_VERTEXFORMAT_BYTE4N, + SG_VERTEXFORMAT_UBYTE4, + SG_VERTEXFORMAT_UBYTE4N, + SG_VERTEXFORMAT_SHORT2, + SG_VERTEXFORMAT_SHORT2N, + SG_VERTEXFORMAT_USHORT2N, + SG_VERTEXFORMAT_SHORT4, + SG_VERTEXFORMAT_SHORT4N, + SG_VERTEXFORMAT_USHORT4N, + SG_VERTEXFORMAT_UINT10_N2, + _SG_VERTEXFORMAT_NUM, + _SG_VERTEXFORMAT_FORCE_U32 = 0x7FFFFFFF +} sg_vertex_format; + +/* + sg_vertex_step + + Defines whether the input pointer of a vertex input stream is advanced + 'per vertex' or 'per instance'. The default step-func is + SG_VERTEXSTEP_PER_VERTEX. SG_VERTEXSTEP_PER_INSTANCE is used with + instanced-rendering. + + The vertex-step is part of the vertex-layout definition + when creating pipeline objects. +*/ +typedef enum sg_vertex_step { + _SG_VERTEXSTEP_DEFAULT, /* value 0 reserved for default-init */ + SG_VERTEXSTEP_PER_VERTEX, + SG_VERTEXSTEP_PER_INSTANCE, + _SG_VERTEXSTEP_NUM, + _SG_VERTEXSTEP_FORCE_U32 = 0x7FFFFFFF +} sg_vertex_step; + +/* + sg_uniform_type + + The data type of a uniform block member. This is used to + describe the internal layout of uniform blocks when creating + a shader object. +*/ +typedef enum sg_uniform_type { + SG_UNIFORMTYPE_INVALID, + SG_UNIFORMTYPE_FLOAT, + SG_UNIFORMTYPE_FLOAT2, + SG_UNIFORMTYPE_FLOAT3, + SG_UNIFORMTYPE_FLOAT4, + SG_UNIFORMTYPE_MAT4, + _SG_UNIFORMTYPE_NUM, + _SG_UNIFORMTYPE_FORCE_U32 = 0x7FFFFFFF +} sg_uniform_type; + +/* + sg_cull_mode + + The face-culling mode, this is used in the + sg_pipeline_desc.cull_mode member when creating a + pipeline object. + + The default cull mode is SG_CULLMODE_NONE +*/ +typedef enum sg_cull_mode { + _SG_CULLMODE_DEFAULT, /* value 0 reserved for default-init */ + SG_CULLMODE_NONE, + SG_CULLMODE_FRONT, + SG_CULLMODE_BACK, + _SG_CULLMODE_NUM, + _SG_CULLMODE_FORCE_U32 = 0x7FFFFFFF +} sg_cull_mode; + +/* + sg_face_winding + + The vertex-winding rule that determines a front-facing primitive. This + is used in the member sg_pipeline_desc.face_winding + when creating a pipeline object. + + The default winding is SG_FACEWINDING_CW (clockwise) +*/ +typedef enum sg_face_winding { + _SG_FACEWINDING_DEFAULT, /* value 0 reserved for default-init */ + SG_FACEWINDING_CCW, + SG_FACEWINDING_CW, + _SG_FACEWINDING_NUM, + _SG_FACEWINDING_FORCE_U32 = 0x7FFFFFFF +} sg_face_winding; + +/* + sg_compare_func + + The compare-function for depth- and stencil-ref tests. + This is used when creating pipeline objects in the members: + + sg_pipeline_desc + .depth + .compare + .stencil + .front.compare + .back.compar + + The default compare func for depth- and stencil-tests is + SG_COMPAREFUNC_ALWAYS. +*/ +typedef enum sg_compare_func { + _SG_COMPAREFUNC_DEFAULT, /* value 0 reserved for default-init */ + SG_COMPAREFUNC_NEVER, + SG_COMPAREFUNC_LESS, + SG_COMPAREFUNC_EQUAL, + SG_COMPAREFUNC_LESS_EQUAL, + SG_COMPAREFUNC_GREATER, + SG_COMPAREFUNC_NOT_EQUAL, + SG_COMPAREFUNC_GREATER_EQUAL, + SG_COMPAREFUNC_ALWAYS, + _SG_COMPAREFUNC_NUM, + _SG_COMPAREFUNC_FORCE_U32 = 0x7FFFFFFF +} sg_compare_func; + +/* + sg_stencil_op + + The operation performed on a currently stored stencil-value when a + comparison test passes or fails. This is used when creating a pipeline + object in the members: + + sg_pipeline_desc + .stencil + .front + .fail_op + .depth_fail_op + .pass_op + .back + .fail_op + .depth_fail_op + .pass_op + + The default value is SG_STENCILOP_KEEP. +*/ +typedef enum sg_stencil_op { + _SG_STENCILOP_DEFAULT, /* value 0 reserved for default-init */ + SG_STENCILOP_KEEP, + SG_STENCILOP_ZERO, + SG_STENCILOP_REPLACE, + SG_STENCILOP_INCR_CLAMP, + SG_STENCILOP_DECR_CLAMP, + SG_STENCILOP_INVERT, + SG_STENCILOP_INCR_WRAP, + SG_STENCILOP_DECR_WRAP, + _SG_STENCILOP_NUM, + _SG_STENCILOP_FORCE_U32 = 0x7FFFFFFF +} sg_stencil_op; + +/* + sg_blend_factor + + The source and destination factors in blending operations. + This is used in the following members when creating a pipeline object: + + sg_pipeline_desc + .colors[i] + .blend + .src_factor_rgb + .dst_factor_rgb + .src_factor_alpha + .dst_factor_alpha + + The default value is SG_BLENDFACTOR_ONE for source + factors, and SG_BLENDFACTOR_ZERO for destination factors. +*/ +typedef enum sg_blend_factor { + _SG_BLENDFACTOR_DEFAULT, /* value 0 reserved for default-init */ + SG_BLENDFACTOR_ZERO, + SG_BLENDFACTOR_ONE, + SG_BLENDFACTOR_SRC_COLOR, + SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR, + SG_BLENDFACTOR_SRC_ALPHA, + SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, + SG_BLENDFACTOR_DST_COLOR, + SG_BLENDFACTOR_ONE_MINUS_DST_COLOR, + SG_BLENDFACTOR_DST_ALPHA, + SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA, + SG_BLENDFACTOR_SRC_ALPHA_SATURATED, + SG_BLENDFACTOR_BLEND_COLOR, + SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR, + SG_BLENDFACTOR_BLEND_ALPHA, + SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA, + _SG_BLENDFACTOR_NUM, + _SG_BLENDFACTOR_FORCE_U32 = 0x7FFFFFFF +} sg_blend_factor; + +/* + sg_blend_op + + Describes how the source and destination values are combined in the + fragment blending operation. It is used in the following members when + creating a pipeline object: + + sg_pipeline_desc + .colors[i] + .blend + .op_rgb + .op_alpha + + The default value is SG_BLENDOP_ADD. +*/ +typedef enum sg_blend_op { + _SG_BLENDOP_DEFAULT, /* value 0 reserved for default-init */ + SG_BLENDOP_ADD, + SG_BLENDOP_SUBTRACT, + SG_BLENDOP_REVERSE_SUBTRACT, + _SG_BLENDOP_NUM, + _SG_BLENDOP_FORCE_U32 = 0x7FFFFFFF +} sg_blend_op; + +/* + sg_color_mask + + Selects the active color channels when writing a fragment color to the + framebuffer. This is used in the members + sg_pipeline_desc.colors[i].write_mask when creating a pipeline object. + + The default colormask is SG_COLORMASK_RGBA (write all colors channels) + + NOTE: since the color mask value 0 is reserved for the default value + (SG_COLORMASK_RGBA), use SG_COLORMASK_NONE if all color channels + should be disabled. +*/ +typedef enum sg_color_mask { + _SG_COLORMASK_DEFAULT = 0, /* value 0 reserved for default-init */ + SG_COLORMASK_NONE = 0x10, /* special value for 'all channels disabled */ + SG_COLORMASK_R = 0x1, + SG_COLORMASK_G = 0x2, + SG_COLORMASK_RG = 0x3, + SG_COLORMASK_B = 0x4, + SG_COLORMASK_RB = 0x5, + SG_COLORMASK_GB = 0x6, + SG_COLORMASK_RGB = 0x7, + SG_COLORMASK_A = 0x8, + SG_COLORMASK_RA = 0x9, + SG_COLORMASK_GA = 0xA, + SG_COLORMASK_RGA = 0xB, + SG_COLORMASK_BA = 0xC, + SG_COLORMASK_RBA = 0xD, + SG_COLORMASK_GBA = 0xE, + SG_COLORMASK_RGBA = 0xF, + _SG_COLORMASK_FORCE_U32 = 0x7FFFFFFF +} sg_color_mask; + +/* + sg_action + + Defines what action should be performed at the start of a render pass: + + SG_ACTION_CLEAR: clear the render target image + SG_ACTION_LOAD: load the previous content of the render target image + SG_ACTION_DONTCARE: leave the render target image content undefined + + This is used in the sg_pass_action structure. + + The default action for all pass attachments is SG_ACTION_CLEAR, with the + clear color rgba = {0.5f, 0.5f, 0.5f, 1.0f], depth=1.0 and stencil=0. + + If you want to override the default behaviour, it is important to not + only set the clear color, but the 'action' field as well (as long as this + is in its _SG_ACTION_DEFAULT, the value fields will be ignored). +*/ +typedef enum sg_action { + _SG_ACTION_DEFAULT, + SG_ACTION_CLEAR, + SG_ACTION_LOAD, + SG_ACTION_DONTCARE, + _SG_ACTION_NUM, + _SG_ACTION_FORCE_U32 = 0x7FFFFFFF +} sg_action; + +/* + sg_pass_action + + The sg_pass_action struct defines the actions to be performed + at the start of a rendering pass in the functions sg_begin_pass() + and sg_begin_default_pass(). + + A separate action and clear values can be defined for each + color attachment, and for the depth-stencil attachment. + + The default clear values are defined by the macros: + + - SG_DEFAULT_CLEAR_RED: 0.5f + - SG_DEFAULT_CLEAR_GREEN: 0.5f + - SG_DEFAULT_CLEAR_BLUE: 0.5f + - SG_DEFAULT_CLEAR_ALPHA: 1.0f + - SG_DEFAULT_CLEAR_DEPTH: 1.0f + - SG_DEFAULT_CLEAR_STENCIL: 0 +*/ +typedef struct sg_color_attachment_action { + sg_action action; + sg_color value; +} sg_color_attachment_action; + +typedef struct sg_depth_attachment_action { + sg_action action; + float value; +} sg_depth_attachment_action; + +typedef struct sg_stencil_attachment_action { + sg_action action; + uint8_t value; +} sg_stencil_attachment_action; + +typedef struct sg_pass_action { + uint32_t _start_canary; + sg_color_attachment_action colors[SG_MAX_COLOR_ATTACHMENTS]; + sg_depth_attachment_action depth; + sg_stencil_attachment_action stencil; + uint32_t _end_canary; +} sg_pass_action; + +/* + sg_bindings + + The sg_bindings structure defines the resource binding slots + of the sokol_gfx render pipeline, used as argument to the + sg_apply_bindings() function. + + A resource binding struct contains: + + - 1..N vertex buffers + - 0..N vertex buffer offsets + - 0..1 index buffers + - 0..1 index buffer offsets + - 0..N vertex shader stage images + - 0..N fragment shader stage images + + The max number of vertex buffer and shader stage images + are defined by the SG_MAX_SHADERSTAGE_BUFFERS and + SG_MAX_SHADERSTAGE_IMAGES configuration constants. + + The optional buffer offsets can be used to put different unrelated + chunks of vertex- and/or index-data into the same buffer objects. +*/ +typedef struct sg_bindings { + uint32_t _start_canary; + sg_buffer vertex_buffers[SG_MAX_SHADERSTAGE_BUFFERS]; + int vertex_buffer_offsets[SG_MAX_SHADERSTAGE_BUFFERS]; + sg_buffer index_buffer; + int index_buffer_offset; + sg_image vs_images[SG_MAX_SHADERSTAGE_IMAGES]; + sg_image fs_images[SG_MAX_SHADERSTAGE_IMAGES]; + uint32_t _end_canary; +} sg_bindings; + +/* + sg_buffer_desc + + Creation parameters for sg_buffer objects, used in the + sg_make_buffer() call. + + The default configuration is: + + .size: 0 (*must* be >0 for buffers without data) + .type: SG_BUFFERTYPE_VERTEXBUFFER + .usage: SG_USAGE_IMMUTABLE + .data.ptr 0 (*must* be valid for immutable buffers) + .data.size 0 (*must* be > 0 for immutable buffers) + .label 0 (optional string label for trace hooks) + + The label will be ignored by sokol_gfx.h, it is only useful + when hooking into sg_make_buffer() or sg_init_buffer() via + the sg_install_trace_hooks() function. + + For immutable buffers which are initialized with initial data, + keep the .size item zero-initialized, and set the size together with the + pointer to the initial data in the .data item. + + For mutable buffers without initial data, keep the .data item + zero-initialized, and set the buffer size in the .size item instead. + + You can also set both size values, but currently both size values must + be identical (this may change in the future when the dynamic resource + management may become more flexible). + + ADVANCED TOPIC: Injecting native 3D-API buffers: + + The following struct members allow to inject your own GL, Metal + or D3D11 buffers into sokol_gfx: + + .gl_buffers[SG_NUM_INFLIGHT_FRAMES] + .mtl_buffers[SG_NUM_INFLIGHT_FRAMES] + .d3d11_buffer + + You must still provide all other struct items except the .data item, and + these must match the creation parameters of the native buffers you + provide. For SG_USAGE_IMMUTABLE, only provide a single native 3D-API + buffer, otherwise you need to provide SG_NUM_INFLIGHT_FRAMES buffers + (only for GL and Metal, not D3D11). Providing multiple buffers for GL and + Metal is necessary because sokol_gfx will rotate through them when + calling sg_update_buffer() to prevent lock-stalls. + + Note that it is expected that immutable injected buffer have already been + initialized with content, and the .content member must be 0! + + Also you need to call sg_reset_state_cache() after calling native 3D-API + functions, and before calling any sokol_gfx function. +*/ +typedef struct sg_buffer_desc { + uint32_t _start_canary; + size_t size; + sg_buffer_type type; + sg_usage usage; + sg_range data; + const char* label; + /* GL specific */ + uint32_t gl_buffers[SG_NUM_INFLIGHT_FRAMES]; + /* Metal specific */ + const void* mtl_buffers[SG_NUM_INFLIGHT_FRAMES]; + /* D3D11 specific */ + const void* d3d11_buffer; + /* WebGPU specific */ + const void* wgpu_buffer; + uint32_t _end_canary; +} sg_buffer_desc; + +/* + sg_image_data + + Defines the content of an image through a 2D array of sg_range structs. + The first array dimension is the cubemap face, and the second array + dimension the mipmap level. +*/ +typedef struct sg_image_data { + sg_range subimage[SG_CUBEFACE_NUM][SG_MAX_MIPMAPS]; +} sg_image_data; + +/* + sg_image_desc + + Creation parameters for sg_image objects, used in the sg_make_image() + call. + + The default configuration is: + + .type: SG_IMAGETYPE_2D + .render_target: false + .width 0 (must be set to >0) + .height 0 (must be set to >0) + .num_slices 1 (3D textures: depth; array textures: number of layers) + .num_mipmaps: 1 + .usage: SG_USAGE_IMMUTABLE + .pixel_format: SG_PIXELFORMAT_RGBA8 for textures, or sg_desc.context.color_format for render targets + .sample_count: 1 for textures, or sg_desc.context.sample_count for render targets + .min_filter: SG_FILTER_NEAREST + .mag_filter: SG_FILTER_NEAREST + .wrap_u: SG_WRAP_REPEAT + .wrap_v: SG_WRAP_REPEAT + .wrap_w: SG_WRAP_REPEAT (only SG_IMAGETYPE_3D) + .border_color SG_BORDERCOLOR_OPAQUE_BLACK + .max_anisotropy 1 (must be 1..16) + .min_lod 0.0f + .max_lod FLT_MAX + .data an sg_image_data struct to define the initial content + .label 0 (optional string label for trace hooks) + + Q: Why is the default sample_count for render targets identical with the + "default sample count" from sg_desc.context.sample_count? + + A: So that it matches the default sample count in pipeline objects. Even + though it is a bit strange/confusing that offscreen render targets by default + get the same sample count as the default framebuffer, but it's better that + an offscreen render target created with default parameters matches + a pipeline object created with default parameters. + + NOTE: + + SG_IMAGETYPE_ARRAY and SG_IMAGETYPE_3D are not supported on WebGL/GLES2, + use sg_query_features().imagetype_array and + sg_query_features().imagetype_3d at runtime to check if array- and + 3D-textures are supported. + + Images with usage SG_USAGE_IMMUTABLE must be fully initialized by + providing a valid .data member which points to initialization data. + + ADVANCED TOPIC: Injecting native 3D-API textures: + + The following struct members allow to inject your own GL, Metal or D3D11 + textures into sokol_gfx: + + .gl_textures[SG_NUM_INFLIGHT_FRAMES] + .mtl_textures[SG_NUM_INFLIGHT_FRAMES] + .d3d11_texture + .d3d11_shader_resource_view + + For GL, you can also specify the texture target or leave it empty to use + the default texture target for the image type (GL_TEXTURE_2D for + SG_IMAGETYPE_2D etc) + + For D3D11, you can provide either a D3D11 texture, or a + shader-resource-view, or both. If only a texture is provided, a matching + shader-resource-view will be created. If only a shader-resource-view is + provided, the texture will be looked up from the shader-resource-view. + + The same rules apply as for injecting native buffers (see sg_buffer_desc + documentation for more details). +*/ +typedef struct sg_image_desc { + uint32_t _start_canary; + sg_image_type type; + bool render_target; + int width; + int height; + int num_slices; + int num_mipmaps; + sg_usage usage; + sg_pixel_format pixel_format; + int sample_count; + sg_filter min_filter; + sg_filter mag_filter; + sg_wrap wrap_u; + sg_wrap wrap_v; + sg_wrap wrap_w; + sg_border_color border_color; + uint32_t max_anisotropy; + float min_lod; + float max_lod; + sg_image_data data; + const char* label; + /* GL specific */ + uint32_t gl_textures[SG_NUM_INFLIGHT_FRAMES]; + uint32_t gl_texture_target; + /* Metal specific */ + const void* mtl_textures[SG_NUM_INFLIGHT_FRAMES]; + /* D3D11 specific */ + const void* d3d11_texture; + const void* d3d11_shader_resource_view; + /* WebGPU specific */ + const void* wgpu_texture; + uint32_t _end_canary; +} sg_image_desc; + +/* + sg_shader_desc + + The structure sg_shader_desc defines all creation parameters for shader + programs, used as input to the sg_make_shader() function: + + - reflection information for vertex attributes (vertex shader inputs): + - vertex attribute name (required for GLES2, optional for GLES3 and GL) + - a semantic name and index (required for D3D11) + - for each shader-stage (vertex and fragment): + - the shader source or bytecode + - an optional entry function name + - an optional compile target (only for D3D11 when source is provided, + defaults are "vs_4_0" and "ps_4_0") + - reflection info for each uniform block used by the shader stage: + - the size of the uniform block in bytes + - reflection info for each uniform block member (only required for GL backends): + - member name + - member type (SG_UNIFORMTYPE_xxx) + - if the member is an array, the number of array items + - reflection info for the texture images used by the shader stage: + - the image type (SG_IMAGETYPE_xxx) + - the sampler type (SG_SAMPLERTYPE_xxx, default is SG_SAMPLERTYPE_FLOAT) + - the name of the texture sampler (required for GLES2, optional everywhere else) + + For all GL backends, shader source-code must be provided. For D3D11 and Metal, + either shader source-code or byte-code can be provided. + + For D3D11, if source code is provided, the d3dcompiler_47.dll will be loaded + on demand. If this fails, shader creation will fail. When compiling HLSL + source code, you can provide an optional target string via + sg_shader_stage_desc.d3d11_target, the default target is "vs_4_0" for the + vertex shader stage and "ps_4_0" for the pixel shader stage. +*/ +typedef struct sg_shader_attr_desc { + const char* name; // GLSL vertex attribute name (only strictly required for GLES2) + const char* sem_name; // HLSL semantic name + int sem_index; // HLSL semantic index +} sg_shader_attr_desc; + +typedef struct sg_shader_uniform_desc { + const char* name; + sg_uniform_type type; + int array_count; +} sg_shader_uniform_desc; + +typedef struct sg_shader_uniform_block_desc { + size_t size; + sg_shader_uniform_desc uniforms[SG_MAX_UB_MEMBERS]; +} sg_shader_uniform_block_desc; + +typedef struct sg_shader_image_desc { + const char* name; + sg_image_type image_type; + sg_sampler_type sampler_type; +} sg_shader_image_desc; + +typedef struct sg_shader_stage_desc { + const char* source; + sg_range bytecode; + const char* entry; + const char* d3d11_target; + sg_shader_uniform_block_desc uniform_blocks[SG_MAX_SHADERSTAGE_UBS]; + sg_shader_image_desc images[SG_MAX_SHADERSTAGE_IMAGES]; +} sg_shader_stage_desc; + +typedef struct sg_shader_desc { + uint32_t _start_canary; + sg_shader_attr_desc attrs[SG_MAX_VERTEX_ATTRIBUTES]; + sg_shader_stage_desc vs; + sg_shader_stage_desc fs; + const char* label; + uint32_t _end_canary; +} sg_shader_desc; + +/* + sg_pipeline_desc + + The sg_pipeline_desc struct defines all creation parameters for an + sg_pipeline object, used as argument to the sg_make_pipeline() function: + + - the vertex layout for all input vertex buffers + - a shader object + - the 3D primitive type (points, lines, triangles, ...) + - the index type (none, 16- or 32-bit) + - all the fixed-function-pipeline state (depth-, stencil-, blend-state, etc...) + + If the vertex data has no gaps between vertex components, you can omit + the .layout.buffers[].stride and layout.attrs[].offset items (leave them + default-initialized to 0), sokol-gfx will then compute the offsets and + strides from the vertex component formats (.layout.attrs[].format). + Please note that ALL vertex attribute offsets must be 0 in order for the + automatic offset computation to kick in. + + The default configuration is as follows: + + .shader: 0 (must be initialized with a valid sg_shader id!) + .layout: + .buffers[]: vertex buffer layouts + .stride: 0 (if no stride is given it will be computed) + .step_func SG_VERTEXSTEP_PER_VERTEX + .step_rate 1 + .attrs[]: vertex attribute declarations + .buffer_index 0 the vertex buffer bind slot + .offset 0 (offsets can be omitted if the vertex layout has no gaps) + .format SG_VERTEXFORMAT_INVALID (must be initialized!) + .depth: + .pixel_format: sg_desc.context.depth_format + .compare: SG_COMPAREFUNC_ALWAYS + .write_enabled: false + .bias: 0.0f + .bias_slope_scale: 0.0f + .bias_clamp: 0.0f + .stencil: + .enabled: false + .front/back: + .compare: SG_COMPAREFUNC_ALWAYS + .depth_fail_op: SG_STENCILOP_KEEP + .pass_op: SG_STENCILOP_KEEP + .compare: SG_COMPAREFUNC_ALWAYS + .read_mask: 0 + .write_mask: 0 + .ref: 0 + .color_count 1 + .colors[0..color_count] + .pixel_format sg_desc.context.color_format + .write_mask: SG_COLORMASK_RGBA + .blend: + .enabled: false + .src_factor_rgb: SG_BLENDFACTOR_ONE + .dst_factor_rgb: SG_BLENDFACTOR_ZERO + .op_rgb: SG_BLENDOP_ADD + .src_factor_alpha: SG_BLENDFACTOR_ONE + .dst_factor_alpha: SG_BLENDFACTOR_ZERO + .op_alpha: SG_BLENDOP_ADD + .primitive_type: SG_PRIMITIVETYPE_TRIANGLES + .index_type: SG_INDEXTYPE_NONE + .cull_mode: SG_CULLMODE_NONE + .face_winding: SG_FACEWINDING_CW + .sample_count: sg_desc.context.sample_count + .blend_color: (sg_color) { 0.0f, 0.0f, 0.0f, 0.0f } + .alpha_to_coverage_enabled: false + .label 0 (optional string label for trace hooks) +*/ +typedef struct sg_buffer_layout_desc { + int stride; + sg_vertex_step step_func; + int step_rate; + #if defined(SOKOL_ZIG_BINDINGS) + uint32_t __pad[2]; + #endif +} sg_buffer_layout_desc; + +typedef struct sg_vertex_attr_desc { + int buffer_index; + int offset; + sg_vertex_format format; + #if defined(SOKOL_ZIG_BINDINGS) + uint32_t __pad[2]; + #endif +} sg_vertex_attr_desc; + +typedef struct sg_layout_desc { + sg_buffer_layout_desc buffers[SG_MAX_SHADERSTAGE_BUFFERS]; + sg_vertex_attr_desc attrs[SG_MAX_VERTEX_ATTRIBUTES]; +} sg_layout_desc; + +typedef struct sg_stencil_face_state { + sg_compare_func compare; + sg_stencil_op fail_op; + sg_stencil_op depth_fail_op; + sg_stencil_op pass_op; +} sg_stencil_face_state; + +typedef struct sg_stencil_state { + bool enabled; + sg_stencil_face_state front; + sg_stencil_face_state back; + uint8_t read_mask; + uint8_t write_mask; + uint8_t ref; +} sg_stencil_state; + +typedef struct sg_depth_state { + sg_pixel_format pixel_format; + sg_compare_func compare; + bool write_enabled; + float bias; + float bias_slope_scale; + float bias_clamp; +} sg_depth_state; + +typedef struct sg_blend_state { + bool enabled; + sg_blend_factor src_factor_rgb; + sg_blend_factor dst_factor_rgb; + sg_blend_op op_rgb; + sg_blend_factor src_factor_alpha; + sg_blend_factor dst_factor_alpha; + sg_blend_op op_alpha; +} sg_blend_state; + +typedef struct sg_color_state { + sg_pixel_format pixel_format; + sg_color_mask write_mask; + sg_blend_state blend; +} sg_color_state; + +typedef struct sg_pipeline_desc { + uint32_t _start_canary; + sg_shader shader; + sg_layout_desc layout; + sg_depth_state depth; + sg_stencil_state stencil; + int color_count; + sg_color_state colors[SG_MAX_COLOR_ATTACHMENTS]; + sg_primitive_type primitive_type; + sg_index_type index_type; + sg_cull_mode cull_mode; + sg_face_winding face_winding; + int sample_count; + sg_color blend_color; + bool alpha_to_coverage_enabled; + const char* label; + uint32_t _end_canary; +} sg_pipeline_desc; + +/* + sg_pass_desc + + Creation parameters for an sg_pass object, used as argument + to the sg_make_pass() function. + + A pass object contains 1..4 color-attachments and none, or one, + depth-stencil-attachment. Each attachment consists of + an image, and two additional indices describing + which subimage the pass will render to: one mipmap index, and + if the image is a cubemap, array-texture or 3D-texture, the + face-index, array-layer or depth-slice. + + Pass images must fulfill the following requirements: + + All images must have: + - been created as render target (sg_image_desc.render_target = true) + - the same size + - the same sample count + + In addition, all color-attachment images must have the same pixel format. +*/ +typedef struct sg_pass_attachment_desc { + sg_image image; + int mip_level; + int slice; /* cube texture: face; array texture: layer; 3D texture: slice */ +} sg_pass_attachment_desc; + +typedef struct sg_pass_desc { + uint32_t _start_canary; + sg_pass_attachment_desc color_attachments[SG_MAX_COLOR_ATTACHMENTS]; + sg_pass_attachment_desc depth_stencil_attachment; + const char* label; + uint32_t _end_canary; +} sg_pass_desc; + +/* + sg_trace_hooks + + Installable callback functions to keep track of the sokol-gfx calls, + this is useful for debugging, or keeping track of resource creation + and destruction. + + Trace hooks are installed with sg_install_trace_hooks(), this returns + another sg_trace_hooks struct with the previous set of + trace hook function pointers. These should be invoked by the + new trace hooks to form a proper call chain. +*/ +typedef struct sg_trace_hooks { + void* user_data; + void (*reset_state_cache)(void* user_data); + void (*make_buffer)(const sg_buffer_desc* desc, sg_buffer result, void* user_data); + void (*make_image)(const sg_image_desc* desc, sg_image result, void* user_data); + void (*make_shader)(const sg_shader_desc* desc, sg_shader result, void* user_data); + void (*make_pipeline)(const sg_pipeline_desc* desc, sg_pipeline result, void* user_data); + void (*make_pass)(const sg_pass_desc* desc, sg_pass result, void* user_data); + void (*destroy_buffer)(sg_buffer buf, void* user_data); + void (*destroy_image)(sg_image img, void* user_data); + void (*destroy_shader)(sg_shader shd, void* user_data); + void (*destroy_pipeline)(sg_pipeline pip, void* user_data); + void (*destroy_pass)(sg_pass pass, void* user_data); + void (*update_buffer)(sg_buffer buf, const sg_range* data, void* user_data); + void (*update_image)(sg_image img, const sg_image_data* data, void* user_data); + void (*append_buffer)(sg_buffer buf, const sg_range* data, int result, void* user_data); + void (*begin_default_pass)(const sg_pass_action* pass_action, int width, int height, void* user_data); + void (*begin_pass)(sg_pass pass, const sg_pass_action* pass_action, void* user_data); + void (*apply_viewport)(int x, int y, int width, int height, bool origin_top_left, void* user_data); + void (*apply_scissor_rect)(int x, int y, int width, int height, bool origin_top_left, void* user_data); + void (*apply_pipeline)(sg_pipeline pip, void* user_data); + void (*apply_bindings)(const sg_bindings* bindings, void* user_data); + void (*apply_uniforms)(sg_shader_stage stage, int ub_index, const sg_range* data, void* user_data); + void (*draw)(int base_element, int num_elements, int num_instances, void* user_data); + void (*end_pass)(void* user_data); + void (*commit)(void* user_data); + void (*alloc_buffer)(sg_buffer result, void* user_data); + void (*alloc_image)(sg_image result, void* user_data); + void (*alloc_shader)(sg_shader result, void* user_data); + void (*alloc_pipeline)(sg_pipeline result, void* user_data); + void (*alloc_pass)(sg_pass result, void* user_data); + void (*dealloc_buffer)(sg_buffer buf_id, void* user_data); + void (*dealloc_image)(sg_image img_id, void* user_data); + void (*dealloc_shader)(sg_shader shd_id, void* user_data); + void (*dealloc_pipeline)(sg_pipeline pip_id, void* user_data); + void (*dealloc_pass)(sg_pass pass_id, void* user_data); + void (*init_buffer)(sg_buffer buf_id, const sg_buffer_desc* desc, void* user_data); + void (*init_image)(sg_image img_id, const sg_image_desc* desc, void* user_data); + void (*init_shader)(sg_shader shd_id, const sg_shader_desc* desc, void* user_data); + void (*init_pipeline)(sg_pipeline pip_id, const sg_pipeline_desc* desc, void* user_data); + void (*init_pass)(sg_pass pass_id, const sg_pass_desc* desc, void* user_data); + void (*uninit_buffer)(sg_buffer buf_id, void* user_data); + void (*uninit_image)(sg_image img_id, void* user_data); + void (*uninit_shader)(sg_shader shd_id, void* user_data); + void (*uninit_pipeline)(sg_pipeline pip_id, void* user_data); + void (*uninit_pass)(sg_pass pass_id, void* user_data); + void (*fail_buffer)(sg_buffer buf_id, void* user_data); + void (*fail_image)(sg_image img_id, void* user_data); + void (*fail_shader)(sg_shader shd_id, void* user_data); + void (*fail_pipeline)(sg_pipeline pip_id, void* user_data); + void (*fail_pass)(sg_pass pass_id, void* user_data); + void (*push_debug_group)(const char* name, void* user_data); + void (*pop_debug_group)(void* user_data); + void (*err_buffer_pool_exhausted)(void* user_data); + void (*err_image_pool_exhausted)(void* user_data); + void (*err_shader_pool_exhausted)(void* user_data); + void (*err_pipeline_pool_exhausted)(void* user_data); + void (*err_pass_pool_exhausted)(void* user_data); + void (*err_context_mismatch)(void* user_data); + void (*err_pass_invalid)(void* user_data); + void (*err_draw_invalid)(void* user_data); + void (*err_bindings_invalid)(void* user_data); +} sg_trace_hooks; + +/* + sg_buffer_info + sg_image_info + sg_shader_info + sg_pipeline_info + sg_pass_info + + These structs contain various internal resource attributes which + might be useful for debug-inspection. Please don't rely on the + actual content of those structs too much, as they are quite closely + tied to sokol_gfx.h internals and may change more frequently than + the other public API elements. + + The *_info structs are used as the return values of the following functions: + + sg_query_buffer_info() + sg_query_image_info() + sg_query_shader_info() + sg_query_pipeline_info() + sg_query_pass_info() +*/ +typedef struct sg_slot_info { + sg_resource_state state; /* the current state of this resource slot */ + uint32_t res_id; /* type-neutral resource if (e.g. sg_buffer.id) */ + uint32_t ctx_id; /* the context this resource belongs to */ +} sg_slot_info; + +typedef struct sg_buffer_info { + sg_slot_info slot; /* resource pool slot info */ + uint32_t update_frame_index; /* frame index of last sg_update_buffer() */ + uint32_t append_frame_index; /* frame index of last sg_append_buffer() */ + int append_pos; /* current position in buffer for sg_append_buffer() */ + bool append_overflow; /* is buffer in overflow state (due to sg_append_buffer) */ + int num_slots; /* number of renaming-slots for dynamically updated buffers */ + int active_slot; /* currently active write-slot for dynamically updated buffers */ +} sg_buffer_info; + +typedef struct sg_image_info { + sg_slot_info slot; /* resource pool slot info */ + uint32_t upd_frame_index; /* frame index of last sg_update_image() */ + int num_slots; /* number of renaming-slots for dynamically updated images */ + int active_slot; /* currently active write-slot for dynamically updated images */ + int width; /* image width */ + int height; /* image height */ +} sg_image_info; + +typedef struct sg_shader_info { + sg_slot_info slot; /* resoure pool slot info */ +} sg_shader_info; + +typedef struct sg_pipeline_info { + sg_slot_info slot; /* resource pool slot info */ +} sg_pipeline_info; + +typedef struct sg_pass_info { + sg_slot_info slot; /* resource pool slot info */ +} sg_pass_info; + +/* + sg_desc + + The sg_desc struct contains configuration values for sokol_gfx, + it is used as parameter to the sg_setup() call. + + NOTE that all callback function pointers come in two versions, one without + a userdata pointer, and one with a userdata pointer. You would + either initialize one or the other depending on whether you pass data + to your callbacks. + + FIXME: explain the various configuration options + + The default configuration is: + + .buffer_pool_size 128 + .image_pool_size 128 + .shader_pool_size 32 + .pipeline_pool_size 64 + .pass_pool_size 16 + .context_pool_size 16 + .sampler_cache_size 64 + .uniform_buffer_size 4 MB (4*1024*1024) + .staging_buffer_size 8 MB (8*1024*1024) + + .context.color_format: default value depends on selected backend: + all GL backends: SG_PIXELFORMAT_RGBA8 + Metal and D3D11: SG_PIXELFORMAT_BGRA8 + WGPU: *no default* (must be queried from WGPU swapchain) + .context.depth_format SG_PIXELFORMAT_DEPTH_STENCIL + .context.sample_count 1 + + GL specific: + .context.gl.force_gles2 + if this is true the GL backend will act in "GLES2 fallback mode" even + when compiled with SOKOL_GLES3, this is useful to fall back + to traditional WebGL if a browser doesn't support a WebGL2 context + + Metal specific: + (NOTE: All Objective-C object references are transferred through + a bridged (const void*) to sokol_gfx, which will use a unretained + bridged cast (__bridged id) to retrieve the Objective-C + references back. Since the bridge cast is unretained, the caller + must hold a strong reference to the Objective-C object for the + duration of the sokol_gfx call! + + .context.metal.device + a pointer to the MTLDevice object + .context.metal.renderpass_descriptor_cb + .context.metal_renderpass_descriptor_userdata_cb + A C callback function to obtain the MTLRenderPassDescriptor for the + current frame when rendering to the default framebuffer, will be called + in sg_begin_default_pass(). + .context.metal.drawable_cb + .context.metal.drawable_userdata_cb + a C callback function to obtain a MTLDrawable for the current + frame when rendering to the default framebuffer, will be called in + sg_end_pass() of the default pass + .context.metal.user_data + optional user data pointer passed to the userdata versions of + callback functions + + D3D11 specific: + .context.d3d11.device + a pointer to the ID3D11Device object, this must have been created + before sg_setup() is called + .context.d3d11.device_context + a pointer to the ID3D11DeviceContext object + .context.d3d11.render_target_view_cb + .context.d3d11.render_target_view_userdata_cb + a C callback function to obtain a pointer to the current + ID3D11RenderTargetView object of the default framebuffer, + this function will be called in sg_begin_pass() when rendering + to the default framebuffer + .context.d3d11.depth_stencil_view_cb + .context.d3d11.depth_stencil_view_userdata_cb + a C callback function to obtain a pointer to the current + ID3D11DepthStencilView object of the default framebuffer, + this function will be called in sg_begin_pass() when rendering + to the default framebuffer + .context.metal.user_data + optional user data pointer passed to the userdata versions of + callback functions + + WebGPU specific: + .context.wgpu.device + a WGPUDevice handle + .context.wgpu.render_format + WGPUTextureFormat of the swap chain surface + .context.wgpu.render_view_cb + .context.wgpu.render_view_userdata_cb + callback to get the current WGPUTextureView of the swapchain's + rendering attachment (may be an MSAA surface) + .context.wgpu.resolve_view_cb + .context.wgpu.resolve_view_userdata_cb + callback to get the current WGPUTextureView of the swapchain's + MSAA-resolve-target surface, must return 0 if not MSAA rendering + .context.wgpu.depth_stencil_view_cb + .context.wgpu.depth_stencil_view_userdata_cb + callback to get current default-pass depth-stencil-surface WGPUTextureView + the pixel format of the default WGPUTextureView must be WGPUTextureFormat_Depth24Plus8 + .context.metal.user_data + optional user data pointer passed to the userdata versions of + callback functions + + When using sokol_gfx.h and sokol_app.h together, consider using the + helper function sapp_sgcontext() in the sokol_glue.h header to + initialize the sg_desc.context nested struct. sapp_sgcontext() returns + a completely initialized sg_context_desc struct with information + provided by sokol_app.h. +*/ +typedef struct sg_gl_context_desc { + bool force_gles2; +} sg_gl_context_desc; + +typedef struct sg_metal_context_desc { + const void* device; + const void* (*renderpass_descriptor_cb)(void); + const void* (*renderpass_descriptor_userdata_cb)(void*); + const void* (*drawable_cb)(void); + const void* (*drawable_userdata_cb)(void*); + void* user_data; +} sg_metal_context_desc; + +typedef struct sg_d3d11_context_desc { + const void* device; + const void* device_context; + const void* (*render_target_view_cb)(void); + const void* (*render_target_view_userdata_cb)(void*); + const void* (*depth_stencil_view_cb)(void); + const void* (*depth_stencil_view_userdata_cb)(void*); + void* user_data; +} sg_d3d11_context_desc; + +typedef struct sg_wgpu_context_desc { + const void* device; /* WGPUDevice */ + const void* (*render_view_cb)(void); /* returns WGPUTextureView */ + const void* (*render_view_userdata_cb)(void*); + const void* (*resolve_view_cb)(void); /* returns WGPUTextureView */ + const void* (*resolve_view_userdata_cb)(void*); + const void* (*depth_stencil_view_cb)(void); /* returns WGPUTextureView, must be WGPUTextureFormat_Depth24Plus8 */ + const void* (*depth_stencil_view_userdata_cb)(void*); + void* user_data; +} sg_wgpu_context_desc; + +typedef struct sg_context_desc { + sg_pixel_format color_format; + sg_pixel_format depth_format; + int sample_count; + sg_gl_context_desc gl; + sg_metal_context_desc metal; + sg_d3d11_context_desc d3d11; + sg_wgpu_context_desc wgpu; +} sg_context_desc; + +typedef struct sg_desc { + uint32_t _start_canary; + int buffer_pool_size; + int image_pool_size; + int shader_pool_size; + int pipeline_pool_size; + int pass_pool_size; + int context_pool_size; + int uniform_buffer_size; + int staging_buffer_size; + int sampler_cache_size; + sg_context_desc context; + uint32_t _end_canary; +} sg_desc; + +/* setup and misc functions */ +SOKOL_GFX_API_DECL void sg_setup(const sg_desc* desc); +SOKOL_GFX_API_DECL void sg_shutdown(void); +SOKOL_GFX_API_DECL bool sg_isvalid(void); +SOKOL_GFX_API_DECL void sg_reset_state_cache(void); +SOKOL_GFX_API_DECL sg_trace_hooks sg_install_trace_hooks(const sg_trace_hooks* trace_hooks); +SOKOL_GFX_API_DECL void sg_push_debug_group(const char* name); +SOKOL_GFX_API_DECL void sg_pop_debug_group(void); + +/* resource creation, destruction and updating */ +SOKOL_GFX_API_DECL sg_buffer sg_make_buffer(const sg_buffer_desc* desc); +SOKOL_GFX_API_DECL sg_image sg_make_image(const sg_image_desc* desc); +SOKOL_GFX_API_DECL sg_shader sg_make_shader(const sg_shader_desc* desc); +SOKOL_GFX_API_DECL sg_pipeline sg_make_pipeline(const sg_pipeline_desc* desc); +SOKOL_GFX_API_DECL sg_pass sg_make_pass(const sg_pass_desc* desc); +SOKOL_GFX_API_DECL void sg_destroy_buffer(sg_buffer buf); +SOKOL_GFX_API_DECL void sg_destroy_image(sg_image img); +SOKOL_GFX_API_DECL void sg_destroy_shader(sg_shader shd); +SOKOL_GFX_API_DECL void sg_destroy_pipeline(sg_pipeline pip); +SOKOL_GFX_API_DECL void sg_destroy_pass(sg_pass pass); +SOKOL_GFX_API_DECL void sg_update_buffer(sg_buffer buf, const sg_range* data); +SOKOL_GFX_API_DECL void sg_update_image(sg_image img, const sg_image_data* data); +SOKOL_GFX_API_DECL int sg_append_buffer(sg_buffer buf, const sg_range* data); +SOKOL_GFX_API_DECL bool sg_query_buffer_overflow(sg_buffer buf); + +/* rendering functions */ +SOKOL_GFX_API_DECL void sg_begin_default_pass(const sg_pass_action* pass_action, int width, int height); +SOKOL_GFX_API_DECL void sg_begin_default_passf(const sg_pass_action* pass_action, float width, float height); +SOKOL_GFX_API_DECL void sg_begin_pass(sg_pass pass, const sg_pass_action* pass_action); +SOKOL_GFX_API_DECL void sg_apply_viewport(int x, int y, int width, int height, bool origin_top_left); +SOKOL_GFX_API_DECL void sg_apply_viewportf(float x, float y, float width, float height, bool origin_top_left); +SOKOL_GFX_API_DECL void sg_apply_scissor_rect(int x, int y, int width, int height, bool origin_top_left); +SOKOL_GFX_API_DECL void sg_apply_scissor_rectf(float x, float y, float width, float height, bool origin_top_left); +SOKOL_GFX_API_DECL void sg_apply_pipeline(sg_pipeline pip); +SOKOL_GFX_API_DECL void sg_apply_bindings(const sg_bindings* bindings); +SOKOL_GFX_API_DECL void sg_apply_uniforms(sg_shader_stage stage, int ub_index, const sg_range* data); +SOKOL_GFX_API_DECL void sg_draw(int base_element, int num_elements, int num_instances); +SOKOL_GFX_API_DECL void sg_end_pass(void); +SOKOL_GFX_API_DECL void sg_commit(void); + +/* getting information */ +SOKOL_GFX_API_DECL sg_desc sg_query_desc(void); +SOKOL_GFX_API_DECL sg_backend sg_query_backend(void); +SOKOL_GFX_API_DECL sg_features sg_query_features(void); +SOKOL_GFX_API_DECL sg_limits sg_query_limits(void); +SOKOL_GFX_API_DECL sg_pixelformat_info sg_query_pixelformat(sg_pixel_format fmt); +/* get current state of a resource (INITIAL, ALLOC, VALID, FAILED, INVALID) */ +SOKOL_GFX_API_DECL sg_resource_state sg_query_buffer_state(sg_buffer buf); +SOKOL_GFX_API_DECL sg_resource_state sg_query_image_state(sg_image img); +SOKOL_GFX_API_DECL sg_resource_state sg_query_shader_state(sg_shader shd); +SOKOL_GFX_API_DECL sg_resource_state sg_query_pipeline_state(sg_pipeline pip); +SOKOL_GFX_API_DECL sg_resource_state sg_query_pass_state(sg_pass pass); +/* get runtime information about a resource */ +SOKOL_GFX_API_DECL sg_buffer_info sg_query_buffer_info(sg_buffer buf); +SOKOL_GFX_API_DECL sg_image_info sg_query_image_info(sg_image img); +SOKOL_GFX_API_DECL sg_shader_info sg_query_shader_info(sg_shader shd); +SOKOL_GFX_API_DECL sg_pipeline_info sg_query_pipeline_info(sg_pipeline pip); +SOKOL_GFX_API_DECL sg_pass_info sg_query_pass_info(sg_pass pass); +/* get resource creation desc struct with their default values replaced */ +SOKOL_GFX_API_DECL sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc* desc); +SOKOL_GFX_API_DECL sg_image_desc sg_query_image_defaults(const sg_image_desc* desc); +SOKOL_GFX_API_DECL sg_shader_desc sg_query_shader_defaults(const sg_shader_desc* desc); +SOKOL_GFX_API_DECL sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_desc* desc); +SOKOL_GFX_API_DECL sg_pass_desc sg_query_pass_defaults(const sg_pass_desc* desc); + +/* separate resource allocation and initialization (for async setup) */ +SOKOL_GFX_API_DECL sg_buffer sg_alloc_buffer(void); +SOKOL_GFX_API_DECL sg_image sg_alloc_image(void); +SOKOL_GFX_API_DECL sg_shader sg_alloc_shader(void); +SOKOL_GFX_API_DECL sg_pipeline sg_alloc_pipeline(void); +SOKOL_GFX_API_DECL sg_pass sg_alloc_pass(void); +SOKOL_GFX_API_DECL void sg_dealloc_buffer(sg_buffer buf_id); +SOKOL_GFX_API_DECL void sg_dealloc_image(sg_image img_id); +SOKOL_GFX_API_DECL void sg_dealloc_shader(sg_shader shd_id); +SOKOL_GFX_API_DECL void sg_dealloc_pipeline(sg_pipeline pip_id); +SOKOL_GFX_API_DECL void sg_dealloc_pass(sg_pass pass_id); +SOKOL_GFX_API_DECL void sg_init_buffer(sg_buffer buf_id, const sg_buffer_desc* desc); +SOKOL_GFX_API_DECL void sg_init_image(sg_image img_id, const sg_image_desc* desc); +SOKOL_GFX_API_DECL void sg_init_shader(sg_shader shd_id, const sg_shader_desc* desc); +SOKOL_GFX_API_DECL void sg_init_pipeline(sg_pipeline pip_id, const sg_pipeline_desc* desc); +SOKOL_GFX_API_DECL void sg_init_pass(sg_pass pass_id, const sg_pass_desc* desc); +SOKOL_GFX_API_DECL bool sg_uninit_buffer(sg_buffer buf_id); +SOKOL_GFX_API_DECL bool sg_uninit_image(sg_image img_id); +SOKOL_GFX_API_DECL bool sg_uninit_shader(sg_shader shd_id); +SOKOL_GFX_API_DECL bool sg_uninit_pipeline(sg_pipeline pip_id); +SOKOL_GFX_API_DECL bool sg_uninit_pass(sg_pass pass_id); +SOKOL_GFX_API_DECL void sg_fail_buffer(sg_buffer buf_id); +SOKOL_GFX_API_DECL void sg_fail_image(sg_image img_id); +SOKOL_GFX_API_DECL void sg_fail_shader(sg_shader shd_id); +SOKOL_GFX_API_DECL void sg_fail_pipeline(sg_pipeline pip_id); +SOKOL_GFX_API_DECL void sg_fail_pass(sg_pass pass_id); + +/* rendering contexts (optional) */ +SOKOL_GFX_API_DECL sg_context sg_setup_context(void); +SOKOL_GFX_API_DECL void sg_activate_context(sg_context ctx_id); +SOKOL_GFX_API_DECL void sg_discard_context(sg_context ctx_id); + +/* Backend-specific helper functions, these may come in handy for mixing + sokol-gfx rendering with 'native backend' rendering functions. + + This group of functions will be expanded as needed. +*/ + +/* D3D11: return ID3D11Device */ +SOKOL_GFX_API_DECL const void* sg_d3d11_device(void); + +/* Metal: return __bridge-casted MTLDevice */ +SOKOL_GFX_API_DECL const void* sg_mtl_device(void); + +/* Metal: return __bridge-casted MTLRenderCommandEncoder in current pass (or zero if outside pass) */ +SOKOL_GFX_API_DECL const void* sg_mtl_render_command_encoder(void); + +#ifdef __cplusplus +} /* extern "C" */ + +/* reference-based equivalents for c++ */ +inline void sg_setup(const sg_desc& desc) { return sg_setup(&desc); } + +inline sg_buffer sg_make_buffer(const sg_buffer_desc& desc) { return sg_make_buffer(&desc); } +inline sg_image sg_make_image(const sg_image_desc& desc) { return sg_make_image(&desc); } +inline sg_shader sg_make_shader(const sg_shader_desc& desc) { return sg_make_shader(&desc); } +inline sg_pipeline sg_make_pipeline(const sg_pipeline_desc& desc) { return sg_make_pipeline(&desc); } +inline sg_pass sg_make_pass(const sg_pass_desc& desc) { return sg_make_pass(&desc); } +inline void sg_update_image(sg_image img, const sg_image_data& data) { return sg_update_image(img, &data); } + +inline void sg_begin_default_pass(const sg_pass_action& pass_action, int width, int height) { return sg_begin_default_pass(&pass_action, width, height); } +inline void sg_begin_default_passf(const sg_pass_action& pass_action, float width, float height) { return sg_begin_default_passf(&pass_action, width, height); } +inline void sg_begin_pass(sg_pass pass, const sg_pass_action& pass_action) { return sg_begin_pass(pass, &pass_action); } +inline void sg_apply_bindings(const sg_bindings& bindings) { return sg_apply_bindings(&bindings); } +inline void sg_apply_uniforms(sg_shader_stage stage, int ub_index, const sg_range& data) { return sg_apply_uniforms(stage, ub_index, &data); } + +inline sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc& desc) { return sg_query_buffer_defaults(&desc); } +inline sg_image_desc sg_query_image_defaults(const sg_image_desc& desc) { return sg_query_image_defaults(&desc); } +inline sg_shader_desc sg_query_shader_defaults(const sg_shader_desc& desc) { return sg_query_shader_defaults(&desc); } +inline sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_desc& desc) { return sg_query_pipeline_defaults(&desc); } +inline sg_pass_desc sg_query_pass_defaults(const sg_pass_desc& desc) { return sg_query_pass_defaults(&desc); } + +inline void sg_init_buffer(sg_buffer buf_id, const sg_buffer_desc& desc) { return sg_init_buffer(buf_id, &desc); } +inline void sg_init_image(sg_image img_id, const sg_image_desc& desc) { return sg_init_image(img_id, &desc); } +inline void sg_init_shader(sg_shader shd_id, const sg_shader_desc& desc) { return sg_init_shader(shd_id, &desc); } +inline void sg_init_pipeline(sg_pipeline pip_id, const sg_pipeline_desc& desc) { return sg_init_pipeline(pip_id, &desc); } +inline void sg_init_pass(sg_pass pass_id, const sg_pass_desc& desc) { return sg_init_pass(pass_id, &desc); } + +inline void sg_update_buffer(sg_buffer buf_id, const sg_range& data) { return sg_update_buffer(buf_id, &data); } +inline int sg_append_buffer(sg_buffer buf_id, const sg_range& data) { return sg_append_buffer(buf_id, &data); } +#endif +#endif // SOKOL_GFX_INCLUDED + +/*--- IMPLEMENTATION ---------------------------------------------------------*/ +#ifdef SOKOL_GFX_IMPL +#define SOKOL_GFX_IMPL_INCLUDED (1) + +#if !(defined(SOKOL_GLCORE33)||defined(SOKOL_GLES2)||defined(SOKOL_GLES3)||defined(SOKOL_D3D11)||defined(SOKOL_METAL)||defined(SOKOL_WGPU)||defined(SOKOL_DUMMY_BACKEND)) +#error "Please select a backend with SOKOL_GLCORE33, SOKOL_GLES2, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU or SOKOL_DUMMY_BACKEND" +#endif +#include /* memset */ +#include /* FLT_MAX */ + +#ifndef SOKOL_API_IMPL + #define SOKOL_API_IMPL +#endif +#ifndef SOKOL_DEBUG + #ifndef NDEBUG + #define SOKOL_DEBUG (1) + #endif +#endif +#ifndef SOKOL_ASSERT + #include + #define SOKOL_ASSERT(c) assert(c) +#endif +#ifndef SOKOL_VALIDATE_BEGIN + #define SOKOL_VALIDATE_BEGIN() _sg_validate_begin() +#endif +#ifndef SOKOL_VALIDATE + #define SOKOL_VALIDATE(cond, err) _sg_validate((cond), err) +#endif +#ifndef SOKOL_VALIDATE_END + #define SOKOL_VALIDATE_END() _sg_validate_end() +#endif +#ifndef SOKOL_UNREACHABLE + #define SOKOL_UNREACHABLE SOKOL_ASSERT(false) +#endif +#ifndef SOKOL_MALLOC + #include + #define SOKOL_MALLOC(s) malloc(s) + #define SOKOL_FREE(p) free(p) +#endif +#ifndef SOKOL_LOG + #ifdef SOKOL_DEBUG + #include + #define SOKOL_LOG(s) { SOKOL_ASSERT(s); puts(s); } + #else + #define SOKOL_LOG(s) + #endif +#endif + +#ifndef _SOKOL_PRIVATE + #if defined(__GNUC__) || defined(__clang__) + #define _SOKOL_PRIVATE __attribute__((unused)) static + #else + #define _SOKOL_PRIVATE static + #endif +#endif + +#ifndef _SOKOL_UNUSED + #define _SOKOL_UNUSED(x) (void)(x) +#endif + +#if defined(SOKOL_TRACE_HOOKS) +#define _SG_TRACE_ARGS(fn, ...) if (_sg.hooks.fn) { _sg.hooks.fn(__VA_ARGS__, _sg.hooks.user_data); } +#define _SG_TRACE_NOARGS(fn) if (_sg.hooks.fn) { _sg.hooks.fn(_sg.hooks.user_data); } +#else +#define _SG_TRACE_ARGS(fn, ...) +#define _SG_TRACE_NOARGS(fn) +#endif + +/* default clear values */ +#ifndef SG_DEFAULT_CLEAR_RED +#define SG_DEFAULT_CLEAR_RED (0.5f) +#endif +#ifndef SG_DEFAULT_CLEAR_GREEN +#define SG_DEFAULT_CLEAR_GREEN (0.5f) +#endif +#ifndef SG_DEFAULT_CLEAR_BLUE +#define SG_DEFAULT_CLEAR_BLUE (0.5f) +#endif +#ifndef SG_DEFAULT_CLEAR_ALPHA +#define SG_DEFAULT_CLEAR_ALPHA (1.0f) +#endif +#ifndef SG_DEFAULT_CLEAR_DEPTH +#define SG_DEFAULT_CLEAR_DEPTH (1.0f) +#endif +#ifndef SG_DEFAULT_CLEAR_STENCIL +#define SG_DEFAULT_CLEAR_STENCIL (0) +#endif + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4115) /* named type definition in parentheses */ +#pragma warning(disable:4505) /* unreferenced local function has been removed */ +#pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union (needed by d3d11.h) */ +#pragma warning(disable:4054) /* 'type cast': from function pointer */ +#pragma warning(disable:4055) /* 'type cast': from data pointer */ +#endif + +#if defined(SOKOL_D3D11) + #ifndef D3D11_NO_HELPERS + #define D3D11_NO_HELPERS + #endif + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #ifndef NOMINMAX + #define NOMINMAX + #endif + #include + #include + #ifdef _MSC_VER + #if (defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) + #pragma comment (lib, "WindowsApp") + #else + #pragma comment (lib, "kernel32") + #pragma comment (lib, "user32") + #pragma comment (lib, "dxgi") + #pragma comment (lib, "d3d11") + #pragma comment (lib, "dxguid") + #endif + #endif +#elif defined(SOKOL_METAL) + // see https://clang.llvm.org/docs/LanguageExtensions.html#automatic-reference-counting + #if !defined(__cplusplus) + #if __has_feature(objc_arc) && !__has_feature(objc_arc_fields) + #error "sokol_gfx.h requires __has_feature(objc_arc_field) if ARC is enabled (use a more recent compiler version)" + #endif + #endif + #include + #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE + #define _SG_TARGET_MACOS (1) + #else + #define _SG_TARGET_IOS (1) + #if defined(TARGET_IPHONE_SIMULATOR) && TARGET_IPHONE_SIMULATOR + #define _SG_TARGET_IOS_SIMULATOR (1) + #endif + #endif + #import +#elif defined(SOKOL_WGPU) + #if defined(__EMSCRIPTEN__) + #include + #else + #include + #endif +#elif defined(SOKOL_GLCORE33) || defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + #define _SOKOL_ANY_GL (1) + + // include platform specific GL headers (or on Win32: use an embedded GL loader) + #if !defined(SOKOL_EXTERNAL_GL_LOADER) + #if defined(_WIN32) + #if defined(SOKOL_GLCORE33) && !defined(SOKOL_EXTERNAL_GL_LOADER) + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #ifndef NOMINMAX + #define NOMINMAX + #endif + #include + #define _SOKOL_USE_WIN32_GL_LOADER (1) + #pragma comment (lib, "kernel32") // GetProcAddress() + #endif + #elif defined(__APPLE__) + #include + #ifndef GL_SILENCE_DEPRECATION + #define GL_SILENCE_DEPRECATION + #endif + #if defined(TARGET_OS_IPHONE) && !TARGET_OS_IPHONE + #include + #else + #include + #include + #endif + #elif defined(__EMSCRIPTEN__) || defined(__ANDROID__) + #if defined(SOKOL_GLES3) + #include + #elif defined(SOKOL_GLES2) + #ifndef GL_EXT_PROTOTYPES + #define GL_GLEXT_PROTOTYPES + #endif + #include + #include + #endif + #elif defined(__linux__) || defined(__unix__) + #define GL_GLEXT_PROTOTYPES + #include + #endif + #endif + + // optional GL loader definitions (only on Win32) + #if defined(_SOKOL_USE_WIN32_GL_LOADER) + #define __gl_h_ 1 + #define __gl32_h_ 1 + #define __gl31_h_ 1 + #define __GL_H__ 1 + #define __glext_h_ 1 + #define __GLEXT_H_ 1 + #define __gltypes_h_ 1 + #define __glcorearb_h_ 1 + #define __gl_glcorearb_h_ 1 + #define GL_APIENTRY APIENTRY + + typedef unsigned int GLenum; + typedef unsigned int GLuint; + typedef int GLsizei; + typedef char GLchar; + typedef ptrdiff_t GLintptr; + typedef ptrdiff_t GLsizeiptr; + typedef double GLclampd; + typedef unsigned short GLushort; + typedef unsigned char GLubyte; + typedef unsigned char GLboolean; + typedef uint64_t GLuint64; + typedef double GLdouble; + typedef unsigned short GLhalf; + typedef float GLclampf; + typedef unsigned int GLbitfield; + typedef signed char GLbyte; + typedef short GLshort; + typedef void GLvoid; + typedef int64_t GLint64; + typedef float GLfloat; + typedef struct __GLsync * GLsync; + typedef int GLint; + #define GL_INT_2_10_10_10_REV 0x8D9F + #define GL_R32F 0x822E + #define GL_PROGRAM_POINT_SIZE 0x8642 + #define GL_STENCIL_ATTACHMENT 0x8D20 + #define GL_DEPTH_ATTACHMENT 0x8D00 + #define GL_COLOR_ATTACHMENT2 0x8CE2 + #define GL_COLOR_ATTACHMENT0 0x8CE0 + #define GL_R16F 0x822D + #define GL_COLOR_ATTACHMENT22 0x8CF6 + #define GL_DRAW_FRAMEBUFFER 0x8CA9 + #define GL_FRAMEBUFFER_COMPLETE 0x8CD5 + #define GL_NUM_EXTENSIONS 0x821D + #define GL_INFO_LOG_LENGTH 0x8B84 + #define GL_VERTEX_SHADER 0x8B31 + #define GL_INCR 0x1E02 + #define GL_DYNAMIC_DRAW 0x88E8 + #define GL_STATIC_DRAW 0x88E4 + #define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 + #define GL_TEXTURE_CUBE_MAP 0x8513 + #define GL_FUNC_SUBTRACT 0x800A + #define GL_FUNC_REVERSE_SUBTRACT 0x800B + #define GL_CONSTANT_COLOR 0x8001 + #define GL_DECR_WRAP 0x8508 + #define GL_R8 0x8229 + #define GL_LINEAR_MIPMAP_LINEAR 0x2703 + #define GL_ELEMENT_ARRAY_BUFFER 0x8893 + #define GL_SHORT 0x1402 + #define GL_DEPTH_TEST 0x0B71 + #define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 + #define GL_LINK_STATUS 0x8B82 + #define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 + #define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E + #define GL_RGBA16F 0x881A + #define GL_CONSTANT_ALPHA 0x8003 + #define GL_READ_FRAMEBUFFER 0x8CA8 + #define GL_TEXTURE0 0x84C0 + #define GL_TEXTURE_MIN_LOD 0x813A + #define GL_CLAMP_TO_EDGE 0x812F + #define GL_UNSIGNED_SHORT_5_6_5 0x8363 + #define GL_TEXTURE_WRAP_R 0x8072 + #define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 + #define GL_NEAREST_MIPMAP_NEAREST 0x2700 + #define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 + #define GL_SRC_ALPHA_SATURATE 0x0308 + #define GL_STREAM_DRAW 0x88E0 + #define GL_ONE 1 + #define GL_NEAREST_MIPMAP_LINEAR 0x2702 + #define GL_RGB10_A2 0x8059 + #define GL_RGBA8 0x8058 + #define GL_COLOR_ATTACHMENT1 0x8CE1 + #define GL_RGBA4 0x8056 + #define GL_RGB8 0x8051 + #define GL_ARRAY_BUFFER 0x8892 + #define GL_STENCIL 0x1802 + #define GL_TEXTURE_2D 0x0DE1 + #define GL_DEPTH 0x1801 + #define GL_FRONT 0x0404 + #define GL_STENCIL_BUFFER_BIT 0x00000400 + #define GL_REPEAT 0x2901 + #define GL_RGBA 0x1908 + #define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 + #define GL_DECR 0x1E03 + #define GL_FRAGMENT_SHADER 0x8B30 + #define GL_FLOAT 0x1406 + #define GL_TEXTURE_MAX_LOD 0x813B + #define GL_DEPTH_COMPONENT 0x1902 + #define GL_ONE_MINUS_DST_ALPHA 0x0305 + #define GL_COLOR 0x1800 + #define GL_TEXTURE_2D_ARRAY 0x8C1A + #define GL_TRIANGLES 0x0004 + #define GL_UNSIGNED_BYTE 0x1401 + #define GL_TEXTURE_MAG_FILTER 0x2800 + #define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 + #define GL_NONE 0 + #define GL_SRC_COLOR 0x0300 + #define GL_BYTE 0x1400 + #define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A + #define GL_LINE_STRIP 0x0003 + #define GL_TEXTURE_3D 0x806F + #define GL_CW 0x0900 + #define GL_LINEAR 0x2601 + #define GL_RENDERBUFFER 0x8D41 + #define GL_GEQUAL 0x0206 + #define GL_COLOR_BUFFER_BIT 0x00004000 + #define GL_RGBA32F 0x8814 + #define GL_BLEND 0x0BE2 + #define GL_ONE_MINUS_SRC_ALPHA 0x0303 + #define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 + #define GL_TEXTURE_WRAP_T 0x2803 + #define GL_TEXTURE_WRAP_S 0x2802 + #define GL_TEXTURE_MIN_FILTER 0x2801 + #define GL_LINEAR_MIPMAP_NEAREST 0x2701 + #define GL_EXTENSIONS 0x1F03 + #define GL_NO_ERROR 0 + #define GL_REPLACE 0x1E01 + #define GL_KEEP 0x1E00 + #define GL_CCW 0x0901 + #define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 + #define GL_RGB 0x1907 + #define GL_TRIANGLE_STRIP 0x0005 + #define GL_FALSE 0 + #define GL_ZERO 0 + #define GL_CULL_FACE 0x0B44 + #define GL_INVERT 0x150A + #define GL_INT 0x1404 + #define GL_UNSIGNED_INT 0x1405 + #define GL_UNSIGNED_SHORT 0x1403 + #define GL_NEAREST 0x2600 + #define GL_SCISSOR_TEST 0x0C11 + #define GL_LEQUAL 0x0203 + #define GL_STENCIL_TEST 0x0B90 + #define GL_DITHER 0x0BD0 + #define GL_DEPTH_COMPONENT16 0x81A5 + #define GL_EQUAL 0x0202 + #define GL_FRAMEBUFFER 0x8D40 + #define GL_RGB5 0x8050 + #define GL_LINES 0x0001 + #define GL_DEPTH_BUFFER_BIT 0x00000100 + #define GL_SRC_ALPHA 0x0302 + #define GL_INCR_WRAP 0x8507 + #define GL_LESS 0x0201 + #define GL_MULTISAMPLE 0x809D + #define GL_FRAMEBUFFER_BINDING 0x8CA6 + #define GL_BACK 0x0405 + #define GL_ALWAYS 0x0207 + #define GL_FUNC_ADD 0x8006 + #define GL_ONE_MINUS_DST_COLOR 0x0307 + #define GL_NOTEQUAL 0x0205 + #define GL_DST_COLOR 0x0306 + #define GL_COMPILE_STATUS 0x8B81 + #define GL_RED 0x1903 + #define GL_COLOR_ATTACHMENT3 0x8CE3 + #define GL_DST_ALPHA 0x0304 + #define GL_RGB5_A1 0x8057 + #define GL_GREATER 0x0204 + #define GL_POLYGON_OFFSET_FILL 0x8037 + #define GL_TRUE 1 + #define GL_NEVER 0x0200 + #define GL_POINTS 0x0000 + #define GL_ONE_MINUS_SRC_COLOR 0x0301 + #define GL_MIRRORED_REPEAT 0x8370 + #define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D + #define GL_R11F_G11F_B10F 0x8C3A + #define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B + #define GL_RGBA32UI 0x8D70 + #define GL_RGB32UI 0x8D71 + #define GL_RGBA16UI 0x8D76 + #define GL_RGB16UI 0x8D77 + #define GL_RGBA8UI 0x8D7C + #define GL_RGB8UI 0x8D7D + #define GL_RGBA32I 0x8D82 + #define GL_RGB32I 0x8D83 + #define GL_RGBA16I 0x8D88 + #define GL_RGB16I 0x8D89 + #define GL_RGBA8I 0x8D8E + #define GL_RGB8I 0x8D8F + #define GL_RED_INTEGER 0x8D94 + #define GL_RG 0x8227 + #define GL_RG_INTEGER 0x8228 + #define GL_R8 0x8229 + #define GL_R16 0x822A + #define GL_RG8 0x822B + #define GL_RG16 0x822C + #define GL_R16F 0x822D + #define GL_R32F 0x822E + #define GL_RG16F 0x822F + #define GL_RG32F 0x8230 + #define GL_R8I 0x8231 + #define GL_R8UI 0x8232 + #define GL_R16I 0x8233 + #define GL_R16UI 0x8234 + #define GL_R32I 0x8235 + #define GL_R32UI 0x8236 + #define GL_RG8I 0x8237 + #define GL_RG8UI 0x8238 + #define GL_RG16I 0x8239 + #define GL_RG16UI 0x823A + #define GL_RG32I 0x823B + #define GL_RG32UI 0x823C + #define GL_RGBA_INTEGER 0x8D99 + #define GL_R8_SNORM 0x8F94 + #define GL_RG8_SNORM 0x8F95 + #define GL_RGB8_SNORM 0x8F96 + #define GL_RGBA8_SNORM 0x8F97 + #define GL_R16_SNORM 0x8F98 + #define GL_RG16_SNORM 0x8F99 + #define GL_RGB16_SNORM 0x8F9A + #define GL_RGBA16_SNORM 0x8F9B + #define GL_RGBA16 0x805B + #define GL_MAX_TEXTURE_SIZE 0x0D33 + #define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C + #define GL_MAX_3D_TEXTURE_SIZE 0x8073 + #define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF + #define GL_MAX_VERTEX_ATTRIBS 0x8869 + #define GL_CLAMP_TO_BORDER 0x812D + #define GL_TEXTURE_BORDER_COLOR 0x1004 + #define GL_CURRENT_PROGRAM 0x8B8D + #endif + + #ifndef GL_UNSIGNED_INT_2_10_10_10_REV + #define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 + #endif + #ifndef GL_UNSIGNED_INT_24_8 + #define GL_UNSIGNED_INT_24_8 0x84FA + #endif + #ifndef GL_TEXTURE_MAX_ANISOTROPY_EXT + #define GL_TEXTURE_MAX_ANISOTROPY_EXT 0x84FE + #endif + #ifndef GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT + #define GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT 0x84FF + #endif + #ifndef GL_COMPRESSED_RGBA_S3TC_DXT1_EXT + #define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT 0x83F1 + #endif + #ifndef GL_COMPRESSED_RGBA_S3TC_DXT3_EXT + #define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT 0x83F2 + #endif + #ifndef GL_COMPRESSED_RGBA_S3TC_DXT5_EXT + #define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 + #endif + #ifndef GL_COMPRESSED_RED_RGTC1 + #define GL_COMPRESSED_RED_RGTC1 0x8DBB + #endif + #ifndef GL_COMPRESSED_SIGNED_RED_RGTC1 + #define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC + #endif + #ifndef GL_COMPRESSED_RED_GREEN_RGTC2 + #define GL_COMPRESSED_RED_GREEN_RGTC2 0x8DBD + #endif + #ifndef GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2 + #define GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2 0x8DBE + #endif + #ifndef GL_COMPRESSED_RGBA_BPTC_UNORM_ARB + #define GL_COMPRESSED_RGBA_BPTC_UNORM_ARB 0x8E8C + #endif + #ifndef GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB + #define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB 0x8E8D + #endif + #ifndef GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB + #define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB 0x8E8E + #endif + #ifndef GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB + #define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB 0x8E8F + #endif + #ifndef GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG + #define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG 0x8C01 + #endif + #ifndef GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG + #define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG 0x8C00 + #endif + #ifndef GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG + #define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG 0x8C03 + #endif + #ifndef GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG + #define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG 0x8C02 + #endif + #ifndef GL_COMPRESSED_RGB8_ETC2 + #define GL_COMPRESSED_RGB8_ETC2 0x9274 + #endif + #ifndef GL_COMPRESSED_RGBA8_ETC2_EAC + #define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 + #endif + #ifndef GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 + #define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 + #endif + #ifndef GL_COMPRESSED_RG11_EAC + #define GL_COMPRESSED_RG11_EAC 0x9272 + #endif + #ifndef GL_COMPRESSED_SIGNED_RG11_EAC + #define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 + #endif + #ifndef GL_DEPTH24_STENCIL8 + #define GL_DEPTH24_STENCIL8 0x88F0 + #endif + #ifndef GL_HALF_FLOAT + #define GL_HALF_FLOAT 0x140B + #endif + #ifndef GL_DEPTH_STENCIL + #define GL_DEPTH_STENCIL 0x84F9 + #endif + #ifndef GL_LUMINANCE + #define GL_LUMINANCE 0x1909 + #endif + + #ifdef SOKOL_GLES2 + #ifdef GL_ANGLE_instanced_arrays + #define _SOKOL_GL_INSTANCING_ENABLED + #define glDrawArraysInstanced(mode, first, count, instancecount) glDrawArraysInstancedANGLE(mode, first, count, instancecount) + #define glDrawElementsInstanced(mode, count, type, indices, instancecount) glDrawElementsInstancedANGLE(mode, count, type, indices, instancecount) + #define glVertexAttribDivisor(index, divisor) glVertexAttribDivisorANGLE(index, divisor) + #elif defined(GL_EXT_draw_instanced) && defined(GL_EXT_instanced_arrays) + #define _SOKOL_GL_INSTANCING_ENABLED + #define glDrawArraysInstanced(mode, first, count, instancecount) glDrawArraysInstancedEXT(mode, first, count, instancecount) + #define glDrawElementsInstanced(mode, count, type, indices, instancecount) glDrawElementsInstancedEXT(mode, count, type, indices, instancecount) + #define glVertexAttribDivisor(index, divisor) glVertexAttribDivisorEXT(index, divisor) + #else + #define _SOKOL_GLES2_INSTANCING_ERROR "Select GL_ANGLE_instanced_arrays or (GL_EXT_draw_instanced & GL_EXT_instanced_arrays) to enable instancing in GLES2" + #define glDrawArraysInstanced(mode, first, count, instancecount) SOKOL_ASSERT(0 && _SOKOL_GLES2_INSTANCING_ERROR) + #define glDrawElementsInstanced(mode, count, type, indices, instancecount) SOKOL_ASSERT(0 && _SOKOL_GLES2_INSTANCING_ERROR) + #define glVertexAttribDivisor(index, divisor) SOKOL_ASSERT(0 && _SOKOL_GLES2_INSTANCING_ERROR) + #endif + #else + #define _SOKOL_GL_INSTANCING_ENABLED + #endif + #define _SG_GL_CHECK_ERROR() { SOKOL_ASSERT(glGetError() == GL_NO_ERROR); } +#endif + +/*=== COMMON BACKEND STUFF ===================================================*/ + +/* resource pool slots */ +typedef struct { + uint32_t id; + uint32_t ctx_id; + sg_resource_state state; +} _sg_slot_t; + +/* constants */ +enum { + _SG_STRING_SIZE = 16, + _SG_SLOT_SHIFT = 16, + _SG_SLOT_MASK = (1<<_SG_SLOT_SHIFT)-1, + _SG_MAX_POOL_SIZE = (1<<_SG_SLOT_SHIFT), + _SG_DEFAULT_BUFFER_POOL_SIZE = 128, + _SG_DEFAULT_IMAGE_POOL_SIZE = 128, + _SG_DEFAULT_SHADER_POOL_SIZE = 32, + _SG_DEFAULT_PIPELINE_POOL_SIZE = 64, + _SG_DEFAULT_PASS_POOL_SIZE = 16, + _SG_DEFAULT_CONTEXT_POOL_SIZE = 16, + _SG_DEFAULT_SAMPLER_CACHE_CAPACITY = 64, + _SG_DEFAULT_UB_SIZE = 4 * 1024 * 1024, + _SG_DEFAULT_STAGING_SIZE = 8 * 1024 * 1024, +}; + +/* fixed-size string */ +typedef struct { + char buf[_SG_STRING_SIZE]; +} _sg_str_t; + +/* helper macros */ +#define _sg_def(val, def) (((val) == 0) ? (def) : (val)) +#define _sg_def_flt(val, def) (((val) == 0.0f) ? (def) : (val)) +#define _sg_min(a,b) ((ab)?a:b) +#define _sg_clamp(v,v0,v1) ((vv1)?(v1):(v))) +#define _sg_fequal(val,cmp,delta) (((val-cmp)> -delta)&&((val-cmp)size = (int)desc->size; + cmn->append_pos = 0; + cmn->append_overflow = false; + cmn->type = desc->type; + cmn->usage = desc->usage; + cmn->update_frame_index = 0; + cmn->append_frame_index = 0; + cmn->num_slots = (cmn->usage == SG_USAGE_IMMUTABLE) ? 1 : SG_NUM_INFLIGHT_FRAMES; + cmn->active_slot = 0; +} + +typedef struct { + sg_image_type type; + bool render_target; + int width; + int height; + int num_slices; + int num_mipmaps; + sg_usage usage; + sg_pixel_format pixel_format; + int sample_count; + sg_filter min_filter; + sg_filter mag_filter; + sg_wrap wrap_u; + sg_wrap wrap_v; + sg_wrap wrap_w; + sg_border_color border_color; + uint32_t max_anisotropy; + uint32_t upd_frame_index; + int num_slots; + int active_slot; +} _sg_image_common_t; + +_SOKOL_PRIVATE void _sg_image_common_init(_sg_image_common_t* cmn, const sg_image_desc* desc) { + cmn->type = desc->type; + cmn->render_target = desc->render_target; + cmn->width = desc->width; + cmn->height = desc->height; + cmn->num_slices = desc->num_slices; + cmn->num_mipmaps = desc->num_mipmaps; + cmn->usage = desc->usage; + cmn->pixel_format = desc->pixel_format; + cmn->sample_count = desc->sample_count; + cmn->min_filter = desc->min_filter; + cmn->mag_filter = desc->mag_filter; + cmn->wrap_u = desc->wrap_u; + cmn->wrap_v = desc->wrap_v; + cmn->wrap_w = desc->wrap_w; + cmn->border_color = desc->border_color; + cmn->max_anisotropy = desc->max_anisotropy; + cmn->upd_frame_index = 0; + cmn->num_slots = (cmn->usage == SG_USAGE_IMMUTABLE) ? 1 : SG_NUM_INFLIGHT_FRAMES; + cmn->active_slot = 0; +} + +typedef struct { + size_t size; +} _sg_uniform_block_t; + +typedef struct { + sg_image_type image_type; + sg_sampler_type sampler_type; +} _sg_shader_image_t; + +typedef struct { + int num_uniform_blocks; + int num_images; + _sg_uniform_block_t uniform_blocks[SG_MAX_SHADERSTAGE_UBS]; + _sg_shader_image_t images[SG_MAX_SHADERSTAGE_IMAGES]; +} _sg_shader_stage_t; + +typedef struct { + _sg_shader_stage_t stage[SG_NUM_SHADER_STAGES]; +} _sg_shader_common_t; + +_SOKOL_PRIVATE void _sg_shader_common_init(_sg_shader_common_t* cmn, const sg_shader_desc* desc) { + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS) ? &desc->vs : &desc->fs; + _sg_shader_stage_t* stage = &cmn->stage[stage_index]; + SOKOL_ASSERT(stage->num_uniform_blocks == 0); + for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { + const sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; + if (0 == ub_desc->size) { + break; + } + stage->uniform_blocks[ub_index].size = ub_desc->size; + stage->num_uniform_blocks++; + } + SOKOL_ASSERT(stage->num_images == 0); + for (int img_index = 0; img_index < SG_MAX_SHADERSTAGE_IMAGES; img_index++) { + const sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; + if (img_desc->image_type == _SG_IMAGETYPE_DEFAULT) { + break; + } + stage->images[img_index].image_type = img_desc->image_type; + stage->images[img_index].sampler_type = img_desc->sampler_type; + stage->num_images++; + } + } +} + +typedef struct { + sg_shader shader_id; + sg_index_type index_type; + bool vertex_layout_valid[SG_MAX_SHADERSTAGE_BUFFERS]; + int color_attachment_count; + sg_pixel_format color_formats[SG_MAX_COLOR_ATTACHMENTS]; + sg_pixel_format depth_format; + int sample_count; + float depth_bias; + float depth_bias_slope_scale; + float depth_bias_clamp; + sg_color blend_color; +} _sg_pipeline_common_t; + +_SOKOL_PRIVATE void _sg_pipeline_common_init(_sg_pipeline_common_t* cmn, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(desc->color_count < SG_MAX_COLOR_ATTACHMENTS); + cmn->shader_id = desc->shader; + cmn->index_type = desc->index_type; + for (int i = 0; i < SG_MAX_SHADERSTAGE_BUFFERS; i++) { + cmn->vertex_layout_valid[i] = false; + } + cmn->color_attachment_count = desc->color_count; + for (int i = 0; i < cmn->color_attachment_count; i++) { + cmn->color_formats[i] = desc->colors[i].pixel_format; + } + cmn->depth_format = desc->depth.pixel_format; + cmn->sample_count = desc->sample_count; + cmn->depth_bias = desc->depth.bias; + cmn->depth_bias_slope_scale = desc->depth.bias_slope_scale; + cmn->depth_bias_clamp = desc->depth.bias_clamp; + cmn->blend_color = desc->blend_color; +} + +typedef struct { + sg_image image_id; + int mip_level; + int slice; +} _sg_pass_attachment_common_t; + +typedef struct { + int num_color_atts; + _sg_pass_attachment_common_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; + _sg_pass_attachment_common_t ds_att; +} _sg_pass_common_t; + +_SOKOL_PRIVATE void _sg_pass_common_init(_sg_pass_common_t* cmn, const sg_pass_desc* desc) { + const sg_pass_attachment_desc* att_desc; + _sg_pass_attachment_common_t* att; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + att_desc = &desc->color_attachments[i]; + if (att_desc->image.id != SG_INVALID_ID) { + cmn->num_color_atts++; + att = &cmn->color_atts[i]; + att->image_id = att_desc->image; + att->mip_level = att_desc->mip_level; + att->slice = att_desc->slice; + } + } + att_desc = &desc->depth_stencil_attachment; + if (att_desc->image.id != SG_INVALID_ID) { + att = &cmn->ds_att; + att->image_id = att_desc->image; + att->mip_level = att_desc->mip_level; + att->slice = att_desc->slice; + } +} + +/*=== GENERIC SAMPLER CACHE ==================================================*/ + +/* + this is used by the Metal and WGPU backends to reduce the + number of sampler state objects created through the backend API +*/ +typedef struct { + sg_filter min_filter; + sg_filter mag_filter; + sg_wrap wrap_u; + sg_wrap wrap_v; + sg_wrap wrap_w; + sg_border_color border_color; + uint32_t max_anisotropy; + int min_lod; /* orig min/max_lod is float, this is int(min/max_lod*1000.0) */ + int max_lod; + uintptr_t sampler_handle; +} _sg_sampler_cache_item_t; + +typedef struct { + int capacity; + int num_items; + _sg_sampler_cache_item_t* items; +} _sg_sampler_cache_t; + +_SOKOL_PRIVATE void _sg_smpcache_init(_sg_sampler_cache_t* cache, int capacity) { + SOKOL_ASSERT(cache && (capacity > 0)); + memset(cache, 0, sizeof(_sg_sampler_cache_t)); + cache->capacity = capacity; + const size_t size = (size_t)cache->capacity * sizeof(_sg_sampler_cache_item_t); + cache->items = (_sg_sampler_cache_item_t*) SOKOL_MALLOC(size); + SOKOL_ASSERT(cache->items); + memset(cache->items, 0, size); +} + +_SOKOL_PRIVATE void _sg_smpcache_discard(_sg_sampler_cache_t* cache) { + SOKOL_ASSERT(cache && cache->items); + SOKOL_FREE(cache->items); + cache->items = 0; + cache->num_items = 0; + cache->capacity = 0; +} + +_SOKOL_PRIVATE int _sg_smpcache_minlod_int(float min_lod) { + return (int) (min_lod * 1000.0f); +} + +_SOKOL_PRIVATE int _sg_smpcache_maxlod_int(float max_lod) { + return (int) (_sg_clamp(max_lod, 0.0f, 1000.0f) * 1000.0f); +} + +_SOKOL_PRIVATE int _sg_smpcache_find_item(const _sg_sampler_cache_t* cache, const sg_image_desc* img_desc) { + /* return matching sampler cache item index or -1 */ + SOKOL_ASSERT(cache && cache->items); + SOKOL_ASSERT(img_desc); + const int min_lod = _sg_smpcache_minlod_int(img_desc->min_lod); + const int max_lod = _sg_smpcache_maxlod_int(img_desc->max_lod); + for (int i = 0; i < cache->num_items; i++) { + const _sg_sampler_cache_item_t* item = &cache->items[i]; + if ((img_desc->min_filter == item->min_filter) && + (img_desc->mag_filter == item->mag_filter) && + (img_desc->wrap_u == item->wrap_u) && + (img_desc->wrap_v == item->wrap_v) && + (img_desc->wrap_w == item->wrap_w) && + (img_desc->max_anisotropy == item->max_anisotropy) && + (img_desc->border_color == item->border_color) && + (min_lod == item->min_lod) && + (max_lod == item->max_lod)) + { + return i; + } + } + /* fallthrough: no matching cache item found */ + return -1; +} + +_SOKOL_PRIVATE void _sg_smpcache_add_item(_sg_sampler_cache_t* cache, const sg_image_desc* img_desc, uintptr_t sampler_handle) { + SOKOL_ASSERT(cache && cache->items); + SOKOL_ASSERT(img_desc); + SOKOL_ASSERT(cache->num_items < cache->capacity); + const int item_index = cache->num_items++; + _sg_sampler_cache_item_t* item = &cache->items[item_index]; + item->min_filter = img_desc->min_filter; + item->mag_filter = img_desc->mag_filter; + item->wrap_u = img_desc->wrap_u; + item->wrap_v = img_desc->wrap_v; + item->wrap_w = img_desc->wrap_w; + item->border_color = img_desc->border_color; + item->max_anisotropy = img_desc->max_anisotropy; + item->min_lod = _sg_smpcache_minlod_int(img_desc->min_lod); + item->max_lod = _sg_smpcache_maxlod_int(img_desc->max_lod); + item->sampler_handle = sampler_handle; +} + +_SOKOL_PRIVATE uintptr_t _sg_smpcache_sampler(_sg_sampler_cache_t* cache, int item_index) { + SOKOL_ASSERT(cache && cache->items); + SOKOL_ASSERT(item_index < cache->num_items); + return cache->items[item_index].sampler_handle; +} + +/*=== DUMMY BACKEND DECLARATIONS =============================================*/ +#if defined(SOKOL_DUMMY_BACKEND) +typedef struct { + _sg_slot_t slot; + _sg_buffer_common_t cmn; +} _sg_dummy_buffer_t; +typedef _sg_dummy_buffer_t _sg_buffer_t; + +typedef struct { + _sg_slot_t slot; + _sg_image_common_t cmn; +} _sg_dummy_image_t; +typedef _sg_dummy_image_t _sg_image_t; + +typedef struct { + _sg_slot_t slot; + _sg_shader_common_t cmn; +} _sg_dummy_shader_t; +typedef _sg_dummy_shader_t _sg_shader_t; + +typedef struct { + _sg_slot_t slot; + _sg_shader_t* shader; + _sg_pipeline_common_t cmn; +} _sg_dummy_pipeline_t; +typedef _sg_dummy_pipeline_t _sg_pipeline_t; + +typedef struct { + _sg_image_t* image; +} _sg_dummy_attachment_t; + +typedef struct { + _sg_slot_t slot; + _sg_pass_common_t cmn; + struct { + _sg_dummy_attachment_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; + _sg_dummy_attachment_t ds_att; + } dmy; +} _sg_dummy_pass_t; +typedef _sg_dummy_pass_t _sg_pass_t; +typedef _sg_pass_attachment_common_t _sg_pass_attachment_t; + +typedef struct { + _sg_slot_t slot; +} _sg_dummy_context_t; +typedef _sg_dummy_context_t _sg_context_t; + +/*== GL BACKEND DECLARATIONS =================================================*/ +#elif defined(_SOKOL_ANY_GL) +typedef struct { + _sg_slot_t slot; + _sg_buffer_common_t cmn; + struct { + GLuint buf[SG_NUM_INFLIGHT_FRAMES]; + bool ext_buffers; /* if true, external buffers were injected with sg_buffer_desc.gl_buffers */ + } gl; +} _sg_gl_buffer_t; +typedef _sg_gl_buffer_t _sg_buffer_t; + +typedef struct { + _sg_slot_t slot; + _sg_image_common_t cmn; + struct { + GLenum target; + GLuint depth_render_buffer; + GLuint msaa_render_buffer; + GLuint tex[SG_NUM_INFLIGHT_FRAMES]; + bool ext_textures; /* if true, external textures were injected with sg_image_desc.gl_textures */ + } gl; +} _sg_gl_image_t; +typedef _sg_gl_image_t _sg_image_t; + +typedef struct { + GLint gl_loc; + sg_uniform_type type; + uint8_t count; + uint16_t offset; +} _sg_gl_uniform_t; + +typedef struct { + int num_uniforms; + _sg_gl_uniform_t uniforms[SG_MAX_UB_MEMBERS]; +} _sg_gl_uniform_block_t; + +typedef struct { + int gl_tex_slot; +} _sg_gl_shader_image_t; + +typedef struct { + _sg_str_t name; +} _sg_gl_shader_attr_t; + +typedef struct { + _sg_gl_uniform_block_t uniform_blocks[SG_MAX_SHADERSTAGE_UBS]; + _sg_gl_shader_image_t images[SG_MAX_SHADERSTAGE_IMAGES]; +} _sg_gl_shader_stage_t; + +typedef struct { + _sg_slot_t slot; + _sg_shader_common_t cmn; + struct { + GLuint prog; + _sg_gl_shader_attr_t attrs[SG_MAX_VERTEX_ATTRIBUTES]; + _sg_gl_shader_stage_t stage[SG_NUM_SHADER_STAGES]; + } gl; +} _sg_gl_shader_t; +typedef _sg_gl_shader_t _sg_shader_t; + +typedef struct { + int8_t vb_index; /* -1 if attr is not enabled */ + int8_t divisor; /* -1 if not initialized */ + uint8_t stride; + uint8_t size; + uint8_t normalized; + int offset; + GLenum type; +} _sg_gl_attr_t; + +typedef struct { + _sg_slot_t slot; + _sg_pipeline_common_t cmn; + _sg_shader_t* shader; + struct { + _sg_gl_attr_t attrs[SG_MAX_VERTEX_ATTRIBUTES]; + sg_depth_state depth; + sg_stencil_state stencil; + sg_primitive_type primitive_type; + sg_blend_state blend; + sg_color_mask color_write_mask[SG_MAX_COLOR_ATTACHMENTS]; + sg_cull_mode cull_mode; + sg_face_winding face_winding; + int sample_count; + bool alpha_to_coverage_enabled; + } gl; +} _sg_gl_pipeline_t; +typedef _sg_gl_pipeline_t _sg_pipeline_t; + +typedef struct { + _sg_image_t* image; + GLuint gl_msaa_resolve_buffer; +} _sg_gl_attachment_t; + +typedef struct { + _sg_slot_t slot; + _sg_pass_common_t cmn; + struct { + GLuint fb; + _sg_gl_attachment_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; + _sg_gl_attachment_t ds_att; + } gl; +} _sg_gl_pass_t; +typedef _sg_gl_pass_t _sg_pass_t; +typedef _sg_pass_attachment_common_t _sg_pass_attachment_t; + +typedef struct { + _sg_slot_t slot; + #if !defined(SOKOL_GLES2) + GLuint vao; + #endif + GLuint default_framebuffer; +} _sg_gl_context_t; +typedef _sg_gl_context_t _sg_context_t; + +typedef struct { + _sg_gl_attr_t gl_attr; + GLuint gl_vbuf; +} _sg_gl_cache_attr_t; + +typedef struct { + GLenum target; + GLuint texture; +} _sg_gl_texture_bind_slot; + +typedef struct { + sg_depth_state depth; + sg_stencil_state stencil; + sg_blend_state blend; + sg_color_mask color_write_mask[SG_MAX_COLOR_ATTACHMENTS]; + sg_cull_mode cull_mode; + sg_face_winding face_winding; + bool polygon_offset_enabled; + int sample_count; + sg_color blend_color; + bool alpha_to_coverage_enabled; + _sg_gl_cache_attr_t attrs[SG_MAX_VERTEX_ATTRIBUTES]; + GLuint vertex_buffer; + GLuint index_buffer; + GLuint stored_vertex_buffer; + GLuint stored_index_buffer; + GLuint prog; + _sg_gl_texture_bind_slot textures[SG_MAX_SHADERSTAGE_IMAGES]; + _sg_gl_texture_bind_slot stored_texture; + int cur_ib_offset; + GLenum cur_primitive_type; + GLenum cur_index_type; + GLenum cur_active_texture; + _sg_pipeline_t* cur_pipeline; + sg_pipeline cur_pipeline_id; +} _sg_gl_state_cache_t; + +typedef struct { + bool valid; + bool gles2; + bool in_pass; + int cur_pass_width; + int cur_pass_height; + _sg_context_t* cur_context; + _sg_pass_t* cur_pass; + sg_pass cur_pass_id; + _sg_gl_state_cache_t cache; + bool ext_anisotropic; + GLint max_anisotropy; + GLint max_combined_texture_image_units; + #if _SOKOL_USE_WIN32_GL_LOADER + HINSTANCE opengl32_dll; + #endif +} _sg_gl_backend_t; + +/*== D3D11 BACKEND DECLARATIONS ==============================================*/ +#elif defined(SOKOL_D3D11) + +typedef struct { + _sg_slot_t slot; + _sg_buffer_common_t cmn; + struct { + ID3D11Buffer* buf; + } d3d11; +} _sg_d3d11_buffer_t; +typedef _sg_d3d11_buffer_t _sg_buffer_t; + +typedef struct { + _sg_slot_t slot; + _sg_image_common_t cmn; + struct { + DXGI_FORMAT format; + ID3D11Texture2D* tex2d; + ID3D11Texture3D* tex3d; + ID3D11Texture2D* texds; + ID3D11Texture2D* texmsaa; + ID3D11ShaderResourceView* srv; + ID3D11SamplerState* smp; + } d3d11; +} _sg_d3d11_image_t; +typedef _sg_d3d11_image_t _sg_image_t; + +typedef struct { + _sg_str_t sem_name; + int sem_index; +} _sg_d3d11_shader_attr_t; + +typedef struct { + ID3D11Buffer* cbufs[SG_MAX_SHADERSTAGE_UBS]; +} _sg_d3d11_shader_stage_t; + +typedef struct { + _sg_slot_t slot; + _sg_shader_common_t cmn; + struct { + _sg_d3d11_shader_attr_t attrs[SG_MAX_VERTEX_ATTRIBUTES]; + _sg_d3d11_shader_stage_t stage[SG_NUM_SHADER_STAGES]; + ID3D11VertexShader* vs; + ID3D11PixelShader* fs; + void* vs_blob; + size_t vs_blob_length; + } d3d11; +} _sg_d3d11_shader_t; +typedef _sg_d3d11_shader_t _sg_shader_t; + +typedef struct { + _sg_slot_t slot; + _sg_pipeline_common_t cmn; + _sg_shader_t* shader; + struct { + UINT stencil_ref; + UINT vb_strides[SG_MAX_SHADERSTAGE_BUFFERS]; + D3D_PRIMITIVE_TOPOLOGY topology; + DXGI_FORMAT index_format; + ID3D11InputLayout* il; + ID3D11RasterizerState* rs; + ID3D11DepthStencilState* dss; + ID3D11BlendState* bs; + } d3d11; +} _sg_d3d11_pipeline_t; +typedef _sg_d3d11_pipeline_t _sg_pipeline_t; + +typedef struct { + _sg_image_t* image; + ID3D11RenderTargetView* rtv; +} _sg_d3d11_color_attachment_t; + +typedef struct { + _sg_image_t* image; + ID3D11DepthStencilView* dsv; +} _sg_d3d11_ds_attachment_t; + +typedef struct { + _sg_slot_t slot; + _sg_pass_common_t cmn; + struct { + _sg_d3d11_color_attachment_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; + _sg_d3d11_ds_attachment_t ds_att; + } d3d11; +} _sg_d3d11_pass_t; +typedef _sg_d3d11_pass_t _sg_pass_t; +typedef _sg_pass_attachment_common_t _sg_pass_attachment_t; + +typedef struct { + _sg_slot_t slot; +} _sg_d3d11_context_t; +typedef _sg_d3d11_context_t _sg_context_t; + +typedef struct { + bool valid; + ID3D11Device* dev; + ID3D11DeviceContext* ctx; + const void* (*rtv_cb)(void); + const void* (*rtv_userdata_cb)(void*); + const void* (*dsv_cb)(void); + const void* (*dsv_userdata_cb)(void*); + void* user_data; + bool in_pass; + bool use_indexed_draw; + int cur_width; + int cur_height; + int num_rtvs; + _sg_pass_t* cur_pass; + sg_pass cur_pass_id; + _sg_pipeline_t* cur_pipeline; + sg_pipeline cur_pipeline_id; + ID3D11RenderTargetView* cur_rtvs[SG_MAX_COLOR_ATTACHMENTS]; + ID3D11DepthStencilView* cur_dsv; + /* on-demand loaded d3dcompiler_47.dll handles */ + HINSTANCE d3dcompiler_dll; + bool d3dcompiler_dll_load_failed; + pD3DCompile D3DCompile_func; + /* the following arrays are used for unbinding resources, they will always contain zeroes */ + ID3D11RenderTargetView* zero_rtvs[SG_MAX_COLOR_ATTACHMENTS]; + ID3D11Buffer* zero_vbs[SG_MAX_SHADERSTAGE_BUFFERS]; + UINT zero_vb_offsets[SG_MAX_SHADERSTAGE_BUFFERS]; + UINT zero_vb_strides[SG_MAX_SHADERSTAGE_BUFFERS]; + ID3D11Buffer* zero_cbs[SG_MAX_SHADERSTAGE_UBS]; + ID3D11ShaderResourceView* zero_srvs[SG_MAX_SHADERSTAGE_IMAGES]; + ID3D11SamplerState* zero_smps[SG_MAX_SHADERSTAGE_IMAGES]; + /* global subresourcedata array for texture updates */ + D3D11_SUBRESOURCE_DATA subres_data[SG_MAX_MIPMAPS * SG_MAX_TEXTUREARRAY_LAYERS]; +} _sg_d3d11_backend_t; + +/*=== METAL BACKEND DECLARATIONS =============================================*/ +#elif defined(SOKOL_METAL) + +#if defined(_SG_TARGET_MACOS) || defined(_SG_TARGET_IOS_SIMULATOR) +#define _SG_MTL_UB_ALIGN (256) +#else +#define _SG_MTL_UB_ALIGN (16) +#endif +#define _SG_MTL_INVALID_SLOT_INDEX (0) + +typedef struct { + uint32_t frame_index; /* frame index at which it is safe to release this resource */ + int slot_index; +} _sg_mtl_release_item_t; + +typedef struct { + NSMutableArray* pool; + int num_slots; + int free_queue_top; + int* free_queue; + int release_queue_front; + int release_queue_back; + _sg_mtl_release_item_t* release_queue; +} _sg_mtl_idpool_t; + +typedef struct { + _sg_slot_t slot; + _sg_buffer_common_t cmn; + struct { + int buf[SG_NUM_INFLIGHT_FRAMES]; /* index into _sg_mtl_pool */ + } mtl; +} _sg_mtl_buffer_t; +typedef _sg_mtl_buffer_t _sg_buffer_t; + +typedef struct { + _sg_slot_t slot; + _sg_image_common_t cmn; + struct { + int tex[SG_NUM_INFLIGHT_FRAMES]; + int depth_tex; + int msaa_tex; + int sampler_state; + } mtl; +} _sg_mtl_image_t; +typedef _sg_mtl_image_t _sg_image_t; + +typedef struct { + int mtl_lib; + int mtl_func; +} _sg_mtl_shader_stage_t; + +typedef struct { + _sg_slot_t slot; + _sg_shader_common_t cmn; + struct { + _sg_mtl_shader_stage_t stage[SG_NUM_SHADER_STAGES]; + } mtl; +} _sg_mtl_shader_t; +typedef _sg_mtl_shader_t _sg_shader_t; + +typedef struct { + _sg_slot_t slot; + _sg_pipeline_common_t cmn; + _sg_shader_t* shader; + struct { + MTLPrimitiveType prim_type; + int index_size; + MTLIndexType index_type; + MTLCullMode cull_mode; + MTLWinding winding; + uint32_t stencil_ref; + int rps; + int dss; + } mtl; +} _sg_mtl_pipeline_t; +typedef _sg_mtl_pipeline_t _sg_pipeline_t; + +typedef struct { + _sg_image_t* image; +} _sg_mtl_attachment_t; + +typedef struct { + _sg_slot_t slot; + _sg_pass_common_t cmn; + struct { + _sg_mtl_attachment_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; + _sg_mtl_attachment_t ds_att; + } mtl; +} _sg_mtl_pass_t; +typedef _sg_mtl_pass_t _sg_pass_t; +typedef _sg_pass_attachment_common_t _sg_pass_attachment_t; + +typedef struct { + _sg_slot_t slot; +} _sg_mtl_context_t; +typedef _sg_mtl_context_t _sg_context_t; + +/* resouce binding state cache */ +typedef struct { + const _sg_pipeline_t* cur_pipeline; + sg_pipeline cur_pipeline_id; + const _sg_buffer_t* cur_indexbuffer; + int cur_indexbuffer_offset; + sg_buffer cur_indexbuffer_id; + const _sg_buffer_t* cur_vertexbuffers[SG_MAX_SHADERSTAGE_BUFFERS]; + int cur_vertexbuffer_offsets[SG_MAX_SHADERSTAGE_BUFFERS]; + sg_buffer cur_vertexbuffer_ids[SG_MAX_SHADERSTAGE_BUFFERS]; + const _sg_image_t* cur_vs_images[SG_MAX_SHADERSTAGE_IMAGES]; + sg_image cur_vs_image_ids[SG_MAX_SHADERSTAGE_IMAGES]; + const _sg_image_t* cur_fs_images[SG_MAX_SHADERSTAGE_IMAGES]; + sg_image cur_fs_image_ids[SG_MAX_SHADERSTAGE_IMAGES]; +} _sg_mtl_state_cache_t; + +typedef struct { + bool valid; + const void*(*renderpass_descriptor_cb)(void); + const void*(*renderpass_descriptor_userdata_cb)(void*); + const void*(*drawable_cb)(void); + const void*(*drawable_userdata_cb)(void*); + void* user_data; + uint32_t frame_index; + uint32_t cur_frame_rotate_index; + int ub_size; + int cur_ub_offset; + uint8_t* cur_ub_base_ptr; + bool in_pass; + bool pass_valid; + int cur_width; + int cur_height; + _sg_mtl_state_cache_t state_cache; + _sg_sampler_cache_t sampler_cache; + _sg_mtl_idpool_t idpool; + dispatch_semaphore_t sem; + id device; + id cmd_queue; + id cmd_buffer; + id cmd_encoder; + id uniform_buffers[SG_NUM_INFLIGHT_FRAMES]; +} _sg_mtl_backend_t; + +/*=== WGPU BACKEND DECLARATIONS ==============================================*/ +#elif defined(SOKOL_WGPU) + +#define _SG_WGPU_STAGING_ALIGN (256) +#define _SG_WGPU_STAGING_PIPELINE_SIZE (8) +#define _SG_WGPU_ROWPITCH_ALIGN (256) +#define _SG_WGPU_MAX_SHADERSTAGE_IMAGES (8) +#define _SG_WGPU_MAX_UNIFORM_UPDATE_SIZE (1<<16) + +typedef struct { + _sg_slot_t slot; + _sg_buffer_common_t cmn; + struct { + WGPUBuffer buf; + } wgpu; +} _sg_wgpu_buffer_t; +typedef _sg_wgpu_buffer_t _sg_buffer_t; + +typedef struct { + _sg_slot_t slot; + _sg_image_common_t cmn; + struct { + WGPUTexture tex; + WGPUTextureView tex_view; + WGPUTexture msaa_tex; + WGPUSampler sampler; + } wgpu; +} _sg_wgpu_image_t; +typedef _sg_wgpu_image_t _sg_image_t; + +typedef struct { + WGPUShaderModule module; + WGPUBindGroupLayout bind_group_layout; + _sg_str_t entry; +} _sg_wgpu_shader_stage_t; + +typedef struct { + _sg_slot_t slot; + _sg_shader_common_t cmn; + struct { + _sg_wgpu_shader_stage_t stage[SG_NUM_SHADER_STAGES]; + } wgpu; +} _sg_wgpu_shader_t; +typedef _sg_wgpu_shader_t _sg_shader_t; + +typedef struct { + _sg_slot_t slot; + _sg_pipeline_common_t cmn; + _sg_shader_t* shader; + struct { + WGPURenderPipeline pip; + uint32_t stencil_ref; + } wgpu; +} _sg_wgpu_pipeline_t; +typedef _sg_wgpu_pipeline_t _sg_pipeline_t; + +typedef struct { + _sg_image_t* image; + WGPUTextureView render_tex_view; + WGPUTextureView resolve_tex_view; +} _sg_wgpu_attachment_t; + +typedef struct { + _sg_slot_t slot; + _sg_pass_common_t cmn; + struct { + _sg_wgpu_attachment_t color_atts[SG_MAX_COLOR_ATTACHMENTS]; + _sg_wgpu_attachment_t ds_att; + } wgpu; +} _sg_wgpu_pass_t; +typedef _sg_wgpu_pass_t _sg_pass_t; +typedef _sg_pass_attachment_common_t _sg_pass_attachment_t; + +typedef struct { + _sg_slot_t slot; +} _sg_wgpu_context_t; +typedef _sg_wgpu_context_t _sg_context_t; + +/* a pool of per-frame uniform buffers */ +typedef struct { + WGPUBindGroupLayout bindgroup_layout; + uint32_t num_bytes; + uint32_t offset; /* current offset into current frame's mapped uniform buffer */ + uint32_t bind_offsets[SG_NUM_SHADER_STAGES][SG_MAX_SHADERSTAGE_UBS]; + WGPUBuffer buf; /* the GPU-side uniform buffer */ + WGPUBindGroup bindgroup; + struct { + int num; + int cur; + WGPUBuffer buf[_SG_WGPU_STAGING_PIPELINE_SIZE]; /* CPU-side staging buffers */ + uint8_t* ptr[_SG_WGPU_STAGING_PIPELINE_SIZE]; /* if != 0, staging buffer currently mapped */ + } stage; +} _sg_wgpu_ubpool_t; + +/* ...a similar pool (like uniform buffer pool) of dynamic-resource staging buffers */ +typedef struct { + uint32_t num_bytes; + uint32_t offset; /* current offset into current frame's staging buffer */ + int num; /* number of staging buffers */ + int cur; /* this frame's staging buffer */ + WGPUBuffer buf[_SG_WGPU_STAGING_PIPELINE_SIZE]; /* CPU-side staging buffers */ + uint8_t* ptr[_SG_WGPU_STAGING_PIPELINE_SIZE]; /* if != 0, staging buffer currently mapped */ +} _sg_wgpu_stagingpool_t; + +/* the WGPU backend state */ +typedef struct { + bool valid; + bool in_pass; + bool draw_indexed; + int cur_width; + int cur_height; + WGPUDevice dev; + WGPUTextureView (*render_view_cb)(void); + WGPUTextureView (*render_view_userdata_cb)(void*); + WGPUTextureView (*resolve_view_cb)(void); + WGPUTextureView (*resolve_view_userdata_cb)(void*); + WGPUTextureView (*depth_stencil_view_cb)(void); + WGPUTextureView (*depth_stencil_view_userdata_cb)(void*); + void* user_data; + WGPUQueue queue; + WGPUCommandEncoder render_cmd_enc; + WGPUCommandEncoder staging_cmd_enc; + WGPURenderPassEncoder pass_enc; + WGPUBindGroup empty_bind_group; + const _sg_pipeline_t* cur_pipeline; + sg_pipeline cur_pipeline_id; + _sg_sampler_cache_t sampler_cache; + _sg_wgpu_ubpool_t ub; + _sg_wgpu_stagingpool_t staging; +} _sg_wgpu_backend_t; +#endif + +/*=== RESOURCE POOL DECLARATIONS =============================================*/ + +/* this *MUST* remain 0 */ +#define _SG_INVALID_SLOT_INDEX (0) + +typedef struct { + int size; + int queue_top; + uint32_t* gen_ctrs; + int* free_queue; +} _sg_pool_t; + +typedef struct { + _sg_pool_t buffer_pool; + _sg_pool_t image_pool; + _sg_pool_t shader_pool; + _sg_pool_t pipeline_pool; + _sg_pool_t pass_pool; + _sg_pool_t context_pool; + _sg_buffer_t* buffers; + _sg_image_t* images; + _sg_shader_t* shaders; + _sg_pipeline_t* pipelines; + _sg_pass_t* passes; + _sg_context_t* contexts; +} _sg_pools_t; + +/*=== VALIDATION LAYER DECLARATIONS ==========================================*/ +typedef enum { + /* special case 'validation was successful' */ + _SG_VALIDATE_SUCCESS, + + /* buffer creation */ + _SG_VALIDATE_BUFFERDESC_CANARY, + _SG_VALIDATE_BUFFERDESC_SIZE, + _SG_VALIDATE_BUFFERDESC_DATA, + _SG_VALIDATE_BUFFERDESC_DATA_SIZE, + _SG_VALIDATE_BUFFERDESC_NO_DATA, + + /* image creation */ + _SG_VALIDATE_IMAGEDESC_CANARY, + _SG_VALIDATE_IMAGEDESC_WIDTH, + _SG_VALIDATE_IMAGEDESC_HEIGHT, + _SG_VALIDATE_IMAGEDESC_RT_PIXELFORMAT, + _SG_VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT, + _SG_VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT, + _SG_VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT, + _SG_VALIDATE_IMAGEDESC_RT_IMMUTABLE, + _SG_VALIDATE_IMAGEDESC_RT_NO_DATA, + _SG_VALIDATE_IMAGEDESC_DATA, + _SG_VALIDATE_IMAGEDESC_NO_DATA, + + /* shader creation */ + _SG_VALIDATE_SHADERDESC_CANARY, + _SG_VALIDATE_SHADERDESC_SOURCE, + _SG_VALIDATE_SHADERDESC_BYTECODE, + _SG_VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE, + _SG_VALIDATE_SHADERDESC_NO_BYTECODE_SIZE, + _SG_VALIDATE_SHADERDESC_NO_CONT_UBS, + _SG_VALIDATE_SHADERDESC_NO_CONT_IMGS, + _SG_VALIDATE_SHADERDESC_NO_CONT_UB_MEMBERS, + _SG_VALIDATE_SHADERDESC_NO_UB_MEMBERS, + _SG_VALIDATE_SHADERDESC_UB_MEMBER_NAME, + _SG_VALIDATE_SHADERDESC_UB_SIZE_MISMATCH, + _SG_VALIDATE_SHADERDESC_IMG_NAME, + _SG_VALIDATE_SHADERDESC_ATTR_NAMES, + _SG_VALIDATE_SHADERDESC_ATTR_SEMANTICS, + _SG_VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG, + + /* pipeline creation */ + _SG_VALIDATE_PIPELINEDESC_CANARY, + _SG_VALIDATE_PIPELINEDESC_SHADER, + _SG_VALIDATE_PIPELINEDESC_NO_ATTRS, + _SG_VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4, + _SG_VALIDATE_PIPELINEDESC_ATTR_NAME, + _SG_VALIDATE_PIPELINEDESC_ATTR_SEMANTICS, + + /* pass creation */ + _SG_VALIDATE_PASSDESC_CANARY, + _SG_VALIDATE_PASSDESC_NO_COLOR_ATTS, + _SG_VALIDATE_PASSDESC_NO_CONT_COLOR_ATTS, + _SG_VALIDATE_PASSDESC_IMAGE, + _SG_VALIDATE_PASSDESC_MIPLEVEL, + _SG_VALIDATE_PASSDESC_FACE, + _SG_VALIDATE_PASSDESC_LAYER, + _SG_VALIDATE_PASSDESC_SLICE, + _SG_VALIDATE_PASSDESC_IMAGE_NO_RT, + _SG_VALIDATE_PASSDESC_COLOR_INV_PIXELFORMAT, + _SG_VALIDATE_PASSDESC_DEPTH_INV_PIXELFORMAT, + _SG_VALIDATE_PASSDESC_IMAGE_SIZES, + _SG_VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS, + + /* sg_begin_pass validation */ + _SG_VALIDATE_BEGINPASS_PASS, + _SG_VALIDATE_BEGINPASS_IMAGE, + + /* sg_apply_pipeline validation */ + _SG_VALIDATE_APIP_PIPELINE_VALID_ID, + _SG_VALIDATE_APIP_PIPELINE_EXISTS, + _SG_VALIDATE_APIP_PIPELINE_VALID, + _SG_VALIDATE_APIP_SHADER_EXISTS, + _SG_VALIDATE_APIP_SHADER_VALID, + _SG_VALIDATE_APIP_ATT_COUNT, + _SG_VALIDATE_APIP_COLOR_FORMAT, + _SG_VALIDATE_APIP_DEPTH_FORMAT, + _SG_VALIDATE_APIP_SAMPLE_COUNT, + + /* sg_apply_bindings validation */ + _SG_VALIDATE_ABND_PIPELINE, + _SG_VALIDATE_ABND_PIPELINE_EXISTS, + _SG_VALIDATE_ABND_PIPELINE_VALID, + _SG_VALIDATE_ABND_VBS, + _SG_VALIDATE_ABND_VB_EXISTS, + _SG_VALIDATE_ABND_VB_TYPE, + _SG_VALIDATE_ABND_VB_OVERFLOW, + _SG_VALIDATE_ABND_NO_IB, + _SG_VALIDATE_ABND_IB, + _SG_VALIDATE_ABND_IB_EXISTS, + _SG_VALIDATE_ABND_IB_TYPE, + _SG_VALIDATE_ABND_IB_OVERFLOW, + _SG_VALIDATE_ABND_VS_IMGS, + _SG_VALIDATE_ABND_VS_IMG_EXISTS, + _SG_VALIDATE_ABND_VS_IMG_TYPES, + _SG_VALIDATE_ABND_FS_IMGS, + _SG_VALIDATE_ABND_FS_IMG_EXISTS, + _SG_VALIDATE_ABND_FS_IMG_TYPES, + + /* sg_apply_uniforms validation */ + _SG_VALIDATE_AUB_NO_PIPELINE, + _SG_VALIDATE_AUB_NO_UB_AT_SLOT, + _SG_VALIDATE_AUB_SIZE, + + /* sg_update_buffer validation */ + _SG_VALIDATE_UPDATEBUF_USAGE, + _SG_VALIDATE_UPDATEBUF_SIZE, + _SG_VALIDATE_UPDATEBUF_ONCE, + _SG_VALIDATE_UPDATEBUF_APPEND, + + /* sg_append_buffer validation */ + _SG_VALIDATE_APPENDBUF_USAGE, + _SG_VALIDATE_APPENDBUF_SIZE, + _SG_VALIDATE_APPENDBUF_UPDATE, + + /* sg_update_image validation */ + _SG_VALIDATE_UPDIMG_USAGE, + _SG_VALIDATE_UPDIMG_NOTENOUGHDATA, + _SG_VALIDATE_UPDIMG_SIZE, + _SG_VALIDATE_UPDIMG_COMPRESSED, + _SG_VALIDATE_UPDIMG_ONCE +} _sg_validate_error_t; + +/*=== GENERIC BACKEND STATE ==================================================*/ + +typedef struct { + bool valid; + sg_desc desc; /* original desc with default values patched in */ + uint32_t frame_index; + sg_context active_context; + sg_pass cur_pass; + sg_pipeline cur_pipeline; + bool pass_valid; + bool bindings_valid; + bool next_draw_valid; + #if defined(SOKOL_DEBUG) + _sg_validate_error_t validate_error; + #endif + _sg_pools_t pools; + sg_backend backend; + sg_features features; + sg_limits limits; + sg_pixelformat_info formats[_SG_PIXELFORMAT_NUM]; + #if defined(_SOKOL_ANY_GL) + _sg_gl_backend_t gl; + #elif defined(SOKOL_METAL) + _sg_mtl_backend_t mtl; + #elif defined(SOKOL_D3D11) + _sg_d3d11_backend_t d3d11; + #elif defined(SOKOL_WGPU) + _sg_wgpu_backend_t wgpu; + #endif + #if defined(SOKOL_TRACE_HOOKS) + sg_trace_hooks hooks; + #endif +} _sg_state_t; +static _sg_state_t _sg; + +/*-- helper functions --------------------------------------------------------*/ + +_SOKOL_PRIVATE bool _sg_strempty(const _sg_str_t* str) { + return 0 == str->buf[0]; +} + +_SOKOL_PRIVATE const char* _sg_strptr(const _sg_str_t* str) { + return &str->buf[0]; +} + +_SOKOL_PRIVATE void _sg_strcpy(_sg_str_t* dst, const char* src) { + SOKOL_ASSERT(dst); + if (src) { + #if defined(_MSC_VER) + strncpy_s(dst->buf, _SG_STRING_SIZE, src, (_SG_STRING_SIZE-1)); + #else + strncpy(dst->buf, src, _SG_STRING_SIZE); + #endif + dst->buf[_SG_STRING_SIZE-1] = 0; + } + else { + memset(dst->buf, 0, _SG_STRING_SIZE); + } +} + +/* return byte size of a vertex format */ +_SOKOL_PRIVATE int _sg_vertexformat_bytesize(sg_vertex_format fmt) { + switch (fmt) { + case SG_VERTEXFORMAT_FLOAT: return 4; + case SG_VERTEXFORMAT_FLOAT2: return 8; + case SG_VERTEXFORMAT_FLOAT3: return 12; + case SG_VERTEXFORMAT_FLOAT4: return 16; + case SG_VERTEXFORMAT_BYTE4: return 4; + case SG_VERTEXFORMAT_BYTE4N: return 4; + case SG_VERTEXFORMAT_UBYTE4: return 4; + case SG_VERTEXFORMAT_UBYTE4N: return 4; + case SG_VERTEXFORMAT_SHORT2: return 4; + case SG_VERTEXFORMAT_SHORT2N: return 4; + case SG_VERTEXFORMAT_USHORT2N: return 4; + case SG_VERTEXFORMAT_SHORT4: return 8; + case SG_VERTEXFORMAT_SHORT4N: return 8; + case SG_VERTEXFORMAT_USHORT4N: return 8; + case SG_VERTEXFORMAT_UINT10_N2: return 4; + case SG_VERTEXFORMAT_INVALID: return 0; + default: + SOKOL_UNREACHABLE; + return -1; + } +} + +/* return the byte size of a shader uniform */ +_SOKOL_PRIVATE int _sg_uniform_size(sg_uniform_type type, int count) { + switch (type) { + case SG_UNIFORMTYPE_INVALID: return 0; + case SG_UNIFORMTYPE_FLOAT: return 4 * count; + case SG_UNIFORMTYPE_FLOAT2: return 8 * count; + case SG_UNIFORMTYPE_FLOAT3: return 12 * count; /* FIXME: std140??? */ + case SG_UNIFORMTYPE_FLOAT4: return 16 * count; + case SG_UNIFORMTYPE_MAT4: return 64 * count; + default: + SOKOL_UNREACHABLE; + return -1; + } +} + +/* return true if pixel format is a compressed format */ +_SOKOL_PRIVATE bool _sg_is_compressed_pixel_format(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_BC1_RGBA: + case SG_PIXELFORMAT_BC2_RGBA: + case SG_PIXELFORMAT_BC3_RGBA: + case SG_PIXELFORMAT_BC4_R: + case SG_PIXELFORMAT_BC4_RSN: + case SG_PIXELFORMAT_BC5_RG: + case SG_PIXELFORMAT_BC5_RGSN: + case SG_PIXELFORMAT_BC6H_RGBF: + case SG_PIXELFORMAT_BC6H_RGBUF: + case SG_PIXELFORMAT_BC7_RGBA: + case SG_PIXELFORMAT_PVRTC_RGB_2BPP: + case SG_PIXELFORMAT_PVRTC_RGB_4BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: + case SG_PIXELFORMAT_ETC2_RGB8: + case SG_PIXELFORMAT_ETC2_RGB8A1: + case SG_PIXELFORMAT_ETC2_RGBA8: + case SG_PIXELFORMAT_ETC2_RG11: + case SG_PIXELFORMAT_ETC2_RG11SN: + return true; + default: + return false; + } +} + +/* return true if pixel format is a valid render target format */ +_SOKOL_PRIVATE bool _sg_is_valid_rendertarget_color_format(sg_pixel_format fmt) { + const int fmt_index = (int) fmt; + SOKOL_ASSERT((fmt_index >= 0) && (fmt_index < _SG_PIXELFORMAT_NUM)); + return _sg.formats[fmt_index].render && !_sg.formats[fmt_index].depth; +} + +/* return true if pixel format is a valid depth format */ +_SOKOL_PRIVATE bool _sg_is_valid_rendertarget_depth_format(sg_pixel_format fmt) { + const int fmt_index = (int) fmt; + SOKOL_ASSERT((fmt_index >= 0) && (fmt_index < _SG_PIXELFORMAT_NUM)); + return _sg.formats[fmt_index].render && _sg.formats[fmt_index].depth; +} + +/* return true if pixel format is a depth-stencil format */ +_SOKOL_PRIVATE bool _sg_is_depth_stencil_format(sg_pixel_format fmt) { + return (SG_PIXELFORMAT_DEPTH_STENCIL == fmt); +} + +/* return the bytes-per-pixel for a pixel format */ +_SOKOL_PRIVATE int _sg_pixelformat_bytesize(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_R8: + case SG_PIXELFORMAT_R8SN: + case SG_PIXELFORMAT_R8UI: + case SG_PIXELFORMAT_R8SI: + return 1; + + case SG_PIXELFORMAT_R16: + case SG_PIXELFORMAT_R16SN: + case SG_PIXELFORMAT_R16UI: + case SG_PIXELFORMAT_R16SI: + case SG_PIXELFORMAT_R16F: + case SG_PIXELFORMAT_RG8: + case SG_PIXELFORMAT_RG8SN: + case SG_PIXELFORMAT_RG8UI: + case SG_PIXELFORMAT_RG8SI: + return 2; + + case SG_PIXELFORMAT_R32UI: + case SG_PIXELFORMAT_R32SI: + case SG_PIXELFORMAT_R32F: + case SG_PIXELFORMAT_RG16: + case SG_PIXELFORMAT_RG16SN: + case SG_PIXELFORMAT_RG16UI: + case SG_PIXELFORMAT_RG16SI: + case SG_PIXELFORMAT_RG16F: + case SG_PIXELFORMAT_RGBA8: + case SG_PIXELFORMAT_RGBA8SN: + case SG_PIXELFORMAT_RGBA8UI: + case SG_PIXELFORMAT_RGBA8SI: + case SG_PIXELFORMAT_BGRA8: + case SG_PIXELFORMAT_RGB10A2: + case SG_PIXELFORMAT_RG11B10F: + return 4; + + case SG_PIXELFORMAT_RG32UI: + case SG_PIXELFORMAT_RG32SI: + case SG_PIXELFORMAT_RG32F: + case SG_PIXELFORMAT_RGBA16: + case SG_PIXELFORMAT_RGBA16SN: + case SG_PIXELFORMAT_RGBA16UI: + case SG_PIXELFORMAT_RGBA16SI: + case SG_PIXELFORMAT_RGBA16F: + return 8; + + case SG_PIXELFORMAT_RGBA32UI: + case SG_PIXELFORMAT_RGBA32SI: + case SG_PIXELFORMAT_RGBA32F: + return 16; + + default: + SOKOL_UNREACHABLE; + return 0; + } +} + +_SOKOL_PRIVATE int _sg_roundup(int val, int round_to) { + return (val+(round_to-1)) & ~(round_to-1); +} + +/* return row pitch for an image + see ComputePitch in https://github.com/microsoft/DirectXTex/blob/master/DirectXTex/DirectXTexUtil.cpp +*/ +_SOKOL_PRIVATE int _sg_row_pitch(sg_pixel_format fmt, int width, int row_align) { + int pitch; + switch (fmt) { + case SG_PIXELFORMAT_BC1_RGBA: + case SG_PIXELFORMAT_BC4_R: + case SG_PIXELFORMAT_BC4_RSN: + case SG_PIXELFORMAT_ETC2_RGB8: + case SG_PIXELFORMAT_ETC2_RGB8A1: + pitch = ((width + 3) / 4) * 8; + pitch = pitch < 8 ? 8 : pitch; + break; + case SG_PIXELFORMAT_BC2_RGBA: + case SG_PIXELFORMAT_BC3_RGBA: + case SG_PIXELFORMAT_BC5_RG: + case SG_PIXELFORMAT_BC5_RGSN: + case SG_PIXELFORMAT_BC6H_RGBF: + case SG_PIXELFORMAT_BC6H_RGBUF: + case SG_PIXELFORMAT_BC7_RGBA: + case SG_PIXELFORMAT_ETC2_RGBA8: + case SG_PIXELFORMAT_ETC2_RG11: + case SG_PIXELFORMAT_ETC2_RG11SN: + pitch = ((width + 3) / 4) * 16; + pitch = pitch < 16 ? 16 : pitch; + break; + case SG_PIXELFORMAT_PVRTC_RGB_4BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: + { + const int block_size = 4*4; + const int bpp = 4; + int width_blocks = width / 4; + width_blocks = width_blocks < 2 ? 2 : width_blocks; + pitch = width_blocks * ((block_size * bpp) / 8); + } + break; + case SG_PIXELFORMAT_PVRTC_RGB_2BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: + { + const int block_size = 8*4; + const int bpp = 2; + int width_blocks = width / 4; + width_blocks = width_blocks < 2 ? 2 : width_blocks; + pitch = width_blocks * ((block_size * bpp) / 8); + } + break; + default: + pitch = width * _sg_pixelformat_bytesize(fmt); + break; + } + pitch = _sg_roundup(pitch, row_align); + return pitch; +} + +/* compute the number of rows in a surface depending on pixel format */ +_SOKOL_PRIVATE int _sg_num_rows(sg_pixel_format fmt, int height) { + int num_rows; + switch (fmt) { + case SG_PIXELFORMAT_BC1_RGBA: + case SG_PIXELFORMAT_BC4_R: + case SG_PIXELFORMAT_BC4_RSN: + case SG_PIXELFORMAT_ETC2_RGB8: + case SG_PIXELFORMAT_ETC2_RGB8A1: + case SG_PIXELFORMAT_ETC2_RGBA8: + case SG_PIXELFORMAT_ETC2_RG11: + case SG_PIXELFORMAT_ETC2_RG11SN: + case SG_PIXELFORMAT_BC2_RGBA: + case SG_PIXELFORMAT_BC3_RGBA: + case SG_PIXELFORMAT_BC5_RG: + case SG_PIXELFORMAT_BC5_RGSN: + case SG_PIXELFORMAT_BC6H_RGBF: + case SG_PIXELFORMAT_BC6H_RGBUF: + case SG_PIXELFORMAT_BC7_RGBA: + case SG_PIXELFORMAT_PVRTC_RGB_4BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: + case SG_PIXELFORMAT_PVRTC_RGB_2BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: + num_rows = ((height + 3) / 4); + break; + default: + num_rows = height; + break; + } + if (num_rows < 1) { + num_rows = 1; + } + return num_rows; +} + +/* return pitch of a 2D subimage / texture slice + see ComputePitch in https://github.com/microsoft/DirectXTex/blob/master/DirectXTex/DirectXTexUtil.cpp +*/ +_SOKOL_PRIVATE int _sg_surface_pitch(sg_pixel_format fmt, int width, int height, int row_align) { + int num_rows = _sg_num_rows(fmt, height); + return num_rows * _sg_row_pitch(fmt, width, row_align); +} + +/* capability table pixel format helper functions */ +_SOKOL_PRIVATE void _sg_pixelformat_all(sg_pixelformat_info* pfi) { + pfi->sample = true; + pfi->filter = true; + pfi->blend = true; + pfi->render = true; + pfi->msaa = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_s(sg_pixelformat_info* pfi) { + pfi->sample = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_sf(sg_pixelformat_info* pfi) { + pfi->sample = true; + pfi->filter = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_sr(sg_pixelformat_info* pfi) { + pfi->sample = true; + pfi->render = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_srmd(sg_pixelformat_info* pfi) { + pfi->sample = true; + pfi->render = true; + pfi->msaa = true; + pfi->depth = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_srm(sg_pixelformat_info* pfi) { + pfi->sample = true; + pfi->render = true; + pfi->msaa = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_sfrm(sg_pixelformat_info* pfi) { + pfi->sample = true; + pfi->filter = true; + pfi->render = true; + pfi->msaa = true; +} +_SOKOL_PRIVATE void _sg_pixelformat_sbrm(sg_pixelformat_info* pfi) { + pfi->sample = true; + pfi->blend = true; + pfi->render = true; + pfi->msaa = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_sbr(sg_pixelformat_info* pfi) { + pfi->sample = true; + pfi->blend = true; + pfi->render = true; +} + +_SOKOL_PRIVATE void _sg_pixelformat_sfbr(sg_pixelformat_info* pfi) { + pfi->sample = true; + pfi->filter = true; + pfi->blend = true; + pfi->render = true; +} + +/* resolve pass action defaults into a new pass action struct */ +_SOKOL_PRIVATE void _sg_resolve_default_pass_action(const sg_pass_action* from, sg_pass_action* to) { + SOKOL_ASSERT(from && to); + *to = *from; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + if (to->colors[i].action == _SG_ACTION_DEFAULT) { + to->colors[i].action = SG_ACTION_CLEAR; + to->colors[i].value.r = SG_DEFAULT_CLEAR_RED; + to->colors[i].value.g = SG_DEFAULT_CLEAR_GREEN; + to->colors[i].value.b = SG_DEFAULT_CLEAR_BLUE; + to->colors[i].value.a = SG_DEFAULT_CLEAR_ALPHA; + } + } + if (to->depth.action == _SG_ACTION_DEFAULT) { + to->depth.action = SG_ACTION_CLEAR; + to->depth.value = SG_DEFAULT_CLEAR_DEPTH; + } + if (to->stencil.action == _SG_ACTION_DEFAULT) { + to->stencil.action = SG_ACTION_CLEAR; + to->stencil.value = SG_DEFAULT_CLEAR_STENCIL; + } +} + +/*== DUMMY BACKEND IMPL ======================================================*/ +#if defined(SOKOL_DUMMY_BACKEND) + +_SOKOL_PRIVATE void _sg_dummy_setup_backend(const sg_desc* desc) { + SOKOL_ASSERT(desc); + _SOKOL_UNUSED(desc); + _sg.backend = SG_BACKEND_DUMMY; + for (int i = SG_PIXELFORMAT_R8; i < SG_PIXELFORMAT_BC1_RGBA; i++) { + _sg.formats[i].sample = true; + _sg.formats[i].filter = true; + _sg.formats[i].render = true; + _sg.formats[i].blend = true; + _sg.formats[i].msaa = true; + } + _sg.formats[SG_PIXELFORMAT_DEPTH].depth = true; + _sg.formats[SG_PIXELFORMAT_DEPTH_STENCIL].depth = true; +} + +_SOKOL_PRIVATE void _sg_dummy_discard_backend(void) { + /* empty */ +} + +_SOKOL_PRIVATE void _sg_dummy_reset_state_cache(void) { + /* empty*/ +} + +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_context(_sg_context_t* ctx) { + SOKOL_ASSERT(ctx); + _SOKOL_UNUSED(ctx); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_dummy_destroy_context(_sg_context_t* ctx) { + SOKOL_ASSERT(ctx); + _SOKOL_UNUSED(ctx); +} + +_SOKOL_PRIVATE void _sg_dummy_activate_context(_sg_context_t* ctx) { + SOKOL_ASSERT(ctx); + _SOKOL_UNUSED(ctx); +} + +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { + SOKOL_ASSERT(buf && desc); + _sg_buffer_common_init(&buf->cmn, desc); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_dummy_destroy_buffer(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf); + _SOKOL_UNUSED(buf); +} + +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_image(_sg_image_t* img, const sg_image_desc* desc) { + SOKOL_ASSERT(img && desc); + _sg_image_common_init(&img->cmn, desc); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_dummy_destroy_image(_sg_image_t* img) { + SOKOL_ASSERT(img); + _SOKOL_UNUSED(img); +} + +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { + SOKOL_ASSERT(shd && desc); + _sg_shader_common_init(&shd->cmn, desc); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_dummy_destroy_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd); + _SOKOL_UNUSED(shd); +} + +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(pip && desc); + pip->shader = shd; + _sg_pipeline_common_init(&pip->cmn, desc); + for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + const sg_vertex_attr_desc* a_desc = &desc->layout.attrs[attr_index]; + if (a_desc->format == SG_VERTEXFORMAT_INVALID) { + break; + } + SOKOL_ASSERT(a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS); + pip->cmn.vertex_layout_valid[a_desc->buffer_index] = true; + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_dummy_destroy_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + _SOKOL_UNUSED(pip); +} + +_SOKOL_PRIVATE sg_resource_state _sg_dummy_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { + SOKOL_ASSERT(pass && desc); + SOKOL_ASSERT(att_images && att_images[0]); + + _sg_pass_common_init(&pass->cmn, desc); + + const sg_pass_attachment_desc* att_desc; + for (int i = 0; i < pass->cmn.num_color_atts; i++) { + att_desc = &desc->color_attachments[i]; + SOKOL_ASSERT(att_desc->image.id != SG_INVALID_ID); + SOKOL_ASSERT(0 == pass->dmy.color_atts[i].image); + SOKOL_ASSERT(att_images[i] && (att_images[i]->slot.id == att_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(att_images[i]->cmn.pixel_format)); + pass->dmy.color_atts[i].image = att_images[i]; + } + + SOKOL_ASSERT(0 == pass->dmy.ds_att.image); + att_desc = &desc->depth_stencil_attachment; + if (att_desc->image.id != SG_INVALID_ID) { + const int ds_img_index = SG_MAX_COLOR_ATTACHMENTS; + SOKOL_ASSERT(att_images[ds_img_index] && (att_images[ds_img_index]->slot.id == att_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(att_images[ds_img_index]->cmn.pixel_format)); + pass->dmy.ds_att.image = att_images[ds_img_index]; + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_dummy_destroy_pass(_sg_pass_t* pass) { + SOKOL_ASSERT(pass); + _SOKOL_UNUSED(pass); +} + +_SOKOL_PRIVATE _sg_image_t* _sg_dummy_pass_color_image(const _sg_pass_t* pass, int index) { + SOKOL_ASSERT(pass && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + /* NOTE: may return null */ + return pass->dmy.color_atts[index].image; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_dummy_pass_ds_image(const _sg_pass_t* pass) { + /* NOTE: may return null */ + SOKOL_ASSERT(pass); + return pass->dmy.ds_att.image; +} + +_SOKOL_PRIVATE void _sg_dummy_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { + SOKOL_ASSERT(action); + _SOKOL_UNUSED(pass); + _SOKOL_UNUSED(action); + _SOKOL_UNUSED(w); + _SOKOL_UNUSED(h); +} + +_SOKOL_PRIVATE void _sg_dummy_end_pass(void) { + /* empty */ +} + +_SOKOL_PRIVATE void _sg_dummy_commit(void) { + /* empty */ +} + +_SOKOL_PRIVATE void _sg_dummy_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { + _SOKOL_UNUSED(x); + _SOKOL_UNUSED(y); + _SOKOL_UNUSED(w); + _SOKOL_UNUSED(h); + _SOKOL_UNUSED(origin_top_left); +} + +_SOKOL_PRIVATE void _sg_dummy_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { + _SOKOL_UNUSED(x); + _SOKOL_UNUSED(y); + _SOKOL_UNUSED(w); + _SOKOL_UNUSED(h); + _SOKOL_UNUSED(origin_top_left); +} + +_SOKOL_PRIVATE void _sg_dummy_apply_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + _SOKOL_UNUSED(pip); +} + +_SOKOL_PRIVATE void _sg_dummy_apply_bindings( + _sg_pipeline_t* pip, + _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, + _sg_buffer_t* ib, int ib_offset, + _sg_image_t** vs_imgs, int num_vs_imgs, + _sg_image_t** fs_imgs, int num_fs_imgs) +{ + SOKOL_ASSERT(pip); + SOKOL_ASSERT(vbs && vb_offsets); + SOKOL_ASSERT(vs_imgs); + SOKOL_ASSERT(fs_imgs); + _SOKOL_UNUSED(pip); + _SOKOL_UNUSED(vbs); _SOKOL_UNUSED(vb_offsets); _SOKOL_UNUSED(num_vbs); + _SOKOL_UNUSED(ib); _SOKOL_UNUSED(ib_offset); + _SOKOL_UNUSED(vs_imgs); _SOKOL_UNUSED(num_vs_imgs); + _SOKOL_UNUSED(fs_imgs); _SOKOL_UNUSED(num_fs_imgs); +} + +_SOKOL_PRIVATE void _sg_dummy_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { + _SOKOL_UNUSED(stage_index); + _SOKOL_UNUSED(ub_index); + _SOKOL_UNUSED(data); +} + +_SOKOL_PRIVATE void _sg_dummy_draw(int base_element, int num_elements, int num_instances) { + _SOKOL_UNUSED(base_element); + _SOKOL_UNUSED(num_elements); + _SOKOL_UNUSED(num_instances); +} + +_SOKOL_PRIVATE void _sg_dummy_update_buffer(_sg_buffer_t* buf, const sg_range* data) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + _SOKOL_UNUSED(data); + if (++buf->cmn.active_slot >= buf->cmn.num_slots) { + buf->cmn.active_slot = 0; + } +} + +_SOKOL_PRIVATE int _sg_dummy_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + _SOKOL_UNUSED(data); + if (new_frame) { + if (++buf->cmn.active_slot >= buf->cmn.num_slots) { + buf->cmn.active_slot = 0; + } + } + /* NOTE: this is a requirement from WebGPU, but we want identical behaviour across all backend */ + return _sg_roundup((int)data->size, 4); +} + +_SOKOL_PRIVATE void _sg_dummy_update_image(_sg_image_t* img, const sg_image_data* data) { + SOKOL_ASSERT(img && data); + _SOKOL_UNUSED(data); + if (++img->cmn.active_slot >= img->cmn.num_slots) { + img->cmn.active_slot = 0; + } +} + +/*== GL BACKEND ==============================================================*/ +#elif defined(_SOKOL_ANY_GL) + +/*=== OPTIONAL GL LOADER FOR WIN32 ===========================================*/ +#if defined(_SOKOL_USE_WIN32_GL_LOADER) + +// X Macro list of GL function names and signatures +#define _SG_GL_FUNCS \ + _SG_XMACRO(glBindVertexArray, void, (GLuint array)) \ + _SG_XMACRO(glFramebufferTextureLayer, void, (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer)) \ + _SG_XMACRO(glGenFramebuffers, void, (GLsizei n, GLuint * framebuffers)) \ + _SG_XMACRO(glBindFramebuffer, void, (GLenum target, GLuint framebuffer)) \ + _SG_XMACRO(glBindRenderbuffer, void, (GLenum target, GLuint renderbuffer)) \ + _SG_XMACRO(glGetStringi, const GLubyte *, (GLenum name, GLuint index)) \ + _SG_XMACRO(glClearBufferfi, void, (GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil)) \ + _SG_XMACRO(glClearBufferfv, void, (GLenum buffer, GLint drawbuffer, const GLfloat * value)) \ + _SG_XMACRO(glClearBufferuiv, void, (GLenum buffer, GLint drawbuffer, const GLuint * value)) \ + _SG_XMACRO(glClearBufferiv, void, (GLenum buffer, GLint drawbuffer, const GLint * value)) \ + _SG_XMACRO(glDeleteRenderbuffers, void, (GLsizei n, const GLuint * renderbuffers)) \ + _SG_XMACRO(glUniform4fv, void, (GLint location, GLsizei count, const GLfloat * value)) \ + _SG_XMACRO(glUniform2fv, void, (GLint location, GLsizei count, const GLfloat * value)) \ + _SG_XMACRO(glUseProgram, void, (GLuint program)) \ + _SG_XMACRO(glShaderSource, void, (GLuint shader, GLsizei count, const GLchar *const* string, const GLint * length)) \ + _SG_XMACRO(glLinkProgram, void, (GLuint program)) \ + _SG_XMACRO(glGetUniformLocation, GLint, (GLuint program, const GLchar * name)) \ + _SG_XMACRO(glGetShaderiv, void, (GLuint shader, GLenum pname, GLint * params)) \ + _SG_XMACRO(glGetProgramInfoLog, void, (GLuint program, GLsizei bufSize, GLsizei * length, GLchar * infoLog)) \ + _SG_XMACRO(glGetAttribLocation, GLint, (GLuint program, const GLchar * name)) \ + _SG_XMACRO(glDisableVertexAttribArray, void, (GLuint index)) \ + _SG_XMACRO(glDeleteShader, void, (GLuint shader)) \ + _SG_XMACRO(glDeleteProgram, void, (GLuint program)) \ + _SG_XMACRO(glCompileShader, void, (GLuint shader)) \ + _SG_XMACRO(glStencilFuncSeparate, void, (GLenum face, GLenum func, GLint ref, GLuint mask)) \ + _SG_XMACRO(glStencilOpSeparate, void, (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass)) \ + _SG_XMACRO(glRenderbufferStorageMultisample, void, (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height)) \ + _SG_XMACRO(glDrawBuffers, void, (GLsizei n, const GLenum * bufs)) \ + _SG_XMACRO(glVertexAttribDivisor, void, (GLuint index, GLuint divisor)) \ + _SG_XMACRO(glBufferSubData, void, (GLenum target, GLintptr offset, GLsizeiptr size, const void * data)) \ + _SG_XMACRO(glGenBuffers, void, (GLsizei n, GLuint * buffers)) \ + _SG_XMACRO(glCheckFramebufferStatus, GLenum, (GLenum target)) \ + _SG_XMACRO(glFramebufferRenderbuffer, void, (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer)) \ + _SG_XMACRO(glCompressedTexImage2D, void, (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const void * data)) \ + _SG_XMACRO(glCompressedTexImage3D, void, (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const void * data)) \ + _SG_XMACRO(glActiveTexture, void, (GLenum texture)) \ + _SG_XMACRO(glTexSubImage3D, void, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const void * pixels)) \ + _SG_XMACRO(glUniformMatrix4fv, void, (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value)) \ + _SG_XMACRO(glRenderbufferStorage, void, (GLenum target, GLenum internalformat, GLsizei width, GLsizei height)) \ + _SG_XMACRO(glGenTextures, void, (GLsizei n, GLuint * textures)) \ + _SG_XMACRO(glPolygonOffset, void, (GLfloat factor, GLfloat units)) \ + _SG_XMACRO(glDrawElements, void, (GLenum mode, GLsizei count, GLenum type, const void * indices)) \ + _SG_XMACRO(glDeleteFramebuffers, void, (GLsizei n, const GLuint * framebuffers)) \ + _SG_XMACRO(glBlendEquationSeparate, void, (GLenum modeRGB, GLenum modeAlpha)) \ + _SG_XMACRO(glDeleteTextures, void, (GLsizei n, const GLuint * textures)) \ + _SG_XMACRO(glGetProgramiv, void, (GLuint program, GLenum pname, GLint * params)) \ + _SG_XMACRO(glBindTexture, void, (GLenum target, GLuint texture)) \ + _SG_XMACRO(glTexImage3D, void, (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const void * pixels)) \ + _SG_XMACRO(glCreateShader, GLuint, (GLenum type)) \ + _SG_XMACRO(glTexSubImage2D, void, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const void * pixels)) \ + _SG_XMACRO(glClearDepth, void, (GLdouble depth)) \ + _SG_XMACRO(glFramebufferTexture2D, void, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)) \ + _SG_XMACRO(glCreateProgram, GLuint, (void)) \ + _SG_XMACRO(glViewport, void, (GLint x, GLint y, GLsizei width, GLsizei height)) \ + _SG_XMACRO(glDeleteBuffers, void, (GLsizei n, const GLuint * buffers)) \ + _SG_XMACRO(glDrawArrays, void, (GLenum mode, GLint first, GLsizei count)) \ + _SG_XMACRO(glDrawElementsInstanced, void, (GLenum mode, GLsizei count, GLenum type, const void * indices, GLsizei instancecount)) \ + _SG_XMACRO(glVertexAttribPointer, void, (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void * pointer)) \ + _SG_XMACRO(glUniform1i, void, (GLint location, GLint v0)) \ + _SG_XMACRO(glDisable, void, (GLenum cap)) \ + _SG_XMACRO(glColorMask, void, (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha)) \ + _SG_XMACRO(glColorMaski, void, (GLuint buf, GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha)) \ + _SG_XMACRO(glBindBuffer, void, (GLenum target, GLuint buffer)) \ + _SG_XMACRO(glDeleteVertexArrays, void, (GLsizei n, const GLuint * arrays)) \ + _SG_XMACRO(glDepthMask, void, (GLboolean flag)) \ + _SG_XMACRO(glDrawArraysInstanced, void, (GLenum mode, GLint first, GLsizei count, GLsizei instancecount)) \ + _SG_XMACRO(glClearStencil, void, (GLint s)) \ + _SG_XMACRO(glScissor, void, (GLint x, GLint y, GLsizei width, GLsizei height)) \ + _SG_XMACRO(glUniform3fv, void, (GLint location, GLsizei count, const GLfloat * value)) \ + _SG_XMACRO(glGenRenderbuffers, void, (GLsizei n, GLuint * renderbuffers)) \ + _SG_XMACRO(glBufferData, void, (GLenum target, GLsizeiptr size, const void * data, GLenum usage)) \ + _SG_XMACRO(glBlendFuncSeparate, void, (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha)) \ + _SG_XMACRO(glTexParameteri, void, (GLenum target, GLenum pname, GLint param)) \ + _SG_XMACRO(glGetIntegerv, void, (GLenum pname, GLint * data)) \ + _SG_XMACRO(glEnable, void, (GLenum cap)) \ + _SG_XMACRO(glBlitFramebuffer, void, (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter)) \ + _SG_XMACRO(glStencilMask, void, (GLuint mask)) \ + _SG_XMACRO(glAttachShader, void, (GLuint program, GLuint shader)) \ + _SG_XMACRO(glGetError, GLenum, (void)) \ + _SG_XMACRO(glClearColor, void, (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)) \ + _SG_XMACRO(glBlendColor, void, (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha)) \ + _SG_XMACRO(glTexParameterf, void, (GLenum target, GLenum pname, GLfloat param)) \ + _SG_XMACRO(glTexParameterfv, void, (GLenum target, GLenum pname, GLfloat* params)) \ + _SG_XMACRO(glGetShaderInfoLog, void, (GLuint shader, GLsizei bufSize, GLsizei * length, GLchar * infoLog)) \ + _SG_XMACRO(glDepthFunc, void, (GLenum func)) \ + _SG_XMACRO(glStencilOp , void, (GLenum fail, GLenum zfail, GLenum zpass)) \ + _SG_XMACRO(glStencilFunc, void, (GLenum func, GLint ref, GLuint mask)) \ + _SG_XMACRO(glEnableVertexAttribArray, void, (GLuint index)) \ + _SG_XMACRO(glBlendFunc, void, (GLenum sfactor, GLenum dfactor)) \ + _SG_XMACRO(glUniform1fv, void, (GLint location, GLsizei count, const GLfloat * value)) \ + _SG_XMACRO(glReadBuffer, void, (GLenum src)) \ + _SG_XMACRO(glClear, void, (GLbitfield mask)) \ + _SG_XMACRO(glTexImage2D, void, (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void * pixels)) \ + _SG_XMACRO(glGenVertexArrays, void, (GLsizei n, GLuint * arrays)) \ + _SG_XMACRO(glFrontFace, void, (GLenum mode)) \ + _SG_XMACRO(glCullFace, void, (GLenum mode)) + +// generate GL function pointer typedefs +#define _SG_XMACRO(name, ret, args) typedef ret (GL_APIENTRY* PFN_ ## name) args; +_SG_GL_FUNCS +#undef _SG_XMACRO + +// generate GL function pointers +#define _SG_XMACRO(name, ret, args) static PFN_ ## name name; +_SG_GL_FUNCS +#undef _SG_XMACRO + +// helper function to lookup GL functions in GL DLL +typedef PROC (WINAPI * _sg_wglGetProcAddress)(LPCSTR); +_SOKOL_PRIVATE void* _sg_gl_getprocaddr(const char* name, _sg_wglGetProcAddress wgl_getprocaddress) { + void* proc_addr = (void*) wgl_getprocaddress(name); + if (0 == proc_addr) { + proc_addr = (void*) GetProcAddress(_sg.gl.opengl32_dll, name); + } + SOKOL_ASSERT(proc_addr); + return proc_addr; +} + +// populate GL function pointers +_SOKOL_PRIVATE void _sg_gl_load_opengl(void) { + SOKOL_ASSERT(0 == _sg.gl.opengl32_dll); + _sg.gl.opengl32_dll = LoadLibraryA("opengl32.dll"); + SOKOL_ASSERT(_sg.gl.opengl32_dll); + _sg_wglGetProcAddress wgl_getprocaddress = (_sg_wglGetProcAddress) GetProcAddress(_sg.gl.opengl32_dll, "wglGetProcAddress"); + SOKOL_ASSERT(wgl_getprocaddress); + #define _SG_XMACRO(name, ret, args) name = (PFN_ ## name) _sg_gl_getprocaddr(#name, wgl_getprocaddress); + _SG_GL_FUNCS + #undef _SG_XMACRO +} + +_SOKOL_PRIVATE void _sg_gl_unload_opengl(void) { + SOKOL_ASSERT(_sg.gl.opengl32_dll); + FreeLibrary(_sg.gl.opengl32_dll); + _sg.gl.opengl32_dll = 0; +} +#endif // _SOKOL_USE_WIN32_GL_LOADER + +/*-- type translation --------------------------------------------------------*/ +_SOKOL_PRIVATE GLenum _sg_gl_buffer_target(sg_buffer_type t) { + switch (t) { + case SG_BUFFERTYPE_VERTEXBUFFER: return GL_ARRAY_BUFFER; + case SG_BUFFERTYPE_INDEXBUFFER: return GL_ELEMENT_ARRAY_BUFFER; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_texture_target(sg_image_type t) { + switch (t) { + case SG_IMAGETYPE_2D: return GL_TEXTURE_2D; + case SG_IMAGETYPE_CUBE: return GL_TEXTURE_CUBE_MAP; + #if !defined(SOKOL_GLES2) + case SG_IMAGETYPE_3D: return GL_TEXTURE_3D; + case SG_IMAGETYPE_ARRAY: return GL_TEXTURE_2D_ARRAY; + #endif + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_usage(sg_usage u) { + switch (u) { + case SG_USAGE_IMMUTABLE: return GL_STATIC_DRAW; + case SG_USAGE_DYNAMIC: return GL_DYNAMIC_DRAW; + case SG_USAGE_STREAM: return GL_STREAM_DRAW; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_shader_stage(sg_shader_stage stage) { + switch (stage) { + case SG_SHADERSTAGE_VS: return GL_VERTEX_SHADER; + case SG_SHADERSTAGE_FS: return GL_FRAGMENT_SHADER; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLint _sg_gl_vertexformat_size(sg_vertex_format fmt) { + switch (fmt) { + case SG_VERTEXFORMAT_FLOAT: return 1; + case SG_VERTEXFORMAT_FLOAT2: return 2; + case SG_VERTEXFORMAT_FLOAT3: return 3; + case SG_VERTEXFORMAT_FLOAT4: return 4; + case SG_VERTEXFORMAT_BYTE4: return 4; + case SG_VERTEXFORMAT_BYTE4N: return 4; + case SG_VERTEXFORMAT_UBYTE4: return 4; + case SG_VERTEXFORMAT_UBYTE4N: return 4; + case SG_VERTEXFORMAT_SHORT2: return 2; + case SG_VERTEXFORMAT_SHORT2N: return 2; + case SG_VERTEXFORMAT_USHORT2N: return 2; + case SG_VERTEXFORMAT_SHORT4: return 4; + case SG_VERTEXFORMAT_SHORT4N: return 4; + case SG_VERTEXFORMAT_USHORT4N: return 4; + case SG_VERTEXFORMAT_UINT10_N2: return 4; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_vertexformat_type(sg_vertex_format fmt) { + switch (fmt) { + case SG_VERTEXFORMAT_FLOAT: + case SG_VERTEXFORMAT_FLOAT2: + case SG_VERTEXFORMAT_FLOAT3: + case SG_VERTEXFORMAT_FLOAT4: + return GL_FLOAT; + case SG_VERTEXFORMAT_BYTE4: + case SG_VERTEXFORMAT_BYTE4N: + return GL_BYTE; + case SG_VERTEXFORMAT_UBYTE4: + case SG_VERTEXFORMAT_UBYTE4N: + return GL_UNSIGNED_BYTE; + case SG_VERTEXFORMAT_SHORT2: + case SG_VERTEXFORMAT_SHORT2N: + case SG_VERTEXFORMAT_SHORT4: + case SG_VERTEXFORMAT_SHORT4N: + return GL_SHORT; + case SG_VERTEXFORMAT_USHORT2N: + case SG_VERTEXFORMAT_USHORT4N: + return GL_UNSIGNED_SHORT; + case SG_VERTEXFORMAT_UINT10_N2: + return GL_UNSIGNED_INT_2_10_10_10_REV; + default: + SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLboolean _sg_gl_vertexformat_normalized(sg_vertex_format fmt) { + switch (fmt) { + case SG_VERTEXFORMAT_BYTE4N: + case SG_VERTEXFORMAT_UBYTE4N: + case SG_VERTEXFORMAT_SHORT2N: + case SG_VERTEXFORMAT_USHORT2N: + case SG_VERTEXFORMAT_SHORT4N: + case SG_VERTEXFORMAT_USHORT4N: + case SG_VERTEXFORMAT_UINT10_N2: + return GL_TRUE; + default: + return GL_FALSE; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_primitive_type(sg_primitive_type t) { + switch (t) { + case SG_PRIMITIVETYPE_POINTS: return GL_POINTS; + case SG_PRIMITIVETYPE_LINES: return GL_LINES; + case SG_PRIMITIVETYPE_LINE_STRIP: return GL_LINE_STRIP; + case SG_PRIMITIVETYPE_TRIANGLES: return GL_TRIANGLES; + case SG_PRIMITIVETYPE_TRIANGLE_STRIP: return GL_TRIANGLE_STRIP; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_index_type(sg_index_type t) { + switch (t) { + case SG_INDEXTYPE_NONE: return 0; + case SG_INDEXTYPE_UINT16: return GL_UNSIGNED_SHORT; + case SG_INDEXTYPE_UINT32: return GL_UNSIGNED_INT; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_compare_func(sg_compare_func cmp) { + switch (cmp) { + case SG_COMPAREFUNC_NEVER: return GL_NEVER; + case SG_COMPAREFUNC_LESS: return GL_LESS; + case SG_COMPAREFUNC_EQUAL: return GL_EQUAL; + case SG_COMPAREFUNC_LESS_EQUAL: return GL_LEQUAL; + case SG_COMPAREFUNC_GREATER: return GL_GREATER; + case SG_COMPAREFUNC_NOT_EQUAL: return GL_NOTEQUAL; + case SG_COMPAREFUNC_GREATER_EQUAL: return GL_GEQUAL; + case SG_COMPAREFUNC_ALWAYS: return GL_ALWAYS; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_stencil_op(sg_stencil_op op) { + switch (op) { + case SG_STENCILOP_KEEP: return GL_KEEP; + case SG_STENCILOP_ZERO: return GL_ZERO; + case SG_STENCILOP_REPLACE: return GL_REPLACE; + case SG_STENCILOP_INCR_CLAMP: return GL_INCR; + case SG_STENCILOP_DECR_CLAMP: return GL_DECR; + case SG_STENCILOP_INVERT: return GL_INVERT; + case SG_STENCILOP_INCR_WRAP: return GL_INCR_WRAP; + case SG_STENCILOP_DECR_WRAP: return GL_DECR_WRAP; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_blend_factor(sg_blend_factor f) { + switch (f) { + case SG_BLENDFACTOR_ZERO: return GL_ZERO; + case SG_BLENDFACTOR_ONE: return GL_ONE; + case SG_BLENDFACTOR_SRC_COLOR: return GL_SRC_COLOR; + case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return GL_ONE_MINUS_SRC_COLOR; + case SG_BLENDFACTOR_SRC_ALPHA: return GL_SRC_ALPHA; + case SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return GL_ONE_MINUS_SRC_ALPHA; + case SG_BLENDFACTOR_DST_COLOR: return GL_DST_COLOR; + case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return GL_ONE_MINUS_DST_COLOR; + case SG_BLENDFACTOR_DST_ALPHA: return GL_DST_ALPHA; + case SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return GL_ONE_MINUS_DST_ALPHA; + case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return GL_SRC_ALPHA_SATURATE; + case SG_BLENDFACTOR_BLEND_COLOR: return GL_CONSTANT_COLOR; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return GL_ONE_MINUS_CONSTANT_COLOR; + case SG_BLENDFACTOR_BLEND_ALPHA: return GL_CONSTANT_ALPHA; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return GL_ONE_MINUS_CONSTANT_ALPHA; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_blend_op(sg_blend_op op) { + switch (op) { + case SG_BLENDOP_ADD: return GL_FUNC_ADD; + case SG_BLENDOP_SUBTRACT: return GL_FUNC_SUBTRACT; + case SG_BLENDOP_REVERSE_SUBTRACT: return GL_FUNC_REVERSE_SUBTRACT; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_filter(sg_filter f) { + switch (f) { + case SG_FILTER_NEAREST: return GL_NEAREST; + case SG_FILTER_LINEAR: return GL_LINEAR; + case SG_FILTER_NEAREST_MIPMAP_NEAREST: return GL_NEAREST_MIPMAP_NEAREST; + case SG_FILTER_NEAREST_MIPMAP_LINEAR: return GL_NEAREST_MIPMAP_LINEAR; + case SG_FILTER_LINEAR_MIPMAP_NEAREST: return GL_LINEAR_MIPMAP_NEAREST; + case SG_FILTER_LINEAR_MIPMAP_LINEAR: return GL_LINEAR_MIPMAP_LINEAR; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_wrap(sg_wrap w) { + switch (w) { + case SG_WRAP_CLAMP_TO_EDGE: return GL_CLAMP_TO_EDGE; + #if defined(SOKOL_GLCORE33) + case SG_WRAP_CLAMP_TO_BORDER: return GL_CLAMP_TO_BORDER; + #else + case SG_WRAP_CLAMP_TO_BORDER: return GL_CLAMP_TO_EDGE; + #endif + case SG_WRAP_REPEAT: return GL_REPEAT; + case SG_WRAP_MIRRORED_REPEAT: return GL_MIRRORED_REPEAT; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_teximage_type(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_R8: + case SG_PIXELFORMAT_R8UI: + case SG_PIXELFORMAT_RG8: + case SG_PIXELFORMAT_RG8UI: + case SG_PIXELFORMAT_RGBA8: + case SG_PIXELFORMAT_RGBA8UI: + case SG_PIXELFORMAT_BGRA8: + return GL_UNSIGNED_BYTE; + case SG_PIXELFORMAT_R8SN: + case SG_PIXELFORMAT_R8SI: + case SG_PIXELFORMAT_RG8SN: + case SG_PIXELFORMAT_RG8SI: + case SG_PIXELFORMAT_RGBA8SN: + case SG_PIXELFORMAT_RGBA8SI: + return GL_BYTE; + case SG_PIXELFORMAT_R16: + case SG_PIXELFORMAT_R16UI: + case SG_PIXELFORMAT_RG16: + case SG_PIXELFORMAT_RG16UI: + case SG_PIXELFORMAT_RGBA16: + case SG_PIXELFORMAT_RGBA16UI: + return GL_UNSIGNED_SHORT; + case SG_PIXELFORMAT_R16SN: + case SG_PIXELFORMAT_R16SI: + case SG_PIXELFORMAT_RG16SN: + case SG_PIXELFORMAT_RG16SI: + case SG_PIXELFORMAT_RGBA16SN: + case SG_PIXELFORMAT_RGBA16SI: + return GL_SHORT; + case SG_PIXELFORMAT_R16F: + case SG_PIXELFORMAT_RG16F: + case SG_PIXELFORMAT_RGBA16F: + return GL_HALF_FLOAT; + case SG_PIXELFORMAT_R32UI: + case SG_PIXELFORMAT_RG32UI: + case SG_PIXELFORMAT_RGBA32UI: + return GL_UNSIGNED_INT; + case SG_PIXELFORMAT_R32SI: + case SG_PIXELFORMAT_RG32SI: + case SG_PIXELFORMAT_RGBA32SI: + return GL_INT; + case SG_PIXELFORMAT_R32F: + case SG_PIXELFORMAT_RG32F: + case SG_PIXELFORMAT_RGBA32F: + return GL_FLOAT; + #if !defined(SOKOL_GLES2) + case SG_PIXELFORMAT_RGB10A2: + return GL_UNSIGNED_INT_2_10_10_10_REV; + case SG_PIXELFORMAT_RG11B10F: + return GL_UNSIGNED_INT_10F_11F_11F_REV; + #endif + case SG_PIXELFORMAT_DEPTH: + return GL_UNSIGNED_SHORT; + case SG_PIXELFORMAT_DEPTH_STENCIL: + return GL_UNSIGNED_INT_24_8; + default: + SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_teximage_format(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_R8: + case SG_PIXELFORMAT_R8SN: + case SG_PIXELFORMAT_R16: + case SG_PIXELFORMAT_R16SN: + case SG_PIXELFORMAT_R16F: + case SG_PIXELFORMAT_R32F: + #if defined(SOKOL_GLES2) + return GL_LUMINANCE; + #else + if (_sg.gl.gles2) { + return GL_LUMINANCE; + } + else { + return GL_RED; + } + #endif + #if !defined(SOKOL_GLES2) + case SG_PIXELFORMAT_R8UI: + case SG_PIXELFORMAT_R8SI: + case SG_PIXELFORMAT_R16UI: + case SG_PIXELFORMAT_R16SI: + case SG_PIXELFORMAT_R32UI: + case SG_PIXELFORMAT_R32SI: + return GL_RED_INTEGER; + case SG_PIXELFORMAT_RG8: + case SG_PIXELFORMAT_RG8SN: + case SG_PIXELFORMAT_RG16: + case SG_PIXELFORMAT_RG16SN: + case SG_PIXELFORMAT_RG16F: + case SG_PIXELFORMAT_RG32F: + return GL_RG; + case SG_PIXELFORMAT_RG8UI: + case SG_PIXELFORMAT_RG8SI: + case SG_PIXELFORMAT_RG16UI: + case SG_PIXELFORMAT_RG16SI: + case SG_PIXELFORMAT_RG32UI: + case SG_PIXELFORMAT_RG32SI: + return GL_RG_INTEGER; + #endif + case SG_PIXELFORMAT_RGBA8: + case SG_PIXELFORMAT_RGBA8SN: + case SG_PIXELFORMAT_RGBA16: + case SG_PIXELFORMAT_RGBA16SN: + case SG_PIXELFORMAT_RGBA16F: + case SG_PIXELFORMAT_RGBA32F: + case SG_PIXELFORMAT_RGB10A2: + return GL_RGBA; + #if !defined(SOKOL_GLES2) + case SG_PIXELFORMAT_RGBA8UI: + case SG_PIXELFORMAT_RGBA8SI: + case SG_PIXELFORMAT_RGBA16UI: + case SG_PIXELFORMAT_RGBA16SI: + case SG_PIXELFORMAT_RGBA32UI: + case SG_PIXELFORMAT_RGBA32SI: + return GL_RGBA_INTEGER; + #endif + case SG_PIXELFORMAT_RG11B10F: + return GL_RGB; + case SG_PIXELFORMAT_DEPTH: + return GL_DEPTH_COMPONENT; + case SG_PIXELFORMAT_DEPTH_STENCIL: + return GL_DEPTH_STENCIL; + case SG_PIXELFORMAT_BC1_RGBA: + return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + case SG_PIXELFORMAT_BC2_RGBA: + return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + case SG_PIXELFORMAT_BC3_RGBA: + return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + case SG_PIXELFORMAT_BC4_R: + return GL_COMPRESSED_RED_RGTC1; + case SG_PIXELFORMAT_BC4_RSN: + return GL_COMPRESSED_SIGNED_RED_RGTC1; + case SG_PIXELFORMAT_BC5_RG: + return GL_COMPRESSED_RED_GREEN_RGTC2; + case SG_PIXELFORMAT_BC5_RGSN: + return GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2; + case SG_PIXELFORMAT_BC6H_RGBF: + return GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB; + case SG_PIXELFORMAT_BC6H_RGBUF: + return GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB; + case SG_PIXELFORMAT_BC7_RGBA: + return GL_COMPRESSED_RGBA_BPTC_UNORM_ARB; + case SG_PIXELFORMAT_PVRTC_RGB_2BPP: + return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG; + case SG_PIXELFORMAT_PVRTC_RGB_4BPP: + return GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; + case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: + return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; + case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: + return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; + case SG_PIXELFORMAT_ETC2_RGB8: + return GL_COMPRESSED_RGB8_ETC2; + case SG_PIXELFORMAT_ETC2_RGB8A1: + return GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2; + case SG_PIXELFORMAT_ETC2_RGBA8: + return GL_COMPRESSED_RGBA8_ETC2_EAC; + case SG_PIXELFORMAT_ETC2_RG11: + return GL_COMPRESSED_RG11_EAC; + case SG_PIXELFORMAT_ETC2_RG11SN: + return GL_COMPRESSED_SIGNED_RG11_EAC; + default: + SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_teximage_internal_format(sg_pixel_format fmt) { + #if defined(SOKOL_GLES2) + return _sg_gl_teximage_format(fmt); + #else + if (_sg.gl.gles2) { + return _sg_gl_teximage_format(fmt); + } + else { + switch (fmt) { + case SG_PIXELFORMAT_R8: return GL_R8; + case SG_PIXELFORMAT_R8SN: return GL_R8_SNORM; + case SG_PIXELFORMAT_R8UI: return GL_R8UI; + case SG_PIXELFORMAT_R8SI: return GL_R8I; + #if !defined(SOKOL_GLES3) + case SG_PIXELFORMAT_R16: return GL_R16; + case SG_PIXELFORMAT_R16SN: return GL_R16_SNORM; + #endif + case SG_PIXELFORMAT_R16UI: return GL_R16UI; + case SG_PIXELFORMAT_R16SI: return GL_R16I; + case SG_PIXELFORMAT_R16F: return GL_R16F; + case SG_PIXELFORMAT_RG8: return GL_RG8; + case SG_PIXELFORMAT_RG8SN: return GL_RG8_SNORM; + case SG_PIXELFORMAT_RG8UI: return GL_RG8UI; + case SG_PIXELFORMAT_RG8SI: return GL_RG8I; + case SG_PIXELFORMAT_R32UI: return GL_R32UI; + case SG_PIXELFORMAT_R32SI: return GL_R32I; + case SG_PIXELFORMAT_R32F: return GL_R32F; + #if !defined(SOKOL_GLES3) + case SG_PIXELFORMAT_RG16: return GL_RG16; + case SG_PIXELFORMAT_RG16SN: return GL_RG16_SNORM; + #endif + case SG_PIXELFORMAT_RG16UI: return GL_RG16UI; + case SG_PIXELFORMAT_RG16SI: return GL_RG16I; + case SG_PIXELFORMAT_RG16F: return GL_RG16F; + case SG_PIXELFORMAT_RGBA8: return GL_RGBA8; + case SG_PIXELFORMAT_RGBA8SN: return GL_RGBA8_SNORM; + case SG_PIXELFORMAT_RGBA8UI: return GL_RGBA8UI; + case SG_PIXELFORMAT_RGBA8SI: return GL_RGBA8I; + case SG_PIXELFORMAT_RGB10A2: return GL_RGB10_A2; + case SG_PIXELFORMAT_RG11B10F: return GL_R11F_G11F_B10F; + case SG_PIXELFORMAT_RG32UI: return GL_RG32UI; + case SG_PIXELFORMAT_RG32SI: return GL_RG32I; + case SG_PIXELFORMAT_RG32F: return GL_RG32F; + #if !defined(SOKOL_GLES3) + case SG_PIXELFORMAT_RGBA16: return GL_RGBA16; + case SG_PIXELFORMAT_RGBA16SN: return GL_RGBA16_SNORM; + #endif + case SG_PIXELFORMAT_RGBA16UI: return GL_RGBA16UI; + case SG_PIXELFORMAT_RGBA16SI: return GL_RGBA16I; + case SG_PIXELFORMAT_RGBA16F: return GL_RGBA16F; + case SG_PIXELFORMAT_RGBA32UI: return GL_RGBA32UI; + case SG_PIXELFORMAT_RGBA32SI: return GL_RGBA32I; + case SG_PIXELFORMAT_RGBA32F: return GL_RGBA32F; + case SG_PIXELFORMAT_DEPTH: return GL_DEPTH_COMPONENT16; + case SG_PIXELFORMAT_DEPTH_STENCIL: return GL_DEPTH24_STENCIL8; + case SG_PIXELFORMAT_BC1_RGBA: return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + case SG_PIXELFORMAT_BC2_RGBA: return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + case SG_PIXELFORMAT_BC3_RGBA: return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + case SG_PIXELFORMAT_BC4_R: return GL_COMPRESSED_RED_RGTC1; + case SG_PIXELFORMAT_BC4_RSN: return GL_COMPRESSED_SIGNED_RED_RGTC1; + case SG_PIXELFORMAT_BC5_RG: return GL_COMPRESSED_RED_GREEN_RGTC2; + case SG_PIXELFORMAT_BC5_RGSN: return GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2; + case SG_PIXELFORMAT_BC6H_RGBF: return GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB; + case SG_PIXELFORMAT_BC6H_RGBUF: return GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB; + case SG_PIXELFORMAT_BC7_RGBA: return GL_COMPRESSED_RGBA_BPTC_UNORM_ARB; + case SG_PIXELFORMAT_PVRTC_RGB_2BPP: return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG; + case SG_PIXELFORMAT_PVRTC_RGB_4BPP: return GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG; + case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG; + case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG; + case SG_PIXELFORMAT_ETC2_RGB8: return GL_COMPRESSED_RGB8_ETC2; + case SG_PIXELFORMAT_ETC2_RGB8A1: return GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2; + case SG_PIXELFORMAT_ETC2_RGBA8: return GL_COMPRESSED_RGBA8_ETC2_EAC; + case SG_PIXELFORMAT_ETC2_RG11: return GL_COMPRESSED_RG11_EAC; + case SG_PIXELFORMAT_ETC2_RG11SN: return GL_COMPRESSED_SIGNED_RG11_EAC; + default: SOKOL_UNREACHABLE; return 0; + } + } + #endif +} + +_SOKOL_PRIVATE GLenum _sg_gl_cubeface_target(int face_index) { + switch (face_index) { + case 0: return GL_TEXTURE_CUBE_MAP_POSITIVE_X; + case 1: return GL_TEXTURE_CUBE_MAP_NEGATIVE_X; + case 2: return GL_TEXTURE_CUBE_MAP_POSITIVE_Y; + case 3: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Y; + case 4: return GL_TEXTURE_CUBE_MAP_POSITIVE_Z; + case 5: return GL_TEXTURE_CUBE_MAP_NEGATIVE_Z; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE GLenum _sg_gl_depth_attachment_format(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_DEPTH: return GL_DEPTH_COMPONENT16; + case SG_PIXELFORMAT_DEPTH_STENCIL: return GL_DEPTH24_STENCIL8; + default: SOKOL_UNREACHABLE; return 0; + } +} + +/* see: https://www.khronos.org/registry/OpenGL-Refpages/es3.0/html/glTexImage2D.xhtml */ +_SOKOL_PRIVATE void _sg_gl_init_pixelformats(bool has_bgra) { + #if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R8]); + } + else { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R8]); + } + #else + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R8]); + #endif + #if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8SI]); + #if !defined(SOKOL_GLES3) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16SN]); + #endif + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32SI]); + #if !defined(SOKOL_GLES3) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16SN]); + #endif + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16SI]); + } + #endif + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA8]); + #if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8SI]); + } + #endif + if (has_bgra) { + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_BGRA8]); + } + #if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGB10A2]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG11B10F]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG32UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG32SI]); + #if !defined(SOKOL_GLES3) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16SN]); + #endif + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16SI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]); + } + #endif + // FIXME: WEBGL_depth_texture extension? + _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH]); + _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH_STENCIL]); +} + +/* FIXME: OES_half_float_blend */ +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_half_float(bool has_colorbuffer_half_float, bool has_texture_half_float_linear) { + #if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + if (has_texture_half_float_linear) { + if (has_colorbuffer_half_float) { + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); + } + else { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R16F]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG16F]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); + } + } + else { + if (has_colorbuffer_half_float) { + _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_R16F]); + _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RG16F]); + _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); + } + else { + _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_R16F]); + _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RG16F]); + _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); + } + } + } + else { + #endif + /* GLES2 can only render to RGBA, and there's no RG format */ + if (has_texture_half_float_linear) { + if (has_colorbuffer_half_float) { + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); + } + else { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); + } + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R16F]); + } + else { + if (has_colorbuffer_half_float) { + _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); + } + else { + _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); + } + _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_R16F]); + } + #if !defined(SOKOL_GLES2) + } + #endif +} + +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_float(bool has_colorbuffer_float, bool has_texture_float_linear, bool has_float_blend) { + #if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + if (has_texture_float_linear) { + if (has_colorbuffer_float) { + if (has_float_blend) { + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } + else { + _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } + } + else { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } + } + else { + if (has_colorbuffer_float) { + _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } + else { + _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } + } + } + else { + #endif + /* GLES2 can only render to RGBA, and there's no RG format */ + if (has_texture_float_linear) { + if (has_colorbuffer_float) { + if (has_float_blend) { + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } + else { + _sg_pixelformat_sfrm(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } + } + else { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R32F]); + } + else { + if (has_colorbuffer_float) { + _sg_pixelformat_sbrm(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } + else { + _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + } + _sg_pixelformat_s(&_sg.formats[SG_PIXELFORMAT_R32F]); + } + #if !defined(SOKOL_GLES2) + } + #endif +} + +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_s3tc(void) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC1_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC2_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_RGBA]); +} + +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_rgtc(void) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_R]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_RSN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RG]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RGSN]); +} + +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_bptc(void) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBF]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBUF]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_RGBA]); +} + +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_pvrtc(void) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGB_2BPP]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGB_4BPP]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGBA_2BPP]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGBA_4BPP]); +} + +_SOKOL_PRIVATE void _sg_gl_init_pixelformats_etc2(void) { + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8A1]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGBA8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RG11]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RG11SN]); +} + +_SOKOL_PRIVATE void _sg_gl_init_limits(void) { + _SG_GL_CHECK_ERROR(); + GLint gl_int; + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.limits.max_image_size_2d = gl_int; + _sg.limits.max_image_size_array = gl_int; + glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.limits.max_image_size_cube = gl_int; + glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &gl_int); + _SG_GL_CHECK_ERROR(); + if (gl_int > SG_MAX_VERTEX_ATTRIBUTES) { + gl_int = SG_MAX_VERTEX_ATTRIBUTES; + } + _sg.limits.max_vertex_attrs = gl_int; + #if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.limits.max_image_size_3d = gl_int; + glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.limits.max_image_array_layers = gl_int; + } + #endif + if (_sg.gl.ext_anisotropic) { + glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.gl.max_anisotropy = gl_int; + } + else { + _sg.gl.max_anisotropy = 1; + } + glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &gl_int); + _SG_GL_CHECK_ERROR(); + _sg.gl.max_combined_texture_image_units = gl_int; +} + +#if defined(SOKOL_GLCORE33) +_SOKOL_PRIVATE void _sg_gl_init_caps_glcore33(void) { + _sg.backend = SG_BACKEND_GLCORE33; + + _sg.features.origin_top_left = false; + _sg.features.instancing = true; + _sg.features.multiple_render_targets = true; + _sg.features.msaa_render_targets = true; + _sg.features.imagetype_3d = true; + _sg.features.imagetype_array = true; + _sg.features.image_clamp_to_border = true; + _sg.features.mrt_independent_blend_state = false; + _sg.features.mrt_independent_write_mask = true; + + /* scan extensions */ + bool has_s3tc = false; /* BC1..BC3 */ + bool has_rgtc = false; /* BC4 and BC5 */ + bool has_bptc = false; /* BC6H and BC7 */ + bool has_pvrtc = false; + bool has_etc2 = false; + GLint num_ext = 0; + glGetIntegerv(GL_NUM_EXTENSIONS, &num_ext); + for (int i = 0; i < num_ext; i++) { + const char* ext = (const char*) glGetStringi(GL_EXTENSIONS, (GLuint)i); + if (ext) { + if (strstr(ext, "_texture_compression_s3tc")) { + has_s3tc = true; + } + else if (strstr(ext, "_texture_compression_rgtc")) { + has_rgtc = true; + } + else if (strstr(ext, "_texture_compression_bptc")) { + has_bptc = true; + } + else if (strstr(ext, "_texture_compression_pvrtc")) { + has_pvrtc = true; + } + else if (strstr(ext, "_ES3_compatibility")) { + has_etc2 = true; + } + else if (strstr(ext, "_texture_filter_anisotropic")) { + _sg.gl.ext_anisotropic = true; + } + } + } + + /* limits */ + _sg_gl_init_limits(); + + /* pixel formats */ + const bool has_bgra = false; /* not a bug */ + const bool has_colorbuffer_float = true; + const bool has_colorbuffer_half_float = true; + const bool has_texture_float_linear = true; /* FIXME??? */ + const bool has_texture_half_float_linear = true; + const bool has_float_blend = true; + _sg_gl_init_pixelformats(has_bgra); + _sg_gl_init_pixelformats_float(has_colorbuffer_float, has_texture_float_linear, has_float_blend); + _sg_gl_init_pixelformats_half_float(has_colorbuffer_half_float, has_texture_half_float_linear); + if (has_s3tc) { + _sg_gl_init_pixelformats_s3tc(); + } + if (has_rgtc) { + _sg_gl_init_pixelformats_rgtc(); + } + if (has_bptc) { + _sg_gl_init_pixelformats_bptc(); + } + if (has_pvrtc) { + _sg_gl_init_pixelformats_pvrtc(); + } + if (has_etc2) { + _sg_gl_init_pixelformats_etc2(); + } +} +#endif + +#if defined(SOKOL_GLES3) +_SOKOL_PRIVATE void _sg_gl_init_caps_gles3(void) { + _sg.backend = SG_BACKEND_GLES3; + + _sg.features.origin_top_left = false; + _sg.features.instancing = true; + _sg.features.multiple_render_targets = true; + _sg.features.msaa_render_targets = true; + _sg.features.imagetype_3d = true; + _sg.features.imagetype_array = true; + _sg.features.image_clamp_to_border = false; + _sg.features.mrt_independent_blend_state = false; + _sg.features.mrt_independent_write_mask = false; + + bool has_s3tc = false; /* BC1..BC3 */ + bool has_rgtc = false; /* BC4 and BC5 */ + bool has_bptc = false; /* BC6H and BC7 */ + bool has_pvrtc = false; + #if defined(__EMSCRIPTEN__) + bool has_etc2 = false; + #else + bool has_etc2 = true; + #endif + bool has_colorbuffer_float = false; + bool has_colorbuffer_half_float = false; + bool has_texture_float_linear = false; + bool has_float_blend = false; + GLint num_ext = 0; + glGetIntegerv(GL_NUM_EXTENSIONS, &num_ext); + for (int i = 0; i < num_ext; i++) { + const char* ext = (const char*) glGetStringi(GL_EXTENSIONS, (GLuint)i); + if (ext) { + if (strstr(ext, "_texture_compression_s3tc")) { + has_s3tc = true; + } + else if (strstr(ext, "_compressed_texture_s3tc")) { + has_s3tc = true; + } + else if (strstr(ext, "_texture_compression_rgtc")) { + has_rgtc = true; + } + else if (strstr(ext, "_texture_compression_bptc")) { + has_bptc = true; + } + else if (strstr(ext, "_texture_compression_pvrtc")) { + has_pvrtc = true; + } + else if (strstr(ext, "_compressed_texture_pvrtc")) { + has_pvrtc = true; + } + else if (strstr(ext, "_compressed_texture_etc")) { + has_etc2 = true; + } + else if (strstr(ext, "_color_buffer_float")) { + has_colorbuffer_float = true; + } + else if (strstr(ext, "_color_buffer_half_float")) { + has_colorbuffer_half_float = true; + } + else if (strstr(ext, "_texture_float_linear")) { + has_texture_float_linear = true; + } + else if (strstr(ext, "_float_blend")) { + has_float_blend = true; + } + else if (strstr(ext, "_texture_filter_anisotropic")) { + _sg.gl.ext_anisotropic = true; + } + } + } + + /* on WebGL2, color_buffer_float also includes 16-bit formats + see: https://developer.mozilla.org/en-US/docs/Web/API/EXT_color_buffer_float + */ + #if defined(__EMSCRIPTEN__) + has_colorbuffer_half_float = has_colorbuffer_float; + #endif + + /* limits */ + _sg_gl_init_limits(); + + /* pixel formats */ + const bool has_texture_half_float_linear = true; + const bool has_bgra = false; /* not a bug */ + _sg_gl_init_pixelformats(has_bgra); + _sg_gl_init_pixelformats_float(has_colorbuffer_float, has_texture_float_linear, has_float_blend); + _sg_gl_init_pixelformats_half_float(has_colorbuffer_half_float, has_texture_half_float_linear); + if (has_s3tc) { + _sg_gl_init_pixelformats_s3tc(); + } + if (has_rgtc) { + _sg_gl_init_pixelformats_rgtc(); + } + if (has_bptc) { + _sg_gl_init_pixelformats_bptc(); + } + if (has_pvrtc) { + _sg_gl_init_pixelformats_pvrtc(); + } + if (has_etc2) { + _sg_gl_init_pixelformats_etc2(); + } +} +#endif + +#if defined(SOKOL_GLES3) || defined(SOKOL_GLES2) +_SOKOL_PRIVATE void _sg_gl_init_caps_gles2(void) { + _sg.backend = SG_BACKEND_GLES2; + + bool has_s3tc = false; /* BC1..BC3 */ + bool has_rgtc = false; /* BC4 and BC5 */ + bool has_bptc = false; /* BC6H and BC7 */ + bool has_pvrtc = false; + bool has_etc2 = false; + bool has_texture_float = false; + bool has_texture_float_linear = false; + bool has_colorbuffer_float = false; + bool has_float_blend = false; + bool has_instancing = false; + const char* ext = (const char*) glGetString(GL_EXTENSIONS); + if (ext) { + has_s3tc = strstr(ext, "_texture_compression_s3tc") || strstr(ext, "_compressed_texture_s3tc"); + has_rgtc = strstr(ext, "_texture_compression_rgtc"); + has_bptc = strstr(ext, "_texture_compression_bptc"); + has_pvrtc = strstr(ext, "_texture_compression_pvrtc") || strstr(ext, "_compressed_texture_pvrtc"); + has_etc2 = strstr(ext, "_compressed_texture_etc"); + has_texture_float = strstr(ext, "_texture_float"); + has_texture_float_linear = strstr(ext, "_texture_float_linear"); + has_colorbuffer_float = strstr(ext, "_color_buffer_float"); + has_float_blend = strstr(ext, "_float_blend"); + /* don't bother with half_float support on WebGL1 + has_texture_half_float = strstr(ext, "_texture_half_float"); + has_texture_half_float_linear = strstr(ext, "_texture_half_float_linear"); + has_colorbuffer_half_float = strstr(ext, "_color_buffer_half_float"); + */ + has_instancing = strstr(ext, "_instanced_arrays"); + _sg.gl.ext_anisotropic = strstr(ext, "ext_anisotropic"); + } + + _sg.features.origin_top_left = false; + #if defined(_SOKOL_GL_INSTANCING_ENABLED) + _sg.features.instancing = has_instancing; + #endif + _sg.features.multiple_render_targets = false; + _sg.features.msaa_render_targets = false; + _sg.features.imagetype_3d = false; + _sg.features.imagetype_array = false; + _sg.features.image_clamp_to_border = false; + _sg.features.mrt_independent_blend_state = false; + _sg.features.mrt_independent_write_mask = false; + + /* limits */ + _sg_gl_init_limits(); + + /* pixel formats */ + const bool has_bgra = false; /* not a bug */ + const bool has_texture_half_float = false; + const bool has_texture_half_float_linear = false; + const bool has_colorbuffer_half_float = false; + _sg_gl_init_pixelformats(has_bgra); + if (has_texture_float) { + _sg_gl_init_pixelformats_float(has_colorbuffer_float, has_texture_float_linear, has_float_blend); + } + if (has_texture_half_float) { + _sg_gl_init_pixelformats_half_float(has_colorbuffer_half_float, has_texture_half_float_linear); + } + if (has_s3tc) { + _sg_gl_init_pixelformats_s3tc(); + } + if (has_rgtc) { + _sg_gl_init_pixelformats_rgtc(); + } + if (has_bptc) { + _sg_gl_init_pixelformats_bptc(); + } + if (has_pvrtc) { + _sg_gl_init_pixelformats_pvrtc(); + } + if (has_etc2) { + _sg_gl_init_pixelformats_etc2(); + } + /* GLES2 doesn't allow multi-sampled render targets at all */ + for (int i = 0; i < _SG_PIXELFORMAT_NUM; i++) { + _sg.formats[i].msaa = false; + } +} +#endif + +/*-- state cache implementation ----------------------------------------------*/ +_SOKOL_PRIVATE void _sg_gl_cache_clear_buffer_bindings(bool force) { + if (force || (_sg.gl.cache.vertex_buffer != 0)) { + glBindBuffer(GL_ARRAY_BUFFER, 0); + _sg.gl.cache.vertex_buffer = 0; + } + if (force || (_sg.gl.cache.index_buffer != 0)) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + _sg.gl.cache.index_buffer = 0; + } +} + +_SOKOL_PRIVATE void _sg_gl_cache_bind_buffer(GLenum target, GLuint buffer) { + SOKOL_ASSERT((GL_ARRAY_BUFFER == target) || (GL_ELEMENT_ARRAY_BUFFER == target)); + if (target == GL_ARRAY_BUFFER) { + if (_sg.gl.cache.vertex_buffer != buffer) { + _sg.gl.cache.vertex_buffer = buffer; + glBindBuffer(target, buffer); + } + } + else { + if (_sg.gl.cache.index_buffer != buffer) { + _sg.gl.cache.index_buffer = buffer; + glBindBuffer(target, buffer); + } + } +} + +_SOKOL_PRIVATE void _sg_gl_cache_store_buffer_binding(GLenum target) { + if (target == GL_ARRAY_BUFFER) { + _sg.gl.cache.stored_vertex_buffer = _sg.gl.cache.vertex_buffer; + } + else { + _sg.gl.cache.stored_index_buffer = _sg.gl.cache.index_buffer; + } +} + +_SOKOL_PRIVATE void _sg_gl_cache_restore_buffer_binding(GLenum target) { + if (target == GL_ARRAY_BUFFER) { + if (_sg.gl.cache.stored_vertex_buffer != 0) { + /* we only care restoring valid ids */ + _sg_gl_cache_bind_buffer(target, _sg.gl.cache.stored_vertex_buffer); + _sg.gl.cache.stored_vertex_buffer = 0; + } + } + else { + if (_sg.gl.cache.stored_index_buffer != 0) { + /* we only care restoring valid ids */ + _sg_gl_cache_bind_buffer(target, _sg.gl.cache.stored_index_buffer); + _sg.gl.cache.stored_index_buffer = 0; + } + } +} + +/* called when from _sg_gl_destroy_buffer() */ +_SOKOL_PRIVATE void _sg_gl_cache_invalidate_buffer(GLuint buf) { + if (buf == _sg.gl.cache.vertex_buffer) { + _sg.gl.cache.vertex_buffer = 0; + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + if (buf == _sg.gl.cache.index_buffer) { + _sg.gl.cache.index_buffer = 0; + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + if (buf == _sg.gl.cache.stored_vertex_buffer) { + _sg.gl.cache.stored_vertex_buffer = 0; + } + if (buf == _sg.gl.cache.stored_index_buffer) { + _sg.gl.cache.stored_index_buffer = 0; + } + for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { + if (buf == _sg.gl.cache.attrs[i].gl_vbuf) { + _sg.gl.cache.attrs[i].gl_vbuf = 0; + } + } +} + +_SOKOL_PRIVATE void _sg_gl_cache_active_texture(GLenum texture) { + if (_sg.gl.cache.cur_active_texture != texture) { + _sg.gl.cache.cur_active_texture = texture; + glActiveTexture(texture); + } +} + +_SOKOL_PRIVATE void _sg_gl_cache_clear_texture_bindings(bool force) { + for (int i = 0; (i < SG_MAX_SHADERSTAGE_IMAGES) && (i < _sg.gl.max_combined_texture_image_units); i++) { + if (force || (_sg.gl.cache.textures[i].texture != 0)) { + GLenum gl_texture_slot = (GLenum) (GL_TEXTURE0 + i); + glActiveTexture(gl_texture_slot); + glBindTexture(GL_TEXTURE_2D, 0); + glBindTexture(GL_TEXTURE_CUBE_MAP, 0); + #if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + glBindTexture(GL_TEXTURE_3D, 0); + glBindTexture(GL_TEXTURE_2D_ARRAY, 0); + } + #endif + _sg.gl.cache.textures[i].target = 0; + _sg.gl.cache.textures[i].texture = 0; + _sg.gl.cache.cur_active_texture = gl_texture_slot; + } + } +} + +_SOKOL_PRIVATE void _sg_gl_cache_bind_texture(int slot_index, GLenum target, GLuint texture) { + /* it's valid to call this function with target=0 and/or texture=0 + target=0 will unbind the previous binding, texture=0 will clear + the new binding + */ + SOKOL_ASSERT(slot_index < SG_MAX_SHADERSTAGE_IMAGES); + if (slot_index >= _sg.gl.max_combined_texture_image_units) { + return; + } + _sg_gl_texture_bind_slot* slot = &_sg.gl.cache.textures[slot_index]; + if ((slot->target != target) || (slot->texture != texture)) { + _sg_gl_cache_active_texture((GLenum)(GL_TEXTURE0 + slot_index)); + /* if the target has changed, clear the previous binding on that target */ + if ((target != slot->target) && (slot->target != 0)) { + glBindTexture(slot->target, 0); + } + /* apply new binding (texture can be 0 to unbind) */ + if (target != 0) { + glBindTexture(target, texture); + } + slot->target = target; + slot->texture = texture; + } +} + +_SOKOL_PRIVATE void _sg_gl_cache_store_texture_binding(int slot_index) { + SOKOL_ASSERT(slot_index < SG_MAX_SHADERSTAGE_IMAGES); + _sg.gl.cache.stored_texture = _sg.gl.cache.textures[slot_index]; +} + +_SOKOL_PRIVATE void _sg_gl_cache_restore_texture_binding(int slot_index) { + SOKOL_ASSERT(slot_index < SG_MAX_SHADERSTAGE_IMAGES); + _sg_gl_texture_bind_slot* slot = &_sg.gl.cache.stored_texture; + if (slot->texture != 0) { + /* we only care restoring valid ids */ + SOKOL_ASSERT(slot->target != 0); + _sg_gl_cache_bind_texture(slot_index, slot->target, slot->texture); + slot->target = 0; + slot->texture = 0; + } +} + +/* called from _sg_gl_destroy_texture() */ +_SOKOL_PRIVATE void _sg_gl_cache_invalidate_texture(GLuint tex) { + for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { + _sg_gl_texture_bind_slot* slot = &_sg.gl.cache.textures[i]; + if (tex == slot->texture) { + _sg_gl_cache_active_texture((GLenum)(GL_TEXTURE0 + i)); + glBindTexture(slot->target, 0); + slot->target = 0; + slot->texture = 0; + } + } + if (tex == _sg.gl.cache.stored_texture.texture) { + _sg.gl.cache.stored_texture.target = 0; + _sg.gl.cache.stored_texture.texture = 0; + } +} + +/* called from _sg_gl_destroy_shader() */ +_SOKOL_PRIVATE void _sg_gl_cache_invalidate_program(GLuint prog) { + if (prog == _sg.gl.cache.prog) { + _sg.gl.cache.prog = 0; + glUseProgram(0); + } +} + +_SOKOL_PRIVATE void _sg_gl_reset_state_cache(void) { + if (_sg.gl.cur_context) { + _SG_GL_CHECK_ERROR(); + #if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + glBindVertexArray(_sg.gl.cur_context->vao); + _SG_GL_CHECK_ERROR(); + } + #endif + memset(&_sg.gl.cache, 0, sizeof(_sg.gl.cache)); + _sg_gl_cache_clear_buffer_bindings(true); + _SG_GL_CHECK_ERROR(); + _sg_gl_cache_clear_texture_bindings(true); + _SG_GL_CHECK_ERROR(); + for (int i = 0; i < _sg.limits.max_vertex_attrs; i++) { + _sg_gl_attr_t* attr = &_sg.gl.cache.attrs[i].gl_attr; + attr->vb_index = -1; + attr->divisor = -1; + glDisableVertexAttribArray((GLuint)i); + _SG_GL_CHECK_ERROR(); + } + _sg.gl.cache.cur_primitive_type = GL_TRIANGLES; + + /* shader program */ + glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&_sg.gl.cache.prog); + _SG_GL_CHECK_ERROR(); + + /* depth and stencil state */ + _sg.gl.cache.depth.compare = SG_COMPAREFUNC_ALWAYS; + _sg.gl.cache.stencil.front.compare = SG_COMPAREFUNC_ALWAYS; + _sg.gl.cache.stencil.front.fail_op = SG_STENCILOP_KEEP; + _sg.gl.cache.stencil.front.depth_fail_op = SG_STENCILOP_KEEP; + _sg.gl.cache.stencil.front.pass_op = SG_STENCILOP_KEEP; + _sg.gl.cache.stencil.back.compare = SG_COMPAREFUNC_ALWAYS; + _sg.gl.cache.stencil.back.fail_op = SG_STENCILOP_KEEP; + _sg.gl.cache.stencil.back.depth_fail_op = SG_STENCILOP_KEEP; + _sg.gl.cache.stencil.back.pass_op = SG_STENCILOP_KEEP; + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_ALWAYS); + glDepthMask(GL_FALSE); + glDisable(GL_STENCIL_TEST); + glStencilFunc(GL_ALWAYS, 0, 0); + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glStencilMask(0); + + /* blend state */ + _sg.gl.cache.blend.src_factor_rgb = SG_BLENDFACTOR_ONE; + _sg.gl.cache.blend.dst_factor_rgb = SG_BLENDFACTOR_ZERO; + _sg.gl.cache.blend.op_rgb = SG_BLENDOP_ADD; + _sg.gl.cache.blend.src_factor_alpha = SG_BLENDFACTOR_ONE; + _sg.gl.cache.blend.dst_factor_alpha = SG_BLENDFACTOR_ZERO; + _sg.gl.cache.blend.op_alpha = SG_BLENDOP_ADD; + glDisable(GL_BLEND); + glBlendFuncSeparate(GL_ONE, GL_ZERO, GL_ONE, GL_ZERO); + glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD); + glBlendColor(0.0f, 0.0f, 0.0f, 0.0f); + + /* standalone state */ + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + _sg.gl.cache.color_write_mask[i] = SG_COLORMASK_RGBA; + } + _sg.gl.cache.cull_mode = SG_CULLMODE_NONE; + _sg.gl.cache.face_winding = SG_FACEWINDING_CW; + _sg.gl.cache.sample_count = 1; + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glPolygonOffset(0.0f, 0.0f); + glDisable(GL_POLYGON_OFFSET_FILL); + glDisable(GL_CULL_FACE); + glFrontFace(GL_CW); + glCullFace(GL_BACK); + glEnable(GL_SCISSOR_TEST); + glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); + glEnable(GL_DITHER); + glDisable(GL_POLYGON_OFFSET_FILL); + #if defined(SOKOL_GLCORE33) + glEnable(GL_MULTISAMPLE); + glEnable(GL_PROGRAM_POINT_SIZE); + #endif + } +} + +_SOKOL_PRIVATE void _sg_gl_setup_backend(const sg_desc* desc) { + /* assumes that _sg.gl is already zero-initialized */ + _sg.gl.valid = true; + #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + _sg.gl.gles2 = desc->context.gl.force_gles2; + #else + _SOKOL_UNUSED(desc); + _sg.gl.gles2 = false; + #endif + + #if defined(_SOKOL_USE_WIN32_GL_LOADER) + _sg_gl_load_opengl(); + #endif + + /* clear initial GL error state */ + #if defined(SOKOL_DEBUG) + while (glGetError() != GL_NO_ERROR); + #endif + #if defined(SOKOL_GLCORE33) + _sg_gl_init_caps_glcore33(); + #elif defined(SOKOL_GLES3) + if (_sg.gl.gles2) { + _sg_gl_init_caps_gles2(); + } + else { + _sg_gl_init_caps_gles3(); + } + #else + _sg_gl_init_caps_gles2(); + #endif +} + +_SOKOL_PRIVATE void _sg_gl_discard_backend(void) { + SOKOL_ASSERT(_sg.gl.valid); + _sg.gl.valid = false; + #if defined(_SOKOL_USE_WIN32_GL_LOADER) + _sg_gl_unload_opengl(); + #endif +} + +_SOKOL_PRIVATE void _sg_gl_activate_context(_sg_context_t* ctx) { + SOKOL_ASSERT(_sg.gl.valid); + /* NOTE: ctx can be 0 to unset the current context */ + _sg.gl.cur_context = ctx; + _sg_gl_reset_state_cache(); +} + +/*-- GL backend resource creation and destruction ----------------------------*/ +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_context(_sg_context_t* ctx) { + SOKOL_ASSERT(ctx); + SOKOL_ASSERT(0 == ctx->default_framebuffer); + _SG_GL_CHECK_ERROR(); + glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&ctx->default_framebuffer); + _SG_GL_CHECK_ERROR(); + #if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + SOKOL_ASSERT(0 == ctx->vao); + glGenVertexArrays(1, &ctx->vao); + glBindVertexArray(ctx->vao); + _SG_GL_CHECK_ERROR(); + } + #endif + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_gl_destroy_context(_sg_context_t* ctx) { + SOKOL_ASSERT(ctx); + #if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + if (ctx->vao) { + glDeleteVertexArrays(1, &ctx->vao); + } + _SG_GL_CHECK_ERROR(); + } + #else + _SOKOL_UNUSED(ctx); + #endif +} + +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { + SOKOL_ASSERT(buf && desc); + _SG_GL_CHECK_ERROR(); + _sg_buffer_common_init(&buf->cmn, desc); + buf->gl.ext_buffers = (0 != desc->gl_buffers[0]); + GLenum gl_target = _sg_gl_buffer_target(buf->cmn.type); + GLenum gl_usage = _sg_gl_usage(buf->cmn.usage); + for (int slot = 0; slot < buf->cmn.num_slots; slot++) { + GLuint gl_buf = 0; + if (buf->gl.ext_buffers) { + SOKOL_ASSERT(desc->gl_buffers[slot]); + gl_buf = desc->gl_buffers[slot]; + } + else { + glGenBuffers(1, &gl_buf); + _sg_gl_cache_store_buffer_binding(gl_target); + _sg_gl_cache_bind_buffer(gl_target, gl_buf); + glBufferData(gl_target, buf->cmn.size, 0, gl_usage); + if (buf->cmn.usage == SG_USAGE_IMMUTABLE) { + SOKOL_ASSERT(desc->data.ptr); + glBufferSubData(gl_target, 0, buf->cmn.size, desc->data.ptr); + } + _sg_gl_cache_restore_buffer_binding(gl_target); + } + buf->gl.buf[slot] = gl_buf; + } + _SG_GL_CHECK_ERROR(); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_gl_destroy_buffer(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf); + _SG_GL_CHECK_ERROR(); + for (int slot = 0; slot < buf->cmn.num_slots; slot++) { + if (buf->gl.buf[slot]) { + _sg_gl_cache_invalidate_buffer(buf->gl.buf[slot]); + if (!buf->gl.ext_buffers) { + glDeleteBuffers(1, &buf->gl.buf[slot]); + } + } + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE bool _sg_gl_supported_texture_format(sg_pixel_format fmt) { + const int fmt_index = (int) fmt; + SOKOL_ASSERT((fmt_index > SG_PIXELFORMAT_NONE) && (fmt_index < _SG_PIXELFORMAT_NUM)); + return _sg.formats[fmt_index].sample; +} + +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_image(_sg_image_t* img, const sg_image_desc* desc) { + SOKOL_ASSERT(img && desc); + _SG_GL_CHECK_ERROR(); + _sg_image_common_init(&img->cmn, desc); + img->gl.ext_textures = (0 != desc->gl_textures[0]); + + /* check if texture format is support */ + if (!_sg_gl_supported_texture_format(img->cmn.pixel_format)) { + SOKOL_LOG("texture format not supported by GL context\n"); + return SG_RESOURCESTATE_FAILED; + } + /* check for optional texture types */ + if ((img->cmn.type == SG_IMAGETYPE_3D) && !_sg.features.imagetype_3d) { + SOKOL_LOG("3D textures not supported by GL context\n"); + return SG_RESOURCESTATE_FAILED; + } + if ((img->cmn.type == SG_IMAGETYPE_ARRAY) && !_sg.features.imagetype_array) { + SOKOL_LOG("array textures not supported by GL context\n"); + return SG_RESOURCESTATE_FAILED; + } + + #if !defined(SOKOL_GLES2) + bool msaa = false; + if (!_sg.gl.gles2) { + msaa = (img->cmn.sample_count > 1) && (_sg.features.msaa_render_targets); + } + #endif + + if (_sg_is_valid_rendertarget_depth_format(img->cmn.pixel_format)) { + /* special case depth-stencil-buffer? */ + SOKOL_ASSERT((img->cmn.usage == SG_USAGE_IMMUTABLE) && (img->cmn.num_slots == 1)); + SOKOL_ASSERT(!img->gl.ext_textures); /* cannot provide external texture for depth images */ + glGenRenderbuffers(1, &img->gl.depth_render_buffer); + glBindRenderbuffer(GL_RENDERBUFFER, img->gl.depth_render_buffer); + GLenum gl_depth_format = _sg_gl_depth_attachment_format(img->cmn.pixel_format); + #if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2 && msaa) { + glRenderbufferStorageMultisample(GL_RENDERBUFFER, img->cmn.sample_count, gl_depth_format, img->cmn.width, img->cmn.height); + } + else + #endif + { + glRenderbufferStorage(GL_RENDERBUFFER, gl_depth_format, img->cmn.width, img->cmn.height); + } + } + else { + /* regular color texture */ + img->gl.target = _sg_gl_texture_target(img->cmn.type); + const GLenum gl_internal_format = _sg_gl_teximage_internal_format(img->cmn.pixel_format); + + /* if this is a MSAA render target, need to create a separate render buffer */ + #if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2 && img->cmn.render_target && msaa) { + glGenRenderbuffers(1, &img->gl.msaa_render_buffer); + glBindRenderbuffer(GL_RENDERBUFFER, img->gl.msaa_render_buffer); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, img->cmn.sample_count, gl_internal_format, img->cmn.width, img->cmn.height); + } + #endif + + if (img->gl.ext_textures) { + /* inject externally GL textures */ + for (int slot = 0; slot < img->cmn.num_slots; slot++) { + SOKOL_ASSERT(desc->gl_textures[slot]); + img->gl.tex[slot] = desc->gl_textures[slot]; + } + if (desc->gl_texture_target) { + img->gl.target = (GLenum)desc->gl_texture_target; + } + } + else { + /* create our own GL texture(s) */ + const GLenum gl_format = _sg_gl_teximage_format(img->cmn.pixel_format); + const bool is_compressed = _sg_is_compressed_pixel_format(img->cmn.pixel_format); + for (int slot = 0; slot < img->cmn.num_slots; slot++) { + glGenTextures(1, &img->gl.tex[slot]); + SOKOL_ASSERT(img->gl.tex[slot]); + _sg_gl_cache_store_texture_binding(0); + _sg_gl_cache_bind_texture(0, img->gl.target, img->gl.tex[slot]); + GLenum gl_min_filter = _sg_gl_filter(img->cmn.min_filter); + GLenum gl_mag_filter = _sg_gl_filter(img->cmn.mag_filter); + glTexParameteri(img->gl.target, GL_TEXTURE_MIN_FILTER, (GLint)gl_min_filter); + glTexParameteri(img->gl.target, GL_TEXTURE_MAG_FILTER, (GLint)gl_mag_filter); + if (_sg.gl.ext_anisotropic && (img->cmn.max_anisotropy > 1)) { + GLint max_aniso = (GLint) img->cmn.max_anisotropy; + if (max_aniso > _sg.gl.max_anisotropy) { + max_aniso = _sg.gl.max_anisotropy; + } + glTexParameteri(img->gl.target, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_aniso); + } + if (img->cmn.type == SG_IMAGETYPE_CUBE) { + glTexParameteri(img->gl.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(img->gl.target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + else { + glTexParameteri(img->gl.target, GL_TEXTURE_WRAP_S, (GLint)_sg_gl_wrap(img->cmn.wrap_u)); + glTexParameteri(img->gl.target, GL_TEXTURE_WRAP_T, (GLint)_sg_gl_wrap(img->cmn.wrap_v)); + #if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2 && (img->cmn.type == SG_IMAGETYPE_3D)) { + glTexParameteri(img->gl.target, GL_TEXTURE_WRAP_R, (GLint)_sg_gl_wrap(img->cmn.wrap_w)); + } + #endif + #if defined(SOKOL_GLCORE33) + float border[4]; + switch (img->cmn.border_color) { + case SG_BORDERCOLOR_TRANSPARENT_BLACK: + border[0] = 0.0f; border[1] = 0.0f; border[2] = 0.0f; border[3] = 0.0f; + break; + case SG_BORDERCOLOR_OPAQUE_WHITE: + border[0] = 1.0f; border[1] = 1.0f; border[2] = 1.0f; border[3] = 1.0f; + break; + default: + border[0] = 0.0f; border[1] = 0.0f; border[2] = 0.0f; border[3] = 1.0f; + break; + } + glTexParameterfv(img->gl.target, GL_TEXTURE_BORDER_COLOR, border); + #endif + } + #if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + /* GL spec has strange defaults for mipmap min/max lod: -1000 to +1000 */ + const float min_lod = _sg_clamp(desc->min_lod, 0.0f, 1000.0f); + const float max_lod = _sg_clamp(desc->max_lod, 0.0f, 1000.0f); + glTexParameterf(img->gl.target, GL_TEXTURE_MIN_LOD, min_lod); + glTexParameterf(img->gl.target, GL_TEXTURE_MAX_LOD, max_lod); + } + #endif + const int num_faces = img->cmn.type == SG_IMAGETYPE_CUBE ? 6 : 1; + int data_index = 0; + for (int face_index = 0; face_index < num_faces; face_index++) { + for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++, data_index++) { + GLenum gl_img_target = img->gl.target; + if (SG_IMAGETYPE_CUBE == img->cmn.type) { + gl_img_target = _sg_gl_cubeface_target(face_index); + } + const GLvoid* data_ptr = desc->data.subimage[face_index][mip_index].ptr; + const GLsizei data_size = (GLsizei) desc->data.subimage[face_index][mip_index].size; + int mip_width = img->cmn.width >> mip_index; + if (mip_width == 0) { + mip_width = 1; + } + int mip_height = img->cmn.height >> mip_index; + if (mip_height == 0) { + mip_height = 1; + } + if ((SG_IMAGETYPE_2D == img->cmn.type) || (SG_IMAGETYPE_CUBE == img->cmn.type)) { + if (is_compressed) { + glCompressedTexImage2D(gl_img_target, mip_index, gl_internal_format, + mip_width, mip_height, 0, data_size, data_ptr); + } + else { + const GLenum gl_type = _sg_gl_teximage_type(img->cmn.pixel_format); + glTexImage2D(gl_img_target, mip_index, (GLint)gl_internal_format, + mip_width, mip_height, 0, gl_format, gl_type, data_ptr); + } + } + #if !defined(SOKOL_GLES2) + else if (!_sg.gl.gles2 && ((SG_IMAGETYPE_3D == img->cmn.type) || (SG_IMAGETYPE_ARRAY == img->cmn.type))) { + int mip_depth = img->cmn.num_slices; + if (SG_IMAGETYPE_3D == img->cmn.type) { + mip_depth >>= mip_index; + } + if (mip_depth == 0) { + mip_depth = 1; + } + if (is_compressed) { + glCompressedTexImage3D(gl_img_target, mip_index, gl_internal_format, + mip_width, mip_height, mip_depth, 0, data_size, data_ptr); + } + else { + const GLenum gl_type = _sg_gl_teximage_type(img->cmn.pixel_format); + glTexImage3D(gl_img_target, mip_index, (GLint)gl_internal_format, + mip_width, mip_height, mip_depth, 0, gl_format, gl_type, data_ptr); + } + } + #endif + } + } + _sg_gl_cache_restore_texture_binding(0); + } + } + } + _SG_GL_CHECK_ERROR(); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_gl_destroy_image(_sg_image_t* img) { + SOKOL_ASSERT(img); + _SG_GL_CHECK_ERROR(); + for (int slot = 0; slot < img->cmn.num_slots; slot++) { + if (img->gl.tex[slot]) { + _sg_gl_cache_invalidate_texture(img->gl.tex[slot]); + if (!img->gl.ext_textures) { + glDeleteTextures(1, &img->gl.tex[slot]); + } + } + } + if (img->gl.depth_render_buffer) { + glDeleteRenderbuffers(1, &img->gl.depth_render_buffer); + } + if (img->gl.msaa_render_buffer) { + glDeleteRenderbuffers(1, &img->gl.msaa_render_buffer); + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE GLuint _sg_gl_compile_shader(sg_shader_stage stage, const char* src) { + SOKOL_ASSERT(src); + _SG_GL_CHECK_ERROR(); + GLuint gl_shd = glCreateShader(_sg_gl_shader_stage(stage)); + glShaderSource(gl_shd, 1, &src, 0); + glCompileShader(gl_shd); + GLint compile_status = 0; + glGetShaderiv(gl_shd, GL_COMPILE_STATUS, &compile_status); + if (!compile_status) { + /* compilation failed, log error and delete shader */ + GLint log_len = 0; + glGetShaderiv(gl_shd, GL_INFO_LOG_LENGTH, &log_len); + if (log_len > 0) { + GLchar* log_buf = (GLchar*) SOKOL_MALLOC((size_t)log_len); + glGetShaderInfoLog(gl_shd, log_len, &log_len, log_buf); + SOKOL_LOG(log_buf); + SOKOL_FREE(log_buf); + } + glDeleteShader(gl_shd); + gl_shd = 0; + } + _SG_GL_CHECK_ERROR(); + return gl_shd; +} + +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { + SOKOL_ASSERT(shd && desc); + SOKOL_ASSERT(!shd->gl.prog); + _SG_GL_CHECK_ERROR(); + + _sg_shader_common_init(&shd->cmn, desc); + + /* copy vertex attribute names over, these are required for GLES2, and optional for GLES3 and GL3.x */ + for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { + _sg_strcpy(&shd->gl.attrs[i].name, desc->attrs[i].name); + } + + GLuint gl_vs = _sg_gl_compile_shader(SG_SHADERSTAGE_VS, desc->vs.source); + GLuint gl_fs = _sg_gl_compile_shader(SG_SHADERSTAGE_FS, desc->fs.source); + if (!(gl_vs && gl_fs)) { + return SG_RESOURCESTATE_FAILED; + } + GLuint gl_prog = glCreateProgram(); + glAttachShader(gl_prog, gl_vs); + glAttachShader(gl_prog, gl_fs); + glLinkProgram(gl_prog); + glDeleteShader(gl_vs); + glDeleteShader(gl_fs); + _SG_GL_CHECK_ERROR(); + + GLint link_status; + glGetProgramiv(gl_prog, GL_LINK_STATUS, &link_status); + if (!link_status) { + GLint log_len = 0; + glGetProgramiv(gl_prog, GL_INFO_LOG_LENGTH, &log_len); + if (log_len > 0) { + GLchar* log_buf = (GLchar*) SOKOL_MALLOC((size_t)log_len); + glGetProgramInfoLog(gl_prog, log_len, &log_len, log_buf); + SOKOL_LOG(log_buf); + SOKOL_FREE(log_buf); + } + glDeleteProgram(gl_prog); + return SG_RESOURCESTATE_FAILED; + } + shd->gl.prog = gl_prog; + + /* resolve uniforms */ + _SG_GL_CHECK_ERROR(); + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS)? &desc->vs : &desc->fs; + _sg_gl_shader_stage_t* gl_stage = &shd->gl.stage[stage_index]; + for (int ub_index = 0; ub_index < shd->cmn.stage[stage_index].num_uniform_blocks; ub_index++) { + const sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; + SOKOL_ASSERT(ub_desc->size > 0); + _sg_gl_uniform_block_t* ub = &gl_stage->uniform_blocks[ub_index]; + SOKOL_ASSERT(ub->num_uniforms == 0); + int cur_uniform_offset = 0; + for (int u_index = 0; u_index < SG_MAX_UB_MEMBERS; u_index++) { + const sg_shader_uniform_desc* u_desc = &ub_desc->uniforms[u_index]; + if (u_desc->type == SG_UNIFORMTYPE_INVALID) { + break; + } + _sg_gl_uniform_t* u = &ub->uniforms[u_index]; + u->type = u_desc->type; + u->count = (uint8_t) u_desc->array_count; + u->offset = (uint16_t) cur_uniform_offset; + cur_uniform_offset += _sg_uniform_size(u->type, u->count); + if (u_desc->name) { + u->gl_loc = glGetUniformLocation(gl_prog, u_desc->name); + } + else { + u->gl_loc = u_index; + } + ub->num_uniforms++; + } + SOKOL_ASSERT(ub_desc->size == (size_t)cur_uniform_offset); + } + } + + /* resolve image locations */ + _SG_GL_CHECK_ERROR(); + GLuint cur_prog = 0; + glGetIntegerv(GL_CURRENT_PROGRAM, (GLint*)&cur_prog); + glUseProgram(gl_prog); + int gl_tex_slot = 0; + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS)? &desc->vs : &desc->fs; + _sg_gl_shader_stage_t* gl_stage = &shd->gl.stage[stage_index]; + for (int img_index = 0; img_index < shd->cmn.stage[stage_index].num_images; img_index++) { + const sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; + SOKOL_ASSERT(img_desc->image_type != _SG_IMAGETYPE_DEFAULT); + _sg_gl_shader_image_t* gl_img = &gl_stage->images[img_index]; + GLint gl_loc = img_index; + if (img_desc->name) { + gl_loc = glGetUniformLocation(gl_prog, img_desc->name); + } + if (gl_loc != -1) { + gl_img->gl_tex_slot = gl_tex_slot++; + glUniform1i(gl_loc, gl_img->gl_tex_slot); + } + else { + gl_img->gl_tex_slot = -1; + } + } + } + /* it's legal to call glUseProgram with 0 */ + glUseProgram(cur_prog); + _SG_GL_CHECK_ERROR(); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_gl_destroy_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd); + _SG_GL_CHECK_ERROR(); + if (shd->gl.prog) { + _sg_gl_cache_invalidate_program(shd->gl.prog); + glDeleteProgram(shd->gl.prog); + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(pip && shd && desc); + SOKOL_ASSERT(!pip->shader && pip->cmn.shader_id.id == SG_INVALID_ID); + SOKOL_ASSERT(desc->shader.id == shd->slot.id); + SOKOL_ASSERT(shd->gl.prog); + pip->shader = shd; + _sg_pipeline_common_init(&pip->cmn, desc); + pip->gl.primitive_type = desc->primitive_type; + pip->gl.depth = desc->depth; + pip->gl.stencil = desc->stencil; + // FIXME: blend color and write mask per draw-buffer-attachment (requires GL4) + pip->gl.blend = desc->colors[0].blend; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + pip->gl.color_write_mask[i] = desc->colors[i].write_mask; + } + pip->gl.cull_mode = desc->cull_mode; + pip->gl.face_winding = desc->face_winding; + pip->gl.sample_count = desc->sample_count; + pip->gl.alpha_to_coverage_enabled = desc->alpha_to_coverage_enabled; + + /* resolve vertex attributes */ + for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + pip->gl.attrs[attr_index].vb_index = -1; + } + for (int attr_index = 0; attr_index < _sg.limits.max_vertex_attrs; attr_index++) { + const sg_vertex_attr_desc* a_desc = &desc->layout.attrs[attr_index]; + if (a_desc->format == SG_VERTEXFORMAT_INVALID) { + break; + } + SOKOL_ASSERT(a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS); + const sg_buffer_layout_desc* l_desc = &desc->layout.buffers[a_desc->buffer_index]; + const sg_vertex_step step_func = l_desc->step_func; + const int step_rate = l_desc->step_rate; + GLint attr_loc = attr_index; + if (!_sg_strempty(&shd->gl.attrs[attr_index].name)) { + attr_loc = glGetAttribLocation(pip->shader->gl.prog, _sg_strptr(&shd->gl.attrs[attr_index].name)); + } + SOKOL_ASSERT(attr_loc < (GLint)_sg.limits.max_vertex_attrs); + if (attr_loc != -1) { + _sg_gl_attr_t* gl_attr = &pip->gl.attrs[attr_loc]; + SOKOL_ASSERT(gl_attr->vb_index == -1); + gl_attr->vb_index = (int8_t) a_desc->buffer_index; + if (step_func == SG_VERTEXSTEP_PER_VERTEX) { + gl_attr->divisor = 0; + } + else { + gl_attr->divisor = (int8_t) step_rate; + } + SOKOL_ASSERT(l_desc->stride > 0); + gl_attr->stride = (uint8_t) l_desc->stride; + gl_attr->offset = a_desc->offset; + gl_attr->size = (uint8_t) _sg_gl_vertexformat_size(a_desc->format); + gl_attr->type = _sg_gl_vertexformat_type(a_desc->format); + gl_attr->normalized = _sg_gl_vertexformat_normalized(a_desc->format); + pip->cmn.vertex_layout_valid[a_desc->buffer_index] = true; + } + else { + SOKOL_LOG("Vertex attribute not found in shader: "); + SOKOL_LOG(_sg_strptr(&shd->gl.attrs[attr_index].name)); + } + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_gl_destroy_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + _SOKOL_UNUSED(pip); + /* empty */ +} + +/* + _sg_create_pass + + att_imgs must point to a _sg_image* att_imgs[SG_MAX_COLOR_ATTACHMENTS+1] array, + first entries are the color attachment images (or nullptr), last entry + is the depth-stencil image (or nullptr). +*/ +_SOKOL_PRIVATE sg_resource_state _sg_gl_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { + SOKOL_ASSERT(pass && att_images && desc); + SOKOL_ASSERT(att_images && att_images[0]); + _SG_GL_CHECK_ERROR(); + + _sg_pass_common_init(&pass->cmn, desc); + + /* copy image pointers */ + const sg_pass_attachment_desc* att_desc; + for (int i = 0; i < pass->cmn.num_color_atts; i++) { + att_desc = &desc->color_attachments[i]; + SOKOL_ASSERT(att_desc->image.id != SG_INVALID_ID); + SOKOL_ASSERT(0 == pass->gl.color_atts[i].image); + SOKOL_ASSERT(att_images[i] && (att_images[i]->slot.id == att_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(att_images[i]->cmn.pixel_format)); + pass->gl.color_atts[i].image = att_images[i]; + } + SOKOL_ASSERT(0 == pass->gl.ds_att.image); + att_desc = &desc->depth_stencil_attachment; + if (att_desc->image.id != SG_INVALID_ID) { + const int ds_img_index = SG_MAX_COLOR_ATTACHMENTS; + SOKOL_ASSERT(att_images[ds_img_index] && (att_images[ds_img_index]->slot.id == att_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(att_images[ds_img_index]->cmn.pixel_format)); + pass->gl.ds_att.image = att_images[ds_img_index]; + } + + /* store current framebuffer binding (restored at end of function) */ + GLuint gl_orig_fb; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&gl_orig_fb); + + /* create a framebuffer object */ + glGenFramebuffers(1, &pass->gl.fb); + glBindFramebuffer(GL_FRAMEBUFFER, pass->gl.fb); + + /* attach msaa render buffer or textures */ + const bool is_msaa = (0 != att_images[0]->gl.msaa_render_buffer); + if (is_msaa) { + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + const _sg_image_t* att_img = pass->gl.color_atts[i].image; + if (att_img) { + const GLuint gl_render_buffer = att_img->gl.msaa_render_buffer; + SOKOL_ASSERT(gl_render_buffer); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, (GLenum)(GL_COLOR_ATTACHMENT0+i), GL_RENDERBUFFER, gl_render_buffer); + } + } + } + else { + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + const _sg_image_t* att_img = pass->gl.color_atts[i].image; + const int mip_level = pass->cmn.color_atts[i].mip_level; + const int slice = pass->cmn.color_atts[i].slice; + if (att_img) { + const GLuint gl_tex = att_img->gl.tex[0]; + SOKOL_ASSERT(gl_tex); + const GLenum gl_att = (GLenum)(GL_COLOR_ATTACHMENT0 + i); + switch (att_img->cmn.type) { + case SG_IMAGETYPE_2D: + glFramebufferTexture2D(GL_FRAMEBUFFER, gl_att, GL_TEXTURE_2D, gl_tex, mip_level); + break; + case SG_IMAGETYPE_CUBE: + glFramebufferTexture2D(GL_FRAMEBUFFER, gl_att, _sg_gl_cubeface_target(slice), gl_tex, mip_level); + break; + default: + /* 3D- or array-texture */ + #if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + glFramebufferTextureLayer(GL_FRAMEBUFFER, gl_att, gl_tex, mip_level, slice); + } + #endif + break; + } + } + } + } + + /* attach depth-stencil buffer to framebuffer */ + if (pass->gl.ds_att.image) { + const GLuint gl_render_buffer = pass->gl.ds_att.image->gl.depth_render_buffer; + SOKOL_ASSERT(gl_render_buffer); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, gl_render_buffer); + if (_sg_is_depth_stencil_format(pass->gl.ds_att.image->cmn.pixel_format)) { + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, gl_render_buffer); + } + } + + /* check if framebuffer is complete */ + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + SOKOL_LOG("Framebuffer completeness check failed!\n"); + return SG_RESOURCESTATE_FAILED; + } + + /* setup color attachments for the framebuffer */ + #if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + GLenum att[SG_MAX_COLOR_ATTACHMENTS] = { + GL_COLOR_ATTACHMENT0, + GL_COLOR_ATTACHMENT1, + GL_COLOR_ATTACHMENT2, + GL_COLOR_ATTACHMENT3 + }; + glDrawBuffers(pass->cmn.num_color_atts, att); + } + #endif + + /* create MSAA resolve framebuffers if necessary */ + if (is_msaa) { + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + _sg_gl_attachment_t* gl_att = &pass->gl.color_atts[i]; + _sg_pass_attachment_t* cmn_att = &pass->cmn.color_atts[i]; + if (gl_att->image) { + SOKOL_ASSERT(0 == gl_att->gl_msaa_resolve_buffer); + glGenFramebuffers(1, &gl_att->gl_msaa_resolve_buffer); + glBindFramebuffer(GL_FRAMEBUFFER, gl_att->gl_msaa_resolve_buffer); + const GLuint gl_tex = gl_att->image->gl.tex[0]; + SOKOL_ASSERT(gl_tex); + switch (gl_att->image->cmn.type) { + case SG_IMAGETYPE_2D: + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, gl_tex, cmn_att->mip_level); + break; + case SG_IMAGETYPE_CUBE: + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + _sg_gl_cubeface_target(cmn_att->slice), gl_tex, cmn_att->mip_level); + break; + default: + #if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, gl_tex, cmn_att->mip_level, cmn_att->slice); + } + #endif + break; + } + /* check if framebuffer is complete */ + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + SOKOL_LOG("Framebuffer completeness check failed (msaa resolve buffer)!\n"); + return SG_RESOURCESTATE_FAILED; + } + /* setup color attachments for the framebuffer */ + #if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2) { + const GLenum gl_draw_bufs = GL_COLOR_ATTACHMENT0; + glDrawBuffers(1, &gl_draw_bufs); + } + #endif + } + } + } + + /* restore original framebuffer binding */ + glBindFramebuffer(GL_FRAMEBUFFER, gl_orig_fb); + _SG_GL_CHECK_ERROR(); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_gl_destroy_pass(_sg_pass_t* pass) { + SOKOL_ASSERT(pass); + _SG_GL_CHECK_ERROR(); + if (0 != pass->gl.fb) { + glDeleteFramebuffers(1, &pass->gl.fb); + } + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + if (pass->gl.color_atts[i].gl_msaa_resolve_buffer) { + glDeleteFramebuffers(1, &pass->gl.color_atts[i].gl_msaa_resolve_buffer); + } + } + if (pass->gl.ds_att.gl_msaa_resolve_buffer) { + glDeleteFramebuffers(1, &pass->gl.ds_att.gl_msaa_resolve_buffer); + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE _sg_image_t* _sg_gl_pass_color_image(const _sg_pass_t* pass, int index) { + SOKOL_ASSERT(pass && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + /* NOTE: may return null */ + return pass->gl.color_atts[index].image; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_gl_pass_ds_image(const _sg_pass_t* pass) { + /* NOTE: may return null */ + SOKOL_ASSERT(pass); + return pass->gl.ds_att.image; +} + +_SOKOL_PRIVATE void _sg_gl_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { + /* FIXME: what if a texture used as render target is still bound, should we + unbind all currently bound textures in begin pass? */ + SOKOL_ASSERT(action); + SOKOL_ASSERT(!_sg.gl.in_pass); + _SG_GL_CHECK_ERROR(); + _sg.gl.in_pass = true; + _sg.gl.cur_pass = pass; /* can be 0 */ + if (pass) { + _sg.gl.cur_pass_id.id = pass->slot.id; + } + else { + _sg.gl.cur_pass_id.id = SG_INVALID_ID; + } + _sg.gl.cur_pass_width = w; + _sg.gl.cur_pass_height = h; + + /* number of color attachments */ + const int num_color_atts = pass ? pass->cmn.num_color_atts : 1; + + /* bind the render pass framebuffer */ + if (pass) { + /* offscreen pass */ + SOKOL_ASSERT(pass->gl.fb); + glBindFramebuffer(GL_FRAMEBUFFER, pass->gl.fb); + } + else { + /* default pass */ + SOKOL_ASSERT(_sg.gl.cur_context); + glBindFramebuffer(GL_FRAMEBUFFER, _sg.gl.cur_context->default_framebuffer); + } + glViewport(0, 0, w, h); + glScissor(0, 0, w, h); + + /* clear color and depth-stencil attachments if needed */ + bool clear_color = false; + for (int i = 0; i < num_color_atts; i++) { + if (SG_ACTION_CLEAR == action->colors[i].action) { + clear_color = true; + break; + } + } + const bool clear_depth = (action->depth.action == SG_ACTION_CLEAR); + const bool clear_stencil = (action->stencil.action == SG_ACTION_CLEAR); + + bool need_pip_cache_flush = false; + if (clear_color) { + bool need_color_mask_flush = false; + // NOTE: not a bug to iterate over all possible color attachments + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + if (SG_COLORMASK_RGBA != _sg.gl.cache.color_write_mask[i]) { + need_pip_cache_flush = true; + need_color_mask_flush = true; + _sg.gl.cache.color_write_mask[i] = SG_COLORMASK_RGBA; + } + } + if (need_color_mask_flush) { + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + } + } + if (clear_depth) { + if (!_sg.gl.cache.depth.write_enabled) { + need_pip_cache_flush = true; + _sg.gl.cache.depth.write_enabled = true; + glDepthMask(GL_TRUE); + } + if (_sg.gl.cache.depth.compare != SG_COMPAREFUNC_ALWAYS) { + need_pip_cache_flush = true; + _sg.gl.cache.depth.compare = SG_COMPAREFUNC_ALWAYS; + glDepthFunc(GL_ALWAYS); + } + } + if (clear_stencil) { + if (_sg.gl.cache.stencil.write_mask != 0xFF) { + need_pip_cache_flush = true; + _sg.gl.cache.stencil.write_mask = 0xFF; + glStencilMask(0xFF); + } + } + if (need_pip_cache_flush) { + /* we messed with the state cache directly, need to clear cached + pipeline to force re-evaluation in next sg_apply_pipeline() */ + _sg.gl.cache.cur_pipeline = 0; + _sg.gl.cache.cur_pipeline_id.id = SG_INVALID_ID; + } + bool use_mrt_clear = (0 != pass); + #if defined(SOKOL_GLES2) + use_mrt_clear = false; + #else + if (_sg.gl.gles2) { + use_mrt_clear = false; + } + #endif + if (!use_mrt_clear) { + GLbitfield clear_mask = 0; + if (clear_color) { + clear_mask |= GL_COLOR_BUFFER_BIT; + const sg_color c = action->colors[0].value; + glClearColor(c.r, c.g, c.b, c.a); + } + if (clear_depth) { + clear_mask |= GL_DEPTH_BUFFER_BIT; + #ifdef SOKOL_GLCORE33 + glClearDepth(action->depth.value); + #else + glClearDepthf(action->depth.value); + #endif + } + if (clear_stencil) { + clear_mask |= GL_STENCIL_BUFFER_BIT; + glClearStencil(action->stencil.value); + } + if (0 != clear_mask) { + glClear(clear_mask); + } + } + #if !defined SOKOL_GLES2 + else { + SOKOL_ASSERT(pass); + for (int i = 0; i < num_color_atts; i++) { + if (action->colors[i].action == SG_ACTION_CLEAR) { + glClearBufferfv(GL_COLOR, i, &action->colors[i].value.r); + } + } + if (pass->gl.ds_att.image) { + if (clear_depth && clear_stencil) { + glClearBufferfi(GL_DEPTH_STENCIL, 0, action->depth.value, action->stencil.value); + } + else if (clear_depth) { + glClearBufferfv(GL_DEPTH, 0, &action->depth.value); + } + else if (clear_stencil) { + GLint val = (GLint) action->stencil.value; + glClearBufferiv(GL_STENCIL, 0, &val); + } + } + } + #endif + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE void _sg_gl_end_pass(void) { + SOKOL_ASSERT(_sg.gl.in_pass); + _SG_GL_CHECK_ERROR(); + + /* if this was an offscreen pass, and MSAA rendering was used, need + to resolve into the pass images */ + #if !defined(SOKOL_GLES2) + if (!_sg.gl.gles2 && _sg.gl.cur_pass) { + /* check if the pass object is still valid */ + const _sg_pass_t* pass = _sg.gl.cur_pass; + SOKOL_ASSERT(pass->slot.id == _sg.gl.cur_pass_id.id); + bool is_msaa = (0 != _sg.gl.cur_pass->gl.color_atts[0].gl_msaa_resolve_buffer); + if (is_msaa) { + SOKOL_ASSERT(pass->gl.fb); + glBindFramebuffer(GL_READ_FRAMEBUFFER, pass->gl.fb); + SOKOL_ASSERT(pass->gl.color_atts[0].image); + const int w = pass->gl.color_atts[0].image->cmn.width; + const int h = pass->gl.color_atts[0].image->cmn.height; + for (int att_index = 0; att_index < SG_MAX_COLOR_ATTACHMENTS; att_index++) { + const _sg_gl_attachment_t* gl_att = &pass->gl.color_atts[att_index]; + if (gl_att->image) { + SOKOL_ASSERT(gl_att->gl_msaa_resolve_buffer); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gl_att->gl_msaa_resolve_buffer); + glReadBuffer((GLenum)(GL_COLOR_ATTACHMENT0 + att_index)); + glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST); + } + else { + break; + } + } + } + } + #endif + _sg.gl.cur_pass = 0; + _sg.gl.cur_pass_id.id = SG_INVALID_ID; + _sg.gl.cur_pass_width = 0; + _sg.gl.cur_pass_height = 0; + + SOKOL_ASSERT(_sg.gl.cur_context); + glBindFramebuffer(GL_FRAMEBUFFER, _sg.gl.cur_context->default_framebuffer); + _sg.gl.in_pass = false; + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE void _sg_gl_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_sg.gl.in_pass); + y = origin_top_left ? (_sg.gl.cur_pass_height - (y+h)) : y; + glViewport(x, y, w, h); +} + +_SOKOL_PRIVATE void _sg_gl_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_sg.gl.in_pass); + y = origin_top_left ? (_sg.gl.cur_pass_height - (y+h)) : y; + glScissor(x, y, w, h); +} + +_SOKOL_PRIVATE void _sg_gl_apply_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + SOKOL_ASSERT(pip->shader && (pip->cmn.shader_id.id == pip->shader->slot.id)); + _SG_GL_CHECK_ERROR(); + if ((_sg.gl.cache.cur_pipeline != pip) || (_sg.gl.cache.cur_pipeline_id.id != pip->slot.id)) { + _sg.gl.cache.cur_pipeline = pip; + _sg.gl.cache.cur_pipeline_id.id = pip->slot.id; + _sg.gl.cache.cur_primitive_type = _sg_gl_primitive_type(pip->gl.primitive_type); + _sg.gl.cache.cur_index_type = _sg_gl_index_type(pip->cmn.index_type); + + /* update depth state */ + { + const sg_depth_state* state_ds = &pip->gl.depth; + sg_depth_state* cache_ds = &_sg.gl.cache.depth; + if (state_ds->compare != cache_ds->compare) { + cache_ds->compare = state_ds->compare; + glDepthFunc(_sg_gl_compare_func(state_ds->compare)); + } + if (state_ds->write_enabled != cache_ds->write_enabled) { + cache_ds->write_enabled = state_ds->write_enabled; + glDepthMask(state_ds->write_enabled); + } + if (!_sg_fequal(state_ds->bias, cache_ds->bias, 0.000001f) || + !_sg_fequal(state_ds->bias_slope_scale, cache_ds->bias_slope_scale, 0.000001f)) + { + /* according to ANGLE's D3D11 backend: + D3D11 SlopeScaledDepthBias ==> GL polygonOffsetFactor + D3D11 DepthBias ==> GL polygonOffsetUnits + DepthBiasClamp has no meaning on GL + */ + cache_ds->bias = state_ds->bias; + cache_ds->bias_slope_scale = state_ds->bias_slope_scale; + glPolygonOffset(state_ds->bias_slope_scale, state_ds->bias); + bool po_enabled = true; + if (_sg_fequal(state_ds->bias, 0.0f, 0.000001f) && + _sg_fequal(state_ds->bias_slope_scale, 0.0f, 0.000001f)) + { + po_enabled = false; + } + if (po_enabled != _sg.gl.cache.polygon_offset_enabled) { + _sg.gl.cache.polygon_offset_enabled = po_enabled; + if (po_enabled) { + glEnable(GL_POLYGON_OFFSET_FILL); + } + else { + glDisable(GL_POLYGON_OFFSET_FILL); + } + } + } + } + + /* update stencil state */ + { + const sg_stencil_state* state_ss = &pip->gl.stencil; + sg_stencil_state* cache_ss = &_sg.gl.cache.stencil; + if (state_ss->enabled != cache_ss->enabled) { + cache_ss->enabled = state_ss->enabled; + if (state_ss->enabled) { + glEnable(GL_STENCIL_TEST); + } + else { + glDisable(GL_STENCIL_TEST); + } + } + if (state_ss->write_mask != cache_ss->write_mask) { + cache_ss->write_mask = state_ss->write_mask; + glStencilMask(state_ss->write_mask); + } + for (int i = 0; i < 2; i++) { + const sg_stencil_face_state* state_sfs = (i==0)? &state_ss->front : &state_ss->back; + sg_stencil_face_state* cache_sfs = (i==0)? &cache_ss->front : &cache_ss->back; + GLenum gl_face = (i==0)? GL_FRONT : GL_BACK; + if ((state_sfs->compare != cache_sfs->compare) || + (state_ss->read_mask != cache_ss->read_mask) || + (state_ss->ref != cache_ss->ref)) + { + cache_sfs->compare = state_sfs->compare; + glStencilFuncSeparate(gl_face, + _sg_gl_compare_func(state_sfs->compare), + state_ss->ref, + state_ss->read_mask); + } + if ((state_sfs->fail_op != cache_sfs->fail_op) || + (state_sfs->depth_fail_op != cache_sfs->depth_fail_op) || + (state_sfs->pass_op != cache_sfs->pass_op)) + { + cache_sfs->fail_op = state_sfs->fail_op; + cache_sfs->depth_fail_op = state_sfs->depth_fail_op; + cache_sfs->pass_op = state_sfs->pass_op; + glStencilOpSeparate(gl_face, + _sg_gl_stencil_op(state_sfs->fail_op), + _sg_gl_stencil_op(state_sfs->depth_fail_op), + _sg_gl_stencil_op(state_sfs->pass_op)); + } + } + cache_ss->read_mask = state_ss->read_mask; + cache_ss->ref = state_ss->ref; + } + + /* update blend state + FIXME: separate blend state per color attachment not support, needs GL4 + */ + { + const sg_blend_state* state_bs = &pip->gl.blend; + sg_blend_state* cache_bs = &_sg.gl.cache.blend; + if (state_bs->enabled != cache_bs->enabled) { + cache_bs->enabled = state_bs->enabled; + if (state_bs->enabled) { + glEnable(GL_BLEND); + } + else { + glDisable(GL_BLEND); + } + } + if ((state_bs->src_factor_rgb != cache_bs->src_factor_rgb) || + (state_bs->dst_factor_rgb != cache_bs->dst_factor_rgb) || + (state_bs->src_factor_alpha != cache_bs->src_factor_alpha) || + (state_bs->dst_factor_alpha != cache_bs->dst_factor_alpha)) + { + cache_bs->src_factor_rgb = state_bs->src_factor_rgb; + cache_bs->dst_factor_rgb = state_bs->dst_factor_rgb; + cache_bs->src_factor_alpha = state_bs->src_factor_alpha; + cache_bs->dst_factor_alpha = state_bs->dst_factor_alpha; + glBlendFuncSeparate(_sg_gl_blend_factor(state_bs->src_factor_rgb), + _sg_gl_blend_factor(state_bs->dst_factor_rgb), + _sg_gl_blend_factor(state_bs->src_factor_alpha), + _sg_gl_blend_factor(state_bs->dst_factor_alpha)); + } + if ((state_bs->op_rgb != cache_bs->op_rgb) || (state_bs->op_alpha != cache_bs->op_alpha)) { + cache_bs->op_rgb = state_bs->op_rgb; + cache_bs->op_alpha = state_bs->op_alpha; + glBlendEquationSeparate(_sg_gl_blend_op(state_bs->op_rgb), _sg_gl_blend_op(state_bs->op_alpha)); + } + } + + /* standalone state */ + for (GLuint i = 0; i < (GLuint)pip->cmn.color_attachment_count; i++) { + if (pip->gl.color_write_mask[i] != _sg.gl.cache.color_write_mask[i]) { + const sg_color_mask cm = pip->gl.color_write_mask[i]; + _sg.gl.cache.color_write_mask[i] = cm; + #ifdef SOKOL_GLCORE33 + glColorMaski(i, + (cm & SG_COLORMASK_R) != 0, + (cm & SG_COLORMASK_G) != 0, + (cm & SG_COLORMASK_B) != 0, + (cm & SG_COLORMASK_A) != 0); + #else + if (0 == i) { + glColorMask((cm & SG_COLORMASK_R) != 0, + (cm & SG_COLORMASK_G) != 0, + (cm & SG_COLORMASK_B) != 0, + (cm & SG_COLORMASK_A) != 0); + } + #endif + } + } + + if (!_sg_fequal(pip->cmn.blend_color.r, _sg.gl.cache.blend_color.r, 0.0001f) || + !_sg_fequal(pip->cmn.blend_color.g, _sg.gl.cache.blend_color.g, 0.0001f) || + !_sg_fequal(pip->cmn.blend_color.b, _sg.gl.cache.blend_color.b, 0.0001f) || + !_sg_fequal(pip->cmn.blend_color.a, _sg.gl.cache.blend_color.a, 0.0001f)) + { + sg_color c = pip->cmn.blend_color; + _sg.gl.cache.blend_color = c; + glBlendColor(c.r, c.g, c.b, c.a); + } + if (pip->gl.cull_mode != _sg.gl.cache.cull_mode) { + _sg.gl.cache.cull_mode = pip->gl.cull_mode; + if (SG_CULLMODE_NONE == pip->gl.cull_mode) { + glDisable(GL_CULL_FACE); + } + else { + glEnable(GL_CULL_FACE); + GLenum gl_mode = (SG_CULLMODE_FRONT == pip->gl.cull_mode) ? GL_FRONT : GL_BACK; + glCullFace(gl_mode); + } + } + if (pip->gl.face_winding != _sg.gl.cache.face_winding) { + _sg.gl.cache.face_winding = pip->gl.face_winding; + GLenum gl_winding = (SG_FACEWINDING_CW == pip->gl.face_winding) ? GL_CW : GL_CCW; + glFrontFace(gl_winding); + } + if (pip->gl.alpha_to_coverage_enabled != _sg.gl.cache.alpha_to_coverage_enabled) { + _sg.gl.cache.alpha_to_coverage_enabled = pip->gl.alpha_to_coverage_enabled; + if (pip->gl.alpha_to_coverage_enabled) { + glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE); + } + else { + glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE); + } + } + #ifdef SOKOL_GLCORE33 + if (pip->gl.sample_count != _sg.gl.cache.sample_count) { + _sg.gl.cache.sample_count = pip->gl.sample_count; + if (pip->gl.sample_count > 1) { + glEnable(GL_MULTISAMPLE); + } + else { + glDisable(GL_MULTISAMPLE); + } + } + #endif + + /* bind shader program */ + if (pip->shader->gl.prog != _sg.gl.cache.prog) { + _sg.gl.cache.prog = pip->shader->gl.prog; + glUseProgram(pip->shader->gl.prog); + } + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE void _sg_gl_apply_bindings( + _sg_pipeline_t* pip, + _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, + _sg_buffer_t* ib, int ib_offset, + _sg_image_t** vs_imgs, int num_vs_imgs, + _sg_image_t** fs_imgs, int num_fs_imgs) +{ + SOKOL_ASSERT(pip); + _SOKOL_UNUSED(num_fs_imgs); + _SOKOL_UNUSED(num_vs_imgs); + _SOKOL_UNUSED(num_vbs); + _SG_GL_CHECK_ERROR(); + + /* bind textures */ + _SG_GL_CHECK_ERROR(); + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[stage_index]; + const _sg_gl_shader_stage_t* gl_stage = &pip->shader->gl.stage[stage_index]; + _sg_image_t** imgs = (stage_index == SG_SHADERSTAGE_VS)? vs_imgs : fs_imgs; + SOKOL_ASSERT(((stage_index == SG_SHADERSTAGE_VS)? num_vs_imgs : num_fs_imgs) == stage->num_images); + for (int img_index = 0; img_index < stage->num_images; img_index++) { + const _sg_gl_shader_image_t* gl_shd_img = &gl_stage->images[img_index]; + if (gl_shd_img->gl_tex_slot != -1) { + _sg_image_t* img = imgs[img_index]; + const GLuint gl_tex = img->gl.tex[img->cmn.active_slot]; + SOKOL_ASSERT(img && img->gl.target); + SOKOL_ASSERT((gl_shd_img->gl_tex_slot != -1) && gl_tex); + _sg_gl_cache_bind_texture(gl_shd_img->gl_tex_slot, img->gl.target, gl_tex); + } + } + } + _SG_GL_CHECK_ERROR(); + + /* index buffer (can be 0) */ + const GLuint gl_ib = ib ? ib->gl.buf[ib->cmn.active_slot] : 0; + _sg_gl_cache_bind_buffer(GL_ELEMENT_ARRAY_BUFFER, gl_ib); + _sg.gl.cache.cur_ib_offset = ib_offset; + + /* vertex attributes */ + for (GLuint attr_index = 0; attr_index < (GLuint)_sg.limits.max_vertex_attrs; attr_index++) { + _sg_gl_attr_t* attr = &pip->gl.attrs[attr_index]; + _sg_gl_cache_attr_t* cache_attr = &_sg.gl.cache.attrs[attr_index]; + bool cache_attr_dirty = false; + int vb_offset = 0; + GLuint gl_vb = 0; + if (attr->vb_index >= 0) { + /* attribute is enabled */ + SOKOL_ASSERT(attr->vb_index < num_vbs); + _sg_buffer_t* vb = vbs[attr->vb_index]; + SOKOL_ASSERT(vb); + gl_vb = vb->gl.buf[vb->cmn.active_slot]; + vb_offset = vb_offsets[attr->vb_index] + attr->offset; + if ((gl_vb != cache_attr->gl_vbuf) || + (attr->size != cache_attr->gl_attr.size) || + (attr->type != cache_attr->gl_attr.type) || + (attr->normalized != cache_attr->gl_attr.normalized) || + (attr->stride != cache_attr->gl_attr.stride) || + (vb_offset != cache_attr->gl_attr.offset) || + (cache_attr->gl_attr.divisor != attr->divisor)) + { + _sg_gl_cache_bind_buffer(GL_ARRAY_BUFFER, gl_vb); + glVertexAttribPointer(attr_index, attr->size, attr->type, + attr->normalized, attr->stride, + (const GLvoid*)(GLintptr)vb_offset); + #if defined(_SOKOL_GL_INSTANCING_ENABLED) + if (_sg.features.instancing) { + glVertexAttribDivisor(attr_index, (GLuint)attr->divisor); + } + #endif + cache_attr_dirty = true; + } + if (cache_attr->gl_attr.vb_index == -1) { + glEnableVertexAttribArray(attr_index); + cache_attr_dirty = true; + } + } + else { + /* attribute is disabled */ + if (cache_attr->gl_attr.vb_index != -1) { + glDisableVertexAttribArray(attr_index); + cache_attr_dirty = true; + } + } + if (cache_attr_dirty) { + cache_attr->gl_attr = *attr; + cache_attr->gl_attr.offset = vb_offset; + cache_attr->gl_vbuf = gl_vb; + } + } + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE void _sg_gl_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { + SOKOL_ASSERT(_sg.gl.cache.cur_pipeline); + SOKOL_ASSERT(_sg.gl.cache.cur_pipeline->slot.id == _sg.gl.cache.cur_pipeline_id.id); + SOKOL_ASSERT(_sg.gl.cache.cur_pipeline->shader->slot.id == _sg.gl.cache.cur_pipeline->cmn.shader_id.id); + SOKOL_ASSERT(_sg.gl.cache.cur_pipeline->shader->cmn.stage[stage_index].num_uniform_blocks > ub_index); + SOKOL_ASSERT(_sg.gl.cache.cur_pipeline->shader->cmn.stage[stage_index].uniform_blocks[ub_index].size == data->size); + const _sg_gl_shader_stage_t* gl_stage = &_sg.gl.cache.cur_pipeline->shader->gl.stage[stage_index]; + const _sg_gl_uniform_block_t* gl_ub = &gl_stage->uniform_blocks[ub_index]; + for (int u_index = 0; u_index < gl_ub->num_uniforms; u_index++) { + const _sg_gl_uniform_t* u = &gl_ub->uniforms[u_index]; + SOKOL_ASSERT(u->type != SG_UNIFORMTYPE_INVALID); + if (u->gl_loc == -1) { + continue; + } + GLfloat* ptr = (GLfloat*) (((uint8_t*)data->ptr) + u->offset); + switch (u->type) { + case SG_UNIFORMTYPE_INVALID: + break; + case SG_UNIFORMTYPE_FLOAT: + glUniform1fv(u->gl_loc, u->count, ptr); + break; + case SG_UNIFORMTYPE_FLOAT2: + glUniform2fv(u->gl_loc, u->count, ptr); + break; + case SG_UNIFORMTYPE_FLOAT3: + glUniform3fv(u->gl_loc, u->count, ptr); + break; + case SG_UNIFORMTYPE_FLOAT4: + glUniform4fv(u->gl_loc, u->count, ptr); + break; + case SG_UNIFORMTYPE_MAT4: + glUniformMatrix4fv(u->gl_loc, u->count, GL_FALSE, ptr); + break; + default: + SOKOL_UNREACHABLE; + break; + } + } +} + +_SOKOL_PRIVATE void _sg_gl_draw(int base_element, int num_elements, int num_instances) { + const GLenum i_type = _sg.gl.cache.cur_index_type; + const GLenum p_type = _sg.gl.cache.cur_primitive_type; + if (0 != i_type) { + /* indexed rendering */ + const int i_size = (i_type == GL_UNSIGNED_SHORT) ? 2 : 4; + const int ib_offset = _sg.gl.cache.cur_ib_offset; + const GLvoid* indices = (const GLvoid*)(GLintptr)(base_element*i_size+ib_offset); + if (num_instances == 1) { + glDrawElements(p_type, num_elements, i_type, indices); + } + else { + if (_sg.features.instancing) { + glDrawElementsInstanced(p_type, num_elements, i_type, indices, num_instances); + } + } + } + else { + /* non-indexed rendering */ + if (num_instances == 1) { + glDrawArrays(p_type, base_element, num_elements); + } + else { + if (_sg.features.instancing) { + glDrawArraysInstanced(p_type, base_element, num_elements, num_instances); + } + } + } +} + +_SOKOL_PRIVATE void _sg_gl_commit(void) { + SOKOL_ASSERT(!_sg.gl.in_pass); + /* "soft" clear bindings (only those that are actually bound) */ + _sg_gl_cache_clear_buffer_bindings(false); + _sg_gl_cache_clear_texture_bindings(false); +} + +_SOKOL_PRIVATE void _sg_gl_update_buffer(_sg_buffer_t* buf, const sg_range* data) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + /* only one update per buffer per frame allowed */ + if (++buf->cmn.active_slot >= buf->cmn.num_slots) { + buf->cmn.active_slot = 0; + } + GLenum gl_tgt = _sg_gl_buffer_target(buf->cmn.type); + SOKOL_ASSERT(buf->cmn.active_slot < SG_NUM_INFLIGHT_FRAMES); + GLuint gl_buf = buf->gl.buf[buf->cmn.active_slot]; + SOKOL_ASSERT(gl_buf); + _SG_GL_CHECK_ERROR(); + _sg_gl_cache_store_buffer_binding(gl_tgt); + _sg_gl_cache_bind_buffer(gl_tgt, gl_buf); + glBufferSubData(gl_tgt, 0, (GLsizeiptr)data->size, data->ptr); + _sg_gl_cache_restore_buffer_binding(gl_tgt); + _SG_GL_CHECK_ERROR(); +} + +_SOKOL_PRIVATE int _sg_gl_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + if (new_frame) { + if (++buf->cmn.active_slot >= buf->cmn.num_slots) { + buf->cmn.active_slot = 0; + } + } + GLenum gl_tgt = _sg_gl_buffer_target(buf->cmn.type); + SOKOL_ASSERT(buf->cmn.active_slot < SG_NUM_INFLIGHT_FRAMES); + GLuint gl_buf = buf->gl.buf[buf->cmn.active_slot]; + SOKOL_ASSERT(gl_buf); + _SG_GL_CHECK_ERROR(); + _sg_gl_cache_store_buffer_binding(gl_tgt); + _sg_gl_cache_bind_buffer(gl_tgt, gl_buf); + glBufferSubData(gl_tgt, buf->cmn.append_pos, (GLsizeiptr)data->size, data->ptr); + _sg_gl_cache_restore_buffer_binding(gl_tgt); + _SG_GL_CHECK_ERROR(); + /* NOTE: this is a requirement from WebGPU, but we want identical behaviour across all backend */ + return _sg_roundup((int)data->size, 4); +} + +_SOKOL_PRIVATE void _sg_gl_update_image(_sg_image_t* img, const sg_image_data* data) { + SOKOL_ASSERT(img && data); + /* only one update per image per frame allowed */ + if (++img->cmn.active_slot >= img->cmn.num_slots) { + img->cmn.active_slot = 0; + } + SOKOL_ASSERT(img->cmn.active_slot < SG_NUM_INFLIGHT_FRAMES); + SOKOL_ASSERT(0 != img->gl.tex[img->cmn.active_slot]); + _sg_gl_cache_store_texture_binding(0); + _sg_gl_cache_bind_texture(0, img->gl.target, img->gl.tex[img->cmn.active_slot]); + const GLenum gl_img_format = _sg_gl_teximage_format(img->cmn.pixel_format); + const GLenum gl_img_type = _sg_gl_teximage_type(img->cmn.pixel_format); + const int num_faces = img->cmn.type == SG_IMAGETYPE_CUBE ? 6 : 1; + const int num_mips = img->cmn.num_mipmaps; + for (int face_index = 0; face_index < num_faces; face_index++) { + for (int mip_index = 0; mip_index < num_mips; mip_index++) { + GLenum gl_img_target = img->gl.target; + if (SG_IMAGETYPE_CUBE == img->cmn.type) { + gl_img_target = _sg_gl_cubeface_target(face_index); + } + const GLvoid* data_ptr = data->subimage[face_index][mip_index].ptr; + int mip_width = img->cmn.width >> mip_index; + if (mip_width == 0) { + mip_width = 1; + } + int mip_height = img->cmn.height >> mip_index; + if (mip_height == 0) { + mip_height = 1; + } + if ((SG_IMAGETYPE_2D == img->cmn.type) || (SG_IMAGETYPE_CUBE == img->cmn.type)) { + glTexSubImage2D(gl_img_target, mip_index, + 0, 0, + mip_width, mip_height, + gl_img_format, gl_img_type, + data_ptr); + } + #if !defined(SOKOL_GLES2) + else if (!_sg.gl.gles2 && ((SG_IMAGETYPE_3D == img->cmn.type) || (SG_IMAGETYPE_ARRAY == img->cmn.type))) { + int mip_depth = img->cmn.num_slices >> mip_index; + if (mip_depth == 0) { + mip_depth = 1; + } + glTexSubImage3D(gl_img_target, mip_index, + 0, 0, 0, + mip_width, mip_height, mip_depth, + gl_img_format, gl_img_type, + data_ptr); + + } + #endif + } + } + _sg_gl_cache_restore_texture_binding(0); +} + +/*== D3D11 BACKEND IMPLEMENTATION ============================================*/ +#elif defined(SOKOL_D3D11) + +#if defined(__cplusplus) +#define _sg_d3d11_AddRef(self) (self)->AddRef() +#else +#define _sg_d3d11_AddRef(self) (self)->lpVtbl->AddRef(self) +#endif + +#if defined(__cplusplus) +#define _sg_d3d11_Release(self) (self)->Release() +#else +#define _sg_d3d11_Release(self) (self)->lpVtbl->Release(self) +#endif + +/*-- D3D11 C/C++ wrappers ----------------------------------------------------*/ +static inline HRESULT _sg_d3d11_CheckFormatSupport(ID3D11Device* self, DXGI_FORMAT Format, UINT* pFormatSupport) { + #if defined(__cplusplus) + return self->CheckFormatSupport(Format, pFormatSupport); + #else + return self->lpVtbl->CheckFormatSupport(self, Format, pFormatSupport); + #endif +} + +static inline void _sg_d3d11_OMSetRenderTargets(ID3D11DeviceContext* self, UINT NumViews, ID3D11RenderTargetView* const* ppRenderTargetViews, ID3D11DepthStencilView *pDepthStencilView) { + #if defined(__cplusplus) + self->OMSetRenderTargets(NumViews, ppRenderTargetViews, pDepthStencilView); + #else + self->lpVtbl->OMSetRenderTargets(self, NumViews, ppRenderTargetViews, pDepthStencilView); + #endif +} + +static inline void _sg_d3d11_RSSetState(ID3D11DeviceContext* self, ID3D11RasterizerState* pRasterizerState) { + #if defined(__cplusplus) + self->RSSetState(pRasterizerState); + #else + self->lpVtbl->RSSetState(self, pRasterizerState); + #endif +} + +static inline void _sg_d3d11_OMSetDepthStencilState(ID3D11DeviceContext* self, ID3D11DepthStencilState* pDepthStencilState, UINT StencilRef) { + #if defined(__cplusplus) + self->OMSetDepthStencilState(pDepthStencilState, StencilRef); + #else + self->lpVtbl->OMSetDepthStencilState(self, pDepthStencilState, StencilRef); + #endif +} + +static inline void _sg_d3d11_OMSetBlendState(ID3D11DeviceContext* self, ID3D11BlendState* pBlendState, const FLOAT BlendFactor[4], UINT SampleMask) { + #if defined(__cplusplus) + self->OMSetBlendState(pBlendState, BlendFactor, SampleMask); + #else + self->lpVtbl->OMSetBlendState(self, pBlendState, BlendFactor, SampleMask); + #endif +} + +static inline void _sg_d3d11_IASetVertexBuffers(ID3D11DeviceContext* self, UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppVertexBuffers, const UINT* pStrides, const UINT* pOffsets) { + #if defined(__cplusplus) + self->IASetVertexBuffers(StartSlot, NumBuffers, ppVertexBuffers, pStrides, pOffsets); + #else + self->lpVtbl->IASetVertexBuffers(self, StartSlot, NumBuffers, ppVertexBuffers, pStrides, pOffsets); + #endif +} + +static inline void _sg_d3d11_IASetIndexBuffer(ID3D11DeviceContext* self, ID3D11Buffer* pIndexBuffer, DXGI_FORMAT Format, UINT Offset) { + #if defined(__cplusplus) + self->IASetIndexBuffer(pIndexBuffer, Format, Offset); + #else + self->lpVtbl->IASetIndexBuffer(self, pIndexBuffer, Format, Offset); + #endif +} + +static inline void _sg_d3d11_IASetInputLayout(ID3D11DeviceContext* self, ID3D11InputLayout* pInputLayout) { + #if defined(__cplusplus) + self->IASetInputLayout(pInputLayout); + #else + self->lpVtbl->IASetInputLayout(self, pInputLayout); + #endif +} + +static inline void _sg_d3d11_VSSetShader(ID3D11DeviceContext* self, ID3D11VertexShader* pVertexShader, ID3D11ClassInstance* const* ppClassInstances, UINT NumClassInstances) { + #if defined(__cplusplus) + self->VSSetShader(pVertexShader, ppClassInstances, NumClassInstances); + #else + self->lpVtbl->VSSetShader(self, pVertexShader, ppClassInstances, NumClassInstances); + #endif +} + +static inline void _sg_d3d11_PSSetShader(ID3D11DeviceContext* self, ID3D11PixelShader* pPixelShader, ID3D11ClassInstance* const* ppClassInstances, UINT NumClassInstances) { + #if defined(__cplusplus) + self->PSSetShader(pPixelShader, ppClassInstances, NumClassInstances); + #else + self->lpVtbl->PSSetShader(self, pPixelShader, ppClassInstances, NumClassInstances); + #endif +} + +static inline void _sg_d3d11_VSSetConstantBuffers(ID3D11DeviceContext* self, UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers) { + #if defined(__cplusplus) + self->VSSetConstantBuffers(StartSlot, NumBuffers, ppConstantBuffers); + #else + self->lpVtbl->VSSetConstantBuffers(self, StartSlot, NumBuffers, ppConstantBuffers); + #endif +} + +static inline void _sg_d3d11_PSSetConstantBuffers(ID3D11DeviceContext* self, UINT StartSlot, UINT NumBuffers, ID3D11Buffer* const* ppConstantBuffers) { + #if defined(__cplusplus) + self->PSSetConstantBuffers(StartSlot, NumBuffers, ppConstantBuffers); + #else + self->lpVtbl->PSSetConstantBuffers(self, StartSlot, NumBuffers, ppConstantBuffers); + #endif +} + +static inline void _sg_d3d11_VSSetShaderResources(ID3D11DeviceContext* self, UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView* const* ppShaderResourceViews) { + #if defined(__cplusplus) + self->VSSetShaderResources(StartSlot, NumViews, ppShaderResourceViews); + #else + self->lpVtbl->VSSetShaderResources(self, StartSlot, NumViews, ppShaderResourceViews); + #endif +} + +static inline void _sg_d3d11_PSSetShaderResources(ID3D11DeviceContext* self, UINT StartSlot, UINT NumViews, ID3D11ShaderResourceView* const* ppShaderResourceViews) { + #if defined(__cplusplus) + self->PSSetShaderResources(StartSlot, NumViews, ppShaderResourceViews); + #else + self->lpVtbl->PSSetShaderResources(self, StartSlot, NumViews, ppShaderResourceViews); + #endif +} + +static inline void _sg_d3d11_VSSetSamplers(ID3D11DeviceContext* self, UINT StartSlot, UINT NumSamplers, ID3D11SamplerState* const* ppSamplers) { + #if defined(__cplusplus) + self->VSSetSamplers(StartSlot, NumSamplers, ppSamplers); + #else + self->lpVtbl->VSSetSamplers(self, StartSlot, NumSamplers, ppSamplers); + #endif +} + +static inline void _sg_d3d11_PSSetSamplers(ID3D11DeviceContext* self, UINT StartSlot, UINT NumSamplers, ID3D11SamplerState* const* ppSamplers) { + #if defined(__cplusplus) + self->PSSetSamplers(StartSlot, NumSamplers, ppSamplers); + #else + self->lpVtbl->PSSetSamplers(self, StartSlot, NumSamplers, ppSamplers); + #endif +} + +static inline HRESULT _sg_d3d11_CreateBuffer(ID3D11Device* self, const D3D11_BUFFER_DESC* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Buffer** ppBuffer) { + #if defined(__cplusplus) + return self->CreateBuffer(pDesc, pInitialData, ppBuffer); + #else + return self->lpVtbl->CreateBuffer(self, pDesc, pInitialData, ppBuffer); + #endif +} + +static inline HRESULT _sg_d3d11_CreateTexture2D(ID3D11Device* self, const D3D11_TEXTURE2D_DESC* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Texture2D** ppTexture2D) { + #if defined(__cplusplus) + return self->CreateTexture2D(pDesc, pInitialData, ppTexture2D); + #else + return self->lpVtbl->CreateTexture2D(self, pDesc, pInitialData, ppTexture2D); + #endif +} + +static inline HRESULT _sg_d3d11_CreateShaderResourceView(ID3D11Device* self, ID3D11Resource* pResource, const D3D11_SHADER_RESOURCE_VIEW_DESC* pDesc, ID3D11ShaderResourceView** ppSRView) { + #if defined(__cplusplus) + return self->CreateShaderResourceView(pResource, pDesc, ppSRView); + #else + return self->lpVtbl->CreateShaderResourceView(self, pResource, pDesc, ppSRView); + #endif +} + +static inline void _sg_d3d11_GetResource(ID3D11View* self, ID3D11Resource** ppResource) { + #if defined(__cplusplus) + self->GetResource(ppResource); + #else + self->lpVtbl->GetResource(self, ppResource); + #endif +} + +static inline HRESULT _sg_d3d11_CreateTexture3D(ID3D11Device* self, const D3D11_TEXTURE3D_DESC* pDesc, const D3D11_SUBRESOURCE_DATA* pInitialData, ID3D11Texture3D** ppTexture3D) { + #if defined(__cplusplus) + return self->CreateTexture3D(pDesc, pInitialData, ppTexture3D); + #else + return self->lpVtbl->CreateTexture3D(self, pDesc, pInitialData, ppTexture3D); + #endif +} + +static inline HRESULT _sg_d3d11_CreateSamplerState(ID3D11Device* self, const D3D11_SAMPLER_DESC* pSamplerDesc, ID3D11SamplerState** ppSamplerState) { + #if defined(__cplusplus) + return self->CreateSamplerState(pSamplerDesc, ppSamplerState); + #else + return self->lpVtbl->CreateSamplerState(self, pSamplerDesc, ppSamplerState); + #endif +} + +static inline LPVOID _sg_d3d11_GetBufferPointer(ID3D10Blob* self) { + #if defined(__cplusplus) + return self->GetBufferPointer(); + #else + return self->lpVtbl->GetBufferPointer(self); + #endif +} + +static inline SIZE_T _sg_d3d11_GetBufferSize(ID3D10Blob* self) { + #if defined(__cplusplus) + return self->GetBufferSize(); + #else + return self->lpVtbl->GetBufferSize(self); + #endif +} + +static inline HRESULT _sg_d3d11_CreateVertexShader(ID3D11Device* self, const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D11ClassLinkage* pClassLinkage, ID3D11VertexShader** ppVertexShader) { + #if defined(__cplusplus) + return self->CreateVertexShader(pShaderBytecode, BytecodeLength, pClassLinkage, ppVertexShader); + #else + return self->lpVtbl->CreateVertexShader(self, pShaderBytecode, BytecodeLength, pClassLinkage, ppVertexShader); + #endif +} + +static inline HRESULT _sg_d3d11_CreatePixelShader(ID3D11Device* self, const void* pShaderBytecode, SIZE_T BytecodeLength, ID3D11ClassLinkage* pClassLinkage, ID3D11PixelShader** ppPixelShader) { + #if defined(__cplusplus) + return self->CreatePixelShader(pShaderBytecode, BytecodeLength, pClassLinkage, ppPixelShader); + #else + return self->lpVtbl->CreatePixelShader(self, pShaderBytecode, BytecodeLength, pClassLinkage, ppPixelShader); + #endif +} + +static inline HRESULT _sg_d3d11_CreateInputLayout(ID3D11Device* self, const D3D11_INPUT_ELEMENT_DESC* pInputElementDescs, UINT NumElements, const void* pShaderBytecodeWithInputSignature, SIZE_T BytecodeLength, ID3D11InputLayout **ppInputLayout) { + #if defined(__cplusplus) + return self->CreateInputLayout(pInputElementDescs, NumElements, pShaderBytecodeWithInputSignature, BytecodeLength, ppInputLayout); + #else + return self->lpVtbl->CreateInputLayout(self, pInputElementDescs, NumElements, pShaderBytecodeWithInputSignature, BytecodeLength, ppInputLayout); + #endif +} + +static inline HRESULT _sg_d3d11_CreateRasterizerState(ID3D11Device* self, const D3D11_RASTERIZER_DESC* pRasterizerDesc, ID3D11RasterizerState** ppRasterizerState) { + #if defined(__cplusplus) + return self->CreateRasterizerState(pRasterizerDesc, ppRasterizerState); + #else + return self->lpVtbl->CreateRasterizerState(self, pRasterizerDesc, ppRasterizerState); + #endif +} + +static inline HRESULT _sg_d3d11_CreateDepthStencilState(ID3D11Device* self, const D3D11_DEPTH_STENCIL_DESC* pDepthStencilDesc, ID3D11DepthStencilState** ppDepthStencilState) { + #if defined(__cplusplus) + return self->CreateDepthStencilState(pDepthStencilDesc, ppDepthStencilState); + #else + return self->lpVtbl->CreateDepthStencilState(self, pDepthStencilDesc, ppDepthStencilState); + #endif +} + +static inline HRESULT _sg_d3d11_CreateBlendState(ID3D11Device* self, const D3D11_BLEND_DESC* pBlendStateDesc, ID3D11BlendState** ppBlendState) { + #if defined(__cplusplus) + return self->CreateBlendState(pBlendStateDesc, ppBlendState); + #else + return self->lpVtbl->CreateBlendState(self, pBlendStateDesc, ppBlendState); + #endif +} + +static inline HRESULT _sg_d3d11_CreateRenderTargetView(ID3D11Device* self, ID3D11Resource *pResource, const D3D11_RENDER_TARGET_VIEW_DESC* pDesc, ID3D11RenderTargetView** ppRTView) { + #if defined(__cplusplus) + return self->CreateRenderTargetView(pResource, pDesc, ppRTView); + #else + return self->lpVtbl->CreateRenderTargetView(self, pResource, pDesc, ppRTView); + #endif +} + +static inline HRESULT _sg_d3d11_CreateDepthStencilView(ID3D11Device* self, ID3D11Resource* pResource, const D3D11_DEPTH_STENCIL_VIEW_DESC* pDesc, ID3D11DepthStencilView** ppDepthStencilView) { + #if defined(__cplusplus) + return self->CreateDepthStencilView(pResource, pDesc, ppDepthStencilView); + #else + return self->lpVtbl->CreateDepthStencilView(self, pResource, pDesc, ppDepthStencilView); + #endif +} + +static inline void _sg_d3d11_RSSetViewports(ID3D11DeviceContext* self, UINT NumViewports, const D3D11_VIEWPORT* pViewports) { + #if defined(__cplusplus) + self->RSSetViewports(NumViewports, pViewports); + #else + self->lpVtbl->RSSetViewports(self, NumViewports, pViewports); + #endif +} + +static inline void _sg_d3d11_RSSetScissorRects(ID3D11DeviceContext* self, UINT NumRects, const D3D11_RECT* pRects) { + #if defined(__cplusplus) + self->RSSetScissorRects(NumRects, pRects); + #else + self->lpVtbl->RSSetScissorRects(self, NumRects, pRects); + #endif +} + +static inline void _sg_d3d11_ClearRenderTargetView(ID3D11DeviceContext* self, ID3D11RenderTargetView* pRenderTargetView, const FLOAT ColorRGBA[4]) { + #if defined(__cplusplus) + self->ClearRenderTargetView(pRenderTargetView, ColorRGBA); + #else + self->lpVtbl->ClearRenderTargetView(self, pRenderTargetView, ColorRGBA); + #endif +} + +static inline void _sg_d3d11_ClearDepthStencilView(ID3D11DeviceContext* self, ID3D11DepthStencilView* pDepthStencilView, UINT ClearFlags, FLOAT Depth, UINT8 Stencil) { + #if defined(__cplusplus) + self->ClearDepthStencilView(pDepthStencilView, ClearFlags, Depth, Stencil); + #else + self->lpVtbl->ClearDepthStencilView(self, pDepthStencilView, ClearFlags, Depth, Stencil); + #endif +} + +static inline void _sg_d3d11_ResolveSubresource(ID3D11DeviceContext* self, ID3D11Resource* pDstResource, UINT DstSubresource, ID3D11Resource* pSrcResource, UINT SrcSubresource, DXGI_FORMAT Format) { + #if defined(__cplusplus) + self->ResolveSubresource(pDstResource, DstSubresource, pSrcResource, SrcSubresource, Format); + #else + self->lpVtbl->ResolveSubresource(self, pDstResource, DstSubresource, pSrcResource, SrcSubresource, Format); + #endif +} + +static inline void _sg_d3d11_IASetPrimitiveTopology(ID3D11DeviceContext* self, D3D11_PRIMITIVE_TOPOLOGY Topology) { + #if defined(__cplusplus) + self->IASetPrimitiveTopology(Topology); + #else + self->lpVtbl->IASetPrimitiveTopology(self, Topology); + #endif +} + +static inline void _sg_d3d11_UpdateSubresource(ID3D11DeviceContext* self, ID3D11Resource* pDstResource, UINT DstSubresource, const D3D11_BOX* pDstBox, const void* pSrcData, UINT SrcRowPitch, UINT SrcDepthPitch) { + #if defined(__cplusplus) + self->UpdateSubresource(pDstResource, DstSubresource, pDstBox, pSrcData, SrcRowPitch, SrcDepthPitch); + #else + self->lpVtbl->UpdateSubresource(self, pDstResource, DstSubresource, pDstBox, pSrcData, SrcRowPitch, SrcDepthPitch); + #endif +} + +static inline void _sg_d3d11_DrawIndexed(ID3D11DeviceContext* self, UINT IndexCount, UINT StartIndexLocation, INT BaseVertexLocation) { + #if defined(__cplusplus) + self->DrawIndexed(IndexCount, StartIndexLocation, BaseVertexLocation); + #else + self->lpVtbl->DrawIndexed(self, IndexCount, StartIndexLocation, BaseVertexLocation); + #endif +} + +static inline void _sg_d3d11_DrawIndexedInstanced(ID3D11DeviceContext* self, UINT IndexCountPerInstance, UINT InstanceCount, UINT StartIndexLocation, INT BaseVertexLocation, UINT StartInstanceLocation) { + #if defined(__cplusplus) + self->DrawIndexedInstanced(IndexCountPerInstance, InstanceCount, StartIndexLocation, BaseVertexLocation, StartInstanceLocation); + #else + self->lpVtbl->DrawIndexedInstanced(self, IndexCountPerInstance, InstanceCount, StartIndexLocation, BaseVertexLocation, StartInstanceLocation); + #endif +} + +static inline void _sg_d3d11_Draw(ID3D11DeviceContext* self, UINT VertexCount, UINT StartVertexLocation) { + #if defined(__cplusplus) + self->Draw(VertexCount, StartVertexLocation); + #else + self->lpVtbl->Draw(self, VertexCount, StartVertexLocation); + #endif +} + +static inline void _sg_d3d11_DrawInstanced(ID3D11DeviceContext* self, UINT VertexCountPerInstance, UINT InstanceCount, UINT StartVertexLocation, UINT StartInstanceLocation) { + #if defined(__cplusplus) + self->DrawInstanced(VertexCountPerInstance, InstanceCount, StartVertexLocation, StartInstanceLocation); + #else + self->lpVtbl->DrawInstanced(self, VertexCountPerInstance, InstanceCount, StartVertexLocation, StartInstanceLocation); + #endif +} + +static inline HRESULT _sg_d3d11_Map(ID3D11DeviceContext* self, ID3D11Resource* pResource, UINT Subresource, D3D11_MAP MapType, UINT MapFlags, D3D11_MAPPED_SUBRESOURCE* pMappedResource) { + #if defined(__cplusplus) + return self->Map(pResource, Subresource, MapType, MapFlags, pMappedResource); + #else + return self->lpVtbl->Map(self, pResource, Subresource, MapType, MapFlags, pMappedResource); + #endif +} + +static inline void _sg_d3d11_Unmap(ID3D11DeviceContext* self, ID3D11Resource* pResource, UINT Subresource) { + #if defined(__cplusplus) + self->Unmap(pResource, Subresource); + #else + self->lpVtbl->Unmap(self, pResource, Subresource); + #endif +} + +/*-- enum translation functions ----------------------------------------------*/ +_SOKOL_PRIVATE D3D11_USAGE _sg_d3d11_usage(sg_usage usg) { + switch (usg) { + case SG_USAGE_IMMUTABLE: + return D3D11_USAGE_IMMUTABLE; + case SG_USAGE_DYNAMIC: + case SG_USAGE_STREAM: + return D3D11_USAGE_DYNAMIC; + default: + SOKOL_UNREACHABLE; + return (D3D11_USAGE) 0; + } +} + +_SOKOL_PRIVATE UINT _sg_d3d11_cpu_access_flags(sg_usage usg) { + switch (usg) { + case SG_USAGE_IMMUTABLE: + return 0; + case SG_USAGE_DYNAMIC: + case SG_USAGE_STREAM: + return D3D11_CPU_ACCESS_WRITE; + default: + SOKOL_UNREACHABLE; + return 0; + } +} + +_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_pixel_format(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_R8: return DXGI_FORMAT_R8_UNORM; + case SG_PIXELFORMAT_R8SN: return DXGI_FORMAT_R8_SNORM; + case SG_PIXELFORMAT_R8UI: return DXGI_FORMAT_R8_UINT; + case SG_PIXELFORMAT_R8SI: return DXGI_FORMAT_R8_SINT; + case SG_PIXELFORMAT_R16: return DXGI_FORMAT_R16_UNORM; + case SG_PIXELFORMAT_R16SN: return DXGI_FORMAT_R16_SNORM; + case SG_PIXELFORMAT_R16UI: return DXGI_FORMAT_R16_UINT; + case SG_PIXELFORMAT_R16SI: return DXGI_FORMAT_R16_SINT; + case SG_PIXELFORMAT_R16F: return DXGI_FORMAT_R16_FLOAT; + case SG_PIXELFORMAT_RG8: return DXGI_FORMAT_R8G8_UNORM; + case SG_PIXELFORMAT_RG8SN: return DXGI_FORMAT_R8G8_SNORM; + case SG_PIXELFORMAT_RG8UI: return DXGI_FORMAT_R8G8_UINT; + case SG_PIXELFORMAT_RG8SI: return DXGI_FORMAT_R8G8_SINT; + case SG_PIXELFORMAT_R32UI: return DXGI_FORMAT_R32_UINT; + case SG_PIXELFORMAT_R32SI: return DXGI_FORMAT_R32_SINT; + case SG_PIXELFORMAT_R32F: return DXGI_FORMAT_R32_FLOAT; + case SG_PIXELFORMAT_RG16: return DXGI_FORMAT_R16G16_UNORM; + case SG_PIXELFORMAT_RG16SN: return DXGI_FORMAT_R16G16_SNORM; + case SG_PIXELFORMAT_RG16UI: return DXGI_FORMAT_R16G16_UINT; + case SG_PIXELFORMAT_RG16SI: return DXGI_FORMAT_R16G16_SINT; + case SG_PIXELFORMAT_RG16F: return DXGI_FORMAT_R16G16_FLOAT; + case SG_PIXELFORMAT_RGBA8: return DXGI_FORMAT_R8G8B8A8_UNORM; + case SG_PIXELFORMAT_RGBA8SN: return DXGI_FORMAT_R8G8B8A8_SNORM; + case SG_PIXELFORMAT_RGBA8UI: return DXGI_FORMAT_R8G8B8A8_UINT; + case SG_PIXELFORMAT_RGBA8SI: return DXGI_FORMAT_R8G8B8A8_SINT; + case SG_PIXELFORMAT_BGRA8: return DXGI_FORMAT_B8G8R8A8_UNORM; + case SG_PIXELFORMAT_RGB10A2: return DXGI_FORMAT_R10G10B10A2_UNORM; + case SG_PIXELFORMAT_RG11B10F: return DXGI_FORMAT_R11G11B10_FLOAT; + case SG_PIXELFORMAT_RG32UI: return DXGI_FORMAT_R32G32_UINT; + case SG_PIXELFORMAT_RG32SI: return DXGI_FORMAT_R32G32_SINT; + case SG_PIXELFORMAT_RG32F: return DXGI_FORMAT_R32G32_FLOAT; + case SG_PIXELFORMAT_RGBA16: return DXGI_FORMAT_R16G16B16A16_UNORM; + case SG_PIXELFORMAT_RGBA16SN: return DXGI_FORMAT_R16G16B16A16_SNORM; + case SG_PIXELFORMAT_RGBA16UI: return DXGI_FORMAT_R16G16B16A16_UINT; + case SG_PIXELFORMAT_RGBA16SI: return DXGI_FORMAT_R16G16B16A16_SINT; + case SG_PIXELFORMAT_RGBA16F: return DXGI_FORMAT_R16G16B16A16_FLOAT; + case SG_PIXELFORMAT_RGBA32UI: return DXGI_FORMAT_R32G32B32A32_UINT; + case SG_PIXELFORMAT_RGBA32SI: return DXGI_FORMAT_R32G32B32A32_SINT; + case SG_PIXELFORMAT_RGBA32F: return DXGI_FORMAT_R32G32B32A32_FLOAT; + case SG_PIXELFORMAT_DEPTH: return DXGI_FORMAT_D32_FLOAT; + case SG_PIXELFORMAT_DEPTH_STENCIL: return DXGI_FORMAT_D24_UNORM_S8_UINT; + case SG_PIXELFORMAT_BC1_RGBA: return DXGI_FORMAT_BC1_UNORM; + case SG_PIXELFORMAT_BC2_RGBA: return DXGI_FORMAT_BC2_UNORM; + case SG_PIXELFORMAT_BC3_RGBA: return DXGI_FORMAT_BC3_UNORM; + case SG_PIXELFORMAT_BC4_R: return DXGI_FORMAT_BC4_UNORM; + case SG_PIXELFORMAT_BC4_RSN: return DXGI_FORMAT_BC4_SNORM; + case SG_PIXELFORMAT_BC5_RG: return DXGI_FORMAT_BC5_UNORM; + case SG_PIXELFORMAT_BC5_RGSN: return DXGI_FORMAT_BC5_SNORM; + case SG_PIXELFORMAT_BC6H_RGBF: return DXGI_FORMAT_BC6H_SF16; + case SG_PIXELFORMAT_BC6H_RGBUF: return DXGI_FORMAT_BC6H_UF16; + case SG_PIXELFORMAT_BC7_RGBA: return DXGI_FORMAT_BC7_UNORM; + default: return DXGI_FORMAT_UNKNOWN; + }; +} + +_SOKOL_PRIVATE D3D11_PRIMITIVE_TOPOLOGY _sg_d3d11_primitive_topology(sg_primitive_type prim_type) { + switch (prim_type) { + case SG_PRIMITIVETYPE_POINTS: return D3D11_PRIMITIVE_TOPOLOGY_POINTLIST; + case SG_PRIMITIVETYPE_LINES: return D3D11_PRIMITIVE_TOPOLOGY_LINELIST; + case SG_PRIMITIVETYPE_LINE_STRIP: return D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP; + case SG_PRIMITIVETYPE_TRIANGLES: return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST; + case SG_PRIMITIVETYPE_TRIANGLE_STRIP: return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP; + default: SOKOL_UNREACHABLE; return (D3D11_PRIMITIVE_TOPOLOGY) 0; + } +} + +_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_index_format(sg_index_type index_type) { + switch (index_type) { + case SG_INDEXTYPE_NONE: return DXGI_FORMAT_UNKNOWN; + case SG_INDEXTYPE_UINT16: return DXGI_FORMAT_R16_UINT; + case SG_INDEXTYPE_UINT32: return DXGI_FORMAT_R32_UINT; + default: SOKOL_UNREACHABLE; return (DXGI_FORMAT) 0; + } +} + +_SOKOL_PRIVATE D3D11_FILTER _sg_d3d11_filter(sg_filter min_f, sg_filter mag_f, uint32_t max_anisotropy) { + if (max_anisotropy > 1) { + return D3D11_FILTER_ANISOTROPIC; + } + else if (mag_f == SG_FILTER_NEAREST) { + switch (min_f) { + case SG_FILTER_NEAREST: + case SG_FILTER_NEAREST_MIPMAP_NEAREST: + return D3D11_FILTER_MIN_MAG_MIP_POINT; + case SG_FILTER_LINEAR: + case SG_FILTER_LINEAR_MIPMAP_NEAREST: + return D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT; + case SG_FILTER_NEAREST_MIPMAP_LINEAR: + return D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR; + case SG_FILTER_LINEAR_MIPMAP_LINEAR: + return D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR; + default: + SOKOL_UNREACHABLE; break; + } + } + else if (mag_f == SG_FILTER_LINEAR) { + switch (min_f) { + case SG_FILTER_NEAREST: + case SG_FILTER_NEAREST_MIPMAP_NEAREST: + return D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT; + case SG_FILTER_LINEAR: + case SG_FILTER_LINEAR_MIPMAP_NEAREST: + return D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT; + case SG_FILTER_NEAREST_MIPMAP_LINEAR: + return D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR; + case SG_FILTER_LINEAR_MIPMAP_LINEAR: + return D3D11_FILTER_MIN_MAG_MIP_LINEAR; + default: + SOKOL_UNREACHABLE; break; + } + } + /* invalid value for mag filter */ + SOKOL_UNREACHABLE; + return D3D11_FILTER_MIN_MAG_MIP_POINT; +} + +_SOKOL_PRIVATE D3D11_TEXTURE_ADDRESS_MODE _sg_d3d11_address_mode(sg_wrap m) { + switch (m) { + case SG_WRAP_REPEAT: return D3D11_TEXTURE_ADDRESS_WRAP; + case SG_WRAP_CLAMP_TO_EDGE: return D3D11_TEXTURE_ADDRESS_CLAMP; + case SG_WRAP_CLAMP_TO_BORDER: return D3D11_TEXTURE_ADDRESS_BORDER; + case SG_WRAP_MIRRORED_REPEAT: return D3D11_TEXTURE_ADDRESS_MIRROR; + default: SOKOL_UNREACHABLE; return (D3D11_TEXTURE_ADDRESS_MODE) 0; + } +} + +_SOKOL_PRIVATE DXGI_FORMAT _sg_d3d11_vertex_format(sg_vertex_format fmt) { + switch (fmt) { + case SG_VERTEXFORMAT_FLOAT: return DXGI_FORMAT_R32_FLOAT; + case SG_VERTEXFORMAT_FLOAT2: return DXGI_FORMAT_R32G32_FLOAT; + case SG_VERTEXFORMAT_FLOAT3: return DXGI_FORMAT_R32G32B32_FLOAT; + case SG_VERTEXFORMAT_FLOAT4: return DXGI_FORMAT_R32G32B32A32_FLOAT; + case SG_VERTEXFORMAT_BYTE4: return DXGI_FORMAT_R8G8B8A8_SINT; + case SG_VERTEXFORMAT_BYTE4N: return DXGI_FORMAT_R8G8B8A8_SNORM; + case SG_VERTEXFORMAT_UBYTE4: return DXGI_FORMAT_R8G8B8A8_UINT; + case SG_VERTEXFORMAT_UBYTE4N: return DXGI_FORMAT_R8G8B8A8_UNORM; + case SG_VERTEXFORMAT_SHORT2: return DXGI_FORMAT_R16G16_SINT; + case SG_VERTEXFORMAT_SHORT2N: return DXGI_FORMAT_R16G16_SNORM; + case SG_VERTEXFORMAT_USHORT2N: return DXGI_FORMAT_R16G16_UNORM; + case SG_VERTEXFORMAT_SHORT4: return DXGI_FORMAT_R16G16B16A16_SINT; + case SG_VERTEXFORMAT_SHORT4N: return DXGI_FORMAT_R16G16B16A16_SNORM; + case SG_VERTEXFORMAT_USHORT4N: return DXGI_FORMAT_R16G16B16A16_UNORM; + case SG_VERTEXFORMAT_UINT10_N2: return DXGI_FORMAT_R10G10B10A2_UNORM; + default: SOKOL_UNREACHABLE; return (DXGI_FORMAT) 0; + } +} + +_SOKOL_PRIVATE D3D11_INPUT_CLASSIFICATION _sg_d3d11_input_classification(sg_vertex_step step) { + switch (step) { + case SG_VERTEXSTEP_PER_VERTEX: return D3D11_INPUT_PER_VERTEX_DATA; + case SG_VERTEXSTEP_PER_INSTANCE: return D3D11_INPUT_PER_INSTANCE_DATA; + default: SOKOL_UNREACHABLE; return (D3D11_INPUT_CLASSIFICATION) 0; + } +} + +_SOKOL_PRIVATE D3D11_CULL_MODE _sg_d3d11_cull_mode(sg_cull_mode m) { + switch (m) { + case SG_CULLMODE_NONE: return D3D11_CULL_NONE; + case SG_CULLMODE_FRONT: return D3D11_CULL_FRONT; + case SG_CULLMODE_BACK: return D3D11_CULL_BACK; + default: SOKOL_UNREACHABLE; return (D3D11_CULL_MODE) 0; + } +} + +_SOKOL_PRIVATE D3D11_COMPARISON_FUNC _sg_d3d11_compare_func(sg_compare_func f) { + switch (f) { + case SG_COMPAREFUNC_NEVER: return D3D11_COMPARISON_NEVER; + case SG_COMPAREFUNC_LESS: return D3D11_COMPARISON_LESS; + case SG_COMPAREFUNC_EQUAL: return D3D11_COMPARISON_EQUAL; + case SG_COMPAREFUNC_LESS_EQUAL: return D3D11_COMPARISON_LESS_EQUAL; + case SG_COMPAREFUNC_GREATER: return D3D11_COMPARISON_GREATER; + case SG_COMPAREFUNC_NOT_EQUAL: return D3D11_COMPARISON_NOT_EQUAL; + case SG_COMPAREFUNC_GREATER_EQUAL: return D3D11_COMPARISON_GREATER_EQUAL; + case SG_COMPAREFUNC_ALWAYS: return D3D11_COMPARISON_ALWAYS; + default: SOKOL_UNREACHABLE; return (D3D11_COMPARISON_FUNC) 0; + } +} + +_SOKOL_PRIVATE D3D11_STENCIL_OP _sg_d3d11_stencil_op(sg_stencil_op op) { + switch (op) { + case SG_STENCILOP_KEEP: return D3D11_STENCIL_OP_KEEP; + case SG_STENCILOP_ZERO: return D3D11_STENCIL_OP_ZERO; + case SG_STENCILOP_REPLACE: return D3D11_STENCIL_OP_REPLACE; + case SG_STENCILOP_INCR_CLAMP: return D3D11_STENCIL_OP_INCR_SAT; + case SG_STENCILOP_DECR_CLAMP: return D3D11_STENCIL_OP_DECR_SAT; + case SG_STENCILOP_INVERT: return D3D11_STENCIL_OP_INVERT; + case SG_STENCILOP_INCR_WRAP: return D3D11_STENCIL_OP_INCR; + case SG_STENCILOP_DECR_WRAP: return D3D11_STENCIL_OP_DECR; + default: SOKOL_UNREACHABLE; return (D3D11_STENCIL_OP) 0; + } +} + +_SOKOL_PRIVATE D3D11_BLEND _sg_d3d11_blend_factor(sg_blend_factor f) { + switch (f) { + case SG_BLENDFACTOR_ZERO: return D3D11_BLEND_ZERO; + case SG_BLENDFACTOR_ONE: return D3D11_BLEND_ONE; + case SG_BLENDFACTOR_SRC_COLOR: return D3D11_BLEND_SRC_COLOR; + case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return D3D11_BLEND_INV_SRC_COLOR; + case SG_BLENDFACTOR_SRC_ALPHA: return D3D11_BLEND_SRC_ALPHA; + case SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return D3D11_BLEND_INV_SRC_ALPHA; + case SG_BLENDFACTOR_DST_COLOR: return D3D11_BLEND_DEST_COLOR; + case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return D3D11_BLEND_INV_DEST_COLOR; + case SG_BLENDFACTOR_DST_ALPHA: return D3D11_BLEND_DEST_ALPHA; + case SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return D3D11_BLEND_INV_DEST_ALPHA; + case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return D3D11_BLEND_SRC_ALPHA_SAT; + case SG_BLENDFACTOR_BLEND_COLOR: return D3D11_BLEND_BLEND_FACTOR; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return D3D11_BLEND_INV_BLEND_FACTOR; + case SG_BLENDFACTOR_BLEND_ALPHA: return D3D11_BLEND_BLEND_FACTOR; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return D3D11_BLEND_INV_BLEND_FACTOR; + default: SOKOL_UNREACHABLE; return (D3D11_BLEND) 0; + } +} + +_SOKOL_PRIVATE D3D11_BLEND_OP _sg_d3d11_blend_op(sg_blend_op op) { + switch (op) { + case SG_BLENDOP_ADD: return D3D11_BLEND_OP_ADD; + case SG_BLENDOP_SUBTRACT: return D3D11_BLEND_OP_SUBTRACT; + case SG_BLENDOP_REVERSE_SUBTRACT: return D3D11_BLEND_OP_REV_SUBTRACT; + default: SOKOL_UNREACHABLE; return (D3D11_BLEND_OP) 0; + } +} + +_SOKOL_PRIVATE UINT8 _sg_d3d11_color_write_mask(sg_color_mask m) { + UINT8 res = 0; + if (m & SG_COLORMASK_R) { + res |= D3D11_COLOR_WRITE_ENABLE_RED; + } + if (m & SG_COLORMASK_G) { + res |= D3D11_COLOR_WRITE_ENABLE_GREEN; + } + if (m & SG_COLORMASK_B) { + res |= D3D11_COLOR_WRITE_ENABLE_BLUE; + } + if (m & SG_COLORMASK_A) { + res |= D3D11_COLOR_WRITE_ENABLE_ALPHA; + } + return res; +} + +/* see: https://docs.microsoft.com/en-us/windows/win32/direct3d11/overviews-direct3d-11-resources-limits#resource-limits-for-feature-level-11-hardware */ +_SOKOL_PRIVATE void _sg_d3d11_init_caps(void) { + _sg.backend = SG_BACKEND_D3D11; + + _sg.features.instancing = true; + _sg.features.origin_top_left = true; + _sg.features.multiple_render_targets = true; + _sg.features.msaa_render_targets = true; + _sg.features.imagetype_3d = true; + _sg.features.imagetype_array = true; + _sg.features.image_clamp_to_border = true; + _sg.features.mrt_independent_blend_state = true; + _sg.features.mrt_independent_write_mask = true; + + _sg.limits.max_image_size_2d = 16 * 1024; + _sg.limits.max_image_size_cube = 16 * 1024; + _sg.limits.max_image_size_3d = 2 * 1024; + _sg.limits.max_image_size_array = 16 * 1024; + _sg.limits.max_image_array_layers = 2 * 1024; + _sg.limits.max_vertex_attrs = SG_MAX_VERTEX_ATTRIBUTES; + + /* see: https://docs.microsoft.com/en-us/windows/win32/api/d3d11/ne-d3d11-d3d11_format_support */ + for (int fmt = (SG_PIXELFORMAT_NONE+1); fmt < _SG_PIXELFORMAT_NUM; fmt++) { + UINT dxgi_fmt_caps = 0; + const DXGI_FORMAT dxgi_fmt = _sg_d3d11_pixel_format((sg_pixel_format)fmt); + if (dxgi_fmt != DXGI_FORMAT_UNKNOWN) { + HRESULT hr = _sg_d3d11_CheckFormatSupport(_sg.d3d11.dev, dxgi_fmt, &dxgi_fmt_caps); + SOKOL_ASSERT(SUCCEEDED(hr) || (E_FAIL == hr)); + if (!SUCCEEDED(hr)) { + dxgi_fmt_caps = 0; + } + } + sg_pixelformat_info* info = &_sg.formats[fmt]; + info->sample = 0 != (dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_TEXTURE2D); + info->filter = 0 != (dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_SHADER_SAMPLE); + info->render = 0 != (dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_RENDER_TARGET); + info->blend = 0 != (dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_BLENDABLE); + info->msaa = 0 != (dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_MULTISAMPLE_RENDERTARGET); + info->depth = 0 != (dxgi_fmt_caps & D3D11_FORMAT_SUPPORT_DEPTH_STENCIL); + if (info->depth) { + info->render = true; + } + } +} + +_SOKOL_PRIVATE void _sg_d3d11_setup_backend(const sg_desc* desc) { + /* assume _sg.d3d11 already is zero-initialized */ + SOKOL_ASSERT(desc); + SOKOL_ASSERT(desc->context.d3d11.device); + SOKOL_ASSERT(desc->context.d3d11.device_context); + SOKOL_ASSERT(desc->context.d3d11.render_target_view_cb || desc->context.d3d11.render_target_view_userdata_cb); + SOKOL_ASSERT(desc->context.d3d11.depth_stencil_view_cb || desc->context.d3d11.depth_stencil_view_userdata_cb); + _sg.d3d11.valid = true; + _sg.d3d11.dev = (ID3D11Device*) desc->context.d3d11.device; + _sg.d3d11.ctx = (ID3D11DeviceContext*) desc->context.d3d11.device_context; + _sg.d3d11.rtv_cb = desc->context.d3d11.render_target_view_cb; + _sg.d3d11.rtv_userdata_cb = desc->context.d3d11.render_target_view_userdata_cb; + _sg.d3d11.dsv_cb = desc->context.d3d11.depth_stencil_view_cb; + _sg.d3d11.dsv_userdata_cb = desc->context.d3d11.depth_stencil_view_userdata_cb; + _sg.d3d11.user_data = desc->context.d3d11.user_data; + _sg_d3d11_init_caps(); +} + +_SOKOL_PRIVATE void _sg_d3d11_discard_backend(void) { + SOKOL_ASSERT(_sg.d3d11.valid); + _sg.d3d11.valid = false; +} + +_SOKOL_PRIVATE void _sg_d3d11_clear_state(void) { + /* clear all the device context state, so that resource refs don't keep stuck in the d3d device context */ + _sg_d3d11_OMSetRenderTargets(_sg.d3d11.ctx, SG_MAX_COLOR_ATTACHMENTS, _sg.d3d11.zero_rtvs, NULL); + _sg_d3d11_RSSetState(_sg.d3d11.ctx, NULL); + _sg_d3d11_OMSetDepthStencilState(_sg.d3d11.ctx, NULL, 0); + _sg_d3d11_OMSetBlendState(_sg.d3d11.ctx, NULL, NULL, 0xFFFFFFFF); + _sg_d3d11_IASetVertexBuffers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_BUFFERS, _sg.d3d11.zero_vbs, _sg.d3d11.zero_vb_strides, _sg.d3d11.zero_vb_offsets); + _sg_d3d11_IASetIndexBuffer(_sg.d3d11.ctx, NULL, DXGI_FORMAT_UNKNOWN, 0); + _sg_d3d11_IASetInputLayout(_sg.d3d11.ctx, NULL); + _sg_d3d11_VSSetShader(_sg.d3d11.ctx, NULL, NULL, 0); + _sg_d3d11_PSSetShader(_sg.d3d11.ctx, NULL, NULL, 0); + _sg_d3d11_VSSetConstantBuffers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_UBS, _sg.d3d11.zero_cbs); + _sg_d3d11_PSSetConstantBuffers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_UBS, _sg.d3d11.zero_cbs); + _sg_d3d11_VSSetShaderResources(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, _sg.d3d11.zero_srvs); + _sg_d3d11_PSSetShaderResources(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, _sg.d3d11.zero_srvs); + _sg_d3d11_VSSetSamplers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, _sg.d3d11.zero_smps); + _sg_d3d11_PSSetSamplers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, _sg.d3d11.zero_smps); +} + +_SOKOL_PRIVATE void _sg_d3d11_reset_state_cache(void) { + /* just clear the d3d11 device context state */ + _sg_d3d11_clear_state(); +} + +_SOKOL_PRIVATE void _sg_d3d11_activate_context(_sg_context_t* ctx) { + _SOKOL_UNUSED(ctx); + _sg_d3d11_clear_state(); +} + +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_context(_sg_context_t* ctx) { + SOKOL_ASSERT(ctx); + _SOKOL_UNUSED(ctx); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_d3d11_destroy_context(_sg_context_t* ctx) { + SOKOL_ASSERT(ctx); + _SOKOL_UNUSED(ctx); + /* empty */ +} + +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { + SOKOL_ASSERT(buf && desc); + SOKOL_ASSERT(!buf->d3d11.buf); + _sg_buffer_common_init(&buf->cmn, desc); + const bool injected = (0 != desc->d3d11_buffer); + if (injected) { + buf->d3d11.buf = (ID3D11Buffer*) desc->d3d11_buffer; + _sg_d3d11_AddRef(buf->d3d11.buf); + } + else { + D3D11_BUFFER_DESC d3d11_desc; + memset(&d3d11_desc, 0, sizeof(d3d11_desc)); + d3d11_desc.ByteWidth = (UINT)buf->cmn.size; + d3d11_desc.Usage = _sg_d3d11_usage(buf->cmn.usage); + d3d11_desc.BindFlags = buf->cmn.type == SG_BUFFERTYPE_VERTEXBUFFER ? D3D11_BIND_VERTEX_BUFFER : D3D11_BIND_INDEX_BUFFER; + d3d11_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(buf->cmn.usage); + D3D11_SUBRESOURCE_DATA* init_data_ptr = 0; + D3D11_SUBRESOURCE_DATA init_data; + memset(&init_data, 0, sizeof(init_data)); + if (buf->cmn.usage == SG_USAGE_IMMUTABLE) { + SOKOL_ASSERT(desc->data.ptr); + init_data.pSysMem = desc->data.ptr; + init_data_ptr = &init_data; + } + HRESULT hr = _sg_d3d11_CreateBuffer(_sg.d3d11.dev, &d3d11_desc, init_data_ptr, &buf->d3d11.buf); + _SOKOL_UNUSED(hr); + SOKOL_ASSERT(SUCCEEDED(hr) && buf->d3d11.buf); + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_d3d11_destroy_buffer(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf); + if (buf->d3d11.buf) { + _sg_d3d11_Release(buf->d3d11.buf); + } +} + +_SOKOL_PRIVATE void _sg_d3d11_fill_subres_data(const _sg_image_t* img, const sg_image_data* data) { + const int num_faces = (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6:1; + const int num_slices = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? img->cmn.num_slices:1; + int subres_index = 0; + for (int face_index = 0; face_index < num_faces; face_index++) { + for (int slice_index = 0; slice_index < num_slices; slice_index++) { + for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++, subres_index++) { + SOKOL_ASSERT(subres_index < (SG_MAX_MIPMAPS * SG_MAX_TEXTUREARRAY_LAYERS)); + D3D11_SUBRESOURCE_DATA* subres_data = &_sg.d3d11.subres_data[subres_index]; + const int mip_width = ((img->cmn.width>>mip_index)>0) ? img->cmn.width>>mip_index : 1; + const int mip_height = ((img->cmn.height>>mip_index)>0) ? img->cmn.height>>mip_index : 1; + const sg_range* subimg_data = &(data->subimage[face_index][mip_index]); + const size_t slice_size = subimg_data->size / (size_t)num_slices; + const size_t slice_offset = slice_size * (size_t)slice_index; + const uint8_t* ptr = (const uint8_t*) subimg_data->ptr; + subres_data->pSysMem = ptr + slice_offset; + subres_data->SysMemPitch = (UINT)_sg_row_pitch(img->cmn.pixel_format, mip_width, 1); + if (img->cmn.type == SG_IMAGETYPE_3D) { + /* FIXME? const int mip_depth = ((img->depth>>mip_index)>0) ? img->depth>>mip_index : 1; */ + subres_data->SysMemSlicePitch = (UINT)_sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1); + } + else { + subres_data->SysMemSlicePitch = 0; + } + } + } + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_image(_sg_image_t* img, const sg_image_desc* desc) { + SOKOL_ASSERT(img && desc); + SOKOL_ASSERT(!img->d3d11.tex2d && !img->d3d11.tex3d && !img->d3d11.texds && !img->d3d11.texmsaa); + SOKOL_ASSERT(!img->d3d11.srv && !img->d3d11.smp); + HRESULT hr; + _SOKOL_UNUSED(hr); + + _sg_image_common_init(&img->cmn, desc); + const bool injected = (0 != desc->d3d11_texture) || (0 != desc->d3d11_shader_resource_view); + const bool msaa = (img->cmn.sample_count > 1); + img->d3d11.format = _sg_d3d11_pixel_format(img->cmn.pixel_format); + + /* special case depth-stencil buffer? */ + if (_sg_is_valid_rendertarget_depth_format(img->cmn.pixel_format)) { + /* create only a depth-texture */ + SOKOL_ASSERT(!injected); + if (img->d3d11.format == DXGI_FORMAT_UNKNOWN) { + SOKOL_LOG("trying to create a D3D11 depth-texture with unsupported pixel format\n"); + return SG_RESOURCESTATE_FAILED; + } + D3D11_TEXTURE2D_DESC d3d11_desc; + memset(&d3d11_desc, 0, sizeof(d3d11_desc)); + d3d11_desc.Width = (UINT)img->cmn.width; + d3d11_desc.Height = (UINT)img->cmn.height; + d3d11_desc.MipLevels = 1; + d3d11_desc.ArraySize = 1; + d3d11_desc.Format = img->d3d11.format; + d3d11_desc.Usage = D3D11_USAGE_DEFAULT; + d3d11_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL; + d3d11_desc.SampleDesc.Count = (UINT)img->cmn.sample_count; + d3d11_desc.SampleDesc.Quality = (UINT) (msaa ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0); + hr = _sg_d3d11_CreateTexture2D(_sg.d3d11.dev, &d3d11_desc, NULL, &img->d3d11.texds); + SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11.texds); + } + else { + /* create (or inject) color texture and shader-resource-view */ + + /* prepare initial content pointers */ + D3D11_SUBRESOURCE_DATA* init_data = 0; + if (!injected && (img->cmn.usage == SG_USAGE_IMMUTABLE) && !img->cmn.render_target) { + _sg_d3d11_fill_subres_data(img, &desc->data); + init_data = _sg.d3d11.subres_data; + } + if (img->cmn.type != SG_IMAGETYPE_3D) { + /* 2D-, cube- or array-texture */ + /* if this is an MSAA render target, the following texture will be the 'resolve-texture' */ + + /* first check for injected texture and/or resource view */ + if (injected) { + img->d3d11.tex2d = (ID3D11Texture2D*) desc->d3d11_texture; + img->d3d11.srv = (ID3D11ShaderResourceView*) desc->d3d11_shader_resource_view; + if (img->d3d11.tex2d) { + _sg_d3d11_AddRef(img->d3d11.tex2d); + } + else { + /* if only a shader-resource-view was provided, but no texture, lookup + the texture from the shader-resource-view, this also bumps the refcount + */ + SOKOL_ASSERT(img->d3d11.srv); + _sg_d3d11_GetResource((ID3D11View*)img->d3d11.srv, (ID3D11Resource**)&img->d3d11.tex2d); + SOKOL_ASSERT(img->d3d11.tex2d); + } + if (img->d3d11.srv) { + _sg_d3d11_AddRef(img->d3d11.srv); + } + } + + /* if not injected, create texture */ + if (0 == img->d3d11.tex2d) { + D3D11_TEXTURE2D_DESC d3d11_tex_desc; + memset(&d3d11_tex_desc, 0, sizeof(d3d11_tex_desc)); + d3d11_tex_desc.Width = (UINT)img->cmn.width; + d3d11_tex_desc.Height = (UINT)img->cmn.height; + d3d11_tex_desc.MipLevels = (UINT)img->cmn.num_mipmaps; + switch (img->cmn.type) { + case SG_IMAGETYPE_ARRAY: d3d11_tex_desc.ArraySize = (UINT)img->cmn.num_slices; break; + case SG_IMAGETYPE_CUBE: d3d11_tex_desc.ArraySize = 6; break; + default: d3d11_tex_desc.ArraySize = 1; break; + } + d3d11_tex_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + d3d11_tex_desc.Format = img->d3d11.format; + if (img->cmn.render_target) { + d3d11_tex_desc.Usage = D3D11_USAGE_DEFAULT; + if (!msaa) { + d3d11_tex_desc.BindFlags |= D3D11_BIND_RENDER_TARGET; + } + d3d11_tex_desc.CPUAccessFlags = 0; + } + else { + d3d11_tex_desc.Usage = _sg_d3d11_usage(img->cmn.usage); + d3d11_tex_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(img->cmn.usage); + } + if (img->d3d11.format == DXGI_FORMAT_UNKNOWN) { + /* trying to create a texture format that's not supported by D3D */ + SOKOL_LOG("trying to create a D3D11 texture with unsupported pixel format\n"); + return SG_RESOURCESTATE_FAILED; + } + d3d11_tex_desc.SampleDesc.Count = 1; + d3d11_tex_desc.SampleDesc.Quality = 0; + d3d11_tex_desc.MiscFlags = (img->cmn.type == SG_IMAGETYPE_CUBE) ? D3D11_RESOURCE_MISC_TEXTURECUBE : 0; + + hr = _sg_d3d11_CreateTexture2D(_sg.d3d11.dev, &d3d11_tex_desc, init_data, &img->d3d11.tex2d); + SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11.tex2d); + } + + /* ...and similar, if not injected, create shader-resource-view */ + if (0 == img->d3d11.srv) { + D3D11_SHADER_RESOURCE_VIEW_DESC d3d11_srv_desc; + memset(&d3d11_srv_desc, 0, sizeof(d3d11_srv_desc)); + d3d11_srv_desc.Format = img->d3d11.format; + switch (img->cmn.type) { + case SG_IMAGETYPE_2D: + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + d3d11_srv_desc.Texture2D.MipLevels = (UINT)img->cmn.num_mipmaps; + break; + case SG_IMAGETYPE_CUBE: + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE; + d3d11_srv_desc.TextureCube.MipLevels = (UINT)img->cmn.num_mipmaps; + break; + case SG_IMAGETYPE_ARRAY: + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY; + d3d11_srv_desc.Texture2DArray.MipLevels = (UINT)img->cmn.num_mipmaps; + d3d11_srv_desc.Texture2DArray.ArraySize = (UINT)img->cmn.num_slices; + break; + default: + SOKOL_UNREACHABLE; break; + } + hr = _sg_d3d11_CreateShaderResourceView(_sg.d3d11.dev, (ID3D11Resource*)img->d3d11.tex2d, &d3d11_srv_desc, &img->d3d11.srv); + SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11.srv); + } + } + else { + /* 3D texture - same procedure, first check if injected, than create non-injected */ + if (injected) { + img->d3d11.tex3d = (ID3D11Texture3D*) desc->d3d11_texture; + img->d3d11.srv = (ID3D11ShaderResourceView*) desc->d3d11_shader_resource_view; + if (img->d3d11.tex3d) { + _sg_d3d11_AddRef(img->d3d11.tex3d); + } + else { + SOKOL_ASSERT(img->d3d11.srv); + _sg_d3d11_GetResource((ID3D11View*)img->d3d11.srv, (ID3D11Resource**)&img->d3d11.tex3d); + SOKOL_ASSERT(img->d3d11.tex3d); + } + if (img->d3d11.srv) { + _sg_d3d11_AddRef(img->d3d11.srv); + } + } + + if (0 == img->d3d11.tex3d) { + D3D11_TEXTURE3D_DESC d3d11_tex_desc; + memset(&d3d11_tex_desc, 0, sizeof(d3d11_tex_desc)); + d3d11_tex_desc.Width = (UINT)img->cmn.width; + d3d11_tex_desc.Height = (UINT)img->cmn.height; + d3d11_tex_desc.Depth = (UINT)img->cmn.num_slices; + d3d11_tex_desc.MipLevels = (UINT)img->cmn.num_mipmaps; + d3d11_tex_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + d3d11_tex_desc.Format = img->d3d11.format; + if (img->cmn.render_target) { + d3d11_tex_desc.Usage = D3D11_USAGE_DEFAULT; + if (!msaa) { + d3d11_tex_desc.BindFlags |= D3D11_BIND_RENDER_TARGET; + } + d3d11_tex_desc.CPUAccessFlags = 0; + } + else { + d3d11_tex_desc.Usage = _sg_d3d11_usage(img->cmn.usage); + d3d11_tex_desc.CPUAccessFlags = _sg_d3d11_cpu_access_flags(img->cmn.usage); + } + if (img->d3d11.format == DXGI_FORMAT_UNKNOWN) { + /* trying to create a texture format that's not supported by D3D */ + SOKOL_LOG("trying to create a D3D11 texture with unsupported pixel format\n"); + return SG_RESOURCESTATE_FAILED; + } + hr = _sg_d3d11_CreateTexture3D(_sg.d3d11.dev, &d3d11_tex_desc, init_data, &img->d3d11.tex3d); + SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11.tex3d); + } + + if (0 == img->d3d11.srv) { + D3D11_SHADER_RESOURCE_VIEW_DESC d3d11_srv_desc; + memset(&d3d11_srv_desc, 0, sizeof(d3d11_srv_desc)); + d3d11_srv_desc.Format = img->d3d11.format; + d3d11_srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D; + d3d11_srv_desc.Texture3D.MipLevels = (UINT)img->cmn.num_mipmaps; + hr = _sg_d3d11_CreateShaderResourceView(_sg.d3d11.dev, (ID3D11Resource*)img->d3d11.tex3d, &d3d11_srv_desc, &img->d3d11.srv); + SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11.srv); + } + } + + /* also need to create a separate MSAA render target texture? */ + if (msaa) { + D3D11_TEXTURE2D_DESC d3d11_tex_desc; + memset(&d3d11_tex_desc, 0, sizeof(d3d11_tex_desc)); + d3d11_tex_desc.Width = (UINT)img->cmn.width; + d3d11_tex_desc.Height = (UINT)img->cmn.height; + d3d11_tex_desc.MipLevels = 1; + d3d11_tex_desc.ArraySize = 1; + d3d11_tex_desc.Format = img->d3d11.format; + d3d11_tex_desc.Usage = D3D11_USAGE_DEFAULT; + d3d11_tex_desc.BindFlags = D3D11_BIND_RENDER_TARGET; + d3d11_tex_desc.CPUAccessFlags = 0; + d3d11_tex_desc.SampleDesc.Count = (UINT)img->cmn.sample_count; + d3d11_tex_desc.SampleDesc.Quality = (UINT)D3D11_STANDARD_MULTISAMPLE_PATTERN; + hr = _sg_d3d11_CreateTexture2D(_sg.d3d11.dev, &d3d11_tex_desc, NULL, &img->d3d11.texmsaa); + SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11.texmsaa); + } + + /* sampler state object, note D3D11 implements an internal shared-pool for sampler objects */ + D3D11_SAMPLER_DESC d3d11_smp_desc; + memset(&d3d11_smp_desc, 0, sizeof(d3d11_smp_desc)); + d3d11_smp_desc.Filter = _sg_d3d11_filter(img->cmn.min_filter, img->cmn.mag_filter, img->cmn.max_anisotropy); + d3d11_smp_desc.AddressU = _sg_d3d11_address_mode(img->cmn.wrap_u); + d3d11_smp_desc.AddressV = _sg_d3d11_address_mode(img->cmn.wrap_v); + d3d11_smp_desc.AddressW = _sg_d3d11_address_mode(img->cmn.wrap_w); + switch (img->cmn.border_color) { + case SG_BORDERCOLOR_TRANSPARENT_BLACK: + /* all 0.0f */ + break; + case SG_BORDERCOLOR_OPAQUE_WHITE: + for (int i = 0; i < 4; i++) { + d3d11_smp_desc.BorderColor[i] = 1.0f; + } + break; + default: + /* opaque black */ + d3d11_smp_desc.BorderColor[3] = 1.0f; + break; + } + d3d11_smp_desc.MaxAnisotropy = img->cmn.max_anisotropy; + d3d11_smp_desc.ComparisonFunc = D3D11_COMPARISON_NEVER; + d3d11_smp_desc.MinLOD = desc->min_lod; + d3d11_smp_desc.MaxLOD = desc->max_lod; + hr = _sg_d3d11_CreateSamplerState(_sg.d3d11.dev, &d3d11_smp_desc, &img->d3d11.smp); + SOKOL_ASSERT(SUCCEEDED(hr) && img->d3d11.smp); + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_d3d11_destroy_image(_sg_image_t* img) { + SOKOL_ASSERT(img); + if (img->d3d11.tex2d) { + _sg_d3d11_Release(img->d3d11.tex2d); + } + if (img->d3d11.tex3d) { + _sg_d3d11_Release(img->d3d11.tex3d); + } + if (img->d3d11.texds) { + _sg_d3d11_Release(img->d3d11.texds); + } + if (img->d3d11.texmsaa) { + _sg_d3d11_Release(img->d3d11.texmsaa); + } + if (img->d3d11.srv) { + _sg_d3d11_Release(img->d3d11.srv); + } + if (img->d3d11.smp) { + _sg_d3d11_Release(img->d3d11.smp); + } +} + +_SOKOL_PRIVATE bool _sg_d3d11_load_d3dcompiler_dll(void) { + /* on UWP, don't do anything (not tested) */ + #if (defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) + return true; + #else + /* load DLL on demand */ + if ((0 == _sg.d3d11.d3dcompiler_dll) && !_sg.d3d11.d3dcompiler_dll_load_failed) { + _sg.d3d11.d3dcompiler_dll = LoadLibraryA("d3dcompiler_47.dll"); + if (0 == _sg.d3d11.d3dcompiler_dll) { + /* don't attempt to load missing DLL in the future */ + SOKOL_LOG("failed to load d3dcompiler_47.dll!\n"); + _sg.d3d11.d3dcompiler_dll_load_failed = true; + return false; + } + /* look up function pointers */ + _sg.d3d11.D3DCompile_func = (pD3DCompile)(void*) GetProcAddress(_sg.d3d11.d3dcompiler_dll, "D3DCompile"); + SOKOL_ASSERT(_sg.d3d11.D3DCompile_func); + } + return 0 != _sg.d3d11.d3dcompiler_dll; + #endif +} + +#if (defined(WINAPI_FAMILY_PARTITION) && !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)) +#define _sg_d3d11_D3DCompile D3DCompile +#else +#define _sg_d3d11_D3DCompile _sg.d3d11.D3DCompile_func +#endif + +_SOKOL_PRIVATE ID3DBlob* _sg_d3d11_compile_shader(const sg_shader_stage_desc* stage_desc) { + if (!_sg_d3d11_load_d3dcompiler_dll()) { + return NULL; + } + SOKOL_ASSERT(stage_desc->d3d11_target); + ID3DBlob* output = NULL; + ID3DBlob* errors_or_warnings = NULL; + HRESULT hr = _sg_d3d11_D3DCompile( + stage_desc->source, /* pSrcData */ + strlen(stage_desc->source), /* SrcDataSize */ + NULL, /* pSourceName */ + NULL, /* pDefines */ + NULL, /* pInclude */ + stage_desc->entry ? stage_desc->entry : "main", /* pEntryPoint */ + stage_desc->d3d11_target, /* pTarget (vs_5_0 or ps_5_0) */ + D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR | D3DCOMPILE_OPTIMIZATION_LEVEL3, /* Flags1 */ + 0, /* Flags2 */ + &output, /* ppCode */ + &errors_or_warnings); /* ppErrorMsgs */ + if (errors_or_warnings) { + SOKOL_LOG((LPCSTR)_sg_d3d11_GetBufferPointer(errors_or_warnings)); + _sg_d3d11_Release(errors_or_warnings); errors_or_warnings = NULL; + } + if (FAILED(hr)) { + /* just in case, usually output is NULL here */ + if (output) { + _sg_d3d11_Release(output); + output = NULL; + } + } + return output; +} + +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { + SOKOL_ASSERT(shd && desc); + SOKOL_ASSERT(!shd->d3d11.vs && !shd->d3d11.fs && !shd->d3d11.vs_blob); + HRESULT hr; + _SOKOL_UNUSED(hr); + + _sg_shader_common_init(&shd->cmn, desc); + + /* copy vertex attribute semantic names and indices */ + for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { + _sg_strcpy(&shd->d3d11.attrs[i].sem_name, desc->attrs[i].sem_name); + shd->d3d11.attrs[i].sem_index = desc->attrs[i].sem_index; + } + + /* shader stage uniform blocks and image slots */ + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + _sg_shader_stage_t* cmn_stage = &shd->cmn.stage[stage_index]; + _sg_d3d11_shader_stage_t* d3d11_stage = &shd->d3d11.stage[stage_index]; + for (int ub_index = 0; ub_index < cmn_stage->num_uniform_blocks; ub_index++) { + const _sg_uniform_block_t* ub = &cmn_stage->uniform_blocks[ub_index]; + + /* create a D3D constant buffer for each uniform block */ + SOKOL_ASSERT(0 == d3d11_stage->cbufs[ub_index]); + D3D11_BUFFER_DESC cb_desc; + memset(&cb_desc, 0, sizeof(cb_desc)); + cb_desc.ByteWidth = (UINT)_sg_roundup((int)ub->size, 16); + cb_desc.Usage = D3D11_USAGE_DEFAULT; + cb_desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + hr = _sg_d3d11_CreateBuffer(_sg.d3d11.dev, &cb_desc, NULL, &d3d11_stage->cbufs[ub_index]); + SOKOL_ASSERT(SUCCEEDED(hr) && d3d11_stage->cbufs[ub_index]); + } + } + + const void* vs_ptr = 0, *fs_ptr = 0; + SIZE_T vs_length = 0, fs_length = 0; + ID3DBlob* vs_blob = 0, *fs_blob = 0; + if (desc->vs.bytecode.ptr && desc->fs.bytecode.ptr) { + /* create from shader byte code */ + vs_ptr = desc->vs.bytecode.ptr; + fs_ptr = desc->fs.bytecode.ptr; + vs_length = desc->vs.bytecode.size; + fs_length = desc->fs.bytecode.size; + } + else { + /* compile from shader source code */ + vs_blob = _sg_d3d11_compile_shader(&desc->vs); + fs_blob = _sg_d3d11_compile_shader(&desc->fs); + if (vs_blob && fs_blob) { + vs_ptr = _sg_d3d11_GetBufferPointer(vs_blob); + vs_length = _sg_d3d11_GetBufferSize(vs_blob); + fs_ptr = _sg_d3d11_GetBufferPointer(fs_blob); + fs_length = _sg_d3d11_GetBufferSize(fs_blob); + } + } + sg_resource_state result = SG_RESOURCESTATE_FAILED; + if (vs_ptr && fs_ptr && (vs_length > 0) && (fs_length > 0)) { + /* create the D3D vertex- and pixel-shader objects */ + hr = _sg_d3d11_CreateVertexShader(_sg.d3d11.dev, vs_ptr, vs_length, NULL, &shd->d3d11.vs); + bool vs_succeeded = SUCCEEDED(hr) && shd->d3d11.vs; + hr = _sg_d3d11_CreatePixelShader(_sg.d3d11.dev, fs_ptr, fs_length, NULL, &shd->d3d11.fs); + bool fs_succeeded = SUCCEEDED(hr) && shd->d3d11.fs; + + /* need to store the vertex shader byte code, this is needed later in sg_create_pipeline */ + if (vs_succeeded && fs_succeeded) { + shd->d3d11.vs_blob_length = vs_length; + shd->d3d11.vs_blob = SOKOL_MALLOC((size_t)vs_length); + SOKOL_ASSERT(shd->d3d11.vs_blob); + memcpy(shd->d3d11.vs_blob, vs_ptr, vs_length); + result = SG_RESOURCESTATE_VALID; + } + } + if (vs_blob) { + _sg_d3d11_Release(vs_blob); vs_blob = 0; + } + if (fs_blob) { + _sg_d3d11_Release(fs_blob); fs_blob = 0; + } + return result; +} + +_SOKOL_PRIVATE void _sg_d3d11_destroy_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd); + if (shd->d3d11.vs) { + _sg_d3d11_Release(shd->d3d11.vs); + } + if (shd->d3d11.fs) { + _sg_d3d11_Release(shd->d3d11.fs); + } + if (shd->d3d11.vs_blob) { + SOKOL_FREE(shd->d3d11.vs_blob); + } + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + _sg_shader_stage_t* cmn_stage = &shd->cmn.stage[stage_index]; + _sg_d3d11_shader_stage_t* d3d11_stage = &shd->d3d11.stage[stage_index]; + for (int ub_index = 0; ub_index < cmn_stage->num_uniform_blocks; ub_index++) { + if (d3d11_stage->cbufs[ub_index]) { + _sg_d3d11_Release(d3d11_stage->cbufs[ub_index]); + } + } + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(pip && shd && desc); + SOKOL_ASSERT(desc->shader.id == shd->slot.id); + SOKOL_ASSERT(shd->slot.state == SG_RESOURCESTATE_VALID); + SOKOL_ASSERT(shd->d3d11.vs_blob && shd->d3d11.vs_blob_length > 0); + SOKOL_ASSERT(!pip->d3d11.il && !pip->d3d11.rs && !pip->d3d11.dss && !pip->d3d11.bs); + + pip->shader = shd; + _sg_pipeline_common_init(&pip->cmn, desc); + pip->d3d11.index_format = _sg_d3d11_index_format(pip->cmn.index_type); + pip->d3d11.topology = _sg_d3d11_primitive_topology(desc->primitive_type); + pip->d3d11.stencil_ref = desc->stencil.ref; + + /* create input layout object */ + HRESULT hr; + _SOKOL_UNUSED(hr); + D3D11_INPUT_ELEMENT_DESC d3d11_comps[SG_MAX_VERTEX_ATTRIBUTES]; + memset(d3d11_comps, 0, sizeof(d3d11_comps)); + int attr_index = 0; + for (; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + const sg_vertex_attr_desc* a_desc = &desc->layout.attrs[attr_index]; + if (a_desc->format == SG_VERTEXFORMAT_INVALID) { + break; + } + SOKOL_ASSERT(a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS); + const sg_buffer_layout_desc* l_desc = &desc->layout.buffers[a_desc->buffer_index]; + const sg_vertex_step step_func = l_desc->step_func; + const int step_rate = l_desc->step_rate; + D3D11_INPUT_ELEMENT_DESC* d3d11_comp = &d3d11_comps[attr_index]; + d3d11_comp->SemanticName = _sg_strptr(&shd->d3d11.attrs[attr_index].sem_name); + d3d11_comp->SemanticIndex = (UINT)shd->d3d11.attrs[attr_index].sem_index; + d3d11_comp->Format = _sg_d3d11_vertex_format(a_desc->format); + d3d11_comp->InputSlot = (UINT)a_desc->buffer_index; + d3d11_comp->AlignedByteOffset = (UINT)a_desc->offset; + d3d11_comp->InputSlotClass = _sg_d3d11_input_classification(step_func); + if (SG_VERTEXSTEP_PER_INSTANCE == step_func) { + d3d11_comp->InstanceDataStepRate = (UINT)step_rate; + } + pip->cmn.vertex_layout_valid[a_desc->buffer_index] = true; + } + for (int layout_index = 0; layout_index < SG_MAX_SHADERSTAGE_BUFFERS; layout_index++) { + if (pip->cmn.vertex_layout_valid[layout_index]) { + const sg_buffer_layout_desc* l_desc = &desc->layout.buffers[layout_index]; + SOKOL_ASSERT(l_desc->stride > 0); + pip->d3d11.vb_strides[layout_index] = (UINT)l_desc->stride; + } + else { + pip->d3d11.vb_strides[layout_index] = 0; + } + } + hr = _sg_d3d11_CreateInputLayout(_sg.d3d11.dev, + d3d11_comps, /* pInputElementDesc */ + (UINT)attr_index, /* NumElements */ + shd->d3d11.vs_blob, /* pShaderByteCodeWithInputSignature */ + shd->d3d11.vs_blob_length, /* BytecodeLength */ + &pip->d3d11.il); + SOKOL_ASSERT(SUCCEEDED(hr) && pip->d3d11.il); + + /* create rasterizer state */ + D3D11_RASTERIZER_DESC rs_desc; + memset(&rs_desc, 0, sizeof(rs_desc)); + rs_desc.FillMode = D3D11_FILL_SOLID; + rs_desc.CullMode = _sg_d3d11_cull_mode(desc->cull_mode); + rs_desc.FrontCounterClockwise = desc->face_winding == SG_FACEWINDING_CCW; + rs_desc.DepthBias = (INT) pip->cmn.depth_bias; + rs_desc.DepthBiasClamp = pip->cmn.depth_bias_clamp; + rs_desc.SlopeScaledDepthBias = pip->cmn.depth_bias_slope_scale; + rs_desc.DepthClipEnable = TRUE; + rs_desc.ScissorEnable = TRUE; + rs_desc.MultisampleEnable = desc->sample_count > 1; + rs_desc.AntialiasedLineEnable = FALSE; + hr = _sg_d3d11_CreateRasterizerState(_sg.d3d11.dev, &rs_desc, &pip->d3d11.rs); + SOKOL_ASSERT(SUCCEEDED(hr) && pip->d3d11.rs); + + /* create depth-stencil state */ + D3D11_DEPTH_STENCIL_DESC dss_desc; + memset(&dss_desc, 0, sizeof(dss_desc)); + dss_desc.DepthEnable = TRUE; + dss_desc.DepthWriteMask = desc->depth.write_enabled ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO; + dss_desc.DepthFunc = _sg_d3d11_compare_func(desc->depth.compare); + dss_desc.StencilEnable = desc->stencil.enabled; + dss_desc.StencilReadMask = desc->stencil.read_mask; + dss_desc.StencilWriteMask = desc->stencil.write_mask; + const sg_stencil_face_state* sf = &desc->stencil.front; + dss_desc.FrontFace.StencilFailOp = _sg_d3d11_stencil_op(sf->fail_op); + dss_desc.FrontFace.StencilDepthFailOp = _sg_d3d11_stencil_op(sf->depth_fail_op); + dss_desc.FrontFace.StencilPassOp = _sg_d3d11_stencil_op(sf->pass_op); + dss_desc.FrontFace.StencilFunc = _sg_d3d11_compare_func(sf->compare); + const sg_stencil_face_state* sb = &desc->stencil.back; + dss_desc.BackFace.StencilFailOp = _sg_d3d11_stencil_op(sb->fail_op); + dss_desc.BackFace.StencilDepthFailOp = _sg_d3d11_stencil_op(sb->depth_fail_op); + dss_desc.BackFace.StencilPassOp = _sg_d3d11_stencil_op(sb->pass_op); + dss_desc.BackFace.StencilFunc = _sg_d3d11_compare_func(sb->compare); + hr = _sg_d3d11_CreateDepthStencilState(_sg.d3d11.dev, &dss_desc, &pip->d3d11.dss); + SOKOL_ASSERT(SUCCEEDED(hr) && pip->d3d11.dss); + + /* create blend state */ + D3D11_BLEND_DESC bs_desc; + memset(&bs_desc, 0, sizeof(bs_desc)); + bs_desc.AlphaToCoverageEnable = desc->alpha_to_coverage_enabled; + bs_desc.IndependentBlendEnable = TRUE; + { + int i = 0; + for (i = 0; i < desc->color_count; i++) { + const sg_blend_state* src = &desc->colors[i].blend; + D3D11_RENDER_TARGET_BLEND_DESC* dst = &bs_desc.RenderTarget[i]; + dst->BlendEnable = src->enabled; + dst->SrcBlend = _sg_d3d11_blend_factor(src->src_factor_rgb); + dst->DestBlend = _sg_d3d11_blend_factor(src->dst_factor_rgb); + dst->BlendOp = _sg_d3d11_blend_op(src->op_rgb); + dst->SrcBlendAlpha = _sg_d3d11_blend_factor(src->src_factor_alpha); + dst->DestBlendAlpha = _sg_d3d11_blend_factor(src->dst_factor_alpha); + dst->BlendOpAlpha = _sg_d3d11_blend_op(src->op_alpha); + dst->RenderTargetWriteMask = _sg_d3d11_color_write_mask(desc->colors[i].write_mask); + } + for (; i < 8; i++) { + D3D11_RENDER_TARGET_BLEND_DESC* dst = &bs_desc.RenderTarget[i]; + dst->BlendEnable = FALSE; + dst->SrcBlend = dst->SrcBlendAlpha = D3D11_BLEND_ONE; + dst->DestBlend = dst->DestBlendAlpha = D3D11_BLEND_ZERO; + dst->BlendOp = dst->BlendOpAlpha = D3D11_BLEND_OP_ADD; + dst->RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; + } + } + hr = _sg_d3d11_CreateBlendState(_sg.d3d11.dev, &bs_desc, &pip->d3d11.bs); + SOKOL_ASSERT(SUCCEEDED(hr) && pip->d3d11.bs); + + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_d3d11_destroy_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + if (pip->d3d11.il) { + _sg_d3d11_Release(pip->d3d11.il); + } + if (pip->d3d11.rs) { + _sg_d3d11_Release(pip->d3d11.rs); + } + if (pip->d3d11.dss) { + _sg_d3d11_Release(pip->d3d11.dss); + } + if (pip->d3d11.bs) { + _sg_d3d11_Release(pip->d3d11.bs); + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_d3d11_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { + SOKOL_ASSERT(pass && desc); + SOKOL_ASSERT(att_images && att_images[0]); + SOKOL_ASSERT(_sg.d3d11.dev); + + _sg_pass_common_init(&pass->cmn, desc); + + for (int i = 0; i < pass->cmn.num_color_atts; i++) { + const sg_pass_attachment_desc* att_desc = &desc->color_attachments[i]; + _SOKOL_UNUSED(att_desc); + SOKOL_ASSERT(att_desc->image.id != SG_INVALID_ID); + _sg_image_t* att_img = att_images[i]; + SOKOL_ASSERT(att_img && (att_img->slot.id == att_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(att_img->cmn.pixel_format)); + SOKOL_ASSERT(0 == pass->d3d11.color_atts[i].image); + pass->d3d11.color_atts[i].image = att_img; + + /* create D3D11 render-target-view */ + const _sg_pass_attachment_t* cmn_att = &pass->cmn.color_atts[i]; + SOKOL_ASSERT(0 == pass->d3d11.color_atts[i].rtv); + ID3D11Resource* d3d11_res = 0; + const bool is_msaa = att_img->cmn.sample_count > 1; + D3D11_RENDER_TARGET_VIEW_DESC d3d11_rtv_desc; + memset(&d3d11_rtv_desc, 0, sizeof(d3d11_rtv_desc)); + d3d11_rtv_desc.Format = att_img->d3d11.format; + if ((att_img->cmn.type == SG_IMAGETYPE_2D) || is_msaa) { + if (is_msaa) { + d3d11_res = (ID3D11Resource*) att_img->d3d11.texmsaa; + d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS; + } + else { + d3d11_res = (ID3D11Resource*) att_img->d3d11.tex2d; + d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; + d3d11_rtv_desc.Texture2D.MipSlice = (UINT)cmn_att->mip_level; + } + } + else if ((att_img->cmn.type == SG_IMAGETYPE_CUBE) || (att_img->cmn.type == SG_IMAGETYPE_ARRAY)) { + d3d11_res = (ID3D11Resource*) att_img->d3d11.tex2d; + d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY; + d3d11_rtv_desc.Texture2DArray.MipSlice = (UINT)cmn_att->mip_level; + d3d11_rtv_desc.Texture2DArray.FirstArraySlice = (UINT)cmn_att->slice; + d3d11_rtv_desc.Texture2DArray.ArraySize = 1; + } + else { + SOKOL_ASSERT(att_img->cmn.type == SG_IMAGETYPE_3D); + d3d11_res = (ID3D11Resource*) att_img->d3d11.tex3d; + d3d11_rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D; + d3d11_rtv_desc.Texture3D.MipSlice = (UINT)cmn_att->mip_level; + d3d11_rtv_desc.Texture3D.FirstWSlice = (UINT)cmn_att->slice; + d3d11_rtv_desc.Texture3D.WSize = 1; + } + SOKOL_ASSERT(d3d11_res); + HRESULT hr = _sg_d3d11_CreateRenderTargetView(_sg.d3d11.dev, d3d11_res, &d3d11_rtv_desc, &pass->d3d11.color_atts[i].rtv); + _SOKOL_UNUSED(hr); + SOKOL_ASSERT(SUCCEEDED(hr) && pass->d3d11.color_atts[i].rtv); + } + + /* optional depth-stencil image */ + SOKOL_ASSERT(0 == pass->d3d11.ds_att.image); + SOKOL_ASSERT(0 == pass->d3d11.ds_att.dsv); + if (desc->depth_stencil_attachment.image.id != SG_INVALID_ID) { + const int ds_img_index = SG_MAX_COLOR_ATTACHMENTS; + const sg_pass_attachment_desc* att_desc = &desc->depth_stencil_attachment; + _SOKOL_UNUSED(att_desc); + _sg_image_t* att_img = att_images[ds_img_index]; + SOKOL_ASSERT(att_img && (att_img->slot.id == att_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(att_img->cmn.pixel_format)); + SOKOL_ASSERT(0 == pass->d3d11.ds_att.image); + pass->d3d11.ds_att.image = att_img; + + /* create D3D11 depth-stencil-view */ + D3D11_DEPTH_STENCIL_VIEW_DESC d3d11_dsv_desc; + memset(&d3d11_dsv_desc, 0, sizeof(d3d11_dsv_desc)); + d3d11_dsv_desc.Format = att_img->d3d11.format; + const bool is_msaa = att_img->cmn.sample_count > 1; + if (is_msaa) { + d3d11_dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DMS; + } + else { + d3d11_dsv_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D; + } + ID3D11Resource* d3d11_res = (ID3D11Resource*) att_img->d3d11.texds; + SOKOL_ASSERT(d3d11_res); + HRESULT hr = _sg_d3d11_CreateDepthStencilView(_sg.d3d11.dev, d3d11_res, &d3d11_dsv_desc, &pass->d3d11.ds_att.dsv); + _SOKOL_UNUSED(hr); + SOKOL_ASSERT(SUCCEEDED(hr) && pass->d3d11.ds_att.dsv); + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_d3d11_destroy_pass(_sg_pass_t* pass) { + SOKOL_ASSERT(pass); + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + if (pass->d3d11.color_atts[i].rtv) { + _sg_d3d11_Release(pass->d3d11.color_atts[i].rtv); + } + } + if (pass->d3d11.ds_att.dsv) { + _sg_d3d11_Release(pass->d3d11.ds_att.dsv); + } +} + +_SOKOL_PRIVATE _sg_image_t* _sg_d3d11_pass_color_image(const _sg_pass_t* pass, int index) { + SOKOL_ASSERT(pass && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + /* NOTE: may return null */ + return pass->d3d11.color_atts[index].image; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_d3d11_pass_ds_image(const _sg_pass_t* pass) { + /* NOTE: may return null */ + SOKOL_ASSERT(pass); + return pass->d3d11.ds_att.image; +} + +_SOKOL_PRIVATE void _sg_d3d11_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { + SOKOL_ASSERT(action); + SOKOL_ASSERT(!_sg.d3d11.in_pass); + SOKOL_ASSERT(_sg.d3d11.rtv_cb || _sg.d3d11.rtv_userdata_cb); + SOKOL_ASSERT(_sg.d3d11.dsv_cb || _sg.d3d11.dsv_userdata_cb); + _sg.d3d11.in_pass = true; + _sg.d3d11.cur_width = w; + _sg.d3d11.cur_height = h; + if (pass) { + _sg.d3d11.cur_pass = pass; + _sg.d3d11.cur_pass_id.id = pass->slot.id; + _sg.d3d11.num_rtvs = 0; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + _sg.d3d11.cur_rtvs[i] = pass->d3d11.color_atts[i].rtv; + if (_sg.d3d11.cur_rtvs[i]) { + _sg.d3d11.num_rtvs++; + } + } + _sg.d3d11.cur_dsv = pass->d3d11.ds_att.dsv; + } + else { + /* render to default frame buffer */ + _sg.d3d11.cur_pass = 0; + _sg.d3d11.cur_pass_id.id = SG_INVALID_ID; + _sg.d3d11.num_rtvs = 1; + if (_sg.d3d11.rtv_cb) { + _sg.d3d11.cur_rtvs[0] = (ID3D11RenderTargetView*) _sg.d3d11.rtv_cb(); + } + else { + _sg.d3d11.cur_rtvs[0] = (ID3D11RenderTargetView*) _sg.d3d11.rtv_userdata_cb(_sg.d3d11.user_data); + } + for (int i = 1; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + _sg.d3d11.cur_rtvs[i] = 0; + } + if (_sg.d3d11.dsv_cb) { + _sg.d3d11.cur_dsv = (ID3D11DepthStencilView*) _sg.d3d11.dsv_cb(); + } + else { + _sg.d3d11.cur_dsv = (ID3D11DepthStencilView*) _sg.d3d11.dsv_userdata_cb(_sg.d3d11.user_data); + } + SOKOL_ASSERT(_sg.d3d11.cur_rtvs[0] && _sg.d3d11.cur_dsv); + } + /* apply the render-target- and depth-stencil-views */ + _sg_d3d11_OMSetRenderTargets(_sg.d3d11.ctx, SG_MAX_COLOR_ATTACHMENTS, _sg.d3d11.cur_rtvs, _sg.d3d11.cur_dsv); + + /* set viewport and scissor rect to cover whole screen */ + D3D11_VIEWPORT vp; + memset(&vp, 0, sizeof(vp)); + vp.Width = (FLOAT) w; + vp.Height = (FLOAT) h; + vp.MaxDepth = 1.0f; + _sg_d3d11_RSSetViewports(_sg.d3d11.ctx, 1, &vp); + D3D11_RECT rect; + rect.left = 0; + rect.top = 0; + rect.right = w; + rect.bottom = h; + _sg_d3d11_RSSetScissorRects(_sg.d3d11.ctx, 1, &rect); + + /* perform clear action */ + for (int i = 0; i < _sg.d3d11.num_rtvs; i++) { + if (action->colors[i].action == SG_ACTION_CLEAR) { + _sg_d3d11_ClearRenderTargetView(_sg.d3d11.ctx, _sg.d3d11.cur_rtvs[i], &action->colors[i].value.r); + } + } + UINT ds_flags = 0; + if (action->depth.action == SG_ACTION_CLEAR) { + ds_flags |= D3D11_CLEAR_DEPTH; + } + if (action->stencil.action == SG_ACTION_CLEAR) { + ds_flags |= D3D11_CLEAR_STENCIL; + } + if ((0 != ds_flags) && _sg.d3d11.cur_dsv) { + _sg_d3d11_ClearDepthStencilView(_sg.d3d11.ctx, _sg.d3d11.cur_dsv, ds_flags, action->depth.value, action->stencil.value); + } +} + +/* D3D11CalcSubresource only exists for C++ */ +_SOKOL_PRIVATE UINT _sg_d3d11_calcsubresource(UINT mip_slice, UINT array_slice, UINT mip_levels) { + return mip_slice + array_slice * mip_levels; +} + +_SOKOL_PRIVATE void _sg_d3d11_end_pass(void) { + SOKOL_ASSERT(_sg.d3d11.in_pass && _sg.d3d11.ctx); + _sg.d3d11.in_pass = false; + + /* need to resolve MSAA render target into texture? */ + if (_sg.d3d11.cur_pass) { + SOKOL_ASSERT(_sg.d3d11.cur_pass->slot.id == _sg.d3d11.cur_pass_id.id); + for (int i = 0; i < _sg.d3d11.num_rtvs; i++) { + _sg_pass_attachment_t* cmn_att = &_sg.d3d11.cur_pass->cmn.color_atts[i]; + _sg_image_t* att_img = _sg.d3d11.cur_pass->d3d11.color_atts[i].image; + SOKOL_ASSERT(att_img && (att_img->slot.id == cmn_att->image_id.id)); + if (att_img->cmn.sample_count > 1) { + /* FIXME: support MSAA resolve into 3D texture */ + SOKOL_ASSERT(att_img->d3d11.tex2d && att_img->d3d11.texmsaa && !att_img->d3d11.tex3d); + SOKOL_ASSERT(DXGI_FORMAT_UNKNOWN != att_img->d3d11.format); + UINT dst_subres = _sg_d3d11_calcsubresource((UINT)cmn_att->mip_level, (UINT)cmn_att->slice, (UINT)att_img->cmn.num_mipmaps); + _sg_d3d11_ResolveSubresource(_sg.d3d11.ctx, + (ID3D11Resource*) att_img->d3d11.tex2d, /* pDstResource */ + dst_subres, /* DstSubresource */ + (ID3D11Resource*) att_img->d3d11.texmsaa, /* pSrcResource */ + 0, /* SrcSubresource */ + att_img->d3d11.format); + } + } + } + _sg.d3d11.cur_pass = 0; + _sg.d3d11.cur_pass_id.id = SG_INVALID_ID; + _sg.d3d11.cur_pipeline = 0; + _sg.d3d11.cur_pipeline_id.id = SG_INVALID_ID; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + _sg.d3d11.cur_rtvs[i] = 0; + } + _sg.d3d11.cur_dsv = 0; + _sg_d3d11_clear_state(); +} + +_SOKOL_PRIVATE void _sg_d3d11_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_sg.d3d11.ctx); + SOKOL_ASSERT(_sg.d3d11.in_pass); + D3D11_VIEWPORT vp; + vp.TopLeftX = (FLOAT) x; + vp.TopLeftY = (FLOAT) (origin_top_left ? y : (_sg.d3d11.cur_height - (y + h))); + vp.Width = (FLOAT) w; + vp.Height = (FLOAT) h; + vp.MinDepth = 0.0f; + vp.MaxDepth = 1.0f; + _sg_d3d11_RSSetViewports(_sg.d3d11.ctx, 1, &vp); +} + +_SOKOL_PRIVATE void _sg_d3d11_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_sg.d3d11.ctx); + SOKOL_ASSERT(_sg.d3d11.in_pass); + D3D11_RECT rect; + rect.left = x; + rect.top = (origin_top_left ? y : (_sg.d3d11.cur_height - (y + h))); + rect.right = x + w; + rect.bottom = origin_top_left ? (y + h) : (_sg.d3d11.cur_height - y); + _sg_d3d11_RSSetScissorRects(_sg.d3d11.ctx, 1, &rect); +} + +_SOKOL_PRIVATE void _sg_d3d11_apply_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + SOKOL_ASSERT(pip->shader && (pip->cmn.shader_id.id == pip->shader->slot.id)); + SOKOL_ASSERT(_sg.d3d11.ctx); + SOKOL_ASSERT(_sg.d3d11.in_pass); + SOKOL_ASSERT(pip->d3d11.rs && pip->d3d11.bs && pip->d3d11.dss && pip->d3d11.il); + + _sg.d3d11.cur_pipeline = pip; + _sg.d3d11.cur_pipeline_id.id = pip->slot.id; + _sg.d3d11.use_indexed_draw = (pip->d3d11.index_format != DXGI_FORMAT_UNKNOWN); + + _sg_d3d11_RSSetState(_sg.d3d11.ctx, pip->d3d11.rs); + _sg_d3d11_OMSetDepthStencilState(_sg.d3d11.ctx, pip->d3d11.dss, pip->d3d11.stencil_ref); + _sg_d3d11_OMSetBlendState(_sg.d3d11.ctx, pip->d3d11.bs, &pip->cmn.blend_color.r, 0xFFFFFFFF); + _sg_d3d11_IASetPrimitiveTopology(_sg.d3d11.ctx, pip->d3d11.topology); + _sg_d3d11_IASetInputLayout(_sg.d3d11.ctx, pip->d3d11.il); + _sg_d3d11_VSSetShader(_sg.d3d11.ctx, pip->shader->d3d11.vs, NULL, 0); + _sg_d3d11_VSSetConstantBuffers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_UBS, pip->shader->d3d11.stage[SG_SHADERSTAGE_VS].cbufs); + _sg_d3d11_PSSetShader(_sg.d3d11.ctx, pip->shader->d3d11.fs, NULL, 0); + _sg_d3d11_PSSetConstantBuffers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_UBS, pip->shader->d3d11.stage[SG_SHADERSTAGE_FS].cbufs); +} + +_SOKOL_PRIVATE void _sg_d3d11_apply_bindings( + _sg_pipeline_t* pip, + _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, + _sg_buffer_t* ib, int ib_offset, + _sg_image_t** vs_imgs, int num_vs_imgs, + _sg_image_t** fs_imgs, int num_fs_imgs) +{ + SOKOL_ASSERT(pip); + SOKOL_ASSERT(_sg.d3d11.ctx); + SOKOL_ASSERT(_sg.d3d11.in_pass); + + /* gather all the D3D11 resources into arrays */ + ID3D11Buffer* d3d11_ib = ib ? ib->d3d11.buf : 0; + ID3D11Buffer* d3d11_vbs[SG_MAX_SHADERSTAGE_BUFFERS]; + UINT d3d11_vb_offsets[SG_MAX_SHADERSTAGE_BUFFERS]; + ID3D11ShaderResourceView* d3d11_vs_srvs[SG_MAX_SHADERSTAGE_IMAGES]; + ID3D11SamplerState* d3d11_vs_smps[SG_MAX_SHADERSTAGE_IMAGES]; + ID3D11ShaderResourceView* d3d11_fs_srvs[SG_MAX_SHADERSTAGE_IMAGES]; + ID3D11SamplerState* d3d11_fs_smps[SG_MAX_SHADERSTAGE_IMAGES]; + int i; + for (i = 0; i < num_vbs; i++) { + SOKOL_ASSERT(vbs[i]->d3d11.buf); + d3d11_vbs[i] = vbs[i]->d3d11.buf; + d3d11_vb_offsets[i] = (UINT)vb_offsets[i]; + } + for (; i < SG_MAX_SHADERSTAGE_BUFFERS; i++) { + d3d11_vbs[i] = 0; + d3d11_vb_offsets[i] = 0; + } + for (i = 0; i < num_vs_imgs; i++) { + SOKOL_ASSERT(vs_imgs[i]->d3d11.srv); + SOKOL_ASSERT(vs_imgs[i]->d3d11.smp); + d3d11_vs_srvs[i] = vs_imgs[i]->d3d11.srv; + d3d11_vs_smps[i] = vs_imgs[i]->d3d11.smp; + } + for (; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { + d3d11_vs_srvs[i] = 0; + d3d11_vs_smps[i] = 0; + } + for (i = 0; i < num_fs_imgs; i++) { + SOKOL_ASSERT(fs_imgs[i]->d3d11.srv); + SOKOL_ASSERT(fs_imgs[i]->d3d11.smp); + d3d11_fs_srvs[i] = fs_imgs[i]->d3d11.srv; + d3d11_fs_smps[i] = fs_imgs[i]->d3d11.smp; + } + for (; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { + d3d11_fs_srvs[i] = 0; + d3d11_fs_smps[i] = 0; + } + + _sg_d3d11_IASetVertexBuffers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_BUFFERS, d3d11_vbs, pip->d3d11.vb_strides, d3d11_vb_offsets); + _sg_d3d11_IASetIndexBuffer(_sg.d3d11.ctx, d3d11_ib, pip->d3d11.index_format, (UINT)ib_offset); + _sg_d3d11_VSSetShaderResources(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, d3d11_vs_srvs); + _sg_d3d11_VSSetSamplers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, d3d11_vs_smps); + _sg_d3d11_PSSetShaderResources(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, d3d11_fs_srvs); + _sg_d3d11_PSSetSamplers(_sg.d3d11.ctx, 0, SG_MAX_SHADERSTAGE_IMAGES, d3d11_fs_smps); +} + +_SOKOL_PRIVATE void _sg_d3d11_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { + SOKOL_ASSERT(_sg.d3d11.ctx && _sg.d3d11.in_pass); + SOKOL_ASSERT(_sg.d3d11.cur_pipeline && _sg.d3d11.cur_pipeline->slot.id == _sg.d3d11.cur_pipeline_id.id); + SOKOL_ASSERT(_sg.d3d11.cur_pipeline->shader && _sg.d3d11.cur_pipeline->shader->slot.id == _sg.d3d11.cur_pipeline->cmn.shader_id.id); + SOKOL_ASSERT(ub_index < _sg.d3d11.cur_pipeline->shader->cmn.stage[stage_index].num_uniform_blocks); + SOKOL_ASSERT(data->size == _sg.d3d11.cur_pipeline->shader->cmn.stage[stage_index].uniform_blocks[ub_index].size); + ID3D11Buffer* cb = _sg.d3d11.cur_pipeline->shader->d3d11.stage[stage_index].cbufs[ub_index]; + SOKOL_ASSERT(cb); + _sg_d3d11_UpdateSubresource(_sg.d3d11.ctx, (ID3D11Resource*)cb, 0, NULL, data->ptr, 0, 0); +} + +_SOKOL_PRIVATE void _sg_d3d11_draw(int base_element, int num_elements, int num_instances) { + SOKOL_ASSERT(_sg.d3d11.in_pass); + if (_sg.d3d11.use_indexed_draw) { + if (1 == num_instances) { + _sg_d3d11_DrawIndexed(_sg.d3d11.ctx, (UINT)num_elements, (UINT)base_element, 0); + } + else { + _sg_d3d11_DrawIndexedInstanced(_sg.d3d11.ctx, (UINT)num_elements, (UINT)num_instances, (UINT)base_element, 0, 0); + } + } + else { + if (1 == num_instances) { + _sg_d3d11_Draw(_sg.d3d11.ctx, (UINT)num_elements, (UINT)base_element); + } + else { + _sg_d3d11_DrawInstanced(_sg.d3d11.ctx, (UINT)num_elements, (UINT)num_instances, (UINT)base_element, 0); + } + } +} + +_SOKOL_PRIVATE void _sg_d3d11_commit(void) { + SOKOL_ASSERT(!_sg.d3d11.in_pass); +} + +_SOKOL_PRIVATE void _sg_d3d11_update_buffer(_sg_buffer_t* buf, const sg_range* data) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + SOKOL_ASSERT(_sg.d3d11.ctx); + SOKOL_ASSERT(buf->d3d11.buf); + D3D11_MAPPED_SUBRESOURCE d3d11_msr; + HRESULT hr = _sg_d3d11_Map(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0, D3D11_MAP_WRITE_DISCARD, 0, &d3d11_msr); + _SOKOL_UNUSED(hr); + SOKOL_ASSERT(SUCCEEDED(hr)); + memcpy(d3d11_msr.pData, data->ptr, data->size); + _sg_d3d11_Unmap(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0); +} + +_SOKOL_PRIVATE int _sg_d3d11_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + SOKOL_ASSERT(_sg.d3d11.ctx); + SOKOL_ASSERT(buf->d3d11.buf); + D3D11_MAP map_type = new_frame ? D3D11_MAP_WRITE_DISCARD : D3D11_MAP_WRITE_NO_OVERWRITE; + D3D11_MAPPED_SUBRESOURCE d3d11_msr; + HRESULT hr = _sg_d3d11_Map(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0, map_type, 0, &d3d11_msr); + _SOKOL_UNUSED(hr); + SOKOL_ASSERT(SUCCEEDED(hr)); + uint8_t* dst_ptr = (uint8_t*)d3d11_msr.pData + buf->cmn.append_pos; + memcpy(dst_ptr, data->ptr, data->size); + _sg_d3d11_Unmap(_sg.d3d11.ctx, (ID3D11Resource*)buf->d3d11.buf, 0); + /* NOTE: this is a requirement from WebGPU, but we want identical behaviour across all backend */ + return _sg_roundup((int)data->size, 4); +} + +_SOKOL_PRIVATE void _sg_d3d11_update_image(_sg_image_t* img, const sg_image_data* data) { + SOKOL_ASSERT(img && data); + SOKOL_ASSERT(_sg.d3d11.ctx); + SOKOL_ASSERT(img->d3d11.tex2d || img->d3d11.tex3d); + ID3D11Resource* d3d11_res = 0; + if (img->d3d11.tex3d) { + d3d11_res = (ID3D11Resource*) img->d3d11.tex3d; + } + else { + d3d11_res = (ID3D11Resource*) img->d3d11.tex2d; + } + SOKOL_ASSERT(d3d11_res); + const int num_faces = (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6:1; + const int num_slices = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? img->cmn.num_slices:1; + UINT subres_index = 0; + HRESULT hr; + _SOKOL_UNUSED(hr); + D3D11_MAPPED_SUBRESOURCE d3d11_msr; + for (int face_index = 0; face_index < num_faces; face_index++) { + for (int slice_index = 0; slice_index < num_slices; slice_index++) { + for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++, subres_index++) { + SOKOL_ASSERT(subres_index < (SG_MAX_MIPMAPS * SG_MAX_TEXTUREARRAY_LAYERS)); + const int mip_width = ((img->cmn.width>>mip_index)>0) ? img->cmn.width>>mip_index : 1; + const int mip_height = ((img->cmn.height>>mip_index)>0) ? img->cmn.height>>mip_index : 1; + const int src_pitch = _sg_row_pitch(img->cmn.pixel_format, mip_width, 1); + const sg_range* subimg_data = &(data->subimage[face_index][mip_index]); + const size_t slice_size = subimg_data->size / (size_t)num_slices; + const size_t slice_offset = slice_size * (size_t)slice_index; + const uint8_t* slice_ptr = ((const uint8_t*)subimg_data->ptr) + slice_offset; + hr = _sg_d3d11_Map(_sg.d3d11.ctx, d3d11_res, subres_index, D3D11_MAP_WRITE_DISCARD, 0, &d3d11_msr); + SOKOL_ASSERT(SUCCEEDED(hr)); + /* FIXME: need to handle difference in depth-pitch for 3D textures as well! */ + if (src_pitch == (int)d3d11_msr.RowPitch) { + memcpy(d3d11_msr.pData, slice_ptr, slice_size); + } + else { + SOKOL_ASSERT(src_pitch < (int)d3d11_msr.RowPitch); + const uint8_t* src_ptr = slice_ptr; + uint8_t* dst_ptr = (uint8_t*) d3d11_msr.pData; + for (int row_index = 0; row_index < mip_height; row_index++) { + memcpy(dst_ptr, src_ptr, (size_t)src_pitch); + src_ptr += src_pitch; + dst_ptr += d3d11_msr.RowPitch; + } + } + _sg_d3d11_Unmap(_sg.d3d11.ctx, d3d11_res, subres_index); + } + } + } +} + +/*== METAL BACKEND IMPLEMENTATION ============================================*/ +#elif defined(SOKOL_METAL) + +#if __has_feature(objc_arc) +#define _SG_OBJC_RETAIN(obj) { } +#define _SG_OBJC_RELEASE(obj) { obj = nil; } +#define _SG_OBJC_RELEASE_WITH_NULL(obj) { obj = [NSNull null]; } +#else +#define _SG_OBJC_RETAIN(obj) { [obj retain]; } +#define _SG_OBJC_RELEASE(obj) { [obj release]; obj = nil; } +#define _SG_OBJC_RELEASE_WITH_NULL(obj) { [obj release]; obj = [NSNull null]; } +#endif + +/*-- enum translation functions ----------------------------------------------*/ +_SOKOL_PRIVATE MTLLoadAction _sg_mtl_load_action(sg_action a) { + switch (a) { + case SG_ACTION_CLEAR: return MTLLoadActionClear; + case SG_ACTION_LOAD: return MTLLoadActionLoad; + case SG_ACTION_DONTCARE: return MTLLoadActionDontCare; + default: SOKOL_UNREACHABLE; return (MTLLoadAction)0; + } +} + +_SOKOL_PRIVATE MTLResourceOptions _sg_mtl_buffer_resource_options(sg_usage usg) { + switch (usg) { + case SG_USAGE_IMMUTABLE: + return MTLResourceStorageModeShared; + case SG_USAGE_DYNAMIC: + case SG_USAGE_STREAM: + #if defined(_SG_TARGET_MACOS) + return MTLCPUCacheModeWriteCombined|MTLResourceStorageModeManaged; + #else + return MTLCPUCacheModeWriteCombined; + #endif + default: + SOKOL_UNREACHABLE; + return 0; + } +} + +_SOKOL_PRIVATE MTLVertexStepFunction _sg_mtl_step_function(sg_vertex_step step) { + switch (step) { + case SG_VERTEXSTEP_PER_VERTEX: return MTLVertexStepFunctionPerVertex; + case SG_VERTEXSTEP_PER_INSTANCE: return MTLVertexStepFunctionPerInstance; + default: SOKOL_UNREACHABLE; return (MTLVertexStepFunction)0; + } +} + +_SOKOL_PRIVATE MTLVertexFormat _sg_mtl_vertex_format(sg_vertex_format fmt) { + switch (fmt) { + case SG_VERTEXFORMAT_FLOAT: return MTLVertexFormatFloat; + case SG_VERTEXFORMAT_FLOAT2: return MTLVertexFormatFloat2; + case SG_VERTEXFORMAT_FLOAT3: return MTLVertexFormatFloat3; + case SG_VERTEXFORMAT_FLOAT4: return MTLVertexFormatFloat4; + case SG_VERTEXFORMAT_BYTE4: return MTLVertexFormatChar4; + case SG_VERTEXFORMAT_BYTE4N: return MTLVertexFormatChar4Normalized; + case SG_VERTEXFORMAT_UBYTE4: return MTLVertexFormatUChar4; + case SG_VERTEXFORMAT_UBYTE4N: return MTLVertexFormatUChar4Normalized; + case SG_VERTEXFORMAT_SHORT2: return MTLVertexFormatShort2; + case SG_VERTEXFORMAT_SHORT2N: return MTLVertexFormatShort2Normalized; + case SG_VERTEXFORMAT_USHORT2N: return MTLVertexFormatUShort2Normalized; + case SG_VERTEXFORMAT_SHORT4: return MTLVertexFormatShort4; + case SG_VERTEXFORMAT_SHORT4N: return MTLVertexFormatShort4Normalized; + case SG_VERTEXFORMAT_USHORT4N: return MTLVertexFormatUShort4Normalized; + case SG_VERTEXFORMAT_UINT10_N2: return MTLVertexFormatUInt1010102Normalized; + default: SOKOL_UNREACHABLE; return (MTLVertexFormat)0; + } +} + +_SOKOL_PRIVATE MTLPrimitiveType _sg_mtl_primitive_type(sg_primitive_type t) { + switch (t) { + case SG_PRIMITIVETYPE_POINTS: return MTLPrimitiveTypePoint; + case SG_PRIMITIVETYPE_LINES: return MTLPrimitiveTypeLine; + case SG_PRIMITIVETYPE_LINE_STRIP: return MTLPrimitiveTypeLineStrip; + case SG_PRIMITIVETYPE_TRIANGLES: return MTLPrimitiveTypeTriangle; + case SG_PRIMITIVETYPE_TRIANGLE_STRIP: return MTLPrimitiveTypeTriangleStrip; + default: SOKOL_UNREACHABLE; return (MTLPrimitiveType)0; + } +} + +_SOKOL_PRIVATE MTLPixelFormat _sg_mtl_pixel_format(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_R8: return MTLPixelFormatR8Unorm; + case SG_PIXELFORMAT_R8SN: return MTLPixelFormatR8Snorm; + case SG_PIXELFORMAT_R8UI: return MTLPixelFormatR8Uint; + case SG_PIXELFORMAT_R8SI: return MTLPixelFormatR8Sint; + case SG_PIXELFORMAT_R16: return MTLPixelFormatR16Unorm; + case SG_PIXELFORMAT_R16SN: return MTLPixelFormatR16Snorm; + case SG_PIXELFORMAT_R16UI: return MTLPixelFormatR16Uint; + case SG_PIXELFORMAT_R16SI: return MTLPixelFormatR16Sint; + case SG_PIXELFORMAT_R16F: return MTLPixelFormatR16Float; + case SG_PIXELFORMAT_RG8: return MTLPixelFormatRG8Unorm; + case SG_PIXELFORMAT_RG8SN: return MTLPixelFormatRG8Snorm; + case SG_PIXELFORMAT_RG8UI: return MTLPixelFormatRG8Uint; + case SG_PIXELFORMAT_RG8SI: return MTLPixelFormatRG8Sint; + case SG_PIXELFORMAT_R32UI: return MTLPixelFormatR32Uint; + case SG_PIXELFORMAT_R32SI: return MTLPixelFormatR32Sint; + case SG_PIXELFORMAT_R32F: return MTLPixelFormatR32Float; + case SG_PIXELFORMAT_RG16: return MTLPixelFormatRG16Unorm; + case SG_PIXELFORMAT_RG16SN: return MTLPixelFormatRG16Snorm; + case SG_PIXELFORMAT_RG16UI: return MTLPixelFormatRG16Uint; + case SG_PIXELFORMAT_RG16SI: return MTLPixelFormatRG16Sint; + case SG_PIXELFORMAT_RG16F: return MTLPixelFormatRG16Float; + case SG_PIXELFORMAT_RGBA8: return MTLPixelFormatRGBA8Unorm; + case SG_PIXELFORMAT_RGBA8SN: return MTLPixelFormatRGBA8Snorm; + case SG_PIXELFORMAT_RGBA8UI: return MTLPixelFormatRGBA8Uint; + case SG_PIXELFORMAT_RGBA8SI: return MTLPixelFormatRGBA8Sint; + case SG_PIXELFORMAT_BGRA8: return MTLPixelFormatBGRA8Unorm; + case SG_PIXELFORMAT_RGB10A2: return MTLPixelFormatRGB10A2Unorm; + case SG_PIXELFORMAT_RG11B10F: return MTLPixelFormatRG11B10Float; + case SG_PIXELFORMAT_RG32UI: return MTLPixelFormatRG32Uint; + case SG_PIXELFORMAT_RG32SI: return MTLPixelFormatRG32Sint; + case SG_PIXELFORMAT_RG32F: return MTLPixelFormatRG32Float; + case SG_PIXELFORMAT_RGBA16: return MTLPixelFormatRGBA16Unorm; + case SG_PIXELFORMAT_RGBA16SN: return MTLPixelFormatRGBA16Snorm; + case SG_PIXELFORMAT_RGBA16UI: return MTLPixelFormatRGBA16Uint; + case SG_PIXELFORMAT_RGBA16SI: return MTLPixelFormatRGBA16Sint; + case SG_PIXELFORMAT_RGBA16F: return MTLPixelFormatRGBA16Float; + case SG_PIXELFORMAT_RGBA32UI: return MTLPixelFormatRGBA32Uint; + case SG_PIXELFORMAT_RGBA32SI: return MTLPixelFormatRGBA32Sint; + case SG_PIXELFORMAT_RGBA32F: return MTLPixelFormatRGBA32Float; + case SG_PIXELFORMAT_DEPTH: return MTLPixelFormatDepth32Float; + case SG_PIXELFORMAT_DEPTH_STENCIL: return MTLPixelFormatDepth32Float_Stencil8; + #if defined(_SG_TARGET_MACOS) + case SG_PIXELFORMAT_BC1_RGBA: return MTLPixelFormatBC1_RGBA; + case SG_PIXELFORMAT_BC2_RGBA: return MTLPixelFormatBC2_RGBA; + case SG_PIXELFORMAT_BC3_RGBA: return MTLPixelFormatBC3_RGBA; + case SG_PIXELFORMAT_BC4_R: return MTLPixelFormatBC4_RUnorm; + case SG_PIXELFORMAT_BC4_RSN: return MTLPixelFormatBC4_RSnorm; + case SG_PIXELFORMAT_BC5_RG: return MTLPixelFormatBC5_RGUnorm; + case SG_PIXELFORMAT_BC5_RGSN: return MTLPixelFormatBC5_RGSnorm; + case SG_PIXELFORMAT_BC6H_RGBF: return MTLPixelFormatBC6H_RGBFloat; + case SG_PIXELFORMAT_BC6H_RGBUF: return MTLPixelFormatBC6H_RGBUfloat; + case SG_PIXELFORMAT_BC7_RGBA: return MTLPixelFormatBC7_RGBAUnorm; + #else + case SG_PIXELFORMAT_PVRTC_RGB_2BPP: return MTLPixelFormatPVRTC_RGB_2BPP; + case SG_PIXELFORMAT_PVRTC_RGB_4BPP: return MTLPixelFormatPVRTC_RGB_4BPP; + case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: return MTLPixelFormatPVRTC_RGBA_2BPP; + case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: return MTLPixelFormatPVRTC_RGBA_4BPP; + case SG_PIXELFORMAT_ETC2_RGB8: return MTLPixelFormatETC2_RGB8; + case SG_PIXELFORMAT_ETC2_RGB8A1: return MTLPixelFormatETC2_RGB8A1; + case SG_PIXELFORMAT_ETC2_RGBA8: return MTLPixelFormatEAC_RGBA8; + case SG_PIXELFORMAT_ETC2_RG11: return MTLPixelFormatEAC_RG11Unorm; + case SG_PIXELFORMAT_ETC2_RG11SN: return MTLPixelFormatEAC_RG11Snorm; + #endif + default: return MTLPixelFormatInvalid; + } +} + +_SOKOL_PRIVATE MTLColorWriteMask _sg_mtl_color_write_mask(sg_color_mask m) { + MTLColorWriteMask mtl_mask = MTLColorWriteMaskNone; + if (m & SG_COLORMASK_R) { + mtl_mask |= MTLColorWriteMaskRed; + } + if (m & SG_COLORMASK_G) { + mtl_mask |= MTLColorWriteMaskGreen; + } + if (m & SG_COLORMASK_B) { + mtl_mask |= MTLColorWriteMaskBlue; + } + if (m & SG_COLORMASK_A) { + mtl_mask |= MTLColorWriteMaskAlpha; + } + return mtl_mask; +} + +_SOKOL_PRIVATE MTLBlendOperation _sg_mtl_blend_op(sg_blend_op op) { + switch (op) { + case SG_BLENDOP_ADD: return MTLBlendOperationAdd; + case SG_BLENDOP_SUBTRACT: return MTLBlendOperationSubtract; + case SG_BLENDOP_REVERSE_SUBTRACT: return MTLBlendOperationReverseSubtract; + default: SOKOL_UNREACHABLE; return (MTLBlendOperation)0; + } +} + +_SOKOL_PRIVATE MTLBlendFactor _sg_mtl_blend_factor(sg_blend_factor f) { + switch (f) { + case SG_BLENDFACTOR_ZERO: return MTLBlendFactorZero; + case SG_BLENDFACTOR_ONE: return MTLBlendFactorOne; + case SG_BLENDFACTOR_SRC_COLOR: return MTLBlendFactorSourceColor; + case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return MTLBlendFactorOneMinusSourceColor; + case SG_BLENDFACTOR_SRC_ALPHA: return MTLBlendFactorSourceAlpha; + case SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return MTLBlendFactorOneMinusSourceAlpha; + case SG_BLENDFACTOR_DST_COLOR: return MTLBlendFactorDestinationColor; + case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return MTLBlendFactorOneMinusDestinationColor; + case SG_BLENDFACTOR_DST_ALPHA: return MTLBlendFactorDestinationAlpha; + case SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return MTLBlendFactorOneMinusDestinationAlpha; + case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return MTLBlendFactorSourceAlphaSaturated; + case SG_BLENDFACTOR_BLEND_COLOR: return MTLBlendFactorBlendColor; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return MTLBlendFactorOneMinusBlendColor; + case SG_BLENDFACTOR_BLEND_ALPHA: return MTLBlendFactorBlendAlpha; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return MTLBlendFactorOneMinusBlendAlpha; + default: SOKOL_UNREACHABLE; return (MTLBlendFactor)0; + } +} + +_SOKOL_PRIVATE MTLCompareFunction _sg_mtl_compare_func(sg_compare_func f) { + switch (f) { + case SG_COMPAREFUNC_NEVER: return MTLCompareFunctionNever; + case SG_COMPAREFUNC_LESS: return MTLCompareFunctionLess; + case SG_COMPAREFUNC_EQUAL: return MTLCompareFunctionEqual; + case SG_COMPAREFUNC_LESS_EQUAL: return MTLCompareFunctionLessEqual; + case SG_COMPAREFUNC_GREATER: return MTLCompareFunctionGreater; + case SG_COMPAREFUNC_NOT_EQUAL: return MTLCompareFunctionNotEqual; + case SG_COMPAREFUNC_GREATER_EQUAL: return MTLCompareFunctionGreaterEqual; + case SG_COMPAREFUNC_ALWAYS: return MTLCompareFunctionAlways; + default: SOKOL_UNREACHABLE; return (MTLCompareFunction)0; + } +} + +_SOKOL_PRIVATE MTLStencilOperation _sg_mtl_stencil_op(sg_stencil_op op) { + switch (op) { + case SG_STENCILOP_KEEP: return MTLStencilOperationKeep; + case SG_STENCILOP_ZERO: return MTLStencilOperationZero; + case SG_STENCILOP_REPLACE: return MTLStencilOperationReplace; + case SG_STENCILOP_INCR_CLAMP: return MTLStencilOperationIncrementClamp; + case SG_STENCILOP_DECR_CLAMP: return MTLStencilOperationDecrementClamp; + case SG_STENCILOP_INVERT: return MTLStencilOperationInvert; + case SG_STENCILOP_INCR_WRAP: return MTLStencilOperationIncrementWrap; + case SG_STENCILOP_DECR_WRAP: return MTLStencilOperationDecrementWrap; + default: SOKOL_UNREACHABLE; return (MTLStencilOperation)0; + } +} + +_SOKOL_PRIVATE MTLCullMode _sg_mtl_cull_mode(sg_cull_mode m) { + switch (m) { + case SG_CULLMODE_NONE: return MTLCullModeNone; + case SG_CULLMODE_FRONT: return MTLCullModeFront; + case SG_CULLMODE_BACK: return MTLCullModeBack; + default: SOKOL_UNREACHABLE; return (MTLCullMode)0; + } +} + +_SOKOL_PRIVATE MTLWinding _sg_mtl_winding(sg_face_winding w) { + switch (w) { + case SG_FACEWINDING_CW: return MTLWindingClockwise; + case SG_FACEWINDING_CCW: return MTLWindingCounterClockwise; + default: SOKOL_UNREACHABLE; return (MTLWinding)0; + } +} + +_SOKOL_PRIVATE MTLIndexType _sg_mtl_index_type(sg_index_type t) { + switch (t) { + case SG_INDEXTYPE_UINT16: return MTLIndexTypeUInt16; + case SG_INDEXTYPE_UINT32: return MTLIndexTypeUInt32; + default: SOKOL_UNREACHABLE; return (MTLIndexType)0; + } +} + +_SOKOL_PRIVATE int _sg_mtl_index_size(sg_index_type t) { + switch (t) { + case SG_INDEXTYPE_NONE: return 0; + case SG_INDEXTYPE_UINT16: return 2; + case SG_INDEXTYPE_UINT32: return 4; + default: SOKOL_UNREACHABLE; return 0; + } +} + +_SOKOL_PRIVATE MTLTextureType _sg_mtl_texture_type(sg_image_type t) { + switch (t) { + case SG_IMAGETYPE_2D: return MTLTextureType2D; + case SG_IMAGETYPE_CUBE: return MTLTextureTypeCube; + case SG_IMAGETYPE_3D: return MTLTextureType3D; + case SG_IMAGETYPE_ARRAY: return MTLTextureType2DArray; + default: SOKOL_UNREACHABLE; return (MTLTextureType)0; + } +} + +_SOKOL_PRIVATE bool _sg_mtl_is_pvrtc(sg_pixel_format fmt) { + switch (fmt) { + case SG_PIXELFORMAT_PVRTC_RGB_2BPP: + case SG_PIXELFORMAT_PVRTC_RGB_4BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: + return true; + default: + return false; + } +} + +_SOKOL_PRIVATE MTLSamplerAddressMode _sg_mtl_address_mode(sg_wrap w) { + switch (w) { + case SG_WRAP_REPEAT: return MTLSamplerAddressModeRepeat; + case SG_WRAP_CLAMP_TO_EDGE: return MTLSamplerAddressModeClampToEdge; + #if defined(_SG_TARGET_MACOS) + case SG_WRAP_CLAMP_TO_BORDER: return MTLSamplerAddressModeClampToBorderColor; + #else + /* clamp-to-border not supported on iOS, fall back to clamp-to-edge */ + case SG_WRAP_CLAMP_TO_BORDER: return MTLSamplerAddressModeClampToEdge; + #endif + case SG_WRAP_MIRRORED_REPEAT: return MTLSamplerAddressModeMirrorRepeat; + default: SOKOL_UNREACHABLE; return (MTLSamplerAddressMode)0; + } +} + +#if defined(_SG_TARGET_MACOS) +_SOKOL_PRIVATE MTLSamplerBorderColor _sg_mtl_border_color(sg_border_color c) { + switch (c) { + case SG_BORDERCOLOR_TRANSPARENT_BLACK: return MTLSamplerBorderColorTransparentBlack; + case SG_BORDERCOLOR_OPAQUE_BLACK: return MTLSamplerBorderColorOpaqueBlack; + case SG_BORDERCOLOR_OPAQUE_WHITE: return MTLSamplerBorderColorOpaqueWhite; + default: SOKOL_UNREACHABLE; return (MTLSamplerBorderColor)0; + } +} +#endif + +_SOKOL_PRIVATE MTLSamplerMinMagFilter _sg_mtl_minmag_filter(sg_filter f) { + switch (f) { + case SG_FILTER_NEAREST: + case SG_FILTER_NEAREST_MIPMAP_NEAREST: + case SG_FILTER_NEAREST_MIPMAP_LINEAR: + return MTLSamplerMinMagFilterNearest; + case SG_FILTER_LINEAR: + case SG_FILTER_LINEAR_MIPMAP_NEAREST: + case SG_FILTER_LINEAR_MIPMAP_LINEAR: + return MTLSamplerMinMagFilterLinear; + default: + SOKOL_UNREACHABLE; return (MTLSamplerMinMagFilter)0; + } +} + +_SOKOL_PRIVATE MTLSamplerMipFilter _sg_mtl_mip_filter(sg_filter f) { + switch (f) { + case SG_FILTER_NEAREST: + case SG_FILTER_LINEAR: + return MTLSamplerMipFilterNotMipmapped; + case SG_FILTER_NEAREST_MIPMAP_NEAREST: + case SG_FILTER_LINEAR_MIPMAP_NEAREST: + return MTLSamplerMipFilterNearest; + case SG_FILTER_NEAREST_MIPMAP_LINEAR: + case SG_FILTER_LINEAR_MIPMAP_LINEAR: + return MTLSamplerMipFilterLinear; + default: + SOKOL_UNREACHABLE; return (MTLSamplerMipFilter)0; + } +} + +/*-- a pool for all Metal resource objects, with deferred release queue -------*/ + +_SOKOL_PRIVATE void _sg_mtl_init_pool(const sg_desc* desc) { + _sg.mtl.idpool.num_slots = 2 * + ( + 2 * desc->buffer_pool_size + + 5 * desc->image_pool_size + + 4 * desc->shader_pool_size + + 2 * desc->pipeline_pool_size + + desc->pass_pool_size + ); + _sg.mtl.idpool.pool = [NSMutableArray arrayWithCapacity:(NSUInteger)_sg.mtl.idpool.num_slots]; + _SG_OBJC_RETAIN(_sg.mtl.idpool.pool); + NSNull* null = [NSNull null]; + for (int i = 0; i < _sg.mtl.idpool.num_slots; i++) { + [_sg.mtl.idpool.pool addObject:null]; + } + SOKOL_ASSERT([_sg.mtl.idpool.pool count] == (NSUInteger)_sg.mtl.idpool.num_slots); + /* a queue of currently free slot indices */ + _sg.mtl.idpool.free_queue_top = 0; + _sg.mtl.idpool.free_queue = (int*)SOKOL_MALLOC((size_t)_sg.mtl.idpool.num_slots * sizeof(int)); + /* pool slot 0 is reserved! */ + for (int i = _sg.mtl.idpool.num_slots-1; i >= 1; i--) { + _sg.mtl.idpool.free_queue[_sg.mtl.idpool.free_queue_top++] = i; + } + /* a circular queue which holds release items (frame index + when a resource is to be released, and the resource's + pool index + */ + _sg.mtl.idpool.release_queue_front = 0; + _sg.mtl.idpool.release_queue_back = 0; + _sg.mtl.idpool.release_queue = (_sg_mtl_release_item_t*)SOKOL_MALLOC((size_t)_sg.mtl.idpool.num_slots * sizeof(_sg_mtl_release_item_t)); + for (int i = 0; i < _sg.mtl.idpool.num_slots; i++) { + _sg.mtl.idpool.release_queue[i].frame_index = 0; + _sg.mtl.idpool.release_queue[i].slot_index = _SG_MTL_INVALID_SLOT_INDEX; + } +} + +_SOKOL_PRIVATE void _sg_mtl_destroy_pool(void) { + SOKOL_FREE(_sg.mtl.idpool.release_queue); _sg.mtl.idpool.release_queue = 0; + SOKOL_FREE(_sg.mtl.idpool.free_queue); _sg.mtl.idpool.free_queue = 0; + _SG_OBJC_RELEASE(_sg.mtl.idpool.pool); +} + +/* get a new free resource pool slot */ +_SOKOL_PRIVATE int _sg_mtl_alloc_pool_slot(void) { + SOKOL_ASSERT(_sg.mtl.idpool.free_queue_top > 0); + const int slot_index = _sg.mtl.idpool.free_queue[--_sg.mtl.idpool.free_queue_top]; + SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots)); + return slot_index; +} + +/* put a free resource pool slot back into the free-queue */ +_SOKOL_PRIVATE void _sg_mtl_free_pool_slot(int slot_index) { + SOKOL_ASSERT(_sg.mtl.idpool.free_queue_top < _sg.mtl.idpool.num_slots); + SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots)); + _sg.mtl.idpool.free_queue[_sg.mtl.idpool.free_queue_top++] = slot_index; +} + +/* add an MTLResource to the pool, return pool index or 0 if input was 'nil' */ +_SOKOL_PRIVATE int _sg_mtl_add_resource(id res) { + if (nil == res) { + return _SG_MTL_INVALID_SLOT_INDEX; + } + const int slot_index = _sg_mtl_alloc_pool_slot(); + SOKOL_ASSERT([NSNull null] == _sg.mtl.idpool.pool[(NSUInteger)slot_index]); + _sg.mtl.idpool.pool[(NSUInteger)slot_index] = res; + return slot_index; +} + +/* mark an MTLResource for release, this will put the resource into the + deferred-release queue, and the resource will then be released N frames later, + the special pool index 0 will be ignored (this means that a nil + value was provided to _sg_mtl_add_resource() +*/ +_SOKOL_PRIVATE void _sg_mtl_release_resource(uint32_t frame_index, int slot_index) { + if (slot_index == _SG_MTL_INVALID_SLOT_INDEX) { + return; + } + SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots)); + SOKOL_ASSERT([NSNull null] != _sg.mtl.idpool.pool[(NSUInteger)slot_index]); + int release_index = _sg.mtl.idpool.release_queue_front++; + if (_sg.mtl.idpool.release_queue_front >= _sg.mtl.idpool.num_slots) { + /* wrap-around */ + _sg.mtl.idpool.release_queue_front = 0; + } + /* release queue full? */ + SOKOL_ASSERT(_sg.mtl.idpool.release_queue_front != _sg.mtl.idpool.release_queue_back); + SOKOL_ASSERT(0 == _sg.mtl.idpool.release_queue[release_index].frame_index); + const uint32_t safe_to_release_frame_index = frame_index + SG_NUM_INFLIGHT_FRAMES + 1; + _sg.mtl.idpool.release_queue[release_index].frame_index = safe_to_release_frame_index; + _sg.mtl.idpool.release_queue[release_index].slot_index = slot_index; +} + +/* run garbage-collection pass on all resources in the release-queue */ +_SOKOL_PRIVATE void _sg_mtl_garbage_collect(uint32_t frame_index) { + while (_sg.mtl.idpool.release_queue_back != _sg.mtl.idpool.release_queue_front) { + if (frame_index < _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].frame_index) { + /* don't need to check further, release-items past this are too young */ + break; + } + /* safe to release this resource */ + const int slot_index = _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].slot_index; + SOKOL_ASSERT((slot_index > 0) && (slot_index < _sg.mtl.idpool.num_slots)); + SOKOL_ASSERT(_sg.mtl.idpool.pool[(NSUInteger)slot_index] != [NSNull null]); + _SG_OBJC_RELEASE_WITH_NULL(_sg.mtl.idpool.pool[(NSUInteger)slot_index]); + /* put the now free pool index back on the free queue */ + _sg_mtl_free_pool_slot(slot_index); + /* reset the release queue slot and advance the back index */ + _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].frame_index = 0; + _sg.mtl.idpool.release_queue[_sg.mtl.idpool.release_queue_back].slot_index = _SG_MTL_INVALID_SLOT_INDEX; + _sg.mtl.idpool.release_queue_back++; + if (_sg.mtl.idpool.release_queue_back >= _sg.mtl.idpool.num_slots) { + /* wrap-around */ + _sg.mtl.idpool.release_queue_back = 0; + } + } +} + +_SOKOL_PRIVATE id _sg_mtl_id(int slot_index) { + return _sg.mtl.idpool.pool[(NSUInteger)slot_index]; +} + +_SOKOL_PRIVATE void _sg_mtl_init_sampler_cache(const sg_desc* desc) { + SOKOL_ASSERT(desc->sampler_cache_size > 0); + _sg_smpcache_init(&_sg.mtl.sampler_cache, desc->sampler_cache_size); +} + +/* destroy the sampler cache, and release all sampler objects */ +_SOKOL_PRIVATE void _sg_mtl_destroy_sampler_cache(uint32_t frame_index) { + SOKOL_ASSERT(_sg.mtl.sampler_cache.items); + SOKOL_ASSERT(_sg.mtl.sampler_cache.num_items <= _sg.mtl.sampler_cache.capacity); + for (int i = 0; i < _sg.mtl.sampler_cache.num_items; i++) { + _sg_mtl_release_resource(frame_index, (int)_sg_smpcache_sampler(&_sg.mtl.sampler_cache, i)); + } + _sg_smpcache_discard(&_sg.mtl.sampler_cache); +} + +/* + create and add an MTLSamplerStateObject and return its resource pool index, + reuse identical sampler state if one exists +*/ +_SOKOL_PRIVATE int _sg_mtl_create_sampler(id mtl_device, const sg_image_desc* img_desc) { + SOKOL_ASSERT(img_desc); + int index = _sg_smpcache_find_item(&_sg.mtl.sampler_cache, img_desc); + if (index >= 0) { + /* reuse existing sampler */ + return (int)_sg_smpcache_sampler(&_sg.mtl.sampler_cache, index); + } + else { + /* create a new Metal sampler state object and add to sampler cache */ + MTLSamplerDescriptor* mtl_desc = [[MTLSamplerDescriptor alloc] init]; + mtl_desc.sAddressMode = _sg_mtl_address_mode(img_desc->wrap_u); + mtl_desc.tAddressMode = _sg_mtl_address_mode(img_desc->wrap_v); + if (SG_IMAGETYPE_3D == img_desc->type) { + mtl_desc.rAddressMode = _sg_mtl_address_mode(img_desc->wrap_w); + } + #if defined(_SG_TARGET_MACOS) + mtl_desc.borderColor = _sg_mtl_border_color(img_desc->border_color); + #endif + mtl_desc.minFilter = _sg_mtl_minmag_filter(img_desc->min_filter); + mtl_desc.magFilter = _sg_mtl_minmag_filter(img_desc->mag_filter); + mtl_desc.mipFilter = _sg_mtl_mip_filter(img_desc->min_filter); + mtl_desc.lodMinClamp = img_desc->min_lod; + mtl_desc.lodMaxClamp = img_desc->max_lod; + mtl_desc.maxAnisotropy = img_desc->max_anisotropy; + mtl_desc.normalizedCoordinates = YES; + id mtl_sampler = [mtl_device newSamplerStateWithDescriptor:mtl_desc]; + _SG_OBJC_RELEASE(mtl_desc); + int sampler_handle = _sg_mtl_add_resource(mtl_sampler); + _sg_smpcache_add_item(&_sg.mtl.sampler_cache, img_desc, (uintptr_t)sampler_handle); + return sampler_handle; + } +} + +_SOKOL_PRIVATE void _sg_mtl_clear_state_cache(void) { + memset(&_sg.mtl.state_cache, 0, sizeof(_sg.mtl.state_cache)); +} + +/* https://developer.apple.com/metal/Metal-Feature-Set-Tables.pdf */ +_SOKOL_PRIVATE void _sg_mtl_init_caps(void) { + #if defined(_SG_TARGET_MACOS) + _sg.backend = SG_BACKEND_METAL_MACOS; + #elif defined(_SG_TARGET_IOS) + #if defined(_SG_TARGET_IOS_SIMULATOR) + _sg.backend = SG_BACKEND_METAL_SIMULATOR; + #else + _sg.backend = SG_BACKEND_METAL_IOS; + #endif + #endif + _sg.features.instancing = true; + _sg.features.origin_top_left = true; + _sg.features.multiple_render_targets = true; + _sg.features.msaa_render_targets = true; + _sg.features.imagetype_3d = true; + _sg.features.imagetype_array = true; + #if defined(_SG_TARGET_MACOS) + _sg.features.image_clamp_to_border = true; + #else + _sg.features.image_clamp_to_border = false; + #endif + _sg.features.mrt_independent_blend_state = true; + _sg.features.mrt_independent_write_mask = true; + + #if defined(_SG_TARGET_MACOS) + _sg.limits.max_image_size_2d = 16 * 1024; + _sg.limits.max_image_size_cube = 16 * 1024; + _sg.limits.max_image_size_3d = 2 * 1024; + _sg.limits.max_image_size_array = 16 * 1024; + _sg.limits.max_image_array_layers = 2 * 1024; + #else + /* newer iOS devices support 16k textures */ + _sg.limits.max_image_size_2d = 8 * 1024; + _sg.limits.max_image_size_cube = 8 * 1024; + _sg.limits.max_image_size_3d = 2 * 1024; + _sg.limits.max_image_size_array = 8 * 1024; + _sg.limits.max_image_array_layers = 2 * 1024; + #endif + _sg.limits.max_vertex_attrs = SG_MAX_VERTEX_ATTRIBUTES; + + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8SI]); + #if defined(_SG_TARGET_MACOS) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16SN]); + #else + _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_R16]); + _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_R16SN]); + #endif + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32SI]); + #if defined(_SG_TARGET_MACOS) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R32F]); + #else + _sg_pixelformat_sbr(&_sg.formats[SG_PIXELFORMAT_R32F]); + #endif + #if defined(_SG_TARGET_MACOS) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16SN]); + #else + _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_RG16]); + _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_RG16SN]); + #endif + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_BGRA8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGB10A2]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG11B10F]); + #if defined(_SG_TARGET_MACOS) + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG32UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG32SI]); + #else + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32SI]); + #endif + #if defined(_SG_TARGET_MACOS) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG32F]); + #else + _sg_pixelformat_sbr(&_sg.formats[SG_PIXELFORMAT_RG32F]); + #endif + #if defined(_SG_TARGET_MACOS) + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16SN]); + #else + _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_RGBA16]); + _sg_pixelformat_sfbr(&_sg.formats[SG_PIXELFORMAT_RGBA16SN]); + #endif + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); + #if defined(_SG_TARGET_MACOS) + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + #else + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + #endif + _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH]); + _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH_STENCIL]); + #if defined(_SG_TARGET_MACOS) + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC1_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC2_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_R]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_RSN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RG]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RGSN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBF]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBUF]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_RGBA]); + #else + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGB_2BPP]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGB_4BPP]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGBA_2BPP]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_PVRTC_RGBA_4BPP]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGB8A1]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RGBA8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RG11]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_ETC2_RG11SN]); + #endif +} + +/*-- main Metal backend state and functions ----------------------------------*/ +_SOKOL_PRIVATE void _sg_mtl_setup_backend(const sg_desc* desc) { + /* assume already zero-initialized */ + SOKOL_ASSERT(desc); + SOKOL_ASSERT(desc->context.metal.device); + SOKOL_ASSERT(desc->context.metal.renderpass_descriptor_cb || desc->context.metal.renderpass_descriptor_userdata_cb); + SOKOL_ASSERT(desc->context.metal.drawable_cb || desc->context.metal.drawable_userdata_cb); + SOKOL_ASSERT(desc->uniform_buffer_size > 0); + _sg_mtl_init_pool(desc); + _sg_mtl_init_sampler_cache(desc); + _sg_mtl_clear_state_cache(); + _sg.mtl.valid = true; + _sg.mtl.renderpass_descriptor_cb = desc->context.metal.renderpass_descriptor_cb; + _sg.mtl.renderpass_descriptor_userdata_cb = desc->context.metal.renderpass_descriptor_userdata_cb; + _sg.mtl.drawable_cb = desc->context.metal.drawable_cb; + _sg.mtl.drawable_userdata_cb = desc->context.metal.drawable_userdata_cb; + _sg.mtl.user_data = desc->context.metal.user_data; + _sg.mtl.frame_index = 1; + _sg.mtl.ub_size = desc->uniform_buffer_size; + _sg.mtl.sem = dispatch_semaphore_create(SG_NUM_INFLIGHT_FRAMES); + _sg.mtl.device = (__bridge id) desc->context.metal.device; + _sg.mtl.cmd_queue = [_sg.mtl.device newCommandQueue]; + MTLResourceOptions res_opts = MTLResourceCPUCacheModeWriteCombined; + #if defined(_SG_TARGET_MACOS) + res_opts |= MTLResourceStorageModeManaged; + #endif + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + _sg.mtl.uniform_buffers[i] = [_sg.mtl.device + newBufferWithLength:(NSUInteger)_sg.mtl.ub_size + options:res_opts + ]; + } + _sg_mtl_init_caps(); +} + +_SOKOL_PRIVATE void _sg_mtl_discard_backend(void) { + SOKOL_ASSERT(_sg.mtl.valid); + /* wait for the last frame to finish */ + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + dispatch_semaphore_wait(_sg.mtl.sem, DISPATCH_TIME_FOREVER); + } + /* semaphore must be "relinquished" before destruction */ + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + dispatch_semaphore_signal(_sg.mtl.sem); + } + _sg_mtl_destroy_sampler_cache(_sg.mtl.frame_index); + _sg_mtl_garbage_collect(_sg.mtl.frame_index + SG_NUM_INFLIGHT_FRAMES + 2); + _sg_mtl_destroy_pool(); + _sg.mtl.valid = false; + + _SG_OBJC_RELEASE(_sg.mtl.sem); + _SG_OBJC_RELEASE(_sg.mtl.device); + _SG_OBJC_RELEASE(_sg.mtl.cmd_queue); + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + _SG_OBJC_RELEASE(_sg.mtl.uniform_buffers[i]); + } + /* NOTE: MTLCommandBuffer and MTLRenderCommandEncoder are auto-released */ + _sg.mtl.cmd_buffer = nil; + _sg.mtl.cmd_encoder = nil; +} + +_SOKOL_PRIVATE void _sg_mtl_bind_uniform_buffers(void) { + SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); + for (int slot = 0; slot < SG_MAX_SHADERSTAGE_UBS; slot++) { + [_sg.mtl.cmd_encoder + setVertexBuffer:_sg.mtl.uniform_buffers[_sg.mtl.cur_frame_rotate_index] + offset:0 + atIndex:(NSUInteger)slot]; + [_sg.mtl.cmd_encoder + setFragmentBuffer:_sg.mtl.uniform_buffers[_sg.mtl.cur_frame_rotate_index] + offset:0 + atIndex:(NSUInteger)slot]; + } +} + +_SOKOL_PRIVATE void _sg_mtl_reset_state_cache(void) { + _sg_mtl_clear_state_cache(); + + /* need to restore the uniform buffer binding (normally happens in + _sg_mtl_begin_pass() + */ + if (nil != _sg.mtl.cmd_encoder) { + _sg_mtl_bind_uniform_buffers(); + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_context(_sg_context_t* ctx) { + SOKOL_ASSERT(ctx); + _SOKOL_UNUSED(ctx); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_mtl_destroy_context(_sg_context_t* ctx) { + SOKOL_ASSERT(ctx); + _SOKOL_UNUSED(ctx); + /* empty */ +} + +_SOKOL_PRIVATE void _sg_mtl_activate_context(_sg_context_t* ctx) { + _SOKOL_UNUSED(ctx); + _sg_mtl_clear_state_cache(); +} + +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { + SOKOL_ASSERT(buf && desc); + _sg_buffer_common_init(&buf->cmn, desc); + const bool injected = (0 != desc->mtl_buffers[0]); + MTLResourceOptions mtl_options = _sg_mtl_buffer_resource_options(buf->cmn.usage); + for (int slot = 0; slot < buf->cmn.num_slots; slot++) { + id mtl_buf; + if (injected) { + SOKOL_ASSERT(desc->mtl_buffers[slot]); + mtl_buf = (__bridge id) desc->mtl_buffers[slot]; + } + else { + if (buf->cmn.usage == SG_USAGE_IMMUTABLE) { + SOKOL_ASSERT(desc->data.ptr); + mtl_buf = [_sg.mtl.device newBufferWithBytes:desc->data.ptr length:(NSUInteger)buf->cmn.size options:mtl_options]; + } + else { + mtl_buf = [_sg.mtl.device newBufferWithLength:(NSUInteger)buf->cmn.size options:mtl_options]; + } + } + buf->mtl.buf[slot] = _sg_mtl_add_resource(mtl_buf); + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_mtl_destroy_buffer(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf); + for (int slot = 0; slot < buf->cmn.num_slots; slot++) { + /* it's valid to call release resource with '0' */ + _sg_mtl_release_resource(_sg.mtl.frame_index, buf->mtl.buf[slot]); + } +} + +_SOKOL_PRIVATE void _sg_mtl_copy_image_data(const _sg_image_t* img, __unsafe_unretained id mtl_tex, const sg_image_data* data) { + const int num_faces = (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6:1; + const int num_slices = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? img->cmn.num_slices : 1; + for (int face_index = 0; face_index < num_faces; face_index++) { + for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++) { + SOKOL_ASSERT(data->subimage[face_index][mip_index].ptr); + SOKOL_ASSERT(data->subimage[face_index][mip_index].size > 0); + const uint8_t* data_ptr = (const uint8_t*)data->subimage[face_index][mip_index].ptr; + const int mip_width = _sg_max(img->cmn.width >> mip_index, 1); + const int mip_height = _sg_max(img->cmn.height >> mip_index, 1); + /* special case PVRTC formats: bytePerRow must be 0 */ + int bytes_per_row = 0; + int bytes_per_slice = _sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1); + if (!_sg_mtl_is_pvrtc(img->cmn.pixel_format)) { + bytes_per_row = _sg_row_pitch(img->cmn.pixel_format, mip_width, 1); + } + MTLRegion region; + if (img->cmn.type == SG_IMAGETYPE_3D) { + const int mip_depth = _sg_max(img->cmn.num_slices >> mip_index, 1); + region = MTLRegionMake3D(0, 0, 0, (NSUInteger)mip_width, (NSUInteger)mip_height, (NSUInteger)mip_depth); + /* FIXME: apparently the minimal bytes_per_image size for 3D texture + is 4 KByte... somehow need to handle this */ + } + else { + region = MTLRegionMake2D(0, 0, (NSUInteger)mip_width, (NSUInteger)mip_height); + } + for (int slice_index = 0; slice_index < num_slices; slice_index++) { + const int mtl_slice_index = (img->cmn.type == SG_IMAGETYPE_CUBE) ? face_index : slice_index; + const int slice_offset = slice_index * bytes_per_slice; + SOKOL_ASSERT((slice_offset + bytes_per_slice) <= (int)data->subimage[face_index][mip_index].size); + [mtl_tex replaceRegion:region + mipmapLevel:(NSUInteger)mip_index + slice:(NSUInteger)mtl_slice_index + withBytes:data_ptr + slice_offset + bytesPerRow:(NSUInteger)bytes_per_row + bytesPerImage:(NSUInteger)bytes_per_slice]; + } + } + } +} + +/* + FIXME: METAL RESOURCE STORAGE MODE FOR macOS AND iOS + + For immutable textures on macOS, the recommended procedure is to create + a MTLStorageModeManaged texture with the immutable content first, + and then use the GPU to blit the content into a MTLStorageModePrivate + texture before the first use. + + On iOS use the same one-time-blit procedure, but from a + MTLStorageModeShared to a MTLStorageModePrivate texture. + + It probably makes sense to handle this in a separate 'resource manager' + with a recycable pool of blit-source-textures? +*/ + +/* initialize MTLTextureDescritor with common attributes */ +_SOKOL_PRIVATE bool _sg_mtl_init_texdesc_common(MTLTextureDescriptor* mtl_desc, _sg_image_t* img) { + mtl_desc.textureType = _sg_mtl_texture_type(img->cmn.type); + mtl_desc.pixelFormat = _sg_mtl_pixel_format(img->cmn.pixel_format); + if (MTLPixelFormatInvalid == mtl_desc.pixelFormat) { + SOKOL_LOG("Unsupported texture pixel format!\n"); + return false; + } + mtl_desc.width = (NSUInteger)img->cmn.width; + mtl_desc.height = (NSUInteger)img->cmn.height; + if (SG_IMAGETYPE_3D == img->cmn.type) { + mtl_desc.depth = (NSUInteger)img->cmn.num_slices; + } + else { + mtl_desc.depth = 1; + } + mtl_desc.mipmapLevelCount = (NSUInteger)img->cmn.num_mipmaps; + if (SG_IMAGETYPE_ARRAY == img->cmn.type) { + mtl_desc.arrayLength = (NSUInteger)img->cmn.num_slices; + } + else { + mtl_desc.arrayLength = 1; + } + mtl_desc.usage = MTLTextureUsageShaderRead; + if (img->cmn.usage != SG_USAGE_IMMUTABLE) { + mtl_desc.cpuCacheMode = MTLCPUCacheModeWriteCombined; + } + #if defined(_SG_TARGET_MACOS) + /* macOS: use managed textures */ + mtl_desc.resourceOptions = MTLResourceStorageModeManaged; + mtl_desc.storageMode = MTLStorageModeManaged; + #else + /* iOS: use CPU/GPU shared memory */ + mtl_desc.resourceOptions = MTLResourceStorageModeShared; + mtl_desc.storageMode = MTLStorageModeShared; + #endif + return true; +} + +/* initialize MTLTextureDescritor with rendertarget attributes */ +_SOKOL_PRIVATE void _sg_mtl_init_texdesc_rt(MTLTextureDescriptor* mtl_desc, _sg_image_t* img) { + SOKOL_ASSERT(img->cmn.render_target); + _SOKOL_UNUSED(img); + /* reset the cpuCacheMode to 'default' */ + mtl_desc.cpuCacheMode = MTLCPUCacheModeDefaultCache; + /* render targets are only visible to the GPU */ + mtl_desc.resourceOptions = MTLResourceStorageModePrivate; + mtl_desc.storageMode = MTLStorageModePrivate; + /* non-MSAA render targets are shader-readable */ + mtl_desc.usage = MTLTextureUsageShaderRead | MTLTextureUsageRenderTarget; +} + +/* initialize MTLTextureDescritor with MSAA attributes */ +_SOKOL_PRIVATE void _sg_mtl_init_texdesc_rt_msaa(MTLTextureDescriptor* mtl_desc, _sg_image_t* img) { + SOKOL_ASSERT(img->cmn.sample_count > 1); + /* reset the cpuCacheMode to 'default' */ + mtl_desc.cpuCacheMode = MTLCPUCacheModeDefaultCache; + /* render targets are only visible to the GPU */ + mtl_desc.resourceOptions = MTLResourceStorageModePrivate; + mtl_desc.storageMode = MTLStorageModePrivate; + /* MSAA render targets are not shader-readable (instead they are resolved) */ + mtl_desc.usage = MTLTextureUsageRenderTarget; + mtl_desc.textureType = MTLTextureType2DMultisample; + mtl_desc.depth = 1; + mtl_desc.arrayLength = 1; + mtl_desc.mipmapLevelCount = 1; + mtl_desc.sampleCount = (NSUInteger)img->cmn.sample_count; +} + +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_image(_sg_image_t* img, const sg_image_desc* desc) { + SOKOL_ASSERT(img && desc); + _sg_image_common_init(&img->cmn, desc); + const bool injected = (0 != desc->mtl_textures[0]); + const bool msaa = (img->cmn.sample_count > 1); + + /* first initialize all Metal resource pool slots to 'empty' */ + for (int i = 0; i < SG_NUM_INFLIGHT_FRAMES; i++) { + img->mtl.tex[i] = _sg_mtl_add_resource(nil); + } + img->mtl.sampler_state = _sg_mtl_add_resource(nil); + img->mtl.depth_tex = _sg_mtl_add_resource(nil); + img->mtl.msaa_tex = _sg_mtl_add_resource(nil); + + /* initialize a Metal texture descriptor with common attributes */ + MTLTextureDescriptor* mtl_desc = [[MTLTextureDescriptor alloc] init]; + if (!_sg_mtl_init_texdesc_common(mtl_desc, img)) { + _SG_OBJC_RELEASE(mtl_desc); + return SG_RESOURCESTATE_FAILED; + } + + /* special case depth-stencil-buffer? */ + if (_sg_is_valid_rendertarget_depth_format(img->cmn.pixel_format)) { + /* depth-stencil buffer texture must always be a render target */ + SOKOL_ASSERT(img->cmn.render_target); + SOKOL_ASSERT(img->cmn.type == SG_IMAGETYPE_2D); + SOKOL_ASSERT(img->cmn.num_mipmaps == 1); + SOKOL_ASSERT(!injected); + if (msaa) { + _sg_mtl_init_texdesc_rt_msaa(mtl_desc, img); + } + else { + _sg_mtl_init_texdesc_rt(mtl_desc, img); + } + id tex = [_sg.mtl.device newTextureWithDescriptor:mtl_desc]; + SOKOL_ASSERT(nil != tex); + img->mtl.depth_tex = _sg_mtl_add_resource(tex); + } + else { + /* create the color texture + In case this is a render target without MSAA, add the relevant + render-target descriptor attributes. + In case this is a render target *with* MSAA, the color texture + will serve as MSAA-resolve target (not as render target), and rendering + will go into a separate render target texture of type + MTLTextureType2DMultisample. + */ + if (img->cmn.render_target && !msaa) { + _sg_mtl_init_texdesc_rt(mtl_desc, img); + } + for (int slot = 0; slot < img->cmn.num_slots; slot++) { + id tex; + if (injected) { + SOKOL_ASSERT(desc->mtl_textures[slot]); + tex = (__bridge id) desc->mtl_textures[slot]; + } + else { + tex = [_sg.mtl.device newTextureWithDescriptor:mtl_desc]; + if ((img->cmn.usage == SG_USAGE_IMMUTABLE) && !img->cmn.render_target) { + _sg_mtl_copy_image_data(img, tex, &desc->data); + } + } + img->mtl.tex[slot] = _sg_mtl_add_resource(tex); + } + + /* if MSAA color render target, create an additional MSAA render-surface texture */ + if (img->cmn.render_target && msaa) { + _sg_mtl_init_texdesc_rt_msaa(mtl_desc, img); + id tex = [_sg.mtl.device newTextureWithDescriptor:mtl_desc]; + img->mtl.msaa_tex = _sg_mtl_add_resource(tex); + } + + /* create (possibly shared) sampler state */ + img->mtl.sampler_state = _sg_mtl_create_sampler(_sg.mtl.device, desc); + } + _SG_OBJC_RELEASE(mtl_desc); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_mtl_destroy_image(_sg_image_t* img) { + SOKOL_ASSERT(img); + /* it's valid to call release resource with a 'null resource' */ + for (int slot = 0; slot < img->cmn.num_slots; slot++) { + _sg_mtl_release_resource(_sg.mtl.frame_index, img->mtl.tex[slot]); + } + _sg_mtl_release_resource(_sg.mtl.frame_index, img->mtl.depth_tex); + _sg_mtl_release_resource(_sg.mtl.frame_index, img->mtl.msaa_tex); + /* NOTE: sampler state objects are shared and not released until shutdown */ +} + +_SOKOL_PRIVATE id _sg_mtl_compile_library(const char* src) { + NSError* err = NULL; + id lib = [_sg.mtl.device + newLibraryWithSource:[NSString stringWithUTF8String:src] + options:nil + error:&err + ]; + if (err) { + SOKOL_LOG([err.localizedDescription UTF8String]); + } + return lib; +} + +_SOKOL_PRIVATE id _sg_mtl_library_from_bytecode(const void* ptr, size_t num_bytes) { + NSError* err = NULL; + dispatch_data_t lib_data = dispatch_data_create(ptr, num_bytes, NULL, DISPATCH_DATA_DESTRUCTOR_DEFAULT); + id lib = [_sg.mtl.device newLibraryWithData:lib_data error:&err]; + if (err) { + SOKOL_LOG([err.localizedDescription UTF8String]); + } + _SG_OBJC_RELEASE(lib_data); + return lib; +} + +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { + SOKOL_ASSERT(shd && desc); + + _sg_shader_common_init(&shd->cmn, desc); + + /* create metal libray objects and lookup entry functions */ + id vs_lib; + id fs_lib; + id vs_func; + id fs_func; + const char* vs_entry = desc->vs.entry; + const char* fs_entry = desc->fs.entry; + if (desc->vs.bytecode.ptr && desc->fs.bytecode.ptr) { + /* separate byte code provided */ + vs_lib = _sg_mtl_library_from_bytecode(desc->vs.bytecode.ptr, desc->vs.bytecode.size); + fs_lib = _sg_mtl_library_from_bytecode(desc->fs.bytecode.ptr, desc->fs.bytecode.size); + if (nil == vs_lib || nil == fs_lib) { + return SG_RESOURCESTATE_FAILED; + } + vs_func = [vs_lib newFunctionWithName:[NSString stringWithUTF8String:vs_entry]]; + fs_func = [fs_lib newFunctionWithName:[NSString stringWithUTF8String:fs_entry]]; + } + else if (desc->vs.source && desc->fs.source) { + /* separate sources provided */ + vs_lib = _sg_mtl_compile_library(desc->vs.source); + fs_lib = _sg_mtl_compile_library(desc->fs.source); + if (nil == vs_lib || nil == fs_lib) { + return SG_RESOURCESTATE_FAILED; + } + vs_func = [vs_lib newFunctionWithName:[NSString stringWithUTF8String:vs_entry]]; + fs_func = [fs_lib newFunctionWithName:[NSString stringWithUTF8String:fs_entry]]; + } + else { + return SG_RESOURCESTATE_FAILED; + } + if (nil == vs_func) { + SOKOL_LOG("vertex shader entry function not found\n"); + return SG_RESOURCESTATE_FAILED; + } + if (nil == fs_func) { + SOKOL_LOG("fragment shader entry function not found\n"); + return SG_RESOURCESTATE_FAILED; + } + /* it is legal to call _sg_mtl_add_resource with a nil value, this will return a special 0xFFFFFFFF index */ + shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_lib = _sg_mtl_add_resource(vs_lib); + shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_lib = _sg_mtl_add_resource(fs_lib); + shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_func = _sg_mtl_add_resource(vs_func); + shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_func = _sg_mtl_add_resource(fs_func); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_mtl_destroy_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd); + /* it is valid to call _sg_mtl_release_resource with a 'null resource' */ + _sg_mtl_release_resource(_sg.mtl.frame_index, shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_func); + _sg_mtl_release_resource(_sg.mtl.frame_index, shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_lib); + _sg_mtl_release_resource(_sg.mtl.frame_index, shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_func); + _sg_mtl_release_resource(_sg.mtl.frame_index, shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_lib); +} + +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(pip && shd && desc); + SOKOL_ASSERT(desc->shader.id == shd->slot.id); + + pip->shader = shd; + _sg_pipeline_common_init(&pip->cmn, desc); + + sg_primitive_type prim_type = desc->primitive_type; + pip->mtl.prim_type = _sg_mtl_primitive_type(prim_type); + pip->mtl.index_size = _sg_mtl_index_size(pip->cmn.index_type); + if (SG_INDEXTYPE_NONE != pip->cmn.index_type) { + pip->mtl.index_type = _sg_mtl_index_type(pip->cmn.index_type); + } + pip->mtl.cull_mode = _sg_mtl_cull_mode(desc->cull_mode); + pip->mtl.winding = _sg_mtl_winding(desc->face_winding); + pip->mtl.stencil_ref = desc->stencil.ref; + + /* create vertex-descriptor */ + MTLVertexDescriptor* vtx_desc = [MTLVertexDescriptor vertexDescriptor]; + for (NSUInteger attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + const sg_vertex_attr_desc* a_desc = &desc->layout.attrs[attr_index]; + if (a_desc->format == SG_VERTEXFORMAT_INVALID) { + break; + } + SOKOL_ASSERT(a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS); + vtx_desc.attributes[attr_index].format = _sg_mtl_vertex_format(a_desc->format); + vtx_desc.attributes[attr_index].offset = (NSUInteger)a_desc->offset; + vtx_desc.attributes[attr_index].bufferIndex = (NSUInteger)(a_desc->buffer_index + SG_MAX_SHADERSTAGE_UBS); + pip->cmn.vertex_layout_valid[a_desc->buffer_index] = true; + } + for (NSUInteger layout_index = 0; layout_index < SG_MAX_SHADERSTAGE_BUFFERS; layout_index++) { + if (pip->cmn.vertex_layout_valid[layout_index]) { + const sg_buffer_layout_desc* l_desc = &desc->layout.buffers[layout_index]; + const NSUInteger mtl_vb_slot = layout_index + SG_MAX_SHADERSTAGE_UBS; + SOKOL_ASSERT(l_desc->stride > 0); + vtx_desc.layouts[mtl_vb_slot].stride = (NSUInteger)l_desc->stride; + vtx_desc.layouts[mtl_vb_slot].stepFunction = _sg_mtl_step_function(l_desc->step_func); + vtx_desc.layouts[mtl_vb_slot].stepRate = (NSUInteger)l_desc->step_rate; + } + } + + /* render-pipeline descriptor */ + MTLRenderPipelineDescriptor* rp_desc = [[MTLRenderPipelineDescriptor alloc] init]; + rp_desc.vertexDescriptor = vtx_desc; + SOKOL_ASSERT(shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_func != _SG_MTL_INVALID_SLOT_INDEX); + rp_desc.vertexFunction = _sg_mtl_id(shd->mtl.stage[SG_SHADERSTAGE_VS].mtl_func); + SOKOL_ASSERT(shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_func != _SG_MTL_INVALID_SLOT_INDEX); + rp_desc.fragmentFunction = _sg_mtl_id(shd->mtl.stage[SG_SHADERSTAGE_FS].mtl_func); + rp_desc.sampleCount = (NSUInteger)desc->sample_count; + rp_desc.alphaToCoverageEnabled = desc->alpha_to_coverage_enabled; + rp_desc.alphaToOneEnabled = NO; + rp_desc.rasterizationEnabled = YES; + rp_desc.depthAttachmentPixelFormat = _sg_mtl_pixel_format(desc->depth.pixel_format); + if (desc->depth.pixel_format == SG_PIXELFORMAT_DEPTH_STENCIL) { + rp_desc.stencilAttachmentPixelFormat = _sg_mtl_pixel_format(desc->depth.pixel_format); + } + /* FIXME: this only works on macOS 10.13! + for (int i = 0; i < (SG_MAX_SHADERSTAGE_UBS+SG_MAX_SHADERSTAGE_BUFFERS); i++) { + rp_desc.vertexBuffers[i].mutability = MTLMutabilityImmutable; + } + for (int i = 0; i < SG_MAX_SHADERSTAGE_UBS; i++) { + rp_desc.fragmentBuffers[i].mutability = MTLMutabilityImmutable; + } + */ + for (NSUInteger i = 0; i < (NSUInteger)desc->color_count; i++) { + SOKOL_ASSERT(i < SG_MAX_COLOR_ATTACHMENTS); + const sg_color_state* cs = &desc->colors[i]; + rp_desc.colorAttachments[i].pixelFormat = _sg_mtl_pixel_format(cs->pixel_format); + rp_desc.colorAttachments[i].writeMask = _sg_mtl_color_write_mask(cs->write_mask); + rp_desc.colorAttachments[i].blendingEnabled = cs->blend.enabled; + rp_desc.colorAttachments[i].alphaBlendOperation = _sg_mtl_blend_op(cs->blend.op_alpha); + rp_desc.colorAttachments[i].rgbBlendOperation = _sg_mtl_blend_op(cs->blend.op_rgb); + rp_desc.colorAttachments[i].destinationAlphaBlendFactor = _sg_mtl_blend_factor(cs->blend.dst_factor_alpha); + rp_desc.colorAttachments[i].destinationRGBBlendFactor = _sg_mtl_blend_factor(cs->blend.dst_factor_rgb); + rp_desc.colorAttachments[i].sourceAlphaBlendFactor = _sg_mtl_blend_factor(cs->blend.src_factor_alpha); + rp_desc.colorAttachments[i].sourceRGBBlendFactor = _sg_mtl_blend_factor(cs->blend.src_factor_rgb); + } + NSError* err = NULL; + id mtl_rps = [_sg.mtl.device newRenderPipelineStateWithDescriptor:rp_desc error:&err]; + _SG_OBJC_RELEASE(rp_desc); + if (nil == mtl_rps) { + SOKOL_ASSERT(err); + SOKOL_LOG([err.localizedDescription UTF8String]); + return SG_RESOURCESTATE_FAILED; + } + + /* depth-stencil-state */ + MTLDepthStencilDescriptor* ds_desc = [[MTLDepthStencilDescriptor alloc] init]; + ds_desc.depthCompareFunction = _sg_mtl_compare_func(desc->depth.compare); + ds_desc.depthWriteEnabled = desc->depth.write_enabled; + if (desc->stencil.enabled) { + const sg_stencil_face_state* sb = &desc->stencil.back; + ds_desc.backFaceStencil = [[MTLStencilDescriptor alloc] init]; + ds_desc.backFaceStencil.stencilFailureOperation = _sg_mtl_stencil_op(sb->fail_op); + ds_desc.backFaceStencil.depthFailureOperation = _sg_mtl_stencil_op(sb->depth_fail_op); + ds_desc.backFaceStencil.depthStencilPassOperation = _sg_mtl_stencil_op(sb->pass_op); + ds_desc.backFaceStencil.stencilCompareFunction = _sg_mtl_compare_func(sb->compare); + ds_desc.backFaceStencil.readMask = desc->stencil.read_mask; + ds_desc.backFaceStencil.writeMask = desc->stencil.write_mask; + const sg_stencil_face_state* sf = &desc->stencil.front; + ds_desc.frontFaceStencil = [[MTLStencilDescriptor alloc] init]; + ds_desc.frontFaceStencil.stencilFailureOperation = _sg_mtl_stencil_op(sf->fail_op); + ds_desc.frontFaceStencil.depthFailureOperation = _sg_mtl_stencil_op(sf->depth_fail_op); + ds_desc.frontFaceStencil.depthStencilPassOperation = _sg_mtl_stencil_op(sf->pass_op); + ds_desc.frontFaceStencil.stencilCompareFunction = _sg_mtl_compare_func(sf->compare); + ds_desc.frontFaceStencil.readMask = desc->stencil.read_mask; + ds_desc.frontFaceStencil.writeMask = desc->stencil.write_mask; + } + id mtl_dss = [_sg.mtl.device newDepthStencilStateWithDescriptor:ds_desc]; + _SG_OBJC_RELEASE(ds_desc); + pip->mtl.rps = _sg_mtl_add_resource(mtl_rps); + pip->mtl.dss = _sg_mtl_add_resource(mtl_dss); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_mtl_destroy_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + /* it's valid to call release resource with a 'null resource' */ + _sg_mtl_release_resource(_sg.mtl.frame_index, pip->mtl.rps); + _sg_mtl_release_resource(_sg.mtl.frame_index, pip->mtl.dss); +} + +_SOKOL_PRIVATE sg_resource_state _sg_mtl_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { + SOKOL_ASSERT(pass && desc); + SOKOL_ASSERT(att_images && att_images[0]); + + _sg_pass_common_init(&pass->cmn, desc); + + /* copy image pointers */ + const sg_pass_attachment_desc* att_desc; + for (int i = 0; i < pass->cmn.num_color_atts; i++) { + att_desc = &desc->color_attachments[i]; + if (att_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(att_desc->image.id != SG_INVALID_ID); + SOKOL_ASSERT(0 == pass->mtl.color_atts[i].image); + SOKOL_ASSERT(att_images[i] && (att_images[i]->slot.id == att_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(att_images[i]->cmn.pixel_format)); + pass->mtl.color_atts[i].image = att_images[i]; + } + } + SOKOL_ASSERT(0 == pass->mtl.ds_att.image); + att_desc = &desc->depth_stencil_attachment; + if (att_desc->image.id != SG_INVALID_ID) { + const int ds_img_index = SG_MAX_COLOR_ATTACHMENTS; + SOKOL_ASSERT(att_images[ds_img_index] && (att_images[ds_img_index]->slot.id == att_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(att_images[ds_img_index]->cmn.pixel_format)); + pass->mtl.ds_att.image = att_images[ds_img_index]; + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_mtl_destroy_pass(_sg_pass_t* pass) { + SOKOL_ASSERT(pass); + _SOKOL_UNUSED(pass); +} + +_SOKOL_PRIVATE _sg_image_t* _sg_mtl_pass_color_image(const _sg_pass_t* pass, int index) { + SOKOL_ASSERT(pass && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + /* NOTE: may return null */ + return pass->mtl.color_atts[index].image; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_mtl_pass_ds_image(const _sg_pass_t* pass) { + /* NOTE: may return null */ + SOKOL_ASSERT(pass); + return pass->mtl.ds_att.image; +} + +_SOKOL_PRIVATE void _sg_mtl_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { + SOKOL_ASSERT(action); + SOKOL_ASSERT(!_sg.mtl.in_pass); + SOKOL_ASSERT(_sg.mtl.cmd_queue); + SOKOL_ASSERT(nil == _sg.mtl.cmd_encoder); + SOKOL_ASSERT(_sg.mtl.renderpass_descriptor_cb || _sg.mtl.renderpass_descriptor_userdata_cb); + _sg.mtl.in_pass = true; + _sg.mtl.cur_width = w; + _sg.mtl.cur_height = h; + _sg_mtl_clear_state_cache(); + + /* if this is the first pass in the frame, create a command buffer */ + if (nil == _sg.mtl.cmd_buffer) { + /* block until the oldest frame in flight has finished */ + dispatch_semaphore_wait(_sg.mtl.sem, DISPATCH_TIME_FOREVER); + _sg.mtl.cmd_buffer = [_sg.mtl.cmd_queue commandBufferWithUnretainedReferences]; + } + + /* if this is first pass in frame, get uniform buffer base pointer */ + if (0 == _sg.mtl.cur_ub_base_ptr) { + _sg.mtl.cur_ub_base_ptr = (uint8_t*)[_sg.mtl.uniform_buffers[_sg.mtl.cur_frame_rotate_index] contents]; + } + + /* initialize a render pass descriptor */ + MTLRenderPassDescriptor* pass_desc = nil; + if (pass) { + /* offscreen render pass */ + pass_desc = [MTLRenderPassDescriptor renderPassDescriptor]; + } + else { + /* default render pass, call user-provided callback to provide render pass descriptor */ + if (_sg.mtl.renderpass_descriptor_cb) { + pass_desc = (__bridge MTLRenderPassDescriptor*) _sg.mtl.renderpass_descriptor_cb(); + } + else { + pass_desc = (__bridge MTLRenderPassDescriptor*) _sg.mtl.renderpass_descriptor_userdata_cb(_sg.mtl.user_data); + } + + } + if (pass_desc) { + _sg.mtl.pass_valid = true; + } + else { + /* default pass descriptor will not be valid if window is minimized, + don't do any rendering in this case */ + _sg.mtl.pass_valid = false; + return; + } + if (pass) { + /* setup pass descriptor for offscreen rendering */ + SOKOL_ASSERT(pass->slot.state == SG_RESOURCESTATE_VALID); + for (NSUInteger i = 0; i < (NSUInteger)pass->cmn.num_color_atts; i++) { + const _sg_pass_attachment_t* cmn_att = &pass->cmn.color_atts[i]; + const _sg_mtl_attachment_t* mtl_att = &pass->mtl.color_atts[i]; + const _sg_image_t* att_img = mtl_att->image; + SOKOL_ASSERT(att_img->slot.state == SG_RESOURCESTATE_VALID); + SOKOL_ASSERT(att_img->slot.id == cmn_att->image_id.id); + const bool is_msaa = (att_img->cmn.sample_count > 1); + pass_desc.colorAttachments[i].loadAction = _sg_mtl_load_action(action->colors[i].action); + pass_desc.colorAttachments[i].storeAction = is_msaa ? MTLStoreActionMultisampleResolve : MTLStoreActionStore; + sg_color c = action->colors[i].value; + pass_desc.colorAttachments[i].clearColor = MTLClearColorMake(c.r, c.g, c.b, c.a); + if (is_msaa) { + SOKOL_ASSERT(att_img->mtl.msaa_tex != _SG_MTL_INVALID_SLOT_INDEX); + SOKOL_ASSERT(att_img->mtl.tex[mtl_att->image->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + pass_desc.colorAttachments[i].texture = _sg_mtl_id(att_img->mtl.msaa_tex); + pass_desc.colorAttachments[i].resolveTexture = _sg_mtl_id(att_img->mtl.tex[att_img->cmn.active_slot]); + pass_desc.colorAttachments[i].resolveLevel = (NSUInteger)cmn_att->mip_level; + switch (att_img->cmn.type) { + case SG_IMAGETYPE_CUBE: + case SG_IMAGETYPE_ARRAY: + pass_desc.colorAttachments[i].resolveSlice = (NSUInteger)cmn_att->slice; + break; + case SG_IMAGETYPE_3D: + pass_desc.colorAttachments[i].resolveDepthPlane = (NSUInteger)cmn_att->slice; + break; + default: break; + } + } + else { + SOKOL_ASSERT(att_img->mtl.tex[att_img->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + pass_desc.colorAttachments[i].texture = _sg_mtl_id(att_img->mtl.tex[att_img->cmn.active_slot]); + pass_desc.colorAttachments[i].level = (NSUInteger)cmn_att->mip_level; + switch (att_img->cmn.type) { + case SG_IMAGETYPE_CUBE: + case SG_IMAGETYPE_ARRAY: + pass_desc.colorAttachments[i].slice = (NSUInteger)cmn_att->slice; + break; + case SG_IMAGETYPE_3D: + pass_desc.colorAttachments[i].depthPlane = (NSUInteger)cmn_att->slice; + break; + default: break; + } + } + } + const _sg_image_t* ds_att_img = pass->mtl.ds_att.image; + if (0 != ds_att_img) { + SOKOL_ASSERT(ds_att_img->slot.state == SG_RESOURCESTATE_VALID); + SOKOL_ASSERT(ds_att_img->slot.id == pass->cmn.ds_att.image_id.id); + SOKOL_ASSERT(ds_att_img->mtl.depth_tex != _SG_MTL_INVALID_SLOT_INDEX); + pass_desc.depthAttachment.texture = _sg_mtl_id(ds_att_img->mtl.depth_tex); + pass_desc.depthAttachment.loadAction = _sg_mtl_load_action(action->depth.action); + pass_desc.depthAttachment.clearDepth = action->depth.value; + if (_sg_is_depth_stencil_format(ds_att_img->cmn.pixel_format)) { + pass_desc.stencilAttachment.texture = _sg_mtl_id(ds_att_img->mtl.depth_tex); + pass_desc.stencilAttachment.loadAction = _sg_mtl_load_action(action->stencil.action); + pass_desc.stencilAttachment.clearStencil = action->stencil.value; + } + } + } + else { + /* setup pass descriptor for default rendering */ + pass_desc.colorAttachments[0].loadAction = _sg_mtl_load_action(action->colors[0].action); + sg_color c = action->colors[0].value; + pass_desc.colorAttachments[0].clearColor = MTLClearColorMake(c.r, c.g, c.b, c.a); + pass_desc.depthAttachment.loadAction = _sg_mtl_load_action(action->depth.action); + pass_desc.depthAttachment.clearDepth = action->depth.value; + pass_desc.stencilAttachment.loadAction = _sg_mtl_load_action(action->stencil.action); + pass_desc.stencilAttachment.clearStencil = action->stencil.value; + } + + /* create a render command encoder, this might return nil if window is minimized */ + _sg.mtl.cmd_encoder = [_sg.mtl.cmd_buffer renderCommandEncoderWithDescriptor:pass_desc]; + if (nil == _sg.mtl.cmd_encoder) { + _sg.mtl.pass_valid = false; + return; + } + + /* bind the global uniform buffer, this only happens once per pass */ + _sg_mtl_bind_uniform_buffers(); +} + +_SOKOL_PRIVATE void _sg_mtl_end_pass(void) { + SOKOL_ASSERT(_sg.mtl.in_pass); + _sg.mtl.in_pass = false; + _sg.mtl.pass_valid = false; + if (nil != _sg.mtl.cmd_encoder) { + [_sg.mtl.cmd_encoder endEncoding]; + /* NOTE: MTLRenderCommandEncoder is autoreleased */ + _sg.mtl.cmd_encoder = nil; + } +} + +_SOKOL_PRIVATE void _sg_mtl_commit(void) { + SOKOL_ASSERT(!_sg.mtl.in_pass); + SOKOL_ASSERT(!_sg.mtl.pass_valid); + SOKOL_ASSERT(_sg.mtl.drawable_cb || _sg.mtl.drawable_userdata_cb); + SOKOL_ASSERT(nil == _sg.mtl.cmd_encoder); + SOKOL_ASSERT(nil != _sg.mtl.cmd_buffer); + + #if defined(_SG_TARGET_MACOS) + [_sg.mtl.uniform_buffers[_sg.mtl.cur_frame_rotate_index] didModifyRange:NSMakeRange(0, (NSUInteger)_sg.mtl.cur_ub_offset)]; + #endif + + /* present, commit and signal semaphore when done */ + id cur_drawable = nil; + if (_sg.mtl.drawable_cb) { + cur_drawable = (__bridge id) _sg.mtl.drawable_cb(); + } + else { + cur_drawable = (__bridge id) _sg.mtl.drawable_userdata_cb(_sg.mtl.user_data); + } + [_sg.mtl.cmd_buffer presentDrawable:cur_drawable]; + [_sg.mtl.cmd_buffer addCompletedHandler:^(id cmd_buffer) { + _SOKOL_UNUSED(cmd_buffer); + dispatch_semaphore_signal(_sg.mtl.sem); + }]; + [_sg.mtl.cmd_buffer commit]; + + /* garbage-collect resources pending for release */ + _sg_mtl_garbage_collect(_sg.mtl.frame_index); + + /* rotate uniform buffer slot */ + if (++_sg.mtl.cur_frame_rotate_index >= SG_NUM_INFLIGHT_FRAMES) { + _sg.mtl.cur_frame_rotate_index = 0; + } + _sg.mtl.frame_index++; + _sg.mtl.cur_ub_offset = 0; + _sg.mtl.cur_ub_base_ptr = 0; + /* NOTE: MTLCommandBuffer is autoreleased */ + _sg.mtl.cmd_buffer = nil; +} + +_SOKOL_PRIVATE void _sg_mtl_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_sg.mtl.in_pass); + if (!_sg.mtl.pass_valid) { + return; + } + SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); + MTLViewport vp; + vp.originX = (double) x; + vp.originY = (double) (origin_top_left ? y : (_sg.mtl.cur_height - (y + h))); + vp.width = (double) w; + vp.height = (double) h; + vp.znear = 0.0; + vp.zfar = 1.0; + [_sg.mtl.cmd_encoder setViewport:vp]; +} + +_SOKOL_PRIVATE void _sg_mtl_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_sg.mtl.in_pass); + if (!_sg.mtl.pass_valid) { + return; + } + SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); + /* clip against framebuffer rect */ + x = _sg_min(_sg_max(0, x), _sg.mtl.cur_width-1); + y = _sg_min(_sg_max(0, y), _sg.mtl.cur_height-1); + if ((x + w) > _sg.mtl.cur_width) { + w = _sg.mtl.cur_width - x; + } + if ((y + h) > _sg.mtl.cur_height) { + h = _sg.mtl.cur_height - y; + } + w = _sg_max(w, 1); + h = _sg_max(h, 1); + + MTLScissorRect r; + r.x = (NSUInteger)x; + r.y = (NSUInteger) (origin_top_left ? y : (_sg.mtl.cur_height - (y + h))); + r.width = (NSUInteger)w; + r.height = (NSUInteger)h; + [_sg.mtl.cmd_encoder setScissorRect:r]; +} + +_SOKOL_PRIVATE void _sg_mtl_apply_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + SOKOL_ASSERT(pip->shader && (pip->cmn.shader_id.id == pip->shader->slot.id)); + SOKOL_ASSERT(_sg.mtl.in_pass); + if (!_sg.mtl.pass_valid) { + return; + } + SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); + + if ((_sg.mtl.state_cache.cur_pipeline != pip) || (_sg.mtl.state_cache.cur_pipeline_id.id != pip->slot.id)) { + _sg.mtl.state_cache.cur_pipeline = pip; + _sg.mtl.state_cache.cur_pipeline_id.id = pip->slot.id; + sg_color c = pip->cmn.blend_color; + [_sg.mtl.cmd_encoder setBlendColorRed:c.r green:c.g blue:c.b alpha:c.a]; + [_sg.mtl.cmd_encoder setCullMode:pip->mtl.cull_mode]; + [_sg.mtl.cmd_encoder setFrontFacingWinding:pip->mtl.winding]; + [_sg.mtl.cmd_encoder setStencilReferenceValue:pip->mtl.stencil_ref]; + [_sg.mtl.cmd_encoder setDepthBias:pip->cmn.depth_bias slopeScale:pip->cmn.depth_bias_slope_scale clamp:pip->cmn.depth_bias_clamp]; + SOKOL_ASSERT(pip->mtl.rps != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.cmd_encoder setRenderPipelineState:_sg_mtl_id(pip->mtl.rps)]; + SOKOL_ASSERT(pip->mtl.dss != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.cmd_encoder setDepthStencilState:_sg_mtl_id(pip->mtl.dss)]; + } +} + +_SOKOL_PRIVATE void _sg_mtl_apply_bindings( + _sg_pipeline_t* pip, + _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, + _sg_buffer_t* ib, int ib_offset, + _sg_image_t** vs_imgs, int num_vs_imgs, + _sg_image_t** fs_imgs, int num_fs_imgs) +{ + _SOKOL_UNUSED(pip); + SOKOL_ASSERT(_sg.mtl.in_pass); + if (!_sg.mtl.pass_valid) { + return; + } + SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); + + /* store index buffer binding, this will be needed later in sg_draw() */ + _sg.mtl.state_cache.cur_indexbuffer = ib; + _sg.mtl.state_cache.cur_indexbuffer_offset = ib_offset; + if (ib) { + SOKOL_ASSERT(pip->cmn.index_type != SG_INDEXTYPE_NONE); + _sg.mtl.state_cache.cur_indexbuffer_id.id = ib->slot.id; + } + else { + SOKOL_ASSERT(pip->cmn.index_type == SG_INDEXTYPE_NONE); + _sg.mtl.state_cache.cur_indexbuffer_id.id = SG_INVALID_ID; + } + + /* apply vertex buffers */ + NSUInteger slot; + for (slot = 0; slot < (NSUInteger)num_vbs; slot++) { + const _sg_buffer_t* vb = vbs[slot]; + if ((_sg.mtl.state_cache.cur_vertexbuffers[slot] != vb) || + (_sg.mtl.state_cache.cur_vertexbuffer_offsets[slot] != vb_offsets[slot]) || + (_sg.mtl.state_cache.cur_vertexbuffer_ids[slot].id != vb->slot.id)) + { + _sg.mtl.state_cache.cur_vertexbuffers[slot] = vb; + _sg.mtl.state_cache.cur_vertexbuffer_offsets[slot] = vb_offsets[slot]; + _sg.mtl.state_cache.cur_vertexbuffer_ids[slot].id = vb->slot.id; + const NSUInteger mtl_slot = SG_MAX_SHADERSTAGE_UBS + slot; + SOKOL_ASSERT(vb->mtl.buf[vb->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.cmd_encoder setVertexBuffer:_sg_mtl_id(vb->mtl.buf[vb->cmn.active_slot]) + offset:(NSUInteger)vb_offsets[slot] + atIndex:mtl_slot]; + } + } + + /* apply vertex shader images */ + for (slot = 0; slot < (NSUInteger)num_vs_imgs; slot++) { + const _sg_image_t* img = vs_imgs[slot]; + if ((_sg.mtl.state_cache.cur_vs_images[slot] != img) || (_sg.mtl.state_cache.cur_vs_image_ids[slot].id != img->slot.id)) { + _sg.mtl.state_cache.cur_vs_images[slot] = img; + _sg.mtl.state_cache.cur_vs_image_ids[slot].id = img->slot.id; + SOKOL_ASSERT(img->mtl.tex[img->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.cmd_encoder setVertexTexture:_sg_mtl_id(img->mtl.tex[img->cmn.active_slot]) atIndex:slot]; + SOKOL_ASSERT(img->mtl.sampler_state != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.cmd_encoder setVertexSamplerState:_sg_mtl_id(img->mtl.sampler_state) atIndex:slot]; + } + } + + /* apply fragment shader images */ + for (slot = 0; slot < (NSUInteger)num_fs_imgs; slot++) { + const _sg_image_t* img = fs_imgs[slot]; + if ((_sg.mtl.state_cache.cur_fs_images[slot] != img) || (_sg.mtl.state_cache.cur_fs_image_ids[slot].id != img->slot.id)) { + _sg.mtl.state_cache.cur_fs_images[slot] = img; + _sg.mtl.state_cache.cur_fs_image_ids[slot].id = img->slot.id; + SOKOL_ASSERT(img->mtl.tex[img->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.cmd_encoder setFragmentTexture:_sg_mtl_id(img->mtl.tex[img->cmn.active_slot]) atIndex:slot]; + SOKOL_ASSERT(img->mtl.sampler_state != _SG_MTL_INVALID_SLOT_INDEX); + [_sg.mtl.cmd_encoder setFragmentSamplerState:_sg_mtl_id(img->mtl.sampler_state) atIndex:slot]; + } + } +} + +_SOKOL_PRIVATE void _sg_mtl_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { + SOKOL_ASSERT(_sg.mtl.in_pass); + if (!_sg.mtl.pass_valid) { + return; + } + SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); + SOKOL_ASSERT(((size_t)_sg.mtl.cur_ub_offset + data->size) <= (size_t)_sg.mtl.ub_size); + SOKOL_ASSERT((_sg.mtl.cur_ub_offset & (_SG_MTL_UB_ALIGN-1)) == 0); + SOKOL_ASSERT(_sg.mtl.state_cache.cur_pipeline && _sg.mtl.state_cache.cur_pipeline->shader); + SOKOL_ASSERT(_sg.mtl.state_cache.cur_pipeline->slot.id == _sg.mtl.state_cache.cur_pipeline_id.id); + SOKOL_ASSERT(_sg.mtl.state_cache.cur_pipeline->shader->slot.id == _sg.mtl.state_cache.cur_pipeline->cmn.shader_id.id); + SOKOL_ASSERT(ub_index < _sg.mtl.state_cache.cur_pipeline->shader->cmn.stage[stage_index].num_uniform_blocks); + SOKOL_ASSERT(data->size <= _sg.mtl.state_cache.cur_pipeline->shader->cmn.stage[stage_index].uniform_blocks[ub_index].size); + + /* copy to global uniform buffer, record offset into cmd encoder, and advance offset */ + uint8_t* dst = &_sg.mtl.cur_ub_base_ptr[_sg.mtl.cur_ub_offset]; + memcpy(dst, data->ptr, data->size); + if (stage_index == SG_SHADERSTAGE_VS) { + [_sg.mtl.cmd_encoder setVertexBufferOffset:(NSUInteger)_sg.mtl.cur_ub_offset atIndex:(NSUInteger)ub_index]; + } + else { + [_sg.mtl.cmd_encoder setFragmentBufferOffset:(NSUInteger)_sg.mtl.cur_ub_offset atIndex:(NSUInteger)ub_index]; + } + _sg.mtl.cur_ub_offset = _sg_roundup(_sg.mtl.cur_ub_offset + (int)data->size, _SG_MTL_UB_ALIGN); +} + +_SOKOL_PRIVATE void _sg_mtl_draw(int base_element, int num_elements, int num_instances) { + SOKOL_ASSERT(_sg.mtl.in_pass); + if (!_sg.mtl.pass_valid) { + return; + } + SOKOL_ASSERT(nil != _sg.mtl.cmd_encoder); + SOKOL_ASSERT(_sg.mtl.state_cache.cur_pipeline && (_sg.mtl.state_cache.cur_pipeline->slot.id == _sg.mtl.state_cache.cur_pipeline_id.id)); + if (SG_INDEXTYPE_NONE != _sg.mtl.state_cache.cur_pipeline->cmn.index_type) { + /* indexed rendering */ + SOKOL_ASSERT(_sg.mtl.state_cache.cur_indexbuffer && (_sg.mtl.state_cache.cur_indexbuffer->slot.id == _sg.mtl.state_cache.cur_indexbuffer_id.id)); + const _sg_buffer_t* ib = _sg.mtl.state_cache.cur_indexbuffer; + SOKOL_ASSERT(ib->mtl.buf[ib->cmn.active_slot] != _SG_MTL_INVALID_SLOT_INDEX); + const NSUInteger index_buffer_offset = (NSUInteger) (_sg.mtl.state_cache.cur_indexbuffer_offset + base_element * _sg.mtl.state_cache.cur_pipeline->mtl.index_size); + [_sg.mtl.cmd_encoder drawIndexedPrimitives:_sg.mtl.state_cache.cur_pipeline->mtl.prim_type + indexCount:(NSUInteger)num_elements + indexType:_sg.mtl.state_cache.cur_pipeline->mtl.index_type + indexBuffer:_sg_mtl_id(ib->mtl.buf[ib->cmn.active_slot]) + indexBufferOffset:index_buffer_offset + instanceCount:(NSUInteger)num_instances]; + } + else { + /* non-indexed rendering */ + [_sg.mtl.cmd_encoder drawPrimitives:_sg.mtl.state_cache.cur_pipeline->mtl.prim_type + vertexStart:(NSUInteger)base_element + vertexCount:(NSUInteger)num_elements + instanceCount:(NSUInteger)num_instances]; + } +} + +_SOKOL_PRIVATE void _sg_mtl_update_buffer(_sg_buffer_t* buf, const sg_range* data) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + if (++buf->cmn.active_slot >= buf->cmn.num_slots) { + buf->cmn.active_slot = 0; + } + __unsafe_unretained id mtl_buf = _sg_mtl_id(buf->mtl.buf[buf->cmn.active_slot]); + void* dst_ptr = [mtl_buf contents]; + memcpy(dst_ptr, data->ptr, data->size); + #if defined(_SG_TARGET_MACOS) + [mtl_buf didModifyRange:NSMakeRange(0, data->size)]; + #endif +} + +_SOKOL_PRIVATE int _sg_mtl_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + if (new_frame) { + if (++buf->cmn.active_slot >= buf->cmn.num_slots) { + buf->cmn.active_slot = 0; + } + } + __unsafe_unretained id mtl_buf = _sg_mtl_id(buf->mtl.buf[buf->cmn.active_slot]); + uint8_t* dst_ptr = (uint8_t*) [mtl_buf contents]; + dst_ptr += buf->cmn.append_pos; + memcpy(dst_ptr, data->ptr, data->size); + #if defined(_SG_TARGET_MACOS) + [mtl_buf didModifyRange:NSMakeRange((NSUInteger)buf->cmn.append_pos, (NSUInteger)data->size)]; + #endif + /* NOTE: this is a requirement from WebGPU, but we want identical behaviour across all backends */ + return _sg_roundup((int)data->size, 4); +} + +_SOKOL_PRIVATE void _sg_mtl_update_image(_sg_image_t* img, const sg_image_data* data) { + SOKOL_ASSERT(img && data); + if (++img->cmn.active_slot >= img->cmn.num_slots) { + img->cmn.active_slot = 0; + } + __unsafe_unretained id mtl_tex = _sg_mtl_id(img->mtl.tex[img->cmn.active_slot]); + _sg_mtl_copy_image_data(img, mtl_tex, data); +} + +/*== WEBGPU BACKEND IMPLEMENTATION ===========================================*/ +#elif defined(SOKOL_WGPU) + +_SOKOL_PRIVATE WGPUBufferUsageFlags _sg_wgpu_buffer_usage(sg_buffer_type t, sg_usage u) { + WGPUBufferUsageFlags res = 0; + if (SG_BUFFERTYPE_VERTEXBUFFER == t) { + res |= WGPUBufferUsage_Vertex; + } + else { + res |= WGPUBufferUsage_Index; + } + if (SG_USAGE_IMMUTABLE != u) { + res |= WGPUBufferUsage_CopyDst; + } + return res; +} + +_SOKOL_PRIVATE WGPULoadOp _sg_wgpu_load_op(sg_action a) { + switch (a) { + case SG_ACTION_CLEAR: + case SG_ACTION_DONTCARE: + return WGPULoadOp_Clear; + case SG_ACTION_LOAD: + return WGPULoadOp_Load; + default: + SOKOL_UNREACHABLE; + return (WGPULoadOp)0; + } +} + +_SOKOL_PRIVATE WGPUTextureViewDimension _sg_wgpu_tex_viewdim(sg_image_type t) { + switch (t) { + case SG_IMAGETYPE_2D: return WGPUTextureViewDimension_2D; + case SG_IMAGETYPE_CUBE: return WGPUTextureViewDimension_Cube; + case SG_IMAGETYPE_3D: return WGPUTextureViewDimension_3D; + case SG_IMAGETYPE_ARRAY: return WGPUTextureViewDimension_2DArray; + default: SOKOL_UNREACHABLE; return WGPUTextureViewDimension_Force32; + } +} + +_SOKOL_PRIVATE WGPUTextureComponentType _sg_wgpu_tex_comptype(sg_sampler_type t) { + switch (t) { + case SG_SAMPLERTYPE_FLOAT: return WGPUTextureComponentType_Float; + case SG_SAMPLERTYPE_SINT: return WGPUTextureComponentType_Sint; + case SG_SAMPLERTYPE_UINT: return WGPUTextureComponentType_Uint; + default: SOKOL_UNREACHABLE; return WGPUTextureComponentType_Force32; + } +} + +_SOKOL_PRIVATE WGPUTextureDimension _sg_wgpu_tex_dim(sg_image_type t) { + if (SG_IMAGETYPE_3D == t) { + return WGPUTextureDimension_3D; + } + else { + return WGPUTextureDimension_2D; + } +} + +_SOKOL_PRIVATE WGPUAddressMode _sg_wgpu_sampler_addrmode(sg_wrap m) { + switch (m) { + case SG_WRAP_REPEAT: + return WGPUAddressMode_Repeat; + case SG_WRAP_CLAMP_TO_EDGE: + case SG_WRAP_CLAMP_TO_BORDER: + return WGPUAddressMode_ClampToEdge; + case SG_WRAP_MIRRORED_REPEAT: + return WGPUAddressMode_MirrorRepeat; + default: + SOKOL_UNREACHABLE; + return WGPUAddressMode_Force32; + } +} + +_SOKOL_PRIVATE WGPUFilterMode _sg_wgpu_sampler_minmagfilter(sg_filter f) { + switch (f) { + case SG_FILTER_NEAREST: + case SG_FILTER_NEAREST_MIPMAP_NEAREST: + case SG_FILTER_NEAREST_MIPMAP_LINEAR: + return WGPUFilterMode_Nearest; + case SG_FILTER_LINEAR: + case SG_FILTER_LINEAR_MIPMAP_NEAREST: + case SG_FILTER_LINEAR_MIPMAP_LINEAR: + return WGPUFilterMode_Linear; + default: + SOKOL_UNREACHABLE; + return WGPUFilterMode_Force32; + } +} + +_SOKOL_PRIVATE WGPUFilterMode _sg_wgpu_sampler_mipfilter(sg_filter f) { + switch (f) { + case SG_FILTER_NEAREST: + case SG_FILTER_LINEAR: + case SG_FILTER_NEAREST_MIPMAP_NEAREST: + case SG_FILTER_LINEAR_MIPMAP_NEAREST: + return WGPUFilterMode_Nearest; + case SG_FILTER_NEAREST_MIPMAP_LINEAR: + case SG_FILTER_LINEAR_MIPMAP_LINEAR: + return WGPUFilterMode_Linear; + default: + SOKOL_UNREACHABLE; + return WGPUFilterMode_Force32; + } +} + +_SOKOL_PRIVATE WGPUIndexFormat _sg_wgpu_indexformat(sg_index_type t) { + /* NOTE: there's no WGPUIndexFormat_None */ + return (t == SG_INDEXTYPE_UINT16) ? WGPUIndexFormat_Uint16 : WGPUIndexFormat_Uint32; +} + +_SOKOL_PRIVATE WGPUInputStepMode _sg_wgpu_stepmode(sg_vertex_step s) { + return (s == SG_VERTEXSTEP_PER_VERTEX) ? WGPUInputStepMode_Vertex : WGPUInputStepMode_Instance; +} + +_SOKOL_PRIVATE WGPUVertexFormat _sg_wgpu_vertexformat(sg_vertex_format f) { + switch (f) { + case SG_VERTEXFORMAT_FLOAT: return WGPUVertexFormat_Float; + case SG_VERTEXFORMAT_FLOAT2: return WGPUVertexFormat_Float2; + case SG_VERTEXFORMAT_FLOAT3: return WGPUVertexFormat_Float3; + case SG_VERTEXFORMAT_FLOAT4: return WGPUVertexFormat_Float4; + case SG_VERTEXFORMAT_BYTE4: return WGPUVertexFormat_Char4; + case SG_VERTEXFORMAT_BYTE4N: return WGPUVertexFormat_Char4Norm; + case SG_VERTEXFORMAT_UBYTE4: return WGPUVertexFormat_UChar4; + case SG_VERTEXFORMAT_UBYTE4N: return WGPUVertexFormat_UChar4Norm; + case SG_VERTEXFORMAT_SHORT2: return WGPUVertexFormat_Short2; + case SG_VERTEXFORMAT_SHORT2N: return WGPUVertexFormat_Short2Norm; + case SG_VERTEXFORMAT_USHORT2N: return WGPUVertexFormat_UShort2Norm; + case SG_VERTEXFORMAT_SHORT4: return WGPUVertexFormat_Short4; + case SG_VERTEXFORMAT_SHORT4N: return WGPUVertexFormat_Short4Norm; + case SG_VERTEXFORMAT_USHORT4N: return WGPUVertexFormat_UShort4Norm; + /* FIXME! UINT10_N2 */ + case SG_VERTEXFORMAT_UINT10_N2: + default: + SOKOL_UNREACHABLE; + return WGPUVertexFormat_Force32; + } +} + +_SOKOL_PRIVATE WGPUPrimitiveTopology _sg_wgpu_topology(sg_primitive_type t) { + switch (t) { + case SG_PRIMITIVETYPE_POINTS: return WGPUPrimitiveTopology_PointList; + case SG_PRIMITIVETYPE_LINES: return WGPUPrimitiveTopology_LineList; + case SG_PRIMITIVETYPE_LINE_STRIP: return WGPUPrimitiveTopology_LineStrip; + case SG_PRIMITIVETYPE_TRIANGLES: return WGPUPrimitiveTopology_TriangleList; + case SG_PRIMITIVETYPE_TRIANGLE_STRIP: return WGPUPrimitiveTopology_TriangleStrip; + default: SOKOL_UNREACHABLE; return WGPUPrimitiveTopology_Force32; + } +} + +_SOKOL_PRIVATE WGPUFrontFace _sg_wgpu_frontface(sg_face_winding fw) { + return (fw == SG_FACEWINDING_CCW) ? WGPUFrontFace_CCW : WGPUFrontFace_CW; +} + +_SOKOL_PRIVATE WGPUCullMode _sg_wgpu_cullmode(sg_cull_mode cm) { + switch (cm) { + case SG_CULLMODE_NONE: return WGPUCullMode_None; + case SG_CULLMODE_FRONT: return WGPUCullMode_Front; + case SG_CULLMODE_BACK: return WGPUCullMode_Back; + default: SOKOL_UNREACHABLE; return WGPUCullMode_Force32; + } +} + +_SOKOL_PRIVATE WGPUTextureFormat _sg_wgpu_textureformat(sg_pixel_format p) { + switch (p) { + case SG_PIXELFORMAT_NONE: return WGPUTextureFormat_Undefined; + case SG_PIXELFORMAT_R8: return WGPUTextureFormat_R8Unorm; + case SG_PIXELFORMAT_R8SN: return WGPUTextureFormat_R8Snorm; + case SG_PIXELFORMAT_R8UI: return WGPUTextureFormat_R8Uint; + case SG_PIXELFORMAT_R8SI: return WGPUTextureFormat_R8Sint; + case SG_PIXELFORMAT_R16UI: return WGPUTextureFormat_R16Uint; + case SG_PIXELFORMAT_R16SI: return WGPUTextureFormat_R16Sint; + case SG_PIXELFORMAT_R16F: return WGPUTextureFormat_R16Float; + case SG_PIXELFORMAT_RG8: return WGPUTextureFormat_RG8Unorm; + case SG_PIXELFORMAT_RG8SN: return WGPUTextureFormat_RG8Snorm; + case SG_PIXELFORMAT_RG8UI: return WGPUTextureFormat_RG8Uint; + case SG_PIXELFORMAT_RG8SI: return WGPUTextureFormat_RG8Sint; + case SG_PIXELFORMAT_R32UI: return WGPUTextureFormat_R32Uint; + case SG_PIXELFORMAT_R32SI: return WGPUTextureFormat_R32Sint; + case SG_PIXELFORMAT_R32F: return WGPUTextureFormat_R32Float; + case SG_PIXELFORMAT_RG16UI: return WGPUTextureFormat_RG16Uint; + case SG_PIXELFORMAT_RG16SI: return WGPUTextureFormat_RG16Sint; + case SG_PIXELFORMAT_RG16F: return WGPUTextureFormat_RG16Float; + case SG_PIXELFORMAT_RGBA8: return WGPUTextureFormat_RGBA8Unorm; + case SG_PIXELFORMAT_RGBA8SN: return WGPUTextureFormat_RGBA8Snorm; + case SG_PIXELFORMAT_RGBA8UI: return WGPUTextureFormat_RGBA8Uint; + case SG_PIXELFORMAT_RGBA8SI: return WGPUTextureFormat_RGBA8Sint; + case SG_PIXELFORMAT_BGRA8: return WGPUTextureFormat_BGRA8Unorm; + case SG_PIXELFORMAT_RGB10A2: return WGPUTextureFormat_RGB10A2Unorm; + case SG_PIXELFORMAT_RG11B10F: return WGPUTextureFormat_RG11B10Float; + case SG_PIXELFORMAT_RG32UI: return WGPUTextureFormat_RG32Uint; + case SG_PIXELFORMAT_RG32SI: return WGPUTextureFormat_RG32Sint; + case SG_PIXELFORMAT_RG32F: return WGPUTextureFormat_RG32Float; + case SG_PIXELFORMAT_RGBA16UI: return WGPUTextureFormat_RGBA16Uint; + case SG_PIXELFORMAT_RGBA16SI: return WGPUTextureFormat_RGBA16Sint; + case SG_PIXELFORMAT_RGBA16F: return WGPUTextureFormat_RGBA16Float; + case SG_PIXELFORMAT_RGBA32UI: return WGPUTextureFormat_RGBA32Uint; + case SG_PIXELFORMAT_RGBA32SI: return WGPUTextureFormat_RGBA32Sint; + case SG_PIXELFORMAT_RGBA32F: return WGPUTextureFormat_RGBA32Float; + case SG_PIXELFORMAT_DEPTH: return WGPUTextureFormat_Depth24Plus; + case SG_PIXELFORMAT_DEPTH_STENCIL: return WGPUTextureFormat_Depth24PlusStencil8; + case SG_PIXELFORMAT_BC1_RGBA: return WGPUTextureFormat_BC1RGBAUnorm; + case SG_PIXELFORMAT_BC2_RGBA: return WGPUTextureFormat_BC2RGBAUnorm; + case SG_PIXELFORMAT_BC3_RGBA: return WGPUTextureFormat_BC3RGBAUnorm; + case SG_PIXELFORMAT_BC4_R: return WGPUTextureFormat_BC4RUnorm; + case SG_PIXELFORMAT_BC4_RSN: return WGPUTextureFormat_BC4RSnorm; + case SG_PIXELFORMAT_BC5_RG: return WGPUTextureFormat_BC5RGUnorm; + case SG_PIXELFORMAT_BC5_RGSN: return WGPUTextureFormat_BC5RGSnorm; + case SG_PIXELFORMAT_BC6H_RGBF: return WGPUTextureFormat_BC6HRGBSfloat; + case SG_PIXELFORMAT_BC6H_RGBUF: return WGPUTextureFormat_BC6HRGBUfloat; + case SG_PIXELFORMAT_BC7_RGBA: return WGPUTextureFormat_BC7RGBAUnorm; + + /* NOT SUPPORTED */ + case SG_PIXELFORMAT_R16: + case SG_PIXELFORMAT_R16SN: + case SG_PIXELFORMAT_RG16: + case SG_PIXELFORMAT_RG16SN: + case SG_PIXELFORMAT_RGBA16: + case SG_PIXELFORMAT_RGBA16SN: + case SG_PIXELFORMAT_PVRTC_RGB_2BPP: + case SG_PIXELFORMAT_PVRTC_RGB_4BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_2BPP: + case SG_PIXELFORMAT_PVRTC_RGBA_4BPP: + case SG_PIXELFORMAT_ETC2_RGB8: + case SG_PIXELFORMAT_ETC2_RGB8A1: + case SG_PIXELFORMAT_ETC2_RGBA8: + case SG_PIXELFORMAT_ETC2_RG11: + case SG_PIXELFORMAT_ETC2_RG11SN: + default: + SOKOL_UNREACHABLE; + return WGPUTextureFormat_Force32; + } +} + +/* +FIXME ??? this isn't needed anywhere? +_SOKOL_PRIVATE WGPUTextureAspect _sg_wgpu_texture_aspect(sg_pixel_format fmt) { + if (_sg_is_valid_rendertarget_depth_format(fmt)) { + if (!_sg_is_depth_stencil_format(fmt)) { + return WGPUTextureAspect_DepthOnly; + } + } + return WGPUTextureAspect_All; +} +*/ + +_SOKOL_PRIVATE WGPUCompareFunction _sg_wgpu_comparefunc(sg_compare_func f) { + switch (f) { + case SG_COMPAREFUNC_NEVER: return WGPUCompareFunction_Never; + case SG_COMPAREFUNC_LESS: return WGPUCompareFunction_Less; + case SG_COMPAREFUNC_EQUAL: return WGPUCompareFunction_Equal; + case SG_COMPAREFUNC_LESS_EQUAL: return WGPUCompareFunction_LessEqual; + case SG_COMPAREFUNC_GREATER: return WGPUCompareFunction_Greater; + case SG_COMPAREFUNC_NOT_EQUAL: return WGPUCompareFunction_NotEqual; + case SG_COMPAREFUNC_GREATER_EQUAL: return WGPUCompareFunction_GreaterEqual; + case SG_COMPAREFUNC_ALWAYS: return WGPUCompareFunction_Always; + default: SOKOL_UNREACHABLE; return WGPUCompareFunction_Force32; + } +} + +_SOKOL_PRIVATE WGPUStencilOperation _sg_wgpu_stencilop(sg_stencil_op op) { + switch (op) { + case SG_STENCILOP_KEEP: return WGPUStencilOperation_Keep; + case SG_STENCILOP_ZERO: return WGPUStencilOperation_Zero; + case SG_STENCILOP_REPLACE: return WGPUStencilOperation_Replace; + case SG_STENCILOP_INCR_CLAMP: return WGPUStencilOperation_IncrementClamp; + case SG_STENCILOP_DECR_CLAMP: return WGPUStencilOperation_DecrementClamp; + case SG_STENCILOP_INVERT: return WGPUStencilOperation_Invert; + case SG_STENCILOP_INCR_WRAP: return WGPUStencilOperation_IncrementWrap; + case SG_STENCILOP_DECR_WRAP: return WGPUStencilOperation_DecrementWrap; + default: SOKOL_UNREACHABLE; return WGPUStencilOperation_Force32; + } +} + +_SOKOL_PRIVATE WGPUBlendOperation _sg_wgpu_blendop(sg_blend_op op) { + switch (op) { + case SG_BLENDOP_ADD: return WGPUBlendOperation_Add; + case SG_BLENDOP_SUBTRACT: return WGPUBlendOperation_Subtract; + case SG_BLENDOP_REVERSE_SUBTRACT: return WGPUBlendOperation_ReverseSubtract; + default: SOKOL_UNREACHABLE; return WGPUBlendOperation_Force32; + } +} + +_SOKOL_PRIVATE WGPUBlendFactor _sg_wgpu_blendfactor(sg_blend_factor f) { + switch (f) { + case SG_BLENDFACTOR_ZERO: return WGPUBlendFactor_Zero; + case SG_BLENDFACTOR_ONE: return WGPUBlendFactor_One; + case SG_BLENDFACTOR_SRC_COLOR: return WGPUBlendFactor_SrcColor; + case SG_BLENDFACTOR_ONE_MINUS_SRC_COLOR: return WGPUBlendFactor_OneMinusSrcColor; + case SG_BLENDFACTOR_SRC_ALPHA: return WGPUBlendFactor_SrcAlpha; + case SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA: return WGPUBlendFactor_OneMinusSrcAlpha; + case SG_BLENDFACTOR_DST_COLOR: return WGPUBlendFactor_DstColor; + case SG_BLENDFACTOR_ONE_MINUS_DST_COLOR: return WGPUBlendFactor_OneMinusDstColor; + case SG_BLENDFACTOR_DST_ALPHA: return WGPUBlendFactor_DstAlpha; + case SG_BLENDFACTOR_ONE_MINUS_DST_ALPHA: return WGPUBlendFactor_OneMinusDstAlpha; + case SG_BLENDFACTOR_SRC_ALPHA_SATURATED: return WGPUBlendFactor_SrcAlphaSaturated; + case SG_BLENDFACTOR_BLEND_COLOR: return WGPUBlendFactor_BlendColor; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_COLOR: return WGPUBlendFactor_OneMinusBlendColor; + /* FIXME: separate blend alpha value not supported? */ + case SG_BLENDFACTOR_BLEND_ALPHA: return WGPUBlendFactor_BlendColor; + case SG_BLENDFACTOR_ONE_MINUS_BLEND_ALPHA: return WGPUBlendFactor_OneMinusBlendColor; + default: + SOKOL_UNREACHABLE; return WGPUBlendFactor_Force32; + } +} + +_SOKOL_PRIVATE WGPUColorWriteMaskFlags _sg_wgpu_colorwritemask(uint8_t m) { + WGPUColorWriteMaskFlags res = 0; + if (0 != (m & SG_COLORMASK_R)) { + res |= WGPUColorWriteMask_Red; + } + if (0 != (m & SG_COLORMASK_G)) { + res |= WGPUColorWriteMask_Green; + } + if (0 != (m & SG_COLORMASK_B)) { + res |= WGPUColorWriteMask_Blue; + } + if (0 != (m & SG_COLORMASK_A)) { + res |= WGPUColorWriteMask_Alpha; + } + return res; +} + +_SOKOL_PRIVATE void _sg_wgpu_init_caps(void) { + _sg.backend = SG_BACKEND_WGPU; + _sg.features.instancing = true; + _sg.features.origin_top_left = true; + _sg.features.multiple_render_targets = true; + _sg.features.msaa_render_targets = true; + _sg.features.imagetype_3d = true; + _sg.features.imagetype_array = true; + _sg.features.image_clamp_to_border = false; + _sg.features.mrt_independent_blend_state = true; + _sg.features.mrt_independent_write_mask = true; + + /* FIXME: max images size??? */ + _sg.limits.max_image_size_2d = 8 * 1024; + _sg.limits.max_image_size_cube = 8 * 1024; + _sg.limits.max_image_size_3d = 2 * 1024; + _sg.limits.max_image_size_array = 8 * 1024; + _sg.limits.max_image_array_layers = 2 * 1024; + _sg.limits.max_vertex_attrs = SG_MAX_VERTEX_ATTRIBUTES; + + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_R8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R8SI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_R16SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_R16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RG8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG8SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_R32SI]); + _sg_pixelformat_sbr(&_sg.formats[SG_PIXELFORMAT_R32F]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RG16SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RG16F]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA8]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_RGBA8SN]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA8SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_BGRA8]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGB10A2]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RG32SI]); + _sg_pixelformat_sbr(&_sg.formats[SG_PIXELFORMAT_RG32F]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16UI]); + _sg_pixelformat_srm(&_sg.formats[SG_PIXELFORMAT_RGBA16SI]); + _sg_pixelformat_all(&_sg.formats[SG_PIXELFORMAT_RGBA16F]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32UI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32SI]); + _sg_pixelformat_sr(&_sg.formats[SG_PIXELFORMAT_RGBA32F]); + _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH]); + _sg_pixelformat_srmd(&_sg.formats[SG_PIXELFORMAT_DEPTH_STENCIL]); + + /* FIXME FIXME FIXME: need to check if BC texture compression is + actually supported, currently the WebGPU C-API doesn't allow this + */ + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC1_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC2_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC3_RGBA]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_R]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC4_RSN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RG]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC5_RGSN]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBF]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC6H_RGBUF]); + _sg_pixelformat_sf(&_sg.formats[SG_PIXELFORMAT_BC7_RGBA]); +} + +/* + WGPU uniform buffer pool implementation: + + At start of frame, a mapped buffer is grabbed from the pool, + or a new buffer is created if there is no mapped buffer available. + + At end of frame, the current buffer is unmapped before queue submit, + and async-mapped immediately again. + + UNIFORM BUFFER FIXME: + + - As per WebGPU spec, it should be possible to create a Uniform|MapWrite + buffer, but this isn't currently allowed in Dawn. +*/ +_SOKOL_PRIVATE void _sg_wgpu_ubpool_init(const sg_desc* desc) { + + /* Add the max-uniform-update size (64 KB) to the requested buffer size, + this is to prevent validation errors in the WebGPU implementation + if the entire buffer size is used per frame. 64 KB is the allowed + max uniform update size on NVIDIA + */ + _sg.wgpu.ub.num_bytes = desc->uniform_buffer_size + _SG_WGPU_MAX_UNIFORM_UPDATE_SIZE; + + WGPUBufferDescriptor ub_desc; + memset(&ub_desc, 0, sizeof(ub_desc)); + ub_desc.size = _sg.wgpu.ub.num_bytes; + ub_desc.usage = WGPUBufferUsage_Uniform|WGPUBufferUsage_CopyDst; + _sg.wgpu.ub.buf = wgpuDeviceCreateBuffer(_sg.wgpu.dev, &ub_desc); + SOKOL_ASSERT(_sg.wgpu.ub.buf); + + WGPUBindGroupLayoutBinding ub_bglb_desc[SG_NUM_SHADER_STAGES][SG_MAX_SHADERSTAGE_UBS]; + memset(ub_bglb_desc, 0, sizeof(ub_bglb_desc)); + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + WGPUShaderStage vis = (stage_index == SG_SHADERSTAGE_VS) ? WGPUShaderStage_Vertex : WGPUShaderStage_Fragment; + for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { + int bind_index = stage_index * SG_MAX_SHADERSTAGE_UBS + ub_index; + ub_bglb_desc[stage_index][ub_index].binding = bind_index; + ub_bglb_desc[stage_index][ub_index].visibility = vis; + ub_bglb_desc[stage_index][ub_index].type = WGPUBindingType_UniformBuffer; + ub_bglb_desc[stage_index][ub_index].hasDynamicOffset = true; + } + } + + WGPUBindGroupLayoutDescriptor ub_bgl_desc; + memset(&ub_bgl_desc, 0, sizeof(ub_bgl_desc)); + ub_bgl_desc.bindingCount = SG_NUM_SHADER_STAGES * SG_MAX_SHADERSTAGE_UBS; + ub_bgl_desc.bindings = &ub_bglb_desc[0][0]; + _sg.wgpu.ub.bindgroup_layout = wgpuDeviceCreateBindGroupLayout(_sg.wgpu.dev, &ub_bgl_desc); + SOKOL_ASSERT(_sg.wgpu.ub.bindgroup_layout); + + WGPUBindGroupBinding ub_bgb[SG_NUM_SHADER_STAGES][SG_MAX_SHADERSTAGE_UBS]; + memset(ub_bgb, 0, sizeof(ub_bgb)); + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { + int bind_index = stage_index * SG_MAX_SHADERSTAGE_UBS + ub_index; + ub_bgb[stage_index][ub_index].binding = bind_index; + ub_bgb[stage_index][ub_index].buffer = _sg.wgpu.ub.buf; + // FIXME FIXME FIXME FIXME: HACK FOR VALIDATION BUG IN DAWN + ub_bgb[stage_index][ub_index].size = (1<<16); + } + } + WGPUBindGroupDescriptor bg_desc; + memset(&bg_desc, 0, sizeof(bg_desc)); + bg_desc.layout = _sg.wgpu.ub.bindgroup_layout; + bg_desc.bindingCount = SG_NUM_SHADER_STAGES * SG_MAX_SHADERSTAGE_UBS; + bg_desc.bindings = &ub_bgb[0][0]; + _sg.wgpu.ub.bindgroup = wgpuDeviceCreateBindGroup(_sg.wgpu.dev, &bg_desc); + SOKOL_ASSERT(_sg.wgpu.ub.bindgroup); +} + +_SOKOL_PRIVATE void _sg_wgpu_ubpool_discard(void) { + if (_sg.wgpu.ub.buf) { + wgpuBufferRelease(_sg.wgpu.ub.buf); + _sg.wgpu.ub.buf = 0; + } + if (_sg.wgpu.ub.bindgroup) { + wgpuBindGroupRelease(_sg.wgpu.ub.bindgroup); + _sg.wgpu.ub.bindgroup = 0; + } + if (_sg.wgpu.ub.bindgroup_layout) { + wgpuBindGroupLayoutRelease(_sg.wgpu.ub.bindgroup_layout); + _sg.wgpu.ub.bindgroup_layout = 0; + } + for (int i = 0; i < _sg.wgpu.ub.stage.num; i++) { + if (_sg.wgpu.ub.stage.buf[i]) { + wgpuBufferRelease(_sg.wgpu.ub.stage.buf[i]); + _sg.wgpu.ub.stage.buf[i] = 0; + _sg.wgpu.ub.stage.ptr[i] = 0; + } + } +} + +_SOKOL_PRIVATE void _sg_wgpu_ubpool_mapped_callback(WGPUBufferMapAsyncStatus status, void* data, uint64_t data_len, void* user_data) { + if (!_sg.wgpu.valid) { + return; + } + /* FIXME: better handling for this */ + if (WGPUBufferMapAsyncStatus_Success != status) { + SOKOL_LOG("Mapping uniform buffer failed!\n"); + SOKOL_ASSERT(false); + } + SOKOL_ASSERT(data && (data_len == _sg.wgpu.ub.num_bytes)); + int index = (int)(intptr_t) user_data; + SOKOL_ASSERT(index < _sg.wgpu.ub.stage.num); + SOKOL_ASSERT(0 == _sg.wgpu.ub.stage.ptr[index]); + _sg.wgpu.ub.stage.ptr[index] = (uint8_t*) data; +} + +_SOKOL_PRIVATE void _sg_wgpu_ubpool_next_frame(bool first_frame) { + + /* immediately request a new mapping for the last frame's current staging buffer */ + if (!first_frame) { + WGPUBuffer ub_src = _sg.wgpu.ub.stage.buf[_sg.wgpu.ub.stage.cur]; + wgpuBufferMapWriteAsync(ub_src, _sg_wgpu_ubpool_mapped_callback, (void*)(intptr_t)_sg.wgpu.ub.stage.cur); + } + + /* rewind per-frame offsets */ + _sg.wgpu.ub.offset = 0; + memset(&_sg.wgpu.ub.bind_offsets, 0, sizeof(_sg.wgpu.ub.bind_offsets)); + + /* check if a mapped staging buffer is available, otherwise create one */ + for (int i = 0; i < _sg.wgpu.ub.stage.num; i++) { + if (_sg.wgpu.ub.stage.ptr[i]) { + _sg.wgpu.ub.stage.cur = i; + return; + } + } + + /* no mapped uniform buffer available, create one */ + SOKOL_ASSERT(_sg.wgpu.ub.stage.num < _SG_WGPU_STAGING_PIPELINE_SIZE); + _sg.wgpu.ub.stage.cur = _sg.wgpu.ub.stage.num++; + const int cur = _sg.wgpu.ub.stage.cur; + + WGPUBufferDescriptor desc; + memset(&desc, 0, sizeof(desc)); + desc.size = _sg.wgpu.ub.num_bytes; + desc.usage = WGPUBufferUsage_CopySrc|WGPUBufferUsage_MapWrite; + WGPUCreateBufferMappedResult res = wgpuDeviceCreateBufferMapped(_sg.wgpu.dev, &desc); + _sg.wgpu.ub.stage.buf[cur] = res.buffer; + _sg.wgpu.ub.stage.ptr[cur] = (uint8_t*) res.data; + SOKOL_ASSERT(_sg.wgpu.ub.stage.buf[cur]); + SOKOL_ASSERT(_sg.wgpu.ub.stage.ptr[cur]); + SOKOL_ASSERT(res.dataLength == _sg.wgpu.ub.num_bytes); +} + +_SOKOL_PRIVATE void _sg_wgpu_ubpool_flush(void) { + /* unmap staging buffer and copy to uniform buffer */ + const int cur = _sg.wgpu.ub.stage.cur; + SOKOL_ASSERT(_sg.wgpu.ub.stage.ptr[cur]); + _sg.wgpu.ub.stage.ptr[cur] = 0; + WGPUBuffer src_buf = _sg.wgpu.ub.stage.buf[cur]; + wgpuBufferUnmap(src_buf); + if (_sg.wgpu.ub.offset > 0) { + WGPUBuffer dst_buf = _sg.wgpu.ub.buf; + wgpuCommandEncoderCopyBufferToBuffer(_sg.wgpu.render_cmd_enc, src_buf, 0, dst_buf, 0, _sg.wgpu.ub.offset); + } +} + +/* helper function to compute number of bytes needed in staging buffer to copy image data */ +_SOKOL_PRIVATE uint32_t _sg_wgpu_image_data_buffer_size(const _sg_image_t* img) { + uint32_t num_bytes = 0; + const uint32_t num_faces = (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6:1; + const uint32_t num_slices = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? img->cmn.num_slices : 1; + for (int mip_index = 0; mip_index < img->cmn.num_mipmaps; mip_index++) { + const uint32_t mip_width = _sg_max(img->cmn.width >> mip_index, 1); + const uint32_t mip_height = _sg_max(img->cmn.height >> mip_index, 1); + /* row-pitch must be 256-aligend */ + const uint32_t bytes_per_slice = _sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, _SG_WGPU_ROWPITCH_ALIGN); + num_bytes += bytes_per_slice * num_slices * num_faces; + } + return num_bytes; +} + +/* helper function to copy image data into a texture via a staging buffer, returns number of + bytes copied +*/ +_SOKOL_PRIVATE uint32_t _sg_wgpu_copy_image_data(WGPUBuffer stg_buf, uint8_t* stg_base_ptr, uint32_t stg_base_offset, _sg_image_t* img, const sg_image_data* data) { + SOKOL_ASSERT(_sg.wgpu.staging_cmd_enc); + SOKOL_ASSERT(stg_buf && stg_base_ptr); + SOKOL_ASSERT(img); + SOKOL_ASSERT(data); + uint32_t stg_offset = stg_base_offset; + const uint32_t num_faces = (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6:1; + const uint32_t num_slices = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? img->cmn.num_slices : 1; + const sg_pixel_format fmt = img->cmn.pixel_format; + WGPUBufferCopyView src_view; + memset(&src_view, 0, sizeof(src_view)); + src_view.buffer = stg_buf; + WGPUTextureCopyView dst_view; + memset(&dst_view, 0, sizeof(dst_view)); + dst_view.texture = img->wgpu.tex; + WGPUExtent3D extent; + memset(&extent, 0, sizeof(extent)); + + for (uint32_t face_index = 0; face_index < num_faces; face_index++) { + for (uint32_t mip_index = 0; mip_index < (uint32_t)img->cmn.num_mipmaps; mip_index++) { + SOKOL_ASSERT(data->subimage[face_index][mip_index].ptr); + SOKOL_ASSERT(data->subimage[face_index][mip_index].size > 0); + const uint8_t* src_base_ptr = (const uint8_t*)data->subimage[face_index][mip_index].ptr; + SOKOL_ASSERT(src_base_ptr); + uint8_t* dst_base_ptr = stg_base_ptr + stg_offset; + + const uint32_t mip_width = _sg_max(img->cmn.width >> mip_index, 1); + const uint32_t mip_height = _sg_max(img->cmn.height >> mip_index, 1); + const uint32_t mip_depth = (img->cmn.type == SG_IMAGETYPE_3D) ? _sg_max(img->cmn.num_slices >> mip_index, 1) : 1; + const uint32_t num_rows = _sg_num_rows(fmt, mip_height); + const uint32_t src_bytes_per_row = _sg_row_pitch(fmt, mip_width, 1); + const uint32_t dst_bytes_per_row = _sg_row_pitch(fmt, mip_width, _SG_WGPU_ROWPITCH_ALIGN); + const uint32_t src_bytes_per_slice = _sg_surface_pitch(fmt, mip_width, mip_height, 1); + const uint32_t dst_bytes_per_slice = _sg_surface_pitch(fmt, mip_width, mip_height, _SG_WGPU_ROWPITCH_ALIGN); + SOKOL_ASSERT((uint32_t)data->subimage[face_index][mip_index].size == (src_bytes_per_slice * num_slices)); + SOKOL_ASSERT(src_bytes_per_row <= dst_bytes_per_row); + SOKOL_ASSERT(src_bytes_per_slice == (src_bytes_per_row * num_rows)); + SOKOL_ASSERT(dst_bytes_per_slice == (dst_bytes_per_row * num_rows)); + _SOKOL_UNUSED(src_bytes_per_slice); + + /* copy data into mapped staging buffer */ + if (src_bytes_per_row == dst_bytes_per_row) { + /* can do a single memcpy */ + uint32_t num_bytes = data->subimage[face_index][mip_index].size; + memcpy(dst_base_ptr, src_base_ptr, num_bytes); + } + else { + /* src/dst pitch doesn't match, need to copy row by row */ + uint8_t* dst_ptr = dst_base_ptr; + const uint8_t* src_ptr = src_base_ptr; + for (uint32_t slice_index = 0; slice_index < num_slices; slice_index++) { + SOKOL_ASSERT(dst_ptr == dst_base_ptr + slice_index * dst_bytes_per_slice); + for (uint32_t row_index = 0; row_index < num_rows; row_index++) { + memcpy(dst_ptr, src_ptr, src_bytes_per_row); + src_ptr += src_bytes_per_row; + dst_ptr += dst_bytes_per_row; + } + } + } + + /* record the staging copy operation into command encoder */ + src_view.imageHeight = mip_height; + src_view.rowPitch = dst_bytes_per_row; + dst_view.mipLevel = mip_index; + extent.width = mip_width; + extent.height = mip_height; + extent.depth = mip_depth; + SOKOL_ASSERT((img->cmn.type != SG_IMAGETYPE_CUBE) || (num_slices == 1)); + for (uint32_t slice_index = 0; slice_index < num_slices; slice_index++) { + const uint32_t layer_index = (img->cmn.type == SG_IMAGETYPE_ARRAY) ? slice_index : face_index; + src_view.offset = stg_offset; + dst_view.arrayLayer = layer_index; + wgpuCommandEncoderCopyBufferToTexture(_sg.wgpu.staging_cmd_enc, &src_view, &dst_view, &extent); + stg_offset += dst_bytes_per_slice; + SOKOL_ASSERT(stg_offset <= _sg.wgpu.staging.num_bytes); + } + } + } + SOKOL_ASSERT(stg_offset >= stg_base_offset); + return (stg_offset - stg_base_offset); +} + +/* + The WGPU staging buffer implementation: + + Very similar to the uniform buffer pool, there's a pool of big + per-frame staging buffers, each must be big enough to hold + all data uploaded to dynamic resources for one frame. + + Staging buffers are created on demand and reused, because the + 'frame pipeline depth' of WGPU isn't predictable. + + The difference to the uniform buffer system is that there isn't + a 1:1 relationship for source- and destination for the + data-copy operation. There's always one staging buffer as copy-source + per frame, but many copy-destinations (regular vertex/index buffers + or images). Instead of one big copy-operation at the end of the frame, + multiple copy-operations will be written throughout the frame. +*/ +_SOKOL_PRIVATE void _sg_wgpu_staging_init(const sg_desc* desc) { + SOKOL_ASSERT(desc && (desc->staging_buffer_size > 0)); + _sg.wgpu.staging.num_bytes = desc->staging_buffer_size; + /* there's actually nothing more to do here */ +} + +_SOKOL_PRIVATE void _sg_wgpu_staging_discard(void) { + for (int i = 0; i < _sg.wgpu.staging.num; i++) { + if (_sg.wgpu.staging.buf[i]) { + wgpuBufferRelease(_sg.wgpu.staging.buf[i]); + _sg.wgpu.staging.buf[i] = 0; + _sg.wgpu.staging.ptr[i] = 0; + } + } +} + +_SOKOL_PRIVATE void _sg_wgpu_staging_mapped_callback(WGPUBufferMapAsyncStatus status, void* data, uint64_t data_len, void* user_data) { + if (!_sg.wgpu.valid) { + return; + } + /* FIXME: better handling for this */ + if (WGPUBufferMapAsyncStatus_Success != status) { + SOKOL_ASSERT("Mapping staging buffer failed!\n"); + SOKOL_ASSERT(false); + } + SOKOL_ASSERT(data && (data_len == _sg.wgpu.staging.num_bytes)); + int index = (int)(intptr_t) user_data; + SOKOL_ASSERT(index < _sg.wgpu.staging.num); + SOKOL_ASSERT(0 == _sg.wgpu.staging.ptr[index]); + _sg.wgpu.staging.ptr[index] = (uint8_t*) data; +} + +_SOKOL_PRIVATE void _sg_wgpu_staging_next_frame(bool first_frame) { + + /* immediately request a new mapping for the last frame's current staging buffer */ + if (!first_frame) { + WGPUBuffer cur_buf = _sg.wgpu.staging.buf[_sg.wgpu.staging.cur]; + wgpuBufferMapWriteAsync(cur_buf, _sg_wgpu_staging_mapped_callback, (void*)(intptr_t)_sg.wgpu.staging.cur); + } + + /* rewind staging-buffer offset */ + _sg.wgpu.staging.offset = 0; + + /* check if mapped staging buffer is available, otherwise create one */ + for (int i = 0; i < _sg.wgpu.staging.num; i++) { + if (_sg.wgpu.staging.ptr[i]) { + _sg.wgpu.staging.cur = i; + return; + } + } + + /* no mapped buffer available, create one */ + SOKOL_ASSERT(_sg.wgpu.staging.num < _SG_WGPU_STAGING_PIPELINE_SIZE); + _sg.wgpu.staging.cur = _sg.wgpu.staging.num++; + const int cur = _sg.wgpu.staging.cur; + + WGPUBufferDescriptor desc; + memset(&desc, 0, sizeof(desc)); + desc.size = _sg.wgpu.staging.num_bytes; + desc.usage = WGPUBufferUsage_CopySrc|WGPUBufferUsage_MapWrite; + WGPUCreateBufferMappedResult res = wgpuDeviceCreateBufferMapped(_sg.wgpu.dev, &desc); + _sg.wgpu.staging.buf[cur] = res.buffer; + _sg.wgpu.staging.ptr[cur] = (uint8_t*) res.data; + SOKOL_ASSERT(_sg.wgpu.staging.buf[cur]); + SOKOL_ASSERT(_sg.wgpu.staging.ptr[cur]); + SOKOL_ASSERT(res.dataLength == _sg.wgpu.staging.num_bytes); +} + +_SOKOL_PRIVATE uint32_t _sg_wgpu_staging_copy_to_buffer(WGPUBuffer dst_buf, uint32_t dst_buf_offset, const void* data, uint32_t data_num_bytes) { + /* Copy a chunk of data into the staging buffer, and record a blit-operation into + the command encoder, bump the offset for the next data chunk, return 0 if there + was not enough room in the staging buffer, return the number of actually + copied bytes on success. + + NOTE: that the number of staging bytes to be copied must be a multiple of 4. + + */ + SOKOL_ASSERT(_sg.wgpu.staging_cmd_enc); + SOKOL_ASSERT((dst_buf_offset & 3) == 0); + SOKOL_ASSERT(data_num_bytes > 0); + uint32_t copy_num_bytes = _sg_roundup(data_num_bytes, 4); + if ((_sg.wgpu.staging.offset + copy_num_bytes) >= _sg.wgpu.staging.num_bytes) { + SOKOL_LOG("WGPU: Per frame staging buffer full (in _sg_wgpu_staging_copy_to_buffer())!\n"); + return false; + } + const int cur = _sg.wgpu.staging.cur; + SOKOL_ASSERT(_sg.wgpu.staging.ptr[cur]); + uint32_t stg_buf_offset = _sg.wgpu.staging.offset; + uint8_t* stg_ptr = _sg.wgpu.staging.ptr[cur] + stg_buf_offset; + memcpy(stg_ptr, data, data_num_bytes); + WGPUBuffer stg_buf = _sg.wgpu.staging.buf[cur]; + wgpuCommandEncoderCopyBufferToBuffer(_sg.wgpu.staging_cmd_enc, stg_buf, stg_buf_offset, dst_buf, dst_buf_offset, copy_num_bytes); + _sg.wgpu.staging.offset = stg_buf_offset + copy_num_bytes; + return copy_num_bytes; +} + +_SOKOL_PRIVATE bool _sg_wgpu_staging_copy_to_texture(_sg_image_t* img, const sg_image_data* data) { + /* similar to _sg_wgpu_staging_copy_to_buffer(), but with image data instead */ + SOKOL_ASSERT(_sg.wgpu.staging_cmd_enc); + uint32_t num_bytes = _sg_wgpu_image_data_buffer_size(img); + if ((_sg.wgpu.staging.offset + num_bytes) >= _sg.wgpu.staging.num_bytes) { + SOKOL_LOG("WGPU: Per frame staging buffer full (in _sg_wgpu_staging_copy_to_texture)!\n"); + return false; + } + const int cur = _sg.wgpu.staging.cur; + SOKOL_ASSERT(_sg.wgpu.staging.ptr[cur]); + uint32_t stg_offset = _sg.wgpu.staging.offset; + uint8_t* stg_ptr = _sg.wgpu.staging.ptr[cur]; + WGPUBuffer stg_buf = _sg.wgpu.staging.buf[cur]; + uint32_t bytes_copied = _sg_wgpu_copy_image_data(stg_buf, stg_ptr, stg_offset, img, data); + _SOKOL_UNUSED(bytes_copied); + SOKOL_ASSERT(bytes_copied == num_bytes); + _sg.wgpu.staging.offset = _sg_roundup(stg_offset + num_bytes, _SG_WGPU_STAGING_ALIGN); + return true; +} + +_SOKOL_PRIVATE void _sg_wgpu_staging_unmap(void) { + /* called at end of frame before queue-submit */ + const int cur = _sg.wgpu.staging.cur; + SOKOL_ASSERT(_sg.wgpu.staging.ptr[cur]); + _sg.wgpu.staging.ptr[cur] = 0; + wgpuBufferUnmap(_sg.wgpu.staging.buf[cur]); +} + +/*--- WGPU sampler cache functions ---*/ +_SOKOL_PRIVATE void _sg_wgpu_init_sampler_cache(const sg_desc* desc) { + SOKOL_ASSERT(desc->sampler_cache_size > 0); + _sg_smpcache_init(&_sg.wgpu.sampler_cache, desc->sampler_cache_size); +} + +_SOKOL_PRIVATE void _sg_wgpu_destroy_sampler_cache(void) { + SOKOL_ASSERT(_sg.wgpu.sampler_cache.items); + SOKOL_ASSERT(_sg.wgpu.sampler_cache.num_items <= _sg.wgpu.sampler_cache.capacity); + for (int i = 0; i < _sg.wgpu.sampler_cache.num_items; i++) { + wgpuSamplerRelease((WGPUSampler)_sg_smpcache_sampler(&_sg.wgpu.sampler_cache, i)); + } + _sg_smpcache_discard(&_sg.wgpu.sampler_cache); +} + +_SOKOL_PRIVATE WGPUSampler _sg_wgpu_create_sampler(const sg_image_desc* img_desc) { + SOKOL_ASSERT(img_desc); + int index = _sg_smpcache_find_item(&_sg.wgpu.sampler_cache, img_desc); + if (index >= 0) { + /* reuse existing sampler */ + return (WGPUSampler) _sg_smpcache_sampler(&_sg.wgpu.sampler_cache, index); + } + else { + /* create a new WGPU sampler and add to sampler cache */ + /* FIXME: anisotropic filtering not supported? */ + WGPUSamplerDescriptor smp_desc; + memset(&smp_desc, 0, sizeof(smp_desc)); + smp_desc.addressModeU = _sg_wgpu_sampler_addrmode(img_desc->wrap_u); + smp_desc.addressModeV = _sg_wgpu_sampler_addrmode(img_desc->wrap_v); + smp_desc.addressModeW = _sg_wgpu_sampler_addrmode(img_desc->wrap_w); + smp_desc.magFilter = _sg_wgpu_sampler_minmagfilter(img_desc->mag_filter); + smp_desc.minFilter = _sg_wgpu_sampler_minmagfilter(img_desc->min_filter); + smp_desc.mipmapFilter = _sg_wgpu_sampler_mipfilter(img_desc->min_filter); + smp_desc.lodMinClamp = img_desc->min_lod; + smp_desc.lodMaxClamp = img_desc->max_lod; + WGPUSampler smp = wgpuDeviceCreateSampler(_sg.wgpu.dev, &smp_desc); + SOKOL_ASSERT(smp); + _sg_smpcache_add_item(&_sg.wgpu.sampler_cache, img_desc, (uintptr_t)smp); + return smp; + } +} + +/*--- WGPU backend API functions ---*/ +_SOKOL_PRIVATE void _sg_wgpu_setup_backend(const sg_desc* desc) { + SOKOL_ASSERT(desc); + SOKOL_ASSERT(desc->context.wgpu.device); + SOKOL_ASSERT(desc->context.wgpu.render_view_cb || desc->context.wgpu.render_view_userdata_cb); + SOKOL_ASSERT(desc->context.wgpu.resolve_view_cb || desc->context.wgpu.resolve_view_userdata_cb); + SOKOL_ASSERT(desc->context.wgpu.depth_stencil_view_cb || desc->context.wgpu.depth_stencil_view_userdata_cb); + SOKOL_ASSERT(desc->uniform_buffer_size > 0); + SOKOL_ASSERT(desc->staging_buffer_size > 0); + _sg.backend = SG_BACKEND_WGPU; + _sg.wgpu.valid = true; + _sg.wgpu.dev = (WGPUDevice) desc->context.wgpu.device; + _sg.wgpu.render_view_cb = (WGPUTextureView(*)(void)) desc->context.wgpu.render_view_cb; + _sg.wgpu.render_view_userdata_cb = (WGPUTextureView(*)(void*)) desc->context.wgpu.render_view_userdata_cb; + _sg.wgpu.resolve_view_cb = (WGPUTextureView(*)(void)) desc->context.wgpu.resolve_view_cb; + _sg.wgpu.resolve_view_userdata_cb = (WGPUTextureView(*)(void*)) desc->context.wgpu.resolve_view_userdata_cb; + _sg.wgpu.depth_stencil_view_cb = (WGPUTextureView(*)(void)) desc->context.wgpu.depth_stencil_view_cb; + _sg.wgpu.depth_stencil_view_userdata_cb = (WGPUTextureView(*)(void*)) desc->context.wgpu.depth_stencil_view_userdata_cb; + _sg.wgpu.user_data = desc->context.wgpu.user_data; + _sg.wgpu.queue = wgpuDeviceCreateQueue(_sg.wgpu.dev); + SOKOL_ASSERT(_sg.wgpu.queue); + + /* setup WebGPU features and limits */ + _sg_wgpu_init_caps(); + + /* setup the sampler cache, uniform and staging buffer pools */ + _sg_wgpu_init_sampler_cache(&_sg.desc); + _sg_wgpu_ubpool_init(desc); + _sg_wgpu_ubpool_next_frame(true); + _sg_wgpu_staging_init(desc); + _sg_wgpu_staging_next_frame(true); + + /* create an empty bind group for shader stages without bound images */ + WGPUBindGroupLayoutDescriptor bgl_desc; + memset(&bgl_desc, 0, sizeof(bgl_desc)); + WGPUBindGroupLayout empty_bgl = wgpuDeviceCreateBindGroupLayout(_sg.wgpu.dev, &bgl_desc); + SOKOL_ASSERT(empty_bgl); + WGPUBindGroupDescriptor bg_desc; + memset(&bg_desc, 0, sizeof(bg_desc)); + bg_desc.layout = empty_bgl; + _sg.wgpu.empty_bind_group = wgpuDeviceCreateBindGroup(_sg.wgpu.dev, &bg_desc); + SOKOL_ASSERT(_sg.wgpu.empty_bind_group); + wgpuBindGroupLayoutRelease(empty_bgl); + + /* create initial per-frame command encoders */ + WGPUCommandEncoderDescriptor cmd_enc_desc; + memset(&cmd_enc_desc, 0, sizeof(cmd_enc_desc)); + _sg.wgpu.render_cmd_enc = wgpuDeviceCreateCommandEncoder(_sg.wgpu.dev, &cmd_enc_desc); + SOKOL_ASSERT(_sg.wgpu.render_cmd_enc); + _sg.wgpu.staging_cmd_enc = wgpuDeviceCreateCommandEncoder(_sg.wgpu.dev, &cmd_enc_desc); + SOKOL_ASSERT(_sg.wgpu.staging_cmd_enc); +} + +_SOKOL_PRIVATE void _sg_wgpu_discard_backend(void) { + SOKOL_ASSERT(_sg.wgpu.valid); + SOKOL_ASSERT(_sg.wgpu.render_cmd_enc); + SOKOL_ASSERT(_sg.wgpu.staging_cmd_enc); + _sg.wgpu.valid = false; + _sg_wgpu_ubpool_discard(); + _sg_wgpu_staging_discard(); + _sg_wgpu_destroy_sampler_cache(); + wgpuBindGroupRelease(_sg.wgpu.empty_bind_group); + wgpuCommandEncoderRelease(_sg.wgpu.render_cmd_enc); + _sg.wgpu.render_cmd_enc = 0; + wgpuCommandEncoderRelease(_sg.wgpu.staging_cmd_enc); + _sg.wgpu.staging_cmd_enc = 0; + if (_sg.wgpu.queue) { + wgpuQueueRelease(_sg.wgpu.queue); + _sg.wgpu.queue = 0; + } +} + +_SOKOL_PRIVATE void _sg_wgpu_reset_state_cache(void) { + SOKOL_LOG("_sg_wgpu_reset_state_cache: FIXME\n"); +} + +_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_context(_sg_context_t* ctx) { + SOKOL_ASSERT(ctx); + _SOKOL_UNUSED(ctx); + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_wgpu_destroy_context(_sg_context_t* ctx) { + SOKOL_ASSERT(ctx); + _SOKOL_UNUSED(ctx); +} + +_SOKOL_PRIVATE void _sg_wgpu_activate_context(_sg_context_t* ctx) { + (void)ctx; + SOKOL_LOG("_sg_wgpu_activate_context: FIXME\n"); +} + +_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { + SOKOL_ASSERT(buf && desc); + const bool injected = (0 != desc->wgpu_buffer); + _sg_buffer_common_init(&buf->cmn, desc); + if (injected) { + buf->wgpu.buf = (WGPUBuffer) desc->wgpu_buffer; + wgpuBufferReference(buf->wgpu.buf); + } + else { + WGPUBufferDescriptor wgpu_buf_desc; + memset(&wgpu_buf_desc, 0, sizeof(wgpu_buf_desc)); + wgpu_buf_desc.usage = _sg_wgpu_buffer_usage(buf->cmn.type, buf->cmn.usage); + wgpu_buf_desc.size = buf->cmn.size; + if (SG_USAGE_IMMUTABLE == buf->cmn.usage) { + SOKOL_ASSERT(desc->data.ptr); + WGPUCreateBufferMappedResult res = wgpuDeviceCreateBufferMapped(_sg.wgpu.dev, &wgpu_buf_desc); + buf->wgpu.buf = res.buffer; + SOKOL_ASSERT(res.data && (res.dataLength == buf->cmn.size)); + memcpy(res.data, desc->data.ptr, buf->cmn.size); + wgpuBufferUnmap(res.buffer); + } + else { + buf->wgpu.buf = wgpuDeviceCreateBuffer(_sg.wgpu.dev, &wgpu_buf_desc); + } + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_wgpu_destroy_buffer(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf); + WGPUBuffer wgpu_buf = buf->wgpu.buf; + if (0 != wgpu_buf) { + wgpuBufferRelease(wgpu_buf); + } +} + +_SOKOL_PRIVATE void _sg_wgpu_init_texdesc_common(WGPUTextureDescriptor* wgpu_tex_desc, const sg_image_desc* desc) { + wgpu_tex_desc->usage = WGPUTextureUsage_Sampled|WGPUTextureUsage_CopyDst; + wgpu_tex_desc->dimension = _sg_wgpu_tex_dim(desc->type); + wgpu_tex_desc->size.width = desc->width; + wgpu_tex_desc->size.height = desc->height; + if (desc->type == SG_IMAGETYPE_3D) { + wgpu_tex_desc->size.depth = desc->num_slices; + wgpu_tex_desc->arrayLayerCount = 1; + } + else if (desc->type == SG_IMAGETYPE_CUBE) { + wgpu_tex_desc->size.depth = 1; + wgpu_tex_desc->arrayLayerCount = 6; + } + else { + wgpu_tex_desc->size.depth = 1; + wgpu_tex_desc->arrayLayerCount = desc->num_slices; + } + wgpu_tex_desc->format = _sg_wgpu_textureformat(desc->pixel_format); + wgpu_tex_desc->mipLevelCount = desc->num_mipmaps; + wgpu_tex_desc->sampleCount = 1; +} + +_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_image(_sg_image_t* img, const sg_image_desc* desc) { + SOKOL_ASSERT(img && desc); + SOKOL_ASSERT(_sg.wgpu.dev); + SOKOL_ASSERT(_sg.wgpu.staging_cmd_enc); + + _sg_image_common_init(&img->cmn, desc); + + const bool injected = (0 != desc->wgpu_texture); + const bool is_msaa = desc->sample_count > 1; + WGPUTextureDescriptor wgpu_tex_desc; + memset(&wgpu_tex_desc, 0, sizeof(wgpu_tex_desc)); + _sg_wgpu_init_texdesc_common(&wgpu_tex_desc, desc); + if (_sg_is_valid_rendertarget_depth_format(img->cmn.pixel_format)) { + SOKOL_ASSERT(img->cmn.render_target); + SOKOL_ASSERT(img->cmn.type == SG_IMAGETYPE_2D); + SOKOL_ASSERT(img->cmn.num_mipmaps == 1); + SOKOL_ASSERT(!injected); + /* NOTE: a depth-stencil texture will never be MSAA-resolved, so there + won't be a separate MSAA- and resolve-texture + */ + wgpu_tex_desc.usage = WGPUTextureUsage_OutputAttachment; + wgpu_tex_desc.sampleCount = desc->sample_count; + img->wgpu.tex = wgpuDeviceCreateTexture(_sg.wgpu.dev, &wgpu_tex_desc); + SOKOL_ASSERT(img->wgpu.tex); + } + else { + if (injected) { + img->wgpu.tex = (WGPUTexture) desc->wgpu_texture; + wgpuTextureReference(img->wgpu.tex); + } + else { + /* NOTE: in the MSAA-rendertarget case, both the MSAA texture *and* + the resolve texture need OutputAttachment usage + */ + if (img->cmn.render_target) { + wgpu_tex_desc.usage = WGPUTextureUsage_Sampled|WGPUTextureUsage_OutputAttachment; + } + img->wgpu.tex = wgpuDeviceCreateTexture(_sg.wgpu.dev, &wgpu_tex_desc); + SOKOL_ASSERT(img->wgpu.tex); + + /* copy content into texture via a throw-away staging buffer */ + if (desc->usage == SG_USAGE_IMMUTABLE && !desc->render_target) { + WGPUBufferDescriptor wgpu_buf_desc; + memset(&wgpu_buf_desc, 0, sizeof(wgpu_buf_desc)); + wgpu_buf_desc.size = _sg_wgpu_image_data_buffer_size(img); + wgpu_buf_desc.usage = WGPUBufferUsage_CopySrc|WGPUBufferUsage_CopyDst; + WGPUCreateBufferMappedResult map = wgpuDeviceCreateBufferMapped(_sg.wgpu.dev, &wgpu_buf_desc); + SOKOL_ASSERT(map.buffer && map.data); + uint32_t num_bytes = _sg_wgpu_copy_image_data(map.buffer, (uint8_t*)map.data, 0, img, &desc->data); + _SOKOL_UNUSED(num_bytes); + SOKOL_ASSERT(num_bytes == wgpu_buf_desc.size); + wgpuBufferUnmap(map.buffer); + wgpuBufferRelease(map.buffer); + } + } + + /* create texture view object */ + WGPUTextureViewDescriptor wgpu_view_desc; + memset(&wgpu_view_desc, 0, sizeof(wgpu_view_desc)); + wgpu_view_desc.dimension = _sg_wgpu_tex_viewdim(desc->type); + img->wgpu.tex_view = wgpuTextureCreateView(img->wgpu.tex, &wgpu_view_desc); + + /* if render target and MSAA, then a separate texture in MSAA format is needed + which will be resolved into the regular texture at the end of the + offscreen-render pass + */ + if (desc->render_target && is_msaa) { + wgpu_tex_desc.dimension = WGPUTextureDimension_2D; + wgpu_tex_desc.size.depth = 1; + wgpu_tex_desc.arrayLayerCount = 1; + wgpu_tex_desc.mipLevelCount = 1; + wgpu_tex_desc.usage = WGPUTextureUsage_OutputAttachment; + wgpu_tex_desc.sampleCount = desc->sample_count; + img->wgpu.msaa_tex = wgpuDeviceCreateTexture(_sg.wgpu.dev, &wgpu_tex_desc); + SOKOL_ASSERT(img->wgpu.msaa_tex); + } + + /* create sampler via shared-sampler-cache */ + img->wgpu.sampler = _sg_wgpu_create_sampler(desc); + SOKOL_ASSERT(img->wgpu.sampler); + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_wgpu_destroy_image(_sg_image_t* img) { + SOKOL_ASSERT(img); + if (img->wgpu.tex) { + wgpuTextureRelease(img->wgpu.tex); + img->wgpu.tex = 0; + } + if (img->wgpu.tex_view) { + wgpuTextureViewRelease(img->wgpu.tex_view); + img->wgpu.tex_view = 0; + } + if (img->wgpu.msaa_tex) { + wgpuTextureRelease(img->wgpu.msaa_tex); + img->wgpu.msaa_tex = 0; + } + /* NOTE: do *not* destroy the sampler from the shared-sampler-cache */ + img->wgpu.sampler = 0; +} + +/* + How BindGroups work in WebGPU: + + - up to 4 bind groups can be bound simultaneously + - up to 16 bindings per bind group + - 'binding' slots are local per bind group + - in the shader: + layout(set=0, binding=1) corresponds to bind group 0, binding 1 + + Now how to map this to sokol-gfx's bind model: + + Reduce SG_MAX_SHADERSTAGE_IMAGES to 8, then: + + 1 bind group for all 8 uniform buffers + 1 bind group for vertex shader textures + samplers + 1 bind group for fragment shader textures + samples + + Alternatively: + + 1 bind group for 8 uniform buffer slots + 1 bind group for 8 vs images + 8 vs samplers + 1 bind group for 12 fs images + 1 bind group for 12 fs samplers + + I guess this means that we need to create BindGroups on the + fly during sg_apply_bindings() :/ +*/ +_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { + SOKOL_ASSERT(shd && desc); + SOKOL_ASSERT(desc->vs.bytecode.ptr && desc->fs.bytecode.ptr); + _sg_shader_common_init(&shd->cmn, desc); + + bool success = true; + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + const sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS) ? &desc->vs : &desc->fs; + SOKOL_ASSERT((stage_desc->bytecode.size & 3) == 0); + + _sg_shader_stage_t* cmn_stage = &shd->cmn.stage[stage_index]; + _sg_wgpu_shader_stage_t* wgpu_stage = &shd->wgpu.stage[stage_index]; + + _sg_strcpy(&wgpu_stage->entry, stage_desc->entry); + WGPUShaderModuleDescriptor wgpu_shdmod_desc; + memset(&wgpu_shdmod_desc, 0, sizeof(wgpu_shdmod_desc)); + wgpu_shdmod_desc.codeSize = stage_desc->bytecode.size >> 2; + wgpu_shdmod_desc.code = (const uint32_t*) stage_desc->bytecode.ptr; + wgpu_stage->module = wgpuDeviceCreateShaderModule(_sg.wgpu.dev, &wgpu_shdmod_desc); + if (0 == wgpu_stage->module) { + success = false; + } + + /* create image/sampler bind group for the shader stage */ + WGPUShaderStage vis = (stage_index == SG_SHADERSTAGE_VS) ? WGPUShaderStage_Vertex : WGPUShaderStage_Fragment; + int num_imgs = cmn_stage->num_images; + if (num_imgs > _SG_WGPU_MAX_SHADERSTAGE_IMAGES) { + num_imgs = _SG_WGPU_MAX_SHADERSTAGE_IMAGES; + } + WGPUBindGroupLayoutBinding bglb_desc[_SG_WGPU_MAX_SHADERSTAGE_IMAGES * 2]; + memset(bglb_desc, 0, sizeof(bglb_desc)); + for (int img_index = 0; img_index < num_imgs; img_index++) { + /* texture- and sampler-bindings */ + WGPUBindGroupLayoutBinding* tex_desc = &bglb_desc[img_index*2 + 0]; + WGPUBindGroupLayoutBinding* smp_desc = &bglb_desc[img_index*2 + 1]; + + tex_desc->binding = img_index; + tex_desc->visibility = vis; + tex_desc->type = WGPUBindingType_SampledTexture; + tex_desc->textureDimension = _sg_wgpu_tex_viewdim(cmn_stage->images[img_index].image_type); + tex_desc->textureComponentType = _sg_wgpu_tex_comptype(cmn_stage->images[img_index].sampler_type); + + smp_desc->binding = img_index + _SG_WGPU_MAX_SHADERSTAGE_IMAGES; + smp_desc->visibility = vis; + smp_desc->type = WGPUBindingType_Sampler; + } + WGPUBindGroupLayoutDescriptor img_bgl_desc; + memset(&img_bgl_desc, 0, sizeof(img_bgl_desc)); + img_bgl_desc.bindingCount = num_imgs * 2; + img_bgl_desc.bindings = &bglb_desc[0]; + wgpu_stage->bind_group_layout = wgpuDeviceCreateBindGroupLayout(_sg.wgpu.dev, &img_bgl_desc); + SOKOL_ASSERT(wgpu_stage->bind_group_layout); + } + return success ? SG_RESOURCESTATE_VALID : SG_RESOURCESTATE_FAILED; +} + +_SOKOL_PRIVATE void _sg_wgpu_destroy_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd); + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + _sg_wgpu_shader_stage_t* wgpu_stage = &shd->wgpu.stage[stage_index]; + if (wgpu_stage->module) { + wgpuShaderModuleRelease(wgpu_stage->module); + wgpu_stage->module = 0; + } + if (wgpu_stage->bind_group_layout) { + wgpuBindGroupLayoutRelease(wgpu_stage->bind_group_layout); + wgpu_stage->bind_group_layout = 0; + } + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(pip && shd && desc); + SOKOL_ASSERT(desc->shader.id == shd->slot.id); + SOKOL_ASSERT(shd->wgpu.stage[SG_SHADERSTAGE_VS].bind_group_layout); + SOKOL_ASSERT(shd->wgpu.stage[SG_SHADERSTAGE_FS].bind_group_layout); + pip->shader = shd; + _sg_pipeline_common_init(&pip->cmn, desc); + pip->wgpu.stencil_ref = (uint32_t) desc->stencil.ref; + + WGPUBindGroupLayout pip_bgl[3] = { + _sg.wgpu.ub.bindgroup_layout, + shd->wgpu.stage[SG_SHADERSTAGE_VS].bind_group_layout, + shd->wgpu.stage[SG_SHADERSTAGE_FS].bind_group_layout + }; + WGPUPipelineLayoutDescriptor pl_desc; + memset(&pl_desc, 0, sizeof(pl_desc)); + pl_desc.bindGroupLayoutCount = 3; + pl_desc.bindGroupLayouts = &pip_bgl[0]; + WGPUPipelineLayout pip_layout = wgpuDeviceCreatePipelineLayout(_sg.wgpu.dev, &pl_desc); + + WGPUVertexBufferLayoutDescriptor vb_desc[SG_MAX_SHADERSTAGE_BUFFERS]; + memset(&vb_desc, 0, sizeof(vb_desc)); + WGPUVertexAttributeDescriptor va_desc[SG_MAX_SHADERSTAGE_BUFFERS][SG_MAX_VERTEX_ATTRIBUTES]; + memset(&va_desc, 0, sizeof(va_desc)); + int vb_idx = 0; + for (; vb_idx < SG_MAX_SHADERSTAGE_BUFFERS; vb_idx++) { + const sg_buffer_layout_desc* src_vb_desc = &desc->layout.buffers[vb_idx]; + if (0 == src_vb_desc->stride) { + break; + } + vb_desc[vb_idx].arrayStride = src_vb_desc->stride; + vb_desc[vb_idx].stepMode = _sg_wgpu_stepmode(src_vb_desc->step_func); + /* NOTE: WebGPU has no support for vertex step rate (because that's + not supported by Core Vulkan + */ + int va_idx = 0; + for (int va_loc = 0; va_loc < SG_MAX_VERTEX_ATTRIBUTES; va_loc++) { + const sg_vertex_attr_desc* src_va_desc = &desc->layout.attrs[va_loc]; + if (SG_VERTEXFORMAT_INVALID == src_va_desc->format) { + break; + } + pip->cmn.vertex_layout_valid[src_va_desc->buffer_index] = true; + if (vb_idx == src_va_desc->buffer_index) { + va_desc[vb_idx][va_idx].format = _sg_wgpu_vertexformat(src_va_desc->format); + va_desc[vb_idx][va_idx].offset = src_va_desc->offset; + va_desc[vb_idx][va_idx].shaderLocation = va_loc; + va_idx++; + } + } + vb_desc[vb_idx].attributeCount = va_idx; + vb_desc[vb_idx].attributes = &va_desc[vb_idx][0]; + } + WGPUVertexStateDescriptor vx_state_desc; + memset(&vx_state_desc, 0, sizeof(vx_state_desc)); + vx_state_desc.indexFormat = _sg_wgpu_indexformat(desc->index_type); + vx_state_desc.vertexBufferCount = vb_idx; + vx_state_desc.vertexBuffers = vb_desc; + + WGPURasterizationStateDescriptor rs_desc; + memset(&rs_desc, 0, sizeof(rs_desc)); + rs_desc.frontFace = _sg_wgpu_frontface(desc->face_winding); + rs_desc.cullMode = _sg_wgpu_cullmode(desc->cull_mode); + rs_desc.depthBias = (int32_t) desc->depth.bias; + rs_desc.depthBiasClamp = desc->depth.bias_clamp; + rs_desc.depthBiasSlopeScale = desc->depth.bias_slope_scale; + + WGPUDepthStencilStateDescriptor ds_desc; + memset(&ds_desc, 0, sizeof(ds_desc)); + ds_desc.format = _sg_wgpu_textureformat(desc->depth.pixel_format); + ds_desc.depthWriteEnabled = desc->depth.write_enabled; + ds_desc.depthCompare = _sg_wgpu_comparefunc(desc->depth.compare); + ds_desc.stencilReadMask = desc->stencil.read_mask; + ds_desc.stencilWriteMask = desc->stencil.write_mask; + ds_desc.stencilFront.compare = _sg_wgpu_comparefunc(desc->stencil.front.compare); + ds_desc.stencilFront.failOp = _sg_wgpu_stencilop(desc->stencil.front.fail_op); + ds_desc.stencilFront.depthFailOp = _sg_wgpu_stencilop(desc->stencil.front.depth_fail_op); + ds_desc.stencilFront.passOp = _sg_wgpu_stencilop(desc->stencil.front.pass_op); + ds_desc.stencilBack.compare = _sg_wgpu_comparefunc(desc->stencil.back.compare); + ds_desc.stencilBack.failOp = _sg_wgpu_stencilop(desc->stencil.back.fail_op); + ds_desc.stencilBack.depthFailOp = _sg_wgpu_stencilop(desc->stencil.back.depth_fail_op); + ds_desc.stencilBack.passOp = _sg_wgpu_stencilop(desc->stencil.back.pass_op); + + WGPUProgrammableStageDescriptor fs_desc; + memset(&fs_desc, 0, sizeof(fs_desc)); + fs_desc.module = shd->wgpu.stage[SG_SHADERSTAGE_FS].module; + fs_desc.entryPoint = shd->wgpu.stage[SG_SHADERSTAGE_VS].entry.buf; + + WGPUColorStateDescriptor cs_desc[SG_MAX_COLOR_ATTACHMENTS]; + memset(cs_desc, 0, sizeof(cs_desc)); + for (uint32_t i = 0; i < desc->color_count; i++) { + SOKOL_ASSERT(i < SG_MAX_COLOR_ATTACHMENTS); + cs_desc[i].format = _sg_wgpu_textureformat(desc->colors[i].pixel_format); + cs_desc[i].colorBlend.operation = _sg_wgpu_blendop(desc->colors[i].blend.op_rgb); + cs_desc[i].colorBlend.srcFactor = _sg_wgpu_blendfactor(desc->colors[i].blend.src_factor_rgb); + cs_desc[i].colorBlend.dstFactor = _sg_wgpu_blendfactor(desc->colors[i].blend.dst_factor_rgb); + cs_desc[i].alphaBlend.operation = _sg_wgpu_blendop(desc->colors[i].blend.op_alpha); + cs_desc[i].alphaBlend.srcFactor = _sg_wgpu_blendfactor(desc->colors[i].blend.src_factor_alpha); + cs_desc[i].alphaBlend.dstFactor = _sg_wgpu_blendfactor(desc->colors[i].blend.dst_factor_alpha); + cs_desc[i].writeMask = _sg_wgpu_colorwritemask(desc->colors[i].write_mask); + } + + WGPURenderPipelineDescriptor pip_desc; + memset(&pip_desc, 0, sizeof(pip_desc)); + pip_desc.layout = pip_layout; + pip_desc.vertexStage.module = shd->wgpu.stage[SG_SHADERSTAGE_VS].module; + pip_desc.vertexStage.entryPoint = shd->wgpu.stage[SG_SHADERSTAGE_VS].entry.buf; + pip_desc.fragmentStage = &fs_desc; + pip_desc.vertexState = &vx_state_desc; + pip_desc.primitiveTopology = _sg_wgpu_topology(desc->primitive_type); + pip_desc.rasterizationState = &rs_desc; + pip_desc.sampleCount = desc->sample_count; + if (SG_PIXELFORMAT_NONE != desc->depth.pixel_format) { + pip_desc.depthStencilState = &ds_desc; + } + pip_desc.colorStateCount = desc->color_count; + pip_desc.colorStates = cs_desc; + pip_desc.sampleMask = 0xFFFFFFFF; /* FIXME: ??? */ + pip->wgpu.pip = wgpuDeviceCreateRenderPipeline(_sg.wgpu.dev, &pip_desc); + SOKOL_ASSERT(0 != pip->wgpu.pip); + wgpuPipelineLayoutRelease(pip_layout); + + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_wgpu_destroy_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + if (pip->wgpu.pip) { + wgpuRenderPipelineRelease(pip->wgpu.pip); + pip->wgpu.pip = 0; + } +} + +_SOKOL_PRIVATE sg_resource_state _sg_wgpu_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { + SOKOL_ASSERT(pass && desc); + SOKOL_ASSERT(att_images && att_images[0]); + _sg_pass_common_init(&pass->cmn, desc); + + /* copy image pointers and create render-texture views */ + const sg_pass_attachment_desc* att_desc; + for (uint32_t i = 0; i < pass->cmn.num_color_atts; i++) { + att_desc = &desc->color_attachments[i]; + if (att_desc->image.id != SG_INVALID_ID) { + SOKOL_ASSERT(att_desc->image.id != SG_INVALID_ID); + SOKOL_ASSERT(0 == pass->wgpu.color_atts[i].image); + _sg_image_t* img = att_images[i]; + SOKOL_ASSERT(img && (img->slot.id == att_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_color_format(img->cmn.pixel_format)); + pass->wgpu.color_atts[i].image = img; + /* create a render-texture-view to render into the right sub-surface */ + const bool is_msaa = img->cmn.sample_count > 1; + WGPUTextureViewDescriptor view_desc; + memset(&view_desc, 0, sizeof(view_desc)); + view_desc.baseMipLevel = is_msaa ? 0 : att_desc->mip_level; + view_desc.mipLevelCount = 1; + view_desc.baseArrayLayer = is_msaa ? 0 : att_desc->slice; + view_desc.arrayLayerCount = 1; + WGPUTexture wgpu_tex = is_msaa ? img->wgpu.msaa_tex : img->wgpu.tex; + SOKOL_ASSERT(wgpu_tex); + pass->wgpu.color_atts[i].render_tex_view = wgpuTextureCreateView(wgpu_tex, &view_desc); + SOKOL_ASSERT(pass->wgpu.color_atts[i].render_tex_view); + /* ... and if needed a separate resolve texture view */ + if (is_msaa) { + view_desc.baseMipLevel = att_desc->mip_level; + view_desc.baseArrayLayer = att_desc->slice; + WGPUTexture wgpu_tex = img->wgpu.tex; + pass->wgpu.color_atts[i].resolve_tex_view = wgpuTextureCreateView(wgpu_tex, &view_desc); + SOKOL_ASSERT(pass->wgpu.color_atts[i].resolve_tex_view); + } + } + } + SOKOL_ASSERT(0 == pass->wgpu.ds_att.image); + att_desc = &desc->depth_stencil_attachment; + if (att_desc->image.id != SG_INVALID_ID) { + const int ds_img_index = SG_MAX_COLOR_ATTACHMENTS; + SOKOL_ASSERT(att_images[ds_img_index] && (att_images[ds_img_index]->slot.id == att_desc->image.id)); + SOKOL_ASSERT(_sg_is_valid_rendertarget_depth_format(att_images[ds_img_index]->cmn.pixel_format)); + _sg_image_t* ds_img = att_images[ds_img_index]; + pass->wgpu.ds_att.image = ds_img; + /* create a render-texture view */ + SOKOL_ASSERT(0 == att_desc->mip_level); + SOKOL_ASSERT(0 == att_desc->slice); + WGPUTextureViewDescriptor view_desc; + memset(&view_desc, 0, sizeof(view_desc)); + WGPUTexture wgpu_tex = ds_img->wgpu.tex; + SOKOL_ASSERT(wgpu_tex); + pass->wgpu.ds_att.render_tex_view = wgpuTextureCreateView(wgpu_tex, &view_desc); + SOKOL_ASSERT(pass->wgpu.ds_att.render_tex_view); + } + return SG_RESOURCESTATE_VALID; +} + +_SOKOL_PRIVATE void _sg_wgpu_destroy_pass(_sg_pass_t* pass) { + SOKOL_ASSERT(pass); + for (uint32_t i = 0; i < pass->cmn.num_color_atts; i++) { + if (pass->wgpu.color_atts[i].render_tex_view) { + wgpuTextureViewRelease(pass->wgpu.color_atts[i].render_tex_view); + pass->wgpu.color_atts[i].render_tex_view = 0; + } + if (pass->wgpu.color_atts[i].resolve_tex_view) { + wgpuTextureViewRelease(pass->wgpu.color_atts[i].resolve_tex_view); + pass->wgpu.color_atts[i].resolve_tex_view = 0; + } + } + if (pass->wgpu.ds_att.render_tex_view) { + wgpuTextureViewRelease(pass->wgpu.ds_att.render_tex_view); + pass->wgpu.ds_att.render_tex_view = 0; + } +} + +_SOKOL_PRIVATE _sg_image_t* _sg_wgpu_pass_color_image(const _sg_pass_t* pass, int index) { + SOKOL_ASSERT(pass && (index >= 0) && (index < SG_MAX_COLOR_ATTACHMENTS)); + /* NOTE: may return null */ + return pass->wgpu.color_atts[index].image; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_wgpu_pass_ds_image(const _sg_pass_t* pass) { + /* NOTE: may return null */ + SOKOL_ASSERT(pass); + return pass->wgpu.ds_att.image; +} + +_SOKOL_PRIVATE void _sg_wgpu_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { + SOKOL_ASSERT(action); + SOKOL_ASSERT(!_sg.wgpu.in_pass); + SOKOL_ASSERT(_sg.wgpu.render_cmd_enc); + SOKOL_ASSERT(_sg.wgpu.dev); + SOKOL_ASSERT(_sg.wgpu.render_view_cb || _sg.wgpu.render_view_userdata_cb); + SOKOL_ASSERT(_sg.wgpu.resolve_view_cb || _sg.wgpu.resolve_view_userdata_cb); + SOKOL_ASSERT(_sg.wgpu.depth_stencil_view_cb || _sg.wgpu.depth_stencil_view_userdata_cb); + _sg.wgpu.in_pass = true; + _sg.wgpu.cur_width = w; + _sg.wgpu.cur_height = h; + _sg.wgpu.cur_pipeline = 0; + _sg.wgpu.cur_pipeline_id.id = SG_INVALID_ID; + + SOKOL_ASSERT(_sg.wgpu.render_cmd_enc); + if (pass) { + WGPURenderPassDescriptor wgpu_pass_desc; + memset(&wgpu_pass_desc, 0, sizeof(wgpu_pass_desc)); + WGPURenderPassColorAttachmentDescriptor wgpu_color_att_desc[SG_MAX_COLOR_ATTACHMENTS]; + memset(&wgpu_color_att_desc, 0, sizeof(wgpu_color_att_desc)); + SOKOL_ASSERT(pass->slot.state == SG_RESOURCESTATE_VALID); + for (uint32_t i = 0; i < pass->cmn.num_color_atts; i++) { + const _sg_wgpu_attachment_t* wgpu_att = &pass->wgpu.color_atts[i]; + wgpu_color_att_desc[i].loadOp = _sg_wgpu_load_op(action->colors[i].action); + wgpu_color_att_desc[i].storeOp = WGPUStoreOp_Store; + wgpu_color_att_desc[i].clearColor.r = action->colors[i].value.r; + wgpu_color_att_desc[i].clearColor.g = action->colors[i].value.g; + wgpu_color_att_desc[i].clearColor.b = action->colors[i].value.b; + wgpu_color_att_desc[i].clearColor.a = action->colors[i].value.a; + wgpu_color_att_desc[i].attachment = wgpu_att->render_tex_view; + if (wgpu_att->image->cmn.sample_count > 1) { + wgpu_color_att_desc[i].resolveTarget = wgpu_att->resolve_tex_view; + } + } + wgpu_pass_desc.colorAttachmentCount = pass->cmn.num_color_atts; + wgpu_pass_desc.colorAttachments = &wgpu_color_att_desc[0]; + if (pass->wgpu.ds_att.image) { + WGPURenderPassDepthStencilAttachmentDescriptor wgpu_ds_att_desc; + memset(&wgpu_ds_att_desc, 0, sizeof(wgpu_ds_att_desc)); + wgpu_ds_att_desc.depthLoadOp = _sg_wgpu_load_op(action->depth.action); + wgpu_ds_att_desc.clearDepth = action->depth.value; + wgpu_ds_att_desc.stencilLoadOp = _sg_wgpu_load_op(action->stencil.action); + wgpu_ds_att_desc.clearStencil = action->stencil.value; + wgpu_ds_att_desc.attachment = pass->wgpu.ds_att.render_tex_view; + wgpu_pass_desc.depthStencilAttachment = &wgpu_ds_att_desc; + _sg.wgpu.pass_enc = wgpuCommandEncoderBeginRenderPass(_sg.wgpu.render_cmd_enc, &wgpu_pass_desc); + } + } + else { + /* default render pass */ + WGPUTextureView wgpu_render_view = _sg.wgpu.render_view_cb ? _sg.wgpu.render_view_cb() : _sg.wgpu.render_view_userdata_cb(_sg.wgpu.user_data); + WGPUTextureView wgpu_resolve_view = _sg.wgpu.resolve_view_cb ? _sg.wgpu.resolve_view_cb() : _sg.wgpu.resolve_view_userdata_cb(_sg.wgpu.user_data); + WGPUTextureView wgpu_depth_stencil_view = _sg.wgpu.depth_stencil_view_cb ? _sg.wgpu.depth_stencil_view_cb() : _sg.wgpu.depth_stencil_view_userdata_cb(_sg.wgpu.user_data); + + WGPURenderPassDescriptor pass_desc; + memset(&pass_desc, 0, sizeof(pass_desc)); + WGPURenderPassColorAttachmentDescriptor color_att_desc; + memset(&color_att_desc, 0, sizeof(color_att_desc)); + color_att_desc.loadOp = _sg_wgpu_load_op(action->colors[0].action); + color_att_desc.clearColor.r = action->colors[0].value.r; + color_att_desc.clearColor.g = action->colors[0].value.g; + color_att_desc.clearColor.b = action->colors[0].value.b; + color_att_desc.clearColor.a = action->colors[0].value.a; + color_att_desc.attachment = wgpu_render_view; + color_att_desc.resolveTarget = wgpu_resolve_view; /* null if no MSAA rendering */ + pass_desc.colorAttachmentCount = 1; + pass_desc.colorAttachments = &color_att_desc; + WGPURenderPassDepthStencilAttachmentDescriptor ds_att_desc; + memset(&ds_att_desc, 0, sizeof(ds_att_desc)); + ds_att_desc.attachment = wgpu_depth_stencil_view; + SOKOL_ASSERT(0 != ds_att_desc.attachment); + ds_att_desc.depthLoadOp = _sg_wgpu_load_op(action->depth.action); + ds_att_desc.clearDepth = action->depth.value; + ds_att_desc.stencilLoadOp = _sg_wgpu_load_op(action->stencil.action); + ds_att_desc.clearStencil = action->stencil.value; + pass_desc.depthStencilAttachment = &ds_att_desc; + _sg.wgpu.pass_enc = wgpuCommandEncoderBeginRenderPass(_sg.wgpu.render_cmd_enc, &pass_desc); + } + SOKOL_ASSERT(_sg.wgpu.pass_enc); + + /* initial uniform buffer binding (required even if no uniforms are set in the frame) */ + wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, + 0, /* groupIndex 0 is reserved for uniform buffers */ + _sg.wgpu.ub.bindgroup, + SG_NUM_SHADER_STAGES * SG_MAX_SHADERSTAGE_UBS, + &_sg.wgpu.ub.bind_offsets[0][0]); +} + +_SOKOL_PRIVATE void _sg_wgpu_end_pass(void) { + SOKOL_ASSERT(_sg.wgpu.in_pass); + SOKOL_ASSERT(_sg.wgpu.pass_enc); + _sg.wgpu.in_pass = false; + wgpuRenderPassEncoderEndPass(_sg.wgpu.pass_enc); + wgpuRenderPassEncoderRelease(_sg.wgpu.pass_enc); + _sg.wgpu.pass_enc = 0; +} + +_SOKOL_PRIVATE void _sg_wgpu_commit(void) { + SOKOL_ASSERT(!_sg.wgpu.in_pass); + SOKOL_ASSERT(_sg.wgpu.queue); + SOKOL_ASSERT(_sg.wgpu.render_cmd_enc); + SOKOL_ASSERT(_sg.wgpu.staging_cmd_enc); + + /* finish and submit this frame's work */ + _sg_wgpu_ubpool_flush(); + _sg_wgpu_staging_unmap(); + + WGPUCommandBuffer cmd_bufs[2]; + + WGPUCommandBufferDescriptor cmd_buf_desc; + memset(&cmd_buf_desc, 0, sizeof(cmd_buf_desc)); + cmd_bufs[0] = wgpuCommandEncoderFinish(_sg.wgpu.staging_cmd_enc, &cmd_buf_desc); + SOKOL_ASSERT(cmd_bufs[0]); + wgpuCommandEncoderRelease(_sg.wgpu.staging_cmd_enc); + _sg.wgpu.staging_cmd_enc = 0; + + cmd_bufs[1] = wgpuCommandEncoderFinish(_sg.wgpu.render_cmd_enc, &cmd_buf_desc); + SOKOL_ASSERT(cmd_bufs[1]); + wgpuCommandEncoderRelease(_sg.wgpu.render_cmd_enc); + _sg.wgpu.render_cmd_enc = 0; + + wgpuQueueSubmit(_sg.wgpu.queue, 2, &cmd_bufs[0]); + + wgpuCommandBufferRelease(cmd_bufs[0]); + wgpuCommandBufferRelease(cmd_bufs[1]); + + /* create a new render- and staging-command-encoders for next frame */ + WGPUCommandEncoderDescriptor cmd_enc_desc; + memset(&cmd_enc_desc, 0, sizeof(cmd_enc_desc)); + _sg.wgpu.staging_cmd_enc = wgpuDeviceCreateCommandEncoder(_sg.wgpu.dev, &cmd_enc_desc); + _sg.wgpu.render_cmd_enc = wgpuDeviceCreateCommandEncoder(_sg.wgpu.dev, &cmd_enc_desc); + + /* grab new staging buffers for uniform- and vertex/image-updates */ + _sg_wgpu_ubpool_next_frame(false); + _sg_wgpu_staging_next_frame(false); +} + +_SOKOL_PRIVATE void _sg_wgpu_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_sg.wgpu.in_pass); + SOKOL_ASSERT(_sg.wgpu.pass_enc); + float xf = (float) x; + float yf = (float) (origin_top_left ? y : (_sg.wgpu.cur_height - (y + h))); + float wf = (float) w; + float hf = (float) h; + wgpuRenderPassEncoderSetViewport(_sg.wgpu.pass_enc, xf, yf, wf, hf, 0.0f, 1.0f); +} + +_SOKOL_PRIVATE void _sg_wgpu_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_sg.wgpu.in_pass); + SOKOL_ASSERT(_sg.wgpu.pass_enc); + SOKOL_ASSERT(_sg.wgpu.in_pass); + SOKOL_ASSERT(_sg.wgpu.pass_enc); + + /* clip against framebuffer rect */ + x = _sg_min(_sg_max(0, x), _sg.wgpu.cur_width-1); + y = _sg_min(_sg_max(0, y), _sg.wgpu.cur_height-1); + if ((x + w) > _sg.wgpu.cur_width) { + w = _sg.wgpu.cur_width - x; + } + if ((y + h) > _sg.wgpu.cur_height) { + h = _sg.wgpu.cur_height - y; + } + w = _sg_max(w, 1); + h = _sg_max(h, 1); + + uint32_t sx = (uint32_t) x; + uint32_t sy = origin_top_left ? y : (_sg.wgpu.cur_height - (y + h)); + uint32_t sw = w; + uint32_t sh = h; + wgpuRenderPassEncoderSetScissorRect(_sg.wgpu.pass_enc, sx, sy, sw, sh); +} + +_SOKOL_PRIVATE void _sg_wgpu_apply_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + SOKOL_ASSERT(pip->wgpu.pip); + SOKOL_ASSERT(_sg.wgpu.in_pass); + SOKOL_ASSERT(_sg.wgpu.pass_enc); + _sg.wgpu.draw_indexed = (pip->cmn.index_type != SG_INDEXTYPE_NONE); + _sg.wgpu.cur_pipeline = pip; + _sg.wgpu.cur_pipeline_id.id = pip->slot.id; + wgpuRenderPassEncoderSetPipeline(_sg.wgpu.pass_enc, pip->wgpu.pip); + wgpuRenderPassEncoderSetBlendColor(_sg.wgpu.pass_enc, (WGPUColor*)&pip->cmn.blend_color); + wgpuRenderPassEncoderSetStencilReference(_sg.wgpu.pass_enc, pip->wgpu.stencil_ref); +} + +_SOKOL_PRIVATE WGPUBindGroup _sg_wgpu_create_images_bindgroup(WGPUBindGroupLayout bgl, _sg_image_t** imgs, int num_imgs) { + SOKOL_ASSERT(_sg.wgpu.dev); + SOKOL_ASSERT(num_imgs <= _SG_WGPU_MAX_SHADERSTAGE_IMAGES); + WGPUBindGroupBinding img_bgb[_SG_WGPU_MAX_SHADERSTAGE_IMAGES * 2]; + memset(&img_bgb, 0, sizeof(img_bgb)); + for (int img_index = 0; img_index < num_imgs; img_index++) { + WGPUBindGroupBinding* tex_bdg = &img_bgb[img_index*2 + 0]; + WGPUBindGroupBinding* smp_bdg = &img_bgb[img_index*2 + 1]; + tex_bdg->binding = img_index; + tex_bdg->textureView = imgs[img_index]->wgpu.tex_view; + smp_bdg->binding = img_index + _SG_WGPU_MAX_SHADERSTAGE_IMAGES; + smp_bdg->sampler = imgs[img_index]->wgpu.sampler; + } + WGPUBindGroupDescriptor bg_desc; + memset(&bg_desc, 0, sizeof(bg_desc)); + bg_desc.layout = bgl; + bg_desc.bindingCount = 2 * num_imgs; + bg_desc.bindings = &img_bgb[0]; + WGPUBindGroup bg = wgpuDeviceCreateBindGroup(_sg.wgpu.dev, &bg_desc); + SOKOL_ASSERT(bg); + return bg; +} + +_SOKOL_PRIVATE void _sg_wgpu_apply_bindings( + _sg_pipeline_t* pip, + _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, + _sg_buffer_t* ib, int ib_offset, + _sg_image_t** vs_imgs, int num_vs_imgs, + _sg_image_t** fs_imgs, int num_fs_imgs) +{ + SOKOL_ASSERT(_sg.wgpu.in_pass); + SOKOL_ASSERT(_sg.wgpu.pass_enc); + SOKOL_ASSERT(pip->shader && (pip->cmn.shader_id.id == pip->shader->slot.id)); + + /* index buffer */ + if (ib) { + wgpuRenderPassEncoderSetIndexBuffer(_sg.wgpu.pass_enc, ib->wgpu.buf, ib_offset); + } + + /* vertex buffers */ + for (uint32_t slot = 0; slot < (uint32_t)num_vbs; slot++) { + wgpuRenderPassEncoderSetVertexBuffer(_sg.wgpu.pass_enc, slot, vbs[slot]->wgpu.buf, (uint64_t)vb_offsets[slot]); + } + + /* need to create throw-away bind groups for images */ + if (num_vs_imgs > 0) { + if (num_vs_imgs > _SG_WGPU_MAX_SHADERSTAGE_IMAGES) { + num_vs_imgs = _SG_WGPU_MAX_SHADERSTAGE_IMAGES; + } + WGPUBindGroupLayout vs_bgl = pip->shader->wgpu.stage[SG_SHADERSTAGE_VS].bind_group_layout; + SOKOL_ASSERT(vs_bgl); + WGPUBindGroup vs_img_bg = _sg_wgpu_create_images_bindgroup(vs_bgl, vs_imgs, num_vs_imgs); + wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, 1, vs_img_bg, 0, 0); + wgpuBindGroupRelease(vs_img_bg); + } + else { + wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, 1, _sg.wgpu.empty_bind_group, 0, 0); + } + if (num_fs_imgs > 0) { + if (num_fs_imgs > _SG_WGPU_MAX_SHADERSTAGE_IMAGES) { + num_fs_imgs = _SG_WGPU_MAX_SHADERSTAGE_IMAGES; + } + WGPUBindGroupLayout fs_bgl = pip->shader->wgpu.stage[SG_SHADERSTAGE_FS].bind_group_layout; + SOKOL_ASSERT(fs_bgl); + WGPUBindGroup fs_img_bg = _sg_wgpu_create_images_bindgroup(fs_bgl, fs_imgs, num_fs_imgs); + wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, 2, fs_img_bg, 0, 0); + wgpuBindGroupRelease(fs_img_bg); + } + else { + wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, 2, _sg.wgpu.empty_bind_group, 0, 0); + } +} + +_SOKOL_PRIVATE void _sg_wgpu_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { + SOKOL_ASSERT(_sg.wgpu.in_pass); + SOKOL_ASSERT(_sg.wgpu.pass_enc); + SOKOL_ASSERT((_sg.wgpu.ub.offset + data->size) <= _sg.wgpu.ub.num_bytes); + SOKOL_ASSERT((_sg.wgpu.ub.offset & (_SG_WGPU_STAGING_ALIGN-1)) == 0); + SOKOL_ASSERT(_sg.wgpu.cur_pipeline && _sg.wgpu.cur_pipeline->shader); + SOKOL_ASSERT(_sg.wgpu.cur_pipeline->slot.id == _sg.wgpu.cur_pipeline_id.id); + SOKOL_ASSERT(_sg.wgpu.cur_pipeline->shader->slot.id == _sg.wgpu.cur_pipeline->cmn.shader_id.id); + SOKOL_ASSERT(ub_index < _sg.wgpu.cur_pipeline->shader->cmn.stage[stage_index].num_uniform_blocks); + SOKOL_ASSERT(data->size <= _sg.wgpu.cur_pipeline->shader->cmn.stage[stage_index].uniform_blocks[ub_index].size); + SOKOL_ASSERT(data->size <= _SG_WGPU_MAX_UNIFORM_UPDATE_SIZE); + SOKOL_ASSERT(0 != _sg.wgpu.ub.stage.ptr[_sg.wgpu.ub.stage.cur]); + + uint8_t* dst_ptr = _sg.wgpu.ub.stage.ptr[_sg.wgpu.ub.stage.cur] + _sg.wgpu.ub.offset; + memcpy(dst_ptr, data->ptr, data->size); + _sg.wgpu.ub.bind_offsets[stage_index][ub_index] = _sg.wgpu.ub.offset; + wgpuRenderPassEncoderSetBindGroup(_sg.wgpu.pass_enc, + 0, /* groupIndex 0 is reserved for uniform buffers */ + _sg.wgpu.ub.bindgroup, + SG_NUM_SHADER_STAGES * SG_MAX_SHADERSTAGE_UBS, + &_sg.wgpu.ub.bind_offsets[0][0]); + _sg.wgpu.ub.offset = _sg_roundup(_sg.wgpu.ub.offset + data->size, _SG_WGPU_STAGING_ALIGN); +} + +_SOKOL_PRIVATE void _sg_wgpu_draw(int base_element, int num_elements, int num_instances) { + SOKOL_ASSERT(_sg.wgpu.in_pass); + SOKOL_ASSERT(_sg.wgpu.pass_enc); + if (_sg.wgpu.draw_indexed) { + wgpuRenderPassEncoderDrawIndexed(_sg.wgpu.pass_enc, num_elements, num_instances, base_element, 0, 0); + } + else { + wgpuRenderPassEncoderDraw(_sg.wgpu.pass_enc, num_elements, num_instances, base_element, 0); + } +} + +_SOKOL_PRIVATE void _sg_wgpu_update_buffer(_sg_buffer_t* buf, const sg_range* data) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + uint32_t copied_num_bytes = _sg_wgpu_staging_copy_to_buffer(buf->wgpu.buf, 0, data->ptr, data->size); + SOKOL_ASSERT(copied_num_bytes > 0); _SOKOL_UNUSED(copied_num_bytes); +} + +_SOKOL_PRIVATE int _sg_wgpu_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { + SOKOL_ASSERT(buf && data && data->ptr && (data->size > 0)); + _SOKOL_UNUSED(new_frame); + uint32_t copied_num_bytes = _sg_wgpu_staging_copy_to_buffer(buf->wgpu.buf, buf->cmn.append_pos, data->ptr, data->size); + SOKOL_ASSERT(copied_num_bytes > 0); _SOKOL_UNUSED(copied_num_bytes); + return (int)copied_num_bytes; +} + +_SOKOL_PRIVATE void _sg_wgpu_update_image(_sg_image_t* img, const sg_image_data* data) { + SOKOL_ASSERT(img && data); + bool success = _sg_wgpu_staging_copy_to_texture(img, data); + SOKOL_ASSERT(success); + _SOKOL_UNUSED(success); +} +#endif + +/*== BACKEND API WRAPPERS ====================================================*/ +static inline void _sg_setup_backend(const sg_desc* desc) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_setup_backend(desc); + #elif defined(SOKOL_METAL) + _sg_mtl_setup_backend(desc); + #elif defined(SOKOL_D3D11) + _sg_d3d11_setup_backend(desc); + #elif defined(SOKOL_WGPU) + _sg_wgpu_setup_backend(desc); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_setup_backend(desc); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_discard_backend(void) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_discard_backend(); + #elif defined(SOKOL_METAL) + _sg_mtl_discard_backend(); + #elif defined(SOKOL_D3D11) + _sg_d3d11_discard_backend(); + #elif defined(SOKOL_WGPU) + _sg_wgpu_discard_backend(); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_discard_backend(); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_reset_state_cache(void) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_reset_state_cache(); + #elif defined(SOKOL_METAL) + _sg_mtl_reset_state_cache(); + #elif defined(SOKOL_D3D11) + _sg_d3d11_reset_state_cache(); + #elif defined(SOKOL_WGPU) + _sg_wgpu_reset_state_cache(); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_reset_state_cache(); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_activate_context(_sg_context_t* ctx) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_activate_context(ctx); + #elif defined(SOKOL_METAL) + _sg_mtl_activate_context(ctx); + #elif defined(SOKOL_D3D11) + _sg_d3d11_activate_context(ctx); + #elif defined(SOKOL_WGPU) + _sg_wgpu_activate_context(ctx); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_activate_context(ctx); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline sg_resource_state _sg_create_context(_sg_context_t* ctx) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_create_context(ctx); + #elif defined(SOKOL_METAL) + return _sg_mtl_create_context(ctx); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_create_context(ctx); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_create_context(ctx); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_create_context(ctx); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_destroy_context(_sg_context_t* ctx) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_destroy_context(ctx); + #elif defined(SOKOL_METAL) + _sg_mtl_destroy_context(ctx); + #elif defined(SOKOL_D3D11) + _sg_d3d11_destroy_context(ctx); + #elif defined(SOKOL_WGPU) + _sg_wgpu_destroy_context(ctx); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_destroy_context(ctx); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline sg_resource_state _sg_create_buffer(_sg_buffer_t* buf, const sg_buffer_desc* desc) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_create_buffer(buf, desc); + #elif defined(SOKOL_METAL) + return _sg_mtl_create_buffer(buf, desc); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_create_buffer(buf, desc); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_create_buffer(buf, desc); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_create_buffer(buf, desc); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_destroy_buffer(_sg_buffer_t* buf) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_destroy_buffer(buf); + #elif defined(SOKOL_METAL) + _sg_mtl_destroy_buffer(buf); + #elif defined(SOKOL_D3D11) + _sg_d3d11_destroy_buffer(buf); + #elif defined(SOKOL_WGPU) + _sg_wgpu_destroy_buffer(buf); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_destroy_buffer(buf); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline sg_resource_state _sg_create_image(_sg_image_t* img, const sg_image_desc* desc) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_create_image(img, desc); + #elif defined(SOKOL_METAL) + return _sg_mtl_create_image(img, desc); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_create_image(img, desc); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_create_image(img, desc); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_create_image(img, desc); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_destroy_image(_sg_image_t* img) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_destroy_image(img); + #elif defined(SOKOL_METAL) + _sg_mtl_destroy_image(img); + #elif defined(SOKOL_D3D11) + _sg_d3d11_destroy_image(img); + #elif defined(SOKOL_WGPU) + _sg_wgpu_destroy_image(img); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_destroy_image(img); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline sg_resource_state _sg_create_shader(_sg_shader_t* shd, const sg_shader_desc* desc) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_create_shader(shd, desc); + #elif defined(SOKOL_METAL) + return _sg_mtl_create_shader(shd, desc); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_create_shader(shd, desc); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_create_shader(shd, desc); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_create_shader(shd, desc); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_destroy_shader(_sg_shader_t* shd) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_destroy_shader(shd); + #elif defined(SOKOL_METAL) + _sg_mtl_destroy_shader(shd); + #elif defined(SOKOL_D3D11) + _sg_d3d11_destroy_shader(shd); + #elif defined(SOKOL_WGPU) + _sg_wgpu_destroy_shader(shd); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_destroy_shader(shd); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline sg_resource_state _sg_create_pipeline(_sg_pipeline_t* pip, _sg_shader_t* shd, const sg_pipeline_desc* desc) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_create_pipeline(pip, shd, desc); + #elif defined(SOKOL_METAL) + return _sg_mtl_create_pipeline(pip, shd, desc); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_create_pipeline(pip, shd, desc); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_create_pipeline(pip, shd, desc); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_create_pipeline(pip, shd, desc); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_destroy_pipeline(_sg_pipeline_t* pip) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_destroy_pipeline(pip); + #elif defined(SOKOL_METAL) + _sg_mtl_destroy_pipeline(pip); + #elif defined(SOKOL_D3D11) + _sg_d3d11_destroy_pipeline(pip); + #elif defined(SOKOL_WGPU) + _sg_wgpu_destroy_pipeline(pip); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_destroy_pipeline(pip); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline sg_resource_state _sg_create_pass(_sg_pass_t* pass, _sg_image_t** att_images, const sg_pass_desc* desc) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_create_pass(pass, att_images, desc); + #elif defined(SOKOL_METAL) + return _sg_mtl_create_pass(pass, att_images, desc); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_create_pass(pass, att_images, desc); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_create_pass(pass, att_images, desc); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_create_pass(pass, att_images, desc); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_destroy_pass(_sg_pass_t* pass) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_destroy_pass(pass); + #elif defined(SOKOL_METAL) + _sg_mtl_destroy_pass(pass); + #elif defined(SOKOL_D3D11) + _sg_d3d11_destroy_pass(pass); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_destroy_pass(pass); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_destroy_pass(pass); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline _sg_image_t* _sg_pass_color_image(const _sg_pass_t* pass, int index) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_pass_color_image(pass, index); + #elif defined(SOKOL_METAL) + return _sg_mtl_pass_color_image(pass, index); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_pass_color_image(pass, index); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_pass_color_image(pass, index); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_pass_color_image(pass, index); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline _sg_image_t* _sg_pass_ds_image(const _sg_pass_t* pass) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_pass_ds_image(pass); + #elif defined(SOKOL_METAL) + return _sg_mtl_pass_ds_image(pass); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_pass_ds_image(pass); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_pass_ds_image(pass); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_pass_ds_image(pass); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_begin_pass(_sg_pass_t* pass, const sg_pass_action* action, int w, int h) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_begin_pass(pass, action, w, h); + #elif defined(SOKOL_METAL) + _sg_mtl_begin_pass(pass, action, w, h); + #elif defined(SOKOL_D3D11) + _sg_d3d11_begin_pass(pass, action, w, h); + #elif defined(SOKOL_WGPU) + _sg_wgpu_begin_pass(pass, action, w, h); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_begin_pass(pass, action, w, h); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_end_pass(void) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_end_pass(); + #elif defined(SOKOL_METAL) + _sg_mtl_end_pass(); + #elif defined(SOKOL_D3D11) + _sg_d3d11_end_pass(); + #elif defined(SOKOL_WGPU) + _sg_wgpu_end_pass(); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_end_pass(); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_apply_viewport(int x, int y, int w, int h, bool origin_top_left) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_apply_viewport(x, y, w, h, origin_top_left); + #elif defined(SOKOL_METAL) + _sg_mtl_apply_viewport(x, y, w, h, origin_top_left); + #elif defined(SOKOL_D3D11) + _sg_d3d11_apply_viewport(x, y, w, h, origin_top_left); + #elif defined(SOKOL_WGPU) + _sg_wgpu_apply_viewport(x, y, w, h, origin_top_left); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_apply_viewport(x, y, w, h, origin_top_left); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_apply_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_apply_scissor_rect(x, y, w, h, origin_top_left); + #elif defined(SOKOL_METAL) + _sg_mtl_apply_scissor_rect(x, y, w, h, origin_top_left); + #elif defined(SOKOL_D3D11) + _sg_d3d11_apply_scissor_rect(x, y, w, h, origin_top_left); + #elif defined(SOKOL_WGPU) + _sg_wgpu_apply_scissor_rect(x, y, w, h, origin_top_left); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_apply_scissor_rect(x, y, w, h, origin_top_left); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_apply_pipeline(_sg_pipeline_t* pip) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_apply_pipeline(pip); + #elif defined(SOKOL_METAL) + _sg_mtl_apply_pipeline(pip); + #elif defined(SOKOL_D3D11) + _sg_d3d11_apply_pipeline(pip); + #elif defined(SOKOL_WGPU) + _sg_wgpu_apply_pipeline(pip); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_apply_pipeline(pip); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_apply_bindings( + _sg_pipeline_t* pip, + _sg_buffer_t** vbs, const int* vb_offsets, int num_vbs, + _sg_buffer_t* ib, int ib_offset, + _sg_image_t** vs_imgs, int num_vs_imgs, + _sg_image_t** fs_imgs, int num_fs_imgs) +{ + #if defined(_SOKOL_ANY_GL) + _sg_gl_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs); + #elif defined(SOKOL_METAL) + _sg_mtl_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs); + #elif defined(SOKOL_D3D11) + _sg_d3d11_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs); + #elif defined(SOKOL_WGPU) + _sg_wgpu_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_apply_uniforms(stage_index, ub_index, data); + #elif defined(SOKOL_METAL) + _sg_mtl_apply_uniforms(stage_index, ub_index, data); + #elif defined(SOKOL_D3D11) + _sg_d3d11_apply_uniforms(stage_index, ub_index, data); + #elif defined(SOKOL_WGPU) + _sg_wgpu_apply_uniforms(stage_index, ub_index, data); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_apply_uniforms(stage_index, ub_index, data); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_draw(int base_element, int num_elements, int num_instances) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_draw(base_element, num_elements, num_instances); + #elif defined(SOKOL_METAL) + _sg_mtl_draw(base_element, num_elements, num_instances); + #elif defined(SOKOL_D3D11) + _sg_d3d11_draw(base_element, num_elements, num_instances); + #elif defined(SOKOL_WGPU) + _sg_wgpu_draw(base_element, num_elements, num_instances); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_draw(base_element, num_elements, num_instances); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_commit(void) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_commit(); + #elif defined(SOKOL_METAL) + _sg_mtl_commit(); + #elif defined(SOKOL_D3D11) + _sg_d3d11_commit(); + #elif defined(SOKOL_WGPU) + _sg_wgpu_commit(); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_commit(); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_update_buffer(_sg_buffer_t* buf, const sg_range* data) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_update_buffer(buf, data); + #elif defined(SOKOL_METAL) + _sg_mtl_update_buffer(buf, data); + #elif defined(SOKOL_D3D11) + _sg_d3d11_update_buffer(buf, data); + #elif defined(SOKOL_WGPU) + _sg_wgpu_update_buffer(buf, data); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_update_buffer(buf, data); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline int _sg_append_buffer(_sg_buffer_t* buf, const sg_range* data, bool new_frame) { + #if defined(_SOKOL_ANY_GL) + return _sg_gl_append_buffer(buf, data, new_frame); + #elif defined(SOKOL_METAL) + return _sg_mtl_append_buffer(buf, data, new_frame); + #elif defined(SOKOL_D3D11) + return _sg_d3d11_append_buffer(buf, data, new_frame); + #elif defined(SOKOL_WGPU) + return _sg_wgpu_append_buffer(buf, data, new_frame); + #elif defined(SOKOL_DUMMY_BACKEND) + return _sg_dummy_append_buffer(buf, data, new_frame); + #else + #error("INVALID BACKEND"); + #endif +} + +static inline void _sg_update_image(_sg_image_t* img, const sg_image_data* data) { + #if defined(_SOKOL_ANY_GL) + _sg_gl_update_image(img, data); + #elif defined(SOKOL_METAL) + _sg_mtl_update_image(img, data); + #elif defined(SOKOL_D3D11) + _sg_d3d11_update_image(img, data); + #elif defined(SOKOL_WGPU) + _sg_wgpu_update_image(img, data); + #elif defined(SOKOL_DUMMY_BACKEND) + _sg_dummy_update_image(img, data); + #else + #error("INVALID BACKEND"); + #endif +} + +/*== RESOURCE POOLS ==========================================================*/ + +_SOKOL_PRIVATE void _sg_init_pool(_sg_pool_t* pool, int num) { + SOKOL_ASSERT(pool && (num >= 1)); + /* slot 0 is reserved for the 'invalid id', so bump the pool size by 1 */ + pool->size = num + 1; + pool->queue_top = 0; + /* generation counters indexable by pool slot index, slot 0 is reserved */ + size_t gen_ctrs_size = sizeof(uint32_t) * (size_t)pool->size; + pool->gen_ctrs = (uint32_t*) SOKOL_MALLOC(gen_ctrs_size); + SOKOL_ASSERT(pool->gen_ctrs); + memset(pool->gen_ctrs, 0, gen_ctrs_size); + /* it's not a bug to only reserve 'num' here */ + pool->free_queue = (int*) SOKOL_MALLOC(sizeof(int) * (size_t)num); + SOKOL_ASSERT(pool->free_queue); + /* never allocate the zero-th pool item since the invalid id is 0 */ + for (int i = pool->size-1; i >= 1; i--) { + pool->free_queue[pool->queue_top++] = i; + } +} + +_SOKOL_PRIVATE void _sg_discard_pool(_sg_pool_t* pool) { + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + SOKOL_FREE(pool->free_queue); + pool->free_queue = 0; + SOKOL_ASSERT(pool->gen_ctrs); + SOKOL_FREE(pool->gen_ctrs); + pool->gen_ctrs = 0; + pool->size = 0; + pool->queue_top = 0; +} + +_SOKOL_PRIVATE int _sg_pool_alloc_index(_sg_pool_t* pool) { + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + if (pool->queue_top > 0) { + int slot_index = pool->free_queue[--pool->queue_top]; + SOKOL_ASSERT((slot_index > 0) && (slot_index < pool->size)); + return slot_index; + } + else { + /* pool exhausted */ + return _SG_INVALID_SLOT_INDEX; + } +} + +_SOKOL_PRIVATE void _sg_pool_free_index(_sg_pool_t* pool, int slot_index) { + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < pool->size)); + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + SOKOL_ASSERT(pool->queue_top < pool->size); + #ifdef SOKOL_DEBUG + /* debug check against double-free */ + for (int i = 0; i < pool->queue_top; i++) { + SOKOL_ASSERT(pool->free_queue[i] != slot_index); + } + #endif + pool->free_queue[pool->queue_top++] = slot_index; + SOKOL_ASSERT(pool->queue_top <= (pool->size-1)); +} + +_SOKOL_PRIVATE void _sg_reset_slot(_sg_slot_t* slot) { + SOKOL_ASSERT(slot); + memset(slot, 0, sizeof(_sg_slot_t)); +} + +_SOKOL_PRIVATE void _sg_reset_buffer(_sg_buffer_t* buf) { + SOKOL_ASSERT(buf); + _sg_slot_t slot = buf->slot; + memset(buf, 0, sizeof(_sg_buffer_t)); + buf->slot = slot; + buf->slot.state = SG_RESOURCESTATE_ALLOC; +} + +_SOKOL_PRIVATE void _sg_reset_image(_sg_image_t* img) { + SOKOL_ASSERT(img); + _sg_slot_t slot = img->slot; + memset(img, 0, sizeof(_sg_image_t)); + img->slot = slot; + img->slot.state = SG_RESOURCESTATE_ALLOC; +} + +_SOKOL_PRIVATE void _sg_reset_shader(_sg_shader_t* shd) { + SOKOL_ASSERT(shd); + _sg_slot_t slot = shd->slot; + memset(shd, 0, sizeof(_sg_shader_t)); + shd->slot = slot; + shd->slot.state = SG_RESOURCESTATE_ALLOC; +} + +_SOKOL_PRIVATE void _sg_reset_pipeline(_sg_pipeline_t* pip) { + SOKOL_ASSERT(pip); + _sg_slot_t slot = pip->slot; + memset(pip, 0, sizeof(_sg_pipeline_t)); + pip->slot = slot; + pip->slot.state = SG_RESOURCESTATE_ALLOC; +} + +_SOKOL_PRIVATE void _sg_reset_pass(_sg_pass_t* pass) { + SOKOL_ASSERT(pass); + _sg_slot_t slot = pass->slot; + memset(pass, 0, sizeof(_sg_pass_t)); + pass->slot = slot; + pass->slot.state = SG_RESOURCESTATE_ALLOC; +} + +_SOKOL_PRIVATE void _sg_reset_context(_sg_context_t* ctx) { + SOKOL_ASSERT(ctx); + _sg_slot_t slot = ctx->slot; + memset(ctx, 0, sizeof(_sg_context_t)); + ctx->slot = slot; + ctx->slot.state = SG_RESOURCESTATE_ALLOC; +} + +_SOKOL_PRIVATE void _sg_setup_pools(_sg_pools_t* p, const sg_desc* desc) { + SOKOL_ASSERT(p); + SOKOL_ASSERT(desc); + /* note: the pools here will have an additional item, since slot 0 is reserved */ + SOKOL_ASSERT((desc->buffer_pool_size > 0) && (desc->buffer_pool_size < _SG_MAX_POOL_SIZE)); + _sg_init_pool(&p->buffer_pool, desc->buffer_pool_size); + size_t buffer_pool_byte_size = sizeof(_sg_buffer_t) * (size_t)p->buffer_pool.size; + p->buffers = (_sg_buffer_t*) SOKOL_MALLOC(buffer_pool_byte_size); + SOKOL_ASSERT(p->buffers); + memset(p->buffers, 0, buffer_pool_byte_size); + + SOKOL_ASSERT((desc->image_pool_size > 0) && (desc->image_pool_size < _SG_MAX_POOL_SIZE)); + _sg_init_pool(&p->image_pool, desc->image_pool_size); + size_t image_pool_byte_size = sizeof(_sg_image_t) * (size_t)p->image_pool.size; + p->images = (_sg_image_t*) SOKOL_MALLOC(image_pool_byte_size); + SOKOL_ASSERT(p->images); + memset(p->images, 0, image_pool_byte_size); + + SOKOL_ASSERT((desc->shader_pool_size > 0) && (desc->shader_pool_size < _SG_MAX_POOL_SIZE)); + _sg_init_pool(&p->shader_pool, desc->shader_pool_size); + size_t shader_pool_byte_size = sizeof(_sg_shader_t) * (size_t)p->shader_pool.size; + p->shaders = (_sg_shader_t*) SOKOL_MALLOC(shader_pool_byte_size); + SOKOL_ASSERT(p->shaders); + memset(p->shaders, 0, shader_pool_byte_size); + + SOKOL_ASSERT((desc->pipeline_pool_size > 0) && (desc->pipeline_pool_size < _SG_MAX_POOL_SIZE)); + _sg_init_pool(&p->pipeline_pool, desc->pipeline_pool_size); + size_t pipeline_pool_byte_size = sizeof(_sg_pipeline_t) * (size_t)p->pipeline_pool.size; + p->pipelines = (_sg_pipeline_t*) SOKOL_MALLOC(pipeline_pool_byte_size); + SOKOL_ASSERT(p->pipelines); + memset(p->pipelines, 0, pipeline_pool_byte_size); + + SOKOL_ASSERT((desc->pass_pool_size > 0) && (desc->pass_pool_size < _SG_MAX_POOL_SIZE)); + _sg_init_pool(&p->pass_pool, desc->pass_pool_size); + size_t pass_pool_byte_size = sizeof(_sg_pass_t) * (size_t)p->pass_pool.size; + p->passes = (_sg_pass_t*) SOKOL_MALLOC(pass_pool_byte_size); + SOKOL_ASSERT(p->passes); + memset(p->passes, 0, pass_pool_byte_size); + + SOKOL_ASSERT((desc->context_pool_size > 0) && (desc->context_pool_size < _SG_MAX_POOL_SIZE)); + _sg_init_pool(&p->context_pool, desc->context_pool_size); + size_t context_pool_byte_size = sizeof(_sg_context_t) * (size_t)p->context_pool.size; + p->contexts = (_sg_context_t*) SOKOL_MALLOC(context_pool_byte_size); + SOKOL_ASSERT(p->contexts); + memset(p->contexts, 0, context_pool_byte_size); +} + +_SOKOL_PRIVATE void _sg_discard_pools(_sg_pools_t* p) { + SOKOL_ASSERT(p); + SOKOL_FREE(p->contexts); p->contexts = 0; + SOKOL_FREE(p->passes); p->passes = 0; + SOKOL_FREE(p->pipelines); p->pipelines = 0; + SOKOL_FREE(p->shaders); p->shaders = 0; + SOKOL_FREE(p->images); p->images = 0; + SOKOL_FREE(p->buffers); p->buffers = 0; + _sg_discard_pool(&p->context_pool); + _sg_discard_pool(&p->pass_pool); + _sg_discard_pool(&p->pipeline_pool); + _sg_discard_pool(&p->shader_pool); + _sg_discard_pool(&p->image_pool); + _sg_discard_pool(&p->buffer_pool); +} + +/* allocate the slot at slot_index: + - bump the slot's generation counter + - create a resource id from the generation counter and slot index + - set the slot's id to this id + - set the slot's state to ALLOC + - return the resource id +*/ +_SOKOL_PRIVATE uint32_t _sg_slot_alloc(_sg_pool_t* pool, _sg_slot_t* slot, int slot_index) { + /* FIXME: add handling for an overflowing generation counter, + for now, just overflow (another option is to disable + the slot) + */ + SOKOL_ASSERT(pool && pool->gen_ctrs); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < pool->size)); + SOKOL_ASSERT((slot->state == SG_RESOURCESTATE_INITIAL) && (slot->id == SG_INVALID_ID)); + uint32_t ctr = ++pool->gen_ctrs[slot_index]; + slot->id = (ctr<<_SG_SLOT_SHIFT)|(slot_index & _SG_SLOT_MASK); + slot->state = SG_RESOURCESTATE_ALLOC; + return slot->id; +} + +/* extract slot index from id */ +_SOKOL_PRIVATE int _sg_slot_index(uint32_t id) { + int slot_index = (int) (id & _SG_SLOT_MASK); + SOKOL_ASSERT(_SG_INVALID_SLOT_INDEX != slot_index); + return slot_index; +} + +/* returns pointer to resource by id without matching id check */ +_SOKOL_PRIVATE _sg_buffer_t* _sg_buffer_at(const _sg_pools_t* p, uint32_t buf_id) { + SOKOL_ASSERT(p && (SG_INVALID_ID != buf_id)); + int slot_index = _sg_slot_index(buf_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->buffer_pool.size)); + return &p->buffers[slot_index]; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_image_at(const _sg_pools_t* p, uint32_t img_id) { + SOKOL_ASSERT(p && (SG_INVALID_ID != img_id)); + int slot_index = _sg_slot_index(img_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->image_pool.size)); + return &p->images[slot_index]; +} + +_SOKOL_PRIVATE _sg_shader_t* _sg_shader_at(const _sg_pools_t* p, uint32_t shd_id) { + SOKOL_ASSERT(p && (SG_INVALID_ID != shd_id)); + int slot_index = _sg_slot_index(shd_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->shader_pool.size)); + return &p->shaders[slot_index]; +} + +_SOKOL_PRIVATE _sg_pipeline_t* _sg_pipeline_at(const _sg_pools_t* p, uint32_t pip_id) { + SOKOL_ASSERT(p && (SG_INVALID_ID != pip_id)); + int slot_index = _sg_slot_index(pip_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->pipeline_pool.size)); + return &p->pipelines[slot_index]; +} + +_SOKOL_PRIVATE _sg_pass_t* _sg_pass_at(const _sg_pools_t* p, uint32_t pass_id) { + SOKOL_ASSERT(p && (SG_INVALID_ID != pass_id)); + int slot_index = _sg_slot_index(pass_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->pass_pool.size)); + return &p->passes[slot_index]; +} + +_SOKOL_PRIVATE _sg_context_t* _sg_context_at(const _sg_pools_t* p, uint32_t context_id) { + SOKOL_ASSERT(p && (SG_INVALID_ID != context_id)); + int slot_index = _sg_slot_index(context_id); + SOKOL_ASSERT((slot_index > _SG_INVALID_SLOT_INDEX) && (slot_index < p->context_pool.size)); + return &p->contexts[slot_index]; +} + +/* returns pointer to resource with matching id check, may return 0 */ +_SOKOL_PRIVATE _sg_buffer_t* _sg_lookup_buffer(const _sg_pools_t* p, uint32_t buf_id) { + if (SG_INVALID_ID != buf_id) { + _sg_buffer_t* buf = _sg_buffer_at(p, buf_id); + if (buf->slot.id == buf_id) { + return buf; + } + } + return 0; +} + +_SOKOL_PRIVATE _sg_image_t* _sg_lookup_image(const _sg_pools_t* p, uint32_t img_id) { + if (SG_INVALID_ID != img_id) { + _sg_image_t* img = _sg_image_at(p, img_id); + if (img->slot.id == img_id) { + return img; + } + } + return 0; +} + +_SOKOL_PRIVATE _sg_shader_t* _sg_lookup_shader(const _sg_pools_t* p, uint32_t shd_id) { + SOKOL_ASSERT(p); + if (SG_INVALID_ID != shd_id) { + _sg_shader_t* shd = _sg_shader_at(p, shd_id); + if (shd->slot.id == shd_id) { + return shd; + } + } + return 0; +} + +_SOKOL_PRIVATE _sg_pipeline_t* _sg_lookup_pipeline(const _sg_pools_t* p, uint32_t pip_id) { + SOKOL_ASSERT(p); + if (SG_INVALID_ID != pip_id) { + _sg_pipeline_t* pip = _sg_pipeline_at(p, pip_id); + if (pip->slot.id == pip_id) { + return pip; + } + } + return 0; +} + +_SOKOL_PRIVATE _sg_pass_t* _sg_lookup_pass(const _sg_pools_t* p, uint32_t pass_id) { + SOKOL_ASSERT(p); + if (SG_INVALID_ID != pass_id) { + _sg_pass_t* pass = _sg_pass_at(p, pass_id); + if (pass->slot.id == pass_id) { + return pass; + } + } + return 0; +} + +_SOKOL_PRIVATE _sg_context_t* _sg_lookup_context(const _sg_pools_t* p, uint32_t ctx_id) { + SOKOL_ASSERT(p); + if (SG_INVALID_ID != ctx_id) { + _sg_context_t* ctx = _sg_context_at(p, ctx_id); + if (ctx->slot.id == ctx_id) { + return ctx; + } + } + return 0; +} + +_SOKOL_PRIVATE void _sg_destroy_all_resources(_sg_pools_t* p, uint32_t ctx_id) { + /* this is a bit dumb since it loops over all pool slots to + find the occupied slots, on the other hand it is only ever + executed at shutdown + NOTE: ONLY EXECUTE THIS AT SHUTDOWN + ...because the free queues will not be reset + and the resource slots not be cleared! + */ + for (int i = 1; i < p->buffer_pool.size; i++) { + if (p->buffers[i].slot.ctx_id == ctx_id) { + sg_resource_state state = p->buffers[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_destroy_buffer(&p->buffers[i]); + } + } + } + for (int i = 1; i < p->image_pool.size; i++) { + if (p->images[i].slot.ctx_id == ctx_id) { + sg_resource_state state = p->images[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_destroy_image(&p->images[i]); + } + } + } + for (int i = 1; i < p->shader_pool.size; i++) { + if (p->shaders[i].slot.ctx_id == ctx_id) { + sg_resource_state state = p->shaders[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_destroy_shader(&p->shaders[i]); + } + } + } + for (int i = 1; i < p->pipeline_pool.size; i++) { + if (p->pipelines[i].slot.ctx_id == ctx_id) { + sg_resource_state state = p->pipelines[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_destroy_pipeline(&p->pipelines[i]); + } + } + } + for (int i = 1; i < p->pass_pool.size; i++) { + if (p->passes[i].slot.ctx_id == ctx_id) { + sg_resource_state state = p->passes[i].slot.state; + if ((state == SG_RESOURCESTATE_VALID) || (state == SG_RESOURCESTATE_FAILED)) { + _sg_destroy_pass(&p->passes[i]); + } + } + } +} + +/*== VALIDATION LAYER ========================================================*/ +#if defined(SOKOL_DEBUG) +/* return a human readable string for an _sg_validate_error */ +_SOKOL_PRIVATE const char* _sg_validate_string(_sg_validate_error_t err) { + switch (err) { + /* buffer creation validation errors */ + case _SG_VALIDATE_BUFFERDESC_CANARY: return "sg_buffer_desc not initialized"; + case _SG_VALIDATE_BUFFERDESC_SIZE: return "sg_buffer_desc.size cannot be 0"; + case _SG_VALIDATE_BUFFERDESC_DATA: return "immutable buffers must be initialized with data (sg_buffer_desc.data.ptr and sg_buffer_desc.data.size)"; + case _SG_VALIDATE_BUFFERDESC_DATA_SIZE: return "immutable buffer data size differs from buffer size"; + case _SG_VALIDATE_BUFFERDESC_NO_DATA: return "dynamic/stream usage buffers cannot be initialized with data"; + + /* image creation validation errros */ + case _SG_VALIDATE_IMAGEDESC_CANARY: return "sg_image_desc not initialized"; + case _SG_VALIDATE_IMAGEDESC_WIDTH: return "sg_image_desc.width must be > 0"; + case _SG_VALIDATE_IMAGEDESC_HEIGHT: return "sg_image_desc.height must be > 0"; + case _SG_VALIDATE_IMAGEDESC_RT_PIXELFORMAT: return "invalid pixel format for render-target image"; + case _SG_VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT: return "invalid pixel format for non-render-target image"; + case _SG_VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT: return "non-render-target images cannot be multisampled"; + case _SG_VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT: return "MSAA not supported for this pixel format"; + case _SG_VALIDATE_IMAGEDESC_RT_IMMUTABLE: return "render target images must be SG_USAGE_IMMUTABLE"; + case _SG_VALIDATE_IMAGEDESC_RT_NO_DATA: return "render target images cannot be initialized with data"; + case _SG_VALIDATE_IMAGEDESC_DATA: return "missing or invalid data for immutable image"; + case _SG_VALIDATE_IMAGEDESC_NO_DATA: return "dynamic/stream usage images cannot be initialized with data"; + + /* shader creation */ + case _SG_VALIDATE_SHADERDESC_CANARY: return "sg_shader_desc not initialized"; + case _SG_VALIDATE_SHADERDESC_SOURCE: return "shader source code required"; + case _SG_VALIDATE_SHADERDESC_BYTECODE: return "shader byte code required"; + case _SG_VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE: return "shader source or byte code required"; + case _SG_VALIDATE_SHADERDESC_NO_BYTECODE_SIZE: return "shader byte code length (in bytes) required"; + case _SG_VALIDATE_SHADERDESC_NO_CONT_UBS: return "shader uniform blocks must occupy continuous slots"; + case _SG_VALIDATE_SHADERDESC_NO_CONT_UB_MEMBERS: return "uniform block members must occupy continuous slots"; + case _SG_VALIDATE_SHADERDESC_NO_UB_MEMBERS: return "GL backend requires uniform block member declarations"; + case _SG_VALIDATE_SHADERDESC_UB_MEMBER_NAME: return "uniform block member name missing"; + case _SG_VALIDATE_SHADERDESC_UB_SIZE_MISMATCH: return "size of uniform block members doesn't match uniform block size"; + case _SG_VALIDATE_SHADERDESC_NO_CONT_IMGS: return "shader images must occupy continuous slots"; + case _SG_VALIDATE_SHADERDESC_IMG_NAME: return "GL backend requires uniform block member names"; + case _SG_VALIDATE_SHADERDESC_ATTR_NAMES: return "GLES2 backend requires vertex attribute names"; + case _SG_VALIDATE_SHADERDESC_ATTR_SEMANTICS: return "D3D11 backend requires vertex attribute semantics"; + case _SG_VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG: return "vertex attribute name/semantic string too long (max len 16)"; + + /* pipeline creation */ + case _SG_VALIDATE_PIPELINEDESC_CANARY: return "sg_pipeline_desc not initialized"; + case _SG_VALIDATE_PIPELINEDESC_SHADER: return "sg_pipeline_desc.shader missing or invalid"; + case _SG_VALIDATE_PIPELINEDESC_NO_ATTRS: return "sg_pipeline_desc.layout.attrs is empty or not continuous"; + case _SG_VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4: return "sg_pipeline_desc.layout.buffers[].stride must be multiple of 4"; + case _SG_VALIDATE_PIPELINEDESC_ATTR_NAME: return "GLES2/WebGL missing vertex attribute name in shader"; + case _SG_VALIDATE_PIPELINEDESC_ATTR_SEMANTICS: return "D3D11 missing vertex attribute semantics in shader"; + + /* pass creation */ + case _SG_VALIDATE_PASSDESC_CANARY: return "sg_pass_desc not initialized"; + case _SG_VALIDATE_PASSDESC_NO_COLOR_ATTS: return "sg_pass_desc.color_attachments[0] must be valid"; + case _SG_VALIDATE_PASSDESC_NO_CONT_COLOR_ATTS: return "color attachments must occupy continuous slots"; + case _SG_VALIDATE_PASSDESC_IMAGE: return "pass attachment image is not valid"; + case _SG_VALIDATE_PASSDESC_MIPLEVEL: return "pass attachment mip level is bigger than image has mipmaps"; + case _SG_VALIDATE_PASSDESC_FACE: return "pass attachment image is cubemap, but face index is too big"; + case _SG_VALIDATE_PASSDESC_LAYER: return "pass attachment image is array texture, but layer index is too big"; + case _SG_VALIDATE_PASSDESC_SLICE: return "pass attachment image is 3d texture, but slice value is too big"; + case _SG_VALIDATE_PASSDESC_IMAGE_NO_RT: return "pass attachment image must be render targets"; + case _SG_VALIDATE_PASSDESC_COLOR_INV_PIXELFORMAT: return "pass color-attachment images must have a renderable pixel format"; + case _SG_VALIDATE_PASSDESC_DEPTH_INV_PIXELFORMAT: return "pass depth-attachment image must have depth pixel format"; + case _SG_VALIDATE_PASSDESC_IMAGE_SIZES: return "all pass attachments must have the same size"; + case _SG_VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS: return "all pass attachments must have the same sample count"; + + /* sg_begin_pass */ + case _SG_VALIDATE_BEGINPASS_PASS: return "sg_begin_pass: pass must be valid"; + case _SG_VALIDATE_BEGINPASS_IMAGE: return "sg_begin_pass: one or more attachment images are not valid"; + + /* sg_apply_pipeline */ + case _SG_VALIDATE_APIP_PIPELINE_VALID_ID: return "sg_apply_pipeline: invalid pipeline id provided"; + case _SG_VALIDATE_APIP_PIPELINE_EXISTS: return "sg_apply_pipeline: pipeline object no longer alive"; + case _SG_VALIDATE_APIP_PIPELINE_VALID: return "sg_apply_pipeline: pipeline object not in valid state"; + case _SG_VALIDATE_APIP_SHADER_EXISTS: return "sg_apply_pipeline: shader object no longer alive"; + case _SG_VALIDATE_APIP_SHADER_VALID: return "sg_apply_pipeline: shader object not in valid state"; + case _SG_VALIDATE_APIP_ATT_COUNT: return "sg_apply_pipeline: number of pipeline color attachments doesn't match number of pass color attachments"; + case _SG_VALIDATE_APIP_COLOR_FORMAT: return "sg_apply_pipeline: pipeline color attachment pixel format doesn't match pass color attachment pixel format"; + case _SG_VALIDATE_APIP_DEPTH_FORMAT: return "sg_apply_pipeline: pipeline depth pixel_format doesn't match pass depth attachment pixel format"; + case _SG_VALIDATE_APIP_SAMPLE_COUNT: return "sg_apply_pipeline: pipeline MSAA sample count doesn't match render pass attachment sample count"; + + /* sg_apply_bindings */ + case _SG_VALIDATE_ABND_PIPELINE: return "sg_apply_bindings: must be called after sg_apply_pipeline"; + case _SG_VALIDATE_ABND_PIPELINE_EXISTS: return "sg_apply_bindings: currently applied pipeline object no longer alive"; + case _SG_VALIDATE_ABND_PIPELINE_VALID: return "sg_apply_bindings: currently applied pipeline object not in valid state"; + case _SG_VALIDATE_ABND_VBS: return "sg_apply_bindings: number of vertex buffers doesn't match number of pipeline vertex layouts"; + case _SG_VALIDATE_ABND_VB_EXISTS: return "sg_apply_bindings: vertex buffer no longer alive"; + case _SG_VALIDATE_ABND_VB_TYPE: return "sg_apply_bindings: buffer in vertex buffer slot is not a SG_BUFFERTYPE_VERTEXBUFFER"; + case _SG_VALIDATE_ABND_VB_OVERFLOW: return "sg_apply_bindings: buffer in vertex buffer slot is overflown"; + case _SG_VALIDATE_ABND_NO_IB: return "sg_apply_bindings: pipeline object defines indexed rendering, but no index buffer provided"; + case _SG_VALIDATE_ABND_IB: return "sg_apply_bindings: pipeline object defines non-indexed rendering, but index buffer provided"; + case _SG_VALIDATE_ABND_IB_EXISTS: return "sg_apply_bindings: index buffer no longer alive"; + case _SG_VALIDATE_ABND_IB_TYPE: return "sg_apply_bindings: buffer in index buffer slot is not a SG_BUFFERTYPE_INDEXBUFFER"; + case _SG_VALIDATE_ABND_IB_OVERFLOW: return "sg_apply_bindings: buffer in index buffer slot is overflown"; + case _SG_VALIDATE_ABND_VS_IMGS: return "sg_apply_bindings: vertex shader image count doesn't match sg_shader_desc"; + case _SG_VALIDATE_ABND_VS_IMG_EXISTS: return "sg_apply_bindings: vertex shader image no longer alive"; + case _SG_VALIDATE_ABND_VS_IMG_TYPES: return "sg_apply_bindings: one or more vertex shader image types don't match sg_shader_desc"; + case _SG_VALIDATE_ABND_FS_IMGS: return "sg_apply_bindings: fragment shader image count doesn't match sg_shader_desc"; + case _SG_VALIDATE_ABND_FS_IMG_EXISTS: return "sg_apply_bindings: fragment shader image no longer alive"; + case _SG_VALIDATE_ABND_FS_IMG_TYPES: return "sg_apply_bindings: one or more fragment shader image types don't match sg_shader_desc"; + + /* sg_apply_uniforms */ + case _SG_VALIDATE_AUB_NO_PIPELINE: return "sg_apply_uniforms: must be called after sg_apply_pipeline()"; + case _SG_VALIDATE_AUB_NO_UB_AT_SLOT: return "sg_apply_uniforms: no uniform block declaration at this shader stage UB slot"; + case _SG_VALIDATE_AUB_SIZE: return "sg_apply_uniforms: data size exceeds declared uniform block size"; + + /* sg_update_buffer */ + case _SG_VALIDATE_UPDATEBUF_USAGE: return "sg_update_buffer: cannot update immutable buffer"; + case _SG_VALIDATE_UPDATEBUF_SIZE: return "sg_update_buffer: update size is bigger than buffer size"; + case _SG_VALIDATE_UPDATEBUF_ONCE: return "sg_update_buffer: only one update allowed per buffer and frame"; + case _SG_VALIDATE_UPDATEBUF_APPEND: return "sg_update_buffer: cannot call sg_update_buffer and sg_append_buffer in same frame"; + + /* sg_append_buffer */ + case _SG_VALIDATE_APPENDBUF_USAGE: return "sg_append_buffer: cannot append to immutable buffer"; + case _SG_VALIDATE_APPENDBUF_SIZE: return "sg_append_buffer: overall appended size is bigger than buffer size"; + case _SG_VALIDATE_APPENDBUF_UPDATE: return "sg_append_buffer: cannot call sg_append_buffer and sg_update_buffer in same frame"; + + /* sg_update_image */ + case _SG_VALIDATE_UPDIMG_USAGE: return "sg_update_image: cannot update immutable image"; + case _SG_VALIDATE_UPDIMG_NOTENOUGHDATA: return "sg_update_image: not enough subimage data provided"; + case _SG_VALIDATE_UPDIMG_SIZE: return "sg_update_image: provided subimage data size too big"; + case _SG_VALIDATE_UPDIMG_COMPRESSED: return "sg_update_image: cannot update images with compressed format"; + case _SG_VALIDATE_UPDIMG_ONCE: return "sg_update_image: only one update allowed per image and frame"; + + default: return "unknown validation error"; + } +} +#endif /* defined(SOKOL_DEBUG) */ + +/*-- validation checks -------------------------------------------------------*/ +#if defined(SOKOL_DEBUG) +_SOKOL_PRIVATE void _sg_validate_begin(void) { + _sg.validate_error = _SG_VALIDATE_SUCCESS; +} + +_SOKOL_PRIVATE void _sg_validate(bool cond, _sg_validate_error_t err) { + if (!cond) { + _sg.validate_error = err; + SOKOL_LOG(_sg_validate_string(err)); + } +} + +_SOKOL_PRIVATE bool _sg_validate_end(void) { + if (_sg.validate_error != _SG_VALIDATE_SUCCESS) { + #if !defined(SOKOL_VALIDATE_NON_FATAL) + SOKOL_LOG("^^^^ VALIDATION FAILED, TERMINATING ^^^^"); + SOKOL_ASSERT(false); + #endif + return false; + } + else { + return true; + } +} +#endif + +_SOKOL_PRIVATE bool _sg_validate_buffer_desc(const sg_buffer_desc* desc) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(desc); + return true; + #else + SOKOL_ASSERT(desc); + SOKOL_VALIDATE_BEGIN(); + SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_BUFFERDESC_CANARY); + SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_BUFFERDESC_CANARY); + SOKOL_VALIDATE(desc->size > 0, _SG_VALIDATE_BUFFERDESC_SIZE); + bool injected = (0 != desc->gl_buffers[0]) || + (0 != desc->mtl_buffers[0]) || + (0 != desc->d3d11_buffer) || + (0 != desc->wgpu_buffer); + if (!injected && (desc->usage == SG_USAGE_IMMUTABLE)) { + SOKOL_VALIDATE((0 != desc->data.ptr) && (desc->data.size > 0), _SG_VALIDATE_BUFFERDESC_DATA); + SOKOL_VALIDATE(desc->size == desc->data.size, _SG_VALIDATE_BUFFERDESC_DATA_SIZE); + } + else { + SOKOL_VALIDATE(0 == desc->data.ptr, _SG_VALIDATE_BUFFERDESC_NO_DATA); + } + return SOKOL_VALIDATE_END(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_image_desc(const sg_image_desc* desc) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(desc); + return true; + #else + SOKOL_ASSERT(desc); + SOKOL_VALIDATE_BEGIN(); + SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_IMAGEDESC_CANARY); + SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_IMAGEDESC_CANARY); + SOKOL_VALIDATE(desc->width > 0, _SG_VALIDATE_IMAGEDESC_WIDTH); + SOKOL_VALIDATE(desc->height > 0, _SG_VALIDATE_IMAGEDESC_HEIGHT); + const sg_pixel_format fmt = desc->pixel_format; + const sg_usage usage = desc->usage; + const bool injected = (0 != desc->gl_textures[0]) || + (0 != desc->mtl_textures[0]) || + (0 != desc->d3d11_texture) || + (0 != desc->wgpu_texture); + if (desc->render_target) { + SOKOL_ASSERT(((int)fmt >= 0) && ((int)fmt < _SG_PIXELFORMAT_NUM)); + SOKOL_VALIDATE(_sg.formats[fmt].render, _SG_VALIDATE_IMAGEDESC_RT_PIXELFORMAT); + /* on GLES2, sample count for render targets is completely ignored */ + #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + if (!_sg.gl.gles2) { + #endif + if (desc->sample_count > 1) { + SOKOL_VALIDATE(_sg.features.msaa_render_targets && _sg.formats[fmt].msaa, _SG_VALIDATE_IMAGEDESC_NO_MSAA_RT_SUPPORT); + } + #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + } + #endif + SOKOL_VALIDATE(usage == SG_USAGE_IMMUTABLE, _SG_VALIDATE_IMAGEDESC_RT_IMMUTABLE); + SOKOL_VALIDATE(desc->data.subimage[0][0].ptr==0, _SG_VALIDATE_IMAGEDESC_RT_NO_DATA); + } + else { + SOKOL_VALIDATE(desc->sample_count <= 1, _SG_VALIDATE_IMAGEDESC_MSAA_BUT_NO_RT); + const bool valid_nonrt_fmt = !_sg_is_valid_rendertarget_depth_format(fmt); + SOKOL_VALIDATE(valid_nonrt_fmt, _SG_VALIDATE_IMAGEDESC_NONRT_PIXELFORMAT); + /* FIXME: should use the same "expected size" computation as in _sg_validate_update_image() here */ + if (!injected && (usage == SG_USAGE_IMMUTABLE)) { + const int num_faces = desc->type == SG_IMAGETYPE_CUBE ? 6:1; + const int num_mips = desc->num_mipmaps; + for (int face_index = 0; face_index < num_faces; face_index++) { + for (int mip_index = 0; mip_index < num_mips; mip_index++) { + const bool has_data = desc->data.subimage[face_index][mip_index].ptr != 0; + const bool has_size = desc->data.subimage[face_index][mip_index].size > 0; + SOKOL_VALIDATE(has_data && has_size, _SG_VALIDATE_IMAGEDESC_DATA); + } + } + } + else { + for (int face_index = 0; face_index < SG_CUBEFACE_NUM; face_index++) { + for (int mip_index = 0; mip_index < SG_MAX_MIPMAPS; mip_index++) { + const bool no_data = 0 == desc->data.subimage[face_index][mip_index].ptr; + const bool no_size = 0 == desc->data.subimage[face_index][mip_index].size; + SOKOL_VALIDATE(no_data && no_size, _SG_VALIDATE_IMAGEDESC_NO_DATA); + } + } + } + } + return SOKOL_VALIDATE_END(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_shader_desc(const sg_shader_desc* desc) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(desc); + return true; + #else + SOKOL_ASSERT(desc); + SOKOL_VALIDATE_BEGIN(); + SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_SHADERDESC_CANARY); + SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_SHADERDESC_CANARY); + #if defined(SOKOL_GLES2) + SOKOL_VALIDATE(0 != desc->attrs[0].name, _SG_VALIDATE_SHADERDESC_ATTR_NAMES); + #elif defined(SOKOL_D3D11) + SOKOL_VALIDATE(0 != desc->attrs[0].sem_name, _SG_VALIDATE_SHADERDESC_ATTR_SEMANTICS); + #endif + #if defined(SOKOL_GLCORE33) || defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + /* on GL, must provide shader source code */ + SOKOL_VALIDATE(0 != desc->vs.source, _SG_VALIDATE_SHADERDESC_SOURCE); + SOKOL_VALIDATE(0 != desc->fs.source, _SG_VALIDATE_SHADERDESC_SOURCE); + #elif defined(SOKOL_METAL) || defined(SOKOL_D3D11) + /* on Metal or D3D11, must provide shader source code or byte code */ + SOKOL_VALIDATE((0 != desc->vs.source)||(0 != desc->vs.bytecode.ptr), _SG_VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE); + SOKOL_VALIDATE((0 != desc->fs.source)||(0 != desc->fs.bytecode.ptr), _SG_VALIDATE_SHADERDESC_SOURCE_OR_BYTECODE); + #elif defined(SOKOL_WGPU) + /* on WGPU byte code must be provided */ + SOKOL_VALIDATE((0 != desc->vs.bytecode.ptr), _SG_VALIDATE_SHADERDESC_BYTECODE); + SOKOL_VALIDATE((0 != desc->fs.bytecode.ptr), _SG_VALIDATE_SHADERDESC_BYTECODE); + #else + /* Dummy Backend, don't require source or bytecode */ + #endif + for (int i = 0; i < SG_MAX_VERTEX_ATTRIBUTES; i++) { + if (desc->attrs[i].name) { + SOKOL_VALIDATE(strlen(desc->attrs[i].name) < _SG_STRING_SIZE, _SG_VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG); + } + if (desc->attrs[i].sem_name) { + SOKOL_VALIDATE(strlen(desc->attrs[i].sem_name) < _SG_STRING_SIZE, _SG_VALIDATE_SHADERDESC_ATTR_STRING_TOO_LONG); + } + } + /* if shader byte code, the size must also be provided */ + if (0 != desc->vs.bytecode.ptr) { + SOKOL_VALIDATE(desc->vs.bytecode.size > 0, _SG_VALIDATE_SHADERDESC_NO_BYTECODE_SIZE); + } + if (0 != desc->fs.bytecode.ptr) { + SOKOL_VALIDATE(desc->fs.bytecode.size > 0, _SG_VALIDATE_SHADERDESC_NO_BYTECODE_SIZE); + } + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + const sg_shader_stage_desc* stage_desc = (stage_index == 0)? &desc->vs : &desc->fs; + bool uniform_blocks_continuous = true; + for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { + const sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; + if (ub_desc->size > 0) { + SOKOL_VALIDATE(uniform_blocks_continuous, _SG_VALIDATE_SHADERDESC_NO_CONT_UBS); + bool uniforms_continuous = true; + int uniform_offset = 0; + int num_uniforms = 0; + for (int u_index = 0; u_index < SG_MAX_UB_MEMBERS; u_index++) { + const sg_shader_uniform_desc* u_desc = &ub_desc->uniforms[u_index]; + if (u_desc->type != SG_UNIFORMTYPE_INVALID) { + SOKOL_VALIDATE(uniforms_continuous, _SG_VALIDATE_SHADERDESC_NO_CONT_UB_MEMBERS); + #if defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + SOKOL_VALIDATE(0 != u_desc->name, _SG_VALIDATE_SHADERDESC_UB_MEMBER_NAME); + #endif + const int array_count = u_desc->array_count; + uniform_offset += _sg_uniform_size(u_desc->type, array_count); + num_uniforms++; + } + else { + uniforms_continuous = false; + } + } + #if defined(SOKOL_GLCORE33) || defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + SOKOL_VALIDATE((size_t)uniform_offset == ub_desc->size, _SG_VALIDATE_SHADERDESC_UB_SIZE_MISMATCH); + SOKOL_VALIDATE(num_uniforms > 0, _SG_VALIDATE_SHADERDESC_NO_UB_MEMBERS); + #endif + } + else { + uniform_blocks_continuous = false; + } + } + bool images_continuous = true; + for (int img_index = 0; img_index < SG_MAX_SHADERSTAGE_IMAGES; img_index++) { + const sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; + if (img_desc->image_type != _SG_IMAGETYPE_DEFAULT) { + SOKOL_VALIDATE(images_continuous, _SG_VALIDATE_SHADERDESC_NO_CONT_IMGS); + #if defined(SOKOL_GLES2) + SOKOL_VALIDATE(0 != img_desc->name, _SG_VALIDATE_SHADERDESC_IMG_NAME); + #endif + } + else { + images_continuous = false; + } + } + } + return SOKOL_VALIDATE_END(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_pipeline_desc(const sg_pipeline_desc* desc) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(desc); + return true; + #else + SOKOL_ASSERT(desc); + SOKOL_VALIDATE_BEGIN(); + SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_PIPELINEDESC_CANARY); + SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_PIPELINEDESC_CANARY); + SOKOL_VALIDATE(desc->shader.id != SG_INVALID_ID, _SG_VALIDATE_PIPELINEDESC_SHADER); + for (int buf_index = 0; buf_index < SG_MAX_SHADERSTAGE_BUFFERS; buf_index++) { + const sg_buffer_layout_desc* l_desc = &desc->layout.buffers[buf_index]; + if (l_desc->stride == 0) { + continue; + } + SOKOL_VALIDATE((l_desc->stride & 3) == 0, _SG_VALIDATE_PIPELINEDESC_LAYOUT_STRIDE4); + } + SOKOL_VALIDATE(desc->layout.attrs[0].format != SG_VERTEXFORMAT_INVALID, _SG_VALIDATE_PIPELINEDESC_NO_ATTRS); + const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, desc->shader.id); + SOKOL_VALIDATE(0 != shd, _SG_VALIDATE_PIPELINEDESC_SHADER); + if (shd) { + SOKOL_VALIDATE(shd->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_PIPELINEDESC_SHADER); + bool attrs_cont = true; + for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + const sg_vertex_attr_desc* a_desc = &desc->layout.attrs[attr_index]; + if (a_desc->format == SG_VERTEXFORMAT_INVALID) { + attrs_cont = false; + continue; + } + SOKOL_VALIDATE(attrs_cont, _SG_VALIDATE_PIPELINEDESC_NO_ATTRS); + SOKOL_ASSERT(a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS); + #if defined(SOKOL_GLES2) + /* on GLES2, vertex attribute names must be provided */ + SOKOL_VALIDATE(!_sg_strempty(&shd->gl.attrs[attr_index].name), _SG_VALIDATE_PIPELINEDESC_ATTR_NAME); + #elif defined(SOKOL_D3D11) + /* on D3D11, semantic names (and semantic indices) must be provided */ + SOKOL_VALIDATE(!_sg_strempty(&shd->d3d11.attrs[attr_index].sem_name), _SG_VALIDATE_PIPELINEDESC_ATTR_SEMANTICS); + #endif + } + } + return SOKOL_VALIDATE_END(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_pass_desc(const sg_pass_desc* desc) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(desc); + return true; + #else + SOKOL_ASSERT(desc); + SOKOL_VALIDATE_BEGIN(); + SOKOL_VALIDATE(desc->_start_canary == 0, _SG_VALIDATE_PASSDESC_CANARY); + SOKOL_VALIDATE(desc->_end_canary == 0, _SG_VALIDATE_PASSDESC_CANARY); + bool atts_cont = true; + int width = -1, height = -1, sample_count = -1; + for (int att_index = 0; att_index < SG_MAX_COLOR_ATTACHMENTS; att_index++) { + const sg_pass_attachment_desc* att = &desc->color_attachments[att_index]; + if (att->image.id == SG_INVALID_ID) { + SOKOL_VALIDATE(att_index > 0, _SG_VALIDATE_PASSDESC_NO_COLOR_ATTS); + atts_cont = false; + continue; + } + SOKOL_VALIDATE(atts_cont, _SG_VALIDATE_PASSDESC_NO_CONT_COLOR_ATTS); + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, att->image.id); + SOKOL_ASSERT(img); + SOKOL_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_PASSDESC_IMAGE); + SOKOL_VALIDATE(att->mip_level < img->cmn.num_mipmaps, _SG_VALIDATE_PASSDESC_MIPLEVEL); + if (img->cmn.type == SG_IMAGETYPE_CUBE) { + SOKOL_VALIDATE(att->slice < 6, _SG_VALIDATE_PASSDESC_FACE); + } + else if (img->cmn.type == SG_IMAGETYPE_ARRAY) { + SOKOL_VALIDATE(att->slice < img->cmn.num_slices, _SG_VALIDATE_PASSDESC_LAYER); + } + else if (img->cmn.type == SG_IMAGETYPE_3D) { + SOKOL_VALIDATE(att->slice < img->cmn.num_slices, _SG_VALIDATE_PASSDESC_SLICE); + } + SOKOL_VALIDATE(img->cmn.render_target, _SG_VALIDATE_PASSDESC_IMAGE_NO_RT); + if (att_index == 0) { + width = img->cmn.width >> att->mip_level; + height = img->cmn.height >> att->mip_level; + sample_count = img->cmn.sample_count; + } + else { + SOKOL_VALIDATE(width == img->cmn.width >> att->mip_level, _SG_VALIDATE_PASSDESC_IMAGE_SIZES); + SOKOL_VALIDATE(height == img->cmn.height >> att->mip_level, _SG_VALIDATE_PASSDESC_IMAGE_SIZES); + SOKOL_VALIDATE(sample_count == img->cmn.sample_count, _SG_VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS); + } + SOKOL_VALIDATE(_sg_is_valid_rendertarget_color_format(img->cmn.pixel_format), _SG_VALIDATE_PASSDESC_COLOR_INV_PIXELFORMAT); + } + if (desc->depth_stencil_attachment.image.id != SG_INVALID_ID) { + const sg_pass_attachment_desc* att = &desc->depth_stencil_attachment; + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, att->image.id); + SOKOL_ASSERT(img); + SOKOL_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_PASSDESC_IMAGE); + SOKOL_VALIDATE(att->mip_level < img->cmn.num_mipmaps, _SG_VALIDATE_PASSDESC_MIPLEVEL); + if (img->cmn.type == SG_IMAGETYPE_CUBE) { + SOKOL_VALIDATE(att->slice < 6, _SG_VALIDATE_PASSDESC_FACE); + } + else if (img->cmn.type == SG_IMAGETYPE_ARRAY) { + SOKOL_VALIDATE(att->slice < img->cmn.num_slices, _SG_VALIDATE_PASSDESC_LAYER); + } + else if (img->cmn.type == SG_IMAGETYPE_3D) { + SOKOL_VALIDATE(att->slice < img->cmn.num_slices, _SG_VALIDATE_PASSDESC_SLICE); + } + SOKOL_VALIDATE(img->cmn.render_target, _SG_VALIDATE_PASSDESC_IMAGE_NO_RT); + SOKOL_VALIDATE(width == img->cmn.width >> att->mip_level, _SG_VALIDATE_PASSDESC_IMAGE_SIZES); + SOKOL_VALIDATE(height == img->cmn.height >> att->mip_level, _SG_VALIDATE_PASSDESC_IMAGE_SIZES); + SOKOL_VALIDATE(sample_count == img->cmn.sample_count, _SG_VALIDATE_PASSDESC_IMAGE_SAMPLE_COUNTS); + SOKOL_VALIDATE(_sg_is_valid_rendertarget_depth_format(img->cmn.pixel_format), _SG_VALIDATE_PASSDESC_DEPTH_INV_PIXELFORMAT); + } + return SOKOL_VALIDATE_END(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_begin_pass(_sg_pass_t* pass) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(pass); + return true; + #else + SOKOL_VALIDATE_BEGIN(); + SOKOL_VALIDATE(pass->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_BEGINPASS_PASS); + + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + const _sg_pass_attachment_t* att = &pass->cmn.color_atts[i]; + const _sg_image_t* img = _sg_pass_color_image(pass, i); + if (img) { + SOKOL_VALIDATE(img->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_BEGINPASS_IMAGE); + SOKOL_VALIDATE(img->slot.id == att->image_id.id, _SG_VALIDATE_BEGINPASS_IMAGE); + } + } + const _sg_image_t* ds_img = _sg_pass_ds_image(pass); + if (ds_img) { + const _sg_pass_attachment_t* att = &pass->cmn.ds_att; + SOKOL_VALIDATE(ds_img->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_BEGINPASS_IMAGE); + SOKOL_VALIDATE(ds_img->slot.id == att->image_id.id, _SG_VALIDATE_BEGINPASS_IMAGE); + } + return SOKOL_VALIDATE_END(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_apply_pipeline(sg_pipeline pip_id) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(pip_id); + return true; + #else + SOKOL_VALIDATE_BEGIN(); + /* the pipeline object must be alive and valid */ + SOKOL_VALIDATE(pip_id.id != SG_INVALID_ID, _SG_VALIDATE_APIP_PIPELINE_VALID_ID); + const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + SOKOL_VALIDATE(pip != 0, _SG_VALIDATE_APIP_PIPELINE_EXISTS); + if (!pip) { + return SOKOL_VALIDATE_END(); + } + SOKOL_VALIDATE(pip->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_APIP_PIPELINE_VALID); + /* the pipeline's shader must be alive and valid */ + SOKOL_ASSERT(pip->shader); + SOKOL_VALIDATE(pip->shader->slot.id == pip->cmn.shader_id.id, _SG_VALIDATE_APIP_SHADER_EXISTS); + SOKOL_VALIDATE(pip->shader->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_APIP_SHADER_VALID); + /* check that pipeline attributes match current pass attributes */ + const _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, _sg.cur_pass.id); + if (pass) { + /* an offscreen pass */ + SOKOL_VALIDATE(pip->cmn.color_attachment_count == pass->cmn.num_color_atts, _SG_VALIDATE_APIP_ATT_COUNT); + for (int i = 0; i < pip->cmn.color_attachment_count; i++) { + const _sg_image_t* att_img = _sg_pass_color_image(pass, i); + SOKOL_VALIDATE(pip->cmn.color_formats[i] == att_img->cmn.pixel_format, _SG_VALIDATE_APIP_COLOR_FORMAT); + SOKOL_VALIDATE(pip->cmn.sample_count == att_img->cmn.sample_count, _SG_VALIDATE_APIP_SAMPLE_COUNT); + } + const _sg_image_t* att_dsimg = _sg_pass_ds_image(pass); + if (att_dsimg) { + SOKOL_VALIDATE(pip->cmn.depth_format == att_dsimg->cmn.pixel_format, _SG_VALIDATE_APIP_DEPTH_FORMAT); + } + else { + SOKOL_VALIDATE(pip->cmn.depth_format == SG_PIXELFORMAT_NONE, _SG_VALIDATE_APIP_DEPTH_FORMAT); + } + } + else { + /* default pass */ + SOKOL_VALIDATE(pip->cmn.color_attachment_count == 1, _SG_VALIDATE_APIP_ATT_COUNT); + SOKOL_VALIDATE(pip->cmn.color_formats[0] == _sg.desc.context.color_format, _SG_VALIDATE_APIP_COLOR_FORMAT); + SOKOL_VALIDATE(pip->cmn.depth_format == _sg.desc.context.depth_format, _SG_VALIDATE_APIP_DEPTH_FORMAT); + SOKOL_VALIDATE(pip->cmn.sample_count == _sg.desc.context.sample_count, _SG_VALIDATE_APIP_SAMPLE_COUNT); + } + return SOKOL_VALIDATE_END(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_apply_bindings(const sg_bindings* bindings) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(bindings); + return true; + #else + SOKOL_VALIDATE_BEGIN(); + + /* a pipeline object must have been applied */ + SOKOL_VALIDATE(_sg.cur_pipeline.id != SG_INVALID_ID, _SG_VALIDATE_ABND_PIPELINE); + const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, _sg.cur_pipeline.id); + SOKOL_VALIDATE(pip != 0, _SG_VALIDATE_ABND_PIPELINE_EXISTS); + if (!pip) { + return SOKOL_VALIDATE_END(); + } + SOKOL_VALIDATE(pip->slot.state == SG_RESOURCESTATE_VALID, _SG_VALIDATE_ABND_PIPELINE_VALID); + SOKOL_ASSERT(pip->shader && (pip->cmn.shader_id.id == pip->shader->slot.id)); + + /* has expected vertex buffers, and vertex buffers still exist */ + for (int i = 0; i < SG_MAX_SHADERSTAGE_BUFFERS; i++) { + if (bindings->vertex_buffers[i].id != SG_INVALID_ID) { + SOKOL_VALIDATE(pip->cmn.vertex_layout_valid[i], _SG_VALIDATE_ABND_VBS); + /* buffers in vertex-buffer-slots must be of type SG_BUFFERTYPE_VERTEXBUFFER */ + const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, bindings->vertex_buffers[i].id); + SOKOL_VALIDATE(buf != 0, _SG_VALIDATE_ABND_VB_EXISTS); + if (buf && buf->slot.state == SG_RESOURCESTATE_VALID) { + SOKOL_VALIDATE(SG_BUFFERTYPE_VERTEXBUFFER == buf->cmn.type, _SG_VALIDATE_ABND_VB_TYPE); + SOKOL_VALIDATE(!buf->cmn.append_overflow, _SG_VALIDATE_ABND_VB_OVERFLOW); + } + } + else { + /* vertex buffer provided in a slot which has no vertex layout in pipeline */ + SOKOL_VALIDATE(!pip->cmn.vertex_layout_valid[i], _SG_VALIDATE_ABND_VBS); + } + } + + /* index buffer expected or not, and index buffer still exists */ + if (pip->cmn.index_type == SG_INDEXTYPE_NONE) { + /* pipeline defines non-indexed rendering, but index buffer provided */ + SOKOL_VALIDATE(bindings->index_buffer.id == SG_INVALID_ID, _SG_VALIDATE_ABND_IB); + } + else { + /* pipeline defines indexed rendering, but no index buffer provided */ + SOKOL_VALIDATE(bindings->index_buffer.id != SG_INVALID_ID, _SG_VALIDATE_ABND_NO_IB); + } + if (bindings->index_buffer.id != SG_INVALID_ID) { + /* buffer in index-buffer-slot must be of type SG_BUFFERTYPE_INDEXBUFFER */ + const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, bindings->index_buffer.id); + SOKOL_VALIDATE(buf != 0, _SG_VALIDATE_ABND_IB_EXISTS); + if (buf && buf->slot.state == SG_RESOURCESTATE_VALID) { + SOKOL_VALIDATE(SG_BUFFERTYPE_INDEXBUFFER == buf->cmn.type, _SG_VALIDATE_ABND_IB_TYPE); + SOKOL_VALIDATE(!buf->cmn.append_overflow, _SG_VALIDATE_ABND_IB_OVERFLOW); + } + } + + /* has expected vertex shader images */ + for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { + _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_VS]; + if (bindings->vs_images[i].id != SG_INVALID_ID) { + SOKOL_VALIDATE(i < stage->num_images, _SG_VALIDATE_ABND_VS_IMGS); + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, bindings->vs_images[i].id); + SOKOL_VALIDATE(img != 0, _SG_VALIDATE_ABND_VS_IMG_EXISTS); + if (img && img->slot.state == SG_RESOURCESTATE_VALID) { + SOKOL_VALIDATE(img->cmn.type == stage->images[i].image_type, _SG_VALIDATE_ABND_VS_IMG_TYPES); + } + } + else { + SOKOL_VALIDATE(i >= stage->num_images, _SG_VALIDATE_ABND_VS_IMGS); + } + } + + /* has expected fragment shader images */ + for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++) { + _sg_shader_stage_t* stage = &pip->shader->cmn.stage[SG_SHADERSTAGE_FS]; + if (bindings->fs_images[i].id != SG_INVALID_ID) { + SOKOL_VALIDATE(i < stage->num_images, _SG_VALIDATE_ABND_FS_IMGS); + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, bindings->fs_images[i].id); + SOKOL_VALIDATE(img != 0, _SG_VALIDATE_ABND_FS_IMG_EXISTS); + if (img && img->slot.state == SG_RESOURCESTATE_VALID) { + SOKOL_VALIDATE(img->cmn.type == stage->images[i].image_type, _SG_VALIDATE_ABND_FS_IMG_TYPES); + } + } + else { + SOKOL_VALIDATE(i >= stage->num_images, _SG_VALIDATE_ABND_FS_IMGS); + } + } + return SOKOL_VALIDATE_END(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_apply_uniforms(sg_shader_stage stage_index, int ub_index, const sg_range* data) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(stage_index); + _SOKOL_UNUSED(ub_index); + _SOKOL_UNUSED(data); + return true; + #else + SOKOL_ASSERT((stage_index == SG_SHADERSTAGE_VS) || (stage_index == SG_SHADERSTAGE_FS)); + SOKOL_ASSERT((ub_index >= 0) && (ub_index < SG_MAX_SHADERSTAGE_UBS)); + SOKOL_VALIDATE_BEGIN(); + SOKOL_VALIDATE(_sg.cur_pipeline.id != SG_INVALID_ID, _SG_VALIDATE_AUB_NO_PIPELINE); + const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, _sg.cur_pipeline.id); + SOKOL_ASSERT(pip && (pip->slot.id == _sg.cur_pipeline.id)); + SOKOL_ASSERT(pip->shader && (pip->shader->slot.id == pip->cmn.shader_id.id)); + + /* check that there is a uniform block at 'stage' and 'ub_index' */ + const _sg_shader_stage_t* stage = &pip->shader->cmn.stage[stage_index]; + SOKOL_VALIDATE(ub_index < stage->num_uniform_blocks, _SG_VALIDATE_AUB_NO_UB_AT_SLOT); + + /* check that the provided data size doesn't exceed the uniform block size */ + SOKOL_VALIDATE(data->size <= stage->uniform_blocks[ub_index].size, _SG_VALIDATE_AUB_SIZE); + + return SOKOL_VALIDATE_END(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_update_buffer(const _sg_buffer_t* buf, const sg_range* data) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(buf); + _SOKOL_UNUSED(data); + return true; + #else + SOKOL_ASSERT(buf && data && data->ptr); + SOKOL_VALIDATE_BEGIN(); + SOKOL_VALIDATE(buf->cmn.usage != SG_USAGE_IMMUTABLE, _SG_VALIDATE_UPDATEBUF_USAGE); + SOKOL_VALIDATE(buf->cmn.size >= (int)data->size, _SG_VALIDATE_UPDATEBUF_SIZE); + SOKOL_VALIDATE(buf->cmn.update_frame_index != _sg.frame_index, _SG_VALIDATE_UPDATEBUF_ONCE); + SOKOL_VALIDATE(buf->cmn.append_frame_index != _sg.frame_index, _SG_VALIDATE_UPDATEBUF_APPEND); + return SOKOL_VALIDATE_END(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_append_buffer(const _sg_buffer_t* buf, const sg_range* data) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(buf); + _SOKOL_UNUSED(data); + return true; + #else + SOKOL_ASSERT(buf && data && data->ptr); + SOKOL_VALIDATE_BEGIN(); + SOKOL_VALIDATE(buf->cmn.usage != SG_USAGE_IMMUTABLE, _SG_VALIDATE_APPENDBUF_USAGE); + SOKOL_VALIDATE(buf->cmn.size >= (buf->cmn.append_pos + (int)data->size), _SG_VALIDATE_APPENDBUF_SIZE); + SOKOL_VALIDATE(buf->cmn.update_frame_index != _sg.frame_index, _SG_VALIDATE_APPENDBUF_UPDATE); + return SOKOL_VALIDATE_END(); + #endif +} + +_SOKOL_PRIVATE bool _sg_validate_update_image(const _sg_image_t* img, const sg_image_data* data) { + #if !defined(SOKOL_DEBUG) + _SOKOL_UNUSED(img); + _SOKOL_UNUSED(data); + return true; + #else + SOKOL_ASSERT(img && data); + SOKOL_VALIDATE_BEGIN(); + SOKOL_VALIDATE(img->cmn.usage != SG_USAGE_IMMUTABLE, _SG_VALIDATE_UPDIMG_USAGE); + SOKOL_VALIDATE(img->cmn.upd_frame_index != _sg.frame_index, _SG_VALIDATE_UPDIMG_ONCE); + SOKOL_VALIDATE(!_sg_is_compressed_pixel_format(img->cmn.pixel_format), _SG_VALIDATE_UPDIMG_COMPRESSED); + const int num_faces = (img->cmn.type == SG_IMAGETYPE_CUBE) ? 6 : 1; + const int num_mips = img->cmn.num_mipmaps; + for (int face_index = 0; face_index < num_faces; face_index++) { + for (int mip_index = 0; mip_index < num_mips; mip_index++) { + SOKOL_VALIDATE(0 != data->subimage[face_index][mip_index].ptr, _SG_VALIDATE_UPDIMG_NOTENOUGHDATA); + const int mip_width = _sg_max(img->cmn.width >> mip_index, 1); + const int mip_height = _sg_max(img->cmn.height >> mip_index, 1); + const int bytes_per_slice = _sg_surface_pitch(img->cmn.pixel_format, mip_width, mip_height, 1); + const int expected_size = bytes_per_slice * img->cmn.num_slices; + SOKOL_VALIDATE(data->subimage[face_index][mip_index].size <= (size_t)expected_size, _SG_VALIDATE_UPDIMG_SIZE); + } + } + return SOKOL_VALIDATE_END(); + #endif +} + +/*== fill in desc default values =============================================*/ +_SOKOL_PRIVATE sg_buffer_desc _sg_buffer_desc_defaults(const sg_buffer_desc* desc) { + sg_buffer_desc def = *desc; + def.type = _sg_def(def.type, SG_BUFFERTYPE_VERTEXBUFFER); + def.usage = _sg_def(def.usage, SG_USAGE_IMMUTABLE); + if (def.size == 0) { + def.size = def.data.size; + } + else if (def.data.size == 0) { + def.data.size = def.size; + } + return def; +} + +_SOKOL_PRIVATE sg_image_desc _sg_image_desc_defaults(const sg_image_desc* desc) { + sg_image_desc def = *desc; + def.type = _sg_def(def.type, SG_IMAGETYPE_2D); + def.num_slices = _sg_def(def.num_slices, 1); + def.num_mipmaps = _sg_def(def.num_mipmaps, 1); + def.usage = _sg_def(def.usage, SG_USAGE_IMMUTABLE); + if (desc->render_target) { + def.pixel_format = _sg_def(def.pixel_format, _sg.desc.context.color_format); + def.sample_count = _sg_def(def.sample_count, _sg.desc.context.sample_count); + } + else { + def.pixel_format = _sg_def(def.pixel_format, SG_PIXELFORMAT_RGBA8); + def.sample_count = _sg_def(def.sample_count, 1); + } + def.min_filter = _sg_def(def.min_filter, SG_FILTER_NEAREST); + def.mag_filter = _sg_def(def.mag_filter, SG_FILTER_NEAREST); + def.wrap_u = _sg_def(def.wrap_u, SG_WRAP_REPEAT); + def.wrap_v = _sg_def(def.wrap_v, SG_WRAP_REPEAT); + def.wrap_w = _sg_def(def.wrap_w, SG_WRAP_REPEAT); + def.border_color = _sg_def(def.border_color, SG_BORDERCOLOR_OPAQUE_BLACK); + def.max_anisotropy = _sg_def(def.max_anisotropy, 1); + def.max_lod = _sg_def_flt(def.max_lod, FLT_MAX); + return def; +} + +_SOKOL_PRIVATE sg_shader_desc _sg_shader_desc_defaults(const sg_shader_desc* desc) { + sg_shader_desc def = *desc; + #if defined(SOKOL_METAL) + def.vs.entry = _sg_def(def.vs.entry, "_main"); + def.fs.entry = _sg_def(def.fs.entry, "_main"); + #else + def.vs.entry = _sg_def(def.vs.entry, "main"); + def.fs.entry = _sg_def(def.fs.entry, "main"); + #endif + #if defined(SOKOL_D3D11) + if (def.vs.source) { + def.vs.d3d11_target = _sg_def(def.vs.d3d11_target, "vs_4_0"); + } + if (def.fs.source) { + def.fs.d3d11_target = _sg_def(def.fs.d3d11_target, "ps_4_0"); + } + #endif + for (int stage_index = 0; stage_index < SG_NUM_SHADER_STAGES; stage_index++) { + sg_shader_stage_desc* stage_desc = (stage_index == SG_SHADERSTAGE_VS)? &def.vs : &def.fs; + for (int ub_index = 0; ub_index < SG_MAX_SHADERSTAGE_UBS; ub_index++) { + sg_shader_uniform_block_desc* ub_desc = &stage_desc->uniform_blocks[ub_index]; + if (0 == ub_desc->size) { + break; + } + for (int u_index = 0; u_index < SG_MAX_UB_MEMBERS; u_index++) { + sg_shader_uniform_desc* u_desc = &ub_desc->uniforms[u_index]; + if (u_desc->type == SG_UNIFORMTYPE_INVALID) { + break; + } + u_desc->array_count = _sg_def(u_desc->array_count, 1); + } + } + for (int img_index = 0; img_index < SG_MAX_SHADERSTAGE_IMAGES; img_index++) { + sg_shader_image_desc* img_desc = &stage_desc->images[img_index]; + if (img_desc->image_type == _SG_IMAGETYPE_DEFAULT) { + break; + } + img_desc->sampler_type = _sg_def(img_desc->sampler_type, SG_SAMPLERTYPE_FLOAT); + } + } + return def; +} + +_SOKOL_PRIVATE sg_pipeline_desc _sg_pipeline_desc_defaults(const sg_pipeline_desc* desc) { + sg_pipeline_desc def = *desc; + + def.primitive_type = _sg_def(def.primitive_type, SG_PRIMITIVETYPE_TRIANGLES); + def.index_type = _sg_def(def.index_type, SG_INDEXTYPE_NONE); + def.cull_mode = _sg_def(def.cull_mode, SG_CULLMODE_NONE); + def.face_winding = _sg_def(def.face_winding, SG_FACEWINDING_CW); + def.sample_count = _sg_def(def.sample_count, _sg.desc.context.sample_count); + + def.stencil.front.compare = _sg_def(def.stencil.front.compare, SG_COMPAREFUNC_ALWAYS); + def.stencil.front.fail_op = _sg_def(def.stencil.front.fail_op, SG_STENCILOP_KEEP); + def.stencil.front.depth_fail_op = _sg_def(def.stencil.front.depth_fail_op, SG_STENCILOP_KEEP); + def.stencil.front.pass_op = _sg_def(def.stencil.front.pass_op, SG_STENCILOP_KEEP); + def.stencil.back.compare = _sg_def(def.stencil.back.compare, SG_COMPAREFUNC_ALWAYS); + def.stencil.back.fail_op = _sg_def(def.stencil.back.fail_op, SG_STENCILOP_KEEP); + def.stencil.back.depth_fail_op = _sg_def(def.stencil.back.depth_fail_op, SG_STENCILOP_KEEP); + def.stencil.back.pass_op = _sg_def(def.stencil.back.pass_op, SG_STENCILOP_KEEP); + + def.depth.compare = _sg_def(def.depth.compare, SG_COMPAREFUNC_ALWAYS); + def.depth.pixel_format = _sg_def(def.depth.pixel_format, _sg.desc.context.depth_format); + def.color_count = _sg_def(def.color_count, 1); + if (def.color_count > SG_MAX_COLOR_ATTACHMENTS) { + def.color_count = SG_MAX_COLOR_ATTACHMENTS; + } + for (int i = 0; i < def.color_count; i++) { + sg_color_state* cs = &def.colors[i]; + cs->pixel_format = _sg_def(cs->pixel_format, _sg.desc.context.color_format); + cs->write_mask = _sg_def(cs->write_mask, SG_COLORMASK_RGBA); + sg_blend_state* bs = &def.colors[i].blend; + bs->src_factor_rgb = _sg_def(bs->src_factor_rgb, SG_BLENDFACTOR_ONE); + bs->dst_factor_rgb = _sg_def(bs->dst_factor_rgb, SG_BLENDFACTOR_ZERO); + bs->op_rgb = _sg_def(bs->op_rgb, SG_BLENDOP_ADD); + bs->src_factor_alpha = _sg_def(bs->src_factor_alpha, SG_BLENDFACTOR_ONE); + bs->dst_factor_alpha = _sg_def(bs->dst_factor_alpha, SG_BLENDFACTOR_ZERO); + bs->op_alpha = _sg_def(bs->op_alpha, SG_BLENDOP_ADD); + } + + for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + sg_vertex_attr_desc* a_desc = &def.layout.attrs[attr_index]; + if (a_desc->format == SG_VERTEXFORMAT_INVALID) { + break; + } + SOKOL_ASSERT(a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS); + sg_buffer_layout_desc* b_desc = &def.layout.buffers[a_desc->buffer_index]; + b_desc->step_func = _sg_def(b_desc->step_func, SG_VERTEXSTEP_PER_VERTEX); + b_desc->step_rate = _sg_def(b_desc->step_rate, 1); + } + + /* resolve vertex layout strides and offsets */ + int auto_offset[SG_MAX_SHADERSTAGE_BUFFERS]; + memset(auto_offset, 0, sizeof(auto_offset)); + bool use_auto_offset = true; + for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + /* to use computed offsets, *all* attr offsets must be 0 */ + if (def.layout.attrs[attr_index].offset != 0) { + use_auto_offset = false; + } + } + for (int attr_index = 0; attr_index < SG_MAX_VERTEX_ATTRIBUTES; attr_index++) { + sg_vertex_attr_desc* a_desc = &def.layout.attrs[attr_index]; + if (a_desc->format == SG_VERTEXFORMAT_INVALID) { + break; + } + SOKOL_ASSERT(a_desc->buffer_index < SG_MAX_SHADERSTAGE_BUFFERS); + if (use_auto_offset) { + a_desc->offset = auto_offset[a_desc->buffer_index]; + } + auto_offset[a_desc->buffer_index] += _sg_vertexformat_bytesize(a_desc->format); + } + /* compute vertex strides if needed */ + for (int buf_index = 0; buf_index < SG_MAX_SHADERSTAGE_BUFFERS; buf_index++) { + sg_buffer_layout_desc* l_desc = &def.layout.buffers[buf_index]; + if (l_desc->stride == 0) { + l_desc->stride = auto_offset[buf_index]; + } + } + + return def; +} + +_SOKOL_PRIVATE sg_pass_desc _sg_pass_desc_defaults(const sg_pass_desc* desc) { + /* FIXME: no values to replace in sg_pass_desc? */ + sg_pass_desc def = *desc; + return def; +} + +/*== allocate/initialize resource private functions ==========================*/ +_SOKOL_PRIVATE sg_buffer _sg_alloc_buffer(void) { + sg_buffer res; + int slot_index = _sg_pool_alloc_index(&_sg.pools.buffer_pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id = _sg_slot_alloc(&_sg.pools.buffer_pool, &_sg.pools.buffers[slot_index].slot, slot_index); + } + else { + /* pool is exhausted */ + res.id = SG_INVALID_ID; + } + return res; +} + +_SOKOL_PRIVATE sg_image _sg_alloc_image(void) { + sg_image res; + int slot_index = _sg_pool_alloc_index(&_sg.pools.image_pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id = _sg_slot_alloc(&_sg.pools.image_pool, &_sg.pools.images[slot_index].slot, slot_index); + } + else { + /* pool is exhausted */ + res.id = SG_INVALID_ID; + } + return res; +} + +_SOKOL_PRIVATE sg_shader _sg_alloc_shader(void) { + sg_shader res; + int slot_index = _sg_pool_alloc_index(&_sg.pools.shader_pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id = _sg_slot_alloc(&_sg.pools.shader_pool, &_sg.pools.shaders[slot_index].slot, slot_index); + } + else { + /* pool is exhausted */ + res.id = SG_INVALID_ID; + } + return res; +} + +_SOKOL_PRIVATE sg_pipeline _sg_alloc_pipeline(void) { + sg_pipeline res; + int slot_index = _sg_pool_alloc_index(&_sg.pools.pipeline_pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id =_sg_slot_alloc(&_sg.pools.pipeline_pool, &_sg.pools.pipelines[slot_index].slot, slot_index); + } + else { + /* pool is exhausted */ + res.id = SG_INVALID_ID; + } + return res; +} + +_SOKOL_PRIVATE sg_pass _sg_alloc_pass(void) { + sg_pass res; + int slot_index = _sg_pool_alloc_index(&_sg.pools.pass_pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id = _sg_slot_alloc(&_sg.pools.pass_pool, &_sg.pools.passes[slot_index].slot, slot_index); + } + else { + /* pool is exhausted */ + res.id = SG_INVALID_ID; + } + return res; +} + +_SOKOL_PRIVATE void _sg_dealloc_buffer(sg_buffer buf_id) { + SOKOL_ASSERT(buf_id.id != SG_INVALID_ID); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + SOKOL_ASSERT(buf && buf->slot.state == SG_RESOURCESTATE_ALLOC); + _sg_reset_slot(&buf->slot); + _sg_pool_free_index(&_sg.pools.buffer_pool, _sg_slot_index(buf_id.id)); +} + +_SOKOL_PRIVATE void _sg_dealloc_image(sg_image img_id) { + SOKOL_ASSERT(img_id.id != SG_INVALID_ID); + _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + SOKOL_ASSERT(img && img->slot.state == SG_RESOURCESTATE_ALLOC); + _sg_reset_slot(&img->slot); + _sg_pool_free_index(&_sg.pools.image_pool, _sg_slot_index(img_id.id)); +} + +_SOKOL_PRIVATE void _sg_dealloc_shader(sg_shader shd_id) { + SOKOL_ASSERT(shd_id.id != SG_INVALID_ID); + _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + SOKOL_ASSERT(shd && shd->slot.state == SG_RESOURCESTATE_ALLOC); + _sg_reset_slot(&shd->slot); + _sg_pool_free_index(&_sg.pools.shader_pool, _sg_slot_index(shd_id.id)); +} + +_SOKOL_PRIVATE void _sg_dealloc_pipeline(sg_pipeline pip_id) { + SOKOL_ASSERT(pip_id.id != SG_INVALID_ID); + _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + SOKOL_ASSERT(pip && pip->slot.state == SG_RESOURCESTATE_ALLOC); + _sg_reset_slot(&pip->slot); + _sg_pool_free_index(&_sg.pools.pipeline_pool, _sg_slot_index(pip_id.id)); +} + +_SOKOL_PRIVATE void _sg_dealloc_pass(sg_pass pass_id) { + SOKOL_ASSERT(pass_id.id != SG_INVALID_ID); + _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); + SOKOL_ASSERT(pass && pass->slot.state == SG_RESOURCESTATE_ALLOC); + _sg_reset_slot(&pass->slot); + _sg_pool_free_index(&_sg.pools.pass_pool, _sg_slot_index(pass_id.id)); +} + +_SOKOL_PRIVATE void _sg_init_buffer(sg_buffer buf_id, const sg_buffer_desc* desc) { + SOKOL_ASSERT(buf_id.id != SG_INVALID_ID && desc); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + SOKOL_ASSERT(buf && buf->slot.state == SG_RESOURCESTATE_ALLOC); + buf->slot.ctx_id = _sg.active_context.id; + if (_sg_validate_buffer_desc(desc)) { + buf->slot.state = _sg_create_buffer(buf, desc); + } + else { + buf->slot.state = SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT((buf->slot.state == SG_RESOURCESTATE_VALID)||(buf->slot.state == SG_RESOURCESTATE_FAILED)); +} + +_SOKOL_PRIVATE void _sg_init_image(sg_image img_id, const sg_image_desc* desc) { + SOKOL_ASSERT(img_id.id != SG_INVALID_ID && desc); + _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + SOKOL_ASSERT(img && img->slot.state == SG_RESOURCESTATE_ALLOC); + img->slot.ctx_id = _sg.active_context.id; + if (_sg_validate_image_desc(desc)) { + img->slot.state = _sg_create_image(img, desc); + } + else { + img->slot.state = SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT((img->slot.state == SG_RESOURCESTATE_VALID)||(img->slot.state == SG_RESOURCESTATE_FAILED)); +} + +_SOKOL_PRIVATE void _sg_init_shader(sg_shader shd_id, const sg_shader_desc* desc) { + SOKOL_ASSERT(shd_id.id != SG_INVALID_ID && desc); + _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + SOKOL_ASSERT(shd && shd->slot.state == SG_RESOURCESTATE_ALLOC); + shd->slot.ctx_id = _sg.active_context.id; + if (_sg_validate_shader_desc(desc)) { + shd->slot.state = _sg_create_shader(shd, desc); + } + else { + shd->slot.state = SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT((shd->slot.state == SG_RESOURCESTATE_VALID)||(shd->slot.state == SG_RESOURCESTATE_FAILED)); +} + +_SOKOL_PRIVATE void _sg_init_pipeline(sg_pipeline pip_id, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(pip_id.id != SG_INVALID_ID && desc); + _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + SOKOL_ASSERT(pip && pip->slot.state == SG_RESOURCESTATE_ALLOC); + pip->slot.ctx_id = _sg.active_context.id; + if (_sg_validate_pipeline_desc(desc)) { + _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, desc->shader.id); + if (shd && (shd->slot.state == SG_RESOURCESTATE_VALID)) { + pip->slot.state = _sg_create_pipeline(pip, shd, desc); + } + else { + pip->slot.state = SG_RESOURCESTATE_FAILED; + } + } + else { + pip->slot.state = SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT((pip->slot.state == SG_RESOURCESTATE_VALID)||(pip->slot.state == SG_RESOURCESTATE_FAILED)); +} + +_SOKOL_PRIVATE void _sg_init_pass(sg_pass pass_id, const sg_pass_desc* desc) { + SOKOL_ASSERT(pass_id.id != SG_INVALID_ID && desc); + _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); + SOKOL_ASSERT(pass && pass->slot.state == SG_RESOURCESTATE_ALLOC); + pass->slot.ctx_id = _sg.active_context.id; + if (_sg_validate_pass_desc(desc)) { + /* lookup pass attachment image pointers */ + _sg_image_t* att_imgs[SG_MAX_COLOR_ATTACHMENTS + 1]; + for (int i = 0; i < SG_MAX_COLOR_ATTACHMENTS; i++) { + if (desc->color_attachments[i].image.id) { + att_imgs[i] = _sg_lookup_image(&_sg.pools, desc->color_attachments[i].image.id); + /* FIXME: this shouldn't be an assertion, but result in a SG_RESOURCESTATE_FAILED pass */ + SOKOL_ASSERT(att_imgs[i] && att_imgs[i]->slot.state == SG_RESOURCESTATE_VALID); + } + else { + att_imgs[i] = 0; + } + } + const int ds_att_index = SG_MAX_COLOR_ATTACHMENTS; + if (desc->depth_stencil_attachment.image.id) { + att_imgs[ds_att_index] = _sg_lookup_image(&_sg.pools, desc->depth_stencil_attachment.image.id); + /* FIXME: this shouldn't be an assertion, but result in a SG_RESOURCESTATE_FAILED pass */ + SOKOL_ASSERT(att_imgs[ds_att_index] && att_imgs[ds_att_index]->slot.state == SG_RESOURCESTATE_VALID); + } + else { + att_imgs[ds_att_index] = 0; + } + pass->slot.state = _sg_create_pass(pass, att_imgs, desc); + } + else { + pass->slot.state = SG_RESOURCESTATE_FAILED; + } + SOKOL_ASSERT((pass->slot.state == SG_RESOURCESTATE_VALID)||(pass->slot.state == SG_RESOURCESTATE_FAILED)); +} + +_SOKOL_PRIVATE bool _sg_uninit_buffer(sg_buffer buf_id) { + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if (buf) { + if (buf->slot.ctx_id == _sg.active_context.id) { + _sg_destroy_buffer(buf); + _sg_reset_buffer(buf); + return true; + } + else { + SOKOL_LOG("_sg_uninit_buffer: active context mismatch (must be same as for creation)"); + _SG_TRACE_NOARGS(err_context_mismatch); + } + } + return false; +} + +_SOKOL_PRIVATE bool _sg_uninit_image(sg_image img_id) { + _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img) { + if (img->slot.ctx_id == _sg.active_context.id) { + _sg_destroy_image(img); + _sg_reset_image(img); + return true; + } + else { + SOKOL_LOG("_sg_uninit_image: active context mismatch (must be same as for creation)"); + _SG_TRACE_NOARGS(err_context_mismatch); + } + } + return false; +} + +_SOKOL_PRIVATE bool _sg_uninit_shader(sg_shader shd_id) { + _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + if (shd->slot.ctx_id == _sg.active_context.id) { + _sg_destroy_shader(shd); + _sg_reset_shader(shd); + return true; + } + else { + SOKOL_LOG("_sg_uninit_shader: active context mismatch (must be same as for creation)"); + _SG_TRACE_NOARGS(err_context_mismatch); + } + } + return false; +} + +_SOKOL_PRIVATE bool _sg_uninit_pipeline(sg_pipeline pip_id) { + _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + if (pip) { + if (pip->slot.ctx_id == _sg.active_context.id) { + _sg_destroy_pipeline(pip); + _sg_reset_pipeline(pip); + return true; + } + else { + SOKOL_LOG("_sg_uninit_pipeline: active context mismatch (must be same as for creation)"); + _SG_TRACE_NOARGS(err_context_mismatch); + } + } + return false; +} + +_SOKOL_PRIVATE bool _sg_uninit_pass(sg_pass pass_id) { + _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); + if (pass) { + if (pass->slot.ctx_id == _sg.active_context.id) { + _sg_destroy_pass(pass); + _sg_reset_pass(pass); + return true; + } + else { + SOKOL_LOG("_sg_uninit_pass: active context mismatch (must be same as for creation)"); + _SG_TRACE_NOARGS(err_context_mismatch); + } + } + return false; +} + +/*== PUBLIC API FUNCTIONS ====================================================*/ + +#if defined(SOKOL_METAL) + // this is ARC compatible + #if defined(__cplusplus) + #define _SG_CLEAR(type, item) { item = (type) { }; } + #else + #define _SG_CLEAR(type, item) { item = (type) { 0 }; } + #endif +#else + #define _SG_CLEAR(type, item) { memset(&item, 0, sizeof(item)); } +#endif + +SOKOL_API_IMPL void sg_setup(const sg_desc* desc) { + SOKOL_ASSERT(desc); + SOKOL_ASSERT((desc->_start_canary == 0) && (desc->_end_canary == 0)); + _SG_CLEAR(_sg_state_t, _sg); + _sg.desc = *desc; + + /* replace zero-init items with their default values + NOTE: on WebGPU, the default color pixel format MUST be provided, + cannot be a default compile-time constant. + */ + #if defined(SOKOL_WGPU) + SOKOL_ASSERT(SG_PIXELFORMAT_NONE != _sg.desc.context.color_format); + #elif defined(SOKOL_METAL) || defined(SOKOL_D3D11) + _sg.desc.context.color_format = _sg_def(_sg.desc.context.color_format, SG_PIXELFORMAT_BGRA8); + #else + _sg.desc.context.color_format = _sg_def(_sg.desc.context.color_format, SG_PIXELFORMAT_RGBA8); + #endif + _sg.desc.context.depth_format = _sg_def(_sg.desc.context.depth_format, SG_PIXELFORMAT_DEPTH_STENCIL); + _sg.desc.context.sample_count = _sg_def(_sg.desc.context.sample_count, 1); + _sg.desc.buffer_pool_size = _sg_def(_sg.desc.buffer_pool_size, _SG_DEFAULT_BUFFER_POOL_SIZE); + _sg.desc.image_pool_size = _sg_def(_sg.desc.image_pool_size, _SG_DEFAULT_IMAGE_POOL_SIZE); + _sg.desc.shader_pool_size = _sg_def(_sg.desc.shader_pool_size, _SG_DEFAULT_SHADER_POOL_SIZE); + _sg.desc.pipeline_pool_size = _sg_def(_sg.desc.pipeline_pool_size, _SG_DEFAULT_PIPELINE_POOL_SIZE); + _sg.desc.pass_pool_size = _sg_def(_sg.desc.pass_pool_size, _SG_DEFAULT_PASS_POOL_SIZE); + _sg.desc.context_pool_size = _sg_def(_sg.desc.context_pool_size, _SG_DEFAULT_CONTEXT_POOL_SIZE); + _sg.desc.uniform_buffer_size = _sg_def(_sg.desc.uniform_buffer_size, _SG_DEFAULT_UB_SIZE); + _sg.desc.staging_buffer_size = _sg_def(_sg.desc.staging_buffer_size, _SG_DEFAULT_STAGING_SIZE); + _sg.desc.sampler_cache_size = _sg_def(_sg.desc.sampler_cache_size, _SG_DEFAULT_SAMPLER_CACHE_CAPACITY); + + _sg_setup_pools(&_sg.pools, &_sg.desc); + _sg.frame_index = 1; + _sg_setup_backend(&_sg.desc); + _sg.valid = true; + sg_setup_context(); +} + +SOKOL_API_IMPL void sg_shutdown(void) { + /* can only delete resources for the currently set context here, if multiple + contexts are used, the app code must take care of properly releasing them + (since only the app code can switch between 3D-API contexts) + */ + if (_sg.active_context.id != SG_INVALID_ID) { + _sg_context_t* ctx = _sg_lookup_context(&_sg.pools, _sg.active_context.id); + if (ctx) { + _sg_destroy_all_resources(&_sg.pools, _sg.active_context.id); + _sg_destroy_context(ctx); + } + } + _sg_discard_backend(); + _sg_discard_pools(&_sg.pools); + _sg.valid = false; +} + +SOKOL_API_IMPL bool sg_isvalid(void) { + return _sg.valid; +} + +SOKOL_API_IMPL sg_desc sg_query_desc(void) { + SOKOL_ASSERT(_sg.valid); + return _sg.desc; +} + +SOKOL_API_IMPL sg_backend sg_query_backend(void) { + SOKOL_ASSERT(_sg.valid); + return _sg.backend; +} + +SOKOL_API_IMPL sg_features sg_query_features(void) { + SOKOL_ASSERT(_sg.valid); + return _sg.features; +} + +SOKOL_API_IMPL sg_limits sg_query_limits(void) { + SOKOL_ASSERT(_sg.valid); + return _sg.limits; +} + +SOKOL_API_IMPL sg_pixelformat_info sg_query_pixelformat(sg_pixel_format fmt) { + SOKOL_ASSERT(_sg.valid); + int fmt_index = (int) fmt; + SOKOL_ASSERT((fmt_index > SG_PIXELFORMAT_NONE) && (fmt_index < _SG_PIXELFORMAT_NUM)); + return _sg.formats[fmt_index]; +} + +SOKOL_API_IMPL sg_context sg_setup_context(void) { + SOKOL_ASSERT(_sg.valid); + sg_context res; + int slot_index = _sg_pool_alloc_index(&_sg.pools.context_pool); + if (_SG_INVALID_SLOT_INDEX != slot_index) { + res.id = _sg_slot_alloc(&_sg.pools.context_pool, &_sg.pools.contexts[slot_index].slot, slot_index); + _sg_context_t* ctx = _sg_context_at(&_sg.pools, res.id); + ctx->slot.state = _sg_create_context(ctx); + SOKOL_ASSERT(ctx->slot.state == SG_RESOURCESTATE_VALID); + _sg_activate_context(ctx); + } + else { + /* pool is exhausted */ + res.id = SG_INVALID_ID; + } + _sg.active_context = res; + return res; +} + +SOKOL_API_IMPL void sg_discard_context(sg_context ctx_id) { + SOKOL_ASSERT(_sg.valid); + _sg_destroy_all_resources(&_sg.pools, ctx_id.id); + _sg_context_t* ctx = _sg_lookup_context(&_sg.pools, ctx_id.id); + if (ctx) { + _sg_destroy_context(ctx); + _sg_reset_context(ctx); + _sg_reset_slot(&ctx->slot); + _sg_pool_free_index(&_sg.pools.context_pool, _sg_slot_index(ctx_id.id)); + } + _sg.active_context.id = SG_INVALID_ID; + _sg_activate_context(0); +} + +SOKOL_API_IMPL void sg_activate_context(sg_context ctx_id) { + SOKOL_ASSERT(_sg.valid); + _sg.active_context = ctx_id; + _sg_context_t* ctx = _sg_lookup_context(&_sg.pools, ctx_id.id); + /* NOTE: ctx can be 0 here if the context is no longer valid */ + _sg_activate_context(ctx); +} + +SOKOL_API_IMPL sg_trace_hooks sg_install_trace_hooks(const sg_trace_hooks* trace_hooks) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(trace_hooks); + _SOKOL_UNUSED(trace_hooks); + #if defined(SOKOL_TRACE_HOOKS) + sg_trace_hooks old_hooks = _sg.hooks; + _sg.hooks = *trace_hooks; + #else + static sg_trace_hooks old_hooks; + SOKOL_LOG("sg_install_trace_hooks() called, but SG_TRACE_HOOKS is not defined!"); + #endif + return old_hooks; +} + +SOKOL_API_IMPL sg_buffer sg_alloc_buffer(void) { + SOKOL_ASSERT(_sg.valid); + sg_buffer res = _sg_alloc_buffer(); + _SG_TRACE_ARGS(alloc_buffer, res); + return res; +} + +SOKOL_API_IMPL sg_image sg_alloc_image(void) { + SOKOL_ASSERT(_sg.valid); + sg_image res = _sg_alloc_image(); + _SG_TRACE_ARGS(alloc_image, res); + return res; +} + +SOKOL_API_IMPL sg_shader sg_alloc_shader(void) { + SOKOL_ASSERT(_sg.valid); + sg_shader res = _sg_alloc_shader(); + _SG_TRACE_ARGS(alloc_shader, res); + return res; +} + +SOKOL_API_IMPL sg_pipeline sg_alloc_pipeline(void) { + SOKOL_ASSERT(_sg.valid); + sg_pipeline res = _sg_alloc_pipeline(); + _SG_TRACE_ARGS(alloc_pipeline, res); + return res; +} + +SOKOL_API_IMPL sg_pass sg_alloc_pass(void) { + SOKOL_ASSERT(_sg.valid); + sg_pass res = _sg_alloc_pass(); + _SG_TRACE_ARGS(alloc_pass, res); + return res; +} + +SOKOL_API_IMPL void sg_dealloc_buffer(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + _sg_dealloc_buffer(buf_id); + _SG_TRACE_ARGS(dealloc_buffer, buf_id); +} + +SOKOL_API_IMPL void sg_dealloc_image(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + _sg_dealloc_image(img_id); + _SG_TRACE_ARGS(dealloc_image, img_id); +} + +SOKOL_API_IMPL void sg_dealloc_shader(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + _sg_dealloc_shader(shd_id); + _SG_TRACE_ARGS(dealloc_shader, shd_id); +} + +SOKOL_API_IMPL void sg_dealloc_pipeline(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + _sg_dealloc_pipeline(pip_id); + _SG_TRACE_ARGS(dealloc_pipeline, pip_id); +} + +SOKOL_API_IMPL void sg_dealloc_pass(sg_pass pass_id) { + SOKOL_ASSERT(_sg.valid); + _sg_dealloc_pass(pass_id); + _SG_TRACE_ARGS(dealloc_pass, pass_id); +} + +SOKOL_API_IMPL void sg_init_buffer(sg_buffer buf_id, const sg_buffer_desc* desc) { + SOKOL_ASSERT(_sg.valid); + sg_buffer_desc desc_def = _sg_buffer_desc_defaults(desc); + _sg_init_buffer(buf_id, &desc_def); + _SG_TRACE_ARGS(init_buffer, buf_id, &desc_def); +} + +SOKOL_API_IMPL void sg_init_image(sg_image img_id, const sg_image_desc* desc) { + SOKOL_ASSERT(_sg.valid); + sg_image_desc desc_def = _sg_image_desc_defaults(desc); + _sg_init_image(img_id, &desc_def); + _SG_TRACE_ARGS(init_image, img_id, &desc_def); +} + +SOKOL_API_IMPL void sg_init_shader(sg_shader shd_id, const sg_shader_desc* desc) { + SOKOL_ASSERT(_sg.valid); + sg_shader_desc desc_def = _sg_shader_desc_defaults(desc); + _sg_init_shader(shd_id, &desc_def); + _SG_TRACE_ARGS(init_shader, shd_id, &desc_def); +} + +SOKOL_API_IMPL void sg_init_pipeline(sg_pipeline pip_id, const sg_pipeline_desc* desc) { + SOKOL_ASSERT(_sg.valid); + sg_pipeline_desc desc_def = _sg_pipeline_desc_defaults(desc); + _sg_init_pipeline(pip_id, &desc_def); + _SG_TRACE_ARGS(init_pipeline, pip_id, &desc_def); +} + +SOKOL_API_IMPL void sg_init_pass(sg_pass pass_id, const sg_pass_desc* desc) { + SOKOL_ASSERT(_sg.valid); + sg_pass_desc desc_def = _sg_pass_desc_defaults(desc); + _sg_init_pass(pass_id, &desc_def); + _SG_TRACE_ARGS(init_pass, pass_id, &desc_def); +} + +SOKOL_API_IMPL bool sg_uninit_buffer(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + bool res = _sg_uninit_buffer(buf_id); + _SG_TRACE_ARGS(uninit_buffer, buf_id); + return res; +} + +SOKOL_API_IMPL bool sg_uninit_image(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + bool res = _sg_uninit_image(img_id); + _SG_TRACE_ARGS(uninit_image, img_id); + return res; +} + +SOKOL_API_IMPL bool sg_uninit_shader(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + bool res = _sg_uninit_shader(shd_id); + _SG_TRACE_ARGS(uninit_shader, shd_id); + return res; +} + +SOKOL_API_IMPL bool sg_uninit_pipeline(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + bool res = _sg_uninit_pipeline(pip_id); + _SG_TRACE_ARGS(uninit_pipeline, pip_id); + return res; +} + +SOKOL_API_IMPL bool sg_uninit_pass(sg_pass pass_id) { + SOKOL_ASSERT(_sg.valid); + bool res = _sg_uninit_pass(pass_id); + _SG_TRACE_ARGS(uninit_pass, pass_id); + return res; +} + +/*-- set allocated resource to failed state ----------------------------------*/ +SOKOL_API_IMPL void sg_fail_buffer(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(buf_id.id != SG_INVALID_ID); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + SOKOL_ASSERT(buf && buf->slot.state == SG_RESOURCESTATE_ALLOC); + buf->slot.ctx_id = _sg.active_context.id; + buf->slot.state = SG_RESOURCESTATE_FAILED; + _SG_TRACE_ARGS(fail_buffer, buf_id); +} + +SOKOL_API_IMPL void sg_fail_image(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(img_id.id != SG_INVALID_ID); + _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + SOKOL_ASSERT(img && img->slot.state == SG_RESOURCESTATE_ALLOC); + img->slot.ctx_id = _sg.active_context.id; + img->slot.state = SG_RESOURCESTATE_FAILED; + _SG_TRACE_ARGS(fail_image, img_id); +} + +SOKOL_API_IMPL void sg_fail_shader(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(shd_id.id != SG_INVALID_ID); + _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + SOKOL_ASSERT(shd && shd->slot.state == SG_RESOURCESTATE_ALLOC); + shd->slot.ctx_id = _sg.active_context.id; + shd->slot.state = SG_RESOURCESTATE_FAILED; + _SG_TRACE_ARGS(fail_shader, shd_id); +} + +SOKOL_API_IMPL void sg_fail_pipeline(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(pip_id.id != SG_INVALID_ID); + _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + SOKOL_ASSERT(pip && pip->slot.state == SG_RESOURCESTATE_ALLOC); + pip->slot.ctx_id = _sg.active_context.id; + pip->slot.state = SG_RESOURCESTATE_FAILED; + _SG_TRACE_ARGS(fail_pipeline, pip_id); +} + +SOKOL_API_IMPL void sg_fail_pass(sg_pass pass_id) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(pass_id.id != SG_INVALID_ID); + _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); + SOKOL_ASSERT(pass && pass->slot.state == SG_RESOURCESTATE_ALLOC); + pass->slot.ctx_id = _sg.active_context.id; + pass->slot.state = SG_RESOURCESTATE_FAILED; + _SG_TRACE_ARGS(fail_pass, pass_id); +} + +/*-- get resource state */ +SOKOL_API_IMPL sg_resource_state sg_query_buffer_state(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + sg_resource_state res = buf ? buf->slot.state : SG_RESOURCESTATE_INVALID; + return res; +} + +SOKOL_API_IMPL sg_resource_state sg_query_image_state(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + sg_resource_state res = img ? img->slot.state : SG_RESOURCESTATE_INVALID; + return res; +} + +SOKOL_API_IMPL sg_resource_state sg_query_shader_state(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + sg_resource_state res = shd ? shd->slot.state : SG_RESOURCESTATE_INVALID; + return res; +} + +SOKOL_API_IMPL sg_resource_state sg_query_pipeline_state(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + sg_resource_state res = pip ? pip->slot.state : SG_RESOURCESTATE_INVALID; + return res; +} + +SOKOL_API_IMPL sg_resource_state sg_query_pass_state(sg_pass pass_id) { + SOKOL_ASSERT(_sg.valid); + _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); + sg_resource_state res = pass ? pass->slot.state : SG_RESOURCESTATE_INVALID; + return res; +} + +/*-- allocate and initialize resource ----------------------------------------*/ +SOKOL_API_IMPL sg_buffer sg_make_buffer(const sg_buffer_desc* desc) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(desc); + sg_buffer_desc desc_def = _sg_buffer_desc_defaults(desc); + sg_buffer buf_id = _sg_alloc_buffer(); + if (buf_id.id != SG_INVALID_ID) { + _sg_init_buffer(buf_id, &desc_def); + } + else { + SOKOL_LOG("buffer pool exhausted!"); + _SG_TRACE_NOARGS(err_buffer_pool_exhausted); + } + _SG_TRACE_ARGS(make_buffer, &desc_def, buf_id); + return buf_id; +} + +SOKOL_API_IMPL sg_image sg_make_image(const sg_image_desc* desc) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(desc); + sg_image_desc desc_def = _sg_image_desc_defaults(desc); + sg_image img_id = _sg_alloc_image(); + if (img_id.id != SG_INVALID_ID) { + _sg_init_image(img_id, &desc_def); + } + else { + SOKOL_LOG("image pool exhausted!"); + _SG_TRACE_NOARGS(err_image_pool_exhausted); + } + _SG_TRACE_ARGS(make_image, &desc_def, img_id); + return img_id; +} + +SOKOL_API_IMPL sg_shader sg_make_shader(const sg_shader_desc* desc) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(desc); + sg_shader_desc desc_def = _sg_shader_desc_defaults(desc); + sg_shader shd_id = _sg_alloc_shader(); + if (shd_id.id != SG_INVALID_ID) { + _sg_init_shader(shd_id, &desc_def); + } + else { + SOKOL_LOG("shader pool exhausted!"); + _SG_TRACE_NOARGS(err_shader_pool_exhausted); + } + _SG_TRACE_ARGS(make_shader, &desc_def, shd_id); + return shd_id; +} + +SOKOL_API_IMPL sg_pipeline sg_make_pipeline(const sg_pipeline_desc* desc) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(desc); + sg_pipeline_desc desc_def = _sg_pipeline_desc_defaults(desc); + sg_pipeline pip_id = _sg_alloc_pipeline(); + if (pip_id.id != SG_INVALID_ID) { + _sg_init_pipeline(pip_id, &desc_def); + } + else { + SOKOL_LOG("pipeline pool exhausted!"); + _SG_TRACE_NOARGS(err_pipeline_pool_exhausted); + } + _SG_TRACE_ARGS(make_pipeline, &desc_def, pip_id); + return pip_id; +} + +SOKOL_API_IMPL sg_pass sg_make_pass(const sg_pass_desc* desc) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(desc); + sg_pass_desc desc_def = _sg_pass_desc_defaults(desc); + sg_pass pass_id = _sg_alloc_pass(); + if (pass_id.id != SG_INVALID_ID) { + _sg_init_pass(pass_id, &desc_def); + } + else { + SOKOL_LOG("pass pool exhausted!"); + _SG_TRACE_NOARGS(err_pass_pool_exhausted); + } + _SG_TRACE_ARGS(make_pass, &desc_def, pass_id); + return pass_id; +} + +/*-- destroy resource --------------------------------------------------------*/ +SOKOL_API_IMPL void sg_destroy_buffer(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + _SG_TRACE_ARGS(destroy_buffer, buf_id); + if (_sg_uninit_buffer(buf_id)) { + _sg_dealloc_buffer(buf_id); + } +} + +SOKOL_API_IMPL void sg_destroy_image(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + _SG_TRACE_ARGS(destroy_image, img_id); + if (_sg_uninit_image(img_id)) { + _sg_dealloc_image(img_id); + } +} + +SOKOL_API_IMPL void sg_destroy_shader(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + _SG_TRACE_ARGS(destroy_shader, shd_id); + if (_sg_uninit_shader(shd_id)) { + _sg_dealloc_shader(shd_id); + } +} + +SOKOL_API_IMPL void sg_destroy_pipeline(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + _SG_TRACE_ARGS(destroy_pipeline, pip_id); + if (_sg_uninit_pipeline(pip_id)) { + _sg_dealloc_pipeline(pip_id); + } +} + +SOKOL_API_IMPL void sg_destroy_pass(sg_pass pass_id) { + SOKOL_ASSERT(_sg.valid); + _SG_TRACE_ARGS(destroy_pass, pass_id); + if (_sg_uninit_pass(pass_id)) { + _sg_dealloc_pass(pass_id); + } +} + +SOKOL_API_IMPL void sg_begin_default_pass(const sg_pass_action* pass_action, int width, int height) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(pass_action); + SOKOL_ASSERT((pass_action->_start_canary == 0) && (pass_action->_end_canary == 0)); + sg_pass_action pa; + _sg_resolve_default_pass_action(pass_action, &pa); + _sg.cur_pass.id = SG_INVALID_ID; + _sg.pass_valid = true; + _sg_begin_pass(0, &pa, width, height); + _SG_TRACE_ARGS(begin_default_pass, pass_action, width, height); +} + +SOKOL_API_IMPL void sg_begin_default_passf(const sg_pass_action* pass_action, float width, float height) { + sg_begin_default_pass(pass_action, (int)width, (int)height); +} + +SOKOL_API_IMPL void sg_begin_pass(sg_pass pass_id, const sg_pass_action* pass_action) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(pass_action); + SOKOL_ASSERT((pass_action->_start_canary == 0) && (pass_action->_end_canary == 0)); + _sg.cur_pass = pass_id; + _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); + if (pass && _sg_validate_begin_pass(pass)) { + _sg.pass_valid = true; + sg_pass_action pa; + _sg_resolve_default_pass_action(pass_action, &pa); + const _sg_image_t* img = _sg_pass_color_image(pass, 0); + SOKOL_ASSERT(img); + const int w = img->cmn.width; + const int h = img->cmn.height; + _sg_begin_pass(pass, &pa, w, h); + _SG_TRACE_ARGS(begin_pass, pass_id, pass_action); + } + else { + _sg.pass_valid = false; + _SG_TRACE_NOARGS(err_pass_invalid); + } +} + +SOKOL_API_IMPL void sg_apply_viewport(int x, int y, int width, int height, bool origin_top_left) { + SOKOL_ASSERT(_sg.valid); + if (!_sg.pass_valid) { + _SG_TRACE_NOARGS(err_pass_invalid); + return; + } + _sg_apply_viewport(x, y, width, height, origin_top_left); + _SG_TRACE_ARGS(apply_viewport, x, y, width, height, origin_top_left); +} + +SOKOL_API_IMPL void sg_apply_viewportf(float x, float y, float width, float height, bool origin_top_left) { + sg_apply_viewport((int)x, (int)y, (int)width, (int)height, origin_top_left); +} + +SOKOL_API_IMPL void sg_apply_scissor_rect(int x, int y, int width, int height, bool origin_top_left) { + SOKOL_ASSERT(_sg.valid); + if (!_sg.pass_valid) { + _SG_TRACE_NOARGS(err_pass_invalid); + return; + } + _sg_apply_scissor_rect(x, y, width, height, origin_top_left); + _SG_TRACE_ARGS(apply_scissor_rect, x, y, width, height, origin_top_left); +} + +SOKOL_API_IMPL void sg_apply_scissor_rectf(float x, float y, float width, float height, bool origin_top_left) { + sg_apply_scissor_rect((int)x, (int)y, (int)width, (int)height, origin_top_left); +} + +SOKOL_API_IMPL void sg_apply_pipeline(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + _sg.bindings_valid = false; + if (!_sg_validate_apply_pipeline(pip_id)) { + _sg.next_draw_valid = false; + _SG_TRACE_NOARGS(err_draw_invalid); + return; + } + if (!_sg.pass_valid) { + _SG_TRACE_NOARGS(err_pass_invalid); + return; + } + _sg.cur_pipeline = pip_id; + _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + SOKOL_ASSERT(pip); + _sg.next_draw_valid = (SG_RESOURCESTATE_VALID == pip->slot.state); + SOKOL_ASSERT(pip->shader && (pip->shader->slot.id == pip->cmn.shader_id.id)); + _sg_apply_pipeline(pip); + _SG_TRACE_ARGS(apply_pipeline, pip_id); +} + +SOKOL_API_IMPL void sg_apply_bindings(const sg_bindings* bindings) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(bindings); + SOKOL_ASSERT((bindings->_start_canary == 0) && (bindings->_end_canary==0)); + if (!_sg_validate_apply_bindings(bindings)) { + _sg.next_draw_valid = false; + _SG_TRACE_NOARGS(err_draw_invalid); + return; + } + _sg.bindings_valid = true; + + _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, _sg.cur_pipeline.id); + SOKOL_ASSERT(pip); + + _sg_buffer_t* vbs[SG_MAX_SHADERSTAGE_BUFFERS] = { 0 }; + int num_vbs = 0; + for (int i = 0; i < SG_MAX_SHADERSTAGE_BUFFERS; i++, num_vbs++) { + if (bindings->vertex_buffers[i].id) { + vbs[i] = _sg_lookup_buffer(&_sg.pools, bindings->vertex_buffers[i].id); + SOKOL_ASSERT(vbs[i]); + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == vbs[i]->slot.state); + _sg.next_draw_valid &= !vbs[i]->cmn.append_overflow; + } + else { + break; + } + } + + _sg_buffer_t* ib = 0; + if (bindings->index_buffer.id) { + ib = _sg_lookup_buffer(&_sg.pools, bindings->index_buffer.id); + SOKOL_ASSERT(ib); + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == ib->slot.state); + _sg.next_draw_valid &= !ib->cmn.append_overflow; + } + + _sg_image_t* vs_imgs[SG_MAX_SHADERSTAGE_IMAGES] = { 0 }; + int num_vs_imgs = 0; + for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++, num_vs_imgs++) { + if (bindings->vs_images[i].id) { + vs_imgs[i] = _sg_lookup_image(&_sg.pools, bindings->vs_images[i].id); + SOKOL_ASSERT(vs_imgs[i]); + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == vs_imgs[i]->slot.state); + } + else { + break; + } + } + + _sg_image_t* fs_imgs[SG_MAX_SHADERSTAGE_IMAGES] = { 0 }; + int num_fs_imgs = 0; + for (int i = 0; i < SG_MAX_SHADERSTAGE_IMAGES; i++, num_fs_imgs++) { + if (bindings->fs_images[i].id) { + fs_imgs[i] = _sg_lookup_image(&_sg.pools, bindings->fs_images[i].id); + SOKOL_ASSERT(fs_imgs[i]); + _sg.next_draw_valid &= (SG_RESOURCESTATE_VALID == fs_imgs[i]->slot.state); + } + else { + break; + } + } + if (_sg.next_draw_valid) { + const int* vb_offsets = bindings->vertex_buffer_offsets; + int ib_offset = bindings->index_buffer_offset; + _sg_apply_bindings(pip, vbs, vb_offsets, num_vbs, ib, ib_offset, vs_imgs, num_vs_imgs, fs_imgs, num_fs_imgs); + _SG_TRACE_ARGS(apply_bindings, bindings); + } + else { + _SG_TRACE_NOARGS(err_draw_invalid); + } +} + +SOKOL_API_IMPL void sg_apply_uniforms(sg_shader_stage stage, int ub_index, const sg_range* data) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT((stage == SG_SHADERSTAGE_VS) || (stage == SG_SHADERSTAGE_FS)); + SOKOL_ASSERT((ub_index >= 0) && (ub_index < SG_MAX_SHADERSTAGE_UBS)); + SOKOL_ASSERT(data && data->ptr && (data->size > 0)); + if (!_sg_validate_apply_uniforms(stage, ub_index, data)) { + _sg.next_draw_valid = false; + _SG_TRACE_NOARGS(err_draw_invalid); + return; + } + if (!_sg.pass_valid) { + _SG_TRACE_NOARGS(err_pass_invalid); + return; + } + if (!_sg.next_draw_valid) { + _SG_TRACE_NOARGS(err_draw_invalid); + } + _sg_apply_uniforms(stage, ub_index, data); + _SG_TRACE_ARGS(apply_uniforms, stage, ub_index, data); +} + +SOKOL_API_IMPL void sg_draw(int base_element, int num_elements, int num_instances) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(base_element >= 0); + SOKOL_ASSERT(num_elements >= 0); + SOKOL_ASSERT(num_instances >= 0); + #if defined(SOKOL_DEBUG) + if (!_sg.bindings_valid) { + SOKOL_LOG("attempting to draw without resource bindings"); + } + #endif + if (!_sg.pass_valid) { + _SG_TRACE_NOARGS(err_pass_invalid); + return; + } + if (!_sg.next_draw_valid) { + _SG_TRACE_NOARGS(err_draw_invalid); + return; + } + if (!_sg.bindings_valid) { + _SG_TRACE_NOARGS(err_bindings_invalid); + return; + } + /* attempting to draw with zero elements or instances is not technically an + error, but might be handled as an error in the backend API (e.g. on Metal) + */ + if ((0 == num_elements) || (0 == num_instances)) { + _SG_TRACE_NOARGS(err_draw_invalid); + return; + } + _sg_draw(base_element, num_elements, num_instances); + _SG_TRACE_ARGS(draw, base_element, num_elements, num_instances); +} + +SOKOL_API_IMPL void sg_end_pass(void) { + SOKOL_ASSERT(_sg.valid); + if (!_sg.pass_valid) { + _SG_TRACE_NOARGS(err_pass_invalid); + return; + } + _sg_end_pass(); + _sg.cur_pass.id = SG_INVALID_ID; + _sg.cur_pipeline.id = SG_INVALID_ID; + _sg.pass_valid = false; + _SG_TRACE_NOARGS(end_pass); +} + +SOKOL_API_IMPL void sg_commit(void) { + SOKOL_ASSERT(_sg.valid); + _sg_commit(); + _SG_TRACE_NOARGS(commit); + _sg.frame_index++; +} + +SOKOL_API_IMPL void sg_reset_state_cache(void) { + SOKOL_ASSERT(_sg.valid); + _sg_reset_state_cache(); + _SG_TRACE_NOARGS(reset_state_cache); +} + +SOKOL_API_IMPL void sg_update_buffer(sg_buffer buf_id, const sg_range* data) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(data && data->ptr && (data->size > 0)); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if ((data->size > 0) && buf && (buf->slot.state == SG_RESOURCESTATE_VALID)) { + if (_sg_validate_update_buffer(buf, data)) { + SOKOL_ASSERT(data->size <= (size_t)buf->cmn.size); + /* only one update allowed per buffer and frame */ + SOKOL_ASSERT(buf->cmn.update_frame_index != _sg.frame_index); + /* update and append on same buffer in same frame not allowed */ + SOKOL_ASSERT(buf->cmn.append_frame_index != _sg.frame_index); + _sg_update_buffer(buf, data); + buf->cmn.update_frame_index = _sg.frame_index; + } + } + _SG_TRACE_ARGS(update_buffer, buf_id, data); +} + +SOKOL_API_IMPL int sg_append_buffer(sg_buffer buf_id, const sg_range* data) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(data && data->ptr); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + int result; + if (buf) { + /* rewind append cursor in a new frame */ + if (buf->cmn.append_frame_index != _sg.frame_index) { + buf->cmn.append_pos = 0; + buf->cmn.append_overflow = false; + } + if ((buf->cmn.append_pos + _sg_roundup((int)data->size, 4)) > buf->cmn.size) { + buf->cmn.append_overflow = true; + } + const int start_pos = buf->cmn.append_pos; + if (buf->slot.state == SG_RESOURCESTATE_VALID) { + if (_sg_validate_append_buffer(buf, data)) { + if (!buf->cmn.append_overflow && (data->size > 0)) { + /* update and append on same buffer in same frame not allowed */ + SOKOL_ASSERT(buf->cmn.update_frame_index != _sg.frame_index); + int copied_num_bytes = _sg_append_buffer(buf, data, buf->cmn.append_frame_index != _sg.frame_index); + buf->cmn.append_pos += copied_num_bytes; + buf->cmn.append_frame_index = _sg.frame_index; + } + } + } + result = start_pos; + } + else { + /* FIXME: should we return -1 here? */ + result = 0; + } + _SG_TRACE_ARGS(append_buffer, buf_id, data, result); + return result; +} + +SOKOL_API_IMPL bool sg_query_buffer_overflow(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + bool result = buf ? buf->cmn.append_overflow : false; + return result; +} + +SOKOL_API_IMPL void sg_update_image(sg_image img_id, const sg_image_data* data) { + SOKOL_ASSERT(_sg.valid); + _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img && img->slot.state == SG_RESOURCESTATE_VALID) { + if (_sg_validate_update_image(img, data)) { + SOKOL_ASSERT(img->cmn.upd_frame_index != _sg.frame_index); + _sg_update_image(img, data); + img->cmn.upd_frame_index = _sg.frame_index; + } + } + _SG_TRACE_ARGS(update_image, img_id, data); +} + +SOKOL_API_IMPL void sg_push_debug_group(const char* name) { + SOKOL_ASSERT(_sg.valid); + SOKOL_ASSERT(name); + _SOKOL_UNUSED(name); + _SG_TRACE_ARGS(push_debug_group, name); +} + +SOKOL_API_IMPL void sg_pop_debug_group(void) { + SOKOL_ASSERT(_sg.valid); + _SG_TRACE_NOARGS(pop_debug_group); +} + +SOKOL_API_IMPL sg_buffer_info sg_query_buffer_info(sg_buffer buf_id) { + SOKOL_ASSERT(_sg.valid); + sg_buffer_info info; + memset(&info, 0, sizeof(info)); + const _sg_buffer_t* buf = _sg_lookup_buffer(&_sg.pools, buf_id.id); + if (buf) { + info.slot.state = buf->slot.state; + info.slot.res_id = buf->slot.id; + info.slot.ctx_id = buf->slot.ctx_id; + info.update_frame_index = buf->cmn.update_frame_index; + info.append_frame_index = buf->cmn.append_frame_index; + info.append_pos = buf->cmn.append_pos; + info.append_overflow = buf->cmn.append_overflow; + #if defined(SOKOL_D3D11) + info.num_slots = 1; + info.active_slot = 0; + #else + info.num_slots = buf->cmn.num_slots; + info.active_slot = buf->cmn.active_slot; + #endif + } + return info; +} + +SOKOL_API_IMPL sg_image_info sg_query_image_info(sg_image img_id) { + SOKOL_ASSERT(_sg.valid); + sg_image_info info; + memset(&info, 0, sizeof(info)); + const _sg_image_t* img = _sg_lookup_image(&_sg.pools, img_id.id); + if (img) { + info.slot.state = img->slot.state; + info.slot.res_id = img->slot.id; + info.slot.ctx_id = img->slot.ctx_id; + #if defined(SOKOL_D3D11) + info.num_slots = 1; + info.active_slot = 0; + #else + info.num_slots = img->cmn.num_slots; + info.active_slot = img->cmn.active_slot; + #endif + info.width = img->cmn.width; + info.height = img->cmn.height; + } + return info; +} + +SOKOL_API_IMPL sg_shader_info sg_query_shader_info(sg_shader shd_id) { + SOKOL_ASSERT(_sg.valid); + sg_shader_info info; + memset(&info, 0, sizeof(info)); + const _sg_shader_t* shd = _sg_lookup_shader(&_sg.pools, shd_id.id); + if (shd) { + info.slot.state = shd->slot.state; + info.slot.res_id = shd->slot.id; + info.slot.ctx_id = shd->slot.ctx_id; + } + return info; +} + +SOKOL_API_IMPL sg_pipeline_info sg_query_pipeline_info(sg_pipeline pip_id) { + SOKOL_ASSERT(_sg.valid); + sg_pipeline_info info; + memset(&info, 0, sizeof(info)); + const _sg_pipeline_t* pip = _sg_lookup_pipeline(&_sg.pools, pip_id.id); + if (pip) { + info.slot.state = pip->slot.state; + info.slot.res_id = pip->slot.id; + info.slot.ctx_id = pip->slot.ctx_id; + } + return info; +} + +SOKOL_API_IMPL sg_pass_info sg_query_pass_info(sg_pass pass_id) { + SOKOL_ASSERT(_sg.valid); + sg_pass_info info; + memset(&info, 0, sizeof(info)); + const _sg_pass_t* pass = _sg_lookup_pass(&_sg.pools, pass_id.id); + if (pass) { + info.slot.state = pass->slot.state; + info.slot.res_id = pass->slot.id; + info.slot.ctx_id = pass->slot.ctx_id; + } + return info; +} + +SOKOL_API_IMPL sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc* desc) { + SOKOL_ASSERT(_sg.valid && desc); + return _sg_buffer_desc_defaults(desc); +} + +SOKOL_API_IMPL sg_image_desc sg_query_image_defaults(const sg_image_desc* desc) { + SOKOL_ASSERT(_sg.valid && desc); + return _sg_image_desc_defaults(desc); +} + +SOKOL_API_IMPL sg_shader_desc sg_query_shader_defaults(const sg_shader_desc* desc) { + SOKOL_ASSERT(_sg.valid && desc); + return _sg_shader_desc_defaults(desc); +} + +SOKOL_API_IMPL sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_desc* desc) { + SOKOL_ASSERT(_sg.valid && desc); + return _sg_pipeline_desc_defaults(desc); +} + +SOKOL_API_IMPL sg_pass_desc sg_query_pass_defaults(const sg_pass_desc* desc) { + SOKOL_ASSERT(_sg.valid && desc); + return _sg_pass_desc_defaults(desc); +} + +SOKOL_API_IMPL const void* sg_d3d11_device(void) { +#if defined(SOKOL_D3D11) + return (const void*) _sg.d3d11.dev; +#else + return 0; +#endif +} + +SOKOL_API_IMPL const void* sg_mtl_device(void) { +#if defined(SOKOL_METAL) + if (nil != _sg.mtl.device) { + return (__bridge const void*) _sg.mtl.device; + } + else { + return 0; + } +#else + return 0; +#endif +} + +SOKOL_API_IMPL const void* sg_mtl_render_command_encoder(void) { + #if defined(SOKOL_METAL) + if (nil != _sg.mtl.cmd_encoder) { + return (__bridge const void*) _sg.mtl.cmd_encoder; + } + else { + return 0; + } + #else + return 0; + #endif +} + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#endif /* SOKOL_GFX_IMPL */ diff --git a/v_windows/v/old/thirdparty/sokol/sokol_v.h b/v_windows/v/old/thirdparty/sokol/sokol_v.h new file mode 100644 index 0000000..cc08678 --- /dev/null +++ b/v_windows/v/old/thirdparty/sokol/sokol_v.h @@ -0,0 +1,20 @@ +#if defined(__ANDROID__) + // Adapted from https://stackoverflow.com/a/196018/1904615 + #define V_ANDROID_LOG_STR_VALUE(arg) #arg + #define V_ANDROID_LOG_NAME(tag_name) V_ANDROID_LOG_STR_VALUE(tag_name) + + #ifndef V_ANDROID_LOG_TAG + #if defined(APPNAME) + #define V_ANDROID_LOG_TAG APPNAME + #else + #define V_ANDROID_LOG_TAG "V_ANDROID" + #endif + #endif + + #define V_ANDROID_LOG_TAG_NAME V_ANDROID_LOG_NAME(V_ANDROID_LOG_TAG) + + #include + #define printf(...) __android_log_print(ANDROID_LOG_INFO, V_ANDROID_LOG_TAG_NAME, __VA_ARGS__) + #define fprintf(a, ...) __android_log_print(ANDROID_LOG_ERROR, V_ANDROID_LOG_TAG_NAME, __VA_ARGS__) +#endif + diff --git a/v_windows/v/old/thirdparty/sokol/util/sokol_fontstash.h b/v_windows/v/old/thirdparty/sokol/util/sokol_fontstash.h new file mode 100644 index 0000000..9bb915f --- /dev/null +++ b/v_windows/v/old/thirdparty/sokol/util/sokol_fontstash.h @@ -0,0 +1,1785 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_FONTSTASH_IMPL) +#define SOKOL_FONTSTASH_IMPL +#endif +#ifndef SOKOL_FONTSTASH_INCLUDED +/* + sokol_fontstash.h -- renderer for https://github.com/memononen/fontstash + on top of sokol_gl.h + + Project URL: https://github.com/floooh/sokol + + Do this: + #define SOKOL_IMPL or + #define SOKOL_FONTSTASH_IMPL + + before you include this file in *one* C or C++ file to create the + implementation. + + The following defines are used by the implementation to select the + platform-specific embedded shader code (these are the same defines as + used by sokol_gfx.h and sokol_app.h): + + SOKOL_GLCORE33 + SOKOL_GLES2 + SOKOL_GLES3 + SOKOL_D3D11 + SOKOL_METAL + + ...optionally provide the following macros to override defaults: + + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + SOKOL_MALLOC(s) - your own malloc function (default: malloc(s)) + SOKOL_FREE(p) - your own free function (default: free(p)) + SOKOL_FONTSTASH_API_DECL - public function declaration prefix (default: extern) + SOKOL_API_DECL - same as SOKOL_FONTSTASH_API_DECL + SOKOL_API_IMPL - public function implementation prefix (default: -) + SOKOL_LOG(msg) - your own logging function (default: puts(msg)) + SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) + + Include the following headers before including sokol_fontstash.h: + + sokol_gfx.h + + Additionally include the following headers for including the sokol_fontstash.h + implementation: + + sokol_gl.h + + HOW TO + ====== + --- First initialize sokol-gfx and sokol-gl as usual: + + sg_setup(&(sg_desc){...}); + sgl_setup(&(sgl_desc){...}); + + --- Create at least one fontstash context with sfons_create() (this replaces + glfonsCreate() from fontstash.h's example GL renderer: + + FONScontext* ctx = sfons_create(atlas_width, atlas_height, FONS_ZERO_TOPLEFT); + + Each FONScontext manages one font atlas texture which can hold rasterized + glyphs for multiple fonts. + + --- From here on, use fontstash.h's functions "as usual" to add TTF + font data and draw text. Note that (just like with sokol-gl), text + rendering can happen anywhere in the frame, not only inside + a sokol-gfx rendering pass. + + --- You can use the helper function + + uint32_t sfons_rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a) + + To convert a 0..255 RGBA color into a packed uint32_t color value + expected by fontstash.h. + + --- Once per frame before calling sgl_draw(), call: + + sfons_flush(FONScontext* ctx) + + ...this will update the dynamic sokol-gfx texture with the latest font + atlas content. + + --- To actually render the text (and any other sokol-gl draw commands), + call sgl_draw() inside a sokol-gfx frame. + + --- NOTE that you can mix fontstash.h calls with sokol-gl calls to mix + text rendering with sokol-gl rendering. You can also use + sokol-gl's matrix stack to position fontstash.h text in 3D. + + --- finally on application shutdown, call: + + sfons_shutdown() + + before sgl_shutdown() and sg_shutdown() + + + WHAT HAPPENS UNDER THE HOOD: + ============================ + + sfons_create(): + - creates a sokol-gfx shader compatible with sokol-gl + - creates an sgl_pipeline object with alpha-blending using + this shader + - creates a 1-byte-per-pixel font atlas texture via sokol-gfx + (pixel format SG_PIXELFORMAT_R8) + + fonsDrawText(): + - this will call the following sequence of sokol-gl functions: + + sgl_enable_texture(); + sgl_texture(...); + sgl_push_pipeline(); + sgl_load_pipeline(...); + sgl_begin_triangles(); + for each vertex: + sg_v2f_t2f_c1i(...); + sgl_end(); + sgl_pop_pipeline(); + sgl_disable_texture(); + + - note that sokol-gl will merge several sgl_*_begin/sgl_end pairs + into a single draw call if no relevant state has changed, typically + all calls to fonsDrawText() will be merged into a single draw call + as long as all calls use the same FONScontext + + sfons_flush(): + - this will call sg_update_image() on the font atlas texture + if fontstash.h has added any rasterized glyphs since the last + frame + + sfons_shutdown(): + - destroy the font atlas texture, sgl_pipeline and sg_shader objects + + LICENSE + ======= + zlib/libpng license + + Copyright (c) 2018 Andre Weissflog + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software in a + product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ +#define SOKOL_FONTSTASH_INCLUDED (1) +#include +#include + +#if !defined(SOKOL_GFX_INCLUDED) +#error "Please include sokol_gfx.h before sokol_fontstash.h" +#endif + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_FONTSTASH_API_DECL) +#define SOKOL_FONTSTASH_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_FONTSTASH_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_FONTSTASH_IMPL) +#define SOKOL_FONTSTASH_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_FONTSTASH_API_DECL __declspec(dllimport) +#else +#define SOKOL_FONTSTASH_API_DECL extern +#endif +#endif +#ifdef __cplusplus +extern "C" { +#endif + +SOKOL_FONTSTASH_API_DECL FONScontext* sfons_create(int width, int height, int flags); +SOKOL_FONTSTASH_API_DECL void sfons_destroy(FONScontext* ctx); +SOKOL_FONTSTASH_API_DECL void sfons_flush(FONScontext* ctx); +SOKOL_FONTSTASH_API_DECL uint32_t sfons_rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a); + +#ifdef __cplusplus +} /* extern "C" */ +#endif +#endif /* SOKOL_FONTSTASH_INCLUDED */ + +/*-- IMPLEMENTATION ----------------------------------------------------------*/ +#ifdef SOKOL_FONTSTASH_IMPL +#define SOKOL_FONTSTASH_IMPL_INCLUDED (1) +#include /* memset, memcpy */ + +#if !defined(SOKOL_GL_INCLUDED) +#error "Please include sokol_gl.h before sokol_fontstash.h" +#endif +#if !defined(FONS_H) +#error "Please include fontstash.h before sokol_fontstash.h" +#endif + +#ifndef SOKOL_API_IMPL + #define SOKOL_API_IMPL +#endif +#ifndef SOKOL_DEBUG + #ifndef NDEBUG + #define SOKOL_DEBUG (1) + #endif +#endif +#ifndef SOKOL_ASSERT + #include + #define SOKOL_ASSERT(c) assert(c) +#endif +#ifndef SOKOL_MALLOC + #include + #define SOKOL_MALLOC(s) malloc(s) + #define SOKOL_FREE(p) free(p) +#endif +#ifndef SOKOL_LOG + #ifdef SOKOL_DEBUG + #include + #define SOKOL_LOG(s) { SOKOL_ASSERT(s); puts(s); } + #else + #define SOKOL_LOG(s) + #endif +#endif +#ifndef SOKOL_UNREACHABLE + #define SOKOL_UNREACHABLE SOKOL_ASSERT(false) +#endif +#ifndef _SOKOL_UNUSED + #define _SOKOL_UNUSED(x) (void)(x) +#endif + +#if defined(SOKOL_GLCORE33) +/* + Embedded source code compiled with: + + sokol-shdc -i sfons.glsl -o sfons.h -l glsl330:glsl100:hlsl4:metal_macos:metal_ios:metal_sim:wgpu -b + + (not that for Metal and D3D11 byte code, sokol-shdc must be run + on macOS and Windows) + + @vs vs + uniform vs_params { + uniform mat4 mvp; + uniform mat4 tm; + }; + in vec4 position; + in vec2 texcoord0; + in vec4 color0; + out vec4 uv; + out vec4 color; + void main() { + gl_Position = mvp * position; + uv = tm * vec4(texcoord0, 0.0, 1.0); + color = color0; + } + @end + + @fs fs + uniform sampler2D tex; + in vec4 uv; + in vec4 color; + out vec4 frag_color; + void main() { + frag_color = vec4(1.0, 1.0, 1.0, texture(tex, uv.xy).r) * color; + } + @end + + @program sfontstash vs fs +*/ +static const char _sfons_vs_source_glsl330[415] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x33,0x30,0x0a,0x0a,0x75,0x6e, + 0x69,0x66,0x6f,0x72,0x6d,0x20,0x76,0x65,0x63,0x34,0x20,0x76,0x73,0x5f,0x70,0x61, + 0x72,0x61,0x6d,0x73,0x5b,0x38,0x5d,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28, + 0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x69,0x6e, + 0x20,0x76,0x65,0x63,0x34,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,0x0a, + 0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x75,0x76,0x3b,0x0a,0x6c,0x61,0x79, + 0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x31, + 0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x32,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f, + 0x72,0x64,0x30,0x3b,0x0a,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f, + 0x6c,0x6f,0x72,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61, + 0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x32,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63, + 0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20, + 0x6d,0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x67,0x6c,0x5f, + 0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x6d,0x61,0x74,0x34,0x28, + 0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x30,0x5d,0x2c,0x20,0x76,0x73, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x31,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70, + 0x61,0x72,0x61,0x6d,0x73,0x5b,0x32,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72, + 0x61,0x6d,0x73,0x5b,0x33,0x5d,0x29,0x20,0x2a,0x20,0x70,0x6f,0x73,0x69,0x74,0x69, + 0x6f,0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x6d,0x61,0x74, + 0x34,0x28,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x34,0x5d,0x2c,0x20, + 0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x35,0x5d,0x2c,0x20,0x76,0x73, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x36,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70, + 0x61,0x72,0x61,0x6d,0x73,0x5b,0x37,0x5d,0x29,0x20,0x2a,0x20,0x76,0x65,0x63,0x34, + 0x28,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x2c,0x20,0x30,0x2e,0x30,0x2c, + 0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72, + 0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +static const char _sfons_fs_source_glsl330[195] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x33,0x30,0x0a,0x0a,0x75,0x6e, + 0x69,0x66,0x6f,0x72,0x6d,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x32,0x44,0x20, + 0x74,0x65,0x78,0x3b,0x0a,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63, + 0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x6f,0x75,0x74,0x20,0x76, + 0x65,0x63,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a, + 0x69,0x6e,0x20,0x76,0x65,0x63,0x34,0x20,0x75,0x76,0x3b,0x0a,0x69,0x6e,0x20,0x76, + 0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64, + 0x20,0x6d,0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x72, + 0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x76,0x65,0x63,0x34,0x28, + 0x31,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x2c,0x20,0x74, + 0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x74,0x65,0x78,0x2c,0x20,0x75,0x76,0x2e,0x78, + 0x79,0x29,0x2e,0x78,0x29,0x20,0x2a,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x7d, + 0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_GLES2) || defined(SOKOL_GLES3) +static const char _sfons_vs_source_glsl100[381] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x31,0x30,0x30,0x0a,0x0a,0x75,0x6e, + 0x69,0x66,0x6f,0x72,0x6d,0x20,0x76,0x65,0x63,0x34,0x20,0x76,0x73,0x5f,0x70,0x61, + 0x72,0x61,0x6d,0x73,0x5b,0x38,0x5d,0x3b,0x0a,0x61,0x74,0x74,0x72,0x69,0x62,0x75, + 0x74,0x65,0x20,0x76,0x65,0x63,0x34,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e, + 0x3b,0x0a,0x76,0x61,0x72,0x79,0x69,0x6e,0x67,0x20,0x76,0x65,0x63,0x34,0x20,0x75, + 0x76,0x3b,0x0a,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x20,0x76,0x65,0x63, + 0x32,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a,0x76,0x61,0x72, + 0x79,0x69,0x6e,0x67,0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b, + 0x0a,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x20,0x76,0x65,0x63,0x34,0x20, + 0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61, + 0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f, + 0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x6d,0x61,0x74,0x34,0x28,0x76,0x73, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x30,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70, + 0x61,0x72,0x61,0x6d,0x73,0x5b,0x31,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72, + 0x61,0x6d,0x73,0x5b,0x32,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d, + 0x73,0x5b,0x33,0x5d,0x29,0x20,0x2a,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e, + 0x3b,0x0a,0x20,0x20,0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x6d,0x61,0x74,0x34,0x28, + 0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x34,0x5d,0x2c,0x20,0x76,0x73, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x35,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70, + 0x61,0x72,0x61,0x6d,0x73,0x5b,0x36,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72, + 0x61,0x6d,0x73,0x5b,0x37,0x5d,0x29,0x20,0x2a,0x20,0x76,0x65,0x63,0x34,0x28,0x74, + 0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20,0x31, + 0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +static const char _sfons_fs_source_glsl100[233] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x31,0x30,0x30,0x0a,0x70,0x72,0x65, + 0x63,0x69,0x73,0x69,0x6f,0x6e,0x20,0x6d,0x65,0x64,0x69,0x75,0x6d,0x70,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x3b,0x0a,0x70,0x72,0x65,0x63,0x69,0x73,0x69,0x6f,0x6e,0x20, + 0x68,0x69,0x67,0x68,0x70,0x20,0x69,0x6e,0x74,0x3b,0x0a,0x0a,0x75,0x6e,0x69,0x66, + 0x6f,0x72,0x6d,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65, + 0x72,0x32,0x44,0x20,0x74,0x65,0x78,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x79,0x69,0x6e, + 0x67,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x34,0x20,0x75,0x76,0x3b, + 0x0a,0x76,0x61,0x72,0x79,0x69,0x6e,0x67,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76, + 0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64, + 0x20,0x6d,0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x67,0x6c, + 0x5f,0x46,0x72,0x61,0x67,0x44,0x61,0x74,0x61,0x5b,0x30,0x5d,0x20,0x3d,0x20,0x76, + 0x65,0x63,0x34,0x28,0x31,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x2c,0x20,0x31,0x2e, + 0x30,0x2c,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x32,0x44,0x28,0x74,0x65,0x78, + 0x2c,0x20,0x75,0x76,0x2e,0x78,0x79,0x29,0x2e,0x78,0x29,0x20,0x2a,0x20,0x63,0x6f, + 0x6c,0x6f,0x72,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_METAL) +static const uint8_t _sfons_vs_bytecode_metal_macos[3360] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x20,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x3b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x10,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x00,0x48,0x41,0x53,0x48,0x20,0x00,0x54,0xec,0xec,0xa8,0x23,0x83,0x8d, + 0xc7,0xcf,0x6e,0x67,0x94,0x95,0x54,0x94,0xeb,0x98,0x15,0x2a,0x2c,0x54,0xa0,0x4b, + 0x24,0x6f,0x5e,0xed,0x6e,0x76,0x45,0xb7,0x5e,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x45,0x4e,0x44,0x54,0x37,0x00,0x00, + 0x00,0x56,0x41,0x54,0x54,0x22,0x00,0x03,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x6e,0x00,0x00,0x80,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x00,0x01,0x80, + 0x63,0x6f,0x6c,0x6f,0x72,0x30,0x00,0x02,0x80,0x56,0x41,0x54,0x59,0x05,0x00,0x03, + 0x00,0x06,0x04,0x06,0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54, + 0xde,0xc0,0x17,0x0b,0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0xf0,0x0b,0x00,0x00, + 0xff,0xff,0xff,0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0xf9,0x02,0x00,0x00, + 0x0b,0x82,0x20,0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91, + 0x41,0xc8,0x04,0x49,0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19, + 0x1e,0x04,0x8b,0x62,0x80,0x14,0x45,0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14, + 0x38,0x08,0x18,0x49,0x0a,0x32,0x44,0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80, + 0x0c,0x19,0x21,0x72,0x24,0x07,0xc8,0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0, + 0x01,0x00,0x00,0x00,0x51,0x18,0x00,0x00,0x81,0x00,0x00,0x00,0x1b,0xc8,0x25,0xf8, + 0xff,0xff,0xff,0xff,0x01,0x90,0x80,0x8a,0x18,0x87,0x77,0x90,0x07,0x79,0x28,0x87, + 0x71,0xa0,0x07,0x76,0xc8,0x87,0x36,0x90,0x87,0x77,0xa8,0x07,0x77,0x20,0x87,0x72, + 0x20,0x87,0x36,0x20,0x87,0x74,0xb0,0x87,0x74,0x20,0x87,0x72,0x68,0x83,0x79,0x88, + 0x07,0x79,0xa0,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07, + 0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1,0x1c,0x00,0x82,0x1c,0xd2,0x61,0x1e,0xc2,0x41, + 0x1c,0xd8,0xa1,0x1c,0xda,0x80,0x1e,0xc2,0x21,0x1d,0xd8,0xa1,0x0d,0xc6,0x21,0x1c, + 0xd8,0x81,0x1d,0xe6,0x01,0x30,0x87,0x70,0x60,0x87,0x79,0x28,0x07,0x80,0x60,0x87, + 0x72,0x98,0x87,0x79,0x68,0x03,0x78,0x90,0x87,0x72,0x18,0x87,0x74,0x98,0x87,0x72, + 0x68,0x03,0x73,0x80,0x87,0x76,0x08,0x07,0x72,0x00,0xcc,0x21,0x1c,0xd8,0x61,0x1e, + 0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0xc0,0x1c,0xe4,0x21,0x1c,0xda,0xa1,0x1c,0xda, + 0x00,0x1e,0xde,0x21,0x1d,0xdc,0x81,0x1e,0xca,0x41,0x1e,0xda,0xa0,0x1c,0xd8,0x21, + 0x1d,0xda,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x06,0x77,0x78,0x87,0x36,0x30, + 0x07,0x79,0x08,0x87,0x76,0x28,0x87,0x36,0x80,0x87,0x77,0x48,0x07,0x77,0xa0,0x87, + 0x72,0x90,0x87,0x36,0x28,0x07,0x76,0x48,0x87,0x76,0x68,0x03,0x77,0x78,0x07,0x77, + 0x68,0x03,0x76,0x28,0x87,0x70,0x30,0x07,0x80,0x70,0x87,0x77,0x68,0x83,0x74,0x70, + 0x07,0x73,0x98,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07, + 0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0x40,0x1d,0xea,0xa1, + 0x1d,0xe0,0xa1,0x0d,0xe8,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x1e,0x00,0x73,0x08, + 0x07,0x76,0x98,0x87,0x72,0x00,0x08,0x77,0x78,0x87,0x36,0x70,0x87,0x70,0x70,0x87, + 0x79,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41, + 0x1e,0xea,0xa1,0x1c,0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xe6,0x21,0x1d,0xce,0xc1,0x1d, + 0xca,0x81,0x1c,0xda,0x40,0x1f,0xca,0x41,0x1e,0xde,0x61,0x1e,0xda,0xc0,0x1c,0xe0, + 0xa1,0x0d,0xda,0x21,0x1c,0xe8,0x01,0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x80, + 0x70,0x87,0x77,0x68,0x03,0x7a,0x90,0x87,0x70,0x80,0x07,0x78,0x48,0x07,0x77,0x38, + 0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00, + 0x62,0x1e,0xe8,0x21,0x1c,0xc6,0x61,0x1d,0xda,0x00,0x1e,0xe4,0xe1,0x1d,0xe8,0xa1, + 0x1c,0xc6,0x81,0x1e,0xde,0x41,0x1e,0xda,0x40,0x1c,0xea,0xc1,0x1c,0xcc,0xa1,0x1c, + 0xe4,0xa1,0x0d,0xe6,0x21,0x1d,0xf4,0xa1,0x1c,0x00,0x3c,0x00,0x88,0x7a,0x70,0x87, + 0x79,0x08,0x07,0x73,0x28,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a, + 0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xea,0x61,0x1e,0xca,0xa1,0x0d, + 0xe6,0xe1,0x1d,0xcc,0x81,0x1e,0xda,0xc0,0x1c,0xd8,0xe1,0x1d,0xc2,0x81,0x1e,0x00, + 0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x36,0x18,0x02,0x01,0x2c,0x40,0x05,0x00, + 0x49,0x18,0x00,0x00,0x01,0x00,0x00,0x00,0x13,0x84,0x40,0x00,0x89,0x20,0x00,0x00, + 0x23,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85,0x04,0x93,0x22,0xa4,0x84, + 0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a,0x8c,0x0b,0x84,0xa4,0x4c, + 0x10,0x44,0x33,0x00,0xc3,0x08,0x04,0x70,0x90,0x34,0x45,0x94,0x30,0xf9,0x0c,0x80, + 0x34,0xf4,0xef,0x50,0x13,0x1a,0x42,0x08,0xc3,0x08,0x02,0x90,0x04,0x61,0x26,0x6a, + 0x1e,0xe8,0x41,0x1e,0xea,0x61,0x1c,0xe8,0xc1,0x0d,0xda,0xa1,0x1c,0xe8,0x21,0x1c, + 0xd8,0x41,0x0f,0xf4,0xa0,0x1d,0xc2,0x81,0x1e,0xe4,0x21,0x1d,0xf0,0x01,0x05,0xe4, + 0x20,0x69,0x8a,0x28,0x61,0xf2,0x2b,0xe9,0x7f,0x80,0x08,0x60,0x24,0x24,0x94,0x32, + 0x88,0x60,0x08,0xa5,0x10,0x61,0x84,0x43,0x68,0x20,0x60,0x8e,0x00,0x0c,0x72,0x60, + 0xcd,0x11,0x80,0xc2,0x20,0x42,0x20,0x0c,0x23,0x10,0xcb,0x08,0x00,0x00,0x00,0x00, + 0x13,0xb2,0x70,0x48,0x07,0x79,0xb0,0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60, + 0x87,0x72,0x68,0x83,0x76,0x08,0x87,0x71,0x78,0x87,0x79,0xc0,0x87,0x38,0x80,0x03, + 0x37,0x88,0x83,0x38,0x70,0x03,0x38,0xd8,0x70,0x1b,0xe5,0xd0,0x06,0xf0,0xa0,0x07, + 0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x90,0x0e,0x71, + 0xa0,0x07,0x78,0xa0,0x07,0x78,0xd0,0x06,0xe9,0x80,0x07,0x7a,0x80,0x07,0x7a,0x80, + 0x07,0x6d,0x90,0x0e,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07, + 0x6d,0x90,0x0e,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d, + 0x90,0x0e,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60, + 0x0e,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0e, + 0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x71, + 0x60,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x6d,0x60,0x0f,0x72,0x40, + 0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0f,0x73,0x20,0x07, + 0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0f,0x74,0x80,0x07,0x7a, + 0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x76,0x40,0x07,0x7a,0x60, + 0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x79,0x60,0x07,0x7a,0x10,0x07, + 0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x6d,0x60,0x0f,0x71,0x20,0x07,0x78, + 0xa0,0x07,0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10, + 0x07,0x79,0x20,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07, + 0x6d,0x60,0x0f,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72, + 0x50,0x07,0x76,0xd0,0x06,0xf6,0x50,0x07,0x71,0x20,0x07,0x7a,0x50,0x07,0x71,0x20, + 0x07,0x7a,0x50,0x07,0x71,0x20,0x07,0x6d,0x60,0x0f,0x71,0x00,0x07,0x72,0x40,0x07, + 0x7a,0x10,0x07,0x70,0x20,0x07,0x74,0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x6d, + 0xe0,0x0e,0x78,0xa0,0x07,0x71,0x60,0x07,0x7a,0x30,0x07,0x72,0x30,0x84,0x49,0x00, + 0x00,0x08,0x00,0x00,0x00,0x00,0x00,0xc8,0x02,0x01,0x00,0x00,0x0b,0x00,0x00,0x00, + 0x32,0x1e,0x98,0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x5a, + 0x25,0x30,0x02,0x50,0x04,0x05,0x18,0x50,0x80,0x02,0x85,0x50,0x10,0x65,0x50,0x20, + 0xd4,0x46,0x00,0x88,0x8d,0x35,0x28,0x0f,0x01,0x00,0x00,0x00,0x79,0x18,0x00,0x00, + 0x19,0x01,0x00,0x00,0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9, + 0xb9,0xb4,0x37,0xb7,0x21,0xc6,0x32,0x28,0x00,0xb3,0x50,0xb9,0x1b,0x43,0x0b,0x93, + 0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62,0x2c,0x81,0x22,0x2c,0x06,0xd9,0x20,0x08,0x0e, + 0x8e,0xad,0x0c,0x84,0x89,0xc9,0xaa,0x09,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d, + 0x24,0x07,0x46,0xc6,0x25,0x86,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac, + 0x25,0x07,0x46,0xc6,0x25,0x86,0xc6,0x25,0x27,0x65,0x88,0xa0,0x10,0x43,0x8c,0x25, + 0x58,0x8e,0x45,0x60,0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x51,0x8e,0x25,0x58,0x82, + 0x45,0xe0,0x16,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42,0x56, + 0xe6,0xf6,0x26,0xd7,0x36,0xf7,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x50, + 0x12,0x72,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66,0x61, + 0x73,0x74,0x5f,0x6d,0x61,0x74,0x68,0x5f,0x65,0x6e,0x61,0x62,0x6c,0x65,0x43,0x04, + 0x65,0x61,0x19,0x84,0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9,0x98, + 0xc9,0x85,0xb5,0x95,0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d,0xa1, + 0x7d,0x91,0xa5,0xcd,0x85,0x89,0xb1,0x95,0x0d,0x11,0x94,0x86,0x61,0x10,0x96,0x26, + 0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0xe2,0x16,0x46,0x97,0x66,0x57,0xf6, + 0x45,0xf6,0x56,0x27,0xc6,0x56,0xf6,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44, + 0x50,0x1e,0x92,0x41,0x58,0x9a,0x9c,0xcb,0xd8,0x5b,0x1b,0x5c,0x1a,0x5b,0x99,0x8b, + 0x5b,0x18,0x5d,0x9a,0x5d,0xd9,0x17,0xdb,0x9b,0xdb,0xd9,0x17,0xdb,0x9b,0xdb,0xd9, + 0x17,0x59,0xda,0x5c,0x98,0x18,0x5b,0xd9,0x10,0x41,0x89,0x78,0x06,0x61,0x69,0x72, + 0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x6e,0x61,0x74,0x69,0x76,0x65,0x5f, + 0x77,0x69,0x64,0x65,0x5f,0x76,0x65,0x63,0x74,0x6f,0x72,0x73,0x5f,0x64,0x69,0x73, + 0x61,0x62,0x6c,0x65,0x43,0x04,0x65,0x62,0x14,0x96,0x26,0xe7,0x62,0x57,0x26,0x47, + 0x57,0x86,0xf7,0xf5,0x56,0x47,0x07,0x57,0x47,0xc7,0xa5,0x6e,0xae,0x4c,0x0e,0x85, + 0xed,0x6d,0xcc,0x0d,0x26,0x85,0x51,0x58,0x9a,0x9c,0x4b,0x98,0xdc,0xd9,0x17,0x5d, + 0x1e,0x5c,0xd9,0x97,0x5b,0x58,0x5b,0x19,0x0d,0x33,0xb6,0xb7,0x30,0x3a,0x1a,0x32, + 0x61,0x69,0x72,0x2e,0x61,0x72,0x67,0x5f,0x6e,0x61,0x6d,0x65,0x14,0xea,0xec,0x86, + 0x30,0x4a,0xa5,0x58,0xca,0xa5,0x60,0x4a,0xa6,0x68,0x5c,0xea,0xe6,0xca,0xe4,0x50, + 0xd8,0xde,0xc6,0xdc,0x62,0x52,0x58,0x8c,0xbd,0xb1,0xbd,0xc9,0x0d,0x61,0x94,0x4a, + 0xe1,0x94,0x4b,0xc1,0x94,0x4c,0xe9,0xc8,0x84,0xa5,0xc9,0xb9,0xc0,0xbd,0xcd,0xa5, + 0xd1,0xa5,0xbd,0xb9,0x71,0x39,0x63,0xfb,0x82,0x7a,0x9b,0x4b,0xa3,0x4b,0x7b,0x73, + 0x1b,0xa2,0x28,0x9f,0x72,0x29,0x98,0x92,0x29,0x60,0x30,0xc4,0x50,0x36,0xc5,0x53, + 0xc2,0x80,0x50,0x58,0x9a,0x9c,0x8b,0x5d,0x99,0x1c,0x5d,0x19,0xde,0x57,0x9a,0x1b, + 0x5c,0x1d,0x1d,0xa5,0xb0,0x34,0x39,0x17,0xb6,0xb7,0xb1,0x30,0xba,0xb4,0x37,0xb7, + 0xaf,0x34,0x37,0xb2,0x32,0x3c,0x66,0x67,0x65,0x6e,0x65,0x72,0x61,0x74,0x65,0x64, + 0x28,0x38,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x44,0x76,0x34,0x5f,0x66,0x29, + 0x44,0xe0,0xde,0xe6,0xd2,0xe8,0xd2,0xde,0xdc,0x86,0x50,0x8b,0xa0,0x8c,0x81,0x42, + 0x06,0x8b,0xb0,0x04,0x4a,0x19,0x28,0x97,0x82,0x29,0x99,0x62,0x06,0xd4,0xce,0xca, + 0xdc,0xca,0xe4,0xc2,0xe8,0xca,0xc8,0x50,0x72,0xe8,0xca,0xf0,0xc6,0xde,0xde,0xe4, + 0xc8,0x60,0x88,0xec,0x64,0xbe,0xcc,0x52,0x68,0x98,0xb1,0xbd,0x85,0xd1,0xc9,0x30, + 0xa1,0x2b,0xc3,0x1b,0x7b,0x7b,0x93,0x23,0x83,0x19,0x42,0x2d,0x81,0x32,0x06,0x0a, + 0x19,0x2c,0xc1,0x12,0x28,0x68,0xa0,0x5c,0x4a,0x1a,0x28,0x99,0xa2,0x06,0xbc,0xce, + 0xca,0xdc,0xca,0xe4,0xc2,0xe8,0xca,0xc8,0x50,0x6c,0xc6,0xde,0xd8,0xde,0xe4,0x60, + 0x88,0xec,0x68,0xbe,0xcc,0x52,0x68,0x8c,0xbd,0xb1,0xbd,0xc9,0xc1,0x0c,0xa1,0x96, + 0x41,0x19,0x03,0x85,0x0c,0x96,0x61,0x09,0x14,0x36,0x50,0x2e,0x05,0x53,0x32,0xa5, + 0x0d,0xa8,0x84,0xa5,0xc9,0xb9,0x88,0xd5,0x99,0x99,0x95,0xc9,0xf1,0x09,0x4b,0x93, + 0x73,0x11,0xab,0x33,0x33,0x2b,0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x23,0x12,0x96,0x26, + 0xe7,0x22,0x57,0x16,0x46,0x46,0x2a,0x2c,0x4d,0xce,0x65,0x8e,0x4e,0xae,0x6e,0x8c, + 0xee,0x8b,0x2e,0x0f,0xae,0xec,0x2b,0xcd,0xcd,0xec,0x8d,0x88,0x19,0xdb,0x5b,0x18, + 0x1d,0x0d,0x1e,0x0d,0x87,0x36,0x3b,0x38,0x0a,0x74,0x6d,0x43,0xa8,0x45,0x58,0x88, + 0x45,0x50,0xe6,0x40,0xa1,0x83,0x85,0x58,0x88,0x45,0x50,0xe6,0x40,0xa9,0x03,0x46, + 0x61,0x69,0x72,0x2e,0x61,0x72,0x67,0x5f,0x74,0x79,0x70,0x65,0x5f,0x73,0x69,0x7a, + 0x65,0xbc,0xc2,0xd2,0xe4,0x5c,0xc2,0xe4,0xce,0xbe,0xe8,0xf2,0xe0,0xca,0xbe,0xc2, + 0xd8,0xd2,0xce,0xdc,0xbe,0xe6,0xd2,0xf4,0xca,0x98,0xd8,0xcd,0x7d,0xc1,0x85,0xc9, + 0x85,0xb5,0xcd,0x71,0xf8,0x92,0x81,0x19,0x42,0x06,0x0b,0xa2,0xbc,0x81,0x02,0x07, + 0x4b,0xa1,0x90,0xc1,0x22,0x2c,0x81,0x12,0x07,0x8a,0x1c,0x28,0x76,0xa0,0xdc,0xc1, + 0x52,0x28,0x78,0xb0,0x24,0xca,0xa5,0xe4,0x81,0x92,0x29,0x7a,0x30,0x04,0x51,0xce, + 0x40,0x59,0x03,0xc5,0x0d,0x94,0x3d,0x18,0x62,0x24,0x80,0x22,0x06,0x0a,0x1f,0xf0, + 0x79,0x6b,0x73,0x4b,0x83,0x7b,0xa3,0x2b,0x73,0xa3,0x03,0x19,0x43,0x0b,0x93,0xe3, + 0x33,0x95,0xd6,0x06,0xc7,0x56,0x06,0x32,0xb4,0xb2,0x02,0x42,0x25,0x14,0x14,0x34, + 0x44,0x50,0xfe,0x60,0x88,0xa1,0xf8,0x81,0x02,0x0a,0x8d,0x32,0xc4,0x50,0x42,0x41, + 0x09,0x85,0x46,0x19,0x11,0xb1,0x03,0x3b,0xd8,0x43,0x3b,0xb8,0x41,0x3b,0xbc,0x03, + 0x39,0xd4,0x03,0x3b,0x94,0x83,0x1b,0x98,0x03,0x3b,0x84,0xc3,0x39,0xcc,0xc3,0x14, + 0x21,0x18,0x46,0x28,0xec,0xc0,0x0e,0xf6,0xd0,0x0e,0x6e,0x90,0x0e,0xe4,0x50,0x0e, + 0xee,0x40,0x0f,0x53,0x82,0x62,0xc4,0x12,0x0e,0xe9,0x20,0x0f,0x6e,0x60,0x0f,0xe5, + 0x20,0x0f,0xf3,0x90,0x0e,0xef,0xe0,0x0e,0x53,0x02,0x63,0x04,0x15,0x0e,0xe9,0x20, + 0x0f,0x6e,0xc0,0x0e,0xe1,0xe0,0x0e,0xe7,0x50,0x0f,0xe1,0x70,0x0e,0xe5,0xf0,0x0b, + 0xf6,0x50,0x0e,0xf2,0x30,0x0f,0xe9,0xf0,0x0e,0xee,0x30,0x25,0x40,0x46,0x4c,0xe1, + 0x90,0x0e,0xf2,0xe0,0x06,0xe3,0xf0,0x0e,0xed,0x00,0x0f,0xe9,0xc0,0x0e,0xe5,0xf0, + 0x0b,0xef,0x00,0x0f,0xf4,0x90,0x0e,0xef,0xe0,0x0e,0xf3,0x30,0xc5,0x50,0x18,0x07, + 0x92,0xa8,0x11,0x4a,0x38,0xa4,0x83,0x3c,0xb8,0x81,0x3d,0x94,0x83,0x3c,0xd0,0x43, + 0x39,0xe0,0xc3,0x94,0xa0,0x0f,0x00,0x00,0x79,0x18,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3, + 0x8c,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10, + 0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30, + 0x05,0x3d,0x88,0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03, + 0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07, + 0x7a,0x70,0x03,0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e, + 0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d, + 0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b, + 0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76, + 0x60,0x07,0x7b,0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90, + 0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87, + 0x5f,0x08,0x87,0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e, + 0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c, + 0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca, + 0x61,0x06,0xd6,0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8, + 0xc3,0x38,0x94,0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82, + 0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x0c,0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70,0x83, + 0x74,0x68,0x07,0x78,0x60,0x87,0x74,0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53,0x0f, + 0xee,0x00,0x0f,0xf2,0x50,0x0e,0xe4,0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e,0xec, + 0x50,0x0e,0x33,0x20,0x28,0x1d,0xdc,0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c,0xdc, + 0x81,0x1e,0xdc,0xe0,0x1c,0xe4,0xe1,0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38,0xb0, + 0x43,0x3a,0x9c,0x83,0x3b,0xcc,0x50,0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x60, + 0x87,0x77,0x78,0x07,0x78,0x98,0x51,0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33,0x1e, + 0x6a,0x1e,0xca,0x61,0x1c,0xe8,0x21,0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4,0xa1, + 0x1c,0xcc,0x21,0x1d,0xf0,0x61,0x06,0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0,0x43, + 0x3d,0xd0,0x43,0x39,0xfc,0xc2,0x3c,0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3,0x8c, + 0xc5,0x0a,0x87,0x79,0x98,0x87,0x77,0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07,0x72, + 0x00,0x00,0x00,0x00,0x71,0x20,0x00,0x00,0x02,0x00,0x00,0x00,0x06,0x50,0x30,0x00, + 0xd2,0xd0,0x00,0x00,0x61,0x20,0x00,0x00,0x3d,0x00,0x00,0x00,0x13,0x04,0x41,0x2c, + 0x10,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0xf4,0xc6,0x22,0x86,0x61,0x18,0xc6,0x22, + 0x04,0x41,0x10,0xc6,0x22,0x82,0x20,0x08,0xa8,0x95,0x40,0x19,0x14,0x01,0xbd,0x11, + 0x00,0x1a,0x33,0x00,0x24,0x66,0x00,0x28,0xcc,0x00,0x00,0x00,0xe3,0x15,0x0b,0x84, + 0x61,0x10,0x05,0x65,0x90,0x01,0x1a,0x0c,0x13,0x02,0xf9,0x8c,0x57,0x3c,0x14,0xc7, + 0x2d,0x14,0x94,0x41,0x06,0xea,0x70,0x4c,0x08,0xe4,0x63,0x41,0x01,0x9f,0xf1,0x0a, + 0x2a,0x0b,0x83,0x30,0x70,0x28,0x28,0x83,0x0c,0x19,0x43,0x99,0x10,0xc8,0xc7,0x8a, + 0x00,0x3e,0xe3,0x15,0x99,0x67,0x06,0x66,0x40,0x51,0x50,0x06,0x19,0xbc,0x48,0x33, + 0x21,0x90,0x8f,0x15,0x01,0x7c,0xc6,0x2b,0xbc,0x31,0x60,0x83,0x35,0x18,0x03,0x0a, + 0xca,0x20,0x83,0x18,0x60,0x99,0x09,0x81,0x7c,0xc6,0x2b,0xc4,0xe0,0x0c,0xe0,0xe0, + 0x0d,0x3c,0x0a,0xca,0x20,0x83,0x19,0x70,0x61,0x60,0x42,0x20,0x1f,0x0b,0x0a,0xf8, + 0x8c,0x57,0x9c,0x01,0x1b,0xd4,0x01,0x1d,0x88,0x01,0x05,0xc5,0x86,0x00,0x3e,0xb3, + 0x0d,0x61,0x10,0x00,0xb3,0x0d,0x01,0x1b,0x04,0xb3,0x0d,0xc1,0x23,0x64,0x10,0x10, + 0x03,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x5b,0x86,0x20,0x10,0x85,0x2d,0x43,0x11, + 0x88,0xc2,0x96,0x41,0x09,0x44,0x61,0xcb,0xf0,0x04,0xa2,0xb0,0x65,0xa0,0x02,0x51, + 0xd8,0x32,0x60,0x81,0x28,0x6c,0x19,0xba,0x40,0x14,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _sfons_fs_bytecode_metal_macos[2941] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x7d,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd5,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xa0,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0xfd,0x3a,0x06,0x83,0x09,0xe9,0xe7, + 0xd2,0xb5,0xf9,0x1e,0x86,0x99,0xb0,0x0c,0x8c,0xbe,0xe0,0xd3,0x2d,0x9b,0x8c,0xe9, + 0x2a,0x92,0xa3,0xeb,0x0b,0xf7,0x98,0x18,0x30,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x45,0x4e,0x44,0x54,0x04,0x00,0x00, + 0x00,0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17, + 0x0b,0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x84,0x0a,0x00,0x00,0xff,0xff,0xff, + 0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0x9e,0x02,0x00,0x00,0x0b,0x82,0x20, + 0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04, + 0x49,0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b, + 0x62,0x80,0x14,0x45,0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14,0x38,0x08,0x18, + 0x49,0x0a,0x32,0x44,0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21, + 0x72,0x24,0x07,0xc8,0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00, + 0x00,0x51,0x18,0x00,0x00,0x89,0x00,0x00,0x00,0x1b,0xcc,0x25,0xf8,0xff,0xff,0xff, + 0xff,0x01,0x60,0x00,0x09,0xa8,0x88,0x71,0x78,0x07,0x79,0x90,0x87,0x72,0x18,0x07, + 0x7a,0x60,0x87,0x7c,0x68,0x03,0x79,0x78,0x87,0x7a,0x70,0x07,0x72,0x28,0x07,0x72, + 0x68,0x03,0x72,0x48,0x07,0x7b,0x48,0x07,0x72,0x28,0x87,0x36,0x98,0x87,0x78,0x90, + 0x07,0x7a,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xcc, + 0x21,0x1c,0xd8,0x61,0x1e,0xca,0x01,0x20,0xc8,0x21,0x1d,0xe6,0x21,0x1c,0xc4,0x81, + 0x1d,0xca,0xa1,0x0d,0xe8,0x21,0x1c,0xd2,0x81,0x1d,0xda,0x60,0x1c,0xc2,0x81,0x1d, + 0xd8,0x61,0x1e,0x00,0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x08,0x76,0x28,0x87, + 0x79,0x98,0x87,0x36,0x80,0x07,0x79,0x28,0x87,0x71,0x48,0x87,0x79,0x28,0x87,0x36, + 0x30,0x07,0x78,0x68,0x87,0x70,0x20,0x07,0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1,0x1c, + 0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xcc,0x41,0x1e,0xc2,0xa1,0x1d,0xca,0xa1,0x0d,0xe0, + 0xe1,0x1d,0xd2,0xc1,0x1d,0xe8,0xa1,0x1c,0xe4,0xa1,0x0d,0xca,0x81,0x1d,0xd2,0xa1, + 0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x60,0x70,0x87,0x77,0x68,0x03,0x73,0x90, + 0x87,0x70,0x68,0x87,0x72,0x68,0x03,0x78,0x78,0x87,0x74,0x70,0x07,0x7a,0x28,0x07, + 0x79,0x68,0x83,0x72,0x60,0x87,0x74,0x68,0x87,0x36,0x70,0x87,0x77,0x70,0x87,0x36, + 0x60,0x87,0x72,0x08,0x07,0x73,0x00,0x08,0x77,0x78,0x87,0x36,0x48,0x07,0x77,0x30, + 0x87,0x79,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8, + 0x41,0x1e,0xea,0xa1,0x1c,0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xd4,0xa1,0x1e,0xda,0x01, + 0x1e,0xda,0x80,0x1e,0xc2,0x41,0x1c,0xd8,0xa1,0x1c,0xe6,0x01,0x30,0x87,0x70,0x60, + 0x87,0x79,0x28,0x07,0x80,0x70,0x87,0x77,0x68,0x03,0x77,0x08,0x07,0x77,0x98,0x87, + 0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1, + 0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0x60,0x1e,0xd2,0xe1,0x1c,0xdc,0xa1,0x1c, + 0xc8,0xa1,0x0d,0xf4,0xa1,0x1c,0xe4,0xe1,0x1d,0xe6,0xa1,0x0d,0xcc,0x01,0x1e,0xda, + 0xa0,0x1d,0xc2,0x81,0x1e,0xd0,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x08,0x77, + 0x78,0x87,0x36,0xa0,0x07,0x79,0x08,0x07,0x78,0x80,0x87,0x74,0x70,0x87,0x73,0x68, + 0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xe6, + 0x81,0x1e,0xc2,0x61,0x1c,0xd6,0xa1,0x0d,0xe0,0x41,0x1e,0xde,0x81,0x1e,0xca,0x61, + 0x1c,0xe8,0xe1,0x1d,0xe4,0xa1,0x0d,0xc4,0xa1,0x1e,0xcc,0xc1,0x1c,0xca,0x41,0x1e, + 0xda,0x60,0x1e,0xd2,0x41,0x1f,0xca,0x01,0xc0,0x03,0x80,0xa8,0x07,0x77,0x98,0x87, + 0x70,0x30,0x87,0x72,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74, + 0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0xa2,0x1e,0xe6,0xa1,0x1c,0xda,0x60,0x1e, + 0xde,0xc1,0x1c,0xe8,0xa1,0x0d,0xcc,0x81,0x1d,0xde,0x21,0x1c,0xe8,0x01,0x30,0x87, + 0x70,0x60,0x87,0x79,0x28,0x07,0x60,0x83,0x21,0x0c,0xc0,0x02,0x54,0x1b,0x8c,0x81, + 0x00,0x16,0xa0,0xda,0x80,0x10,0xff,0xff,0xff,0xff,0x3f,0x00,0x0c,0x20,0x01,0xd5, + 0x06,0xa3,0x08,0x80,0x05,0xa8,0x36,0x18,0x86,0x00,0x2c,0x40,0x05,0x49,0x18,0x00, + 0x00,0x03,0x00,0x00,0x00,0x13,0x86,0x40,0x18,0x26,0x0c,0x44,0x61,0x00,0x00,0x00, + 0x00,0x89,0x20,0x00,0x00,0x21,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85, + 0x04,0x93,0x22,0xa4,0x84,0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a, + 0x8c,0x0b,0x84,0xa4,0x4c,0x10,0x4c,0x33,0x00,0xc3,0x08,0x04,0x70,0x90,0x34,0x45, + 0x94,0x30,0xf9,0x0c,0x80,0x34,0xf4,0xef,0x50,0x13,0x0a,0xc2,0x51,0xd2,0x14,0x51, + 0xc2,0xe4,0xff,0x13,0x71,0x4d,0x54,0x44,0xfc,0xf6,0xf0,0x4f,0x63,0x04,0xc0,0x20, + 0xc2,0x10,0x5c,0x24,0x4d,0x11,0x25,0x4c,0xfe,0x2f,0x01,0xcc,0xb3,0x10,0xd1,0x3f, + 0x8d,0x11,0x00,0x83,0x08,0x85,0x50,0x0a,0x11,0x02,0x31,0x74,0x86,0x11,0x04,0x60, + 0x8e,0x20,0x98,0x23,0x00,0x83,0x61,0x04,0x61,0x29,0x48,0x20,0x26,0x29,0xa6,0x00, + 0xb5,0x81,0x80,0x1c,0x58,0xc3,0x08,0xc4,0x32,0x02,0x00,0x00,0x00,0x13,0xb2,0x70, + 0x48,0x07,0x79,0xb0,0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60,0x87,0x72,0x68, + 0x83,0x76,0x08,0x87,0x71,0x78,0x87,0x79,0xc0,0x87,0x38,0x80,0x03,0x37,0x88,0x83, + 0x38,0x70,0x03,0x38,0xd8,0x70,0x1b,0xe5,0xd0,0x06,0xf0,0xa0,0x07,0x76,0x40,0x07, + 0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x90,0x0e,0x71,0xa0,0x07,0x78, + 0xa0,0x07,0x78,0xd0,0x06,0xe9,0x80,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07,0x6d,0x90, + 0x0e,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x6d,0x90,0x0e, + 0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x76, + 0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0e,0x73,0x20, + 0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0e,0x76,0x40,0x07, + 0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x71,0x60,0x07,0x7a, + 0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x6d,0x60,0x0f,0x72,0x40,0x07,0x7a,0x30, + 0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0f,0x73,0x20,0x07,0x7a,0x30,0x07, + 0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0f,0x74,0x80,0x07,0x7a,0x60,0x07,0x74, + 0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0, + 0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x79,0x60,0x07,0x7a,0x10,0x07,0x72,0x80,0x07, + 0x7a,0x10,0x07,0x72,0x80,0x07,0x6d,0x60,0x0f,0x71,0x20,0x07,0x78,0xa0,0x07,0x71, + 0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10,0x07,0x79,0x20, + 0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f, + 0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76, + 0xd0,0x06,0xf6,0x50,0x07,0x71,0x20,0x07,0x7a,0x50,0x07,0x71,0x20,0x07,0x7a,0x50, + 0x07,0x71,0x20,0x07,0x6d,0x60,0x0f,0x71,0x00,0x07,0x72,0x40,0x07,0x7a,0x10,0x07, + 0x70,0x20,0x07,0x74,0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x6d,0xe0,0x0e,0x78, + 0xa0,0x07,0x71,0x60,0x07,0x7a,0x30,0x07,0x72,0x30,0x84,0x41,0x00,0x00,0x08,0x00, + 0x00,0x00,0x00,0x00,0x18,0xc2,0x38,0x40,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x64, + 0x81,0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x32,0x1e,0x98,0x10,0x19,0x11,0x4c, + 0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x5a,0x25,0x30,0x02,0x50,0x04,0x85,0x50, + 0x10,0x65,0x40,0x70,0xac,0x41,0x79,0x08,0x00,0x79,0x18,0x00,0x00,0xd7,0x00,0x00, + 0x00,0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37, + 0xb7,0x21,0xc6,0x42,0x3c,0x00,0x84,0x50,0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b, + 0xd3,0x2b,0x1b,0x62,0x2c,0xc2,0x23,0x2c,0x05,0xd9,0x20,0x08,0x0e,0x8e,0xad,0x0c, + 0x84,0x89,0xc9,0xaa,0x09,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x24,0x07,0x46, + 0xc6,0x25,0x86,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x25,0x07,0x46, + 0xc6,0x25,0x86,0xc6,0x25,0x27,0x65,0x88,0xf0,0x10,0x43,0x8c,0x45,0x58,0x8c,0x65, + 0x60,0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x79,0x8e,0x45,0x58,0x84,0x65,0xe0,0x16, + 0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42,0x56,0xe6,0xf6,0x26, + 0xd7,0x36,0xf7,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x78,0x12,0x72,0x61, + 0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66,0x61,0x73,0x74,0x5f, + 0x6d,0x61,0x74,0x68,0x5f,0x65,0x6e,0x61,0x62,0x6c,0x65,0x43,0x84,0x67,0x61,0x19, + 0x84,0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9,0x98,0xc9,0x85,0xb5, + 0x95,0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d,0xa1,0x7d,0x91,0xa5, + 0xcd,0x85,0x89,0xb1,0x95,0x0d,0x11,0x9e,0x86,0x61,0x10,0x96,0x26,0xe7,0x32,0xf6, + 0xd6,0x06,0x97,0xc6,0x56,0xe6,0xe2,0x16,0x46,0x97,0x66,0x57,0xf6,0x45,0xf6,0x56, + 0x27,0xc6,0x56,0xf6,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x78,0x1e,0x92, + 0x41,0x58,0x9a,0x9c,0xcb,0xd8,0x5b,0x1b,0x5c,0x1a,0x5b,0x99,0x8b,0x5b,0x18,0x5d, + 0x9a,0x5d,0xd9,0x17,0xdb,0x9b,0xdb,0xd9,0x17,0xdb,0x9b,0xdb,0xd9,0x17,0x59,0xda, + 0x5c,0x98,0x18,0x5b,0xd9,0x10,0xe1,0x89,0x78,0x06,0x61,0x69,0x72,0x2e,0x63,0x6f, + 0x6d,0x70,0x69,0x6c,0x65,0x2e,0x6e,0x61,0x74,0x69,0x76,0x65,0x5f,0x77,0x69,0x64, + 0x65,0x5f,0x76,0x65,0x63,0x74,0x6f,0x72,0x73,0x5f,0x64,0x69,0x73,0x61,0x62,0x6c, + 0x65,0x43,0x84,0x67,0x62,0x14,0x96,0x26,0xe7,0x22,0x57,0xe6,0x46,0x56,0x26,0xf7, + 0x45,0x17,0x26,0x77,0x56,0x46,0xc7,0x28,0x2c,0x4d,0xce,0x25,0x4c,0xee,0xec,0x8b, + 0x2e,0x0f,0xae,0xec,0xcb,0x2d,0xac,0xad,0x8c,0x86,0x19,0xdb,0x5b,0x18,0x1d,0x0d, + 0x99,0xb0,0x34,0x39,0x97,0x30,0xb9,0xb3,0x2f,0xb7,0xb0,0xb6,0x32,0x2a,0x66,0x72, + 0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x43,0x98,0xa7,0x5a,0x86,0xc7,0x7a,0xae, + 0x07,0x7b,0xb2,0x21,0xc2,0xa3,0x51,0x0a,0x4b,0x93,0x73,0x31,0x93,0x0b,0x3b,0x6b, + 0x2b,0x73,0xa3,0xfb,0x4a,0x73,0x83,0xab,0xa3,0xe3,0x52,0x37,0x57,0x26,0x87,0xc2, + 0xf6,0x36,0xe6,0x06,0x93,0x42,0x25,0x2c,0x4d,0xce,0x65,0xac,0xcc,0x8d,0xae,0x4c, + 0x8e,0x4f,0x58,0x9a,0x9c,0x0b,0x5c,0x99,0xdc,0x1c,0x5c,0xd9,0x18,0x5d,0x9a,0x5d, + 0x19,0x85,0x3a,0xbb,0x21,0xd2,0x32,0x3c,0xdc,0xd3,0x3d,0xde,0xf3,0x3d,0xd6,0x73, + 0x3d,0xd8,0x03,0x06,0x5c,0xea,0xe6,0xca,0xe4,0x50,0xd8,0xde,0xc6,0xdc,0x62,0x52, + 0x58,0x8c,0xbd,0xb1,0xbd,0xc9,0x0d,0x91,0x16,0xe1,0xe1,0x1e,0x31,0x78,0xbc,0xe7, + 0x7b,0xac,0xe7,0x7a,0xb0,0x67,0x0c,0xb8,0x84,0xa5,0xc9,0xb9,0xd0,0x95,0xe1,0xd1, + 0xd5,0xc9,0x95,0x51,0x0a,0x4b,0x93,0x73,0x61,0x7b,0x1b,0x0b,0xa3,0x4b,0x7b,0x73, + 0xfb,0x4a,0x73,0x23,0x2b,0xc3,0xa3,0x12,0x96,0x26,0xe7,0x32,0x17,0xd6,0x06,0xc7, + 0x56,0x46,0x8c,0xae,0x0c,0x8f,0xae,0x4e,0xae,0x4c,0x86,0x8c,0xc7,0x8c,0xed,0x2d, + 0x8c,0x8e,0x05,0x64,0x2e,0xac,0x0d,0x8e,0xad,0xcc,0x87,0x03,0x5d,0x19,0xde,0x10, + 0x6a,0x21,0x9e,0x32,0x78,0xcc,0x60,0x19,0x16,0xe1,0x39,0x83,0xc7,0x7a,0xd0,0xe0, + 0xc1,0x9e,0x34,0xe0,0x12,0x96,0x26,0xe7,0x32,0x17,0xd6,0x06,0xc7,0x56,0x26,0xc7, + 0x63,0x2e,0xac,0x0d,0x8e,0xad,0x4c,0x8e,0x08,0x5d,0x19,0xde,0x54,0x1b,0x1c,0x9b, + 0xdc,0x10,0x69,0x39,0x9e,0x35,0x78,0xcc,0x60,0x19,0x16,0xe1,0xb1,0x1e,0x36,0x78, + 0xb0,0xa7,0x0d,0x86,0x20,0x4f,0x18,0x3c,0x64,0xf0,0xa8,0xc1,0xe3,0x06,0x43,0x0c, + 0x04,0x78,0xb6,0xe7,0x0d,0x46,0x44,0xec,0xc0,0x0e,0xf6,0xd0,0x0e,0x6e,0xd0,0x0e, + 0xef,0x40,0x0e,0xf5,0xc0,0x0e,0xe5,0xe0,0x06,0xe6,0xc0,0x0e,0xe1,0x70,0x0e,0xf3, + 0x30,0x45,0x08,0x86,0x11,0x0a,0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b,0xa4,0x03,0x39, + 0x94,0x83,0x3b,0xd0,0xc3,0x94,0xa0,0x18,0xb1,0x84,0x43,0x3a,0xc8,0x83,0x1b,0xd8, + 0x43,0x39,0xc8,0xc3,0x3c,0xa4,0xc3,0x3b,0xb8,0xc3,0x94,0xc0,0x18,0x41,0x85,0x43, + 0x3a,0xc8,0x83,0x1b,0xb0,0x43,0x38,0xb8,0xc3,0x39,0xd4,0x43,0x38,0x9c,0x43,0x39, + 0xfc,0x82,0x3d,0x94,0x83,0x3c,0xcc,0x43,0x3a,0xbc,0x83,0x3b,0x4c,0x09,0x90,0x11, + 0x53,0x38,0xa4,0x83,0x3c,0xb8,0xc1,0x38,0xbc,0x43,0x3b,0xc0,0x43,0x3a,0xb0,0x43, + 0x39,0xfc,0xc2,0x3b,0xc0,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x3c,0x4c,0x31,0x14, + 0xc6,0x81,0x24,0x6a,0x04,0x13,0x0e,0xe9,0x20,0x0f,0x6e,0x60,0x0e,0xf2,0x10,0x0e, + 0xe7,0xd0,0x0e,0xe5,0xe0,0x0e,0xf4,0x30,0x25,0x80,0x03,0x00,0x00,0x79,0x18,0x00, + 0x00,0x6d,0x00,0x00,0x00,0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d, + 0x88,0x43,0x38,0x84,0xc3,0x8c,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c, + 0xe6,0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d, + 0xce,0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88,0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d, + 0xc8,0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79, + 0x48,0x87,0x70,0x70,0x07,0x7a,0x70,0x03,0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc, + 0x11,0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50, + 0x0e,0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30, + 0x89,0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03, + 0x3b,0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07, + 0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76, + 0x78,0x87,0x77,0x80,0x87,0x5f,0x08,0x87,0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98, + 0x81,0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8, + 0xa1,0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21, + 0x1c,0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6,0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43, + 0x39,0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94,0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f, + 0xbc,0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x0c,0xc7,0x69,0x87,0x70, + 0x58,0x87,0x72,0x70,0x83,0x74,0x68,0x07,0x78,0x60,0x87,0x74,0x18,0x87,0x74,0xa0, + 0x87,0x19,0xce,0x53,0x0f,0xee,0x00,0x0f,0xf2,0x50,0x0e,0xe4,0x90,0x0e,0xe3,0x40, + 0x0f,0xe1,0x20,0x0e,0xec,0x50,0x0e,0x33,0x20,0x28,0x1d,0xdc,0xc1,0x1e,0xc2,0x41, + 0x1e,0xd2,0x21,0x1c,0xdc,0x81,0x1e,0xdc,0xe0,0x1c,0xe4,0xe1,0x1d,0xea,0x01,0x1e, + 0x66,0x18,0x51,0x38,0xb0,0x43,0x3a,0x9c,0x83,0x3b,0xcc,0x50,0x24,0x76,0x60,0x07, + 0x7b,0x68,0x07,0x37,0x60,0x87,0x77,0x78,0x07,0x78,0x98,0x51,0x4c,0xf4,0x90,0x0f, + 0xf0,0x50,0x0e,0x33,0x1e,0x6a,0x1e,0xca,0x61,0x1c,0xe8,0x21,0x1d,0xde,0xc1,0x1d, + 0x7e,0x01,0x1e,0xe4,0xa1,0x1c,0xcc,0x21,0x1d,0xf0,0x61,0x06,0x54,0x85,0x83,0x38, + 0xcc,0xc3,0x3b,0xb0,0x43,0x3d,0xd0,0x43,0x39,0xfc,0xc2,0x3c,0xe4,0x43,0x3b,0x88, + 0xc3,0x3b,0xb0,0xc3,0x8c,0xc5,0x0a,0x87,0x79,0x98,0x87,0x77,0x18,0x87,0x74,0x08, + 0x07,0x7a,0x28,0x07,0x72,0x00,0x00,0x00,0x00,0x71,0x20,0x00,0x00,0x08,0x00,0x00, + 0x00,0x16,0xb0,0x01,0x48,0xe4,0x4b,0x00,0xf3,0x2c,0xc4,0x3f,0x11,0xd7,0x44,0x45, + 0xc4,0x6f,0x0f,0x7e,0x85,0x17,0xb7,0x6d,0x00,0x05,0x03,0x20,0x0d,0x0d,0x00,0x00, + 0x00,0x61,0x20,0x00,0x00,0x16,0x00,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00, + 0x00,0x0b,0x00,0x00,0x00,0x14,0xc7,0x22,0x80,0x40,0x20,0x88,0x8d,0x00,0x8c,0x25, + 0x00,0x01,0xa9,0x11,0x80,0x1a,0x20,0x31,0x03,0x40,0x61,0x0e,0xe2,0xba,0xae,0x6a, + 0x06,0x80,0xc0,0x0c,0xc0,0x08,0xc0,0x18,0x01,0x08,0x82,0x20,0xfe,0x01,0x00,0x00, + 0x00,0x83,0x0c,0x0f,0x91,0x8c,0x18,0x28,0x42,0x80,0x39,0x4d,0x80,0x2c,0xc9,0x30, + 0xc8,0x70,0x04,0x8d,0x05,0x91,0x7c,0x66,0x1b,0x94,0x00,0xc8,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _sfons_vs_bytecode_metal_ios[3344] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x10,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x3b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x00,0x48,0x41,0x53,0x48,0x20,0x00,0x58,0xff,0x12,0x36,0x80,0x88,0xb5, + 0xf7,0x1c,0xc3,0xd5,0x31,0x57,0x18,0x26,0x26,0xf0,0x8c,0xd4,0x15,0xa2,0x55,0x8b, + 0xd5,0x3c,0x6b,0x11,0xbd,0x17,0x49,0x4a,0x02,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0x45,0x4e,0x44,0x54,0x37,0x00,0x00, + 0x00,0x56,0x41,0x54,0x54,0x22,0x00,0x03,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x6e,0x00,0x00,0x80,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x00,0x01,0x80, + 0x63,0x6f,0x6c,0x6f,0x72,0x30,0x00,0x02,0x80,0x56,0x41,0x54,0x59,0x05,0x00,0x03, + 0x00,0x06,0x04,0x06,0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54, + 0xde,0xc0,0x17,0x0b,0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0xec,0x0b,0x00,0x00, + 0xff,0xff,0xff,0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0xf8,0x02,0x00,0x00, + 0x0b,0x82,0x20,0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91, + 0x41,0xc8,0x04,0x49,0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19, + 0x1e,0x04,0x8b,0x62,0x80,0x14,0x45,0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14, + 0x38,0x08,0x18,0x49,0x0a,0x32,0x44,0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80, + 0x0c,0x19,0x21,0x72,0x24,0x07,0xc8,0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0, + 0x01,0x00,0x00,0x00,0x51,0x18,0x00,0x00,0x82,0x00,0x00,0x00,0x1b,0xc8,0x25,0xf8, + 0xff,0xff,0xff,0xff,0x01,0x90,0x80,0x8a,0x18,0x87,0x77,0x90,0x07,0x79,0x28,0x87, + 0x71,0xa0,0x07,0x76,0xc8,0x87,0x36,0x90,0x87,0x77,0xa8,0x07,0x77,0x20,0x87,0x72, + 0x20,0x87,0x36,0x20,0x87,0x74,0xb0,0x87,0x74,0x20,0x87,0x72,0x68,0x83,0x79,0x88, + 0x07,0x79,0xa0,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07, + 0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1,0x1c,0x00,0x82,0x1c,0xd2,0x61,0x1e,0xc2,0x41, + 0x1c,0xd8,0xa1,0x1c,0xda,0x80,0x1e,0xc2,0x21,0x1d,0xd8,0xa1,0x0d,0xc6,0x21,0x1c, + 0xd8,0x81,0x1d,0xe6,0x01,0x30,0x87,0x70,0x60,0x87,0x79,0x28,0x07,0x80,0x60,0x87, + 0x72,0x98,0x87,0x79,0x68,0x03,0x78,0x90,0x87,0x72,0x18,0x87,0x74,0x98,0x87,0x72, + 0x68,0x03,0x73,0x80,0x87,0x76,0x08,0x07,0x72,0x00,0xcc,0x21,0x1c,0xd8,0x61,0x1e, + 0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0xc0,0x1c,0xe4,0x21,0x1c,0xda,0xa1,0x1c,0xda, + 0x00,0x1e,0xde,0x21,0x1d,0xdc,0x81,0x1e,0xca,0x41,0x1e,0xda,0xa0,0x1c,0xd8,0x21, + 0x1d,0xda,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x06,0x77,0x78,0x87,0x36,0x30, + 0x07,0x79,0x08,0x87,0x76,0x28,0x87,0x36,0x80,0x87,0x77,0x48,0x07,0x77,0xa0,0x87, + 0x72,0x90,0x87,0x36,0x28,0x07,0x76,0x48,0x87,0x76,0x68,0x03,0x77,0x78,0x07,0x77, + 0x68,0x03,0x76,0x28,0x87,0x70,0x30,0x07,0x80,0x70,0x87,0x77,0x68,0x83,0x74,0x70, + 0x07,0x73,0x98,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07, + 0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0x40,0x1d,0xea,0xa1, + 0x1d,0xe0,0xa1,0x0d,0xe8,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x1e,0x00,0x73,0x08, + 0x07,0x76,0x98,0x87,0x72,0x00,0x08,0x77,0x78,0x87,0x36,0x70,0x87,0x70,0x70,0x87, + 0x79,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41, + 0x1e,0xea,0xa1,0x1c,0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xe6,0x21,0x1d,0xce,0xc1,0x1d, + 0xca,0x81,0x1c,0xda,0x40,0x1f,0xca,0x41,0x1e,0xde,0x61,0x1e,0xda,0xc0,0x1c,0xe0, + 0xa1,0x0d,0xda,0x21,0x1c,0xe8,0x01,0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x80, + 0x70,0x87,0x77,0x68,0x03,0x7a,0x90,0x87,0x70,0x80,0x07,0x78,0x48,0x07,0x77,0x38, + 0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00, + 0x62,0x1e,0xe8,0x21,0x1c,0xc6,0x61,0x1d,0xda,0x00,0x1e,0xe4,0xe1,0x1d,0xe8,0xa1, + 0x1c,0xc6,0x81,0x1e,0xde,0x41,0x1e,0xda,0x40,0x1c,0xea,0xc1,0x1c,0xcc,0xa1,0x1c, + 0xe4,0xa1,0x0d,0xe6,0x21,0x1d,0xf4,0xa1,0x1c,0x00,0x3c,0x00,0x88,0x7a,0x70,0x87, + 0x79,0x08,0x07,0x73,0x28,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a, + 0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xea,0x61,0x1e,0xca,0xa1,0x0d, + 0xe6,0xe1,0x1d,0xcc,0x81,0x1e,0xda,0xc0,0x1c,0xd8,0xe1,0x1d,0xc2,0x81,0x1e,0x00, + 0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x36,0x20,0x02,0x01,0x24,0xc0,0x02,0x54, + 0x00,0x00,0x00,0x00,0x49,0x18,0x00,0x00,0x01,0x00,0x00,0x00,0x13,0x84,0x40,0x00, + 0x89,0x20,0x00,0x00,0x23,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85,0x04, + 0x93,0x22,0xa4,0x84,0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a,0x8c, + 0x0b,0x84,0xa4,0x4c,0x10,0x44,0x33,0x00,0xc3,0x08,0x04,0x70,0x90,0x34,0x45,0x94, + 0x30,0xf9,0x0c,0x80,0x34,0xf4,0xef,0x50,0x13,0x1a,0x42,0x08,0xc3,0x08,0x02,0x90, + 0x04,0x61,0x26,0x6a,0x1e,0xe8,0x41,0x1e,0xea,0x61,0x1c,0xe8,0xc1,0x0d,0xda,0xa1, + 0x1c,0xe8,0x21,0x1c,0xd8,0x41,0x0f,0xf4,0xa0,0x1d,0xc2,0x81,0x1e,0xe4,0x21,0x1d, + 0xf0,0x01,0x05,0xe4,0x20,0x69,0x8a,0x28,0x61,0xf2,0x2b,0xe9,0x7f,0x80,0x08,0x60, + 0x24,0x24,0x94,0x32,0x88,0x60,0x08,0xa5,0x10,0x61,0x84,0x43,0x68,0x20,0x60,0x8e, + 0x00,0x0c,0x52,0x60,0xcd,0x11,0x80,0xc2,0x20,0x42,0x20,0x0c,0x23,0x10,0xcb,0x08, + 0x00,0x00,0x00,0x00,0x13,0xa8,0x70,0x48,0x07,0x79,0xb0,0x03,0x3a,0x68,0x83,0x70, + 0x80,0x07,0x78,0x60,0x87,0x72,0x68,0x83,0x74,0x78,0x87,0x79,0xc8,0x03,0x37,0x80, + 0x03,0x37,0x80,0x83,0x0d,0xb7,0x51,0x0e,0x6d,0x00,0x0f,0x7a,0x60,0x07,0x74,0xa0, + 0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xe9,0x10,0x07,0x7a,0x80,0x07, + 0x7a,0x80,0x07,0x6d,0x90,0x0e,0x78,0xa0,0x07,0x78,0xa0,0x07,0x78,0xd0,0x06,0xe9, + 0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xd0,0x06,0xe9,0x30, + 0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xe9,0x60,0x07, + 0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xe6,0x30,0x07,0x72, + 0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xe6,0x60,0x07,0x74,0xa0, + 0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x10,0x07,0x76,0xa0,0x07, + 0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xd0,0x06,0xf6,0x20,0x07,0x74,0xa0,0x07,0x73, + 0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xf6,0x30,0x07,0x72,0xa0,0x07,0x73,0x20, + 0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xf6,0x40,0x07,0x78,0xa0,0x07,0x76,0x40,0x07, + 0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a, + 0x60,0x07,0x74,0xd0,0x06,0xf6,0x90,0x07,0x76,0xa0,0x07,0x71,0x20,0x07,0x78,0xa0, + 0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07, + 0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x6d,0x60,0x0f,0x71,0x90,0x07,0x72, + 0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xd0,0x06,0xf6,0x20, + 0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07, + 0x6d,0x60,0x0f,0x75,0x10,0x07,0x72,0xa0,0x07,0x75,0x10,0x07,0x72,0xa0,0x07,0x75, + 0x10,0x07,0x72,0xd0,0x06,0xf6,0x10,0x07,0x70,0x20,0x07,0x74,0xa0,0x07,0x71,0x00, + 0x07,0x72,0x40,0x07,0x7a,0x10,0x07,0x70,0x20,0x07,0x74,0xd0,0x06,0xee,0x80,0x07, + 0x7a,0x10,0x07,0x76,0xa0,0x07,0x73,0x20,0x07,0x43,0x98,0x04,0x00,0x80,0x00,0x00, + 0x00,0x00,0x00,0x80,0x2c,0x10,0x00,0x00,0x0b,0x00,0x00,0x00,0x32,0x1e,0x98,0x10, + 0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x5a,0x25,0x30,0x02,0x50, + 0x04,0x05,0x18,0x50,0x80,0x02,0x85,0x50,0x10,0x65,0x50,0x20,0xd4,0x46,0x00,0x88, + 0x8d,0x25,0x34,0x05,0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x19,0x01,0x00,0x00, + 0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37,0xb7, + 0x21,0xc6,0x32,0x28,0x00,0xb3,0x50,0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b,0xd3, + 0x2b,0x1b,0x62,0x2c,0x81,0x22,0x2c,0x06,0xd9,0x20,0x08,0x0e,0x8e,0xad,0x0c,0x84, + 0x89,0xc9,0xaa,0x09,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26,0x07,0x46, + 0xc6,0xc5,0xe6,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x26,0x07, + 0x46,0xc6,0xc5,0xe6,0x26,0x65,0x88,0xa0,0x10,0x43,0x8c,0x25,0x58,0x8e,0x45,0x60, + 0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x51,0x8e,0x25,0x58,0x84,0x45,0xe0,0x16,0x96, + 0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42,0x56,0xe6,0xf6,0x26,0xd7, + 0x36,0xf7,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x50,0x12,0x72,0x61,0x69, + 0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66,0x61,0x73,0x74,0x5f,0x6d, + 0x61,0x74,0x68,0x5f,0x65,0x6e,0x61,0x62,0x6c,0x65,0x43,0x04,0x65,0x21,0x19,0x84, + 0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9,0x98,0xc9,0x85,0xb5,0x95, + 0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d,0xa1,0x7d,0x95,0xb9,0x85, + 0x89,0xb1,0x95,0x0d,0x11,0x94,0x86,0x61,0x10,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06, + 0x97,0xc6,0x56,0xe6,0xe2,0x16,0x46,0x97,0x66,0x57,0xf6,0x45,0xf6,0x56,0x27,0xc6, + 0x56,0xf6,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x50,0x1e,0x92,0x41,0x58, + 0x9a,0x9c,0xcb,0xd8,0x5b,0x1b,0x5c,0x1a,0x5b,0x99,0x8b,0x5b,0x18,0x5d,0x9a,0x5d, + 0xd9,0x17,0xdb,0x9b,0xdb,0xd9,0x17,0xdb,0x9b,0xdb,0xd9,0x17,0x59,0xda,0x5c,0x98, + 0x18,0x5b,0xd9,0x10,0x41,0x89,0x78,0x06,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70, + 0x69,0x6c,0x65,0x2e,0x6e,0x61,0x74,0x69,0x76,0x65,0x5f,0x77,0x69,0x64,0x65,0x5f, + 0x76,0x65,0x63,0x74,0x6f,0x72,0x73,0x5f,0x64,0x69,0x73,0x61,0x62,0x6c,0x65,0x43, + 0x04,0x65,0x62,0x14,0x96,0x26,0xe7,0x62,0x57,0x26,0x47,0x57,0x86,0xf7,0xf5,0x56, + 0x47,0x07,0x57,0x47,0xc7,0xa5,0x6e,0xae,0x4c,0x0e,0x85,0xed,0x6d,0xcc,0x0d,0x26, + 0x85,0x51,0x58,0x9a,0x9c,0x4b,0x98,0xdc,0xd9,0x17,0x5d,0x1e,0x5c,0xd9,0x97,0x5b, + 0x58,0x5b,0x19,0x0d,0x33,0xb6,0xb7,0x30,0x3a,0x1a,0x32,0x61,0x69,0x72,0x2e,0x61, + 0x72,0x67,0x5f,0x6e,0x61,0x6d,0x65,0x14,0xea,0xec,0x86,0x30,0x4a,0xa5,0x58,0xca, + 0xa5,0x60,0x4a,0xa6,0x68,0x5c,0xea,0xe6,0xca,0xe4,0x50,0xd8,0xde,0xc6,0xdc,0x62, + 0x52,0x58,0x8c,0xbd,0xb1,0xbd,0xc9,0x0d,0x61,0x94,0x4a,0xe1,0x94,0x4b,0xc1,0x94, + 0x4c,0xe9,0xc8,0x84,0xa5,0xc9,0xb9,0xc0,0xbd,0xcd,0xa5,0xd1,0xa5,0xbd,0xb9,0x71, + 0x39,0x63,0xfb,0x82,0x7a,0x9b,0x4b,0xa3,0x4b,0x7b,0x73,0x1b,0xa2,0x28,0x9f,0x72, + 0x29,0x98,0x92,0x29,0x60,0x30,0xc4,0x50,0x36,0xc5,0x53,0xc2,0x80,0x50,0x58,0x9a, + 0x9c,0x8b,0x5d,0x99,0x1c,0x5d,0x19,0xde,0x57,0x9a,0x1b,0x5c,0x1d,0x1d,0xa5,0xb0, + 0x34,0x39,0x17,0xb6,0xb7,0xb1,0x30,0xba,0xb4,0x37,0xb7,0xaf,0x34,0x37,0xb2,0x32, + 0x3c,0x66,0x67,0x65,0x6e,0x65,0x72,0x61,0x74,0x65,0x64,0x28,0x38,0x70,0x6f,0x73, + 0x69,0x74,0x69,0x6f,0x6e,0x44,0x76,0x34,0x5f,0x66,0x29,0x44,0xe0,0xde,0xe6,0xd2, + 0xe8,0xd2,0xde,0xdc,0x86,0x50,0x8b,0xa0,0x8c,0x81,0x42,0x06,0x8b,0xb0,0x04,0x4a, + 0x19,0x28,0x97,0x82,0x29,0x99,0x62,0x06,0xd4,0xce,0xca,0xdc,0xca,0xe4,0xc2,0xe8, + 0xca,0xc8,0x50,0x72,0xe8,0xca,0xf0,0xc6,0xde,0xde,0xe4,0xc8,0x60,0x88,0xec,0x64, + 0xbe,0xcc,0x52,0x68,0x98,0xb1,0xbd,0x85,0xd1,0xc9,0x30,0xa1,0x2b,0xc3,0x1b,0x7b, + 0x7b,0x93,0x23,0x83,0x19,0x42,0x2d,0x81,0x32,0x06,0x0a,0x19,0x2c,0xc1,0x12,0x28, + 0x68,0xa0,0x5c,0x4a,0x1a,0x28,0x99,0xa2,0x06,0xbc,0xce,0xca,0xdc,0xca,0xe4,0xc2, + 0xe8,0xca,0xc8,0x50,0x6c,0xc6,0xde,0xd8,0xde,0xe4,0x60,0x88,0xec,0x68,0xbe,0xcc, + 0x52,0x68,0x8c,0xbd,0xb1,0xbd,0xc9,0xc1,0x0c,0xa1,0x96,0x41,0x19,0x03,0x85,0x0c, + 0x96,0x61,0x09,0x14,0x36,0x50,0x2e,0x05,0x53,0x32,0xa5,0x0d,0xa8,0x84,0xa5,0xc9, + 0xb9,0x88,0xd5,0x99,0x99,0x95,0xc9,0xf1,0x09,0x4b,0x93,0x73,0x11,0xab,0x33,0x33, + 0x2b,0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x23,0x12,0x96,0x26,0xe7,0x22,0x57,0x16,0x46, + 0x46,0x2a,0x2c,0x4d,0xce,0x65,0x8e,0x4e,0xae,0x6e,0x8c,0xee,0x8b,0x2e,0x0f,0xae, + 0xec,0x2b,0xcd,0xcd,0xec,0x8d,0x88,0x19,0xdb,0x5b,0x18,0x1d,0x0d,0x1e,0x0d,0x87, + 0x36,0x3b,0x38,0x0a,0x74,0x6d,0x43,0xa8,0x45,0x58,0x88,0x45,0x50,0xe6,0x40,0xa1, + 0x83,0x85,0x58,0x88,0x45,0x50,0xe6,0x40,0xa9,0x03,0x46,0x61,0x69,0x72,0x2e,0x61, + 0x72,0x67,0x5f,0x74,0x79,0x70,0x65,0x5f,0x73,0x69,0x7a,0x65,0xbc,0xc2,0xd2,0xe4, + 0x5c,0xc2,0xe4,0xce,0xbe,0xe8,0xf2,0xe0,0xca,0xbe,0xc2,0xd8,0xd2,0xce,0xdc,0xbe, + 0xe6,0xd2,0xf4,0xca,0x98,0xd8,0xcd,0x7d,0xc1,0x85,0xc9,0x85,0xb5,0xcd,0x71,0xf8, + 0x92,0x81,0x19,0x42,0x06,0x0b,0xa2,0xbc,0x81,0x02,0x07,0x4b,0xa1,0x90,0xc1,0x22, + 0x2c,0x81,0x12,0x07,0x8a,0x1c,0x28,0x76,0xa0,0xdc,0xc1,0x52,0x28,0x78,0xb0,0x24, + 0xca,0xa5,0xe4,0x81,0x92,0x29,0x7a,0x30,0x04,0x51,0xce,0x40,0x59,0x03,0xc5,0x0d, + 0x94,0x3d,0x18,0x62,0x24,0x80,0x22,0x06,0x0a,0x1f,0xf0,0x79,0x6b,0x73,0x4b,0x83, + 0x7b,0xa3,0x2b,0x73,0xa3,0x03,0x19,0x43,0x0b,0x93,0xe3,0x33,0x95,0xd6,0x06,0xc7, + 0x56,0x06,0x32,0xb4,0xb2,0x02,0x42,0x25,0x14,0x14,0x34,0x44,0x50,0xfe,0x60,0x88, + 0xa1,0xf8,0x81,0x02,0x0a,0x8d,0x32,0xc4,0x50,0x42,0x41,0x09,0x85,0x46,0x19,0x11, + 0xb1,0x03,0x3b,0xd8,0x43,0x3b,0xb8,0x41,0x3b,0xbc,0x03,0x39,0xd4,0x03,0x3b,0x94, + 0x83,0x1b,0x98,0x03,0x3b,0x84,0xc3,0x39,0xcc,0xc3,0x14,0x21,0x18,0x46,0x28,0xec, + 0xc0,0x0e,0xf6,0xd0,0x0e,0x6e,0x90,0x0e,0xe4,0x50,0x0e,0xee,0x40,0x0f,0x53,0x82, + 0x62,0xc4,0x12,0x0e,0xe9,0x20,0x0f,0x6e,0x60,0x0f,0xe5,0x20,0x0f,0xf3,0x90,0x0e, + 0xef,0xe0,0x0e,0x53,0x02,0x63,0x04,0x15,0x0e,0xe9,0x20,0x0f,0x6e,0xc0,0x0e,0xe1, + 0xe0,0x0e,0xe7,0x50,0x0f,0xe1,0x70,0x0e,0xe5,0xf0,0x0b,0xf6,0x50,0x0e,0xf2,0x30, + 0x0f,0xe9,0xf0,0x0e,0xee,0x30,0x25,0x40,0x46,0x4c,0xe1,0x90,0x0e,0xf2,0xe0,0x06, + 0xe3,0xf0,0x0e,0xed,0x00,0x0f,0xe9,0xc0,0x0e,0xe5,0xf0,0x0b,0xef,0x00,0x0f,0xf4, + 0x90,0x0e,0xef,0xe0,0x0e,0xf3,0x30,0xc5,0x50,0x18,0x07,0x92,0xa8,0x11,0x4a,0x38, + 0xa4,0x83,0x3c,0xb8,0x81,0x3d,0x94,0x83,0x3c,0xd0,0x43,0x39,0xe0,0xc3,0x94,0xa0, + 0x0f,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x6d,0x00,0x00,0x00,0x33,0x08,0x80,0x1c, + 0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3,0x8c,0x42,0x80,0x07, + 0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80,0x0e, + 0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88,0x43, + 0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78,0x8c, + 0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07,0x7a,0x70,0x03,0x76, + 0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f,0x6e, + 0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c,0xd8, + 0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39,0xb4, + 0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b,0x68, + 0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07, + 0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87,0x5f,0x08,0x87,0x71, + 0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e,0xf5, + 0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c,0xe4, + 0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6,0x90, + 0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94,0x43, + 0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03,0x3b, + 0xb0,0xc3,0x0c,0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70,0x83,0x74,0x68,0x07,0x78, + 0x60,0x87,0x74,0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53,0x0f,0xee,0x00,0x0f,0xf2, + 0x50,0x0e,0xe4,0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e,0xec,0x50,0x0e,0x33,0x20, + 0x28,0x1d,0xdc,0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c,0xdc,0x81,0x1e,0xdc,0xe0, + 0x1c,0xe4,0xe1,0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38,0xb0,0x43,0x3a,0x9c,0x83, + 0x3b,0xcc,0x50,0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x60,0x87,0x77,0x78,0x07, + 0x78,0x98,0x51,0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33,0x1e,0x6a,0x1e,0xca,0x61, + 0x1c,0xe8,0x21,0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4,0xa1,0x1c,0xcc,0x21,0x1d, + 0xf0,0x61,0x06,0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0,0x43,0x3d,0xd0,0x43,0x39, + 0xfc,0xc2,0x3c,0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3,0x8c,0xc5,0x0a,0x87,0x79, + 0x98,0x87,0x77,0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07,0x72,0x00,0x00,0x00,0x00, + 0x71,0x20,0x00,0x00,0x02,0x00,0x00,0x00,0x06,0x50,0x30,0x00,0xd2,0xd0,0x00,0x00, + 0x61,0x20,0x00,0x00,0x3d,0x00,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00, + 0x09,0x00,0x00,0x00,0xf4,0xc6,0x22,0x86,0x61,0x18,0xc6,0x22,0x04,0x41,0x10,0xc6, + 0x22,0x82,0x20,0x08,0xa8,0x95,0x40,0x19,0x14,0x01,0xbd,0x11,0x00,0x1a,0x33,0x00, + 0x24,0x66,0x00,0x28,0xcc,0x00,0x00,0x00,0xe3,0x15,0x0b,0x84,0x61,0x10,0x05,0x65, + 0x90,0x01,0x1a,0x0c,0x13,0x02,0xf9,0x8c,0x57,0x3c,0x14,0xc7,0x2d,0x14,0x94,0x41, + 0x06,0xea,0x70,0x4c,0x08,0xe4,0x63,0x41,0x01,0x9f,0xf1,0x0a,0x2a,0x0b,0x83,0x30, + 0x70,0x28,0x28,0x83,0x0c,0x19,0x43,0x99,0x10,0xc8,0xc7,0x8a,0x00,0x3e,0xe3,0x15, + 0x99,0x67,0x06,0x66,0x40,0x51,0x50,0x06,0x19,0xbc,0x48,0x33,0x21,0x90,0x8f,0x15, + 0x01,0x7c,0xc6,0x2b,0xbc,0x31,0x60,0x83,0x35,0x18,0x03,0x0a,0xca,0x20,0x83,0x18, + 0x60,0x99,0x09,0x81,0x7c,0xc6,0x2b,0xc4,0xe0,0x0c,0xe0,0xe0,0x0d,0x3c,0x0a,0xca, + 0x20,0x83,0x19,0x70,0x61,0x60,0x42,0x20,0x1f,0x0b,0x0a,0xf8,0x8c,0x57,0x9c,0x01, + 0x1b,0xd4,0x01,0x1d,0x88,0x01,0x05,0xc5,0x86,0x00,0x3e,0xb3,0x0d,0x61,0x10,0x00, + 0xb3,0x0d,0x01,0x1b,0x04,0xb3,0x0d,0xc1,0x23,0x64,0x10,0x10,0x03,0x00,0x00,0x00, + 0x09,0x00,0x00,0x00,0x5b,0x86,0x20,0x10,0x85,0x2d,0x43,0x11,0x88,0xc2,0x96,0x41, + 0x09,0x44,0x61,0xcb,0xf0,0x04,0xa2,0xb0,0x65,0xa0,0x02,0x51,0xd8,0x32,0x60,0x81, + 0x28,0x6c,0x19,0xba,0x40,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _sfons_fs_bytecode_metal_ios[2925] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd5,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x90,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0x1b,0x2c,0xa9,0x59,0x2d,0x7d,0xde, + 0xcd,0x5c,0x34,0x76,0x1e,0x63,0x4d,0x0c,0xdc,0xfc,0x74,0x70,0x26,0x76,0xe8,0xde, + 0xed,0xf7,0xc3,0xf7,0xe9,0x62,0x28,0xfa,0xd6,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0x45,0x4e,0x44,0x54,0x04,0x00,0x00, + 0x00,0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17, + 0x0b,0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x7c,0x0a,0x00,0x00,0xff,0xff,0xff, + 0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0x9c,0x02,0x00,0x00,0x0b,0x82,0x20, + 0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04, + 0x49,0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b, + 0x62,0x80,0x14,0x45,0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14,0x38,0x08,0x18, + 0x49,0x0a,0x32,0x44,0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21, + 0x72,0x24,0x07,0xc8,0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00, + 0x00,0x51,0x18,0x00,0x00,0x89,0x00,0x00,0x00,0x1b,0xcc,0x25,0xf8,0xff,0xff,0xff, + 0xff,0x01,0x60,0x00,0x09,0xa8,0x88,0x71,0x78,0x07,0x79,0x90,0x87,0x72,0x18,0x07, + 0x7a,0x60,0x87,0x7c,0x68,0x03,0x79,0x78,0x87,0x7a,0x70,0x07,0x72,0x28,0x07,0x72, + 0x68,0x03,0x72,0x48,0x07,0x7b,0x48,0x07,0x72,0x28,0x87,0x36,0x98,0x87,0x78,0x90, + 0x07,0x7a,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xcc, + 0x21,0x1c,0xd8,0x61,0x1e,0xca,0x01,0x20,0xc8,0x21,0x1d,0xe6,0x21,0x1c,0xc4,0x81, + 0x1d,0xca,0xa1,0x0d,0xe8,0x21,0x1c,0xd2,0x81,0x1d,0xda,0x60,0x1c,0xc2,0x81,0x1d, + 0xd8,0x61,0x1e,0x00,0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x08,0x76,0x28,0x87, + 0x79,0x98,0x87,0x36,0x80,0x07,0x79,0x28,0x87,0x71,0x48,0x87,0x79,0x28,0x87,0x36, + 0x30,0x07,0x78,0x68,0x87,0x70,0x20,0x07,0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1,0x1c, + 0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xcc,0x41,0x1e,0xc2,0xa1,0x1d,0xca,0xa1,0x0d,0xe0, + 0xe1,0x1d,0xd2,0xc1,0x1d,0xe8,0xa1,0x1c,0xe4,0xa1,0x0d,0xca,0x81,0x1d,0xd2,0xa1, + 0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x60,0x70,0x87,0x77,0x68,0x03,0x73,0x90, + 0x87,0x70,0x68,0x87,0x72,0x68,0x03,0x78,0x78,0x87,0x74,0x70,0x07,0x7a,0x28,0x07, + 0x79,0x68,0x83,0x72,0x60,0x87,0x74,0x68,0x87,0x36,0x70,0x87,0x77,0x70,0x87,0x36, + 0x60,0x87,0x72,0x08,0x07,0x73,0x00,0x08,0x77,0x78,0x87,0x36,0x48,0x07,0x77,0x30, + 0x87,0x79,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8, + 0x41,0x1e,0xea,0xa1,0x1c,0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xd4,0xa1,0x1e,0xda,0x01, + 0x1e,0xda,0x80,0x1e,0xc2,0x41,0x1c,0xd8,0xa1,0x1c,0xe6,0x01,0x30,0x87,0x70,0x60, + 0x87,0x79,0x28,0x07,0x80,0x70,0x87,0x77,0x68,0x03,0x77,0x08,0x07,0x77,0x98,0x87, + 0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1, + 0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0x60,0x1e,0xd2,0xe1,0x1c,0xdc,0xa1,0x1c, + 0xc8,0xa1,0x0d,0xf4,0xa1,0x1c,0xe4,0xe1,0x1d,0xe6,0xa1,0x0d,0xcc,0x01,0x1e,0xda, + 0xa0,0x1d,0xc2,0x81,0x1e,0xd0,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x08,0x77, + 0x78,0x87,0x36,0xa0,0x07,0x79,0x08,0x07,0x78,0x80,0x87,0x74,0x70,0x87,0x73,0x68, + 0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xe6, + 0x81,0x1e,0xc2,0x61,0x1c,0xd6,0xa1,0x0d,0xe0,0x41,0x1e,0xde,0x81,0x1e,0xca,0x61, + 0x1c,0xe8,0xe1,0x1d,0xe4,0xa1,0x0d,0xc4,0xa1,0x1e,0xcc,0xc1,0x1c,0xca,0x41,0x1e, + 0xda,0x60,0x1e,0xd2,0x41,0x1f,0xca,0x01,0xc0,0x03,0x80,0xa8,0x07,0x77,0x98,0x87, + 0x70,0x30,0x87,0x72,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74, + 0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0xa2,0x1e,0xe6,0xa1,0x1c,0xda,0x60,0x1e, + 0xde,0xc1,0x1c,0xe8,0xa1,0x0d,0xcc,0x81,0x1d,0xde,0x21,0x1c,0xe8,0x01,0x30,0x87, + 0x70,0x60,0x87,0x79,0x28,0x07,0x60,0x83,0x21,0x0c,0xc0,0x02,0x54,0x1b,0x8c,0x81, + 0x00,0x16,0xa0,0xda,0x80,0x10,0xff,0xff,0xff,0xff,0x3f,0x00,0x0c,0x20,0x01,0xd5, + 0x06,0xa3,0x08,0x80,0x05,0xa8,0x36,0x18,0x86,0x00,0x2c,0x40,0x05,0x49,0x18,0x00, + 0x00,0x03,0x00,0x00,0x00,0x13,0x86,0x40,0x18,0x26,0x0c,0x44,0x61,0x00,0x00,0x00, + 0x00,0x89,0x20,0x00,0x00,0x21,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85, + 0x04,0x93,0x22,0xa4,0x84,0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a, + 0x8c,0x0b,0x84,0xa4,0x4c,0x10,0x4c,0x33,0x00,0xc3,0x08,0x04,0x70,0x90,0x34,0x45, + 0x94,0x30,0xf9,0x0c,0x80,0x34,0xf4,0xef,0x50,0x13,0x0a,0xc2,0x51,0xd2,0x14,0x51, + 0xc2,0xe4,0xff,0x13,0x71,0x4d,0x54,0x44,0xfc,0xf6,0xf0,0x4f,0x63,0x04,0xc0,0x20, + 0xc2,0x10,0x5c,0x24,0x4d,0x11,0x25,0x4c,0xfe,0x2f,0x01,0xcc,0xb3,0x10,0xd1,0x3f, + 0x8d,0x11,0x00,0x83,0x08,0x85,0x50,0x0a,0x11,0x02,0x31,0x74,0x86,0x11,0x04,0x60, + 0x8e,0x20,0x98,0x23,0x00,0x83,0x61,0x04,0x61,0x29,0x48,0x20,0x26,0x29,0xa6,0x00, + 0xb5,0x81,0x80,0x14,0x58,0xc3,0x08,0xc4,0x32,0x02,0x00,0x00,0x00,0x13,0xa8,0x70, + 0x48,0x07,0x79,0xb0,0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60,0x87,0x72,0x68, + 0x83,0x74,0x78,0x87,0x79,0xc8,0x03,0x37,0x80,0x03,0x37,0x80,0x83,0x0d,0xb7,0x51, + 0x0e,0x6d,0x00,0x0f,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07, + 0x74,0xd0,0x06,0xe9,0x10,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07,0x6d,0x90,0x0e,0x78, + 0xa0,0x07,0x78,0xa0,0x07,0x78,0xd0,0x06,0xe9,0x10,0x07,0x76,0xa0,0x07,0x71,0x60, + 0x07,0x7a,0x10,0x07,0x76,0xd0,0x06,0xe9,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07, + 0x7a,0x30,0x07,0x72,0xd0,0x06,0xe9,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a, + 0x60,0x07,0x74,0xd0,0x06,0xe6,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30, + 0x07,0x72,0xd0,0x06,0xe6,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07, + 0x74,0xd0,0x06,0xf6,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x7a,0x10,0x07,0x76, + 0xd0,0x06,0xf6,0x20,0x07,0x74,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0, + 0x06,0xf6,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06, + 0xf6,0x40,0x07,0x78,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6, + 0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x90, + 0x07,0x76,0xa0,0x07,0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xd0,0x06, + 0xf6,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72, + 0x80,0x07,0x6d,0x60,0x0f,0x71,0x90,0x07,0x72,0xa0,0x07,0x72,0x50,0x07,0x76,0xa0, + 0x07,0x72,0x50,0x07,0x76,0xd0,0x06,0xf6,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07, + 0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f,0x75,0x10,0x07,0x72, + 0xa0,0x07,0x75,0x10,0x07,0x72,0xa0,0x07,0x75,0x10,0x07,0x72,0xd0,0x06,0xf6,0x10, + 0x07,0x70,0x20,0x07,0x74,0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x7a,0x10,0x07, + 0x70,0x20,0x07,0x74,0xd0,0x06,0xee,0x80,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x73, + 0x20,0x07,0x43,0x18,0x04,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x80,0x21,0x8c,0x03, + 0x04,0x80,0x00,0x00,0x00,0x00,0x00,0x40,0x16,0x08,0x00,0x00,0x00,0x08,0x00,0x00, + 0x00,0x32,0x1e,0x98,0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43, + 0x5a,0x23,0x00,0x25,0x50,0x04,0x85,0x50,0x10,0x65,0x40,0x70,0x2c,0xa1,0x29,0x00, + 0x00,0x79,0x18,0x00,0x00,0xd7,0x00,0x00,0x00,0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2, + 0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37,0xb7,0x21,0xc6,0x42,0x3c,0x00,0x84,0x50, + 0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62,0x2c,0xc3,0x23,0x2c, + 0x05,0xd9,0x20,0x08,0x0e,0x8e,0xad,0x0c,0x84,0x89,0xc9,0xaa,0x09,0xc4,0xae,0x4c, + 0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26,0x07,0x46,0xc6,0xc5,0xe6,0x06,0x04,0xa5,0xad, + 0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x26,0x07,0x46,0xc6,0xc5,0xe6,0x26,0x65,0x88, + 0xf0,0x10,0x43,0x8c,0x65,0x58,0x8c,0x45,0x60,0xd1,0x54,0x46,0x17,0xc6,0x36,0x04, + 0x79,0x8e,0x65,0x58,0x84,0x45,0xe0,0x16,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97, + 0xc6,0x56,0xe6,0x42,0x56,0xe6,0xf6,0x26,0xd7,0x36,0xf7,0x45,0x96,0x36,0x17,0x26, + 0xc6,0x56,0x36,0x44,0x78,0x12,0x72,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69, + 0x6c,0x65,0x2e,0x66,0x61,0x73,0x74,0x5f,0x6d,0x61,0x74,0x68,0x5f,0x65,0x6e,0x61, + 0x62,0x6c,0x65,0x43,0x84,0x67,0x21,0x19,0x84,0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1, + 0xa5,0xb1,0x95,0xb9,0x98,0xc9,0x85,0xb5,0x95,0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d, + 0x99,0x95,0xd1,0x8d,0xa1,0x7d,0x95,0xb9,0x85,0x89,0xb1,0x95,0x0d,0x11,0x9e,0x86, + 0x61,0x10,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0xe2,0x16,0x46, + 0x97,0x66,0x57,0xf6,0x45,0xf6,0x56,0x27,0xc6,0x56,0xf6,0x45,0x96,0x36,0x17,0x26, + 0xc6,0x56,0x36,0x44,0x78,0x1e,0x92,0x41,0x58,0x9a,0x9c,0xcb,0xd8,0x5b,0x1b,0x5c, + 0x1a,0x5b,0x99,0x8b,0x5b,0x18,0x5d,0x9a,0x5d,0xd9,0x17,0xdb,0x9b,0xdb,0xd9,0x17, + 0xdb,0x9b,0xdb,0xd9,0x17,0x59,0xda,0x5c,0x98,0x18,0x5b,0xd9,0x10,0xe1,0x89,0x78, + 0x06,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x6e,0x61,0x74, + 0x69,0x76,0x65,0x5f,0x77,0x69,0x64,0x65,0x5f,0x76,0x65,0x63,0x74,0x6f,0x72,0x73, + 0x5f,0x64,0x69,0x73,0x61,0x62,0x6c,0x65,0x43,0x84,0x67,0x62,0x14,0x96,0x26,0xe7, + 0x22,0x57,0xe6,0x46,0x56,0x26,0xf7,0x45,0x17,0x26,0x77,0x56,0x46,0xc7,0x28,0x2c, + 0x4d,0xce,0x25,0x4c,0xee,0xec,0x8b,0x2e,0x0f,0xae,0xec,0xcb,0x2d,0xac,0xad,0x8c, + 0x86,0x19,0xdb,0x5b,0x18,0x1d,0x0d,0x99,0xb0,0x34,0x39,0x97,0x30,0xb9,0xb3,0x2f, + 0xb7,0xb0,0xb6,0x32,0x2a,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x43, + 0x98,0xa7,0x5a,0x84,0xc7,0x7a,0xae,0x07,0x7b,0xb2,0x21,0xc2,0xa3,0x51,0x0a,0x4b, + 0x93,0x73,0x31,0x93,0x0b,0x3b,0x6b,0x2b,0x73,0xa3,0xfb,0x4a,0x73,0x83,0xab,0xa3, + 0xe3,0x52,0x37,0x57,0x26,0x87,0xc2,0xf6,0x36,0xe6,0x06,0x93,0x42,0x25,0x2c,0x4d, + 0xce,0x65,0xac,0xcc,0x8d,0xae,0x4c,0x8e,0x4f,0x58,0x9a,0x9c,0x0b,0x5c,0x99,0xdc, + 0x1c,0x5c,0xd9,0x18,0x5d,0x9a,0x5d,0x19,0x85,0x3a,0xbb,0x21,0xd2,0x22,0x3c,0xdc, + 0xd3,0x3d,0xde,0xf3,0x3d,0xd6,0x73,0x3d,0xd8,0x03,0x06,0x5c,0xea,0xe6,0xca,0xe4, + 0x50,0xd8,0xde,0xc6,0xdc,0x62,0x52,0x58,0x8c,0xbd,0xb1,0xbd,0xc9,0x0d,0x91,0x96, + 0xe1,0xe1,0x1e,0x31,0x78,0xbc,0xe7,0x7b,0xac,0xe7,0x7a,0xb0,0x67,0x0c,0xb8,0x84, + 0xa5,0xc9,0xb9,0xd0,0x95,0xe1,0xd1,0xd5,0xc9,0x95,0x51,0x0a,0x4b,0x93,0x73,0x61, + 0x7b,0x1b,0x0b,0xa3,0x4b,0x7b,0x73,0xfb,0x4a,0x73,0x23,0x2b,0xc3,0xa3,0x12,0x96, + 0x26,0xe7,0x32,0x17,0xd6,0x06,0xc7,0x56,0x46,0x8c,0xae,0x0c,0x8f,0xae,0x4e,0xae, + 0x4c,0x86,0x8c,0xc7,0x8c,0xed,0x2d,0x8c,0x8e,0x05,0x64,0x2e,0xac,0x0d,0x8e,0xad, + 0xcc,0x87,0x03,0x5d,0x19,0xde,0x10,0x6a,0x21,0x9e,0x32,0x78,0xcc,0x60,0x11,0x96, + 0xe1,0x39,0x83,0xc7,0x7a,0xd0,0xe0,0xc1,0x9e,0x34,0xe0,0x12,0x96,0x26,0xe7,0x32, + 0x17,0xd6,0x06,0xc7,0x56,0x26,0xc7,0x63,0x2e,0xac,0x0d,0x8e,0xad,0x4c,0x8e,0x08, + 0x5d,0x19,0xde,0x54,0x1b,0x1c,0x9b,0xdc,0x10,0x69,0x39,0x9e,0x35,0x78,0xcc,0x60, + 0x11,0x96,0xe1,0xb1,0x1e,0x36,0x78,0xb0,0xa7,0x0d,0x86,0x20,0x4f,0x18,0x3c,0x64, + 0xf0,0xa8,0xc1,0xe3,0x06,0x43,0x0c,0x04,0x78,0xb6,0xe7,0x0d,0x46,0x44,0xec,0xc0, + 0x0e,0xf6,0xd0,0x0e,0x6e,0xd0,0x0e,0xef,0x40,0x0e,0xf5,0xc0,0x0e,0xe5,0xe0,0x06, + 0xe6,0xc0,0x0e,0xe1,0x70,0x0e,0xf3,0x30,0x45,0x08,0x86,0x11,0x0a,0x3b,0xb0,0x83, + 0x3d,0xb4,0x83,0x1b,0xa4,0x03,0x39,0x94,0x83,0x3b,0xd0,0xc3,0x94,0xa0,0x18,0xb1, + 0x84,0x43,0x3a,0xc8,0x83,0x1b,0xd8,0x43,0x39,0xc8,0xc3,0x3c,0xa4,0xc3,0x3b,0xb8, + 0xc3,0x94,0xc0,0x18,0x41,0x85,0x43,0x3a,0xc8,0x83,0x1b,0xb0,0x43,0x38,0xb8,0xc3, + 0x39,0xd4,0x43,0x38,0x9c,0x43,0x39,0xfc,0x82,0x3d,0x94,0x83,0x3c,0xcc,0x43,0x3a, + 0xbc,0x83,0x3b,0x4c,0x09,0x90,0x11,0x53,0x38,0xa4,0x83,0x3c,0xb8,0xc1,0x38,0xbc, + 0x43,0x3b,0xc0,0x43,0x3a,0xb0,0x43,0x39,0xfc,0xc2,0x3b,0xc0,0x03,0x3d,0xa4,0xc3, + 0x3b,0xb8,0xc3,0x3c,0x4c,0x31,0x14,0xc6,0x81,0x24,0x6a,0x04,0x13,0x0e,0xe9,0x20, + 0x0f,0x6e,0x60,0x0e,0xf2,0x10,0x0e,0xe7,0xd0,0x0e,0xe5,0xe0,0x0e,0xf4,0x30,0x25, + 0x80,0x03,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x6d,0x00,0x00,0x00,0x33,0x08,0x80, + 0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3,0x8c,0x42,0x80, + 0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80, + 0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88, + 0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78, + 0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07,0x7a,0x70,0x03, + 0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f, + 0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c, + 0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39, + 0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b, + 0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60, + 0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87,0x5f,0x08,0x87, + 0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e, + 0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c, + 0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6, + 0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94, + 0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03, + 0x3b,0xb0,0xc3,0x0c,0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70,0x83,0x74,0x68,0x07, + 0x78,0x60,0x87,0x74,0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53,0x0f,0xee,0x00,0x0f, + 0xf2,0x50,0x0e,0xe4,0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e,0xec,0x50,0x0e,0x33, + 0x20,0x28,0x1d,0xdc,0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c,0xdc,0x81,0x1e,0xdc, + 0xe0,0x1c,0xe4,0xe1,0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38,0xb0,0x43,0x3a,0x9c, + 0x83,0x3b,0xcc,0x50,0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x60,0x87,0x77,0x78, + 0x07,0x78,0x98,0x51,0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33,0x1e,0x6a,0x1e,0xca, + 0x61,0x1c,0xe8,0x21,0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4,0xa1,0x1c,0xcc,0x21, + 0x1d,0xf0,0x61,0x06,0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0,0x43,0x3d,0xd0,0x43, + 0x39,0xfc,0xc2,0x3c,0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3,0x8c,0xc5,0x0a,0x87, + 0x79,0x98,0x87,0x77,0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07,0x72,0x00,0x00,0x00, + 0x00,0x71,0x20,0x00,0x00,0x08,0x00,0x00,0x00,0x16,0xb0,0x01,0x48,0xe4,0x4b,0x00, + 0xf3,0x2c,0xc4,0x3f,0x11,0xd7,0x44,0x45,0xc4,0x6f,0x0f,0x7e,0x85,0x17,0xb7,0x6d, + 0x00,0x05,0x03,0x20,0x0d,0x0d,0x00,0x00,0x00,0x61,0x20,0x00,0x00,0x16,0x00,0x00, + 0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x14,0xc7,0x22, + 0x80,0x40,0x20,0x88,0x8d,0x00,0x8c,0x25,0x00,0x01,0xa9,0x11,0x80,0x1a,0x20,0x31, + 0x03,0x40,0x61,0x0e,0xe2,0xba,0xae,0x6a,0x06,0x80,0xc0,0x0c,0xc0,0x08,0xc0,0x18, + 0x01,0x08,0x82,0x20,0xfe,0x01,0x00,0x00,0x00,0x83,0x0c,0x0f,0x91,0x8c,0x18,0x28, + 0x42,0x80,0x39,0x4d,0x80,0x2c,0xc9,0x30,0xc8,0x70,0x04,0x8d,0x05,0x91,0x7c,0x66, + 0x1b,0x94,0x00,0xc8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const char _sfons_vs_source_metal_sim[698] = { + 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, + 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, + 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, + 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, + 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x76, + 0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x34,0x78,0x34,0x20,0x6d,0x76,0x70,0x3b,0x0a,0x20,0x20,0x20, + 0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x78,0x34,0x20,0x74,0x6d,0x3b,0x0a,0x7d,0x3b, + 0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f, + 0x75,0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20, + 0x75,0x76,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x30,0x29, + 0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e, + 0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34, + 0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x5b,0x5b,0x70, + 0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73, + 0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x69,0x6e,0x0a,0x7b, + 0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x70,0x6f,0x73,0x69, + 0x74,0x69,0x6f,0x6e,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65, + 0x28,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74, + 0x32,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x20,0x5b,0x5b,0x61,0x74, + 0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28,0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20, + 0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x20, + 0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28,0x32,0x29,0x5d,0x5d, + 0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20,0x31,0x36,0x20,0x22, + 0x22,0x0a,0x76,0x65,0x72,0x74,0x65,0x78,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f, + 0x75,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x28,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x69, + 0x6e,0x20,0x69,0x6e,0x20,0x5b,0x5b,0x73,0x74,0x61,0x67,0x65,0x5f,0x69,0x6e,0x5d, + 0x5d,0x2c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x61,0x6e,0x74,0x20,0x76,0x73,0x5f,0x70, + 0x61,0x72,0x61,0x6d,0x73,0x26,0x20,0x5f,0x32,0x30,0x20,0x5b,0x5b,0x62,0x75,0x66, + 0x66,0x65,0x72,0x28,0x30,0x29,0x5d,0x5d,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20, + 0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20, + 0x7b,0x7d,0x3b,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20,0x31,0x36,0x20,0x22,0x22,0x0a, + 0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74, + 0x69,0x6f,0x6e,0x20,0x3d,0x20,0x5f,0x32,0x30,0x2e,0x6d,0x76,0x70,0x20,0x2a,0x20, + 0x69,0x6e,0x2e,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,0x0a,0x23,0x6c,0x69, + 0x6e,0x65,0x20,0x31,0x37,0x20,0x22,0x22,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74, + 0x2e,0x75,0x76,0x20,0x3d,0x20,0x5f,0x32,0x30,0x2e,0x74,0x6d,0x20,0x2a,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x34,0x28,0x69,0x6e,0x2e,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72, + 0x64,0x30,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x23, + 0x6c,0x69,0x6e,0x65,0x20,0x31,0x38,0x20,0x22,0x22,0x0a,0x20,0x20,0x20,0x20,0x6f, + 0x75,0x74,0x2e,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x69,0x6e,0x2e,0x63,0x6f, + 0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e, + 0x20,0x6f,0x75,0x74,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +static const char _sfons_fs_source_metal_sim[498] = { + 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, + 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, + 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, + 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, + 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d, + 0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72, + 0x20,0x5b,0x5b,0x63,0x6f,0x6c,0x6f,0x72,0x28,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x7d, + 0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f, + 0x69,0x6e,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20, + 0x75,0x76,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x30,0x29, + 0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e, + 0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20, + 0x31,0x31,0x20,0x22,0x22,0x0a,0x66,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x20,0x6d, + 0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x28,0x6d, + 0x61,0x69,0x6e,0x30,0x5f,0x69,0x6e,0x20,0x69,0x6e,0x20,0x5b,0x5b,0x73,0x74,0x61, + 0x67,0x65,0x5f,0x69,0x6e,0x5d,0x5d,0x2c,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65, + 0x32,0x64,0x3c,0x66,0x6c,0x6f,0x61,0x74,0x3e,0x20,0x74,0x65,0x78,0x20,0x5b,0x5b, + 0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x30,0x29,0x5d,0x5d,0x2c,0x20,0x73,0x61, + 0x6d,0x70,0x6c,0x65,0x72,0x20,0x74,0x65,0x78,0x53,0x6d,0x70,0x6c,0x72,0x20,0x5b, + 0x5b,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x28,0x30,0x29,0x5d,0x5d,0x29,0x0a,0x7b, + 0x0a,0x20,0x20,0x20,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6f, + 0x75,0x74,0x20,0x3d,0x20,0x7b,0x7d,0x3b,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20,0x31, + 0x31,0x20,0x22,0x22,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x66,0x72,0x61, + 0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34, + 0x28,0x31,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x2c,0x20, + 0x74,0x65,0x78,0x2e,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28,0x74,0x65,0x78,0x53,0x6d, + 0x70,0x6c,0x72,0x2c,0x20,0x69,0x6e,0x2e,0x75,0x76,0x2e,0x78,0x79,0x29,0x2e,0x78, + 0x29,0x20,0x2a,0x20,0x69,0x6e,0x2e,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x20,0x20, + 0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6f,0x75,0x74,0x3b,0x0a,0x7d,0x0a, + 0x0a,0x00, +}; +#elif defined(SOKOL_D3D11) +static const uint8_t _sfons_vs_bytecode_hlsl4[1008] = { + 0x44,0x58,0x42,0x43,0xf3,0xd9,0xf4,0x58,0x44,0xc2,0xb9,0x96,0x56,0x7c,0xb3,0x71, + 0x84,0xc5,0xde,0x61,0x01,0x00,0x00,0x00,0xf0,0x03,0x00,0x00,0x05,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0x14,0x01,0x00,0x00,0x78,0x01,0x00,0x00,0xe8,0x01,0x00,0x00, + 0x74,0x03,0x00,0x00,0x52,0x44,0x45,0x46,0xd8,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x48,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x04,0xfe,0xff, + 0x10,0x81,0x00,0x00,0xaf,0x00,0x00,0x00,0x3c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d, + 0x73,0x00,0xab,0xab,0x3c,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x60,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x90,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x98,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xa8,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x40,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x98,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x5f,0x32,0x30,0x5f, + 0x6d,0x76,0x70,0x00,0x02,0x00,0x03,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x5f,0x32,0x30,0x5f,0x74,0x6d,0x00,0x4d,0x69,0x63,0x72,0x6f, + 0x73,0x6f,0x66,0x74,0x20,0x28,0x52,0x29,0x20,0x48,0x4c,0x53,0x4c,0x20,0x53,0x68, + 0x61,0x64,0x65,0x72,0x20,0x43,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x72,0x20,0x31,0x30, + 0x2e,0x31,0x00,0xab,0x49,0x53,0x47,0x4e,0x5c,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x50,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x03,0x03,0x00,0x00,0x50,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x54,0x45,0x58,0x43, + 0x4f,0x4f,0x52,0x44,0x00,0xab,0xab,0xab,0x4f,0x53,0x47,0x4e,0x68,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x00, + 0x50,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x59,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0f,0x00,0x00,0x00, + 0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x00,0x53,0x56,0x5f,0x50,0x6f,0x73,0x69, + 0x74,0x69,0x6f,0x6e,0x00,0xab,0xab,0xab,0x53,0x48,0x44,0x52,0x84,0x01,0x00,0x00, + 0x40,0x00,0x01,0x00,0x61,0x00,0x00,0x00,0x59,0x00,0x00,0x04,0x46,0x8e,0x20,0x00, + 0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x5f,0x00,0x00,0x03,0xf2,0x10,0x10,0x00, + 0x00,0x00,0x00,0x00,0x5f,0x00,0x00,0x03,0x32,0x10,0x10,0x00,0x01,0x00,0x00,0x00, + 0x5f,0x00,0x00,0x03,0xf2,0x10,0x10,0x00,0x02,0x00,0x00,0x00,0x65,0x00,0x00,0x03, + 0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x65,0x00,0x00,0x03,0xf2,0x20,0x10,0x00, + 0x01,0x00,0x00,0x00,0x67,0x00,0x00,0x04,0xf2,0x20,0x10,0x00,0x02,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x68,0x00,0x00,0x02,0x01,0x00,0x00,0x00,0x38,0x00,0x00,0x08, + 0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x56,0x15,0x10,0x00,0x01,0x00,0x00,0x00, + 0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x32,0x00,0x00,0x0a, + 0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x06,0x10,0x10,0x00,0x01,0x00,0x00,0x00, + 0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x46,0x0e,0x10,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00, + 0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x36,0x00,0x00,0x05,0xf2,0x20,0x10,0x00,0x01,0x00,0x00,0x00, + 0x46,0x1e,0x10,0x00,0x02,0x00,0x00,0x00,0x38,0x00,0x00,0x08,0xf2,0x00,0x10,0x00, + 0x00,0x00,0x00,0x00,0x56,0x15,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x32,0x00,0x00,0x0a,0xf2,0x00,0x10,0x00, + 0x00,0x00,0x00,0x00,0x06,0x10,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00, + 0x32,0x00,0x00,0x0a,0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0xa6,0x1a,0x10,0x00, + 0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00,0x32,0x00,0x00,0x0a,0xf2,0x20,0x10,0x00, + 0x02,0x00,0x00,0x00,0xf6,0x1f,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00, + 0x3e,0x00,0x00,0x01,0x53,0x54,0x41,0x54,0x74,0x00,0x00,0x00,0x09,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + +}; +static const uint8_t _sfons_fs_bytecode_hlsl4[640] = { + 0x44,0x58,0x42,0x43,0x5e,0xbc,0x77,0xd9,0xc9,0xdf,0x7d,0x8e,0x0c,0x24,0x18,0x66, + 0x36,0xd3,0xf4,0x78,0x01,0x00,0x00,0x00,0x80,0x02,0x00,0x00,0x05,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0xd4,0x00,0x00,0x00,0x20,0x01,0x00,0x00,0x54,0x01,0x00,0x00, + 0x04,0x02,0x00,0x00,0x52,0x44,0x45,0x46,0x98,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x04,0xff,0xff, + 0x10,0x81,0x00,0x00,0x6d,0x00,0x00,0x00,0x5c,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x69,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x5f,0x74,0x65,0x78,0x5f,0x73,0x61,0x6d, + 0x70,0x6c,0x65,0x72,0x00,0x74,0x65,0x78,0x00,0x4d,0x69,0x63,0x72,0x6f,0x73,0x6f, + 0x66,0x74,0x20,0x28,0x52,0x29,0x20,0x48,0x4c,0x53,0x4c,0x20,0x53,0x68,0x61,0x64, + 0x65,0x72,0x20,0x43,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x72,0x20,0x31,0x30,0x2e,0x31, + 0x00,0xab,0xab,0xab,0x49,0x53,0x47,0x4e,0x44,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x03,0x00,0x00,0x38,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x0f,0x0f,0x00,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x00,0xab,0xab,0xab, + 0x4f,0x53,0x47,0x4e,0x2c,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x53,0x56,0x5f,0x54,0x61,0x72,0x67,0x65, + 0x74,0x00,0xab,0xab,0x53,0x48,0x44,0x52,0xa8,0x00,0x00,0x00,0x40,0x00,0x00,0x00, + 0x2a,0x00,0x00,0x00,0x5a,0x00,0x00,0x03,0x00,0x60,0x10,0x00,0x00,0x00,0x00,0x00, + 0x58,0x18,0x00,0x04,0x00,0x70,0x10,0x00,0x00,0x00,0x00,0x00,0x55,0x55,0x00,0x00, + 0x62,0x10,0x00,0x03,0x32,0x10,0x10,0x00,0x00,0x00,0x00,0x00,0x62,0x10,0x00,0x03, + 0xf2,0x10,0x10,0x00,0x01,0x00,0x00,0x00,0x65,0x00,0x00,0x03,0xf2,0x20,0x10,0x00, + 0x00,0x00,0x00,0x00,0x68,0x00,0x00,0x02,0x01,0x00,0x00,0x00,0x45,0x00,0x00,0x09, + 0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x10,0x10,0x00,0x00,0x00,0x00,0x00, + 0x96,0x73,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x10,0x00,0x00,0x00,0x00,0x00, + 0x36,0x00,0x00,0x05,0x12,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x01,0x40,0x00,0x00, + 0x00,0x00,0x80,0x3f,0x38,0x00,0x00,0x07,0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00, + 0x06,0x0c,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x1e,0x10,0x00,0x01,0x00,0x00,0x00, + 0x3e,0x00,0x00,0x01,0x53,0x54,0x41,0x54,0x74,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + +}; +#elif defined(SOKOL_WGPU) +/* + WebGPU shader blobs: + + Vertex shader source: + + #version 450 + + layout(set = 0, binding = 0, std140) uniform vs_params + { + mat4 mvp; + mat4 tm; + } _20; + + layout(location = 0) in vec4 position; + layout(location = 0) out vec4 uv; + layout(location = 1) in vec2 texcoord0; + layout(location = 1) out vec4 color; + layout(location = 2) in vec4 color0; + + void main() + { + gl_Position = _20.mvp * position; + uv = _20.tm * vec4(texcoord0, 0.0, 1.0); + color = color0; + } + + Fragment shader source: + + #version 450 + + layout(location = 0, set = 2, binding = 0) uniform sampler2D tex; + + layout(location = 0) out vec4 frag_color; + layout(location = 0) in vec4 uv; + layout(location = 1) in vec4 color; + + void main() + { + frag_color = vec4(1.0, 1.0, 1.0, texture(tex, uv.xy).x) * color; + } +*/ +static const uint8_t _sfons_vs_bytecode_wgpu[1932] = { + 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x08,0x00,0x08,0x00,0x32,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, + 0x02,0x00,0x00,0x00,0x47,0x4c,0x53,0x4c,0x2e,0x73,0x74,0x64,0x2e,0x34,0x35,0x30, + 0x00,0x00,0x00,0x00,0x0e,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x0f,0x00,0x0d,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, + 0x00,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x24,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x30,0x00,0x00,0x00, + 0x31,0x00,0x00,0x00,0x07,0x00,0x03,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x37,0x00,0x02,0x00,0x00,0x00,0xc2,0x01,0x00,0x00,0x01,0x00,0x00,0x00, + 0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65, + 0x73,0x73,0x65,0x64,0x20,0x63,0x6c,0x69,0x65,0x6e,0x74,0x20,0x76,0x75,0x6c,0x6b, + 0x61,0x6e,0x31,0x30,0x30,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c, + 0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x63,0x6c,0x69,0x65,0x6e, + 0x74,0x20,0x6f,0x70,0x65,0x6e,0x67,0x6c,0x31,0x30,0x30,0x0a,0x2f,0x2f,0x20,0x4f, + 0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64, + 0x20,0x74,0x61,0x72,0x67,0x65,0x74,0x2d,0x65,0x6e,0x76,0x20,0x76,0x75,0x6c,0x6b, + 0x61,0x6e,0x31,0x2e,0x30,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c, + 0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x74,0x61,0x72,0x67,0x65, + 0x74,0x2d,0x65,0x6e,0x76,0x20,0x6f,0x70,0x65,0x6e,0x67,0x6c,0x0a,0x2f,0x2f,0x20, + 0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65, + 0x64,0x20,0x65,0x6e,0x74,0x72,0x79,0x2d,0x70,0x6f,0x69,0x6e,0x74,0x20,0x6d,0x61, + 0x69,0x6e,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20,0x31,0x0a,0x00,0x05,0x00,0x04,0x00, + 0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e,0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00, + 0x0c,0x00,0x00,0x00,0x67,0x6c,0x5f,0x50,0x65,0x72,0x56,0x65,0x72,0x74,0x65,0x78, + 0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x06,0x00,0x07,0x00, + 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x67,0x6c,0x5f,0x50,0x6f,0x69,0x6e,0x74, + 0x53,0x69,0x7a,0x65,0x00,0x00,0x00,0x00,0x06,0x00,0x07,0x00,0x0c,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x67,0x6c,0x5f,0x43,0x6c,0x69,0x70,0x44,0x69,0x73,0x74,0x61, + 0x6e,0x63,0x65,0x00,0x06,0x00,0x07,0x00,0x0c,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x67,0x6c,0x5f,0x43,0x75,0x6c,0x6c,0x44,0x69,0x73,0x74,0x61,0x6e,0x63,0x65,0x00, + 0x05,0x00,0x03,0x00,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00, + 0x12,0x00,0x00,0x00,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x00,0x00,0x00, + 0x06,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0x76,0x70,0x00, + 0x06,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x74,0x6d,0x00,0x00, + 0x05,0x00,0x03,0x00,0x14,0x00,0x00,0x00,0x5f,0x32,0x30,0x00,0x05,0x00,0x05,0x00, + 0x19,0x00,0x00,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x00,0x00,0x00, + 0x05,0x00,0x03,0x00,0x1e,0x00,0x00,0x00,0x75,0x76,0x00,0x00,0x05,0x00,0x05,0x00, + 0x24,0x00,0x00,0x00,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x2c,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x2d,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x00,0x00, + 0x05,0x00,0x05,0x00,0x30,0x00,0x00,0x00,0x67,0x6c,0x5f,0x56,0x65,0x72,0x74,0x65, + 0x78,0x49,0x44,0x00,0x05,0x00,0x06,0x00,0x31,0x00,0x00,0x00,0x67,0x6c,0x5f,0x49, + 0x6e,0x73,0x74,0x61,0x6e,0x63,0x65,0x49,0x44,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x0b,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x47,0x00,0x03,0x00, + 0x0c,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x48,0x00,0x04,0x00,0x12,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x12,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00, + 0x48,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x05,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x12,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x23,0x00,0x00,0x00, + 0x40,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x12,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x47,0x00,0x03,0x00,0x12,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x22,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x19,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x1e,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x24,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x2c,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x2d,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x30,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x31,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x13,0x00,0x02,0x00,0x03,0x00,0x00,0x00,0x21,0x00,0x03,0x00, + 0x04,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x16,0x00,0x03,0x00,0x07,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x15,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x20,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x0a,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x1c,0x00,0x04,0x00,0x0b,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x0a,0x00,0x00,0x00,0x1e,0x00,0x06,0x00,0x0c,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x0d,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x0d,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x15,0x00,0x04,0x00, + 0x0f,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, + 0x0f,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x1e,0x00,0x04,0x00, + 0x12,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x13,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x13,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x15,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x18,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x18,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x1c,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x1c,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, + 0x0f,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x17,0x00,0x04,0x00, + 0x22,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x23,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x23,0x00,0x00,0x00,0x24,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x00,0x00,0x80,0x3f,0x3b,0x00,0x04,0x00, + 0x1c,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x18,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x2f,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x2f,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x2f,0x00,0x00,0x00,0x31,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x36,0x00,0x05,0x00, + 0x03,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0xf8,0x00,0x02,0x00,0x06,0x00,0x00,0x00,0x08,0x00,0x04,0x00,0x01,0x00,0x00,0x00, + 0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x15,0x00,0x00,0x00, + 0x16,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x08,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x91,0x00,0x05,0x00, + 0x08,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x1a,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x1c,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x0e,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x1d,0x00,0x00,0x00,0x1b,0x00,0x00,0x00, + 0x08,0x00,0x04,0x00,0x01,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x15,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x14,0x00,0x00,0x00, + 0x1f,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x22,0x00,0x00,0x00,0x25,0x00,0x00,0x00, + 0x24,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x28,0x00,0x00,0x00, + 0x25,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x07,0x00,0x00,0x00, + 0x29,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x50,0x00,0x07,0x00, + 0x08,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x29,0x00,0x00,0x00, + 0x26,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x91,0x00,0x05,0x00,0x08,0x00,0x00,0x00, + 0x2b,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x3e,0x00,0x03,0x00, + 0x1e,0x00,0x00,0x00,0x2b,0x00,0x00,0x00,0x08,0x00,0x04,0x00,0x01,0x00,0x00,0x00, + 0x13,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0x2e,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x2c,0x00,0x00,0x00, + 0x2e,0x00,0x00,0x00,0xfd,0x00,0x01,0x00,0x38,0x00,0x01,0x00, +}; +static const uint8_t fs_bytecode_wgpu[980] = { + 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x08,0x00,0x08,0x00,0x1e,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, + 0x02,0x00,0x00,0x00,0x47,0x4c,0x53,0x4c,0x2e,0x73,0x74,0x64,0x2e,0x34,0x35,0x30, + 0x00,0x00,0x00,0x00,0x0e,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x0f,0x00,0x08,0x00,0x04,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, + 0x00,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x1b,0x00,0x00,0x00, + 0x10,0x00,0x03,0x00,0x05,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x07,0x00,0x03,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x37,0x00,0x02,0x00,0x00,0x00, + 0xc2,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64, + 0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x63,0x6c,0x69, + 0x65,0x6e,0x74,0x20,0x76,0x75,0x6c,0x6b,0x61,0x6e,0x31,0x30,0x30,0x0a,0x2f,0x2f, + 0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73, + 0x65,0x64,0x20,0x63,0x6c,0x69,0x65,0x6e,0x74,0x20,0x6f,0x70,0x65,0x6e,0x67,0x6c, + 0x31,0x30,0x30,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50, + 0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x74,0x61,0x72,0x67,0x65,0x74,0x2d, + 0x65,0x6e,0x76,0x20,0x76,0x75,0x6c,0x6b,0x61,0x6e,0x31,0x2e,0x30,0x0a,0x2f,0x2f, + 0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73, + 0x65,0x64,0x20,0x74,0x61,0x72,0x67,0x65,0x74,0x2d,0x65,0x6e,0x76,0x20,0x6f,0x70, + 0x65,0x6e,0x67,0x6c,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65, + 0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x65,0x6e,0x74,0x72,0x79,0x2d, + 0x70,0x6f,0x69,0x6e,0x74,0x20,0x6d,0x61,0x69,0x6e,0x0a,0x23,0x6c,0x69,0x6e,0x65, + 0x20,0x31,0x0a,0x00,0x05,0x00,0x04,0x00,0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, + 0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x0a,0x00,0x00,0x00,0x66,0x72,0x61,0x67, + 0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x05,0x00,0x03,0x00,0x0f,0x00,0x00,0x00, + 0x74,0x65,0x78,0x00,0x05,0x00,0x03,0x00,0x12,0x00,0x00,0x00,0x75,0x76,0x00,0x00, + 0x05,0x00,0x04,0x00,0x1b,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x0a,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x0f,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x0f,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x0f,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x1b,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x13,0x00,0x02,0x00,0x03,0x00,0x00,0x00,0x21,0x00,0x03,0x00,0x04,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x16,0x00,0x03,0x00,0x07,0x00,0x00,0x00,0x20,0x00,0x00,0x00, + 0x17,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x2b,0x00,0x04,0x00,0x07,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x80,0x3f, + 0x19,0x00,0x09,0x00,0x0c,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x1b,0x00,0x03,0x00,0x0d,0x00,0x00,0x00,0x0c,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0d,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x17,0x00,0x04,0x00,0x13,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x36,0x00,0x05,0x00,0x03,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x06,0x00,0x00,0x00,0x08,0x00,0x04,0x00, + 0x01,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x0d,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x08,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x4f,0x00,0x07,0x00, + 0x13,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x14,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x57,0x00,0x05,0x00,0x08,0x00,0x00,0x00, + 0x16,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x51,0x00,0x05,0x00, + 0x07,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x50,0x00,0x07,0x00,0x08,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, + 0x0b,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x08,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x85,0x00,0x05,0x00, + 0x08,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x1c,0x00,0x00,0x00, + 0x3e,0x00,0x03,0x00,0x0a,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0xfd,0x00,0x01,0x00, + 0x38,0x00,0x01,0x00, +}; +#elif defined(SOKOL_DUMMY_BACKEND) +static const char* _sfons_vs_source_dummy = ""; +static const char* _sfons_fs_source_dummy = ""; +#else +#error "Please define one of SOKOL_GLCORE33, SOKOL_GLES2, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU or SOKOL_DUMMY_BACKEND!" +#endif + +typedef struct _sfons_t { + sg_shader shd; + sgl_pipeline pip; + sg_image img; + int width, height; + bool img_dirty; +} _sfons_t; + +static int _sfons_render_create(void* user_ptr, int width, int height) { + SOKOL_ASSERT(user_ptr && (width > 8) && (height > 8)); + _sfons_t* sfons = (_sfons_t*) user_ptr; + + /* sokol-gl compatible shader which treats RED channel as alpha */ + if (sfons->shd.id == SG_INVALID_ID) { + sg_shader_desc shd_desc; + memset(&shd_desc, 0, sizeof(shd_desc)); + shd_desc.attrs[0].name = "position"; + shd_desc.attrs[1].name = "texcoord0"; + shd_desc.attrs[2].name = "color0"; + shd_desc.attrs[0].sem_name = "TEXCOORD"; + shd_desc.attrs[0].sem_index = 0; + shd_desc.attrs[1].sem_name = "TEXCOORD"; + shd_desc.attrs[1].sem_index = 1; + shd_desc.attrs[2].sem_name = "TEXCOORD"; + shd_desc.attrs[2].sem_index = 2; + sg_shader_uniform_block_desc* ub = &shd_desc.vs.uniform_blocks[0]; + ub->size = 128; + ub->uniforms[0].name = "vs_params"; + ub->uniforms[0].type = SG_UNIFORMTYPE_FLOAT4; + ub->uniforms[0].array_count = 8; + shd_desc.fs.images[0].name = "tex"; + shd_desc.fs.images[0].image_type = SG_IMAGETYPE_2D; + shd_desc.fs.images[0].sampler_type = SG_SAMPLERTYPE_FLOAT; + shd_desc.label = "sokol-fontstash-shader"; + #if defined(SOKOL_GLCORE33) + shd_desc.vs.source = _sfons_vs_source_glsl330; + shd_desc.fs.source = _sfons_fs_source_glsl330; + #elif defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + shd_desc.vs.source = _sfons_vs_source_glsl100; + shd_desc.fs.source = _sfons_fs_source_glsl100; + #elif defined(SOKOL_METAL) + shd_desc.vs.entry = "main0"; + shd_desc.fs.entry = "main0"; + switch (sg_query_backend()) { + case SG_BACKEND_METAL_MACOS: + shd_desc.vs.bytecode = SG_RANGE(_sfons_vs_bytecode_metal_macos); + shd_desc.fs.bytecode = SG_RANGE(_sfons_fs_bytecode_metal_macos); + break; + case SG_BACKEND_METAL_IOS: + shd_desc.vs.bytecode = SG_RANGE(_sfons_vs_bytecode_metal_ios); + shd_desc.fs.bytecode = SG_RANGE(_sfons_fs_bytecode_metal_ios); + break; + default: + shd_desc.vs.source = _sfons_vs_source_metal_sim; + shd_desc.fs.source = _sfons_fs_source_metal_sim; + break; + } + #elif defined(SOKOL_D3D11) + shd_desc.vs.bytecode = SG_RANGE(_sfons_vs_bytecode_hlsl4); + shd_desc.fs.bytecode = SG_RANGE(_sfons_fs_bytecode_hlsl4); + #elif defined(SOKOL_WGPU) + shd_desc.vs.byte_code = _sfons_vs_bytecode_wgpu; + shd_desc.vs.byte_code_size = sizeof(_sfons_vs_bytecode_wgpu); + shd_desc.fs.byte_code = _sfons_fs_bytecode_wgpu; + shd_desc.fs.byte_code_size = sizeof(_sfons_fs_bytecode_wgpu); + #else + shd_desc.vs.source = _sfons_vs_src_dummy; + shd_desc.fs.source = _sfons_fs_src_dummy; + #endif + shd_desc.label = "sfons-shader"; + sfons->shd = sg_make_shader(&shd_desc); + } + + /* sokol-gl pipeline object */ + if (sfons->pip.id == SG_INVALID_ID) { + sg_pipeline_desc pip_desc; + memset(&pip_desc, 0, sizeof(pip_desc)); + pip_desc.shader = sfons->shd; + pip_desc.colors[0].blend.enabled = true; + pip_desc.colors[0].blend.src_factor_rgb = SG_BLENDFACTOR_SRC_ALPHA; + pip_desc.colors[0].blend.dst_factor_rgb = SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA; + sfons->pip = sgl_make_pipeline(&pip_desc); + } + + /* create or re-create font atlas texture */ + if (sfons->img.id != SG_INVALID_ID) { + sg_destroy_image(sfons->img); + sfons->img.id = SG_INVALID_ID; + } + sfons->width = width; + sfons->height = height; + + SOKOL_ASSERT(sfons->img.id == SG_INVALID_ID); + sg_image_desc img_desc; + memset(&img_desc, 0, sizeof(img_desc)); + img_desc.width = sfons->width; + img_desc.height = sfons->height; + img_desc.min_filter = SG_FILTER_LINEAR; + img_desc.mag_filter = SG_FILTER_LINEAR; + img_desc.usage = SG_USAGE_DYNAMIC; + img_desc.pixel_format = SG_PIXELFORMAT_R8; + sfons->img = sg_make_image(&img_desc); + return 1; +} + +static int _sfons_render_resize(void* user_ptr, int width, int height) { + return _sfons_render_create(user_ptr, width, height); +} + +static void _sfons_render_update(void* user_ptr, int* rect, const unsigned char* data) { + SOKOL_ASSERT(user_ptr && rect && data); + _SOKOL_UNUSED(rect); + _SOKOL_UNUSED(data); + _sfons_t* sfons = (_sfons_t*) user_ptr; + sfons->img_dirty = true; +} + +static void _sfons_render_draw(void* user_ptr, const float* verts, const float* tcoords, const unsigned int* colors, int nverts) { + SOKOL_ASSERT(user_ptr && verts && tcoords && colors && (nverts > 0)); + _sfons_t* sfons = (_sfons_t*) user_ptr; + sgl_enable_texture(); + sgl_texture(sfons->img); + sgl_push_pipeline(); + sgl_load_pipeline(sfons->pip); + sgl_begin_triangles(); + for (int i = 0; i < nverts; i++) { + sgl_v2f_t2f_c1i(verts[2*i+0], verts[2*i+1], tcoords[2*i+0], tcoords[2*i+1], colors[i]); + } + sgl_end(); + sgl_pop_pipeline(); + sgl_disable_texture(); +} + +static void _sfons_render_delete(void* user_ptr) { + SOKOL_ASSERT(user_ptr); + _sfons_t* sfons = (_sfons_t*) user_ptr; + if (sfons->img.id != SG_INVALID_ID) { + sg_destroy_image(sfons->img); + sfons->img.id = SG_INVALID_ID; + } + if (sfons->pip.id != SG_INVALID_ID) { + sgl_destroy_pipeline(sfons->pip); + sfons->pip.id = SG_INVALID_ID; + } + if (sfons->shd.id != SG_INVALID_ID) { + sg_destroy_shader(sfons->shd); + sfons->shd.id = SG_INVALID_ID; + } + SOKOL_FREE(sfons); +} + +// NOTE clang analyzer will report a potential memory leak for the call +// to SOKOL_MALLOC in the sfons_create() function, this is a false positive +// (the freeing happens in _sfons_render_delete()). The following macro +// silences the false positive when compilation happens with the analyzer active +#if __clang_analyzer__ +#define _SFONS_CLANG_ANALYZER_SILENCE_POTENTIAL_LEAK_FALSE_POSITIVE(x) SOKOL_FREE(x) +#else +#define _SFONS_CLANG_ANALYZER_SILENCE_POTENTIAL_LEAK_FALSE_POSITIVE(x) +#endif + +SOKOL_API_IMPL FONScontext* sfons_create(int width, int height, int flags) { + SOKOL_ASSERT((width > 0) && (height > 0)); + FONSparams params; + _sfons_t* sfons = (_sfons_t*) SOKOL_MALLOC(sizeof(_sfons_t)); + memset(sfons, 0, sizeof(_sfons_t)); + memset(¶ms, 0, sizeof(params)); + params.width = width; + params.height = height; + params.flags = (unsigned char) flags; + params.renderCreate = _sfons_render_create; + params.renderResize = _sfons_render_resize; + params.renderUpdate = _sfons_render_update; + params.renderDraw = _sfons_render_draw; + params.renderDelete = _sfons_render_delete; + params.userPtr = sfons; + FONScontext* ctx = fonsCreateInternal(¶ms); + _SFONS_CLANG_ANALYZER_SILENCE_POTENTIAL_LEAK_FALSE_POSITIVE(sfons); + return ctx; +} + +SOKOL_API_IMPL void sfons_destroy(FONScontext* ctx) { + SOKOL_ASSERT(ctx); + fonsDeleteInternal(ctx); +} + +SOKOL_API_IMPL void sfons_flush(FONScontext* ctx) { + SOKOL_ASSERT(ctx && ctx->params.userPtr); + _sfons_t* sfons = (_sfons_t*) ctx->params.userPtr; + if (sfons->img_dirty) { + sfons->img_dirty = false; + sg_image_data data; + memset(&data, 0, sizeof(data)); + data.subimage[0][0].ptr = ctx->texData; + data.subimage[0][0].size = (size_t) (sfons->width * sfons->height); + sg_update_image(sfons->img, &data); + } +} + +SOKOL_API_IMPL uint32_t sfons_rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + return ((uint32_t)r) | ((uint32_t)g<<8) | ((uint32_t)b<<16) | ((uint32_t)a<<24); +} + +#endif /* SOKOL_FONTSTASH_IMPL */ diff --git a/v_windows/v/old/thirdparty/sokol/util/sokol_gl.h b/v_windows/v/old/thirdparty/sokol/util/sokol_gl.h new file mode 100644 index 0000000..e41fc06 --- /dev/null +++ b/v_windows/v/old/thirdparty/sokol/util/sokol_gl.h @@ -0,0 +1,3297 @@ +#if defined(SOKOL_IMPL) && !defined(SOKOL_GL_IMPL) +#define SOKOL_GL_IMPL +#endif +#ifndef SOKOL_GL_INCLUDED +/* + sokol_gl.h -- OpenGL 1.x style rendering on top of sokol_gfx.h + + Project URL: https://github.com/floooh/sokol + + Do this: + #define SOKOL_IMPL or + #define SOKOL_GL_IMPL + before you include this file in *one* C or C++ file to create the + implementation. + + The following defines are used by the implementation to select the + platform-specific embedded shader code (these are the same defines as + used by sokol_gfx.h and sokol_app.h): + + SOKOL_GLCORE33 + SOKOL_GLES2 + SOKOL_GLES3 + SOKOL_D3D11 + SOKOL_METAL + SOKOL_WGPU + + ...optionally provide the following macros to override defaults: + + SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) + SOKOL_MALLOC(s) - your own malloc function (default: malloc(s)) + SOKOL_FREE(p) - your own free function (default: free(p)) + SOKOL_GL_API_DECL - public function declaration prefix (default: extern) + SOKOL_API_DECL - same as SOKOL_GL_API_DECL + SOKOL_API_IMPL - public function implementation prefix (default: -) + SOKOL_LOG(msg) - your own logging function (default: puts(msg)) + SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) + + If sokol_gl.h is compiled as a DLL, define the following before + including the declaration or implementation: + + SOKOL_DLL + + On Windows, SOKOL_DLL will define SOKOL_GL_API_DECL as __declspec(dllexport) + or __declspec(dllimport) as needed. + + Include the following headers before including sokol_gl.h: + + sokol_gfx.h + + Matrix functions have been taken from MESA and Regal. + + FEATURE OVERVIEW: + ================= + sokol_gl.h implements a subset of the OpenGLES 1.x feature set useful for + when you just want to quickly render a bunch of colored triangles or + lines without having to mess with buffers and + shaders. + + The current feature set is mostly useful for debug visualizations + and simple UI-style 2D rendering: + + What's implemented: + - vertex components: + - position (x, y, z) + - 2D texture coords (u, v) + - color (r, g, b, a) + - primitive types: + - triangle list and strip + - line list and strip + - quad list (TODO: quad strips) + - point list (TODO: point size) + - one texture layer (no multi-texturing) + - viewport and scissor-rect with selectable origin (top-left or bottom-left) + - all GL 1.x matrix stack functions, and additionally equivalent + functions for gluPerspective and gluLookat + + Notable GLES 1.x features that are *NOT* implemented: + - vertex lighting (this is the most likely GL feature that might be added later) + - vertex arrays (although providing whole chunks of vertex data at once + might be a useful feature for a later version) + - texture coordinate generation + - point size and line width + - all pixel store functions + - no ALPHA_TEST + - no clear functions (clearing is handled by the sokol-gfx render pass) + - fog + + Notable differences to GL: + - No "enum soup" for render states etc, instead there's a + 'pipeline stack', this is similar to GL's matrix stack, + but for pipeline-state-objects. The pipeline object at + the top of the pipeline stack defines the active set of render states + - All angles are in radians, not degrees (note the sgl_rad() and + sgl_deg() conversion functions) + - No enable/disable state for scissor test, this is always enabled + + STEP BY STEP: + ============= + --- To initialize sokol-gl, call: + + sgl_setup(const sgl_desc_t* desc) + + NOTE that sgl_setup() must be called *after* initializing sokol-gfx + (via sg_setup). This is because sgl_setup() needs to create + sokol-gfx resource objects. + + sgl_setup() needs to know the attributes of the sokol-gfx render pass + where sokol-gl rendering will happen through the passed-in sgl_desc_t + struct: + + sg_pixel_format color_format - color pixel format of render pass + sg_pixel_format depth_format - depth pixel format of render pass + int sample_count - MSAA sample count of render pass + + These values have the same defaults as sokol_gfx.h and sokol_app.h, + to use the default values, leave them zero-initialized. + + You can adjust the maximum number of vertices and drawing commands + per frame through the members: + + int max_vertices - default is 65536 + int max_commands - default is 16384 + + You can adjust the size of the internal pipeline state object pool + with: + + int pipeline_pool_size - default is 64 + + Finally you can change the face winding for front-facing triangles + and quads: + + sg_face_winding face_winding - default is SG_FACEWINDING_CCW + + The default winding for front faces is counter-clock-wise. This is + the same as OpenGL's default, but different from sokol-gfx. + + --- Optionally create pipeline-state-objects if you need render state + that differs from sokol-gl's default state: + + sgl_pipeline pip = sgl_make_pipeline(const sg_pipeline_desc* desc) + + The similarity with sokol_gfx.h's sg_pipeline type and sg_make_pipeline() + function is intended. sgl_make_pipeline() also takes a standard + sokol-gfx sg_pipeline_desc object to describe the render state, but + without: + - shader + - vertex layout + - color- and depth-pixel-formats + - primitive type (lines, triangles, ...) + - MSAA sample count + Those will be filled in by sgl_make_pipeline(). Note that each + call to sgl_make_pipeline() needs to create several sokol-gfx + pipeline objects (one for each primitive type). + + --- if you need to destroy sgl_pipeline objects before sgl_shutdown(): + + sgl_destroy_pipeline(sgl_pipeline pip) + + --- After sgl_setup() you can call any of the sokol-gl functions anywhere + in a frame, *except* sgl_draw(). The 'vanilla' functions + will only change internal sokol-gl state, and not call any sokol-gfx + functions. + + --- Unlike OpenGL, sokol-gl has a function to reset internal state to + a known default. This is useful at the start of a sequence of + rendering operations: + + void sgl_defaults(void) + + This will set the following default state: + + - current texture coordinate to u=0.0f, v=0.0f + - current color to white (rgba all 1.0f) + - unbind the current texture and texturing will be disabled + - *all* matrices will be set to identity (also the projection matrix) + - the default render state will be set by loading the 'default pipeline' + into the top of the pipeline stack + + The current matrix- and pipeline-stack-depths will not be changed by + sgl_defaults(). + + --- change the currently active renderstate through the + pipeline-stack functions, this works similar to the + traditional GL matrix stack: + + ...load the default pipeline state on the top of the pipeline stack: + + sgl_default_pipeline() + + ...load a specific pipeline on the top of the pipeline stack: + + sgl_load_pipeline(sgl_pipeline pip) + + ...push and pop the pipeline stack: + sgl_push_pipeline() + sgl_pop_pipeline() + + --- control texturing with: + + sgl_enable_texture() + sgl_disable_texture() + sgl_texture(sg_image img) + + --- set the current viewport and scissor rect with: + + sgl_viewport(int x, int y, int w, int h, bool origin_top_left) + sgl_scissor_rect(int x, int y, int w, int h, bool origin_top_left) + + ...or call these alternatives which take float arguments (this might allow + to avoid casting between float and integer in more strongly typed languages + when floating point pixel coordinates are used): + + sgl_viewportf(float x, float y, float w, float h, bool origin_top_left) + sgl_scissor_rectf(float x, float y, float w, float h, bool origin_top_left) + + ...these calls add a new command to the internal command queue, so + that the viewport or scissor rect are set at the right time relative + to other sokol-gl calls. + + --- adjust the transform matrices, matrix manipulation works just like + the OpenGL matrix stack: + + ...set the current matrix mode: + + sgl_matrix_mode_modelview() + sgl_matrix_mode_projection() + sgl_matrix_mode_texture() + + ...load the identity matrix into the current matrix: + + sgl_load_identity() + + ...translate, rotate and scale the current matrix: + + sgl_translate(float x, float y, float z) + sgl_rotate(float angle_rad, float x, float y, float z) + sgl_scale(float x, float y, float z) + + NOTE that all angles in sokol-gl are in radians, not in degree. + Convert between radians and degree with the helper functions: + + float sgl_rad(float deg) - degrees to radians + float sgl_deg(float rad) - radians to degrees + + ...directly load the current matrix from a float[16] array: + + sgl_load_matrix(const float m[16]) + sgl_load_transpose_matrix(const float m[16]) + + ...directly multiply the current matrix from a float[16] array: + + sgl_mult_matrix(const float m[16]) + sgl_mult_transpose_matrix(const float m[16]) + + The memory layout of those float[16] arrays is the same as in OpenGL. + + ...more matrix functions: + + sgl_frustum(float left, float right, float bottom, float top, float near, float far) + sgl_ortho(float left, float right, float bottom, float top, float near, float far) + sgl_perspective(float fov_y, float aspect, float near, float far) + sgl_lookat(float eye_x, float eye_y, float eye_z, float center_x, float center_y, float center_z, float up_x, float up_y, float up_z) + + These functions work the same as glFrustum(), glOrtho(), gluPerspective() + and gluLookAt(). + + ...and finally to push / pop the current matrix stack: + + sgl_push_matrix(void) + sgl_pop_matrix(void) + + Again, these work the same as glPushMatrix() and glPopMatrix(). + + --- perform primitive rendering: + + ...set the current texture coordinate and color 'registers' with: + + sgl_t2f(float u, float v) - set current texture coordinate + sgl_c*(...) - set current color + + There are several functions for setting the color (as float values, + unsigned byte values, packed as unsigned 32-bit integer, with + and without alpha). + + NOTE that these are the only functions that can be called both inside + sgl_begin_*() / sgl_end() and outside. + + ...start a primitive vertex sequence with: + + sgl_begin_points() + sgl_begin_lines() + sgl_begin_line_strip() + sgl_begin_triangles() + sgl_begin_triangle_strip() + sgl_begin_quads() + + ...after sgl_begin_*() specify vertices: + + sgl_v*(...) + sgl_v*_t*(...) + sgl_v*_c*(...) + sgl_v*_t*_c*(...) + + These functions write a new vertex to sokol-gl's internal vertex buffer, + optionally with texture-coords and color. If the texture coordinate + and/or color is missing, it will be taken from the current texture-coord + and color 'register'. + + ...finally, after specifying vertices, call: + + sgl_end() + + This will record a new draw command in sokol-gl's internal command + list, or it will extend the previous draw command if no relevant + state has changed since the last sgl_begin/end pair. + + --- inside a sokol-gfx rendering pass, call: + + sgl_draw() + + This will render everything that has been recorded since the last + call to sgl_draw() through sokol-gfx, and will 'rewind' the internal + vertex-, uniform- and command-buffers. + + --- sokol-gl tracks a single internal error code which can be + queried with + + sgl_error_t sgl_error(void) + + ...which can return the following error codes: + + SGL_NO_ERROR - all OK, no error occurred since last sgl_draw() + SGL_ERROR_VERTICES_FULL - internal vertex buffer is full (checked in sgl_end()) + SGL_ERROR_UNIFORMS_FULL - the internal uniforms buffer is full (checked in sgl_end()) + SGL_ERROR_COMMANDS_FULL - the internal command buffer is full (checked in sgl_end()) + SGL_ERROR_STACK_OVERFLOW - matrix- or pipeline-stack overflow + SGL_ERROR_STACK_UNDERFLOW - matrix- or pipeline-stack underflow + + ...if sokol-gl is in an error-state, sgl_draw() will skip any rendering, + and reset the error code to SGL_NO_ERROR. + + UNDER THE HOOD: + =============== + sokol_gl.h works by recording vertex data and rendering commands into + memory buffers, and then drawing the recorded commands via sokol_gfx.h + + The only functions which call into sokol_gfx.h are: + - sgl_setup() + - sgl_shutdown() + - sgl_draw() + + sgl_setup() must be called after initializing sokol-gfx. + sgl_shutdown() must be called before shutting down sokol-gfx. + sgl_draw() must be called once per frame inside a sokol-gfx render pass. + + All other sokol-gl function can be called anywhere in a frame, since + they just record data into memory buffers owned by sokol-gl. + + What happens in: + + sgl_setup(): + - 3 memory buffers are allocated, one for vertex data, + one for uniform data, and one for commands + - sokol-gfx resources are created: a (dynamic) vertex buffer, + a shader object (using embedded shader source or byte code), + and an 8x8 all-white default texture + + One vertex is 24 bytes: + - float3 position + - float2 texture coords + - uint32_t color + + One uniform block is 128 bytes: + - mat4 model-view-projection matrix + - mat4 texture matrix + + One draw command is ca. 24 bytes for the actual + command code plus command arguments. + + Each sgl_end() consumes one command, and one uniform block + (only when the matrices have changed). + The required size for one sgl_begin/end pair is (at most): + + (152 + 24 * num_verts) bytes + + sgl_shutdown(): + - all sokol-gfx resources (buffer, shader, default-texture and + all pipeline objects) are destroyed + - the 3 memory buffers are freed + + sgl_draw(): + - copy all recorded vertex data into the dynamic sokol-gfx buffer + via a call to sg_update_buffer() + - for each recorded command: + - if it's a viewport command, call sg_apply_viewport() + - if it's a scissor-rect command, call sg_apply_scissor_rect() + - if it's a draw command: + - depending on what has changed since the last draw command, + call sg_apply_pipeline(), sg_apply_bindings() and + sg_apply_uniforms() + - finally call sg_draw() + + All other functions only modify the internally tracked state, add + data to the vertex, uniform and command buffers, or manipulate + the matrix stack. + + ON DRAW COMMAND MERGING + ======================= + Not every call to sgl_end() will automatically record a new draw command. + If possible, the previous draw command will simply be extended, + resulting in fewer actual draw calls later in sgl_draw(). + + A draw command will be merged with the previous command if "no relevant + state has changed" since the last sgl_end(), meaning: + + - no calls to sgl_apply_viewport() and sgl_apply_scissor_rect() + - the primitive type hasn't changed + - the primitive type isn't a 'strip type' (no line or triangle strip) + - the pipeline state object hasn't changed + - none of the matrices has changed + - none of the texture state has changed + + Merging a draw command simply means that the number of vertices + to render in the previous draw command will be incremented by the + number of vertices in the new draw command. + + LICENSE + ======= + zlib/libpng license + + Copyright (c) 2018 Andre Weissflog + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the + use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software in a + product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + + 3. This notice may not be removed or altered from any source + distribution. +*/ +#define SOKOL_GL_INCLUDED (1) +#include +#include + +#if !defined(SOKOL_GFX_INCLUDED) +#error "Please include sokol_gfx.h before sokol_gl.h" +#endif + +#if defined(SOKOL_API_DECL) && !defined(SOKOL_GL_API_DECL) +#define SOKOL_GL_API_DECL SOKOL_API_DECL +#endif +#ifndef SOKOL_GL_API_DECL +#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_GL_IMPL) +#define SOKOL_GL_API_DECL __declspec(dllexport) +#elif defined(_WIN32) && defined(SOKOL_DLL) +#define SOKOL_GL_API_DECL __declspec(dllimport) +#else +#define SOKOL_GL_API_DECL extern +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* sokol_gl pipeline handle (created with sgl_make_pipeline()) */ +typedef struct sgl_pipeline { uint32_t id; } sgl_pipeline; + +/* + sgl_error_t + + Errors are reset each frame after calling sgl_draw(), + get the last error code with sgl_error() +*/ +typedef enum sgl_error_t { + SGL_NO_ERROR = 0, + SGL_ERROR_VERTICES_FULL, + SGL_ERROR_UNIFORMS_FULL, + SGL_ERROR_COMMANDS_FULL, + SGL_ERROR_STACK_OVERFLOW, + SGL_ERROR_STACK_UNDERFLOW, +} sgl_error_t; + +typedef struct sgl_desc_t { + int max_vertices; /* size for vertex buffer */ + int max_commands; /* size of uniform- and command-buffers */ + int pipeline_pool_size; /* size of the internal pipeline pool, default is 64 */ + sg_pixel_format color_format; + sg_pixel_format depth_format; + int sample_count; + sg_face_winding face_winding; /* default front face winding is CCW */ +} sgl_desc_t; + +/* setup/shutdown/misc */ +SOKOL_GL_API_DECL void sgl_setup(const sgl_desc_t* desc); +SOKOL_GL_API_DECL void sgl_shutdown(void); +SOKOL_GL_API_DECL sgl_error_t sgl_error(void); +SOKOL_GL_API_DECL void sgl_defaults(void); +SOKOL_GL_API_DECL float sgl_rad(float deg); +SOKOL_GL_API_DECL float sgl_deg(float rad); + +/* create and destroy pipeline objects */ +SOKOL_GL_API_DECL sgl_pipeline sgl_make_pipeline(const sg_pipeline_desc* desc); +SOKOL_GL_API_DECL void sgl_destroy_pipeline(sgl_pipeline pip); + +/* render state functions */ +SOKOL_GL_API_DECL void sgl_viewport(int x, int y, int w, int h, bool origin_top_left); +SOKOL_GL_API_DECL void sgl_viewportf(float x, float y, float w, float h, bool origin_top_left); +SOKOL_GL_API_DECL void sgl_scissor_rect(int x, int y, int w, int h, bool origin_top_left); +SOKOL_GL_API_DECL void sgl_scissor_rectf(float x, float y, float w, float h, bool origin_top_left); +SOKOL_GL_API_DECL void sgl_enable_texture(void); +SOKOL_GL_API_DECL void sgl_disable_texture(void); +SOKOL_GL_API_DECL void sgl_texture(sg_image img); + +/* pipeline stack functions */ +SOKOL_GL_API_DECL void sgl_default_pipeline(void); +SOKOL_GL_API_DECL void sgl_load_pipeline(sgl_pipeline pip); +SOKOL_GL_API_DECL void sgl_push_pipeline(void); +SOKOL_GL_API_DECL void sgl_pop_pipeline(void); + +/* matrix stack functions */ +SOKOL_GL_API_DECL void sgl_matrix_mode_modelview(void); +SOKOL_GL_API_DECL void sgl_matrix_mode_projection(void); +SOKOL_GL_API_DECL void sgl_matrix_mode_texture(void); +SOKOL_GL_API_DECL void sgl_load_identity(void); +SOKOL_GL_API_DECL void sgl_load_matrix(const float m[16]); +SOKOL_GL_API_DECL void sgl_load_transpose_matrix(const float m[16]); +SOKOL_GL_API_DECL void sgl_mult_matrix(const float m[16]); +SOKOL_GL_API_DECL void sgl_mult_transpose_matrix(const float m[16]); +SOKOL_GL_API_DECL void sgl_rotate(float angle_rad, float x, float y, float z); +SOKOL_GL_API_DECL void sgl_scale(float x, float y, float z); +SOKOL_GL_API_DECL void sgl_translate(float x, float y, float z); +SOKOL_GL_API_DECL void sgl_frustum(float l, float r, float b, float t, float n, float f); +SOKOL_GL_API_DECL void sgl_ortho(float l, float r, float b, float t, float n, float f); +SOKOL_GL_API_DECL void sgl_perspective(float fov_y, float aspect, float z_near, float z_far); +SOKOL_GL_API_DECL void sgl_lookat(float eye_x, float eye_y, float eye_z, float center_x, float center_y, float center_z, float up_x, float up_y, float up_z); +SOKOL_GL_API_DECL void sgl_push_matrix(void); +SOKOL_GL_API_DECL void sgl_pop_matrix(void); + +/* these functions only set the internal 'current texcoord / color' (valid inside or outside begin/end) */ +SOKOL_GL_API_DECL void sgl_t2f(float u, float v); +SOKOL_GL_API_DECL void sgl_c3f(float r, float g, float b); +SOKOL_GL_API_DECL void sgl_c4f(float r, float g, float b, float a); +SOKOL_GL_API_DECL void sgl_c3b(uint8_t r, uint8_t g, uint8_t b); +SOKOL_GL_API_DECL void sgl_c4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a); +SOKOL_GL_API_DECL void sgl_c1i(uint32_t rgba); + +/* define primitives, each begin/end is one draw command */ +SOKOL_GL_API_DECL void sgl_begin_points(void); +SOKOL_GL_API_DECL void sgl_begin_lines(void); +SOKOL_GL_API_DECL void sgl_begin_line_strip(void); +SOKOL_GL_API_DECL void sgl_begin_triangles(void); +SOKOL_GL_API_DECL void sgl_begin_triangle_strip(void); +SOKOL_GL_API_DECL void sgl_begin_quads(void); +SOKOL_GL_API_DECL void sgl_v2f(float x, float y); +SOKOL_GL_API_DECL void sgl_v3f(float x, float y, float z); +SOKOL_GL_API_DECL void sgl_v2f_t2f(float x, float y, float u, float v); +SOKOL_GL_API_DECL void sgl_v3f_t2f(float x, float y, float z, float u, float v); +SOKOL_GL_API_DECL void sgl_v2f_c3f(float x, float y, float r, float g, float b); +SOKOL_GL_API_DECL void sgl_v2f_c3b(float x, float y, uint8_t r, uint8_t g, uint8_t b); +SOKOL_GL_API_DECL void sgl_v2f_c4f(float x, float y, float r, float g, float b, float a); +SOKOL_GL_API_DECL void sgl_v2f_c4b(float x, float y, uint8_t r, uint8_t g, uint8_t b, uint8_t a); +SOKOL_GL_API_DECL void sgl_v2f_c1i(float x, float y, uint32_t rgba); +SOKOL_GL_API_DECL void sgl_v3f_c3f(float x, float y, float z, float r, float g, float b); +SOKOL_GL_API_DECL void sgl_v3f_c3b(float x, float y, float z, uint8_t r, uint8_t g, uint8_t b); +SOKOL_GL_API_DECL void sgl_v3f_c4f(float x, float y, float z, float r, float g, float b, float a); +SOKOL_GL_API_DECL void sgl_v3f_c4b(float x, float y, float z, uint8_t r, uint8_t g, uint8_t b, uint8_t a); +SOKOL_GL_API_DECL void sgl_v3f_c1i(float x, float y, float z, uint32_t rgba); +SOKOL_GL_API_DECL void sgl_v2f_t2f_c3f(float x, float y, float u, float v, float r, float g, float b); +SOKOL_GL_API_DECL void sgl_v2f_t2f_c3b(float x, float y, float u, float v, uint8_t r, uint8_t g, uint8_t b); +SOKOL_GL_API_DECL void sgl_v2f_t2f_c4f(float x, float y, float u, float v, float r, float g, float b, float a); +SOKOL_GL_API_DECL void sgl_v2f_t2f_c4b(float x, float y, float u, float v, uint8_t r, uint8_t g, uint8_t b, uint8_t a); +SOKOL_GL_API_DECL void sgl_v2f_t2f_c1i(float x, float y, float u, float v, uint32_t rgba); +SOKOL_GL_API_DECL void sgl_v3f_t2f_c3f(float x, float y, float z, float u, float v, float r, float g, float b); +SOKOL_GL_API_DECL void sgl_v3f_t2f_c3b(float x, float y, float z, float u, float v, uint8_t r, uint8_t g, uint8_t b); +SOKOL_GL_API_DECL void sgl_v3f_t2f_c4f(float x, float y, float z, float u, float v, float r, float g, float b, float a); +SOKOL_GL_API_DECL void sgl_v3f_t2f_c4b(float x, float y, float z, float u, float v, uint8_t r, uint8_t g, uint8_t b, uint8_t a); +SOKOL_GL_API_DECL void sgl_v3f_t2f_c1i(float x, float y, float z, float u, float v, uint32_t rgba); +SOKOL_GL_API_DECL void sgl_end(void); + +/* render everything */ +SOKOL_GL_API_DECL void sgl_draw(void); + +#ifdef __cplusplus +} /* extern "C" */ + +/* reference-based equivalents for C++ */ +inline void sgl_setup(const sgl_desc_t& desc) { return sgl_setup(&desc); } +inline sgl_pipeline sgl_make_pipeline(const sg_pipeline_desc& desc) { return sgl_make_pipeline(&desc); } +#endif +#endif /* SOKOL_GL_INCLUDED */ + +/*-- IMPLEMENTATION ----------------------------------------------------------*/ +#ifdef SOKOL_GL_IMPL +#define SOKOL_GL_IMPL_INCLUDED (1) + +#include /* offsetof */ +#include /* memset */ +#include /* M_PI, sqrtf, sinf, cosf */ + +#ifndef M_PI +#define M_PI 3.14159265358979323846264338327 +#endif + +#ifndef SOKOL_API_IMPL + #define SOKOL_API_IMPL +#endif +#ifndef SOKOL_DEBUG + #ifndef NDEBUG + #define SOKOL_DEBUG (1) + #endif +#endif +#ifndef SOKOL_ASSERT + #include + #define SOKOL_ASSERT(c) assert(c) +#endif +#ifndef SOKOL_MALLOC + #include + #define SOKOL_MALLOC(s) malloc(s) + #define SOKOL_FREE(p) free(p) +#endif +#ifndef SOKOL_LOG + #ifdef SOKOL_DEBUG + #include + #define SOKOL_LOG(s) { SOKOL_ASSERT(s); puts(s); } + #else + #define SOKOL_LOG(s) + #endif +#endif +#ifndef SOKOL_UNREACHABLE + #define SOKOL_UNREACHABLE SOKOL_ASSERT(false) +#endif + +#define _sgl_def(val, def) (((val) == 0) ? (def) : (val)) +#define _SGL_INIT_COOKIE (0xABCDABCD) + +/* + Embedded source code compiled with: + + sokol-shdc -i sgl.glsl -o sgl.h -l glsl330:glsl100:hlsl4:metal_macos:metal_ios:metal_sim:wgpu -b + + (not that for Metal and D3D11 byte code, sokol-shdc must be run + on macOS and Windows) + + @vs vs + uniform vs_params { + mat4 mvp; + mat4 tm; + }; + in vec4 position; + in vec2 texcoord0; + in vec4 color0; + out vec4 uv; + out vec4 color; + void main() { + gl_Position = mvp * position; + uv = tm * vec4(texcoord0, 0.0, 1.0); + color = color0; + } + @end + + @fs fs + uniform sampler2D tex; + in vec4 uv; + in vec4 color; + out vec4 frag_color; + void main() { + frag_color = texture(tex, uv.xy) * color; + } + @end + + @program sgl vs fs +*/ + +#if defined(SOKOL_GLCORE33) +static const char _sgl_vs_source_glsl330[415] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x33,0x30,0x0a,0x0a,0x75,0x6e, + 0x69,0x66,0x6f,0x72,0x6d,0x20,0x76,0x65,0x63,0x34,0x20,0x76,0x73,0x5f,0x70,0x61, + 0x72,0x61,0x6d,0x73,0x5b,0x38,0x5d,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28, + 0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x69,0x6e, + 0x20,0x76,0x65,0x63,0x34,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,0x0a, + 0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x75,0x76,0x3b,0x0a,0x6c,0x61,0x79, + 0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x31, + 0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x32,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f, + 0x72,0x64,0x30,0x3b,0x0a,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f, + 0x6c,0x6f,0x72,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61, + 0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x32,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63, + 0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20, + 0x6d,0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x67,0x6c,0x5f, + 0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x6d,0x61,0x74,0x34,0x28, + 0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x30,0x5d,0x2c,0x20,0x76,0x73, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x31,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70, + 0x61,0x72,0x61,0x6d,0x73,0x5b,0x32,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72, + 0x61,0x6d,0x73,0x5b,0x33,0x5d,0x29,0x20,0x2a,0x20,0x70,0x6f,0x73,0x69,0x74,0x69, + 0x6f,0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x6d,0x61,0x74, + 0x34,0x28,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x34,0x5d,0x2c,0x20, + 0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x35,0x5d,0x2c,0x20,0x76,0x73, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x36,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70, + 0x61,0x72,0x61,0x6d,0x73,0x5b,0x37,0x5d,0x29,0x20,0x2a,0x20,0x76,0x65,0x63,0x34, + 0x28,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x2c,0x20,0x30,0x2e,0x30,0x2c, + 0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72, + 0x20,0x3d,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +static const char _sgl_fs_source_glsl330[172] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x33,0x30,0x0a,0x0a,0x75,0x6e, + 0x69,0x66,0x6f,0x72,0x6d,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x32,0x44,0x20, + 0x74,0x65,0x78,0x3b,0x0a,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63, + 0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x6f,0x75,0x74,0x20,0x76, + 0x65,0x63,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a, + 0x69,0x6e,0x20,0x76,0x65,0x63,0x34,0x20,0x75,0x76,0x3b,0x0a,0x69,0x6e,0x20,0x76, + 0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64, + 0x20,0x6d,0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x72, + 0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75, + 0x72,0x65,0x28,0x74,0x65,0x78,0x2c,0x20,0x75,0x76,0x2e,0x78,0x79,0x29,0x20,0x2a, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_GLES2) || defined(SOKOL_GLES3) +static const char _sgl_vs_source_glsl100[381] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x31,0x30,0x30,0x0a,0x0a,0x75,0x6e, + 0x69,0x66,0x6f,0x72,0x6d,0x20,0x76,0x65,0x63,0x34,0x20,0x76,0x73,0x5f,0x70,0x61, + 0x72,0x61,0x6d,0x73,0x5b,0x38,0x5d,0x3b,0x0a,0x61,0x74,0x74,0x72,0x69,0x62,0x75, + 0x74,0x65,0x20,0x76,0x65,0x63,0x34,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e, + 0x3b,0x0a,0x76,0x61,0x72,0x79,0x69,0x6e,0x67,0x20,0x76,0x65,0x63,0x34,0x20,0x75, + 0x76,0x3b,0x0a,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x20,0x76,0x65,0x63, + 0x32,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x3b,0x0a,0x76,0x61,0x72, + 0x79,0x69,0x6e,0x67,0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b, + 0x0a,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x20,0x76,0x65,0x63,0x34,0x20, + 0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61, + 0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x67,0x6c,0x5f,0x50,0x6f, + 0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x6d,0x61,0x74,0x34,0x28,0x76,0x73, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x30,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70, + 0x61,0x72,0x61,0x6d,0x73,0x5b,0x31,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72, + 0x61,0x6d,0x73,0x5b,0x32,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d, + 0x73,0x5b,0x33,0x5d,0x29,0x20,0x2a,0x20,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e, + 0x3b,0x0a,0x20,0x20,0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x6d,0x61,0x74,0x34,0x28, + 0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x34,0x5d,0x2c,0x20,0x76,0x73, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x35,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70, + 0x61,0x72,0x61,0x6d,0x73,0x5b,0x36,0x5d,0x2c,0x20,0x76,0x73,0x5f,0x70,0x61,0x72, + 0x61,0x6d,0x73,0x5b,0x37,0x5d,0x29,0x20,0x2a,0x20,0x76,0x65,0x63,0x34,0x28,0x74, + 0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20,0x31, + 0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d, + 0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +static const char _sgl_fs_source_glsl100[210] = { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x31,0x30,0x30,0x0a,0x70,0x72,0x65, + 0x63,0x69,0x73,0x69,0x6f,0x6e,0x20,0x6d,0x65,0x64,0x69,0x75,0x6d,0x70,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x3b,0x0a,0x70,0x72,0x65,0x63,0x69,0x73,0x69,0x6f,0x6e,0x20, + 0x68,0x69,0x67,0x68,0x70,0x20,0x69,0x6e,0x74,0x3b,0x0a,0x0a,0x75,0x6e,0x69,0x66, + 0x6f,0x72,0x6d,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65, + 0x72,0x32,0x44,0x20,0x74,0x65,0x78,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x79,0x69,0x6e, + 0x67,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x34,0x20,0x75,0x76,0x3b, + 0x0a,0x76,0x61,0x72,0x79,0x69,0x6e,0x67,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76, + 0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64, + 0x20,0x6d,0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x67,0x6c, + 0x5f,0x46,0x72,0x61,0x67,0x44,0x61,0x74,0x61,0x5b,0x30,0x5d,0x20,0x3d,0x20,0x74, + 0x65,0x78,0x74,0x75,0x72,0x65,0x32,0x44,0x28,0x74,0x65,0x78,0x2c,0x20,0x75,0x76, + 0x2e,0x78,0x79,0x29,0x20,0x2a,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x7d,0x0a, + 0x0a,0x00, +}; +#elif defined(SOKOL_METAL) +static const uint8_t _sgl_vs_bytecode_metal_macos[3360] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x20,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x3b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x10,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x00,0x48,0x41,0x53,0x48,0x20,0x00,0x54,0xec,0xec,0xa8,0x23,0x83,0x8d, + 0xc7,0xcf,0x6e,0x67,0x94,0x95,0x54,0x94,0xeb,0x98,0x15,0x2a,0x2c,0x54,0xa0,0x4b, + 0x24,0x6f,0x5e,0xed,0x6e,0x76,0x45,0xb7,0x5e,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x45,0x4e,0x44,0x54,0x37,0x00,0x00, + 0x00,0x56,0x41,0x54,0x54,0x22,0x00,0x03,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x6e,0x00,0x00,0x80,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x00,0x01,0x80, + 0x63,0x6f,0x6c,0x6f,0x72,0x30,0x00,0x02,0x80,0x56,0x41,0x54,0x59,0x05,0x00,0x03, + 0x00,0x06,0x04,0x06,0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54, + 0xde,0xc0,0x17,0x0b,0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0xf0,0x0b,0x00,0x00, + 0xff,0xff,0xff,0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0xf9,0x02,0x00,0x00, + 0x0b,0x82,0x20,0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91, + 0x41,0xc8,0x04,0x49,0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19, + 0x1e,0x04,0x8b,0x62,0x80,0x14,0x45,0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14, + 0x38,0x08,0x18,0x49,0x0a,0x32,0x44,0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80, + 0x0c,0x19,0x21,0x72,0x24,0x07,0xc8,0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0, + 0x01,0x00,0x00,0x00,0x51,0x18,0x00,0x00,0x81,0x00,0x00,0x00,0x1b,0xc8,0x25,0xf8, + 0xff,0xff,0xff,0xff,0x01,0x90,0x80,0x8a,0x18,0x87,0x77,0x90,0x07,0x79,0x28,0x87, + 0x71,0xa0,0x07,0x76,0xc8,0x87,0x36,0x90,0x87,0x77,0xa8,0x07,0x77,0x20,0x87,0x72, + 0x20,0x87,0x36,0x20,0x87,0x74,0xb0,0x87,0x74,0x20,0x87,0x72,0x68,0x83,0x79,0x88, + 0x07,0x79,0xa0,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07, + 0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1,0x1c,0x00,0x82,0x1c,0xd2,0x61,0x1e,0xc2,0x41, + 0x1c,0xd8,0xa1,0x1c,0xda,0x80,0x1e,0xc2,0x21,0x1d,0xd8,0xa1,0x0d,0xc6,0x21,0x1c, + 0xd8,0x81,0x1d,0xe6,0x01,0x30,0x87,0x70,0x60,0x87,0x79,0x28,0x07,0x80,0x60,0x87, + 0x72,0x98,0x87,0x79,0x68,0x03,0x78,0x90,0x87,0x72,0x18,0x87,0x74,0x98,0x87,0x72, + 0x68,0x03,0x73,0x80,0x87,0x76,0x08,0x07,0x72,0x00,0xcc,0x21,0x1c,0xd8,0x61,0x1e, + 0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0xc0,0x1c,0xe4,0x21,0x1c,0xda,0xa1,0x1c,0xda, + 0x00,0x1e,0xde,0x21,0x1d,0xdc,0x81,0x1e,0xca,0x41,0x1e,0xda,0xa0,0x1c,0xd8,0x21, + 0x1d,0xda,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x06,0x77,0x78,0x87,0x36,0x30, + 0x07,0x79,0x08,0x87,0x76,0x28,0x87,0x36,0x80,0x87,0x77,0x48,0x07,0x77,0xa0,0x87, + 0x72,0x90,0x87,0x36,0x28,0x07,0x76,0x48,0x87,0x76,0x68,0x03,0x77,0x78,0x07,0x77, + 0x68,0x03,0x76,0x28,0x87,0x70,0x30,0x07,0x80,0x70,0x87,0x77,0x68,0x83,0x74,0x70, + 0x07,0x73,0x98,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07, + 0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0x40,0x1d,0xea,0xa1, + 0x1d,0xe0,0xa1,0x0d,0xe8,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x1e,0x00,0x73,0x08, + 0x07,0x76,0x98,0x87,0x72,0x00,0x08,0x77,0x78,0x87,0x36,0x70,0x87,0x70,0x70,0x87, + 0x79,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41, + 0x1e,0xea,0xa1,0x1c,0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xe6,0x21,0x1d,0xce,0xc1,0x1d, + 0xca,0x81,0x1c,0xda,0x40,0x1f,0xca,0x41,0x1e,0xde,0x61,0x1e,0xda,0xc0,0x1c,0xe0, + 0xa1,0x0d,0xda,0x21,0x1c,0xe8,0x01,0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x80, + 0x70,0x87,0x77,0x68,0x03,0x7a,0x90,0x87,0x70,0x80,0x07,0x78,0x48,0x07,0x77,0x38, + 0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00, + 0x62,0x1e,0xe8,0x21,0x1c,0xc6,0x61,0x1d,0xda,0x00,0x1e,0xe4,0xe1,0x1d,0xe8,0xa1, + 0x1c,0xc6,0x81,0x1e,0xde,0x41,0x1e,0xda,0x40,0x1c,0xea,0xc1,0x1c,0xcc,0xa1,0x1c, + 0xe4,0xa1,0x0d,0xe6,0x21,0x1d,0xf4,0xa1,0x1c,0x00,0x3c,0x00,0x88,0x7a,0x70,0x87, + 0x79,0x08,0x07,0x73,0x28,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a, + 0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xea,0x61,0x1e,0xca,0xa1,0x0d, + 0xe6,0xe1,0x1d,0xcc,0x81,0x1e,0xda,0xc0,0x1c,0xd8,0xe1,0x1d,0xc2,0x81,0x1e,0x00, + 0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x36,0x18,0x02,0x01,0x2c,0x40,0x05,0x00, + 0x49,0x18,0x00,0x00,0x01,0x00,0x00,0x00,0x13,0x84,0x40,0x00,0x89,0x20,0x00,0x00, + 0x23,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85,0x04,0x93,0x22,0xa4,0x84, + 0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a,0x8c,0x0b,0x84,0xa4,0x4c, + 0x10,0x44,0x33,0x00,0xc3,0x08,0x04,0x70,0x90,0x34,0x45,0x94,0x30,0xf9,0x0c,0x80, + 0x34,0xf4,0xef,0x50,0x13,0x1a,0x42,0x08,0xc3,0x08,0x02,0x90,0x04,0x61,0x26,0x6a, + 0x1e,0xe8,0x41,0x1e,0xea,0x61,0x1c,0xe8,0xc1,0x0d,0xda,0xa1,0x1c,0xe8,0x21,0x1c, + 0xd8,0x41,0x0f,0xf4,0xa0,0x1d,0xc2,0x81,0x1e,0xe4,0x21,0x1d,0xf0,0x01,0x05,0xe4, + 0x20,0x69,0x8a,0x28,0x61,0xf2,0x2b,0xe9,0x7f,0x80,0x08,0x60,0x24,0x24,0x94,0x32, + 0x88,0x60,0x08,0xa5,0x10,0x61,0x84,0x43,0x68,0x20,0x60,0x8e,0x00,0x0c,0x72,0x60, + 0xcd,0x11,0x80,0xc2,0x20,0x42,0x20,0x0c,0x23,0x10,0xcb,0x08,0x00,0x00,0x00,0x00, + 0x13,0xb2,0x70,0x48,0x07,0x79,0xb0,0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60, + 0x87,0x72,0x68,0x83,0x76,0x08,0x87,0x71,0x78,0x87,0x79,0xc0,0x87,0x38,0x80,0x03, + 0x37,0x88,0x83,0x38,0x70,0x03,0x38,0xd8,0x70,0x1b,0xe5,0xd0,0x06,0xf0,0xa0,0x07, + 0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x90,0x0e,0x71, + 0xa0,0x07,0x78,0xa0,0x07,0x78,0xd0,0x06,0xe9,0x80,0x07,0x7a,0x80,0x07,0x7a,0x80, + 0x07,0x6d,0x90,0x0e,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07, + 0x6d,0x90,0x0e,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d, + 0x90,0x0e,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60, + 0x0e,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0e, + 0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x71, + 0x60,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x6d,0x60,0x0f,0x72,0x40, + 0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0f,0x73,0x20,0x07, + 0x7a,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0f,0x74,0x80,0x07,0x7a, + 0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x76,0x40,0x07,0x7a,0x60, + 0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x79,0x60,0x07,0x7a,0x10,0x07, + 0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x6d,0x60,0x0f,0x71,0x20,0x07,0x78, + 0xa0,0x07,0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10, + 0x07,0x79,0x20,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07, + 0x6d,0x60,0x0f,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72, + 0x50,0x07,0x76,0xd0,0x06,0xf6,0x50,0x07,0x71,0x20,0x07,0x7a,0x50,0x07,0x71,0x20, + 0x07,0x7a,0x50,0x07,0x71,0x20,0x07,0x6d,0x60,0x0f,0x71,0x00,0x07,0x72,0x40,0x07, + 0x7a,0x10,0x07,0x70,0x20,0x07,0x74,0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x6d, + 0xe0,0x0e,0x78,0xa0,0x07,0x71,0x60,0x07,0x7a,0x30,0x07,0x72,0x30,0x84,0x49,0x00, + 0x00,0x08,0x00,0x00,0x00,0x00,0x00,0xc8,0x02,0x01,0x00,0x00,0x0b,0x00,0x00,0x00, + 0x32,0x1e,0x98,0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x5a, + 0x25,0x30,0x02,0x50,0x04,0x05,0x18,0x50,0x80,0x02,0x85,0x50,0x10,0x65,0x50,0x20, + 0xd4,0x46,0x00,0x88,0x8d,0x35,0x28,0x0f,0x01,0x00,0x00,0x00,0x79,0x18,0x00,0x00, + 0x19,0x01,0x00,0x00,0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9, + 0xb9,0xb4,0x37,0xb7,0x21,0xc6,0x32,0x28,0x00,0xb3,0x50,0xb9,0x1b,0x43,0x0b,0x93, + 0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62,0x2c,0x81,0x22,0x2c,0x06,0xd9,0x20,0x08,0x0e, + 0x8e,0xad,0x0c,0x84,0x89,0xc9,0xaa,0x09,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d, + 0x24,0x07,0x46,0xc6,0x25,0x86,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac, + 0x25,0x07,0x46,0xc6,0x25,0x86,0xc6,0x25,0x27,0x65,0x88,0xa0,0x10,0x43,0x8c,0x25, + 0x58,0x8e,0x45,0x60,0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x51,0x8e,0x25,0x58,0x82, + 0x45,0xe0,0x16,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42,0x56, + 0xe6,0xf6,0x26,0xd7,0x36,0xf7,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x50, + 0x12,0x72,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66,0x61, + 0x73,0x74,0x5f,0x6d,0x61,0x74,0x68,0x5f,0x65,0x6e,0x61,0x62,0x6c,0x65,0x43,0x04, + 0x65,0x61,0x19,0x84,0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9,0x98, + 0xc9,0x85,0xb5,0x95,0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d,0xa1, + 0x7d,0x91,0xa5,0xcd,0x85,0x89,0xb1,0x95,0x0d,0x11,0x94,0x86,0x61,0x10,0x96,0x26, + 0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0xe2,0x16,0x46,0x97,0x66,0x57,0xf6, + 0x45,0xf6,0x56,0x27,0xc6,0x56,0xf6,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44, + 0x50,0x1e,0x92,0x41,0x58,0x9a,0x9c,0xcb,0xd8,0x5b,0x1b,0x5c,0x1a,0x5b,0x99,0x8b, + 0x5b,0x18,0x5d,0x9a,0x5d,0xd9,0x17,0xdb,0x9b,0xdb,0xd9,0x17,0xdb,0x9b,0xdb,0xd9, + 0x17,0x59,0xda,0x5c,0x98,0x18,0x5b,0xd9,0x10,0x41,0x89,0x78,0x06,0x61,0x69,0x72, + 0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x6e,0x61,0x74,0x69,0x76,0x65,0x5f, + 0x77,0x69,0x64,0x65,0x5f,0x76,0x65,0x63,0x74,0x6f,0x72,0x73,0x5f,0x64,0x69,0x73, + 0x61,0x62,0x6c,0x65,0x43,0x04,0x65,0x62,0x14,0x96,0x26,0xe7,0x62,0x57,0x26,0x47, + 0x57,0x86,0xf7,0xf5,0x56,0x47,0x07,0x57,0x47,0xc7,0xa5,0x6e,0xae,0x4c,0x0e,0x85, + 0xed,0x6d,0xcc,0x0d,0x26,0x85,0x51,0x58,0x9a,0x9c,0x4b,0x98,0xdc,0xd9,0x17,0x5d, + 0x1e,0x5c,0xd9,0x97,0x5b,0x58,0x5b,0x19,0x0d,0x33,0xb6,0xb7,0x30,0x3a,0x1a,0x32, + 0x61,0x69,0x72,0x2e,0x61,0x72,0x67,0x5f,0x6e,0x61,0x6d,0x65,0x14,0xea,0xec,0x86, + 0x30,0x4a,0xa5,0x58,0xca,0xa5,0x60,0x4a,0xa6,0x68,0x5c,0xea,0xe6,0xca,0xe4,0x50, + 0xd8,0xde,0xc6,0xdc,0x62,0x52,0x58,0x8c,0xbd,0xb1,0xbd,0xc9,0x0d,0x61,0x94,0x4a, + 0xe1,0x94,0x4b,0xc1,0x94,0x4c,0xe9,0xc8,0x84,0xa5,0xc9,0xb9,0xc0,0xbd,0xcd,0xa5, + 0xd1,0xa5,0xbd,0xb9,0x71,0x39,0x63,0xfb,0x82,0x7a,0x9b,0x4b,0xa3,0x4b,0x7b,0x73, + 0x1b,0xa2,0x28,0x9f,0x72,0x29,0x98,0x92,0x29,0x60,0x30,0xc4,0x50,0x36,0xc5,0x53, + 0xc2,0x80,0x50,0x58,0x9a,0x9c,0x8b,0x5d,0x99,0x1c,0x5d,0x19,0xde,0x57,0x9a,0x1b, + 0x5c,0x1d,0x1d,0xa5,0xb0,0x34,0x39,0x17,0xb6,0xb7,0xb1,0x30,0xba,0xb4,0x37,0xb7, + 0xaf,0x34,0x37,0xb2,0x32,0x3c,0x66,0x67,0x65,0x6e,0x65,0x72,0x61,0x74,0x65,0x64, + 0x28,0x38,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x44,0x76,0x34,0x5f,0x66,0x29, + 0x44,0xe0,0xde,0xe6,0xd2,0xe8,0xd2,0xde,0xdc,0x86,0x50,0x8b,0xa0,0x8c,0x81,0x42, + 0x06,0x8b,0xb0,0x04,0x4a,0x19,0x28,0x97,0x82,0x29,0x99,0x62,0x06,0xd4,0xce,0xca, + 0xdc,0xca,0xe4,0xc2,0xe8,0xca,0xc8,0x50,0x72,0xe8,0xca,0xf0,0xc6,0xde,0xde,0xe4, + 0xc8,0x60,0x88,0xec,0x64,0xbe,0xcc,0x52,0x68,0x98,0xb1,0xbd,0x85,0xd1,0xc9,0x30, + 0xa1,0x2b,0xc3,0x1b,0x7b,0x7b,0x93,0x23,0x83,0x19,0x42,0x2d,0x81,0x32,0x06,0x0a, + 0x19,0x2c,0xc1,0x12,0x28,0x68,0xa0,0x5c,0x4a,0x1a,0x28,0x99,0xa2,0x06,0xbc,0xce, + 0xca,0xdc,0xca,0xe4,0xc2,0xe8,0xca,0xc8,0x50,0x6c,0xc6,0xde,0xd8,0xde,0xe4,0x60, + 0x88,0xec,0x68,0xbe,0xcc,0x52,0x68,0x8c,0xbd,0xb1,0xbd,0xc9,0xc1,0x0c,0xa1,0x96, + 0x41,0x19,0x03,0x85,0x0c,0x96,0x61,0x09,0x14,0x36,0x50,0x2e,0x05,0x53,0x32,0xa5, + 0x0d,0xa8,0x84,0xa5,0xc9,0xb9,0x88,0xd5,0x99,0x99,0x95,0xc9,0xf1,0x09,0x4b,0x93, + 0x73,0x11,0xab,0x33,0x33,0x2b,0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x23,0x12,0x96,0x26, + 0xe7,0x22,0x57,0x16,0x46,0x46,0x2a,0x2c,0x4d,0xce,0x65,0x8e,0x4e,0xae,0x6e,0x8c, + 0xee,0x8b,0x2e,0x0f,0xae,0xec,0x2b,0xcd,0xcd,0xec,0x8d,0x88,0x19,0xdb,0x5b,0x18, + 0x1d,0x0d,0x1e,0x0d,0x87,0x36,0x3b,0x38,0x0a,0x74,0x6d,0x43,0xa8,0x45,0x58,0x88, + 0x45,0x50,0xe6,0x40,0xa1,0x83,0x85,0x58,0x88,0x45,0x50,0xe6,0x40,0xa9,0x03,0x46, + 0x61,0x69,0x72,0x2e,0x61,0x72,0x67,0x5f,0x74,0x79,0x70,0x65,0x5f,0x73,0x69,0x7a, + 0x65,0xbc,0xc2,0xd2,0xe4,0x5c,0xc2,0xe4,0xce,0xbe,0xe8,0xf2,0xe0,0xca,0xbe,0xc2, + 0xd8,0xd2,0xce,0xdc,0xbe,0xe6,0xd2,0xf4,0xca,0x98,0xd8,0xcd,0x7d,0xc1,0x85,0xc9, + 0x85,0xb5,0xcd,0x71,0xf8,0x92,0x81,0x19,0x42,0x06,0x0b,0xa2,0xbc,0x81,0x02,0x07, + 0x4b,0xa1,0x90,0xc1,0x22,0x2c,0x81,0x12,0x07,0x8a,0x1c,0x28,0x76,0xa0,0xdc,0xc1, + 0x52,0x28,0x78,0xb0,0x24,0xca,0xa5,0xe4,0x81,0x92,0x29,0x7a,0x30,0x04,0x51,0xce, + 0x40,0x59,0x03,0xc5,0x0d,0x94,0x3d,0x18,0x62,0x24,0x80,0x22,0x06,0x0a,0x1f,0xf0, + 0x79,0x6b,0x73,0x4b,0x83,0x7b,0xa3,0x2b,0x73,0xa3,0x03,0x19,0x43,0x0b,0x93,0xe3, + 0x33,0x95,0xd6,0x06,0xc7,0x56,0x06,0x32,0xb4,0xb2,0x02,0x42,0x25,0x14,0x14,0x34, + 0x44,0x50,0xfe,0x60,0x88,0xa1,0xf8,0x81,0x02,0x0a,0x8d,0x32,0xc4,0x50,0x42,0x41, + 0x09,0x85,0x46,0x19,0x11,0xb1,0x03,0x3b,0xd8,0x43,0x3b,0xb8,0x41,0x3b,0xbc,0x03, + 0x39,0xd4,0x03,0x3b,0x94,0x83,0x1b,0x98,0x03,0x3b,0x84,0xc3,0x39,0xcc,0xc3,0x14, + 0x21,0x18,0x46,0x28,0xec,0xc0,0x0e,0xf6,0xd0,0x0e,0x6e,0x90,0x0e,0xe4,0x50,0x0e, + 0xee,0x40,0x0f,0x53,0x82,0x62,0xc4,0x12,0x0e,0xe9,0x20,0x0f,0x6e,0x60,0x0f,0xe5, + 0x20,0x0f,0xf3,0x90,0x0e,0xef,0xe0,0x0e,0x53,0x02,0x63,0x04,0x15,0x0e,0xe9,0x20, + 0x0f,0x6e,0xc0,0x0e,0xe1,0xe0,0x0e,0xe7,0x50,0x0f,0xe1,0x70,0x0e,0xe5,0xf0,0x0b, + 0xf6,0x50,0x0e,0xf2,0x30,0x0f,0xe9,0xf0,0x0e,0xee,0x30,0x25,0x40,0x46,0x4c,0xe1, + 0x90,0x0e,0xf2,0xe0,0x06,0xe3,0xf0,0x0e,0xed,0x00,0x0f,0xe9,0xc0,0x0e,0xe5,0xf0, + 0x0b,0xef,0x00,0x0f,0xf4,0x90,0x0e,0xef,0xe0,0x0e,0xf3,0x30,0xc5,0x50,0x18,0x07, + 0x92,0xa8,0x11,0x4a,0x38,0xa4,0x83,0x3c,0xb8,0x81,0x3d,0x94,0x83,0x3c,0xd0,0x43, + 0x39,0xe0,0xc3,0x94,0xa0,0x0f,0x00,0x00,0x79,0x18,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3, + 0x8c,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10, + 0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30, + 0x05,0x3d,0x88,0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03, + 0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07, + 0x7a,0x70,0x03,0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e, + 0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d, + 0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b, + 0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76, + 0x60,0x07,0x7b,0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90, + 0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87, + 0x5f,0x08,0x87,0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e, + 0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c, + 0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca, + 0x61,0x06,0xd6,0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8, + 0xc3,0x38,0x94,0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82, + 0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x0c,0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70,0x83, + 0x74,0x68,0x07,0x78,0x60,0x87,0x74,0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53,0x0f, + 0xee,0x00,0x0f,0xf2,0x50,0x0e,0xe4,0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e,0xec, + 0x50,0x0e,0x33,0x20,0x28,0x1d,0xdc,0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c,0xdc, + 0x81,0x1e,0xdc,0xe0,0x1c,0xe4,0xe1,0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38,0xb0, + 0x43,0x3a,0x9c,0x83,0x3b,0xcc,0x50,0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x60, + 0x87,0x77,0x78,0x07,0x78,0x98,0x51,0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33,0x1e, + 0x6a,0x1e,0xca,0x61,0x1c,0xe8,0x21,0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4,0xa1, + 0x1c,0xcc,0x21,0x1d,0xf0,0x61,0x06,0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0,0x43, + 0x3d,0xd0,0x43,0x39,0xfc,0xc2,0x3c,0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3,0x8c, + 0xc5,0x0a,0x87,0x79,0x98,0x87,0x77,0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07,0x72, + 0x00,0x00,0x00,0x00,0x71,0x20,0x00,0x00,0x02,0x00,0x00,0x00,0x06,0x50,0x30,0x00, + 0xd2,0xd0,0x00,0x00,0x61,0x20,0x00,0x00,0x3d,0x00,0x00,0x00,0x13,0x04,0x41,0x2c, + 0x10,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0xf4,0xc6,0x22,0x86,0x61,0x18,0xc6,0x22, + 0x04,0x41,0x10,0xc6,0x22,0x82,0x20,0x08,0xa8,0x95,0x40,0x19,0x14,0x01,0xbd,0x11, + 0x00,0x1a,0x33,0x00,0x24,0x66,0x00,0x28,0xcc,0x00,0x00,0x00,0xe3,0x15,0x0b,0x84, + 0x61,0x10,0x05,0x65,0x90,0x01,0x1a,0x0c,0x13,0x02,0xf9,0x8c,0x57,0x3c,0x14,0xc7, + 0x2d,0x14,0x94,0x41,0x06,0xea,0x70,0x4c,0x08,0xe4,0x63,0x41,0x01,0x9f,0xf1,0x0a, + 0x2a,0x0b,0x83,0x30,0x70,0x28,0x28,0x83,0x0c,0x19,0x43,0x99,0x10,0xc8,0xc7,0x8a, + 0x00,0x3e,0xe3,0x15,0x99,0x67,0x06,0x66,0x40,0x51,0x50,0x06,0x19,0xbc,0x48,0x33, + 0x21,0x90,0x8f,0x15,0x01,0x7c,0xc6,0x2b,0xbc,0x31,0x60,0x83,0x35,0x18,0x03,0x0a, + 0xca,0x20,0x83,0x18,0x60,0x99,0x09,0x81,0x7c,0xc6,0x2b,0xc4,0xe0,0x0c,0xe0,0xe0, + 0x0d,0x3c,0x0a,0xca,0x20,0x83,0x19,0x70,0x61,0x60,0x42,0x20,0x1f,0x0b,0x0a,0xf8, + 0x8c,0x57,0x9c,0x01,0x1b,0xd4,0x01,0x1d,0x88,0x01,0x05,0xc5,0x86,0x00,0x3e,0xb3, + 0x0d,0x61,0x10,0x00,0xb3,0x0d,0x01,0x1b,0x04,0xb3,0x0d,0xc1,0x23,0x64,0x10,0x10, + 0x03,0x00,0x00,0x00,0x09,0x00,0x00,0x00,0x5b,0x86,0x20,0x10,0x85,0x2d,0x43,0x11, + 0x88,0xc2,0x96,0x41,0x09,0x44,0x61,0xcb,0xf0,0x04,0xa2,0xb0,0x65,0xa0,0x02,0x51, + 0xd8,0x32,0x60,0x81,0x28,0x6c,0x19,0xba,0x40,0x14,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _sgl_fs_bytecode_metal_macos[2909] = { + 0x4d,0x54,0x4c,0x42,0x01,0x80,0x02,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x5d,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd5,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x80,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0xc3,0xf2,0x7e,0x47,0xcc,0xf2,0x87, + 0xc2,0x27,0x51,0x05,0xdb,0xac,0xc5,0x2d,0x03,0xfc,0xe6,0x91,0x1c,0x38,0xc9,0xd8, + 0x34,0x4c,0xa7,0xd1,0x68,0xc2,0x62,0x41,0xf6,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x01,0x00,0x45,0x4e,0x44,0x54,0x45,0x4e,0x44,0x54,0x04,0x00,0x00, + 0x00,0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17, + 0x0b,0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x64,0x0a,0x00,0x00,0xff,0xff,0xff, + 0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0x96,0x02,0x00,0x00,0x0b,0x82,0x20, + 0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04, + 0x49,0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b, + 0x62,0x80,0x14,0x45,0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14,0x38,0x08,0x18, + 0x49,0x0a,0x32,0x44,0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21, + 0x72,0x24,0x07,0xc8,0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00, + 0x00,0x51,0x18,0x00,0x00,0x89,0x00,0x00,0x00,0x1b,0xcc,0x25,0xf8,0xff,0xff,0xff, + 0xff,0x01,0x60,0x00,0x09,0xa8,0x88,0x71,0x78,0x07,0x79,0x90,0x87,0x72,0x18,0x07, + 0x7a,0x60,0x87,0x7c,0x68,0x03,0x79,0x78,0x87,0x7a,0x70,0x07,0x72,0x28,0x07,0x72, + 0x68,0x03,0x72,0x48,0x07,0x7b,0x48,0x07,0x72,0x28,0x87,0x36,0x98,0x87,0x78,0x90, + 0x07,0x7a,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xcc, + 0x21,0x1c,0xd8,0x61,0x1e,0xca,0x01,0x20,0xc8,0x21,0x1d,0xe6,0x21,0x1c,0xc4,0x81, + 0x1d,0xca,0xa1,0x0d,0xe8,0x21,0x1c,0xd2,0x81,0x1d,0xda,0x60,0x1c,0xc2,0x81,0x1d, + 0xd8,0x61,0x1e,0x00,0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x08,0x76,0x28,0x87, + 0x79,0x98,0x87,0x36,0x80,0x07,0x79,0x28,0x87,0x71,0x48,0x87,0x79,0x28,0x87,0x36, + 0x30,0x07,0x78,0x68,0x87,0x70,0x20,0x07,0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1,0x1c, + 0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xcc,0x41,0x1e,0xc2,0xa1,0x1d,0xca,0xa1,0x0d,0xe0, + 0xe1,0x1d,0xd2,0xc1,0x1d,0xe8,0xa1,0x1c,0xe4,0xa1,0x0d,0xca,0x81,0x1d,0xd2,0xa1, + 0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x60,0x70,0x87,0x77,0x68,0x03,0x73,0x90, + 0x87,0x70,0x68,0x87,0x72,0x68,0x03,0x78,0x78,0x87,0x74,0x70,0x07,0x7a,0x28,0x07, + 0x79,0x68,0x83,0x72,0x60,0x87,0x74,0x68,0x87,0x36,0x70,0x87,0x77,0x70,0x87,0x36, + 0x60,0x87,0x72,0x08,0x07,0x73,0x00,0x08,0x77,0x78,0x87,0x36,0x48,0x07,0x77,0x30, + 0x87,0x79,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8, + 0x41,0x1e,0xea,0xa1,0x1c,0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xd4,0xa1,0x1e,0xda,0x01, + 0x1e,0xda,0x80,0x1e,0xc2,0x41,0x1c,0xd8,0xa1,0x1c,0xe6,0x01,0x30,0x87,0x70,0x60, + 0x87,0x79,0x28,0x07,0x80,0x70,0x87,0x77,0x68,0x03,0x77,0x08,0x07,0x77,0x98,0x87, + 0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1, + 0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0x60,0x1e,0xd2,0xe1,0x1c,0xdc,0xa1,0x1c, + 0xc8,0xa1,0x0d,0xf4,0xa1,0x1c,0xe4,0xe1,0x1d,0xe6,0xa1,0x0d,0xcc,0x01,0x1e,0xda, + 0xa0,0x1d,0xc2,0x81,0x1e,0xd0,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x08,0x77, + 0x78,0x87,0x36,0xa0,0x07,0x79,0x08,0x07,0x78,0x80,0x87,0x74,0x70,0x87,0x73,0x68, + 0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xe6, + 0x81,0x1e,0xc2,0x61,0x1c,0xd6,0xa1,0x0d,0xe0,0x41,0x1e,0xde,0x81,0x1e,0xca,0x61, + 0x1c,0xe8,0xe1,0x1d,0xe4,0xa1,0x0d,0xc4,0xa1,0x1e,0xcc,0xc1,0x1c,0xca,0x41,0x1e, + 0xda,0x60,0x1e,0xd2,0x41,0x1f,0xca,0x01,0xc0,0x03,0x80,0xa8,0x07,0x77,0x98,0x87, + 0x70,0x30,0x87,0x72,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74, + 0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0xa2,0x1e,0xe6,0xa1,0x1c,0xda,0x60,0x1e, + 0xde,0xc1,0x1c,0xe8,0xa1,0x0d,0xcc,0x81,0x1d,0xde,0x21,0x1c,0xe8,0x01,0x30,0x87, + 0x70,0x60,0x87,0x79,0x28,0x07,0x60,0x83,0x21,0x0c,0xc0,0x02,0x54,0x1b,0x8c,0x81, + 0x00,0x16,0xa0,0xda,0x80,0x10,0xff,0xff,0xff,0xff,0x3f,0x00,0x0c,0x20,0x01,0xd5, + 0x06,0xa3,0x08,0x80,0x05,0xa8,0x36,0x18,0x86,0x00,0x2c,0x40,0x05,0x49,0x18,0x00, + 0x00,0x03,0x00,0x00,0x00,0x13,0x86,0x40,0x18,0x26,0x0c,0x44,0x61,0x00,0x00,0x00, + 0x00,0x89,0x20,0x00,0x00,0x20,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85, + 0x04,0x93,0x22,0xa4,0x84,0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a, + 0x8c,0x0b,0x84,0xa4,0x4c,0x10,0x48,0x33,0x00,0xc3,0x08,0x04,0x70,0x90,0x34,0x45, + 0x94,0x30,0xf9,0x0c,0x80,0x34,0xf4,0xef,0x50,0x13,0x0a,0xc2,0x51,0xd2,0x14,0x51, + 0xc2,0xe4,0xff,0x13,0x71,0x4d,0x54,0x44,0xfc,0xf6,0xf0,0x4f,0x63,0x04,0xc0,0x20, + 0xc2,0x10,0x5c,0x24,0x4d,0x11,0x25,0x4c,0xfe,0x2f,0x01,0xcc,0xb3,0x10,0xd1,0x3f, + 0x8d,0x11,0x00,0x83,0x08,0x85,0x50,0x0a,0x11,0x02,0x31,0x74,0x86,0x11,0x04,0x60, + 0x8e,0x20,0x98,0x23,0x00,0x83,0x61,0x04,0x61,0x29,0x48,0x20,0x26,0x29,0xa6,0x00, + 0xb5,0x81,0x80,0x1c,0x58,0x23,0x00,0x00,0x00,0x13,0xb2,0x70,0x48,0x07,0x79,0xb0, + 0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60,0x87,0x72,0x68,0x83,0x76,0x08,0x87, + 0x71,0x78,0x87,0x79,0xc0,0x87,0x38,0x80,0x03,0x37,0x88,0x83,0x38,0x70,0x03,0x38, + 0xd8,0x70,0x1b,0xe5,0xd0,0x06,0xf0,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74, + 0xa0,0x07,0x76,0x40,0x07,0x6d,0x90,0x0e,0x71,0xa0,0x07,0x78,0xa0,0x07,0x78,0xd0, + 0x06,0xe9,0x80,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07,0x6d,0x90,0x0e,0x71,0x60,0x07, + 0x7a,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x6d,0x90,0x0e,0x73,0x20,0x07,0x7a, + 0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x90,0x0e,0x76,0x40,0x07,0x7a,0x60, + 0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0e,0x73,0x20,0x07,0x7a,0x30,0x07, + 0x72,0xa0,0x07,0x73,0x20,0x07,0x6d,0x60,0x0e,0x76,0x40,0x07,0x7a,0x60,0x07,0x74, + 0xa0,0x07,0x76,0x40,0x07,0x6d,0x60,0x0f,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xa0, + 0x07,0x71,0x60,0x07,0x6d,0x60,0x0f,0x72,0x40,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07, + 0x73,0x20,0x07,0x6d,0x60,0x0f,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xa0,0x07,0x73, + 0x20,0x07,0x6d,0x60,0x0f,0x74,0x80,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40, + 0x07,0x6d,0x60,0x0f,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07, + 0x6d,0x60,0x0f,0x79,0x60,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72, + 0x80,0x07,0x6d,0x60,0x0f,0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xa0, + 0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10,0x07,0x79,0x20,0x07,0x7a,0x20,0x07, + 0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f,0x72,0x50,0x07,0x76, + 0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xd0,0x06,0xf6,0x50, + 0x07,0x71,0x20,0x07,0x7a,0x50,0x07,0x71,0x20,0x07,0x7a,0x50,0x07,0x71,0x20,0x07, + 0x6d,0x60,0x0f,0x71,0x00,0x07,0x72,0x40,0x07,0x7a,0x10,0x07,0x70,0x20,0x07,0x74, + 0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x6d,0xe0,0x0e,0x78,0xa0,0x07,0x71,0x60, + 0x07,0x7a,0x30,0x07,0x72,0x30,0x84,0x41,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x00, + 0x18,0xc2,0x38,0x40,0x00,0x08,0x00,0x00,0x00,0x00,0x00,0x64,0x81,0x00,0x00,0x00, + 0x00,0x08,0x00,0x00,0x00,0x32,0x1e,0x98,0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26, + 0x47,0xc6,0x04,0x43,0x5a,0x25,0x30,0x02,0x50,0x04,0x85,0x50,0x10,0x65,0x40,0x70, + 0xac,0x41,0x79,0x08,0x00,0x79,0x18,0x00,0x00,0xd7,0x00,0x00,0x00,0x1a,0x03,0x4c, + 0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37,0xb7,0x21,0xc6,0x42, + 0x3c,0x00,0x84,0x50,0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62, + 0x2c,0xc2,0x23,0x2c,0x05,0xd9,0x20,0x08,0x0e,0x8e,0xad,0x0c,0x84,0x89,0xc9,0xaa, + 0x09,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x24,0x07,0x46,0xc6,0x25,0x86,0x06, + 0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x25,0x07,0x46,0xc6,0x25,0x86,0xc6, + 0x25,0x27,0x65,0x88,0xf0,0x10,0x43,0x8c,0x45,0x58,0x8c,0x65,0x60,0xd1,0x54,0x46, + 0x17,0xc6,0x36,0x04,0x79,0x8e,0x45,0x58,0x84,0x65,0xe0,0x16,0x96,0x26,0xe7,0x32, + 0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42,0x56,0xe6,0xf6,0x26,0xd7,0x36,0xf7,0x45, + 0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x78,0x12,0x72,0x61,0x69,0x72,0x2e,0x63, + 0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66,0x61,0x73,0x74,0x5f,0x6d,0x61,0x74,0x68, + 0x5f,0x65,0x6e,0x61,0x62,0x6c,0x65,0x43,0x84,0x67,0x61,0x19,0x84,0xa5,0xc9,0xb9, + 0x8c,0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9,0x98,0xc9,0x85,0xb5,0x95,0x89,0xd5,0x99, + 0x99,0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d,0xa1,0x7d,0x91,0xa5,0xcd,0x85,0x89,0xb1, + 0x95,0x0d,0x11,0x9e,0x86,0x61,0x10,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6, + 0x56,0xe6,0xe2,0x16,0x46,0x97,0x66,0x57,0xf6,0x45,0xf6,0x56,0x27,0xc6,0x56,0xf6, + 0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x78,0x1e,0x92,0x41,0x58,0x9a,0x9c, + 0xcb,0xd8,0x5b,0x1b,0x5c,0x1a,0x5b,0x99,0x8b,0x5b,0x18,0x5d,0x9a,0x5d,0xd9,0x17, + 0xdb,0x9b,0xdb,0xd9,0x17,0xdb,0x9b,0xdb,0xd9,0x17,0x59,0xda,0x5c,0x98,0x18,0x5b, + 0xd9,0x10,0xe1,0x89,0x78,0x06,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c, + 0x65,0x2e,0x6e,0x61,0x74,0x69,0x76,0x65,0x5f,0x77,0x69,0x64,0x65,0x5f,0x76,0x65, + 0x63,0x74,0x6f,0x72,0x73,0x5f,0x64,0x69,0x73,0x61,0x62,0x6c,0x65,0x43,0x84,0x67, + 0x62,0x14,0x96,0x26,0xe7,0x22,0x57,0xe6,0x46,0x56,0x26,0xf7,0x45,0x17,0x26,0x77, + 0x56,0x46,0xc7,0x28,0x2c,0x4d,0xce,0x25,0x4c,0xee,0xec,0x8b,0x2e,0x0f,0xae,0xec, + 0xcb,0x2d,0xac,0xad,0x8c,0x86,0x19,0xdb,0x5b,0x18,0x1d,0x0d,0x99,0xb0,0x34,0x39, + 0x97,0x30,0xb9,0xb3,0x2f,0xb7,0xb0,0xb6,0x32,0x2a,0x66,0x72,0x61,0x67,0x5f,0x63, + 0x6f,0x6c,0x6f,0x72,0x43,0x98,0xa7,0x5a,0x86,0xc7,0x7a,0xae,0x07,0x7b,0xb2,0x21, + 0xc2,0xa3,0x51,0x0a,0x4b,0x93,0x73,0x31,0x93,0x0b,0x3b,0x6b,0x2b,0x73,0xa3,0xfb, + 0x4a,0x73,0x83,0xab,0xa3,0xe3,0x52,0x37,0x57,0x26,0x87,0xc2,0xf6,0x36,0xe6,0x06, + 0x93,0x42,0x25,0x2c,0x4d,0xce,0x65,0xac,0xcc,0x8d,0xae,0x4c,0x8e,0x4f,0x58,0x9a, + 0x9c,0x0b,0x5c,0x99,0xdc,0x1c,0x5c,0xd9,0x18,0x5d,0x9a,0x5d,0x19,0x85,0x3a,0xbb, + 0x21,0xd2,0x32,0x3c,0xdc,0xd3,0x3d,0xde,0xf3,0x3d,0xd6,0x73,0x3d,0xd8,0x03,0x06, + 0x5c,0xea,0xe6,0xca,0xe4,0x50,0xd8,0xde,0xc6,0xdc,0x62,0x52,0x58,0x8c,0xbd,0xb1, + 0xbd,0xc9,0x0d,0x91,0x16,0xe1,0xe1,0x1e,0x31,0x78,0xbc,0xe7,0x7b,0xac,0xe7,0x7a, + 0xb0,0x67,0x0c,0xb8,0x84,0xa5,0xc9,0xb9,0xd0,0x95,0xe1,0xd1,0xd5,0xc9,0x95,0x51, + 0x0a,0x4b,0x93,0x73,0x61,0x7b,0x1b,0x0b,0xa3,0x4b,0x7b,0x73,0xfb,0x4a,0x73,0x23, + 0x2b,0xc3,0xa3,0x12,0x96,0x26,0xe7,0x32,0x17,0xd6,0x06,0xc7,0x56,0x46,0x8c,0xae, + 0x0c,0x8f,0xae,0x4e,0xae,0x4c,0x86,0x8c,0xc7,0x8c,0xed,0x2d,0x8c,0x8e,0x05,0x64, + 0x2e,0xac,0x0d,0x8e,0xad,0xcc,0x87,0x03,0x5d,0x19,0xde,0x10,0x6a,0x21,0x9e,0x32, + 0x78,0xcc,0x60,0x19,0x16,0xe1,0x39,0x83,0xc7,0x7a,0xd0,0xe0,0xc1,0x9e,0x34,0xe0, + 0x12,0x96,0x26,0xe7,0x32,0x17,0xd6,0x06,0xc7,0x56,0x26,0xc7,0x63,0x2e,0xac,0x0d, + 0x8e,0xad,0x4c,0x8e,0x08,0x5d,0x19,0xde,0x54,0x1b,0x1c,0x9b,0xdc,0x10,0x69,0x39, + 0x9e,0x35,0x78,0xcc,0x60,0x19,0x16,0xe1,0xb1,0x1e,0x36,0x78,0xb0,0xa7,0x0d,0x86, + 0x20,0x4f,0x18,0x3c,0x64,0xf0,0xa8,0xc1,0xe3,0x06,0x43,0x0c,0x04,0x78,0xb6,0xe7, + 0x0d,0x46,0x44,0xec,0xc0,0x0e,0xf6,0xd0,0x0e,0x6e,0xd0,0x0e,0xef,0x40,0x0e,0xf5, + 0xc0,0x0e,0xe5,0xe0,0x06,0xe6,0xc0,0x0e,0xe1,0x70,0x0e,0xf3,0x30,0x45,0x08,0x86, + 0x11,0x0a,0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b,0xa4,0x03,0x39,0x94,0x83,0x3b,0xd0, + 0xc3,0x94,0xa0,0x18,0xb1,0x84,0x43,0x3a,0xc8,0x83,0x1b,0xd8,0x43,0x39,0xc8,0xc3, + 0x3c,0xa4,0xc3,0x3b,0xb8,0xc3,0x94,0xc0,0x18,0x41,0x85,0x43,0x3a,0xc8,0x83,0x1b, + 0xb0,0x43,0x38,0xb8,0xc3,0x39,0xd4,0x43,0x38,0x9c,0x43,0x39,0xfc,0x82,0x3d,0x94, + 0x83,0x3c,0xcc,0x43,0x3a,0xbc,0x83,0x3b,0x4c,0x09,0x90,0x11,0x53,0x38,0xa4,0x83, + 0x3c,0xb8,0xc1,0x38,0xbc,0x43,0x3b,0xc0,0x43,0x3a,0xb0,0x43,0x39,0xfc,0xc2,0x3b, + 0xc0,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x3c,0x4c,0x31,0x14,0xc6,0x81,0x24,0x6a, + 0x04,0x13,0x0e,0xe9,0x20,0x0f,0x6e,0x60,0x0e,0xf2,0x10,0x0e,0xe7,0xd0,0x0e,0xe5, + 0xe0,0x0e,0xf4,0x30,0x25,0x80,0x03,0x00,0x00,0x79,0x18,0x00,0x00,0x6d,0x00,0x00, + 0x00,0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84, + 0xc3,0x8c,0x42,0x80,0x07,0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed, + 0x10,0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66, + 0x30,0x05,0x3d,0x88,0x43,0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c, + 0x03,0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70, + 0x07,0x7a,0x70,0x03,0x76,0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90, + 0x0e,0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4, + 0x1d,0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83, + 0x3b,0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14, + 0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70, + 0x90,0x87,0x70,0x60,0x07,0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80, + 0x87,0x5f,0x08,0x87,0x71,0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0, + 0x0e,0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1, + 0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d, + 0xca,0x61,0x06,0xd6,0x90,0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39, + 0xb8,0xc3,0x38,0x94,0x43,0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc, + 0x82,0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x0c,0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70, + 0x83,0x74,0x68,0x07,0x78,0x60,0x87,0x74,0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53, + 0x0f,0xee,0x00,0x0f,0xf2,0x50,0x0e,0xe4,0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e, + 0xec,0x50,0x0e,0x33,0x20,0x28,0x1d,0xdc,0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c, + 0xdc,0x81,0x1e,0xdc,0xe0,0x1c,0xe4,0xe1,0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38, + 0xb0,0x43,0x3a,0x9c,0x83,0x3b,0xcc,0x50,0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37, + 0x60,0x87,0x77,0x78,0x07,0x78,0x98,0x51,0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33, + 0x1e,0x6a,0x1e,0xca,0x61,0x1c,0xe8,0x21,0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4, + 0xa1,0x1c,0xcc,0x21,0x1d,0xf0,0x61,0x06,0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0, + 0x43,0x3d,0xd0,0x43,0x39,0xfc,0xc2,0x3c,0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3, + 0x8c,0xc5,0x0a,0x87,0x79,0x98,0x87,0x77,0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07, + 0x72,0x00,0x00,0x00,0x00,0x71,0x20,0x00,0x00,0x08,0x00,0x00,0x00,0x16,0xb0,0x01, + 0x48,0xe4,0x4b,0x00,0xf3,0x2c,0xc4,0x3f,0x11,0xd7,0x44,0x45,0xc4,0x6f,0x0f,0x7e, + 0x85,0x17,0xb7,0x6d,0x00,0x05,0x03,0x20,0x0d,0x0d,0x00,0x00,0x00,0x61,0x20,0x00, + 0x00,0x0f,0x00,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00,0x06,0x00,0x00, + 0x00,0xc4,0x46,0x00,0xc6,0x12,0x80,0x80,0xd4,0x08,0x40,0x0d,0x90,0x98,0x01,0xa0, + 0x30,0x03,0x40,0x60,0x04,0x00,0x00,0x00,0x00,0x83,0x0c,0x8b,0x60,0x8c,0x18,0x28, + 0x42,0x40,0x29,0x49,0x50,0x20,0x86,0x60,0x01,0x23,0x9f,0xd9,0x06,0x23,0x00,0x32, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _sgl_vs_bytecode_metal_ios[3344] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x10,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x3b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x01,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x00,0x48,0x41,0x53,0x48,0x20,0x00,0x58,0xff,0x12,0x36,0x80,0x88,0xb5, + 0xf7,0x1c,0xc3,0xd5,0x31,0x57,0x18,0x26,0x26,0xf0,0x8c,0xd4,0x15,0xa2,0x55,0x8b, + 0xd5,0x3c,0x6b,0x11,0xbd,0x17,0x49,0x4a,0x02,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0x45,0x4e,0x44,0x54,0x37,0x00,0x00, + 0x00,0x56,0x41,0x54,0x54,0x22,0x00,0x03,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f, + 0x6e,0x00,0x00,0x80,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x00,0x01,0x80, + 0x63,0x6f,0x6c,0x6f,0x72,0x30,0x00,0x02,0x80,0x56,0x41,0x54,0x59,0x05,0x00,0x03, + 0x00,0x06,0x04,0x06,0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54, + 0xde,0xc0,0x17,0x0b,0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0xec,0x0b,0x00,0x00, + 0xff,0xff,0xff,0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0xf8,0x02,0x00,0x00, + 0x0b,0x82,0x20,0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91, + 0x41,0xc8,0x04,0x49,0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19, + 0x1e,0x04,0x8b,0x62,0x80,0x14,0x45,0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14, + 0x38,0x08,0x18,0x49,0x0a,0x32,0x44,0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80, + 0x0c,0x19,0x21,0x72,0x24,0x07,0xc8,0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0, + 0x01,0x00,0x00,0x00,0x51,0x18,0x00,0x00,0x82,0x00,0x00,0x00,0x1b,0xc8,0x25,0xf8, + 0xff,0xff,0xff,0xff,0x01,0x90,0x80,0x8a,0x18,0x87,0x77,0x90,0x07,0x79,0x28,0x87, + 0x71,0xa0,0x07,0x76,0xc8,0x87,0x36,0x90,0x87,0x77,0xa8,0x07,0x77,0x20,0x87,0x72, + 0x20,0x87,0x36,0x20,0x87,0x74,0xb0,0x87,0x74,0x20,0x87,0x72,0x68,0x83,0x79,0x88, + 0x07,0x79,0xa0,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07, + 0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1,0x1c,0x00,0x82,0x1c,0xd2,0x61,0x1e,0xc2,0x41, + 0x1c,0xd8,0xa1,0x1c,0xda,0x80,0x1e,0xc2,0x21,0x1d,0xd8,0xa1,0x0d,0xc6,0x21,0x1c, + 0xd8,0x81,0x1d,0xe6,0x01,0x30,0x87,0x70,0x60,0x87,0x79,0x28,0x07,0x80,0x60,0x87, + 0x72,0x98,0x87,0x79,0x68,0x03,0x78,0x90,0x87,0x72,0x18,0x87,0x74,0x98,0x87,0x72, + 0x68,0x03,0x73,0x80,0x87,0x76,0x08,0x07,0x72,0x00,0xcc,0x21,0x1c,0xd8,0x61,0x1e, + 0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0xc0,0x1c,0xe4,0x21,0x1c,0xda,0xa1,0x1c,0xda, + 0x00,0x1e,0xde,0x21,0x1d,0xdc,0x81,0x1e,0xca,0x41,0x1e,0xda,0xa0,0x1c,0xd8,0x21, + 0x1d,0xda,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x06,0x77,0x78,0x87,0x36,0x30, + 0x07,0x79,0x08,0x87,0x76,0x28,0x87,0x36,0x80,0x87,0x77,0x48,0x07,0x77,0xa0,0x87, + 0x72,0x90,0x87,0x36,0x28,0x07,0x76,0x48,0x87,0x76,0x68,0x03,0x77,0x78,0x07,0x77, + 0x68,0x03,0x76,0x28,0x87,0x70,0x30,0x07,0x80,0x70,0x87,0x77,0x68,0x83,0x74,0x70, + 0x07,0x73,0x98,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07, + 0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0x40,0x1d,0xea,0xa1, + 0x1d,0xe0,0xa1,0x0d,0xe8,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x1e,0x00,0x73,0x08, + 0x07,0x76,0x98,0x87,0x72,0x00,0x08,0x77,0x78,0x87,0x36,0x70,0x87,0x70,0x70,0x87, + 0x79,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41, + 0x1e,0xea,0xa1,0x1c,0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xe6,0x21,0x1d,0xce,0xc1,0x1d, + 0xca,0x81,0x1c,0xda,0x40,0x1f,0xca,0x41,0x1e,0xde,0x61,0x1e,0xda,0xc0,0x1c,0xe0, + 0xa1,0x0d,0xda,0x21,0x1c,0xe8,0x01,0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x80, + 0x70,0x87,0x77,0x68,0x03,0x7a,0x90,0x87,0x70,0x80,0x07,0x78,0x48,0x07,0x77,0x38, + 0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00, + 0x62,0x1e,0xe8,0x21,0x1c,0xc6,0x61,0x1d,0xda,0x00,0x1e,0xe4,0xe1,0x1d,0xe8,0xa1, + 0x1c,0xc6,0x81,0x1e,0xde,0x41,0x1e,0xda,0x40,0x1c,0xea,0xc1,0x1c,0xcc,0xa1,0x1c, + 0xe4,0xa1,0x0d,0xe6,0x21,0x1d,0xf4,0xa1,0x1c,0x00,0x3c,0x00,0x88,0x7a,0x70,0x87, + 0x79,0x08,0x07,0x73,0x28,0x87,0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a, + 0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xea,0x61,0x1e,0xca,0xa1,0x0d, + 0xe6,0xe1,0x1d,0xcc,0x81,0x1e,0xda,0xc0,0x1c,0xd8,0xe1,0x1d,0xc2,0x81,0x1e,0x00, + 0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x36,0x20,0x02,0x01,0x24,0xc0,0x02,0x54, + 0x00,0x00,0x00,0x00,0x49,0x18,0x00,0x00,0x01,0x00,0x00,0x00,0x13,0x84,0x40,0x00, + 0x89,0x20,0x00,0x00,0x23,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85,0x04, + 0x93,0x22,0xa4,0x84,0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a,0x8c, + 0x0b,0x84,0xa4,0x4c,0x10,0x44,0x33,0x00,0xc3,0x08,0x04,0x70,0x90,0x34,0x45,0x94, + 0x30,0xf9,0x0c,0x80,0x34,0xf4,0xef,0x50,0x13,0x1a,0x42,0x08,0xc3,0x08,0x02,0x90, + 0x04,0x61,0x26,0x6a,0x1e,0xe8,0x41,0x1e,0xea,0x61,0x1c,0xe8,0xc1,0x0d,0xda,0xa1, + 0x1c,0xe8,0x21,0x1c,0xd8,0x41,0x0f,0xf4,0xa0,0x1d,0xc2,0x81,0x1e,0xe4,0x21,0x1d, + 0xf0,0x01,0x05,0xe4,0x20,0x69,0x8a,0x28,0x61,0xf2,0x2b,0xe9,0x7f,0x80,0x08,0x60, + 0x24,0x24,0x94,0x32,0x88,0x60,0x08,0xa5,0x10,0x61,0x84,0x43,0x68,0x20,0x60,0x8e, + 0x00,0x0c,0x52,0x60,0xcd,0x11,0x80,0xc2,0x20,0x42,0x20,0x0c,0x23,0x10,0xcb,0x08, + 0x00,0x00,0x00,0x00,0x13,0xa8,0x70,0x48,0x07,0x79,0xb0,0x03,0x3a,0x68,0x83,0x70, + 0x80,0x07,0x78,0x60,0x87,0x72,0x68,0x83,0x74,0x78,0x87,0x79,0xc8,0x03,0x37,0x80, + 0x03,0x37,0x80,0x83,0x0d,0xb7,0x51,0x0e,0x6d,0x00,0x0f,0x7a,0x60,0x07,0x74,0xa0, + 0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xe9,0x10,0x07,0x7a,0x80,0x07, + 0x7a,0x80,0x07,0x6d,0x90,0x0e,0x78,0xa0,0x07,0x78,0xa0,0x07,0x78,0xd0,0x06,0xe9, + 0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xd0,0x06,0xe9,0x30, + 0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xe9,0x60,0x07, + 0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xe6,0x30,0x07,0x72, + 0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xe6,0x60,0x07,0x74,0xa0, + 0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x10,0x07,0x76,0xa0,0x07, + 0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xd0,0x06,0xf6,0x20,0x07,0x74,0xa0,0x07,0x73, + 0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xf6,0x30,0x07,0x72,0xa0,0x07,0x73,0x20, + 0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xf6,0x40,0x07,0x78,0xa0,0x07,0x76,0x40,0x07, + 0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a, + 0x60,0x07,0x74,0xd0,0x06,0xf6,0x90,0x07,0x76,0xa0,0x07,0x71,0x20,0x07,0x78,0xa0, + 0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07, + 0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x6d,0x60,0x0f,0x71,0x90,0x07,0x72, + 0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07,0x76,0xd0,0x06,0xf6,0x20, + 0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07, + 0x6d,0x60,0x0f,0x75,0x10,0x07,0x72,0xa0,0x07,0x75,0x10,0x07,0x72,0xa0,0x07,0x75, + 0x10,0x07,0x72,0xd0,0x06,0xf6,0x10,0x07,0x70,0x20,0x07,0x74,0xa0,0x07,0x71,0x00, + 0x07,0x72,0x40,0x07,0x7a,0x10,0x07,0x70,0x20,0x07,0x74,0xd0,0x06,0xee,0x80,0x07, + 0x7a,0x10,0x07,0x76,0xa0,0x07,0x73,0x20,0x07,0x43,0x98,0x04,0x00,0x80,0x00,0x00, + 0x00,0x00,0x00,0x80,0x2c,0x10,0x00,0x00,0x0b,0x00,0x00,0x00,0x32,0x1e,0x98,0x10, + 0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x5a,0x25,0x30,0x02,0x50, + 0x04,0x05,0x18,0x50,0x80,0x02,0x85,0x50,0x10,0x65,0x50,0x20,0xd4,0x46,0x00,0x88, + 0x8d,0x25,0x34,0x05,0x00,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x19,0x01,0x00,0x00, + 0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32,0xb9,0xb9,0xb4,0x37,0xb7, + 0x21,0xc6,0x32,0x28,0x00,0xb3,0x50,0xb9,0x1b,0x43,0x0b,0x93,0xfb,0x9a,0x4b,0xd3, + 0x2b,0x1b,0x62,0x2c,0x81,0x22,0x2c,0x06,0xd9,0x20,0x08,0x0e,0x8e,0xad,0x0c,0x84, + 0x89,0xc9,0xaa,0x09,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd,0x0d,0x64,0x26,0x07,0x46, + 0xc6,0xc5,0xe6,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd,0xac,0xac,0x65,0x26,0x07, + 0x46,0xc6,0xc5,0xe6,0x26,0x65,0x88,0xa0,0x10,0x43,0x8c,0x25,0x58,0x8e,0x45,0x60, + 0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x51,0x8e,0x25,0x58,0x84,0x45,0xe0,0x16,0x96, + 0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42,0x56,0xe6,0xf6,0x26,0xd7, + 0x36,0xf7,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x50,0x12,0x72,0x61,0x69, + 0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66,0x61,0x73,0x74,0x5f,0x6d, + 0x61,0x74,0x68,0x5f,0x65,0x6e,0x61,0x62,0x6c,0x65,0x43,0x04,0x65,0x21,0x19,0x84, + 0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9,0x98,0xc9,0x85,0xb5,0x95, + 0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d,0xa1,0x7d,0x95,0xb9,0x85, + 0x89,0xb1,0x95,0x0d,0x11,0x94,0x86,0x61,0x10,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06, + 0x97,0xc6,0x56,0xe6,0xe2,0x16,0x46,0x97,0x66,0x57,0xf6,0x45,0xf6,0x56,0x27,0xc6, + 0x56,0xf6,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44,0x50,0x1e,0x92,0x41,0x58, + 0x9a,0x9c,0xcb,0xd8,0x5b,0x1b,0x5c,0x1a,0x5b,0x99,0x8b,0x5b,0x18,0x5d,0x9a,0x5d, + 0xd9,0x17,0xdb,0x9b,0xdb,0xd9,0x17,0xdb,0x9b,0xdb,0xd9,0x17,0x59,0xda,0x5c,0x98, + 0x18,0x5b,0xd9,0x10,0x41,0x89,0x78,0x06,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70, + 0x69,0x6c,0x65,0x2e,0x6e,0x61,0x74,0x69,0x76,0x65,0x5f,0x77,0x69,0x64,0x65,0x5f, + 0x76,0x65,0x63,0x74,0x6f,0x72,0x73,0x5f,0x64,0x69,0x73,0x61,0x62,0x6c,0x65,0x43, + 0x04,0x65,0x62,0x14,0x96,0x26,0xe7,0x62,0x57,0x26,0x47,0x57,0x86,0xf7,0xf5,0x56, + 0x47,0x07,0x57,0x47,0xc7,0xa5,0x6e,0xae,0x4c,0x0e,0x85,0xed,0x6d,0xcc,0x0d,0x26, + 0x85,0x51,0x58,0x9a,0x9c,0x4b,0x98,0xdc,0xd9,0x17,0x5d,0x1e,0x5c,0xd9,0x97,0x5b, + 0x58,0x5b,0x19,0x0d,0x33,0xb6,0xb7,0x30,0x3a,0x1a,0x32,0x61,0x69,0x72,0x2e,0x61, + 0x72,0x67,0x5f,0x6e,0x61,0x6d,0x65,0x14,0xea,0xec,0x86,0x30,0x4a,0xa5,0x58,0xca, + 0xa5,0x60,0x4a,0xa6,0x68,0x5c,0xea,0xe6,0xca,0xe4,0x50,0xd8,0xde,0xc6,0xdc,0x62, + 0x52,0x58,0x8c,0xbd,0xb1,0xbd,0xc9,0x0d,0x61,0x94,0x4a,0xe1,0x94,0x4b,0xc1,0x94, + 0x4c,0xe9,0xc8,0x84,0xa5,0xc9,0xb9,0xc0,0xbd,0xcd,0xa5,0xd1,0xa5,0xbd,0xb9,0x71, + 0x39,0x63,0xfb,0x82,0x7a,0x9b,0x4b,0xa3,0x4b,0x7b,0x73,0x1b,0xa2,0x28,0x9f,0x72, + 0x29,0x98,0x92,0x29,0x60,0x30,0xc4,0x50,0x36,0xc5,0x53,0xc2,0x80,0x50,0x58,0x9a, + 0x9c,0x8b,0x5d,0x99,0x1c,0x5d,0x19,0xde,0x57,0x9a,0x1b,0x5c,0x1d,0x1d,0xa5,0xb0, + 0x34,0x39,0x17,0xb6,0xb7,0xb1,0x30,0xba,0xb4,0x37,0xb7,0xaf,0x34,0x37,0xb2,0x32, + 0x3c,0x66,0x67,0x65,0x6e,0x65,0x72,0x61,0x74,0x65,0x64,0x28,0x38,0x70,0x6f,0x73, + 0x69,0x74,0x69,0x6f,0x6e,0x44,0x76,0x34,0x5f,0x66,0x29,0x44,0xe0,0xde,0xe6,0xd2, + 0xe8,0xd2,0xde,0xdc,0x86,0x50,0x8b,0xa0,0x8c,0x81,0x42,0x06,0x8b,0xb0,0x04,0x4a, + 0x19,0x28,0x97,0x82,0x29,0x99,0x62,0x06,0xd4,0xce,0xca,0xdc,0xca,0xe4,0xc2,0xe8, + 0xca,0xc8,0x50,0x72,0xe8,0xca,0xf0,0xc6,0xde,0xde,0xe4,0xc8,0x60,0x88,0xec,0x64, + 0xbe,0xcc,0x52,0x68,0x98,0xb1,0xbd,0x85,0xd1,0xc9,0x30,0xa1,0x2b,0xc3,0x1b,0x7b, + 0x7b,0x93,0x23,0x83,0x19,0x42,0x2d,0x81,0x32,0x06,0x0a,0x19,0x2c,0xc1,0x12,0x28, + 0x68,0xa0,0x5c,0x4a,0x1a,0x28,0x99,0xa2,0x06,0xbc,0xce,0xca,0xdc,0xca,0xe4,0xc2, + 0xe8,0xca,0xc8,0x50,0x6c,0xc6,0xde,0xd8,0xde,0xe4,0x60,0x88,0xec,0x68,0xbe,0xcc, + 0x52,0x68,0x8c,0xbd,0xb1,0xbd,0xc9,0xc1,0x0c,0xa1,0x96,0x41,0x19,0x03,0x85,0x0c, + 0x96,0x61,0x09,0x14,0x36,0x50,0x2e,0x05,0x53,0x32,0xa5,0x0d,0xa8,0x84,0xa5,0xc9, + 0xb9,0x88,0xd5,0x99,0x99,0x95,0xc9,0xf1,0x09,0x4b,0x93,0x73,0x11,0xab,0x33,0x33, + 0x2b,0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x23,0x12,0x96,0x26,0xe7,0x22,0x57,0x16,0x46, + 0x46,0x2a,0x2c,0x4d,0xce,0x65,0x8e,0x4e,0xae,0x6e,0x8c,0xee,0x8b,0x2e,0x0f,0xae, + 0xec,0x2b,0xcd,0xcd,0xec,0x8d,0x88,0x19,0xdb,0x5b,0x18,0x1d,0x0d,0x1e,0x0d,0x87, + 0x36,0x3b,0x38,0x0a,0x74,0x6d,0x43,0xa8,0x45,0x58,0x88,0x45,0x50,0xe6,0x40,0xa1, + 0x83,0x85,0x58,0x88,0x45,0x50,0xe6,0x40,0xa9,0x03,0x46,0x61,0x69,0x72,0x2e,0x61, + 0x72,0x67,0x5f,0x74,0x79,0x70,0x65,0x5f,0x73,0x69,0x7a,0x65,0xbc,0xc2,0xd2,0xe4, + 0x5c,0xc2,0xe4,0xce,0xbe,0xe8,0xf2,0xe0,0xca,0xbe,0xc2,0xd8,0xd2,0xce,0xdc,0xbe, + 0xe6,0xd2,0xf4,0xca,0x98,0xd8,0xcd,0x7d,0xc1,0x85,0xc9,0x85,0xb5,0xcd,0x71,0xf8, + 0x92,0x81,0x19,0x42,0x06,0x0b,0xa2,0xbc,0x81,0x02,0x07,0x4b,0xa1,0x90,0xc1,0x22, + 0x2c,0x81,0x12,0x07,0x8a,0x1c,0x28,0x76,0xa0,0xdc,0xc1,0x52,0x28,0x78,0xb0,0x24, + 0xca,0xa5,0xe4,0x81,0x92,0x29,0x7a,0x30,0x04,0x51,0xce,0x40,0x59,0x03,0xc5,0x0d, + 0x94,0x3d,0x18,0x62,0x24,0x80,0x22,0x06,0x0a,0x1f,0xf0,0x79,0x6b,0x73,0x4b,0x83, + 0x7b,0xa3,0x2b,0x73,0xa3,0x03,0x19,0x43,0x0b,0x93,0xe3,0x33,0x95,0xd6,0x06,0xc7, + 0x56,0x06,0x32,0xb4,0xb2,0x02,0x42,0x25,0x14,0x14,0x34,0x44,0x50,0xfe,0x60,0x88, + 0xa1,0xf8,0x81,0x02,0x0a,0x8d,0x32,0xc4,0x50,0x42,0x41,0x09,0x85,0x46,0x19,0x11, + 0xb1,0x03,0x3b,0xd8,0x43,0x3b,0xb8,0x41,0x3b,0xbc,0x03,0x39,0xd4,0x03,0x3b,0x94, + 0x83,0x1b,0x98,0x03,0x3b,0x84,0xc3,0x39,0xcc,0xc3,0x14,0x21,0x18,0x46,0x28,0xec, + 0xc0,0x0e,0xf6,0xd0,0x0e,0x6e,0x90,0x0e,0xe4,0x50,0x0e,0xee,0x40,0x0f,0x53,0x82, + 0x62,0xc4,0x12,0x0e,0xe9,0x20,0x0f,0x6e,0x60,0x0f,0xe5,0x20,0x0f,0xf3,0x90,0x0e, + 0xef,0xe0,0x0e,0x53,0x02,0x63,0x04,0x15,0x0e,0xe9,0x20,0x0f,0x6e,0xc0,0x0e,0xe1, + 0xe0,0x0e,0xe7,0x50,0x0f,0xe1,0x70,0x0e,0xe5,0xf0,0x0b,0xf6,0x50,0x0e,0xf2,0x30, + 0x0f,0xe9,0xf0,0x0e,0xee,0x30,0x25,0x40,0x46,0x4c,0xe1,0x90,0x0e,0xf2,0xe0,0x06, + 0xe3,0xf0,0x0e,0xed,0x00,0x0f,0xe9,0xc0,0x0e,0xe5,0xf0,0x0b,0xef,0x00,0x0f,0xf4, + 0x90,0x0e,0xef,0xe0,0x0e,0xf3,0x30,0xc5,0x50,0x18,0x07,0x92,0xa8,0x11,0x4a,0x38, + 0xa4,0x83,0x3c,0xb8,0x81,0x3d,0x94,0x83,0x3c,0xd0,0x43,0x39,0xe0,0xc3,0x94,0xa0, + 0x0f,0x00,0x00,0x00,0x79,0x18,0x00,0x00,0x6d,0x00,0x00,0x00,0x33,0x08,0x80,0x1c, + 0xc4,0xe1,0x1c,0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3,0x8c,0x42,0x80,0x07, + 0x79,0x78,0x07,0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80,0x0e, + 0x33,0x0c,0x42,0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88,0x43, + 0x38,0x84,0x83,0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78,0x8c, + 0x74,0x70,0x07,0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07,0x7a,0x70,0x03,0x76, + 0x78,0x87,0x70,0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f,0x6e, + 0x30,0x0f,0xe3,0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c,0xd8, + 0x21,0x1d,0xc2,0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39,0xb4, + 0x03,0x3c,0xbc,0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b,0x68, + 0x07,0x37,0x68,0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07, + 0x76,0x28,0x07,0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87,0x5f,0x08,0x87,0x71, + 0x18,0x87,0x72,0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e,0xf5, + 0xc0,0x0e,0xec,0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c,0xe4, + 0xa1,0x1c,0xdc,0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6,0x90, + 0x43,0x39,0xc8,0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94,0x43, + 0x38,0x88,0x03,0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03,0x3b, + 0xb0,0xc3,0x0c,0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70,0x83,0x74,0x68,0x07,0x78, + 0x60,0x87,0x74,0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53,0x0f,0xee,0x00,0x0f,0xf2, + 0x50,0x0e,0xe4,0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e,0xec,0x50,0x0e,0x33,0x20, + 0x28,0x1d,0xdc,0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c,0xdc,0x81,0x1e,0xdc,0xe0, + 0x1c,0xe4,0xe1,0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38,0xb0,0x43,0x3a,0x9c,0x83, + 0x3b,0xcc,0x50,0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x60,0x87,0x77,0x78,0x07, + 0x78,0x98,0x51,0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33,0x1e,0x6a,0x1e,0xca,0x61, + 0x1c,0xe8,0x21,0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4,0xa1,0x1c,0xcc,0x21,0x1d, + 0xf0,0x61,0x06,0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0,0x43,0x3d,0xd0,0x43,0x39, + 0xfc,0xc2,0x3c,0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3,0x8c,0xc5,0x0a,0x87,0x79, + 0x98,0x87,0x77,0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07,0x72,0x00,0x00,0x00,0x00, + 0x71,0x20,0x00,0x00,0x02,0x00,0x00,0x00,0x06,0x50,0x30,0x00,0xd2,0xd0,0x00,0x00, + 0x61,0x20,0x00,0x00,0x3d,0x00,0x00,0x00,0x13,0x04,0x41,0x2c,0x10,0x00,0x00,0x00, + 0x09,0x00,0x00,0x00,0xf4,0xc6,0x22,0x86,0x61,0x18,0xc6,0x22,0x04,0x41,0x10,0xc6, + 0x22,0x82,0x20,0x08,0xa8,0x95,0x40,0x19,0x14,0x01,0xbd,0x11,0x00,0x1a,0x33,0x00, + 0x24,0x66,0x00,0x28,0xcc,0x00,0x00,0x00,0xe3,0x15,0x0b,0x84,0x61,0x10,0x05,0x65, + 0x90,0x01,0x1a,0x0c,0x13,0x02,0xf9,0x8c,0x57,0x3c,0x14,0xc7,0x2d,0x14,0x94,0x41, + 0x06,0xea,0x70,0x4c,0x08,0xe4,0x63,0x41,0x01,0x9f,0xf1,0x0a,0x2a,0x0b,0x83,0x30, + 0x70,0x28,0x28,0x83,0x0c,0x19,0x43,0x99,0x10,0xc8,0xc7,0x8a,0x00,0x3e,0xe3,0x15, + 0x99,0x67,0x06,0x66,0x40,0x51,0x50,0x06,0x19,0xbc,0x48,0x33,0x21,0x90,0x8f,0x15, + 0x01,0x7c,0xc6,0x2b,0xbc,0x31,0x60,0x83,0x35,0x18,0x03,0x0a,0xca,0x20,0x83,0x18, + 0x60,0x99,0x09,0x81,0x7c,0xc6,0x2b,0xc4,0xe0,0x0c,0xe0,0xe0,0x0d,0x3c,0x0a,0xca, + 0x20,0x83,0x19,0x70,0x61,0x60,0x42,0x20,0x1f,0x0b,0x0a,0xf8,0x8c,0x57,0x9c,0x01, + 0x1b,0xd4,0x01,0x1d,0x88,0x01,0x05,0xc5,0x86,0x00,0x3e,0xb3,0x0d,0x61,0x10,0x00, + 0xb3,0x0d,0x01,0x1b,0x04,0xb3,0x0d,0xc1,0x23,0x64,0x10,0x10,0x03,0x00,0x00,0x00, + 0x09,0x00,0x00,0x00,0x5b,0x86,0x20,0x10,0x85,0x2d,0x43,0x11,0x88,0xc2,0x96,0x41, + 0x09,0x44,0x61,0xcb,0xf0,0x04,0xa2,0xb0,0x65,0xa0,0x02,0x51,0xd8,0x32,0x60,0x81, + 0x28,0x6c,0x19,0xba,0x40,0x14,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +static const uint8_t _sgl_fs_bytecode_metal_ios[2893] = { + 0x4d,0x54,0x4c,0x42,0x01,0x00,0x02,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x4d,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x58,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x6d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xcd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xd5,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xdd,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x70,0x0a,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x6d,0x00,0x00,0x00, + 0x4e,0x41,0x4d,0x45,0x06,0x00,0x6d,0x61,0x69,0x6e,0x30,0x00,0x54,0x59,0x50,0x45, + 0x01,0x00,0x01,0x48,0x41,0x53,0x48,0x20,0x00,0xa8,0xe6,0x5c,0x09,0x1c,0x98,0x6d, + 0xf9,0x59,0x27,0x6f,0xf0,0xd6,0x41,0x4d,0xf9,0x93,0x2f,0x51,0x99,0xc5,0xc8,0xc7, + 0xad,0x1e,0x89,0xca,0xfe,0x25,0x89,0x6a,0x2a,0x4f,0x46,0x46,0x54,0x18,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x56,0x45,0x52,0x53,0x08,0x00,0x01,0x00,0x08, + 0x00,0x01,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0x45,0x4e,0x44,0x54,0x04,0x00,0x00, + 0x00,0x45,0x4e,0x44,0x54,0x04,0x00,0x00,0x00,0x45,0x4e,0x44,0x54,0xde,0xc0,0x17, + 0x0b,0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x5c,0x0a,0x00,0x00,0xff,0xff,0xff, + 0xff,0x42,0x43,0xc0,0xde,0x21,0x0c,0x00,0x00,0x94,0x02,0x00,0x00,0x0b,0x82,0x20, + 0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x07,0x81,0x23,0x91,0x41,0xc8,0x04, + 0x49,0x06,0x10,0x32,0x39,0x92,0x01,0x84,0x0c,0x25,0x05,0x08,0x19,0x1e,0x04,0x8b, + 0x62,0x80,0x14,0x45,0x02,0x42,0x92,0x0b,0x42,0xa4,0x10,0x32,0x14,0x38,0x08,0x18, + 0x49,0x0a,0x32,0x44,0x24,0x48,0x0a,0x90,0x21,0x23,0xc4,0x52,0x80,0x0c,0x19,0x21, + 0x72,0x24,0x07,0xc8,0x48,0x11,0x62,0xa8,0xa0,0xa8,0x40,0xc6,0xf0,0x01,0x00,0x00, + 0x00,0x51,0x18,0x00,0x00,0x89,0x00,0x00,0x00,0x1b,0xcc,0x25,0xf8,0xff,0xff,0xff, + 0xff,0x01,0x60,0x00,0x09,0xa8,0x88,0x71,0x78,0x07,0x79,0x90,0x87,0x72,0x18,0x07, + 0x7a,0x60,0x87,0x7c,0x68,0x03,0x79,0x78,0x87,0x7a,0x70,0x07,0x72,0x28,0x07,0x72, + 0x68,0x03,0x72,0x48,0x07,0x7b,0x48,0x07,0x72,0x28,0x87,0x36,0x98,0x87,0x78,0x90, + 0x07,0x7a,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xcc, + 0x21,0x1c,0xd8,0x61,0x1e,0xca,0x01,0x20,0xc8,0x21,0x1d,0xe6,0x21,0x1c,0xc4,0x81, + 0x1d,0xca,0xa1,0x0d,0xe8,0x21,0x1c,0xd2,0x81,0x1d,0xda,0x60,0x1c,0xc2,0x81,0x1d, + 0xd8,0x61,0x1e,0x00,0x73,0x08,0x07,0x76,0x98,0x87,0x72,0x00,0x08,0x76,0x28,0x87, + 0x79,0x98,0x87,0x36,0x80,0x07,0x79,0x28,0x87,0x71,0x48,0x87,0x79,0x28,0x87,0x36, + 0x30,0x07,0x78,0x68,0x87,0x70,0x20,0x07,0xc0,0x1c,0xc2,0x81,0x1d,0xe6,0xa1,0x1c, + 0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xcc,0x41,0x1e,0xc2,0xa1,0x1d,0xca,0xa1,0x0d,0xe0, + 0xe1,0x1d,0xd2,0xc1,0x1d,0xe8,0xa1,0x1c,0xe4,0xa1,0x0d,0xca,0x81,0x1d,0xd2,0xa1, + 0x1d,0x00,0x7a,0x90,0x87,0x7a,0x28,0x07,0x60,0x70,0x87,0x77,0x68,0x03,0x73,0x90, + 0x87,0x70,0x68,0x87,0x72,0x68,0x03,0x78,0x78,0x87,0x74,0x70,0x07,0x7a,0x28,0x07, + 0x79,0x68,0x83,0x72,0x60,0x87,0x74,0x68,0x87,0x36,0x70,0x87,0x77,0x70,0x87,0x36, + 0x60,0x87,0x72,0x08,0x07,0x73,0x00,0x08,0x77,0x78,0x87,0x36,0x48,0x07,0x77,0x30, + 0x87,0x79,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74,0x00,0xe8, + 0x41,0x1e,0xea,0xa1,0x1c,0x00,0xc2,0x1d,0xde,0xa1,0x0d,0xd4,0xa1,0x1e,0xda,0x01, + 0x1e,0xda,0x80,0x1e,0xc2,0x41,0x1c,0xd8,0xa1,0x1c,0xe6,0x01,0x30,0x87,0x70,0x60, + 0x87,0x79,0x28,0x07,0x80,0x70,0x87,0x77,0x68,0x03,0x77,0x08,0x07,0x77,0x98,0x87, + 0x36,0x30,0x07,0x78,0x68,0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1, + 0x1e,0xca,0x01,0x20,0xdc,0xe1,0x1d,0xda,0x60,0x1e,0xd2,0xe1,0x1c,0xdc,0xa1,0x1c, + 0xc8,0xa1,0x0d,0xf4,0xa1,0x1c,0xe4,0xe1,0x1d,0xe6,0xa1,0x0d,0xcc,0x01,0x1e,0xda, + 0xa0,0x1d,0xc2,0x81,0x1e,0xd0,0x01,0xa0,0x07,0x79,0xa8,0x87,0x72,0x00,0x08,0x77, + 0x78,0x87,0x36,0xa0,0x07,0x79,0x08,0x07,0x78,0x80,0x87,0x74,0x70,0x87,0x73,0x68, + 0x83,0x76,0x08,0x07,0x7a,0x40,0x07,0x80,0x1e,0xe4,0xa1,0x1e,0xca,0x01,0x20,0xe6, + 0x81,0x1e,0xc2,0x61,0x1c,0xd6,0xa1,0x0d,0xe0,0x41,0x1e,0xde,0x81,0x1e,0xca,0x61, + 0x1c,0xe8,0xe1,0x1d,0xe4,0xa1,0x0d,0xc4,0xa1,0x1e,0xcc,0xc1,0x1c,0xca,0x41,0x1e, + 0xda,0x60,0x1e,0xd2,0x41,0x1f,0xca,0x01,0xc0,0x03,0x80,0xa8,0x07,0x77,0x98,0x87, + 0x70,0x30,0x87,0x72,0x68,0x03,0x73,0x80,0x87,0x36,0x68,0x87,0x70,0xa0,0x07,0x74, + 0x00,0xe8,0x41,0x1e,0xea,0xa1,0x1c,0x00,0xa2,0x1e,0xe6,0xa1,0x1c,0xda,0x60,0x1e, + 0xde,0xc1,0x1c,0xe8,0xa1,0x0d,0xcc,0x81,0x1d,0xde,0x21,0x1c,0xe8,0x01,0x30,0x87, + 0x70,0x60,0x87,0x79,0x28,0x07,0x60,0x83,0x21,0x0c,0xc0,0x02,0x54,0x1b,0x8c,0x81, + 0x00,0x16,0xa0,0xda,0x80,0x10,0xff,0xff,0xff,0xff,0x3f,0x00,0x0c,0x20,0x01,0xd5, + 0x06,0xa3,0x08,0x80,0x05,0xa8,0x36,0x18,0x86,0x00,0x2c,0x40,0x05,0x49,0x18,0x00, + 0x00,0x03,0x00,0x00,0x00,0x13,0x86,0x40,0x18,0x26,0x0c,0x44,0x61,0x00,0x00,0x00, + 0x00,0x89,0x20,0x00,0x00,0x20,0x00,0x00,0x00,0x32,0x22,0x48,0x09,0x20,0x64,0x85, + 0x04,0x93,0x22,0xa4,0x84,0x04,0x93,0x22,0xe3,0x84,0xa1,0x90,0x14,0x12,0x4c,0x8a, + 0x8c,0x0b,0x84,0xa4,0x4c,0x10,0x48,0x33,0x00,0xc3,0x08,0x04,0x70,0x90,0x34,0x45, + 0x94,0x30,0xf9,0x0c,0x80,0x34,0xf4,0xef,0x50,0x13,0x0a,0xc2,0x51,0xd2,0x14,0x51, + 0xc2,0xe4,0xff,0x13,0x71,0x4d,0x54,0x44,0xfc,0xf6,0xf0,0x4f,0x63,0x04,0xc0,0x20, + 0xc2,0x10,0x5c,0x24,0x4d,0x11,0x25,0x4c,0xfe,0x2f,0x01,0xcc,0xb3,0x10,0xd1,0x3f, + 0x8d,0x11,0x00,0x83,0x08,0x85,0x50,0x0a,0x11,0x02,0x31,0x74,0x86,0x11,0x04,0x60, + 0x8e,0x20,0x98,0x23,0x00,0x83,0x61,0x04,0x61,0x29,0x48,0x20,0x26,0x29,0xa6,0x00, + 0xb5,0x81,0x80,0x14,0x58,0x23,0x00,0x00,0x00,0x13,0xa8,0x70,0x48,0x07,0x79,0xb0, + 0x03,0x3a,0x68,0x83,0x70,0x80,0x07,0x78,0x60,0x87,0x72,0x68,0x83,0x74,0x78,0x87, + 0x79,0xc8,0x03,0x37,0x80,0x03,0x37,0x80,0x83,0x0d,0xb7,0x51,0x0e,0x6d,0x00,0x0f, + 0x7a,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xe9, + 0x10,0x07,0x7a,0x80,0x07,0x7a,0x80,0x07,0x6d,0x90,0x0e,0x78,0xa0,0x07,0x78,0xa0, + 0x07,0x78,0xd0,0x06,0xe9,0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x7a,0x10,0x07, + 0x76,0xd0,0x06,0xe9,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72, + 0xd0,0x06,0xe9,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0, + 0x06,0xe6,0x30,0x07,0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06, + 0xe6,0x60,0x07,0x74,0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6, + 0x10,0x07,0x76,0xa0,0x07,0x71,0x60,0x07,0x7a,0x10,0x07,0x76,0xd0,0x06,0xf6,0x20, + 0x07,0x74,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xf6,0x30,0x07, + 0x72,0xa0,0x07,0x73,0x20,0x07,0x7a,0x30,0x07,0x72,0xd0,0x06,0xf6,0x40,0x07,0x78, + 0xa0,0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x60,0x07,0x74,0xa0, + 0x07,0x76,0x40,0x07,0x7a,0x60,0x07,0x74,0xd0,0x06,0xf6,0x90,0x07,0x76,0xa0,0x07, + 0x71,0x20,0x07,0x78,0xa0,0x07,0x71,0x20,0x07,0x78,0xd0,0x06,0xf6,0x10,0x07,0x72, + 0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x7a,0x10,0x07,0x72,0x80,0x07,0x6d,0x60, + 0x0f,0x71,0x90,0x07,0x72,0xa0,0x07,0x72,0x50,0x07,0x76,0xa0,0x07,0x72,0x50,0x07, + 0x76,0xd0,0x06,0xf6,0x20,0x07,0x75,0x60,0x07,0x7a,0x20,0x07,0x75,0x60,0x07,0x7a, + 0x20,0x07,0x75,0x60,0x07,0x6d,0x60,0x0f,0x75,0x10,0x07,0x72,0xa0,0x07,0x75,0x10, + 0x07,0x72,0xa0,0x07,0x75,0x10,0x07,0x72,0xd0,0x06,0xf6,0x10,0x07,0x70,0x20,0x07, + 0x74,0xa0,0x07,0x71,0x00,0x07,0x72,0x40,0x07,0x7a,0x10,0x07,0x70,0x20,0x07,0x74, + 0xd0,0x06,0xee,0x80,0x07,0x7a,0x10,0x07,0x76,0xa0,0x07,0x73,0x20,0x07,0x43,0x18, + 0x04,0x00,0x80,0x00,0x00,0x00,0x00,0x00,0x80,0x21,0x8c,0x03,0x04,0x80,0x00,0x00, + 0x00,0x00,0x00,0x40,0x16,0x08,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x32,0x1e,0x98, + 0x10,0x19,0x11,0x4c,0x90,0x8c,0x09,0x26,0x47,0xc6,0x04,0x43,0x5a,0x23,0x00,0x25, + 0x50,0x04,0x85,0x50,0x10,0x65,0x40,0x70,0x2c,0xa1,0x29,0x00,0x00,0x79,0x18,0x00, + 0x00,0xd7,0x00,0x00,0x00,0x1a,0x03,0x4c,0x10,0x97,0x29,0xa2,0x25,0x10,0xab,0x32, + 0xb9,0xb9,0xb4,0x37,0xb7,0x21,0xc6,0x42,0x3c,0x00,0x84,0x50,0xb9,0x1b,0x43,0x0b, + 0x93,0xfb,0x9a,0x4b,0xd3,0x2b,0x1b,0x62,0x2c,0xc3,0x23,0x2c,0x05,0xd9,0x20,0x08, + 0x0e,0x8e,0xad,0x0c,0x84,0x89,0xc9,0xaa,0x09,0xc4,0xae,0x4c,0x6e,0x2e,0xed,0xcd, + 0x0d,0x64,0x26,0x07,0x46,0xc6,0xc5,0xe6,0x06,0x04,0xa5,0xad,0x8c,0x2e,0x8c,0xcd, + 0xac,0xac,0x65,0x26,0x07,0x46,0xc6,0xc5,0xe6,0x26,0x65,0x88,0xf0,0x10,0x43,0x8c, + 0x65,0x58,0x8c,0x45,0x60,0xd1,0x54,0x46,0x17,0xc6,0x36,0x04,0x79,0x8e,0x65,0x58, + 0x84,0x45,0xe0,0x16,0x96,0x26,0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0x42, + 0x56,0xe6,0xf6,0x26,0xd7,0x36,0xf7,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44, + 0x78,0x12,0x72,0x61,0x69,0x72,0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x66, + 0x61,0x73,0x74,0x5f,0x6d,0x61,0x74,0x68,0x5f,0x65,0x6e,0x61,0x62,0x6c,0x65,0x43, + 0x84,0x67,0x21,0x19,0x84,0xa5,0xc9,0xb9,0x8c,0xbd,0xb5,0xc1,0xa5,0xb1,0x95,0xb9, + 0x98,0xc9,0x85,0xb5,0x95,0x89,0xd5,0x99,0x99,0x95,0xc9,0x7d,0x99,0x95,0xd1,0x8d, + 0xa1,0x7d,0x95,0xb9,0x85,0x89,0xb1,0x95,0x0d,0x11,0x9e,0x86,0x61,0x10,0x96,0x26, + 0xe7,0x32,0xf6,0xd6,0x06,0x97,0xc6,0x56,0xe6,0xe2,0x16,0x46,0x97,0x66,0x57,0xf6, + 0x45,0xf6,0x56,0x27,0xc6,0x56,0xf6,0x45,0x96,0x36,0x17,0x26,0xc6,0x56,0x36,0x44, + 0x78,0x1e,0x92,0x41,0x58,0x9a,0x9c,0xcb,0xd8,0x5b,0x1b,0x5c,0x1a,0x5b,0x99,0x8b, + 0x5b,0x18,0x5d,0x9a,0x5d,0xd9,0x17,0xdb,0x9b,0xdb,0xd9,0x17,0xdb,0x9b,0xdb,0xd9, + 0x17,0x59,0xda,0x5c,0x98,0x18,0x5b,0xd9,0x10,0xe1,0x89,0x78,0x06,0x61,0x69,0x72, + 0x2e,0x63,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x2e,0x6e,0x61,0x74,0x69,0x76,0x65,0x5f, + 0x77,0x69,0x64,0x65,0x5f,0x76,0x65,0x63,0x74,0x6f,0x72,0x73,0x5f,0x64,0x69,0x73, + 0x61,0x62,0x6c,0x65,0x43,0x84,0x67,0x62,0x14,0x96,0x26,0xe7,0x22,0x57,0xe6,0x46, + 0x56,0x26,0xf7,0x45,0x17,0x26,0x77,0x56,0x46,0xc7,0x28,0x2c,0x4d,0xce,0x25,0x4c, + 0xee,0xec,0x8b,0x2e,0x0f,0xae,0xec,0xcb,0x2d,0xac,0xad,0x8c,0x86,0x19,0xdb,0x5b, + 0x18,0x1d,0x0d,0x99,0xb0,0x34,0x39,0x97,0x30,0xb9,0xb3,0x2f,0xb7,0xb0,0xb6,0x32, + 0x2a,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x43,0x98,0xa7,0x5a,0x84, + 0xc7,0x7a,0xae,0x07,0x7b,0xb2,0x21,0xc2,0xa3,0x51,0x0a,0x4b,0x93,0x73,0x31,0x93, + 0x0b,0x3b,0x6b,0x2b,0x73,0xa3,0xfb,0x4a,0x73,0x83,0xab,0xa3,0xe3,0x52,0x37,0x57, + 0x26,0x87,0xc2,0xf6,0x36,0xe6,0x06,0x93,0x42,0x25,0x2c,0x4d,0xce,0x65,0xac,0xcc, + 0x8d,0xae,0x4c,0x8e,0x4f,0x58,0x9a,0x9c,0x0b,0x5c,0x99,0xdc,0x1c,0x5c,0xd9,0x18, + 0x5d,0x9a,0x5d,0x19,0x85,0x3a,0xbb,0x21,0xd2,0x22,0x3c,0xdc,0xd3,0x3d,0xde,0xf3, + 0x3d,0xd6,0x73,0x3d,0xd8,0x03,0x06,0x5c,0xea,0xe6,0xca,0xe4,0x50,0xd8,0xde,0xc6, + 0xdc,0x62,0x52,0x58,0x8c,0xbd,0xb1,0xbd,0xc9,0x0d,0x91,0x96,0xe1,0xe1,0x1e,0x31, + 0x78,0xbc,0xe7,0x7b,0xac,0xe7,0x7a,0xb0,0x67,0x0c,0xb8,0x84,0xa5,0xc9,0xb9,0xd0, + 0x95,0xe1,0xd1,0xd5,0xc9,0x95,0x51,0x0a,0x4b,0x93,0x73,0x61,0x7b,0x1b,0x0b,0xa3, + 0x4b,0x7b,0x73,0xfb,0x4a,0x73,0x23,0x2b,0xc3,0xa3,0x12,0x96,0x26,0xe7,0x32,0x17, + 0xd6,0x06,0xc7,0x56,0x46,0x8c,0xae,0x0c,0x8f,0xae,0x4e,0xae,0x4c,0x86,0x8c,0xc7, + 0x8c,0xed,0x2d,0x8c,0x8e,0x05,0x64,0x2e,0xac,0x0d,0x8e,0xad,0xcc,0x87,0x03,0x5d, + 0x19,0xde,0x10,0x6a,0x21,0x9e,0x32,0x78,0xcc,0x60,0x11,0x96,0xe1,0x39,0x83,0xc7, + 0x7a,0xd0,0xe0,0xc1,0x9e,0x34,0xe0,0x12,0x96,0x26,0xe7,0x32,0x17,0xd6,0x06,0xc7, + 0x56,0x26,0xc7,0x63,0x2e,0xac,0x0d,0x8e,0xad,0x4c,0x8e,0x08,0x5d,0x19,0xde,0x54, + 0x1b,0x1c,0x9b,0xdc,0x10,0x69,0x39,0x9e,0x35,0x78,0xcc,0x60,0x11,0x96,0xe1,0xb1, + 0x1e,0x36,0x78,0xb0,0xa7,0x0d,0x86,0x20,0x4f,0x18,0x3c,0x64,0xf0,0xa8,0xc1,0xe3, + 0x06,0x43,0x0c,0x04,0x78,0xb6,0xe7,0x0d,0x46,0x44,0xec,0xc0,0x0e,0xf6,0xd0,0x0e, + 0x6e,0xd0,0x0e,0xef,0x40,0x0e,0xf5,0xc0,0x0e,0xe5,0xe0,0x06,0xe6,0xc0,0x0e,0xe1, + 0x70,0x0e,0xf3,0x30,0x45,0x08,0x86,0x11,0x0a,0x3b,0xb0,0x83,0x3d,0xb4,0x83,0x1b, + 0xa4,0x03,0x39,0x94,0x83,0x3b,0xd0,0xc3,0x94,0xa0,0x18,0xb1,0x84,0x43,0x3a,0xc8, + 0x83,0x1b,0xd8,0x43,0x39,0xc8,0xc3,0x3c,0xa4,0xc3,0x3b,0xb8,0xc3,0x94,0xc0,0x18, + 0x41,0x85,0x43,0x3a,0xc8,0x83,0x1b,0xb0,0x43,0x38,0xb8,0xc3,0x39,0xd4,0x43,0x38, + 0x9c,0x43,0x39,0xfc,0x82,0x3d,0x94,0x83,0x3c,0xcc,0x43,0x3a,0xbc,0x83,0x3b,0x4c, + 0x09,0x90,0x11,0x53,0x38,0xa4,0x83,0x3c,0xb8,0xc1,0x38,0xbc,0x43,0x3b,0xc0,0x43, + 0x3a,0xb0,0x43,0x39,0xfc,0xc2,0x3b,0xc0,0x03,0x3d,0xa4,0xc3,0x3b,0xb8,0xc3,0x3c, + 0x4c,0x31,0x14,0xc6,0x81,0x24,0x6a,0x04,0x13,0x0e,0xe9,0x20,0x0f,0x6e,0x60,0x0e, + 0xf2,0x10,0x0e,0xe7,0xd0,0x0e,0xe5,0xe0,0x0e,0xf4,0x30,0x25,0x80,0x03,0x00,0x00, + 0x00,0x79,0x18,0x00,0x00,0x6d,0x00,0x00,0x00,0x33,0x08,0x80,0x1c,0xc4,0xe1,0x1c, + 0x66,0x14,0x01,0x3d,0x88,0x43,0x38,0x84,0xc3,0x8c,0x42,0x80,0x07,0x79,0x78,0x07, + 0x73,0x98,0x71,0x0c,0xe6,0x00,0x0f,0xed,0x10,0x0e,0xf4,0x80,0x0e,0x33,0x0c,0x42, + 0x1e,0xc2,0xc1,0x1d,0xce,0xa1,0x1c,0x66,0x30,0x05,0x3d,0x88,0x43,0x38,0x84,0x83, + 0x1b,0xcc,0x03,0x3d,0xc8,0x43,0x3d,0x8c,0x03,0x3d,0xcc,0x78,0x8c,0x74,0x70,0x07, + 0x7b,0x08,0x07,0x79,0x48,0x87,0x70,0x70,0x07,0x7a,0x70,0x03,0x76,0x78,0x87,0x70, + 0x20,0x87,0x19,0xcc,0x11,0x0e,0xec,0x90,0x0e,0xe1,0x30,0x0f,0x6e,0x30,0x0f,0xe3, + 0xf0,0x0e,0xf0,0x50,0x0e,0x33,0x10,0xc4,0x1d,0xde,0x21,0x1c,0xd8,0x21,0x1d,0xc2, + 0x61,0x1e,0x66,0x30,0x89,0x3b,0xbc,0x83,0x3b,0xd0,0x43,0x39,0xb4,0x03,0x3c,0xbc, + 0x83,0x3c,0x84,0x03,0x3b,0xcc,0xf0,0x14,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x68, + 0x87,0x72,0x68,0x07,0x37,0x80,0x87,0x70,0x90,0x87,0x70,0x60,0x07,0x76,0x28,0x07, + 0x76,0xf8,0x05,0x76,0x78,0x87,0x77,0x80,0x87,0x5f,0x08,0x87,0x71,0x18,0x87,0x72, + 0x98,0x87,0x79,0x98,0x81,0x2c,0xee,0xf0,0x0e,0xee,0xe0,0x0e,0xf5,0xc0,0x0e,0xec, + 0x30,0x03,0x62,0xc8,0xa1,0x1c,0xe4,0xa1,0x1c,0xcc,0xa1,0x1c,0xe4,0xa1,0x1c,0xdc, + 0x61,0x1c,0xca,0x21,0x1c,0xc4,0x81,0x1d,0xca,0x61,0x06,0xd6,0x90,0x43,0x39,0xc8, + 0x43,0x39,0x98,0x43,0x39,0xc8,0x43,0x39,0xb8,0xc3,0x38,0x94,0x43,0x38,0x88,0x03, + 0x3b,0x94,0xc3,0x2f,0xbc,0x83,0x3c,0xfc,0x82,0x3b,0xd4,0x03,0x3b,0xb0,0xc3,0x0c, + 0xc7,0x69,0x87,0x70,0x58,0x87,0x72,0x70,0x83,0x74,0x68,0x07,0x78,0x60,0x87,0x74, + 0x18,0x87,0x74,0xa0,0x87,0x19,0xce,0x53,0x0f,0xee,0x00,0x0f,0xf2,0x50,0x0e,0xe4, + 0x90,0x0e,0xe3,0x40,0x0f,0xe1,0x20,0x0e,0xec,0x50,0x0e,0x33,0x20,0x28,0x1d,0xdc, + 0xc1,0x1e,0xc2,0x41,0x1e,0xd2,0x21,0x1c,0xdc,0x81,0x1e,0xdc,0xe0,0x1c,0xe4,0xe1, + 0x1d,0xea,0x01,0x1e,0x66,0x18,0x51,0x38,0xb0,0x43,0x3a,0x9c,0x83,0x3b,0xcc,0x50, + 0x24,0x76,0x60,0x07,0x7b,0x68,0x07,0x37,0x60,0x87,0x77,0x78,0x07,0x78,0x98,0x51, + 0x4c,0xf4,0x90,0x0f,0xf0,0x50,0x0e,0x33,0x1e,0x6a,0x1e,0xca,0x61,0x1c,0xe8,0x21, + 0x1d,0xde,0xc1,0x1d,0x7e,0x01,0x1e,0xe4,0xa1,0x1c,0xcc,0x21,0x1d,0xf0,0x61,0x06, + 0x54,0x85,0x83,0x38,0xcc,0xc3,0x3b,0xb0,0x43,0x3d,0xd0,0x43,0x39,0xfc,0xc2,0x3c, + 0xe4,0x43,0x3b,0x88,0xc3,0x3b,0xb0,0xc3,0x8c,0xc5,0x0a,0x87,0x79,0x98,0x87,0x77, + 0x18,0x87,0x74,0x08,0x07,0x7a,0x28,0x07,0x72,0x00,0x00,0x00,0x00,0x71,0x20,0x00, + 0x00,0x08,0x00,0x00,0x00,0x16,0xb0,0x01,0x48,0xe4,0x4b,0x00,0xf3,0x2c,0xc4,0x3f, + 0x11,0xd7,0x44,0x45,0xc4,0x6f,0x0f,0x7e,0x85,0x17,0xb7,0x6d,0x00,0x05,0x03,0x20, + 0x0d,0x0d,0x00,0x00,0x00,0x61,0x20,0x00,0x00,0x0f,0x00,0x00,0x00,0x13,0x04,0x41, + 0x2c,0x10,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0xc4,0x46,0x00,0xc6,0x12,0x80,0x80, + 0xd4,0x08,0x40,0x0d,0x90,0x98,0x01,0xa0,0x30,0x03,0x40,0x60,0x04,0x00,0x00,0x00, + 0x00,0x83,0x0c,0x8b,0x60,0x8c,0x18,0x28,0x42,0x40,0x29,0x49,0x50,0x20,0x86,0x60, + 0x01,0x23,0x9f,0xd9,0x06,0x23,0x00,0x32,0x00,0x00,0x00,0x00,0x00, +}; +static const char _sgl_vs_source_metal_sim[698] = { + 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, + 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, + 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, + 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, + 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x76, + 0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x34,0x78,0x34,0x20,0x6d,0x76,0x70,0x3b,0x0a,0x20,0x20,0x20, + 0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x78,0x34,0x20,0x74,0x6d,0x3b,0x0a,0x7d,0x3b, + 0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f, + 0x75,0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20, + 0x75,0x76,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x30,0x29, + 0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e, + 0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34, + 0x20,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x20,0x5b,0x5b,0x70, + 0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73, + 0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x69,0x6e,0x0a,0x7b, + 0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x70,0x6f,0x73,0x69, + 0x74,0x69,0x6f,0x6e,0x20,0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65, + 0x28,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74, + 0x32,0x20,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x20,0x5b,0x5b,0x61,0x74, + 0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28,0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x20,0x20, + 0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x20, + 0x5b,0x5b,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x28,0x32,0x29,0x5d,0x5d, + 0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20,0x31,0x36,0x20,0x22, + 0x22,0x0a,0x76,0x65,0x72,0x74,0x65,0x78,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f, + 0x75,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x28,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x69, + 0x6e,0x20,0x69,0x6e,0x20,0x5b,0x5b,0x73,0x74,0x61,0x67,0x65,0x5f,0x69,0x6e,0x5d, + 0x5d,0x2c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x61,0x6e,0x74,0x20,0x76,0x73,0x5f,0x70, + 0x61,0x72,0x61,0x6d,0x73,0x26,0x20,0x5f,0x32,0x30,0x20,0x5b,0x5b,0x62,0x75,0x66, + 0x66,0x65,0x72,0x28,0x30,0x29,0x5d,0x5d,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20, + 0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20, + 0x7b,0x7d,0x3b,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20,0x31,0x36,0x20,0x22,0x22,0x0a, + 0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74, + 0x69,0x6f,0x6e,0x20,0x3d,0x20,0x5f,0x32,0x30,0x2e,0x6d,0x76,0x70,0x20,0x2a,0x20, + 0x69,0x6e,0x2e,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,0x0a,0x23,0x6c,0x69, + 0x6e,0x65,0x20,0x31,0x37,0x20,0x22,0x22,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74, + 0x2e,0x75,0x76,0x20,0x3d,0x20,0x5f,0x32,0x30,0x2e,0x74,0x6d,0x20,0x2a,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x34,0x28,0x69,0x6e,0x2e,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72, + 0x64,0x30,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x23, + 0x6c,0x69,0x6e,0x65,0x20,0x31,0x38,0x20,0x22,0x22,0x0a,0x20,0x20,0x20,0x20,0x6f, + 0x75,0x74,0x2e,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x69,0x6e,0x2e,0x63,0x6f, + 0x6c,0x6f,0x72,0x30,0x3b,0x0a,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e, + 0x20,0x6f,0x75,0x74,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +static const char _sgl_fs_source_metal_sim[473] = { + 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, + 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, + 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, + 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, + 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d, + 0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72, + 0x20,0x5b,0x5b,0x63,0x6f,0x6c,0x6f,0x72,0x28,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x7d, + 0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f, + 0x69,0x6e,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20, + 0x75,0x76,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x30,0x29, + 0x5d,0x5d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x63, + 0x6f,0x6c,0x6f,0x72,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e, + 0x31,0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20, + 0x31,0x31,0x20,0x22,0x22,0x0a,0x66,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x20,0x6d, + 0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x28,0x6d, + 0x61,0x69,0x6e,0x30,0x5f,0x69,0x6e,0x20,0x69,0x6e,0x20,0x5b,0x5b,0x73,0x74,0x61, + 0x67,0x65,0x5f,0x69,0x6e,0x5d,0x5d,0x2c,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65, + 0x32,0x64,0x3c,0x66,0x6c,0x6f,0x61,0x74,0x3e,0x20,0x74,0x65,0x78,0x20,0x5b,0x5b, + 0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x30,0x29,0x5d,0x5d,0x2c,0x20,0x73,0x61, + 0x6d,0x70,0x6c,0x65,0x72,0x20,0x74,0x65,0x78,0x53,0x6d,0x70,0x6c,0x72,0x20,0x5b, + 0x5b,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x28,0x30,0x29,0x5d,0x5d,0x29,0x0a,0x7b, + 0x0a,0x20,0x20,0x20,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6f, + 0x75,0x74,0x20,0x3d,0x20,0x7b,0x7d,0x3b,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20,0x31, + 0x31,0x20,0x22,0x22,0x0a,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x66,0x72,0x61, + 0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x74,0x65,0x78,0x2e,0x73,0x61, + 0x6d,0x70,0x6c,0x65,0x28,0x74,0x65,0x78,0x53,0x6d,0x70,0x6c,0x72,0x2c,0x20,0x69, + 0x6e,0x2e,0x75,0x76,0x2e,0x78,0x79,0x29,0x20,0x2a,0x20,0x69,0x6e,0x2e,0x63,0x6f, + 0x6c,0x6f,0x72,0x3b,0x0a,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20, + 0x6f,0x75,0x74,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, +}; +#elif defined(SOKOL_D3D11) +static const uint8_t _sgl_vs_bytecode_hlsl4[1008] = { + 0x44,0x58,0x42,0x43,0xf3,0xd9,0xf4,0x58,0x44,0xc2,0xb9,0x96,0x56,0x7c,0xb3,0x71, + 0x84,0xc5,0xde,0x61,0x01,0x00,0x00,0x00,0xf0,0x03,0x00,0x00,0x05,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0x14,0x01,0x00,0x00,0x78,0x01,0x00,0x00,0xe8,0x01,0x00,0x00, + 0x74,0x03,0x00,0x00,0x52,0x44,0x45,0x46,0xd8,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x48,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x04,0xfe,0xff, + 0x10,0x81,0x00,0x00,0xaf,0x00,0x00,0x00,0x3c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d, + 0x73,0x00,0xab,0xab,0x3c,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x60,0x00,0x00,0x00, + 0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x90,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x98,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xa8,0x00,0x00,0x00,0x40,0x00,0x00,0x00,0x40,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x98,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x5f,0x32,0x30,0x5f, + 0x6d,0x76,0x70,0x00,0x02,0x00,0x03,0x00,0x04,0x00,0x04,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x5f,0x32,0x30,0x5f,0x74,0x6d,0x00,0x4d,0x69,0x63,0x72,0x6f, + 0x73,0x6f,0x66,0x74,0x20,0x28,0x52,0x29,0x20,0x48,0x4c,0x53,0x4c,0x20,0x53,0x68, + 0x61,0x64,0x65,0x72,0x20,0x43,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x72,0x20,0x31,0x30, + 0x2e,0x31,0x00,0xab,0x49,0x53,0x47,0x4e,0x5c,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x50,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x03,0x03,0x00,0x00,0x50,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0f,0x0f,0x00,0x00,0x54,0x45,0x58,0x43, + 0x4f,0x4f,0x52,0x44,0x00,0xab,0xab,0xab,0x4f,0x53,0x47,0x4e,0x68,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x00, + 0x50,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x59,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x0f,0x00,0x00,0x00, + 0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x00,0x53,0x56,0x5f,0x50,0x6f,0x73,0x69, + 0x74,0x69,0x6f,0x6e,0x00,0xab,0xab,0xab,0x53,0x48,0x44,0x52,0x84,0x01,0x00,0x00, + 0x40,0x00,0x01,0x00,0x61,0x00,0x00,0x00,0x59,0x00,0x00,0x04,0x46,0x8e,0x20,0x00, + 0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x5f,0x00,0x00,0x03,0xf2,0x10,0x10,0x00, + 0x00,0x00,0x00,0x00,0x5f,0x00,0x00,0x03,0x32,0x10,0x10,0x00,0x01,0x00,0x00,0x00, + 0x5f,0x00,0x00,0x03,0xf2,0x10,0x10,0x00,0x02,0x00,0x00,0x00,0x65,0x00,0x00,0x03, + 0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x65,0x00,0x00,0x03,0xf2,0x20,0x10,0x00, + 0x01,0x00,0x00,0x00,0x67,0x00,0x00,0x04,0xf2,0x20,0x10,0x00,0x02,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x68,0x00,0x00,0x02,0x01,0x00,0x00,0x00,0x38,0x00,0x00,0x08, + 0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x56,0x15,0x10,0x00,0x01,0x00,0x00,0x00, + 0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x32,0x00,0x00,0x0a, + 0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x06,0x10,0x10,0x00,0x01,0x00,0x00,0x00, + 0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x46,0x0e,0x10,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00, + 0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x36,0x00,0x00,0x05,0xf2,0x20,0x10,0x00,0x01,0x00,0x00,0x00, + 0x46,0x1e,0x10,0x00,0x02,0x00,0x00,0x00,0x38,0x00,0x00,0x08,0xf2,0x00,0x10,0x00, + 0x00,0x00,0x00,0x00,0x56,0x15,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x32,0x00,0x00,0x0a,0xf2,0x00,0x10,0x00, + 0x00,0x00,0x00,0x00,0x06,0x10,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00, + 0x32,0x00,0x00,0x0a,0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0xa6,0x1a,0x10,0x00, + 0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00,0x32,0x00,0x00,0x0a,0xf2,0x20,0x10,0x00, + 0x02,0x00,0x00,0x00,0xf6,0x1f,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x8e,0x20,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x46,0x0e,0x10,0x00,0x00,0x00,0x00,0x00, + 0x3e,0x00,0x00,0x01,0x53,0x54,0x41,0x54,0x74,0x00,0x00,0x00,0x09,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + +}; +static const uint8_t _sgl_fs_bytecode_hlsl4[620] = { + 0x44,0x58,0x42,0x43,0x4f,0x7a,0xe1,0xcf,0x0c,0x89,0xc0,0x09,0x50,0x5a,0xca,0xe9, + 0x75,0x77,0xd1,0x26,0x01,0x00,0x00,0x00,0x6c,0x02,0x00,0x00,0x05,0x00,0x00,0x00, + 0x34,0x00,0x00,0x00,0xd4,0x00,0x00,0x00,0x20,0x01,0x00,0x00,0x54,0x01,0x00,0x00, + 0xf0,0x01,0x00,0x00,0x52,0x44,0x45,0x46,0x98,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x1c,0x00,0x00,0x00,0x00,0x04,0xff,0xff, + 0x10,0x81,0x00,0x00,0x6d,0x00,0x00,0x00,0x5c,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x69,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x0d,0x00,0x00,0x00,0x5f,0x74,0x65,0x78,0x5f,0x73,0x61,0x6d, + 0x70,0x6c,0x65,0x72,0x00,0x74,0x65,0x78,0x00,0x4d,0x69,0x63,0x72,0x6f,0x73,0x6f, + 0x66,0x74,0x20,0x28,0x52,0x29,0x20,0x48,0x4c,0x53,0x4c,0x20,0x53,0x68,0x61,0x64, + 0x65,0x72,0x20,0x43,0x6f,0x6d,0x70,0x69,0x6c,0x65,0x72,0x20,0x31,0x30,0x2e,0x31, + 0x00,0xab,0xab,0xab,0x49,0x53,0x47,0x4e,0x44,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x08,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0f,0x03,0x00,0x00,0x38,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x0f,0x0f,0x00,0x00,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52,0x44,0x00,0xab,0xab,0xab, + 0x4f,0x53,0x47,0x4e,0x2c,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x53,0x56,0x5f,0x54,0x61,0x72,0x67,0x65, + 0x74,0x00,0xab,0xab,0x53,0x48,0x44,0x52,0x94,0x00,0x00,0x00,0x40,0x00,0x00,0x00, + 0x25,0x00,0x00,0x00,0x5a,0x00,0x00,0x03,0x00,0x60,0x10,0x00,0x00,0x00,0x00,0x00, + 0x58,0x18,0x00,0x04,0x00,0x70,0x10,0x00,0x00,0x00,0x00,0x00,0x55,0x55,0x00,0x00, + 0x62,0x10,0x00,0x03,0x32,0x10,0x10,0x00,0x00,0x00,0x00,0x00,0x62,0x10,0x00,0x03, + 0xf2,0x10,0x10,0x00,0x01,0x00,0x00,0x00,0x65,0x00,0x00,0x03,0xf2,0x20,0x10,0x00, + 0x00,0x00,0x00,0x00,0x68,0x00,0x00,0x02,0x01,0x00,0x00,0x00,0x45,0x00,0x00,0x09, + 0xf2,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x10,0x10,0x00,0x00,0x00,0x00,0x00, + 0x46,0x7e,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x10,0x00,0x00,0x00,0x00,0x00, + 0x38,0x00,0x00,0x07,0xf2,0x20,0x10,0x00,0x00,0x00,0x00,0x00,0x46,0x0e,0x10,0x00, + 0x00,0x00,0x00,0x00,0x46,0x1e,0x10,0x00,0x01,0x00,0x00,0x00,0x3e,0x00,0x00,0x01, + 0x53,0x54,0x41,0x54,0x74,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, +}; +#elif defined(SOKOL_WGPU) +static const uint8_t _sgl_vs_bytecode_wgpu[1932] = { + 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x08,0x00,0x08,0x00,0x32,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, + 0x02,0x00,0x00,0x00,0x47,0x4c,0x53,0x4c,0x2e,0x73,0x74,0x64,0x2e,0x34,0x35,0x30, + 0x00,0x00,0x00,0x00,0x0e,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x0f,0x00,0x0d,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, + 0x00,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x24,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x30,0x00,0x00,0x00, + 0x31,0x00,0x00,0x00,0x07,0x00,0x03,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x03,0x00,0x37,0x00,0x02,0x00,0x00,0x00,0xc2,0x01,0x00,0x00,0x01,0x00,0x00,0x00, + 0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65, + 0x73,0x73,0x65,0x64,0x20,0x63,0x6c,0x69,0x65,0x6e,0x74,0x20,0x76,0x75,0x6c,0x6b, + 0x61,0x6e,0x31,0x30,0x30,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c, + 0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x63,0x6c,0x69,0x65,0x6e, + 0x74,0x20,0x6f,0x70,0x65,0x6e,0x67,0x6c,0x31,0x30,0x30,0x0a,0x2f,0x2f,0x20,0x4f, + 0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64, + 0x20,0x74,0x61,0x72,0x67,0x65,0x74,0x2d,0x65,0x6e,0x76,0x20,0x76,0x75,0x6c,0x6b, + 0x61,0x6e,0x31,0x2e,0x30,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c, + 0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x74,0x61,0x72,0x67,0x65, + 0x74,0x2d,0x65,0x6e,0x76,0x20,0x6f,0x70,0x65,0x6e,0x67,0x6c,0x0a,0x2f,0x2f,0x20, + 0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65, + 0x64,0x20,0x65,0x6e,0x74,0x72,0x79,0x2d,0x70,0x6f,0x69,0x6e,0x74,0x20,0x6d,0x61, + 0x69,0x6e,0x0a,0x23,0x6c,0x69,0x6e,0x65,0x20,0x31,0x0a,0x00,0x05,0x00,0x04,0x00, + 0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e,0x00,0x00,0x00,0x00,0x05,0x00,0x06,0x00, + 0x0c,0x00,0x00,0x00,0x67,0x6c,0x5f,0x50,0x65,0x72,0x56,0x65,0x72,0x74,0x65,0x78, + 0x00,0x00,0x00,0x00,0x06,0x00,0x06,0x00,0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x67,0x6c,0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x06,0x00,0x07,0x00, + 0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x67,0x6c,0x5f,0x50,0x6f,0x69,0x6e,0x74, + 0x53,0x69,0x7a,0x65,0x00,0x00,0x00,0x00,0x06,0x00,0x07,0x00,0x0c,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x67,0x6c,0x5f,0x43,0x6c,0x69,0x70,0x44,0x69,0x73,0x74,0x61, + 0x6e,0x63,0x65,0x00,0x06,0x00,0x07,0x00,0x0c,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x67,0x6c,0x5f,0x43,0x75,0x6c,0x6c,0x44,0x69,0x73,0x74,0x61,0x6e,0x63,0x65,0x00, + 0x05,0x00,0x03,0x00,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00, + 0x12,0x00,0x00,0x00,0x76,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x00,0x00,0x00, + 0x06,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x6d,0x76,0x70,0x00, + 0x06,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x74,0x6d,0x00,0x00, + 0x05,0x00,0x03,0x00,0x14,0x00,0x00,0x00,0x5f,0x32,0x30,0x00,0x05,0x00,0x05,0x00, + 0x19,0x00,0x00,0x00,0x70,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x00,0x00,0x00,0x00, + 0x05,0x00,0x03,0x00,0x1e,0x00,0x00,0x00,0x75,0x76,0x00,0x00,0x05,0x00,0x05,0x00, + 0x24,0x00,0x00,0x00,0x74,0x65,0x78,0x63,0x6f,0x6f,0x72,0x64,0x30,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x2c,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00, + 0x05,0x00,0x04,0x00,0x2d,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f,0x72,0x30,0x00,0x00, + 0x05,0x00,0x05,0x00,0x30,0x00,0x00,0x00,0x67,0x6c,0x5f,0x56,0x65,0x72,0x74,0x65, + 0x78,0x49,0x44,0x00,0x05,0x00,0x06,0x00,0x31,0x00,0x00,0x00,0x67,0x6c,0x5f,0x49, + 0x6e,0x73,0x74,0x61,0x6e,0x63,0x65,0x49,0x44,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x0c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x0b,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x0c,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x47,0x00,0x03,0x00, + 0x0c,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x48,0x00,0x04,0x00,0x12,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x12,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x23,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x00,0x05,0x00, + 0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00, + 0x48,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x05,0x00,0x00,0x00, + 0x48,0x00,0x05,0x00,0x12,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x23,0x00,0x00,0x00, + 0x40,0x00,0x00,0x00,0x48,0x00,0x05,0x00,0x12,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x47,0x00,0x03,0x00,0x12,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x22,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x14,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x19,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x1e,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x24,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x2c,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x2d,0x00,0x00,0x00,0x1e,0x00,0x00,0x00, + 0x02,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x30,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, + 0x05,0x00,0x00,0x00,0x47,0x00,0x04,0x00,0x31,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, + 0x06,0x00,0x00,0x00,0x13,0x00,0x02,0x00,0x03,0x00,0x00,0x00,0x21,0x00,0x03,0x00, + 0x04,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x16,0x00,0x03,0x00,0x07,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x17,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0x15,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x20,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x2b,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x0a,0x00,0x00,0x00, + 0x01,0x00,0x00,0x00,0x1c,0x00,0x04,0x00,0x0b,0x00,0x00,0x00,0x07,0x00,0x00,0x00, + 0x0a,0x00,0x00,0x00,0x1e,0x00,0x06,0x00,0x0c,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x07,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x0d,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x0c,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x0d,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x15,0x00,0x04,0x00, + 0x0f,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, + 0x0f,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x1e,0x00,0x04,0x00, + 0x12,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x13,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x13,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x15,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x18,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x18,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x1c,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x1c,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, + 0x0f,0x00,0x00,0x00,0x1f,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x17,0x00,0x04,0x00, + 0x22,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x23,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x23,0x00,0x00,0x00,0x24,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x26,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2b,0x00,0x04,0x00, + 0x07,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x00,0x00,0x80,0x3f,0x3b,0x00,0x04,0x00, + 0x1c,0x00,0x00,0x00,0x2c,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x18,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x20,0x00,0x04,0x00, + 0x2f,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x2f,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x3b,0x00,0x04,0x00, + 0x2f,0x00,0x00,0x00,0x31,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x36,0x00,0x05,0x00, + 0x03,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0xf8,0x00,0x02,0x00,0x06,0x00,0x00,0x00,0x08,0x00,0x04,0x00,0x01,0x00,0x00,0x00, + 0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x41,0x00,0x05,0x00,0x15,0x00,0x00,0x00, + 0x16,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x11,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x08,0x00,0x00,0x00,0x1a,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x91,0x00,0x05,0x00, + 0x08,0x00,0x00,0x00,0x1b,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x1a,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x1c,0x00,0x00,0x00,0x1d,0x00,0x00,0x00,0x0e,0x00,0x00,0x00, + 0x10,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x1d,0x00,0x00,0x00,0x1b,0x00,0x00,0x00, + 0x08,0x00,0x04,0x00,0x01,0x00,0x00,0x00,0x12,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x41,0x00,0x05,0x00,0x15,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x14,0x00,0x00,0x00, + 0x1f,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x21,0x00,0x00,0x00, + 0x20,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x22,0x00,0x00,0x00,0x25,0x00,0x00,0x00, + 0x24,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x07,0x00,0x00,0x00,0x28,0x00,0x00,0x00, + 0x25,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x51,0x00,0x05,0x00,0x07,0x00,0x00,0x00, + 0x29,0x00,0x00,0x00,0x25,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x50,0x00,0x07,0x00, + 0x08,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x28,0x00,0x00,0x00,0x29,0x00,0x00,0x00, + 0x26,0x00,0x00,0x00,0x27,0x00,0x00,0x00,0x91,0x00,0x05,0x00,0x08,0x00,0x00,0x00, + 0x2b,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x2a,0x00,0x00,0x00,0x3e,0x00,0x03,0x00, + 0x1e,0x00,0x00,0x00,0x2b,0x00,0x00,0x00,0x08,0x00,0x04,0x00,0x01,0x00,0x00,0x00, + 0x13,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3d,0x00,0x04,0x00,0x08,0x00,0x00,0x00, + 0x2e,0x00,0x00,0x00,0x2d,0x00,0x00,0x00,0x3e,0x00,0x03,0x00,0x2c,0x00,0x00,0x00, + 0x2e,0x00,0x00,0x00,0xfd,0x00,0x01,0x00,0x38,0x00,0x01,0x00, +}; +static const uint8_t _sgl_fs_bytecode_wgpu[916] = { + 0x03,0x02,0x23,0x07,0x00,0x00,0x01,0x00,0x08,0x00,0x08,0x00,0x19,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x11,0x00,0x02,0x00,0x01,0x00,0x00,0x00,0x0b,0x00,0x06,0x00, + 0x02,0x00,0x00,0x00,0x47,0x4c,0x53,0x4c,0x2e,0x73,0x74,0x64,0x2e,0x34,0x35,0x30, + 0x00,0x00,0x00,0x00,0x0e,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x0f,0x00,0x08,0x00,0x04,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, + 0x00,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x16,0x00,0x00,0x00, + 0x10,0x00,0x03,0x00,0x05,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x07,0x00,0x03,0x00, + 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x37,0x00,0x02,0x00,0x00,0x00, + 0xc2,0x01,0x00,0x00,0x01,0x00,0x00,0x00,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64, + 0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x63,0x6c,0x69, + 0x65,0x6e,0x74,0x20,0x76,0x75,0x6c,0x6b,0x61,0x6e,0x31,0x30,0x30,0x0a,0x2f,0x2f, + 0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73, + 0x65,0x64,0x20,0x63,0x6c,0x69,0x65,0x6e,0x74,0x20,0x6f,0x70,0x65,0x6e,0x67,0x6c, + 0x31,0x30,0x30,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50, + 0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x74,0x61,0x72,0x67,0x65,0x74,0x2d, + 0x65,0x6e,0x76,0x20,0x76,0x75,0x6c,0x6b,0x61,0x6e,0x31,0x2e,0x30,0x0a,0x2f,0x2f, + 0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65,0x50,0x72,0x6f,0x63,0x65,0x73,0x73, + 0x65,0x64,0x20,0x74,0x61,0x72,0x67,0x65,0x74,0x2d,0x65,0x6e,0x76,0x20,0x6f,0x70, + 0x65,0x6e,0x67,0x6c,0x0a,0x2f,0x2f,0x20,0x4f,0x70,0x4d,0x6f,0x64,0x75,0x6c,0x65, + 0x50,0x72,0x6f,0x63,0x65,0x73,0x73,0x65,0x64,0x20,0x65,0x6e,0x74,0x72,0x79,0x2d, + 0x70,0x6f,0x69,0x6e,0x74,0x20,0x6d,0x61,0x69,0x6e,0x0a,0x23,0x6c,0x69,0x6e,0x65, + 0x20,0x31,0x0a,0x00,0x05,0x00,0x04,0x00,0x05,0x00,0x00,0x00,0x6d,0x61,0x69,0x6e, + 0x00,0x00,0x00,0x00,0x05,0x00,0x05,0x00,0x0a,0x00,0x00,0x00,0x66,0x72,0x61,0x67, + 0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x05,0x00,0x03,0x00,0x0e,0x00,0x00,0x00, + 0x74,0x65,0x78,0x00,0x05,0x00,0x03,0x00,0x11,0x00,0x00,0x00,0x75,0x76,0x00,0x00, + 0x05,0x00,0x04,0x00,0x16,0x00,0x00,0x00,0x63,0x6f,0x6c,0x6f,0x72,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x0a,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x22,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x0e,0x00,0x00,0x00,0x21,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x11,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x47,0x00,0x04,0x00,0x16,0x00,0x00,0x00,0x1e,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x13,0x00,0x02,0x00,0x03,0x00,0x00,0x00,0x21,0x00,0x03,0x00,0x04,0x00,0x00,0x00, + 0x03,0x00,0x00,0x00,0x16,0x00,0x03,0x00,0x07,0x00,0x00,0x00,0x20,0x00,0x00,0x00, + 0x17,0x00,0x04,0x00,0x08,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x04,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x09,0x00,0x00,0x00,0x0a,0x00,0x00,0x00,0x03,0x00,0x00,0x00, + 0x19,0x00,0x09,0x00,0x0b,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x1b,0x00,0x03,0x00,0x0c,0x00,0x00,0x00,0x0b,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x0d,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0c,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x0d,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x20,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x08,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x17,0x00,0x04,0x00,0x12,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x02,0x00,0x00,0x00, + 0x3b,0x00,0x04,0x00,0x10,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x01,0x00,0x00,0x00, + 0x36,0x00,0x05,0x00,0x03,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x04,0x00,0x00,0x00,0xf8,0x00,0x02,0x00,0x06,0x00,0x00,0x00,0x08,0x00,0x04,0x00, + 0x01,0x00,0x00,0x00,0x0b,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x0c,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x0e,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x08,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x11,0x00,0x00,0x00,0x4f,0x00,0x07,0x00, + 0x12,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x13,0x00,0x00,0x00,0x13,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x57,0x00,0x05,0x00,0x08,0x00,0x00,0x00, + 0x15,0x00,0x00,0x00,0x0f,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x3d,0x00,0x04,0x00, + 0x08,0x00,0x00,0x00,0x17,0x00,0x00,0x00,0x16,0x00,0x00,0x00,0x85,0x00,0x05,0x00, + 0x08,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x15,0x00,0x00,0x00,0x17,0x00,0x00,0x00, + 0x3e,0x00,0x03,0x00,0x0a,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0xfd,0x00,0x01,0x00, + 0x38,0x00,0x01,0x00, +}; +#elif defined(SOKOL_DUMMY_BACKEND) +static const char* _sgl_vs_source_dummy = ""; +static const char* _sgl_fs_source_dummy = ""; +#else +#error "Please define one of SOKOL_GLCORE33, SOKOL_GLES2, SOKOL_GLES3, SOKOL_D3D11, SOKOL_METAL, SOKOL_WGPU or SOKOL_DUMMY_BACKEND!" +#endif + +typedef enum { + SGL_PRIMITIVETYPE_POINTS = 0, + SGL_PRIMITIVETYPE_LINES, + SGL_PRIMITIVETYPE_LINE_STRIP, + SGL_PRIMITIVETYPE_TRIANGLES, + SGL_PRIMITIVETYPE_TRIANGLE_STRIP, + SGL_PRIMITIVETYPE_QUADS, + SGL_NUM_PRIMITIVE_TYPES, +} _sgl_primitive_type_t; + +typedef struct { + uint32_t id; + sg_resource_state state; +} _sgl_slot_t; + +typedef struct { + int size; + int queue_top; + uint32_t* gen_ctrs; + int* free_queue; +} _sgl_pool_t; + +typedef struct { + _sgl_slot_t slot; + sg_pipeline pip[SGL_NUM_PRIMITIVE_TYPES]; +} _sgl_pipeline_t; + +typedef struct { + _sgl_pool_t pool; + _sgl_pipeline_t* pips; +} _sgl_pipeline_pool_t; + +typedef enum { + SGL_MATRIXMODE_MODELVIEW, + SGL_MATRIXMODE_PROJECTION, + SGL_MATRIXMODE_TEXTURE, + SGL_NUM_MATRIXMODES +} _sgl_matrix_mode_t; + +typedef struct { + float pos[3]; + float uv[2]; + uint32_t rgba; +} _sgl_vertex_t; + +typedef struct { + float v[4][4]; +} _sgl_matrix_t; + +typedef struct { + _sgl_matrix_t mvp; /* model-view-projection matrix */ + _sgl_matrix_t tm; /* texture matrix */ +} _sgl_uniform_t; + +typedef enum { + SGL_COMMAND_DRAW, + SGL_COMMAND_VIEWPORT, + SGL_COMMAND_SCISSOR_RECT, +} _sgl_command_type_t; + +typedef struct { + sg_pipeline pip; + sg_image img; + int base_vertex; + int num_vertices; + int uniform_index; +} _sgl_draw_args_t; + +typedef struct { + int x, y, w, h; + bool origin_top_left; +} _sgl_viewport_args_t; + +typedef struct { + int x, y, w, h; + bool origin_top_left; +} _sgl_scissor_rect_args_t; + +typedef union { + _sgl_draw_args_t draw; + _sgl_viewport_args_t viewport; + _sgl_scissor_rect_args_t scissor_rect; +} _sgl_args_t; + +typedef struct { + _sgl_command_type_t cmd; + _sgl_args_t args; +} _sgl_command_t; + +#define _SGL_INVALID_SLOT_INDEX (0) +#define _SGL_MAX_STACK_DEPTH (64) +#define _SGL_DEFAULT_PIPELINE_POOL_SIZE (64) +#define _SGL_DEFAULT_MAX_VERTICES (1<<16) +#define _SGL_DEFAULT_MAX_COMMANDS (1<<14) +#define _SGL_SLOT_SHIFT (16) +#define _SGL_MAX_POOL_SIZE (1<<_SGL_SLOT_SHIFT) +#define _SGL_SLOT_MASK (_SGL_MAX_POOL_SIZE-1) + +typedef struct { + uint32_t init_cookie; + sgl_desc_t desc; + + int num_vertices; + int num_uniforms; + int num_commands; + int cur_vertex; + int cur_uniform; + int cur_command; + _sgl_vertex_t* vertices; + _sgl_uniform_t* uniforms; + _sgl_command_t* commands; + + /* state tracking */ + int base_vertex; + int vtx_count; /* number of times vtx function has been called, used for non-triangle primitives */ + sgl_error_t error; + bool in_begin; + float u, v; + uint32_t rgba; + _sgl_primitive_type_t cur_prim_type; + sg_image cur_img; + bool texturing_enabled; + bool matrix_dirty; /* reset in sgl_end(), set in any of the matrix stack functions */ + + /* sokol-gfx resources */ + sg_buffer vbuf; + sg_image def_img; /* a default white texture */ + sg_shader shd; + sg_bindings bind; + sgl_pipeline def_pip; + _sgl_pipeline_pool_t pip_pool; + + /* pipeline stack */ + int pip_tos; + sgl_pipeline pip_stack[_SGL_MAX_STACK_DEPTH]; + + /* matrix stacks */ + _sgl_matrix_mode_t cur_matrix_mode; + int matrix_tos[SGL_NUM_MATRIXMODES]; + _sgl_matrix_t matrix_stack[SGL_NUM_MATRIXMODES][_SGL_MAX_STACK_DEPTH]; +} _sgl_t; +static _sgl_t _sgl; + +/*== PRIVATE FUNCTIONS =======================================================*/ + +static void _sgl_init_pool(_sgl_pool_t* pool, int num) { + SOKOL_ASSERT(pool && (num >= 1)); + /* slot 0 is reserved for the 'invalid id', so bump the pool size by 1 */ + pool->size = num + 1; + pool->queue_top = 0; + /* generation counters indexable by pool slot index, slot 0 is reserved */ + size_t gen_ctrs_size = sizeof(uint32_t) * (size_t)pool->size; + pool->gen_ctrs = (uint32_t*) SOKOL_MALLOC(gen_ctrs_size); + SOKOL_ASSERT(pool->gen_ctrs); + memset(pool->gen_ctrs, 0, gen_ctrs_size); + /* it's not a bug to only reserve 'num' here */ + pool->free_queue = (int*) SOKOL_MALLOC(sizeof(int) * (size_t)num); + SOKOL_ASSERT(pool->free_queue); + /* never allocate the zero-th pool item since the invalid id is 0 */ + for (int i = pool->size-1; i >= 1; i--) { + pool->free_queue[pool->queue_top++] = i; + } +} + +static void _sgl_discard_pool(_sgl_pool_t* pool) { + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + SOKOL_FREE(pool->free_queue); + pool->free_queue = 0; + SOKOL_ASSERT(pool->gen_ctrs); + SOKOL_FREE(pool->gen_ctrs); + pool->gen_ctrs = 0; + pool->size = 0; + pool->queue_top = 0; +} + +static int _sgl_pool_alloc_index(_sgl_pool_t* pool) { + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + if (pool->queue_top > 0) { + int slot_index = pool->free_queue[--pool->queue_top]; + SOKOL_ASSERT((slot_index > 0) && (slot_index < pool->size)); + return slot_index; + } + else { + /* pool exhausted */ + return _SGL_INVALID_SLOT_INDEX; + } +} + +static void _sgl_pool_free_index(_sgl_pool_t* pool, int slot_index) { + SOKOL_ASSERT((slot_index > _SGL_INVALID_SLOT_INDEX) && (slot_index < pool->size)); + SOKOL_ASSERT(pool); + SOKOL_ASSERT(pool->free_queue); + SOKOL_ASSERT(pool->queue_top < pool->size); + #ifdef SOKOL_DEBUG + /* debug check against double-free */ + for (int i = 0; i < pool->queue_top; i++) { + SOKOL_ASSERT(pool->free_queue[i] != slot_index); + } + #endif + pool->free_queue[pool->queue_top++] = slot_index; + SOKOL_ASSERT(pool->queue_top <= (pool->size-1)); +} + +static void _sgl_reset_pipeline(_sgl_pipeline_t* pip) { + SOKOL_ASSERT(pip); + memset(pip, 0, sizeof(_sgl_pipeline_t)); +} + +static void _sgl_setup_pipeline_pool(const sgl_desc_t* desc) { + SOKOL_ASSERT(desc); + /* note: the pools here will have an additional item, since slot 0 is reserved */ + SOKOL_ASSERT((desc->pipeline_pool_size > 0) && (desc->pipeline_pool_size < _SGL_MAX_POOL_SIZE)); + _sgl_init_pool(&_sgl.pip_pool.pool, desc->pipeline_pool_size); + size_t pool_byte_size = sizeof(_sgl_pipeline_t) * (size_t)_sgl.pip_pool.pool.size; + _sgl.pip_pool.pips = (_sgl_pipeline_t*) SOKOL_MALLOC(pool_byte_size); + SOKOL_ASSERT(_sgl.pip_pool.pips); + memset(_sgl.pip_pool.pips, 0, pool_byte_size); +} + +static void _sgl_discard_pipeline_pool(void) { + SOKOL_FREE(_sgl.pip_pool.pips); _sgl.pip_pool.pips = 0; + _sgl_discard_pool(&_sgl.pip_pool.pool); +} + +/* allocate the slot at slot_index: + - bump the slot's generation counter + - create a resource id from the generation counter and slot index + - set the slot's id to this id + - set the slot's state to ALLOC + - return the resource id +*/ +static uint32_t _sgl_slot_alloc(_sgl_pool_t* pool, _sgl_slot_t* slot, int slot_index) { + /* FIXME: add handling for an overflowing generation counter, + for now, just overflow (another option is to disable + the slot) + */ + SOKOL_ASSERT(pool && pool->gen_ctrs); + SOKOL_ASSERT((slot_index > _SGL_INVALID_SLOT_INDEX) && (slot_index < pool->size)); + SOKOL_ASSERT((slot->state == SG_RESOURCESTATE_INITIAL) && (slot->id == SG_INVALID_ID)); + uint32_t ctr = ++pool->gen_ctrs[slot_index]; + slot->id = (ctr<<_SGL_SLOT_SHIFT)|(slot_index & _SGL_SLOT_MASK); + slot->state = SG_RESOURCESTATE_ALLOC; + return slot->id; +} + +/* extract slot index from id */ +static int _sgl_slot_index(uint32_t id) { + int slot_index = (int) (id & _SGL_SLOT_MASK); + SOKOL_ASSERT(_SGL_INVALID_SLOT_INDEX != slot_index); + return slot_index; +} + +/* get pipeline pointer without id-check */ +static _sgl_pipeline_t* _sgl_pipeline_at(uint32_t pip_id) { + SOKOL_ASSERT(SG_INVALID_ID != pip_id); + int slot_index = _sgl_slot_index(pip_id); + SOKOL_ASSERT((slot_index > _SGL_INVALID_SLOT_INDEX) && (slot_index < _sgl.pip_pool.pool.size)); + return &_sgl.pip_pool.pips[slot_index]; +} + +/* get pipeline pointer with id-check, returns 0 if no match */ +static _sgl_pipeline_t* _sgl_lookup_pipeline(uint32_t pip_id) { + if (SG_INVALID_ID != pip_id) { + _sgl_pipeline_t* pip = _sgl_pipeline_at(pip_id); + if (pip->slot.id == pip_id) { + return pip; + } + } + return 0; +} + +/* make pipeline id from uint32_t id */ +static sgl_pipeline _sgl_make_pip_id(uint32_t pip_id) { + sgl_pipeline pip; + pip.id = pip_id; + return pip; +} + +static sgl_pipeline _sgl_alloc_pipeline(void) { + sgl_pipeline res; + int slot_index = _sgl_pool_alloc_index(&_sgl.pip_pool.pool); + if (_SGL_INVALID_SLOT_INDEX != slot_index) { + res = _sgl_make_pip_id(_sgl_slot_alloc(&_sgl.pip_pool.pool, &_sgl.pip_pool.pips[slot_index].slot, slot_index)); + } + else { + /* pool is exhausted */ + res = _sgl_make_pip_id(SG_INVALID_ID); + } + return res; +} + +static void _sgl_init_pipeline(sgl_pipeline pip_id, const sg_pipeline_desc* in_desc) { + SOKOL_ASSERT((pip_id.id != SG_INVALID_ID) && in_desc); + + /* create a new desc with 'patched' shader and pixel format state */ + sg_pipeline_desc desc = *in_desc; + desc.layout.buffers[0].stride = sizeof(_sgl_vertex_t); + { + sg_vertex_attr_desc* pos = &desc.layout.attrs[0]; + pos->offset = offsetof(_sgl_vertex_t, pos); + pos->format = SG_VERTEXFORMAT_FLOAT3; + } + { + sg_vertex_attr_desc* uv = &desc.layout.attrs[1]; + uv->offset = offsetof(_sgl_vertex_t, uv); + uv->format = SG_VERTEXFORMAT_FLOAT2; + } + { + sg_vertex_attr_desc* rgba = &desc.layout.attrs[2]; + rgba->offset = offsetof(_sgl_vertex_t, rgba); + rgba->format = SG_VERTEXFORMAT_UBYTE4N; + } + if (in_desc->shader.id == SG_INVALID_ID) { + desc.shader = _sgl.shd; + } + desc.index_type = SG_INDEXTYPE_NONE; + desc.sample_count = _sgl.desc.sample_count; + if (desc.face_winding == _SG_FACEWINDING_DEFAULT) { + desc.face_winding = _sgl.desc.face_winding; + } + desc.depth.pixel_format = _sgl.desc.depth_format; + desc.colors[0].pixel_format = _sgl.desc.color_format; + if (desc.colors[0].write_mask == _SG_COLORMASK_DEFAULT) { + desc.colors[0].write_mask = SG_COLORMASK_RGB; + } + + _sgl_pipeline_t* pip = _sgl_lookup_pipeline(pip_id.id); + SOKOL_ASSERT(pip && (pip->slot.state == SG_RESOURCESTATE_ALLOC)); + pip->slot.state = SG_RESOURCESTATE_VALID; + for (int i = 0; i < SGL_NUM_PRIMITIVE_TYPES; i++) { + switch (i) { + case SGL_PRIMITIVETYPE_POINTS: + desc.primitive_type = SG_PRIMITIVETYPE_POINTS; + break; + case SGL_PRIMITIVETYPE_LINES: + desc.primitive_type = SG_PRIMITIVETYPE_LINES; + break; + case SGL_PRIMITIVETYPE_LINE_STRIP: + desc.primitive_type = SG_PRIMITIVETYPE_LINE_STRIP; + break; + case SGL_PRIMITIVETYPE_TRIANGLES: + desc.primitive_type = SG_PRIMITIVETYPE_TRIANGLES; + break; + case SGL_PRIMITIVETYPE_TRIANGLE_STRIP: + case SGL_PRIMITIVETYPE_QUADS: + desc.primitive_type = SG_PRIMITIVETYPE_TRIANGLE_STRIP; + break; + } + if (SGL_PRIMITIVETYPE_QUADS == i) { + /* quads are emulated via triangles, use the same pipeline object */ + pip->pip[i] = pip->pip[SGL_PRIMITIVETYPE_TRIANGLES]; + } + else { + pip->pip[i] = sg_make_pipeline(&desc); + if (pip->pip[i].id == SG_INVALID_ID) { + SOKOL_LOG("sokol_gl.h: failed to create pipeline object"); + pip->slot.state = SG_RESOURCESTATE_FAILED; + } + } + } +} + +static sgl_pipeline _sgl_make_pipeline(const sg_pipeline_desc* desc) { + SOKOL_ASSERT(desc); + sgl_pipeline pip_id = _sgl_alloc_pipeline(); + if (pip_id.id != SG_INVALID_ID) { + _sgl_init_pipeline(pip_id, desc); + } + else { + SOKOL_LOG("sokol_gl.h: pipeline pool exhausted!"); + } + return pip_id; +} + +static void _sgl_destroy_pipeline(sgl_pipeline pip_id) { + _sgl_pipeline_t* pip = _sgl_lookup_pipeline(pip_id.id); + if (pip) { + for (int i = 0; i < SGL_NUM_PRIMITIVE_TYPES; i++) { + if (i != SGL_PRIMITIVETYPE_QUADS) { + sg_destroy_pipeline(pip->pip[i]); + } + } + _sgl_reset_pipeline(pip); + _sgl_pool_free_index(&_sgl.pip_pool.pool, _sgl_slot_index(pip_id.id)); + } +} + +static sg_pipeline _sgl_get_pipeline(sgl_pipeline pip_id, _sgl_primitive_type_t prim_type) { + _sgl_pipeline_t* pip = _sgl_lookup_pipeline(pip_id.id); + if (pip) { + return pip->pip[prim_type]; + } + else { + sg_pipeline dummy_id = { SG_INVALID_ID }; + return dummy_id; + } +} + +static inline void _sgl_begin(_sgl_primitive_type_t mode) { + _sgl.in_begin = true; + _sgl.base_vertex = _sgl.cur_vertex; + _sgl.vtx_count = 0; + _sgl.cur_prim_type = mode; +} + +static void _sgl_rewind(void) { + _sgl.base_vertex = 0; + _sgl.cur_vertex = 0; + _sgl.cur_uniform = 0; + _sgl.cur_command = 0; + _sgl.error = SGL_NO_ERROR; + _sgl.matrix_dirty = true; +} + +static inline _sgl_vertex_t* _sgl_next_vertex(void) { + if (_sgl.cur_vertex < _sgl.num_vertices) { + return &_sgl.vertices[_sgl.cur_vertex++]; + } + else { + _sgl.error = SGL_ERROR_VERTICES_FULL; + return 0; + } +} + +static inline _sgl_uniform_t* _sgl_next_uniform(void) { + if (_sgl.cur_uniform < _sgl.num_uniforms) { + return &_sgl.uniforms[_sgl.cur_uniform++]; + } + else { + _sgl.error = SGL_ERROR_UNIFORMS_FULL; + return 0; + } +} + +static inline _sgl_command_t* _sgl_prev_command(void) { + if (_sgl.cur_command > 0) { + return &_sgl.commands[_sgl.cur_command - 1]; + } + else { + return 0; + } +} + +static inline _sgl_command_t* _sgl_next_command(void) { + if (_sgl.cur_command < _sgl.num_commands) { + return &_sgl.commands[_sgl.cur_command++]; + } + else { + _sgl.error = SGL_ERROR_COMMANDS_FULL; + return 0; + } +} + +static inline uint32_t _sgl_pack_rgbab(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + return (uint32_t)(((uint32_t)a<<24)|((uint32_t)b<<16)|((uint32_t)g<<8)|r); +} + +static inline float _sgl_clamp(float v, float lo, float hi) { + if (v < lo) return lo; + else if (v > hi) return hi; + else return v; +} + +static inline uint32_t _sgl_pack_rgbaf(float r, float g, float b, float a) { + uint8_t r_u8 = (uint8_t) (_sgl_clamp(r, 0.0f, 1.0f) * 255.0f); + uint8_t g_u8 = (uint8_t) (_sgl_clamp(g, 0.0f, 1.0f) * 255.0f); + uint8_t b_u8 = (uint8_t) (_sgl_clamp(b, 0.0f, 1.0f) * 255.0f); + uint8_t a_u8 = (uint8_t) (_sgl_clamp(a, 0.0f, 1.0f) * 255.0f); + return _sgl_pack_rgbab(r_u8, g_u8, b_u8, a_u8); +} + +static inline void _sgl_vtx(float x, float y, float z, float u, float v, uint32_t rgba) { + SOKOL_ASSERT(_sgl.in_begin); + _sgl_vertex_t* vtx; + /* handle non-native primitive types */ + if ((_sgl.cur_prim_type == SGL_PRIMITIVETYPE_QUADS) && ((_sgl.vtx_count & 3) == 3)) { + /* for quads, before writing the last quad vertex, reuse + the first and third vertex to start the second triangle in the quad + */ + vtx = _sgl_next_vertex(); + if (vtx) { *vtx = *(vtx - 3); } + vtx = _sgl_next_vertex(); + if (vtx) { *vtx = *(vtx - 2); } + } + vtx = _sgl_next_vertex(); + if (vtx) { + vtx->pos[0] = x; vtx->pos[1] = y; vtx->pos[2] = z; + vtx->uv[0] = u; vtx->uv[1] = v; + vtx->rgba = rgba; + } + _sgl.vtx_count++; +} + +static void _sgl_identity(_sgl_matrix_t* m) { + for (int c = 0; c < 4; c++) { + for (int r = 0; r < 4; r++) { + m->v[c][r] = (r == c) ? 1.0f : 0.0f; + } + } +} + +static void _sgl_transpose(_sgl_matrix_t* dst, const _sgl_matrix_t* m) { + SOKOL_ASSERT(dst != m); + for (int c = 0; c < 4; c++) { + for (int r = 0; r < 4; r++) { + dst->v[r][c] = m->v[c][r]; + } + } +} + +/* _sgl_rotate, _sgl_frustum, _sgl_ortho from MESA m_matric.c */ +static void _sgl_matmul4(_sgl_matrix_t* p, const _sgl_matrix_t* a, const _sgl_matrix_t* b) { + for (int r = 0; r < 4; r++) { + float ai0=a->v[0][r], ai1=a->v[1][r], ai2=a->v[2][r], ai3=a->v[3][r]; + p->v[0][r] = ai0*b->v[0][0] + ai1*b->v[0][1] + ai2*b->v[0][2] + ai3*b->v[0][3]; + p->v[1][r] = ai0*b->v[1][0] + ai1*b->v[1][1] + ai2*b->v[1][2] + ai3*b->v[1][3]; + p->v[2][r] = ai0*b->v[2][0] + ai1*b->v[2][1] + ai2*b->v[2][2] + ai3*b->v[2][3]; + p->v[3][r] = ai0*b->v[3][0] + ai1*b->v[3][1] + ai2*b->v[3][2] + ai3*b->v[3][3]; + } +} + +static void _sgl_mul(_sgl_matrix_t* dst, const _sgl_matrix_t* m) { + _sgl_matmul4(dst, dst, m); +} + +static void _sgl_rotate(_sgl_matrix_t* dst, float a, float x, float y, float z) { + + float s = sinf(a); + float c = cosf(a); + + float mag = sqrtf(x*x + y*y + z*z); + if (mag < 1.0e-4F) { + return; + } + x /= mag; + y /= mag; + z /= mag; + float xx = x * x; + float yy = y * y; + float zz = z * z; + float xy = x * y; + float yz = y * z; + float zx = z * x; + float xs = x * s; + float ys = y * s; + float zs = z * s; + float one_c = 1.0f - c; + + _sgl_matrix_t m; + m.v[0][0] = (one_c * xx) + c; + m.v[1][0] = (one_c * xy) - zs; + m.v[2][0] = (one_c * zx) + ys; + m.v[3][0] = 0.0f; + m.v[0][1] = (one_c * xy) + zs; + m.v[1][1] = (one_c * yy) + c; + m.v[2][1] = (one_c * yz) - xs; + m.v[3][1] = 0.0f; + m.v[0][2] = (one_c * zx) - ys; + m.v[1][2] = (one_c * yz) + xs; + m.v[2][2] = (one_c * zz) + c; + m.v[3][2] = 0.0f; + m.v[0][3] = 0.0f; + m.v[1][3] = 0.0f; + m.v[2][3] = 0.0f; + m.v[3][3] = 1.0f; + _sgl_mul(dst, &m); +} + +static void _sgl_scale(_sgl_matrix_t* dst, float x, float y, float z) { + for (int r = 0; r < 4; r++) { + dst->v[0][r] *= x; + dst->v[1][r] *= y; + dst->v[2][r] *= z; + } +} + +static void _sgl_translate(_sgl_matrix_t* dst, float x, float y, float z) { + for (int r = 0; r < 4; r++) { + dst->v[3][r] = dst->v[0][r]*x + dst->v[1][r]*y + dst->v[2][r]*z + dst->v[3][r]; + } +} + +static void _sgl_frustum(_sgl_matrix_t* dst, float left, float right, float bottom, float top, float znear, float zfar) { + float x = (2.0f * znear) / (right - left); + float y = (2.0f * znear) / (top - bottom); + float a = (right + left) / (right - left); + float b = (top + bottom) / (top - bottom); + float c = -(zfar + znear) / (zfar - znear); + float d = -(2.0f * zfar * znear) / (zfar - znear); + _sgl_matrix_t m; + m.v[0][0] = x; m.v[0][1] = 0.0f; m.v[0][2] = 0.0f; m.v[0][3] = 0.0f; + m.v[1][0] = 0.0f; m.v[1][1] = y; m.v[1][2] = 0.0f; m.v[1][3] = 0.0f; + m.v[2][0] = a; m.v[2][1] = b; m.v[2][2] = c; m.v[2][3] = -1.0f; + m.v[3][0] = 0.0f; m.v[3][1] = 0.0f; m.v[3][2] = d; m.v[3][3] = 0.0f; + _sgl_mul(dst, &m); +} + +static void _sgl_ortho(_sgl_matrix_t* dst, float left, float right, float bottom, float top, float znear, float zfar) { + _sgl_matrix_t m; + m.v[0][0] = 2.0f / (right - left); + m.v[1][0] = 0.0f; + m.v[2][0] = 0.0f; + m.v[3][0] = -(right + left) / (right - left); + m.v[0][1] = 0.0f; + m.v[1][1] = 2.0f / (top - bottom); + m.v[2][1] = 0.0f; + m.v[3][1] = -(top + bottom) / (top - bottom); + m.v[0][2] = 0.0f; + m.v[1][2] = 0.0f; + m.v[2][2] = -2.0f / (zfar - znear); + m.v[3][2] = -(zfar + znear) / (zfar - znear); + m.v[0][3] = 0.0f; + m.v[1][3] = 0.0f; + m.v[2][3] = 0.0f; + m.v[3][3] = 1.0f; + + _sgl_mul(dst, &m); +} + +/* _sgl_perspective, _sgl_lookat from Regal project.c */ +static void _sgl_perspective(_sgl_matrix_t* dst, float fovy, float aspect, float znear, float zfar) { + float sine = sinf(fovy / 2.0f); + float delta_z = zfar - znear; + if ((delta_z == 0.0f) || (sine == 0.0f) || (aspect == 0.0f)) { + return; + } + float cotan = cosf(fovy / 2.0f) / sine; + _sgl_matrix_t m; + _sgl_identity(&m); + m.v[0][0] = cotan / aspect; + m.v[1][1] = cotan; + m.v[2][2] = -(zfar + znear) / delta_z; + m.v[2][3] = -1.0f; + m.v[3][2] = -2.0f * znear * zfar / delta_z; + m.v[3][3] = 0.0f; + _sgl_mul(dst, &m); +} + +static void _sgl_normalize(float v[3]) { + float r = sqrtf(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]); + if (r == 0.0f) { + return; + } + v[0] /= r; + v[1] /= r; + v[2] /= r; +} + +static void _sgl_cross(float v1[3], float v2[3], float res[3]) { + res[0] = v1[1]*v2[2] - v1[2]*v2[1]; + res[1] = v1[2]*v2[0] - v1[0]*v2[2]; + res[2] = v1[0]*v2[1] - v1[1]*v2[0]; +} + +static void _sgl_lookat(_sgl_matrix_t* dst, + float eye_x, float eye_y, float eye_z, + float center_x, float center_y, float center_z, + float up_x, float up_y, float up_z) +{ + float fwd[3], side[3], up[3]; + + fwd[0] = center_x - eye_x; fwd[1] = center_y - eye_y; fwd[2] = center_z - eye_z; + up[0] = up_x; up[1] = up_y; up[2] = up_z; + _sgl_normalize(fwd); + _sgl_cross(fwd, up, side); + _sgl_normalize(side); + _sgl_cross(side, fwd, up); + + _sgl_matrix_t m; + _sgl_identity(&m); + m.v[0][0] = side[0]; + m.v[1][0] = side[1]; + m.v[2][0] = side[2]; + m.v[0][1] = up[0]; + m.v[1][1] = up[1]; + m.v[2][1] = up[2]; + m.v[0][2] = -fwd[0]; + m.v[1][2] = -fwd[1]; + m.v[2][2] = -fwd[2]; + _sgl_mul(dst, &m); + _sgl_translate(dst, -eye_x, -eye_y, -eye_z); +} + +/* current top-of-stack projection matrix */ +static inline _sgl_matrix_t* _sgl_matrix_projection(void) { + return &_sgl.matrix_stack[SGL_MATRIXMODE_PROJECTION][_sgl.matrix_tos[SGL_MATRIXMODE_PROJECTION]]; +} + +/* get top-of-stack modelview matrix */ +static inline _sgl_matrix_t* _sgl_matrix_modelview(void) { + return &_sgl.matrix_stack[SGL_MATRIXMODE_MODELVIEW][_sgl.matrix_tos[SGL_MATRIXMODE_MODELVIEW]]; +} + +/* get top-of-stack texture matrix */ +static inline _sgl_matrix_t* _sgl_matrix_texture(void) { + return &_sgl.matrix_stack[SGL_MATRIXMODE_TEXTURE][_sgl.matrix_tos[SGL_MATRIXMODE_TEXTURE]]; +} + +/* get pointer to current top-of-stack of current matrix mode */ +static inline _sgl_matrix_t* _sgl_matrix(void) { + return &_sgl.matrix_stack[_sgl.cur_matrix_mode][_sgl.matrix_tos[_sgl.cur_matrix_mode]]; +} + +/*== PUBLIC FUNCTIONS ========================================================*/ +SOKOL_API_IMPL void sgl_setup(const sgl_desc_t* desc) { + SOKOL_ASSERT(desc); + memset(&_sgl, 0, sizeof(_sgl)); + _sgl.init_cookie = _SGL_INIT_COOKIE; + _sgl.desc = *desc; + _sgl.desc.pipeline_pool_size = _sgl_def(_sgl.desc.pipeline_pool_size, _SGL_DEFAULT_PIPELINE_POOL_SIZE); + _sgl.desc.max_vertices = _sgl_def(_sgl.desc.max_vertices, _SGL_DEFAULT_MAX_VERTICES); + _sgl.desc.max_commands = _sgl_def(_sgl.desc.max_commands, _SGL_DEFAULT_MAX_COMMANDS); + _sgl.desc.face_winding = _sgl_def(_sgl.desc.face_winding, SG_FACEWINDING_CCW); + + /* allocate buffers and pools */ + _sgl.num_vertices = _sgl.desc.max_vertices; + _sgl.num_uniforms = _sgl.desc.max_commands; + _sgl.num_commands = _sgl.num_uniforms; + _sgl.vertices = (_sgl_vertex_t*) SOKOL_MALLOC((size_t)_sgl.num_vertices * sizeof(_sgl_vertex_t)); + SOKOL_ASSERT(_sgl.vertices); + _sgl.uniforms = (_sgl_uniform_t*) SOKOL_MALLOC((size_t)_sgl.num_uniforms * sizeof(_sgl_uniform_t)); + SOKOL_ASSERT(_sgl.uniforms); + _sgl.commands = (_sgl_command_t*) SOKOL_MALLOC((size_t)_sgl.num_commands * sizeof(_sgl_command_t)); + SOKOL_ASSERT(_sgl.commands); + _sgl_setup_pipeline_pool(&_sgl.desc); + + /* create sokol-gfx resource objects */ + sg_push_debug_group("sokol-gl"); + + sg_buffer_desc vbuf_desc; + memset(&vbuf_desc, 0, sizeof(vbuf_desc)); + vbuf_desc.size = (size_t)_sgl.num_vertices * sizeof(_sgl_vertex_t); + vbuf_desc.type = SG_BUFFERTYPE_VERTEXBUFFER; + vbuf_desc.usage = SG_USAGE_STREAM; + vbuf_desc.label = "sgl-vertex-buffer"; + _sgl.vbuf = sg_make_buffer(&vbuf_desc); + SOKOL_ASSERT(SG_INVALID_ID != _sgl.vbuf.id); + + uint32_t pixels[64]; + for (int i = 0; i < 64; i++) { + pixels[i] = 0xFFFFFFFF; + } + sg_image_desc img_desc; + memset(&img_desc, 0, sizeof(img_desc)); + img_desc.type = SG_IMAGETYPE_2D; + img_desc.width = 8; + img_desc.height = 8; + img_desc.num_mipmaps = 1; + img_desc.pixel_format = SG_PIXELFORMAT_RGBA8; + img_desc.min_filter = SG_FILTER_NEAREST; + img_desc.mag_filter = SG_FILTER_NEAREST; + img_desc.data.subimage[0][0] = SG_RANGE(pixels); + img_desc.label = "sgl-default-texture"; + _sgl.def_img = sg_make_image(&img_desc); + SOKOL_ASSERT(SG_INVALID_ID != _sgl.def_img.id); + _sgl.cur_img = _sgl.def_img; + + sg_shader_desc shd_desc; + memset(&shd_desc, 0, sizeof(shd_desc)); + shd_desc.attrs[0].name = "position"; + shd_desc.attrs[1].name = "texcoord0"; + shd_desc.attrs[2].name = "color0"; + shd_desc.attrs[0].sem_name = "TEXCOORD"; + shd_desc.attrs[0].sem_index = 0; + shd_desc.attrs[1].sem_name = "TEXCOORD"; + shd_desc.attrs[1].sem_index = 1; + shd_desc.attrs[2].sem_name = "TEXCOORD"; + shd_desc.attrs[2].sem_index = 2; + sg_shader_uniform_block_desc* ub = &shd_desc.vs.uniform_blocks[0]; + ub->size = sizeof(_sgl_uniform_t); + ub->uniforms[0].name = "vs_params"; + ub->uniforms[0].type = SG_UNIFORMTYPE_FLOAT4; + ub->uniforms[0].array_count = 8; + shd_desc.fs.images[0].name = "tex"; + shd_desc.fs.images[0].image_type = SG_IMAGETYPE_2D; + shd_desc.fs.images[0].sampler_type = SG_SAMPLERTYPE_FLOAT; + shd_desc.label = "sgl-shader"; + #if defined(SOKOL_GLCORE33) + shd_desc.vs.source = _sgl_vs_source_glsl330; + shd_desc.fs.source = _sgl_fs_source_glsl330; + #elif defined(SOKOL_GLES2) || defined(SOKOL_GLES3) + shd_desc.vs.source = _sgl_vs_source_glsl100; + shd_desc.fs.source = _sgl_fs_source_glsl100; + #elif defined(SOKOL_METAL) + shd_desc.vs.entry = "main0"; + shd_desc.fs.entry = "main0"; + switch (sg_query_backend()) { + case SG_BACKEND_METAL_MACOS: + shd_desc.vs.bytecode = SG_RANGE(_sgl_vs_bytecode_metal_macos); + shd_desc.fs.bytecode = SG_RANGE(_sgl_fs_bytecode_metal_macos); + break; + case SG_BACKEND_METAL_IOS: + shd_desc.vs.bytecode = SG_RANGE(_sgl_vs_bytecode_metal_ios); + shd_desc.fs.bytecode = SG_RANGE(_sgl_fs_bytecode_metal_ios); + break; + default: + shd_desc.vs.source = _sgl_vs_source_metal_sim; + shd_desc.fs.source = _sgl_fs_source_metal_sim; + break; + } + #elif defined(SOKOL_D3D11) + shd_desc.vs.bytecode = SG_RANGE(_sgl_vs_bytecode_hlsl4); + shd_desc.fs.bytecode = SG_RANGE(_sgl_fs_bytecode_hlsl4); + #elif defined(SOKOL_WGPU) + shd_desc.vs.bytecode = SG_RANGE(_sgl_vs_bytecode_wgpu); + shd_desc.fs.bytecode = SG_RANGE(_sgl_fs_bytecode_wgpu); + #else + shd_desc.vs.source = _sgl_vs_src_dummy; + shd_desc.fs.source = _sgl_fs_src_dummy; + #endif + _sgl.shd = sg_make_shader(&shd_desc); + SOKOL_ASSERT(SG_INVALID_ID != _sgl.shd.id); + + /* create default pipeline object */ + sg_pipeline_desc def_pip_desc; + memset(&def_pip_desc, 0, sizeof(def_pip_desc)); + def_pip_desc.depth.write_enabled = true; + _sgl.def_pip = _sgl_make_pipeline(&def_pip_desc); + sg_pop_debug_group(); + + /* default state */ + _sgl.rgba = 0xFFFFFFFF; + for (int i = 0; i < SGL_NUM_MATRIXMODES; i++) { + _sgl_identity(&_sgl.matrix_stack[i][0]); + } + _sgl.pip_stack[0] = _sgl.def_pip; + _sgl.matrix_dirty = true; +} + +SOKOL_API_IMPL void sgl_shutdown(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + SOKOL_FREE(_sgl.vertices); _sgl.vertices = 0; + SOKOL_FREE(_sgl.uniforms); _sgl.uniforms = 0; + SOKOL_FREE(_sgl.commands); _sgl.commands = 0; + sg_push_debug_group("sokol-gl"); + sg_destroy_buffer(_sgl.vbuf); + sg_destroy_image(_sgl.def_img); + sg_destroy_shader(_sgl.shd); + for (int i = 0; i < _sgl.pip_pool.pool.size; i++) { + _sgl_pipeline_t* pip = &_sgl.pip_pool.pips[i]; + _sgl_destroy_pipeline(_sgl_make_pip_id(pip->slot.id)); + } + sg_pop_debug_group(); + _sgl_discard_pipeline_pool(); + _sgl.init_cookie = 0; +} + +SOKOL_API_IMPL sgl_error_t sgl_error(void) { + return _sgl.error; +} + +SOKOL_API_IMPL float sgl_rad(float deg) { + return (deg * (float)M_PI) / 180.0f; +} + +SOKOL_API_IMPL float sgl_deg(float rad) { + return (rad * 180.0f) / (float)M_PI; +} + +SOKOL_API_IMPL sgl_pipeline sgl_make_pipeline(const sg_pipeline_desc* desc) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + return _sgl_make_pipeline(desc); +} + +SOKOL_API_IMPL void sgl_destroy_pipeline(sgl_pipeline pip_id) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl_destroy_pipeline(pip_id); +} + +SOKOL_API_IMPL void sgl_load_pipeline(sgl_pipeline pip_id) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + SOKOL_ASSERT((_sgl.pip_tos >= 0) && (_sgl.pip_tos < _SGL_MAX_STACK_DEPTH)); + _sgl.pip_stack[_sgl.pip_tos] = pip_id; +} + +SOKOL_API_IMPL void sgl_default_pipeline(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + SOKOL_ASSERT((_sgl.pip_tos >= 0) && (_sgl.pip_tos < _SGL_MAX_STACK_DEPTH)); + _sgl.pip_stack[_sgl.pip_tos] = _sgl.def_pip; +} + +SOKOL_API_IMPL void sgl_push_pipeline(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + if (_sgl.pip_tos < (_SGL_MAX_STACK_DEPTH - 1)) { + _sgl.pip_tos++; + _sgl.pip_stack[_sgl.pip_tos] = _sgl.pip_stack[_sgl.pip_tos-1]; + } + else { + _sgl.error = SGL_ERROR_STACK_OVERFLOW; + } +} + +SOKOL_API_IMPL void sgl_pop_pipeline(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + if (_sgl.pip_tos > 0) { + _sgl.pip_tos--; + } + else { + _sgl.error = SGL_ERROR_STACK_UNDERFLOW; + } +} + +SOKOL_API_IMPL void sgl_defaults(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + SOKOL_ASSERT(!_sgl.in_begin); + _sgl.u = 0.0f; _sgl.v = 0.0f; + _sgl.rgba = 0xFFFFFFFF; + _sgl.texturing_enabled = false; + _sgl.cur_img = _sgl.def_img; + sgl_default_pipeline(); + _sgl_identity(_sgl_matrix_texture()); + _sgl_identity(_sgl_matrix_modelview()); + _sgl_identity(_sgl_matrix_projection()); + _sgl.cur_matrix_mode = SGL_MATRIXMODE_MODELVIEW; + _sgl.matrix_dirty = true; +} + +SOKOL_API_IMPL void sgl_viewport(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + SOKOL_ASSERT(!_sgl.in_begin); + _sgl_command_t* cmd = _sgl_next_command(); + if (cmd) { + cmd->cmd = SGL_COMMAND_VIEWPORT; + cmd->args.viewport.x = x; + cmd->args.viewport.y = y; + cmd->args.viewport.w = w; + cmd->args.viewport.h = h; + cmd->args.viewport.origin_top_left = origin_top_left; + } +} + +SOKOL_API_IMPL void sgl_viewportf(float x, float y, float w, float h, bool origin_top_left) { + sgl_viewport((int)x, (int)y, (int)w, (int)h, origin_top_left); +} + +SOKOL_API_IMPL void sgl_scissor_rect(int x, int y, int w, int h, bool origin_top_left) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + SOKOL_ASSERT(!_sgl.in_begin); + _sgl_command_t* cmd = _sgl_next_command(); + if (cmd) { + cmd->cmd = SGL_COMMAND_SCISSOR_RECT; + cmd->args.scissor_rect.x = x; + cmd->args.scissor_rect.y = y; + cmd->args.scissor_rect.w = w; + cmd->args.scissor_rect.h = h; + cmd->args.scissor_rect.origin_top_left = origin_top_left; + } +} + +SOKOL_API_IMPL void sgl_scissor_rectf(float x, float y, float w, float h, bool origin_top_left) { + sgl_scissor_rect((int)x, (int)y, (int)w, (int)h, origin_top_left); +} + +SOKOL_API_IMPL void sgl_enable_texture(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + SOKOL_ASSERT(!_sgl.in_begin); + _sgl.texturing_enabled = true; +} + +SOKOL_API_IMPL void sgl_disable_texture(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + SOKOL_ASSERT(!_sgl.in_begin); + _sgl.texturing_enabled = false; +} + +SOKOL_API_IMPL void sgl_texture(sg_image img) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + SOKOL_ASSERT(!_sgl.in_begin); + if (SG_INVALID_ID != img.id) { + _sgl.cur_img = img; + } + else { + _sgl.cur_img = _sgl.def_img; + } +} + +SOKOL_API_IMPL void sgl_begin_points(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + SOKOL_ASSERT(!_sgl.in_begin); + _sgl_begin(SGL_PRIMITIVETYPE_POINTS); +} + +SOKOL_API_IMPL void sgl_begin_lines(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + SOKOL_ASSERT(!_sgl.in_begin); + _sgl_begin(SGL_PRIMITIVETYPE_LINES); +} + +SOKOL_API_IMPL void sgl_begin_line_strip(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + SOKOL_ASSERT(!_sgl.in_begin); + _sgl_begin(SGL_PRIMITIVETYPE_LINE_STRIP); +} + +SOKOL_API_IMPL void sgl_begin_triangles(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + SOKOL_ASSERT(!_sgl.in_begin); + _sgl_begin(SGL_PRIMITIVETYPE_TRIANGLES); +} + +SOKOL_API_IMPL void sgl_begin_triangle_strip(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + SOKOL_ASSERT(!_sgl.in_begin); + _sgl_begin(SGL_PRIMITIVETYPE_TRIANGLE_STRIP); +} + +SOKOL_API_IMPL void sgl_begin_quads(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + SOKOL_ASSERT(!_sgl.in_begin); + _sgl_begin(SGL_PRIMITIVETYPE_QUADS); +} + +SOKOL_API_IMPL void sgl_end(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + SOKOL_ASSERT(_sgl.in_begin); + SOKOL_ASSERT(_sgl.cur_vertex >= _sgl.base_vertex); + _sgl.in_begin = false; + bool matrix_dirty = _sgl.matrix_dirty; + if (matrix_dirty) { + _sgl.matrix_dirty = false; + _sgl_uniform_t* uni = _sgl_next_uniform(); + if (uni) { + _sgl_matmul4(&uni->mvp, _sgl_matrix_projection(), _sgl_matrix_modelview()); + uni->tm = *_sgl_matrix_texture(); + } + } + /* check if command can be merged with previous command */ + sg_pipeline pip = _sgl_get_pipeline(_sgl.pip_stack[_sgl.pip_tos], _sgl.cur_prim_type); + sg_image img = _sgl.texturing_enabled ? _sgl.cur_img : _sgl.def_img; + _sgl_command_t* prev_cmd = _sgl_prev_command(); + bool merge_cmd = false; + if (prev_cmd) { + if ((prev_cmd->cmd == SGL_COMMAND_DRAW) && + (_sgl.cur_prim_type != SGL_PRIMITIVETYPE_LINE_STRIP) && + (_sgl.cur_prim_type != SGL_PRIMITIVETYPE_TRIANGLE_STRIP) && + !matrix_dirty && + (prev_cmd->args.draw.img.id == img.id) && + (prev_cmd->args.draw.pip.id == pip.id)) + { + merge_cmd = true; + } + } + if (merge_cmd) { + /* draw command can be merged with the previous command */ + prev_cmd->args.draw.num_vertices += _sgl.cur_vertex - _sgl.base_vertex; + } + else { + /* append a new draw command */ + _sgl_command_t* cmd = _sgl_next_command(); + if (cmd) { + SOKOL_ASSERT(_sgl.cur_uniform > 0); + cmd->cmd = SGL_COMMAND_DRAW; + cmd->args.draw.img = img; + cmd->args.draw.pip = _sgl_get_pipeline(_sgl.pip_stack[_sgl.pip_tos], _sgl.cur_prim_type); + cmd->args.draw.base_vertex = _sgl.base_vertex; + cmd->args.draw.num_vertices = _sgl.cur_vertex - _sgl.base_vertex; + cmd->args.draw.uniform_index = _sgl.cur_uniform - 1; + } + } +} + +SOKOL_API_IMPL void sgl_t2f(float u, float v) { + _sgl.u = u; _sgl.v = v; +} + +SOKOL_API_IMPL void sgl_c3f(float r, float g, float b) { + _sgl.rgba = _sgl_pack_rgbaf(r, g, b, 1.0f); +} + +SOKOL_API_IMPL void sgl_c4f(float r, float g, float b, float a) { + _sgl.rgba = _sgl_pack_rgbaf(r, g, b, a); +} + +SOKOL_API_IMPL void sgl_c3b(uint8_t r, uint8_t g, uint8_t b) { + _sgl.rgba = _sgl_pack_rgbab(r, g, b, 255); +} + +SOKOL_API_IMPL void sgl_c4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + _sgl.rgba = _sgl_pack_rgbab(r, g, b, a); +} + +SOKOL_API_IMPL void sgl_c1i(uint32_t rgba) { + _sgl.rgba = rgba; +} + +SOKOL_API_IMPL void sgl_v2f(float x, float y) { + _sgl_vtx(x, y, 0.0f, _sgl.u, _sgl.v, _sgl.rgba); +} + +SOKOL_API_IMPL void sgl_v3f(float x, float y, float z) { + _sgl_vtx(x, y, z, _sgl.u, _sgl.v, _sgl.rgba); +} + +SOKOL_API_IMPL void sgl_v2f_t2f(float x, float y, float u, float v) { + _sgl_vtx(x, y, 0.0f, u, v, _sgl.rgba); +} + +SOKOL_API_IMPL void sgl_v3f_t2f(float x, float y, float z, float u, float v) { + _sgl_vtx(x, y, z, u, v, _sgl.rgba); +} + +SOKOL_API_IMPL void sgl_v2f_c3f(float x, float y, float r, float g, float b) { + _sgl_vtx(x, y, 0.0f, _sgl.u, _sgl.v, _sgl_pack_rgbaf(r, g, b, 1.0f)); +} + +SOKOL_API_IMPL void sgl_v2f_c3b(float x, float y, uint8_t r, uint8_t g, uint8_t b) { + _sgl_vtx(x, y, 0.0f, _sgl.u, _sgl.v, _sgl_pack_rgbab(r, g, b, 255)); +} + +SOKOL_API_IMPL void sgl_v2f_c4f(float x, float y, float r, float g, float b, float a) { + _sgl_vtx(x, y, 0.0f, _sgl.u, _sgl.v, _sgl_pack_rgbaf(r, g, b, a)); +} + +SOKOL_API_IMPL void sgl_v2f_c4b(float x, float y, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + _sgl_vtx(x, y, 0.0f, _sgl.u, _sgl.v, _sgl_pack_rgbab(r, g, b, a)); +} + +SOKOL_API_IMPL void sgl_v2f_c1i(float x, float y, uint32_t rgba) { + _sgl_vtx(x, y, 0.0f, _sgl.u, _sgl.v, rgba); +} + +SOKOL_API_IMPL void sgl_v3f_c3f(float x, float y, float z, float r, float g, float b) { + _sgl_vtx(x, y, z, _sgl.u, _sgl.v, _sgl_pack_rgbaf(r, g, b, 1.0f)); +} + +SOKOL_API_IMPL void sgl_v3f_c3b(float x, float y, float z, uint8_t r, uint8_t g, uint8_t b) { + _sgl_vtx(x, y, z, _sgl.u, _sgl.v, _sgl_pack_rgbab(r, g, b, 255)); +} + +SOKOL_API_IMPL void sgl_v3f_c4f(float x, float y, float z, float r, float g, float b, float a) { + _sgl_vtx(x, y, z, _sgl.u, _sgl.v, _sgl_pack_rgbaf(r, g, b, a)); +} + +SOKOL_API_IMPL void sgl_v3f_c4b(float x, float y, float z, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + _sgl_vtx(x, y, z, _sgl.u, _sgl.v, _sgl_pack_rgbab(r, g, b, a)); +} + +SOKOL_API_IMPL void sgl_v3f_c1i(float x, float y, float z, uint32_t rgba) { + _sgl_vtx(x, y, z, _sgl.u, _sgl.v, rgba); +} + +SOKOL_API_IMPL void sgl_v2f_t2f_c3f(float x, float y, float u, float v, float r, float g, float b) { + _sgl_vtx(x, y, 0.0f, u, v, _sgl_pack_rgbaf(r, g, b, 1.0f)); +} + +SOKOL_API_IMPL void sgl_v2f_t2f_c3b(float x, float y, float u, float v, uint8_t r, uint8_t g, uint8_t b) { + _sgl_vtx(x, y, 0.0f, u, v, _sgl_pack_rgbab(r, g, b, 255)); +} + +SOKOL_API_IMPL void sgl_v2f_t2f_c4f(float x, float y, float u, float v, float r, float g, float b, float a) { + _sgl_vtx(x, y, 0.0f, u, v, _sgl_pack_rgbaf(r, g, b, a)); +} + +SOKOL_API_IMPL void sgl_v2f_t2f_c4b(float x, float y, float u, float v, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + _sgl_vtx(x, y, 0.0f, u, v, _sgl_pack_rgbab(r, g, b, a)); +} + +SOKOL_API_IMPL void sgl_v2f_t2f_c1i(float x, float y, float u, float v, uint32_t rgba) { + _sgl_vtx(x, y, 0.0f, u, v, rgba); +} + +SOKOL_API_IMPL void sgl_v3f_t2f_c3f(float x, float y, float z, float u, float v, float r, float g, float b) { + _sgl_vtx(x, y, z, u, v, _sgl_pack_rgbaf(r, g, b, 1.0f)); +} + +SOKOL_API_IMPL void sgl_v3f_t2f_c3b(float x, float y, float z, float u, float v, uint8_t r, uint8_t g, uint8_t b) { + _sgl_vtx(x, y, z, u, v, _sgl_pack_rgbab(r, g, b, 255)); +} + +SOKOL_API_IMPL void sgl_v3f_t2f_c4f(float x, float y, float z, float u, float v, float r, float g, float b, float a) { + _sgl_vtx(x, y, z, u, v, _sgl_pack_rgbaf(r, g, b, a)); +} + +SOKOL_API_IMPL void sgl_v3f_t2f_c4b(float x, float y, float z, float u, float v, uint8_t r, uint8_t g, uint8_t b, uint8_t a) { + _sgl_vtx(x, y, z, u, v, _sgl_pack_rgbab(r, g, b, a)); +} + +SOKOL_API_IMPL void sgl_v3f_t2f_c1i(float x, float y, float z, float u, float v, uint32_t rgba) { + _sgl_vtx(x, y, z, u, v, rgba); +} + +SOKOL_API_IMPL void sgl_matrix_mode_modelview(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl.cur_matrix_mode = SGL_MATRIXMODE_MODELVIEW; +} + +SOKOL_API_IMPL void sgl_matrix_mode_projection(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl.cur_matrix_mode = SGL_MATRIXMODE_PROJECTION; +} + +SOKOL_API_IMPL void sgl_matrix_mode_texture(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl.cur_matrix_mode = SGL_MATRIXMODE_TEXTURE; +} + +SOKOL_API_IMPL void sgl_load_identity(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl.matrix_dirty = true; + _sgl_identity(_sgl_matrix()); +} + +SOKOL_API_IMPL void sgl_load_matrix(const float m[16]) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl.matrix_dirty = true; + memcpy(&_sgl_matrix()->v[0][0], &m[0], 64); +} + +SOKOL_API_IMPL void sgl_load_transpose_matrix(const float m[16]) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl.matrix_dirty = true; + _sgl_transpose(_sgl_matrix(), (const _sgl_matrix_t*) &m[0]); +} + +SOKOL_API_IMPL void sgl_mult_matrix(const float m[16]) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl.matrix_dirty = true; + const _sgl_matrix_t* m0 = (const _sgl_matrix_t*) &m[0]; + _sgl_mul(_sgl_matrix(), m0); +} + +SOKOL_API_IMPL void sgl_mult_transpose_matrix(const float m[16]) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl.matrix_dirty = true; + _sgl_matrix_t m0; + _sgl_transpose(&m0, (const _sgl_matrix_t*) &m[0]); + _sgl_mul(_sgl_matrix(), &m0); +} + +SOKOL_API_IMPL void sgl_rotate(float angle_rad, float x, float y, float z) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl.matrix_dirty = true; + _sgl_rotate(_sgl_matrix(), angle_rad, x, y, z); +} + +SOKOL_API_IMPL void sgl_scale(float x, float y, float z) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl.matrix_dirty = true; + _sgl_scale(_sgl_matrix(), x, y, z); +} + +SOKOL_API_IMPL void sgl_translate(float x, float y, float z) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl.matrix_dirty = true; + _sgl_translate(_sgl_matrix(), x, y, z); +} + +SOKOL_API_IMPL void sgl_frustum(float l, float r, float b, float t, float n, float f) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl.matrix_dirty = true; + _sgl_frustum(_sgl_matrix(), l, r, b, t, n, f); +} + +SOKOL_API_IMPL void sgl_ortho(float l, float r, float b, float t, float n, float f) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl.matrix_dirty = true; + _sgl_ortho(_sgl_matrix(), l, r, b, t, n, f); +} + +SOKOL_API_IMPL void sgl_perspective(float fov_y, float aspect, float z_near, float z_far) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl.matrix_dirty = true; + _sgl_perspective(_sgl_matrix(), fov_y, aspect, z_near, z_far); +} + +SOKOL_API_IMPL void sgl_lookat(float eye_x, float eye_y, float eye_z, float center_x, float center_y, float center_z, float up_x, float up_y, float up_z) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + _sgl.matrix_dirty = true; + _sgl_lookat(_sgl_matrix(), eye_x, eye_y, eye_z, center_x, center_y, center_z, up_x, up_y, up_z); +} + +SOKOL_GL_API_DECL void sgl_push_matrix(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + SOKOL_ASSERT((_sgl.cur_matrix_mode >= 0) && (_sgl.cur_matrix_mode < SGL_NUM_MATRIXMODES)); + _sgl.matrix_dirty = true; + if (_sgl.matrix_tos[_sgl.cur_matrix_mode] < (_SGL_MAX_STACK_DEPTH - 1)) { + const _sgl_matrix_t* src = _sgl_matrix(); + _sgl.matrix_tos[_sgl.cur_matrix_mode]++; + _sgl_matrix_t* dst = _sgl_matrix(); + *dst = *src; + } + else { + _sgl.error = SGL_ERROR_STACK_OVERFLOW; + } +} + +SOKOL_GL_API_DECL void sgl_pop_matrix(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + SOKOL_ASSERT((_sgl.cur_matrix_mode >= 0) && (_sgl.cur_matrix_mode < SGL_NUM_MATRIXMODES)); + _sgl.matrix_dirty = true; + if (_sgl.matrix_tos[_sgl.cur_matrix_mode] > 0) { + _sgl.matrix_tos[_sgl.cur_matrix_mode]--; + } + else { + _sgl.error = SGL_ERROR_STACK_UNDERFLOW; + } +} + +/* this renders the accumulated draw commands via sokol-gfx */ +SOKOL_API_IMPL void sgl_draw(void) { + SOKOL_ASSERT(_SGL_INIT_COOKIE == _sgl.init_cookie); + if ((_sgl.error == SGL_NO_ERROR) && (_sgl.cur_vertex > 0) && (_sgl.cur_command > 0)) { + uint32_t cur_pip_id = SG_INVALID_ID; + uint32_t cur_img_id = SG_INVALID_ID; + int cur_uniform_index = -1; + sg_push_debug_group("sokol-gl"); + const sg_range range = { _sgl.vertices, (size_t)_sgl.cur_vertex * sizeof(_sgl_vertex_t) }; + sg_update_buffer(_sgl.vbuf, &range); + _sgl.bind.vertex_buffers[0] = _sgl.vbuf; + for (int i = 0; i < _sgl.cur_command; i++) { + const _sgl_command_t* cmd = &_sgl.commands[i]; + switch (cmd->cmd) { + case SGL_COMMAND_VIEWPORT: + { + const _sgl_viewport_args_t* args = &cmd->args.viewport; + sg_apply_viewport(args->x, args->y, args->w, args->h, args->origin_top_left); + } + break; + case SGL_COMMAND_SCISSOR_RECT: + { + const _sgl_scissor_rect_args_t* args = &cmd->args.scissor_rect; + sg_apply_scissor_rect(args->x, args->y, args->w, args->h, args->origin_top_left); + } + break; + case SGL_COMMAND_DRAW: + { + const _sgl_draw_args_t* args = &cmd->args.draw; + if (args->pip.id != cur_pip_id) { + sg_apply_pipeline(args->pip); + cur_pip_id = args->pip.id; + /* when pipeline changes, also need to re-apply uniforms and bindings */ + cur_img_id = SG_INVALID_ID; + cur_uniform_index = -1; + } + if (cur_img_id != args->img.id) { + _sgl.bind.fs_images[0] = args->img; + sg_apply_bindings(&_sgl.bind); + cur_img_id = args->img.id; + } + if (cur_uniform_index != args->uniform_index) { + const sg_range ub_range = { &_sgl.uniforms[args->uniform_index], sizeof(_sgl_uniform_t) }; + sg_apply_uniforms(SG_SHADERSTAGE_VS, 0, &ub_range); + cur_uniform_index = args->uniform_index; + } + /* FIXME: what if number of vertices doesn't match the primitive type? */ + if (args->num_vertices > 0) { + sg_draw(args->base_vertex, args->num_vertices, 1); + } + } + break; + } + } + sg_pop_debug_group(); + } + _sgl_rewind(); +} +#endif /* SOKOL_GL_IMPL */ diff --git a/v_windows/v/old/thirdparty/stb_image/stb_image.h b/v_windows/v/old/thirdparty/stb_image/stb_image.h new file mode 100644 index 0000000..816511c --- /dev/null +++ b/v_windows/v/old/thirdparty/stb_image/stb_image.h @@ -0,0 +1,7766 @@ +/* stb_image - v2.26 - public domain image loader - http://nothings.org/stb + no warranty implied; use at your own risk + + Do this: + #define STB_IMAGE_IMPLEMENTATION + before you include this file in *one* C or C++ file to create the implementation. + + // i.e. it should look like this: + #include ... + #include ... + #include ... + #define STB_IMAGE_IMPLEMENTATION + #include "stb_image.h" + + You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. + And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free + + + QUICK NOTES: + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface + + JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) + PNG 1/2/4/8/16-bit-per-channel + + TGA (not sure what subset, if a subset) + BMP non-1bpp, non-RLE + PSD (composited view only, no extra channels, 8/16 bit-per-channel) + + GIF (*comp always reports as 4-channel) + HDR (radiance rgbE format) + PIC (Softimage PIC) + PNM (PPM and PGM binary only) + + Animated GIF still needs a proper API, but here's one way to do it: + http://gist.github.com/urraka/685d9a6340b26b830d49 + + - decode from memory or through FILE (define STBI_NO_STDIO to remove code) + - decode from arbitrary I/O callbacks + - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) + + Full documentation under "DOCUMENTATION" below. + + +LICENSE + + See end of file for license information. + +RECENT REVISION HISTORY: + + 2.26 (2020-07-13) many minor fixes + 2.25 (2020-02-02) fix warnings + 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically + 2.23 (2019-08-11) fix clang static analysis warning + 2.22 (2019-03-04) gif fixes, fix warnings + 2.21 (2019-02-25) fix typo in comment + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings + 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes + 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 + RGB-format JPEG; remove white matting in PSD; + allocate large structures on the stack; + correct channel count for PNG & BMP + 2.10 (2016-01-22) avoid warning introduced in 2.09 + 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED + + See end of file for full revision history. + + + ============================ Contributors ========================= + + Image formats Extensions, features + Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) + Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) + Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) + Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) + Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) + Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) + Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) + github:urraka (animated gif) Junggon Kim (PNM comments) + Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) + socks-the-fox (16-bit PNG) + Jeremy Sawicki (handle all ImageNet JPGs) + Optimizations & bugfixes Mikhail Morozov (1-bit BMP) + Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) + Arseny Kapoulkine + John-Mark Allen + Carmelo J Fdez-Aguera + + Bug & warning fixes + Marc LeBlanc David Woo Guillaume George Martins Mozeiko + Christpher Lloyd Jerry Jansson Joseph Thomson Blazej Dariusz Roszkowski + Phil Jordan Dave Moore Roy Eltham + Hayaki Saito Nathan Reed Won Chun + Luke Graham Johan Duparc Nick Verigakis the Horde3D community + Thomas Ruf Ronny Chevalier github:rlyeh + Janez Zemva John Bartholomew Michal Cichon github:romigrou + Jonathan Blow Ken Hamada Tero Hanninen github:svdijk + Laurent Gomila Cort Stratton github:snagar + Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex + Cass Everitt Ryamond Barbiero github:grim210 + Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw + Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus + Josh Tobin Matthew Gregan github:poppolopoppo + Julian Raschke Gregory Mullen Christian Floisand github:darealshinji + Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 + Brad Weinberger Matvey Cherevko [reserved] + Luca Sas Alexander Veselov Zack Middleton [reserved] + Ryan C. Gordon [reserved] [reserved] + DO NOT ADD YOUR NAME HERE + + To add your name to the credits, pick a random blank space in the middle and fill it. + 80% of merge conflicts on stb PRs are due to people adding their name at the end + of the credits. +*/ + +#ifndef STBI_INCLUDE_STB_IMAGE_H +#define STBI_INCLUDE_STB_IMAGE_H + +// DOCUMENTATION +// +// Limitations: +// - no 12-bit-per-channel JPEG +// - no JPEGs with arithmetic coding +// - GIF always returns *comp=4 +// +// Basic usage (see HDR discussion below for HDR usage): +// int x,y,n; +// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); +// // ... process data if not NULL ... +// // ... x = width, y = height, n = # 8-bit components per pixel ... +// // ... replace '0' with '1'..'4' to force that many components per pixel +// // ... but 'n' will always be the number that it would have been if you said 0 +// stbi_image_free(data) +// +// Standard parameters: +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *channels_in_file -- outputs # of image components in image file +// int desired_channels -- if non-zero, # of image components requested in result +// +// The return value from an image loader is an 'unsigned char *' which points +// to the pixel data, or NULL on an allocation failure or if the image is +// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, +// with each pixel consisting of N interleaved 8-bit components; the first +// pixel pointed to is top-left-most in the image. There is no padding between +// image scanlines or between pixels, regardless of format. The number of +// components N is 'desired_channels' if desired_channels is non-zero, or +// *channels_in_file otherwise. If desired_channels is non-zero, +// *channels_in_file has the number of components that _would_ have been +// output otherwise. E.g. if you set desired_channels to 4, you will always +// get RGBA output, but you can check *channels_in_file to see if it's trivially +// opaque because e.g. there were only 3 channels in the source image. +// +// An output image with N components has the following components interleaved +// in this order in each pixel: +// +// N=#comp components +// 1 grey +// 2 grey, alpha +// 3 red, green, blue +// 4 red, green, blue, alpha +// +// If image loading fails for any reason, the return value will be NULL, +// and *x, *y, *channels_in_file will be unchanged. The function +// stbi_failure_reason() can be queried for an extremely brief, end-user +// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS +// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// more user-friendly ones. +// +// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. +// +// =========================================================================== +// +// UNICODE: +// +// If compiling for Windows and you wish to use Unicode filenames, compile +// with +// #define STBI_WINDOWS_UTF8 +// and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert +// Windows wchar_t filenames to utf8. +// +// =========================================================================== +// +// Philosophy +// +// stb libraries are designed with the following priorities: +// +// 1. easy to use +// 2. easy to maintain +// 3. good performance +// +// Sometimes I let "good performance" creep up in priority over "easy to maintain", +// and for best performance I may provide less-easy-to-use APIs that give higher +// performance, in addition to the easy-to-use ones. Nevertheless, it's important +// to keep in mind that from the standpoint of you, a client of this library, +// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. +// +// Some secondary priorities arise directly from the first two, some of which +// provide more explicit reasons why performance can't be emphasized. +// +// - Portable ("ease of use") +// - Small source code footprint ("easy to maintain") +// - No dependencies ("ease of use") +// +// =========================================================================== +// +// I/O callbacks +// +// I/O callbacks allow you to read from arbitrary sources, like packaged +// files or some other source. Data read from callbacks are processed +// through a small internal buffer (currently 128 bytes) to try to reduce +// overhead. +// +// The three functions you must define are "read" (reads some bytes of data), +// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). +// +// =========================================================================== +// +// SIMD support +// +// The JPEG decoder will try to automatically use SIMD kernels on x86 when +// supported by the compiler. For ARM Neon support, you must explicitly +// request it. +// +// (The old do-it-yourself SIMD API is no longer supported in the current +// code.) +// +// On x86, SSE2 will automatically be used when available based on a run-time +// test; if not, the generic C versions are used as a fall-back. On ARM targets, +// the typical path is to have separate builds for NEON and non-NEON devices +// (at least this is true for iOS and Android). Therefore, the NEON support is +// toggled by a build flag: define STBI_NEON to get NEON loops. +// +// If for some reason you do not want to use any of SIMD code, or if +// you have issues compiling it, you can disable it entirely by +// defining STBI_NO_SIMD. +// +// =========================================================================== +// +// HDR image support (disable by defining STBI_NO_HDR) +// +// stb_image supports loading HDR images in general, and currently the Radiance +// .HDR file format specifically. You can still load any file through the existing +// interface; if you attempt to load an HDR file, it will be automatically remapped +// to LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// both of these constants can be reconfigured through this interface: +// +// stbi_hdr_to_ldr_gamma(2.2f); +// stbi_hdr_to_ldr_scale(1.0f); +// +// (note, do not use _inverse_ constants; stbi_image will invert them +// appropriately). +// +// Additionally, there is a new, parallel interface for loading files as +// (linear) floats to preserve the full dynamic range: +// +// float *data = stbi_loadf(filename, &x, &y, &n, 0); +// +// If you load LDR images through this interface, those images will +// be promoted to floating point values, run through the inverse of +// constants corresponding to the above: +// +// stbi_ldr_to_hdr_scale(1.0f); +// stbi_ldr_to_hdr_gamma(2.2f); +// +// Finally, given a filename (or an open file or memory block--see header +// file for details) containing image data, you can query for the "most +// appropriate" interface to use (that is, whether the image is HDR or +// not), using: +// +// stbi_is_hdr(char *filename); +// +// =========================================================================== +// +// iPhone PNG support: +// +// By default we convert iphone-formatted PNGs back to RGB, even though +// they are internally encoded differently. You can disable this conversion +// by calling stbi_convert_iphone_png_to_rgb(0), in which case +// you will always just get the native iphone "format" through (which +// is BGR stored in RGB). +// +// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per +// pixel to remove any premultiplied alpha *only* if the image file explicitly +// says there's premultiplied data (currently only happens in iPhone images, +// and only if iPhone convert-to-rgb processing is on). +// +// =========================================================================== +// +// ADDITIONAL CONFIGURATION +// +// - You can suppress implementation of any of the decoders to reduce +// your code footprint by #defining one or more of the following +// symbols before creating the implementation. +// +// STBI_NO_JPEG +// STBI_NO_PNG +// STBI_NO_BMP +// STBI_NO_PSD +// STBI_NO_TGA +// STBI_NO_GIF +// STBI_NO_HDR +// STBI_NO_PIC +// STBI_NO_PNM (.ppm and .pgm) +// +// - You can request *only* certain decoders and suppress all other ones +// (this will be more forward-compatible, as addition of new decoders +// doesn't require you to disable them explicitly): +// +// STBI_ONLY_JPEG +// STBI_ONLY_PNG +// STBI_ONLY_BMP +// STBI_ONLY_PSD +// STBI_ONLY_TGA +// STBI_ONLY_GIF +// STBI_ONLY_HDR +// STBI_ONLY_PIC +// STBI_ONLY_PNM (.ppm and .pgm) +// +// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still +// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB +// +// - If you define STBI_MAX_DIMENSIONS, stb_image will reject images greater +// than that size (in either width or height) without further processing. +// This is to let programs in the wild set an upper bound to prevent +// denial-of-service attacks on untrusted data, as one could generate a +// valid image of gigantic dimensions and force stb_image to allocate a +// huge block of memory and spend disproportionate time decoding it. By +// default this is set to (1 << 24), which is 16777216, but that's still +// very big. + +#ifdef __TINYC__ +#define STBI_NO_SIMD +#endif + +#ifndef STBI_NO_STDIO +#include +#endif // STBI_NO_STDIO + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for desired_channels + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +#include +typedef unsigned char stbi_uc; +typedef unsigned short stbi_us; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef STBIDEF +#ifdef STB_IMAGE_STATIC +#define STBIDEF static +#else +#define STBIDEF extern +#endif +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - works on images of any type +// + +// +// load image by filename, open file, or memory buffer +// + +typedef struct +{ + int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative + int (*eof) (void *user); // returns nonzero if we are at end of file/data +} stbi_io_callbacks; + +//////////////////////////////////// +// +// 8-bits-per-channel interface +// + +STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +// for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +#endif + +#ifdef STBI_WINDOWS_UTF8 +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); +#endif + +//////////////////////////////////// +// +// 16-bits-per-channel interface +// + +STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +#endif + +//////////////////////////////////// +// +// float-per-channel interface +// +#ifndef STBI_NO_LINEAR + STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + + #ifndef STBI_NO_STDIO + STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); + #endif +#endif + +#ifndef STBI_NO_HDR + STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); + STBIDEF void stbi_hdr_to_ldr_scale(float scale); +#endif // STBI_NO_HDR + +#ifndef STBI_NO_LINEAR + STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); + STBIDEF void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_LINEAR + +// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename); +STBIDEF int stbi_is_hdr_from_file(FILE *f); +#endif // STBI_NO_STDIO + + +// get a VERY brief reason for failure +// on most compilers (and ALL modern mainstream compilers) this is threadsafe +STBIDEF const char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +STBIDEF void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); +STBIDEF int stbi_is_16_bit (char const *filename); +STBIDEF int stbi_is_16_bit_from_file(FILE *f); +#endif + + + +// for image formats that explicitly notate that they have premultiplied alpha, +// we just return the colors as stored in the file. set this flag to force +// unpremultiplication. results are undefined if the unpremultiply overflow. +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); + +// indicate whether we should process iphone images back to canonical format, +// or just pass them through "as-is" +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); + +// flip the image vertically, so the first pixel in the output array is the bottom left +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); + +// as above, but only applies to images loaded on the thread that calls the function +// this function is only available if your compiler supports thread-local variables; +// calling it will fail to link if your compiler doesn't +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip); + +// ZLIB client - used by PNG, available for other purposes + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); +STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H + +#ifdef STB_IMAGE_IMPLEMENTATION + +#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ + || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ + || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ + || defined(STBI_ONLY_ZLIB) + #ifndef STBI_ONLY_JPEG + #define STBI_NO_JPEG + #endif + #ifndef STBI_ONLY_PNG + #define STBI_NO_PNG + #endif + #ifndef STBI_ONLY_BMP + #define STBI_NO_BMP + #endif + #ifndef STBI_ONLY_PSD + #define STBI_NO_PSD + #endif + #ifndef STBI_ONLY_TGA + #define STBI_NO_TGA + #endif + #ifndef STBI_ONLY_GIF + #define STBI_NO_GIF + #endif + #ifndef STBI_ONLY_HDR + #define STBI_NO_HDR + #endif + #ifndef STBI_ONLY_PIC + #define STBI_NO_PIC + #endif + #ifndef STBI_ONLY_PNM + #define STBI_NO_PNM + #endif +#endif + +#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) +#define STBI_NO_ZLIB +#endif + + +#include +#include // ptrdiff_t on osx +#include +#include +#include + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +#include // ldexp, pow +#endif + +#ifndef STBI_NO_STDIO +#include +#endif + +#ifndef STBI_ASSERT +#include +#define STBI_ASSERT(x) assert(x) +#endif + +#ifdef __cplusplus +#define STBI_EXTERN extern "C" +#else +#define STBI_EXTERN extern +#endif + + +#ifndef _MSC_VER + #ifdef __cplusplus + #define stbi_inline inline + #else + #define stbi_inline + #endif +#else + #define stbi_inline __forceinline +#endif + +#ifndef STBI_NO_THREAD_LOCALS + #if defined(__cplusplus) && __cplusplus >= 201103L + #define STBI_THREAD_LOCAL thread_local + #elif defined(__GNUC__) && __GNUC__ < 5 + #define STBI_THREAD_LOCAL __thread + #elif defined(_MSC_VER) + #define STBI_THREAD_LOCAL __declspec(thread) + #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) + #define STBI_THREAD_LOCAL _Thread_local + #endif + + #ifndef STBI_THREAD_LOCAL + #if defined(__GNUC__) + #define STBI_THREAD_LOCAL __thread + #endif + #endif +#endif + +#ifdef _MSC_VER +typedef unsigned short stbi__uint16; +typedef signed short stbi__int16; +typedef unsigned int stbi__uint32; +typedef signed int stbi__int32; +#else +#include +typedef uint16_t stbi__uint16; +typedef int16_t stbi__int16; +typedef uint32_t stbi__uint32; +typedef int32_t stbi__int32; +#endif + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBI_NOTUSED(v) (void)(v) +#else +#define STBI_NOTUSED(v) (void)sizeof(v) +#endif + +#ifdef _MSC_VER +#define STBI_HAS_LROTL +#endif + +#ifdef STBI_HAS_LROTL + #define stbi_lrot(x,y) _lrotl(x,y) +#else + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) +#endif + +#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) +// ok +#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." +#endif + +#ifndef STBI_MALLOC +#define STBI_MALLOC(sz) malloc(sz) +#define STBI_REALLOC(p,newsz) realloc(p,newsz) +#define STBI_FREE(p) free(p) +#endif + +#ifndef STBI_REALLOC_SIZED +#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) +#endif + +// x86/x64 detection +#if defined(__x86_64__) || defined(_M_X64) +#define STBI__X64_TARGET +#elif defined(__i386) || defined(_M_IX86) +#define STBI__X86_TARGET +#endif + +#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) +// gcc doesn't support sse2 intrinsics unless you compile with -msse2, +// which in turn means it gets to use SSE2 everywhere. This is unfortunate, +// but previous attempts to provide the SSE2 functions with runtime +// detection caused numerous issues. The way architecture extensions are +// exposed in GCC/Clang is, sadly, not really suited for one-file libs. +// New behavior: if compiled with -msse2, we use SSE2 without any +// detection; if not, we don't use it at all. +#define STBI_NO_SIMD +#endif + +#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) +// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET +// +// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the +// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. +// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not +// simultaneously enabling "-mstackrealign". +// +// See https://github.com/nothings/stb/issues/81 for more information. +// +// So default to no SSE2 on 32-bit MinGW. If you've read this far and added +// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. +#define STBI_NO_SIMD +#endif + +#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) +#define STBI_SSE2 +#include + +#ifdef _MSC_VER + +#if _MSC_VER >= 1400 // not VC6 +#include // __cpuid +static int stbi__cpuid3(void) +{ + int info[4]; + __cpuid(info,1); + return info[3]; +} +#else +static int stbi__cpuid3(void) +{ + int res; + __asm { + mov eax,1 + cpuid + mov res,edx + } + return res; +} +#endif + +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + int info3 = stbi__cpuid3(); + return ((info3 >> 26) & 1) != 0; +} +#endif + +#else // assume GCC-style if not VC++ +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) + +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) +static int stbi__sse2_available(void) +{ + // If we're even attempting to compile this on GCC/Clang, that means + // -msse2 is on, which means the compiler is allowed to use SSE2 + // instructions at will, and so are we. + return 1; +} +#endif + +#endif +#endif + +// ARM NEON +#if defined(STBI_NO_SIMD) && defined(STBI_NEON) +#undef STBI_NEON +#endif + +#ifdef STBI_NEON +#include +// assume GCC or Clang on ARM targets +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) +#endif + +#ifndef STBI_SIMD_ALIGN +#define STBI_SIMD_ALIGN(type, name) type name +#endif + +#ifndef STBI_MAX_DIMENSIONS +#define STBI_MAX_DIMENSIONS (1 << 24) +#endif + +/////////////////////////////////////////////// +// +// stbi__context struct and start_xxx functions + +// stbi__context structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + stbi__uint32 img_x, img_y; + int img_n, img_out_n; + + stbi_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + stbi_uc buffer_start[128]; + int callback_already_read; + + stbi_uc *img_buffer, *img_buffer_end; + stbi_uc *img_buffer_original, *img_buffer_original_end; +} stbi__context; + + +static void stbi__refill_buffer(stbi__context *s); + +// initialize a memory-decode context +static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; + s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; +} + +// initialize a callback-based context +static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) +{ + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = s->buffer_start; + stbi__refill_buffer(s); + s->img_buffer_original_end = s->img_buffer_end; +} + +#ifndef STBI_NO_STDIO + +static int stbi__stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stbi__stdio_skip(void *user, int n) +{ + int ch; + fseek((FILE*) user, n, SEEK_CUR); + ch = fgetc((FILE*) user); /* have to read a byte to reset feof()'s flag */ + if (ch != EOF) { + ungetc(ch, (FILE *) user); /* push byte back onto stream if valid. */ + } +} + +static int stbi__stdio_eof(void *user) +{ + return feof((FILE*) user) || ferror((FILE *) user); +} + +static stbi_io_callbacks stbi__stdio_callbacks = +{ + stbi__stdio_read, + stbi__stdio_skip, + stbi__stdio_eof, +}; + +static void stbi__start_file(stbi__context *s, FILE *f) +{ + stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi__context *s) { } + +#endif // !STBI_NO_STDIO + +static void stbi__rewind(stbi__context *s) +{ + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; + s->img_buffer_end = s->img_buffer_original_end; +} + +enum +{ + STBI_ORDER_RGB, + STBI_ORDER_BGR +}; + +typedef struct +{ + int bits_per_channel; + int num_channels; + int channel_order; +} stbi__result_info; + +#ifndef STBI_NO_JPEG +static int stbi__jpeg_test(stbi__context *s); +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNG +static int stbi__png_test(stbi__context *s); +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__png_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_BMP +static int stbi__bmp_test(stbi__context *s); +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_TGA +static int stbi__tga_test(stbi__context *s); +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s); +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); +static int stbi__psd_is16(stbi__context *s); +#endif + +#ifndef STBI_NO_HDR +static int stbi__hdr_test(stbi__context *s); +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_test(stbi__context *s); +static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_GIF +static int stbi__gif_test(stbi__context *s); +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNM +static int stbi__pnm_test(stbi__context *s); +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +static +#ifdef STBI_THREAD_LOCAL +STBI_THREAD_LOCAL +#endif +const char *stbi__g_failure_reason; + +STBIDEF const char *stbi_failure_reason(void) +{ + return stbi__g_failure_reason; +} + +#ifndef STBI_NO_FAILURE_STRINGS +static int stbi__err(const char *str) +{ + stbi__g_failure_reason = str; + return 0; +} +#endif + +static void *stbi__malloc(size_t size) +{ + return STBI_MALLOC(size); +} + +// stb_image uses ints pervasively, including for offset calculations. +// therefore the largest decoded image size we can support with the +// current code, even on 64-bit targets, is INT_MAX. this is not a +// significant limitation for the intended use case. +// +// we do, however, need to make sure our size calculations don't +// overflow. hence a few helper functions for size calculations that +// multiply integers together, making sure that they're non-negative +// and no overflow occurs. + +// return 1 if the sum is valid, 0 on overflow. +// negative terms are considered invalid. +static int stbi__addsizes_valid(int a, int b) +{ + if (b < 0) return 0; + // now 0 <= b <= INT_MAX, hence also + // 0 <= INT_MAX - b <= INTMAX. + // And "a + b <= INT_MAX" (which might overflow) is the + // same as a <= INT_MAX - b (no overflow) + return a <= INT_MAX - b; +} + +// returns 1 if the product is valid, 0 on overflow. +// negative factors are considered invalid. +static int stbi__mul2sizes_valid(int a, int b) +{ + if (a < 0 || b < 0) return 0; + if (b == 0) return 1; // mul-by-0 is always safe + // portable way to check for no overflows in a*b + return a <= INT_MAX/b; +} + +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) +// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow +static int stbi__mad2sizes_valid(int a, int b, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); +} +#endif + +// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow +static int stbi__mad3sizes_valid(int a, int b, int c, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__addsizes_valid(a*b*c, add); +} + +// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); +} +#endif + +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) +// mallocs with size overflow checking +static void *stbi__malloc_mad2(int a, int b, int add) +{ + if (!stbi__mad2sizes_valid(a, b, add)) return NULL; + return stbi__malloc(a*b + add); +} +#endif + +static void *stbi__malloc_mad3(int a, int b, int c, int add) +{ + if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; + return stbi__malloc(a*b*c + add); +} + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) +{ + if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; + return stbi__malloc(a*b*c*d + add); +} +#endif + +// stbi__err - error +// stbi__errpf - error returning pointer to float +// stbi__errpuc - error returning pointer to unsigned char + +#ifdef STBI_NO_FAILURE_STRINGS + #define stbi__err(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define stbi__err(x,y) stbi__err(y) +#else + #define stbi__err(x,y) stbi__err(x) +#endif + +#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) +#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) + +STBIDEF void stbi_image_free(void *retval_from_stbi_load) +{ + STBI_FREE(retval_from_stbi_load); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +#endif + +#ifndef STBI_NO_HDR +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +static int stbi__vertically_flip_on_load_global = 0; + +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load_global = flag_true_if_should_flip; +} + +#ifndef STBI_THREAD_LOCAL +#define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global +#else +static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set; + +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load_local = flag_true_if_should_flip; + stbi__vertically_flip_on_load_set = 1; +} + +#define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \ + ? stbi__vertically_flip_on_load_local \ + : stbi__vertically_flip_on_load_global) +#endif // STBI_THREAD_LOCAL + +static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields + ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed + ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order + ri->num_channels = 0; + + #ifndef STBI_NO_JPEG + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PNG + if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_BMP + if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_GIF + if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PSD + if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); + #else + STBI_NOTUSED(bpc); + #endif + #ifndef STBI_NO_PIC + if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PNM + if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); + return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + + #ifndef STBI_NO_TGA + // test tga last because it's a crappy test! + if (stbi__tga_test(s)) + return stbi__tga_load(s,x,y,comp,req_comp, ri); + #endif + + return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); +} + +static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi_uc *reduced; + + reduced = (stbi_uc *) stbi__malloc(img_len); + if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling + + STBI_FREE(orig); + return reduced; +} + +static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi__uint16 *enlarged; + + enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); + if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff + + STBI_FREE(orig); + return enlarged; +} + +static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) +{ + int row; + size_t bytes_per_row = (size_t)w * bytes_per_pixel; + stbi_uc temp[2048]; + stbi_uc *bytes = (stbi_uc *)image; + + for (row = 0; row < (h>>1); row++) { + stbi_uc *row0 = bytes + row*bytes_per_row; + stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; + // swap row0 with row1 + size_t bytes_left = bytes_per_row; + while (bytes_left) { + size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); + memcpy(temp, row0, bytes_copy); + memcpy(row0, row1, bytes_copy); + memcpy(row1, temp, bytes_copy); + row0 += bytes_copy; + row1 += bytes_copy; + bytes_left -= bytes_copy; + } + } +} + +#ifndef STBI_NO_GIF +static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) +{ + int slice; + int slice_size = w * h * bytes_per_pixel; + + stbi_uc *bytes = (stbi_uc *)image; + for (slice = 0; slice < z; ++slice) { + stbi__vertical_flip(bytes, w, h, bytes_per_pixel); + bytes += slice_size; + } +} +#endif + +static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); + + if (result == NULL) + return NULL; + + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + + if (ri.bits_per_channel != 8) { + result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 8; + } + + // @TODO: move stbi__convert_format to here + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); + } + + return (unsigned char *) result; +} + +static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); + + if (result == NULL) + return NULL; + + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + + if (ri.bits_per_channel != 16) { + result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 16; + } + + // @TODO: move stbi__convert_format16 to here + // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); + } + + return (stbi__uint16 *) result; +} + +#if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR) +static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) +{ + if (stbi__vertically_flip_on_load && result != NULL) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); + } +} +#endif + +#ifndef STBI_NO_STDIO + +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); +STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); +#endif + +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) +{ + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); +} +#endif + +static FILE *stbi__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) + return 0; + + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) + return 0; + +#if _MSC_VER >= 1400 + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; +#else + f = _wfopen(wFilename, wMode); +#endif + +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + + +STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + unsigned char *result; + if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__uint16 *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + stbi__uint16 *result; + if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file_16(f,x,y,comp,req_comp); + fclose(f); + return result; +} + + +#endif //!STBI_NO_STDIO + +STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_mem(&s,buffer,len); + + result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); + if (stbi__vertically_flip_on_load) { + stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); + } + + return result; +} +#endif + +#ifndef STBI_NO_LINEAR +static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + stbi__result_info ri; + float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); + if (hdr_data) + stbi__float_postprocess(hdr_data,x,y,comp,req_comp); + return hdr_data; + } + #endif + data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); + if (data) + return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); +} + +STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + float *result; + FILE *f = stbi__fopen(filename, "rb"); + if (!f) return stbi__errpf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_file(&s,f); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} +#endif // !STBI_NO_STDIO + +#endif // !STBI_NO_LINEAR + +// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is +// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always +// reports false! + +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +STBIDEF int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + long pos = ftell(f); + int res; + stbi__context s; + stbi__start_file(&s,f); + res = stbi__hdr_test(&s); + fseek(f, pos, SEEK_SET); + return res; + #else + STBI_NOTUSED(f); + return 0; + #endif +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(clbk); + STBI_NOTUSED(user); + return 0; + #endif +} + +#ifndef STBI_NO_LINEAR +static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; + +STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } +STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } +#endif + +static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; + +STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } +STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + STBI__SCAN_load=0, + STBI__SCAN_type, + STBI__SCAN_header +}; + +static void stbi__refill_buffer(stbi__context *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original); + if (n == 0) { + // at end of file, treat same as if from memory, but need to handle case + // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file + s->read_from_callbacks = 0; + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start+1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +stbi_inline static stbi_uc stbi__get8(stbi__context *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + stbi__refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +stbi_inline static int stbi__at_eof(stbi__context *s) +{ + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) return 1; + } + + return s->img_buffer >= s->img_buffer_end; +} +#endif + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) +// nothing +#else +static void stbi__skip(stbi__context *s, int n) +{ + if (n == 0) return; // already there! + if (n < 0) { + s->img_buffer = s->img_buffer_end; + return; + } + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM) +// nothing +#else +static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) +{ + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + int res, count; + + memcpy(buffer, s->img_buffer, blen); + + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } + + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} +#endif + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else +static int stbi__get16be(stbi__context *s) +{ + int z = stbi__get8(s); + return (z << 8) + stbi__get8(s); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else +static stbi__uint32 stbi__get32be(stbi__context *s) +{ + stbi__uint32 z = stbi__get16be(s); + return (z << 16) + stbi__get16be(s); +} +#endif + +#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) +// nothing +#else +static int stbi__get16le(stbi__context *s) +{ + int z = stbi__get8(s); + return z + (stbi__get8(s) << 8); +} +#endif + +#ifndef STBI_NO_BMP +static stbi__uint32 stbi__get32le(stbi__context *s) +{ + stbi__uint32 z = stbi__get16le(s); + return z + (stbi__get16le(s) << 16); +} +#endif + +#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings + +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static stbi_uc stbi__compute_y(int r, int g, int b) +{ + return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else +static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); + if (good == NULL) { + STBI_FREE(data); + return stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return stbi__errpuc("unsupported", "Unsupported format conversion"); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else +static stbi__uint16 stbi__compute_y_16(int r, int g, int b) +{ + return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); +} +#endif + +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else +static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + stbi__uint16 *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); + if (good == NULL) { + STBI_FREE(data); + return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + stbi__uint16 *src = data + j * x * img_n ; + stbi__uint16 *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return (stbi__uint16*) stbi__errpuc("unsupported", "Unsupported format conversion"); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} +#endif + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output; + if (!data) return NULL; + output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); + } + } + if (n < comp) { + for (i=0; i < x*y; ++i) { + output[i*comp + n] = data[i*comp + n]/255.0f; + } + } + STBI_FREE(data); + return output; +} +#endif + +#ifndef STBI_NO_HDR +#define stbi__float2int(x) ((int) (x)) +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output; + if (!data) return NULL; + output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + } + STBI_FREE(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder +// +// simple implementation +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - some SIMD kernels for common paths on targets with SSE2/NEON +// - uses a lot of intermediate memory, could cache poorly + +#ifndef STBI_NO_JPEG + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + stbi_uc fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + stbi__uint16 code[256]; + stbi_uc values[256]; + stbi_uc size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} stbi__huffman; + +typedef struct +{ + stbi__context *s; + stbi__huffman huff_dc[4]; + stbi__huffman huff_ac[4]; + stbi__uint16 dequant[4][64]; + stbi__int16 fast_ac[4][1 << FAST_BITS]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + stbi_uc *data; + void *raw_data, *raw_coeff; + stbi_uc *linebuf; + short *coeff; // progressive only + int coeff_w, coeff_h; // number of 8x8 coefficient blocks + } img_comp[4]; + + stbi__uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int progressive; + int spec_start; + int spec_end; + int succ_high; + int succ_low; + int eob_run; + int jfif; + int app14_color_transform; // Adobe APP14 tag + int rgb; + + int scan_n, order[4]; + int restart_interval, todo; + +// kernels + void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); + void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); + stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); +} stbi__jpeg; + +static int stbi__build_huffman(stbi__huffman *h, int *count) +{ + int i,j,k=0; + unsigned int code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) + for (j=0; j < count[i]; ++j) + h->size[k++] = (stbi_uc) (i+1); + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (stbi__uint16) (code++); + if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (stbi_uc) i; + } + } + } + return 1; +} + +// build a table that decodes both magnitude and value of small ACs in +// one go. +static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) +{ + int i; + for (i=0; i < (1 << FAST_BITS); ++i) { + stbi_uc fast = h->fast[i]; + fast_ac[i] = 0; + if (fast < 255) { + int rs = h->values[fast]; + int run = (rs >> 4) & 15; + int magbits = rs & 15; + int len = h->size[fast]; + + if (magbits && len + magbits <= FAST_BITS) { + // magnitude code followed by receive_extend code + int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); + int m = 1 << (magbits - 1); + if (k < m) k += (~0U << magbits) + 1; + // if the result is small enough, we can fit it in fast_ac table + if (k >= -128 && k <= 127) + fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); + } + } + } +} + +static void stbi__grow_buffer_unsafe(stbi__jpeg *j) +{ + do { + unsigned int b = j->nomore ? 0 : stbi__get8(j->s); + if (b == 0xff) { + int c = stbi__get8(j->s); + while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; + STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; +} + +// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); + + sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB + k = stbi_lrot(j->code_buffer, n); + if (n < 0 || n >= (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))) return 0; + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k + (stbi__jbias[n] & ~sgn); +} + +// get some unsigned bits +stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) +{ + unsigned int k; + if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k; +} + +stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) +{ + unsigned int k; + if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); + k = j->code_buffer; + j->code_buffer <<= 1; + --j->code_bits; + return k & 0x80000000; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static const stbi_uc stbi__jpeg_dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) +{ + int diff,dc,k; + int t; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? stbi__extend_receive(j, t) : 0; + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc * dequant[0]); + + // decode AC components, see JPEG spec + k = 1; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * dequant[zig]); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); + } + } + } while (k < 64); + return 1; +} + +static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) +{ + int diff,dc; + int t; + if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + if (j->succ_high == 0) { + // first scan for DC coefficient, must be first + memset(data,0,64*sizeof(data[0])); // 0 all the ac values now + t = stbi__jpeg_huff_decode(j, hdc); + if (t == -1) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + diff = t ? stbi__extend_receive(j, t) : 0; + + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc << j->succ_low); + } else { + // refinement scan for DC coefficient + if (stbi__jpeg_get_bit(j)) + data[0] += (short) (1 << j->succ_low); + } + return 1; +} + +// @OPTIMIZE: store non-zigzagged during the decode passes, +// and only de-zigzag when dequantizing +static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) +{ + int k; + if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->succ_high == 0) { + int shift = j->succ_low; + + if (j->eob_run) { + --j->eob_run; + return 1; + } + + k = j->spec_start; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) << shift); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r); + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + --j->eob_run; + break; + } + k += 16; + } else { + k += r; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) << shift); + } + } + } while (k <= j->spec_end); + } else { + // refinement scan for these AC coefficients + + short bit = (short) (1 << j->succ_low); + + if (j->eob_run) { + --j->eob_run; + for (k = j->spec_start; k <= j->spec_end; ++k) { + short *p = &data[stbi__jpeg_dezigzag[k]]; + if (*p != 0) + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } + } else { + k = j->spec_start; + do { + int r,s; + int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r) - 1; + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + r = 64; // force end of block + } else { + // r=15 s=0 should write 16 0s, so we just do + // a run of 15 0s and then write s (which is 0), + // so we don't have to do anything special here + } + } else { + if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); + // sign bit + if (stbi__jpeg_get_bit(j)) + s = bit; + else + s = -bit; + } + + // advance by r + while (k <= j->spec_end) { + short *p = &data[stbi__jpeg_dezigzag[k++]]; + if (*p != 0) { + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } else { + if (r == 0) { + *p = (short) s; + break; + } + --r; + } + } + } while (k <= j->spec_end); + } + } + return 1; +} + +// take a -128..127 value and stbi__clamp it and convert to 0..255 +stbi_inline static stbi_uc stbi__clamp(int x) +{ + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (stbi_uc) x; +} + +#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) +#define stbi__fsh(x) ((x) * 4096) + +// derived from jidctint -- DCT_ISLOW +#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * stbi__f2f(0.5411961f); \ + t2 = p1 + p3*stbi__f2f(-1.847759065f); \ + t3 = p1 + p2*stbi__f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = stbi__fsh(p2+p3); \ + t1 = stbi__fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ + t0 = t0*stbi__f2f( 0.298631336f); \ + t1 = t1*stbi__f2f( 2.053119869f); \ + t2 = t2*stbi__f2f( 3.072711026f); \ + t3 = t3*stbi__f2f( 1.501321110f); \ + p1 = p5 + p1*stbi__f2f(-0.899976223f); \ + p2 = p5 + p2*stbi__f2f(-2.562915447f); \ + p3 = p3*stbi__f2f(-1.961570560f); \ + p4 = p4*stbi__f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) +{ + int i,val[64],*v=val; + stbi_uc *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0]*4; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128<<17); + x1 += 65536 + (128<<17); + x2 += 65536 + (128<<17); + x3 += 65536 + (128<<17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = stbi__clamp((x0+t3) >> 17); + o[7] = stbi__clamp((x0-t3) >> 17); + o[1] = stbi__clamp((x1+t2) >> 17); + o[6] = stbi__clamp((x1-t2) >> 17); + o[2] = stbi__clamp((x2+t1) >> 17); + o[5] = stbi__clamp((x2-t1) >> 17); + o[3] = stbi__clamp((x3+t0) >> 17); + o[4] = stbi__clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SSE2 +// sse2 integer IDCT. not the fastest possible implementation but it +// produces bit-identical results to the generic C version so it's +// fully "transparent". +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + // This is constructed to match our regular (generic) integer IDCT exactly. + __m128i row0, row1, row2, row3, row4, row5, row6, row7; + __m128i tmp; + + // dot product constant: even elems=x, odd elems=y + #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) + + // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) + // out(1) = c1[even]*x + c1[odd]*y + #define dct_rot(out0,out1, x,y,c0,c1) \ + __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ + __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ + __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ + __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ + __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ + __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) + + // out = in << 12 (in 16-bit, out 32-bit) + #define dct_widen(out, in) \ + __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ + __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) + + // wide add + #define dct_wadd(out, a, b) \ + __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_add_epi32(a##_h, b##_h) + + // wide sub + #define dct_wsub(out, a, b) \ + __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) + + // butterfly a/b, add bias, then shift by "s" and pack + #define dct_bfly32o(out0, out1, a,b,bias,s) \ + { \ + __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ + __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ + dct_wadd(sum, abiased, b); \ + dct_wsub(dif, abiased, b); \ + out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ + out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ + } + + // 8-bit interleave step (for transposes) + #define dct_interleave8(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi8(a, b); \ + b = _mm_unpackhi_epi8(tmp, b) + + // 16-bit interleave step (for transposes) + #define dct_interleave16(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi16(a, b); \ + b = _mm_unpackhi_epi16(tmp, b) + + #define dct_pass(bias,shift) \ + { \ + /* even part */ \ + dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ + __m128i sum04 = _mm_add_epi16(row0, row4); \ + __m128i dif04 = _mm_sub_epi16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ + dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ + __m128i sum17 = _mm_add_epi16(row1, row7); \ + __m128i sum35 = _mm_add_epi16(row3, row5); \ + dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ + dct_wadd(x4, y0o, y4o); \ + dct_wadd(x5, y1o, y5o); \ + dct_wadd(x6, y2o, y5o); \ + dct_wadd(x7, y3o, y4o); \ + dct_bfly32o(row0,row7, x0,x7,bias,shift); \ + dct_bfly32o(row1,row6, x1,x6,bias,shift); \ + dct_bfly32o(row2,row5, x2,x5,bias,shift); \ + dct_bfly32o(row3,row4, x3,x4,bias,shift); \ + } + + __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); + __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); + __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); + __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); + __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); + __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); + __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); + __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); + + // rounding biases in column/row passes, see stbi__idct_block for explanation. + __m128i bias_0 = _mm_set1_epi32(512); + __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); + + // load + row0 = _mm_load_si128((const __m128i *) (data + 0*8)); + row1 = _mm_load_si128((const __m128i *) (data + 1*8)); + row2 = _mm_load_si128((const __m128i *) (data + 2*8)); + row3 = _mm_load_si128((const __m128i *) (data + 3*8)); + row4 = _mm_load_si128((const __m128i *) (data + 4*8)); + row5 = _mm_load_si128((const __m128i *) (data + 5*8)); + row6 = _mm_load_si128((const __m128i *) (data + 6*8)); + row7 = _mm_load_si128((const __m128i *) (data + 7*8)); + + // column pass + dct_pass(bias_0, 10); + + { + // 16bit 8x8 transpose pass 1 + dct_interleave16(row0, row4); + dct_interleave16(row1, row5); + dct_interleave16(row2, row6); + dct_interleave16(row3, row7); + + // transpose pass 2 + dct_interleave16(row0, row2); + dct_interleave16(row1, row3); + dct_interleave16(row4, row6); + dct_interleave16(row5, row7); + + // transpose pass 3 + dct_interleave16(row0, row1); + dct_interleave16(row2, row3); + dct_interleave16(row4, row5); + dct_interleave16(row6, row7); + } + + // row pass + dct_pass(bias_1, 17); + + { + // pack + __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 + __m128i p1 = _mm_packus_epi16(row2, row3); + __m128i p2 = _mm_packus_epi16(row4, row5); + __m128i p3 = _mm_packus_epi16(row6, row7); + + // 8bit 8x8 transpose pass 1 + dct_interleave8(p0, p2); // a0e0a1e1... + dct_interleave8(p1, p3); // c0g0c1g1... + + // transpose pass 2 + dct_interleave8(p0, p1); // a0c0e0g0... + dct_interleave8(p2, p3); // b0d0f0h0... + + // transpose pass 3 + dct_interleave8(p0, p2); // a0b0c0d0... + dct_interleave8(p1, p3); // a4b4c4d4... + + // store + _mm_storel_epi64((__m128i *) out, p0); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p2); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p1); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p3); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); + } + +#undef dct_const +#undef dct_rot +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_interleave8 +#undef dct_interleave16 +#undef dct_pass +} + +#endif // STBI_SSE2 + +#ifdef STBI_NEON + +// NEON integer IDCT. should produce bit-identical +// results to the generic C version. +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; + + int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); + int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); + int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); + int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); + int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); + int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); + int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); + int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); + int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); + int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); + int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); + int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); + +#define dct_long_mul(out, inq, coeff) \ + int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) + +#define dct_long_mac(out, acc, inq, coeff) \ + int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) + +#define dct_widen(out, inq) \ + int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ + int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) + +// wide add +#define dct_wadd(out, a, b) \ + int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vaddq_s32(a##_h, b##_h) + +// wide sub +#define dct_wsub(out, a, b) \ + int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vsubq_s32(a##_h, b##_h) + +// butterfly a/b, then shift using "shiftop" by "s" and pack +#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ + { \ + dct_wadd(sum, a, b); \ + dct_wsub(dif, a, b); \ + out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ + out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ + } + +#define dct_pass(shiftop, shift) \ + { \ + /* even part */ \ + int16x8_t sum26 = vaddq_s16(row2, row6); \ + dct_long_mul(p1e, sum26, rot0_0); \ + dct_long_mac(t2e, p1e, row6, rot0_1); \ + dct_long_mac(t3e, p1e, row2, rot0_2); \ + int16x8_t sum04 = vaddq_s16(row0, row4); \ + int16x8_t dif04 = vsubq_s16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + int16x8_t sum15 = vaddq_s16(row1, row5); \ + int16x8_t sum17 = vaddq_s16(row1, row7); \ + int16x8_t sum35 = vaddq_s16(row3, row5); \ + int16x8_t sum37 = vaddq_s16(row3, row7); \ + int16x8_t sumodd = vaddq_s16(sum17, sum35); \ + dct_long_mul(p5o, sumodd, rot1_0); \ + dct_long_mac(p1o, p5o, sum17, rot1_1); \ + dct_long_mac(p2o, p5o, sum35, rot1_2); \ + dct_long_mul(p3o, sum37, rot2_0); \ + dct_long_mul(p4o, sum15, rot2_1); \ + dct_wadd(sump13o, p1o, p3o); \ + dct_wadd(sump24o, p2o, p4o); \ + dct_wadd(sump23o, p2o, p3o); \ + dct_wadd(sump14o, p1o, p4o); \ + dct_long_mac(x4, sump13o, row7, rot3_0); \ + dct_long_mac(x5, sump24o, row5, rot3_1); \ + dct_long_mac(x6, sump23o, row3, rot3_2); \ + dct_long_mac(x7, sump14o, row1, rot3_3); \ + dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ + dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ + dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ + dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ + } + + // load + row0 = vld1q_s16(data + 0*8); + row1 = vld1q_s16(data + 1*8); + row2 = vld1q_s16(data + 2*8); + row3 = vld1q_s16(data + 3*8); + row4 = vld1q_s16(data + 4*8); + row5 = vld1q_s16(data + 5*8); + row6 = vld1q_s16(data + 6*8); + row7 = vld1q_s16(data + 7*8); + + // add DC bias + row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); + + // column pass + dct_pass(vrshrn_n_s32, 10); + + // 16bit 8x8 transpose + { +// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. +// whether compilers actually get this is another story, sadly. +#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } +#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } + + // pass 1 + dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 + dct_trn16(row2, row3); + dct_trn16(row4, row5); + dct_trn16(row6, row7); + + // pass 2 + dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 + dct_trn32(row1, row3); + dct_trn32(row4, row6); + dct_trn32(row5, row7); + + // pass 3 + dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 + dct_trn64(row1, row5); + dct_trn64(row2, row6); + dct_trn64(row3, row7); + +#undef dct_trn16 +#undef dct_trn32 +#undef dct_trn64 + } + + // row pass + // vrshrn_n_s32 only supports shifts up to 16, we need + // 17. so do a non-rounding shift of 16 first then follow + // up with a rounding shift by 1. + dct_pass(vshrn_n_s32, 16); + + { + // pack and round + uint8x8_t p0 = vqrshrun_n_s16(row0, 1); + uint8x8_t p1 = vqrshrun_n_s16(row1, 1); + uint8x8_t p2 = vqrshrun_n_s16(row2, 1); + uint8x8_t p3 = vqrshrun_n_s16(row3, 1); + uint8x8_t p4 = vqrshrun_n_s16(row4, 1); + uint8x8_t p5 = vqrshrun_n_s16(row5, 1); + uint8x8_t p6 = vqrshrun_n_s16(row6, 1); + uint8x8_t p7 = vqrshrun_n_s16(row7, 1); + + // again, these can translate into one instruction, but often don't. +#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } +#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } + + // sadly can't use interleaved stores here since we only write + // 8 bytes to each scan line! + + // 8x8 8-bit transpose pass 1 + dct_trn8_8(p0, p1); + dct_trn8_8(p2, p3); + dct_trn8_8(p4, p5); + dct_trn8_8(p6, p7); + + // pass 2 + dct_trn8_16(p0, p2); + dct_trn8_16(p1, p3); + dct_trn8_16(p4, p6); + dct_trn8_16(p5, p7); + + // pass 3 + dct_trn8_32(p0, p4); + dct_trn8_32(p1, p5); + dct_trn8_32(p2, p6); + dct_trn8_32(p3, p7); + + // store + vst1_u8(out, p0); out += out_stride; + vst1_u8(out, p1); out += out_stride; + vst1_u8(out, p2); out += out_stride; + vst1_u8(out, p3); out += out_stride; + vst1_u8(out, p4); out += out_stride; + vst1_u8(out, p5); out += out_stride; + vst1_u8(out, p6); out += out_stride; + vst1_u8(out, p7); + +#undef dct_trn8_8 +#undef dct_trn8_16 +#undef dct_trn8_32 + } + +#undef dct_long_mul +#undef dct_long_mac +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_pass +} + +#endif // STBI_NEON + +#define STBI__MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static stbi_uc stbi__get_marker(stbi__jpeg *j) +{ + stbi_uc x; + if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } + x = stbi__get8(j->s); + if (x != 0xff) return STBI__MARKER_none; + while (x == 0xff) + x = stbi__get8(j->s); // consume repeated 0xff fill bytes + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, stbi__jpeg_reset the entropy decoder and +// the dc prediction +static void stbi__jpeg_reset(stbi__jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; + j->marker = STBI__MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + j->eob_run = 0; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int stbi__parse_entropy_coded_data(stbi__jpeg *z) +{ + stbi__jpeg_reset(z); + if (!z->progressive) { + if (z->scan_n == 1) { + int i,j; + STBI_SIMD_ALIGN(short, data[64]); + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + STBI_SIMD_ALIGN(short, data[64]); + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } else { + if (z->scan_n == 1) { + int i,j; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + if (z->spec_start == 0) { + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } else { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) + return 0; + } + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x); + int y2 = (j*z->img_comp[n].v + y); + short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } +} + +static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) +{ + int i; + for (i=0; i < 64; ++i) + data[i] *= dequant[i]; +} + +static void stbi__jpeg_finish(stbi__jpeg *z) +{ + if (z->progressive) { + // dequantize and idct the data + int i,j,n; + for (n=0; n < z->s->img_n; ++n) { + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + } + } + } + } +} + +static int stbi__process_marker(stbi__jpeg *z, int m) +{ + int L; + switch (m) { + case STBI__MARKER_none: // no marker found + return stbi__err("expected marker","Corrupt JPEG"); + + case 0xDD: // DRI - specify restart interval + if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); + z->restart_interval = stbi__get16be(z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = stbi__get16be(z->s)-2; + while (L > 0) { + int q = stbi__get8(z->s); + int p = q >> 4, sixteen = (p != 0); + int t = q & 15,i; + if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); + if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); + + for (i=0; i < 64; ++i) + z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); + L -= (sixteen ? 129 : 65); + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = stbi__get16be(z->s)-2; + while (L > 0) { + stbi_uc *v; + int sizes[16],i,n=0; + int q = stbi__get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = stbi__get8(z->s); + n += sizes[i]; + } + L -= 17; + if (tc == 0) { + if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < n; ++i) + v[i] = stbi__get8(z->s); + if (tc != 0) + stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); + L -= n; + } + return L==0; + } + + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + L = stbi__get16be(z->s); + if (L < 2) { + if (m == 0xFE) + return stbi__err("bad COM len","Corrupt JPEG"); + else + return stbi__err("bad APP len","Corrupt JPEG"); + } + L -= 2; + + if (m == 0xE0 && L >= 5) { // JFIF APP0 segment + static const unsigned char tag[5] = {'J','F','I','F','\0'}; + int ok = 1; + int i; + for (i=0; i < 5; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 5; + if (ok) + z->jfif = 1; + } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment + static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; + int ok = 1; + int i; + for (i=0; i < 6; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 6; + if (ok) { + stbi__get8(z->s); // version + stbi__get16be(z->s); // flags0 + stbi__get16be(z->s); // flags1 + z->app14_color_transform = stbi__get8(z->s); // color transform + L -= 6; + } + } + + stbi__skip(z->s, L); + return 1; + } + + return stbi__err("unknown marker","Corrupt JPEG"); +} + +// after we see SOS +static int stbi__process_scan_header(stbi__jpeg *z) +{ + int i; + int Ls = stbi__get16be(z->s); + z->scan_n = stbi__get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = stbi__get8(z->s), which; + int q = stbi__get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0; // no match + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + + { + int aa; + z->spec_start = stbi__get8(z->s); + z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 + aa = stbi__get8(z->s); + z->succ_high = (aa >> 4); + z->succ_low = (aa & 15); + if (z->progressive) { + if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) + return stbi__err("bad SOS", "Corrupt JPEG"); + } else { + if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); + if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); + z->spec_end = 63; + } + } + + return 1; +} + +static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) +{ + int i; + for (i=0; i < ncomp; ++i) { + if (z->img_comp[i].raw_data) { + STBI_FREE(z->img_comp[i].raw_data); + z->img_comp[i].raw_data = NULL; + z->img_comp[i].data = NULL; + } + if (z->img_comp[i].raw_coeff) { + STBI_FREE(z->img_comp[i].raw_coeff); + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].coeff = 0; + } + if (z->img_comp[i].linebuf) { + STBI_FREE(z->img_comp[i].linebuf); + z->img_comp[i].linebuf = NULL; + } + } + return why; +} + +static int stbi__process_frame_header(stbi__jpeg *z, int scan) +{ + stbi__context *s = z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG + p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + c = stbi__get8(s); + if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); + + z->rgb = 0; + for (i=0; i < s->img_n; ++i) { + static const unsigned char rgb[3] = { 'R', 'G', 'B' }; + z->img_comp[i].id = stbi__get8(s); + if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) + ++z->rgb; + q = stbi__get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); + z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); + } + + if (scan != STBI__SCAN_load) return 1; + + if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + // these sizes can't be more than 17 bits + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + // + // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) + // so these muls can't overflow with 32-bit ints (which we require) + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].coeff = 0; + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].linebuf = NULL; + z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); + if (z->img_comp[i].raw_data == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + // align blocks for idct using mmx/sse + z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + if (z->progressive) { + // w2, h2 are multiples of 8 (see above) + z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; + z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; + z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); + if (z->img_comp[i].raw_coeff == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); + } + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define stbi__DNL(x) ((x) == 0xdc) +#define stbi__SOI(x) ((x) == 0xd8) +#define stbi__EOI(x) ((x) == 0xd9) +#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) +#define stbi__SOS(x) ((x) == 0xda) + +#define stbi__SOF_progressive(x) ((x) == 0xc2) + +static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) +{ + int m; + z->jfif = 0; + z->app14_color_transform = -1; // valid values are 0,1,2 + z->marker = STBI__MARKER_none; // initialize cached marker to empty + m = stbi__get_marker(z); + if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); + if (scan == STBI__SCAN_type) return 1; + m = stbi__get_marker(z); + while (!stbi__SOF(m)) { + if (!stbi__process_marker(z,m)) return 0; + m = stbi__get_marker(z); + while (m == STBI__MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); + m = stbi__get_marker(z); + } + } + z->progressive = stbi__SOF_progressive(m); + if (!stbi__process_frame_header(z, scan)) return 0; + return 1; +} + +// decode image to YCbCr format +static int stbi__decode_jpeg_image(stbi__jpeg *j) +{ + int m; + for (m = 0; m < 4; m++) { + j->img_comp[m].raw_data = NULL; + j->img_comp[m].raw_coeff = NULL; + } + j->restart_interval = 0; + if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; + m = stbi__get_marker(j); + while (!stbi__EOI(m)) { + if (stbi__SOS(m)) { + if (!stbi__process_scan_header(j)) return 0; + if (!stbi__parse_entropy_coded_data(j)) return 0; + if (j->marker == STBI__MARKER_none ) { + // handle 0s at the end of image data from IP Kamera 9060 + while (!stbi__at_eof(j->s)) { + int x = stbi__get8(j->s); + if (x == 255) { + j->marker = stbi__get8(j->s); + break; + } + } + // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 + } + } else if (stbi__DNL(m)) { + int Ld = stbi__get16be(j->s); + stbi__uint32 NL = stbi__get16be(j->s); + if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); + if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); + } else { + if (!stbi__process_marker(j, m)) return 0; + } + m = stbi__get_marker(j); + } + if (j->progressive) + stbi__jpeg_finish(j); + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, + int w, int hs); + +#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) + +static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i=0; i < w; ++i) + out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + stbi_uc *input = in_near; + + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = stbi__div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = stbi__div4(n+input[i-1]); + out[i*2+1] = stbi__div4(n+input[i+1]); + } + out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); + + return out; +} + +#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) + +static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = stbi__div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i=0,t0,t1; + + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + // process groups of 8 pixels for as long as we can. + // note we can't handle the last pixel in a row in this loop + // because we need to handle the filter boundary conditions. + for (; i < ((w-1) & ~7); i += 8) { +#if defined(STBI_SSE2) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + __m128i zero = _mm_setzero_si128(); + __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); + __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); + __m128i farw = _mm_unpacklo_epi8(farb, zero); + __m128i nearw = _mm_unpacklo_epi8(nearb, zero); + __m128i diff = _mm_sub_epi16(farw, nearw); + __m128i nears = _mm_slli_epi16(nearw, 2); + __m128i curr = _mm_add_epi16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + __m128i prv0 = _mm_slli_si128(curr, 2); + __m128i nxt0 = _mm_srli_si128(curr, 2); + __m128i prev = _mm_insert_epi16(prv0, t1, 0); + __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + __m128i bias = _mm_set1_epi16(8); + __m128i curs = _mm_slli_epi16(curr, 2); + __m128i prvd = _mm_sub_epi16(prev, curr); + __m128i nxtd = _mm_sub_epi16(next, curr); + __m128i curb = _mm_add_epi16(curs, bias); + __m128i even = _mm_add_epi16(prvd, curb); + __m128i odd = _mm_add_epi16(nxtd, curb); + + // interleave even and odd pixels, then undo scaling. + __m128i int0 = _mm_unpacklo_epi16(even, odd); + __m128i int1 = _mm_unpackhi_epi16(even, odd); + __m128i de0 = _mm_srli_epi16(int0, 4); + __m128i de1 = _mm_srli_epi16(int1, 4); + + // pack and write output + __m128i outv = _mm_packus_epi16(de0, de1); + _mm_storeu_si128((__m128i *) (out + i*2), outv); +#elif defined(STBI_NEON) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + uint8x8_t farb = vld1_u8(in_far + i); + uint8x8_t nearb = vld1_u8(in_near + i); + int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); + int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); + int16x8_t curr = vaddq_s16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + int16x8_t prv0 = vextq_s16(curr, curr, 7); + int16x8_t nxt0 = vextq_s16(curr, curr, 1); + int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); + int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + int16x8_t curs = vshlq_n_s16(curr, 2); + int16x8_t prvd = vsubq_s16(prev, curr); + int16x8_t nxtd = vsubq_s16(next, curr); + int16x8_t even = vaddq_s16(curs, prvd); + int16x8_t odd = vaddq_s16(curs, nxtd); + + // undo scaling and round, then store with even/odd phases interleaved + uint8x8x2_t o; + o.val[0] = vqrshrun_n_s16(even, 4); + o.val[1] = vqrshrun_n_s16(odd, 4); + vst2_u8(out + i*2, o); +#endif + + // "previous" value for next iter + t1 = 3*in_near[i+7] + in_far[i+7]; + } + + t0 = t1; + t1 = 3*in_near[i] + in_far[i]; + out[i*2] = stbi__div16(3*t1 + t0 + 8); + + for (++i; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} +#endif + +static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + STBI_NOTUSED(in_far); + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +// this is a reduced-precision calculation of YCbCr-to-RGB introduced +// to make sure the code produces the same results in both SIMD and scalar +#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) +{ + int i = 0; + +#ifdef STBI_SSE2 + // step == 3 is pretty ugly on the final interleave, and i'm not convinced + // it's useful in practice (you wouldn't use it for textures, for example). + // so just accelerate step == 4 case. + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + __m128i signflip = _mm_set1_epi8(-0x80); + __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); + __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); + __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); + __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); + __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); + __m128i xw = _mm_set1_epi16(255); // alpha channel + + for (; i+7 < count; i += 8) { + // load + __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); + __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); + __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); + __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 + __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 + + // unpack to short (and left-shift cr, cb by 8) + __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); + __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); + __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); + + // color transform + __m128i yws = _mm_srli_epi16(yw, 4); + __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); + __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); + __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); + __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); + __m128i rws = _mm_add_epi16(cr0, yws); + __m128i gwt = _mm_add_epi16(cb0, yws); + __m128i bws = _mm_add_epi16(yws, cb1); + __m128i gws = _mm_add_epi16(gwt, cr1); + + // descale + __m128i rw = _mm_srai_epi16(rws, 4); + __m128i bw = _mm_srai_epi16(bws, 4); + __m128i gw = _mm_srai_epi16(gws, 4); + + // back to byte, set up for transpose + __m128i brb = _mm_packus_epi16(rw, bw); + __m128i gxb = _mm_packus_epi16(gw, xw); + + // transpose to interleave channels + __m128i t0 = _mm_unpacklo_epi8(brb, gxb); + __m128i t1 = _mm_unpackhi_epi8(brb, gxb); + __m128i o0 = _mm_unpacklo_epi16(t0, t1); + __m128i o1 = _mm_unpackhi_epi16(t0, t1); + + // store + _mm_storeu_si128((__m128i *) (out + 0), o0); + _mm_storeu_si128((__m128i *) (out + 16), o1); + out += 32; + } + } +#endif + +#ifdef STBI_NEON + // in this version, step=3 support would be easy to add. but is there demand? + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + uint8x8_t signflip = vdup_n_u8(0x80); + int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); + int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); + int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); + int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); + + for (; i+7 < count; i += 8) { + // load + uint8x8_t y_bytes = vld1_u8(y + i); + uint8x8_t cr_bytes = vld1_u8(pcr + i); + uint8x8_t cb_bytes = vld1_u8(pcb + i); + int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); + int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); + + // expand to s16 + int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); + int16x8_t crw = vshll_n_s8(cr_biased, 7); + int16x8_t cbw = vshll_n_s8(cb_biased, 7); + + // color transform + int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); + int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); + int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); + int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); + int16x8_t rws = vaddq_s16(yws, cr0); + int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); + int16x8_t bws = vaddq_s16(yws, cb1); + + // undo scaling, round, convert to byte + uint8x8x4_t o; + o.val[0] = vqrshrun_n_s16(rws, 4); + o.val[1] = vqrshrun_n_s16(gws, 4); + o.val[2] = vqrshrun_n_s16(bws, 4); + o.val[3] = vdup_n_u8(255); + + // store, interleaving r/g/b/a + vst4_u8(out, o); + out += 8*4; + } + } +#endif + + for (; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#endif + +// set up the kernels +static void stbi__setup_jpeg(stbi__jpeg *j) +{ + j->idct_block_kernel = stbi__idct_block; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; + +#ifdef STBI_SSE2 + if (stbi__sse2_available()) { + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; + } +#endif + +#ifdef STBI_NEON + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; +#endif +} + +// clean up the temporary component buffers +static void stbi__cleanup_jpeg(stbi__jpeg *j) +{ + stbi__free_jpeg_components(j, j->s->img_n, 0); +} + +typedef struct +{ + resample_row_func resample; + stbi_uc *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi__resample; + +// fast 0..255 * 0..255 => 0..255 rounded multiplication +static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) +{ + unsigned int t = x*y + 128; + return (stbi_uc) ((t + (t >>8)) >> 8); +} + +static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n, is_rgb; + z->s->img_n = 0; // make stbi__cleanup_jpeg safe + + // validate req_comp + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + + // load a jpeg image from whichever source, but leave in YCbCr format + if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; + + is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); + + if (z->s->img_n == 3 && n < 3 && !is_rgb) + decode_n = 1; + else + decode_n = z->s->img_n; + + // resample and color-convert + { + int k; + unsigned int i,j; + stbi_uc *output; + stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; + + stbi__resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; + else r->resample = stbi__resample_row_generic; + } + + // can't error after this so, this is safe + output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); + if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + stbi_uc *out = output + n * z->s->img_x * j; + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + stbi_uc *y = coutput[0]; + if (z->s->img_n == 3) { + if (is_rgb) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = y[i]; + out[1] = coutput[1][i]; + out[2] = coutput[2][i]; + out[3] = 255; + out += n; + } + } else { + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else if (z->s->img_n == 4) { + if (z->app14_color_transform == 0) { // CMYK + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(coutput[0][i], m); + out[1] = stbi__blinn_8x8(coutput[1][i], m); + out[2] = stbi__blinn_8x8(coutput[2][i], m); + out[3] = 255; + out += n; + } + } else if (z->app14_color_transform == 2) { // YCCK + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(255 - out[0], m); + out[1] = stbi__blinn_8x8(255 - out[1], m); + out[2] = stbi__blinn_8x8(255 - out[2], m); + out += n; + } + } else { // YCbCr + alpha? Ignore the fourth channel for now + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else + for (i=0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + if (is_rgb) { + if (n == 1) + for (i=0; i < z->s->img_x; ++i) + *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + else { + for (i=0; i < z->s->img_x; ++i, out += 2) { + out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + out[1] = 255; + } + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); + stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); + stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); + out[0] = stbi__compute_y(r, g, b); + out[1] = 255; + out += n; + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); + out[1] = 255; + out += n; + } + } else { + stbi_uc *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; } + } + } + } + stbi__cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output + return output; + } +} + +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + unsigned char* result; + stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); + STBI_NOTUSED(ri); + j->s = s; + stbi__setup_jpeg(j); + result = load_jpeg_image(j, x,y,comp,req_comp); + STBI_FREE(j); + return result; +} + +static int stbi__jpeg_test(stbi__context *s) +{ + int r; + stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); + j->s = s; + stbi__setup_jpeg(j); + r = stbi__decode_jpeg_header(j, STBI__SCAN_type); + stbi__rewind(s); + STBI_FREE(j); + return r; +} + +static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) +{ + if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { + stbi__rewind( j->s ); + return 0; + } + if (x) *x = j->s->img_x; + if (y) *y = j->s->img_y; + if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; + return 1; +} + +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) +{ + int result; + stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); + j->s = s; + result = stbi__jpeg_info_raw(j, x, y, comp); + STBI_FREE(j); + return result; +} +#endif + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +#ifndef STBI_NO_ZLIB + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables +#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + stbi__uint16 fast[1 << STBI__ZFAST_BITS]; + stbi__uint16 firstcode[16]; + int maxcode[17]; + stbi__uint16 firstsymbol[16]; + stbi_uc size[288]; + stbi__uint16 value[288]; +} stbi__zhuffman; + +stbi_inline static int stbi__bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +stbi_inline static int stbi__bit_reverse(int v, int bits) +{ + STBI_ASSERT(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return stbi__bitreverse16(v) >> (16-bits); +} + +static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 0, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + if (sizes[i] > (1 << i)) + return stbi__err("bad sizes", "Corrupt PNG"); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (stbi__uint16) code; + z->firstsymbol[i] = (stbi__uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); + z->size [c] = (stbi_uc ) s; + z->value[c] = (stbi__uint16) i; + if (s <= STBI__ZFAST_BITS) { + int j = stbi__bit_reverse(next_code[s],s); + while (j < (1 << STBI__ZFAST_BITS)) { + z->fast[j] = fastv; + j += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + stbi_uc *zbuffer, *zbuffer_end; + int num_bits; + stbi__uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + stbi__zhuffman z_length, z_distance; +} stbi__zbuf; + +stbi_inline static int stbi__zeof(stbi__zbuf *z) +{ + return (z->zbuffer >= z->zbuffer_end); +} + +stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) +{ + return stbi__zeof(z) ? 0 : *z->zbuffer++; +} + +static void stbi__fill_bits(stbi__zbuf *z) +{ + do { + if (z->code_buffer >= (1U << z->num_bits)) { + z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */ + return; + } + z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) stbi__fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s,k; + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = stbi__bit_reverse(a->code_buffer, 16); + for (s=STBI__ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s >= 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + if (b >= sizeof (z->size)) return -1; // some data was corrupt somewhere! + if (z->size[b] != s) return -1; // was originally an assert, but report failure instead. + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s; + if (a->num_bits < 16) { + if (stbi__zeof(a)) { + return -1; /* report error for unexpected end of data. */ + } + stbi__fill_bits(a); + } + b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; + if (b) { + s = b >> 9; + a->code_buffer >>= s; + a->num_bits -= s; + return b & 511; + } + return stbi__zhuffman_decode_slowpath(a, z); +} + +static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes +{ + char *q; + unsigned int cur, limit, old_limit; + z->zout = zout; + if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); + cur = (unsigned int) (z->zout - z->zout_start); + limit = old_limit = (unsigned) (z->zout_end - z->zout_start); + if (UINT_MAX - cur < (unsigned) n) return stbi__err("outofmem", "Out of memory"); + while (cur + n > limit) { + if(limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory"); + limit *= 2; + } + q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); + STBI_NOTUSED(old_limit); + if (q == NULL) return stbi__err("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static const int stbi__zlength_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static const int stbi__zlength_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static const int stbi__zdist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int stbi__parse_huffman_block(stbi__zbuf *a) +{ + char *zout = a->zout; + for(;;) { + int z = stbi__zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes + if (zout >= a->zout_end) { + if (!stbi__zexpand(a, zout, 1)) return 0; + zout = a->zout; + } + *zout++ = (char) z; + } else { + stbi_uc *p; + int len,dist; + if (z == 256) { + a->zout = zout; + return 1; + } + z -= 257; + len = stbi__zlength_base[z]; + if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); + z = stbi__zhuffman_decode(a, &a->z_distance); + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); + dist = stbi__zdist_base[z]; + if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); + if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); + if (zout + len > a->zout_end) { + if (!stbi__zexpand(a, zout, len)) return 0; + zout = a->zout; + } + p = (stbi_uc *) (zout - dist); + if (dist == 1) { // run of one byte; common in images. + stbi_uc v = *p; + if (len) { do *zout++ = v; while (--len); } + } else { + if (len) { do *zout++ = *p++; while (--len); } + } + } + } +} + +static int stbi__compute_huffman_codes(stbi__zbuf *a) +{ + static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + stbi__zhuffman z_codelength; + stbi_uc lencodes[286+32+137];//padding for maximum single op + stbi_uc codelength_sizes[19]; + int i,n; + + int hlit = stbi__zreceive(a,5) + 257; + int hdist = stbi__zreceive(a,5) + 1; + int hclen = stbi__zreceive(a,4) + 4; + int ntot = hlit + hdist; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = stbi__zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; + } + if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < ntot) { + int c = stbi__zhuffman_decode(a, &z_codelength); + if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); + if (c < 16) + lencodes[n++] = (stbi_uc) c; + else { + stbi_uc fill = 0; + if (c == 16) { + c = stbi__zreceive(a,2)+3; + if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); + fill = lencodes[n-1]; + } else if (c == 17) { + c = stbi__zreceive(a,3)+3; + } else if (c == 18) { + c = stbi__zreceive(a,7)+11; + } else { + return stbi__err("bad codelengths", "Corrupt PNG"); + } + if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); + memset(lencodes+n, fill, c); + n += c; + } + } + if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); + if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int stbi__parse_uncompressed_block(stbi__zbuf *a) +{ + stbi_uc header[4]; + int len,nlen,k; + if (a->num_bits & 7) + stbi__zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check + a->code_buffer >>= 8; + a->num_bits -= 8; + } + if (a->num_bits < 0) return stbi__err("zlib corrupt","Corrupt PNG"); + // now fill header the normal way + while (k < 4) + header[k++] = stbi__zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!stbi__zexpand(a, a->zout, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int stbi__parse_zlib_header(stbi__zbuf *a) +{ + int cmf = stbi__zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = stbi__zget8(a); + if (stbi__zeof(a)) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +static const stbi_uc stbi__zdefault_length[288] = +{ + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 +}; +static const stbi_uc stbi__zdefault_distance[32] = +{ + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +}; +/* +Init algorithm: +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; + for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; + for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; + for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; + + for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; +} +*/ + +static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!stbi__parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = stbi__zreceive(a,1); + type = stbi__zreceive(a,2); + if (type == 0) { + if (!stbi__parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; + } else { + if (!stbi__compute_huffman_codes(a)) return 0; + } + if (!stbi__parse_huffman_block(a)) return 0; + } + } while (!final); + return 1; +} + +static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return stbi__parse_zlib(a, parse_header); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer+len; + if (stbi__do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} +#endif + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + +#ifndef STBI_NO_PNG +typedef struct +{ + stbi__uint32 length; + stbi__uint32 type; +} stbi__pngchunk; + +static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) +{ + stbi__pngchunk c; + c.length = stbi__get32be(s); + c.type = stbi__get32be(s); + return c; +} + +static int stbi__check_png_header(stbi__context *s) +{ + static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi__context *s; + stbi_uc *idata, *expanded, *out; + int depth; +} stbi__png; + + +enum { + STBI__F_none=0, + STBI__F_sub=1, + STBI__F_up=2, + STBI__F_avg=3, + STBI__F_paeth=4, + // synthetic filters used for first scanline to avoid needing a dummy row of 0s + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static stbi_uc first_row_filter[5] = +{ + STBI__F_none, + STBI__F_sub, + STBI__F_none, + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static int stbi__paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; + +// create the png data from post-deflated data +static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) +{ + int bytes = (depth == 16? 2 : 1); + stbi__context *s = a->s; + stbi__uint32 i,j,stride = x*out_n*bytes; + stbi__uint32 img_len, img_width_bytes; + int k; + int img_n = s->img_n; // copy it into a local for later + + int output_bytes = out_n*bytes; + int filter_bytes = img_n*bytes; + int width = x; + + STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); + a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into + if (!a->out) return stbi__err("outofmem", "Out of memory"); + + if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); + img_width_bytes = (((img_n * x * depth) + 7) >> 3); + img_len = (img_width_bytes + 1) * y; + + // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, + // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), + // so just check for raw_len < img_len always. + if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *prior; + int filter = *raw++; + + if (filter > 4) + return stbi__err("invalid filter","Corrupt PNG"); + + if (depth < 8) { + if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG"); + cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place + filter_bytes = 1; + width = img_width_bytes; + } + prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above + + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + + // handle first byte explicitly + for (k=0; k < filter_bytes; ++k) { + switch (filter) { + case STBI__F_none : cur[k] = raw[k]; break; + case STBI__F_sub : cur[k] = raw[k]; break; + case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; + case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; + case STBI__F_avg_first : cur[k] = raw[k]; break; + case STBI__F_paeth_first: cur[k] = raw[k]; break; + } + } + + if (depth == 8) { + if (img_n != out_n) + cur[img_n] = 255; // first pixel + raw += img_n; + cur += out_n; + prior += out_n; + } else if (depth == 16) { + if (img_n != out_n) { + cur[filter_bytes] = 255; // first pixel top byte + cur[filter_bytes+1] = 255; // first pixel bottom byte + } + raw += filter_bytes; + cur += output_bytes; + prior += output_bytes; + } else { + raw += 1; + cur += 1; + prior += 1; + } + + // this is a little gross, so that we don't switch per-pixel or per-component + if (depth < 8 || img_n == out_n) { + int nk = (width - 1)*filter_bytes; + #define STBI__CASE(f) \ + case f: \ + for (k=0; k < nk; ++k) + switch (filter) { + // "none" filter turns into a memcpy here; make that explicit. + case STBI__F_none: memcpy(cur, raw, nk); break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; + } + #undef STBI__CASE + raw += nk; + } else { + STBI_ASSERT(img_n+1 == out_n); + #define STBI__CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ + for (k=0; k < filter_bytes; ++k) + switch (filter) { + STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; + } + #undef STBI__CASE + + // the loop above sets the high byte of the pixels' alpha, but for + // 16 bit png files we also need the low byte set. we'll do that here. + if (depth == 16) { + cur = a->out + stride*j; // start at the beginning of the row again + for (i=0; i < x; ++i,cur+=output_bytes) { + cur[filter_bytes+1] = 255; + } + } + } + } + + // we make a separate pass to expand bits to pixels; for performance, + // this could run two scanlines behind the above code, so it won't + // intefere with filtering but will still be in the cache. + if (depth < 8) { + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; + // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit + // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop + stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + + // note that the final byte might overshoot and write more data than desired. + // we can allocate enough data that this never writes out of memory, but it + // could also overwrite the next scanline. can it overwrite non-empty data + // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. + // so we need to explicitly clamp the final ones + + if (depth == 4) { + for (k=x*img_n; k >= 2; k-=2, ++in) { + *cur++ = scale * ((*in >> 4) ); + *cur++ = scale * ((*in ) & 0x0f); + } + if (k > 0) *cur++ = scale * ((*in >> 4) ); + } else if (depth == 2) { + for (k=x*img_n; k >= 4; k-=4, ++in) { + *cur++ = scale * ((*in >> 6) ); + *cur++ = scale * ((*in >> 4) & 0x03); + *cur++ = scale * ((*in >> 2) & 0x03); + *cur++ = scale * ((*in ) & 0x03); + } + if (k > 0) *cur++ = scale * ((*in >> 6) ); + if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); + if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); + } else if (depth == 1) { + for (k=x*img_n; k >= 8; k-=8, ++in) { + *cur++ = scale * ((*in >> 7) ); + *cur++ = scale * ((*in >> 6) & 0x01); + *cur++ = scale * ((*in >> 5) & 0x01); + *cur++ = scale * ((*in >> 4) & 0x01); + *cur++ = scale * ((*in >> 3) & 0x01); + *cur++ = scale * ((*in >> 2) & 0x01); + *cur++ = scale * ((*in >> 1) & 0x01); + *cur++ = scale * ((*in ) & 0x01); + } + if (k > 0) *cur++ = scale * ((*in >> 7) ); + if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); + if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); + if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); + if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); + if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); + if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); + } + if (img_n != out_n) { + int q; + // insert alpha = 255 + cur = a->out + stride*j; + if (img_n == 1) { + for (q=x-1; q >= 0; --q) { + cur[q*2+1] = 255; + cur[q*2+0] = cur[q]; + } + } else { + STBI_ASSERT(img_n == 3); + for (q=x-1; q >= 0; --q) { + cur[q*4+3] = 255; + cur[q*4+2] = cur[q*3+2]; + cur[q*4+1] = cur[q*3+1]; + cur[q*4+0] = cur[q*3+0]; + } + } + } + } + } else if (depth == 16) { + // force the image data from big-endian to platform-native. + // this is done in a separate pass due to the decoding relying + // on the data being untouched, but could probably be done + // per-line during decode if care is taken. + stbi_uc *cur = a->out; + stbi__uint16 *cur16 = (stbi__uint16*)cur; + + for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { + *cur16 = (cur[0] << 8) | cur[1]; + } + } + + return 1; +} + +static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) +{ + int bytes = (depth == 16 ? 2 : 1); + int out_bytes = out_n * bytes; + stbi_uc *final; + int p; + if (!interlaced) + return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); + + // de-interlacing + final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; + if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { + STBI_FREE(final); + return 0; + } + for (j=0; j < y; ++j) { + for (i=0; i < x; ++i) { + int out_y = j*yspc[p]+yorig[p]; + int out_x = i*xspc[p]+xorig[p]; + memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, + a->out + (j*x+i)*out_bytes, out_bytes); + } + } + STBI_FREE(a->out); + image_data += img_len; + image_data_len -= img_len; + } + } + a->out = final; + + return 1; +} + +static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi__uint16 *p = (stbi__uint16*) z->out; + + // compute color-based transparency, assuming we've + // already got 65535 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i = 0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 65535); + p += 2; + } + } else { + for (i = 0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) +{ + stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; + stbi_uc *p, *temp_out, *orig = a->out; + + p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); + if (p == NULL) return stbi__err("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + STBI_FREE(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi__unpremultiply_on_load = 0; +static int stbi__de_iphone_flag = 0; + +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag = flag_true_if_should_convert; +} + +static void stbi__de_iphone(stbi__png *z) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + STBI_ASSERT(s->img_out_n == 4); + if (stbi__unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + stbi_uc a = p[3]; + stbi_uc t = p[0]; + if (a) { + stbi_uc half = a / 2; + p[0] = (p[2] * 255 + half) / a; + p[1] = (p[1] * 255 + half) / a; + p[2] = ( t * 255 + half) / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) + +static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) +{ + stbi_uc palette[1024], pal_img_n=0; + stbi_uc has_trans=0, tc[3]={0}; + stbi__uint16 tc16[3]; + stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, color=0, is_iphone=0; + stbi__context *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!stbi__check_png_header(s)) return 0; + + if (scan == STBI__SCAN_type) return 1; + + for (;;) { + stbi__pngchunk c = stbi__get_chunk_header(s); + switch (c.type) { + case STBI__PNG_TYPE('C','g','B','I'): + is_iphone = 1; + stbi__skip(s, c.length); + break; + case STBI__PNG_TYPE('I','H','D','R'): { + int comp,filter; + if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); + s->img_x = stbi__get32be(s); + s->img_y = stbi__get32be(s); + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); + color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); + comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); + filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); + interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + if (scan == STBI__SCAN_header) return 1; + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); + // if SCAN_header, have to scan to see if we have a tRNS + } + break; + } + + case STBI__PNG_TYPE('P','L','T','E'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = stbi__get8(s); + palette[i*4+1] = stbi__get8(s); + palette[i*4+2] = stbi__get8(s); + palette[i*4+3] = 255; + } + break; + } + + case STBI__PNG_TYPE('t','R','N','S'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = stbi__get8(s); + } else { + if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); + if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); + has_trans = 1; + if (z->depth == 16) { + for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is + } else { + for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger + } + } + break; + } + + case STBI__PNG_TYPE('I','D','A','T'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); + if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } + if ((int)(ioff + c.length) < (int)ioff) return 0; + if (ioff + c.length > idata_limit) { + stbi__uint32 idata_limit_old = idata_limit; + stbi_uc *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + STBI_NOTUSED(idata_limit_old); + p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); + z->idata = p; + } + if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case STBI__PNG_TYPE('I','E','N','D'): { + stbi__uint32 raw_len, bpl; + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (scan != STBI__SCAN_load) return 1; + if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); + // initial guess for decoded data size to avoid unnecessary reallocs + bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component + raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; + z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); + if (z->expanded == NULL) return 0; // zlib should set error + STBI_FREE(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; + if (has_trans) { + if (z->depth == 16) { + if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; + } else { + if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; + } + } + if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) + stbi__de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } else if (has_trans) { + // non-paletted image with tRNS -> source image has (constant) alpha + ++s->img_n; + } + STBI_FREE(z->expanded); z->expanded = NULL; + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + return 1; + } + + default: + // if critical, fail + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX PNG chunk not known"; + invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); + invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); + invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); + invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); + #endif + return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); + } + stbi__skip(s, c.length); + break; + } + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + } +} + +static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) +{ + void *result=NULL; + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { + if (p->depth <= 8) + ri->bits_per_channel = 8; + else if (p->depth == 16) + ri->bits_per_channel = 16; + else + return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth"); + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + if (ri->bits_per_channel == 8) + result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + else + result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_n; + } + STBI_FREE(p->out); p->out = NULL; + STBI_FREE(p->expanded); p->expanded = NULL; + STBI_FREE(p->idata); p->idata = NULL; + + return result; +} + +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi__png p; + p.s = s; + return stbi__do_png(&p, x,y,comp,req_comp, ri); +} + +static int stbi__png_test(stbi__context *s) +{ + int r; + r = stbi__check_png_header(s); + stbi__rewind(s); + return r; +} + +static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) +{ + if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { + stbi__rewind( p->s ); + return 0; + } + if (x) *x = p->s->img_x; + if (y) *y = p->s->img_y; + if (comp) *comp = p->s->img_n; + return 1; +} + +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__png p; + p.s = s; + return stbi__png_info_raw(&p, x, y, comp); +} + +static int stbi__png_is16(stbi__context *s) +{ + stbi__png p; + p.s = s; + if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) + return 0; + if (p.depth != 16) { + stbi__rewind(p.s); + return 0; + } + return 1; +} +#endif + +// Microsoft/Windows BMP image + +#ifndef STBI_NO_BMP +static int stbi__bmp_test_raw(stbi__context *s) +{ + int r; + int sz; + if (stbi__get8(s) != 'B') return 0; + if (stbi__get8(s) != 'M') return 0; + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + stbi__get32le(s); // discard data offset + sz = stbi__get32le(s); + r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); + return r; +} + +static int stbi__bmp_test(stbi__context *s) +{ + int r = stbi__bmp_test_raw(s); + stbi__rewind(s); + return r; +} + + +// returns 0..31 for the highest set bit +static int stbi__high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) { n += 16; z >>= 16; } + if (z >= 0x00100) { n += 8; z >>= 8; } + if (z >= 0x00010) { n += 4; z >>= 4; } + if (z >= 0x00004) { n += 2; z >>= 2; } + if (z >= 0x00002) { n += 1;/* >>= 1;*/ } + return n; +} + +static int stbi__bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +// extract an arbitrarily-aligned N-bit value (N=bits) +// from v, and then make it 8-bits long and fractionally +// extend it to full full range. +static int stbi__shiftsigned(unsigned int v, int shift, int bits) +{ + static unsigned int mul_table[9] = { + 0, + 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, + 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, + }; + static unsigned int shift_table[9] = { + 0, 0,0,1,0,2,4,6,0, + }; + if (shift < 0) + v <<= -shift; + else + v >>= shift; + STBI_ASSERT(v < 256); + v >>= (8-bits); + STBI_ASSERT(bits >= 0 && bits <= 8); + return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; +} + +typedef struct +{ + int bpp, offset, hsz; + unsigned int mr,mg,mb,ma, all_a; + int extra_read; +} stbi__bmp_data; + +static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) +{ + int hsz; + if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + info->offset = stbi__get32le(s); + info->hsz = hsz = stbi__get32le(s); + info->mr = info->mg = info->mb = info->ma = 0; + info->extra_read = 14; + + if (info->offset < 0) return stbi__errpuc("bad BMP", "bad BMP"); + + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = stbi__get16le(s); + s->img_y = stbi__get16le(s); + } else { + s->img_x = stbi__get32le(s); + s->img_y = stbi__get32le(s); + } + if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); + info->bpp = stbi__get16le(s); + if (hsz != 12) { + int compress = stbi__get32le(s); + if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); + stbi__get32le(s); // discard sizeof + stbi__get32le(s); // discard hres + stbi__get32le(s); // discard vres + stbi__get32le(s); // discard colorsused + stbi__get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + } + if (info->bpp == 16 || info->bpp == 32) { + if (compress == 0) { + if (info->bpp == 32) { + info->mr = 0xffu << 16; + info->mg = 0xffu << 8; + info->mb = 0xffu << 0; + info->ma = 0xffu << 24; + info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 + } else { + info->mr = 31u << 10; + info->mg = 31u << 5; + info->mb = 31u << 0; + } + } else if (compress == 3) { + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->extra_read += 12; + // not documented, but generated by photoshop and handled by mspaint + if (info->mr == info->mg && info->mg == info->mb) { + // ?!?!? + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else { + int i; + if (hsz != 108 && hsz != 124) + return stbi__errpuc("bad BMP", "bad BMP"); + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->ma = stbi__get32le(s); + stbi__get32le(s); // discard color space + for (i=0; i < 12; ++i) + stbi__get32le(s); // discard color space parameters + if (hsz == 124) { + stbi__get32le(s); // discard rendering intent + stbi__get32le(s); // discard offset of profile data + stbi__get32le(s); // discard size of profile data + stbi__get32le(s); // discard reserved + } + } + } + return (void *) 1; +} + + +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + unsigned int mr=0,mg=0,mb=0,ma=0, all_a; + stbi_uc pal[256][4]; + int psize=0,i,j,width; + int flip_vertically, pad, target; + stbi__bmp_data info; + STBI_NOTUSED(ri); + + info.all_a = 255; + if (stbi__bmp_parse_header(s, &info) == NULL) + return NULL; // error code already set + + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + mr = info.mr; + mg = info.mg; + mb = info.mb; + ma = info.ma; + all_a = info.all_a; + + if (info.hsz == 12) { + if (info.bpp < 24) + psize = (info.offset - info.extra_read - 24) / 3; + } else { + if (info.bpp < 16) + psize = (info.offset - info.extra_read - info.hsz) >> 2; + } + if (psize == 0) { + STBI_ASSERT(info.offset == s->callback_already_read + (int) (s->img_buffer - s->img_buffer_original)); + if (info.offset != s->callback_already_read + (int) (s->img_buffer - s->img_buffer_original)) { + return stbi__errpuc("bad offset", "Corrupt BMP"); + } + } + + if (info.bpp == 24 && ma == 0xff000000) + s->img_n = 3; + else + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + + // sanity-check size + if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "Corrupt BMP"); + + out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (info.bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + if (info.hsz != 12) stbi__get8(s); + pal[i][3] = 255; + } + stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + if (info.bpp == 1) width = (s->img_x + 7) >> 3; + else if (info.bpp == 4) width = (s->img_x + 1) >> 1; + else if (info.bpp == 8) width = s->img_x; + else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + if (info.bpp == 1) { + for (j=0; j < (int) s->img_y; ++j) { + int bit_offset = 7, v = stbi__get8(s); + for (i=0; i < (int) s->img_x; ++i) { + int color = (v>>bit_offset)&0x1; + out[z++] = pal[color][0]; + out[z++] = pal[color][1]; + out[z++] = pal[color][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + if((--bit_offset) < 0) { + bit_offset = 7; + v = stbi__get8(s); + } + } + stbi__skip(s, pad); + } + } else { + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=stbi__get8(s),v2=0; + if (info.bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (info.bpp == 8) ? stbi__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + stbi__skip(s, pad); + } + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + stbi__skip(s, info.offset - info.extra_read - info.hsz); + if (info.bpp == 24) width = 3 * s->img_x; + else if (info.bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (info.bpp == 24) { + easy = 1; + } else if (info.bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); + gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); + bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); + ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); + if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + unsigned char a; + out[z+2] = stbi__get8(s); + out[z+1] = stbi__get8(s); + out[z+0] = stbi__get8(s); + z += 3; + a = (easy == 2 ? stbi__get8(s) : 255); + all_a |= a; + if (target == 4) out[z++] = a; + } + } else { + int bpp = info.bpp; + for (i=0; i < (int) s->img_x; ++i) { + stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); + unsigned int a; + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); + a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); + all_a |= a; + if (target == 4) out[z++] = STBI__BYTECAST(a); + } + } + stbi__skip(s, pad); + } + } + + // if alpha channel is all 0s, replace with all 255s + if (target == 4 && all_a == 0) + for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) + out[i] = 255; + + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i]; p1[i] = p2[i]; p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} +#endif + +// Targa Truevision - TGA +// by Jonathan Dummer +#ifndef STBI_NO_TGA +// returns STBI_rgb or whatever, 0 on error +static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) +{ + // only RGB or RGBA (incl. 16bit) or grey allowed + if (is_rgb16) *is_rgb16 = 0; + switch(bits_per_pixel) { + case 8: return STBI_grey; + case 16: if(is_grey) return STBI_grey_alpha; + // fallthrough + case 15: if(is_rgb16) *is_rgb16 = 1; + return STBI_rgb; + case 24: // fallthrough + case 32: return bits_per_pixel/8; + default: return 0; + } +} + +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; + int sz, tga_colormap_type; + stbi__get8(s); // discard Offset + tga_colormap_type = stbi__get8(s); // colormap type + if( tga_colormap_type > 1 ) { + stbi__rewind(s); + return 0; // only RGB or indexed allowed + } + tga_image_type = stbi__get8(s); // image type + if ( tga_colormap_type == 1 ) { // colormapped (paletted) image + if (tga_image_type != 1 && tga_image_type != 9) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip image x and y origin + tga_colormap_bpp = sz; + } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE + if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { + stbi__rewind(s); + return 0; // only RGB or grey allowed, +/- RLE + } + stbi__skip(s,9); // skip colormap specification and image x/y origin + tga_colormap_bpp = 0; + } + tga_w = stbi__get16le(s); + if( tga_w < 1 ) { + stbi__rewind(s); + return 0; // test width + } + tga_h = stbi__get16le(s); + if( tga_h < 1 ) { + stbi__rewind(s); + return 0; // test height + } + tga_bits_per_pixel = stbi__get8(s); // bits per pixel + stbi__get8(s); // ignore alpha bits + if (tga_colormap_bpp != 0) { + if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { + // when using a colormap, tga_bits_per_pixel is the size of the indexes + // I don't think anything but 8 or 16bit indexes makes sense + stbi__rewind(s); + return 0; + } + tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); + } else { + tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); + } + if(!tga_comp) { + stbi__rewind(s); + return 0; + } + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp; + return 1; // seems to have passed everything +} + +static int stbi__tga_test(stbi__context *s) +{ + int res = 0; + int sz, tga_color_type; + stbi__get8(s); // discard Offset + tga_color_type = stbi__get8(s); // color type + if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed + sz = stbi__get8(s); // image type + if ( tga_color_type == 1 ) { // colormapped (paletted) image + if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + stbi__skip(s,4); // skip image x and y origin + } else { // "normal" image w/o colormap + if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE + stbi__skip(s,9); // skip colormap specification and image x/y origin + } + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height + sz = stbi__get8(s); // bits per pixel + if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + + res = 1; // if we got this far, everything's good and we can return 1 instead of 0 + +errorEnd: + stbi__rewind(s); + return res; +} + +// read 16bit value and convert to 24bit RGB +static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) +{ + stbi__uint16 px = (stbi__uint16)stbi__get16le(s); + stbi__uint16 fiveBitMask = 31; + // we have 3 channels with 5bits each + int r = (px >> 10) & fiveBitMask; + int g = (px >> 5) & fiveBitMask; + int b = px & fiveBitMask; + // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later + out[0] = (stbi_uc)((r * 255)/31); + out[1] = (stbi_uc)((g * 255)/31); + out[2] = (stbi_uc)((b * 255)/31); + + // some people claim that the most significant bit might be used for alpha + // (possibly if an alpha-bit is set in the "image descriptor byte") + // but that only made 16bit test images completely translucent.. + // so let's treat all 15 and 16bit TGAs as RGB with no alpha. +} + +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + // read in the TGA header stuff + int tga_offset = stbi__get8(s); + int tga_indexed = stbi__get8(s); + int tga_image_type = stbi__get8(s); + int tga_is_RLE = 0; + int tga_palette_start = stbi__get16le(s); + int tga_palette_len = stbi__get16le(s); + int tga_palette_bits = stbi__get8(s); + int tga_x_origin = stbi__get16le(s); + int tga_y_origin = stbi__get16le(s); + int tga_width = stbi__get16le(s); + int tga_height = stbi__get16le(s); + int tga_bits_per_pixel = stbi__get8(s); + int tga_comp, tga_rgb16=0; + int tga_inverted = stbi__get8(s); + // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4] = {0}; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + STBI_NOTUSED(ri); + STBI_NOTUSED(tga_x_origin); // @TODO + STBI_NOTUSED(tga_y_origin); // @TODO + + if (tga_height > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (tga_width > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); + else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); + + if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency + return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); + + // tga info + *x = tga_width; + *y = tga_height; + if (comp) *comp = tga_comp; + + if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) + return stbi__errpuc("too large", "Corrupt TGA"); + + tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); + if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); + + // skip to the data's starting position (offset usually = 0) + stbi__skip(s, tga_offset ); + + if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { + for (i=0; i < tga_height; ++i) { + int row = tga_inverted ? tga_height -i - 1 : i; + stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; + stbi__getn(s, tga_row, tga_width * tga_comp); + } + } else { + // do I need to load a palette? + if ( tga_indexed) + { + if (tga_palette_len == 0) { /* you have to have at least one entry! */ + STBI_FREE(tga_data); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + + // any data to skip? (offset usually = 0) + stbi__skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); + if (!tga_palette) { + STBI_FREE(tga_data); + return stbi__errpuc("outofmem", "Out of memory"); + } + if (tga_rgb16) { + stbi_uc *pal_entry = tga_palette; + STBI_ASSERT(tga_comp == STBI_rgb); + for (i=0; i < tga_palette_len; ++i) { + stbi__tga_read_rgb16(s, pal_entry); + pal_entry += tga_comp; + } + } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { + STBI_FREE(tga_data); + STBI_FREE(tga_palette); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + } + // load the data + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = stbi__get8(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in index, then perform the lookup + int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); + if ( pal_idx >= tga_palette_len ) { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_comp; + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else if(tga_rgb16) { + STBI_ASSERT(tga_comp == STBI_rgb); + stbi__tga_read_rgb16(s, raw_data); + } else { + // read in the data raw + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = stbi__get8(s); + } + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + + // copy data + for (j = 0; j < tga_comp; ++j) + tga_data[i*tga_comp+j] = raw_data[j]; + + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * tga_comp; + int index2 = (tga_height - 1 - j) * tga_width * tga_comp; + for (i = tga_width * tga_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + STBI_FREE( tga_palette ); + } + } + + // swap RGB - if the source data was RGB16, it already is in the right order + if (tga_comp >= 3 && !tga_rgb16) + { + unsigned char* tga_pixel = tga_data; + for (i=0; i < tga_width * tga_height; ++i) + { + unsigned char temp = tga_pixel[0]; + tga_pixel[0] = tga_pixel[2]; + tga_pixel[2] = temp; + tga_pixel += tga_comp; + } + } + + // convert to target component count + if (req_comp && req_comp != tga_comp) + tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); + + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + STBI_NOTUSED(tga_palette_start); + // OK, done + return tga_data; +} +#endif + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s) +{ + int r = (stbi__get32be(s) == 0x38425053); + stbi__rewind(s); + return r; +} + +static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) +{ + int count, nleft, len; + + count = 0; + while ((nleft = pixelCount - count) > 0) { + len = stbi__get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + if (len > nleft) return 0; // corrupt data + count += len; + while (len) { + *p = stbi__get8(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi_uc val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len = 257 - len; + if (len > nleft) return 0; // corrupt data + val = stbi__get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + + return 1; +} + +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + int pixelCount; + int channelCount, compression; + int channel, i; + int bitdepth; + int w,h; + stbi_uc *out; + STBI_NOTUSED(ri); + + // Check identifier + if (stbi__get32be(s) != 0x38425053) // "8BPS" + return stbi__errpuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (stbi__get16be(s) != 1) + return stbi__errpuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + stbi__skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) + return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = stbi__get32be(s); + w = stbi__get32be(s); + + if (h > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (w > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + // Make sure the depth is 8 bits. + bitdepth = stbi__get16be(s); + if (bitdepth != 8 && bitdepth != 16) + return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (stbi__get16be(s) != 3) + return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + stbi__skip(s,stbi__get32be(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + stbi__skip(s, stbi__get32be(s) ); + + // Skip the reserved data. + stbi__skip(s, stbi__get32be(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = stbi__get16be(s); + if (compression > 1) + return stbi__errpuc("bad compression", "PSD has an unknown compression format"); + + // Check size + if (!stbi__mad3sizes_valid(4, w, h, 0)) + return stbi__errpuc("too large", "Corrupt PSD"); + + // Create the destination image. + + if (!compression && bitdepth == 16 && bpc == 16) { + out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); + ri->bits_per_channel = 16; + } else + out = (stbi_uc *) stbi__malloc(4 * w*h); + + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, + // which we're going to just skip. + stbi__skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++, p += 4) + *p = (channel == 3 ? 255 : 0); + } else { + // Read the RLE data. + if (!stbi__psd_decode_rle(s, p, pixelCount)) { + STBI_FREE(out); + return stbi__errpuc("corrupt", "bad RLE data"); + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + if (channel >= channelCount) { + // Fill this channel with default data. + if (bitdepth == 16 && bpc == 16) { + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + stbi__uint16 val = channel == 3 ? 65535 : 0; + for (i = 0; i < pixelCount; i++, q += 4) + *q = val; + } else { + stbi_uc *p = out+channel; + stbi_uc val = channel == 3 ? 255 : 0; + for (i = 0; i < pixelCount; i++, p += 4) + *p = val; + } + } else { + if (ri->bits_per_channel == 16) { // output bpc + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + for (i = 0; i < pixelCount; i++, q += 4) + *q = (stbi__uint16) stbi__get16be(s); + } else { + stbi_uc *p = out+channel; + if (bitdepth == 16) { // input bpc + for (i = 0; i < pixelCount; i++, p += 4) + *p = (stbi_uc) (stbi__get16be(s) >> 8); + } else { + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); + } + } + } + } + } + + // remove weird white matte from PSD + if (channelCount >= 4) { + if (ri->bits_per_channel == 16) { + for (i=0; i < w*h; ++i) { + stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; + if (pixel[3] != 0 && pixel[3] != 65535) { + float a = pixel[3] / 65535.0f; + float ra = 1.0f / a; + float inv_a = 65535.0f * (1 - ra); + pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); + pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); + pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); + } + } + } else { + for (i=0; i < w*h; ++i) { + unsigned char *pixel = out + 4*i; + if (pixel[3] != 0 && pixel[3] != 255) { + float a = pixel[3] / 255.0f; + float ra = 1.0f / a; + float inv_a = 255.0f * (1 - ra); + pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); + pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); + pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); + } + } + } + } + + // convert to desired output format + if (req_comp && req_comp != 4) { + if (ri->bits_per_channel == 16) + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); + else + out = stbi__convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + if (comp) *comp = 4; + *y = h; + *x = w; + + return out; +} +#endif + +// ************************************************************************************************* +// Softimage PIC loader +// by Tom Seddon +// +// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format +// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ + +#ifndef STBI_NO_PIC +static int stbi__pic_is4(stbi__context *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (stbi__get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int stbi__pic_test_core(stbi__context *s) +{ + int i; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + stbi__get8(s); + + if (!stbi__pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} stbi__pic_packet; + +static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); + dest[i]=stbi__get8(s); + } + } + + return dest; +} + +static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) +{ + int mask=0x80,i; + + for (i=0;i<4; ++i, mask>>=1) + if (channel&mask) + dest[i]=src[i]; +} + +static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + stbi__pic_packet packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return stbi__errpuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + + act_comp |= packet->channel; + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; ytype) { + default: + return stbi__errpuc("bad format","packet has bad compression type"); + + case 0: {//uncompressed + int x; + + for(x=0;xchannel,dest)) + return 0; + break; + } + + case 1://Pure RLE + { + int left=width, i; + + while (left>0) { + stbi_uc count,value[4]; + + count=stbi__get8(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (stbi_uc) left; + + if (!stbi__readval(s,packet->channel,value)) return 0; + + for(i=0; ichannel,dest,value); + left -= count; + } + } + break; + + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = stbi__get8(s), i; + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + + if (count==128) + count = stbi__get16be(s); + else + count -= 127; + if (count > left) + return stbi__errpuc("bad file","scanline overrun"); + + if (!stbi__readval(s,packet->channel,value)) + return 0; + + for(i=0;ichannel,dest,value); + } else { // Raw + ++count; + if (count>left) return stbi__errpuc("bad file","scanline overrun"); + + for(i=0;ichannel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) +{ + stbi_uc *result; + int i, x,y, internal_comp; + STBI_NOTUSED(ri); + + if (!comp) comp = &internal_comp; + + for (i=0; i<92; ++i) + stbi__get8(s); + + x = stbi__get16be(s); + y = stbi__get16be(s); + + if (y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); + if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); + + stbi__get32be(s); //skip `ratio' + stbi__get16be(s); //skip `fields' + stbi__get16be(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); + memset(result, 0xff, x*y*4); + + if (!stbi__pic_load_core(s,x,y,comp, result)) { + STBI_FREE(result); + result=0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=stbi__convert_format(result,4,req_comp,x,y); + + return result; +} + +static int stbi__pic_test(stbi__context *s) +{ + int r = stbi__pic_test_core(s); + stbi__rewind(s); + return r; +} +#endif + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb + +#ifndef STBI_NO_GIF +typedef struct +{ + stbi__int16 prefix; + stbi_uc first; + stbi_uc suffix; +} stbi__gif_lzw; + +typedef struct +{ + int w,h; + stbi_uc *out; // output buffer (always 4 components) + stbi_uc *background; // The current "background" as far as a gif is concerned + stbi_uc *history; + int flags, bgindex, ratio, transparent, eflags; + stbi_uc pal[256][4]; + stbi_uc lpal[256][4]; + stbi__gif_lzw codes[8192]; + stbi_uc *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; + int delay; +} stbi__gif; + +static int stbi__gif_test_raw(stbi__context *s) +{ + int sz; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; + sz = stbi__get8(s); + if (sz != '9' && sz != '7') return 0; + if (stbi__get8(s) != 'a') return 0; + return 1; +} + +static int stbi__gif_test(stbi__context *s) +{ + int r = stbi__gif_test_raw(s); + stbi__rewind(s); + return r; +} + +static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + pal[i][3] = transp == i ? 0 : 255; + } +} + +static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) +{ + stbi_uc version; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') + return stbi__err("not GIF", "Corrupt GIF"); + + version = stbi__get8(s); + if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); + if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); + + stbi__g_failure_reason = ""; + g->w = stbi__get16le(s); + g->h = stbi__get16le(s); + g->flags = stbi__get8(s); + g->bgindex = stbi__get8(s); + g->ratio = stbi__get8(s); + g->transparent = -1; + + if (g->w > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (g->h > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + + if (is_info) return 1; + + if (g->flags & 0x80) + stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + + return 1; +} + +static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); + if (!stbi__gif_header(s, g, comp, 1)) { + STBI_FREE(g); + stbi__rewind( s ); + return 0; + } + if (x) *x = g->w; + if (y) *y = g->h; + STBI_FREE(g); + return 1; +} + +static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) +{ + stbi_uc *p, *c; + int idx; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi__out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + idx = g->cur_x + g->cur_y; + p = &g->out[idx]; + g->history[idx / 4] = 1; + + c = &g->color_table[g->codes[code].suffix * 4]; + if (c[3] > 128) { // don't render transparent pixels; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; + + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; + + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } +} + +static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) +{ + stbi_uc lzw_cs; + stbi__int32 len, init_code; + stbi__uint32 first; + stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi__gif_lzw *p; + + lzw_cs = stbi__get8(s); + if (lzw_cs > 12) return NULL; + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (init_code = 0; init_code < clear; init_code++) { + g->codes[init_code].prefix = -1; + g->codes[init_code].first = (stbi_uc) init_code; + g->codes[init_code].suffix = (stbi_uc) init_code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = stbi__get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (stbi__int32) stbi__get8(s) << valid_bits; + valid_bits += 8; + } else { + stbi__int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + stbi__skip(s, len); + while ((len = stbi__get8(s)) > 0) + stbi__skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) { + return stbi__errpuc("no clear code", "Corrupt GIF"); + } + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 8192) { + return stbi__errpuc("too many codes", "Corrupt GIF"); + } + + p->prefix = (stbi__int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + + stbi__out_gif_code(g, (stbi__uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +// two back is the image from two frames ago, used for a very specific disposal format +static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) +{ + int dispose; + int first_frame; + int pi; + int pcount; + STBI_NOTUSED(req_comp); + + // on first frame, any non-written pixels get the background colour (non-transparent) + first_frame = 0; + if (g->out == 0) { + if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header + if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) + return stbi__errpuc("too large", "GIF image is too large"); + pcount = g->w * g->h; + g->out = (stbi_uc *) stbi__malloc(4 * pcount); + g->background = (stbi_uc *) stbi__malloc(4 * pcount); + g->history = (stbi_uc *) stbi__malloc(pcount); + if (!g->out || !g->background || !g->history) + return stbi__errpuc("outofmem", "Out of memory"); + + // image is treated as "transparent" at the start - ie, nothing overwrites the current background; + // background colour is only used for pixels that are not rendered first frame, after that "background" + // color refers to the color that was there the previous frame. + memset(g->out, 0x00, 4 * pcount); + memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) + memset(g->history, 0x00, pcount); // pixels that were affected previous frame + first_frame = 1; + } else { + // second frame - how do we dispose of the previous one? + dispose = (g->eflags & 0x1C) >> 2; + pcount = g->w * g->h; + + if ((dispose == 3) && (two_back == 0)) { + dispose = 2; // if I don't have an image to revert back to, default to the old background + } + + if (dispose == 3) { // use previous graphic + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); + } + } + } else if (dispose == 2) { + // restore what was changed last frame to background before that frame; + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi]) { + memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); + } + } + } else { + // This is a non-disposal case eithe way, so just + // leave the pixels as is, and they will become the new background + // 1: do not dispose + // 0: not specified. + } + + // background is what out is after the undoing of the previou frame; + memcpy( g->background, g->out, 4 * g->w * g->h ); + } + + // clear my history; + memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame + + for (;;) { + int tag = stbi__get8(s); + switch (tag) { + case 0x2C: /* Image Descriptor */ + { + stbi__int32 x, y, w, h; + stbi_uc *o; + + x = stbi__get16le(s); + y = stbi__get16le(s); + w = stbi__get16le(s); + h = stbi__get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + // if the width of the specified rectangle is 0, that means + // we may not see *any* pixels or the image is malformed; + // to make sure this is caught, move the current y down to + // max_y (which is what out_gif_code checks). + if (w == 0) + g->cur_y = g->max_y; + + g->lflags = stbi__get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (stbi_uc *) g->lpal; + } else if (g->flags & 0x80) { + g->color_table = (stbi_uc *) g->pal; + } else + return stbi__errpuc("missing color table", "Corrupt GIF"); + + o = stbi__process_gif_raster(s, g); + if (!o) return NULL; + + // if this was the first frame, + pcount = g->w * g->h; + if (first_frame && (g->bgindex > 0)) { + // if first frame, any pixel not drawn to gets the background color + for (pi = 0; pi < pcount; ++pi) { + if (g->history[pi] == 0) { + g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; + memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); + } + } + } + + return o; + } + + case 0x21: // Comment Extension. + { + int len; + int ext = stbi__get8(s); + if (ext == 0xF9) { // Graphic Control Extension. + len = stbi__get8(s); + if (len == 4) { + g->eflags = stbi__get8(s); + g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. + + // unset old transparent + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 255; + } + if (g->eflags & 0x01) { + g->transparent = stbi__get8(s); + if (g->transparent >= 0) { + g->pal[g->transparent][3] = 0; + } + } else { + // don't need transparent + stbi__skip(s, 1); + g->transparent = -1; + } + } else { + stbi__skip(s, len); + break; + } + } + while ((len = stbi__get8(s)) != 0) { + stbi__skip(s, len); + } + break; + } + + case 0x3B: // gif stream termination code + return (stbi_uc *) s; // using '1' causes warning on some compilers + + default: + return stbi__errpuc("unknown code", "Corrupt GIF"); + } + } +} + +static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) +{ + if (stbi__gif_test(s)) { + int layers = 0; + stbi_uc *u = 0; + stbi_uc *out = 0; + stbi_uc *two_back = 0; + stbi__gif g; + int stride; + int out_size = 0; + int delays_size = 0; + memset(&g, 0, sizeof(g)); + if (delays) { + *delays = 0; + } + + do { + u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + + if (u) { + *x = g.w; + *y = g.h; + ++layers; + stride = g.w * g.h * 4; + + if (out) { + void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride ); + if (NULL == tmp) { + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); + return stbi__errpuc("outofmem", "Out of memory"); + } + else { + out = (stbi_uc*) tmp; + out_size = layers * stride; + } + + if (delays) { + *delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); + delays_size = layers * sizeof(int); + } + } else { + out = (stbi_uc*)stbi__malloc( layers * stride ); + out_size = layers * stride; + if (delays) { + *delays = (int*) stbi__malloc( layers * sizeof(int) ); + delays_size = layers * sizeof(int); + } + } + memcpy( out + ((layers - 1) * stride), u, stride ); + if (layers >= 2) { + two_back = out - 2 * stride; + } + + if (delays) { + (*delays)[layers - 1U] = g.delay; + } + } + } while (u != 0); + + // free temp buffer; + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); + + // do the final conversion after loading everything; + if (req_comp && req_comp != 4) + out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); + + *z = layers; + return out; + } else { + return stbi__errpuc("not GIF", "Image was not as a gif type."); + } +} + +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *u = 0; + stbi__gif g; + memset(&g, 0, sizeof(g)); + STBI_NOTUSED(ri); + + u = stbi__gif_load_next(s, &g, comp, req_comp, 0); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + if (u) { + *x = g.w; + *y = g.h; + + // moved conversion to after successful load so that the same + // can be done for multiple frames. + if (req_comp && req_comp != 4) + u = stbi__convert_format(u, 4, req_comp, g.w, g.h); + } else if (g.out) { + // if there was an error and we allocated an image buffer, free it! + STBI_FREE(g.out); + } + + // free buffers needed for multiple frame loading; + STBI_FREE(g.history); + STBI_FREE(g.background); + + return u; +} + +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) +{ + return stbi__gif_info_raw(s,x,y,comp); +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int stbi__hdr_test_core(stbi__context *s, const char *signature) +{ + int i; + for (i=0; signature[i]; ++i) + if (stbi__get8(s) != signature[i]) + return 0; + stbi__rewind(s); + return 1; +} + +static int stbi__hdr_test(stbi__context* s) +{ + int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); + stbi__rewind(s); + if(!r) { + r = stbi__hdr_test_core(s, "#?RGBE\n"); + stbi__rewind(s); + } + return r; +} + +#define STBI__HDR_BUFLEN 1024 +static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) stbi__get8(z); + + while (!stbi__at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == STBI__HDR_BUFLEN-1) { + // flush to end of line + while (!stbi__at_eof(z) && stbi__get8(z) != '\n') + ; + break; + } + c = (char) stbi__get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if ( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + const char *headerToken; + STBI_NOTUSED(ri); + + // Check identifier + headerToken = stbi__hdr_gettoken(s,buffer); + if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) + return stbi__errpf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = (int) strtol(token, NULL, 10); + + if (height > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + if (width > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + + *x = width; + *y = height; + + if (comp) *comp = 3; + if (req_comp == 0) req_comp = 3; + + if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) + return stbi__errpf("too large", "HDR image is too large"); + + // Read data + hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); + if (!hdr_data) + return stbi__errpf("outofmem", "Out of memory"); + + // Load image data + // image data is stored as some number of sca + if ( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + stbi__getn(s, rgbe, 4); + stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = stbi__get8(s); + c2 = stbi__get8(s); + len = stbi__get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + stbi_uc rgbe[4]; + rgbe[0] = (stbi_uc) c1; + rgbe[1] = (stbi_uc) c2; + rgbe[2] = (stbi_uc) len; + rgbe[3] = (stbi_uc) stbi__get8(s); + stbi__hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + STBI_FREE(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= stbi__get8(s); + if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) { + scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); + if (!scanline) { + STBI_FREE(hdr_data); + return stbi__errpf("outofmem", "Out of memory"); + } + } + + for (k = 0; k < 4; ++k) { + int nleft; + i = 0; + while ((nleft = width - i) > 0) { + count = stbi__get8(s); + if (count > 128) { + // Run + value = stbi__get8(s); + count -= 128; + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = stbi__get8(s); + } + } + } + for (i=0; i < width; ++i) + stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + if (scanline) + STBI_FREE(scanline); + } + + return hdr_data; +} + +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int dummy; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (stbi__hdr_test(s) == 0) { + stbi__rewind( s ); + return 0; + } + + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) { + stbi__rewind( s ); + return 0; + } + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *y = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *x = (int) strtol(token, NULL, 10); + *comp = 3; + return 1; +} +#endif // STBI_NO_HDR + +#ifndef STBI_NO_BMP +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) +{ + void *p; + stbi__bmp_data info; + + info.all_a = 255; + p = stbi__bmp_parse_header(s, &info); + stbi__rewind( s ); + if (p == NULL) + return 0; + if (x) *x = s->img_x; + if (y) *y = s->img_y; + if (comp) { + if (info.bpp == 24 && info.ma == 0xff000000) + *comp = 3; + else + *comp = info.ma ? 4 : 3; + } + return 1; +} +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) +{ + int channelCount, dummy, depth; + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + *y = stbi__get32be(s); + *x = stbi__get32be(s); + depth = stbi__get16be(s); + if (depth != 8 && depth != 16) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 3) { + stbi__rewind( s ); + return 0; + } + *comp = 4; + return 1; +} + +static int stbi__psd_is16(stbi__context *s) +{ + int channelCount, depth; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + (void) stbi__get32be(s); + (void) stbi__get32be(s); + depth = stbi__get16be(s); + if (depth != 16) { + stbi__rewind( s ); + return 0; + } + return 1; +} +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) +{ + int act_comp=0,num_packets=0,chained,dummy; + stbi__pic_packet packets[10]; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { + stbi__rewind(s); + return 0; + } + + stbi__skip(s, 88); + + *x = stbi__get16be(s); + *y = stbi__get16be(s); + if (stbi__at_eof(s)) { + stbi__rewind( s); + return 0; + } + if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi__rewind( s ); + return 0; + } + + stbi__skip(s, 8); + + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; + + packet = &packets[num_packets++]; + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + act_comp |= packet->channel; + + if (stbi__at_eof(s)) { + stbi__rewind( s ); + return 0; + } + if (packet->size != 8) { + stbi__rewind( s ); + return 0; + } + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); + + return 1; +} +#endif + +// ************************************************************************************************* +// Portable Gray Map and Portable Pixel Map loader +// by Ken Miller +// +// PGM: http://netpbm.sourceforge.net/doc/pgm.html +// PPM: http://netpbm.sourceforge.net/doc/ppm.html +// +// Known limitations: +// Does not support comments in the header section +// Does not support ASCII image data (formats P2 and P3) +// Does not support 16-bit-per-channel + +#ifndef STBI_NO_PNM + +static int stbi__pnm_test(stbi__context *s) +{ + char p, t; + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind( s ); + return 0; + } + return 1; +} + +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + STBI_NOTUSED(ri); + + if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) + return 0; + + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + + if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "PNM too large"); + + out = (stbi_uc *) stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + stbi__getn(s, out, s->img_n * s->img_x * s->img_y); + + if (req_comp && req_comp != s->img_n) { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + return out; +} + +static int stbi__pnm_isspace(char c) +{ + return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; +} + +static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) +{ + for (;;) { + while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) + *c = (char) stbi__get8(s); + + if (stbi__at_eof(s) || *c != '#') + break; + + while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) + *c = (char) stbi__get8(s); + } +} + +static int stbi__pnm_isdigit(char c) +{ + return c >= '0' && c <= '9'; +} + +static int stbi__pnm_getinteger(stbi__context *s, char *c) +{ + int value = 0; + + while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { + value = value*10 + (*c - '0'); + *c = (char) stbi__get8(s); + } + + return value; +} + +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) +{ + int maxv, dummy; + char c, p, t; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + stbi__rewind(s); + + // Get identifier + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind(s); + return 0; + } + + *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm + + c = (char) stbi__get8(s); + stbi__pnm_skip_whitespace(s, &c); + + *x = stbi__pnm_getinteger(s, &c); // read width + stbi__pnm_skip_whitespace(s, &c); + + *y = stbi__pnm_getinteger(s, &c); // read height + stbi__pnm_skip_whitespace(s, &c); + + maxv = stbi__pnm_getinteger(s, &c); // read max value + + if (maxv > 255) + return stbi__err("max value > 255", "PPM image not 8-bit"); + else + return 1; +} +#endif + +static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) +{ + #ifndef STBI_NO_JPEG + if (stbi__jpeg_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNG + if (stbi__png_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_GIF + if (stbi__gif_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_BMP + if (stbi__bmp_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PIC + if (stbi__pic_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_info(s, x, y, comp)) return 1; + #endif + + // test tga last because it's a crappy test! + #ifndef STBI_NO_TGA + if (stbi__tga_info(s, x, y, comp)) + return 1; + #endif + return stbi__err("unknown image type", "Image not of any known type, or corrupt"); +} + +static int stbi__is_16_main(stbi__context *s) +{ + #ifndef STBI_NO_PNG + if (stbi__png_is16(s)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_is16(s)) return 1; + #endif + + return 0; +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__info_main(&s,x,y,comp); + fseek(f,pos,SEEK_SET); + return r; +} + +STBIDEF int stbi_is_16_bit(char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_is_16_bit_from_file(f); + fclose(f); + return result; +} + +STBIDEF int stbi_is_16_bit_from_file(FILE *f) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__is_16_main(&s); + fseek(f,pos,SEEK_SET); + return r; +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__is_16_main(&s); +} + +STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__is_16_main(&s); +} + +#endif // STB_IMAGE_IMPLEMENTATION + +/* + revision history: + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs + 2.19 (2018-02-11) fix warning + 2.18 (2018-01-30) fix warnings + 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug + 1-bit BMP + *_is_16_bit api + avoid warnings + 2.16 (2017-07-23) all functions have 16-bit variants; + STBI_NO_STDIO works again; + compilation fixes; + fix rounding in unpremultiply; + optimize vertical flip; + disable raw_len validation; + documentation fixes + 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; + warning fixes; disable run-time SSE detection on gcc; + uniform handling of optional "return" values; + thread-safe initialization of zlib tables + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) allocate large structures on the stack + remove white matting for transparent PSD + fix reported channel count for PNG & BMP + re-enable SSE2 in non-gcc 64-bit + support RGB-formatted JPEG + read 16-bit PNGs (only as 8-bit) + 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED + 2.09 (2016-01-16) allow comments in PNM files + 16-bit-per-pixel TGA (not bit-per-component) + info() for TGA could break due to .hdr handling + info() for BMP to shares code instead of sloppy parse + can use STBI_REALLOC_SIZED if allocator doesn't support realloc + code cleanup + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) fix compiler warnings + partial animated GIF support + limited 16-bpc PSD support + #ifdef unused functions + bug with < 92 byte PIC,PNM,HDR,TGA + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) extra corruption checking (mmozeiko) + stbi_set_flip_vertically_on_load (nguillemot) + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) + progressive JPEG (stb) + PGM/PPM support (Ken Miller) + STBI_MALLOC,STBI_REALLOC,STBI_FREE + GIF bugfix -- seemingly never worked + STBI_NO_*, STBI_ONLY_* + 1.48 (2014-12-14) fix incorrectly-named assert() + 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) + optimize PNG (ryg) + fix bug in interlaced PNG with user-specified channel count (stb) + 1.46 (2014-08-26) + fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG + 1.45 (2014-08-16) + fix MSVC-ARM internal compiler error by wrapping malloc + 1.44 (2014-08-07) + various warning fixes from Ronny Chevalier + 1.43 (2014-07-15) + fix MSVC-only compiler problem in code changed in 1.42 + 1.42 (2014-07-09) + don't define _CRT_SECURE_NO_WARNINGS (affects user code) + fixes to stbi__cleanup_jpeg path + added STBI_ASSERT to avoid requiring assert.h + 1.41 (2014-06-25) + fix search&replace from 1.36 that messed up comments/error messages + 1.40 (2014-06-22) + fix gcc struct-initialization warning + 1.39 (2014-06-15) + fix to TGA optimization when req_comp != number of components in TGA; + fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) + add support for BMP version 5 (more ignored fields) + 1.38 (2014-06-06) + suppress MSVC warnings on integer casts truncating values + fix accidental rename of 'skip' field of I/O + 1.37 (2014-06-04) + remove duplicate typedef + 1.36 (2014-06-03) + convert to header file single-file library + if de-iphone isn't set, load iphone images color-swapped instead of returning NULL + 1.35 (2014-05-27) + various warnings + fix broken STBI_SIMD path + fix bug where stbi_load_from_file no longer left file pointer in correct place + fix broken non-easy path for 32-bit BMP (possibly never used) + TGA optimization by Arseny Kapoulkine + 1.34 (unknown) + use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case + 1.33 (2011-07-14) + make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements + 1.32 (2011-07-13) + support for "info" function for all supported filetypes (SpartanJ) + 1.31 (2011-06-20) + a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) + added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) + removed deprecated format-specific test/load functions + removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway + error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) + fix inefficiency in decoding 32-bit BMP (David Woo) + 1.29 (2010-08-16) + various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) + fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-stbi_uc to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) + 1.21 fix use of 'stbi_uc' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 (2008-08-02) + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - stbi__convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant + 0.50 (2006-11-19) + first released version +*/ + + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/v_windows/v/old/thirdparty/stb_image/stbi.c b/v_windows/v/old/thirdparty/stb_image/stbi.c new file mode 100644 index 0000000..0375a5a --- /dev/null +++ b/v_windows/v/old/thirdparty/stb_image/stbi.c @@ -0,0 +1,3 @@ + +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" diff --git a/v_windows/v/old/thirdparty/stdatomic/nix/atomic.h b/v_windows/v/old/thirdparty/stdatomic/nix/atomic.h new file mode 100644 index 0000000..3bcb867 --- /dev/null +++ b/v_windows/v/old/thirdparty/stdatomic/nix/atomic.h @@ -0,0 +1,435 @@ +/* + Compability header for stdatomic.h that works for all compilers supported + by V. For TCC libatomic from the operating system is used + +*/ +#ifndef __ATOMIC_H +#define __ATOMIC_H + +#ifndef __cplusplus +// If C just use stdatomic.h +#ifndef __TINYC__ +#include +#endif +#else +// CPP wrapper for atomic operations that are compatible with C +#include "atomic_cpp.h" +#endif + +#ifdef __TINYC__ + +typedef volatile long long atomic_llong; +typedef volatile unsigned long long atomic_ullong; +typedef volatile uintptr_t atomic_uintptr_t; + +// use functions for 64, 32 and 8 bit from libatomic directly +// since tcc is not capible to use "generic" C functions +// there is no header file for libatomic so we provide function declarations here + +extern unsigned long long __atomic_load_8(unsigned long long* x, int mo); +extern void __atomic_store_8(unsigned long long* x, unsigned long long y, int mo); +extern _Bool __atomic_compare_exchange_8(unsigned long long* x, unsigned long long* expected, unsigned long long y, int mo, int mo2); +extern _Bool __atomic_compare_exchange_8(unsigned long long* x, unsigned long long* expected, unsigned long long y, int mo, int mo2); +extern unsigned long long __atomic_exchange_8(unsigned long long* x, unsigned long long y, int mo); +extern unsigned long long __atomic_fetch_add_8(unsigned long long* x, unsigned long long y, int mo); +extern unsigned long long __atomic_fetch_sub_8(unsigned long long* x, unsigned long long y, int mo); +extern unsigned long long __atomic_fetch_and_8(unsigned long long* x, unsigned long long y, int mo); +extern unsigned long long __atomic_fetch_or_8(unsigned long long* x, unsigned long long y, int mo); +extern unsigned long long __atomic_fetch_xor_8(unsigned long long* x, unsigned long long y, int mo); + +extern unsigned int __atomic_load_4(unsigned int* x, int mo); +extern void __atomic_store_4(unsigned int* x, unsigned int y, int mo); +extern _Bool __atomic_compare_exchange_4(unsigned int* x, unsigned int* expected, unsigned int y, int mo, int mo2); +extern _Bool __atomic_compare_exchange_4(unsigned int* x, unsigned int* expected, unsigned int y, int mo, int mo2); +extern unsigned int __atomic_exchange_4(unsigned int* x, unsigned int y, int mo); +extern unsigned int __atomic_fetch_add_4(unsigned int* x, unsigned int y, int mo); +extern unsigned int __atomic_fetch_sub_4(unsigned int* x, unsigned int y, int mo); +extern unsigned int __atomic_fetch_and_4(unsigned int* x, unsigned int y, int mo); +extern unsigned int __atomic_fetch_or_4(unsigned int* x, unsigned int y, int mo); +extern unsigned int __atomic_fetch_xor_4(unsigned int* x, unsigned int y, int mo); + +extern unsigned short __atomic_load_2(unsigned short* x, int mo); +extern void __atomic_store_2(unsigned short* x, unsigned short y, int mo); +extern _Bool __atomic_compare_exchange_2(unsigned short* x, unsigned short* expected, unsigned short y, int mo, int mo2); +extern _Bool __atomic_compare_exchange_2(unsigned short* x, unsigned short* expected, unsigned short y, int mo, int mo2); +extern unsigned short __atomic_exchange_2(unsigned short* x, unsigned short y, int mo); +extern unsigned short __atomic_fetch_add_2(unsigned short* x, unsigned short y, int mo); +extern unsigned short __atomic_fetch_sub_2(unsigned short* x, unsigned short y, int mo); +extern unsigned short __atomic_fetch_and_2(unsigned short* x, unsigned short y, int mo); +extern unsigned short __atomic_fetch_or_2(unsigned short* x, unsigned short y, int mo); +extern unsigned short __atomic_fetch_xor_2(unsigned short* x, unsigned short y, int mo); + +extern unsigned char __atomic_load_1(unsigned char* x, int mo); +extern void __atomic_store_1(unsigned char* x, unsigned char y, int mo); +extern _Bool __atomic_compare_exchange_1(unsigned char* x, unsigned char* expected, unsigned char y, int mo, int mo2); +extern _Bool __atomic_compare_exchange_1(unsigned char* x, unsigned char* expected, unsigned char y, int mo, int mo2); +extern unsigned char __atomic_exchange_1(unsigned char* x, unsigned char y, int mo); +extern unsigned char __atomic_fetch_add_1(unsigned char* x, unsigned char y, int mo); +extern unsigned char __atomic_fetch_sub_1(unsigned char* x, unsigned char y, int mo); +extern unsigned char __atomic_fetch_and_1(unsigned char* x, unsigned char y, int mo); +extern unsigned char __atomic_fetch_or_1(unsigned char* x, unsigned char y, int mo); +extern unsigned char __atomic_fetch_xor_1(unsigned char* x, unsigned char y, int mo); + +// The default functions should work with pointers so we have to decide based on pointer size +#if UINTPTR_MAX == 0xFFFFFFFF + +#define atomic_load_explicit __atomic_load_4 +#define atomic_store_explicit __atomic_store_4 +#define atomic_compare_exchange_weak_explicit __atomic_compare_exchange_4 +#define atomic_compare_exchange_strong_explicit __atomic_compare_exchange_4 +#define atomic_exchange_explicit __atomic_exchange_4 +#define atomic_fetch_add_explicit __atomic_fetch_add_4 +#define atomic_fetch_sub_explicit __atomic_sub_fetch_4 + +#else + +#define atomic_load_explicit __atomic_load_8 +#define atomic_store_explicit __atomic_store_8 +#define atomic_compare_exchange_weak_explicit __atomic_compare_exchange_8 +#define atomic_compare_exchange_strong_explicit __atomic_compare_exchange_8 +#define atomic_exchange_explicit __atomic_exchange_8 +#define atomic_fetch_add_explicit __atomic_fetch_add_8 +#define atomic_fetch_sub_explicit __atomic_sub_fetch_8 + +#endif + +// memory order policies - we use "sequentially consistent" by default + +#define memory_order_relaxed 0 +#define memory_order_consume 1 +#define memory_order_acquire 2 +#define memory_order_release 3 +#define memory_order_acq_rel 4 +#define memory_order_seq_cst 5 + +static inline void** atomic_load(void** x) { + return (void**)atomic_load_explicit((unsigned long long*)x, memory_order_seq_cst); +} +static inline void atomic_store(void** x, void* y) { + atomic_store_explicit((unsigned long long*)x, (uintptr_t)y, memory_order_seq_cst); +} +static inline int atomic_compare_exchange_weak(void** x, void** expected, void* y) { + return (int)atomic_compare_exchange_weak_explicit((unsigned long long*)x, (unsigned long long*)expected, (uintptr_t)y, memory_order_seq_cst, memory_order_seq_cst); +} +static inline int atomic_compare_exchange_strong(void** x, void** expected, void* y) { + return (int)atomic_compare_exchange_strong_explicit((unsigned long long*)x, (unsigned long long*)expected, (uintptr_t)y, memory_order_seq_cst, memory_order_seq_cst); +} +static inline uintptr_t atomic_exchange(void** x, void* y) { + return atomic_exchange_explicit((unsigned long long*)x, (uintptr_t)y, memory_order_seq_cst); +} +static inline uintptr_t atomic_fetch_add(uintptr_t* x, uintptr_t y) { + return atomic_fetch_add_explicit(x, y, memory_order_seq_cst); +} +static inline uintptr_t atomic_fetch_sub(uintptr_t* x, uintptr_t y) { + return atomic_fetch_sub_explicit(x, y, memory_order_seq_cst); +} +static inline uintptr_t atomic_fetch_and(uintptr_t* x, uintptr_t y) { + return atomic_fetch_and_explicit(x, y, memory_order_seq_cst); +} +static inline uintptr_t atomic_fetch_or(uintptr_t* x, uintptr_t y) { + return atomic_fetch_or_explicit(x, y, memory_order_seq_cst); +} +static inline uintptr_t atomic_fetch_xor(uintptr_t* x, uintptr_t y) { + return atomic_fetch_xor_explicit(x, y, memory_order_seq_cst); +} + +#define atomic_load_ptr atomic_load +#define atomic_store_ptr atomic_store +#define atomic_compare_exchange_weak_ptr atomic_compare_exchange_weak +#define atomic_compare_exchange_strong_ptr atomic_compare_exchange_strong +#define atomic_exchange_ptr atomic_exchange +#define atomic_fetch_add_ptr atomic_fetch_add +#define atomic_fetch_sub_ptr atomic_fetch_sub +#define atomic_fetch_and_ptr atomic_fetch_and +#define atomic_fetch_or_ptr atomic_fetch_or +#define atomic_fetch_xor_ptr atomic_fetch_xor + +// specialized versions for 64 bit + +static inline unsigned long long atomic_load_u64(unsigned long long* x) { + return __atomic_load_8(x, memory_order_seq_cst); +} +static inline void atomic_store_u64(unsigned long long* x, unsigned long long y) { + __atomic_store_8(x, y, memory_order_seq_cst); +} +static inline int atomic_compare_exchange_weak_u64(unsigned long long* x, unsigned long long* expected, unsigned long long y) { + return (int)__atomic_compare_exchange_8(x, expected, y, memory_order_seq_cst, memory_order_seq_cst); +} +static inline int atomic_compare_exchange_strong_u64(unsigned long long* x, unsigned long long* expected, unsigned long long y) { + return (int)__atomic_compare_exchange_8(x, expected, y, memory_order_seq_cst, memory_order_seq_cst); +} +static inline unsigned long long atomic_exchange_u64(unsigned long long* x, unsigned long long y) { + return __atomic_exchange_8(x, y, memory_order_seq_cst); +} +static inline unsigned long long atomic_fetch_add_u64(unsigned long long* x, unsigned long long y) { + return __atomic_fetch_add_8(x, y, memory_order_seq_cst); +} +static inline unsigned long long atomic_fetch_sub_u64(unsigned long long* x, unsigned long long y) { + return __atomic_fetch_sub_8(x, y, memory_order_seq_cst); +} +static inline unsigned long long atomic_fetch_and_u64(unsigned long long* x, unsigned long long y) { + return __atomic_fetch_and_8(x, y, memory_order_seq_cst); +} +static inline unsigned long long atomic_fetch_or_u64(unsigned long long* x, unsigned long long y) { + return __atomic_fetch_or_8(x, y, memory_order_seq_cst); +} +static inline unsigned long long atomic_fetch_xor_u64(unsigned long long* x, unsigned long long y) { + return __atomic_fetch_xor_8(x, y, memory_order_seq_cst); +} + +static inline unsigned atomic_load_u32(unsigned* x) { + return __atomic_load_4(x, memory_order_seq_cst); +} +static inline void atomic_store_u32(unsigned* x, unsigned y) { + __atomic_store_4(x, y, memory_order_seq_cst); +} +static inline int atomic_compare_exchange_weak_u32(unsigned* x, unsigned* expected, unsigned y) { + return (int)__atomic_compare_exchange_4(x, expected, y, memory_order_seq_cst, memory_order_seq_cst); +} +static inline int atomic_compare_exchange_strong_u32(unsigned* x, unsigned* expected, unsigned y) { + return (int)__atomic_compare_exchange_4(x, expected, y, memory_order_seq_cst, memory_order_seq_cst); +} +static inline unsigned atomic_exchange_u32(unsigned* x, unsigned y) { + return __atomic_exchange_4(x, y, memory_order_seq_cst); +} +static inline unsigned atomic_fetch_add_u32(unsigned* x, unsigned y) { + return __atomic_fetch_add_4(x, y, memory_order_seq_cst); +} +static inline unsigned atomic_fetch_sub_u32(unsigned* x, unsigned y) { + return __atomic_fetch_sub_4(x, y, memory_order_seq_cst); +} +static inline unsigned atomic_fetch_and_u32(unsigned* x, unsigned y) { + return __atomic_fetch_and_4(x, y, memory_order_seq_cst); +} +static inline unsigned atomic_fetch_or_u32(unsigned* x, unsigned y) { + return __atomic_fetch_or_4(x, y, memory_order_seq_cst); +} +static inline unsigned atomic_fetch_xor_u32(unsigned* x, unsigned y) { + return __atomic_fetch_xor_4(x, y, memory_order_seq_cst); +} + +static inline unsigned short atomic_load_u16(unsigned short* x) { + return __atomic_load_2(x, memory_order_seq_cst); +} +static inline void atomic_store_u16(void* x, unsigned short y) { + __atomic_store_2(x, y, memory_order_seq_cst); +} +static inline int atomic_compare_exchange_weak_u16(void* x, unsigned short* expected, unsigned short y) { + return (int)__atomic_compare_exchange_2(x, expected, y, memory_order_seq_cst, memory_order_seq_cst); +} +static inline int atomic_compare_exchange_strong_u16(unsigned short* x, unsigned short* expected, unsigned short y) { + return (int)__atomic_compare_exchange_2(x, expected, y, memory_order_seq_cst, memory_order_seq_cst); +} +static inline unsigned short atomic_exchange_u16(unsigned short* x, unsigned short y) { + return __atomic_exchange_2(x, y, memory_order_seq_cst); +} +static inline unsigned short atomic_fetch_add_u16(unsigned short* x, unsigned short y) { + return __atomic_fetch_add_2(x, y, memory_order_seq_cst); +} +static inline unsigned short atomic_fetch_sub_u16(unsigned short* x, unsigned short y) { + return __atomic_fetch_sub_2(x, y, memory_order_seq_cst); +} +static inline unsigned short atomic_fetch_and_u16(unsigned short* x, unsigned short y) { + return __atomic_fetch_and_2(x, y, memory_order_seq_cst); +} +static inline unsigned short atomic_fetch_or_u16(unsigned short* x, unsigned short y) { + return __atomic_fetch_or_2(x, y, memory_order_seq_cst); +} +static inline unsigned short atomic_fetch_xor_u16(unsigned short* x, unsigned short y) { + return __atomic_fetch_xor_2(x, y, memory_order_seq_cst); +} + +static inline unsigned char atomic_load_byte(unsigned char* x) { + return __atomic_load_1(x, memory_order_seq_cst); +} +static inline void atomic_store_byte(unsigned char* x, unsigned char y) { + __atomic_store_1(x, y, memory_order_seq_cst); +} +static inline int atomic_compare_exchange_weak_byte(unsigned char* x, unsigned char* expected, unsigned char y) { + return __atomic_compare_exchange_1(x, expected, y, memory_order_seq_cst, memory_order_seq_cst); +} +static inline int atomic_compare_exchange_strong_byte(unsigned char* x, unsigned char* expected, unsigned char y) { + return __atomic_compare_exchange_1(x, expected, y, memory_order_seq_cst, memory_order_seq_cst); +} +static inline unsigned char atomic_exchange_byte(unsigned char* x, unsigned char y) { + return __atomic_exchange_1(x, y, memory_order_seq_cst); +} +static inline unsigned char atomic_fetch_add_byte(unsigned char* x, unsigned char y) { + return __atomic_fetch_add_1(x, y, memory_order_seq_cst); +} +static inline unsigned char atomic_fetch_sub_byte(unsigned char* x, unsigned char y) { + return __atomic_fetch_sub_1(x, y, memory_order_seq_cst); +} +static inline unsigned char atomic_fetch_and_byte(unsigned char* x, unsigned char y) { + return __atomic_fetch_and_1(x, y, memory_order_seq_cst); +} +static inline unsigned char atomic_fetch_or_byte(unsigned char* x, unsigned char y) { + return __atomic_fetch_or_1(x, y, memory_order_seq_cst); +} +static inline unsigned char atomic_fetch_xor_byte(unsigned char* x, unsigned char y) { + return __atomic_fetch_xor_1(x, y, memory_order_seq_cst); +} + +#else + +// Since V might be confused with "generic" C functions either we provide special versions +// for gcc/clang, too +static inline unsigned long long atomic_load_u64(unsigned long long* x) { + return atomic_load_explicit((_Atomic unsigned long long*)x, memory_order_seq_cst); +} +static inline void atomic_store_u64(unsigned long long* x, unsigned long long y) { + atomic_store_explicit((_Atomic unsigned long long*)x, y, memory_order_seq_cst); +} +static inline int atomic_compare_exchange_weak_u64(unsigned long long* x, unsigned long long* expected, unsigned long long y) { + return (int)atomic_compare_exchange_weak_explicit((_Atomic unsigned long long*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst); +} +static inline int atomic_compare_exchange_strong_u64(unsigned long long* x, unsigned long long* expected, unsigned long long y) { + return (int)atomic_compare_exchange_strong_explicit((_Atomic unsigned long long*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst); +} +static inline unsigned long long atomic_exchange_u64(unsigned long long* x, unsigned long long y) { + return atomic_exchange_explicit((_Atomic unsigned long long*)x, y, memory_order_seq_cst); +} +static inline unsigned long long atomic_fetch_add_u64(unsigned long long* x, unsigned long long y) { + return atomic_fetch_add_explicit((_Atomic unsigned long long*)x, y, memory_order_seq_cst); +} +static inline unsigned long long atomic_fetch_sub_u64(unsigned long long* x, unsigned long long y) { + return atomic_fetch_sub_explicit((_Atomic unsigned long long*)x, y, memory_order_seq_cst); +} +static inline unsigned long long atomic_fetch_and_u64(unsigned long long* x, unsigned long long y) { + return atomic_fetch_and_explicit((_Atomic unsigned long long*)x, y, memory_order_seq_cst); +} +static inline unsigned long long atomic_fetch_or_u64(unsigned long long* x, unsigned long long y) { + return atomic_fetch_or_explicit((_Atomic unsigned long long*)x, y, memory_order_seq_cst); +} +static inline unsigned long long atomic_fetch_xor_u64(unsigned long long* x, unsigned long long y) { + return atomic_fetch_xor_explicit((_Atomic unsigned long long*)x, y, memory_order_seq_cst); +} + + +static inline void* atomic_load_ptr(void** x) { + return (void*)atomic_load_explicit((_Atomic uintptr_t*)x, memory_order_seq_cst); +} +static inline void atomic_store_ptr(void** x, void* y) { + atomic_store_explicit((_Atomic uintptr_t*)x, (uintptr_t)y, memory_order_seq_cst); +} +static inline int atomic_compare_exchange_weak_ptr(void** x, void** expected, void* y) { + return (int)atomic_compare_exchange_weak_explicit((_Atomic uintptr_t*)x, (unsigned long *)expected, (uintptr_t)y, memory_order_seq_cst, memory_order_seq_cst); +} +static inline int atomic_compare_exchange_strong_ptr(void** x, void** expected, void* y) { + return (int)atomic_compare_exchange_strong_explicit((_Atomic uintptr_t*)x, (unsigned long *)expected, (uintptr_t)y, memory_order_seq_cst, memory_order_seq_cst); +} +static inline void* atomic_exchange_ptr(void** x, void* y) { + return (void*)atomic_exchange_explicit((_Atomic uintptr_t*)x, (uintptr_t)y, memory_order_seq_cst); +} +static inline void* atomic_fetch_add_ptr(void** x, void* y) { + return (void*)atomic_fetch_add_explicit((_Atomic uintptr_t*)x, (uintptr_t)y, memory_order_seq_cst); +} +static inline void* atomic_fetch_sub_ptr(void** x, void* y) { + return (void*)atomic_fetch_sub_explicit((_Atomic uintptr_t*)x, (uintptr_t)y, memory_order_seq_cst); +} +static inline void* atomic_fetch_and_ptr(void** x, void* y) { + return (void*)atomic_fetch_and_explicit((_Atomic uintptr_t*)x, (uintptr_t)y, memory_order_seq_cst); +} +static inline void* atomic_fetch_or_ptr(void** x, void* y) { + return (void*)atomic_fetch_or_explicit((_Atomic uintptr_t*)x, (uintptr_t)y, memory_order_seq_cst); +} +static inline void* atomic_fetch_xor_ptr(void** x, void* y) { + return (void*)atomic_fetch_xor_explicit((_Atomic uintptr_t*)x, (uintptr_t)y, memory_order_seq_cst); +} + + +static inline unsigned atomic_load_u32(unsigned* x) { + return atomic_load_explicit((_Atomic unsigned*)x, memory_order_seq_cst); +} +static inline void atomic_store_u32(unsigned* x, unsigned y) { + atomic_store_explicit((_Atomic unsigned*)x, y, memory_order_seq_cst); +} +static inline int atomic_compare_exchange_weak_u32(unsigned* x, unsigned* expected, unsigned y) { + return (int)atomic_compare_exchange_weak_explicit((_Atomic unsigned*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst); +} +static inline int atomic_compare_exchange_strong_u32(unsigned* x, unsigned* expected, unsigned y) { + return (int)atomic_compare_exchange_strong_explicit((_Atomic unsigned*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst); +} +static inline unsigned atomic_exchange_u32(unsigned* x, unsigned y) { + return atomic_exchange_explicit((_Atomic unsigned*)x, y, memory_order_seq_cst); +} +static inline unsigned atomic_fetch_add_u32(unsigned* x, unsigned y) { + return atomic_fetch_add_explicit((_Atomic unsigned*)x, y, memory_order_seq_cst); +} +static inline unsigned atomic_fetch_sub_u32(unsigned* x, unsigned y) { + return atomic_fetch_sub_explicit((_Atomic unsigned*)x, y, memory_order_seq_cst); +} +static inline unsigned atomic_fetch_and_u32(unsigned* x, unsigned y) { + return atomic_fetch_and_explicit((_Atomic unsigned*)x, y, memory_order_seq_cst); +} +static inline unsigned atomic_fetch_or_u32(unsigned* x, unsigned y) { + return atomic_fetch_or_explicit((_Atomic unsigned*)x, y, memory_order_seq_cst); +} +static inline unsigned atomic_fetch_xor_u32(unsigned* x, unsigned y) { + return atomic_fetch_xor_explicit((_Atomic unsigned*)x, y, memory_order_seq_cst); +} + +static inline unsigned short atomic_load_u16(unsigned short* x) { + return atomic_load_explicit((_Atomic unsigned short*)x, memory_order_seq_cst); +} +static inline void atomic_store_u16(void* x, unsigned short y) { + atomic_store_explicit((_Atomic unsigned short*)x, y, memory_order_seq_cst); +} +static inline int atomic_compare_exchange_weak_u16(void* x, unsigned short* expected, unsigned short y) { + return (int)atomic_compare_exchange_weak_explicit((_Atomic unsigned short*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst); +} +static inline int atomic_compare_exchange_strong_u16(unsigned short* x, unsigned short* expected, unsigned short y) { + return (int)atomic_compare_exchange_strong_explicit((_Atomic unsigned short*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst); +} +static inline unsigned short atomic_exchange_u16(unsigned short* x, unsigned short y) { + return atomic_exchange_explicit((_Atomic unsigned short*)x, y, memory_order_seq_cst); +} +static inline unsigned short atomic_fetch_add_u16(unsigned short* x, unsigned short y) { + return atomic_fetch_add_explicit((_Atomic unsigned short*)x, y, memory_order_seq_cst); +} +static inline unsigned short atomic_fetch_sub_u16(unsigned short* x, unsigned short y) { + return atomic_fetch_sub_explicit((_Atomic unsigned short*)x, y, memory_order_seq_cst); +} +static inline unsigned short atomic_fetch_and_u16(unsigned short* x, unsigned short y) { + return atomic_fetch_and_explicit((_Atomic unsigned short*)x, y, memory_order_seq_cst); +} +static inline unsigned short atomic_fetch_or_u16(unsigned short* x, unsigned short y) { + return atomic_fetch_or_explicit((_Atomic unsigned short*)x, y, memory_order_seq_cst); +} +static inline unsigned short atomic_fetch_xor_u16(unsigned short* x, unsigned short y) { + return atomic_fetch_xor_explicit((_Atomic unsigned short*)x, y, memory_order_seq_cst); +} + +static inline unsigned char atomic_load_byte(unsigned char* x) { + return atomic_load_explicit((_Atomic unsigned char*)x, memory_order_seq_cst); +} +static inline void atomic_store_byte(unsigned char* x, unsigned char y) { + atomic_store_explicit((_Atomic unsigned char*)x, y, memory_order_seq_cst); +} +static inline int atomic_compare_exchange_weak_byte(unsigned char* x, unsigned char* expected, unsigned char y) { + return (int)atomic_compare_exchange_weak_explicit((_Atomic unsigned char*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst); +} +static inline int atomic_compare_exchange_strong_byte(unsigned char* x, unsigned char* expected, unsigned char y) { + return (int)atomic_compare_exchange_strong_explicit((_Atomic unsigned char*)x, expected, y, memory_order_seq_cst, memory_order_seq_cst); +} +static inline unsigned char atomic_exchange_byte(unsigned char* x, unsigned char y) { + return atomic_exchange_explicit((_Atomic unsigned char*)x, y, memory_order_seq_cst); +} +static inline unsigned char atomic_fetch_add_byte(unsigned char* x, unsigned char y) { + return atomic_fetch_add_explicit((_Atomic unsigned char*)x, y, memory_order_seq_cst); +} +static inline unsigned char atomic_fetch_sub_byte(unsigned char* x, unsigned char y) { + return atomic_fetch_sub_explicit((_Atomic unsigned char*)x, y, memory_order_seq_cst); +} +static inline unsigned char atomic_fetch_and_byte(unsigned char* x, unsigned char y) { + return atomic_fetch_and_explicit((_Atomic unsigned char*)x, y, memory_order_seq_cst); +} +static inline unsigned char atomic_fetch_or_byte(unsigned char* x, unsigned char y) { + return atomic_fetch_or_explicit((_Atomic unsigned char*)x, y, memory_order_seq_cst); +} +static inline unsigned char atomic_fetch_xor_byte(unsigned char* x, unsigned char y) { + return atomic_fetch_xor_explicit((_Atomic unsigned char*)x, y, memory_order_seq_cst); +} + +#endif +#endif diff --git a/v_windows/v/old/thirdparty/stdatomic/nix/atomic_cpp.h b/v_windows/v/old/thirdparty/stdatomic/nix/atomic_cpp.h new file mode 100644 index 0000000..252f5c6 --- /dev/null +++ b/v_windows/v/old/thirdparty/stdatomic/nix/atomic_cpp.h @@ -0,0 +1,541 @@ +// Source: https://android.googlesource.com/platform/bionic/+/lollipop-release/libc/include/stdatomic.h +/*- + * Copyright (c) 2011 Ed Schouten + * David Chisnall + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +#ifndef _STDATOMIC_H_ +#define _STDATOMIC_H_ +#include +#if defined(__cplusplus) && defined(_USING_LIBCXX) +#ifdef __clang__ +#if __has_feature(cxx_atomic) +#define _STDATOMIC_HAVE_ATOMIC +#endif +#else /* gcc */ +#if __GNUC_PREREQ(4, 7) +#define _STDATOMIC_HAVE_ATOMIC +#endif +#endif +#endif +#ifdef _STDATOMIC_HAVE_ATOMIC +/* We have a usable C++ ; use it instead. */ +#include +#undef _Atomic +/* Also defined by for gcc. But not used in macros. */ +/* Also a clang intrinsic. */ +/* Should not be used by client code before this file is */ +/* included. The definitions in themselves see */ +/* the old definition, as they should. */ +/* Client code sees the following definition. */ +#define _Atomic(t) std::atomic +using std::atomic_bool; +using std::atomic_char; +using std::atomic_char16_t; +using std::atomic_char32_t; +using std::atomic_compare_exchange_strong; +using std::atomic_compare_exchange_strong_explicit; +using std::atomic_compare_exchange_weak; +using std::atomic_compare_exchange_weak_explicit; +using std::atomic_exchange; +using std::atomic_exchange_explicit; +using std::atomic_fetch_add; +using std::atomic_fetch_add_explicit; +using std::atomic_fetch_and; +using std::atomic_fetch_and_explicit; +using std::atomic_fetch_or; +using std::atomic_fetch_or_explicit; +using std::atomic_fetch_sub; +using std::atomic_fetch_sub_explicit; +using std::atomic_fetch_xor; +using std::atomic_fetch_xor_explicit; +using std::atomic_init; +using std::atomic_int; +using std::atomic_int_fast16_t; +using std::atomic_int_fast32_t; +using std::atomic_int_fast64_t; +using std::atomic_int_fast8_t; +using std::atomic_int_least16_t; +using std::atomic_int_least32_t; +using std::atomic_int_least64_t; +using std::atomic_int_least8_t; +using std::atomic_intmax_t; +using std::atomic_intptr_t; +using std::atomic_is_lock_free; +using std::atomic_llong; +using std::atomic_load; +using std::atomic_load_explicit; +using std::atomic_long; +using std::atomic_ptrdiff_t; +using std::atomic_schar; +using std::atomic_short; +using std::atomic_signal_fence; +using std::atomic_size_t; +using std::atomic_store; +using std::atomic_store_explicit; +using std::atomic_thread_fence; +using std::atomic_uchar; +using std::atomic_uint; +using std::atomic_uint_fast16_t; +using std::atomic_uint_fast32_t; +using std::atomic_uint_fast64_t; +using std::atomic_uint_fast8_t; +using std::atomic_uint_least16_t; +using std::atomic_uint_least32_t; +using std::atomic_uint_least64_t; +using std::atomic_uint_least8_t; +using std::atomic_uintmax_t; +using std::atomic_uintptr_t; +using std::atomic_ullong; +using std::atomic_ulong; +using std::atomic_ushort; +using std::atomic_wchar_t; +using std::memory_order; +using std::memory_order_acq_rel; +using std::memory_order_consume; +using std::memory_order_relaxed; +using std::memory_order_release; +using std::memory_order_seq_cst; +#else /* unavailable, possibly because this is C, not C++ */ +#include +#include +/* + * C: Do it ourselves. + * Note that the runtime representation defined here should be compatible + * with the C++ one, i.e. an _Atomic(T) needs to contain the same + * bits as a T. + */ +#include /* For ptrdiff_t. */ +#include /* TODO: Should pollute namespace less. */ +#if __STDC_VERSION__ >= 201112L +#include /* For char16_t and char32_t. */ +#endif +#ifdef __clang__ +#if __has_extension(c_atomic) || __has_extension(cxx_atomic) +#define __CLANG_ATOMICS +#else +#error "stdatomic.h does not support your compiler" +#endif +#if __has_builtin(__sync_swap) +#define __HAS_BUILTIN_SYNC_SWAP +#endif +#else +#if __GNUC_PREREQ(4, 7) +#define __GNUC_ATOMICS +#else +#define __SYNC_ATOMICS +#ifdef __cplusplus +#define __ATOMICS_AVOID_DOT_INIT +#endif +#endif +#endif +/* + * 7.17.1 Atomic lock-free macros. + */ +#ifdef __GCC_ATOMIC_BOOL_LOCK_FREE +#define ATOMIC_BOOL_LOCK_FREE __GCC_ATOMIC_BOOL_LOCK_FREE +#elif defined(__SYNC_ATOMICS) +#define ATOMIC_BOOL_LOCK_FREE 2 /* For all modern platforms */ +#endif +#ifdef __GCC_ATOMIC_CHAR_LOCK_FREE +#define ATOMIC_CHAR_LOCK_FREE __GCC_ATOMIC_CHAR_LOCK_FREE +#elif defined(__SYNC_ATOMICS) +#define ATOMIC_CHAR_LOCK_FREE 2 +#endif +#ifdef __GCC_ATOMIC_CHAR16_T_LOCK_FREE +#define ATOMIC_CHAR16_T_LOCK_FREE __GCC_ATOMIC_CHAR16_T_LOCK_FREE +#elif defined(__SYNC_ATOMICS) +#define ATOMIC_CHAR16_T_LOCK_FREE 2 +#endif +#ifdef __GCC_ATOMIC_CHAR32_T_LOCK_FREE +#define ATOMIC_CHAR32_T_LOCK_FREE __GCC_ATOMIC_CHAR32_T_LOCK_FREE +#elif defined(__SYNC_ATOMICS) +#define ATOMIC_CHAR32_T_LOCK_FREE 2 +#endif +#ifdef __GCC_ATOMIC_WCHAR_T_LOCK_FREE +#define ATOMIC_WCHAR_T_LOCK_FREE __GCC_ATOMIC_WCHAR_T_LOCK_FREE +#elif defined(__SYNC_ATOMICS) +#define ATOMIC_WCHAR_T_LOCK_FREE 2 +#endif +#ifdef __GCC_ATOMIC_SHORT_LOCK_FREE +#define ATOMIC_SHORT_LOCK_FREE __GCC_ATOMIC_SHORT_LOCK_FREE +#elif defined(__SYNC_ATOMICS) +#define ATOMIC_SHORT_LOCK_FREE 2 +#endif +#ifdef __GCC_ATOMIC_INT_LOCK_FREE +#define ATOMIC_INT_LOCK_FREE __GCC_ATOMIC_INT_LOCK_FREE +#elif defined(__SYNC_ATOMICS) +#define ATOMIC_INT_LOCK_FREE 2 +#endif +#ifdef __GCC_ATOMIC_LONG_LOCK_FREE +#define ATOMIC_LONG_LOCK_FREE __GCC_ATOMIC_LONG_LOCK_FREE +#elif defined(__SYNC_ATOMICS) +#define ATOMIC_LONG_LOCK_FREE 2 +#endif +#ifdef __GCC_ATOMIC_LLONG_LOCK_FREE +#define ATOMIC_LLONG_LOCK_FREE __GCC_ATOMIC_LLONG_LOCK_FREE +#elif defined(__SYNC_ATOMICS) +#define ATOMIC_LLONG_LOCK_FREE 1 /* maybe */ +#endif +#ifdef __GCC_ATOMIC_POINTER_LOCK_FREE +#define ATOMIC_POINTER_LOCK_FREE __GCC_ATOMIC_POINTER_LOCK_FREE +#elif defined(__SYNC_ATOMICS) +#define ATOMIC_POINTER_LOCK_FREE 2 +#endif +/* + * 7.17.2 Initialization. + */ +#if defined(__CLANG_ATOMICS) +#define ATOMIC_VAR_INIT(value) (value) +#define atomic_init(obj, value) __c11_atomic_init(obj, value) +#else +#ifdef __ATOMICS_AVOID_DOT_INIT +#define ATOMIC_VAR_INIT(value) \ + { \ + value \ + } +#else +#define ATOMIC_VAR_INIT(value) \ + { \ + .__val = (value) \ + } +#endif +#define atomic_init(obj, value) ((void)((obj)->__val = (value))) +#endif +/* + * Clang and recent GCC both provide predefined macros for the memory + * orderings. If we are using a compiler that doesn't define them, use the + * clang values - these will be ignored in the fallback path. + */ +#ifndef __ATOMIC_RELAXED +#define __ATOMIC_RELAXED 0 +#endif +#ifndef __ATOMIC_CONSUME +#define __ATOMIC_CONSUME 1 +#endif +#ifndef __ATOMIC_ACQUIRE +#define __ATOMIC_ACQUIRE 2 +#endif +#ifndef __ATOMIC_RELEASE +#define __ATOMIC_RELEASE 3 +#endif +#ifndef __ATOMIC_ACQ_REL +#define __ATOMIC_ACQ_REL 4 +#endif +#ifndef __ATOMIC_SEQ_CST +#define __ATOMIC_SEQ_CST 5 +#endif +/* + * 7.17.3 Order and consistency. + * + * The memory_order_* constants that denote the barrier behaviour of the + * atomic operations. + * The enum values must be identical to those used by the + * C++ header. + */ +typedef enum +{ + memory_order_relaxed = __ATOMIC_RELAXED, + memory_order_consume = __ATOMIC_CONSUME, + memory_order_acquire = __ATOMIC_ACQUIRE, + memory_order_release = __ATOMIC_RELEASE, + memory_order_acq_rel = __ATOMIC_ACQ_REL, + memory_order_seq_cst = __ATOMIC_SEQ_CST +} memory_order; +/* + * 7.17.4 Fences. + */ +static __inline void +atomic_thread_fence(memory_order __order __attribute__((unused))) +{ +#ifdef __CLANG_ATOMICS + __c11_atomic_thread_fence(__order); +#elif defined(__GNUC_ATOMICS) + __atomic_thread_fence(__order); +#else + __sync_synchronize(); +#endif +} +static __inline void +atomic_signal_fence(memory_order __order __attribute__((unused))) +{ +#ifdef __CLANG_ATOMICS + __c11_atomic_signal_fence(__order); +#elif defined(__GNUC_ATOMICS) + __atomic_signal_fence(__order); +#else + __asm volatile("" :: + : "memory"); +#endif +} +/* + * 7.17.5 Lock-free property. + */ +#if defined(_KERNEL) +/* Atomics in kernelspace are always lock-free. */ +#define atomic_is_lock_free(obj) \ + ((void)(obj), (_Bool)1) +#elif defined(__CLANG_ATOMICS) +#define atomic_is_lock_free(obj) \ + __c11_atomic_is_lock_free(sizeof(*(obj))) +#elif defined(__GNUC_ATOMICS) +#define atomic_is_lock_free(obj) \ + __atomic_is_lock_free(sizeof((obj)->__val), &(obj)->__val) +#else +#define atomic_is_lock_free(obj) \ + ((void)(obj), sizeof((obj)->__val) <= sizeof(void *)) +#endif +/* + * 7.17.6 Atomic integer types. + */ +#ifndef __CLANG_ATOMICS +/* + * No native support for _Atomic(). Place object in structure to prevent + * most forms of direct non-atomic access. + */ +#define _Atomic(T) \ + struct \ + { \ + T volatile __val; \ + } +#endif +typedef _Atomic(bool) atomic_bool; +typedef _Atomic(char) atomic_char; +typedef _Atomic(signed char) atomic_schar; +typedef _Atomic(unsigned char) atomic_uchar; +typedef _Atomic(short) atomic_short; +typedef _Atomic(unsigned short) atomic_ushort; +typedef _Atomic(int) atomic_int; +typedef _Atomic(unsigned int) atomic_uint; +typedef _Atomic(long) atomic_long; +typedef _Atomic(unsigned long) atomic_ulong; +typedef _Atomic(long long) atomic_llong; +typedef _Atomic(unsigned long long) atomic_ullong; +#if __STDC_VERSION__ >= 201112L || __cplusplus >= 201103L +typedef _Atomic(char16_t) atomic_char16_t; +typedef _Atomic(char32_t) atomic_char32_t; +#endif +typedef _Atomic(wchar_t) atomic_wchar_t; +typedef _Atomic(int_least8_t) atomic_int_least8_t; +typedef _Atomic(uint_least8_t) atomic_uint_least8_t; +typedef _Atomic(int_least16_t) atomic_int_least16_t; +typedef _Atomic(uint_least16_t) atomic_uint_least16_t; +typedef _Atomic(int_least32_t) atomic_int_least32_t; +typedef _Atomic(uint_least32_t) atomic_uint_least32_t; +typedef _Atomic(int_least64_t) atomic_int_least64_t; +typedef _Atomic(uint_least64_t) atomic_uint_least64_t; +typedef _Atomic(int_fast8_t) atomic_int_fast8_t; +typedef _Atomic(uint_fast8_t) atomic_uint_fast8_t; +typedef _Atomic(int_fast16_t) atomic_int_fast16_t; +typedef _Atomic(uint_fast16_t) atomic_uint_fast16_t; +typedef _Atomic(int_fast32_t) atomic_int_fast32_t; +typedef _Atomic(uint_fast32_t) atomic_uint_fast32_t; +typedef _Atomic(int_fast64_t) atomic_int_fast64_t; +typedef _Atomic(uint_fast64_t) atomic_uint_fast64_t; +typedef _Atomic(intptr_t) atomic_intptr_t; +typedef _Atomic(uintptr_t) atomic_uintptr_t; +typedef _Atomic(size_t) atomic_size_t; +typedef _Atomic(ptrdiff_t) atomic_ptrdiff_t; +typedef _Atomic(intmax_t) atomic_intmax_t; +typedef _Atomic(uintmax_t) atomic_uintmax_t; +/* + * 7.17.7 Operations on atomic types. + */ +/* + * Compiler-specific operations. + */ +#if defined(__CLANG_ATOMICS) +#define atomic_compare_exchange_strong_explicit(object, expected, \ + desired, success, failure) \ + __c11_atomic_compare_exchange_strong(object, expected, desired, \ + success, failure) +#define atomic_compare_exchange_weak_explicit(object, expected, \ + desired, success, failure) \ + __c11_atomic_compare_exchange_weak(object, expected, desired, \ + success, failure) +#define atomic_exchange_explicit(object, desired, order) \ + __c11_atomic_exchange(object, desired, order) +#define atomic_fetch_add_explicit(object, operand, order) \ + __c11_atomic_fetch_add(object, operand, order) +#define atomic_fetch_and_explicit(object, operand, order) \ + __c11_atomic_fetch_and(object, operand, order) +#define atomic_fetch_or_explicit(object, operand, order) \ + __c11_atomic_fetch_or(object, operand, order) +#define atomic_fetch_sub_explicit(object, operand, order) \ + __c11_atomic_fetch_sub(object, operand, order) +#define atomic_fetch_xor_explicit(object, operand, order) \ + __c11_atomic_fetch_xor(object, operand, order) +#define atomic_load_explicit(object, order) \ + __c11_atomic_load(object, order) +#define atomic_store_explicit(object, desired, order) \ + __c11_atomic_store(object, desired, order) +#elif defined(__GNUC_ATOMICS) +#define atomic_compare_exchange_strong_explicit(object, expected, \ + desired, success, failure) \ + __atomic_compare_exchange_n(&(object)->__val, expected, \ + desired, 0, success, failure) +#define atomic_compare_exchange_weak_explicit(object, expected, \ + desired, success, failure) \ + __atomic_compare_exchange_n(&(object)->__val, expected, \ + desired, 1, success, failure) +#define atomic_exchange_explicit(object, desired, order) \ + __atomic_exchange_n(&(object)->__val, desired, order) +#define atomic_fetch_add_explicit(object, operand, order) \ + __atomic_fetch_add(&(object)->__val, operand, order) +#define atomic_fetch_and_explicit(object, operand, order) \ + __atomic_fetch_and(&(object)->__val, operand, order) +#define atomic_fetch_or_explicit(object, operand, order) \ + __atomic_fetch_or(&(object)->__val, operand, order) +#define atomic_fetch_sub_explicit(object, operand, order) \ + __atomic_fetch_sub(&(object)->__val, operand, order) +#define atomic_fetch_xor_explicit(object, operand, order) \ + __atomic_fetch_xor(&(object)->__val, operand, order) +#define atomic_load_explicit(object, order) \ + __atomic_load_n(&(object)->__val, order) +#define atomic_store_explicit(object, desired, order) \ + __atomic_store_n(&(object)->__val, desired, order) +#else +#define __atomic_apply_stride(object, operand) \ + (((__typeof__((object)->__val))0) + (operand)) +#define atomic_compare_exchange_strong_explicit(object, expected, \ + desired, success, failure) __extension__({ \ + __typeof__(expected) __ep = (expected); \ + __typeof__(*__ep) __e = *__ep; \ + (void)(success); \ + (void)(failure); \ + (bool)((*__ep = __sync_val_compare_and_swap(&(object)->__val, \ + __e, desired)) == __e); \ +}) +#define atomic_compare_exchange_weak_explicit(object, expected, \ + desired, success, failure) \ + atomic_compare_exchange_strong_explicit(object, expected, \ + desired, success, failure) +#ifdef __HAS_BUILTIN_SYNC_SWAP +/* Clang provides a full-barrier atomic exchange - use it if available. */ +#define atomic_exchange_explicit(object, desired, order) \ + ((void)(order), __sync_swap(&(object)->__val, desired)) +#else +/* + * __sync_lock_test_and_set() is only an acquire barrier in theory (although in + * practice it is usually a full barrier) so we need an explicit barrier before + * it. + */ +#define atomic_exchange_explicit(object, desired, order) \ + __extension__({ \ + __typeof__(object) __o = (object); \ + __typeof__(desired) __d = (desired); \ + (void)(order); \ + __sync_synchronize(); \ + __sync_lock_test_and_set(&(__o)->__val, __d); \ + }) +#endif +#define atomic_fetch_add_explicit(object, operand, order) \ + ((void)(order), __sync_fetch_and_add(&(object)->__val, \ + __atomic_apply_stride(object, operand))) +#define atomic_fetch_and_explicit(object, operand, order) \ + ((void)(order), __sync_fetch_and_and(&(object)->__val, operand)) +#define atomic_fetch_or_explicit(object, operand, order) \ + ((void)(order), __sync_fetch_and_or(&(object)->__val, operand)) +#define atomic_fetch_sub_explicit(object, operand, order) \ + ((void)(order), __sync_fetch_and_sub(&(object)->__val, \ + __atomic_apply_stride(object, operand))) +#define atomic_fetch_xor_explicit(object, operand, order) \ + ((void)(order), __sync_fetch_and_xor(&(object)->__val, operand)) +#define atomic_load_explicit(object, order) \ + ((void)(order), __sync_fetch_and_add(&(object)->__val, 0)) +#define atomic_store_explicit(object, desired, order) \ + ((void)atomic_exchange_explicit(object, desired, order)) +#endif +/* + * Convenience functions. + * + * Don't provide these in kernel space. In kernel space, we should be + * disciplined enough to always provide explicit barriers. + */ +#ifndef _KERNEL +#define atomic_compare_exchange_strong(object, expected, desired) \ + atomic_compare_exchange_strong_explicit(object, expected, \ + desired, memory_order_seq_cst, memory_order_seq_cst) +#define atomic_compare_exchange_weak(object, expected, desired) \ + atomic_compare_exchange_weak_explicit(object, expected, \ + desired, memory_order_seq_cst, memory_order_seq_cst) +#define atomic_exchange(object, desired) \ + atomic_exchange_explicit(object, desired, memory_order_seq_cst) +#define atomic_fetch_add(object, operand) \ + atomic_fetch_add_explicit(object, operand, memory_order_seq_cst) +#define atomic_fetch_and(object, operand) \ + atomic_fetch_and_explicit(object, operand, memory_order_seq_cst) +#define atomic_fetch_or(object, operand) \ + atomic_fetch_or_explicit(object, operand, memory_order_seq_cst) +#define atomic_fetch_sub(object, operand) \ + atomic_fetch_sub_explicit(object, operand, memory_order_seq_cst) +#define atomic_fetch_xor(object, operand) \ + atomic_fetch_xor_explicit(object, operand, memory_order_seq_cst) +#define atomic_load(object) \ + atomic_load_explicit(object, memory_order_seq_cst) +#define atomic_store(object, desired) \ + atomic_store_explicit(object, desired, memory_order_seq_cst) +#endif /* !_KERNEL */ +/* + * 7.17.8 Atomic flag type and operations. + * + * XXX: Assume atomic_bool can be used as an atomic_flag. Is there some + * kind of compiler built-in type we could use? + */ +typedef struct +{ + atomic_bool __flag; +} atomic_flag; +#define ATOMIC_FLAG_INIT \ + { \ + ATOMIC_VAR_INIT(false) \ + } +static __inline bool +atomic_flag_test_and_set_explicit(volatile atomic_flag *__object, + memory_order __order) +{ + return (atomic_exchange_explicit(&__object->__flag, 1, __order)); +} +static __inline void +atomic_flag_clear_explicit(volatile atomic_flag *__object, memory_order __order) +{ + atomic_store_explicit(&__object->__flag, 0, __order); +} +#ifndef _KERNEL +static __inline bool +atomic_flag_test_and_set(volatile atomic_flag *__object) +{ + return (atomic_flag_test_and_set_explicit(__object, + memory_order_seq_cst)); +} +static __inline void +atomic_flag_clear(volatile atomic_flag *__object) +{ + atomic_flag_clear_explicit(__object, memory_order_seq_cst); +} +#endif /* !_KERNEL */ +#endif /* unavailable */ +#endif /* !_STDATOMIC_H_ */ diff --git a/v_windows/v/old/thirdparty/stdatomic/win/atomic.h b/v_windows/v/old/thirdparty/stdatomic/win/atomic.h new file mode 100644 index 0000000..f6d7cf5 --- /dev/null +++ b/v_windows/v/old/thirdparty/stdatomic/win/atomic.h @@ -0,0 +1,366 @@ +/* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef COMPAT_ATOMICS_WIN32_STDATOMIC_H +#define COMPAT_ATOMICS_WIN32_STDATOMIC_H + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include + +#ifdef __TINYC__ +#endif + +#define ATOMIC_FLAG_INIT 0 + +#define ATOMIC_VAR_INIT(value) (value) + +#define atomic_init(obj, value) \ + do \ + { \ + *(obj) = (value); \ + } while (0) + +#define kill_dependency(y) ((void)0) + +#define atomic_thread_fence(order) \ + MemoryBarrier(); + +#define atomic_signal_fence(order) \ + ((void)0) + +#define atomic_is_lock_free(obj) 0 + +typedef intptr_t atomic_flag; +typedef intptr_t atomic_bool; +typedef intptr_t atomic_char; +typedef intptr_t atomic_schar; +typedef intptr_t atomic_uchar; +typedef intptr_t atomic_short; +typedef intptr_t atomic_ushort; +typedef intptr_t atomic_int; +typedef intptr_t atomic_uint; +typedef intptr_t atomic_long; +typedef intptr_t atomic_ulong; +typedef intptr_t atomic_llong; +typedef intptr_t atomic_ullong; +typedef intptr_t atomic_wchar_t; +typedef intptr_t atomic_int_least8_t; +typedef intptr_t atomic_uint_least8_t; +typedef intptr_t atomic_int_least16_t; +typedef intptr_t atomic_uint_least16_t; +typedef intptr_t atomic_int_least32_t; +typedef intptr_t atomic_uint_least32_t; +typedef intptr_t atomic_int_least64_t; +typedef intptr_t atomic_uint_least64_t; +typedef intptr_t atomic_int_fast8_t; +typedef intptr_t atomic_uint_fast8_t; +typedef intptr_t atomic_int_fast16_t; +typedef intptr_t atomic_uint_fast16_t; +typedef intptr_t atomic_int_fast32_t; +typedef intptr_t atomic_uint_fast32_t; +typedef intptr_t atomic_int_fast64_t; +typedef intptr_t atomic_uint_fast64_t; +typedef intptr_t atomic_intptr_t; +typedef intptr_t atomic_uintptr_t; +typedef intptr_t atomic_size_t; +typedef intptr_t atomic_ptrdiff_t; +typedef intptr_t atomic_intmax_t; +typedef intptr_t atomic_uintmax_t; + +#ifdef __TINYC__ +/* + For TCC it is missing the x64 version of _InterlockedExchangeAdd64 so we + fake it (works the same) with InterlockedCompareExchange64 until it + succeeds +*/ +__CRT_INLINE LONGLONG _InterlockedExchangeAdd64(LONGLONG volatile *Addend, LONGLONG Value) +{ + LONGLONG Old; + do + { + Old = *Addend; + } while (InterlockedCompareExchange64(Addend, Old + Value, Old) != Old); + return Old; +} + +__CRT_INLINE LONG _InterlockedExchangeAdd(LONG volatile *Addend, LONG Value) +{ + LONG Old; + do + { + Old = *Addend; + } while (InterlockedCompareExchange(Addend, Old + Value, Old) != Old); + return Old; +} + +__CRT_INLINE SHORT _InterlockedExchangeAdd16(SHORT volatile *Addend, SHORT Value) +{ + SHORT Old; + do + { + Old = *Addend; + } while (InterlockedCompareExchange16(Addend, Old + Value, Old) != Old); + return Old; +} + +#define InterlockedIncrement64 _InterlockedExchangeAdd64 + +#endif + +#define atomic_store(object, desired) \ + do \ + { \ + *(object) = (desired); \ + MemoryBarrier(); \ + } while (0) + +#define atomic_store_explicit(object, desired, order) \ + atomic_store(object, desired) + +#define atomic_load(object) \ + (MemoryBarrier(), *(object)) + +#define atomic_load_explicit(object, order) \ + atomic_load(object) + +#define atomic_exchange(object, desired) \ + InterlockedExchangePointer(object, desired) + +#define atomic_exchange_explicit(object, desired, order) \ + atomic_exchange(object, desired) + +static inline int atomic_compare_exchange_strong(intptr_t *object, intptr_t *expected, + intptr_t desired) +{ + intptr_t old = *expected; + *expected = (intptr_t)InterlockedCompareExchangePointer( + (PVOID *)object, (PVOID)desired, (PVOID)old); + return *expected == old; +} + +#define atomic_compare_exchange_strong_explicit(object, expected, desired, success, failure) \ + atomic_compare_exchange_strong(object, expected, desired) + +#define atomic_compare_exchange_weak(object, expected, desired) \ + atomic_compare_exchange_strong(object, expected, desired) + +#define atomic_compare_exchange_weak_explicit(object, expected, desired, success, failure) \ + atomic_compare_exchange_weak(object, expected, desired) + +#ifdef _WIN64 + +#define atomic_fetch_add(object, operand) \ + InterlockedExchangeAdd64(object, operand) + +#define atomic_fetch_sub(object, operand) \ + InterlockedExchangeAdd64(object, -(operand)) + +#define atomic_fetch_or(object, operand) \ + InterlockedOr64(object, operand) + +#define atomic_fetch_xor(object, operand) \ + InterlockedXor64(object, operand) + +#define atomic_fetch_and(object, operand) \ + InterlockedAnd64(object, operand) +#else +#define atomic_fetch_add(object, operand) \ + InterlockedExchangeAdd(object, operand) + +#define atomic_fetch_sub(object, operand) \ + InterlockedExchangeAdd(object, -(operand)) + +#define atomic_fetch_or(object, operand) \ + InterlockedOr(object, operand) + +#define atomic_fetch_xor(object, operand) \ + InterlockedXor(object, operand) + +#define atomic_fetch_and(object, operand) \ + InterlockedAnd(object, operand) +#endif /* _WIN64 */ + +/* specialized versions with explicit object size */ + +#define atomic_load_ptr atomic_load +#define atomic_store_ptr atomic_store +#define atomic_compare_exchange_weak_ptr atomic_compare_exchange_weak +#define atomic_compare_exchange_strong_ptr atomic_compare_exchange_strong +#define atomic_exchange_ptr atomic_exchange +#define atomic_fetch_add_ptr atomic_fetch_add +#define atomic_fetch_sub_ptr atomic_fetch_sub +#define atomic_fetch_and_ptr atomic_fetch_and +#define atomic_fetch_or_ptr atomic_fetch_or +#define atomic_fetch_xor_ptr atomic_fetch_xor + +static inline void atomic_store_u64(unsigned long long* object, unsigned long long desired) { + do { + *(object) = (desired); + MemoryBarrier(); + } while (0); +} + +static inline unsigned long long atomic_load_u64(unsigned long long* object) { + return (MemoryBarrier(), *(object)); +} + +#define atomic_exchange_u64(object, desired) \ + InterlockedExchange64(object, desired) + +static inline int atomic_compare_exchange_strong_u64(unsigned long long* object, unsigned long long* expected, + unsigned long long desired) +{ + unsigned long long old = *expected; + *expected = InterlockedCompareExchange64(object, desired, old); + return *expected == old; +} + +#define atomic_compare_exchange_weak_u64(object, expected, desired) \ + atomic_compare_exchange_strong_u64(object, expected, desired) + +#define atomic_fetch_add_u64(object, operand) \ + InterlockedExchangeAdd64(object, operand) + +#define atomic_fetch_sub_u64(object, operand) \ + InterlockedExchangeAdd64(object, -(operand)) + +#define atomic_fetch_or_u64(object, operand) \ + InterlockedOr64(object, operand) + +#define atomic_fetch_xor_u64(object, operand) \ + InterlockedXor64(object, operand) + +#define atomic_fetch_and_u64(object, operand) \ + InterlockedAnd64(object, operand) + + + +static inline void atomic_store_u32(unsigned* object, unsigned desired) { + do { + *(object) = (desired); + MemoryBarrier(); + } while (0); +} + +static inline unsigned atomic_load_u32(unsigned* object) { + return (MemoryBarrier(), *(object)); +} + +#define atomic_exchange_u32(object, desired) \ + InterlockedExchange(object, desired) + +static inline int atomic_compare_exchange_strong_u32(unsigned* object, unsigned* expected, + unsigned desired) +{ + unsigned old = *expected; + *expected = InterlockedCompareExchange(object, desired, old); + return *expected == old; +} + +#define atomic_compare_exchange_weak_u32(object, expected, desired) \ + atomic_compare_exchange_strong_u32(object, expected, desired) + +#define atomic_fetch_add_u32(object, operand) \ + InterlockedExchangeAdd(object, operand) + +#define atomic_fetch_sub_u32(object, operand) \ + InterlockedExchangeAdd(object, -(operand)) + +#define atomic_fetch_or_u32(object, operand) \ + InterlockedOr(object, operand) + +#define atomic_fetch_xor_u32(object, operand) \ + InterlockedXor(object, operand) + +#define atomic_fetch_and_u32(object, operand) \ + InterlockedAnd(object, operand) + + + +static inline void atomic_store_u16(unsigned short* object, unsigned short desired) { + do { + *(object) = (desired); + MemoryBarrier(); + } while (0); +} + +static inline unsigned short atomic_load_u16(unsigned short* object) { + return (MemoryBarrier(), *(object)); +} + +#define atomic_exchange_u16(object, desired) \ + InterlockedExchange16(object, desired) + +static inline int atomic_compare_exchange_strong_u16(unsigned short* object, unsigned short* expected, + unsigned short desired) +{ + unsigned short old = *expected; + *expected = InterlockedCompareExchange16(object, desired, old); + return *expected == old; +} + +#define atomic_compare_exchange_weak_u16(object, expected, desired) \ + atomic_compare_exchange_strong_u16(object, expected, desired) + +#define atomic_fetch_add_u16(object, operand) \ + InterlockedExchangeAdd16(object, operand) + +#define atomic_fetch_sub_u16(object, operand) \ + InterlockedExchangeAdd16(object, -(operand)) + +#define atomic_fetch_or_u16(object, operand) \ + InterlockedOr16(object, operand) + +#define atomic_fetch_xor_u16(object, operand) \ + InterlockedXor16(object, operand) + +#define atomic_fetch_and_u16(object, operand) \ + InterlockedAnd16(object, operand) + + + +#define atomic_fetch_add_explicit(object, operand, order) \ + atomic_fetch_add(object, operand) + +#define atomic_fetch_sub_explicit(object, operand, order) \ + atomic_fetch_sub(object, operand) + +#define atomic_fetch_or_explicit(object, operand, order) \ + atomic_fetch_or(object, operand) + +#define atomic_fetch_xor_explicit(object, operand, order) \ + atomic_fetch_xor(object, operand) + +#define atomic_fetch_and_explicit(object, operand, order) \ + atomic_fetch_and(object, operand) + +#define atomic_flag_test_and_set(object) \ + atomic_exchange(object, 1) + +#define atomic_flag_test_and_set_explicit(object, order) \ + atomic_flag_test_and_set(object) + +#define atomic_flag_clear(object) \ + atomic_store(object, 0) + +#define atomic_flag_clear_explicit(object, order) \ + atomic_flag_clear(object) + +#endif /* COMPAT_ATOMICS_WIN32_STDATOMIC_H */ diff --git a/v_windows/v/old/thirdparty/vschannel/vschannel.c b/v_windows/v/old/thirdparty/vschannel/vschannel.c new file mode 100644 index 0000000..0d55dde --- /dev/null +++ b/v_windows/v/old/thirdparty/vschannel/vschannel.c @@ -0,0 +1,1095 @@ +#include +#include + +// Proxy +WCHAR * psz_proxy_server = L"proxy"; +INT i_proxy_port = 80; + +// Options +INT port_number = 443; +BOOL use_proxy = FALSE; +DWORD protocol = 0; +ALG_ID aid_key_exch = 0; + +// TODO: joe-c +// socket / tls ctx +struct TlsContext { + // SSPI + PSecurityFunctionTable sspi; + // Cred store + HCERTSTORE cert_store; + SCHANNEL_CRED schannel_cred; + // Socket + SOCKET socket; + CredHandle h_client_creds; + CtxtHandle h_context; + PCCERT_CONTEXT p_pemote_cert_context; + BOOL creds_initialized; + BOOL context_initialized; +}; + +TlsContext new_tls_context() { + return (struct TlsContext) { + .cert_store = NULL, + .socket = INVALID_SOCKET, + .creds_initialized = FALSE, + .context_initialized = FALSE, + .p_pemote_cert_context = NULL + }; +}; + +void vschannel_cleanup(TlsContext *tls_ctx) { + // Free the server certificate context. + if(tls_ctx->p_pemote_cert_context) { + CertFreeCertificateContext(tls_ctx->p_pemote_cert_context); + tls_ctx->p_pemote_cert_context = NULL; + } + + // Free SSPI context handle. + if(tls_ctx->context_initialized) { + tls_ctx->sspi->DeleteSecurityContext(&tls_ctx->h_context); + tls_ctx->context_initialized = FALSE; + } + + // Free SSPI credentials handle. + if(tls_ctx->creds_initialized) { + tls_ctx->sspi->FreeCredentialsHandle(&tls_ctx->h_client_creds); + tls_ctx->creds_initialized = FALSE; + } + + // Close socket. + if(tls_ctx->socket != INVALID_SOCKET) { + closesocket(tls_ctx->socket); + tls_ctx->socket = INVALID_SOCKET; + } + + // Close "MY" certificate store. + if(tls_ctx->cert_store) { + CertCloseStore(tls_ctx->cert_store, 0); + tls_ctx->cert_store = NULL; + } +} + +void vschannel_init(TlsContext *tls_ctx) { + tls_ctx->sspi = InitSecurityInterface(); + + if(tls_ctx->sspi == NULL) { + wprintf(L"Error 0x%x reading security interface.\n", + GetLastError()); + vschannel_cleanup(tls_ctx); + } + + // Create credentials. + if(create_credentials(tls_ctx)) { + wprintf(L"Error creating credentials\n"); + vschannel_cleanup(tls_ctx); + } + tls_ctx->creds_initialized = TRUE; +} + +INT request(TlsContext *tls_ctx, INT iport, LPWSTR host, CHAR *req, CHAR **out) +{ + SecBuffer ExtraData; + SECURITY_STATUS Status; + + INT i; + INT iOption; + PCHAR pszOption; + + INT resp_length = 0; + + protocol = SP_PROT_TLS1_2_CLIENT; + + port_number = iport; + + // Connect to server. + if(connect_to_server(tls_ctx, host, port_number)) { + wprintf(L"Error connecting to server\n"); + vschannel_cleanup(tls_ctx); + return resp_length; + } + + // Perform handshake + if(perform_client_handshake(tls_ctx, host, &ExtraData)) { + wprintf(L"Error performing handshake\n"); + vschannel_cleanup(tls_ctx); + return resp_length; + } + tls_ctx->context_initialized = TRUE; + + // Authenticate server's credentials. + + // Get server's certificate. + Status = tls_ctx->sspi->QueryContextAttributes(&tls_ctx->h_context, + SECPKG_ATTR_REMOTE_CERT_CONTEXT, + (PVOID)&tls_ctx->p_pemote_cert_context); + if(Status != SEC_E_OK) { + wprintf(L"Error 0x%x querying remote certificate\n", Status); + vschannel_cleanup(tls_ctx); + return resp_length; + } + + // Attempt to validate server certificate. + Status = verify_server_certificate(tls_ctx->p_pemote_cert_context, host,0); + if(Status) { + // The server certificate did not validate correctly. At this + // point, we cannot tell if we are connecting to the correct + // server, or if we are connecting to a "man in the middle" + // attack server. + + // It is therefore best if we abort the connection. + + wprintf(L"Error 0x%x authenticating server credentials!\n", Status); + vschannel_cleanup(tls_ctx); + return resp_length; + } + + // Free the server certificate context. + CertFreeCertificateContext(tls_ctx->p_pemote_cert_context); + tls_ctx->p_pemote_cert_context = NULL; + + // Request from server + if(https_make_request(tls_ctx, req, out, &resp_length)) { + vschannel_cleanup(tls_ctx); + return resp_length; + } + + // Send a close_notify alert to the server and + // close down the connection. + if(disconnect_from_server(tls_ctx)) { + wprintf(L"Error disconnecting from server\n"); + vschannel_cleanup(tls_ctx); + return resp_length; + } + tls_ctx->context_initialized = FALSE; + tls_ctx->socket = INVALID_SOCKET; + + return resp_length; +} + + +static SECURITY_STATUS create_credentials(TlsContext *tls_ctx) { + TimeStamp tsExpiry; + SECURITY_STATUS Status; + + DWORD cSupportedAlgs = 0; + ALG_ID rgbSupportedAlgs[16]; + + PCCERT_CONTEXT pCertContext = NULL; + + // Open the "MY" certificate store, which is where Internet Explorer + // stores its client certificates. + if(tls_ctx->cert_store == NULL) { + tls_ctx->cert_store = CertOpenSystemStore(0, L"MY"); + + if(!tls_ctx->cert_store) { + wprintf(L"Error 0x%x returned by CertOpenSystemStore\n", + GetLastError()); + return SEC_E_NO_CREDENTIALS; + } + } + + // Build Schannel credential structure. Currently, this sample only + // specifies the protocol to be used (and optionally the certificate, + // of course). Real applications may wish to specify other parameters + // as well. + + ZeroMemory(&tls_ctx->schannel_cred, sizeof(tls_ctx->schannel_cred)); + + tls_ctx->schannel_cred.dwVersion = SCHANNEL_CRED_VERSION; + if(pCertContext) + { + tls_ctx->schannel_cred.cCreds = 1; + tls_ctx->schannel_cred.paCred = &pCertContext; + } + + tls_ctx->schannel_cred.grbitEnabledProtocols = protocol; + + if(aid_key_exch) + { + rgbSupportedAlgs[cSupportedAlgs++] = aid_key_exch; + } + + if(cSupportedAlgs) + { + tls_ctx->schannel_cred.cSupportedAlgs = cSupportedAlgs; + tls_ctx->schannel_cred.palgSupportedAlgs = rgbSupportedAlgs; + } + + tls_ctx->schannel_cred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS; + + // The SCH_CRED_MANUAL_CRED_VALIDATION flag is specified because + // this sample verifies the server certificate manually. + // Applications that expect to run on WinNT, Win9x, or WinME + // should specify this flag and also manually verify the server + // certificate. Applications running on newer versions of Windows can + // leave off this flag, in which case the InitializeSecurityContext + // function will validate the server certificate automatically. + // tls_ctx->schannel_cred.dwFlags |= SCH_CRED_MANUAL_CRED_VALIDATION; + + // Create an SSPI credential. + + Status = tls_ctx->sspi->AcquireCredentialsHandle( + NULL, // Name of principal + UNISP_NAME_W, // Name of package + SECPKG_CRED_OUTBOUND, // Flags indicating use + NULL, // Pointer to logon ID + &tls_ctx->schannel_cred, // Package specific data + NULL, // Pointer to GetKey() func + NULL, // Value to pass to GetKey() + &tls_ctx->h_client_creds, // (out) Cred Handle + &tsExpiry); // (out) Lifetime (optional) + if(Status != SEC_E_OK) { + wprintf(L"Error 0x%x returned by AcquireCredentialsHandle\n", Status); + goto cleanup; + } + +cleanup: + + // Free the certificate context. Schannel has already made its own copy. + + if(pCertContext) { + CertFreeCertificateContext(pCertContext); + } + + + return Status; +} + + +static INT connect_to_server(TlsContext *tls_ctx, LPWSTR host, INT port_number) { + SOCKET Socket; + + SOCKADDR_STORAGE local_address = { 0 }; + SOCKADDR_STORAGE remote_address = { 0 }; + + DWORD local_address_length = sizeof(local_address); + DWORD remote_address_length = sizeof(remote_address); + + struct timeval tv; + tv.tv_sec = 60; + tv.tv_usec = 0; + + Socket = socket(PF_INET, SOCK_STREAM, 0); + if(Socket == INVALID_SOCKET) { + wprintf(L"Error %d creating socket\n", WSAGetLastError()); + return WSAGetLastError(); + } + + LPWSTR connect_name = use_proxy ? psz_proxy_server : host; + + WCHAR service_name[10]; + int res = wsprintf(service_name, L"%d", port_number); + + if(WSAConnectByNameW(Socket,connect_name, service_name, &local_address_length, + &local_address, &remote_address_length, &remote_address, &tv, NULL) == SOCKET_ERROR) { + wprintf(L"Error %d connecting to \"%s\" (%s)\n", + WSAGetLastError(), + connect_name, + service_name); + closesocket(Socket); + return WSAGetLastError(); + } + + if(use_proxy) { + BYTE pbMessage[200]; + DWORD cbMessage; + + // Build message for proxy server + strcpy(pbMessage, "CONNECT "); + strcat(pbMessage, host); + strcat(pbMessage, ":"); + _itoa(port_number, pbMessage + strlen(pbMessage), 10); + strcat(pbMessage, " HTTP/1.0\r\nUser-Agent: webclient\r\n\r\n"); + cbMessage = (DWORD)strlen(pbMessage); + + // Send message to proxy server + if(send(Socket, pbMessage, cbMessage, 0) == SOCKET_ERROR) { + wprintf(L"Error %d sending message to proxy!\n", WSAGetLastError()); + return WSAGetLastError(); + } + + // Receive message from proxy server + cbMessage = recv(Socket, pbMessage, 200, 0); + if(cbMessage == SOCKET_ERROR) { + wprintf(L"Error %d receiving message from proxy\n", WSAGetLastError()); + return WSAGetLastError(); + } + + // this sample is limited but in normal use it + // should continue to receive until CR LF CR LF is received + } + + tls_ctx->socket = Socket; + + return SEC_E_OK; +} + + +static LONG disconnect_from_server(TlsContext *tls_ctx) { + DWORD dwType; + PBYTE pbMessage; + DWORD cbMessage; + DWORD cbData; + + SecBufferDesc OutBuffer; + SecBuffer OutBuffers[1]; + DWORD dwSSPIFlags; + DWORD dwSSPIOutFlags; + TimeStamp tsExpiry; + DWORD Status; + + // Notify schannel that we are about to close the connection. + + dwType = SCHANNEL_SHUTDOWN; + + OutBuffers[0].pvBuffer = &dwType; + OutBuffers[0].BufferType = SECBUFFER_TOKEN; + OutBuffers[0].cbBuffer = sizeof(dwType); + + OutBuffer.cBuffers = 1; + OutBuffer.pBuffers = OutBuffers; + OutBuffer.ulVersion = SECBUFFER_VERSION; + + Status = tls_ctx->sspi->ApplyControlToken(&tls_ctx->h_context, &OutBuffer); + + if(FAILED(Status)) { + wprintf(L"Error 0x%x returned by ApplyControlToken\n", Status); + goto cleanup; + } + + // Build an SSL close notify message. + + dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT | + ISC_REQ_REPLAY_DETECT | + ISC_REQ_CONFIDENTIALITY | + ISC_RET_EXTENDED_ERROR | + ISC_REQ_ALLOCATE_MEMORY | + ISC_REQ_STREAM; + + OutBuffers[0].pvBuffer = NULL; + OutBuffers[0].BufferType = SECBUFFER_TOKEN; + OutBuffers[0].cbBuffer = 0; + + OutBuffer.cBuffers = 1; + OutBuffer.pBuffers = OutBuffers; + OutBuffer.ulVersion = SECBUFFER_VERSION; + + Status = tls_ctx->sspi->InitializeSecurityContext( + &tls_ctx->h_client_creds, &tls_ctx->h_context, NULL, dwSSPIFlags, 0, SECURITY_NATIVE_DREP, + NULL, 0, &tls_ctx->h_context, &OutBuffer, &dwSSPIOutFlags, &tsExpiry); + + if(FAILED(Status)) { + wprintf(L"Error 0x%x returned by InitializeSecurityContext\n", Status); + goto cleanup; + } + + pbMessage = OutBuffers[0].pvBuffer; + cbMessage = OutBuffers[0].cbBuffer; + + // Send the close notify message to the server. + + if(pbMessage != NULL && cbMessage != 0) { + cbData = send(tls_ctx->socket, pbMessage, cbMessage, 0); + if(cbData == SOCKET_ERROR || cbData == 0) { + Status = WSAGetLastError(); + wprintf(L"Error %d sending close notify\n", Status); + goto cleanup; + } + + // Free output buffer. + tls_ctx->sspi->FreeContextBuffer(pbMessage); + } + + +cleanup: + + // Free the security context. + tls_ctx->sspi->DeleteSecurityContext(&tls_ctx->h_context); + + // Close the socket. + closesocket(tls_ctx->socket); + + return Status; +} + + +static SECURITY_STATUS perform_client_handshake(TlsContext *tls_ctx, WCHAR *host, SecBuffer *pExtraData) { + SecBufferDesc OutBuffer; + SecBuffer OutBuffers[1]; + DWORD dwSSPIFlags; + DWORD dwSSPIOutFlags; + TimeStamp tsExpiry; + SECURITY_STATUS scRet; + DWORD cbData; + + dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT | + ISC_REQ_REPLAY_DETECT | + ISC_REQ_CONFIDENTIALITY | + ISC_RET_EXTENDED_ERROR | + ISC_REQ_ALLOCATE_MEMORY | + ISC_REQ_STREAM; + + // + // Initiate a ClientHello message and generate a token. + // + + OutBuffers[0].pvBuffer = NULL; + OutBuffers[0].BufferType = SECBUFFER_TOKEN; + OutBuffers[0].cbBuffer = 0; + + OutBuffer.cBuffers = 1; + OutBuffer.pBuffers = OutBuffers; + OutBuffer.ulVersion = SECBUFFER_VERSION; + + scRet = tls_ctx->sspi->InitializeSecurityContext( + &tls_ctx->h_client_creds, + NULL, + host, + dwSSPIFlags, + 0, + SECURITY_NATIVE_DREP, + NULL, + 0, + &tls_ctx->h_context, + &OutBuffer, + &dwSSPIOutFlags, + &tsExpiry); + + if(scRet != SEC_I_CONTINUE_NEEDED) + { + wprintf(L"Error %d returned by InitializeSecurityContext (1)\n", scRet); + return scRet; + } + + // Send response to server if there is one. + if(OutBuffers[0].cbBuffer != 0 && OutBuffers[0].pvBuffer != NULL) + { + cbData = send(tls_ctx->socket, OutBuffers[0].pvBuffer, OutBuffers[0].cbBuffer, 0); + if(cbData == SOCKET_ERROR || cbData == 0) { + wprintf(L"Error %d sending data to server (1)\n", WSAGetLastError()); + tls_ctx->sspi->FreeContextBuffer(OutBuffers[0].pvBuffer); + tls_ctx->sspi->DeleteSecurityContext(&tls_ctx->h_context); + return SEC_E_INTERNAL_ERROR; + } + + // Free output buffer. + tls_ctx->sspi->FreeContextBuffer(OutBuffers[0].pvBuffer); + OutBuffers[0].pvBuffer = NULL; + } + + return client_handshake_loop(tls_ctx, TRUE, pExtraData); +} + + +static SECURITY_STATUS client_handshake_loop(TlsContext *tls_ctx, BOOL fDoInitialRead, SecBuffer *pExtraData) { + SecBufferDesc InBuffer; + SecBuffer InBuffers[2]; + SecBufferDesc OutBuffer; + SecBuffer OutBuffers[1]; + DWORD dwSSPIFlags; + DWORD dwSSPIOutFlags; + TimeStamp tsExpiry; + SECURITY_STATUS scRet; + DWORD cbData; + + PUCHAR IoBuffer; + DWORD cbIoBuffer; + BOOL fDoRead; + + + dwSSPIFlags = ISC_REQ_SEQUENCE_DETECT | + ISC_REQ_REPLAY_DETECT | + ISC_REQ_CONFIDENTIALITY | + ISC_RET_EXTENDED_ERROR | + ISC_REQ_ALLOCATE_MEMORY | + ISC_REQ_STREAM; + + // + // Allocate data buffer. + // + + IoBuffer = LocalAlloc(LPTR, IO_BUFFER_SIZE); + if(IoBuffer == NULL) + { + wprintf(L"Out of memory (1)\n"); + return SEC_E_INTERNAL_ERROR; + } + cbIoBuffer = 0; + + fDoRead = fDoInitialRead; + + + // + // Loop until the handshake is finished or an error occurs. + // + + scRet = SEC_I_CONTINUE_NEEDED; + + while(scRet == SEC_I_CONTINUE_NEEDED || + scRet == SEC_E_INCOMPLETE_MESSAGE || + scRet == SEC_I_INCOMPLETE_CREDENTIALS) { + + // Read data from server. + if(0 == cbIoBuffer || scRet == SEC_E_INCOMPLETE_MESSAGE) { + if(fDoRead) { + cbData = recv(tls_ctx->socket, + IoBuffer + cbIoBuffer, + IO_BUFFER_SIZE - cbIoBuffer, + 0); + if(cbData == SOCKET_ERROR) { + wprintf(L"Error %d reading data from server\n", WSAGetLastError()); + scRet = SEC_E_INTERNAL_ERROR; + break; + } + else if(cbData == 0) { + wprintf(L"Server unexpectedly disconnected\n"); + scRet = SEC_E_INTERNAL_ERROR; + break; + } + + cbIoBuffer += cbData; + } + else { + fDoRead = TRUE; + } + } + + // Set up the input buffers. Buffer 0 is used to pass in data + // received from the server. Schannel will consume some or all + // of this. Leftover data (if any) will be placed in buffer 1 and + // given a buffer type of SECBUFFER_EXTRA. + + InBuffers[0].pvBuffer = IoBuffer; + InBuffers[0].cbBuffer = cbIoBuffer; + InBuffers[0].BufferType = SECBUFFER_TOKEN; + + InBuffers[1].pvBuffer = NULL; + InBuffers[1].cbBuffer = 0; + InBuffers[1].BufferType = SECBUFFER_EMPTY; + + InBuffer.cBuffers = 2; + InBuffer.pBuffers = InBuffers; + InBuffer.ulVersion = SECBUFFER_VERSION; + + // Set up the output buffers. These are initialized to NULL + // so as to make it less likely we'll attempt to free random + // garbage later. + + OutBuffers[0].pvBuffer = NULL; + OutBuffers[0].BufferType= SECBUFFER_TOKEN; + OutBuffers[0].cbBuffer = 0; + + OutBuffer.cBuffers = 1; + OutBuffer.pBuffers = OutBuffers; + OutBuffer.ulVersion = SECBUFFER_VERSION; + + // Call InitializeSecurityContext. + + scRet = tls_ctx->sspi->InitializeSecurityContext( + &tls_ctx->h_client_creds, &tls_ctx->h_context, NULL, dwSSPIFlags, 0, SECURITY_NATIVE_DREP, + &InBuffer, 0, NULL, &OutBuffer, &dwSSPIOutFlags, &tsExpiry); + + // If InitializeSecurityContext was successful (or if the error was + // one of the special extended ones), send the contends of the output + // buffer to the server. + + if(scRet == SEC_E_OK || + scRet == SEC_I_CONTINUE_NEEDED || + FAILED(scRet) && (dwSSPIOutFlags & ISC_RET_EXTENDED_ERROR)) { + if(OutBuffers[0].cbBuffer != 0 && OutBuffers[0].pvBuffer != NULL) { + cbData = send(tls_ctx->socket, + OutBuffers[0].pvBuffer, + OutBuffers[0].cbBuffer, + 0); + if(cbData == SOCKET_ERROR || cbData == 0) { + wprintf(L"Error %d sending data to server (2)\n", + WSAGetLastError()); + tls_ctx->sspi->FreeContextBuffer(OutBuffers[0].pvBuffer); + tls_ctx->sspi->DeleteSecurityContext(&tls_ctx->h_context); + return SEC_E_INTERNAL_ERROR; + } + + // Free output buffer. + tls_ctx->sspi->FreeContextBuffer(OutBuffers[0].pvBuffer); + OutBuffers[0].pvBuffer = NULL; + } + } + + // If InitializeSecurityContext returned SEC_E_INCOMPLETE_MESSAGE, + // then we need to read more data from the server and try again. + if(scRet == SEC_E_INCOMPLETE_MESSAGE) { + continue; + } + + // If InitializeSecurityContext returned SEC_E_OK, then the + // handshake completed successfully. + + if(scRet == SEC_E_OK) { + // If the "extra" buffer contains data, this is encrypted application + // protocol layer stuff. It needs to be saved. The application layer + // will later decrypt it with DecryptMessage. + + if(InBuffers[1].BufferType == SECBUFFER_EXTRA) + { + pExtraData->pvBuffer = LocalAlloc(LPTR, InBuffers[1].cbBuffer); + if(pExtraData->pvBuffer == NULL) { + wprintf(L"Out of memory (2)\n"); + return SEC_E_INTERNAL_ERROR; + } + + MoveMemory(pExtraData->pvBuffer, + IoBuffer + (cbIoBuffer - InBuffers[1].cbBuffer), + InBuffers[1].cbBuffer); + + pExtraData->cbBuffer = InBuffers[1].cbBuffer; + pExtraData->BufferType = SECBUFFER_TOKEN; + + // wprintf(L"%d bytes of app data was bundled with handshake data\n", pExtraData->cbBuffer); + } + else { + pExtraData->pvBuffer = NULL; + pExtraData->cbBuffer = 0; + pExtraData->BufferType = SECBUFFER_EMPTY; + } + + // Bail out to quit + break; + } + + // Check for fatal error. + if(FAILED(scRet)) { + wprintf(L"Error 0x%x returned by InitializeSecurityContext (2)\n", scRet); + break; + } + + // If InitializeSecurityContext returned SEC_I_INCOMPLETE_CREDENTIALS, + // then the server just requested client authentication. + if(scRet == SEC_I_INCOMPLETE_CREDENTIALS) { + // Busted. The server has requested client authentication and + // the credential we supplied didn't contain a client certificate. + + // This function will read the list of trusted certificate + // authorities ("issuers") that was received from the server + // and attempt to find a suitable client certificate that + // was issued by one of these. If this function is successful, + // then we will connect using the new certificate. Otherwise, + // we will attempt to connect anonymously (using our current + // credentials). + + get_new_client_credentials(tls_ctx); + + // Go around again. + fDoRead = FALSE; + scRet = SEC_I_CONTINUE_NEEDED; + continue; + } + + // Copy any leftover data from the "extra" buffer, and go around + // again. + + if ( InBuffers[1].BufferType == SECBUFFER_EXTRA ) { + MoveMemory(IoBuffer, + IoBuffer + (cbIoBuffer - InBuffers[1].cbBuffer), + InBuffers[1].cbBuffer); + + cbIoBuffer = InBuffers[1].cbBuffer; + } + else { + cbIoBuffer = 0; + } + } + + // Delete the security context in the case of a fatal error. + if(FAILED(scRet)) { + tls_ctx->sspi->DeleteSecurityContext(&tls_ctx->h_context); + } + + LocalFree(IoBuffer); + + return scRet; +} + + +static SECURITY_STATUS https_make_request(TlsContext *tls_ctx, CHAR *req, CHAR **out, int *length) { + SecPkgContext_StreamSizes Sizes; + SECURITY_STATUS scRet; + SecBufferDesc Message; + SecBuffer Buffers[4]; + SecBuffer *pDataBuffer; + SecBuffer *pExtraBuffer; + SecBuffer ExtraBuffer; + + PBYTE pbIoBuffer; + DWORD cbIoBuffer; + DWORD cbIoBufferLength; + PBYTE pbMessage; + DWORD cbMessage; + + DWORD cbData; + INT i; + + + // Read stream encryption properties. + scRet = tls_ctx->sspi->QueryContextAttributes(&tls_ctx->h_context, SECPKG_ATTR_STREAM_SIZES, &Sizes); + if(scRet != SEC_E_OK) { + wprintf(L"Error 0x%x reading SECPKG_ATTR_STREAM_SIZES\n", scRet); + return scRet; + } + + // Allocate a working buffer. The plaintext sent to EncryptMessage + // should never be more than 'Sizes.cbMaximumMessage', so a buffer + // size of this plus the header and trailer sizes should be safe enough. + cbIoBufferLength = Sizes.cbHeader + Sizes.cbMaximumMessage + Sizes.cbTrailer; + + pbIoBuffer = LocalAlloc(LPTR, cbIoBufferLength); + if(pbIoBuffer == NULL) { + wprintf(L"Out of memory (2)\n"); + return SEC_E_INTERNAL_ERROR; + } + + // Build an HTTP request to send to the server. + + // Build the HTTP request offset into the data buffer by "header size" + // bytes. This enables Schannel to perform the encryption in place, + // which is a significant performance win. + pbMessage = pbIoBuffer + Sizes.cbHeader; + + // Build HTTP request. Note that I'm assuming that this is less than + // the maximum message size. If it weren't, it would have to be broken up. + sprintf(pbMessage, "%s", req); + + cbMessage = (DWORD)strlen(pbMessage); + + + // Encrypt the HTTP request. + Buffers[0].pvBuffer = pbIoBuffer; + Buffers[0].cbBuffer = Sizes.cbHeader; + Buffers[0].BufferType = SECBUFFER_STREAM_HEADER; + + Buffers[1].pvBuffer = pbMessage; + Buffers[1].cbBuffer = cbMessage; + Buffers[1].BufferType = SECBUFFER_DATA; + + Buffers[2].pvBuffer = pbMessage + cbMessage; + Buffers[2].cbBuffer = Sizes.cbTrailer; + Buffers[2].BufferType = SECBUFFER_STREAM_TRAILER; + + Buffers[3].BufferType = SECBUFFER_EMPTY; + + Message.ulVersion = SECBUFFER_VERSION; + Message.cBuffers = 4; + Message.pBuffers = Buffers; + + scRet = tls_ctx->sspi->EncryptMessage(&tls_ctx->h_context, 0, &Message, 0); + + if(FAILED(scRet)) { + wprintf(L"Error 0x%x returned by EncryptMessage\n", scRet); + return scRet; + } + + // Send the encrypted data to the server. + cbData = send(tls_ctx->socket, pbIoBuffer, Buffers[0].cbBuffer + Buffers[1].cbBuffer + Buffers[2].cbBuffer, 0); + if(cbData == SOCKET_ERROR || cbData == 0) { + wprintf(L"Error %d sending data to server (3)\n", WSAGetLastError()); + tls_ctx->sspi->DeleteSecurityContext(&tls_ctx->h_context); + return SEC_E_INTERNAL_ERROR; + } + + // Read data from server until done. + INT buff_size = vsc_init_resp_buff_size; + cbIoBuffer = 0; + while(TRUE){ + // Read some data. + if(0 == cbIoBuffer || scRet == SEC_E_INCOMPLETE_MESSAGE) { + cbData = recv(tls_ctx->socket, pbIoBuffer + cbIoBuffer, cbIoBufferLength - cbIoBuffer, 0); + if(cbData == SOCKET_ERROR) { + wprintf(L"Error %d reading data from server\n", WSAGetLastError()); + scRet = SEC_E_INTERNAL_ERROR; + break; + } + else if(cbData == 0) { + // Server disconnected. + if(cbIoBuffer) { + wprintf(L"Server unexpectedly disconnected\n"); + scRet = SEC_E_INTERNAL_ERROR; + return scRet; + } + else { + break; + } + } + else { + cbIoBuffer += cbData; + } + } + + // Attempt to decrypt the received data. + Buffers[0].pvBuffer = pbIoBuffer; + Buffers[0].cbBuffer = cbIoBuffer; + Buffers[0].BufferType = SECBUFFER_DATA; + + Buffers[1].BufferType = SECBUFFER_EMPTY; + Buffers[2].BufferType = SECBUFFER_EMPTY; + Buffers[3].BufferType = SECBUFFER_EMPTY; + + Message.ulVersion = SECBUFFER_VERSION; + Message.cBuffers = 4; + Message.pBuffers = Buffers; + + scRet = tls_ctx->sspi->DecryptMessage(&tls_ctx->h_context, &Message, 0, NULL); + + if(scRet == SEC_E_INCOMPLETE_MESSAGE) { + // The input buffer contains only a fragment of an + // encrypted record. Loop around and read some more + // data. + continue; + } + + // Server signalled end of session + if(scRet == SEC_I_CONTEXT_EXPIRED) { + break; + } + + if( scRet != SEC_E_OK && + scRet != SEC_I_RENEGOTIATE && + scRet != SEC_I_CONTEXT_EXPIRED) + { + wprintf(L"Error 0x%x returned by DecryptMessage\n", scRet); + return scRet; + } + + // Locate data and (optional) extra buffers. + pDataBuffer = NULL; + pExtraBuffer = NULL; + for(i = 1; i < 4; i++) { + if(pDataBuffer == NULL && Buffers[i].BufferType == SECBUFFER_DATA) + { + pDataBuffer = &Buffers[i]; + // wprintf(L"Buffers[%d].BufferType = SECBUFFER_DATA\n",i); + } + if(pExtraBuffer == NULL && Buffers[i].BufferType == SECBUFFER_EXTRA) + { + pExtraBuffer = &Buffers[i]; + } + } + + // increase buffer size if we need + int required_length = *length+(int)pDataBuffer->cbBuffer; + if( required_length > buff_size ) { + CHAR *a = realloc(*out, required_length); + if( a == NULL ) { + scRet = SEC_E_INTERNAL_ERROR; + return scRet; + } + *out = a; + buff_size = required_length; + } + // Copy the decrypted data to our output buffer + memcpy(*out+*length, pDataBuffer->pvBuffer, (int)pDataBuffer->cbBuffer); + *length += (int)pDataBuffer->cbBuffer; + + // Move any "extra" data to the input buffer. + if(pExtraBuffer) { + MoveMemory(pbIoBuffer, pExtraBuffer->pvBuffer, pExtraBuffer->cbBuffer); + cbIoBuffer = pExtraBuffer->cbBuffer; + } + else { + cbIoBuffer = 0; + } + + if(scRet == SEC_I_RENEGOTIATE) + { + // The server wants to perform another handshake sequence. + scRet = client_handshake_loop(tls_ctx, FALSE, &ExtraBuffer); + if(scRet != SEC_E_OK) { + return scRet; + } + + // Move any "extra" data to the input buffer. + if(ExtraBuffer.pvBuffer) + { + MoveMemory(pbIoBuffer, ExtraBuffer.pvBuffer, ExtraBuffer.cbBuffer); + cbIoBuffer = ExtraBuffer.cbBuffer; + } + } + } + + return SEC_E_OK; +} + + +static DWORD verify_server_certificate( PCCERT_CONTEXT pServerCert, LPWSTR host, DWORD dwCertFlags) { + HTTPSPolicyCallbackData polHttps; + CERT_CHAIN_POLICY_PARA PolicyPara; + CERT_CHAIN_POLICY_STATUS PolicyStatus; + CERT_CHAIN_PARA ChainPara; + PCCERT_CHAIN_CONTEXT pChainContext = NULL; + + CHAR *rgszUsages[] = { szOID_PKIX_KP_SERVER_AUTH, + szOID_SERVER_GATED_CRYPTO, + szOID_SGC_NETSCAPE }; + DWORD cUsages = sizeof(rgszUsages) / sizeof(CHAR*); + + PWSTR pwszServerName = NULL; + DWORD cchServerName; + DWORD Status; + + if(pServerCert == NULL) + { + Status = SEC_E_WRONG_PRINCIPAL; + goto cleanup; + } + + if(host == NULL || wcslen(host) == 0) { + Status = SEC_E_WRONG_PRINCIPAL; + goto cleanup; + } + + // Build certificate chain. + + ZeroMemory(&ChainPara, sizeof(ChainPara)); + ChainPara.cbSize = sizeof(ChainPara); + ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_OR; + ChainPara.RequestedUsage.Usage.cUsageIdentifier = cUsages; + ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = rgszUsages; + + if(!CertGetCertificateChain(NULL, pServerCert, NULL, pServerCert->hCertStore, &ChainPara, 0, NULL, &pChainContext)) { + Status = GetLastError(); + wprintf(L"Error 0x%x returned by CertGetCertificateChain!\n", Status); + goto cleanup; + } + + // Validate certificate chain. + ZeroMemory(&polHttps, sizeof(HTTPSPolicyCallbackData)); + polHttps.cbStruct = sizeof(HTTPSPolicyCallbackData); + polHttps.dwAuthType = AUTHTYPE_SERVER; + polHttps.fdwChecks = dwCertFlags; + polHttps.pwszServerName = host; + + memset(&PolicyPara, 0, sizeof(PolicyPara)); + PolicyPara.cbSize = sizeof(PolicyPara); + PolicyPara.pvExtraPolicyPara = &polHttps; + + memset(&PolicyStatus, 0, sizeof(PolicyStatus)); + PolicyStatus.cbSize = sizeof(PolicyStatus); + + if(!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, pChainContext, &PolicyPara, &PolicyStatus)){ + Status = GetLastError(); + wprintf(L"Error 0x%x returned by CertVerifyCertificateChainPolicy!\n", Status); + goto cleanup; + } + + if(PolicyStatus.dwError) { + Status = PolicyStatus.dwError; + goto cleanup; + } + + + Status = SEC_E_OK; + +cleanup: + + if(pChainContext) + { + CertFreeCertificateChain(pChainContext); + } + + if(pwszServerName) + { + LocalFree(pwszServerName); + } + + return Status; +} + + +static void get_new_client_credentials(TlsContext *tls_ctx) { + CredHandle hCreds; + SecPkgContext_IssuerListInfoEx IssuerListInfo; + PCCERT_CHAIN_CONTEXT pChainContext; + CERT_CHAIN_FIND_BY_ISSUER_PARA FindByIssuerPara; + PCCERT_CONTEXT pCertContext; + TimeStamp tsExpiry; + SECURITY_STATUS Status; + + // Read list of trusted issuers from schannel. + Status = tls_ctx->sspi->QueryContextAttributes(&tls_ctx->h_context, SECPKG_ATTR_ISSUER_LIST_EX, (PVOID)&IssuerListInfo); + if(Status != SEC_E_OK) { + wprintf(L"Error 0x%x querying issuer list info\n", Status); + return; + } + + // Enumerate the client certificates. + + ZeroMemory(&FindByIssuerPara, sizeof(FindByIssuerPara)); + + FindByIssuerPara.cbSize = sizeof(FindByIssuerPara); + FindByIssuerPara.pszUsageIdentifier = szOID_PKIX_KP_CLIENT_AUTH; + FindByIssuerPara.dwKeySpec = 0; + FindByIssuerPara.cIssuer = IssuerListInfo.cIssuers; + FindByIssuerPara.rgIssuer = IssuerListInfo.aIssuers; + + pChainContext = NULL; + + while(TRUE) { + // Find a certificate chain. + pChainContext = CertFindChainInStore(tls_ctx->cert_store, + X509_ASN_ENCODING, + 0, + CERT_CHAIN_FIND_BY_ISSUER, + &FindByIssuerPara, + pChainContext); + if(pChainContext == NULL) { + wprintf(L"Error 0x%x finding cert chain\n", GetLastError()); + break; + } + + // Get pointer to leaf certificate context. + pCertContext = pChainContext->rgpChain[0]->rgpElement[0]->pCertContext; + + // Create schannel credential. + tls_ctx->schannel_cred.dwVersion = SCHANNEL_CRED_VERSION; + tls_ctx->schannel_cred.cCreds = 1; + tls_ctx->schannel_cred.paCred = &pCertContext; + + Status = tls_ctx->sspi->AcquireCredentialsHandle( + NULL, // Name of principal + UNISP_NAME_W, // Name of package + SECPKG_CRED_OUTBOUND, // Flags indicating use + NULL, // Pointer to logon ID + &tls_ctx->schannel_cred, // Package specific data + NULL, // Pointer to GetKey() func + NULL, // Value to pass to GetKey() + &hCreds, // (out) Cred Handle + &tsExpiry); // (out) Lifetime (optional) + if(Status != SEC_E_OK) { + wprintf(L"Error 0x%x returned by AcquireCredentialsHandle\n", Status); + continue; + } + + // Destroy the old credentials. + tls_ctx->sspi->FreeCredentialsHandle(&tls_ctx->h_client_creds); + + tls_ctx->h_client_creds = hCreds; + + // + // As you can see, this sample code maintains a single credential + // handle, replacing it as necessary. This is a little unusual. + // + // Many applications maintain a global credential handle that's + // anonymous (that is, it doesn't contain a client certificate), + // which is used to connect to all servers. If a particular server + // should require client authentication, then a new credential + // is created for use when connecting to that server. The global + // anonymous credential is retained for future connections to + // other servers. + // + // Maintaining a single anonymous credential that's used whenever + // possible is most efficient, since creating new credentials all + // the time is rather expensive. + // + + break; + } +} diff --git a/v_windows/v/old/thirdparty/vschannel/vschannel.h b/v_windows/v/old/thirdparty/vschannel/vschannel.h new file mode 100644 index 0000000..0338645 --- /dev/null +++ b/v_windows/v/old/thirdparty/vschannel/vschannel.h @@ -0,0 +1,47 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define SECURITY_WIN32 +#include +#include + +#define vsc_init_resp_buff_size 44000 + +#define IO_BUFFER_SIZE 0x10000 + +#define TLS_MAX_BUFSIZ 32768 + +// Define here to be sure +#define SP_PROT_TLS1_2_CLIENT 0x00000800 + +typedef struct TlsContext TlsContext; + +TlsContext new_tls_context(); + +static void vschannel_init(TlsContext *tls_ctx); + +static void vschannel_cleanup(TlsContext *tls_ctx); + +static INT request(TlsContext *tls_ctx, INT iport, LPWSTR host, CHAR *req, CHAR **out); + +static SECURITY_STATUS https_make_request(TlsContext *tls_ctx, CHAR *req, CHAR **out, int *length); + +static INT connect_to_server(TlsContext *tls_ctx, LPWSTR host, INT port_number); + +static LONG disconnect_from_server(TlsContext *tls_ctx); + +static SECURITY_STATUS perform_client_handshake(TlsContext *tls_ctx, LPWSTR host, SecBuffer *pExtraData); + +static SECURITY_STATUS client_handshake_loop(TlsContext *tls_ctx, BOOL fDoInitialRead, SecBuffer *pExtraData); + +static DWORD verify_server_certificate(PCCERT_CONTEXT pServerCert, LPWSTR host, DWORD dwCertFlags); + +static SECURITY_STATUS create_credentials(TlsContext *tls_ctx); + +static void get_new_client_credentials(TlsContext *tls_ctx); diff --git a/v_windows/v/old/thirdparty/zip/miniz.h b/v_windows/v/old/thirdparty/zip/miniz.h new file mode 100644 index 0000000..33e7fa1 --- /dev/null +++ b/v_windows/v/old/thirdparty/zip/miniz.h @@ -0,0 +1,6859 @@ +/* + miniz.c v1.15 - public domain deflate/inflate, zlib-subset, ZIP + reading/writing/appending, PNG writing See "unlicense" statement at the end + of this file. Rich Geldreich , last updated Oct. 13, + 2013 Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: + http://www.ietf.org/rfc/rfc1951.txt + + Most API's defined in miniz.c are optional. For example, to disable the + archive related functions just define MINIZ_NO_ARCHIVE_APIS, or to get rid of + all stdio usage define MINIZ_NO_STDIO (see the list below for more macros). + + * Change History + 10/13/13 v1.15 r4 - Interim bugfix release while I work on the next major + release with Zip64 support (almost there!): + - Critical fix for the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY bug + (thanks kahmyong.moon@hp.com) which could cause locate files to not find + files. This bug would only have occured in earlier versions if you explicitly + used this flag, OR if you used mz_zip_extract_archive_file_to_heap() or + mz_zip_add_mem_to_archive_file_in_place() (which used this flag). If you + can't switch to v1.15 but want to fix this bug, just remove the uses of this + flag from both helper funcs (and of course don't use the flag). + - Bugfix in mz_zip_reader_extract_to_mem_no_alloc() from kymoon when + pUser_read_buf is not NULL and compressed size is > uncompressed size + - Fixing mz_zip_reader_extract_*() funcs so they don't try to extract + compressed data from directory entries, to account for weird zipfiles which + contain zero-size compressed data on dir entries. Hopefully this fix won't + cause any issues on weird zip archives, because it assumes the low 16-bits of + zip external attributes are DOS attributes (which I believe they always are + in practice). + - Fixing mz_zip_reader_is_file_a_directory() so it doesn't check the + internal attributes, just the filename and external attributes + - mz_zip_reader_init_file() - missing MZ_FCLOSE() call if the seek failed + - Added cmake support for Linux builds which builds all the examples, + tested with clang v3.3 and gcc v4.6. + - Clang fix for tdefl_write_image_to_png_file_in_memory() from toffaletti + - Merged MZ_FORCEINLINE fix from hdeanclark + - Fix include before config #ifdef, thanks emil.brink + - Added tdefl_write_image_to_png_file_in_memory_ex(): supports Y flipping + (super useful for OpenGL apps), and explicit control over the compression + level (so you can set it to 1 for real-time compression). + - Merged in some compiler fixes from paulharris's github repro. + - Retested this build under Windows (VS 2010, including static analysis), + tcc 0.9.26, gcc v4.6 and clang v3.3. + - Added example6.c, which dumps an image of the mandelbrot set to a PNG + file. + - Modified example2 to help test the + MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY flag more. + - In r3: Bugfix to mz_zip_writer_add_file() found during merge: Fix + possible src file fclose() leak if alignment bytes+local header file write + faiiled + - In r4: Minor bugfix to mz_zip_writer_add_from_zip_reader(): Was pushing the + wrong central dir header offset, appears harmless in this release, but it + became a problem in the zip64 branch 5/20/12 v1.14 - MinGW32/64 GCC 4.6.1 + compiler fixes: added MZ_FORCEINLINE, #include (thanks fermtect). + 5/19/12 v1.13 - From jason@cornsyrup.org and kelwert@mtu.edu - Fix + mz_crc32() so it doesn't compute the wrong CRC-32's when mz_ulong is 64-bit. + - Temporarily/locally slammed in "typedef unsigned long mz_ulong" and + re-ran a randomized regression test on ~500k files. + - Eliminated a bunch of warnings when compiling with GCC 32-bit/64. + - Ran all examples, miniz.c, and tinfl.c through MSVC 2008's /analyze + (static analysis) option and fixed all warnings (except for the silly "Use of + the comma-operator in a tested expression.." analysis warning, which I + purposely use to work around a MSVC compiler warning). + - Created 32-bit and 64-bit Codeblocks projects/workspace. Built and + tested Linux executables. The codeblocks workspace is compatible with + Linux+Win32/x64. + - Added miniz_tester solution/project, which is a useful little app + derived from LZHAM's tester app that I use as part of the regression test. + - Ran miniz.c and tinfl.c through another series of regression testing on + ~500,000 files and archives. + - Modified example5.c so it purposely disables a bunch of high-level + functionality (MINIZ_NO_STDIO, etc.). (Thanks to corysama for the + MINIZ_NO_STDIO bug report.) + - Fix ftell() usage in examples so they exit with an error on files which + are too large (a limitation of the examples, not miniz itself). 4/12/12 v1.12 + - More comments, added low-level example5.c, fixed a couple minor + level_and_flags issues in the archive API's. level_and_flags can now be set + to MZ_DEFAULT_COMPRESSION. Thanks to Bruce Dawson + for the feedback/bug report. 5/28/11 v1.11 - Added statement from + unlicense.org 5/27/11 v1.10 - Substantial compressor optimizations: + - Level 1 is now ~4x faster than before. The L1 compressor's throughput + now varies between 70-110MB/sec. on a + - Core i7 (actual throughput varies depending on the type of data, and x64 + vs. x86). + - Improved baseline L2-L9 compression perf. Also, greatly improved + compression perf. issues on some file types. + - Refactored the compression code for better readability and + maintainability. + - Added level 10 compression level (L10 has slightly better ratio than + level 9, but could have a potentially large drop in throughput on some + files). 5/15/11 v1.09 - Initial stable release. + + * Low-level Deflate/Inflate implementation notes: + + Compression: Use the "tdefl" API's. The compressor supports raw, static, + and dynamic blocks, lazy or greedy parsing, match length filtering, RLE-only, + and Huffman-only streams. It performs and compresses approximately as well as + zlib. + + Decompression: Use the "tinfl" API's. The entire decompressor is + implemented as a single function coroutine: see tinfl_decompress(). It + supports decompression into a 32KB (or larger power of 2) wrapping buffer, or + into a memory block large enough to hold the entire file. + + The low-level tdefl/tinfl API's do not make any use of dynamic memory + allocation. + + * zlib-style API notes: + + miniz.c implements a fairly large subset of zlib. There's enough + functionality present for it to be a drop-in zlib replacement in many apps: + The z_stream struct, optional memory allocation callbacks + deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound + inflateInit/inflateInit2/inflate/inflateEnd + compress, compress2, compressBound, uncompress + CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly + routines. Supports raw deflate streams or standard zlib streams with adler-32 + checking. + + Limitations: + The callback API's are not implemented yet. No support for gzip headers or + zlib static dictionaries. I've tried to closely emulate zlib's various + flavors of stream flushing and return status codes, but there are no + guarantees that miniz.c pulls this off perfectly. + + * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, + originally written by Alex Evans. Supports 1-4 bytes/pixel images. + + * ZIP archive API notes: + + The ZIP archive API's where designed with simplicity and efficiency in + mind, with just enough abstraction to get the job done with minimal fuss. + There are simple API's to retrieve file information, read files from existing + archives, create new archives, append new files to existing archives, or + clone archive data from one archive to another. It supports archives located + in memory or the heap, on disk (using stdio.h), or you can specify custom + file read/write callbacks. + + - Archive reading: Just call this function to read a single file from a + disk archive: + + void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const + char *pArchive_name, size_t *pSize, mz_uint zip_flags); + + For more complex cases, use the "mz_zip_reader" functions. Upon opening an + archive, the entire central directory is located and read as-is into memory, + and subsequent file access only occurs when reading individual files. + + - Archives file scanning: The simple way is to use this function to scan a + loaded archive for a specific file: + + int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, + const char *pComment, mz_uint flags); + + The locate operation can optionally check file comments too, which (as one + example) can be used to identify multiple versions of the same file in an + archive. This function uses a simple linear search through the central + directory, so it's not very fast. + + Alternately, you can iterate through all the files in an archive (using + mz_zip_reader_get_num_files()) and retrieve detailed info on each file by + calling mz_zip_reader_file_stat(). + + - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer + immediately writes compressed file data to disk and builds an exact image of + the central directory in memory. The central directory image is written all + at once at the end of the archive file when the archive is finalized. + + The archive writer can optionally align each file's local header and file + data to any power of 2 alignment, which can be useful when the archive will + be read from optical media. Also, the writer supports placing arbitrary data + blobs at the very beginning of ZIP archives. Archives written using either + feature are still readable by any ZIP tool. + + - Archive appending: The simple way to add a single file to an archive is + to call this function: + + mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, + const char *pArchive_name, const void *pBuf, size_t buf_size, const void + *pComment, mz_uint16 comment_size, mz_uint level_and_flags); + + The archive will be created if it doesn't already exist, otherwise it'll be + appended to. Note the appending is done in-place and is not an atomic + operation, so if something goes wrong during the operation it's possible the + archive could be left without a central directory (although the local file + headers and file data will be fine, so the archive will be recoverable). + + For more complex archive modification scenarios: + 1. The safest way is to use a mz_zip_reader to read the existing archive, + cloning only those bits you want to preserve into a new archive using using + the mz_zip_writer_add_from_zip_reader() function (which compiles the + compressed file data as-is). When you're done, delete the old archive and + rename the newly written archive, and you're done. This is safe but requires + a bunch of temporary disk space or heap memory. + + 2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using + mz_zip_writer_init_from_reader(), append new files as needed, then finalize + the archive which will write an updated central directory to the original + archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() + does.) There's a possibility that the archive's central directory could be + lost with this method if anything goes wrong, though. + + - ZIP archive support limitations: + No zip64 or spanning support. Extraction functions can only handle + unencrypted, stored or deflated files. Requires streams capable of seeking. + + * This is a header file library, like stb_image.c. To get only a header file, + either cut and paste the below header, or create miniz.h, #define + MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it. + + * Important: For best perf. be sure to customize the below macros for your + target platform: #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 #define + MINIZ_LITTLE_ENDIAN 1 #define MINIZ_HAS_64BIT_REGISTERS 1 + + * On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before + including miniz.c to ensure miniz uses the 64-bit variants: fopen64(), + stat64(), etc. Otherwise you won't be able to process large files (i.e. + 32-bit stat() fails for me on files > 0x7FFFFFFF bytes). +*/ + +#ifndef MINIZ_HEADER_INCLUDED +#define MINIZ_HEADER_INCLUDED + +#include +#include + +// Defines to completely disable specific portions of miniz.c: +// If all macros here are defined the only functionality remaining will be +// CRC-32, adler-32, tinfl, and tdefl. + +// Define MINIZ_NO_STDIO to disable all usage and any functions which rely on +// stdio for file I/O. +//#define MINIZ_NO_STDIO + +// If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able +// to get the current time, or get/set file times, and the C run-time funcs that +// get/set times won't be called. The current downside is the times written to +// your archives will be from 1979. +//#define MINIZ_NO_TIME + +// Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. +//#define MINIZ_NO_ARCHIVE_APIS + +// Define MINIZ_NO_ARCHIVE_APIS to disable all writing related ZIP archive +// API's. +//#define MINIZ_NO_ARCHIVE_WRITING_APIS + +// Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression +// API's. +//#define MINIZ_NO_ZLIB_APIS + +// Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent +// conflicts against stock zlib. +//#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES + +// Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. +// Note if MINIZ_NO_MALLOC is defined then the user must always provide custom +// user alloc/free/realloc callbacks to the zlib and archive API's, and a few +// stand-alone helper API's which don't provide custom user functions (such as +// tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. +//#define MINIZ_NO_MALLOC + +#if defined(__TINYC__) && (defined(__linux) || defined(__linux__)) +// TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc +// on Linux +#define MINIZ_NO_TIME +#endif + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS) +#include +#endif + +#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || \ + defined(__i386) || defined(__i486__) || defined(__i486) || \ + defined(i386) || defined(__ia64__) || defined(__x86_64__) +// MINIZ_X86_OR_X64_CPU is only used to help set the below macros. +#define MINIZ_X86_OR_X64_CPU 1 +#endif + +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU +// Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. +#define MINIZ_LITTLE_ENDIAN 1 +#endif + +/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES only if not set */ +#if !defined(MINIZ_USE_UNALIGNED_LOADS_AND_STORES) +#if MINIZ_X86_OR_X64_CPU +/* Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient + * integer loads and stores from unaligned addresses. */ +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 +#define MINIZ_UNALIGNED_USE_MEMCPY +#else +#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 0 +#endif +#endif + +#if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || \ + defined(_LP64) || defined(__LP64__) || defined(__ia64__) || \ + defined(__x86_64__) +// Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are +// reasonably fast (and don't involve compiler generated calls to helper +// functions). +#define MINIZ_HAS_64BIT_REGISTERS 1 +#endif + +#ifdef __APPLE__ +#define ftello64 ftello +#define fseeko64 fseeko +#define fopen64 fopen +#define freopen64 freopen + +// Darwin OSX +#define MZ_PLATFORM 19 +#endif + +#ifndef MZ_PLATFORM +#if defined(_WIN64) || defined(_WIN32) || defined(__WIN32__) +#define MZ_PLATFORM 0 +#else +// UNIX +#define MZ_PLATFORM 3 +#endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// ------------------- zlib-style API Definitions. + +// For more compatibility with zlib, miniz.c uses unsigned long for some +// parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! +typedef unsigned long mz_ulong; + +// mz_free() internally uses the MZ_FREE() macro (which by default calls free() +// unless you've modified the MZ_MALLOC macro) to release a block allocated from +// the heap. +void mz_free(void *p); + +#define MZ_ADLER32_INIT (1) +// mz_adler32() returns the initial adler-32 value to use when called with +// ptr==NULL. +mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); + +#define MZ_CRC32_INIT (0) +// mz_crc32() returns the initial CRC-32 value to use when called with +// ptr==NULL. +mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); + +// Compression strategies. +enum { + MZ_DEFAULT_STRATEGY = 0, + MZ_FILTERED = 1, + MZ_HUFFMAN_ONLY = 2, + MZ_RLE = 3, + MZ_FIXED = 4 +}; + +/* miniz error codes. Be sure to update mz_zip_get_error_string() if you add or + * modify this enum. */ +typedef enum { + MZ_ZIP_NO_ERROR = 0, + MZ_ZIP_UNDEFINED_ERROR, + MZ_ZIP_TOO_MANY_FILES, + MZ_ZIP_FILE_TOO_LARGE, + MZ_ZIP_UNSUPPORTED_METHOD, + MZ_ZIP_UNSUPPORTED_ENCRYPTION, + MZ_ZIP_UNSUPPORTED_FEATURE, + MZ_ZIP_FAILED_FINDING_CENTRAL_DIR, + MZ_ZIP_NOT_AN_ARCHIVE, + MZ_ZIP_INVALID_HEADER_OR_CORRUPTED, + MZ_ZIP_UNSUPPORTED_MULTIDISK, + MZ_ZIP_DECOMPRESSION_FAILED, + MZ_ZIP_COMPRESSION_FAILED, + MZ_ZIP_UNEXPECTED_DECOMPRESSED_SIZE, + MZ_ZIP_CRC_CHECK_FAILED, + MZ_ZIP_UNSUPPORTED_CDIR_SIZE, + MZ_ZIP_ALLOC_FAILED, + MZ_ZIP_FILE_OPEN_FAILED, + MZ_ZIP_FILE_CREATE_FAILED, + MZ_ZIP_FILE_WRITE_FAILED, + MZ_ZIP_FILE_READ_FAILED, + MZ_ZIP_FILE_CLOSE_FAILED, + MZ_ZIP_FILE_SEEK_FAILED, + MZ_ZIP_FILE_STAT_FAILED, + MZ_ZIP_INVALID_PARAMETER, + MZ_ZIP_INVALID_FILENAME, + MZ_ZIP_BUF_TOO_SMALL, + MZ_ZIP_INTERNAL_ERROR, + MZ_ZIP_FILE_NOT_FOUND, + MZ_ZIP_ARCHIVE_TOO_LARGE, + MZ_ZIP_VALIDATION_FAILED, + MZ_ZIP_WRITE_CALLBACK_FAILED, + MZ_ZIP_TOTAL_ERRORS +} mz_zip_error; + +// Method +#define MZ_DEFLATED 8 + +#ifndef MINIZ_NO_ZLIB_APIS + +// Heap allocation callbacks. +// Note that mz_alloc_func parameter types purpsosely differ from zlib's: +// items/size is size_t, not unsigned long. +typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size); +typedef void (*mz_free_func)(void *opaque, void *address); +typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, + size_t size); + +#define MZ_VERSION "9.1.15" +#define MZ_VERNUM 0x91F0 +#define MZ_VER_MAJOR 9 +#define MZ_VER_MINOR 1 +#define MZ_VER_REVISION 15 +#define MZ_VER_SUBREVISION 0 + +// Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The +// other values are for advanced use (refer to the zlib docs). +enum { + MZ_NO_FLUSH = 0, + MZ_PARTIAL_FLUSH = 1, + MZ_SYNC_FLUSH = 2, + MZ_FULL_FLUSH = 3, + MZ_FINISH = 4, + MZ_BLOCK = 5 +}; + +// Return status codes. MZ_PARAM_ERROR is non-standard. +enum { + MZ_OK = 0, + MZ_STREAM_END = 1, + MZ_NEED_DICT = 2, + MZ_ERRNO = -1, + MZ_STREAM_ERROR = -2, + MZ_DATA_ERROR = -3, + MZ_MEM_ERROR = -4, + MZ_BUF_ERROR = -5, + MZ_VERSION_ERROR = -6, + MZ_PARAM_ERROR = -10000 +}; + +// Compression levels: 0-9 are the standard zlib-style levels, 10 is best +// possible compression (not zlib compatible, and may be very slow), +// MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL. +enum { + MZ_NO_COMPRESSION = 0, + MZ_BEST_SPEED = 1, + MZ_BEST_COMPRESSION = 9, + MZ_UBER_COMPRESSION = 10, + MZ_DEFAULT_LEVEL = 6, + MZ_DEFAULT_COMPRESSION = -1 +}; + +// Window bits +#define MZ_DEFAULT_WINDOW_BITS 15 + +struct mz_internal_state; + +// Compression/decompression stream struct. +typedef struct mz_stream_s { + const unsigned char *next_in; // pointer to next byte to read + unsigned int avail_in; // number of bytes available at next_in + mz_ulong total_in; // total number of bytes consumed so far + + unsigned char *next_out; // pointer to next byte to write + unsigned int avail_out; // number of bytes that can be written to next_out + mz_ulong total_out; // total number of bytes produced so far + + char *msg; // error msg (unused) + struct mz_internal_state *state; // internal state, allocated by zalloc/zfree + + mz_alloc_func + zalloc; // optional heap allocation function (defaults to malloc) + mz_free_func zfree; // optional heap free function (defaults to free) + void *opaque; // heap alloc function user pointer + + int data_type; // data_type (unused) + mz_ulong adler; // adler32 of the source or uncompressed data + mz_ulong reserved; // not used +} mz_stream; + +typedef mz_stream *mz_streamp; + +// Returns the version string of miniz.c. +const char *mz_version(void); + +// mz_deflateInit() initializes a compressor with default options: +// Parameters: +// pStream must point to an initialized mz_stream struct. +// level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. +// level 1 enables a specially optimized compression function that's been +// optimized purely for performance, not ratio. (This special func. is +// currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and +// MINIZ_LITTLE_ENDIAN are defined.) +// Return values: +// MZ_OK on success. +// MZ_STREAM_ERROR if the stream is bogus. +// MZ_PARAM_ERROR if the input parameters are bogus. +// MZ_MEM_ERROR on out of memory. +int mz_deflateInit(mz_streamp pStream, int level); + +// mz_deflateInit2() is like mz_deflate(), except with more control: +// Additional parameters: +// method must be MZ_DEFLATED +// window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with +// zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no +// header or footer) mem_level must be between [1, 9] (it's checked but +// ignored by miniz.c) +int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, + int mem_level, int strategy); + +// Quickly resets a compressor without having to reallocate anything. Same as +// calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). +int mz_deflateReset(mz_streamp pStream); + +// mz_deflate() compresses the input to output, consuming as much of the input +// and producing as much output as possible. Parameters: +// pStream is the stream to read from and write to. You must initialize/update +// the next_in, avail_in, next_out, and avail_out members. flush may be +// MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH. +// Return values: +// MZ_OK on success (when flushing, or if more input is needed but not +// available, and/or there's more output to be written but the output buffer +// is full). MZ_STREAM_END if all input has been consumed and all output bytes +// have been written. Don't call mz_deflate() on the stream anymore. +// MZ_STREAM_ERROR if the stream is bogus. +// MZ_PARAM_ERROR if one of the parameters is invalid. +// MZ_BUF_ERROR if no forward progress is possible because the input and/or +// output buffers are empty. (Fill up the input buffer or free up some output +// space and try again.) +int mz_deflate(mz_streamp pStream, int flush); + +// mz_deflateEnd() deinitializes a compressor: +// Return values: +// MZ_OK on success. +// MZ_STREAM_ERROR if the stream is bogus. +int mz_deflateEnd(mz_streamp pStream); + +// mz_deflateBound() returns a (very) conservative upper bound on the amount of +// data that could be generated by deflate(), assuming flush is set to only +// MZ_NO_FLUSH or MZ_FINISH. +mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); + +// Single-call compression functions mz_compress() and mz_compress2(): +// Returns MZ_OK on success, or one of the error codes from mz_deflate() on +// failure. +int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, + const unsigned char *pSource, mz_ulong source_len); +int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, + const unsigned char *pSource, mz_ulong source_len, int level); + +// mz_compressBound() returns a (very) conservative upper bound on the amount of +// data that could be generated by calling mz_compress(). +mz_ulong mz_compressBound(mz_ulong source_len); + +// Initializes a decompressor. +int mz_inflateInit(mz_streamp pStream); + +// mz_inflateInit2() is like mz_inflateInit() with an additional option that +// controls the window size and whether or not the stream has been wrapped with +// a zlib header/footer: window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse +// zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). +int mz_inflateInit2(mz_streamp pStream, int window_bits); + +// Decompresses the input stream to the output, consuming only as much of the +// input as needed, and writing as much to the output as possible. Parameters: +// pStream is the stream to read from and write to. You must initialize/update +// the next_in, avail_in, next_out, and avail_out members. flush may be +// MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. On the first call, if flush is +// MZ_FINISH it's assumed the input and output buffers are both sized large +// enough to decompress the entire stream in a single call (this is slightly +// faster). MZ_FINISH implies that there are no more source bytes available +// beside what's already in the input buffer, and that the output buffer is +// large enough to hold the rest of the decompressed data. +// Return values: +// MZ_OK on success. Either more input is needed but not available, and/or +// there's more output to be written but the output buffer is full. +// MZ_STREAM_END if all needed input has been consumed and all output bytes +// have been written. For zlib streams, the adler-32 of the decompressed data +// has also been verified. MZ_STREAM_ERROR if the stream is bogus. +// MZ_DATA_ERROR if the deflate stream is invalid. +// MZ_PARAM_ERROR if one of the parameters is invalid. +// MZ_BUF_ERROR if no forward progress is possible because the input buffer is +// empty but the inflater needs more input to continue, or if the output +// buffer is not large enough. Call mz_inflate() again with more input data, +// or with more room in the output buffer (except when using single call +// decompression, described above). +int mz_inflate(mz_streamp pStream, int flush); + +// Deinitializes a decompressor. +int mz_inflateEnd(mz_streamp pStream); + +// Single-call decompression. +// Returns MZ_OK on success, or one of the error codes from mz_inflate() on +// failure. +int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, + const unsigned char *pSource, mz_ulong source_len); + +// Returns a string description of the specified error code, or NULL if the +// error code is invalid. +const char *mz_error(int err); + +// Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used +// as a drop-in replacement for the subset of zlib that miniz.c supports. Define +// MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib +// in the same project. +#ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES +typedef unsigned char Byte; +typedef unsigned int uInt; +typedef mz_ulong uLong; +typedef Byte Bytef; +typedef uInt uIntf; +typedef char charf; +typedef int intf; +typedef void *voidpf; +typedef uLong uLongf; +typedef void *voidp; +typedef void *const voidpc; +#define Z_NULL 0 +#define Z_NO_FLUSH MZ_NO_FLUSH +#define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH +#define Z_SYNC_FLUSH MZ_SYNC_FLUSH +#define Z_FULL_FLUSH MZ_FULL_FLUSH +#define Z_FINISH MZ_FINISH +#define Z_BLOCK MZ_BLOCK +#define Z_OK MZ_OK +#define Z_STREAM_END MZ_STREAM_END +#define Z_NEED_DICT MZ_NEED_DICT +#define Z_ERRNO MZ_ERRNO +#define Z_STREAM_ERROR MZ_STREAM_ERROR +#define Z_DATA_ERROR MZ_DATA_ERROR +#define Z_MEM_ERROR MZ_MEM_ERROR +#define Z_BUF_ERROR MZ_BUF_ERROR +#define Z_VERSION_ERROR MZ_VERSION_ERROR +#define Z_PARAM_ERROR MZ_PARAM_ERROR +#define Z_NO_COMPRESSION MZ_NO_COMPRESSION +#define Z_BEST_SPEED MZ_BEST_SPEED +#define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION +#define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION +#define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY +#define Z_FILTERED MZ_FILTERED +#define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY +#define Z_RLE MZ_RLE +#define Z_FIXED MZ_FIXED +#define Z_DEFLATED MZ_DEFLATED +#define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS +#define alloc_func mz_alloc_func +#define free_func mz_free_func +#define internal_state mz_internal_state +#define z_stream mz_stream +#define deflateInit mz_deflateInit +#define deflateInit2 mz_deflateInit2 +#define deflateReset mz_deflateReset +#define deflate mz_deflate +#define deflateEnd mz_deflateEnd +#define deflateBound mz_deflateBound +#define compress mz_compress +#define compress2 mz_compress2 +#define compressBound mz_compressBound +#define inflateInit mz_inflateInit +#define inflateInit2 mz_inflateInit2 +#define inflate mz_inflate +#define inflateEnd mz_inflateEnd +#define uncompress mz_uncompress +#define crc32 mz_crc32 +#define adler32 mz_adler32 +#define MAX_WBITS 15 +#define MAX_MEM_LEVEL 9 +#define zError mz_error +#define ZLIB_VERSION MZ_VERSION +#define ZLIB_VERNUM MZ_VERNUM +#define ZLIB_VER_MAJOR MZ_VER_MAJOR +#define ZLIB_VER_MINOR MZ_VER_MINOR +#define ZLIB_VER_REVISION MZ_VER_REVISION +#define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION +#define zlibVersion mz_version +#define zlib_version mz_version() +#endif // #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES + +#endif // MINIZ_NO_ZLIB_APIS + +// ------------------- Types and macros + +typedef unsigned char mz_uint8; +typedef signed short mz_int16; +typedef unsigned short mz_uint16; +typedef unsigned int mz_uint32; +typedef unsigned int mz_uint; +typedef long long mz_int64; +typedef unsigned long long mz_uint64; +typedef int mz_bool; + +#define MZ_FALSE (0) +#define MZ_TRUE (1) + +// An attempt to work around MSVC's spammy "warning C4127: conditional +// expression is constant" message. +#ifdef _MSC_VER +#define MZ_MACRO_END while (0, 0) +#else +#define MZ_MACRO_END while (0) +#endif + +// ------------------- ZIP archive reading/writing + +#ifndef MINIZ_NO_ARCHIVE_APIS + +enum { + MZ_ZIP_MAX_IO_BUF_SIZE = 64 * 1024, + MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 260, + MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 256 +}; + +typedef struct { + mz_uint32 m_file_index; + mz_uint32 m_central_dir_ofs; + mz_uint16 m_version_made_by; + mz_uint16 m_version_needed; + mz_uint16 m_bit_flag; + mz_uint16 m_method; +#ifndef MINIZ_NO_TIME + time_t m_time; +#endif + mz_uint32 m_crc32; + mz_uint64 m_comp_size; + mz_uint64 m_uncomp_size; + mz_uint16 m_internal_attr; + mz_uint32 m_external_attr; + mz_uint64 m_local_header_ofs; + mz_uint32 m_comment_size; + char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; + char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; +} mz_zip_archive_file_stat; + +typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, + void *pBuf, size_t n); +typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, + const void *pBuf, size_t n); +typedef mz_bool (*mz_file_needs_keepalive)(void *pOpaque); + +struct mz_zip_internal_state_tag; +typedef struct mz_zip_internal_state_tag mz_zip_internal_state; + +typedef enum { + MZ_ZIP_MODE_INVALID = 0, + MZ_ZIP_MODE_READING = 1, + MZ_ZIP_MODE_WRITING = 2, + MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 +} mz_zip_mode; + +typedef enum { + MZ_ZIP_TYPE_INVALID = 0, + MZ_ZIP_TYPE_USER, + MZ_ZIP_TYPE_MEMORY, + MZ_ZIP_TYPE_HEAP, + MZ_ZIP_TYPE_FILE, + MZ_ZIP_TYPE_CFILE, + MZ_ZIP_TOTAL_TYPES +} mz_zip_type; + +typedef struct { + mz_uint64 m_archive_size; + mz_uint64 m_central_directory_file_ofs; + + /* We only support up to UINT32_MAX files in zip64 mode. */ + mz_uint32 m_total_files; + mz_zip_mode m_zip_mode; + mz_zip_type m_zip_type; + mz_zip_error m_last_error; + + mz_uint64 m_file_offset_alignment; + + mz_alloc_func m_pAlloc; + mz_free_func m_pFree; + mz_realloc_func m_pRealloc; + void *m_pAlloc_opaque; + + mz_file_read_func m_pRead; + mz_file_write_func m_pWrite; + mz_file_needs_keepalive m_pNeeds_keepalive; + void *m_pIO_opaque; + + mz_zip_internal_state *m_pState; + +} mz_zip_archive; + +typedef enum { + MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, + MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, + MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, + MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800 +} mz_zip_flags; + +// ZIP archive reading + +// Inits a ZIP archive reader. +// These functions read and validate the archive's central directory. +mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, + mz_uint32 flags); +mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, + size_t size, mz_uint32 flags); + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, + mz_uint32 flags); +#endif + +// Returns the total number of files in the archive. +mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); + +// Returns detailed information about an archive file entry. +mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, + mz_zip_archive_file_stat *pStat); + +// Determines if an archive file entry is a directory entry. +mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, + mz_uint file_index); +mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, + mz_uint file_index); + +// Retrieves the filename of an archive file entry. +// Returns the number of bytes written to pFilename, or if filename_buf_size is +// 0 this function returns the number of bytes needed to fully store the +// filename. +mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, + char *pFilename, mz_uint filename_buf_size); + +// Attempts to locates a file in the archive's central directory. +// Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH +// Returns -1 if the file cannot be found. +int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, + const char *pComment, mz_uint flags); + +// Extracts a archive file to a memory buffer using no memory allocation. +mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, + mz_uint file_index, void *pBuf, + size_t buf_size, mz_uint flags, + void *pUser_read_buf, + size_t user_read_buf_size); +mz_bool mz_zip_reader_extract_file_to_mem_no_alloc( + mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, + mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); + +// Extracts a archive file to a memory buffer. +mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, + void *pBuf, size_t buf_size, + mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, + const char *pFilename, void *pBuf, + size_t buf_size, mz_uint flags); + +// Extracts a archive file to a dynamically allocated heap buffer. +void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, + size_t *pSize, mz_uint flags); +void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, + const char *pFilename, size_t *pSize, + mz_uint flags); + +// Extracts a archive file using a callback function to output the file's data. +mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, + mz_uint file_index, + mz_file_write_func pCallback, + void *pOpaque, mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, + const char *pFilename, + mz_file_write_func pCallback, + void *pOpaque, mz_uint flags); + +#ifndef MINIZ_NO_STDIO +// Extracts a archive file to a disk file and sets its last accessed and +// modified times. This function only extracts files, not archive directory +// records. +mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, + const char *pDst_filename, mz_uint flags); +mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, + const char *pArchive_filename, + const char *pDst_filename, + mz_uint flags); +#endif + +// Ends archive reading, freeing all allocations, and closing the input archive +// file if mz_zip_reader_init_file() was used. +mz_bool mz_zip_reader_end(mz_zip_archive *pZip); + +// ZIP archive writing + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +// Inits a ZIP archive writer. +mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); +mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, + size_t size_to_reserve_at_beginning, + size_t initial_allocation_size); + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, + mz_uint64 size_to_reserve_at_beginning); +#endif + +// Converts a ZIP archive reader object into a writer object, to allow efficient +// in-place file appends to occur on an existing archive. For archives opened +// using mz_zip_reader_init_file, pFilename must be the archive's filename so it +// can be reopened for writing. If the file can't be reopened, +// mz_zip_reader_end() will be called. For archives opened using +// mz_zip_reader_init_mem, the memory block must be growable using the realloc +// callback (which defaults to realloc unless you've overridden it). Finally, +// for archives opened using mz_zip_reader_init, the mz_zip_archive's user +// provided m_pWrite function cannot be NULL. Note: In-place archive +// modification is not recommended unless you know what you're doing, because if +// execution stops or something goes wrong before the archive is finalized the +// file's central directory will be hosed. +mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, + const char *pFilename); + +// Adds the contents of a memory buffer to an archive. These functions record +// the current local time into the archive. To add a directory entry, call this +// method with an archive name ending in a forwardslash with empty buffer. +// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, +// MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or +// just set to MZ_DEFAULT_COMPRESSION. +mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, + const void *pBuf, size_t buf_size, + mz_uint level_and_flags); +mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, + const char *pArchive_name, const void *pBuf, + size_t buf_size, const void *pComment, + mz_uint16 comment_size, + mz_uint level_and_flags, mz_uint64 uncomp_size, + mz_uint32 uncomp_crc32); + +#ifndef MINIZ_NO_STDIO +// Adds the contents of a disk file to an archive. This function also records +// the disk file's modified time into the archive. level_and_flags - compression +// level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd +// with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. +mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, + const char *pSrc_filename, const void *pComment, + mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint32 ext_attributes); +#endif + +// Adds a file to an archive by fully cloning the data from another archive. +// This function fully clones the source file's compressed data (no +// recompression), along with its full filename, extra data, and comment fields. +mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, + mz_zip_archive *pSource_zip, + mz_uint file_index); + +// Finalizes the archive by writing the central directory records followed by +// the end of central directory record. After an archive is finalized, the only +// valid call on the mz_zip_archive struct is mz_zip_writer_end(). An archive +// must be manually finalized by calling this function for it to be valid. +mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); +mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, + size_t *pSize); + +// Ends archive writing, freeing all allocations, and closing the output file if +// mz_zip_writer_init_file() was used. Note for the archive to be valid, it must +// have been finalized before ending. +mz_bool mz_zip_writer_end(mz_zip_archive *pZip); + +// Misc. high-level helper functions: + +// mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) +// appends a memory blob to a ZIP archive. level_and_flags - compression level +// (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero +// or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. +mz_bool mz_zip_add_mem_to_archive_file_in_place( + const char *pZip_filename, const char *pArchive_name, const void *pBuf, + size_t buf_size, const void *pComment, mz_uint16 comment_size, + mz_uint level_and_flags); + +// Reads a single file from an archive into a heap block. +// Returns NULL on failure. +void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, + const char *pArchive_name, + size_t *pSize, mz_uint zip_flags); + +#endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +#endif // #ifndef MINIZ_NO_ARCHIVE_APIS + +// ------------------- Low-level Decompression API Definitions + +// Decompression flags used by tinfl_decompress(). +// TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and +// ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the +// input is a raw deflate stream. TINFL_FLAG_HAS_MORE_INPUT: If set, there are +// more input bytes available beyond the end of the supplied input buffer. If +// clear, the input buffer contains all remaining input. +// TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large +// enough to hold the entire decompressed stream. If clear, the output buffer is +// at least the size of the dictionary (typically 32KB). +// TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the +// decompressed bytes. +enum { + TINFL_FLAG_PARSE_ZLIB_HEADER = 1, + TINFL_FLAG_HAS_MORE_INPUT = 2, + TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, + TINFL_FLAG_COMPUTE_ADLER32 = 8 +}; + +// High level decompression functions: +// tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block +// allocated via malloc(). On entry: +// pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data +// to decompress. +// On return: +// Function returns a pointer to the decompressed data, or NULL on failure. +// *pOut_len will be set to the decompressed data's size, which could be larger +// than src_buf_len on uncompressible data. The caller must call mz_free() on +// the returned block when it's no longer needed. +void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, + size_t *pOut_len, int flags); + +// tinfl_decompress_mem_to_mem() decompresses a block in memory to another block +// in memory. Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the +// number of bytes written on success. +#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) +size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, + const void *pSrc_buf, size_t src_buf_len, + int flags); + +// tinfl_decompress_mem_to_callback() decompresses a block in memory to an +// internal 32KB buffer, and a user provided callback function will be called to +// flush the buffer. Returns 1 on success or 0 on failure. +typedef int (*tinfl_put_buf_func_ptr)(const void *pBuf, int len, void *pUser); +int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, + tinfl_put_buf_func_ptr pPut_buf_func, + void *pPut_buf_user, int flags); + +struct tinfl_decompressor_tag; +typedef struct tinfl_decompressor_tag tinfl_decompressor; + +// Max size of LZ dictionary. +#define TINFL_LZ_DICT_SIZE 32768 + +// Return status. +typedef enum { + TINFL_STATUS_BAD_PARAM = -3, + TINFL_STATUS_ADLER32_MISMATCH = -2, + TINFL_STATUS_FAILED = -1, + TINFL_STATUS_DONE = 0, + TINFL_STATUS_NEEDS_MORE_INPUT = 1, + TINFL_STATUS_HAS_MORE_OUTPUT = 2 +} tinfl_status; + +// Initializes the decompressor to its initial state. +#define tinfl_init(r) \ + do { \ + (r)->m_state = 0; \ + } \ + MZ_MACRO_END +#define tinfl_get_adler32(r) (r)->m_check_adler32 + +// Main low-level decompressor coroutine function. This is the only function +// actually needed for decompression. All the other functions are just +// high-level helpers for improved usability. This is a universal API, i.e. it +// can be used as a building block to build any desired higher level +// decompression API. In the limit case, it can be called once per every byte +// input or output. +tinfl_status tinfl_decompress(tinfl_decompressor *r, + const mz_uint8 *pIn_buf_next, + size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, + mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, + const mz_uint32 decomp_flags); + +// Internal/private bits follow. +enum { + TINFL_MAX_HUFF_TABLES = 3, + TINFL_MAX_HUFF_SYMBOLS_0 = 288, + TINFL_MAX_HUFF_SYMBOLS_1 = 32, + TINFL_MAX_HUFF_SYMBOLS_2 = 19, + TINFL_FAST_LOOKUP_BITS = 10, + TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS +}; + +typedef struct { + mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0]; + mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], + m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; +} tinfl_huff_table; + +#if MINIZ_HAS_64BIT_REGISTERS +#define TINFL_USE_64BIT_BITBUF 1 +#endif + +#if TINFL_USE_64BIT_BITBUF +typedef mz_uint64 tinfl_bit_buf_t; +#define TINFL_BITBUF_SIZE (64) +#else +typedef mz_uint32 tinfl_bit_buf_t; +#define TINFL_BITBUF_SIZE (32) +#endif + +struct tinfl_decompressor_tag { + mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, + m_check_adler32, m_dist, m_counter, m_num_extra, + m_table_sizes[TINFL_MAX_HUFF_TABLES]; + tinfl_bit_buf_t m_bit_buf; + size_t m_dist_from_out_buf_start; + tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES]; + mz_uint8 m_raw_header[4], + m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; +}; + +// ------------------- Low-level Compression API Definitions + +// Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly +// slower, and raw/dynamic blocks will be output more frequently). +#define TDEFL_LESS_MEMORY 0 + +// tdefl_init() compression flags logically OR'd together (low 12 bits contain +// the max. number of probes per dictionary search): TDEFL_DEFAULT_MAX_PROBES: +// The compressor defaults to 128 dictionary probes per dictionary search. +// 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ +// (slowest/best compression). +enum { + TDEFL_HUFFMAN_ONLY = 0, + TDEFL_DEFAULT_MAX_PROBES = 128, + TDEFL_MAX_PROBES_MASK = 0xFFF +}; + +// TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before +// the deflate data, and the Adler-32 of the source data at the end. Otherwise, +// you'll get raw deflate data. TDEFL_COMPUTE_ADLER32: Always compute the +// adler-32 of the input data (even when not writing zlib headers). +// TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more +// efficient lazy parsing. TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to +// decrease the compressor's initialization time to the minimum, but the output +// may vary from run to run given the same input (depending on the contents of +// memory). TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a +// distance of 1) TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. +// TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. +// TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. +// The low 12 bits are reserved to control the max # of hash probes per +// dictionary lookup (see TDEFL_MAX_PROBES_MASK). +enum { + TDEFL_WRITE_ZLIB_HEADER = 0x01000, + TDEFL_COMPUTE_ADLER32 = 0x02000, + TDEFL_GREEDY_PARSING_FLAG = 0x04000, + TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000, + TDEFL_RLE_MATCHES = 0x10000, + TDEFL_FILTER_MATCHES = 0x20000, + TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000, + TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000 +}; + +// High level compression functions: +// tdefl_compress_mem_to_heap() compresses a block in memory to a heap block +// allocated via malloc(). On entry: +// pSrc_buf, src_buf_len: Pointer and size of source block to compress. +// flags: The max match finder probes (default is 128) logically OR'd against +// the above flags. Higher probes are slower but improve compression. +// On return: +// Function returns a pointer to the compressed data, or NULL on failure. +// *pOut_len will be set to the compressed data's size, which could be larger +// than src_buf_len on uncompressible data. The caller must free() the returned +// block when it's no longer needed. +void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, + size_t *pOut_len, int flags); + +// tdefl_compress_mem_to_mem() compresses a block in memory to another block in +// memory. Returns 0 on failure. +size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, + const void *pSrc_buf, size_t src_buf_len, + int flags); + +// Compresses an image to a compressed PNG file in memory. +// On entry: +// pImage, w, h, and num_chans describe the image to compress. num_chans may be +// 1, 2, 3, or 4. The image pitch in bytes per scanline will be w*num_chans. +// The leftmost pixel on the top scanline is stored first in memory. level may +// range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, +// MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL If flip is +// true, the image will be flipped on the Y axis (useful for OpenGL apps). +// On return: +// Function returns a pointer to the compressed data, or NULL on failure. +// *pLen_out will be set to the size of the PNG image file. +// The caller must mz_free() the returned heap block (which will typically be +// larger than *pLen_out) when it's no longer needed. +void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, + int h, int num_chans, + size_t *pLen_out, + mz_uint level, mz_bool flip); +void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, + int num_chans, size_t *pLen_out); + +// Output stream interface. The compressor uses this interface to write +// compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. +typedef mz_bool (*tdefl_put_buf_func_ptr)(const void *pBuf, int len, + void *pUser); + +// tdefl_compress_mem_to_output() compresses a block to an output stream. The +// above helpers use this function internally. +mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, + tdefl_put_buf_func_ptr pPut_buf_func, + void *pPut_buf_user, int flags); + +enum { + TDEFL_MAX_HUFF_TABLES = 3, + TDEFL_MAX_HUFF_SYMBOLS_0 = 288, + TDEFL_MAX_HUFF_SYMBOLS_1 = 32, + TDEFL_MAX_HUFF_SYMBOLS_2 = 19, + TDEFL_LZ_DICT_SIZE = 32768, + TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, + TDEFL_MIN_MATCH_LEN = 3, + TDEFL_MAX_MATCH_LEN = 258 +}; + +// TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed +// output block (using static/fixed Huffman codes). +#if TDEFL_LESS_MEMORY +enum { + TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, + TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, + TDEFL_MAX_HUFF_SYMBOLS = 288, + TDEFL_LZ_HASH_BITS = 12, + TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, + TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, + TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS +}; +#else +enum { + TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, + TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13) / 10, + TDEFL_MAX_HUFF_SYMBOLS = 288, + TDEFL_LZ_HASH_BITS = 15, + TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, + TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, + TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS +}; +#endif + +// The low-level tdefl functions below may be used directly if the above helper +// functions aren't flexible enough. The low-level functions don't make any heap +// allocations, unlike the above helper functions. +typedef enum { + TDEFL_STATUS_BAD_PARAM = -2, + TDEFL_STATUS_PUT_BUF_FAILED = -1, + TDEFL_STATUS_OKAY = 0, + TDEFL_STATUS_DONE = 1, +} tdefl_status; + +// Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums +typedef enum { + TDEFL_NO_FLUSH = 0, + TDEFL_SYNC_FLUSH = 2, + TDEFL_FULL_FLUSH = 3, + TDEFL_FINISH = 4 +} tdefl_flush; + +// tdefl's compression state structure. +typedef struct { + tdefl_put_buf_func_ptr m_pPut_buf_func; + void *m_pPut_buf_user; + mz_uint m_flags, m_max_probes[2]; + int m_greedy_parsing; + mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size; + mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end; + mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, + m_bit_buffer; + mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, + m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, + m_wants_to_finish; + tdefl_status m_prev_return_status; + const void *m_pIn_buf; + void *m_pOut_buf; + size_t *m_pIn_buf_size, *m_pOut_buf_size; + tdefl_flush m_flush; + const mz_uint8 *m_pSrc; + size_t m_src_buf_left, m_out_buf_ofs; + mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1]; + mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; + mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE]; + mz_uint16 m_next[TDEFL_LZ_DICT_SIZE]; + mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE]; + mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE]; +} tdefl_compressor; + +// Initializes the compressor. +// There is no corresponding deinit() function because the tdefl API's do not +// dynamically allocate memory. pBut_buf_func: If NULL, output data will be +// supplied to the specified callback. In this case, the user should call the +// tdefl_compress_buffer() API for compression. If pBut_buf_func is NULL the +// user should always call the tdefl_compress() API. flags: See the above enums +// (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.) +tdefl_status tdefl_init(tdefl_compressor *d, + tdefl_put_buf_func_ptr pPut_buf_func, + void *pPut_buf_user, int flags); + +// Compresses a block of data, consuming as much of the specified input buffer +// as possible, and writing as much compressed data to the specified output +// buffer as possible. +tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, + size_t *pIn_buf_size, void *pOut_buf, + size_t *pOut_buf_size, tdefl_flush flush); + +// tdefl_compress_buffer() is only usable when the tdefl_init() is called with a +// non-NULL tdefl_put_buf_func_ptr. tdefl_compress_buffer() always consumes the +// entire input buffer. +tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, + size_t in_buf_size, tdefl_flush flush); + +tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); +mz_uint32 tdefl_get_adler32(tdefl_compressor *d); + +// Can't use tdefl_create_comp_flags_from_zip_params if MINIZ_NO_ZLIB_APIS isn't +// defined, because it uses some of its macros. +#ifndef MINIZ_NO_ZLIB_APIS +// Create tdefl_compress() flags given zlib-style compression parameters. +// level may range from [0,10] (where 10 is absolute max compression, but may be +// much slower on some files) window_bits may be -15 (raw deflate) or 15 (zlib) +// strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, +// MZ_RLE, or MZ_FIXED +mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, + int strategy); +#endif // #ifndef MINIZ_NO_ZLIB_APIS + +#define MZ_UINT16_MAX (0xFFFFU) +#define MZ_UINT32_MAX (0xFFFFFFFFU) + +#ifdef __cplusplus +} +#endif + +#endif // MINIZ_HEADER_INCLUDED + +// ------------------- End of Header: Implementation follows. (If you only want +// the header, define MINIZ_HEADER_FILE_ONLY.) + +#ifndef MINIZ_HEADER_FILE_ONLY + +typedef unsigned char mz_validate_uint16[sizeof(mz_uint16) == 2 ? 1 : -1]; +typedef unsigned char mz_validate_uint32[sizeof(mz_uint32) == 4 ? 1 : -1]; +typedef unsigned char mz_validate_uint64[sizeof(mz_uint64) == 8 ? 1 : -1]; + +#include +#include + +#define MZ_ASSERT(x) assert(x) + +#ifdef MINIZ_NO_MALLOC +#define MZ_MALLOC(x) NULL +#define MZ_FREE(x) (void)x, ((void)0) +#define MZ_REALLOC(p, x) NULL +#else +#define MZ_MALLOC(x) malloc(x) +#define MZ_FREE(x) free(x) +#define MZ_REALLOC(p, x) realloc(p, x) +#endif + +#define MZ_MAX(a, b) (((a) > (b)) ? (a) : (b)) +#define MZ_MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN +#define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) +#define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) +#else +#define MZ_READ_LE16(p) \ + ((mz_uint32)(((const mz_uint8 *)(p))[0]) | \ + ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) +#define MZ_READ_LE32(p) \ + ((mz_uint32)(((const mz_uint8 *)(p))[0]) | \ + ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | \ + ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | \ + ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) +#endif + +#define MZ_READ_LE64(p) \ + (((mz_uint64)MZ_READ_LE32(p)) | \ + (((mz_uint64)MZ_READ_LE32((const mz_uint8 *)(p) + sizeof(mz_uint32))) \ + << 32U)) + +#ifdef _MSC_VER +#define MZ_FORCEINLINE __forceinline +#elif defined(__GNUC__) +#define MZ_FORCEINLINE inline __attribute__((__always_inline__)) +#else +#define MZ_FORCEINLINE inline +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// ------------------- zlib-style API's + +mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len) { + mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); + size_t block_len = buf_len % 5552; + if (!ptr) + return MZ_ADLER32_INIT; + while (buf_len) { + for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { + s1 += ptr[0], s2 += s1; + s1 += ptr[1], s2 += s1; + s1 += ptr[2], s2 += s1; + s1 += ptr[3], s2 += s1; + s1 += ptr[4], s2 += s1; + s1 += ptr[5], s2 += s1; + s1 += ptr[6], s2 += s1; + s1 += ptr[7], s2 += s1; + } + for (; i < block_len; ++i) + s1 += *ptr++, s2 += s1; + s1 %= 65521U, s2 %= 65521U; + buf_len -= block_len; + block_len = 5552; + } + return (s2 << 16) + s1; +} + +// Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C +// implementation that balances processor cache usage against speed": +// http://www.geocities.com/malbrain/ +mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) { + static const mz_uint32 s_crc32[16] = { + 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, + 0x4db26158, 0x5005713c, 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, + 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c}; + mz_uint32 crcu32 = (mz_uint32)crc; + if (!ptr) + return MZ_CRC32_INIT; + crcu32 = ~crcu32; + while (buf_len--) { + mz_uint8 b = *ptr++; + crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; + crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; + } + return ~crcu32; +} + +void mz_free(void *p) { MZ_FREE(p); } + +#ifndef MINIZ_NO_ZLIB_APIS + +static void *def_alloc_func(void *opaque, size_t items, size_t size) { + (void)opaque, (void)items, (void)size; + return MZ_MALLOC(items * size); +} +static void def_free_func(void *opaque, void *address) { + (void)opaque, (void)address; + MZ_FREE(address); +} +static void *def_realloc_func(void *opaque, void *address, size_t items, + size_t size) { + (void)opaque, (void)address, (void)items, (void)size; + return MZ_REALLOC(address, items * size); +} + +const char *mz_version(void) { return MZ_VERSION; } + +int mz_deflateInit(mz_streamp pStream, int level) { + return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, + MZ_DEFAULT_STRATEGY); +} + +int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, + int mem_level, int strategy) { + tdefl_compressor *pComp; + mz_uint comp_flags = + TDEFL_COMPUTE_ADLER32 | + tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy); + + if (!pStream) + return MZ_STREAM_ERROR; + if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || + ((window_bits != MZ_DEFAULT_WINDOW_BITS) && + (-window_bits != MZ_DEFAULT_WINDOW_BITS))) + return MZ_PARAM_ERROR; + + pStream->data_type = 0; + pStream->adler = MZ_ADLER32_INIT; + pStream->msg = NULL; + pStream->reserved = 0; + pStream->total_in = 0; + pStream->total_out = 0; + if (!pStream->zalloc) + pStream->zalloc = def_alloc_func; + if (!pStream->zfree) + pStream->zfree = def_free_func; + + pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, + sizeof(tdefl_compressor)); + if (!pComp) + return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state *)pComp; + + if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY) { + mz_deflateEnd(pStream); + return MZ_PARAM_ERROR; + } + + return MZ_OK; +} + +int mz_deflateReset(mz_streamp pStream) { + if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || + (!pStream->zfree)) + return MZ_STREAM_ERROR; + pStream->total_in = pStream->total_out = 0; + tdefl_init((tdefl_compressor *)pStream->state, NULL, NULL, + ((tdefl_compressor *)pStream->state)->m_flags); + return MZ_OK; +} + +int mz_deflate(mz_streamp pStream, int flush) { + size_t in_bytes, out_bytes; + mz_ulong orig_total_in, orig_total_out; + int mz_status = MZ_OK; + + if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || + (!pStream->next_out)) + return MZ_STREAM_ERROR; + if (!pStream->avail_out) + return MZ_BUF_ERROR; + + if (flush == MZ_PARTIAL_FLUSH) + flush = MZ_SYNC_FLUSH; + + if (((tdefl_compressor *)pStream->state)->m_prev_return_status == + TDEFL_STATUS_DONE) + return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR; + + orig_total_in = pStream->total_in; + orig_total_out = pStream->total_out; + for (;;) { + tdefl_status defl_status; + in_bytes = pStream->avail_in; + out_bytes = pStream->avail_out; + + defl_status = tdefl_compress((tdefl_compressor *)pStream->state, + pStream->next_in, &in_bytes, pStream->next_out, + &out_bytes, (tdefl_flush)flush); + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tdefl_get_adler32((tdefl_compressor *)pStream->state); + + pStream->next_out += (mz_uint)out_bytes; + pStream->avail_out -= (mz_uint)out_bytes; + pStream->total_out += (mz_uint)out_bytes; + + if (defl_status < 0) { + mz_status = MZ_STREAM_ERROR; + break; + } else if (defl_status == TDEFL_STATUS_DONE) { + mz_status = MZ_STREAM_END; + break; + } else if (!pStream->avail_out) + break; + else if ((!pStream->avail_in) && (flush != MZ_FINISH)) { + if ((flush) || (pStream->total_in != orig_total_in) || + (pStream->total_out != orig_total_out)) + break; + return MZ_BUF_ERROR; // Can't make forward progress without some input. + } + } + return mz_status; +} + +int mz_deflateEnd(mz_streamp pStream) { + if (!pStream) + return MZ_STREAM_ERROR; + if (pStream->state) { + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; + } + return MZ_OK; +} + +mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) { + (void)pStream; + // This is really over conservative. (And lame, but it's actually pretty + // tricky to compute a true upper bound given the way tdefl's blocking works.) + return MZ_MAX(128 + (source_len * 110) / 100, + 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5); +} + +int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, + const unsigned char *pSource, mz_ulong source_len, int level) { + int status; + mz_stream stream; + memset(&stream, 0, sizeof(stream)); + + // In case mz_ulong is 64-bits (argh I hate longs). + if ((source_len | *pDest_len) > 0xFFFFFFFFU) + return MZ_PARAM_ERROR; + + stream.next_in = pSource; + stream.avail_in = (mz_uint32)source_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; + + status = mz_deflateInit(&stream, level); + if (status != MZ_OK) + return status; + + status = mz_deflate(&stream, MZ_FINISH); + if (status != MZ_STREAM_END) { + mz_deflateEnd(&stream); + return (status == MZ_OK) ? MZ_BUF_ERROR : status; + } + + *pDest_len = stream.total_out; + return mz_deflateEnd(&stream); +} + +int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, + const unsigned char *pSource, mz_ulong source_len) { + return mz_compress2(pDest, pDest_len, pSource, source_len, + MZ_DEFAULT_COMPRESSION); +} + +mz_ulong mz_compressBound(mz_ulong source_len) { + return mz_deflateBound(NULL, source_len); +} + +typedef struct { + tinfl_decompressor m_decomp; + mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; + int m_window_bits; + mz_uint8 m_dict[TINFL_LZ_DICT_SIZE]; + tinfl_status m_last_status; +} inflate_state; + +int mz_inflateInit2(mz_streamp pStream, int window_bits) { + inflate_state *pDecomp; + if (!pStream) + return MZ_STREAM_ERROR; + if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && + (-window_bits != MZ_DEFAULT_WINDOW_BITS)) + return MZ_PARAM_ERROR; + + pStream->data_type = 0; + pStream->adler = 0; + pStream->msg = NULL; + pStream->total_in = 0; + pStream->total_out = 0; + pStream->reserved = 0; + if (!pStream->zalloc) + pStream->zalloc = def_alloc_func; + if (!pStream->zfree) + pStream->zfree = def_free_func; + + pDecomp = (inflate_state *)pStream->zalloc(pStream->opaque, 1, + sizeof(inflate_state)); + if (!pDecomp) + return MZ_MEM_ERROR; + + pStream->state = (struct mz_internal_state *)pDecomp; + + tinfl_init(&pDecomp->m_decomp); + pDecomp->m_dict_ofs = 0; + pDecomp->m_dict_avail = 0; + pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; + pDecomp->m_first_call = 1; + pDecomp->m_has_flushed = 0; + pDecomp->m_window_bits = window_bits; + + return MZ_OK; +} + +int mz_inflateInit(mz_streamp pStream) { + return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS); +} + +int mz_inflate(mz_streamp pStream, int flush) { + inflate_state *pState; + mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32; + size_t in_bytes, out_bytes, orig_avail_in; + tinfl_status status; + + if ((!pStream) || (!pStream->state)) + return MZ_STREAM_ERROR; + if (flush == MZ_PARTIAL_FLUSH) + flush = MZ_SYNC_FLUSH; + if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) + return MZ_STREAM_ERROR; + + pState = (inflate_state *)pStream->state; + if (pState->m_window_bits > 0) + decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER; + orig_avail_in = pStream->avail_in; + + first_call = pState->m_first_call; + pState->m_first_call = 0; + if (pState->m_last_status < 0) + return MZ_DATA_ERROR; + + if (pState->m_has_flushed && (flush != MZ_FINISH)) + return MZ_STREAM_ERROR; + pState->m_has_flushed |= (flush == MZ_FINISH); + + if ((flush == MZ_FINISH) && (first_call)) { + // MZ_FINISH on the first call implies that the input and output buffers are + // large enough to hold the entire compressed/decompressed file. + decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF; + in_bytes = pStream->avail_in; + out_bytes = pStream->avail_out; + status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, + pStream->next_out, pStream->next_out, &out_bytes, + decomp_flags); + pState->m_last_status = status; + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tinfl_get_adler32(&pState->m_decomp); + pStream->next_out += (mz_uint)out_bytes; + pStream->avail_out -= (mz_uint)out_bytes; + pStream->total_out += (mz_uint)out_bytes; + + if (status < 0) + return MZ_DATA_ERROR; + else if (status != TINFL_STATUS_DONE) { + pState->m_last_status = TINFL_STATUS_FAILED; + return MZ_BUF_ERROR; + } + return MZ_STREAM_END; + } + // flush != MZ_FINISH then we must assume there's more input. + if (flush != MZ_FINISH) + decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT; + + if (pState->m_dict_avail) { + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; + pStream->avail_out -= n; + pStream->total_out += n; + pState->m_dict_avail -= n; + pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + return ((pState->m_last_status == TINFL_STATUS_DONE) && + (!pState->m_dict_avail)) + ? MZ_STREAM_END + : MZ_OK; + } + + for (;;) { + in_bytes = pStream->avail_in; + out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs; + + status = tinfl_decompress( + &pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, + pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags); + pState->m_last_status = status; + + pStream->next_in += (mz_uint)in_bytes; + pStream->avail_in -= (mz_uint)in_bytes; + pStream->total_in += (mz_uint)in_bytes; + pStream->adler = tinfl_get_adler32(&pState->m_decomp); + + pState->m_dict_avail = (mz_uint)out_bytes; + + n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); + memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); + pStream->next_out += n; + pStream->avail_out -= n; + pStream->total_out += n; + pState->m_dict_avail -= n; + pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); + + if (status < 0) + return MZ_DATA_ERROR; // Stream is corrupted (there could be some + // uncompressed data left in the output dictionary - + // oh well). + else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in)) + return MZ_BUF_ERROR; // Signal caller that we can't make forward progress + // without supplying more input or by setting flush + // to MZ_FINISH. + else if (flush == MZ_FINISH) { + // The output buffer MUST be large to hold the remaining uncompressed data + // when flush==MZ_FINISH. + if (status == TINFL_STATUS_DONE) + return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END; + // status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's + // at least 1 more byte on the way. If there's no more room left in the + // output buffer then something is wrong. + else if (!pStream->avail_out) + return MZ_BUF_ERROR; + } else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || + (!pStream->avail_out) || (pState->m_dict_avail)) + break; + } + + return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) + ? MZ_STREAM_END + : MZ_OK; +} + +int mz_inflateEnd(mz_streamp pStream) { + if (!pStream) + return MZ_STREAM_ERROR; + if (pStream->state) { + pStream->zfree(pStream->opaque, pStream->state); + pStream->state = NULL; + } + return MZ_OK; +} + +int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, + const unsigned char *pSource, mz_ulong source_len) { + mz_stream stream; + int status; + memset(&stream, 0, sizeof(stream)); + + // In case mz_ulong is 64-bits (argh I hate longs). + if ((source_len | *pDest_len) > 0xFFFFFFFFU) + return MZ_PARAM_ERROR; + + stream.next_in = pSource; + stream.avail_in = (mz_uint32)source_len; + stream.next_out = pDest; + stream.avail_out = (mz_uint32)*pDest_len; + + status = mz_inflateInit(&stream); + if (status != MZ_OK) + return status; + + status = mz_inflate(&stream, MZ_FINISH); + if (status != MZ_STREAM_END) { + mz_inflateEnd(&stream); + return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR + : status; + } + *pDest_len = stream.total_out; + + return mz_inflateEnd(&stream); +} + +const char *mz_error(int err) { + static struct { + int m_err; + const char *m_pDesc; + } s_error_descs[] = {{MZ_OK, ""}, + {MZ_STREAM_END, "stream end"}, + {MZ_NEED_DICT, "need dictionary"}, + {MZ_ERRNO, "file error"}, + {MZ_STREAM_ERROR, "stream error"}, + {MZ_DATA_ERROR, "data error"}, + {MZ_MEM_ERROR, "out of memory"}, + {MZ_BUF_ERROR, "buf error"}, + {MZ_VERSION_ERROR, "version error"}, + {MZ_PARAM_ERROR, "parameter error"}}; + mz_uint i; + for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) + if (s_error_descs[i].m_err == err) + return s_error_descs[i].m_pDesc; + return NULL; +} + +#endif // MINIZ_NO_ZLIB_APIS + +// ------------------- Low-level Decompression (completely independent from all +// compression API's) + +#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l) +#define TINFL_MEMSET(p, c, l) memset(p, c, l) + +#define TINFL_CR_BEGIN \ + switch (r->m_state) { \ + case 0: +#define TINFL_CR_RETURN(state_index, result) \ + do { \ + status = result; \ + r->m_state = state_index; \ + goto common_exit; \ + case state_index:; \ + } \ + MZ_MACRO_END +#define TINFL_CR_RETURN_FOREVER(state_index, result) \ + do { \ + for (;;) { \ + TINFL_CR_RETURN(state_index, result); \ + } \ + } \ + MZ_MACRO_END +#define TINFL_CR_FINISH } + +// TODO: If the caller has indicated that there's no more input, and we attempt +// to read beyond the input buf, then something is wrong with the input because +// the inflator never reads ahead more than it needs to. Currently +// TINFL_GET_BYTE() pads the end of the stream with 0's in this scenario. +#define TINFL_GET_BYTE(state_index, c) \ + do { \ + if (pIn_buf_cur >= pIn_buf_end) { \ + for (;;) { \ + if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { \ + TINFL_CR_RETURN(state_index, TINFL_STATUS_NEEDS_MORE_INPUT); \ + if (pIn_buf_cur < pIn_buf_end) { \ + c = *pIn_buf_cur++; \ + break; \ + } \ + } else { \ + c = 0; \ + break; \ + } \ + } \ + } else \ + c = *pIn_buf_cur++; \ + } \ + MZ_MACRO_END + +#define TINFL_NEED_BITS(state_index, n) \ + do { \ + mz_uint c; \ + TINFL_GET_BYTE(state_index, c); \ + bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ + num_bits += 8; \ + } while (num_bits < (mz_uint)(n)) +#define TINFL_SKIP_BITS(state_index, n) \ + do { \ + if (num_bits < (mz_uint)(n)) { \ + TINFL_NEED_BITS(state_index, n); \ + } \ + bit_buf >>= (n); \ + num_bits -= (n); \ + } \ + MZ_MACRO_END +#define TINFL_GET_BITS(state_index, b, n) \ + do { \ + if (num_bits < (mz_uint)(n)) { \ + TINFL_NEED_BITS(state_index, n); \ + } \ + b = bit_buf & ((1 << (n)) - 1); \ + bit_buf >>= (n); \ + num_bits -= (n); \ + } \ + MZ_MACRO_END + +// TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes +// remaining in the input buffer falls below 2. It reads just enough bytes from +// the input stream that are needed to decode the next Huffman code (and +// absolutely no more). It works by trying to fully decode a Huffman code by +// using whatever bits are currently present in the bit buffer. If this fails, +// it reads another byte, and tries again until it succeeds or until the bit +// buffer contains >=15 bits (deflate's max. Huffman code size). +#define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \ + do { \ + temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ + if (temp >= 0) { \ + code_len = temp >> 9; \ + if ((code_len) && (num_bits >= code_len)) \ + break; \ + } else if (num_bits > TINFL_FAST_LOOKUP_BITS) { \ + code_len = TINFL_FAST_LOOKUP_BITS; \ + do { \ + temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ + } while ((temp < 0) && (num_bits >= (code_len + 1))); \ + if (temp >= 0) \ + break; \ + } \ + TINFL_GET_BYTE(state_index, c); \ + bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); \ + num_bits += 8; \ + } while (num_bits < 15); + +// TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex +// than you would initially expect because the zlib API expects the decompressor +// to never read beyond the final byte of the deflate stream. (In other words, +// when this macro wants to read another byte from the input, it REALLY needs +// another byte in order to fully decode the next Huffman code.) Handling this +// properly is particularly important on raw deflate (non-zlib) streams, which +// aren't followed by a byte aligned adler-32. The slow path is only executed at +// the very end of the input buffer. +#define TINFL_HUFF_DECODE(state_index, sym, pHuff) \ + do { \ + int temp; \ + mz_uint code_len, c; \ + if (num_bits < 15) { \ + if ((pIn_buf_end - pIn_buf_cur) < 2) { \ + TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \ + } else { \ + bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | \ + (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); \ + pIn_buf_cur += 2; \ + num_bits += 16; \ + } \ + } \ + if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= \ + 0) \ + code_len = temp >> 9, temp &= 511; \ + else { \ + code_len = TINFL_FAST_LOOKUP_BITS; \ + do { \ + temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ + } while (temp < 0); \ + } \ + sym = temp; \ + bit_buf >>= code_len; \ + num_bits -= code_len; \ + } \ + MZ_MACRO_END + +tinfl_status tinfl_decompress(tinfl_decompressor *r, + const mz_uint8 *pIn_buf_next, + size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, + mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, + const mz_uint32 decomp_flags) { + static const int s_length_base[31] = { + 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, + 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0}; + static const int s_length_extra[31] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, + 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, + 4, 4, 5, 5, 5, 5, 0, 0, 0}; + static const int s_dist_base[32] = { + 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, + 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, + 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0}; + static const int s_dist_extra[32] = {0, 0, 0, 0, 1, 1, 2, 2, 3, 3, + 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 9, 9, 10, 10, 11, 11, 12, 12, 13, 13}; + static const mz_uint8 s_length_dezigzag[19] = { + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + static const int s_min_table_sizes[3] = {257, 1, 4}; + + tinfl_status status = TINFL_STATUS_FAILED; + mz_uint32 num_bits, dist, counter, num_extra; + tinfl_bit_buf_t bit_buf; + const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = + pIn_buf_next + *pIn_buf_size; + mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = + pOut_buf_next + *pOut_buf_size; + size_t out_buf_size_mask = + (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) + ? (size_t)-1 + : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, + dist_from_out_buf_start; + + // Ensure the output buffer's size is a power of 2, unless the output buffer + // is large enough to hold the entire output file (in which case it doesn't + // matter). + if (((out_buf_size_mask + 1) & out_buf_size_mask) || + (pOut_buf_next < pOut_buf_start)) { + *pIn_buf_size = *pOut_buf_size = 0; + return TINFL_STATUS_BAD_PARAM; + } + + num_bits = r->m_num_bits; + bit_buf = r->m_bit_buf; + dist = r->m_dist; + counter = r->m_counter; + num_extra = r->m_num_extra; + dist_from_out_buf_start = r->m_dist_from_out_buf_start; + TINFL_CR_BEGIN + + bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; + r->m_z_adler32 = r->m_check_adler32 = 1; + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) { + TINFL_GET_BYTE(1, r->m_zhdr0); + TINFL_GET_BYTE(2, r->m_zhdr1); + counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || + (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); + if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) + counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || + ((out_buf_size_mask + 1) < + (size_t)(1U << (8U + (r->m_zhdr0 >> 4))))); + if (counter) { + TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); + } + } + + do { + TINFL_GET_BITS(3, r->m_final, 3); + r->m_type = r->m_final >> 1; + if (r->m_type == 0) { + TINFL_SKIP_BITS(5, num_bits & 7); + for (counter = 0; counter < 4; ++counter) { + if (num_bits) + TINFL_GET_BITS(6, r->m_raw_header[counter], 8); + else + TINFL_GET_BYTE(7, r->m_raw_header[counter]); + } + if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != + (mz_uint)(0xFFFF ^ + (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) { + TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); + } + while ((counter) && (num_bits)) { + TINFL_GET_BITS(51, dist, 8); + while (pOut_buf_cur >= pOut_buf_end) { + TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); + } + *pOut_buf_cur++ = (mz_uint8)dist; + counter--; + } + while (counter) { + size_t n; + while (pOut_buf_cur >= pOut_buf_end) { + TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); + } + while (pIn_buf_cur >= pIn_buf_end) { + if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { + TINFL_CR_RETURN(38, TINFL_STATUS_NEEDS_MORE_INPUT); + } else { + TINFL_CR_RETURN_FOREVER(40, TINFL_STATUS_FAILED); + } + } + n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), + (size_t)(pIn_buf_end - pIn_buf_cur)), + counter); + TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); + pIn_buf_cur += n; + pOut_buf_cur += n; + counter -= (mz_uint)n; + } + } else if (r->m_type == 3) { + TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED); + } else { + if (r->m_type == 1) { + mz_uint8 *p = r->m_tables[0].m_code_size; + mz_uint i; + r->m_table_sizes[0] = 288; + r->m_table_sizes[1] = 32; + TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32); + for (i = 0; i <= 143; ++i) + *p++ = 8; + for (; i <= 255; ++i) + *p++ = 9; + for (; i <= 279; ++i) + *p++ = 7; + for (; i <= 287; ++i) + *p++ = 8; + } else { + for (counter = 0; counter < 3; counter++) { + TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); + r->m_table_sizes[counter] += s_min_table_sizes[counter]; + } + MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); + for (counter = 0; counter < r->m_table_sizes[2]; counter++) { + mz_uint s; + TINFL_GET_BITS(14, s, 3); + r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; + } + r->m_table_sizes[2] = 19; + } + for (; (int)r->m_type >= 0; r->m_type--) { + int tree_next, tree_cur; + tinfl_huff_table *pTable; + mz_uint i, j, used_syms, total, sym_index, next_code[17], + total_syms[16]; + pTable = &r->m_tables[r->m_type]; + MZ_CLEAR_OBJ(total_syms); + MZ_CLEAR_OBJ(pTable->m_look_up); + MZ_CLEAR_OBJ(pTable->m_tree); + for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) + total_syms[pTable->m_code_size[i]]++; + used_syms = 0, total = 0; + next_code[0] = next_code[1] = 0; + for (i = 1; i <= 15; ++i) { + used_syms += total_syms[i]; + next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); + } + if ((65536 != total) && (used_syms > 1)) { + TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED); + } + for (tree_next = -1, sym_index = 0; + sym_index < r->m_table_sizes[r->m_type]; ++sym_index) { + mz_uint rev_code = 0, l, cur_code, + code_size = pTable->m_code_size[sym_index]; + if (!code_size) + continue; + cur_code = next_code[code_size]++; + for (l = code_size; l > 0; l--, cur_code >>= 1) + rev_code = (rev_code << 1) | (cur_code & 1); + if (code_size <= TINFL_FAST_LOOKUP_BITS) { + mz_int16 k = (mz_int16)((code_size << 9) | sym_index); + while (rev_code < TINFL_FAST_LOOKUP_SIZE) { + pTable->m_look_up[rev_code] = k; + rev_code += (1 << code_size); + } + continue; + } + if (0 == + (tree_cur = pTable->m_look_up[rev_code & + (TINFL_FAST_LOOKUP_SIZE - 1)])) { + pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = + (mz_int16)tree_next; + tree_cur = tree_next; + tree_next -= 2; + } + rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1); + for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) { + tree_cur -= ((rev_code >>= 1) & 1); + if (!pTable->m_tree[-tree_cur - 1]) { + pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; + tree_cur = tree_next; + tree_next -= 2; + } else + tree_cur = pTable->m_tree[-tree_cur - 1]; + } + rev_code >>= 1; + tree_cur -= (rev_code & 1); + pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index; + } + if (r->m_type == 2) { + for (counter = 0; + counter < (r->m_table_sizes[0] + r->m_table_sizes[1]);) { + mz_uint s; + TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); + if (dist < 16) { + r->m_len_codes[counter++] = (mz_uint8)dist; + continue; + } + if ((dist == 16) && (!counter)) { + TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED); + } + num_extra = "\02\03\07"[dist - 16]; + TINFL_GET_BITS(18, s, num_extra); + s += "\03\03\013"[dist - 16]; + TINFL_MEMSET(r->m_len_codes + counter, + (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); + counter += s; + } + if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter) { + TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); + } + TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, + r->m_table_sizes[0]); + TINFL_MEMCPY(r->m_tables[1].m_code_size, + r->m_len_codes + r->m_table_sizes[0], + r->m_table_sizes[1]); + } + } + for (;;) { + mz_uint8 *pSrc; + for (;;) { + if (((pIn_buf_end - pIn_buf_cur) < 4) || + ((pOut_buf_end - pOut_buf_cur) < 2)) { + TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]); + if (counter >= 256) + break; + while (pOut_buf_cur >= pOut_buf_end) { + TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); + } + *pOut_buf_cur++ = (mz_uint8)counter; + } else { + int sym2; + mz_uint code_len; +#if TINFL_USE_64BIT_BITBUF + if (num_bits < 30) { + bit_buf |= + (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); + pIn_buf_cur += 4; + num_bits += 32; + } +#else + if (num_bits < 15) { + bit_buf |= + (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); + pIn_buf_cur += 2; + num_bits += 16; + } +#endif + if ((sym2 = + r->m_tables[0] + .m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= + 0) + code_len = sym2 >> 9; + else { + code_len = TINFL_FAST_LOOKUP_BITS; + do { + sym2 = r->m_tables[0] + .m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; + } while (sym2 < 0); + } + counter = sym2; + bit_buf >>= code_len; + num_bits -= code_len; + if (counter & 256) + break; + +#if !TINFL_USE_64BIT_BITBUF + if (num_bits < 15) { + bit_buf |= + (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); + pIn_buf_cur += 2; + num_bits += 16; + } +#endif + if ((sym2 = + r->m_tables[0] + .m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= + 0) + code_len = sym2 >> 9; + else { + code_len = TINFL_FAST_LOOKUP_BITS; + do { + sym2 = r->m_tables[0] + .m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; + } while (sym2 < 0); + } + bit_buf >>= code_len; + num_bits -= code_len; + + pOut_buf_cur[0] = (mz_uint8)counter; + if (sym2 & 256) { + pOut_buf_cur++; + counter = sym2; + break; + } + pOut_buf_cur[1] = (mz_uint8)sym2; + pOut_buf_cur += 2; + } + } + if ((counter &= 511) == 256) + break; + + num_extra = s_length_extra[counter - 257]; + counter = s_length_base[counter - 257]; + if (num_extra) { + mz_uint extra_bits; + TINFL_GET_BITS(25, extra_bits, num_extra); + counter += extra_bits; + } + + TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]); + num_extra = s_dist_extra[dist]; + dist = s_dist_base[dist]; + if (num_extra) { + mz_uint extra_bits; + TINFL_GET_BITS(27, extra_bits, num_extra); + dist += extra_bits; + } + + dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; + if ((dist > dist_from_out_buf_start) && + (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) { + TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); + } + + pSrc = pOut_buf_start + + ((dist_from_out_buf_start - dist) & out_buf_size_mask); + + if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end) { + while (counter--) { + while (pOut_buf_cur >= pOut_buf_end) { + TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); + } + *pOut_buf_cur++ = + pOut_buf_start[(dist_from_out_buf_start++ - dist) & + out_buf_size_mask]; + } + continue; + } +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES + else if ((counter >= 9) && (counter <= dist)) { + const mz_uint8 *pSrc_end = pSrc + (counter & ~7); + do { + ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0]; + ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1]; + pOut_buf_cur += 8; + } while ((pSrc += 8) < pSrc_end); + if ((counter &= 7) < 3) { + if (counter) { + pOut_buf_cur[0] = pSrc[0]; + if (counter > 1) + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + continue; + } + } +#endif + do { + pOut_buf_cur[0] = pSrc[0]; + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur[2] = pSrc[2]; + pOut_buf_cur += 3; + pSrc += 3; + } while ((int)(counter -= 3) > 2); + if ((int)counter > 0) { + pOut_buf_cur[0] = pSrc[0]; + if ((int)counter > 1) + pOut_buf_cur[1] = pSrc[1]; + pOut_buf_cur += counter; + } + } + } + } while (!(r->m_final & 1)); + if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) { + TINFL_SKIP_BITS(32, num_bits & 7); + for (counter = 0; counter < 4; ++counter) { + mz_uint s; + if (num_bits) + TINFL_GET_BITS(41, s, 8); + else + TINFL_GET_BYTE(42, s); + r->m_z_adler32 = (r->m_z_adler32 << 8) | s; + } + } + TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE); + TINFL_CR_FINISH + +common_exit: + r->m_num_bits = num_bits; + r->m_bit_buf = bit_buf; + r->m_dist = dist; + r->m_counter = counter; + r->m_num_extra = num_extra; + r->m_dist_from_out_buf_start = dist_from_out_buf_start; + *pIn_buf_size = pIn_buf_cur - pIn_buf_next; + *pOut_buf_size = pOut_buf_cur - pOut_buf_next; + if ((decomp_flags & + (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && + (status >= 0)) { + const mz_uint8 *ptr = pOut_buf_next; + size_t buf_len = *pOut_buf_size; + mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, + s2 = r->m_check_adler32 >> 16; + size_t block_len = buf_len % 5552; + while (buf_len) { + for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { + s1 += ptr[0], s2 += s1; + s1 += ptr[1], s2 += s1; + s1 += ptr[2], s2 += s1; + s1 += ptr[3], s2 += s1; + s1 += ptr[4], s2 += s1; + s1 += ptr[5], s2 += s1; + s1 += ptr[6], s2 += s1; + s1 += ptr[7], s2 += s1; + } + for (; i < block_len; ++i) + s1 += *ptr++, s2 += s1; + s1 %= 65521U, s2 %= 65521U; + buf_len -= block_len; + block_len = 5552; + } + r->m_check_adler32 = (s2 << 16) + s1; + if ((status == TINFL_STATUS_DONE) && + (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && + (r->m_check_adler32 != r->m_z_adler32)) + status = TINFL_STATUS_ADLER32_MISMATCH; + } + return status; +} + +// Higher level helper functions. +void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, + size_t *pOut_len, int flags) { + tinfl_decompressor decomp; + void *pBuf = NULL, *pNew_buf; + size_t src_buf_ofs = 0, out_buf_capacity = 0; + *pOut_len = 0; + tinfl_init(&decomp); + for (;;) { + size_t src_buf_size = src_buf_len - src_buf_ofs, + dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity; + tinfl_status status = tinfl_decompress( + &decomp, (const mz_uint8 *)pSrc_buf + src_buf_ofs, &src_buf_size, + (mz_uint8 *)pBuf, pBuf ? (mz_uint8 *)pBuf + *pOut_len : NULL, + &dst_buf_size, + (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | + TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); + if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) { + MZ_FREE(pBuf); + *pOut_len = 0; + return NULL; + } + src_buf_ofs += src_buf_size; + *pOut_len += dst_buf_size; + if (status == TINFL_STATUS_DONE) + break; + new_out_buf_capacity = out_buf_capacity * 2; + if (new_out_buf_capacity < 128) + new_out_buf_capacity = 128; + pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); + if (!pNew_buf) { + MZ_FREE(pBuf); + *pOut_len = 0; + return NULL; + } + pBuf = pNew_buf; + out_buf_capacity = new_out_buf_capacity; + } + return pBuf; +} + +size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, + const void *pSrc_buf, size_t src_buf_len, + int flags) { + tinfl_decompressor decomp; + tinfl_status status; + tinfl_init(&decomp); + status = + tinfl_decompress(&decomp, (const mz_uint8 *)pSrc_buf, &src_buf_len, + (mz_uint8 *)pOut_buf, (mz_uint8 *)pOut_buf, &out_buf_len, + (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | + TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); + return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED + : out_buf_len; +} + +int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, + tinfl_put_buf_func_ptr pPut_buf_func, + void *pPut_buf_user, int flags) { + int result = 0; + tinfl_decompressor decomp; + mz_uint8 *pDict = (mz_uint8 *)MZ_MALLOC(TINFL_LZ_DICT_SIZE); + size_t in_buf_ofs = 0, dict_ofs = 0; + if (!pDict) + return TINFL_STATUS_FAILED; + tinfl_init(&decomp); + for (;;) { + size_t in_buf_size = *pIn_buf_size - in_buf_ofs, + dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; + tinfl_status status = + tinfl_decompress(&decomp, (const mz_uint8 *)pIn_buf + in_buf_ofs, + &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, + (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | + TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); + in_buf_ofs += in_buf_size; + if ((dst_buf_size) && + (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) + break; + if (status != TINFL_STATUS_HAS_MORE_OUTPUT) { + result = (status == TINFL_STATUS_DONE); + break; + } + dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); + } + MZ_FREE(pDict); + *pIn_buf_size = in_buf_ofs; + return result; +} + +// ------------------- Low-level Compression (independent from all decompression +// API's) + +// Purposely making these tables static for faster init and thread safety. +static const mz_uint16 s_tdefl_len_sym[256] = { + 257, 258, 259, 260, 261, 262, 263, 264, 265, 265, 266, 266, 267, 267, 268, + 268, 269, 269, 269, 269, 270, 270, 270, 270, 271, 271, 271, 271, 272, 272, + 272, 272, 273, 273, 273, 273, 273, 273, 273, 273, 274, 274, 274, 274, 274, + 274, 274, 274, 275, 275, 275, 275, 275, 275, 275, 275, 276, 276, 276, 276, + 276, 276, 276, 276, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, 277, + 277, 277, 277, 277, 277, 278, 278, 278, 278, 278, 278, 278, 278, 278, 278, + 278, 278, 278, 278, 278, 278, 279, 279, 279, 279, 279, 279, 279, 279, 279, + 279, 279, 279, 279, 279, 279, 279, 280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280, 280, 280, 280, 280, 281, 281, 281, 281, 281, 281, 281, + 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, + 281, 281, 281, 281, 281, 281, 281, 281, 281, 281, 282, 282, 282, 282, 282, + 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, + 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 282, 283, 283, 283, + 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, + 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 283, 284, + 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, + 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, 284, + 285}; + +static const mz_uint8 s_tdefl_len_extra[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0}; + +static const mz_uint8 s_tdefl_small_dist_sym[512] = { + 0, 1, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, + 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17}; + +static const mz_uint8 s_tdefl_small_dist_extra[512] = { + 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7}; + +static const mz_uint8 s_tdefl_large_dist_sym[128] = { + 0, 0, 18, 19, 20, 20, 21, 21, 22, 22, 22, 22, 23, 23, 23, 23, 24, 24, 24, + 24, 24, 24, 24, 24, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 27, 27, 27, 27, 27, + 27, 27, 27, 27, 27, 27, 27, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, + 28, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29}; + +static const mz_uint8 s_tdefl_large_dist_extra[128] = { + 0, 0, 8, 8, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11, + 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 12, 12, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13}; + +// Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted +// values. +typedef struct { + mz_uint16 m_key, m_sym_index; +} tdefl_sym_freq; +static tdefl_sym_freq *tdefl_radix_sort_syms(mz_uint num_syms, + tdefl_sym_freq *pSyms0, + tdefl_sym_freq *pSyms1) { + mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; + tdefl_sym_freq *pCur_syms = pSyms0, *pNew_syms = pSyms1; + MZ_CLEAR_OBJ(hist); + for (i = 0; i < num_syms; i++) { + mz_uint freq = pSyms0[i].m_key; + hist[freq & 0xFF]++; + hist[256 + ((freq >> 8) & 0xFF)]++; + } + while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) + total_passes--; + for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8) { + const mz_uint32 *pHist = &hist[pass << 8]; + mz_uint offsets[256], cur_ofs = 0; + for (i = 0; i < 256; i++) { + offsets[i] = cur_ofs; + cur_ofs += pHist[i]; + } + for (i = 0; i < num_syms; i++) + pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = + pCur_syms[i]; + { + tdefl_sym_freq *t = pCur_syms; + pCur_syms = pNew_syms; + pNew_syms = t; + } + } + return pCur_syms; +} + +// tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, +// alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. +static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n) { + int root, leaf, next, avbl, used, dpth; + if (n == 0) + return; + else if (n == 1) { + A[0].m_key = 1; + return; + } + A[0].m_key += A[1].m_key; + root = 0; + leaf = 2; + for (next = 1; next < n - 1; next++) { + if (leaf >= n || A[root].m_key < A[leaf].m_key) { + A[next].m_key = A[root].m_key; + A[root++].m_key = (mz_uint16)next; + } else + A[next].m_key = A[leaf++].m_key; + if (leaf >= n || (root < next && A[root].m_key < A[leaf].m_key)) { + A[next].m_key = (mz_uint16)(A[next].m_key + A[root].m_key); + A[root++].m_key = (mz_uint16)next; + } else + A[next].m_key = (mz_uint16)(A[next].m_key + A[leaf++].m_key); + } + A[n - 2].m_key = 0; + for (next = n - 3; next >= 0; next--) + A[next].m_key = A[A[next].m_key].m_key + 1; + avbl = 1; + used = dpth = 0; + root = n - 2; + next = n - 1; + while (avbl > 0) { + while (root >= 0 && (int)A[root].m_key == dpth) { + used++; + root--; + } + while (avbl > used) { + A[next--].m_key = (mz_uint16)(dpth); + avbl--; + } + avbl = 2 * used; + dpth++; + used = 0; + } +} + +// Limits canonical Huffman code table's max code size. +enum { TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 }; +static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, + int code_list_len, + int max_code_size) { + int i; + mz_uint32 total = 0; + if (code_list_len <= 1) + return; + for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) + pNum_codes[max_code_size] += pNum_codes[i]; + for (i = max_code_size; i > 0; i--) + total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i)); + while (total != (1UL << max_code_size)) { + pNum_codes[max_code_size]--; + for (i = max_code_size - 1; i > 0; i--) + if (pNum_codes[i]) { + pNum_codes[i]--; + pNum_codes[i + 1] += 2; + break; + } + total--; + } +} + +static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, + int table_len, int code_size_limit, + int static_table) { + int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; + mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; + MZ_CLEAR_OBJ(num_codes); + if (static_table) { + for (i = 0; i < table_len; i++) + num_codes[d->m_huff_code_sizes[table_num][i]]++; + } else { + tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], + *pSyms; + int num_used_syms = 0; + const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0]; + for (i = 0; i < table_len; i++) + if (pSym_count[i]) { + syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i]; + syms0[num_used_syms++].m_sym_index = (mz_uint16)i; + } + + pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); + tdefl_calculate_minimum_redundancy(pSyms, num_used_syms); + + for (i = 0; i < num_used_syms; i++) + num_codes[pSyms[i].m_key]++; + + tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, + code_size_limit); + + MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]); + MZ_CLEAR_OBJ(d->m_huff_codes[table_num]); + for (i = 1, j = num_used_syms; i <= code_size_limit; i++) + for (l = num_codes[i]; l > 0; l--) + d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i); + } + + next_code[1] = 0; + for (j = 0, i = 2; i <= code_size_limit; i++) + next_code[i] = j = ((j + num_codes[i - 1]) << 1); + + for (i = 0; i < table_len; i++) { + mz_uint rev_code = 0, code, code_size; + if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) + continue; + code = next_code[code_size]++; + for (l = code_size; l > 0; l--, code >>= 1) + rev_code = (rev_code << 1) | (code & 1); + d->m_huff_codes[table_num][i] = (mz_uint16)rev_code; + } +} + +#define TDEFL_PUT_BITS(b, l) \ + do { \ + mz_uint bits = b; \ + mz_uint len = l; \ + MZ_ASSERT(bits <= ((1U << len) - 1U)); \ + d->m_bit_buffer |= (bits << d->m_bits_in); \ + d->m_bits_in += len; \ + while (d->m_bits_in >= 8) { \ + if (d->m_pOutput_buf < d->m_pOutput_buf_end) \ + *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \ + d->m_bit_buffer >>= 8; \ + d->m_bits_in -= 8; \ + } \ + } \ + MZ_MACRO_END + +#define TDEFL_RLE_PREV_CODE_SIZE() \ + { \ + if (rle_repeat_count) { \ + if (rle_repeat_count < 3) { \ + d->m_huff_count[2][prev_code_size] = (mz_uint16)( \ + d->m_huff_count[2][prev_code_size] + rle_repeat_count); \ + while (rle_repeat_count--) \ + packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \ + } else { \ + d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 16; \ + packed_code_sizes[num_packed_code_sizes++] = \ + (mz_uint8)(rle_repeat_count - 3); \ + } \ + rle_repeat_count = 0; \ + } \ + } + +#define TDEFL_RLE_ZERO_CODE_SIZE() \ + { \ + if (rle_z_count) { \ + if (rle_z_count < 3) { \ + d->m_huff_count[2][0] = \ + (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); \ + while (rle_z_count--) \ + packed_code_sizes[num_packed_code_sizes++] = 0; \ + } else if (rle_z_count <= 10) { \ + d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 17; \ + packed_code_sizes[num_packed_code_sizes++] = \ + (mz_uint8)(rle_z_count - 3); \ + } else { \ + d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); \ + packed_code_sizes[num_packed_code_sizes++] = 18; \ + packed_code_sizes[num_packed_code_sizes++] = \ + (mz_uint8)(rle_z_count - 11); \ + } \ + rle_z_count = 0; \ + } \ + } + +static mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { + 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15}; + +static void tdefl_start_dynamic_block(tdefl_compressor *d) { + int num_lit_codes, num_dist_codes, num_bit_lengths; + mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, + rle_repeat_count, packed_code_sizes_index; + mz_uint8 + code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], + packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], + prev_code_size = 0xFF; + + d->m_huff_count[0][256] = 1; + + tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE); + tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE); + + for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) + if (d->m_huff_code_sizes[0][num_lit_codes - 1]) + break; + for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) + if (d->m_huff_code_sizes[1][num_dist_codes - 1]) + break; + + memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], + sizeof(mz_uint8) * num_lit_codes); + memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], + sizeof(mz_uint8) * num_dist_codes); + total_code_sizes_to_pack = num_lit_codes + num_dist_codes; + num_packed_code_sizes = 0; + rle_z_count = 0; + rle_repeat_count = 0; + + memset(&d->m_huff_count[2][0], 0, + sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2); + for (i = 0; i < total_code_sizes_to_pack; i++) { + mz_uint8 code_size = code_sizes_to_pack[i]; + if (!code_size) { + TDEFL_RLE_PREV_CODE_SIZE(); + if (++rle_z_count == 138) { + TDEFL_RLE_ZERO_CODE_SIZE(); + } + } else { + TDEFL_RLE_ZERO_CODE_SIZE(); + if (code_size != prev_code_size) { + TDEFL_RLE_PREV_CODE_SIZE(); + d->m_huff_count[2][code_size] = + (mz_uint16)(d->m_huff_count[2][code_size] + 1); + packed_code_sizes[num_packed_code_sizes++] = code_size; + } else if (++rle_repeat_count == 6) { + TDEFL_RLE_PREV_CODE_SIZE(); + } + } + prev_code_size = code_size; + } + if (rle_repeat_count) { + TDEFL_RLE_PREV_CODE_SIZE(); + } else { + TDEFL_RLE_ZERO_CODE_SIZE(); + } + + tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE); + + TDEFL_PUT_BITS(2, 2); + + TDEFL_PUT_BITS(num_lit_codes - 257, 5); + TDEFL_PUT_BITS(num_dist_codes - 1, 5); + + for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) + if (d->m_huff_code_sizes + [2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) + break; + num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); + TDEFL_PUT_BITS(num_bit_lengths - 4, 4); + for (i = 0; (int)i < num_bit_lengths; i++) + TDEFL_PUT_BITS( + d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3); + + for (packed_code_sizes_index = 0; + packed_code_sizes_index < num_packed_code_sizes;) { + mz_uint code = packed_code_sizes[packed_code_sizes_index++]; + MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2); + TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]); + if (code >= 16) + TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], + "\02\03\07"[code - 16]); + } +} + +static void tdefl_start_static_block(tdefl_compressor *d) { + mz_uint i; + mz_uint8 *p = &d->m_huff_code_sizes[0][0]; + + for (i = 0; i <= 143; ++i) + *p++ = 8; + for (; i <= 255; ++i) + *p++ = 9; + for (; i <= 279; ++i) + *p++ = 7; + for (; i <= 287; ++i) + *p++ = 8; + + memset(d->m_huff_code_sizes[1], 5, 32); + + tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE); + tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE); + + TDEFL_PUT_BITS(1, 2); +} + +static const mz_uint mz_bitmasks[17] = { + 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, + 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF}; + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && \ + MINIZ_HAS_64BIT_REGISTERS +static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) { + mz_uint flags; + mz_uint8 *pLZ_codes; + mz_uint8 *pOutput_buf = d->m_pOutput_buf; + mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf; + mz_uint64 bit_buffer = d->m_bit_buffer; + mz_uint bits_in = d->m_bits_in; + +#define TDEFL_PUT_BITS_FAST(b, l) \ + { \ + bit_buffer |= (((mz_uint64)(b)) << bits_in); \ + bits_in += (l); \ + } + + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; + flags >>= 1) { + if (flags == 1) + flags = *pLZ_codes++ | 0x100; + + if (flags & 1) { + mz_uint s0, s1, n0, n1, sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0], + match_dist = *(const mz_uint16 *)(pLZ_codes + 1); + pLZ_codes += 3; + + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], + d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], + s_tdefl_len_extra[match_len]); + + // This sequence coaxes MSVC into using cmov's vs. jmp's. + s0 = s_tdefl_small_dist_sym[match_dist & 511]; + n0 = s_tdefl_small_dist_extra[match_dist & 511]; + s1 = s_tdefl_large_dist_sym[match_dist >> 8]; + n1 = s_tdefl_large_dist_extra[match_dist >> 8]; + sym = (match_dist < 512) ? s0 : s1; + num_extra_bits = (match_dist < 512) ? n0 : n1; + + MZ_ASSERT(d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], + d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], + num_extra_bits); + } else { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], + d->m_huff_code_sizes[0][lit]); + + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) { + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], + d->m_huff_code_sizes[0][lit]); + + if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) { + flags >>= 1; + lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], + d->m_huff_code_sizes[0][lit]); + } + } + } + + if (pOutput_buf >= d->m_pOutput_buf_end) + return MZ_FALSE; + + *(mz_uint64 *)pOutput_buf = bit_buffer; + pOutput_buf += (bits_in >> 3); + bit_buffer >>= (bits_in & ~7); + bits_in &= 7; + } + +#undef TDEFL_PUT_BITS_FAST + + d->m_pOutput_buf = pOutput_buf; + d->m_bits_in = 0; + d->m_bit_buffer = 0; + + while (bits_in) { + mz_uint32 n = MZ_MIN(bits_in, 16); + TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n); + bit_buffer >>= n; + bits_in -= n; + } + + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); + + return (d->m_pOutput_buf < d->m_pOutput_buf_end); +} +#else +static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) { + mz_uint flags; + mz_uint8 *pLZ_codes; + + flags = 1; + for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; + flags >>= 1) { + if (flags == 1) + flags = *pLZ_codes++ | 0x100; + if (flags & 1) { + mz_uint sym, num_extra_bits; + mz_uint match_len = pLZ_codes[0], + match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); + pLZ_codes += 3; + + MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], + d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); + TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], + s_tdefl_len_extra[match_len]); + + if (match_dist < 512) { + sym = s_tdefl_small_dist_sym[match_dist]; + num_extra_bits = s_tdefl_small_dist_extra[match_dist]; + } else { + sym = s_tdefl_large_dist_sym[match_dist >> 8]; + num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8]; + } + TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); + TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); + } else { + mz_uint lit = *pLZ_codes++; + MZ_ASSERT(d->m_huff_code_sizes[0][lit]); + TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); + } + } + + TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); + + return (d->m_pOutput_buf < d->m_pOutput_buf_end); +} +#endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && + // MINIZ_HAS_64BIT_REGISTERS + +static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block) { + if (static_block) + tdefl_start_static_block(d); + else + tdefl_start_dynamic_block(d); + return tdefl_compress_lz_codes(d); +} + +static int tdefl_flush_block(tdefl_compressor *d, int flush) { + mz_uint saved_bit_buf, saved_bits_in; + mz_uint8 *pSaved_output_buf; + mz_bool comp_block_succeeded = MZ_FALSE; + int n, use_raw_block = + ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && + (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size; + mz_uint8 *pOutput_buf_start = + ((d->m_pPut_buf_func == NULL) && + ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) + ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) + : d->m_output_buf; + + d->m_pOutput_buf = pOutput_buf_start; + d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16; + + MZ_ASSERT(!d->m_output_flush_remaining); + d->m_output_flush_ofs = 0; + d->m_output_flush_remaining = 0; + + *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left); + d->m_pLZ_code_buf -= (d->m_num_flags_left == 8); + + if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index)) { + TDEFL_PUT_BITS(0x78, 8); + TDEFL_PUT_BITS(0x01, 8); + } + + TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1); + + pSaved_output_buf = d->m_pOutput_buf; + saved_bit_buf = d->m_bit_buffer; + saved_bits_in = d->m_bits_in; + + if (!use_raw_block) + comp_block_succeeded = + tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || + (d->m_total_lz_bytes < 48)); + + // If the block gets expanded, forget the current contents of the output + // buffer and send a raw block instead. + if (((use_raw_block) || + ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= + d->m_total_lz_bytes))) && + ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size)) { + mz_uint i; + d->m_pOutput_buf = pSaved_output_buf; + d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + TDEFL_PUT_BITS(0, 2); + if (d->m_bits_in) { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF) { + TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16); + } + for (i = 0; i < d->m_total_lz_bytes; ++i) { + TDEFL_PUT_BITS( + d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], + 8); + } + } + // Check for the extremely unlikely (if not impossible) case of the compressed + // block not fitting into the output buffer when using dynamic codes. + else if (!comp_block_succeeded) { + d->m_pOutput_buf = pSaved_output_buf; + d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; + tdefl_compress_block(d, MZ_TRUE); + } + + if (flush) { + if (flush == TDEFL_FINISH) { + if (d->m_bits_in) { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) { + mz_uint i, a = d->m_adler32; + for (i = 0; i < 4; i++) { + TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); + a <<= 8; + } + } + } else { + mz_uint i, z = 0; + TDEFL_PUT_BITS(0, 3); + if (d->m_bits_in) { + TDEFL_PUT_BITS(0, 8 - d->m_bits_in); + } + for (i = 2; i; --i, z ^= 0xFFFF) { + TDEFL_PUT_BITS(z & 0xFFFF, 16); + } + } + } + + MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end); + + memset(&d->m_huff_count[0][0], 0, + sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, + sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + + d->m_pLZ_code_buf = d->m_lz_code_buf + 1; + d->m_pLZ_flags = d->m_lz_code_buf; + d->m_num_flags_left = 8; + d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; + d->m_total_lz_bytes = 0; + d->m_block_index++; + + if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0) { + if (d->m_pPut_buf_func) { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; + if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user)) + return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED); + } else if (pOutput_buf_start == d->m_output_buf) { + int bytes_to_copy = (int)MZ_MIN( + (size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs)); + memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, + bytes_to_copy); + d->m_out_buf_ofs += bytes_to_copy; + if ((n -= bytes_to_copy) != 0) { + d->m_output_flush_ofs = bytes_to_copy; + d->m_output_flush_remaining = n; + } + } else { + d->m_out_buf_ofs += n; + } + } + + return d->m_output_flush_remaining; +} + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES +#define TDEFL_READ_UNALIGNED_WORD(p) ((p)[0] | (p)[1] << 8) +static MZ_FORCEINLINE void +tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, + mz_uint max_match_len, mz_uint *pMatch_dist, + mz_uint *pMatch_len) { + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, + match_len = *pMatch_len, probe_pos = pos, next_probe_pos, + probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint16 *s = (const mz_uint16 *)(d->m_dict + pos), *p, *q; + mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), + s01 = *s; + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); + if (max_match_len <= match_len) + return; + for (;;) { + for (;;) { + if (--num_probes_left == 0) + return; +#define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || \ + ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ + return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) \ + break; + TDEFL_PROBE; + TDEFL_PROBE; + TDEFL_PROBE; + } + if (!dist) + break; + q = (const mz_uint16 *)(d->m_dict + probe_pos); + if (*q != s01) + continue; + p = s; + probe_len = 32; + do { + } while ((*(++p) == *(++q)) && (*(++p) == *(++q)) && (*(++p) == *(++q)) && + (*(++p) == *(++q)) && (--probe_len > 0)); + if (!probe_len) { + *pMatch_dist = dist; + *pMatch_len = MZ_MIN(max_match_len, TDEFL_MAX_MATCH_LEN); + break; + } else if ((probe_len = ((mz_uint)(p - s) * 2) + + (mz_uint)(*(const mz_uint8 *)p == + *(const mz_uint8 *)q)) > match_len) { + *pMatch_dist = dist; + if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == + max_match_len) + break; + c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]); + } + } +} +#else +static MZ_FORCEINLINE void +tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, + mz_uint max_match_len, mz_uint *pMatch_dist, + mz_uint *pMatch_len) { + mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, + match_len = *pMatch_len, probe_pos = pos, next_probe_pos, + probe_len; + mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; + const mz_uint8 *s = d->m_dict + pos, *p, *q; + mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1]; + MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); + if (max_match_len <= match_len) + return; + for (;;) { + for (;;) { + if (--num_probes_left == 0) + return; +#define TDEFL_PROBE \ + next_probe_pos = d->m_next[probe_pos]; \ + if ((!next_probe_pos) || \ + ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) \ + return; \ + probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ + if ((d->m_dict[probe_pos + match_len] == c0) && \ + (d->m_dict[probe_pos + match_len - 1] == c1)) \ + break; + TDEFL_PROBE; + TDEFL_PROBE; + TDEFL_PROBE; + } + if (!dist) + break; + p = s; + q = d->m_dict + probe_pos; + for (probe_len = 0; probe_len < max_match_len; probe_len++) + if (*p++ != *q++) + break; + if (probe_len > match_len) { + *pMatch_dist = dist; + if ((*pMatch_len = match_len = probe_len) == max_match_len) + return; + c0 = d->m_dict[pos + match_len]; + c1 = d->m_dict[pos + match_len - 1]; + } + } +} +#endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN +static mz_bool tdefl_compress_fast(tdefl_compressor *d) { + // Faster, minimally featured LZRW1-style match+parse loop with better + // register utilization. Intended for applications where raw throughput is + // valued more highly than ratio. + mz_uint lookahead_pos = d->m_lookahead_pos, + lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, + total_lz_bytes = d->m_total_lz_bytes, + num_flags_left = d->m_num_flags_left; + mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags; + mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; + + while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size))) { + const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096; + mz_uint dst_pos = + (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; + mz_uint num_bytes_to_process = (mz_uint)MZ_MIN( + d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size); + d->m_src_buf_left -= num_bytes_to_process; + lookahead_size += num_bytes_to_process; + + while (num_bytes_to_process) { + mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process); + memcpy(d->m_dict + dst_pos, d->m_pSrc, n); + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, + MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos)); + d->m_pSrc += n; + dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK; + num_bytes_to_process -= n; + } + + dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size); + if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) + break; + + while (lookahead_size >= 4) { + mz_uint cur_match_dist, cur_match_len = 1; + mz_uint8 *pCur_dict = d->m_dict + cur_pos; + mz_uint first_trigram = (*(const mz_uint32 *)pCur_dict) & 0xFFFFFF; + mz_uint hash = + (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & + TDEFL_LEVEL1_HASH_SIZE_MASK; + mz_uint probe_pos = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)lookahead_pos; + + if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= + dict_size) && + ((mz_uint32)( + *(d->m_dict + (probe_pos & TDEFL_LZ_DICT_SIZE_MASK)) | + (*(d->m_dict + ((probe_pos & TDEFL_LZ_DICT_SIZE_MASK) + 1)) + << 8) | + (*(d->m_dict + ((probe_pos & TDEFL_LZ_DICT_SIZE_MASK) + 2)) + << 16)) == first_trigram)) { + const mz_uint16 *p = (const mz_uint16 *)pCur_dict; + const mz_uint16 *q = + (const mz_uint16 *)(d->m_dict + + (probe_pos & TDEFL_LZ_DICT_SIZE_MASK)); + mz_uint32 probe_len = 32; + do { + } while ((*(++p) == *(++q)) && (*(++p) == *(++q)) && + (*(++p) == *(++q)) && (*(++p) == *(++q)) && (--probe_len > 0)); + cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q); + if (!probe_len) + cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0; + + if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || + ((cur_match_len == TDEFL_MIN_MATCH_LEN) && + (cur_match_dist >= 8U * 1024U))) { + cur_match_len = 1; + *pLZ_code_buf++ = (mz_uint8)first_trigram; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + d->m_huff_count[0][(mz_uint8)first_trigram]++; + } else { + mz_uint32 s0, s1; + cur_match_len = MZ_MIN(cur_match_len, lookahead_size); + + MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && + (cur_match_dist >= 1) && + (cur_match_dist <= TDEFL_LZ_DICT_SIZE)); + + cur_match_dist--; + + pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN); + *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist; + pLZ_code_buf += 3; + *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80); + + s0 = s_tdefl_small_dist_sym[cur_match_dist & 511]; + s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8]; + d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++; + + d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - + TDEFL_MIN_MATCH_LEN]]++; + } + } else { + *pLZ_code_buf++ = (mz_uint8)first_trigram; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + d->m_huff_count[0][(mz_uint8)first_trigram]++; + } + + if (--num_flags_left == 0) { + num_flags_left = 8; + pLZ_flags = pLZ_code_buf++; + } + + total_lz_bytes += cur_match_len; + lookahead_pos += cur_match_len; + dict_size = MZ_MIN(dict_size + cur_match_len, TDEFL_LZ_DICT_SIZE); + cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK; + MZ_ASSERT(lookahead_size >= cur_match_len); + lookahead_size -= cur_match_len; + + if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) { + int n; + d->m_lookahead_pos = lookahead_pos; + d->m_lookahead_size = lookahead_size; + d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; + d->m_pLZ_code_buf = pLZ_code_buf; + d->m_pLZ_flags = pLZ_flags; + d->m_num_flags_left = num_flags_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + total_lz_bytes = d->m_total_lz_bytes; + pLZ_code_buf = d->m_pLZ_code_buf; + pLZ_flags = d->m_pLZ_flags; + num_flags_left = d->m_num_flags_left; + } + } + + while (lookahead_size) { + mz_uint8 lit = d->m_dict[cur_pos]; + + total_lz_bytes++; + *pLZ_code_buf++ = lit; + *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); + if (--num_flags_left == 0) { + num_flags_left = 8; + pLZ_flags = pLZ_code_buf++; + } + + d->m_huff_count[0][lit]++; + + lookahead_pos++; + dict_size = MZ_MIN(dict_size + 1, TDEFL_LZ_DICT_SIZE); + cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; + lookahead_size--; + + if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) { + int n; + d->m_lookahead_pos = lookahead_pos; + d->m_lookahead_size = lookahead_size; + d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; + d->m_pLZ_code_buf = pLZ_code_buf; + d->m_pLZ_flags = pLZ_flags; + d->m_num_flags_left = num_flags_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + total_lz_bytes = d->m_total_lz_bytes; + pLZ_code_buf = d->m_pLZ_code_buf; + pLZ_flags = d->m_pLZ_flags; + num_flags_left = d->m_num_flags_left; + } + } + } + + d->m_lookahead_pos = lookahead_pos; + d->m_lookahead_size = lookahead_size; + d->m_dict_size = dict_size; + d->m_total_lz_bytes = total_lz_bytes; + d->m_pLZ_code_buf = pLZ_code_buf; + d->m_pLZ_flags = pLZ_flags; + d->m_num_flags_left = num_flags_left; + return MZ_TRUE; +} +#endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + +static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, + mz_uint8 lit) { + d->m_total_lz_bytes++; + *d->m_pLZ_code_buf++ = lit; + *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1); + if (--d->m_num_flags_left == 0) { + d->m_num_flags_left = 8; + d->m_pLZ_flags = d->m_pLZ_code_buf++; + } + d->m_huff_count[0][lit]++; +} + +static MZ_FORCEINLINE void +tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist) { + mz_uint32 s0, s1; + + MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && + (match_dist <= TDEFL_LZ_DICT_SIZE)); + + d->m_total_lz_bytes += match_len; + + d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN); + + match_dist -= 1; + d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF); + d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); + d->m_pLZ_code_buf += 3; + + *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); + if (--d->m_num_flags_left == 0) { + d->m_num_flags_left = 8; + d->m_pLZ_flags = d->m_pLZ_code_buf++; + } + + s0 = s_tdefl_small_dist_sym[match_dist & 511]; + s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127]; + d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++; + + if (match_len >= TDEFL_MIN_MATCH_LEN) + d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; +} + +static mz_bool tdefl_compress_normal(tdefl_compressor *d) { + const mz_uint8 *pSrc = d->m_pSrc; + size_t src_buf_left = d->m_src_buf_left; + tdefl_flush flush = d->m_flush; + + while ((src_buf_left) || ((flush) && (d->m_lookahead_size))) { + mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos; + // Update dictionary and hash chains. Keeps the lookahead size equal to + // TDEFL_MAX_MATCH_LEN. + if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1)) { + mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & + TDEFL_LZ_DICT_SIZE_MASK, + ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2; + mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] + << TDEFL_LZ_HASH_SHIFT) ^ + d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK]; + mz_uint num_bytes_to_process = (mz_uint)MZ_MIN( + src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size); + const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process; + src_buf_left -= num_bytes_to_process; + d->m_lookahead_size += num_bytes_to_process; + while (pSrc != pSrc_end) { + mz_uint8 c = *pSrc++; + d->m_dict[dst_pos] = c; + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; + hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)(ins_pos); + dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; + ins_pos++; + } + } else { + while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) { + mz_uint8 c = *pSrc++; + mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & + TDEFL_LZ_DICT_SIZE_MASK; + src_buf_left--; + d->m_dict[dst_pos] = c; + if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) + d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; + if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN) { + mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2; + mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] + << (TDEFL_LZ_HASH_SHIFT * 2)) ^ + (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] + << TDEFL_LZ_HASH_SHIFT) ^ + c) & + (TDEFL_LZ_HASH_SIZE - 1); + d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; + d->m_hash[hash] = (mz_uint16)(ins_pos); + } + } + } + d->m_dict_size = + MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size); + if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) + break; + + // Simple lazy/greedy parsing state machine. + len_to_move = 1; + cur_match_dist = 0; + cur_match_len = + d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); + cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; + if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS)) { + if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) { + mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK]; + cur_match_len = 0; + while (cur_match_len < d->m_lookahead_size) { + if (d->m_dict[cur_pos + cur_match_len] != c) + break; + cur_match_len++; + } + if (cur_match_len < TDEFL_MIN_MATCH_LEN) + cur_match_len = 0; + else + cur_match_dist = 1; + } + } else { + tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, + d->m_lookahead_size, &cur_match_dist, &cur_match_len); + } + if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && + (cur_match_dist >= 8U * 1024U)) || + (cur_pos == cur_match_dist) || + ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5))) { + cur_match_dist = cur_match_len = 0; + } + if (d->m_saved_match_len) { + if (cur_match_len > d->m_saved_match_len) { + tdefl_record_literal(d, (mz_uint8)d->m_saved_lit); + if (cur_match_len >= 128) { + tdefl_record_match(d, cur_match_len, cur_match_dist); + d->m_saved_match_len = 0; + len_to_move = cur_match_len; + } else { + d->m_saved_lit = d->m_dict[cur_pos]; + d->m_saved_match_dist = cur_match_dist; + d->m_saved_match_len = cur_match_len; + } + } else { + tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist); + len_to_move = d->m_saved_match_len - 1; + d->m_saved_match_len = 0; + } + } else if (!cur_match_dist) + tdefl_record_literal(d, + d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]); + else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || + (cur_match_len >= 128)) { + tdefl_record_match(d, cur_match_len, cur_match_dist); + len_to_move = cur_match_len; + } else { + d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; + d->m_saved_match_dist = cur_match_dist; + d->m_saved_match_len = cur_match_len; + } + // Move the lookahead forward by len_to_move bytes. + d->m_lookahead_pos += len_to_move; + MZ_ASSERT(d->m_lookahead_size >= len_to_move); + d->m_lookahead_size -= len_to_move; + d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, TDEFL_LZ_DICT_SIZE); + // Check if it's time to flush the current LZ codes to the internal output + // buffer. + if ((d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) || + ((d->m_total_lz_bytes > 31 * 1024) && + (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= + d->m_total_lz_bytes) || + (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS)))) { + int n; + d->m_pSrc = pSrc; + d->m_src_buf_left = src_buf_left; + if ((n = tdefl_flush_block(d, 0)) != 0) + return (n < 0) ? MZ_FALSE : MZ_TRUE; + } + } + + d->m_pSrc = pSrc; + d->m_src_buf_left = src_buf_left; + return MZ_TRUE; +} + +static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d) { + if (d->m_pIn_buf_size) { + *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; + } + + if (d->m_pOut_buf_size) { + size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, + d->m_output_flush_remaining); + memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, + d->m_output_buf + d->m_output_flush_ofs, n); + d->m_output_flush_ofs += (mz_uint)n; + d->m_output_flush_remaining -= (mz_uint)n; + d->m_out_buf_ofs += n; + + *d->m_pOut_buf_size = d->m_out_buf_ofs; + } + + return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE + : TDEFL_STATUS_OKAY; +} + +tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, + size_t *pIn_buf_size, void *pOut_buf, + size_t *pOut_buf_size, tdefl_flush flush) { + if (!d) { + if (pIn_buf_size) + *pIn_buf_size = 0; + if (pOut_buf_size) + *pOut_buf_size = 0; + return TDEFL_STATUS_BAD_PARAM; + } + + d->m_pIn_buf = pIn_buf; + d->m_pIn_buf_size = pIn_buf_size; + d->m_pOut_buf = pOut_buf; + d->m_pOut_buf_size = pOut_buf_size; + d->m_pSrc = (const mz_uint8 *)(pIn_buf); + d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0; + d->m_out_buf_ofs = 0; + d->m_flush = flush; + + if (((d->m_pPut_buf_func != NULL) == + ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || + (d->m_prev_return_status != TDEFL_STATUS_OKAY) || + (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || + (pIn_buf_size && *pIn_buf_size && !pIn_buf) || + (pOut_buf_size && *pOut_buf_size && !pOut_buf)) { + if (pIn_buf_size) + *pIn_buf_size = 0; + if (pOut_buf_size) + *pOut_buf_size = 0; + return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM); + } + d->m_wants_to_finish |= (flush == TDEFL_FINISH); + + if ((d->m_output_flush_remaining) || (d->m_finished)) + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); + +#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) && + ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) && + ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | + TDEFL_RLE_MATCHES)) == 0)) { + if (!tdefl_compress_fast(d)) + return d->m_prev_return_status; + } else +#endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN + { + if (!tdefl_compress_normal(d)) + return d->m_prev_return_status; + } + + if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && + (pIn_buf)) + d->m_adler32 = + (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, + d->m_pSrc - (const mz_uint8 *)pIn_buf); + + if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && + (!d->m_output_flush_remaining)) { + if (tdefl_flush_block(d, flush) < 0) + return d->m_prev_return_status; + d->m_finished = (flush == TDEFL_FINISH); + if (flush == TDEFL_FULL_FLUSH) { + MZ_CLEAR_OBJ(d->m_hash); + MZ_CLEAR_OBJ(d->m_next); + d->m_dict_size = 0; + } + } + + return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); +} + +tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, + size_t in_buf_size, tdefl_flush flush) { + MZ_ASSERT(d->m_pPut_buf_func); + return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush); +} + +tdefl_status tdefl_init(tdefl_compressor *d, + tdefl_put_buf_func_ptr pPut_buf_func, + void *pPut_buf_user, int flags) { + d->m_pPut_buf_func = pPut_buf_func; + d->m_pPut_buf_user = pPut_buf_user; + d->m_flags = (mz_uint)(flags); + d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; + d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0; + d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; + if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) + MZ_CLEAR_OBJ(d->m_hash); + d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = + d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0; + d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = + d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0; + d->m_pLZ_code_buf = d->m_lz_code_buf + 1; + d->m_pLZ_flags = d->m_lz_code_buf; + d->m_num_flags_left = 8; + d->m_pOutput_buf = d->m_output_buf; + d->m_pOutput_buf_end = d->m_output_buf; + d->m_prev_return_status = TDEFL_STATUS_OKAY; + d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; + d->m_adler32 = 1; + d->m_pIn_buf = NULL; + d->m_pOut_buf = NULL; + d->m_pIn_buf_size = NULL; + d->m_pOut_buf_size = NULL; + d->m_flush = TDEFL_NO_FLUSH; + d->m_pSrc = NULL; + d->m_src_buf_left = 0; + d->m_out_buf_ofs = 0; + memset(&d->m_huff_count[0][0], 0, + sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); + memset(&d->m_huff_count[1][0], 0, + sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); + return TDEFL_STATUS_OKAY; +} + +tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d) { + return d->m_prev_return_status; +} + +mz_uint32 tdefl_get_adler32(tdefl_compressor *d) { return d->m_adler32; } + +mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, + tdefl_put_buf_func_ptr pPut_buf_func, + void *pPut_buf_user, int flags) { + tdefl_compressor *pComp; + mz_bool succeeded; + if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) + return MZ_FALSE; + pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); + if (!pComp) + return MZ_FALSE; + succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == + TDEFL_STATUS_OKAY); + succeeded = + succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == + TDEFL_STATUS_DONE); + MZ_FREE(pComp); + return succeeded; +} + +typedef struct { + size_t m_size, m_capacity; + mz_uint8 *m_pBuf; + mz_bool m_expandable; +} tdefl_output_buffer; + +static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, + void *pUser) { + tdefl_output_buffer *p = (tdefl_output_buffer *)pUser; + size_t new_size = p->m_size + len; + if (new_size > p->m_capacity) { + size_t new_capacity = p->m_capacity; + mz_uint8 *pNew_buf; + if (!p->m_expandable) + return MZ_FALSE; + do { + new_capacity = MZ_MAX(128U, new_capacity << 1U); + } while (new_size > new_capacity); + pNew_buf = (mz_uint8 *)MZ_REALLOC(p->m_pBuf, new_capacity); + if (!pNew_buf) + return MZ_FALSE; + p->m_pBuf = pNew_buf; + p->m_capacity = new_capacity; + } + memcpy((mz_uint8 *)p->m_pBuf + p->m_size, pBuf, len); + p->m_size = new_size; + return MZ_TRUE; +} + +void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, + size_t *pOut_len, int flags) { + tdefl_output_buffer out_buf; + MZ_CLEAR_OBJ(out_buf); + if (!pOut_len) + return MZ_FALSE; + else + *pOut_len = 0; + out_buf.m_expandable = MZ_TRUE; + if (!tdefl_compress_mem_to_output( + pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) + return NULL; + *pOut_len = out_buf.m_size; + return out_buf.m_pBuf; +} + +size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, + const void *pSrc_buf, size_t src_buf_len, + int flags) { + tdefl_output_buffer out_buf; + MZ_CLEAR_OBJ(out_buf); + if (!pOut_buf) + return 0; + out_buf.m_pBuf = (mz_uint8 *)pOut_buf; + out_buf.m_capacity = out_buf_len; + if (!tdefl_compress_mem_to_output( + pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) + return 0; + return out_buf.m_size; +} + +#ifndef MINIZ_NO_ZLIB_APIS +static const mz_uint s_tdefl_num_probes[11] = {0, 1, 6, 32, 16, 32, + 128, 256, 512, 768, 1500}; + +// level may actually range from [0,10] (10 is a "hidden" max level, where we +// want a bit more compression and it's fine if throughput to fall off a cliff +// on some files). +mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, + int strategy) { + mz_uint comp_flags = + s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | + ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); + if (window_bits > 0) + comp_flags |= TDEFL_WRITE_ZLIB_HEADER; + + if (!level) + comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; + else if (strategy == MZ_FILTERED) + comp_flags |= TDEFL_FILTER_MATCHES; + else if (strategy == MZ_HUFFMAN_ONLY) + comp_flags &= ~TDEFL_MAX_PROBES_MASK; + else if (strategy == MZ_FIXED) + comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS; + else if (strategy == MZ_RLE) + comp_flags |= TDEFL_RLE_MATCHES; + + return comp_flags; +} +#endif // MINIZ_NO_ZLIB_APIS + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4204) // nonstandard extension used : non-constant + // aggregate initializer (also supported by GNU + // C and C99, so no big deal) +#endif + +// Simple PNG writer function by Alex Evans, 2011. Released into the public +// domain: https://gist.github.com/908299, more context at +// http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/. +// This is actually a modification of Alex's original code so PNG files +// generated by this function pass pngcheck. +void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, + int h, int num_chans, + size_t *pLen_out, + mz_uint level, mz_bool flip) { + // Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was + // defined. + static const mz_uint s_tdefl_png_num_probes[11] = { + 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500}; + tdefl_compressor *pComp = + (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); + tdefl_output_buffer out_buf; + int i, bpl = w * num_chans, y, z; + mz_uint32 c; + *pLen_out = 0; + if (!pComp) + return NULL; + MZ_CLEAR_OBJ(out_buf); + out_buf.m_expandable = MZ_TRUE; + out_buf.m_capacity = 57 + MZ_MAX(64, (1 + bpl) * h); + if (NULL == (out_buf.m_pBuf = (mz_uint8 *)MZ_MALLOC(out_buf.m_capacity))) { + MZ_FREE(pComp); + return NULL; + } + // write dummy header + for (z = 41; z; --z) + tdefl_output_buffer_putter(&z, 1, &out_buf); + // compress image data + tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, + s_tdefl_png_num_probes[MZ_MIN(10, level)] | + TDEFL_WRITE_ZLIB_HEADER); + for (y = 0; y < h; ++y) { + tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); + tdefl_compress_buffer(pComp, + (mz_uint8 *)pImage + (flip ? (h - 1 - y) : y) * bpl, + bpl, TDEFL_NO_FLUSH); + } + if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != + TDEFL_STATUS_DONE) { + MZ_FREE(pComp); + MZ_FREE(out_buf.m_pBuf); + return NULL; + } + // write real header + *pLen_out = out_buf.m_size - 41; + { + static const mz_uint8 chans[] = {0x00, 0x00, 0x04, 0x02, 0x06}; + mz_uint8 pnghdr[41] = {0x89, + 0x50, + 0x4e, + 0x47, + 0x0d, + 0x0a, + 0x1a, + 0x0a, + 0x00, + 0x00, + 0x00, + 0x0d, + 0x49, + 0x48, + 0x44, + 0x52, + 0, + 0, + (mz_uint8)(w >> 8), + (mz_uint8)w, + 0, + 0, + (mz_uint8)(h >> 8), + (mz_uint8)h, + 8, + chans[num_chans], + 0, + 0, + 0, + 0, + 0, + 0, + 0, + (mz_uint8)(*pLen_out >> 24), + (mz_uint8)(*pLen_out >> 16), + (mz_uint8)(*pLen_out >> 8), + (mz_uint8)*pLen_out, + 0x49, + 0x44, + 0x41, + 0x54}; + c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, pnghdr + 12, 17); + for (i = 0; i < 4; ++i, c <<= 8) + ((mz_uint8 *)(pnghdr + 29))[i] = (mz_uint8)(c >> 24); + memcpy(out_buf.m_pBuf, pnghdr, 41); + } + // write footer (IDAT CRC-32, followed by IEND chunk) + if (!tdefl_output_buffer_putter( + "\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) { + *pLen_out = 0; + MZ_FREE(pComp); + MZ_FREE(out_buf.m_pBuf); + return NULL; + } + c = (mz_uint32)mz_crc32(MZ_CRC32_INIT, out_buf.m_pBuf + 41 - 4, + *pLen_out + 4); + for (i = 0; i < 4; ++i, c <<= 8) + (out_buf.m_pBuf + out_buf.m_size - 16)[i] = (mz_uint8)(c >> 24); + // compute final size of file, grab compressed data buffer and return + *pLen_out += 57; + MZ_FREE(pComp); + return out_buf.m_pBuf; +} +void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, + int num_chans, size_t *pLen_out) { + // Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we + // can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's + // where #defined out) + return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, + pLen_out, 6, MZ_FALSE); +} + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +// ------------------- .ZIP archive reading + +#ifndef MINIZ_NO_ARCHIVE_APIS + +#ifdef MINIZ_NO_STDIO +#define MZ_FILE void * +#else +#include +#include + +#if defined(_MSC_VER) +static FILE *mz_fopen(const char *pFilename, const char *pMode) { + FILE *pFile = NULL; + fopen_s(&pFile, pFilename, pMode); + return pFile; +} +static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) { + FILE *pFile = NULL; + if (freopen_s(&pFile, pPath, pMode, pStream)) + return NULL; + return pFile; +} +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FILE FILE +#define MZ_FOPEN mz_fopen +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 _ftelli64 +#define MZ_FSEEK64 _fseeki64 +#define MZ_FILE_STAT_STRUCT _stat +#define MZ_FILE_STAT _stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN mz_freopen +#define MZ_DELETE_FILE remove +#elif defined(__MINGW32__) +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FILE FILE +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftell +#define MZ_FSEEK64 fseek +#define MZ_FILE_STAT_STRUCT _stat +#define MZ_FILE_STAT _stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +#define MZ_DELETE_FILE remove +#elif defined(__TINYC__) +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FILE FILE +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftell +#define MZ_FSEEK64 fseek +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +#define MZ_DELETE_FILE remove +#elif defined(__GNUC__) && _LARGEFILE64_SOURCE +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FILE FILE +#define MZ_FOPEN(f, m) fopen64(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#define MZ_FTELL64 ftello64 +#define MZ_FSEEK64 fseeko64 +#define MZ_FILE_STAT_STRUCT stat64 +#define MZ_FILE_STAT stat64 +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(p, m, s) freopen64(p, m, s) +#define MZ_DELETE_FILE remove +#else +#ifndef MINIZ_NO_TIME +#include +#endif +#define MZ_FILE FILE +#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FCLOSE fclose +#define MZ_FREAD fread +#define MZ_FWRITE fwrite +#if _FILE_OFFSET_BITS == 64 || _POSIX_C_SOURCE >= 200112L +#define MZ_FTELL64 ftello +#define MZ_FSEEK64 fseeko +#else +#define MZ_FTELL64 ftell +#define MZ_FSEEK64 fseek +#endif +#define MZ_FILE_STAT_STRUCT stat +#define MZ_FILE_STAT stat +#define MZ_FFLUSH fflush +#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +#define MZ_DELETE_FILE remove +#endif // #ifdef _MSC_VER +#endif // #ifdef MINIZ_NO_STDIO + +#define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c)) + +// Various ZIP archive enums. To completely avoid cross platform compiler +// alignment and platform endian issues, miniz.c doesn't use structs for any of +// this stuff. +enum { + // ZIP archive identifiers and record sizes + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, + MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, + MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50, + MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22, + + /* ZIP64 archive identifier and record sizes */ + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06064b50, + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG = 0x07064b50, + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE = 56, + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE = 20, + MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID = 0x0001, + MZ_ZIP_DATA_DESCRIPTOR_ID = 0x08074b50, + MZ_ZIP_DATA_DESCRIPTER_SIZE64 = 24, + MZ_ZIP_DATA_DESCRIPTER_SIZE32 = 16, + + // Central directory header record offsets + MZ_ZIP_CDH_SIG_OFS = 0, + MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, + MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, + MZ_ZIP_CDH_BIT_FLAG_OFS = 8, + MZ_ZIP_CDH_METHOD_OFS = 10, + MZ_ZIP_CDH_FILE_TIME_OFS = 12, + MZ_ZIP_CDH_FILE_DATE_OFS = 14, + MZ_ZIP_CDH_CRC32_OFS = 16, + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, + MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, + MZ_ZIP_CDH_EXTRA_LEN_OFS = 30, + MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, + MZ_ZIP_CDH_DISK_START_OFS = 34, + MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, + MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42, + // Local directory header offsets + MZ_ZIP_LDH_SIG_OFS = 0, + MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, + MZ_ZIP_LDH_BIT_FLAG_OFS = 6, + MZ_ZIP_LDH_METHOD_OFS = 8, + MZ_ZIP_LDH_FILE_TIME_OFS = 10, + MZ_ZIP_LDH_FILE_DATE_OFS = 12, + MZ_ZIP_LDH_CRC32_OFS = 14, + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22, + MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, + MZ_ZIP_LDH_EXTRA_LEN_OFS = 28, + // End of central directory offsets + MZ_ZIP_ECDH_SIG_OFS = 0, + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8, + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, + MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, + MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, + MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20, + + /* ZIP64 End of central directory locator offsets */ + MZ_ZIP64_ECDL_SIG_OFS = 0, /* 4 bytes */ + MZ_ZIP64_ECDL_NUM_DISK_CDIR_OFS = 4, /* 4 bytes */ + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS = 8, /* 8 bytes */ + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS = 16, /* 4 bytes */ + + /* ZIP64 End of central directory header offsets */ + MZ_ZIP64_ECDH_SIG_OFS = 0, /* 4 bytes */ + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS = 4, /* 8 bytes */ + MZ_ZIP64_ECDH_VERSION_MADE_BY_OFS = 12, /* 2 bytes */ + MZ_ZIP64_ECDH_VERSION_NEEDED_OFS = 14, /* 2 bytes */ + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS = 16, /* 4 bytes */ + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS = 20, /* 4 bytes */ + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 24, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS = 32, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_SIZE_OFS = 40, /* 8 bytes */ + MZ_ZIP64_ECDH_CDIR_OFS_OFS = 48, /* 8 bytes */ + MZ_ZIP_VERSION_MADE_BY_DOS_FILESYSTEM_ID = 0, + MZ_ZIP_DOS_DIR_ATTRIBUTE_BITFLAG = 0x10, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_IS_ENCRYPTED = 1, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_COMPRESSED_PATCH_FLAG = 32, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_USES_STRONG_ENCRYPTION = 64, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED = 8192, + MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_UTF8 = 1 << 11 +}; + +typedef struct { + void *m_p; + size_t m_size, m_capacity; + mz_uint m_element_size; +} mz_zip_array; + +struct mz_zip_internal_state_tag { + mz_zip_array m_central_dir; + mz_zip_array m_central_dir_offsets; + mz_zip_array m_sorted_central_dir_offsets; + + /* The flags passed in when the archive is initially opened. */ + uint32_t m_init_flags; + + /* MZ_TRUE if the archive has a zip64 end of central directory headers, etc. + */ + mz_bool m_zip64; + + /* MZ_TRUE if we found zip64 extended info in the central directory (m_zip64 + * will also be slammed to true too, even if we didn't find a zip64 end of + * central dir header, etc.) */ + mz_bool m_zip64_has_extended_info_fields; + + /* These fields are used by the file, FILE, memory, and memory/heap read/write + * helpers. */ + MZ_FILE *m_pFile; + mz_uint64 m_file_archive_start_ofs; + + void *m_pMem; + size_t m_mem_size; + size_t m_mem_capacity; +}; + +#define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) \ + (array_ptr)->m_element_size = element_size +#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) \ + ((element_type *)((array_ptr)->m_p))[index] + +static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, + mz_zip_array *pArray) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p); + memset(pArray, 0, sizeof(mz_zip_array)); +} + +static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, + mz_zip_array *pArray, + size_t min_new_capacity, + mz_uint growing) { + void *pNew_p; + size_t new_capacity = min_new_capacity; + MZ_ASSERT(pArray->m_element_size); + if (pArray->m_capacity >= min_new_capacity) + return MZ_TRUE; + if (growing) { + new_capacity = MZ_MAX(1, pArray->m_capacity); + while (new_capacity < min_new_capacity) + new_capacity *= 2; + } + if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, + pArray->m_element_size, new_capacity))) + return MZ_FALSE; + pArray->m_p = pNew_p; + pArray->m_capacity = new_capacity; + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, + mz_zip_array *pArray, + size_t new_capacity, + mz_uint growing) { + if (new_capacity > pArray->m_capacity) { + if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) + return MZ_FALSE; + } + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, + mz_zip_array *pArray, + size_t new_size, + mz_uint growing) { + if (new_size > pArray->m_capacity) { + if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) + return MZ_FALSE; + } + pArray->m_size = new_size; + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, + mz_zip_array *pArray, + size_t n) { + return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE); +} + +static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, + mz_zip_array *pArray, + const void *pElements, + size_t n) { + if (0 == n) + return MZ_TRUE; + if (!pElements) + return MZ_FALSE; + + size_t orig_size = pArray->m_size; + if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) + return MZ_FALSE; + memcpy((mz_uint8 *)pArray->m_p + orig_size * pArray->m_element_size, + pElements, n * pArray->m_element_size); + return MZ_TRUE; +} + +#ifndef MINIZ_NO_TIME +static time_t mz_zip_dos_to_time_t(int dos_time, int dos_date) { + struct tm tm; + memset(&tm, 0, sizeof(tm)); + tm.tm_isdst = -1; + tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; + tm.tm_mon = ((dos_date >> 5) & 15) - 1; + tm.tm_mday = dos_date & 31; + tm.tm_hour = (dos_time >> 11) & 31; + tm.tm_min = (dos_time >> 5) & 63; + tm.tm_sec = (dos_time << 1) & 62; + return mktime(&tm); +} + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS +static void mz_zip_time_t_to_dos_time(time_t time, mz_uint16 *pDOS_time, + mz_uint16 *pDOS_date) { +#ifdef _MSC_VER + struct tm tm_struct; + struct tm *tm = &tm_struct; + errno_t err = localtime_s(tm, &time); + if (err) { + *pDOS_date = 0; + *pDOS_time = 0; + return; + } +#else + struct tm *tm = localtime(&time); +#endif /* #ifdef _MSC_VER */ + + *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + + ((tm->tm_sec) >> 1)); + *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + + ((tm->tm_mon + 1) << 5) + tm->tm_mday); +} +#endif /* MINIZ_NO_ARCHIVE_WRITING_APIS */ + +#ifndef MINIZ_NO_STDIO +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS +static mz_bool mz_zip_get_file_modified_time(const char *pFilename, + time_t *pTime) { + struct MZ_FILE_STAT_STRUCT file_stat; + + /* On Linux with x86 glibc, this call will fail on large files (I think >= + * 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh. */ + if (MZ_FILE_STAT(pFilename, &file_stat) != 0) + return MZ_FALSE; + + *pTime = file_stat.st_mtime; + + return MZ_TRUE; +} +#endif /* #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS*/ + +static mz_bool mz_zip_set_file_times(const char *pFilename, time_t access_time, + time_t modified_time) { + struct utimbuf t; + + memset(&t, 0, sizeof(t)); + t.actime = access_time; + t.modtime = modified_time; + + return !utime(pFilename, &t); +} +#endif /* #ifndef MINIZ_NO_STDIO */ +#endif /* #ifndef MINIZ_NO_TIME */ + +static MZ_FORCEINLINE mz_bool mz_zip_set_error(mz_zip_archive *pZip, + mz_zip_error err_num) { + if (pZip) + pZip->m_last_error = err_num; + return MZ_FALSE; +} + +static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, + mz_uint32 flags) { + (void)flags; + if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) + return MZ_FALSE; + + if (!pZip->m_pAlloc) + pZip->m_pAlloc = def_alloc_func; + if (!pZip->m_pFree) + pZip->m_pFree = def_free_func; + if (!pZip->m_pRealloc) + pZip->m_pRealloc = def_realloc_func; + + pZip->m_zip_mode = MZ_ZIP_MODE_READING; + pZip->m_archive_size = 0; + pZip->m_central_directory_file_ofs = 0; + pZip->m_total_files = 0; + + if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc( + pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) + return MZ_FALSE; + memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, + sizeof(mz_uint8)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, + sizeof(mz_uint32)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, + sizeof(mz_uint32)); + return MZ_TRUE; +} + +static MZ_FORCEINLINE mz_bool +mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, + const mz_zip_array *pCentral_dir_offsets, + mz_uint l_index, mz_uint r_index) { + const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT( + pCentral_dir_array, mz_uint8, + MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, + l_index)), + *pE; + const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT( + pCentral_dir_array, mz_uint8, + MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index)); + mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), + r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS); + mz_uint8 l = 0, r = 0; + pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pE = pL + MZ_MIN(l_len, r_len); + while (pL < pE) { + if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) + break; + pL++; + pR++; + } + return (pL == pE) ? (l_len < r_len) : (l < r); +} + +#define MZ_SWAP_UINT32(a, b) \ + do { \ + mz_uint32 t = a; \ + a = b; \ + b = t; \ + } \ + MZ_MACRO_END + +// Heap sort of lowercased filenames, used to help accelerate plain central +// directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), +// but it could allocate memory.) +static void +mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip) { + mz_zip_internal_state *pState = pZip->m_pState; + const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; + const mz_zip_array *pCentral_dir = &pState->m_central_dir; + mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT( + &pState->m_sorted_central_dir_offsets, mz_uint32, 0); + const int size = pZip->m_total_files; + int start = (size - 2) >> 1, end; + while (start >= 0) { + int child, root = start; + for (;;) { + if ((child = (root << 1) + 1) >= size) + break; + child += + (((child + 1) < size) && + (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, + pIndices[child], pIndices[child + 1]))); + if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, + pIndices[root], pIndices[child])) + break; + MZ_SWAP_UINT32(pIndices[root], pIndices[child]); + root = child; + } + start--; + } + + end = size - 1; + while (end > 0) { + int child, root = 0; + MZ_SWAP_UINT32(pIndices[end], pIndices[0]); + for (;;) { + if ((child = (root << 1) + 1) >= end) + break; + child += + (((child + 1) < end) && + mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, + pIndices[child], pIndices[child + 1])); + if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, + pIndices[root], pIndices[child])) + break; + MZ_SWAP_UINT32(pIndices[root], pIndices[child]); + root = child; + } + end--; + } +} + +static mz_bool mz_zip_reader_locate_header_sig(mz_zip_archive *pZip, + mz_uint32 record_sig, + mz_uint32 record_size, + mz_int64 *pOfs) { + mz_int64 cur_file_ofs; + mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; + mz_uint8 *pBuf = (mz_uint8 *)buf_u32; + + /* Basic sanity checks - reject files which are too small */ + if (pZip->m_archive_size < record_size) + return MZ_FALSE; + + /* Find the record by scanning the file from the end towards the beginning. */ + cur_file_ofs = + MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0); + for (;;) { + int i, + n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs); + + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n) + return MZ_FALSE; + + for (i = n - 4; i >= 0; --i) { + mz_uint s = MZ_READ_LE32(pBuf + i); + if (s == record_sig) { + if ((pZip->m_archive_size - (cur_file_ofs + i)) >= record_size) + break; + } + } + + if (i >= 0) { + cur_file_ofs += i; + break; + } + + /* Give up if we've searched the entire file, or we've gone back "too far" + * (~64kb) */ + if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= + (MZ_UINT16_MAX + record_size))) + return MZ_FALSE; + + cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0); + } + + *pOfs = cur_file_ofs; + return MZ_TRUE; +} + +static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, + mz_uint flags) { + mz_uint cdir_size = 0, cdir_entries_on_this_disk = 0, num_this_disk = 0, + cdir_disk_index = 0; + mz_uint64 cdir_ofs = 0; + mz_int64 cur_file_ofs = 0; + const mz_uint8 *p; + + mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; + mz_uint8 *pBuf = (mz_uint8 *)buf_u32; + mz_bool sort_central_dir = + ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0); + mz_uint32 zip64_end_of_central_dir_locator_u32 + [(MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + sizeof(mz_uint32) - 1) / + sizeof(mz_uint32)]; + mz_uint8 *pZip64_locator = (mz_uint8 *)zip64_end_of_central_dir_locator_u32; + + mz_uint32 zip64_end_of_central_dir_header_u32 + [(MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / + sizeof(mz_uint32)]; + mz_uint8 *pZip64_end_of_central_dir = + (mz_uint8 *)zip64_end_of_central_dir_header_u32; + + mz_uint64 zip64_end_of_central_dir_ofs = 0; + + /* Basic sanity checks - reject files which are too small, and check the first + * 4 bytes of the file to make sure a local header is there. */ + if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (!mz_zip_reader_locate_header_sig( + pZip, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG, + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE, &cur_file_ofs)) + return mz_zip_set_error(pZip, MZ_ZIP_FAILED_FINDING_CENTRAL_DIR); + + /* Read and verify the end of central directory record. */ + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + if (MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (cur_file_ofs >= (MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE + + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) { + if (pZip->m_pRead(pZip->m_pIO_opaque, + cur_file_ofs - MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE, + pZip64_locator, + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) == + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIZE) { + if (MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_SIG_OFS) == + MZ_ZIP64_END_OF_CENTRAL_DIR_LOCATOR_SIG) { + zip64_end_of_central_dir_ofs = MZ_READ_LE64( + pZip64_locator + MZ_ZIP64_ECDL_REL_OFS_TO_ZIP64_ECDR_OFS); + if (zip64_end_of_central_dir_ofs > + (pZip->m_archive_size - MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE)) + return mz_zip_set_error(pZip, MZ_ZIP_NOT_AN_ARCHIVE); + + if (pZip->m_pRead(pZip->m_pIO_opaque, zip64_end_of_central_dir_ofs, + pZip64_end_of_central_dir, + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) == + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE) { + if (MZ_READ_LE32(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIG_OFS) == + MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIG) { + pZip->m_pState->m_zip64 = MZ_TRUE; + } + } + } + } + } + + pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS); + cdir_entries_on_this_disk = + MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); + num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS); + cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS); + cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS); + cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS); + + if (pZip->m_pState->m_zip64) { + mz_uint32 zip64_total_num_of_disks = + MZ_READ_LE32(pZip64_locator + MZ_ZIP64_ECDL_TOTAL_NUMBER_OF_DISKS_OFS); + mz_uint64 zip64_cdir_total_entries = MZ_READ_LE64( + pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_TOTAL_ENTRIES_OFS); + mz_uint64 zip64_cdir_total_entries_on_this_disk = MZ_READ_LE64( + pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS); + mz_uint64 zip64_size_of_end_of_central_dir_record = MZ_READ_LE64( + pZip64_end_of_central_dir + MZ_ZIP64_ECDH_SIZE_OF_RECORD_OFS); + mz_uint64 zip64_size_of_central_directory = + MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_SIZE_OFS); + + if (zip64_size_of_end_of_central_dir_record < + (MZ_ZIP64_END_OF_CENTRAL_DIR_HEADER_SIZE - 12)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if (zip64_total_num_of_disks != 1U) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + /* Check for miniz's practical limits */ + if (zip64_cdir_total_entries > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + + pZip->m_total_files = (mz_uint32)zip64_cdir_total_entries; + + if (zip64_cdir_total_entries_on_this_disk > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_TOO_MANY_FILES); + + cdir_entries_on_this_disk = + (mz_uint32)zip64_cdir_total_entries_on_this_disk; + + /* Check for miniz's current practical limits (sorry, this should be enough + * for millions of files) */ + if (zip64_size_of_central_directory > MZ_UINT32_MAX) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_CDIR_SIZE); + + cdir_size = (mz_uint32)zip64_size_of_central_directory; + + num_this_disk = MZ_READ_LE32(pZip64_end_of_central_dir + + MZ_ZIP64_ECDH_NUM_THIS_DISK_OFS); + + cdir_disk_index = MZ_READ_LE32(pZip64_end_of_central_dir + + MZ_ZIP64_ECDH_NUM_DISK_CDIR_OFS); + + cdir_ofs = + MZ_READ_LE64(pZip64_end_of_central_dir + MZ_ZIP64_ECDH_CDIR_OFS_OFS); + } + + if (pZip->m_total_files != cdir_entries_on_this_disk) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (((num_this_disk | cdir_disk_index) != 0) && + ((num_this_disk != 1) || (cdir_disk_index != 1))) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (cdir_size < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + pZip->m_central_directory_file_ofs = cdir_ofs; + + if (pZip->m_total_files) { + mz_uint i, n; + /* Read the entire central directory into a heap block, and allocate another + * heap block to hold the unsorted central dir file record offsets, and + * possibly another to hold the sorted indices. */ + if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, + MZ_FALSE)) || + (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, + pZip->m_total_files, MZ_FALSE))) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if (sort_central_dir) { + if (!mz_zip_array_resize(pZip, + &pZip->m_pState->m_sorted_central_dir_offsets, + pZip->m_total_files, MZ_FALSE)) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + } + + if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, + pZip->m_pState->m_central_dir.m_p, + cdir_size) != cdir_size) + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + + /* Now create an index into the central directory file records, do some + * basic sanity checking on each record */ + p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p; + for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) { + mz_uint total_header_size, disk_index, bit_flags, filename_size, + ext_data_size; + mz_uint64 comp_size, decomp_size, local_header_ofs; + + if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || + (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, + i) = + (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p); + + if (sort_central_dir) + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, + mz_uint32, i) = i; + + comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + filename_size = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + ext_data_size = MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS); + + if ((!pZip->m_pState->m_zip64_has_extended_info_fields) && + (ext_data_size) && + (MZ_MAX(MZ_MAX(comp_size, decomp_size), local_header_ofs) == + MZ_UINT32_MAX)) { + /* Attempt to find zip64 extended information field in the entry's extra + * data */ + mz_uint32 extra_size_remaining = ext_data_size; + + if (extra_size_remaining) { + const mz_uint8 *pExtra_data; + void *buf = NULL; + + if (MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + ext_data_size > + n) { + buf = MZ_MALLOC(ext_data_size); + if (buf == NULL) + return mz_zip_set_error(pZip, MZ_ZIP_ALLOC_FAILED); + + if (pZip->m_pRead(pZip->m_pIO_opaque, + cdir_ofs + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + + filename_size, + buf, ext_data_size) != ext_data_size) { + MZ_FREE(buf); + return mz_zip_set_error(pZip, MZ_ZIP_FILE_READ_FAILED); + } + + pExtra_data = (mz_uint8 *)buf; + } else { + pExtra_data = p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size; + } + + do { + mz_uint32 field_id; + mz_uint32 field_data_size; + + if (extra_size_remaining < (sizeof(mz_uint16) * 2)) { + MZ_FREE(buf); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + field_id = MZ_READ_LE16(pExtra_data); + field_data_size = MZ_READ_LE16(pExtra_data + sizeof(mz_uint16)); + + if ((field_data_size + sizeof(mz_uint16) * 2) > + extra_size_remaining) { + MZ_FREE(buf); + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + if (field_id == MZ_ZIP64_EXTENDED_INFORMATION_FIELD_HEADER_ID) { + /* Ok, the archive didn't have any zip64 headers but it uses a + * zip64 extended information field so mark it as zip64 anyway + * (this can occur with infozip's zip util when it reads + * compresses files from stdin). */ + pZip->m_pState->m_zip64 = MZ_TRUE; + pZip->m_pState->m_zip64_has_extended_info_fields = MZ_TRUE; + break; + } + + pExtra_data += sizeof(mz_uint16) * 2 + field_data_size; + extra_size_remaining = + extra_size_remaining - sizeof(mz_uint16) * 2 - field_data_size; + } while (extra_size_remaining); + + MZ_FREE(buf); + } + } + + /* I've seen archives that aren't marked as zip64 that uses zip64 ext + * data, argh */ + if ((comp_size != MZ_UINT32_MAX) && (decomp_size != MZ_UINT32_MAX)) { + if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && + (decomp_size != comp_size)) || + (decomp_size && !comp_size)) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS); + if ((disk_index == MZ_UINT16_MAX) || + ((disk_index != num_this_disk) && (disk_index != 1))) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_MULTIDISK); + + if (comp_size != MZ_UINT32_MAX) { + if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + } + + bit_flags = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + if (bit_flags & MZ_ZIP_GENERAL_PURPOSE_BIT_FLAG_LOCAL_DIR_IS_MASKED) + return mz_zip_set_error(pZip, MZ_ZIP_UNSUPPORTED_ENCRYPTION); + + if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > + n) + return mz_zip_set_error(pZip, MZ_ZIP_INVALID_HEADER_OR_CORRUPTED); + + n -= total_header_size; + p += total_header_size; + } + } + + if (sort_central_dir) + mz_zip_reader_sort_central_dir_offsets_by_filename(pZip); + + return MZ_TRUE; +} + +mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, + mz_uint32 flags) { + if ((!pZip) || (!pZip->m_pRead)) + return MZ_FALSE; + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + pZip->m_archive_size = size; + if (!mz_zip_reader_read_central_dir(pZip, flags)) { + mz_zip_reader_end(pZip); + return MZ_FALSE; + } + return MZ_TRUE; +} + +static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, + void *pBuf, size_t n) { + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + size_t s = (file_ofs >= pZip->m_archive_size) + ? 0 + : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n); + memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s); + return s; +} + +mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, + size_t size, mz_uint32 flags) { + if (!mz_zip_reader_init_internal(pZip, flags)) + return MZ_FALSE; + pZip->m_archive_size = size; + pZip->m_pRead = mz_zip_mem_read_func; + pZip->m_pIO_opaque = pZip; +#ifdef __cplusplus + pZip->m_pState->m_pMem = const_cast(pMem); +#else + pZip->m_pState->m_pMem = (void *)pMem; +#endif + pZip->m_pState->m_mem_size = size; + if (!mz_zip_reader_read_central_dir(pZip, flags)) { + mz_zip_reader_end(pZip); + return MZ_FALSE; + } + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, + void *pBuf, size_t n) { + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + if (((mz_int64)file_ofs < 0) || + (((cur_ofs != (mz_int64)file_ofs)) && + (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) + return 0; + return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile); +} + +mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, + mz_uint32 flags) { + mz_uint64 file_size; + MZ_FILE *pFile = MZ_FOPEN(pFilename, "rb"); + if (!pFile) + return MZ_FALSE; + if (MZ_FSEEK64(pFile, 0, SEEK_END)) { + MZ_FCLOSE(pFile); + return MZ_FALSE; + } + file_size = MZ_FTELL64(pFile); + if (!mz_zip_reader_init_internal(pZip, flags)) { + MZ_FCLOSE(pFile); + return MZ_FALSE; + } + pZip->m_pRead = mz_zip_file_read_func; + pZip->m_pIO_opaque = pZip; + pZip->m_pState->m_pFile = pFile; + pZip->m_archive_size = file_size; + if (!mz_zip_reader_read_central_dir(pZip, flags)) { + mz_zip_reader_end(pZip); + return MZ_FALSE; + } + return MZ_TRUE; +} +#endif // #ifndef MINIZ_NO_STDIO + +mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip) { + return pZip ? pZip->m_total_files : 0; +} + +static MZ_FORCEINLINE const mz_uint8 * +mz_zip_reader_get_cdh(mz_zip_archive *pZip, mz_uint file_index) { + if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files) || + (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return NULL; + return &MZ_ZIP_ARRAY_ELEMENT( + &pZip->m_pState->m_central_dir, mz_uint8, + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, + file_index)); +} + +mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, + mz_uint file_index) { + mz_uint m_bit_flag; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + if (!p) + return MZ_FALSE; + m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + return (m_bit_flag & 1); +} + +mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, + mz_uint file_index) { + mz_uint filename_len, external_attr; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + if (!p) + return MZ_FALSE; + + // First see if the filename ends with a '/' character. + filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + if (filename_len) { + if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/') + return MZ_TRUE; + } + + // Bugfix: This code was also checking if the internal attribute was non-zero, + // which wasn't correct. Most/all zip writers (hopefully) set DOS + // file/directory attributes in the low 16-bits, so check for the DOS + // directory flag and ignore the source OS ID in the created by field. + // FIXME: Remove this check? Is it necessary - we already check the filename. + external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); + if ((external_attr & 0x10) != 0) + return MZ_TRUE; + + return MZ_FALSE; +} + +mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, + mz_zip_archive_file_stat *pStat) { + mz_uint n; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + if ((!p) || (!pStat)) + return MZ_FALSE; + + // Unpack the central directory record. + pStat->m_file_index = file_index; + pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT( + &pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index); + pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS); + pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS); + pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); + pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); +#ifndef MINIZ_NO_TIME + pStat->m_time = + mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), + MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS)); +#endif + pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS); + pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); + pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); + pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + + // Copy as much of the filename and comment as possible. + n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1); + memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); + pStat->m_filename[n] = '\0'; + + n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); + n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1); + pStat->m_comment_size = n; + memcpy(pStat->m_comment, + p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), + n); + pStat->m_comment[n] = '\0'; + + return MZ_TRUE; +} + +mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, + char *pFilename, mz_uint filename_buf_size) { + mz_uint n; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + if (!p) { + if (filename_buf_size) + pFilename[0] = '\0'; + return 0; + } + n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); + if (filename_buf_size) { + n = MZ_MIN(n, filename_buf_size - 1); + memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); + pFilename[n] = '\0'; + } + return n + 1; +} + +static MZ_FORCEINLINE mz_bool mz_zip_reader_string_equal(const char *pA, + const char *pB, + mz_uint len, + mz_uint flags) { + mz_uint i; + if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE) + return 0 == memcmp(pA, pB, len); + for (i = 0; i < len; ++i) + if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i])) + return MZ_FALSE; + return MZ_TRUE; +} + +static MZ_FORCEINLINE int +mz_zip_reader_filename_compare(const mz_zip_array *pCentral_dir_array, + const mz_zip_array *pCentral_dir_offsets, + mz_uint l_index, const char *pR, mz_uint r_len) { + const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT( + pCentral_dir_array, mz_uint8, + MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, + l_index)), + *pE; + mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS); + mz_uint8 l = 0, r = 0; + pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + pE = pL + MZ_MIN(l_len, r_len); + while (pL < pE) { + if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) + break; + pL++; + pR++; + } + return (pL == pE) ? (int)(l_len - r_len) : (l - r); +} + +static int mz_zip_reader_locate_file_binary_search(mz_zip_archive *pZip, + const char *pFilename) { + mz_zip_internal_state *pState = pZip->m_pState; + const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; + const mz_zip_array *pCentral_dir = &pState->m_central_dir; + mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT( + &pState->m_sorted_central_dir_offsets, mz_uint32, 0); + const int size = pZip->m_total_files; + const mz_uint filename_len = (mz_uint)strlen(pFilename); + int l = 0, h = size - 1; + while (l <= h) { + int m = (l + h) >> 1, file_index = pIndices[m], + comp = + mz_zip_reader_filename_compare(pCentral_dir, pCentral_dir_offsets, + file_index, pFilename, filename_len); + if (!comp) + return file_index; + else if (comp < 0) + l = m + 1; + else + h = m - 1; + } + return -1; +} + +int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, + const char *pComment, mz_uint flags) { + mz_uint file_index; + size_t name_len, comment_len; + if ((!pZip) || (!pZip->m_pState) || (!pName) || + (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return -1; + if (((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && + (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size)) + return mz_zip_reader_locate_file_binary_search(pZip, pName); + name_len = strlen(pName); + if (name_len > 0xFFFF) + return -1; + comment_len = pComment ? strlen(pComment) : 0; + if (comment_len > 0xFFFF) + return -1; + for (file_index = 0; file_index < pZip->m_total_files; file_index++) { + const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT( + &pZip->m_pState->m_central_dir, mz_uint8, + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, + file_index)); + mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); + const char *pFilename = + (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + if (filename_len < name_len) + continue; + if (comment_len) { + mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), + file_comment_len = + MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS); + const char *pFile_comment = pFilename + filename_len + file_extra_len; + if ((file_comment_len != comment_len) || + (!mz_zip_reader_string_equal(pComment, pFile_comment, + file_comment_len, flags))) + continue; + } + if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len)) { + int ofs = filename_len - 1; + do { + if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || + (pFilename[ofs] == ':')) + break; + } while (--ofs >= 0); + ofs++; + pFilename += ofs; + filename_len -= ofs; + } + if ((filename_len == name_len) && + (mz_zip_reader_string_equal(pName, pFilename, filename_len, flags))) + return file_index; + } + return -1; +} + +mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, + mz_uint file_index, void *pBuf, + size_t buf_size, mz_uint flags, + void *pUser_read_buf, + size_t user_read_buf_size) { + int status = TINFL_STATUS_DONE; + mz_uint64 needed_size, cur_file_ofs, comp_remaining, + out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail; + mz_zip_archive_file_stat file_stat; + void *pRead_buf; + mz_uint32 + local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / + sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + tinfl_decompressor inflator; + + if ((buf_size) && (!pBuf)) + return MZ_FALSE; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + // Empty file, or a directory (but not always a directory - I've seen odd zips + // with directories that have compressed data which inflates to 0 bytes) + if (!file_stat.m_comp_size) + return MZ_TRUE; + + // Entry is a subdirectory (I've seen old zips with dir entries which have + // compressed deflate data which inflates to 0 bytes, but these entries claim + // to uncompress to 512 bytes in the headers). I'm torn how to handle this + // case - should it fail instead? + if (mz_zip_reader_is_file_a_directory(pZip, file_index)) + return MZ_TRUE; + + // Encryption and patch files are not supported. + if (file_stat.m_bit_flag & (1 | 32)) + return MZ_FALSE; + + // This function only supports stored and deflate. + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && + (file_stat.m_method != MZ_DEFLATED)) + return MZ_FALSE; + + // Ensure supplied output buffer is large enough. + needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size + : file_stat.m_uncomp_size; + if (buf_size < needed_size) + return MZ_FALSE; + + // Read and parse the local directory entry. + cur_file_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return MZ_FALSE; + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return MZ_FALSE; + + cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) + return MZ_FALSE; + + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) { + // The file is stored or the caller has requested the compressed data. + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, + (size_t)needed_size) != needed_size) + return MZ_FALSE; + return ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) != 0) || + (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, + (size_t)file_stat.m_uncomp_size) == file_stat.m_crc32); + } + + // Decompress the file either directly from memory or from a file input + // buffer. + tinfl_init(&inflator); + + if (pZip->m_pState->m_pMem) { + // Read directly from the archive in memory. + pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; + read_buf_size = read_buf_avail = file_stat.m_comp_size; + comp_remaining = 0; + } else if (pUser_read_buf) { + // Use a user provided read buffer. + if (!user_read_buf_size) + return MZ_FALSE; + pRead_buf = (mz_uint8 *)pUser_read_buf; + read_buf_size = user_read_buf_size; + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } else { + // Temporarily allocate a read buffer. + read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); +#ifdef _MSC_VER + if (((0, sizeof(size_t) == sizeof(mz_uint32))) && + (read_buf_size > 0x7FFFFFFF)) +#else + if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) +#endif + return MZ_FALSE; + if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, + (size_t)read_buf_size))) + return MZ_FALSE; + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + + do { + size_t in_buf_size, + out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs); + if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, + (size_t)read_buf_avail) != read_buf_avail) { + status = TINFL_STATUS_FAILED; + break; + } + cur_file_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + read_buf_ofs = 0; + } + in_buf_size = (size_t)read_buf_avail; + status = tinfl_decompress( + &inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, + (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, + TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | + (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0)); + read_buf_avail -= in_buf_size; + read_buf_ofs += in_buf_size; + out_buf_ofs += out_buf_size; + } while (status == TINFL_STATUS_NEEDS_MORE_INPUT); + + if (status == TINFL_STATUS_DONE) { + // Make sure the entire file was decompressed, and check its CRC. + if ((out_buf_ofs != file_stat.m_uncomp_size) || + (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, + (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32)) + status = TINFL_STATUS_FAILED; + } + + if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf)) + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + + return status == TINFL_STATUS_DONE; +} + +mz_bool mz_zip_reader_extract_file_to_mem_no_alloc( + mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, + mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) { + int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); + if (file_index < 0) + return MZ_FALSE; + return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, + flags, pUser_read_buf, + user_read_buf_size); +} + +mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, + void *pBuf, size_t buf_size, + mz_uint flags) { + return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, + flags, NULL, 0); +} + +mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, + const char *pFilename, void *pBuf, + size_t buf_size, mz_uint flags) { + return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, + buf_size, flags, NULL, 0); +} + +void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, + size_t *pSize, mz_uint flags) { + mz_uint64 comp_size, uncomp_size, alloc_size; + const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); + void *pBuf; + + if (pSize) + *pSize = 0; + if (!p) + return NULL; + + comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); + + alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size; +#ifdef _MSC_VER + if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) +#else + if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) +#endif + return NULL; + if (NULL == + (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size))) + return NULL; + + if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, + flags)) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return NULL; + } + + if (pSize) + *pSize = (size_t)alloc_size; + return pBuf; +} + +void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, + const char *pFilename, size_t *pSize, + mz_uint flags) { + int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); + if (file_index < 0) { + if (pSize) + *pSize = 0; + return MZ_FALSE; + } + return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags); +} + +mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, + mz_uint file_index, + mz_file_write_func pCallback, + void *pOpaque, mz_uint flags) { + int status = TINFL_STATUS_DONE; + mz_uint file_crc32 = MZ_CRC32_INIT; + mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, + out_buf_ofs = 0, cur_file_ofs; + mz_zip_archive_file_stat file_stat; + void *pRead_buf = NULL; + void *pWrite_buf = NULL; + mz_uint32 + local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / + sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + // Empty file, or a directory (but not always a directory - I've seen odd zips + // with directories that have compressed data which inflates to 0 bytes) + if (!file_stat.m_comp_size) + return MZ_TRUE; + + // Entry is a subdirectory (I've seen old zips with dir entries which have + // compressed deflate data which inflates to 0 bytes, but these entries claim + // to uncompress to 512 bytes in the headers). I'm torn how to handle this + // case - should it fail instead? + if (mz_zip_reader_is_file_a_directory(pZip, file_index)) + return MZ_TRUE; + + // Encryption and patch files are not supported. + if (file_stat.m_bit_flag & (1 | 32)) + return MZ_FALSE; + + // This function only supports stored and deflate. + if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && + (file_stat.m_method != MZ_DEFLATED)) + return MZ_FALSE; + + // Read and parse the local directory entry. + cur_file_ofs = file_stat.m_local_header_ofs; + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return MZ_FALSE; + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return MZ_FALSE; + + cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) + return MZ_FALSE; + + // Decompress the file either directly from memory or from a file input + // buffer. + if (pZip->m_pState->m_pMem) { + pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; + read_buf_size = read_buf_avail = file_stat.m_comp_size; + comp_remaining = 0; + } else { + read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); + if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, + (size_t)read_buf_size))) + return MZ_FALSE; + read_buf_avail = 0; + comp_remaining = file_stat.m_comp_size; + } + + if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) { + // The file is stored or the caller has requested the compressed data. + if (pZip->m_pState->m_pMem) { +#ifdef _MSC_VER + if (((0, sizeof(size_t) == sizeof(mz_uint32))) && + (file_stat.m_comp_size > 0xFFFFFFFF)) +#else + if (((sizeof(size_t) == sizeof(mz_uint32))) && + (file_stat.m_comp_size > 0xFFFFFFFF)) +#endif + return MZ_FALSE; + if (pCallback(pOpaque, out_buf_ofs, pRead_buf, + (size_t)file_stat.m_comp_size) != file_stat.m_comp_size) + status = TINFL_STATUS_FAILED; + else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + file_crc32 = + (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, + (size_t)file_stat.m_comp_size); + // cur_file_ofs += file_stat.m_comp_size; + out_buf_ofs += file_stat.m_comp_size; + // comp_remaining = 0; + } else { + while (comp_remaining) { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, + (size_t)read_buf_avail) != read_buf_avail) { + status = TINFL_STATUS_FAILED; + break; + } + + if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) + file_crc32 = (mz_uint32)mz_crc32( + file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail); + + if (pCallback(pOpaque, out_buf_ofs, pRead_buf, + (size_t)read_buf_avail) != read_buf_avail) { + status = TINFL_STATUS_FAILED; + break; + } + cur_file_ofs += read_buf_avail; + out_buf_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + } + } + } else { + tinfl_decompressor inflator; + tinfl_init(&inflator); + + if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, + TINFL_LZ_DICT_SIZE))) + status = TINFL_STATUS_FAILED; + else { + do { + mz_uint8 *pWrite_buf_cur = + (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + size_t in_buf_size, + out_buf_size = + TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); + if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) { + read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); + if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, + (size_t)read_buf_avail) != read_buf_avail) { + status = TINFL_STATUS_FAILED; + break; + } + cur_file_ofs += read_buf_avail; + comp_remaining -= read_buf_avail; + read_buf_ofs = 0; + } + + in_buf_size = (size_t)read_buf_avail; + status = tinfl_decompress( + &inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, + (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, + comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); + read_buf_avail -= in_buf_size; + read_buf_ofs += in_buf_size; + + if (out_buf_size) { + if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != + out_buf_size) { + status = TINFL_STATUS_FAILED; + break; + } + file_crc32 = + (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size); + if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size) { + status = TINFL_STATUS_FAILED; + break; + } + } + } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || + (status == TINFL_STATUS_HAS_MORE_OUTPUT)); + } + } + + if ((status == TINFL_STATUS_DONE) && + (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) { + // Make sure the entire file was decompressed, and check its CRC. + if ((out_buf_ofs != file_stat.m_uncomp_size) || + (file_crc32 != file_stat.m_crc32)) + status = TINFL_STATUS_FAILED; + } + + if (!pZip->m_pState->m_pMem) + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + if (pWrite_buf) + pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf); + + return status == TINFL_STATUS_DONE; +} + +mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, + const char *pFilename, + mz_file_write_func pCallback, + void *pOpaque, mz_uint flags) { + int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); + if (file_index < 0) + return MZ_FALSE; + return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, + flags); +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, + const void *pBuf, size_t n) { + (void)ofs; + return MZ_FWRITE(pBuf, 1, n, (MZ_FILE *)pOpaque); +} + +mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, + const char *pDst_filename, + mz_uint flags) { + mz_bool status; + mz_zip_archive_file_stat file_stat; + MZ_FILE *pFile; + if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) + return MZ_FALSE; + + pFile = MZ_FOPEN(pDst_filename, "wb"); + if (!pFile) + return MZ_FALSE; + status = mz_zip_reader_extract_to_callback( + pZip, file_index, mz_zip_file_write_callback, pFile, flags); + if (MZ_FCLOSE(pFile) == EOF) + return MZ_FALSE; +#ifndef MINIZ_NO_TIME + if (status) { + mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time); + } +#endif + + return status; +} +#endif // #ifndef MINIZ_NO_STDIO + +mz_bool mz_zip_reader_end(mz_zip_archive *pZip) { + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || + (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return MZ_FALSE; + + mz_zip_internal_state *pState = pZip->m_pState; + pZip->m_pState = NULL; + mz_zip_array_clear(pZip, &pState->m_central_dir); + mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); + mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +#ifndef MINIZ_NO_STDIO + if (pState->m_pFile) { + MZ_FCLOSE(pState->m_pFile); + pState->m_pFile = NULL; + } +#endif // #ifndef MINIZ_NO_STDIO + + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + + pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, + const char *pArchive_filename, + const char *pDst_filename, + mz_uint flags) { + int file_index = + mz_zip_reader_locate_file(pZip, pArchive_filename, NULL, flags); + if (file_index < 0) + return MZ_FALSE; + return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags); +} +#endif + +// ------------------- .ZIP archive writing + +#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +static void mz_write_le16(mz_uint8 *p, mz_uint16 v) { + p[0] = (mz_uint8)v; + p[1] = (mz_uint8)(v >> 8); +} +static void mz_write_le32(mz_uint8 *p, mz_uint32 v) { + p[0] = (mz_uint8)v; + p[1] = (mz_uint8)(v >> 8); + p[2] = (mz_uint8)(v >> 16); + p[3] = (mz_uint8)(v >> 24); +} +#define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v)) +#define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v)) + +mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size) { + if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || + (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) + return MZ_FALSE; + + if (pZip->m_file_offset_alignment) { + // Ensure user specified file offset alignment is a power of 2. + if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1)) + return MZ_FALSE; + } + + if (!pZip->m_pAlloc) + pZip->m_pAlloc = def_alloc_func; + if (!pZip->m_pFree) + pZip->m_pFree = def_free_func; + if (!pZip->m_pRealloc) + pZip->m_pRealloc = def_realloc_func; + + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + pZip->m_archive_size = existing_size; + pZip->m_central_directory_file_ofs = 0; + pZip->m_total_files = 0; + + if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc( + pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) + return MZ_FALSE; + memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, + sizeof(mz_uint8)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, + sizeof(mz_uint32)); + MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, + sizeof(mz_uint32)); + return MZ_TRUE; +} + +static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, + const void *pBuf, size_t n) { + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_zip_internal_state *pState = pZip->m_pState; + mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size); + + if ((!n) || + ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF))) + return 0; + + if (new_size > pState->m_mem_capacity) { + void *pNew_block; + size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); + while (new_capacity < new_size) + new_capacity *= 2; + if (NULL == (pNew_block = pZip->m_pRealloc( + pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity))) + return 0; + pState->m_pMem = pNew_block; + pState->m_mem_capacity = new_capacity; + } + memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n); + pState->m_mem_size = (size_t)new_size; + return n; +} + +mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, + size_t size_to_reserve_at_beginning, + size_t initial_allocation_size) { + pZip->m_pWrite = mz_zip_heap_write_func; + pZip->m_pIO_opaque = pZip; + if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning)) + return MZ_FALSE; + if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, + size_to_reserve_at_beginning))) { + if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc( + pZip->m_pAlloc_opaque, 1, initial_allocation_size))) { + mz_zip_writer_end(pZip); + return MZ_FALSE; + } + pZip->m_pState->m_mem_capacity = initial_allocation_size; + } + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, + const void *pBuf, size_t n) { + mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; + mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); + if (((mz_int64)file_ofs < 0) || + (((cur_ofs != (mz_int64)file_ofs)) && + (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) + return 0; + return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile); +} + +mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, + mz_uint64 size_to_reserve_at_beginning) { + MZ_FILE *pFile; + pZip->m_pWrite = mz_zip_file_write_func; + pZip->m_pIO_opaque = pZip; + if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning)) + return MZ_FALSE; + if (NULL == (pFile = MZ_FOPEN(pFilename, "wb"))) { + mz_zip_writer_end(pZip); + return MZ_FALSE; + } + pZip->m_pState->m_pFile = pFile; + if (size_to_reserve_at_beginning) { + mz_uint64 cur_ofs = 0; + char buf[4096]; + MZ_CLEAR_OBJ(buf); + do { + size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n) { + mz_zip_writer_end(pZip); + return MZ_FALSE; + } + cur_ofs += n; + size_to_reserve_at_beginning -= n; + } while (size_to_reserve_at_beginning); + } + return MZ_TRUE; +} +#endif // #ifndef MINIZ_NO_STDIO + +mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, + const char *pFilename) { + mz_zip_internal_state *pState; + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) + return MZ_FALSE; + // No sense in trying to write to an archive that's already at the support max + // size + if ((pZip->m_total_files == 0xFFFF) || + ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) + return MZ_FALSE; + + pState = pZip->m_pState; + + if (pState->m_pFile) { +#ifdef MINIZ_NO_STDIO + pFilename; + return MZ_FALSE; +#else + // Archive is being read from stdio - try to reopen as writable. + if (pZip->m_pIO_opaque != pZip) + return MZ_FALSE; + if (!pFilename) + return MZ_FALSE; + pZip->m_pWrite = mz_zip_file_write_func; + if (NULL == + (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile))) { + // The mz_zip_archive is now in a bogus state because pState->m_pFile is + // NULL, so just close it. + mz_zip_reader_end(pZip); + return MZ_FALSE; + } +#endif // #ifdef MINIZ_NO_STDIO + } else if (pState->m_pMem) { + // Archive lives in a memory block. Assume it's from the heap that we can + // resize using the realloc callback. + if (pZip->m_pIO_opaque != pZip) + return MZ_FALSE; + pState->m_mem_capacity = pState->m_mem_size; + pZip->m_pWrite = mz_zip_heap_write_func; + } + // Archive is being read via a user provided read function - make sure the + // user has specified a write function too. + else if (!pZip->m_pWrite) + return MZ_FALSE; + + // Start writing new files at the archive's current central directory + // location. + pZip->m_archive_size = pZip->m_central_directory_file_ofs; + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; + pZip->m_central_directory_file_ofs = 0; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, + const void *pBuf, size_t buf_size, + mz_uint level_and_flags) { + return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, + level_and_flags, 0, 0); +} + +typedef struct { + mz_zip_archive *m_pZip; + mz_uint64 m_cur_archive_file_ofs; + mz_uint64 m_comp_size; +} mz_zip_writer_add_state; + +static mz_bool mz_zip_writer_add_put_buf_callback(const void *pBuf, int len, + void *pUser) { + mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser; + if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, + pState->m_cur_archive_file_ofs, pBuf, + len) != len) + return MZ_FALSE; + pState->m_cur_archive_file_ofs += len; + pState->m_comp_size += len; + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_create_local_dir_header( + mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, + mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, + mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, + mz_uint16 dos_time, mz_uint16 dos_date) { + (void)pZip; + memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, comp_size); + MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, uncomp_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size); + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_create_central_dir_header( + mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, + mz_uint16 extra_size, mz_uint16 comment_size, mz_uint64 uncomp_size, + mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, + mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, + mz_uint64 local_header_ofs, mz_uint32 ext_attributes) { + (void)pZip; + mz_uint16 version_made_by = 10 * MZ_VER_MAJOR + MZ_VER_MINOR; + version_made_by |= (MZ_PLATFORM << 8); + + memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_MADE_BY_OFS, version_made_by); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, comp_size); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, uncomp_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size); + MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes); + MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_header_ofs); + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_add_to_central_dir( + mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size, + const void *pExtra, mz_uint16 extra_size, const void *pComment, + mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, + mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, + mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, + mz_uint32 ext_attributes) { + mz_zip_internal_state *pState = pZip->m_pState; + mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size; + size_t orig_central_dir_size = pState->m_central_dir.m_size; + mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; + + // No zip64 support yet + if ((local_header_ofs > 0xFFFFFFFF) || + (((mz_uint64)pState->m_central_dir.m_size + + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + + comment_size) > 0xFFFFFFFF)) + return MZ_FALSE; + + if (!mz_zip_writer_create_central_dir_header( + pZip, central_dir_header, filename_size, extra_size, comment_size, + uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, + dos_date, local_header_ofs, ext_attributes)) + return MZ_FALSE; + + if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, + filename_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, + extra_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, + comment_size)) || + (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, + ¢ral_dir_ofs, 1))) { + // Try to push the central directory array back into its original state. + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, + MZ_FALSE); + return MZ_FALSE; + } + + return MZ_TRUE; +} + +static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name) { + // Basic ZIP archive filename validity checks: Valid filenames cannot start + // with a forward slash, cannot contain a drive letter, and cannot use + // DOS-style backward slashes. + if (*pArchive_name == '/') + return MZ_FALSE; + while (*pArchive_name) { + if ((*pArchive_name == '\\') || (*pArchive_name == ':')) + return MZ_FALSE; + pArchive_name++; + } + return MZ_TRUE; +} + +static mz_uint +mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip) { + mz_uint32 n; + if (!pZip->m_file_offset_alignment) + return 0; + n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1)); + return (pZip->m_file_offset_alignment - n) & + (pZip->m_file_offset_alignment - 1); +} + +static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, + mz_uint64 cur_file_ofs, mz_uint32 n) { + char buf[4096]; + memset(buf, 0, MZ_MIN(sizeof(buf), n)); + while (n) { + mz_uint32 s = MZ_MIN(sizeof(buf), n); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s) + return MZ_FALSE; + cur_file_ofs += s; + n -= s; + } + return MZ_TRUE; +} + +mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, + const char *pArchive_name, const void *pBuf, + size_t buf_size, const void *pComment, + mz_uint16 comment_size, + mz_uint level_and_flags, mz_uint64 uncomp_size, + mz_uint32 uncomp_crc32) { + mz_uint32 ext_attributes = 0; + mz_uint16 method = 0, dos_time = 0, dos_date = 0; + mz_uint level, num_alignment_padding_bytes; + mz_uint64 local_dir_header_ofs, cur_archive_file_ofs, comp_size = 0; + size_t archive_name_size; + mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + tdefl_compressor *pComp = NULL; + mz_bool store_data_uncompressed; + mz_zip_internal_state *pState; + + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + level = level_and_flags & 0xF; + store_data_uncompressed = + ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)); + + if ((!pZip) || (!pZip->m_pState) || + (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || + (!pArchive_name) || ((comment_size) && (!pComment)) || + (pZip->m_total_files == 0xFFFF) || (level > MZ_UBER_COMPRESSION)) + return MZ_FALSE; + + local_dir_header_ofs = cur_archive_file_ofs = pZip->m_archive_size; + pState = pZip->m_pState; + + if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size)) + return MZ_FALSE; + // No zip64 support yet + if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) + return MZ_FALSE; + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return MZ_FALSE; + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_WRITING_APIS) + { + time_t cur_time; + time(&cur_time); + mz_zip_time_t_to_dos_time(cur_time, &dos_time, &dos_date); + } +#endif // #ifndef MINIZ_NO_TIME + + archive_name_size = strlen(pArchive_name); + if (archive_name_size > 0xFFFF) + return MZ_FALSE; + + num_alignment_padding_bytes = + mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + // no zip64 support yet + if ((pZip->m_total_files == 0xFFFF) || + ((pZip->m_archive_size + num_alignment_padding_bytes + + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + + comment_size + archive_name_size) > 0xFFFFFFFF)) + return MZ_FALSE; + + if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/')) { + // Set DOS Subdirectory attribute bit. + ext_attributes |= 0x10; + // Subdirectories cannot contain data. + if ((buf_size) || (uncomp_size)) + return MZ_FALSE; + } + + // Try to do any allocations before writing to the archive, so if an + // allocation fails the file remains unmodified. (A good idea if we're doing + // an in-place modification.) + if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + + archive_name_size + comment_size)) || + (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1))) + return MZ_FALSE; + + if ((!store_data_uncompressed) && (buf_size)) { + if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc( + pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)))) + return MZ_FALSE; + } + + if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, + num_alignment_padding_bytes + + sizeof(local_dir_header))) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + local_dir_header_ofs += num_alignment_padding_bytes; + if (pZip->m_file_offset_alignment) { + MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == + 0); + } + cur_archive_file_ofs += + num_alignment_padding_bytes + sizeof(local_dir_header); + + MZ_CLEAR_OBJ(local_dir_header); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, + archive_name_size) != archive_name_size) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + cur_archive_file_ofs += archive_name_size; + + if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) { + uncomp_crc32 = + (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, buf_size); + uncomp_size = buf_size; + if (uncomp_size <= 3) { + level = 0; + store_data_uncompressed = MZ_TRUE; + } + } + + if (store_data_uncompressed) { + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, + buf_size) != buf_size) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + + cur_archive_file_ofs += buf_size; + comp_size = buf_size; + + if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) + method = MZ_DEFLATED; + } else if (buf_size) { + mz_zip_writer_add_state state; + + state.m_pZip = pZip; + state.m_cur_archive_file_ofs = cur_archive_file_ofs; + state.m_comp_size = 0; + + if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, + tdefl_create_comp_flags_from_zip_params( + level, -15, MZ_DEFAULT_STRATEGY)) != + TDEFL_STATUS_OKAY) || + (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != + TDEFL_STATUS_DONE)) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + return MZ_FALSE; + } + + comp_size = state.m_comp_size; + cur_archive_file_ofs = state.m_cur_archive_file_ofs; + + method = MZ_DEFLATED; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + pComp = NULL; + + // no zip64 support yet + if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF)) + return MZ_FALSE; + + if (!mz_zip_writer_create_local_dir_header( + pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, + comp_size, uncomp_crc32, method, 0, dos_time, dos_date)) + return MZ_FALSE; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, + sizeof(local_dir_header)) != sizeof(local_dir_header)) + return MZ_FALSE; + + if (!mz_zip_writer_add_to_central_dir( + pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, + comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, + dos_time, dos_date, local_dir_header_ofs, ext_attributes)) + return MZ_FALSE; + + pZip->m_total_files++; + pZip->m_archive_size = cur_archive_file_ofs; + + return MZ_TRUE; +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, + const char *pSrc_filename, const void *pComment, + mz_uint16 comment_size, mz_uint level_and_flags, + mz_uint32 ext_attributes) { + mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; + mz_uint16 method = 0, dos_time = 0, dos_date = 0; + time_t file_modified_time; + mz_uint64 local_dir_header_ofs, cur_archive_file_ofs, uncomp_size = 0, + comp_size = 0; + size_t archive_name_size; + mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + MZ_FILE *pSrc_file = NULL; + + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + level = level_and_flags & 0xF; + + if ((!pZip) || (!pZip->m_pState) || + (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || + ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) + return MZ_FALSE; + + local_dir_header_ofs = cur_archive_file_ofs = pZip->m_archive_size; + + if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) + return MZ_FALSE; + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return MZ_FALSE; + + archive_name_size = strlen(pArchive_name); + if (archive_name_size > 0xFFFF) + return MZ_FALSE; + + num_alignment_padding_bytes = + mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + // no zip64 support yet + if ((pZip->m_total_files == 0xFFFF) || + ((pZip->m_archive_size + num_alignment_padding_bytes + + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + + comment_size + archive_name_size) > 0xFFFFFFFF)) + return MZ_FALSE; + + memset(&file_modified_time, 0, sizeof(file_modified_time)); +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_WRITING_APIS) +#ifndef MINIZ_NO_STDIO + if (!mz_zip_get_file_modified_time(pSrc_filename, &file_modified_time)) + return MZ_FALSE; +#endif + mz_zip_time_t_to_dos_time(file_modified_time, &dos_time, &dos_date); +#endif + + pSrc_file = MZ_FOPEN(pSrc_filename, "rb"); + if (!pSrc_file) + return MZ_FALSE; + MZ_FSEEK64(pSrc_file, 0, SEEK_END); + uncomp_size = MZ_FTELL64(pSrc_file); + MZ_FSEEK64(pSrc_file, 0, SEEK_SET); + + if (uncomp_size > 0xFFFFFFFF) { + // No zip64 support yet + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + if (uncomp_size <= 3) + level = 0; + + if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, + num_alignment_padding_bytes + + sizeof(local_dir_header))) { + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + local_dir_header_ofs += num_alignment_padding_bytes; + if (pZip->m_file_offset_alignment) { + MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == + 0); + } + cur_archive_file_ofs += + num_alignment_padding_bytes + sizeof(local_dir_header); + + MZ_CLEAR_OBJ(local_dir_header); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, + archive_name_size) != archive_name_size) { + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + cur_archive_file_ofs += archive_name_size; + + if (uncomp_size) { + mz_uint64 uncomp_remaining = uncomp_size; + void *pRead_buf = + pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE); + if (!pRead_buf) { + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + + if (!level) { + while (uncomp_remaining) { + mz_uint n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, uncomp_remaining); + if ((MZ_FREAD(pRead_buf, 1, n, pSrc_file) != n) || + (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, + n) != n)) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + uncomp_crc32 = + (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); + uncomp_remaining -= n; + cur_archive_file_ofs += n; + } + comp_size = uncomp_size; + } else { + mz_bool result = MZ_FALSE; + mz_zip_writer_add_state state; + tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc( + pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)); + if (!pComp) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + + state.m_pZip = pZip; + state.m_cur_archive_file_ofs = cur_archive_file_ofs; + state.m_comp_size = 0; + + if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, + tdefl_create_comp_flags_from_zip_params( + level, -15, MZ_DEFAULT_STRATEGY)) != + TDEFL_STATUS_OKAY) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + + for (;;) { + size_t in_buf_size = + (mz_uint32)MZ_MIN(uncomp_remaining, MZ_ZIP_MAX_IO_BUF_SIZE); + tdefl_status status; + + if (MZ_FREAD(pRead_buf, 1, in_buf_size, pSrc_file) != in_buf_size) + break; + + uncomp_crc32 = (mz_uint32)mz_crc32( + uncomp_crc32, (const mz_uint8 *)pRead_buf, in_buf_size); + uncomp_remaining -= in_buf_size; + + status = tdefl_compress_buffer(pComp, pRead_buf, in_buf_size, + uncomp_remaining ? TDEFL_NO_FLUSH + : TDEFL_FINISH); + if (status == TDEFL_STATUS_DONE) { + result = MZ_TRUE; + break; + } else if (status != TDEFL_STATUS_OKAY) + break; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); + + if (!result) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + MZ_FCLOSE(pSrc_file); + return MZ_FALSE; + } + + comp_size = state.m_comp_size; + cur_archive_file_ofs = state.m_cur_archive_file_ofs; + + method = MZ_DEFLATED; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); + } + + MZ_FCLOSE(pSrc_file); + pSrc_file = NULL; + + // no zip64 support yet + if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF)) + return MZ_FALSE; + + if (!mz_zip_writer_create_local_dir_header( + pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, + comp_size, uncomp_crc32, method, 0, dos_time, dos_date)) + return MZ_FALSE; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, + sizeof(local_dir_header)) != sizeof(local_dir_header)) + return MZ_FALSE; + + if (!mz_zip_writer_add_to_central_dir( + pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, + comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, + dos_time, dos_date, local_dir_header_ofs, ext_attributes)) + return MZ_FALSE; + + pZip->m_total_files++; + pZip->m_archive_size = cur_archive_file_ofs; + + return MZ_TRUE; +} +#endif // #ifndef MINIZ_NO_STDIO + +mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, + mz_zip_archive *pSource_zip, + mz_uint file_index) { + mz_uint n, bit_flags, num_alignment_padding_bytes; + mz_uint64 comp_bytes_remaining, local_dir_header_ofs; + mz_uint64 cur_src_file_ofs, cur_dst_file_ofs; + mz_uint32 + local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / + sizeof(mz_uint32)]; + mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; + mz_uint8 central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; + size_t orig_central_dir_size; + mz_zip_internal_state *pState; + void *pBuf; + const mz_uint8 *pSrc_central_header; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) + return MZ_FALSE; + if (NULL == + (pSrc_central_header = mz_zip_reader_get_cdh(pSource_zip, file_index))) + return MZ_FALSE; + pState = pZip->m_pState; + + num_alignment_padding_bytes = + mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); + + // no zip64 support yet + if ((pZip->m_total_files == 0xFFFF) || + ((pZip->m_archive_size + num_alignment_padding_bytes + + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) > + 0xFFFFFFFF)) + return MZ_FALSE; + + cur_src_file_ofs = + MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + cur_dst_file_ofs = pZip->m_archive_size; + + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, + pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return MZ_FALSE; + if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) + return MZ_FALSE; + cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; + + if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, + num_alignment_padding_bytes)) + return MZ_FALSE; + cur_dst_file_ofs += num_alignment_padding_bytes; + local_dir_header_ofs = cur_dst_file_ofs; + if (pZip->m_file_offset_alignment) { + MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == + 0); + } + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) + return MZ_FALSE; + cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; + + n = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); + comp_bytes_remaining = + n + MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); + + if (NULL == + (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, + (size_t)MZ_MAX(sizeof(mz_uint32) * 4, + MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, + comp_bytes_remaining))))) + return MZ_FALSE; + + while (comp_bytes_remaining) { + n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining); + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, + n) != n) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return MZ_FALSE; + } + cur_src_file_ofs += n; + + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return MZ_FALSE; + } + cur_dst_file_ofs += n; + + comp_bytes_remaining -= n; + } + + bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); + if (bit_flags & 8) { + // Copy data descriptor + if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, + sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return MZ_FALSE; + } + + n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == 0x08074b50) ? 4 : 3); + if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + return MZ_FALSE; + } + + // cur_src_file_ofs += n; + cur_dst_file_ofs += n; + } + pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); + + // no zip64 support yet + if (cur_dst_file_ofs > 0xFFFFFFFF) + return MZ_FALSE; + + orig_central_dir_size = pState->m_central_dir.m_size; + + memcpy(central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); + MZ_WRITE_LE32(central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, + local_dir_header_ofs); + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_header, + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) + return MZ_FALSE; + + n = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS) + + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS) + + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS); + if (!mz_zip_array_push_back( + pZip, &pState->m_central_dir, + pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n)) { + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, + MZ_FALSE); + return MZ_FALSE; + } + + if (pState->m_central_dir.m_size > 0xFFFFFFFF) + return MZ_FALSE; + n = (mz_uint32)orig_central_dir_size; + if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1)) { + mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, + MZ_FALSE); + return MZ_FALSE; + } + + pZip->m_total_files++; + pZip->m_archive_size = cur_dst_file_ofs; + + return MZ_TRUE; +} + +mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) { + mz_zip_internal_state *pState; + mz_uint64 central_dir_ofs, central_dir_size; + mz_uint8 hdr[MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE]; + + if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) + return MZ_FALSE; + + pState = pZip->m_pState; + + // no zip64 support yet + if ((pZip->m_total_files > 0xFFFF) || + ((pZip->m_archive_size + pState->m_central_dir.m_size + + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) + return MZ_FALSE; + + central_dir_ofs = 0; + central_dir_size = 0; + if (pZip->m_total_files) { + // Write central directory + central_dir_ofs = pZip->m_archive_size; + central_dir_size = pState->m_central_dir.m_size; + pZip->m_central_directory_file_ofs = central_dir_ofs; + if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, + pState->m_central_dir.m_p, + (size_t)central_dir_size) != central_dir_size) + return MZ_FALSE; + pZip->m_archive_size += central_dir_size; + } + + // Write end of central directory record + MZ_CLEAR_OBJ(hdr); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG); + MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, + pZip->m_total_files); + MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, central_dir_size); + MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, central_dir_ofs); + + if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, + sizeof(hdr)) != sizeof(hdr)) + return MZ_FALSE; +#ifndef MINIZ_NO_STDIO + if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF)) + return MZ_FALSE; +#endif // #ifndef MINIZ_NO_STDIO + + pZip->m_archive_size += sizeof(hdr); + + pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED; + return MZ_TRUE; +} + +mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, + size_t *pSize) { + if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pSize)) + return MZ_FALSE; + if (pZip->m_pWrite != mz_zip_heap_write_func) + return MZ_FALSE; + if (!mz_zip_writer_finalize_archive(pZip)) + return MZ_FALSE; + + *pBuf = pZip->m_pState->m_pMem; + *pSize = pZip->m_pState->m_mem_size; + pZip->m_pState->m_pMem = NULL; + pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0; + return MZ_TRUE; +} + +mz_bool mz_zip_writer_end(mz_zip_archive *pZip) { + mz_zip_internal_state *pState; + mz_bool status = MZ_TRUE; + if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || + ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && + (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED))) + return MZ_FALSE; + + pState = pZip->m_pState; + pZip->m_pState = NULL; + mz_zip_array_clear(pZip, &pState->m_central_dir); + mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); + mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); + +#ifndef MINIZ_NO_STDIO + if (pState->m_pFile) { + MZ_FCLOSE(pState->m_pFile); + pState->m_pFile = NULL; + } +#endif // #ifndef MINIZ_NO_STDIO + + if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) { + pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem); + pState->m_pMem = NULL; + } + + pZip->m_pFree(pZip->m_pAlloc_opaque, pState); + pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; + return status; +} + +#ifndef MINIZ_NO_STDIO +mz_bool mz_zip_add_mem_to_archive_file_in_place( + const char *pZip_filename, const char *pArchive_name, const void *pBuf, + size_t buf_size, const void *pComment, mz_uint16 comment_size, + mz_uint level_and_flags) { + mz_bool status, created_new_archive = MZ_FALSE; + mz_zip_archive zip_archive; + struct MZ_FILE_STAT_STRUCT file_stat; + MZ_CLEAR_OBJ(zip_archive); + if ((int)level_and_flags < 0) + level_and_flags = MZ_DEFAULT_LEVEL; + if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || + ((comment_size) && (!pComment)) || + ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION)) + return MZ_FALSE; + if (!mz_zip_writer_validate_archive_name(pArchive_name)) + return MZ_FALSE; + if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0) { + // Create a new archive. + if (!mz_zip_writer_init_file(&zip_archive, pZip_filename, 0)) + return MZ_FALSE; + created_new_archive = MZ_TRUE; + } else { + // Append to an existing archive. + if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, + level_and_flags | + MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) + return MZ_FALSE; + if (!mz_zip_writer_init_from_reader(&zip_archive, pZip_filename)) { + mz_zip_reader_end(&zip_archive); + return MZ_FALSE; + } + } + status = + mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, + pComment, comment_size, level_and_flags, 0, 0); + // Always finalize, even if adding failed for some reason, so we have a valid + // central directory. (This may not always succeed, but we can try.) + if (!mz_zip_writer_finalize_archive(&zip_archive)) + status = MZ_FALSE; + if (!mz_zip_writer_end(&zip_archive)) + status = MZ_FALSE; + if ((!status) && (created_new_archive)) { + // It's a new archive and something went wrong, so just delete it. + int ignoredStatus = MZ_DELETE_FILE(pZip_filename); + (void)ignoredStatus; + } + return status; +} + +void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, + const char *pArchive_name, + size_t *pSize, mz_uint flags) { + int file_index; + mz_zip_archive zip_archive; + void *p = NULL; + + if (pSize) + *pSize = 0; + + if ((!pZip_filename) || (!pArchive_name)) + return NULL; + + MZ_CLEAR_OBJ(zip_archive); + if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, + flags | + MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) + return NULL; + + if ((file_index = mz_zip_reader_locate_file(&zip_archive, pArchive_name, NULL, + flags)) >= 0) + p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags); + + mz_zip_reader_end(&zip_archive); + return p; +} + +#endif // #ifndef MINIZ_NO_STDIO + +#endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS + +#endif // #ifndef MINIZ_NO_ARCHIVE_APIS + +#ifdef __cplusplus +} +#endif + +#endif // MINIZ_HEADER_FILE_ONLY + +/* + This is free and unencumbered software released into the public domain. + + Anyone is free to copy, modify, publish, use, compile, sell, or + distribute this software, either in source code form or as a compiled + binary, for any purpose, commercial or non-commercial, and by any + means. + + In jurisdictions that recognize copyright laws, the author or authors + of this software dedicate any and all copyright interest in the + software to the public domain. We make this dedication for the benefit + of the public at large and to the detriment of our heirs and + successors. We intend this dedication to be an overt act of + relinquishment in perpetuity of all present and future rights to this + software under copyright law. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + + For more information, please refer to +*/ diff --git a/v_windows/v/old/thirdparty/zip/zip.c b/v_windows/v/old/thirdparty/zip/zip.c new file mode 100644 index 0000000..503df25 --- /dev/null +++ b/v_windows/v/old/thirdparty/zip/zip.c @@ -0,0 +1,1034 @@ +/* + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#define __STDC_WANT_LIB_EXT1__ 1 + +#include +#include +#include + +#if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) || \ + defined(__MINGW32__) +/* Win32, DOS, MSVC, MSVS */ +#include + +#define MKDIR(DIRNAME) _mkdir(DIRNAME) +#define STRCLONE(STR) ((STR) ? _strdup(STR) : NULL) +#define HAS_DEVICE(P) \ + ((((P)[0] >= 'A' && (P)[0] <= 'Z') || ((P)[0] >= 'a' && (P)[0] <= 'z')) && \ + (P)[1] == ':') +#define FILESYSTEM_PREFIX_LEN(P) (HAS_DEVICE(P) ? 2 : 0) + +#else + +#include // needed for symlink() on BSD +int symlink(const char *target, const char *linkpath); // needed on Linux + +#define MKDIR(DIRNAME) mkdir(DIRNAME, 0755) +#define STRCLONE(STR) ((STR) ? strdup(STR) : NULL) + +#endif + +#include "miniz.h" +#include "zip.h" + +#ifndef HAS_DEVICE +#define HAS_DEVICE(P) 0 +#endif + +#ifndef FILESYSTEM_PREFIX_LEN +#define FILESYSTEM_PREFIX_LEN(P) 0 +#endif + +#ifndef ISSLASH +#define ISSLASH(C) ((C) == '/' || (C) == '\\') +#endif + +#define CLEANUP(ptr) \ + do { \ + if (ptr) { \ + free((void *)ptr); \ + ptr = NULL; \ + } \ + } while (0) + +static const char *base_name(const char *name) { + char const *p; + char const *base = name += FILESYSTEM_PREFIX_LEN(name); + int all_slashes = 1; + + for (p = name; *p; p++) { + if (ISSLASH(*p)) + base = p + 1; + else + all_slashes = 0; + } + + /* If NAME is all slashes, arrange to return `/'. */ + if (*base == '\0' && ISSLASH(*name) && all_slashes) + --base; + + return base; +} + +static int mkpath(char *path) { + char *p; + char npath[MAX_PATH + 1]; + int len = 0; + int has_device = HAS_DEVICE(path); + + memset(npath, 0, MAX_PATH + 1); + if (has_device) { + // only on windows + npath[0] = path[0]; + npath[1] = path[1]; + len = 2; + } + for (p = path + len; *p && len < MAX_PATH; p++) { + if (ISSLASH(*p) && ((!has_device && len > 0) || (has_device && len > 2))) { +#if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) || \ + defined(__MINGW32__) +#else + if ('\\' == *p) { + *p = '/'; + } +#endif + + if (MKDIR(npath) == -1) { + if (errno != EEXIST) { + return -1; + } + } + } + npath[len++] = *p; + } + + return 0; +} + +static char *strrpl(const char *str, size_t n, char oldchar, char newchar) { + char c; + size_t i; + char *rpl = (char *)calloc((1 + n), sizeof(char)); + char *begin = rpl; + if (!rpl) { + return NULL; + } + + for (i = 0; (i < n) && (c = *str++); ++i) { + if (c == oldchar) { + c = newchar; + } + *rpl++ = c; + } + + return begin; +} + +struct zip_entry_t { + int index; + char *name; + mz_uint64 uncomp_size; + mz_uint64 comp_size; + mz_uint32 uncomp_crc32; + mz_uint64 offset; + mz_uint8 header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + mz_uint64 header_offset; + mz_uint16 method; + mz_zip_writer_add_state state; + tdefl_compressor comp; + mz_uint32 external_attr; + time_t m_time; +}; + +struct zip_t { + mz_zip_archive archive; + mz_uint level; + struct zip_entry_t entry; +}; + +struct zip_t *zip_open(const char *zipname, int level, char mode) { + struct zip_t *zip = NULL; + + if (!zipname || strlen(zipname) < 1) { + // zip_t archive name is empty or NULL + goto cleanup; + } + + if (level < 0) + level = MZ_DEFAULT_LEVEL; + if ((level & 0xF) > MZ_UBER_COMPRESSION) { + // Wrong compression level + goto cleanup; + } + + zip = (struct zip_t *)calloc((size_t)1, sizeof(struct zip_t)); + if (!zip) + goto cleanup; + + zip->level = (mz_uint)level; + switch (mode) { + case 'w': + // Create a new archive. + if (!mz_zip_writer_init_file(&(zip->archive), zipname, 0)) { + // Cannot initialize zip_archive writer + goto cleanup; + } + break; + + case 'r': + case 'a': + if (!mz_zip_reader_init_file( + &(zip->archive), zipname, + zip->level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) { + // An archive file does not exist or cannot initialize + // zip_archive reader + goto cleanup; + } + if (mode == 'a' && + !mz_zip_writer_init_from_reader(&(zip->archive), zipname)) { + mz_zip_reader_end(&(zip->archive)); + goto cleanup; + } + break; + + default: + goto cleanup; + } + + return zip; + +cleanup: + CLEANUP(zip); + return NULL; +} + +void zip_close(struct zip_t *zip) { + if (zip) { + // Always finalize, even if adding failed for some reason, so we have a + // valid central directory. + mz_zip_writer_finalize_archive(&(zip->archive)); + + mz_zip_writer_end(&(zip->archive)); + mz_zip_reader_end(&(zip->archive)); + + CLEANUP(zip); + } +} + +int zip_is64(struct zip_t *zip) { + if (!zip) { + // zip_t handler is not initialized + return -1; + } + + if (!zip->archive.m_pState) { + // zip state is not initialized + return -1; + } + + return (int)zip->archive.m_pState->m_zip64; +} + +int zip_entry_open(struct zip_t *zip, const char *entryname) { + size_t entrylen = 0; + mz_zip_archive *pzip = NULL; + mz_uint num_alignment_padding_bytes, level; + mz_zip_archive_file_stat stats; + + if (!zip || !entryname) { + return -1; + } + + entrylen = strlen(entryname); + if (entrylen < 1) { + return -1; + } + + /* + .ZIP File Format Specification Version: 6.3.3 + + 4.4.17.1 The name of the file, with optional relative path. + The path stored MUST not contain a drive or + device letter, or a leading slash. All slashes + MUST be forward slashes '/' as opposed to + backwards slashes '\' for compatibility with Amiga + and UNIX file systems etc. If input came from standard + input, there is no file name field. + */ + zip->entry.name = strrpl(entryname, entrylen, '\\', '/'); + if (!zip->entry.name) { + // Cannot parse zip entry name + return -1; + } + + pzip = &(zip->archive); + if (pzip->m_zip_mode == MZ_ZIP_MODE_READING) { + zip->entry.index = + mz_zip_reader_locate_file(pzip, zip->entry.name, NULL, 0); + if (zip->entry.index < 0) { + goto cleanup; + } + + if (!mz_zip_reader_file_stat(pzip, (mz_uint)zip->entry.index, &stats)) { + goto cleanup; + } + + zip->entry.comp_size = stats.m_comp_size; + zip->entry.uncomp_size = stats.m_uncomp_size; + zip->entry.uncomp_crc32 = stats.m_crc32; + zip->entry.offset = stats.m_central_dir_ofs; + zip->entry.header_offset = stats.m_local_header_ofs; + zip->entry.method = stats.m_method; + zip->entry.external_attr = stats.m_external_attr; +#ifndef MINIZ_NO_TIME + zip->entry.m_time = stats.m_time; +#endif + + return 0; + } + + zip->entry.index = (int)zip->archive.m_total_files; + zip->entry.comp_size = 0; + zip->entry.uncomp_size = 0; + zip->entry.uncomp_crc32 = MZ_CRC32_INIT; + zip->entry.offset = zip->archive.m_archive_size; + zip->entry.header_offset = zip->archive.m_archive_size; + memset(zip->entry.header, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE * sizeof(mz_uint8)); + zip->entry.method = 0; + + // UNIX or APPLE +#if MZ_PLATFORM == 3 || MZ_PLATFORM == 19 + // regular file with rw-r--r-- persmissions + zip->entry.external_attr = (mz_uint32)(0100644) << 16; +#else + zip->entry.external_attr = 0; +#endif + + num_alignment_padding_bytes = + mz_zip_writer_compute_padding_needed_for_file_alignment(pzip); + + if (!pzip->m_pState || (pzip->m_zip_mode != MZ_ZIP_MODE_WRITING)) { + // Wrong zip mode + goto cleanup; + } + if (zip->level & MZ_ZIP_FLAG_COMPRESSED_DATA) { + // Wrong zip compression level + goto cleanup; + } + // no zip64 support yet + if ((pzip->m_total_files == 0xFFFF) || + ((pzip->m_archive_size + num_alignment_padding_bytes + + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + + entrylen) > 0xFFFFFFFF)) { + // No zip64 support yet + goto cleanup; + } + if (!mz_zip_writer_write_zeros(pzip, zip->entry.offset, + num_alignment_padding_bytes + + sizeof(zip->entry.header))) { + // Cannot memset zip entry header + goto cleanup; + } + + zip->entry.header_offset += num_alignment_padding_bytes; + if (pzip->m_file_offset_alignment) { + MZ_ASSERT( + (zip->entry.header_offset & (pzip->m_file_offset_alignment - 1)) == 0); + } + zip->entry.offset += num_alignment_padding_bytes + sizeof(zip->entry.header); + + if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.offset, zip->entry.name, + entrylen) != entrylen) { + // Cannot write data to zip entry + goto cleanup; + } + + zip->entry.offset += entrylen; + level = zip->level & 0xF; + if (level) { + zip->entry.state.m_pZip = pzip; + zip->entry.state.m_cur_archive_file_ofs = zip->entry.offset; + zip->entry.state.m_comp_size = 0; + + if (tdefl_init(&(zip->entry.comp), mz_zip_writer_add_put_buf_callback, + &(zip->entry.state), + (int)tdefl_create_comp_flags_from_zip_params( + (int)level, -15, MZ_DEFAULT_STRATEGY)) != + TDEFL_STATUS_OKAY) { + // Cannot initialize the zip compressor + goto cleanup; + } + } + + zip->entry.m_time = time(NULL); + + return 0; + +cleanup: + CLEANUP(zip->entry.name); + return -1; +} + +int zip_entry_openbyindex(struct zip_t *zip, int index) { + mz_zip_archive *pZip = NULL; + mz_zip_archive_file_stat stats; + mz_uint namelen; + const mz_uint8 *pHeader; + const char *pFilename; + + if (!zip) { + // zip_t handler is not initialized + return -1; + } + + pZip = &(zip->archive); + if (pZip->m_zip_mode != MZ_ZIP_MODE_READING) { + // open by index requires readonly mode + return -1; + } + + if (index < 0 || (mz_uint)index >= pZip->m_total_files) { + // index out of range + return -1; + } + + if (!(pHeader = &MZ_ZIP_ARRAY_ELEMENT( + &pZip->m_pState->m_central_dir, mz_uint8, + MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, + mz_uint32, index)))) { + // cannot find header in central directory + return -1; + } + + namelen = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); + pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; + + /* + .ZIP File Format Specification Version: 6.3.3 + + 4.4.17.1 The name of the file, with optional relative path. + The path stored MUST not contain a drive or + device letter, or a leading slash. All slashes + MUST be forward slashes '/' as opposed to + backwards slashes '\' for compatibility with Amiga + and UNIX file systems etc. If input came from standard + input, there is no file name field. + */ + zip->entry.name = strrpl(pFilename, namelen, '\\', '/'); + if (!zip->entry.name) { + // local entry name is NULL + return -1; + } + + if (!mz_zip_reader_file_stat(pZip, (mz_uint)index, &stats)) { + return -1; + } + + zip->entry.index = index; + zip->entry.comp_size = stats.m_comp_size; + zip->entry.uncomp_size = stats.m_uncomp_size; + zip->entry.uncomp_crc32 = stats.m_crc32; + zip->entry.offset = stats.m_central_dir_ofs; + zip->entry.header_offset = stats.m_local_header_ofs; + zip->entry.method = stats.m_method; + zip->entry.external_attr = stats.m_external_attr; +#ifndef MINIZ_NO_TIME + zip->entry.m_time = stats.m_time; +#endif + + return 0; +} + +int zip_entry_close(struct zip_t *zip) { + mz_zip_archive *pzip = NULL; + mz_uint level; + tdefl_status done; + mz_uint16 entrylen; + mz_uint16 dos_time, dos_date; + int status = -1; + + if (!zip) { + // zip_t handler is not initialized + goto cleanup; + } + + pzip = &(zip->archive); + if (pzip->m_zip_mode == MZ_ZIP_MODE_READING) { + status = 0; + goto cleanup; + } + + level = zip->level & 0xF; + if (level) { + done = tdefl_compress_buffer(&(zip->entry.comp), "", 0, TDEFL_FINISH); + if (done != TDEFL_STATUS_DONE && done != TDEFL_STATUS_OKAY) { + // Cannot flush compressed buffer + goto cleanup; + } + zip->entry.comp_size = zip->entry.state.m_comp_size; + zip->entry.offset = zip->entry.state.m_cur_archive_file_ofs; + zip->entry.method = MZ_DEFLATED; + } + + entrylen = (mz_uint16)strlen(zip->entry.name); + // no zip64 support yet + if ((zip->entry.comp_size > 0xFFFFFFFF) || (zip->entry.offset > 0xFFFFFFFF)) { + // No zip64 support, yet + goto cleanup; + } + +#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_WRITING_APIS) + mz_zip_time_t_to_dos_time(zip->entry.m_time, &dos_time, &dos_date); +#endif + if (!mz_zip_writer_create_local_dir_header( + pzip, zip->entry.header, entrylen, 0, zip->entry.uncomp_size, + zip->entry.comp_size, zip->entry.uncomp_crc32, zip->entry.method, 0, + dos_time, dos_date)) { + // Cannot create zip entry header + goto cleanup; + } + + if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.header_offset, + zip->entry.header, + sizeof(zip->entry.header)) != sizeof(zip->entry.header)) { + // Cannot write zip entry header + goto cleanup; + } + + if (!mz_zip_writer_add_to_central_dir( + pzip, zip->entry.name, entrylen, NULL, 0, "", 0, + zip->entry.uncomp_size, zip->entry.comp_size, zip->entry.uncomp_crc32, + zip->entry.method, 0, dos_time, dos_date, zip->entry.header_offset, + zip->entry.external_attr)) { + // Cannot write to zip central dir + goto cleanup; + } + + pzip->m_total_files++; + pzip->m_archive_size = zip->entry.offset; + status = 0; + +cleanup: + if (zip) { + zip->entry.m_time = 0; + CLEANUP(zip->entry.name); + } + return status; +} + +const char *zip_entry_name(struct zip_t *zip) { + if (!zip) { + // zip_t handler is not initialized + return NULL; + } + + return zip->entry.name; +} + +int zip_entry_index(struct zip_t *zip) { + if (!zip) { + // zip_t handler is not initialized + return -1; + } + + return zip->entry.index; +} + +int zip_entry_isdir(struct zip_t *zip) { + if (!zip) { + // zip_t handler is not initialized + return -1; + } + + if (zip->entry.index < 0) { + // zip entry is not opened + return -1; + } + + return (int)mz_zip_reader_is_file_a_directory(&zip->archive, + (mz_uint)zip->entry.index); +} + +unsigned long long zip_entry_size(struct zip_t *zip) { + return zip ? zip->entry.uncomp_size : 0; +} + +unsigned int zip_entry_crc32(struct zip_t *zip) { + return zip ? zip->entry.uncomp_crc32 : 0; +} + +int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize) { + mz_uint level; + mz_zip_archive *pzip = NULL; + tdefl_status status; + + if (!zip) { + // zip_t handler is not initialized + return -1; + } + + pzip = &(zip->archive); + if (buf && bufsize > 0) { + zip->entry.uncomp_size += bufsize; + zip->entry.uncomp_crc32 = (mz_uint32)mz_crc32( + zip->entry.uncomp_crc32, (const mz_uint8 *)buf, bufsize); + + level = zip->level & 0xF; + if (!level) { + if ((pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.offset, buf, + bufsize) != bufsize)) { + // Cannot write buffer + return -1; + } + zip->entry.offset += bufsize; + zip->entry.comp_size += bufsize; + } else { + status = tdefl_compress_buffer(&(zip->entry.comp), buf, bufsize, + TDEFL_NO_FLUSH); + if (status != TDEFL_STATUS_DONE && status != TDEFL_STATUS_OKAY) { + // Cannot compress buffer + return -1; + } + } + } + + return 0; +} + +int zip_entry_fwrite(struct zip_t *zip, const char *filename) { + int status = 0; + size_t n = 0; + FILE *stream = NULL; + mz_uint8 buf[MZ_ZIP_MAX_IO_BUF_SIZE]; + struct MZ_FILE_STAT_STRUCT file_stat; + + if (!zip) { + // zip_t handler is not initialized + return -1; + } + + memset(buf, 0, MZ_ZIP_MAX_IO_BUF_SIZE); + memset((void *)&file_stat, 0, sizeof(struct MZ_FILE_STAT_STRUCT)); + if (MZ_FILE_STAT(filename, &file_stat) != 0) { + // problem getting information - check errno + return -1; + } + + if ((file_stat.st_mode & 0200) == 0) { + // MS-DOS read-only attribute + zip->entry.external_attr |= 0x01; + } + zip->entry.external_attr |= (mz_uint32)((file_stat.st_mode & 0xFFFF) << 16); + zip->entry.m_time = file_stat.st_mtime; + +#if defined(_MSC_VER) + if (fopen_s(&stream, filename, "rb")) +#else + if (!(stream = fopen(filename, "rb"))) +#endif + { + // Cannot open filename + return -1; + } + + while ((n = fread(buf, sizeof(mz_uint8), MZ_ZIP_MAX_IO_BUF_SIZE, stream)) > + 0) { + if (zip_entry_write(zip, buf, n) < 0) { + status = -1; + break; + } + } + fclose(stream); + + return status; +} + +ssize_t zip_entry_read(struct zip_t *zip, void **buf, size_t *bufsize) { + mz_zip_archive *pzip = NULL; + mz_uint idx; + size_t size = 0; + + if (!zip) { + // zip_t handler is not initialized + return -1; + } + + pzip = &(zip->archive); + if (pzip->m_zip_mode != MZ_ZIP_MODE_READING || zip->entry.index < 0) { + // the entry is not found or we do not have read access + return -1; + } + + idx = (mz_uint)zip->entry.index; + if (mz_zip_reader_is_file_a_directory(pzip, idx)) { + // the entry is a directory + return -1; + } + + *buf = mz_zip_reader_extract_to_heap(pzip, idx, &size, 0); + if (*buf && bufsize) { + *bufsize = size; + } + return size; +} + +ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf, size_t bufsize) { + mz_zip_archive *pzip = NULL; + + if (!zip) { + // zip_t handler is not initialized + return -1; + } + + pzip = &(zip->archive); + if (pzip->m_zip_mode != MZ_ZIP_MODE_READING || zip->entry.index < 0) { + // the entry is not found or we do not have read access + return -1; + } + + if (!mz_zip_reader_extract_to_mem_no_alloc(pzip, (mz_uint)zip->entry.index, + buf, bufsize, 0, NULL, 0)) { + return -1; + } + + return (ssize_t)zip->entry.uncomp_size; +} + +int zip_entry_fread(struct zip_t *zip, const char *filename) { + mz_zip_archive *pzip = NULL; + mz_uint idx; + mz_uint32 xattr = 0; + mz_zip_archive_file_stat info; + + if (!zip) { + // zip_t handler is not initialized + return -1; + } + + memset((void *)&info, 0, sizeof(mz_zip_archive_file_stat)); + pzip = &(zip->archive); + if (pzip->m_zip_mode != MZ_ZIP_MODE_READING || zip->entry.index < 0) { + // the entry is not found or we do not have read access + return -1; + } + + idx = (mz_uint)zip->entry.index; + if (mz_zip_reader_is_file_a_directory(pzip, idx)) { + // the entry is a directory + return -1; + } + + if (!mz_zip_reader_extract_to_file(pzip, idx, filename, 0)) { + return -1; + } + +#if defined(_MSC_VER) +#else + if (!mz_zip_reader_file_stat(pzip, idx, &info)) { + // Cannot get information about zip archive; + return -1; + } + + xattr = (info.m_external_attr >> 16) & 0xFFFF; + if (xattr > 0) { + if (chmod(filename, (mode_t)xattr) < 0) { + return -1; + } + } +#endif + + return 0; +} + +int zip_entry_extract(struct zip_t *zip, + size_t (*on_extract)(void *arg, unsigned long long offset, + const void *buf, size_t bufsize), + void *arg) { + mz_zip_archive *pzip = NULL; + mz_uint idx; + + if (!zip) { + // zip_t handler is not initialized + return -1; + } + + pzip = &(zip->archive); + if (pzip->m_zip_mode != MZ_ZIP_MODE_READING || zip->entry.index < 0) { + // the entry is not found or we do not have read access + return -1; + } + + idx = (mz_uint)zip->entry.index; + return (mz_zip_reader_extract_to_callback(pzip, idx, on_extract, arg, 0)) + ? 0 + : -1; +} + +int zip_total_entries(struct zip_t *zip) { + if (!zip) { + // zip_t handler is not initialized + return -1; + } + + return (int)zip->archive.m_total_files; +} + +int zip_create(const char *zipname, const char *filenames[], size_t len) { + int status = 0; + size_t i; + mz_zip_archive zip_archive; + struct MZ_FILE_STAT_STRUCT file_stat; + mz_uint32 ext_attributes = 0; + + if (!zipname || strlen(zipname) < 1) { + // zip_t archive name is empty or NULL + return -1; + } + + // Create a new archive. + if (!memset(&(zip_archive), 0, sizeof(zip_archive))) { + // Cannot memset zip archive + return -1; + } + + if (!mz_zip_writer_init_file(&zip_archive, zipname, 0)) { + // Cannot initialize zip_archive writer + return -1; + } + + memset((void *)&file_stat, 0, sizeof(struct MZ_FILE_STAT_STRUCT)); + + for (i = 0; i < len; ++i) { + const char *name = filenames[i]; + if (!name) { + status = -1; + break; + } + + if (MZ_FILE_STAT(name, &file_stat) != 0) { + // problem getting information - check errno + status = -1; + break; + } + + if ((file_stat.st_mode & 0200) == 0) { + // MS-DOS read-only attribute + ext_attributes |= 0x01; + } + ext_attributes |= (mz_uint32)((file_stat.st_mode & 0xFFFF) << 16); + + if (!mz_zip_writer_add_file(&zip_archive, base_name(name), name, "", 0, + ZIP_DEFAULT_COMPRESSION_LEVEL, + ext_attributes)) { + // Cannot add file to zip_archive + status = -1; + break; + } + } + + mz_zip_writer_finalize_archive(&zip_archive); + mz_zip_writer_end(&zip_archive); + return status; +} + +static char *normalize(char *name, char *const nname, size_t len) { + size_t offn = 0; + size_t offnn = 0, ncpy = 0; + + if (name == NULL || nname == NULL || len <= 0) { + return NULL; + } + // skip trailing '/' + while (ISSLASH(*name)) + name++; + + for (; offn < len; offn++) { + if (ISSLASH(name[offn])) { + if (ncpy > 0 && strncmp(&nname[offnn], ".", 1) && + strncmp(&nname[offnn], "..", 2)) { + offnn += ncpy; + nname[offnn++] = name[offn]; // append '/' + } + ncpy = 0; + } else { + nname[offnn + ncpy] = name[offn]; + ncpy++; + } + } + + // at the end, extra check what we've already copied + if (ncpy == 0 || !strncmp(&nname[offnn], ".", 1) || + !strncmp(&nname[offnn], "..", 2)) { + nname[offnn] = 0; + } + return nname; +} + +static int extract(mz_zip_archive *zip_archive, const char *dir, + int (*on_extract)(const char *filename, void *arg), + void *arg) { + int status = -1; + mz_uint i, n; + char path[MAX_PATH + 1]; + char symlink_to[MAX_PATH + 1]; + mz_zip_archive_file_stat info; + size_t dirlen = 0; + mz_uint32 xattr = 0; + + memset(path, 0, sizeof(path)); + memset(symlink_to, 0, sizeof(symlink_to)); + + dirlen = strlen(dir); + if (dirlen + 1 > MAX_PATH) { + return -1; + } + + memset((void *)&info, 0, sizeof(mz_zip_archive_file_stat)); + +#if defined(_MSC_VER) + strcpy_s(path, MAX_PATH, dir); +#else + strcpy(path, dir); +#endif + + if (!ISSLASH(path[dirlen - 1])) { +#if defined(_WIN32) || defined(__WIN32__) + path[dirlen] = '\\'; +#else + path[dirlen] = '/'; +#endif + ++dirlen; + } + + // Get and print information about each file in the archive. + n = mz_zip_reader_get_num_files(zip_archive); + for (i = 0; i < n; ++i) { + if (!mz_zip_reader_file_stat(zip_archive, i, &info)) { + // Cannot get information about zip archive; + goto out; + } + if (!normalize(info.m_filename, info.m_filename, strlen(info.m_filename))) { + // Cannot normalize file name; + goto out; + } +#if defined(_MSC_VER) + strncpy_s(&path[dirlen], MAX_PATH - dirlen, info.m_filename, + MAX_PATH - dirlen); +#else + strncpy(&path[dirlen], info.m_filename, MAX_PATH - dirlen); +#endif + if (mkpath(path) < 0) { + // Cannot make a path + goto out; + } + + if ((((info.m_version_made_by >> 8) == 3) || + ((info.m_version_made_by >> 8) == + 19)) // if zip is produced on Unix or macOS (3 and 19 from + // section 4.4.2.2 of zip standard) + && info.m_external_attr & + (0x20 << 24)) { // and has sym link attribute (0x80 is file, 0x40 + // is directory) +#if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) || \ + defined(__MINGW32__) +#else + if (info.m_uncomp_size > MAX_PATH || + !mz_zip_reader_extract_to_mem_no_alloc(zip_archive, i, symlink_to, + MAX_PATH, 0, NULL, 0)) { + goto out; + } + symlink_to[info.m_uncomp_size] = '\0'; + if (symlink(symlink_to, path) != 0) { + goto out; + } +#endif + } else { + if (!mz_zip_reader_is_file_a_directory(zip_archive, i)) { + if (!mz_zip_reader_extract_to_file(zip_archive, i, path, 0)) { + // Cannot extract zip archive to file + goto out; + } + } + +#if defined(_MSC_VER) +#else + xattr = (info.m_external_attr >> 16) & 0xFFFF; + if (xattr > 0) { + if (chmod(path, (mode_t)xattr) < 0) { + goto out; + } + } +#endif + } + + if (on_extract) { + if (on_extract(path, arg) < 0) { + goto out; + } + } + } + status = 0; + +out: + // Close the archive, freeing any resources it was using + if (!mz_zip_reader_end(zip_archive)) { + // Cannot end zip reader + status = -1; + } + return status; +} + +int zip_extract(const char *zipname, const char *dir, + int (*on_extract)(const char *filename, void *arg), void *arg) { + mz_zip_archive zip_archive; + if (!zipname || !dir) { + // Cannot parse zip archive name + return -1; + } + if (!memset(&zip_archive, 0, sizeof(mz_zip_archive))) { + // Cannot memset zip archive + return MZ_FALSE; + } + // Now try to open the archive. + if (!mz_zip_reader_init_file(&zip_archive, zipname, 0)) { + // Cannot initialize zip_archive reader + return MZ_FALSE; + } + + int status = extract(&zip_archive, dir, on_extract, arg); + + return status; +} + +int zip_extract_without_callback(const char *zipname, const char *dir) { + return zip_extract(zipname, dir, NULL, NULL); +} // for simple V bind ¯\_(ツ)_/¯ + +int zip_extract_stream(const char *stream, size_t size, const char *dir, + int (*on_extract)(const char *filename, void *arg), + void *arg) { + mz_zip_archive zip_archive; + if (!stream || !dir) { + // Cannot parse zip archive stream + return -1; + } + if (!memset(&zip_archive, 0, sizeof(mz_zip_archive))) { + // Cannot memset zip archive + return MZ_FALSE; + } + if (!mz_zip_reader_init_mem(&zip_archive, stream, size, 0)) { + // Cannot initialize zip_archive reader + return MZ_FALSE; + } + + int status = extract(&zip_archive, dir, on_extract, arg); + + return status; +} diff --git a/v_windows/v/old/thirdparty/zip/zip.h b/v_windows/v/old/thirdparty/zip/zip.h new file mode 100644 index 0000000..cd4198b --- /dev/null +++ b/v_windows/v/old/thirdparty/zip/zip.h @@ -0,0 +1,345 @@ +/* + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once +#ifndef ZIP_H +#define ZIP_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#if !defined(_SSIZE_T_DEFINED) && !defined(_SSIZE_T_DEFINED_) && \ + !defined(__DEFINED_ssize_t) && !defined(__ssize_t_defined) && \ + !defined(_SSIZE_T) && !defined(_SSIZE_T_) && !defined(_SSIZE_T_DECLARED) + +// 64-bit Windows is the only mainstream platform +// where sizeof(long) != sizeof(void*) +#ifdef _WIN64 +typedef long long ssize_t; /* byte count or error */ +#else +typedef long ssize_t; /* byte count or error */ +#endif + +#define _SSIZE_T_DEFINED +#define _SSIZE_T_DEFINED_ +#define __DEFINED_ssize_t +#define __ssize_t_defined +#define _SSIZE_T +#define _SSIZE_T_ +#define _SSIZE_T_DECLARED + +#endif + +#ifndef MAX_PATH +#define MAX_PATH 32767 /* # chars in a path name including NULL */ +#endif + +/** + * @mainpage + * + * Documenation for @ref zip. + */ + +/** + * @addtogroup zip + * @{ + */ + +/** + * Default zip compression level. + */ + +#define ZIP_DEFAULT_COMPRESSION_LEVEL 6 + +/** + * @struct zip_t + * + * This data structure is used throughout the library to represent zip archive - + * forward declaration. + */ +struct zip_t; + +/** + * Opens zip archive with compression level using the given mode. + * + * @param zipname zip archive file name. + * @param level compression level (0-9 are the standard zlib-style levels). + * @param mode file access mode. + * - 'r': opens a file for reading/extracting (the file must exists). + * - 'w': creates an empty file for writing. + * - 'a': appends to an existing archive. + * + * @return the zip archive handler or NULL on error + */ +extern struct zip_t *zip_open(const char *zipname, int level, char mode); + +/** + * Closes the zip archive, releases resources - always finalize. + * + * @param zip zip archive handler. + */ +extern void zip_close(struct zip_t *zip); + +/** + * Determines if the archive has a zip64 end of central directory headers. + * + * @param zip zip archive handler. + * + * @return the return code - 1 (true), 0 (false), negative number (< 0) on + * error. + */ +extern int zip_is64(struct zip_t *zip); + +/** + * Opens an entry by name in the zip archive. + * + * For zip archive opened in 'w' or 'a' mode the function will append + * a new entry. In readonly mode the function tries to locate the entry + * in global dictionary. + * + * @param zip zip archive handler. + * @param entryname an entry name in local dictionary. + * + * @return the return code - 0 on success, negative number (< 0) on error. + */ +extern int zip_entry_open(struct zip_t *zip, const char *entryname); + +/** + * Opens a new entry by index in the zip archive. + * + * This function is only valid if zip archive was opened in 'r' (readonly) mode. + * + * @param zip zip archive handler. + * @param index index in local dictionary. + * + * @return the return code - 0 on success, negative number (< 0) on error. + */ +extern int zip_entry_openbyindex(struct zip_t *zip, int index); + +/** + * Closes a zip entry, flushes buffer and releases resources. + * + * @param zip zip archive handler. + * + * @return the return code - 0 on success, negative number (< 0) on error. + */ +extern int zip_entry_close(struct zip_t *zip); + +/** + * Returns a local name of the current zip entry. + * + * The main difference between user's entry name and local entry name + * is optional relative path. + * Following .ZIP File Format Specification - the path stored MUST not contain + * a drive or device letter, or a leading slash. + * All slashes MUST be forward slashes '/' as opposed to backwards slashes '\' + * for compatibility with Amiga and UNIX file systems etc. + * + * @param zip: zip archive handler. + * + * @return the pointer to the current zip entry name, or NULL on error. + */ +extern const char *zip_entry_name(struct zip_t *zip); + +/** + * Returns an index of the current zip entry. + * + * @param zip zip archive handler. + * + * @return the index on success, negative number (< 0) on error. + */ +extern int zip_entry_index(struct zip_t *zip); + +/** + * Determines if the current zip entry is a directory entry. + * + * @param zip zip archive handler. + * + * @return the return code - 1 (true), 0 (false), negative number (< 0) on + * error. + */ +extern int zip_entry_isdir(struct zip_t *zip); + +/** + * Returns an uncompressed size of the current zip entry. + * + * @param zip zip archive handler. + * + * @return the uncompressed size in bytes. + */ +extern unsigned long long zip_entry_size(struct zip_t *zip); + +/** + * Returns CRC-32 checksum of the current zip entry. + * + * @param zip zip archive handler. + * + * @return the CRC-32 checksum. + */ +extern unsigned int zip_entry_crc32(struct zip_t *zip); + +/** + * Compresses an input buffer for the current zip entry. + * + * @param zip zip archive handler. + * @param buf input buffer. + * @param bufsize input buffer size (in bytes). + * + * @return the return code - 0 on success, negative number (< 0) on error. + */ +extern int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize); + +/** + * Compresses a file for the current zip entry. + * + * @param zip zip archive handler. + * @param filename input file. + * + * @return the return code - 0 on success, negative number (< 0) on error. + */ +extern int zip_entry_fwrite(struct zip_t *zip, const char *filename); + +/** + * Extracts the current zip entry into output buffer. + * + * The function allocates sufficient memory for a output buffer. + * + * @param zip zip archive handler. + * @param buf output buffer. + * @param bufsize output buffer size (in bytes). + * + * @note remember to release memory allocated for a output buffer. + * for large entries, please take a look at zip_entry_extract function. + * + * @return the return code - the number of bytes actually read on success. + * Otherwise a -1 on error. + */ +extern ssize_t zip_entry_read(struct zip_t *zip, void **buf, size_t *bufsize); + +/** + * Extracts the current zip entry into a memory buffer using no memory + * allocation. + * + * @param zip zip archive handler. + * @param buf preallocated output buffer. + * @param bufsize output buffer size (in bytes). + * + * @note ensure supplied output buffer is large enough. + * zip_entry_size function (returns uncompressed size for the current + * entry) can be handy to estimate how big buffer is needed. for large + * entries, please take a look at zip_entry_extract function. + * + * @return the return code - the number of bytes actually read on success. + * Otherwise a -1 on error (e.g. bufsize is not large enough). + */ +extern ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf, + size_t bufsize); + +/** + * Extracts the current zip entry into output file. + * + * @param zip zip archive handler. + * @param filename output file. + * + * @return the return code - 0 on success, negative number (< 0) on error. + */ +extern int zip_entry_fread(struct zip_t *zip, const char *filename); + +/** + * Extracts the current zip entry using a callback function (on_extract). + * + * @param zip zip archive handler. + * @param on_extract callback function. + * @param arg opaque pointer (optional argument, which you can pass to the + * on_extract callback) + * + * @return the return code - 0 on success, negative number (< 0) on error. + */ +extern int +zip_entry_extract(struct zip_t *zip, + size_t (*on_extract)(void *arg, unsigned long long offset, + const void *data, size_t size), + void *arg); + +/** + * Returns the number of all entries (files and directories) in the zip archive. + * + * @param zip zip archive handler. + * + * @return the return code - the number of entries on success, negative number + * (< 0) on error. + */ +extern int zip_total_entries(struct zip_t *zip); + +/** + * Creates a new archive and puts files into a single zip archive. + * + * @param zipname zip archive file. + * @param filenames input files. + * @param len: number of input files. + * + * @return the return code - 0 on success, negative number (< 0) on error. + */ +extern int zip_create(const char *zipname, const char *filenames[], size_t len); + +/** + * Extracts a zip archive file into directory. + * + * If on_extract_entry is not NULL, the callback will be called after + * successfully extracted each zip entry. + * Returning a negative value from the callback will cause abort and return an + * error. The last argument (void *arg) is optional, which you can use to pass + * data to the on_extract_entry callback. + * + * @param zipname zip archive file. + * @param dir output directory. + * @param on_extract_entry on extract callback. + * @param arg opaque pointer. + * + * @return the return code - 0 on success, negative number (< 0) on error. + */ +extern int zip_extract(const char *zipname, const char *dir, + int (*on_extract_entry)(const char *filename, void *arg), + void *arg); +// temporary working unzip solution +extern int zip_extract_without_callback(const char *zipname, const char *dir); + +/** + * Extracts a zip archive stream into directory. + * + * If on_extract is not NULL, the callback will be called after + * successfully extracted each zip entry. + * Returning a negative value from the callback will cause abort and return an + * error. The last argument (void *arg) is optional, which you can use to pass + * data to the on_extract callback. + * + * @param stream zip archive stream. + * @param size stream size. + * @param dir output directory. + * @param on_extract on extract callback. + * @param arg opaque pointer. + * + * @return the return code - 0 on success, negative number (< 0) on error. + */ +extern int zip_extract_stream(const char *stream, size_t size, const char *dir, + int (*on_extract)(const char *filename, + void *arg), + void *arg); +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/README.md b/v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/README.md new file mode 100644 index 0000000..0225cb5 --- /dev/null +++ b/v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/README.md @@ -0,0 +1,446 @@ +## Building a 150 KB web blog in V with 0 dependencies + +Hello, + +In this guide, we'll build a simple web blog in V. + +The benefits of using V for web: +- A safe, fast, language with the development agility of Python or Ruby and +the performance of C. +- Zero dependencies: everything you need for web development comes with the language +in a 1 MB package. +- Very small resulting binaries: the blog we'll create in this tutorial is about 150 KB. +- Easy deployments: a single binary file that even includes the precompiled templates. +- Runs on the cheapest hardware with minimum footprint: for most apps a $3 instance +is enough. +- Fast development without any boilerplate. + +*Please note that V and Vweb are at a very early stage and are changing rapidly.* + +The code is available here. + + +### Installing V + +``` +wget https://github.com/vlang/v/releases/latest/download/v_linux.zip +unzip v_linux.zip +cd v +sudo ./v symlink +``` + +Now V should be globally available on your system. + +> On macOS use `v_macos.zip`, on Windows - `v_windows.zip`. +If you use a BSD system, Solaris, Android, or simply want to install V +from source, follow the simple instructions here: +https://github.com/vlang/v#installing-v-from-source + + +### Install SQLite development dependency + +If you don't have it already installed, look at the +[`sqlite` README](../../vlib/sqlite/README.md) for instructions. + + +### Creating a new Vweb project + +V projects can be created anywhere and don't need to have a certain structure: + +```bash +mkdir blog +v init +``` + +First, let's create a simple hello world website: + +```v oksyntax +// blog.v +module main + +import vweb + +struct App { + vweb.Context +} + +fn main() { + app := App{} + vweb.run(app, 8081) +} + +['/index'] +pub fn (mut app App) index() vweb.Result { + return app.text('Hello world from vweb!') +} +``` + +Run it with + +```bash +v run blog.v +``` + +``` +Running a Vweb app on http://localhost:8081 ... +``` + +Vweb helpfully provided a link, open http://localhost:8081/ in your browser: + + + +The `App` struct is an entry point of our web application. If you have experience +with an MVC web framework, you can think of it as a controller. (Vweb is +not an MVC framework however.) It embeds the vweb Context object, that's why we get access +to methods like `.text()`. + + +As you can see, there are no routing rules. The `index()` action handles the `/` request by default. +Vweb often uses convention over configuration and adding a new action requires +no routing rules either: + +```v oksyntax +// blog.v +import vweb +import time + +fn (mut app App) time() vweb.Result { + return app.text(time.now().format()) +} +``` + + + + +>You have to rebuild and restart the website every time you change the code. +In the future, Vweb will detect changes and recompile the website in the background +while it's running. + +The `.text(string)` method returns a plain text document with the provided +text, which isn't frequently used in websites. + + +### HTML View + +Let's return an HTML view instead. Create `index.html` in the same directory: + +```html + + + V Blog + + + @message +
      + + + +``` + +and update our `index()` action so that it returns the HTML view we just created: + +```v ignore +// blog.v +pub fn (mut app App) index() vweb.Result { + message := 'Hello, world from Vweb!' + return $vweb.html() +} +``` + + + +Good, now we have an actual HTML page. + +The V template language is similar to C#'s Razor: `@message` prints the value +of `message`. + +You may notice something unusual: the `message` variable created in the `index()` +action is automatically available in the view. + +It's another feature of Vweb to reduce the boilerplate in your web apps. +No need to create view models just to pass data, or use an unsafe and untyped +alternative, like C#'s `ViewBag["message"]`. + +Making all action variables available in the view may seem crazy, +but V is a language with pure functions by default, and you won't be able +to modify any data from a view. `@foo.bar()` will only work if the `bar()` method +doesn't modify `foo`. + +The HTML template is compiled to V during the compilation of the website, +that's done by the `$vweb.html()` line. +(`$` always means compile time actions in V.) offering the following benefits: + +- Great performance, since the templates don't need to be compiled +on every request, like in almost every major web framework. + +- Easier deployment, since all your HTML templates are compiled +into a single binary file together with the web application itself. + +- All errors in the templates are guaranteed to be caught during compilation. + + +### Fetching data with V ORM + +Now let's display some articles! + +We'll be using V's builtin ORM and a SQLite database. +(V ORM will also support MySQL, Postgre, and SQL Server soon.) + + +Add a SQLite handle to `App`: + +```v oksyntax +// blog.v +import sqlite +import vweb + +struct App { + vweb.Context +pub mut: + db sqlite.DB +} +``` + + + +Add the `init_server()` method where we'll connect to a database: + +```v oksyntax +// blog.v +pub fn (mut app App) init_server() { + app.db = sqlite.connect(':memory:') or { panic(err) } + sql app.db { + create table Article + } + + first_article := Article{ + title: 'Hello, world!' + text: 'V is great.' + } + + second_article := Article{ + title: 'Second post.' + text: 'Hm... what should I write about?' + } + + sql app.db { + insert first_article into Article + insert second_article into Article + } +} +``` + +Code in the `init_server()` function is run only once during app's startup, so we are going +to have one DB connection for all requests. Modify the main method to call the `init_server()` +function before adding it to the vweb system: + +```v oksyntax +fn main() { + mut app := App{} + app.init_server() + vweb.run(app, 8081) +} +``` + +Because `init_server()` modifies properties of the app struct we now have to make it mutable +with the `mut` keyword. + +Create a new file `article.v`: + + +```v oksyntax +// article.v +module main + +struct Article { + id int [primary; sql: serial] + title string + text string +} + +pub fn (app &App) find_all_articles() []Article { + return sql app.db { + select from Article + } +} +``` + +Notice that the `Article` structure conforms to the same structure and naming as +the database table in the creation SQL statement. Also we need to add ORM decorators +to our primary key to let it know that it is the primary key and it should auto-increment + +Let's fetch the articles in the `index()` action: + +```v ignore +// blog.v +pub fn (app App) index() vweb.Result { + articles := app.find_all_articles() + return $vweb.html() +} +``` + + +Finally, let's update our view: + +```html + + @for article in articles +
      + @article.title
      + @article.text +
      + @end + +``` + +```bash +v run . +``` + + + +That was very simple, wasn't it? + +The built-in V ORM uses a syntax very similar to SQL. The queries are built with V. +For example, if we only wanted to find articles with ids between 100 and 200, we'd do: + +```v oksyntax +// article.v + +return sql app.db { + select from Article where id >= 100 && id <= 200 +} +``` + +Retrieving a single article is very simple: + +```v oksyntax +// article.v +pub fn (app &App) retrieve_article() ?Article { + return sql app.db { + select from Article limit 1 + } +} +``` + +V ORM uses V's optionals for single values, which is very useful, since +bad queries will always be handled by the developer: + +```v ignore +// article.v +article := app.retrieve_article(10) or { + app.text('Article not found') + return +} +``` + + +### Adding new articles + +Create `new.html`: + +```html + + + V Blog + + +
      +
      + + +
      + + +``` + +```v ignore +// article.v +import vweb +['/new_article'; post] +pub fn (mut app App) new_article() vweb.Result { + title := app.form['title'] + text := app.form['text'] + if title == '' || text == '' { + return app.text('Empty text/title') + } + article := Article{ + title: title + text: text + } + println(article) + sql app.db { + insert article into Article + } + return app.redirect('/') +} +``` + +> Untyped `form['key']` is temporary. Very soon Vweb will accept query and form +parameters via function arguments: `new_article(title, text string) {`. + +The decorator on our function tells vweb the path to our endpoint, `/new_article`, +and that it is an HTTP POST type operation. + +We need to update `index.html` to add a link to the "new article" page: + +```html +New article +``` + +Next we need to add the HTML endpoint to our code like we did with `index.html`: + +```v ignore +['/new'] +pub fn (mut app App) new() vweb.Result { + return $vweb.html() +} +``` + +Re-running this code will now allow us to add new posts to our blog endpoint + + +### JSON endpoints + +This tutorial used the traditional server-side rendering. If you prefer +to render everything on the client or need an API, creating JSON endpoints +in V is very simple: + +```v oksyntax +// article.v +import vweb +import json + +['/articles'; get] +pub fn (mut app App) articles() vweb.Result { + articles := app.find_all_articles() + return app.json(json.encode(articles)) +} +``` + + + + +### Persistent data +If one wants to persist data they need to use a file instead of memory SQLite Database. +Replace the `init_server()` function with this instead: + +```v oksyntax +// blog.v +pub fn (mut app App) init_server() { + app.db = sqlite.connect('blog.db') or { panic(err) } + sql app.db { + create table Article + } +} +``` + +As we can see it attempts to open a file in the current directory named `blog.db`. +If the database file doesn't exist it will create it. The second command will +create the table `Article` if none exists already. Now every time the +app is run you will see the articles created from the previous executions + + +To be continued... + +For an example of a more sophisticated web app written in V, check out Vorum: https://github.com/vlang/vorum diff --git a/v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/code/blog/.gitignore b/v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/code/blog/.gitignore new file mode 100644 index 0000000..2fdf6da --- /dev/null +++ b/v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/code/blog/.gitignore @@ -0,0 +1 @@ +blog.db diff --git a/v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/code/blog/article.v b/v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/code/blog/article.v new file mode 100644 index 0000000..f127532 --- /dev/null +++ b/v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/code/blog/article.v @@ -0,0 +1,13 @@ +module main + +struct Article { + id int [primary; sql: serial] + title string + text string +} + +pub fn (app &App) find_all_articles() []Article { + return sql app.db { + select from Article + } +} diff --git a/v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/code/blog/blog.sqlite b/v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/code/blog/blog.sqlite new file mode 100644 index 0000000..c5956d6 --- /dev/null +++ b/v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/code/blog/blog.sqlite @@ -0,0 +1,17 @@ +drop table if exists Article; + +create table Article ( + id integer primary key, + title text default "", + text text default "" +); + +insert into Article (title, text) values ( + "Hello, world!", + "V is great." +); + +insert into Article (title, text) values ( + "Second post.", + "Hm... what should I write about?" +); diff --git a/v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/code/blog/blog.v b/v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/code/blog/blog.v new file mode 100644 index 0000000..823567c --- /dev/null +++ b/v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/code/blog/blog.v @@ -0,0 +1,83 @@ +module main + +import vweb +import time +import sqlite +import json + +struct App { + vweb.Context +pub mut: + db sqlite.DB [server_var] + user_id string +} + +fn main() { + mut app := App{} + app.init_server() + vweb.run(app, 8081) +} + +/* +pub fn (mut app App) index_text() vweb.Result { + app.vweb.text('Hello, world from vweb!') + return vweb.Result{} +} + +pub fn (app &App) index_html() vweb.Result { + message := 'Hello, world from Vweb!' + return $vweb.html() +} +*/ +['/index'] +pub fn (app &App) index() vweb.Result { + articles := app.find_all_articles() + return $vweb.html() +} + +pub fn (mut app App) init_server() { + app.db = sqlite.connect('blog.db') or { panic(err) } + sql app.db { + create table Article + } +} + +pub fn (mut app App) before_request() { + app.user_id = app.get_cookie('id') or { '0' } +} + +['/new'] +pub fn (mut app App) new() vweb.Result { + return $vweb.html() +} + +['/new_article'; post] +pub fn (mut app App) new_article() vweb.Result { + title := app.form['title'] + text := app.form['text'] + if title == '' || text == '' { + return app.text('Empty text/title') + } + article := Article{ + title: title + text: text + } + println('posting article') + println(article) + sql app.db { + insert article into Article + } + + return app.redirect('/') +} + +['/articles'; get] +pub fn (mut app App) articles() vweb.Result { + articles := app.find_all_articles() + json_result := json.encode(articles) + return app.json(json_result) +} + +fn (mut app App) time() { + app.text(time.now().format()) +} diff --git a/v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/code/blog/index.html b/v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/code/blog/index.html new file mode 100644 index 0000000..399b267 --- /dev/null +++ b/v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/code/blog/index.html @@ -0,0 +1,16 @@ + +
      + V Blog +
      + + user id = @app.user_id
      + @for article in articles +
      + @article.title
      + @article.text +
      +
      + @end + New article + + diff --git a/v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/code/blog/new.html b/v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/code/blog/new.html new file mode 100644 index 0000000..04ca000 --- /dev/null +++ b/v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/code/blog/new.html @@ -0,0 +1,13 @@ + +
      + V Blog +
      + +
      +
      + + +
      + + + diff --git a/v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/img/articles1.png b/v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/img/articles1.png new file mode 100644 index 0000000..2e17732 Binary files /dev/null and b/v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/img/articles1.png differ diff --git a/v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/img/articles_json.png b/v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/img/articles_json.png new file mode 100644 index 0000000..dd95810 Binary files /dev/null and b/v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/img/articles_json.png differ diff --git a/v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/img/hello.png b/v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/img/hello.png new file mode 100644 index 0000000..aa45962 Binary files /dev/null and b/v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/img/hello.png differ diff --git a/v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/img/hello_html.png b/v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/img/hello_html.png new file mode 100644 index 0000000..a6d2195 Binary files /dev/null and b/v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/img/hello_html.png differ diff --git a/v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/img/time.png b/v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/img/time.png new file mode 100644 index 0000000..62c372e Binary files /dev/null and b/v_windows/v/old/tutorials/building_a_simple_web_blog_with_vweb/img/time.png differ diff --git a/v_windows/v/old/v.def b/v_windows/v/old/v.def new file mode 100644 index 0000000..549a74f --- /dev/null +++ b/v_windows/v/old/v.def @@ -0,0 +1,5 @@ +LIBRARY v.exe + +EXPORTS +__bt_init +tcc_backtrace diff --git a/v_windows/v/old/v.exe b/v_windows/v/old/v.exe new file mode 100644 index 0000000..ce39675 Binary files /dev/null and b/v_windows/v/old/v.exe differ diff --git a/v_windows/v/old/v.mod b/v_windows/v/old/v.mod new file mode 100644 index 0000000..fc3d660 --- /dev/null +++ b/v_windows/v/old/v.mod @@ -0,0 +1,8 @@ +Module { + name: 'V' + description: 'The V programming language.' + version: '0.2.2' + license: 'MIT' + repo_url: 'https://github.com/vlang/v' + dependencies: [] +} diff --git a/v_windows/v/old/v_old.exe b/v_windows/v/old/v_old.exe new file mode 100644 index 0000000..0080ab7 Binary files /dev/null and b/v_windows/v/old/v_old.exe differ diff --git a/v_windows/v/old/vc b/v_windows/v/old/vc new file mode 160000 index 0000000..a5dccf8 --- /dev/null +++ b/v_windows/v/old/vc @@ -0,0 +1 @@ +Subproject commit a5dccf83c1c5dfdd81d924d0d2985b30f73c3146 diff --git a/v_windows/v/old/vlib/.vdocignore b/v_windows/v/old/vlib/.vdocignore new file mode 100644 index 0000000..74dadf4 --- /dev/null +++ b/v_windows/v/old/vlib/.vdocignore @@ -0,0 +1,15 @@ +builtin/bare +builtin/js +builtin/linux_bare +dl/example +oldgg/ +os/bare +os2/ +net/websocket/examples +uiold/ +v/tests/ +v/checker/tests/ +v/gen/js/tests/ +v/gen/x64/tests/ +v/gen/tests/ +vweb/tests diff --git a/v_windows/v/old/vlib/arrays/arrays.v b/v_windows/v/old/vlib/arrays/arrays.v new file mode 100644 index 0000000..26636d1 --- /dev/null +++ b/v_windows/v/old/vlib/arrays/arrays.v @@ -0,0 +1,126 @@ +module arrays + +// Common arrays functions: +// - min / max - return the value of the minumum / maximum +// - idx_min / idx_max - return the index of the first minumum / maximum +// - merge - combine two sorted arrays and maintain sorted order + +// min returns the minimum +pub fn min(a []T) T { + if a.len == 0 { + panic('.min called on an empty array') + } + mut val := a[0] + for e in a { + if e < val { + val = e + } + } + return val +} + +// max returns the maximum +pub fn max(a []T) T { + if a.len == 0 { + panic('.max called on an empty array') + } + mut val := a[0] + for e in a { + if e > val { + val = e + } + } + return val +} + +// idx_min returns the index of the first minimum +pub fn idx_min(a []T) int { + if a.len == 0 { + panic('.idx_min called on an empty array') + } + mut idx := 0 + mut val := a[0] + for i, e in a { + if e < val { + val = e + idx = i + } + } + return idx +} + +// idx_max returns the index of the first maximum +pub fn idx_max(a []T) int { + if a.len == 0 { + panic('.idx_max called on an empty array') + } + mut idx := 0 + mut val := a[0] + for i, e in a { + if e > val { + val = e + idx = i + } + } + return idx +} + +// merge two sorted arrays (ascending) and maintain sorted order +[direct_array_access] +pub fn merge(a []T, b []T) []T { + mut m := []T{len: a.len + b.len} + mut ia := 0 + mut ib := 0 + mut j := 0 + // TODO efficient approach to merge_desc where: a[ia] >= b[ib] + for ia < a.len && ib < b.len { + if a[ia] <= b[ib] { + m[j] = a[ia] + ia++ + } else { + m[j] = b[ib] + ib++ + } + j++ + } + // a leftovers + for ia < a.len { + m[j] = a[ia] + ia++ + j++ + } + // b leftovers + for ib < b.len { + m[j] = b[ib] + ib++ + j++ + } + return m +} + +// group n arrays into a single array of arrays with n elements +pub fn group(lists ...[]T) [][]T { + mut length := if lists.len > 0 { lists[0].len } else { 0 } + // calculate length of output by finding shortest input array + for ndx in 1 .. lists.len { + if lists[ndx].len < length { + length = lists[ndx].len + } + } + + if length > 0 { + mut arr := [][]T{cap: length} + // append all combined arrays into the resultant array + for ndx in 0 .. length { + mut zipped := []T{cap: lists.len} + // combine each list item for the ndx position into one array + for list_ndx in 0 .. lists.len { + zipped << lists[list_ndx][ndx] + } + arr << zipped + } + return arr + } + + return [][]T{} +} diff --git a/v_windows/v/old/vlib/arrays/arrays_test.v b/v_windows/v/old/vlib/arrays/arrays_test.v new file mode 100644 index 0000000..54042b1 --- /dev/null +++ b/v_windows/v/old/vlib/arrays/arrays_test.v @@ -0,0 +1,87 @@ +module arrays + +fn test_min() { + a := [8, 2, 6, 4] + assert min(a) == 2 + assert min(a[2..]) == 4 + b := [f32(5.1), 3.1, 1.1, 9.1] + assert min(b) == f32(1.1) + assert min(b[..2]) == f32(3.1) + c := [byte(4), 9, 3, 1] + assert min(c) == byte(1) + assert min(c[..3]) == byte(3) +} + +fn test_max() { + a := [8, 2, 6, 4] + assert max(a) == 8 + assert max(a[1..]) == 6 + b := [f32(5.1), 3.1, 1.1, 9.1] + assert max(b) == f32(9.1) + assert max(b[..3]) == f32(5.1) + c := [byte(4), 9, 3, 1] + assert max(c) == byte(9) + assert max(c[2..]) == byte(3) +} + +fn test_idx_min() { + a := [8, 2, 6, 4] + assert idx_min(a) == 1 + b := [f32(5.1), 3.1, 1.1, 9.1] + assert idx_min(b) == 2 + c := [byte(4), 9, 3, 1] + assert idx_min(c) == 3 +} + +fn test_idx_max() { + a := [8, 2, 6, 4] + assert idx_max(a) == 0 + b := [f32(5.1), 3.1, 1.1, 9.1] + assert idx_max(b) == 3 + c := [byte(4), 9, 3, 1] + assert idx_max(c) == 1 +} + +fn test_merge() { + a := [1, 3, 5, 5, 7] + b := [2, 4, 4, 5, 6, 8] + c := []int{} + d := []int{} + assert merge(a, b) == [1, 2, 3, 4, 4, 5, 5, 5, 6, 7, 8] + assert merge(c, d) == [] + assert merge(a, c) == a + assert merge(d, b) == b +} + +fn test_fixed_array_assignment() { + mut a := [2]int{} + a[0] = 111 + a[1] = 222 + b := a + assert b[0] == a[0] + assert b[1] == a[1] + mut c := [2]int{} + c = a + assert c[0] == a[0] + assert c[1] == a[1] + d := [3]int{init: 333} + for val in d { + assert val == 333 + } + e := [3]string{init: 'vlang'} + for val in e { + assert val == 'vlang' + } +} + +fn test_group() { + x := [4, 5, 6] + y := [2, 1, 3] + + z := group(x, y) + assert z == [[4, 2], [5, 1], [6, 3]] + x2 := [8, 9] + z2 := group(x2, y) + assert z2 == [[8, 2], [9, 1]] + assert group(x, []int{}) == [][]int{} +} diff --git a/v_windows/v/old/vlib/benchmark/README.md b/v_windows/v/old/vlib/benchmark/README.md new file mode 100644 index 0000000..6f4f7db --- /dev/null +++ b/v_windows/v/old/vlib/benchmark/README.md @@ -0,0 +1,45 @@ +Example usage of this module: +```v +import benchmark + +mut bmark := benchmark.new_benchmark() +// by default the benchmark will be verbose, i.e. it will include timing information +// if you want it to be silent, set bmark.verbose = false +for { + bmark.step() // call this when you want to advance the benchmark. + // The timing info in bmark.step_message will be measured starting from the last call to bmark.step + // .... + // bmark.fail() // call this if the step failed + // bmark.step_message(('failed') + bmark.ok() // call this when the step succeeded + println(bmark.step_message('ok')) +} +bmark.stop() +// call when you want to finalize the benchmark +println(bmark.total_message('remarks about the benchmark')) +``` + +benchmark.start() and b.measure() are convenience methods, +intended to be used in combination. Their goal is to make +benchmarking of small snippets of code as *short*, easy to +write, and then to read and analyze the results, as possible. + +Example: +```v +import time +import benchmark + +mut b := benchmark.start() +// your code section 1 ... +time.sleep(1500 * time.millisecond) +b.measure('code_1') +// your code section 2 ... +time.sleep(500 * time.millisecond) +b.measure('code_2') +``` + +... which will produce on stdout something like this: +```text +SPENT 1500.063 ms in code_1 +SPENT 500.061 ms in code_2 +``` diff --git a/v_windows/v/old/vlib/benchmark/benchmark.v b/v_windows/v/old/vlib/benchmark/benchmark.v new file mode 100644 index 0000000..cd9f994 --- /dev/null +++ b/v_windows/v/old/vlib/benchmark/benchmark.v @@ -0,0 +1,218 @@ +module benchmark + +import time +import term + +pub const ( + b_ok = term.ok_message('OK ') + b_fail = term.fail_message('FAIL') + b_skip = term.warn_message('SKIP') + b_spent = term.ok_message('SPENT') +) + +pub struct Benchmark { +pub mut: + bench_timer time.StopWatch + verbose bool + no_cstep bool + step_timer time.StopWatch + ntotal int + nok int + nfail int + nskip int + nexpected_steps int + cstep int + bok string + bfail string +} + +// new_benchmark returns a `Benchmark` instance on the stack. +pub fn new_benchmark() Benchmark { + return Benchmark{ + bench_timer: time.new_stopwatch() + verbose: true + } +} + +// new_benchmark_no_cstep returns a new `Benchmark` instance with step counting disabled. +pub fn new_benchmark_no_cstep() Benchmark { + return Benchmark{ + bench_timer: time.new_stopwatch() + verbose: true + no_cstep: true + } +} + +// new_benchmark_pointer returns a new `Benchmark` instance allocated on the heap. +// This is useful for long-lived use of `Benchmark` instances. +pub fn new_benchmark_pointer() &Benchmark { + return &Benchmark{ + bench_timer: time.new_stopwatch() + verbose: true + } +} + +// set_total_expected_steps sets the the total amount of steps the benchmark is expected to take. +pub fn (mut b Benchmark) set_total_expected_steps(n int) { + b.nexpected_steps = n +} + +// stop stops the internal benchmark timer. +pub fn (mut b Benchmark) stop() { + b.bench_timer.stop() +} + +// step increases the step count by 1 and restarts the internal timer. +pub fn (mut b Benchmark) step() { + b.step_timer.restart() + if !b.no_cstep { + b.cstep++ + } +} + +// fail increases the fail count by 1 and stops the internal timer. +pub fn (mut b Benchmark) fail() { + b.step_timer.stop() + b.ntotal++ + b.nfail++ +} + +// ok increases the ok count by 1 and stops the internal timer. +pub fn (mut b Benchmark) ok() { + b.step_timer.stop() + b.ntotal++ + b.nok++ +} + +// skip increases the skip count by 1 and stops the internal timer. +pub fn (mut b Benchmark) skip() { + b.step_timer.stop() + b.ntotal++ + b.nskip++ +} + +// fail_many increases the fail count by `n` and stops the internal timer. +pub fn (mut b Benchmark) fail_many(n int) { + b.step_timer.stop() + b.ntotal += n + b.nfail += n +} + +// ok_many increases the ok count by `n` and stops the internal timer. +pub fn (mut b Benchmark) ok_many(n int) { + b.step_timer.stop() + b.ntotal += n + b.nok += n +} + +// neither_fail_nor_ok stops the internal timer. +pub fn (mut b Benchmark) neither_fail_nor_ok() { + b.step_timer.stop() +} + +// start returns a new, running, instance of `Benchmark`. +// This is a shorthand for calling `new_benchmark().step()`. +pub fn start() Benchmark { + mut b := new_benchmark() + b.step() + return b +} + +// measure prints the current time spent doing `label`, since the benchmark was started. +pub fn (mut b Benchmark) measure(label string) i64 { + b.ok() + res := b.step_timer.elapsed().microseconds() + println(b.step_message_with_label(benchmark.b_spent, 'in $label')) + b.step() + return res +} + +// step_message_with_label_and_duration returns a string describing the current step. +pub fn (b &Benchmark) step_message_with_label_and_duration(label string, msg string, sduration time.Duration) string { + timed_line := b.tdiff_in_ms(msg, sduration.microseconds()) + if b.nexpected_steps > 1 { + mut sprogress := '' + if b.nexpected_steps < 10 { + sprogress = if b.no_cstep { + 'TMP1/${b.nexpected_steps:1d}' + } else { + '${b.cstep:1d}/${b.nexpected_steps:1d}' + } + } else if b.nexpected_steps >= 10 && b.nexpected_steps < 100 { + sprogress = if b.no_cstep { + 'TMP2/${b.nexpected_steps:2d}' + } else { + '${b.cstep:2d}/${b.nexpected_steps:2d}' + } + } else if b.nexpected_steps >= 100 && b.nexpected_steps < 1000 { + sprogress = if b.no_cstep { + 'TMP3/${b.nexpected_steps:3d}' + } else { + '${b.cstep:3d}/${b.nexpected_steps:3d}' + } + } else { + sprogress = if b.no_cstep { + 'TMP4/${b.nexpected_steps:4d}' + } else { + '${b.cstep:4d}/${b.nexpected_steps:4d}' + } + } + return '${label:-5s} [$sprogress] $timed_line' + } + return '${label:-5s}$timed_line' +} + +// step_message_with_label returns a string describing the current step using current time as duration. +pub fn (b &Benchmark) step_message_with_label(label string, msg string) string { + return b.step_message_with_label_and_duration(label, msg, b.step_timer.elapsed()) +} + +// step_message returns a string describing the current step. +pub fn (b &Benchmark) step_message(msg string) string { + return b.step_message_with_label('', msg) +} + +// step_message_ok returns a string describing the current step with an standard "OK" label. +pub fn (b &Benchmark) step_message_ok(msg string) string { + return b.step_message_with_label(benchmark.b_ok, msg) +} + +// step_message_fail returns a string describing the current step with an standard "FAIL" label. +pub fn (b &Benchmark) step_message_fail(msg string) string { + return b.step_message_with_label(benchmark.b_fail, msg) +} + +// step_message_skip returns a string describing the current step with an standard "SKIP" label. +pub fn (b &Benchmark) step_message_skip(msg string) string { + return b.step_message_with_label(benchmark.b_skip, msg) +} + +// total_message returns a string with total summary of the benchmark run. +pub fn (b &Benchmark) total_message(msg string) string { + the_label := term.colorize(term.gray, msg) + mut tmsg := '${term.colorize(term.bold, 'Summary for $the_label:')} ' + if b.nfail > 0 { + tmsg += term.colorize(term.bold, term.colorize(term.red, '$b.nfail failed')) + ', ' + } + if b.nok > 0 { + tmsg += term.colorize(term.bold, term.colorize(term.green, '$b.nok passed')) + ', ' + } + if b.nskip > 0 { + tmsg += term.colorize(term.bold, term.colorize(term.yellow, '$b.nskip skipped')) + ', ' + } + tmsg += '$b.ntotal total. ${term.colorize(term.bold, 'Runtime:')} ${b.bench_timer.elapsed().microseconds() / 1000} ms.\n' + return tmsg +} + +// total_duration returns the duration in ms. +pub fn (b &Benchmark) total_duration() i64 { + return b.bench_timer.elapsed().milliseconds() +} + +// tdiff_in_ms prefixes `s` with a time difference calculation. +fn (b &Benchmark) tdiff_in_ms(s string, tdiff i64) string { + if b.verbose { + return '${f64(tdiff) / 1000.0:9.3f} ms $s' + } + return s +} diff --git a/v_windows/v/old/vlib/bitfield/README.md b/v_windows/v/old/vlib/bitfield/README.md new file mode 100644 index 0000000..8b82c4c --- /dev/null +++ b/v_windows/v/old/vlib/bitfield/README.md @@ -0,0 +1,11 @@ +# Quickstart + +`bitfield` is a module for +manipulating arrays of bits, i.e. series of zeroes and ones spread across an +array of storage units (unsigned 32-bit integers). + +## BitField structure + +Bit arrays are stored in data structures called 'BitField'. The structure is +'opaque', i.e. its internals are not available to the end user. This module +provides API (functions and methods) for accessing and modifying bit arrays. diff --git a/v_windows/v/old/vlib/bitfield/bitfield.v b/v_windows/v/old/vlib/bitfield/bitfield.v new file mode 100644 index 0000000..9ed4e2b --- /dev/null +++ b/v_windows/v/old/vlib/bitfield/bitfield.v @@ -0,0 +1,569 @@ +module bitfield + +/* +bitfield is a module for +manipulating arrays of bits, i.e. series of zeroes and ones spread across an +array of storage units (unsigned 32-bit integers). + +BitField structure +------------------ + +Bit arrays are stored in data structures called 'BitField'. The structure is +'opaque', i.e. its internals are not available to the end user. This module +provides API (functions and methods) for accessing and modifying bit arrays. +*/ +pub struct BitField { +mut: + size int + // field *u32 + field []u32 +} + +// helper functions +const ( + slot_size = 32 +) + +// from_bytes converts a byte array into a bitfield. +// [0x0F, 0x01] => 0000 1111 0000 0001 +pub fn from_bytes(input []byte) BitField { + mut output := new(input.len * 8) + for i, b in input { + mut ob := byte(0) + if b & 0b10000000 > 0 { + ob |= 0b00000001 + } + if b & 0b01000000 > 0 { + ob |= 0b00000010 + } + if b & 0b00100000 > 0 { + ob |= 0b00000100 + } + if b & 0b00010000 > 0 { + ob |= 0b00001000 + } + if b & 0b00001000 > 0 { + ob |= 0b00010000 + } + if b & 0b00000100 > 0 { + ob |= 0b00100000 + } + if b & 0b00000010 > 0 { + ob |= 0b01000000 + } + if b & 0b00000001 > 0 { + ob |= 0b10000000 + } + output.field[i / 4] |= u32(ob) << ((i % 4) * 8) + } + return output +} + +// from_bytes_lowest_bits_first converts a byte array into a bitfield +// [0x0F, 0x01] => 1111 0000 1000 0000 +pub fn from_bytes_lowest_bits_first(input []byte) BitField { + mut output := new(input.len * 8) + for i, b in input { + output.field[i / 4] |= u32(b) << ((i % 4) * 8) + } + return output +} + +// from_str converts a string of characters ('0' and '1') to a bit +// array. Any character different from '0' is treated as '1'. +pub fn from_str(input string) BitField { + mut output := new(input.len) + for i in 0 .. input.len { + if input[i] != `0` { + output.set_bit(i) + } + } + return output +} + +// str converts the bit array to a string of characters ('0' and '1') and +// return the string +pub fn (input BitField) str() string { + mut output := '' + for i in 0 .. input.size { + if input.get_bit(i) == 1 { + output = output + '1' + } else { + output = output + '0' + } + } + return output +} + +// new creates an empty bit array of capable of storing 'size' bits. +pub fn new(size int) BitField { + output := BitField{ + size: size + // field: *u32(calloc(zbitnslots(size) * slot_size / 8)) + field: []u32{len: zbitnslots(size)} + } + return output +} + +// frees the memory allocated for the bitfield instance +[unsafe] +pub fn (instance &BitField) free() { + unsafe { + instance.field.free() + } +} + +// get_bit returns the value (0 or 1) of bit number 'bit_nr' (count from 0). +pub fn (instance BitField) get_bit(bitnr int) int { + if bitnr >= instance.size { + return 0 + } + return int((instance.field[bitslot(bitnr)] >> (bitnr % bitfield.slot_size)) & u32(1)) +} + +// set_bit sets bit number 'bit_nr' to 1 (count from 0). +pub fn (mut instance BitField) set_bit(bitnr int) { + if bitnr >= instance.size { + return + } + instance.field[bitslot(bitnr)] |= bitmask(bitnr) +} + +// clear_bit clears (sets to zero) bit number 'bit_nr' (count from 0). +pub fn (mut instance BitField) clear_bit(bitnr int) { + if bitnr >= instance.size { + return + } + instance.field[bitslot(bitnr)] &= ~bitmask(bitnr) +} + +// extract returns the value converted from a slice of bit numbers +// from 'start' by the length of 'len'. +// 0101 (1, 2) => 0b10 +pub fn (instance BitField) extract(start int, len int) u64 { + // panic? + if start < 0 { + return 0 + } + mut output := u64(0) + for i in 0 .. len { + output |= u64(instance.get_bit(start + len - i - 1)) << i + } + return output +} + +// insert sets bit numbers from 'start' to 'len' length with +// the value converted from the number 'value'. +// 0000 (1, 2, 0b10) => 0100 +pub fn (mut instance BitField) insert(start int, len int, _value T) { + // panic? + if start < 0 { + return + } + mut value := _value + for i in 0 .. len { + pos := start + len - i - 1 + if value & 1 == 1 { + instance.set_bit(pos) + } else { + instance.clear_bit(pos) + } + value >>= 1 + } +} + +// extract returns the value converted from a slice of bit numbers +// from 'start' by the length of 'len'. +// 0101 (1, 2) => 0b01 +pub fn (instance BitField) extract_lowest_bits_first(start int, len int) u64 { + // panic? + if start < 0 { + return 0 + } + mut output := u64(0) + for i in 0 .. len { + output |= u64(instance.get_bit(start + i)) << i + } + return output +} + +// insert sets bit numbers from 'start' to 'len' length with +// the value converted from the number 'value'. +// 0000 (1, 2, 0b10) => 0010 +pub fn (mut instance BitField) insert_lowest_bits_first(start int, len int, _value T) { + // panic? + if start < 0 { + return + } + mut value := _value + for pos in start .. start + len { + if value & 1 == 1 { + instance.set_bit(pos) + } else { + instance.clear_bit(pos) + } + value >>= 1 + } +} + +// set_all sets all bits in the array to 1. +pub fn (mut instance BitField) set_all() { + for i in 0 .. zbitnslots(instance.size) { + instance.field[i] = u32(-1) + } + instance.clear_tail() +} + +// clear_all clears (sets to zero) all bits in the array. +pub fn (mut instance BitField) clear_all() { + for i in 0 .. zbitnslots(instance.size) { + instance.field[i] = u32(0) + } +} + +// toggle_bit changes the value (from 0 to 1 or from 1 to 0) of bit +// number 'bit_nr'. +pub fn (mut instance BitField) toggle_bit(bitnr int) { + if bitnr >= instance.size { + return + } + instance.field[bitslot(bitnr)] ^= bitmask(bitnr) +} + +// bf_and performs logical AND operation on every pair of bits from 'input1' and +// 'input2' and returns the result as a new array. If inputs differ in size, +// the tail of the longer one is ignored. +pub fn bf_and(input1 BitField, input2 BitField) BitField { + size := min(input1.size, input2.size) + bitnslots := zbitnslots(size) + mut output := new(size) + for i in 0 .. bitnslots { + output.field[i] = input1.field[i] & input2.field[i] + } + output.clear_tail() + return output +} + +// bf_not toggles all bits in a bit array and returns the result as a new array. +pub fn bf_not(input BitField) BitField { + size := input.size + bitnslots := zbitnslots(size) + mut output := new(size) + for i in 0 .. bitnslots { + output.field[i] = ~input.field[i] + } + output.clear_tail() + return output +} + +// bf_or performs logical OR operation on every pair of bits from 'input1' and +// 'input2' and returns the result as a new array. If inputs differ in size, +// the tail of the longer one is ignored. +pub fn bf_or(input1 BitField, input2 BitField) BitField { + size := min(input1.size, input2.size) + bitnslots := zbitnslots(size) + mut output := new(size) + for i in 0 .. bitnslots { + output.field[i] = input1.field[i] | input2.field[i] + } + output.clear_tail() + return output +} + +// bf_xor perform logical XOR operation on every pair of bits from 'input1' and +// 'input2' and returns the result as a new array. If inputs differ in size, +// the tail of the longer one is ignored. +pub fn bf_xor(input1 BitField, input2 BitField) BitField { + size := min(input1.size, input2.size) + bitnslots := zbitnslots(size) + mut output := new(size) + for i in 0 .. bitnslots { + output.field[i] = input1.field[i] ^ input2.field[i] + } + output.clear_tail() + return output +} + +// join concatenates two bit arrays and return the result as a new array. +pub fn join(input1 BitField, input2 BitField) BitField { + output_size := input1.size + input2.size + mut output := new(output_size) + // copy the first input to output as is + for i in 0 .. zbitnslots(input1.size) { + output.field[i] = input1.field[i] + } + // find offset bit and offset slot + offset_bit := input1.size % bitfield.slot_size + offset_slot := input1.size / bitfield.slot_size + for i in 0 .. zbitnslots(input2.size) { + output.field[i + offset_slot] |= u32(input2.field[i] << u32(offset_bit)) + } + /* + * If offset_bit is not zero, additional operations are needed. + * Number of iterations depends on the nr of slots in output. Two + * options: + * (a) nr of slots in output is the sum of inputs' slots. In this + * case, the nr of bits in the last slot of output is less than the + * nr of bits in the second input (i.e. ), OR + * (b) nr of slots of output is the sum of inputs' slots less one + * (i.e. less iterations needed). In this case, the nr of bits in + * the last slot of output is greater than the nr of bits in the second + * input. + * If offset_bit is zero, no additional copies needed. + */ + if (output_size - 1) % bitfield.slot_size < (input2.size - 1) % bitfield.slot_size { + for i in 0 .. zbitnslots(input2.size) { + output.field[i + offset_slot + 1] |= u32(input2.field[i] >> u32(bitfield.slot_size - offset_bit)) + } + } else if (output_size - 1) % bitfield.slot_size > (input2.size - 1) % bitfield.slot_size { + for i in 0 .. zbitnslots(input2.size) - 1 { + output.field[i + offset_slot + 1] |= u32(input2.field[i] >> u32(bitfield.slot_size - offset_bit)) + } + } + return output +} + +// get_size returns the number of bits the array can hold. +pub fn (instance BitField) get_size() int { + return instance.size +} + +// clone creates a copy of a bit array. +pub fn (instance BitField) clone() BitField { + bitnslots := zbitnslots(instance.size) + mut output := new(instance.size) + for i in 0 .. bitnslots { + output.field[i] = instance.field[i] + } + return output +} + +// cmp compares two bit arrays bit by bit and returns 'true' if they are +// identical by length and contents and 'false' otherwise. +[deprecated: 'use a == b instead'] +[deprecated_after: '2021-06-29'] +pub fn (instance BitField) cmp(input BitField) bool { + if instance.size != input.size { + return false + } + for i in 0 .. zbitnslots(instance.size) { + if instance.field[i] != input.field[i] { + return false + } + } + return true +} + +pub fn (a BitField) == (b BitField) bool { + if a.size != b.size { + return false + } + for i in 0 .. zbitnslots(a.size) { + if a.field[i] != b.field[i] { + return false + } + } + return true +} + +// pop_count returns the number of set bits (ones) in the array. +pub fn (instance BitField) pop_count() int { + size := instance.size + bitnslots := zbitnslots(size) + tail := size % bitfield.slot_size + mut count := 0 + for i in 0 .. bitnslots - 1 { + for j in 0 .. bitfield.slot_size { + if u32(instance.field[i] >> u32(j)) & u32(1) == u32(1) { + count++ + } + } + } + for j in 0 .. tail { + if u32(instance.field[bitnslots - 1] >> u32(j)) & u32(1) == u32(1) { + count++ + } + } + return count +} + +// hamming computes the Hamming distance between two bit arrays. +pub fn hamming(input1 BitField, input2 BitField) int { + input_xored := bf_xor(input1, input2) + return input_xored.pop_count() +} + +// pos checks if the array contains a sub-array 'needle' and returns its +// position if it does, -1 if it does not, and -2 on error. +pub fn (haystack BitField) pos(needle BitField) int { + heystack_size := haystack.size + needle_size := needle.size + diff := heystack_size - needle_size + // needle longer than haystack; return error code -2 + if diff < 0 { + return -2 + } + for i := 0; i <= diff; i++ { + needle_candidate := haystack.slice(i, needle_size + i) + if needle_candidate == needle { + // needle matches a sub-array of haystack; return starting position of the sub-array + return i + } + } + // nothing matched; return -1 + return -1 +} + +// slice returns a sub-array of bits between 'start_bit_nr' (included) and +// 'end_bit_nr' (excluded). +pub fn (input BitField) slice(_start int, _end int) BitField { + // boundary checks + mut start := _start + mut end := _end + if end > input.size { + end = input.size // or panic? + } + if start > end { + start = end // or panic? + } + mut output := new(end - start) + start_offset := start % bitfield.slot_size + end_offset := (end - 1) % bitfield.slot_size + start_slot := start / bitfield.slot_size + end_slot := (end - 1) / bitfield.slot_size + output_slots := zbitnslots(end - start) + if output_slots > 1 { + if start_offset != 0 { + for i in 0 .. output_slots - 1 { + output.field[i] = u32(input.field[start_slot + i] >> u32(start_offset)) + output.field[i] = output.field[i] | u32(input.field[start_slot + i + 1] << u32(bitfield.slot_size - start_offset)) + } + } else { + for i in 0 .. output_slots - 1 { + output.field[i] = u32(input.field[start_slot + i]) + } + } + } + if start_offset > end_offset { + output.field[(end - start - 1) / bitfield.slot_size] = u32(input.field[end_slot - 1] >> u32(start_offset)) + mut mask := u32((1 << (end_offset + 1)) - 1) + mask = input.field[end_slot] & mask + mask = u32(mask << u32(bitfield.slot_size - start_offset)) + output.field[(end - start - 1) / bitfield.slot_size] |= mask + } else if start_offset == 0 { + mut mask := u32(0) + if end_offset == bitfield.slot_size - 1 { + mask = u32(-1) + } else { + mask = u32(u32(1) << u32(end_offset + 1)) + mask = mask - u32(1) + } + output.field[(end - start - 1) / bitfield.slot_size] = (input.field[end_slot] & mask) + } else { + mut mask := u32(((1 << (end_offset - start_offset + 1)) - 1) << start_offset) + mask = input.field[end_slot] & mask + mask = u32(mask >> u32(start_offset)) + output.field[(end - start - 1) / bitfield.slot_size] |= mask + } + return output +} + +// reverse reverses the order of bits in the array (swap the first with the +// last, the second with the last but one and so on). +pub fn (instance BitField) reverse() BitField { + size := instance.size + bitnslots := zbitnslots(size) + mut output := new(size) + for i := 0; i < (bitnslots - 1); i++ { + for j in 0 .. bitfield.slot_size { + if u32(instance.field[i] >> u32(j)) & u32(1) == u32(1) { + output.set_bit(size - i * bitfield.slot_size - j - 1) + } + } + } + bits_in_last_input_slot := (size - 1) % bitfield.slot_size + 1 + for j in 0 .. bits_in_last_input_slot { + if u32(instance.field[bitnslots - 1] >> u32(j)) & u32(1) == u32(1) { + output.set_bit(bits_in_last_input_slot - j - 1) + } + } + return output +} + +// resize changes the size of the bit array to 'new_size'. +pub fn (mut instance BitField) resize(new_size int) { + new_bitnslots := zbitnslots(new_size) + old_size := instance.size + old_bitnslots := zbitnslots(old_size) + mut field := []u32{len: new_bitnslots} + for i := 0; i < old_bitnslots && i < new_bitnslots; i++ { + field[i] = instance.field[i] + } + instance.field = field.clone() + instance.size = new_size + if new_size < old_size && new_size % bitfield.slot_size != 0 { + instance.clear_tail() + } +} + +// rotate circular-shifts the bits by 'offset' positions (move +// 'offset' bit to 0, 'offset+1' bit to 1, and so on). +pub fn (instance BitField) rotate(offset int) BitField { + /* + * + * This function "cuts" the bitfield into two and swaps them. + * If the offset is positive, the cutting point is counted from the + * beginning of the bit array, otherwise from the end. + * + */ + size := instance.size + // removing extra rotations + mut offset_internal := offset % size + if offset_internal == 0 { + // nothing to shift + return instance + } + if offset_internal < 0 { + offset_internal = offset_internal + size + } + first_chunk := instance.slice(0, offset_internal) + second_chunk := instance.slice(offset_internal, size) + output := join(second_chunk, first_chunk) + return output +} + +// Internal functions +// clear_tail clears the extra bits that are not part of the bitfield, but yet are allocated +fn (mut instance BitField) clear_tail() { + tail := instance.size % bitfield.slot_size + if tail != 0 { + // create a mask for the tail + mask := u32((1 << tail) - 1) + // clear the extra bits + instance.field[zbitnslots(instance.size) - 1] = instance.field[zbitnslots(instance.size) - 1] & mask + } +} + +// bitmask is the bitmask needed to access a particular bit at offset bitnr +fn bitmask(bitnr int) u32 { + return u32(u32(1) << u32(bitnr % bitfield.slot_size)) +} + +// bitslot is the slot index (i.e. the integer) where a particular bit is located +fn bitslot(size int) int { + return size / bitfield.slot_size +} + +// min returns the minimum of 2 integers; it is here to avoid importing math just for that +fn min(input1 int, input2 int) int { + if input1 < input2 { + return input1 + } else { + return input2 + } +} + +// zbitnslots returns the minimum number of whole integers, needed to represent a bitfield of size length +fn zbitnslots(length int) int { + return (length - 1) / bitfield.slot_size + 1 +} diff --git a/v_windows/v/old/vlib/bitfield/bitfield_test.v b/v_windows/v/old/vlib/bitfield/bitfield_test.v new file mode 100644 index 0000000..ae61d38 --- /dev/null +++ b/v_windows/v/old/vlib/bitfield/bitfield_test.v @@ -0,0 +1,333 @@ +import bitfield +import rand + +fn test_bf_new_size() { + instance := bitfield.new(75) + assert instance.get_size() == 75 +} + +fn test_bf_set_clear_toggle_get() { + mut instance := bitfield.new(75) + instance.set_bit(47) + assert instance.get_bit(47) == 1 + instance.clear_bit(47) + assert instance.get_bit(47) == 0 + instance.toggle_bit(47) + assert instance.get_bit(47) == 1 +} + +fn test_bf_insert_extract() { + mut instance := bitfield.new(11) + instance.set_all() + instance.insert(2, 9, 3) + assert instance.extract(2, 1) == 0 + assert instance.extract(2, 8) == 1 + assert instance.extract(10, 1) == 1 + instance.set_all() + instance.insert_lowest_bits_first(2, 9, 3) + assert instance.extract_lowest_bits_first(2, 1) == 1 + assert instance.extract_lowest_bits_first(2, 8) == 3 + assert instance.extract_lowest_bits_first(10, 1) == 0 +} + +fn test_bf_and_not_or_xor() { + len := 80 + mut input1 := bitfield.new(len) + mut input2 := bitfield.new(len) + mut i := 0 + for i < len { + if rand.intn(2) == 1 { + input1.set_bit(i) + } + if rand.intn(2) == 1 { + input2.set_bit(i) + } + i++ + } + output1 := bitfield.bf_xor(input1, input2) + bf_and := bitfield.bf_and(input1, input2) + bf_or := bitfield.bf_or(input1, input2) + bf_not := bitfield.bf_not(bf_and) + output2 := bitfield.bf_and(bf_or, bf_not) + mut result := 1 + for i < len { + if output1.get_bit(i) != output2.get_bit(i) { + result = 0 + } + } + assert result == 1 +} + +fn test_clone_cmp() { + len := 80 + mut input := bitfield.new(len) + for i in 0 .. len { + if rand.intn(2) == 1 { + input.set_bit(i) + } + } + output := input.clone() + assert output.get_size() == len + assert input == output +} + +fn test_slice_join() { + len := 80 + mut input := bitfield.new(len) + for i in 0 .. len { + if rand.intn(2) == 1 { + input.set_bit(i) + } + } + mut result := 1 + for point := 1; point < (len - 1); point++ { + // divide a bitfield into two subfields + chunk1 := input.slice(0, point) + chunk2 := input.slice(point, input.get_size()) + // concatenate them back into one and compare to the original + output := bitfield.join(chunk1, chunk2) + if input != output { + result = 0 + } + } + assert result == 1 +} + +fn test_pop_count() { + len := 80 + mut count0 := 0 + mut input := bitfield.new(len) + for i in 0 .. len { + if rand.intn(2) == 1 { + input.set_bit(i) + count0++ + } + } + count1 := input.pop_count() + assert count0 == count1 +} + +fn test_hamming() { + len := 80 + mut count := 0 + mut input1 := bitfield.new(len) + mut input2 := bitfield.new(len) + for i in 0 .. len { + match rand.intn(4) { + 0, 1 { + input1.set_bit(i) + count++ + } + 2 { + input2.set_bit(i) + count++ + } + 3 { + input1.set_bit(i) + input2.set_bit(i) + } + else {} + } + } + assert count == bitfield.hamming(input1, input2) +} + +fn test_bf_from_bytes() { + input := [byte(0x01), 0xF0, 0x0F, 0xF0, 0xFF] + output := bitfield.from_bytes(input).str() + assert output == '00000001' + '11110000' + '00001111' + '11110000' + '11111111' + newoutput := bitfield.from_str(output).str() + assert newoutput == output +} + +fn test_bf_from_bytes_lowest_bits_first() { + input := [byte(0x01), 0xF0] + output := bitfield.from_bytes_lowest_bits_first(input).str() + assert output == '10000000' + '00001111' + newoutput := bitfield.from_str(output).str() + assert newoutput == output +} + +fn test_bf_from_str() { + len := 80 + mut input := '' + for _ in 0 .. len { + if rand.intn(2) == 1 { + input = input + '1' + } else { + input = input + '0' + } + } + output := bitfield.from_str(input) + mut result := 1 + for i in 0 .. len { + if input[i] != output.get_bit(i) + 48 { + result = 0 + } + } + assert result == 1 +} + +fn test_bf_bf2str() { + len := 80 + mut input := bitfield.new(len) + for i in 0 .. len { + if rand.intn(2) == 1 { + input.set_bit(i) + } + } + mut check := '' + for i in 0 .. len { + if input.get_bit(i) == 1 { + check = check + '1' + } else { + check = check + '0' + } + } + output := input.str() + mut result := 1 + for i in 0 .. len { + if check[i] != output[i] { + result = 0 + } + } + assert result == 1 +} + +fn test_bf_set_all() { + len := 80 + mut input := bitfield.new(len) + input.set_all() + mut result := 1 + for i in 0 .. len { + if input.get_bit(i) != 1 { + result = 0 + } + } + assert result == 1 +} + +fn test_bf_clear_all() { + len := 80 + mut input := bitfield.new(len) + for i in 0 .. len { + if rand.intn(2) == 1 { + input.set_bit(i) + } + } + input.clear_all() + mut result := 1 + for i in 0 .. len { + if input.get_bit(i) != 0 { + result = 0 + } + } + assert result == 1 +} + +fn test_bf_reverse() { + len := 80 + mut input := bitfield.new(len) + for i in 0 .. len { + if rand.intn(2) == 1 { + input.set_bit(i) + } + } + check := input.clone() + output := input.reverse() + mut result := 1 + for i in 0 .. len { + if output.get_bit(i) != check.get_bit(len - i - 1) { + result = 0 + } + } + assert result == 1 +} + +fn test_bf_resize() { + len := 80 + mut input := bitfield.new(rand.intn(len) + 1) + for _ in 0 .. 100 { + input.resize(rand.intn(len) + 1) + input.set_bit(input.get_size() - 1) + } + assert input.get_bit(input.get_size() - 1) == 1 +} + +fn test_bf_pos() { + /* + * + * set haystack size to 80 + * test different sizes of needle, from 1 to 80 + * test different positions of needle, from 0 to where it fits + * all haystacks here contain exactly one instanse of needle, + * so search should return non-negative-values + * + */ + len := 80 + mut result := 1 + for i := 1; i < len; i++ { // needle size + for j in 0 .. len - i { // needle position in the haystack + // create the needle + mut needle := bitfield.new(i) + // fill the needle with random values + for k in 0 .. i { + if rand.intn(2) == 1 { + needle.set_bit(k) + } + } + // make sure the needle contains at least one set bit, selected randomly + r := rand.intn(i) + needle.set_bit(r) + // create the haystack, make sure it contains the needle + mut haystack := needle.clone() + // if there is space between the start of the haystack and the sought needle, fill it with zeroes + if j > 0 { + start := bitfield.new(j) + tmp := bitfield.join(start, haystack) + haystack = tmp + } + // if there is space between the sought needle and the end of haystack, fill it with zeroes + if j + i < len { + end := bitfield.new(len - j - i) + tmp2 := bitfield.join(haystack, end) + haystack = tmp2 + } + // now let's test + // the result should be equal to j + if haystack.pos(needle) != j { + result = 0 + } + } + } + assert result == 1 +} + +fn test_bf_rotate() { + mut result := 1 + len := 80 + for i := 1; i < 80 && result == 1; i++ { + mut chunk1 := bitfield.new(i) + chunk2 := bitfield.new(len - i) + chunk1.set_all() + input := bitfield.join(chunk1, chunk2) + output := input.rotate(i) + if output.get_bit(len - i - 1) != 0 || output.get_bit(len - i) != 1 { + result = 0 + } + } + assert result == 1 +} + +fn test_bf_printing() { + len := 80 + mut input := bitfield.new(len) + for i in 0 .. len { + if rand.intn(2) == 0 { + input.set_bit(i) + } + } + // the following should convert the bitfield input into a string automatically + println(input) + assert true +} diff --git a/v_windows/v/old/vlib/builtin/array.v b/v_windows/v/old/vlib/builtin/array.v new file mode 100644 index 0000000..99055f3 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/array.v @@ -0,0 +1,664 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module builtin + +import strings + +// array is a struct used for denoting array types in V +pub struct array { +pub: + element_size int // size in bytes of one element in the array. +pub mut: + data voidptr + offset int // in bytes (should be `size_t`) + len int // length of the array. + cap int // capacity of the array. +} + +// array.data uses a void pointer, which allows implementing arrays without generics and without generating +// extra code for every type. +// Internal function, used by V (`nums := []int`) +fn __new_array(mylen int, cap int, elm_size int) array { + cap_ := if cap < mylen { mylen } else { cap } + arr := array{ + element_size: elm_size + data: vcalloc(cap_ * elm_size) + len: mylen + cap: cap_ + } + return arr +} + +fn __new_array_with_default(mylen int, cap int, elm_size int, val voidptr) array { + cap_ := if cap < mylen { mylen } else { cap } + mut arr := array{ + element_size: elm_size + data: vcalloc(cap_ * elm_size) + len: mylen + cap: cap_ + } + if val != 0 { + for i in 0 .. arr.len { + unsafe { arr.set_unsafe(i, val) } + } + } + return arr +} + +fn __new_array_with_array_default(mylen int, cap int, elm_size int, val array) array { + cap_ := if cap < mylen { mylen } else { cap } + mut arr := array{ + element_size: elm_size + data: vcalloc(cap_ * elm_size) + len: mylen + cap: cap_ + } + for i in 0 .. arr.len { + val_clone := unsafe { val.clone_to_depth(1) } + unsafe { arr.set_unsafe(i, &val_clone) } + } + return arr +} + +// Private function, used by V (`nums := [1, 2, 3]`) +fn new_array_from_c_array(len int, cap int, elm_size int, c_array voidptr) array { + cap_ := if cap < len { len } else { cap } + arr := array{ + element_size: elm_size + data: vcalloc(cap_ * elm_size) + len: len + cap: cap_ + } + // TODO Write all memory functions (like memcpy) in V + unsafe { C.memcpy(arr.data, c_array, len * elm_size) } + return arr +} + +// Private function, used by V (`nums := [1, 2, 3] !`) +fn new_array_from_c_array_no_alloc(len int, cap int, elm_size int, c_array voidptr) array { + arr := array{ + element_size: elm_size + data: c_array + len: len + cap: cap + } + return arr +} + +// Private function. Doubles array capacity if needed. +fn (mut a array) ensure_cap(required int) { + if required <= a.cap { + return + } + mut cap := if a.cap > 0 { a.cap } else { 2 } + for required > cap { + cap *= 2 + } + new_size := cap * a.element_size + new_data := vcalloc(new_size) + if a.data != voidptr(0) { + unsafe { C.memcpy(new_data, a.data, a.len * a.element_size) } + // TODO: the old data may be leaked when no GC is used (ref-counting?) + } + a.data = new_data + a.offset = 0 + a.cap = cap +} + +// repeat returns a new array with the given array elements repeated given times. +// `cgen` will replace this with an apropriate call to `repeat_to_depth()` + +// This is a dummy placeholder that will be overridden by `cgen` with an appropriate +// call to `repeat_to_depth()`. However the `checker` needs it here. +pub fn (a array) repeat(count int) array { + return unsafe { a.repeat_to_depth(count, 0) } +} + +// version of `repeat()` that handles multi dimensional arrays +// `unsafe` to call directly because `depth` is not checked +[unsafe] +pub fn (a array) repeat_to_depth(count int, depth int) array { + if count < 0 { + panic('array.repeat: count is negative: $count') + } + mut size := count * a.len * a.element_size + if size == 0 { + size = a.element_size + } + arr := array{ + element_size: a.element_size + data: vcalloc(size) + len: count * a.len + cap: count * a.len + } + if a.len > 0 { + for i in 0 .. count { + if depth > 0 { + ary_clone := unsafe { a.clone_to_depth(depth) } + unsafe { C.memcpy(arr.get_unsafe(i * a.len), &byte(ary_clone.data), a.len * a.element_size) } + } else { + unsafe { C.memcpy(arr.get_unsafe(i * a.len), &byte(a.data), a.len * a.element_size) } + } + } + } + return arr +} + +// sort_with_compare sorts array in-place using given `compare` function as comparator. +pub fn (mut a array) sort_with_compare(compare voidptr) { + $if freestanding { + panic('sort does not work with -freestanding') + } $else { + C.qsort(mut a.data, a.len, a.element_size, compare) + } +} + +// insert inserts a value in the array at index `i` +pub fn (mut a array) insert(i int, val voidptr) { + $if !no_bounds_checking ? { + if i < 0 || i > a.len { + panic('array.insert: index out of range (i == $i, a.len == $a.len)') + } + } + a.ensure_cap(a.len + 1) + unsafe { + C.memmove(a.get_unsafe(i + 1), a.get_unsafe(i), (a.len - i) * a.element_size) + a.set_unsafe(i, val) + } + a.len++ +} + +// insert_many inserts many values into the array from index `i`. +[unsafe] +pub fn (mut a array) insert_many(i int, val voidptr, size int) { + $if !no_bounds_checking ? { + if i < 0 || i > a.len { + panic('array.insert_many: index out of range (i == $i, a.len == $a.len)') + } + } + a.ensure_cap(a.len + size) + elem_size := a.element_size + unsafe { + iptr := a.get_unsafe(i) + C.memmove(a.get_unsafe(i + size), iptr, (a.len - i) * elem_size) + C.memcpy(iptr, val, size * elem_size) + } + a.len += size +} + +// prepend prepends one value to the array. +pub fn (mut a array) prepend(val voidptr) { + a.insert(0, val) +} + +// prepend_many prepends another array to this array. +[unsafe] +pub fn (mut a array) prepend_many(val voidptr, size int) { + unsafe { a.insert_many(0, val, size) } +} + +// delete deletes array element at index `i`. +pub fn (mut a array) delete(i int) { + a.delete_many(i, 1) +} + +// delete_many deletes `size` elements beginning with index `i` +pub fn (mut a array) delete_many(i int, size int) { + $if !no_bounds_checking ? { + if i < 0 || i + size > a.len { + endidx := if size > 1 { '..${i + size}' } else { '' } + panic('array.delete: index out of range (i == $i$endidx, a.len == $a.len)') + } + } + // NB: if a is [12,34], a.len = 2, a.delete(0) + // should move (2-0-1) elements = 1 element (the 34) forward + old_data := a.data + new_size := a.len - size + new_cap := if new_size == 0 { 1 } else { new_size } + a.data = vcalloc(new_cap * a.element_size) + unsafe { C.memcpy(a.data, old_data, i * a.element_size) } + unsafe { + C.memcpy(&byte(a.data) + i * a.element_size, &byte(old_data) + (i + size) * a.element_size, + (a.len - i - size) * a.element_size) + } + a.len = new_size + a.cap = new_cap +} + +// clear clears the array without deallocating the allocated data. +pub fn (mut a array) clear() { + a.len = 0 +} + +// trim trims the array length to "index" without modifying the allocated data. If "index" is greater +// than len nothing will be changed. +pub fn (mut a array) trim(index int) { + if index < a.len { + a.len = index + } +} + +// we manually inline this for single operations for performance without -prod +[inline; unsafe] +fn (a array) get_unsafe(i int) voidptr { + unsafe { + return &byte(a.data) + i * a.element_size + } +} + +// Private function. Used to implement array[] operator. +fn (a array) get(i int) voidptr { + $if !no_bounds_checking ? { + if i < 0 || i >= a.len { + panic('array.get: index out of range (i == $i, a.len == $a.len)') + } + } + unsafe { + return &byte(a.data) + i * a.element_size + } +} + +// Private function. Used to implement x = a[i] or { ... } +fn (a array) get_with_check(i int) voidptr { + if i < 0 || i >= a.len { + return 0 + } + unsafe { + return &byte(a.data) + i * a.element_size + } +} + +// first returns the first element of the array. +pub fn (a array) first() voidptr { + $if !no_bounds_checking ? { + if a.len == 0 { + panic('array.first: array is empty') + } + } + return a.data +} + +// last returns the last element of the array. +pub fn (a array) last() voidptr { + $if !no_bounds_checking ? { + if a.len == 0 { + panic('array.last: array is empty') + } + } + unsafe { + return &byte(a.data) + (a.len - 1) * a.element_size + } +} + +// pop returns the last element of the array, and removes it. +pub fn (mut a array) pop() voidptr { + // in a sense, this is the opposite of `a << x` + $if !no_bounds_checking ? { + if a.len == 0 { + panic('array.pop: array is empty') + } + } + new_len := a.len - 1 + last_elem := unsafe { &byte(a.data) + new_len * a.element_size } + a.len = new_len + // NB: a.cap is not changed here *on purpose*, so that + // further << ops on that array will be more efficient. + return unsafe { memdup(last_elem, a.element_size) } +} + +// delete_last efficiently deletes the last element of the array. +pub fn (mut a array) delete_last() { + // copy pasting code for performance + $if !no_bounds_checking ? { + if a.len == 0 { + panic('array.pop: array is empty') + } + } + a.len-- +} + +// slice returns an array using the same buffer as original array +// but starting from the `start` element and ending with the element before +// the `end` element of the original array with the length and capacity +// set to the number of the elements in the slice. +fn (a array) slice(start int, _end int) array { + mut end := _end + $if !no_bounds_checking ? { + if start > end { + panic('array.slice: invalid slice index ($start > $end)') + } + if end > a.len { + panic('array.slice: slice bounds out of range ($end >= $a.len)') + } + if start < 0 { + panic('array.slice: slice bounds out of range ($start < 0)') + } + } + offset := start * a.element_size + data := unsafe { &byte(a.data) + offset } + l := end - start + res := array{ + element_size: a.element_size + data: data + offset: a.offset + offset + len: l + cap: l + } + return res +} + +// used internally for [2..4] +fn (a array) slice2(start int, _end int, end_max bool) array { + end := if end_max { a.len } else { _end } + return a.slice(start, end) +} + +// `clone_static_to_depth()` returns an independent copy of a given array. +// Unlike `clone_to_depth()` it has a value receiver and is used internally +// for slice-clone expressions like `a[2..4].clone()` and in -autofree generated code. +fn (a array) clone_static_to_depth(depth int) array { + return unsafe { a.clone_to_depth(depth) } +} + +// clone returns an independent copy of a given array. +// this will be overwritten by `cgen` with an apropriate call to `.clone_to_depth()` +// However the `checker` needs it here. +pub fn (a &array) clone() array { + return unsafe { a.clone_to_depth(0) } +} + +// recursively clone given array - `unsafe` when called directly because depth is not checked +[unsafe] +pub fn (a &array) clone_to_depth(depth int) array { + mut size := a.cap * a.element_size + if size == 0 { + size++ + } + mut arr := array{ + element_size: a.element_size + data: vcalloc(size) + len: a.len + cap: a.cap + } + // Recursively clone-generated elements if array element is array type + if depth > 0 && a.element_size == sizeof(array) && a.len >= 0 && a.cap >= a.len { + for i in 0 .. a.len { + ar := array{} + unsafe { C.memcpy(&ar, a.get_unsafe(i), int(sizeof(array))) } + ar_clone := unsafe { ar.clone_to_depth(depth - 1) } + unsafe { arr.set_unsafe(i, &ar_clone) } + } + return arr + } else { + if !isnil(a.data) { + unsafe { C.memcpy(&byte(arr.data), a.data, a.cap * a.element_size) } + } + return arr + } +} + +// we manually inline this for single operations for performance without -prod +[inline; unsafe] +fn (mut a array) set_unsafe(i int, val voidptr) { + unsafe { C.memcpy(&byte(a.data) + a.element_size * i, val, a.element_size) } +} + +// Private function. Used to implement assigment to the array element. +fn (mut a array) set(i int, val voidptr) { + $if !no_bounds_checking ? { + if i < 0 || i >= a.len { + panic('array.set: index out of range (i == $i, a.len == $a.len)') + } + } + unsafe { C.memcpy(&byte(a.data) + a.element_size * i, val, a.element_size) } +} + +fn (mut a array) push(val voidptr) { + a.ensure_cap(a.len + 1) + unsafe { C.memmove(&byte(a.data) + a.element_size * a.len, val, a.element_size) } + a.len++ +} + +// push_many implements the functionality for pushing another array. +// `val` is array.data and user facing usage is `a << [1,2,3]` +[unsafe] +pub fn (mut a3 array) push_many(val voidptr, size int) { + if a3.data == val && !isnil(a3.data) { + // handle `arr << arr` + copy := a3.clone() + a3.ensure_cap(a3.len + size) + unsafe { + // C.memcpy(a.data, copy.data, copy.element_size * copy.len) + C.memcpy(a3.get_unsafe(a3.len), copy.data, a3.element_size * size) + } + } else { + a3.ensure_cap(a3.len + size) + if !isnil(a3.data) && !isnil(val) { + unsafe { C.memcpy(a3.get_unsafe(a3.len), val, a3.element_size * size) } + } + } + a3.len += size +} + +// reverse_in_place reverses existing array data, modifying original array. +pub fn (mut a array) reverse_in_place() { + if a.len < 2 { + return + } + unsafe { + mut tmp_value := malloc(a.element_size) + for i in 0 .. a.len / 2 { + C.memcpy(tmp_value, &byte(a.data) + i * a.element_size, a.element_size) + C.memcpy(&byte(a.data) + i * a.element_size, &byte(a.data) + + (a.len - 1 - i) * a.element_size, a.element_size) + C.memcpy(&byte(a.data) + (a.len - 1 - i) * a.element_size, tmp_value, a.element_size) + } + free(tmp_value) + } +} + +// reverse returns a new array with the elements of the original array in reverse order. +pub fn (a array) reverse() array { + if a.len < 2 { + return a + } + mut arr := array{ + element_size: a.element_size + data: vcalloc(a.cap * a.element_size) + len: a.len + cap: a.cap + } + for i in 0 .. a.len { + unsafe { arr.set_unsafe(i, a.get_unsafe(a.len - 1 - i)) } + } + return arr +} + +// pub fn (a []int) free() { +// free frees all memory occupied by the array. +[unsafe] +pub fn (a &array) free() { + $if prealloc { + return + } + // if a.is_slice { + // return + // } + unsafe { free(&byte(a.data) - a.offset) } +} + +[unsafe] +pub fn (mut a []string) free() { + $if prealloc { + return + } + for s in a { + unsafe { s.free() } + } + unsafe { free(a.data) } +} + +// str returns a string representation of the array of strings +// => '["a", "b", "c"]'. +[manualfree] +pub fn (a []string) str() string { + mut sb := strings.new_builder(a.len * 3) + sb.write_string('[') + for i in 0 .. a.len { + val := a[i] + sb.write_string("'") + sb.write_string(val) + sb.write_string("'") + if i < a.len - 1 { + sb.write_string(', ') + } + } + sb.write_string(']') + res := sb.str() + unsafe { sb.free() } + return res +} + +// hex returns a string with the hexadecimal representation +// of the byte elements of the array. +pub fn (b []byte) hex() string { + mut hex := unsafe { malloc(b.len * 2 + 1) } + mut dst_i := 0 + for i in b { + n0 := i >> 4 + unsafe { + hex[dst_i] = if n0 < 10 { n0 + `0` } else { n0 + byte(87) } + dst_i++ + } + n1 := i & 0xF + unsafe { + hex[dst_i] = if n1 < 10 { n1 + `0` } else { n1 + byte(87) } + dst_i++ + } + } + unsafe { + hex[dst_i] = 0 + return tos(hex, dst_i) + } +} + +// copy copies the `src` byte array elements to the `dst` byte array. +// The number of the elements copied is the minimum of the length of both arrays. +// Returns the number of elements copied. +// TODO: implement for all types +pub fn copy(dst []byte, src []byte) int { + min := if dst.len < src.len { dst.len } else { src.len } + if min > 0 { + unsafe { C.memcpy(&byte(dst.data), src.data, min) } + } + return min +} + +// Private function. Comparator for int type. +fn compare_ints(a &int, b &int) int { + if *a < *b { + return -1 + } + if *a > *b { + return 1 + } + return 0 +} + +fn compare_ints_reverse(a &int, b &int) int { + if *a > *b { + return -1 + } + if *a < *b { + return 1 + } + return 0 +} + +// sort sorts an array of int in place in ascending order. +pub fn (mut a []int) sort() { + a.sort_with_compare(compare_ints) +} + +// index returns the first index at which a given element can be found in the array +// or -1 if the value is not found. +[direct_array_access] +pub fn (a []string) index(v string) int { + for i in 0 .. a.len { + if a[i] == v { + return i + } + } + return -1 +} + +// reduce executes a given reducer function on each element of the array, +// resulting in a single output value. +pub fn (a []int) reduce(iter fn (int, int) int, accum_start int) int { + mut accum_ := accum_start + for i in a { + accum_ = iter(accum_, i) + } + return accum_ +} + +// grow_cap grows the array's capacity by `amount` elements. +pub fn (mut a array) grow_cap(amount int) { + a.ensure_cap(a.cap + amount) +} + +// grow_len ensures that an array has a.len + amount of length +[unsafe] +pub fn (mut a array) grow_len(amount int) { + a.ensure_cap(a.len + amount) + a.len += amount +} + +// eq checks if the arrays have the same elements or not. +// TODO: make it work with all types. +pub fn (a1 []string) eq(a2 []string) bool { + // return array_eq(a, a2) + if a1.len != a2.len { + return false + } + size_of_string := int(sizeof(string)) + for i in 0 .. a1.len { + offset := i * size_of_string + s1 := unsafe { &string(&byte(a1.data) + offset) } + s2 := unsafe { &string(&byte(a2.data) + offset) } + if *s1 != *s2 { + return false + } + } + return true +} + +// pointers returns a new array, where each element +// is the address of the corresponding element in the array. +[unsafe] +pub fn (a array) pointers() []voidptr { + mut res := []voidptr{} + for i in 0 .. a.len { + unsafe { res << a.get_unsafe(i) } + } + return res +} + +// voidptr.vbytes() - makes a V []byte structure from a C style memory buffer. NB: the data is reused, NOT copied! +[unsafe] +pub fn (data voidptr) vbytes(len int) []byte { + res := array{ + element_size: 1 + data: data + len: len + cap: len + } + return res +} + +// byteptr.vbytes() - makes a V []byte structure from a C style memory buffer. NB: the data is reused, NOT copied! +[unsafe] +pub fn (data &byte) vbytes(len int) []byte { + return unsafe { voidptr(data).vbytes(len) } +} diff --git a/v_windows/v/old/vlib/builtin/array_d_gcboehm_opt.v b/v_windows/v/old/vlib/builtin/array_d_gcboehm_opt.v new file mode 100644 index 0000000..330977d --- /dev/null +++ b/v_windows/v/old/vlib/builtin/array_d_gcboehm_opt.v @@ -0,0 +1,268 @@ +// non-pub versions of array functions +// that allocale new memory using `GC_MALLOC_ATOMIC()` +// when `-gc boehm_*_opt` is used. These memory areas are not +// scanned for pointers. + +module builtin + +fn __new_array_noscan(mylen int, cap int, elm_size int) array { + cap_ := if cap < mylen { mylen } else { cap } + arr := array{ + element_size: elm_size + data: vcalloc_noscan(cap_ * elm_size) + len: mylen + cap: cap_ + } + return arr +} + +fn __new_array_with_default_noscan(mylen int, cap int, elm_size int, val voidptr) array { + cap_ := if cap < mylen { mylen } else { cap } + mut arr := array{ + element_size: elm_size + data: vcalloc_noscan(cap_ * elm_size) + len: mylen + cap: cap_ + } + if val != 0 { + for i in 0 .. arr.len { + unsafe { arr.set_unsafe(i, val) } + } + } + return arr +} + +fn __new_array_with_array_default_noscan(mylen int, cap int, elm_size int, val array) array { + cap_ := if cap < mylen { mylen } else { cap } + mut arr := array{ + element_size: elm_size + data: vcalloc_noscan(cap_ * elm_size) + len: mylen + cap: cap_ + } + for i in 0 .. arr.len { + val_clone := val.clone() + unsafe { arr.set_unsafe(i, &val_clone) } + } + return arr +} + +// Private function, used by V (`nums := [1, 2, 3]`) +fn new_array_from_c_array_noscan(len int, cap int, elm_size int, c_array voidptr) array { + cap_ := if cap < len { len } else { cap } + arr := array{ + element_size: elm_size + data: vcalloc_noscan(cap_ * elm_size) + len: len + cap: cap_ + } + // TODO Write all memory functions (like memcpy) in V + unsafe { C.memcpy(arr.data, c_array, len * elm_size) } + return arr +} + +// Private function. Doubles array capacity if needed. +fn (mut a array) ensure_cap_noscan(required int) { + if required <= a.cap { + return + } + mut cap := if a.cap > 0 { a.cap } else { 2 } + for required > cap { + cap *= 2 + } + new_size := cap * a.element_size + new_data := vcalloc_noscan(new_size) + if a.data != voidptr(0) { + unsafe { C.memcpy(new_data, a.data, a.len * a.element_size) } + // TODO: the old data may be leaked when no GC is used (ref-counting?) + } + a.data = new_data + a.offset = 0 + a.cap = cap +} + +// repeat returns a new array with the given array elements repeated given times. +// `cgen` will replace this with an apropriate call to `repeat_to_depth()` + +// version of `repeat()` that handles multi dimensional arrays +// `unsafe` to call directly because `depth` is not checked +[unsafe] +fn (a array) repeat_to_depth_noscan(count int, depth int) array { + if count < 0 { + panic('array.repeat: count is negative: $count') + } + mut size := count * a.len * a.element_size + if size == 0 { + size = a.element_size + } + arr := array{ + element_size: a.element_size + data: if depth > 0 { vcalloc(size) } else { vcalloc_noscan(size) } + len: count * a.len + cap: count * a.len + } + if a.len > 0 { + for i in 0 .. count { + if depth > 0 { + ary_clone := unsafe { a.clone_to_depth_noscan(depth) } + unsafe { C.memcpy(arr.get_unsafe(i * a.len), &byte(ary_clone.data), a.len * a.element_size) } + } else { + unsafe { C.memcpy(arr.get_unsafe(i * a.len), &byte(a.data), a.len * a.element_size) } + } + } + } + return arr +} + +// insert inserts a value in the array at index `i` +fn (mut a array) insert_noscan(i int, val voidptr) { + $if !no_bounds_checking ? { + if i < 0 || i > a.len { + panic('array.insert: index out of range (i == $i, a.len == $a.len)') + } + } + a.ensure_cap_noscan(a.len + 1) + unsafe { + C.memmove(a.get_unsafe(i + 1), a.get_unsafe(i), (a.len - i) * a.element_size) + a.set_unsafe(i, val) + } + a.len++ +} + +// insert_many inserts many values into the array from index `i`. +[unsafe] +fn (mut a array) insert_many_noscan(i int, val voidptr, size int) { + $if !no_bounds_checking ? { + if i < 0 || i > a.len { + panic('array.insert_many: index out of range (i == $i, a.len == $a.len)') + } + } + a.ensure_cap_noscan(a.len + size) + elem_size := a.element_size + unsafe { + iptr := a.get_unsafe(i) + C.memmove(a.get_unsafe(i + size), iptr, (a.len - i) * elem_size) + C.memcpy(iptr, val, size * elem_size) + } + a.len += size +} + +// prepend prepends one value to the array. +fn (mut a array) prepend_noscan(val voidptr) { + a.insert_noscan(0, val) +} + +// prepend_many prepends another array to this array. +[unsafe] +fn (mut a array) prepend_many_noscan(val voidptr, size int) { + unsafe { a.insert_many_noscan(0, val, size) } +} + +// pop returns the last element of the array, and removes it. +fn (mut a array) pop_noscan() voidptr { + // in a sense, this is the opposite of `a << x` + $if !no_bounds_checking ? { + if a.len == 0 { + panic('array.pop: array is empty') + } + } + new_len := a.len - 1 + last_elem := unsafe { &byte(a.data) + new_len * a.element_size } + a.len = new_len + // NB: a.cap is not changed here *on purpose*, so that + // further << ops on that array will be more efficient. + return unsafe { memdup_noscan(last_elem, a.element_size) } +} + +// `clone_static_to_depth_noscan()` returns an independent copy of a given array. +// Unlike `clone_to_depth_noscan()` it has a value receiver and is used internally +// for slice-clone expressions like `a[2..4].clone()` and in -autofree generated code. +fn (a array) clone_static_to_depth_noscan(depth int) array { + return unsafe { a.clone_to_depth_noscan(depth) } +} + +// recursively clone given array - `unsafe` when called directly because depth is not checked +[unsafe] +fn (a &array) clone_to_depth_noscan(depth int) array { + mut size := a.cap * a.element_size + if size == 0 { + size++ + } + mut arr := array{ + element_size: a.element_size + data: if depth == 0 { vcalloc_noscan(size) } else { vcalloc(size) } + len: a.len + cap: a.cap + } + // Recursively clone-generated elements if array element is array type + if depth > 0 { + for i in 0 .. a.len { + ar := array{} + unsafe { C.memcpy(&ar, a.get_unsafe(i), int(sizeof(array))) } + ar_clone := unsafe { ar.clone_to_depth_noscan(depth - 1) } + unsafe { arr.set_unsafe(i, &ar_clone) } + } + return arr + } else { + if !isnil(a.data) { + unsafe { C.memcpy(&byte(arr.data), a.data, a.cap * a.element_size) } + } + return arr + } +} + +fn (mut a array) push_noscan(val voidptr) { + a.ensure_cap_noscan(a.len + 1) + unsafe { C.memmove(&byte(a.data) + a.element_size * a.len, val, a.element_size) } + a.len++ +} + +// push_many implements the functionality for pushing another array. +// `val` is array.data and user facing usage is `a << [1,2,3]` +[unsafe] +fn (mut a3 array) push_many_noscan(val voidptr, size int) { + if a3.data == val && !isnil(a3.data) { + // handle `arr << arr` + copy := a3.clone() + a3.ensure_cap_noscan(a3.len + size) + unsafe { + // C.memcpy(a.data, copy.data, copy.element_size * copy.len) + C.memcpy(a3.get_unsafe(a3.len), copy.data, a3.element_size * size) + } + } else { + a3.ensure_cap_noscan(a3.len + size) + if !isnil(a3.data) && !isnil(val) { + unsafe { C.memcpy(a3.get_unsafe(a3.len), val, a3.element_size * size) } + } + } + a3.len += size +} + +// reverse returns a new array with the elements of the original array in reverse order. +fn (a array) reverse_noscan() array { + if a.len < 2 { + return a + } + mut arr := array{ + element_size: a.element_size + data: vcalloc_noscan(a.cap * a.element_size) + len: a.len + cap: a.cap + } + for i in 0 .. a.len { + unsafe { arr.set_unsafe(i, a.get_unsafe(a.len - 1 - i)) } + } + return arr +} + +// grow_cap grows the array's capacity by `amount` elements. +fn (mut a array) grow_cap_noscan(amount int) { + a.ensure_cap_noscan(a.cap + amount) +} + +// grow_len ensures that an array has a.len + amount of length +[unsafe] +fn (mut a array) grow_len_noscan(amount int) { + a.ensure_cap_noscan(a.len + amount) + a.len += amount +} diff --git a/v_windows/v/old/vlib/builtin/array_notd_gcboehm_opt.v b/v_windows/v/old/vlib/builtin/array_notd_gcboehm_opt.v new file mode 100644 index 0000000..1703167 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/array_notd_gcboehm_opt.v @@ -0,0 +1,10 @@ +// dummy placeholder for functions from `array_d_gcboehm_opt.v` +// that might be needed for compile time +// `$if gcboehm_opt ? { ... } $else { ... }` + +module builtin + +// this is needed in `string.v` +fn __new_array_noscan(mylen int, cap int, elm_size int) array { + return array{} +} diff --git a/v_windows/v/old/vlib/builtin/array_test.v b/v_windows/v/old/vlib/builtin/array_test.v new file mode 100644 index 0000000..d7699ca --- /dev/null +++ b/v_windows/v/old/vlib/builtin/array_test.v @@ -0,0 +1,1469 @@ +fn test_pointer() { + mut arr := []&int{} + a := 1 + b := 2 + c := 3 + arr << &a + arr << &b + arr << &c + assert *arr[0] == 1 + arr[1] = &c + assert *arr[1] == 3 + mut d_arr := [arr] // [][]&int + d_arr << arr + assert *d_arr[0][1] == 3 + println(*d_arr[0][1]) + assert *d_arr[1][0] == 1 +} + +fn test_assign() { + mut arr := [2, 4, 8, 16, 32, 64, 128] + arr[0] = 2 + arr[1] &= 255 + arr[2] |= 255 + arr[3] <<= 4 + arr[4] >>= 4 + arr[5] %= 5 + arr[6] ^= 3 + assert arr[0] == 2 + assert arr[1] == 4 & 255 + assert arr[2] == 8 | 255 + assert arr[3] == 16 << 4 + assert arr[4] == 32 >> 4 + assert arr[5] == 64 % 5 + assert arr[6] == 128 ^ 3 +} + +fn test_ints() { + mut a := [1, 5, 2, 3] + assert a.len == 4 + assert a[0] == 1 + assert a[2] == 2 + assert a.last() == 3 + a << 4 + assert a.len == 5 + assert a[4] == 4 + assert a.last() == 4 + s := a.str() + assert s == '[1, 5, 2, 3, 4]' + assert a[1] == 5 + assert a.last() == 4 +} + +fn test_deleting() { + mut a := [1, 5, 2, 3, 4] + assert a.len == 5 + assert a.str() == '[1, 5, 2, 3, 4]' + a.delete(0) + assert a.str() == '[5, 2, 3, 4]' + assert a.len == 4 + a.delete(1) + assert a.str() == '[5, 3, 4]' + assert a.len == 3 + a.delete(a.len - 1) + assert a.str() == '[5, 3]' + assert a.len == 2 +} + +fn test_slice_delete() { + mut a := [1.5, 2.5, 3.25, 4.5, 5.75] + b := a[2..4] + a.delete(0) + assert a == [2.5, 3.25, 4.5, 5.75] + assert b == [3.25, 4.5] + a = [3.75, 4.25, -1.5, 2.25, 6.0] + c := a[..3] + a.delete(2) + assert a == [3.75, 4.25, 2.25, 6.0] + assert c == [3.75, 4.25, -1.5] +} + +fn test_delete_many() { + mut a := [1, 2, 3, 4, 5, 6, 7, 8, 9] + b := a[2..6] + a.delete_many(4, 3) + assert a == [1, 2, 3, 4, 8, 9] + assert b == [3, 4, 5, 6] + c := a[..a.len] + a.delete_many(2, 0) // this should just clone + a[1] = 17 + assert a == [1, 17, 3, 4, 8, 9] + assert c == [1, 2, 3, 4, 8, 9] + a.delete_many(0, a.len) + assert a == []int{} +} + +fn test_short() { + a := [1, 2, 3] + assert a.len == 3 + assert a.cap == 3 + assert a[0] == 1 + assert a[1] == 2 + assert a[2] == 3 +} + +fn test_large() { + mut a := [0].repeat(0) + for i in 0 .. 10000 { + a << i + } + assert a.len == 10000 + assert a[234] == 234 +} + +struct Chunk { + val string +} + +struct Kkk { + q []Chunk +} + +fn test_empty() { + mut chunks := []Chunk{} + a := Chunk{} + assert chunks.len == 0 + chunks << a + assert chunks.len == 1 + chunks = [] + assert chunks.len == 0 + chunks << a + assert chunks.len == 1 +} + +fn test_push() { + mut a := []int{} + a << 1 + a << 3 + assert a[1] == 3 + assert a.str() == '[1, 3]' +} + +fn test_insert() { + mut a := [1, 2] + a.insert(0, 3) + assert a[0] == 3 + assert a[2] == 2 + assert a.len == 3 + a.insert(1, 4) + assert a[1] == 4 + assert a[2] == 1 + assert a.len == 4 + a.insert(4, 5) + assert a[4] == 5 + assert a[3] == 2 + assert a.len == 5 + mut b := []f64{} + assert b.len == 0 + b.insert(0, f64(1.1)) + assert b.len == 1 + assert b[0] == f64(1.1) +} + +fn test_insert_many() { + mut a := [3, 4] + a.insert(0, [1, 2]) + assert a == [1, 2, 3, 4] + b := [5, 6] + a.insert(1, b) + assert a == [1, 5, 6, 2, 3, 4] +} + +fn test_prepend() { + mut a := []int{} + assert a.len == 0 + a.prepend(1) + assert a.len == 1 + assert a[0] == 1 + mut b := []f64{} + assert b.len == 0 + b.prepend(f64(1.1)) + assert b.len == 1 + assert b[0] == f64(1.1) +} + +fn test_prepend_many() { + mut a := [3, 4] + a.prepend([1, 2]) + assert a == [1, 2, 3, 4] + b := [5, 6] + a.prepend(b) + assert a == [5, 6, 1, 2, 3, 4] +} + +fn test_strings() { + a := ['a', 'b', 'c'] + assert a.str() == "['a', 'b', 'c']" +} + +/* +fn test_compare_ints() { + assert compare_ints(1, 2) == -1 + assert compare_ints(2, 1) == 1 + assert compare_ints(0, 0) == 0 + + a := 1 + b := 2 + assert compare_ints(a, b) == -1 + assert compare_ints(b, a) == 1 + assert compare_ints(a, a) == 0 +} +*/ +fn test_repeat() { + { + a := [0].repeat(5) + assert a.len == 5 + assert a[0] == 0 && a[1] == 0 && a[2] == 0 && a[3] == 0 && a[4] == 0 + } + { + a := [1.1].repeat(10) + assert a[0] == 1.1 + assert a[5] == 1.1 + assert a[9] == 1.1 + } + { + a := [i64(-123)].repeat(10) + assert a[0] == -123 + assert a[5] == -123 + assert a[9] == -123 + } + { + a := [u64(123)].repeat(10) + assert a[0] == 123 + assert a[5] == 123 + assert a[9] == 123 + } + { + a := [1.1].repeat(10) + assert a[0] == 1.1 + assert a[5] == 1.1 + assert a[9] == 1.1 + } + { + a := [1, 2].repeat(2) + assert a[0] == 1 + assert a[1] == 2 + assert a[2] == 1 + assert a[3] == 2 + } + { + a := ['1', 'abc'].repeat(2) + assert a[0] == '1' + assert a[1] == 'abc' + assert a[2] == '1' + assert a[3] == 'abc' + } + { + mut a := ['1', 'abc'].repeat(0) + assert a.len == 0 + a << 'abc' + assert a[0] == 'abc' + } +} + +fn test_deep_repeat() { + mut a3 := [[[1, 1], [2, 2], [3, 3]], [[4, 4], [5, 5], [6, 6]]] + r := a3.repeat(3) + a3[1][1][0] = 17 + assert r == [ + [[1, 1], [2, 2], [3, 3]], + [[4, 4], [5, 5], [6, 6]], + [[1, 1], [2, 2], [3, 3]], + [[4, 4], [5, 5], [6, 6]], + [[1, 1], [2, 2], [3, 3]], + [[4, 4], [5, 5], [6, 6]], + ] + assert a3 == [[[1, 1], [2, 2], [3, 3]], [[4, 4], [17, 5], [6, 6]]] +} + +fn test_right() { + a := [1, 2, 3, 4] + c := a[1..a.len] + d := a[1..] + assert c[0] == 2 + assert c[1] == 3 + assert d[0] == 2 + assert d[1] == 3 +} + +fn test_left() { + a := [1, 2, 3] + c := a[0..2] + d := a[..2] + assert c[0] == 1 + assert c[1] == 2 + assert d[0] == 1 + assert d[1] == 2 +} + +fn test_slice() { + a := [1, 2, 3, 4] + b := a[2..4] + assert b.len == 2 + assert a[1..2].len == 1 + assert a.len == 4 +} + +fn test_push_many() { + mut a := [1, 2, 3] + b := [4, 5, 6] + a << b + assert a.len == 6 + assert a[0] == 1 + assert a[3] == 4 + assert a[5] == 6 +} + +fn test_reverse() { + a := [1, 2, 3, 4] + b := ['test', 'array', 'reverse'] + c := a.reverse() + println(c) + d := b.reverse() + for i, _ in c { + assert c[i] == a[a.len - i - 1] + } + for i, _ in d { + assert d[i] == b[b.len - i - 1] + } + e := []int{} + f := e.reverse() + assert f.len == 0 +} + +const ( + c_n = 5 +) + +struct Foooj { + a [5]int // c_n +} + +fn test_fixed() { + mut nums := [4]int{} + // x := nums[1..3] + // assert x.len == 2 + assert nums[0] == 0 + assert nums[1] == 0 + assert nums[2] == 0 + assert nums[3] == 0 + nums[1] = 7 + assert nums[1] == 7 + nums2 := [5]int{} // c_n + assert nums2[c_n - 1] == 0 +} + +fn modify(mut numbers []int) { + numbers[0] = 777 +} + +fn test_mut_slice() { + mut n := [1, 2, 3] + // modify(mut n) + modify(mut n[..2]) + assert n[0] == 777 + modify(mut n[2..]) + assert n[2] == 777 + println(n) +} + +fn double_up(mut a []int) { + for i := 0; i < a.len; i++ { + a[i] = a[i] * 2 + } +} + +fn double_up_v2(mut a []int) { + for i, _ in a { + a[i] = a[i] * 2 // or val*2, doesn't matter + } +} + +fn test_mut_arg() { + mut arr := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + double_up(mut arr) + assert arr.str() == '[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]' + arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + double_up_v2(mut arr) + assert arr.str() == '[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]' +} + +fn test_clone() { + nums := [1, 2, 3, 4, 100] + _ = nums + nums2 := nums.clone() + assert nums2.len == 5 + assert nums.str() == '[1, 2, 3, 4, 100]' + assert nums2.str() == '[1, 2, 3, 4, 100]' + assert nums[1..3].str() == '[2, 3]' +} + +/* +fn test_copy() { + a := [1, 2, 3] + b := a + assert b[0] == 1 + assert b[1] == 2 + assert b[2] == 3 +} +*/ +fn test_multi_array_clone() { + // 2d array_int + mut a2_1 := [[1, 2, 3], [4, 5, 6]] + mut a2_2 := a2_1.clone() + a2_1[0][1] = 0 + a2_2[1][0] = 0 + assert a2_1 == [[1, 0, 3], [4, 5, 6]] + assert a2_2 == [[1, 2, 3], [0, 5, 6]] + // 2d array_string + mut b2_1 := [['1', '2', '3'], ['4', '5', '6']] + mut b2_2 := b2_1.clone() + b2_1[0][1] = '0' + b2_2[1][0] = '0' + assert b2_1 == [['1', '0', '3'], ['4', '5', '6']] + assert b2_2 == [['1', '2', '3'], ['0', '5', '6']] + // 3d array_int + mut a3_1 := [[[1, 1], [2, 2], [3, 3]], [[4, 4], [5, 5], [6, 6]]] + mut a3_2 := a3_1.clone() + a3_1[0][0][1] = 0 + a3_2[0][1][0] = 0 + assert a3_1 == [[[1, 0], [2, 2], [3, 3]], [[4, 4], [5, 5], + [6, 6], + ]] + assert a3_2 == [[[1, 1], [0, 2], [3, 3]], [[4, 4], [5, 5], + [6, 6], + ]] + // 3d array_string + mut b3_1 := [[['1', '1'], ['2', '2'], ['3', '3']], [['4', '4'], + ['5', '5'], ['6', '6']]] + mut b3_2 := b3_1.clone() + b3_1[0][0][1] = '0' + b3_2[0][1][0] = '0' + assert b3_1 == [[['1', '0'], ['2', '2'], ['3', '3']], [['4', '4'], + ['5', '5'], ['6', '6']]] + assert b3_2 == [[['1', '1'], ['0', '2'], ['3', '3']], [['4', '4'], + ['5', '5'], ['6', '6']]] +} + +fn test_doubling() { + mut nums := [1, 2, 3, 4, 5] + for i in 0 .. nums.len { + nums[i] *= 2 + } + println(nums.str()) + assert nums.str() == '[2, 4, 6, 8, 10]' +} + +struct Test2 { + one int + two int +} + +struct Test { + a string +mut: + b []Test2 +} + +// TODO: default array/struct str methods +fn (ta []Test2) str() string { + mut s := '[' + for i, t in ta { + s += t.str() + if i < ta.len - 1 { + s += ', ' + } + } + s += ']' + return s +} + +fn (t Test2) str() string { + return '{$t.one $t.two}' +} + +fn (t Test) str() string { + return '{$t.a $t.b}' +} + +fn test_struct_print() { + mut a := Test{ + a: 'Test' + b: [] + } + b := Test2{ + one: 1 + two: 2 + } + a.b << b + a.b << b + assert a.str() == '{Test [{1 2}, {1 2}]}' + assert b.str() == '{1 2}' + assert a.b.str() == '[{1 2}, {1 2}]' +} + +fn test_single_element() { + mut a := [1] + a << 2 + assert a.len == 2 + assert a[0] == 1 + assert a[1] == 2 + println(a) +} + +fn test_find_index() { + // string + a := ['v', 'is', 'great'] + assert a.index('v') == 0 + assert a.index('is') == 1 + assert a.index('gre') == -1 + // int + b := [1, 2, 3, 4] + assert b.index(1) == 0 + assert b.index(4) == 3 + assert b.index(5) == -1 + // byte + c := [0x22, 0x33, 0x55] + assert c.index(0x22) == 0 + assert c.index(0x55) == 2 + assert c.index(0x99) == -1 + // char + d := [`a`, `b`, `c`] + assert d.index(`b`) == 1 + assert d.index(`c`) == 2 + assert d.index(`u`) == -1 +} + +fn test_multi() { + a := [[1, 2, 3], [4, 5, 6]] + assert a.len == 2 + assert a[0].len == 3 + assert a[0][0] == 1 + assert a[0][2] == 3 + assert a[1][2] == 6 + // TODO + // b := [ [[1,2,3],[4,5,6]], [[1,2]] ] + // assert b[0][0][0] == 1 +} + +fn test_in() { + a := [1, 2, 3] + assert 1 in a + assert 2 in a + assert 3 in a + assert !(4 in a) + assert !(0 in a) + assert 0 !in a + assert 4 !in a + b := [1, 4, 0] + c := [3, 6, 2, 0] + assert 0 in b + assert 0 in c +} + +fn sum(prev int, curr int) int { + return prev + curr +} + +fn sub(prev int, curr int) int { + return prev - curr +} + +fn test_reduce() { + a := [1, 2, 3, 4, 5] + b := a.reduce(sum, 0) + c := a.reduce(sum, 5) + d := a.reduce(sum, -1) + assert b == 15 + assert c == 20 + assert d == 14 + e := [1, 2, 3] + f := e.reduce(sub, 0) + g := e.reduce(sub, -1) + assert f == -6 + assert g == -7 +} + +fn filter_test_helper_1(a int) bool { + return a > 3 +} + +fn test_filter() { + a := [1, 2, 3, 4, 5, 6] + b := a.filter(it % 2 == 0) + assert b.len == 3 + assert b[0] == 2 + assert b[1] == 4 + assert b[2] == 6 + c := ['v', 'is', 'awesome'] + d := c.filter(it.len > 1) + assert d[0] == 'is' + assert d[1] == 'awesome' + //////// + arr := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + println(arr.filter(it % 2 == 0 || it % 3 == 0)) + assert true + assert [1, 2, 3].len == 3 + mut mut_arr := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + mut_arr = mut_arr.filter(it < 4) + assert mut_arr.len == 3 + assert a.filter(filter_test_helper_1) == [4, 5, 6] + assert [1, 5, 10].filter(filter_test_helper_1) == [5, 10] + // TODO + // assert arr.filter(arr % 2).len == 5 +} + +fn test_anon_fn_filter() { + filter_num := fn (i int) bool { + return i % 2 == 0 + } + assert [1, 2, 3, 4, 5].filter(filter_num) == [2, 4] +} + +fn test_anon_fn_arg_filter() { + a := [1, 2, 3, 4].filter(fn (i int) bool { + return i % 2 == 0 + }) + assert a == [2, 4] +} + +fn map_test_helper_1(i int) int { + return i * i +} + +fn map_test_helper_2(i int, b string) int { + return i + b.len +} + +fn map_test_helper_3(i int, b []string) int { + return i + b.map(it.len)[i % b.len] +} + +fn test_map() { + nums := [1, 2, 3, 4, 5, 6] + strs := ['v', 'is', 'awesome'] + // assert nums.map() == + // assert nums.map(it, 'excessive') == + // identity + assert nums.map(it) == [1, 2, 3, 4, 5, 6] + assert strs.map(it) == ['v', 'is', 'awesome'] + assert nums.map(it - it) == [0, 0, 0, 0, 0, 0] + assert nums.map(it - it)[0] == 0 + // type switch + assert nums.map(it * 10) == [10, 20, 30, 40, 50, 60] + assert nums.map(it * it) == [1, 4, 9, 16, 25, 36] + assert nums.map('$it') == ['1', '2', '3', '4', '5', '6'] + assert nums.map(it % 2 == 0) == [false, true, false, true, false, true] + assert strs.map(it.to_upper()) == ['V', 'IS', 'AWESOME'] + assert strs.map(it == 'awesome') == [false, false, true] + assert strs.map(it.len in nums) == [true, true, false] + assert strs.map(int(7)) == [7, 7, 7] + // external func + assert nums.map(map_test_helper_1(it)) == [1, 4, 9, 16, 25, 36] + assert nums.map(map_test_helper_2(it, 'bb')) == [3, 4, 5, 6, 7, 8] + assert nums.map(map_test_helper_3(it, strs)) == [3, 9, 4, 6, 12, 7] + // empty array as input + assert []int{len: 0}.map(it * 2) == [] + // nested maps (where it is of same type) + assert nums.map(strs.map(int(7)) == [7, 7, 7]) == [true, true, true, true, true, true] + assert nums.map('$it' + strs.map('a')[0]) == ['1a', '2a', '3a', '4a', '5a', '6a'] + assert nums.map(it + strs.map(int(7))[0]) == [8, 9, 10, 11, 12, 13] + assert nums.map(it + strs.map(it.len)[0]) == [2, 3, 4, 5, 6, 7] + assert strs.map(it.len + strs.map(it.len)[0]) == [2, 3, 8] + // nested (different it types) + assert strs.map(it[nums.map(it - it)[0]]) == [byte(`v`), `i`, `a`] + assert nums[0..3].map('$it' + strs.map(it)[it - 1]) == ['1v', '2is', '3awesome'] + assert nums.map(map_test_helper_1) == [1, 4, 9, 16, 25, 36] + assert [1, 5, 10].map(map_test_helper_1) == [1, 25, 100] + assert nums == [1, 2, 3, 4, 5, 6] + assert strs == ['v', 'is', 'awesome'] +} + +fn test_anon_fn_map() { + add_num := fn (i int) int { + return i + 1 + } + assert [1, 2, 3].map(add_num) == [2, 3, 4] +} + +fn test_multi_anon_fn_map() { + a := [1, 2, 3].map(fn (i int) int { + return i + 1 + }) + b := [1, 2, 3].map(fn (i int) int { + return i + 2 + }) + assert a == [2, 3, 4] + assert b == [3, 4, 5] +} + +fn test_anon_fn_arg_map() { + a := [1, 2, 3].map(fn (i int) int { + return i + 1 + }) + assert a == [2, 3, 4] +} + +fn test_anon_fn_arg_different_type_map() { + i_to_str := fn (i int) string { + return i.str() + } + a := [1, 2, 3].map(i_to_str) + assert a == ['1', '2', '3'] +} + +fn test_anon_fn_inline_different_type_map() { + a := [1, 2, 3].map(fn (i int) string { + return i.str() + }) + assert a == ['1', '2', '3'] +} + +fn test_array_str() { + numbers := [1, 2, 3] + assert numbers == [1, 2, 3] + numbers2 := [numbers, [4, 5, 6]] // dup str() bug + println(numbers2) + assert true + assert numbers.str() == '[1, 2, 3]' + // QTODO + // assert numbers2.str() == '[[1, 2, 3], [4, 5, 6]]' +} + +struct User { + age int + name string +} + +fn test_eq() { + assert [5, 6, 7] != [6, 7] + assert [`a`, `b`] == [`a`, `b`] + assert [User{ + age: 22 + name: 'bob' + }] == [User{ + age: 22 + name: 'bob' + }] + assert [map{ + 'bob': 22 + }, map{ + 'tom': 33 + }] == [map{ + 'bob': 22 + }, map{ + 'tom': 33 + }] + assert [[1, 2, 3], [4]] == [[1, 2, 3], [4]] +} + +fn test_fixed_array_eq() { + a1 := [1, 2, 3]! + assert a1 == [1, 2, 3]! + assert a1 != [2, 3, 4]! + + a2 := [[1, 2]!, [3, 4]!]! + assert a2 == [[1, 2]!, [3, 4]!]! + assert a2 != [[3, 4]!, [1, 2]!]! + + a3 := [[1, 2], [3, 4]]! + assert a3 == [[1, 2], [3, 4]]! + assert a3 != [[1, 1], [2, 2]]! + + a4 := [[`a`, `b`], [`c`, `d`]]! + assert a4 == [[`a`, `b`], [`c`, `d`]]! + assert a4 != [[`c`, `a`], [`a`, `b`]]! + + a5 := [['aaa', 'bbb'], ['ccc', 'ddd']]! + assert a5 == [['aaa', 'bbb'], ['ccc', 'ddd']]! + assert a5 != [['abc', 'def'], ['ccc', 'ddd']]! + + a6 := [['aaa', 'bbb']!, ['ccc', 'ddd']!]! + assert a6 == [['aaa', 'bbb']!, ['ccc', 'ddd']!]! + assert a6 != [['aaa', 'bbb']!, ['aaa', 'ddd']!]! + + a7 := [[1, 2]!, [3, 4]!] + assert a7 == [[1, 2]!, [3, 4]!] + assert a7 != [[2, 3]!, [1, 2]!] + + a8 := [['aaa', 'bbb']!, ['ccc', 'ddd']!] + assert a8 == [['aaa', 'bbb']!, ['ccc', 'ddd']!] + assert a8 != [['bbb', 'aaa']!, ['cccc', 'dddd']!] +} + +fn test_fixed_array_literal_eq() { + assert [1, 2, 3]! == [1, 2, 3]! + assert [1, 1, 1]! != [1, 2, 3]! + + assert [[1, 2], [3, 4]]! == [[1, 2], [3, 4]]! + assert [[1, 1], [2, 2]]! != [[1, 2], [3, 4]]! + + assert [[1, 1]!, [2, 2]!]! == [[1, 1]!, [2, 2]!]! + assert [[1, 1]!, [2, 2]!]! != [[1, 2]!, [2, 3]!]! + + assert [[1, 1]!, [2, 2]!] == [[1, 1]!, [2, 2]!] + assert [[1, 1]!, [2, 2]!] != [[1, 2]!, [2, 3]!] +} + +fn test_sort() { + mut a := ['hi', '1', '5', '3'] + a.sort() + assert a[0] == '1' + assert a[1] == '3' + assert a[2] == '5' + assert a[3] == 'hi' + + mut nums := [67, -3, 108, 42, 7] + nums.sort() + assert nums[0] == -3 + assert nums[1] == 7 + assert nums[2] == 42 + assert nums[3] == 67 + assert nums[4] == 108 + + nums.sort(a < b) + assert nums[0] == -3 + assert nums[1] == 7 + assert nums[2] == 42 + assert nums[3] == 67 + assert nums[4] == 108 + + mut users := [User{22, 'Peter'}, User{20, 'Bob'}, User{25, 'Alice'}] + users.sort(a.age < b.age) + assert users[0].age == 20 + assert users[1].age == 22 + assert users[2].age == 25 + assert users[0].name == 'Bob' + assert users[1].name == 'Peter' + assert users[2].name == 'Alice' + + users.sort(a.age > b.age) + assert users[0].age == 25 + assert users[1].age == 22 + assert users[2].age == 20 + + users.sort(a.name < b.name) // Test sorting by string fields + // assert users.map(it.name).join(' ') == 'Alice Bob Peter' +} + +fn test_rune_sort() { + mut bs := [`f`, `e`, `d`, `b`, `c`, `a`] + bs.sort() + println(bs) + assert '$bs' == '[`a`, `b`, `c`, `d`, `e`, `f`]' + + bs.sort(a > b) + println(bs) + assert '$bs' == '[`f`, `e`, `d`, `c`, `b`, `a`]' + + bs.sort(a < b) + println(bs) + assert '$bs' == '[`a`, `b`, `c`, `d`, `e`, `f`]' +} + +fn test_sort_by_different_order_of_a_b() { + mut x := [1, 2, 3] + x.sort(a < b) + println(x) + assert x == [1, 2, 3] + + mut y := [1, 2, 3] + y.sort(b < a) + println(y) + assert y == [3, 2, 1] +} + +fn test_f32_sort() { + mut f := [f32(50.0), 15, 1, 79, 38, 0, 27] + f.sort() + assert f[0] == 0.0 + assert f[1] == 1.0 + assert f[6] == 79.0 +} + +fn test_f64_sort() { + mut f := [50.0, 15, 1, 79, 38, 0, 27] + f.sort() + assert f[0] == 0.0 + assert f[1] == 1.0 + assert f[6] == 79.0 +} + +fn test_i64_sort() { + mut f := [i64(50), 15, 1, 79, 38, 0, 27] + f.sort() + assert f[0] == 0 + assert f[1] == 1 + assert f[6] == 79 +} + +/* +fn test_for_last() { + numbers := [1, 2, 3, 4] + mut s := '[' + for num in numbers { + s += '$num' + if !last { + s += ', ' + + } + } + s += ']' + assert s == '[1, 2, 3, 4]' +} +*/ +struct Foo { +mut: + bar []int +} + +fn test_in_struct() { + mut baz := Foo{ + bar: [0, 0, 0] + } + baz.bar[0] += 2 + baz.bar[0]++ + assert baz.bar[0] == 3 +} + +[direct_array_access] +fn test_direct_modification() { + mut foo := [2, 0, 5] + foo[1] = 3 + foo[0] *= 7 + foo[1]-- + foo[2] -= 2 + assert foo[0] == 14 + assert foo[1] == 2 + assert foo[2] == 3 +} + +fn test_bools() { + println('test b') + mut a := [true, false] + a << true + println(a) +} + +fn test_push_many_self() { + mut actual_arr := [1, 2, 3, 4] + actual_arr << actual_arr + expected_arr := [1, 2, 3, 4, 1, 2, 3, 4] + assert actual_arr.len == expected_arr.len + for i in 0 .. actual_arr.len { + assert actual_arr[i] == expected_arr[i] + } +} + +fn test_for() { + nums := [1, 2, 3] + mut sum := 0 + for num in nums { + sum += num + } + assert sum == 6 +} + +fn test_clear() { + mut arr := [1, 2, 3] + assert arr.len == 3 + arr.clear() + assert arr.len == 0 + arr << 3 + arr << 2 + arr << 1 + arr << 0 + assert arr.len == 4 + assert arr[0] == 3 + assert arr[1] == 2 + assert arr[2] == 1 + assert arr[3] == 0 + arr.clear() + assert arr.len == 0 +} + +fn test_trim() { + mut arr := [1, 2, 3, 4, 5, 6, 7, 8, 9] + assert arr.len == 9 + arr.trim(9) + assert arr.len == 9 + assert arr.last() == 9 + arr.trim(7) + assert arr.len == 7 + assert arr.last() == 7 + arr.trim(2) + assert arr.len == 2 + assert arr.last() == 2 +} + +fn test_hex() { + // array hex + st := [byte(`V`), `L`, `A`, `N`, `G`] + assert st.hex() == '564c414e47' + assert st.hex().len == 10 + st1 := [byte(0x41)].repeat(100) + assert st1.hex() == '41'.repeat(100) +} + +fn test_left_shift_precendence() { + mut arr := []int{} + arr << 1 + 1 + arr << 1 - 1 + arr << 2 / 1 + arr << 2 * 1 + assert arr[0] == 2 + assert arr[1] == 0 + assert arr[2] == 2 + assert arr[3] == 2 +} + +fn test_array_with_cap() { + a4 := []int{len: 1, cap: 10} + assert a4.len == 1 + assert a4.cap == 10 + a5 := []int{len: 1, cap: 10} + assert a5.len == 1 + assert a5.cap == 10 +} + +fn test_multi_array_index() { + mut a := [][]int{len: 2, init: []int{len: 3, init: 0}} + a[0][0] = 1 + assert '$a' == '[[1, 0, 0], [0, 0, 0]]' + mut b := [[0].repeat(3)].repeat(2) + b[0][0] = 1 + assert '$b' == '[[1, 0, 0], [0, 0, 0]]' +} + +fn test_plus_assign_string() { + mut a := [''] + a[0] += 'abc' + assert a == ['abc'] +} + +fn mut_arr_with_eq_in_fn(mut a []int) { + if a == [1, 2, 3, 4] { + a[0] = 0 + } + if [0, 2, 3, 4] == a { + a[1] = 0 + } + if !(a != [0, 0, 3, 4]) { + a[2] = 0 + } + if !([0, 0, 0, 4] != a) { + a[3] = 0 + } +} + +fn test_mut_arr_with_eq_in_fn() { + mut a := [1, 2, 3, 4] + mut_arr_with_eq_in_fn(mut a) + assert a == [0, 0, 0, 0] +} + +fn array_in_mut(mut a []int) { + if 1 in a { + a[0] = 2 + } +} + +fn test_array_in_mut() { + mut a := [1, 2] + array_in_mut(mut a) + assert a == [2, 2] +} + +// test array delete in function with mut argument +fn delete_nums(mut arr []int) { + arr.delete(0) +} + +fn test_array_delete_in_mut() { + mut nums := [1, 2, 3] + delete_nums(mut nums) + assert nums == [2, 3] +} + +// test array add in function with mut argument +fn add_nums(mut arr []int) { + arr << 4 +} + +fn test_array_add_in_mut() { + mut nums := [1, 2, 3] + add_nums(mut nums) + assert nums == [1, 2, 3, 4] +} + +fn test_reverse_in_place() { + mut a := [1, 2, 3, 4] + a.reverse_in_place() + assert a == [4, 3, 2, 1] + mut b := ['a', 'b', 'c'] + b.reverse_in_place() + assert b == ['c', 'b', 'a'] + mut c := [[1, 2], [3, 4], [5, 6]] + c.reverse_in_place() + assert c == [[5, 6], [3, 4], [1, 2]] +} + +fn test_array_int_pop() { + mut a := [1, 2, 3, 4, 5] + assert a.len == 5 + x := a.last() + y := a.pop() + assert x == y + assert a.len == 4 + z := a.pop() + assert a.len == 3 + assert z == 4 + x1 := a.pop() + x2 := a.pop() + final := a.pop() + assert final == 1 +} + +fn test_array_string_pop() { + mut a := ['abc', 'def', 'xyz'] + assert a.len == 3 + assert a.pop() == 'xyz' + assert a.pop() == 'def' + assert a.pop() == 'abc' + assert a.len == 0 + assert a.cap == 3 +} + +fn test_array_first() { + a := [3] + assert a.first() == 3 + b := [1, 2, 3, 4] + assert b.first() == 1 + c := ['abc', 'def'] + assert c.first()[0] == `a` + s := [Chunk{'a'}] + assert s.first().val == 'a' +} + +fn test_array_last() { + a := [3] + assert a.last() == 3 + b := [1, 2, 3, 4] + assert b.last() == 4 + c := ['abc', 'def'] + assert c.last()[0] == `d` + s := [Chunk{'a'}] + assert s.last().val == 'a' +} + +[direct_array_access] +fn test_direct_array_access() { + mut a := [11, 22, 33, 44] + assert a[0] == 11 + assert a[2] == 33 + x := a[0] + a[0] = 21 + a[1] += 2 + a[2] = x + 3 + a[3] -= a[1] + assert a == [21, 24, 14, 20] +} + +[direct_array_access] +fn test_direct_array_access_via_ptr() { + mut b := [11, 22, 33, 44] + unsafe { + mut a := &b + assert a[0] == 11 + assert a[2] == 33 + x := a[0] + a[0] = 21 + a[1] += 2 + a[2] = x + 3 + a[3] -= a[1] + assert a == [21, 24, 14, 20] + } +} + +fn test_push_arr_string_free() { + mut lines := ['hi'] + s := 'a' + 'b' + lines << s + // make sure the data in the array is valid after freeing the string + unsafe { s.free() } + // + println(lines) + assert lines.len == 2 + assert lines[0] == 'hi' + assert lines[1] == 'ab' +} + +const ( + grid_size_1 = 2 + grid_size_2 = 3 + grid_size_3 = 4 + cell_value = 123 +) + +fn test_multidimensional_array_initialization_with_consts() { + mut data := [][][]int{len: grid_size_1, init: [][]int{len: grid_size_2, init: []int{len: grid_size_3, init: cell_value}}} + assert data.len == grid_size_1 + assert data[0].len == grid_size_2 + assert data[0][0].len == grid_size_3 + assert data[0][0][0] == cell_value + assert data[1][1][1] == cell_value +} + +fn test_byteptr_vbytes() { + unsafe { + bp := malloc(5) + bp[0] = 1 + bp[1] = 2 + bp[2] = 3 + bp[3] = 4 + bp[4] = 255 + bytes := bp.vbytes(5) + println(bytes) + assert bytes.len == 5 + assert bytes[0] == 1 + assert bytes[1] == 2 + assert bytes[2] == 3 + assert bytes[3] == 4 + assert bytes[4] == 255 + } +} + +fn test_voidptr_vbytes() { + unsafe { + bp := malloc(3) + bp[0] = 4 + bp[1] = 5 + bp[2] = 6 + bytes := voidptr(bp).vbytes(3) + assert bytes.len == 3 + assert bytes[0] == 4 + assert bytes[1] == 5 + assert bytes[2] == 6 + println(bytes) + } +} + +fn test_multi_array_prepend() { + mut a := [][]int{} + a.prepend([1, 2, 3]) + assert a == [[1, 2, 3]] + mut b := [][]int{} + b.prepend([[1, 2, 3]]) + assert b == [[1, 2, 3]] +} + +fn test_multi_array_insert() { + mut a := [][]int{} + a.insert(0, [1, 2, 3]) + assert a == [[1, 2, 3]] + mut b := [][]int{} + b.insert(0, [[1, 2, 3]]) + assert b == [[1, 2, 3]] +} + +fn test_multi_array_in() { + a := [[1]] + println([1] in a) + assert [1] in a +} + +fn test_any_type_array_contains() { + a := [true, false] + assert a.contains(true) + assert true in a + assert a.contains(false) + assert false in a + b := [i64(2), 3, 4] + assert b.contains(i64(3)) + assert 5 !in b + c := [[1], [2]] + assert c.contains([1]) + assert [2] in c + assert [3] !in c +} + +struct Person { + name string + nums []int + kv map[string]string +} + +fn test_struct_array_of_multi_type_in() { + ivan := Person{ + name: 'ivan' + nums: [1, 2, 3] + kv: map{ + 'aaa': '111' + } + } + people := [Person{ + name: 'ivan' + nums: [1, 2, 3] + kv: map{ + 'aaa': '111' + } + }, Person{ + name: 'bob' + nums: [2] + kv: map{ + 'bbb': '222' + } + }] + println(ivan in people) + assert ivan in people +} + +fn test_struct_array_of_multi_type_index() { + ivan := Person{ + name: 'ivan' + nums: [1, 2, 3] + kv: map{ + 'aaa': '111' + } + } + people := [Person{ + name: 'ivan' + nums: [1, 2, 3] + kv: map{ + 'aaa': '111' + } + }, Person{ + name: 'bob' + nums: [2] + kv: map{ + 'bbb': '222' + } + }] + println(people.index(ivan)) + assert people.index(ivan) == 0 +} + +struct Coord { + x int + y int + z int +} + +fn test_array_struct_contains() { + mut coords := []Coord{} + coord_1 := Coord{ + x: 1 + y: 2 + z: -1 + } + coords << coord_1 + exists := coord_1 in coords + not_exists := coord_1 !in coords + println('`exists`: $exists and `not exists`: $not_exists') + assert exists == true + assert not_exists == false +} + +fn test_array_struct_ref_contains() { + mut coords := []&Coord{} + coord_1 := &Coord{ + x: 1 + y: 2 + z: -1 + } + coords << coord_1 + exists := coord_1 in coords + println(exists) + assert exists == true +} + +fn test_array_struct_ref_index() { + mut coords := []&Coord{} + coord_1 := &Coord{ + x: 1 + y: 2 + z: -1 + } + coords << coord_1 + println(coords.index(coord_1)) + assert coords.index(coord_1) == 0 +} + +fn test_array_of_array_append() { + mut x := [][]int{len: 4} + println(x) // OK + x[2] << 123 // RTE + println(x) + assert '$x' == '[[], [], [123], []]' +} + +fn test_array_of_map_insert() { + mut x := []map[string]int{len: 4} + println(x) // OK + x[2]['123'] = 123 // RTE + println(x) + assert '$x' == "[{}, {}, {'123': 123}, {}]" +} + +fn test_multi_fixed_array_init() { + a := [3][3]int{} + assert '$a' == '[[0, 0, 0], [0, 0, 0], [0, 0, 0]]' +} + +struct Numbers { + odds []int + evens []int +} + +fn test_array_of_multi_filter() { + arr := [1, 2, 3, 4, 5] + nums := Numbers{ + odds: arr.filter(it % 2 == 1) + evens: arr.filter(it % 2 == 0) + } + println(nums) + assert nums.odds == [1, 3, 5] + assert nums.evens == [2, 4] +} + +fn test_array_of_multi_map() { + arr := [1, 3, 5] + nums := Numbers{ + odds: arr.map(it + 2) + evens: arr.map(it * 2) + } + println(nums) + assert nums.odds == [3, 5, 7] + assert nums.evens == [2, 6, 10] +} + +fn test_multi_fixed_array_with_default_init() { + a := [3][3]int{init: [3]int{init: 10}} + println(a) + assert a == [[10, 10, 10]!, [10, 10, 10]!, [10, 10, 10]!]! +} + +struct Abc { +mut: + x i64 + y i64 + z i64 +} + +fn test_clone_of_same_elem_size_array() { + mut arr := []Abc{} + arr << Abc{1, 2, 3} + arr << Abc{2, 3, 4} + arr2 := arr.clone() + println(arr2) + assert arr2 == [Abc{1, 2, 3}, Abc{2, 3, 4}] +} + +pub fn example(mut arr []T) []T { + return arr.clone() +} + +fn test_generic_mutable_arrays() { + mut arr := [1, 2, 3] + assert example(mut arr) == [1, 2, 3] +} diff --git a/v_windows/v/old/vlib/builtin/builtin.c.v b/v_windows/v/old/vlib/builtin/builtin.c.v new file mode 100644 index 0000000..0b0ca80 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/builtin.c.v @@ -0,0 +1,527 @@ +module builtin + +type FnExitCb = fn () + +fn C.atexit(f FnExitCb) int +fn C.strerror(int) &char + +[noreturn] +fn vhalt() { + for {} +} + +// exit terminates execution immediately and returns exit `code` to the shell. +[noreturn] +pub fn exit(code int) { + C.exit(code) +} + +fn vcommithash() string { + return unsafe { tos5(&char(C.V_CURRENT_COMMIT_HASH)) } +} + +// panic_debug private function that V uses for panics, -cg/-g is passed +// recent versions of tcc print nicer backtraces automatically +// NB: the duplication here is because tcc_backtrace should be called directly +// inside the panic functions. +[noreturn] +fn panic_debug(line_no int, file string, mod string, fn_name string, s string) { + // NB: the order here is important for a stabler test output + // module is less likely to change than function, etc... + // During edits, the line number will change most frequently, + // so it is last + $if freestanding { + bare_panic(s) + } $else { + eprintln('================ V panic ================') + eprintln(' module: $mod') + eprintln(' function: ${fn_name}()') + eprintln(' message: $s') + eprintln(' file: $file:$line_no') + eprintln(' v hash: $vcommithash()') + eprintln('=========================================') + $if exit_after_panic_message ? { + C.exit(1) + } $else $if no_backtrace ? { + C.exit(1) + } $else { + $if tinyc { + $if panics_break_into_debugger ? { + break_if_debugger_attached() + } $else { + C.tcc_backtrace(c'Backtrace') + } + C.exit(1) + } + print_backtrace_skipping_top_frames(1) + $if panics_break_into_debugger ? { + break_if_debugger_attached() + } + C.exit(1) + } + } + vhalt() +} + +[noreturn] +pub fn panic_optional_not_set(s string) { + panic('optional not set ($s)') +} + +// panic prints a nice error message, then exits the process with exit code of 1. +// It also shows a backtrace on most platforms. +[noreturn] +pub fn panic(s string) { + $if freestanding { + bare_panic(s) + } $else { + eprint('V panic: ') + eprintln(s) + eprintln('v hash: $vcommithash()') + $if exit_after_panic_message ? { + C.exit(1) + } $else $if no_backtrace ? { + C.exit(1) + } $else { + $if tinyc { + $if panics_break_into_debugger ? { + break_if_debugger_attached() + } $else { + C.tcc_backtrace(c'Backtrace') + } + C.exit(1) + } + print_backtrace_skipping_top_frames(1) + $if panics_break_into_debugger ? { + break_if_debugger_attached() + } + C.exit(1) + } + } + vhalt() +} + +// return a C-API error message matching to `errnum` +pub fn c_error_number_str(errnum int) string { + mut err_msg := '' + $if freestanding { + err_msg = 'error $errnum' + } $else { + $if !vinix { + c_msg := C.strerror(errnum) + err_msg = string{ + str: &byte(c_msg) + len: unsafe { C.strlen(c_msg) } + is_lit: 1 + } + } + } + return err_msg +} + +// panic with a C-API error message matching `errnum` +[noreturn] +pub fn panic_error_number(basestr string, errnum int) { + panic(basestr + c_error_number_str(errnum)) +} + +// eprintln prints a message with a line end, to stderr. Both stderr and stdout are flushed. +pub fn eprintln(s string) { + if s.str == 0 { + eprintln('eprintln(NIL)') + return + } + $if freestanding { + // flushing is only a thing with C.FILE from stdio.h, not on the syscall level + bare_eprint(s.str, u64(s.len)) + bare_eprint(c'\n', 1) + } $else $if ios { + C.WrappedNSLog(s.str) + } $else { + C.fflush(C.stdout) + C.fflush(C.stderr) + // eprintln is used in panics, so it should not fail at all + $if android { + C.fprintf(C.stderr, c'%.*s\n', s.len, s.str) + } + _writeln_to_fd(2, s) + C.fflush(C.stderr) + } +} + +// eprint prints a message to stderr. Both stderr and stdout are flushed. +pub fn eprint(s string) { + if s.str == 0 { + eprint('eprint(NIL)') + return + } + $if freestanding { + // flushing is only a thing with C.FILE from stdio.h, not on the syscall level + bare_eprint(s.str, u64(s.len)) + } $else $if ios { + // TODO: Implement a buffer as NSLog doesn't have a "print" + C.WrappedNSLog(s.str) + } $else { + C.fflush(C.stdout) + C.fflush(C.stderr) + $if android { + C.fprintf(C.stderr, c'%.*s', s.len, s.str) + } + _write_buf_to_fd(2, s.str, s.len) + C.fflush(C.stderr) + } +} + +// print prints a message to stdout. Unlike `println` stdout is not automatically flushed. +// A call to `flush()` will flush the output buffer to stdout. +[manualfree] +pub fn print(s string) { + $if android { + C.fprintf(C.stdout, c'%.*s', s.len, s.str) // logcat + } + // no else if for android termux support + $if ios { + // TODO: Implement a buffer as NSLog doesn't have a "print" + C.WrappedNSLog(s.str) + } $else $if freestanding { + bare_print(s.str, u64(s.len)) + } $else { + _write_buf_to_fd(1, s.str, s.len) + } +} + +// println prints a message with a line end, to stdout. stdout is flushed. +[manualfree] +pub fn println(s string) { + if s.str == 0 { + println('println(NIL)') + return + } + $if android { + C.fprintf(C.stdout, c'%.*s\n', s.len, s.str) // logcat + return + } + // no else if for android termux support + $if ios { + C.WrappedNSLog(s.str) + return + } $else $if freestanding { + bare_print(s.str, u64(s.len)) + bare_print(c'\n', 1) + return + } $else { + _writeln_to_fd(1, s) + } +} + +[manualfree] +fn _writeln_to_fd(fd int, s string) { + unsafe { + buf_len := s.len + 1 // space for \n + mut buf := malloc(buf_len) + defer { + free(buf) + } + C.memcpy(buf, s.str, s.len) + buf[s.len] = `\n` + _write_buf_to_fd(fd, buf, buf_len) + } +} + +[manualfree] +fn _write_buf_to_fd(fd int, buf &byte, buf_len int) { + if buf_len <= 0 { + return + } + unsafe { + mut ptr := buf + mut remaining_bytes := buf_len + for remaining_bytes > 0 { + x := C.write(fd, ptr, remaining_bytes) + ptr += x + remaining_bytes -= x + } + } +} + +__global total_m = i64(0) +// malloc dynamically allocates a `n` bytes block of memory on the heap. +// malloc returns a `byteptr` pointing to the memory address of the allocated space. +// unlike the `calloc` family of functions - malloc will not zero the memory block. +[unsafe] +pub fn malloc(n int) &byte { + if n <= 0 { + panic('> V malloc(<=0)') + } + $if vplayground ? { + if n > 10000 { + panic('allocating more than 10 KB at once is not allowed in the V playground') + } + if total_m > 50 * 1024 * 1024 { + panic('allocating more than 50 MB is not allowed in the V playground') + } + } + $if trace_malloc ? { + total_m += n + C.fprintf(C.stderr, c'_v_malloc %6d total %10d\n', n, total_m) + // print_backtrace() + } + mut res := &byte(0) + $if prealloc { + return unsafe { prealloc_malloc(n) } + } $else $if gcboehm ? { + unsafe { + res = C.GC_MALLOC(n) + } + } $else $if freestanding { + mut e := Errno{} + res, e = mm_alloc(u64(n)) + if e != .enoerror { + eprint('malloc() failed: ') + eprintln(e.str()) + panic('malloc() failed') + } + } $else { + res = unsafe { C.malloc(n) } + } + if res == 0 { + panic('malloc($n) failed') + } + $if debug_malloc ? { + // Fill in the memory with something != 0, so it is easier to spot + // when the calling code wrongly relies on it being zeroed. + unsafe { C.memset(res, 0x88, n) } + } + return res +} + +[unsafe] +pub fn malloc_noscan(n int) &byte { + if n <= 0 { + panic('> V malloc(<=0)') + } + $if vplayground ? { + if n > 10000 { + panic('allocating more than 10 KB at once is not allowed in the V playground') + } + if total_m > 50 * 1024 * 1024 { + panic('allocating more than 50 MB is not allowed in the V playground') + } + } + $if trace_malloc ? { + total_m += n + C.fprintf(C.stderr, c'_v_malloc %6d total %10d\n', n, total_m) + // print_backtrace() + } + mut res := &byte(0) + $if prealloc { + return unsafe { prealloc_malloc(n) } + } $else $if gcboehm ? { + $if gcboehm_opt ? { + unsafe { + res = C.GC_MALLOC_ATOMIC(n) + } + } $else { + unsafe { + res = C.GC_MALLOC(n) + } + } + } $else $if freestanding { + mut e := Errno{} + res, e = mm_alloc(u64(n)) + if e != .enoerror { + eprint('malloc() failed: ') + eprintln(e.str()) + panic('malloc() failed') + } + } $else { + res = unsafe { C.malloc(n) } + } + if res == 0 { + panic('malloc($n) failed') + } + $if debug_malloc ? { + // Fill in the memory with something != 0, so it is easier to spot + // when the calling code wrongly relies on it being zeroed. + unsafe { C.memset(res, 0x88, n) } + } + return res +} + +// v_realloc resizes the memory block `b` with `n` bytes. +// The `b byteptr` must be a pointer to an existing memory block +// previously allocated with `malloc`, `v_calloc` or `vcalloc`. +// Please, see also realloc_data, and use it instead if possible. +[unsafe] +pub fn v_realloc(b &byte, n int) &byte { + $if trace_realloc ? { + C.fprintf(C.stderr, c'v_realloc %6d\n', n) + } + mut new_ptr := &byte(0) + $if prealloc { + unsafe { + new_ptr = malloc(n) + C.memcpy(new_ptr, b, n) + } + return new_ptr + } $else $if gcboehm ? { + new_ptr = unsafe { C.GC_REALLOC(b, n) } + } $else { + new_ptr = unsafe { C.realloc(b, n) } + } + if new_ptr == 0 { + panic('realloc($n) failed') + } + return new_ptr +} + +// realloc_data resizes the memory block pointed by `old_data` to `new_size` +// bytes. `old_data` must be a pointer to an existing memory block, previously +// allocated with `malloc`, `v_calloc` or `vcalloc`, of size `old_data`. +// realloc_data returns a pointer to the new location of the block. +// NB: if you know the old data size, it is preferable to call `realloc_data`, +// instead of `v_realloc`, at least during development, because `realloc_data` +// can make debugging easier, when you compile your program with +// `-d debug_realloc`. +[unsafe] +pub fn realloc_data(old_data &byte, old_size int, new_size int) &byte { + $if trace_realloc ? { + C.fprintf(C.stderr, c'realloc_data old_size: %6d new_size: %6d\n', old_size, new_size) + } + $if prealloc { + return unsafe { prealloc_realloc(old_data, old_size, new_size) } + } + $if debug_realloc ? { + // NB: this is slower, but helps debugging memory problems. + // The main idea is to always force reallocating: + // 1) allocate a new memory block + // 2) copy the old to the new + // 3) fill the old with 0x57 (`W`) + // 4) free the old block + // => if there is still a pointer to the old block somewhere + // it will point to memory that is now filled with 0x57. + unsafe { + new_ptr := malloc(new_size) + min_size := if old_size < new_size { old_size } else { new_size } + C.memcpy(new_ptr, old_data, min_size) + C.memset(old_data, 0x57, old_size) + free(old_data) + return new_ptr + } + } + mut nptr := &byte(0) + $if gcboehm ? { + nptr = unsafe { C.GC_REALLOC(old_data, new_size) } + } $else { + nptr = unsafe { C.realloc(old_data, new_size) } + } + if nptr == 0 { + panic('realloc_data($old_data, $old_size, $new_size) failed') + } + return nptr +} + +// vcalloc dynamically allocates a zeroed `n` bytes block of memory on the heap. +// vcalloc returns a `byteptr` pointing to the memory address of the allocated space. +// Unlike `v_calloc` vcalloc checks for negative values given in `n`. +pub fn vcalloc(n int) &byte { + if n < 0 { + panic('calloc(<0)') + } else if n == 0 { + return &byte(0) + } + $if trace_vcalloc ? { + total_m += n + C.fprintf(C.stderr, c'vcalloc %6d total %10d\n', n, total_m) + } + $if prealloc { + return unsafe { prealloc_calloc(n) } + } $else $if gcboehm ? { + return unsafe { &byte(C.GC_MALLOC(n)) } + } $else { + return unsafe { C.calloc(1, n) } + } +} + +// special versions of the above that allocate memory which is not scanned +// for pointers (but is collected) when the Boehm garbage collection is used +pub fn vcalloc_noscan(n int) &byte { + $if trace_vcalloc ? { + total_m += n + C.fprintf(C.stderr, c'vcalloc_noscan %6d total %10d\n', n, total_m) + } + $if prealloc { + return unsafe { prealloc_calloc(n) } + } $else $if gcboehm ? { + $if vplayground ? { + if n > 10000 { + panic('allocating more than 10 KB is not allowed in the playground') + } + } + if n < 0 { + panic('calloc(<0)') + } + return $if gcboehm_opt ? { + unsafe { &byte(C.memset(C.GC_MALLOC_ATOMIC(n), 0, n)) } + } $else { + unsafe { &byte(C.GC_MALLOC(n)) } + } + } $else { + return unsafe { vcalloc(n) } + } +} + +// free allows for manually freeing memory allocated at the address `ptr`. +[unsafe] +pub fn free(ptr voidptr) { + $if prealloc { + return + } $else $if gcboehm ? { + // It is generally better to leave it to Boehm's gc to free things. + // Calling C.GC_FREE(ptr) was tried initially, but does not work + // well with programs that do manual management themselves. + // + // The exception is doing leak detection for manual memory management: + $if gcboehm_leak ? { + unsafe { C.GC_FREE(ptr) } + } + } $else { + C.free(ptr) + } +} + +// memdup dynamically allocates a `sz` bytes block of memory on the heap +// memdup then copies the contents of `src` into the allocated space and +// returns a pointer to the newly allocated space. +[unsafe] +pub fn memdup(src voidptr, sz int) voidptr { + if sz == 0 { + return vcalloc(1) + } + unsafe { + mem := malloc(sz) + return C.memcpy(mem, src, sz) + } +} + +[unsafe] +pub fn memdup_noscan(src voidptr, sz int) voidptr { + if sz == 0 { + return vcalloc_noscan(1) + } + unsafe { + mem := vcalloc_noscan(sz) + return C.memcpy(mem, src, sz) + } +} + +[inline] +fn v_fixed_index(i int, len int) int { + $if !no_bounds_checking ? { + if i < 0 || i >= len { + s := 'fixed array index out of range (index: $i, len: $len)' + panic(s) + } + } + return i +} diff --git a/v_windows/v/old/vlib/builtin/builtin.v b/v_windows/v/old/vlib/builtin/builtin.v new file mode 100644 index 0000000..d3a1474 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/builtin.v @@ -0,0 +1,131 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module builtin + +// isnil returns true if an object is nil (only for C objects). +[inline] +pub fn isnil(v voidptr) bool { + return v == 0 +} + +/* +fn on_panic(f fn(int)int) { + // TODO +} +*/ + +// print_backtrace shows a backtrace of the current call stack on stdout +pub fn print_backtrace() { + // At the time of backtrace_symbols_fd call, the C stack would look something like this: + // * print_backtrace_skipping_top_frames + // * print_backtrace itself + // * the rest of the backtrace frames + // => top 2 frames should be skipped, since they will not be informative to the developer + $if !no_backtrace ? { + $if freestanding { + println(bare_backtrace()) + } $else { + $if tinyc { + C.tcc_backtrace(c'Backtrace') + } $else { + print_backtrace_skipping_top_frames(2) + } + } + } +} + +struct VCastTypeIndexName { + tindex int + tname string +} + +// will be filled in cgen +__global as_cast_type_indexes []VCastTypeIndexName + +fn __as_cast(obj voidptr, obj_type int, expected_type int) voidptr { + if obj_type != expected_type { + mut obj_name := as_cast_type_indexes[0].tname.clone() + mut expected_name := as_cast_type_indexes[0].tname.clone() + for x in as_cast_type_indexes { + if x.tindex == obj_type { + obj_name = x.tname.clone() + } + if x.tindex == expected_type { + expected_name = x.tname.clone() + } + } + panic('as cast: cannot cast `$obj_name` to `$expected_name`') + } + return obj +} + +// VAssertMetaInfo is used during assertions. An instance of it +// is filled in by compile time generated code, when an assertion fails. +pub struct VAssertMetaInfo { +pub: + fpath string // the source file path of the assertion + line_nr int // the line number of the assertion + fn_name string // the function name in which the assertion is + src string // the actual source line of the assertion + op string // the operation of the assertion, i.e. '==', '<', 'call', etc ... + llabel string // the left side of the infix expressions as source + rlabel string // the right side of the infix expressions as source + lvalue string // the stringified *actual value* of the left side of a failed assertion + rvalue string // the stringified *actual value* of the right side of a failed assertion +} + +fn __print_assert_failure(i &VAssertMetaInfo) { + eprintln('$i.fpath:${i.line_nr + 1}: FAIL: fn $i.fn_name: assert $i.src') + if i.op.len > 0 && i.op != 'call' { + eprintln(' left value: $i.llabel = $i.lvalue') + if i.rlabel == i.rvalue { + eprintln(' right value: $i.rlabel') + } else { + eprintln(' right value: $i.rlabel = $i.rvalue') + } + } +} + +// MethodArgs holds type information for function and/or method arguments. +pub struct MethodArgs { +pub: + typ int + name string +} + +// FunctionData holds information about a parsed function. +pub struct FunctionData { +pub: + name string + attrs []string + args []MethodArgs + return_type int + typ int +} + +// FieldData holds information about a field. Fields reside on structs. +pub struct FieldData { +pub: + name string + attrs []string + is_pub bool + is_mut bool + is_shared bool + typ int +} + +pub enum AttributeKind { + plain // [name] + string // ['name'] + number // [123] + comptime_define // [if name] +} + +pub struct StructAttribute { +pub: + name string + has_arg bool + arg string + kind AttributeKind +} diff --git a/v_windows/v/old/vlib/builtin/builtin_d_gcboehm.v b/v_windows/v/old/vlib/builtin/builtin_d_gcboehm.v new file mode 100644 index 0000000..befec1d --- /dev/null +++ b/v_windows/v/old/vlib/builtin/builtin_d_gcboehm.v @@ -0,0 +1,91 @@ +module builtin + +#flag -DGC_THREADS=1 + +$if static_boehm ? { + $if macos { + #flag -I$first_existing("/opt/homebrew/include", "/usr/local/include") + #flag $first_existing("/opt/homebrew/lib/libgc.a", "/usr/local/lib/libgc.a") + } $else $if linux { + #flag -l:libgc.a + } $else $if openbsd { + #flag -I/usr/local/include + #flag /usr/local/lib/libgc.a + #flag -lpthread + } $else $if windows { + #flag -DGC_NOT_DLL=1 + $if tinyc { + #flag -I@VEXEROOT/thirdparty/libgc/include + #flag -L@VEXEROOT/thirdparty/libgc + #flag -lgc + } $else { + #flag -DGC_BUILTIN_ATOMIC=1 + #flag -I@VEXEROOT/thirdparty/libgc + #flag @VEXEROOT/thirdparty/libgc/gc.o + } + } $else { + #flag -lgc + } +} $else { + $if macos { + #pkgconfig bdw-gc + } $else $if openbsd || freebsd { + #flag -I/usr/local/include + #flag -L/usr/local/lib + } + $if windows { + $if tinyc { + #flag -I@VEXEROOT/thirdparty/libgc/include + #flag -L@VEXEROOT/thirdparty/libgc + #flag -lgc + } $else { + #flag -DGC_BUILTIN_ATOMIC=1 + #flag -I@VEXEROOT/thirdparty/libgc + #flag @VEXEROOT/thirdparty/libgc/gc.o + } + } $else { + #flag -lgc + } +} + +$if gcboehm_leak ? { + #flag -DGC_DEBUG=1 +} + +#include + +// replacements for `malloc()/calloc()`, `realloc()` and `free()` +// for use with Boehm-GC +// Do not use them manually. They are automatically chosen when +// compiled with `-gc boehm` or `-gc boehm_leak`. +fn C.GC_MALLOC(n size_t) voidptr + +fn C.GC_MALLOC_ATOMIC(n size_t) voidptr + +fn C.GC_MALLOC_UNCOLLECTABLE(n size_t) voidptr + +fn C.GC_REALLOC(ptr voidptr, n size_t) voidptr + +fn C.GC_FREE(ptr voidptr) + +// explicitely perform garbage collection now! Garbage collections +// are done automatically when needed, so this function is hardly needed +fn C.GC_gcollect() + +// functions to temporarily suspend/resume garbage collection +fn C.GC_disable() + +fn C.GC_enable() + +// returns non-zero if GC is disabled +fn C.GC_is_disabled() int + +// protect memory block from being freed before this call +fn C.GC_reachable_here(voidptr) + +// for leak detection it is advisable to do explicit garbage collections +pub fn gc_check_leaks() { + $if gcboehm_leak ? { + C.GC_gcollect() + } +} diff --git a/v_windows/v/old/vlib/builtin/builtin_ios.c.v b/v_windows/v/old/vlib/builtin/builtin_ios.c.v new file mode 100644 index 0000000..f745b4d --- /dev/null +++ b/v_windows/v/old/vlib/builtin/builtin_ios.c.v @@ -0,0 +1,6 @@ +module builtin + +// TODO: Remove this later, added to make sure v self works +$if ios { + #include "@VEXEROOT/thirdparty/ios/ios.m" +} diff --git a/v_windows/v/old/vlib/builtin/builtin_nix.c.v b/v_windows/v/old/vlib/builtin/builtin_nix.c.v new file mode 100644 index 0000000..0d79af4 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/builtin_nix.c.v @@ -0,0 +1,144 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module builtin + +// pub fn vsyscall(id int +// + +/* +pub const ( + sys_write = 1 + sys_mkdir = 83 +) +const ( + stdin_value = 0 + stdout_value = 1 + stderr_value = 2 +) +*/ + +fn builtin_init() { + // Do nothing +} + +fn print_backtrace_skipping_top_frames(xskipframes int) bool { + $if no_backtrace ? { + return false + } $else { + skipframes := xskipframes + 2 + $if macos || freebsd || openbsd || netbsd { + return print_backtrace_skipping_top_frames_bsd(skipframes) + } $else $if linux { + return print_backtrace_skipping_top_frames_linux(skipframes) + } $else { + println('print_backtrace_skipping_top_frames is not implemented. skipframes: $skipframes') + } + } + return false +} + +// the functions below are not called outside this file, +// so there is no need to have their twins in builtin_windows.v +fn print_backtrace_skipping_top_frames_bsd(skipframes int) bool { + $if no_backtrace ? { + return false + } $else { + $if macos || freebsd || openbsd || netbsd { + buffer := [100]voidptr{} + nr_ptrs := C.backtrace(&buffer[0], 100) + if nr_ptrs < 2 { + eprintln('C.backtrace returned less than 2 frames') + return false + } + C.backtrace_symbols_fd(&buffer[skipframes], nr_ptrs - skipframes, 2) + } + return true + } +} + +fn C.tcc_backtrace(fmt &char) int +fn print_backtrace_skipping_top_frames_linux(skipframes int) bool { + $if android { + eprintln('On Android no backtrace is available.') + return false + } + $if !glibc { + eprintln('backtrace_symbols is missing => printing backtraces is not available.') + eprintln('Some libc implementations like musl simply do not provide it.') + return false + } + $if no_backtrace ? { + return false + } $else { + $if linux && !freestanding { + $if tinyc { + C.tcc_backtrace(c'Backtrace') + return false + } + buffer := [100]voidptr{} + nr_ptrs := C.backtrace(&buffer[0], 100) + if nr_ptrs < 2 { + eprintln('C.backtrace returned less than 2 frames') + return false + } + nr_actual_frames := nr_ptrs - skipframes + mut sframes := []string{} + //////csymbols := backtrace_symbols(*voidptr(&buffer[skipframes]), nr_actual_frames) + csymbols := C.backtrace_symbols(voidptr(&buffer[skipframes]), nr_actual_frames) + for i in 0 .. nr_actual_frames { + sframes << unsafe { tos2(&byte(csymbols[i])) } + } + for sframe in sframes { + executable := sframe.all_before('(') + addr := sframe.all_after('[').all_before(']') + beforeaddr := sframe.all_before('[') + cmd := 'addr2line -e $executable $addr' + // taken from os, to avoid depending on the os module inside builtin.v + f := C.popen(&char(cmd.str), c'r') + if isnil(f) { + eprintln(sframe) + continue + } + buf := [1000]byte{} + mut output := '' + unsafe { + bp := &buf[0] + for C.fgets(&char(bp), 1000, f) != 0 { + output += tos(bp, vstrlen(bp)) + } + } + output = output.trim_space() + ':' + if C.pclose(f) != 0 { + eprintln(sframe) + continue + } + if output in ['??:0:', '??:?:'] { + output = '' + } + // See http://wiki.dwarfstd.org/index.php?title=Path_Discriminators + // NB: it is shortened here to just d. , just so that it fits, and so + // that the common error file:lineno: line format is enforced. + output = output.replace(' (discriminator', ': (d.') + eprintln('${output:-55s} | ${addr:14s} | $beforeaddr') + } + } + } + return true +} + +fn break_if_debugger_attached() { + unsafe { + mut ptr := &voidptr(0) + *ptr = voidptr(0) + _ = ptr + } +} + +// These functions are Windows specific - provide dummys for *nix +pub fn winapi_lasterr_str() string { + return '' +} + +[noreturn] +pub fn panic_lasterr() {} diff --git a/v_windows/v/old/vlib/builtin/builtin_notd_gcboehm.v b/v_windows/v/old/vlib/builtin/builtin_notd_gcboehm.v new file mode 100644 index 0000000..4479072 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/builtin_notd_gcboehm.v @@ -0,0 +1,20 @@ +module builtin + +// Just define the C functions, so that V does not error because of the missing definitions. + +// NB: they will NOT be used, since calls to them are wrapped with `$if gcboehm ? { }` + +fn C.GC_MALLOC(n size_t) voidptr + +fn C.GC_MALLOC_ATOMIC(n size_t) voidptr + +fn C.GC_MALLOC_UNCOLLECTABLE(n size_t) voidptr + +fn C.GC_REALLOC(ptr voidptr, n size_t) voidptr + +fn C.GC_FREE(ptr voidptr) + +// provide an empty function when manual memory management is used +// to simplify leak detection +// +pub fn gc_check_leaks() {} diff --git a/v_windows/v/old/vlib/builtin/builtin_windows.c.v b/v_windows/v/old/vlib/builtin/builtin_windows.c.v new file mode 100644 index 0000000..97592cf --- /dev/null +++ b/v_windows/v/old/vlib/builtin/builtin_windows.c.v @@ -0,0 +1,304 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module builtin + +// dbghelp.h is already included in cheaders.v +#flag windows -l dbghelp +// SymbolInfo is used by print_backtrace_skipping_top_frames_msvc +pub struct SymbolInfo { +pub mut: + f_size_of_struct u32 // must be 88 to be recognised by SymFromAddr + f_type_index u32 // Type Index of symbol + f_reserved [2]u64 + f_index u32 + f_size u32 + f_mod_base u64 // Base Address of module comtaining this symbol + f_flags u32 + f_value u64 // Value of symbol, ValuePresent should be 1 + f_address u64 // Address of symbol including base address of module + f_register u32 // register holding value or pointer to value + f_scope u32 // scope of the symbol + f_tag u32 // pdb classification + f_name_len u32 // Actual length of name + f_max_name_len u32 // must be manually set + f_name byte // must be calloc(f_max_name_len) +} + +pub struct SymbolInfoContainer { +pub mut: + syminfo SymbolInfo + f_name_rest [254]char +} + +pub struct Line64 { +pub mut: + f_size_of_struct u32 + f_key voidptr + f_line_number u32 + f_file_name &byte + f_address u64 +} + +// returns the current options mask +fn C.SymSetOptions(symoptions u32) u32 + +// returns handle +fn C.GetCurrentProcess() voidptr + +fn C.SymInitialize(h_process voidptr, p_user_search_path &byte, b_invade_process int) int + +fn C.CaptureStackBackTrace(frames_to_skip u32, frames_to_capture u32, p_backtrace voidptr, p_backtrace_hash voidptr) u16 + +fn C.SymFromAddr(h_process voidptr, address u64, p_displacement voidptr, p_symbol voidptr) int + +fn C.SymGetLineFromAddr64(h_process voidptr, address u64, p_displacement voidptr, p_line &Line64) int + +// Ref - https://docs.microsoft.com/en-us/windows/win32/api/dbghelp/nf-dbghelp-symsetoptions +const ( + symopt_undname = 0x00000002 + symopt_deferred_loads = 0x00000004 + symopt_no_cpp = 0x00000008 + symopt_load_lines = 0x00000010 + symopt_include_32bit_modules = 0x00002000 + symopt_allow_zero_address = 0x01000000 + symopt_debug = 0x80000000 +) + +// g_original_codepage - used to restore the original windows console code page when exiting +__global ( + g_original_codepage = u32(0) +) + +// utf8 to stdout needs C.SetConsoleOutputCP(C.CP_UTF8) +fn C.GetConsoleOutputCP() u32 + +fn C.SetConsoleOutputCP(wCodePageID u32) bool + +fn restore_codepage() { + C.SetConsoleOutputCP(g_original_codepage) +} + +fn is_terminal(fd int) int { + mut mode := u32(0) + osfh := voidptr(C._get_osfhandle(fd)) + C.GetConsoleMode(osfh, voidptr(&mode)) + return int(mode) +} + +fn builtin_init() { + g_original_codepage = C.GetConsoleOutputCP() + C.SetConsoleOutputCP(C.CP_UTF8) + C.atexit(restore_codepage) + if is_terminal(1) > 0 { + C.SetConsoleMode(C.GetStdHandle(C.STD_OUTPUT_HANDLE), C.ENABLE_PROCESSED_OUTPUT | C.ENABLE_WRAP_AT_EOL_OUTPUT | 0x0004) // enable_virtual_terminal_processing + C.SetConsoleMode(C.GetStdHandle(C.STD_ERROR_HANDLE), C.ENABLE_PROCESSED_OUTPUT | C.ENABLE_WRAP_AT_EOL_OUTPUT | 0x0004) // enable_virtual_terminal_processing + unsafe { + C.setbuf(C.stdout, 0) + C.setbuf(C.stderr, 0) + } + } + $if !no_backtrace ? { + add_unhandled_exception_handler() + } +} + +fn print_backtrace_skipping_top_frames(skipframes int) bool { + $if msvc { + return print_backtrace_skipping_top_frames_msvc(skipframes) + } + $if tinyc { + return print_backtrace_skipping_top_frames_tcc(skipframes) + } + $if mingw { + return print_backtrace_skipping_top_frames_mingw(skipframes) + } + eprintln('print_backtrace_skipping_top_frames is not implemented') + return false +} + +fn print_backtrace_skipping_top_frames_msvc(skipframes int) bool { + $if msvc { + mut offset := u64(0) + backtraces := [100]voidptr{} + sic := SymbolInfoContainer{} + mut si := &sic.syminfo + si.f_size_of_struct = sizeof(SymbolInfo) // Note: C.SYMBOL_INFO is 88 + si.f_max_name_len = sizeof(SymbolInfoContainer) - sizeof(SymbolInfo) - 1 + fname := &char(&si.f_name) + mut sline64 := Line64{ + f_file_name: &byte(0) + } + sline64.f_size_of_struct = sizeof(Line64) + + handle := C.GetCurrentProcess() + defer { + C.SymCleanup(handle) + } + + C.SymSetOptions(symopt_debug | symopt_load_lines | symopt_undname) + + syminitok := C.SymInitialize(handle, 0, 1) + if syminitok != 1 { + eprintln('Failed getting process: Aborting backtrace.\n') + return false + } + + frames := int(C.CaptureStackBackTrace(skipframes + 1, 100, &backtraces[0], 0)) + if frames < 2 { + eprintln('C.CaptureStackBackTrace returned less than 2 frames') + return false + } + for i in 0 .. frames { + frame_addr := backtraces[i] + if C.SymFromAddr(handle, frame_addr, &offset, si) == 1 { + nframe := frames - i - 1 + mut lineinfo := '' + if C.SymGetLineFromAddr64(handle, frame_addr, &offset, &sline64) == 1 { + file_name := unsafe { tos3(sline64.f_file_name) } + lnumber := sline64.f_line_number + lineinfo = '$file_name:$lnumber' + } else { + addr: + lineinfo = '?? : address = 0x${(&frame_addr):x}' + } + sfunc := unsafe { tos3(fname) } + eprintln('${nframe:-2d}: ${sfunc:-25s} $lineinfo') + } else { + // https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes + cerr := int(C.GetLastError()) + if cerr == 87 { + eprintln('SymFromAddr failure: $cerr = The parameter is incorrect)') + } else if cerr == 487 { + // probably caused because the .pdb isn't in the executable folder + eprintln('SymFromAddr failure: $cerr = Attempt to access invalid address (Verify that you have the .pdb file in the right folder.)') + } else { + eprintln('SymFromAddr failure: $cerr (see https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes)') + } + } + } + return true + } $else { + eprintln('print_backtrace_skipping_top_frames_msvc must be called only when the compiler is msvc') + return false + } +} + +fn print_backtrace_skipping_top_frames_mingw(skipframes int) bool { + eprintln('print_backtrace_skipping_top_frames_mingw is not implemented') + return false +} + +fn C.tcc_backtrace(fmt &char) int + +fn print_backtrace_skipping_top_frames_tcc(skipframes int) bool { + $if tinyc { + $if no_backtrace ? { + eprintln('backtraces are disabled') + return false + } $else { + C.tcc_backtrace(c'Backtrace') + return true + } + } $else { + eprintln('print_backtrace_skipping_top_frames_tcc must be called only when the compiler is tcc') + return false + } + // Not reachable, but it looks like it's not detectable by V + return false +} + +// TODO copypaste from os +// we want to be able to use this here without having to `import os` +struct ExceptionRecord { +pub: + // status_ constants + code u32 + flags u32 + record &ExceptionRecord + address voidptr + param_count u32 + // params []voidptr +} + +struct ContextRecord { + // TODO +} + +struct ExceptionPointers { +pub: + exception_record &ExceptionRecord + context_record &ContextRecord +} + +type VectoredExceptionHandler = fn (&ExceptionPointers) int + +fn C.AddVectoredExceptionHandler(int, C.PVECTORED_EXCEPTION_HANDLER) + +fn add_vectored_exception_handler(handler VectoredExceptionHandler) { + C.AddVectoredExceptionHandler(1, C.PVECTORED_EXCEPTION_HANDLER(handler)) +} + +[windows_stdcall] +fn unhandled_exception_handler(e &ExceptionPointers) int { + match e.exception_record.code { + // These are 'used' by the backtrace printer + // so we dont want to catch them... + 0x4001000A, 0x40010006 { + return 0 + } + else { + println('Unhandled Exception 0x${e.exception_record.code:X}') + print_backtrace_skipping_top_frames(5) + } + } + + return 0 +} + +fn add_unhandled_exception_handler() { + add_vectored_exception_handler(VectoredExceptionHandler(voidptr(unhandled_exception_handler))) +} + +fn C.IsDebuggerPresent() bool + +fn C.__debugbreak() + +fn break_if_debugger_attached() { + $if tinyc { + unsafe { + mut ptr := &voidptr(0) + *ptr = voidptr(0) + _ = ptr + } + } $else { + if C.IsDebuggerPresent() { + C.__debugbreak() + } + } +} + +// return an error message generated from WinAPI's `LastError` +pub fn winapi_lasterr_str() string { + err_msg_id := C.GetLastError() + if err_msg_id == 8 { + // handle this case special since `FormatMessage()` might not work anymore + return 'insufficient memory' + } + mut msgbuf := &u16(0) + res := C.FormatMessage(C.FORMAT_MESSAGE_ALLOCATE_BUFFER | C.FORMAT_MESSAGE_FROM_SYSTEM | C.FORMAT_MESSAGE_IGNORE_INSERTS, + C.NULL, err_msg_id, C.MAKELANGID(C.LANG_NEUTRAL, C.SUBLANG_DEFAULT), &msgbuf, + 0, C.NULL) + err_msg := if res == 0 { + 'Win-API error $err_msg_id' + } else { + unsafe { string_from_wide(msgbuf) } + } + return err_msg +} + +// panic with an error message generated from WinAPI's `LastError` +[noreturn] +pub fn panic_lasterr(base string) { + panic(base + winapi_lasterr_str()) +} diff --git a/v_windows/v/old/vlib/builtin/byte_test.v b/v_windows/v/old/vlib/builtin/byte_test.v new file mode 100644 index 0000000..8b38eb8 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/byte_test.v @@ -0,0 +1,22 @@ +fn test_clone() { + a := [byte(0), 1, 2] + b := a.clone() + assert b.len == 3 + assert b[0] == 0 + assert b[1] == 1 + assert b[2] == 2 + assert b[1].str() == '1' + xx := byte(35) + assert xx.str() == '35' + assert xx.ascii_str() == '#' + println(typeof(`A`).name) + assert typeof(`A`).name == 'rune' + x := rune(`A`) + assert x.str() == 'A' + assert typeof(x).name == 'rune' + // + y := `Z` + assert typeof(y).name == 'rune' + assert y.str() == 'Z' + // assert b[1].str() == '1' TODO +} diff --git a/v_windows/v/old/vlib/builtin/cfns.c.v b/v_windows/v/old/vlib/builtin/cfns.c.v new file mode 100644 index 0000000..b67da34 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/cfns.c.v @@ -0,0 +1,462 @@ +module builtin + +// +fn C.memcpy(dest &byte, src &byte, n int) voidptr + +fn C.memcmp(&byte, &byte, int) int + +fn C.memmove(&byte, &byte, int) voidptr + +[trusted] +fn C.calloc(int, int) &byte + +fn C.malloc(int) &byte + +fn C.realloc(a &byte, b int) &byte + +fn C.free(ptr voidptr) + +[noreturn; trusted] +fn C.exit(code int) + +fn C.qsort(base voidptr, items size_t, item_size size_t, cb qsort_callback_func) + +fn C.sprintf(a ...voidptr) int + +fn C.strlen(s &char) int + +fn C.sscanf(&byte, &byte, ...&byte) int + +[trusted] +fn C.isdigit(c int) bool + +// stdio.h +fn C.popen(c &char, t &char) voidptr + +// +fn C.backtrace(a &voidptr, size int) int + +fn C.backtrace_symbols(a &voidptr, size int) &&char + +fn C.backtrace_symbols_fd(a &voidptr, size int, fd int) + +// +pub fn proc_pidpath(int, voidptr, int) int + +fn C.realpath(&char, &char) &char + +// fn C.chmod(byteptr, mode_t) int +fn C.chmod(&char, u32) int + +fn C.printf(&char, ...voidptr) int + +fn C.puts(&char) int + +fn C.fputs(str &char, stream &C.FILE) int + +fn C.fflush(&C.FILE) int + +// TODO define args in these functions +fn C.fseek(stream &C.FILE, offset int, whence int) int + +fn C.fopen(filename &char, mode &char) &C.FILE + +fn C.fileno(&C.FILE) int + +fn C.fread(ptr voidptr, item_size size_t, items size_t, stream &C.FILE) size_t + +fn C.fwrite(ptr voidptr, item_size size_t, items size_t, stream &C.FILE) size_t + +fn C.fclose(stream &C.FILE) int + +fn C.pclose(stream &C.FILE) int + +// process execution, os.process: +[trusted] +fn C.getpid() int + +fn C.getuid() int + +fn C.geteuid() int + +fn C.system(cmd &char) int + +fn C.posix_spawn(child_pid &int, path &char, file_actions voidptr, attrp voidptr, argv &&char, envp &&char) int + +fn C.posix_spawnp(child_pid &int, exefile &char, file_actions voidptr, attrp voidptr, argv &&char, envp &&char) int + +fn C.execve(cmd_path &char, args voidptr, envs voidptr) int + +fn C.execvp(cmd_path &char, args &&char) int + +fn C._execve(cmd_path &char, args voidptr, envs voidptr) int + +fn C._execvp(cmd_path &char, args &&char) int + +[trusted] +fn C.fork() int + +fn C.wait(status &int) int + +fn C.waitpid(pid int, status &int, options int) int + +[trusted] +fn C.kill(pid int, sig int) int + +fn C.setenv(&char, &char, int) int + +fn C.unsetenv(&char) int + +fn C.access(path &char, amode int) int + +fn C.remove(filename &char) int + +fn C.rmdir(path &char) int + +fn C.chdir(path &char) int + +fn C.rewind(stream &C.FILE) int + +fn C.stat(&char, voidptr) int + +fn C.lstat(path &char, buf &C.stat) int + +fn C.rename(old_filename &char, new_filename &char) int + +fn C.fgets(str &char, n int, stream &C.FILE) int + +fn C.memset(str voidptr, c int, n size_t) int + +[trusted] +fn C.sigemptyset() int + +fn C.getcwd(buf &char, size size_t) &char + +[trusted] +fn C.mktime() int + +fn C.gettimeofday(tv &C.timeval, tz &C.timezone) int + +[trusted] +fn C.sleep(seconds u32) u32 + +// fn C.usleep(usec useconds_t) int +[trusted] +fn C.usleep(usec u32) int + +fn C.opendir(&char) voidptr + +fn C.closedir(dirp &C.DIR) int + +// fn C.mkdir(path &char, mode mode_t) int +fn C.mkdir(path &char, mode u32) int + +// C.rand returns a pseudorandom integer from 0 (inclusive) to C.RAND_MAX (exclusive) +[trusted] +fn C.rand() int + +// C.srand seeds the internal PRNG with the given value. +[trusted] +fn C.srand(seed u32) + +fn C.atof(str &char) f64 + +[trusted] +fn C.tolower(c int) int + +[trusted] +fn C.toupper(c int) int + +[trusted] +fn C.getchar() int + +[trusted] +fn C.strerror(int) &char + +fn C.snprintf(str &char, size size_t, format &char, opt ...voidptr) int + +fn C.fprintf(voidptr, &char, ...voidptr) + +[trusted] +fn C.WIFEXITED(status int) bool + +[trusted] +fn C.WEXITSTATUS(status int) int + +[trusted] +fn C.WIFSIGNALED(status int) bool + +[trusted] +fn C.WTERMSIG(status int) int + +[trusted] +fn C.isatty(fd int) int + +fn C.syscall(number int, va ...voidptr) int + +fn C.sysctl(name &int, namelen u32, oldp voidptr, oldlenp voidptr, newp voidptr, newlen size_t) int + +[trusted] +fn C._fileno(int) int + +fn C._get_osfhandle(fd int) C.intptr_t + +fn C.GetModuleFileName(hModule voidptr, lpFilename &u16, nSize u32) int + +fn C.GetModuleFileNameW(hModule voidptr, lpFilename &u16, nSize u32) u32 + +fn C.CreateFile(lpFilename &u16, dwDesiredAccess u32, dwShareMode u32, lpSecurityAttributes &u16, dwCreationDisposition u32, dwFlagsAndAttributes u32, hTemplateFile voidptr) voidptr + +fn C.CreateFileW(lpFilename &u16, dwDesiredAccess u32, dwShareMode u32, lpSecurityAttributes &u16, dwCreationDisposition u32, dwFlagsAndAttributes u32, hTemplateFile voidptr) u32 + +fn C.GetFinalPathNameByHandleW(hFile voidptr, lpFilePath &u16, nSize u32, dwFlags u32) int + +fn C.CreatePipe(hReadPipe &voidptr, hWritePipe &voidptr, lpPipeAttributes voidptr, nSize u32) bool + +fn C.SetHandleInformation(hObject voidptr, dwMask u32, dw_flags u32) bool + +fn C.ExpandEnvironmentStringsW(lpSrc &u16, lpDst &u16, nSize u32) u32 + +fn C.GetComputerNameW(&u16, &u32) bool + +fn C.GetUserNameW(&u16, &u32) bool + +[trusted] +fn C.SendMessageTimeout() u32 + +fn C.SendMessageTimeoutW(hWnd voidptr, Msg u32, wParam &u16, lParam &u32, fuFlags u32, uTimeout u32, lpdwResult &u64) u32 + +fn C.CreateProcessW(lpApplicationName &u16, lpCommandLine &u16, lpProcessAttributes voidptr, lpThreadAttributes voidptr, bInheritHandles bool, dwCreationFlags u32, lpEnvironment voidptr, lpCurrentDirectory &u16, lpStartupInfo voidptr, lpProcessInformation voidptr) bool + +fn C.ReadFile(hFile voidptr, lpBuffer voidptr, nNumberOfBytesToRead u32, lpNumberOfBytesRead C.LPDWORD, lpOverlapped voidptr) bool + +fn C.GetFileAttributesW(lpFileName &byte) u32 + +fn C.RegQueryValueEx(hKey voidptr, lpValueName &u16, lp_reserved &u32, lpType &u32, lpData &byte, lpcbData &u32) voidptr + +fn C.RegQueryValueExW(hKey voidptr, lpValueName &u16, lp_reserved &u32, lpType &u32, lpData &byte, lpcbData &u32) int + +fn C.RegOpenKeyEx(hKey voidptr, lpSubKey &u16, ulOptions u32, samDesired u32, phkResult voidptr) voidptr + +fn C.RegOpenKeyExW(hKey voidptr, lpSubKey &u16, ulOptions u32, samDesired u32, phkResult voidptr) int + +fn C.RegSetValueEx() voidptr + +fn C.RegSetValueExW(hKey voidptr, lpValueName &u16, Reserved u32, dwType u32, lpData &byte, lpcbData u32) int + +fn C.RegCloseKey(hKey voidptr) + +fn C.RemoveDirectory(lpPathName &u16) int + +// fn C.GetStdHandle() voidptr +fn C.GetStdHandle(u32) voidptr + +// fn C.SetConsoleMode() +fn C.SetConsoleMode(voidptr, u32) int + +// fn C.GetConsoleMode() int +fn C.GetConsoleMode(voidptr, &u32) int + +[trusted] +fn C.GetCurrentProcessId() int + +fn C.wprintf() + +// fn C.setbuf() +fn C.setbuf(voidptr, &char) + +fn C.SymCleanup(hProcess voidptr) + +fn C.MultiByteToWideChar(codePage u32, dwFlags u32, lpMultiMyteStr &char, cbMultiByte int, lpWideCharStr &u16, cchWideChar int) int + +fn C.wcslen(str &u16) int + +fn C.WideCharToMultiByte(codePage u32, dwFlags u32, lpWideCharStr &u16, cchWideChar int, lpMultiByteStr &char, cbMultiByte int, lpDefaultChar &char, lpUsedDefaultChar &int) int + +fn C._wstat(path &u16, buffer &C._stat) int + +fn C._wrename(oldname &u16, newname &u16) int + +fn C._wfopen(filename &u16, mode &u16) voidptr + +fn C._wpopen(command &u16, mode &u16) voidptr + +fn C._pclose(stream &C.FILE) int + +fn C._wsystem(command &u16) int + +fn C._wgetenv(varname &u16) voidptr + +fn C._putenv(envstring &char) int + +fn C._waccess(path &u16, mode int) int + +fn C._wremove(path &u16) int + +fn C.ReadConsole(in_input_handle voidptr, out_buffer voidptr, in_chars_to_read u32, out_read_chars &u32, in_input_control voidptr) bool + +fn C.WriteConsole() voidptr + +fn C.WriteFile() voidptr + +fn C._wchdir(dirname &u16) + +fn C._wgetcwd(buffer &u16, maxlen int) int + +fn C._fullpath() int + +fn C.GetFullPathName(voidptr, u32, voidptr, voidptr) u32 + +[trusted] +fn C.GetCommandLine() voidptr + +fn C.LocalFree() + +fn C.FindFirstFileW(lpFileName &u16, lpFindFileData voidptr) voidptr + +fn C.FindFirstFile(lpFileName &byte, lpFindFileData voidptr) voidptr + +fn C.FindNextFile(hFindFile voidptr, lpFindFileData voidptr) int + +fn C.FindClose(hFindFile voidptr) + +// macro +fn C.MAKELANGID(lgid voidptr, srtid voidptr) int + +fn C.FormatMessage(dwFlags u32, lpSource voidptr, dwMessageId u32, dwLanguageId u32, lpBuffer voidptr, nSize int, Arguments ...voidptr) voidptr + +fn C.CloseHandle(voidptr) int + +fn C.GetExitCodeProcess(hProcess voidptr, lpExitCode &u32) + +[trusted] +fn C.GetTickCount() i64 + +[trusted] +fn C.Sleep(dwMilliseconds u32) + +fn C.WSAStartup(u16, &voidptr) int + +fn C.WSAGetLastError() int + +fn C.closesocket(int) int + +fn C.vschannel_init(&C.TlsContext) + +fn C.request(&C.TlsContext, int, &u16, &byte, &&byte) int + +fn C.vschannel_cleanup(&C.TlsContext) + +fn C.URLDownloadToFile(int, &u16, &u16, int, int) + +fn C.GetLastError() u32 + +fn C.CreateDirectory(&byte, int) bool + +// win crypto +fn C.BCryptGenRandom(int, voidptr, int, int) int + +// win synchronization +fn C.CreateMutex(int, bool, &byte) voidptr + +fn C.WaitForSingleObject(voidptr, int) int + +fn C.ReleaseMutex(voidptr) bool + +fn C.CreateEvent(int, bool, bool, &byte) voidptr + +fn C.SetEvent(voidptr) int + +fn C.CreateSemaphore(voidptr, int, int, voidptr) voidptr + +fn C.ReleaseSemaphore(voidptr, int, voidptr) voidptr + +fn C.InitializeSRWLock(voidptr) + +fn C.AcquireSRWLockShared(voidptr) + +fn C.AcquireSRWLockExclusive(voidptr) + +fn C.ReleaseSRWLockShared(voidptr) + +fn C.ReleaseSRWLockExclusive(voidptr) + +// pthread.h +fn C.pthread_mutex_init(voidptr, voidptr) int + +fn C.pthread_mutex_lock(voidptr) int + +fn C.pthread_mutex_unlock(voidptr) int + +fn C.pthread_mutex_destroy(voidptr) int + +fn C.pthread_rwlockattr_init(voidptr) int + +fn C.pthread_rwlockattr_setkind_np(voidptr, int) int + +fn C.pthread_rwlockattr_setpshared(voidptr, int) int + +fn C.pthread_rwlock_init(voidptr, voidptr) int + +fn C.pthread_rwlock_rdlock(voidptr) int + +fn C.pthread_rwlock_wrlock(voidptr) int + +fn C.pthread_rwlock_unlock(voidptr) int + +fn C.pthread_condattr_init(voidptr) int + +fn C.pthread_condattr_setpshared(voidptr, int) int + +fn C.pthread_condattr_destroy(voidptr) int + +fn C.pthread_cond_init(voidptr, voidptr) int + +fn C.pthread_cond_signal(voidptr) int + +fn C.pthread_cond_wait(voidptr, voidptr) int + +fn C.pthread_cond_timedwait(voidptr, voidptr, voidptr) int + +fn C.pthread_cond_destroy(voidptr) int + +fn C.sem_init(voidptr, int, u32) int + +fn C.sem_post(voidptr) int + +fn C.sem_wait(voidptr) int + +fn C.sem_trywait(voidptr) int + +fn C.sem_timedwait(voidptr, voidptr) int + +fn C.sem_destroy(voidptr) int + +// MacOS semaphore functions +fn C.dispatch_semaphore_create(i64) voidptr + +fn C.dispatch_semaphore_signal(voidptr) i64 + +fn C.dispatch_semaphore_wait(voidptr, u64) i64 + +fn C.dispatch_time(u64, i64) u64 + +fn C.dispatch_release(voidptr) + +// file descriptor based reading/writing +fn C.read(fd int, buf voidptr, count size_t) int + +fn C.write(fd int, buf voidptr, count size_t) int + +fn C.close(fd int) int + +// pipes +fn C.pipe(pipefds &int) int + +fn C.dup2(oldfd int, newfd int) int + +// used by gl, stbi, freetype +fn C.glTexImage2D() + +// used by ios for println +fn C.WrappedNSLog(str &byte) diff --git a/v_windows/v/old/vlib/builtin/chan.v b/v_windows/v/old/vlib/builtin/chan.v new file mode 100644 index 0000000..1692d5f --- /dev/null +++ b/v_windows/v/old/vlib/builtin/chan.v @@ -0,0 +1,32 @@ +module builtin + +// ChanState describes the result of an attempted channel transaction. +pub enum ChanState { + success + not_ready // push()/pop() would have to wait, but no_block was requested + closed +} + +/* +The following methods are only stubs. +The real implementation is in `vlib/sync/channels.v` +*/ + +// close closes the channel for further push transactions. +// closed channels cannot be pushed to, however they can be popped +// from as long as there is still objects available in the channel buffer. +pub fn (ch chan) close() {} + +// try_pop returns `ChanState.success` if an object is popped from the channel. +// try_pop effectively pops from the channel without waiting for objects to become available. +// Both the test and pop transaction is done atomically. +pub fn (ch chan) try_pop(obj voidptr) ChanState { + return .success +} + +// try_push returns `ChanState.success` if the object is pushed to the channel. +// try_push effectively both push and test if the transaction `ch <- a` succeeded. +// Both the test and push transaction is done atomically. +pub fn (ch chan) try_push(obj voidptr) ChanState { + return .success +} diff --git a/v_windows/v/old/vlib/builtin/float.v b/v_windows/v/old/vlib/builtin/float.v new file mode 100644 index 0000000..99fe8b8 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/float.v @@ -0,0 +1,205 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license that can be found in the LICENSE file. +module builtin + +import strconv + +#include +/* +----------------------------------- +----- f64 to string functions ----- +*/ +// str return a `f64` as `string` in suitable notation. +[inline] +pub fn (x f64) str() string { + unsafe { + f := strconv.Float64u{ + f: x + } + if f.u == strconv.double_minus_zero { + return '-0' + } + if f.u == strconv.double_plus_zero { + return '0' + } + } + abs_x := f64_abs(x) + if abs_x >= 0.0001 && abs_x < 1.0e6 { + return strconv.f64_to_str_l(x) + } else { + return strconv.ftoa_64(x) + } +} + +// strg return a `f64` as `string` in "g" printf format +[inline] +pub fn (x f64) strg() string { + if x == 0 { + return '0' + } + abs_x := f64_abs(x) + if abs_x >= 0.0001 && abs_x < 1.0e6 { + return strconv.f64_to_str_l_no_dot(x) + } else { + return strconv.ftoa_64(x) + } +} + +// str returns the value of the `float_literal` as a `string`. +[inline] +pub fn (d float_literal) str() string { + return f64(d).str() +} + +// strsci returns the `f64` as a `string` in scientific notation with `digit_num` decimals displayed, max 17 digits. +// Example: assert f64(1.234).strsci(3) == '1.234e+00' +[inline] +pub fn (x f64) strsci(digit_num int) string { + mut n_digit := digit_num + if n_digit < 1 { + n_digit = 1 + } else if n_digit > 17 { + n_digit = 17 + } + return strconv.f64_to_str(x, n_digit) +} + +// strlong returns a decimal notation of the `f64` as a `string`. +// Example: assert f64(1.23456).strlong() == '1.23456' +[inline] +pub fn (x f64) strlong() string { + return strconv.f64_to_str_l(x) +} + +/* +----------------------------------- +----- f32 to string functions ----- +*/ +// str returns a `f32` as `string` in suitable notation. +[inline] +pub fn (x f32) str() string { + unsafe { + f := strconv.Float32u{ + f: x + } + if f.u == strconv.single_minus_zero { + return '-0' + } + if f.u == strconv.single_plus_zero { + return '0' + } + } + abs_x := f32_abs(x) + if abs_x >= 0.0001 && abs_x < 1.0e6 { + return strconv.f32_to_str_l(x) + } else { + return strconv.ftoa_32(x) + } +} + +// strg return a `f32` as `string` in "g" printf format +[inline] +pub fn (x f32) strg() string { + if x == 0 { + return '0' + } + abs_x := f32_abs(x) + if abs_x >= 0.0001 && abs_x < 1.0e6 { + return strconv.f32_to_str_l_no_dot(x) + } else { + return strconv.ftoa_32(x) + } +} + +// strsci returns the `f32` as a `string` in scientific notation with `digit_num` deciamals displayed, max 8 digits. +// Example: assert f32(1.234).strsci(3) == '1.234e+00' +[inline] +pub fn (x f32) strsci(digit_num int) string { + mut n_digit := digit_num + if n_digit < 1 { + n_digit = 1 + } else if n_digit > 8 { + n_digit = 8 + } + return strconv.f32_to_str(x, n_digit) +} + +// strlong returns a decimal notation of the `f32` as a `string`. +[inline] +pub fn (x f32) strlong() string { + return strconv.f32_to_str_l(x) +} + +/* +----------------------- +----- C functions ----- +*/ +// f32_abs returns the absolute value of `a` as a `f32` value. +// Example: assert f32_abs(-2.0) == 2.0 +[inline] +pub fn f32_abs(a f32) f32 { + return if a < 0 { -a } else { a } +} + +// f64_abs returns the absolute value of `a` as a `f64` value. +// Example: assert f64_abs(-2.0) == f64(2.0) +[inline] +fn f64_abs(a f64) f64 { + return if a < 0 { -a } else { a } +} + +// f32_max returns the largest `f32` of input `a` and `b`. +// Example: assert f32_max(2.0,3.0) == 3.0 +[inline] +pub fn f32_max(a f32, b f32) f32 { + return if a > b { a } else { b } +} + +// f32_min returns the smallest `f32` of input `a` and `b`. +// Example: assert f32_min(2.0,3.0) == 2.0 +[inline] +pub fn f32_min(a f32, b f32) f32 { + return if a < b { a } else { b } +} + +// f64_max returns the largest `f64` of input `a` and `b`. +// Example: assert f64_max(2.0,3.0) == 3.0 +[inline] +pub fn f64_max(a f64, b f64) f64 { + return if a > b { a } else { b } +} + +// f64_min returns the smallest `f64` of input `a` and `b`. +// Example: assert f64_min(2.0,3.0) == 2.0 +[inline] +fn f64_min(a f64, b f64) f64 { + return if a < b { a } else { b } +} + +// eq_epsilon returns true if the `f32` is equal to input `b`. +// using an epsilon of typically 1E-5 or higher (backend/compiler dependent). +// Example: assert f32(2.0).eq_epsilon(2.0) +[inline] +pub fn (a f32) eq_epsilon(b f32) bool { + hi := f32_max(f32_abs(a), f32_abs(b)) + delta := f32_abs(a - b) + if hi > f32(1.0) { + return delta <= hi * (4 * f32(C.FLT_EPSILON)) + } else { + return (1 / (4 * f32(C.FLT_EPSILON))) * delta <= hi + } +} + +// eq_epsilon returns true if the `f64` is equal to input `b`. +// using an epsilon of typically 1E-9 or higher (backend/compiler dependent). +// Example: assert f64(2.0).eq_epsilon(2.0) +[inline] +pub fn (a f64) eq_epsilon(b f64) bool { + hi := f64_max(f64_abs(a), f64_abs(b)) + delta := f64_abs(a - b) + if hi > 1.0 { + return delta <= hi * (4 * f64(C.DBL_EPSILON)) + } else { + return (1 / (4 * f64(C.DBL_EPSILON))) * delta <= hi + } +} diff --git a/v_windows/v/old/vlib/builtin/float_test.v b/v_windows/v/old/vlib/builtin/float_test.v new file mode 100644 index 0000000..cad2799 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/float_test.v @@ -0,0 +1,147 @@ +fn test_float_decl() { + // z := 1f + // assert z > 0 + x1 := 1e10 + x2 := -2e16 + x3 := 1e-15 + x4 := -9e-4 + assert typeof(x1).name == 'f64' + assert typeof(x2).name == 'f64' + assert typeof(x3).name == 'f64' + assert typeof(x4).name == 'f64' + x5 := 4e108 + x6 := -7e99 + x7 := 3e-205 + x8 := -6e-147 + assert typeof(x5).name == 'f64' + assert typeof(x6).name == 'f64' + assert typeof(x7).name == 'f64' + assert typeof(x8).name == 'f64' + x9 := 312874834.77 + x10 := -22399994.06 + x11 := 0.0000000019 + x12 := -0.00000000008 + assert typeof(x9).name == 'f64' + assert typeof(x10).name == 'f64' + assert typeof(x11).name == 'f64' + assert typeof(x12).name == 'f64' + x13 := 34234234809890890898903213154353453453253253243432413232228908902183918392183902432432438980380123021983901392183921389083913890389089031.0 + x14 := -39999999999999999999222212128182813294989082302832183928343325325233253242312331324392839238239829389038097438248932789371837218372837293.8 + x15 := 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002 + x16 := -0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004 + assert typeof(x13).name == 'f64' + assert typeof(x14).name == 'f64' + assert typeof(x15).name == 'f64' + assert typeof(x16).name == 'f64' +} + +fn test_f32_equal_operator() { + b := f32(1.0) + mut a := f32(1.0) + a += 0.0000019073486328125 + assert a != b + a -= 0.0000019073486328125 + assert a == b + assert -1 == 1 * -1 + assert -1.0 == 1.0 * -1.0 + a = 1 + a += 0.0000019073486328125 + a -= 0.0000019073486328125 + assert a == f32(1.0) + a += 0.000001 + assert !(a < f32(1)) + assert !(a <= f32(1)) + assert a > f32(1) + assert a >= 1 + assert a != 1 + f := 1.2 + ab := int(f) + assert ab == 1 + e := f32(-1.602176634e-19) + m := f32(9.1093837015e-31) + assert e < m + assert e <= m + assert e != m + assert !(e == m) + assert m >= e + assert m > e +} + +fn test_f64_equal_operator() { + b := 1.0 + mut a := 1.0 + a += 0.0000019073486328125 + assert a != b + a -= 0.0000019073486328125 + assert a == b + e := -1.602176634e-19 + m := 9.1093837015e-31 + assert e < m + assert e <= m + assert e != m + assert !(e == m) + assert m >= e + assert m > e +} + +fn test_f64_eq_epsilon() { + a := 1.662248544459347e308 + b := 1.662248544459348e308 + x := 1.662248544459352e308 + assert a != b + assert a.eq_epsilon(b) + assert b.eq_epsilon(a) + assert (-a).eq_epsilon(-b) + assert (-b).eq_epsilon(-a) + assert !a.eq_epsilon(x) + assert !x.eq_epsilon(a) + assert !a.eq_epsilon(-b) + assert !(-a).eq_epsilon(b) + c := 1.5367748374385438503 + d := -1.5367748374385447257 + z := 1.5367748378943546 + assert c != -d + assert c.eq_epsilon(-d) + assert d.eq_epsilon(-c) + assert !c.eq_epsilon(z) + assert !z.eq_epsilon(c) + e := 2.531434251587394233e-308 + f := 2.531434251587395675e-308 + y := 2.531434251587398934e-308 + assert e != f + assert e.eq_epsilon(f) + assert (-f).eq_epsilon(-e) + assert !e.eq_epsilon(y) + assert !(-y).eq_epsilon(-e) +} + +fn test_f32_eq_epsilon() { + a := f32(3.244331e38) + b := f32(3.244332e38) + x := f32(3.244338e38) + assert a != b + assert a.eq_epsilon(b) + assert b.eq_epsilon(a) + assert (-a).eq_epsilon(-b) + assert (-b).eq_epsilon(-a) + assert !a.eq_epsilon(x) + assert !(-x).eq_epsilon(-a) + assert !a.eq_epsilon(-b) + assert !(-a).eq_epsilon(b) + c := f32(0.9546742) + d := f32(-0.9546745) + z := f32(0.9546754) + assert c != -d + assert c.eq_epsilon(-d) + assert d.eq_epsilon(-c) + assert !c.eq_epsilon(z) + assert !z.eq_epsilon(c) + e := f32(-1.5004390e-38) + f := f32(-1.5004395e-38) + y := f32(-1.5004409e-38) + assert e != f + assert e.eq_epsilon(f) + assert (-f).eq_epsilon(-e) + assert !e.eq_epsilon(y) + assert !(-y).eq_epsilon(-e) +} diff --git a/v_windows/v/old/vlib/builtin/float_x64.v b/v_windows/v/old/vlib/builtin/float_x64.v new file mode 100644 index 0000000..4a491a0 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/float_x64.v @@ -0,0 +1,6 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license that can be found in the LICENSE file. +module builtin + +fn float_test() { +} diff --git a/v_windows/v/old/vlib/builtin/int.v b/v_windows/v/old/vlib/builtin/int.v new file mode 100644 index 0000000..9c769d9 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/int.v @@ -0,0 +1,479 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module builtin + +// +// ----- value to string functions ----- +// + +type u8 = byte + +// ptr_str returns the address of `ptr` as a `string`. +pub fn ptr_str(ptr voidptr) string { + buf1 := u64(ptr).hex() + return buf1 +} + +pub fn (x size_t) str() string { + return u64(x).str() +} + +pub fn (cptr &char) str() string { + return u64(cptr).hex() +} + +const ( + // digit pairs in reverse order + digit_pairs = '00102030405060708090011121314151617181910212223242526272829203132333435363738393041424344454647484940515253545556575859506162636465666768696071727374757677787970818283848586878889809192939495969798999' +) + +// This implementation is the quickest with gcc -O2 +// str_l returns the string representation of the integer nn with max chars. +[direct_array_access; inline] +fn (nn int) str_l(max int) string { + unsafe { + mut n := i64(nn) + mut d := 0 + if n == 0 { + return '0' + } + + mut is_neg := false + if n < 0 { + n = -n + is_neg = true + } + mut index := max + mut buf := malloc_noscan(max + 1) + buf[index] = 0 + index-- + + for n > 0 { + n1 := int(n / 100) + // calculate the digit_pairs start index + d = ((int(n) - (n1 * 100)) << 1) + n = n1 + buf[index] = digit_pairs.str[d] + index-- + d++ + buf[index] = digit_pairs.str[d] + index-- + } + index++ + // remove head zero + if d < 20 { + index++ + } + // Prepend - if it's negative + if is_neg { + index-- + buf[index] = `-` + } + diff := max - index + C.memmove(buf, buf + index, diff + 1) + /* + // === manual memory move for bare metal === + mut c:= 0 + for c < diff { + buf[c] = buf[c+index] + c++ + } + buf[c] = 0 + */ + return tos(buf, diff) + + // return tos(memdup(&buf[0] + index, (max - index)), (max - index)) + } +} + +// str returns the value of the `i8` as a `string`. +// Example: assert i8(-2).str() == '-2' +pub fn (n i8) str() string { + return int(n).str_l(5) +} + +// str returns the value of the `i16` as a `string`. +// Example: assert i16(-20).str() == '-20' +pub fn (n i16) str() string { + return int(n).str_l(7) +} + +// str returns the value of the `u16` as a `string`. +// Example: assert u16(20).str() == '20' +pub fn (n u16) str() string { + return int(n).str_l(7) +} + +// str returns the value of the `int` as a `string`. +// Example: assert int(-2020).str() == '-2020' +pub fn (n int) str() string { + return n.str_l(12) +} + +// str returns the value of the `u32` as a `string`. +// Example: assert u32(20000).str() == '20000' +[direct_array_access; inline] +pub fn (nn u32) str() string { + unsafe { + mut n := nn + mut d := u32(0) + if n == 0 { + return '0' + } + max := 12 + mut buf := malloc_noscan(max + 1) + mut index := max + buf[index] = 0 + index-- + for n > 0 { + n1 := n / u32(100) + d = ((n - (n1 * u32(100))) << u32(1)) + n = n1 + buf[index] = digit_pairs[d] + index-- + d++ + buf[index] = digit_pairs[d] + index-- + } + index++ + // remove head zero + if d < u32(20) { + index++ + } + diff := max - index + C.memmove(buf, buf + index, diff + 1) + return tos(buf, diff) + + // return tos(memdup(&buf[0] + index, (max - index)), (max - index)) + } +} + +// str returns the value of the `int_literal` as a `string`. +[inline] +pub fn (n int_literal) str() string { + return i64(n).str() +} + +// str returns the value of the `i64` as a `string`. +// Example: assert i64(-200000).str() == '-200000' +[direct_array_access; inline] +pub fn (nn i64) str() string { + unsafe { + mut n := nn + mut d := i64(0) + if n == 0 { + return '0' + } + max := 20 + mut buf := malloc_noscan(max + 1) + mut is_neg := false + if n < 0 { + n = -n + is_neg = true + } + mut index := max + buf[index] = 0 + index-- + for n > 0 { + n1 := n / i64(100) + d = ((n - (n1 * i64(100))) << i64(1)) + n = n1 + buf[index] = digit_pairs[d] + index-- + d++ + buf[index] = digit_pairs[d] + index-- + } + index++ + // remove head zero + if d < i64(20) { + index++ + } + // Prepend - if it's negative + if is_neg { + index-- + buf[index] = `-` + } + diff := max - index + C.memmove(buf, buf + index, diff + 1) + return tos(buf, diff) + // return tos(memdup(&buf[0] + index, (max - index)), (max - index)) + } +} + +// str returns the value of the `u64` as a `string`. +// Example: assert u64(2000000).str() == '2000000' +[direct_array_access; inline] +pub fn (nn u64) str() string { + unsafe { + mut n := nn + mut d := u64(0) + if n == 0 { + return '0' + } + max := 20 + mut buf := malloc_noscan(max + 1) + mut index := max + buf[index] = 0 + index-- + for n > 0 { + n1 := n / 100 + d = ((n - (n1 * 100)) << 1) + n = n1 + buf[index] = digit_pairs[d] + index-- + d++ + buf[index] = digit_pairs[d] + index-- + } + index++ + // remove head zero + if d < 20 { + index++ + } + diff := max - index + C.memmove(buf, buf + index, diff + 1) + return tos(buf, diff) + // return tos(memdup(&buf[0] + index, (max - index)), (max - index)) + } +} + +// str returns the value of the `bool` as a `string`. +// Example: assert (2 > 1).str() == 'true' +pub fn (b bool) str() string { + if b { + return 'true' + } + return 'false' +} + +// +// ----- value to hex string functions ----- +// + +// u64_to_hex converts the number `nn` to a (zero padded if necessary) hexadecimal `string`. +[direct_array_access; inline] +fn u64_to_hex(nn u64, len byte) string { + mut n := nn + mut buf := [256]byte{} + buf[len] = 0 + mut i := 0 + for i = len - 1; i >= 0; i-- { + d := byte(n & 0xF) + x := if d < 10 { d + `0` } else { d + 87 } + buf[i] = x + n = n >> 4 + } + return unsafe { tos(memdup(&buf[0], len + 1), len) } +} + +// u64_to_hex_no_leading_zeros converts the number `nn` to hexadecimal `string`. +[direct_array_access; inline] +fn u64_to_hex_no_leading_zeros(nn u64, len byte) string { + mut n := nn + mut buf := [256]byte{} + buf[len] = 0 + mut i := 0 + for i = len - 1; i >= 0; i-- { + d := byte(n & 0xF) + x := if d < 10 { d + `0` } else { d + 87 } + buf[i] = x + n = n >> 4 + if n == 0 { + break + } + } + res_len := len - i + return unsafe { tos(memdup(&buf[i], res_len + 1), res_len) } +} + +// hex returns the value of the `byte` as a hexadecimal `string`. +// Note that the output is zero padded for values below 16. +// Example: assert byte(2).hex() == '02' +// Example: assert byte(15).hex() == '0f' +// Example: assert byte(255).hex() == 'ff' +pub fn (nn byte) hex() string { + if nn == 0 { + return '00' + } + return u64_to_hex(nn, 2) +} + +// hex returns the value of the `i8` as a hexadecimal `string`. +// Note that the output is zero padded for values below 16. +// Example: assert i8(8).hex() == '08' +// Example: assert i8(10).hex() == '0a' +// Example: assert i8(15).hex() == '0f' +pub fn (nn i8) hex() string { + return byte(nn).hex() +} + +// hex returns the value of the `u16` as a hexadecimal `string`. +// Note that the output is ***not*** zero padded. +// Example: assert u16(2).hex() == '2' +// Example: assert u16(200).hex() == 'c8' +pub fn (nn u16) hex() string { + if nn == 0 { + return '0' + } + return u64_to_hex_no_leading_zeros(nn, 4) +} + +// hex returns the value of the `i16` as a hexadecimal `string`. +// Note that the output is ***not*** zero padded. +// Example: assert i16(2).hex() == '2' +// Example: assert i16(200).hex() == 'c8' +pub fn (nn i16) hex() string { + return u16(nn).hex() +} + +// hex returns the value of the `u32` as a hexadecimal `string`. +// Note that the output is ***not*** zero padded. +// Example: assert u32(2).hex() == '2' +// Example: assert u32(200).hex() == 'c8' +pub fn (nn u32) hex() string { + if nn == 0 { + return '0' + } + return u64_to_hex_no_leading_zeros(nn, 8) +} + +// hex returns the value of the `int` as a hexadecimal `string`. +// Note that the output is ***not*** zero padded. +// Example: assert int(2).hex() == '2' +// Example: assert int(200).hex() == 'c8' +pub fn (nn int) hex() string { + return u32(nn).hex() +} + +// hex2 returns the value of the `int` as a `0x`-prefixed hexadecimal `string`. +// Note that the output after `0x` is ***not*** zero padded. +// Example: assert int(8).hex2() == '0x8' +// Example: assert int(15).hex2() == '0xf' +// Example: assert int(18).hex2() == '0x12' +pub fn (n int) hex2() string { + return '0x' + n.hex() +} + +// hex returns the value of the `u64` as a hexadecimal `string`. +// Note that the output is ***not*** zero padded. +// Example: assert u64(2).hex() == '2' +// Example: assert u64(2000).hex() == '7d0' +pub fn (nn u64) hex() string { + if nn == 0 { + return '0' + } + return u64_to_hex_no_leading_zeros(nn, 16) +} + +// hex returns the value of the `i64` as a hexadecimal `string`. +// Note that the output is ***not*** zero padded. +// Example: assert i64(2).hex() == '2' +// Example: assert i64(-200).hex() == 'ffffffffffffff38' +// Example: assert i64(2021).hex() == '7e5' +pub fn (nn i64) hex() string { + return u64(nn).hex() +} + +// hex returns the value of the `int_literal` as a hexadecimal `string`. +// Note that the output is ***not*** zero padded. +pub fn (nn int_literal) hex() string { + return u64(nn).hex() +} + +// hex returns the value of the `voidptr` as a hexadecimal `string`. +// Note that the output is ***not*** zero padded. +pub fn (nn voidptr) str() string { + return u64(nn).hex() +} + +// hex returns the value of the `byteptr` as a hexadecimal `string`. +// Note that the output is ***not*** zero padded. +// pub fn (nn byteptr) str() string { +pub fn (nn byteptr) str() string { + return u64(nn).hex() +} + +pub fn (nn byte) hex_full() string { + return u64_to_hex(u64(nn), 2) +} + +pub fn (nn i8) hex_full() string { + return u64_to_hex(u64(nn), 2) +} + +pub fn (nn u16) hex_full() string { + return u64_to_hex(u64(nn), 4) +} + +pub fn (nn i16) hex_full() string { + return u64_to_hex(u64(nn), 4) +} + +pub fn (nn u32) hex_full() string { + return u64_to_hex(u64(nn), 8) +} + +pub fn (nn int) hex_full() string { + return u64_to_hex(u64(nn), 8) +} + +pub fn (nn i64) hex_full() string { + return u64_to_hex(u64(nn), 16) +} + +pub fn (nn voidptr) hex_full() string { + return u64_to_hex(u64(nn), 16) +} + +pub fn (nn int_literal) hex_full() string { + return u64_to_hex(u64(nn), 16) +} + +// hex_full returns the value of the `u64` as a *full* 16-digit hexadecimal `string`. +// Example: assert u64(2).hex_full() == '0000000000000002' +// Example: assert u64(255).hex_full() == '00000000000000ff' +pub fn (nn u64) hex_full() string { + return u64_to_hex(nn, 16) +} + +// str returns the contents of `byte` as a zero terminated `string`. +// Example: assert byte(111).str() == '111' +pub fn (b byte) str() string { + return int(b).str_l(7) +} + +// ascii_str returns the contents of `byte` as a zero terminated ASCII `string` character. +// Example: assert byte(97).ascii_str() == 'a' +pub fn (b byte) ascii_str() string { + mut str := string{ + str: unsafe { malloc_noscan(2) } + len: 1 + } + unsafe { + str.str[0] = b + str.str[1] = 0 + } + // println(str) + return str +} + +// str_escaped returns the contents of `byte` as an escaped `string`. +// Example: assert byte(0).str_escaped() == r'`\0`' +pub fn (b byte) str_escaped() string { + str := match b { + 0 { r'`\0`' } + 7 { r'`\a`' } + 8 { r'`\b`' } + 9 { r'`\t`' } + 10 { r'`\n`' } + 11 { r'`\v`' } + 12 { r'`\f`' } + 13 { r'`\r`' } + 27 { r'`\e`' } + 32...126 { b.ascii_str() } + else { '0x' + b.hex() } + } + return str +} diff --git a/v_windows/v/old/vlib/builtin/int_test.v b/v_windows/v/old/vlib/builtin/int_test.v new file mode 100644 index 0000000..900cf27 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/int_test.v @@ -0,0 +1,230 @@ +const ( + a = 3 + u = u64(1) +) + +fn test_const() { + b := (true && true) || false + assert b == true + assert a == 3 + assert u == u64(1) + assert u == 1 // make sure this works without the cast +} + +fn test_str_methods() { + assert i8(1).str() == '1' + assert i8(-1).str() == '-1' + assert i16(1).str() == '1' + assert i16(-1).str() == '-1' + assert int(1).str() == '1' + assert int(-1).str() == '-1' + assert int(2147483647).str() == '2147483647' + assert int(2147483648).str() == '-2147483648' + assert int(-2147483648).str() == '-2147483648' + assert i64(1).str() == '1' + assert i64(-1).str() == '-1' + // assert byte(1).str() == '1' + // assert byte(-1).str() == '255' + assert u16(1).str() == '1' + assert u16(-1).str() == '65535' + assert u32(1).str() == '1' + assert u32(-1).str() == '4294967295' + assert u64(1).str() == '1' + assert u64(-1).str() == '18446744073709551615' + assert voidptr(-1).str() == 'ffffffffffffffff' + assert voidptr(1).str() == '1' + assert byteptr(-1).str() == 'ffffffffffffffff' + assert byteptr(1).str() == '1' +} + +fn test_and_precendence() { + assert (2 & 0 == 0) == ((2 & 0) == 0) + assert (2 & 0 != 0) == ((2 & 0) != 0) + assert (0 & 0 >= 0) == ((0 & 0) >= 0) + assert (0 & 0 <= 0) == ((0 & 0) <= 0) + assert (0 & 0 < 1) == ((0 & 0) < 1) + assert (1 & 2 > 0) == ((1 & 2) > 0) +} + +fn test_or_precendence() { + assert (1 | 0 == 0) == ((1 | 0) == 0) + assert (1 | 0 != 1) == ((1 | 0) != 1) + assert (1 | 0 >= 2) == ((1 | 0) >= 2) + assert (1 | 0 <= 0) == ((1 | 0) <= 0) + assert (1 | 0 < 0) == ((1 | 0) < 0) + assert (1 | 0 > 1) == ((1 | 0) > 1) +} + +fn test_xor_precendence() { + assert (1 ^ 0 == 2) == ((1 ^ 0) == 2) + assert (1 ^ 0 != 2) == ((1 ^ 0) != 2) + assert (1 ^ 0 >= 0) == ((1 ^ 0) >= 0) + assert (1 ^ 0 <= 1) == ((1 ^ 0) <= 1) + assert (1 ^ 0 < 0) == ((1 ^ 0) < 0) + assert (1 ^ 0 > 1) == ((1 ^ 0) > 1) +} + +fn test_left_shift_precendence() { + assert (2 << 4 | 3) == ((2 << 4) | 3) + assert (2 << 4 | 3) != (2 << (4 | 3)) +} + +fn test_right_shift_precendence() { + assert (256 >> 4 | 3) == ((256 >> 4) | 3) + assert (256 >> 4 | 3) != (256 >> (4 | 3)) +} + +fn test_i8_print() { + b := i8(0) + println(b) + c := i16(7) + println(c) + d := u16(6) + println(d) + assert true +} + +/* +fn test_cmp() { + assert 1 ≠ 2 + assert 1 ⩽ 2 + assert 1 ⩾ 0 +} +*/ +type MyInt = int + +fn test_int_alias() { + i := MyInt(2) + assert i + 10 == 12 +} + +fn test_hex() { + x := u64(10) + assert x.hex() == 'a' + b := 1234 + assert b.hex() == '4d2' + b1 := -1 + assert b1.hex() == 'ffffffff' +} + +fn test_bin() { + x1 := 0b10 + assert x1 == 2 + x2 := 0b10101010 + assert x2 == 0xAA + x3 := -0b0000001 + assert x3 == -1 + x4 := 0b11111111 + assert x4 == 255 + x5 := byte(0b11111111) + assert x5 == 255 + x6 := char(0b11111111) + assert int(x6) == -1 + x7 := 0b0 + assert x7 == 0 + x8 := -0b0 + assert x8 == 0 +} + +fn test_oct() { + x1 := 0o12 + assert x1 == 10 + x2 := 00000o350 + assert x2 == 232 + x3 := 000o00073 + assert x3 == 59 + x4 := 00000000 + assert x4 == 0 + x5 := 00000195 + assert x5 == 195 + x6 := -0o744 + assert x6 == -484 + x7 := -000o000042 + assert x7 == -34 + x8 := -0000112 + assert x8 == -112 + x9 := -000 + assert x9 == 0 +} + +fn test_num_separator() { + // int + assert 100_000_0 == 1000000 + assert -2_23_4_6 == -22346 + + // bin + assert 0b0_11 == 3 + assert -0b0_100 == -4 + + // oct + assert 0o1_73 == 123 + assert -0o17_5 == -125 + assert -0o175 == -125 + + // hex + assert 0xFF == 255 + assert 0xF_F == 255 + + // f32 or f64 + assert 312_2.55 == 3122.55 + assert 312_2.55 == 3122.55 + +} + +fn test_int_decl() { + x1 := 0 + x2 := 1333 + x3 := -88955 + x4 := 2000000000 + x5 := -1999999999 + assert typeof(x1).name == 'int' + assert typeof(x2).name == 'int' + assert typeof(x3).name == 'int' + assert typeof(x4).name == 'int' + assert typeof(x5).name == 'int' + x7 := u64(-321314588900011) + assert typeof(x7).name == 'u64' +} + +fn test_int_to_hex() { + // array hex + st := [byte(`V`), `L`, `A`, `N`, `G`] + assert st.hex() == '564c414e47' + assert st.hex().len == 10 + st1 := [byte(0x41)].repeat(100) + assert st1.hex() == '41'.repeat(100) + // --- int to hex tests + c0 := 12 + // 8Bit + assert byte(0).hex() == '00' + assert byte(c0).hex() == '0c' + assert i8(c0).hex() == '0c' + assert byte(127).hex() == '7f' + assert i8(127).hex() == '7f' + assert byte(255).hex() == 'ff' + assert byte(-1).hex() == 'ff' + // 16bit + assert u16(0).hex() == '0' + assert i16(c0).hex() == 'c' + assert u16(c0).hex() == 'c' + assert i16(32767).hex() == '7fff' + assert u16(32767).hex() == '7fff' + assert i16(-1).hex() == 'ffff' + assert u16(65535).hex() == 'ffff' + // 32bit + assert u32(0).hex() == '0' + assert c0.hex() == 'c' + assert u32(c0).hex() == 'c' + assert 2147483647.hex() == '7fffffff' + assert u32(2147483647).hex() == '7fffffff' + assert (-1).hex() == 'ffffffffffffffff' + assert u32(4294967295).hex() == 'ffffffff' + // 64 bit + assert u64(0).hex() == '0' + assert i64(c0).hex() == 'c' + assert u64(c0).hex() == 'c' + assert i64(9223372036854775807).hex() == '7fffffffffffffff' + assert u64(9223372036854775807).hex() == '7fffffffffffffff' + assert i64(-1).hex() == 'ffffffffffffffff' + assert u64(18446744073709551615).hex() == 'ffffffffffffffff' +} diff --git a/v_windows/v/old/vlib/builtin/isnil_test.v b/v_windows/v/old/vlib/builtin/isnil_test.v new file mode 100644 index 0000000..15df9f0 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/isnil_test.v @@ -0,0 +1,19 @@ +fn test_isnil_byteptr() { + pb := &byte(0) + assert isnil(pb) +} + +fn test_isnil_voidptr() { + pv := voidptr(0) + assert isnil(pv) +} + +fn test_isnil_charptr() { + pc := &char(0) + assert isnil(pc) +} + +fn test_isnil_intptr() { + pi := &int(0) + assert isnil(pi) +} diff --git a/v_windows/v/old/vlib/builtin/js/array.js.v b/v_windows/v/old/vlib/builtin/js/array.js.v new file mode 100644 index 0000000..a741059 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/js/array.js.v @@ -0,0 +1,195 @@ +module builtin + +struct array { + arr JS.Array +pub: + len int + cap int +} + +#function flatIntoArray(target, source, sourceLength, targetIndex, depth) { +#"use strict"; +# +#for (var sourceIndex = 0; sourceIndex < sourceLength; ++sourceIndex) { +#if (sourceIndex in source) { +#var element = source[sourceIndex]; +#if (depth > 0 && Array.isArray(element)) +#targetIndex = flatIntoArray(target, element, element.length, targetIndex, depth - 1); +#else { +#target[targetIndex] = element; +#++targetIndex; +#} +#} +#} +#return targetIndex; +#} +#function flatArray(target,depth) { +#var array = target +#var length = array.length; +#var depthNum = 1; +# +#if (depth !== undefined) +#depthNum = +depth +# +#var result = [] +# +#flatIntoArray(result, array, length, 0, depthNum); +#return result; +#} + +[unsafe] +pub fn (a array) repeat_to_depth(count int, depth int) array { + if count < 0 { + panic('array.repeat: count is negative: $count') + } + mut arr := empty_array() + #let tmp = new Array(a.arr.length * +count); + #tmp.fill(a.arr); + # + #arr.arr = flatArray(tmp,depth+1); + + return arr +} + +// last returns the last element of the array. +pub fn (a array) last() voidptr { + mut res := voidptr(0) + #res = a.arr[a.len-1]; + + return res +} + +fn (a array) get(ix int) voidptr { + mut result := voidptr(0) + #result = a.arr[ix] + + return result +} + +pub fn (a array) repeat(count int) array { + unsafe { + return a.repeat_to_depth(count, 0) + } +} + +fn empty_array() array { + mut arr := array{} + #arr = new array([]) + + return arr +} + +fn (a &array) set_len(i int) { + #a.arr.length=i +} + +pub fn (mut a array) sort_with_compare(compare voidptr) { + #a.arr.sort(compare) +} + +#function $sortComparator(a, b) +#{ +#"use strict"; +#a = a.$toJS(); +#b = b.$toJS(); +# +#if (a > b) return 1; +#if (a < b) return -1; +#return 0; +# +# +#} + +pub fn (mut a array) sort() { + #a.arr.sort($sortComparator) +} + +pub fn (a array) index(v string) int { + for i in 0 .. a.len { + #if (a.arr[i].toString() == v.toString()) + + { + return i + } + } + return -1 +} + +pub fn (a array) slice(start int, end int) array { + mut result := a + #result = new array(a.arr.slice(start,end)) + + return result +} + +pub fn (mut a array) insert(i int, val voidptr) { + #a.arr.splice(i,0,val) +} + +pub fn (mut a array) insert_many(i int, val voidptr, size int) { + #a.arr.splice(i,0,...val.slice(0,+size)) +} + +pub fn (mut a array) join(separator string) string { + mut res := '' + #res = new builtin.string(a.arr.join(separator +'')); + + return res +} + +fn (a array) push(val voidptr) { + #a.arr.push(val) +} + +pub fn (a array) str() string { + mut res := '' + #res = new builtin.string(a + '') + + return res +} + +#array.prototype[Symbol.iterator] = function () { return this.arr[Symbol.iterator](); } +#array.prototype.entries = function () { return this.arr.entries(); } +#array.prototype.map = function(callback) { return new builtin.array(this.arr.map(callback)); } +#array.prototype.filter = function(callback) { return new array(this.arr.filter( function (it) { return (+callback(it)) != 0; } )); } +#Object.defineProperty(array.prototype,'cap',{ get: function () { return this.len; } }) +// delete deletes array element at index `i`. +pub fn (mut a array) delete(i int) { + a.delete_many(i, 1) +} + +// delete_many deletes `size` elements beginning with index `i` +pub fn (mut a array) delete_many(i int, size int) { + #a.arr.splice(i.valueOf(),size.valueOf()) +} + +// prepend prepends one value to the array. +pub fn (mut a array) prepend(val voidptr) { + a.insert(0, val) +} + +// prepend_many prepends another array to this array. +[unsafe] +pub fn (mut a array) prepend_many(val voidptr, size int) { + unsafe { a.insert_many(0, val, size) } +} + +pub fn (a array) reverse() array { + mut res := array{} + #res.arr = Array.from(a.arr).reverse() + + return res +} + +#array.prototype.$includes = function (elem) { return this.arr.find(function(e) { return vEq(elem,e); }) !== undefined;} + +// reduce executes a given reducer function on each element of the array, +// resulting in a single output value. +pub fn (a array) reduce(iter fn (int, int) int, accum_start int) int { + mut accum_ := accum_start + #for (let i of a) { + #accum_ = iter(accum_, a.arr[i]) + #} + + return accum_ +} diff --git a/v_windows/v/old/vlib/builtin/js/builtin.js.v b/v_windows/v/old/vlib/builtin/js/builtin.js.v new file mode 100644 index 0000000..d763466 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/js/builtin.js.v @@ -0,0 +1,6 @@ +module builtin + +// used to generate JS throw statements. +pub fn js_throw(s any) { + #throw (s instanceof Error ? s : new Error(s)) +} diff --git a/v_windows/v/old/vlib/builtin/js/builtin.v b/v_windows/v/old/vlib/builtin/js/builtin.v new file mode 100644 index 0000000..86b60ad --- /dev/null +++ b/v_windows/v/old/vlib/builtin/js/builtin.v @@ -0,0 +1,96 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. + +module builtin + +fn (a any) toString() + +pub fn println(s any) { + // Quickfix to properly print basic types + // TODO: Add proper detection code for this + JS.console.log(s.toString()) +} + +pub fn print(s any) { + // TODO + // $if js.node { + JS.process.stdout.write(s.toString()) + // } $else { + // panic('Cannot `print` in a browser, use `println` instead') + // } +} + +pub fn eprintln(s any) { + JS.console.error(s.toString()) +} + +pub fn eprint(s any) { + // TODO + // $if js.node { + JS.process.stderr.write(s.toString()) + // } $else { + // panic('Cannot `eprint` in a browser, use `eprintln` instead') + // } +} + +// Exits the process in node, and halts execution in the browser +// because `process.exit` is undefined. Workaround for not having +// a 'real' way to exit in the browser. +pub fn exit(c int) { + JS.process.exit(c) + js_throw('exit($c)') +} + +pub fn unwrap(opt any) any { + o := &Option(opt) + if o.state != 0 { + js_throw(o.err) + } + return opt +} + +pub fn panic(s string) { + eprintln('V panic: $s') + exit(1) +} + +struct Option { + state byte + err Error +} + +pub struct Error { +pub: + msg string + code int +} + +pub fn (o Option) str() string { + if o.state == 0 { + return 'Option{ ok }' + } + if o.state == 1 { + return 'Option{ none }' + } + return 'Option{ error: "$o.err" }' +} + +pub fn error(s string) Option { + return Option{ + state: 2 + err: Error{ + msg: s + } + } +} + +pub fn error_with_code(s string, code int) Option { + return Option{ + state: 2 + err: Error{ + msg: s + code: code + } + } +} diff --git a/v_windows/v/old/vlib/builtin/js/byte.js.v b/v_windows/v/old/vlib/builtin/js/byte.js.v new file mode 100644 index 0000000..a2508db --- /dev/null +++ b/v_windows/v/old/vlib/builtin/js/byte.js.v @@ -0,0 +1,8 @@ +module builtin + +pub fn (b byte) is_space() bool { + mut result := false + #result = /^\s*$/.test(String.fromCharCode(b)) + + return result +} diff --git a/v_windows/v/old/vlib/builtin/js/int.js.v b/v_windows/v/old/vlib/builtin/js/int.js.v new file mode 100644 index 0000000..08e52a9 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/js/int.js.v @@ -0,0 +1,8 @@ +module builtin + +pub fn (i int) str() string { + mut res := '' + #res = new builtin.string( i ) + + return res +} diff --git a/v_windows/v/old/vlib/builtin/js/jsfns.js.v b/v_windows/v/old/vlib/builtin/js/jsfns.js.v new file mode 100644 index 0000000..277c702 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/js/jsfns.js.v @@ -0,0 +1,125 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. + +// This file contains JS functions present in both node and the browser. +// They have been ported from their TypeScript definitions. + +module builtin + +pub struct JS.Number {} + +pub struct JS.String { + length JS.Number +} + +pub struct JS.Boolean {} + +pub struct JS.Array { + length JS.Number +} + +pub struct JS.Map {} + +// browser: https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/Error +// node: https://nodejs.org/api/errors.html#errors_class_error +pub struct JS.Error { +pub: + name string + message string + stack string +} + +// Type prototype functions +fn (v JS.String) toString() JS.String +fn (v JS.Number) toString() JS.String +fn (v JS.Boolean) toString() JS.String +fn (v JS.Array) toString() JS.String +fn (v JS.Map) toString() JS.String + +// Hack for "`[]JS.String` is not a struct" when returning arr.length or arr.len +// TODO: Fix []JS.String not a struct error +fn native_str_arr_len(arr []JS.String) int { + len := 0 + #len = arr.length + + return len +} + +// Top level functions +fn JS.eval(string) any +fn JS.parseInt(string, f64) JS.Number +fn JS.parseFloat(string) JS.Number +fn JS.isNaN(f64) bool +fn JS.isFinite(f64) bool +fn JS.decodeURI(string) string +fn JS.decodeURIComponent(string) string +fn JS.encodeURI(string) string + +type EncodeURIComponentArg = bool | f64 | string + +fn JS.encodeURIComponent(EncodeURIComponentArg) string +fn JS.escape(string) string +fn JS.unescape(string) string + +// console +fn JS.console.assert(bool, ...any) +fn JS.console.clear() +fn JS.console.count(string) +fn JS.console.countReset(string) +fn JS.console.debug(...any) +fn JS.console.dir(any, any) +fn JS.console.dirxml(...any) +fn JS.console.error(...any) +fn JS.console.exception(string, ...any) +fn JS.console.group(...any) +fn JS.console.groupCollapsed(...any) +fn JS.console.groupEnd() +fn JS.console.info(...any) +fn JS.console.log(...any) +fn JS.console.table(any, []string) +fn JS.console.time(string) +fn JS.console.timeEnd(string) +fn JS.console.timeLog(string, ...any) +fn JS.console.timeStamp(string) +fn JS.console.trace(...any) +fn JS.console.warn(...any) + +// Math +fn JS.Math.abs(f64) f64 +fn JS.Math.acos(f64) f64 +fn JS.Math.asin(f64) f64 +fn JS.Math.atan(f64) f64 +fn JS.Math.atan2(f64, f64) f64 +fn JS.Math.ceil(f64) f64 +fn JS.Math.cos(f64) f64 +fn JS.Math.exp(f64) f64 +fn JS.Math.floor(f64) f64 +fn JS.Math.log(f64) f64 +fn JS.Math.max(...f64) f64 +fn JS.Math.min(...f64) f64 +fn JS.Math.pow(f64, f64) f64 +fn JS.Math.random() f64 +fn JS.Math.round(f64) f64 +fn JS.Math.sin(f64) f64 +fn JS.Math.sqrt(f64) f64 +fn JS.Math.tan(f64) f64 + +// JSON +fn JS.JSON.stringify(any) string +fn JS.JSON.parse(string) any + +// String +fn (v JS.String) slice(a int, b int) JS.String +fn (v JS.String) split(dot JS.String) []JS.String +fn (s JS.String) indexOf(needle JS.String) int +fn (s JS.String) lastIndexOf(needle JS.String) int + +fn (s JS.String) charAt(i int) JS.String +fn (s JS.String) charCodeAt(i int) byte +fn (s JS.String) toUpperCase() JS.String +fn (s JS.String) toLowerCase() JS.String +fn (s JS.String) concat(a JS.String) JS.String +fn (s JS.String) includes(substr JS.String) bool +fn (s JS.String) endsWith(substr JS.String) bool +fn (s JS.String) startsWith(substr JS.String) bool diff --git a/v_windows/v/old/vlib/builtin/js/jsfns_browser.js.v b/v_windows/v/old/vlib/builtin/js/jsfns_browser.js.v new file mode 100644 index 0000000..72daec3 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/js/jsfns_browser.js.v @@ -0,0 +1,59 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. + +// This file contains JS functions only present in the browser. +// They have been ported from their TypeScript definitions. + +module builtin + +// Window +fn JS.atob(string) string +fn JS.btoa(string) string +fn JS.clearInterval(int) +fn JS.clearTimeout(int) + +// fn JS.createImageBitmap(ImageBitmapSource, ImageBitmapOptions) Promise +// fn JS.createImageBitmap(ImageBitmapSource, int, int, int, int, ImageBitmapOptions) Promise + +// TODO: js async attribute +// [js_async] +// fn JS.fetch(RequestInfo, RequestInit) Promise +fn JS.queueMicrotask(fn ()) +fn JS.setInterval(any, int, ...any) int +fn JS.setTimeout(any, int, ...any) int + +fn JS.alert(any) +fn JS.blur() +fn JS.captureEvents() +fn JS.close() +fn JS.confirm(string) bool + +// fn JS.departFocus(NavigationReason, FocusNavigationOrigin) +fn JS.focus() + +// fn JS.getComputedStyle(Element, string | null) CSSStyleDeclaration +// fn JS.getMatchedCSSRules(Element, string | null) CSSRuleList +// fn JS.getSelection() Selection | null +// fn JS.matchMedia(string) MediaQueryList +fn JS.moveBy(int, int) +fn JS.moveTo(int, int) +fn JS.msWriteProfilerMark(string) + +// fn JS.open(string, string, string, bool) ?Window +// fn JS.postMessage(any, string, []Transferable) +fn JS.print() +fn JS.prompt(string, string) ?string +fn JS.releaseEvents() +fn JS.resizeBy(int, int) +fn JS.resizeTo(int, int) + +// fn JS.scroll(ScrollToOptions) +fn JS.scroll(int, int) + +// fn JS.scrollBy(ScrollToOptions) +fn JS.scrollBy(int, int) + +// fn JS.scrollTo(ScrollToOptions) +fn JS.scrollTo(int, int) +fn JS.stop() diff --git a/v_windows/v/old/vlib/builtin/js/jsfns_node.js.v b/v_windows/v/old/vlib/builtin/js/jsfns_node.js.v new file mode 100644 index 0000000..6f65629 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/js/jsfns_node.js.v @@ -0,0 +1,31 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. + +// This file contains JS functions only present in node.js. +// They have been ported from their TypeScript definitions. + +module builtin + +pub struct JS.node_process { +pub: + arch string + argsv []string + env []string + platform string + version string + // TODO: add all properties +} + +// hack to access process properties +pub fn js_node_process() JS.node_process { + #return process + + return JS.node_process{} +} + +fn JS.process.exit(int) +fn JS.process.stdout.write(string) bool +fn JS.process.stdout.writeln(string) bool +fn JS.process.stderr.write(string) bool +fn JS.process.stderr.writeln(string) bool diff --git a/v_windows/v/old/vlib/builtin/js/map.js.v b/v_windows/v/old/vlib/builtin/js/map.js.v new file mode 100644 index 0000000..894d614 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/js/map.js.v @@ -0,0 +1,16 @@ +module builtin + +struct map { + m JS.Map + len int +} + +// Removes the mapping of a particular key from the map. +[unsafe] +pub fn (mut m map) delete(key voidptr) { + #m.map.delete(key) +} + +pub fn (m &map) free() {} + +#map.prototype[Symbol.iterator] = function () { return this.map[Symbol.iterator](); } diff --git a/v_windows/v/old/vlib/builtin/js/string.js.v b/v_windows/v/old/vlib/builtin/js/string.js.v new file mode 100644 index 0000000..e7d2708 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/js/string.js.v @@ -0,0 +1,466 @@ +module builtin + +pub struct string { +pub: + str JS.String + len int +} + +pub fn (s string) slice(a int, b int) string { + return string(s.str.slice(a, b)) +} + +pub fn (s string) after(dot string) string { + return string(s.str.slice(s.str.lastIndexOf(dot.str) + 1, int(s.str.length))) +} + +pub fn (s string) after_char(dot byte) string { + // TODO: Implement after byte + return s +} + +pub fn (s string) all_after(dot string) string { + return string(s.str.slice(s.str.indexOf(dot.str) + 1, int(s.str.length))) +} + +// why does this exist? +pub fn (s string) all_after_last(dot string) string { + return s.after(dot) +} + +pub fn (s string) all_before(dot string) string { + return string(s.str.slice(0, s.str.indexOf(dot.str))) +} + +pub fn (s string) all_before_last(dot string) string { + return string(s.str.slice(0, s.str.lastIndexOf(dot.str))) +} + +pub fn (s string) bool() bool { + return s == 'true' +} + +pub fn (s string) split(dot string) []string { + mut arr := s.str.split(dot.str).map(string(it)) + #arr = new array(arr) + + return arr +} + +pub fn (s string) bytes() []byte { + sep := '' + mut arr := s.str.split(sep.str).map(it.charCodeAt(0)) + #arr = new array(arr) + + return arr +} + +pub fn (s string) capitalize() string { + part := string(s.str.slice(1, int(s.str.length))) + return string(s.str.charAt(0).toUpperCase().concat(part.str)) +} + +pub fn (s string) clone() string { + return string(s.str) +} + +pub fn (s string) contains(substr string) bool { + return s.str.includes(substr.str) +} + +pub fn (s string) contains_any(chars string) bool { + sep := '' + for x in chars.str.split(sep.str) { + if s.str.includes(x) { + return true + } + } + return false +} + +pub fn (s string) contains_any_substr(chars []string) bool { + for x in chars { + if s.str.includes(x.str) { + return true + } + } + return false +} + +pub fn (s string) count(substr string) int { + // TODO: "error: `[]JS.String` is not a struct" when returning arr.length or arr.len + arr := s.str.split(substr.str) + return native_str_arr_len(arr) +} + +pub fn (s string) ends_with(p string) bool { + return s.str.endsWith(p.str) +} + +pub fn (s string) starts_with(p string) bool { + return s.str.startsWith(p.str) +} + +pub fn (s string) fields() []string { + mut res := []string{} + mut word_start := 0 + mut word_len := 0 + mut is_in_word := false + mut is_space := false + for i, c in s { + is_space = c in [32, 9, 10] + if !is_space { + word_len++ + } + if !is_in_word && !is_space { + word_start = i + is_in_word = true + continue + } + if is_space && is_in_word { + res << s[word_start..word_start + word_len] + is_in_word = false + word_len = 0 + word_start = 0 + continue + } + } + if is_in_word && word_len > 0 { + // collect the remainder word at the end + res << s[word_start..s.len] + } + return res +} + +pub fn (s string) find_between(start string, end string) string { + return string(s.str.slice(s.str.indexOf(start.str), s.str.indexOf(end.str) + 1)) +} + +// unnecessary in the JS backend, implemented for api parity. +pub fn (s string) free() {} + +pub fn (s string) hash() int { + mut h := u32(0) + if h == 0 && s.len > 0 { + for c in s { + h = h * 31 + u32(c) + } + } + return int(h) +} + +// int returns the value of the string as an integer `'1'.int() == 1`. +pub fn (s string) int() int { + return int(JS.parseInt(s)) +} + +// i64 returns the value of the string as i64 `'1'.i64() == i64(1)`. +pub fn (s string) i64() i64 { + return i64(JS.parseInt(s)) +} + +// i8 returns the value of the string as i8 `'1'.i8() == i8(1)`. +pub fn (s string) i8() i8 { + return i8(JS.parseInt(s)) +} + +// i16 returns the value of the string as i16 `'1'.i16() == i16(1)`. +pub fn (s string) i16() i16 { + return i16(JS.parseInt(s)) +} + +// f32 returns the value of the string as f32 `'1.0'.f32() == f32(1)`. +pub fn (s string) f32() f32 { + // return C.atof(&char(s.str)) + return f32(JS.parseFloat(s)) +} + +// f64 returns the value of the string as f64 `'1.0'.f64() == f64(1)`. +pub fn (s string) f64() f64 { + return f64(JS.parseFloat(s)) +} + +// u16 returns the value of the string as u16 `'1'.u16() == u16(1)`. +pub fn (s string) u16() u16 { + return u16(JS.parseInt(s)) +} + +// u32 returns the value of the string as u32 `'1'.u32() == u32(1)`. +pub fn (s string) u32() u32 { + return u32(JS.parseInt(s)) +} + +// u64 returns the value of the string as u64 `'1'.u64() == u64(1)`. +pub fn (s string) u64() u64 { + return u64(JS.parseInt(s)) +} + +// trim_right strips any of the characters given in `cutset` from the right of the string. +// Example: assert ' Hello V d'.trim_right(' d') == ' Hello V' +pub fn (s string) trim_right(cutset string) string { + if s.len < 1 || cutset.len < 1 { + return s.clone() + } + + mut pos := s.len - 1 + + for pos >= 0 { + mut found := false + for cs in cutset { + if s[pos] == cs { + found = true + } + } + if !found { + break + } + pos-- + } + + if pos < 0 { + return '' + } + + return s[..pos + 1] +} + +// trim_left strips any of the characters given in `cutset` from the left of the string. +// Example: assert 'd Hello V developer'.trim_left(' d') == 'Hello V developer' +[direct_array_access] +pub fn (s string) trim_left(cutset string) string { + if s.len < 1 || cutset.len < 1 { + return s.clone() + } + mut pos := 0 + for pos < s.len { + mut found := false + for cs in cutset { + if s[pos] == cs { + found = true + break + } + } + if !found { + break + } + pos++ + } + return s[pos..] +} + +// trim_prefix strips `str` from the start of the string. +// Example: assert 'WorldHello V'.trim_prefix('World') == 'Hello V' +pub fn (s string) trim_prefix(str string) string { + if s.starts_with(str) { + return s[str.len..] + } + return s.clone() +} + +// trim_suffix strips `str` from the end of the string. +// Example: assert 'Hello VWorld'.trim_suffix('World') == 'Hello V' +pub fn (s string) trim_suffix(str string) string { + if s.ends_with(str) { + return s[..s.len - str.len] + } + return s.clone() +} + +// compare_strings returns `-1` if `a < b`, `1` if `a > b` else `0`. +pub fn compare_strings(a &string, b &string) int { + if a < b { + return -1 + } + if a > b { + return 1 + } + return 0 +} + +// compare_strings_reverse returns `1` if `a < b`, `-1` if `a > b` else `0`. +fn compare_strings_reverse(a &string, b &string) int { + if a < b { + return 1 + } + if a > b { + return -1 + } + return 0 +} + +// compare_strings_by_len returns `-1` if `a.len < b.len`, `1` if `a.len > b.len` else `0`. +fn compare_strings_by_len(a &string, b &string) int { + if a.len < b.len { + return -1 + } + if a.len > b.len { + return 1 + } + return 0 +} + +// compare_lower_strings returns the same as compare_strings but converts `a` and `b` to lower case before comparing. +fn compare_lower_strings(a &string, b &string) int { + aa := a.to_lower() + bb := b.to_lower() + return compare_strings(&aa, &bb) +} + +// at returns the byte at index `idx`. +// Example: assert 'ABC'.at(1) == byte(`B`) +fn (s string) at(idx int) byte { + mut result := byte(0) + #result = new byte(s.str.charCodeAt(result)) + + return result +} + +pub fn (s string) to_lower() string { + mut result := '' + #let str = s.str.toLowerCase() + #result = new string(str) + + return result +} + +// TODO: check if that behaves the same as V's own string.replace(old_sub,new_sub): +pub fn (s string) replace(old_sub string, new_sub string) string { + mut result := '' + #result = new string( s.str.replaceAll(old_sub.str, new_sub.str) ) + + return result +} + +pub fn (s string) to_upper() string { + mut result := '' + #let str = s.str.toUpperCase() + #result = new string(str) + + return result +} + +// sort sorts the string array. +pub fn (mut s []string) sort() { + s.sort_with_compare(compare_strings) +} + +// sort_ignore_case sorts the string array using case insesitive comparing. +pub fn (mut s []string) sort_ignore_case() { + s.sort_with_compare(compare_lower_strings) +} + +// sort_by_len sorts the the string array by each string's `.len` length. +pub fn (mut s []string) sort_by_len() { + s.sort_with_compare(compare_strings_by_len) +} + +// str returns a copy of the string +pub fn (s string) str() string { + return s.clone() +} + +pub fn (s string) repeat(count int) string { + mut result := '' + #result = new string(s.str.repeat(count)) + + return result +} + +// TODO(playX): Use this iterator instead of using .split('').map(c => byte(c)) +#function string_iterator(string) { this.stringIteratorFieldIndex = 0; this.stringIteratorIteratedString = string.str; } +#string_iterator.prototype.next = function next() { +#var done = true; +#var value = undefined; +#var position = this.stringIteratorFieldIndex; +#if (position !== -1) { +#var string = this.stringIteratorIteratedString; +#var length = string.length >>> 0; +#if (position >= length) { +#this.stringIteratorFieldIndex = -1; +#} else { +#done = false; +#var first = string.charCodeAt(position); +#if (first < 0xD800 || first > 0xDBFF || position + 1 === length) +#value = new byte(string[position]); +#else { +#value = new byte(string[position]+string[position+1]) +#} +#this.stringIteratorFieldIndex = position + value.length; +#} +#} +#return { +#value, done +#} +#} +#string.prototype[Symbol.iterator] = function () { return new string_iterator(this) } + +// TODO: Make these functions actually work. +// strip_margin allows multi-line strings to be formatted in a way that removes white-space +// before a delimeter. by default `|` is used. +// Note: the delimiter has to be a byte at this time. That means surrounding +// the value in ``. +// +// Example: +// st := 'Hello there, +// |this is a string, +// | Everything before the first | is removed'.strip_margin() +// Returns: +// Hello there, +// this is a string, +// Everything before the first | is removed +pub fn (s string) strip_margin() string { + return s.strip_margin_custom(`|`) +} + +// strip_margin_custom does the same as `strip_margin` but will use `del` as delimiter instead of `|` +[direct_array_access] +pub fn (s string) strip_margin_custom(del byte) string { + mut sep := del + if sep.is_space() { + eprintln('Warning: `strip_margin` cannot use white-space as a delimiter') + eprintln(' Defaulting to `|`') + sep = `|` + } + // don't know how much space the resulting string will be, but the max it + // can be is this big + mut ret := []byte{} + #ret = new array() + + mut count := 0 + for i := 0; i < s.len; i++ { + if s[i] in [10, 13] { + unsafe { + ret[count] = s[i] + } + count++ + // CRLF + if s[i] == 13 && i < s.len - 1 && s[i + 1] == 10 { + unsafe { + ret[count] = s[i + 1] + } + count++ + i++ + } + for s[i] != sep { + i++ + if i >= s.len { + break + } + } + } else { + unsafe { + ret[count] = s[i] + } + count++ + } + } + /* + unsafe { + ret[count] = 0 + return ret.vstring_with_len(count) + }*/ + mut result := '' + #for (let x of ret.arr) result.str += String.fromCharCode(x.val) + + return result +} diff --git a/v_windows/v/old/vlib/builtin/linux_bare/libc_impl.v b/v_windows/v/old/vlib/builtin/linux_bare/libc_impl.v new file mode 100644 index 0000000..735cac6 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/linux_bare/libc_impl.v @@ -0,0 +1,157 @@ +module builtin + +[unsafe] +pub fn memcpy(dest &C.void, src &C.void, n size_t) &C.void { + dest_ := unsafe { &byte(dest) } + src_ := unsafe { &byte(src) } + unsafe { + for i in 0 .. int(n) { + dest_[i] = src_[i] + } + } + return unsafe { dest } +} + +[export: 'malloc'] +[unsafe] +fn __malloc(n size_t) &C.void { + return unsafe { malloc(int(n)) } +} + +[unsafe] +fn strlen(_s &C.void) size_t { + s := unsafe { &byte(_s) } + mut i := 0 + for ; unsafe { s[i] } != 0; i++ {} + return size_t(i) +} + +[unsafe] +fn realloc(old_area &C.void, new_size size_t) &C.void { + if old_area == 0 { + return unsafe { malloc(int(new_size)) } + } + if new_size == size_t(0) { + unsafe { free(old_area) } + return 0 + } + old_size := unsafe { *(&u64(old_area - sizeof(u64))) } + if u64(new_size) <= old_size { + return unsafe { old_area } + } else { + new_area := unsafe { malloc(int(new_size)) } + unsafe { memmove(new_area, old_area, size_t(old_size)) } + unsafe { free(old_area) } + return new_area + } +} + +[unsafe] +fn memset(s &C.void, c int, n size_t) &C.void { + mut s_ := unsafe { &char(s) } + for i in 0 .. int(n) { + unsafe { + s_[i] = char(c) + } + } + return unsafe { s } +} + +[unsafe] +fn memmove(dest &C.void, src &C.void, n size_t) &C.void { + dest_ := unsafe { &byte(dest) } + src_ := unsafe { &byte(src) } + mut temp_buf := unsafe { malloc(int(n)) } + for i in 0 .. int(n) { + unsafe { + temp_buf[i] = src_[i] + } + } + + for i in 0 .. int(n) { + unsafe { + dest_[i] = temp_buf[i] + } + } + unsafe { free(temp_buf) } + return unsafe { dest } +} + +[export: 'calloc'] +[unsafe] +fn __calloc(nmemb size_t, size size_t) &C.void { + new_area := unsafe { malloc(int(nmemb) * int(size)) } + unsafe { memset(new_area, 0, nmemb * size) } + return new_area +} + +fn getchar() int { + x := byte(0) + sys_read(0, &x, 1) + return int(x) +} + +fn memcmp(a &C.void, b &C.void, n size_t) int { + a_ := unsafe { &byte(a) } + b_ := unsafe { &byte(b) } + for i in 0 .. int(n) { + if unsafe { a_[i] != b_[i] } { + unsafe { + return a_[i] - b_[i] + } + } + } + return 0 +} + +[export: 'free'] +[unsafe] +fn __free(ptr &C.void) { + err := mm_free(ptr) + if err != .enoerror { + eprintln('free error:') + panic(err) + } +} + +fn vsprintf(str &char, format &char, ap &byte) int { + panic('vsprintf(): string interpolation is not supported in `-freestanding`') +} + +fn vsnprintf(str &char, size size_t, format &char, ap &byte) int { + panic('vsnprintf(): string interpolation is not supported in `-freestanding`') +} + +// not really needed +fn bare_read(buf &byte, count u64) (i64, Errno) { + return sys_read(0, buf, count) +} + +fn bare_print(buf &byte, len u64) { + sys_write(1, buf, len) +} + +fn bare_eprint(buf &byte, len u64) { + sys_write(2, buf, len) +} + +pub fn write(fd i64, buf &byte, count u64) i64 { + x, _ := sys_write(fd, buf, count) + return x +} + +[noreturn] +fn bare_panic(msg string) { + println('V panic' + msg) + exit(1) +} + +fn bare_backtrace() string { + return 'backtraces are not available with `-freestanding`' +} + +[export: 'exit'] +[noreturn] +fn __exit(code int) { + sys_exit(code) +} diff --git a/v_windows/v/old/vlib/builtin/linux_bare/linux_syscalls.v b/v_windows/v/old/vlib/builtin/linux_bare/linux_syscalls.v new file mode 100644 index 0000000..b0b2c47 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/linux_bare/linux_syscalls.v @@ -0,0 +1,433 @@ +module builtin + +enum SigIndex { + si_signo = 0x00 + si_code = 0x02 + si_pid = 0x04 + si_uid = 0x05 + si_status = 0x06 + si_size = 0x80 +} + +enum Signo { + sighup = 1 // Hangup. + sigint = 2 // Interactive attention signal. + sigquit = 3 // Quit. + sigill = 4 // Illegal instruction. + sigtrap = 5 // Trace/breakpoint trap. + sigabrt = 6 // Abnormal termination. + sigbus = 7 + sigfpe = 8 // Erroneous arithmetic operation. + sigkill = 9 // Killed. + sigusr1 = 10 + sigsegv = 11 // Invalid access to memory. + sigusr2 = 12 + sigpipe = 13 // Broken pipe. + sigalrm = 14 // Alarm clock. + sigterm = 15 // Termination request. + sigstkflt = 16 + sigchld = 17 + sigcont = 18 + sigstop = 19 + sigtstp = 20 + sigttin = 21 // Background read from control terminal. + sigttou = 22 // Background write to control terminal. + sigurg = 23 + sigxcpu = 24 // CPU time limit exceeded. + sigxfsz = 25 // File size limit exceeded. + sigvtalrm = 26 // Virtual timer expired. + sigprof = 27 // Profiling timer expired. + sigwinch = 28 + sigpoll = 29 + sigsys = 31 +} + +// List of all the errors returned by syscalls +enum Errno { + enoerror = 0x00000000 + eperm = 0x00000001 + enoent = 0x00000002 + esrch = 0x00000003 + eintr = 0x00000004 + eio = 0x00000005 + enxio = 0x00000006 + e2big = 0x00000007 + enoexec = 0x00000008 + ebadf = 0x00000009 + echild = 0x0000000a + eagain = 0x0000000b + enomem = 0x0000000c + eacces = 0x0000000d + efault = 0x0000000e + enotblk = 0x0000000f + ebusy = 0x00000010 + eexist = 0x00000011 + exdev = 0x00000012 + enodev = 0x00000013 + enotdir = 0x00000014 + eisdir = 0x00000015 + einval = 0x00000016 + enfile = 0x00000017 + emfile = 0x00000018 + enotty = 0x00000019 + etxtbsy = 0x0000001a + efbig = 0x0000001b + enospc = 0x0000001c + espipe = 0x0000001d + erofs = 0x0000001e + emlink = 0x0000001f + epipe = 0x00000020 + edom = 0x00000021 + erange = 0x00000022 +} + +enum MemProt { + prot_read = 0x1 + prot_write = 0x2 + prot_exec = 0x4 + prot_none = 0x0 + prot_growsdown = 0x01000000 + prot_growsup = 0x02000000 +} + +enum MapFlags { + map_shared = 0x01 + map_private = 0x02 + map_shared_validate = 0x03 + map_type = 0x0f + map_fixed = 0x10 + map_file = 0x00 + map_anonymous = 0x20 + map_huge_shift = 26 + map_huge_mask = 0x3f +} + +// const ( +// fcntlf_dupfd = 0x00000000 +// fcntlf_exlck = 0x00000004 +// fcntlf_getfd = 0x00000001 +// fcntlf_getfl = 0x00000003 +// fcntlf_getlk = 0x00000005 +// fcntlf_getlk64 = 0x0000000c +// fcntlf_getown = 0x00000009 +// fcntlf_getowner_uids = 0x00000011 +// fcntlf_getown_ex = 0x00000010 +// fcntlf_getsig = 0x0000000b +// fcntlf_ofd_getlk = 0x00000024 +// fcntlf_ofd_setlk = 0x00000025 +// fcntlf_ofd_setlkw = 0x00000026 +// fcntlf_owner_pgrp = 0x00000002 +// fcntlf_owner_pid = 0x00000001 +// fcntlf_owner_tid = 0x00000000 +// fcntlf_rdlck = 0x00000000 +// fcntlf_setfd = 0x00000002 +// fcntlf_setfl = 0x00000004 +// fcntlf_setlk = 0x00000006 +// fcntlf_setlk64 = 0x0000000d +// fcntlf_setlkw = 0x00000007 +// fcntlf_setlkw64 = 0x0000000e +// fcntlf_setown = 0x00000008 +// fcntlf_setown_ex = 0x0000000f +// fcntlf_setsig = 0x0000000a +// fcntlf_shlck = 0x00000008 +// fcntlf_unlck = 0x00000002 +// fcntlf_wrlck = 0x00000001 +// fcntllock_ex = 0x00000002 +// fcntllock_mand = 0x00000020 +// fcntllock_nb = 0x00000004 +// fcntllock_read = 0x00000040 +// fcntllock_rw = 0x000000c0 +// fcntllock_sh = 0x00000001 +// fcntllock_un = 0x00000008 +// fcntllock_write = 0x00000080 +// fcntlo_accmode = 0x00000003 +// fcntlo_append = 0x00000400 +// fcntlo_cloexec = 0x00080000 +// fcntlo_creat = 0x00000040 +// fcntlo_direct = 0x00004000 +// fcntlo_directory = 0x00010000 +// fcntlo_dsync = 0x00001000 +// fcntlo_excl = 0x00000080 +// fcntlo_largefile = 0x00008000 +// fcntlo_ndelay = 0x00000800 +// fcntlo_noatime = 0x00040000 +// fcntlo_noctty = 0x00000100 +// fcntlo_nofollow = 0x00020000 +// fcntlo_nonblock = 0x00000800 +// fcntlo_path = 0x00200000 +// fcntlo_rdonly = 0x00000000 +// fcntlo_rdwr = 0x00000002 +// fcntlo_trunc = 0x00000200 +// fcntlo_wronly = 0x00000001 +// ) + +/* +Paraphrased from "man 2 waitid" on Linux + + Upon successful return, waitid() fills in the + following fields of the siginfo_t structure + pointed to by infop: + + si_pid, offset 0x10, int index 0x04: + The process ID of the child. + + si_uid: offset 0x14, int index 0x05 + The real user ID of the child. + + si_signo: offset 0x00, int index 0x00 + Always set to SIGCHLD. + + si_status: ofset 0x18, int index 0x06 + 1 the exit status of the child, as given to _exit(2) + (or exit(3)) (sc_sys.cld_exited) + 2 the signal that caused the child to terminate, stop, + or continue. + 3 The si_code field can be used to determine how to + interpret this field. + + si_code, set to one of (enum Wi_si_code), offset 0x08, int index 0x02: + CLD_EXITED (child called _exit(2)); + CLD_KILLED (child killed by signal); + CLD_DUMPED (child killed by signal, and dumped core); + CLD_STOPPED (child stopped by signal); + CLD_TRAPPED (traced child has trapped); + CLD_CONTINUED (child continued by SIGCONT). +*/ + +const ( + wp_sys_wnohang = u64(0x00000001) + wp_sys_wuntraced = u64(0x00000002) + wp_sys_wstopped = u64(0x00000002) + wp_sys_wexited = u64(0x00000004) + wp_sys_wcontinued = u64(0x00000008) + wp_sys_wnowait = u64(0x01000000) // don't reap, just poll status. + wp_sys___wnothread = u64(0x20000000) // don't wait on children of other threads in this group + wp_sys___wall = u64(0x40000000) // wait on all children, regardless of type + wp_sys___wclone = u64(0x80000000) // wait only on non-sigchld children +) + +// First argument to waitid: +enum WiWhich { + p_all = 0 + p_pid = 1 + p_pgid = 2 +} + +enum WiSiCode { + cld_exited = 1 // child has exited + cld_killed = 2 // child was killed + cld_dumped = 3 // child terminated abnormally + cld_trapped = 4 // traced child has trapped + cld_stopped = 5 // child has stopped + cld_continued = 6 // stopped child has continued +} + +fn split_int_errno(rc_in u64) (i64, Errno) { + rc := i64(rc_in) + if rc < 0 { + return i64(-1), Errno(-rc) + } + return rc, Errno.enoerror +} + +// 0 sys_read +fn sys_read(fd i64, buf &byte, count u64) (i64, Errno) { + return split_int_errno(sys_call3(0, u64(fd), u64(buf), count)) +} + +// 1 sys_write +pub fn sys_write(fd i64, buf &byte, count u64) (i64, Errno) { + return split_int_errno(sys_call3(1, u64(fd), u64(buf), count)) +} + +// 2 sys_open +fn sys_open(filename &byte, flags i64, mode int) (i64, Errno) { + return split_int_errno(sys_call3(2, u64(filename), u64(flags), u64(mode))) +} + +// 3 sys_close +fn sys_close(fd i64) Errno { + return Errno(-i64(sys_call1(3, u64(fd)))) +} + +// 9 sys_mmap +fn sys_mmap(addr &byte, len u64, prot MemProt, flags MapFlags, fildes u64, off u64) (&byte, Errno) { + rc := sys_call6(9, u64(addr), len, u64(prot), u64(flags), fildes, off) + a, e := split_int_errno(rc) + return &byte(a), e +} + +// 11 sys_munmap +fn sys_munmap(addr voidptr, len u64) Errno { + return Errno(-sys_call2(11, u64(addr), len)) +} + +// 22 sys_pipe +fn sys_pipe(filedes &int) Errno { + return Errno(sys_call1(22, u64(filedes))) +} + +// 24 sys_sched_yield +fn sys_sched_yield() Errno { + return Errno(sys_call0(24)) +} + +// 28 sys_madvise +fn sys_madvise(addr voidptr, len u64, advice int) Errno { + return Errno(sys_call3(28, u64(addr), len, u64(advice))) +} + +// 39 sys_getpid +fn sys_getpid() int { + return int(sys_call0(39)) +} + +// 57 sys_fork +fn sys_fork() int { + return int(sys_call0(57)) +} + +// 58 sys_vfork +fn sys_vfork() int { + return int(sys_call0(58)) +} + +// 33 sys_dup2 +fn sys_dup2(oldfd int, newfd int) (i64, Errno) { + return split_int_errno(sys_call2(33, u64(oldfd), u64(newfd))) +} + +// 59 sys_execve +fn sys_execve(filename &byte, argv []&byte, envp []&byte) int { + return int(sys_call3(59, u64(filename), argv.data, envp.data)) +} + +// 60 sys_exit +[noreturn] +fn sys_exit(ec int) { + sys_call1(60, u64(ec)) + for {} +} + +// 102 sys_getuid +fn sys_getuid() int { + return int(sys_call0(102)) +} + +// 247 sys_waitid +fn sys_waitid(which WiWhich, pid int, infop &int, options int, ru voidptr) Errno { + return Errno(sys_call5(247, u64(which), u64(pid), u64(infop), u64(options), u64(ru))) +} + +fn sys_call0(scn u64) u64 { + mut res := u64(0) + asm amd64 { + syscall + ; =a (res) + ; a (scn) + } + return res +} + +fn sys_call1(scn u64, arg1 u64) u64 { + mut res := u64(0) + asm amd64 { + syscall + ; =a (res) + ; a (scn) + D (arg1) + } + return res +} + +fn sys_call2(scn u64, arg1 u64, arg2 u64) u64 { + mut res := u64(0) + asm amd64 { + syscall + ; =a (res) + ; a (scn) + D (arg1) + S (arg2) + } + return res +} + +fn sys_call3(scn u64, arg1 u64, arg2 u64, arg3 u64) u64 { + mut res := u64(0) + asm amd64 { + syscall + ; =a (res) + ; a (scn) + D (arg1) + S (arg2) + d (arg3) + } + return res +} + +fn sys_call4(scn u64, arg1 u64, arg2 u64, arg3 u64, arg4 u64) u64 { + mut res := u64(0) + asm amd64 { + mov r10, arg4 + syscall + ; =a (res) + ; a (scn) + D (arg1) + S (arg2) + d (arg3) + r (arg4) + ; r10 + } + return res +} + +fn sys_call5(scn u64, arg1 u64, arg2 u64, arg3 u64, arg4 u64, arg5 u64) u64 { + mut res := u64(0) + asm amd64 { + mov r10, arg4 + mov r8, arg5 + syscall + ; =a (res) + ; a (scn) + D (arg1) + S (arg2) + d (arg3) + r (arg4) + r (arg5) + ; r10 + r8 + } + return res +} + +fn sys_call6(scn u64, arg1 u64, arg2 u64, arg3 u64, arg4 u64, arg5 u64, arg6 u64) u64 { + mut res := u64(0) + asm amd64 { + mov r10, arg4 + mov r8, arg5 + mov r9, arg6 + syscall + ; =a (res) + ; a (scn) + D (arg1) + S (arg2) + d (arg3) + r (arg4) + r (arg5) + r (arg6) + ; r10 + r8 + r9 + } + return res +} + +asm amd64 { + .globl _start + _start: + call main + mov rax, 60 + xor rdi, rdi + syscall + ret +} diff --git a/v_windows/v/old/vlib/builtin/linux_bare/memory_managment.v b/v_windows/v/old/vlib/builtin/linux_bare/memory_managment.v new file mode 100644 index 0000000..9c23dcf --- /dev/null +++ b/v_windows/v/old/vlib/builtin/linux_bare/memory_managment.v @@ -0,0 +1,29 @@ +module builtin + +fn mm_alloc(size u64) (&byte, Errno) { + // BEGIN CONSTS + // the constants need to be here, since the initialization of other constants, + // which happen before these ones would, require malloc + mem_prot := MemProt(int(MemProt.prot_read) | int(MemProt.prot_write)) + map_flags := MapFlags(int(MapFlags.map_private) | int(MapFlags.map_anonymous)) + // END CONSTS + + a, e := sys_mmap(&byte(0), size + sizeof(u64), mem_prot, map_flags, -1, 0) + if e == .enoerror { + unsafe { + mut ap := &u64(a) + *ap = size + x2 := &byte(a + sizeof(u64)) + return x2, e + } + } + return &byte(0), e +} + +fn mm_free(addr &byte) Errno { + unsafe { + ap := &u64(addr - sizeof(u64)) + size := *ap + return sys_munmap(addr - sizeof(u64), size + sizeof(u64)) + } +} diff --git a/v_windows/v/old/vlib/builtin/linux_bare/old/.checks/.gitignore b/v_windows/v/old/vlib/builtin/linux_bare/old/.checks/.gitignore new file mode 100644 index 0000000..4132964 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/linux_bare/old/.checks/.gitignore @@ -0,0 +1,5 @@ +checks +linuxsys/linuxsys +string/string +consts/consts +structs/structs diff --git a/v_windows/v/old/vlib/builtin/linux_bare/old/.checks/checks.v b/v_windows/v/old/vlib/builtin/linux_bare/old/.checks/checks.v new file mode 100644 index 0000000..8fd3575 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/linux_bare/old/.checks/checks.v @@ -0,0 +1,32 @@ +module main + +import os + +fn failed (msg string) { + println ("!!! failed: $msg") +} + +fn passed (msg string) { + println (">>> passed: $msg") +} + + +fn vcheck(vfile string) { + run_check := "v -user_mod_path . -freestanding run " + if 0 == os.system("$run_check $vfile/${vfile}.v") { + passed(run_check) + } else { + failed(run_check) + } + os.system("ls -lh $vfile/$vfile") + os.system("rm -f $vfile/$vfile") +} + +fn main() { + vcheck("linuxsys") + vcheck("string") + vcheck("consts") + vcheck("structs") + exit(0) +} + diff --git a/v_windows/v/old/vlib/builtin/linux_bare/old/.checks/consts/consts.v b/v_windows/v/old/vlib/builtin/linux_bare/old/.checks/consts/consts.v new file mode 100644 index 0000000..eee2c61 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/linux_bare/old/.checks/consts/consts.v @@ -0,0 +1,22 @@ +module main +import forkedtest + +const ( + integer1 = 111 + integer2 = 222 + integer3 = integer1+integer2 + integer9 = integer3 * 3 + abc = "123" +) + +fn check_const_initialization() { + assert abc == "123" + assert integer9 == 999 +} + +fn main(){ + mut fails := 0 + fails += forkedtest.normal_run(check_const_initialization, "check_const_initialization") + assert fails == 0 + sys_exit(0) +} diff --git a/v_windows/v/old/vlib/builtin/linux_bare/old/.checks/forkedtest/forkedtest.v b/v_windows/v/old/vlib/builtin/linux_bare/old/.checks/forkedtest/forkedtest.v new file mode 100644 index 0000000..6d9272c --- /dev/null +++ b/v_windows/v/old/vlib/builtin/linux_bare/old/.checks/forkedtest/forkedtest.v @@ -0,0 +1,49 @@ +module forkedtest + +pub fn run (op fn(), label string, code Wi_si_code, status int) int { + child := sys_fork() + if child == 0 { + op() + sys_exit(0) + } + + siginfo := []int{len:int(Sig_index.si_size)} + + e := sys_waitid(.p_pid, child, intptr(&siginfo[0]), .wexited, 0) + + assert e == .enoerror + assert siginfo[int(Sig_index.si_pid)] == child + assert siginfo[int(Sig_index.si_signo)] == int(Signo.sigchld) + assert siginfo[int(Sig_index.si_uid)] == sys_getuid() + + r_code := siginfo[Sig_index.si_code] + r_status := siginfo[Sig_index.si_status] + + print("+++ ") + print(label) + if (int(code) == r_code) && (status == r_status) { + println(" PASSED") + return 0 + } + println(" FAILED") + + if int(code) != r_code { + print(">> Expecting si_code 0x") + println(i64_str(int(code),16)) + print(">> Got 0x") + println(i64_str(r_code,16)) + } + + if status != r_status { + print(">> Expecting status 0x") + println(i64_str(status,16)) + print(">> Got 0x") + println(i64_str(r_status,16)) + } + + return 1 +} + +pub fn normal_run (op fn(), label string) int { + return run (op, label, .cld_exited, 0) +} diff --git a/v_windows/v/old/vlib/builtin/linux_bare/old/.checks/linuxsys/linuxsys.v b/v_windows/v/old/vlib/builtin/linux_bare/old/.checks/linuxsys/linuxsys.v new file mode 100644 index 0000000..cca5cb6 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/linux_bare/old/.checks/linuxsys/linuxsys.v @@ -0,0 +1,300 @@ +module main +import forkedtest + +const ( + sample_text_file1 = "" +) + +fn check_fork_minimal () { + child := sys_fork() + ec := 100 + if child == 0 { + println("child") + sys_exit(ec) + } + siginfo := [ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0] + + e := sys_waitid(.p_pid, child, intptr(siginfo.data) , .wexited, 0) + + assert e == .enoerror + //println(i64_tos(buffer0,80,siginfo[Sig_index.si_code],16)) + assert siginfo[Sig_index.si_code] == int(Wi_si_code.cld_exited) + assert siginfo[Sig_index.si_pid] == child + assert siginfo[Sig_index.si_status] == ec + assert siginfo[Sig_index.si_signo] == int(Signo.sigchld) + assert siginfo[Sig_index.si_uid] == sys_getuid() +} + +fn check_read_write_pipe() { + // Checks the following system calls: + // sys_pipe + // sys_write + // sys_read + // sys_close + // + buffer0 := []byte{len:(128)} + buffer := byteptr(buffer0.data) + + fd := [-1, -1] + + assert fd[0] == -1 + assert fd[1] == -1 + + a := sys_pipe(intptr(&fd[0])) + + assert a == .enoerror + + assert fd[0] != -1 + assert fd[1] != -1 + + test_data := "test_data" + b := test_data.len + 1 + c1, e1 := sys_write (fd[1], test_data.str, u64(b)) + + assert e1 == .enoerror + assert c1 == b + + c2, e2 := sys_read(fd[0], buffer, u64(b)) + + assert e2 == .enoerror + assert c2 == b + + assert buffer[b-1] == 0 + + for i in 0..b { + assert test_data[i] == buffer[i] + } + + assert sys_close(fd[0]) == .enoerror + assert sys_close(fd[1]) == .enoerror + + assert sys_close(-1) == .ebadf +} + +fn check_read_file() { + /* + Checks the following system calls: + sys_read + sys_write + sys_close + sys_open + */ + buffer0 := []byte{len:(128)} + buffer := byteptr(buffer0.data) + + test_file := "sample_text1.txt" + sample_text := "Do not change this text.\n" + fd, ec := sys_open(test_file.str, .o_rdonly, 0) + assert fd > 0 + assert ec == .enoerror + n := sample_text.len + c, e := sys_read(fd, buffer, u64(n*2)) + assert e == .enoerror + assert c == n + for i in 0..n { + assert sample_text[i] == buffer[i] + } + assert sys_close(fd) == .enoerror +} + +fn check_open_file_fail() { + fd1, ec1 := sys_open("./nofilehere".str, .o_rdonly, 0) + assert fd1 == -1 + assert ec1 == .enoent +} + +/* +fn check_print() { + println ("checking print and println") + + a := sys_pipe(intptr(fd)) + assert a != -1 + assert fd[0] != -1 + assert fd[1] != -1 + + //sys_dup2 + println ("print and println passed") +} +*/ + +fn check_munmap_fail() { + ec := sys_munmap(-16384,8192) + assert ec == .einval +} + +fn check_mmap_one_page() { + mp := int(Mm_prot.prot_read) | int(Mm_prot.prot_write) + mf := int(Map_flags.map_private) | int(Map_flags.map_anonymous) + mut a, e := sys_mmap(0, u64(Linux_mem.page_size), Mm_prot(mp), Map_flags(mf), -1, 0) + + assert e == .enoerror + assert a != byteptr(-1) + + for i in 0..int(Linux_mem.page_size) { + b := i & 0xFF + a[i] = b + assert a[i] == b + } + + ec := sys_munmap(a, u64(Linux_mem.page_size)) + assert ec == .enoerror +} + +fn check_mm_pages() { + for i in 0 .. int(Linux_mem.page_size)-4 { + assert u32(1) == mm_pages(u64(i)) + } + for i in int(Linux_mem.page_size)-3 .. (int(Linux_mem.page_size)*2)-4 { + assert u32(2) == mm_pages(u64(i)) + } + for i in (int(Linux_mem.page_size)*2)-3 .. (int(Linux_mem.page_size)*3)-4 { + assert u32(3) == mm_pages(u64(i)) + } +} + +//pub fn mm_alloc(size u64) (voidptr, Errno) + +fn check_mm_alloc() { + for i in 1 .. 2000 { + size := u64(i*1000) + pages := mm_pages(size) + mut a, e := mm_alloc(size) + + assert e == .enoerror + ap := intptr(a-4) + assert *ap == int(pages) + assert e == .enoerror + assert !isnil(a) + + if (i%111) == 0 { + for j in 0 .. int(size) { + b := j & 0xFF + a[j] = b + assert b == int(a[j]) + } + } + + mfa := mm_free(a) + + assert mfa == .enoerror + } +} + +fn check_int_array_ro() { + a := [100,110,120,130] + assert a.len == 4 + assert a[0] == 100 + assert a[1] == 110 + assert a[2] == 120 + assert a[3] == 130 +} + +fn check_int_array_rw() { + mut a := [-10,-11,-12,-13] + assert a.len == 4 + assert a[0] == -10 + assert a[1] == -11 + assert a[2] == -12 + assert a[3] == -13 + for i in 0..a.len { + b := -a[i] * 10 + a[i] = b + assert a[i] == b + } + assert a[3] == 130 +} + +fn check_int64_array_ro() { + a := [i64(1000),1100,1200,1300,1400] + assert a.len == 5 + assert a[0] == 1000 + assert a[1] == 1100 + assert a[2] == 1200 + assert a[3] == 1300 + assert a[4] == 1400 +} + +fn check_voidptr_array_ro() { + a := [ + voidptr(10000), + voidptr(11000), + voidptr(12000), + voidptr(13000), + voidptr(14000), + voidptr(15000) + ] + assert a.len == 6 + assert a[0] == voidptr(10000) + assert a[1] == voidptr(11000) + assert a[2] == voidptr(12000) + assert a[3] == voidptr(13000) + assert a[4] == voidptr(14000) + assert a[5] == voidptr(15000) +} + +fn check_voidptr_array_rw() { + mut a := [ + voidptr(-1), + voidptr(-1), + voidptr(-1), + voidptr(-1), + voidptr(-1), + voidptr(-1) + ] + assert a.len == 6 + + assert a[0] == voidptr(-1) + assert a[1] == voidptr(-1) + assert a[2] == voidptr(-1) + assert a[3] == voidptr(-1) + assert a[4] == voidptr(-1) + assert a[5] == voidptr(-1) + + a[0] = voidptr(100000) + assert a[0] == voidptr(100000) + + a[1] = voidptr(110000) + assert a[1] == voidptr(110000) + + a[2] = voidptr(120000) + assert a[2] == voidptr(120000) + + a[3] = voidptr(130000) + assert a[3] == voidptr(130000) + + a[4] = voidptr(140000) + assert a[4] == voidptr(140000) + + a[5] = voidptr(150000) + assert a[5] == voidptr(150000) +} + + +fn main() { + mut fails := 0 + fails += forkedtest.normal_run(check_fork_minimal, "check_fork_minimal") + fails += forkedtest.normal_run(check_munmap_fail, "check_munmap_fail") + fails += forkedtest.normal_run(check_mmap_one_page, "check_mmap_one_page") + fails += forkedtest.normal_run(check_mm_pages, "check_mm_pages") + fails += forkedtest.normal_run(check_mm_alloc, "check_mm_alloc") + fails += forkedtest.normal_run(check_read_write_pipe, "check_read_write_pipe") + fails += forkedtest.normal_run(check_read_file, "check_read_file") + // check_print() + fails += forkedtest.normal_run(check_open_file_fail, "check_open_file_fail") + fails += forkedtest.normal_run(check_int_array_ro, "check_int_array_ro") + fails += forkedtest.normal_run(check_int_array_rw, "check_int_array_rw") + fails += forkedtest.normal_run(check_int64_array_ro, "check_int64_array_ro") + fails += forkedtest.normal_run(check_voidptr_array_ro, "check_voidptr_array_ro") + fails += forkedtest.normal_run(check_voidptr_array_rw, "check_voidptr_array_rw") + + assert fails == 0 + sys_exit(0) +} diff --git a/v_windows/v/old/vlib/builtin/linux_bare/old/.checks/readme.md b/v_windows/v/old/vlib/builtin/linux_bare/old/.checks/readme.md new file mode 100644 index 0000000..03c1b30 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/linux_bare/old/.checks/readme.md @@ -0,0 +1,5 @@ +In this directory: +``` +v run checks.v +``` + diff --git a/v_windows/v/old/vlib/builtin/linux_bare/old/.checks/sample_text1.txt b/v_windows/v/old/vlib/builtin/linux_bare/old/.checks/sample_text1.txt new file mode 100644 index 0000000..f06e75a --- /dev/null +++ b/v_windows/v/old/vlib/builtin/linux_bare/old/.checks/sample_text1.txt @@ -0,0 +1 @@ +Do not change this text. diff --git a/v_windows/v/old/vlib/builtin/linux_bare/old/.checks/string/string.v b/v_windows/v/old/vlib/builtin/linux_bare/old/.checks/string/string.v new file mode 100644 index 0000000..54c06e7 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/linux_bare/old/.checks/string/string.v @@ -0,0 +1,63 @@ +module main +import forkedtest + +fn check_string_eq () { + assert "monkey" != "rat" + some_animal := "a bird" + assert some_animal == "a bird" +} + +fn check_i64_tos() { + buffer0 := []byte{len:(128)} + buffer := byteptr(buffer0.data) + + s0 := i64_tos(buffer, 70, 140, 10) + assert s0 == "140" + + s1 := i64_tos(buffer, 70, -160, 10) + assert s1 == "-160" + + s2 := i64_tos(buffer, 70, 65537, 16) + assert s2 == "10001" + + s3 := i64_tos(buffer, 70, -160000, 10) + assert s3 == "-160000" +} + +fn check_i64_str() { + assert "141" == i64_str(141, 10) + assert "-161" == i64_str(-161, 10) + assert "10002" == i64_str(65538, 16) + assert "-160001" == i64_str(-160001, 10) +} + +fn check_str_clone() { + a := i64_str(1234,10) + b := a.clone() + assert a == b + c := i64_str(-6789,10).clone() + assert c == "-6789" +} + +fn check_string_add_works(){ + abc := 'abc' + combined := 'a' + 'b' + 'c' + assert abc.len == combined.len + assert abc[0] == combined[0] + assert abc[1] == combined[1] + assert abc[2] == combined[2] + assert abc[0] == `a` + assert abc == combined +} + +fn main () { + mut fails := 0 + fails += forkedtest.normal_run(check_string_eq, "check_string_eq") + fails += forkedtest.normal_run(check_i64_tos, "check_i64_tos") + fails += forkedtest.normal_run(check_i64_str, "check_i64_str") + fails += forkedtest.normal_run(check_str_clone, "check_str_clone") + fails += forkedtest.normal_run(check_string_add_works, "check_string_add_works") + assert fails == 0 + sys_exit(0) +} + diff --git a/v_windows/v/old/vlib/builtin/linux_bare/old/.checks/structs/structs.v b/v_windows/v/old/vlib/builtin/linux_bare/old/.checks/structs/structs.v new file mode 100644 index 0000000..f63285d --- /dev/null +++ b/v_windows/v/old/vlib/builtin/linux_bare/old/.checks/structs/structs.v @@ -0,0 +1,42 @@ +module main +import forkedtest + +struct SimpleEmptyStruct{ +} + +struct NonEmptyStruct{ + x int + y int + z int +} + +fn check_simple_empty_struct(){ + s := SimpleEmptyStruct{} + addr_s := &s + str_addr_s := ptr_str( addr_s ) + assert !isnil(addr_s) + assert str_addr_s.len > 3 + println(str_addr_s) +} + +fn check_non_empty_struct(){ + a := NonEmptyStruct{1,2,3} + b := NonEmptyStruct{4,5,6} + assert sizeof(NonEmptyStruct) > 0 + assert sizeof(SimpleEmptyStruct) < sizeof(NonEmptyStruct) + assert a.x == 1 + assert a.y == 2 + assert a.z == 3 + assert b.x + b.y + b.z == 15 + assert ptr_str(&a) != ptr_str(&b) + println('sizeof SimpleEmptyStruct:' + i64_str( sizeof(SimpleEmptyStruct) , 10 )) + println('sizeof NonEmptyStruct:' + i64_str( sizeof(NonEmptyStruct) , 10 )) +} + +fn main(){ + mut fails := 0 + fails += forkedtest.normal_run(check_simple_empty_struct, "check_simple_empty_struct") + fails += forkedtest.normal_run(check_non_empty_struct, "check_non_empty_struct") + assert fails == 0 + sys_exit(0) +} diff --git a/v_windows/v/old/vlib/builtin/linux_bare/old/array_bare.v b/v_windows/v/old/vlib/builtin/linux_bare/old/array_bare.v new file mode 100644 index 0000000..b92214f --- /dev/null +++ b/v_windows/v/old/vlib/builtin/linux_bare/old/array_bare.v @@ -0,0 +1,53 @@ +module builtin + +pub struct array { +pub: + data voidptr + len int + cap int + element_size int +} + +// for now off the stack +fn new_array_from_c_array(len int, cap int, elm_size int, c_array voidptr) array { + arr := array{ + len: len + cap: cap + element_size: elm_size + data: c_array + } + return arr +} + +// Private function. Used to implement array[] operator +fn (a array) get(i int) voidptr { + if i < 0 || i >= a.len { + panic('array.get: index out of range') // FIXME: (i == $i, a.len == $a.len)') + } + return a.data + i * a.element_size +} + +// Private function. Used to implement assigment to the array element. +fn (mut a array) set(i int, val voidptr) { + if i < 0 || i >= a.len { + panic('array.set: index out of range') // FIXME: (i == $i, a.len == $a.len)') + } + mem_copy(a.data + a.element_size * i, val, a.element_size) +} + +// array.repeat returns new array with the given array elements +// repeated `nr_repeat` times +pub fn (a array) repeat(nr_repeats int) array { + assert nr_repeats >= 0 + + arr := array{ + len: nr_repeats * a.len + cap: nr_repeats * a.len + element_size: a.element_size + data: malloc(nr_repeats * a.len * a.element_size) + } + for i in 0 .. nr_repeats { + mem_copy(arr.data + i * a.len * a.element_size, a.data, a.len * a.element_size) + } + return arr +} diff --git a/v_windows/v/old/vlib/builtin/linux_bare/old/builtin_bare.v b/v_windows/v/old/vlib/builtin/linux_bare/old/builtin_bare.v new file mode 100644 index 0000000..a7be853 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/linux_bare/old/builtin_bare.v @@ -0,0 +1,60 @@ +module builtin + +// called by the generated main/init +fn init() { +} + +pub fn isnil(p voidptr) bool { + return p == 0 +} + +pub fn print(s string) { + sys_write(1, s.str, u64(s.len)) +} + +pub fn println(s string) { + print(s) + print('\n') +} + +pub fn panic(s string) { + eprint('V panic: ') + eprintln(s) + sys_exit(1) +} + +// replaces panic when -debug arg is passed +fn panic_debug(line_no int, file string, mod string, fn_name string, s string) { + eprintln('================ V panic ================') + eprint(' module: ') + eprintln('mod') + eprint(' function: ') + eprint(fn_name) + eprintln('()') + eprintln(' file: ') + eprintln(file) + // println(' line: ${line_no}') + eprint(' message: ') + eprintln(s) + eprintln('=========================================') + sys_exit(1) +} + +pub fn eprint(s string) { + if isnil(s.str) { + panic('eprint(NIL)') + } + sys_write(2, s.str, u64(s.len)) +} + +pub fn eprint_ln(s string) { + eprint(s) + eprint('\n') +} + +pub fn eprintln(s string) { + if isnil(s.str) { + panic('eprintln(NIL)') + } + eprint_ln(s) +} diff --git a/v_windows/v/old/vlib/builtin/linux_bare/old/linuxsys_bare.v b/v_windows/v/old/vlib/builtin/linux_bare/old/linuxsys_bare.v new file mode 100644 index 0000000..ddec203 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/linux_bare/old/linuxsys_bare.v @@ -0,0 +1,759 @@ +module builtin + +pub enum Linux_mem { + page_size = 4096 +} + +pub const ( + wp_sys_wnohang = u64(0x00000001) + wp_sys_wuntraced = u64(0x00000002) + wp_sys_wstopped = u64(0x00000002) + wp_sys_wexited = u64(0x00000004) + wp_sys_wcontinued = u64(0x00000008) + wp_sys_wnowait = u64(0x01000000) // don't reap, just poll status. + wp_sys___wnothread = u64(0x20000000) // don't wait on children of other threads in this group + wp_sys___wall = u64(0x40000000) // wait on all children, regardless of type + wp_sys___wclone = u64(0x80000000) // wait only on non-sigchld children +) + +// First argument to waitid: +pub enum Wi_which { + p_all = 0 + p_pid = 1 + p_pgid = 2 +} + +pub enum Wi_si_code { + cld_exited = 1 // child has exited + cld_killed = 2 // child was killed + cld_dumped = 3 // child terminated abnormally + cld_trapped = 4 // traced child has trapped + cld_stopped = 5 // child has stopped + cld_continued = 6 // stopped child has continued +} + +/* +Paraphrased from "man 2 waitid" on Linux + + Upon successful return, waitid() fills in the + following fields of the siginfo_t structure + pointed to by infop: + + si_pid, offset 0x10, int index 0x04: + The process ID of the child. + + si_uid: offset 0x14, int index 0x05 + The real user ID of the child. + + si_signo: offset 0x00, int index 0x00 + Always set to SIGCHLD. + + si_status: ofset 0x18, int index 0x06 + 1 the exit status of the child, as given to _exit(2) + (or exit(3)) (sc_sys.cld_exited) + 2 the signal that caused the child to terminate, stop, + or continue. + 3 The si_code field can be used to determine how to + interpret this field. + + si_code, set to one of (enum Wi_si_code), offset 0x08, int index 0x02: + CLD_EXITED (child called _exit(2)); + CLD_KILLED (child killed by signal); + CLD_DUMPED (child killed by signal, and dumped core); + CLD_STOPPED (child stopped by signal); + CLD_TRAPPED (traced child has trapped); + CLD_CONTINUED (child continued by SIGCONT). +*/ + +pub enum Sig_index { + si_signo = 0x00 + si_code = 0x02 + si_pid = 0x04 + si_uid = 0x05 + si_status = 0x06 + si_size = 0x80 +} + +pub enum Signo { + sighup = 1 // Hangup. + sigint = 2 // Interactive attention signal. + sigquit = 3 // Quit. + sigill = 4 // Illegal instruction. + sigtrap = 5 // Trace/breakpoint trap. + sigabrt = 6 // Abnormal termination. + sigbus = 7 + sigfpe = 8 // Erroneous arithmetic operation. + sigkill = 9 // Killed. + sigusr1 = 10 + sigsegv = 11 // Invalid access to storage. + sigusr2 = 12 + sigpipe = 13 // Broken pipe. + sigalrm = 14 // Alarm clock. + sigterm = 15 // Termination request. + sigstkflt = 16 + sigchld = 17 + sigcont = 18 + sigstop = 19 + sigtstp = 20 + sigttin = 21 // Background read from control terminal. + sigttou = 22 // Background write to control terminal. + sigurg = 23 + sigxcpu = 24 // CPU time limit exceeded. + sigxfsz = 25 // File size limit exceeded. + sigvtalrm = 26 // Virtual timer expired. + sigprof = 27 // Profiling timer expired. + sigwinch = 28 + sigpoll = 29 + sigsys = 31 +} + +pub const ( + fcntlf_dupfd = 0x00000000 + fcntlf_exlck = 0x00000004 + fcntlf_getfd = 0x00000001 + fcntlf_getfl = 0x00000003 + fcntlf_getlk = 0x00000005 + fcntlf_getlk64 = 0x0000000c + fcntlf_getown = 0x00000009 + fcntlf_getowner_uids = 0x00000011 + fcntlf_getown_ex = 0x00000010 + fcntlf_getsig = 0x0000000b + fcntlf_ofd_getlk = 0x00000024 + fcntlf_ofd_setlk = 0x00000025 + fcntlf_ofd_setlkw = 0x00000026 + fcntlf_owner_pgrp = 0x00000002 + fcntlf_owner_pid = 0x00000001 + fcntlf_owner_tid = 0x00000000 + fcntlf_rdlck = 0x00000000 + fcntlf_setfd = 0x00000002 + fcntlf_setfl = 0x00000004 + fcntlf_setlk = 0x00000006 + fcntlf_setlk64 = 0x0000000d + fcntlf_setlkw = 0x00000007 + fcntlf_setlkw64 = 0x0000000e + fcntlf_setown = 0x00000008 + fcntlf_setown_ex = 0x0000000f + fcntlf_setsig = 0x0000000a + fcntlf_shlck = 0x00000008 + fcntlf_unlck = 0x00000002 + fcntlf_wrlck = 0x00000001 + fcntllock_ex = 0x00000002 + fcntllock_mand = 0x00000020 + fcntllock_nb = 0x00000004 + fcntllock_read = 0x00000040 + fcntllock_rw = 0x000000c0 + fcntllock_sh = 0x00000001 + fcntllock_un = 0x00000008 + fcntllock_write = 0x00000080 + fcntlo_accmode = 0x00000003 + fcntlo_append = 0x00000400 + fcntlo_cloexec = 0x00080000 + fcntlo_creat = 0x00000040 + fcntlo_direct = 0x00004000 + fcntlo_directory = 0x00010000 + fcntlo_dsync = 0x00001000 + fcntlo_excl = 0x00000080 + fcntlo_largefile = 0x00008000 + fcntlo_ndelay = 0x00000800 + fcntlo_noatime = 0x00040000 + fcntlo_noctty = 0x00000100 + fcntlo_nofollow = 0x00020000 + fcntlo_nonblock = 0x00000800 + fcntlo_path = 0x00200000 + fcntlo_rdonly = 0x00000000 + fcntlo_rdwr = 0x00000002 + fcntlo_trunc = 0x00000200 + fcntlo_wronly = 0x00000001 +) + +pub enum Errno { + enoerror = 0x00000000 + e2big = 0x00000007 + eacces = 0x0000000d + eagain = 0x0000000b + ebadf = 0x00000009 + ebusy = 0x00000010 + echild = 0x0000000a + edom = 0x00000021 + eexist = 0x00000011 + efault = 0x0000000e + efbig = 0x0000001b + eintr = 0x00000004 + einval = 0x00000016 + eio = 0x00000005 + eisdir = 0x00000015 + emfile = 0x00000018 + emlink = 0x0000001f + enfile = 0x00000017 + enodev = 0x00000013 + enoent = 0x00000002 + enoexec = 0x00000008 + enomem = 0x0000000c + enospc = 0x0000001c + enotblk = 0x0000000f + enotdir = 0x00000014 + enotty = 0x00000019 + enxio = 0x00000006 + eperm = 0x00000001 + epipe = 0x00000020 + erange = 0x00000022 + erofs = 0x0000001e + espipe = 0x0000001d + esrch = 0x00000003 + etxtbsy = 0x0000001a + exdev = 0x00000012 +} + +pub enum Mm_prot { + prot_read = 0x1 + prot_write = 0x2 + prot_exec = 0x4 + prot_none = 0x0 + prot_growsdown = 0x01000000 + prot_growsup = 0x02000000 +} + +pub enum Map_flags { + map_shared = 0x01 + map_private = 0x02 + map_shared_validate = 0x03 + map_type = 0x0f + map_fixed = 0x10 + map_file = 0x00 + map_anonymous = 0x20 + map_huge_shift = 26 + map_huge_mask = 0x3f +} + +fn sys_call0(scn u64) u64 { + res := u64(0) + asm amd64 { + syscall + ; =a (res) + ; a (scn) + } + return res +} + +fn sys_call1(scn u64, arg1 u64) u64 { + res := u64(0) + asm amd64 { + syscall + ; =a (res) + ; a (scn) + D (arg1) + } + return res +} + +fn sys_call2(scn u64, arg1 u64, arg2 u64) u64 { + res := u64(0) + asm amd64 { + syscall + ; =a (res) + ; a (scn) + D (arg1) + S (arg2) + } + return res +} + +fn sys_call3(scn u64, arg1 u64, arg2 u64, arg3 u64) u64 { + res := u64(0) + asm amd64 { + syscall + ; =a (res) + ; a (scn) + D (arg1) + S (arg2) + d (arg3) + } + return res +} + +fn sys_call4(scn u64, arg1 u64, arg2 u64, arg3 u64, arg4 u64) u64 { + res := u64(0) + asm amd64 { + mov r10, arg4 + syscall + ; =a (res) + ; a (scn) + D (arg1) + S (arg2) + d (arg3) + r (arg4) + ; r10 + } + return res +} + +fn sys_call5(scn u64, arg1 u64, arg2 u64, arg3 u64, arg4 u64, arg5 u64) u64 { + res := u64(0) + asm amd64 { + mov r10, arg4 + mov r8, arg5 + syscall + ; =a (res) + ; a (scn) + D (arg1) + S (arg2) + d (arg3) + r (arg4) + r (arg5) + ; r10 + r8 + } + return res +} + +fn sys_call6(scn u64, arg1 u64, arg2 u64, arg3 u64, arg4 u64, arg5 u64, arg6 u64) u64 { + res := u64(0) + asm amd64 { + mov r10, arg4 + mov r8, arg5 + mov r9, arg6 + syscall + ; =a (res) + ; a (scn) + D (arg1) + S (arg2) + d (arg3) + r (arg4) + r (arg5) + r (arg6) + ; r10 + r8 + r9 + } + return res +} + +fn split_int_errno(rc_in u64) (i64, Errno) { + rc := i64(rc_in) + if rc < 0 { + return i64(-1), Errno(-rc) + } + return rc, Errno.enoerror +} + +// 0 sys_read unsigned int fd char *buf size_t count +pub fn sys_read(fd i64, buf &byte, count u64) (i64, Errno) { + return split_int_errno(sys_call3(0, u64(fd), u64(buf), count)) +} + +// 1 sys_write unsigned int fd, const char *buf, size_t count +pub fn sys_write(fd i64, buf &byte, count u64) (i64, Errno) { + return split_int_errno(sys_call3(1, u64(fd), u64(buf), count)) +} + +pub fn sys_open(filename &byte, flags i64, mode int) (i64, Errno) { + // 2 sys_open const char *filename int flags int mode + return split_int_errno(sys_call3(2, u64(filename), u64(flags), u64(mode))) +} + +pub fn sys_close(fd i64) Errno { + // 3 sys_close unsigned int fd + return Errno(-i64(sys_call1(3, u64(fd)))) +} + +// 9 sys_mmap unsigned long addr unsigned long len unsigned long prot unsigned long flags unsigned long fd unsigned long off +pub fn sys_mmap(addr &byte, len u64, prot Mm_prot, flags Map_flags, fildes u64, off u64) (&byte, Errno) { + rc := sys_call6(9, u64(addr), len, u64(prot), u64(flags), fildes, off) + a, e := split_int_errno(rc) + return &byte(a), e +} + +pub fn sys_munmap(addr voidptr, len u64) Errno { + // 11 sys_munmap unsigned long addr size_t len + return Errno(-sys_call2(11, u64(addr), len)) +} + +// 22 sys_pipe int *filedes +pub fn sys_pipe(filedes &int) Errno { + return Errno(sys_call1(22, u64(filedes))) +} + +// 24 sys_sched_yield +pub fn sys_sched_yield() Errno { + return Errno(sys_call0(24)) +} + +pub fn sys_madvise(addr voidptr, len u64, advice int) Errno { + // 28 sys_madvise unsigned long start size_t len_in int behavior + return Errno(sys_call3(28, u64(addr), len, u64(advice))) +} + +// 39 sys_getpid +pub fn sys_getpid() int { + return int(sys_call0(39)) +} + +// 57 sys_fork +pub fn sys_fork() int { + return int(sys_call0(57)) +} + +// 58 sys_vfork +pub fn sys_vfork() int { + return int(sys_call0(58)) +} + +// 33 sys_dup2 unsigned int oldfd unsigned int newfd +pub fn sys_dup2(oldfd int, newfd int) (i64, Errno) { + return split_int_errno(sys_call2(33, u64(oldfd), u64(newfd))) +} + +// 59 sys_execve const char *filename const char *const argv[] const char *const envp[] +// pub fn sys_execve(filename byteptr, argv []byteptr, envp []byteptr) int { +// return sys_call3(59, filename, argv, envp) +//} + +// 60 sys_exit int error_code +pub fn sys_exit(ec int) { + sys_call1(60, u64(ec)) +} + +// 102 sys_getuid +pub fn sys_getuid() int { + return int(sys_call0(102)) +} + +// 247 sys_waitid int which pid_t upid struct siginfo *infop int options struct rusage *ru +pub fn sys_waitid(which Wi_which, pid int, infop &int, options int, ru voidptr) Errno { + return Errno(sys_call5(247, u64(which), u64(pid), u64(infop), u64(options), u64(ru))) +} + +/* +A few years old, but still relevant +https://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/ + +>0 sys_read unsigned int fd char *buf size_t count +>1 sys_write unsigned int fd const char *buf size_t count +>2 sys_open const char *filename int flags int mode +>3 sys_close unsigned int fd +4 sys_stat const char *filename struct stat *statbuf +5 sys_fstat unsigned int fd struct stat *statbuf +6 sys_lstat fconst char *filename struct stat *statbuf +7 sys_poll struct poll_fd *ufds unsigned int nfds long timeout_msecs +8 sys_lseek unsigned int fd off_t offset unsigned int origin +>9 sys_mmap unsigned long addr unsigned long len unsigned long prot unsigned long flags unsigned long fd unsigned long off +10 sys_mprotect unsigned long start size_t len unsigned long prot +>11 sys_munmap unsigned long addr size_t len +12 sys_brk unsigned long brk +13 sys_rt_sigaction int sig const struct sigaction *act struct sigaction *oact size_t sigsetsize +14 sys_rt_sigprocmask int how sigset_t *nset sigset_t *oset size_t sigsetsize +15 sys_rt_sigreturn unsigned long __unused +16 sys_ioctl unsigned int fd unsigned int cmd unsigned long arg +17 sys_pread64 unsigned long fd char *buf size_t count loff_t pos +18 sys_pwrite64 unsigned int fd const char *buf size_t count loff_t pos +19 sys_readv unsigned long fd const struct iovec *vec unsigned long vlen +20 sys_writev unsigned long fd const struct iovec *vec unsigned long vlen +21 sys_access const char *filename int mode +>22 sys_pipe int *filedes +23 sys_select int n fd_set *inp fd_set *outp fd_set*exp struct timeval *tvp +>24 sys_sched_yield +25 sys_mremap unsigned long addr unsigned long old_len unsigned long new_len unsigned long flags unsigned long new_addr +26 sys_msync unsigned long start size_t len int flags +27 sys_mincore unsigned long start size_t len unsigned char *vec +>28 sys_madvise unsigned long start size_t len_in int behavior +29 sys_shmget key_t key size_t size int shmflg +30 sys_shmat int shmid char *shmaddr int shmflg +31 sys_shmctl int shmid int cmd struct shmid_ds *buf +32 sys_dup unsigned int fildes +33 sys_dup2 unsigned int oldfd unsigned int newfd +34 sys_pause +35 sys_nanosleep struct timespec *rqtp struct timespec *rmtp +36 sys_getitimer int which struct itimerval *value +37 sys_alarm unsigned int seconds +38 sys_setitimer int which struct itimerval *value struct itimerval *ovalue +>39 sys_getpid +40 sys_sendfile int out_fd int in_fd off_t *offset size_t count +41 sys_socket int family int type int protocol +42 sys_connect int fd struct sockaddr *uservaddr int addrlen +43 sys_accept int fd struct sockaddr *upeer_sockaddr int *upeer_addrlen +44 sys_sendto int fd void *buff size_t len unsigned flags struct sockaddr *addr int addr_len +45 sys_recvfrom int fd void *ubuf size_t size unsigned flags struct sockaddr *addr int *addr_len +46 sys_sendmsg int fd struct msghdr *msg unsigned flags +47 sys_recvmsg int fd struct msghdr *msg unsigned int flags +48 sys_shutdown int fd int how +49 sys_bind int fd struct sokaddr *umyaddr int addrlen +50 sys_listen int fd int backlog +51 sys_getsockname int fd struct sockaddr *usockaddr int *usockaddr_len +52 sys_getpeername int fd struct sockaddr *usockaddr int *usockaddr_len +53 sys_socketpair int family int type int protocol int *usockvec +54 sys_setsockopt int fd int level int optname char *optval int optlen +55 sys_getsockopt int fd int level int optname char *optval int *optlen +56 sys_clone unsigned long clone_flags unsigned long newsp void *parent_tid void *child_tid +>57 sys_fork +>58 sys_vfork +>59 sys_execve const char *filename const char *const argv[] const char *const envp[] +>60 sys_exit int error_code +61 sys_wait4 pid_t upid int *stat_addr int options struct rusage *ru +62 sys_kill pid_t pid int sig +63 sys_uname struct old_utsname *name +64 sys_semget key_t key int nsems int semflg +65 sys_semop int semid struct sembuf *tsops unsigned nsops +66 sys_semctl int semid int semnum int cmd union semun arg +67 sys_shmdt char *shmaddr +68 sys_msgget key_t key int msgflg +69 sys_msgsnd int msqid struct msgbuf *msgp size_t msgsz int msgflg +70 sys_msgrcv int msqid struct msgbuf *msgp size_t msgsz long msgtyp int msgflg +71 sys_msgctl int msqid int cmd struct msqid_ds *buf +72 sys_fcntl unsigned int fd unsigned int cmd unsigned long arg +73 sys_flock unsigned int fd unsigned int cmd +74 sys_fsync unsigned int fd +75 sys_fdatasync unsigned int fd +76 sys_truncate const char *path long length +77 sys_ftruncate unsigned int fd unsigned long length +78 sys_getdents unsigned int fd struct linux_dirent *dirent unsigned int count +79 sys_getcwd char *buf unsigned long size +80 sys_chdir const char *filename +81 sys_fchdir unsigned int fd +82 sys_rename const char *oldname const char *newname +83 sys_mkdir const char *pathname int mode +84 sys_rmdir const char *pathname +85 sys_creat const char *pathname int mode +86 sys_link const char *oldname const char *newname +87 sys_unlink const char *pathname +88 sys_symlink const char *oldname const char *newname +89 sys_readlink const char *path char *buf int bufsiz +90 sys_chmod const char *filename mode_t mode +91 sys_fchmod unsigned int fd mode_t mode +92 sys_chown const char *filename uid_t user gid_t group +93 sys_fchown unsigned int fd uid_t user gid_t group +94 sys_lchown const char *filename uid_t user gid_t group +95 sys_umask int mask +96 sys_gettimeofday struct timeval *tv struct timezone *tz +97 sys_getrlimit unsigned int resource struct rlimit *rlim +98 sys_getrusage int who struct rusage *ru +99 sys_sysinfo struct sysinfo *info +100 sys_times struct sysinfo *info +101 sys_ptrace long request long pid unsigned long addr unsigned long data +>102 sys_getuid +103 sys_syslog int type char *buf int len +104 sys_getgid +105 sys_setuid uid_t uid +106 sys_setgid gid_t gid +107 sys_geteuid +108 sys_getegid +109 sys_setpgid pid_t pid pid_t pgid +110 sys_getppid +111 sys_getpgrp +112 sys_setsid +113 sys_setreuid uid_t ruid uid_t euid +114 sys_setregid gid_t rgid gid_t egid +115 sys_getgroups int gidsetsize gid_t *grouplist +116 sys_setgroups int gidsetsize gid_t *grouplist +117 sys_setresuid uid_t *ruid uid_t *euid uid_t *suid +118 sys_getresuid uid_t *ruid uid_t *euid uid_t *suid +119 sys_setresgid gid_t rgid gid_t egid gid_t sgid +120 sys_getresgid gid_t *rgid gid_t *egid gid_t *sgid +121 sys_getpgid pid_t pid +122 sys_setfsuid uid_t uid +123 sys_setfsgid gid_t gid +124 sys_getsid pid_t pid +125 sys_capget cap_user_header_t header cap_user_data_t dataptr +126 sys_capset cap_user_header_t header const cap_user_data_t data +127 sys_rt_sigpending sigset_t *set size_t sigsetsize +128 sys_rt_sigtimedwait const sigset_t *uthese siginfo_t *uinfo const struct timespec *uts size_t sigsetsize +129 sys_rt_sigqueueinfo pid_t pid int sig siginfo_t *uinfo +130 sys_rt_sigsuspend sigset_t *unewset size_t sigsetsize +131 sys_sigaltstack const stack_t *uss stack_t *uoss +132 sys_utime char *filename struct utimbuf *times +133 sys_mknod const char *filename umode_t mode unsigned dev +134 sys_uselib NOT IMPLEMENTED +135 sys_personality unsigned int personality +136 sys_ustat unsigned dev struct ustat *ubuf +137 sys_statfs const char *pathname struct statfs *buf +138 sys_fstatfs unsigned int fd struct statfs *buf +139 sys_sysfs int option unsigned long arg1 unsigned long arg2 +140 sys_getpriority int which int who +141 sys_setpriority int which int who int niceval +142 sys_sched_setparam pid_t pid struct sched_param *param +143 sys_sched_getparam pid_t pid struct sched_param *param +144 sys_sched_setscheduler pid_t pid int policy struct sched_param *param +145 sys_sched_getscheduler pid_t pid +146 sys_sched_get_priority_max int policy +147 sys_sched_get_priority_min int policy +148 sys_sched_rr_get_interval pid_t pid struct timespec *interval +149 sys_mlock unsigned long start size_t len +150 sys_munlock unsigned long start size_t len +151 sys_mlockall int flags +152 sys_munlockall +153 sys_vhangup +154 sys_modify_ldt int func void *ptr unsigned long bytecount +155 sys_pivot_root const char *new_root const char *put_old +156 sys__sysctl struct __sysctl_args *args +157 sys_prctl int option unsigned long arg2 unsigned long arg3 unsigned long arg4 unsigned long arg5 +158 sys_arch_prctl struct task_struct *task int code unsigned long *addr +159 sys_adjtimex struct timex *txc_p +160 sys_setrlimit unsigned int resource struct rlimit *rlim +161 sys_chroot const char *filename +162 sys_sync +163 sys_acct const char *name +164 sys_settimeofday struct timeval *tv struct timezone *tz +165 sys_mount char *dev_name char *dir_name char *type unsigned long flags void *data +166 sys_umount2 const char *target int flags +167 sys_swapon const char *specialfile int swap_flags +168 sys_swapoff const char *specialfile +169 sys_reboot int magic1 int magic2 unsigned int cmd void *arg +170 sys_sethostname char *name int len +171 sys_setdomainname char *name int len +172 sys_iopl unsigned int level struct pt_regs *regs +173 sys_ioperm unsigned long from unsigned long num int turn_on +174 sys_create_module REMOVED IN Linux 2.6 +175 sys_init_module void *umod unsigned long len const char *uargs +176 sys_delete_module const chat *name_user unsigned int flags +177 sys_get_kernel_syms REMOVED IN Linux 2.6 +178 sys_query_module REMOVED IN Linux 2.6 +179 sys_quotactl unsigned int cmd const char *special qid_t id void *addr +180 sys_nfsservctl NOT IMPLEMENTED +181 sys_getpmsg NOT IMPLEMENTED +182 sys_putpmsg NOT IMPLEMENTED +183 sys_afs_syscall NOT IMPLEMENTED +184 sys_tuxcall NOT IMPLEMENTED +185 sys_security NOT IMPLEMENTED +186 sys_gettid +187 sys_readahead int fd loff_t offset size_t count +188 sys_setxattr const char *pathname const char *name const void *value size_t size int flags +189 sys_lsetxattr const char *pathname const char *name const void *value size_t size int flags +190 sys_fsetxattr int fd const char *name const void *value size_t size int flags +191 sys_getxattr const char *pathname const char *name void *value size_t size +192 sys_lgetxattr const char *pathname const char *name void *value size_t size +193 sys_fgetxattr int fd const har *name void *value size_t size +194 sys_listxattr const char *pathname char *list size_t size +195 sys_llistxattr const char *pathname char *list size_t size +196 sys_flistxattr int fd char *list size_t size +197 sys_removexattr const char *pathname const char *name +198 sys_lremovexattr const char *pathname const char *name +199 sys_fremovexattr int fd const char *name +200 sys_tkill pid_t pid ing sig +201 sys_time time_t *tloc +202 sys_futex u32 *uaddr int op u32 val struct timespec *utime u32 *uaddr2 u32 val3 +203 sys_sched_setaffinity pid_t pid unsigned int len unsigned long *user_mask_ptr +204 sys_sched_getaffinity pid_t pid unsigned int len unsigned long *user_mask_ptr +205 sys_set_thread_area NOT IMPLEMENTED. Use arch_prctl +206 sys_io_setup unsigned nr_events aio_context_t *ctxp +207 sys_io_destroy aio_context_t ctx +208 sys_io_getevents aio_context_t ctx_id long min_nr long nr struct io_event *events +209 sys_io_submit aio_context_t ctx_id long nr struct iocb **iocbpp +210 sys_io_cancel aio_context_t ctx_id struct iocb *iocb struct io_event *result +211 sys_get_thread_area NOT IMPLEMENTED. Use arch_prctl +212 sys_lookup_dcookie u64 cookie64 long buf long len +213 sys_epoll_create int size +214 sys_epoll_ctl_old NOT IMPLEMENTED +215 sys_epoll_wait_old NOT IMPLEMENTED +216 sys_remap_file_pages unsigned long start unsigned long size unsigned long prot unsigned long pgoff unsigned long flags +217 sys_getdents64 unsigned int fd struct linux_dirent64 *dirent unsigned int count +218 sys_set_tid_address int *tidptr +219 sys_restart_syscall +220 sys_semtimedop int semid struct sembuf *tsops unsigned nsops const struct timespec *timeout +221 sys_fadvise64 int fd loff_t offset size_t len int advice +222 sys_timer_create const clockid_t which_clock struct sigevent *timer_event_spec timer_t *created_timer_id +223 sys_timer_settime timer_t timer_id int flags const struct itimerspec *new_setting struct itimerspec *old_setting +224 sys_timer_gettime timer_t timer_id struct itimerspec *setting +225 sys_timer_getoverrun timer_t timer_id +226 sys_timer_delete timer_t timer_id +227 sys_clock_settime const clockid_t which_clock const struct timespec *tp +228 sys_clock_gettime const clockid_t which_clock struct timespec *tp +229 sys_clock_getres const clockid_t which_clock struct timespec *tp +230 sys_clock_nanosleep const clockid_t which_clock int flags const struct timespec *rqtp struct timespec *rmtp +231 sys_exit_group int error_code +232 sys_epoll_wait int epfd struct epoll_event *events int maxevents int timeout +233 sys_epoll_ctl int epfd int op int fd struct epoll_event *event +234 sys_tgkill pid_t tgid pid_t pid int sig +235 sys_utimes char *filename struct timeval *utimes +236 sys_vserver NOT IMPLEMENTED +237 sys_mbind unsigned long start unsigned long len unsigned long mode unsigned long *nmask unsigned long maxnode unsigned flags +238 sys_set_mempolicy int mode unsigned long *nmask unsigned long maxnode +239 sys_get_mempolicy int *policy unsigned long *nmask unsigned long maxnode unsigned long addr unsigned long flags +240 sys_mq_open const char *u_name int oflag mode_t mode struct mq_attr *u_attr +241 sys_mq_unlink const char *u_name +242 sys_mq_timedsend mqd_t mqdes const char *u_msg_ptr size_t msg_len unsigned int msg_prio const stuct timespec *u_abs_timeout +243 sys_mq_timedreceive mqd_t mqdes char *u_msg_ptr size_t msg_len unsigned int *u_msg_prio const struct timespec *u_abs_timeout +244 sys_mq_notify mqd_t mqdes const struct sigevent *u_notification +245 sys_mq_getsetattr mqd_t mqdes const struct mq_attr *u_mqstat struct mq_attr *u_omqstat +246 sys_kexec_load unsigned long entry unsigned long nr_segments struct kexec_segment *segments unsigned long flags +>247 sys_waitid int which pid_t upid struct siginfo *infop int options struct rusage *ru +248 sys_add_key const char *_type const char *_description const void *_payload size_t plen +249 sys_request_key const char *_type const char *_description const char *_callout_info key_serial_t destringid +250 sys_keyctl int option unsigned long arg2 unsigned long arg3 unsigned long arg4 unsigned long arg5 +251 sys_ioprio_set int which int who int ioprio +252 sys_ioprio_get int which int who +253 sys_inotify_init +254 sys_inotify_add_watch int fd const char *pathname u32 mask +255 sys_inotify_rm_watch int fd __s32 wd +256 sys_migrate_pages pid_t pid unsigned long maxnode const unsigned long *old_nodes const unsigned long *new_nodes +257 sys_openat int dfd const char *filename int flags int mode +258 sys_mkdirat int dfd const char *pathname int mode +259 sys_mknodat int dfd const char *filename int mode unsigned dev +260 sys_fchownat int dfd const char *filename uid_t user gid_t group int flag +261 sys_futimesat int dfd const char *filename struct timeval *utimes +262 sys_newfstatat int dfd const char *filename struct stat *statbuf int flag +263 sys_unlinkat int dfd const char *pathname int flag +264 sys_renameat int oldfd const char *oldname int newfd const char *newname +265 sys_linkat int oldfd const char *oldname int newfd const char *newname int flags +266 sys_symlinkat const char *oldname int newfd const char *newname +267 sys_readlinkat int dfd const char *pathname char *buf int bufsiz +268 sys_fchmodat int dfd const char *filename mode_t mode +269 sys_faccessat int dfd const char *filename int mode +270 sys_pselect6 int n fd_set *inp fd_set *outp fd_set *exp struct timespec *tsp void *sig +271 sys_ppoll struct pollfd *ufds unsigned int nfds struct timespec *tsp const sigset_t *sigmask size_t sigsetsize +272 sys_unshare unsigned long unshare_flags +273 sys_set_robust_list struct robust_list_head *head size_t len +274 sys_get_robust_list int pid struct robust_list_head **head_ptr size_t *len_ptr +275 sys_splice int fd_in loff_t *off_in int fd_out loff_t *off_out size_t len unsigned int flags +276 sys_tee int fdin int fdout size_t len unsigned int flags +277 sys_sync_file_range long fd loff_t offset loff_t bytes long flags +278 sys_vmsplice int fd const struct iovec *iov unsigned long nr_segs unsigned int flags +279 sys_move_pages pid_t pid unsigned long nr_pages const void **pages const int *nodes int *status int flags +280 sys_utimensat int dfd const char *filename struct timespec *utimes int flags +281 sys_epoll_pwait int epfd struct epoll_event *events int maxevents int timeout const sigset_t *sigmask size_t sigsetsize +282 sys_signalfd int ufd sigset_t *user_mask size_t sizemask +283 sys_timerfd_create int clockid int flags +284 sys_eventfd unsigned int count +285 sys_fallocate long fd long mode loff_t offset loff_t len +286 sys_timerfd_settime int ufd int flags const struct itimerspec *utmr struct itimerspec *otmr +287 sys_timerfd_gettime int ufd struct itimerspec *otmr +288 sys_accept4 int fd struct sockaddr *upeer_sockaddr int *upeer_addrlen int flags +289 sys_signalfd4 int ufd sigset_t *user_mask size_t sizemask int flags +290 sys_eventfd2 unsigned int count int flags +291 sys_epoll_create1 int flags +292 sys_dup3 unsigned int oldfd unsigned int newfd int flags +293 sys_pipe2 int *filedes int flags +294 sys_inotify_init1 int flags +295 sys_preadv unsigned long fd const struct iovec *vec unsigned long vlen unsigned long pos_l unsigned long pos_h +296 sys_pwritev unsigned long fd const struct iovec *vec unsigned long vlen unsigned long pos_l unsigned long pos_h +297 sys_rt_tgsigqueueinfo pid_t tgid pid_t pid int sig siginfo_t *uinfo +298 sys_perf_event_open struct perf_event_attr *attr_uptr pid_t pid int cpu int group_fd unsigned long flags +299 sys_recvmmsg int fd struct msghdr *mmsg unsigned int vlen unsigned int flags struct timespec *timeout +300 sys_fanotify_init unsigned int flags unsigned int event_f_flags +301 sys_fanotify_mark long fanotify_fd long flags __u64 mask long dfd long pathname +302 sys_prlimit64 pid_t pid unsigned int resource const struct rlimit64 *new_rlim struct rlimit64 *old_rlim +303 sys_name_to_handle_at int dfd const char *name struct file_handle *handle int *mnt_id int flag +304 sys_open_by_handle_at int dfd const char *name struct file_handle *handle int *mnt_id int flags +305 sys_clock_adjtime clockid_t which_clock struct timex *tx +306 sys_syncfs int fd +307 sys_sendmmsg int fd struct mmsghdr *mmsg unsigned int vlen unsigned int flags +308 sys_setns int fd int nstype +309 sys_getcpu unsigned *cpup unsigned *nodep struct getcpu_cache *unused +310 sys_process_vm_readv pid_t pid const struct iovec *lvec unsigned long liovcnt const struct iovec *rvec unsigned long riovcnt unsigned long flags +311 sys_process_vm_writev pid_t pid const struct iovec *lvec unsigned long liovcnt const struct iovcc *rvec unsigned long riovcnt unsigned long flags +312 sys_kcmp pid_t pid1 pid_t pid2 int type unsigned long idx1 unsigned long idx2 +313 sys_finit_module int fd const char __user *uargs int flags +314 sys_sched_setattr pid_t pid struct sched_attr __user *attr unsigned int flags +315 sys_sched_getattr pid_t pid struct sched_attr __user *attr unsigned int size unsigned int flags +316 sys_renameat2 int olddfd const char __user *oldname int newdfd const char __user *newname unsigned int flags +317 sys_seccomp unsigned int op unsigned int flags const char __user *uargs +318 sys_getrandom char __user *buf size_t count unsigned int flags +319 sys_memfd_create const char __user *uname_ptr unsigned int flags +320 sys_kexec_file_load int kernel_fd int initrd_fd unsigned long cmdline_len const char __user *cmdline_ptr unsigned long flags +321 sys_bpf int cmd union bpf_attr *attr unsigned int size +322 stub_execveat int dfd const char __user *filename const char __user *const __user *argv const char __user *const __user *envp int flags +323 userfaultfd int flags +324 membarrier int cmd int flags +325 mlock2 unsigned long start size_t len int flags +326 copy_file_range int fd_in loff_t __user *off_in int fd_out loff_t __user * off_out size_t len unsigned int flags +327 preadv2 unsigned long fd const struct iovec __user *vec unsigned long vlen unsigned long pos_l unsigned long pos_h int flags +328 pwritev2 unsigned long fd const struct iovec __user *vec unsigned long vlen unsigned long pos_l unsigned long pos_h int flags +*/ diff --git a/v_windows/v/old/vlib/builtin/linux_bare/old/mm_bare.v b/v_windows/v/old/vlib/builtin/linux_bare/old/mm_bare.v new file mode 100644 index 0000000..cee5f99 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/linux_bare/old/mm_bare.v @@ -0,0 +1,58 @@ +module builtin + +const ( + mem_prot = Mm_prot(int(Mm_prot.prot_read) | int(Mm_prot.prot_write)) + mem_flags = Map_flags(int(Map_flags.map_private) | int(Map_flags.map_anonymous)) + page_size = u64(Linux_mem.page_size) +) + +pub fn mm_pages(size u64) u32 { + pages := (size + u64(4) + page_size) / page_size + return u32(pages) +} + +pub fn mm_alloc(size u64) (&byte, Errno) { + pages := mm_pages(size) + n_bytes := u64(pages * u32(Linux_mem.page_size)) + + a, e := sys_mmap(0, n_bytes, mem_prot, mem_flags, -1, 0) + if e == .enoerror { + mut ap := &int(a) + *ap = pages + return &byte(a + 4), e + } + return &byte(0), e +} + +pub fn mm_free(addr &byte) Errno { + ap := &int(addr - 4) + size := u64(*ap) * u64(Linux_mem.page_size) + + return sys_munmap(ap, size) +} + +pub fn mem_copy(dest0 voidptr, src0 voidptr, n int) voidptr { + mut dest := &byte(dest0) + src := &byte(src0) + for i in 0 .. n { + dest[i] = src[i] + } + return dest0 +} + +[unsafe] +pub fn malloc(n int) &byte { + if n < 0 { + panic('malloc(<0)') + } + + ptr, e := mm_alloc(u64(n)) + assert e == .enoerror + assert !isnil(ptr) + return ptr +} + +[unsafe] +pub fn free(ptr voidptr) { + assert mm_free(ptr) == .enoerror +} diff --git a/v_windows/v/old/vlib/builtin/linux_bare/old/string_bare.v b/v_windows/v/old/vlib/builtin/linux_bare/old/string_bare.v new file mode 100644 index 0000000..8f7edfc --- /dev/null +++ b/v_windows/v/old/vlib/builtin/linux_bare/old/string_bare.v @@ -0,0 +1,150 @@ +module builtin + +pub struct string { +pub: + str &byte + len int +} + +pub fn strlen(s &byte) int { + mut i := 0 + for ; s[i] != 0; i++ {} + return i +} + +pub fn tos(s &byte, len int) string { + if s == 0 { + panic('tos(): nil string') + } + return string{ + str: s + len: len + } +} + +fn (s string) add(a string) string { + new_len := a.len + s.len + mut res := string{ + len: new_len + str: malloc(new_len + 1) + } + for j in 0 .. s.len { + res[j] = s[j] + } + for j in 0 .. a.len { + res[s.len + j] = a[j] + } + res[new_len] = 0 // V strings are not null terminated, but just in case + return res +} + +/* +pub fn tos_clone(s byteptr) string { + if s == 0 { + panic('tos: nil string') + } + return tos2(s).clone() +} +*/ + +// Same as `tos`, but calculates the length. Called by `string(bytes)` casts. +// Used only internally. +pub fn tos2(s &byte) string { + if s == 0 { + panic('tos2: nil string') + } + return string{ + str: s + len: strlen(s) + } +} + +pub fn tos3(s &char) string { + if s == 0 { + panic('tos3: nil string') + } + return string{ + str: &byte(s) + len: strlen(&byte(s)) + } +} + +pub fn string_eq(s1 string, s2 string) bool { + if s1.len != s2.len { + return false + } + for i in 0 .. s1.len { + if s1[i] != s2[i] { + return false + } + } + return true +} + +pub fn string_ne(s1 string, s2 string) bool { + return !string_eq(s1, s2) +} + +pub fn i64_tos(buf &byte, len int, n0 i64, base int) string { + if base < 2 { + panic('base must be >= 2') + } + if base > 36 { + panic('base must be <= 36') + } + + mut b := tos(buf, len) + mut i := len - 1 + + mut n := n0 + neg := n < 0 + if neg { + n = -n + } + + b[i--] = 0 + + for { + c := (n % base) + 48 + b[i--] = if c > 57 { c + 7 } else { c } + if i < 0 { + panic('buffer to small') + } + n /= base + if n < 1 { + break + } + } + if neg { + if i < 0 { + panic('buffer to small') + } + b[i--] = 45 + } + offset := i + 1 + b.str = b.str + offset + b.len -= (offset + 1) + return b +} + +pub fn i64_str(n0 i64, base int) string { + buf := malloc(80) + return i64_tos(buf, 79, n0, base) +} + +pub fn ptr_str(ptr voidptr) string { + buf := [16]byte{} + hex := i64_tos(buf, 15, i64(ptr), 16) + res := '0x' + hex + return res +} + +pub fn (a string) clone() string { + mut b := string{ + len: a.len + str: malloc(a.len + 1) + } + mem_copy(b.str, a.str, a.len) + b[a.len] = 0 + return b +} diff --git a/v_windows/v/old/vlib/builtin/linux_bare/old/syscallwrapper_test.v b/v_windows/v/old/vlib/builtin/linux_bare/old/syscallwrapper_test.v new file mode 100644 index 0000000..cbaf355 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/linux_bare/old/syscallwrapper_test.v @@ -0,0 +1,27 @@ +import os + +fn test_syscallwrappers() { + if true { + return + } + $if linux { + $if x64 { + exe := os.executable() + vdir := os.dir(exe) + if vdir.len > 1 { + dot_checks := vdir + '/.checks' + assert os.is_dir(dot_checks) + + os.chdir(dot_checks) + checks_v := 'checks.v' + assert os.exists(checks_v) + rc := os.execute_or_exit('v run $checks_v') + assert rc.exit_code == 0 + assert !rc.output.contains('V panic: An assertion failed.') + assert !rc.output.contains('failed') + } else { + panic("Can't find test directory") + } + } + } +} diff --git a/v_windows/v/old/vlib/builtin/map.v b/v_windows/v/old/vlib/builtin/map.v new file mode 100644 index 0000000..37b2e49 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/map.v @@ -0,0 +1,794 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module builtin + +fn C.wyhash(&byte, u64, u64, &u64) u64 + +fn C.wyhash64(u64, u64) u64 + +/* +This is a highly optimized hashmap implementation. It has several traits that +in combination makes it very fast and memory efficient. Here is a short expl- +anation of each trait. After reading this you should have a basic understand- +ing of how it functions: + +1. Hash-function: Wyhash. Wyhash is the fastest hash-function for short keys +passing SMHasher, so it was an obvious choice. + +2. Open addressing: Robin Hood Hashing. With this method, a hash-collision is +resolved by probing. As opposed to linear probing, Robin Hood hashing has a +simple but clever twist: As new keys are inserted, old keys are shifted arou- +nd in a way such that all keys stay reasonably close to the slot they origin- +ally hash to. A new key may displace a key already inserted if its probe cou- +nt is larger than that of the key at the current position. + +3. Memory layout: key-value pairs are stored in a `DenseArray`. This is a dy- +namic array with a very low volume of unused memory, at the cost of more rea- +llocations when inserting elements. It also preserves the order of the key-v- +alues. This array is named `key_values`. Instead of probing a new key-value, +this map probes two 32-bit numbers collectively. The first number has its 8 +most significant bits reserved for the probe-count and the remaining 24 bits +are cached bits from the hash which are utilized for faster re-hashing. This +number is often referred to as `meta`. The other 32-bit number is the index +at which the key-value was pushed to in `key_values`. Both of these numbers +are stored in a sparse array `metas`. The `meta`s and `kv_index`s are stored +at even and odd indices, respectively: + +metas = [meta, kv_index, 0, 0, meta, kv_index, 0, 0, meta, kv_index, ...] +key_values = [kv, kv, kv, ...] + +4. The size of metas is a power of two. This enables the use of bitwise AND +to convert the 64-bit hash to a bucket/index that doesn't overflow metas. If +the size is power of two you can use "hash & (SIZE - 1)" instead of "hash % +SIZE". Modulo is extremely expensive so using '&' is a big performance impro- +vement. The general concern with this approach is that you only make use of +the lower bits of the hash which can cause more collisions. This is solved by +using a well-dispersed hash-function. + +5. The hashmap keeps track of the highest probe_count. The trick is to alloc- +ate `extra_metas` > max(probe_count), so you never have to do any bounds-che- +cking since the extra meta memory ensures that a meta will never go beyond +the last index. + +6. Cached rehashing. When the `load_factor` of the map exceeds the `max_load_ +factor` the size of metas is doubled and all the key-values are "rehashed" to +find the index for their meta's in the new array. Instead of rehashing compl- +etely, it simply uses the cached-hashbits stored in the meta, resulting in +much faster rehashing. +*/ +const ( + // Number of bits from the hash stored for each entry + hashbits = 24 + // Number of bits from the hash stored for rehashing + max_cached_hashbits = 16 + // Initial log-number of buckets in the hashtable + init_log_capicity = 5 + // Initial number of buckets in the hashtable + init_capicity = 1 << init_log_capicity + // Maximum load-factor (len / capacity) + max_load_factor = 0.8 + // Initial highest even index in metas + init_even_index = init_capicity - 2 + // Used for incrementing `extra_metas` when max + // probe count is too high, to avoid overflow + extra_metas_inc = 4 + // Bitmask to select all the hashbits + hash_mask = u32(0x00FFFFFF) + // Used for incrementing the probe-count + probe_inc = u32(0x01000000) +) + +// fast_string_eq is intended to be fast when +// the strings are very likely to be equal +// TODO: add branch prediction hints +[inline] +fn fast_string_eq(a string, b string) bool { + if a.len != b.len { + return false + } + unsafe { + return C.memcmp(a.str, b.str, b.len) == 0 + } +} + +// DenseArray represents a dynamic array with very low growth factor +struct DenseArray { + key_bytes int + value_bytes int +mut: + cap int + len int + deletes u32 // count + // array allocated (with `cap` bytes) on first deletion + // has non-zero element when key deleted + all_deleted &byte + values &byte + keys &byte +} + +[inline] +fn new_dense_array(key_bytes int, value_bytes int) DenseArray { + cap := 8 + return DenseArray{ + key_bytes: key_bytes + value_bytes: value_bytes + cap: cap + len: 0 + deletes: 0 + all_deleted: 0 + keys: unsafe { malloc(cap * key_bytes) } + values: unsafe { malloc(cap * value_bytes) } + } +} + +[inline] +fn (d &DenseArray) key(i int) voidptr { + return unsafe { d.keys + i * d.key_bytes } +} + +// for cgen +[inline] +fn (d &DenseArray) value(i int) voidptr { + return unsafe { d.values + i * d.value_bytes } +} + +[inline] +fn (d &DenseArray) has_index(i int) bool { + return d.deletes == 0 || unsafe { d.all_deleted[i] } == 0 +} + +// Make space to append an element and return index +// The growth-factor is roughly 1.125 `(x + (x >> 3))` +[inline] +fn (mut d DenseArray) expand() int { + old_cap := d.cap + old_value_size := d.value_bytes * old_cap + old_key_size := d.key_bytes * old_cap + if d.cap == d.len { + d.cap += d.cap >> 3 + unsafe { + d.keys = realloc_data(d.keys, old_key_size, d.key_bytes * d.cap) + d.values = realloc_data(d.values, old_value_size, d.value_bytes * d.cap) + if d.deletes != 0 { + d.all_deleted = realloc_data(d.all_deleted, old_cap, d.cap) + C.memset(d.all_deleted + d.len, 0, d.cap - d.len) + } + } + } + push_index := d.len + unsafe { + if d.deletes != 0 { + d.all_deleted[push_index] = 0 + } + } + d.len++ + return push_index +} + +// Move all zeros to the end of the array and resize array +fn (mut d DenseArray) zeros_to_end() { + // TODO alloca? + mut tmp_value := unsafe { malloc(d.value_bytes) } + mut tmp_key := unsafe { malloc(d.key_bytes) } + mut count := 0 + for i in 0 .. d.len { + if d.has_index(i) { + // swap (TODO: optimize) + unsafe { + if count != i { + // Swap keys + C.memcpy(tmp_key, d.key(count), d.key_bytes) + C.memcpy(d.key(count), d.key(i), d.key_bytes) + C.memcpy(d.key(i), tmp_key, d.key_bytes) + // Swap values + C.memcpy(tmp_value, d.value(count), d.value_bytes) + C.memcpy(d.value(count), d.value(i), d.value_bytes) + C.memcpy(d.value(i), tmp_value, d.value_bytes) + } + } + count++ + } + } + unsafe { + free(tmp_value) + free(tmp_key) + d.deletes = 0 + // TODO: reallocate instead as more deletes are likely + free(d.all_deleted) + } + d.len = count + old_cap := d.cap + d.cap = if count < 8 { 8 } else { count } + unsafe { + d.values = realloc_data(d.values, d.value_bytes * old_cap, d.value_bytes * d.cap) + d.keys = realloc_data(d.keys, d.key_bytes * old_cap, d.key_bytes * d.cap) + } +} + +type MapHashFn = fn (voidptr) u64 + +type MapEqFn = fn (voidptr, voidptr) bool + +type MapCloneFn = fn (voidptr, voidptr) + +type MapFreeFn = fn (voidptr) + +// map is the internal representation of a V `map` type. +pub struct map { + // Number of bytes of a key + key_bytes int + // Number of bytes of a value + value_bytes int +mut: + // Highest even index in the hashtable + even_index u32 + // Number of cached hashbits left for rehashing + cached_hashbits byte + // Used for right-shifting out used hashbits + shift byte + // Array storing key-values (ordered) + key_values DenseArray + // Pointer to meta-data: + // - Odd indices store kv_index. + // - Even indices store probe_count and hashbits. + metas &u32 + // Extra metas that allows for no ranging when incrementing + // index in the hashmap + extra_metas u32 + has_string_keys bool + hash_fn MapHashFn + key_eq_fn MapEqFn + clone_fn MapCloneFn + free_fn MapFreeFn +pub mut: + // Number of key-values currently in the hashmap + len int +} + +fn map_hash_string(pkey voidptr) u64 { + key := *unsafe { &string(pkey) } + return C.wyhash(key.str, u64(key.len), 0, &u64(C._wyp)) +} + +fn map_hash_int_1(pkey voidptr) u64 { + return C.wyhash64(*unsafe { &byte(pkey) }, 0) +} + +fn map_hash_int_2(pkey voidptr) u64 { + return C.wyhash64(*unsafe { &u16(pkey) }, 0) +} + +fn map_hash_int_4(pkey voidptr) u64 { + return C.wyhash64(*unsafe { &u32(pkey) }, 0) +} + +fn map_hash_int_8(pkey voidptr) u64 { + return C.wyhash64(*unsafe { &u64(pkey) }, 0) +} + +fn map_eq_string(a voidptr, b voidptr) bool { + return fast_string_eq(*unsafe { &string(a) }, *unsafe { &string(b) }) +} + +fn map_eq_int_1(a voidptr, b voidptr) bool { + return unsafe { *&byte(a) == *&byte(b) } +} + +fn map_eq_int_2(a voidptr, b voidptr) bool { + return unsafe { *&u16(a) == *&u16(b) } +} + +fn map_eq_int_4(a voidptr, b voidptr) bool { + return unsafe { *&u32(a) == *&u32(b) } +} + +fn map_eq_int_8(a voidptr, b voidptr) bool { + return unsafe { *&u64(a) == *&u64(b) } +} + +fn map_clone_string(dest voidptr, pkey voidptr) { + unsafe { + s := *&string(pkey) + (*&string(dest)) = s.clone() + } +} + +fn map_clone_int_1(dest voidptr, pkey voidptr) { + unsafe { + *&byte(dest) = *&byte(pkey) + } +} + +fn map_clone_int_2(dest voidptr, pkey voidptr) { + unsafe { + *&u16(dest) = *&u16(pkey) + } +} + +fn map_clone_int_4(dest voidptr, pkey voidptr) { + unsafe { + *&u32(dest) = *&u32(pkey) + } +} + +fn map_clone_int_8(dest voidptr, pkey voidptr) { + unsafe { + *&u64(dest) = *&u64(pkey) + } +} + +fn map_free_string(pkey voidptr) { + unsafe { + (*&string(pkey)).free() + } +} + +fn map_free_nop(_ voidptr) { +} + +fn new_map(key_bytes int, value_bytes int, hash_fn MapHashFn, key_eq_fn MapEqFn, clone_fn MapCloneFn, free_fn MapFreeFn) map { + metasize := int(sizeof(u32) * (init_capicity + extra_metas_inc)) + // for now assume anything bigger than a pointer is a string + has_string_keys := key_bytes > sizeof(voidptr) + return map{ + key_bytes: key_bytes + value_bytes: value_bytes + even_index: init_even_index + cached_hashbits: max_cached_hashbits + shift: init_log_capicity + key_values: new_dense_array(key_bytes, value_bytes) + metas: unsafe { &u32(vcalloc_noscan(metasize)) } + extra_metas: extra_metas_inc + len: 0 + has_string_keys: has_string_keys + hash_fn: hash_fn + key_eq_fn: key_eq_fn + clone_fn: clone_fn + free_fn: free_fn + } +} + +fn new_map_init(hash_fn MapHashFn, key_eq_fn MapEqFn, clone_fn MapCloneFn, free_fn MapFreeFn, n int, key_bytes int, value_bytes int, keys voidptr, values voidptr) map { + mut out := new_map(key_bytes, value_bytes, hash_fn, key_eq_fn, clone_fn, free_fn) + // TODO pre-allocate n slots + mut pkey := &byte(keys) + mut pval := &byte(values) + for _ in 0 .. n { + unsafe { + out.set(pkey, pval) + pkey = pkey + key_bytes + pval = pval + value_bytes + } + } + return out +} + +pub fn (mut m map) move() map { + r := *m + unsafe { + C.memset(m, 0, sizeof(map)) + } + return r +} + +[inline] +fn (m &map) key_to_index(pkey voidptr) (u32, u32) { + hash := m.hash_fn(pkey) + index := hash & m.even_index + meta := ((hash >> m.shift) & hash_mask) | probe_inc + return u32(index), u32(meta) +} + +[inline] +fn (m &map) meta_less(_index u32, _metas u32) (u32, u32) { + mut index := _index + mut meta := _metas + for meta < unsafe { m.metas[index] } { + index += 2 + meta += probe_inc + } + return index, meta +} + +[inline] +fn (mut m map) meta_greater(_index u32, _metas u32, kvi u32) { + mut meta := _metas + mut index := _index + mut kv_index := kvi + for unsafe { m.metas[index] } != 0 { + if meta > unsafe { m.metas[index] } { + unsafe { + tmp_meta := m.metas[index] + m.metas[index] = meta + meta = tmp_meta + tmp_index := m.metas[index + 1] + m.metas[index + 1] = kv_index + kv_index = tmp_index + } + } + index += 2 + meta += probe_inc + } + unsafe { + m.metas[index] = meta + m.metas[index + 1] = kv_index + } + probe_count := (meta >> hashbits) - 1 + m.ensure_extra_metas(probe_count) +} + +[inline] +fn (mut m map) ensure_extra_metas(probe_count u32) { + if (probe_count << 1) == m.extra_metas { + size_of_u32 := sizeof(u32) + old_mem_size := (m.even_index + 2 + m.extra_metas) + m.extra_metas += extra_metas_inc + mem_size := (m.even_index + 2 + m.extra_metas) + unsafe { + x := realloc_data(&byte(m.metas), int(size_of_u32 * old_mem_size), int(size_of_u32 * mem_size)) + m.metas = &u32(x) + C.memset(m.metas + mem_size - extra_metas_inc, 0, int(sizeof(u32) * extra_metas_inc)) + } + // Should almost never happen + if probe_count == 252 { + panic('Probe overflow') + } + } +} + +// Insert new element to the map. The element is inserted if its key is +// not equivalent to the key of any other element already in the container. +// If the key already exists, its value is changed to the value of the new element. +fn (mut m map) set(key voidptr, value voidptr) { + load_factor := f32(m.len << 1) / f32(m.even_index) + if load_factor > max_load_factor { + m.expand() + } + mut index, mut meta := m.key_to_index(key) + index, meta = m.meta_less(index, meta) + // While we might have a match + for meta == unsafe { m.metas[index] } { + kv_index := int(unsafe { m.metas[index + 1] }) + pkey := unsafe { m.key_values.key(kv_index) } + if m.key_eq_fn(key, pkey) { + unsafe { + pval := m.key_values.value(kv_index) + C.memcpy(pval, value, m.value_bytes) + } + return + } + index += 2 + meta += probe_inc + } + kv_index := m.key_values.expand() + unsafe { + pkey := m.key_values.key(kv_index) + pvalue := m.key_values.value(kv_index) + m.clone_fn(pkey, key) + C.memcpy(&byte(pvalue), value, m.value_bytes) + } + m.meta_greater(index, meta, u32(kv_index)) + m.len++ +} + +// Doubles the size of the hashmap +fn (mut m map) expand() { + old_cap := m.even_index + m.even_index = ((m.even_index + 2) << 1) - 2 + // Check if any hashbits are left + if m.cached_hashbits == 0 { + m.shift += max_cached_hashbits + m.cached_hashbits = max_cached_hashbits + m.rehash() + } else { + m.cached_rehash(old_cap) + m.cached_hashbits-- + } +} + +// A rehash is the reconstruction of the hash table: +// All the elements in the container are rearranged according +// to their hash value into the newly sized key-value container. +// Rehashes are performed when the load_factor is going to surpass +// the max_load_factor in an operation. +fn (mut m map) rehash() { + meta_bytes := sizeof(u32) * (m.even_index + 2 + m.extra_metas) + unsafe { + // TODO: use realloc_data here too + x := v_realloc(&byte(m.metas), int(meta_bytes)) + m.metas = &u32(x) + C.memset(m.metas, 0, meta_bytes) + } + for i := 0; i < m.key_values.len; i++ { + if !m.key_values.has_index(i) { + continue + } + pkey := unsafe { m.key_values.key(i) } + mut index, mut meta := m.key_to_index(pkey) + index, meta = m.meta_less(index, meta) + m.meta_greater(index, meta, u32(i)) + } +} + +// This method works like rehash. However, instead of rehashing the +// key completely, it uses the bits cached in `metas`. +fn (mut m map) cached_rehash(old_cap u32) { + old_metas := m.metas + metasize := int(sizeof(u32) * (m.even_index + 2 + m.extra_metas)) + m.metas = unsafe { &u32(vcalloc(metasize)) } + old_extra_metas := m.extra_metas + for i := u32(0); i <= old_cap + old_extra_metas; i += 2 { + if unsafe { old_metas[i] } == 0 { + continue + } + old_meta := unsafe { old_metas[i] } + old_probe_count := ((old_meta >> hashbits) - 1) << 1 + old_index := (i - old_probe_count) & (m.even_index >> 1) + mut index := (old_index | (old_meta << m.shift)) & m.even_index + mut meta := (old_meta & hash_mask) | probe_inc + index, meta = m.meta_less(index, meta) + kv_index := unsafe { old_metas[i + 1] } + m.meta_greater(index, meta, kv_index) + } + unsafe { free(old_metas) } +} + +// This method is used for assignment operators. If the argument-key +// does not exist in the map, it's added to the map along with the zero/default value. +// If the key exists, its respective value is returned. +fn (mut m map) get_and_set(key voidptr, zero voidptr) voidptr { + for { + mut index, mut meta := m.key_to_index(key) + for { + if meta == unsafe { m.metas[index] } { + kv_index := int(unsafe { m.metas[index + 1] }) + pkey := unsafe { m.key_values.key(kv_index) } + if m.key_eq_fn(key, pkey) { + pval := unsafe { m.key_values.value(kv_index) } + return unsafe { &byte(pval) } + } + } + index += 2 + meta += probe_inc + if meta > unsafe { m.metas[index] } { + break + } + } + // Key not found, insert key with zero-value + m.set(key, zero) + } + assert false + return voidptr(0) +} + +// If `key` matches the key of an element in the container, +// the method returns a reference to its mapped value. +// If not, a zero/default value is returned. +fn (m &map) get(key voidptr, zero voidptr) voidptr { + mut index, mut meta := m.key_to_index(key) + for { + if meta == unsafe { m.metas[index] } { + kv_index := int(unsafe { m.metas[index + 1] }) + pkey := unsafe { m.key_values.key(kv_index) } + if m.key_eq_fn(key, pkey) { + pval := unsafe { m.key_values.value(kv_index) } + return unsafe { &byte(pval) } + } + } + index += 2 + meta += probe_inc + if meta > unsafe { m.metas[index] } { + break + } + } + return zero +} + +// If `key` matches the key of an element in the container, +// the method returns a reference to its mapped value. +// If not, a zero pointer is returned. +// This is used in `x := m['key'] or { ... }` +fn (m &map) get_check(key voidptr) voidptr { + mut index, mut meta := m.key_to_index(key) + for { + if meta == unsafe { m.metas[index] } { + kv_index := int(unsafe { m.metas[index + 1] }) + pkey := unsafe { m.key_values.key(kv_index) } + if m.key_eq_fn(key, pkey) { + pval := unsafe { m.key_values.value(kv_index) } + return unsafe { &byte(pval) } + } + } + index += 2 + meta += probe_inc + if meta > unsafe { m.metas[index] } { + break + } + } + return 0 +} + +// Checks whether a particular key exists in the map. +fn (m &map) exists(key voidptr) bool { + mut index, mut meta := m.key_to_index(key) + for { + if meta == unsafe { m.metas[index] } { + kv_index := int(unsafe { m.metas[index + 1] }) + pkey := unsafe { m.key_values.key(kv_index) } + if m.key_eq_fn(key, pkey) { + return true + } + } + index += 2 + meta += probe_inc + if meta > unsafe { m.metas[index] } { + break + } + } + return false +} + +[inline] +fn (mut d DenseArray) delete(i int) { + if d.deletes == 0 { + d.all_deleted = vcalloc(d.cap) // sets to 0 + } + d.deletes++ + unsafe { + d.all_deleted[i] = 1 + } +} + +// Removes the mapping of a particular key from the map. +[unsafe] +pub fn (mut m map) delete(key voidptr) { + mut index, mut meta := m.key_to_index(key) + index, meta = m.meta_less(index, meta) + // Perform backwards shifting + for meta == unsafe { m.metas[index] } { + kv_index := int(unsafe { m.metas[index + 1] }) + pkey := unsafe { m.key_values.key(kv_index) } + if m.key_eq_fn(key, pkey) { + for (unsafe { m.metas[index + 2] } >> hashbits) > 1 { + unsafe { + m.metas[index] = m.metas[index + 2] - probe_inc + m.metas[index + 1] = m.metas[index + 3] + } + index += 2 + } + m.len-- + m.key_values.delete(kv_index) + unsafe { + m.metas[index] = 0 + m.free_fn(pkey) + // Mark key as deleted + C.memset(pkey, 0, m.key_bytes) + } + if m.key_values.len <= 32 { + return + } + // Clean up key_values if too many have been deleted + if m.key_values.deletes >= (m.key_values.len >> 1) { + m.key_values.zeros_to_end() + m.rehash() + } + return + } + index += 2 + meta += probe_inc + } +} + +// Returns all keys in the map. +fn (m &map) keys() array { + mut keys := __new_array(m.len, 0, m.key_bytes) + mut item := unsafe { &byte(keys.data) } + if m.key_values.deletes == 0 { + for i := 0; i < m.key_values.len; i++ { + unsafe { + pkey := m.key_values.key(i) + m.clone_fn(item, pkey) + item = item + m.key_bytes + } + } + return keys + } + for i := 0; i < m.key_values.len; i++ { + if !m.key_values.has_index(i) { + continue + } + unsafe { + pkey := m.key_values.key(i) + m.clone_fn(item, pkey) + item = item + m.key_bytes + } + } + return keys +} + +// warning: only copies keys, does not clone +[unsafe] +fn (d &DenseArray) clone() DenseArray { + res := DenseArray{ + key_bytes: d.key_bytes + value_bytes: d.value_bytes + cap: d.cap + len: d.len + deletes: d.deletes + all_deleted: 0 + values: 0 + keys: 0 + } + unsafe { + if d.deletes != 0 { + res.all_deleted = memdup(d.all_deleted, d.cap) + } + res.keys = memdup(d.keys, d.cap * d.key_bytes) + res.values = memdup(d.values, d.cap * d.value_bytes) + } + return res +} + +// clone returns a clone of the `map`. +[unsafe] +pub fn (m &map) clone() map { + metasize := int(sizeof(u32) * (m.even_index + 2 + m.extra_metas)) + res := map{ + key_bytes: m.key_bytes + value_bytes: m.value_bytes + even_index: m.even_index + cached_hashbits: m.cached_hashbits + shift: m.shift + key_values: unsafe { m.key_values.clone() } + metas: unsafe { &u32(malloc(metasize)) } + extra_metas: m.extra_metas + len: m.len + has_string_keys: m.has_string_keys + hash_fn: m.hash_fn + key_eq_fn: m.key_eq_fn + clone_fn: m.clone_fn + free_fn: m.free_fn + } + unsafe { C.memcpy(res.metas, m.metas, metasize) } + if !m.has_string_keys { + return res + } + // clone keys + for i in 0 .. m.key_values.len { + if !m.key_values.has_index(i) { + continue + } + m.clone_fn(res.key_values.key(i), m.key_values.key(i)) + } + return res +} + +// free releases all memory resources occupied by the `map`. +[unsafe] +pub fn (m &map) free() { + unsafe { free(m.metas) } + if m.key_values.deletes == 0 { + for i := 0; i < m.key_values.len; i++ { + unsafe { + pkey := m.key_values.key(i) + m.free_fn(pkey) + } + } + } else { + for i := 0; i < m.key_values.len; i++ { + if !m.key_values.has_index(i) { + continue + } + unsafe { + pkey := m.key_values.key(i) + m.free_fn(pkey) + } + } + unsafe { free(m.key_values.all_deleted) } + } + unsafe { + free(m.key_values.keys) + free(m.key_values.values) + } +} diff --git a/v_windows/v/old/vlib/builtin/map_d_gcboehm_opt.v b/v_windows/v/old/vlib/builtin/map_d_gcboehm_opt.v new file mode 100644 index 0000000..93049b7 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/map_d_gcboehm_opt.v @@ -0,0 +1,146 @@ +// "noscan" versions of `map` initialization routines +// +// They are used when the compiler can proof that either the keys or the values or both +// do not contain any pointers. Such objects can be placed in a memory area that is not +// scanned by the garbage collector + +module builtin + +[inline] +fn new_dense_array_noscan(key_bytes int, key_noscan bool, value_bytes int, value_noscan bool) DenseArray { + cap := 8 + keys := if key_noscan { + unsafe { malloc_noscan(cap * key_bytes) } + } else { + unsafe { malloc(cap * key_bytes) } + } + values := if value_noscan { + unsafe { malloc_noscan(cap * value_bytes) } + } else { + unsafe { malloc(cap * value_bytes) } + } + return DenseArray{ + key_bytes: key_bytes + value_bytes: value_bytes + cap: cap + len: 0 + deletes: 0 + all_deleted: 0 + keys: keys + values: values + } +} + +fn new_map_noscan_key(key_bytes int, value_bytes int, hash_fn MapHashFn, key_eq_fn MapEqFn, clone_fn MapCloneFn, free_fn MapFreeFn) map { + metasize := int(sizeof(u32) * (init_capicity + extra_metas_inc)) + // for now assume anything bigger than a pointer is a string + has_string_keys := key_bytes > sizeof(voidptr) + return map{ + key_bytes: key_bytes + value_bytes: value_bytes + even_index: init_even_index + cached_hashbits: max_cached_hashbits + shift: init_log_capicity + key_values: new_dense_array_noscan(key_bytes, true, value_bytes, false) + metas: unsafe { &u32(vcalloc_noscan(metasize)) } + extra_metas: extra_metas_inc + len: 0 + has_string_keys: has_string_keys + hash_fn: hash_fn + key_eq_fn: key_eq_fn + clone_fn: clone_fn + free_fn: free_fn + } +} + +fn new_map_noscan_value(key_bytes int, value_bytes int, hash_fn MapHashFn, key_eq_fn MapEqFn, clone_fn MapCloneFn, free_fn MapFreeFn) map { + metasize := int(sizeof(u32) * (init_capicity + extra_metas_inc)) + // for now assume anything bigger than a pointer is a string + has_string_keys := key_bytes > sizeof(voidptr) + return map{ + key_bytes: key_bytes + value_bytes: value_bytes + even_index: init_even_index + cached_hashbits: max_cached_hashbits + shift: init_log_capicity + key_values: new_dense_array_noscan(key_bytes, false, value_bytes, true) + metas: unsafe { &u32(vcalloc_noscan(metasize)) } + extra_metas: extra_metas_inc + len: 0 + has_string_keys: has_string_keys + hash_fn: hash_fn + key_eq_fn: key_eq_fn + clone_fn: clone_fn + free_fn: free_fn + } +} + +fn new_map_noscan_key_value(key_bytes int, value_bytes int, hash_fn MapHashFn, key_eq_fn MapEqFn, clone_fn MapCloneFn, free_fn MapFreeFn) map { + metasize := int(sizeof(u32) * (init_capicity + extra_metas_inc)) + // for now assume anything bigger than a pointer is a string + has_string_keys := key_bytes > sizeof(voidptr) + return map{ + key_bytes: key_bytes + value_bytes: value_bytes + even_index: init_even_index + cached_hashbits: max_cached_hashbits + shift: init_log_capicity + key_values: new_dense_array_noscan(key_bytes, true, value_bytes, true) + metas: unsafe { &u32(vcalloc_noscan(metasize)) } + extra_metas: extra_metas_inc + len: 0 + has_string_keys: has_string_keys + hash_fn: hash_fn + key_eq_fn: key_eq_fn + clone_fn: clone_fn + free_fn: free_fn + } +} + +fn new_map_init_noscan_key(hash_fn MapHashFn, key_eq_fn MapEqFn, clone_fn MapCloneFn, free_fn MapFreeFn, n int, key_bytes int, value_bytes int, keys voidptr, values voidptr) map { + mut out := new_map_noscan_key(key_bytes, value_bytes, hash_fn, key_eq_fn, clone_fn, + free_fn) + // TODO pre-allocate n slots + mut pkey := &byte(keys) + mut pval := &byte(values) + for _ in 0 .. n { + unsafe { + out.set(pkey, pval) + pkey = pkey + key_bytes + pval = pval + value_bytes + } + } + return out +} + +fn new_map_init_noscan_value(hash_fn MapHashFn, key_eq_fn MapEqFn, clone_fn MapCloneFn, free_fn MapFreeFn, n int, key_bytes int, value_bytes int, keys voidptr, values voidptr) map { + mut out := new_map_noscan_value(key_bytes, value_bytes, hash_fn, key_eq_fn, clone_fn, + free_fn) + // TODO pre-allocate n slots + mut pkey := &byte(keys) + mut pval := &byte(values) + for _ in 0 .. n { + unsafe { + out.set(pkey, pval) + pkey = pkey + key_bytes + pval = pval + value_bytes + } + } + return out +} + +fn new_map_init_noscan_key_value(hash_fn MapHashFn, key_eq_fn MapEqFn, clone_fn MapCloneFn, free_fn MapFreeFn, n int, key_bytes int, value_bytes int, keys voidptr, values voidptr) map { + mut out := new_map_noscan_key_value(key_bytes, value_bytes, hash_fn, key_eq_fn, clone_fn, + free_fn) + // TODO pre-allocate n slots + mut pkey := &byte(keys) + mut pval := &byte(values) + for _ in 0 .. n { + unsafe { + out.set(pkey, pval) + pkey = pkey + key_bytes + pval = pval + value_bytes + } + } + return out +} diff --git a/v_windows/v/old/vlib/builtin/map_of_floats_test.v b/v_windows/v/old/vlib/builtin/map_of_floats_test.v new file mode 100644 index 0000000..3906cb1 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/map_of_floats_test.v @@ -0,0 +1,27 @@ +fn test_map_of_f32() { + mut m32 := map[f32]string{} + m32[1.0] = 'one' + println(m32) + assert '$m32' == r"{1.: 'one'}" + for k, v in m32 { + assert typeof(k).name == 'f32' + assert typeof(v).name == 'string' + assert k == 1.0 + assert v == 'one' + } +} + +fn test_map_of_f64() { + mut m64 := map{ + 3.14: 'pi' + } + m64[1.0] = 'one' + println(m64) + assert '$m64' == r"{3.14: 'pi', 1.: 'one'}" + for k, v in m64 { + assert typeof(k).name == 'f64' + assert typeof(v).name == 'string' + assert k in [1.0, 3.14] + assert v in ['pi', 'one'] + } +} diff --git a/v_windows/v/old/vlib/builtin/map_test.v b/v_windows/v/old/vlib/builtin/map_test.v new file mode 100644 index 0000000..9c50ab7 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/map_test.v @@ -0,0 +1,947 @@ +import rand + +const ( + strings = unique_strings(20000, 10) +) + +fn unique_strings(arr_len int, str_len int) []string { + mut arr := []string{cap: arr_len} + for arr.len < arr_len { + str := rand.string(str_len) + if str !in arr { + arr << str + } + } + return arr +} + +fn test_get_and_set_many() { + mut m := map[string]int{} + for i, s in strings { + m[s] = i + assert m[s] == i + assert m.len == i + 1 + } + for i, s in strings { + assert m[s] == i + } + assert m.len == strings.len +} + +fn test_for_in_many() { + mut m := map[string]int{} + for i, s in strings { + m[s] = i + } + for k, v in m { + assert m[k] == v + } +} + +fn test_keys_many() { + mut m := map[string]int{} + for i, s in strings { + m[s] = i + } + keys := m.keys() + assert keys.len == strings.len + assert keys.len == m.len + assert keys == strings +} + +fn test_deletes_many() { + mut m := map[string]int{} + for i, s in strings { + m[s] = i + } + for i, s in strings { + m.delete(s) + assert m[s] == 0 + assert m.len == strings.len - (i + 1) + } + assert m.len == 0 + assert m.keys().len == 0 +} + +struct User { +mut: + name string +} + +struct Aaa { +mut: + m map[string]int + users map[string]User +} + +fn (mut a Aaa) set(key string, val int) { + a.m[key] = val +} + +fn test_map() { + mut m := map[string]int{} + assert m.len == 0 + m['hi'] = 80 + m['hello'] = 101 + assert m['hi'] == 80 + assert m['hello'] == 101 + assert m.len == 2 + assert 'hi' in m + mut sum := 0 + // Test `for in` + for _, val in m { + sum += val + } + assert sum == 80 + 101 + // Test `.keys()` + keys := m.keys() + assert keys.len == 2 + assert 'hi' in keys + assert 'hello' in keys + m.delete('hi') + assert m.len == 1 + m.delete('aloha') + assert m.len == 1 + assert m['hi'] == 0 + assert m.keys().len == 1 + assert m.keys()[0] == 'hello' + // // + mut users := map[string]User{} + users['1'] = User{'Peter'} + peter := users['1'] + assert peter.name == 'Peter' + mut a := Aaa{ + m: map[string]int{} + users: map[string]User{} + } + a.users['Bob'] = User{'Bob'} + q := a.users['Bob'] + assert q.name == 'Bob' + // test struct field change + a.users['Bob'].name = 'bob' + q2 := a.users['Bob'] + assert q2.name == 'bob' + a.m['one'] = 1 + a.set('two', 2) + assert a.m['one'] == 1 + assert a.m['two'] == 2 +} + +fn test_map_init() { + one := 'one' + three := 'three' + m := map{ + one: 1 + 'two': 2 + three: 1 + 2 + } + assert m['one'] == 1 + assert m['two'] == 2 + assert m['three'] == 3 + assert m['unknown'] == 0 +} + +fn test_string_map() { + // m := map[string]Fn +} + +fn test_large_map() { + // ticks := time.ticks() + mut nums := map[string]int{} + n := 30 * 1000 + for i in 0 .. n { + key := i.str() + nums[key] = i + } + assert nums['1'] == 1 + assert nums['999'] == 999 + assert nums['1000000'] == 0 + // println(time.ticks() - ticks) +} + +fn test_various_map_value() { + mut m1 := map[string]int{} + m1['test'] = 1 + assert m1['test'] == 1 + mut m2 := map[string]string{} + m2['test'] = 'test' + assert m2['test'] == 'test' + mut m3 := map[string]i8{} + m3['test'] = i8(0) + assert m3['test'] == i8(0) + mut m4 := map[string]i16{} + m4['test'] = i16(0) + assert m4['test'] == i16(0) + mut m7 := map[string]u16{} + m7['test'] = u16(0) + assert m7['test'] == u16(0) + mut m8 := map[string]u32{} + m8['test'] = u32(0) + assert m8['test'] == u32(0) + mut m9 := map[string]bool{} + m9['test'] = true + assert m9['test'] == true + mut m10 := map[string]byte{} + m10['test'] = byte(0) + assert m10['test'] == byte(0) + mut m11 := map[string]f32{} + m11['test'] = f32(0.0) + assert m11['test'] == f32(0.0) + mut m12 := map[string]f64{} + m12['test'] = f64(0.0) + assert m12['test'] == f64(0.0) + // mut m13 := map[string]rune + // m13['test'] = rune(0) + // assert m13['test'] == rune(0) + mut m14 := map[string]voidptr{} + m14['test'] = voidptr(0) + assert m14['test'] == voidptr(0) + mut m15 := map[string]&byte{} + m15['test'] = &byte(0) + assert m15['test'] == &byte(0) + mut m16 := map[string]i64{} + m16['test'] = i64(0) + assert m16['test'] == i64(0) + mut m17 := map[string]u64{} + m17['test'] = u64(0) + assert m17['test'] == u64(0) + mut m18 := map[string]&int{} + m18['test'] = &int(0) + assert m18['test'] == &int(0) +} + +fn test_string_arr() { + mut m := map[string][]string{} + m['a'] = ['one', 'two'] + assert m['a'].len == 2 + assert m['a'][0] == 'one' + assert m['a'][1] == 'two' +} + +fn mut_map(mut m map[string]int) { + m['a'] = 10 +} + +fn test_mut_arg() { + mut m := map[string]int{} + mut_map(mut m) + a := m['a'] + assert a == 10 +} + +fn test_delete() { + mut m := map[string]int{} + m['one'] = 1 + m['two'] = 2 + println(m['two']) // => "2" + m.delete('two') + println(m['two'].str()) // => 0 + assert ('two' in m) == false + println('two' in m) // => true, on Linux and Windows <-- wrong ! +} + +fn test_delete_size() { + arr := ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] + mut m := map[string]int{} + for _ in 0 .. 10 { + for i in 0 .. 10 { + m[arr[i]] = i + } + assert m.len == 10 + println(m.len) + for i in 0 .. 10 { + m.delete(arr[i]) + } + } +} + +fn test_nested_for_in() { + mut m := map[string]int{} + for i in 0 .. 1000 { + m[i.str()] = i + } + mut i := 0 + for key1, _ in m { + assert key1 == i.str() + i++ + mut j := 0 + for key2, _ in m { + assert key2 == j.str() + j++ + } + } +} + +fn test_delete_in_for_in() { + mut m := map[string]string{} + for i in 0 .. 1000 { + m[i.str()] = i.str() + } + mut i := 0 + for key, _ in m { + assert key == i.str() + m.delete(key) + i++ + } + assert m.str() == '{}' + assert m.len == 0 +} + +fn test_set_in_for_in() { + mut m := map[string]string{} + for i in 0 .. 10 { + m[i.str()] = i.str() + } + mut last_key := '' + mut i := 0 + for key, _ in m { + m['10'] = '10' + assert key == i.str() + last_key = key + i++ + } + assert last_key == '10' +} + +fn test_delete_and_set_in_for_in() { + mut m := map[string]string{} + for i in 0 .. 1000 { + m[i.str()] = i.str() + } + mut i := 0 + for key, _ in m { + assert key == i.str() + m.delete(key) + m[key] = i.str() + if i == 999 { + break + } + i++ + } + assert m.len == 1000 + i = 0 + for key, _ in m { + assert m[key] == i.str() + i++ + } + assert i == 1000 +} + +struct Mstruct1 { +pub mut: + mymap map[string]int +} + +struct Mstruct2 { +pub mut: + mymap map[string]f64 +} + +struct Mstruct3 { +pub mut: + mymap map[string]u16 +} + +fn test_map_assign() { + mut a := map[string]f64{} + mut b := map[string]int{} + mut c := map[string]u16{} + a = map{ + 'x': 12.4 + 'y': 3 + } + b = map{ + 'u': -13 + 'v': 12 + } + c = map{ + 's': u16(5) + 't': 3 + } + _ := Mstruct1{map{ + 'p': 12 + }} + _ := Mstruct2{map{ + 'q': 1.7 + }} + _ := Mstruct3{map{ + 'r': u16(6) + 's': 5 + }} +} + +fn test_postfix_op_directly() { + mut a := map[string]int{} + a['aaa']++ + assert a['aaa'] == 1 + a['aaa']++ + assert a['aaa'] == 2 + a['bbb']-- + assert a['bbb'] == -1 + a['bbb']-- + assert a['bbb'] == -2 +} + +fn test_map_push_directly() { + mut a := map[string][]string{} + a['aaa'] << ['a', 'b', 'c'] + assert a['aaa'].len == 3 + assert a['aaa'] == ['a', 'b', 'c'] +} + +fn test_assign_directly() { + mut a := map[string]int{} + a['aaa'] += 4 + assert a['aaa'] == 4 + a['aaa'] -= 2 + assert a['aaa'] == 2 +} + +fn test_map_in_directly() { + for k, v in map{ + 'aa': 1 + } { + assert k == 'aa' + assert v == 1 + } +} + +fn test_plus_assign_string() { + mut m := map{ + 'one': '' + } + m['one'] += '1' + assert m.len == 1 + assert m['one'] == '1' +} + +fn test_map_keys_to_array() { + m := map{ + 'a': 'b' + 'c': 'd' + } + mut arr := []string{} + for k, _ in m { + arr << k + } + sarr := arr.str() + println(sarr) + assert sarr == "['a', 'c']" +} + +fn map_in_mut(mut m map[string]int) { + if 'one' in m { + m['one'] = 2 + } +} + +fn test_map_in_mut() { + mut m := map{ + 'one': 1 + } + map_in_mut(mut m) + assert m['one'] == 2 +} + +fn test_map_in() { + m := map{ + 'Foo': 'bar' + } + if 'foo'.capitalize() in m { + println('ok') + } else { + assert false + } +} + +fn mut_map_with_relation_op_in_fn(mut m map[string]int) { + if m['one'] == 1 { + m['three'] = 3 + } + if m['two'] != 1 { + m['four'] = 4 + } + if m['one'] > 0 { + m['five'] = 5 + } + if m['one'] < 2 { + m['six'] = 6 + } + if m['two'] >= 2 { + m['seven'] = 7 + } + if m['two'] <= 2 { + m['eight'] = 8 + } +} + +fn test_mut_map_with_relation_op_in_fn() { + mut m := map{ + 'one': 1 + 'two': 2 + } + mut_map_with_relation_op_in_fn(mut m) + assert 'three' in m + assert 'four' in m + assert 'five' in m + assert 'six' in m + assert 'seven' in m + assert 'eight' in m +} + +fn test_map_str_after_delete() { + mut m := map{ + 'first': 1 + 'second': 2 + 'third': 3 + } + osm := '$m' + m.delete('second') + nsm := '$m' + println('m: $m') + assert osm == "{'first': 1, 'second': 2, 'third': 3}" + assert nsm == "{'first': 1, 'third': 3}" +} + +fn test_modify_map_value() { + mut m1 := map{ + 'foo': 3 + 'bar': -7 + } + m1['foo'] += 5 + m1['bar'] *= -2 + assert m1['foo'] == 8 + assert m1['bar'] == 14 +} + +fn test_map_clone() { + mut nums := map{ + 'foo': 1 + 'bar': 2 + } + mut nums2 := nums.clone() + nums2['foo']++ + nums2['bar'] *= 4 + assert nums['foo'] == 1 + assert nums['bar'] == 2 + assert nums2['foo'] == 2 + assert nums2['bar'] == 8 +} + +struct MValue { + name string + misc map[string]string +} + +fn test_map_default_zero() { + m := map[string]MValue{} + v := m['unknown'] + x := v.misc['x'] + println(x) + assert x == '' +} + +fn test_map_or() { + m := map{ + 'first': 1 + 'second': 2 + 'third': 3 + } + _ = m + // num := m['first'] or { return } +} + +fn test_int_keys() { + mut m := map[int]int{} + m[3] = 9 + m[4] = 16 + assert m.len == 2 + assert m[3] == 9 + assert m[4] == 16 + m[5] += 24 + m[5]++ + assert m[5] == 25 + mut m2 := map{ + 3: 9 + 4: 16 + 5: 25 + } + + four := 4 + m2.delete(3) + m2.delete(four) + m2.delete(5) + assert m2.len == 0 + assert m2[3] == 0 + assert m2[4] == 0 + assert m2[5] == 0 + assert m2.keys() == [] + + m2 = map{ + 3: 9 + 4: 16 + 5: 25 + } + + assert m2.len == 3 + // clone + mc := m.clone() + same := mc == m + assert same + assert mc.len == 3 + assert mc.keys() == [3, 4, 5] + mut all := []int{} + for k, v in mc { + assert m[k] == v + all << k + all << v + } + assert all == [3, 9, 4, 16, 5, 25] + + mut m3 := map{ + 1: 'one' + 2: 'two' + } + assert m3[1] == 'one' + m3.delete(1) +} + +enum Color { + red + green + blue +} + +type ColorAlias = Color + +fn test_alias_enum() { + mut m := map[ColorAlias]string{} + m[Color.red] = 'hi' + assert m[Color.red] == 'hi' +} + +fn test_enum_in_map() { + mut m := map[Color]string{} + m[Color.red] = 'hi' + assert Color.red in m + assert Color.green !in m + assert Color.blue !in m +} + +fn test_voidptr_keys() { + mut m := map[voidptr]string{} + v := 5 + m[&v] = 'var' + m[&m] = 'map' + assert m[&v] == 'var' + assert m[&m] == 'map' + assert m.len == 2 +} + +fn test_rune_keys() { + mut m := map{ + `!`: 2 + `%`: 3 + } + assert typeof(m).name == 'map[rune]int' + assert m[`!`] == 2 + m[`@`] = 7 + assert m.len == 3 + println(m) + assert '$m' == '{`!`: 2, `%`: 3, `@`: 7}' + + mut a := []rune{} + for k, v in m { + a << k + a << rune(v) + `0` + } + assert a == [`!`, `2`, `%`, `3`, `@`, `7`] +} + +fn test_eq() { + a := map{ + 'a': 1 + 'b': 2 + } + assert a == map{ + 'a': 1 + 'b': 2 + } + b := map{ + 'a': [[1]] + 'b': [[2]] + } + assert b == map{ + 'a': [[1]] + 'b': [[2]] + } + c := map{ + 'a': map{ + '11': 1 + } + 'b': map{ + '22': 2 + } + } + assert c == map{ + 'a': map{ + '11': 1 + } + 'b': map{ + '22': 2 + } + } + d := map{ + 'a': MValue{ + name: 'aa' + misc: map{ + '11': '1' + } + } + 'b': MValue{ + name: 'bb' + misc: map{ + '22': '2' + } + } + } + assert d == map{ + 'a': MValue{ + name: 'aa' + misc: map{ + '11': '1' + } + } + 'b': MValue{ + name: 'bb' + misc: map{ + '22': '2' + } + } + } +} + +fn test_non_string_key_map_str() { + assert map{ + 23: 4 + }.str() == '{23: 4}' + assert map{ + `a`: 12 + `b`: 13 + }.str() == '{`a`: 12, `b`: 13}' + assert map{ + 23: 'foo' + 25: 'bar' + }.str() == "{23: 'foo', 25: 'bar'}" +} + +fn test_map_assign_empty_map_init() { + mut a := map{ + 'one': 1 + } + a = map{} + println(a) + assert a == map[string]int{} + assert '$a' == '{}' +} + +fn test_in_map_literal() { + assert 1 in map{ + 1: 'one' + } +} + +fn test_byte_keys() { + mut m := map[byte]byte{} + byte_max := byte(255) + for i in byte(0) .. byte_max { + m[i] = i + assert m[i] == i + } + for k, v in m { + assert k == v + } + for i in byte(0) .. 100 { + m[i]++ + assert m[i] == i + 1 + } + assert m.len == byte_max + keys := m.keys() + for i in byte(0) .. byte_max { + assert keys[i] == i + } + for i in byte(0) .. byte_max { + m.delete(i) + assert m[i] == 0 + } + assert m.len == 0 +} + +fn test_i16_keys() { + mut m := map[i16]i16{} + end := i16(1000) + for i in i16(0) .. end { + m[i] = i + assert m[i] == i + } + for k, v in m { + assert k == v + } + for i in i16(0) .. 500 { + m[i]++ + assert m[i] == i + 1 + } + assert m.len == end + keys := m.keys() + for i in i16(0) .. end { + assert keys[i] == i + } + for i in i16(0) .. end { + m.delete(i) + assert m[i] == 0 + } + assert m.len == 0 +} + +fn test_u16_keys() { + mut m := map[u16]u16{} + end := u16(1000) + for i in u16(0) .. end { + m[i] = i + assert m[i] == i + } + for k, v in m { + assert k == v + } + for i in u16(0) .. 500 { + m[i]++ + assert m[i] == i + 1 + } + assert m.len == end + keys := m.keys() + for i in u16(0) .. end { + assert keys[i] == i + } + for i in u16(0) .. end { + m.delete(i) + assert m[i] == 0 + } + assert m.len == 0 +} + +fn test_u32_keys() { + mut m := map[u32]u32{} + end := u32(1000) + for i in u32(0) .. end { + m[i] = i + assert m[i] == i + } + for k, v in m { + assert k == v + } + for i in u32(0) .. 500 { + m[i]++ + assert m[i] == i + 1 + } + assert m.len == end + keys := m.keys() + for i in u32(0) .. end { + assert keys[i] == i + } + for i in u32(0) .. end { + m.delete(i) + assert m[i] == 0 + } + assert m.len == 0 +} + +fn test_int_keys2() { + mut m := map[int]int{} + end := 1000 + for i in int(0) .. end { + m[i] = i + assert m[i] == i + } + for k, v in m { + assert k == v + } + for i in int(0) .. 500 { + m[i]++ + assert m[i] == i + 1 + } + assert m.len == end + keys := m.keys() + for i in int(0) .. end { + assert keys[i] == i + } + for i in int(0) .. end { + m.delete(i) + assert m[i] == 0 + } + assert m.len == 0 +} + +fn test_i64_keys() { + mut m := map[i64]i64{} + end := i64(1000) + for i in i64(0) .. end { + m[i] = i + assert m[i] == i + } + for k, v in m { + assert k == v + } + for i in i64(0) .. 500 { + m[i]++ + assert m[i] == i + 1 + } + assert m.len == end + keys := m.keys() + for i in i64(0) .. end { + assert keys[i] == i + } + for i in i64(0) .. end { + m.delete(i) + assert m[i] == 0 + } + assert m.len == 0 +} + +fn test_u64_keys() { + mut m := map[u64]u64{} + end := u64(1000) + for i in u64(0) .. end { + m[i] = i + assert m[i] == i + } + for k, v in m { + assert k == v + } + for i in u64(0) .. 500 { + m[i]++ + assert m[i] == i + 1 + } + assert m.len == end + keys := m.keys() + for i in u64(0) .. end { + assert keys[i] == i + } + for i in u64(0) .. end { + m.delete(i) + assert m[i] == 0 + } + assert m.len == 0 +} + +fn test_map_set_fixed_array_variable() { + mut m := map[string][2]f64{} + m['A'] = [1.1, 2.2]! + println(m) + assert '$m' == "{'A': [1.1, 2.2]}" + + mut m2 := map[string][2]f64{} + arr := [1.1, 2.2]! + m2['A'] = arr + println(m2) + assert '$m2' == "{'A': [1.1, 2.2]}" +} diff --git a/v_windows/v/old/vlib/builtin/option.v b/v_windows/v/old/vlib/builtin/option.v new file mode 100644 index 0000000..4e8a14f --- /dev/null +++ b/v_windows/v/old/vlib/builtin/option.v @@ -0,0 +1,103 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module builtin + +// IError holds information about an error instance +pub interface IError { + msg string + code int +} + +// Error is the default implementation of IError, that is returned by e.g. `error()` +pub struct Error { +pub: + msg string + code int +} + +pub fn (err IError) str() string { + return match err { + None__ { 'none' } + Error { err.msg } + else { '$err.type_name(): $err.msg' } + } +} + +const none__ = IError(&None__{}) + +struct None__ { + msg string + code int +} + +fn (_ None__) str() string { + return 'none' +} + +[if trace_error ?] +fn trace_error(x string) { + eprintln('> ${@FN} | $x') +} + +// error returns a default error instance containing the error given in `message`. +// Example: `if ouch { return error('an error occurred') }` +[inline] +pub fn error(message string) IError { + trace_error(message) + return &Error{ + msg: message + } +} + +// error_with_code returns a default error instance containing the given `message` and error `code`. +// `if ouch { return error_with_code('an error occurred', 1) }` +[inline] +pub fn error_with_code(message string, code int) IError { + trace_error('$message | code: $code') + return &Error{ + msg: message + code: code + } +} + +// Option is the base of V's internal optional return system. +struct Option { + state byte + err IError = none__ + // Data is trailing after err + // and is not included in here but in the + // derived Option_xxx types +} + +fn opt_ok(data voidptr, mut option Option, size int) { + unsafe { + *option = Option{} + // use err to get the end of OptionBase and then memcpy into it + C.memcpy(&byte(&option.err) + sizeof(IError), data, size) + } +} + +[unsafe] +pub fn (e &Error) free() { + unsafe { e.msg.free() } +} + +[unsafe] +pub fn (n &None__) free() { + unsafe { n.msg.free() } +} + +[typedef] +struct C.IError { + _object voidptr +} + +[unsafe] +pub fn (ie &IError) free() { + unsafe { + ie.msg.free() + cie := &C.IError(ie) + free(cie._object) + } +} diff --git a/v_windows/v/old/vlib/builtin/prealloc.c.v b/v_windows/v/old/vlib/builtin/prealloc.c.v new file mode 100644 index 0000000..0ef66fc --- /dev/null +++ b/v_windows/v/old/vlib/builtin/prealloc.c.v @@ -0,0 +1,114 @@ +module builtin + +// With -prealloc, V calls libc's malloc to get chunks, each at least 16MB +// in size, as needed. Once a chunk is available, all malloc() calls within +// V code, that can fit inside the chunk, will use it instead, each bumping a +// pointer, till the chunk is filled. Once a chunk is filled, a new chunk will +// be allocated by calling libc's malloc, and the process continues. +// Each new chunk has a pointer to the old one, and at the end of the program, +// the entire linked list of chunks is freed. +// The goal of all this is to amortize the cost of calling libc's malloc, +// trading higher memory usage for a compiler (or any single threaded batch +// mode program), for a ~8-10% speed increase. +// NB: `-prealloc` is NOT safe to be used for multithreaded programs! + +// size of the preallocated chunk +const prealloc_block_size = 16 * 1024 * 1024 + +__global g_memory_block &VMemoryBlock +[heap] +struct VMemoryBlock { +mut: + id int + cap int + start &byte = 0 + previous &VMemoryBlock = 0 + remaining int + current &byte = 0 + mallocs int +} + +[unsafe] +fn vmemory_block_new(prev &VMemoryBlock, at_least int) &VMemoryBlock { + mut v := unsafe { &VMemoryBlock(C.calloc(1, sizeof(VMemoryBlock))) } + if prev != 0 { + v.id = prev.id + 1 + } + v.previous = prev + block_size := if at_least < prealloc_block_size { prealloc_block_size } else { at_least } + v.start = unsafe { C.malloc(block_size) } + v.cap = block_size + v.remaining = block_size + v.current = v.start + return v +} + +[unsafe] +fn vmemory_block_malloc(n int) &byte { + unsafe { + if g_memory_block.remaining < n { + g_memory_block = vmemory_block_new(g_memory_block, n) + } + mut res := &byte(0) + res = g_memory_block.current + g_memory_block.remaining -= n + g_memory_block.mallocs++ + g_memory_block.current += n + return res + } +} + +///////////////////////////////////////////////// + +[unsafe] +fn prealloc_vinit() { + unsafe { + g_memory_block = vmemory_block_new(voidptr(0), prealloc_block_size) + $if !freestanding { + C.atexit(prealloc_vcleanup) + } + } +} + +[unsafe] +fn prealloc_vcleanup() { + $if prealloc_stats ? { + // NB: we do 2 loops here, because string interpolation + // in the first loop may still use g_memory_block + // The second loop however should *not* allocate at all. + mut nr_mallocs := i64(0) + mut mb := g_memory_block + for mb != 0 { + nr_mallocs += mb.mallocs + eprintln('> freeing mb.id: ${mb.id:3} | cap: ${mb.cap:7} | rem: ${mb.remaining:7} | start: ${voidptr(mb.start)} | current: ${voidptr(mb.current)} | diff: ${u64(mb.current) - u64(mb.start):7} bytes | mallocs: $mb.mallocs') + mb = mb.previous + } + eprintln('> nr_mallocs: $nr_mallocs') + } + unsafe { + for g_memory_block != 0 { + C.free(g_memory_block.start) + g_memory_block = g_memory_block.previous + } + } +} + +[unsafe] +fn prealloc_malloc(n int) &byte { + return unsafe { vmemory_block_malloc(n) } +} + +[unsafe] +fn prealloc_realloc(old_data &byte, old_size int, new_size int) &byte { + new_ptr := unsafe { vmemory_block_malloc(new_size) } + min_size := if old_size < new_size { old_size } else { new_size } + unsafe { C.memcpy(new_ptr, old_data, min_size) } + return new_ptr +} + +[unsafe] +fn prealloc_calloc(n int) &byte { + new_ptr := unsafe { vmemory_block_malloc(n) } + unsafe { C.memset(new_ptr, 0, n) } + return new_ptr +} diff --git a/v_windows/v/old/vlib/builtin/rune.v b/v_windows/v/old/vlib/builtin/rune.v new file mode 100644 index 0000000..f756452 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/rune.v @@ -0,0 +1,62 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license that can be found in the LICENSE file. +module builtin + +// This was never working correctly, the issue is now +// fixed however the type checks in checker need to be +// updated. if you uncomment it you will see the issue +// type rune = int + +pub fn (c rune) str() string { + return utf32_to_str(u32(c)) + /* + unsafe { + fst_byte := int(c)>>8 * 3 & 0xff + len := utf8_char_len(byte(fst_byte)) + println('len=$len') + mut str := string{ + len: len + str: malloc(len + 1) + } + for i in 0..len { + str.str[i] = byte(int(c)>>8 * (3 - i) & 0xff) + } + str.str[len] = `\0` + println(str) + return str + } + */ +} + +// string converts a rune array to a string +pub fn (ra []rune) string() string { + mut res := '' + for r in ra { + res += r.str() + } + return res +} + +// Define this on byte as well, so that we can do `s[0].is_capital()` +pub fn (c byte) is_capital() bool { + return c >= `A` && c <= `Z` +} + +pub fn (b []byte) clone() []byte { + mut res := []byte{len: b.len} + // mut res := make([]byte, {repeat:b.len}) + for i in 0 .. b.len { + res[i] = b[i] + } + return res +} + +// TODO: remove this once runes are implemented +pub fn (b []byte) bytestr() string { + unsafe { + buf := malloc_noscan(b.len + 1) + C.memcpy(buf, b.data, b.len) + buf[b.len] = 0 + return tos(buf, b.len) + } +} diff --git a/v_windows/v/old/vlib/builtin/sorted_map.v b/v_windows/v/old/vlib/builtin/sorted_map.v new file mode 100644 index 0000000..2567a3c --- /dev/null +++ b/v_windows/v/old/vlib/builtin/sorted_map.v @@ -0,0 +1,457 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. + +module builtin + +// import strings + +// B-trees are balanced search trees with all leaves at +// the same level. B-trees are generally faster than +// binary search trees due to the better locality of +// reference, since multiple keys are stored in one node. + +// The number for `degree` has been picked through vigor- +// ous benchmarking but can be changed to any number > 1. +// `degree` determines the maximum length of each node. +const ( + degree = 6 + mid_index = degree - 1 + max_len = 2 * degree - 1 + children_bytes = sizeof(voidptr) * (max_len + 1) +) + +pub struct SortedMap { + value_bytes int +mut: + root &mapnode +pub mut: + len int +} + +struct mapnode { +mut: + children &voidptr + len int + keys [11]string // TODO: Should use `max_len` + values [11]voidptr // TODO: Should use `max_len` +} + +fn new_sorted_map(n int, value_bytes int) SortedMap { // TODO: Remove `n` + return SortedMap{ + value_bytes: value_bytes + root: new_node() + len: 0 + } +} + +fn new_sorted_map_init(n int, value_bytes int, keys &string, values voidptr) SortedMap { + mut out := new_sorted_map(n, value_bytes) + for i in 0 .. n { + unsafe { + out.set(keys[i], &byte(values) + i * value_bytes) + } + } + return out +} + +// The tree is initialized with an empty node as root to +// avoid having to check whether the root is null for +// each insertion. +fn new_node() &mapnode { + return &mapnode{ + children: 0 + len: 0 + } +} + +// This implementation does proactive insertion, meaning +// that splits are done top-down and not bottom-up. +fn (mut m SortedMap) set(key string, value voidptr) { + mut node := m.root + mut child_index := 0 + mut parent := &mapnode(0) + for { + if node.len == max_len { + if isnil(parent) { + parent = new_node() + m.root = parent + } + parent.split_child(child_index, mut node) + if key == parent.keys[child_index] { + unsafe { + C.memcpy(parent.values[child_index], value, m.value_bytes) + } + return + } + if key < parent.keys[child_index] { + node = unsafe { &mapnode(parent.children[child_index]) } + } else { + node = unsafe { &mapnode(parent.children[child_index + 1]) } + } + } + mut i := 0 + for i < node.len && key > node.keys[i] { + i++ + } + if i != node.len && key == node.keys[i] { + unsafe { + C.memcpy(node.values[i], value, m.value_bytes) + } + return + } + if isnil(node.children) { + mut j := node.len - 1 + for j >= 0 && key < node.keys[j] { + node.keys[j + 1] = node.keys[j] + node.values[j + 1] = node.values[j] + j-- + } + node.keys[j + 1] = key + unsafe { + node.values[j + 1] = malloc(m.value_bytes) + C.memcpy(node.values[j + 1], value, m.value_bytes) + } + node.len++ + m.len++ + return + } + parent = node + child_index = i + node = unsafe { &mapnode(node.children[child_index]) } + } +} + +fn (mut n mapnode) split_child(child_index int, mut y mapnode) { + mut z := new_node() + z.len = mid_index + y.len = mid_index + for j := mid_index - 1; j >= 0; j-- { + z.keys[j] = y.keys[j + degree] + z.values[j] = y.values[j + degree] + } + if !isnil(y.children) { + z.children = unsafe { &voidptr(malloc(int(children_bytes))) } + for jj := degree - 1; jj >= 0; jj-- { + unsafe { + z.children[jj] = y.children[jj + degree] + } + } + } + unsafe { + if isnil(n.children) { + n.children = &voidptr(malloc(int(children_bytes))) + } + n.children[n.len + 1] = n.children[n.len] + } + for j := n.len; j > child_index; j-- { + n.keys[j] = n.keys[j - 1] + n.values[j] = n.values[j - 1] + unsafe { + n.children[j] = n.children[j - 1] + } + } + n.keys[child_index] = y.keys[mid_index] + n.values[child_index] = y.values[mid_index] + unsafe { + n.children[child_index] = voidptr(y) + n.children[child_index + 1] = voidptr(z) + } + n.len++ +} + +fn (m SortedMap) get(key string, out voidptr) bool { + mut node := m.root + for { + mut i := node.len - 1 + for i >= 0 && key < node.keys[i] { + i-- + } + if i != -1 && key == node.keys[i] { + unsafe { + C.memcpy(out, node.values[i], m.value_bytes) + } + return true + } + if isnil(node.children) { + break + } + node = unsafe { &mapnode(node.children[i + 1]) } + } + return false +} + +fn (m SortedMap) exists(key string) bool { + if isnil(m.root) { // TODO: find out why root can be nil + return false + } + mut node := m.root + for { + mut i := node.len - 1 + for i >= 0 && key < node.keys[i] { + i-- + } + if i != -1 && key == node.keys[i] { + return true + } + if isnil(node.children) { + break + } + node = unsafe { &mapnode(node.children[i + 1]) } + } + return false +} + +fn (n &mapnode) find_key(k string) int { + mut idx := 0 + for idx < n.len && n.keys[idx] < k { + idx++ + } + return idx +} + +fn (mut n mapnode) remove_key(k string) bool { + idx := n.find_key(k) + if idx < n.len && n.keys[idx] == k { + if isnil(n.children) { + n.remove_from_leaf(idx) + } else { + n.remove_from_non_leaf(idx) + } + return true + } else { + if isnil(n.children) { + return false + } + flag := if idx == n.len { true } else { false } + if unsafe { &mapnode(n.children[idx]) }.len < degree { + n.fill(idx) + } + + mut node := &mapnode(0) + if flag && idx > n.len { + node = unsafe { &mapnode(n.children[idx - 1]) } + } else { + node = unsafe { &mapnode(n.children[idx]) } + } + return node.remove_key(k) + } +} + +fn (mut n mapnode) remove_from_leaf(idx int) { + for i := idx + 1; i < n.len; i++ { + n.keys[i - 1] = n.keys[i] + n.values[i - 1] = n.values[i] + } + n.len-- +} + +fn (mut n mapnode) remove_from_non_leaf(idx int) { + k := n.keys[idx] + if unsafe { &mapnode(n.children[idx]) }.len >= degree { + mut current := unsafe { &mapnode(n.children[idx]) } + for !isnil(current.children) { + current = unsafe { &mapnode(current.children[current.len]) } + } + predecessor := current.keys[current.len - 1] + n.keys[idx] = predecessor + n.values[idx] = current.values[current.len - 1] + mut node := unsafe { &mapnode(n.children[idx]) } + node.remove_key(predecessor) + } else if unsafe { &mapnode(n.children[idx + 1]) }.len >= degree { + mut current := unsafe { &mapnode(n.children[idx + 1]) } + for !isnil(current.children) { + current = unsafe { &mapnode(current.children[0]) } + } + successor := current.keys[0] + n.keys[idx] = successor + n.values[idx] = current.values[0] + mut node := unsafe { &mapnode(n.children[idx + 1]) } + node.remove_key(successor) + } else { + n.merge(idx) + mut node := unsafe { &mapnode(n.children[idx]) } + node.remove_key(k) + } +} + +fn (mut n mapnode) fill(idx int) { + if idx != 0 && unsafe { &mapnode(n.children[idx - 1]) }.len >= degree { + n.borrow_from_prev(idx) + } else if idx != n.len && unsafe { &mapnode(n.children[idx + 1]) }.len >= degree { + n.borrow_from_next(idx) + } else if idx != n.len { + n.merge(idx) + } else { + n.merge(idx - 1) + } +} + +fn (mut n mapnode) borrow_from_prev(idx int) { + mut child := unsafe { &mapnode(n.children[idx]) } + mut sibling := unsafe { &mapnode(n.children[idx - 1]) } + for i := child.len - 1; i >= 0; i-- { + child.keys[i + 1] = child.keys[i] + child.values[i + 1] = child.values[i] + } + if !isnil(child.children) { + for i := child.len; i >= 0; i-- { + unsafe { + child.children[i + 1] = child.children[i] + } + } + } + child.keys[0] = n.keys[idx - 1] + child.values[0] = n.values[idx - 1] + if !isnil(child.children) { + unsafe { + child.children[0] = sibling.children[sibling.len] + } + } + n.keys[idx - 1] = sibling.keys[sibling.len - 1] + n.values[idx - 1] = sibling.values[sibling.len - 1] + child.len++ + sibling.len-- +} + +fn (mut n mapnode) borrow_from_next(idx int) { + mut child := unsafe { &mapnode(n.children[idx]) } + mut sibling := unsafe { &mapnode(n.children[idx + 1]) } + child.keys[child.len] = n.keys[idx] + child.values[child.len] = n.values[idx] + if !isnil(child.children) { + unsafe { + child.children[child.len + 1] = sibling.children[0] + } + } + n.keys[idx] = sibling.keys[0] + n.values[idx] = sibling.values[0] + for i := 1; i < sibling.len; i++ { + sibling.keys[i - 1] = sibling.keys[i] + sibling.values[i - 1] = sibling.values[i] + } + if !isnil(sibling.children) { + for i := 1; i <= sibling.len; i++ { + unsafe { + sibling.children[i - 1] = sibling.children[i] + } + } + } + child.len++ + sibling.len-- +} + +fn (mut n mapnode) merge(idx int) { + mut child := unsafe { &mapnode(n.children[idx]) } + sibling := unsafe { &mapnode(n.children[idx + 1]) } + child.keys[mid_index] = n.keys[idx] + child.values[mid_index] = n.values[idx] + for i in 0 .. sibling.len { + child.keys[i + degree] = sibling.keys[i] + child.values[i + degree] = sibling.values[i] + } + if !isnil(child.children) { + for i := 0; i <= sibling.len; i++ { + unsafe { + child.children[i + degree] = sibling.children[i] + } + } + } + for i := idx + 1; i < n.len; i++ { + n.keys[i - 1] = n.keys[i] + n.values[i - 1] = n.values[i] + } + for i := idx + 2; i <= n.len; i++ { + unsafe { + n.children[i - 1] = n.children[i] + } + } + child.len += sibling.len + 1 + n.len-- + // free(sibling) +} + +pub fn (mut m SortedMap) delete(key string) { + if m.root.len == 0 { + return + } + + removed := m.root.remove_key(key) + if removed { + m.len-- + } + + if m.root.len == 0 { + // tmp := t.root + if isnil(m.root.children) { + return + } else { + m.root = unsafe { &mapnode(m.root.children[0]) } + } + // free(tmp) + } +} + +// Insert all keys of the subtree into array `keys` +// starting at `at`. Keys are inserted in order. +fn (n &mapnode) subkeys(mut keys []string, at int) int { + mut position := at + if !isnil(n.children) { + // Traverse children and insert + // keys inbetween children + for i in 0 .. n.len { + child := unsafe { &mapnode(n.children[i]) } + position += child.subkeys(mut keys, position) + keys[position] = n.keys[i] + position++ + } + // Insert the keys of the last child + child := unsafe { &mapnode(n.children[n.len]) } + position += child.subkeys(mut keys, position) + } else { + // If leaf, insert keys + for i in 0 .. n.len { + keys[position + i] = n.keys[i] + } + position += n.len + } + // Return # of added keys + return position - at +} + +pub fn (m &SortedMap) keys() []string { + mut keys := []string{len: m.len} + if isnil(m.root) || m.root.len == 0 { + return keys + } + m.root.subkeys(mut keys, 0) + return keys +} + +fn (mut n mapnode) free() { + println('TODO') +} + +pub fn (mut m SortedMap) free() { + if isnil(m.root) { + return + } + m.root.free() +} + +pub fn (m SortedMap) print() { + println('TODO') +} + +// pub fn (m map_string) str() string { +// if m.len == 0 { +// return '{}' +// } +// mut sb := strings.new_builder(50) +// sb.writeln('{') +// for key, val in m { +// sb.writeln(' "$key" => "$val"') +// } +// sb.writeln('}') +// return sb.str() +// } diff --git a/v_windows/v/old/vlib/builtin/sorting_test.v b/v_windows/v/old/vlib/builtin/sorting_test.v new file mode 100644 index 0000000..4d0ff9f --- /dev/null +++ b/v_windows/v/old/vlib/builtin/sorting_test.v @@ -0,0 +1,84 @@ +const ( + unsorted = [2, 30, 10, 20, 1] + sorted_asc = [1, 2, 10, 20, 30] + sorted_desc = [30, 20, 10, 2, 1] +) + +fn test_sorting_simple() { + mut a := unsorted.clone() + a.sort() + eprintln(' a: $a') + assert a == sorted_asc +} + +fn test_sorting_with_condition_expression() { + mut a := unsorted.clone() + a.sort(a > b) + eprintln(' a: $a') + assert a == sorted_desc +} + +fn test_sorting_primitives_with_condition_expression() { + mut x := ['9', '87', '3210', '654'] + x.sort(a.len < b.len) + assert x == ['9', '87', '654', '3210'] +} + +// fn get_score(word string) int { +// mut total := 0 +// for letter in word { +// total += int(letter) - 97 +// } +// return total +// } + +// fn test_sorting_with_fn_call_in_condition_expression() { +// mut words := ['aaaa', 'a', 'b', 'foo', 'bar'] +// words.sort(get_score(a) < get_score(b)) +// } + +fn mysort(mut a []int) { + a.sort() +} + +fn test_sorting_by_passing_a_mut_array_to_a_function() { + mut a := unsorted.clone() + mysort(mut a) + eprintln(' a: $a') + assert a == sorted_asc +} + +/* +fn test_sorting_by_passing_an_anonymous_sorting_function() { + mut a := unsorted + a.sort(fn(a &int, b &int) int { return *b - *a }) + eprintln(' a: $a') + assert a == sort_desc +} +*/ +fn test_sorting_u64s() { + mut a := [u64(3), 2, 1, 9, 0, 8] + a.sort() + eprintln(' a: $a') + assert a == [u64(0), 1, 2, 3, 8, 9] + a.sort(a > b) + eprintln(' a: $a') + assert a == [u64(9), 8, 3, 2, 1, 0] +} + +struct User { + age int + name string +} + +fn g(mut users []User) { + users.sort(a.name > b.name) +} + +fn f(mut users []User) { + users.sort(a.name < b.name) +} + +fn z(mut users []User) { + users.sort(a.name < b.name) +} diff --git a/v_windows/v/old/vlib/builtin/string.v b/v_windows/v/old/vlib/builtin/string.v new file mode 100644 index 0000000..169ed2d --- /dev/null +++ b/v_windows/v/old/vlib/builtin/string.v @@ -0,0 +1,1640 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module builtin + +import strconv + +/* +NB: A V string should be/is immutable from the point of view of + V user programs after it is first created. A V string is + also slightly larger than the equivalent C string because + the V string also has an integer length attached. + + This tradeoff is made, since V strings are created just *once*, + but potentially used *many times* over their lifetime. + + The V string implementation uses a struct, that has a .str field, + which points to a C style 0 terminated memory block. Although not + strictly necessary from the V point of view, that additional 0 + is *very useful for C interoperability*. + + The V string implementation also has an integer .len field, + containing the length of the .str field, excluding the + terminating 0 (just like the C's strlen(s) would do). + + The 0 ending of .str, and the .len field, mean that in practice: + a) a V string s can be used very easily, wherever a + C string is needed, just by passing s.str, + without a need for further conversion/copying. + + b) where strlen(s) is needed, you can just pass s.len, + without having to constantly recompute the length of s + *over and over again* like some C programs do. This is because + V strings are immutable and so their length does not change. + + Ordinary V code *does not need* to be concerned with the + additional 0 in the .str field. The 0 *must* be put there by the + low level string creating functions inside this module. + + Failing to do this will lead to programs that work most of the + time, when used with pure V functions, but fail in strange ways, + when used with modules using C functions (for example os and so on). +*/ +pub struct string { +pub: + str &byte = 0 // points to a C style 0 terminated string of bytes. + len int // the length of the .str field, excluding the ending 0 byte. It is always equal to strlen(.str). + // NB string.is_lit is an enumeration of the following: + // .is_lit == 0 => a fresh string, should be freed by autofree + // .is_lit == 1 => a literal string from .rodata, should NOT be freed + // .is_lit == -98761234 => already freed string, protects against double frees. + // ---------> ^^^^^^^^^ calling free on these is a bug. + // Any other value means that the string has been corrupted. +mut: + is_lit int +} + +// vstrlen returns the V length of the C string `s` (0 terminator is not counted). +[unsafe] +pub fn vstrlen(s &byte) int { + return unsafe { C.strlen(&char(s)) } +} + +pub fn (s string) runes() []rune { + mut runes := []rune{cap: s.len} + for i := 0; i < s.len; i++ { + char_len := utf8_char_len(unsafe { s.str[i] }) + if char_len > 1 { + end := if s.len - 1 >= i + char_len { i + char_len } else { s.len } + mut r := unsafe { s[i..end] } + runes << r.utf32_code() + i += char_len - 1 + } else { + runes << unsafe { s.str[i] } + } + } + return runes +} + +// tos converts a C string to a V string. +// String data is reused, not copied. +[unsafe] +pub fn tos(s &byte, len int) string { + // This should never happen. + if s == 0 { + panic('tos(): nil string') + } + return string{ + str: unsafe { s } + len: len + } +} + +// tos_clone returns a copy of `s`. +[unsafe] +pub fn tos_clone(s &byte) string { + return unsafe { tos2(s) }.clone() +} + +// tos2 does the same as `tos`, but also calculates the length. Called by `string(bytes)` casts. +// Used only internally. +[unsafe] +pub fn tos2(s &byte) string { + if s == 0 { + panic('tos2: nil string') + } + return string{ + str: unsafe { s } + len: unsafe { vstrlen(s) } + } +} + +// tos3 does the same as `tos2`, but for char*, to avoid warnings. +[unsafe] +pub fn tos3(s &char) string { + if s == 0 { + panic('tos3: nil string') + } + return string{ + str: &byte(s) + len: unsafe { C.strlen(s) } + } +} + +// tos4 does the same as `tos2`, but returns an empty string on nil ptr. +[unsafe] +pub fn tos4(s &byte) string { + if s == 0 { + return '' + } + return unsafe { tos2(s) } +} + +// tos5 does the same as `tos4`, but for char*, to avoid warnings. +[unsafe] +pub fn tos5(s &char) string { + if s == 0 { + return '' + } + return unsafe { tos3(s) } +} + +// vstring converts a C style string to a V string. NB: the string data is reused, NOT copied. +// strings returned from this function will be normal V strings beside that (i.e. they would be +// freed by V's -autofree mechanism, when they are no longer used). +[unsafe] +pub fn (bp &byte) vstring() string { + return string{ + str: unsafe { bp } + len: unsafe { C.strlen(&char(bp)) } + } +} + +// vstring_with_len converts a C style string to a V string. +// NB: the string data is reused, NOT copied. +[unsafe] +pub fn (bp &byte) vstring_with_len(len int) string { + return string{ + str: unsafe { bp } + len: len + is_lit: 0 + } +} + +// vstring converts C char* to V string. +// NB: the string data is reused, NOT copied. +[unsafe] +pub fn (cp &char) vstring() string { + return string{ + str: &byte(cp) + len: unsafe { C.strlen(cp) } + is_lit: 0 + } +} + +// vstring_with_len converts C char* to V string. +// NB: the string data is reused, NOT copied. +[unsafe] +pub fn (cp &char) vstring_with_len(len int) string { + return string{ + str: &byte(cp) + len: len + is_lit: 0 + } +} + +// vstring_literal converts a C style string to a V string. +// NB: the string data is reused, NOT copied. +// NB2: unlike vstring, vstring_literal will mark the string +// as a literal, so it will not be freed by autofree. +// This is suitable for readonly strings, C string literals etc, +// that can be read by the V program, but that should not be +// managed by it, for example `os.args` is implemented using it. +[unsafe] +pub fn (bp &byte) vstring_literal() string { + return string{ + str: unsafe { bp } + len: unsafe { C.strlen(&char(bp)) } + is_lit: 1 + } +} + +// vstring_with_len converts a C style string to a V string. +// NB: the string data is reused, NOT copied. +[unsafe] +pub fn (bp &byte) vstring_literal_with_len(len int) string { + return string{ + str: unsafe { bp } + len: len + is_lit: 1 + } +} + +// vstring_literal converts C char* to V string. +// See also vstring_literal defined on byteptr for more details. +// NB: the string data is reused, NOT copied. +[unsafe] +pub fn (cp &char) vstring_literal() string { + return string{ + str: &byte(cp) + len: unsafe { C.strlen(cp) } + is_lit: 1 + } +} + +// vstring_literal_with_len converts C char* to V string. +// See also vstring_literal_with_len defined on byteptr. +// NB: the string data is reused, NOT copied. +[unsafe] +pub fn (cp &char) vstring_literal_with_len(len int) string { + return string{ + str: &byte(cp) + len: len + is_lit: 1 + } +} + +// clone_static returns an independent copy of a given array. +// It should be used only in -autofree generated code. +fn (a string) clone_static() string { + return a.clone() +} + +// clone returns a copy of the V string `a`. +pub fn (a string) clone() string { + if a.len == 0 { + return '' + } + mut b := string{ + str: unsafe { malloc_noscan(a.len + 1) } + len: a.len + } + unsafe { + C.memcpy(b.str, a.str, a.len) + b.str[a.len] = 0 + } + return b +} + +// cstring_to_vstring creates a copy of cstr and turns it into a v string. +[unsafe] +pub fn cstring_to_vstring(cstr &char) string { + return unsafe { tos_clone(&byte(cstr)) } +} + +// replace_once replaces the first occurence of `rep` with the string passed in `with`. +pub fn (s string) replace_once(rep string, with string) string { + idx := s.index_(rep) + if idx == -1 { + return s.clone() + } + return s.substr(0, idx) + with + s.substr(idx + rep.len, s.len) +} + +// replace replaces all occurences of `rep` with the string passed in `with`. +[direct_array_access] +pub fn (s string) replace(rep string, with string) string { + if s.len == 0 || rep.len == 0 || rep.len > s.len { + return s.clone() + } + if !s.contains(rep) { + return s.clone() + } + // TODO PERF Allocating ints is expensive. Should be a stack array + // Get locations of all reps within this string + mut idxs := []int{cap: s.len / rep.len} + defer { + unsafe { idxs.free() } + } + mut idx := 0 + for { + idx = s.index_after(rep, idx) + if idx == -1 { + break + } + idxs << idx + idx += rep.len + } + // Dont change the string if there's nothing to replace + if idxs.len == 0 { + return s.clone() + } + // Now we know the number of replacements we need to do and we can calc the len of the new string + new_len := s.len + idxs.len * (with.len - rep.len) + mut b := unsafe { malloc_noscan(new_len + 1) } // add space for the null byte at the end + // Fill the new string + mut b_i := 0 + mut s_idx := 0 + for _, rep_pos in idxs { + for i in s_idx .. rep_pos { // copy everything up to piece being replaced + unsafe { + b[b_i] = s[i] + } + b_i++ + } + s_idx = rep_pos + rep.len // move string index past replacement + for i in 0 .. with.len { // copy replacement piece + unsafe { + b[b_i] = with[i] + } + b_i++ + } + } + if s_idx < s.len { // if any original after last replacement, copy it + for i in s_idx .. s.len { + unsafe { + b[b_i] = s[i] + } + b_i++ + } + } + unsafe { + b[new_len] = 0 + return tos(b, new_len) + } +} + +struct RepIndex { + idx int + val_idx int +} + +// compare_rep_index returns the result of comparing RepIndex `a` and `b`. +fn compare_rep_index(a &RepIndex, b &RepIndex) int { + if a.idx < b.idx { + return -1 + } + if a.idx > b.idx { + return 1 + } + return 0 +} + +// sort2 sorts the RepIndex array using `compare_rep_index`. +fn (mut a []RepIndex) sort2() { + a.sort_with_compare(compare_rep_index) +} + +// replace_each replaces all occurences of the string pairs given in `vals`. +// Example: assert 'ABCD'.replace_each(['B','C/','C','D','D','C']) == 'AC/DC' +[direct_array_access] +pub fn (s string) replace_each(vals []string) string { + if s.len == 0 || vals.len == 0 { + return s.clone() + } + if vals.len % 2 != 0 { + eprintln('string.replace_each(): odd number of strings') + return s.clone() + } + // `rep` - string to replace + // `with` - string to replace with + // Remember positions of all rep strings, and calculate the length + // of the new string to do just one allocation. + mut new_len := s.len + mut idxs := []RepIndex{} + mut idx := 0 + s_ := s.clone() + for rep_i := 0; rep_i < vals.len; rep_i += 2 { + // vals: ['rep1, 'with1', 'rep2', 'with2'] + rep := vals[rep_i] + with := vals[rep_i + 1] + for { + idx = s_.index_after(rep, idx) + if idx == -1 { + break + } + // The string already found is set to `/del`, to avoid duplicate searches. + for i in 0 .. rep.len { + unsafe { + s_.str[idx + i] = 127 + } + } + // We need to remember both the position in the string, + // and which rep/with pair it refers to. + idxs << RepIndex{ + idx: idx + val_idx: rep_i + } + idx += rep.len + new_len += with.len - rep.len + } + } + // Dont change the string if there's nothing to replace + if idxs.len == 0 { + return s.clone() + } + idxs.sort2() + mut b := unsafe { malloc_noscan(new_len + 1) } // add space for 0 terminator + // Fill the new string + mut idx_pos := 0 + mut cur_idx := idxs[idx_pos] + mut b_i := 0 + for i := 0; i < s.len; i++ { + if i == cur_idx.idx { + // Reached the location of rep, replace it with "with" + rep := vals[cur_idx.val_idx] + with := vals[cur_idx.val_idx + 1] + for j in 0 .. with.len { + unsafe { + b[b_i] = with[j] + } + b_i++ + } + // Skip the length of rep, since we just replaced it with "with" + i += rep.len - 1 + // Go to the next index + idx_pos++ + if idx_pos < idxs.len { + cur_idx = idxs[idx_pos] + } + } else { + // Rep doesnt start here, just copy + unsafe { + b[b_i] = s.str[i] + } + b_i++ + } + } + unsafe { + b[new_len] = 0 + return tos(b, new_len) + } +} + +// bool returns `true` if the string equals the word "true" it will return `false` otherwise. +pub fn (s string) bool() bool { + return s == 'true' || s == 't' // TODO t for pg, remove +} + +// int returns the value of the string as an integer `'1'.int() == 1`. +pub fn (s string) int() int { + return int(strconv.common_parse_int(s, 0, 32, false, false) or { 0 }) +} + +// i64 returns the value of the string as i64 `'1'.i64() == i64(1)`. +pub fn (s string) i64() i64 { + return strconv.common_parse_int(s, 0, 64, false, false) or { 0 } +} + +// i8 returns the value of the string as i8 `'1'.i8() == i8(1)`. +pub fn (s string) i8() i8 { + return i8(strconv.common_parse_int(s, 0, 8, false, false) or { 0 }) +} + +// i16 returns the value of the string as i16 `'1'.i16() == i16(1)`. +pub fn (s string) i16() i16 { + return i16(strconv.common_parse_int(s, 0, 16, false, false) or { 0 }) +} + +// f32 returns the value of the string as f32 `'1.0'.f32() == f32(1)`. +pub fn (s string) f32() f32 { + // return C.atof(&char(s.str)) + return f32(strconv.atof64(s)) +} + +// f64 returns the value of the string as f64 `'1.0'.f64() == f64(1)`. +pub fn (s string) f64() f64 { + // return C.atof(&char(s.str)) + return strconv.atof64(s) +} + +// u16 returns the value of the string as u16 `'1'.u16() == u16(1)`. +pub fn (s string) u16() u16 { + return u16(strconv.common_parse_uint(s, 0, 16, false, false) or { 0 }) +} + +// u32 returns the value of the string as u32 `'1'.u32() == u32(1)`. +pub fn (s string) u32() u32 { + return u32(strconv.common_parse_uint(s, 0, 32, false, false) or { 0 }) +} + +// u64 returns the value of the string as u64 `'1'.u64() == u64(1)`. +pub fn (s string) u64() u64 { + return strconv.common_parse_uint(s, 0, 64, false, false) or { 0 } +} + +[direct_array_access] +fn (s string) == (a string) bool { + if s.str == 0 { + // should never happen + panic('string.eq(): nil string') + } + if s.len != a.len { + return false + } + if s.len > 0 { + last_idx := s.len - 1 + if s[last_idx] != a[last_idx] { + return false + } + } + unsafe { + return C.memcmp(s.str, a.str, a.len) == 0 + } +} + +fn (s string) < (a string) bool { + for i in 0 .. s.len { + if i >= a.len || s[i] > a[i] { + return false + } else if s[i] < a[i] { + return true + } + } + if s.len < a.len { + return true + } + return false +} + +fn (s string) + (a string) string { + new_len := a.len + s.len + mut res := string{ + str: unsafe { malloc_noscan(new_len + 1) } + len: new_len + } + for j in 0 .. s.len { + unsafe { + res.str[j] = s.str[j] + } + } + for j in 0 .. a.len { + unsafe { + res.str[s.len + j] = a.str[j] + } + } + unsafe { + res.str[new_len] = 0 // V strings are not null terminated, but just in case + } + return res +} + +// split splits the string to an array by `delim`. +// Example: assert 'A B C'.split(' ') == ['A','B','C'] +// If `delim` is empty the string is split by it's characters. +// Example: assert 'DEF'.split('') == ['D','E','F'] +pub fn (s string) split(delim string) []string { + return s.split_nth(delim, 0) +} + +// split_nth splits the string based on the passed `delim` substring. +// It returns the first Nth parts. When N=0, return all the splits. +// The last returned element has the remainder of the string, even if +// the remainder contains more `delim` substrings. +[direct_array_access] +pub fn (s string) split_nth(delim string, nth int) []string { + mut res := []string{} + mut i := 0 + + match delim.len { + 0 { + i = 1 + for ch in s { + if nth > 0 && i >= nth { + res << s[i..] + break + } + res << ch.ascii_str() + i++ + } + return res + } + 1 { + mut start := 0 + delim_byte := delim[0] + + for i < s.len { + if s[i] == delim_byte { + was_last := nth > 0 && res.len == nth - 1 + if was_last { + break + } + val := s.substr(start, i) + res << val + start = i + delim.len + i = start + } else { + i++ + } + } + + // Then the remaining right part of the string + if nth < 1 || res.len < nth { + res << s[start..] + } + return res + } + else { + mut start := 0 + // Take the left part for each delimiter occurence + for i <= s.len { + is_delim := i + delim.len <= s.len && s.substr(i, i + delim.len) == delim + if is_delim { + was_last := nth > 0 && res.len == nth - 1 + if was_last { + break + } + val := s.substr(start, i) + res << val + start = i + delim.len + i = start + } else { + i++ + } + } + // Then the remaining right part of the string + if nth < 1 || res.len < nth { + res << s[start..] + } + return res + } + } +} + +// split_into_lines splits the string by newline characters. +// newlines are stripped. +// Both `\n` and `\r\n` newline endings are supported. +[direct_array_access] +pub fn (s string) split_into_lines() []string { + mut res := []string{} + if s.len == 0 { + return res + } + mut start := 0 + mut end := 0 + for i := 0; i < s.len; i++ { + if s[i] == 10 { + end = if i > 0 && s[i - 1] == 13 { i - 1 } else { i } + res << if start == end { '' } else { s[start..end] } + start = i + 1 + } + } + if start < s.len { + res << s[start..] + } + return res +} + +// used internally for [2..4] +fn (s string) substr2(start int, _end int, end_max bool) string { + end := if end_max { s.len } else { _end } + return s.substr(start, end) +} + +// substr returns the string between index positions `start` and `end`. +// Example: assert 'ABCD'.substr(1,3) == 'BC' +pub fn (s string) substr(start int, end int) string { + $if !no_bounds_checking ? { + if start > end || start > s.len || end > s.len || start < 0 || end < 0 { + panic('substr($start, $end) out of bounds (len=$s.len)') + } + } + len := end - start + if len == s.len { + return s.clone() + } + mut res := string{ + str: unsafe { malloc_noscan(len + 1) } + len: len + } + for i in 0 .. len { + unsafe { + res.str[i] = s.str[start + i] + } + } + unsafe { + res.str[len] = 0 + } + return res +} + +// index returns the position of the first character of the input string. +// It will return `-1` if the input string can't be found. +fn (s string) index_(p string) int { + if p.len > s.len || p.len == 0 { + return -1 + } + if p.len > 2 { + return s.index_kmp(p) + } + mut i := 0 + for i < s.len { + mut j := 0 + for j < p.len && unsafe { s.str[i + j] == p.str[j] } { + j++ + } + if j == p.len { + return i + } + i++ + } + return -1 +} + +// index returns the position of the first character of the input string. +// It will return `none` if the input string can't be found. +pub fn (s string) index(p string) ?int { + idx := s.index_(p) + if idx == -1 { + return none + } + return idx +} + +// index_kmp does KMP search. +[direct_array_access; manualfree] +fn (s string) index_kmp(p string) int { + if p.len > s.len { + return -1 + } + mut prefix := []int{len: p.len} + defer { + unsafe { prefix.free() } + } + mut j := 0 + for i := 1; i < p.len; i++ { + for unsafe { p.str[j] != p.str[i] } && j > 0 { + j = prefix[j - 1] + } + if unsafe { p.str[j] == p.str[i] } { + j++ + } + prefix[i] = j + } + j = 0 + for i in 0 .. s.len { + for unsafe { p.str[j] != s.str[i] } && j > 0 { + j = prefix[j - 1] + } + if unsafe { p.str[j] == s.str[i] } { + j++ + } + if j == p.len { + return i - p.len + 1 + } + } + return -1 +} + +// index_any returns the position of any of the characters in the input string - if found. +pub fn (s string) index_any(chars string) int { + for c in chars { + idx := s.index_(c.ascii_str()) + if idx == -1 { + continue + } + return idx + } + return -1 +} + +// last_index returns the position of the last occurence of the input string. +fn (s string) last_index_(p string) int { + if p.len > s.len || p.len == 0 { + return -1 + } + mut i := s.len - p.len + for i >= 0 { + mut j := 0 + for j < p.len && unsafe { s.str[i + j] == p.str[j] } { + j++ + } + if j == p.len { + return i + } + i-- + } + return -1 +} + +// last_index returns the position of the last occurence of the input string. +pub fn (s string) last_index(p string) ?int { + idx := s.last_index_(p) + if idx == -1 { + return none + } + return idx +} + +// index_after returns the position of the input string, starting search from `start` position. +pub fn (s string) index_after(p string, start int) int { + if p.len > s.len { + return -1 + } + mut strt := start + if start < 0 { + strt = 0 + } + if start >= s.len { + return -1 + } + mut i := strt + for i < s.len { + mut j := 0 + mut ii := i + for j < p.len && unsafe { s.str[ii] == p.str[j] } { + j++ + ii++ + } + if j == p.len { + return i + } + i++ + } + return -1 +} + +// index_byte returns the index of byte `c` if found in the string. +// index_byte returns -1 if the byte can not be found. +pub fn (s string) index_byte(c byte) int { + for i in 0 .. s.len { + if unsafe { s.str[i] } == c { + return i + } + } + return -1 +} + +// last_index_byte returns the index of the last occurence of byte `c` if found in the string. +// last_index_byte returns -1 if the byte is not found. +pub fn (s string) last_index_byte(c byte) int { + for i := s.len - 1; i >= 0; i-- { + if unsafe { s.str[i] == c } { + return i + } + } + return -1 +} + +// count returns the number of occurrences of `substr` in the string. +// count returns -1 if no `substr` could be found. +pub fn (s string) count(substr string) int { + if s.len == 0 || substr.len == 0 { + return 0 + } + if substr.len > s.len { + return 0 + } + + mut n := 0 + + if substr.len == 1 { + target := substr[0] + + for letter in s { + if letter == target { + n++ + } + } + + return n + } + + mut i := 0 + for { + i = s.index_after(substr, i) + if i == -1 { + return n + } + i += substr.len + n++ + } + return 0 // TODO can never get here - v doesn't know that +} + +// contains returns `true` if the string contains `substr`. +pub fn (s string) contains(substr string) bool { + if substr.len == 0 { + return true + } + if s.index_(substr) == -1 { + return false + } + return true +} + +// contains_any returns `true` if the string contains any chars in `chars`. +pub fn (s string) contains_any(chars string) bool { + for c in chars { + if s.contains(c.ascii_str()) { + return true + } + } + return false +} + +// contains_any_substr returns `true` if the string contains any of the strings in `substrs`. +pub fn (s string) contains_any_substr(substrs []string) bool { + if substrs.len == 0 { + return true + } + for sub in substrs { + if s.contains(sub) { + return true + } + } + return false +} + +// starts_with returns `true` if the string starts with `p`. +pub fn (s string) starts_with(p string) bool { + if p.len > s.len { + return false + } + for i in 0 .. p.len { + if unsafe { s.str[i] != p.str[i] } { + return false + } + } + return true +} + +// ends_with returns `true` if the string ends with `p`. +pub fn (s string) ends_with(p string) bool { + if p.len > s.len { + return false + } + for i in 0 .. p.len { + if unsafe { p.str[i] != s.str[s.len - p.len + i] } { + return false + } + } + return true +} + +// to_lower returns the string in all lowercase characters. +// TODO only works with ASCII +pub fn (s string) to_lower() string { + unsafe { + mut b := malloc_noscan(s.len + 1) + for i in 0 .. s.len { + if s.str[i] >= `A` && s.str[i] <= `Z` { + b[i] = s.str[i] + 32 + } else { + b[i] = s.str[i] + } + } + b[s.len] = 0 + return tos(b, s.len) + } +} + +// is_lower returns `true` if all characters in the string is lowercase. +// Example: assert 'hello developer'.is_lower() == true +[direct_array_access] +pub fn (s string) is_lower() bool { + for i in 0 .. s.len { + if s[i] >= `A` && s[i] <= `Z` { + return false + } + } + return true +} + +// to_upper returns the string in all uppercase characters. +// Example: assert 'Hello V'.to_upper() == 'HELLO V' +pub fn (s string) to_upper() string { + unsafe { + mut b := malloc_noscan(s.len + 1) + for i in 0 .. s.len { + if s.str[i] >= `a` && s.str[i] <= `z` { + b[i] = s.str[i] - 32 + } else { + b[i] = s.str[i] + } + } + b[s.len] = 0 + return tos(b, s.len) + } +} + +// is_upper returns `true` if all characters in the string is uppercase. +// Example: assert 'HELLO V'.is_upper() == true +[direct_array_access] +pub fn (s string) is_upper() bool { + for i in 0 .. s.len { + if s[i] >= `a` && s[i] <= `z` { + return false + } + } + return true +} + +// capitalize returns the string with the first character capitalized. +// Example: assert 'hello'.capitalize() == 'Hello' +[direct_array_access] +pub fn (s string) capitalize() string { + if s.len == 0 { + return '' + } + s0 := s[0] + letter := s0.ascii_str() + uletter := letter.to_upper() + if s.len == 1 { + return uletter + } + srest := s[1..] + res := uletter + srest + return res +} + +// is_capital returns `true` if the first character in the string is a capital letter. +// Example: assert 'Hello'.is_capital() == true +[direct_array_access] +pub fn (s string) is_capital() bool { + if s.len == 0 || !(s[0] >= `A` && s[0] <= `Z`) { + return false + } + for i in 1 .. s.len { + if s[i] >= `A` && s[i] <= `Z` { + return false + } + } + return true +} + +// title returns the string with each word capitalized. +// Example: assert 'hello v developer'.title() == 'Hello V Developer' +pub fn (s string) title() string { + words := s.split(' ') + mut tit := []string{} + for word in words { + tit << word.capitalize() + } + title := tit.join(' ') + return title +} + +// is_title returns true if all words of the string is capitalized. +// Example: assert 'Hello V Developer'.is_title() == true +pub fn (s string) is_title() bool { + words := s.split(' ') + for word in words { + if !word.is_capital() { + return false + } + } + return true +} + +// find_between returns the string found between `start` string and `end` string. +// Example: assert 'hey [man] how you doin'.find_between('[', ']') == 'man' +pub fn (s string) find_between(start string, end string) string { + start_pos := s.index_(start) + if start_pos == -1 { + return '' + } + // First get everything to the right of 'start' + val := s[start_pos + start.len..] + end_pos := val.index_(end) + if end_pos == -1 { + return val + } + return val[..end_pos] +} + +// trim_space strips any of ` `, `\n`, `\t`, `\v`, `\f`, `\r` from the start and end of the string. +// Example: assert ' Hello V '.trim_space() == 'Hello V' +pub fn (s string) trim_space() string { + return s.trim(' \n\t\v\f\r') +} + +// trim strips any of the characters given in `cutset` from the start and end of the string. +// Example: assert ' ffHello V ffff'.trim(' f') == 'Hello V' +[direct_array_access] +pub fn (s string) trim(cutset string) string { + if s.len < 1 || cutset.len < 1 { + return s.clone() + } + mut pos_left := 0 + mut pos_right := s.len - 1 + mut cs_match := true + for pos_left <= s.len && pos_right >= -1 && cs_match { + cs_match = false + for cs in cutset { + if s[pos_left] == cs { + pos_left++ + cs_match = true + break + } + } + for cs in cutset { + if s[pos_right] == cs { + pos_right-- + cs_match = true + break + } + } + if pos_left > pos_right { + return '' + } + } + return s.substr(pos_left, pos_right + 1) +} + +// trim_left strips any of the characters given in `cutset` from the left of the string. +// Example: assert 'd Hello V developer'.trim_left(' d') == 'Hello V developer' +[direct_array_access] +pub fn (s string) trim_left(cutset string) string { + if s.len < 1 || cutset.len < 1 { + return s.clone() + } + mut pos := 0 + for pos < s.len { + mut found := false + for cs in cutset { + if s[pos] == cs { + found = true + break + } + } + if !found { + break + } + pos++ + } + return s[pos..] +} + +// trim_right strips any of the characters given in `cutset` from the right of the string. +// Example: assert ' Hello V d'.trim_right(' d') == ' Hello V' +[direct_array_access] +pub fn (s string) trim_right(cutset string) string { + if s.len < 1 || cutset.len < 1 { + return s.clone() + } + mut pos := s.len - 1 + for pos >= 0 { + mut found := false + for cs in cutset { + if s[pos] == cs { + found = true + } + } + if !found { + break + } + pos-- + } + if pos < 0 { + return '' + } + return s[..pos + 1] +} + +// trim_prefix strips `str` from the start of the string. +// Example: assert 'WorldHello V'.trim_prefix('World') == 'Hello V' +pub fn (s string) trim_prefix(str string) string { + if s.starts_with(str) { + return s[str.len..] + } + return s.clone() +} + +// trim_suffix strips `str` from the end of the string. +// Example: assert 'Hello VWorld'.trim_suffix('World') == 'Hello V' +pub fn (s string) trim_suffix(str string) string { + if s.ends_with(str) { + return s[..s.len - str.len] + } + return s.clone() +} + +// compare_strings returns `-1` if `a < b`, `1` if `a > b` else `0`. +pub fn compare_strings(a &string, b &string) int { + if a < b { + return -1 + } + if a > b { + return 1 + } + return 0 +} + +// compare_strings_reverse returns `1` if `a < b`, `-1` if `a > b` else `0`. +fn compare_strings_reverse(a &string, b &string) int { + if a < b { + return 1 + } + if a > b { + return -1 + } + return 0 +} + +// compare_strings_by_len returns `-1` if `a.len < b.len`, `1` if `a.len > b.len` else `0`. +fn compare_strings_by_len(a &string, b &string) int { + if a.len < b.len { + return -1 + } + if a.len > b.len { + return 1 + } + return 0 +} + +// compare_lower_strings returns the same as compare_strings but converts `a` and `b` to lower case before comparing. +fn compare_lower_strings(a &string, b &string) int { + aa := a.to_lower() + bb := b.to_lower() + return compare_strings(&aa, &bb) +} + +// sort sorts the string array. +pub fn (mut s []string) sort() { + s.sort_with_compare(compare_strings) +} + +// sort_ignore_case sorts the string array using case insesitive comparing. +pub fn (mut s []string) sort_ignore_case() { + s.sort_with_compare(compare_lower_strings) +} + +// sort_by_len sorts the the string array by each string's `.len` length. +pub fn (mut s []string) sort_by_len() { + s.sort_with_compare(compare_strings_by_len) +} + +// str returns a copy of the string +pub fn (s string) str() string { + return s.clone() +} + +// at returns the byte at index `idx`. +// Example: assert 'ABC'.at(1) == byte(`B`) +fn (s string) at(idx int) byte { + $if !no_bounds_checking ? { + if idx < 0 || idx >= s.len { + panic('string index out of range: $idx / $s.len') + } + } + unsafe { + return s.str[idx] + } +} + +// version of `at()` that is used in `a[i] or {` +// return an error when the index is out of range +fn (s string) at_with_check(idx int) ?byte { + if idx < 0 || idx >= s.len { + return error('string index out of range') + } + unsafe { + return s.str[idx] + } +} + +// is_space returns `true` if the byte is a white space character. +// The following list is considered white space characters: ` `, `\t`, `\n`, `\v`, `\f`, `\r`, 0x85, 0xa0 +// Example: assert byte(` `).is_space() == true +[inline] +pub fn (c byte) is_space() bool { + // 0x85 is NEXT LINE (NEL) + // 0xa0 is NO-BREAK SPACE + return c == 32 || (c > 8 && c < 14) || (c == 0x85) || (c == 0xa0) +} + +// is_digit returns `true` if the byte is in range 0-9 and `false` otherwise. +// Example: assert byte(`9`) == true +[inline] +pub fn (c byte) is_digit() bool { + return c >= `0` && c <= `9` +} + +// is_hex_digit returns `true` if the byte is either in range 0-9, a-f or A-F and `false` otherwise. +// Example: assert byte(`F`) == true +[inline] +pub fn (c byte) is_hex_digit() bool { + return c.is_digit() || (c >= `a` && c <= `f`) || (c >= `A` && c <= `F`) +} + +// is_oct_digit returns `true` if the byte is in range 0-7 and `false` otherwise. +// Example: assert byte(`7`) == true +[inline] +pub fn (c byte) is_oct_digit() bool { + return c >= `0` && c <= `7` +} + +// is_bin_digit returns `true` if the byte is a binary digit (0 or 1) and `false` otherwise. +// Example: assert byte(`0`) == true +[inline] +pub fn (c byte) is_bin_digit() bool { + return c == `0` || c == `1` +} + +// is_letter returns `true` if the byte is in range a-z or A-Z and `false` otherwise. +// Example: assert byte(`V`) == true +[inline] +pub fn (c byte) is_letter() bool { + return (c >= `a` && c <= `z`) || (c >= `A` && c <= `Z`) +} + +// free allows for manually freeing the memory occupied by the string +[manualfree; unsafe] +pub fn (s &string) free() { + $if prealloc { + return + } + if s.is_lit == -98761234 { + double_free_msg := unsafe { &byte(c'double string.free() detected\n') } + double_free_msg_len := unsafe { vstrlen(double_free_msg) } + $if freestanding { + bare_eprint(double_free_msg, u64(double_free_msg_len)) + } $else { + _write_buf_to_fd(1, double_free_msg, double_free_msg_len) + } + return + } + if s.is_lit == 1 || s.str == 0 { + return + } + unsafe { + free(s.str) + } + s.is_lit = -98761234 +} + +// before returns the contents before `sub` in the string. +// If the substring is not found, it returns the full input string. +// Example: assert '23:34:45.234'.before('.') == '23:34:45' +// Example: assert 'abcd'.before('.') == 'abcd' +// TODO: deprecate and remove either .before or .all_before +pub fn (s string) before(sub string) string { + pos := s.index_(sub) + if pos == -1 { + return s.clone() + } + return s[..pos] +} + +// all_before returns the contents before `sub` in the string. +// If the substring is not found, it returns the full input string. +// Example: assert '23:34:45.234'.all_before('.') == '23:34:45' +// Example: assert 'abcd'.all_before('.') == 'abcd' +pub fn (s string) all_before(sub string) string { + // TODO remove dup method + pos := s.index_(sub) + if pos == -1 { + return s.clone() + } + return s[..pos] +} + +// all_before_last returns the contents before the last occurence of `sub` in the string. +// If the substring is not found, it returns the full input string. +// Example: assert '23:34:45.234'.all_before_last(':') == '23:34' +// Example: assert 'abcd'.all_before_last('.') == 'abcd' +pub fn (s string) all_before_last(sub string) string { + pos := s.last_index_(sub) + if pos == -1 { + return s.clone() + } + return s[..pos] +} + +// all_after returns the contents after `sub` in the string. +// If the substring is not found, it returns the full input string. +// Example: assert '23:34:45.234'.all_after('.') == '234' +// Example: assert 'abcd'.all_after('z') == 'abcd' +pub fn (s string) all_after(sub string) string { + pos := s.index_(sub) + if pos == -1 { + return s.clone() + } + return s[pos + sub.len..] +} + +// all_after_last returns the contents after the last occurence of `sub` in the string. +// If the substring is not found, it returns the full input string. +// Example: assert '23:34:45.234'.all_after_last(':') == '45.234' +// Example: assert 'abcd'.all_after_last('z') == 'abcd' +pub fn (s string) all_after_last(sub string) string { + pos := s.last_index_(sub) + if pos == -1 { + return s.clone() + } + return s[pos + sub.len..] +} + +// after returns the contents after the last occurence of `sub` in the string. +// If the substring is not found, it returns the full input string. +// Example: assert '23:34:45.234'.after(':') == '45.234' +// Example: assert 'abcd'.after('z') == 'abcd' +// TODO: deprecate either .all_after_last or .after +pub fn (s string) after(sub string) string { + return s.all_after_last(sub) +} + +// after_char returns the contents after the first occurence of `sub` character in the string. +// If the substring is not found, it returns the full input string. +// Example: assert '23:34:45.234'.after_char(`:`) == '34:45.234' +// Example: assert 'abcd'.after_char(`:`) == 'abcd' +pub fn (s string) after_char(sub byte) string { + mut pos := -1 + for i, c in s { + if c == sub { + pos = i + break + } + } + if pos == -1 { + return s.clone() + } + return s[pos + 1..] +} + +// join joins a string array into a string using `sep` separator. +// Example: assert ['Hello','V'].join(' ') == 'Hello V' +pub fn (a []string) join(sep string) string { + if a.len == 0 { + return '' + } + mut len := 0 + for val in a { + len += val.len + sep.len + } + len -= sep.len + // Allocate enough memory + mut res := string{ + str: unsafe { malloc_noscan(len + 1) } + len: len + } + mut idx := 0 + for i, val in a { + unsafe { + C.memcpy(res.str + idx, val.str, val.len) + idx += val.len + } + // Add sep if it's not last + if i != a.len - 1 { + unsafe { + C.memcpy(res.str + idx, sep.str, sep.len) + idx += sep.len + } + } + } + unsafe { + res.str[res.len] = 0 + } + return res +} + +// join joins a string array into a string using a `\n` newline delimiter. +pub fn (s []string) join_lines() string { + return s.join('\n') +} + +// reverse returns a reversed string. +// Example: assert 'Hello V'.reverse() == 'V olleH' +pub fn (s string) reverse() string { + if s.len == 0 || s.len == 1 { + return s.clone() + } + mut res := string{ + str: unsafe { malloc_noscan(s.len + 1) } + len: s.len + } + for i := s.len - 1; i >= 0; i-- { + unsafe { + res.str[s.len - i - 1] = s[i] + } + } + unsafe { + res.str[res.len] = 0 + } + return res +} + +// limit returns a portion of the string, starting at `0` and extending for a given number of characters afterward. +// 'hello'.limit(2) => 'he' +// 'hi'.limit(10) => 'hi' +pub fn (s string) limit(max int) string { + u := s.runes() + if u.len <= max { + return s.clone() + } + return u[0..max].string() +} + +// hash returns an integer hash of the string. +pub fn (s string) hash() int { + mut h := u32(0) + if h == 0 && s.len > 0 { + for c in s { + h = h * 31 + u32(c) + } + } + return int(h) +} + +// bytes returns the string converted to a byte array. +pub fn (s string) bytes() []byte { + if s.len == 0 { + return [] + } + mut buf := []byte{len: s.len} + unsafe { C.memcpy(buf.data, s.str, s.len) } + return buf +} + +// repeat returns a new string with `count` number of copies of the string it was called on. +pub fn (s string) repeat(count int) string { + if count < 0 { + panic('string.repeat: count is negative: $count') + } else if count == 0 { + return '' + } else if count == 1 { + return s.clone() + } + mut ret := unsafe { malloc_noscan(s.len * count + 1) } + for i in 0 .. count { + for j in 0 .. s.len { + unsafe { + ret[i * s.len + j] = s[j] + } + } + } + new_len := s.len * count + unsafe { + ret[new_len] = 0 + } + return unsafe { ret.vstring_with_len(new_len) } +} + +// fields returns a string array of the string split by `\t` and ` ` +// Example: assert '\t\tv = v'.fields() == ['v', '=', 'v'] +// Example: assert ' sss ssss'.fields() == ['sss', 'ssss'] +pub fn (s string) fields() []string { + mut res := []string{} + mut word_start := 0 + mut word_len := 0 + mut is_in_word := false + mut is_space := false + for i, c in s { + is_space = c in [32, 9, 10] + if !is_space { + word_len++ + } + if !is_in_word && !is_space { + word_start = i + is_in_word = true + continue + } + if is_space && is_in_word { + res << s[word_start..word_start + word_len] + is_in_word = false + word_len = 0 + word_start = 0 + continue + } + } + if is_in_word && word_len > 0 { + // collect the remainder word at the end + res << s[word_start..s.len] + } + return res +} + +// strip_margin allows multi-line strings to be formatted in a way that removes white-space +// before a delimeter. by default `|` is used. +// Note: the delimiter has to be a byte at this time. That means surrounding +// the value in ``. +// +// Example: +// st := 'Hello there, +// |this is a string, +// | Everything before the first | is removed'.strip_margin() +// Returns: +// Hello there, +// this is a string, +// Everything before the first | is removed +pub fn (s string) strip_margin() string { + return s.strip_margin_custom(`|`) +} + +// strip_margin_custom does the same as `strip_margin` but will use `del` as delimiter instead of `|` +[direct_array_access] +pub fn (s string) strip_margin_custom(del byte) string { + mut sep := del + if sep.is_space() { + eprintln('Warning: `strip_margin` cannot use white-space as a delimiter') + eprintln(' Defaulting to `|`') + sep = `|` + } + // don't know how much space the resulting string will be, but the max it + // can be is this big + mut ret := unsafe { malloc_noscan(s.len + 1) } + mut count := 0 + for i := 0; i < s.len; i++ { + if s[i] in [10, 13] { + unsafe { + ret[count] = s[i] + } + count++ + // CRLF + if s[i] == 13 && i < s.len - 1 && s[i + 1] == 10 { + unsafe { + ret[count] = s[i + 1] + } + count++ + i++ + } + for s[i] != sep { + i++ + if i >= s.len { + break + } + } + } else { + unsafe { + ret[count] = s[i] + } + count++ + } + } + unsafe { + ret[count] = 0 + return ret.vstring_with_len(count) + } +} diff --git a/v_windows/v/old/vlib/builtin/string_charptr_byteptr_helpers.v b/v_windows/v/old/vlib/builtin/string_charptr_byteptr_helpers.v new file mode 100644 index 0000000..e738ee2 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/string_charptr_byteptr_helpers.v @@ -0,0 +1,104 @@ +module builtin + +// NB: this file will be removed soon + +// byteptr.vbytes() - makes a V []byte structure from a C style memory buffer. NB: the data is reused, NOT copied! +[unsafe] +pub fn (data byteptr) vbytes(len int) []byte { + return unsafe { voidptr(data).vbytes(len) } +} + +// vstring converts a C style string to a V string. NB: the string data is reused, NOT copied. +// strings returned from this function will be normal V strings beside that (i.e. they would be +// freed by V's -autofree mechanism, when they are no longer used). +[unsafe] +pub fn (bp byteptr) vstring() string { + return string{ + str: bp + len: unsafe { C.strlen(&char(bp)) } + } +} + +// vstring_with_len converts a C style string to a V string. +// NB: the string data is reused, NOT copied. +[unsafe] +pub fn (bp byteptr) vstring_with_len(len int) string { + return string{ + str: bp + len: len + is_lit: 0 + } +} + +// vstring converts C char* to V string. +// NB: the string data is reused, NOT copied. +[unsafe] +pub fn (cp charptr) vstring() string { + return string{ + str: byteptr(cp) + len: unsafe { C.strlen(&char(cp)) } + is_lit: 0 + } +} + +// vstring_with_len converts C char* to V string. +// NB: the string data is reused, NOT copied. +[unsafe] +pub fn (cp charptr) vstring_with_len(len int) string { + return string{ + str: byteptr(cp) + len: len + is_lit: 0 + } +} + +// vstring_literal converts a C style string to a V string. +// NB: the string data is reused, NOT copied. +// NB2: unlike vstring, vstring_literal will mark the string +// as a literal, so it will not be freed by autofree. +// This is suitable for readonly strings, C string literals etc, +// that can be read by the V program, but that should not be +// managed by it, for example `os.args` is implemented using it. +[unsafe] +pub fn (bp byteptr) vstring_literal() string { + return string{ + str: bp + len: unsafe { C.strlen(&char(bp)) } + is_lit: 1 + } +} + +// vstring_with_len converts a C style string to a V string. +// NB: the string data is reused, NOT copied. +[unsafe] +pub fn (bp byteptr) vstring_literal_with_len(len int) string { + return string{ + str: bp + len: len + is_lit: 1 + } +} + +// vstring_literal converts C char* to V string. +// See also vstring_literal defined on byteptr for more details. +// NB: the string data is reused, NOT copied. +[unsafe] +pub fn (cp charptr) vstring_literal() string { + return string{ + str: byteptr(cp) + len: unsafe { C.strlen(&char(cp)) } + is_lit: 1 + } +} + +// vstring_literal_with_len converts C char* to V string. +// See also vstring_literal_with_len defined on byteptr. +// NB: the string data is reused, NOT copied. +[unsafe] +pub fn (cp charptr) vstring_literal_with_len(len int) string { + return string{ + str: byteptr(cp) + len: len + is_lit: 1 + } +} diff --git a/v_windows/v/old/vlib/builtin/string_int_test.v b/v_windows/v/old/vlib/builtin/string_int_test.v new file mode 100644 index 0000000..a17563d --- /dev/null +++ b/v_windows/v/old/vlib/builtin/string_int_test.v @@ -0,0 +1,221 @@ +import strconv + +fn test_common_atoi() { + // test common cases + assert '70zzz'.int() == 70 + assert '2901issue'.int() == 2901 + assert '234232w'.int() == 234232 + assert '-9009x'.int() == -9009 + assert '0y'.int() == 0 + + // test lead zeros + assert '0000012'.int() == 12 + assert '-0000012'.int() == -12 + assert '0x001F'.int() == 31 + assert '-0x001F'.int() == -31 + assert '0x001f'.int() == 31 + assert '0o00011'.int() == 9 + assert '0b00001001'.int() == 9 + + // test underscore in string + assert '-10_000'.int() == -10000 + assert '-0x00_0_f_ff'.int() == -0xfff + assert '10_000_000'.int() == 10000000 + + for n in -10000 .. 100000 { + s := n.str() + 'z' + assert s.int() == n + } +} + +fn test_unsigned_cast() { + // tests for u16 + + // test common cases + assert '70zzz'.u16() == 70 + assert '2901issue'.u16() == 2901 + assert '0y'.u16() == 0 + + // test lead zeros + assert '0000012'.u16() == 12 + assert '0x001F'.u16() == 31 + assert '0x001f'.u16() == 31 + assert '0o00011'.u16() == 9 + assert '0b00001001'.u16() == 9 + + // tests for u32 + + // test common cases + assert '70zzz'.u32() == 70 + assert '2901issue'.u32() == 2901 + assert '234232w'.u32() == 234232 + assert '-9009x'.u32() == 0 + assert '0y'.u32() == 0 + + // test lead zeros + assert '0000012'.u32() == 12 + assert '-0000012'.u32() == 0 + assert '0x001F'.u32() == 31 + assert '-0x001F'.u32() == 0 + assert '0x001f'.u32() == 31 + assert '0o00011'.u32() == 9 + assert '0b00001001'.u32() == 9 + + // test underscore in string + assert '-10_000'.u32() == 0 + assert '-0x00_0_f_ff'.u32() == 0 + assert '10_000_000'.u32() == 10000000 + + for n in 0 .. u32(100) { + s := n.str() + 'z' + assert s.u32() == n + } + + // tests for u64 + + // test common cases + assert '70zzz'.u64() == 70 + assert '2901issue'.u64() == 2901 + assert '234232w'.u64() == 234232 + assert '-9009x'.u64() == 0 + assert '0y'.u64() == 0 + + // test lead zeros + assert '0000012'.u64() == 12 + assert '-0000012'.u64() == 0 + assert '0x001F'.u64() == 31 + assert '-0x001F'.u64() == 0 + assert '0x001f'.u64() == 31 + assert '0o00011'.u64() == 9 + assert '0b00001001'.u64() == 9 + + // test underscore in string + assert '-10_000'.u64() == 0 + assert '-0x00_0_f_ff'.u64() == 0 + assert '10_000_000'.u64() == 10000000 + + for n in 0 .. u64(10000) { + s := n.str() + 'z' + assert s.u64() == n + } +} + +fn test_signed_cast() { + // tests for i64 + + // test common cases + assert '70zzz'.i64() == 70 + assert '2901issue'.i64() == 2901 + assert '234232w'.i64() == 234232 + assert '-9009x'.i64() == -9009 + assert '0y'.i64() == 0 + + // test lead zeros + assert '0000012'.i64() == 12 + assert '-0000012'.i64() == -12 + assert '0x001F'.i64() == 31 + assert '-0x001F'.i64() == -31 + assert '0x001f'.i64() == 31 + assert '0o00011'.i64() == 9 + assert '0b00001001'.i64() == 9 + + // test underscore in string + assert '-10_000'.i64() == -10000 + assert '-0x00_0_f_ff'.i64() == -0xfff + assert '10_000_000'.i64() == 10000000 + + for n in -10000 .. 100000 { + s := n.str() + 'z' + assert s.i64() == n + } + + // tests for i8 + + // test common cases + assert '70zzz'.i8() == 70 + assert '29issue'.i8() == 29 + assert '22w'.i8() == 22 + assert '-90x'.i8() == -90 + assert '0y'.i8() == 0 + + // test lead zeros + assert '0000012'.i8() == 12 + assert '-0000012'.i8() == -12 + assert '0x001F'.i8() == 31 + assert '-0x001F'.i8() == -31 + assert '0x001f'.i8() == 31 + assert '0o00011'.i8() == 9 + assert '0b000011'.i8() == 3 + + // test underscore in string + assert '-10_0'.i8() == -100 + assert '-0x0_0_f'.i8() == -0xf + assert '10_0'.i8() == 100 + + for n in -10 .. 100 { + s := n.str() + 'z' + assert s.i8() == n + } + + // tests for i16 + + // test common cases + assert '70zzz'.i16() == 70 + assert '2901issue'.i16() == 2901 + assert '2342w'.i16() == 2342 + assert '-9009x'.i16() == -9009 + assert '0y'.i16() == 0 + + // test lead zeros + assert '0000012'.i16() == 12 + assert '-0000012'.i16() == -12 + assert '0x001F'.i16() == 31 + assert '-0x001F'.i16() == -31 + assert '0x001f'.i16() == 31 + assert '0o00011'.i16() == 9 + assert '0b00001001'.i16() == 9 + + // test underscore in string + assert '-10_0'.i16() == -100 + assert '-0x00_0_fff'.i16() == -0xfff + assert '10_0'.i16() == 100 + + for n in -100 .. 100 { + s := n.str() + 'z' + assert s.i16() == n + } + + // test g format + unsafe { + mut u := strconv.Float64u{ + u: strconv.double_plus_zero + } + assert '${u.f:g}' == '0' + assert '${u.f:G}' == '0' + u.u = strconv.double_minus_zero + assert '${u.f:g}' == '0' + assert '${u.f:G}' == '0' + u.u = strconv.double_plus_infinity + assert '${u.f:g}' == '+inf' + assert '${u.f:G}' == '+INF' + u.u = strconv.double_minus_infinity + assert '${u.f:g}' == '-inf' + assert '${u.f:G}' == '-INF' + } + unsafe { + mut u := strconv.Float32u{ + u: strconv.single_plus_zero + } + assert '${u.f:g}' == '0' + assert '${u.f:G}' == '0' + u.u = strconv.single_minus_zero + assert '${u.f:g}' == '0' + assert '${u.f:G}' == '0' + u.u = strconv.single_plus_infinity + assert '${u.f:g}' == '+inf' + assert '${u.f:G}' == '+INF' + u.u = strconv.single_minus_infinity + assert '${u.f:g}' == '-inf' + assert '${u.f:G}' == '-INF' + } +} diff --git a/v_windows/v/old/vlib/builtin/string_interpolation.v b/v_windows/v/old/vlib/builtin/string_interpolation.v new file mode 100644 index 0000000..10064ac --- /dev/null +++ b/v_windows/v/old/vlib/builtin/string_interpolation.v @@ -0,0 +1,713 @@ +module builtin + +import strconv +import strings + +/*============================================================================= +Copyright (c) 2019-2021 Dario Deledda. All rights reserved. +Use of this source code is governed by an MIT license +that can be found in the LICENSE file. + +This file contains string interpolation V functions +=============================================================================*/ + +//============================================================================= +// Enum format types max 0x1F => 32 types +//============================================================================= +pub enum StrIntpType { + si_no_str = 0 // no parameter to print only fix string + si_c + si_u8 + si_i8 + si_u16 + si_i16 + si_u32 + si_i32 + si_u64 + si_i64 + si_e32 + si_e64 + si_f32 + si_f64 + si_g32 + si_g64 + si_s + si_p + si_vp +} + +pub fn (x StrIntpType) str() string { + match x { + .si_no_str { return 'no_str' } + .si_c { return 'c' } + .si_u8 { return 'u8' } + .si_i8 { return 'i8' } + .si_u16 { return 'u16' } + .si_i16 { return 'i16' } + .si_u32 { return 'u32' } + .si_i32 { return 'i32' } + .si_u64 { return 'u64' } + .si_i64 { return 'i64' } + .si_f32 { return 'f32' } + .si_f64 { return 'f64' } + .si_g32 { return 'f32' } // g32 format use f32 data + .si_g64 { return 'f64' } // g64 format use f64 data + .si_e32 { return 'f32' } // e32 format use f32 data + .si_e64 { return 'f64' } // e64 format use f64 data + .si_s { return 's' } + .si_p { return 'p' } + .si_vp { return 'vp' } + } +} + +//============================================================================= +// Union data +//============================================================================= +pub union StrIntpMem { +pub mut: + d_c u32 + d_u8 byte + d_i8 i8 + d_u16 u16 + d_i16 i16 + d_u32 u32 + d_i32 int + d_u64 u64 + d_i64 i64 + d_f32 f32 + d_f64 f64 + d_s string + d_p voidptr + d_vp voidptr +} + +[inline] +fn fabs32(x f32) f32 { + return if x < 0 { -x } else { x } +} + +[inline] +fn fabs64(x f64) f64 { + return if x < 0 { -x } else { x } +} + +[inline] +fn abs64(x i64) u64 { + return if x < 0 { u64(-x) } else { u64(x) } +} + +//========================================= +// +// u32/u64 bit compact format +// +//___ 32 24 16 8 +//___ | | | | +//_3333333333222222222211111111110000000000 +//_9876543210987654321098765432109876543210 +//_nPPPPPPPPBBBBWWWWWWWWWWTDDDDDDDSUAA===== +// = data type 5 bit max 32 data type +// A allign 2 bit Note: for now only 1 used! +// U uppercase 1 bit 0 do nothing, 1 do to_upper() +// S sign 1 bit show the sign if positive +// D decimals 7 bit number of decimals digit to show +// T tail zeros 1 bit 1 remove tail zeros, 0 do nothing +// W Width 10 bit number of char for padding and indentation +// B num base 4 bit start from 2, 0 for base 10 +// P pad char 1/8 bit padding char (in u32 format reduced to 1 bit as flag for `0` padding) +// -------------- +// TOTAL: 39/32 bit +//========================================= + +// convert from data format to compact u64 +pub fn get_str_intp_u64_format(fmt_type StrIntpType, in_width int, in_precision int, in_tail_zeros bool, in_sign bool, in_pad_ch byte, in_base int, in_upper_case bool) u64 { + width := if in_width != 0 { abs64(in_width) } else { u64(0) } + allign := if in_width > 0 { u64(1 << 5) } else { u64(0) } // two bit 0 .left 1 .rigth, for now we use only one + upper_case := if in_upper_case { u64(1 << 7) } else { u64(0) } + sign := if in_sign { u64(1 << 8) } else { u64(0) } + precision := if in_precision != 987698 { + (u64(in_precision & 0x7F) << 9) + } else { + u64(0x7F) << 9 + } + tail_zeros := if in_tail_zeros { u32(1) << 16 } else { u32(0) } + base := u64((in_base & 0xf) << 27) + res := u64((u64(fmt_type) & 0x1F) | allign | upper_case | sign | precision | tail_zeros | (u64(width & 0x3FF) << 17) | base | (u64(in_pad_ch) << 31)) + return res +} + +// convert from data format to compact u32 +pub fn get_str_intp_u32_format(fmt_type StrIntpType, in_width int, in_precision int, in_tail_zeros bool, in_sign bool, in_pad_ch byte, in_base int, in_upper_case bool) u32 { + width := if in_width != 0 { abs64(in_width) } else { u32(0) } + allign := if in_width > 0 { u32(1 << 5) } else { u32(0) } // two bit 0 .left 1 .rigth, for now we use only one + upper_case := if in_upper_case { u32(1 << 7) } else { u32(0) } + sign := if in_sign { u32(1 << 8) } else { u32(0) } + precision := if in_precision != 987698 { + (u32(in_precision & 0x7F) << 9) + } else { + u32(0x7F) << 9 + } + tail_zeros := if in_tail_zeros { u32(1) << 16 } else { u32(0) } + base := u32((in_base & 0xf) << 27) + res := u32((u32(fmt_type) & 0x1F) | allign | upper_case | sign | precision | tail_zeros | (u32(width & 0x3FF) << 17) | base | (u32(in_pad_ch & 1) << 31)) + return res +} + +// convert from struct to formated string +[manualfree] +fn (data StrIntpData) get_fmt_format(mut sb strings.Builder) { + x := data.fmt + typ := StrIntpType(x & 0x1F) + allign := int((x >> 5) & 0x01) + upper_case := if ((x >> 7) & 0x01) > 0 { true } else { false } + sign := int((x >> 8) & 0x01) + precision := int((x >> 9) & 0x7F) + tail_zeros := if ((x >> 16) & 0x01) > 0 { true } else { false } + width := int(i16((x >> 17) & 0x3FF)) + mut base := int(x >> 27) & 0xF + fmt_pad_ch := byte((x >> 31) & 0xFF) + + // no string interpolation is needed, return empty string + if typ == .si_no_str { + return + } + + // if width > 0 { println("${x.hex()} Type: ${x & 0x7F} Width: ${width} Precision: ${precision} allign:${allign}") } + + // manage base if any + if base > 0 { + base += 2 // we start from 2, 0 == base 10 + } + + // mange pad char, for now only 0 allowed + mut pad_ch := byte(` `) + if fmt_pad_ch > 0 { + // pad_ch = fmt_pad_ch + pad_ch = `0` + } + + len0_set := if width > 0 { width } else { -1 } + len1_set := if precision == 0x7F { -1 } else { precision } + sign_set := if sign == 1 { true } else { false } + + mut bf := strconv.BF_param{ + pad_ch: pad_ch // padding char + len0: len0_set // default len for whole the number or string + len1: len1_set // number of decimal digits, if needed + positive: true // mandatory: the sign of the number passed + sign_flag: sign_set // flag for print sign as prefix in padding + allign: .left // alignment of the string + rm_tail_zero: tail_zeros // false // remove the tail zeros from floats + } + + // allign + if fmt_pad_ch == 0 { + match allign { + 0 { bf.allign = .left } + 1 { bf.allign = .right } + // 2 { bf.allign = .center } + else { bf.allign = .left } + } + } else { + bf.allign = .right + } + + unsafe { + // strings + if typ == .si_s { + mut s := '' + if upper_case { + s = data.d.d_s.to_upper() + } else { + s = data.d.d_s.clone() + } + if width == 0 { + sb.write_string(s) + } else { + strconv.format_str_sb(s, bf, mut sb) + } + s.free() + return + } + + // signed int + if typ in [.si_i8, .si_i16, .si_i32, .si_i64] { + mut d := data.d.d_i64 + if typ == .si_i8 { + d = i64(data.d.d_i8) + } else if typ == .si_i16 { + d = i64(data.d.d_i16) + } else if typ == .si_i32 { + d = i64(data.d.d_i32) + } + + if base == 0 { + if width == 0 { + d_str := d.str() + sb.write_string(d_str) + d_str.free() + return + } + if d < 0 { + bf.positive = false + } + strconv.format_dec_sb(abs64(d), bf, mut sb) + } else { + mut hx := strconv.format_int(d, base) + if upper_case { + tmp := hx + hx = hx.to_upper() + tmp.free() + } + if width == 0 { + sb.write_string(hx) + } else { + strconv.format_str_sb(hx, bf, mut sb) + } + hx.free() + } + return + } + + // unsigned int and pointers + if typ in [.si_u8, .si_u16, .si_u32, .si_u64] { + mut d := data.d.d_u64 + if typ == .si_u8 { + d = u64(data.d.d_u8) + } else if typ == .si_u16 { + d = u64(data.d.d_u16) + } else if typ == .si_u32 { + d = u64(data.d.d_u32) + } + if base == 0 { + if width == 0 { + d_str := d.str() + sb.write_string(d_str) + d_str.free() + return + } + strconv.format_dec_sb(d, bf, mut sb) + } else { + mut hx := strconv.format_uint(d, base) + if upper_case { + tmp := hx + hx = hx.to_upper() + tmp.free() + } + if width == 0 { + sb.write_string(hx) + } else { + strconv.format_str_sb(hx, bf, mut sb) + } + hx.free() + } + return + } + + // pointers + if typ == .si_p { + mut d := data.d.d_u64 + base = 16 // TODO: **** decide the behaviour of this flag! **** + if base == 0 { + if width == 0 { + d_str := d.str() + sb.write_string(d_str) + d_str.free() + return + } + strconv.format_dec_sb(d, bf, mut sb) + } else { + mut hx := strconv.format_uint(d, base) + if upper_case { + tmp := hx + hx = hx.to_upper() + tmp.free() + } + if width == 0 { + sb.write_string(hx) + } else { + strconv.format_str_sb(hx, bf, mut sb) + } + hx.free() + } + return + } + + // default settings for floats + mut use_default_str := false + if width == 0 && precision == 0x7F { + bf.len1 = 3 + use_default_str = true + } + if bf.len1 < 0 { + bf.len1 = 3 + } + + match typ { + // floating point + .si_f32 { + // println("HERE: f32") + if use_default_str { + mut f := data.d.d_f32.str() + if upper_case { + tmp := f + f = f.to_upper() + tmp.free() + } + sb.write_string(f) + f.free() + } else { + // println("HERE: f32 format") + // println(data.d.d_f32) + if data.d.d_f32 < 0 { + bf.positive = false + } + mut f := strconv.format_fl(data.d.d_f32, bf) + if upper_case { + tmp := f + f = f.to_upper() + tmp.free() + } + sb.write_string(f) + f.free() + } + } + .si_f64 { + // println("HERE: f64") + if use_default_str { + mut f := data.d.d_f64.str() + if upper_case { + tmp := f + f = f.to_upper() + tmp.free() + } + sb.write_string(f) + f.free() + } else { + if data.d.d_f64 < 0 { + bf.positive = false + } + f_union := strconv.Float64u{ + f: data.d.d_f64 + } + if f_union.u == strconv.double_minus_zero { + bf.positive = false + } + + mut f := strconv.format_fl(data.d.d_f64, bf) + if upper_case { + tmp := f + f = f.to_upper() + tmp.free() + } + sb.write_string(f) + f.free() + } + } + .si_g32 { + // println("HERE: g32") + if use_default_str { + mut f := data.d.d_f32.strg() + if upper_case { + tmp := f + f = f.to_upper() + tmp.free() + } + sb.write_string(f) + f.free() + } else { + // Manage +/-0 + if data.d.d_f32 == strconv.single_plus_zero { + tmp_str := '0' + strconv.format_str_sb(tmp_str, bf, mut sb) + tmp_str.free() + return + } + if data.d.d_f32 == strconv.single_minus_zero { + tmp_str := '-0' + strconv.format_str_sb(tmp_str, bf, mut sb) + tmp_str.free() + return + } + // Manage +/-INF + if data.d.d_f32 == strconv.single_plus_infinity { + mut tmp_str := '+inf' + if upper_case { + tmp_str = '+INF' + } + strconv.format_str_sb(tmp_str, bf, mut sb) + tmp_str.free() + } + if data.d.d_f32 == strconv.single_minus_infinity { + mut tmp_str := '-inf' + if upper_case { + tmp_str = '-INF' + } + strconv.format_str_sb(tmp_str, bf, mut sb) + tmp_str.free() + } + + if data.d.d_f32 < 0 { + bf.positive = false + } + d := fabs32(data.d.d_f32) + if d < 999_999.0 && d >= 0.00001 { + mut f := strconv.format_fl(data.d.d_f32, bf) + if upper_case { + tmp := f + f = f.to_upper() + tmp.free() + } + sb.write_string(f) + f.free() + return + } + mut f := strconv.format_es(data.d.d_f32, bf) + if upper_case { + tmp := f + f = f.to_upper() + tmp.free() + } + sb.write_string(f) + f.free() + } + } + .si_g64 { + // println("HERE: g64") + if use_default_str { + mut f := data.d.d_f64.strg() + if upper_case { + tmp := f + f = f.to_upper() + tmp.free() + } + sb.write_string(f) + f.free() + } else { + // Manage +/-0 + if data.d.d_f64 == strconv.double_plus_zero { + tmp_str := '0' + strconv.format_str_sb(tmp_str, bf, mut sb) + tmp_str.free() + return + } + if data.d.d_f64 == strconv.double_minus_zero { + tmp_str := '-0' + strconv.format_str_sb(tmp_str, bf, mut sb) + tmp_str.free() + return + } + // Manage +/-INF + if data.d.d_f64 == strconv.double_plus_infinity { + mut tmp_str := '+inf' + if upper_case { + tmp_str = '+INF' + } + strconv.format_str_sb(tmp_str, bf, mut sb) + tmp_str.free() + } + if data.d.d_f64 == strconv.double_minus_infinity { + mut tmp_str := '-inf' + if upper_case { + tmp_str = '-INF' + } + strconv.format_str_sb(tmp_str, bf, mut sb) + tmp_str.free() + } + + if data.d.d_f64 < 0 { + bf.positive = false + } + d := fabs64(data.d.d_f64) + if d < 999_999.0 && d >= 0.00001 { + mut f := strconv.format_fl(data.d.d_f64, bf) + if upper_case { + tmp := f + f = f.to_upper() + tmp.free() + } + sb.write_string(f) + f.free() + return + } + mut f := strconv.format_es(data.d.d_f64, bf) + if upper_case { + tmp := f + f = f.to_upper() + tmp.free() + } + sb.write_string(f) + f.free() + } + } + .si_e32 { + // println("HERE: e32") + bf.len1 = 6 + if use_default_str { + mut f := data.d.d_f32.str() + if upper_case { + tmp := f + f = f.to_upper() + tmp.free() + } + sb.write_string(f) + f.free() + } else { + if data.d.d_f32 < 0 { + bf.positive = false + } + mut f := strconv.format_es(data.d.d_f32, bf) + if upper_case { + tmp := f + f = f.to_upper() + tmp.free() + } + sb.write_string(f) + f.free() + } + } + .si_e64 { + // println("HERE: e64") + bf.len1 = 6 + if use_default_str { + mut f := data.d.d_f64.str() + if upper_case { + tmp := f + f = f.to_upper() + tmp.free() + } + sb.write_string(f) + f.free() + } else { + if data.d.d_f64 < 0 { + bf.positive = false + } + mut f := strconv.format_es(data.d.d_f64, bf) + if upper_case { + tmp := f + f = f.to_upper() + tmp.free() + } + sb.write_string(f) + f.free() + } + } + // runes + .si_c { + ss := utf32_to_str(data.d.d_c) + sb.write_string(ss) + ss.free() + } + // v pointers + .si_vp { + ss := u64(data.d.d_vp).hex() + sb.write_string(ss) + ss.free() + } + else { + sb.write_string('***ERROR!***') + } + } + } +} + +//==================================================================================== + +// storing struct used by cgen +pub struct StrIntpCgenData { +pub: + str string + fmt string + d string +} + +// NOTE: LOW LEVEL struct +// storing struct passed to V in the C code +pub struct StrIntpData { +pub: + str string + // fmt u64 // expanded version for future use, 64 bit + fmt u32 + d StrIntpMem +} + +// interpolation function +[manualfree] +pub fn str_intp(data_len int, in_data voidptr) string { + mut res := strings.new_builder(256) + unsafe { + mut i := 0 + for i < data_len { + data := &StrIntpData(&byte(in_data) + (int(sizeof(StrIntpData)) * i)) + // avoid empty strings + if data.str.len != 0 { + res.write_string(data.str) + } + // skip empty data + if data.fmt != 0 { + data.get_fmt_format(mut &res) + } + i++ + } + } + ret := res.str() + unsafe { res.free() } + return ret +} + +//==================================================================================== +// Utility for the compiler "auto_str_methods.v" +//==================================================================================== + +// substitute old _STR calls + +pub const ( + // BUG: this const is not released from the memory! use a const for now + // si_s_code = "0x" + int(StrIntpType.si_s).hex() // code for a simple string + si_s_code = '0xfe10' + si_g32_code = '0xfe0e' + si_g64_code = '0xfe0f' +) + +[inline] +pub fn str_intp_sq(in_str string) string { + return 'str_intp(2, _MOV((StrIntpData[]){{_SLIT("\'"), $si_s_code, {.d_s = $in_str}},{_SLIT("\'"), 0, {.d_c = 0 }}}))' +} + +[inline] +pub fn str_intp_rune(in_str string) string { + return 'str_intp(2, _MOV((StrIntpData[]){{_SLIT("\`"), $si_s_code, {.d_s = $in_str}},{_SLIT("\`"), 0, {.d_c = 0 }}}))' +} + +[inline] +pub fn str_intp_g32(in_str string) string { + return 'str_intp(1, _MOV((StrIntpData[]){{_SLIT0, $si_g32_code, {.d_f32 = $in_str }}}))' +} + +[inline] +pub fn str_intp_g64(in_str string) string { + return 'str_intp(1, _MOV((StrIntpData[]){{_SLIT0, $si_g64_code, {.d_f64 = $in_str }}}))' +} + +// replace %% with the in_str +[manualfree] +pub fn str_intp_sub(base_str string, in_str string) string { + index := base_str.index('%%') or { + eprintln('No strin interpolation %% parameteres') + exit(1) + } + // return base_str[..index] + in_str + base_str[index+2..] + + unsafe { + st_str := base_str[..index] + if index + 2 < base_str.len { + en_str := base_str[index + 2..] + res_str := 'str_intp(2, _MOV((StrIntpData[]){{_SLIT("$st_str"), $si_s_code, {.d_s = $in_str }},{_SLIT("$en_str"), 0, {.d_c = 0}}}))' + st_str.free() + en_str.free() + return res_str + } + res2_str := 'str_intp(1, _MOV((StrIntpData[]){{_SLIT("$st_str"), $si_s_code, {.d_s = $in_str }}}))' + st_str.free() + return res2_str + } +} diff --git a/v_windows/v/old/vlib/builtin/string_strip_margin_test.v b/v_windows/v/old/vlib/builtin/string_strip_margin_test.v new file mode 100644 index 0000000..372b8af --- /dev/null +++ b/v_windows/v/old/vlib/builtin/string_strip_margin_test.v @@ -0,0 +1,95 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. + +fn test_strip_margins_no_tabs() { + no_tabs := ['Hello there', 'This is a string', 'With multiple lines'].join('\n') + no_tabs_stripped := 'Hello there + |This is a string + |With multiple lines'.strip_margin() + assert no_tabs == no_tabs_stripped +} + +fn test_strip_margins_text_before() { + text_before := ['There is text', 'before the delimiter', 'that should be removed as well'].join('\n') + text_before_stripped := 'There is text + f lasj asldfj j lksjdf |before the delimiter + Which is removed hello |that should be removed as well'.strip_margin() + assert text_before_stripped == text_before +} + +fn test_strip_margins_white_space_after_delim() { + tabs := [' Tab', ' spaces', ' another tab'].join('\n') + tabs_stripped := ' Tab + | spaces + | another tab'.strip_margin() + assert tabs == tabs_stripped +} + +fn test_strip_margins_alternate_delim() { + alternate_delimiter := ['This has a different delim,', 'but that is ok', + 'because everything works', + ].join('\n') + alternate_delimiter_stripped := 'This has a different delim, + #but that is ok + #because everything works'.strip_margin_custom(`#`) + assert alternate_delimiter_stripped == alternate_delimiter +} + +fn test_strip_margins_multiple_delims_after_first() { + delim_after_first_instance := ['The delimiter used', + 'only matters the |||| First time it is seen', 'not any | other | times'].join('\n') + delim_after_first_instance_stripped := 'The delimiter used + |only matters the |||| First time it is seen + |not any | other | times'.strip_margin() + assert delim_after_first_instance_stripped == delim_after_first_instance +} + +fn test_strip_margins_uneven_delims() { + uneven_delims := ["It doesn't matter if the delims are uneven,", + 'The text will still be delimited correctly.', 'Maybe not everything needs 3 lines?', + 'Let us go for 4 then', + ].join('\n') + uneven_delims_stripped := "It doesn't matter if the delims are uneven, + |The text will still be delimited correctly. + |Maybe not everything needs 3 lines? + |Let us go for 4 then".strip_margin() + assert uneven_delims_stripped == uneven_delims +} + +fn test_strip_margins_multiple_blank_lines() { + multi_blank_lines := ['Multiple blank lines will be removed.', + ' I actually consider this a feature.', + ].join('\n') + multi_blank_lines_stripped := 'Multiple blank lines will be removed. + + + + | I actually consider this a feature.'.strip_margin() + assert multi_blank_lines == multi_blank_lines_stripped +} + +fn test_strip_margins_end_newline() { + end_with_newline := ['This line will end with a newline', 'Something cool or something.', ''].join('\n') + end_with_newline_stripped := 'This line will end with a newline + |Something cool or something. + + '.strip_margin() + assert end_with_newline_stripped == end_with_newline +} + +fn test_strip_margins_space_delimiter() { + space_delimiter := ['Using a white-space char will', 'revert back to default behavior.'].join('\n') + space_delimiter_stripped := 'Using a white-space char will + |revert back to default behavior.'.strip_margin_custom(`\n`) + assert space_delimiter == space_delimiter_stripped +} + +fn test_strip_margins_crlf() { + crlf := ["This string's line endings have CR as well as LFs.", 'This should pass', 'Definitely'].join('\r\n') + crlf_stripped := "This string's line endings have CR as well as LFs.\r + |This should pass\r + |Definitely".strip_margin() + + assert crlf == crlf_stripped +} diff --git a/v_windows/v/old/vlib/builtin/string_test.v b/v_windows/v/old/vlib/builtin/string_test.v new file mode 100644 index 0000000..8a5ee66 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/string_test.v @@ -0,0 +1,912 @@ +import strings + +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. + +struct Foo { + bar int +mut: + str string +} + +fn test_add() { + mut a := 'a' + a += 'b' + assert a == ('ab') + a = 'a' + for i := 1; i < 1000; i++ { + a += 'b' + } + assert a.len == 1000 + assert a.ends_with('bbbbb') + a += '123' + assert a.ends_with('3') +} + +fn test_ends_with() { + a := 'browser.v' + assert a.ends_with('.v') + + s := 'V Programming Language' + assert s.ends_with('guage') == true + assert s.ends_with('Language') == true + assert s.ends_with('Programming Language') == true + assert s.ends_with('V') == false +} + +fn test_between() { + s := 'hello [man] how you doing' + assert s.find_between('[', ']') == 'man' +} + +fn test_compare() { + a := 'Music' + b := 'src' + assert b >= a +} + +fn test_lt() { + a := '' + b := 'a' + c := 'a' + d := 'b' + e := 'aa' + f := 'ab' + assert a < b + assert !(b < c) + assert c < d + assert !(d < e) + assert c < e + assert e < f +} + +fn test_ge() { + a := 'aa' + b := 'aa' + c := 'ab' + d := 'abc' + e := 'aaa' + assert b >= a + assert c >= b + assert d >= c + assert !(c >= d) + assert e >= a +} + +fn test_compare_strings() { + a := 'aa' + b := 'aa' + c := 'ab' + d := 'abc' + e := 'aaa' + assert compare_strings(a, b) == 0 + assert compare_strings(b, c) == -1 + assert compare_strings(c, d) == -1 + assert compare_strings(d, e) == 1 + assert compare_strings(a, e) == -1 + assert compare_strings(e, a) == 1 +} + +fn test_sort() { + mut vals := [ + 'arr', + 'an', + 'a', + 'any', + ] + len := vals.len + vals.sort() + assert len == vals.len + assert vals[0] == 'a' + assert vals[1] == 'an' + assert vals[2] == 'any' + assert vals[3] == 'arr' +} + +fn test_sort_reverse() { + mut vals := [ + 'arr', + 'an', + 'a', + 'any', + ] + len := vals.len + vals.sort(b > a) + assert len == vals.len + assert vals[0] == 'a' + assert vals[1] == 'an' + assert vals[2] == 'any' + assert vals[3] == 'arr' +} + +fn test_split_nth() { + a := '1,2,3' + assert a.split(',').len == 3 + assert a.split_nth(',', -1).len == 3 + assert a.split_nth(',', 0).len == 3 + assert a.split_nth(',', 1).len == 1 + assert a.split_nth(',', 2).len == 2 + assert a.split_nth(',', 10).len == 3 + b := '1::2::3' + assert b.split('::').len == 3 + assert b.split_nth('::', -1).len == 3 + assert b.split_nth('::', 0).len == 3 + assert b.split_nth('::', 1).len == 1 + assert b.split_nth('::', 2).len == 2 + assert b.split_nth('::', 10).len == 3 + c := 'ABCDEF' + println(c.split('').len) + assert c.split('').len == 6 + assert c.split_nth('', 3).len == 3 + assert c.split_nth('BC', -1).len == 2 + d := ',' + assert d.split(',').len == 2 + assert d.split_nth('', 3).len == 1 + assert d.split_nth(',', -1).len == 2 + assert d.split_nth(',', 3).len == 2 + e := ',,,0,,,,,a,,b,' + assert e.split(',,').len == 5 + assert e.split_nth(',,', 3).len == 3 + assert e.split_nth(',', -1).len == 12 + assert e.split_nth(',', 3).len == 3 +} + +fn test_split_nth_values() { + line := 'CMD=eprintln(phase=1)' + + a0 := line.split_nth('=', 0) + assert a0.len == 3 + assert a0[0] == 'CMD' + assert a0[1] == 'eprintln(phase' + assert a0[2] == '1)' + + a1 := line.split_nth('=', 1) + assert a1.len == 1 + assert a1[0] == 'CMD=eprintln(phase=1)' + + a2 := line.split_nth('=', 2) + assert a2.len == 2 + assert a2[0] == 'CMD' + assert a2[1] == 'eprintln(phase=1)' + + a3 := line.split_nth('=', 3) + assert a3.len == 3 + assert a3[0] == 'CMD' + assert a3[1] == 'eprintln(phase' + assert a3[2] == '1)' + + a4 := line.split_nth('=', 4) + assert a4.len == 3 + assert a4[0] == 'CMD' + assert a4[1] == 'eprintln(phase' + assert a4[2] == '1)' +} + +fn test_split() { + mut s := 'volt/twitch.v:34' + mut vals := s.split(':') + assert vals.len == 2 + assert vals[0] == 'volt/twitch.v' + assert vals[1] == '34' + // ///////// + s = '2018-01-01z13:01:02' + vals = s.split('z') + assert vals.len == 2 + assert vals[0] == '2018-01-01' + assert vals[1] == '13:01:02' + // ////////// + s = '4627a862c3dec29fb3182a06b8965e0025759e18___1530207969___blue' + vals = s.split('___') + assert vals.len == 3 + assert vals[0] == '4627a862c3dec29fb3182a06b8965e0025759e18' + assert vals[1] == '1530207969' + assert vals[2] == 'blue' + // ///////// + s = 'lalala' + vals = s.split('a') + assert vals.len == 4 + assert vals[0] == 'l' + assert vals[1] == 'l' + assert vals[2] == 'l' + assert vals[3] == '' + // ///////// + s = 'awesome' + a := s.split('') + assert a.len == 7 + assert a[0] == 'a' + assert a[1] == 'w' + assert a[2] == 'e' + assert a[3] == 's' + assert a[4] == 'o' + assert a[5] == 'm' + assert a[6] == 'e' + // ///////// + s = 'wavy turquoise bags' + vals = s.split(' bags') + assert vals.len == 2 + assert vals[0] == 'wavy turquoise' + assert vals[1] == '' +} + +fn test_trim_space() { + a := ' a ' + assert a.trim_space() == 'a' + code := ' + +fn main() { + println(2) +} + +' + code_clean := 'fn main() { + println(2) +}' + assert code.trim_space() == code_clean +} + +fn test_join() { + mut strings := ['a', 'b', 'c'] + mut s := strings.join(' ') + assert s == 'a b c' + strings = [ + 'one +two ', + 'three! +four!', + ] + s = strings.join(' ') + assert s.contains('one') && s.contains('two ') && s.contains('four') + empty := []string{len: 0} + assert empty.join('A') == '' +} + +fn test_clone() { + mut a := 'a' + a += 'a' + a += 'a' + b := a + c := a.clone() + assert c == a + assert c == 'aaa' + assert b == 'aaa' +} + +fn test_replace() { + a := 'hello man!' + mut b := a.replace('man', 'world') + assert b == ('hello world!') + b = b.replace('!', '') + assert b == ('hello world') + b = b.replace('h', 'H') + assert b == ('Hello world') + b = b.replace('foo', 'bar') + assert b == ('Hello world') + s := 'hey man how are you' + assert s.replace('man ', '') == 'hey how are you' + lol := 'lol lol lol' + assert lol.replace('lol', 'LOL') == 'LOL LOL LOL' + b = 'oneBtwoBBthree' + assert b.replace('B', '') == 'onetwothree' + b = '*charptr' + assert b.replace('charptr', 'byteptr') == '*byteptr' + c := 'abc' + assert c.replace('', '-') == c + v := 'a b c d' + assert v.replace(' ', ' ') == 'a b c d' +} + +fn test_replace_each() { + s := 'hello man man :)' + q := s.replace_each([ + 'man', + 'dude', + 'hello', + 'hey', + ]) + assert q == 'hey dude dude :)' + bb := '[b]bold[/b] [code]code[/code]' + assert bb.replace_each([ + '[b]', + '', + '[/b]', + '', + '[code]', + '', + '[/code]', + '', + ]) == 'bold code' + bb2 := '[b]cool[/b]' + assert bb2.replace_each([ + '[b]', + '', + '[/b]', + '', + ]) == 'cool' + t := 'aaaaaaaa' + y := t.replace_each([ + 'aa', + 'b', + ]) + assert y == 'bbbb' + s2 := 'hello_world hello' + assert s2.replace_each(['hello_world', 'aaa', 'hello', 'bbb']) == 'aaa bbb' +} + +fn test_itoa() { + num := 777 + assert num.str() == '777' + big := 7779998 + assert big.str() == '7779998' + a := 3 + assert a.str() == '3' + b := 5555 + assert b.str() == '5555' + zero := 0 + assert zero.str() == '0' + neg := -7 + assert neg.str() == '-7' +} + +fn test_reassign() { + a := 'hi' + mut b := a + b += '!' + assert a == 'hi' + assert b == 'hi!' +} + +fn test_runes() { + s := 'привет' + assert s.len == 12 + s2 := 'privet' + assert s2.len == 6 + u := s.runes() + assert u.len == 6 + assert s2.substr(1, 4).len == 3 + assert s2.substr(1, 4) == 'riv' + assert s2[1..4].len == 3 + assert s2[1..4] == 'riv' + assert s2[..4].len == 4 + assert s2[..4] == 'priv' + assert s2[2..].len == 4 + assert s2[2..] == 'ivet' + assert u[1..4].string().len == 6 + assert u[1..4].string() == 'рив' + assert s2.substr(1, 2) == 'r' + assert u[1..2].string() == 'р' + assert s2.runes()[1] == `r` + assert u[1] == `р` + first := u[0] + last := u[u.len - 1] + assert first.str().len == 2 + assert last.str().len == 2 +} + +fn test_contains() { + s := 'view.v' + assert s.contains('vi') + assert !s.contains('random') + assert ''.contains('') + assert 'abc'.contains('') +} + +fn test_contains_any() { + assert !'team'.contains_any('i') + assert 'fail'.contains_any('ui') + assert 'ure'.contains_any('ui') + assert 'failure'.contains_any('ui') + assert !'foo'.contains_any('') + assert !''.contains_any('') +} + +fn test_contains_any_substr() { + s := 'Some random text' + assert s.contains_any_substr(['false', 'not', 'rand']) + assert !s.contains_any_substr(['ABC', 'invalid']) + assert ''.contains_any_substr([]) + assert 'abc'.contains_any_substr(['']) +} + +fn test_arr_contains() { + a := ['a', 'b', 'c'] + assert a.contains('b') + ints := [1, 2, 3] + assert ints.contains(2) +} + +fn test_to_num() { + s := '7' + assert s.int() == 7 + assert s.u64() == 7 + f := '71.5 hasdf' + // QTODO + assert f.f32() == 71.5 + vals := ['9'] + assert vals[0].int() == 9 + big := '93993993939322' + assert big.u64() == 93993993939322 + assert big.i64() == 93993993939322 +} + +fn test_inter_format_string() { + float_num := 1.52345 + float_num_string := '-${float_num:.3f}-' + assert float_num_string == '-1.523-' + int_num := 7 + int_num_string := '-${int_num:03d}-' + assert int_num_string == '-007-' + ch := `a` + ch_string := '-${ch:c}-' + assert ch_string == '-a-' + hex_n := 192 + hex_n_string := '-${hex_n:x}-' + assert hex_n_string == '-c0-' + oct_n := 192 + oct_n_string := '-${oct_n:o}-' + assert oct_n_string == '-300-' + str := 'abc' + str_string := '-${str:s}-' + assert str_string == '-abc-' +} + +fn test_hash() { + s := '10000' + assert s.hash() == 46730161 + s2 := '24640' + assert s2.hash() == 47778736 + s3 := 'Content-Type' + assert s3.hash() == 949037134 + s4 := 'bad_key' + assert s4.hash() == -346636507 + s5 := '24640' + // From a map collision test + assert s5.hash() % ((1 << 20) - 1) == s.hash() % ((1 << 20) - 1) + assert s5.hash() % ((1 << 20) - 1) == 592861 +} + +fn test_trim() { + assert 'banana'.trim('bna') == '' + assert 'abc'.trim('ac') == 'b' + assert 'aaabccc'.trim('ac') == 'b' +} + +fn test_trim_left() { + mut s := 'module main' + assert s.trim_left(' ') == 'module main' + s = ' module main' + assert s.trim_left(' ') == 'module main' + // test cutset + s = 'banana' + assert s.trim_left('ba') == 'nana' + assert s.trim_left('ban') == '' +} + +fn test_trim_right() { + mut s := 'module main' + assert s.trim_right(' ') == 'module main' + s = 'module main ' + assert s.trim_right(' ') == 'module main' + // test cutset + s = 'banana' + assert s.trim_right('na') == 'b' + assert s.trim_right('ban') == '' +} + +fn test_all_before() { + s := 'fn hello fn' + assert s.all_before(' ') == 'fn' + assert s.all_before('2') == s + assert s.all_before('') == s +} + +fn test_all_before_last() { + s := 'fn hello fn' + assert s.all_before_last(' ') == 'fn hello' + assert s.all_before_last('2') == s + assert s.all_before_last('') == s +} + +fn test_all_after() { + s := 'fn hello' + assert s.all_after('fn ') == 'hello' + assert s.all_after('test') == s + assert s.all_after('') == s + assert s.after('e') == 'llo' + x := s.after('e') + assert x == 'llo' +} + +fn test_reverse() { + assert 'hello'.reverse() == 'olleh' + assert ''.reverse() == '' + assert 'a'.reverse() == 'a' +} + +fn test_bytes_to_string() { + mut buf := vcalloc(10) + unsafe { + buf[0] = `h` + buf[1] = `e` + buf[2] = `l` + buf[3] = `l` + buf[4] = `o` + } + assert unsafe { buf.vstring() } == 'hello' + assert unsafe { buf.vstring_with_len(2) } == 'he' + bytes := [byte(`h`), `e`, `l`, `l`, `o`] + assert bytes.bytestr() == 'hello' +} + +fn test_charptr() { + foo := &char('VLANG'.str) + println(typeof(foo).name) + assert typeof(foo).name == '&char' + assert unsafe { foo.vstring() } == 'VLANG' + assert unsafe { foo.vstring_with_len(3) } == 'VLA' +} + +fn test_count() { + assert ''.count('') == 0 + assert ''.count('a') == 0 + assert 'a'.count('') == 0 + assert 'aa'.count('a') == 2 + assert 'aa'.count('aa') == 1 + assert 'aabbaa'.count('aa') == 2 + assert 'bbaabb'.count('aa') == 1 +} + +fn test_lower() { + mut s := 'A' + assert !s.is_lower() + assert s.to_lower() == 'a' + assert s.to_lower().len == 1 + s = 'HELLO' + assert !s.is_lower() + assert s.to_lower() == 'hello' + assert s.to_lower().len == 5 + s = 'Aloha' + assert !s.is_lower() + assert s.to_lower() == 'aloha' + s = 'Have A nice Day!' + assert !s.is_lower() + assert s.to_lower() == 'have a nice day!' + s = 'hi' + assert s.is_lower() + assert s.to_lower() == 'hi' + assert 'aloha!'[0] == `a` + assert 'aloha!'[5] == `!` +} + +fn test_upper() { + mut s := 'a' + assert !s.is_upper() + assert s.to_upper() == 'A' + assert s.to_upper().len == 1 + s = 'hello' + assert !s.is_upper() + assert s.to_upper() == 'HELLO' + assert s.to_upper().len == 5 + s = 'Aloha' + assert !s.is_upper() + assert s.to_upper() == 'ALOHA' + s = 'have a nice day!' + assert !s.is_upper() + assert s.to_upper() == 'HAVE A NICE DAY!' + s = 'HI' + assert s.is_upper() + assert s.to_upper() == 'HI' +} + +fn test_capitalize() { + mut s := 'hello' + assert !s.is_capital() + assert s.capitalize() == 'Hello' + s = 'test' + assert !s.is_capital() + assert s.capitalize() == 'Test' + s = 'i am ray' + assert !s.is_capital() + assert s.capitalize() == 'I am ray' + s = '' + assert !s.is_capital() + assert s.capitalize() == '' + s = 'TEST IT' + assert !s.is_capital() + assert s.capitalize() == 'TEST IT' + s = 'Test it' + assert s.is_capital() + assert s.capitalize() == 'Test it' + assert 'GameMission_t'.capitalize() == 'GameMission_t' +} + +fn test_title() { + mut s := 'hello world' + assert !s.is_title() + assert s.title() == 'Hello World' + s = 'HELLO WORLD' + assert !s.is_title() + assert s.title() == 'HELLO WORLD' + s = 'Hello World' + assert s.is_title() + assert s.title() == 'Hello World' +} + +fn test_for_loop() { + mut i := 0 + s := 'abcd' + + for c in s { + assert c == s[i] + i++ + } +} + +fn test_for_loop_two() { + s := 'abcd' + + for i, c in s { + assert c == s[i] + } +} + +fn test_quote() { + a := `\'` + println('testing double quotes') + b := 'hi' + assert b == 'hi' + assert a.str() == "'" +} + +fn test_limit() { + s := 'hello' + assert s.limit(2) == 'he' + assert s.limit(9) == s + assert s.limit(0) == '' + // assert s.limit(-1) == '' +} + +fn test_repeat() { + s1 := 'V! ' + assert s1.repeat(5) == 'V! V! V! V! V! ' + assert s1.repeat(1) == s1 + assert s1.repeat(0) == '' + s2 := '' + assert s2.repeat(5) == s2 + assert s2.repeat(1) == s2 + assert s2.repeat(0) == s2 + // TODO Add test for negative values +} + +fn test_starts_with() { + s := 'V Programming Language' + assert s.starts_with('V') == true + assert s.starts_with('V Programming') == true + assert s.starts_with('Language') == false +} + +fn test_trim_prefix() { + s := 'V Programming Language' + assert s.trim_prefix('V ') == 'Programming Language' + assert s.trim_prefix('V Programming ') == 'Language' + assert s.trim_prefix('Language') == s + + s2 := 'TestTestTest' + assert s2.trim_prefix('Test') == 'TestTest' + assert s2.trim_prefix('TestTest') == 'Test' + + s3 := '123Test123Test' + assert s3.trim_prefix('123') == 'Test123Test' + assert s3.trim_prefix('123Test') == '123Test' +} + +fn test_trim_suffix() { + s := 'V Programming Language' + assert s.trim_suffix(' Language') == 'V Programming' + assert s.trim_suffix(' Programming Language') == 'V' + assert s.trim_suffix('V') == s + + s2 := 'TestTestTest' + assert s2.trim_suffix('Test') == 'TestTest' + assert s2.trim_suffix('TestTest') == 'Test' + + s3 := '123Test123Test' + assert s3.trim_suffix('123') == s3 + assert s3.trim_suffix('123Test') == '123Test' +} + +fn test_raw() { + raw := r'raw\nstring' + lines := raw.split('\n') + println(lines) + assert lines.len == 1 + println('raw string: "$raw"') + + raw2 := r'Hello V\0' + assert raw2[7] == `\\` + assert raw2[8] == `0` + + raw3 := r'Hello V\x00' + assert raw3[7] == `\\` + assert raw3[8] == `x` + assert raw3[9] == `0` + assert raw3[10] == `0` +} + +fn test_raw_with_quotes() { + raw := r"some'" + r'"thing' // " should be escaped in the generated C code + assert raw[0] == `s` + assert raw[5] == `"` + assert raw[6] == `t` +} + +fn test_escape() { + a := 10 + println("\"$a") + assert "\"$a" == '"10' +} + +fn test_atoi() { + assert '234232'.int() == 234232 + assert '-9009'.int() == -9009 + assert '0'.int() == 0 + for n in -10000 .. 100000 { + s := n.str() + assert s.int() == n + } +} + +fn test_raw_inter() { + world := 'world' + println(world) + s := r'hello\n$world' + assert s == r'hello\n$world' + assert s.contains('$') +} + +fn test_c_r() { + // This used to break because of r'' and c'' + c := 42 + println('$c') + r := 50 + println('$r') +} + +fn test_inter_before_comp_if() { + s := '123' + // This used to break ('123 $....') + $if linux { + println(s) + } + assert s == '123' +} + +fn test_double_quote_inter() { + a := 1 + b := 2 + println('$a $b') + assert '$a $b' == '1 2' + assert '$a $b' == '1 2' +} + +fn foo(b byte) byte { + return b - 10 +} + +fn filter(b byte) bool { + return b != `a` +} + +fn test_split_into_lines() { + line_content := 'Line' + text_crlf := '$line_content\r\n$line_content\r\n$line_content' + lines_crlf := text_crlf.split_into_lines() + + assert lines_crlf.len == 3 + for line in lines_crlf { + assert line == line_content + } + + text_lf := '$line_content\n$line_content\n$line_content' + lines_lf := text_lf.split_into_lines() + + assert lines_lf.len == 3 + for line in lines_lf { + assert line == line_content + } +} + +fn test_string_literal_with_backslash() { + a := 'HelloWorld' + assert a == 'HelloWorld' + + b := 'OneTwoThree' + assert b == 'OneTwoThree' +} + +/* +type MyString = string + +fn test_string_alias() { + s := MyString('hi') + ss := s + '!' +} +*/ + +// sort an array of structs, by their string field values + +struct Ka { + s string + i int +} + +fn test_sorter() { + mut arr := [ + Ka{ + s: 'bbb' + i: 100 + }, + Ka{ + s: 'aaa' + i: 101 + }, + Ka{ + s: 'ccc' + i: 102 + }, + ] + cmp := fn (a &Ka, b &Ka) int { + return compare_strings(a.s, b.s) + } + arr.sort_with_compare(cmp) + assert arr[0].s == 'aaa' + assert arr[0].i == 101 + assert arr[1].s == 'bbb' + assert arr[1].i == 100 + assert arr[2].s == 'ccc' + assert arr[2].i == 102 +} + +fn test_fields() { + assert 'a bcde'.fields() == ['a', 'bcde'] + assert ' sss \t ssss '.fields() == ['sss', 'ssss'] + assert '\n xyz \t abc def'.fields() == ['xyz', 'abc', 'def'] + assert 'hello'.fields() == ['hello'] + assert ''.fields() == [] +} + +fn test_interpolation_after_quoted_variable_still_works() { + rr := 'abc' + tt := 'xyz' + + // Basic interpolation, no internal quotes + yy := 'Replacing $rr with $tt' + assert yy == 'Replacing abc with xyz' + + // Interpolation after quoted variable ending with 'r'quote + // that may be mistaken with the start of a raw string, + // ensure that it is not. + ss := 'Replacing "$rr" with "$tt"' + assert ss == 'Replacing "abc" with "xyz"' + zz := "Replacing '$rr' with '$tt'" + assert zz == "Replacing 'abc' with 'xyz'" + + // Interpolation after quoted variable ending with 'c'quote + // may be mistaken with the start of a c string, so + // check it is not. + cc := 'abc' + ccc := "Replacing '$cc' with '$tt'" + assert ccc == "Replacing 'abc' with 'xyz'" + cccq := 'Replacing "$cc" with "$tt"' + assert cccq == 'Replacing "abc" with "xyz"' +} + +fn test_emoji_to_runes() { + x := '👋' + assert x.runes()[0] == `👋` +} + +fn test_string_to_rune() { + x := 'Hello World 👋' + assert x.runes().len == 13 +} diff --git a/v_windows/v/old/vlib/builtin/utf8.c.v b/v_windows/v/old/vlib/builtin/utf8.c.v new file mode 100644 index 0000000..f2ead3a --- /dev/null +++ b/v_windows/v/old/vlib/builtin/utf8.c.v @@ -0,0 +1,79 @@ +module builtin + +const ( + cp_utf8 = 65001 +) + +pub fn (_str string) to_wide() &u16 { + $if windows { + unsafe { + num_chars := (C.MultiByteToWideChar(cp_utf8, 0, &char(_str.str), _str.len, + 0, 0)) + mut wstr := &u16(malloc_noscan((num_chars + 1) * 2)) // sizeof(wchar_t) + if wstr != 0 { + C.MultiByteToWideChar(cp_utf8, 0, &char(_str.str), _str.len, wstr, num_chars) + C.memset(&byte(wstr) + num_chars * 2, 0, 2) + } + return wstr + } + } $else { + return 0 + } +} + +[unsafe] +pub fn string_from_wide(_wstr &u16) string { + $if windows { + unsafe { + wstr_len := C.wcslen(_wstr) + return string_from_wide2(_wstr, wstr_len) + } + } $else { + return '' + } +} + +[unsafe] +pub fn string_from_wide2(_wstr &u16, len int) string { + $if windows { + unsafe { + num_chars := C.WideCharToMultiByte(cp_utf8, 0, _wstr, len, 0, 0, 0, 0) + mut str_to := malloc_noscan(num_chars + 1) + if str_to != 0 { + C.WideCharToMultiByte(cp_utf8, 0, _wstr, len, &char(str_to), num_chars, + 0, 0) + C.memset(str_to + num_chars, 0, 1) + } + return tos2(str_to) + } + } $else { + return '' + } +} + +// Reads an utf8 character from standard input +pub fn utf8_getchar() int { + c := C.getchar() + len := utf8_len(byte(~c)) + if c < 0 { + return 0 + } else if len == 0 { + return c + } else if len == 1 { + return -1 + } else { + mut uc := c & ((1 << (7 - len)) - 1) + for i := 0; i + 1 < len; i++ { + c2 := C.getchar() + if c2 != -1 && (c2 >> 6) == 2 { + uc <<= 6 + uc |= (c2 & 63) + } else if c2 == -1 { + return 0 + } else { + return -1 + } + } + return uc + } +} diff --git a/v_windows/v/old/vlib/builtin/utf8.v b/v_windows/v/old/vlib/builtin/utf8.v new file mode 100644 index 0000000..6f3745a --- /dev/null +++ b/v_windows/v/old/vlib/builtin/utf8.v @@ -0,0 +1,188 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module builtin + +pub fn utf8_char_len(b byte) int { + return ((0xe5000000 >> ((b >> 3) & 0x1e)) & 3) + 1 +} + +// Convert utf32 to utf8 +// utf32 == Codepoint +pub fn utf32_to_str(code u32) string { + unsafe { + mut buffer := malloc_noscan(5) + return utf32_to_str_no_malloc(code, buffer) + } +} + +[unsafe] +pub fn utf32_to_str_no_malloc(code u32, buf voidptr) string { + icode := int(code) // Prevents doing casts everywhere + mut res := '' + unsafe { + mut buffer := &byte(buf) + if icode <= 127 { + // 0x7F + buffer[0] = byte(icode) + buffer[1] = 0 + res = tos(buffer, 1) + } else if icode <= 2047 { + // 0x7FF + buffer[0] = 192 | byte(icode >> 6) // 0xC0 - 110xxxxx + buffer[1] = 128 | byte(icode & 63) // 0x80 - 0x3F - 10xxxxxx + buffer[2] = 0 + res = tos(buffer, 2) + } else if icode <= 65535 { + // 0xFFFF + buffer[0] = 224 | byte(icode >> 12) // 0xE0 - 1110xxxx + buffer[1] = 128 | (byte(icode >> 6) & 63) // 0x80 - 0x3F - 10xxxxxx + buffer[2] = 128 | byte(icode & 63) // 0x80 - 0x3F - 10xxxxxx + buffer[3] = 0 + res = tos(buffer, 3) + } + // 0x10FFFF + else if icode <= 1114111 { + buffer[0] = 240 | byte(icode >> 18) // 0xF0 - 11110xxx + buffer[1] = 128 | (byte(icode >> 12) & 63) // 0x80 - 0x3F - 10xxxxxx + buffer[2] = 128 | (byte(icode >> 6) & 63) // 0x80 - 0x3F - 10xxxxxx + buffer[3] = 128 | byte(icode & 63) // 0x80 - 0x3F - 10xxxxxx + buffer[4] = 0 + res = tos(buffer, 4) + } + } + res.is_lit = 1 // let autofree know this string doesn't have to be freed + return res +} + +// Convert utf8 to utf32 +pub fn (_rune string) utf32_code() int { + if _rune.len == 0 { + return 0 + } + // save ASC symbol as is + if _rune.len == 1 { + return int(_rune[0]) + } + mut b := byte(int(_rune[0])) + // TODO should be + // res := int( rune[0] << rune.len) + b = b << _rune.len + mut res := int(b) + mut shift := 6 - _rune.len + for i := 1; i < _rune.len; i++ { + c := int(_rune[i]) + res = res << shift + res |= c & 63 // 0x3f + shift = 6 + } + return res +} + +// Calculate length to read from the first byte +fn utf8_len(c byte) int { + mut b := 0 + mut x := c + if (x & 240) != 0 { + // 0xF0 + x >>= 4 + } else { + b += 4 + } + if (x & 12) != 0 { + // 0x0C + x >>= 2 + } else { + b += 2 + } + if (x & 2) == 0 { + // 0x02 + b++ + } + return b +} + +// Calculate string length for in number of codepoints +pub fn utf8_str_len(s string) int { + mut l := 0 + mut i := 0 + for i < s.len { + l++ + i += ((0xe5000000 >> ((unsafe { s.str[i] } >> 3) & 0x1e)) & 3) + 1 + } + return l +} + +// Calculate string length for formatting, i.e. number of "characters" +// This is simplified implementation. if you need specification compliant width, +// use utf8.east_asian.display_width. +pub fn utf8_str_visible_length(s string) int { + mut l := 0 + mut ul := 1 + for i := 0; i < s.len; i += ul { + c := unsafe { s.str[i] } + ul = ((0xe5000000 >> ((unsafe { s.str[i] } >> 3) & 0x1e)) & 3) + 1 + if i + ul > s.len { // incomplete UTF-8 sequence + return l + } + l++ + // avoid the match if not needed + if ul == 1 { + continue + } + // recognize combining characters and wide characters + match ul { + 2 { + r := u64((u16(c) << 8) | unsafe { s.str[i + 1] }) + if r >= 0xcc80 && r < 0xcdb0 { + // diacritical marks + l-- + } + } + 3 { + r := u64((u32(c) << 16) | unsafe { (u32(s.str[i + 1]) << 8) | s.str[i + 2] }) + // diacritical marks extended + // diacritical marks supplement + // diacritical marks for symbols + if (r >= 0xe1aab0 && r <= 0xe1ac7f) + || (r >= 0xe1b780 && r <= 0xe1b87f) + || (r >= 0xe28390 && r <= 0xe2847f) + || (r >= 0xefb8a0 && r <= 0xefb8af) { + // diacritical marks + l-- + } + // Hangru + // CJK Unified Ideographics + // Hangru + // CJK + else if (r >= 0xe18480 && r <= 0xe1859f) + || (r >= 0xe2ba80 && r <= 0xe2bf95) + || (r >= 0xe38080 && r <= 0xe4b77f) + || (r >= 0xe4b880 && r <= 0xea807f) + || (r >= 0xeaa5a0 && r <= 0xeaa79f) + || (r >= 0xeab080 && r <= 0xed9eaf) + || (r >= 0xefa480 && r <= 0xefac7f) + || (r >= 0xefb8b8 && r <= 0xefb9af) { + // half marks + l++ + } + } + 4 { + r := u64((u32(c) << 24) | unsafe { + (u32(s.str[i + 1]) << 16) | (u32(s.str[i + 2]) << 8) | s.str[i + 3] + }) + // Enclosed Ideographic Supplement + // Emoji + // CJK Unified Ideographs Extension B-G + if (r >= 0x0f9f8880 && r <= 0xf09f8a8f) + || (r >= 0xf09f8c80 && r <= 0xf09f9c90) + || (r >= 0xf09fa490 && r <= 0xf09fa7af) + || (r >= 0xf0a08080 && r <= 0xf180807f) { + l++ + } + } + else {} + } + } + return l +} diff --git a/v_windows/v/old/vlib/builtin/utf8_test.v b/v_windows/v/old/vlib/builtin/utf8_test.v new file mode 100644 index 0000000..46983f1 --- /dev/null +++ b/v_windows/v/old/vlib/builtin/utf8_test.v @@ -0,0 +1,28 @@ +fn test_utf8_char_len() { + assert utf8_char_len(`a`) == 1 + println(utf8_char_len(`a`)) + s := 'п' + assert utf8_char_len(s[0]) == 2 +} + +fn test_utf8_wide_char() { + $if msvc { + // TODO: make this test pass msvc too + return + } + r := `✔` + s := '✔' + println('r: $r') + println('s: $s') + rstr := r.str() + println('rstr: $rstr') + assert utf8_char_len(r) == 1 + assert utf8_char_len(s[0]) == 3 + assert s == rstr + val := rstr.str + unsafe { + assert val[0].hex() == 'e2' + assert val[1].hex() == '9c' + assert val[2].hex() == '94' + } +} diff --git a/v_windows/v/old/vlib/cli/README.md b/v_windows/v/old/vlib/cli/README.md new file mode 100644 index 0000000..93d8d62 --- /dev/null +++ b/v_windows/v/old/vlib/cli/README.md @@ -0,0 +1,30 @@ +Usage example: + +```v +module main + +import os +import cli + +fn main() { + mut app := cli.Command{ + name: 'example-app' + description: 'example-app' + execute: fn (cmd cli.Command) ? { + println('hello app') + return + } + commands: [ + cli.Command{ + name: 'sub' + execute: fn (cmd cli.Command) ? { + println('hello subcommand') + return + } + }, + ] + } + app.setup() + app.parse(os.args) +} +``` diff --git a/v_windows/v/old/vlib/cli/command.v b/v_windows/v/old/vlib/cli/command.v new file mode 100644 index 0000000..43940d1 --- /dev/null +++ b/v_windows/v/old/vlib/cli/command.v @@ -0,0 +1,305 @@ +module cli + +type FnCommandCallback = fn (cmd Command) ? + +// str returns the `string` representation of the callback. +pub fn (f FnCommandCallback) str() string { + return 'FnCommandCallback=>' + ptr_str(f) +} + +// Command is a structured representation of a single command +// or chain of commands. +pub struct Command { +pub mut: + name string + usage string + description string + version string + pre_execute FnCommandCallback + execute FnCommandCallback + post_execute FnCommandCallback + disable_help bool + disable_version bool + disable_flags bool + sort_flags bool + sort_commands bool + parent &Command = 0 + commands []Command + flags []Flag + required_args int + args []string +} + +// str returns the `string` representation of the `Command`. +pub fn (cmd Command) str() string { + mut res := []string{} + res << 'Command{' + res << ' name: "$cmd.name"' + res << ' usage: "$cmd.usage"' + res << ' version: "$cmd.version"' + res << ' description: "$cmd.description"' + res << ' disable_help: $cmd.disable_help' + res << ' disable_flags: $cmd.disable_flags' + res << ' disable_version: $cmd.disable_version' + res << ' sort_flags: $cmd.sort_flags' + res << ' sort_commands: $cmd.sort_commands' + res << ' cb execute: $cmd.execute' + res << ' cb pre_execute: $cmd.pre_execute' + res << ' cb post_execute: $cmd.post_execute' + if cmd.parent == 0 { + res << ' parent: &Command(0)' + } else { + res << ' parent: &Command{$cmd.parent.name ...}' + } + res << ' commands: $cmd.commands' + res << ' flags: $cmd.flags' + res << ' required_args: $cmd.required_args' + res << ' args: $cmd.args' + res << '}' + return res.join('\n') +} + +// is_root returns `true` if this `Command` has no parents. +pub fn (cmd Command) is_root() bool { + return isnil(cmd.parent) +} + +// root returns the root `Command` of the command chain. +pub fn (cmd Command) root() Command { + if cmd.is_root() { + return cmd + } + return cmd.parent.root() +} + +// full_name returns the full `string` representation of all commands int the chain. +pub fn (cmd Command) full_name() string { + if cmd.is_root() { + return cmd.name + } + return cmd.parent.full_name() + ' $cmd.name' +} + +// add_commands adds the `commands` array of `Command`s as sub-commands. +pub fn (mut cmd Command) add_commands(commands []Command) { + for command in commands { + cmd.add_command(command) + } +} + +// add_command adds `command` as a sub-command of this `Command`. +pub fn (mut cmd Command) add_command(command Command) { + mut subcmd := command + if cmd.commands.contains(subcmd.name) { + println('Command with the name `$subcmd.name` already exists') + exit(1) + } + subcmd.parent = unsafe { cmd } + cmd.commands << subcmd +} + +// setup ensures that all sub-commands of this `Command` +// is linked as a chain. +pub fn (mut cmd Command) setup() { + for mut subcmd in cmd.commands { + subcmd.parent = unsafe { cmd } + subcmd.setup() + } +} + +// add_flags adds the array `flags` to this `Command`. +pub fn (mut cmd Command) add_flags(flags []Flag) { + for flag in flags { + cmd.add_flag(flag) + } +} + +// add_flag adds `flag` to this `Command`. +pub fn (mut cmd Command) add_flag(flag Flag) { + if cmd.flags.contains(flag.name) { + println('Flag with the name `$flag.name` already exists') + exit(1) + } + cmd.flags << flag +} + +// parse parses `args` into this structured `Command`. +pub fn (mut cmd Command) parse(args []string) { + if !cmd.disable_flags { + cmd.add_default_flags() + } + cmd.add_default_commands() + if cmd.sort_flags { + cmd.flags.sort(a.name < b.name) + } + if cmd.sort_commands { + cmd.commands.sort(a.name < b.name) + } + cmd.args = args[1..] + if !cmd.disable_flags { + cmd.parse_flags() + } + cmd.parse_commands() +} + +// add_default_flags adds the commonly used `-h`/`--help` and +// `-v`/`--version` flags to the `Command`. +fn (mut cmd Command) add_default_flags() { + if !cmd.disable_help && !cmd.flags.contains('help') { + use_help_abbrev := !cmd.flags.contains('h') && cmd.flags.have_abbrev() + cmd.add_flag(help_flag(use_help_abbrev)) + } + if !cmd.disable_version && cmd.version != '' && !cmd.flags.contains('version') { + use_version_abbrev := !cmd.flags.contains('v') && cmd.flags.have_abbrev() + cmd.add_flag(version_flag(use_version_abbrev)) + } +} + +// add_default_commands adds the command functions of the +// commonly used `help` and `version` flags to the `Command`. +fn (mut cmd Command) add_default_commands() { + if !cmd.disable_help && !cmd.commands.contains('help') && cmd.is_root() { + cmd.add_command(help_cmd()) + } + if !cmd.disable_version && cmd.version != '' && !cmd.commands.contains('version') { + cmd.add_command(version_cmd()) + } +} + +fn (mut cmd Command) parse_flags() { + for { + if cmd.args.len < 1 || !cmd.args[0].starts_with('-') { + break + } + mut found := false + for i in 0 .. cmd.flags.len { + unsafe { + mut flag := &cmd.flags[i] + if flag.matches(cmd.args, cmd.flags.have_abbrev()) { + found = true + flag.found = true + cmd.args = flag.parse(cmd.args, cmd.flags.have_abbrev()) or { + println('Failed to parse flag `${cmd.args[0]}`: $err') + exit(1) + } + break + } + } + } + if !found { + println('Command `$cmd.name` has no flag `${cmd.args[0]}`') + exit(1) + } + } +} + +fn (mut cmd Command) parse_commands() { + global_flags := cmd.flags.filter(it.global) + cmd.check_help_flag() + cmd.check_version_flag() + for i in 0 .. cmd.args.len { + arg := cmd.args[i] + for j in 0 .. cmd.commands.len { + mut command := cmd.commands[j] + if command.name == arg { + for flag in global_flags { + command.add_flag(flag) + } + command.parse(cmd.args[i..]) + return + } + } + } + if cmd.is_root() && isnil(cmd.execute) { + if !cmd.disable_help { + cmd.execute_help() + return + } + } + // if no further command was found, execute current command + if cmd.required_args > 0 { + if cmd.required_args > cmd.args.len { + eprintln('Command `$cmd.name` needs at least $cmd.required_args arguments') + exit(1) + } + } + cmd.check_required_flags() + if !isnil(cmd.pre_execute) { + cmd.pre_execute(*cmd) or { + eprintln('cli preexecution error: $err') + exit(1) + } + } + if !isnil(cmd.execute) { + cmd.execute(*cmd) or { + eprintln('cli execution error: $err') + exit(1) + } + } + if !isnil(cmd.post_execute) { + cmd.post_execute(*cmd) or { + eprintln('cli postexecution error: $err') + exit(1) + } + } +} + +fn (cmd Command) check_help_flag() { + if !cmd.disable_help && cmd.flags.contains('help') { + help_flag := cmd.flags.get_bool('help') or { return } // ignore error and handle command normally + if help_flag { + cmd.execute_help() + exit(0) + } + } +} + +fn (cmd Command) check_version_flag() { + if !cmd.disable_version && cmd.version != '' && cmd.flags.contains('version') { + version_flag := cmd.flags.get_bool('version') or { return } // ignore error and handle command normally + if version_flag { + version_cmd := cmd.commands.get('version') or { return } // ignore error and handle command normally + version_cmd.execute(version_cmd) or { panic(err) } + exit(0) + } + } +} + +fn (cmd Command) check_required_flags() { + for flag in cmd.flags { + if flag.required && flag.value.len == 0 { + full_name := cmd.full_name() + println('Flag `$flag.name` is required by `$full_name`') + exit(1) + } + } +} + +// execute_help executes the callback registered +// for the `-h`/`--help` flag option. +pub fn (cmd Command) execute_help() { + if cmd.commands.contains('help') { + help_cmd := cmd.commands.get('help') or { return } // ignore error and handle command normally + help_cmd.execute(help_cmd) or { panic(err) } + } else { + print(cmd.help_message()) + } +} + +fn (cmds []Command) get(name string) ?Command { + for cmd in cmds { + if cmd.name == name { + return cmd + } + } + return error('Command `$name` not found in $cmds') +} + +fn (cmds []Command) contains(name string) bool { + for cmd in cmds { + if cmd.name == name { + return true + } + } + return false +} diff --git a/v_windows/v/old/vlib/cli/command_test.v b/v_windows/v/old/vlib/cli/command_test.v new file mode 100644 index 0000000..aae7199 --- /dev/null +++ b/v_windows/v/old/vlib/cli/command_test.v @@ -0,0 +1,221 @@ +import cli + +fn test_if_command_parses_empty_args() { + mut cmd := cli.Command{ + name: 'command' + execute: empty_func + } + cmd.parse(['command']) + assert cmd.name == 'command' && compare_arrays(cmd.args, []) +} + +fn test_if_command_parses_args() { + mut cmd := cli.Command{ + name: 'command' + execute: empty_func + } + cmd.parse(['command', 'arg0', 'arg1']) + assert cmd.name == 'command' && compare_arrays(cmd.args, ['arg0', 'arg1']) +} + +fn test_if_subcommands_parse_args() { + mut cmd := cli.Command{ + name: 'command' + } + subcmd := cli.Command{ + name: 'subcommand' + execute: if_subcommands_parse_args_func + } + cmd.add_command(subcmd) + cmd.parse(['command', 'subcommand', 'arg0', 'arg1']) +} + +fn if_subcommands_parse_args_func(cmd cli.Command) ? { + assert cmd.name == 'subcommand' && compare_arrays(cmd.args, ['arg0', 'arg1']) +} + +fn test_if_command_has_default_help_subcommand() { + mut cmd := cli.Command{ + name: 'command' + } + cmd.parse(['command']) + assert has_command(cmd, 'help') +} + +fn test_if_command_has_default_version_subcommand_if_version_is_set() { + mut cmd := cli.Command{ + name: 'command' + version: '1.0.0' + } + cmd.parse(['command']) + assert has_command(cmd, 'version') +} + +fn flag_should_be_set(cmd cli.Command) ? { + flag := cmd.flags.get_string('flag') ? + assert flag == 'value' +} + +fn test_if_flag_gets_set() { + mut cmd := cli.Command{ + name: 'command' + execute: flag_should_be_set + } + cmd.add_flag(cli.Flag{ + flag: .string + name: 'flag' + }) + cmd.parse(['command', '-flag', 'value']) +} + +fn test_if_flag_gets_set_with_abbrev() { + mut cmd := cli.Command{ + name: 'command' + execute: flag_should_be_set + } + cmd.add_flag(cli.Flag{ + flag: .string + name: 'flag' + abbrev: 'f' + }) + cmd.parse(['command', '-f', 'value']) +} + +fn test_if_flag_gets_set_with_long_arg() { + mut cmd := cli.Command{ + name: 'command' + execute: flag_should_be_set + } + cmd.add_flag(cli.Flag{ + flag: .string + name: 'flag' + abbrev: 'f' + }) + cmd.parse(['command', '--flag', 'value']) +} + +fn flag_should_have_value_of_42(cmd cli.Command) ? { + flag := cmd.flags.get_string('flag') ? + assert flag == 'value' + value := cmd.flags.get_int('value') ? + assert value == 42 +} + +fn test_if_multiple_flags_get_set() { + mut cmd := cli.Command{ + name: 'command' + execute: flag_should_have_value_of_42 + } + cmd.add_flag(cli.Flag{ + flag: .string + name: 'flag' + }) + cmd.add_flag(cli.Flag{ + flag: .int + name: 'value' + }) + cmd.parse(['command', '-flag', 'value', '-value', '42']) +} + +fn test_if_required_flags_get_set() { + mut cmd := cli.Command{ + name: 'command' + execute: flag_should_have_value_of_42 + } + cmd.add_flag(cli.Flag{ + flag: .string + name: 'flag' + }) + cmd.add_flag(cli.Flag{ + flag: .int + name: 'value' + required: true + }) + cmd.parse(['command', '-flag', 'value', '-value', '42']) +} + +fn flag_is_set_in_subcommand(cmd cli.Command) ? { + flag := cmd.flags.get_string('flag') or { panic(err) } + assert flag == 'value' +} + +fn test_if_flag_gets_set_in_subcommand() { + mut cmd := cli.Command{ + name: 'command' + execute: empty_func + } + mut subcmd := cli.Command{ + name: 'subcommand' + execute: flag_is_set_in_subcommand + } + subcmd.add_flag(cli.Flag{ + flag: .string + name: 'flag' + }) + cmd.add_command(subcmd) + cmd.parse(['command', 'subcommand', '-flag', 'value']) +} + +fn test_if_global_flag_gets_set_in_subcommand() { + mut cmd := cli.Command{ + name: 'command' + execute: empty_func + } + cmd.add_flag(cli.Flag{ + flag: .string + name: 'flag' + global: true + }) + subcmd := cli.Command{ + name: 'subcommand' + execute: flag_is_set_in_subcommand + } + cmd.add_command(subcmd) + cmd.parse(['command', '-flag', 'value', 'subcommand']) +} + +fn test_command_setup() { + mut cmd := cli.Command{ + name: 'root' + commands: [ + cli.Command{ + name: 'child' + commands: [ + cli.Command{ + name: 'child-child' + }, + ] + }, + ] + } + assert isnil(cmd.commands[0].parent) + assert isnil(cmd.commands[0].commands[0].parent) + cmd.setup() + assert cmd.commands[0].parent.name == 'root' + assert cmd.commands[0].commands[0].parent.name == 'child' +} + +// helper functions +fn empty_func(cmd cli.Command) ? { +} + +fn has_command(cmd cli.Command, name string) bool { + for subcmd in cmd.commands { + if subcmd.name == name { + return true + } + } + return false +} + +fn compare_arrays(array0 []string, array1 []string) bool { + if array0.len != array1.len { + return false + } + for i in 0 .. array0.len { + if array0[i] != array1[i] { + return false + } + } + return true +} diff --git a/v_windows/v/old/vlib/cli/flag.v b/v_windows/v/old/vlib/cli/flag.v new file mode 100644 index 0000000..be84f6f --- /dev/null +++ b/v_windows/v/old/vlib/cli/flag.v @@ -0,0 +1,310 @@ +module cli + +pub enum FlagType { + bool + int + float + string + // If flag can set multiple time, use array type + int_array + float_array + string_array +} + +// Flag holds information for a command line flag. +// (flags are also commonly referred to as "options" or "switches") +// These are typically denoted in the shell by a short form `-f` and/or a long form `--flag` +pub struct Flag { +pub mut: + flag FlagType + // Name of flag + name string + // Like short option + abbrev string + // Desciption of flag + description string + global bool + // If flag is requierd + required bool + // Default value if no value provide by command line + default_value []string = [] +mut: + // Set true if flag found. + found bool + // Value of flag + value []string = [] +} + +// get_all_found returns an array of all `Flag`s found in the command parameters +pub fn (flags []Flag) get_all_found() []Flag { + return flags.filter(it.found) +} + +// get_bool returns `true` if the flag is set. +// get_bool returns an error if the `FlagType` is not boolean. +pub fn (flag Flag) get_bool() ?bool { + if flag.flag != .bool { + return error('$flag.name: Invalid flag type `$flag.flag`, expected `bool`') + } + + val := flag.get_value_or_default_value() + + return val.len > 0 && val[0] == 'true' +} + +// get_bool returns `true` if the flag specified in `name` is set. +// get_bool returns an error if the `FlagType` is not boolean. +pub fn (flags []Flag) get_bool(name string) ?bool { + flag := flags.get(name) ? + return flag.get_bool() +} + +// get_int returns the `int` value argument of the flag. +// get_int returns an error if the `FlagType` is not integer. +pub fn (flag Flag) get_int() ?int { + if flag.flag != .int { + return error('$flag.name: Invalid flag type `$flag.flag`, expected `int`') + } + + val := flag.get_value_or_default_value() + + if val.len == 0 { + return 0 + } else { + return val[0].int() + } +} + +// get_ints returns the array of `int` value argument of the flag specified in `name`. +// get_ints returns an error if the `FlagType` is not integer. +pub fn (flag Flag) get_ints() ?[]int { + if flag.flag != .int_array { + return error('$flag.name: Invalid flag type `$flag.flag`, expected `int_array`') + } + + val := flag.get_value_or_default_value() + + if val.len == 0 { + return []int{} + } else { + mut values := []int{} + + for f in val { + values << f.int() + } + + return values + } +} + +// get_int returns the `int` value argument of the flag specified in `name`. +// get_int returns an error if the `FlagType` is not integer. +pub fn (flags []Flag) get_int(name string) ?int { + flag := flags.get(name) ? + return flag.get_int() +} + +// get_ints returns the array of `int` value argument of the flag specified in `name`. +// get_ints returns an error if the `FlagType` is not integer. +pub fn (flags []Flag) get_ints(name string) ?[]int { + flag := flags.get(name) ? + return flag.get_ints() +} + +// get_float returns the `f64` value argument of the flag. +// get_float returns an error if the `FlagType` is not floating point. +pub fn (flag Flag) get_float() ?f64 { + if flag.flag != .float { + return error('$flag.name: Invalid flag type `$flag.flag`, expected `float`') + } + + val := flag.get_value_or_default_value() + + if val.len == 0 { + return 0.0 + } else { + return val[0].f64() + } +} + +// get_floats returns the `f64` value argument of the flag. +// get_floats returns an error if the `FlagType` is not floating point. +pub fn (flag Flag) get_floats() ?[]f64 { + if flag.flag != .float_array { + return error('$flag.name: Invalid flag type `$flag.flag`, expected `float_array`') + } + + val := flag.get_value_or_default_value() + + if val.len == 0 { + return []f64{} + } else { + mut values := []f64{} + + for f in val { + values << f.f64() + } + + return values + } +} + +// get_float returns the `f64` value argument of the flag specified in `name`. +// get_float returns an error if the `FlagType` is not floating point. +pub fn (flags []Flag) get_float(name string) ?f64 { + flag := flags.get(name) ? + return flag.get_float() +} + +// get_floats returns the array of `f64` value argument of the flag specified in `name`. +// get_floats returns an error if the `FlagType` is not floating point. +pub fn (flags []Flag) get_floats(name string) ?[]f64 { + flag := flags.get(name) ? + return flag.get_floats() +} + +// get_string returns the `string` value argument of the flag. +// get_string returns an error if the `FlagType` is not string. +pub fn (flag Flag) get_string() ?string { + if flag.flag != .string { + return error('$flag.name: Invalid flag type `$flag.flag`, expected `string`') + } + + val := flag.get_value_or_default_value() + + if val.len == 0 { + return '' + } else { + return val[0] + } +} + +// get_strings returns the array of `string` value argument of the flag. +// get_strings returns an error if the `FlagType` is not string. +pub fn (flag Flag) get_strings() ?[]string { + if flag.flag != .string_array { + return error('$flag.name: Invalid flag type `$flag.flag`, expected `string_array`') + } + + val := flag.get_value_or_default_value() + + if val.len == 0 { + return []string{} + } else { + return val + } +} + +// get_string returns the `string` value argument of the flag specified in `name`. +// get_string returns an error if the `FlagType` is not string. +pub fn (flags []Flag) get_string(name string) ?string { + flag := flags.get(name) ? + return flag.get_string() +} + +// get_strings returns the `string` value argument of the flag specified in `name`. +// get_strings returns an error if the `FlagType` is not string. +pub fn (flags []Flag) get_strings(name string) ?[]string { + flag := flags.get(name) ? + return flag.get_strings() +} + +// parse parses flag values from arguments and return +// an array of arguments with all consumed elements removed. +fn (mut flag Flag) parse(args []string, with_abbrev bool) ?[]string { + if flag.matches(args, with_abbrev) { + if flag.flag == .bool { + new_args := flag.parse_bool(args) ? + return new_args + } else { + if flag.value.len > 0 && flag.flag != .int_array && flag.flag != .float_array + && flag.flag != .string_array { + return error('The argument `$flag.name` accept only one value!') + } + + new_args := flag.parse_raw(args) ? + return new_args + } + } else { + return args + } +} + +// matches returns `true` if first arg in `args` matches this flag. +fn (mut flag Flag) matches(args []string, with_abbrev bool) bool { + if with_abbrev { + return (flag.name != '' && args[0] == '--$flag.name') + || (flag.name != '' && args[0].starts_with('--$flag.name=')) + || (flag.abbrev != '' && args[0] == '-$flag.abbrev') + || (flag.abbrev != '' && args[0].starts_with('-$flag.abbrev=')) + } else { + return (flag.name != '' && args[0] == '-$flag.name') + || (flag.name != '' && args[0].starts_with('-$flag.name=')) + } +} + +fn (mut flag Flag) parse_raw(args []string) ?[]string { + if args[0].len > flag.name.len && args[0].contains('=') { + flag.value << args[0].split('=')[1] + return args[1..] + } else if args.len >= 2 { + flag.value << args[1] + return args[2..] + } + return error('Missing argument for `$flag.name`') +} + +fn (mut flag Flag) parse_bool(args []string) ?[]string { + if args[0].len > flag.name.len && args[0].contains('=') { + flag.value = [args[0].split('=')[1]] + return args[1..] + } else if args.len >= 2 { + if args[1] in ['true', 'false'] { + flag.value = [args[1]] + return args[2..] + } + } + // In fact bool cannot be multiple + flag.value = ['true'] + return args[1..] +} + +// get returns the `Flag` matching `name` or an error +// if it can't be found. +fn (flags []Flag) get(name string) ?Flag { + for flag in flags { + if flag.name == name { + return flag + } + } + return error('Flag `$name` not found in $flags') +} + +fn (flags []Flag) contains(name string) bool { + for flag in flags { + if flag.name == name || flag.abbrev == name { + return true + } + } + return false +} + +fn (flags []Flag) have_abbrev() bool { + mut have_abbrev := false + for flag in flags { + if flag.abbrev != '' { + have_abbrev = true + } + } + return have_abbrev +} + +// Check if value is set by command line option. If not, return default value. +fn (flag Flag) get_value_or_default_value() []string { + if flag.value.len == 0 && flag.default_value.len > 0 { + // If default value is set and no value provide, use default value. + return flag.default_value + } else { + return flag.value + } +} diff --git a/v_windows/v/old/vlib/cli/flag_test.v b/v_windows/v/old/vlib/cli/flag_test.v new file mode 100644 index 0000000..a40a7c0 --- /dev/null +++ b/v_windows/v/old/vlib/cli/flag_test.v @@ -0,0 +1,216 @@ +import cli + +fn test_if_string_flag_parses() { + mut flag := cli.Flag{ + flag: .string + name: 'flag' + } + flag.parse(['-flag', 'value1'], false) or { panic(err) } + mut value := flag.get_string() or { panic(err) } + assert value == 'value1' + + flag = cli.Flag{ + flag: .string + name: 'flag' + } + flag.parse(['-flag=value2'], false) or { panic(err) } + value = flag.get_string() or { panic(err) } + assert value == 'value2' + + flag = cli.Flag{ + flag: .string_array + name: 'flag' + } + flag.parse(['-flag=value1'], false) or { panic(err) } + flag.parse(['-flag=value2'], false) or { panic(err) } + mut values := flag.get_strings() or { panic(err) } + assert values == ['value1', 'value2'] + + flags := [ + cli.Flag{ + flag: .string_array + name: 'flag' + value: ['a', 'b', 'c'] + }, + cli.Flag{ + flag: .string + name: 'flag2' + }, + ] + + values = flags.get_strings('flag') or { panic(err) } + assert values == ['a', 'b', 'c'] +} + +fn test_if_bool_flag_parses() { + mut flag := cli.Flag{ + flag: .bool + name: 'flag' + } + mut value := false + flag.parse(['-flag'], false) or { panic(err) } + value = flag.get_bool() or { panic(err) } + assert value == true + flag.parse(['-flag', 'false'], false) or { panic(err) } + value = flag.get_bool() or { panic(err) } + assert value == false + flag.parse(['-flag', 'true'], false) or { panic(err) } + value = flag.get_bool() or { panic(err) } + assert value == true + flag.parse(['-flag=false'], false) or { panic(err) } + value = flag.get_bool() or { panic(err) } + assert value == false + flag.parse(['-flag=true'], false) or { panic(err) } + value = flag.get_bool() or { panic(err) } + assert value == true +} + +fn test_if_int_flag_parses() { + mut flag := cli.Flag{ + flag: .int + name: 'flag' + } + + mut value := 0 + flag.parse(['-flag', '42'], false) or { panic(err) } + value = flag.get_int() or { panic(err) } + assert value == 42 + + flag = cli.Flag{ + flag: .int + name: 'flag' + } + + flag.parse(['-flag=45'], false) or { panic(err) } + value = flag.get_int() or { panic(err) } + assert value == 45 + + flag = cli.Flag{ + flag: .int_array + name: 'flag' + } + + flag.parse(['-flag=42'], false) or { panic(err) } + flag.parse(['-flag=45'], false) or { panic(err) } + mut values := flag.get_ints() or { panic(err) } + assert values == [42, 45] + + flags := [ + cli.Flag{ + flag: .int_array + name: 'flag' + value: ['1', '2', '3'] + }, + cli.Flag{ + flag: .int + name: 'flag2' + }, + ] + + values = flags.get_ints('flag') or { panic(err) } + assert values == [1, 2, 3] +} + +fn test_if_float_flag_parses() { + mut flag := cli.Flag{ + flag: .float + name: 'flag' + } + mut value := f64(0) + flag.parse(['-flag', '3.14158'], false) or { panic(err) } + value = flag.get_float() or { panic(err) } + assert value == 3.14158 + + flag = cli.Flag{ + flag: .float + name: 'flag' + } + + flag.parse(['-flag=3.14159'], false) or { panic(err) } + value = flag.get_float() or { panic(err) } + assert value == 3.14159 + + flag = cli.Flag{ + flag: .float_array + name: 'flag' + } + + flag.parse(['-flag=3.1'], false) or { panic(err) } + flag.parse(['-flag=1.3'], false) or { panic(err) } + mut values := flag.get_floats() or { panic(err) } + assert values == [3.1, 1.3] + + flags := [ + cli.Flag{ + flag: .float_array + name: 'flag' + value: ['1.1', '2.2', '3.3'] + }, + cli.Flag{ + flag: .float + name: 'flag2' + }, + ] + + values = flags.get_floats('flag') or { panic(err) } + assert values == [1.1, 2.2, 3.3] +} + +fn test_if_flag_parses_with_abbrev() { + mut flag := cli.Flag{ + flag: .bool + name: 'flag' + abbrev: 'f' + } + mut value := false + flag.parse(['--flag'], true) or { panic(err) } + value = flag.get_bool() or { panic(err) } + assert value == true + + value = false + flag = cli.Flag{ + flag: .bool + name: 'flag' + abbrev: 'f' + } + flag.parse(['-f'], true) or { panic(err) } + value = flag.get_bool() or { panic(err) } + assert value == true +} + +fn test_if_multiple_value_on_single_value() { + mut flag := cli.Flag{ + flag: .float + name: 'flag' + } + + flag.parse(['-flag', '3.14158'], false) or { panic(err) } + + if _ := flag.parse(['-flag', '3.222'], false) { + panic("No multiple value flag don't raise an error!") + } else { + assert true + } +} + +fn test_default_value() { + mut flag := cli.Flag{ + flag: .float + name: 'flag' + default_value: ['1.234'] + } + + flag.parse(['-flag', '3.14158'], false) or { panic(err) } + mut value := flag.get_float() or { panic(err) } + assert value == 3.14158 + + flag = cli.Flag{ + flag: .float + name: 'flag' + default_value: ['1.234'] + } + + flag.parse([''], false) or { panic(err) } + value = flag.get_float() or { panic(err) } + assert value == 1.234 +} diff --git a/v_windows/v/old/vlib/cli/help.v b/v_windows/v/old/vlib/cli/help.v new file mode 100644 index 0000000..6ce3e41 --- /dev/null +++ b/v_windows/v/old/vlib/cli/help.v @@ -0,0 +1,172 @@ +module cli + +import term +import strings + +const ( + base_indent_len = 2 + min_description_indent_len = 20 + spacing = 2 +) + +fn help_flag(with_abbrev bool) Flag { + sabbrev := if with_abbrev { 'h' } else { '' } + return Flag{ + flag: .bool + name: 'help' + abbrev: sabbrev + description: 'Prints help information.' + } +} + +fn help_cmd() Command { + return Command{ + name: 'help' + usage: '' + description: 'Prints help information.' + execute: print_help_for_command + } +} + +fn print_help_for_command(help_cmd Command) ? { + if help_cmd.args.len > 0 { + mut cmd := help_cmd.parent + for arg in help_cmd.args { + mut found := false + for sub_cmd in cmd.commands { + if sub_cmd.name == arg { + cmd = unsafe { &sub_cmd } + found = true + break + } + } + if !found { + args := help_cmd.args.join(' ') + println('Invalid command: $args') + return + } + } + print(cmd.help_message()) + } else { + if help_cmd.parent != 0 { + print(help_cmd.parent.help_message()) + } + } +} + +fn (cmd Command) help_message() string { + mut help := '' + help += 'Usage: $cmd.full_name()' + if cmd.flags.len > 0 { + help += ' [flags]' + } + if cmd.commands.len > 0 { + help += ' [commands]' + } + if cmd.usage.len > 0 { + help += ' $cmd.usage' + } else { + for i in 0 .. cmd.required_args { + help += ' ' + } + } + help += '\n' + if cmd.description != '' { + help += '\n$cmd.description\n' + } + mut abbrev_len := 0 + mut name_len := cli.min_description_indent_len + if cmd.flags.have_abbrev() { + for flag in cmd.flags { + abbrev_len = max(abbrev_len, flag.abbrev.len + cli.spacing + 1) // + 1 for '-' in front + name_len = max(name_len, abbrev_len + flag.name.len + cli.spacing + 2) // + 2 for '--' in front + } + for command in cmd.commands { + name_len = max(name_len, command.name.len + cli.spacing) + } + } else { + for flag in cmd.flags { + name_len = max(name_len, abbrev_len + flag.name.len + cli.spacing + 1) // + 1 for '-' in front + } + for command in cmd.commands { + name_len = max(name_len, command.name.len + cli.spacing) + } + } + if cmd.flags.len > 0 { + help += '\nFlags:\n' + for flag in cmd.flags { + mut flag_name := '' + if flag.abbrev != '' && cmd.flags.have_abbrev() { + abbrev_indent := ' '.repeat(abbrev_len - flag.abbrev.len - 1) // - 1 for '-' in front + flag_name = '-$flag.abbrev$abbrev_indent--$flag.name' + } else if cmd.flags.have_abbrev() { + abbrev_indent := ' '.repeat(abbrev_len) + flag_name = '$abbrev_indent--$flag.name' + } else { + flag_name = '-$flag.name' + } + mut required := '' + if flag.required { + required = ' (required)' + } + base_indent := ' '.repeat(cli.base_indent_len) + description_indent := ' '.repeat(name_len - flag_name.len) + help += '$base_indent$flag_name$description_indent' + + pretty_description(flag.description + required, cli.base_indent_len + name_len) + + '\n' + } + } + if cmd.commands.len > 0 { + help += '\nCommands:\n' + for command in cmd.commands { + base_indent := ' '.repeat(cli.base_indent_len) + description_indent := ' '.repeat(name_len - command.name.len) + help += '$base_indent$command.name$description_indent' + + pretty_description(command.description, name_len) + '\n' + } + } + return help +} + +// pretty_description resizes description text depending on terminal width. +// Essentially, smart wrap-around +fn pretty_description(s string, indent_len int) string { + width, _ := term.get_terminal_size() + // Don't prettify if the terminal is that small, it won't be pretty anyway. + if indent_len > width { + return s + } + indent := ' '.repeat(indent_len) + chars_per_line := width - indent_len + // Give us enough room, better a little bigger than smaller + mut acc := strings.new_builder(((s.len / chars_per_line) + 1) * (width + 1)) + for k, line in s.split('\n') { + if k != 0 { + acc.write_string('\n$indent') + } + mut i := chars_per_line - 2 + mut j := 0 + for ; i < line.len; i += chars_per_line - 2 { + for line[i] != ` ` { + i-- + } + // indent was already done the first iteration + if j != 0 { + acc.write_string(indent) + } + acc.writeln(line[j..i].trim_space()) + j = i + } + // We need this even though it should never happen + if j != 0 { + acc.write_string(indent) + } + acc.write_string(line[j..].trim_space()) + } + return acc.str() +} + +fn max(a int, b int) int { + res := if a > b { a } else { b } + return res +} diff --git a/v_windows/v/old/vlib/cli/version.v b/v_windows/v/old/vlib/cli/version.v new file mode 100644 index 0000000..0f3f583 --- /dev/null +++ b/v_windows/v/old/vlib/cli/version.v @@ -0,0 +1,25 @@ +module cli + +fn version_flag(with_abbrev bool) Flag { + sabbrev := if with_abbrev { 'v' } else { '' } + return Flag{ + flag: .bool + name: 'version' + abbrev: sabbrev + description: 'Prints version information.' + } +} + +fn version_cmd() Command { + return Command{ + name: 'version' + description: 'Prints version information.' + execute: version_func + } +} + +fn version_func(version_cmd Command) ? { + cmd := version_cmd.parent + version := '$cmd.name version $cmd.version' + println(version) +} diff --git a/v_windows/v/old/vlib/clipboard/clipboard.v b/v_windows/v/old/vlib/clipboard/clipboard.v new file mode 100644 index 0000000..d82289b --- /dev/null +++ b/v_windows/v/old/vlib/clipboard/clipboard.v @@ -0,0 +1,37 @@ +module clipboard + +// new returns a new `Clipboard` instance allocated on the heap. +// The `Clipboard` resources can be released with `free()` +pub fn new() &Clipboard { + return new_clipboard() +} + +// copy copies `text` into the clipboard. +pub fn (mut cb Clipboard) copy(text string) bool { + return cb.set_text(text) +} + +// paste returns current entry as a `string` from the clipboard. +pub fn (mut cb Clipboard) paste() string { + return cb.get_text() +} + +// clear_all clears the clipboard. +pub fn (mut cb Clipboard) clear_all() { + cb.clear() +} + +// destroy destroys the clipboard and free it's resources. +pub fn (mut cb Clipboard) destroy() { + cb.free() +} + +// check_ownership returns `true` if the `Clipboard` has the content ownership. +pub fn (cb Clipboard) check_ownership() bool { + return cb.has_ownership() +} + +// is_available returns `true` if the clipboard is available for use. +pub fn (cb &Clipboard) is_available() bool { + return cb.check_availability() +} diff --git a/v_windows/v/old/vlib/clipboard/clipboard_android.c.v b/v_windows/v/old/vlib/clipboard/clipboard_android.c.v new file mode 100644 index 0000000..eb1e6b8 --- /dev/null +++ b/v_windows/v/old/vlib/clipboard/clipboard_android.c.v @@ -0,0 +1,15 @@ +module clipboard + +import clipboard.dummy + +pub type Clipboard = dummy.Clipboard + +fn new_clipboard() &Clipboard { + return dummy.new_clipboard() +} + +// new_primary returns a new X11 `PRIMARY` type `Clipboard` instance allocated on the heap. +// Please note: new_primary only works on X11 based systems. +pub fn new_primary() &Clipboard { + return dummy.new_primary() +} diff --git a/v_windows/v/old/vlib/clipboard/clipboard_darwin.c.v b/v_windows/v/old/vlib/clipboard/clipboard_darwin.c.v new file mode 100644 index 0000000..eff5d3b --- /dev/null +++ b/v_windows/v/old/vlib/clipboard/clipboard_darwin.c.v @@ -0,0 +1,70 @@ +module clipboard + +#include +#include +#flag -framework Cocoa +#include "@VEXEROOT/vlib/clipboard/clipboard_darwin.m" + +pub struct Clipboard { + pb voidptr + last_cb_serial i64 +mut: + foo int // TODO remove, for mut hack +} + +fn C.darwin_new_pasteboard() voidptr + +fn C.darwin_get_pasteboard_text(voidptr) &byte + +fn C.darwin_set_pasteboard_text(voidptr, string) bool + +fn new_clipboard() &Clipboard { + cb := &Clipboard{ + pb: C.darwin_new_pasteboard() // pb + } + return cb +} + +pub fn (cb &Clipboard) check_availability() bool { + return cb.pb != C.NULL +} + +pub fn (mut cb Clipboard) clear() { + cb.foo = 0 + cb.set_text('') + //#[cb->pb clearContents]; +} + +pub fn (mut cb Clipboard) free() { + cb.foo = 0 + // nothing to free +} + +pub fn (cb &Clipboard) has_ownership() bool { + if cb.last_cb_serial == 0 { + return false + } + //#return [cb->pb changeCount] == cb->last_cb_serial; + return false +} + +fn C.OSAtomicCompareAndSwapLong() + +pub fn (mut cb Clipboard) set_text(text string) bool { + return C.darwin_set_pasteboard_text(cb.pb, text) +} + +pub fn (mut cb Clipboard) get_text() string { + cb.foo = 0 + if isnil(cb.pb) { + return '' + } + utf8_clip := C.darwin_get_pasteboard_text(cb.pb) + return unsafe { utf8_clip.vstring() } +} + +// new_primary returns a new X11 `PRIMARY` type `Clipboard` instance allocated on the heap. +// Please note: new_primary only works on X11 based systems. +pub fn new_primary() &Clipboard { + panic('Primary clipboard is not supported on non-Linux systems.') +} diff --git a/v_windows/v/old/vlib/clipboard/clipboard_darwin.m b/v_windows/v/old/vlib/clipboard/clipboard_darwin.m new file mode 100644 index 0000000..cabc744 --- /dev/null +++ b/v_windows/v/old/vlib/clipboard/clipboard_darwin.m @@ -0,0 +1,23 @@ +//NSPasteboard* darwin_new_pasteboard() { +void* darwin_new_pasteboard() { + return (__bridge void*) [NSPasteboard generalPasteboard]; +} + +char* darwin_get_pasteboard_text(void* pb) { + NSString *ns_clip = [((__bridge NSPasteboard*)pb) stringForType:NSStringPboardType]; //NSPasteboardTypeString + if (ns_clip == nil) { + return ""; + } + return [ns_clip UTF8String]; +} + +bool darwin_set_pasteboard_text(void* _pb, string text) { + NSPasteboard* pb = (__bridge NSPasteboard*) _pb; + NSString *ns_clip = [[ NSString alloc ] initWithBytesNoCopy:text.str length:text.len encoding:NSUTF8StringEncoding freeWhenDone: false]; + [pb declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil]; + bool ret = [pb setString:ns_clip forType:NSStringPboardType]; + //[ns_clip release]; + int serial = [pb changeCount]; + //OSAtomicCompareAndSwapLong(cb.last_cb_serial, serial, &cb.last_cb_serial); + return ret; +} diff --git a/v_windows/v/old/vlib/clipboard/clipboard_default.c.v b/v_windows/v/old/vlib/clipboard/clipboard_default.c.v new file mode 100644 index 0000000..d8f65ce --- /dev/null +++ b/v_windows/v/old/vlib/clipboard/clipboard_default.c.v @@ -0,0 +1,15 @@ +module clipboard + +import clipboard.x11 + +pub type Clipboard = x11.Clipboard + +fn new_clipboard() &Clipboard { + return x11.new_clipboard() +} + +// new_primary returns a new X11 `PRIMARY` type `Clipboard` instance allocated on the heap. +// Please note: new_primary only works on X11 based systems. +pub fn new_primary() &Clipboard { + return x11.new_primary() +} diff --git a/v_windows/v/old/vlib/clipboard/clipboard_solaris.c.v b/v_windows/v/old/vlib/clipboard/clipboard_solaris.c.v new file mode 100644 index 0000000..eb1e6b8 --- /dev/null +++ b/v_windows/v/old/vlib/clipboard/clipboard_solaris.c.v @@ -0,0 +1,15 @@ +module clipboard + +import clipboard.dummy + +pub type Clipboard = dummy.Clipboard + +fn new_clipboard() &Clipboard { + return dummy.new_clipboard() +} + +// new_primary returns a new X11 `PRIMARY` type `Clipboard` instance allocated on the heap. +// Please note: new_primary only works on X11 based systems. +pub fn new_primary() &Clipboard { + return dummy.new_primary() +} diff --git a/v_windows/v/old/vlib/clipboard/clipboard_test.v b/v_windows/v/old/vlib/clipboard/clipboard_test.v new file mode 100644 index 0000000..6c97f44 --- /dev/null +++ b/v_windows/v/old/vlib/clipboard/clipboard_test.v @@ -0,0 +1,29 @@ +import clipboard + +fn run_test(is_primary bool) { + mut cb := if is_primary { clipboard.new_primary() } else { clipboard.new() } + if !cb.is_available() { + return + } + assert cb.check_ownership() == false + assert cb.copy('I am a good boy!') == true + // assert cb.check_ownership() == true TODO + assert cb.paste() == 'I am a good boy!' + cb.clear_all() + assert cb.paste().len <= 0 + cb.destroy() +} + +fn test_primary() { + $if linux || freebsd { + // run_test(true) + return + } +} + +fn test_clipboard() { + $if linux || freebsd { + return + } + run_test(false) +} diff --git a/v_windows/v/old/vlib/clipboard/clipboard_windows.c.v b/v_windows/v/old/vlib/clipboard/clipboard_windows.c.v new file mode 100644 index 0000000..9ae42d1 --- /dev/null +++ b/v_windows/v/old/vlib/clipboard/clipboard_windows.c.v @@ -0,0 +1,186 @@ +module clipboard + +import time + +#include +#flag -luser32 + +struct WndClassEx { + cb_size u32 + style u32 + lpfn_wnd_proc voidptr + cb_cls_extra int + cb_wnd_extra int + h_instance C.HINSTANCE + h_icon C.HICON + h_cursor C.HCURSOR + hbr_background C.HBRUSH + lpsz_menu_name &u16 // LPCWSTR + lpsz_class_name &u16 + h_icon_sm &u16 +} + +fn C.RegisterClassEx(class &WndClassEx) int + +fn C.GetClipboardOwner() &C.HWND + +fn C.CreateWindowEx(dwExStyle i64, lpClassName &u16, lpWindowName &u16, dwStyle i64, x int, y int, nWidth int, nHeight int, hWndParent i64, hMenu voidptr, h_instance voidptr, lpParam voidptr) &C.HWND + +// fn C.MultiByteToWideChar(CodePage u32, dw_flags u16, lpMultiByteStr byteptr, cbMultiByte int, lpWideCharStr u16, cchWideChar int) int +fn C.EmptyClipboard() + +fn C.CloseClipboard() + +fn C.GlobalAlloc(uFlag u32, size i64) C.HGLOBAL + +fn C.GlobalFree(buf C.HGLOBAL) + +fn C.GlobalLock(buf C.HGLOBAL) voidptr + +fn C.GlobalUnlock(buf C.HGLOBAL) bool + +fn C.SetClipboardData(uFormat u32, data voidptr) C.HANDLE + +fn C.GetClipboardData(uFormat u32) C.HANDLE + +fn C.DefWindowProc(hwnd C.HWND, msg u32, wParam C.WPARAM, lParam C.LPARAM) C.LRESULT + +fn C.SetLastError(error i64) + +fn C.OpenClipboard(hwnd C.HWND) int + +fn C.DestroyWindow(hwnd C.HWND) + +struct Clipboard { + max_retries int + retry_delay int +mut: + hwnd C.HWND + foo int // TODO remove +} + +fn (cb &Clipboard) get_clipboard_lock() bool { + mut retries := cb.max_retries + mut last_error := u32(0) + for { + retries-- + if retries < 0 { + break + } + last_error = C.GetLastError() + if C.OpenClipboard(cb.hwnd) > 0 { + return true + } else if last_error != u32(C.ERROR_ACCESS_DENIED) { + return false + } + time.sleep(cb.retry_delay * time.second) + } + C.SetLastError(last_error) + return false +} + +fn new_clipboard() &Clipboard { + mut cb := &Clipboard{ + max_retries: 5 + retry_delay: 5 + } + class_name := 'clipboard' + wndclass := WndClassEx{ + cb_size: sizeof(WndClassEx) + lpfn_wnd_proc: voidptr(&C.DefWindowProc) + lpsz_class_name: class_name.to_wide() + lpsz_menu_name: 0 + h_icon_sm: 0 + } + if C.RegisterClassEx(&wndclass) == 0 && C.GetLastError() != u32(C.ERROR_CLASS_ALREADY_EXISTS) { + println('Failed registering class.') + } + hwnd := C.CreateWindowEx(0, wndclass.lpsz_class_name, wndclass.lpsz_class_name, 0, + 0, 0, 0, 0, C.HWND_MESSAGE, C.NULL, C.NULL, C.NULL) + if hwnd == C.NULL { + println('Error creating window!') + } + cb.hwnd = hwnd + return cb +} + +pub fn (cb &Clipboard) check_availability() bool { + return cb.hwnd != C.HWND(C.NULL) +} + +pub fn (cb &Clipboard) has_ownership() bool { + return C.GetClipboardOwner() == cb.hwnd +} + +pub fn (mut cb Clipboard) clear() { + if !cb.get_clipboard_lock() { + return + } + C.EmptyClipboard() + C.CloseClipboard() + cb.foo = 0 +} + +pub fn (mut cb Clipboard) free() { + C.DestroyWindow(cb.hwnd) + cb.foo = 0 +} + +// the string.to_wide doesn't work with SetClipboardData, don't know why +fn to_wide(text string) C.HGLOBAL { + len_required := C.MultiByteToWideChar(C.CP_UTF8, C.MB_ERR_INVALID_CHARS, text.str, + text.len + 1, C.NULL, 0) + buf := C.GlobalAlloc(C.GMEM_MOVEABLE, i64(sizeof(u16)) * len_required) + if buf != C.HGLOBAL(C.NULL) { + mut locked := &u16(C.GlobalLock(buf)) + C.MultiByteToWideChar(C.CP_UTF8, C.MB_ERR_INVALID_CHARS, text.str, text.len + 1, + locked, len_required) + unsafe { + locked[len_required - 1] = u16(0) + } + C.GlobalUnlock(buf) + } + return buf +} + +pub fn (mut cb Clipboard) set_text(text string) bool { + cb.foo = 0 + buf := to_wide(text) + if !cb.get_clipboard_lock() { + C.GlobalFree(buf) + return false + } else { + // EmptyClipboard must be called to properly update clipboard ownership + C.EmptyClipboard() + if C.SetClipboardData(C.CF_UNICODETEXT, buf) == C.HANDLE(C.NULL) { + println('SetClipboardData: Failed.') + C.CloseClipboard() + C.GlobalFree(buf) + return false + } + } + // CloseClipboard appears to change the sequence number... + C.CloseClipboard() + return true +} + +pub fn (mut cb Clipboard) get_text() string { + cb.foo = 0 + if !cb.get_clipboard_lock() { + return '' + } + h_data := C.GetClipboardData(C.CF_UNICODETEXT) + if h_data == C.HANDLE(C.NULL) { + C.CloseClipboard() + return '' + } + str := unsafe { string_from_wide(&u16(C.GlobalLock(C.HGLOBAL(h_data)))) } + C.GlobalUnlock(C.HGLOBAL(h_data)) + return str +} + +// new_primary returns a new X11 `PRIMARY` type `Clipboard` instance allocated on the heap. +// Please note: new_primary only works on X11 based systems. +pub fn new_primary() &Clipboard { + panic('Primary clipboard is not supported on non-Linux systems.') +} diff --git a/v_windows/v/old/vlib/clipboard/dummy/dummy_clipboard.v b/v_windows/v/old/vlib/clipboard/dummy/dummy_clipboard.v new file mode 100644 index 0000000..a3f4f35 --- /dev/null +++ b/v_windows/v/old/vlib/clipboard/dummy/dummy_clipboard.v @@ -0,0 +1,49 @@ +module dummy + +pub struct Clipboard { +mut: + text string // text data sent or received + got_text bool // used to confirm that we have got the text + is_owner bool // to save selection owner state +} + +// new_clipboard returns a new `Clipboard` instance allocated on the heap. +// The `Clipboard` resources can be released with `free()` +pub fn new_clipboard() &Clipboard { + return &Clipboard{} +} + +// new_primary returns a new X11 `PRIMARY` type `Clipboard` instance allocated on the heap. +// Please note: new_primary only works on X11 based systems. +pub fn new_primary() &Clipboard { + return &Clipboard{} +} + +pub fn (mut cb Clipboard) set_text(text string) bool { + cb.text = text + cb.is_owner = true + cb.got_text = true + return true +} + +pub fn (mut cb Clipboard) get_text() string { + return cb.text +} + +pub fn (mut cb Clipboard) clear() { + cb.text = '' + cb.is_owner = false +} + +pub fn (mut cb Clipboard) free() { +} + +pub fn (cb &Clipboard) has_ownership() bool { + return cb.is_owner +} + +pub fn (cb &Clipboard) check_availability() bool { + // This is a dummy clipboard implementation, + // which can be always used, although it does not do much... + return true +} diff --git a/v_windows/v/old/vlib/clipboard/x11/clipboard.c.v b/v_windows/v/old/vlib/clipboard/x11/clipboard.c.v new file mode 100644 index 0000000..d5a4656 --- /dev/null +++ b/v_windows/v/old/vlib/clipboard/x11/clipboard.c.v @@ -0,0 +1,501 @@ +// Currently there is only X11 Selections support and no way to handle Wayland +// but since Wayland isn't extremely adopted, we are covering almost all Linux distros. +module x11 + +import time +import sync +import math + +$if freebsd { + #flag -I/usr/local/include + #flag -L/usr/local/lib +} $else $if openbsd { + #flag -I/usr/X11R6/include + #flag -L/usr/X11R6/lib +} +#flag -lX11 + +#include # Please install a package with the X11 development headers, for example: `apt-get install libx11-dev` +// X11 +[typedef] +struct C.Display { +} + +type Window = u64 +type Atom = u64 + +fn C.XInitThreads() int + +fn C.XCloseDisplay(d &C.Display) + +fn C.XFlush(d &C.Display) + +fn C.XDestroyWindow(d &C.Display, w Window) + +fn C.XNextEvent(d &C.Display, e &C.XEvent) + +fn C.XSetSelectionOwner(d &C.Display, a Atom, w Window, time int) + +fn C.XGetSelectionOwner(d &C.Display, a Atom) Window + +fn C.XChangeProperty(d &C.Display, requestor Window, property Atom, typ Atom, format int, mode int, data voidptr, nelements int) int + +fn C.XSendEvent(d &C.Display, requestor Window, propogate int, mask i64, event &C.XEvent) + +fn C.XInternAtom(d &C.Display, typ &byte, only_if_exists int) Atom + +fn C.XCreateSimpleWindow(d &C.Display, root Window, x int, y int, width u32, height u32, border_width u32, border u64, background u64) Window + +fn C.XOpenDisplay(name &byte) &C.Display + +fn C.XConvertSelection(d &C.Display, selection Atom, target Atom, property Atom, requestor Window, time int) int + +fn C.XSync(d &C.Display, discard int) int + +fn C.XGetWindowProperty(d &C.Display, w Window, property Atom, offset i64, length i64, delete int, req_type Atom, actual_type_return &Atom, actual_format_return &int, nitems &u64, bytes_after_return &u64, prop_return &&byte) int + +fn C.XDeleteProperty(d &C.Display, w Window, property Atom) int + +fn C.DefaultScreen(display &C.Display) int + +fn C.RootWindow(display &C.Display, screen_number int) Window + +fn C.BlackPixel(display &C.Display, screen_number int) u32 + +fn C.WhitePixel(display &C.Display, screen_number int) u32 + +fn C.XFree(data voidptr) + +fn todo_del() {} + +[typedef] +struct C.XSelectionRequestEvent { +mut: + display &C.Display // Display the event was read from + owner Window + requestor Window + selection Atom + target Atom + property Atom + time int +} + +[typedef] +struct C.XSelectionEvent { +mut: + @type int + display &C.Display // Display the event was read from + requestor Window + selection Atom + target Atom + property Atom + time int +} + +[typedef] +struct C.XSelectionClearEvent { +mut: + window Window + selection Atom +} + +[typedef] +struct C.XDestroyWindowEvent { +mut: + window Window +} + +[typedef] +union C.XEvent { +mut: + @type int + xdestroywindow C.XDestroyWindowEvent + xselectionclear C.XSelectionClearEvent + xselectionrequest C.XSelectionRequestEvent + xselection C.XSelectionEvent +} + +const ( + atom_names = ['TARGETS', 'CLIPBOARD', 'PRIMARY', 'SECONDARY', 'TEXT', 'UTF8_STRING', 'text/plain', + 'text/html', + ] +) + +// UNSUPPORTED TYPES: MULTIPLE, INCR, TIMESTAMP, image/bmp, image/jpeg, image/tiff, image/png +// all the atom types we need +// currently we only support text +// in the future, maybe we can extend this +// to support other mime types +enum AtomType { + xa_atom = 0 // value 4 + xa_string = 1 // value 31 + targets = 2 + clipboard = 3 + primary = 4 + secondary = 5 + text = 6 + utf8_string = 7 + text_plain = 8 + text_html = 9 +} + +pub struct Clipboard { + display &C.Display +mut: + selection Atom // the selection atom + window Window + atoms []Atom + mutex &sync.Mutex + text string // text data sent or received + got_text bool // used to confirm that we have got the text + is_owner bool // to save selection owner state +} + +struct Property { + actual_type Atom + actual_format int + nitems u64 + data &byte +} + +// new_clipboard returns a new `Clipboard` instance allocated on the heap. +// The `Clipboard` resources can be released with `free()` +pub fn new_clipboard() &Clipboard { + return new_x11_clipboard(.clipboard) +} + +// new_x11_clipboard initializes a new clipboard of the given selection type. +// Multiple clipboard instance types can be initialized and used separately. +fn new_x11_clipboard(selection AtomType) &Clipboard { + if selection !in [.clipboard, .primary, .secondary] { + panic('Wrong AtomType. Must be one of .primary, .secondary or .clipboard.') + } + // init x11 thread support + status := C.XInitThreads() + if status == 0 { + println('WARN: this system does not support threads; clipboard will cause the program to lock.') + } + + display := new_display() + + if display == C.NULL { + println('ERROR: No X Server running. Clipboard cannot be used.') + return &Clipboard{ + display: 0 + mutex: sync.new_mutex() + } + } + + mut cb := &Clipboard{ + display: display + window: create_xwindow(display) + mutex: sync.new_mutex() + } + cb.intern_atoms() + cb.selection = cb.get_atom(selection) + // start the listener on another thread or + // we will be locked and will have to hard exit + go cb.start_listener() + return cb +} + +pub fn (cb &Clipboard) check_availability() bool { + return cb.display != C.NULL +} + +pub fn (mut cb Clipboard) free() { + C.XDestroyWindow(cb.display, cb.window) + cb.window = Window(0) + // FIX ME: program hangs when closing display + // XCloseDisplay(cb.display) +} + +pub fn (mut cb Clipboard) clear() { + cb.mutex.@lock() + C.XSetSelectionOwner(cb.display, cb.selection, Window(0), C.CurrentTime) + C.XFlush(cb.display) + cb.is_owner = false + cb.text = '' + cb.mutex.unlock() +} + +pub fn (cb &Clipboard) has_ownership() bool { + return cb.is_owner +} + +fn (cb &Clipboard) take_ownership() { + C.XSetSelectionOwner(cb.display, cb.selection, cb.window, C.CurrentTime) + C.XFlush(cb.display) +} + +// set_text stores `text` in the system clipboard. +pub fn (mut cb Clipboard) set_text(text string) bool { + if cb.window == Window(0) { + return false + } + cb.mutex.@lock() + cb.text = text + cb.is_owner = true + cb.take_ownership() + C.XFlush(cb.display) + cb.mutex.unlock() + // sleep a little bit + time.sleep(1 * time.millisecond) + return cb.is_owner +} + +pub fn (mut cb Clipboard) get_text() string { + if cb.window == Window(0) { + return '' + } + if cb.is_owner { + return cb.text + } + cb.got_text = false + + // Request a list of possible conversions, if we're pasting. + C.XConvertSelection(cb.display, cb.selection, cb.get_atom(.targets), cb.selection, + cb.window, C.CurrentTime) + + // wait for the text to arrive + mut retries := 5 + for { + if cb.got_text || retries == 0 { + break + } + time.sleep(50 * time.millisecond) + retries-- + } + return cb.text +} + +// transmit_selection is crucial to handling all the different data types. +// If we ever support other mimetypes they should be handled here. +fn (mut cb Clipboard) transmit_selection(xse &C.XSelectionEvent) bool { + if xse.target == cb.get_atom(.targets) { + targets := cb.get_supported_targets() + C.XChangeProperty(xse.display, xse.requestor, xse.property, cb.get_atom(.xa_atom), + 32, C.PropModeReplace, targets.data, targets.len) + } else if cb.is_supported_target(xse.target) && cb.is_owner && cb.text != '' { + cb.mutex.@lock() + C.XChangeProperty(xse.display, xse.requestor, xse.property, xse.target, 8, C.PropModeReplace, + cb.text.str, cb.text.len) + cb.mutex.unlock() + } else { + return false + } + return true +} + +fn (mut cb Clipboard) start_listener() { + event := C.XEvent{} + mut sent_request := false + mut to_be_requested := Atom(0) + for { + C.XNextEvent(cb.display, &event) + if unsafe { event.@type == 0 } { + println('error') + continue + } + match unsafe { event.@type } { + C.DestroyNotify { + if unsafe { event.xdestroywindow.window == cb.window } { + // we are done + return + } + } + C.SelectionClear { + if unsafe { event.xselectionclear.window == cb.window } && unsafe { + event.xselectionclear.selection == cb.selection + } { + cb.mutex.@lock() + cb.is_owner = false + cb.text = '' + cb.mutex.unlock() + } + } + C.SelectionRequest { + if unsafe { event.xselectionrequest.selection == cb.selection } { + mut xsre := &C.XSelectionRequestEvent{ + display: 0 + } + xsre = unsafe { &event.xselectionrequest } + + mut xse := C.XSelectionEvent{ + @type: C.SelectionNotify // 31 + display: xsre.display + requestor: xsre.requestor + selection: xsre.selection + time: xsre.time + target: xsre.target + property: xsre.property + } + if !cb.transmit_selection(&xse) { + xse.property = new_atom(0) + } + C.XSendEvent(cb.display, xse.requestor, 0, C.PropertyChangeMask, voidptr(&xse)) + C.XFlush(cb.display) + } + } + C.SelectionNotify { + if unsafe { + event.xselection.selection == cb.selection + && event.xselection.property != Atom(0) + } { + if unsafe { event.xselection.target == cb.get_atom(.targets) && !sent_request } { + sent_request = true + prop := read_property(cb.display, cb.window, cb.selection) + to_be_requested = cb.pick_target(prop) + if to_be_requested != Atom(0) { + C.XConvertSelection(cb.display, cb.selection, to_be_requested, + cb.selection, cb.window, C.CurrentTime) + } + } else if unsafe { event.xselection.target == to_be_requested } { + sent_request = false + to_be_requested = Atom(0) + cb.mutex.@lock() + prop := unsafe { + read_property(event.xselection.display, event.xselection.requestor, + event.xselection.property) + } + unsafe { + C.XDeleteProperty(event.xselection.display, event.xselection.requestor, + event.xselection.property) + } + if cb.is_supported_target(prop.actual_type) { + cb.got_text = true + unsafe { + cb.text = prop.data.vstring() // TODO: return byteptr to support other mimetypes + } + } + cb.mutex.unlock() + } + } + } + C.PropertyNotify {} + else {} + } + } +} + +/* +* Helpers +*/ +// intern_atoms initializes all the atoms we need. +fn (mut cb Clipboard) intern_atoms() { + cb.atoms << Atom(4) // XA_ATOM + cb.atoms << Atom(31) // XA_STRING + for i, name in x11.atom_names { + only_if_exists := if i == int(AtomType.utf8_string) { 1 } else { 0 } + cb.atoms << C.XInternAtom(cb.display, &char(name.str), only_if_exists) + if i == int(AtomType.utf8_string) && cb.atoms[i] == Atom(0) { + cb.atoms[i] = cb.get_atom(.xa_string) + } + } +} + +fn read_property(d &C.Display, w Window, p Atom) Property { + actual_type := Atom(0) + actual_format := 0 + nitems := u64(0) + bytes_after := u64(0) + ret := &byte(0) + mut read_bytes := 1024 + for { + if ret != 0 { + C.XFree(ret) + } + C.XGetWindowProperty(d, w, p, 0, read_bytes, 0, 0, &actual_type, &actual_format, + &nitems, &bytes_after, &ret) + read_bytes *= 2 + if bytes_after == 0 { + break + } + } + return Property{actual_type, actual_format, nitems, ret} +} + +// pick_target finds the best target given a local copy of a property. +fn (cb &Clipboard) pick_target(prop Property) Atom { + // The list of targets is a list of atoms, so it should have type XA_ATOM + // but it may have the type TARGETS instead. + if (prop.actual_type != cb.get_atom(.xa_atom) && prop.actual_type != cb.get_atom(.targets)) + || prop.actual_format != 32 { + // This would be really broken. Targets have to be an atom list + // and applications should support this. Nevertheless, some + // seem broken (MATLAB 7, for instance), so ask for STRING + // next instead as the lowest common denominator + return cb.get_atom(.xa_string) + } else { + atom_list := &Atom(voidptr(prop.data)) + + mut to_be_requested := Atom(0) + + // This is higher than the maximum priority. + mut priority := math.max_i32 + + for i in 0 .. prop.nitems { + // See if this data type is allowed and of higher priority (closer to zero) + // than the present one. + + target := unsafe { atom_list[i] } + if cb.is_supported_target(target) { + index := cb.get_target_index(target) + if priority > index && index >= 0 { + priority = index + to_be_requested = target + } + } + } + return to_be_requested + } +} + +fn (cb &Clipboard) get_atoms(types ...AtomType) []Atom { + mut atoms := []Atom{} + for typ in types { + atoms << cb.atoms[typ] + } + return atoms +} + +fn (cb &Clipboard) get_atom(typ AtomType) Atom { + return cb.atoms[typ] +} + +fn (cb &Clipboard) is_supported_target(target Atom) bool { + return cb.get_target_index(target) >= 0 +} + +fn (cb &Clipboard) get_target_index(target Atom) int { + for i, atom in cb.get_supported_targets() { + if atom == target { + return i + } + } + return -1 +} + +fn (cb &Clipboard) get_supported_targets() []Atom { + return cb.get_atoms(AtomType.utf8_string, .xa_string, .text, .text_plain, .text_html) +} + +fn new_atom(value int) &Atom { + return unsafe { &Atom(&u64(u64(value))) } +} + +fn create_xwindow(display &C.Display) Window { + n := C.DefaultScreen(display) + return C.XCreateSimpleWindow(display, C.RootWindow(display, n), 0, 0, 1, 1, 0, C.BlackPixel(display, + n), C.WhitePixel(display, n)) +} + +fn new_display() &C.Display { + return C.XOpenDisplay(C.NULL) +} + +// new_primary returns a new X11 `PRIMARY` type `Clipboard` instance allocated on the heap. +// Please note: new_primary only works on X11 based systems. +pub fn new_primary() &Clipboard { + return new_x11_clipboard(.primary) +} diff --git a/v_windows/v/old/vlib/context/README.md b/v_windows/v/old/vlib/context/README.md new file mode 100644 index 0000000..7fbf18b --- /dev/null +++ b/v_windows/v/old/vlib/context/README.md @@ -0,0 +1,166 @@ +# Context + +This module defines the Context type, which carries deadlines, cancellation signals, +and other request-scoped values across API boundaries and between processes. + +Incoming requests to a server should create a Context, and outgoing calls to servers +should accept a Context. The chain of function calls between them must propagate the +Context, optionally replacing it with a derived Context created using with_cancel, +with_deadline, with_timeout, or with_value. When a Context is canceled, all Contexts +derived from it are also canceled. + +The with_cancel, with_deadline, and with_timeout functions take a Context (the parent) +and return a derived Context (the child). Calling the cancel function +cancels the child and its children, removes the parent's reference to the child, +and stops any associated timers. + +Programs that use Contexts should follow these rules to keep interfaces consistent +across different modules. + +Do not store Contexts inside a struct type; instead, pass a Context explicitly +to each function that needs it. The Context should be the first parameter, +typically named ctx, just to make it more consistent. + +## Examples + +In this section you can see some usage examples for this module + +### Context With Cancellation + +```v +import context + +// This example demonstrates the use of a cancelable context to prevent a +// routine leak. By the end of the example function, the routine started +// by gen will return without leaking. +fn example_with_cancel() { + // gen generates integers in a separate routine and + // sends them to the returned channel. + // The callers of gen need to cancel the context once + // they are done consuming generated integers not to leak + // the internal routine started by gen. + gen := fn (ctx context.Context) chan int { + dst := chan int{} + go fn (ctx context.Context, dst chan int) { + mut v := 0 + ch := ctx.done() + for { + select { + _ := <-ch { + // returning not to leak the routine + return + } + dst <- v { + v++ + } + } + } + }(ctx, dst) + return dst + } + + ctx := context.with_cancel(context.background()) + defer { + context.cancel(ctx) + } + + ch := gen(ctx) + for i in 0 .. 5 { + v := <-ch + assert i == v + } +} +``` + +### Context With Deadline + +```v +import context +import time + +const ( + // a reasonable duration to block in an example + short_duration = 1 * time.millisecond +) + +// This example passes a context with an arbitrary deadline to tell a blocking +// function that it should abandon its work as soon as it gets to it. +fn example_with_deadline() { + dur := time.now().add(short_duration) + ctx := context.with_deadline(context.background(), dur) + + defer { + // Even though ctx will be expired, it is good practice to call its + // cancellation function in any case. Failure to do so may keep the + // context and its parent alive longer than necessary. + context.cancel(ctx) + } + + ctx_ch := ctx.done() + select { + _ := <-ctx_ch {} + 1 * time.second { + panic('This should not happen') + } + } +} +``` + +### Context With Timeout + +```v +import context +import time + +const ( + // a reasonable duration to block in an example + short_duration = 1 * time.millisecond +) + +// This example passes a context with a timeout to tell a blocking function that +// it should abandon its work after the timeout elapses. +fn example_with_timeout() { + // Pass a context with a timeout to tell a blocking function that it + // should abandon its work after the timeout elapses. + ctx := context.with_timeout(context.background(), short_duration) + defer { + context.cancel(ctx) + } + + ctx_ch := ctx.done() + select { + _ := <-ctx_ch {} + 1 * time.second { + panic('This should not happen') + } + } +} +``` + +### Context With Value + +```v +import context + +type ValueContextKey = string + +// This example demonstrates how a value can be passed to the context +// and also how to retrieve it if it exists. +fn example_with_value() { + f := fn (ctx context.Context, key ValueContextKey) string { + if value := ctx.value(key) { + if !isnil(value) { + return *(&string(value)) + } + } + return 'key not found' + } + + key := ValueContextKey('language') + value := 'VAL' + ctx := context.with_value(context.background(), key, &value) + + assert value == f(ctx, key) + assert 'key not found' == f(ctx, ValueContextKey('color')) +} +``` diff --git a/v_windows/v/old/vlib/context/_context.v b/v_windows/v/old/vlib/context/_context.v new file mode 100644 index 0000000..5d4e2d1 --- /dev/null +++ b/v_windows/v/old/vlib/context/_context.v @@ -0,0 +1,81 @@ +// This module defines the Context type, which carries deadlines, cancellation signals, +// and other request-scoped values across API boundaries and between processes. +// Based off: https://github.com/golang/go/tree/master/src/context +// Last commit: https://github.com/golang/go/commit/52bf14e0e8bdcd73f1ddfb0c4a1d0200097d3ba2 +module context + +import time + +const ( + background = EmptyContext(0) + todo = EmptyContext(1) + + cancel_context_key = 'context.CancelContext' + + // canceled is the error returned by Context.err when the context is canceled. + canceled = error('context canceled') + + // deadline_exceeded is the error returned by Context.err when the context's + // deadline passes. + deadline_exceeded = error('context deadline exceeded') +) + +pub interface Context { + // deadline returns the time when work done on behalf of this context + // should be canceled. deadline returns none when no deadline is + // set. Successive calls to deadline return the same results. + deadline() ?time.Time + // done returns a channel that's closed when work done on behalf of this + // context should be canceled. done may return nil if this context can + // never be canceled. Successive calls to done return the same value. + // The close of the done channel may happen asynchronously, + // after the cancel function returns. + // + // with_cancel arranges for done to be closed when cancel is called; + // with_deadline arranges for done to be closed when the deadline + // expires; with_timeout arranges for done to be closed when the timeout + // elapses. + done() chan int + // If done is not yet closed, err returns nil. + // If done is closed, err returns a non-nil error explaining why: + // canceled if the context was canceled + // or deadline_exceeded if the context's deadline passed. + // After err returns a non-nil error, successive calls to err return the same error. + err() IError + // Value returns the value associated with this context for key, or nil + // if no value is associated with key. Successive calls to Value with + // the same key returns the same result. + // + // Use context values only for request-scoped data that transits + // processes and API boundaries, not for passing optional parameters to + // functions. + // + // A key identifies a specific value in a Context. Functions that wish + // to store values in Context typically allocate a key in a global + // variable then use that key as the argument to context.with_value and + // Context.Value. A key can be any type that supports equality; + // packages should define keys as an unexported type to avoid + // collisions. + value(key string) ?voidptr + str() string +} + +// background returns an empty Context. It is never canceled, has no +// values, and has no deadline. It is typically used by the main function, +// initialization, and tests, and as the top-level Context for incoming +// requests. +pub fn background() Context { + return context.background +} + +// todo returns an empty Context. Code should use todo when +// it's unclear which Context to use or it is not yet available (because the +// surrounding function has not yet been extended to accept a Context +// parameter). +pub fn todo() Context { + return context.todo +} + +fn context_name(ctx Context) string { + return typeof(ctx) +} diff --git a/v_windows/v/old/vlib/context/cancel.v b/v_windows/v/old/vlib/context/cancel.v new file mode 100644 index 0000000..2972b17 --- /dev/null +++ b/v_windows/v/old/vlib/context/cancel.v @@ -0,0 +1,181 @@ +// This module defines the Context type, which carries deadlines, cancellation signals, +// and other request-scoped values across API boundaries and between processes. +// Based off: https://github.com/golang/go/tree/master/src/context +// Last commit: https://github.com/golang/go/commit/52bf14e0e8bdcd73f1ddfb0c4a1d0200097d3ba2 +module context + +import rand +import sync +import time + +pub interface Canceler { + id string + cancel(remove_from_parent bool, err IError) + done() chan int +} + +pub fn cancel(ctx Context) { + match mut ctx { + CancelContext { + ctx.cancel(true, canceled) + } + TimerContext { + ctx.cancel(true, canceled) + } + else {} + } +} + +// A CancelContext can be canceled. When canceled, it also cancels any children +// that implement Canceler. +pub struct CancelContext { + id string +mut: + context Context + mutex &sync.Mutex + done chan int + children map[string]Canceler + err IError +} + +// with_cancel returns a copy of parent with a new done channel. The returned +// context's done channel is closed when the returned cancel function is called +// or when the parent context's done channel is closed, whichever happens first. +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete. +pub fn with_cancel(parent Context) Context { + mut c := new_cancel_context(parent) + propagate_cancel(parent, mut c) + return Context(c) +} + +// new_cancel_context returns an initialized CancelContext. +fn new_cancel_context(parent Context) &CancelContext { + return &CancelContext{ + id: rand.uuid_v4() + context: parent + mutex: sync.new_mutex() + done: chan int{cap: 2} + err: none + } +} + +pub fn (ctx CancelContext) deadline() ?time.Time { + return none +} + +pub fn (mut ctx CancelContext) done() chan int { + ctx.mutex.@lock() + done := ctx.done + ctx.mutex.unlock() + return done +} + +pub fn (mut ctx CancelContext) err() IError { + ctx.mutex.@lock() + err := ctx.err + ctx.mutex.unlock() + return err +} + +pub fn (ctx CancelContext) value(key string) ?voidptr { + if key == cancel_context_key { + return voidptr(unsafe { &ctx }) + } + return ctx.context.value(key) +} + +pub fn (ctx CancelContext) str() string { + return context_name(ctx.context) + '.with_cancel' +} + +fn (mut ctx CancelContext) cancel(remove_from_parent bool, err IError) { + if err is none { + panic('context: internal error: missing cancel error') + } + + ctx.mutex.@lock() + if !(ctx.err is none) { + ctx.mutex.unlock() + // already canceled + return + } + + ctx.err = err + + if !ctx.done.closed { + ctx.done <- 0 + ctx.done.close() + } + + for _, child in ctx.children { + // NOTE: acquiring the child's lock while holding parent's lock. + child.cancel(false, err) + } + + ctx.children = map[string]Canceler{} + ctx.mutex.unlock() + + if remove_from_parent { + remove_child(ctx.context, ctx) + } +} + +fn propagate_cancel(parent Context, mut child Canceler) { + done := parent.done() + select { + _ := <-done { + // parent is already canceled + child.cancel(false, parent.err()) + return + } + } + mut p := parent_cancel_context(parent) or { + go fn (parent Context, mut child Canceler) { + pdone := parent.done() + select { + _ := <-pdone { + child.cancel(false, parent.err()) + } + } + }(parent, mut child) + return + } + + if p.err is none { + p.children[child.id] = *child + } else { + // parent has already been canceled + child.cancel(false, p.err) + } +} + +// parent_cancel_context returns the underlying CancelContext for parent. +// It does this by looking up parent.value(&cancel_context_key) to find +// the innermost enclosing CancelContext and then checking whether +// parent.done() matches that CancelContext. (If not, the CancelContext +// has been wrapped in a custom implementation providing a +// different done channel, in which case we should not bypass it.) +fn parent_cancel_context(parent Context) ?CancelContext { + done := parent.done() + if done.closed { + return none + } + if p_ptr := parent.value(cancel_context_key) { + if !isnil(p_ptr) { + mut p := &CancelContext(p_ptr) + pdone := p.done() + if done == pdone { + return *p + } + } + } + return none +} + +// remove_child removes a context from its parent. +fn remove_child(parent Context, child Canceler) { + mut p := parent_cancel_context(parent) or { return } + p.children.delete(child.id) +} diff --git a/v_windows/v/old/vlib/context/cancel_test.v b/v_windows/v/old/vlib/context/cancel_test.v new file mode 100644 index 0000000..6b9fdaa --- /dev/null +++ b/v_windows/v/old/vlib/context/cancel_test.v @@ -0,0 +1,42 @@ +import context + +// This example demonstrates the use of a cancelable context to prevent a +// routine leak. By the end of the example function, the routine started +// by gen will return without leaking. +fn test_with_cancel() { + // gen generates integers in a separate routine and + // sends them to the returned channel. + // The callers of gen need to cancel the context once + // they are done consuming generated integers not to leak + // the internal routine started by gen. + gen := fn (ctx context.Context) chan int { + dst := chan int{} + go fn (ctx context.Context, dst chan int) { + mut v := 0 + ch := ctx.done() + for { + select { + _ := <-ch { + // returning not to leak the routine + return + } + dst <- v { + v++ + } + } + } + }(ctx, dst) + return dst + } + + ctx := context.with_cancel(context.background()) + defer { + context.cancel(ctx) + } + + ch := gen(ctx) + for i in 0 .. 5 { + v := <-ch + assert i == v + } +} diff --git a/v_windows/v/old/vlib/context/deadline.v b/v_windows/v/old/vlib/context/deadline.v new file mode 100644 index 0000000..43fd056 --- /dev/null +++ b/v_windows/v/old/vlib/context/deadline.v @@ -0,0 +1,94 @@ +// This module defines the Context type, which carries deadlines, cancellation signals, +// and other request-scoped values across API boundaries and between processes. +// Based off: https://github.com/golang/go/tree/master/src/context +// Last commit: https://github.com/golang/go/commit/52bf14e0e8bdcd73f1ddfb0c4a1d0200097d3ba2 +module context + +import rand +import time + +// A TimerContext carries a timer and a deadline. It embeds a CancelContext to +// implement done and err. It implements cancel by stopping its timer then +// delegating to CancelContext.cancel +pub struct TimerContext { + id string +mut: + cancel_ctx CancelContext + deadline time.Time +} + +// with_deadline returns a copy of the parent context with the deadline adjusted +// to be no later than d. If the parent's deadline is already earlier than d, +// with_deadline(parent, d) is semantically equivalent to parent. The returned +// context's Done channel is closed when the deadline expires, when the returned +// cancel function is called, or when the parent context's Done channel is +// closed, whichever happens first. +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete. +pub fn with_deadline(parent Context, d time.Time) Context { + id := rand.uuid_v4() + if cur := parent.deadline() { + if cur < d { + // The current deadline is already sooner than the new one. + return with_cancel(parent) + } + } + cancel_ctx := new_cancel_context(parent) + mut ctx := &TimerContext{ + cancel_ctx: cancel_ctx + deadline: d + id: id + } + propagate_cancel(parent, mut ctx) + dur := d - time.now() + if dur.nanoseconds() <= 0 { + ctx.cancel(true, deadline_exceeded) // deadline has already passed + return Context(ctx) + } + + if ctx.err() is none { + go fn (mut ctx TimerContext, dur time.Duration) { + time.sleep(dur) + ctx.cancel(true, deadline_exceeded) + }(mut ctx, dur) + } + return Context(ctx) +} + +// with_timeout returns with_deadline(parent, time.now().add(timeout)). +// +// Canceling this context releases resources associated with it, so code should +// call cancel as soon as the operations running in this Context complete +pub fn with_timeout(parent Context, timeout time.Duration) Context { + return with_deadline(parent, time.now().add(timeout)) +} + +pub fn (ctx TimerContext) deadline() ?time.Time { + return ctx.deadline +} + +pub fn (mut ctx TimerContext) done() chan int { + return ctx.cancel_ctx.done() +} + +pub fn (mut ctx TimerContext) err() IError { + return ctx.cancel_ctx.err() +} + +pub fn (ctx TimerContext) value(key string) ?voidptr { + return ctx.cancel_ctx.value(key) +} + +pub fn (mut ctx TimerContext) cancel(remove_from_parent bool, err IError) { + ctx.cancel_ctx.cancel(false, err) + if remove_from_parent { + // Remove this TimerContext from its parent CancelContext's children. + remove_child(ctx.cancel_ctx.context, ctx) + } +} + +pub fn (ctx TimerContext) str() string { + return context_name(ctx.cancel_ctx.context) + '.with_deadline(' + ctx.deadline.str() + ' [' + + (time.now() - ctx.deadline).str() + '])' +} diff --git a/v_windows/v/old/vlib/context/deadline_test.v b/v_windows/v/old/vlib/context/deadline_test.v new file mode 100644 index 0000000..e4d7280 --- /dev/null +++ b/v_windows/v/old/vlib/context/deadline_test.v @@ -0,0 +1,48 @@ +import context +import time + +const ( + // a reasonable duration to block in an example + short_duration = 1 * time.millisecond +) + +// This example passes a context with an arbitrary deadline to tell a blocking +// function that it should abandon its work as soon as it gets to it. +fn test_with_deadline() { + dur := time.now().add(short_duration) + ctx := context.with_deadline(context.background(), dur) + + defer { + // Even though ctx will be expired, it is good practice to call its + // cancellation function in any case. Failure to do so may keep the + // context and its parent alive longer than necessary. + context.cancel(ctx) + } + + ctx_ch := ctx.done() + select { + _ := <-ctx_ch {} + 1 * time.second { + panic('This should not happen') + } + } +} + +// This example passes a context with a timeout to tell a blocking function that +// it should abandon its work after the timeout elapses. +fn test_with_timeout() { + // Pass a context with a timeout to tell a blocking function that it + // should abandon its work after the timeout elapses. + ctx := context.with_timeout(context.background(), short_duration) + defer { + context.cancel(ctx) + } + + ctx_ch := ctx.done() + select { + _ := <-ctx_ch {} + 1 * time.second { + panic('This should not happen') + } + } +} diff --git a/v_windows/v/old/vlib/context/empty.v b/v_windows/v/old/vlib/context/empty.v new file mode 100644 index 0000000..335369a --- /dev/null +++ b/v_windows/v/old/vlib/context/empty.v @@ -0,0 +1,42 @@ +// This module defines the Context type, which carries deadlines, cancellation signals, +// and other request-scoped values across API boundaries and between processes. +// Based off: https://github.com/golang/go/tree/master/src/context +// Last commit: https://github.com/golang/go/commit/52bf14e0e8bdcd73f1ddfb0c4a1d0200097d3ba2 +module context + +import time + +// An EmptyContext is never canceled, has no values, and has no deadline. It is not +// struct{}, since vars of this type must have distinct addresses. +pub type EmptyContext = int + +pub fn (ctx EmptyContext) deadline() ?time.Time { + return none +} + +pub fn (ctx EmptyContext) done() chan int { + ch := chan int{} + defer { + ch.close() + } + return ch +} + +pub fn (ctx EmptyContext) err() IError { + // TODO: Change this to `none` + return none_ +} + +pub fn (ctx EmptyContext) value(key string) ?voidptr { + return none +} + +pub fn (ctx EmptyContext) str() string { + if ctx == background { + return 'context.Background' + } + if ctx == todo { + return 'context.TODO' + } + return 'unknown empty Context' +} diff --git a/v_windows/v/old/vlib/context/empty_test.v b/v_windows/v/old/vlib/context/empty_test.v new file mode 100644 index 0000000..433d8c8 --- /dev/null +++ b/v_windows/v/old/vlib/context/empty_test.v @@ -0,0 +1,17 @@ +module context + +fn test_background() { + ctx := background() + assert 'context.Background' == ctx.str() + if _ := ctx.value('') { + panic('This should never happen') + } +} + +fn test_todo() { + ctx := todo() + assert 'context.TODO' == ctx.str() + if _ := ctx.value('') { + panic('This should never happen') + } +} diff --git a/v_windows/v/old/vlib/context/err.v b/v_windows/v/old/vlib/context/err.v new file mode 100644 index 0000000..23cfe56 --- /dev/null +++ b/v_windows/v/old/vlib/context/err.v @@ -0,0 +1,12 @@ +module context + +const none_ = IError(&None{}) + +struct None { + msg string + code int +} + +fn (_ None) str() string { + return 'none' +} diff --git a/v_windows/v/old/vlib/context/value.v b/v_windows/v/old/vlib/context/value.v new file mode 100644 index 0000000..19a2289 --- /dev/null +++ b/v_windows/v/old/vlib/context/value.v @@ -0,0 +1,57 @@ +// This module defines the Context type, which carries deadlines, cancellation signals, +// and other request-scoped values across API boundaries and between processes. +// Based off: https://github.com/golang/go/tree/master/src/context +// Last commit: https://github.com/golang/go/commit/52bf14e0e8bdcd73f1ddfb0c4a1d0200097d3ba2 +module context + +import time + +// A ValueContext carries a key-value pair. It implements Value for that key and +// delegates all other calls to the embedded Context. +pub struct ValueContext { + key string + value voidptr +mut: + context Context +} + +// with_value returns a copy of parent in which the value associated with key is +// val. +// +// Use context Values only for request-scoped data that transits processes and +// APIs, not for passing optional parameters to functions. +// +// The provided key must be comparable and should not be of type +// string or any other built-in type to avoid collisions between +// packages using context. Users of with_value should define their own +// types for keys +pub fn with_value(parent Context, key string, value voidptr) Context { + return &ValueContext{ + context: parent + key: key + value: value + } +} + +pub fn (ctx ValueContext) deadline() ?time.Time { + return ctx.context.deadline() +} + +pub fn (ctx ValueContext) done() chan int { + return ctx.context.done() +} + +pub fn (ctx ValueContext) err() IError { + return ctx.context.err() +} + +pub fn (ctx ValueContext) value(key string) ?voidptr { + if ctx.key == key { + return ctx.value + } + return ctx.context.value(key) +} + +pub fn (ctx ValueContext) str() string { + return context_name(ctx.context) + '.with_value' +} diff --git a/v_windows/v/old/vlib/context/value_test.v b/v_windows/v/old/vlib/context/value_test.v new file mode 100644 index 0000000..a8ed5b5 --- /dev/null +++ b/v_windows/v/old/vlib/context/value_test.v @@ -0,0 +1,23 @@ +import context + +type ValueContextKey = string + +// This example demonstrates how a value can be passed to the context +// and also how to retrieve it if it exists. +fn test_with_value() { + f := fn (ctx context.Context, key ValueContextKey) string { + if value := ctx.value(key) { + if !isnil(value) { + return *(&string(value)) + } + } + return 'key not found' + } + + key := ValueContextKey('language') + value := 'VAL' + ctx := context.with_value(context.background(), key, &value) + + assert value == f(ctx, key) + assert 'key not found' == f(ctx, ValueContextKey('color')) +} diff --git a/v_windows/v/old/vlib/crypto/aes/aes.v b/v_windows/v/old/vlib/crypto/aes/aes.v new file mode 100644 index 0000000..f7e2cec --- /dev/null +++ b/v_windows/v/old/vlib/crypto/aes/aes.v @@ -0,0 +1,77 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +// Based off: https://github.com/golang/go/blob/master/src/crypto/aes +// Last commit: https://github.com/golang/go/commit/691a2d457ab1bf03bd46d4b69e0f93b8993c0055 +module aes + +import crypto.internal.subtle + +pub const ( + // The AES block size in bytes. + block_size = 16 +) + +// AesCipher represents an AES encryption using a particular key. +struct AesCipher { +mut: + enc []u32 + dec []u32 +} + +// new_cipher creates and returns a new `AesCipher`. +// The key argument should be the AES key, +// either 16, 24, or 32 bytes to select +// AES-128, AES-192, or AES-256. +pub fn new_cipher(key []byte) AesCipher { + k := key.len + match k { + 16, 24, 32 { + // break + } + else { + panic('crypto.aes: invalid key size ' + k.str()) + // return error('crypto.aes: invalid key size ' + k.str()) + } + } + // for now use generic version + return new_cipher_generic(key) +} + +// block_size returns the block size of the checksum in bytes. +pub fn (c &AesCipher) block_size() int { + return aes.block_size +} + +// encrypt encrypts the blocks in `src` to `dst`. +// Please note: `dst` and `src` are both mutable for performance reasons. +pub fn (c &AesCipher) encrypt(mut dst []byte, mut src []byte) { + if src.len < aes.block_size { + panic('crypto.aes: input not full block') + } + if dst.len < aes.block_size { + panic('crypto.aes: output not full block') + } + // if subtle.inexact_overlap(dst[:block_size], src[:block_size]) { + if subtle.inexact_overlap((*dst)[..aes.block_size], (*src)[..aes.block_size]) { + panic('crypto.aes: invalid buffer overlap') + } + // for now use generic version + encrypt_block_generic(c.enc, mut dst, src) +} + +// decrypt decrypts the blocks in `src` to `dst`. +// Please note: `dst` and `src` are both mutable for performance reasons. +pub fn (c &AesCipher) decrypt(mut dst []byte, mut src []byte) { + if src.len < aes.block_size { + panic('crypto.aes: input not full block') + } + if dst.len < aes.block_size { + panic('crypto.aes: output not full block') + } + if subtle.inexact_overlap((*dst)[..aes.block_size], (*src)[..aes.block_size]) { + panic('crypto.aes: invalid buffer overlap') + } + // for now use generic version + decrypt_block_generic(c.dec, mut dst, src) +} diff --git a/v_windows/v/old/vlib/crypto/aes/aes_cbc.v b/v_windows/v/old/vlib/crypto/aes/aes_cbc.v new file mode 100644 index 0000000..253f79c --- /dev/null +++ b/v_windows/v/old/vlib/crypto/aes/aes_cbc.v @@ -0,0 +1,126 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +// Cipher block chaining (CBC) mode. +// CBC provides confidentiality by xoring (chaining) each plaintext block +// with the previous ciphertext block before applying the block cipher. +// See NIST SP 800-38A, pp 10-11 +// NOTE this will be moved to crypto.cipher interface (joe-c) +module aes + +import crypto.cipher +import crypto.internal.subtle + +struct AesCbc { +mut: + b AesCipher + block_size int + iv []byte + tmp []byte +} + +// internal +fn new_aes_cbc(b AesCipher, iv []byte) AesCbc { + return AesCbc{ + b: b + block_size: b.block_size() + iv: iv.clone() + tmp: []byte{len: (b.block_size())} + } +} + +// new_cbc returns a `AesCbc` which encrypts in cipher block chaining +// mode, using the given Block. The length of iv must be the same as the +// Block's block size. +pub fn new_cbc(b AesCipher, iv []byte) AesCbc { + if iv.len != b.block_size() { + panic('crypto.cipher.new_cbc_encrypter: IV length must equal block size') + } + return new_aes_cbc(b, iv) +} + +// block_size returns the block size of the checksum in bytes. +pub fn (x &AesCbc) block_size() int { + return x.block_size +} + +// encrypt_blocks encrypts the blocks in `src_` to `dst_`. +// Please note: `dst_` is mutable for performance reasons. +pub fn (x &AesCbc) encrypt_blocks(mut dst_ []byte, src_ []byte) { + unsafe { + mut dst := *dst_ + mut src := src_ + if src.len % x.block_size != 0 { + panic('crypto.cipher: input not full blocks') + } + if dst.len < src.len { + panic('crypto.cipher: output smaller than input') + } + if subtle.inexact_overlap(dst[..src.len], src_) { + panic('crypto.cipher: invalid buffer overlap') + } + mut iv := x.iv + for src.len > 0 { + // Write the xor to dst, then encrypt in place. + cipher.xor_bytes(mut dst[..x.block_size], src[..x.block_size], iv) + x.b.encrypt(mut dst[..x.block_size], mut dst[..x.block_size]) + // Move to the next block with this block as the next iv. + iv = dst[..x.block_size] + if x.block_size >= src.len { + src = [] + } else { + src = src[x.block_size..] + } + dst = dst[x.block_size..] + } + // Save the iv for the next crypt_blocks call. + copy(x.iv, iv) + } +} + +// decrypt_blocks decrypts the blocks in `src` to `dst`. +// Please note: `dst` is mutable for performance reasons. +pub fn (mut x AesCbc) decrypt_blocks(mut dst []byte, src []byte) { + if src.len % x.block_size != 0 { + panic('crypto.cipher: input not full blocks') + } + if dst.len < src.len { + panic('crypto.cipher: output smaller than input') + } + if subtle.inexact_overlap((*dst)[..src.len], src) { + panic('crypto.cipher: invalid buffer overlap') + } + if src.len == 0 { + return + } + // For each block, we need to xor the decrypted data with the previous block's ciphertext (the iv). + // To avoid making a copy each time, we loop over the blocks BACKWARDS. + mut end := src.len + mut start := end - x.block_size + mut prev := start - x.block_size + // Copy the last block of ciphertext in preparation as the new iv. + copy(x.tmp, src[start..end]) + // Loop over all but the first block. + for start > 0 { + mut src_chunk := src[start..end] + x.b.decrypt(mut (*dst)[start..end], mut src_chunk) + cipher.xor_bytes(mut (*dst)[start..end], (*dst)[start..end], src[prev..start]) + end = start + start = prev + prev -= x.block_size + } + // The first block is special because it uses the saved iv. + mut src_chunk := src[start..end] + x.b.decrypt(mut (*dst)[start..end], mut src_chunk) + cipher.xor_bytes(mut (*dst)[start..end], (*dst)[start..end], x.iv) + // Set the new iv to the first block we copied earlier. + x.iv = x.tmp + x.tmp = x.iv +} + +fn (x &AesCbc) set_iv(iv []byte) { + if iv.len != x.iv.len { + panic('cipher: incorrect length IV') + } + copy(x.iv, iv) +} diff --git a/v_windows/v/old/vlib/crypto/aes/aes_test.v b/v_windows/v/old/vlib/crypto/aes/aes_test.v new file mode 100644 index 0000000..ca85d06 --- /dev/null +++ b/v_windows/v/old/vlib/crypto/aes/aes_test.v @@ -0,0 +1,27 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +import crypto.aes + +fn test_crypto_aes() { + // TEST CBC + key := '6368616e676520746869732070617373'.bytes() + mut ciphertext := '73c86d43a9d700a253a96c85b0f6b03ac9792e0e757f869cca306bd3cba1c62b'.bytes() + block := aes.new_cipher(key) + // The IV needs to be unique, but not secure. Therefore it's common to + // include it at the beginning of the ciphertext. + if ciphertext.len < aes.block_size { + panic('ciphertext too short') + } + iv := ciphertext[..aes.block_size] + ciphertext = ciphertext[aes.block_size..] + // CBC mode always works in whole blocks. + if ciphertext.len % aes.block_size != 0 { + panic('ciphertext is not a multiple of the block size') + } + mode := aes.new_cbc(block, iv) + cipher_clone := ciphertext.clone() + mode.encrypt_blocks(mut ciphertext, cipher_clone) + assert ciphertext.hex() == 'c210459b514668ddc44674885e4979215265a6c44431a248421254ef357a8c2a308a8bddf5623af9df91737562041cf1' + println('ok') +} diff --git a/v_windows/v/old/vlib/crypto/aes/block_generic.v b/v_windows/v/old/vlib/crypto/aes/block_generic.v new file mode 100644 index 0000000..5da938e --- /dev/null +++ b/v_windows/v/old/vlib/crypto/aes/block_generic.v @@ -0,0 +1,183 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +// This implementation is derived from the golang implementation +// which itself is derived in part from the reference +// ANSI C implementation, which carries the following notice: +// +// rijndael-alg-fst.c +// +// @version 3.0 (December 2000) +// +// Optimised ANSI C code for the Rijndael cipher (now AES) +// +// @author Vincent Rijmen +// @author Antoon Bosselaers +// @author Paulo Barreto +// +// This code is hereby placed in the public domain. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS +// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// See FIPS 197 for specification, and see Daemen and Rijmen's Rijndael submission +// for implementation details. +// https://csrc.nist.gov/csrc/media/publications/fips/197/final/documents/fips-197.pdf +// https://csrc.nist.gov/archive/aes/rijndael/Rijndael-ammended.pdf +module aes + +import encoding.binary + +// Encrypt one block from src into dst, using the expanded key xk. +fn encrypt_block_generic(xk []u32, mut dst []byte, src []byte) { + _ = src[15] // early bounds check + mut s0 := binary.big_endian_u32(src[..4]) + mut s1 := binary.big_endian_u32(src[4..8]) + mut s2 := binary.big_endian_u32(src[8..12]) + mut s3 := binary.big_endian_u32(src[12..16]) + // First round just XORs input with key. + s0 ^= xk[0] + s1 ^= xk[1] + s2 ^= xk[2] + s3 ^= xk[3] + // Middle rounds shuffle using tables. + // Number of rounds is set by length of expanded key. + nr := xk.len / 4 - 2 // - 2: one above, one more below + mut k := 4 + mut t0 := u32(0) + mut t1 := u32(0) + mut t2 := u32(0) + mut t3 := u32(0) + for _ in 0 .. nr { + t0 = xk[k + 0] ^ te0[byte(s0 >> 24)] ^ te1[byte(s1 >> 16)] ^ te2[byte(s2 >> 8)] ^ u32(te3[byte(s3)]) + t1 = xk[k + 1] ^ te0[byte(s1 >> 24)] ^ te1[byte(s2 >> 16)] ^ te2[byte(s3 >> 8)] ^ u32(te3[byte(s0)]) + t2 = xk[k + 2] ^ te0[byte(s2 >> 24)] ^ te1[byte(s3 >> 16)] ^ te2[byte(s0 >> 8)] ^ u32(te3[byte(s1)]) + t3 = xk[k + 3] ^ te0[byte(s3 >> 24)] ^ te1[byte(s0 >> 16)] ^ te2[byte(s1 >> 8)] ^ u32(te3[byte(s2)]) + k += 4 + s0 = t0 + s1 = t1 + s2 = t2 + s3 = t3 + } + // Last round uses s-box directly and XORs to produce output. + s0 = s_box0[t0 >> 24] << 24 | s_box0[t1 >> 16 & 0xff] << 16 | u32(s_box0[t2 >> 8 & 0xff] << 8) | s_box0[t3 & u32(0xff)] + s1 = s_box0[t1 >> 24] << 24 | s_box0[t2 >> 16 & 0xff] << 16 | u32(s_box0[t3 >> 8 & 0xff] << 8) | s_box0[t0 & u32(0xff)] + s2 = s_box0[t2 >> 24] << 24 | s_box0[t3 >> 16 & 0xff] << 16 | u32(s_box0[t0 >> 8 & 0xff] << 8) | s_box0[t1 & u32(0xff)] + s3 = s_box0[t3 >> 24] << 24 | s_box0[t0 >> 16 & 0xff] << 16 | u32(s_box0[t1 >> 8 & 0xff] << 8) | s_box0[t2 & u32(0xff)] + s0 ^= xk[k + 0] + s1 ^= xk[k + 1] + s2 ^= xk[k + 2] + s3 ^= xk[k + 3] + _ := dst[15] // early bounds check + binary.big_endian_put_u32(mut (*dst)[0..4], s0) + binary.big_endian_put_u32(mut (*dst)[4..8], s1) + binary.big_endian_put_u32(mut (*dst)[8..12], s2) + binary.big_endian_put_u32(mut (*dst)[12..16], s3) +} + +// Decrypt one block from src into dst, using the expanded key xk. +fn decrypt_block_generic(xk []u32, mut dst []byte, src []byte) { + _ = src[15] // early bounds check + mut s0 := binary.big_endian_u32(src[0..4]) + mut s1 := binary.big_endian_u32(src[4..8]) + mut s2 := binary.big_endian_u32(src[8..12]) + mut s3 := binary.big_endian_u32(src[12..16]) + // First round just XORs input with key. + s0 ^= xk[0] + s1 ^= xk[1] + s2 ^= xk[2] + s3 ^= xk[3] + // Middle rounds shuffle using tables. + // Number of rounds is set by length of expanded key. + nr := xk.len / 4 - 2 // - 2: one above, one more below + mut k := 4 + mut t0 := u32(0) + mut t1 := u32(0) + mut t2 := u32(0) + mut t3 := u32(0) + for _ in 0 .. nr { + t0 = xk[k + 0] ^ td0[byte(s0 >> 24)] ^ td1[byte(s3 >> 16)] ^ td2[byte(s2 >> 8)] ^ u32(td3[byte(s1)]) + t1 = xk[k + 1] ^ td0[byte(s1 >> 24)] ^ td1[byte(s0 >> 16)] ^ td2[byte(s3 >> 8)] ^ u32(td3[byte(s2)]) + t2 = xk[k + 2] ^ td0[byte(s2 >> 24)] ^ td1[byte(s1 >> 16)] ^ td2[byte(s0 >> 8)] ^ u32(td3[byte(s3)]) + t3 = xk[k + 3] ^ td0[byte(s3 >> 24)] ^ td1[byte(s2 >> 16)] ^ td2[byte(s1 >> 8)] ^ u32(td3[byte(s0)]) + k += 4 + s0 = t0 + s1 = t1 + s2 = t2 + s3 = t3 + } + // Last round uses s-box directly and XORs to produce output. + s0 = u32(s_box1[t0 >> 24]) << 24 | u32(s_box1[t3 >> 16 & 0xff]) << 16 | u32(s_box1[t2 >> 8 & 0xff] << 8) | u32(s_box1[t1 & u32(0xff)]) + s1 = u32(s_box1[t1 >> 24]) << 24 | u32(s_box1[t0 >> 16 & 0xff]) << 16 | u32(s_box1[t3 >> 8 & 0xff] << 8) | u32(s_box1[t2 & u32(0xff)]) + s2 = u32(s_box1[t2 >> 24]) << 24 | u32(s_box1[t1 >> 16 & 0xff]) << 16 | u32(s_box1[t0 >> 8 & 0xff] << 8) | u32(s_box1[t3 & u32(0xff)]) + s3 = u32(s_box1[t3 >> 24]) << 24 | u32(s_box1[t2 >> 16 & 0xff]) << 16 | u32(s_box1[t1 >> 8 & 0xff] << 8) | u32(s_box1[t0 & u32(0xff)]) + s0 ^= xk[k + 0] + s1 ^= xk[k + 1] + s2 ^= xk[k + 2] + s3 ^= xk[k + 3] + _ = dst[15] // early bounds check + binary.big_endian_put_u32(mut (*dst)[..4], s0) + binary.big_endian_put_u32(mut (*dst)[4..8], s1) + binary.big_endian_put_u32(mut (*dst)[8..12], s2) + binary.big_endian_put_u32(mut (*dst)[12..16], s3) +} + +// Apply s_box0 to each byte in w. +fn subw(w u32) u32 { + return u32(s_box0[w >> 24]) << 24 | u32(s_box0[w >> 16 & 0xff] << 16) | u32(s_box0[w >> 8 & 0xff] << 8) | u32(s_box0[w & u32(0xff)]) +} + +// Rotate +fn rotw(w u32) u32 { + return (w << 8) | (w >> 24) +} + +// Key expansion algorithm. See FIPS-197, Figure 11. +// Their rcon[i] is our powx[i-1] << 24. +fn expand_key_generic(key []byte, mut enc []u32, mut dec []u32) { + // Encryption key setup. + mut i := 0 + nk := key.len / 4 + for i = 0; i < nk; i++ { + if 4 * i >= key.len { + break + } + enc[i] = binary.big_endian_u32(key[4 * i..]) + } + for i < enc.len { + mut t := enc[i - 1] + if i % nk == 0 { + t = subw(rotw(t)) ^ u32(pow_x[i / nk - 1]) << 24 + } else if nk > 6 && i % nk == 4 { + t = subw(t) + } + enc[i] = enc[i - nk] ^ t + i++ + } + // Derive decryption key from encryption key. + // Reverse the 4-word round key sets from enc to produce dec. + // All sets but the first and last get the MixColumn transform applied. + if dec.len == 0 { + return + } + n := enc.len + for i = 0; i < n; i += 4 { + ei := n - i - 4 + for j in 0 .. 4 { + mut x := enc[ei + j] + if i > 0 && i + 4 < n { + x = td0[s_box0[x >> 24]] ^ td1[s_box0[x >> 16 & 0xff]] ^ td2[s_box0[x >> 8 & 0xff]] ^ td3[s_box0[x & u32(0xff)]] + } + dec[i + j] = x + } + } +} diff --git a/v_windows/v/old/vlib/crypto/aes/const.v b/v_windows/v/old/vlib/crypto/aes/const.v new file mode 100644 index 0000000..1fe3357 --- /dev/null +++ b/v_windows/v/old/vlib/crypto/aes/const.v @@ -0,0 +1,374 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. + +// Package aes implements AES encryption (formerly Rijndael), as defined in +// U.S. Federal Information Processing Standards Publication 197. +// +// The AES operations in this package are not implemented using constant-time algorithms. +// An exception is when running on systems with enabled hardware support for AES +// that makes these operations constant-time. Examples include amd64 systems using AES-NI +// extensions and s390x systems using Message-Security-Assist extensions. +// On such systems, when the result of NewCipher is passed to cipher.NewGCM, +// the GHASH operation used by GCM is also constant-time. +module aes + +// This file contains AES constants - 8720 bytes of initialized data. + +// https://csrc.nist.gov/publications/fips/fips197/fips-197.pdf + +// AES is based on the mathematical behavior of binary polynomials +// (polynomials over GF(2)) modulo the irreducible polynomial x⁸ + x⁴ + x³ + x + 1. +// Addition of these binary polynomials corresponds to binary xor. +// Reducing mod poly corresponds to binary xor with poly every +// time a 0x100 bit appears. +const ( + poly = (1<<8) | (1<<4) | (1<<3) | (1<<1) | (1<<0) // x⁸ + x⁴ + x³ + x + 1 +) + +// Powers of x mod poly in GF(2). +const ( + pow_x = [ + byte(0x01), + 0x02, + 0x04, + 0x08, + 0x10, + 0x20, + 0x40, + 0x80, + 0x1b, + 0x36, + 0x6c, + 0xd8, + 0xab, + 0x4d, + 0x9a, + 0x2f, + ] +) + +// FIPS-197 Figure 7. S-box substitution values in hexadecimal format. +const ( + s_box0 = [ + byte(0x63), 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, + 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, + 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, + 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, + 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, + 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, + 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, + 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, + 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, + 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, + 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, + 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, + 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, + 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, + 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, + 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16, + ] +) + +// FIPS-197 Figure 14. Inverse S-box substitution values in hexadecimal format. +const ( + s_box1 = [ + byte(0x52), 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb, + 0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb, + 0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e, + 0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25, + 0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92, + 0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84, + 0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06, + 0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b, + 0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73, + 0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e, + 0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b, + 0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4, + 0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f, + 0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef, + 0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d, + ] +) + +// Lookup tables for encryption. + +const ( + te0 = [ + u32(0xc66363a5), 0xf87c7c84, 0xee777799, 0xf67b7b8d, 0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554, + 0x60303050, 0x02010103, 0xce6767a9, 0x562b2b7d, 0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a, + 0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87, 0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b, + 0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea, 0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b, + 0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a, 0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f, + 0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108, 0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f, + 0x0804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e, 0x30181828, 0x379696a1, 0x0a05050f, 0x2f9a9ab5, + 0x0e070709, 0x24121236, 0x1b80809b, 0xdfe2e23d, 0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f, + 0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e, 0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb, + 0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce, 0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497, + 0xa65353f5, 0xb9d1d168, 0x00000000, 0xc1eded2c, 0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed, + 0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b, 0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a, + 0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16, 0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594, + 0x8a4545cf, 0xe9f9f910, 0x04020206, 0xfe7f7f81, 0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3, + 0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x058f8f8a, 0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504, + 0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163, 0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d, + 0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f, 0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739, + 0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47, 0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395, + 0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f, 0x44222266, 0x542a2a7e, 0x3b9090ab, 0x0b888883, + 0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c, 0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76, + 0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e, 0x924949db, 0x0c06060a, 0x4824246c, 0xb85c5ce4, + 0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6, 0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b, + 0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7, 0x018d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0, + 0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25, 0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818, + 0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72, 0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651, + 0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21, 0x964b4bdd, 0x61bdbddc, 0x0d8b8b86, 0x0f8a8a85, + 0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa, 0x904848d8, 0x06030305, 0xf7f6f601, 0x1c0e0e12, + 0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0, 0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9, + 0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133, 0xd26969bb, 0xa9d9d970, 0x078e8e89, 0x339494a7, + 0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920, 0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a, + 0x038c8c8f, 0x59a1a1f8, 0x09898980, 0x1a0d0d17, 0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8, + 0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11, 0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a, + ] + te1 = [ + u32(0xa5c66363), 0x84f87c7c, 0x99ee7777, 0x8df67b7b, 0x0dfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5, + 0x50603030, 0x03020101, 0xa9ce6767, 0x7d562b2b, 0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676, + 0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d, 0x15effafa, 0xebb25959, 0xc98e4747, 0x0bfbf0f0, + 0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf, 0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0, + 0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626, 0x5a6c3636, 0x417e3f3f, 0x02f5f7f7, 0x4f83cccc, + 0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x08f9f1f1, 0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515, + 0x0c080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3, 0x28301818, 0xa1379696, 0x0f0a0505, 0xb52f9a9a, + 0x090e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2, 0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575, + 0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a, 0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0, + 0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3, 0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484, + 0xf5a65353, 0x68b9d1d1, 0x00000000, 0x2cc1eded, 0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b, + 0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939, 0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf, + 0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb, 0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585, + 0xcf8a4545, 0x10e9f9f9, 0x06040202, 0x81fe7f7f, 0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8, + 0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f, 0xad3f9292, 0xbc219d9d, 0x48703838, 0x04f1f5f5, + 0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121, 0x30201010, 0x1ae5ffff, 0x0efdf3f3, 0x6dbfd2d2, + 0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec, 0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717, + 0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d, 0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373, + 0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc, 0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888, + 0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414, 0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb, + 0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a, 0xdb924949, 0x0a0c0606, 0x6c482424, 0xe4b85c5c, + 0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262, 0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979, + 0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d, 0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9, + 0xb4d86c6c, 0xfaac5656, 0x07f3f4f4, 0x25cfeaea, 0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808, + 0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e, 0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6, + 0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f, 0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a, + 0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666, 0xd8904848, 0x05060303, 0x01f7f6f6, 0x121c0e0e, + 0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9, 0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e, + 0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111, 0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494, + 0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9, 0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf, + 0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d, 0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868, + 0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f, 0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616, + ] + te2 = [ + u32(0x63a5c663), 0x7c84f87c, 0x7799ee77, 0x7b8df67b, 0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5, + 0x30506030, 0x01030201, 0x67a9ce67, 0x2b7d562b, 0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76, + 0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d, 0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0, + 0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af, 0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0, + 0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26, 0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc, + 0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1, 0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15, + 0x040c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3, 0x18283018, 0x96a13796, 0x050f0a05, 0x9ab52f9a, + 0x07090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2, 0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75, + 0x091b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a, 0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0, + 0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3, 0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384, + 0x53f5a653, 0xd168b9d1, 0x00000000, 0xed2cc1ed, 0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b, + 0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239, 0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf, + 0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb, 0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185, + 0x45cf8a45, 0xf910e9f9, 0x02060402, 0x7f81fe7f, 0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8, + 0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f, 0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5, + 0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221, 0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2, + 0xcd4c81cd, 0x0c14180c, 0x13352613, 0xec2fc3ec, 0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17, + 0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d, 0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673, + 0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc, 0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88, + 0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814, 0xde79a7de, 0x5ee2bc5e, 0x0b1d160b, 0xdb76addb, + 0xe03bdbe0, 0x32566432, 0x3a4e743a, 0x0a1e140a, 0x49db9249, 0x060a0c06, 0x246c4824, 0x5ce4b85c, + 0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462, 0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279, + 0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d, 0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9, + 0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea, 0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x08181008, + 0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e, 0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6, + 0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f, 0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a, + 0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66, 0x48d89048, 0x03050603, 0xf601f7f6, 0x0e121c0e, + 0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9, 0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e, + 0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211, 0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394, + 0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9, 0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df, + 0x8c8f038c, 0xa1f859a1, 0x89800989, 0x0d171a0d, 0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068, + 0x41c38241, 0x99b02999, 0x2d775a2d, 0x0f111e0f, 0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16, + ] + te3 = [ + u32(0x6363a5c6), 0x7c7c84f8, 0x777799ee, 0x7b7b8df6, 0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491, + 0x30305060, 0x01010302, 0x6767a9ce, 0x2b2b7d56, 0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec, + 0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa, 0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb, + 0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45, 0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b, + 0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c, 0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83, + 0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9, 0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a, + 0x04040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d, 0x18182830, 0x9696a137, 0x05050f0a, 0x9a9ab52f, + 0x0707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf, 0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea, + 0x09091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34, 0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b, + 0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d, 0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713, + 0x5353f5a6, 0xd1d168b9, 0x00000000, 0xeded2cc1, 0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6, + 0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72, 0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85, + 0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed, 0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411, + 0x4545cf8a, 0xf9f910e9, 0x02020604, 0x7f7f81fe, 0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b, + 0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05, 0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1, + 0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342, 0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf, + 0xcdcd4c81, 0x0c0c1418, 0x13133526, 0xecec2fc3, 0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e, + 0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a, 0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6, + 0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3, 0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b, + 0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28, 0xdede79a7, 0x5e5ee2bc, 0x0b0b1d16, 0xdbdb76ad, + 0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0x0a0a1e14, 0x4949db92, 0x06060a0c, 0x24246c48, 0x5c5ce4b8, + 0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4, 0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2, + 0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da, 0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049, + 0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf, 0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x08081810, + 0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c, 0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197, + 0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e, 0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f, + 0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc, 0x4848d890, 0x03030506, 0xf6f601f7, 0x0e0e121c, + 0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069, 0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927, + 0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322, 0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733, + 0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9, 0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5, + 0x8c8c8f03, 0xa1a1f859, 0x89898009, 0x0d0d171a, 0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0, + 0x4141c382, 0x9999b029, 0x2d2d775a, 0x0f0f111e, 0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c, + ] +) + +// Lookup tables for decryption. +const ( + td0 = [ + u32(0x51f4a750), 0x7e416553, 0x1a17a4c3, 0x3a275e96, 0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393, + 0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25, 0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f, + 0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1, 0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6, + 0x038f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da, 0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844, + 0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd, 0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4, + 0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45, 0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94, + 0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7, 0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a, + 0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5, 0x302887f2, 0x23bfa5b2, 0x02036aba, 0xed16825c, + 0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1, 0x65daf4cd, 0x0605bed5, 0xd134621f, 0xc4a6fe8a, + 0x342e539d, 0xa2f355a0, 0x058ae132, 0xa4f6eb75, 0x0b83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051, + 0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46, 0x91548db5, 0x71c45d05, 0x0406d46f, 0x605015ff, + 0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77, 0xb0e842bd, 0x07898b88, 0xe7195b38, 0x79c8eedb, + 0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x00000000, 0x09808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e, + 0xfd0efffb, 0x0f853856, 0x3daed51e, 0x362d3927, 0x0a0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a, + 0x0c0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e, 0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16, + 0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d, 0x0e090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8, + 0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd, 0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34, + 0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163, 0xd731dcca, 0x42638510, 0x13972240, 0x84c61120, + 0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d, 0x1d9e2f4b, 0xdcb230f3, 0x0d8652ec, 0x77c1e3d0, + 0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422, 0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef, + 0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36, 0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4, + 0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662, 0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5, + 0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3, 0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b, + 0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8, 0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6, + 0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6, 0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0, + 0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815, 0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f, + 0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df, 0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f, + 0x9d5eea04, 0x018c355d, 0xfa877473, 0xfb0b412e, 0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713, + 0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89, 0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c, + 0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf, 0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86, + 0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f, 0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541, + 0x39a80171, 0x080cb3de, 0xd8b4e49c, 0x6456c190, 0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742, + ] + td1 = [ + u32(0x5051f4a7), 0x537e4165, 0xc31a17a4, 0x963a275e, 0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303, + 0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c, 0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3, + 0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0, 0x02c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9, + 0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259, 0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8, + 0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971, 0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a, + 0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f, 0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b, + 0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8, 0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab, + 0x07b2eb28, 0x032fb5c2, 0x9a86c57b, 0xa5d33708, 0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682, + 0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2, 0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe, + 0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb, 0x390b83ec, 0xaa4060ef, 0x065e719f, 0x51bd6e10, + 0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd, 0xb591548d, 0x0571c45d, 0x6f0406d4, 0xff605015, + 0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e, 0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee, + 0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x00000000, 0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72, + 0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39, 0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e, + 0xb10c0a67, 0x0f9357e7, 0xd2b4ee96, 0x9e1b9b91, 0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a, + 0x0ae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17, 0x0b0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9, + 0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60, 0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e, + 0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1, 0xcad731dc, 0x10426385, 0x40139722, 0x2084c611, + 0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1, 0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3, + 0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964, 0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390, + 0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b, 0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf, + 0xe42c3a9d, 0x0d507892, 0x9b6a5fcc, 0x62547e46, 0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af, + 0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512, 0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb, + 0x09cd2678, 0xf46e5918, 0x01ec9ab7, 0xa8834f9a, 0x65e6956e, 0x7eaaffe6, 0x0821bccf, 0xe6ef15e8, + 0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c, 0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266, + 0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8, 0x4af10498, 0xf741ecda, 0x0e7fcd50, 0x2f1791f6, + 0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604, 0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551, + 0x049d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41, 0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647, + 0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c, 0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1, + 0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737, 0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db, + 0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340, 0x72161dc3, 0x0cbce225, 0x8b283c49, 0x41ff0d95, + 0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1, 0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857, + ] + td2 = [ + u32(0xa75051f4), 0x65537e41, 0xa4c31a17, 0x5e963a27, 0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x03934be3, + 0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502, 0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562, + 0x5a49deb1, 0x1b6725ba, 0x0e9845ea, 0xc0e15dfe, 0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3, + 0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552, 0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9, + 0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9, 0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce, + 0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253, 0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908, + 0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b, 0xd323ab73, 0x02e2724b, 0x8f57e31f, 0xab2a6655, + 0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x08a5d337, 0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16, + 0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69, 0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6, + 0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6, 0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e, + 0x8af93e21, 0x063d96dd, 0x05aedd3e, 0xbd464de6, 0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050, + 0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9, 0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8, + 0x0a47a17c, 0x0fe97c42, 0x1ec9f884, 0x00000000, 0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a, + 0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d, 0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436, + 0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b, 0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12, + 0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b, 0x0d0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e, + 0x198557f1, 0x074caf75, 0xddbbee99, 0x60fda37f, 0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb, + 0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4, 0xdccad731, 0x85104263, 0x22401397, 0x112084c6, + 0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729, 0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1, + 0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9, 0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233, + 0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0x0b3698d4, 0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad, + 0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e, 0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3, + 0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25, 0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b, + 0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f, 0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15, + 0x9bd9bae7, 0x36ce4a6f, 0x09d4ea9f, 0x7cd629b0, 0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2, + 0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7, 0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791, + 0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x04dfe496, 0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665, + 0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b, 0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6, + 0x618c9ad7, 0x0c7a37a1, 0x148e59f8, 0x3c89eb13, 0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47, + 0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7, 0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844, + 0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3, 0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d, + 0x017139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456, 0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8, + ] + td3 = [ + u32(0xf4a75051), 0x4165537e, 0x17a4c31a, 0x275e963a, 0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b, + 0x30fa5520, 0x766df6ad, 0xcc769188, 0x024c25f5, 0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5, + 0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d, 0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b, + 0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95, 0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e, + 0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27, 0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d, + 0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562, 0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x082b94f9, + 0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752, 0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66, + 0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3, 0x2887f230, 0xbfa5b223, 0x036aba02, 0x16825ced, + 0xcf1c2b8a, 0x79b492a7, 0x07f2f0f3, 0x69e2a14e, 0xdaf4cd65, 0x05bed506, 0x34621fd1, 0xa6fe8ac4, + 0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4, 0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd, + 0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d, 0x548db591, 0xc45d0571, 0x06d46f04, 0x5015ff60, + 0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767, 0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79, + 0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x00000000, 0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c, + 0x0efffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736, 0x0fd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24, + 0x0a67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b, 0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c, + 0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12, 0x090d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814, + 0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3, 0x01269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b, + 0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8, 0x31dccad7, 0x63851042, 0x97224013, 0xc6112084, + 0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7, 0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077, + 0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247, 0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22, + 0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698, 0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f, + 0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254, 0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582, + 0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf, 0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb, + 0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883, 0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef, + 0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629, 0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035, + 0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533, 0x04984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17, + 0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4, 0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46, + 0x5eea049d, 0x8c355d01, 0x877473fa, 0x0b412efb, 0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d, + 0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb, 0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a, + 0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73, 0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678, + 0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2, 0x1dc37216, 0xe2250cbc, 0x3c498b28, 0x0d9541ff, + 0xa8017139, 0x0cb3de08, 0xb4e49cd8, 0x56c19064, 0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0, + ] +) diff --git a/v_windows/v/old/vlib/crypto/aes/cypher_generic.v b/v_windows/v/old/vlib/crypto/aes/cypher_generic.v new file mode 100644 index 0000000..fb8bb08 --- /dev/null +++ b/v_windows/v/old/vlib/crypto/aes/cypher_generic.v @@ -0,0 +1,16 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module aes + +// new_cipher_generic creates and returns a new cipher.Block +// this is the generiv v version, no arch optimisations +fn new_cipher_generic(key []byte) AesCipher { + n := key.len + 28 + mut c := AesCipher{ + enc: []u32{len: n} + dec: []u32{len: n} + } + expand_key_generic(key, mut c.enc, mut c.dec) + return c +} diff --git a/v_windows/v/old/vlib/crypto/cipher/xor_generic.v b/v_windows/v/old/vlib/crypto/cipher/xor_generic.v new file mode 100644 index 0000000..bbec30b --- /dev/null +++ b/v_windows/v/old/vlib/crypto/cipher/xor_generic.v @@ -0,0 +1,33 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module cipher + +// NOTE: Implement other versions (joe-c) +// xor_bytes xors the bytes in a and b. The destination should have enough +// space, otherwise xor_bytes will panic. Returns the number of bytes xor'd. +pub fn xor_bytes(mut dst []byte, a []byte, b []byte) int { + mut n := a.len + if b.len < n { + n = b.len + } + if n == 0 { + return 0 + } + safe_xor_bytes(mut dst, a, b, n) + return n +} + +// safe_xor_bytes XORs the bytes in `a` and `b` into `dst` it does so `n` times. +// Please note: `n` needs to be smaller or equal than the length of `a` and `b`. +pub fn safe_xor_bytes(mut dst []byte, a []byte, b []byte, n int) { + for i in 0 .. n { + dst[i] = a[i] ^ b[i] + } +} + +// xor_words XORs multiples of 4 or 8 bytes (depending on architecture.) +// The slice arguments `a` and `b` are assumed to be of equal length. +pub fn xor_words(mut dst []byte, a []byte, b []byte) { + safe_xor_bytes(mut dst, a, b, b.len) +} diff --git a/v_windows/v/old/vlib/crypto/crypto.v b/v_windows/v/old/vlib/crypto/crypto.v new file mode 100644 index 0000000..12a092f --- /dev/null +++ b/v_windows/v/old/vlib/crypto/crypto.v @@ -0,0 +1,23 @@ +module crypto + +pub enum Hash { + md4 + md5 + sha1 + sha224 + sha256 + sha384 + sha512 + md5sha1 + ripemd160 + sha3_224 + sha3_256 + sha3_384 + sha3_512 + sha512_224 + sha512_256 + blake2s_256 + blake2b_256 + blake2b_384 + blake2b_512 +} diff --git a/v_windows/v/old/vlib/crypto/hmac/hmac.v b/v_windows/v/old/vlib/crypto/hmac/hmac.v new file mode 100644 index 0000000..3f45467 --- /dev/null +++ b/v_windows/v/old/vlib/crypto/hmac/hmac.v @@ -0,0 +1,44 @@ +// HMAC: Keyed-Hashing for Message Authentication implemented in v +// implementation based on https://tools.ietf.org/html/rfc2104 +module hmac + +import crypto.internal.subtle + +const ( + ipad = []byte{len: 256, init: 0x36} // TODO is 256 enough?? + opad = []byte{len: 256, init: 0x5C} + npad = []byte{len: 256, init: 0} +) + +// new returns a HMAC byte array, depending on the hash algorithm used. +pub fn new(key []byte, data []byte, hash_func fn ([]byte) []byte, blocksize int) []byte { + mut b_key := []byte{} + if key.len <= blocksize { + b_key = key.clone() // TODO: remove .clone() once https://github.com/vlang/v/issues/6604 gets fixed + } else { + b_key = hash_func(key) + } + if b_key.len < blocksize { + b_key << hmac.npad[..blocksize - b_key.len] + } + mut inner := []byte{} + for i, b in hmac.ipad[..blocksize] { + inner << b_key[i] ^ b + } + inner << data + inner_hash := hash_func(inner) + mut outer := []byte{cap: b_key.len} + for i, b in hmac.opad[..blocksize] { + outer << b_key[i] ^ b + } + outer << inner_hash + digest := hash_func(outer) + return digest +} + +// equal compares 2 MACs for equality, without leaking timing info. +// NB: if the lengths of the 2 MACs are different, probably a completely different +// hash function was used to generate them => no useful timing information. +pub fn equal(mac1 []byte, mac2 []byte) bool { + return subtle.constant_time_compare(mac1, mac2) == 1 +} diff --git a/v_windows/v/old/vlib/crypto/hmac/hmac_test.v b/v_windows/v/old/vlib/crypto/hmac/hmac_test.v new file mode 100644 index 0000000..3b78420 --- /dev/null +++ b/v_windows/v/old/vlib/crypto/hmac/hmac_test.v @@ -0,0 +1,226 @@ +// tests are taken from https://tools.ietf.org/html/rfc2202 +module hmac + +import crypto.hmac +import crypto.md5 +import crypto.sha1 +import crypto.sha256 +import crypto.sha512 + +// not yet supported +// import crypto.md4 +// import crypto.md5sha1 +// import crypto.ripemd160 +// import crypto.sha3_224 +// import crypto.sha3_256 +// import crypto.sha3_384 +// import crypto.sha3_512 +// import crypto.blake2s_256 +// import crypto.blake2b_256 +// import crypto.blake2b_384 +// import crypto.blake2b_512 +const ( + keys = [[byte(0xb), 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb, 0xb], + 'Jefe'.bytes(), + [byte(0xAA), 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA], + [byte(0x01), 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19], + [byte(0x0c), 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c], + [byte(0xaa), 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa], + [byte(0xaa), 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa], + ] + data = ['Hi There'.bytes(), 'what do ya want for nothing?'.bytes(), + [byte(0xDD), 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, + 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, + 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, + 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD], + [byte(0xcd), 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, + 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd], + 'Test With Truncation'.bytes(), + 'Test Using Larger Than Block-Size Key - Hash Key First'.bytes(), + 'Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data'.bytes(), + ] +) + +fn test_hmac_md5() { + md5_expected_results := [ + '9294727a3638bb1c13f48ef8158bfc9d', + '750c783e6ab0b503eaa86e310a5db738', + '56be34521d144c88dbb8c733f0e8b3f6', + '697eaf0aca3a3aea3a75164746ffaa79', + '56461ef2342edc00f9bab995690efd4c', + '6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd', + '6f630fad67cda0ee1fb1f562db3aa53e', + ] + mut result := '' + for i, key in hmac.keys { + result = hmac.new(key, hmac.data[i], md5.sum, md5.block_size).hex() + assert result == md5_expected_results[i] + } +} + +fn test_hmac_sha1() { + sha1_expected_results := [ + '675b0b3a1b4ddf4e124872da6c2f632bfed957e9', + 'effcdf6ae5eb2fa2d27416d5f184df9c259a7c79', + 'd730594d167e35d5956fd8003d0db3d3f46dc7bb', + '4c9007f4026250c6bc8414f9bf50c86c2d7235da', + '37268b7e21e84da5720c53c4ba03ad1104039fa7', + 'aa4ae5e15272d00e95705637ce8a3b55ed402112', + 'e8e99d0f45237d786d6bbaa7965c7808bbff1a91', + ] + mut result := '' + for i, key in hmac.keys { + result = hmac.new(key, hmac.data[i], sha1.sum, sha1.block_size).hex() + assert result == sha1_expected_results[i] + } +} + +fn test_hmac_sha224() { + sha224_expected_results := [ + '4e841ce7a4ae83fbcf71e3cd64bfbf277f73a14680aae8c518ac7861', + 'a30e01098bc6dbbf45690f3a7e9e6d0f8bbea2a39e6148008fd05e44', + 'cbff7c2716bbaa7c77bed4f491d3e8456cb6c574e92f672b291acf5b', + '6c11506874013cac6a2abc1bb382627cec6a90d86efc012de7afec5a', + 'd812c97a5e1412f2eb08dc4d95548117780f2930fa4e0e553d985c68', + '9ed2eebc0ed23576efc815e9b5bc0d9257e36d13e4dd5d5f0c809b38', + '7358939e58683a448ac5065196d33191a1c1d33d4b8b0304dc60f5e0', + ] + mut result := '' + for i, key in hmac.keys { + result = hmac.new(key, hmac.data[i], sha256.sum224, sha256.block_size).hex() + assert result == sha224_expected_results[i] + } +} + +fn test_hmac_sha256() { + sha256_expected_results := [ + '492ce020fe2534a5789dc3848806c78f4f6711397f08e7e7a12ca5a4483c8aa6', + '5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843', + '7dda3cc169743a6484649f94f0eda0f9f2ff496a9733fb796ed5adb40a44c3c1', + '82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b', + '2282475faa2def6936685d9c06566f2d782307ace7a27ada2037e6285efcb008', + '6953025ed96f0c09f80a96f78e6538dbe2e7b820e3dd970e7ddd39091b32352f', + '6355ac22e890d0a3c8481a5ca4825bc884d3e7a1ff98a2fc2ac7d8e064c3b2e6', + ] + mut result := '' + for i, key in hmac.keys { + result = hmac.new(key, hmac.data[i], sha256.sum, sha256.block_size).hex() + assert result == sha256_expected_results[i] + } +} + +fn test_hmac_sha384() { + sha384_expected_results := [ + '7afaa633e20d379b02395915fbc385ff8dc27dcd3885e1068ab942eeab52ec1f20ad382a92370d8b2e0ac8b83c4d53bf', + 'af45d2e376484031617f78d2b58a6b1b9c7ef464f5a01b47e42ec3736322445e8e2240ca5e69e2c78b3239ecfab21649', + '1383e82e28286b91f4cc7afbd13d5b5c6f887c05e7c4542484043a37a5fe45802a9470fb663bd7b6570fe2f503fc92f5', + '3e8a69b7783c25851933ab6290af6ca77a9981480850009cc5577c6e1f573b4e6801dd23c4a7d679ccf8a386c674cffb', + '10e0150a42d0ae6f9d3f55da7a8261c383b024c8d81b40e95d120acfd53fb018af5e77846ad99451059f0579cb9a718b', + '69d2e2f55de9f09878f04d23d8670d49cb734825cdb9cd9e72e446171a43540b90e17cf086e6fa3a599382a286c61340', + '34f065bdedc2487c30a634d9a49cf42116f78bb386ea4d498aea05c0077f05373cfdaa9b59a7b0481bced9e3f55016a9', + ] + mut result := '' + for i, key in hmac.keys { + result = hmac.new(key, hmac.data[i], sha512.sum384, sha512.block_size).hex() + assert result == sha384_expected_results[i] + } +} + +fn test_hmac_sha512() { + sha512_expected_results := [ + '7641c48a3b4aa8f887c07b3e83f96affb89c978fed8c96fcbbf4ad596eebfe496f9f16da6cd080ba393c6f365ad72b50d15c71bfb1d6b81f66a911786c6ce932', + '164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737', + 'ad9b5c7de72693737cd5e9d9f41170d18841fec1201c1c1b02e05cae116718009f771cad9946ddbf7e3cde3e818d9ae85d91b2badae94172d096a44a79c91e86', + 'b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3dba91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd', + 'da2c03a1f8d34ce536b246c9dc47281d7052d3f82a7b4f6dfe9ee9f5accdae02dd72f9b89324f25f9b8276a2e3d31c0a87b8b6c1dcefd7602cc881a7d120e3fd', + '132c9ebc32531071f6c4d9e8842291e9403e5940f813170a3ba3a0dd6c055c8b8ca587b24c56c47f3c1f2fb8ee8f9fbc8d92deed0f83426be3e8a2e9056778b3', + '09441cda584ed2f4d2f5b519c71baf3c79cce19dfc89a548e73b3bb382a9124d6e792b77bf57903ff5858e5d111d15f45d6fd118eea023f28d2eb234ebe62f85', + ] + mut result := '' + for i, key in hmac.keys { + result = hmac.new(key, hmac.data[i], sha512.sum512, sha512.block_size).hex() + assert result == sha512_expected_results[i] + } +} + +fn test_hmac_equal() { + mac1_1 := '7641c48a3b4aa8f887c07b3e83f96affb89c978fed8c96fcbbf4ad596eebfe496f9f16da6cd080ba393c6f365ad72b50d15c71bfb1d6b81f66a911786c6ce932'.bytes() + mac1_2 := '7641c48a3b4aa8f887c07b3e83f96affb89c978fed8c96fcbbf4ad596eebfe496f9f16da6cd080ba393c6f365ad72b50d15c71bfb1d6b81f66a911786c6ce932'.bytes() + mac2_1 := '164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737'.bytes() + mac2_2 := '164b7a7bfcf819e2e395fbe73b56e0a387bd64222e831fd610270cd7ea2505549758bf75c05a994a6d034f65f8f0e6fdcaeab1a34d4a6b4b636e070a38bce737'.bytes() + assert hmac.equal(mac1_1, mac1_2) + assert hmac.equal(mac2_1, mac2_2) + assert !hmac.equal(mac1_1, mac2_1) + assert !hmac.equal(mac1_1, mac2_2) +} + +// not yet supported by crypto module +// sha3_224_expected_results := [ +// 'f68da7f7bf577de799bb1224b7acfef9e8de015a63475ed5904a4693' +// '7fdb8dd88bd2f60d1b798634ad386811c2cfc85bfaf5d52bbace5e66' +// '3c9b90dbbd88c2af888fb1b43ec9d424c7fbf0d2b9d0140952b110b5' +// 'a9d7685a19c4e0dbd9df2556cc8a7d2a7733b67625ce594c78270eeb' +// 'f865c4fe082e4dd1873a9d83e1ca3bf827c3256d91274574a8b66f13' +// '852c3fb04b18a04df20c007e608027c44230fdd440cf7a50a0bc4fd9' +// '14db797c7f4c69fd1d4c0ababeb9f90971fc62622cc7852dee156265' +// ] +// sha3_256_expected_results := [ +// '874d1d4e6e8302439bf707052e5d787d92bffcf0715853784e30da740a81e198' +// 'c7d4072e788877ae3596bbb0da73b887c9171f93095b294ae857fbe2645e1ba5' +// 'b55008323817b4df9398f32fd09d3ce624a3ac2a4f329c3b750c47647990de2a' +// '57366a45e2305321a4bc5aa5fe2ef8a921f6af8273d7fe7be6cfedb3f0aea6d7' +// 'a0cd54f140b61480cd22120d600e30c2508c4ae0d335fd69770f2b4ddc80cd19' +// '016a1a59d67944c350d992a9bc1e8e7f6d1ace9c9ff6be92eda103961fe897ab' +// '415c2b5cde6b2aecd637fa2384aa87e5a0b0c5bc20d53550bbac5474b18769bf' +// ] +// sha3_384_expected_results := [ +// 'b34fdb255dc7fb7f0c4bb2c1caeb0379b81ece60ec1b3cb2c5ec509141fcb77ca16d1e06f93049734be4948e24b932e3' +// 'f1101f8cbf9766fd6764d2ed61903f21ca9b18f57cf3e1a23ca13508a93243ce48c045dc007f26a21b3f5e0e9df4c20a' +// '5bd8a0b98f9f4201eaec41d01fd1e274c266a2517527c1879b0460a692e1a430aefb82f0c9aea33406582ffeeef0bba6' +// '3a5d7a879702c086bc96d1dd8aa15d9c46446b95521311c606fdc4e308f4b984da2d0f9449b3ba8425ec7fb8c31bc136' +// '0cdfc206fd95ca1f27e8e8bd443164814460ca50f8d34d776b18f9eb300231a3d5bace731f694a59faa84c2e4ae7e235' +// '7172a2a2bb002c22669a2f85b8faaacfcc4e8a19d47ef5ee7a97f79bf21e1d89403ab3768b43929f12eded01e3ddd604' +// '45081e207f796f372aff5a098249f52d045e350ed5c805b3445a79ad0d4931c4b86d41bd1bb2ac935d1b32c344d56709' +// ] +// sha3_512_expected_results := [ +// 'd2d9588c7e7886b08e09b56a7ac9d7e30a4badf13b37a041f5dfde34d87c086b5db1a7ec679bcfce81fa2eee982573c01dfb8d988e302f78d7b20d7d7ac2dfd7' +// '5a4bfeab6166427c7a3647b747292b8384537cdb89afb3bf5665e4c5e709350b287baec921fd7ca0ee7a0c31d022a95e1fc92ba9d77df883960275beb4e62024' +// 'f25055024a17dfe15a25d6c40b00f45e8548f641844f2288170430ba0b7889bfaf04d9398121d165375300fe813f3cb6db9639921dcfb712b9177b8f5261d474' +// 'b27eab1d6e8d87461c29f7f5739dd58e98aa35f8e823ad38c5492a2088fa0281993bbfff9a0e9c6bf121ae9ec9bb09d84a5ebac817182ea974673fb133ca0d1d' +// '69e9553223ede3637f08f9cc01ea9ded8f3b4202b5cc1feb60071e195a942f0ca0fa1cd70d3f1f9f24b2e18057b3001e7d5160e61eb6099f75ea4e0d6b849bd2' +// 'eea495d39d9a07154b1266b028e233b9fd84de884ac8f0578e679f095ef14da96d0a355ed4738565884aec755c1b3f5ff09a918b437f6526e17dd8e77f425b95' +// '1488670c683959b5304fa17c172bea81724a249b44981a3eb52cfc66ff0758b7cd1204745131b8adbc714db7fc4550ce26af5f2326067ad1e699f05cae8bb792' +// ] +// blake2b_expected_results := [ +// 'be2c398cbd5eef033031f9cee2caba52b280604f4afabf86de21973398c821fd120de5277f08955234182989c68b640af7dfa8cb9228eef4b48ffe768ef595eb' +// '6ff884f8ddc2a6586b3c98a4cd6ebdf14ec10204b6710073eb5865ade37a2643b8807c1335d107ecdb9ffeaeb6828c4625ba172c66379efcd222c2de11727ab4' +// '548fc7c3d5bd5a0ac74c5fe582037a259c774b81fb791d6c86e675d03b361939e21ea0fb9c6401df22170ebf8017d908675bbcf65911025a1ab5d271a372f43f' +// 'e5dbb6de2fee42a1caa06e4e7b84ce408ffa5c4a9de2632eca769cde8875014c72d0720feaf53f76e6a180357f528d7bf484fa3a14e8cc1f0f3bada717b43491' +// '057f3bcc3511aa9627d96ab2ad02ec3dc86741514d19a3c9b10e539b0ca7c2587d9d04118636a67e18871633ecf8705a3ef6697cf6b64339f71b8cdffab89e34' +// '368aba23ca42648157771936b436a0ecb3d83e5fe21e020fef2a08dc9e59739ea919a8d0f46c45b99491f426f1e7c62352d9d67c066571d74e191b69bedaf718' +// 'f1c9b64e121330c512dc31e0d4a2fc84b7ca5be64e08934a7fc4640c4a1f5cc3c1f34d811c8079cc2df65a4e5d68baf833a1ec558546abeaa7d564840618db7b' +// ] +// blake2s_expected_results := [ +// '139cd736b926dae4853aab90655120e0305c476fde978166e472c7c8698c21b1' +// '464434dcbece095d456a1d62d6ec56f898e625a39e5c52bdf94daf111bad83aa' +// '90b6281e2f3038c9056af0b4a7e763cae6fe5d9eb4386a0ec95237890c104ff0' +// '92394bc2486bea31db01c74be46332edd1499e9700ea41df0670df79fcc0f7c6' +// '97a771b4e4e2fd4d4d0fd8aca2a0663ad8ad4a463cabbf603bf837dd84dec050' +// '41202b7be1fd03f84658f4c18a08b43ddbbd73eb012750c8d1ebc8601f1e064c' +// '467201ef5997a3442932b318083488cf9aa1d89bef2146154b4816d34863e33d' +// ] diff --git a/v_windows/v/old/vlib/crypto/internal/subtle/aliasing.v b/v_windows/v/old/vlib/crypto/internal/subtle/aliasing.v new file mode 100644 index 0000000..e09748e --- /dev/null +++ b/v_windows/v/old/vlib/crypto/internal/subtle/aliasing.v @@ -0,0 +1,29 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +// Package subtle implements functions that are often useful in cryptographic +// code but require careful thought to use correctly. +module subtle + +// NOTE: require unsafe in future +// any_overlap reports whether x and y share memory at any (not necessarily +// corresponding) index. The memory beyond the slice length is ignored. +pub fn any_overlap(x []byte, y []byte) bool { + // NOTE: Remember to come back to this (joe-c) + return x.len > 0 && y.len > 0 && // &x.data[0] <= &y.data[y.len-1] && + // &y.data[0] <= &x.data[x.len-1] + unsafe { &x[0] <= &y[y.len - 1] && &y[0] <= &x[x.len - 1] } +} + +// inexact_overlap reports whether x and y share memory at any non-corresponding +// index. The memory beyond the slice length is ignored. Note that x and y can +// have different lengths and still not have any inexact overlap. +// +// inexact_overlap can be used to implement the requirements of the crypto/cipher +// AEAD, Block, BlockMode and Stream interfaces. +pub fn inexact_overlap(x []byte, y []byte) bool { + if x.len == 0 || y.len == 0 || unsafe { &x[0] == &y[0] } { + return false + } + return any_overlap(x, y) +} diff --git a/v_windows/v/old/vlib/crypto/internal/subtle/comparison.v b/v_windows/v/old/vlib/crypto/internal/subtle/comparison.v new file mode 100644 index 0000000..e7dd162 --- /dev/null +++ b/v_windows/v/old/vlib/crypto/internal/subtle/comparison.v @@ -0,0 +1,53 @@ +module subtle + +// constant_time_byte_eq returns 1 when x == y. +pub fn constant_time_byte_eq(x byte, y byte) int { + return int((u32(x ^ y) - 1) >> 31) +} + +// constant_time_eq returns 1 when x == y. +pub fn constant_time_eq(x int, y int) int { + return int((u64(u32(x ^ y)) - 1) >> 63) +} + +// constant_time_select returns x when v == 1, and y when v == 0. +// it is undefined when v is any other value +pub fn constant_time_select(v int, x int, y int) int { + return (~(v - 1) & x) | ((v - 1) & y) +} + +// constant_time_compare returns 1 when x and y have equal contents. +// The runtime of this function is proportional of the length of x and y. +// It is *NOT* dependent on their content. +pub fn constant_time_compare(x []byte, y []byte) int { + if x.len != y.len { + return 0 + } + mut v := byte(0) + for i in 0 .. x.len { + v |= x[i] ^ y[i] + } + return constant_time_byte_eq(v, 0) +} + +// constant_time_copy copies the contents of y into x, when v == 1. +// When v == 0, x is left unchanged. this function is undefined, when +// v takes any other value +pub fn constant_time_copy(v int, mut x []byte, y []byte) { + if x.len != y.len { + panic('subtle: arrays have different lengths') + } + xmask := byte(v - 1) + ymask := byte(~(v - 1)) + for i := 0; i < x.len; i++ { + x[i] = x[i] & xmask | y[i] & ymask + } +} + +// constant_time_less_or_eq returns 1 if x <= y, and 0 otherwise. +// it is undefined when x or y are negative, or > (2^32 - 1) +pub fn constant_time_less_or_eq(x int, y int) int { + x32 := int(x) + y32 := int(y) + return int(((x32 - y32 - 1) >> 31) & 1) +} diff --git a/v_windows/v/old/vlib/crypto/internal/subtle/comparison_test.v b/v_windows/v/old/vlib/crypto/internal/subtle/comparison_test.v new file mode 100644 index 0000000..b339746 --- /dev/null +++ b/v_windows/v/old/vlib/crypto/internal/subtle/comparison_test.v @@ -0,0 +1,65 @@ +module subtle + +fn test_constant_time_byte_eq() { + assert constant_time_byte_eq(0, 0) == 1 + assert constant_time_byte_eq(1, 1) == 1 + assert constant_time_byte_eq(255, 255) == 1 + assert constant_time_byte_eq(255, 1) == 0 + assert constant_time_byte_eq(1, 255) == 0 + assert constant_time_byte_eq(2, 1) == 0 +} + +fn test_constant_time_eq() { + assert constant_time_eq(0, 0) == 1 + assert constant_time_eq(255, 255) == 1 + assert constant_time_eq(65536, 65536) == 1 + assert constant_time_eq(-1, -1) == 1 + assert constant_time_eq(-256, -256) == 1 + assert constant_time_eq(0, 1) == 0 +} + +fn test_constant_time_select() { + assert constant_time_select(1, 1, 0) == 1 + assert constant_time_select(1, 1, 255) == 1 + assert constant_time_select(1, 1, 255 * 255) == 1 + assert constant_time_select(1, 2, 0) == 2 + assert constant_time_select(1, 2, 255) == 2 + assert constant_time_select(1, 2, 255 * 255) == 2 + // + assert constant_time_select(0, 1, 0) == 0 + assert constant_time_select(0, 1, 255) == 255 + assert constant_time_select(0, 1, 255 * 255) == 255 * 255 + assert constant_time_select(0, 2, 0) == 0 + assert constant_time_select(0, 2, 255) == 255 + assert constant_time_select(0, 2, 255 * 255) == 255 * 255 +} + +fn test_constant_time_compare() { + assert constant_time_compare([byte(1), 2, 3], [byte(1), 2, 3]) == 1 + assert constant_time_compare([byte(1), 2, 3], [byte(1), 2, 9]) == 0 + assert constant_time_compare([byte(1), 2, 3], [byte(1), 2, 3, 4]) == 0 + assert constant_time_compare([byte(1), 2, 3], [byte(1), 2]) == 0 +} + +fn test_constant_time_copy() { + y := [byte(3), 4, 5] + mut x := [byte(0), 0, 0] + constant_time_copy(0, mut x, y) + assert x == [byte(0), 0, 0] + constant_time_copy(1, mut x, y) + assert x == y + assert x == [byte(3), 4, 5] +} + +fn test_constant_time_less_or_eq() { + assert constant_time_less_or_eq(1, 1) == 1 + assert constant_time_less_or_eq(1, 2) == 1 + assert constant_time_less_or_eq(1, 3) == 1 + assert constant_time_less_or_eq(255, 255) == 1 + assert constant_time_less_or_eq(255, 256) == 1 + assert constant_time_less_or_eq(255, 257) == 1 + assert constant_time_less_or_eq(1, 0) == 0 + assert constant_time_less_or_eq(2, 1) == 0 + assert constant_time_less_or_eq(3, 2) == 0 + assert constant_time_less_or_eq(255, 3) == 0 +} diff --git a/v_windows/v/old/vlib/crypto/md5/md5.v b/v_windows/v/old/vlib/crypto/md5/md5.v new file mode 100644 index 0000000..17796b1 --- /dev/null +++ b/v_windows/v/old/vlib/crypto/md5/md5.v @@ -0,0 +1,154 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +// Package md5 implements the MD5 hash algorithm as defined in RFC 1321. +// MD5 is cryptographically broken and should not be used for secure +// applications. +// Based off: https://github.com/golang/go/blob/master/src/crypto/md5 +// Last commit: https://github.com/golang/go/commit/ed7f323c8f4f6bc61a75146bf34f5b8f73063a17 +module md5 + +import encoding.binary + +pub const ( + // The size of an MD5 checksum in bytes. + size = 16 + // The blocksize of MD5 in bytes. + block_size = 64 +) + +const ( + init0 = 0x67452301 + init1 = 0xEFCDAB89 + init2 = 0x98BADCFE + init3 = 0x10325476 +) + +// Digest represents the partial evaluation of a checksum. +struct Digest { +mut: + s []u32 + x []byte + nx int + len u64 +} + +fn (mut d Digest) reset() { + d.s = []u32{len: (4)} + d.x = []byte{len: md5.block_size} + d.s[0] = u32(md5.init0) + d.s[1] = u32(md5.init1) + d.s[2] = u32(md5.init2) + d.s[3] = u32(md5.init3) + d.nx = 0 + d.len = 0 +} + +// new returns a new Digest (implementing hash.Hash) computing the MD5 checksum. +pub fn new() &Digest { + mut d := &Digest{} + d.reset() + return d +} + +// write writes the contents of `p_` to the internal hash representation. +pub fn (mut d Digest) write(p_ []byte) ?int { + unsafe { + mut p := p_ + nn := p.len + d.len += u64(nn) + if d.nx > 0 { + n := copy(d.x[d.nx..], p) + d.nx += n + if d.nx == md5.block_size { + block(mut d, d.x) + d.nx = 0 + } + if n >= p.len { + p = [] + } else { + p = p[n..] + } + } + if p.len >= md5.block_size { + n := p.len & ~(md5.block_size - 1) + block(mut d, p[..n]) + if n >= p.len { + p = [] + } else { + p = p[n..] + } + } + if p.len > 0 { + d.nx = copy(d.x, p) + } + return nn + } +} + +// sum returns the md5 sum of the bytes in `b_in`. +pub fn (d &Digest) sum(b_in []byte) []byte { + // Make a copy of d so that caller can keep writing and summing. + mut d0 := *d + hash := d0.checksum() + mut b_out := b_in.clone() + for b in hash { + b_out << b + } + return b_out +} + +// checksum returns the byte checksum of the `Digest`. +pub fn (mut d Digest) checksum() []byte { + // Append 0x80 to the end of the message and then append zeros + // until the length is a multiple of 56 bytes. Finally append + // 8 bytes representing the message length in bits. + // + // 1 byte end marker :: 0-63 padding bytes :: 8 byte length + // tmp := [1 + 63 + 8]byte{0x80} + mut tmp := []byte{len: (1 + 63 + 8)} + tmp[0] = 0x80 + pad := ((55 - d.len) % 64) // calculate number of padding bytes + binary.little_endian_put_u64(mut tmp[1 + pad..], d.len << 3) // append length in bits + d.write(tmp[..1 + pad + 8]) or { panic(err) } + // The previous write ensures that a whole number of + // blocks (i.e. a multiple of 64 bytes) have been hashed. + if d.nx != 0 { + panic('d.nx != 0') + } + mut digest := []byte{len: md5.size} + binary.little_endian_put_u32(mut digest, d.s[0]) + binary.little_endian_put_u32(mut digest[4..], d.s[1]) + binary.little_endian_put_u32(mut digest[8..], d.s[2]) + binary.little_endian_put_u32(mut digest[12..], d.s[3]) + return digest +} + +// sum returns the MD5 checksum of the data. +pub fn sum(data []byte) []byte { + mut d := new() + d.write(data) or { panic(err) } + return d.checksum() +} + +fn block(mut dig Digest, p []byte) { + // For now just use block_generic until we have specific + // architecture optimized versions + block_generic(mut dig, p) +} + +// size returns the size of the checksum in bytes. +pub fn (d &Digest) size() int { + return md5.size +} + +// block_size returns the block size of the checksum in bytes. +pub fn (d &Digest) block_size() int { + return md5.block_size +} + +// hexhash returns a hexadecimal MD5 hash sum `string` of `s`. +// Example: assert md5.hexhash('V') == '5206560a306a2e085a437fd258eb57ce' +pub fn hexhash(s string) string { + return sum(s.bytes()).hex() +} diff --git a/v_windows/v/old/vlib/crypto/md5/md5_test.v b/v_windows/v/old/vlib/crypto/md5/md5_test.v new file mode 100644 index 0000000..fd43c1c --- /dev/null +++ b/v_windows/v/old/vlib/crypto/md5/md5_test.v @@ -0,0 +1,8 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +import crypto.md5 + +fn test_crypto_md5() { + assert md5.sum('this is a md5 checksum.'.bytes()).hex() == '6fb421ff99036547655984da12973431' +} diff --git a/v_windows/v/old/vlib/crypto/md5/md5block_generic.v b/v_windows/v/old/vlib/crypto/md5/md5block_generic.v new file mode 100644 index 0000000..ebc4a8d --- /dev/null +++ b/v_windows/v/old/vlib/crypto/md5/md5block_generic.v @@ -0,0 +1,132 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. + +// This is the generic version with no architecture optimizations. +// In its own file so that an architecture +// optimized verision can be substituted + +module md5 + +import math.bits +import encoding.binary + +fn block_generic(mut dig Digest, p []byte) { + // load state + mut a := dig.s[0] + mut b := dig.s[1] + mut c := dig.s[2] + mut d := dig.s[3] + + for i := 0; i <= p.len - block_size; i += block_size { + mut q := p[i..] + q = q[..block_size] + // save current state + aa := a + bb := b + cc := c + dd := d + + // load input block + x0 := binary.little_endian_u32(q[4 * 0x0..]) + x1 := binary.little_endian_u32(q[4 * 0x1..]) + x2 := binary.little_endian_u32(q[4 * 0x2..]) + x3 := binary.little_endian_u32(q[4 * 0x3..]) + x4 := binary.little_endian_u32(q[4 * 0x4..]) + x5 := binary.little_endian_u32(q[4 * 0x5..]) + x6 := binary.little_endian_u32(q[4 * 0x6..]) + x7 := binary.little_endian_u32(q[4 * 0x7..]) + x8 := binary.little_endian_u32(q[4 * 0x8..]) + x9 := binary.little_endian_u32(q[4 * 0x9..]) + xa := binary.little_endian_u32(q[4 * 0xa..]) + xb := binary.little_endian_u32(q[4 * 0xb..]) + xc := binary.little_endian_u32(q[4 * 0xc..]) + xd := binary.little_endian_u32(q[4 * 0xd..]) + xe := binary.little_endian_u32(q[4 * 0xe..]) + xf := binary.little_endian_u32(q[4 * 0xf..]) + + // round 1 + a = b + bits.rotate_left_32((((c ^ d) & b) ^ d) + a + x0 + u32(0xd76aa478), 7) + d = a + bits.rotate_left_32((((b ^ c) & a) ^ c) + d + x1 + u32(0xe8c7b756), 12) + c = d + bits.rotate_left_32((((a ^ b) & d) ^ b) + c + x2 + u32(0x242070db), 17) + b = c + bits.rotate_left_32((((d ^ a) & c) ^ a) + b + x3 + u32(0xc1bdceee), 22) + a = b + bits.rotate_left_32((((c ^ d) & b) ^ d) + a + x4 + u32(0xf57c0faf), 7) + d = a + bits.rotate_left_32((((b ^ c) & a) ^ c) + d + x5 + u32(0x4787c62a), 12) + c = d + bits.rotate_left_32((((a ^ b) & d) ^ b) + c + x6 + u32(0xa8304613), 17) + b = c + bits.rotate_left_32((((d ^ a) & c) ^ a) + b + x7 + u32(0xfd469501), 22) + a = b + bits.rotate_left_32((((c ^ d) & b) ^ d) + a + x8 + u32(0x698098d8), 7) + d = a + bits.rotate_left_32((((b ^ c) & a) ^ c) + d + x9 + u32(0x8b44f7af), 12) + c = d + bits.rotate_left_32((((a ^ b) & d) ^ b) + c + xa + u32(0xffff5bb1), 17) + b = c + bits.rotate_left_32((((d ^ a) & c) ^ a) + b + xb + u32(0x895cd7be), 22) + a = b + bits.rotate_left_32((((c ^ d) & b) ^ d) + a + xc + u32(0x6b901122), 7) + d = a + bits.rotate_left_32((((b ^ c) & a) ^ c) + d + xd + u32(0xfd987193), 12) + c = d + bits.rotate_left_32((((a ^ b) & d) ^ b) + c + xe + u32(0xa679438e), 17) + b = c + bits.rotate_left_32((((d ^ a) & c) ^ a) + b + xf + u32(0x49b40821), 22) + + // round 2 + a = b + bits.rotate_left_32((((b ^ c) & d) ^ c) + a + x1 + u32(0xf61e2562), 5) + d = a + bits.rotate_left_32((((a ^ b) & c) ^ b) + d + x6 + u32(0xc040b340), 9) + c = d + bits.rotate_left_32((((d ^ a) & b) ^ a) + c + xb + u32(0x265e5a51), 14) + b = c + bits.rotate_left_32((((c ^ d) & a) ^ d) + b + x0 + u32(0xe9b6c7aa), 20) + a = b + bits.rotate_left_32((((b ^ c) & d) ^ c) + a + x5 + u32(0xd62f105d), 5) + d = a + bits.rotate_left_32((((a ^ b) & c) ^ b) + d + xa + u32(0x02441453), 9) + c = d + bits.rotate_left_32((((d ^ a) & b) ^ a) + c + xf + u32(0xd8a1e681), 14) + b = c + bits.rotate_left_32((((c ^ d) & a) ^ d) + b + x4 + u32(0xe7d3fbc8), 20) + a = b + bits.rotate_left_32((((b ^ c) & d) ^ c) + a + x9 + u32(0x21e1cde6), 5) + d = a + bits.rotate_left_32((((a ^ b) & c) ^ b) + d + xe + u32(0xc33707d6), 9) + c = d + bits.rotate_left_32((((d ^ a) & b) ^ a) + c + x3 + u32(0xf4d50d87), 14) + b = c + bits.rotate_left_32((((c ^ d) & a) ^ d) + b + x8 + u32(0x455a14ed), 20) + a = b + bits.rotate_left_32((((b ^ c) & d) ^ c) + a + xd + u32(0xa9e3e905), 5) + d = a + bits.rotate_left_32((((a ^ b) & c) ^ b) + d + x2 + u32(0xfcefa3f8), 9) + c = d + bits.rotate_left_32((((d ^ a) & b) ^ a) + c + x7 + u32(0x676f02d9), 14) + b = c + bits.rotate_left_32((((c ^ d) & a) ^ d) + b + xc + u32(0x8d2a4c8a), 20) + + // round 3 + a = b + bits.rotate_left_32((b ^ c ^ d) + a + x5 + u32(0xfffa3942), 4) + d = a + bits.rotate_left_32((a ^ b ^ c) + d + x8 + u32(0x8771f681), 11) + c = d + bits.rotate_left_32((d ^ a ^ b) + c + xb + u32(0x6d9d6122), 16) + b = c + bits.rotate_left_32((c ^ d ^ a) + b + xe + u32(0xfde5380c), 23) + a = b + bits.rotate_left_32((b ^ c ^ d) + a + x1 + u32(0xa4beea44), 4) + d = a + bits.rotate_left_32((a ^ b ^ c) + d + x4 + u32(0x4bdecfa9), 11) + c = d + bits.rotate_left_32((d ^ a ^ b) + c + x7 + u32(0xf6bb4b60), 16) + b = c + bits.rotate_left_32((c ^ d ^ a) + b + xa + u32(0xbebfbc70), 23) + a = b + bits.rotate_left_32((b ^ c ^ d) + a + xd + u32(0x289b7ec6), 4) + d = a + bits.rotate_left_32((a ^ b ^ c) + d + x0 + u32(0xeaa127fa), 11) + c = d + bits.rotate_left_32((d ^ a ^ b) + c + x3 + u32(0xd4ef3085), 16) + b = c + bits.rotate_left_32((c ^ d ^ a) + b + x6 + u32(0x04881d05), 23) + a = b + bits.rotate_left_32((b ^ c ^ d) + a + x9 + u32(0xd9d4d039), 4) + d = a + bits.rotate_left_32((a ^ b ^ c) + d + xc + u32(0xe6db99e5), 11) + c = d + bits.rotate_left_32((d ^ a ^ b) + c + xf + u32(0x1fa27cf8), 16) + b = c + bits.rotate_left_32((c ^ d ^ a) + b + x2 + u32(0xc4ac5665), 23) + + // round 4 + a = b + bits.rotate_left_32((c ^ (b | ~d)) + a + x0 + u32(0xf4292244), 6) + d = a + bits.rotate_left_32((b ^ (a | ~c)) + d + x7 + u32(0x432aff97), 10) + c = d + bits.rotate_left_32((a ^ (d | ~b)) + c + xe + u32(0xab9423a7), 15) + b = c + bits.rotate_left_32((d ^ (c | ~a)) + b + x5 + u32(0xfc93a039), 21) + a = b + bits.rotate_left_32((c ^ (b | ~d)) + a + xc + u32(0x655b59c3), 6) + d = a + bits.rotate_left_32((b ^ (a | ~c)) + d + x3 + u32(0x8f0ccc92), 10) + c = d + bits.rotate_left_32((a ^ (d | ~b)) + c + xa + u32(0xffeff47d), 15) + b = c + bits.rotate_left_32((d ^ (c | ~a)) + b + x1 + u32(0x85845dd1), 21) + a = b + bits.rotate_left_32((c ^ (b | ~d)) + a + x8 + u32(0x6fa87e4f), 6) + d = a + bits.rotate_left_32((b ^ (a | ~c)) + d + xf + u32(0xfe2ce6e0), 10) + c = d + bits.rotate_left_32((a ^ (d | ~b)) + c + x6 + u32(0xa3014314), 15) + b = c + bits.rotate_left_32((d ^ (c | ~a)) + b + xd + u32(0x4e0811a1), 21) + a = b + bits.rotate_left_32((c ^ (b | ~d)) + a + x4 + u32(0xf7537e82), 6) + d = a + bits.rotate_left_32((b ^ (a | ~c)) + d + xb + u32(0xbd3af235), 10) + c = d + bits.rotate_left_32((a ^ (d | ~b)) + c + x2 + u32(0x2ad7d2bb), 15) + b = c + bits.rotate_left_32((d ^ (c | ~a)) + b + x9 + u32(0xeb86d391), 21) + + // add saved state + a += aa + b += bb + c += cc + d += dd + } + + // save state + dig.s[0] = a + dig.s[1] = b + dig.s[2] = c + dig.s[3] = d +} diff --git a/v_windows/v/old/vlib/crypto/rand/crypto_rand_read_test.v b/v_windows/v/old/vlib/crypto/rand/crypto_rand_read_test.v new file mode 100644 index 0000000..824923d --- /dev/null +++ b/v_windows/v/old/vlib/crypto/rand/crypto_rand_read_test.v @@ -0,0 +1,15 @@ +import crypto.rand + +fn test_reading() ? { + a := rand.read(32) ? + // dump(a.hex()) + assert a.len == 32 + mut histogram := [256]int{} + for b in a { + histogram[b]++ + } + // dump(histogram) + for h in histogram { + assert h < 10 + } +} diff --git a/v_windows/v/old/vlib/crypto/rand/rand.v b/v_windows/v/old/vlib/crypto/rand/rand.v new file mode 100644 index 0000000..0703558 --- /dev/null +++ b/v_windows/v/old/vlib/crypto/rand/rand.v @@ -0,0 +1,10 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. + +module rand + +struct ReadError { + msg string = 'crypto.rand.read() error reading random bytes' + code int +} diff --git a/v_windows/v/old/vlib/crypto/rand/rand_darwin.c.v b/v_windows/v/old/vlib/crypto/rand/rand_darwin.c.v new file mode 100644 index 0000000..a53198b --- /dev/null +++ b/v_windows/v/old/vlib/crypto/rand/rand_darwin.c.v @@ -0,0 +1,21 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. + +module rand + +#include + +#flag darwin -framework Security + +fn C.SecRandomCopyBytes(rnd C.SecRandomRef, count size_t, bytes voidptr) int + +// read returns an array of `bytes_needed` random bytes read from the OS. +pub fn read(bytes_needed int) ?[]byte { + mut buffer := []byte{len: bytes_needed} + status := C.SecRandomCopyBytes(C.SecRandomRef(0), bytes_needed, buffer.data) + if status != 0 { + return IError(&ReadError{}) + } + return buffer +} diff --git a/v_windows/v/old/vlib/crypto/rand/rand_default.c.v b/v_windows/v/old/vlib/crypto/rand/rand_default.c.v new file mode 100644 index 0000000..2e1e823 --- /dev/null +++ b/v_windows/v/old/vlib/crypto/rand/rand_default.c.v @@ -0,0 +1,9 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module rand + +// read returns an array of `bytes_needed` random bytes read from the OS. +pub fn read(bytes_needed int) ?[]byte { + return error('rand.read is not implemented on this platform') +} diff --git a/v_windows/v/old/vlib/crypto/rand/rand_linux.c.v b/v_windows/v/old/vlib/crypto/rand/rand_linux.c.v new file mode 100644 index 0000000..52ff3da --- /dev/null +++ b/v_windows/v/old/vlib/crypto/rand/rand_linux.c.v @@ -0,0 +1,39 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module rand + +#include + +const ( + read_batch_size = 256 +) + +// read returns an array of `bytes_needed` random bytes read from the OS. +pub fn read(bytes_needed int) ?[]byte { + mut buffer := unsafe { malloc_noscan(bytes_needed) } + mut bytes_read := 0 + mut remaining_bytes := bytes_needed + // getrandom syscall wont block if requesting <= 256 bytes + for bytes_read < bytes_needed { + batch_size := if remaining_bytes > rand.read_batch_size { + rand.read_batch_size + } else { + remaining_bytes + } + rbytes := unsafe { getrandom(batch_size, buffer + bytes_read) } + if rbytes == -1 { + unsafe { free(buffer) } + return IError(&ReadError{}) + } + bytes_read += rbytes + } + return unsafe { buffer.vbytes(bytes_needed) } +} + +fn getrandom(bytes_needed int, buffer voidptr) int { + if bytes_needed > rand.read_batch_size { + panic('getrandom() dont request more than $rand.read_batch_size bytes at once.') + } + return unsafe { C.syscall(C.SYS_getrandom, buffer, bytes_needed, 0) } +} diff --git a/v_windows/v/old/vlib/crypto/rand/rand_solaris.c.v b/v_windows/v/old/vlib/crypto/rand/rand_solaris.c.v new file mode 100644 index 0000000..8d0ba0c --- /dev/null +++ b/v_windows/v/old/vlib/crypto/rand/rand_solaris.c.v @@ -0,0 +1,42 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. + +module rand + +#include + +fn C.getrandom(p &byte, n size_t, flags u32) int + +const ( + read_batch_size = 256 +) + +// read returns an array of `bytes_needed` random bytes read from the OS. +pub fn read(bytes_needed int) ?[]byte { + mut buffer := unsafe { malloc_noscan(bytes_needed) } + mut bytes_read := 0 + mut remaining_bytes := bytes_needed + // getrandom syscall wont block if requesting <= 256 bytes + for bytes_read < bytes_needed { + batch_size := if remaining_bytes > rand.read_batch_size { + rand.read_batch_size + } else { + remaining_bytes + } + rbytes := unsafe { getrandom(batch_size, buffer + bytes_read) } + if rbytes == -1 { + unsafe { free(buffer) } + return IError(&ReadError{}) + } + bytes_read += rbytes + } + return unsafe { buffer.vbytes(bytes_needed) } +} + +fn v_getrandom(bytes_needed int, buffer voidptr) int { + if bytes_needed > rand.read_batch_size { + panic('getrandom() dont request more than $rand.read_batch_size bytes at once.') + } + return C.getrandom(buffer, bytes_needed, 0) +} diff --git a/v_windows/v/old/vlib/crypto/rand/rand_windows.c.v b/v_windows/v/old/vlib/crypto/rand/rand_windows.c.v new file mode 100644 index 0000000..075a3ed --- /dev/null +++ b/v_windows/v/old/vlib/crypto/rand/rand_windows.c.v @@ -0,0 +1,25 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. + +module rand + +#flag windows -Llibraries/bcrypt +#flag windows -lbcrypt +#include + +const ( + status_success = 0x00000000 + bcrypt_use_system_preferred_rng = 0x00000002 +) + +// read returns an array of `bytes_needed` random bytes read from the OS. +pub fn read(bytes_needed int) ?[]byte { + mut buffer := []byte{len: bytes_needed} + // use bcrypt_use_system_preferred_rng because we passed null as algo + status := C.BCryptGenRandom(0, buffer.data, bytes_needed, rand.bcrypt_use_system_preferred_rng) + if status != rand.status_success { + return IError(&ReadError{}) + } + return buffer +} diff --git a/v_windows/v/old/vlib/crypto/rand/utils.v b/v_windows/v/old/vlib/crypto/rand/utils.v new file mode 100644 index 0000000..31eaf2d --- /dev/null +++ b/v_windows/v/old/vlib/crypto/rand/utils.v @@ -0,0 +1,55 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. + +module rand + +import math.bits +import encoding.binary + +// int_u64 returns a random unsigned 64-bit integer `u64` read from a real OS source of entropy. +pub fn int_u64(max u64) ?u64 { + bitlen := bits.len_64(max) + if bitlen == 0 { + return u64(0) + } + k := (bitlen + 7) / 8 + mut b := u64(bitlen % 8) + if b == u64(0) { + b = u64(8) + } + mut n := u64(0) + for { + mut bytes := read(k) ? + bytes[0] &= byte(int(u64(1) << b) - 1) + x := bytes_to_u64(bytes) + n = x[0] + // NOTE: maybe until we have bigint could do it another way? + // if x.len > 1 { + // n = u64(u32(x[1])<= ws; k++ { + z[k] = binary.big_endian_u64(b[i - ws..i]) + i -= ws + } + if i > 0 { + mut d := u64(0) + for s := u64(0); i > 0; s += u64(8) { + d |= u64(b[i - 1]) << s + i-- + } + z[z.len - 1] = d + } + return z +} diff --git a/v_windows/v/old/vlib/crypto/rc4/rc4.v b/v_windows/v/old/vlib/crypto/rc4/rc4.v new file mode 100644 index 0000000..97b2be4 --- /dev/null +++ b/v_windows/v/old/vlib/crypto/rc4/rc4.v @@ -0,0 +1,79 @@ +module rc4 + +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +// Package rc4 implements RC4 encryption, as defined in Bruce Schneier's +// Applied Cryptography. +// +// RC4 is cryptographically broken and should not be used for secure +// applications. +// Based off: https://github.com/golang/go/blob/master/src/crypto/rc4 +// Last commit: https://github.com/golang/go/commit/b35dacaac57b039205d9b07ea24098e2c3fcb12e +import crypto.internal.subtle + +// A Cipher is an instance of RC4 using a particular key. +struct Cipher { +mut: + s []u32 + i byte + j byte +} + +// new_cipher creates and returns a new Cipher. The key argument should be the +// RC4 key, at least 1 byte and at most 256 bytes. +pub fn new_cipher(key []byte) ?Cipher { + if key.len < 1 || key.len > 256 { + return error('crypto.rc4: invalid key size ' + key.len.str()) + } + mut c := Cipher{ + s: []u32{len: (256)} + } + for i in 0 .. 256 { + c.s[i] = u32(i) + } + mut j := byte(0) + for i in 0 .. 256 { + j += byte(c.s[i]) + key[i % key.len] + tmp := c.s[i] + c.s[i] = c.s[j] + c.s[j] = tmp + } + return c +} + +// reset zeros the key data and makes the Cipher unusable.good to com +// +// Deprecated: Reset can't guarantee that the key will be entirely removed from +// the process's memory. +pub fn (mut c Cipher) reset() { + for i in c.s { + c.s[i] = 0 + } + c.i = 0 + c.j = 0 +} + +// xor_key_stream sets dst to the result of XORing src with the key stream. +// Dst and src must overlap entirely or not at all. +pub fn (mut c Cipher) xor_key_stream(mut dst []byte, mut src []byte) { + if src.len == 0 { + return + } + if subtle.inexact_overlap(dst, src) { + panic('crypto.rc4: invalid buffer overlap') + } + mut i := c.i + mut j := c.j + for k, v in src { + i += byte(1) + x := c.s[i] + j += byte(x) + y := c.s[j] + c.s[i] = y + c.s[j] = x + dst[k] = v ^ byte(c.s[byte(x + y)]) + } + c.i = i + c.j = j +} diff --git a/v_windows/v/old/vlib/crypto/rc4/rc4_test.v b/v_windows/v/old/vlib/crypto/rc4/rc4_test.v new file mode 100644 index 0000000..dd9ac0f --- /dev/null +++ b/v_windows/v/old/vlib/crypto/rc4/rc4_test.v @@ -0,0 +1,22 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +import crypto.rc4 + +fn test_crypto_rc4() { + key := 'tthisisourrc4key'.bytes() + + mut c := rc4.new_cipher(key) or { + println(err) + return + } + + mut src := 'toencrypt'.bytes() + + // src & dst same, encrypt in place + c.xor_key_stream(mut src, mut src) // encrypt data + + c.reset() + + assert src.hex() == '189a39a91aea8afa65' +} diff --git a/v_windows/v/old/vlib/crypto/readme.txt b/v_windows/v/old/vlib/crypto/readme.txt new file mode 100644 index 0000000..93b4cdf --- /dev/null +++ b/v_windows/v/old/vlib/crypto/readme.txt @@ -0,0 +1,29 @@ +Based on Go's crypto packages. + +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/v_windows/v/old/vlib/crypto/sha1/sha1.v b/v_windows/v/old/vlib/crypto/sha1/sha1.v new file mode 100644 index 0000000..d792eb1 --- /dev/null +++ b/v_windows/v/old/vlib/crypto/sha1/sha1.v @@ -0,0 +1,155 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +// Package sha1 implements the SHA-1 hash algorithm as defined in RFC 3174. +// SHA-1 is cryptographically broken and should not be used for secure +// applications. +// Based off: https://github.com/golang/go/blob/master/src/crypto/sha1 +// Last commit: https://github.com/golang/go/commit/3ce865d7a0b88714cc433454ae2370a105210c01 +module sha1 + +import encoding.binary + +pub const ( + // The size of a SHA-1 checksum in bytes. + size = 20 + // The blocksize of SHA-1 in bytes. + block_size = 64 +) + +const ( + chunk = 64 + init0 = 0x67452301 + init1 = 0xEFCDAB89 + init2 = 0x98BADCFE + init3 = 0x10325476 + init4 = 0xC3D2E1F0 +) + +// digest represents the partial evaluation of a checksum. +struct Digest { +mut: + h []u32 + x []byte + nx int + len u64 +} + +fn (mut d Digest) reset() { + d.x = []byte{len: sha1.chunk} + d.h = []u32{len: (5)} + d.h[0] = u32(sha1.init0) + d.h[1] = u32(sha1.init1) + d.h[2] = u32(sha1.init2) + d.h[3] = u32(sha1.init3) + d.h[4] = u32(sha1.init4) + d.nx = 0 + d.len = 0 +} + +// new returns a new Digest (implementing hash.Hash) computing the SHA1 checksum. +pub fn new() &Digest { + mut d := &Digest{} + d.reset() + return d +} + +// write writes the contents of `p_` to the internal hash representation. +[manualfree] +pub fn (mut d Digest) write(p_ []byte) ?int { + nn := p_.len + unsafe { + mut p := p_ + d.len += u64(nn) + if d.nx > 0 { + n := copy(d.x[d.nx..], p) + d.nx += n + if d.nx == sha1.chunk { + block(mut d, d.x) + d.nx = 0 + } + if n >= p.len { + p = [] + } else { + p = p[n..] + } + } + if p.len >= sha1.chunk { + n := p.len & ~(sha1.chunk - 1) + block(mut d, p[..n]) + if n >= p.len { + p = [] + } else { + p = p[n..] + } + } + if p.len > 0 { + d.nx = copy(d.x, p) + } + } + return nn +} + +// sum returns a copy of the generated sum of the bytes in `b_in`. +pub fn (d &Digest) sum(b_in []byte) []byte { + // Make a copy of d so that caller can keep writing and summing. + mut d0 := *d + hash := d0.checksum() + mut b_out := b_in.clone() + for b in hash { + b_out << b + } + return b_out +} + +// checksum returns the byte checksum of the `Digest`. +fn (mut d Digest) checksum() []byte { + mut len := d.len + // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64. + mut tmp := []byte{len: (64)} + tmp[0] = 0x80 + if int(len) % 64 < 56 { + d.write(tmp[..56 - int(len) % 64]) or { panic(err) } + } else { + d.write(tmp[..64 + 56 - int(len) % 64]) or { panic(err) } + } + // Length in bits. + len <<= 3 + binary.big_endian_put_u64(mut tmp, len) + d.write(tmp[..8]) or { panic(err) } + mut digest := []byte{len: sha1.size} + binary.big_endian_put_u32(mut digest, d.h[0]) + binary.big_endian_put_u32(mut digest[4..], d.h[1]) + binary.big_endian_put_u32(mut digest[8..], d.h[2]) + binary.big_endian_put_u32(mut digest[12..], d.h[3]) + binary.big_endian_put_u32(mut digest[16..], d.h[4]) + return digest +} + +// sum returns the SHA-1 checksum of the bytes passed in `data`. +pub fn sum(data []byte) []byte { + mut d := new() + d.write(data) or { panic(err) } + return d.checksum() +} + +fn block(mut dig Digest, p []byte) { + // For now just use block_generic until we have specific + // architecture optimized versions + block_generic(mut dig, p) +} + +// size returns the size of the checksum in bytes. +pub fn (d &Digest) size() int { + return sha1.size +} + +// block_size returns the block size of the checksum in bytes. +pub fn (d &Digest) block_size() int { + return sha1.block_size +} + +// hexhash returns a hexadecimal SHA1 hash sum `string` of `s`. +pub fn hexhash(s string) string { + return sum(s.bytes()).hex() +} diff --git a/v_windows/v/old/vlib/crypto/sha1/sha1_test.v b/v_windows/v/old/vlib/crypto/sha1/sha1_test.v new file mode 100644 index 0000000..b52ea06 --- /dev/null +++ b/v_windows/v/old/vlib/crypto/sha1/sha1_test.v @@ -0,0 +1,8 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +import crypto.sha1 + +fn test_crypto_sha1() { + assert sha1.sum('This is a sha1 checksum.'.bytes()).hex() == 'e100d74442faa5dcd59463b808983c810a8eb5a1' +} diff --git a/v_windows/v/old/vlib/crypto/sha1/sha1block_generic.v b/v_windows/v/old/vlib/crypto/sha1/sha1block_generic.v new file mode 100644 index 0000000..a0dc92c --- /dev/null +++ b/v_windows/v/old/vlib/crypto/sha1/sha1block_generic.v @@ -0,0 +1,118 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +// This is the generic version with no architecture optimizations. +// In its own file so that an architecture +// optimized verision can be substituted +module sha1 + +import math.bits + +const ( + _k0 = 0x5A827999 + _k1 = 0x6ED9EBA1 + _k2 = 0x8F1BBCDC + _k3 = 0xCA62C1D6 +) + +fn block_generic(mut dig Digest, p_ []byte) { + unsafe { + mut p := p_ + mut w := []u32{len: (16)} + mut h0 := dig.h[0] + mut h1 := dig.h[1] + mut h2 := dig.h[2] + mut h3 := dig.h[3] + mut h4 := dig.h[4] + for p.len >= chunk { + // Can interlace the computation of w with the + // rounds below if needed for speed. + for i in 0 .. 16 { + j := i * 4 + w[i] = u32(p[j] << 24) | u32(p[j + 1] << 16) | u32(p[j + 2] << 8) | u32(p[j + 3]) + } + mut a := h0 + mut b := h1 + mut c := h2 + mut d := h3 + mut e := h4 + // Each of the four 20-iteration rounds + // differs only in the computation of f and + // the choice of K (_k0, _k1, etc). + mut i := 0 + for i < 16 { + f := b & c | (~b) & d + t := bits.rotate_left_32(a, 5) + f + e + w[i & 0xf] + u32(sha1._k0) + e = d + d = c + c = bits.rotate_left_32(b, 30) + b = a + a = t + i++ + } + for i < 20 { + tmp := w[(i - 3) & 0xf] ^ w[(i - 8) & 0xf] ^ w[(i - 14) & 0xf] ^ w[i & 0xf] + w[i & 0xf] = (tmp << 1) | (tmp >> (32 - 1)) + f := b & c | (~b) & d + t := bits.rotate_left_32(a, 5) + f + e + w[i & 0xf] + u32(sha1._k0) + e = d + d = c + c = bits.rotate_left_32(b, 30) + b = a + a = t + i++ + } + for i < 40 { + tmp := w[(i - 3) & 0xf] ^ w[(i - 8) & 0xf] ^ w[(i - 14) & 0xf] ^ w[i & 0xf] + w[i & 0xf] = (tmp << 1) | (tmp >> (32 - 1)) + f := b ^ c ^ d + t := bits.rotate_left_32(a, 5) + f + e + w[i & 0xf] + u32(sha1._k1) + e = d + d = c + c = bits.rotate_left_32(b, 30) + b = a + a = t + i++ + } + for i < 60 { + tmp := w[(i - 3) & 0xf] ^ w[(i - 8) & 0xf] ^ w[(i - 14) & 0xf] ^ w[i & 0xf] + w[i & 0xf] = (tmp << 1) | (tmp >> (32 - 1)) + f := ((b | c) & d) | (b & c) + t := bits.rotate_left_32(a, 5) + f + e + w[i & 0xf] + u32(sha1._k2) + e = d + d = c + c = bits.rotate_left_32(b, 30) + b = a + a = t + i++ + } + for i < 80 { + tmp := w[(i - 3) & 0xf] ^ w[(i - 8) & 0xf] ^ w[(i - 14) & 0xf] ^ w[i & 0xf] + w[i & 0xf] = (tmp << 1) | (tmp >> (32 - 1)) + f := b ^ c ^ d + t := bits.rotate_left_32(a, 5) + f + e + w[i & 0xf] + u32(sha1._k3) + e = d + d = c + c = bits.rotate_left_32(b, 30) + b = a + a = t + i++ + } + h0 += a + h1 += b + h2 += c + h3 += d + h4 += e + if chunk >= p.len { + p = [] + } else { + p = p[chunk..] + } + } + dig.h[0] = h0 + dig.h[1] = h1 + dig.h[2] = h2 + dig.h[3] = h3 + dig.h[4] = h4 + } +} diff --git a/v_windows/v/old/vlib/crypto/sha256/sha256.v b/v_windows/v/old/vlib/crypto/sha256/sha256.v new file mode 100644 index 0000000..106df94 --- /dev/null +++ b/v_windows/v/old/vlib/crypto/sha256/sha256.v @@ -0,0 +1,226 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +// Package sha256 implements the SHA224 and SHA256 hash algorithms as defined +// in FIPS 180-4. +// Based off: https://github.com/golang/go/tree/master/src/crypto/sha256 +// Last commit: https://github.com/golang/go/commit/3ce865d7a0b88714cc433454ae2370a105210c01 +module sha256 + +import encoding.binary + +pub const ( + // The size of a SHA256 checksum in bytes. + size = 32 + // The size of a SHA224 checksum in bytes. + size224 = 28 + // The blocksize of SHA256 and SHA224 in bytes. + block_size = 64 +) + +const ( + chunk = 64 + init0 = 0x6A09E667 + init1 = 0xBB67AE85 + init2 = 0x3C6EF372 + init3 = 0xA54FF53A + init4 = 0x510E527F + init5 = 0x9B05688C + init6 = 0x1F83D9AB + init7 = 0x5BE0CD19 + init0_224 = 0xC1059ED8 + init1_224 = 0x367CD507 + init2_224 = 0x3070DD17 + init3_224 = 0xF70E5939 + init4_224 = 0xFFC00B31 + init5_224 = 0x68581511 + init6_224 = 0x64F98FA7 + init7_224 = 0xBEFA4FA4 +) + +// digest represents the partial evaluation of a checksum. +struct Digest { +mut: + h []u32 + x []byte + nx int + len u64 + is224 bool // mark if this digest is SHA-224 +} + +fn (mut d Digest) reset() { + d.h = []u32{len: (8)} + d.x = []byte{len: sha256.chunk} + if !d.is224 { + d.h[0] = u32(sha256.init0) + d.h[1] = u32(sha256.init1) + d.h[2] = u32(sha256.init2) + d.h[3] = u32(sha256.init3) + d.h[4] = u32(sha256.init4) + d.h[5] = u32(sha256.init5) + d.h[6] = u32(sha256.init6) + d.h[7] = u32(sha256.init7) + } else { + d.h[0] = u32(sha256.init0_224) + d.h[1] = u32(sha256.init1_224) + d.h[2] = u32(sha256.init2_224) + d.h[3] = u32(sha256.init3_224) + d.h[4] = u32(sha256.init4_224) + d.h[5] = u32(sha256.init5_224) + d.h[6] = u32(sha256.init6_224) + d.h[7] = u32(sha256.init7_224) + } + d.nx = 0 + d.len = 0 +} + +// new returns a new Digest (implementing hash.Hash) computing the SHA256 checksum. +pub fn new() &Digest { + mut d := &Digest{} + d.reset() + return d +} + +// new224 returns a new Digest (implementing hash.Hash) computing the SHA224 checksum. +pub fn new224() &Digest { + mut d := &Digest{} + d.is224 = true + d.reset() + return d +} + +// write writes the contents of `p_` to the internal hash representation. +fn (mut d Digest) write(p_ []byte) ?int { + unsafe { + mut p := p_ + nn := p.len + d.len += u64(nn) + if d.nx > 0 { + n := copy(d.x[d.nx..], p) + d.nx += n + if d.nx == sha256.chunk { + block(mut d, d.x) + d.nx = 0 + } + if n >= p.len { + p = [] + } else { + p = p[n..] + } + } + if p.len >= sha256.chunk { + n := p.len & ~(sha256.chunk - 1) + block(mut d, p[..n]) + if n >= p.len { + p = [] + } else { + p = p[n..] + } + } + if p.len > 0 { + d.nx = copy(d.x, p) + } + return nn + } +} + +pub fn (d &Digest) sum(b_in []byte) []byte { + // Make a copy of d so that caller can keep writing and summing. + mut d0 := *d + hash := d0.checksum() + mut b_out := b_in.clone() + if d0.is224 { + for b in hash[..sha256.size224] { + b_out << b + } + } else { + for b in hash { + b_out << b + } + } + return b_out +} + +fn (mut d Digest) checksum() []byte { + mut len := d.len + // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64. + mut tmp := []byte{len: (64)} + tmp[0] = 0x80 + if int(len) % 64 < 56 { + d.write(tmp[..56 - int(len) % 64]) or { panic(err) } + } else { + d.write(tmp[..64 + 56 - int(len) % 64]) or { panic(err) } + } + // Length in bits. + len <<= u64(3) + binary.big_endian_put_u64(mut tmp, len) + d.write(tmp[..8]) or { panic(err) } + if d.nx != 0 { + panic('d.nx != 0') + } + mut digest := []byte{len: sha256.size} + binary.big_endian_put_u32(mut digest, d.h[0]) + binary.big_endian_put_u32(mut digest[4..], d.h[1]) + binary.big_endian_put_u32(mut digest[8..], d.h[2]) + binary.big_endian_put_u32(mut digest[12..], d.h[3]) + binary.big_endian_put_u32(mut digest[16..], d.h[4]) + binary.big_endian_put_u32(mut digest[20..], d.h[5]) + binary.big_endian_put_u32(mut digest[24..], d.h[6]) + if !d.is224 { + binary.big_endian_put_u32(mut digest[28..], d.h[7]) + } + return digest +} + +// sum returns the SHA256 checksum of the bytes in `data`. +// Example: assert sha256.sum('V'.bytes()).len > 0 == true +pub fn sum(data []byte) []byte { + return sum256(data) +} + +// sum256 returns the SHA256 checksum of the data. +pub fn sum256(data []byte) []byte { + mut d := new() + d.write(data) or { panic(err) } + return d.checksum() +} + +// sum224 returns the SHA224 checksum of the data. +pub fn sum224(data []byte) []byte { + mut d := new224() + d.write(data) or { panic(err) } + sum := d.checksum() + sum224 := []byte{len: sha256.size224} + copy(sum224, sum[..sha256.size224]) + return sum224 +} + +fn block(mut dig Digest, p []byte) { + // For now just use block_generic until we have specific + // architecture optimized versions + block_generic(mut dig, p) +} + +// size returns the size of the checksum in bytes. +pub fn (d &Digest) size() int { + if !d.is224 { + return sha256.size + } + return sha256.size224 +} + +// block_size returns the block size of the checksum in bytes. +pub fn (d &Digest) block_size() int { + return sha256.block_size +} + +// hexhash returns a hexadecimal SHA256 hash sum `string` of `s`. +// Example: assert sha256.hexhash('V') == 'de5a6f78116eca62d7fc5ce159d23ae6b889b365a1739ad2cf36f925a140d0cc' +pub fn hexhash(s string) string { + return sum256(s.bytes()).hex() +} + +// hexhash_224 returns a hexadecimal SHA224 hash sum `string` of `s`. +pub fn hexhash_224(s string) string { + return sum224(s.bytes()).hex() +} diff --git a/v_windows/v/old/vlib/crypto/sha256/sha256_test.v b/v_windows/v/old/vlib/crypto/sha256/sha256_test.v new file mode 100644 index 0000000..5aeacdc --- /dev/null +++ b/v_windows/v/old/vlib/crypto/sha256/sha256_test.v @@ -0,0 +1,16 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +import crypto.sha256 + +fn test_crypto_sha256() { + assert sha256.sum('This is a sha256 checksum.'.bytes()).hex() == 'dc7163299659529eae29683eb1ffec50d6c8fc7275ecb10c145fde0e125b8727' +} + +fn test_crypto_sha256_writer() { + mut digest := sha256.new() + digest.write('This is a'.bytes()) or { assert false } + digest.write(' sha256 checksum.'.bytes()) or { assert false } + sum := digest.sum([]) + assert sum.hex() == 'dc7163299659529eae29683eb1ffec50d6c8fc7275ecb10c145fde0e125b8727' +} diff --git a/v_windows/v/old/vlib/crypto/sha256/sha256block_generic.v b/v_windows/v/old/vlib/crypto/sha256/sha256block_generic.v new file mode 100644 index 0000000..e3989cc --- /dev/null +++ b/v_windows/v/old/vlib/crypto/sha256/sha256block_generic.v @@ -0,0 +1,154 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +// SHA256 block step. +// This is the generic version with no architecture optimizations. +// In its own file so that an architecture +// optimized verision can be substituted +module sha256 + +import math.bits + +const ( + _k = [ + 0x428a2f98, + 0x71374491, + 0xb5c0fbcf, + 0xe9b5dba5, + 0x3956c25b, + 0x59f111f1, + 0x923f82a4, + 0xab1c5ed5, + 0xd807aa98, + 0x12835b01, + 0x243185be, + 0x550c7dc3, + 0x72be5d74, + 0x80deb1fe, + 0x9bdc06a7, + 0xc19bf174, + 0xe49b69c1, + 0xefbe4786, + 0x0fc19dc6, + 0x240ca1cc, + 0x2de92c6f, + 0x4a7484aa, + 0x5cb0a9dc, + 0x76f988da, + 0x983e5152, + 0xa831c66d, + 0xb00327c8, + 0xbf597fc7, + 0xc6e00bf3, + 0xd5a79147, + 0x06ca6351, + 0x14292967, + 0x27b70a85, + 0x2e1b2138, + 0x4d2c6dfc, + 0x53380d13, + 0x650a7354, + 0x766a0abb, + 0x81c2c92e, + 0x92722c85, + 0xa2bfe8a1, + 0xa81a664b, + 0xc24b8b70, + 0xc76c51a3, + 0xd192e819, + 0xd6990624, + 0xf40e3585, + 0x106aa070, + 0x19a4c116, + 0x1e376c08, + 0x2748774c, + 0x34b0bcb5, + 0x391c0cb3, + 0x4ed8aa4a, + 0x5b9cca4f, + 0x682e6ff3, + 0x748f82ee, + 0x78a5636f, + 0x84c87814, + 0x8cc70208, + 0x90befffa, + 0xa4506ceb, + 0xbef9a3f7, + 0xc67178f2, + ] +) + +fn block_generic(mut dig Digest, p_ []byte) { + unsafe { + mut p := p_ + mut w := []u32{len: (64)} + mut h0 := dig.h[0] + mut h1 := dig.h[1] + mut h2 := dig.h[2] + mut h3 := dig.h[3] + mut h4 := dig.h[4] + mut h5 := dig.h[5] + mut h6 := dig.h[6] + mut h7 := dig.h[7] + for p.len >= chunk { + // Can interlace the computation of w with the + // rounds below if needed for speed. + for i in 0 .. 16 { + j := i * 4 + w[i] = u32(p[j] << 24) | u32(p[j + 1] << 16) | u32(p[j + 2] << 8) | u32(p[j + 3]) + } + for i := 16; i < 64; i++ { + v1 := w[i - 2] + t1 := (bits.rotate_left_32(v1, -17)) ^ (bits.rotate_left_32(v1, -19)) ^ (v1 >> 10) + v2 := w[i - 15] + t2 := (bits.rotate_left_32(v2, -7)) ^ (bits.rotate_left_32(v2, -18)) ^ (v2 >> 3) + w[i] = t1 + w[i - 7] + t2 + w[i - 16] + } + mut a := h0 + mut b := h1 + mut c := h2 + mut d := h3 + mut e := h4 + mut f := h5 + mut g := h6 + mut h := h7 + for i in 0 .. 64 { + t1 := h + + ((bits.rotate_left_32(e, -6)) ^ (bits.rotate_left_32(e, -11)) ^ (bits.rotate_left_32(e, -25))) + + ((e & f) ^ (~e & g)) + u32(sha256._k[i]) + w[i] + t2 := + ((bits.rotate_left_32(a, -2)) ^ (bits.rotate_left_32(a, -13)) ^ (bits.rotate_left_32(a, -22))) + + ((a & b) ^ (a & c) ^ (b & c)) + h = g + g = f + f = e + e = d + t1 + d = c + c = b + b = a + a = t1 + t2 + } + h0 += a + h1 += b + h2 += c + h3 += d + h4 += e + h5 += f + h6 += g + h7 += h + if chunk >= p.len { + p = [] + } else { + p = p[chunk..] + } + } + dig.h[0] = h0 + dig.h[1] = h1 + dig.h[2] = h2 + dig.h[3] = h3 + dig.h[4] = h4 + dig.h[5] = h5 + dig.h[6] = h6 + dig.h[7] = h7 + } +} diff --git a/v_windows/v/old/vlib/crypto/sha512/sha512.v b/v_windows/v/old/vlib/crypto/sha512/sha512.v new file mode 100644 index 0000000..dd5d3a9 --- /dev/null +++ b/v_windows/v/old/vlib/crypto/sha512/sha512.v @@ -0,0 +1,324 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +// Package sha512 implements the SHA-384, SHA-512, SHA-512/224, and SHA-512/256 +// hash algorithms as defined in FIPS 180-4. +// Based off: https://github.com/golang/go/tree/master/src/crypto/sha512 +// Last commit: https://github.com/golang/go/commit/3ce865d7a0b88714cc433454ae2370a105210c01 +module sha512 + +import crypto +import encoding.binary + +pub const ( + // size is the size, in bytes, of a SHA-512 checksum. + size = 64 + // size224 is the size, in bytes, of a SHA-512/224 checksum. + size224 = 28 + // size256 is the size, in bytes, of a SHA-512/256 checksum. + size256 = 32 + // size384 is the size, in bytes, of a SHA-384 checksum. + size384 = 48 + // block_size is the block size, in bytes, of the SHA-512/224, + // SHA-512/256, SHA-384 and SHA-512 hash functions. + block_size = 128 +) + +const ( + chunk = 128 + init0 = u64(0x6a09e667f3bcc908) + init1 = u64(0xbb67ae8584caa73b) + init2 = u64(0x3c6ef372fe94f82b) + init3 = u64(0xa54ff53a5f1d36f1) + init4 = u64(0x510e527fade682d1) + init5 = u64(0x9b05688c2b3e6c1f) + init6 = u64(0x1f83d9abfb41bd6b) + init7 = u64(0x5be0cd19137e2179) + init0_224 = u64(0x8c3d37c819544da2) + init1_224 = u64(0x73e1996689dcd4d6) + init2_224 = u64(0x1dfab7ae32ff9c82) + init3_224 = u64(0x679dd514582f9fcf) + init4_224 = u64(0x0f6d2b697bd44da8) + init5_224 = u64(0x77e36f7304c48942) + init6_224 = u64(0x3f9d85a86a1d36c8) + init7_224 = u64(0x1112e6ad91d692a1) + init0_256 = u64(0x22312194fc2bf72c) + init1_256 = u64(0x9f555fa3c84c64c2) + init2_256 = u64(0x2393b86b6f53b151) + init3_256 = u64(0x963877195940eabd) + init4_256 = u64(0x96283ee2a88effe3) + init5_256 = u64(0xbe5e1e2553863992) + init6_256 = u64(0x2b0199fc2c85b8aa) + init7_256 = u64(0x0eb72ddc81c52ca2) + init0_384 = u64(0xcbbb9d5dc1059ed8) + init1_384 = u64(0x629a292a367cd507) + init2_384 = u64(0x9159015a3070dd17) + init3_384 = u64(0x152fecd8f70e5939) + init4_384 = u64(0x67332667ffc00b31) + init5_384 = u64(0x8eb44a8768581511) + init6_384 = u64(0xdb0c2e0d64f98fa7) + init7_384 = u64(0x47b5481dbefa4fa4) +) + +// Digest represents the partial evaluation of a checksum. +struct Digest { +mut: + h []u64 + x []byte + nx int + len u64 + function crypto.Hash +} + +fn (mut d Digest) reset() { + d.h = []u64{len: (8)} + d.x = []byte{len: sha512.chunk} + match d.function { + .sha384 { + d.h[0] = sha512.init0_384 + d.h[1] = sha512.init1_384 + d.h[2] = sha512.init2_384 + d.h[3] = sha512.init3_384 + d.h[4] = sha512.init4_384 + d.h[5] = sha512.init5_384 + d.h[6] = sha512.init6_384 + d.h[7] = sha512.init7_384 + } + .sha512_224 { + d.h[0] = sha512.init0_224 + d.h[1] = sha512.init1_224 + d.h[2] = sha512.init2_224 + d.h[3] = sha512.init3_224 + d.h[4] = sha512.init4_224 + d.h[5] = sha512.init5_224 + d.h[6] = sha512.init6_224 + d.h[7] = sha512.init7_224 + } + .sha512_256 { + d.h[0] = sha512.init0_256 + d.h[1] = sha512.init1_256 + d.h[2] = sha512.init2_256 + d.h[3] = sha512.init3_256 + d.h[4] = sha512.init4_256 + d.h[5] = sha512.init5_256 + d.h[6] = sha512.init6_256 + d.h[7] = sha512.init7_256 + } + else { + d.h[0] = sha512.init0 + d.h[1] = sha512.init1 + d.h[2] = sha512.init2 + d.h[3] = sha512.init3 + d.h[4] = sha512.init4 + d.h[5] = sha512.init5 + d.h[6] = sha512.init6 + d.h[7] = sha512.init7 + } + } + d.nx = 0 + d.len = 0 +} + +// internal +fn new_digest(hash crypto.Hash) &Digest { + mut d := &Digest{ + function: hash + } + d.reset() + return d +} + +// new returns a new Digest (implementing hash.Hash) computing the SHA-512 checksum. +pub fn new() &Digest { + return new_digest(.sha512) +} + +// new512_224 returns a new Digest (implementing hash.Hash) computing the SHA-512/224 checksum. +fn new512_224() &Digest { + return new_digest(.sha512_224) +} + +// new512_256 returns a new Digest (implementing hash.Hash) computing the SHA-512/256 checksum. +fn new512_256() &Digest { + return new_digest(.sha512_256) +} + +// new384 returns a new Digest (implementing hash.Hash) computing the SHA-384 checksum. +fn new384() &Digest { + return new_digest(.sha384) +} + +// write writes the contents of `p_` to the internal hash representation. +fn (mut d Digest) write(p_ []byte) ?int { + unsafe { + mut p := p_ + nn := p.len + d.len += u64(nn) + if d.nx > 0 { + n := copy(d.x[d.nx..], p) + d.nx += n + if d.nx == sha512.chunk { + block(mut d, d.x) + d.nx = 0 + } + if n >= p.len { + p = [] + } else { + p = p[n..] + } + } + if p.len >= sha512.chunk { + n := p.len & ~(sha512.chunk - 1) + block(mut d, p[..n]) + if n >= p.len { + p = [] + } else { + p = p[n..] + } + } + if p.len > 0 { + d.nx = copy(d.x, p) + } + return nn + } +} + +fn (d &Digest) sum(b_in []byte) []byte { + // Make a copy of d so that caller can keep writing and summing. + mut d0 := *d + hash := d0.checksum() + mut b_out := b_in.clone() + match d0.function { + .sha384 { + for b in hash[..sha512.size384] { + b_out << b + } + } + .sha512_224 { + for b in hash[..sha512.size224] { + b_out << b + } + } + .sha512_256 { + for b in hash[..sha512.size256] { + b_out << b + } + } + else { + for b in hash { + b_out << b + } + } + } + return b_out +} + +fn (mut d Digest) checksum() []byte { + // Padding. Add a 1 bit and 0 bits until 112 bytes mod 128. + mut len := d.len + mut tmp := []byte{len: (128)} + tmp[0] = 0x80 + if int(len) % 128 < 112 { + d.write(tmp[..112 - int(len) % 128]) or { panic(err) } + } else { + d.write(tmp[..128 + 112 - int(len) % 128]) or { panic(err) } + } + // Length in bits. + len <<= u64(3) + binary.big_endian_put_u64(mut tmp, u64(0)) // upper 64 bits are always zero, because len variable has type u64 + binary.big_endian_put_u64(mut tmp[8..], len) + d.write(tmp[..16]) or { panic(err) } + if d.nx != 0 { + panic('d.nx != 0') + } + mut digest := []byte{len: sha512.size} + binary.big_endian_put_u64(mut digest, d.h[0]) + binary.big_endian_put_u64(mut digest[8..], d.h[1]) + binary.big_endian_put_u64(mut digest[16..], d.h[2]) + binary.big_endian_put_u64(mut digest[24..], d.h[3]) + binary.big_endian_put_u64(mut digest[32..], d.h[4]) + binary.big_endian_put_u64(mut digest[40..], d.h[5]) + if d.function != .sha384 { + binary.big_endian_put_u64(mut digest[48..], d.h[6]) + binary.big_endian_put_u64(mut digest[56..], d.h[7]) + } + return digest +} + +// sum512 returns the SHA512 checksum of the data. +pub fn sum512(data []byte) []byte { + mut d := new_digest(.sha512) + d.write(data) or { panic(err) } + return d.checksum() +} + +// sum384 returns the SHA384 checksum of the data. +pub fn sum384(data []byte) []byte { + mut d := new_digest(.sha384) + d.write(data) or { panic(err) } + sum := d.checksum() + sum384 := []byte{len: sha512.size384} + copy(sum384, sum[..sha512.size384]) + return sum384 +} + +// sum512_224 returns the Sum512/224 checksum of the data. +pub fn sum512_224(data []byte) []byte { + mut d := new_digest(.sha512_224) + d.write(data) or { panic(err) } + sum := d.checksum() + sum224 := []byte{len: sha512.size224} + copy(sum224, sum[..sha512.size224]) + return sum224 +} + +// sum512_256 returns the Sum512/256 checksum of the data. +pub fn sum512_256(data []byte) []byte { + mut d := new_digest(.sha512_256) + d.write(data) or { panic(err) } + sum := d.checksum() + sum256 := []byte{len: sha512.size256} + copy(sum256, sum[..sha512.size256]) + return sum256 +} + +fn block(mut dig Digest, p []byte) { + // For now just use block_generic until we have specific + // architecture optimized versions + block_generic(mut dig, p) +} + +// size returns the size of the checksum in bytes. +pub fn (d &Digest) size() int { + match d.function { + .sha512_224 { return sha512.size224 } + .sha512_256 { return sha512.size256 } + .sha384 { return sha512.size384 } + else { return sha512.size } + } +} + +// block_size returns the block size of the checksum in bytes. +pub fn (d &Digest) block_size() int { + return sha512.block_size +} + +// hexhash returns a hexadecimal SHA512 hash sum `string` of `s`. +pub fn hexhash(s string) string { + return sum512(s.bytes()).hex() +} + +// hexhash_384 returns a hexadecimal SHA384 hash sum `string` of `s`. +pub fn hexhash_384(s string) string { + return sum384(s.bytes()).hex() +} + +// hexhash_512_224 returns a hexadecimal SHA512/224 hash sum `string` of `s`. +pub fn hexhash_512_224(s string) string { + return sum512_224(s.bytes()).hex() +} + +// hexhash_512_256 returns a hexadecimal 512/256 hash sum `string` of `s`. +pub fn hexhash_512_256(s string) string { + return sum512_256(s.bytes()).hex() +} diff --git a/v_windows/v/old/vlib/crypto/sha512/sha512_test.v b/v_windows/v/old/vlib/crypto/sha512/sha512_test.v new file mode 100644 index 0000000..50676d9 --- /dev/null +++ b/v_windows/v/old/vlib/crypto/sha512/sha512_test.v @@ -0,0 +1,8 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +import crypto.sha512 + +fn test_crypto_sha512() { + assert sha512.sum512('This is a sha512 checksum.'.bytes()).hex() == '4143e55fcba7e39b20f62a1368e5eb28f64a8859458886117ac66027832e0f9f5263daec688c439d2d0fa07059334668d39e59543039703dbb7e03ec9da7f8d7' +} diff --git a/v_windows/v/old/vlib/crypto/sha512/sha512block_generic.v b/v_windows/v/old/vlib/crypto/sha512/sha512block_generic.v new file mode 100644 index 0000000..72a6f5e --- /dev/null +++ b/v_windows/v/old/vlib/crypto/sha512/sha512block_generic.v @@ -0,0 +1,108 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +// SHA512 block step. +// This is the generic version with no architecture optimizations. +// In its own file so that an architecture +// optimized verision can be substituted +module sha512 + +import math.bits + +const ( + _k = [u64(0x428a2f98d728ae22), u64(0x7137449123ef65cd), u64(0xb5c0fbcfec4d3b2f), u64(0xe9b5dba58189dbbc), + u64(0x3956c25bf348b538), u64(0x59f111f1b605d019), u64(0x923f82a4af194f9b), u64(0xab1c5ed5da6d8118), + u64(0xd807aa98a3030242), u64(0x12835b0145706fbe), u64(0x243185be4ee4b28c), u64(0x550c7dc3d5ffb4e2), + u64(0x72be5d74f27b896f), u64(0x80deb1fe3b1696b1), u64(0x9bdc06a725c71235), u64(0xc19bf174cf692694), + u64(0xe49b69c19ef14ad2), u64(0xefbe4786384f25e3), u64(0x0fc19dc68b8cd5b5), u64(0x240ca1cc77ac9c65), + u64(0x2de92c6f592b0275), u64(0x4a7484aa6ea6e483), u64(0x5cb0a9dcbd41fbd4), u64(0x76f988da831153b5), + u64(0x983e5152ee66dfab), u64(0xa831c66d2db43210), u64(0xb00327c898fb213f), u64(0xbf597fc7beef0ee4), + u64(0xc6e00bf33da88fc2), u64(0xd5a79147930aa725), u64(0x06ca6351e003826f), u64(0x142929670a0e6e70), + u64(0x27b70a8546d22ffc), u64(0x2e1b21385c26c926), u64(0x4d2c6dfc5ac42aed), u64(0x53380d139d95b3df), + u64(0x650a73548baf63de), u64(0x766a0abb3c77b2a8), u64(0x81c2c92e47edaee6), u64(0x92722c851482353b), + u64(0xa2bfe8a14cf10364), u64(0xa81a664bbc423001), u64(0xc24b8b70d0f89791), u64(0xc76c51a30654be30), + u64(0xd192e819d6ef5218), u64(0xd69906245565a910), u64(0xf40e35855771202a), u64(0x106aa07032bbd1b8), + u64(0x19a4c116b8d2d0c8), u64(0x1e376c085141ab53), u64(0x2748774cdf8eeb99), u64(0x34b0bcb5e19b48a8), + u64(0x391c0cb3c5c95a63), u64(0x4ed8aa4ae3418acb), u64(0x5b9cca4f7763e373), u64(0x682e6ff3d6b2b8a3), + u64(0x748f82ee5defb2fc), u64(0x78a5636f43172f60), u64(0x84c87814a1f0ab72), u64(0x8cc702081a6439ec), + u64(0x90befffa23631e28), u64(0xa4506cebde82bde9), u64(0xbef9a3f7b2c67915), u64(0xc67178f2e372532b), + u64(0xca273eceea26619c), u64(0xd186b8c721c0c207), u64(0xeada7dd6cde0eb1e), u64(0xf57d4f7fee6ed178), + u64(0x06f067aa72176fba), u64(0x0a637dc5a2c898a6), u64(0x113f9804bef90dae), u64(0x1b710b35131c471b), + u64(0x28db77f523047d84), u64(0x32caab7b40c72493), u64(0x3c9ebe0a15c9bebc), u64(0x431d67c49c100d4c), + u64(0x4cc5d4becb3e42b6), u64(0x597f299cfc657e2a), u64(0x5fcb6fab3ad6faec), u64(0x6c44198c4a475817)] +) + +fn block_generic(mut dig Digest, p_ []byte) { + unsafe { + mut p := p_ + mut w := []u64{len: (80)} + mut h0 := dig.h[0] + mut h1 := dig.h[1] + mut h2 := dig.h[2] + mut h3 := dig.h[3] + mut h4 := dig.h[4] + mut h5 := dig.h[5] + mut h6 := dig.h[6] + mut h7 := dig.h[7] + for p.len >= chunk { + for i in 0 .. 16 { + j := i * 8 + w[i] = (u64(p[j]) << 56) | + (u64(p[j + 1]) << 48) | (u64(p[j + 2]) << 40) | + (u64(p[j + 3]) << 32) | (u64(p[j + 4]) << 24) | + (u64(p[j + 5]) << 16) | (u64(p[j + 6]) << 8) | u64(p[j + 7]) + } + for i := 16; i < 80; i++ { + v1 := w[i - 2] + t1 := bits.rotate_left_64(v1, -19) ^ bits.rotate_left_64(v1, -61) ^ (v1 >> 6) + v2 := w[i - 15] + t2 := bits.rotate_left_64(v2, -1) ^ bits.rotate_left_64(v2, -8) ^ (v2 >> 7) + w[i] = t1 + w[i - 7] + t2 + w[i - 16] + } + mut a := h0 + mut b := h1 + mut c := h2 + mut d := h3 + mut e := h4 + mut f := h5 + mut g := h6 + mut h := h7 + for i in 0 .. 80 { + t1 := h + + (bits.rotate_left_64(e, -14) ^ bits.rotate_left_64(e, -18) ^ bits.rotate_left_64(e, -41)) + + ((e & f) ^ (~e & g)) + _k[i] + w[i] + t2 := (bits.rotate_left_64(a, -28) ^ bits.rotate_left_64(a, -34) ^ bits.rotate_left_64(a, -39)) + + ((a & b) ^ (a & c) ^ (b & c)) + h = g + g = f + f = e + e = d + t1 + d = c + c = b + b = a + a = t1 + t2 + } + h0 += a + h1 += b + h2 += c + h3 += d + h4 += e + h5 += f + h6 += g + h7 += h + if chunk >= p.len { + p = [] + } else { + p = p[chunk..] + } + } + dig.h[0] = h0 + dig.h[1] = h1 + dig.h[2] = h2 + dig.h[3] = h3 + dig.h[4] = h4 + dig.h[5] = h5 + dig.h[6] = h6 + dig.h[7] = h7 + } +} diff --git a/v_windows/v/old/vlib/darwin/darwin.m b/v_windows/v/old/vlib/darwin/darwin.m new file mode 100644 index 0000000..5cab83a --- /dev/null +++ b/v_windows/v/old/vlib/darwin/darwin.m @@ -0,0 +1,9 @@ + +///void NSLog(id x); + +#include + +NSString* nsstring2(string s) { + return [ [ NSString alloc ] initWithBytesNoCopy:s.str length:s.len + encoding:NSUTF8StringEncoding freeWhenDone: false]; +} diff --git a/v_windows/v/old/vlib/darwin/darwin.v b/v_windows/v/old/vlib/darwin/darwin.v new file mode 100644 index 0000000..b86636e --- /dev/null +++ b/v_windows/v/old/vlib/darwin/darwin.v @@ -0,0 +1,57 @@ +module darwin + +#include +#include + +#flag -framework Cocoa +#flag -framework Carbon + +struct C.NSString {} + +#include "@VEXEROOT/vlib/darwin/darwin.m" + +fn C.nsstring2(s string) voidptr + +// macOS and iOS helpers +// pub fn nsstring(s string) *C.NSString { +pub fn nsstring(s string) voidptr { + return C.nsstring2(s) + // println('ns $s len=$s.len') + //# return [ [ NSString alloc ] initWithBytesNoCopy:s.str length:s.len + //# encoding:NSUTF8StringEncoding freeWhenDone: false]; + // return 0 + + // ns := C.alloc_NSString() + // return ns.initWithBytesNoCopy(s.str, length: s.len, + // encoding: NSUTF8StringEncoding, freeWhenDone: false) +} + +// returns absolute path to folder where your resources should / will reside +// for .app packages: .../my.app/Contents/Resources +// for cli: .../parent_folder/Resources + +fn C.CFBundleCopyResourcesDirectoryURL(bundle voidptr) &byte +fn C.CFBundleGetMainBundle() voidptr +fn C.CFURLGetFileSystemRepresentation(url &byte, resolve_against_base bool, buffer &byte, buffer_size int) int +fn C.CFRelease(url &byte) + +pub fn resource_path() string { + main_bundle := C.CFBundleGetMainBundle() + resource_dir_url := C.CFBundleCopyResourcesDirectoryURL(main_bundle) + if isnil(resource_dir_url) { + panic('CFBundleCopyResourcesDirectoryURL failed') + } + buffer_size := 4096 + mut buffer := unsafe { malloc_noscan(buffer_size) } + unsafe { + buffer[0] = 0 + } + conv_result := C.CFURLGetFileSystemRepresentation(resource_dir_url, true, buffer, + buffer_size) + if conv_result == 0 { + panic('CFURLGetFileSystemRepresentation failed') + } + result := unsafe { buffer.vstring() } + C.CFRelease(resource_dir_url) + return result +} diff --git a/v_windows/v/old/vlib/dl/dl.v b/v_windows/v/old/vlib/dl/dl.v new file mode 100644 index 0000000..04ad16b --- /dev/null +++ b/v_windows/v/old/vlib/dl/dl.v @@ -0,0 +1,48 @@ +module dl + +pub const ( + version = 1 + dl_ext = get_shared_library_extension() +) + +// get_shared_library_extension returns the platform dependent shared library extension +// i.e. .dll on windows, .so on most unixes, .dylib on macos. +[inline] +pub fn get_shared_library_extension() string { + return $if windows { + '.dll' + } $else $if macos { + '.dylib' + } $else { + '.so' + } +} + +// get_libname returns a library name with the operating system specific extension for +// shared libraries. +[inline] +pub fn get_libname(libname string) string { + return '$libname$dl.dl_ext' +} + +// open_opt - loads the dynamic shared object. +// Unlike open, open_opt return an option. +pub fn open_opt(filename string, flags int) ?voidptr { + shared_object_handle := open(filename, flags) + if shared_object_handle == 0 { + e := dlerror() + return error(e) + } + return shared_object_handle +} + +// sym_opt returns the address of a symbol in a given shared object, if found. +// Unlike sym, sym_opt returns an option. +pub fn sym_opt(shared_object_handle voidptr, symbol string) ?voidptr { + sym_handle := sym(shared_object_handle, symbol) + if sym_handle == 0 { + e := dlerror() + return error(e) + } + return sym_handle +} diff --git a/v_windows/v/old/vlib/dl/dl_nix.c.v b/v_windows/v/old/vlib/dl/dl_nix.c.v new file mode 100644 index 0000000..40f63b5 --- /dev/null +++ b/v_windows/v/old/vlib/dl/dl_nix.c.v @@ -0,0 +1,43 @@ +module dl + +#include + +$if linux { + #flag -ldl +} + +pub const ( + rtld_now = C.RTLD_NOW + rtld_lazy = C.RTLD_LAZY +) + +fn C.dlopen(filename &char, flags int) voidptr + +fn C.dlsym(handle voidptr, symbol &char) voidptr + +fn C.dlclose(handle voidptr) int + +fn C.dlerror() &char + +// open loads the dynamic shared object. +pub fn open(filename string, flags int) voidptr { + return C.dlopen(&char(filename.str), flags) +} + +// close frees a given shared object. +pub fn close(handle voidptr) bool { + return C.dlclose(handle) == 0 +} + +// sym returns an address of a symbol in a given shared object. +pub fn sym(handle voidptr, symbol string) voidptr { + return C.dlsym(handle, &char(symbol.str)) +} + +// dlerror provides a text error diagnostic message for functions in `dl` +// it returns a human-readable string, describing the most recent error +// that occurred from a call to one of the `dl` functions, since the last +// call to dlerror() +pub fn dlerror() string { + return unsafe { cstring_to_vstring(C.dlerror()) } +} diff --git a/v_windows/v/old/vlib/dl/dl_test.v b/v_windows/v/old/vlib/dl/dl_test.v new file mode 100644 index 0000000..c4f0824 --- /dev/null +++ b/v_windows/v/old/vlib/dl/dl_test.v @@ -0,0 +1,46 @@ +import dl + +fn test_dl() { + $if linux { + run_test_invalid_lib_linux() + return + } + $if windows { + run_test_invalid_lib_windows() + run_test_valid_lib_windows() + run_test_invalid_sym_windows() + run_test_valid_sym_windows() + return + } $else { + eprint('currently not implemented on this platform') + } +} + +fn run_test_invalid_lib_linux() { + // ensure a not-existing dl won't be loaded + h := dl.open('not-existing-dynamic-link-library', dl.rtld_now) + assert h == 0 +} + +fn run_test_invalid_lib_windows() { + // ensure a not-existing dl won't be loaded + h := dl.open('not-existing-dynamic-link-library', dl.rtld_now) + assert h == 0 +} + +fn run_test_valid_lib_windows() { + h := dl.open('shell32', dl.rtld_now) + assert h != 0 +} + +fn run_test_invalid_sym_windows() { + h := dl.open('shell32', dl.rtld_now) + proc := dl.sym(h, 'CommandLineToArgvW2') + assert proc == 0 +} + +fn run_test_valid_sym_windows() { + h := dl.open('shell32', dl.rtld_now) + proc := dl.sym(h, 'CommandLineToArgvW') + assert proc != 0 +} diff --git a/v_windows/v/old/vlib/dl/dl_windows.c.v b/v_windows/v/old/vlib/dl/dl_windows.c.v new file mode 100644 index 0000000..b6ae2fc --- /dev/null +++ b/v_windows/v/old/vlib/dl/dl_windows.c.v @@ -0,0 +1,39 @@ +module dl + +pub const ( + rtld_now = 0 + rtld_lazy = 0 +) + +fn C.LoadLibrary(libfilename &u16) voidptr + +fn C.GetProcAddress(handle voidptr, procname &byte) voidptr + +fn C.FreeLibrary(handle voidptr) bool + +// open loads a given module into the address space of the calling process. +pub fn open(filename string, flags int) voidptr { + res := C.LoadLibrary(filename.to_wide()) + return res +} + +// close frees the loaded a given module. +pub fn close(handle voidptr) bool { + return C.FreeLibrary(handle) +} + +// sym returns an address of an exported function or variable from a given module. +pub fn sym(handle voidptr, symbol string) voidptr { + return C.GetProcAddress(handle, symbol.str) +} + +// dlerror provides a text error diagnostic message for functions in `dl` +// it returns a human-readable string, describing the most recent error +// that occurred from a call to one of the `dl` functions, since the last +// call to dlerror() +pub fn dlerror() string { + // https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror + // Unlike dlerror(), GetLastError returns just an error code, that is function specific. + cerr := int(C.GetLastError()) + return 'error code $cerr' +} diff --git a/v_windows/v/old/vlib/encoding/base64/base64.v b/v_windows/v/old/vlib/encoding/base64/base64.v new file mode 100644 index 0000000..203ff0b --- /dev/null +++ b/v_windows/v/old/vlib/encoding/base64/base64.v @@ -0,0 +1,224 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module base64 + +const ( + index = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 63, 62, 62, 63, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 63, 0, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51]! + ending_table = [0, 2, 1]! + enc_table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' +) + +// decode decodes the base64 encoded `string` value passed in `data`. +// Please note: If you need to decode many strings repeatedly, take a look at `decode_in_buffer`. +// Example: assert base64.decode('ViBpbiBiYXNlIDY0') == 'V in base 64' +pub fn decode(data string) []byte { + size := data.len * 3 / 4 + if size <= 0 || data.len % 4 != 0 { + return [] + } + unsafe { + buffer := malloc(size) + n := decode_in_buffer(data, buffer) + return buffer.vbytes(n) + } +} + +// decode_str is the string variant of decode +pub fn decode_str(data string) string { + size := data.len * 3 / 4 + if size <= 0 || data.len % 4 != 0 { + return '' + } + unsafe { + buffer := malloc_noscan(size + 1) + buffer[size] = 0 + return tos(buffer, decode_in_buffer(data, buffer)) + } +} + +// encode encodes the `[]byte` value passed in `data` to base64. +// Please note: base64 encoding returns a `string` that is ~ 4/3 larger than the input. +// Please note: If you need to encode many strings repeatedly, take a look at `encode_in_buffer`. +// Example: assert base64.encode('V in base 64') == 'ViBpbiBiYXNlIDY0' +pub fn encode(data []byte) string { + return alloc_and_encode(data.data, data.len) +} + +// encode_str is the string variant of encode +pub fn encode_str(data string) string { + return alloc_and_encode(data.str, data.len) +} + +// alloc_and_encode is a private function that allocates and encodes data into a string +// Used by encode and encode_str +fn alloc_and_encode(src &byte, len int) string { + size := 4 * ((len + 2) / 3) + if size <= 0 { + return '' + } + unsafe { + buffer := malloc_noscan(size + 1) + buffer[size] = 0 + return tos(buffer, encode_from_buffer(buffer, src, len)) + } +} + +// url_decode returns a decoded URL `string` version of +// the a base64 url encoded `string` passed in `data`. +pub fn url_decode(data string) []byte { + mut result := data.replace_each(['-', '+', '_', '/']) + match result.len % 4 { + // Pad with trailing '='s + 2 { result += '==' } // 2 pad chars + 3 { result += '=' } // 1 pad char + else {} // no padding + } + return decode(result) +} + +// url_decode_str is the string variant of url_decode +pub fn url_decode_str(data string) string { + mut result := data.replace_each(['-', '+', '_', '/']) + match result.len % 4 { + // Pad with trailing '='s + 2 { result += '==' } // 2 pad chars + 3 { result += '=' } // 1 pad char + else {} // no padding + } + return decode_str(result) +} + +// url_encode returns a base64 URL encoded `string` version +// of the value passed in `data`. +pub fn url_encode(data []byte) string { + return encode(data).replace_each(['+', '-', '/', '_', '=', '']) +} + +// url_encode_str is the string variant of url_encode +pub fn url_encode_str(data string) string { + return encode_str(data).replace_each(['+', '-', '/', '_', '=', '']) +} + +// decode_in_buffer decodes the base64 encoded `string` reference passed in `data` into `buffer`. +// decode_in_buffer returns the size of the decoded data in the buffer. +// Please note: The `buffer` should be large enough (i.e. 3/4 of the data.len, or larger) +// to hold the decoded data. +// Please note: This function does NOT allocate new memory, and is thus suitable for handling very large strings. +pub fn decode_in_buffer(data &string, buffer &byte) int { + mut padding := 0 + if data.ends_with('=') { + if data.ends_with('==') { + padding = 2 + } else { + padding = 1 + } + } + // input_length is the length of meaningful data + input_length := data.len - padding + output_length := input_length * 3 / 4 + + mut i := 0 + mut j := 0 + mut b := &byte(0) + mut d := &byte(0) + unsafe { + d = &byte(data.str) + b = &byte(buffer) + } + for i < input_length { + mut char_a := 0 + mut char_b := 0 + mut char_c := 0 + mut char_d := 0 + if i < input_length { + char_a = base64.index[unsafe { d[i] }] + i++ + } + if i < input_length { + char_b = base64.index[unsafe { d[i] }] + i++ + } + if i < input_length { + char_c = base64.index[unsafe { d[i] }] + i++ + } + if i < input_length { + char_d = base64.index[unsafe { d[i] }] + i++ + } + + decoded_bytes := (char_a << 18) | (char_b << 12) | (char_c << 6) | (char_d << 0) + unsafe { + b[j] = byte(decoded_bytes >> 16) + b[j + 1] = byte((decoded_bytes >> 8) & 0xff) + b[j + 2] = byte((decoded_bytes >> 0) & 0xff) + } + j += 3 + } + return output_length +} + +// encode_in_buffer base64 encodes the `[]byte` passed in `data` into `buffer`. +// encode_in_buffer returns the size of the encoded data in the buffer. +// Please note: The buffer should be large enough (i.e. 4/3 of the data.len, or larger) to hold the encoded data. +// Please note: The function does NOT allocate new memory, and is suitable for handling very large strings. +pub fn encode_in_buffer(data []byte, buffer &byte) int { + return encode_from_buffer(buffer, data.data, data.len) +} + +// encode_from_buffer will perform encoding from any type of src buffer +// and write the bytes into `dest`. +// Please note: The `dest` buffer should be large enough (i.e. 4/3 of the src_len, or larger) to hold the encoded data. +// Please note: This function is for internal base64 encoding +fn encode_from_buffer(dest &byte, src &byte, src_len int) int { + input_length := src_len + output_length := 4 * ((input_length + 2) / 3) + + mut i := 0 + mut j := 0 + + mut d := unsafe { src } + mut b := unsafe { dest } + mut etable := base64.enc_table.str + for i < input_length { + mut octet_a := 0 + mut octet_b := 0 + mut octet_c := 0 + + if i < input_length { + octet_a = int(unsafe { d[i] }) + i++ + } + if i < input_length { + octet_b = int(unsafe { d[i] }) + i++ + } + if i < input_length { + octet_c = int(unsafe { d[i] }) + i++ + } + + triple := ((octet_a << 0x10) + (octet_b << 0x08) + octet_c) + + unsafe { + b[j] = etable[(triple >> 3 * 6) & 63] // 63 is 0x3F + b[j + 1] = etable[(triple >> 2 * 6) & 63] + b[j + 2] = etable[(triple >> 1 * 6) & 63] + b[j + 3] = etable[(triple >> 0 * 6) & 63] + } + j += 4 + } + + padding_length := base64.ending_table[input_length % 3] + for i = 0; i < padding_length; i++ { + unsafe { + b[output_length - 1 - i] = `=` + } + } + return output_length +} diff --git a/v_windows/v/old/vlib/encoding/base64/base64_memory_test.v b/v_windows/v/old/vlib/encoding/base64/base64_memory_test.v new file mode 100644 index 0000000..a1ac47a --- /dev/null +++ b/v_windows/v/old/vlib/encoding/base64/base64_memory_test.v @@ -0,0 +1,32 @@ +import encoding.base64 + +fn test_long_encoding() { + repeats := 1000 + input_size := 3000 + + s_original := []byte{len: input_size, init: `a`} + s_encoded := base64.encode(s_original) + s_decoded := base64.decode(s_encoded) + + assert s_encoded.len > s_original.len + assert s_original == s_decoded + + mut s := 0 + + ebuffer := unsafe { malloc(s_encoded.len) } + for _ in 0 .. repeats { + resultsize := base64.encode_in_buffer(s_original, ebuffer) + s += resultsize + assert resultsize == s_encoded.len + } + + dbuffer := unsafe { malloc(s_decoded.len) } + for _ in 0 .. repeats { + resultsize := base64.decode_in_buffer(s_encoded, dbuffer) + s += resultsize + assert resultsize == s_decoded.len + } + + println('Final s: $s') + // assert s == 39147008 +} diff --git a/v_windows/v/old/vlib/encoding/base64/base64_test.v b/v_windows/v/old/vlib/encoding/base64/base64_test.v new file mode 100644 index 0000000..e461330 --- /dev/null +++ b/v_windows/v/old/vlib/encoding/base64/base64_test.v @@ -0,0 +1,132 @@ +import encoding.base64 + +struct TestPair { + decoded string + encoded string +} + +const ( + pairs = [ + // RFC 3548 examples + TestPair{'\x14\xfb\x9c\x03\xd9\x7e', 'FPucA9l+'}, + TestPair{'\x14\xfb\x9c\x03\xd9', 'FPucA9k='}, + TestPair{'\x14\xfb\x9c\x03', 'FPucAw=='}, + // RFC 4648 examples + TestPair{'', ''}, + TestPair{'f', 'Zg=='}, + TestPair{'fo', 'Zm8='}, + TestPair{'foo', 'Zm9v'}, + TestPair{'foob', 'Zm9vYg=='}, + TestPair{'fooba', 'Zm9vYmE='}, + TestPair{'foobar', 'Zm9vYmFy'}, + // Wikipedia examples + TestPair{'sure.', 'c3VyZS4='}, + TestPair{'sure', 'c3VyZQ=='}, + TestPair{'sur', 'c3Vy'}, + TestPair{'su', 'c3U='}, + TestPair{'leasure.', 'bGVhc3VyZS4='}, + TestPair{'easure.', 'ZWFzdXJlLg=='}, + TestPair{'asure.', 'YXN1cmUu'}, + TestPair{'sure.', 'c3VyZS4='}, + ] + + man_pair = TestPair{'Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure.', 'TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb24sIGJ1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlciBhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQgYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGludWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZGdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm5hbCBwbGVhc3VyZS4='} +) + +fn test_decode() { + assert base64.decode(man_pair.encoded) == man_pair.decoded.bytes() + + // Test for incorrect padding. + assert base64.decode('aGk') == ''.bytes() + assert base64.decode('aGk=') == 'hi'.bytes() + assert base64.decode('aGk==') == ''.bytes() + + for i, p in pairs { + got := base64.decode(p.encoded) + if got != p.decoded.bytes() { + eprintln('pairs[$i]: expected = $p.decoded, got = $got') + assert false + } + } +} + +fn test_decode_str() { + assert base64.decode_str(man_pair.encoded) == man_pair.decoded + + // Test for incorrect padding. + assert base64.decode_str('aGk') == '' + assert base64.decode_str('aGk=') == 'hi' + assert base64.decode_str('aGk==') == '' + + for i, p in pairs { + got := base64.decode_str(p.encoded) + if got != p.decoded { + eprintln('pairs[$i]: expected = $p.decoded, got = $got') + assert false + } + } +} + +fn test_encode() { + assert base64.encode(man_pair.decoded.bytes()) == man_pair.encoded + + for i, p in pairs { + got := base64.encode(p.decoded.bytes()) + if got != p.encoded { + eprintln('pairs[$i]: expected = $p.encoded, got = $got') + assert false + } + } +} + +fn test_encode_str() { + assert base64.encode_str(man_pair.decoded) == man_pair.encoded + + for i, p in pairs { + got := base64.encode_str(p.decoded) + if got != p.encoded { + eprintln('pairs[$i]: expected = $p.encoded, got = $got') + assert false + } + } +} + +fn test_url_encode() { + test := base64.url_encode('Hello Base64Url encoding!'.bytes()) + assert test == 'SGVsbG8gQmFzZTY0VXJsIGVuY29kaW5nIQ' +} + +fn test_url_encode_str() { + test := base64.url_encode_str('Hello Base64Url encoding!') + assert test == 'SGVsbG8gQmFzZTY0VXJsIGVuY29kaW5nIQ' +} + +fn test_url_decode() { + test := base64.url_decode('SGVsbG8gQmFzZTY0VXJsIGVuY29kaW5nIQ') + assert test == 'Hello Base64Url encoding!'.bytes() +} + +fn test_url_decode_str() { + test := base64.url_decode_str('SGVsbG8gQmFzZTY0VXJsIGVuY29kaW5nIQ') + assert test == 'Hello Base64Url encoding!' +} + +fn test_encode_null_byte() { + assert base64.encode([byte(`A`), 0, `C`]) == 'QQBD' +} + +fn test_encode_null_byte_str() { + // While this works, bytestr() does a memcpy + s := [byte(`A`), 0, `C`].bytestr() + assert base64.encode_str(s) == 'QQBD' +} + +fn test_decode_null_byte() { + assert base64.decode('QQBD') == [byte(`A`), 0, `C`] +} + +fn test_decode_null_byte_str() { + // While this works, bytestr() does a memcpy + s := [byte(`A`), 0, `C`].bytestr() + assert base64.decode_str('QQBD') == s +} diff --git a/v_windows/v/old/vlib/encoding/binary/binary.v b/v_windows/v/old/vlib/encoding/binary/binary.v new file mode 100644 index 0000000..d7fe298 --- /dev/null +++ b/v_windows/v/old/vlib/encoding/binary/binary.v @@ -0,0 +1,100 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module binary + +// Little Endian +[inline] +pub fn little_endian_u16(b []byte) u16 { + _ = b[1] // bounds check + return u16(b[0]) | (u16(b[1]) << u16(8)) +} + +[inline] +pub fn little_endian_put_u16(mut b []byte, v u16) { + _ = b[1] // bounds check + b[0] = byte(v) + b[1] = byte(v >> u16(8)) +} + +[inline] +pub fn little_endian_u32(b []byte) u32 { + _ = b[3] // bounds check + return u32(b[0]) | (u32(b[1]) << u32(8)) | (u32(b[2]) << u32(16)) | (u32(b[3]) << u32(24)) +} + +[inline] +pub fn little_endian_put_u32(mut b []byte, v u32) { + _ = b[3] // bounds check + b[0] = byte(v) + b[1] = byte(v >> u32(8)) + b[2] = byte(v >> u32(16)) + b[3] = byte(v >> u32(24)) +} + +[inline] +pub fn little_endian_u64(b []byte) u64 { + _ = b[7] // bounds check + return u64(b[0]) | (u64(b[1]) << u64(8)) | (u64(b[2]) << u64(16)) | (u64(b[3]) << u64(24)) | (u64(b[4]) << u64(32)) | (u64(b[5]) << u64(40)) | (u64(b[6]) << u64(48)) | (u64(b[7]) << u64(56)) +} + +[inline] +pub fn little_endian_put_u64(mut b []byte, v u64) { + _ = b[7] // bounds check + b[0] = byte(v) + b[1] = byte(v >> u64(8)) + b[2] = byte(v >> u64(16)) + b[3] = byte(v >> u64(24)) + b[4] = byte(v >> u64(32)) + b[5] = byte(v >> u64(40)) + b[6] = byte(v >> u64(48)) + b[7] = byte(v >> u64(56)) +} + +// Big Endian +[inline] +pub fn big_endian_u16(b []byte) u16 { + _ = b[1] // bounds check + return u16(b[1]) | (u16(b[0]) << u16(8)) +} + +[inline] +pub fn big_endian_put_u16(mut b []byte, v u16) { + _ = b[1] // bounds check + b[0] = byte(v >> u16(8)) + b[1] = byte(v) +} + +[inline] +pub fn big_endian_u32(b []byte) u32 { + _ = b[3] // bounds check + return u32(b[3]) | (u32(b[2]) << u32(8)) | (u32(b[1]) << u32(16)) | (u32(b[0]) << u32(24)) +} + +[inline] +pub fn big_endian_put_u32(mut b []byte, v u32) { + _ = b[3] // bounds check + b[0] = byte(v >> u32(24)) + b[1] = byte(v >> u32(16)) + b[2] = byte(v >> u32(8)) + b[3] = byte(v) +} + +[inline] +pub fn big_endian_u64(b []byte) u64 { + _ = b[7] // bounds check + return u64(b[7]) | (u64(b[6]) << u64(8)) | (u64(b[5]) << u64(16)) | (u64(b[4]) << u64(24)) | (u64(b[3]) << u64(32)) | (u64(b[2]) << u64(40)) | (u64(b[1]) << u64(48)) | (u64(b[0]) << u64(56)) +} + +[inline] +pub fn big_endian_put_u64(mut b []byte, v u64) { + _ = b[7] // bounds check + b[0] = byte(v >> u64(56)) + b[1] = byte(v >> u64(48)) + b[2] = byte(v >> u64(40)) + b[3] = byte(v >> u64(32)) + b[4] = byte(v >> u64(24)) + b[5] = byte(v >> u64(16)) + b[6] = byte(v >> u64(8)) + b[7] = byte(v) +} diff --git a/v_windows/v/old/vlib/encoding/csv/README.md b/v_windows/v/old/vlib/encoding/csv/README.md new file mode 100644 index 0000000..01f3e4e --- /dev/null +++ b/v_windows/v/old/vlib/encoding/csv/README.md @@ -0,0 +1,19 @@ +## Reader example + +```v +import encoding.csv + +data := 'x,y\na,b,c\n' +mut parser := csv.new_reader(data) +// read each line +for { + items := parser.read() or { break } + println(items) +} +``` + +It prints: +``` +['x', 'y'] +['a', 'b', 'c'] +``` diff --git a/v_windows/v/old/vlib/encoding/csv/reader.v b/v_windows/v/old/vlib/encoding/csv/reader.v new file mode 100644 index 0000000..dafd022 --- /dev/null +++ b/v_windows/v/old/vlib/encoding/csv/reader.v @@ -0,0 +1,196 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module csv + +// Once interfaces are further along the idea would be to have something similar to +// go's io.reader & bufio.reader rather than reading the whole file into string, this +// would then satisfy that interface. I designed it this way to be easily adapted. +struct ErrCommentIsDelimiter { + msg string = 'encoding.csv: comment cannot be the same as delimiter' + code int +} + +struct ErrInvalidDelimiter { + msg string = 'encoding.csv: invalid delimiter' + code int +} + +struct ErrEndOfFile { + msg string = 'encoding.csv: end of file' + code int +} + +struct ErrInvalidLineEnding { + msg string = 'encoding.csv: could not find any valid line endings' + code int +} + +struct Reader { + // not used yet + // has_header bool + // headings []string + data string +pub mut: + delimiter byte + comment byte + is_mac_pre_osx_le bool + row_pos int +} + +// new_reader initializes a Reader with string data to parse +pub fn new_reader(data string) &Reader { + return &Reader{ + delimiter: `,` + comment: `#` + data: data + } +} + +// read reads a row from the CSV data. +// If successful, the result holds an array of each column's data. +pub fn (mut r Reader) read() ?[]string { + l := r.read_record() ? + return l +} + +// Once we have multi dimensional array +// pub fn (mut r Reader) read_all() ?[][]string { +// mut records := []string{} +// for { +// record := r.read_record() or { +// if err.error == err_eof.error { +// return records +// } else { +// return err +// } +// } +// records << record +// } +// return records +// } +fn (mut r Reader) read_line() ?string { + // last record + if r.row_pos == r.data.len { + return IError(&ErrEndOfFile{}) + } + le := if r.is_mac_pre_osx_le { '\r' } else { '\n' } + mut i := r.data.index_after(le, r.row_pos) + if i == -1 { + if r.row_pos == 0 { + // check for pre osx mac line endings + i = r.data.index_after('\r', r.row_pos) + if i != -1 { + r.is_mac_pre_osx_le = true + } else { + // no valid line endings found + return IError(&ErrInvalidLineEnding{}) + } + } else { + // No line ending on file + i = r.data.len - 1 + } + } + mut line := r.data[r.row_pos..i] + r.row_pos = i + 1 + // normalize win line endings (remove extra \r) + if !r.is_mac_pre_osx_le && (line.len >= 1 && line[line.len - 1] == `\r`) { + line = line[..line.len - 1] + } + return line +} + +fn (mut r Reader) read_record() ?[]string { + if r.delimiter == r.comment { + return IError(&ErrCommentIsDelimiter{}) + } + if !valid_delim(r.delimiter) { + return IError(&ErrInvalidDelimiter{}) + } + mut need_read := true + mut keep_raw := false + mut line := '' + mut fields := []string{} + mut i := -1 + for { + if need_read { + l := r.read_line() ? + if l.len <= 0 { + if keep_raw { + line += '\n' + } + continue + } else if l[0] == r.comment { + if keep_raw { + line += '\n' + l + } + continue + } else { + if keep_raw { + line += '\n' + } + line += l + } + need_read = false + keep_raw = false + } + if line.len == 0 || line[0] != `"` { // not quoted + j := line.index(r.delimiter.ascii_str()) or { + // last + fields << line[..line.len] + break + } + i = j + fields << line[..i] + line = line[i + 1..] + continue + } else { // quoted + mut need_more := true + mut has_double_quotes := false + mut j := 0 + mut n := 1 + for n < line.len { + if line[n] == `"` { + if n == line.len - 1 || line[n + 1] != `"` { + need_more = false + j = n - 1 + break + } else { + has_double_quotes = true + n++ + } + } + n++ + } + if need_more { + need_read = true + keep_raw = true + continue + } + line = line[1..] + if j + 1 == line.len { + // last record + fields << if has_double_quotes { line[..j].replace('""', '"') } else { line[..j] } + break + } + next := line[j + 1] + if next == r.delimiter { + fields << if has_double_quotes { line[..j].replace('""', '"') } else { line[..j] } + if j + 2 == line.len { + line = '' + } else { + line = line[j + 2..] + } + continue + } + } + if i <= -1 && fields.len == 0 { + return IError(&ErrInvalidDelimiter{}) + } + } + return fields +} + +fn valid_delim(b byte) bool { + return b != 0 && b != `"` && b != `\r` && b != `\n` +} diff --git a/v_windows/v/old/vlib/encoding/csv/reader_test.v b/v_windows/v/old/vlib/encoding/csv/reader_test.v new file mode 100644 index 0000000..cd54827 --- /dev/null +++ b/v_windows/v/old/vlib/encoding/csv/reader_test.v @@ -0,0 +1,253 @@ +import encoding.csv + +fn test_encoding_csv_reader() { + data := 'name,email,phone,other\njoe,joe@blow.com,0400000000,test\nsam,sam@likesham.com,0433000000,"test quoted field"\n#chris,chris@nomail.com,94444444,"commented row"\n' + mut csv_reader := csv.new_reader(data) + mut row_count := 0 + for { + row := csv_reader.read() or { break } + row_count++ + if row_count == 1 { + assert row[0] == 'name' + assert row[1] == 'email' + assert row[2] == 'phone' + assert row[3] == 'other' + } else if row_count == 2 { + assert row[0] == 'joe' + assert row[1] == 'joe@blow.com' + assert row[2] == '0400000000' + assert row[3] == 'test' + } else if row_count == 3 { + assert row[0] == 'sam' + assert row[1] == 'sam@likesham.com' + assert row[2] == '0433000000' + // quoted field + assert row[3] == 'test quoted field' + } + } + assert row_count == 3 +} + +fn test_line_break_lf() { + lf_data := 'name,email\njoe,joe@blow.com\n' + mut csv_reader := csv.new_reader(lf_data) + mut row_count := 0 + for { + row := csv_reader.read() or { break } + row_count++ + if row_count == 1 { + assert row[0] == 'name' + assert row[1] == 'email' + } else if row_count == 2 { + assert row[0] == 'joe' + assert row[1] == 'joe@blow.com' + } + } + assert row_count == 2 +} + +fn test_line_break_cr() { + cr_data := 'name,email\rjoe,joe@blow.com\r' + mut csv_reader := csv.new_reader(cr_data) + mut row_count := 0 + for { + row := csv_reader.read() or { break } + row_count++ + if row_count == 1 { + assert row[0] == 'name' + assert row[1] == 'email' + } else if row_count == 2 { + assert row[0] == 'joe' + assert row[1] == 'joe@blow.com' + } + } + assert row_count == 2 +} + +fn test_line_break_crlf() { + crlf_data := 'name,email\r\njoe,joe@blow.com\r\n' + mut csv_reader := csv.new_reader(crlf_data) + mut row_count := 0 + for { + row := csv_reader.read() or { break } + row_count++ + if row_count == 1 { + assert row[0] == 'name' + assert row[1] == 'email' + } else if row_count == 2 { + assert row[0] == 'joe' + assert row[1] == 'joe@blow.com' + } + } + assert row_count == 2 +} + +fn test_no_line_ending() { + data := 'name,email,phone,other\njoe,joe@blow.com,0400000000,test' + mut csv_reader := csv.new_reader(data) + mut row_count := 0 + for { + csv_reader.read() or { break } + row_count++ + } + assert row_count == 2 +} + +fn test_last_field_empty() { + data := '"name","description","value"\n"one","first","1"\n"two","second",\n' + mut csv_reader := csv.new_reader(data) + mut row_count := 0 + for { + row := csv_reader.read() or { break } + row_count++ + if row_count == 1 { + assert row[0] == 'name' + assert row[1] == 'description' + assert row[2] == 'value' + } else if row_count == 2 { + assert row[0] == 'one' + assert row[1] == 'first' + assert row[2] == '1' + } else if row_count == 3 { + assert row[0] == 'two' + assert row[1] == 'second' + assert row[2] == '' + } + } + assert row_count == 3 +} + +fn test_empty_fields_no_quotes() { + data := '1,2,3,4\n,6,7,8\n9,,11,12\n13,14,,16\n17,18,19,\n' + + mut csv_reader := csv.new_reader(data) + mut row_count := 0 + for { + row := csv_reader.read() or { break } + row_count++ + if row_count == 1 { + assert row[0] == '1' + assert row[1] == '2' + assert row[2] == '3' + assert row[3] == '4' + } else if row_count == 2 { + assert row[0] == '' + assert row[1] == '6' + assert row[2] == '7' + assert row[3] == '8' + } else if row_count == 3 { + assert row[0] == '9' + assert row[1] == '' + assert row[2] == '11' + assert row[3] == '12' + } else if row_count == 4 { + assert row[0] == '13' + assert row[1] == '14' + assert row[2] == '' + assert row[3] == '16' + } else if row_count == 5 { + assert row[0] == '17' + assert row[1] == '18' + assert row[2] == '19' + assert row[3] == '' + } + } + assert row_count == 5 +} + +fn test_empty_line() { + data := '"name","description","value"\n\n\n"one","first","1"\n\n"two","second",\n' + mut csv_reader := csv.new_reader(data) + mut row_count := 0 + for { + row := csv_reader.read() or { break } + row_count++ + if row_count == 1 { + assert row[0] == 'name' + assert row[1] == 'description' + assert row[2] == 'value' + } else if row_count == 2 { + assert row[0] == 'one' + assert row[1] == 'first' + assert row[2] == '1' + } else if row_count == 3 { + assert row[0] == 'two' + assert row[1] == 'second' + } + } + assert row_count == 3 +} + +fn test_field_multiple_line() { + data := '"name","multiple + + line","value"\n"one","first","1"\n' + mut csv_reader := csv.new_reader(data) + mut row_count := 0 + for { + row := csv_reader.read() or { break } + row_count++ + if row_count == 1 { + assert row[0] == 'name' + assert row[1] == 'multiple\n\n line' + assert row[2] == 'value' + } else if row_count == 2 { + assert row[0] == 'one' + assert row[1] == 'first' + assert row[2] == '1' + } + } + assert row_count == 2 +} + +fn test_field_quotes_for_parts() { + data := 'a1,"b1",c1\n"a2",b2,c2\na3,b3,"c3"\na4,b4,c4\n' + mut csv_reader := csv.new_reader(data) + mut row_count := 0 + for { + row := csv_reader.read() or { break } + row_count++ + if row_count == 1 { + assert row[0] == 'a1' + assert row[1] == 'b1' + assert row[2] == 'c1' + } else if row_count == 2 { + assert row[0] == 'a2' + assert row[1] == 'b2' + assert row[2] == 'c2' + } else if row_count == 3 { + assert row[0] == 'a3' + assert row[1] == 'b3' + assert row[2] == 'c3' + } else if row_count == 4 { + assert row[0] == 'a4' + assert row[1] == 'b4' + assert row[2] == 'c4' + } + } + assert row_count == 4 +} + +fn test_field_double_quotes() { + row1 := '11,"12\n13"\n' + row2 := '21,"2""2""\n23"\n' + row3 := '"3""1""",32\n' + data := row1 + row2 + row3 + mut csv_reader := csv.new_reader(data) + mut row_count := 0 + for { + row := csv_reader.read() or { break } + row_count++ + if row_count == 1 { + assert row[0] == '11' + assert row[1] == '12\n13' + } else if row_count == 2 { + assert row[0] == '21' + assert row[1] == '2"2"\n23' + } else if row_count == 3 { + assert row[0] == '3"1"' + assert row[1] == '32' + } + } + assert row_count == 3 +} diff --git a/v_windows/v/old/vlib/encoding/csv/writer.v b/v_windows/v/old/vlib/encoding/csv/writer.v new file mode 100644 index 0000000..735ca20 --- /dev/null +++ b/v_windows/v/old/vlib/encoding/csv/writer.v @@ -0,0 +1,80 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module csv + +import strings + +struct Writer { +mut: + sb strings.Builder +pub mut: + use_crlf bool + delimiter byte +} + +pub fn new_writer() &Writer { + return &Writer{ + delimiter: `,` + sb: strings.new_builder(200) + } +} + +// write writes a single record +pub fn (mut w Writer) write(record []string) ?bool { + if !valid_delim(w.delimiter) { + return IError(&ErrInvalidDelimiter{}) + } + le := if w.use_crlf { '\r\n' } else { '\n' } + for n, field_ in record { + mut field := field_ + if n > 0 { + w.sb.write_string(w.delimiter.ascii_str()) + } + if !w.field_needs_quotes(field) { + w.sb.write_string(field) + continue + } + w.sb.write_string('"') + for field.len > 0 { + mut i := field.index_any('"\r\n') + if i < 0 { + i = field.len + } + w.sb.write_string(field[..i]) + field = field[i..] + if field.len > 0 { + z := field[0] + match z { + `"` { w.sb.write_string('""') } + `\r`, `\n` { w.sb.write_string(le) } + else {} + } + field = field[1..] + } + } + w.sb.write_string('"') + } + w.sb.write_string(le) + return true +} + +// Once we have multi dimensional array +// pub fn (w &Writer) write_all(records [][]string) { +// for _, record in records { +// w.write(record) +// } +// } +fn (w &Writer) field_needs_quotes(field string) bool { + if field == '' { + return false + } + if field.contains(w.delimiter.ascii_str()) || (field.index_any('"\r\n') != -1) { + return true + } + return false +} + +pub fn (mut w Writer) str() string { + return w.sb.str() +} diff --git a/v_windows/v/old/vlib/encoding/csv/writer_test.v b/v_windows/v/old/vlib/encoding/csv/writer_test.v new file mode 100644 index 0000000..92882dd --- /dev/null +++ b/v_windows/v/old/vlib/encoding/csv/writer_test.v @@ -0,0 +1,11 @@ +import encoding.csv + +fn test_encoding_csv_writer() { + mut csv_writer := csv.new_writer() + + csv_writer.write(['name', 'email', 'phone', 'other']) or {} + csv_writer.write(['joe', 'joe@blow.com', '0400000000', 'test']) or {} + csv_writer.write(['sam', 'sam@likesham.com', '0433000000', 'needs, quoting']) or {} + + assert csv_writer.str() == 'name,email,phone,other\njoe,joe@blow.com,0400000000,test\nsam,sam@likesham.com,0433000000,"needs, quoting"\n' +} diff --git a/v_windows/v/old/vlib/encoding/utf8/east_asian/east_asian_width.v b/v_windows/v/old/vlib/encoding/utf8/east_asian/east_asian_width.v new file mode 100644 index 0000000..d1ac547 --- /dev/null +++ b/v_windows/v/old/vlib/encoding/utf8/east_asian/east_asian_width.v @@ -0,0 +1,1204 @@ +// Copyright (c) 2021 Takahiro Yaota, a.k.a. zakuro. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. + +module east_asian + +import encoding.utf8 + +// EastAsianWidthType represents East_Asian_Width informative prorperty +pub enum EastAsianWidthProperty { + full + half + wide + narrow + ambiguous + neutral +} + +// display_width return the display width as number of unicode chars from a string. +pub fn display_width(s string, ambiguous_width int) int { + mut i, mut n := 0, 0 + for i < s.len { + c_len := utf8_char_len(s[i]) + n += match east_asian_width_property_at(s, i) { + .ambiguous { ambiguous_width } + .full, .wide { int(2) } + else { int(1) } + } + i += c_len + } + return n +} + +// width_property_at returns the East Asian Width properties at string[index] +pub fn east_asian_width_property_at(s string, index int) EastAsianWidthProperty { + codepoint := utf8.get_uchar(s, index) + mut left, mut right := 0, east_asian.east_asian_width_data.len - 1 + for left <= right { + middle := left + ((right - left) / 2) + entry := east_asian.east_asian_width_data[middle] + if codepoint < entry.point { + right = middle - 1 + continue + } + + last := entry.point + entry.len + if codepoint > last { + left = middle + 1 + continue + } + + return entry.property + } + return .neutral +} + +struct EAWEntry { + property EastAsianWidthProperty + point int + len int +} + +// EastAsianWidth-13.0.0.txt +const ( + east_asian_width_data = [ + EAWEntry{.neutral, 0x0000, 32}, + EAWEntry{.narrow, 0x0020, 95}, + EAWEntry{.neutral, 0x007f, 34}, + EAWEntry{.ambiguous, 0x00a1, 1}, + EAWEntry{.narrow, 0x00a2, 2}, + EAWEntry{.ambiguous, 0x00a4, 1}, + EAWEntry{.narrow, 0x00a5, 2}, + EAWEntry{.ambiguous, 0x00a7, 2}, + EAWEntry{.neutral, 0x00a9, 1}, + EAWEntry{.ambiguous, 0x00aa, 1}, + EAWEntry{.neutral, 0x00ab, 1}, + EAWEntry{.narrow, 0x00ac, 1}, + EAWEntry{.ambiguous, 0x00ad, 2}, + EAWEntry{.narrow, 0x00af, 1}, + EAWEntry{.ambiguous, 0x00b0, 5}, + EAWEntry{.neutral, 0x00b5, 1}, + EAWEntry{.ambiguous, 0x00b6, 5}, + EAWEntry{.neutral, 0x00bb, 1}, + EAWEntry{.ambiguous, 0x00bc, 4}, + EAWEntry{.neutral, 0x00c0, 6}, + EAWEntry{.ambiguous, 0x00c6, 1}, + EAWEntry{.neutral, 0x00c7, 9}, + EAWEntry{.ambiguous, 0x00d0, 1}, + EAWEntry{.neutral, 0x00d1, 6}, + EAWEntry{.ambiguous, 0x00d7, 2}, + EAWEntry{.neutral, 0x00d9, 5}, + EAWEntry{.ambiguous, 0x00de, 4}, + EAWEntry{.neutral, 0x00e2, 4}, + EAWEntry{.ambiguous, 0x00e6, 1}, + EAWEntry{.neutral, 0x00e7, 1}, + EAWEntry{.ambiguous, 0x00e8, 3}, + EAWEntry{.neutral, 0x00eb, 1}, + EAWEntry{.ambiguous, 0x00ec, 2}, + EAWEntry{.neutral, 0x00ee, 2}, + EAWEntry{.ambiguous, 0x00f0, 1}, + EAWEntry{.neutral, 0x00f1, 1}, + EAWEntry{.ambiguous, 0x00f2, 2}, + EAWEntry{.neutral, 0x00f4, 3}, + EAWEntry{.ambiguous, 0x00f7, 4}, + EAWEntry{.neutral, 0x00fb, 1}, + EAWEntry{.ambiguous, 0x00fc, 1}, + EAWEntry{.neutral, 0x00fd, 1}, + EAWEntry{.ambiguous, 0x00fe, 1}, + EAWEntry{.neutral, 0x00ff, 2}, + EAWEntry{.ambiguous, 0x0101, 1}, + EAWEntry{.neutral, 0x0102, 15}, + EAWEntry{.ambiguous, 0x0111, 1}, + EAWEntry{.neutral, 0x0112, 1}, + EAWEntry{.ambiguous, 0x0113, 1}, + EAWEntry{.neutral, 0x0114, 7}, + EAWEntry{.ambiguous, 0x011b, 1}, + EAWEntry{.neutral, 0x011c, 10}, + EAWEntry{.ambiguous, 0x0126, 2}, + EAWEntry{.neutral, 0x0128, 3}, + EAWEntry{.ambiguous, 0x012b, 1}, + EAWEntry{.neutral, 0x012c, 5}, + EAWEntry{.ambiguous, 0x0131, 3}, + EAWEntry{.neutral, 0x0134, 4}, + EAWEntry{.ambiguous, 0x0138, 1}, + EAWEntry{.neutral, 0x0139, 6}, + EAWEntry{.ambiguous, 0x013f, 4}, + EAWEntry{.neutral, 0x0143, 1}, + EAWEntry{.ambiguous, 0x0144, 1}, + EAWEntry{.neutral, 0x0145, 3}, + EAWEntry{.ambiguous, 0x0148, 4}, + EAWEntry{.neutral, 0x014c, 1}, + EAWEntry{.ambiguous, 0x014d, 1}, + EAWEntry{.neutral, 0x014e, 4}, + EAWEntry{.ambiguous, 0x0152, 2}, + EAWEntry{.neutral, 0x0154, 18}, + EAWEntry{.ambiguous, 0x0166, 2}, + EAWEntry{.neutral, 0x0168, 3}, + EAWEntry{.ambiguous, 0x016b, 1}, + EAWEntry{.neutral, 0x016c, 98}, + EAWEntry{.ambiguous, 0x01ce, 1}, + EAWEntry{.neutral, 0x01cf, 1}, + EAWEntry{.ambiguous, 0x01d0, 1}, + EAWEntry{.neutral, 0x01d1, 1}, + EAWEntry{.ambiguous, 0x01d2, 1}, + EAWEntry{.neutral, 0x01d3, 1}, + EAWEntry{.ambiguous, 0x01d4, 1}, + EAWEntry{.neutral, 0x01d5, 1}, + EAWEntry{.ambiguous, 0x01d6, 1}, + EAWEntry{.neutral, 0x01d7, 1}, + EAWEntry{.ambiguous, 0x01d8, 1}, + EAWEntry{.neutral, 0x01d9, 1}, + EAWEntry{.ambiguous, 0x01da, 1}, + EAWEntry{.neutral, 0x01db, 1}, + EAWEntry{.ambiguous, 0x01dc, 1}, + EAWEntry{.neutral, 0x01dd, 116}, + EAWEntry{.ambiguous, 0x0251, 1}, + EAWEntry{.neutral, 0x0252, 15}, + EAWEntry{.ambiguous, 0x0261, 1}, + EAWEntry{.neutral, 0x0262, 98}, + EAWEntry{.ambiguous, 0x02c4, 1}, + EAWEntry{.neutral, 0x02c5, 2}, + EAWEntry{.ambiguous, 0x02c7, 1}, + EAWEntry{.neutral, 0x02c8, 1}, + EAWEntry{.ambiguous, 0x02c9, 3}, + EAWEntry{.neutral, 0x02cc, 1}, + EAWEntry{.ambiguous, 0x02cd, 1}, + EAWEntry{.neutral, 0x02ce, 2}, + EAWEntry{.ambiguous, 0x02d0, 1}, + EAWEntry{.neutral, 0x02d1, 7}, + EAWEntry{.ambiguous, 0x02d8, 4}, + EAWEntry{.neutral, 0x02dc, 1}, + EAWEntry{.ambiguous, 0x02dd, 1}, + EAWEntry{.neutral, 0x02de, 1}, + EAWEntry{.ambiguous, 0x02df, 1}, + EAWEntry{.neutral, 0x02e0, 32}, + EAWEntry{.ambiguous, 0x0300, 112}, + EAWEntry{.neutral, 0x0370, 8}, + EAWEntry{.neutral, 0x037a, 6}, + EAWEntry{.neutral, 0x0384, 7}, + EAWEntry{.neutral, 0x038c, 1}, + EAWEntry{.neutral, 0x038e, 3}, + EAWEntry{.ambiguous, 0x0391, 17}, + EAWEntry{.ambiguous, 0x03a3, 7}, + EAWEntry{.neutral, 0x03aa, 7}, + EAWEntry{.ambiguous, 0x03b1, 17}, + EAWEntry{.neutral, 0x03c2, 1}, + EAWEntry{.ambiguous, 0x03c3, 7}, + EAWEntry{.neutral, 0x03ca, 55}, + EAWEntry{.ambiguous, 0x0401, 1}, + EAWEntry{.neutral, 0x0402, 14}, + EAWEntry{.ambiguous, 0x0410, 64}, + EAWEntry{.neutral, 0x0450, 1}, + EAWEntry{.ambiguous, 0x0451, 1}, + EAWEntry{.neutral, 0x0452, 222}, + EAWEntry{.neutral, 0x0531, 38}, + EAWEntry{.neutral, 0x0559, 50}, + EAWEntry{.neutral, 0x058d, 3}, + EAWEntry{.neutral, 0x0591, 55}, + EAWEntry{.neutral, 0x05d0, 27}, + EAWEntry{.neutral, 0x05ef, 6}, + EAWEntry{.neutral, 0x0600, 29}, + EAWEntry{.neutral, 0x061e, 240}, + EAWEntry{.neutral, 0x070f, 60}, + EAWEntry{.neutral, 0x074d, 101}, + EAWEntry{.neutral, 0x07c0, 59}, + EAWEntry{.neutral, 0x07fd, 49}, + EAWEntry{.neutral, 0x0830, 15}, + EAWEntry{.neutral, 0x0840, 28}, + EAWEntry{.neutral, 0x085e, 1}, + EAWEntry{.neutral, 0x0860, 11}, + EAWEntry{.neutral, 0x08a0, 21}, + EAWEntry{.neutral, 0x08b6, 18}, + EAWEntry{.neutral, 0x08d3, 177}, + EAWEntry{.neutral, 0x0985, 8}, + EAWEntry{.neutral, 0x098f, 2}, + EAWEntry{.neutral, 0x0993, 22}, + EAWEntry{.neutral, 0x09aa, 7}, + EAWEntry{.neutral, 0x09b2, 1}, + EAWEntry{.neutral, 0x09b6, 4}, + EAWEntry{.neutral, 0x09bc, 9}, + EAWEntry{.neutral, 0x09c7, 2}, + EAWEntry{.neutral, 0x09cb, 4}, + EAWEntry{.neutral, 0x09d7, 1}, + EAWEntry{.neutral, 0x09dc, 2}, + EAWEntry{.neutral, 0x09df, 5}, + EAWEntry{.neutral, 0x09e6, 25}, + EAWEntry{.neutral, 0x0a01, 3}, + EAWEntry{.neutral, 0x0a05, 6}, + EAWEntry{.neutral, 0x0a0f, 2}, + EAWEntry{.neutral, 0x0a13, 22}, + EAWEntry{.neutral, 0x0a2a, 7}, + EAWEntry{.neutral, 0x0a32, 2}, + EAWEntry{.neutral, 0x0a35, 2}, + EAWEntry{.neutral, 0x0a38, 2}, + EAWEntry{.neutral, 0x0a3c, 1}, + EAWEntry{.neutral, 0x0a3e, 5}, + EAWEntry{.neutral, 0x0a47, 2}, + EAWEntry{.neutral, 0x0a4b, 3}, + EAWEntry{.neutral, 0x0a51, 1}, + EAWEntry{.neutral, 0x0a59, 4}, + EAWEntry{.neutral, 0x0a5e, 1}, + EAWEntry{.neutral, 0x0a66, 17}, + EAWEntry{.neutral, 0x0a81, 3}, + EAWEntry{.neutral, 0x0a85, 9}, + EAWEntry{.neutral, 0x0a8f, 3}, + EAWEntry{.neutral, 0x0a93, 22}, + EAWEntry{.neutral, 0x0aaa, 7}, + EAWEntry{.neutral, 0x0ab2, 2}, + EAWEntry{.neutral, 0x0ab5, 5}, + EAWEntry{.neutral, 0x0abc, 10}, + EAWEntry{.neutral, 0x0ac7, 3}, + EAWEntry{.neutral, 0x0acb, 3}, + EAWEntry{.neutral, 0x0ad0, 1}, + EAWEntry{.neutral, 0x0ae0, 4}, + EAWEntry{.neutral, 0x0ae6, 12}, + EAWEntry{.neutral, 0x0af9, 7}, + EAWEntry{.neutral, 0x0b01, 3}, + EAWEntry{.neutral, 0x0b05, 8}, + EAWEntry{.neutral, 0x0b0f, 2}, + EAWEntry{.neutral, 0x0b13, 22}, + EAWEntry{.neutral, 0x0b2a, 7}, + EAWEntry{.neutral, 0x0b32, 2}, + EAWEntry{.neutral, 0x0b35, 5}, + EAWEntry{.neutral, 0x0b3c, 9}, + EAWEntry{.neutral, 0x0b47, 2}, + EAWEntry{.neutral, 0x0b4b, 3}, + EAWEntry{.neutral, 0x0b55, 3}, + EAWEntry{.neutral, 0x0b5c, 2}, + EAWEntry{.neutral, 0x0b5f, 5}, + EAWEntry{.neutral, 0x0b66, 18}, + EAWEntry{.neutral, 0x0b82, 2}, + EAWEntry{.neutral, 0x0b85, 6}, + EAWEntry{.neutral, 0x0b8e, 3}, + EAWEntry{.neutral, 0x0b92, 4}, + EAWEntry{.neutral, 0x0b99, 2}, + EAWEntry{.neutral, 0x0b9c, 1}, + EAWEntry{.neutral, 0x0b9e, 2}, + EAWEntry{.neutral, 0x0ba3, 2}, + EAWEntry{.neutral, 0x0ba8, 3}, + EAWEntry{.neutral, 0x0bae, 12}, + EAWEntry{.neutral, 0x0bbe, 5}, + EAWEntry{.neutral, 0x0bc6, 3}, + EAWEntry{.neutral, 0x0bca, 4}, + EAWEntry{.neutral, 0x0bd0, 1}, + EAWEntry{.neutral, 0x0bd7, 1}, + EAWEntry{.neutral, 0x0be6, 21}, + EAWEntry{.neutral, 0x0c00, 13}, + EAWEntry{.neutral, 0x0c0e, 3}, + EAWEntry{.neutral, 0x0c12, 23}, + EAWEntry{.neutral, 0x0c2a, 16}, + EAWEntry{.neutral, 0x0c3d, 8}, + EAWEntry{.neutral, 0x0c46, 3}, + EAWEntry{.neutral, 0x0c4a, 4}, + EAWEntry{.neutral, 0x0c55, 2}, + EAWEntry{.neutral, 0x0c58, 3}, + EAWEntry{.neutral, 0x0c60, 4}, + EAWEntry{.neutral, 0x0c66, 10}, + EAWEntry{.neutral, 0x0c77, 22}, + EAWEntry{.neutral, 0x0c8e, 3}, + EAWEntry{.neutral, 0x0c92, 23}, + EAWEntry{.neutral, 0x0caa, 10}, + EAWEntry{.neutral, 0x0cb5, 5}, + EAWEntry{.neutral, 0x0cbc, 9}, + EAWEntry{.neutral, 0x0cc6, 3}, + EAWEntry{.neutral, 0x0cca, 4}, + EAWEntry{.neutral, 0x0cd5, 2}, + EAWEntry{.neutral, 0x0cde, 1}, + EAWEntry{.neutral, 0x0ce0, 4}, + EAWEntry{.neutral, 0x0ce6, 10}, + EAWEntry{.neutral, 0x0cf1, 2}, + EAWEntry{.neutral, 0x0d00, 13}, + EAWEntry{.neutral, 0x0d0e, 3}, + EAWEntry{.neutral, 0x0d12, 51}, + EAWEntry{.neutral, 0x0d46, 3}, + EAWEntry{.neutral, 0x0d4a, 6}, + EAWEntry{.neutral, 0x0d54, 16}, + EAWEntry{.neutral, 0x0d66, 26}, + EAWEntry{.neutral, 0x0d81, 3}, + EAWEntry{.neutral, 0x0d85, 18}, + EAWEntry{.neutral, 0x0d9a, 24}, + EAWEntry{.neutral, 0x0db3, 9}, + EAWEntry{.neutral, 0x0dbd, 1}, + EAWEntry{.neutral, 0x0dc0, 7}, + EAWEntry{.neutral, 0x0dca, 1}, + EAWEntry{.neutral, 0x0dcf, 6}, + EAWEntry{.neutral, 0x0dd6, 1}, + EAWEntry{.neutral, 0x0dd8, 8}, + EAWEntry{.neutral, 0x0de6, 10}, + EAWEntry{.neutral, 0x0df2, 3}, + EAWEntry{.neutral, 0x0e01, 58}, + EAWEntry{.neutral, 0x0e3f, 29}, + EAWEntry{.neutral, 0x0e81, 2}, + EAWEntry{.neutral, 0x0e84, 1}, + EAWEntry{.neutral, 0x0e86, 5}, + EAWEntry{.neutral, 0x0e8c, 24}, + EAWEntry{.neutral, 0x0ea5, 1}, + EAWEntry{.neutral, 0x0ea7, 23}, + EAWEntry{.neutral, 0x0ec0, 5}, + EAWEntry{.neutral, 0x0ec6, 1}, + EAWEntry{.neutral, 0x0ec8, 6}, + EAWEntry{.neutral, 0x0ed0, 10}, + EAWEntry{.neutral, 0x0edc, 4}, + EAWEntry{.neutral, 0x0f00, 72}, + EAWEntry{.neutral, 0x0f49, 36}, + EAWEntry{.neutral, 0x0f71, 39}, + EAWEntry{.neutral, 0x0f99, 36}, + EAWEntry{.neutral, 0x0fbe, 15}, + EAWEntry{.neutral, 0x0fce, 13}, + EAWEntry{.neutral, 0x1000, 198}, + EAWEntry{.neutral, 0x10c7, 1}, + EAWEntry{.neutral, 0x10cd, 1}, + EAWEntry{.neutral, 0x10d0, 48}, + EAWEntry{.wide, 0x1100, 96}, + EAWEntry{.neutral, 0x1160, 233}, + EAWEntry{.neutral, 0x124a, 4}, + EAWEntry{.neutral, 0x1250, 7}, + EAWEntry{.neutral, 0x1258, 1}, + EAWEntry{.neutral, 0x125a, 4}, + EAWEntry{.neutral, 0x1260, 41}, + EAWEntry{.neutral, 0x128a, 4}, + EAWEntry{.neutral, 0x1290, 33}, + EAWEntry{.neutral, 0x12b2, 4}, + EAWEntry{.neutral, 0x12b8, 7}, + EAWEntry{.neutral, 0x12c0, 1}, + EAWEntry{.neutral, 0x12c2, 4}, + EAWEntry{.neutral, 0x12c8, 15}, + EAWEntry{.neutral, 0x12d8, 57}, + EAWEntry{.neutral, 0x1312, 4}, + EAWEntry{.neutral, 0x1318, 67}, + EAWEntry{.neutral, 0x135d, 32}, + EAWEntry{.neutral, 0x1380, 26}, + EAWEntry{.neutral, 0x13a0, 86}, + EAWEntry{.neutral, 0x13f8, 6}, + EAWEntry{.neutral, 0x1400, 669}, + EAWEntry{.neutral, 0x16a0, 89}, + EAWEntry{.neutral, 0x1700, 13}, + EAWEntry{.neutral, 0x170e, 7}, + EAWEntry{.neutral, 0x1720, 23}, + EAWEntry{.neutral, 0x1740, 20}, + EAWEntry{.neutral, 0x1760, 13}, + EAWEntry{.neutral, 0x176e, 3}, + EAWEntry{.neutral, 0x1772, 2}, + EAWEntry{.neutral, 0x1780, 94}, + EAWEntry{.neutral, 0x17e0, 10}, + EAWEntry{.neutral, 0x17f0, 10}, + EAWEntry{.neutral, 0x1800, 15}, + EAWEntry{.neutral, 0x1810, 10}, + EAWEntry{.neutral, 0x1820, 89}, + EAWEntry{.neutral, 0x1880, 43}, + EAWEntry{.neutral, 0x18b0, 70}, + EAWEntry{.neutral, 0x1900, 31}, + EAWEntry{.neutral, 0x1920, 12}, + EAWEntry{.neutral, 0x1930, 12}, + EAWEntry{.neutral, 0x1940, 1}, + EAWEntry{.neutral, 0x1944, 42}, + EAWEntry{.neutral, 0x1970, 5}, + EAWEntry{.neutral, 0x1980, 44}, + EAWEntry{.neutral, 0x19b0, 26}, + EAWEntry{.neutral, 0x19d0, 11}, + EAWEntry{.neutral, 0x19de, 62}, + EAWEntry{.neutral, 0x1a1e, 65}, + EAWEntry{.neutral, 0x1a60, 29}, + EAWEntry{.neutral, 0x1a7f, 11}, + EAWEntry{.neutral, 0x1a90, 10}, + EAWEntry{.neutral, 0x1aa0, 14}, + EAWEntry{.neutral, 0x1ab0, 17}, + EAWEntry{.neutral, 0x1b00, 76}, + EAWEntry{.neutral, 0x1b50, 45}, + EAWEntry{.neutral, 0x1b80, 116}, + EAWEntry{.neutral, 0x1bfc, 60}, + EAWEntry{.neutral, 0x1c3b, 15}, + EAWEntry{.neutral, 0x1c4d, 60}, + EAWEntry{.neutral, 0x1c90, 43}, + EAWEntry{.neutral, 0x1cbd, 11}, + EAWEntry{.neutral, 0x1cd0, 43}, + EAWEntry{.neutral, 0x1d00, 250}, + EAWEntry{.neutral, 0x1dfb, 283}, + EAWEntry{.neutral, 0x1f18, 6}, + EAWEntry{.neutral, 0x1f20, 38}, + EAWEntry{.neutral, 0x1f48, 6}, + EAWEntry{.neutral, 0x1f50, 8}, + EAWEntry{.neutral, 0x1f59, 1}, + EAWEntry{.neutral, 0x1f5b, 1}, + EAWEntry{.neutral, 0x1f5d, 1}, + EAWEntry{.neutral, 0x1f5f, 31}, + EAWEntry{.neutral, 0x1f80, 53}, + EAWEntry{.neutral, 0x1fb6, 15}, + EAWEntry{.neutral, 0x1fc6, 14}, + EAWEntry{.neutral, 0x1fd6, 6}, + EAWEntry{.neutral, 0x1fdd, 19}, + EAWEntry{.neutral, 0x1ff2, 3}, + EAWEntry{.neutral, 0x1ff6, 9}, + EAWEntry{.neutral, 0x2000, 16}, + EAWEntry{.ambiguous, 0x2010, 1}, + EAWEntry{.neutral, 0x2011, 2}, + EAWEntry{.ambiguous, 0x2013, 4}, + EAWEntry{.neutral, 0x2017, 1}, + EAWEntry{.ambiguous, 0x2018, 2}, + EAWEntry{.neutral, 0x201a, 2}, + EAWEntry{.ambiguous, 0x201c, 2}, + EAWEntry{.neutral, 0x201e, 2}, + EAWEntry{.ambiguous, 0x2020, 3}, + EAWEntry{.neutral, 0x2023, 1}, + EAWEntry{.ambiguous, 0x2024, 4}, + EAWEntry{.neutral, 0x2028, 8}, + EAWEntry{.ambiguous, 0x2030, 1}, + EAWEntry{.neutral, 0x2031, 1}, + EAWEntry{.ambiguous, 0x2032, 2}, + EAWEntry{.neutral, 0x2034, 1}, + EAWEntry{.ambiguous, 0x2035, 1}, + EAWEntry{.neutral, 0x2036, 5}, + EAWEntry{.ambiguous, 0x203b, 1}, + EAWEntry{.neutral, 0x203c, 2}, + EAWEntry{.ambiguous, 0x203e, 1}, + EAWEntry{.neutral, 0x203f, 38}, + EAWEntry{.neutral, 0x2066, 12}, + EAWEntry{.ambiguous, 0x2074, 1}, + EAWEntry{.neutral, 0x2075, 10}, + EAWEntry{.ambiguous, 0x207f, 1}, + EAWEntry{.neutral, 0x2080, 1}, + EAWEntry{.ambiguous, 0x2081, 4}, + EAWEntry{.neutral, 0x2085, 10}, + EAWEntry{.neutral, 0x2090, 13}, + EAWEntry{.neutral, 0x20a0, 9}, + EAWEntry{.half, 0x20a9, 1}, + EAWEntry{.neutral, 0x20aa, 2}, + EAWEntry{.ambiguous, 0x20ac, 1}, + EAWEntry{.neutral, 0x20ad, 19}, + EAWEntry{.neutral, 0x20d0, 33}, + EAWEntry{.neutral, 0x2100, 3}, + EAWEntry{.ambiguous, 0x2103, 1}, + EAWEntry{.neutral, 0x2104, 1}, + EAWEntry{.ambiguous, 0x2105, 1}, + EAWEntry{.neutral, 0x2106, 3}, + EAWEntry{.ambiguous, 0x2109, 1}, + EAWEntry{.neutral, 0x210a, 9}, + EAWEntry{.ambiguous, 0x2113, 1}, + EAWEntry{.neutral, 0x2114, 2}, + EAWEntry{.ambiguous, 0x2116, 1}, + EAWEntry{.neutral, 0x2117, 10}, + EAWEntry{.ambiguous, 0x2121, 2}, + EAWEntry{.neutral, 0x2123, 3}, + EAWEntry{.ambiguous, 0x2126, 1}, + EAWEntry{.neutral, 0x2127, 4}, + EAWEntry{.ambiguous, 0x212b, 1}, + EAWEntry{.neutral, 0x212c, 39}, + EAWEntry{.ambiguous, 0x2153, 2}, + EAWEntry{.neutral, 0x2155, 6}, + EAWEntry{.ambiguous, 0x215b, 4}, + EAWEntry{.neutral, 0x215f, 1}, + EAWEntry{.ambiguous, 0x2160, 12}, + EAWEntry{.neutral, 0x216c, 4}, + EAWEntry{.ambiguous, 0x2170, 10}, + EAWEntry{.neutral, 0x217a, 15}, + EAWEntry{.ambiguous, 0x2189, 1}, + EAWEntry{.neutral, 0x218a, 2}, + EAWEntry{.ambiguous, 0x2190, 10}, + EAWEntry{.neutral, 0x219a, 30}, + EAWEntry{.ambiguous, 0x21b8, 2}, + EAWEntry{.neutral, 0x21ba, 24}, + EAWEntry{.ambiguous, 0x21d2, 1}, + EAWEntry{.neutral, 0x21d3, 1}, + EAWEntry{.ambiguous, 0x21d4, 1}, + EAWEntry{.neutral, 0x21d5, 18}, + EAWEntry{.ambiguous, 0x21e7, 1}, + EAWEntry{.neutral, 0x21e8, 24}, + EAWEntry{.ambiguous, 0x2200, 1}, + EAWEntry{.neutral, 0x2201, 1}, + EAWEntry{.ambiguous, 0x2202, 2}, + EAWEntry{.neutral, 0x2204, 3}, + EAWEntry{.ambiguous, 0x2207, 2}, + EAWEntry{.neutral, 0x2209, 2}, + EAWEntry{.ambiguous, 0x220b, 1}, + EAWEntry{.neutral, 0x220c, 3}, + EAWEntry{.ambiguous, 0x220f, 1}, + EAWEntry{.neutral, 0x2210, 1}, + EAWEntry{.ambiguous, 0x2211, 1}, + EAWEntry{.neutral, 0x2212, 3}, + EAWEntry{.ambiguous, 0x2215, 1}, + EAWEntry{.neutral, 0x2216, 4}, + EAWEntry{.ambiguous, 0x221a, 1}, + EAWEntry{.neutral, 0x221b, 2}, + EAWEntry{.ambiguous, 0x221d, 4}, + EAWEntry{.neutral, 0x2221, 2}, + EAWEntry{.ambiguous, 0x2223, 1}, + EAWEntry{.neutral, 0x2224, 1}, + EAWEntry{.ambiguous, 0x2225, 1}, + EAWEntry{.neutral, 0x2226, 1}, + EAWEntry{.ambiguous, 0x2227, 6}, + EAWEntry{.neutral, 0x222d, 1}, + EAWEntry{.ambiguous, 0x222e, 1}, + EAWEntry{.neutral, 0x222f, 5}, + EAWEntry{.ambiguous, 0x2234, 4}, + EAWEntry{.neutral, 0x2238, 4}, + EAWEntry{.ambiguous, 0x223c, 2}, + EAWEntry{.neutral, 0x223e, 10}, + EAWEntry{.ambiguous, 0x2248, 1}, + EAWEntry{.neutral, 0x2249, 3}, + EAWEntry{.ambiguous, 0x224c, 1}, + EAWEntry{.neutral, 0x224d, 5}, + EAWEntry{.ambiguous, 0x2252, 1}, + EAWEntry{.neutral, 0x2253, 13}, + EAWEntry{.ambiguous, 0x2260, 2}, + EAWEntry{.neutral, 0x2262, 2}, + EAWEntry{.ambiguous, 0x2264, 4}, + EAWEntry{.neutral, 0x2268, 2}, + EAWEntry{.ambiguous, 0x226a, 2}, + EAWEntry{.neutral, 0x226c, 2}, + EAWEntry{.ambiguous, 0x226e, 2}, + EAWEntry{.neutral, 0x2270, 18}, + EAWEntry{.ambiguous, 0x2282, 2}, + EAWEntry{.neutral, 0x2284, 2}, + EAWEntry{.ambiguous, 0x2286, 2}, + EAWEntry{.neutral, 0x2288, 13}, + EAWEntry{.ambiguous, 0x2295, 1}, + EAWEntry{.neutral, 0x2296, 3}, + EAWEntry{.ambiguous, 0x2299, 1}, + EAWEntry{.neutral, 0x229a, 11}, + EAWEntry{.ambiguous, 0x22a5, 1}, + EAWEntry{.neutral, 0x22a6, 25}, + EAWEntry{.ambiguous, 0x22bf, 1}, + EAWEntry{.neutral, 0x22c0, 82}, + EAWEntry{.ambiguous, 0x2312, 1}, + EAWEntry{.neutral, 0x2313, 7}, + EAWEntry{.wide, 0x231a, 2}, + EAWEntry{.neutral, 0x231c, 13}, + EAWEntry{.wide, 0x2329, 2}, + EAWEntry{.neutral, 0x232b, 190}, + EAWEntry{.wide, 0x23e9, 4}, + EAWEntry{.neutral, 0x23ed, 3}, + EAWEntry{.wide, 0x23f0, 1}, + EAWEntry{.neutral, 0x23f1, 2}, + EAWEntry{.wide, 0x23f3, 1}, + EAWEntry{.neutral, 0x23f4, 51}, + EAWEntry{.neutral, 0x2440, 11}, + EAWEntry{.ambiguous, 0x2460, 138}, + EAWEntry{.neutral, 0x24ea, 1}, + EAWEntry{.ambiguous, 0x24eb, 97}, + EAWEntry{.neutral, 0x254c, 4}, + EAWEntry{.ambiguous, 0x2550, 36}, + EAWEntry{.neutral, 0x2574, 12}, + EAWEntry{.ambiguous, 0x2580, 16}, + EAWEntry{.neutral, 0x2590, 2}, + EAWEntry{.ambiguous, 0x2592, 4}, + EAWEntry{.neutral, 0x2596, 10}, + EAWEntry{.ambiguous, 0x25a0, 2}, + EAWEntry{.neutral, 0x25a2, 1}, + EAWEntry{.ambiguous, 0x25a3, 7}, + EAWEntry{.neutral, 0x25aa, 8}, + EAWEntry{.ambiguous, 0x25b2, 2}, + EAWEntry{.neutral, 0x25b4, 2}, + EAWEntry{.ambiguous, 0x25b6, 2}, + EAWEntry{.neutral, 0x25b8, 4}, + EAWEntry{.ambiguous, 0x25bc, 2}, + EAWEntry{.neutral, 0x25be, 2}, + EAWEntry{.ambiguous, 0x25c0, 2}, + EAWEntry{.neutral, 0x25c2, 4}, + EAWEntry{.ambiguous, 0x25c6, 3}, + EAWEntry{.neutral, 0x25c9, 2}, + EAWEntry{.ambiguous, 0x25cb, 1}, + EAWEntry{.neutral, 0x25cc, 2}, + EAWEntry{.ambiguous, 0x25ce, 4}, + EAWEntry{.neutral, 0x25d2, 16}, + EAWEntry{.ambiguous, 0x25e2, 4}, + EAWEntry{.neutral, 0x25e6, 9}, + EAWEntry{.ambiguous, 0x25ef, 1}, + EAWEntry{.neutral, 0x25f0, 13}, + EAWEntry{.wide, 0x25fd, 2}, + EAWEntry{.neutral, 0x25ff, 6}, + EAWEntry{.ambiguous, 0x2605, 2}, + EAWEntry{.neutral, 0x2607, 2}, + EAWEntry{.ambiguous, 0x2609, 1}, + EAWEntry{.neutral, 0x260a, 4}, + EAWEntry{.ambiguous, 0x260e, 2}, + EAWEntry{.neutral, 0x2610, 4}, + EAWEntry{.wide, 0x2614, 2}, + EAWEntry{.neutral, 0x2616, 6}, + EAWEntry{.ambiguous, 0x261c, 1}, + EAWEntry{.neutral, 0x261d, 1}, + EAWEntry{.ambiguous, 0x261e, 1}, + EAWEntry{.neutral, 0x261f, 33}, + EAWEntry{.ambiguous, 0x2640, 1}, + EAWEntry{.neutral, 0x2641, 1}, + EAWEntry{.ambiguous, 0x2642, 1}, + EAWEntry{.neutral, 0x2643, 5}, + EAWEntry{.wide, 0x2648, 12}, + EAWEntry{.neutral, 0x2654, 12}, + EAWEntry{.ambiguous, 0x2660, 2}, + EAWEntry{.neutral, 0x2662, 1}, + EAWEntry{.ambiguous, 0x2663, 3}, + EAWEntry{.neutral, 0x2666, 1}, + EAWEntry{.ambiguous, 0x2667, 4}, + EAWEntry{.neutral, 0x266b, 1}, + EAWEntry{.ambiguous, 0x266c, 2}, + EAWEntry{.neutral, 0x266e, 1}, + EAWEntry{.ambiguous, 0x266f, 1}, + EAWEntry{.neutral, 0x2670, 15}, + EAWEntry{.wide, 0x267f, 1}, + EAWEntry{.neutral, 0x2680, 19}, + EAWEntry{.wide, 0x2693, 1}, + EAWEntry{.neutral, 0x2694, 10}, + EAWEntry{.ambiguous, 0x269e, 2}, + EAWEntry{.neutral, 0x26a0, 1}, + EAWEntry{.wide, 0x26a1, 1}, + EAWEntry{.neutral, 0x26a2, 8}, + EAWEntry{.wide, 0x26aa, 2}, + EAWEntry{.neutral, 0x26ac, 17}, + EAWEntry{.wide, 0x26bd, 2}, + EAWEntry{.ambiguous, 0x26bf, 1}, + EAWEntry{.neutral, 0x26c0, 4}, + EAWEntry{.wide, 0x26c4, 2}, + EAWEntry{.ambiguous, 0x26c6, 8}, + EAWEntry{.wide, 0x26ce, 1}, + EAWEntry{.ambiguous, 0x26cf, 5}, + EAWEntry{.wide, 0x26d4, 1}, + EAWEntry{.ambiguous, 0x26d5, 13}, + EAWEntry{.neutral, 0x26e2, 1}, + EAWEntry{.ambiguous, 0x26e3, 1}, + EAWEntry{.neutral, 0x26e4, 4}, + EAWEntry{.ambiguous, 0x26e8, 2}, + EAWEntry{.wide, 0x26ea, 1}, + EAWEntry{.ambiguous, 0x26eb, 7}, + EAWEntry{.wide, 0x26f2, 2}, + EAWEntry{.ambiguous, 0x26f4, 1}, + EAWEntry{.wide, 0x26f5, 1}, + EAWEntry{.ambiguous, 0x26f6, 4}, + EAWEntry{.wide, 0x26fa, 1}, + EAWEntry{.ambiguous, 0x26fb, 2}, + EAWEntry{.wide, 0x26fd, 1}, + EAWEntry{.ambiguous, 0x26fe, 2}, + EAWEntry{.neutral, 0x2700, 5}, + EAWEntry{.wide, 0x2705, 1}, + EAWEntry{.neutral, 0x2706, 4}, + EAWEntry{.wide, 0x270a, 2}, + EAWEntry{.neutral, 0x270c, 28}, + EAWEntry{.wide, 0x2728, 1}, + EAWEntry{.neutral, 0x2729, 20}, + EAWEntry{.ambiguous, 0x273d, 1}, + EAWEntry{.neutral, 0x273e, 14}, + EAWEntry{.wide, 0x274c, 1}, + EAWEntry{.neutral, 0x274d, 1}, + EAWEntry{.wide, 0x274e, 1}, + EAWEntry{.neutral, 0x274f, 4}, + EAWEntry{.wide, 0x2753, 3}, + EAWEntry{.neutral, 0x2756, 1}, + EAWEntry{.wide, 0x2757, 1}, + EAWEntry{.neutral, 0x2758, 30}, + EAWEntry{.ambiguous, 0x2776, 10}, + EAWEntry{.neutral, 0x2780, 21}, + EAWEntry{.wide, 0x2795, 3}, + EAWEntry{.neutral, 0x2798, 24}, + EAWEntry{.wide, 0x27b0, 1}, + EAWEntry{.neutral, 0x27b1, 14}, + EAWEntry{.wide, 0x27bf, 1}, + EAWEntry{.neutral, 0x27c0, 38}, + EAWEntry{.narrow, 0x27e6, 8}, + EAWEntry{.neutral, 0x27ee, 407}, + EAWEntry{.narrow, 0x2985, 2}, + EAWEntry{.neutral, 0x2987, 404}, + EAWEntry{.wide, 0x2b1b, 2}, + EAWEntry{.neutral, 0x2b1d, 51}, + EAWEntry{.wide, 0x2b50, 1}, + EAWEntry{.neutral, 0x2b51, 4}, + EAWEntry{.wide, 0x2b55, 1}, + EAWEntry{.ambiguous, 0x2b56, 4}, + EAWEntry{.neutral, 0x2b5a, 26}, + EAWEntry{.neutral, 0x2b76, 32}, + EAWEntry{.neutral, 0x2b97, 152}, + EAWEntry{.neutral, 0x2c30, 47}, + EAWEntry{.neutral, 0x2c60, 148}, + EAWEntry{.neutral, 0x2cf9, 45}, + EAWEntry{.neutral, 0x2d27, 1}, + EAWEntry{.neutral, 0x2d2d, 1}, + EAWEntry{.neutral, 0x2d30, 56}, + EAWEntry{.neutral, 0x2d6f, 2}, + EAWEntry{.neutral, 0x2d7f, 24}, + EAWEntry{.neutral, 0x2da0, 7}, + EAWEntry{.neutral, 0x2da8, 7}, + EAWEntry{.neutral, 0x2db0, 7}, + EAWEntry{.neutral, 0x2db8, 7}, + EAWEntry{.neutral, 0x2dc0, 7}, + EAWEntry{.neutral, 0x2dc8, 7}, + EAWEntry{.neutral, 0x2dd0, 7}, + EAWEntry{.neutral, 0x2dd8, 7}, + EAWEntry{.neutral, 0x2de0, 115}, + EAWEntry{.wide, 0x2e80, 26}, + EAWEntry{.wide, 0x2e9b, 89}, + EAWEntry{.wide, 0x2f00, 214}, + EAWEntry{.wide, 0x2ff0, 12}, + EAWEntry{.full, 0x3000, 1}, + EAWEntry{.wide, 0x3001, 62}, + EAWEntry{.neutral, 0x303f, 1}, + EAWEntry{.wide, 0x3041, 86}, + EAWEntry{.wide, 0x3099, 103}, + EAWEntry{.wide, 0x3105, 43}, + EAWEntry{.wide, 0x3131, 94}, + EAWEntry{.wide, 0x3190, 84}, + EAWEntry{.wide, 0x31f0, 47}, + EAWEntry{.wide, 0x3220, 40}, + EAWEntry{.ambiguous, 0x3248, 8}, + EAWEntry{.wide, 0x3250, 7024}, + EAWEntry{.neutral, 0x4dc0, 64}, + EAWEntry{.wide, 0x4e00, 22157}, + EAWEntry{.wide, 0xa490, 55}, + EAWEntry{.neutral, 0xa4d0, 348}, + EAWEntry{.neutral, 0xa640, 184}, + EAWEntry{.neutral, 0xa700, 192}, + EAWEntry{.neutral, 0xa7c2, 9}, + EAWEntry{.neutral, 0xa7f5, 56}, + EAWEntry{.neutral, 0xa830, 10}, + EAWEntry{.neutral, 0xa840, 56}, + EAWEntry{.neutral, 0xa880, 70}, + EAWEntry{.neutral, 0xa8ce, 12}, + EAWEntry{.neutral, 0xa8e0, 116}, + EAWEntry{.neutral, 0xa95f, 1}, + EAWEntry{.wide, 0xa960, 29}, + EAWEntry{.neutral, 0xa980, 78}, + EAWEntry{.neutral, 0xa9cf, 11}, + EAWEntry{.neutral, 0xa9de, 33}, + EAWEntry{.neutral, 0xaa00, 55}, + EAWEntry{.neutral, 0xaa40, 14}, + EAWEntry{.neutral, 0xaa50, 10}, + EAWEntry{.neutral, 0xaa5c, 103}, + EAWEntry{.neutral, 0xaadb, 28}, + EAWEntry{.neutral, 0xab01, 6}, + EAWEntry{.neutral, 0xab09, 6}, + EAWEntry{.neutral, 0xab11, 6}, + EAWEntry{.neutral, 0xab20, 7}, + EAWEntry{.neutral, 0xab28, 7}, + EAWEntry{.neutral, 0xab30, 60}, + EAWEntry{.neutral, 0xab70, 126}, + EAWEntry{.neutral, 0xabf0, 10}, + EAWEntry{.wide, 0xac00, 11172}, + EAWEntry{.neutral, 0xd7b0, 23}, + EAWEntry{.neutral, 0xd7cb, 49}, + EAWEntry{.neutral, 0xd800, 2048}, + EAWEntry{.ambiguous, 0xe000, 6400}, + EAWEntry{.wide, 0xf900, 512}, + EAWEntry{.neutral, 0xfb00, 7}, + EAWEntry{.neutral, 0xfb13, 5}, + EAWEntry{.neutral, 0xfb1d, 26}, + EAWEntry{.neutral, 0xfb38, 5}, + EAWEntry{.neutral, 0xfb3e, 1}, + EAWEntry{.neutral, 0xfb40, 2}, + EAWEntry{.neutral, 0xfb43, 2}, + EAWEntry{.neutral, 0xfb46, 124}, + EAWEntry{.neutral, 0xfbd3, 365}, + EAWEntry{.neutral, 0xfd50, 64}, + EAWEntry{.neutral, 0xfd92, 54}, + EAWEntry{.neutral, 0xfdf0, 14}, + EAWEntry{.ambiguous, 0xfe00, 16}, + EAWEntry{.wide, 0xfe10, 10}, + EAWEntry{.neutral, 0xfe20, 16}, + EAWEntry{.wide, 0xfe30, 35}, + EAWEntry{.wide, 0xfe54, 19}, + EAWEntry{.wide, 0xfe68, 4}, + EAWEntry{.neutral, 0xfe70, 5}, + EAWEntry{.neutral, 0xfe76, 135}, + EAWEntry{.neutral, 0xfeff, 1}, + EAWEntry{.full, 0xff01, 96}, + EAWEntry{.half, 0xff61, 94}, + EAWEntry{.half, 0xffc2, 6}, + EAWEntry{.half, 0xffca, 6}, + EAWEntry{.half, 0xffd2, 6}, + EAWEntry{.half, 0xffda, 3}, + EAWEntry{.full, 0xffe0, 7}, + EAWEntry{.half, 0xffe8, 7}, + EAWEntry{.neutral, 0xfff9, 4}, + EAWEntry{.ambiguous, 0xfffd, 1}, + EAWEntry{.neutral, 0x10000, 12}, + EAWEntry{.neutral, 0x1000d, 26}, + EAWEntry{.neutral, 0x10028, 19}, + EAWEntry{.neutral, 0x1003c, 2}, + EAWEntry{.neutral, 0x1003f, 15}, + EAWEntry{.neutral, 0x10050, 14}, + EAWEntry{.neutral, 0x10080, 123}, + EAWEntry{.neutral, 0x10100, 3}, + EAWEntry{.neutral, 0x10107, 45}, + EAWEntry{.neutral, 0x10137, 88}, + EAWEntry{.neutral, 0x10190, 13}, + EAWEntry{.neutral, 0x101a0, 1}, + EAWEntry{.neutral, 0x101d0, 46}, + EAWEntry{.neutral, 0x10280, 29}, + EAWEntry{.neutral, 0x102a0, 49}, + EAWEntry{.neutral, 0x102e0, 28}, + EAWEntry{.neutral, 0x10300, 36}, + EAWEntry{.neutral, 0x1032d, 30}, + EAWEntry{.neutral, 0x10350, 43}, + EAWEntry{.neutral, 0x10380, 30}, + EAWEntry{.neutral, 0x1039f, 37}, + EAWEntry{.neutral, 0x103c8, 14}, + EAWEntry{.neutral, 0x10400, 158}, + EAWEntry{.neutral, 0x104a0, 10}, + EAWEntry{.neutral, 0x104b0, 36}, + EAWEntry{.neutral, 0x104d8, 36}, + EAWEntry{.neutral, 0x10500, 40}, + EAWEntry{.neutral, 0x10530, 52}, + EAWEntry{.neutral, 0x1056f, 1}, + EAWEntry{.neutral, 0x10600, 311}, + EAWEntry{.neutral, 0x10740, 22}, + EAWEntry{.neutral, 0x10760, 8}, + EAWEntry{.neutral, 0x10800, 6}, + EAWEntry{.neutral, 0x10808, 1}, + EAWEntry{.neutral, 0x1080a, 44}, + EAWEntry{.neutral, 0x10837, 2}, + EAWEntry{.neutral, 0x1083c, 1}, + EAWEntry{.neutral, 0x1083f, 23}, + EAWEntry{.neutral, 0x10857, 72}, + EAWEntry{.neutral, 0x108a7, 9}, + EAWEntry{.neutral, 0x108e0, 19}, + EAWEntry{.neutral, 0x108f4, 2}, + EAWEntry{.neutral, 0x108fb, 33}, + EAWEntry{.neutral, 0x1091f, 27}, + EAWEntry{.neutral, 0x1093f, 1}, + EAWEntry{.neutral, 0x10980, 56}, + EAWEntry{.neutral, 0x109bc, 20}, + EAWEntry{.neutral, 0x109d2, 50}, + EAWEntry{.neutral, 0x10a05, 2}, + EAWEntry{.neutral, 0x10a0c, 8}, + EAWEntry{.neutral, 0x10a15, 3}, + EAWEntry{.neutral, 0x10a19, 29}, + EAWEntry{.neutral, 0x10a38, 3}, + EAWEntry{.neutral, 0x10a3f, 10}, + EAWEntry{.neutral, 0x10a50, 9}, + EAWEntry{.neutral, 0x10a60, 64}, + EAWEntry{.neutral, 0x10ac0, 39}, + EAWEntry{.neutral, 0x10aeb, 12}, + EAWEntry{.neutral, 0x10b00, 54}, + EAWEntry{.neutral, 0x10b39, 29}, + EAWEntry{.neutral, 0x10b58, 27}, + EAWEntry{.neutral, 0x10b78, 26}, + EAWEntry{.neutral, 0x10b99, 4}, + EAWEntry{.neutral, 0x10ba9, 7}, + EAWEntry{.neutral, 0x10c00, 73}, + EAWEntry{.neutral, 0x10c80, 51}, + EAWEntry{.neutral, 0x10cc0, 51}, + EAWEntry{.neutral, 0x10cfa, 46}, + EAWEntry{.neutral, 0x10d30, 10}, + EAWEntry{.neutral, 0x10e60, 31}, + EAWEntry{.neutral, 0x10e80, 42}, + EAWEntry{.neutral, 0x10eab, 3}, + EAWEntry{.neutral, 0x10eb0, 2}, + EAWEntry{.neutral, 0x10f00, 40}, + EAWEntry{.neutral, 0x10f30, 42}, + EAWEntry{.neutral, 0x10fb0, 28}, + EAWEntry{.neutral, 0x10fe0, 23}, + EAWEntry{.neutral, 0x11000, 78}, + EAWEntry{.neutral, 0x11052, 30}, + EAWEntry{.neutral, 0x1107f, 67}, + EAWEntry{.neutral, 0x110cd, 1}, + EAWEntry{.neutral, 0x110d0, 25}, + EAWEntry{.neutral, 0x110f0, 10}, + EAWEntry{.neutral, 0x11100, 53}, + EAWEntry{.neutral, 0x11136, 18}, + EAWEntry{.neutral, 0x11150, 39}, + EAWEntry{.neutral, 0x11180, 96}, + EAWEntry{.neutral, 0x111e1, 20}, + EAWEntry{.neutral, 0x11200, 18}, + EAWEntry{.neutral, 0x11213, 44}, + EAWEntry{.neutral, 0x11280, 7}, + EAWEntry{.neutral, 0x11288, 1}, + EAWEntry{.neutral, 0x1128a, 4}, + EAWEntry{.neutral, 0x1128f, 15}, + EAWEntry{.neutral, 0x1129f, 11}, + EAWEntry{.neutral, 0x112b0, 59}, + EAWEntry{.neutral, 0x112f0, 10}, + EAWEntry{.neutral, 0x11300, 4}, + EAWEntry{.neutral, 0x11305, 8}, + EAWEntry{.neutral, 0x1130f, 2}, + EAWEntry{.neutral, 0x11313, 22}, + EAWEntry{.neutral, 0x1132a, 7}, + EAWEntry{.neutral, 0x11332, 2}, + EAWEntry{.neutral, 0x11335, 5}, + EAWEntry{.neutral, 0x1133b, 10}, + EAWEntry{.neutral, 0x11347, 2}, + EAWEntry{.neutral, 0x1134b, 3}, + EAWEntry{.neutral, 0x11350, 1}, + EAWEntry{.neutral, 0x11357, 1}, + EAWEntry{.neutral, 0x1135d, 7}, + EAWEntry{.neutral, 0x11366, 7}, + EAWEntry{.neutral, 0x11370, 5}, + EAWEntry{.neutral, 0x11400, 92}, + EAWEntry{.neutral, 0x1145d, 5}, + EAWEntry{.neutral, 0x11480, 72}, + EAWEntry{.neutral, 0x114d0, 10}, + EAWEntry{.neutral, 0x11580, 54}, + EAWEntry{.neutral, 0x115b8, 38}, + EAWEntry{.neutral, 0x11600, 69}, + EAWEntry{.neutral, 0x11650, 10}, + EAWEntry{.neutral, 0x11660, 13}, + EAWEntry{.neutral, 0x11680, 57}, + EAWEntry{.neutral, 0x116c0, 10}, + EAWEntry{.neutral, 0x11700, 27}, + EAWEntry{.neutral, 0x1171d, 15}, + EAWEntry{.neutral, 0x11730, 16}, + EAWEntry{.neutral, 0x11800, 60}, + EAWEntry{.neutral, 0x118a0, 83}, + EAWEntry{.neutral, 0x118ff, 8}, + EAWEntry{.neutral, 0x11909, 1}, + EAWEntry{.neutral, 0x1190c, 8}, + EAWEntry{.neutral, 0x11915, 2}, + EAWEntry{.neutral, 0x11918, 30}, + EAWEntry{.neutral, 0x11937, 2}, + EAWEntry{.neutral, 0x1193b, 12}, + EAWEntry{.neutral, 0x11950, 10}, + EAWEntry{.neutral, 0x119a0, 8}, + EAWEntry{.neutral, 0x119aa, 46}, + EAWEntry{.neutral, 0x119da, 11}, + EAWEntry{.neutral, 0x11a00, 72}, + EAWEntry{.neutral, 0x11a50, 83}, + EAWEntry{.neutral, 0x11ac0, 57}, + EAWEntry{.neutral, 0x11c00, 9}, + EAWEntry{.neutral, 0x11c0a, 45}, + EAWEntry{.neutral, 0x11c38, 14}, + EAWEntry{.neutral, 0x11c50, 29}, + EAWEntry{.neutral, 0x11c70, 32}, + EAWEntry{.neutral, 0x11c92, 22}, + EAWEntry{.neutral, 0x11ca9, 14}, + EAWEntry{.neutral, 0x11d00, 7}, + EAWEntry{.neutral, 0x11d08, 2}, + EAWEntry{.neutral, 0x11d0b, 44}, + EAWEntry{.neutral, 0x11d3a, 1}, + EAWEntry{.neutral, 0x11d3c, 2}, + EAWEntry{.neutral, 0x11d3f, 9}, + EAWEntry{.neutral, 0x11d50, 10}, + EAWEntry{.neutral, 0x11d60, 6}, + EAWEntry{.neutral, 0x11d67, 2}, + EAWEntry{.neutral, 0x11d6a, 37}, + EAWEntry{.neutral, 0x11d90, 2}, + EAWEntry{.neutral, 0x11d93, 6}, + EAWEntry{.neutral, 0x11da0, 10}, + EAWEntry{.neutral, 0x11ee0, 25}, + EAWEntry{.neutral, 0x11fb0, 1}, + EAWEntry{.neutral, 0x11fc0, 50}, + EAWEntry{.neutral, 0x11fff, 923}, + EAWEntry{.neutral, 0x12400, 111}, + EAWEntry{.neutral, 0x12470, 5}, + EAWEntry{.neutral, 0x12480, 196}, + EAWEntry{.neutral, 0x13000, 1071}, + EAWEntry{.neutral, 0x13430, 9}, + EAWEntry{.neutral, 0x14400, 583}, + EAWEntry{.neutral, 0x16800, 569}, + EAWEntry{.neutral, 0x16a40, 31}, + EAWEntry{.neutral, 0x16a60, 10}, + EAWEntry{.neutral, 0x16a6e, 2}, + EAWEntry{.neutral, 0x16ad0, 30}, + EAWEntry{.neutral, 0x16af0, 6}, + EAWEntry{.neutral, 0x16b00, 70}, + EAWEntry{.neutral, 0x16b50, 10}, + EAWEntry{.neutral, 0x16b5b, 7}, + EAWEntry{.neutral, 0x16b63, 21}, + EAWEntry{.neutral, 0x16b7d, 19}, + EAWEntry{.neutral, 0x16e40, 91}, + EAWEntry{.neutral, 0x16f00, 75}, + EAWEntry{.neutral, 0x16f4f, 57}, + EAWEntry{.neutral, 0x16f8f, 17}, + EAWEntry{.wide, 0x16fe0, 5}, + EAWEntry{.wide, 0x16ff0, 2}, + EAWEntry{.wide, 0x17000, 6136}, + EAWEntry{.wide, 0x18800, 1238}, + EAWEntry{.wide, 0x18d00, 9}, + EAWEntry{.wide, 0x1b000, 287}, + EAWEntry{.wide, 0x1b150, 3}, + EAWEntry{.wide, 0x1b164, 4}, + EAWEntry{.wide, 0x1b170, 396}, + EAWEntry{.neutral, 0x1bc00, 107}, + EAWEntry{.neutral, 0x1bc70, 13}, + EAWEntry{.neutral, 0x1bc80, 9}, + EAWEntry{.neutral, 0x1bc90, 10}, + EAWEntry{.neutral, 0x1bc9c, 8}, + EAWEntry{.neutral, 0x1d000, 246}, + EAWEntry{.neutral, 0x1d100, 39}, + EAWEntry{.neutral, 0x1d129, 192}, + EAWEntry{.neutral, 0x1d200, 70}, + EAWEntry{.neutral, 0x1d2e0, 20}, + EAWEntry{.neutral, 0x1d300, 87}, + EAWEntry{.neutral, 0x1d360, 25}, + EAWEntry{.neutral, 0x1d400, 85}, + EAWEntry{.neutral, 0x1d456, 71}, + EAWEntry{.neutral, 0x1d49e, 2}, + EAWEntry{.neutral, 0x1d4a2, 1}, + EAWEntry{.neutral, 0x1d4a5, 2}, + EAWEntry{.neutral, 0x1d4a9, 4}, + EAWEntry{.neutral, 0x1d4ae, 12}, + EAWEntry{.neutral, 0x1d4bb, 1}, + EAWEntry{.neutral, 0x1d4bd, 7}, + EAWEntry{.neutral, 0x1d4c5, 65}, + EAWEntry{.neutral, 0x1d507, 4}, + EAWEntry{.neutral, 0x1d50d, 8}, + EAWEntry{.neutral, 0x1d516, 7}, + EAWEntry{.neutral, 0x1d51e, 28}, + EAWEntry{.neutral, 0x1d53b, 4}, + EAWEntry{.neutral, 0x1d540, 5}, + EAWEntry{.neutral, 0x1d546, 1}, + EAWEntry{.neutral, 0x1d54a, 7}, + EAWEntry{.neutral, 0x1d552, 340}, + EAWEntry{.neutral, 0x1d6a8, 292}, + EAWEntry{.neutral, 0x1d7ce, 702}, + EAWEntry{.neutral, 0x1da9b, 5}, + EAWEntry{.neutral, 0x1daa1, 15}, + EAWEntry{.neutral, 0x1e000, 7}, + EAWEntry{.neutral, 0x1e008, 17}, + EAWEntry{.neutral, 0x1e01b, 7}, + EAWEntry{.neutral, 0x1e023, 2}, + EAWEntry{.neutral, 0x1e026, 5}, + EAWEntry{.neutral, 0x1e100, 45}, + EAWEntry{.neutral, 0x1e130, 14}, + EAWEntry{.neutral, 0x1e140, 10}, + EAWEntry{.neutral, 0x1e14e, 2}, + EAWEntry{.neutral, 0x1e2c0, 58}, + EAWEntry{.neutral, 0x1e2ff, 1}, + EAWEntry{.neutral, 0x1e800, 197}, + EAWEntry{.neutral, 0x1e8c7, 16}, + EAWEntry{.neutral, 0x1e900, 76}, + EAWEntry{.neutral, 0x1e950, 10}, + EAWEntry{.neutral, 0x1e95e, 2}, + EAWEntry{.neutral, 0x1ec71, 68}, + EAWEntry{.neutral, 0x1ed01, 61}, + EAWEntry{.neutral, 0x1ee00, 4}, + EAWEntry{.neutral, 0x1ee05, 27}, + EAWEntry{.neutral, 0x1ee21, 2}, + EAWEntry{.neutral, 0x1ee24, 1}, + EAWEntry{.neutral, 0x1ee27, 1}, + EAWEntry{.neutral, 0x1ee29, 10}, + EAWEntry{.neutral, 0x1ee34, 4}, + EAWEntry{.neutral, 0x1ee39, 1}, + EAWEntry{.neutral, 0x1ee3b, 1}, + EAWEntry{.neutral, 0x1ee42, 1}, + EAWEntry{.neutral, 0x1ee47, 1}, + EAWEntry{.neutral, 0x1ee49, 1}, + EAWEntry{.neutral, 0x1ee4b, 1}, + EAWEntry{.neutral, 0x1ee4d, 3}, + EAWEntry{.neutral, 0x1ee51, 2}, + EAWEntry{.neutral, 0x1ee54, 1}, + EAWEntry{.neutral, 0x1ee57, 1}, + EAWEntry{.neutral, 0x1ee59, 1}, + EAWEntry{.neutral, 0x1ee5b, 1}, + EAWEntry{.neutral, 0x1ee5d, 1}, + EAWEntry{.neutral, 0x1ee5f, 1}, + EAWEntry{.neutral, 0x1ee61, 2}, + EAWEntry{.neutral, 0x1ee64, 1}, + EAWEntry{.neutral, 0x1ee67, 4}, + EAWEntry{.neutral, 0x1ee6c, 7}, + EAWEntry{.neutral, 0x1ee74, 4}, + EAWEntry{.neutral, 0x1ee79, 4}, + EAWEntry{.neutral, 0x1ee7e, 1}, + EAWEntry{.neutral, 0x1ee80, 10}, + EAWEntry{.neutral, 0x1ee8b, 17}, + EAWEntry{.neutral, 0x1eea1, 3}, + EAWEntry{.neutral, 0x1eea5, 5}, + EAWEntry{.neutral, 0x1eeab, 17}, + EAWEntry{.neutral, 0x1eef0, 2}, + EAWEntry{.neutral, 0x1f000, 4}, + EAWEntry{.wide, 0x1f004, 1}, + EAWEntry{.neutral, 0x1f005, 39}, + EAWEntry{.neutral, 0x1f030, 100}, + EAWEntry{.neutral, 0x1f0a0, 15}, + EAWEntry{.neutral, 0x1f0b1, 15}, + EAWEntry{.neutral, 0x1f0c1, 14}, + EAWEntry{.wide, 0x1f0cf, 1}, + EAWEntry{.neutral, 0x1f0d1, 37}, + EAWEntry{.ambiguous, 0x1f100, 11}, + EAWEntry{.neutral, 0x1f10b, 5}, + EAWEntry{.ambiguous, 0x1f110, 30}, + EAWEntry{.neutral, 0x1f12e, 2}, + EAWEntry{.ambiguous, 0x1f130, 58}, + EAWEntry{.neutral, 0x1f16a, 6}, + EAWEntry{.ambiguous, 0x1f170, 30}, + EAWEntry{.wide, 0x1f18e, 1}, + EAWEntry{.ambiguous, 0x1f18f, 2}, + EAWEntry{.wide, 0x1f191, 10}, + EAWEntry{.ambiguous, 0x1f19b, 18}, + EAWEntry{.neutral, 0x1f1ad, 1}, + EAWEntry{.neutral, 0x1f1e6, 26}, + EAWEntry{.wide, 0x1f200, 3}, + EAWEntry{.wide, 0x1f210, 44}, + EAWEntry{.wide, 0x1f240, 9}, + EAWEntry{.wide, 0x1f250, 2}, + EAWEntry{.wide, 0x1f260, 6}, + EAWEntry{.wide, 0x1f300, 33}, + EAWEntry{.neutral, 0x1f321, 12}, + EAWEntry{.wide, 0x1f32d, 9}, + EAWEntry{.neutral, 0x1f336, 1}, + EAWEntry{.wide, 0x1f337, 70}, + EAWEntry{.neutral, 0x1f37d, 1}, + EAWEntry{.wide, 0x1f37e, 22}, + EAWEntry{.neutral, 0x1f394, 12}, + EAWEntry{.wide, 0x1f3a0, 43}, + EAWEntry{.neutral, 0x1f3cb, 4}, + EAWEntry{.wide, 0x1f3cf, 5}, + EAWEntry{.neutral, 0x1f3d4, 12}, + EAWEntry{.wide, 0x1f3e0, 17}, + EAWEntry{.neutral, 0x1f3f1, 3}, + EAWEntry{.wide, 0x1f3f4, 1}, + EAWEntry{.neutral, 0x1f3f5, 3}, + EAWEntry{.wide, 0x1f3f8, 71}, + EAWEntry{.neutral, 0x1f43f, 1}, + EAWEntry{.wide, 0x1f440, 1}, + EAWEntry{.neutral, 0x1f441, 1}, + EAWEntry{.wide, 0x1f442, 187}, + EAWEntry{.neutral, 0x1f4fd, 2}, + EAWEntry{.wide, 0x1f4ff, 63}, + EAWEntry{.neutral, 0x1f53e, 13}, + EAWEntry{.wide, 0x1f54b, 4}, + EAWEntry{.neutral, 0x1f54f, 1}, + EAWEntry{.wide, 0x1f550, 24}, + EAWEntry{.neutral, 0x1f568, 18}, + EAWEntry{.wide, 0x1f57a, 1}, + EAWEntry{.neutral, 0x1f57b, 26}, + EAWEntry{.wide, 0x1f595, 2}, + EAWEntry{.neutral, 0x1f597, 13}, + EAWEntry{.wide, 0x1f5a4, 1}, + EAWEntry{.neutral, 0x1f5a5, 86}, + EAWEntry{.wide, 0x1f5fb, 85}, + EAWEntry{.neutral, 0x1f650, 48}, + EAWEntry{.wide, 0x1f680, 70}, + EAWEntry{.neutral, 0x1f6c6, 6}, + EAWEntry{.wide, 0x1f6cc, 1}, + EAWEntry{.neutral, 0x1f6cd, 3}, + EAWEntry{.wide, 0x1f6d0, 3}, + EAWEntry{.neutral, 0x1f6d3, 2}, + EAWEntry{.wide, 0x1f6d5, 3}, + EAWEntry{.neutral, 0x1f6e0, 11}, + EAWEntry{.wide, 0x1f6eb, 2}, + EAWEntry{.neutral, 0x1f6f0, 4}, + EAWEntry{.wide, 0x1f6f4, 9}, + EAWEntry{.neutral, 0x1f700, 116}, + EAWEntry{.neutral, 0x1f780, 89}, + EAWEntry{.wide, 0x1f7e0, 12}, + EAWEntry{.neutral, 0x1f800, 12}, + EAWEntry{.neutral, 0x1f810, 56}, + EAWEntry{.neutral, 0x1f850, 10}, + EAWEntry{.neutral, 0x1f860, 40}, + EAWEntry{.neutral, 0x1f890, 30}, + EAWEntry{.neutral, 0x1f8b0, 2}, + EAWEntry{.neutral, 0x1f900, 12}, + EAWEntry{.wide, 0x1f90c, 47}, + EAWEntry{.neutral, 0x1f93b, 1}, + EAWEntry{.wide, 0x1f93c, 10}, + EAWEntry{.neutral, 0x1f946, 1}, + EAWEntry{.wide, 0x1f947, 50}, + EAWEntry{.wide, 0x1f97a, 82}, + EAWEntry{.wide, 0x1f9cd, 51}, + EAWEntry{.neutral, 0x1fa00, 84}, + EAWEntry{.neutral, 0x1fa60, 14}, + EAWEntry{.wide, 0x1fa70, 5}, + EAWEntry{.wide, 0x1fa78, 3}, + EAWEntry{.wide, 0x1fa80, 7}, + EAWEntry{.wide, 0x1fa90, 25}, + EAWEntry{.wide, 0x1fab0, 7}, + EAWEntry{.wide, 0x1fac0, 3}, + EAWEntry{.wide, 0x1fad0, 7}, + EAWEntry{.neutral, 0x1fb00, 147}, + EAWEntry{.neutral, 0x1fb94, 55}, + EAWEntry{.neutral, 0x1fbf0, 10}, + EAWEntry{.wide, 0x20000, 65534}, + EAWEntry{.wide, 0x30000, 65534}, + EAWEntry{.neutral, 0xe0001, 1}, + EAWEntry{.neutral, 0xe0020, 96}, + EAWEntry{.ambiguous, 0xe0100, 240}, + EAWEntry{.ambiguous, 0xf0000, 65534}, + EAWEntry{.ambiguous, 0x100000, 65534}, + ] +) diff --git a/v_windows/v/old/vlib/encoding/utf8/east_asian/east_asian_width_test.v b/v_windows/v/old/vlib/encoding/utf8/east_asian/east_asian_width_test.v new file mode 100644 index 0000000..a44a9f8 --- /dev/null +++ b/v_windows/v/old/vlib/encoding/utf8/east_asian/east_asian_width_test.v @@ -0,0 +1,23 @@ +module east_asian + +fn test_width() { + assert east_asian_width_property_at('A', 0) == .narrow + assert east_asian_width_property_at('A', 0) == .full + assert east_asian_width_property_at('ア', 0) == .half + assert east_asian_width_property_at('ア', 0) == .wide + assert east_asian_width_property_at('☆', 0) == .ambiguous + assert east_asian_width_property_at('ج', 0) == .neutral + assert display_width('abc', 1) == 3 + assert display_width('ひらがな', 1) == 8 + assert display_width('カタカナ', 1) == 8 + assert display_width('カタカナ', 1) == 4 + assert display_width('한글', 1) == 4 + assert display_width('한자', 1) == 4 + assert display_width('漢字', 1) == 4 + assert display_width('简体字', 1) == 6 + assert display_width('繁體字', 1) == 6 + assert display_width('अरबी लिपि', 1) == 9 + assert display_width('☆', 1) == 1 + assert display_width('☆', 2) == 2 + assert display_width('🐈👽📛', 1) == 6 +} diff --git a/v_windows/v/old/vlib/encoding/utf8/encoding_utf8_test.v b/v_windows/v/old/vlib/encoding/utf8/encoding_utf8_test.v new file mode 100644 index 0000000..ebea87c --- /dev/null +++ b/v_windows/v/old/vlib/encoding/utf8/encoding_utf8_test.v @@ -0,0 +1,9 @@ +import encoding.utf8 + +fn test_validate_str() { + assert utf8.validate_str('añçá') == true + assert utf8.validate_str('\x61\xC3\xB1\xC3\xA7\xC3\xA1') == true + assert utf8.validate_str('\xC0\xC1') == false + assert utf8.validate_str('\xF5\xFF') == false + assert utf8.validate_str('\xE0\xEF') == false +} diff --git a/v_windows/v/old/vlib/encoding/utf8/utf8.v b/v_windows/v/old/vlib/encoding/utf8/utf8.v new file mode 100644 index 0000000..88c598f --- /dev/null +++ b/v_windows/v/old/vlib/encoding/utf8/utf8.v @@ -0,0 +1,88 @@ +module utf8 + +struct Utf8State { +mut: + index int + subindex int + failed bool +} + +pub fn validate_str(str string) bool { + return validate(str.str, str.len) +} + +pub fn validate(data &byte, len int) bool { + mut state := Utf8State{} + for i := 0; i < len; i++ { + s := unsafe { data[i] } + if s == 0 { + break + } + state.next_state(s) + if state.failed { + return false + } + } + return !state.failed && state.subindex <= 0 +} + +fn (mut s Utf8State) seq(r0 bool, r1 bool, is_tail bool) bool { + if s.subindex == 0 || (s.index > 1 && s.subindex == 1) || (s.index >= 6 && s.subindex == 2) { + if (s.subindex == 0 && r0) || (s.subindex == 1 && r1) || (s.subindex == 2 && is_tail) { + s.subindex++ + return true + } + } else { + s.failed = true + if is_tail { + s.index = 0 + s.subindex = 0 + s.failed = false + } + return true + } + s.index++ + s.subindex = 0 + return false +} + +fn (mut s Utf8State) next_state(c byte) { + // sequence 1 + if s.index == 0 { + if (c >= 0x00 + 1 && c <= 0x7F) || c == 0x00 { + return + } + s.index++ + s.subindex = 0 + } + is_tail := c >= 0x80 && c <= 0xBF + // sequence 2 + if s.index == 1 && s.seq(c >= 0xC2 && c <= 0xDF, false, is_tail) { + return + } + // sequence 3 + if s.index == 2 && s.seq(c == 0xE0, c >= 0xA0 && c <= 0xBF, is_tail) { + return + } + if s.index == 3 && s.seq(c >= 0xE1 && c <= 0xEC, c >= 0x80 && c <= 0xBF, is_tail) { + return + } + if s.index == 4 && s.seq(c == 0xED, c >= 0x80 && c <= 0x9F, is_tail) { + return + } + if s.index == 5 && s.seq(c >= 0xEE && c <= 0xEF, c >= 0x80 && c <= 0xBF, is_tail) { + return + } + // sequence 4 + if s.index == 6 && s.seq(c == 0xF0, c >= 0x90 && c <= 0xBF, is_tail) { + return + } + if s.index == 7 && s.seq(c >= 0xF1 && c <= 0xF3, c >= 0x80 && c <= 0xBF, is_tail) { + return + } + if s.index == 8 && s.seq(c == 0xF4, c >= 0x80 && c <= 0x8F, is_tail) { + return + } + // we should never reach here + s.failed = true +} diff --git a/v_windows/v/old/vlib/encoding/utf8/utf8_util.v b/v_windows/v/old/vlib/encoding/utf8/utf8_util.v new file mode 100644 index 0000000..2e3da0d --- /dev/null +++ b/v_windows/v/old/vlib/encoding/utf8/utf8_util.v @@ -0,0 +1,1161 @@ +/* +utf-8 util + +Copyright (c) 2019-2021 Dario Deledda. All rights reserved. +Use of this source code is governed by an MIT license +that can be found in the LICENSE file. + +This file contains utilities for utf8 strings +*/ +module utf8 + +/* +Utility functions +*/ + +// len return the length as number of unicode chars from a string +pub fn len(s string) int { + if s.len == 0 { + return 0 + } + + mut count := 0 + mut index := 0 + + for { + ch_len := utf8_char_len(s[index]) + index += ch_len + count++ + if index >= s.len { + break + } + } + return count +} + +// get_uchar convert a unicode glyph in string[index] into a int unicode char +pub fn get_uchar(s string, index int) int { + mut res := 0 + mut ch_len := 0 + if s.len > 0 { + ch_len = utf8_char_len(s[index]) + + if ch_len == 1 { + return u16(s[index]) + } + if ch_len > 1 && ch_len < 5 { + mut lword := 0 + for i := 0; i < ch_len; i++ { + lword = (lword << 8) | int(s[index + i]) + } + + // 2 byte utf-8 + // byte format: 110xxxxx 10xxxxxx + // + if ch_len == 2 { + res = (lword & 0x1f00) >> 2 | (lword & 0x3f) + } + // 3 byte utf-8 + // byte format: 1110xxxx 10xxxxxx 10xxxxxx + // + else if ch_len == 3 { + res = (lword & 0x0f0000) >> 4 | (lword & 0x3f00) >> 2 | (lword & 0x3f) + } + // 4 byte utf-8 + // byte format: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + // + else if ch_len == 4 { + res = ((lword & 0x07000000) >> 6) | ((lword & 0x003f0000) >> 4) | ((lword & 0x00003F00) >> 2) | (lword & 0x0000003f) + } + } + } + return res +} + +// raw_index - get the raw chracter from the string by the given index value. +// example: '我是V Lang'.raw_index(1) => '是' + +// raw_index - get the raw chracter from the string by the given index value. +// example: utf8.raw_index('我是V Lang', 1) => '是' +pub fn raw_index(s string, index int) string { + mut r := []rune{} + + for i := 0; i < s.len; i++ { + if r.len - 1 == index { + break + } + + b := s[i] + ch_len := ((0xe5000000 >> ((b >> 3) & 0x1e)) & 3) + + r << if ch_len > 0 { + i += ch_len + rune(get_uchar(s, i - ch_len)) + } else { + rune(b) + } + } + + return r[index].str() +} + +// reverse - returns a reversed string. +// example: utf8.reverse('你好世界hello world') => 'dlrow olleh界世好你'. +pub fn reverse(s string) string { + len_s := len(s) + if len_s == 0 || len_s == 1 { + return s.clone() + } + mut str_array := []string{} + for i in 0 .. len_s { + str_array << raw_index(s, i) + } + str_array = str_array.reverse() + return str_array.join('') +} + +/* +Conversion functions +*/ + +// to_upper return an uppercase string from a string +pub fn to_upper(s string) string { + return up_low(s, true) +} + +// to_lower return an lowercase string from a string +pub fn to_lower(s string) string { + return up_low(s, false) +} + +/* +Punctuation functions + +The "western" function search on a small table, that is quicker than +the global unicode table search. **Use only for western chars**. +*/ + +// +// Western +// + +// is_punct return true if the string[index] byte is the start of a unicode western punctuation +pub fn is_punct(s string, index int) bool { + return is_uchar_punct(get_uchar(s, index)) +} + +// is_uchar_punct return true if the input unicode is a western unicode punctuation +pub fn is_uchar_punct(uchar int) bool { + return find_punct_in_table(uchar, utf8.unicode_punct_western) != 0 +} + +// +// Global +// + +// is_global_punct return true if the string[index] byte of is the start of a global unicode punctuation +pub fn is_global_punct(s string, index int) bool { + return is_uchar_global_punct(get_uchar(s, index)) +} + +// is_uchar_global_punct return true if the input unicode is a global unicode punctuation +pub fn is_uchar_global_punct(uchar int) bool { + return find_punct_in_table(uchar, utf8.unicode_punct) != 0 +} + +/* +Private functions +*/ + +// Raw to_lower utf-8 function +fn utf8_to_lower(in_cp int) int { + mut cp := in_cp + if ((0x0041 <= cp) && (0x005a >= cp)) || ((0x00c0 <= cp) && (0x00d6 >= cp)) + || ((0x00d8 <= cp) && (0x00de >= cp)) || ((0x0391 <= cp) && (0x03a1 >= cp)) + || ((0x03a3 <= cp) && (0x03ab >= cp)) || ((0x0410 <= cp) && (0x042f >= cp)) { + cp += 32 + } else if (0x0400 <= cp) && (0x040f >= cp) { + cp += 80 + } else if ((0x0100 <= cp) && (0x012f >= cp)) || ((0x0132 <= cp) && (0x0137 >= cp)) + || ((0x014a <= cp) && (0x0177 >= cp)) || ((0x0182 <= cp) && (0x0185 >= cp)) + || ((0x01a0 <= cp) && (0x01a5 >= cp)) || ((0x01de <= cp) && (0x01ef >= cp)) + || ((0x01f8 <= cp) && (0x021f >= cp)) || ((0x0222 <= cp) && (0x0233 >= cp)) + || ((0x0246 <= cp) && (0x024f >= cp)) || ((0x03d8 <= cp) && (0x03ef >= cp)) + || ((0x0460 <= cp) && (0x0481 >= cp)) || ((0x048a <= cp) && (0x04ff >= cp)) { + cp |= 0x1 + } else if ((0x0139 <= cp) && (0x0148 >= cp)) || ((0x0179 <= cp) && (0x017e >= cp)) + || ((0x01af <= cp) && (0x01b0 >= cp)) || ((0x01b3 <= cp) && (0x01b6 >= cp)) + || ((0x01cd <= cp) && (0x01dc >= cp)) { + cp += 1 + cp &= ~0x1 + } else if ((0x0531 <= cp) && (0x0556 >= cp)) || ((0x10A0 <= cp) && (0x10C5 >= cp)) { + // ARMENIAN or GEORGIAN + cp += 0x30 + } else if (((0x1E00 <= cp) && (0x1E94 >= cp)) || ((0x1EA0 <= cp) && (0x1EF8 >= cp))) + && (cp & 1 == 0) { + // LATIN CAPITAL LETTER + cp += 1 + } else if (0x24B6 <= cp) && (0x24CF >= cp) { + // CIRCLED LATIN + cp += 0x1a + } else if (0xFF21 <= cp) && (0xFF3A >= cp) { + // FULLWIDTH LATIN CAPITAL + cp += 0x19 + } else if ((0x1F08 <= cp) && (0x1F0F >= cp)) || ((0x1F18 <= cp) && (0x1F1D >= cp)) + || ((0x1F28 <= cp) && (0x1F2F >= cp)) || ((0x1F38 <= cp) && (0x1F3F >= cp)) + || ((0x1F48 <= cp) && (0x1F4D >= cp)) || ((0x1F68 <= cp) && (0x1F6F >= cp)) + || ((0x1F88 <= cp) && (0x1F8F >= cp)) || ((0x1F98 <= cp) && (0x1F9F >= cp)) + || ((0x1FA8 <= cp) && (0x1FAF >= cp)) { + // GREEK + cp -= 8 + } else { + match cp { + 0x0178 { cp = 0x00ff } + 0x0243 { cp = 0x0180 } + 0x018e { cp = 0x01dd } + 0x023d { cp = 0x019a } + 0x0220 { cp = 0x019e } + 0x01b7 { cp = 0x0292 } + 0x01c4 { cp = 0x01c6 } + 0x01c7 { cp = 0x01c9 } + 0x01ca { cp = 0x01cc } + 0x01f1 { cp = 0x01f3 } + 0x01f7 { cp = 0x01bf } + 0x0187 { cp = 0x0188 } + 0x018b { cp = 0x018c } + 0x0191 { cp = 0x0192 } + 0x0198 { cp = 0x0199 } + 0x01a7 { cp = 0x01a8 } + 0x01ac { cp = 0x01ad } + 0x01af { cp = 0x01b0 } + 0x01b8 { cp = 0x01b9 } + 0x01bc { cp = 0x01bd } + 0x01f4 { cp = 0x01f5 } + 0x023b { cp = 0x023c } + 0x0241 { cp = 0x0242 } + 0x03fd { cp = 0x037b } + 0x03fe { cp = 0x037c } + 0x03ff { cp = 0x037d } + 0x037f { cp = 0x03f3 } + 0x0386 { cp = 0x03ac } + 0x0388 { cp = 0x03ad } + 0x0389 { cp = 0x03ae } + 0x038a { cp = 0x03af } + 0x038c { cp = 0x03cc } + 0x038e { cp = 0x03cd } + 0x038f { cp = 0x03ce } + 0x0370 { cp = 0x0371 } + 0x0372 { cp = 0x0373 } + 0x0376 { cp = 0x0377 } + 0x03f4 { cp = 0x03b8 } + 0x03cf { cp = 0x03d7 } + 0x03f9 { cp = 0x03f2 } + 0x03f7 { cp = 0x03f8 } + 0x03fa { cp = 0x03fb } + // GREEK + 0x1F59 { cp = 0x1F51 } + 0x1F5B { cp = 0x1F53 } + 0x1F5D { cp = 0x1F55 } + 0x1F5F { cp = 0x1F57 } + 0x1FB8 { cp = 0x1FB0 } + 0x1FB9 { cp = 0x1FB1 } + 0x1FD8 { cp = 0x1FD0 } + 0x1FD9 { cp = 0x1FD1 } + 0x1FE8 { cp = 0x1FE0 } + 0x1FE9 { cp = 0x1FE1 } + else {} + } + } + + return cp +} + +// Raw to_upper utf-8 function +fn utf8_to_upper(in_cp int) int { + mut cp := in_cp + if ((0x0061 <= cp) && (0x007a >= cp)) || ((0x00e0 <= cp) && (0x00f6 >= cp)) + || ((0x00f8 <= cp) && (0x00fe >= cp)) || ((0x03b1 <= cp) && (0x03c1 >= cp)) + || ((0x03c3 <= cp) && (0x03cb >= cp)) || ((0x0430 <= cp) && (0x044f >= cp)) { + cp -= 32 + } else if (0x0450 <= cp) && (0x045f >= cp) { + cp -= 80 + } else if ((0x0100 <= cp) && (0x012f >= cp)) || ((0x0132 <= cp) && (0x0137 >= cp)) + || ((0x014a <= cp) && (0x0177 >= cp)) || ((0x0182 <= cp) && (0x0185 >= cp)) + || ((0x01a0 <= cp) && (0x01a5 >= cp)) || ((0x01de <= cp) && (0x01ef >= cp)) + || ((0x01f8 <= cp) && (0x021f >= cp)) || ((0x0222 <= cp) && (0x0233 >= cp)) + || ((0x0246 <= cp) && (0x024f >= cp)) || ((0x03d8 <= cp) && (0x03ef >= cp)) + || ((0x0460 <= cp) && (0x0481 >= cp)) || ((0x048a <= cp) && (0x04ff >= cp)) { + cp &= ~0x1 + } else if ((0x0139 <= cp) && (0x0148 >= cp)) || ((0x0179 <= cp) && (0x017e >= cp)) + || ((0x01af <= cp) && (0x01b0 >= cp)) || ((0x01b3 <= cp) && (0x01b6 >= cp)) + || ((0x01cd <= cp) && (0x01dc >= cp)) { + cp -= 1 + cp |= 0x1 + } else if ((0x0561 <= cp) && (0x0586 >= cp)) || ((0x10D0 <= cp) && (0x10F5 >= cp)) { + // ARMENIAN or GEORGIAN + cp -= 0x30 + } else if (((0x1E01 <= cp) && (0x1E95 >= cp)) || ((0x1EA1 <= cp) && (0x1EF9 >= cp))) + && (cp & 1 == 1) { + // LATIN CAPITAL LETTER + cp -= 1 + } else if (0x24D0 <= cp) && (0x24E9 >= cp) { + // CIRCLED LATIN + cp -= 0x1a + } else if (0xFF41 <= cp) && (0xFF5A >= cp) { + // FULLWIDTH LATIN CAPITAL + cp -= 0x19 + } else if ((0x1F00 <= cp) && (0x1F07 >= cp)) || ((0x1F10 <= cp) && (0x1F15 >= cp)) + || ((0x1F20 <= cp) && (0x1F27 >= cp)) || ((0x1F30 <= cp) && (0x1F37 >= cp)) + || ((0x1F40 <= cp) && (0x1F45 >= cp)) || ((0x1F60 <= cp) && (0x1F67 >= cp)) + || ((0x1F80 <= cp) && (0x1F87 >= cp)) || ((0x1F90 <= cp) && (0x1F97 >= cp)) + || ((0x1FA0 <= cp) && (0x1FA7 >= cp)) { + // GREEK + cp += 8 + } else { + match cp { + 0x00ff { cp = 0x0178 } + 0x0180 { cp = 0x0243 } + 0x01dd { cp = 0x018e } + 0x019a { cp = 0x023d } + 0x019e { cp = 0x0220 } + 0x0292 { cp = 0x01b7 } + 0x01c6 { cp = 0x01c4 } + 0x01c9 { cp = 0x01c7 } + 0x01cc { cp = 0x01ca } + 0x01f3 { cp = 0x01f1 } + 0x01bf { cp = 0x01f7 } + 0x0188 { cp = 0x0187 } + 0x018c { cp = 0x018b } + 0x0192 { cp = 0x0191 } + 0x0199 { cp = 0x0198 } + 0x01a8 { cp = 0x01a7 } + 0x01ad { cp = 0x01ac } + 0x01b0 { cp = 0x01af } + 0x01b9 { cp = 0x01b8 } + 0x01bd { cp = 0x01bc } + 0x01f5 { cp = 0x01f4 } + 0x023c { cp = 0x023b } + 0x0242 { cp = 0x0241 } + 0x037b { cp = 0x03fd } + 0x037c { cp = 0x03fe } + 0x037d { cp = 0x03ff } + 0x03f3 { cp = 0x037f } + 0x03ac { cp = 0x0386 } + 0x03ad { cp = 0x0388 } + 0x03ae { cp = 0x0389 } + 0x03af { cp = 0x038a } + 0x03cc { cp = 0x038c } + 0x03cd { cp = 0x038e } + 0x03ce { cp = 0x038f } + 0x0371 { cp = 0x0370 } + 0x0373 { cp = 0x0372 } + 0x0377 { cp = 0x0376 } + 0x03d1 { cp = 0x0398 } + 0x03d7 { cp = 0x03cf } + 0x03f2 { cp = 0x03f9 } + 0x03f8 { cp = 0x03f7 } + 0x03fb { cp = 0x03fa } + // GREEK + 0x1F51 { cp = 0x1F59 } + 0x1F53 { cp = 0x1F5B } + 0x1F55 { cp = 0x1F5D } + 0x1F57 { cp = 0x1F5F } + 0x1FB0 { cp = 0x1FB8 } + 0x1FB1 { cp = 0x1FB9 } + 0x1FD0 { cp = 0x1FD8 } + 0x1FD1 { cp = 0x1FD9 } + 0x1FE0 { cp = 0x1FE8 } + 0x1FE1 { cp = 0x1FE9 } + else {} + } + } + + return cp +} + +// +// if upper_flag == true then make low ==> upper conversion +// if upper_flag == false then make upper ==> low conversion +// +// up_low make the dirt job +fn up_low(s string, upper_flag bool) string { + mut index := 0 + mut tab_char := 0 + mut str_res := unsafe { malloc_noscan(s.len + 1) } + + for { + ch_len := utf8_char_len(s[index]) + + if ch_len == 1 { + if upper_flag == true { + unsafe { + str_res[index] = byte(C.toupper(s.str[index])) + } + } else { + unsafe { + str_res[index] = byte(C.tolower(s.str[index])) + } + } + } else if ch_len > 1 && ch_len < 5 { + mut lword := 0 + + for i := 0; i < ch_len; i++ { + lword = (lword << 8) | int(s[index + i]) + } + + // println("#${index} ($lword)") + + mut res := 0 + + // 2 byte utf-8 + // byte format: 110xxxxx 10xxxxxx + // + if ch_len == 2 { + res = (lword & 0x1f00) >> 2 | (lword & 0x3f) + } + // 3 byte utf-8 + // byte format: 1110xxxx 10xxxxxx 10xxxxxx + // + else if ch_len == 3 { + res = (lword & 0x0f0000) >> 4 | (lword & 0x3f00) >> 2 | (lword & 0x3f) + } + // 4 byte utf-8 + // byte format: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + // + else if ch_len == 4 { + res = ((lword & 0x07000000) >> 6) | ((lword & 0x003f0000) >> 4) | ((lword & 0x00003F00) >> 2) | (lword & 0x0000003f) + } + + // println("res: ${res.hex():8}") + + if upper_flag == false { + tab_char = utf8_to_lower(res) + } else { + tab_char = utf8_to_upper(res) + } + + if ch_len == 2 { + ch0 := byte((tab_char >> 6) & 0x1f) | 0xc0 // 110x xxxx + ch1 := byte((tab_char >> 0) & 0x3f) | 0x80 // 10xx xxxx + // C.printf("[%02x%02x] \n",ch0,ch1) + + unsafe { + str_res[index + 0] = ch0 + str_res[index + 1] = ch1 + } + //**************************************************************** + // BUG: doesn't compile, workaround use shitf to right of 0 bit + //**************************************************************** + // str_res[index + 1 ] = byte( tab_char & 0xbf ) // 1011 1111 + } else if ch_len == 3 { + ch0 := byte((tab_char >> 12) & 0x0f) | 0xe0 // 1110 xxxx + ch1 := byte((tab_char >> 6) & 0x3f) | 0x80 // 10xx xxxx + ch2 := byte((tab_char >> 0) & 0x3f) | 0x80 // 10xx xxxx + // C.printf("[%02x%02x%02x] \n",ch0,ch1,ch2) + + unsafe { + str_res[index + 0] = ch0 + str_res[index + 1] = ch1 + str_res[index + 2] = ch2 + } + } + // TODO: write if needed + else if ch_len == 4 { + // place holder!! + // at the present time simply copy the utf8 char + for i in 0 .. ch_len { + unsafe { + str_res[index + i] = s[index + i] + } + } + } + } else { + // other cases, just copy the string + for i in 0 .. ch_len { + unsafe { + str_res[index + i] = s[index + i] + } + } + } + + index += ch_len + + // we are done, exit the loop + if index >= s.len { + break + } + } + + // for c compatibility set the ending 0 + unsafe { + str_res[index] = 0 + // C.printf("str_res: %s\n--------------\n",str_res) + return tos(str_res, s.len) + } +} + +// find punct in lockup table +fn find_punct_in_table(in_code int, in_table []int) int { + // + // We will use a simple binary search + // + + mut first_index := 0 + mut last_index := (in_table.len) + mut index := 0 + mut x := 0 + + for { + index = (first_index + last_index) >> 1 + x = in_table[index] + // C.printf("(%d..%d) index:%d base[%08x]==>[%08x]\n",first_index,last_index,index,in_code,x) + + if x == in_code { + return index + } else if x > in_code { + last_index = index + } else { + first_index = index + } + + if (last_index - first_index) <= 1 { + break + } + } + // C.printf("not found.\n") + return 0 +} + +/* +Unicode punctuation chars + +source: http://www.unicode.org/faq/punctuation_symbols.html +*/ +const ( + // Western punctuation mark + // Character Name Browser Image + unicode_punct_western = [ + 0x0021 /* EXCLAMATION MARK ! */, + 0x0022 /* QUOTATION MARK " */, + 0x0027 /* APOSTROPHE ' */, + 0x002A /* ASTERISK * */, + 0x002C /* COMMA , */, + 0x002E /* FULL STOP . */, + 0x002F /* SOLIDUS / */, + 0x003A /* COLON : */, + 0x003B /* SEMICOLON ; */, + 0x003F /* QUESTION MARK ? */, + 0x00A1 /* INVERTED EXCLAMATION MARK ¡ */, + 0x00A7 /* SECTION SIGN § */, + 0x00B6 /* PILCROW SIGN ¶ */, + 0x00B7 /* MIDDLE DOT · */, + 0x00BF /* INVERTED QUESTION MARK ¿ */, + 0x037E /* GREEK QUESTION MARK ; */, + 0x0387 /* GREEK ANO TELEIA · */, + 0x055A /* ARMENIAN APOSTROPHE ՚ */, + 0x055B /* ARMENIAN EMPHASIS MARK ՛ */, + 0x055C /* ARMENIAN EXCLAMATION MARK ՜ */, + 0x055D /* ARMENIAN COMMA ՝ */, + 0x055E /* ARMENIAN QUESTION MARK ՞ */, + 0x055F /* ARMENIAN ABBREVIATION MARK ՟ */, + 0x0589 /* ARMENIAN FULL STOP ։ */, + 0x05C0 /* HEBREW PUNCTUATION PASEQ ׀ */, + 0x05C3 /* HEBREW PUNCTUATION SOF PASUQ ׃ */, + 0x05C6 /* HEBREW PUNCTUATION NUN HAFUKHA ׆ */, + 0x05F3 /* HEBREW PUNCTUATION GERESH ׳ */, + 0x05F4 /* HEBREW PUNCTUATION GERSHAYIM ״ */, + ] + + // Unicode Characters in the 'Punctuation, Other' Category + // Character Name Browser Image + unicode_punct = [ + 0x0021 /* EXCLAMATION MARK ! */, + 0x0022 /* QUOTATION MARK " */, + 0x0023 /* NUMBER SIGN # */, + 0x0025 /* PERCENT SIGN % */, + 0x0026 /* AMPERSAND & */, + 0x0027 /* APOSTROPHE ' */, + 0x002A /* ASTERISK * */, + 0x002C /* COMMA , */, + 0x002E /* FULL STOP . */, + 0x002F /* SOLIDUS / */, + 0x003A /* COLON : */, + 0x003B /* SEMICOLON ; */, + 0x003F /* QUESTION MARK ? */, + 0x0040 /* COMMERCIAL AT @ */, + 0x005C /* REVERSE SOLIDUS \ */, + 0x00A1 /* INVERTED EXCLAMATION MARK ¡ */, + 0x00A7 /* SECTION SIGN § */, + 0x00B6 /* PILCROW SIGN ¶ */, + 0x00B7 /* MIDDLE DOT · */, + 0x00BF /* INVERTED QUESTION MARK ¿ */, + 0x037E /* GREEK QUESTION MARK ; */, + 0x0387 /* GREEK ANO TELEIA · */, + 0x055A /* ARMENIAN APOSTROPHE ՚ */, + 0x055B /* ARMENIAN EMPHASIS MARK ՛ */, + 0x055C /* ARMENIAN EXCLAMATION MARK ՜ */, + 0x055D /* ARMENIAN COMMA ՝ */, + 0x055E /* ARMENIAN QUESTION MARK ՞ */, + 0x055F /* ARMENIAN ABBREVIATION MARK ՟ */, + 0x0589 /* ARMENIAN FULL STOP ։ */, + 0x05C0 /* HEBREW PUNCTUATION PASEQ ׀ */, + 0x05C3 /* HEBREW PUNCTUATION SOF PASUQ ׃ */, + 0x05C6 /* HEBREW PUNCTUATION NUN HAFUKHA ׆ */, + 0x05F3 /* HEBREW PUNCTUATION GERESH ׳ */, + 0x05F4 /* HEBREW PUNCTUATION GERSHAYIM ״ */, + 0x0609 /* ARABIC-INDIC PER MILLE SIGN ؉ */, + 0x060A /* ARABIC-INDIC PER TEN THOUSAND SIGN ؊ */, + 0x060C /* ARABIC COMMA ، */, + 0x060D /* ARABIC DATE SEPARATOR ؍ */, + 0x061B /* ARABIC SEMICOLON ؛ */, + 0x061E /* ARABIC TRIPLE DOT PUNCTUATION MARK ؞ */, + 0x061F /* ARABIC QUESTION MARK ؟ */, + 0x066A /* ARABIC PERCENT SIGN ٪ */, + 0x066B /* ARABIC DECIMAL SEPARATOR ٫ */, + 0x066C /* ARABIC THOUSANDS SEPARATOR ٬ */, + 0x066D /* ARABIC FIVE POINTED STAR ٭ */, + 0x06D4 /* ARABIC FULL STOP ۔ */, + 0x0700 /* SYRIAC END OF PARAGRAPH ܀ */, + 0x0701 /* SYRIAC SUPRALINEAR FULL STOP ܁ */, + 0x0702 /* SYRIAC SUBLINEAR FULL STOP ܂ */, + 0x0703 /* SYRIAC SUPRALINEAR COLON ܃ */, + 0x0704 /* SYRIAC SUBLINEAR COLON ܄ */, + 0x0705 /* SYRIAC HORIZONTAL COLON ܅ */, + 0x0706 /* SYRIAC COLON SKEWED LEFT ܆ */, + 0x0707 /* SYRIAC COLON SKEWED RIGHT ܇ */, + 0x0708 /* SYRIAC SUPRALINEAR COLON SKEWED LEFT ܈ */, + 0x0709 /* SYRIAC SUBLINEAR COLON SKEWED RIGHT ܉ */, + 0x070A /* SYRIAC CONTRACTION ܊ */, + 0x070B /* SYRIAC HARKLEAN OBELUS ܋ */, + 0x070C /* SYRIAC HARKLEAN METOBELUS ܌ */, + 0x070D /* SYRIAC HARKLEAN ASTERISCUS ܍ */, + 0x07F7 /* NKO SYMBOL GBAKURUNEN ߷ */, + 0x07F8 /* NKO COMMA ߸ */, + 0x07F9 /* NKO EXCLAMATION MARK ߹ */, + 0x0830 /* SAMARITAN PUNCTUATION NEQUDAA ࠰ */, + 0x0831 /* SAMARITAN PUNCTUATION AFSAAQ ࠱ */, + 0x0832 /* SAMARITAN PUNCTUATION ANGED ࠲ */, + 0x0833 /* SAMARITAN PUNCTUATION BAU ࠳ */, + 0x0834 /* SAMARITAN PUNCTUATION ATMAAU ࠴ */, + 0x0835 /* SAMARITAN PUNCTUATION SHIYYAALAA ࠵ */, + 0x0836 /* SAMARITAN ABBREVIATION MARK ࠶ */, + 0x0837 /* SAMARITAN PUNCTUATION MELODIC QITSA ࠷ */, + 0x0838 /* SAMARITAN PUNCTUATION ZIQAA ࠸ */, + 0x0839 /* SAMARITAN PUNCTUATION QITSA ࠹ */, + 0x083A /* SAMARITAN PUNCTUATION ZAEF ࠺ */, + 0x083B /* SAMARITAN PUNCTUATION TURU ࠻ */, + 0x083C /* SAMARITAN PUNCTUATION ARKAANU ࠼ */, + 0x083D /* SAMARITAN PUNCTUATION SOF MASHFAAT ࠽ */, + 0x083E /* SAMARITAN PUNCTUATION ANNAAU ࠾ */, + 0x085E /* MANDAIC PUNCTUATION ࡞ */, + 0x0964 /* DEVANAGARI DANDA । */, + 0x0965 /* DEVANAGARI DOUBLE DANDA ॥ */, + 0x0970 /* DEVANAGARI ABBREVIATION SIGN ॰ */, + 0x09FD /* BENGALI ABBREVIATION SIGN ৽ */, + 0x0A76 /* GURMUKHI ABBREVIATION SIGN ੶ */, + 0x0AF0 /* GUJARATI ABBREVIATION SIGN ૰ */, + 0x0C77 /* TELUGU SIGN SIDDHAM ౷ */, + 0x0C84 /* KANNADA SIGN SIDDHAM ಄ */, + 0x0DF4 /* SINHALA PUNCTUATION KUNDDALIYA ෴ */, + 0x0E4F /* THAI CHARACTER FONGMAN ๏ */, + 0x0E5A /* THAI CHARACTER ANGKHANKHU ๚ */, + 0x0E5B /* THAI CHARACTER KHOMUT ๛ */, + 0x0F04 /* TIBETAN MARK INITIAL YIG MGO MDUN MA ༄ */, + 0x0F05 /* TIBETAN MARK CLOSING YIG MGO SGAB MA ༅ */, + 0x0F06 /* TIBETAN MARK CARET YIG MGO PHUR SHAD MA ༆ */, + 0x0F07 /* TIBETAN MARK YIG MGO TSHEG SHAD MA ༇ */, + 0x0F08 /* TIBETAN MARK SBRUL SHAD ༈ */, + 0x0F09 /* TIBETAN MARK BSKUR YIG MGO ༉ */, + 0x0F0A /* TIBETAN MARK BKA- SHOG YIG MGO ༊ */, + 0x0F0B /* TIBETAN MARK INTERSYLLABIC TSHEG ་ */, + 0x0F0C /* TIBETAN MARK DELIMITER TSHEG BSTAR ༌ */, + 0x0F0D /* TIBETAN MARK SHAD ། */, + 0x0F0E /* TIBETAN MARK NYIS SHAD ༎ */, + 0x0F0F /* TIBETAN MARK TSHEG SHAD ༏ */, + 0x0F10 /* TIBETAN MARK NYIS TSHEG SHAD ༐ */, + 0x0F11 /* TIBETAN MARK RIN CHEN SPUNGS SHAD ༑ */, + 0x0F12 /* TIBETAN MARK RGYA GRAM SHAD ༒ */, + 0x0F14 /* TIBETAN MARK GTER TSHEG ༔ */, + 0x0F85 /* TIBETAN MARK PALUTA ྅ */, + 0x0FD0 /* TIBETAN MARK BSKA- SHOG GI MGO RGYAN ࿐ */, + 0x0FD1 /* TIBETAN MARK MNYAM YIG GI MGO RGYAN ࿑ */, + 0x0FD2 /* TIBETAN MARK NYIS TSHEG ࿒ */, + 0x0FD3 /* TIBETAN MARK INITIAL BRDA RNYING YIG MGO MDUN MA ࿓ */, + 0x0FD4 /* TIBETAN MARK CLOSING BRDA RNYING YIG MGO SGAB MA ࿔ */, + 0x0FD9 /* TIBETAN MARK LEADING MCHAN RTAGS ࿙ */, + 0x0FDA /* TIBETAN MARK TRAILING MCHAN RTAGS ࿚ */, + 0x104A /* MYANMAR SIGN LITTLE SECTION ၊ */, + 0x104B /* MYANMAR SIGN SECTION ။ */, + 0x104C /* MYANMAR SYMBOL LOCATIVE ၌ */, + 0x104D /* MYANMAR SYMBOL COMPLETED ၍ */, + 0x104E /* MYANMAR SYMBOL AFOREMENTIONED ၎ */, + 0x104F /* MYANMAR SYMBOL GENITIVE ၏ */, + 0x10FB /* GEORGIAN PARAGRAPH SEPARATOR ჻ */, + 0x1360 /* ETHIOPIC SECTION MARK ፠ */, + 0x1361 /* ETHIOPIC WORDSPACE ፡ */, + 0x1362 /* ETHIOPIC FULL STOP ። */, + 0x1363 /* ETHIOPIC COMMA ፣ */, + 0x1364 /* ETHIOPIC SEMICOLON ፤ */, + 0x1365 /* ETHIOPIC COLON ፥ */, + 0x1366 /* ETHIOPIC PREFACE COLON ፦ */, + 0x1367 /* ETHIOPIC QUESTION MARK ፧ */, + 0x1368 /* ETHIOPIC PARAGRAPH SEPARATOR ፨ */, + 0x166E /* CANADIAN SYLLABICS FULL STOP ᙮ */, + 0x16EB /* RUNIC SINGLE PUNCTUATION ᛫ */, + 0x16EC /* RUNIC MULTIPLE PUNCTUATION ᛬ */, + 0x16ED /* RUNIC CROSS PUNCTUATION ᛭ */, + 0x1735 /* PHILIPPINE SINGLE PUNCTUATION ᜵ */, + 0x1736 /* PHILIPPINE DOUBLE PUNCTUATION ᜶ */, + 0x17D4 /* KHMER SIGN KHAN ។ */, + 0x17D5 /* KHMER SIGN BARIYOOSAN ៕ */, + 0x17D6 /* KHMER SIGN CAMNUC PII KUUH ៖ */, + 0x17D8 /* KHMER SIGN BEYYAL ៘ */, + 0x17D9 /* KHMER SIGN PHNAEK MUAN ៙ */, + 0x17DA /* KHMER SIGN KOOMUUT ៚ */, + 0x1800 /* MONGOLIAN BIRGA ᠀ */, + 0x1801 /* MONGOLIAN ELLIPSIS ᠁ */, + 0x1802 /* MONGOLIAN COMMA ᠂ */, + 0x1803 /* MONGOLIAN FULL STOP ᠃ */, + 0x1804 /* MONGOLIAN COLON ᠄ */, + 0x1805 /* MONGOLIAN FOUR DOTS ᠅ */, + 0x1807 /* MONGOLIAN SIBE SYLLABLE BOUNDARY MARKER ᠇ */, + 0x1808 /* MONGOLIAN MANCHU COMMA ᠈ */, + 0x1809 /* MONGOLIAN MANCHU FULL STOP ᠉ */, + 0x180A /* MONGOLIAN NIRUGU ᠊ */, + 0x1944 /* LIMBU EXCLAMATION MARK ᥄ */, + 0x1945 /* LIMBU QUESTION MARK ᥅ */, + 0x1A1E /* BUGINESE PALLAWA ᨞ */, + 0x1A1F /* BUGINESE END OF SECTION ᨟ */, + 0x1AA0 /* TAI THAM SIGN WIANG ᪠ */, + 0x1AA1 /* TAI THAM SIGN WIANGWAAK ᪡ */, + 0x1AA2 /* TAI THAM SIGN SAWAN ᪢ */, + 0x1AA3 /* TAI THAM SIGN KEOW ᪣ */, + 0x1AA4 /* TAI THAM SIGN HOY ᪤ */, + 0x1AA5 /* TAI THAM SIGN DOKMAI ᪥ */, + 0x1AA6 /* TAI THAM SIGN REVERSED ROTATED RANA ᪦ */, + 0x1AA8 /* TAI THAM SIGN KAAN ᪨ */, + 0x1AA9 /* TAI THAM SIGN KAANKUU ᪩ */, + 0x1AAA /* TAI THAM SIGN SATKAAN ᪪ */, + 0x1AAB /* TAI THAM SIGN SATKAANKUU ᪫ */, + 0x1AAC /* TAI THAM SIGN HANG ᪬ */, + 0x1AAD /* TAI THAM SIGN CAANG ᪭ */, + 0x1B5A /* BALINESE PANTI ᭚ */, + 0x1B5B /* BALINESE PAMADA ᭛ */, + 0x1B5C /* BALINESE WINDU ᭜ */, + 0x1B5D /* BALINESE CARIK PAMUNGKAH ᭝ */, + 0x1B5E /* BALINESE CARIK SIKI ᭞ */, + 0x1B5F /* BALINESE CARIK PAREREN ᭟ */, + 0x1B60 /* BALINESE PAMENENG ᭠ */, + 0x1BFC /* BATAK SYMBOL BINDU NA METEK ᯼ */, + 0x1BFD /* BATAK SYMBOL BINDU PINARBORAS ᯽ */, + 0x1BFE /* BATAK SYMBOL BINDU JUDUL ᯾ */, + 0x1BFF /* BATAK SYMBOL BINDU PANGOLAT ᯿ */, + 0x1C3B /* LEPCHA PUNCTUATION TA-ROL ᰻ */, + 0x1C3C /* LEPCHA PUNCTUATION NYET THYOOM TA-ROL ᰼ */, + 0x1C3D /* LEPCHA PUNCTUATION CER-WA ᰽ */, + 0x1C3E /* LEPCHA PUNCTUATION TSHOOK CER-WA ᰾ */, + 0x1C3F /* LEPCHA PUNCTUATION TSHOOK ᰿ */, + 0x1C7E /* OL CHIKI PUNCTUATION MUCAAD ᱾ */, + 0x1C7F /* OL CHIKI PUNCTUATION DOUBLE MUCAAD ᱿ */, + 0x1CC0 /* SUNDANESE PUNCTUATION BINDU SURYA ᳀ */, + 0x1CC1 /* SUNDANESE PUNCTUATION BINDU PANGLONG ᳁ */, + 0x1CC2 /* SUNDANESE PUNCTUATION BINDU PURNAMA ᳂ */, + 0x1CC3 /* SUNDANESE PUNCTUATION BINDU CAKRA ᳃ */, + 0x1CC4 /* SUNDANESE PUNCTUATION BINDU LEU SATANGA ᳄ */, + 0x1CC5 /* SUNDANESE PUNCTUATION BINDU KA SATANGA ᳅ */, + 0x1CC6 /* SUNDANESE PUNCTUATION BINDU DA SATANGA ᳆ */, + 0x1CC7 /* SUNDANESE PUNCTUATION BINDU BA SATANGA ᳇ */, + 0x1CD3 /* VEDIC SIGN NIHSHVASA ᳓ */, + 0x2016 /* DOUBLE VERTICAL LINE ‖ */, + 0x2017 /* DOUBLE LOW LINE ‗ */, + 0x2020 /* DAGGER † */, + 0x2021 /* DOUBLE DAGGER ‡ */, + 0x2022 /* BULLET • */, + 0x2023 /* TRIANGULAR BULLET ‣ */, + 0x2024 /* ONE DOT LEADER ․ */, + 0x2025 /* TWO DOT LEADER ‥ */, + 0x2026 /* HORIZONTAL ELLIPSIS … */, + 0x2027 /* HYPHENATION POINT ‧ */, + 0x2030 /* PER MILLE SIGN ‰ */, + 0x2031 /* PER TEN THOUSAND SIGN ‱ */, + 0x2032 /* PRIME ′ */, + 0x2033 /* DOUBLE PRIME ″ */, + 0x2034 /* TRIPLE PRIME ‴ */, + 0x2035 /* REVERSED PRIME ‵ */, + 0x2036 /* REVERSED DOUBLE PRIME ‶ */, + 0x2037 /* REVERSED TRIPLE PRIME ‷ */, + 0x2038 /* CARET ‸ */, + 0x203B /* REFERENCE MARK ※ */, + 0x203C /* DOUBLE EXCLAMATION MARK ‼ */, + 0x203D /* INTERROBANG ‽ */, + 0x203E /* OVERLINE ‾ */, + 0x2041 /* CARET INSERTION POINT ⁁ */, + 0x2042 /* ASTERISM ⁂ */, + 0x2043 /* HYPHEN BULLET ⁃ */, + 0x2047 /* DOUBLE QUESTION MARK ⁇ */, + 0x2048 /* QUESTION EXCLAMATION MARK ⁈ */, + 0x2049 /* EXCLAMATION QUESTION MARK ⁉ */, + 0x204A /* TIRONIAN SIGN ET ⁊ */, + 0x204B /* REVERSED PILCROW SIGN ⁋ */, + 0x204C /* BLACK LEFTWARDS BULLET ⁌ */, + 0x204D /* BLACK RIGHTWARDS BULLET ⁍ */, + 0x204E /* LOW ASTERISK ⁎ */, + 0x204F /* REVERSED SEMICOLON ⁏ */, + 0x2050 /* CLOSE UP ⁐ */, + 0x2051 /* TWO ASTERISKS ALIGNED VERTICALLY ⁑ */, + 0x2053 /* SWUNG DASH ⁓ */, + 0x2055 /* FLOWER PUNCTUATION MARK ⁕ */, + 0x2056 /* THREE DOT PUNCTUATION ⁖ */, + 0x2057 /* QUADRUPLE PRIME ⁗ */, + 0x2058 /* FOUR DOT PUNCTUATION ⁘ */, + 0x2059 /* FIVE DOT PUNCTUATION ⁙ */, + 0x205A /* TWO DOT PUNCTUATION ⁚ */, + 0x205B /* FOUR DOT MARK ⁛ */, + 0x205C /* DOTTED CROSS ⁜ */, + 0x205D /* TRICOLON ⁝ */, + 0x205E /* VERTICAL FOUR DOTS ⁞ */, + 0x2CF9 /* COPTIC OLD NUBIAN FULL STOP ⳹ */, + 0x2CFA /* COPTIC OLD NUBIAN DIRECT QUESTION MARK ⳺ */, + 0x2CFB /* COPTIC OLD NUBIAN INDIRECT QUESTION MARK ⳻ */, + 0x2CFC /* COPTIC OLD NUBIAN VERSE DIVIDER ⳼ */, + 0x2CFE /* COPTIC FULL STOP ⳾ */, + 0x2CFF /* COPTIC MORPHOLOGICAL DIVIDER ⳿ */, + 0x2D70 /* TIFINAGH SEPARATOR MARK ⵰ */, + 0x2E00 /* RIGHT ANGLE SUBSTITUTION MARKER ⸀ */, + 0x2E01 /* RIGHT ANGLE DOTTED SUBSTITUTION MARKER ⸁ */, + 0x2E06 /* RAISED INTERPOLATION MARKER ⸆ */, + 0x2E07 /* RAISED DOTTED INTERPOLATION MARKER ⸇ */, + 0x2E08 /* DOTTED TRANSPOSITION MARKER ⸈ */, + 0x2E0B /* RAISED SQUARE ⸋ */, + 0x2E0E /* EDITORIAL CORONIS ⸎ */, + 0x2E0F /* PARAGRAPHOS ⸏ */, + 0x2E10 /* FORKED PARAGRAPHOS ⸐ */, + 0x2E11 /* REVERSED FORKED PARAGRAPHOS ⸑ */, + 0x2E12 /* HYPODIASTOLE ⸒ */, + 0x2E13 /* DOTTED OBELOS ⸓ */, + 0x2E14 /* DOWNWARDS ANCORA ⸔ */, + 0x2E15 /* UPWARDS ANCORA ⸕ */, + 0x2E16 /* DOTTED RIGHT-POINTING ANGLE ⸖ */, + 0x2E18 /* INVERTED INTERROBANG ⸘ */, + 0x2E19 /* PALM BRANCH ⸙ */, + 0x2E1B /* TILDE WITH RING ABOVE ⸛ */, + 0x2E1E /* TILDE WITH DOT ABOVE ⸞ */, + 0x2E1F /* TILDE WITH DOT BELOW ⸟ */, + 0x2E2A /* TWO DOTS OVER ONE DOT PUNCTUATION ⸪ */, + 0x2E2B /* ONE DOT OVER TWO DOTS PUNCTUATION ⸫ */, + 0x2E2C /* SQUARED FOUR DOT PUNCTUATION ⸬ */, + 0x2E2D /* FIVE DOT MARK ⸭ */, + 0x2E2E /* REVERSED QUESTION MARK ⸮ */, + 0x2E30 /* RING POINT ⸰ */, + 0x2E31 /* WORD SEPARATOR MIDDLE DOT ⸱ */, + 0x2E32 /* TURNED COMMA ⸲ */, + 0x2E33 /* RAISED DOT ⸳ */, + 0x2E34 /* RAISED COMMA ⸴ */, + 0x2E35 /* TURNED SEMICOLON ⸵ */, + 0x2E36 /* DAGGER WITH LEFT GUARD ⸶ */, + 0x2E37 /* DAGGER WITH RIGHT GUARD ⸷ */, + 0x2E38 /* TURNED DAGGER ⸸ */, + 0x2E39 /* TOP HALF SECTION SIGN ⸹ */, + 0x2E3C /* STENOGRAPHIC FULL STOP ⸼ */, + 0x2E3D /* VERTICAL SIX DOTS ⸽ */, + 0x2E3E /* WIGGLY VERTICAL LINE ⸾ */, + 0x2E3F /* CAPITULUM ⸿ */, + 0x2E41 /* REVERSED COMMA ⹁ */, + 0x2E43 /* DASH WITH LEFT UPTURN ⹃ */, + 0x2E44 /* DOUBLE SUSPENSION MARK ⹄ */, + 0x2E45 /* INVERTED LOW KAVYKA ⹅ */, + 0x2E46 /* INVERTED LOW KAVYKA WITH KAVYKA ABOVE ⹆ */, + 0x2E47 /* LOW KAVYKA ⹇ */, + 0x2E48 /* LOW KAVYKA WITH DOT ⹈ */, + 0x2E49 /* DOUBLE STACKED COMMA ⹉ */, + 0x2E4A /* DOTTED SOLIDUS ⹊ */, + 0x2E4B /* TRIPLE DAGGER ⹋ */, + 0x2E4C /* MEDIEVAL COMMA ⹌ */, + 0x2E4D /* PARAGRAPHUS MARK ⹍ */, + 0x2E4E /* PUNCTUS ELEVATUS MARK ⹎ */, + 0x2E4F /* CORNISH VERSE DIVIDER ⹏ */, + 0x3001 /* IDEOGRAPHIC COMMA 、 */, + 0x3002 /* IDEOGRAPHIC FULL STOP 。 */, + 0x3003 /* DITTO MARK 〃 */, + 0x303D /* PART ALTERNATION MARK 〽 */, + 0x30FB /* KATAKANA MIDDLE DOT ・ */, + 0xA4FE /* LISU PUNCTUATION COMMA ꓾ */, + 0xA4FF /* LISU PUNCTUATION FULL STOP ꓿ */, + 0xA60D /* VAI COMMA ꘍ */, + 0xA60E /* VAI FULL STOP ꘎ */, + 0xA60F /* VAI QUESTION MARK ꘏ */, + 0xA673 /* SLAVONIC ASTERISK ꙳ */, + 0xA67E /* CYRILLIC KAVYKA ꙾ */, + 0xA6F2 /* BAMUM NJAEMLI ꛲ */, + 0xA6F3 /* BAMUM FULL STOP ꛳ */, + 0xA6F4 /* BAMUM COLON ꛴ */, + 0xA6F5 /* BAMUM COMMA ꛵ */, + 0xA6F6 /* BAMUM SEMICOLON ꛶ */, + 0xA6F7 /* BAMUM QUESTION MARK ꛷ */, + 0xA874 /* PHAGS-PA SINGLE HEAD MARK ꡴ */, + 0xA875 /* PHAGS-PA DOUBLE HEAD MARK ꡵ */, + 0xA876 /* PHAGS-PA MARK SHAD ꡶ */, + 0xA877 /* PHAGS-PA MARK DOUBLE SHAD ꡷ */, + 0xA8CE /* SAURASHTRA DANDA ꣎ */, + 0xA8CF /* SAURASHTRA DOUBLE DANDA ꣏ */, + 0xA8F8 /* DEVANAGARI SIGN PUSHPIKA ꣸ */, + 0xA8F9 /* DEVANAGARI GAP FILLER ꣹ */, + 0xA8FA /* DEVANAGARI CARET ꣺ */, + 0xA8FC /* DEVANAGARI SIGN SIDDHAM ꣼ */, + 0xA92E /* KAYAH LI SIGN CWI ꤮ */, + 0xA92F /* KAYAH LI SIGN SHYA ꤯ */, + 0xA95F /* REJANG SECTION MARK ꥟ */, + 0xA9C1 /* JAVANESE LEFT RERENGGAN ꧁ */, + 0xA9C2 /* JAVANESE RIGHT RERENGGAN ꧂ */, + 0xA9C3 /* JAVANESE PADA ANDAP ꧃ */, + 0xA9C4 /* JAVANESE PADA MADYA ꧄ */, + 0xA9C5 /* JAVANESE PADA LUHUR ꧅ */, + 0xA9C6 /* JAVANESE PADA WINDU ꧆ */, + 0xA9C7 /* JAVANESE PADA PANGKAT ꧇ */, + 0xA9C8 /* JAVANESE PADA LINGSA ꧈ */, + 0xA9C9 /* JAVANESE PADA LUNGSI ꧉ */, + 0xA9CA /* JAVANESE PADA ADEG ꧊ */, + 0xA9CB /* JAVANESE PADA ADEG ADEG ꧋ */, + 0xA9CC /* JAVANESE PADA PISELEH ꧌ */, + 0xA9CD /* JAVANESE TURNED PADA PISELEH ꧍ */, + 0xA9DE /* JAVANESE PADA TIRTA TUMETES ꧞ */, + 0xA9DF /* JAVANESE PADA ISEN-ISEN ꧟ */, + 0xAA5C /* CHAM PUNCTUATION SPIRAL ꩜ */, + 0xAA5D /* CHAM PUNCTUATION DANDA ꩝ */, + 0xAA5E /* CHAM PUNCTUATION DOUBLE DANDA ꩞ */, + 0xAA5F /* CHAM PUNCTUATION TRIPLE DANDA ꩟ */, + 0xAADE /* TAI VIET SYMBOL HO HOI ꫞ */, + 0xAADF /* TAI VIET SYMBOL KOI KOI ꫟ */, + 0xAAF0 /* MEETEI MAYEK CHEIKHAN ꫰ */, + 0xAAF1 /* MEETEI MAYEK AHANG KHUDAM ꫱ */, + 0xABEB /* MEETEI MAYEK CHEIKHEI ꯫ */, + 0xFE10 /* PRESENTATION FORM FOR VERTICAL COMMA ︐ */, + 0xFE11 /* PRESENTATION FORM FOR VERTICAL IDEOGRAPHIC COMMA ︑ */, + 0xFE12 /* PRESENTATION FORM FOR VERTICAL IDEOGRAPHIC FULL STOP ︒ */, + 0xFE13 /* PRESENTATION FORM FOR VERTICAL COLON ︓ */, + 0xFE14 /* PRESENTATION FORM FOR VERTICAL SEMICOLON ︔ */, + 0xFE15 /* PRESENTATION FORM FOR VERTICAL EXCLAMATION MARK ︕ */, + 0xFE16 /* PRESENTATION FORM FOR VERTICAL QUESTION MARK ︖ */, + 0xFE19 /* PRESENTATION FORM FOR VERTICAL HORIZONTAL ELLIPSIS ︙ */, + 0xFE30 /* PRESENTATION FORM FOR VERTICAL TWO DOT LEADER ︰ */, + 0xFE45 /* SESAME DOT ﹅ */, + 0xFE46 /* WHITE SESAME DOT ﹆ */, + 0xFE49 /* DASHED OVERLINE ﹉ */, + 0xFE4A /* CENTRELINE OVERLINE ﹊ */, + 0xFE4B /* WAVY OVERLINE ﹋ */, + 0xFE4C /* DOUBLE WAVY OVERLINE ﹌ */, + 0xFE50 /* SMALL COMMA ﹐ */, + 0xFE51 /* SMALL IDEOGRAPHIC COMMA ﹑ */, + 0xFE52 /* SMALL FULL STOP ﹒ */, + 0xFE54 /* SMALL SEMICOLON ﹔ */, + 0xFE55 /* SMALL COLON ﹕ */, + 0xFE56 /* SMALL QUESTION MARK ﹖ */, + 0xFE57 /* SMALL EXCLAMATION MARK ﹗ */, + 0xFE5F /* SMALL NUMBER SIGN ﹟ */, + 0xFE60 /* SMALL AMPERSAND ﹠ */, + 0xFE61 /* SMALL ASTERISK ﹡ */, + 0xFE68 /* SMALL REVERSE SOLIDUS ﹨ */, + 0xFE6A /* SMALL PERCENT SIGN ﹪ */, + 0xFE6B /* SMALL COMMERCIAL AT ﹫ */, + 0xFF01 /* FULLWIDTH EXCLAMATION MARK ! */, + 0xFF02 /* FULLWIDTH QUOTATION MARK " */, + 0xFF03 /* FULLWIDTH NUMBER SIGN # */, + 0xFF05 /* FULLWIDTH PERCENT SIGN % */, + 0xFF06 /* FULLWIDTH AMPERSAND & */, + 0xFF07 /* FULLWIDTH APOSTROPHE ' */, + 0xFF0A /* FULLWIDTH ASTERISK * */, + 0xFF0C /* FULLWIDTH COMMA , */, + 0xFF0E /* FULLWIDTH FULL STOP . */, + 0xFF0F /* FULLWIDTH SOLIDUS / */, + 0xFF1A /* FULLWIDTH COLON : */, + 0xFF1B /* FULLWIDTH SEMICOLON ; */, + 0xFF1F /* FULLWIDTH QUESTION MARK ? */, + 0xFF20 /* FULLWIDTH COMMERCIAL AT @ */, + 0xFF3C /* FULLWIDTH REVERSE SOLIDUS \ */, + 0xFF61 /* HALFWIDTH IDEOGRAPHIC FULL STOP 。 */, + 0xFF64 /* HALFWIDTH IDEOGRAPHIC COMMA 、 */, + 0xFF65 /* HALFWIDTH KATAKANA MIDDLE DOT ・ */, + 0x10100 /* AEGEAN WORD SEPARATOR LINE 𐄀 */, + 0x10101 /* AEGEAN WORD SEPARATOR DOT 𐄁 */, + 0x10102 /* AEGEAN CHECK MARK 𐄂 */, + 0x1039F /* UGARITIC WORD DIVIDER 𐎟 */, + 0x103D0 /* OLD PERSIAN WORD DIVIDER 𐏐 */, + 0x1056F /* CAUCASIAN ALBANIAN CITATION MARK 𐕯 */, + 0x10857 /* IMPERIAL ARAMAIC SECTION SIGN 𐡗 */, + 0x1091F /* PHOENICIAN WORD SEPARATOR 𐤟 */, + 0x1093F /* LYDIAN TRIANGULAR MARK 𐤿 */, + 0x10A50 /* KHAROSHTHI PUNCTUATION DOT 𐩐 */, + 0x10A51 /* KHAROSHTHI PUNCTUATION SMALL CIRCLE 𐩑 */, + 0x10A52 /* KHAROSHTHI PUNCTUATION CIRCLE 𐩒 */, + 0x10A53 /* KHAROSHTHI PUNCTUATION CRESCENT BAR 𐩓 */, + 0x10A54 /* KHAROSHTHI PUNCTUATION MANGALAM 𐩔 */, + 0x10A55 /* KHAROSHTHI PUNCTUATION LOTUS 𐩕 */, + 0x10A56 /* KHAROSHTHI PUNCTUATION DANDA 𐩖 */, + 0x10A57 /* KHAROSHTHI PUNCTUATION DOUBLE DANDA 𐩗 */, + 0x10A58 /* KHAROSHTHI PUNCTUATION LINES 𐩘 */, + 0x10A7F /* OLD SOUTH ARABIAN NUMERIC INDICATOR 𐩿 */, + 0x10AF0 /* MANICHAEAN PUNCTUATION STAR 𐫰 */, + 0x10AF1 /* MANICHAEAN PUNCTUATION FLEURON 𐫱 */, + 0x10AF2 /* MANICHAEAN PUNCTUATION DOUBLE DOT WITHIN DOT 𐫲 */, + 0x10AF3 /* MANICHAEAN PUNCTUATION DOT WITHIN DOT 𐫳 */, + 0x10AF4 /* MANICHAEAN PUNCTUATION DOT 𐫴 */, + 0x10AF5 /* MANICHAEAN PUNCTUATION TWO DOTS 𐫵 */, + 0x10AF6 /* MANICHAEAN PUNCTUATION LINE FILLER 𐫶 */, + 0x10B39 /* AVESTAN ABBREVIATION MARK 𐬹 */, + 0x10B3A /* TINY TWO DOTS OVER ONE DOT PUNCTUATION 𐬺 */, + 0x10B3B /* SMALL TWO DOTS OVER ONE DOT PUNCTUATION 𐬻 */, + 0x10B3C /* LARGE TWO DOTS OVER ONE DOT PUNCTUATION 𐬼 */, + 0x10B3D /* LARGE ONE DOT OVER TWO DOTS PUNCTUATION 𐬽 */, + 0x10B3E /* LARGE TWO RINGS OVER ONE RING PUNCTUATION 𐬾 */, + 0x10B3F /* LARGE ONE RING OVER TWO RINGS PUNCTUATION 𐬿 */, + 0x10B99 /* PSALTER PAHLAVI SECTION MARK 𐮙 */, + 0x10B9A /* PSALTER PAHLAVI TURNED SECTION MARK 𐮚 */, + 0x10B9B /* PSALTER PAHLAVI FOUR DOTS WITH CROSS 𐮛 */, + 0x10B9C /* PSALTER PAHLAVI FOUR DOTS WITH DOT 𐮜 */, + 0x10F55 /* SOGDIAN PUNCTUATION TWO VERTICAL BARS 𐽕 */, + 0x10F56 /* SOGDIAN PUNCTUATION TWO VERTICAL BARS WITH DOTS 𐽖 */, + 0x10F57 /* SOGDIAN PUNCTUATION CIRCLE WITH DOT 𐽗 */, + 0x10F58 /* SOGDIAN PUNCTUATION TWO CIRCLES WITH DOTS 𐽘 */, + 0x10F59 /* SOGDIAN PUNCTUATION HALF CIRCLE WITH DOT 𐽙 */, + 0x11047 /* BRAHMI DANDA 𑁇 */, + 0x11048 /* BRAHMI DOUBLE DANDA 𑁈 */, + 0x11049 /* BRAHMI PUNCTUATION DOT 𑁉 */, + 0x1104A /* BRAHMI PUNCTUATION DOUBLE DOT 𑁊 */, + 0x1104B /* BRAHMI PUNCTUATION LINE 𑁋 */, + 0x1104C /* BRAHMI PUNCTUATION CRESCENT BAR 𑁌 */, + 0x1104D /* BRAHMI PUNCTUATION LOTUS 𑁍 */, + 0x110BB /* KAITHI ABBREVIATION SIGN 𑂻 */, + 0x110BC /* KAITHI ENUMERATION SIGN 𑂼 */, + 0x110BE /* KAITHI SECTION MARK 𑂾 */, + 0x110BF /* KAITHI DOUBLE SECTION MARK 𑂿 */, + 0x110C0 /* KAITHI DANDA 𑃀 */, + 0x110C1 /* KAITHI DOUBLE DANDA 𑃁 */, + 0x11140 /* CHAKMA SECTION MARK 𑅀 */, + 0x11141 /* CHAKMA DANDA 𑅁 */, + 0x11142 /* CHAKMA DOUBLE DANDA 𑅂 */, + 0x11143 /* CHAKMA QUESTION MARK 𑅃 */, + 0x11174 /* MAHAJANI ABBREVIATION SIGN 𑅴 */, + 0x11175 /* MAHAJANI SECTION MARK 𑅵 */, + 0x111C5 /* SHARADA DANDA 𑇅 */, + 0x111C6 /* SHARADA DOUBLE DANDA 𑇆 */, + 0x111C7 /* SHARADA ABBREVIATION SIGN 𑇇 */, + 0x111C8 /* SHARADA SEPARATOR 𑇈 */, + 0x111CD /* SHARADA SUTRA MARK 𑇍 */, + 0x111DB /* SHARADA SIGN SIDDHAM 𑇛 */, + 0x111DD /* SHARADA CONTINUATION SIGN 𑇝 */, + 0x111DE /* SHARADA SECTION MARK-1 𑇞 */, + 0x111DF /* SHARADA SECTION MARK-2 𑇟 */, + 0x11238 /* KHOJKI DANDA 𑈸 */, + 0x11239 /* KHOJKI DOUBLE DANDA 𑈹 */, + 0x1123A /* KHOJKI WORD SEPARATOR 𑈺 */, + 0x1123B /* KHOJKI SECTION MARK 𑈻 */, + 0x1123C /* KHOJKI DOUBLE SECTION MARK 𑈼 */, + 0x1123D /* KHOJKI ABBREVIATION SIGN 𑈽 */, + 0x112A9 /* MULTANI SECTION MARK 𑊩 */, + 0x1144B /* NEWA DANDA 𑑋 */, + 0x1144C /* NEWA DOUBLE DANDA 𑑌 */, + 0x1144D /* NEWA COMMA 𑑍 */, + 0x1144E /* NEWA GAP FILLER 𑑎 */, + 0x1144F /* NEWA ABBREVIATION SIGN 𑑏 */, + 0x1145B /* NEWA PLACEHOLDER MARK 𑑛 */, + 0x1145D /* NEWA INSERTION SIGN 𑑝 */, + 0x114C6 /* TIRHUTA ABBREVIATION SIGN 𑓆 */, + 0x115C1 /* SIDDHAM SIGN SIDDHAM 𑗁 */, + 0x115C2 /* SIDDHAM DANDA 𑗂 */, + 0x115C3 /* SIDDHAM DOUBLE DANDA 𑗃 */, + 0x115C4 /* SIDDHAM SEPARATOR DOT 𑗄 */, + 0x115C5 /* SIDDHAM SEPARATOR BAR 𑗅 */, + 0x115C6 /* SIDDHAM REPETITION MARK-1 𑗆 */, + 0x115C7 /* SIDDHAM REPETITION MARK-2 𑗇 */, + 0x115C8 /* SIDDHAM REPETITION MARK-3 𑗈 */, + 0x115C9 /* SIDDHAM END OF TEXT MARK 𑗉 */, + 0x115CA /* SIDDHAM SECTION MARK WITH TRIDENT AND U-SHAPED ORNAMENTS 𑗊 */, + 0x115CB /* SIDDHAM SECTION MARK WITH TRIDENT AND DOTTED CRESCENTS 𑗋 */, + 0x115CC /* SIDDHAM SECTION MARK WITH RAYS AND DOTTED CRESCENTS 𑗌 */, + 0x115CD /* SIDDHAM SECTION MARK WITH RAYS AND DOTTED DOUBLE CRESCENTS 𑗍 */, + 0x115CE /* SIDDHAM SECTION MARK WITH RAYS AND DOTTED TRIPLE CRESCENTS 𑗎 */, + 0x115CF /* SIDDHAM SECTION MARK DOUBLE RING 𑗏 */, + 0x115D0 /* SIDDHAM SECTION MARK DOUBLE RING WITH RAYS 𑗐 */, + 0x115D1 /* SIDDHAM SECTION MARK WITH DOUBLE CRESCENTS 𑗑 */, + 0x115D2 /* SIDDHAM SECTION MARK WITH TRIPLE CRESCENTS 𑗒 */, + 0x115D3 /* SIDDHAM SECTION MARK WITH QUADRUPLE CRESCENTS 𑗓 */, + 0x115D4 /* SIDDHAM SECTION MARK WITH SEPTUPLE CRESCENTS 𑗔 */, + 0x115D5 /* SIDDHAM SECTION MARK WITH CIRCLES AND RAYS 𑗕 */, + 0x115D6 /* SIDDHAM SECTION MARK WITH CIRCLES AND TWO ENCLOSURES 𑗖 */, + 0x115D7 /* SIDDHAM SECTION MARK WITH CIRCLES AND FOUR ENCLOSURES 𑗗 */, + 0x11641 /* MODI DANDA 𑙁 */, + 0x11642 /* MODI DOUBLE DANDA 𑙂 */, + 0x11643 /* MODI ABBREVIATION SIGN 𑙃 */, + 0x11660 /* MONGOLIAN BIRGA WITH ORNAMENT 𑙠 */, + 0x11661 /* MONGOLIAN ROTATED BIRGA 𑙡 */, + 0x11662 /* MONGOLIAN DOUBLE BIRGA WITH ORNAMENT 𑙢 */, + 0x11663 /* MONGOLIAN TRIPLE BIRGA WITH ORNAMENT 𑙣 */, + 0x11664 /* MONGOLIAN BIRGA WITH DOUBLE ORNAMENT 𑙤 */, + 0x11665 /* MONGOLIAN ROTATED BIRGA WITH ORNAMENT 𑙥 */, + 0x11666 /* MONGOLIAN ROTATED BIRGA WITH DOUBLE ORNAMENT 𑙦 */, + 0x11667 /* MONGOLIAN INVERTED BIRGA 𑙧 */, + 0x11668 /* MONGOLIAN INVERTED BIRGA WITH DOUBLE ORNAMENT 𑙨 */, + 0x11669 /* MONGOLIAN SWIRL BIRGA 𑙩 */, + 0x1166A /* MONGOLIAN SWIRL BIRGA WITH ORNAMENT 𑙪 */, + 0x1166B /* MONGOLIAN SWIRL BIRGA WITH DOUBLE ORNAMENT 𑙫 */, + 0x1166C /* MONGOLIAN TURNED SWIRL BIRGA WITH DOUBLE ORNAMENT 𑙬 */, + 0x1173C /* AHOM SIGN SMALL SECTION 𑜼 */, + 0x1173D /* AHOM SIGN SECTION 𑜽 */, + 0x1173E /* AHOM SIGN RULAI 𑜾 */, + 0x1183B /* DOGRA ABBREVIATION SIGN 𑠻 */, + 0x119E2 /* NANDINAGARI SIGN SIDDHAM 𑧢 */, + 0x11A3F /* ZANABAZAR SQUARE INITIAL HEAD MARK 𑨿 */, + 0x11A40 /* ZANABAZAR SQUARE CLOSING HEAD MARK 𑩀 */, + 0x11A41 /* ZANABAZAR SQUARE MARK TSHEG 𑩁 */, + 0x11A42 /* ZANABAZAR SQUARE MARK SHAD 𑩂 */, + 0x11A43 /* ZANABAZAR SQUARE MARK DOUBLE SHAD 𑩃 */, + 0x11A44 /* ZANABAZAR SQUARE MARK LONG TSHEG 𑩄 */, + 0x11A45 /* ZANABAZAR SQUARE INITIAL DOUBLE-LINED HEAD MARK 𑩅 */, + 0x11A46 /* ZANABAZAR SQUARE CLOSING DOUBLE-LINED HEAD MARK 𑩆 */, + 0x11A9A /* SOYOMBO MARK TSHEG 𑪚 */, + 0x11A9B /* SOYOMBO MARK SHAD 𑪛 */, + 0x11A9C /* SOYOMBO MARK DOUBLE SHAD 𑪜 */, + 0x11A9E /* SOYOMBO HEAD MARK WITH MOON AND SUN AND TRIPLE FLAME 𑪞 */, + 0x11A9F /* SOYOMBO HEAD MARK WITH MOON AND SUN AND FLAME 𑪟 */, + 0x11AA0 /* SOYOMBO HEAD MARK WITH MOON AND SUN 𑪠 */, + 0x11AA1 /* SOYOMBO TERMINAL MARK-1 𑪡 */, + 0x11AA2 /* SOYOMBO TERMINAL MARK-2 𑪢 */, + 0x11C41 /* BHAIKSUKI DANDA 𑱁 */, + 0x11C42 /* BHAIKSUKI DOUBLE DANDA 𑱂 */, + 0x11C43 /* BHAIKSUKI WORD SEPARATOR 𑱃 */, + 0x11C44 /* BHAIKSUKI GAP FILLER-1 𑱄 */, + 0x11C45 /* BHAIKSUKI GAP FILLER-2 𑱅 */, + 0x11C70 /* MARCHEN HEAD MARK 𑱰 */, + 0x11C71 /* MARCHEN MARK SHAD 𑱱 */, + 0x11EF7 /* MAKASAR PASSIMBANG 𑻷 */, + 0x11EF8 /* MAKASAR END OF SECTION 𑻸 */, + 0x11FFF /* TAMIL PUNCTUATION END OF TEXT 𑿿 */, + 0x12470 /* CUNEIFORM PUNCTUATION SIGN OLD ASSYRIAN WORD DIVIDER 𒑰 */, + 0x12471 /* CUNEIFORM PUNCTUATION SIGN VERTICAL COLON 𒑱 */, + 0x12472 /* CUNEIFORM PUNCTUATION SIGN DIAGONAL COLON 𒑲 */, + 0x12473 /* CUNEIFORM PUNCTUATION SIGN DIAGONAL TRICOLON 𒑳 */, + 0x12474 /* CUNEIFORM PUNCTUATION SIGN DIAGONAL QUADCOLON 𒑴 */, + 0x16A6E /* MRO DANDA 𖩮 */, + 0x16A6F /* MRO DOUBLE DANDA 𖩯 */, + 0x16AF5 /* BASSA VAH FULL STOP 𖫵 */, + 0x16B37 /* PAHAWH HMONG SIGN VOS THOM 𖬷 */, + 0x16B38 /* PAHAWH HMONG SIGN VOS TSHAB CEEB 𖬸 */, + 0x16B39 /* PAHAWH HMONG SIGN CIM CHEEM 𖬹 */, + 0x16B3A /* PAHAWH HMONG SIGN VOS THIAB 𖬺 */, + 0x16B3B /* PAHAWH HMONG SIGN VOS FEEM 𖬻 */, + 0x16B44 /* PAHAWH HMONG SIGN XAUS 𖭄 */, + 0x16E97 /* MEDEFAIDRIN COMMA 𖺗 */, + 0x16E98 /* MEDEFAIDRIN FULL STOP 𖺘 */, + 0x16E99 /* MEDEFAIDRIN SYMBOL AIVA 𖺙 */, + 0x16E9A /* MEDEFAIDRIN EXCLAMATION OH 𖺚 */, + 0x16FE2 /* OLD CHINESE HOOK MARK 𖿢 */, + 0x1BC9F /* DUPLOYAN PUNCTUATION CHINOOK FULL STOP 𛲟 */, + 0x1DA87 /* SIGNWRITING COMMA 𝪇 */, + 0x1DA88 /* SIGNWRITING FULL STOP 𝪈 */, + 0x1DA89 /* SIGNWRITING SEMICOLON 𝪉 */, + 0x1DA8A /* SIGNWRITING COLON 𝪊 */, + 0x1DA8B /* SIGNWRITING PARENTHESIS 𝪋 */, + 0x1E95E /* ADLAM INITIAL EXCLAMATION MARK 𞥞 */, + 0x1E95F /* ADLAM INITIAL QUESTION MARK */, + ] +) diff --git a/v_windows/v/old/vlib/encoding/utf8/utf8_util_test.v b/v_windows/v/old/vlib/encoding/utf8/utf8_util_test.v new file mode 100644 index 0000000..f09cb76 --- /dev/null +++ b/v_windows/v/old/vlib/encoding/utf8/utf8_util_test.v @@ -0,0 +1,66 @@ +import encoding.utf8 + +fn test_utf8_util() { + // string test + src := 'ăĂ ôÔ testo 怔' //_\u1E5A\u1E5B<=>\u1F49\u1F41<=>\u0128\u012a\u012c" // len 29 runes, raw 49 bytes + src_upper := 'ĂĂ ÔÔ TESTO Æ€”' //_\u1E5A\u1E5A<=>\u1F49\u1F49<=>\u0128\u012A\u012C" + src_lower := 'ăă ôô testo 怔' //_\u1E5B\u1E5B<=>\u1F41\u1F41<=>\u0129\u012B\u012D" + upper := utf8.to_upper(src) + lower := utf8.to_lower(src) + assert upper == src_upper + assert lower == src_lower + + assert utf8.to_upper('абвёabc12{') == 'АБВЁABC12{' + assert utf8.to_lower('АБВЁABC12{') == 'абвёabc12{' + + // test len function + assert utf8.len('') == 0 + assert utf8.len('pippo') == 5 + assert utf8.len(src) == 15 // 29 + assert src.len == 24 // 49 + + // western punctuation + a := '.abc?abcòàè.' + assert utf8.is_punct(a, 0) == true + assert utf8.is_punct('b', 0) == false + assert utf8.is_uchar_punct(0x002E) == true + assert utf8.is_punct(a, 4) == true // ? + assert utf8.is_punct(a, 14) == true // last . + assert utf8.is_punct(a, 12) == false // è + println('OK western') + + // global punctuation + b := '.ĂĂa. ÔÔ TESTO Æ€' + assert utf8.is_global_punct(b, 0) == true + assert utf8.is_global_punct('.', 0) == true + assert utf8.is_uchar_punct(0x002E) == true + assert utf8.is_global_punct(b, 6) == true // . + assert utf8.is_global_punct(b, 1) == false // a + + // test utility functions + assert utf8.get_uchar(b, 0) == 0x002E +} + +fn test_raw_indexing() { + a := '我是V Lang!' + + // test non ascii characters + assert utf8.raw_index(a, 0) == '我' + assert utf8.raw_index(a, 1) == '是' + + // test ascii characters + assert utf8.raw_index(a, 2) == 'V' + assert utf8.raw_index(a, 3) == ' ' + assert utf8.raw_index(a, 4) == 'L' + assert utf8.raw_index(a, 5) == 'a' + assert utf8.raw_index(a, 6) == 'n' + assert utf8.raw_index(a, 7) == 'g' + assert utf8.raw_index(a, 8) == '!' +} + +fn test_reversed() { + a := '我是V Lang!' + b := '你好世界hello world' + assert utf8.reverse(a) == '!gnaL V是我' + assert utf8.reverse(b) == 'dlrow olleh界世好你' +} diff --git a/v_windows/v/old/vlib/eventbus/README.md b/v_windows/v/old/vlib/eventbus/README.md new file mode 100644 index 0000000..93047db --- /dev/null +++ b/v_windows/v/old/vlib/eventbus/README.md @@ -0,0 +1,122 @@ +# Event Bus + +A module to provide eventing capabilities using pub/sub. + +## API + +1. `new()` - create a new `EventBus` + +### Structs: + +**EventBus:** + +1. `publish(name string, sender voidptr, args voidptr)` - publish an event with provided + Params & name +2. `clear_all()` - clear all subscribers +3. `has_subscriber(name string)` - check if a subscriber to an event exists + +**Subscriber:** + +1. `subscribe(name string, handler EventHandlerFn)` - subscribe to an event +2. `subscribe_once(name string, handler EventHandlerFn)` - subscribe only once to an event +3. `subscribe_method(name string, handler EventHandlerFn, receiver voidptr)` - subscribe to + an event and also set the `receiver` as a parameter. + Since it's not yet possible to send methods as parameters, this is a workaround. +4. `is_subscribed(name string)` - check if we are subscribed to an event +5. `unsubscribe(name string)` - unsubscribe from an event + +**Event Handler Signature:** + +The function given to `subscribe`, `subscribe_method` and `subscribe_once` must match this: + +```v oksyntax +fn cb(receiver voidptr, args voidptr, sender voidptr) { +} + +// Since V can map structs to voidptr, this also works +struct ClickEvent { + x int + y int +} + +// Example case where publisher sends ClickEvent as args. +fn on_press(receiver voidptr, e &ClickEvent, sender voidptr) { + println(e.x) + // your code here... +} +``` + +## Usage + +For **usage across modules** +[check the example](https://github.com/vlang/v/tree/master/examples/eventbus). + +_Note: As a general rule, you will need to **subscribe before publishing**._ + +**main.v** + +```v oksyntax +module main + +import eventbus + +// initialize it globally +const ( + eb = eventbus.new() +) + +fn main() { + // get a mutable reference to the subscriber + mut sub := eb.subscriber + // subscribe to the 'error' event + sub.subscribe('error', on_error) + // start the work + do_work() +} + +// the event handler +fn on_error(receiver voidptr, e &Error, work &Work) { + println('error occured on ${work.hours}. Error: $e.message') +} +``` + +**work.v** + +```v oksyntax +module main + +import eventbus + +const eb = eventbus.new() + +struct Work { + hours int +} + +struct AnError { + message string +} + +fn do_work() { + work := Work{20} + // get a mutable Params instance & put some data into it + error := &AnError{'Error: no internet connection.'} + // publish the event + eb.publish('error', work, error) +} +``` + +### Notes: + +1. Each `EventBus` instance has it's own registry (i.e. there is no global event registry + so you can't just subscribe to an event wherever you are. +2. Each `EventBus` has a `Subscriber` instance which will need to be either exposed or you can make + small public helper functions specific to your module like (`onPress`, `onError`) and etc. +3. The `eventbus` module has some helpers to ease getting/setting of Params + (since V doesn't support empty interfaces yet or reflection) so use them (see usage above). + +**The rationale behind separating Subscriber & Publisher:** + +This is mainly for security because if publisher & subscriber are both passed around, +a client can easily publish events acting as the server. +So a client should only be able to use the Subscriber methods. diff --git a/v_windows/v/old/vlib/eventbus/eventbus.v b/v_windows/v/old/vlib/eventbus/eventbus.v new file mode 100644 index 0000000..69cf118 --- /dev/null +++ b/v_windows/v/old/vlib/eventbus/eventbus.v @@ -0,0 +1,121 @@ +module eventbus + +pub type EventHandlerFn = fn (receiver voidptr, args voidptr, sender voidptr) + +pub struct Publisher { +mut: + registry &Registry +} + +pub struct Subscriber { +mut: + registry &Registry +} + +struct Registry { +mut: + events []EventHandler +} + +struct EventHandler { + name string + handler EventHandlerFn + receiver voidptr = voidptr(0) + once bool +} + +pub struct EventBus { +pub mut: + registry &Registry + publisher &Publisher + subscriber &Subscriber +} + +pub fn new() &EventBus { + registry := &Registry{ + events: [] + } + return &EventBus{registry, &Publisher{registry}, &Subscriber{registry}} +} + +// EventBus Methods +pub fn (eb &EventBus) publish(name string, sender voidptr, args voidptr) { + mut publisher := eb.publisher + publisher.publish(name, sender, args) +} + +pub fn (eb &EventBus) clear_all() { + mut publisher := eb.publisher + publisher.clear_all() +} + +pub fn (eb &EventBus) has_subscriber(name string) bool { + return eb.registry.check_subscriber(name) +} + +// Publisher Methods +fn (mut pb Publisher) publish(name string, sender voidptr, args voidptr) { + for event in pb.registry.events { + if event.name == name { + event.handler(event.receiver, args, sender) + } + } + pb.registry.events = pb.registry.events.filter(!(it.name == name && it.once)) +} + +fn (mut p Publisher) clear_all() { + p.registry.events.clear() +} + +// Subscriber Methods +pub fn (mut s Subscriber) subscribe(name string, handler EventHandlerFn) { + s.registry.events << EventHandler{ + name: name + handler: handler + } +} + +pub fn (mut s Subscriber) subscribe_method(name string, handler EventHandlerFn, receiver voidptr) { + s.registry.events << EventHandler{ + name: name + handler: handler + receiver: receiver + } +} + +// unsubscribe_method unsubscribe a receiver for only one method +pub fn (mut s Subscriber) unsubscribe_method(name string, receiver voidptr) { + s.registry.events = s.registry.events.filter(!(it.name == name && it.receiver == receiver)) +} + +// unsubscribe_receiver unsubscribes a receiver from all events +pub fn (mut s Subscriber) unsubscribe_receiver(receiver voidptr) { + s.registry.events = s.registry.events.filter(it.receiver != receiver) +} + +pub fn (mut s Subscriber) subscribe_once(name string, handler EventHandlerFn) { + s.registry.events << EventHandler{ + name: name + handler: handler + once: true + } +} + +pub fn (s &Subscriber) is_subscribed(name string) bool { + return s.registry.check_subscriber(name) +} + +// is_subscribed_method checks whether a receiver was already subscribed for any events +pub fn (s &Subscriber) is_subscribed_method(name string, receiver voidptr) bool { + return s.registry.events.any(it.name == name && it.receiver == receiver) +} + +pub fn (mut s Subscriber) unsubscribe(name string, handler EventHandlerFn) { + // v := voidptr(handler) + s.registry.events = s.registry.events.filter(!(it.name == name && it.handler == handler)) +} + +// Registry Methods +fn (r &Registry) check_subscriber(name string) bool { + return r.events.any(it.name == name) +} diff --git a/v_windows/v/old/vlib/eventbus/eventbus_test.v b/v_windows/v/old/vlib/eventbus/eventbus_test.v new file mode 100644 index 0000000..e27af88 --- /dev/null +++ b/v_windows/v/old/vlib/eventbus/eventbus_test.v @@ -0,0 +1,109 @@ +import eventbus + +struct EventData { + data string +} + +struct FakeReceiver { + ok bool +} + +fn test_eventbus() { + ev_data := &EventData{'hello'} + mut eb := eventbus.new() + eb.subscriber.subscribe_once('on_test', on_test) + assert eb.has_subscriber('on_test') + assert eb.subscriber.is_subscribed('on_test') + eb.publish('on_test', eb, ev_data) + assert !eb.has_subscriber('on_test') + assert !eb.subscriber.is_subscribed('on_test') + eb.subscriber.subscribe('on_test', on_test) + assert eb.has_subscriber('on_test') + assert eb.subscriber.is_subscribed('on_test') + eb.clear_all() + assert !eb.has_subscriber('on_test') + assert !eb.subscriber.is_subscribed('on_test') +} + +fn test_subscribe_method() { + // Does not really test subscribe_method idinvidually though + // given + mut eb := eventbus.new() + r := FakeReceiver{} + + assert !eb.subscriber.is_subscribed_method('on_test_with_receiver', r) + // when + eb.subscriber.subscribe_method('on_test_with_receiver', on_test_with_receiver, r) + + // then + assert eb.subscriber.is_subscribed_method('on_test_with_receiver', r) +} + +fn test_unsubscribe_method() { + // given + mut eb := eventbus.new() + r := FakeReceiver{} + r2 := FakeReceiver{} + + // when + eb.subscriber.subscribe_method('on_test_with_receiver', on_test_with_receiver, r) + eb.subscriber.subscribe_method('on_test_with_receiver', on_test_with_receiver, r2) + eb.subscriber.unsubscribe_method('on_test_with_receiver', r) + + // then + assert !eb.subscriber.is_subscribed_method('on_test_with_receiver', r) + assert eb.subscriber.is_subscribed_method('on_test_with_receiver', r2) +} + +fn test_publish() { + // given + ev_data := &EventData{'hello'} + mut eb := eventbus.new() + + // when + eb.subscriber.subscribe_once('on_test', on_test) + eb.subscriber.subscribe_once('on_test', on_test) + eb.publish('on_test', eb, ev_data) + + // then + assert !eb.subscriber.is_subscribed('on_test') +} + +fn test_publish_with_receiver() { + // given + mut eb := eventbus.new() + ev_data := &EventData{'hello'} + r := FakeReceiver{} + + // when + eb.subscriber.subscribe_method('on_test_with_receiver', on_test_with_receiver, r) + eb.publish('on_test_with_receiver', eb, ev_data) + + // then asserts are in on_test_with_receiver, don't know how to be sure + // that it has been properly called... +} + +fn test_unsubscribe_reveiver() { + // given + mut eb := eventbus.new() + r := &FakeReceiver{} + + // when + eb.subscriber.subscribe_method('on_test_with_receiver', on_test_with_receiver, r) + eb.subscriber.subscribe_method('on_test', on_test, r) + eb.subscriber.unsubscribe_receiver(r) + assert !eb.subscriber.is_subscribed_method('on_test_with_receiver', r) + assert !eb.subscriber.is_subscribed_method('on_test', r) +} + +fn on_test(receiver voidptr, ev &EventData, sender voidptr) { + assert receiver == 0 + assert sender != 0 + assert ev.data == 'hello' +} + +fn on_test_with_receiver(receiver &FakeReceiver, ev &EventData, sender voidptr) { + assert receiver.ok == false + assert sender != 0 + assert ev.data == 'hello' +} diff --git a/v_windows/v/old/vlib/flag/README.md b/v_windows/v/old/vlib/flag/README.md new file mode 100644 index 0000000..1122f77 --- /dev/null +++ b/v_windows/v/old/vlib/flag/README.md @@ -0,0 +1,36 @@ +The `flag` module helps command-line flag parsing. +Main features are: +- parses flags like `-f` or '--flag' or '--stuff=things' or '--things stuff'. +- handles bool, int, float and string args. +- can print usage information listing all the declrared flags. +- handles unknown arguments as error. + +Usage example: + +```v +module main + +import os +import flag + +fn main() { + mut fp := flag.new_flag_parser(os.args) + fp.application('flag_example_tool') + fp.version('v0.0.1') + fp.limit_free_args(0, 0) // comment this, if you expect arbitrary texts after the options + fp.description('This tool is only designed to show how the flag lib is working') + fp.skip_executable() + an_int := fp.int('an_int', 0, 0o123, 'some int to define 0o123 is its default value') + a_bool := fp.bool('a_bool', 0, false, 'some boolean flag. --a_bool will set it to true.') + a_float := fp.float('a_float', 0, 1.0, 'some floating point value, by default 1.0 .') + a_string := fp.string('a_string', `a`, 'no text', 'finally, some text with ' + + ' `-a` as an abbreviation, so you can pass --a_string abc or just -a abc') + additional_args := fp.finalize() or { + eprintln(err) + println(fp.usage()) + return + } + println('an_int: $an_int | a_bool: $a_bool | a_float: $a_float | a_string: "$a_string" ') + println(additional_args.join_lines()) +} +``` diff --git a/v_windows/v/old/vlib/flag/default_flag_options_test.v b/v_windows/v/old/vlib/flag/default_flag_options_test.v new file mode 100644 index 0000000..fb0a423 --- /dev/null +++ b/v_windows/v/old/vlib/flag/default_flag_options_test.v @@ -0,0 +1,35 @@ +import os + +const source = 'vlib/flag/testdata/simplest_flag_program.v' + +const simple_flag_app_executable = os.real_path(os.join_path(os.cache_dir(), 'simple_flag_app.exe')) + +fn testsuite_begin() { + os.chdir(@VMODROOT) + os.rm(simple_flag_app_executable) or {} + res := os.execute('${@VEXE} -o $simple_flag_app_executable $source') + assert res.exit_code == 0 + assert os.execute(simple_flag_app_executable).exit_code == 0 +} + +fn testsuite_end() { + os.rm(simple_flag_app_executable) or {} + assert true +} + +fn check_program(opts string, extension string) { + result := source.replace('.v', extension) + res := os.execute('$simple_flag_app_executable $opts') + lines := os.read_lines(result) or { panic(err) } + assert res.exit_code == 0 + assert res.output.split_into_lines() == lines +} + +fn test_default_builtin_flag_options() { + check_program('', '.out') + check_program(' -- --help', '.dashdash.help.out') + check_program(' -- --version', '.dashdash.version.out') + check_program(' -h', '.help.out') + check_program(' --help', '.help.out') + check_program(' --version', '.version.out') +} diff --git a/v_windows/v/old/vlib/flag/flag.v b/v_windows/v/old/vlib/flag/flag.v new file mode 100644 index 0000000..85bb09d --- /dev/null +++ b/v_windows/v/old/vlib/flag/flag.v @@ -0,0 +1,624 @@ +module flag + +// data object storing information about a defined flag +pub struct Flag { +pub: + name string // name as it appears on command line + abbr byte // shortcut + usage string // help message + val_desc string // something like '' that appears in usage, + // and also the default value, when the flag is not given +} + +struct UnkownFlagError { + msg string + code int +} + +struct MinimumArgsCountError { + msg string + code int +} + +struct MaximumArgsCountError { + msg string + code int +} + +struct NoArgsExpectedError { + msg string + code int +} + +[unsafe] +fn (mut f Flag) free() { + unsafe { + f.name.free() + f.usage.free() + f.val_desc.free() + } +} + +pub fn (f Flag) str() string { + return '' + ' flag:\n' + ' name: $f.name\n' + + ' abbr: `$f.abbr.ascii_str()`\n' + ' usag: $f.usage\n' + + ' desc: $f.val_desc' +} + +pub fn (af []Flag) str() string { + mut res := []string{} + res << '\n []Flag = [' + for f in af { + res << f.str() + } + res << ' ]' + return res.join('\n') +} + +// +pub struct FlagParser { +pub: + original_args []string // the original arguments to be parsed + idx_dashdash int // the index of a `--`, -1 if there is not any + all_after_dashdash []string // all options after `--` are ignored, and will be passed to the application unmodified +pub mut: + usage_examples []string // when set, --help will print: + // Usage: $appname $usage_examples[0]` + // or: $appname $usage_examples[1]` + // etc + default_help_label string = 'display this help and exit' + default_version_label string = 'output version information and exit' + args []string // the current list of processed args + max_free_args int + flags []Flag // registered flags + application_name string + application_version string + application_description string + min_free_args int + args_description string + allow_unknown_args bool // whether passing undescribed arguments is allowed + footers []string // when set, --help will display all the collected footers at the bottom. +} + +[unsafe] +fn (mut f FlagParser) free() { + unsafe { + for a in f.args { + a.free() + } + f.args.free() + // + for flag in f.flags { + flag.free() + } + f.flags.free() + // + f.application_name.free() + f.application_version.free() + f.application_description.free() + f.args_description.free() + } +} + +pub const ( + // used for formating usage message + space = ' ' + underline = '-----------------------------------------------' + max_args_number = 4048 +) + +// create a new flag set for parsing command line arguments +pub fn new_flag_parser(args []string) &FlagParser { + original_args := args.clone() + idx_dashdash := args.index('--') + mut all_before_dashdash := args.clone() + mut all_after_dashdash := []string{} + if idx_dashdash >= 0 { + all_before_dashdash.trim(idx_dashdash) + if idx_dashdash < original_args.len { + all_after_dashdash = original_args[idx_dashdash + 1..] + } + } + return &FlagParser{ + original_args: original_args + idx_dashdash: idx_dashdash + all_after_dashdash: all_after_dashdash + args: all_before_dashdash + max_free_args: flag.max_args_number + } +} + +// usage_example - add an usage example +// All examples will be listed in the help screen. +// If you do not give any examples, then a default usage +// will be shown, based on whether the application takes +// options and expects additional parameters. +pub fn (mut fs FlagParser) usage_example(example string) { + fs.usage_examples << example +} + +// add_footer - add a footnote, that will be shown +// at the bottom of the help screen. +pub fn (mut fs FlagParser) footer(footer string) { + fs.footers << footer +} + +// change the application name to be used in 'usage' output +pub fn (mut fs FlagParser) application(name string) { + fs.application_name = name +} + +// change the application version to be used in 'usage' output +pub fn (mut fs FlagParser) version(vers string) { + fs.application_version = vers +} + +// description appends to the application description lines, shown +// in the help/usage screen +pub fn (mut fs FlagParser) description(desc string) { + if fs.application_description.len == 0 { + fs.application_description = desc + } else { + fs.application_description += '\n$desc' + } +} + +// in most cases you do not need the first argv for flag parsing +pub fn (mut fs FlagParser) skip_executable() { + fs.args.delete(0) +} + +// allow_unknown_args - if your program has sub commands, that have +// their own arguments, you can call .allow_unknown_args(), so that +// the subcommand arguments (which generally are not known to your +// parent program), will not cause the validation in .finalize() to fail. +pub fn (mut fs FlagParser) allow_unknown_args() { + fs.allow_unknown_args = true +} + +// private helper to register a flag +fn (mut fs FlagParser) add_flag(name string, abbr byte, usage string, desc string) { + fs.flags << Flag{ + name: name + abbr: abbr + usage: usage + val_desc: desc + } +} + +// private: general parsing a single argument +// - search args for existence +// if true +// extract the defined value as string +// else +// return an (dummy) error -> argument is not defined +// +// - the name, usage are registered +// - found arguments and corresponding values are removed from args list +[manualfree] +fn (mut fs FlagParser) parse_value(longhand string, shorthand byte) []string { + full := '--$longhand' + defer { + unsafe { full.free() } + } + mut found_entries := []string{} + mut to_delete := []int{} + defer { + unsafe { to_delete.free() } + } + mut should_skip_one := false + for i, arg in fs.args { + if should_skip_one { + should_skip_one = false + continue + } + if arg.len == 0 || arg[0] != `-` { + continue + } + if (arg.len == 2 && arg[0] == `-` && arg[1] == shorthand) || arg == full { + if i + 1 >= fs.args.len { + return [] + } + nextarg := fs.args[i + 1] + if nextarg.len > 2 { + nextarg_rest := nextarg[..2] + if nextarg_rest == '--' { + // It could be end of input (--) or another argument (--abc). + // Both are invalid so die. + unsafe { nextarg_rest.free() } + return [] + } + unsafe { nextarg_rest.free() } + } + found_entries << fs.args[i + 1] + to_delete << i + to_delete << i + 1 + should_skip_one = true + continue + } + if arg.len > full.len + 1 && arg[..full.len + 1] == '$full=' { + found_entries << arg[full.len + 1..] + to_delete << i + continue + } + } + for i, del in to_delete { + // i entrys are deleted so it's shifted left i times. + fs.args.delete(del - i) + } + return found_entries +} + +// special parsing for bool values +// see also: parse_value +// +// special: it is allowed to define bool flags without value +// -> '--flag' is parsed as true +// -> '--flag' is equal to '--flag=true' +fn (mut fs FlagParser) parse_bool_value(longhand string, shorthand byte) ?string { + { + full := '--$longhand' + for i, arg in fs.args { + if arg.len == 0 { + continue + } + if arg[0] != `-` { + continue + } + if (arg.len == 2 && arg[0] == `-` && arg[1] == shorthand) || arg == full { + if fs.args.len > i + 1 && (fs.args[i + 1] in ['true', 'false']) { + val := fs.args[i + 1] + fs.args.delete(i + 1) + fs.args.delete(i) + return val + } else { + fs.args.delete(i) + return 'true' + } + } + if arg.len > full.len + 1 && arg[..full.len + 1] == '$full=' { + // Flag abc=true + val := arg[full.len + 1..] + fs.args.delete(i) + return val + } + if arg.len > 1 && arg[0] == `-` && arg[1] != `-` && arg.index_byte(shorthand) != -1 { + // -abc is equivalent to -a -b -c + return 'true' + } + } + } + return error("parameter '$longhand' not found") +} + +// bool_opt returns an optional that returns the value associated with the flag. +// In the situation that the flag was not provided, it returns null. +pub fn (mut fs FlagParser) bool_opt(name string, abbr byte, usage string) ?bool { + mut res := false + { + fs.add_flag(name, abbr, usage, '') + parsed := fs.parse_bool_value(name, abbr) or { + return error("parameter '$name' not provided") + } + res = parsed == 'true' + } + return res +} + +// defining and parsing a bool flag +// if defined +// the value is returned (true/false) +// else +// the default value is returned +// version with abbr +// TODO error handling for invalid string to bool conversion +pub fn (mut fs FlagParser) bool(name string, abbr byte, bdefault bool, usage string) bool { + value := fs.bool_opt(name, abbr, usage) or { return bdefault } + return value +} + +// int_multi returns all instances of values associated with the flags provided +// In the case that none were found, it returns an empty array. +pub fn (mut fs FlagParser) int_multi(name string, abbr byte, usage string) []int { + fs.add_flag(name, abbr, usage, '') + parsed := fs.parse_value(name, abbr) + mut value := []int{} + for val in parsed { + value << val.int() + } + return value +} + +// int_opt returns an optional that returns the value associated with the flag. +// In the situation that the flag was not provided, it returns null. +pub fn (mut fs FlagParser) int_opt(name string, abbr byte, usage string) ?int { + mut res := 0 + { + fs.add_flag(name, abbr, usage, '') + parsed := fs.parse_value(name, abbr) + if parsed.len == 0 { + return error("parameter '$name' not provided") + } + parsed0 := parsed[0] + res = parsed0.int() + } + return res +} + +// defining and parsing an int flag +// if defined +// the value is returned (int) +// else +// the default value is returned +// version with abbr +// TODO error handling for invalid string to int conversion +pub fn (mut fs FlagParser) int(name string, abbr byte, idefault int, usage string) int { + value := fs.int_opt(name, abbr, usage) or { return idefault } + return value +} + +// float_multi returns all instances of values associated with the flags provided +// In the case that none were found, it returns an empty array. +pub fn (mut fs FlagParser) float_multi(name string, abbr byte, usage string) []f64 { + fs.add_flag(name, abbr, usage, '') + parsed := fs.parse_value(name, abbr) + mut value := []f64{} + for val in parsed { + value << val.f64() + } + return value +} + +// float_opt returns an optional that returns the value associated with the flag. +// In the situation that the flag was not provided, it returns null. +pub fn (mut fs FlagParser) float_opt(name string, abbr byte, usage string) ?f64 { + mut res := 0.0 + { + fs.add_flag(name, abbr, usage, '') + parsed := fs.parse_value(name, abbr) + if parsed.len == 0 { + return error("parameter '$name' not provided") + } + res = parsed[0].f64() + } + return res +} + +// defining and parsing a float flag +// if defined +// the value is returned (float) +// else +// the default value is returned +// version with abbr +// TODO error handling for invalid string to float conversion +pub fn (mut fs FlagParser) float(name string, abbr byte, fdefault f64, usage string) f64 { + value := fs.float_opt(name, abbr, usage) or { return fdefault } + return value +} + +// string_multi returns all instances of values associated with the flags provided +// In the case that none were found, it returns an empty array. +pub fn (mut fs FlagParser) string_multi(name string, abbr byte, usage string) []string { + fs.add_flag(name, abbr, usage, '') + return fs.parse_value(name, abbr) +} + +// string_opt returns an optional that returns the value associated with the flag. +// In the situation that the flag was not provided, it returns null. +pub fn (mut fs FlagParser) string_opt(name string, abbr byte, usage string) ?string { + mut res := '' + { + fs.add_flag(name, abbr, usage, '') + parsed := fs.parse_value(name, abbr) + if parsed.len == 0 { + return error("parameter '$name' not provided") + } + res = parsed[0] + } + return res +} + +// defining and parsing a string flag +// if defined +// the value is returned (string) +// else +// the default value is returned +// version with abbr +pub fn (mut fs FlagParser) string(name string, abbr byte, sdefault string, usage string) string { + value := fs.string_opt(name, abbr, usage) or { return sdefault } + return value +} + +pub fn (mut fs FlagParser) limit_free_args_to_at_least(n int) { + if n > flag.max_args_number { + panic('flag.limit_free_args_to_at_least expect n to be smaller than $flag.max_args_number') + } + if n <= 0 { + panic('flag.limit_free_args_to_at_least expect n to be a positive number') + } + fs.min_free_args = n +} + +pub fn (mut fs FlagParser) limit_free_args_to_exactly(n int) { + if n > flag.max_args_number { + panic('flag.limit_free_args_to_exactly expect n to be smaller than $flag.max_args_number') + } + if n < 0 { + panic('flag.limit_free_args_to_exactly expect n to be a non negative number') + } + fs.min_free_args = n + fs.max_free_args = n +} + +// this will cause an error in finalize() if free args are out of range +// (min, ..., max) +pub fn (mut fs FlagParser) limit_free_args(min int, max int) { + if min > max { + panic('flag.limit_free_args expect min < max, got $min >= $max') + } + fs.min_free_args = min + fs.max_free_args = max +} + +pub fn (mut fs FlagParser) arguments_description(description string) { + fs.args_description = description +} + +// collect all given information and +pub fn (fs FlagParser) usage() string { + positive_min_arg := (fs.min_free_args > 0) + positive_max_arg := (fs.max_free_args > 0 && fs.max_free_args != flag.max_args_number) + no_arguments := (fs.min_free_args == 0 && fs.max_free_args == 0) + mut adesc := if fs.args_description.len > 0 { fs.args_description } else { '[ARGS]' } + if no_arguments { + adesc = '' + } + mut use := []string{} + if fs.application_version != '' { + use << '$fs.application_name $fs.application_version' + use << '$flag.underline' + } + if fs.usage_examples.len == 0 { + use << 'Usage: $fs.application_name [options] $adesc' + } else { + for i, example in fs.usage_examples { + if i == 0 { + use << 'Usage: $fs.application_name $example' + } else { + use << ' or: $fs.application_name $example' + } + } + } + use << '' + if fs.application_description != '' { + use << 'Description: $fs.application_description' + use << '' + } + // show a message about the [ARGS]: + if positive_min_arg || positive_max_arg || no_arguments { + if no_arguments { + use << 'This application does not expect any arguments' + use << '' + } else { + mut s := []string{} + if positive_min_arg { + s << 'at least $fs.min_free_args' + } + if positive_max_arg { + s << 'at most $fs.max_free_args' + } + if positive_min_arg && positive_max_arg && fs.min_free_args == fs.max_free_args { + s = ['exactly $fs.min_free_args'] + } + sargs := s.join(' and ') + use << 'The arguments should be $sargs in number.' + use << '' + } + } + if fs.flags.len > 0 { + use << 'Options:' + for f in fs.flags { + mut onames := []string{} + if f.abbr != 0 { + onames << '-$f.abbr.ascii_str()' + } + if f.name != '' { + if !f.val_desc.contains('') { + onames << '--$f.name $f.val_desc' + } else { + onames << '--$f.name' + } + } + option_names := ' ' + onames.join(', ') + mut xspace := '' + if option_names.len > flag.space.len - 2 { + xspace = '\n$flag.space' + } else { + xspace = flag.space[option_names.len..] + } + fdesc := '$option_names$xspace$f.usage' + use << fdesc + } + } + for footer in fs.footers { + use << footer + } + return use.join('\n').replace('- ,', ' ') +} + +fn (mut fs FlagParser) find_existing_flag(fname string) ?Flag { + for f in fs.flags { + if f.name == fname { + return f + } + } + return error('no such flag') +} + +fn (mut fs FlagParser) handle_builtin_options() { + mut show_version := false + mut show_help := false + fs.find_existing_flag('help') or { + show_help = fs.bool('help', `h`, false, fs.default_help_label) + } + fs.find_existing_flag('version') or { + show_version = fs.bool('version', 0, false, fs.default_version_label) + } + if show_help { + println(fs.usage()) + exit(0) + } + if show_version { + println('$fs.application_name $fs.application_version') + exit(0) + } +} + +// finalize - return all remaining arguments (non options). +// Call .finalize() after all arguments are defined. +// The remaining arguments are returned in the same order they are +// defined on the command line. If additional flags are found, i.e. +// (things starting with '--' or '-'), it returns an error. +pub fn (mut fs FlagParser) finalize() ?[]string { + fs.handle_builtin_options() + mut remaining := fs.args.clone() + if !fs.allow_unknown_args { + for a in remaining { + if (a.len >= 2 && a[..2] == '--') || (a.len == 2 && a[0] == `-`) { + return IError(&UnkownFlagError{ + msg: 'Unknown flag `$a`' + }) + } + } + } + if remaining.len < fs.min_free_args && fs.min_free_args > 0 { + return IError(&MinimumArgsCountError{ + msg: 'Expected at least $fs.min_free_args arguments, but given $remaining.len' + }) + } + if remaining.len > fs.max_free_args && fs.max_free_args > 0 { + return IError(&MaximumArgsCountError{ + msg: 'Expected at most $fs.max_free_args arguments, but given $remaining.len' + }) + } + if remaining.len > 0 && fs.max_free_args == 0 && fs.min_free_args == 0 { + return IError(&NoArgsExpectedError{ + msg: 'Expected no arguments, but given $remaining.len' + }) + } + remaining << fs.all_after_dashdash + return remaining +} + +// remaining_parameters will return all remaining parameters. +// Call .remaining_parameters() *AFTER* you have defined all options +// that your program needs. remaining_parameters will also print any +// parsing errors and stop the program. Use .finalize() instead, if +// you want more control over the error handling. +pub fn (mut fs FlagParser) remaining_parameters() []string { + return fs.finalize() or { + eprintln(err.msg) + println(fs.usage()) + exit(1) + } +} diff --git a/v_windows/v/old/vlib/flag/flag_test.v b/v_windows/v/old/vlib/flag/flag_test.v new file mode 100644 index 0000000..8326193 --- /dev/null +++ b/v_windows/v/old/vlib/flag/flag_test.v @@ -0,0 +1,412 @@ +import flag + +fn test_if_flag_not_given_return_default_values() { + mut fp := flag.new_flag_parser([]) + assert false == fp.bool('a_bool', 0, false, '') + assert 42 == fp.int('an_int', 0, 42, '') + assert 1.0 == fp.float('a_float', 0, 1.0, '') + assert 'stuff' == fp.string('a_string', 0, 'stuff', '') +} + +fn test_could_define_application_name_and_version() { + mut fp := flag.new_flag_parser([]) + fp.application('test app') + fp.version('0.0.42') + fp.description('some text') + assert fp.application_name == 'test app' + assert fp.application_version == '0.0.42' + assert fp.application_description == 'some text' +} + +fn test_bool_flags_do_not_need_an_value() { + mut fp := flag.new_flag_parser(['--a_bool']) + assert true == fp.bool('a_bool', 0, false, '') +} + +fn test_flags_could_be_defined_with_eq() { + mut fp := flag.new_flag_parser([ + '--an_int=42', + '--a_float=2.0', + '--bool_without', + '--a_string=stuff', + '--a_bool=true', + ]) + assert 42 == fp.int('an_int', 0, 0o666, '') + assert true == fp.bool('a_bool', 0, false, '') + assert true == fp.bool('bool_without', 0, false, '') + assert 2.0 == fp.float('a_float', 0, 1.0, '') + assert 'stuff' == fp.string('a_string', 0, 'not_stuff', '') +} + +fn test_values_could_be_defined_without_eq() { + mut fp := flag.new_flag_parser([ + '--an_int', + '42', + '--a_float', + '2.0', + '--bool_without', + '--a_string', + 'stuff', + '--a_bool', + 'true', + ]) + assert 42 == fp.int('an_int', 0, 0o666, '') + assert true == fp.bool('a_bool', 0, false, '') + assert true == fp.bool('bool_without', 0, false, '') + assert 2.0 == fp.float('a_float', 0, 1.0, '') + assert 'stuff' == fp.string('a_string', 0, 'not_stuff', '') +} + +fn test_values_could_be_defined_mixed() { + mut fp := flag.new_flag_parser([ + '--an_int', + '42', + '--a_float=2.0', + '--bool_without', + '--a_string', + 'stuff', + '--a_bool=true', + ]) + assert 42 == fp.int('an_int', 0, 0o666, '') + assert true == fp.bool('a_bool', 0, false, '') + assert true == fp.bool('bool_without', 0, false, '') + assert 2.0 == fp.float('a_float', 0, 1.0, '') + assert 'stuff' == fp.string('a_string', 0, 'not_stuff', '') +} + +fn test_beaware_for_argument_names_with_same_prefix() { + mut fp := flag.new_flag_parser([ + '--short', + '5', + '--shorter=7', + ]) + assert 5 == fp.int('short', 0, 0o666, '') + assert 7 == fp.int('shorter', 0, 0o666, '') +} + +fn test_beaware_for_argument_names_with_same_prefix_inverse() { + mut fp := flag.new_flag_parser([ + '--shorter=7', + '--short', + '5', + ]) + assert 5 == fp.int('short', 0, 0o666, '') + assert 7 == fp.int('shorter', 0, 0o666, '') +} + +fn test_allow_to_skip_executable_path() { + mut fp := flag.new_flag_parser(['./path/to/execuable']) + fp.skip_executable() + args := fp.finalize() or { + assert false + return + } + assert !args.contains('./path/to/execuable') +} + +fn test_none_flag_arguments_are_allowed() { + mut fp := flag.new_flag_parser([ + 'file1', + '--an_int=2', + 'file2', + 'file3', + '--bool_without', + 'file4', + '--outfile', + 'outfile', + ]) + assert 2 == fp.int('an_int', 0, 0o666, '') + assert 'outfile' == fp.string('outfile', 0, 'bad', '') + assert true == fp.bool('bool_without', 0, false, '') +} + +fn test_finalize_returns_none_flag_arguments_ordered() { + mut fp := flag.new_flag_parser(['d', 'b', 'x', 'a', '--outfile', 'outfile']) + fp.string('outfile', 0, 'bad', '') + finalized := fp.finalize() or { + assert false + return + } + expected := ['d', 'b', 'x', 'a'] + for i, v in finalized { + assert v == expected[i] + } +} + +fn test_finalize_returns_error_for_unknown_flags_long() { + mut fp := flag.new_flag_parser(['--known', '--unknown']) + fp.bool('known', 0, false, '') + finalized := fp.finalize() or { + assert err.msg == 'Unknown flag `--unknown`' + return + } + assert finalized.len < 0 // expect error to be returned +} + +fn test_finalize_returns_error_for_unknown_flags_short() { + mut fp := flag.new_flag_parser(['--known', '-x']) + fp.bool('known', 0, false, '') + finalized := fp.finalize() or { + assert err.msg == 'Unknown flag `-x`' + return + } + assert finalized.len < 0 // expect error to be returned +} + +fn test_allow_to_build_usage_message() { + mut fp := flag.new_flag_parser([]) + fp.limit_free_args(1, 4) + fp.application('flag_tool') + fp.version('v0.0.0') + fp.description('some short information about this tool') + fp.int('an_int', 0, 0o666, 'some int to define') + fp.bool('a_bool', 0, false, 'some bool to define') + fp.bool('bool_without_but_really_big', 0, false, 'this should appear on the next line') + fp.float('a_float', 0, 1.0, 'some float as well') + fp.string('a_string', 0, 'not_stuff', 'your credit card number') + usage := fp.usage() + mut all_strings_found := true + for s in ['flag_tool', 'v0.0.0', 'an_int ', 'a_bool', 'bool_without', 'a_float ', + 'a_string ', 'some int to define', 'some bool to define', + 'this should appear on the next line', 'some float as well', 'your credit card number', + 'The arguments should be at least 1 and at most 4 in number.', 'Usage', 'Options:', + 'Description:', 'some short information about this tool'] { + if !usage.contains(s) { + eprintln(" missing '$s' in usage message") + all_strings_found = false + } + } + assert all_strings_found +} + +fn test_if_no_description_given_usage_message_does_not_contain_descpription() { + mut fp := flag.new_flag_parser([]) + fp.application('flag_tool') + fp.version('v0.0.0') + fp.bool('a_bool', 0, false, '') + assert !fp.usage().contains('Description:') +} + +fn test_if_no_options_given_usage_message_does_not_contain_options() { + mut fp := flag.new_flag_parser([]) + fp.application('flag_tool') + fp.version('v0.0.0') + assert !fp.usage().contains('Options:') +} + +fn test_free_args_could_be_limited() { + mut fp1 := flag.new_flag_parser(['a', 'b', 'c']) + fp1.limit_free_args(1, 4) + args := fp1.finalize() or { + assert false + return + } + assert args[0] == 'a' + assert args[1] == 'b' + assert args[2] == 'c' +} + +fn test_error_for_to_few_free_args() { + mut fp1 := flag.new_flag_parser(['a', 'b', 'c']) + fp1.limit_free_args(5, 6) + args := fp1.finalize() or { + assert err.msg.starts_with('Expected at least 5 arguments') + return + } + assert args.len < 0 // expect an error and need to use args +} + +fn test_error_for_to_much_free_args() { + mut fp1 := flag.new_flag_parser(['a', 'b', 'c']) + fp1.limit_free_args(1, 2) + args := fp1.finalize() or { + assert err.msg.starts_with('Expected at most 2 arguments') + return + } + assert args.len < 0 // expect an error and need to use args +} + +fn test_could_expect_no_free_args() { + mut fp1 := flag.new_flag_parser(['a']) + fp1.limit_free_args(0, 0) + args := fp1.finalize() or { + assert err.msg.starts_with('Expected no arguments') + return + } + assert args.len < 0 // expect an error and need to use args +} + +fn test_allow_abreviations() { + mut fp := flag.new_flag_parser(['-v', '-o', 'some_file', '-i', '42', '-f', '2.0']) + v := fp.bool('version', `v`, false, '') + o := fp.string('output', `o`, 'empty', '') + i := fp.int('count', `i`, 0, '') + f := fp.float('value', `f`, 0.0, '') + assert v == true + assert o == 'some_file' + assert i == 42 + assert f == 2.0 + u := fp.usage() + assert u.contains(' -v') + assert u.contains(' -o') + assert u.contains(' -i') + assert u.contains(' -f') + assert u.contains(' -o, --output ') + assert u.contains(' -i, --count ') + assert u.contains(' -f, --value ') +} + +fn test_allow_kebab_options() { + default_value := 'this_is_the_default_value_of_long_option' + long_option_value := 'this_is_a_long_option_value_as_argument' + mut fp := flag.new_flag_parser(['--my-long-flag', 'true', '--my-long-option', long_option_value]) + my_flag := fp.bool('my-long-flag', 0, false, 'flag with long-kebab-name') + my_option := fp.string('my-long-option', 0, default_value, 'string with long-kebab-name') + assert my_flag == true + assert my_option == long_option_value + u := fp.usage() + assert u.contains(' --my-long-flag') + assert u.contains(' --my-long-option') +} + +fn test_not_provided_option_is_not_returned() { + mut fp := flag.new_flag_parser([]) + fp.bool_opt('some-flag', `a`, '') or { + fp.int_opt('some-flag', `a`, '') or { + fp.float_opt('some-flag', `a`, '') or { + fp.string_opt('some-flag', `a`, '') or { + // Everything should not return + return + } + return + } + return + } + return + } + // If we reach here, one of them returned a value. + assert false +} + +fn test_provided_option_is_returned() { + mut fp := flag.new_flag_parser(['-a', '-b', '3', '-c', 'hello', '-d', '3.14']) + a := fp.bool_opt('some-flag', `a`, '') or { panic('bool_opt did not return a bool') } + b := fp.int_opt('some-flag', `b`, '') or { panic('int_opt did not return an int') } + c := fp.string_opt('some-flag', `c`, '') or { panic('string_opt did not return a string') } + d := fp.float_opt('some-flag', `d`, '') or { panic('float_opt did not return a float') } + assert true == a + assert b == 3 + assert c == 'hello' + assert d == 3.14 +} + +fn test_multiple_arguments() { + mut fp := flag.new_flag_parser([ + '-a', + '2', + '-a', + '3', + '-a', + '5', + '-b', + 'a', + '-b', + 'c', + '-b', + 'b', + '-c', + '1.23', + '-c', + '2.34', + '-c', + '3.45', + ]) + // TODO Move to array comparison once it's implemented + // assert fp.int_multi('some-flag', `a`, '') == [2, 3, 5] && + // fp.string_multi('some-flag', `b`, '') == ['a', 'c', 'b'] && + // fp.float_multi('some-flag', `c`, '') == [1.23, 2.34, 3.45] + a := fp.int_multi('some-flag', `a`, '') + b := fp.string_multi('some-flag', `b`, '') + c := fp.float_multi('some-flag', `c`, '') + assert a.len == 3 + assert b.len == 3 + assert c.len == 3 + assert a[0] == 2 + assert a[1] == 3 + assert a[2] == 5 + assert b[0] == 'a' + assert b[1] == 'c' + assert b[2] == 'b' + assert c[0] == 1.23 + assert c[1] == 2.34 + assert c[2] == 3.45 +} + +fn test_long_options_that_start_with_the_same_letter_as_another_short_option() { + mut fp := flag.new_flag_parser([ + '--vabc', + '/abc', + ]) + verbose := fp.bool('verbose', `v`, false, 'Be more verbose.') + vabc := fp.string('vabc', `x`, 'default', 'Another option that *may* conflict with v, but *should not*') + assert verbose == false + assert vabc == '/abc' +} + +fn test_long_options_that_start_with_the_same_letter_as_another_short_option_both_set() { + mut fp := flag.new_flag_parser([ + '-v', + '--vabc', + '/abc', + ]) + verbose := fp.bool('verbose', `v`, false, 'Be more verbose.') + vabc := fp.string('vabc', `x`, 'default', 'Another option that *may* conflict with v, but *should not*') + assert verbose == true + assert vabc == '/abc' +} + +fn test_single_dash() { + mut fp := flag.new_flag_parser([ + '-', + ]) + flag_update := fp.bool('update', `u`, false, 'Update tools') + assert flag_update == false +} + +fn test_optional_flags() { + mut fp := flag.new_flag_parser(['-a', '10', '-b']) + fp.int_opt('some-flag', `a`, '') or { + assert false + return + } + b := fp.string_opt('another-flag', `b`, '') or { 'some_default_value' } + assert b == 'some_default_value' +} + +fn test_dashdash_acts_as_parser_full_stop() ? { + mut fp := flag.new_flag_parser(['-b', '5', '--', '-d', '-x', '-b', '4', '-a', '-c', 'hello', + 'some', 'other', 'parameters']) + a := fp.bool_opt('a-bool-flag', `a`, '') or { false } + b := fp.int_opt('an-int-flag', `b`, '') or { -1 } + c := fp.string_opt('a-string-flag', `c`, '') or { 'default' } + assert a == false + assert b == 5 + assert c == 'default' + args := fp.finalize() ? + assert args.len > 0 + assert args[0] != '--' + assert args == ['-d', '-x', '-b', '4', '-a', '-c', 'hello', 'some', 'other', 'parameters'] +} + +fn test_dashdash_acts_as_parser_full_stop_dashdash_at_end() ? { + mut fp := flag.new_flag_parser(['-b', '5', '-b', '4', 'other', 'params', '--']) + b := fp.int_multi('an-int-flag', `b`, '') + assert b == [5, 4] + args := fp.finalize() ? + assert args.len > 0 +} + +fn test_empty_string_with_flag() { + mut fp := flag.new_flag_parser(['']) + s := fp.string('something', `s`, 'default', 'Hey parse me') +} diff --git a/v_windows/v/old/vlib/flag/testdata/simplest_flag_program.dashdash.help.out b/v_windows/v/old/vlib/flag/testdata/simplest_flag_program.dashdash.help.out new file mode 100644 index 0000000..2529e9f --- /dev/null +++ b/v_windows/v/old/vlib/flag/testdata/simplest_flag_program.dashdash.help.out @@ -0,0 +1 @@ +[vlib/flag/testdata/simplest_flag_program.v:13] rest_of_args: ['--help'] diff --git a/v_windows/v/old/vlib/flag/testdata/simplest_flag_program.dashdash.version.out b/v_windows/v/old/vlib/flag/testdata/simplest_flag_program.dashdash.version.out new file mode 100644 index 0000000..b0b4d65 --- /dev/null +++ b/v_windows/v/old/vlib/flag/testdata/simplest_flag_program.dashdash.version.out @@ -0,0 +1 @@ +[vlib/flag/testdata/simplest_flag_program.v:13] rest_of_args: ['--version'] diff --git a/v_windows/v/old/vlib/flag/testdata/simplest_flag_program.help.out b/v_windows/v/old/vlib/flag/testdata/simplest_flag_program.help.out new file mode 100644 index 0000000..9b5ab9e --- /dev/null +++ b/v_windows/v/old/vlib/flag/testdata/simplest_flag_program.help.out @@ -0,0 +1,7 @@ +abc 0.0.1 +----------------------------------------------- +Usage: abc [options] [ARGS] + +Options: + -h, --help display this help and exit + --version output version information and exit diff --git a/v_windows/v/old/vlib/flag/testdata/simplest_flag_program.out b/v_windows/v/old/vlib/flag/testdata/simplest_flag_program.out new file mode 100644 index 0000000..02b7cea --- /dev/null +++ b/v_windows/v/old/vlib/flag/testdata/simplest_flag_program.out @@ -0,0 +1 @@ +[vlib/flag/testdata/simplest_flag_program.v:13] rest_of_args: [] diff --git a/v_windows/v/old/vlib/flag/testdata/simplest_flag_program.v b/v_windows/v/old/vlib/flag/testdata/simplest_flag_program.v new file mode 100644 index 0000000..cee2ff7 --- /dev/null +++ b/v_windows/v/old/vlib/flag/testdata/simplest_flag_program.v @@ -0,0 +1,14 @@ +import os +import flag + +fn main() { + mut fp := flag.new_flag_parser(os.args) + fp.application('abc') + fp.version('0.0.1') + fp.skip_executable() + rest_of_args := fp.finalize() or { + eprintln(err) + exit(1) + } + dump(rest_of_args) +} diff --git a/v_windows/v/old/vlib/flag/testdata/simplest_flag_program.version.out b/v_windows/v/old/vlib/flag/testdata/simplest_flag_program.version.out new file mode 100644 index 0000000..de52cb0 --- /dev/null +++ b/v_windows/v/old/vlib/flag/testdata/simplest_flag_program.version.out @@ -0,0 +1 @@ +abc 0.0.1 diff --git a/v_windows/v/old/vlib/flag/testdata/usage_example.help.out b/v_windows/v/old/vlib/flag/testdata/usage_example.help.out new file mode 100644 index 0000000..b40047b --- /dev/null +++ b/v_windows/v/old/vlib/flag/testdata/usage_example.help.out @@ -0,0 +1,13 @@ +xyz 0.0.2 +----------------------------------------------- +Usage: xyz [NUMBER]... + or: xyz OPTION + +Description: description line 1 +description line 2 + +Options: + -h, --help display this help and exit + --version output version information and exit +footer 1 +footer 2 diff --git a/v_windows/v/old/vlib/flag/testdata/usage_example.out b/v_windows/v/old/vlib/flag/testdata/usage_example.out new file mode 100644 index 0000000..b2fd000 --- /dev/null +++ b/v_windows/v/old/vlib/flag/testdata/usage_example.out @@ -0,0 +1 @@ +[vlib/flag/testdata/usage_example.v:16] rest_of_args: ['abc', 'def'] diff --git a/v_windows/v/old/vlib/flag/testdata/usage_example.v b/v_windows/v/old/vlib/flag/testdata/usage_example.v new file mode 100644 index 0000000..522b758 --- /dev/null +++ b/v_windows/v/old/vlib/flag/testdata/usage_example.v @@ -0,0 +1,17 @@ +import os +import flag + +fn main() { + mut fp := flag.new_flag_parser(os.args) + fp.skip_executable() + fp.application('xyz') + fp.version('0.0.2') + fp.usage_example('[NUMBER]...') + fp.usage_example('OPTION') + fp.description('description line 1') + fp.description('description line 2') + fp.footer('footer 1') + fp.footer('footer 2') + rest_of_args := fp.remaining_parameters() + dump(rest_of_args) +} diff --git a/v_windows/v/old/vlib/flag/testdata/usage_example.version.out b/v_windows/v/old/vlib/flag/testdata/usage_example.version.out new file mode 100644 index 0000000..9196894 --- /dev/null +++ b/v_windows/v/old/vlib/flag/testdata/usage_example.version.out @@ -0,0 +1 @@ +xyz 0.0.2 diff --git a/v_windows/v/old/vlib/flag/usage_example_test.v b/v_windows/v/old/vlib/flag/usage_example_test.v new file mode 100644 index 0000000..30fbccc --- /dev/null +++ b/v_windows/v/old/vlib/flag/usage_example_test.v @@ -0,0 +1,35 @@ +import os + +const the_source = 'vlib/flag/testdata/usage_example.v' + +const the_executable = os.real_path(os.join_path(os.cache_dir(), 'flag_usage_example_app.exe')) + +fn testsuite_begin() { + os.chdir(@VMODROOT) + os.rm(the_executable) or {} + res := os.execute('${@VEXE} -o $the_executable $the_source') + assert res.exit_code == 0 + assert os.execute(the_executable).exit_code == 0 + C.atexit(fn () { + os.rm(the_executable) or {} + }) +} + +fn normalise_lines(lines []string) string { + return '\n' + lines.join('\n') +} + +fn check_program(opts string, extension string) { + result := the_source.replace('.v', extension) + res := os.execute('$the_executable $opts') + assert res.exit_code == 0 + assert normalise_lines(res.output.split_into_lines()) == normalise_lines(os.read_lines(result) or { + panic(err) + }) +} + +fn test_normal_usage() { + check_program('abc def', '.out') + check_program(' --help', '.help.out') + check_program(' --version', '.version.out') +} diff --git a/v_windows/v/old/vlib/fontstash/a_d_use_freetype.v b/v_windows/v/old/vlib/fontstash/a_d_use_freetype.v new file mode 100644 index 0000000..199fe31 --- /dev/null +++ b/v_windows/v/old/vlib/fontstash/a_d_use_freetype.v @@ -0,0 +1,19 @@ +module fontstash + +#define FONS_USE_FREETYPE 1 +#flag windows -I @VEXEROOT/thirdparty/freetype/include +#flag windows -L @VEXEROOT/thirdparty/freetype/win64 +#flag linux -I/usr/include/freetype2 +#flag darwin -I/usr/local/include/freetype2 +// brew on m1 +#flag darwin -I/opt/homebrew/include/freetype2 +#flag darwin -L/opt/homebrew/lib +// MacPorts +#flag darwin -I/opt/local/include/freetype2 +#flag darwin -L/opt/local/lib +#flag freebsd -I/usr/local/include/freetype2 +#flag freebsd -Wl -L/usr/local/lib +#flag windows -lfreetype +#flag linux -lfreetype +#flag darwin -lfreetype +#flag darwin -lpng -lbz2 -lz diff --git a/v_windows/v/old/vlib/fontstash/fontstash.v b/v_windows/v/old/vlib/fontstash/fontstash.v new file mode 100644 index 0000000..6149a01 --- /dev/null +++ b/v_windows/v/old/vlib/fontstash/fontstash.v @@ -0,0 +1,172 @@ +module fontstash + +#flag -I @VEXEROOT/thirdparty/fontstash +#define FONTSTASH_IMPLEMENTATION +$if gcboehm ? { + #define FONTSTASH_MALLOC GC_MALLOC + #define FONTSTASH_REALLOC GC_REALLOC + #define FONTSTASH_FREE GC_FREE +} +#include "fontstash.h" +#flag -I /usr/local/Cellar/freetype/2.10.2/include/freetype2 + +$if windows { + $if tinyc { + #flag @VEXEROOT/thirdparty/tcc/lib/openlibm.o + } +} $else { + #flag -lm +} + +//#flag -lfreetype +pub const ( + // TODO: fontstash.used_import is used to keep v from warning about unused imports + used_import = 1 +) + +// Contructor and destructor. +[inline] +pub fn create_internal(params &C.FONSparams) &C.FONScontext { + return C.fonsCreateInternal(params) +} + +[inline] +pub fn delete_internal(s &C.FONScontext) { + C.fonsDeleteInternal(s) +} + +[inline] +pub fn (s &C.FONScontext) set_error_callback(callback fn (voidptr, int, int), uptr voidptr) { + C.fonsSetErrorCallback(s, callback, uptr) +} + +// Returns current atlas size. +[inline] +pub fn (s &C.FONScontext) get_atlas_size(width &int, height &int) { + C.fonsGetAtlasSize(s, width, height) +} + +// Expands the atlas size. +[inline] +pub fn (s &C.FONScontext) expand_atlas(width int, height int) int { + return C.fonsExpandAtlas(s, width, height) +} + +// Resets the whole stash. +[inline] +pub fn (s &C.FONScontext) reset_atlas(width int, height int) int { + return C.fonsResetAtlas(s, width, height) +} + +// Add fonts +[inline] +pub fn (s &C.FONScontext) get_font_by_name(name &char) int { + return C.fonsGetFontByName(s, name) +} + +[inline] +pub fn (s &C.FONScontext) add_fallback_font(base int, fallback int) int { + return C.fonsAddFallbackFont(s, base, fallback) +} + +[inline] +pub fn (s &C.FONScontext) add_font_mem(name &char, data &byte, data_size int, free_data int) int { + return C.fonsAddFontMem(s, name, data, data_size, free_data) +} + +// State handling +[inline] +pub fn (s &C.FONScontext) push_state() { + C.fonsPushState(s) +} + +[inline] +pub fn (s &C.FONScontext) pop_state() { + C.fonsPopState(s) +} + +[inline] +pub fn (s &C.FONScontext) clear_state() { + C.fonsClearState(s) +} + +// State setting +[inline] +pub fn (s &C.FONScontext) set_size(size f32) { + C.fonsSetSize(s, size) +} + +[inline] +pub fn (s &C.FONScontext) set_color(color u32) { + C.fonsSetColor(s, color) +} + +[inline] +pub fn (s &C.FONScontext) set_spacing(spacing f32) { + C.fonsSetSpacing(s, spacing) +} + +[inline] +pub fn (s &C.FONScontext) set_blur(blur f32) { + C.fonsSetBlur(s, blur) +} + +[inline] +pub fn (s &C.FONScontext) set_align(align int) { + C.fonsSetAlign(s, align) +} + +[inline] +pub fn (s &C.FONScontext) set_font(font int) { + C.fonsSetFont(s, font) +} + +// Draw text +[inline] +pub fn (s &C.FONScontext) draw_text(x f32, y f32, str &char, end &char) f32 { + return C.fonsDrawText(s, x, y, str, end) +} + +// Measure text +[inline] +pub fn (s &C.FONScontext) text_bounds(x f32, y f32, str &char, end &char, bounds &f32) f32 { + return C.fonsTextBounds(s, x, y, str, end, bounds) +} + +[inline] +pub fn (s &C.FONScontext) line_bounds(y f32, miny &f32, maxy &f32) { + C.fonsLineBounds(s, y, miny, maxy) +} + +[inline] +pub fn (s &C.FONScontext) vert_metrics(ascender &f32, descender &f32, lineh &f32) { + C.fonsVertMetrics(s, ascender, descender, lineh) +} + +// Text iterator +[inline] +pub fn (s &C.FONScontext) text_iter_init(iter &C.FONStextIter, x f32, y f32, str &char, end &char) int { + return C.fonsTextIterInit(s, iter, x, y, str, end) +} + +[inline] +pub fn (s &C.FONScontext) text_iter_next(iter &C.FONStextIter, quad &C.FONSquad) int { + return C.fonsTextIterNext(s, iter, quad) +} + +// Pull texture changes +[inline] +pub fn (s &C.FONScontext) get_texture_data(width &int, height &int) &byte { + return &byte(C.fonsGetTextureData(s, width, height)) +} + +[inline] +pub fn (s &C.FONScontext) validate_texture(dirty &int) int { + return C.fonsValidateTexture(s, dirty) +} + +// Draws the stash texture for debugging +[inline] +pub fn (s &C.FONScontext) draw_debug(x f32, y f32) { + C.fonsDrawDebug(s, x, y) +} diff --git a/v_windows/v/old/vlib/fontstash/fontstash_funcs.v b/v_windows/v/old/vlib/fontstash/fontstash_funcs.v new file mode 100644 index 0000000..8e260e7 --- /dev/null +++ b/v_windows/v/old/vlib/fontstash/fontstash_funcs.v @@ -0,0 +1,53 @@ +module fontstash + +// Contructor and destructor. +fn C.fonsCreateInternal(params &C.FONSparams) &C.FONScontext +fn C.fonsDeleteInternal(s &C.FONScontext) + +fn C.fonsSetErrorCallback(s &C.FONScontext, callback fn (voidptr, int, int), uptr voidptr) + +// Returns current atlas size. +fn C.fonsGetAtlasSize(s &C.FONScontext, width &int, height &int) + +// Expands the atlas size. +fn C.fonsExpandAtlas(s &C.FONScontext, width int, height int) int + +// Resets the whole stash. +fn C.fonsResetAtlas(s &C.FONScontext, width int, height int) int + +// Add fonts +fn C.fonsGetFontByName(s &C.FONScontext, name &char) int +fn C.fonsAddFallbackFont(s &C.FONScontext, base int, fallback int) int +fn C.fonsAddFontMem(s &C.FONScontext, name &char, data &byte, dataSize int, freeData int) int + +// State handling +fn C.fonsPushState(s &C.FONScontext) +fn C.fonsPopState(s &C.FONScontext) +fn C.fonsClearState(s &C.FONScontext) + +// State setting +fn C.fonsSetSize(s &C.FONScontext, size f32) +fn C.fonsSetColor(s &C.FONScontext, color u32) +fn C.fonsSetSpacing(s &C.FONScontext, spacing f32) +fn C.fonsSetBlur(s &C.FONScontext, blur f32) +fn C.fonsSetAlign(s &C.FONScontext, align int) +fn C.fonsSetFont(s &C.FONScontext, font int) + +// Draw text +fn C.fonsDrawText(s &C.FONScontext, x f32, y f32, str &char, end &char) f32 + +// Measure text +fn C.fonsTextBounds(s &C.FONScontext, x f32, y f32, str &char, end &char, bounds &f32) f32 +fn C.fonsLineBounds(s &C.FONScontext, y f32, miny &f32, maxy &f32) +fn C.fonsVertMetrics(s &C.FONScontext, ascender &f32, descender &f32, lineh &f32) + +// Text iterator +fn C.fonsTextIterInit(s &C.FONScontext, iter &C.FONStextIter, x f32, y f32, str &char, end &char) int +fn C.fonsTextIterNext(s &C.FONScontext, iter &C.FONStextIter, quad &C.FONSquad) int + +// Pull texture changes +fn C.fonsGetTextureData(s &C.FONScontext, width &int, height &int) &char +fn C.fonsValidateTexture(s &C.FONScontext, dirty &int) int + +// Draws the stash texture for debugging +fn C.fonsDrawDebug(s &C.FONScontext, x f32, y f32) diff --git a/v_windows/v/old/vlib/fontstash/fontstash_structs.v b/v_windows/v/old/vlib/fontstash/fontstash_structs.v new file mode 100644 index 0000000..83daa52 --- /dev/null +++ b/v_windows/v/old/vlib/fontstash/fontstash_structs.v @@ -0,0 +1,80 @@ +module fontstash + +pub enum FonsFlags { + top_left = 1 + bottom_left = 2 +} + +pub enum FonsAlign { + // Horizontal align + left = 1 // Default + center = 2 + right = 4 + // Vertical align + top = 8 + middle = 16 + bottom = 32 + baseline = 64 // Default +} + +pub enum FonsErrorCode { + // Font atlas is full. + atlas_full = 1 + // Scratch memory used to render glyphs is full, requested size reported in 'val', you may need to bump up FONS_SCRATCH_BUF_SIZE. + scratch_full = 2 + // Calls to fonsPushState has created too large stack, if you need deep state stack bump up FONS_MAX_STATES. + states_overflow = 3 + // Trying to pop too many states fonsPopState(). + states_underflow = 4 +} + +pub struct C.FONSparams { + width int + height int + flags char + userPtr voidptr + // int (*renderCreate)(void* uptr, int width, int height) + renderCreate fn(uptr voidptr, width int, height int) int + // int (*renderResize)(void* uptr, int width, int height) + renderResize fn(uptr voidptr, width int, height int) int + // void (*renderUpdate)(void* uptr, int* rect, const unsigned char* data) + renderUpdate fn(uptr voidptr, rect &int, data byteptr) + // void (*renderDraw)(void* uptr, const float* verts, const float* tcoords, const unsigned int* colors, int nverts) + renderDraw fn(uptr voidptr, verts &f32, tcoords &f32, colors &u32, nverts int) + // void (*renderDelete)(void* uptr) + renderDelete fn(uptr voidptr) +} + +pub struct C.FONSquad +{ + x0 f32 + y0 f32 + s0 f32 + t0 f32 + x1 f32 + y1 f32 + s1 f32 + t1 f32 +} + +pub struct C.FONStextIter { + x f32 + y f32 + nextx f32 + nexty f32 + scale f32 + spacing f32 + codepoint u32 + isize i16 + iblur i16 + font &C.FONSfont + prevGlyphIndex int + str byteptr + next byteptr + end byteptr + utf8state u32 +} + +pub struct C.FONSfont {} + +pub struct C.FONScontext {} diff --git a/v_windows/v/old/vlib/gg/enums.v b/v_windows/v/old/vlib/gg/enums.v new file mode 100644 index 0000000..c87f986 --- /dev/null +++ b/v_windows/v/old/vlib/gg/enums.v @@ -0,0 +1,161 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license that can be found in the LICENSE file. +module gg + +pub enum MouseButton { + left = 0 + right = 1 + middle = 2 + invalid = 256 +} + +// NB: unlike the MouseButton enum from above, +// the [flag]-ed enum here can have combined states, +// representing several pressed buttons at once. + +[flag] +pub enum MouseButtons { + left + right + middle +} + +[flag] +pub enum Modifier { + shift // (1<<0) + ctrl // (1<<1) + alt // (1<<2) + super // (1<<3) +} + +pub enum PenLineType { + solid + dashed + dotted +} + +pub enum KeyCode { + invalid = 0 + space = 32 + apostrophe = 39 //' + comma = 44 //, + minus = 45 //- + period = 46 //. + slash = 47 /// + _0 = 48 + _1 = 49 + _2 = 50 + _3 = 51 + _4 = 52 + _5 = 53 + _6 = 54 + _7 = 55 + _8 = 56 + _9 = 57 + semicolon = 59 //; + equal = 61 //= + a = 65 + b = 66 + c = 67 + d = 68 + e = 69 + f = 70 + g = 71 + h = 72 + i = 73 + j = 74 + k = 75 + l = 76 + m = 77 + n = 78 + o = 79 + p = 80 + q = 81 + r = 82 + s = 83 + t = 84 + u = 85 + v = 86 + w = 87 + x = 88 + y = 89 + z = 90 + left_bracket = 91 //[ + backslash = 92 //\ + right_bracket = 93 //] + grave_accent = 96 //` + world_1 = 161 // non-us #1 + world_2 = 162 // non-us #2 + escape = 256 + enter = 257 + tab = 258 + backspace = 259 + insert = 260 + delete = 261 + right = 262 + left = 263 + down = 264 + up = 265 + page_up = 266 + page_down = 267 + home = 268 + end = 269 + caps_lock = 280 + scroll_lock = 281 + num_lock = 282 + print_screen = 283 + pause = 284 + f1 = 290 + f2 = 291 + f3 = 292 + f4 = 293 + f5 = 294 + f6 = 295 + f7 = 296 + f8 = 297 + f9 = 298 + f10 = 299 + f11 = 300 + f12 = 301 + f13 = 302 + f14 = 303 + f15 = 304 + f16 = 305 + f17 = 306 + f18 = 307 + f19 = 308 + f20 = 309 + f21 = 310 + f22 = 311 + f23 = 312 + f24 = 313 + f25 = 314 + kp_0 = 320 + kp_1 = 321 + kp_2 = 322 + kp_3 = 323 + kp_4 = 324 + kp_5 = 325 + kp_6 = 326 + kp_7 = 327 + kp_8 = 328 + kp_9 = 329 + kp_decimal = 330 + kp_divide = 331 + kp_multiply = 332 + kp_subtract = 333 + kp_add = 334 + kp_enter = 335 + kp_equal = 336 + left_shift = 340 + left_control = 341 + left_alt = 342 + left_super = 343 + right_shift = 344 + right_control = 345 + right_alt = 346 + right_super = 347 + menu = 348 +} + +const key_code_max = 512 diff --git a/v_windows/v/old/vlib/gg/gg.v b/v_windows/v/old/vlib/gg/gg.v new file mode 100644 index 0000000..3b882da --- /dev/null +++ b/v_windows/v/old/vlib/gg/gg.v @@ -0,0 +1,946 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license that can be found in the LICENSE file. + +module gg + +import os +import gx +import sokol +import sokol.sapp +import sokol.sgl +import sokol.gfx +import math + +pub type FNCb = fn (data voidptr) + +pub type FNEvent = fn (e &Event, data voidptr) + +pub type FNFail = fn (msg string, data voidptr) + +pub type FNKeyDown = fn (c KeyCode, m Modifier, data voidptr) + +pub type FNKeyUp = fn (c KeyCode, m Modifier, data voidptr) + +pub type FNMove = fn (x f32, y f32, data voidptr) + +pub type FNClick = fn (x f32, y f32, button MouseButton, data voidptr) + +pub type FNUnClick = fn (x f32, y f32, button MouseButton, data voidptr) + +pub type FNChar = fn (c u32, data voidptr) + +pub struct Event { +pub mut: + frame_count u64 + typ sapp.EventType + key_code KeyCode + char_code u32 + key_repeat bool + modifiers u32 + mouse_button MouseButton + mouse_x f32 + mouse_y f32 + mouse_dx f32 + mouse_dy f32 + scroll_x f32 + scroll_y f32 + num_touches int + touches [8]C.sapp_touchpoint + window_width int + window_height int + framebuffer_width int + framebuffer_height int +} + +pub struct Config { +pub: + width int + height int + use_ortho bool // unused, still here just for backwards compatibility + retina bool + resizable bool + user_data voidptr + font_size int + create_window bool + // window_user_ptr voidptr + window_title string + borderless_window bool + always_on_top bool + bg_color gx.Color + init_fn FNCb = voidptr(0) + frame_fn FNCb = voidptr(0) + native_frame_fn FNCb = voidptr(0) + cleanup_fn FNCb = voidptr(0) + fail_fn FNFail = voidptr(0) + // + event_fn FNEvent = voidptr(0) + quit_fn FNEvent = voidptr(0) + // + keydown_fn FNKeyDown = voidptr(0) + keyup_fn FNKeyUp = voidptr(0) + char_fn FNChar = voidptr(0) + // + move_fn FNMove = voidptr(0) + click_fn FNClick = voidptr(0) + unclick_fn FNUnClick = voidptr(0) + leave_fn FNEvent = voidptr(0) + enter_fn FNEvent = voidptr(0) + resized_fn FNEvent = voidptr(0) + scroll_fn FNEvent = voidptr(0) + // wait_events bool // set this to true for UIs, to save power + fullscreen bool + scale f32 = 1.0 + sample_count int + // ved needs this + // init_text bool + font_path string + custom_bold_font_path string + ui_mode bool // refreshes only on events to save CPU usage + // font bytes for embedding + font_bytes_normal []byte + font_bytes_bold []byte + font_bytes_mono []byte + font_bytes_italic []byte + native_rendering bool // Cocoa on macOS/iOS, GDI+ on Windows +} + +pub struct PenConfig { + color gx.Color + line_type PenLineType = .solid + thickness int = 1 +} + +[heap] +pub struct Context { +mut: + render_text bool = true + // a cache with all images created by the user. used for sokol image init and to save space + // (so that the user can store image ids, not entire Image objects) + image_cache []Image + needs_refresh bool = true + ticks int // for ui mode only +pub: + native_rendering bool +pub mut: + scale f32 = 1.0 + // will get set to 2.0 for retina, will remain 1.0 for normal + width int + height int + clear_pass C.sg_pass_action + window C.sapp_desc + timage_pip C.sgl_pipeline + config Config + ft &FT + font_inited bool + ui_mode bool // do not redraw everything 60 times/second, but only when the user requests + frame u64 // the current frame counted from the start of the application; always increasing + // + mbtn_mask byte + mouse_buttons MouseButtons // typed version of mbtn_mask; easier to use for user programs + mouse_pos_x int + mouse_pos_y int + mouse_dx int + mouse_dy int + scroll_x int + scroll_y int + // + key_modifiers Modifier // the current key modifiers + key_repeat bool // whether the pressed key was an autorepeated one + pressed_keys [key_code_max]bool // an array representing all currently pressed keys + pressed_keys_edge [key_code_max]bool // true when the previous state of pressed_keys, + // *before* the current event was different +} + +pub struct Size { +pub: + width int + height int +} + +fn gg_init_sokol_window(user_data voidptr) { + mut g := unsafe { &Context(user_data) } + desc := sapp.create_desc() + /* + desc := C.sg_desc{ + mtl_device: sapp.metal_get_device() + mtl_renderpass_descriptor_cb: sapp.metal_get_renderpass_descriptor + mtl_drawable_cb: sapp.metal_get_drawable + d3d11_device: sapp.d3d11_get_device() + d3d11_device_context: sapp.d3d11_get_device_context() + d3d11_render_target_view_cb: sapp.d3d11_get_render_target_view + d3d11_depth_stencil_view_cb: sapp.d3d11_get_depth_stencil_view + } + */ + gfx.setup(&desc) + sgl_desc := C.sgl_desc_t{} + sgl.setup(&sgl_desc) + g.scale = dpi_scale() + // is_high_dpi := sapp.high_dpi() + // fb_w := sapp.width() + // fb_h := sapp.height() + // println('g.scale=$g.scale is_high_dpi=$is_high_dpi fb_w=$fb_w fb_h=$fb_h') + // if g.config.init_text { + // `os.is_file()` won't work on Android if the font file is embedded into the APK + exists := $if !android { os.is_file(g.config.font_path) } $else { true } + if g.config.font_path != '' && !exists { + g.render_text = false + } else if g.config.font_path != '' && exists { + // t := time.ticks() + g.ft = new_ft( + font_path: g.config.font_path + custom_bold_font_path: g.config.custom_bold_font_path + scale: dpi_scale() + ) or { panic(err) } + // println('FT took ${time.ticks()-t} ms') + g.font_inited = true + } else { + if g.config.font_bytes_normal.len > 0 { + g.ft = new_ft( + bytes_normal: g.config.font_bytes_normal + bytes_bold: g.config.font_bytes_bold + bytes_mono: g.config.font_bytes_mono + bytes_italic: g.config.font_bytes_italic + scale: sapp.dpi_scale() + ) or { panic(err) } + g.font_inited = true + } else { + sfont := system_font_path() + if g.config.font_path != '' { + eprintln('font file "$g.config.font_path" does not exist, the system font ($sfont) was used instead.') + } + + g.ft = new_ft( + font_path: sfont + custom_bold_font_path: g.config.custom_bold_font_path + scale: sapp.dpi_scale() + ) or { panic(err) } + g.font_inited = true + } + } + // + mut pipdesc := C.sg_pipeline_desc{ + label: c'alpha_image' + } + unsafe { C.memset(&pipdesc, 0, sizeof(pipdesc)) } + + color_state := C.sg_color_state{ + blend: C.sg_blend_state{ + enabled: true + src_factor_rgb: gfx.BlendFactor(C.SG_BLENDFACTOR_SRC_ALPHA) + dst_factor_rgb: gfx.BlendFactor(C.SG_BLENDFACTOR_ONE_MINUS_SRC_ALPHA) + } + } + pipdesc.colors[0] = color_state + + g.timage_pip = sgl.make_pipeline(&pipdesc) + // + if g.config.init_fn != voidptr(0) { + g.config.init_fn(g.config.user_data) + } + // Create images now that we can do that after sg is inited + if g.native_rendering { + return + } + + for i in 0 .. g.image_cache.len { + if g.image_cache[i].simg.id == 0 { + g.image_cache[i].init_sokol_image() + } + } +} + +fn gg_frame_fn(user_data voidptr) { + mut ctx := unsafe { &Context(user_data) } + ctx.frame++ + if ctx.config.frame_fn == voidptr(0) { + return + } + if ctx.native_rendering { + // return + } + + if ctx.ui_mode && !ctx.needs_refresh { + // Draw 3 more frames after the "stop refresh" command + ctx.ticks++ + if ctx.ticks > 3 { + return + } + } + ctx.config.frame_fn(ctx.config.user_data) + ctx.needs_refresh = false +} + +pub fn (mut ctx Context) refresh_ui() { + ctx.needs_refresh = true + ctx.ticks = 0 +} + +fn gg_event_fn(ce &C.sapp_event, user_data voidptr) { + // e := unsafe { &sapp.Event(ce) } + mut e := unsafe { &Event(ce) } + mut g := unsafe { &Context(user_data) } + if g.ui_mode { + g.refresh_ui() + } + if e.typ == .mouse_down { + bitplace := int(e.mouse_button) + g.mbtn_mask |= byte(1 << bitplace) + g.mouse_buttons = MouseButtons(g.mbtn_mask) + } + if e.typ == .mouse_up { + bitplace := int(e.mouse_button) + g.mbtn_mask &= ~(byte(1 << bitplace)) + g.mouse_buttons = MouseButtons(g.mbtn_mask) + } + if e.typ == .mouse_move && e.mouse_button == .invalid { + if g.mbtn_mask & 0x01 > 0 { + e.mouse_button = .left + } + if g.mbtn_mask & 0x02 > 0 { + e.mouse_button = .right + } + if g.mbtn_mask & 0x04 > 0 { + e.mouse_button = .middle + } + } + g.mouse_pos_x = int(e.mouse_x / g.scale) + g.mouse_pos_y = int(e.mouse_y / g.scale) + g.mouse_dx = int(e.mouse_dx / g.scale) + g.mouse_dy = int(e.mouse_dy / g.scale) + g.scroll_x = int(e.scroll_x / g.scale) + g.scroll_y = int(e.scroll_y / g.scale) + g.key_modifiers = Modifier(e.modifiers) + g.key_repeat = e.key_repeat + if e.typ in [.key_down, .key_up] { + key_idx := int(e.key_code) % key_code_max + prev := g.pressed_keys[key_idx] + next := e.typ == .key_down + g.pressed_keys[key_idx] = next + g.pressed_keys_edge[key_idx] = prev != next + } + if g.config.event_fn != voidptr(0) { + g.config.event_fn(e, g.config.user_data) + } + match e.typ { + .mouse_move { + if g.config.move_fn != voidptr(0) { + g.config.move_fn(e.mouse_x / g.scale, e.mouse_y / g.scale, g.config.user_data) + } + } + .mouse_down { + if g.config.click_fn != voidptr(0) { + g.config.click_fn(e.mouse_x / g.scale, e.mouse_y / g.scale, e.mouse_button, + g.config.user_data) + } + } + .mouse_up { + if g.config.unclick_fn != voidptr(0) { + g.config.unclick_fn(e.mouse_x / g.scale, e.mouse_y / g.scale, e.mouse_button, + g.config.user_data) + } + } + .mouse_leave { + if g.config.leave_fn != voidptr(0) { + g.config.leave_fn(e, g.config.user_data) + } + } + .mouse_enter { + if g.config.enter_fn != voidptr(0) { + g.config.enter_fn(e, g.config.user_data) + } + } + .mouse_scroll { + if g.config.scroll_fn != voidptr(0) { + g.config.scroll_fn(e, g.config.user_data) + } + } + .key_down { + if g.config.keydown_fn != voidptr(0) { + g.config.keydown_fn(e.key_code, Modifier(e.modifiers), g.config.user_data) + } + } + .key_up { + if g.config.keyup_fn != voidptr(0) { + g.config.keyup_fn(e.key_code, Modifier(e.modifiers), g.config.user_data) + } + } + .char { + if g.config.char_fn != voidptr(0) { + g.config.char_fn(e.char_code, g.config.user_data) + } + } + .resized { + if g.config.resized_fn != voidptr(0) { + g.config.resized_fn(e, g.config.user_data) + } + } + .quit_requested { + if g.config.quit_fn != voidptr(0) { + g.config.quit_fn(e, g.config.user_data) + } + } + else { + // dump(e) + } + } +} + +fn gg_cleanup_fn(user_data voidptr) { + mut g := unsafe { &Context(user_data) } + if g.config.cleanup_fn != voidptr(0) { + g.config.cleanup_fn(g.config.user_data) + } +} + +fn gg_fail_fn(msg &char, user_data voidptr) { + mut g := unsafe { &Context(user_data) } + vmsg := unsafe { tos3(msg) } + if g.config.fail_fn != voidptr(0) { + g.config.fail_fn(vmsg, g.config.user_data) + } else { + eprintln('gg error: $vmsg') + } +} + +// +pub fn new_context(cfg Config) &Context { + mut g := &Context{ + width: cfg.width + height: cfg.height + config: cfg + ft: 0 + ui_mode: cfg.ui_mode + native_rendering: cfg.native_rendering + } + g.set_bg_color(cfg.bg_color) + // C.printf('new_context() %p\n', cfg.user_data) + window := C.sapp_desc{ + user_data: g + init_userdata_cb: gg_init_sokol_window + frame_userdata_cb: gg_frame_fn + event_userdata_cb: gg_event_fn + fail_userdata_cb: gg_fail_fn + cleanup_userdata_cb: gg_cleanup_fn + window_title: &char(cfg.window_title.str) + html5_canvas_name: &char(cfg.window_title.str) + width: cfg.width + height: cfg.height + sample_count: cfg.sample_count + high_dpi: true + fullscreen: cfg.fullscreen + __v_native_render: cfg.native_rendering + } + g.window = window + return g +} + +pub fn (gg &Context) run() { + sapp.run(&gg.window) +} + +// quit closes the context window and exits the event loop for it +pub fn (ctx &Context) quit() { + sapp.request_quit() // does not require ctx right now, but sokol multi-window might in the future +} + +pub fn (mut ctx Context) set_bg_color(c gx.Color) { + ctx.clear_pass = gfx.create_clear_pass(f32(c.r) / 255.0, f32(c.g) / 255.0, f32(c.b) / 255.0, + f32(c.a) / 255.0) +} + +// TODO: Fix alpha +pub fn (ctx &Context) draw_rect(x f32, y f32, w f32, h f32, c gx.Color) { + $if macos { + if ctx.native_rendering { + C.darwin_draw_rect(x, ctx.height - (y + h), w, h, c) + return + } + } + if c.a != 255 { + sgl.load_pipeline(ctx.timage_pip) + } + sgl.c4b(c.r, c.g, c.b, c.a) + sgl.begin_quads() + sgl.v2f(x * ctx.scale, y * ctx.scale) + sgl.v2f((x + w) * ctx.scale, y * ctx.scale) + sgl.v2f((x + w) * ctx.scale, (y + h) * ctx.scale) + sgl.v2f(x * ctx.scale, (y + h) * ctx.scale) + sgl.end() +} + +[inline] +pub fn (ctx &Context) draw_square(x f32, y f32, s f32, c gx.Color) { + ctx.draw_rect(x, y, s, s, c) +} + +[inline] +pub fn (ctx &Context) set_pixel(x f32, y f32, c gx.Color) { + ctx.draw_square(x, y, 1, c) +} + +pub fn (ctx &Context) draw_triangle(x f32, y f32, x2 f32, y2 f32, x3 f32, y3 f32, c gx.Color) { + if c.a != 255 { + sgl.load_pipeline(ctx.timage_pip) + } + sgl.c4b(c.r, c.g, c.b, c.a) + sgl.begin_quads() + sgl.v2f(x * ctx.scale, y * ctx.scale) + sgl.v2f(x2 * ctx.scale, y2 * ctx.scale) + sgl.v2f(x3 * ctx.scale, y3 * ctx.scale) + sgl.end() +} + +pub fn (ctx &Context) draw_empty_rect(x f32, y f32, w f32, h f32, c gx.Color) { + if c.a != 255 { + sgl.load_pipeline(ctx.timage_pip) + } + sgl.c4b(c.r, c.g, c.b, c.a) + sgl.begin_line_strip() + sgl.v2f(x * ctx.scale, y * ctx.scale) + sgl.v2f((x + w) * ctx.scale, y * ctx.scale) + sgl.v2f((x + w) * ctx.scale, (y + h) * ctx.scale) + sgl.v2f(x * ctx.scale, (y + h) * ctx.scale) + sgl.v2f(x * ctx.scale, y * ctx.scale) + sgl.end() +} + +[inline] +pub fn (ctx &Context) draw_empty_square(x f32, y f32, s f32, c gx.Color) { + ctx.draw_empty_rect(x, y, s, s, c) +} + +pub fn (ctx &Context) draw_circle_line(x f32, y f32, r int, segments int, c gx.Color) { + $if macos { + if ctx.native_rendering { + C.darwin_draw_circle(x - r + 1, ctx.height - (y + r + 3), r, c) + return + } + } + if c.a != 255 { + sgl.load_pipeline(ctx.timage_pip) + } + sgl.c4b(c.r, c.g, c.b, c.a) + nx := x * ctx.scale + ny := y * ctx.scale + nr := r * ctx.scale + mut theta := f32(0) + mut xx := f32(0) + mut yy := f32(0) + sgl.begin_line_strip() + for i := 0; i < segments + 1; i++ { + theta = 2.0 * f32(math.pi) * f32(i) / f32(segments) + xx = nr * math.cosf(theta) + yy = nr * math.sinf(theta) + sgl.v2f(xx + nx, yy + ny) + } + sgl.end() +} + +pub fn (ctx &Context) draw_circle(x f32, y f32, r f32, c gx.Color) { + ctx.draw_circle_with_segments(x, y, r, 10, c) +} + +pub fn (ctx &Context) draw_circle_with_segments(x f32, y f32, r f32, segments int, c gx.Color) { + if c.a != 255 { + sgl.load_pipeline(ctx.timage_pip) + } + sgl.c4b(c.r, c.g, c.b, c.a) + nx := x * ctx.scale + ny := y * ctx.scale + nr := r * ctx.scale + mut theta := f32(0) + mut xx := f32(0) + mut yy := f32(0) + sgl.begin_triangle_strip() + for i := 0; i < segments + 1; i++ { + theta = 2.0 * f32(math.pi) * f32(i) / f32(segments) + xx = nr * math.cosf(theta) + yy = nr * math.sinf(theta) + sgl.v2f(xx + nx, yy + ny) + sgl.v2f(nx, ny) + } + sgl.end() +} + +pub fn (ctx &Context) draw_arc_line(x f32, y f32, r int, start_angle f32, arc_angle f32, segments int, c gx.Color) { + if c.a != 255 { + sgl.load_pipeline(ctx.timage_pip) + } + sgl.c4b(c.r, c.g, c.b, c.a) + theta := f32(arc_angle / f32(segments)) + tan_factor := math.tanf(theta) + rad_factor := math.cosf(theta) + nx := x * ctx.scale + ny := y * ctx.scale + mut xx := f32(r * math.cosf(start_angle)) + mut yy := f32(r * math.sinf(start_angle)) + sgl.begin_line_strip() + for i := 0; i < segments + 1; i++ { + sgl.v2f(xx + nx, yy + ny) + tx := -yy + ty := xx + xx += tx * tan_factor + yy += ty * tan_factor + xx *= rad_factor + yy *= rad_factor + } + sgl.end() +} + +pub fn (ctx &Context) draw_arc(x f32, y f32, r int, start_angle f32, arc_angle f32, segments int, c gx.Color) { + if c.a != 255 { + sgl.load_pipeline(ctx.timage_pip) + } + sgl.c4b(c.r, c.g, c.b, c.a) + nx := x * ctx.scale + ny := y * ctx.scale + theta := f32(arc_angle / f32(segments)) + tan_factor := math.tanf(theta) + rad_factor := math.cosf(theta) + mut xx := f32(r * math.cosf(start_angle)) + mut yy := f32(r * math.sinf(start_angle)) + sgl.begin_triangle_strip() + for i := 0; i < segments + 1; i++ { + sgl.v2f(xx + nx, yy + ny) + sgl.v2f(nx, ny) + tx := -yy + ty := xx + xx += tx * tan_factor + yy += ty * tan_factor + xx *= rad_factor + yy *= rad_factor + } + sgl.end() +} + +pub fn (gg &Context) begin() { + if gg.render_text && gg.font_inited { + gg.ft.flush() + } + sgl.defaults() + sgl.matrix_mode_projection() + sgl.ortho(0.0, f32(sapp.width()), f32(sapp.height()), 0.0, -1.0, 1.0) +} + +pub fn (gg &Context) end() { + gfx.begin_default_pass(gg.clear_pass, sapp.width(), sapp.height()) + sgl.draw() + gfx.end_pass() + gfx.commit() + /* + if gg.config.wait_events { + // println('gg: waiting') + wait_events() + } + */ +} + +// resize the context's Window +pub fn (mut ctx Context) resize(width int, height int) { + ctx.width = width + ctx.height = height +} + +// draw_line draws a line between the points provided +pub fn (ctx &Context) draw_line(x f32, y f32, x2 f32, y2 f32, c gx.Color) { + $if macos { + if ctx.native_rendering { + // Make the line more clear on hi dpi screens: draw a rectangle + mut width := math.abs(x2 - x) + mut height := math.abs(y2 - y) + if width == 0 { + width = 1 + } else if height == 0 { + height = 1 + } + ctx.draw_rect(x, y, f32(width), f32(height), c) + return + } + } + if c.a != 255 { + sgl.load_pipeline(ctx.timage_pip) + } + sgl.c4b(c.r, c.g, c.b, c.a) + sgl.begin_line_strip() + sgl.v2f(x * ctx.scale, y * ctx.scale) + sgl.v2f(x2 * ctx.scale, y2 * ctx.scale) + sgl.end() +} + +// draw_line_with_config draws a line between the points provided with the PenConfig +pub fn (ctx &Context) draw_line_with_config(x f32, y f32, x2 f32, y2 f32, config PenConfig) { + if config.color.a != 255 { + sgl.load_pipeline(ctx.timage_pip) + } + + if config.thickness <= 0 { + return + } + + nx := x * ctx.scale + ny := y * ctx.scale + nx2 := x2 * ctx.scale + ny2 := y2 * ctx.scale + + dx := nx2 - nx + dy := ny2 - ny + length := math.sqrtf(math.powf(x2 - x, 2) + math.powf(y2 - y, 2)) + theta := f32(math.atan2(dy, dx)) + + sgl.push_matrix() + + sgl.translate(nx, ny, 0) + sgl.rotate(theta, 0, 0, 1) + sgl.translate(-nx, -ny, 0) + + if config.line_type == .solid { + ctx.draw_rect(x, y, length, config.thickness, config.color) + } else { + size := if config.line_type == .dotted { config.thickness } else { config.thickness * 3 } + space := if size == 1 { 2 } else { size } + + mut available := length + mut start_x := x + + for i := 0; available > 0; i++ { + if i % 2 == 0 { + ctx.draw_rect(start_x, y, size, config.thickness, config.color) + available -= size + start_x += size + continue + } + + available -= space + start_x += space + } + } + + sgl.pop_matrix() +} + +pub fn (ctx &Context) draw_rounded_rect(x f32, y f32, w f32, h f32, radius f32, color gx.Color) { + sgl.c4b(color.r, color.g, color.b, color.a) + sgl.begin_triangle_strip() + mut theta := f32(0) + mut xx := f32(0) + mut yy := f32(0) + r := radius * ctx.scale + nx := x * ctx.scale + ny := y * ctx.scale + width := w * ctx.scale + height := h * ctx.scale + segments := 2 * math.pi * r + segdiv := segments / 4 + rb := 0 + lb := int(rb + segdiv) + lt := int(lb + segdiv) + rt := int(lt + segdiv) + // left top + lx := nx + r + ly := ny + r + for i in lt .. rt { + theta = 2 * f32(math.pi) * f32(i) / segments + xx = r * math.cosf(theta) + yy = r * math.sinf(theta) + sgl.v2f(xx + lx, yy + ly) + sgl.v2f(lx, ly) + } + // right top + mut rx := nx + width - r + mut ry := ny + r + for i in rt .. int(segments) { + theta = 2 * f32(math.pi) * f32(i) / segments + xx = r * math.cosf(theta) + yy = r * math.sinf(theta) + sgl.v2f(xx + rx, yy + ry) + sgl.v2f(rx, ry) + } + // right bottom + mut rbx := rx + mut rby := ny + height - r + for i in rb .. lb { + theta = 2 * f32(math.pi) * f32(i) / segments + xx = r * math.cosf(theta) + yy = r * math.sinf(theta) + sgl.v2f(xx + rbx, yy + rby) + sgl.v2f(rbx, rby) + } + // left bottom + mut lbx := lx + mut lby := ny + height - r + for i in lb .. lt { + theta = 2 * f32(math.pi) * f32(i) / segments + xx = r * math.cosf(theta) + yy = r * math.sinf(theta) + sgl.v2f(xx + lbx, yy + lby) + sgl.v2f(lbx, lby) + } + sgl.v2f(lx + xx, ly) + sgl.v2f(lx, ly) + sgl.end() + sgl.begin_quads() + sgl.v2f(lx, ly) + sgl.v2f(rx, ry) + sgl.v2f(rbx, rby) + sgl.v2f(lbx, lby) + sgl.end() +} + +pub fn (ctx &Context) draw_empty_rounded_rect(x f32, y f32, w f32, h f32, radius f32, border_color gx.Color) { + mut theta := f32(0) + mut xx := f32(0) + mut yy := f32(0) + r := radius * ctx.scale + nx := x * ctx.scale + ny := y * ctx.scale + width := w * ctx.scale + height := h * ctx.scale + segments := 2 * math.pi * r + segdiv := segments / 4 + rb := 0 + lb := int(rb + segdiv) + lt := int(lb + segdiv) + rt := int(lt + segdiv) + sgl.c4b(border_color.r, border_color.g, border_color.b, border_color.a) + sgl.begin_line_strip() + // left top + lx := nx + r + ly := ny + r + for i in lt .. rt { + theta = 2 * f32(math.pi) * f32(i) / segments + xx = r * math.cosf(theta) + yy = r * math.sinf(theta) + sgl.v2f(xx + lx, yy + ly) + } + // right top + mut rx := nx + width - r + mut ry := ny + r + for i in rt .. int(segments) { + theta = 2 * f32(math.pi) * f32(i) / segments + xx = r * math.cosf(theta) + yy = r * math.sinf(theta) + sgl.v2f(xx + rx, yy + ry) + } + // right bottom + mut rbx := rx + mut rby := ny + height - r + for i in rb .. lb { + theta = 2 * f32(math.pi) * f32(i) / segments + xx = r * math.cosf(theta) + yy = r * math.sinf(theta) + sgl.v2f(xx + rbx, yy + rby) + } + // left bottom + mut lbx := lx + mut lby := ny + height - r + for i in lb .. lt { + theta = 2 * f32(math.pi) * f32(i) / segments + xx = r * math.cosf(theta) + yy = r * math.sinf(theta) + sgl.v2f(xx + lbx, yy + lby) + } + sgl.v2f(lx + xx, ly) + sgl.end() +} + +// draw_convex_poly draws a convex polygon, given an array of points, and a color. +// Note that the points must be given in clockwise order. +pub fn (ctx &Context) draw_convex_poly(points []f32, c gx.Color) { + assert points.len % 2 == 0 + len := points.len / 2 + assert len >= 3 + + if c.a != 255 { + sgl.load_pipeline(ctx.timage_pip) + } + sgl.c4b(c.r, c.g, c.b, c.a) + + sgl.begin_triangle_strip() + x0 := points[0] + y0 := points[1] + for i in 1 .. (len / 2 + 1) { + sgl.v2f(x0, y0) + sgl.v2f(points[i * 4 - 2], points[i * 4 - 1]) + sgl.v2f(points[i * 4], points[i * 4 + 1]) + } + + if len % 2 == 0 { + sgl.v2f(points[2 * len - 2], points[2 * len - 1]) + } + sgl.end() +} + +// draw_empty_poly - draws the borders of a polygon, given an array of points, and a color. +// Note that the points must be given in clockwise order. +pub fn (ctx &Context) draw_empty_poly(points []f32, c gx.Color) { + assert points.len % 2 == 0 + len := points.len / 2 + assert len >= 3 + + if c.a != 255 { + sgl.load_pipeline(ctx.timage_pip) + } + sgl.c4b(c.r, c.g, c.b, c.a) + + sgl.begin_line_strip() + for i in 0 .. len { + sgl.v2f(points[2 * i], points[2 * i + 1]) + } + sgl.v2f(points[0], points[1]) + sgl.end() +} + +pub fn screen_size() Size { + $if macos { + return C.gg_get_screen_size() + } + // TODO windows, linux, etc + return Size{} +} + +// window_size returns the `Size` of the active window +pub fn window_size() Size { + s := dpi_scale() + return Size{int(sapp.width() / s), int(sapp.height() / s)} +} + +// window_size_real_pixels returns the `Size` of the active window without scale +pub fn window_size_real_pixels() Size { + return Size{sapp.width(), sapp.height()} +} + +pub fn dpi_scale() f32 { + mut s := sapp.dpi_scale() + $if android { + s *= android_dpi_scale() + } + // NB: on older X11, `Xft.dpi` from ~/.Xresources, that sokol uses, + // may not be set which leads to sapp.dpi_scale reporting incorrectly 0.0 + if s < 0.1 { + s = 1. + } + return s +} + +pub fn high_dpi() bool { + return C.sapp_high_dpi() +} + +fn C.WaitMessage() + +/* +pub fn wait_events() { + unsafe { + $if macos { + #NSEvent *event = [NSApp nextEventMatchingMask:NSEventMaskAny + #untilDate:[NSDate distantFuture] + #inMode:NSDefaultRunLoopMode + #dequeue:YES]; + #[NSApp sendEvent:event]; + } + $if windows { + C.WaitMessage() + } + } +} +*/ diff --git a/v_windows/v/old/vlib/gg/gg_android.c.v b/v_windows/v/old/vlib/gg/gg_android.c.v new file mode 100644 index 0000000..23450f2 --- /dev/null +++ b/v_windows/v/old/vlib/gg/gg_android.c.v @@ -0,0 +1,37 @@ +module gg + +import sokol.sapp + +#include +#include + +fn C.AConfiguration_new() voidptr +fn C.AConfiguration_fromAssetManager(voidptr, voidptr) +fn C.AConfiguration_getDensity(voidptr) u32 +fn C.AConfiguration_delete(voidptr) + +struct C.AAssetManager {} + +// See https://developer.android.com/ndk/reference/struct/a-native-activity for more info. +struct C.ANativeActivity { +pub: + assetManager voidptr // Pointer to the Asset Manager instance for the application. (AAssetManager *) + callbacks voidptr // Pointer to the callback function table of the native application. (struct ANativeActivityCallbacks *) + clazz voidptr // The NativeActivity object handle. + env voidptr // JNI context for the main thread of the app. + externalDataPath &char // Path to this application's external (removable/mountable) data directory. + instance voidptr // This is the native instance of the application. + internalDataPath &char // Path to this application's internal data directory. + obbPath &char // Available starting with Honeycomb: path to the directory containing the application's OBB files (if any). + sdkVersion int // The platform's SDK version code. + vm voidptr // The global handle on the process's Java VM +} + +pub fn android_dpi_scale() f32 { + config := C.AConfiguration_new() + activity := &C.ANativeActivity(sapp.android_get_native_activity()) + C.AConfiguration_fromAssetManager(config, activity.assetManager) + density := C.AConfiguration_getDensity(config) + C.AConfiguration_delete(config) + return f32(density) / 160 +} diff --git a/v_windows/v/old/vlib/gg/gg_darwin.c.v b/v_windows/v/old/vlib/gg/gg_darwin.c.v new file mode 100644 index 0000000..78e8a45 --- /dev/null +++ b/v_windows/v/old/vlib/gg/gg_darwin.c.v @@ -0,0 +1,21 @@ +module gg + +#include "@VEXEROOT/vlib/gg/gg_darwin.m" + +fn C.gg_get_screen_size() Size + +fn C.darwin_draw_string(x int, y int, s string, cfg voidptr) + +fn C.darwin_text_width(s string) int + +fn C.darwin_window_refresh() + +fn C.darwin_draw_rect(f32, f32, f32, f32, voidptr) + +fn C.darwin_create_image(path string) Image + +fn C.darwin_draw_image(f32, f32, f32, f32, &Image) + +fn C.darwin_draw_circle(f32, f32, f32, voidptr) + +//, gx.Color c) diff --git a/v_windows/v/old/vlib/gg/gg_darwin.m b/v_windows/v/old/vlib/gg/gg_darwin.m new file mode 100644 index 0000000..d6bb5a1 --- /dev/null +++ b/v_windows/v/old/vlib/gg/gg_darwin.m @@ -0,0 +1,126 @@ +#include + +NSColor* nscolor(gx__Color c) { + float red= (float)c.r / 255.0f; + float green= (float)c.g / 255.0f; + float blue= (float)c.b / 255.0f; + return [NSColor colorWithDeviceRed:red green:green blue:blue alpha:1.0f]; +} + +NSString* nsstring(string s) { + return [ [ NSString alloc ] initWithBytesNoCopy:s.str length:s.len + encoding:NSUTF8StringEncoding freeWhenDone: false]; +} + + +gg__Size gg_get_screen_size() { + NSScreen *screen = [NSScreen mainScreen]; + NSDictionary *description = [screen deviceDescription]; + NSSize displayPixelSize = [[description objectForKey:NSDeviceSize] sizeValue]; + CGSize displayPhysicalSize = CGDisplayScreenSize( + [[description objectForKey:@"NSScreenNumber"] unsignedIntValue]); + gg__Size res; + res.width = displayPixelSize.width; + res.height = displayPixelSize.height; + return res; +} + +void darwin_draw_string(int x, int y, string s, gx__TextCfg cfg) { + NSFont* font = [NSFont userFontOfSize: 0]; //cfg.size]; + // # NSFont* font = [NSFont fontWithName:@"Roboto Mono" size:cfg.size]; + if (cfg.mono) { + // # font = [NSFont fontWithName:@"Roboto Mono" size:cfg.size]; + font = [NSFont fontWithName:@"Menlo" size:cfg.size-5]; + } +if (cfg.bold) { + font = [[NSFontManager sharedFontManager] convertFont:font toHaveTrait:NSBoldFontMask]; +} + + + NSDictionary* attr = @{ +NSForegroundColorAttributeName: nscolor(cfg.color), +//NSParagraphStyleAttributeName: paragraphStyle, +NSFontAttributeName: font, +}; + [nsstring(s) drawAtPoint:NSMakePoint(x,y-15) withAttributes:attr]; +} + +int darwin_text_width(string s) { + // println('text_width "$s" len=$s.len') + NSString* n = @""; + if (s.len == 1) { + // println('len=1') + n=[NSString stringWithFormat:@"%c" , s.str[0]]; + } + else { + n = nsstring(s); + } + /* + # if (!defaultFont){ + # defaultFont = [NSFont userFontOfSize: ui__DEFAULT_FONT_SIZE]; + # } + # NSDictionary *attrs = @{ + # NSFontAttributeName: defaultFont, + # }; + */ + NSSize size = [n sizeWithAttributes:nil]; + // # printf("!!!%f\n", ceil(size.width)); + return (int)(ceil(size.width)); +} + + +void darwin_draw_rect(float x, float y, float width, float height, gx__Color c) { + NSColor* color = nscolor(c); + NSRect rect = NSMakeRect(x, y, width, height); + [color setFill]; + NSRectFill(rect); +} + + +void darwin_window_refresh() { + //[g_view setNeedsDisplay:YES]; + // update UI on the main thread TODO separate fn + + dispatch_async(dispatch_get_main_queue(), ^{ + [g_view setNeedsDisplay:YES]; + }); + + //puts("refresh"); + //[g_view drawRect:NSMakeRect(0,0,2000,2000)]; + //[[NSGraphicsContext currentContext] flushGraphics]; +} + +gg__Image darwin_create_image(string path_) { + // file = file.trim_space() + NSString* path = nsstring(path_); + NSImage* img = [[NSImage alloc] initWithContentsOfFile:path]; + if (img == 0) { + } + NSSize size = [img size]; + gg__Image res; + res.width = size.width; + res.height = size.height; + res.path = path_; + res.ok = true; + //printf("inited img width=%d\n", res.width) ; + // need __brige_retained so that the pointer is not freed by ARC + res.data = (__bridge_retained voidptr)(img); + return res; +} + +void darwin_draw_image(float x, float y, float w, float h, gg__Image* img) { + NSImage* i= (__bridge NSImage*)(img->data); + [i drawInRect:NSMakeRect(x,y,w,h)]; +} + +void darwin_draw_circle(float x, float y, float d, gx__Color color) { + NSColor* c = nscolor(color); + NSRect rect = NSMakeRect(x, y, d * 2, d * 2); + NSBezierPath* circlePath = [NSBezierPath bezierPath]; + [circlePath appendBezierPathWithOvalInRect: rect]; + [c setFill]; + // [circlePath stroke]; + [circlePath fill]; + // NSRectFill(rect); +} + diff --git a/v_windows/v/old/vlib/gg/image.v b/v_windows/v/old/vlib/gg/image.v new file mode 100644 index 0000000..89a16d7 --- /dev/null +++ b/v_windows/v/old/vlib/gg/image.v @@ -0,0 +1,384 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license that can be found in the LICENSE file. +module gg + +// import gx +// import sokol.sapp +import sokol.gfx +import os +import sokol +import sokol.sgl +import stbi + +[heap] +pub struct Image { +pub mut: + id int + width int + height int + nr_channels int + ok bool + data voidptr + ext string + simg_ok bool + simg C.sg_image + path string +} + +// DrawImageConfig struct defines the various options +// that can be used to draw an image onto the screen +pub struct DrawImageConfig { +pub: + flip_x bool + flip_y bool + img &Image = voidptr(0) + img_id int + img_rect Rect // defines the size and position on image when rendering to the screen + part_rect Rect // defines the size and position of part of the image to use when rendering + rotate int // amount to rotate the image in degrees + z f32 +} + +pub struct Rect { +pub: + x f32 + y f32 + width f32 + height f32 +} + +fn C.sg_isvalid() bool + +// TODO return ?Image +pub fn (mut ctx Context) create_image(file string) Image { + // println('\ncreate_image("$file")') + if !os.exists(file) { + return Image{} + } + $if macos { + if ctx.native_rendering { + // return C.darwin_create_image(file) + mut img := C.darwin_create_image(file) + // println('created macos image: $img.path w=$img.width') + // C.printf('p = %p\n', img.data) + img.id = ctx.image_cache.len + ctx.image_cache << img + return img + } + } + if !C.sg_isvalid() { + // Sokol is not initialized yet, add stbi object to a queue/cache + // ctx.image_queue << file + stb_img := stbi.load(file) or { return Image{} } + img := Image{ + width: stb_img.width + height: stb_img.height + nr_channels: stb_img.nr_channels + ok: false + data: stb_img.data + ext: stb_img.ext + path: file + id: ctx.image_cache.len + } + ctx.image_cache << img + return img + } + mut img := create_image(file) + img.id = ctx.image_cache.len + ctx.image_cache << img + return img +} + +// TODO copypasta +pub fn (mut ctx Context) create_image_with_size(file string, width int, height int) Image { + if !C.sg_isvalid() { + // Sokol is not initialized yet, add stbi object to a queue/cache + // ctx.image_queue << file + stb_img := stbi.load(file) or { return Image{} } + img := Image{ + width: width + height: height + nr_channels: stb_img.nr_channels + ok: false + data: stb_img.data + ext: stb_img.ext + path: file + id: ctx.image_cache.len + } + ctx.image_cache << img + return img + } + mut img := create_image(file) + img.id = ctx.image_cache.len + ctx.image_cache << img + return img +} + +// TODO remove this +fn create_image(file string) Image { + if !os.exists(file) { + println('gg.create_image(): file not found: $file') + return Image{} // none + } + stb_img := stbi.load(file) or { return Image{} } + mut img := Image{ + width: stb_img.width + height: stb_img.height + nr_channels: stb_img.nr_channels + ok: stb_img.ok + data: stb_img.data + ext: stb_img.ext + path: file + } + img.init_sokol_image() + return img +} + +pub fn (mut ctx Context) create_image_from_memory(buf &byte, bufsize int) Image { + stb_img := stbi.load_from_memory(buf, bufsize) or { return Image{} } + mut img := Image{ + width: stb_img.width + height: stb_img.height + nr_channels: stb_img.nr_channels + ok: stb_img.ok + data: stb_img.data + ext: stb_img.ext + id: ctx.image_cache.len + } + ctx.image_cache << img + return img +} + +pub fn (mut ctx Context) create_image_from_byte_array(b []byte) Image { + return ctx.create_image_from_memory(b.data, b.len) +} + +pub fn (mut ctx Context) cache_image(img Image) int { + ctx.image_cache << img + image_idx := ctx.image_cache.len - 1 + ctx.image_cache[image_idx].id = image_idx + return image_idx +} + +pub fn (mut ctx Context) get_cached_image_by_idx(image_idx int) &Image { + return &ctx.image_cache[image_idx] +} + +pub fn (mut img Image) init_sokol_image() &Image { + // println('\n init sokol image $img.path ok=$img.simg_ok') + mut img_desc := C.sg_image_desc{ + width: img.width + height: img.height + num_mipmaps: 0 + wrap_u: .clamp_to_edge + wrap_v: .clamp_to_edge + label: img.path.str + d3d11_texture: 0 + } + img_desc.data.subimage[0][0] = C.sg_range{ + ptr: img.data + size: size_t(img.nr_channels * img.width * img.height) + } + img.simg = C.sg_make_image(&img_desc) + img.simg_ok = true + img.ok = true + return img +} + +pub struct StreamingImageConfig { + pixel_format gfx.PixelFormat = .rgba8 + wrap_u gfx.Wrap = .clamp_to_edge + wrap_v gfx.Wrap = .clamp_to_edge + min_filter gfx.Filter = .linear + mag_filter gfx.Filter = .linear + num_mipmaps int = 1 + num_slices int = 1 +} + +// new_streaming_image returns a cached `image_idx` of a special image, that +// can be updated *each frame* by calling: gg.update_pixel_data(image_idx, buf) +// ... where buf is a pointer to the actual pixel data for the image. +// NB: you still need to call app.gg.draw_image after that, to actually draw it. +pub fn (mut ctx Context) new_streaming_image(w int, h int, channels int, sicfg StreamingImageConfig) int { + mut img := Image{} + img.width = w + img.height = h + img.nr_channels = channels // 4 bytes per pixel for .rgba8, see pixel_format + mut img_desc := C.sg_image_desc{ + width: img.width + height: img.height + pixel_format: sicfg.pixel_format + num_slices: 1 + num_mipmaps: 1 + usage: .stream + wrap_u: sicfg.wrap_u + wrap_v: sicfg.wrap_v + min_filter: sicfg.min_filter + mag_filter: sicfg.mag_filter + label: img.path.str + } + // Sokol requires that streamed images have NO .ptr/.size initially: + img_desc.data.subimage[0][0] = C.sg_range{ + ptr: 0 + size: size_t(0) + } + img.simg = C.sg_make_image(&img_desc) + img.simg_ok = true + img.ok = true + img_idx := ctx.cache_image(img) + return img_idx +} + +// update_pixel_data is a helper for working with image streams (i.e. images, +// that are updated dynamically by the CPU on each frame) +pub fn (mut ctx Context) update_pixel_data(cached_image_idx int, buf &byte) { + mut image := ctx.get_cached_image_by_idx(cached_image_idx) + image.update_pixel_data(buf) +} + +pub fn (mut img Image) update_pixel_data(buf &byte) { + mut data := C.sg_image_data{} + data.subimage[0][0].ptr = buf + data.subimage[0][0].size = size_t(img.width * img.height * img.nr_channels) + gfx.update_image(img.simg, &data) +} + +// draw_image_with_config takes in a config that details how the +// provided image should be drawn onto the screen +pub fn (ctx &Context) draw_image_with_config(config DrawImageConfig) { + id := if !isnil(config.img) { config.img.id } else { config.img_id } + if id >= ctx.image_cache.len { + eprintln('gg: draw_image() bad img id $id (img cache len = $ctx.image_cache.len)') + return + } + + img := ctx.image_cache[id] + if !img.simg_ok { + return + } + + mut img_rect := config.img_rect + if img_rect.width == 0 && img_rect.height == 0 { + img_rect = Rect{img_rect.x, img_rect.y, img.width, img.height} + } + + mut part_rect := config.part_rect + if part_rect.width == 0 && part_rect.height == 0 { + part_rect = Rect{part_rect.x, part_rect.y, img.width, img.height} + } + + u0 := part_rect.x / img.width + v0 := part_rect.y / img.height + u1 := (part_rect.x + part_rect.width) / img.width + v1 := (part_rect.y + part_rect.height) / img.height + x0 := img_rect.x * ctx.scale + y0 := img_rect.y * ctx.scale + x1 := (img_rect.x + img_rect.width) * ctx.scale + mut y1 := (img_rect.y + img_rect.height) * ctx.scale + if img_rect.height == 0 { + scale := f32(img.width) / f32(img_rect.width) + y1 = f32(img_rect.y + int(f32(img.height) / scale)) * ctx.scale + } + + flip_x := config.flip_x + flip_y := config.flip_y + + mut u0f := if !flip_x { u0 } else { u1 } + mut u1f := if !flip_x { u1 } else { u0 } + mut v0f := if !flip_y { v0 } else { v1 } + mut v1f := if !flip_y { v1 } else { v0 } + + sgl.load_pipeline(ctx.timage_pip) + sgl.enable_texture() + sgl.texture(img.simg) + + if config.rotate != 0 { + width := img_rect.width * ctx.scale + height := (if img_rect.height > 0 { img_rect.height } else { img.height }) * ctx.scale + + sgl.push_matrix() + sgl.translate(x0 + (width / 2), y0 + (height / 2), 0) + sgl.rotate(sgl.rad(-config.rotate), 0, 0, 1) + sgl.translate(-x0 - (width / 2), -y0 - (height / 2), 0) + } + + sgl.begin_quads() + sgl.c4b(255, 255, 255, 255) + sgl.v3f_t2f(x0, y0, config.z, u0f, v0f) + sgl.v3f_t2f(x1, y0, config.z, u1f, v0f) + sgl.v3f_t2f(x1, y1, config.z, u1f, v1f) + sgl.v3f_t2f(x0, y1, config.z, u0f, v1f) + sgl.end() + + if config.rotate != 0 { + sgl.pop_matrix() + } + + sgl.disable_texture() +} + +// Draw part of an image using uv coordinates +// img_rect is the size and position (in pixels on screen) of the displayed rectangle (ie the draw_image args) +// part_rect is the size and position (in absolute pixels in the image) of the wanted part +// eg. On a 600*600 context, to display only the first 400*400 pixels of a 2000*2000 image +// on the entire context surface, call : +// draw_image_part(Rect{0, 0, 600, 600}, Rect{0, 0, 400, 400}, img) +pub fn (ctx &Context) draw_image_part(img_rect Rect, part_rect Rect, img_ &Image) { + ctx.draw_image_with_config( + img: img_ + img_rect: img_rect + part_rect: part_rect + ) +} + +// draw_image draws the provided image onto the screen +pub fn (ctx &Context) draw_image(x f32, y f32, width f32, height f32, img_ &Image) { + $if macos { + if img_.id >= ctx.image_cache.len { + eprintln('gg: draw_image() bad img id $img_.id (img cache len = $ctx.image_cache.len)') + return + } + if ctx.native_rendering { + if img_.width == 0 { + return + } + if !os.exists(img_.path) { + return + } + C.darwin_draw_image(x, ctx.height - (y + height), width, height, img_) + return + } + } + + ctx.draw_image_with_config( + img: img_ + img_rect: Rect{x, y, width, height} + part_rect: Rect{0, 0, img_.width, img_.height} + ) +} + +// draw_image_flipped draws the provided image flipped horizontally (use `draw_image_with_config` to flip vertically) +pub fn (ctx &Context) draw_image_flipped(x f32, y f32, width f32, height f32, img_ &Image) { + ctx.draw_image_with_config( + flip_x: true + img: img_ + img_rect: Rect{x, y, width, height} + ) +} + +// draw_image_by_id draws an image by its id +pub fn (ctx &Context) draw_image_by_id(x f32, y f32, width f32, height f32, id int) { + ctx.draw_image_with_config( + img_id: id + img_rect: Rect{x, y, width, height} + ) +} + +// draw_image_3d draws an image with a z depth +pub fn (ctx &Context) draw_image_3d(x f32, y f32, z f32, width f32, height f32, img_ &Image) { + ctx.draw_image_with_config( + img: img_ + img_rect: Rect{x, y, width, height} + z: z + ) +} diff --git a/v_windows/v/old/vlib/gg/m4/graphic.v b/v_windows/v/old/vlib/gg/m4/graphic.v new file mode 100644 index 0000000..e134e80 --- /dev/null +++ b/v_windows/v/old/vlib/gg/m4/graphic.v @@ -0,0 +1,110 @@ +/********************************************************************** +* +* Simply vector/matrix graphic utility +* +* Copyright (c) 2021 Dario Deledda. All rights reserved. +* Use of this source code is governed by an MIT license +* that can be found in the LICENSE file. +* +* TODO: +**********************************************************************/ +module m4 + +import math + +// Translate degrees to radians +[inline] +pub fn rad(deg f32) f32 { + return (math.pi / 180.0) * deg +} + +// Translate radians to degrees +[inline] +pub fn deg(grad f32) f32 { + return (180.0 / math.pi) * grad +} + +// calculate the Orthographic projection matrix +pub fn ortho(left f32, right f32, bottom f32, top f32, z_near f32, z_far f32) Mat4 { + rml := right - left + rpl := right + left + tmb := top - bottom + tpb := top + bottom + fmn := z_far - z_near + fpn := z_far + z_near + if fmn != 0 { + return Mat4{ e: [ + 2 / rml, 0 , 0, -(rpl / rml), + 0 , 2 / tmb, 0, -(tpb / tmb), + 0 , 0, 2 / fmn, -(fpn / fmn), + 0 , 0, 0, 1, + ]! + } + } + return Mat4{ e: [ + 2 / rml, 0 , 0, -(rpl / rml), + 0 , 2 / tmb, 0, -(tpb / tmb), + 0 , 0, 0, 0, + 0 , 0, 0, 1, + ]! + } +} + +// Calculate the perspective matrix using (fov:fov, ar:aspect_ratio ,n:near_pane, f:far_plane) as parameters +pub fn perspective(fov f32, ar f32, n f32, f f32) Mat4 { + ctan := f32(1.0 / math.tan(fov * (f32(math.pi) / 360.0))) // for the FOV we use 360 instead 180 + return Mat4{ e: [ + ctan / ar, 0, 0, 0, + 0, ctan, 0, 0, + 0, 0, (n + f) / (n - f), -1.0, + 0, 0, (2.0 * n * f) / (n - f), 0, + ]! + } +} + +// Calculate the look-at matrix +pub fn look_at(eye Vec4, center Vec4, up Vec4) Mat4 { + f := (center - eye).normalize3() + s := (f % up).normalize3() + u := (s % f) + + return Mat4{ e: [ + /* [0][0] */ s.e[0], + /* [0][1] */ u.e[0], + /* [0][2] */ - f.e[0], + /* [0][3] */ 0, + + /* [1][1] */ s.e[1], + /* [1][1] */ u.e[1], + /* [1][2] */ - f.e[1], + /* [1][3] */ 0, + + /* [2][0] */ s.e[2], + /* [2][1] */ u.e[2], + /* [2][2] */ - f.e[2], + /* [2][3] */ 0, + + /* [3][0] */ - (s * eye), + /* [3][1] */ - (u * eye), + /* [3][2] */ f * eye, + /* [3][3] */ 1, + ]! + } +} + + +// Get the complete transformation matrix for GLSL demos +pub fn calc_tr_matrices(w f32, h f32, rx f32, ry f32, in_scale f32) Mat4 { + proj := perspective(60, w / h, 0.01, 10.0) + view := look_at(Vec4{ e: [f32(0.0), 1.5, 6, 0]! }, Vec4{ e: [f32(0), 0, 0, 0]! }, Vec4{ e: [f32(0), 1.0, 0, 0]! }) + view_proj := view * proj + + rxm := rotate(rad(rx), Vec4{ e: [f32(1), 0, 0, 0]! }) + rym := rotate(rad(ry), Vec4{ e: [f32(0), 1, 0, 0]! }) + + model := rym * rxm + scale_m := scale(Vec4{ e: [in_scale, in_scale, in_scale, 1]! }) + + res := (scale_m * model) * view_proj + return res +} diff --git a/v_windows/v/old/vlib/gg/m4/m4_test.v b/v_windows/v/old/vlib/gg/m4/m4_test.v new file mode 100644 index 0000000..90b4640 --- /dev/null +++ b/v_windows/v/old/vlib/gg/m4/m4_test.v @@ -0,0 +1,235 @@ +import gg.m4 + +pub fn test_m4() { + unsafe { + // Test Mat4 + mut a := m4.Mat4{ e: [ + f32(0), 1, 2, 3, + 4, 5, 6, 7, + 8, 9, 10, 11, + 12, 13, 14, 15, + ]! + } + mut b := m4.Mat4{} + mut c := m4.Mat4{} + + // equal test + assert a.e == [ + f32(0), 1, 2, 3, + 4, 5, 6, 7, + 8, 9, 10, 11, + 12, 13, 14, 15, + ]! + + // copy test + b.copy(a) + assert a.e == b.e + + // test: transpose, scale + assert b.transpose().mul_scalar(2.0).mul_scalar(0.5).transpose().e == a.e + assert b.sum_all() == 120.0 + + // test rows/columns set/get + for i in 0 .. 4 { + b = m4.zero_m4() + b.set_row(i, m4.Vec4{ e: [f32(1.0), 2, 3, 4]! }) + assert b.get_f(0, i) == 1.0 + assert b.get_f(1, i) == 2.0 + assert b.get_f(2, i) == 3.0 + assert b.get_f(3, i) == 4.0 + // println(b) + c = m4.zero_m4() + c.set_col(i, m4.Vec4{ e: [f32(1.0), 2, 3, 4]! }) + assert c.get_f(i, 0) == 1.0 + assert c.get_f(i, 1) == 2.0 + assert c.get_f(i, 2) == 3.0 + assert c.get_f(i, 3) == 4.0 + // println(c) + } + } +} + +fn test_swap_col_row() { + unsafe { + // swap_col / swap_row + b := m4.Mat4{ e: [ + f32(1), 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12, + 13, 14, 15, 16, + ]! + } + b.swap_col(0, 2) + assert b.e == [ + f32(3), 2, 1, 4, + 7, 6, 5, 8, + 11, 10, 9, 12, + 15, 14, 13, 16, + ]! + b = m4.Mat4{ e: [ + f32(1), 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12, + 13, 14, 15, 16, + ]! + } + b.swap_row(0, 2) + assert b.e == [ + f32(9), 10, 11, 12, + 5, 6, 7, 8, + 1, 2, 3, 4, + 13, 14, 15, 16, + ]! + } +} + +fn test_sum_sub() { + unsafe { + // test sum/sub + b := m4.unit_m4() + c := m4.unit_m4() + assert m4.sub(m4.add(b, c), b).e == m4.unit_m4().e + assert (b + c - b).e == m4.unit_m4().e + } +} + +fn test_transpose() { + unsafe { + b := m4.Mat4{ e: [ + f32(0), 1, 2, 3, + 4, 5, 6, 7, + 8, 9, 10, 11, + 12, 13, 14, 15, + ]! + } + assert b.transpose().transpose().e == b.e + } +} + +fn test_multiplication() { + unsafe { + b := m4.Mat4{ e: [ + f32(1), 0, 0, 0, + 0, 2, 0, 0, + 0, 0, 3, 0, + 0, 0, 0, 4, + ]! + } + c := m4.Mat4{ e: [ + f32(1), 2, 3, 4, + 5, 6, 7, 8, + 9, 10, 11, 12, + 13, 14, 15, 16, + ]! + } + + assert (c * c).e == [ + f32(90),100,110,120, + 202,228,254,280, + 314,356,398,440, + 426,484,542,600, + ]! + + assert m4.mul(c, c).e == [ + f32(90),100,110,120, + 202,228,254,280, + 314,356,398,440, + 426,484,542,600, + ]! + + assert m4.mul(b, c).e == [ + f32(1), 2, 3, 4, + 10, 12, 14, 16, + 27, 30, 33, 36, + 52, 56, 60, 64, + ]! + + assert (b * c).e == [ + f32(1), 2, 3, 4, + 10, 12, 14, 16, + 27, 30, 33, 36, + 52, 56, 60, 64, + ]! + + assert m4.det(b) == 24 + } +} + +fn test_det() { + unsafe { + b := m4.Mat4{ e: [ + f32(5), 6, 6, 8, + 2, 2, 2, 8, + 6, 6, 2, 8, + 2, 3, 6, 7, + ]! + } + assert m4.det(b) == -8 + + c := m4.Mat4{ e: [ + f32(1), 8, 2, 3, + 8, 2, 3, 1, + 2, 3, 3, 2, + 3, 1, 2, 4, + ]! + } + // println("*** INVERSE ****") + // println(m4.mul(b.inverse(),b)) + // println(m4.clean_small(m4.mul(c.inverse(),c))) + // println("****************") + assert m4.mul(b.inverse(), b).e == m4.unit_m4().e + assert m4.mul(c.inverse(), c).is_equal(m4.unit_m4()) + } +} + +fn test_vec4() { + // Test Vec4 + // println("*** Vector4 ****") + assert m4.vec3(1,2,3) == m4.Vec4{[f32(1), 2, 3, 1]!} + mut v := m4.Vec4{[f32(1), 2, 3, 4]!} + assert v * v.inv() == 4 + assert v.mul_scalar(1.0 / v.mod()).mod() == 1 + assert v + m4.Vec4{ e: [f32(5), 6, 7, 8]! } == m4.Vec4{ e: [f32(6), 8, 10, 12]! } + assert v - m4.Vec4{ e: [f32(1), 2, 3, 4]! } == m4.Vec4{ e: [f32(0), 0, 0, 0]! } + assert v.mul_vec4(m4.Vec4{ e: [f32(2), 2, 2, 2]! }) == m4.Vec4{ e: [f32(2), 4, 6, 8]! } + assert f32_abs(v.normalize().mod() - 1) < m4.precision + v = m4.Vec4{[f32(1), 2, 3, 0]!} + assert f32_abs(v.normalize3().mod3() - 1) < m4.precision + assert f32_abs(v.normalize3().mod() - 1) < m4.precision + // cross product + // x y z + // 1 2 3 ==> -3 6 -3 0 + // 4 5 6 + // println(m4.Vec4{[f32(1),2,3,2]!} % m4.Vec4{[f32(4),5,6,2]!}) + assert m4.Vec4{[f32(1), 2, 3, 0]!} % m4.Vec4{[f32(4), 5, 6, 0]!} == m4.Vec4{[ f32(-3), 6, -3, 0, ]!} + assert m4.Vec4{[f32(1), 2, 3, 13]!} % m4.Vec4{[f32(4), 5, 6, 11]!} == m4.Vec4{[ f32(-3), 6, -3, 0, ]!} + // matrix * vector + a := m4.Mat4{ e: [ + f32(1),2,3,4 + 5,6,7,8 + 9,10,11,12 + 13,14,15,16 + ]! + } + assert m4.mul_vec(a, m4.Vec4{[f32(1), 2, 3, 4]!}) == m4.Vec4{[ f32(30), 70, 110,150, ]!} + // Rotation + // println("*** Rotation ****") + rotx := m4.rotate(m4.rad(-90), m4.Vec4{ e: [f32(1.0), 0, 0, 0]! }).clean() + roty := m4.rotate(m4.rad(-90), m4.Vec4{ e: [f32(0), 1.0, 0, 0]! }).clean() + rotz := m4.rotate(m4.rad(-90), m4.Vec4{ e: [f32(0), 0, 1, 0]! }).clean() + // println( rotx ) + // println( roty ) + // println( rotz ) + // println( m4.mul_vec(rotx, m4.Vec4{e:[f32(0),0,1,0]!}).clean()) + assert m4.mul_vec(roty, m4.Vec4{ e: [f32(1.0), 0.0, 0, 0]! }).clean() == m4.Vec4{ e: [f32(0), 0.0, -1, 0]! } + assert m4.mul_vec(rotz, m4.Vec4{ e: [f32(1.0), 0.0, 0, 0]! }).clean() == m4.Vec4{ e: [f32(0), 1, 0, 0]! } + assert m4.mul_vec(rotx, m4.Vec4{ e: [f32(0), 0, 1, 0]! }).clean() == m4.Vec4{ e: [f32(0), -1, 0, 0]! } + // println("****************") +} + +fn test_proj() { + ort := m4.ortho(0,300,0,200,0,0) + assert m4.mul_vec(ort, m4.Vec4{[ f32(150), 100, 0, 1]!}) == m4.Vec4{[ f32(0), 0, 0, 1]!} + assert m4.mul_vec(ort, m4.Vec4{[ f32(0), 0, 0, 1]!}) == m4.Vec4{[ f32(-1), -1, 0, 1]!} + assert m4.mul_vec(ort, m4.Vec4{[ f32(300), 200, 0, 1]!}) == m4.Vec4{[ f32(1), 1, 0, 1]!} +} diff --git a/v_windows/v/old/vlib/gg/m4/matrix.v b/v_windows/v/old/vlib/gg/m4/matrix.v new file mode 100644 index 0000000..c839b9d --- /dev/null +++ b/v_windows/v/old/vlib/gg/m4/matrix.v @@ -0,0 +1,595 @@ +/********************************************************************** +* +* Simply vector/matrix utility +* +* Copyright (c) 2021 Dario Deledda. All rights reserved. +* Use of this source code is governed by an MIT license +* that can be found in the LICENSE file. +* +* TODO: +**********************************************************************/ +module m4 + +import math + +pub union Mat4 { +pub mut: + e [16]f32 + f [4][4]f32 +} + +pub const precision = f32(10e-7) + +// default precision for the module + +/********************************************************************* +* +* Utility +* +*********************************************************************/ +// String representation of the matrix +pub fn (x Mat4) str() string { + unsafe { + return '|${x.e[0]:-6.3},${x.e[1]:-6.3},${x.e[2]:-6.3},${x.e[3]:-6.3}|\n' + + '|${x.e[4]:-6.3},${x.e[5]:-6.3},${x.e[6]:-6.3},${x.e[7]:-6.3}|\n' + + '|${x.e[8]:-6.3},${x.e[9]:-6.3},${x.e[10]:-6.3},${x.e[11]:-6.3}|\n' + + '|${x.e[12]:-6.3},${x.e[13]:-6.3},${x.e[14]:-6.3},${x.e[15]:-6.3}|' + } +} + +// Remove all the raw zeros +[direct_array_access] +pub fn (a Mat4) clean() Mat4 { + unsafe { + x := Mat4{} + for c, value in a.e { + if f32_abs(value) < m4.precision { + x.e[c] = 0 + } else { + x.e[c] = value + } + } + return x + } +} + +// Sum all the elements of the matrix +pub fn (x Mat4) sum_all() f32 { + mut res := f32(0) + for v in unsafe { x.e } { + res += v + } + return res +} + +// Check if two matrix are equal using module precision +[direct_array_access] +pub fn (x Mat4) is_equal(y Mat4) bool { + unsafe { + for c, value in x.e { + if f32_abs(value - y.e[c]) > m4.precision { + return false + } + } + return true + } +} + +//------------------------------------- +// Set/Get values +//------------------------------------- +// Get an element of the matrix using [0..15] indexes, one dimension +pub fn (x Mat4) get_e(elem_index int) f32 { + unsafe { + return x.e[elem_index] + } +} + +// Get an element of the matrix using [0..3][0..3] indexes, two dimension +pub fn (x Mat4) get_f(index_col int, index_row int) f32 { + unsafe { + return x.e[(index_row << 2) + index_col] + } +} + +// Set an element of the matrix using [0..15] indexes, one dimension +pub fn (mut x Mat4) set_e(index int, value f32) { + unsafe { + x.e[index] = value + } +} + +// Set an element of the matrix using [0..3][0..3] indexes, two dimension +pub fn (mut x Mat4) set_f(index_col int, index_row int, value f32) { + unsafe { + x.e[(index_row << 2) + index_col] = value + } +} + +// Copy a matrix elements from another matrix +pub fn (mut x Mat4) copy(y Mat4) { + unsafe { + x.e = [ + y.e[0 ], y.e[1 ], y.e[2 ], y.e[3 ], + y.e[4 ], y.e[5 ], y.e[6 ], y.e[7 ], + y.e[8 ], y.e[9 ], y.e[10], y.e[11], + y.e[12], y.e[13], y.e[14], y.e[15], + ]! + } +} + +// Set the trace of the matrix using a vec4 +pub fn (mut x Mat4) set_trace(v3 Vec4) { + unsafe { + x.e[0 ] = v3.e[0] + x.e[5 ] = v3.e[1] + x.e[10] = v3.e[2] + x.e[15] = v3.e[3] + } +} + +// Get the trace of the matrix +pub fn (x Mat4) get_trace() Vec4 { + unsafe { + return Vec4{ e: [ x.e[0], x.e[5], x.e[10], x.e[15], ]! } + } +} + +// Set all the matrix elements to value +pub fn (mut x Mat4) set_f32(value f32) { + unsafe { + x.e = [ + value, value, value, value, + value, value, value, value, + value, value, value, value, + value, value, value, value, + ]! + } +} + +//------------------------------------- +// Rows/Column access +//------------------------------------- +// Set the row as the input vec4 +[direct_array_access] +[unsafe] +pub fn (mut x Mat4) set_row(row int, v3 Vec4) { + unsafe { + x.e[row * 4 + 0] = v3.e[0] + x.e[row * 4 + 1] = v3.e[1] + x.e[row * 4 + 2] = v3.e[2] + x.e[row * 4 + 3] = v3.e[3] + } +} + +// Get a row from a matrix +[direct_array_access] +[unsafe] +pub fn (x Mat4) get_row(row int) Vec4 { + unsafe { + return Vec4{ + e: [ + x.e[row * 4 + 0], + x.e[row * 4 + 1], + x.e[row * 4 + 2], + x.e[row * 4 + 3], + ]! + } + } +} + +// Set the column as the input vec4 +[direct_array_access] +[unsafe] +pub fn (mut x Mat4) set_col(col int, v3 Vec4) { + unsafe { + x.e[col] = v3.e[0] + x.e[col + 4 ] = v3.e[1] + x.e[col + 8 ] = v3.e[2] + x.e[col + 12] = v3.e[3] + } +} + +// Get a column from a matrix +[direct_array_access] +[unsafe] +pub fn (x Mat4) get_col(col int) Vec4 { + unsafe { + return Vec4{ + e: [ + x.e[col], + x.e[col + 4 ], + x.e[col + 8 ], + x.e[col + 12], + ]! + } + } +} + +// Swap two columns in the matrix +[direct_array_access] +[unsafe] +pub fn (mut x Mat4) swap_col(col1 int, col2 int) { + unsafe { + v0 := x.e[col1] + v1 := x.e[col1 + 4 ] + v2 := x.e[col1 + 8 ] + v3 := x.e[col1 + 12] + + x.e[col1] = x.e[col2] + x.e[col1 + 4 ] = x.e[col2 + 4 ] + x.e[col1 + 8 ] = x.e[col2 + 8 ] + x.e[col1 + 12] = x.e[col2 + 12] + + x.e[col2] = v0 + x.e[col2 + 4 ] = v1 + x.e[col2 + 8 ] = v2 + x.e[col2 + 12] = v3 + } +} + +// Swap two rows in the matrix +[direct_array_access] +[unsafe] +pub fn (mut x Mat4) swap_row(row1 int, row2 int) { + unsafe { + v0 := x.e[row1 * 4 + 0] + v1 := x.e[row1 * 4 + 1] + v2 := x.e[row1 * 4 + 2] + v3 := x.e[row1 * 4 + 3] + + x.e[row1 * 4 + 0] = x.e[row2 * 4 + 0] + x.e[row1 * 4 + 1] = x.e[row2 * 4 + 1] + x.e[row1 * 4 + 2] = x.e[row2 * 4 + 2] + x.e[row1 * 4 + 3] = x.e[row2 * 4 + 3] + + x.e[row2 * 4 + 0] = v0 + x.e[row2 * 4 + 1] = v1 + x.e[row2 * 4 + 2] = v2 + x.e[row2 * 4 + 3] = v3 + } +} + +//------------------------------------- +// Modify data +//------------------------------------- +// Transpose the matrix +pub fn (x Mat4) transpose() Mat4 { + unsafe { + return Mat4{ e: [ + x.e[0 ], x.e[4 ], x.e[8 ], x.e[12], + x.e[1 ], x.e[5 ], x.e[9 ], x.e[13], + x.e[2 ], x.e[6 ], x.e[10], x.e[14], + x.e[3 ], x.e[7 ], x.e[11], x.e[15], + ]! + } + } +} + +// Multiply the all the elements of the matrix by a scalar +pub fn (x Mat4) mul_scalar(s f32) Mat4 { + unsafe { + return Mat4{ e: [ + x.e[0 ] * s, x.e[1 ] * s, x.e[2 ] * s, x.e[3 ] * s, + x.e[4 ] * s, x.e[5 ] * s, x.e[6 ] * s, x.e[7 ] * s, + x.e[8 ] * s, x.e[9 ] * s, x.e[10] * s, x.e[11] * s, + x.e[12] * s, x.e[13] * s, x.e[14] * s, x.e[15] * s, + ]! + } + } +} + +/********************************************************************* +* +* Init/set +* +*********************************************************************/ +// Return a zero matrix +pub fn zero_m4() Mat4 { + return Mat4{ e: [ + f32(0), 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + ]! + } +} + +// Return a unity matrix +pub fn unit_m4() Mat4 { + return Mat4{ e: [ + f32(1), 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1, + ]! + } +} + +// Return a matrix initialized with value +pub fn set_m4(value f32) Mat4 { + return Mat4{ e: [ + value, value, value, value, + value, value, value, value, + value, value, value, value, + value, value, value, value, + ]! + } +} + +/********************************************************************* +* +* Math +* +*********************************************************************/ + +// Sum of matrix, operator + +pub fn (a Mat4) + (b Mat4) Mat4 { + unsafe { + return Mat4{ e: [ + a.e[0 ] + b.e[0 ], a.e[1 ] + b.e[1 ], a.e[2 ] + b.e[2 ], a.e[3 ] + b.e[3 ], + a.e[4 ] + b.e[4 ], a.e[5 ] + b.e[5 ], a.e[6 ] + b.e[6 ], a.e[7 ] + b.e[7 ], + a.e[8 ] + b.e[8 ], a.e[9 ] + b.e[9 ], a.e[10] + b.e[10], a.e[11] + b.e[11], + a.e[12] + b.e[12], a.e[13] + b.e[13], a.e[14] + b.e[14], a.e[15] + b.e[15], + ]! + } + } +} + +// Subtraction of matrix, operator - +pub fn (a Mat4) - (b Mat4) Mat4 { + unsafe { + return Mat4{ e: [ + a.e[0 ] - b.e[0 ], a.e[1 ] - b.e[1 ], a.e[2 ] - b.e[2 ], a.e[3 ] - b.e[3 ], + a.e[4 ] - b.e[4 ], a.e[5 ] - b.e[5 ], a.e[6 ] - b.e[6 ], a.e[7 ] - b.e[7 ], + a.e[8 ] - b.e[8 ], a.e[9 ] - b.e[9 ], a.e[10] - b.e[10], a.e[11] - b.e[11], + a.e[12] - b.e[12], a.e[13] - b.e[13], a.e[14] - b.e[14], a.e[15] - b.e[15], + ]! + } + } +} + +// Multiplication of matrix, operator * +pub fn (a Mat4) * (b Mat4) Mat4 { + unsafe { + return Mat4{ + e: [ + /* [0][0] */ a.f[0][0] * b.f[0][0] + a.f[0][1] * b.f[1][0] + a.f[0][2] * b.f[2][0] + a.f[0][3] * b.f[3][0] + /* [0][1] */, a.f[0][0] * b.f[0][1] + a.f[0][1] * b.f[1][1] + a.f[0][2] * b.f[2][1] + a.f[0][3] * b.f[3][1] + /* [0][2] */, a.f[0][0] * b.f[0][2] + a.f[0][1] * b.f[1][2] + a.f[0][2] * b.f[2][2] + a.f[0][3] * b.f[3][2] + /* [0][3] */, a.f[0][0] * b.f[0][3] + a.f[0][1] * b.f[1][3] + a.f[0][2] * b.f[2][3] + a.f[0][3] * b.f[3][3] + + /* [1][0] */, a.f[1][0] * b.f[0][0] + a.f[1][1] * b.f[1][0] + a.f[1][2] * b.f[2][0] + a.f[1][3] * b.f[3][0] + /* [1][1] */, a.f[1][0] * b.f[0][1] + a.f[1][1] * b.f[1][1] + a.f[1][2] * b.f[2][1] + a.f[1][3] * b.f[3][1] + /* [1][2] */, a.f[1][0] * b.f[0][2] + a.f[1][1] * b.f[1][2] + a.f[1][2] * b.f[2][2] + a.f[1][3] * b.f[3][2] + /* [1][3] */, a.f[1][0] * b.f[0][3] + a.f[1][1] * b.f[1][3] + a.f[1][2] * b.f[2][3] + a.f[1][3] * b.f[3][3] + + /* [2][0] */, a.f[2][0] * b.f[0][0] + a.f[2][1] * b.f[1][0] + a.f[2][2] * b.f[2][0] + a.f[2][3] * b.f[3][0] + /* [2][1] */, a.f[2][0] * b.f[0][1] + a.f[2][1] * b.f[1][1] + a.f[2][2] * b.f[2][1] + a.f[2][3] * b.f[3][1] + /* [2][2] */, a.f[2][0] * b.f[0][2] + a.f[2][1] * b.f[1][2] + a.f[2][2] * b.f[2][2] + a.f[2][3] * b.f[3][2] + /* [2][3] */, a.f[2][0] * b.f[0][3] + a.f[2][1] * b.f[1][3] + a.f[2][2] * b.f[2][3] + a.f[2][3] * b.f[3][3] + + /* [3][0] */, a.f[3][0] * b.f[0][0] + a.f[3][1] * b.f[1][0] + a.f[3][2] * b.f[2][0] + a.f[3][3] * b.f[3][0] + /* [3][1] */, a.f[3][0] * b.f[0][1] + a.f[3][1] * b.f[1][1] + a.f[3][2] * b.f[2][1] + a.f[3][3] * b.f[3][1] + /* [3][2] */, a.f[3][0] * b.f[0][2] + a.f[3][1] * b.f[1][2] + a.f[3][2] * b.f[2][2] + a.f[3][3] * b.f[3][2] + /* [3][3] */, a.f[3][0] * b.f[0][3] + a.f[3][1] * b.f[1][3] + a.f[3][2] * b.f[2][3] + a.f[3][3] * b.f[3][3], + ]! + } + } +} + +// Sum of matrix function +pub fn add(a Mat4, b Mat4) Mat4 { + unsafe { + return a + b + } +} + +// Subtraction of matrix function +pub fn sub(a Mat4, b Mat4) Mat4 { + unsafe { + return a - b + } +} + +// Multiplication of matrix function +pub fn mul(a Mat4, b Mat4) Mat4 { + unsafe { + return a * b + } +} + +// Multiply a Matrix by a vector +pub fn mul_vec(a Mat4, v Vec4) Vec4 { + unsafe { + return Vec4{ e: [ + a.e[0 ] * v.e[0] + a.e[1 ] * v.e[1] + a.e[2 ] * v.e[2] + a.e[3 ] * v.e[3], + a.e[4 ] * v.e[0] + a.e[5 ] * v.e[1] + a.e[6 ] * v.e[2] + a.e[7 ] * v.e[3], + a.e[8 ] * v.e[0] + a.e[9 ] * v.e[1] + a.e[10] * v.e[2] + a.e[11] * v.e[3], + a.e[12] * v.e[0] + a.e[13] * v.e[1] + a.e[14] * v.e[2] + a.e[15] * v.e[3], + ]! + } + } +} + +// Calculate the determinant of the Matrix +pub fn det(x Mat4) f32 { + unsafe { + mut t := [6]f32{} + x00 := x.f[0][0] + x10 := x.f[1][0] + x20 := x.f[2][0] + x30 := x.f[3][0] + x01 := x.f[0][1] + x11 := x.f[1][1] + x21 := x.f[2][1] + x31 := x.f[3][1] + x02 := x.f[0][2] + x12 := x.f[1][2] + x22 := x.f[2][2] + x32 := x.f[3][2] + x03 := x.f[0][3] + x13 := x.f[1][3] + x23 := x.f[2][3] + x33 := x.f[3][3] + + t[0] = x22 * x33 - x23 * x32 + t[1] = x12 * x33 - x13 * x32 + t[2] = x12 * x23 - x13 * x22 + t[3] = x02 * x33 - x03 * x32 + t[4] = x02 * x23 - x03 * x22 + t[5] = x02 * x13 - x03 * x12 + + return 0.0 + + x00 * (x11 * t[0] - x21 * t[1] + x31 * t[2]) - + x10 * (x01 * t[0] - x21 * t[3] + x31 * t[4]) + + x20 * (x01 * t[1] - x11 * t[3] + x31 * t[5]) - + x30 * (x01 * t[2] - x11 * t[4] + x21 * t[5]) + } +} + +// Calculate the inverse of the Matrix +pub fn (x Mat4) inverse() Mat4 { + unsafe { + mut t := [6]f32{} + mut det := f32(0) + + a := x.f[0][0] + b := x.f[1][0] + c := x.f[2][0] + d := x.f[3][0] + e := x.f[0][1] + f := x.f[1][1] + g := x.f[2][1] + h := x.f[3][1] + i := x.f[0][2] + j := x.f[1][2] + k := x.f[2][2] + l := x.f[3][2] + m := x.f[0][3] + n := x.f[1][3] + o := x.f[2][3] + p := x.f[3][3] + + t[0] = k * p - o * l + t[1] = j * p - n * l + t[2] = j * o - n * k + t[3] = i * p - m * l + t[4] = i * o - m * k + t[5] = i * n - m * j + + mut dest := Mat4{} + dest.f[0][0] = f * t[0] - g * t[1] + h * t[2] + dest.f[0][1] = -(e * t[0] - g * t[3] + h * t[4]) + dest.f[0][2] = e * t[1] - f * t[3] + h * t[5] + dest.f[0][3] = -(e * t[2] - f * t[4] + g * t[5]) + + dest.f[1][0] = -(b * t[0] - c * t[1] + d * t[2]) + dest.f[1][1] = a * t[0] - c * t[3] + d * t[4] + dest.f[1][2] = -(a * t[1] - b * t[3] + d * t[5]) + dest.f[1][3] = a * t[2] - b * t[4] + c * t[5] + + t[0] = g * p - o * h + t[1] = f * p - n * h + t[2] = f * o - n * g + t[3] = e * p - m * h + t[4] = e * o - m * g + t[5] = e * n - m * f + + dest.f[2][0] = b * t[0] - c * t[1] + d * t[2] + dest.f[2][1] = -(a * t[0] - c * t[3] + d * t[4]) + dest.f[2][2] = a * t[1] - b * t[3] + d * t[5] + dest.f[2][3] = -(a * t[2] - b * t[4] + c * t[5]) + + t[0] = g * l - k * h + t[1] = f * l - j * h + t[2] = f * k - j * g + t[3] = e * l - i * h + t[4] = e * k - i * g + t[5] = e * j - i * f + + dest.f[3][0] = -(b * t[0] - c * t[1] + d * t[2]) + dest.f[3][1] = a * t[0] - c * t[3] + d * t[4] + dest.f[3][2] = -(a * t[1] - b * t[3] + d * t[5]) + dest.f[3][3] = a * t[2] - b * t[4] + c * t[5] + + tmp := (a * dest.f[0][0] + b * dest.f[0][1] + c * dest.f[0][2] + d * dest.f[0][3]) + if tmp != 0 { + det = f32(1.0) / tmp + } + return dest.mul_scalar(det) + } +} + +/********************************************************************* +* +* Transformations +* +*********************************************************************/ + +// Get a rotation matrix using w as rotation axis vector, the angle is in radians +pub fn rotate(angle f32, w Vec4) Mat4 { + cs := f32(math.cos(angle)) + sn := f32(math.sin(angle)) + cv := f32(1.0) - cs + axis := w.normalize3() + unsafe { + ax := axis.e[0] + ay := axis.e[1] + az := axis.e[2] + + return Mat4{ e: [ + /* [0][0] */ (ax * ax * cv) + cs + /* [0][1] */, (ax * ay * cv) + az * sn + /* [0][2] */, (ax * az * cv) - ay * sn + /* [0][3] */, 0 + + /* [1][0] */, (ay * ax * cv) - az * sn + /* [1][1] */, (ay * ay * cv) + cs + /* [1][2] */, (ay * az * cv) + ax * sn + /* [1][3] */, 0 + + /* [2][0] */, (az * ax * cv) + ay * sn + /* [2][1] */, (az * ay * cv) - ax * sn + /* [2][2] */, (az * az * cv) + cs + /* [2][3] */, 0 + + /* [3][0] */, 0 + /* [3][1] */, 0 + /* [3][2] */, 0 + /* [3][3] */, 1, + ]! + } + } +} + +/********************************************************************* +* +* Graphic +* +*********************************************************************/ +// Get a matrix translated by a vector w +pub fn (x Mat4) translate(w Vec4) Mat4 { + unsafe { + return Mat4{ e: [ + x.e[0], x.e[1], x.e[2 ], x.e[3 ] , + x.e[4], x.e[5], x.e[6 ], x.e[7 ] , + x.e[8], x.e[9], x.e[10], x.e[11] , + x.e[12] + w.e[0], x.e[13] + w.e[1], x.e[14] + w.e[2], x.e[15], + ]! + } + } +} + +// Get a scale matrix, the scale vector is w, only xyz are evaluated. +pub fn scale(w Vec4) Mat4 { + unsafe { + return Mat4{ e: [ + w.e[0], 0, 0, 0, + 0, w.e[1], 0, 0, + 0, 0, w.e[2], 0, + 0, 0, 0, 1, + ]! + } + } +} diff --git a/v_windows/v/old/vlib/gg/m4/vector.v b/v_windows/v/old/vlib/gg/m4/vector.v new file mode 100644 index 0000000..52e4c78 --- /dev/null +++ b/v_windows/v/old/vlib/gg/m4/vector.v @@ -0,0 +1,230 @@ +/********************************************************************** +* +* Simply vector/matrix utility +* +* Copyright (c) 2021 Dario Deledda. All rights reserved. +* Use of this source code is governed by an MIT license +* that can be found in the LICENSE file. +* +* TODO: +**********************************************************************/ +module m4 + +import math + +pub struct Vec4 { +pub mut: + e [4]f32 +} + +/********************************************************************* +* +* Utility +* +*********************************************************************/ +pub fn (x Vec4) str() string { + return '|${x.e[0]:-6.3},${x.e[1]:-6.3},${x.e[2]:-6.3},${x.e[3]:-6.3}|' +} + +// create a Vec4 function passing x,y,z as parameteres. w is set to 1 +pub fn vec3(x f32, y f32, z f32) Vec4 { + return Vec4{ + e: [x, y, z, 1]! + } +} + +// Remove all the raw zeros +[direct_array_access] +pub fn (a Vec4) clean() Vec4 { + mut x := Vec4{} + for c, value in a.e { + if f32_abs(value) < precision { + x.e[c] = 0 + } else { + x.e[c] = value + } + } + return x +} + +// Set all elements to value +pub fn (mut x Vec4) copy(value f32) { + x.e = [value, value, value, value]! +} + +// Scale the vector using a scalar +pub fn (x Vec4) mul_scalar(value f32) Vec4 { + return Vec4{ + e: [x.e[0] * value, x.e[1] * value, x.e[2] * value, x.e[3] * value]! + } +} + +// Reciprocal of the vector +pub fn (x Vec4) inv() Vec4 { + return Vec4{ + e: [ + if x.e[0] != 0 { 1.0 / x.e[0] } else { f32(0) }, + if x.e[1] != 0 { 1.0 / x.e[1] } else { f32(0) }, + if x.e[2] != 0 { 1.0 / x.e[2] } else { f32(0) }, + if x.e[3] != 0 { 1.0 / x.e[3] } else { f32(0) }, + ]! + } +} + +// Normalize the vector +pub fn (x Vec4) normalize() Vec4 { + m := x.mod() + if m == 0 { + return zero_v4() + } + return Vec4{ + e: [ + x.e[0] * (1 / m), + x.e[1] * (1 / m), + x.e[2] * (1 / m), + x.e[3] * (1 / m), + ]! + } +} + +// Normalize only xyz, w set to 0 +pub fn (x Vec4) normalize3() Vec4 { + m := x.mod3() + if m == 0 { + return zero_v4() + } + return Vec4{ + e: [ + x.e[0] * (1 / m), + x.e[1] * (1 / m), + x.e[2] * (1 / m), + 0, + ]! + } +} + +// Module of the vector xyzw +pub fn (x Vec4) mod() f32 { + return f32(math.sqrt(x.e[0] * x.e[0] + x.e[1] * x.e[1] + x.e[2] * x.e[2] + x.e[3] * x.e[3])) +} + +// Module for 3d vector xyz, w ignored +pub fn (x Vec4) mod3() f32 { + return f32(math.sqrt(x.e[0] * x.e[0] + x.e[1] * x.e[1] + x.e[2] * x.e[2])) +} + +/********************************************************************* +* +* Math +* +*********************************************************************/ +// Return a zero vector +pub fn zero_v4() Vec4 { + return Vec4{ + e: [ + f32(0), + 0, + 0, + 0, + ]! + } +} + +// Return all one vector +pub fn one_v4() Vec4 { + return Vec4{ + e: [ + f32(1), + 1, + 1, + 1, + ]! + } +} + +// Return a blank vector +pub fn blank_v4() Vec4 { + return Vec4{ + e: [ + f32(0), + 0, + 0, + 1, + ]! + } +} + +// Set all elements to value +pub fn set_v4(value f32) Vec4 { + return Vec4{ + e: [ + value, + value, + value, + value, + ]! + } +} + +// Sum of all the elements +pub fn (x Vec4) sum() f32 { + return x.e[0] + x.e[1] + x.e[2] + x.e[3] +} + +/********************************************************************* +* +* Operators +* +*********************************************************************/ +// Addition +pub fn (a Vec4) + (b Vec4) Vec4 { + return Vec4{ + e: [ + a.e[0] + b.e[0], + a.e[1] + b.e[1], + a.e[2] + b.e[2], + a.e[3] + b.e[3], + ]! + } +} + +// Subtraction +pub fn (a Vec4) - (b Vec4) Vec4 { + return Vec4{ + e: [ + a.e[0] - b.e[0], + a.e[1] - b.e[1], + a.e[2] - b.e[2], + a.e[3] - b.e[3], + ]! + } +} + +// Dot product +pub fn (a Vec4) * (b Vec4) f32 { + return a.e[0] * b.e[0] + a.e[1] * b.e[1] + a.e[2] * b.e[2] + a.e[3] * b.e[3] +} + +// Cross product +pub fn (a Vec4) % (b Vec4) Vec4 { + return Vec4{ + e: [ + (a.e[1] * b.e[2]) - (a.e[2] * b.e[1]), + (a.e[2] * b.e[0]) - (a.e[0] * b.e[2]), + (a.e[0] * b.e[1]) - (a.e[1] * b.e[0]), + 0, + ]! + } +} + +// Components multiplication +pub fn (x Vec4) mul_vec4(y Vec4) Vec4 { + return Vec4{ + e: [ + x.e[0] * y.e[0], + x.e[1] * y.e[1], + x.e[2] * y.e[2], + x.e[3] * y.e[3], + ]! + } +} diff --git a/v_windows/v/old/vlib/gg/text_rendering.v b/v_windows/v/old/vlib/gg/text_rendering.v new file mode 100644 index 0000000..dd0499d --- /dev/null +++ b/v_windows/v/old/vlib/gg/text_rendering.v @@ -0,0 +1,370 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license that can be found in the LICENSE file. +module gg + +import sokol.sfons +import sokol.sgl +import gx +import os + +enum FontVariant { + normal = 0 + bold + mono + italic +} + +struct FT { +pub: + fons &C.FONScontext + font_normal int + font_bold int + font_mono int + font_italic int + scale f32 = 1.0 +} + +struct FTConfig { + font_path string + custom_bold_font_path string + scale f32 = 1.0 + font_size int + bytes_normal []byte + bytes_bold []byte + bytes_mono []byte + bytes_italic []byte +} + +struct StringToRender { + x int + y int + text string + cfg gx.TextCfg +} + +fn new_ft(c FTConfig) ?&FT { + if c.font_path == '' { + if c.bytes_normal.len > 0 { + fons := sfons.create(512, 512, 1) + bytes_normal := c.bytes_normal + bytes_bold := if c.bytes_bold.len > 0 { + c.bytes_bold + } else { + debug_font_println('setting bold variant to normal') + bytes_normal + } + bytes_mono := if c.bytes_mono.len > 0 { + c.bytes_mono + } else { + debug_font_println('setting mono variant to normal') + bytes_normal + } + bytes_italic := if c.bytes_italic.len > 0 { + c.bytes_italic + } else { + debug_font_println('setting italic variant to normal') + bytes_normal + } + + return &FT{ + fons: fons + font_normal: C.fonsAddFontMem(fons, c'sans', bytes_normal.data, bytes_normal.len, + false) + font_bold: C.fonsAddFontMem(fons, c'sans', bytes_bold.data, bytes_bold.len, + false) + font_mono: C.fonsAddFontMem(fons, c'sans', bytes_mono.data, bytes_mono.len, + false) + font_italic: C.fonsAddFontMem(fons, c'sans', bytes_italic.data, bytes_italic.len, + false) + scale: c.scale + } + } else { + // Load default font + } + } + + if c.font_path == '' || !os.exists(c.font_path) { + $if !android { + println('failed to load font "$c.font_path"') + return none + } + } + + mut bytes := []byte{} + $if android { + // First try any filesystem paths + bytes = os.read_bytes(c.font_path) or { []byte{} } + if bytes.len == 0 { + // ... then try the APK asset path + bytes = os.read_apk_asset(c.font_path) or { + println('failed to load font "$c.font_path"') + return none + } + } + } $else { + bytes = os.read_bytes(c.font_path) or { + println('failed to load font "$c.font_path"') + return none + } + } + bold_path := if c.custom_bold_font_path != '' { + c.custom_bold_font_path + } else { + get_font_path_variant(c.font_path, .bold) + } + bytes_bold := os.read_bytes(bold_path) or { + debug_font_println('failed to load font "$bold_path"') + bytes + } + mono_path := get_font_path_variant(c.font_path, .mono) + bytes_mono := os.read_bytes(mono_path) or { + debug_font_println('failed to load font "$mono_path"') + bytes + } + italic_path := get_font_path_variant(c.font_path, .italic) + bytes_italic := os.read_bytes(italic_path) or { + debug_font_println('failed to load font "$italic_path"') + bytes + } + fons := sfons.create(512, 512, 1) + return &FT{ + fons: fons + font_normal: C.fonsAddFontMem(fons, c'sans', bytes.data, bytes.len, false) + font_bold: C.fonsAddFontMem(fons, c'sans', bytes_bold.data, bytes_bold.len, false) + font_mono: C.fonsAddFontMem(fons, c'sans', bytes_mono.data, bytes_mono.len, false) + font_italic: C.fonsAddFontMem(fons, c'sans', bytes_italic.data, bytes_italic.len, + false) + scale: c.scale + } +} + +pub fn (ctx &Context) set_cfg(cfg gx.TextCfg) { + if !ctx.font_inited { + return + } + if cfg.bold { + ctx.ft.fons.set_font(ctx.ft.font_bold) + } else if cfg.mono { + ctx.ft.fons.set_font(ctx.ft.font_mono) + } else if cfg.italic { + ctx.ft.fons.set_font(ctx.ft.font_italic) + } else { + ctx.ft.fons.set_font(ctx.ft.font_normal) + } + scale := if ctx.ft.scale == 0 { f32(1) } else { ctx.ft.scale } + size := if cfg.mono { cfg.size - 2 } else { cfg.size } + ctx.ft.fons.set_size(scale * f32(size)) + C.fonsSetAlign(ctx.ft.fons, int(cfg.align) | int(cfg.vertical_align)) + color := C.sfons_rgba(cfg.color.r, cfg.color.g, cfg.color.b, cfg.color.a) + if cfg.color.a != 255 { + sgl.load_pipeline(ctx.timage_pip) + } + C.fonsSetColor(ctx.ft.fons, color) + ascender := f32(0.0) + descender := f32(0.0) + lh := f32(0.0) + ctx.ft.fons.vert_metrics(&ascender, &descender, &lh) +} + +pub fn (ctx &Context) draw_text(x int, y int, text_ string, cfg gx.TextCfg) { + $if macos { + if ctx.native_rendering { + if cfg.align == gx.align_right { + width := ctx.text_width(text_) + C.darwin_draw_string(x - width, ctx.height - y, text_, cfg) + } else { + C.darwin_draw_string(x, ctx.height - y, text_, cfg) + } + return + } + } + if !ctx.font_inited { + eprintln('gg: draw_text(): font not initialized') + return + } + // text := text_.trim_space() // TODO remove/optimize + // mut text := text_ + // if text.contains('\t') { + // text = text.replace('\t', ' ') + // } + ctx.set_cfg(cfg) + scale := if ctx.ft.scale == 0 { f32(1) } else { ctx.ft.scale } + C.fonsDrawText(ctx.ft.fons, x * scale, y * scale, &char(text_.str), 0) // TODO: check offsets/alignment +} + +pub fn (ctx &Context) draw_text_def(x int, y int, text string) { + ctx.draw_text(x, y, text) +} + +/* +pub fn (mut gg FT) init_font() { +} +*/ +pub fn (ft &FT) flush() { + sfons.flush(ft.fons) +} + +pub fn (ctx &Context) text_width(s string) int { + $if macos { + if ctx.native_rendering { + return C.darwin_text_width(s) + } + } + // ctx.set_cfg(cfg) TODO + if !ctx.font_inited { + return 0 + } + mut buf := [4]f32{} + C.fonsTextBounds(ctx.ft.fons, 0, 0, &char(s.str), 0, &buf[0]) + if s.ends_with(' ') { + return int((buf[2] - buf[0]) / ctx.scale) + + ctx.text_width('i') // TODO fix this in fontstash? + } + res := int((buf[2] - buf[0]) / ctx.scale) + // println('TW "$s" = $res') + $if macos { + if ctx.native_rendering { + return res * 2 + } + } + return int((buf[2] - buf[0]) / ctx.scale) +} + +pub fn (ctx &Context) text_height(s string) int { + // ctx.set_cfg(cfg) TODO + if !ctx.font_inited { + return 0 + } + mut buf := [4]f32{} + C.fonsTextBounds(ctx.ft.fons, 0, 0, &char(s.str), 0, &buf[0]) + return int((buf[3] - buf[1]) / ctx.scale) +} + +pub fn (ctx &Context) text_size(s string) (int, int) { + // ctx.set_cfg(cfg) TODO + if !ctx.font_inited { + return 0, 0 + } + mut buf := [4]f32{} + C.fonsTextBounds(ctx.ft.fons, 0, 0, &char(s.str), 0, &buf[0]) + return int((buf[2] - buf[0]) / ctx.scale), int((buf[3] - buf[1]) / ctx.scale) +} + +pub fn system_font_path() string { + env_font := os.getenv('VUI_FONT') + if env_font != '' && os.exists(env_font) { + return env_font + } + $if windows { + return 'C:\\Windows\\Fonts\\arial.ttf' + } + mut fonts := ['Ubuntu-R.ttf', 'Arial.ttf', 'LiberationSans-Regular.ttf', 'NotoSans-Regular.ttf', + 'FreeSans.ttf', 'DejaVuSans.ttf'] + $if macos { + fonts = ['/System/Library/Fonts/SFNS.ttf', '/System/Library/Fonts/SFNSText.ttf', + '/Library/Fonts/Arial.ttf', + ] + for font in fonts { + if os.is_file(font) { + return font + } + } + } + $if android { + xml_files := ['/system/etc/system_fonts.xml', '/system/etc/fonts.xml', + '/etc/system_fonts.xml', '/etc/fonts.xml', '/data/fonts/fonts.xml', + '/etc/fallback_fonts.xml', + ] + font_locations := ['/system/fonts', '/data/fonts'] + for xml_file in xml_files { + if os.is_file(xml_file) && os.is_readable(xml_file) { + xml := os.read_file(xml_file) or { continue } + lines := xml.split('\n') + mut candidate_font := '' + for line in lines { + if line.contains('').all_before('<').trim(' \n\t\r') + if candidate_font.contains('.ttf') { + for location in font_locations { + candidate_path := os.join_path(location, candidate_font) + if os.is_file(candidate_path) && os.is_readable(candidate_path) { + return candidate_path + } + } + } + } + } + } + } + } + s := os.execute('fc-list') + if s.exit_code != 0 { + panic('failed to fetch system fonts') + } + system_fonts := s.output.split('\n') + for line in system_fonts { + for font in fonts { + if line.contains(font) && line.contains(':') { + res := line.all_before(':') + println('Using font $res') + return res + } + } + } + panic('failed to init the font') +} + +fn get_font_path_variant(font_path string, variant FontVariant) string { + // TODO: find some way to make this shorter and more eye-pleasant + // NotoSans, LiberationSans, DejaVuSans, Arial and SFNS should work + mut file := os.file_name(font_path) + mut fpath := font_path.replace(file, '') + file = file.replace('.ttf', '') + + match variant { + .normal {} + .bold { + if fpath.ends_with('-Regular') { + file = file.replace('-Regular', '-Bold') + } else if file.starts_with('DejaVuSans') { + file += '-Bold' + } else if file.to_lower().starts_with('arial') { + file += 'bd' + } else { + file += '-bold' + } + $if macos { + if os.exists('SFNS-bold') { + file = 'SFNS-bold' + } + } + } + .italic { + if file.ends_with('-Regular') { + file = file.replace('-Regular', '-Italic') + } else if file.starts_with('DejaVuSans') { + file += '-Oblique' + } else if file.to_lower().starts_with('arial') { + file += 'i' + } else { + file += 'Italic' + } + } + .mono { + if !file.ends_with('Mono-Regular') && file.ends_with('-Regular') { + file = file.replace('-Regular', 'Mono-Regular') + } else if file.to_lower().starts_with('arial') { + // Arial has no mono variant + } else { + file += 'Mono' + } + } + } + return fpath + file + '.ttf' +} + +fn debug_font_println(s string) { + $if debug_font ? { + println(s) + } +} diff --git a/v_windows/v/old/vlib/glm/glm.v b/v_windows/v/old/vlib/glm/glm.v new file mode 100644 index 0000000..abd7981 --- /dev/null +++ b/v_windows/v/old/vlib/glm/glm.v @@ -0,0 +1,428 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module glm + +import math + +/* +#flag -lmyglm +# f32* myglm_ortho(f32, f32, f32, f32); +# f32* myglm_translate(f32, f32, f32); +*/ +// # f32* myglm_rotate(f32 *m, f32 angle, f32, f32, f32); +// # f32* myglm_perspective(f32, f32, f32, f32); +// # f32* myglm_look_at(glm__Vec3, glm__Vec3, glm__Vec3); +// # glm__Vec3 myglm_mult(glm__Vec3, glm__Vec3); +// # glm__Vec3 myglm_cross(glm__Vec3, glm__Vec3); +// # glm__Vec3 myglm_normalize(glm__Vec3); +pub struct Mat4 { +pub: + data &f32 +} + +struct Vec2 { + x f32 + y f32 +} + +struct Vec3 { + x f32 + y f32 + z f32 +} + +pub fn vec3(x f32, y f32, z f32) Vec3 { + res := Vec3{ + x: x + y: y + z: z + } + return res +} + +fn mat4(f &f32) Mat4 { + res := Mat4{ + data: unsafe { f } + } + return res +} + +pub fn (v Vec3) str() string { + return 'Vec3{ $v.x, $v.y, $v.z }' +} + +pub fn (v Vec2) str() string { + return 'Vec3{ $v.x, $v.y }' +} + +pub fn (m Mat4) str() string { + mut s := '[ ' + for i in 0 .. 4 { + if i != 0 { + s += ' ' + } + for j in 0 .. 4 { + val := unsafe {m.data[i * 4 + j]} + s += '${val:5.2f} ' + } + if i != 3 { + s += '\n' + } + } + s += ']' + return s +} + +fn vec2(x int, y int) Vec2 { + res := Vec2{ + x: f32(x) + y: f32(y) + } + return res +} + +fn (a Vec3) add(b Vec3) Vec3 { + res := Vec3{ + x: a.x + b.x + y: a.y + b.y + z: a.z + b.z + } + return res +} + +fn (a Vec3) sub(b Vec3) Vec3 { + res := Vec3{ + x: a.x - b.x + y: a.y - b.y + z: a.z - b.z + } + return res +} + +// fn (a Vec3) mult(b Vec3) Vec3 { +// # return myglm_mult(a,b); +// } +fn (a Vec3) mult_scalar(b f32) Vec3 { + res := Vec3{ + x: a.x * b + y: a.y * b + z: a.z * b + } + return res +} + +fn (a Vec3) print() { + x := a.x + y := a.y + z := a.z + C.printf(c'vec3{%f,%f,%f}\n', x, y, z) + // println('vec3{$x,$y,$z}') +} + +/* +fn rotate(m Mat4, angle f32, vec Vec3) Mat4 { + // # t_mat4 m; + // println('rotate done') + # return glm__mat4( myglm_rotate(m.data, angle, vec.x,vec.y,vec.z) ); + return Mat4{} +} +*/ +fn f32_calloc(n int) &f32 { + return voidptr(vcalloc_noscan(n * int(sizeof(f32)))) +} + +// fn translate(vec Vec3) *f32 { +pub fn translate(m Mat4, v Vec3) Mat4 { + // # return glm__mat4(myglm_translate(vec.x,vec.y,vec.z) ); + a := m.data + mut out := f32_calloc(16) + x := v.x + y := v.y + z := v.z + unsafe { + a00 := a[0] + a01 := a[1] + a02 := a[2] + a03 := a[3] + a10 := a[4] + a11 := a[5] + a12 := a[6] + a13 := a[7] + a20 := a[8] + a21 := a[9] + a22 := a[10] + a23 := a[11] + out[0] = a00 + out[1] = a01 + out[2] = a02 + out[3] = a03 + out[4] = a10 + out[5] = a11 + out[6] = a12 + out[7] = a13 + out[8] = a20 + out[9] = a21 + out[10] = a22 + out[11] = a23 + out[12] = a00 * x + a10 * y + a20 * z + a[12] + out[13] = a01 * x + a11 * y + a21 * z + a[13] + out[14] = a02 * x + a12 * y + a22 * z + a[14] + out[15] = a03 * x + a13 * y + a23 * z + a[15] + } + return mat4(out) +} + +/* +fn normalize(vec Vec3) Vec3 { + # return myglm_normalize(vec); + return Vec3{} +} +*/ +// https://github.com/g-truc/glm/blob/0ceb2b755fb155d593854aefe3e45d416ce153a4/glm/ext/matrix_clip_space.inl +pub fn ortho(left f32, right f32, bottom f32, top f32) Mat4 { + // println('glm ortho($left, $right, $bottom, $top)') + // mat<4, 4, T, defaultp> Result(static_cast(1)); + n := 16 + mut res := f32_calloc(n) + unsafe { + res[0] = 2.0 / (right - left) + res[5] = 2.0 / (top - bottom) + res[10] = 1.0 + res[12] = -(right + left) / (right - left) + res[13] = -(top + bottom) / (top - bottom) + res[15] = 1.0 + } + return mat4(res) +} + +// https://github.com/g-truc/glm/blob/0ceb2b755fb155d593854aefe3e45d416ce153a4/glm/ext/matrix_clip_space.inl +pub fn ortho_zo(left f32, right f32, bottom f32, top f32, zNear f32, zFar f32) Mat4 { + // println('glm ortho($left, $right, $bottom, $top)') + // mat<4, 4, T, defaultp> Result(static_cast(1)); + n := 16 + mut res := f32_calloc(n) + unsafe { + res[0] = 2.0 / (right - left) + res[5] = 2.0 / (top - bottom) + res[10] = 1.0 + res[12] = -(right + left) / (right - left) + res[13] = -(top + bottom) / (top - bottom) + res[14] = -zNear / (zFar - zNear) + res[15] = 1.0 + } + return mat4(res) +} + +// fn scale(a *f32, v Vec3) *f32 { +pub fn scale(m Mat4, v Vec3) Mat4 { + a := m.data + mut out := f32_calloc(16) + x := v.x + y := v.y + z := v.z + unsafe { + out[0] = a[0] * v.x + out[1] = a[1] * x + out[2] = a[2] * x + out[3] = a[3] * x + out[4] = a[4] * y + out[5] = a[5] * y + out[6] = a[6] * y + out[7] = a[7] * y + out[8] = a[8] * z + out[9] = a[9] * z + out[10] = a[10] * z + out[11] = a[11] * z + out[12] = a[12] + out[13] = a[13] + out[14] = a[14] + out[15] = a[15] + } + return mat4(out) +} + +// multiplies two matrices +pub fn mult(a Mat4, b Mat4) Mat4 { + mut out := f32_calloc(16) + for i in 0 .. 4 { + for r in 0 .. 4 { + mut prod := f32(0) + for c in 0 .. 4 { + prod += unsafe {a.data[c * 4 + r] * b.data[i * 4 + c]} + } + unsafe { + out[i * 4 + r] = prod + } + } + } + return mat4(out) +} + +pub fn rotate(angle f32, axis Vec3, src Mat4) Mat4 { + c := f32(math.cos(angle)) + s := f32(math.sin(angle)) + oneminusc := f32(1.0) - c + xy := axis.x * axis.y + yz := axis.y * axis.z + xz := axis.x * axis.z + xs := axis.x * s + ys := axis.y * s + zs := axis.z * s + f00 := axis.x * axis.x * oneminusc + c + f01 := xy * oneminusc + zs + f02 := xz * oneminusc - ys + f10 := xy * oneminusc - zs + f11 := axis.y * axis.y * oneminusc + c + f12 := yz * oneminusc + xs + f20 := xz * oneminusc + ys + f21 := yz * oneminusc - xs + f22 := axis.z * axis.z * oneminusc + c + data := src.data + unsafe { + t00 := data[0] * f00 + data[4] * f01 + data[8] * f02 + t01 := data[1] * f00 + data[5] * f01 + data[9] * f02 + t02 := data[2] * f00 + data[6] * f01 + data[10] * f02 + t03 := data[3] * f00 + data[7] * f01 + data[11] * f02 + t10 := data[0] * f10 + data[4] * f11 + data[8] * f12 + t11 := data[1] * f10 + data[5] * f11 + data[9] * f12 + t12 := data[2] * f10 + data[6] * f11 + data[10] * f12 + t13 := data[3] * f10 + data[7] * f11 + data[11] * f12 + mut dest := src.data + dest[8] = data[0] * f20 + data[4] * f21 + data[8] * f22 + dest[9] = data[1] * f20 + data[5] * f21 + data[9] * f22 + dest[10] = data[2] * f20 + data[6] * f21 + data[10] * f22 + dest[11] = data[3] * f20 + data[7] * f21 + data[11] * f22 + dest[0] = t00 + dest[1] = t01 + dest[2] = t02 + dest[3] = t03 + dest[4] = t10 + dest[5] = t11 + dest[6] = t12 + dest[7] = t13 + return mat4(dest) + } +} + +// fn rotate_z(a *f32, rad f32) *f32 { +pub fn rotate_z(m Mat4, rad f32) Mat4 { + a := m.data + mut out := f32_calloc(16) + s := f32(math.sin(rad)) + c := f32(math.cos(rad)) + unsafe { + a00 := a[0] + a01 := a[1] + a02 := a[2] + a03 := a[3] + a10 := a[4] + a11 := a[5] + a12 := a[6] + a13 := a[7] + out[8] = a[8] + out[9] = a[9] + out[10] = a[10] + out[11] = a[11] + out[12] = a[12] + out[13] = a[13] + out[14] = a[14] + out[15] = a[15] + // Perform axis-specific matrix multiplication + out[0] = a00 * c + a10 * s + out[1] = a01 * c + a11 * s + out[2] = a02 * c + a12 * s + out[3] = a03 * c + a13 * s + out[4] = a10 * c - a00 * s + out[5] = a11 * c - a01 * s + out[6] = a12 * c - a02 * s + out[7] = a13 * c - a03 * s + } + return mat4(out) +} + +pub fn identity() Mat4 { + // 1 0 0 0 + // 0 1 0 0 + // 0 0 1 0 + // 0 0 0 1 + n := 16 + mut res := f32_calloc(int(sizeof(f32)) * n) + unsafe { + res[0] = 1 + res[5] = 1 + res[10] = 1 + res[15] = 1 + } + return mat4(res) +} + +// returns *f32 without allocation +pub fn identity2(mut res &f32) { + res[0] = 1 + res[5] = 1 + res[10] = 1 + res[15] = 1 + // # f32 f[16]={0};// for (int i =0;i<16;i++) + // # printf("!!%d\n", f[0]); + // # glm__identity2(&f); + // # gl__Shader_set_mat4(shader, tos2("projection"), f) ; +} + +pub fn identity3() []f32 { + res := [f32(1.0), 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] + return res +} + +// https://github.com/toji/gl-matrix/blob/1549cf21dfa14a2bc845993485343d519cf064fe/src/gl-matrix/mat4.js +fn ortho_js(left f32, right f32, bottom f32, top f32) &f32 { + // mynear := 1 + // myfar := 1 + lr := 1.0 / (left - right) + bt := 1.0 / (bottom - top) + nf := f32(1.0) / 1.0 // (mynear -myfar) + unsafe { + mut out := &f32(malloc_noscan(int(sizeof(f32) * 16))) + out[0] = -2.0 * lr + out[1] = 0 + out[2] = 0 + out[3] = 0 + out[4] = 0 + out[5] = -2.0 * bt + out[6] = 0 + out[7] = 0 + out[8] = 0 + out[9] = 0 + out[10] = 2.0 * nf + out[11] = 0 + out[12] = (left + right) * lr + out[13] = (top + bottom) * bt + out[14] = 1.0 * nf // (far + near) * nf; + out[15] = 1 + return out + } + // f := 0.0 + // return &f +} + +// fn ortho_old(a, b, c, d f32) *f32 { +// # return myglm_ortho(a,b,c,d); +// } +fn cross(a Vec3, b Vec3) Vec3 { + // # return myglm_cross(a,b); + return Vec3{} +} + +/* +fn perspective(degrees f32, ratio f32, a, b f32) Mat4 { + // println('lang per degrees=$degrees ratio=$ratio a=$a b=$b') + // # printf("lang pers degrees=%f ratio=%f a=%f b=%f\n", degrees, ratio, a,b); + # return glm__mat4( myglm_perspective(degrees, ratio, a,b) ) ; + return Mat4{} +} + +fn look_at(eye, center, up Vec3) Mat4 { + # return glm__mat4( myglm_look_at(eye, center, up) ) ; + return Mat4{} +} +*/ diff --git a/v_windows/v/old/vlib/glm/glm_test.v b/v_windows/v/old/vlib/glm/glm_test.v new file mode 100644 index 0000000..c2a4266 --- /dev/null +++ b/v_windows/v/old/vlib/glm/glm_test.v @@ -0,0 +1,155 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +// might need special case for this +// import gl +import glm + +fn cmp(a f32, b f32) bool { + return int(a * 1000) == int(b * 1000) +} + +fn test_ortho() { + projection := glm.ortho(0, 200, 400, 0) + $if debug { + println(unsafe { projection.data[0] }) + } + unsafe { + assert cmp(projection.data[0], 0.01) + assert cmp(projection.data[1], 0.000000) + assert cmp(projection.data[2], 0.000000) + assert cmp(projection.data[3], 0.000000) + assert cmp(projection.data[4], 0.000000) + assert cmp(projection.data[5], -0.005000) + assert cmp(projection.data[6], 0.000000) + assert cmp(projection.data[7], 0.000000) + assert cmp(projection.data[8], 0.000000) + assert cmp(projection.data[9], 0.000000) + assert cmp(projection.data[10], 1.000000) + assert cmp(projection.data[11], 0.000000) + assert cmp(projection.data[12], -1.000000) + assert cmp(projection.data[13], 1.000000) + assert cmp(projection.data[14], 0.000000) + assert cmp(projection.data[15], 1.000000) + } + // f := gg.ortho(1,2,3,4) + /* + // for debugging broken tetris in gg.o + # projection.data[0]=0.010000; + # projection.data[1]=0.000000; + # projection.data[2]=0.000000; + # projection.data[3]=0.000000; + # projection.data[4]=0.000000; + # projection.data[5]=-0.005000; + # projection.data[6]=0.000000; + # projection.data[7]=0.000000; + # projection.data[8]=0.000000; + # projection.data[9]=0.000000; + # projection.data[10]=1.000000; + # projection.data[11]=0.000000; + # projection.data[12]=-1.000000; + # projection.data[13]=1.000000; + # projection.data[14]=0.000000; + # projection.data[15]=1.000000; + */ +} + +fn test_rotate() { + $if debug { + println('rotate') + } + mut m := glm.identity() + m = glm.scale(m, glm.vec3(2, 2, 2)) + $if debug { + println(m) + } + m = glm.rotate_z(m, 1) + $if debug { + println(m) + } + mut m1 := glm.identity() + mut m2 := glm.identity() + m1 = glm.rotate(1, glm.vec3(1, 0, 0), m1) + m2 = glm.rotate(1, glm.vec3(0, 1, 0), m2) + mut same := true + for i in 0 .. 15 { + if unsafe { m1.data[i] } != unsafe { m2.data[i] } { + same = false + } + } + assert !same +} + +fn test_translate() { + mut m := glm.identity() + m = glm.translate(m, glm.vec3(0, 0, -0.5)) + $if debug { + println(m) + } + unsafe { + assert m.data[0] == 1.0 + assert m.data[1] == 0.0 + assert m.data[2] == 0.0 + assert m.data[3] == 0.0 + // + assert m.data[4] == 0.0 + assert m.data[5] == 1.0 + assert m.data[6] == 0.0 + assert m.data[7] == 0.0 + assert m.data[8] == 0.0 + assert m.data[9] == 0.0 + assert m.data[10] == 1.0 + assert m.data[11] == 0.0 + // + assert m.data[12] == 0.0 + assert m.data[13] == 0.0 + assert m.data[14] == -0.5 + assert m.data[15] == 1.0 + } +} + +fn f32_calloc(n int) &f32 { + return voidptr(vcalloc(n * int(sizeof(f32)))) +} + +fn test_mult1() { + mut adata := f32_calloc(16) + unsafe { + adata[1 * 4 + 1] = 6 + adata[2 * 4 + 3] = 2 + adata[0 * 4 + 2] = 3 + adata[2 * 4 + 1] = 1 + } + mut bdata := f32_calloc(16) + unsafe { + bdata[1 * 4 + 1] = -2 + bdata[2 * 4 + 3] = 1 + bdata[0 * 4 + 2] = 6 + bdata[2 * 4 + 1] = -3 + } + mut expected := f32_calloc(16) + unsafe { + expected[0 * 4 + 0] = 0 // 0*0+0*0+0*6+0*0 + expected[0 * 4 + 1] = 6 // 0*0+0*6+1*6+0*0 + expected[0 * 4 + 2] = 0 // 3*0+0*0+0*6+0*0 + expected[0 * 4 + 3] = 12 // 0*0+0*0+2*6+0*0 + expected[1 * 4 + 0] = 0 // 0*0+0*-2+0*0+0*0 + expected[1 * 4 + 1] = -12 // 0*0­+6*-2+1*0­+0*0 + expected[1 * 4 + 2] = 0 // 3*0­+0*-2­+0*0­+0*0 + expected[1 * 4 + 3] = 0 // 0*0­+0*-2­+2*0­+0*0 + expected[2 * 4 + 0] = 0 // 0*0­+0*-3­+0*0­+0*1 + expected[2 * 4 + 1] = -18 // 0*0­+6*-3­+1*0­+0*1 + expected[2 * 4 + 2] = 0 // 3*0­+0*-3+0*0­+0*1 + expected[2 * 4 + 3] = 0 // 0*0­+0*-3­+2*0­+0*1 + expected[3 * 4 + 0] = 0 // 0*0­+0*0­+0*0­+0*0 + expected[3 * 4 + 1] = 0 // 0*0­+6*0­+1*0­+0*0 + expected[3 * 4 + 2] = 0 // 3*0­+0*0­+0*0­+0*0 + expected[3 * 4 + 3] = 0 // 0*0­+0*0­+2*0­+0*0 + } + mut a := glm.Mat4{adata} + b := glm.Mat4{bdata} + a = glm.mult(a, b) + for i in 0 .. 15 { + assert unsafe { a.data[i] } == unsafe { expected[i] } + } +} diff --git a/v_windows/v/old/vlib/gx/color.v b/v_windows/v/old/vlib/gx/color.v new file mode 100644 index 0000000..eefebb8 --- /dev/null +++ b/v_windows/v/old/vlib/gx/color.v @@ -0,0 +1,234 @@ +module gx + +pub const ( + blue = Color{ + r: 0 + g: 0 + b: 255 + } + red = Color{ + r: 255 + g: 0 + b: 0 + } + green = Color{ + r: 0 + g: 255 + b: 0 + } + yellow = Color{ + r: 255 + g: 255 + b: 0 + } + orange = Color{ + r: 255 + g: 165 + b: 0 + } + purple = Color{ + r: 128 + g: 0 + b: 128 + } + black = Color{ + r: 0 + g: 0 + b: 0 + } + gray = Color{ + r: 128 + g: 128 + b: 128 + } + indigo = Color{ + r: 75 + g: 0 + b: 130 + } + pink = Color{ + r: 255 + g: 192 + b: 203 + } + violet = Color{ + r: 238 + g: 130 + b: 238 + } + white = Color{ + r: 255 + g: 255 + b: 255 + } + dark_blue = Color{ + r: 0 + g: 0 + b: 139 + } + dark_gray = Color{ + r: 169 + g: 169 + b: 169 + } + dark_green = Color{ + r: 0 + g: 100 + b: 0 + } + dark_red = Color{ + r: 139 + g: 0 + b: 0 + } + light_blue = Color{ + r: 173 + g: 216 + b: 230 + } + light_gray = Color{ + r: 211 + g: 211 + b: 211 + } + light_green = Color{ + r: 144 + g: 238 + b: 144 + } + light_red = Color{ + r: 255 + g: 204 + b: 203 + } +) + +// Color represents a 32 bit color value in sRGB format +pub struct Color { +pub mut: + r byte + g byte + b byte + a byte = 255 +} + +// hex takes in a 32 bit integer and splits it into 4 byte values +pub fn hex(color int) Color { + return Color{ + r: byte((color >> 24) & 0xFF) + g: byte((color >> 16) & 0xFF) + b: byte((color >> 8) & 0xFF) + a: byte(color & 0xFF) + } +} + +pub fn rgb(r byte, g byte, b byte) Color { + return Color{ + r: r + g: g + b: b + } +} + +pub fn rgba(r byte, g byte, b byte, a byte) Color { + return Color{ + r: r + g: g + b: b + a: a + } +} + +pub fn (c Color) + (c2 Color) Color { + return Color{ + r: c.r + c2.r + g: c.g + c2.g + b: c.b + c2.b + a: c.b + c2.a + } +} + +pub fn (c Color) - (c2 Color) Color { + return Color{ + r: c.r - c2.r + g: c.g - c2.g + b: c.b - c2.b + a: c.b - c2.a + } +} + +pub fn (c Color) * (c2 Color) Color { + return Color{ + r: c.r * c2.r + g: c.g * c2.g + b: c.b * c2.b + a: c.b * c2.a + } +} + +pub fn (c Color) / (c2 Color) Color { + return Color{ + r: c.r / c2.r + g: c.g / c2.g + b: c.b / c2.b + a: c.b / c2.a + } +} + +pub fn (c Color) eq(c2 Color) bool { + return c.r == c2.r && c.g == c2.g && c.b == c2.b && c.a == c2.a +} + +pub fn (c Color) str() string { + return 'Color{$c.r, $c.g, $c.b, $c.a}' +} + +// rgba8 - convert a color value to an int in the RGBA8 order. +// see https://developer.apple.com/documentation/coreimage/ciformat +[inline] +pub fn (c Color) rgba8() int { + return (int(c.r) << 24) + (int(c.g) << 16) + (int(c.b) << 8) + int(c.a) +} + +// bgra8 - convert a color value to an int in the BGRA8 order. +// see https://developer.apple.com/documentation/coreimage/ciformat +[inline] +pub fn (c Color) bgra8() int { + return (int(c.b) << 24) + (int(c.g) << 16) + (int(c.r) << 8) + int(c.a) +} + +// abgr8 - convert a color value to an int in the ABGR8 order. +// see https://developer.apple.com/documentation/coreimage/ciformat +[inline] +pub fn (c Color) abgr8() int { + return (int(c.a) << 24) + (int(c.b) << 16) + (int(c.g) << 8) + int(c.r) +} + +const ( + string_colors = map{ + 'blue': blue + 'red': red + 'green': green + 'yellow': yellow + 'orange': orange + 'purple': purple + 'black': black + 'gray': gray + 'indigo': indigo + 'pink': pink + 'violet': violet + 'white': white + 'dark_blue': dark_blue + 'dark_gray': dark_gray + 'dark_green': dark_green + 'dark_red': dark_red + 'light_blue': light_blue + 'light_gray': light_gray + 'light_green': light_green + 'light_red': light_red + } +) + +pub fn color_from_string(s string) Color { + return gx.string_colors[s] +} diff --git a/v_windows/v/old/vlib/gx/color_test.v b/v_windows/v/old/vlib/gx/color_test.v new file mode 100644 index 0000000..533a41d --- /dev/null +++ b/v_windows/v/old/vlib/gx/color_test.v @@ -0,0 +1,63 @@ +import gx + +fn test_hex() { + // valid colors + a := gx.hex(0x6c5ce7ff) + b := gx.rgba(108, 92, 231, 255) + assert a.eq(b) + // doesn't give right value with short hex value + short := gx.hex(0xfff) + assert !short.eq(gx.white) +} + +fn test_add() { + a := gx.rgba(100, 100, 100, 100) + b := gx.rgba(100, 100, 100, 100) + r := gx.rgba(200, 200, 200, 200) + assert (a + b).eq(r) +} + +fn test_sub() { + a := gx.rgba(100, 100, 100, 100) + b := gx.rgba(100, 100, 100, 100) + r := gx.rgba(0, 0, 0, 0) + assert (a - b).eq(r) +} + +fn test_mult() { + a := gx.rgba(10, 10, 10, 10) + b := gx.rgba(10, 10, 10, 10) + r := gx.rgba(100, 100, 100, 100) + assert (a * b).eq(r) +} + +fn test_div() { + a := gx.rgba(100, 100, 100, 100) + b := gx.rgba(10, 10, 10, 10) + r := gx.rgba(10, 10, 10, 10) + assert (a / b).eq(r) +} + +fn test_rgba8() { + assert gx.white.rgba8() == -1 + assert gx.black.rgba8() == 255 + assert gx.red.rgba8() == -16776961 + assert gx.green.rgba8() == 16711935 + assert gx.blue.rgba8() == 65535 +} + +fn test_bgra8() { + assert gx.white.bgra8() == -1 + assert gx.black.bgra8() == 255 + assert gx.red.bgra8() == 65535 + assert gx.green.bgra8() == 16711935 + assert gx.blue.bgra8() == -16776961 +} + +fn test_abgr8() { + assert gx.white.abgr8() == -1 + assert gx.black.abgr8() == -16777216 + assert gx.red.abgr8() == -16776961 + assert gx.green.abgr8() == -16711936 + assert gx.blue.abgr8() == -65536 +} diff --git a/v_windows/v/old/vlib/gx/image.v b/v_windows/v/old/vlib/gx/image.v new file mode 100644 index 0000000..4a20ea1 --- /dev/null +++ b/v_windows/v/old/vlib/gx/image.v @@ -0,0 +1,14 @@ +module gx + +pub struct Image { +mut: + obj voidptr +pub: + id int + width int + height int +} + +pub fn (i Image) is_empty() bool { + return isnil(i.obj) +} diff --git a/v_windows/v/old/vlib/gx/text.v b/v_windows/v/old/vlib/gx/text.v new file mode 100644 index 0000000..53c4bd4 --- /dev/null +++ b/v_windows/v/old/vlib/gx/text.v @@ -0,0 +1,39 @@ +module gx + +import fontstash + +const ( + used_import = fontstash.used_import +) + +// TODO: remove these and uae the enum everywhere +pub const ( + align_left = HorizontalAlign.left + align_right = HorizontalAlign.right +) + +pub enum HorizontalAlign { + left = C.FONS_ALIGN_LEFT + center = C.FONS_ALIGN_CENTER + right = C.FONS_ALIGN_RIGHT +} + +pub enum VerticalAlign { + top = C.FONS_ALIGN_TOP + middle = C.FONS_ALIGN_MIDDLE + bottom = C.FONS_ALIGN_BOTTOM + baseline = C.FONS_ALIGN_BASELINE +} + +pub struct TextCfg { +pub: + color Color = black + size int = 16 + align HorizontalAlign = .left + vertical_align VerticalAlign = .top + max_width int + family string + bold bool + mono bool + italic bool +} diff --git a/v_windows/v/old/vlib/hash/crc32/crc32.v b/v_windows/v/old/vlib/hash/crc32/crc32.v new file mode 100644 index 0000000..d29aa12 --- /dev/null +++ b/v_windows/v/old/vlib/hash/crc32/crc32.v @@ -0,0 +1,63 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. + +// This is a very basic crc32 implementation +// at the moment with no architecture optimizations +module crc32 + +// polynomials +pub const ( + ieee = u32(0xedb88320) + castagnoli = u32(0x82f63b78) + koopman = u32(0xeb31d82e) +) + +// The size of a CRC-32 checksum in bytes. +const ( + size = 4 +) + +struct Crc32 { +mut: + table []u32 +} + +fn (mut c Crc32) generate_table(poly int) { + for i in 0 .. 256 { + mut crc := u32(i) + for _ in 0 .. 8 { + if crc & u32(1) == u32(1) { + crc = (crc >> 1) ^ u32(poly) + } else { + crc >>= u32(1) + } + } + c.table << crc + } +} + +fn (c &Crc32) sum32(b []byte) u32 { + mut crc := ~u32(0) + for i in 0 .. b.len { + crc = c.table[byte(crc) ^ b[i]] ^ (crc >> 8) + } + return ~crc +} + +pub fn (c &Crc32) checksum(b []byte) u32 { + return c.sum32(b) +} + +// pass the polynomial to use +pub fn new(poly int) &Crc32 { + mut c := &Crc32{} + c.generate_table(poly) + return c +} + +// calculate crc32 using ieee +pub fn sum(b []byte) u32 { + c := new(int(crc32.ieee)) + return c.sum32(b) +} diff --git a/v_windows/v/old/vlib/hash/crc32/crc32_test.v b/v_windows/v/old/vlib/hash/crc32/crc32_test.v new file mode 100644 index 0000000..7355179 --- /dev/null +++ b/v_windows/v/old/vlib/hash/crc32/crc32_test.v @@ -0,0 +1,14 @@ +import hash.crc32 + +fn test_hash_crc32() { + b1 := 'testing crc32'.bytes() + sum1 := crc32.sum(b1) + assert sum1 == u32(1212124400) + assert sum1.hex() == '483f8cf0' + + c := crc32.new(int(crc32.ieee)) + b2 := 'testing crc32 again'.bytes() + sum2 := c.checksum(b2) + assert sum2 == u32(1420327025) + assert sum2.hex() == '54a87871' +} diff --git a/v_windows/v/old/vlib/hash/fnv1a/fnv1a.v b/v_windows/v/old/vlib/hash/fnv1a/fnv1a.v new file mode 100644 index 0000000..275c8a2 --- /dev/null +++ b/v_windows/v/old/vlib/hash/fnv1a/fnv1a.v @@ -0,0 +1,44 @@ +module fnv1a + +const ( + fnv64_prime = u64(1099511628211) + fnv64_offset_basis = u64(14695981039346656037) + fnv32_offset_basis = u32(2166136261) + fnv32_prime = u32(16777619) +) + +[inline] +pub fn sum32_string(data string) u32 { + mut hash := fnv1a.fnv32_offset_basis + for i in 0 .. data.len { + hash = (hash ^ u32(data[i])) * fnv1a.fnv32_prime + } + return hash +} + +[inline] +pub fn sum32(data []byte) u32 { + mut hash := fnv1a.fnv32_offset_basis + for i in 0 .. data.len { + hash = (hash ^ u32(data[i])) * fnv1a.fnv32_prime + } + return hash +} + +[inline] +pub fn sum64_string(data string) u64 { + mut hash := fnv1a.fnv64_offset_basis + for i in 0 .. data.len { + hash = (hash ^ u64(data[i])) * fnv1a.fnv64_prime + } + return hash +} + +[inline] +pub fn sum64(data []byte) u64 { + mut hash := fnv1a.fnv64_offset_basis + for i in 0 .. data.len { + hash = (hash ^ u64(data[i])) * fnv1a.fnv64_prime + } + return hash +} diff --git a/v_windows/v/old/vlib/hash/fnv1a/fnv1a_test.v b/v_windows/v/old/vlib/hash/fnv1a/fnv1a_test.v new file mode 100644 index 0000000..f775ab1 --- /dev/null +++ b/v_windows/v/old/vlib/hash/fnv1a/fnv1a_test.v @@ -0,0 +1,12 @@ +import hash.fnv1a + +fn test_fnv1a() { + $if windows { + return + } + a := 'apple' + b := fnv1a.sum64_string(a) + c := fnv1a.sum64(a.bytes()) + assert b.hex() == 'f74a62a458befdbf' + assert c.hex() == 'f74a62a458befdbf' +} diff --git a/v_windows/v/old/vlib/hash/hash.v b/v_windows/v/old/vlib/hash/hash.v new file mode 100644 index 0000000..eabb166 --- /dev/null +++ b/v_windows/v/old/vlib/hash/hash.v @@ -0,0 +1,20 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module hash + +interface Hasher { + // Sum appends the current hash to b and returns the resulting array. + // It does not change the underlying hash state. + sum(b []byte) []byte + size() int + block_size() int +} + +interface Hash32er { + sum32() u32 +} + +interface Hash64er { + sum64() u64 +} diff --git a/v_windows/v/old/vlib/hash/wyhash.c.v b/v_windows/v/old/vlib/hash/wyhash.c.v new file mode 100644 index 0000000..758af03 --- /dev/null +++ b/v_windows/v/old/vlib/hash/wyhash.c.v @@ -0,0 +1,23 @@ +module hash + +//#flag -I @VEXEROOT/thirdparty/wyhash +//#include "wyhash.h" +fn C.wyhash(&byte, u64, u64, &u64) u64 + +fn C.wyhash64(u64, u64) u64 + +fn init() { + _ := map{ + 1: 1 + } +} + +[inline] +pub fn wyhash_c(key &byte, len u64, seed u64) u64 { + return C.wyhash(key, len, seed, &u64(C._wyp)) +} + +[inline] +pub fn wyhash64_c(a u64, b u64) u64 { + return C.wyhash64(a, b) +} diff --git a/v_windows/v/old/vlib/hash/wyhash.v b/v_windows/v/old/vlib/hash/wyhash.v new file mode 100644 index 0000000..bd89c42 --- /dev/null +++ b/v_windows/v/old/vlib/hash/wyhash.v @@ -0,0 +1,82 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +// +// this is an implementation of wyhash v4 +// from https://github.com/wangyi-fudan/wyhash +// +// TODO: use u128 once implemented +// currently the C version performs slightly better +// because it uses 128 bit int when available and +// branch prediction hints. the C version will be +// removed once the perfomance is matched. +// you can test performance by running: +// `v run cmd/tools/bench/wyhash.v` +// try running with and without the `-prod` flag +module hash + +const ( + wyp0 = u64(0xa0761d6478bd642f) + wyp1 = u64(0xe7037ed1a0b428db) + wyp2 = u64(0x8ebc6af09c88c6e3) + wyp3 = u64(0x589965cc75374cc3) + wyp4 = u64(0x1d8e4e27c47d124f) +) + +[inline] +pub fn sum64_string(key string, seed u64) u64 { + return wyhash_c(key.str, u64(key.len), seed) +} + +[inline] +pub fn sum64(key []byte, seed u64) u64 { + return wyhash_c(&byte(key.data), u64(key.len), seed) +} + +[inline] +fn wyrotr(v u64, k u32) u64 { + return (v >> k) | (v << (64 - k)) +} + +[inline] +pub fn wymum(a u64, b u64) u64 { + /* + mut r := u128(a) + r = r*b + return (r>>64)^r + */ + mask32 := u32(4294967295) + x0 := a & mask32 + x1 := a >> 32 + y0 := b & mask32 + y1 := b >> 32 + w0 := x0 * y0 + t := x1 * y0 + (w0 >> 32) + mut w1 := t & mask32 + w2 := t >> 32 + w1 += x0 * y1 + hi := x1 * y1 + w2 + (w1 >> 32) + lo := a * b + return hi ^ lo +} + +[inline] +fn wyr3(p &byte, k u64) u64 { + unsafe { + return (u64(p[0]) << 16) | (u64(p[k >> 1]) << 8) | u64(p[k - 1]) + } +} + +[inline] +fn wyr4(p &byte) u64 { + unsafe { + return u32(p[0]) | (u32(p[1]) << u32(8)) | (u32(p[2]) << u32(16)) | (u32(p[3]) << u32(24)) + } +} + +[inline] +fn wyr8(p &byte) u64 { + unsafe { + return u64(p[0]) | (u64(p[1]) << 8) | (u64(p[2]) << 16) | (u64(p[3]) << 24) | (u64(p[4]) << 32) | (u64(p[5]) << 40) | (u64(p[6]) << 48) | (u64(p[7]) << 56) + } +} diff --git a/v_windows/v/old/vlib/io/buffered_reader.v b/v_windows/v/old/vlib/io/buffered_reader.v new file mode 100644 index 0000000..f336e2d --- /dev/null +++ b/v_windows/v/old/vlib/io/buffered_reader.v @@ -0,0 +1,145 @@ +module io + +// BufferedReader provides a buffered interface for a reader +struct BufferedReader { +mut: + reader Reader + buf []byte + offset int // current offset in the buffer + len int + fails int // how many times fill_buffer has read 0 bytes in a row + mfails int // maximum fails, after which we can assume that the stream has ended +pub mut: + end_of_stream bool // whether we reached the end of the upstream reader +} + +// BufferedReaderConfig are options that can be given to a reader +pub struct BufferedReaderConfig { + reader Reader + cap int = 128 * 1024 // large for fast reading of big(ish) files + retries int = 2 // how many times to retry before assuming the stream ended +} + +// new_buffered_reader creates new BufferedReader +pub fn new_buffered_reader(o BufferedReaderConfig) &BufferedReader { + if o.cap <= 0 { + panic('new_buffered_reader should be called with a positive `cap`') + } + // create + r := &BufferedReader{ + reader: o.reader + buf: []byte{len: o.cap, cap: o.cap} + offset: 0 + mfails: o.retries + } + return r +} + +// read fufills the Reader interface +pub fn (mut r BufferedReader) read(mut buf []byte) ?int { + if r.end_of_stream { + return none + } + // read data out of the buffer if we dont have any + if r.needs_fill() { + if !r.fill_buffer() { + // end of stream + return none + } + } + read := copy(buf, r.buf[r.offset..r.len]) + if read == 0 { + return none + } + r.offset += read + return read +} + +pub fn (mut r BufferedReader) free() { + unsafe { + r.buf.free() + } +} + +// fill_buffer attempts to refill the internal buffer +// and returns whether it got any data +fn (mut r BufferedReader) fill_buffer() bool { + if r.end_of_stream { + // we know we have already reached the end of stream + // so return early + return true + } + r.offset = 0 + r.len = 0 + r.len = r.reader.read(mut r.buf) or { + // end of stream was reached + r.end_of_stream = true + return false + } + if r.len == 0 { + r.fails++ + } else { + r.fails = 0 + } + if r.fails >= r.mfails { + // When reading 0 bytes several times in a row, assume the stream has ended. + // This prevents infinite loops ¯\_(ツ)_/¯ ... + r.end_of_stream = true + return false + } + // we got some data + return true +} + +// needs_fill returns whether the buffer needs refilling +fn (r BufferedReader) needs_fill() bool { + return r.offset >= r.len +} + +// end_of_stream returns whether the end of the stream was reached +pub fn (r BufferedReader) end_of_stream() bool { + return r.end_of_stream +} + +// read_line attempts to read a line from the buffered reader +// it will read until it finds a new line character (\n) or +// the end of stream +pub fn (mut r BufferedReader) read_line() ?string { + if r.end_of_stream { + return none + } + mut line := []byte{} + for { + if r.needs_fill() { + // go fetch some new data + if !r.fill_buffer() { + // We are at the end of the stream + if line.len == 0 { + // we had nothing so return nothing + return none + } + return line.bytestr() + } + } + // try and find a newline character + mut i := r.offset + for ; i < r.len; i++ { + c := r.buf[i] + if c == `\n` { + // great, we hit something + // do some checking for whether we hit \r\n or just \n + if i != 0 && r.buf[i - 1] == `\r` { + x := i - 1 + line << r.buf[r.offset..x] + } else { + line << r.buf[r.offset..i] + } + r.offset = i + 1 + return line.bytestr() + } + } + line << r.buf[r.offset..i] + r.offset = i + } + return none +} diff --git a/v_windows/v/old/vlib/io/custom_string_reading_test.v b/v_windows/v/old/vlib/io/custom_string_reading_test.v new file mode 100644 index 0000000..76e3307 --- /dev/null +++ b/v_windows/v/old/vlib/io/custom_string_reading_test.v @@ -0,0 +1,57 @@ +import io + +struct StringReader { + text string +mut: + place int +} + +fn imin(a int, b int) int { + return if a < b { a } else { b } +} + +fn (mut s StringReader) read(mut buf []byte) ?int { + $if debug { + eprintln('>>>> StringReader.read output buf.len: $buf.len') + } + if s.place > s.text.len + 1 { + return none + } + mut howmany := imin(buf.len, s.text.len - s.place) + xxx := s.text[s.place..s.place + howmany].bytes() + read := copy(buf, xxx) + s.place += read + return read +} + +fn read_from_string(text string, capacity int) []byte { + mut str := StringReader{ + text: text + } + mut stream := io.new_buffered_reader(reader: str, cap: capacity) + // + mut buf := []byte{len: 1} + mut res := []byte{} + mut i := 0 + for { + z := stream.read(mut buf) or { break } + res << buf + $if debug { + println('capacity: $capacity, i: $i, buf: $buf | z: $z') + } + i++ + } + return res +} + +pub fn test_reading_from_a_string() { + for capacity in 1 .. 1000 { + assert read_from_string('a', capacity) == [byte(`a`)] + assert read_from_string('ab', capacity) == [byte(`a`), `b`] + assert read_from_string('abc', capacity) == [byte(`a`), `b`, `c`] + assert read_from_string('abcde', capacity) == [byte(`a`), `b`, `c`, `d`, `e`] + large_string_bytes := []byte{len: 1000, init: `x`} + large_string := large_string_bytes.bytestr() + assert read_from_string(large_string, capacity) == large_string_bytes + } +} diff --git a/v_windows/v/old/vlib/io/io.v b/v_windows/v/old/vlib/io/io.v new file mode 100644 index 0000000..c07e074 --- /dev/null +++ b/v_windows/v/old/vlib/io/io.v @@ -0,0 +1,16 @@ +module io + +const ( + buf_max_len = 1024 +) + +pub fn cp(src Reader, mut dst Writer) ? { + mut buf := []byte{len: io.buf_max_len} + for { + len := src.read(mut buf) or { break } + dst.write(buf[..len]) or { return err } + } + unsafe { + buf.free() + } +} diff --git a/v_windows/v/old/vlib/io/io_cp_test.v b/v_windows/v/old/vlib/io/io_cp_test.v new file mode 100644 index 0000000..21c743a --- /dev/null +++ b/v_windows/v/old/vlib/io/io_cp_test.v @@ -0,0 +1,13 @@ +import io +import os + +fn test_cp() ? { + mut f := os.open(@FILE) or { panic(err) } + defer { + f.close() + } + mut r := io.new_buffered_reader(reader: f) + mut stdout := os.stdout() + io.cp(r, mut stdout) ? + assert true +} diff --git a/v_windows/v/old/vlib/io/io_test.v b/v_windows/v/old/vlib/io/io_test.v new file mode 100644 index 0000000..fca17c2 --- /dev/null +++ b/v_windows/v/old/vlib/io/io_test.v @@ -0,0 +1,41 @@ +import io + +struct Buf { +pub: + bytes []byte +mut: + i int +} + +struct Writ { +pub mut: + bytes []byte +} + +fn (mut b Buf) read(mut buf []byte) ?int { + if !(b.i < b.bytes.len) { + return none + } + n := copy(buf, b.bytes[b.i..]) + b.i += n + return n +} + +fn (mut w Writ) write(buf []byte) ?int { + if buf.len <= 0 { + return none + } + w.bytes << buf + return buf.len +} + +fn test_copy() { + src := Buf{ + bytes: 'abcdefghij'.repeat(10).bytes() + } + mut dst := Writ{ + bytes: []byte{} + } + io.cp(src, mut dst) or { assert false } + assert dst.bytes == src.bytes +} diff --git a/v_windows/v/old/vlib/io/multi_writer.v b/v_windows/v/old/vlib/io/multi_writer.v new file mode 100644 index 0000000..837ea30 --- /dev/null +++ b/v_windows/v/old/vlib/io/multi_writer.v @@ -0,0 +1,33 @@ +module io + +// new_multi_writer returns a Writer that writes to all writers. The write +// function of the returned Writer writes to all writers of the MultiWriter, +// returns the length of bytes written, and if any writer fails to write the +// full length an error is returned and writing to other writers stops, and if +// any writer returns an error the error is returned immediately and writing to +// other writers stops. +pub fn new_multi_writer(writers ...Writer) Writer { + return &MultiWriter{ + writers: writers + } +} + +// MultiWriter writes to all its writers. +pub struct MultiWriter { +pub mut: + writers []Writer +} + +// write writes to all writers of the MultiWriter. Returns the length of bytes +// written. If any writer fails to write the full length an error is returned +// and writing to other writers stops. If any writer returns an error the error +// is returned immediately and writing to other writers stops. +pub fn (mut m MultiWriter) write(buf []byte) ?int { + for mut w in m.writers { + n := w.write(buf) ? + if n != buf.len { + return error('io: incomplete write to writer of MultiWriter') + } + } + return buf.len +} diff --git a/v_windows/v/old/vlib/io/multi_writer_test.v b/v_windows/v/old/vlib/io/multi_writer_test.v new file mode 100644 index 0000000..2e317e8 --- /dev/null +++ b/v_windows/v/old/vlib/io/multi_writer_test.v @@ -0,0 +1,66 @@ +module io + +fn test_multi_writer_write_successful() { + w0 := TestWriter{} + w1 := TestWriter{} + mut mw := new_multi_writer(w0, w1) + n := mw.write('0123456789'.bytes()) or { + assert false + return + } + assert n == 10 + assert w0.bytes == '0123456789'.bytes() + assert w1.bytes == '0123456789'.bytes() +} + +fn test_multi_writer_write_incomplete() { + w0 := TestWriter{} + w1 := TestIncompleteWriter{} + mut mw := new_multi_writer(w0, w1) + n := mw.write('0123456789'.bytes()) or { + assert w0.bytes == '0123456789'.bytes() + assert w1.bytes == '012345678'.bytes() + return + } + assert false +} + +fn test_multi_writer_write_error() { + w0 := TestWriter{} + w1 := TestErrorWriter{} + w2 := TestWriter{} + mut mw := new_multi_writer(w0, w1, w2) + n := mw.write('0123456789'.bytes()) or { + assert w0.bytes == '0123456789'.bytes() + assert w2.bytes == [] + return + } + assert false +} + +struct TestWriter { +pub mut: + bytes []byte +} + +fn (mut w TestWriter) write(buf []byte) ?int { + w.bytes << buf + return buf.len +} + +struct TestIncompleteWriter { +pub mut: + bytes []byte +} + +fn (mut w TestIncompleteWriter) write(buf []byte) ?int { + b := buf[..buf.len - 1] + w.bytes << b + return b.len +} + +struct TestErrorWriter {} + +fn (mut w TestErrorWriter) write(buf []byte) ?int { + return error('error writer errored') +} diff --git a/v_windows/v/old/vlib/io/os_file_reader_test.v b/v_windows/v/old/vlib/io/os_file_reader_test.v new file mode 100644 index 0000000..1ed3a1f --- /dev/null +++ b/v_windows/v/old/vlib/io/os_file_reader_test.v @@ -0,0 +1,29 @@ +import os +import io + +fn read_file(file string, cap int) []string { + mut lines := []string{} + mut f := os.open(file) or { panic(err) } + defer { + f.close() + } + mut r := io.new_buffered_reader(reader: f, cap: cap) + for { + l := r.read_line() or { break } + lines << l + // println('Line: $l') + } + assert lines.len > 0 + assert r.end_of_stream == true + println('------------------------------------------------ cap: ${cap:6}; read: ${lines.len:3} lines') + return lines +} + +fn test_file_reader() { + for cap := 1; cap <= 10000; cap += 256 { + lines := read_file(@FILE, cap) + assert lines.last() == '// my last line' + } +} + +// my last line diff --git a/v_windows/v/old/vlib/io/reader.v b/v_windows/v/old/vlib/io/reader.v new file mode 100644 index 0000000..b7377b0 --- /dev/null +++ b/v_windows/v/old/vlib/io/reader.v @@ -0,0 +1,76 @@ +module io + +// Reader represents a stream of data that can be read +pub interface Reader { + // read reads up to buf.len bytes and places + // them into buf. + // A type that implements this should return + // `none` on end of stream (EOF) instead of just returning 0 + read(mut buf []byte) ?int +} + +// make_reader is a temp that converts a type to a reader +// (e.g. for use in struct initialisation) +// (this shouldnt need to be a thing but until coercion gets made better +// it is required) +[deprecated: 'use just `x` instead of `io.make_reader(x)`. Interfaces are now checked against all types.'] +[deprecated_after: '2021-05-27'] +pub fn make_reader(r Reader) Reader { + return r +} + +const ( + read_all_len = 10 * 1024 + read_all_grow_len = 1024 +) + +// ReadAllConfig allows options to be passed for the behaviour +// of read_all +pub struct ReadAllConfig { + reader Reader + read_to_end_of_stream bool +} + +// read_all reads all bytes from a reader until either a 0 length read +// or if read_to_end_of_stream is true then the end of the stream (`none`) +pub fn read_all(config ReadAllConfig) ?[]byte { + r := config.reader + read_till_eof := config.read_to_end_of_stream + + mut b := []byte{len: io.read_all_len} + mut read := 0 + for { + new_read := r.read(mut b[read..]) or { break } + read += new_read + if !read_till_eof && read == 0 { + break + } + if b.len == read { + unsafe { b.grow_len(io.read_all_grow_len) } + } + } + return b[..read] +} + +// read_any reads any available bytes from a reader +// (until the reader returns a read of 0 length) +pub fn read_any(r Reader) ?[]byte { + mut b := []byte{len: io.read_all_len} + mut read := 0 + for { + new_read := r.read(mut b[read..]) or { break } + read += new_read + if new_read == 0 { + break + } + if b.len == read { + unsafe { b.grow_len(io.read_all_grow_len) } + } + } + return b[..read] +} + +// RandomReader represents a stream of data that can be read from at a random location +interface RandomReader { + read_from(pos u64, mut buf []byte) ?int +} diff --git a/v_windows/v/old/vlib/io/reader_test.v b/v_windows/v/old/vlib/io/reader_test.v new file mode 100644 index 0000000..c7a2265 --- /dev/null +++ b/v_windows/v/old/vlib/io/reader_test.v @@ -0,0 +1,130 @@ +module io + +struct Buf { +pub: + bytes []byte +mut: + i int +} + +fn (mut b Buf) read(mut buf []byte) ?int { + if !(b.i < b.bytes.len) { + return none + } + n := copy(buf, b.bytes[b.i..]) + b.i += n + return n +} + +fn test_read_all() { + buf := Buf{ + bytes: '123'.repeat(10).bytes() + } + res := read_all(reader: buf) or { + assert false + ''.bytes() + } + assert res == '123'.repeat(10).bytes() +} + +fn test_read_all_huge() { + buf := Buf{ + bytes: '123'.repeat(100000).bytes() + } + res := read_all(reader: buf) or { + assert false + ''.bytes() + } + assert res == '123'.repeat(100000).bytes() +} + +struct StringReader { + text string +mut: + place int +} + +fn (mut s StringReader) read(mut buf []byte) ?int { + if s.place >= s.text.len { + return none + } + read := copy(buf, s.text[s.place..].bytes()) + s.place += read + return read +} + +const ( + newline_count = 100000 +) + +fn test_stringreader() { + text := '12345\n'.repeat(io.newline_count) + mut s := StringReader{ + text: text + } + mut r := new_buffered_reader(reader: s) + for i := 0; true; i++ { + if _ := r.read_line() { + } else { + assert i == io.newline_count + break + } + } + if _ := r.read_line() { + assert false + } + leftover := read_all(reader: r) or { + assert false + panic('bad') + } + if leftover.len > 0 { + assert false + } +} + +fn test_stringreader2() { + text := '12345\r\n'.repeat(io.newline_count) + mut s := StringReader{ + text: text + } + mut r := new_buffered_reader(reader: s) + for i := 0; true; i++ { + if _ := r.read_line() { + } else { + assert i == io.newline_count + break + } + } + if _ := r.read_line() { + assert false + } + leftover := read_all(reader: r) or { + assert false + panic('bad') + } + if leftover.len > 0 { + assert false + } +} + +fn test_leftover() { + text := 'This is a test\r\nNice try!' + mut s := StringReader{ + text: text + } + mut r := new_buffered_reader(reader: s) + _ := r.read_line() or { + assert false + panic('bad') + } + line2 := r.read_line() or { + assert false + panic('bad') + } + assert line2 == 'Nice try!' + if _ := r.read_line() { + assert false + panic('bad') + } + assert r.end_of_stream() +} diff --git a/v_windows/v/old/vlib/io/readerwriter.v b/v_windows/v/old/vlib/io/readerwriter.v new file mode 100644 index 0000000..ba44172 --- /dev/null +++ b/v_windows/v/old/vlib/io/readerwriter.v @@ -0,0 +1,34 @@ +module io + +// ReaderWriter represents a stream that can be read from and wrote to +pub interface ReaderWriter { + // from Reader + read(mut buf []byte) ?int + // from Writer + write(buf []byte) ?int +} + +// ReaderWriterImpl is a ReaderWriter that can be made from +// a seperate reader and writer (see fn make_readerwriter) +struct ReaderWriterImpl { + r Reader +mut: + w Writer +} + +pub fn (mut r ReaderWriterImpl) read(mut buf []byte) ?int { + return r.r.read(mut buf) +} + +pub fn (mut r ReaderWriterImpl) write(buf []byte) ?int { + return r.w.write(buf) +} + +// make_readerwriter takes a rstream and a wstream and makes +// an rwstream with them +pub fn make_readerwriter(r Reader, w Writer) ReaderWriterImpl { + return ReaderWriterImpl{ + r: r + w: w + } +} diff --git a/v_windows/v/old/vlib/io/util/util.v b/v_windows/v/old/vlib/io/util/util.v new file mode 100644 index 0000000..6f0d93f --- /dev/null +++ b/v_windows/v/old/vlib/io/util/util.v @@ -0,0 +1,104 @@ +module util + +import os +import rand +import rand.seed as rseed + +const ( + retries = 10000 +) + +pub struct TempFileOptions { + path string = os.temp_dir() + pattern string +} + +// temp_file returns an uniquely named, open, writable, `os.File` and it's path +pub fn temp_file(tfo TempFileOptions) ?(os.File, string) { + mut d := tfo.path + if d == '' { + d = os.temp_dir() + } + os.is_writable_folder(d) or { + return error(@FN + + ' could not create temporary file in "$d". Please ensure write permissions.') + } + d = d.trim_right(os.path_separator) + mut rng := rand.new_default() + prefix, suffix := prefix_and_suffix(tfo.pattern) or { return error(@FN + ' ' + err.msg) } + for retry := 0; retry < util.retries; retry++ { + path := os.join_path(d, prefix + random_number(mut rng) + suffix) + mut mode := 'rw+' + $if windows { + mode = 'w+' + } + mut file := os.open_file(path, mode, 0o600) or { + rng.seed(rseed.time_seed_array(2)) + continue + } + if os.exists(path) && os.is_file(path) { + return file, path + } + } + return error(@FN + + ' could not create temporary file in "$d". Retry limit ($util.retries) exhausted. Please ensure write permissions.') +} + +pub struct TempDirOptions { + path string = os.temp_dir() + pattern string +} + +// temp_dir returns an uniquely named, writable, directory path +pub fn temp_dir(tdo TempFileOptions) ?string { + mut d := tdo.path + if d == '' { + d = os.temp_dir() + } + os.is_writable_folder(d) or { + return error(@FN + + ' could not create temporary directory "$d". Please ensure write permissions.') + } + d = d.trim_right(os.path_separator) + mut rng := rand.new_default() + prefix, suffix := prefix_and_suffix(tdo.pattern) or { return error(@FN + ' ' + err.msg) } + for retry := 0; retry < util.retries; retry++ { + path := os.join_path(d, prefix + random_number(mut rng) + suffix) + os.mkdir_all(path) or { + rng.seed(rseed.time_seed_array(2)) + continue + } + if os.is_dir(path) && os.exists(path) { + os.is_writable_folder(path) or { + return error(@FN + + ' could not create temporary directory "$d". Please ensure write permissions.') + } + return path + } + } + return error(@FN + + ' could not create temporary directory "$d". Retry limit ($util.retries) exhausted. Please ensure write permissions.') +} + +// * Utility functions +fn random_number(mut rng rand.PRNG) string { + s := (u32(1e9) + (u32(os.getpid()) + rng.u32() % u32(1e9))).str() + return s.substr(1, s.len) +} + +fn prefix_and_suffix(pattern string) ?(string, string) { + mut pat := pattern + if pat.contains(os.path_separator) { + return error('pattern cannot contain path separators ($os.path_separator).') + } + pos := pat.last_index('*') or { -1 } + mut prefix := '' + mut suffix := '' + if pos != -1 { + prefix = pat.substr(0, pos) + suffix = pat.substr(pos + 1, pat.len) + } else { + prefix = pat + } + return prefix, suffix +} diff --git a/v_windows/v/old/vlib/io/util/util_test.v b/v_windows/v/old/vlib/io/util/util_test.v new file mode 100644 index 0000000..1072cb5 --- /dev/null +++ b/v_windows/v/old/vlib/io/util/util_test.v @@ -0,0 +1,127 @@ +import os +import io.util + +const ( + // tfolder will contain all the temporary files/subfolders made by + // the different tests. It would be removed in testsuite_end(), so + // individual os tests do not need to clean up after themselves. + tfolder = os.join_path(os.temp_dir(), 'v', 'tests', 'io_util_test') +) + +fn testsuite_begin() { + eprintln('testsuite_begin, tfolder = $tfolder') + os.rmdir_all(tfolder) or {} + assert !os.is_dir(tfolder) + os.mkdir_all(tfolder) or { panic(err) } + os.chdir(tfolder) + assert os.is_dir(tfolder) +} + +fn testsuite_end() { + os.chdir(os.wd_at_startup) + os.rmdir_all(tfolder) or {} + assert !os.is_dir(tfolder) + // eprintln('testsuite_end , tfolder = $tfolder removed.') +} + +fn test_temp_file() { + // Test defaults + mut f, mut path := util.temp_file() or { + assert false + return + } + mut prev_path := path + defer { + f.close() + } + assert os.is_file(path) + assert f.is_opened + // Test pattern + f.close() + f, path = util.temp_file( + pattern: 'some_*_test.file' + ) or { + assert false + return + } + assert path != prev_path + assert os.is_file(path) + assert f.is_opened + mut filename := os.file_name(path) + assert filename.contains('_test.file') + // Check for 9 digits where the wildcard is placed in the pattern + for i, c in filename { + if i > 4 && i <= 4 + 9 { + assert c.is_digit() + } + } + // Test custom path + prev_path = path + f.close() + f, path = util.temp_file( + path: tfolder + ) or { + assert false + return + } + assert path != prev_path + assert os.is_file(path) + assert path.contains(tfolder) + assert f.is_opened + filename = os.file_name(path) + for c in filename { + assert c.is_digit() + } +} + +fn test_temp_dir() { + // Test defaults + mut path := util.temp_dir() or { + assert false + return + } + assert os.is_dir(path) + mut writable := os.is_writable_folder(path) or { + assert false + return + } + assert writable + mut prev_path := path + // Test pattern + path = util.temp_dir( + pattern: 'some_*_test_dir' + ) or { + assert false + return + } + assert path != prev_path + assert os.is_dir(path) + mut filename := os.file_name(path) + assert filename.contains('_test_dir') + // Check for 9 digits where the wildcard is placed in the pattern + for i, c in filename { + if i > 4 && i <= 4 + 9 { + assert c.is_digit() + } + } + // Test custom path + prev_path = path + path = util.temp_dir( + path: tfolder + ) or { + assert false + return + } + assert path != prev_path + assert os.is_dir(path) + writable = os.is_writable_folder(path) or { + assert false + return + } + assert writable + assert path.contains(tfolder) + filename = os.file_name(path) + for c in filename { + assert c.is_digit() + } +} diff --git a/v_windows/v/old/vlib/io/writer.v b/v_windows/v/old/vlib/io/writer.v new file mode 100644 index 0000000..322dd45 --- /dev/null +++ b/v_windows/v/old/vlib/io/writer.v @@ -0,0 +1,12 @@ +module io + +// Writer represents a stream of data that can be wrote to +pub interface Writer { + write(buf []byte) ?int +} + +// RandomWriter represents a stream of data that can be wrote to +// at a random pos +pub interface RandomWriter { + write_to(pos u64, buf []byte) ?int +} diff --git a/v_windows/v/old/vlib/json/json_decode_test.v b/v_windows/v/old/vlib/json/json_decode_test.v new file mode 100644 index 0000000..2c4448f --- /dev/null +++ b/v_windows/v/old/vlib/json/json_decode_test.v @@ -0,0 +1,35 @@ +import json + +struct TestTwin { + id int + seed string + pubkey string +} + +struct TestTwins { +mut: + twins []TestTwin [required] +} + +fn test_json_decode_fails_to_decode_unrecognised_array_of_dicts() { + data := '[{"twins":[{"id":123,"seed":"abcde","pubkey":"xyzasd"},{"id":456,"seed":"dfgdfgdfgd","pubkey":"skjldskljh45sdf"}]}]' + json.decode(TestTwins, data) or { + assert err.msg == "expected field 'twins' is missing" + return + } + assert false +} + +fn test_json_decode_works_with_a_dict_of_arrays() { + data := '{"twins":[{"id":123,"seed":"abcde","pubkey":"xyzasd"},{"id":456,"seed":"dfgdfgdfgd","pubkey":"skjldskljh45sdf"}]}' + res := json.decode(TestTwins, data) or { + assert false + exit(1) + } + assert res.twins[0].id == 123 + assert res.twins[0].seed == 'abcde' + assert res.twins[0].pubkey == 'xyzasd' + assert res.twins[1].id == 456 + assert res.twins[1].seed == 'dfgdfgdfgd' + assert res.twins[1].pubkey == 'skjldskljh45sdf' +} diff --git a/v_windows/v/old/vlib/json/json_primitives.v b/v_windows/v/old/vlib/json/json_primitives.v new file mode 100644 index 0000000..0631899 --- /dev/null +++ b/v_windows/v/old/vlib/json/json_primitives.v @@ -0,0 +1,204 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module json + +#flag -I @VEXEROOT/thirdparty/cJSON +#flag @VEXEROOT/thirdparty/cJSON/cJSON.o +#include "cJSON.h" +#define js_get(object, key) cJSON_GetObjectItemCaseSensitive((object), (key)) + +struct C.cJSON { + valueint int + valuedouble f32 + valuestring &char +} + +fn C.cJSON_IsTrue(&C.cJSON) bool + +fn C.cJSON_CreateNumber(int) &C.cJSON + +fn C.cJSON_CreateBool(bool) &C.cJSON + +fn C.cJSON_CreateString(&char) &C.cJSON + +fn C.cJSON_Parse(&char) &C.cJSON + +fn C.cJSON_PrintUnformatted(&C.cJSON) &char + +fn C.cJSON_Print(&C.cJSON) &char + +pub fn decode(typ voidptr, s string) ?voidptr { + // compiler implementation + return 0 +} + +pub fn encode(x voidptr) string { + // compiler implementation + return '' +} + +pub fn encode_pretty(x voidptr) string { + // compiler implementation + return '' +} + +fn decode_int(root &C.cJSON) int { + if isnil(root) { + return 0 + } + return root.valueint +} + +fn decode_i8(root &C.cJSON) i8 { + if isnil(root) { + return i8(0) + } + return i8(root.valueint) +} + +fn decode_i16(root &C.cJSON) i16 { + if isnil(root) { + return i16(0) + } + return i16(root.valueint) +} + +fn decode_i64(root &C.cJSON) i64 { + if isnil(root) { + return i64(0) + } + return i64(root.valuedouble) // i64 is double in C +} + +fn decode_byte(root &C.cJSON) byte { + if isnil(root) { + return byte(0) + } + return byte(root.valueint) +} + +fn decode_u16(root &C.cJSON) u16 { + if isnil(root) { + return u16(0) + } + return u16(root.valueint) +} + +fn decode_u32(root &C.cJSON) u32 { + if isnil(root) { + return u32(0) + } + return u32(root.valueint) +} + +fn decode_u64(root &C.cJSON) u64 { + if isnil(root) { + return u64(0) + } + return u64(root.valueint) +} + +fn decode_f32(root &C.cJSON) f32 { + if isnil(root) { + return f32(0) + } + return root.valuedouble +} + +fn decode_f64(root &C.cJSON) f64 { + if isnil(root) { + return f64(0) + } + return f64(root.valuedouble) +} + +fn decode_string(root &C.cJSON) string { + if isnil(root) { + return '' + } + if isnil(root.valuestring) { + return '' + } + // println('decode string valuestring="$root.valuestring"') + // return tos(root.valuestring, _strlen(root.valuestring)) + return unsafe { tos_clone(&byte(root.valuestring)) } // , _strlen(root.valuestring)) +} + +fn decode_bool(root &C.cJSON) bool { + if isnil(root) { + return false + } + return C.cJSON_IsTrue(root) +} + +// /////////////////// +fn encode_int(val int) &C.cJSON { + return C.cJSON_CreateNumber(val) +} + +fn encode_i8(val i8) &C.cJSON { + return C.cJSON_CreateNumber(val) +} + +fn encode_i16(val i16) &C.cJSON { + return C.cJSON_CreateNumber(val) +} + +fn encode_i64(val i64) &C.cJSON { + return C.cJSON_CreateNumber(val) +} + +fn encode_byte(val byte) &C.cJSON { + return C.cJSON_CreateNumber(val) +} + +fn encode_u16(val u16) &C.cJSON { + return C.cJSON_CreateNumber(val) +} + +fn encode_u32(val u32) &C.cJSON { + return C.cJSON_CreateNumber(val) +} + +fn encode_u64(val u64) &C.cJSON { + return C.cJSON_CreateNumber(val) +} + +fn encode_f32(val f32) &C.cJSON { + return C.cJSON_CreateNumber(val) +} + +fn encode_f64(val f64) &C.cJSON { + return C.cJSON_CreateNumber(val) +} + +fn encode_bool(val bool) &C.cJSON { + return C.cJSON_CreateBool(val) +} + +fn encode_string(val string) &C.cJSON { + return C.cJSON_CreateString(&char(val.str)) +} + +// /////////////////////// +// user := decode_User(json_parse(js_string_var)) +fn json_parse(s string) &C.cJSON { + return C.cJSON_Parse(&char(s.str)) +} + +// json_string := json_print(encode_User(user)) +fn json_print(json &C.cJSON) string { + s := C.cJSON_PrintUnformatted(json) + return unsafe { tos(&byte(s), C.strlen(&char(s))) } +} + +fn json_print_pretty(json &C.cJSON) string { + s := C.cJSON_Print(json) + return unsafe { tos(&byte(s), C.strlen(&char(s))) } +} + +// / cjson wrappers +// fn json_array_for_each(val, root &C.cJSON) { +// #cJSON_ArrayForEach (val ,root) +// } diff --git a/v_windows/v/old/vlib/json/json_test.v b/v_windows/v/old/vlib/json/json_test.v new file mode 100644 index 0000000..8191462 --- /dev/null +++ b/v_windows/v/old/vlib/json/json_test.v @@ -0,0 +1,358 @@ +import json +import time + +enum JobTitle { + manager + executive + worker +} + +struct Employee { + name string + age int + salary f32 + title JobTitle +} + +fn test_simple() ? { + x := Employee{'Peter', 28, 95000.5, .worker} + s := json.encode(x) + eprintln('Employee x: $s') + assert s == '{"name":"Peter","age":28,"salary":95000.5,"title":2}' + y := json.decode(Employee, s) ? + eprintln('Employee y: $y') + assert y.name == 'Peter' + assert y.age == 28 + assert y.salary == 95000.5 + assert y.title == .worker +} + +fn bar(payload string) ?Bar { // ?T doesn't work currently + result := json.decode(T, payload) ? + return result +} + +struct Bar { + x string +} + +fn test_generic() { + result := bar('{"x":"test"}') or { Bar{} } + assert result.x == 'test' +} + +struct User2 { + age int + nums []int + reg_date time.Time +} + +struct User { + age int + nums []int + last_name string [json: lastName] + is_registered bool [json: IsRegistered] + typ int [json: 'type'] + pets string [json: 'pet_animals'; raw] +} + +fn test_parse_user() ? { + s := '{"age": 10, "nums": [1,2,3], "type": 1, "lastName": "Johnson", "IsRegistered": true, "pet_animals": {"name": "Bob", "animal": "Dog"}}' + u2 := json.decode(User2, s) ? + println(u2) + u := json.decode(User, s) ? + println(u) + assert u.age == 10 + assert u.last_name == 'Johnson' + assert u.is_registered == true + assert u.nums.len == 3 + assert u.nums[0] == 1 + assert u.nums[1] == 2 + assert u.nums[2] == 3 + assert u.typ == 1 + assert u.pets == '{"name":"Bob","animal":"Dog"}' +} + +fn test_encode_decode_time() ? { + user := User2{ + age: 25 + reg_date: time.new_time(year: 2020, month: 12, day: 22, hour: 7, minute: 23) + } + s := json.encode(user) + println(s) + assert s.contains('"reg_date":1608621780') + user2 := json.decode(User2, s) ? + assert user2.reg_date.str() == '2020-12-22 07:23:00' + println(user2) + println(user2.reg_date) +} + +fn (mut u User) foo() string { + return json.encode(u) +} + +fn test_encode_user() { + mut usr := User{ + age: 10 + nums: [1, 2, 3] + last_name: 'Johnson' + is_registered: true + typ: 0 + pets: 'foo' + } + expected := '{"age":10,"nums":[1,2,3],"lastName":"Johnson","IsRegistered":true,"type":0,"pet_animals":"foo"}' + out := json.encode(usr) + println(out) + assert out == expected + // Test json.encode on mutable pointers + assert usr.foo() == expected +} + +struct Color { + space string + point string [raw] +} + +fn test_raw_json_field() { + color := json.decode(Color, '{"space": "YCbCr", "point": {"Y": 123}}') or { + println('text') + return + } + assert color.point == '{"Y":123}' + assert color.space == 'YCbCr' +} + +fn test_bad_raw_json_field() { + color := json.decode(Color, '{"space": "YCbCr"}') or { + println('text') + return + } + assert color.point == '' + assert color.space == 'YCbCr' +} + +struct City { + name string +} + +struct Country { + cities []City + name string +} + +fn test_struct_in_struct() ? { + country := json.decode(Country, '{ "name": "UK", "cities": [{"name":"London"}, {"name":"Manchester"}]}') ? + assert country.name == 'UK' + assert country.cities.len == 2 + assert country.cities[0].name == 'London' + assert country.cities[1].name == 'Manchester' + println(country.cities) +} + +fn test_encode_map() { + expected := '{"one":1,"two":2,"three":3,"four":4}' + numbers := map{ + 'one': 1 + 'two': 2 + 'three': 3 + 'four': 4 + } + out := json.encode(numbers) + println(out) + assert out == expected +} + +fn test_parse_map() ? { + expected := map{ + 'one': 1 + 'two': 2 + 'three': 3 + 'four': 4 + } + out := json.decode(map[string]int, '{"one":1,"two":2,"three":3,"four":4}') ? + println(out) + assert out == expected +} + +struct Data { + countries []Country + users map[string]User + extra map[string]map[string]int +} + +fn test_nested_type() ? { + data_expected := '{"countries":[{"cities":[{"name":"London"},{"name":"Manchester"}],"name":"UK"},{"cities":[{"name":"Donlon"},{"name":"Termanches"}],"name":"KU"}],"users":{"Foo":{"age":10,"nums":[1,2,3],"lastName":"Johnson","IsRegistered":true,"type":0,"pet_animals":"little foo"},"Boo":{"age":20,"nums":[5,3,1],"lastName":"Smith","IsRegistered":false,"type":4,"pet_animals":"little boo"}},"extra":{"2":{"n1":2,"n2":4,"n3":8,"n4":16},"3":{"n1":3,"n2":9,"n3":27,"n4":81}}}' + data := Data{ + countries: [ + Country{ + name: 'UK' + cities: [City{'London'}, City{'Manchester'}] + }, + Country{ + name: 'KU' + cities: [City{'Donlon'}, City{'Termanches'}] + }, + ] + users: map{ + 'Foo': User{ + age: 10 + nums: [1, 2, 3] + last_name: 'Johnson' + is_registered: true + typ: 0 + pets: 'little foo' + } + 'Boo': User{ + age: 20 + nums: [5, 3, 1] + last_name: 'Smith' + is_registered: false + typ: 4 + pets: 'little boo' + } + } + extra: map{ + '2': map{ + 'n1': 2 + 'n2': 4 + 'n3': 8 + 'n4': 16 + } + '3': map{ + 'n1': 3 + 'n2': 9 + 'n3': 27 + 'n4': 81 + } + } + } + out := json.encode(data) + println(out) + assert out == data_expected + data2 := json.decode(Data, data_expected) ? + assert data2.countries.len == data.countries.len + for i in 0 .. 1 { + assert data2.countries[i].name == data.countries[i].name + assert data2.countries[i].cities.len == data.countries[i].cities.len + for j in 0 .. 1 { + assert data2.countries[i].cities[j].name == data.countries[i].cities[j].name + } + } + for key, user in data.users { + assert data2.users[key].age == user.age + assert data2.users[key].nums == user.nums + assert data2.users[key].last_name == user.last_name + assert data2.users[key].is_registered == user.is_registered + assert data2.users[key].typ == user.typ + // assert data2.users[key].pets == user.pets // TODO FIX + } + for k, v in data.extra { + for k2, v2 in v { + assert data2.extra[k][k2] == v2 + } + } +} + +struct Foo { +pub: + name string + data T +} + +fn test_generic_struct() ? { + foo_int := Foo{'bar', 12} + foo_enc := json.encode(foo_int) + assert foo_enc == '{"name":"bar","data":12}' + foo_dec := json.decode(Foo, foo_enc) ? + assert foo_dec.name == 'bar' + assert foo_dec.data == 12 +} + +fn test_errors() { + invalid_array := fn () { + data := '{"countries":[{"cities":[{"name":"London"},{"name":"Manchester"}],"name":"UK"},{"cities":{"name":"Donlon"},"name":"KU"}],"users":{"Foo":{"age":10,"nums":[1,2,3],"lastName":"Johnson","IsRegistered":true,"type":0,"pet_animals":"little foo"},"Boo":{"age":20,"nums":[5,3,1],"lastName":"Smith","IsRegistered":false,"type":4,"pet_animals":"little boo"}},"extra":{"2":{"n1":2,"n2":4,"n3":8,"n4":16},"3":{"n1":3,"n2":9,"n3":27,"n4":81}}}' + json.decode(Data, data) or { + println(err) + assert err.msg.starts_with('Json element is not an array:') + return + } + assert false + } + invalid_object := fn () { + data := '{"countries":[{"cities":[{"name":"London"},{"name":"Manchester"}],"name":"UK"},{"cities":[{"name":"Donlon"},{"name":"Termanches"}],"name":"KU"}],"users":[{"age":10,"nums":[1,2,3],"lastName":"Johnson","IsRegistered":true,"type":0,"pet_animals":"little foo"},{"age":20,"nums":[5,3,1],"lastName":"Smith","IsRegistered":false,"type":4,"pet_animals":"little boo"}],"extra":{"2":{"n1":2,"n2":4,"n3":8,"n4":16},"3":{"n1":3,"n2":9,"n3":27,"n4":81}}}' + json.decode(Data, data) or { + println(err) + assert err.msg.starts_with('Json element is not an object:') + return + } + assert false + } + invalid_array() + invalid_object() +} + +type ID = string + +struct Message { + id ID +} + +fn test_decode_alias_struct() ? { + msg := json.decode(Message, '{"id": "118499178790780929"}') ? + // hacky way of comparing aliased strings + assert msg.id.str() == '118499178790780929' +} + +fn test_encode_alias_struct() { + expected := '{"id":"118499178790780929"}' + msg := Message{'118499178790780929'} + out := json.encode(msg) + assert out == expected +} + +struct List { + id int + items []string +} + +fn test_list() ? { + list := json.decode(List, '{"id": 1, "items": ["1", "2"]}') ? + assert list.id == 1 + assert list.items == ['1', '2'] +} + +fn test_list_no_id() ? { + list := json.decode(List, '{"items": ["1", "2"]}') ? + assert list.id == 0 + assert list.items == ['1', '2'] +} + +fn test_list_no_items() ? { + list := json.decode(List, '{"id": 1}') ? + assert list.id == 1 + assert list.items == [] +} + +struct Info { + id int + items []string + maps map[string]string +} + +fn test_decode_null_object() ? { + info := json.decode(Info, '{"id": 22, "items": null, "maps": null}') ? + assert info.id == 22 + assert '$info.items' == '[]' + assert '$info.maps' == '{}' +} + +struct Foo2 { + name string +} + +fn test_pretty() { + foo := Foo2{'Bob'} + assert json.encode_pretty(foo) == '{ + "name": "Bob" +}' +} diff --git a/v_windows/v/old/vlib/log/log.v b/v_windows/v/old/vlib/log/log.v new file mode 100644 index 0000000..a6259a9 --- /dev/null +++ b/v_windows/v/old/vlib/log/log.v @@ -0,0 +1,185 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module log + +import os +import time +import term + +// Level defines possible log levels used by `Log` +pub enum Level { + fatal = 1 + error + warn + info + debug +} + +pub enum LogTarget { + console + file + both +} + +// tag returns the tag for log level `l` as a string. +fn tag_to_cli(l Level) string { + return match l { + .fatal { term.red('FATAL') } + .error { term.red('ERROR') } + .warn { term.yellow('WARN ') } + .info { term.white('INFO ') } + .debug { term.blue('DEBUG') } + } +} + +// tag returns the tag for log level `l` as a string. +fn tag_to_file(l Level) string { + return match l { + .fatal { 'FATAL' } + .error { 'ERROR' } + .warn { 'WARN ' } + .info { 'INFO ' } + .debug { 'DEBUG' } + } +} + +interface Logger { + fatal(s string) + error(s string) + warn(s string) + info(s string) + debug(s string) +} + +// Log represents a logging object +pub struct Log { +mut: + level Level + output_label string + ofile os.File + output_target LogTarget // if true output to file else use stdout/stderr. +pub mut: + output_file_name string // log output to this file +} + +// set_level sets the internal logging to `level`. +pub fn (mut l Log) set_level(level Level) { + l.level = level +} + +// set_output_level sets the internal logging output to `level`. +pub fn (mut l Log) set_output_level(level Level) { + l.level = level +} + +// set_full_logpath sets the output label and output path from `full_log_path`. +pub fn (mut l Log) set_full_logpath(full_log_path string) { + rlog_file := os.real_path(full_log_path) + l.set_output_label(os.file_name(rlog_file)) + l.set_output_path(os.dir(rlog_file)) +} + +// set_output_label sets the `label` for the output. +pub fn (mut l Log) set_output_label(label string) { + l.output_label = label +} + +// set_output_path sets the file to which output is logged to. +pub fn (mut l Log) set_output_path(output_file_path string) { + if l.ofile.is_opened { + l.ofile.close() + } + l.output_target = .file + l.output_file_name = os.join_path(os.real_path(output_file_path), l.output_label) + ofile := os.open_append(l.output_file_name) or { + panic('error while opening log file $l.output_file_name for appending') + } + l.ofile = ofile +} + +// log_to_console_too turns on logging to the console too, in addition to logging to a file. +// You have to call it *after* calling .set_output_path(output_file_path). +pub fn (mut l Log) log_to_console_too() { + if l.output_target != .file { + panic('log_to_console_too should be called *after* .set_output_path') + } + l.output_target = .both +} + +// flush writes the log file content to disk. +pub fn (mut l Log) flush() { + l.ofile.flush() +} + +// close closes the log file. +pub fn (mut l Log) close() { + l.ofile.close() +} + +// log_file writes log line `s` with `level` to the log file. +fn (mut l Log) log_file(s string, level Level) { + timestamp := time.now().format_ss() + e := tag_to_file(level) + l.ofile.writeln('$timestamp [$e] $s') or { panic(err) } +} + +// log_cli writes log line `s` with `level` to stdout. +fn (l &Log) log_cli(s string, level Level) { + timestamp := time.now().format_ss() + e := tag_to_cli(level) + println('$timestamp [$e] $s') +} + +// send_output writes log line `s` with `level` to either the log file or the console +// according to the value of the `.output_target` field. +pub fn (mut l Log) send_output(s &string, level Level) { + if l.output_target == .file || l.output_target == .both { + l.log_file(s, level) + } + if l.output_target == .console || l.output_target == .both { + l.log_cli(s, level) + } +} + +// fatal logs line `s` via `send_output` if `Log.level` is greater than or equal to the `Level.fatal` category. +pub fn (mut l Log) fatal(s string) { + if int(l.level) < int(Level.fatal) { + return + } + l.send_output(s, .fatal) + l.ofile.close() + panic('$l.output_label: $s') +} + +// error logs line `s` via `send_output` if `Log.level` is greater than or equal to the `Level.error` category. +pub fn (mut l Log) error(s string) { + if int(l.level) < int(Level.error) { + return + } + l.send_output(s, .error) +} + +// warn logs line `s` via `send_output` if `Log.level` is greater than or equal to the `Level.warn` category. +pub fn (mut l Log) warn(s string) { + if int(l.level) < int(Level.warn) { + return + } + l.send_output(s, .warn) +} + +// info logs line `s` via `send_output` if `Log.level` is greater than or equal to the `Level.info` category. +pub fn (mut l Log) info(s string) { + if int(l.level) < int(Level.info) { + return + } + l.send_output(s, .info) +} + +// debug logs line `s` via `send_output` if `Log.level` is greater than or equal to the `Level.debug` category. +pub fn (mut l Log) debug(s string) { + if int(l.level) < int(Level.debug) { + return + } + l.send_output(s, .debug) +} diff --git a/v_windows/v/old/vlib/math/big/big.v b/v_windows/v/old/vlib/math/big/big.v new file mode 100644 index 0000000..a93aa9e --- /dev/null +++ b/v_windows/v/old/vlib/math/big/big.v @@ -0,0 +1,322 @@ +module big + +// Wrapper for https://github.com/kokke/tiny-bignum-c +#flag -I @VEXEROOT/thirdparty/bignum +#flag @VEXEROOT/thirdparty/bignum/bn.o +#include "bn.h" + +struct C.bn { +mut: + array [32]u32 +} + +// Big unsigned integer number. +type Number = C.bn + +fn C.bignum_init(n &Number) + +fn C.bignum_from_int(n &Number, i u64) + +fn C.bignum_to_int(n &Number) int + +fn C.bignum_from_string(n &Number, s &char, nbytes int) + +fn C.bignum_to_string(n &Number, s &char, maxsize int) + +// c = a + b +fn C.bignum_add(a &Number, b &Number, c &Number) + +// c = a - b +fn C.bignum_sub(a &Number, b &Number, c &Number) + +// c = a * b +fn C.bignum_mul(a &Number, b &Number, c &Number) + +// c = a / b +fn C.bignum_div(a &Number, b &Number, c &Number) + +// c = a % b +fn C.bignum_mod(a &Number, b &Number, c &Number) + +// c = a/b d=a%b +fn C.bignum_divmod(a &Number, b &Number, c &Number, d &Number) + +// c = a & b +fn C.bignum_and(a &Number, b &Number, c &Number) + +// c = a | b +fn C.bignum_or(a &Number, b &Number, c &Number) + +// c = a xor b +fn C.bignum_xor(a &Number, b &Number, c &Number) + +// b = a << nbits +fn C.bignum_lshift(a &Number, b &Number, nbits int) + +// b = a >> nbits +fn C.bignum_rshift(a &Number, b &Number, nbits int) + +fn C.bignum_cmp(a &Number, b &Number) int + +fn C.bignum_is_zero(a &Number) int + +// n++ +fn C.bignum_inc(n &Number) + +// n-- +fn C.bignum_dec(n &Number) + +// c = a ^ b +fn C.bignum_pow(a &Number, b &Number, c &Number) + +// b = integer_square_root_of(a) +fn C.bignum_isqrt(a &Number, b &Number) + +// copy src number to dst number +fn C.bignum_assign(dst &Number, src &Number) + +// new returns a bignum, initialized to 0 +pub fn new() Number { + return Number{} +} + +// conversion actions to/from big numbers: +// from_int converts an ordinary int number `i` to big.Number +pub fn from_int(i int) Number { + n := Number{} + C.bignum_from_int(&n, i) + return n +} + +// from_u64 converts an ordinary u64 number `u` to big.Number +pub fn from_u64(u u64) Number { + n := Number{} + C.bignum_from_int(&n, u) + return n +} + +// from_hex_string converts a hex string to big.Number +pub fn from_hex_string(input string) Number { + mut s := input.trim_prefix('0x') + if s.len == 0 { + s = '0' + } + padding := '0'.repeat((8 - s.len % 8) % 8) + s = padding + s + n := Number{} + C.bignum_from_string(&n, &char(s.str), s.len) + return n +} + +// from_string converts a decimal string to big.Number +pub fn from_string(input string) Number { + mut n := from_int(0) + for _, c in input { + d := from_int(int(c - `0`)) + n = (n * big.ten) + d + } + return n +} + +// .int() converts (a small) big.Number `n` to an ordinary integer. +pub fn (n &Number) int() int { + r := C.bignum_to_int(n) + return r +} + +const ( + ten = from_int(10) +) + +// .str returns a decimal representation of the big unsigned integer number n. +pub fn (n &Number) str() string { + if n.is_zero() { + return '0' + } + mut digits := []byte{} + mut x := n.clone() + + for !x.is_zero() { + // changes to reflect new api + div, mod := divmod(&x, &big.ten) + digits << byte(mod.int()) + `0` + x = div + } + return digits.reverse().bytestr() +} + +// .hexstr returns a hexadecimal representation of the bignum `n` +pub fn (n &Number) hexstr() string { + mut buf := [8192]byte{} + mut s := '' + unsafe { + bp := &buf[0] + // NB: C.bignum_to_string(), returns the HEXADECIMAL representation of the bignum n + C.bignum_to_string(n, &char(bp), 8192) + s = tos_clone(bp) + } + if s.len == 0 { + return '0' + } + return s +} + +// ////////////////////////////////////////////////////////// +// overloaded ops for the numbers: +pub fn (a &Number) + (b &Number) Number { + c := Number{} + C.bignum_add(a, b, &c) + return c +} + +pub fn (a &Number) - (b &Number) Number { + c := Number{} + C.bignum_sub(a, b, &c) + return c +} + +pub fn (a &Number) * (b &Number) Number { + c := Number{} + C.bignum_mul(a, b, &c) + return c +} + +pub fn (a &Number) / (b &Number) Number { + c := Number{} + C.bignum_div(a, b, &c) + return c +} + +pub fn (a &Number) % (b &Number) Number { + c := Number{} + C.bignum_mod(a, b, &c) + return c +} + +// divmod returns a pair of quotient and remainder from div modulo operation +// between two bignums `a` and `b` +pub fn divmod(a &Number, b &Number) (Number, Number) { + c := Number{} + d := Number{} + C.bignum_divmod(a, b, &c, &d) + return c, d +} + +// ////////////////////////////////////////////////////////// +pub fn cmp(a &Number, b &Number) int { + return C.bignum_cmp(a, b) +} + +pub fn (a &Number) is_zero() bool { + return C.bignum_is_zero(a) != 0 +} + +pub fn (mut a Number) inc() { + C.bignum_inc(&a) +} + +pub fn (mut a Number) dec() { + C.bignum_dec(&a) +} + +pub fn pow(a &Number, b &Number) Number { + c := Number{} + C.bignum_pow(a, b, &c) + return c +} + +pub fn (a &Number) isqrt() Number { + b := Number{} + C.bignum_isqrt(a, &b) + return b +} + +// ////////////////////////////////////////////////////////// +pub fn b_and(a &Number, b &Number) Number { + c := Number{} + C.bignum_and(a, b, &c) + return c +} + +pub fn b_or(a &Number, b &Number) Number { + c := Number{} + C.bignum_or(a, b, &c) + return c +} + +pub fn b_xor(a &Number, b &Number) Number { + c := Number{} + C.bignum_xor(a, b, &c) + return c +} + +pub fn (a &Number) lshift(nbits int) Number { + b := Number{} + C.bignum_lshift(a, &b, nbits) + return b +} + +pub fn (a &Number) rshift(nbits int) Number { + b := Number{} + C.bignum_rshift(a, &b, nbits) + return b +} + +pub fn (a &Number) clone() Number { + b := Number{} + C.bignum_assign(&b, a) + return b +} + +// ////////////////////////////////////////////////////////// +pub fn factorial(nn &Number) Number { + mut n := nn.clone() + mut a := nn.clone() + n.dec() + mut i := 1 + for !n.is_zero() { + res := a * n + n.dec() + a = res + i++ + } + return a +} + +pub fn fact(n int) Number { + return factorial(from_int(n)) +} + +// bytes returns an array of the bytes for the number `n`, +// in little endian format, where .bytes()[0] is the least +// significant byte. The result is NOT trimmed, and will contain 0s, even +// after the significant bytes. +// This method is faster than .bytes_trimmed(), but may be less convenient. +// Example: assert big.from_int(1).bytes()[0] == byte(0x01) +// Example: assert big.from_int(1024).bytes()[1] == byte(0x04) +// Example: assert big.from_int(1048576).bytes()[2] == byte(0x10) +pub fn (n &Number) bytes() []byte { + mut res := []byte{len: 128, init: 0} + unsafe { C.memcpy(res.data, n, 128) } + return res +} + +// bytes_trimmed returns an array of the bytes for the number `n`, +// in little endian format, where .bytes_trimmed()[0] is the least +// significant byte. The result is trimmed, so that *the last* byte +// of the result is also the the last meaningfull byte, != 0 . +// Example: assert big.from_int(1).bytes_trimmed() == [byte(0x01)] +// Example: assert big.from_int(1024).bytes_trimmed() == [byte(0x00), 0x04] +// Example: assert big.from_int(1048576).bytes_trimmed() == [byte(0x00), 0x00, 0x10] +pub fn (n &Number) bytes_trimmed() []byte { + mut res := []byte{len: 128, init: 0} + unsafe { C.memcpy(res.data, n, 128) } + mut non_zero_idx := 127 + for ; non_zero_idx >= 0; non_zero_idx-- { + if res[non_zero_idx] != 0 { + break + } + } + res.trim(non_zero_idx + 1) + return res +} diff --git a/v_windows/v/old/vlib/math/big/big_test.v b/v_windows/v/old/vlib/math/big/big_test.v new file mode 100644 index 0000000..0742b81 --- /dev/null +++ b/v_windows/v/old/vlib/math/big/big_test.v @@ -0,0 +1,167 @@ +import math.big + +fn test_new_big() { + n := big.new() + assert sizeof(big.Number) == 128 + assert n.hexstr() == '0' +} + +fn test_from_int() { + assert big.from_int(255).hexstr() == 'ff' + assert big.from_int(127).hexstr() == '7f' + assert big.from_int(1024).hexstr() == '400' + assert big.from_int(2147483647).hexstr() == '7fffffff' + assert big.from_int(-1).hexstr() == 'ffffffffffffffff' +} + +fn test_from_u64() { + assert big.from_u64(255).hexstr() == 'ff' + assert big.from_u64(127).hexstr() == '7f' + assert big.from_u64(1024).hexstr() == '400' + assert big.from_u64(4294967295).hexstr() == 'ffffffff' + assert big.from_u64(4398046511104).hexstr() == '40000000000' + assert big.from_u64(-1).hexstr() == 'ffffffffffffffff' +} + +fn test_plus() { + mut a := big.from_u64(2) + b := big.from_u64(3) + c := a + b + assert c.hexstr() == '5' + assert (big.from_u64(1024) + big.from_u64(1024)).hexstr() == '800' + a += b + assert a.hexstr() == '5' + a.inc() + assert a.hexstr() == '6' + a.dec() + a.dec() + assert a.hexstr() == '4' +} + +fn test_minus() { + a := big.from_u64(2) + mut b := big.from_u64(3) + c := b - a + assert c.hexstr() == '1' + e := big.from_u64(1024) + ee := e - e + assert ee.hexstr() == '0' + b -= a + assert b.hexstr() == '1' +} + +fn test_divide() { + a := big.from_u64(2) + mut b := big.from_u64(3) + c := b / a + assert c.hexstr() == '1' + assert (b % a).hexstr() == '1' + e := big.from_u64(1024) // dec(1024) == hex(0x400) + ee := e / e + assert ee.hexstr() == '1' + assert (e / a).hexstr() == '200' + assert (e / (a * a)).hexstr() == '100' + b /= a + assert b.hexstr() == '1' +} + +fn test_multiply() { + mut a := big.from_u64(2) + b := big.from_u64(3) + c := b * a + assert c.hexstr() == '6' + e := big.from_u64(1024) + e2 := e * e + e4 := e2 * e2 + e8 := e2 * e2 * e2 * e2 + e9 := e8 + big.from_u64(1) + d := ((e9 * e9) + b) * c + assert e4.hexstr() == '10000000000' + assert e8.hexstr() == '100000000000000000000' + assert e9.hexstr() == '100000000000000000001' + assert d.hexstr() == '60000000000000000000c00000000000000000018' + a *= b + assert a.hexstr() == '6' +} + +fn test_mod() { + assert (big.from_u64(13) % big.from_u64(10)).int() == 3 + assert (big.from_u64(13) % big.from_u64(9)).int() == 4 + assert (big.from_u64(7) % big.from_u64(5)).int() == 2 +} + +fn test_divmod() { + x, y := big.divmod(big.from_u64(13), big.from_u64(10)) + assert x.int() == 1 + assert y.int() == 3 + p, q := big.divmod(big.from_u64(13), big.from_u64(9)) + assert p.int() == 1 + assert q.int() == 4 + c, d := big.divmod(big.from_u64(7), big.from_u64(5)) + assert c.int() == 1 + assert d.int() == 2 +} + +fn test_from_str() { + assert big.from_string('9870123').str() == '9870123' + assert big.from_string('').str() == '0' + assert big.from_string('0').str() == '0' + assert big.from_string('1').str() == '1' + for i := 1; i < 307; i += 61 { + input := '9'.repeat(i) + out := big.from_string(input).str() + // eprintln('>> i: $i input: $input.str()') + // eprintln('>> i: $i out: $out.str()') + assert input == out + } +} + +fn test_from_hex_str() { + assert big.from_hex_string('0x123').hexstr() == '123' + for i in 1 .. 33 { + input := 'e'.repeat(i) + out := big.from_hex_string(input).hexstr() + assert input == out + } + assert big.from_string('0').hexstr() == '0' +} + +fn test_str() { + assert big.from_u64(255).str() == '255' + assert big.from_u64(127).str() == '127' + assert big.from_u64(1024).str() == '1024' + assert big.from_u64(4294967295).str() == '4294967295' + assert big.from_u64(4398046511104).str() == '4398046511104' + assert big.from_int(int(4294967295)).str() == '18446744073709551615' + assert big.from_int(-1).str() == '18446744073709551615' + assert big.from_hex_string('e'.repeat(80)).str() == '1993587900192849410235353592424915306962524220866209251950572167300738410728597846688097947807470' +} + +fn test_factorial() { + f5 := big.factorial(big.from_u64(5)) + assert f5.hexstr() == '78' + f100 := big.factorial(big.from_u64(100)) + assert f100.hexstr() == '1b30964ec395dc24069528d54bbda40d16e966ef9a70eb21b5b2943a321cdf10391745570cca9420c6ecb3b72ed2ee8b02ea2735c61a000000000000000000000000' +} + +fn trimbytes(n int, x []byte) []byte { + mut res := x.clone() + res.trim(n) + return res +} + +fn test_bytes() { + assert big.from_int(0).bytes().len == 128 + assert big.from_hex_string('e'.repeat(100)).bytes().len == 128 + assert trimbytes(3, big.from_int(1).bytes()) == [byte(0x01), 0x00, 0x00] + assert trimbytes(3, big.from_int(1024).bytes()) == [byte(0x00), 0x04, 0x00] + assert trimbytes(3, big.from_int(1048576).bytes()) == [byte(0x00), 0x00, 0x10] +} + +fn test_bytes_trimmed() { + assert big.from_int(0).bytes_trimmed().len == 0 + assert big.from_hex_string('AB'.repeat(50)).bytes_trimmed().len == 50 + assert big.from_int(1).bytes_trimmed() == [byte(0x01)] + assert big.from_int(1024).bytes_trimmed() == [byte(0x00), 0x04] + assert big.from_int(1048576).bytes_trimmed() == [byte(0x00), 0x00, 0x10] +} diff --git a/v_windows/v/old/vlib/math/bits.v b/v_windows/v/old/vlib/math/bits.v new file mode 100644 index 0000000..0626504 --- /dev/null +++ b/v_windows/v/old/vlib/math/bits.v @@ -0,0 +1,59 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module math + +const ( + uvnan = u64(0x7FF8000000000001) + uvinf = u64(0x7FF0000000000000) + uvneginf = u64(0xFFF0000000000000) + uvone = u64(0x3FF0000000000000) + mask = 0x7FF + shift = 64 - 11 - 1 + bias = 1023 + sign_mask = (u64(1) << 63) + frac_mask = ((u64(1) << u64(shift)) - u64(1)) +) + +// inf returns positive infinity if sign >= 0, negative infinity if sign < 0. +pub fn inf(sign int) f64 { + v := if sign >= 0 { math.uvinf } else { math.uvneginf } + return f64_from_bits(v) +} + +// nan returns an IEEE 754 ``not-a-number'' value. +pub fn nan() f64 { + return f64_from_bits(math.uvnan) +} + +// is_nan reports whether f is an IEEE 754 ``not-a-number'' value. +pub fn is_nan(f f64) bool { + // IEEE 754 says that only NaNs satisfy f != f. + // To avoid the floating-point hardware, could use: + // x := f64_bits(f); + // return u32(x>>shift)&mask == mask && x != uvinf && x != uvneginf + return f != f +} + +// is_inf reports whether f is an infinity, according to sign. +// If sign > 0, is_inf reports whether f is positive infinity. +// If sign < 0, is_inf reports whether f is negative infinity. +// If sign == 0, is_inf reports whether f is either infinity. +pub fn is_inf(f f64, sign int) bool { + // Test for infinity by comparing against maximum float. + // To avoid the floating-point hardware, could use: + // x := f64_bits(f); + // return sign >= 0 && x == uvinf || sign <= 0 && x == uvneginf; + return (sign >= 0 && f > max_f64) || (sign <= 0 && f < -max_f64) +} + +// NOTE: (joe-c) exponent notation is borked +// normalize returns a normal number y and exponent exp +// satisfying x == y × 2**exp. It assumes x is finite and non-zero. +// pub fn normalize(x f64) (f64, int) { +// smallest_normal := 2.2250738585072014e-308 // 2**-1022 +// if abs(x) < smallest_normal { +// return x * (1 << 52), -52 +// } +// return x, 0 +// } diff --git a/v_windows/v/old/vlib/math/bits/bits.v b/v_windows/v/old/vlib/math/bits/bits.v new file mode 100644 index 0000000..a3d2220 --- /dev/null +++ b/v_windows/v/old/vlib/math/bits/bits.v @@ -0,0 +1,478 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module bits + +const ( + // See http://supertech.csail.mit.edu/papers/debruijn.pdf + de_bruijn32 = u32(0x077CB531) + de_bruijn32tab = [byte(0), 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, + 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9] + de_bruijn64 = u64(0x03f79d71b4ca8b09) + de_bruijn64tab = [byte(0), 1, 56, 2, 57, 49, 28, 3, 61, 58, 42, 50, 38, 29, 17, 4, 62, 47, + 59, 36, 45, 43, 51, 22, 53, 39, 33, 30, 24, 18, 12, 5, 63, 55, 48, 27, 60, 41, 37, 16, + 46, 35, 44, 21, 52, 32, 23, 11, 54, 26, 40, 15, 34, 20, 31, 10, 25, 14, 19, 9, 13, 8, 7, + 6, + ] +) + +const ( + m0 = u64(0x5555555555555555) // 01010101 ... + m1 = u64(0x3333333333333333) // 00110011 ... + m2 = u64(0x0f0f0f0f0f0f0f0f) // 00001111 ... + m3 = u64(0x00ff00ff00ff00ff) // etc. + m4 = u64(0x0000ffff0000ffff) +) + +const ( + // save importing math mod just for these + max_u32 = u32(4294967295) + max_u64 = u64(18446744073709551615) +) + +// --- LeadingZeros --- +// leading_zeros_8 returns the number of leading zero bits in x; the result is 8 for x == 0. +pub fn leading_zeros_8(x byte) int { + return 8 - len_8(x) +} + +// leading_zeros_16 returns the number of leading zero bits in x; the result is 16 for x == 0. +pub fn leading_zeros_16(x u16) int { + return 16 - len_16(x) +} + +// leading_zeros_32 returns the number of leading zero bits in x; the result is 32 for x == 0. +pub fn leading_zeros_32(x u32) int { + return 32 - len_32(x) +} + +// leading_zeros_64 returns the number of leading zero bits in x; the result is 64 for x == 0. +pub fn leading_zeros_64(x u64) int { + return 64 - len_64(x) +} + +// --- TrailingZeros --- +// trailing_zeros_8 returns the number of trailing zero bits in x; the result is 8 for x == 0. +pub fn trailing_zeros_8(x byte) int { + return int(ntz_8_tab[x]) +} + +// trailing_zeros_16 returns the number of trailing zero bits in x; the result is 16 for x == 0. +pub fn trailing_zeros_16(x u16) int { + if x == 0 { + return 16 + } + // see comment in trailing_zeros_64 + return int(bits.de_bruijn32tab[u32(x & -x) * bits.de_bruijn32 >> (32 - 5)]) +} + +// trailing_zeros_32 returns the number of trailing zero bits in x; the result is 32 for x == 0. +pub fn trailing_zeros_32(x u32) int { + if x == 0 { + return 32 + } + // see comment in trailing_zeros_64 + return int(bits.de_bruijn32tab[(x & -x) * bits.de_bruijn32 >> (32 - 5)]) +} + +// trailing_zeros_64 returns the number of trailing zero bits in x; the result is 64 for x == 0. +pub fn trailing_zeros_64(x u64) int { + if x == 0 { + return 64 + } + // If popcount is fast, replace code below with return popcount(^x & (x - 1)). + // + // x & -x leaves only the right-most bit set in the word. Let k be the + // index of that bit. Since only a single bit is set, the value is two + // to the power of k. Multiplying by a power of two is equivalent to + // left shifting, in this case by k bits. The de Bruijn (64 bit) constant + // is such that all six bit, consecutive substrings are distinct. + // Therefore, if we have a left shifted version of this constant we can + // find by how many bits it was shifted by looking at which six bit + // substring ended up at the top of the word. + // (Knuth, volume 4, section 7.3.1) + return int(bits.de_bruijn64tab[(x & -x) * bits.de_bruijn64 >> (64 - 6)]) +} + +// --- OnesCount --- +// ones_count_8 returns the number of one bits ("population count") in x. +pub fn ones_count_8(x byte) int { + return int(pop_8_tab[x]) +} + +// ones_count_16 returns the number of one bits ("population count") in x. +pub fn ones_count_16(x u16) int { + return int(pop_8_tab[x >> 8] + pop_8_tab[x & u16(0xff)]) +} + +// ones_count_32 returns the number of one bits ("population count") in x. +pub fn ones_count_32(x u32) int { + return int(pop_8_tab[x >> 24] + pop_8_tab[x >> 16 & 0xff] + pop_8_tab[x >> 8 & 0xff] + + pop_8_tab[x & u32(0xff)]) +} + +// ones_count_64 returns the number of one bits ("population count") in x. +pub fn ones_count_64(x u64) int { + // Implementation: Parallel summing of adjacent bits. + // See "Hacker's Delight", Chap. 5: Counting Bits. + // The following pattern shows the general approach: + // + // x = x>>1&(m0&m) + x&(m0&m) + // x = x>>2&(m1&m) + x&(m1&m) + // x = x>>4&(m2&m) + x&(m2&m) + // x = x>>8&(m3&m) + x&(m3&m) + // x = x>>16&(m4&m) + x&(m4&m) + // x = x>>32&(m5&m) + x&(m5&m) + // return int(x) + // + // Masking (& operations) can be left away when there's no + // danger that a field's sum will carry over into the next + // field: Since the result cannot be > 64, 8 bits is enough + // and we can ignore the masks for the shifts by 8 and up. + // Per "Hacker's Delight", the first line can be simplified + // more, but it saves at best one instruction, so we leave + // it alone for clarity. + mut y := (x >> u64(1) & (bits.m0 & bits.max_u64)) + (x & (bits.m0 & bits.max_u64)) + y = (y >> u64(2) & (bits.m1 & bits.max_u64)) + (y & (bits.m1 & bits.max_u64)) + y = ((y >> 4) + y) & (bits.m2 & bits.max_u64) + y += y >> 8 + y += y >> 16 + y += y >> 32 + return int(y) & ((1 << 7) - 1) +} + +// --- RotateLeft --- +// rotate_left_8 returns the value of x rotated left by (k mod 8) bits. +// To rotate x right by k bits, call rotate_left_8(x, -k). +// +// This function's execution time does not depend on the inputs. +[inline] +pub fn rotate_left_8(x byte, k int) byte { + n := byte(8) + s := byte(k) & (n - byte(1)) + return ((x << s) | (x >> (n - s))) +} + +// rotate_left_16 returns the value of x rotated left by (k mod 16) bits. +// To rotate x right by k bits, call rotate_left_16(x, -k). +// +// This function's execution time does not depend on the inputs. +[inline] +pub fn rotate_left_16(x u16, k int) u16 { + n := u16(16) + s := u16(k) & (n - u16(1)) + return ((x << s) | (x >> (n - s))) +} + +// rotate_left_32 returns the value of x rotated left by (k mod 32) bits. +// To rotate x right by k bits, call rotate_left_32(x, -k). +// +// This function's execution time does not depend on the inputs. +[inline] +pub fn rotate_left_32(x u32, k int) u32 { + n := u32(32) + s := u32(k) & (n - u32(1)) + return ((x << s) | (x >> (n - s))) +} + +// rotate_left_64 returns the value of x rotated left by (k mod 64) bits. +// To rotate x right by k bits, call rotate_left_64(x, -k). +// +// This function's execution time does not depend on the inputs. +[inline] +pub fn rotate_left_64(x u64, k int) u64 { + n := u64(64) + s := u64(k) & (n - u64(1)) + return ((x << s) | (x >> (n - s))) +} + +// --- Reverse --- +// reverse_8 returns the value of x with its bits in reversed order. +[inline] +pub fn reverse_8(x byte) byte { + return rev_8_tab[x] +} + +// reverse_16 returns the value of x with its bits in reversed order. +[inline] +pub fn reverse_16(x u16) u16 { + return u16(rev_8_tab[x >> 8]) | (u16(rev_8_tab[x & u16(0xff)]) << 8) +} + +// reverse_32 returns the value of x with its bits in reversed order. +[inline] +pub fn reverse_32(x u32) u32 { + mut y := ((x >> u32(1) & (bits.m0 & bits.max_u32)) | ((x & (bits.m0 & bits.max_u32)) << 1)) + y = ((y >> u32(2) & (bits.m1 & bits.max_u32)) | ((y & (bits.m1 & bits.max_u32)) << u32(2))) + y = ((y >> u32(4) & (bits.m2 & bits.max_u32)) | ((y & (bits.m2 & bits.max_u32)) << u32(4))) + return reverse_bytes_32(u32(y)) +} + +// reverse_64 returns the value of x with its bits in reversed order. +[inline] +pub fn reverse_64(x u64) u64 { + mut y := ((x >> u64(1) & (bits.m0 & bits.max_u64)) | ((x & (bits.m0 & bits.max_u64)) << 1)) + y = ((y >> u64(2) & (bits.m1 & bits.max_u64)) | ((y & (bits.m1 & bits.max_u64)) << 2)) + y = ((y >> u64(4) & (bits.m2 & bits.max_u64)) | ((y & (bits.m2 & bits.max_u64)) << 4)) + return reverse_bytes_64(y) +} + +// --- ReverseBytes --- +// reverse_bytes_16 returns the value of x with its bytes in reversed order. +// +// This function's execution time does not depend on the inputs. +[inline] +pub fn reverse_bytes_16(x u16) u16 { + return (x >> 8) | (x << 8) +} + +// reverse_bytes_32 returns the value of x with its bytes in reversed order. +// +// This function's execution time does not depend on the inputs. +[inline] +pub fn reverse_bytes_32(x u32) u32 { + y := ((x >> u32(8) & (bits.m3 & bits.max_u32)) | ((x & (bits.m3 & bits.max_u32)) << u32(8))) + return u32((y >> 16) | (y << 16)) +} + +// reverse_bytes_64 returns the value of x with its bytes in reversed order. +// +// This function's execution time does not depend on the inputs. +[inline] +pub fn reverse_bytes_64(x u64) u64 { + mut y := ((x >> u64(8) & (bits.m3 & bits.max_u64)) | ((x & (bits.m3 & bits.max_u64)) << u64(8))) + y = ((y >> u64(16) & (bits.m4 & bits.max_u64)) | ((y & (bits.m4 & bits.max_u64)) << u64(16))) + return (y >> 32) | (y << 32) +} + +// --- Len --- +// len_8 returns the minimum number of bits required to represent x; the result is 0 for x == 0. +pub fn len_8(x byte) int { + return int(len_8_tab[x]) +} + +// len_16 returns the minimum number of bits required to represent x; the result is 0 for x == 0. +pub fn len_16(x u16) int { + mut y := x + mut n := 0 + if y >= 1 << 8 { + y >>= 8 + n = 8 + } + return n + int(len_8_tab[y]) +} + +// len_32 returns the minimum number of bits required to represent x; the result is 0 for x == 0. +pub fn len_32(x u32) int { + mut y := x + mut n := 0 + if y >= (1 << 16) { + y >>= 16 + n = 16 + } + if y >= (1 << 8) { + y >>= 8 + n += 8 + } + return n + int(len_8_tab[y]) +} + +// len_64 returns the minimum number of bits required to represent x; the result is 0 for x == 0. +pub fn len_64(x u64) int { + mut y := x + mut n := 0 + if y >= u64(1) << u64(32) { + y >>= 32 + n = 32 + } + if y >= u64(1) << u64(16) { + y >>= 16 + n += 16 + } + if y >= u64(1) << u64(8) { + y >>= 8 + n += 8 + } + return n + int(len_8_tab[y]) +} + +// --- Add with carry --- +// Add returns the sum with carry of x, y and carry: sum = x + y + carry. +// The carry input must be 0 or 1; otherwise the behavior is undefined. +// The carryOut output is guaranteed to be 0 or 1. +// +// add_32 returns the sum with carry of x, y and carry: sum = x + y + carry. +// The carry input must be 0 or 1; otherwise the behavior is undefined. +// The carryOut output is guaranteed to be 0 or 1. +// +// This function's execution time does not depend on the inputs. +pub fn add_32(x u32, y u32, carry u32) (u32, u32) { + sum64 := u64(x) + u64(y) + u64(carry) + sum := u32(sum64) + carry_out := u32(sum64 >> 32) + return sum, carry_out +} + +// add_64 returns the sum with carry of x, y and carry: sum = x + y + carry. +// The carry input must be 0 or 1; otherwise the behavior is undefined. +// The carryOut output is guaranteed to be 0 or 1. +// +// This function's execution time does not depend on the inputs. +pub fn add_64(x u64, y u64, carry u64) (u64, u64) { + sum := x + y + carry + // The sum will overflow if both top bits are set (x & y) or if one of them + // is (x | y), and a carry from the lower place happened. If such a carry + // happens, the top bit will be 1 + 0 + 1 = 0 (&^ sum). + carry_out := ((x & y) | ((x | y) & ~sum)) >> 63 + return sum, carry_out +} + +// --- Subtract with borrow --- +// Sub returns the difference of x, y and borrow: diff = x - y - borrow. +// The borrow input must be 0 or 1; otherwise the behavior is undefined. +// The borrowOut output is guaranteed to be 0 or 1. +// +// sub_32 returns the difference of x, y and borrow, diff = x - y - borrow. +// The borrow input must be 0 or 1; otherwise the behavior is undefined. +// The borrowOut output is guaranteed to be 0 or 1. +// +// This function's execution time does not depend on the inputs. +pub fn sub_32(x u32, y u32, borrow u32) (u32, u32) { + diff := x - y - borrow + // The difference will underflow if the top bit of x is not set and the top + // bit of y is set (^x & y) or if they are the same (^(x ^ y)) and a borrow + // from the lower place happens. If that borrow happens, the result will be + // 1 - 1 - 1 = 0 - 0 - 1 = 1 (& diff). + borrow_out := ((~x & y) | (~(x ^ y) & diff)) >> 31 + return diff, borrow_out +} + +// sub_64 returns the difference of x, y and borrow: diff = x - y - borrow. +// The borrow input must be 0 or 1; otherwise the behavior is undefined. +// The borrowOut output is guaranteed to be 0 or 1. +// +// This function's execution time does not depend on the inputs. +pub fn sub_64(x u64, y u64, borrow u64) (u64, u64) { + diff := x - y - borrow + // See Sub32 for the bit logic. + borrow_out := ((~x & y) | (~(x ^ y) & diff)) >> 63 + return diff, borrow_out +} + +// --- Full-width multiply --- +const ( + two32 = u64(0x100000000) + mask32 = two32 - 1 + overflow_error = 'Overflow Error' + divide_error = 'Divide Error' +) + +// mul_32 returns the 64-bit product of x and y: (hi, lo) = x * y +// with the product bits' upper half returned in hi and the lower +// half returned in lo. +// +// This function's execution time does not depend on the inputs. +pub fn mul_32(x u32, y u32) (u32, u32) { + tmp := u64(x) * u64(y) + hi := u32(tmp >> 32) + lo := u32(tmp) + return hi, lo +} + +// mul_64 returns the 128-bit product of x and y: (hi, lo) = x * y +// with the product bits' upper half returned in hi and the lower +// half returned in lo. +// +// This function's execution time does not depend on the inputs. +pub fn mul_64(x u64, y u64) (u64, u64) { + x0 := x & bits.mask32 + x1 := x >> 32 + y0 := y & bits.mask32 + y1 := y >> 32 + w0 := x0 * y0 + t := x1 * y0 + (w0 >> 32) + mut w1 := t & bits.mask32 + w2 := t >> 32 + w1 += x0 * y1 + hi := x1 * y1 + w2 + (w1 >> 32) + lo := x * y + return hi, lo +} + +// --- Full-width divide --- +// div_32 returns the quotient and remainder of (hi, lo) divided by y: +// quo = (hi, lo)/y, rem = (hi, lo)%y with the dividend bits' upper +// half in parameter hi and the lower half in parameter lo. +// div_32 panics for y == 0 (division by zero) or y <= hi (quotient overflow). +pub fn div_32(hi u32, lo u32, y u32) (u32, u32) { + if y != 0 && y <= hi { + panic(bits.overflow_error) + } + z := (u64(hi) << 32) | u64(lo) + quo := u32(z / u64(y)) + rem := u32(z % u64(y)) + return quo, rem +} + +// div_64 returns the quotient and remainder of (hi, lo) divided by y: +// quo = (hi, lo)/y, rem = (hi, lo)%y with the dividend bits' upper +// half in parameter hi and the lower half in parameter lo. +// div_64 panics for y == 0 (division by zero) or y <= hi (quotient overflow). +pub fn div_64(hi u64, lo u64, y1 u64) (u64, u64) { + mut y := y1 + if y == 0 { + panic(bits.overflow_error) + } + if y <= hi { + panic(bits.overflow_error) + } + s := u32(leading_zeros_64(y)) + y <<= s + yn1 := y >> 32 + yn0 := y & bits.mask32 + un32 := (hi << s) | (lo >> (64 - s)) + un10 := lo << s + un1 := un10 >> 32 + un0 := un10 & bits.mask32 + mut q1 := un32 / yn1 + mut rhat := un32 - q1 * yn1 + for q1 >= bits.two32 || q1 * yn0 > bits.two32 * rhat + un1 { + q1-- + rhat += yn1 + if rhat >= bits.two32 { + break + } + } + un21 := un32 * bits.two32 + un1 - q1 * y + mut q0 := un21 / yn1 + rhat = un21 - q0 * yn1 + for q0 >= bits.two32 || q0 * yn0 > bits.two32 * rhat + un0 { + q0-- + rhat += yn1 + if rhat >= bits.two32 { + break + } + } + return q1 * bits.two32 + q0, (un21 * bits.two32 + un0 - q0 * y) >> s +} + +// rem_32 returns the remainder of (hi, lo) divided by y. Rem32 panics +// for y == 0 (division by zero) but, unlike Div32, it doesn't panic +// on a quotient overflow. +pub fn rem_32(hi u32, lo u32, y u32) u32 { + return u32(((u64(hi) << 32) | u64(lo)) % u64(y)) +} + +// rem_64 returns the remainder of (hi, lo) divided by y. Rem64 panics +// for y == 0 (division by zero) but, unlike div_64, it doesn't panic +// on a quotient overflow. +pub fn rem_64(hi u64, lo u64, y u64) u64 { + // We scale down hi so that hi < y, then use div_64 to compute the + // rem with the guarantee that it won't panic on quotient overflow. + // Given that + // hi ≡ hi%y (mod y) + // we have + // hi<<64 + lo ≡ (hi%y)<<64 + lo (mod y) + _, rem := div_64(hi % y, lo, y) + return rem +} diff --git a/v_windows/v/old/vlib/math/bits/bits_tables.v b/v_windows/v/old/vlib/math/bits/bits_tables.v new file mode 100644 index 0000000..830e1e8 --- /dev/null +++ b/v_windows/v/old/vlib/math/bits/bits_tables.v @@ -0,0 +1,79 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module bits + +const ( + ntz_8_tab = [byte(0x08), 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, + 0x02, 0x00, 0x01, 0x00, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, + 0x00, 0x02, 0x00, 0x01, 0x00, 0x05, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, + 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, + 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x06, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, + 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x05, 0x00, 0x01, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x04, 0x00, 0x01, 0x00, 0x02, + 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x07, 0x00, 0x01, 0x00, + 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x04, 0x00, 0x01, + 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x05, 0x00, + 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x04, + 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, + 0x06, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, + 0x00, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x05, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, 0x02, + 0x00, 0x01, 0x00, 0x04, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x03, 0x00, 0x01, 0x00, + 0x02, 0x00, 0x01, 0x00] + pop_8_tab = [byte(0x00), 0x01, 0x01, 0x02, 0x01, 0x02, 0x02, 0x03, 0x01, 0x02, 0x02, 0x03, + 0x02, 0x03, 0x03, 0x04, 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, 0x02, 0x03, 0x03, + 0x04, 0x03, 0x04, 0x04, 0x05, 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, 0x02, 0x03, + 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, 0x03, + 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, 0x01, 0x02, 0x02, 0x03, 0x02, 0x03, 0x03, 0x04, + 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, + 0x05, 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, + 0x04, 0x05, 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, 0x03, 0x04, 0x04, 0x05, 0x04, + 0x05, 0x05, 0x06, 0x04, 0x05, 0x05, 0x06, 0x05, 0x06, 0x06, 0x07, 0x01, 0x02, 0x02, 0x03, + 0x02, 0x03, 0x03, 0x04, 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, 0x02, 0x03, 0x03, + 0x04, 0x03, 0x04, 0x04, 0x05, 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, 0x02, 0x03, + 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, 0x03, + 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, 0x04, 0x05, 0x05, 0x06, 0x05, 0x06, 0x06, 0x07, + 0x02, 0x03, 0x03, 0x04, 0x03, 0x04, 0x04, 0x05, 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, + 0x06, 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, 0x04, 0x05, 0x05, 0x06, 0x05, 0x06, + 0x06, 0x07, 0x03, 0x04, 0x04, 0x05, 0x04, 0x05, 0x05, 0x06, 0x04, 0x05, 0x05, 0x06, 0x05, + 0x06, 0x06, 0x07, 0x04, 0x05, 0x05, 0x06, 0x05, 0x06, 0x06, 0x07, 0x05, 0x06, 0x06, 0x07, + 0x06, 0x07, 0x07, 0x08] + rev_8_tab = [byte(0x00), 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0, + 0x30, 0xb0, 0x70, 0xf0, 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, 0x18, 0x98, 0x58, + 0xd8, 0x38, 0xb8, 0x78, 0xf8, 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, + 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, + 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, + 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, + 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, 0x0e, 0x8e, 0x4e, 0xce, 0x2e, + 0xae, 0x6e, 0xee, 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 0x01, 0x81, 0x41, 0xc1, + 0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, 0x09, 0x89, 0x49, + 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, 0x05, 0x85, + 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, 0x0d, + 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, + 0xf3, 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, + 0x7b, 0xfb, 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, + 0xb7, 0x77, 0xf7, 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf, + 0x3f, 0xbf, 0x7f, 0xff] + len_8_tab = [byte(0x00), 0x01, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08] +) diff --git a/v_windows/v/old/vlib/math/bits/bits_test.v b/v_windows/v/old/vlib/math/bits/bits_test.v new file mode 100644 index 0000000..23a01b3 --- /dev/null +++ b/v_windows/v/old/vlib/math/bits/bits_test.v @@ -0,0 +1,288 @@ +// +// test suite for bits and bits math functions +// +module bits + +fn test_bits() { + mut i := 0 + mut i1 := u64(0) + + // + // --- LeadingZeros --- + // + + // 8 bit + i = 1 + for x in 0 .. 8 { + // C.printf("x:%02x lz: %d cmp: %d\n", i << x, leading_zeros_8(i << x), 7-x) + assert leading_zeros_8(byte(i << x)) == 7 - x + } + + // 16 bit + i = 1 + for x in 0 .. 16 { + // C.printf("x:%04x lz: %d cmp: %d\n", u16(i) << x, leading_zeros_16(u16(i) << x), 15-x) + assert leading_zeros_16(u16(i) << x) == 15 - x + } + + // 32 bit + i = 1 + for x in 0 .. 32 { + // C.printf("x:%08x lz: %d cmp: %d\n", u32(i) << x, leading_zeros_32(u32(i) << x), 31-x) + assert leading_zeros_32(u32(i) << x) == 31 - x + } + + // 64 bit + i = 1 + for x in 0 .. 64 { + // C.printf("x:%016llx lz: %llu cmp: %d\n", u64(i) << x, leading_zeros_64(u64(i) << x), 63-x) + assert leading_zeros_64(u64(i) << x) == 63 - x + } + + // + // --- ones_count --- + // + + // 8 bit + i = 0 + for x in 0 .. 9 { + // C.printf("x:%02x lz: %llu cmp: %d\n", byte(i), ones_count_8(byte(i)), x) + assert ones_count_8(byte(i)) == x + i = (i << 1) + 1 + } + + // 16 bit + i = 0 + for x in 0 .. 17 { + // C.printf("x:%04x lz: %llu cmp: %d\n", u16(i), ones_count_16(u16(i)), x) + assert ones_count_16(u16(i)) == x + i = (i << 1) + 1 + } + + // 32 bit + i = 0 + for x in 0 .. 33 { + // C.printf("x:%08x lz: %llu cmp: %d\n", u32(i), ones_count_32(u32(i)), x) + assert ones_count_32(u32(i)) == x + i = (i << 1) + 1 + } + + // 64 bit + i1 = 0 + for x in 0 .. 65 { + // C.printf("x:%016llx lz: %llu cmp: %d\n", u64(i1), ones_count_64(u64(i1)), x) + assert ones_count_64(i1) == x + i1 = (i1 << 1) + 1 + } + + // + // --- rotate_left/right --- + // + assert rotate_left_8(0x12, 4) == 0x21 + assert rotate_left_16(0x1234, 8) == 0x3412 + assert rotate_left_32(0x12345678, 16) == 0x56781234 + assert rotate_left_64(0x1234567887654321, 32) == 0x8765432112345678 + + // + // --- reverse --- + // + + // 8 bit + i = 0 + for _ in 0 .. 9 { + mut rv := byte(0) + mut bc := 0 + mut n := i + for bc < 8 { + rv = (rv << 1) | (byte(n) & 0x01) + bc++ + n = n >> 1 + } + // C.printf("x:%02x lz: %llu cmp: %d\n", byte(i), reverse_8(byte(i)), rv) + assert reverse_8(byte(i)) == rv + i = (i << 1) + 1 + } + + // 16 bit + i = 0 + for _ in 0 .. 17 { + mut rv := u16(0) + mut bc := 0 + mut n := i + for bc < 16 { + rv = (rv << 1) | (u16(n) & 0x01) + bc++ + n = n >> 1 + } + // C.printf("x:%04x lz: %llu cmp: %d\n", u16(i), reverse_16(u16(i)), rv) + assert reverse_16(u16(i)) == rv + i = (i << 1) + 1 + } + + // 32 bit + i = 0 + for _ in 0 .. 33 { + mut rv := u32(0) + mut bc := 0 + mut n := i + for bc < 32 { + rv = (rv << 1) | (u32(n) & 0x01) + bc++ + n = n >> 1 + } + // C.printf("x:%08x lz: %llu cmp: %d\n", u32(i), reverse_32(u32(i)), rv) + assert reverse_32(u32(i)) == rv + i = (i << 1) + 1 + } + + // 64 bit + i1 = 0 + for _ in 0 .. 64 { + mut rv := u64(0) + mut bc := 0 + mut n := i1 + for bc < 64 { + rv = (rv << 1) | (n & 0x01) + bc++ + n = n >> 1 + } + // C.printf("x:%016llx lz: %016llx cmp: %016llx\n", u64(i1), reverse_64(u64(i1)), rv) + assert reverse_64(i1) == rv + i1 = (i1 << 1) + 1 + } + + // + // --- add --- + // + + // 32 bit + i = 1 + for x in 0 .. 32 { + v := u32(i) << x + sum, carry := add_32(v, v, u32(0)) + // C.printf("x:%08x [%llu,%llu] %llu\n", u32(i) << x, sum, carry, u64(v) + u64(v)) + assert ((u64(carry) << 32) | u64(sum)) == u64(v) + u64(v) + } + mut sum_32t, mut carry_32t := add_32(0x8000_0000, 0x8000_0000, u32(0)) + assert sum_32t == u32(0) + assert carry_32t == u32(1) + + sum_32t, carry_32t = add_32(0xFFFF_FFFF, 0xFFFF_FFFF, u32(1)) + assert sum_32t == 0xFFFF_FFFF + assert carry_32t == u32(1) + + // 64 bit + i = 1 + for x in 0 .. 63 { + v := u64(i) << x + sum, carry := add_64(v, v, u64(0)) + // C.printf("x:%16x [%llu,%llu] %llu\n", u64(i) << x, sum, carry, u64(v >> 32) + u64(v >> 32)) + assert ((carry << 32) | sum) == v + v + } + mut sum_64t, mut carry_64t := add_64(0x8000_0000_0000_0000, 0x8000_0000_0000_0000, + u64(0)) + assert sum_64t == u64(0) + assert carry_64t == u64(1) + + sum_64t, carry_64t = add_64(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF, u64(1)) + assert sum_64t == 0xFFFF_FFFF_FFFF_FFFF + assert carry_64t == u64(1) + + // + // --- sub --- + // + + // 32 bit + i = 1 + for x in 1 .. 32 { + v0 := u32(i) << x + v1 := v0 >> 1 + mut diff, mut borrow_out := sub_32(v0, v1, u32(0)) + // C.printf("x:%08x [%llu,%llu] %08x\n", u32(i) << x, diff, borrow_out, v0 - v1) + assert diff == v1 + + diff, borrow_out = sub_32(v0, v1, u32(1)) + // C.printf("x:%08x [%llu,%llu] %08x\n", u32(i) << x, diff, borrow_out, v0 - v1) + assert diff == (v1 - 1) + assert borrow_out == u32(0) + + diff, borrow_out = sub_32(v1, v0, u32(1)) + // C.printf("x:%08x [%llu,%llu] %08x\n", u32(i) << x, diff, borrow_out, v1 - v0) + assert borrow_out == u32(1) + } + + // 64 bit + i = 1 + for x in 1 .. 64 { + v0 := u64(i) << x + v1 := v0 >> 1 + mut diff, mut borrow_out := sub_64(v0, v1, u64(0)) + // C.printf("x:%08x [%llu,%llu] %08x\n", u64(i) << x, diff, borrow_out, v0 - v1) + assert diff == v1 + + diff, borrow_out = sub_64(v0, v1, u64(1)) + // C.printf("x:%08x [%llu,%llu] %08x\n", u64(i) << x, diff, borrow_out, v0 - v1) + assert diff == (v1 - 1) + assert borrow_out == u64(0) + + diff, borrow_out = sub_64(v1, v0, u64(1)) + // C.printf("x:%08x [%llu,%llu] %08x\n",u64(i) << x, diff, borrow_out, v1 - v0) + assert borrow_out == u64(1) + } + + // + // --- mul --- + // + + // 32 bit + i = 1 + for x in 0 .. 32 { + v0 := u32(i) << x + v1 := v0 - 1 + hi, lo := mul_32(v0, v1) + assert (u64(hi) << 32) | (u64(lo)) == u64(v0) * u64(v1) + } + + // 64 bit + i = 1 + for x in 0 .. 64 { + v0 := u64(i) << x + v1 := v0 - 1 + hi, lo := mul_64(v0, v1) + // C.printf("v0: %llu v1: %llu [%llu,%llu] tt: %llu\n", v0, v1, hi, lo, (v0 >> 32) * (v1 >> 32)) + assert (hi & 0xFFFF_FFFF_0000_0000) == (((v0 >> 32) * (v1 >> 32)) & 0xFFFF_FFFF_0000_0000) + assert (lo & 0x0000_0000_FFFF_FFFF) == (((v0 & 0x0000_0000_FFFF_FFFF) * (v1 & 0x0000_0000_FFFF_FFFF)) & 0x0000_0000_FFFF_FFFF) + } + + // + // --- div --- + // + + // 32 bit + i = 1 + for x in 0 .. 31 { + hi := u32(i) << x + lo := hi - 1 + y := u32(3) << x + quo, rem := div_32(hi, lo, y) + // C.printf("[%08x_%08x] %08x (%08x,%08x)\n", hi, lo, y, quo, rem) + tst := ((u64(hi) << 32) | u64(lo)) + assert quo == (tst / u64(y)) + assert rem == (tst % u64(y)) + assert rem == rem_32(hi, lo, y) + } + + // 64 bit + i = 1 + for x in 0 .. 62 { + hi := u64(i) << x + lo := u64(2) // hi - 1 + y := u64(0x4000_0000_0000_0000) + quo, rem := div_64(hi, lo, y) + // C.printf("[%016llx_%016llx] %016llx (%016llx,%016llx)\n", hi, lo, y, quo, rem) + assert quo == u64(2) << (x + 1) + _, rem1 := div_64(hi % y, lo, y) + assert rem == rem1 + assert rem == rem_64(hi, lo, y) + } +} diff --git a/v_windows/v/old/vlib/math/complex/complex.v b/v_windows/v/old/vlib/math/complex/complex.v new file mode 100644 index 0000000..9fd7cc8 --- /dev/null +++ b/v_windows/v/old/vlib/math/complex/complex.v @@ -0,0 +1,374 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. + +module complex + +import math + +pub struct Complex { +pub: + re f64 + im f64 +} + +pub fn complex(re f64, im f64) Complex { + return Complex{re, im} +} + +// To String method +pub fn (c Complex) str() string { + mut out := '${c.re:.6f}' + out += if c.im >= 0 { '+${c.im:.6f}' } else { '${c.im:.6f}' } + out += 'i' + return out +} + +// Complex Modulus value +// mod() and abs() return the same +pub fn (c Complex) abs() f64 { + return C.hypot(c.re, c.im) +} + +pub fn (c Complex) mod() f64 { + return c.abs() +} + +// Complex Angle +pub fn (c Complex) angle() f64 { + return math.atan2(c.im, c.re) +} + +// Complex Addition c1 + c2 +pub fn (c1 Complex) + (c2 Complex) Complex { + return Complex{c1.re + c2.re, c1.im + c2.im} +} + +// Complex Substraction c1 - c2 +pub fn (c1 Complex) - (c2 Complex) Complex { + return Complex{c1.re - c2.re, c1.im - c2.im} +} + +// Complex Multiplication c1 * c2 +pub fn (c1 Complex) * (c2 Complex) Complex { + return Complex{(c1.re * c2.re) + ((c1.im * c2.im) * -1), (c1.re * c2.im) + (c1.im * c2.re)} +} + +// Complex Division c1 / c2 +pub fn (c1 Complex) / (c2 Complex) Complex { + denom := (c2.re * c2.re) + (c2.im * c2.im) + return Complex{((c1.re * c2.re) + ((c1.im * -c2.im) * -1)) / denom, ((c1.re * -c2.im) + + (c1.im * c2.re)) / denom} +} + +// Complex Addition c1.add(c2) +pub fn (c1 Complex) add(c2 Complex) Complex { + return c1 + c2 +} + +// Complex Subtraction c1.subtract(c2) +pub fn (c1 Complex) subtract(c2 Complex) Complex { + return c1 - c2 +} + +// Complex Multiplication c1.multiply(c2) +pub fn (c1 Complex) multiply(c2 Complex) Complex { + return Complex{(c1.re * c2.re) + ((c1.im * c2.im) * -1), (c1.re * c2.im) + (c1.im * c2.re)} +} + +// Complex Division c1.divide(c2) +pub fn (c1 Complex) divide(c2 Complex) Complex { + denom := (c2.re * c2.re) + (c2.im * c2.im) + return Complex{((c1.re * c2.re) + ((c1.im * -c2.im) * -1)) / denom, ((c1.re * -c2.im) + + (c1.im * c2.re)) / denom} +} + +// Complex Conjugate +pub fn (c Complex) conjugate() Complex { + return Complex{c.re, -c.im} +} + +// Complex Additive Inverse +// Based on +// http://tutorial.math.lamar.edu/Extras/ComplexPrimer/Arithmetic.aspx +pub fn (c Complex) addinv() Complex { + return Complex{-c.re, -c.im} +} + +// Complex Multiplicative Inverse +// Based on +// http://tutorial.math.lamar.edu/Extras/ComplexPrimer/Arithmetic.aspx +pub fn (c Complex) mulinv() Complex { + return Complex{c.re / (c.re * c.re + c.im * c.im), -c.im / (c.re * c.re + c.im * c.im)} +} + +// Complex Power +// Based on +// https://www.khanacademy.org/math/precalculus/imaginary-and-complex-numbers/multiplying-and-dividing-complex-numbers-in-polar-form/a/complex-number-polar-form-review +pub fn (c Complex) pow(n f64) Complex { + r := math.pow(c.abs(), n) + angle := c.angle() + return Complex{r * math.cos(n * angle), r * math.sin(n * angle)} +} + +// Complex nth root +pub fn (c Complex) root(n f64) Complex { + return c.pow(1.0 / n) +} + +// Complex Exponential +// Using Euler's Identity +// Based on +// https://www.math.wisc.edu/~angenent/Free-Lecture-Notes/freecomplexnumbers.pdf +pub fn (c Complex) exp() Complex { + a := math.exp(c.re) + return Complex{a * math.cos(c.im), a * math.sin(c.im)} +} + +// Complex Natural Logarithm +// Based on +// http://www.chemistrylearning.com/logarithm-of-complex-number/ +pub fn (c Complex) ln() Complex { + return Complex{math.log(c.abs()), c.angle()} +} + +// Complex Log Base Complex +// Based on +// http://www.milefoot.com/math/complex/summaryops.htm +pub fn (c Complex) log(base Complex) Complex { + return base.ln().divide(c.ln()) +} + +// Complex Argument +// Based on +// http://mathworld.wolfram.com/ComplexArgument.html +pub fn (c Complex) arg() f64 { + return math.atan2(c.im, c.re) +} + +// Complex raised to Complex Power +// Based on +// http://mathworld.wolfram.com/ComplexExponentiation.html +pub fn (c Complex) cpow(p Complex) Complex { + a := c.arg() + b := math.pow(c.re, 2) + math.pow(c.im, 2) + d := p.re * a + (1.0 / 2) * p.im * math.log(b) + t1 := math.pow(b, p.re / 2) * math.exp(-p.im * a) + return Complex{t1 * math.cos(d), t1 * math.sin(d)} +} + +// Complex Sin +// Based on +// http://www.milefoot.com/math/complex/functionsofi.htm +pub fn (c Complex) sin() Complex { + return Complex{math.sin(c.re) * math.cosh(c.im), math.cos(c.re) * math.sinh(c.im)} +} + +// Complex Cosine +// Based on +// http://www.milefoot.com/math/complex/functionsofi.htm +pub fn (c Complex) cos() Complex { + return Complex{math.cos(c.re) * math.cosh(c.im), -(math.sin(c.re) * math.sinh(c.im))} +} + +// Complex Tangent +// Based on +// http://www.milefoot.com/math/complex/functionsofi.htm +pub fn (c Complex) tan() Complex { + return c.sin().divide(c.cos()) +} + +// Complex Cotangent +// Based on +// http://www.suitcaseofdreams.net/Trigonometric_Functions.htm +pub fn (c Complex) cot() Complex { + return c.cos().divide(c.sin()) +} + +// Complex Secant +// Based on +// http://www.suitcaseofdreams.net/Trigonometric_Functions.htm +pub fn (c Complex) sec() Complex { + return complex(1, 0).divide(c.cos()) +} + +// Complex Cosecant +// Based on +// http://www.suitcaseofdreams.net/Trigonometric_Functions.htm +pub fn (c Complex) csc() Complex { + return complex(1, 0).divide(c.sin()) +} + +// Complex Arc Sin / Sin Inverse +// Based on +// http://www.milefoot.com/math/complex/summaryops.htm +pub fn (c Complex) asin() Complex { + return complex(0, -1).multiply(complex(0, 1).multiply(c).add(complex(1, 0).subtract(c.pow(2)).root(2)).ln()) +} + +// Complex Arc Consine / Consine Inverse +// Based on +// http://www.milefoot.com/math/complex/summaryops.htm +pub fn (c Complex) acos() Complex { + return complex(0, -1).multiply(c.add(complex(0, 1).multiply(complex(1, 0).subtract(c.pow(2)).root(2))).ln()) +} + +// Complex Arc Tangent / Tangent Inverse +// Based on +// http://www.milefoot.com/math/complex/summaryops.htm +pub fn (c Complex) atan() Complex { + i := complex(0, 1) + return complex(0, 1.0 / 2).multiply(i.add(c).divide(i.subtract(c)).ln()) +} + +// Complex Arc Cotangent / Cotangent Inverse +// Based on +// http://www.suitcaseofdreams.net/Inverse_Functions.htm +pub fn (c Complex) acot() Complex { + return complex(1, 0).divide(c).atan() +} + +// Complex Arc Secant / Secant Inverse +// Based on +// http://www.suitcaseofdreams.net/Inverse_Functions.htm +pub fn (c Complex) asec() Complex { + return complex(1, 0).divide(c).acos() +} + +// Complex Arc Cosecant / Cosecant Inverse +// Based on +// http://www.suitcaseofdreams.net/Inverse_Functions.htm +pub fn (c Complex) acsc() Complex { + return complex(1, 0).divide(c).asin() +} + +// Complex Hyperbolic Sin +// Based on +// http://www.milefoot.com/math/complex/functionsofi.htm +pub fn (c Complex) sinh() Complex { + return Complex{math.cos(c.im) * math.sinh(c.re), math.sin(c.im) * math.cosh(c.re)} +} + +// Complex Hyperbolic Cosine +// Based on +// http://www.milefoot.com/math/complex/functionsofi.htm +pub fn (c Complex) cosh() Complex { + return Complex{math.cos(c.im) * math.cosh(c.re), math.sin(c.im) * math.sinh(c.re)} +} + +// Complex Hyperbolic Tangent +// Based on +// http://www.milefoot.com/math/complex/functionsofi.htm +pub fn (c Complex) tanh() Complex { + return c.sinh().divide(c.cosh()) +} + +// Complex Hyperbolic Cotangent +// Based on +// http://www.suitcaseofdreams.net/Hyperbolic_Functions.htm +pub fn (c Complex) coth() Complex { + return c.cosh().divide(c.sinh()) +} + +// Complex Hyperbolic Secant +// Based on +// http://www.suitcaseofdreams.net/Hyperbolic_Functions.htm +pub fn (c Complex) sech() Complex { + return complex(1, 0).divide(c.cosh()) +} + +// Complex Hyperbolic Cosecant +// Based on +// http://www.suitcaseofdreams.net/Hyperbolic_Functions.htm +pub fn (c Complex) csch() Complex { + return complex(1, 0).divide(c.sinh()) +} + +// Complex Hyperbolic Arc Sin / Sin Inverse +// Based on +// http://www.suitcaseofdreams.net/Inverse__Hyperbolic_Functions.htm +pub fn (c Complex) asinh() Complex { + return c.add(c.pow(2).add(complex(1, 0)).root(2)).ln() +} + +// Complex Hyperbolic Arc Consine / Consine Inverse +// Based on +// http://www.suitcaseofdreams.net/Inverse__Hyperbolic_Functions.htm +pub fn (c Complex) acosh() Complex { + if c.re > 1 { + return c.add(c.pow(2).subtract(complex(1, 0)).root(2)).ln() + } else { + one := complex(1, 0) + return c.add(c.add(one).root(2).multiply(c.subtract(one).root(2))).ln() + } +} + +// Complex Hyperbolic Arc Tangent / Tangent Inverse +// Based on +// http://www.suitcaseofdreams.net/Inverse__Hyperbolic_Functions.htm +pub fn (c Complex) atanh() Complex { + one := complex(1, 0) + if c.re < 1 { + return complex(1.0 / 2, 0).multiply(one.add(c).divide(one.subtract(c)).ln()) + } else { + return complex(1.0 / 2, 0).multiply(one.add(c).ln().subtract(one.subtract(c).ln())) + } +} + +// Complex Hyperbolic Arc Cotangent / Cotangent Inverse +// Based on +// http://www.suitcaseofdreams.net/Inverse__Hyperbolic_Functions.htm +pub fn (c Complex) acoth() Complex { + one := complex(1, 0) + if c.re < 0 || c.re > 1 { + return complex(1.0 / 2, 0).multiply(c.add(one).divide(c.subtract(one)).ln()) + } else { + div := one.divide(c) + return complex(1.0 / 2, 0).multiply(one.add(div).ln().subtract(one.subtract(div).ln())) + } +} + +// Complex Hyperbolic Arc Secant / Secant Inverse +// Based on +// http://www.suitcaseofdreams.net/Inverse__Hyperbolic_Functions.htm +// For certain scenarios, Result mismatch in crossverification with Wolfram Alpha - analysis pending +// pub fn (c Complex) asech() Complex { +// one := complex(1,0) +// if(c.re < -1.0) { +// return one.subtract( +// one.subtract( +// c.pow(2) +// ) +// .root(2) +// ) +// .divide(c) +// .ln() +// } +// else { +// return one.add( +// one.subtract( +// c.pow(2) +// ) +// .root(2) +// ) +// .divide(c) +// .ln() +// } +// } + +// Complex Hyperbolic Arc Cosecant / Cosecant Inverse +// Based on +// http://www.suitcaseofdreams.net/Inverse__Hyperbolic_Functions.htm +pub fn (c Complex) acsch() Complex { + one := complex(1, 0) + if c.re < 0 { + return one.subtract(one.add(c.pow(2)).root(2)).divide(c).ln() + } else { + return one.add(one.add(c.pow(2)).root(2)).divide(c).ln() + } +} + +// Complex Equals +pub fn (c1 Complex) equals(c2 Complex) bool { + return (c1.re == c2.re) && (c1.im == c2.im) +} diff --git a/v_windows/v/old/vlib/math/complex/complex_test.v b/v_windows/v/old/vlib/math/complex/complex_test.v new file mode 100644 index 0000000..ccd448e --- /dev/null +++ b/v_windows/v/old/vlib/math/complex/complex_test.v @@ -0,0 +1,797 @@ +import math +import math.complex as cmplx + +fn tst_res(str1 string, str2 string) bool { + if (math.abs(str1.f64() - str2.f64())) < 1e-5 { + return true + } + return false +} + +fn test_complex_addition() { + // Test is based on and verified from practice examples of Khan Academy + // https://www.khanacademy.org/math/precalculus/imaginary-and-complex-numbers + mut c1 := cmplx.complex(0, -10) + mut c2 := cmplx.complex(-40, 8) + mut result := c1 + c2 + assert result.equals(cmplx.complex(-40, -2)) + c1 = cmplx.complex(-71, 2) + c2 = cmplx.complex(88, -12) + result = c1 + c2 + assert result.equals(cmplx.complex(17, -10)) + c1 = cmplx.complex(0, -30) + c2 = cmplx.complex(52, -30) + result = c1 + c2 + assert result.equals(cmplx.complex(52, -60)) + c1 = cmplx.complex(12, -9) + c2 = cmplx.complex(32, -6) + result = c1 + c2 + assert result.equals(cmplx.complex(44, -15)) +} + +fn test_complex_subtraction() { + // Test is based on and verified from practice examples of Khan Academy + // https://www.khanacademy.org/math/precalculus/imaginary-and-complex-numbers + mut c1 := cmplx.complex(-8, 0) + mut c2 := cmplx.complex(6, 30) + mut result := c1 - c2 + assert result.equals(cmplx.complex(-14, -30)) + c1 = cmplx.complex(-19, 7) + c2 = cmplx.complex(29, 32) + result = c1 - c2 + assert result.equals(cmplx.complex(-48, -25)) + c1 = cmplx.complex(12, 0) + c2 = cmplx.complex(23, 13) + result = c1 - c2 + assert result.equals(cmplx.complex(-11, -13)) + c1 = cmplx.complex(-14, 3) + c2 = cmplx.complex(0, 14) + result = c1 - c2 + assert result.equals(cmplx.complex(-14, -11)) +} + +fn test_complex_multiplication() { + // Test is based on and verified from practice examples of Khan Academy + // https://www.khanacademy.org/math/precalculus/imaginary-and-complex-numbers + mut c1 := cmplx.complex(1, 2) + mut c2 := cmplx.complex(1, -4) + mut result := c1 * c2 + assert result.equals(cmplx.complex(9, -2)) + c1 = cmplx.complex(-4, -4) + c2 = cmplx.complex(-5, -3) + result = c1 * c2 + assert result.equals(cmplx.complex(8, 32)) + c1 = cmplx.complex(4, 4) + c2 = cmplx.complex(-2, -5) + result = c1 * c2 + assert result.equals(cmplx.complex(12, -28)) + c1 = cmplx.complex(2, -2) + c2 = cmplx.complex(4, -4) + result = c1 * c2 + assert result.equals(cmplx.complex(0, -16)) +} + +fn test_complex_division() { + // Test is based on and verified from practice examples of Khan Academy + // https://www.khanacademy.org/math/precalculus/imaginary-and-complex-numbers + mut c1 := cmplx.complex(-9, -6) + mut c2 := cmplx.complex(-3, -2) + mut result := c1 / c2 + assert result.equals(cmplx.complex(3, 0)) + c1 = cmplx.complex(-23, 11) + c2 = cmplx.complex(5, 1) + result = c1 / c2 + assert result.equals(cmplx.complex(-4, 3)) + c1 = cmplx.complex(8, -2) + c2 = cmplx.complex(-4, 1) + result = c1 / c2 + assert result.equals(cmplx.complex(-2, 0)) + c1 = cmplx.complex(11, 24) + c2 = cmplx.complex(-4, -1) + result = c1 / c2 + assert result.equals(cmplx.complex(-4, -5)) +} + +fn test_complex_conjugate() { + // Test is based on and verified from practice examples of Khan Academy + // https://www.khanacademy.org/math/precalculus/imaginary-and-complex-numbers + mut c1 := cmplx.complex(0, 8) + mut result := c1.conjugate() + assert result.equals(cmplx.complex(0, -8)) + c1 = cmplx.complex(7, 3) + result = c1.conjugate() + assert result.equals(cmplx.complex(7, -3)) + c1 = cmplx.complex(2, 2) + result = c1.conjugate() + assert result.equals(cmplx.complex(2, -2)) + c1 = cmplx.complex(7, 0) + result = c1.conjugate() + assert result.equals(cmplx.complex(7, 0)) +} + +fn test_complex_equals() { + mut c1 := cmplx.complex(0, 8) + mut c2 := cmplx.complex(0, 8) + assert c1.equals(c2) + c1 = cmplx.complex(-3, 19) + c2 = cmplx.complex(-3, 19) + assert c1.equals(c2) +} + +fn test_complex_abs() { + mut c1 := cmplx.complex(3, 4) + assert c1.abs() == 5 + c1 = cmplx.complex(1, 2) + assert c1.abs() == math.sqrt(5) + assert c1.abs() == c1.conjugate().abs() + c1 = cmplx.complex(7, 0) + assert c1.abs() == 7 +} + +fn test_complex_angle() { + // Test is based on and verified from practice examples of Khan Academy + // https://www.khanacademy.org/math/precalculus/imaginary-and-complex-numbers + mut c := cmplx.complex(1, 0) + assert c.angle() * 180 / math.pi == 0 + c = cmplx.complex(1, 1) + assert c.angle() * 180 / math.pi == 45 + c = cmplx.complex(0, 1) + assert c.angle() * 180 / math.pi == 90 + c = cmplx.complex(-1, 1) + assert c.angle() * 180 / math.pi == 135 + c = cmplx.complex(-1, -1) + assert c.angle() * 180 / math.pi == -135 + cc := c.conjugate() + assert cc.angle() + c.angle() == 0 +} + +fn test_complex_addinv() { + // Tests were also verified on Wolfram Alpha + mut c1 := cmplx.complex(5, 7) + mut c2 := cmplx.complex(-5, -7) + mut result := c1.addinv() + assert result.equals(c2) + c1 = cmplx.complex(-3, 4) + c2 = cmplx.complex(3, -4) + result = c1.addinv() + assert result.equals(c2) + c1 = cmplx.complex(-1, -2) + c2 = cmplx.complex(1, 2) + result = c1.addinv() + assert result.equals(c2) +} + +fn test_complex_mulinv() { + // Tests were also verified on Wolfram Alpha + mut c1 := cmplx.complex(5, 7) + mut c2 := cmplx.complex(0.067568, -0.094595) + mut result := c1.mulinv() + // Some issue with precision comparison in f64 using == operator hence serializing to string + println(c2.str()) + println(result.str()) + assert result.str() == c2.str() + c1 = cmplx.complex(-3, 4) + c2 = cmplx.complex(-0.12, -0.16) + result = c1.mulinv() + assert result.str() == c2.str() + c1 = cmplx.complex(-1, -2) + c2 = cmplx.complex(-0.2, 0.4) + result = c1.mulinv() + assert result.equals(c2) +} + +fn test_complex_mod() { + // Tests were also verified on Wolfram Alpha + mut c1 := cmplx.complex(5, 7) + mut result := c1.mod() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert tst_res(result.str(), '8.602325') + c1 = cmplx.complex(-3, 4) + result = c1.mod() + assert result == 5 + c1 = cmplx.complex(-1, -2) + result = c1.mod() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert tst_res(result.str(), '2.236068') +} + +fn test_complex_pow() { + // Tests were also verified on Wolfram Alpha + mut c1 := cmplx.complex(5, 7) + mut c2 := cmplx.complex(-24.0, 70.0) + mut result := c1.pow(2) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-3, 4) + c2 = cmplx.complex(117, 44) + result = c1.pow(3) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-1, -2) + c2 = cmplx.complex(-7, -24) + result = c1.pow(4) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() +} + +fn test_complex_root() { + // Tests were also verified on Wolfram Alpha + mut c1 := cmplx.complex(5, 7) + mut c2 := cmplx.complex(2.607904, 1.342074) + mut result := c1.root(2) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-3, 4) + c2 = cmplx.complex(1.264953, 1.150614) + result = c1.root(3) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-1, -2) + c2 = cmplx.complex(1.068059, -0.595482) + result = c1.root(4) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() +} + +fn test_complex_exp() { + // Tests were also verified on Wolfram Alpha + mut c1 := cmplx.complex(5, 7) + mut c2 := cmplx.complex(111.889015, 97.505457) + mut result := c1.exp() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-3, 4) + c2 = cmplx.complex(-0.032543, -0.037679) + result = c1.exp() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-1, -2) + c2 = cmplx.complex(-0.153092, -0.334512) + result = c1.exp() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() +} + +fn test_complex_ln() { + // Tests were also verified on Wolfram Alpha + mut c1 := cmplx.complex(5, 7) + mut c2 := cmplx.complex(2.152033, 0.950547) + mut result := c1.ln() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-3, 4) + c2 = cmplx.complex(1.609438, 2.214297) + result = c1.ln() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-1, -2) + c2 = cmplx.complex(0.804719, -2.034444) + result = c1.ln() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() +} + +fn test_complex_arg() { + // Tests were also verified on Wolfram Alpha + mut c1 := cmplx.complex(5, 7) + mut c2 := cmplx.complex(2.152033, 0.950547) + mut result := c1.arg() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert tst_res(result.str(), '0.950547') + c1 = cmplx.complex(-3, 4) + c2 = cmplx.complex(1.609438, 2.214297) + result = c1.arg() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert tst_res(result.str(), '2.214297') + c1 = cmplx.complex(-1, -2) + c2 = cmplx.complex(0.804719, -2.034444) + result = c1.arg() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert tst_res(result.str(), '-2.034444') +} + +fn test_complex_log() { + // Tests were also verified on Wolfram Alpha + mut c1 := cmplx.complex(5, 7) + mut b1 := cmplx.complex(-6, -2) + mut c2 := cmplx.complex(0.232873, -1.413175) + mut result := c1.log(b1) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-3, 4) + b1 = cmplx.complex(3, -1) + c2 = cmplx.complex(0.152198, -0.409312) + result = c1.log(b1) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-1, -2) + b1 = cmplx.complex(0, 9) + c2 = cmplx.complex(-0.298243, 1.197981) + result = c1.log(b1) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() +} + +fn test_complex_cpow() { + // Tests were also verified on Wolfram Alpha + mut c1 := cmplx.complex(5, 7) + mut r1 := cmplx.complex(2, 2) + mut c2 := cmplx.complex(11.022341, -0.861785) + mut result := c1.cpow(r1) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-3, 4) + r1 = cmplx.complex(-4, -2) + c2 = cmplx.complex(0.118303, 0.063148) + result = c1.cpow(r1) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-1, -2) + r1 = cmplx.complex(8, -9) + c2 = cmplx.complex(-0.000000, 0.000007) + result = c1.cpow(r1) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() +} + +fn test_complex_sin() { + // Tests were also verified on Wolfram Alpha + mut c1 := cmplx.complex(5, 7) + mut c2 := cmplx.complex(-525.794515, 155.536550) + mut result := c1.sin() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-3, 4) + c2 = cmplx.complex(-3.853738, -27.016813) + result = c1.sin() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-1, -2) + c2 = cmplx.complex(-3.165779, -1.959601) + result = c1.sin() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() +} + +fn test_complex_cos() { + // Tests were also verified on Wolfram Alpha + mut c1 := cmplx.complex(5, 7) + mut c2 := cmplx.complex(155.536809, 525.793641) + mut result := c1.cos() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-3, 4) + c2 = cmplx.complex(-27.034946, 3.851153) + result = c1.cos() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-1, -2) + c2 = cmplx.complex(2.032723, -3.051898) + result = c1.cos() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() +} + +fn test_complex_tan() { + // Tests were also verified on Wolfram Alpha + mut c1 := cmplx.complex(5, 7) + mut c2 := cmplx.complex(-0.000001, 1.000001) + mut result := c1.tan() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-3, 4) + c2 = cmplx.complex(0.000187, 0.999356) + result = c1.tan() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-1, -2) + c2 = cmplx.complex(-0.033813, -1.014794) + result = c1.tan() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() +} + +fn test_complex_cot() { + // Tests were also verified on Wolfram Alpha + mut c1 := cmplx.complex(5, 7) + mut c2 := cmplx.complex(-0.000001, -0.999999) + mut result := c1.cot() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-3, 4) + c2 = cmplx.complex(0.000188, -1.000644) + result = c1.cot() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-1, -2) + c2 = cmplx.complex(-0.032798, 0.984329) + result = c1.cot() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() +} + +fn test_complex_sec() { + // Tests were also verified on Wolfram Alpha + mut c1 := cmplx.complex(5, 7) + mut c2 := cmplx.complex(0.000517, -0.001749) + mut result := c1.sec() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-3, 4) + c2 = cmplx.complex(-0.036253, -0.005164) + result = c1.sec() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-1, -2) + c2 = cmplx.complex(0.151176, 0.226974) + result = c1.sec() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() +} + +fn test_complex_csc() { + // Tests were also verified on Wolfram Alpha + mut c1 := cmplx.complex(5, 7) + mut c2 := cmplx.complex(-0.001749, -0.000517) + mut result := c1.csc() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-3, 4) + c2 = cmplx.complex(-0.005174, 0.036276) + result = c1.csc() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-1, -2) + c2 = cmplx.complex(-0.228375, 0.141363) + result = c1.csc() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() +} + +fn test_complex_asin() { + // Tests were also verified on Wolfram Alpha + mut c1 := cmplx.complex(5, 7) + mut c2 := cmplx.complex(0.617064, 2.846289) + mut result := c1.asin() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-3, 4) + c2 = cmplx.complex(-0.633984, 2.305509) + result = c1.asin() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-1, -2) + c2 = cmplx.complex(-0.427079, -1.528571) + result = c1.asin() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() +} + +fn test_complex_acos() { + // Tests were also verified on Wolfram Alpha + mut c1 := cmplx.complex(5, 7) + mut c2 := cmplx.complex(0.953732, -2.846289) + mut result := c1.acos() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-3, 4) + c2 = cmplx.complex(2.204780, -2.305509) + result = c1.acos() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-1, -2) + c2 = cmplx.complex(1.997875, 1.528571) + result = c1.acos() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() +} + +fn test_complex_atan() { + // Tests were also verified on Wolfram Alpha + mut c1 := cmplx.complex(5, 7) + mut c2 := cmplx.complex(1.502727, 0.094441) + mut result := c1.atan() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-3, 4) + c2 = cmplx.complex(-1.448307, 0.158997) + result = c1.atan() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-1, -2) + c2 = cmplx.complex(-1.338973, -0.402359) + result = c1.atan() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() +} + +fn test_complex_acot() { + // Tests were also verified on Wolfram Alpha + mut c1 := cmplx.complex(5, 7) + mut c2 := cmplx.complex(0.068069, -0.094441) + mut result := c1.acot() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-3, 4) + c2 = cmplx.complex(-0.122489, -0.158997) + result = c1.acot() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-1, -2) + c2 = cmplx.complex(-0.231824, 0.402359) + result = c1.acot() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() +} + +fn test_complex_asec() { + // Tests were also verified on Wolfram Alpha + mut c1 := cmplx.complex(5, 7) + mut c2 := cmplx.complex(1.503480, 0.094668) + mut result := c1.asec() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-3, 4) + c2 = cmplx.complex(1.689547, 0.160446) + result = c1.asec() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-1, -2) + c2 = cmplx.complex(1.757114, -0.396568) + result = c1.asec() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() +} + +fn test_complex_acsc() { + // Tests were also verified on Wolfram Alpha + mut c1 := cmplx.complex(5, 7) + mut c2 := cmplx.complex(0.067317, -0.094668) + mut result := c1.acsc() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-3, 4) + c2 = cmplx.complex(-0.118751, -0.160446) + result = c1.acsc() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-1, -2) + c2 = cmplx.complex(-0.186318, 0.396568) + result = c1.acsc() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() +} + +fn test_complex_sinh() { + // Tests were also verified on Wolfram Alpha + mut c1 := cmplx.complex(5, 7) + mut c2 := cmplx.complex(55.941968, 48.754942) + mut result := c1.sinh() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-3, 4) + c2 = cmplx.complex(6.548120, -7.619232) + result = c1.sinh() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-1, -2) + c2 = cmplx.complex(0.489056, -1.403119) + result = c1.sinh() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() +} + +fn test_complex_cosh() { + // Tests were also verified on Wolfram Alpha + mut c1 := cmplx.complex(5, 7) + mut c2 := cmplx.complex(55.947047, 48.750515) + mut result := c1.cosh() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-3, 4) + c2 = cmplx.complex(-6.580663, 7.581553) + result = c1.cosh() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-1, -2) + c2 = cmplx.complex(-0.642148, 1.068607) + result = c1.cosh() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() +} + +fn test_complex_tanh() { + // Tests were also verified on Wolfram Alpha + mut c1 := cmplx.complex(5, 7) + mut c2 := cmplx.complex(0.999988, 0.000090) + mut result := c1.tanh() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-3, 4) + c2 = cmplx.complex(-1.000710, 0.004908) + result = c1.tanh() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-1, -2) + c2 = cmplx.complex(-1.166736, 0.243458) + result = c1.tanh() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() +} + +fn test_complex_coth() { + // Tests were also verified on Wolfram Alpha + mut c1 := cmplx.complex(5, 7) + mut c2 := cmplx.complex(1.000012, -0.000090) + mut result := c1.coth() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-3, 4) + c2 = cmplx.complex(-0.999267, -0.004901) + result = c1.coth() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-1, -2) + c2 = cmplx.complex(-0.821330, -0.171384) + result = c1.coth() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() +} + +fn test_complex_sech() { + // Tests were also verified on Wolfram Alpha + mut c1 := cmplx.complex(5, 7) + mut c2 := cmplx.complex(0.010160, -0.008853) + mut result := c1.sech() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-3, 4) + c2 = cmplx.complex(-0.065294, -0.075225) + result = c1.sech() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-1, -2) + c2 = cmplx.complex(-0.413149, -0.687527) + result = c1.sech() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() +} + +fn test_complex_csch() { + // Tests were also verified on Wolfram Alpha + mut c1 := cmplx.complex(5, 7) + mut c2 := cmplx.complex(0.010159, -0.008854) + mut result := c1.csch() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-3, 4) + c2 = cmplx.complex(0.064877, 0.075490) + result = c1.csch() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-1, -2) + c2 = cmplx.complex(0.221501, 0.635494) + result = c1.csch() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() +} + +fn test_complex_asinh() { + // Tests were also verified on Wolfram Alpha + mut c1 := cmplx.complex(5, 7) + mut c2 := cmplx.complex(2.844098, 0.947341) + mut result := c1.asinh() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-3, 4) + c2 = cmplx.complex(-2.299914, 0.917617) + result = c1.asinh() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-1, -2) + c2 = cmplx.complex(-1.469352, -1.063440) + result = c1.asinh() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() +} + +fn test_complex_acosh() { + // Tests were also verified on Wolfram Alpha + mut c1 := cmplx.complex(5, 7) + mut c2 := cmplx.complex(2.846289, 0.953732) + mut result := c1.acosh() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-3, 4) + c2 = cmplx.complex(2.305509, 2.204780) + result = c1.acosh() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-1, -2) + c2 = cmplx.complex(1.528571, -1.997875) + result = c1.acosh() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() +} + +fn test_complex_atanh() { + // Tests were also verified on Wolfram Alpha + mut c1 := cmplx.complex(5, 7) + mut c2 := cmplx.complex(0.067066, 1.476056) + mut result := c1.atanh() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-3, 4) + c2 = cmplx.complex(-0.117501, 1.409921) + result = c1.atanh() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-1, -2) + c2 = cmplx.complex(-0.173287, -1.178097) + result = c1.atanh() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() +} + +fn test_complex_acoth() { + // Tests were also verified on Wolfram Alpha + mut c1 := cmplx.complex(5, 7) + mut c2 := cmplx.complex(0.067066, -0.094740) + mut result := c1.acoth() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-3, 4) + c2 = cmplx.complex(-0.117501, -0.160875) + result = c1.acoth() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-1, -2) + c2 = cmplx.complex(-0.173287, 0.392699) + result = c1.acoth() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() +} + +// fn test_complex_asech() { +// // Tests were also verified on Wolfram Alpha +// mut c1 := cmplx.complex(5,7) +// mut c2 := cmplx.complex(0.094668,-1.503480) +// mut result := c1.asech() +// // Some issue with precision comparison in f64 using == operator hence serializing to string +// assert result.str() == c2.str() +// c1 = cmplx.complex(-3,4) +// c2 = cmplx.complex(0.160446,-1.689547) +// result = c1.asech() +// // Some issue with precision comparison in f64 using == operator hence serializing to string +// assert result.str() c2.str() +// c1 = cmplx.complex(-1,-2) +// c2 = cmplx.complex(0.396568,1.757114) +// result = c1.asech() +// // Some issue with precision comparison in f64 using == operator hence serializing to string +// assert result.str() == c2.str() +// } + +fn test_complex_acsch() { + // Tests were also verified on Wolfram Alpha + mut c1 := cmplx.complex(5, 7) + mut c2 := cmplx.complex(0.067819, -0.094518) + mut result := c1.acsch() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-3, 4) + c2 = cmplx.complex(-0.121246, -0.159507) + result = c1.acsch() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() + c1 = cmplx.complex(-1, -2) + c2 = cmplx.complex(-0.215612, 0.401586) + result = c1.acsch() + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert result.str() == c2.str() +} + +fn test_complex_re_im() { + c := cmplx.complex(2.1, 9.05) + assert c.re == 2.1 + assert c.im == 9.05 +} diff --git a/v_windows/v/old/vlib/math/const.v b/v_windows/v/old/vlib/math/const.v new file mode 100644 index 0000000..5a831a9 --- /dev/null +++ b/v_windows/v/old/vlib/math/const.v @@ -0,0 +1,49 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module math + +pub const ( + e = 2.71828182845904523536028747135266249775724709369995957496696763 + pi = 3.14159265358979323846264338327950288419716939937510582097494459 + phi = 1.61803398874989484820458683436563811772030917980576286213544862 + tau = 6.28318530717958647692528676655900576839433879875021164194988918 + sqrt2 = 1.41421356237309504880168872420969807856967187537694807317667974 + sqrt_e = 1.64872127070012814684865078781416357165377610071014801157507931 + sqrt_pi = 1.77245385090551602729816748334114518279754945612238712821380779 + sqrt_tau = 2.50662827463100050241576528481104525300698674060993831662992357 + sqrt_phi = 1.27201964951406896425242246173749149171560804184009624861664038 + ln2 = 0.693147180559945309417232121458176568075500134360255254120680009 + log2_e = 1.0 / ln2 + ln10 = 2.30258509299404568401799145468436420760110148862877297603332790 + log10_e = 1.0 / ln10 +) + +// Floating-point limit values +// max is the largest finite value representable by the type. +// smallest_non_zero is the smallest positive, non-zero value representable by the type. +pub const ( + max_f32 = 3.40282346638528859811704183484516925440e+38 // 2**127 * (2**24 - 1) / 2**23 + smallest_non_zero_f32 = 1.401298464324817070923729583289916131280e-45 // 1 / 2**(127 - 1 + 23) + max_f64 = 1.797693134862315708145274237317043567981e+308 // 2**1023 * (2**53 - 1) / 2**52 + smallest_non_zero_f64 = 4.940656458412465441765687928682213723651e-324 // 1 / 2**(1023 - 1 + 52) +) + +// Integer limit values +pub const ( + max_i8 = 127 + min_i8 = -128 + max_i16 = 32767 + min_i16 = -32768 + max_i32 = 2147483647 + min_i32 = -2147483648 + // -9223372036854775808 is wrong because C compilers parse literal values + // without sign first, and 9223372036854775808 overflows i64, hence the + // consecutive subtraction by 1 + min_i64 = i64(-9223372036854775807 - 1) + max_i64 = i64(9223372036854775807) + max_u8 = 255 + max_u16 = 65535 + max_u32 = u32(4294967295) + max_u64 = u64(18446744073709551615) +) diff --git a/v_windows/v/old/vlib/math/factorial/factorial.v b/v_windows/v/old/vlib/math/factorial/factorial.v new file mode 100644 index 0000000..9668d5d --- /dev/null +++ b/v_windows/v/old/vlib/math/factorial/factorial.v @@ -0,0 +1,80 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. + +// Module created by Ulises Jeremias Cornejo Fandos based on +// the definitions provided in https://scientificc.github.io/cmathl/ + +module factorial + +import math + +// factorial calculates the factorial of the provided value. +pub fn factorial(n f64) f64 { + // For a large postive argument (n >= FACTORIALS.len) return max_f64 + + if n >= factorials_table.len { + return math.max_f64 + } + + // Otherwise return n!. + if n == f64(i64(n)) && n >= 0.0 { + return factorials_table[i64(n)] + } + + return math.gamma(n + 1.0) +} + +// log_factorial calculates the log-factorial of the provided value. +pub fn log_factorial(n f64) f64 { + // For a large postive argument (n < 0) return max_f64 + + if n < 0 { + return -math.max_f64 + } + + // If n < N then return ln(n!). + + if n != f64(i64(n)) { + return math.log_gamma(n + 1) + } else if n < log_factorials_table.len { + return log_factorials_table[i64(n)] + } + + // Otherwise return asymptotic expansion of ln(n!). + + return log_factorial_asymptotic_expansion(int(n)) +} + +fn log_factorial_asymptotic_expansion(n int) f64 { + m := 6 + mut term := []f64{} + xx := f64((n + 1) * (n + 1)) + mut xj := f64(n + 1) + + log_factorial := log_sqrt_2pi - xj + (xj - 0.5) * math.log(xj) + + mut i := 0 + + for i = 0; i < m; i++ { + term << b_numbers[i] / xj + xj *= xx + } + + mut sum := term[m - 1] + + for i = m - 2; i >= 0; i-- { + if math.abs(sum) <= math.abs(term[i]) { + break + } + + sum = term[i] + } + + for i >= 0 { + sum += term[i] + i-- + } + + return log_factorial + sum +} diff --git a/v_windows/v/old/vlib/math/factorial/factorial_tables.v b/v_windows/v/old/vlib/math/factorial/factorial_tables.v new file mode 100644 index 0000000..a669b09 --- /dev/null +++ b/v_windows/v/old/vlib/math/factorial/factorial_tables.v @@ -0,0 +1,375 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. + +module factorial + +const ( + log_sqrt_2pi = 9.18938533204672741780329736e-1 + + b_numbers = [ + /* + Bernoulli numbers B(2),B(4),B(6),...,B(20). Only B(2),...,B(10) currently + * used. + */ + f64(1.0 / (6.0 * 2.0 * 1.0)), + -1.0 / (30.0 * 4.0 * 3.0), + 1.0 / (42.0 * 6.0 * 5.0), + -1.0 / (30.0 * 8.0 * 7.0), + 5.0 / (66.0 * 10.0 * 9.0), + -691.0 / (2730.0 * 12.0 * 11.0), + 7.0 / (6.0 * 14.0 * 13.0), + -3617.0 / (510.0 * 16.0 * 15.0), + 43867.0 / (796.0 * 18.0 * 17.0), + -174611.0 / (330.0 * 20.0 * 19.0), + ] + + factorials_table = [ + f64(1.000000000000000000000e+0), /* 0! */ + 1.000000000000000000000e+0, /* 1! */ + 2.000000000000000000000e+0, /* 2! */ + 6.000000000000000000000e+0, /* 3! */ + 2.400000000000000000000e+1, /* 4! */ + 1.200000000000000000000e+2, /* 5! */ + 7.200000000000000000000e+2, /* 6! */ + 5.040000000000000000000e+3, /* 7! */ + 4.032000000000000000000e+4, /* 8! */ + 3.628800000000000000000e+5, /* 9! */ + 3.628800000000000000000e+6, /* 10! */ + 3.991680000000000000000e+7, /* 11! */ + 4.790016000000000000000e+8, /* 12! */ + 6.227020800000000000000e+9, /* 13! */ + 8.717829120000000000000e+10, /* 14! */ + 1.307674368000000000000e+12, /* 15! */ + 2.092278988800000000000e+13, /* 16! */ + 3.556874280960000000000e+14, /* 17! */ + 6.402373705728000000000e+15, /* 18! */ + 1.216451004088320000000e+17, /* 19! */ + 2.432902008176640000000e+18, /* 20! */ + 5.109094217170944000000e+19, /* 21! */ + 1.124000727777607680000e+21, /* 22! */ + 2.585201673888497664000e+22, /* 23! */ + 6.204484017332394393600e+23, /* 24! */ + 1.551121004333098598400e+25, /* 25! */ + 4.032914611266056355840e+26, /* 26! */ + 1.088886945041835216077e+28, /* 27! */ + 3.048883446117138605015e+29, /* 28! */ + 8.841761993739701954544e+30, /* 29! */ + 2.652528598121910586363e+32, /* 30! */ + 8.222838654177922817726e+33, /* 31! */ + 2.631308369336935301672e+35, /* 32! */ + 8.683317618811886495518e+36, /* 33! */ + 2.952327990396041408476e+38, /* 34! */ + 1.033314796638614492967e+40, /* 35! */ + 3.719933267899012174680e+41, /* 36! */ + 1.376375309122634504632e+43, /* 37! */ + 5.230226174666011117600e+44, /* 38! */ + 2.039788208119744335864e+46, /* 39! */ + 8.159152832478977343456e+47, /* 40! */ + 3.345252661316380710817e+49, /* 41! */ + 1.405006117752879898543e+51, /* 42! */ + 6.041526306337383563736e+52, /* 43! */ + 2.658271574788448768044e+54, /* 44! */ + 1.196222208654801945620e+56, /* 45! */ + 5.502622159812088949850e+57, /* 46! */ + 2.586232415111681806430e+59, /* 47! */ + 1.241391559253607267086e+61, /* 48! */ + 6.082818640342675608723e+62, /* 49! */ + 3.041409320171337804361e+64, /* 50! */ + 1.551118753287382280224e+66, /* 51! */ + 8.065817517094387857166e+67, /* 52! */ + 4.274883284060025564298e+69, /* 53! */ + 2.308436973392413804721e+71, /* 54! */ + 1.269640335365827592597e+73, /* 55! */ + 7.109985878048634518540e+74, /* 56! */ + 4.052691950487721675568e+76, /* 57! */ + 2.350561331282878571829e+78, /* 58! */ + 1.386831185456898357379e+80, /* 59! */ + 8.320987112741390144276e+81, /* 60! */ + 5.075802138772247988009e+83, /* 61! */ + 3.146997326038793752565e+85, /* 62! */ + 1.982608315404440064116e+87, /* 63! */ + 1.268869321858841641034e+89, /* 64! */ + 8.247650592082470666723e+90, /* 65! */ + 5.443449390774430640037e+92, /* 66! */ + 3.647111091818868528825e+94, /* 67! */ + 2.480035542436830599601e+96, /* 68! */ + 1.711224524281413113725e+98, /* 69! */ + 1.197857166996989179607e+100, /* 70! */ + 8.504785885678623175212e+101, /* 71! */ + 6.123445837688608686152e+103, /* 72! */ + 4.470115461512684340891e+105, /* 73! */ + 3.307885441519386412260e+107, /* 74! */ + 2.480914081139539809195e+109, /* 75! */ + 1.885494701666050254988e+111, /* 76! */ + 1.451830920282858696341e+113, /* 77! */ + 1.132428117820629783146e+115, /* 78! */ + 8.946182130782975286851e+116, /* 79! */ + 7.156945704626380229481e+118, /* 80! */ + 5.797126020747367985880e+120, /* 81! */ + 4.753643337012841748421e+122, /* 82! */ + 3.945523969720658651190e+124, /* 83! */ + 3.314240134565353266999e+126, /* 84! */ + 2.817104114380550276949e+128, /* 85! */ + 2.422709538367273238177e+130, /* 86! */ + 2.107757298379527717214e+132, /* 87! */ + 1.854826422573984391148e+134, /* 88! */ + 1.650795516090846108122e+136, /* 89! */ + 1.485715964481761497310e+138, /* 90! */ + 1.352001527678402962552e+140, /* 91! */ + 1.243841405464130725548e+142, /* 92! */ + 1.156772507081641574759e+144, /* 93! */ + 1.087366156656743080274e+146, /* 94! */ + 1.032997848823905926260e+148, /* 95! */ + 9.916779348709496892096e+149, /* 96! */ + 9.619275968248211985333e+151, /* 97! */ + 9.426890448883247745626e+153, /* 98! */ + 9.332621544394415268170e+155, /* 99! */ + 9.332621544394415268170e+157, /* 100! */ + 9.425947759838359420852e+159, /* 101! */ + 9.614466715035126609269e+161, /* 102! */ + 9.902900716486180407547e+163, /* 103! */ + 1.029901674514562762385e+166, /* 104! */ + 1.081396758240290900504e+168, /* 105! */ + 1.146280563734708354534e+170, /* 106! */ + 1.226520203196137939352e+172, /* 107! */ + 1.324641819451828974500e+174, /* 108! */ + 1.443859583202493582205e+176, /* 109! */ + 1.588245541522742940425e+178, /* 110! */ + 1.762952551090244663872e+180, /* 111! */ + 1.974506857221074023537e+182, /* 112! */ + 2.231192748659813646597e+184, /* 113! */ + 2.543559733472187557120e+186, /* 114! */ + 2.925093693493015690688e+188, /* 115! */ + 3.393108684451898201198e+190, /* 116! */ + 3.969937160808720895402e+192, /* 117! */ + 4.684525849754290656574e+194, /* 118! */ + 5.574585761207605881323e+196, /* 119! */ + 6.689502913449127057588e+198, /* 120! */ + 8.094298525273443739682e+200, /* 121! */ + 9.875044200833601362412e+202, /* 122! */ + 1.214630436702532967577e+205, /* 123! */ + 1.506141741511140879795e+207, /* 124! */ + 1.882677176888926099744e+209, /* 125! */ + 2.372173242880046885677e+211, /* 126! */ + 3.012660018457659544810e+213, /* 127! */ + 3.856204823625804217357e+215, /* 128! */ + 4.974504222477287440390e+217, /* 129! */ + 6.466855489220473672507e+219, /* 130! */ + 8.471580690878820510985e+221, /* 131! */ + 1.118248651196004307450e+224, /* 132! */ + 1.487270706090685728908e+226, /* 133! */ + 1.992942746161518876737e+228, /* 134! */ + 2.690472707318050483595e+230, /* 135! */ + 3.659042881952548657690e+232, /* 136! */ + 5.012888748274991661035e+234, /* 137! */ + 6.917786472619488492228e+236, /* 138! */ + 9.615723196941089004197e+238, /* 139! */ + 1.346201247571752460588e+241, /* 140! */ + 1.898143759076170969429e+243, /* 141! */ + 2.695364137888162776589e+245, /* 142! */ + 3.854370717180072770522e+247, /* 143! */ + 5.550293832739304789551e+249, /* 144! */ + 8.047926057471991944849e+251, /* 145! */ + 1.174997204390910823948e+254, /* 146! */ + 1.727245890454638911203e+256, /* 147! */ + 2.556323917872865588581e+258, /* 148! */ + 3.808922637630569726986e+260, /* 149! */ + 5.713383956445854590479e+262, /* 150! */ + 8.627209774233240431623e+264, /* 151! */ + 1.311335885683452545607e+267, /* 152! */ + 2.006343905095682394778e+269, /* 153! */ + 3.089769613847350887959e+271, /* 154! */ + 4.789142901463393876336e+273, /* 155! */ + 7.471062926282894447084e+275, /* 156! */ + 1.172956879426414428192e+278, /* 157! */ + 1.853271869493734796544e+280, /* 158! */ + 2.946702272495038326504e+282, /* 159! */ + 4.714723635992061322407e+284, /* 160! */ + 7.590705053947218729075e+286, /* 161! */ + 1.229694218739449434110e+289, /* 162! */ + 2.004401576545302577600e+291, /* 163! */ + 3.287218585534296227263e+293, /* 164! */ + 5.423910666131588774984e+295, /* 165! */ + 9.003691705778437366474e+297, /* 166! */ + 1.503616514864999040201e+300, /* 167! */ + 2.526075744973198387538e+302, /* 168! */ + 4.269068009004705274939e+304, /* 169! */ + 7.257415615307998967397e+306, /* 170! */ + ] + + log_factorials_table = [ + f64(0.000000000000000000000e+0), /* 0! */ + 0.000000000000000000000e+0, /* 1! */ + 6.931471805599453094172e-1, /* 2! */ + 1.791759469228055000812e+0, /* 3! */ + 3.178053830347945619647e+0, /* 4! */ + 4.787491742782045994248e+0, /* 5! */ + 6.579251212010100995060e+0, /* 6! */ + 8.525161361065414300166e+0, /* 7! */ + 1.060460290274525022842e+1, /* 8! */ + 1.280182748008146961121e+1, /* 9! */ + 1.510441257307551529523e+1, /* 10! */ + 1.750230784587388583929e+1, /* 11! */ + 1.998721449566188614952e+1, /* 12! */ + 2.255216385312342288557e+1, /* 13! */ + 2.519122118273868150009e+1, /* 14! */ + 2.789927138384089156609e+1, /* 15! */ + 3.067186010608067280376e+1, /* 16! */ + 3.350507345013688888401e+1, /* 17! */ + 3.639544520803305357622e+1, /* 18! */ + 3.933988418719949403622e+1, /* 19! */ + 4.233561646075348502966e+1, /* 20! */ + 4.538013889847690802616e+1, /* 21! */ + 4.847118135183522387964e+1, /* 22! */ + 5.160667556776437357045e+1, /* 23! */ + 5.478472939811231919009e+1, /* 24! */ + 5.800360522298051993929e+1, /* 25! */ + 6.126170176100200198477e+1, /* 26! */ + 6.455753862700633105895e+1, /* 27! */ + 6.788974313718153498289e+1, /* 28! */ + 7.125703896716800901007e+1, /* 29! */ + 7.465823634883016438549e+1, /* 30! */ + 7.809222355331531063142e+1, /* 31! */ + 8.155795945611503717850e+1, /* 32! */ + 8.505446701758151741396e+1, /* 33! */ + 8.858082754219767880363e+1, /* 34! */ + 9.213617560368709248333e+1, /* 35! */ + 9.571969454214320248496e+1, /* 36! */ + 9.933061245478742692933e+1, /* 37! */ + 1.029681986145138126988e+2, /* 38! */ + 1.066317602606434591262e+2, /* 39! */ + 1.103206397147573954291e+2, /* 40! */ + 1.140342117814617032329e+2, /* 41! */ + 1.177718813997450715388e+2, /* 42! */ + 1.215330815154386339623e+2, /* 43! */ + 1.253172711493568951252e+2, /* 44! */ + 1.291239336391272148826e+2, /* 45! */ + 1.329525750356163098828e+2, /* 46! */ + 1.368027226373263684696e+2, /* 47! */ + 1.406739236482342593987e+2, /* 48! */ + 1.445657439463448860089e+2, /* 49! */ + 1.484777669517730320675e+2, /* 50! */ + 1.524095925844973578392e+2, /* 51! */ + 1.563608363030787851941e+2, /* 52! */ + 1.603311282166309070282e+2, /* 53! */ + 1.643201122631951814118e+2, /* 54! */ + 1.683274454484276523305e+2, /* 55! */ + 1.723527971391628015638e+2, /* 56! */ + 1.763958484069973517152e+2, /* 57! */ + 1.804562914175437710518e+2, /* 58! */ + 1.845338288614494905025e+2, /* 59! */ + 1.886281734236715911873e+2, /* 60! */ + 1.927390472878449024360e+2, /* 61! */ + 1.968661816728899939914e+2, /* 62! */ + 2.010093163992815266793e+2, /* 63! */ + 2.051681994826411985358e+2, /* 64! */ + 2.093425867525368356464e+2, /* 65! */ + 2.135322414945632611913e+2, /* 66! */ + 2.177369341139542272510e+2, /* 67! */ + 2.219564418191303339501e+2, /* 68! */ + 2.261905483237275933323e+2, /* 69! */ + 2.304390435657769523214e+2, /* 70! */ + 2.347017234428182677427e+2, /* 71! */ + 2.389783895618343230538e+2, /* 72! */ + 2.432688490029827141829e+2, /* 73! */ + 2.475729140961868839366e+2, /* 74! */ + 2.518904022097231943772e+2, /* 75! */ + 2.562211355500095254561e+2, /* 76! */ + 2.605649409718632093053e+2, /* 77! */ + 2.649216497985528010421e+2, /* 78! */ + 2.692910976510198225363e+2, /* 79! */ + 2.736731242856937041486e+2, /* 80! */ + 2.780675734403661429141e+2, /* 81! */ + 2.824742926876303960274e+2, /* 82! */ + 2.868931332954269939509e+2, /* 83! */ + 2.913239500942703075662e+2, /* 84! */ + 2.957666013507606240211e+2, /* 85! */ + 3.002209486470141317540e+2, /* 86! */ + 3.046868567656687154726e+2, /* 87! */ + 3.091641935801469219449e+2, /* 88! */ + 3.136528299498790617832e+2, /* 89! */ + 3.181526396202093268500e+2, /* 90! */ + 3.226634991267261768912e+2, /* 91! */ + 3.271852877037752172008e+2, /* 92! */ + 3.317178871969284731381e+2, /* 93! */ + 3.362611819791984770344e+2, /* 94! */ + 3.408150588707990178690e+2, /* 95! */ + 3.453794070622668541074e+2, /* 96! */ + 3.499541180407702369296e+2, /* 97! */ + 3.545390855194408088492e+2, /* 98! */ + 3.591342053695753987760e+2, /* 99! */ + 3.637393755555634901441e+2, /* 100! */ + 3.683544960724047495950e+2, /* 101! */ + 3.729794688856890206760e+2, /* 102! */ + 3.776141978739186564468e+2, /* 103! */ + 3.822585887730600291111e+2, /* 104! */ + 3.869125491232175524822e+2, /* 105! */ + 3.915759882173296196258e+2, /* 106! */ + 3.962488170517915257991e+2, /* 107! */ + 4.009309482789157454921e+2, /* 108! */ + 4.056222961611448891925e+2, /* 109! */ + 4.103227765269373054205e+2, /* 110! */ + 4.150323067282496395563e+2, /* 111! */ + 4.197508055995447340991e+2, /* 112! */ + 4.244781934182570746677e+2, /* 113! */ + 4.292143918666515701285e+2, /* 114! */ + 4.339593239950148201939e+2, /* 115! */ + 4.387129141861211848399e+2, /* 116! */ + 4.434750881209189409588e+2, /* 117! */ + 4.482457727453846057188e+2, /* 118! */ + 4.530248962384961351041e+2, /* 119! */ + 4.578123879812781810984e+2, /* 120! */ + 4.626081785268749221865e+2, /* 121! */ + 4.674121995716081787447e+2, /* 122! */ + 4.722243839269805962399e+2, /* 123! */ + 4.770446654925856331047e+2, /* 124! */ + 4.818729792298879342285e+2, /* 125! */ + 4.867092611368394122258e+2, /* 126! */ + 4.915534482232980034989e+2, /* 127! */ + 4.964054784872176206648e+2, /* 128! */ + 5.012652908915792927797e+2, /* 129! */ + 5.061328253420348751997e+2, /* 130! */ + 5.110080226652360267439e+2, /* 131! */ + 5.158908245878223975982e+2, /* 132! */ + 5.207811737160441513633e+2, /* 133! */ + 5.256790135159950627324e+2, /* 134! */ + 5.305842882944334921812e+2, /* 135! */ + 5.354969431801695441897e+2, /* 136! */ + 5.404169241059976691050e+2, /* 137! */ + 5.453441777911548737966e+2, /* 138! */ + 5.502786517242855655538e+2, /* 139! */ + 5.552202941468948698523e+2, /* 140! */ + 5.601690540372730381305e+2, /* 141! */ + 5.651248810948742988613e+2, /* 142! */ + 5.700877257251342061414e+2, /* 143! */ + 5.750575390247102067619e+2, /* 144! */ + 5.800342727671307811636e+2, /* 145! */ + 5.850178793888391176022e+2, /* 146! */ + 5.900083119756178539038e+2, /* 147! */ + 5.950055242493819689670e+2, /* 148! */ + 6.000094705553274281080e+2, /* 149! */ + 6.050201058494236838580e+2, /* 150! */ + 6.100373856862386081868e+2, /* 151! */ + 6.150612662070848845750e+2, /* 152! */ + 6.200917041284773200381e+2, /* 153! */ + 6.251286567308909491967e+2, /* 154! */ + 6.301720818478101958172e+2, /* 155! */ + 6.352219378550597328635e+2, /* 156! */ + 6.402781836604080409209e+2, /* 157! */ + 6.453407786934350077245e+2, /* 158! */ + 6.504096828956552392500e+2, /* 159! */ + 6.554848567108890661717e+2, /* 160! */ + 6.605662610758735291676e+2, /* 161! */ + 6.656538574111059132426e+2, /* 162! */ + 6.707476076119126755767e+2, /* 163! */ + 6.758474740397368739994e+2, /* 164! */ + 6.809534195136374546094e+2, /* 165! */ + 6.860654073019939978423e+2, /* 166! */ + 6.911834011144107529496e+2, /* 167! */ + 6.963073650938140118743e+2, /* 168! */ + 7.014372638087370853465e+2, /* 169! */ + 7.065730622457873471107e+2, /* 170! */ + 7.117147258022900069535e+2, /* 171! */ + ] +) diff --git a/v_windows/v/old/vlib/math/factorial/factorial_test.v b/v_windows/v/old/vlib/math/factorial/factorial_test.v new file mode 100644 index 0000000..6c2b575 --- /dev/null +++ b/v_windows/v/old/vlib/math/factorial/factorial_test.v @@ -0,0 +1,14 @@ +import math +import math.factorial as fact + +fn test_factorial() { + assert fact.factorial(12) == 479001600 + assert fact.factorial(5) == 120 + assert fact.factorial(0) == 1 +} + +fn test_log_factorial() { + assert fact.log_factorial(12) == math.log(479001600) + assert fact.log_factorial(5) == math.log(120) + assert fact.log_factorial(0) == math.log(1) +} diff --git a/v_windows/v/old/vlib/math/fractions/approximations.v b/v_windows/v/old/vlib/math/fractions/approximations.v new file mode 100644 index 0000000..dd4f855 --- /dev/null +++ b/v_windows/v/old/vlib/math/fractions/approximations.v @@ -0,0 +1,119 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module fractions + +import math + +const ( + default_eps = 1.0e-4 + max_iterations = 50 + zero = fraction(0, 1) +) + +// ------------------------------------------------------------------------ +// Unwrapped evaluation methods for fast evaluation of continued fractions. +// ------------------------------------------------------------------------ +// We need these functions because the evaluation of continued fractions +// always has to be done from the end. Also, the numerator-denominator pairs +// are generated from front to end. This means building a result from a +// previous one isn't possible. So we need unrolled versions to ensure that +// we don't take too much of a performance penalty by calling eval_cf +// several times. +// ------------------------------------------------------------------------ +// eval_1 returns the result of evaluating a continued fraction series of length 1 +fn eval_1(whole i64, d []i64) Fraction { + return fraction(whole * d[0] + 1, d[0]) +} + +// eval_2 returns the result of evaluating a continued fraction series of length 2 +fn eval_2(whole i64, d []i64) Fraction { + den := d[0] * d[1] + 1 + return fraction(whole * den + d[1], den) +} + +// eval_3 returns the result of evaluating a continued fraction series of length 3 +fn eval_3(whole i64, d []i64) Fraction { + d1d2_plus_n2 := d[1] * d[2] + 1 + den := d[0] * d1d2_plus_n2 + d[2] + return fraction(whole * den + d1d2_plus_n2, den) +} + +// eval_cf evaluates a continued fraction series and returns a Fraction. +fn eval_cf(whole i64, den []i64) Fraction { + count := den.len + // Offload some small-scale calculations + // to dedicated functions + match count { + 1 { + return eval_1(whole, den) + } + 2 { + return eval_2(whole, den) + } + 3 { + return eval_3(whole, den) + } + else { + last := count - 1 + mut n := i64(1) + mut d := den[last] + // The calculations are done from back to front + for index := count - 2; index >= 0; index-- { + t := d + d = den[index] * d + n + n = t + } + return fraction(d * whole + n, d) + } + } +} + +// approximate returns a Fraction that approcimates the given value to +// within the default epsilon value (1.0e-4). This means the result will +// be accurate to 3 places after the decimal. +pub fn approximate(val f64) Fraction { + return approximate_with_eps(val, fractions.default_eps) +} + +// approximate_with_eps returns a Fraction +pub fn approximate_with_eps(val f64, eps f64) Fraction { + if val == 0.0 { + return fractions.zero + } + if eps < 0.0 { + panic('Epsilon value cannot be negative.') + } + if math.fabs(val) > math.max_i64 { + panic('Value out of range.') + } + // The integer part is separated first. Then we process the fractional + // part to generate numerators and denominators in tandem. + whole := i64(val) + mut frac := val - f64(whole) + // Quick exit for integers + if frac == 0.0 { + return fraction(whole, 1) + } + mut d := []i64{} + mut partial := fractions.zero + // We must complete the approximation within the maximum number of + // itertations allowed. If we can't panic. + // Empirically tested: the hardest constant to approximate is the + // golden ratio (math.phi) and for f64s, it only needs 38 iterations. + for _ in 0 .. fractions.max_iterations { + // We calculate the reciprocal. That's why the numerator is + // always 1. + frac = 1.0 / frac + den := i64(frac) + d << den + // eval_cf is called often so it needs to be performant + partial = eval_cf(whole, d) + // Check if we're done + if math.fabs(val - partial.f64()) < eps { + return partial + } + frac -= f64(den) + } + panic("Couldn't converge. Please create an issue on https://github.com/vlang/v") +} diff --git a/v_windows/v/old/vlib/math/fractions/approximations_test.v b/v_windows/v/old/vlib/math/fractions/approximations_test.v new file mode 100644 index 0000000..5ee92bf --- /dev/null +++ b/v_windows/v/old/vlib/math/fractions/approximations_test.v @@ -0,0 +1,189 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +import math.fractions +import math + +fn test_half() { + float_val := 0.5 + fract_val := fractions.approximate(float_val) + assert fract_val.equals(fractions.fraction(1, 2)) +} + +fn test_third() { + float_val := 1.0 / 3.0 + fract_val := fractions.approximate(float_val) + assert fract_val.equals(fractions.fraction(1, 3)) +} + +fn test_minus_one_twelfth() { + float_val := -1.0 / 12.0 + fract_val := fractions.approximate(float_val) + assert fract_val.equals(fractions.fraction(-1, 12)) +} + +fn test_zero() { + float_val := 0.0 + println('Pre') + fract_val := fractions.approximate(float_val) + println('Post') + assert fract_val.equals(fractions.fraction(0, 1)) +} + +fn test_minus_one() { + float_val := -1.0 + fract_val := fractions.approximate(float_val) + assert fract_val.equals(fractions.fraction(-1, 1)) +} + +fn test_thirty_three() { + float_val := 33.0 + fract_val := fractions.approximate(float_val) + assert fract_val.equals(fractions.fraction(33, 1)) +} + +fn test_millionth() { + float_val := 1.0 / 1000000.0 + fract_val := fractions.approximate(float_val) + assert fract_val.equals(fractions.fraction(1, 1000000)) +} + +fn test_minus_27_by_57() { + float_val := -27.0 / 57.0 + fract_val := fractions.approximate(float_val) + assert fract_val.equals(fractions.fraction(-27, 57)) +} + +fn test_29_by_104() { + float_val := 29.0 / 104.0 + fract_val := fractions.approximate(float_val) + assert fract_val.equals(fractions.fraction(29, 104)) +} + +fn test_140710_232() { + float_val := 140710.232 + fract_val := fractions.approximate(float_val) + // Approximation will match perfectly for upto 3 places after the decimal + // The result will be within default_eps of original value + assert fract_val.f64() == float_val +} + +fn test_pi_1_digit() { + assert fractions.approximate_with_eps(math.pi, 5.0e-2).equals(fractions.fraction(22, + 7)) +} + +fn test_pi_2_digits() { + assert fractions.approximate_with_eps(math.pi, 5.0e-3).equals(fractions.fraction(22, + 7)) +} + +fn test_pi_3_digits() { + assert fractions.approximate_with_eps(math.pi, 5.0e-4).equals(fractions.fraction(333, + 106)) +} + +fn test_pi_4_digits() { + assert fractions.approximate_with_eps(math.pi, 5.0e-5).equals(fractions.fraction(355, + 113)) +} + +fn test_pi_5_digits() { + assert fractions.approximate_with_eps(math.pi, 5.0e-6).equals(fractions.fraction(355, + 113)) +} + +fn test_pi_6_digits() { + assert fractions.approximate_with_eps(math.pi, 5.0e-7).equals(fractions.fraction(355, + 113)) +} + +fn test_pi_7_digits() { + assert fractions.approximate_with_eps(math.pi, 5.0e-8).equals(fractions.fraction(103993, + 33102)) +} + +fn test_pi_8_digits() { + assert fractions.approximate_with_eps(math.pi, 5.0e-9).equals(fractions.fraction(103993, + 33102)) +} + +fn test_pi_9_digits() { + assert fractions.approximate_with_eps(math.pi, 5.0e-10).equals(fractions.fraction(104348, + 33215)) +} + +fn test_pi_10_digits() { + assert fractions.approximate_with_eps(math.pi, 5.0e-11).equals(fractions.fraction(312689, + 99532)) +} + +fn test_pi_11_digits() { + assert fractions.approximate_with_eps(math.pi, 5.0e-12).equals(fractions.fraction(1146408, + 364913)) +} + +fn test_pi_12_digits() { + assert fractions.approximate_with_eps(math.pi, 5.0e-13).equals(fractions.fraction(4272943, + 1360120)) +} + +fn test_phi_1_digit() { + assert fractions.approximate_with_eps(math.phi, 5.0e-2).equals(fractions.fraction(5, + 3)) +} + +fn test_phi_2_digits() { + assert fractions.approximate_with_eps(math.phi, 5.0e-3).equals(fractions.fraction(21, + 13)) +} + +fn test_phi_3_digits() { + assert fractions.approximate_with_eps(math.phi, 5.0e-4).equals(fractions.fraction(55, + 34)) +} + +fn test_phi_4_digits() { + assert fractions.approximate_with_eps(math.phi, 5.0e-5).equals(fractions.fraction(233, + 144)) +} + +fn test_phi_5_digits() { + assert fractions.approximate_with_eps(math.phi, 5.0e-6).equals(fractions.fraction(610, + 377)) +} + +fn test_phi_6_digits() { + assert fractions.approximate_with_eps(math.phi, 5.0e-7).equals(fractions.fraction(1597, + 987)) +} + +fn test_phi_7_digits() { + assert fractions.approximate_with_eps(math.phi, 5.0e-8).equals(fractions.fraction(6765, + 4181)) +} + +fn test_phi_8_digits() { + assert fractions.approximate_with_eps(math.phi, 5.0e-9).equals(fractions.fraction(17711, + 10946)) +} + +fn test_phi_9_digits() { + assert fractions.approximate_with_eps(math.phi, 5.0e-10).equals(fractions.fraction(75025, + 46368)) +} + +fn test_phi_10_digits() { + assert fractions.approximate_with_eps(math.phi, 5.0e-11).equals(fractions.fraction(196418, + 121393)) +} + +fn test_phi_11_digits() { + assert fractions.approximate_with_eps(math.phi, 5.0e-12).equals(fractions.fraction(514229, + 317811)) +} + +fn test_phi_12_digits() { + assert fractions.approximate_with_eps(math.phi, 5.0e-13).equals(fractions.fraction(2178309, + 1346269)) +} diff --git a/v_windows/v/old/vlib/math/fractions/fraction.v b/v_windows/v/old/vlib/math/fractions/fraction.v new file mode 100644 index 0000000..2e0b7bd --- /dev/null +++ b/v_windows/v/old/vlib/math/fractions/fraction.v @@ -0,0 +1,259 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module fractions + +import math +import math.bits + +// Fraction Struct +// --------------- +// A Fraction has a numerator (n) and a denominator (d). If the user uses +// the helper functions in this module, then the following are guaranteed: +// 1. If the user provides n and d with gcd(n, d) > 1, the fraction will +// not be reduced automatically. +// 2. d cannot be set to zero. The factory function will panic. +// 3. If provided d is negative, it will be made positive. n will change as well. +struct Fraction { + n i64 + d i64 +pub: + is_reduced bool +} + +// A factory function for creating a Fraction, adds a boundary condition +// to ensure that the denominator is non-zero. It automatically converts +// the negative denominator to positive and adjusts the numerator. +// NOTE: Fractions created are not reduced by default. +pub fn fraction(n i64, d i64) Fraction { + if d == 0 { + panic('Denominator cannot be zero') + } + // The denominator is always guaranteed to be positive (and non-zero). + if d < 0 { + return fraction(-n, -d) + } + return Fraction{ + n: n + d: d + is_reduced: math.gcd(n, d) == 1 + } +} + +// To String method +pub fn (f Fraction) str() string { + return '$f.n/$f.d' +} + +// +// + ---------------------+ +// | Arithmetic functions.| +// + ---------------------+ +// +// These are implemented from Knuth, TAOCP Vol 2. Section 4.5 +// +// Returns a correctly reduced result for both addition and subtraction +// NOTE: requires reduced inputs +fn general_addition_result(f1 Fraction, f2 Fraction, addition bool) Fraction { + d1 := math.gcd(f1.d, f2.d) + // d1 happens to be 1 around 600/(pi)^2 or 61 percent of the time (Theorem 4.5.2D) + if d1 == 1 { + num1n2d := f1.n * f2.d + num1d2n := f1.d * f2.n + n := if addition { num1n2d + num1d2n } else { num1n2d - num1d2n } + return Fraction{ + n: n + d: f1.d * f2.d + is_reduced: true + } + } + // Here d1 > 1. + f1den := f1.d / d1 + f2den := f2.d / d1 + term1 := f1.n * f2den + term2 := f2.n * f1den + t := if addition { term1 + term2 } else { term1 - term2 } + d2 := math.gcd(t, d1) + return Fraction{ + n: t / d2 + d: f1den * (f2.d / d2) + is_reduced: true + } +} + +// Fraction add using operator overloading +pub fn (f1 Fraction) + (f2 Fraction) Fraction { + return general_addition_result(f1.reduce(), f2.reduce(), true) +} + +// Fraction subtract using operator overloading +pub fn (f1 Fraction) - (f2 Fraction) Fraction { + return general_addition_result(f1.reduce(), f2.reduce(), false) +} + +// Returns a correctly reduced result for both multiplication and division +// NOTE: requires reduced inputs +fn general_multiplication_result(f1 Fraction, f2 Fraction, multiplication bool) Fraction { + // * Theorem: If f1 and f2 are reduced i.e. gcd(f1.n, f1.d) == 1 and gcd(f2.n, f2.d) == 1, + // then gcd(f1.n * f2.n, f1.d * f2.d) == gcd(f1.n, f2.d) * gcd(f1.d, f2.n) + // * Knuth poses this an exercise for 4.5.1. - Exercise 2 + // * Also, note that: + // The terms are flipped for multiplication and division, so the gcds must be calculated carefully + // We do multiple divisions in order to prevent any possible overflows. + // * One more thing: + // if d = gcd(a, b) for example, then d divides both a and b + if multiplication { + d1 := math.gcd(f1.n, f2.d) + d2 := math.gcd(f1.d, f2.n) + return Fraction{ + n: (f1.n / d1) * (f2.n / d2) + d: (f2.d / d1) * (f1.d / d2) + is_reduced: true + } + } else { + d1 := math.gcd(f1.n, f2.n) + d2 := math.gcd(f1.d, f2.d) + return Fraction{ + n: (f1.n / d1) * (f2.d / d2) + d: (f2.n / d1) * (f1.d / d2) + is_reduced: true + } + } +} + +// Fraction multiply using operator overloading +pub fn (f1 Fraction) * (f2 Fraction) Fraction { + return general_multiplication_result(f1.reduce(), f2.reduce(), true) +} + +// Fraction divide using operator overloading +pub fn (f1 Fraction) / (f2 Fraction) Fraction { + if f2.n == 0 { + panic('Cannot divide by zero') + } + // If the second fraction is negative, it will + // mess up the sign. We need positive denominator + if f2.n < 0 { + return f1.negate() / f2.negate() + } + return general_multiplication_result(f1.reduce(), f2.reduce(), false) +} + +// Fraction negate method +pub fn (f Fraction) negate() Fraction { + return Fraction{ + n: -f.n + d: f.d + is_reduced: f.is_reduced + } +} + +// Fraction reciprocal method +pub fn (f Fraction) reciprocal() Fraction { + if f.n == 0 { + panic('Denominator cannot be zero') + } + return Fraction{ + n: f.d + d: f.n + is_reduced: f.is_reduced + } +} + +// Fraction method which reduces the fraction +pub fn (f Fraction) reduce() Fraction { + if f.is_reduced { + return f + } + cf := math.gcd(f.n, f.d) + return Fraction{ + n: f.n / cf + d: f.d / cf + is_reduced: true + } +} + +// f64 converts the Fraction to 64-bit floating point +pub fn (f Fraction) f64() f64 { + return f64(f.n) / f64(f.d) +} + +// +// + ------------------+ +// | Utility functions.| +// + ------------------+ +// +// Returns the absolute value of an i64 +fn abs(num i64) i64 { + if num < 0 { + return -num + } else { + return num + } +} + +// cmp_i64s compares the two arguments, returns 0 when equal, 1 when the first is bigger, -1 otherwise +fn cmp_i64s(a i64, b i64) int { + if a == b { + return 0 + } else if a > b { + return 1 + } else { + return -1 + } +} + +// cmp_f64s compares the two arguments, returns 0 when equal, 1 when the first is bigger, -1 otherwise +fn cmp_f64s(a f64, b f64) int { + // V uses epsilon comparison internally + if a == b { + return 0 + } else if a > b { + return 1 + } else { + return -1 + } +} + +// Two integers are safe to multiply when their bit lengths +// sum up to less than 64 (conservative estimate). +fn safe_to_multiply(a i64, b i64) bool { + return (bits.len_64(u64(abs(a))) + bits.len_64(u64(abs(b)))) < 64 +} + +// cmp compares the two arguments, returns 0 when equal, 1 when the first is bigger, -1 otherwise +fn cmp(f1 Fraction, f2 Fraction) int { + if safe_to_multiply(f1.n, f2.d) && safe_to_multiply(f2.n, f1.d) { + return cmp_i64s(f1.n * f2.d, f2.n * f1.d) + } else { + return cmp_f64s(f1.f64(), f2.f64()) + } +} + +// +-----------------------------+ +// | Public comparison functions | +// +-----------------------------+ +// equals returns true if both the Fractions are equal +pub fn (f1 Fraction) equals(f2 Fraction) bool { + return cmp(f1, f2) == 0 +} + +// ge returns true if f1 >= f2 +pub fn (f1 Fraction) ge(f2 Fraction) bool { + return cmp(f1, f2) >= 0 +} + +// gt returns true if f1 > f2 +pub fn (f1 Fraction) gt(f2 Fraction) bool { + return cmp(f1, f2) > 0 +} + +// le returns true if f1 <= f2 +pub fn (f1 Fraction) le(f2 Fraction) bool { + return cmp(f1, f2) <= 0 +} + +// lt returns true if f1 < f2 +pub fn (f1 Fraction) lt(f2 Fraction) bool { + return cmp(f1, f2) < 0 +} diff --git a/v_windows/v/old/vlib/math/fractions/fraction_test.v b/v_windows/v/old/vlib/math/fractions/fraction_test.v new file mode 100644 index 0000000..4928b7c --- /dev/null +++ b/v_windows/v/old/vlib/math/fractions/fraction_test.v @@ -0,0 +1,269 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +import math.fractions + +// (Old) results are verified using https://www.calculatorsoup.com/calculators/math/fractions.php +// Newer ones are contrived for corner cases or prepared by hand. +fn test_4_by_8_f64_and_str() { + f := fractions.fraction(4, 8) + assert f.f64() == 0.5 + assert f.str() == '4/8' +} + +fn test_10_by_5_f64_and_str() { + f := fractions.fraction(10, 5) + assert f.f64() == 2.0 + assert f.str() == '10/5' +} + +fn test_9_by_3_f64_and_str() { + f := fractions.fraction(9, 3) + assert f.f64() == 3.0 + assert f.str() == '9/3' +} + +fn test_4_by_minus_5_f64_and_str() { + f := fractions.fraction(4, -5) + assert f.f64() == -0.8 + assert f.str() == '-4/5' +} + +fn test_minus_7_by_minus_92_str() { + f := fractions.fraction(-7, -5) + assert f.str() == '7/5' +} + +fn test_4_by_8_plus_5_by_10() { + f1 := fractions.fraction(4, 8) + f2 := fractions.fraction(5, 10) + sum := f1 + f2 + assert sum.f64() == 1.0 + assert sum.str() == '1/1' + assert sum.equals(fractions.fraction(1, 1)) +} + +fn test_5_by_5_plus_8_by_8() { + f1 := fractions.fraction(5, 5) + f2 := fractions.fraction(8, 8) + sum := f1 + f2 + assert sum.f64() == 2.0 + assert sum.str() == '2/1' + assert sum.equals(fractions.fraction(2, 1)) +} + +fn test_9_by_3_plus_1_by_3() { + f1 := fractions.fraction(9, 3) + f2 := fractions.fraction(1, 3) + sum := f1 + f2 + assert sum.str() == '10/3' + assert sum.equals(fractions.fraction(10, 3)) +} + +fn test_3_by_7_plus_1_by_4() { + f1 := fractions.fraction(3, 7) + f2 := fractions.fraction(1, 4) + sum := f1 + f2 + assert sum.str() == '19/28' + assert sum.equals(fractions.fraction(19, 28)) +} + +fn test_36529_by_12409100000_plus_418754901_by_9174901000() { + f1 := fractions.fraction(i64(36529), i64(12409100000)) + f2 := fractions.fraction(i64(418754901), i64(9174901000)) + sum := f1 + f2 + assert sum.str() == '5196706591957729/113852263999100000' +} + +fn test_4_by_8_plus_minus_5_by_10() { + f1 := fractions.fraction(4, 8) + f2 := fractions.fraction(-5, 10) + diff := f2 + f1 + assert diff.f64() == 0 + assert diff.str() == '0/1' +} + +fn test_4_by_8_minus_5_by_10() { + f1 := fractions.fraction(4, 8) + f2 := fractions.fraction(5, 10) + diff := f2 - f1 + assert diff.f64() == 0 + assert diff.str() == '0/1' +} + +fn test_5_by_5_minus_8_by_8() { + f1 := fractions.fraction(5, 5) + f2 := fractions.fraction(8, 8) + diff := f2 - f1 + assert diff.f64() == 0 + assert diff.str() == '0/1' +} + +fn test_9_by_3_minus_1_by_3() { + f1 := fractions.fraction(9, 3) + f2 := fractions.fraction(1, 3) + diff := f1 - f2 + assert diff.str() == '8/3' +} + +fn test_3_by_7_minus_1_by_4() { + f1 := fractions.fraction(3, 7) + f2 := fractions.fraction(1, 4) + diff := f1 - f2 + assert diff.str() == '5/28' +} + +fn test_36529_by_12409100000_minus_418754901_by_9174901000() { + f1 := fractions.fraction(i64(36529), i64(12409100000)) + f2 := fractions.fraction(i64(418754901), i64(9174901000)) + sum := f1 - f2 + assert sum.str() == '-5196036292040471/113852263999100000' +} + +fn test_4_by_8_times_5_by_10() { + f1 := fractions.fraction(4, 8) + f2 := fractions.fraction(5, 10) + product := f1 * f2 + assert product.f64() == 0.25 + assert product.str() == '1/4' +} + +fn test_5_by_5_times_8_by_8() { + f1 := fractions.fraction(5, 5) + f2 := fractions.fraction(8, 8) + product := f1 * f2 + assert product.f64() == 1.0 + assert product.str() == '1/1' +} + +fn test_9_by_3_times_1_by_3() { + f1 := fractions.fraction(9, 3) + f2 := fractions.fraction(1, 3) + product := f1 * f2 + assert product.f64() == 1.0 + assert product.str() == '1/1' +} + +fn test_3_by_7_times_1_by_4() { + f1 := fractions.fraction(3, 7) + f2 := fractions.fraction(1, 4) + product := f2 * f1 + assert product.f64() == (3.0 / 28.0) + assert product.str() == '3/28' +} + +fn test_4_by_8_over_5_by_10() { + f1 := fractions.fraction(4, 8) + f2 := fractions.fraction(5, 10) + q := f1 / f2 + assert q.f64() == 1.0 + assert q.str() == '1/1' +} + +fn test_5_by_5_over_8_by_8() { + f1 := fractions.fraction(5, 5) + f2 := fractions.fraction(8, 8) + q := f1 / f2 + assert q.f64() == 1.0 + assert q.str() == '1/1' +} + +fn test_9_by_3_over_1_by_3() { + f1 := fractions.fraction(9, 3) + f2 := fractions.fraction(1, 3) + q := f1 / f2 + assert q.f64() == 9.0 + assert q.str() == '9/1' +} + +fn test_3_by_7_over_1_by_4() { + f1 := fractions.fraction(3, 7) + f2 := fractions.fraction(1, 4) + q := f1 / f2 + assert q.str() == '12/7' +} + +fn test_reciprocal_4_by_8() { + f := fractions.fraction(4, 8) + assert f.reciprocal().str() == '8/4' +} + +fn test_reciprocal_5_by_10() { + f := fractions.fraction(5, 10) + assert f.reciprocal().str() == '10/5' +} + +fn test_reciprocal_5_by_5() { + f := fractions.fraction(5, 5) + assert f.reciprocal().str() == '5/5' +} + +fn test_reciprocal_8_by_8() { + f := fractions.fraction(8, 8) + assert f.reciprocal().str() == '8/8' +} + +fn test_reciprocal_9_by_3() { + f := fractions.fraction(9, 3) + assert f.reciprocal().str() == '3/9' +} + +fn test_reciprocal_1_by_3() { + f := fractions.fraction(1, 3) + assert f.reciprocal().str() == '3/1' +} + +fn test_reciprocal_7_by_3() { + f := fractions.fraction(7, 3) + assert f.reciprocal().str() == '3/7' +} + +fn test_reciprocal_1_by_4() { + f := fractions.fraction(1, 4) + assert f.reciprocal().str() == '4/1' +} + +fn test_4_by_8_equals_5_by_10() { + f1 := fractions.fraction(4, 8) + f2 := fractions.fraction(5, 10) + assert f1.equals(f2) +} + +fn test_1_by_2_does_not_equal_3_by_4() { + f1 := fractions.fraction(1, 2) + f2 := fractions.fraction(3, 4) + assert !f1.equals(f2) +} + +fn test_reduce_3_by_9() { + f := fractions.fraction(3, 9) + assert f.reduce().equals(fractions.fraction(1, 3)) +} + +fn test_1_by_3_less_than_2_by_4() { + f1 := fractions.fraction(1, 3) + f2 := fractions.fraction(2, 4) + assert f1.lt(f2) + assert f1.le(f2) +} + +fn test_2_by_3_greater_than_2_by_4() { + f1 := fractions.fraction(2, 3) + f2 := fractions.fraction(2, 4) + assert f1.gt(f2) + assert f1.ge(f2) +} + +fn test_5_by_7_not_less_than_2_by_4() { + f1 := fractions.fraction(5, 7) + f2 := fractions.fraction(2, 4) + assert !f1.lt(f2) + assert !f1.le(f2) +} + +fn test_49_by_75_not_greater_than_2_by_3() { + f1 := fractions.fraction(49, 75) + f2 := fractions.fraction(2, 3) + assert !f1.gt(f2) + assert !f1.ge(f2) +} diff --git a/v_windows/v/old/vlib/math/math.c.v b/v_windows/v/old/vlib/math/math.c.v new file mode 100644 index 0000000..689e648 --- /dev/null +++ b/v_windows/v/old/vlib/math/math.c.v @@ -0,0 +1,296 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module math + +#include + +$if windows { + $if tinyc { + #flag @VEXEROOT/thirdparty/tcc/lib/openlibm.o + } +} $else { + #flag -lm +} + +fn C.acos(x f64) f64 + +fn C.asin(x f64) f64 + +fn C.atan(x f64) f64 + +fn C.atan2(y f64, x f64) f64 + +fn C.cbrt(x f64) f64 + +fn C.ceil(x f64) f64 + +fn C.cos(x f64) f64 + +fn C.cosf(x f32) f32 + +fn C.cosh(x f64) f64 + +fn C.erf(x f64) f64 + +fn C.erfc(x f64) f64 + +fn C.exp(x f64) f64 + +fn C.exp2(x f64) f64 + +fn C.fabs(x f64) f64 + +fn C.floor(x f64) f64 + +fn C.fmod(x f64, y f64) f64 + +fn C.hypot(x f64, y f64) f64 + +fn C.log(x f64) f64 + +fn C.log2(x f64) f64 + +fn C.log10(x f64) f64 + +fn C.lgamma(x f64) f64 + +fn C.pow(x f64, y f64) f64 + +fn C.powf(x f32, y f32) f32 + +fn C.round(x f64) f64 + +fn C.sin(x f64) f64 + +fn C.sinf(x f32) f32 + +fn C.sinh(x f64) f64 + +fn C.sqrt(x f64) f64 + +fn C.sqrtf(x f32) f32 + +fn C.tgamma(x f64) f64 + +fn C.tan(x f64) f64 + +fn C.tanf(x f32) f32 + +fn C.tanh(x f64) f64 + +fn C.trunc(x f64) f64 + +// NOTE +// When adding a new function, please make sure it's in the right place. +// All functions are sorted alphabetically. +// Returns the absolute value. +[inline] +pub fn abs(a f64) f64 { + return C.fabs(a) +} + +// acos calculates inverse cosine (arccosine). +[inline] +pub fn acos(a f64) f64 { + return C.acos(a) +} + +// asin calculates inverse sine (arcsine). +[inline] +pub fn asin(a f64) f64 { + return C.asin(a) +} + +// atan calculates inverse tangent (arctangent). +[inline] +pub fn atan(a f64) f64 { + return C.atan(a) +} + +// atan2 calculates inverse tangent with two arguments, returns the angle between the X axis and the point. +[inline] +pub fn atan2(a f64, b f64) f64 { + return C.atan2(a, b) +} + +// cbrt calculates cubic root. +[inline] +pub fn cbrt(a f64) f64 { + return C.cbrt(a) +} + +// ceil returns the nearest f64 greater or equal to the provided value. +[inline] +pub fn ceil(a f64) f64 { + return C.ceil(a) +} + +// cos calculates cosine. +[inline] +pub fn cos(a f64) f64 { + return C.cos(a) +} + +// cosf calculates cosine. (float32) +[inline] +pub fn cosf(a f32) f32 { + return C.cosf(a) +} + +// cosh calculates hyperbolic cosine. +[inline] +pub fn cosh(a f64) f64 { + return C.cosh(a) +} + +// exp calculates exponent of the number (math.pow(math.E, a)). +[inline] +pub fn exp(a f64) f64 { + return C.exp(a) +} + +// erf computes the error function value +[inline] +pub fn erf(a f64) f64 { + return C.erf(a) +} + +// erfc computes the complementary error function value +[inline] +pub fn erfc(a f64) f64 { + return C.erfc(a) +} + +// exp2 returns the base-2 exponential function of a (math.pow(2, a)). +[inline] +pub fn exp2(a f64) f64 { + return C.exp2(a) +} + +// floor returns the nearest f64 lower or equal of the provided value. +[inline] +pub fn floor(a f64) f64 { + return C.floor(a) +} + +// fmod returns the floating-point remainder of number / denom (rounded towards zero): +[inline] +pub fn fmod(a f64, b f64) f64 { + return C.fmod(a, b) +} + +// gamma computes the gamma function value +[inline] +pub fn gamma(a f64) f64 { + return C.tgamma(a) +} + +// Returns hypotenuse of a right triangle. +[inline] +pub fn hypot(a f64, b f64) f64 { + return C.hypot(a, b) +} + +// log calculates natural (base-e) logarithm of the provided value. +[inline] +pub fn log(a f64) f64 { + return C.log(a) +} + +// log2 calculates base-2 logarithm of the provided value. +[inline] +pub fn log2(a f64) f64 { + return C.log2(a) +} + +// log10 calculates the common (base-10) logarithm of the provided value. +[inline] +pub fn log10(a f64) f64 { + return C.log10(a) +} + +// log_gamma computes the log-gamma function value +[inline] +pub fn log_gamma(a f64) f64 { + return C.lgamma(a) +} + +// log_n calculates base-N logarithm of the provided value. +[inline] +pub fn log_n(a f64, b f64) f64 { + return C.log(a) / C.log(b) +} + +// pow returns base raised to the provided power. +[inline] +pub fn pow(a f64, b f64) f64 { + return C.pow(a, b) +} + +// powf returns base raised to the provided power. (float32) +[inline] +pub fn powf(a f32, b f32) f32 { + return C.powf(a, b) +} + +// round returns the integer nearest to the provided value. +[inline] +pub fn round(f f64) f64 { + return C.round(f) +} + +// sin calculates sine. +[inline] +pub fn sin(a f64) f64 { + return C.sin(a) +} + +// sinf calculates sine. (float32) +[inline] +pub fn sinf(a f32) f32 { + return C.sinf(a) +} + +// sinh calculates hyperbolic sine. +[inline] +pub fn sinh(a f64) f64 { + return C.sinh(a) +} + +// sqrt calculates square-root of the provided value. +[inline] +pub fn sqrt(a f64) f64 { + return C.sqrt(a) +} + +// sqrtf calculates square-root of the provided value. (float32) +[inline] +pub fn sqrtf(a f32) f32 { + return C.sqrtf(a) +} + +// tan calculates tangent. +[inline] +pub fn tan(a f64) f64 { + return C.tan(a) +} + +// tanf calculates tangent. (float32) +[inline] +pub fn tanf(a f32) f32 { + return C.tanf(a) +} + +// tanh calculates hyperbolic tangent. +[inline] +pub fn tanh(a f64) f64 { + return C.tanh(a) +} + +// trunc rounds a toward zero, returning the nearest integral value that is not +// larger in magnitude than a. +[inline] +pub fn trunc(a f64) f64 { + return C.trunc(a) +} diff --git a/v_windows/v/old/vlib/math/math.js.v b/v_windows/v/old/vlib/math/math.js.v new file mode 100644 index 0000000..437e8ab --- /dev/null +++ b/v_windows/v/old/vlib/math/math.js.v @@ -0,0 +1,260 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module math + +// TODO : The commented out functions need either a native V implementation, a +// JS specific implementation, or use some other JS math library, such as +// https://github.com/josdejong/mathjs + +// Replaces C.fabs +fn JS.Math.abs(x f64) f64 + +fn JS.Math.acos(x f64) f64 +fn JS.Math.asin(x f64) f64 +fn JS.Math.atan(x f64) f64 +fn JS.Math.atan2(y f64, x f64) f64 +fn JS.Math.cbrt(x f64) f64 +fn JS.Math.ceil(x f64) f64 +fn JS.Math.cos(x f64) f64 +fn JS.Math.cosh(x f64) f64 + +// fn JS.Math.erf(x f64) f64 // Not in standard JS Math object +// fn JS.Math.erfc(x f64) f64 // Not in standard JS Math object +fn JS.Math.exp(x f64) f64 + +// fn JS.Math.exp2(x f64) f64 // Not in standard JS Math object +fn JS.Math.floor(x f64) f64 + +// fn JS.Math.fmod(x f64, y f64) f64 // Not in standard JS Math object +// fn JS.Math.hypot(x f64, y f64) f64 // Not in standard JS Math object +fn JS.Math.log(x f64) f64 + +// fn JS.Math.log2(x f64) f64 // Not in standard JS Math object +// fn JS.Math.log10(x f64) f64 // Not in standard JS Math object +// fn JS.Math.lgamma(x f64) f64 // Not in standard JS Math object +fn JS.Math.pow(x f64, y f64) f64 +fn JS.Math.round(x f64) f64 +fn JS.Math.sin(x f64) f64 +fn JS.Math.sinh(x f64) f64 +fn JS.Math.sqrt(x f64) f64 + +// fn JS.Math.tgamma(x f64) f64 // Not in standard JS Math object +fn JS.Math.tan(x f64) f64 +fn JS.Math.tanh(x f64) f64 +fn JS.Math.trunc(x f64) f64 + +// NOTE +// When adding a new function, please make sure it's in the right place. +// All functions are sorted alphabetically. + +// Returns the absolute value. +[inline] +pub fn abs(a f64) f64 { + return JS.Math.abs(a) +} + +// acos calculates inverse cosine (arccosine). +[inline] +pub fn acos(a f64) f64 { + return JS.Math.acos(a) +} + +// asin calculates inverse sine (arcsine). +[inline] +pub fn asin(a f64) f64 { + return JS.Math.asin(a) +} + +// atan calculates inverse tangent (arctangent). +[inline] +pub fn atan(a f64) f64 { + return JS.Math.atan(a) +} + +// atan2 calculates inverse tangent with two arguments, returns the angle between the X axis and the point. +[inline] +pub fn atan2(a f64, b f64) f64 { + return JS.Math.atan2(a, b) +} + +// cbrt calculates cubic root. +[inline] +pub fn cbrt(a f64) f64 { + return JS.Math.cbrt(a) +} + +// ceil returns the nearest f64 greater or equal to the provided value. +[inline] +pub fn ceil(a f64) f64 { + return JS.Math.ceil(a) +} + +// cos calculates cosine. +[inline] +pub fn cos(a f64) f64 { + return JS.Math.cos(a) +} + +// cosf calculates cosine. (float32). This doesn't exist in JS +[inline] +pub fn cosf(a f32) f32 { + return f32(JS.Math.cos(a)) +} + +// cosh calculates hyperbolic cosine. +[inline] +pub fn cosh(a f64) f64 { + return JS.Math.cosh(a) +} + +// exp calculates exponent of the number (math.pow(math.E, a)). +[inline] +pub fn exp(a f64) f64 { + return JS.Math.exp(a) +} + +// erf computes the error function value +[inline] +pub fn erf(a f64) f64 { + return JS.Math.erf(a) +} + +// erfc computes the complementary error function value +[inline] +pub fn erfc(a f64) f64 { + return JS.Math.erfc(a) +} + +// exp2 returns the base-2 exponential function of a (math.pow(2, a)). +[inline] +pub fn exp2(a f64) f64 { + return JS.Math.exp2(a) +} + +// floor returns the nearest f64 lower or equal of the provided value. +[inline] +pub fn floor(a f64) f64 { + return JS.Math.floor(a) +} + +// fmod returns the floating-point remainder of number / denom (rounded towards zero): +[inline] +pub fn fmod(a f64, b f64) f64 { + return JS.Math.fmod(a, b) +} + +// gamma computes the gamma function value +[inline] +pub fn gamma(a f64) f64 { + return JS.Math.tgamma(a) +} + +// Returns hypotenuse of a right triangle. +[inline] +pub fn hypot(a f64, b f64) f64 { + return JS.Math.hypot(a, b) +} + +// log calculates natural (base-e) logarithm of the provided value. +[inline] +pub fn log(a f64) f64 { + return JS.Math.log(a) +} + +// log2 calculates base-2 logarithm of the provided value. +[inline] +pub fn log2(a f64) f64 { + return JS.Math.log2(a) +} + +// log10 calculates the common (base-10) logarithm of the provided value. +[inline] +pub fn log10(a f64) f64 { + return JS.Math.log10(a) +} + +// log_gamma computes the log-gamma function value +[inline] +pub fn log_gamma(a f64) f64 { + return JS.Math.lgamma(a) +} + +// log_n calculates base-N logarithm of the provided value. +[inline] +pub fn log_n(a f64, b f64) f64 { + return JS.Math.log(a) / JS.Math.log(b) +} + +// pow returns base raised to the provided power. +[inline] +pub fn pow(a f64, b f64) f64 { + return JS.Math.pow(a, b) +} + +// powf returns base raised to the provided power. (float32) +[inline] +pub fn powf(a f32, b f32) f32 { + return f32(JS.Math.pow(a, b)) +} + +// round returns the integer nearest to the provided value. +[inline] +pub fn round(f f64) f64 { + return JS.Math.round(f) +} + +// sin calculates sine. +[inline] +pub fn sin(a f64) f64 { + return JS.Math.sin(a) +} + +// sinf calculates sine. (float32) +[inline] +pub fn sinf(a f32) f32 { + return f32(JS.Math.sin(a)) +} + +// sinh calculates hyperbolic sine. +[inline] +pub fn sinh(a f64) f64 { + return JS.Math.sinh(a) +} + +// sqrt calculates square-root of the provided value. +[inline] +pub fn sqrt(a f64) f64 { + return JS.Math.sqrt(a) +} + +// sqrtf calculates square-root of the provided value. (float32) +[inline] +pub fn sqrtf(a f32) f32 { + return f32(JS.Math.sqrt(a)) +} + +// tan calculates tangent. +[inline] +pub fn tan(a f64) f64 { + return JS.Math.tan(a) +} + +// tanf calculates tangent. (float32) +[inline] +pub fn tanf(a f32) f32 { + return f32(JS.Math.tan(a)) +} + +// tanh calculates hyperbolic tangent. +[inline] +pub fn tanh(a f64) f64 { + return JS.Math.tanh(a) +} + +// trunc rounds a toward zero, returning the nearest integral value that is not +// larger in magnitude than a. +[inline] +pub fn trunc(a f64) f64 { + return JS.Math.trunc(a) +} diff --git a/v_windows/v/old/vlib/math/math.v b/v_windows/v/old/vlib/math/math.v new file mode 100644 index 0000000..f102f39 --- /dev/null +++ b/v_windows/v/old/vlib/math/math.v @@ -0,0 +1,148 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module math + +// aprox_sin returns an approximation of sin(a) made using lolremez +pub fn aprox_sin(a f64) f64 { + a0 := 1.91059300966915117e-31 + a1 := 1.00086760103908896 + a2 := -1.21276126894734565e-2 + a3 := -1.38078780785773762e-1 + a4 := -2.67353392911981221e-2 + a5 := 2.08026600266304389e-2 + a6 := -3.03996055049204407e-3 + a7 := 1.38235642404333740e-4 + return a0 + a * (a1 + a * (a2 + a * (a3 + a * (a4 + a * (a5 + a * (a6 + a * a7)))))) +} + +// aprox_cos returns an approximation of sin(a) made using lolremez +pub fn aprox_cos(a f64) f64 { + a0 := 9.9995999154986614e-1 + a1 := 1.2548995793001028e-3 + a2 := -5.0648546280678015e-1 + a3 := 1.2942246466519995e-2 + a4 := 2.8668384702547972e-2 + a5 := 7.3726485210586547e-3 + a6 := -3.8510875386947414e-3 + a7 := 4.7196604604366623e-4 + a8 := -1.8776444013090451e-5 + return a0 + a * (a1 + a * (a2 + a * (a3 + a * (a4 + a * (a5 + a * (a6 + a * (a7 + a * a8))))))) +} + +// copysign returns a value with the magnitude of x and the sign of y +[inline] +pub fn copysign(x f64, y f64) f64 { + return f64_from_bits((f64_bits(x) & ~sign_mask) | (f64_bits(y) & sign_mask)) +} + +// degrees convert from degrees to radians. +[inline] +pub fn degrees(radians f64) f64 { + return radians * (180.0 / pi) +} + +// digits returns an array of the digits of n in the given base. +pub fn digits(_n int, base int) []int { + if base < 2 { + panic('digits: Cannot find digits of n with base $base') + } + mut n := _n + mut sign := 1 + if n < 0 { + sign = -1 + n = -n + } + mut res := []int{} + for n != 0 { + res << (n % base) * sign + n /= base + } + return res +} + +[inline] +pub fn fabs(x f64) f64 { + if x < 0.0 { + return -x + } + return x +} + +// gcd calculates greatest common (positive) divisor (or zero if a and b are both zero). +pub fn gcd(a_ i64, b_ i64) i64 { + mut a := a_ + mut b := b_ + if a < 0 { + a = -a + } + if b < 0 { + b = -b + } + for b != 0 { + a %= b + if a == 0 { + return b + } + b %= a + } + return a +} + +// lcm calculates least common (non-negative) multiple. +pub fn lcm(a i64, b i64) i64 { + if a == 0 { + return a + } + res := a * (b / gcd(b, a)) + if res < 0 { + return -res + } + return res +} + +// max returns the maximum value of the two provided. +[inline] +pub fn max(a f64, b f64) f64 { + if a > b { + return a + } + return b +} + +// min returns the minimum value of the two provided. +[inline] +pub fn min(a f64, b f64) f64 { + if a < b { + return a + } + return b +} + +// sign returns the corresponding sign -1.0, 1.0 of the provided number. +// if n is not a number, its sign is nan too. +[inline] +pub fn sign(n f64) f64 { + if is_nan(n) { + return nan() + } + return copysign(1.0, n) +} + +// signi returns the corresponding sign -1.0, 1.0 of the provided number. +[inline] +pub fn signi(n f64) int { + return int(copysign(1.0, n)) +} + +// radians convert from radians to degrees. +[inline] +pub fn radians(degrees f64) f64 { + return degrees * (pi / 180.0) +} + +// signbit returns a value with the boolean representation of the sign for x +[inline] +pub fn signbit(x f64) bool { + return f64_bits(x) & sign_mask != 0 +} diff --git a/v_windows/v/old/vlib/math/math_test.v b/v_windows/v/old/vlib/math/math_test.v new file mode 100644 index 0000000..c0cd7d8 --- /dev/null +++ b/v_windows/v/old/vlib/math/math_test.v @@ -0,0 +1,806 @@ +module math + +struct Fi { + f f64 + i int +} + +const ( + vf_ = [f64(4.9790119248836735e+00), 7.7388724745781045e+00, -2.7688005719200159e-01, + -5.0106036182710749e+00, 9.6362937071984173e+00, 2.9263772392439646e+00, + 5.2290834314593066e+00, 2.7279399104360102e+00, 1.8253080916808550e+00, + -8.6859247685756013e+00, + ] + // The expected results below were computed by the high precision calculators + // at https://keisan.casio.com/. More exact input values (array vf_[], above) + // were obtained by printing them with "%.26f". The answers were calculated + // to 26 digits (by using the "Digit number" drop-down control of each + // calculator). + acos_ = [f64(1.0496193546107222142571536e+00), 6.8584012813664425171660692e-01, + 1.5984878714577160325521819e+00, 2.0956199361475859327461799e+00, + 2.7053008467824138592616927e-01, 1.2738121680361776018155625e+00, + 1.0205369421140629186287407e+00, 1.2945003481781246062157835e+00, + 1.3872364345374451433846657e+00, 2.6231510803970463967294145e+00] + asin_ = [f64(5.2117697218417440497416805e-01), 8.8495619865825236751471477e-01, + -2.769154466281941332086016e-02, -5.2482360935268931351485822e-01, + 1.3002662421166552333051524e+00, 2.9698415875871901741575922e-01, + 5.5025938468083370060258102e-01, 2.7629597861677201301553823e-01, + 1.83559892257451475846656e-01, -1.0523547536021497774980928e+00] + atan_ = [f64(1.372590262129621651920085e+00), 1.442290609645298083020664e+00, + -2.7011324359471758245192595e-01, -1.3738077684543379452781531e+00, + 1.4673921193587666049154681e+00, 1.2415173565870168649117764e+00, + 1.3818396865615168979966498e+00, 1.2194305844639670701091426e+00, + 1.0696031952318783760193244e+00, -1.4561721938838084990898679e+00] + atan2_ = [f64(1.1088291730037004444527075e+00), 9.1218183188715804018797795e-01, + 1.5984772603216203736068915e+00, 2.0352918654092086637227327e+00, + 8.0391819139044720267356014e-01, 1.2861075249894661588866752e+00, + 1.0889904479131695712182587e+00, 1.3044821793397925293797357e+00, + 1.3902530903455392306872261e+00, 2.2859857424479142655411058e+00] + ceil_ = [f64(5.0000000000000000e+00), 8.0000000000000000e+00, copysign(0, -1), + -5.0000000000000000e+00, 1.0000000000000000e+01, 3.0000000000000000e+00, + 6.0000000000000000e+00, 3.0000000000000000e+00, 2.0000000000000000e+00, + -8.0000000000000000e+00, + ] + cos_ = [f64(2.634752140995199110787593e-01), 1.148551260848219865642039e-01, + 9.6191297325640768154550453e-01, 2.938141150061714816890637e-01, + -9.777138189897924126294461e-01, -9.7693041344303219127199518e-01, + 4.940088096948647263961162e-01, -9.1565869021018925545016502e-01, + -2.517729313893103197176091e-01, -7.39241351595676573201918e-01] + // Results for 100000 * pi + vf_[i] + cos_large_ = [f64(2.634752141185559426744e-01), 1.14855126055543100712e-01, + 9.61912973266488928113e-01, 2.9381411499556122552e-01, -9.777138189880161924641e-01, + -9.76930413445147608049e-01, 4.940088097314976789841e-01, -9.15658690217517835002e-01, + -2.51772931436786954751e-01, -7.3924135157173099849e-01] + cosh_ = [f64(7.2668796942212842775517446e+01), 1.1479413465659254502011135e+03, + 1.0385767908766418550935495e+00, 7.5000957789658051428857788e+01, + 7.655246669605357888468613e+03, 9.3567491758321272072888257e+00, + 9.331351599270605471131735e+01, 7.6833430994624643209296404e+00, + 3.1829371625150718153881164e+00, 2.9595059261916188501640911e+03] + exp_ = [f64(1.4533071302642137507696589e+02), 2.2958822575694449002537581e+03, + 7.5814542574851666582042306e-01, 6.6668778421791005061482264e-03, + 1.5310493273896033740861206e+04, 1.8659907517999328638667732e+01, + 1.8662167355098714543942057e+02, 1.5301332413189378961665788e+01, + 6.2047063430646876349125085e+00, 1.6894712385826521111610438e-04] + exp2_ = [f64(3.1537839463286288034313104e+01), 2.1361549283756232296144849e+02, + 8.2537402562185562902577219e-01, 3.1021158628740294833424229e-02, + 7.9581744110252191462569661e+02, 7.6019905892596359262696423e+00, + 3.7506882048388096973183084e+01, 6.6250893439173561733216375e+00, + 3.5438267900243941544605339e+00, 2.4281533133513300984289196e-03] + fabs_ = [f64(4.9790119248836735e+00), 7.7388724745781045e+00, 2.7688005719200159e-01, + 5.0106036182710749e+00, 9.6362937071984173e+00, 2.9263772392439646e+00, + 5.2290834314593066e+00, 2.7279399104360102e+00, 1.8253080916808550e+00, + 8.6859247685756013e+00, + ] + floor_ = [f64(4.0000000000000000e+00), 7.0000000000000000e+00, -1.0000000000000000e+00, + -6.0000000000000000e+00, 9.0000000000000000e+00, 2.0000000000000000e+00, + 5.0000000000000000e+00, 2.0000000000000000e+00, 1.0000000000000000e+00, + -9.0000000000000000e+00, + ] + fmod_ = [f64(4.197615023265299782906368e-02), 2.261127525421895434476482e+00, + 3.231794108794261433104108e-02, 4.989396381728925078391512e+00, + 3.637062928015826201999516e-01, 1.220868282268106064236690e+00, + 4.770916568540693347699744e+00, 1.816180268691969246219742e+00, + 8.734595415957246977711748e-01, 1.314075231424398637614104e+00] + gamma_ = [f64(2.3254348370739963835386613898e+01), 2.991153837155317076427529816e+03, + -4.561154336726758060575129109e+00, 7.719403468842639065959210984e-01, + 1.6111876618855418534325755566e+05, 1.8706575145216421164173224946e+00, + 3.4082787447257502836734201635e+01, 1.579733951448952054898583387e+00, + 9.3834586598354592860187267089e-01, -2.093995902923148389186189429e-05] + log_ = [f64(1.605231462693062999102599e+00), 2.0462560018708770653153909e+00, + -1.2841708730962657801275038e+00, 1.6115563905281545116286206e+00, + 2.2655365644872016636317461e+00, 1.0737652208918379856272735e+00, + 1.6542360106073546632707956e+00, 1.0035467127723465801264487e+00, + 6.0174879014578057187016475e-01, 2.161703872847352815363655e+00] + logb_ = [f64(2.0000000000000000e+00), 2.0000000000000000e+00, -2.0000000000000000e+00, + 2.0000000000000000e+00, 3.0000000000000000e+00, 1.0000000000000000e+00, + 2.0000000000000000e+00, 1.0000000000000000e+00, 0.0000000000000000e+00, + 3.0000000000000000e+00, + ] + log10_ = [f64(6.9714316642508290997617083e-01), 8.886776901739320576279124e-01, + -5.5770832400658929815908236e-01, 6.998900476822994346229723e-01, + 9.8391002850684232013281033e-01, 4.6633031029295153334285302e-01, + 7.1842557117242328821552533e-01, 4.3583479968917773161304553e-01, + 2.6133617905227038228626834e-01, 9.3881606348649405716214241e-01] + log1p_ = [f64(4.8590257759797794104158205e-02), 7.4540265965225865330849141e-02, + -2.7726407903942672823234024e-03, -5.1404917651627649094953380e-02, + 9.1998280672258624681335010e-02, 2.8843762576593352865894824e-02, + 5.0969534581863707268992645e-02, 2.6913947602193238458458594e-02, + 1.8088493239630770262045333e-02, -9.0865245631588989681559268e-02] + log2_ = [f64(2.3158594707062190618898251e+00), 2.9521233862883917703341018e+00, + -1.8526669502700329984917062e+00, 2.3249844127278861543568029e+00, + 3.268478366538305087466309e+00, 1.5491157592596970278166492e+00, + 2.3865580889631732407886495e+00, 1.447811865817085365540347e+00, + 8.6813999540425116282815557e-01, 3.118679457227342224364709e+00] + modf_ = [[f64(4.0000000000000000e+00), 9.7901192488367350108546816e-01], + [f64(7.0000000000000000e+00), 7.3887247457810456552351752e-01], + [f64(-0.0), -2.7688005719200159404635997e-01], + [f64(-5.0000000000000000e+00), + -1.060361827107492160848778e-02, + ], + [f64(9.0000000000000000e+00), 6.3629370719841737980004837e-01], + [f64(2.0000000000000000e+00), 9.2637723924396464525443662e-01], + [f64(5.0000000000000000e+00), 2.2908343145930665230025625e-01], + [f64(2.0000000000000000e+00), 7.2793991043601025126008608e-01], + [f64(1.0000000000000000e+00), 8.2530809168085506044576505e-01], + [f64(-8.0000000000000000e+00), -6.8592476857560136238589621e-01], + ] + nextafter32_ = [4.979012489318848e+00, 7.738873004913330e+00, -2.768800258636475e-01, + -5.010602951049805e+00, 9.636294364929199e+00, 2.926377534866333e+00, 5.229084014892578e+00, + 2.727940082550049e+00, 1.825308203697205e+00, -8.685923576354980e+00] + nextafter64_ = [f64(4.97901192488367438926388786e+00), 7.73887247457810545370193722e+00, + -2.7688005719200153853520874e-01, -5.01060361827107403343006808e+00, + 9.63629370719841915615688777e+00, 2.92637723924396508934364647e+00, + 5.22908343145930754047867595e+00, 2.72793991043601069534929593e+00, + 1.82530809168085528249036997e+00, -8.68592476857559958602905681e+00] + pow_ = [f64(9.5282232631648411840742957e+04), 5.4811599352999901232411871e+07, + 5.2859121715894396531132279e-01, 9.7587991957286474464259698e-06, + 4.328064329346044846740467e+09, 8.4406761805034547437659092e+02, + 1.6946633276191194947742146e+05, 5.3449040147551939075312879e+02, + 6.688182138451414936380374e+01, 2.0609869004248742886827439e-09] + remainder_ = [f64(4.197615023265299782906368e-02), 2.261127525421895434476482e+00, + 3.231794108794261433104108e-02, -2.120723654214984321697556e-02, + 3.637062928015826201999516e-01, 1.220868282268106064236690e+00, + -4.581668629186133046005125e-01, -9.117596417440410050403443e-01, + 8.734595415957246977711748e-01, 1.314075231424398637614104e+00] + round_ = [f64(5), 8, -0.0, -5, 10, 3, 5, 3, 2, -9] + signbit_ = [false, false, true, true, false, false, false, false, false, true] + sin_ = [f64(-9.6466616586009283766724726e-01), 9.9338225271646545763467022e-01, + -2.7335587039794393342449301e-01, 9.5586257685042792878173752e-01, + -2.099421066779969164496634e-01, 2.135578780799860532750616e-01, + -8.694568971167362743327708e-01, 4.019566681155577786649878e-01, + 9.6778633541687993721617774e-01, -6.734405869050344734943028e-01] + // Results for 100000 * pi + vf_[i] + sin_large_ = [f64(-9.646661658548936063912e-01), 9.933822527198506903752e-01, + -2.7335587036246899796e-01, 9.55862576853689321268e-01, -2.099421066862688873691e-01, + 2.13557878070308981163e-01, -8.694568970959221300497e-01, 4.01956668098863248917e-01, + 9.67786335404528727927e-01, -6.7344058693131973066e-01] + sinh_ = [f64(7.2661916084208532301448439e+01), 1.1479409110035194500526446e+03, + -2.8043136512812518927312641e-01, -7.499429091181587232835164e+01, + 7.6552466042906758523925934e+03, 9.3031583421672014313789064e+00, + 9.330815755828109072810322e+01, 7.6179893137269146407361477e+00, + 3.021769180549615819524392e+00, -2.95950575724449499189888e+03] + sqrt_ = [f64(2.2313699659365484748756904e+00), 2.7818829009464263511285458e+00, + 5.2619393496314796848143251e-01, 2.2384377628763938724244104e+00, + 3.1042380236055381099288487e+00, 1.7106657298385224403917771e+00, + 2.286718922705479046148059e+00, 1.6516476350711159636222979e+00, + 1.3510396336454586262419247e+00, 2.9471892997524949215723329e+00] + tan_ = [f64(-3.661316565040227801781974e+00), 8.64900232648597589369854e+00, + -2.8417941955033612725238097e-01, 3.253290185974728640827156e+00, + 2.147275640380293804770778e-01, -2.18600910711067004921551e-01, + -1.760002817872367935518928e+00, -4.389808914752818126249079e-01, + -3.843885560201130679995041e+00, 9.10988793377685105753416e-01] + // Results for 100000 * pi + vf_[i] + tan_large_ = [f64(-3.66131656475596512705e+00), 8.6490023287202547927e+00, + -2.841794195104782406e-01, 3.2532901861033120983e+00, 2.14727564046880001365e-01, + -2.18600910700688062874e-01, -1.760002817699722747043e+00, -4.38980891453536115952e-01, + -3.84388555942723509071e+00, 9.1098879344275101051e-01] + tanh_ = [f64(9.9990531206936338549262119e-01), 9.9999962057085294197613294e-01, + -2.7001505097318677233756845e-01, -9.9991110943061718603541401e-01, + 9.9999999146798465745022007e-01, 9.9427249436125236705001048e-01, + 9.9994257600983138572705076e-01, 9.9149409509772875982054701e-01, + 9.4936501296239685514466577e-01, -9.9999994291374030946055701e-01] + trunc_ = [f64(4.0000000000000000e+00), 7.0000000000000000e+00, copysign(0, -1), + -5.0000000000000000e+00, 9.0000000000000000e+00, 2.0000000000000000e+00, + 5.0000000000000000e+00, 2.0000000000000000e+00, 1.0000000000000000e+00, + -8.0000000000000000e+00, + ] +) + +fn tolerance(a f64, b f64, tol f64) bool { + mut ee := tol + // Multiplying by ee here can underflow denormal values to zero. + // Check a==b so that at least if a and b are small and identical + // we say they match. + if a == b { + return true + } + mut d := a - b + if d < 0 { + d = -d + } + // note: b is correct (expected) value, a is actual value. + // make error tolerance a fraction of b, not a. + if b != 0 { + ee = ee * b + if ee < 0 { + ee = -ee + } + } + return d < ee +} + +fn close(a f64, b f64) bool { + return tolerance(a, b, 1e-14) +} + +fn veryclose(a f64, b f64) bool { + return tolerance(a, b, 4e-16) +} + +fn soclose(a f64, b f64, e f64) bool { + return tolerance(a, b, e) +} + +fn alike(a f64, b f64) bool { + if is_nan(a) && is_nan(b) { + return true + } else if a == b { + return signbit(a) == signbit(b) + } + return false +} + +fn test_nan() { + nan_f64 := nan() + assert nan_f64 != nan_f64 + nan_f32 := f32(nan_f64) + assert nan_f32 != nan_f32 +} + +fn test_acos() { + for i := 0; i < math.vf_.len; i++ { + a := math.vf_[i] / 10 + f := acos(a) + assert soclose(math.acos_[i], f, 1e-7) + } + vfacos_sc_ := [-pi, 1, pi, nan()] + acos_sc_ := [nan(), 0, nan(), nan()] + for i := 0; i < vfacos_sc_.len; i++ { + f := acos(vfacos_sc_[i]) + assert alike(acos_sc_[i], f) + } +} + +fn test_asin() { + for i := 0; i < math.vf_.len; i++ { + a := math.vf_[i] / 10 + f := asin(a) + assert veryclose(math.asin_[i], f) + } + vfasin_sc_ := [-pi, copysign(0, -1), 0, pi, nan()] + asin_sc_ := [nan(), copysign(0, -1), 0, nan(), nan()] + for i := 0; i < vfasin_sc_.len; i++ { + f := asin(vfasin_sc_[i]) + assert alike(asin_sc_[i], f) + } +} + +fn test_atan() { + for i := 0; i < math.vf_.len; i++ { + f := atan(math.vf_[i]) + assert veryclose(math.atan_[i], f) + } + vfatan_sc_ := [inf(-1), copysign(0, -1), 0, inf(1), nan()] + atan_sc_ := [f64(-pi / 2), copysign(0, -1), 0, pi / 2, nan()] + for i := 0; i < vfatan_sc_.len; i++ { + f := atan(vfatan_sc_[i]) + assert alike(atan_sc_[i], f) + } +} + +fn test_atan2() { + for i := 0; i < math.vf_.len; i++ { + f := atan2(10, math.vf_[i]) + assert veryclose(math.atan2_[i], f) + } + vfatan2_sc_ := [[inf(-1), inf(-1)], [inf(-1), -pi], [inf(-1), 0], + [inf(-1), pi], [inf(-1), inf(1)], [inf(-1), nan()], [-pi, inf(-1)], + [-pi, 0], [-pi, inf(1)], [-pi, nan()], [f64(-0.0), inf(-1)], + [f64(-0.0), -pi], [f64(-0.0), -0.0], [f64(-0.0), 0], [f64(-0.0), pi], + [f64(-0.0), inf(1)], [f64(-0.0), nan()], [f64(0), inf(-1)], + [f64(0), -pi], [f64(0), -0.0], [f64(0), 0], [f64(0), pi], + [f64(0), inf(1)], [f64(0), nan()], [pi, inf(-1)], [pi, 0], + [pi, inf(1)], [pi, nan()], [inf(1), inf(-1)], [inf(1), -pi], + [inf(1), 0], [inf(1), pi], [inf(1), inf(1)], [inf(1), nan()], + [nan(), nan()], + ] + atan2_sc_ := [f64(-3.0) * pi / 4.0, /* atan2(-inf, -inf) */ -pi / 2, /* atan2(-inf, -pi) */ + -pi / 2, + /* atan2(-inf, +0) */ -pi / 2, /* atan2(-inf, pi) */ -pi / 4, /* atan2(-inf, +inf) */ + nan(), /* atan2(-inf, nan) */ -pi, /* atan2(-pi, -inf) */ -pi / 2, /* atan2(-pi, +0) */ + -0.0, + /* atan2(-pi, inf) */ nan(), /* atan2(-pi, nan) */ -pi, /* atan2(-0, -inf) */ -pi, + /* atan2(-0, -pi) */ -pi, /* atan2(-0, -0) */ -0.0, /* atan2(-0, +0) */ -0.0, /* atan2(-0, pi) */ + -0.0, + /* atan2(-0, +inf) */ nan(), /* atan2(-0, nan) */ pi, /* atan2(+0, -inf) */ pi, /* atan2(+0, -pi) */ + pi, /* atan2(+0, -0) */ 0, /* atan2(+0, +0) */ 0, /* atan2(+0, pi) */ 0, /* atan2(+0, +inf) */ + nan(), /* atan2(+0, nan) */ pi, /* atan2(pi, -inf) */ pi / 2, /* atan2(pi, +0) */ 0, + /* atan2(pi, +inf) */ nan(), /* atan2(pi, nan) */ 3.0 * pi / 4, /* atan2(+inf, -inf) */ + pi / 2, /* atan2(+inf, -pi) */ pi / 2, /* atan2(+inf, +0) */ pi / 2, /* atan2(+inf, pi) */ + pi / 4, /* atan2(+inf, +inf) */ nan(), /* atan2(+inf, nan) */ + nan(), /* atan2(nan, nan) */ + ] + for i := 0; i < vfatan2_sc_.len; i++ { + f := atan2(vfatan2_sc_[i][0], vfatan2_sc_[i][1]) + assert alike(atan2_sc_[i], f) + } +} + +fn test_ceil() { + // for i := 0; i < vf_.len; i++ { + // f := ceil(vf_[i]) + // assert alike(ceil_[i], f) + // } + vfceil_sc_ := [inf(-1), copysign(0, -1), 0, inf(1), nan()] + ceil_sc_ := [inf(-1), copysign(0, -1), 0, inf(1), nan()] + for i := 0; i < vfceil_sc_.len; i++ { + f := ceil(vfceil_sc_[i]) + assert alike(ceil_sc_[i], f) + } +} + +fn test_cos() { + for i := 0; i < math.vf_.len; i++ { + f := cos(math.vf_[i]) + assert veryclose(math.cos_[i], f) + } + vfcos_sc_ := [inf(-1), inf(1), nan()] + cos_sc_ := [nan(), nan(), nan()] + for i := 0; i < vfcos_sc_.len; i++ { + f := cos(vfcos_sc_[i]) + assert alike(cos_sc_[i], f) + } +} + +fn test_cosh() { + for i := 0; i < math.vf_.len; i++ { + f := cosh(math.vf_[i]) + assert close(math.cosh_[i], f) + } + vfcosh_sc_ := [inf(-1), copysign(0, -1), 0, inf(1), nan()] + cosh_sc_ := [inf(1), 1, 1, inf(1), nan()] + for i := 0; i < vfcosh_sc_.len; i++ { + f := cosh(vfcosh_sc_[i]) + assert alike(cosh_sc_[i], f) + } +} + +fn test_abs() { + for i := 0; i < math.vf_.len; i++ { + f := abs(math.vf_[i]) + assert math.fabs_[i] == f + } +} + +fn test_floor() { + for i := 0; i < math.vf_.len; i++ { + f := floor(math.vf_[i]) + assert alike(math.floor_[i], f) + } + vfceil_sc_ := [inf(-1), copysign(0, -1), 0, inf(1), nan()] + ceil_sc_ := [inf(-1), copysign(0, -1), 0, inf(1), nan()] + for i := 0; i < vfceil_sc_.len; i++ { + f := floor(vfceil_sc_[i]) + assert alike(ceil_sc_[i], f) + } +} + +fn test_max() { + for i := 0; i < math.vf_.len; i++ { + f := max(math.vf_[i], math.ceil_[i]) + assert math.ceil_[i] == f + } +} + +fn test_min() { + for i := 0; i < math.vf_.len; i++ { + f := min(math.vf_[i], math.floor_[i]) + assert math.floor_[i] == f + } +} + +fn test_signi() { + assert signi(inf(-1)) == -1 + assert signi(-72234878292.4586129) == -1 + assert signi(-10) == -1 + assert signi(-pi) == -1 + assert signi(-1) == -1 + assert signi(-0.000000000001) == -1 + assert signi(-0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001) == -1 + assert signi(-0.0) == -1 + // + assert signi(inf(1)) == 1 + assert signi(72234878292.4586129) == 1 + assert signi(10) == 1 + assert signi(pi) == 1 + assert signi(1) == 1 + assert signi(0.000000000001) == 1 + assert signi(0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001) == 1 + assert signi(0.0) == 1 + assert signi(nan()) == 1 +} + +fn test_sign() { + assert sign(inf(-1)) == -1.0 + assert sign(-72234878292.4586129) == -1.0 + assert sign(-10) == -1.0 + assert sign(-pi) == -1.0 + assert sign(-1) == -1.0 + assert sign(-0.000000000001) == -1.0 + assert sign(-0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001) == -1.0 + assert sign(-0.0) == -1.0 + // + assert sign(inf(1)) == 1.0 + assert sign(72234878292.4586129) == 1 + assert sign(10) == 1.0 + assert sign(pi) == 1.0 + assert sign(1) == 1.0 + assert sign(0.000000000001) == 1.0 + assert sign(0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001) == 1.0 + assert sign(0.0) == 1.0 + assert is_nan(sign(nan())) + assert is_nan(sign(-nan())) +} + +fn test_exp() { + for i := 0; i < math.vf_.len; i++ { + f := exp(math.vf_[i]) + assert veryclose(math.exp_[i], f) + } + vfexp_sc_ := [inf(-1), -2000, 2000, inf(1), nan(), /* smallest f64 that overflows Exp(x) */ + 7.097827128933841e+02, 1.48852223e+09, 1.4885222e+09, 1, /* near zero */ + 3.725290298461915e-09, + /* denormal */ -740] + exp_sc_ := [f64(0), 0, inf(1), inf(1), nan(), inf(1), inf(1), + inf(1), 2.718281828459045, 1.0000000037252903, 4.2e-322] + for i := 0; i < vfexp_sc_.len; i++ { + f := exp(vfexp_sc_[i]) + assert alike(exp_sc_[i], f) + } +} + +fn test_exp2() { + for i := 0; i < math.vf_.len; i++ { + f := exp2(math.vf_[i]) + assert soclose(math.exp2_[i], f, 1e-9) + } + vfexp2_sc_ := [f64(-2000), 2000, inf(1), nan(), /* smallest f64 that overflows Exp2(x) */ + 1024, /* near underflow */ -1.07399999999999e+03, /* near zero */ 3.725290298461915e-09] + exp2_sc_ := [f64(0), inf(1), inf(1), nan(), inf(1), 5e-324, 1.0000000025821745] + for i := 0; i < vfexp2_sc_.len; i++ { + f := exp2(vfexp2_sc_[i]) + assert alike(exp2_sc_[i], f) + } +} + +fn test_gamma() { + vfgamma_ := [[inf(1), inf(1)], [inf(-1), nan()], [f64(0), inf(1)], + [f64(-0.0), inf(-1)], [nan(), nan()], [f64(-1), nan()], + [f64(-2), nan()], [f64(-3), nan()], [f64(-1e+16), nan()], + [f64(-1e+300), nan()], [f64(1.7e+308), inf(1)], /* Test inputs inspi_red by Python test suite. */ + // Outputs computed at high precision by PARI/GP. + // If recomputing table entries), be careful to use + // high-precision (%.1000g) formatting of the f64 inputs. + // For example), -2.0000000000000004 is the f64 with exact value + //-2.00000000000000044408920985626161695), and + // gamma(-2.0000000000000004) = -1249999999999999.5386078562728167651513), while + // gamma(-2.00000000000000044408920985626161695) = -1125899906826907.2044875028130093136826. + // Thus the table lists -1.1258999068426235e+15 as the answer. + [f64(0.5), 1.772453850905516], [f64(1.5), 0.886226925452758], + [f64(2.5), 1.329340388179137], [f64(3.5), 3.3233509704478426], + [f64(-0.5), -3.544907701811032], [f64(-1.5), 2.363271801207355], + [f64(-2.5), -0.9453087204829419], [f64(-3.5), 0.2700882058522691], + [f64(0.1), 9.51350769866873], [f64(0.01), 99.4325851191506], + [f64(1e-08), 9.999999942278434e+07], [f64(1e-16), 1e+16], + [f64(0.001), 999.4237724845955], [f64(1e-16), 1e+16], + [f64(1e-308), 1e+308], [f64(5.6e-309), 1.7857142857142864e+308], + [f64(5.5e-309), inf(1)], [f64(1e-309), inf(1)], [f64(1e-323), inf(1)], + [f64(5e-324), inf(1)], [f64(-0.1), -10.686287021193193], + [f64(-0.01), -100.58719796441078], [f64(-1e-08), -1.0000000057721567e+08], + [f64(-1e-16), -1e+16], [f64(-0.001), -1000.5782056293586], + [f64(-1e-16), -1e+16], [f64(-1e-308), -1e+308], [f64(-5.6e-309), -1.7857142857142864e+308], + [f64(-5.5e-309), inf(-1)], [f64(-1e-309), inf(-1)], [f64(-1e-323), inf(-1)], + [f64(-5e-324), inf(-1)], [f64(-0.9999999999999999), -9.007199254740992e+15], + [f64(-1.0000000000000002), 4.5035996273704955e+15], + [f64(-1.9999999999999998), + 2.2517998136852485e+15, + ], + [f64(-2.0000000000000004), -1.1258999068426235e+15], + [f64(-100.00000000000001), + -7.540083334883109e-145, + ], + [f64(-99.99999999999999), 7.540083334884096e-145], [f64(17), 2.0922789888e+13], + [f64(171), 7.257415615307999e+306], [f64(171.6), 1.5858969096672565e+308], + [f64(171.624), 1.7942117599248104e+308], [f64(171.625), inf(1)], + [f64(172), inf(1)], [f64(2000), inf(1)], [f64(-100.5), -3.3536908198076787e-159], + [f64(-160.5), -5.255546447007829e-286], [f64(-170.5), -3.3127395215386074e-308], + [f64(-171.5), 1.9316265431712e-310], [f64(-176.5), -1.196e-321], + [f64(-177.5), 5e-324], [f64(-178.5), -0.0], [f64(-179.5), 0], + [f64(-201.0001), 0], [f64(-202.9999), -0.0], [f64(-1000.5), -0.0], + [f64(-1.0000000003e+09), -0.0], [f64(-4.5035996273704955e+15), 0], + [f64(-63.349078729022985), 4.177797167776188e-88], + [f64(-127.45117632943295), + 1.183111089623681e-214, + ], + ] + _ := vfgamma_[0][0] + // for i := 0; i < math.vf_.len; i++ { + // f := gamma(math.vf_[i]) + // assert veryclose(math.gamma_[i], f) + // } + // for _, g in vfgamma_ { + // f := gamma(g[0]) + // if is_nan(g[1]) || is_inf(g[1], 0) || g[1] == 0 || f == 0 { + // assert alike(g[1], f) + // } else if g[0] > -50 && g[0] <= 171 { + // assert veryclose(g[1], f) + // } else { + // assert soclose(g[1], f, 1e-9) + // } + // } +} + +fn test_hypot() { + for i := 0; i < math.vf_.len; i++ { + a := abs(1e+200 * math.tanh_[i] * sqrt(2.0)) + f := hypot(1e+200 * math.tanh_[i], 1e+200 * math.tanh_[i]) + assert veryclose(a, f) + } + vfhypot_sc_ := [[inf(-1), inf(-1)], [inf(-1), 0], [inf(-1), + inf(1), + ], + [inf(-1), nan()], [f64(-0.0), -0.0], [f64(-0.0), 0], [f64(0), -0.0], + [f64(0), 0], /* +0,0 */ [f64(0), inf(-1)], [f64(0), inf(1)], + [f64(0), nan()], [inf(1), inf(-1)], [inf(1), 0], [inf(1), + inf(1), + ], + [inf(1), nan()], [nan(), inf(-1)], [nan(), 0], [nan(), + inf(1), + ], + [nan(), nan()], + ] + hypot_sc_ := [inf(1), inf(1), inf(1), inf(1), 0, 0, 0, 0, inf(1), + inf(1), nan(), inf(1), inf(1), inf(1), inf(1), inf(1), + nan(), inf(1), nan()] + for i := 0; i < vfhypot_sc_.len; i++ { + f := hypot(vfhypot_sc_[i][0], vfhypot_sc_[i][1]) + assert alike(hypot_sc_[i], f) + } +} + +fn test_log() { + for i := 0; i < math.vf_.len; i++ { + a := abs(math.vf_[i]) + f := log(a) + assert math.log_[i] == f + } + vflog_sc_ := [inf(-1), -pi, copysign(0, -1), 0, 1, inf(1), + nan(), + ] + log_sc_ := [nan(), nan(), inf(-1), inf(-1), 0, inf(1), nan()] + f := log(10) + assert f == ln10 + for i := 0; i < vflog_sc_.len; i++ { + g := log(vflog_sc_[i]) + assert alike(log_sc_[i], g) + } +} + +fn test_log10() { + for i := 0; i < math.vf_.len; i++ { + a := abs(math.vf_[i]) + f := log10(a) + assert veryclose(math.log10_[i], f) + } + vflog_sc_ := [inf(-1), -pi, copysign(0, -1), 0, 1, inf(1), + nan(), + ] + log_sc_ := [nan(), nan(), inf(-1), inf(-1), 0, inf(1), nan()] + for i := 0; i < vflog_sc_.len; i++ { + f := log10(vflog_sc_[i]) + assert alike(log_sc_[i], f) + } +} + +fn test_pow() { + for i := 0; i < math.vf_.len; i++ { + f := pow(10, math.vf_[i]) + assert close(math.pow_[i], f) + } + vfpow_sc_ := [[inf(-1), -pi], [inf(-1), -3], [inf(-1), -0.0], + [inf(-1), 0], [inf(-1), 1], [inf(-1), 3], [inf(-1), pi], + [inf(-1), 0.5], [inf(-1), nan()], [-pi, inf(-1)], [-pi, -pi], + [-pi, -0.0], [-pi, 0], [-pi, 1], [-pi, pi], [-pi, inf(1)], + [-pi, nan()], [f64(-1), inf(-1)], [f64(-1), inf(1)], [f64(-1), nan()], + [f64(-1 / 2), inf(-1)], [f64(-1 / 2), inf(1)], [f64(-0.0), inf(-1)], + [f64(-0.0), -pi], [f64(-0.0), -0.5], [f64(-0.0), -3], + [f64(-0.0), 3], [f64(-0.0), pi], [f64(-0.0), 0.5], [f64(-0.0), inf(1)], + [f64(0), inf(-1)], [f64(0), -pi], [f64(0), -3], [f64(0), -0.0], + [f64(0), 0], [f64(0), 3], [f64(0), pi], [f64(0), inf(1)], + [f64(0), nan()], [f64(1 / 2), inf(-1)], [f64(1 / 2), inf(1)], + [f64(1), inf(-1)], [f64(1), inf(1)], [f64(1), nan()], + [pi, inf(-1)], [pi, -0.0], [pi, 0], [pi, 1], [pi, inf(1)], + [pi, nan()], [inf(1), -pi], [inf(1), -0.0], [inf(1), 0], + [inf(1), 1], [inf(1), pi], [inf(1), nan()], [nan(), -pi], + [nan(), -0.0], [nan(), 0], [nan(), 1], [nan(), pi], [nan(), + nan(), + ]] + pow_sc_ := [f64(0), /* pow(-inf, -pi) */ -0.0, /* pow(-inf, -3) */ 1, /* pow(-inf, -0) */ 1, /* pow(-inf, +0) */ + inf(-1), /* pow(-inf, 1) */ inf(-1), /* pow(-inf, 3) */ + inf(1), /* pow(-inf, pi) */ inf(1), /* pow(-inf, 0.5) */ + nan(), /* pow(-inf, nan) */ 0, /* pow(-pi, -inf) */ nan(), /* pow(-pi, -pi) */ + 1, /* pow(-pi, -0) */ 1, /* pow(-pi, +0) */ -pi, /* pow(-pi, 1) */ nan(), /* pow(-pi, pi) */ + inf(1), /* pow(-pi, +inf) */ nan(), /* pow(-pi, nan) */ 1, /* pow(-1, -inf) IEEE 754-2008 */ + 1, /* pow(-1, +inf) IEEE 754-2008 */ nan(), /* pow(-1, nan) */ + inf(1), /* pow(-1/2, -inf) */ 0, /* pow(-1/2, +inf) */ inf(1), /* pow(-0, -inf) */ + inf(1), /* pow(-0, -pi) */ inf(1), /* pow(-0, -0.5) */ + inf(-1), /* pow(-0, -3) IEEE 754-2008 */ -0.0, /* pow(-0, 3) IEEE 754-2008 */ 0, /* pow(-0, pi) */ + 0, /* pow(-0, 0.5) */ 0, /* pow(-0, +inf) */ inf(1), /* pow(+0, -inf) */ + inf(1), /* pow(+0, -pi) */ inf(1), /* pow(+0, -3) */ 1, /* pow(+0, -0) */ 1, /* pow(+0, +0) */ + 0, /* pow(+0, 3) */ 0, + /* pow(+0, pi) */ 0, /* pow(+0, +inf) */ nan(), /* pow(+0, nan) */ + inf(1), /* pow(1/2, -inf) */ 0, /* pow(1/2, +inf) */ 1, /* pow(1, -inf) IEEE 754-2008 */ + 1, /* pow(1, +inf) IEEE 754-2008 */ 1, /* pow(1, nan) IEEE 754-2008 */ 0, /* pow(pi, -inf) */ + 1, /* pow(pi, -0) */ 1, /* pow(pi, +0) */ pi, /* pow(pi, 1) */ inf(1), /* pow(pi, +inf) */ + nan(), /* pow(pi, nan) */ 0, /* pow(+inf, -pi) */ 1, /* pow(+inf, -0) */ 1, /* pow(+inf, +0) */ + inf(1), /* pow(+inf, 1) */ inf(1), /* pow(+inf, pi) */ + nan(), /* pow(+inf, nan) */ nan(), /* pow(nan, -pi) */ 1, /* pow(nan, -0) */ 1, /* pow(nan, +0) */ + nan(), /* pow(nan, 1) */ nan(), /* pow(nan, pi) */ nan(), /* pow(nan, nan) */] + for i := 0; i < vfpow_sc_.len; i++ { + f := pow(vfpow_sc_[i][0], vfpow_sc_[i][1]) + assert alike(pow_sc_[i], f) + } +} + +fn test_round() { + for i := 0; i < math.vf_.len; i++ { + f := round(math.vf_[i]) + assert alike(math.round_[i], f) + } + vfround_sc_ := [[f64(0), 0], [nan(), nan()], [inf(1), inf(1)]] + vfround_even_sc_ := [[f64(0), 0], [f64(1.390671161567e-309), 0], /* denormal */ + [f64(0.49999999999999994), 0], /* 0.5-epsilon */ [f64(0.5), 0], + [f64(0.5000000000000001), 1], /* 0.5+epsilon */ [f64(-1.5), -2], + [f64(-2.5), -2], [nan(), nan()], [inf(1), inf(1)], + [f64(2251799813685249.5), 2251799813685250], + /* 1 bit fractian */ [f64(2251799813685250.5), 2251799813685250], + [f64(4503599627370495.5), 4503599627370496], /* 1 bit fraction, rounding to 0 bit fractian */ + [f64(4503599627370497), 4503599627370497], /* large integer */ + ] + _ := vfround_even_sc_[0][0] + for i := 0; i < vfround_sc_.len; i++ { + f := round(vfround_sc_[i][0]) + assert alike(vfround_sc_[i][1], f) + } +} + +fn test_sin() { + for i := 0; i < math.vf_.len; i++ { + f := sin(math.vf_[i]) + assert veryclose(math.sin_[i], f) + } + vfsin_sc_ := [inf(-1), copysign(0, -1), 0, inf(1), nan()] + sin_sc_ := [nan(), copysign(0, -1), 0, nan(), nan()] + for i := 0; i < vfsin_sc_.len; i++ { + f := sin(vfsin_sc_[i]) + assert alike(sin_sc_[i], f) + } +} + +fn test_sinh() { + for i := 0; i < math.vf_.len; i++ { + f := sinh(math.vf_[i]) + assert close(math.sinh_[i], f) + } + vfsinh_sc_ := [inf(-1), copysign(0, -1), 0, inf(1), nan()] + sinh_sc_ := [inf(-1), copysign(0, -1), 0, inf(1), nan()] + for i := 0; i < vfsinh_sc_.len; i++ { + f := sinh(vfsinh_sc_[i]) + assert alike(sinh_sc_[i], f) + } +} + +fn test_sqrt() { + for i := 0; i < math.vf_.len; i++ { + mut a := abs(math.vf_[i]) + mut f := sqrt(a) + assert veryclose(math.sqrt_[i], f) + a = abs(math.vf_[i]) + f = sqrt(a) + assert veryclose(math.sqrt_[i], f) + } + vfsqrt_sc_ := [inf(-1), -pi, copysign(0, -1), 0, inf(1), nan()] + sqrt_sc_ := [nan(), nan(), copysign(0, -1), 0, inf(1), nan()] + for i := 0; i < vfsqrt_sc_.len; i++ { + mut f := sqrt(vfsqrt_sc_[i]) + assert alike(sqrt_sc_[i], f) + f = sqrt(vfsqrt_sc_[i]) + assert alike(sqrt_sc_[i], f) + } +} + +fn test_tan() { + for i := 0; i < math.vf_.len; i++ { + f := tan(math.vf_[i]) + assert veryclose(math.tan_[i], f) + } + vfsin_sc_ := [inf(-1), copysign(0, -1), 0, inf(1), nan()] + sin_sc_ := [nan(), copysign(0, -1), 0, nan(), nan()] + // same special cases as sin + for i := 0; i < vfsin_sc_.len; i++ { + f := tan(vfsin_sc_[i]) + assert alike(sin_sc_[i], f) + } +} + +fn test_tanh() { + for i := 0; i < math.vf_.len; i++ { + f := tanh(math.vf_[i]) + assert veryclose(math.tanh_[i], f) + } + vftanh_sc_ := [inf(-1), copysign(0, -1), 0, inf(1), nan()] + tanh_sc_ := [f64(-1), copysign(0, -1), 0, 1, nan()] + for i := 0; i < vftanh_sc_.len; i++ { + f := tanh(vftanh_sc_[i]) + assert alike(tanh_sc_[i], f) + } +} + +fn test_trunc() { + // for i := 0; i < vf_.len; i++ { + // f := trunc(vf_[i]) + // assert alike(trunc_[i], f) + // } + vfceil_sc_ := [inf(-1), copysign(0, -1), 0, inf(1), nan()] + ceil_sc_ := [inf(-1), copysign(0, -1), 0, inf(1), nan()] + for i := 0; i < vfceil_sc_.len; i++ { + f := trunc(vfceil_sc_[i]) + assert alike(ceil_sc_[i], f) + } +} + +fn test_gcd() { + assert gcd(6, 9) == 3 + assert gcd(6, -9) == 3 + assert gcd(-6, -9) == 3 + assert gcd(0, 0) == 0 +} + +fn test_lcm() { + assert lcm(2, 3) == 6 + assert lcm(-2, 3) == 6 + assert lcm(-2, -3) == 6 + assert lcm(0, 0) == 0 +} + +fn test_digits() { + digits_in_10th_base := digits(125, 10) + assert digits_in_10th_base[0] == 5 + assert digits_in_10th_base[1] == 2 + assert digits_in_10th_base[2] == 1 + digits_in_16th_base := digits(15, 16) + assert digits_in_16th_base[0] == 15 + negative_digits := digits(-4, 2) + assert negative_digits[2] == -1 +} + +// Check that math functions of high angle values +// return accurate results. [since (vf_[i] + large) - large != vf_[i], +// testing for Trig(vf_[i] + large) == Trig(vf_[i]), where large is +// a multiple of 2 * pi, is misleading.] +fn test_large_cos() { + large := 100000.0 * pi + for i := 0; i < math.vf_.len; i++ { + f1 := math.cos_large_[i] + f2 := cos(math.vf_[i] + large) + assert soclose(f1, f2, 4e-9) + } +} + +fn test_large_sin() { + large := 100000.0 * pi + for i := 0; i < math.vf_.len; i++ { + f1 := math.sin_large_[i] + f2 := sin(math.vf_[i] + large) + assert soclose(f1, f2, 4e-9) + } +} + +fn test_large_tan() { + large := 100000.0 * pi + for i := 0; i < math.vf_.len; i++ { + f1 := math.tan_large_[i] + f2 := tan(math.vf_[i] + large) + assert soclose(f1, f2, 4e-9) + } +} diff --git a/v_windows/v/old/vlib/math/mathutil/mathutil.v b/v_windows/v/old/vlib/math/mathutil/mathutil.v new file mode 100644 index 0000000..0930e26 --- /dev/null +++ b/v_windows/v/old/vlib/math/mathutil/mathutil.v @@ -0,0 +1,19 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module mathutil + +[inline] +pub fn min(a T, b T) T { + return if a < b { a } else { b } +} + +[inline] +pub fn max(a T, b T) T { + return if a > b { a } else { b } +} + +[inline] +pub fn abs(a T) T { + return if a > 0 { a } else { -a } +} diff --git a/v_windows/v/old/vlib/math/mathutil/mathutil_test.v b/v_windows/v/old/vlib/math/mathutil/mathutil_test.v new file mode 100644 index 0000000..6b3b68a --- /dev/null +++ b/v_windows/v/old/vlib/math/mathutil/mathutil_test.v @@ -0,0 +1,22 @@ +import math.mathutil as mu + +fn test_min() { + assert mu.min(42, 13) == 13 + assert mu.min(5, -10) == -10 + assert mu.min(7.1, 7.3) == 7.1 + assert mu.min(u32(32), u32(17)) == 17 +} + +fn test_max() { + assert mu.max(42, 13) == 42 + assert mu.max(5, -10) == 5 + assert mu.max(7.1, 7.3) == 7.3 + assert mu.max(u32(60), u32(17)) == 60 +} + +fn test_abs() { + assert mu.abs(99) == 99 + assert mu.abs(-10) == 10 + assert mu.abs(1.2345) == 1.2345 + assert mu.abs(-5.5) == 5.5 +} diff --git a/v_windows/v/old/vlib/math/stats/stats.v b/v_windows/v/old/vlib/math/stats/stats.v new file mode 100644 index 0000000..d7317bf --- /dev/null +++ b/v_windows/v/old/vlib/math/stats/stats.v @@ -0,0 +1,249 @@ +module stats + +import math + +// TODO: Implement all of them with generics + +// This module defines the following statistical operations on f64 array +// --------------------------- +// | Summary of Functions | +// --------------------------- +// ----------------------------------------------------------------------- +// freq - Frequency +// mean - Mean +// geometric_mean - Geometric Mean +// harmonic_mean - Harmonic Mean +// median - Median +// mode - Mode +// rms - Root Mean Square +// population_variance - Population Variance +// sample_variance - Sample Variance +// population_stddev - Population Standard Deviation +// sample_stddev - Sample Standard Deviation +// mean_absdev - Mean Absolute Deviation +// min - Minimum of the Array +// max - Maximum of the Array +// range - Range of the Array ( max - min ) +// ----------------------------------------------------------------------- + +// Measure of Occurance +// Frequency of a given number +// Based on +// https://www.mathsisfun.com/data/frequency-distribution.html +pub fn freq(arr []f64, val f64) int { + if arr.len == 0 { + return 0 + } + mut count := 0 + for v in arr { + if v == val { + count++ + } + } + return count +} + +// Measure of Central Tendancy +// Mean of the given input array +// Based on +// https://www.mathsisfun.com/data/central-measures.html +pub fn mean(arr []f64) f64 { + if arr.len == 0 { + return f64(0) + } + mut sum := f64(0) + for v in arr { + sum += v + } + return sum / f64(arr.len) +} + +// Measure of Central Tendancy +// Geometric Mean of the given input array +// Based on +// https://www.mathsisfun.com/numbers/geometric-mean.html +pub fn geometric_mean(arr []f64) f64 { + if arr.len == 0 { + return f64(0) + } + mut sum := f64(1) + for v in arr { + sum *= v + } + return math.pow(sum, f64(1) / arr.len) +} + +// Measure of Central Tendancy +// Harmonic Mean of the given input array +// Based on +// https://www.mathsisfun.com/numbers/harmonic-mean.html +pub fn harmonic_mean(arr []f64) f64 { + if arr.len == 0 { + return f64(0) + } + mut sum := f64(0) + for v in arr { + sum += f64(1) / v + } + return f64(arr.len) / sum +} + +// Measure of Central Tendancy +// Median of the given input array ( input array is assumed to be sorted ) +// Based on +// https://www.mathsisfun.com/data/central-measures.html +pub fn median(arr []f64) f64 { + if arr.len == 0 { + return f64(0) + } + if arr.len % 2 == 0 { + mid := (arr.len / 2) - 1 + return (arr[mid] + arr[mid + 1]) / f64(2) + } else { + return arr[((arr.len - 1) / 2)] + } +} + +// Measure of Central Tendancy +// Mode of the given input array +// Based on +// https://www.mathsisfun.com/data/central-measures.html +pub fn mode(arr []f64) f64 { + if arr.len == 0 { + return f64(0) + } + mut freqs := []int{} + for v in arr { + freqs << freq(arr, v) + } + mut max := 0 + for i in 0 .. freqs.len { + if freqs[i] > freqs[max] { + max = i + } + } + return arr[max] +} + +// Root Mean Square of the given input array +// Based on +// https://en.wikipedia.org/wiki/Root_mean_square +pub fn rms(arr []f64) f64 { + if arr.len == 0 { + return f64(0) + } + mut sum := f64(0) + for v in arr { + sum += math.pow(v, 2) + } + return math.sqrt(sum / f64(arr.len)) +} + +// Measure of Dispersion / Spread +// Population Variance of the given input array +// Based on +// https://www.mathsisfun.com/data/standard-deviation.html +pub fn population_variance(arr []f64) f64 { + if arr.len == 0 { + return f64(0) + } + m := mean(arr) + mut sum := f64(0) + for v in arr { + sum += math.pow(v - m, 2) + } + return sum / f64(arr.len) +} + +// Measure of Dispersion / Spread +// Sample Variance of the given input array +// Based on +// https://www.mathsisfun.com/data/standard-deviation.html +pub fn sample_variance(arr []f64) f64 { + if arr.len == 0 { + return f64(0) + } + m := mean(arr) + mut sum := f64(0) + for v in arr { + sum += math.pow(v - m, 2) + } + return sum / f64(arr.len - 1) +} + +// Measure of Dispersion / Spread +// Population Standard Deviation of the given input array +// Based on +// https://www.mathsisfun.com/data/standard-deviation.html +pub fn population_stddev(arr []f64) f64 { + if arr.len == 0 { + return f64(0) + } + return math.sqrt(population_variance(arr)) +} + +// Measure of Dispersion / Spread +// Sample Standard Deviation of the given input array +// Based on +// https://www.mathsisfun.com/data/standard-deviation.html +pub fn sample_stddev(arr []f64) f64 { + if arr.len == 0 { + return f64(0) + } + return math.sqrt(sample_variance(arr)) +} + +// Measure of Dispersion / Spread +// Mean Absolute Deviation of the given input array +// Based on +// https://en.wikipedia.org/wiki/Average_absolute_deviation +pub fn mean_absdev(arr []f64) f64 { + if arr.len == 0 { + return f64(0) + } + amean := mean(arr) + mut sum := f64(0) + for v in arr { + sum += math.abs(v - amean) + } + return sum / f64(arr.len) +} + +// Minimum of the given input array +pub fn min(arr []f64) f64 { + if arr.len == 0 { + return f64(0) + } + mut min := arr[0] + for v in arr { + if v < min { + min = v + } + } + return min +} + +// Maximum of the given input array +pub fn max(arr []f64) f64 { + if arr.len == 0 { + return f64(0) + } + mut max := arr[0] + for v in arr { + if v > max { + max = v + } + } + return max +} + +// Measure of Dispersion / Spread +// Range ( Maximum - Minimum ) of the given input array +// Based on +// https://www.mathsisfun.com/data/range.html +pub fn range(arr []f64) f64 { + if arr.len == 0 { + return f64(0) + } + return max(arr) - min(arr) +} diff --git a/v_windows/v/old/vlib/math/stats/stats_test.v b/v_windows/v/old/vlib/math/stats/stats_test.v new file mode 100644 index 0000000..c18daff --- /dev/null +++ b/v_windows/v/old/vlib/math/stats/stats_test.v @@ -0,0 +1,269 @@ +import math.stats +import math + +fn test_freq() { + // Tests were also verified on Wolfram Alpha + data := [f64(10.0), f64(10.0), f64(5.9), f64(2.7)] + mut o := stats.freq(data, 10.0) + assert o == 2 + o = stats.freq(data, 2.7) + assert o == 1 + o = stats.freq(data, 15) + assert o == 0 +} + +fn tst_res(str1 string, str2 string) bool { + if (math.abs(str1.f64() - str2.f64())) < 1e-5 { + return true + } + return false +} + +fn test_mean() { + // Tests were also verified on Wolfram Alpha + mut data := [f64(10.0), f64(4.45), f64(5.9), f64(2.7)] + mut o := stats.mean(data) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert tst_res(o.str(), '5.762500') + data = [f64(-3.0), f64(67.31), f64(4.4), f64(1.89)] + o = stats.mean(data) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert tst_res(o.str(), '17.650000') + data = [f64(12.0), f64(7.88), f64(76.122), f64(54.83)] + o = stats.mean(data) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert tst_res(o.str(), '37.708000') +} + +fn test_geometric_mean() { + // Tests were also verified on Wolfram Alpha + mut data := [f64(10.0), f64(4.45), f64(5.9), f64(2.7)] + mut o := stats.geometric_mean(data) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert tst_res(o.str(), '5.15993') + data = [f64(-3.0), f64(67.31), f64(4.4), f64(1.89)] + o = stats.geometric_mean(data) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert o.str() == 'nan' || o.str() == '-nan' || o.str() == '-1.#IND00' || o == f64(0) + || o.str() == '-nan(ind)' // Because in math it yields a complex number + data = [f64(12.0), f64(7.88), f64(76.122), f64(54.83)] + o = stats.geometric_mean(data) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert tst_res(o.str(), '25.064496') +} + +fn test_harmonic_mean() { + // Tests were also verified on Wolfram Alpha + mut data := [f64(10.0), f64(4.45), f64(5.9), f64(2.7)] + mut o := stats.harmonic_mean(data) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert tst_res(o.str(), '4.626519') + data = [f64(-3.0), f64(67.31), f64(4.4), f64(1.89)] + o = stats.harmonic_mean(data) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert tst_res(o.str(), '9.134577') + data = [f64(12.0), f64(7.88), f64(76.122), f64(54.83)] + o = stats.harmonic_mean(data) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert tst_res(o.str(), '16.555477') +} + +fn test_median() { + // Tests were also verified on Wolfram Alpha + // Assumes sorted array + + // Even + mut data := [f64(2.7), f64(4.45), f64(5.9), f64(10.0)] + mut o := stats.median(data) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert tst_res(o.str(), '5.175000') + data = [f64(-3.0), f64(1.89), f64(4.4), f64(67.31)] + o = stats.median(data) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert tst_res(o.str(), '3.145000') + data = [f64(7.88), f64(12.0), f64(54.83), f64(76.122)] + o = stats.median(data) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert tst_res(o.str(), '33.415000') + + // Odd + data = [f64(2.7), f64(4.45), f64(5.9), f64(10.0), f64(22)] + o = stats.median(data) + assert o == f64(5.9) + data = [f64(-3.0), f64(1.89), f64(4.4), f64(9), f64(67.31)] + o = stats.median(data) + assert o == f64(4.4) + data = [f64(7.88), f64(3.3), f64(12.0), f64(54.83), f64(76.122)] + o = stats.median(data) + assert o == f64(12.0) +} + +fn test_mode() { + // Tests were also verified on Wolfram Alpha + mut data := [f64(2.7), f64(2.7), f64(4.45), f64(5.9), f64(10.0)] + mut o := stats.mode(data) + assert o == f64(2.7) + data = [f64(-3.0), f64(1.89), f64(1.89), f64(1.89), f64(9), f64(4.4), f64(4.4), f64(9), + f64(67.31), + ] + o = stats.mode(data) + assert o == f64(1.89) + // Testing greedy nature + data = [f64(2.0), f64(4.0), f64(2.0), f64(4.0)] + o = stats.mode(data) + assert o == f64(2.0) +} + +fn test_rms() { + // Tests were also verified on Wolfram Alpha + mut data := [f64(10.0), f64(4.45), f64(5.9), f64(2.7)] + mut o := stats.rms(data) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert tst_res(o.str(), '6.362046') + data = [f64(-3.0), f64(67.31), f64(4.4), f64(1.89)] + o = stats.rms(data) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert tst_res(o.str(), '33.773393') + data = [f64(12.0), f64(7.88), f64(76.122), f64(54.83)] + o = stats.rms(data) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert tst_res(o.str(), '47.452561') +} + +fn test_population_variance() { + // Tests were also verified on Wolfram Alpha + mut data := [f64(10.0), f64(4.45), f64(5.9), f64(2.7)] + mut o := stats.population_variance(data) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert tst_res(o.str(), '7.269219') + data = [f64(-3.0), f64(67.31), f64(4.4), f64(1.89)] + o = stats.population_variance(data) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert tst_res(o.str(), '829.119550') + data = [f64(12.0), f64(7.88), f64(76.122), f64(54.83)] + o = stats.population_variance(data) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert tst_res(o.str(), '829.852282') +} + +fn test_sample_variance() { + // Tests were also verified on Wolfram Alpha + mut data := [f64(10.0), f64(4.45), f64(5.9), f64(2.7)] + mut o := stats.sample_variance(data) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert tst_res(o.str(), '9.692292') + data = [f64(-3.0), f64(67.31), f64(4.4), f64(1.89)] + o = stats.sample_variance(data) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert tst_res(o.str(), '1105.492733') + data = [f64(12.0), f64(7.88), f64(76.122), f64(54.83)] + o = stats.sample_variance(data) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert tst_res(o.str(), '1106.469709') +} + +fn test_population_stddev() { + // Tests were also verified on Wolfram Alpha + mut data := [f64(10.0), f64(4.45), f64(5.9), f64(2.7)] + mut o := stats.population_stddev(data) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert tst_res(o.str(), '2.696149') + data = [f64(-3.0), f64(67.31), f64(4.4), f64(1.89)] + o = stats.population_stddev(data) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert tst_res(o.str(), '28.794436') + data = [f64(12.0), f64(7.88), f64(76.122), f64(54.83)] + o = stats.population_stddev(data) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert tst_res(o.str(), '28.807157') +} + +fn test_sample_stddev() { + // Tests were also verified on Wolfram Alpha + mut data := [f64(10.0), f64(4.45), f64(5.9), f64(2.7)] + mut o := stats.sample_stddev(data) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert tst_res(o.str(), '3.113245') + data = [f64(-3.0), f64(67.31), f64(4.4), f64(1.89)] + o = stats.sample_stddev(data) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert tst_res(o.str(), '33.248951') + data = [f64(12.0), f64(7.88), f64(76.122), f64(54.83)] + o = stats.sample_stddev(data) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert tst_res(o.str(), '33.263639') +} + +fn test_mean_absdev() { + // Tests were also verified on Wolfram Alpha + mut data := [f64(10.0), f64(4.45), f64(5.9), f64(2.7)] + mut o := stats.mean_absdev(data) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert tst_res(o.str(), '2.187500') + data = [f64(-3.0), f64(67.31), f64(4.4), f64(1.89)] + o = stats.mean_absdev(data) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert tst_res(o.str(), '24.830000') + data = [f64(12.0), f64(7.88), f64(76.122), f64(54.83)] + o = stats.mean_absdev(data) + // Some issue with precision comparison in f64 using == operator hence serializing to string + assert tst_res(o.str(), '27.768000') +} + +fn test_min() { + // Tests were also verified on Wolfram Alpha + mut data := [f64(10.0), f64(4.45), f64(5.9), f64(2.7)] + mut o := stats.min(data) + assert o == f64(2.7) + data = [f64(-3.0), f64(67.31), f64(4.4), f64(1.89)] + o = stats.min(data) + assert o == f64(-3.0) + data = [f64(12.0), f64(7.88), f64(76.122), f64(54.83)] + o = stats.min(data) + assert o == f64(7.88) +} + +fn test_max() { + // Tests were also verified on Wolfram Alpha + mut data := [f64(10.0), f64(4.45), f64(5.9), f64(2.7)] + mut o := stats.max(data) + assert o == f64(10.0) + data = [f64(-3.0), f64(67.31), f64(4.4), f64(1.89)] + o = stats.max(data) + assert o == f64(67.31) + data = [f64(12.0), f64(7.88), f64(76.122), f64(54.83)] + o = stats.max(data) + assert o == f64(76.122) +} + +fn test_range() { + // Tests were also verified on Wolfram Alpha + mut data := [f64(10.0), f64(4.45), f64(5.9), f64(2.7)] + mut o := stats.range(data) + assert o == f64(7.3) + data = [f64(-3.0), f64(67.31), f64(4.4), f64(1.89)] + o = stats.range(data) + assert o == f64(70.31) + data = [f64(12.0), f64(7.88), f64(76.122), f64(54.83)] + o = stats.range(data) + assert o == f64(68.242) +} + +fn test_passing_empty() { + data := []f64{} + assert stats.freq(data, 0) == 0 + assert stats.mean(data) == f64(0) + assert stats.geometric_mean(data) == f64(0) + assert stats.harmonic_mean(data) == f64(0) + assert stats.median(data) == f64(0) + assert stats.mode(data) == f64(0) + assert stats.rms(data) == f64(0) + assert stats.population_variance(data) == f64(0) + assert stats.sample_variance(data) == f64(0) + assert stats.population_stddev(data) == f64(0) + assert stats.sample_stddev(data) == f64(0) + assert stats.mean_absdev(data) == f64(0) + assert stats.min(data) == f64(0) + assert stats.max(data) == f64(0) + assert stats.range(data) == f64(0) +} diff --git a/v_windows/v/old/vlib/math/unsafe.v b/v_windows/v/old/vlib/math/unsafe.v new file mode 100644 index 0000000..e6ebc6f --- /dev/null +++ b/v_windows/v/old/vlib/math/unsafe.v @@ -0,0 +1,38 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module math + +// f32_bits returns the IEEE 754 binary representation of f, +// with the sign bit of f and the result in the same bit position. +// f32_bits(f32_from_bits(x)) == x. +pub fn f32_bits(f f32) u32 { + p := *unsafe { &u32(&f) } + return p +} + +// f32_from_bits returns the floating-point number corresponding +// to the IEEE 754 binary representation b, with the sign bit of b +// and the result in the same bit position. +// f32_from_bits(f32_bits(x)) == x. +pub fn f32_from_bits(b u32) f32 { + p := *unsafe { &f32(&b) } + return p +} + +// f64_bits returns the IEEE 754 binary representation of f, +// with the sign bit of f and the result in the same bit position, +// and f64_bits(f64_from_bits(x)) == x. +pub fn f64_bits(f f64) u64 { + p := *unsafe { &u64(&f) } + return p +} + +// f64_from_bits returns the floating-point number corresponding +// to the IEEE 754 binary representation b, with the sign bit of b +// and the result in the same bit position. +// f64_from_bits(f64_bits(x)) == x. +pub fn f64_from_bits(b u64) f64 { + p := *unsafe { &f64(&b) } + return p +} diff --git a/v_windows/v/old/vlib/math/util/util.v b/v_windows/v/old/vlib/math/util/util.v new file mode 100644 index 0000000..0802d28 --- /dev/null +++ b/v_windows/v/old/vlib/math/util/util.v @@ -0,0 +1,82 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module util + +// imin returns the smallest of two integer values +[inline] +pub fn imin(a int, b int) int { + return if a < b { a } else { b } +} + +// imin returns the biggest of two integer values +[inline] +pub fn imax(a int, b int) int { + return if a > b { a } else { b } +} + +// iabs returns an integer as absolute value +[inline] +pub fn iabs(v int) int { + return if v > 0 { v } else { -v } +} + +// umin returns the smallest of two u32 values +[inline] +pub fn umin(a u32, b u32) u32 { + return if a < b { a } else { b } +} + +// umax returns the biggest of two u32 values +[inline] +pub fn umax(a u32, b u32) u32 { + return if a > b { a } else { b } +} + +// uabs returns an u32 as absolute value +[inline] +pub fn uabs(v u32) u32 { + return if v > 0 { v } else { -v } +} + +// fmin_32 returns the smallest `f32` of input `a` and `b`. +// Example: assert fmin_32(2.0,3.0) == 2.0 +[inline] +pub fn fmin_32(a f32, b f32) f32 { + return if a < b { a } else { b } +} + +// fmax_32 returns the largest `f32` of input `a` and `b`. +// Example: assert fmax_32(2.0,3.0) == 3.0 +[inline] +pub fn fmax_32(a f32, b f32) f32 { + return if a > b { a } else { b } +} + +// fabs_32 returns the absolute value of `a` as a `f32` value. +// Example: assert fabs_32(-2.0) == 2.0 +[inline] +pub fn fabs_32(v f32) f32 { + return if v > 0 { v } else { -v } +} + +// fmin_64 returns the smallest `f64` of input `a` and `b`. +// Example: assert fmin_64(2.0,3.0) == 2.0 +[inline] +pub fn fmin_64(a f64, b f64) f64 { + return if a < b { a } else { b } +} + +// fmax_64 returns the largest `f64` of input `a` and `b`. +// Example: assert fmax_64(2.0,3.0) == 3.0 +[inline] +pub fn fmax_64(a f64, b f64) f64 { + return if a > b { a } else { b } +} + +// fabs_64 returns the absolute value of `a` as a `f64` value. +// Example: assert fabs_64(-2.0) == f64(2.0) +[inline] +pub fn fabs_64(v f64) f64 { + return if v > 0 { v } else { -v } +} diff --git a/v_windows/v/old/vlib/mssql/README.md b/v_windows/v/old/vlib/mssql/README.md new file mode 100644 index 0000000..ff4fefc --- /dev/null +++ b/v_windows/v/old/vlib/mssql/README.md @@ -0,0 +1,69 @@ +# SQL Server ODBC + +* This is a V wrapper of SQL Server ODBC C/C++ library + +## Dependencies +* ODBC C/C++ library + * Linux Install: + * Details: https://docs.microsoft.com/en-us/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server + * `msodbcsql17` and `unixodbc-dev` packages are needed + * Windows Install: + * `odbc` lib is included in windows sdk for most of distributions, + so there is no need to install it separately + * Details: https://docs.microsoft.com/en-us/sql/connect/odbc/microsoft-odbc-driver-for-sql-server + +## Windows Notes +### Using `msvc` +* Make sure `cl.exe` of `msvc` is accessible from command line. +You can run `v` commands in `Visual Studio 2019 Developer Command Prompt` to be safe. +* C Headers and dlls can be automatically resolved by `msvc`. +### Using `tcc` +* Copy those headers to `@VEXEROOT\thirdparty\mssql\include`. +The version number `10.0.18362.0` might differ on your system. +Command Prompt commands: +```cmd +copy "C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\um\sql.h" thirdparty\mssql\include +copy "C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\um\sqlext.h" thirdparty\mssql\include +copy "C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\um\sqltypes.h" thirdparty\mssql\include +copy "C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\um\sqlucode.h" thirdparty\mssql\include +copy "C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\shared\sal.h" thirdparty\mssql\include +copy "C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\shared\concurrencysal.h" thirdparty\mssql\include +``` +* dlls can be automatically resolved by `tcc` + +## TODO +* Support Mac +* Support ORM + +## Usage +```v ignore +import mssql + +fn test_example() ? { + // connect to server + config := mssql.Config{ + driver: 'ODBC Driver 17 for SQL Server' + server: 'tcp:localhost' + uid: '' + pwd: '' + } + + mut conn := mssql.Connection{} + + conn.connect(config.get_conn_str()) ? + + defer { + conn.close() + } + + // get current db name + mut query := 'SELECT DB_NAME()' + mut res := conn.query(query) ? + assert res == mssql.Result{ + rows: [mssql.Row{ + vals: ['master'] + }] + num_rows_affected: -1 + } +} +``` diff --git a/v_windows/v/old/vlib/mssql/_cdef_nix.c.v b/v_windows/v/old/vlib/mssql/_cdef_nix.c.v new file mode 100644 index 0000000..0a9ec00 --- /dev/null +++ b/v_windows/v/old/vlib/mssql/_cdef_nix.c.v @@ -0,0 +1,6 @@ +module mssql + +#flag -lodbc + +#include +#include diff --git a/v_windows/v/old/vlib/mssql/_cdef_windows.c.v b/v_windows/v/old/vlib/mssql/_cdef_windows.c.v new file mode 100644 index 0000000..61724ee --- /dev/null +++ b/v_windows/v/old/vlib/mssql/_cdef_windows.c.v @@ -0,0 +1,12 @@ +module mssql + +// mssql module does not support tcc on windows + +// odbc32 lib comes with windows sdk and does not need to be installed separately. +// v builder for msvc can resolve the sdk includes search path, so no need to repeat here. +#flag windows -lodbc32 + +// Special handling of sql headers on windows. +// Source is in v third party folder. +#flag windows -I@VEXEROOT/thirdparty/mssql/include +#include diff --git a/v_windows/v/old/vlib/mssql/_cdefs.c.v b/v_windows/v/old/vlib/mssql/_cdefs.c.v new file mode 100644 index 0000000..0f8e205 --- /dev/null +++ b/v_windows/v/old/vlib/mssql/_cdefs.c.v @@ -0,0 +1,27 @@ +module mssql + +fn C.SQLAllocHandle(HandleType C.SQLSMALLINT, InputHandle C.SQLHANDLE, OutputHandle &C.SQLHANDLE) C.SQLRETURN + +fn C.SQLSetEnvAttr(EnvironmentHandle C.SQLHENV, Attribute C.SQLINTEGER, Value C.SQLPOINTER, StringLength C.SQLINTEGER) C.SQLRETURN + +fn C.SQLGetDiagRec(HandleType C.SQLSMALLINT, Handle C.SQLHANDLE, RecNumber C.SQLSMALLINT, Sqlstate &C.SQLCHAR, NativeError &C.SQLINTEGER, MessageText &C.SQLCHAR, BufferLength C.SQLSMALLINT, TextLength &C.SQLSMALLINT) C.SQLRETURN + +fn C.SQLSetConnectAttr(ConnectionHandle C.SQLHDBC, Attribute C.SQLINTEGER, Value C.SQLPOINTER, StringLength C.SQLINTEGER) C.SQLRETURN + +fn C.SQLDriverConnect(hdbc C.SQLHDBC, hwnd C.SQLHWND, szConnStrIn &C.SQLCHAR, cbConnStrIn C.SQLSMALLINT, szConnStrOut &C.SQLCHAR, cbConnStrOutMax C.SQLSMALLINT, pcbConnStrOut &C.SQLSMALLINT, fDriverCompletion C.SQLUSMALLINT) C.SQLRETURN + +fn C.SQLDisconnect(ConnectionHandle C.SQLHDBC) C.SQLRETURN + +fn C.SQLExecDirect(StatementHandle C.SQLHSTMT, StatementText &C.SQLCHAR, TextLength C.SQLINTEGER) C.SQLRETURN + +fn C.SQLBindCol(StatementHandle C.SQLHSTMT, ColumnNumber C.SQLUSMALLINT, TargetType C.SQLSMALLINT, TargetValue C.SQLPOINTER, BufferLength C.SQLLEN, StrLen_or_Ind &C.SQLLEN) C.SQLRETURN + +fn C.SQLFetch(StatementHandle C.SQLHSTMT) C.SQLRETURN + +fn C.SQLFreeHandle(HandleType C.SQLSMALLINT, Handle C.SQLHANDLE) C.SQLRETURN + +fn C.SQLNumResultCols(StatementHandle C.SQLHSTMT, ColumnCount &C.SQLSMALLINT) C.SQLRETURN + +fn C.SQLColAttribute(StatementHandle C.SQLHSTMT, ColumnNumber C.SQLUSMALLINT, FieldIdentifier C.SQLUSMALLINT, CharacterAttribute C.SQLPOINTER, BufferLength C.SQLSMALLINT, StringLength C.SQLSMALLINT, NumericAttribute &C.SQLLEN) C.SQLRETURN + +fn C.SQLRowCount(StatementHandle C.SQLHSTMT, RowCount &C.SQLLEN) C.SQLRETURN diff --git a/v_windows/v/old/vlib/mssql/config.v b/v_windows/v/old/vlib/mssql/config.v new file mode 100644 index 0000000..7f26d6f --- /dev/null +++ b/v_windows/v/old/vlib/mssql/config.v @@ -0,0 +1,20 @@ +module mssql + +pub struct Config { +pub: + driver string + server string + uid string + pwd string + // if dbname empty, conn str will not contain Database info, + // and it is up to the server to choose which db to connect to. + dbname string +} + +pub fn (cfg Config) get_conn_str() string { + mut str := 'Driver=$cfg.driver;Server=$cfg.server;UID=$cfg.uid;PWD=$cfg.pwd' + if cfg.dbname != '' { + str += ';Database=$cfg.dbname' + } + return str +} diff --git a/v_windows/v/old/vlib/mssql/mssql.v b/v_windows/v/old/vlib/mssql/mssql.v new file mode 100644 index 0000000..a885e4b --- /dev/null +++ b/v_windows/v/old/vlib/mssql/mssql.v @@ -0,0 +1,125 @@ +module mssql + +pub struct Connection { +mut: + henv C.SQLHENV = C.SQLHENV(C.SQL_NULL_HENV) // Environment + hdbc C.SQLHDBC = C.SQLHDBC(C.SQL_NULL_HDBC) // Connection handle +pub mut: + conn_str string +} + +// connect to db +pub fn (mut conn Connection) connect(conn_str string) ?bool { + conn_str_c := unsafe { &C.SQLCHAR(conn_str.str) } + mut retcode := C.SQLRETURN(C.SQL_SUCCESS) + // Allocate environment handle + retcode = C.SQLAllocHandle(C.SQLSMALLINT(C.SQL_HANDLE_ENV), C.SQLHANDLE(C.SQL_NULL_HANDLE), + unsafe { &C.SQLHANDLE(&conn.henv) }) + check_error(retcode, 'SQLAllocHandle(SQL_HANDLE_ENV)', C.SQLHANDLE(conn.henv), C.SQLSMALLINT(C.SQL_HANDLE_ENV)) ? + + // Set the ODBC version environment attribute + retcode = C.SQLSetEnvAttr(conn.henv, C.SQLINTEGER(C.SQL_ATTR_ODBC_VERSION), &C.SQLPOINTER(C.SQL_OV_ODBC3), + C.SQLINTEGER(0)) + check_error(retcode, 'SQLSetEnvAttr(SQL_ATTR_ODBC_VERSION)', C.SQLHANDLE(conn.henv), + C.SQLSMALLINT(C.SQL_HANDLE_ENV)) ? + + // Allocate connection handle + retcode = C.SQLAllocHandle(C.SQLSMALLINT(C.SQL_HANDLE_DBC), C.SQLHANDLE(conn.henv), + unsafe { &C.SQLHANDLE(&conn.hdbc) }) + check_error(retcode, 'SQLAllocHandle(SQL_HANDLE_DBC)', C.SQLHANDLE(conn.hdbc), C.SQLSMALLINT(C.SQL_HANDLE_DBC)) ? + + // Set login timeout to 5 seconds + retcode = C.SQLSetConnectAttr(conn.hdbc, C.SQLINTEGER(C.SQL_LOGIN_TIMEOUT), C.SQLPOINTER(5), + C.SQLINTEGER(0)) + check_error(retcode, 'SQLSetConnectAttr(SQL_LOGIN_TIMEOUT)', C.SQLHANDLE(conn.hdbc), + C.SQLSMALLINT(C.SQL_HANDLE_DBC)) ? + + // Connect to data source + mut outstr := [1024]char{} + mut outstrlen := C.SQLSMALLINT(0) + retcode = C.SQLDriverConnect(conn.hdbc, C.SQLHWND(0), conn_str_c, C.SQLSMALLINT(C.SQL_NTS), + &C.SQLCHAR(&outstr[0]), C.SQLSMALLINT(sizeof(outstr)), &outstrlen, C.SQLUSMALLINT(C.SQL_DRIVER_NOPROMPT)) + check_error(retcode, 'SQLDriverConnect()', C.SQLHANDLE(conn.hdbc), C.SQLSMALLINT(C.SQL_HANDLE_DBC)) ? + conn.conn_str = conn_str + return true +} + +// close - closes the connection. +pub fn (mut conn Connection) close() { + // Connection + if conn.hdbc != C.SQLHDBC(C.SQL_NULL_HDBC) { + C.SQLDisconnect(conn.hdbc) + C.SQLFreeHandle(C.SQLSMALLINT(C.SQL_HANDLE_DBC), C.SQLHANDLE(conn.hdbc)) + conn.hdbc = C.SQLHDBC(C.SQL_NULL_HDBC) + } + // Environment + if conn.henv != C.SQLHENV(C.SQL_NULL_HENV) { + C.SQLFreeHandle(C.SQLSMALLINT(C.SQL_HANDLE_ENV), C.SQLHANDLE(conn.henv)) + conn.henv = C.SQLHENV(C.SQL_NULL_HENV) + } +} + +// query executes a sql query +pub fn (mut conn Connection) query(q string) ?Result { + mut hstmt := new_hstmt(conn.hdbc) ? + defer { + hstmt.close() + } + + hstmt.exec(q) ? + + affected := hstmt.retrieve_affected_rows() ? + + hstmt.prepare_read() ? + raw_rows := hstmt.read_rows() ? + + mut res := Result{ + rows: []Row{} + num_rows_affected: affected + } + + for rr in raw_rows { + res.rows << Row{ + vals: rr + } + } + + return res +} + +// check_error checks odbc return code and extract error string if available +fn check_error(e C.SQLRETURN, s string, h C.SQLHANDLE, t C.SQLSMALLINT) ? { + if e != C.SQLRETURN(C.SQL_SUCCESS) && e != C.SQLRETURN(C.SQL_SUCCESS_WITH_INFO) { + err_str := extract_error(s, h, t) + return error(err_str) + } +} + +// extract_error extracts error string from odbc +fn extract_error(fnName string, handle C.SQLHANDLE, tp C.SQLSMALLINT) string { + mut err_str := fnName + mut i := 0 + mut native_error := C.SQLINTEGER(0) + mut sql_state := [7]char{} + mut message_text := [256]char{} + mut text_length := C.SQLSMALLINT(0) + mut ret := C.SQLRETURN(C.SQL_SUCCESS) + + for ret == C.SQLRETURN(C.SQL_SUCCESS) { + i++ + ret = C.SQLGetDiagRec(tp, handle, C.SQLSMALLINT(i), &C.SQLCHAR(&sql_state[0]), + &native_error, &C.SQLCHAR(&message_text[0]), C.SQLSMALLINT(sizeof(message_text)), + &text_length) + + // add driver error string + if ret == C.SQLRETURN(C.SQL_SUCCESS) || ret == C.SQLRETURN(C.SQL_SUCCESS_WITH_INFO) { + unsafe { + state_str := (&sql_state[0]).vstring() + native_error_code := int(native_error) + txt_str := (&message_text[0]).vstring() + err_str += '\n\todbc=$state_str:$i:$native_error_code:$txt_str' + } + } + } + return err_str +} diff --git a/v_windows/v/old/vlib/mssql/result.v b/v_windows/v/old/vlib/mssql/result.v new file mode 100644 index 0000000..f5483a2 --- /dev/null +++ b/v_windows/v/old/vlib/mssql/result.v @@ -0,0 +1,13 @@ +module mssql + +pub struct Row { +pub mut: + vals []string +} + +pub struct Result { +pub mut: + rows []Row + // the number of rows affected by sql statement + num_rows_affected int +} diff --git a/v_windows/v/old/vlib/mssql/stmt_handle.v b/v_windows/v/old/vlib/mssql/stmt_handle.v new file mode 100644 index 0000000..8e0b792 --- /dev/null +++ b/v_windows/v/old/vlib/mssql/stmt_handle.v @@ -0,0 +1,127 @@ +module mssql + +// HStmt is handle for sql statement +struct HStmt { +mut: + // db connection reference. Owner is Connection struct. + hdbc C.SQLHDBC = C.SQLHDBC(C.SQL_NULL_HDBC) + // statement handle + hstmt C.SQLHSTMT = C.SQLHSTMT(C.SQL_NULL_HSTMT) + // fields used for computation + column_count int = -1 + // columns + buffers [][]char + // indicators for each column + indicators []C.SQLLEN +} + +// new_hstmt constructs a new statement handle +fn new_hstmt(hdbc C.SQLHDBC) ?HStmt { + mut retcode := C.SQLRETURN(C.SQL_SUCCESS) + mut hstmt := C.SQLHSTMT(C.SQL_NULL_HSTMT) + // Allocate statement handle + retcode = C.SQLAllocHandle(C.SQLSMALLINT(C.SQL_HANDLE_STMT), C.SQLHANDLE(hdbc), unsafe { &C.SQLHANDLE(&hstmt) }) + check_error(retcode, 'SQLAllocHandle(SQL_HANDLE_STMT)', C.SQLHANDLE(hstmt), C.SQLSMALLINT(C.SQL_HANDLE_STMT)) ? + + return HStmt{ + hdbc: hdbc + hstmt: hstmt + } +} + +// close the statement handle +fn (mut h HStmt) close() { + // Deallocate handle + if h.hstmt != C.SQLHSTMT(C.SQL_NULL_HSTMT) { + // check error code? + C.SQLFreeHandle(C.SQLSMALLINT(C.SQL_HANDLE_STMT), C.SQLHANDLE(h.hstmt)) + h.hstmt = C.SQLHSTMT(C.SQL_NULL_HSTMT) + } +} + +// exec executes a Sql statement. Result is stored in odbc driver, and not yet read. +fn (h HStmt) exec(sql string) ? { + retcode := C.SQLExecDirect(h.hstmt, sql.str, C.SQLINTEGER(C.SQL_NTS)) + check_error(retcode, 'SQLExecDirect()', C.SQLHANDLE(h.hstmt), C.SQLSMALLINT(C.SQL_HANDLE_STMT)) ? +} + +// retrieve_affected_rows returns number of rows affected/modified by the last operation. -1 if not applicable. +fn (h HStmt) retrieve_affected_rows() ?int { + count_ret := C.SQLLEN(0) + retcode := C.SQLRowCount(h.hstmt, &count_ret) + check_error(retcode, 'SQLRowCount()', C.SQLHANDLE(h.hstmt), C.SQLSMALLINT(C.SQL_HANDLE_STMT)) ? + return int(count_ret) +} + +fn (h HStmt) retrieve_column_count() ?int { + mut retcode := C.SQLRETURN(C.SQL_SUCCESS) + col_count_buff := C.SQLSMALLINT(0) + retcode = C.SQLNumResultCols(h.hstmt, &col_count_buff) + check_error(retcode, 'SQLNumResultCols()', C.SQLHANDLE(h.hstmt), C.SQLSMALLINT(C.SQL_HANDLE_STMT)) ? + return int(col_count_buff) +} + +// allocate buffers and bind them to drivers +fn (mut h HStmt) prepare_read() ? { + mut retcode := C.SQLRETURN(C.SQL_SUCCESS) + + column_count := h.retrieve_column_count() ? + h.column_count = column_count // remember the count because read will need it + + h.buffers = [][]char{len: h.column_count, cap: h.column_count} + h.indicators = []C.SQLLEN{len: h.column_count, cap: h.column_count} + + for i := 0; i < h.column_count; i++ { + i_col := C.SQLUSMALLINT(i + 1) // col number starts with 1 + size_ret := C.SQLLEN(0) + // find out buffer size needed to read data in this column + retcode = C.SQLColAttribute(h.hstmt, i_col, C.SQLUSMALLINT(C.SQL_DESC_LENGTH), + C.SQLPOINTER(0), C.SQLSMALLINT(0), C.SQLSMALLINT(0), &size_ret) + check_error(retcode, 'SQLColAttribute()', C.SQLHANDLE(h.hstmt), C.SQLSMALLINT(C.SQL_HANDLE_STMT)) ? + + // buffer allocation is the size + 1 to include termination char, since SQL_DESC_LENGTH does not include it. + allocate_size := size_ret + C.SQLLEN(1) + allocate_size_int := int(allocate_size) + buff := []char{len: allocate_size_int, cap: allocate_size_int} + + // bind the buffer + retcode = C.SQLBindCol(h.hstmt, C.SQLUSMALLINT(i_col), C.SQLSMALLINT(C.SQL_C_CHAR), + C.SQLPOINTER(&buff[0]), allocate_size, &h.indicators[i]) + check_error(retcode, 'SQLBindCol()', C.SQLHANDLE(h.hstmt), C.SQLSMALLINT(C.SQL_HANDLE_STMT)) ? + + // record the buffer in HStmt + h.buffers[i] = buff + } +} + +// fetch all rows +fn (h HStmt) read_rows() ?[][]string { + mut retcode := C.SQLRETURN(C.SQL_SUCCESS) + + mut res := [][]string{} + + if h.column_count <= 0 { + // there is nothing in the driver to read from + return res + } + + // Fetch and print each row of data until SQL_NO_DATA returned. + for { + mut row := []string{} + retcode = C.SQLFetch(h.hstmt) + if retcode == C.SQLRETURN(C.SQL_SUCCESS) || retcode == C.SQLRETURN(C.SQL_SUCCESS_WITH_INFO) { + // copy buffered result to res + for content in h.buffers { + row << string(content) + } + } else { + if retcode != C.SQLRETURN(C.SQL_NO_DATA) { + check_error(retcode, 'SQLFetch()', C.SQLHANDLE(h.hstmt), C.SQLSMALLINT(C.SQL_HANDLE_STMT)) ? + } else { + break + } + } + res << row + } + return res +} diff --git a/v_windows/v/old/vlib/mysql/README.md b/v_windows/v/old/vlib/mysql/README.md new file mode 100644 index 0000000..3d0ab97 --- /dev/null +++ b/v_windows/v/old/vlib/mysql/README.md @@ -0,0 +1,30 @@ +For Linux, you need to install `MySQL development` package and `pkg-config`. +For Windows, install [the installer](https://dev.mysql.com/downloads/installer/) , +then copy the `include` and `lib` folders to `\thirdparty\mysql`. + +## Basic Usage + +```v oksyntax +import mysql + +// Create connection +mut connection := mysql.Connection{ + username: 'root' + dbname: 'mysql' +} +// Connect to server +connection.connect() ? +// Change the default database +connection.select_db('db_users') ? +// Do a query +get_users_query_result := connection.query('SELECT * FROM users') ? +// Get the result as maps +for user in get_users_query_result.maps() { + // Access the name of user + println(user['name']) +} +// Free the query result +get_users_query_result.free() +// Close the connection if needed +connection.close() +``` diff --git a/v_windows/v/old/vlib/mysql/_cdefs.c.v b/v_windows/v/old/vlib/mysql/_cdefs.c.v new file mode 100644 index 0000000..23aebed --- /dev/null +++ b/v_windows/v/old/vlib/mysql/_cdefs.c.v @@ -0,0 +1,101 @@ +module mysql + +[typedef] +struct C.MYSQL { +} + +[typedef] +struct C.MYSQL_RES { +} + +[typedef] +struct C.MYSQL_FIELD { + name &byte // Name of column + org_name &byte // Original column name, if an alias + table &byte // Table of column if column was a field + org_table &byte // Org table name, if table was an alias + db &byte // Database for table + catalog &byte // Catalog for table + def &byte // Default value (set by mysql_list_fields) + length int // Width of column (create length) + max_length int // Max width for selected set + name_length u32 + org_name_length u32 + table_length u32 + org_table_length u32 + db_length u32 + catalog_length u32 + def_length u32 + flags u32 // Div flags + decimals u32 // Number of decimals in field + charsetnr u32 // Character set + @type int // Type of field. See mysql_com.h for types +} + +fn C.mysql_init(mysql &C.MYSQL) &C.MYSQL + +fn C.mysql_real_connect(mysql &C.MYSQL, host &char, user &char, passwd &char, db &char, port u32, unix_socket &char, client_flag ConnectionFlag) &C.MYSQL + +fn C.mysql_query(mysql &C.MYSQL, q &byte) int + +fn C.mysql_real_query(mysql &C.MYSQL, q &byte, len u32) int + +fn C.mysql_select_db(mysql &C.MYSQL, db &byte) int + +fn C.mysql_change_user(mysql &C.MYSQL, user &byte, password &byte, db &byte) bool + +fn C.mysql_affected_rows(mysql &C.MYSQL) u64 + +fn C.mysql_options(mysql &C.MYSQL, option int, arg voidptr) int + +fn C.mysql_get_option(mysql &C.MYSQL, option int, arg voidptr) int + +fn C.mysql_list_tables(mysql &C.MYSQL, wild &byte) &C.MYSQL_RES + +fn C.mysql_num_fields(res &C.MYSQL_RES) int + +fn C.mysql_num_rows(res &C.MYSQL_RES) u64 + +fn C.mysql_autocommit(mysql &C.MYSQL, mode bool) + +fn C.mysql_refresh(mysql &C.MYSQL, options u32) int + +fn C.mysql_reset_connection(mysql &C.MYSQL) int + +fn C.mysql_ping(mysql &C.MYSQL) int + +fn C.mysql_store_result(mysql &C.MYSQL) &C.MYSQL_RES + +fn C.mysql_fetch_row(res &C.MYSQL_RES) &&byte + +fn C.mysql_fetch_fields(res &C.MYSQL_RES) &C.MYSQL_FIELD + +fn C.mysql_free_result(res &C.MYSQL_RES) + +fn C.mysql_real_escape_string(mysql &C.MYSQL, to &byte, from &byte, len u64) u64 + +// fn C.mysql_real_escape_string_quote(mysql &C.MYSQL, to &byte, from &byte, len u64, quote byte) u64 (Don't exist in mariadb) + +fn C.mysql_close(sock &C.MYSQL) + +// INFO & VERSION +fn C.mysql_info(mysql &C.MYSQL) &byte + +fn C.mysql_get_host_info(mysql &C.MYSQL) &byte + +fn C.mysql_get_server_info(mysql &C.MYSQL) &byte + +fn C.mysql_get_server_version(mysql &C.MYSQL) u64 + +fn C.mysql_get_client_version() u64 + +fn C.mysql_get_client_info() &byte + +// DEBUG & ERROR INFO +fn C.mysql_error(mysql &C.MYSQL) &byte + +fn C.mysql_errno(mysql &C.MYSQL) int + +fn C.mysql_dump_debug_info(mysql &C.MYSQL) int + +fn C.mysql_debug(debug &byte) diff --git a/v_windows/v/old/vlib/mysql/_cdefs_nix.c.v b/v_windows/v/old/vlib/mysql/_cdefs_nix.c.v new file mode 100644 index 0000000..1462e89 --- /dev/null +++ b/v_windows/v/old/vlib/mysql/_cdefs_nix.c.v @@ -0,0 +1,11 @@ +module mysql + +// Need to check if mysqlclient is not there and use mariadb as alternative because newer system doesn't support mysql 8.0 as default + +$if $pkgconfig('mysqlclient') { + #pkgconfig mysqlclient +} $else { + #pkgconfig mariadb +} + +#include # Please install the mysqlclient development headers diff --git a/v_windows/v/old/vlib/mysql/_cdefs_windows.c.v b/v_windows/v/old/vlib/mysql/_cdefs_windows.c.v new file mode 100644 index 0000000..9e18696 --- /dev/null +++ b/v_windows/v/old/vlib/mysql/_cdefs_windows.c.v @@ -0,0 +1,5 @@ +module mysql + +#flag windows -I@VEXEROOT/thirdparty/mysql/include +#flag windows @VEXEROOT/thirdparty/mysql/lib/libmysql.dll +#include # Please install https://dev.mysql.com/downloads/installer/ , then put the include/ and lib/ folders in thirdparty/mysql diff --git a/v_windows/v/old/vlib/mysql/consts.v b/v_windows/v/old/vlib/mysql/consts.v new file mode 100644 index 0000000..2e9962a --- /dev/null +++ b/v_windows/v/old/vlib/mysql/consts.v @@ -0,0 +1,13 @@ +module mysql + +// MYSQL REFRESH FLAGS +pub const ( + refresh_grant = u32(C.REFRESH_GRANT) + refresh_log = u32(C.REFRESH_LOG) + refresh_tables = u32(C.REFRESH_TABLES) + refresh_hosts = u32(C.REFRESH_HOSTS) + refresh_status = u32(C.REFRESH_STATUS) + refresh_threads = u32(C.REFRESH_THREADS) + refresh_slave = u32(C.REFRESH_SLAVE) + refresh_master = u32(C.REFRESH_MASTER) +) diff --git a/v_windows/v/old/vlib/mysql/enums.v b/v_windows/v/old/vlib/mysql/enums.v new file mode 100644 index 0000000..e2bfef5 --- /dev/null +++ b/v_windows/v/old/vlib/mysql/enums.v @@ -0,0 +1,78 @@ +module mysql + +pub enum FieldType { + type_decimal + type_tiny + type_short + type_long + type_float + type_double + type_null + type_timestamp + type_longlong + type_int24 + type_date + type_time + type_datetime + type_year + type_newdate + type_varchar + type_bit + type_timestamp2 + type_datetime2 + type_time2 + type_json = 245 + type_newdecimal + type_enum + type_set + type_tiny_blob + type_medium_blob + type_long_blob + type_blob + type_var_string + type_string + type_geometry +} + +pub fn (f FieldType) str() string { + return match f { + .type_decimal { 'decimal' } + .type_tiny { 'tiny' } + .type_short { 'short' } + .type_long { 'long' } + .type_float { 'float' } + .type_double { 'double' } + .type_null { 'null' } + .type_timestamp { 'timestamp' } + .type_longlong { 'longlong' } + .type_int24 { 'int24' } + .type_date { 'date' } + .type_time { 'time' } + .type_datetime { 'datetime' } + .type_year { 'year' } + .type_newdate { 'newdate' } + .type_varchar { 'varchar' } + .type_bit { 'bit' } + .type_timestamp2 { 'timestamp2' } + .type_datetime2 { 'datetime2' } + .type_time2 { 'time2' } + .type_json { 'json' } + .type_newdecimal { 'newdecimal' } + .type_enum { 'enum' } + .type_set { 'set' } + .type_tiny_blob { 'tiny_blob' } + .type_medium_blob { 'medium_blob' } + .type_long_blob { 'long_blob' } + .type_blob { 'blob' } + .type_var_string { 'var_string' } + .type_string { 'string' } + .type_geometry { 'geometry' } + } +} + +pub fn (f FieldType) get_len() u32 { + return match f { + .type_blob { 262140 } + else { 0 } + } +} diff --git a/v_windows/v/old/vlib/mysql/mysql.v b/v_windows/v/old/vlib/mysql/mysql.v new file mode 100644 index 0000000..90e7008 --- /dev/null +++ b/v_windows/v/old/vlib/mysql/mysql.v @@ -0,0 +1,239 @@ +module mysql + +// Values for the capabilities flag bitmask used by the MySQL protocol. +// See more on https://dev.mysql.com/doc/dev/mysql-server/latest/group__group__cs__capabilities__flags.html#details +pub enum ConnectionFlag { + // CAN_HANDLE_EXPIRED_PASSWORDS = C.CAN_HANDLE_EXPIRED_PASSWORDS + client_compress = C.CLIENT_COMPRESS + client_found_rows = C.CLIENT_FOUND_ROWS + client_ignore_sigpipe = C.CLIENT_IGNORE_SIGPIPE + client_ignore_space = C.CLIENT_IGNORE_SPACE + client_interactive = C.CLIENT_INTERACTIVE + client_local_files = C.CLIENT_LOCAL_FILES + client_multi_results = C.CLIENT_MULTI_RESULTS + client_multi_statements = C.CLIENT_MULTI_STATEMENTS + client_no_schema = C.CLIENT_NO_SCHEMA + client_odbc = C.CLIENT_ODBC + // client_optional_resultset_metadata = C.CLIENT_OPTIONAL_RESULTSET_METADATA + client_ssl = C.CLIENT_SSL + client_remember_options = C.CLIENT_REMEMBER_OPTIONS +} + +struct SQLError { + msg string + code int +} + +// TODO: Documentation +pub struct Connection { +mut: + conn &C.MYSQL = C.mysql_init(0) +pub mut: + host string = '127.0.0.1' + port u32 = 3306 + username string + password string + dbname string + flag ConnectionFlag +} + +// connect - create a new connection to the MySQL server. +pub fn (mut conn Connection) connect() ?bool { + instance := C.mysql_init(conn.conn) + conn.conn = C.mysql_real_connect(instance, conn.host.str, conn.username.str, conn.password.str, + conn.dbname.str, conn.port, 0, conn.flag) + if isnil(conn.conn) { + return error_with_code(get_error_msg(instance), get_errno(instance)) + } + return true +} + +// query - make an SQL query and receive the results. +// `query()` cannot be used for statements that contain binary data; +// Use `real_query()` instead. +pub fn (conn Connection) query(q string) ?Result { + if C.mysql_query(conn.conn, q.str) != 0 { + return error_with_code(get_error_msg(conn.conn), get_errno(conn.conn)) + } + res := C.mysql_store_result(conn.conn) + return Result{res} +} + +// real_query - make an SQL query and receive the results. +// `real_query()` can be used for statements containing binary data. +// (Binary data may contain the `\0` character, which `query()` +// interprets as the end of the statement string). In addition, +// `real_query()` is faster than `query()`. +pub fn (mut conn Connection) real_query(q string) ?Result { + if C.mysql_real_query(conn.conn, q.str, q.len) != 0 { + return error_with_code(get_error_msg(conn.conn), get_errno(conn.conn)) + } + res := C.mysql_store_result(conn.conn) + return Result{res} +} + +// select_db - change the default database for database queries. +pub fn (mut conn Connection) select_db(dbname string) ?bool { + if C.mysql_select_db(conn.conn, dbname.str) != 0 { + return error_with_code(get_error_msg(conn.conn), get_errno(conn.conn)) + } + return true +} + +// change_user - change the mysql user for the connection. +// Passing an empty string for the `dbname` parameter, resultsg in only changing +// the user and not changing the default database for the connection. +pub fn (mut conn Connection) change_user(username string, password string, dbname string) ?bool { + mut ret := true + if dbname != '' { + ret = C.mysql_change_user(conn.conn, username.str, password.str, dbname.str) + } else { + ret = C.mysql_change_user(conn.conn, username.str, password.str, 0) + } + if !ret { + return error_with_code(get_error_msg(conn.conn), get_errno(conn.conn)) + } + return ret +} + +// affected_rows - return the number of rows changed/deleted/inserted +// by the last `UPDATE`, `DELETE`, or `INSERT` query. +pub fn (conn &Connection) affected_rows() u64 { + return C.mysql_affected_rows(conn.conn) +} + +// autocommit - turns on/off the auto-committing mode for the connection. +// When it is on, then each query is commited right away. +pub fn (mut conn Connection) autocommit(mode bool) { + C.mysql_autocommit(conn.conn, mode) +} + +// tables - returns a list of the names of the tables in the current database, +// that match the simple regular expression specified by the `wildcard` parameter. +// The `wildcard` parameter may contain the wildcard characters `%` or `_`. +// If an empty string is passed, it will return all tables. +// Calling `tables()` is similar to executing query `SHOW TABLES [LIKE wildcard]`. +pub fn (conn &Connection) tables(wildcard string) ?[]string { + cres := C.mysql_list_tables(conn.conn, wildcard.str) + if isnil(cres) { + return error_with_code(get_error_msg(conn.conn), get_errno(conn.conn)) + } + res := Result{cres} + mut tables := []string{} + for row in res.rows() { + tables << row.vals[0] + } + return tables +} + +// escape_string - creates a legal SQL string for use in an SQL statement. +// The `s` argument is encoded to produce an escaped SQL string, +// taking into account the current character set of the connection. +pub fn (conn &Connection) escape_string(s string) string { + unsafe { + to := malloc_noscan(2 * s.len + 1) + C.mysql_real_escape_string(conn.conn, to, s.str, s.len) + return to.vstring() + } +} + +// set_option - sets extra connect options that affect the behavior of +// a connection. This function may be called multiple times to set several +// options. To retrieve the current values for an option, use `get_option()`. +pub fn (mut conn Connection) set_option(option_type int, val voidptr) { + C.mysql_options(conn.conn, option_type, val) +} + +// get_option - return the value of an option, settable by `set_option`. +// https://dev.mysql.com/doc/c-api/5.7/en/mysql-get-option.html +pub fn (conn &Connection) get_option(option_type int) ?voidptr { + ret := voidptr(0) + if C.mysql_get_option(conn.conn, option_type, &ret) != 0 { + return error_with_code(get_error_msg(conn.conn), get_errno(conn.conn)) + } + return ret +} + +// refresh - flush the tables or caches, or resets replication server +// information. The connected user must have the `RELOAD` privilege. +pub fn (mut conn Connection) refresh(options u32) ?bool { + if C.mysql_refresh(conn.conn, options) != 0 { + return error_with_code(get_error_msg(conn.conn), get_errno(conn.conn)) + } + return true +} + +// reset - resets the connection, and clear the session state. +pub fn (mut conn Connection) reset() ?bool { + if C.mysql_reset_connection(conn.conn) != 0 { + return error_with_code(get_error_msg(conn.conn), get_errno(conn.conn)) + } + return true +} + +// ping - pings a server connection, or tries to reconnect if the connection +// has gone down. +pub fn (mut conn Connection) ping() ?bool { + if C.mysql_ping(conn.conn) != 0 { + return error_with_code(get_error_msg(conn.conn), get_errno(conn.conn)) + } + return true +} + +// close - closes the connection. +pub fn (mut conn Connection) close() { + C.mysql_close(conn.conn) +} + +// info - returns information about the most recently executed query. +// See more on https://dev.mysql.com/doc/c-api/8.0/en/mysql-info.html +pub fn (conn &Connection) info() string { + return resolve_nil_str(C.mysql_info(conn.conn)) +} + +// get_host_info - returns a string describing the type of connection in use, +// including the server host name. +pub fn (conn &Connection) get_host_info() string { + return unsafe { C.mysql_get_host_info(conn.conn).vstring() } +} + +// get_server_info - returns a string representing the MySQL server version. +// For example, `8.0.24`. +pub fn (conn &Connection) get_server_info() string { + return unsafe { C.mysql_get_server_info(conn.conn).vstring() } +} + +// get_server_version - returns an integer, representing the MySQL server +// version. The value has the format `XYYZZ` where `X` is the major version, +// `YY` is the release level (or minor version), and `ZZ` is the sub-version +// within the release level. For example, `8.0.24` is returned as `80024`. +pub fn (conn &Connection) get_server_version() u64 { + return C.mysql_get_server_version(conn.conn) +} + +// dump_debug_info - instructs the server to write debugging information +// to the error log. The connected user must have the `SUPER` privilege. +pub fn (mut conn Connection) dump_debug_info() ?bool { + if C.mysql_dump_debug_info(conn.conn) != 0 { + return error_with_code(get_error_msg(conn.conn), get_errno(conn.conn)) + } + return true +} + +// get_client_info - returns client version information as a string. +pub fn get_client_info() string { + return unsafe { C.mysql_get_client_info().vstring() } +} + +// get_client_version - returns the client version information as an integer. +pub fn get_client_version() u64 { + return C.mysql_get_client_version() +} + +// debug - does a `DBUG_PUSH` with the given string. +// `debug()` uses the Fred Fish debug library. +// To use this function, you must compile the client library to support debugging. +// See https://dev.mysql.com/doc/c-api/8.0/en/mysql-debug.html +pub fn debug(debug string) { + C.mysql_debug(debug.str) +} diff --git a/v_windows/v/old/vlib/mysql/mysql_orm_test.v b/v_windows/v/old/vlib/mysql/mysql_orm_test.v new file mode 100644 index 0000000..2acb182 --- /dev/null +++ b/v_windows/v/old/vlib/mysql/mysql_orm_test.v @@ -0,0 +1,77 @@ +import orm +import mysql + +fn test_mysql_orm() { + mut mdb := mysql.Connection{ + host: 'localhost' + port: 3306 + username: 'root' + password: '' + dbname: 'mysql' + } + mdb.connect() or { panic(err) } + db := orm.Connection(mdb) + db.create('Test', [ + orm.TableField{ + name: 'id' + typ: 7 + attrs: [ + StructAttribute{ + name: 'primary' + }, + StructAttribute{ + name: 'sql' + has_arg: true + kind: .plain + arg: 'serial' + }, + ] + }, + orm.TableField{ + name: 'name' + typ: 18 + attrs: [] + }, + orm.TableField{ + name: 'age' + typ: 7 + }, + ]) or { panic(err) } + + db.insert('Test', orm.QueryData{ + fields: ['name', 'age'] + data: [orm.string_to_primitive('Louis'), orm.int_to_primitive(101)] + }) or { panic(err) } + + res := db.@select(orm.SelectConfig{ + table: 'Test' + has_where: true + fields: ['id', 'name', 'age'] + types: [7, 18, 8] + }, orm.QueryData{}, orm.QueryData{ + fields: ['name', 'age'] + data: [orm.Primitive('Louis'), i64(101)] + types: [18, 8] + is_and: [true, true] + kinds: [.eq, .eq] + }) or { panic(err) } + + id := res[0][0] + name := res[0][1] + age := res[0][2] + + assert id is int + if id is int { + assert id == 1 + } + + assert name is string + if name is string { + assert name == 'Louis' + } + + assert age is i64 + if age is i64 { + assert age == 101 + } +} diff --git a/v_windows/v/old/vlib/mysql/orm.v b/v_windows/v/old/vlib/mysql/orm.v new file mode 100644 index 0000000..c68be63 --- /dev/null +++ b/v_windows/v/old/vlib/mysql/orm.v @@ -0,0 +1,296 @@ +module mysql + +import orm +import time + +type Prims = byte | f32 | f64 | i16 | i64 | i8 | int | string | u16 | u32 | u64 + +// sql expr + +pub fn (db Connection) @select(config orm.SelectConfig, data orm.QueryData, where orm.QueryData) ?[][]orm.Primitive { + query := orm.orm_select_gen(config, '`', false, '?', 0, where) + mut ret := [][]orm.Primitive{} + mut stmt := db.init_stmt(query) + stmt.prepare() ? + + mysql_stmt_binder(mut stmt, where) ? + mysql_stmt_binder(mut stmt, data) ? + if data.data.len > 0 || where.data.len > 0 { + stmt.bind_params() ? + } + + mut status := stmt.execute() ? + num_fields := stmt.get_field_count() + metadata := stmt.gen_metadata() + fields := stmt.fetch_fields(metadata) + + mut dataptr := []Prims{} + + for i in 0 .. num_fields { + f := unsafe { fields[i] } + match FieldType(f.@type) { + .type_tiny { + dataptr << byte(0) + } + .type_short { + dataptr << u16(0) + } + .type_long { + dataptr << u32(0) + } + .type_longlong { + dataptr << u64(0) + } + .type_float { + dataptr << f32(0) + } + .type_double { + dataptr << f64(0) + } + .type_string { + dataptr << '' + } + else { + dataptr << byte(0) + } + } + } + + mut vptr := []&char{} + + for d in dataptr { + vptr << d.get_data_ptr() + } + + unsafe { dataptr.free() } + + lens := []u32{len: int(num_fields), init: 0} + stmt.bind_res(fields, vptr, lens, num_fields) + stmt.bind_result_buffer() ? + stmt.store_result() ? + + mut row := 0 + + for { + status = stmt.fetch_stmt() ? + + if status == 1 || status == 100 { + break + } + row++ + data_list := buffer_to_primitive(vptr, config.types) ? + ret << data_list + } + + stmt.close() ? + + return ret +} + +// sql stmt + +pub fn (db Connection) insert(table string, data orm.QueryData) ? { + query := orm.orm_stmt_gen(table, '`', .insert, false, '?', 1, data, orm.QueryData{}) + mysql_stmt_worker(db, query, data, orm.QueryData{}) ? +} + +pub fn (db Connection) update(table string, data orm.QueryData, where orm.QueryData) ? { + query := orm.orm_stmt_gen(table, '`', .update, false, '?', 1, data, where) + mysql_stmt_worker(db, query, data, where) ? +} + +pub fn (db Connection) delete(table string, where orm.QueryData) ? { + query := orm.orm_stmt_gen(table, '`', .delete, false, '?', 1, orm.QueryData{}, where) + mysql_stmt_worker(db, query, orm.QueryData{}, where) ? +} + +pub fn (db Connection) last_id() orm.Primitive { + query := 'SELECT last_insert_rowid();' + id := db.query(query) or { + Result{ + result: 0 + } + } + return orm.Primitive(id.rows()[0].vals[0].int()) +} + +// table +pub fn (db Connection) create(table string, fields []orm.TableField) ? { + query := orm.orm_table_gen(table, '`', false, 0, fields, mysql_type_from_v, false) or { + return err + } + mysql_stmt_worker(db, query, orm.QueryData{}, orm.QueryData{}) ? +} + +pub fn (db Connection) drop(table string) ? { + query := 'DROP TABLE `$table`;' + mysql_stmt_worker(db, query, orm.QueryData{}, orm.QueryData{}) ? +} + +fn mysql_stmt_worker(db Connection, query string, data orm.QueryData, where orm.QueryData) ? { + mut stmt := db.init_stmt(query) + stmt.prepare() ? + mysql_stmt_binder(mut stmt, data) ? + mysql_stmt_binder(mut stmt, where) ? + if data.data.len > 0 || where.data.len > 0 { + stmt.bind_params() ? + } + stmt.execute() ? + stmt.close() ? +} + +fn mysql_stmt_binder(mut stmt Stmt, d orm.QueryData) ? { + for data in d.data { + stmt_binder_match(mut stmt, data) + } +} + +fn stmt_binder_match(mut stmt Stmt, data orm.Primitive) { + match data { + bool { + stmt.bind_bool(&data) + } + i8 { + stmt.bind_i8(&data) + } + i16 { + stmt.bind_i16(&data) + } + int { + stmt.bind_int(&data) + } + i64 { + stmt.bind_i64(&data) + } + byte { + stmt.bind_byte(&data) + } + u16 { + stmt.bind_u16(&data) + } + u32 { + stmt.bind_u32(&data) + } + u64 { + stmt.bind_u64(&data) + } + f32 { + stmt.bind_f32(unsafe { &f32(&data) }) + } + f64 { + stmt.bind_f64(unsafe { &f64(&data) }) + } + string { + stmt.bind_text(data) + } + time.Time { + stmt.bind_int(&int(data.unix)) + } + orm.InfixType { + stmt_binder_match(mut stmt, data.right) + } + } +} + +fn buffer_to_primitive(data_list []&char, types []int) ?[]orm.Primitive { + mut res := []orm.Primitive{} + + for i, data in data_list { + mut primitive := orm.Primitive(0) + match types[i] { + 5 { + primitive = *(&i8(data)) + } + 6 { + primitive = *(&i16(data)) + } + 7, -1 { + primitive = *(&int(data)) + } + 8 { + primitive = *(&i64(data)) + } + 9 { + primitive = *(&byte(data)) + } + 10 { + primitive = *(&u16(data)) + } + 11 { + primitive = *(&u32(data)) + } + 12 { + primitive = *(&u64(data)) + } + 13 { + primitive = *(&f32(data)) + } + 14 { + primitive = *(&f64(data)) + } + 15 { + primitive = *(&bool(data)) + } + orm.string { + primitive = unsafe { cstring_to_vstring(&char(data)) } + } + orm.time { + timestamp := *(&int(data)) + primitive = time.unix(timestamp) + } + else { + return error('Unknown type ${types[i]}') + } + } + res << primitive + } + + return res +} + +fn mysql_type_from_v(typ int) ?string { + str := match typ { + 5, 9, 16 { + 'TINYINT' + } + 6, 10 { + 'SMALLINT' + } + 7, 11 { + 'INT' + } + 8, 12 { + 'BIGINT' + } + 13 { + 'FLOAT' + } + 14 { + 'DOUBLE' + } + orm.string { + 'TEXT' + } + -1 { + 'SERIAL' + } + else { + '' + } + } + if str == '' { + return error('Unknown type $typ') + } + return str +} + +fn (p Prims) get_data_ptr() &char { + return match p { + string { + p.str + } + else { + &char(&p) + } + } +} diff --git a/v_windows/v/old/vlib/mysql/result.v b/v_windows/v/old/vlib/mysql/result.v new file mode 100644 index 0000000..e9e1909 --- /dev/null +++ b/v_windows/v/old/vlib/mysql/result.v @@ -0,0 +1,153 @@ +module mysql + +pub struct Result { + result &C.MYSQL_RES +} + +pub struct Row { +pub mut: + vals []string +} + +pub struct Field { + name string + org_name string + table string + org_table string + db string + catalog string + def string + length int + max_length int + name_length u32 + org_name_length u32 + table_length u32 + org_table_length u32 + db_length u32 + catalog_length u32 + def_length u32 + flags u32 + decimals u32 + charsetnr u32 + type_ FieldType +} + +// fetch_row - fetches the next row from a result. +pub fn (r Result) fetch_row() &&byte { + return C.mysql_fetch_row(r.result) +} + +// n_rows - returns the number of rows from a result. +pub fn (r Result) n_rows() u64 { + return C.mysql_num_rows(r.result) +} + +// n_fields - returns the number of columns from a result. +pub fn (r Result) n_fields() int { + return C.mysql_num_fields(r.result) +} + +// rows - returns array of rows, each containing an array of values, +// one for each column. +pub fn (r Result) rows() []Row { + mut rows := []Row{} + nr_cols := r.n_fields() + for rr := r.fetch_row(); rr; rr = r.fetch_row() { + mut row := Row{} + for i in 0 .. nr_cols { + if unsafe { rr[i] == 0 } { + row.vals << '' + } else { + row.vals << mystring(unsafe { &byte(rr[i]) }) + } + } + rows << row + } + return rows +} + +// maps - returns an array of maps, each containing a set of +// field name: field value pairs. +pub fn (r Result) maps() []map[string]string { + mut array_map := []map[string]string{} + rows := r.rows() + fields := r.fields() + for i in 0 .. rows.len { + mut map_val := map[string]string{} + for j in 0 .. fields.len { + map_val[fields[j].name] = rows[i].vals[j] + } + array_map << map_val + } + return array_map +} + +// fields - returns an array of fields/columns. +// The definitions apply primarily for columns of results, +// such as those produced by `SELECT` statements. +pub fn (r Result) fields() []Field { + mut fields := []Field{} + nr_cols := r.n_fields() + orig_fields := C.mysql_fetch_fields(r.result) + for i in 0 .. nr_cols { + unsafe { + fields << Field{ + name: mystring(orig_fields[i].name) + org_name: mystring(orig_fields[i].org_name) + table: mystring(orig_fields[i].table) + org_table: mystring(orig_fields[i].org_table) + db: mystring(orig_fields[i].db) + catalog: mystring(orig_fields[i].catalog) + def: resolve_nil_str(orig_fields[i].def) + length: orig_fields.length + max_length: orig_fields.max_length + name_length: orig_fields.name_length + org_name_length: orig_fields.org_name_length + table_length: orig_fields.table_length + org_table_length: orig_fields.org_table_length + db_length: orig_fields.db_length + catalog_length: orig_fields.catalog_length + def_length: orig_fields.def_length + flags: orig_fields.flags + decimals: orig_fields.decimals + charsetnr: orig_fields.charsetnr + type_: FieldType(orig_fields.@type) + } + } + } + return fields +} + +// str - serializes the field +pub fn (f Field) str() string { + return ' +{ + name: "$f.name" + org_name: "$f.org_name" + table: "$f.table" + org_table: "$f.org_table" + db: "$f.db" + catalog: "$f.catalog" + def: "$f.def" + length: $f.length + max_length: $f.max_length + name_length: $f.name_length + org_name_length: $f.org_name_length + table_length: $f.table_length + org_table_length: $f.org_table_length + db_length: $f.db_length + catalog_length: $f.catalog_length + def_length: $f.def_length + flags: $f.flags + decimals: $f.decimals + charsetnr: $f.charsetnr + type: $f.type_.str() +} +' +} + +// free - frees the memory used by a result +[unsafe] +pub fn (r &Result) free() { + C.mysql_free_result(r.result) +} diff --git a/v_windows/v/old/vlib/mysql/stmt.c.v b/v_windows/v/old/vlib/mysql/stmt.c.v new file mode 100644 index 0000000..ee91746 --- /dev/null +++ b/v_windows/v/old/vlib/mysql/stmt.c.v @@ -0,0 +1,233 @@ +module mysql + +[typedef] +struct C.MYSQL_STMT { + mysql &C.MYSQL + stmt_id u32 +} + +[typedef] +struct C.MYSQL_BIND { +mut: + buffer_type int + buffer voidptr + buffer_length u32 + length &u32 +} + +const ( + mysql_type_decimal = C.MYSQL_TYPE_DECIMAL + mysql_type_tiny = C.MYSQL_TYPE_TINY + mysql_type_short = C.MYSQL_TYPE_SHORT + mysql_type_long = C.MYSQL_TYPE_LONG + mysql_type_float = C.MYSQL_TYPE_FLOAT + mysql_type_double = C.MYSQL_TYPE_DOUBLE + mysql_type_null = C.MYSQL_TYPE_NULL + mysql_type_timestamp = C.MYSQL_TYPE_TIMESTAMP + mysql_type_longlong = C.MYSQL_TYPE_LONGLONG + mysql_type_int24 = C.MYSQL_TYPE_INT24 + mysql_type_date = C.MYSQL_TYPE_DATE + mysql_type_time = C.MYSQL_TYPE_TIME + mysql_type_datetime = C.MYSQL_TYPE_DATETIME + mysql_type_year = C.MYSQL_TYPE_YEAR + mysql_type_varchar = C.MYSQL_TYPE_VARCHAR + mysql_type_bit = C.MYSQL_TYPE_BIT + mysql_type_timestamp22 = C.MYSQL_TYPE_TIMESTAMP + mysql_type_json = C.MYSQL_TYPE_JSON + mysql_type_newdecimal = C.MYSQL_TYPE_NEWDECIMAL + mysql_type_enum = C.MYSQL_TYPE_ENUM + mysql_type_set = C.MYSQL_TYPE_SET + mysql_type_tiny_blob = C.MYSQL_TYPE_TINY_BLOB + mysql_type_medium_blob = C.MYSQL_TYPE_MEDIUM_BLOB + mysql_type_long_blob = C.MYSQL_TYPE_LONG_BLOB + mysql_type_blob = C.MYSQL_TYPE_BLOB + mysql_type_var_string = C.MYSQL_TYPE_VAR_STRING + mysql_type_string = C.MYSQL_TYPE_STRING + mysql_type_geometry = C.MYSQL_TYPE_GEOMETRY + mysql_no_data = C.MYSQL_NO_DATA +) + +fn C.mysql_stmt_init(&C.MYSQL) &C.MYSQL_STMT +fn C.mysql_stmt_prepare(&C.MYSQL_STMT, &char, u32) int +fn C.mysql_stmt_bind_param(&C.MYSQL_STMT, &C.MYSQL_BIND) bool +fn C.mysql_stmt_execute(&C.MYSQL_STMT) int +fn C.mysql_stmt_close(&C.MYSQL_STMT) bool +fn C.mysql_stmt_free_result(&C.MYSQL_STMT) bool +fn C.mysql_stmt_error(&C.MYSQL_STMT) &char +fn C.mysql_stmt_result_metadata(&C.MYSQL_STMT) &C.MYSQL_RES + +fn C.mysql_stmt_field_count(&C.MYSQL_STMT) u16 +fn C.mysql_stmt_bind_result(&C.MYSQL_STMT, &C.MYSQL_BIND) bool +fn C.mysql_stmt_fetch(&C.MYSQL_STMT) int +fn C.mysql_stmt_next_result(&C.MYSQL_STMT) int +fn C.mysql_stmt_store_result(&C.MYSQL_STMT) int + +struct Stmt { + stmt &C.MYSQL_STMT = &C.MYSQL_STMT(0) + query string +mut: + binds []C.MYSQL_BIND + res []C.MYSQL_BIND +} + +pub fn (db Connection) init_stmt(query string) Stmt { + return Stmt{ + stmt: C.mysql_stmt_init(db.conn) + query: query + binds: []C.MYSQL_BIND{} + } +} + +pub fn (stmt Stmt) prepare() ? { + res := C.mysql_stmt_prepare(stmt.stmt, stmt.query.str, stmt.query.len) + if res != 0 && stmt.get_error_msg() != '' { + return stmt.error(res) + } +} + +pub fn (stmt Stmt) bind_params() ? { + res := C.mysql_stmt_bind_param(stmt.stmt, &C.MYSQL_BIND(stmt.binds.data)) + if res && stmt.get_error_msg() != '' { + return stmt.error(1) + } +} + +pub fn (stmt Stmt) execute() ?int { + res := C.mysql_stmt_execute(stmt.stmt) + if res != 0 && stmt.get_error_msg() != '' { + return stmt.error(res) + } + return res +} + +pub fn (stmt Stmt) next() ?int { + res := C.mysql_stmt_next_result(stmt.stmt) + if res > 0 && stmt.get_error_msg() != '' { + return stmt.error(res) + } + return res +} + +pub fn (stmt Stmt) gen_metadata() &C.MYSQL_RES { + return C.mysql_stmt_result_metadata(stmt.stmt) +} + +pub fn (stmt Stmt) fetch_fields(res &C.MYSQL_RES) &C.MYSQL_FIELD { + return C.mysql_fetch_fields(res) +} + +pub fn (stmt Stmt) fetch_stmt() ?int { + res := C.mysql_stmt_fetch(stmt.stmt) + if res !in [0, 100] && stmt.get_error_msg() != '' { + return stmt.error(res) + } + return res +} + +pub fn (stmt Stmt) close() ? { + if !C.mysql_stmt_close(stmt.stmt) && stmt.get_error_msg() != '' { + return stmt.error(1) + } + if !C.mysql_stmt_free_result(stmt.stmt) && stmt.get_error_msg() != '' { + return stmt.error(1) + } +} + +fn (stmt Stmt) get_error_msg() string { + return unsafe { cstring_to_vstring(&char(C.mysql_stmt_error(stmt.stmt))) } +} + +pub fn (stmt Stmt) error(code int) IError { + msg := stmt.get_error_msg() + return IError(&SQLError{ + msg: '$msg ($code) ($stmt.query)' + code: code + }) +} + +fn (stmt Stmt) get_field_count() u16 { + return C.mysql_stmt_field_count(stmt.stmt) +} + +pub fn (mut stmt Stmt) bind_bool(b &bool) { + stmt.bind(mysql.mysql_type_tiny, b, 0) +} + +pub fn (mut stmt Stmt) bind_byte(b &byte) { + stmt.bind(mysql.mysql_type_tiny, b, 0) +} + +pub fn (mut stmt Stmt) bind_i8(b &i8) { + stmt.bind(mysql.mysql_type_tiny, b, 0) +} + +pub fn (mut stmt Stmt) bind_i16(b &i16) { + stmt.bind(mysql.mysql_type_short, b, 0) +} + +pub fn (mut stmt Stmt) bind_u16(b &u16) { + stmt.bind(mysql.mysql_type_short, b, 0) +} + +pub fn (mut stmt Stmt) bind_int(b &int) { + stmt.bind(mysql.mysql_type_long, b, 0) +} + +pub fn (mut stmt Stmt) bind_u32(b &u32) { + stmt.bind(mysql.mysql_type_long, b, 0) +} + +pub fn (mut stmt Stmt) bind_i64(b &i64) { + stmt.bind(mysql.mysql_type_longlong, b, 0) +} + +pub fn (mut stmt Stmt) bind_u64(b &u64) { + stmt.bind(mysql.mysql_type_longlong, b, 0) +} + +pub fn (mut stmt Stmt) bind_f32(b &f32) { + stmt.bind(mysql.mysql_type_float, b, 0) +} + +pub fn (mut stmt Stmt) bind_f64(b &f64) { + stmt.bind(mysql.mysql_type_double, b, 0) +} + +pub fn (mut stmt Stmt) bind_text(b string) { + stmt.bind(mysql.mysql_type_string, b.str, u32(b.len)) +} + +pub fn (mut stmt Stmt) bind(typ int, buffer voidptr, buf_len u32) { + stmt.binds << C.MYSQL_BIND{ + buffer_type: typ + buffer: buffer + buffer_length: buf_len + length: 0 + } +} + +pub fn (mut stmt Stmt) bind_res(fields &C.MYSQL_FIELD, dataptr []&char, lens []u32, num_fields int) { + for i in 0 .. num_fields { + len := FieldType(unsafe { fields[i].@type }).get_len() + stmt.res << C.MYSQL_BIND{ + buffer_type: unsafe { fields[i].@type } + buffer: dataptr[i] + length: &lens[i] + buffer_length: &len + } + } +} + +pub fn (mut stmt Stmt) bind_result_buffer() ? { + res := C.mysql_stmt_bind_result(stmt.stmt, &C.MYSQL_BIND(stmt.res.data)) + if res && stmt.get_error_msg() != '' { + return stmt.error(1) + } +} + +pub fn (mut stmt Stmt) store_result() ? { + res := C.mysql_stmt_store_result(stmt.stmt) + if res != 0 && stmt.get_error_msg() != '' { + return stmt.error(res) + } +} diff --git a/v_windows/v/old/vlib/mysql/utils.v b/v_windows/v/old/vlib/mysql/utils.v new file mode 100644 index 0000000..2d63327 --- /dev/null +++ b/v_windows/v/old/vlib/mysql/utils.v @@ -0,0 +1,26 @@ +module mysql + +// get_error_msg - returns error message from MySQL instance. +fn get_error_msg(conn &C.MYSQL) string { + return unsafe { C.mysql_error(conn).vstring() } +} + +// get_errno - returns error number from MySQL instance. +fn get_errno(conn &C.MYSQL) int { + return C.mysql_errno(conn) +} + +// resolve_nil_str - returns an empty string if passed value is a nil pointer. +fn resolve_nil_str(ptr &byte) string { + if isnil(ptr) { + return '' + } + return unsafe { ptr.vstring() } +} + +[inline] +fn mystring(b &byte) string { + unsafe { + return b.vstring() + } +} diff --git a/v_windows/v/old/vlib/net/aasocket.c.v b/v_windows/v/old/vlib/net/aasocket.c.v new file mode 100644 index 0000000..60418c3 --- /dev/null +++ b/v_windows/v/old/vlib/net/aasocket.c.v @@ -0,0 +1,104 @@ +module net + +$if windows { + // This is mainly here for tcc on windows + // which apparently doesnt have this definition + #include "@VROOT/vlib/net/ipv6_v6only.h" +} + +// Select represents a select operation +enum Select { + read + write + except +} + +// SocketType are the available sockets +pub enum SocketType { + udp = C.SOCK_DGRAM + tcp = C.SOCK_STREAM + seqpacket = C.SOCK_SEQPACKET +} + +// AddrFamily are the available address families +pub enum AddrFamily { + unix = C.AF_UNIX + ip = C.AF_INET + ip6 = C.AF_INET6 + unspec = C.AF_UNSPEC +} + +fn C.socket(domain AddrFamily, typ SocketType, protocol int) int + +// fn C.setsockopt(sockfd int, level int, optname int, optval voidptr, optlen C.socklen_t) int +fn C.setsockopt(sockfd int, level int, optname int, optval voidptr, optlen u32) int + +fn C.htonl(hostlong u32) int + +fn C.htons(netshort u16) int + +// fn C.bind(sockfd int, addr &C.sockaddr, addrlen C.socklen_t) int +// use voidptr for arg 2 becasue sockaddr is a generic descriptor for any kind of socket operation, +// it can also take sockaddr_in depending on the type of socket used in arg 1 +fn C.bind(sockfd int, addr &Addr, addrlen u32) int + +fn C.listen(sockfd int, backlog int) int + +// fn C.accept(sockfd int, addr &C.sockaddr, addrlen &C.socklen_t) int +fn C.accept(sockfd int, addr &Addr, addrlen &u32) int + +fn C.getaddrinfo(node &char, service &char, hints &C.addrinfo, res &&C.addrinfo) int + +fn C.freeaddrinfo(info &C.addrinfo) + +// fn C.connect(sockfd int, addr &C.sockaddr, addrlen C.socklen_t) int +fn C.connect(sockfd int, addr &Addr, addrlen u32) int + +// fn C.send(sockfd int, buf voidptr, len size_t, flags int) size_t +fn C.send(sockfd int, buf voidptr, len size_t, flags int) int + +// fn C.sendto(sockfd int, buf voidptr, len size_t, flags int, dest_add &C.sockaddr, addrlen C.socklen_t) size_t +fn C.sendto(sockfd int, buf voidptr, len size_t, flags int, dest_add &Addr, addrlen u32) int + +// fn C.recv(sockfd int, buf voidptr, len size_t, flags int) size_t +fn C.recv(sockfd int, buf voidptr, len size_t, flags int) int + +// fn C.recvfrom(sockfd int, buf voidptr, len size_t, flags int, src_addr &C.sockaddr, addrlen &C.socklen_t) size_t +fn C.recvfrom(sockfd int, buf voidptr, len size_t, flags int, src_addr &Addr, addrlen &u32) int + +fn C.shutdown(socket int, how int) int + +fn C.ntohs(netshort u16) int + +// fn C.getpeername(sockfd int, addr &C.sockaddr, addlen &C.socklen_t) int +fn C.getpeername(sockfd int, addr &Addr, addlen &u32) int + +fn C.inet_ntop(af AddrFamily, src voidptr, dst &char, dst_size int) &char + +fn C.WSAAddressToStringA(lpsaAddress &Addr, dwAddressLength u32, lpProtocolInfo voidptr, lpszAddressString &char, lpdwAddressStringLength &u32) int + +// fn C.getsockname(sockfd int, addr &C.sockaddr, addrlen &C.socklen_t) int +fn C.getsockname(sockfd int, addr &C.sockaddr, addrlen &u32) int + +fn C.getsockopt(sockfd int, level int, optname int, optval voidptr, optlen &u32) int + +// defined in builtin +// fn C.read() int +// fn C.close() int + +fn C.ioctlsocket(s int, cmd int, argp &u32) int + +fn C.fcntl(fd int, cmd int, arg ...voidptr) int + +fn C.@select(ndfs int, readfds &C.fd_set, writefds &C.fd_set, exceptfds &C.fd_set, timeout &C.timeval) int + +fn C.FD_ZERO(fdset &C.fd_set) + +fn C.FD_SET(fd int, fdset &C.fd_set) + +fn C.FD_ISSET(fd int, fdset &C.fd_set) bool + +fn C.inet_pton(family AddrFamily, saddr &char, addr voidptr) int + +[typedef] +pub struct C.fd_set {} diff --git a/v_windows/v/old/vlib/net/address.v b/v_windows/v/old/vlib/net/address.v new file mode 100644 index 0000000..af1a000 --- /dev/null +++ b/v_windows/v/old/vlib/net/address.v @@ -0,0 +1,258 @@ +module net + +import io.util +import os + +union AddrData { + Unix + Ip + Ip6 +} + +const ( + addr_ip6_any = [16]byte{init: byte(0)} + addr_ip_any = [4]byte{init: byte(0)} +) + +fn new_ip6(port u16, addr [16]byte) Addr { + a := Addr{ + f: u16(AddrFamily.ip6) + addr: AddrData{ + Ip6: Ip6{ + port: u16(C.htons(port)) + } + } + } + + copy(a.addr.Ip6.addr[0..], addr[0..]) + + return a +} + +fn new_ip(port u16, addr [4]byte) Addr { + a := Addr{ + f: u16(AddrFamily.ip) + addr: AddrData{ + Ip: Ip{ + port: u16(C.htons(port)) + } + } + } + + copy(a.addr.Ip6.addr[0..], addr[0..]) + + return a +} + +fn temp_unix() ?Addr { + // create a temp file to get a filename + // close it + // remove it + // then reuse the filename + mut file, filename := util.temp_file() ? + file.close() + os.rm(filename) ? + addrs := resolve_addrs(filename, .unix, .udp) ? + return addrs[0] +} + +pub fn (a Addr) family() AddrFamily { + return AddrFamily(a.f) +} + +const ( + max_ip_len = 24 + max_ip6_len = 46 +) + +fn (a Ip) str() string { + buf := []byte{len: net.max_ip_len, init: 0} + + res := &char(C.inet_ntop(.ip, &a.addr, buf.data, buf.len)) + + if res == 0 { + return '' + } + + saddr := buf.bytestr() + port := C.ntohs(a.port) + + return '$saddr:$port' +} + +fn (a Ip6) str() string { + buf := []byte{len: net.max_ip6_len, init: 0} + + res := &char(C.inet_ntop(.ip6, &a.addr, buf.data, buf.len)) + + if res == 0 { + return '' + } + + saddr := buf.bytestr() + port := C.ntohs(a.port) + + return '[$saddr]:$port' +} + +const aoffset = __offsetof(Addr, addr) + +fn (a Addr) len() u32 { + match a.family() { + .ip { + return sizeof(Ip) + net.aoffset + } + .ip6 { + return sizeof(Ip6) + net.aoffset + } + .unix { + return sizeof(Unix) + net.aoffset + } + else { + panic('Unknown address family') + } + } +} + +pub fn resolve_addrs(addr string, family AddrFamily, @type SocketType) ?[]Addr { + match family { + .ip, .ip6, .unspec { + return resolve_ipaddrs(addr, family, @type) + } + .unix { + resolved := Unix{} + + if addr.len > max_unix_path { + return error('net: resolve_addrs Unix socket address is too long') + } + + // Copy the unix path into the address struct + unsafe { + C.memcpy(&resolved.path, addr.str, addr.len) + } + + return [Addr{ + f: u16(AddrFamily.unix) + addr: AddrData{ + Unix: resolved + } + }] + } + } +} + +pub fn resolve_addrs_fuzzy(addr string, @type SocketType) ?[]Addr { + if addr.len == 0 { + return none + } + + // Use a small heuristic to figure out what address family this is + // (out of the ones that we support) + + if addr.contains(':') { + // Colon is a reserved character in unix paths + // so this must be an ip address + return resolve_addrs(addr, .unspec, @type) + } + + return resolve_addrs(addr, .unix, @type) +} + +pub fn resolve_ipaddrs(addr string, family AddrFamily, typ SocketType) ?[]Addr { + address, port := split_address(addr) ? + + if addr[0] == `:` { + // Use in6addr_any + return [new_ip6(port, net.addr_ip6_any)] + } + + mut hints := C.addrinfo{ + // ai_family: int(family) + // ai_socktype: int(typ) + // ai_flags: C.AI_PASSIVE + } + hints.ai_family = int(family) + hints.ai_socktype = int(typ) + hints.ai_flags = C.AI_PASSIVE + hints.ai_protocol = 0 + hints.ai_addrlen = 0 + hints.ai_addr = voidptr(0) + hints.ai_canonname = voidptr(0) + hints.ai_next = voidptr(0) + results := &C.addrinfo(0) + + sport := '$port' + + // This might look silly but is recommended by MSDN + $if windows { + socket_error(0 - C.getaddrinfo(&char(address.str), &char(sport.str), &hints, &results)) ? + } $else { + x := C.getaddrinfo(&char(address.str), &char(sport.str), &hints, &results) + wrap_error(x) ? + } + + defer { + C.freeaddrinfo(results) + } + + // Now that we have our linked list of addresses + // convert them into an array + mut addresses := []Addr{} + + for result := results; !isnil(result); result = result.ai_next { + match AddrFamily(result.ai_family) { + .ip, .ip6 { + new_addr := Addr{ + addr: AddrData{ + Ip6: Ip6{} + } + } + unsafe { + C.memcpy(&new_addr, result.ai_addr, result.ai_addrlen) + } + addresses << new_addr + } + else { + panic('Unexpected address family $result.ai_family') + } + } + } + + return addresses +} + +fn (a Addr) str() string { + match AddrFamily(a.f) { + .ip { + unsafe { + return a.addr.Ip.str() + } + } + .ip6 { + unsafe { + return a.addr.Ip6.str() + } + } + .unix { + unsafe { + return tos_clone(a.addr.Unix.path[0..max_unix_path].data) + } + } + .unspec { + return '<.unspec>' + } + } +} + +pub fn addr_from_socket_handle(handle int) Addr { + addr := Addr{ + addr: AddrData{ + Ip6: Ip6{} + } + } + size := sizeof(addr) + + C.getsockname(handle, voidptr(&addr), &size) + + return addr +} diff --git a/v_windows/v/old/vlib/net/address_darwin.c.v b/v_windows/v/old/vlib/net/address_darwin.c.v new file mode 100644 index 0000000..041ccf2 --- /dev/null +++ b/v_windows/v/old/vlib/net/address_darwin.c.v @@ -0,0 +1,74 @@ +module net + +const max_unix_path = 104 + +struct C.addrinfo { +mut: + ai_family int + ai_socktype int + ai_flags int + ai_protocol int + ai_addrlen int + ai_addr voidptr + ai_canonname voidptr + ai_next voidptr +} + +struct C.sockaddr_in6 { +mut: + // 1 + 1 + 2 + 4 + 16 + 4 = 28; + sin6_len byte // 1 + sin6_family byte // 1 + sin6_port u16 // 2 + sin6_flowinfo u32 // 4 + sin6_addr [16]byte // 16 + sin6_scope_id u32 // 4 +} + +struct C.sockaddr_in { +mut: + sin_len byte + sin_family byte + sin_port u16 + sin_addr u32 + sin_zero [8]char +} + +struct C.sockaddr_un { +mut: + sun_len byte + sun_family byte + sun_path [max_unix_path]char +} + +[_pack: '1'] +struct Ip6 { + port u16 + flow_info u32 + addr [16]byte + scope_id u32 +} + +[_pack: '1'] +struct Ip { + port u16 + addr [4]byte + // Pad to size so that socket functions + // dont complain to us (see in.h and bind()) + // TODO(emily): I would really like to use + // some constant calculations here + // so that this doesnt have to be hardcoded + sin_pad [8]byte +} + +struct Unix { + path [max_unix_path]char +} + +[_pack: '1'] +struct Addr { +pub: + len u8 + f u8 + addr AddrData +} diff --git a/v_windows/v/old/vlib/net/address_default.c.v b/v_windows/v/old/vlib/net/address_default.c.v new file mode 100644 index 0000000..95942cc --- /dev/null +++ b/v_windows/v/old/vlib/net/address_default.c.v @@ -0,0 +1,32 @@ +module net + +const max_unix_path = 104 + +struct C.addrinfo { +mut: + ai_family int + ai_socktype int + ai_flags int + ai_protocol int + ai_addrlen int + ai_addr voidptr + ai_canonname voidptr + ai_next voidptr +} + +struct C.sockaddr_in { + sin_family byte + sin_port u16 + sin_addr u32 +} + +struct C.sockaddr_in6 { + sin6_family byte + sin6_port u16 + sin6_addr [4]u32 +} + +struct C.sockaddr_un { + sun_family byte + sun_path [max_unix_path]char +} diff --git a/v_windows/v/old/vlib/net/address_freebsd.c.v b/v_windows/v/old/vlib/net/address_freebsd.c.v new file mode 100644 index 0000000..bc84665 --- /dev/null +++ b/v_windows/v/old/vlib/net/address_freebsd.c.v @@ -0,0 +1,77 @@ +module net + +#include +#include + +const max_unix_path = 104 + +struct C.addrinfo { +mut: + ai_family int + ai_socktype int + ai_flags int + ai_protocol int + ai_addrlen int + ai_addr voidptr + ai_canonname voidptr + ai_next voidptr +} + +struct C.sockaddr_in6 { +mut: + // 1 + 1 + 2 + 4 + 16 + 4 = 28; + sin6_len byte // 1 + sin6_family byte // 1 + sin6_port u16 // 2 + sin6_flowinfo u32 // 4 + sin6_addr [16]byte // 16 + sin6_scope_id u32 // 4 +} + +struct C.sockaddr_in { +mut: + sin_len byte + sin_family byte + sin_port u16 + sin_addr u32 + sin_zero [8]char +} + +struct C.sockaddr_un { +mut: + sun_len byte + sun_family byte + sun_path [max_unix_path]char +} + +[_pack: '1'] +struct Ip6 { + port u16 + flow_info u32 + addr [16]byte + scope_id u32 +} + +[_pack: '1'] +struct Ip { + port u16 + addr [4]byte + // Pad to size so that socket functions + // dont complain to us (see in.h and bind()) + // TODO(emily): I would really like to use + // some constant calculations here + // so that this doesnt have to be hardcoded + sin_pad [8]byte +} + +struct Unix { + path [max_unix_path]char +} + +[_pack: '1'] +struct Addr { +pub: + len u8 + f u8 + addr AddrData +} diff --git a/v_windows/v/old/vlib/net/address_linux.c.v b/v_windows/v/old/vlib/net/address_linux.c.v new file mode 100644 index 0000000..a6f7a97 --- /dev/null +++ b/v_windows/v/old/vlib/net/address_linux.c.v @@ -0,0 +1,63 @@ +module net + +const max_unix_path = 108 + +struct C.addrinfo { +mut: + ai_family int + ai_socktype int + ai_flags int + ai_protocol int + ai_addrlen int + ai_addr voidptr + ai_canonname voidptr + ai_next voidptr +} + +struct C.sockaddr_in { + sin_family u16 + sin_port u16 + sin_addr u32 +} + +struct C.sockaddr_in6 { + sin6_family u16 + sin6_port u16 + sin6_addr [4]u32 +} + +struct C.sockaddr_un { + sun_family u16 + sun_path [max_unix_path]char +} + +[_pack: '1'] +struct Ip6 { + port u16 + flow_info u32 + addr [16]byte + scope_id u32 +} + +[_pack: '1'] +struct Ip { + port u16 + addr [4]byte + // Pad to size so that socket functions + // dont complain to us (see in.h and bind()) + // TODO(emily): I would really like to use + // some constant calculations here + // so that this doesnt have to be hardcoded + sin_pad [8]byte +} + +struct Unix { + path [max_unix_path]byte +} + +[_pack: '1'] +struct Addr { +pub: + f u16 + addr AddrData +} diff --git a/v_windows/v/old/vlib/net/address_test.v b/v_windows/v/old/vlib/net/address_test.v new file mode 100644 index 0000000..5b3aab0 --- /dev/null +++ b/v_windows/v/old/vlib/net/address_test.v @@ -0,0 +1,98 @@ +module net + +$if windows { + $if msvc { + // Force these to be included before afunix! + #include + #include + #include + } $else { + #include "@VROOT/vlib/net/afunix.h" + } +} $else { + #include +} + +fn test_diagnostics() { + dump(aoffset) + eprintln('--------') + in6 := C.sockaddr_in6{} + our_ip6 := Ip6{} + $if macos { + dump(__offsetof(C.sockaddr_in6, sin6_len)) + } + dump(__offsetof(C.sockaddr_in6, sin6_family)) + dump(__offsetof(C.sockaddr_in6, sin6_port)) + dump(__offsetof(C.sockaddr_in6, sin6_addr)) + $if macos { + dump(sizeof(in6.sin6_len)) + } + dump(sizeof(in6.sin6_family)) + dump(sizeof(in6.sin6_port)) + dump(sizeof(in6.sin6_addr)) + dump(sizeof(in6)) + eprintln('') + dump(__offsetof(Ip6, port)) + dump(__offsetof(Ip6, addr)) + dump(sizeof(our_ip6.port)) + dump(sizeof(our_ip6.addr)) + dump(sizeof(our_ip6)) + eprintln('--------') + in4 := C.sockaddr_in{} + our_ip4 := Ip{} + $if macos { + dump(__offsetof(C.sockaddr_in, sin_len)) + } + dump(__offsetof(C.sockaddr_in, sin_family)) + dump(__offsetof(C.sockaddr_in, sin_port)) + dump(__offsetof(C.sockaddr_in, sin_addr)) + $if macos { + dump(sizeof(in4.sin_len)) + } + dump(sizeof(in4.sin_family)) + dump(sizeof(in4.sin_port)) + dump(sizeof(in4.sin_addr)) + dump(sizeof(in4)) + eprintln('') + dump(__offsetof(Ip, port)) + dump(__offsetof(Ip, addr)) + dump(sizeof(our_ip4.port)) + dump(sizeof(our_ip4.addr)) + dump(sizeof(our_ip4)) + eprintln('--------') + dump(__offsetof(C.sockaddr_un, sun_path)) + dump(__offsetof(Unix, path)) + eprintln('--------') +} + +fn test_sizes_unix_sun_path() { + x1 := C.sockaddr_un{} + x2 := Unix{} + assert sizeof(x1.sun_path) == sizeof(x2.path) +} + +fn test_offsets_ipv6() { + assert __offsetof(C.sockaddr_in6, sin6_addr) == __offsetof(Ip6, addr) + aoffset + assert __offsetof(C.sockaddr_in6, sin6_port) == __offsetof(Ip6, port) + aoffset +} + +fn test_offsets_ipv4() { + assert __offsetof(C.sockaddr_in, sin_addr) == __offsetof(Ip, addr) + aoffset + assert __offsetof(C.sockaddr_in, sin_port) == __offsetof(Ip, port) + aoffset +} + +fn test_offsets_unix() { + assert __offsetof(C.sockaddr_un, sun_path) == __offsetof(Unix, path) + aoffset +} + +fn test_sizes_ipv6() { + assert sizeof(C.sockaddr_in6) == sizeof(Ip6) + aoffset +} + +fn test_sizes_ipv4() { + assert sizeof(C.sockaddr_in) == sizeof(Ip) + aoffset +} + +fn test_sizes_unix() { + assert sizeof(C.sockaddr_un) == sizeof(Unix) + aoffset +} diff --git a/v_windows/v/old/vlib/net/address_windows.c.v b/v_windows/v/old/vlib/net/address_windows.c.v new file mode 100644 index 0000000..e50fdb4 --- /dev/null +++ b/v_windows/v/old/vlib/net/address_windows.c.v @@ -0,0 +1,58 @@ +module net + +const max_unix_path = 108 + +struct C.addrinfo { +mut: + ai_family int + ai_socktype int + ai_flags int + ai_protocol int + ai_addrlen int + ai_addr voidptr + ai_canonname voidptr + ai_next voidptr +} + +struct C.sockaddr_in { + sin_family u16 + sin_port u16 + sin_addr u32 +} + +struct C.sockaddr_in6 { + sin6_family u16 + sin6_port u16 + sin6_addr [4]u32 +} + +struct C.sockaddr_un { + sun_family u16 + sun_path [max_unix_path]char +} + +[_pack: '1'] +struct Ip6 { + port u16 + flow_info u32 + addr [16]byte + scope_id u32 +} + +[_pack: '1'] +struct Ip { + port u16 + addr [4]byte + sin_pad [8]byte +} + +struct Unix { + path [max_unix_path]byte +} + +[_pack: '1'] +struct Addr { +pub: + f u16 + addr AddrData +} diff --git a/v_windows/v/old/vlib/net/afunix.h b/v_windows/v/old/vlib/net/afunix.h new file mode 100644 index 0000000..5fedca2 --- /dev/null +++ b/v_windows/v/old/vlib/net/afunix.h @@ -0,0 +1,26 @@ +/** + * This file has no copyright assigned and is placed in the Public Domain. + * This file is part of the mingw-w64 runtime package. + * No warranty is given; refer to the file DISCLAIMER.PD within this package. + */ + +#ifndef _AFUNIX_ +#define _AFUNIX_ + +#define UNIX_PATH_MAX 108 + +#if !defined(ADDRESS_FAMILY) +#define UNDEF_ADDRESS_FAMILY +#define ADDRESS_FAMILY unsigned short +#endif + +typedef struct sockaddr_un { + ADDRESS_FAMILY sun_family; + char sun_path[UNIX_PATH_MAX]; +} SOCKADDR_UN, *PSOCKADDR_UN; + +#if defined(UNDEF_ADDRESS_FAMILY) +#undef ADDRESS_FAMILY +#endif + +#endif /* _AFUNIX_ */ diff --git a/v_windows/v/old/vlib/net/common.v b/v_windows/v/old/vlib/net/common.v new file mode 100644 index 0000000..aab8f16 --- /dev/null +++ b/v_windows/v/old/vlib/net/common.v @@ -0,0 +1,129 @@ +module net + +import time + +// no_deadline should be given to functions when no deadline is wanted (i.e. all functions +// return instantly) +const no_deadline = time.Time{ + unix: 0 +} + +// no_timeout should be given to functions when no timeout is wanted (i.e. all functions +// return instantly) +pub const no_timeout = time.Duration(0) + +// infinite_timeout should be given to functions when an infinite_timeout is wanted (i.e. functions +// only ever return with data) +pub const infinite_timeout = time.infinite + +// Shutdown shutsdown a socket and closes it +fn shutdown(handle int) ? { + $if windows { + C.shutdown(handle, C.SD_BOTH) + socket_error(C.closesocket(handle)) ? + } $else { + C.shutdown(handle, C.SHUT_RDWR) + socket_error(C.close(handle)) ? + } +} + +// Select waits for an io operation (specified by parameter `test`) to be available +fn @select(handle int, test Select, timeout time.Duration) ?bool { + set := C.fd_set{} + + C.FD_ZERO(&set) + C.FD_SET(handle, &set) + + seconds := timeout / time.second + microseconds := time.Duration(timeout - (seconds * time.second)).microseconds() + + mut tt := C.timeval{ + tv_sec: u64(seconds) + tv_usec: u64(microseconds) + } + + mut timeval_timeout := &tt + + // infinite timeout is signaled by passing null as the timeout to + // select + if timeout == net.infinite_timeout { + timeval_timeout = &C.timeval(0) + } + + match test { + .read { + socket_error(C.@select(handle + 1, &set, C.NULL, C.NULL, timeval_timeout)) ? + } + .write { + socket_error(C.@select(handle + 1, C.NULL, &set, C.NULL, timeval_timeout)) ? + } + .except { + socket_error(C.@select(handle + 1, C.NULL, C.NULL, &set, timeval_timeout)) ? + } + } + + return C.FD_ISSET(handle, &set) +} + +// select_with_retry will retry the select if select is failing +// due to interrupted system call. This can happen on signals +// for example the GC Boehm uses signals internally on garbage +// collection +[inline] +fn select_with_retry(handle int, test Select, timeout time.Duration) ?bool { + mut retries := 10 + for retries > 0 { + ready := @select(handle, test, timeout) or { + if err.code == 4 { + // signal! lets retry max 10 times + // suspend thread with sleep to let the gc get + // cycles in the case the Bohem gc is interupting + time.sleep(1 * time.millisecond) + retries -= 1 + continue + } + // we got other error + return err + } + return ready + } + return error('failed to @select more that three times due to interrupted system call') +} + +// wait_for_common wraps the common wait code +fn wait_for_common(handle int, deadline time.Time, timeout time.Duration, test Select) ? { + if deadline.unix == 0 { + // do not accept negative timeout + if timeout < 0 { + return err_timed_out + } + ready := select_with_retry(handle, test, timeout) ? + if ready { + return + } + return err_timed_out + } + // Convert the deadline into a timeout + // and use that + d_timeout := deadline.unix - time.now().unix + if d_timeout < 0 { + // deadline is in the past so this has already + // timed out + return err_timed_out + } + ready := select_with_retry(handle, test, timeout) ? + if ready { + return + } + return err_timed_out +} + +// wait_for_write waits for a write io operation to be available +fn wait_for_write(handle int, deadline time.Time, timeout time.Duration) ? { + return wait_for_common(handle, deadline, timeout, .write) +} + +// wait_for_read waits for a read io operation to be available +fn wait_for_read(handle int, deadline time.Time, timeout time.Duration) ? { + return wait_for_common(handle, deadline, timeout, .read) +} diff --git a/v_windows/v/old/vlib/net/conv/conv.c.v b/v_windows/v/old/vlib/net/conv/conv.c.v new file mode 100644 index 0000000..e29741a --- /dev/null +++ b/v_windows/v/old/vlib/net/conv/conv.c.v @@ -0,0 +1,21 @@ +module conv + +// host to net 32 (htonl) +pub fn htn32(host &u32) u32 { + return C.htonl(host) +} + +// host to net 16 (htons) +pub fn htn16(host &u16) u16 { + return C.htons(host) +} + +// net to host 32 (ntohl) +pub fn nth32(host &u32) u32 { + return C.ntohl(host) +} + +// net to host 16 (ntohs) +pub fn nth16(host &u16) u16 { + return C.ntohs(host) +} diff --git a/v_windows/v/old/vlib/net/conv/conv_default.c.v b/v_windows/v/old/vlib/net/conv/conv_default.c.v new file mode 100644 index 0000000..8e8c582 --- /dev/null +++ b/v_windows/v/old/vlib/net/conv/conv_default.c.v @@ -0,0 +1,46 @@ +module conv + +#include + +fn C.htonl(host u32) u32 +fn C.htons(host u16) u16 + +fn C.ntohl(net u32) u32 +fn C.ntohs(net u16) u16 + +struct Bytes { +mut: + first u32 + last u32 +} + +union LongLong { + Bytes + ll u64 +} + +// host to net 64 (htonll) +pub fn htn64(host &u64) u64 { + mut ll := LongLong{ + ll: host + } + + unsafe { + ll.first = htn32(ll.first) + ll.last = htn32(ll.last) + } + return unsafe { ll.ll } +} + +// net to host 64 (ntohll) +pub fn nth64(net &u64) u64 { + mut ll := LongLong{ + ll: net + } + + unsafe { + ll.first = nth32(ll.first) + ll.last = nth32(ll.last) + } + return unsafe { ll.ll } +} diff --git a/v_windows/v/old/vlib/net/conv/conv_windows.c.v b/v_windows/v/old/vlib/net/conv/conv_windows.c.v new file mode 100644 index 0000000..15827f7 --- /dev/null +++ b/v_windows/v/old/vlib/net/conv/conv_windows.c.v @@ -0,0 +1,21 @@ +module conv + +#include + +fn C.htonll(host u64) u64 +fn C.htonl(host u32) u32 +fn C.htons(host u16) u16 + +fn C.ntohll(net u32) u32 +fn C.ntohl(net u32) u32 +fn C.ntohs(net u16) u16 + +// host to net 64 (htonll) +pub fn htn64(host &u64) u64 { + return C.htonll(host) +} + +// net to host 64 (htonll) +pub fn nth64(host &u64) u64 { + return C.ntohll(host) +} diff --git a/v_windows/v/old/vlib/net/errors.v b/v_windows/v/old/vlib/net/errors.v new file mode 100644 index 0000000..f6ada74 --- /dev/null +++ b/v_windows/v/old/vlib/net/errors.v @@ -0,0 +1,70 @@ +module net + +const ( + errors_base = 0 +) + +// Well defined errors that are returned from socket functions +pub const ( + err_new_socket_failed = error_with_code('net: new_socket failed to create socket', + errors_base + 1) + err_option_not_settable = error_with_code('net: set_option_xxx option not settable', + errors_base + 2) + err_option_wrong_type = error_with_code('net: set_option_xxx option wrong type', + errors_base + 3) + err_port_out_of_range = error_with_code('', errors_base + 5) + err_no_udp_remote = error_with_code('', errors_base + 6) + err_connect_failed = error_with_code('net: connect failed', errors_base + 7) + err_connect_timed_out = error_with_code('net: connect timed out', errors_base + 8) + err_timed_out = error_with_code('net: op timed out', errors_base + 9) + err_timed_out_code = errors_base + 9 +) + +pub fn socket_error(potential_code int) ?int { + $if windows { + if potential_code < 0 { + last_error_int := C.WSAGetLastError() + last_error := wsa_error(last_error_int) + return error_with_code('net: socket error: ($last_error_int) $last_error', + int(last_error)) + } + } $else { + if potential_code < 0 { + last_error := error_code() + return error_with_code('net: socket error: $last_error', last_error) + } + } + + return potential_code +} + +pub fn wrap_error(error_code int) ? { + $if windows { + enum_error := wsa_error(error_code) + return error_with_code('net: socket error: $enum_error', error_code) + } $else { + if error_code == 0 { + return + } + return error_with_code('net: socket error: $error_code', error_code) + } +} + +// wrap_read_result takes a read result and sees if it is 0 for graceful +// connection termination and returns none +// e.g. res := wrap_read_result(C.recv(c.sock.handle, voidptr(buf_ptr), len, 0))? +[inline] +fn wrap_read_result(result int) ?int { + if result == 0 { + return none + } + return result +} + +[inline] +fn wrap_write_result(result int) ?int { + if result == 0 { + return none + } + return result +} diff --git a/v_windows/v/old/vlib/net/ftp/ftp.v b/v_windows/v/old/vlib/net/ftp/ftp.v new file mode 100644 index 0000000..41b2cde --- /dev/null +++ b/v_windows/v/old/vlib/net/ftp/ftp.v @@ -0,0 +1,265 @@ +module ftp + +/* +basic ftp module + RFC-959 + https://tools.ietf.org/html/rfc959 + + Methods: + ftp.connect(host) + ftp.login(user, passw) + pwd := ftp.pwd() + ftp.cd(folder) + dtp := ftp.pasv() + ftp.dir() + ftp.get(file) + dtp.read() + dtp.close() + ftp.close() +*/ +import net +import io + +const ( + connected = 220 + specify_password = 331 + logged_in = 230 + login_first = 503 + anonymous = 530 + open_data_connection = 150 + close_data_connection = 226 + command_ok = 200 + denied = 550 + passive_mode = 227 + complete = 226 +) + +struct DTP { +mut: + conn &net.TcpConn + reader io.BufferedReader + ip string + port int +} + +fn (mut dtp DTP) read() ?[]byte { + mut data := []byte{} + mut buf := []byte{len: 1024} + for { + len := dtp.reader.read(mut buf) or { break } + if len == 0 { + break + } + data << buf[..len] + } + return data +} + +fn (mut dtp DTP) close() { + dtp.conn.close() or { panic(err) } +} + +struct FTP { +mut: + conn &net.TcpConn + reader io.BufferedReader + buffer_size int +} + +pub fn new() FTP { + mut f := FTP{ + conn: 0 + } + f.buffer_size = 1024 + return f +} + +fn (mut zftp FTP) write(data string) ?int { + $if debug { + println('FTP.v >>> $data') + } + return zftp.conn.write('$data\r\n'.bytes()) +} + +fn (mut zftp FTP) read() ?(int, string) { + mut data := zftp.reader.read_line() ? + $if debug { + println('FTP.v <<< $data') + } + if data.len < 5 { + return 0, '' + } + code := data[..3].int() + if data[3] == `-` { + for { + data = zftp.reader.read_line() ? + if data[..3].int() == code && data[3] != `-` { + break + } + } + } + return code, data +} + +pub fn (mut zftp FTP) connect(ip string) ?bool { + zftp.conn = net.dial_tcp('$ip:21') ? + zftp.reader = io.new_buffered_reader(reader: zftp.conn) + code, _ := zftp.read() ? + if code == ftp.connected { + return true + } + return false +} + +pub fn (mut zftp FTP) login(user string, passwd string) ?bool { + zftp.write('USER $user') or { + $if debug { + println('ERROR sending user') + } + return false + } + mut code, _ := zftp.read() ? + if code == ftp.logged_in { + return true + } + if code != ftp.specify_password { + return false + } + zftp.write('PASS $passwd') or { + $if debug { + println('ERROR sending password') + } + return false + } + code, _ = zftp.read() ? + if code == ftp.logged_in { + return true + } + return false +} + +pub fn (mut zftp FTP) close() ? { + zftp.write('QUIT') ? + zftp.conn.close() ? +} + +pub fn (mut zftp FTP) pwd() ?string { + zftp.write('PWD') ? + _, data := zftp.read() ? + spl := data.split('"') // " + if spl.len >= 2 { + return spl[1] + } + return data +} + +pub fn (mut zftp FTP) cd(dir string) ? { + zftp.write('CWD $dir') or { return } + mut code, mut data := zftp.read() ? + match int(code) { + ftp.denied { + $if debug { + println('CD $dir denied!') + } + } + ftp.complete { + code, data = zftp.read() ? + } + else {} + } + $if debug { + println('CD $data') + } +} + +fn new_dtp(msg string) ?&DTP { + if !is_dtp_message_valid(msg) { + return error('Bad message') + } + ip, port := get_host_ip_from_dtp_message(msg) + mut dtp := &DTP{ + ip: ip + port: port + conn: 0 + } + conn := net.dial_tcp('$ip:$port') or { return error('Cannot connect to the data channel') } + dtp.conn = conn + dtp.reader = io.new_buffered_reader(reader: dtp.conn) + return dtp +} + +fn (mut zftp FTP) pasv() ?&DTP { + zftp.write('PASV') ? + code, data := zftp.read() ? + $if debug { + println('pass: $data') + } + if code != ftp.passive_mode { + return error('pasive mode not allowed') + } + dtp := new_dtp(data) ? + return dtp +} + +pub fn (mut zftp FTP) dir() ?[]string { + mut dtp := zftp.pasv() or { return error('Cannot establish data connection') } + zftp.write('LIST') ? + code, _ := zftp.read() ? + if code == ftp.denied { + return error('`LIST` denied') + } + if code != ftp.open_data_connection { + return error('Data channel empty') + } + list_dir := dtp.read() ? + result, _ := zftp.read() ? + if result != ftp.close_data_connection { + println('`LIST` not ok') + } + dtp.close() + mut dir := []string{} + sdir := list_dir.bytestr() + for lfile in sdir.split('\n') { + if lfile.len > 1 { + dir << lfile.after(' ').trim_space() + } + } + return dir +} + +pub fn (mut zftp FTP) get(file string) ?[]byte { + mut dtp := zftp.pasv() or { return error('Cannot stablish data connection') } + zftp.write('RETR $file') ? + code, _ := zftp.read() ? + if code == ftp.denied { + return error('Permission denied') + } + if code != ftp.open_data_connection { + return error('Data connection not ready') + } + blob := dtp.read() ? + dtp.close() + return blob +} + +fn is_dtp_message_valid(msg string) bool { + // An example of message: + // '227 Entering Passive Mode (209,132,183,61,48,218)' + return msg.contains('(') && msg.contains(')') && msg.contains(',') +} + +fn get_host_ip_from_dtp_message(msg string) (string, int) { + mut par_start_idx := -1 + mut par_end_idx := -1 + for i, c in msg { + if c == `(` { + par_start_idx = i + 1 + } else if c == `)` { + par_end_idx = i + } + } + data := msg[par_start_idx..par_end_idx].split(',') + ip := data[0..4].join('.') + port := data[4].int() * 256 + data[5].int() + return ip, port +} diff --git a/v_windows/v/old/vlib/net/ftp/ftp_test.v b/v_windows/v/old/vlib/net/ftp/ftp_test.v new file mode 100644 index 0000000..a62316d --- /dev/null +++ b/v_windows/v/old/vlib/net/ftp/ftp_test.v @@ -0,0 +1,50 @@ +import net.ftp + +fn test_ftp_cleint() { + $if !network ? { + return + } + // NB: this function makes network calls to external servers, + // that is why it is not a very good idea to run it in CI. + // If you want to run it manually, use: + // `v -d network vlib/net/ftp/ftp_test.v` + ftp_client_test_inside() or { panic(err) } +} + +fn ftp_client_test_inside() ? { + mut zftp := ftp.new() + // eprintln(zftp) + defer { + zftp.close() or { panic(err) } + } + connect_result := zftp.connect('ftp.redhat.com') ? + assert connect_result + login_result := zftp.login('ftp', 'ftp') ? + assert login_result + pwd := zftp.pwd() ? + assert pwd.len > 0 + zftp.cd('/') or { + assert false + return + } + dir_list1 := zftp.dir() or { + assert false + return + } + assert dir_list1.len > 0 + zftp.cd('/suse/linux/enterprise/11Server/en/SAT-TOOLS/SRPMS/') or { + assert false + return + } + dir_list2 := zftp.dir() or { + assert false + return + } + assert dir_list2.len > 0 + assert dir_list2.contains('katello-host-tools-3.3.5-8.sles11_4sat.src.rpm') + blob := zftp.get('katello-host-tools-3.3.5-8.sles11_4sat.src.rpm') or { + assert false + return + } + assert blob.len > 0 +} diff --git a/v_windows/v/old/vlib/net/html/README.md b/v_windows/v/old/vlib/net/html/README.md new file mode 100644 index 0000000..a92a6e6 --- /dev/null +++ b/v_windows/v/old/vlib/net/html/README.md @@ -0,0 +1,16 @@ +net/http is an HTML written in pure V. + +## Usage +```v oksyntax +import net.html + +fn main() { + doc := html.parse('

      Hello world!

      ') + tag := doc.get_tag('h1')[0] //

      Hello world!

      + println(tag.name) // h1 + println(tag.content) // Hello world! + println(tag.attributes) // {'class':'title'} + println(tag.str()) //

      Hello world!

      +} +``` +More examples found on [`parser_test.v`](parser_test.v) and [`html_test.v`](html_test.v) diff --git a/v_windows/v/old/vlib/net/html/data_structures.v b/v_windows/v/old/vlib/net/html/data_structures.v new file mode 100644 index 0000000..688b756 --- /dev/null +++ b/v_windows/v/old/vlib/net/html/data_structures.v @@ -0,0 +1,91 @@ +module html + +const ( + null_element = int(0x80000000) +) + +struct Stack { +mut: + elements []int + size int +} + +[inline] +fn is_null(data int) bool { + return data == html.null_element +} + +[inline] +fn (stack Stack) is_empty() bool { + return stack.size <= 0 +} + +fn (stack Stack) peek() int { + return if !stack.is_empty() { stack.elements[stack.size - 1] } else { html.null_element } +} + +fn (mut stack Stack) pop() int { + mut to_return := html.null_element + if !stack.is_empty() { + to_return = stack.elements[stack.size - 1] + stack.size-- + } + return to_return +} + +fn (mut stack Stack) push(item int) { + if stack.elements.len > stack.size { + stack.elements[stack.size] = item + } else { + stack.elements << item + } + stack.size++ +} + +struct BTree { +mut: + all_tags []Tag + node_pointer int + childrens [][]int + parents []int +} + +fn (mut btree BTree) add_children(tag Tag) int { + btree.all_tags << tag + if btree.all_tags.len > 1 { + for btree.childrens.len <= btree.node_pointer { + mut temp_array := btree.childrens + temp_array << []int{} + btree.childrens = temp_array + } + btree.childrens[btree.node_pointer] << btree.all_tags.len - 1 + for btree.parents.len < btree.all_tags.len { + mut temp_array := btree.parents + temp_array << 0 + btree.parents = temp_array + } + btree.parents[btree.all_tags.len - 1] = btree.node_pointer + } + return btree.all_tags.len - 1 +} + +[inline] +fn (btree BTree) get_children() []int { + return btree.childrens[btree.node_pointer] +} + +[inline] +fn (btree BTree) get_parent() int { + return btree.parents[btree.node_pointer] +} + +[inline] +fn (btree BTree) get_stored() Tag { + return btree.all_tags[btree.node_pointer] +} + +fn (mut btree BTree) move_pointer(to int) { + if to < btree.all_tags.len { + btree.node_pointer = to + } +} diff --git a/v_windows/v/old/vlib/net/html/dom.v b/v_windows/v/old/vlib/net/html/dom.v new file mode 100644 index 0000000..f56e9c2 --- /dev/null +++ b/v_windows/v/old/vlib/net/html/dom.v @@ -0,0 +1,189 @@ +module html + +import os + +// The W3C Document Object Model (DOM) is a platform and language-neutral +// interface that allows programs and scripts to dynamically access and +// update the content, structure, and style of a document. +// +// https://www.w3.org/TR/WD-DOM/introduction.html +pub struct DocumentObjectModel { +mut: + root &Tag + constructed bool + btree BTree + all_tags []&Tag + all_attributes map[string][]&Tag + close_tags map[string]bool // add a counter to see count how many times is closed and parse correctly + attributes map[string][]string + tag_attributes map[string][][]&Tag + tag_type map[string][]&Tag + debug_file os.File +} + +[if debug] +fn (mut dom DocumentObjectModel) print_debug(data string) { + $if debug { + if data.len > 0 { + dom.debug_file.writeln(data) or { panic(err) } + } + } +} + +[inline] +fn is_close_tag(tag &Tag) bool { + return tag.name.len > 0 && tag.name[0] == `/` +} + +fn (mut dom DocumentObjectModel) where_is(item_name string, attribute_name string) int { + if attribute_name !in dom.attributes { + dom.attributes[attribute_name] = []string{} + } + mut string_array := dom.attributes[attribute_name] + mut counter := 0 + for value in string_array { + if value == item_name { + return counter + } + counter++ + } + string_array << item_name + dom.attributes[attribute_name] = string_array + return string_array.len - 1 +} + +fn (mut dom DocumentObjectModel) add_tag_attribute(tag &Tag) { + for attribute_name, _ in tag.attributes { + attribute_value := tag.attributes[attribute_name] + location := dom.where_is(attribute_value, attribute_name) + if attribute_name !in dom.tag_attributes { + dom.tag_attributes[attribute_name] = [] + } + for { + mut temp_array := dom.tag_attributes[attribute_name] + temp_array << []&Tag{} + dom.tag_attributes[attribute_name] = temp_array + if location < dom.tag_attributes[attribute_name].len + 1 { + break + } + } + mut temp_array := dom.tag_attributes[attribute_name][location] + temp_array << tag + dom.tag_attributes[attribute_name][location] = temp_array + } +} + +fn (mut dom DocumentObjectModel) add_tag_by_type(tag &Tag) { + tag_name := tag.name + if !(tag_name in dom.tag_type) { + dom.tag_type[tag_name] = [tag] + } else { + mut temp_array := dom.tag_type[tag_name] + temp_array << tag + dom.tag_type[tag_name] = temp_array + } +} + +fn (mut dom DocumentObjectModel) add_tag_by_attribute(tag &Tag) { + for attribute_name in tag.attributes.keys() { + if attribute_name !in dom.all_attributes { + dom.all_attributes[attribute_name] = [tag] + } else { + mut temp_array := dom.all_attributes[attribute_name] + temp_array << tag + dom.all_attributes[attribute_name] = temp_array + } + } +} + +fn (mut dom DocumentObjectModel) construct(tag_list []&Tag) { + dom.constructed = true + mut temp_map := map[string]int{} + mut temp_int := null_element + mut temp_string := '' + mut stack := Stack{} + dom.btree = BTree{} + dom.root = tag_list[0] + dom.all_tags = [tag_list[0]] + temp_map['0'] = dom.btree.add_children(tag_list[0]) + stack.push(0) + root_index := 0 + for index := 1; index < tag_list.len; index++ { + mut tag := tag_list[index] + dom.print_debug(tag.str()) + if is_close_tag(tag) { + temp_int = stack.peek() + temp_string = tag.name[1..] + for !is_null(temp_int) && temp_string != tag_list[temp_int].name + && !tag_list[temp_int].closed { + dom.print_debug(temp_string + ' >> ' + tag_list[temp_int].name + ' ' + + (temp_string == tag_list[temp_int].name).str()) + stack.pop() + temp_int = stack.peek() + } + temp_int = stack.peek() + temp_int = if !is_null(temp_int) { stack.pop() } else { root_index } + if is_null(temp_int) { + stack.push(root_index) + } + dom.print_debug('Removed ' + temp_string + ' -- ' + tag_list[temp_int].name) + } else if tag.name.len > 0 { + dom.add_tag_attribute(tag) // error here + dom.add_tag_by_attribute(tag) + dom.add_tag_by_type(tag) + dom.all_tags << tag + temp_int = stack.peek() + if !is_null(temp_int) { + dom.btree.move_pointer(temp_map[temp_int.str()]) + temp_map[index.str()] = dom.btree.add_children(tag) + mut temp_tag := tag_list[temp_int] + position_in_parent := temp_tag.add_child(tag) // tag_list[temp_int] = temp_tag + tag.add_parent(temp_tag, position_in_parent) + /* + dom.print_debug("Added ${tag.name} as child of '" + tag_list[temp_int].name + + "' which now has ${dom.btree.get_children().len} childrens") + */ + dom.print_debug("Added $tag.name as child of '" + temp_tag.name + + "' which now has $temp_tag.children.len childrens") + } else { // dom.new_root(tag) + stack.push(root_index) + } + temp_string = '/' + tag.name + if temp_string in dom.close_tags && !tag.closed { // if tag ends with /> + dom.print_debug('Pushed ' + temp_string) + stack.push(index) + } + } + } // println(tag_list[root_index]) for debug purposes + dom.root = tag_list[0] +} + +// get_tag_by_attribute_value retrieves all the tags in the document that has the given attribute name and value. +pub fn (mut dom DocumentObjectModel) get_tag_by_attribute_value(name string, value string) []&Tag { + location := dom.where_is(value, name) + return if dom.tag_attributes[name].len > location { + dom.tag_attributes[name][location] + } else { + []&Tag{} + } +} + +// get_tag retrieves all the tags in the document that has the given tag name. +pub fn (dom DocumentObjectModel) get_tag(name string) []&Tag { + return if name in dom.tag_type { dom.tag_type[name] } else { []&Tag{} } +} + +// get_tag_by_attribute retrieves all the tags in the document that has the given attribute name. +pub fn (dom DocumentObjectModel) get_tag_by_attribute(name string) []&Tag { + return if name in dom.all_attributes { dom.all_attributes[name] } else { []&Tag{} } +} + +// get_root returns the root of the document. +pub fn (dom DocumentObjectModel) get_root() &Tag { + return dom.root +} + +// get_tags returns all of the tags stored in the document. +pub fn (dom DocumentObjectModel) get_tags() []&Tag { + return dom.all_tags +} diff --git a/v_windows/v/old/vlib/net/html/dom_test.v b/v_windows/v/old/vlib/net/html/dom_test.v new file mode 100644 index 0000000..d4fd292 --- /dev/null +++ b/v_windows/v/old/vlib/net/html/dom_test.v @@ -0,0 +1,56 @@ +module html + +import strings + +fn generate_temp_html() string { + mut temp_html := strings.new_builder(200) + temp_html.write_string('Giant String') + for counter := 0; counter < 4; counter++ { + temp_html.write_string("
      Look at $counter
      ") + } + temp_html.write_string('') + return temp_html.str() +} + +fn test_search_by_tag_type() { + dom := parse(generate_temp_html()) + assert dom.get_tag('div').len == 4 + assert dom.get_tag('head').len == 1 + assert dom.get_tag('body').len == 1 +} + +fn test_search_by_attribute_value() { + mut dom := parse(generate_temp_html()) + // println(temp_html) + print('Amount ') + println(dom.get_tag_by_attribute_value('id', 'name_0')) + assert dom.get_tag_by_attribute_value('id', 'name_0').len == 1 +} + +fn test_access_parent() { + mut dom := parse(generate_temp_html()) + div_tags := dom.get_tag('div') + parent := div_tags[0].parent + assert parent != 0 + for div_tag in div_tags { + assert div_tag.parent == parent + } +} + +fn test_search_by_attributes() { + dom := parse(generate_temp_html()) + assert dom.get_tag_by_attribute('id').len == 4 +} + +fn test_tags_used() { + dom := parse(generate_temp_html()) + assert dom.get_tags().len == 9 +} + +fn test_access_tag_fields() { + dom := parse(generate_temp_html()) + id_tags := dom.get_tag_by_attribute('id') + assert id_tags[0].name == 'div' + assert id_tags[1].attributes['class'] == 'several-1' +} diff --git a/v_windows/v/old/vlib/net/html/html.v b/v_windows/v/old/vlib/net/html/html.v new file mode 100644 index 0000000..293b643 --- /dev/null +++ b/v_windows/v/old/vlib/net/html/html.v @@ -0,0 +1,18 @@ +module html + +import os + +// parse parses and returns the DOM from the given text. +pub fn parse(text string) DocumentObjectModel { + mut parser := Parser{} + parser.parse_html(text) + return parser.get_dom() +} + +// parse_file parses and returns the DOM from the contents of a file. +pub fn parse_file(filename string) DocumentObjectModel { + content := os.read_file(filename) or { return DocumentObjectModel{ + root: &Tag{} + } } + return parse(content) +} diff --git a/v_windows/v/old/vlib/net/html/html_test.v b/v_windows/v/old/vlib/net/html/html_test.v new file mode 100644 index 0000000..51271cd --- /dev/null +++ b/v_windows/v/old/vlib/net/html/html_test.v @@ -0,0 +1,15 @@ +module html + +fn test_parse() { + doc := parse('

      Hello world!

      ') + tags := doc.get_tag('h1') + assert tags.len == 1 + h1_tag := tags[0] //

      Hello world!

      + assert h1_tag.name == 'h1' + assert h1_tag.content == 'Hello world!' + assert h1_tag.attributes.len == 2 + // TODO: do not remove. Attributes must not have an empty attr. + // assert h1_tag.attributes.len == 1 + assert h1_tag.str() == '

      Hello world!

      ' + // assert h1_tag.str() == '

      Hello world!

      ' +} diff --git a/v_windows/v/old/vlib/net/html/parser.v b/v_windows/v/old/vlib/net/html/parser.v new file mode 100644 index 0000000..b9ad2a1 --- /dev/null +++ b/v_windows/v/old/vlib/net/html/parser.v @@ -0,0 +1,260 @@ +module html + +import os +import strings + +struct LexicalAttributes { +mut: + current_tag &Tag + open_tag bool + open_code bool + open_string int + open_comment bool + is_attribute bool + opened_code_type string + line_count int + lexeme_builder strings.Builder = strings.new_builder(100) + code_tags map[string]bool = map{ + 'script': true + 'style': true + } +} + +// Parser is responsible for reading the HTML strings and converting them into a `DocumentObjectModel`. +pub struct Parser { +mut: + dom DocumentObjectModel + lexical_attributes LexicalAttributes = LexicalAttributes{ + current_tag: &Tag{} + } + filename string = 'direct-parse' + initialized bool + tags []&Tag + debug_file os.File +} + +// This function is used to add a tag for the parser ignore it's content. +// For example, if you have an html or XML with a custom tag, like `' + parser.parse_html(temp_html) + assert parser.tags[2].content.len == script_content.replace('\n', '').len +} diff --git a/v_windows/v/old/vlib/net/html/tag.v b/v_windows/v/old/vlib/net/html/tag.v new file mode 100644 index 0000000..62260c0 --- /dev/null +++ b/v_windows/v/old/vlib/net/html/tag.v @@ -0,0 +1,68 @@ +module html + +import strings + +enum CloseTagType { + in_name + new_tag +} + +// Tag holds the information of an HTML tag. +[heap] +pub struct Tag { +pub mut: + name string + content string + children []&Tag + attributes map[string]string // attributes will be like map[name]value + last_attribute string + parent &Tag = 0 + position_in_parent int + closed bool + close_type CloseTagType = .in_name +} + +fn (mut tag Tag) add_parent(t &Tag, position int) { + tag.position_in_parent = position + tag.parent = t +} + +fn (mut tag Tag) add_child(t &Tag) int { + tag.children << t + return tag.children.len +} + +// text returns the text contents of the tag. +pub fn (tag Tag) text() string { + if tag.name.len >= 2 && tag.name[..2] == 'br' { + return '\n' + } + mut text_str := strings.new_builder(200) + text_str.write_string(tag.content.replace('\n', '')) + for child in tag.children { + text_str.write_string(child.text()) + } + return text_str.str() +} + +pub fn (tag &Tag) str() string { + mut html_str := strings.new_builder(200) + html_str.write_string('<$tag.name') + for key, value in tag.attributes { + html_str.write_string(' $key') + if value.len > 0 { + html_str.write_string('="$value"') + } + } + html_str.write_string(if tag.closed && tag.close_type == .in_name { '/>' } else { '>' }) + html_str.write_string(tag.content) + if tag.children.len > 0 { + for child in tag.children { + html_str.write_string(child.str()) + } + } + if !tag.closed || tag.close_type == .new_tag { + html_str.write_string('') + } + return html_str.str() +} diff --git a/v_windows/v/old/vlib/net/http/backend_nix.c.v b/v_windows/v/old/vlib/net/http/backend_nix.c.v new file mode 100644 index 0000000..1243442 --- /dev/null +++ b/v_windows/v/old/vlib/net/http/backend_nix.c.v @@ -0,0 +1,74 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module http + +import strings +import net.openssl + +const ( + is_used = openssl.is_used +) + +fn (req &Request) ssl_do(port int, method Method, host_name string, path string) ?Response { + // ssl_method := C.SSLv23_method() + ctx := C.SSL_CTX_new(C.TLS_method()) + C.SSL_CTX_set_verify_depth(ctx, 4) + flags := C.SSL_OP_NO_SSLv2 | C.SSL_OP_NO_SSLv3 | C.SSL_OP_NO_COMPRESSION + C.SSL_CTX_set_options(ctx, flags) + mut res := C.SSL_CTX_load_verify_locations(ctx, c'random-org-chain.pem', 0) + web := C.BIO_new_ssl_connect(ctx) + addr := host_name + ':' + port.str() + res = C.BIO_set_conn_hostname(web, addr.str) + ssl := &openssl.SSL(0) + C.BIO_get_ssl(web, &ssl) + preferred_ciphers := 'HIGH:!aNULL:!kRSA:!PSK:!SRP:!MD5:!RC4' + res = C.SSL_set_cipher_list(voidptr(ssl), &char(preferred_ciphers.str)) + if res != 1 { + println('http: openssl: cipher failed') + } + res = C.SSL_set_tlsext_host_name(voidptr(ssl), host_name.str) + res = C.BIO_do_connect(web) + if res != 1 { + return error('cannot connect the endpoint') + } + res = C.BIO_do_handshake(web) + C.SSL_get_peer_certificate(voidptr(ssl)) + res = C.SSL_get_verify_result(voidptr(ssl)) + // ///// + req_headers := req.build_request_headers(method, host_name, path) + $if trace_http_request ? { + eprintln('> $req_headers') + } + // println(req_headers) + C.BIO_puts(web, &char(req_headers.str)) + mut content := strings.new_builder(100) + mut buff := [bufsize]byte{} + bp := unsafe { &buff[0] } + mut readcounter := 0 + for { + readcounter++ + len := unsafe { C.BIO_read(web, bp, bufsize) } + if len <= 0 { + break + } + $if debug_http ? { + eprintln('ssl_do, read ${readcounter:4d} | len: $len') + eprintln('-'.repeat(20)) + eprintln(unsafe { tos(bp, len) }) + eprintln('-'.repeat(20)) + } + unsafe { content.write_ptr(bp, len) } + } + if web != 0 { + C.BIO_free_all(web) + } + if ctx != 0 { + C.SSL_CTX_free(ctx) + } + response_text := content.str() + $if trace_http_response ? { + eprintln('< $response_text') + } + return parse_response(response_text) +} diff --git a/v_windows/v/old/vlib/net/http/backend_windows.c.v b/v_windows/v/old/vlib/net/http/backend_windows.c.v new file mode 100644 index 0000000..9181166 --- /dev/null +++ b/v_windows/v/old/vlib/net/http/backend_windows.c.v @@ -0,0 +1,28 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module http + +#flag windows -I @VEXEROOT/thirdparty/vschannel +#flag -l ws2_32 -l crypt32 -l secur32 -l user32 +#include "vschannel.c" + +fn C.new_tls_context() C.TlsContext + +fn (req &Request) ssl_do(port int, method Method, host_name string, path string) ?Response { + mut ctx := C.new_tls_context() + C.vschannel_init(&ctx) + mut buff := unsafe { malloc_noscan(C.vsc_init_resp_buff_size) } + addr := host_name + sdata := req.build_request_headers(method, host_name, path) + $if trace_http_request ? { + eprintln('> $sdata') + } + length := C.request(&ctx, port, addr.to_wide(), sdata.str, &buff) + C.vschannel_cleanup(&ctx) + response_text := unsafe { buff.vstring_with_len(length) } + $if trace_http_response ? { + eprintln('< $response_text') + } + return parse_response(response_text) +} diff --git a/v_windows/v/old/vlib/net/http/chunked/dechunk.v b/v_windows/v/old/vlib/net/http/chunked/dechunk.v new file mode 100644 index 0000000..0e82586 --- /dev/null +++ b/v_windows/v/old/vlib/net/http/chunked/dechunk.v @@ -0,0 +1,72 @@ +module chunked + +import strings +// See: https://en.wikipedia.org/wiki/Chunked_transfer_encoding +// ///////////////////////////////////////////////////////////// +// The chunk size is transferred as a hexadecimal number +// followed by \r\n as a line separator, +// followed by a chunk of data of the given size. +// The end is marked with a chunk with size 0. + +struct ChunkScanner { +mut: + pos int + text string +} + +fn (mut s ChunkScanner) read_chunk_size() int { + mut n := 0 + for { + if s.pos >= s.text.len { + break + } + c := s.text[s.pos] + if !c.is_hex_digit() { + break + } + n = n << 4 + n += int(unhex(c)) + s.pos++ + } + return n +} + +fn unhex(c byte) byte { + if `0` <= c && c <= `9` { + return c - `0` + } else if `a` <= c && c <= `f` { + return c - `a` + 10 + } else if `A` <= c && c <= `F` { + return c - `A` + 10 + } + return 0 +} + +fn (mut s ChunkScanner) skip_crlf() { + s.pos += 2 +} + +fn (mut s ChunkScanner) read_chunk(chunksize int) string { + startpos := s.pos + s.pos += chunksize + return s.text[startpos..s.pos] +} + +pub fn decode(text string) string { + mut sb := strings.new_builder(100) + mut cscanner := ChunkScanner{ + pos: 0 + text: text + } + for { + csize := cscanner.read_chunk_size() + if 0 == csize { + break + } + cscanner.skip_crlf() + sb.write_string(cscanner.read_chunk(csize)) + cscanner.skip_crlf() + } + cscanner.skip_crlf() + return sb.str() +} diff --git a/v_windows/v/old/vlib/net/http/cookie.v b/v_windows/v/old/vlib/net/http/cookie.v new file mode 100644 index 0000000..d647b3d --- /dev/null +++ b/v_windows/v/old/vlib/net/http/cookie.v @@ -0,0 +1,413 @@ +// Copyright (c) 2019 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module http + +import time +import strings + +pub struct Cookie { +pub mut: + name string + value string + path string // optional + domain string // optional + expires time.Time // optional + raw_expires string // for reading cookies only. optional. + // max_age=0 means no 'Max-Age' attribute specified. + // max_age<0 means delete cookie now, equivalently 'Max-Age: 0' + // max_age>0 means Max-Age attribute present and given in seconds + max_age int + secure bool + http_only bool + same_site SameSite + raw string + unparsed []string // Raw text of unparsed attribute-value pairs +} + +// SameSite allows a server to define a cookie attribute making it impossible for +// the browser to send this cookie along with cross-site requests. The main +// goal is to mitigate the risk of cross-origin information leakage, and provide +// some protection against cross-site request forgery attacks. +// +// See https://tools.ietf.org/html/draft-ietf-httpbis-cookie-same-site-00 for details. +pub enum SameSite { + same_site_default_mode = 1 + same_site_lax_mode + same_site_strict_mode + same_site_none_mode +} + +// Parses all "Set-Cookie" values from the header `h` and +// returns the successfully parsed Cookies. +pub fn read_set_cookies(h map[string][]string) []&Cookie { + cookies_s := h['Set-Cookie'] + cookie_count := cookies_s.len + if cookie_count == 0 { + return [] + } + mut cookies := []&Cookie{} + for _, line in cookies_s { + c := parse_cookie(line) or { continue } + cookies << &c + } + return cookies +} + +// Parses all "Cookie" values from the header `h` and +// returns the successfully parsed Cookies. +// +// if `filter` isn't empty, only cookies of that name are returned +pub fn read_cookies(h map[string][]string, filter string) []&Cookie { + lines := h['Cookie'] + if lines.len == 0 { + return [] + } + mut cookies := []&Cookie{} + for _, line_ in lines { + mut line := line_.trim_space() + mut part := '' + for line.len > 0 { + if line.index_any(';') > 0 { + line_parts := line.split(';') + part = line_parts[0] + line = line_parts[1] + } else { + part = line + line = '' + } + part = part.trim_space() + if part.len == 0 { + continue + } + mut name := part + mut val := '' + if part.contains('=') { + val_parts := part.split('=') + name = val_parts[0] + val = val_parts[1] + } + if !is_cookie_name_valid(name) { + continue + } + if filter != '' && filter != name { + continue + } + val = parse_cookie_value(val, true) or { continue } + cookies << &Cookie{ + name: name + value: val + } + } + } + return cookies +} + +// Returns the serialization of the cookie for use in a Cookie header +// (if only Name and Value are set) or a Set-Cookie response +// header (if other fields are set). +// +// If c.name is invalid, the empty string is returned. +pub fn (c &Cookie) str() string { + if !is_cookie_name_valid(c.name) { + return '' + } + // extra_cookie_length derived from typical length of cookie attributes + // see RFC 6265 Sec 4.1. + extra_cookie_length := 110 + mut b := strings.new_builder(c.name.len + c.value.len + c.domain.len + c.path.len + + extra_cookie_length) + b.write_string(c.name) + b.write_string('=') + b.write_string(sanitize_cookie_value(c.value)) + if c.path.len > 0 { + b.write_string('; path=') + b.write_string(sanitize_cookie_path(c.path)) + } + if c.domain.len > 0 { + if valid_cookie_domain(c.domain) { + // A `domain` containing illegal characters is not + // sanitized but simply dropped which turns the cookie + // into a host-only cookie. A leading dot is okay + // but won't be sent. + mut d := c.domain + if d[0] == `.` { + d = d.substr(1, d.len) + } + b.write_string('; domain=') + b.write_string(d) + } else { + // TODO: Log invalid cookie domain warning + } + } + if c.expires.year > 1600 { + e := c.expires + time_str := '$e.weekday_str(), $e.day.str() $e.smonth() $e.year $e.hhmmss() GMT' + b.write_string('; expires=') + b.write_string(time_str) + } + // TODO: Fix this. Techically a max age of 0 or less should be 0 + // We need a way to not have a max age. + if c.max_age > 0 { + b.write_string('; Max-Age=') + b.write_string(c.max_age.str()) + } else if c.max_age < 0 { + b.write_string('; Max-Age=0') + } + if c.http_only { + b.write_string('; HttpOnly') + } + if c.secure { + b.write_string('; Secure') + } + match c.same_site { + .same_site_default_mode { + b.write_string('; SameSite') + } + .same_site_none_mode { + b.write_string('; SameSite=None') + } + .same_site_lax_mode { + b.write_string('; SameSite=Lax') + } + .same_site_strict_mode { + b.write_string('; SameSite=Strict') + } + } + return b.str() +} + +fn sanitize(valid fn (byte) bool, v string) string { + mut ok := true + for i in 0 .. v.len { + if valid(v[i]) { + continue + } + // TODO: Warn that we're dropping the invalid byte? + ok = false + break + } + if ok { + return v.clone() + } + return v.bytes().filter(valid(it)).bytestr() +} + +fn sanitize_cookie_name(name string) string { + return name.replace_each(['\n', '-', '\r', '-']) +} + +// https://tools.ietf.org/html/rfc6265#section-4.1.1 +// cookie-value = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE ) +// cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E +// ; US-ASCII characters excluding CTLs, +// ; whitespace DQUOTE, comma, semicolon, +// ; and backslash +// We loosen this as spaces and commas are common in cookie values +// but we produce a quoted cookie-value in when value starts or ends +// with a comma or space. +pub fn sanitize_cookie_value(v string) string { + val := sanitize(valid_cookie_value_byte, v) + if v.len == 0 { + return v + } + // Check for the existence of a space or comma + if val.starts_with(' ') || val.ends_with(' ') || val.starts_with(',') || val.ends_with(',') { + return '"$v"' + } + return v +} + +fn sanitize_cookie_path(v string) string { + return sanitize(valid_cookie_path_byte, v) +} + +fn valid_cookie_value_byte(b byte) bool { + return 0x20 <= b && b < 0x7f && b != `"` && b != `;` && b != `\\` +} + +fn valid_cookie_path_byte(b byte) bool { + return 0x20 <= b && b < 0x7f && b != `!` +} + +fn valid_cookie_domain(v string) bool { + if is_cookie_domain_name(v) { + return true + } + // TODO + // valid_ip := net.parse_ip(v) or { + // false + // } + // if valid_ip { + // return true + // } + return false +} + +pub fn is_cookie_domain_name(_s string) bool { + mut s := _s + if s.len == 0 { + return false + } + if s.len > 255 { + return false + } + if s[0] == `.` { + s = s.substr(1, s.len) + } + mut last := `.` + mut ok := false + mut part_len := 0 + for i, _ in s { + c := s[i] + if (`a` <= c && c <= `z`) || (`A` <= c && c <= `Z`) { + // No '_' allowed here (in contrast to package net). + ok = true + part_len++ + } else if `0` <= c && c <= `9` { + // fine + part_len++ + } else if c == `-` { + // Byte before dash cannot be dot. + if last == `.` { + return false + } + part_len++ + } else if c == `.` { + // Byte before dot cannot be dot, dash. + if last == `.` || last == `-` { + return false + } + if part_len > 63 || part_len == 0 { + return false + } + part_len = 0 + } else { + return false + } + last = c + } + if last == `-` || part_len > 63 { + return false + } + return ok +} + +fn parse_cookie_value(_raw string, allow_double_quote bool) ?string { + mut raw := _raw + // Strip the quotes, if present + if allow_double_quote && raw.len > 1 && raw[0] == `"` && raw[raw.len - 1] == `"` { + raw = raw.substr(1, raw.len - 1) + } + for i in 0 .. raw.len { + if !valid_cookie_value_byte(raw[i]) { + return error('http.cookie: invalid cookie value') + } + } + return raw +} + +fn is_cookie_name_valid(name string) bool { + if name == '' { + return false + } + for b in name { + if b < 33 || b > 126 { + return false + } + } + return true +} + +fn parse_cookie(line string) ?Cookie { + mut parts := line.trim_space().split(';') + if parts.len == 1 && parts[0] == '' { + return error('malformed cookie') + } + parts[0] = parts[0].trim_space() + keyval := parts[0].split('=') + if keyval.len != 2 { + return error('malformed cookie') + } + name := keyval[0] + raw_value := keyval[1] + if !is_cookie_name_valid(name) { + return error('malformed cookie') + } + value := parse_cookie_value(raw_value, true) or { return error('malformed cookie') } + mut c := Cookie{ + name: name + value: value + raw: line + } + for i, _ in parts { + parts[i] = parts[i].trim_space() + if parts[i].len == 0 { + continue + } + mut attr := parts[i] + mut raw_val := '' + if attr.contains('=') { + pieces := attr.split('=') + attr = pieces[0] + raw_val = pieces[1] + } + lower_attr := attr.to_lower() + val := parse_cookie_value(raw_val, false) or { + c.unparsed << parts[i] + continue + } + match lower_attr { + 'samesite' { + lower_val := val.to_lower() + match lower_val { + 'lax' { c.same_site = .same_site_lax_mode } + 'strict' { c.same_site = .same_site_strict_mode } + 'none' { c.same_site = .same_site_none_mode } + else { c.same_site = .same_site_default_mode } + } + } + 'secure' { + c.secure = true + continue + } + 'httponly' { + c.http_only = true + continue + } + 'domain' { + c.domain = val + continue + } + 'max-age' { + mut secs := val.int() + if secs != 0 && val[0] != `0` { + break + } + if secs <= 0 { + secs = -1 + } + c.max_age = secs + continue + } + // TODO: Fix this once time works better + // 'expires' { + // c.raw_expires = val + // mut exptime := time.parse_iso(val) + // if exptime.year == 0 { + // exptime = time.parse_iso('Mon, 02-Jan-2006 15:04:05 MST') + // } + // c.expires = exptime + // continue + // } + 'path' { + c.path = val + continue + } + else { + c.unparsed << parts[i] + } + } + } + return c +} diff --git a/v_windows/v/old/vlib/net/http/cookie_test.v b/v_windows/v/old/vlib/net/http/cookie_test.v new file mode 100644 index 0000000..3806618 --- /dev/null +++ b/v_windows/v/old/vlib/net/http/cookie_test.v @@ -0,0 +1,468 @@ +import net.http + +struct SetCookieTestCase { + cookie &http.Cookie + raw string +} + +struct ReadSetCookiesTestCase { + header map[string][]string + cookies []&http.Cookie +} + +struct AddCookieTestCase { + cookie []&http.Cookie + raw string +} + +const ( + write_set_cookie_tests = [ + SetCookieTestCase{ + cookie: &http.Cookie{ + name: 'cookie-1' + value: 'v1' + } + raw: 'cookie-1=v1' + }, + SetCookieTestCase{ + cookie: &http.Cookie{ + name: 'cookie-2' + value: 'two' + max_age: 3600 + } + raw: 'cookie-2=two; Max-Age=3600' + }, + SetCookieTestCase{ + cookie: &http.Cookie{ + name: 'cookie-3' + value: 'three' + domain: '.example.com' + } + raw: 'cookie-3=three; domain=example.com' + }, + SetCookieTestCase{ + cookie: &http.Cookie{ + name: 'cookie-4' + value: 'four' + path: '/restricted/' + } + raw: 'cookie-4=four; path=/restricted/' + }, + SetCookieTestCase{ + cookie: &http.Cookie{ + name: 'cookie-5' + value: 'five' + domain: 'wrong;bad.abc' + } + raw: 'cookie-5=five' + }, + SetCookieTestCase{ + cookie: &http.Cookie{ + name: 'cookie-6' + value: 'six' + domain: 'bad-.abc' + } + raw: 'cookie-6=six' + }, + // SetCookieTestCase{ + // cookie: &http.Cookie{name: 'cookie-7', value: 'seven', domain: '127.0.0.1'}, + // raw: 'cookie-7=seven; domain=127.0.0.1' + // }, + SetCookieTestCase{ + cookie: &http.Cookie{ + name: 'cookie-8' + value: 'eight' + domain: '::1' + } + raw: 'cookie-8=eight' + }, + // { + // cookie: &http.Cookie{name: 'cookie-9', value: 'expiring', expires: time.unix(1257894000, 0)}, + // 'cookie-9=expiring; Expires=Tue, 10 Nov 2009 23:00:00 GMT', + // }, + // According to IETF 6265 Section 5.1.1.5, the year cannot be less than 1601 + // SetCookieTestCase{ + // cookie: &http.Cookie{name: 'cookie-10', value: 'expiring-1601', expires: time.parse('Mon, 01 Jan 1601 01:01:01 GMT')}, + // raw: 'cookie-10=expiring-1601; Expires=Mon, 01 Jan 1601 01:01:01 GMT' + // }, + // SetCookieTestCase{ + // cookie: &http.Cookie{name: 'cookie-11', value: 'invalid-expiry', expires: time.parse('Mon, 01 Jan 1600 01:01:01 GMT')}, + // raw: 'cookie-11=invalid-expiry' + // }, + SetCookieTestCase{ + cookie: &http.Cookie{ + name: 'cookie-12' + value: 'samesite-default' + same_site: .same_site_default_mode + } + raw: 'cookie-12=samesite-default; SameSite' + }, + SetCookieTestCase{ + cookie: &http.Cookie{ + name: 'cookie-13' + value: 'samesite-lax' + same_site: .same_site_lax_mode + } + raw: 'cookie-13=samesite-lax; SameSite=Lax' + }, + SetCookieTestCase{ + cookie: &http.Cookie{ + name: 'cookie-14' + value: 'samesite-strict' + same_site: .same_site_strict_mode + } + raw: 'cookie-14=samesite-strict; SameSite=Strict' + }, + SetCookieTestCase{ + cookie: &http.Cookie{ + name: 'cookie-15' + value: 'samesite-none' + same_site: .same_site_none_mode + } + raw: 'cookie-15=samesite-none; SameSite=None' + }, + // The 'special' cookies have values containing commas or spaces which + // are disallowed by RFC 6265 but are common in the wild. + SetCookieTestCase{ + cookie: &http.Cookie{ + name: 'special-1' + value: 'a z' + } + raw: 'special-1=a z' + }, + SetCookieTestCase{ + cookie: &http.Cookie{ + name: 'special-2' + value: ' z' + } + raw: 'special-2=" z"' + }, + SetCookieTestCase{ + cookie: &http.Cookie{ + name: 'special-3' + value: 'a ' + } + raw: 'special-3="a "' + }, + SetCookieTestCase{ + cookie: &http.Cookie{ + name: 'special-4' + value: ' ' + } + raw: 'special-4=" "' + }, + SetCookieTestCase{ + cookie: &http.Cookie{ + name: 'special-5' + value: 'a,z' + } + raw: 'special-5=a,z' + }, + SetCookieTestCase{ + cookie: &http.Cookie{ + name: 'special-6' + value: ',z' + } + raw: 'special-6=",z"' + }, + SetCookieTestCase{ + cookie: &http.Cookie{ + name: 'special-7' + value: 'a,' + } + raw: 'special-7="a,"' + }, + SetCookieTestCase{ + cookie: &http.Cookie{ + name: 'special-8' + value: ',' + } + raw: 'special-8=","' + }, + SetCookieTestCase{ + cookie: &http.Cookie{ + name: 'empty-value' + value: '' + } + raw: 'empty-value=' + }, + SetCookieTestCase{ + cookie: &http.Cookie{ + name: '' + } + raw: '' + }, + SetCookieTestCase{ + cookie: &http.Cookie{ + name: '\t' + } + raw: '' + }, + SetCookieTestCase{ + cookie: &http.Cookie{ + name: '\r' + } + raw: '' + }, + SetCookieTestCase{ + cookie: &http.Cookie{ + name: 'a\nb' + value: 'v' + } + raw: '' + }, + SetCookieTestCase{ + cookie: &http.Cookie{ + name: 'a\nb' + value: 'v' + } + raw: '' + }, + SetCookieTestCase{ + cookie: &http.Cookie{ + name: 'a\rb' + value: 'v' + } + raw: '' + }, + ] + add_cookies_tests = [ + AddCookieTestCase{ + cookie: [] + raw: '' + }, + AddCookieTestCase{ + cookie: [&http.Cookie{ + name: 'cookie-1' + value: 'v1' + }] + raw: 'cookie-1=v1' + }, + AddCookieTestCase{ + cookie: [&http.Cookie{ + name: 'cookie-1' + value: 'v1' + }, + &http.Cookie{ + name: 'cookie-2' + value: 'v2' + }, + &http.Cookie{ + name: 'cookie-3' + value: 'v3' + }, + ] + raw: 'cookie-1=v1; cookie-2=v2; cookie-3=v3' + }, + ] + read_set_cookies_tests = [ + ReadSetCookiesTestCase{ + header: map{ + 'Set-Cookie': ['Cookie-1=v1'] + } + cookies: [&http.Cookie{ + name: 'Cookie-1' + value: 'v1' + raw: 'Cookie-1=v1' + }] + }, + // ReadSetCookiesTestCase{ + // header: {"Set-Cookie": ["NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly"]}, + // cookies: [&http.Cookie{ + // name: "NID", + // value: "99=YsDT5i3E-CXax-", + // path: "/", + // domain: ".google.ch", + // http_only: true, + // expires: time.parse_iso('Wed, 23-Nov-2011 01:05:03 GMT'), + // raw_expires: "Wed, 23-Nov-2011 01:05:03 GMT", + // raw: "NID=99=YsDT5i3E-CXax-; expires=Wed, 23-Nov-2011 01:05:03 GMT; path=/; domain=.google.ch; HttpOnly" + // }] + // }, + // ReadSetCookiesTestCase{ + // header: {"Set-Cookie": [".ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly"]}, + // cookies: [&http.Cookie{ + // name: ".ASPXAUTH", + // value: "7E3AA", + // path: "/", + // expires: time.parse_iso('Wed, 07-Mar-2012 14:25:06 GMT'), + // raw_expires: "Wed, 07-Mar-2012 14:25:06 GMT", + // http_only: true, + // raw: ".ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly" + // }] + // }, + ReadSetCookiesTestCase{ + header: map{ + 'Set-Cookie': ['ASP.NET_SessionId=foo; path=/; HttpOnly'] + } + cookies: [ + &http.Cookie{ + name: 'ASP.NET_SessionId' + value: 'foo' + path: '/' + http_only: true + raw: 'ASP.NET_SessionId=foo; path=/; HttpOnly' + }, + ] + }, + ReadSetCookiesTestCase{ + header: map{ + 'Set-Cookie': ['samesitedefault=foo; SameSite'] + } + cookies: [ + &http.Cookie{ + name: 'samesitedefault' + value: 'foo' + same_site: .same_site_default_mode + raw: 'samesitedefault=foo; SameSite' + }, + ] + }, + ReadSetCookiesTestCase{ + header: map{ + 'Set-Cookie': ['samesitelax=foo; SameSite=Lax'] + } + cookies: [ + &http.Cookie{ + name: 'samesitelax' + value: 'foo' + same_site: .same_site_lax_mode + raw: 'samesitelax=foo; SameSite=Lax' + }, + ] + }, + ReadSetCookiesTestCase{ + header: map{ + 'Set-Cookie': ['samesitestrict=foo; SameSite=Strict'] + } + cookies: [ + &http.Cookie{ + name: 'samesitestrict' + value: 'foo' + same_site: .same_site_strict_mode + raw: 'samesitestrict=foo; SameSite=Strict' + }, + ] + }, + ReadSetCookiesTestCase{ + header: map{ + 'Set-Cookie': ['samesitenone=foo; SameSite=None'] + } + cookies: [ + &http.Cookie{ + name: 'samesitenone' + value: 'foo' + same_site: .same_site_none_mode + raw: 'samesitenone=foo; SameSite=None' + }, + ] + }, + // Make sure we can properly read back the Set-Cookie headers we create + // for values containing spaces or commas: + ReadSetCookiesTestCase{ + header: map{ + 'Set-Cookie': ['special-1=a z'] + } + cookies: [ + &http.Cookie{ + name: 'special-1' + value: 'a z' + raw: 'special-1=a z' + }, + ] + }, + ReadSetCookiesTestCase{ + header: map{ + 'Set-Cookie': ['special-2=" z"'] + } + cookies: [ + &http.Cookie{ + name: 'special-2' + value: ' z' + raw: 'special-2=" z"' + }, + ] + }, + ReadSetCookiesTestCase{ + header: map{ + 'Set-Cookie': ['special-3="a "'] + } + cookies: [ + &http.Cookie{ + name: 'special-3' + value: 'a ' + raw: 'special-3="a "' + }, + ] + }, + ReadSetCookiesTestCase{ + header: map{ + 'Set-Cookie': ['special-4=" "'] + } + cookies: [ + &http.Cookie{ + name: 'special-4' + value: ' ' + raw: 'special-4=" "' + }, + ] + }, + ReadSetCookiesTestCase{ + header: map{ + 'Set-Cookie': ['special-5=a,z'] + } + cookies: [ + &http.Cookie{ + name: 'special-5' + value: 'a,z' + raw: 'special-5=a,z' + }, + ] + }, + ReadSetCookiesTestCase{ + header: map{ + 'Set-Cookie': ['special-6=",z"'] + } + cookies: [ + &http.Cookie{ + name: 'special-6' + value: ',z' + raw: 'special-6=",z"' + }, + ] + }, + ReadSetCookiesTestCase{ + header: map{ + 'Set-Cookie': ['special-7=","'] + } + cookies: [ + &http.Cookie{ + name: 'special-7' + value: ',' + raw: 'special-8=","' + }, + ] + } + // TODO(bradfitz): users have reported seeing this in the + // wild, but do browsers handle it? RFC 6265 just says "don't + // do that" (section 3) and then never mentions header folding + // again. + // Header{"Set-Cookie": ["ASP.NET_SessionId=foo; path=/; HttpOnly, .ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly"]}, + ] +) + +fn test_write_set_cookies() { + for _, tt in write_set_cookie_tests { + assert tt.cookie.str() == tt.raw + } +} + +fn test_read_set_cookies() { + for _, tt in read_set_cookies_tests { + h := tt.header['Set-Cookie'][0] + c := http.read_set_cookies(tt.header) + println(h) + println(c[0].str()) + assert c[0].str() == h + } +} diff --git a/v_windows/v/old/vlib/net/http/download.v b/v_windows/v/old/vlib/net/http/download.v new file mode 100644 index 0000000..455c1e0 --- /dev/null +++ b/v_windows/v/old/vlib/net/http/download.v @@ -0,0 +1,18 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module http + +import os + +pub fn download_file(url string, out string) ? { + $if debug_http ? { + println('download file url=$url out=$out') + } + s := get(url) or { return err } + if s.status() != .ok { + return error('received http code $s.status_code') + } + os.write_file(out, s.text) ? + // download_file_with_progress(url, out, empty, empty) +} diff --git a/v_windows/v/old/vlib/net/http/download_nix.c.v b/v_windows/v/old/vlib/net/http/download_nix.c.v new file mode 100644 index 0000000..724a256 --- /dev/null +++ b/v_windows/v/old/vlib/net/http/download_nix.c.v @@ -0,0 +1,52 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module http + +type DownloadFn = fn (written int) + +/* +struct DownloadStruct { +mut: + stream voidptr + written int + cb DownloadFn +} +*/ +fn download_cb(ptr voidptr, size size_t, nmemb size_t, userp voidptr) { + /* + mut data := &DownloadStruct(userp) + written := C.fwrite(ptr, size, nmemb, data.stream) + data.written += written + data.cb(data.written) + //#data->cb(data->written); // TODO + return written + */ +} + +pub fn download_file_with_progress(url string, out string, cb DownloadFn, cb_finished fn ()) { + /* + curl := C.curl_easy_init() + if isnil(curl) { + return + } + cout := out.str + fp := C.fopen(cout, 'wb') + C.curl_easy_setopt(curl, CURLOPT_URL, url.str) + C.curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, download_cb) + data := &DownloadStruct { + stream:fp + cb: cb + } + C.curl_easy_setopt(curl, CURLOPT_WRITEDATA, data) + mut d := 0.0 + C.curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &d) + C.curl_easy_perform(curl) + C.curl_easy_cleanup(curl) + C.fclose(fp) + cb_finished() + */ +} + +fn empty() { +} diff --git a/v_windows/v/old/vlib/net/http/download_windows.c.v b/v_windows/v/old/vlib/net/http/download_windows.c.v new file mode 100644 index 0000000..422b6da --- /dev/null +++ b/v_windows/v/old/vlib/net/http/download_windows.c.v @@ -0,0 +1,29 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. + +module http + +#flag -l urlmon + +#include + +fn download_file_with_progress(url string, out string, cb voidptr, cb_finished voidptr) { +} + +/* +pub fn download_file(url, out string) { + C.URLDownloadToFile(0, url.to_wide(), out.to_wide(), 0, 0) + /* + if (res == S_OK) { + println('Download Ok') + # } else if(res == E_OUTOFMEMORY) { + println('Buffer length invalid, or insufficient memory') + # } else if(res == INET_E_DOWNLOAD_FAILURE) { + println('URL is invalid') + # } else { + # printf("Download error: %d\n", res); + # } + */ +} +*/ diff --git a/v_windows/v/old/vlib/net/http/header.v b/v_windows/v/old/vlib/net/http/header.v new file mode 100644 index 0000000..e96563e --- /dev/null +++ b/v_windows/v/old/vlib/net/http/header.v @@ -0,0 +1,700 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module http + +import strings + +// CommonHeader is an enum of the most common HTTP headers +pub enum CommonHeader { + accept + accept_ch + accept_charset + accept_ch_lifetime + accept_encoding + accept_language + accept_patch + accept_post + accept_ranges + access_control_allow_credentials + access_control_allow_headers + access_control_allow_methods + access_control_allow_origin + access_control_expose_headers + access_control_max_age + access_control_request_headers + access_control_request_method + age + allow + alt_svc + authorization + cache_control + clear_site_data + connection + content_disposition + content_encoding + content_language + content_length + content_location + content_range + content_security_policy + content_security_policy_report_only + content_type + cookie + cross_origin_embedder_policy + cross_origin_opener_policy + cross_origin_resource_policy + date + device_memory + digest + dnt + early_data + etag + expect + expect_ct + expires + feature_policy + forwarded + from + host + if_match + if_modified_since + if_none_match + if_range + if_unmodified_since + index + keep_alive + large_allocation + last_modified + link + location + nel + origin + pragma + proxy_authenticate + proxy_authorization + range + referer + referrer_policy + retry_after + save_data + sec_fetch_dest + sec_fetch_mode + sec_fetch_site + sec_fetch_user + sec_websocket_accept + server + server_timing + set_cookie + sourcemap + strict_transport_security + te + timing_allow_origin + tk + trailer + transfer_encoding + upgrade + upgrade_insecure_requests + user_agent + vary + via + want_digest + warning + www_authenticate + x_content_type_options + x_dns_prefetch_control + x_forwarded_for + x_forwarded_host + x_forwarded_proto + x_frame_options + x_xss_protection +} + +pub fn (h CommonHeader) str() string { + return match h { + .accept { 'Accept' } + .accept_ch { 'Accept-CH' } + .accept_charset { 'Accept-Charset' } + .accept_ch_lifetime { 'Accept-CH-Lifetime' } + .accept_encoding { 'Accept-Encoding' } + .accept_language { 'Accept-Language' } + .accept_patch { 'Accept-Patch' } + .accept_post { 'Accept-Post' } + .accept_ranges { 'Accept-Ranges' } + .access_control_allow_credentials { 'Access-Control-Allow-Credentials' } + .access_control_allow_headers { 'Access-Control-Allow-Headers' } + .access_control_allow_methods { 'Access-Control-Allow-Methods' } + .access_control_allow_origin { 'Access-Control-Allow-Origin' } + .access_control_expose_headers { 'Access-Control-Expose-Headers' } + .access_control_max_age { 'Access-Control-Max-Age' } + .access_control_request_headers { 'Access-Control-Request-Headers' } + .access_control_request_method { 'Access-Control-Request-Method' } + .age { 'Age' } + .allow { 'Allow' } + .alt_svc { 'Alt-Svc' } + .authorization { 'Authorization' } + .cache_control { 'Cache-Control' } + .clear_site_data { 'Clear-Site-Data' } + .connection { 'Connection' } + .content_disposition { 'Content-Disposition' } + .content_encoding { 'Content-Encoding' } + .content_language { 'Content-Language' } + .content_length { 'Content-Length' } + .content_location { 'Content-Location' } + .content_range { 'Content-Range' } + .content_security_policy { 'Content-Security-Policy' } + .content_security_policy_report_only { 'Content-Security-Policy-Report-Only' } + .content_type { 'Content-Type' } + .cookie { 'Cookie' } + .cross_origin_embedder_policy { 'Cross-Origin-Embedder-Policy' } + .cross_origin_opener_policy { 'Cross-Origin-Opener-Policy' } + .cross_origin_resource_policy { 'Cross-Origin-Resource-Policy' } + .date { 'Date' } + .device_memory { 'Device-Memory' } + .digest { 'Digest' } + .dnt { 'DNT' } + .early_data { 'Early-Data' } + .etag { 'ETag' } + .expect { 'Expect' } + .expect_ct { 'Expect-CT' } + .expires { 'Expires' } + .feature_policy { 'Feature-Policy' } + .forwarded { 'Forwarded' } + .from { 'From' } + .host { 'Host' } + .if_match { 'If-Match' } + .if_modified_since { 'If-Modified-Since' } + .if_none_match { 'If-None-Match' } + .if_range { 'If-Range' } + .if_unmodified_since { 'If-Unmodified-Since' } + .index { 'Index' } + .keep_alive { 'Keep-Alive' } + .large_allocation { 'Large-Allocation' } + .last_modified { 'Last-Modified' } + .link { 'Link' } + .location { 'Location' } + .nel { 'NEL' } + .origin { 'Origin' } + .pragma { 'Pragma' } + .proxy_authenticate { 'Proxy-Authenticate' } + .proxy_authorization { 'Proxy-Authorization' } + .range { 'Range' } + .referer { 'Referer' } + .referrer_policy { 'Referrer-Policy' } + .retry_after { 'Retry-After' } + .save_data { 'Save-Data' } + .sec_fetch_dest { 'Sec-Fetch-Dest' } + .sec_fetch_mode { 'Sec-Fetch-Mode' } + .sec_fetch_site { 'Sec-Fetch-Site' } + .sec_fetch_user { 'Sec-Fetch-User' } + .sec_websocket_accept { 'Sec-WebSocket-Accept' } + .server { 'Server' } + .server_timing { 'Server-Timing' } + .set_cookie { 'Set-Cookie' } + .sourcemap { 'SourceMap' } + .strict_transport_security { 'Strict-Transport-Security' } + .te { 'TE' } + .timing_allow_origin { 'Timing-Allow-Origin' } + .tk { 'Tk' } + .trailer { 'Trailer' } + .transfer_encoding { 'Transfer-Encoding' } + .upgrade { 'Upgrade' } + .upgrade_insecure_requests { 'Upgrade-Insecure-Requests' } + .user_agent { 'User-Agent' } + .vary { 'Vary' } + .via { 'Via' } + .want_digest { 'Want-Digest' } + .warning { 'Warning' } + .www_authenticate { 'WWW-Authenticate' } + .x_content_type_options { 'X-Content-Type-Options' } + .x_dns_prefetch_control { 'X-DNS-Prefetch-Control' } + .x_forwarded_for { 'X-Forwarded-For' } + .x_forwarded_host { 'X-Forwarded-Host' } + .x_forwarded_proto { 'X-Forwarded-Proto' } + .x_frame_options { 'X-Frame-Options' } + .x_xss_protection { 'X-XSS-Protection' } + } +} + +const common_header_map = map{ + 'accept': CommonHeader.accept + 'accept-ch': .accept_ch + 'accept-charset': .accept_charset + 'accept-ch-lifetime': .accept_ch_lifetime + 'accept-encoding': .accept_encoding + 'accept-language': .accept_language + 'accept-patch': .accept_patch + 'accept-post': .accept_post + 'accept-ranges': .accept_ranges + 'access-control-allow-credentials': .access_control_allow_credentials + 'access-control-allow-headers': .access_control_allow_headers + 'access-control-allow-methods': .access_control_allow_methods + 'access-control-allow-origin': .access_control_allow_origin + 'access-control-expose-headers': .access_control_expose_headers + 'access-control-max-age': .access_control_max_age + 'access-control-request-headers': .access_control_request_headers + 'access-control-request-method': .access_control_request_method + 'age': .age + 'allow': .allow + 'alt-svc': .alt_svc + 'authorization': .authorization + 'cache-control': .cache_control + 'clear-site-data': .clear_site_data + 'connection': .connection + 'content-disposition': .content_disposition + 'content-encoding': .content_encoding + 'content-language': .content_language + 'content-length': .content_length + 'content-location': .content_location + 'content-range': .content_range + 'content-security-policy': .content_security_policy + 'content-security-policy-report-only': .content_security_policy_report_only + 'content-type': .content_type + 'cookie': .cookie + 'cross-origin-embedder-policy': .cross_origin_embedder_policy + 'cross-origin-opener-policy': .cross_origin_opener_policy + 'cross-origin-resource-policy': .cross_origin_resource_policy + 'date': .date + 'device-memory': .device_memory + 'digest': .digest + 'dnt': .dnt + 'early-data': .early_data + 'etag': .etag + 'expect': .expect + 'expect-ct': .expect_ct + 'expires': .expires + 'feature-policy': .feature_policy + 'forwarded': .forwarded + 'from': .from + 'host': .host + 'if-match': .if_match + 'if-modified-since': .if_modified_since + 'if-none-match': .if_none_match + 'if-range': .if_range + 'if-unmodified-since': .if_unmodified_since + 'index': .index + 'keep-alive': .keep_alive + 'large-allocation': .large_allocation + 'last-modified': .last_modified + 'link': .link + 'location': .location + 'nel': .nel + 'origin': .origin + 'pragma': .pragma + 'proxy-authenticate': .proxy_authenticate + 'proxy-authorization': .proxy_authorization + 'range': .range + 'referer': .referer + 'referrer-policy': .referrer_policy + 'retry-after': .retry_after + 'save-data': .save_data + 'sec-fetch-dest': .sec_fetch_dest + 'sec-fetch-mode': .sec_fetch_mode + 'sec-fetch-site': .sec_fetch_site + 'sec-fetch-user': .sec_fetch_user + 'sec-websocket-accept': .sec_websocket_accept + 'server': .server + 'server-timing': .server_timing + 'set-cookie': .set_cookie + 'sourcemap': .sourcemap + 'strict-transport-security': .strict_transport_security + 'te': .te + 'timing-allow-origin': .timing_allow_origin + 'tk': .tk + 'trailer': .trailer + 'transfer-encoding': .transfer_encoding + 'upgrade': .upgrade + 'upgrade-insecure-requests': .upgrade_insecure_requests + 'user-agent': .user_agent + 'vary': .vary + 'via': .via + 'want-digest': .want_digest + 'warning': .warning + 'www-authenticate': .www_authenticate + 'x-content-type-options': .x_content_type_options + 'x-dns-prefetch-control': .x_dns_prefetch_control + 'x-forwarded-for': .x_forwarded_for + 'x-forwarded-host': .x_forwarded_host + 'x-forwarded-proto': .x_forwarded_proto + 'x-frame-options': .x_frame_options + 'x-xss-protection': .x_xss_protection +} + +// Header represents the key-value pairs in an HTTP header +[noinit] +pub struct Header { +mut: + data map[string][]string + // map of lowercase header keys to their original keys + // in order of appearance + keys map[string][]string +} + +pub fn (mut h Header) free() { + unsafe { + h.data.free() + h.keys.free() + } +} + +pub struct HeaderConfig { + key CommonHeader + value string +} + +// Create a new Header object +pub fn new_header(kvs ...HeaderConfig) Header { + mut h := Header{ + data: map[string][]string{} + } + for kv in kvs { + h.add(kv.key, kv.value) + } + return h +} + +// new_header_from_map creates a Header from key value pairs +pub fn new_header_from_map(kvs map[CommonHeader]string) Header { + mut h := new_header() + h.add_map(kvs) + return h +} + +// new_custom_header_from_map creates a Header from string key value pairs +pub fn new_custom_header_from_map(kvs map[string]string) ?Header { + mut h := new_header() + h.add_custom_map(kvs) ? + return h +} + +// add appends a value to the header key. +pub fn (mut h Header) add(key CommonHeader, value string) { + k := key.str() + h.data[k] << value + h.add_key(k) +} + +// add_custom appends a value to a custom header key. This function will +// return an error if the key contains invalid header characters. +pub fn (mut h Header) add_custom(key string, value string) ? { + is_valid(key) ? + h.data[key] << value + h.add_key(key) +} + +// add_map appends the value for each header key. +pub fn (mut h Header) add_map(kvs map[CommonHeader]string) { + for k, v in kvs { + h.add(k, v) + } +} + +// add_custom_map appends the value for each custom header key. +pub fn (mut h Header) add_custom_map(kvs map[string]string) ? { + for k, v in kvs { + h.add_custom(k, v) ? + } +} + +// set sets the key-value pair. This function will clear any other values +// that exist for the CommonHeader. +pub fn (mut h Header) set(key CommonHeader, value string) { + k := key.str() + h.data[k] = [value] + h.add_key(k) +} + +// set_custom sets the key-value pair for a custom header key. This +// function will clear any other values that exist for the header. This +// function will return an error if the key contains invalid header +// characters. +pub fn (mut h Header) set_custom(key string, value string) ? { + is_valid(key) ? + h.data[key] = [value] + h.add_key(key) +} + +// delete deletes all values for a key. +pub fn (mut h Header) delete(key CommonHeader) { + h.delete_custom(key.str()) +} + +// delete_custom deletes all values for a custom header key. +pub fn (mut h Header) delete_custom(key string) { + h.data.delete(key) + + // remove key from keys metadata + kl := key.to_lower() + if kl in h.keys { + h.keys[kl] = h.keys[kl].filter(it != key) + } +} + +pub struct HeaderCoerceConfig { + canonicalize bool +} + +// coerce coerces data in the Header by joining keys that match +// case-insensitively into one entry. +pub fn (mut h Header) coerce(flags ...HeaderCoerceConfig) { + canon := flags.any(it.canonicalize) + + for kl, data_keys in h.keys { + master_key := if canon { canonicalize(kl) } else { data_keys[0] } + + // save master data + master_data := h.data[master_key] + h.data.delete(master_key) + + for key in data_keys { + if key == master_key { + h.data[master_key] << master_data + continue + } + h.data[master_key] << h.data[key] + h.data.delete(key) + } + h.keys[kl] = [master_key] + } +} + +// contains returns whether the header key exists in the map. +pub fn (h Header) contains(key CommonHeader) bool { + return h.contains_custom(key.str()) +} + +pub struct HeaderQueryConfig { + exact bool +} + +// contains_custom returns whether the custom header key exists in the map. +pub fn (h Header) contains_custom(key string, flags ...HeaderQueryConfig) bool { + if flags.any(it.exact) { + return key in h.data + } + return key.to_lower() in h.keys +} + +// get gets the first value for the CommonHeader, or none if the key +// does not exist. +pub fn (h Header) get(key CommonHeader) ?string { + return h.get_custom(key.str()) +} + +// get_custom gets the first value for the custom header, or none if +// the key does not exist. +pub fn (h Header) get_custom(key string, flags ...HeaderQueryConfig) ?string { + mut data_key := key + if !flags.any(it.exact) { + // get the first key from key metadata + k := key.to_lower() + if h.keys[k].len == 0 { + return none + } + data_key = h.keys[k][0] + } + if h.data[data_key].len == 0 { + return none + } + return h.data[data_key][0] +} + +// starting_with gets the first header starting with key, or none if +// the key does not exist. +pub fn (h Header) starting_with(key string) ?string { + for k, _ in h.data { + if k.starts_with(key) { + return k + } + } + return none +} + +// values gets all values for the CommonHeader. +pub fn (h Header) values(key CommonHeader) []string { + return h.custom_values(key.str()) +} + +// custom_values gets all values for the custom header. +pub fn (h Header) custom_values(key string, flags ...HeaderQueryConfig) []string { + if flags.any(it.exact) { + return h.data[key] + } + // case insensitive lookup + mut values := []string{cap: 10} + for k in h.keys[key.to_lower()] { + values << h.data[k] + } + return values +} + +// keys gets all header keys as strings +pub fn (h Header) keys() []string { + return h.data.keys() +} + +pub struct HeaderRenderConfig { + version Version + coerce bool + canonicalize bool +} + +// render renders the Header into a string for use in sending HTTP +// requests. All header lines will end in `\r\n` +[manualfree] +pub fn (h Header) render(flags HeaderRenderConfig) string { + // estimate ~48 bytes per header + mut sb := strings.new_builder(h.data.len * 48) + if flags.coerce { + for kl, data_keys in h.keys { + key := if flags.version == .v2_0 { + kl + } else if flags.canonicalize { + canonicalize(kl) + } else { + data_keys[0] + } + sb.write_string(key) + sb.write_string(': ') + for i in 0 .. data_keys.len - 1 { + k := data_keys[i] + for v in h.data[k] { + sb.write_string(v) + sb.write_string(',') + } + } + k := data_keys[data_keys.len - 1] + sb.write_string(h.data[k].join(',')) + sb.write_string('\r\n') + } + } else { + for k, v in h.data { + key := if flags.version == .v2_0 { + k.to_lower() + } else if flags.canonicalize { + canonicalize(k.to_lower()) + } else { + k + } + sb.write_string(key) + sb.write_string(': ') + sb.write_string(v.join(',')) + sb.write_string('\r\n') + } + } + res := sb.str() + unsafe { sb.free() } + return res +} + +// join combines two Header structs into a new Header struct +pub fn (h Header) join(other Header) Header { + mut combined := Header{ + data: h.data.clone() + keys: h.keys.clone() + } + for k in other.keys() { + for v in other.custom_values(k, exact: true) { + combined.add_custom(k, v) or { + // panic because this should never fail + panic('unexpected error: $err') + } + } + } + return combined +} + +// canonicalize canonicalizes an HTTP header key +// Common headers are determined by the common_header_map +// Custom headers are capitalized on the first letter and any letter after a '-' +// NOTE: Assumes sl is lowercase, since the caller usually already has the lowercase key +fn canonicalize(sl string) string { + // check if we have a common header + if sl in http.common_header_map { + return http.common_header_map[sl].str() + } + return sl.split('-').map(it.capitalize()).join('-') +} + +// Helper function to add a key to the keys map +fn (mut h Header) add_key(key string) { + kl := key.to_lower() + if !h.keys[kl].contains(key) { + h.keys[kl] << key + } +} + +// Custom error struct for invalid header tokens +struct HeaderKeyError { + msg string + code int + header string + invalid_char byte +} + +// is_valid checks if the header token contains all valid bytes +fn is_valid(header string) ? { + for _, c in header { + if int(c) >= 128 || !is_token(c) { + return IError(HeaderKeyError{ + msg: "Invalid header key: '$header'" + code: 1 + header: header + invalid_char: c + }) + } + } + if header.len == 0 { + return IError(HeaderKeyError{ + msg: "Invalid header key: '$header'" + code: 2 + header: header + invalid_char: 0 + }) + } +} + +// is_token checks if the byte is valid for a header token +fn is_token(b byte) bool { + return match b { + 33, 35...39, 42, 43, 45, 46, 48...57, 65...90, 94...122, 124, 126 { true } + else { false } + } +} + +// str returns the headers string as seen in HTTP/1.1 requests. +// Key order is not guaranteed. +pub fn (h Header) str() string { + return h.render(version: .v1_1) +} + +// parse_headers parses a newline delimited string into a Header struct +fn parse_headers(s string) ?Header { + mut h := new_header() + mut last_key := '' + mut last_value := '' + for line in s.split_into_lines() { + if line.len == 0 { + break + } + // handle header fold + if line[0] == ` ` || line[0] == `\t` { + last_value += ' ${line.trim(' \t')}' + continue + } else if last_key != '' { + h.add_custom(last_key, last_value) ? + } + last_key, last_value = parse_header(line) ? + } + h.add_custom(last_key, last_value) ? + return h +} + +fn parse_header(s string) ?(string, string) { + if !s.contains(':') { + return error('missing colon in header') + } + words := s.split_nth(':', 2) + // TODO: parse quoted text according to the RFC + return words[0], words[1].trim(' \t') +} diff --git a/v_windows/v/old/vlib/net/http/header_test.v b/v_windows/v/old/vlib/net/http/header_test.v new file mode 100644 index 0000000..3740d8a --- /dev/null +++ b/v_windows/v/old/vlib/net/http/header_test.v @@ -0,0 +1,361 @@ +module http + +fn test_header_new() { + h := new_header(HeaderConfig{ key: .accept, value: 'nothing' }, + key: .expires + value: 'yesterday' + ) + assert h.contains(.accept) + assert h.contains(.expires) + accept := h.get(.accept) or { '' } + expires := h.get(.expires) or { '' } + assert accept == 'nothing' + assert expires == 'yesterday' +} + +fn test_header_invalid_key() { + mut h := new_header() + h.add_custom('space is invalid', ':(') or { return } + panic('should have returned') +} + +fn test_header_adds_multiple() { + mut h := new_header() + h.add(.accept, 'one') + h.add(.accept, 'two') + + assert h.values(.accept) == ['one', 'two'] +} + +fn test_header_get() ? { + mut h := new_header(key: .dnt, value: 'one') + h.add_custom('dnt', 'two') ? + dnt := h.get_custom('dnt') or { '' } + exact := h.get_custom('dnt', exact: true) or { '' } + assert dnt == 'one' + assert exact == 'two' +} + +fn test_header_set() ? { + mut h := new_header(HeaderConfig{ key: .dnt, value: 'one' }, + key: .dnt + value: 'two' + ) + assert h.values(.dnt) == ['one', 'two'] + h.set_custom('DNT', 'three') ? + assert h.values(.dnt) == ['three'] +} + +fn test_header_delete() { + mut h := new_header(HeaderConfig{ key: .dnt, value: 'one' }, + key: .dnt + value: 'two' + ) + assert h.values(.dnt) == ['one', 'two'] + h.delete(.dnt) + assert h.values(.dnt) == [] +} + +fn test_header_delete_not_existing() { + mut h := new_header() + assert h.data.len == 0 + assert h.keys.len == 0 + h.delete(.dnt) + assert h.data.len == 0 + assert h.keys.len == 0 +} + +fn test_custom_header() ? { + mut h := new_header() + h.add_custom('AbC', 'dEf') ? + h.add_custom('aBc', 'GhI') ? + assert h.custom_values('AbC', exact: true) == ['dEf'] + assert h.custom_values('aBc', exact: true) == ['GhI'] + assert h.custom_values('ABC') == ['dEf', 'GhI'] + assert h.custom_values('abc') == ['dEf', 'GhI'] + assert h.keys() == ['AbC', 'aBc'] + h.delete_custom('AbC') + h.delete_custom('aBc') + + h.add_custom('abc', 'def') ? + assert h.custom_values('abc') == ['def'] + assert h.custom_values('ABC') == ['def'] + assert h.keys() == ['abc'] + h.delete_custom('abc') + + h.add_custom('accEPT', '*/*') ? + assert h.custom_values('ACCept') == ['*/*'] + assert h.values(.accept) == ['*/*'] + assert h.keys() == ['accEPT'] +} + +fn test_contains_custom() ? { + mut h := new_header() + h.add_custom('Hello', 'world') ? + assert h.contains_custom('hello') + assert h.contains_custom('HELLO') + assert h.contains_custom('Hello', exact: true) + assert h.contains_custom('hello', exact: true) == false + assert h.contains_custom('HELLO', exact: true) == false +} + +fn test_get_custom() ? { + mut h := new_header() + h.add_custom('Hello', 'world') ? + assert h.get_custom('hello') ? == 'world' + assert h.get_custom('HELLO') ? == 'world' + assert h.get_custom('Hello', exact: true) ? == 'world' + if _ := h.get_custom('hello', exact: true) { + // should be none + assert false + } + if _ := h.get_custom('HELLO', exact: true) { + // should be none + assert false + } +} + +fn test_starting_with() ? { + mut h := new_header() + h.add_custom('Hello-1', 'world') ? + h.add_custom('Hello-21', 'world') ? + assert h.starting_with('Hello-') ? == 'Hello-1' + assert h.starting_with('Hello-2') ? == 'Hello-21' +} + +fn test_custom_values() ? { + mut h := new_header() + h.add_custom('Hello', 'world') ? + assert h.custom_values('hello') == ['world'] + assert h.custom_values('HELLO') == ['world'] + assert h.custom_values('Hello', exact: true) == ['world'] + assert h.custom_values('hello', exact: true) == [] + assert h.custom_values('HELLO', exact: true) == [] +} + +fn test_coerce() ? { + mut h := new_header() + h.add_custom('accept', 'foo') ? + h.add(.accept, 'bar') + assert h.values(.accept) == ['foo', 'bar'] + assert h.keys().len == 2 + + h.coerce() + assert h.values(.accept) == ['foo', 'bar'] + assert h.keys() == ['accept'] // takes the first occurrence +} + +fn test_coerce_canonicalize() ? { + mut h := new_header() + h.add_custom('accept', 'foo') ? + h.add(.accept, 'bar') + assert h.values(.accept) == ['foo', 'bar'] + assert h.keys().len == 2 + + h.coerce(canonicalize: true) + assert h.values(.accept) == ['foo', 'bar'] + assert h.keys() == ['Accept'] // canonicalize header +} + +fn test_coerce_custom() ? { + mut h := new_header() + h.add_custom('Hello', 'foo') ? + h.add_custom('hello', 'bar') ? + h.add_custom('HELLO', 'baz') ? + assert h.custom_values('hello') == ['foo', 'bar', 'baz'] + assert h.keys().len == 3 + + h.coerce() + assert h.custom_values('hello') == ['foo', 'bar', 'baz'] + assert h.keys() == ['Hello'] // takes the first occurrence +} + +fn test_coerce_canonicalize_custom() ? { + mut h := new_header() + h.add_custom('foo-BAR', 'foo') ? + h.add_custom('FOO-bar', 'bar') ? + assert h.custom_values('foo-bar') == ['foo', 'bar'] + assert h.keys().len == 2 + + h.coerce(canonicalize: true) + assert h.custom_values('foo-bar') == ['foo', 'bar'] + assert h.keys() == ['Foo-Bar'] // capitalizes the header +} + +fn test_render_version() ? { + mut h := new_header() + h.add_custom('accept', 'foo') ? + h.add_custom('Accept', 'bar') ? + h.add(.accept, 'baz') + + s1_0 := h.render(version: .v1_0) + assert s1_0.contains('accept: foo\r\n') + assert s1_0.contains('Accept: bar,baz\r\n') + + s1_1 := h.render(version: .v1_1) + assert s1_1.contains('accept: foo\r\n') + assert s1_1.contains('Accept: bar,baz\r\n') + + s2_0 := h.render(version: .v2_0) + assert s2_0.contains('accept: foo\r\n') + assert s2_0.contains('accept: bar,baz\r\n') +} + +fn test_render_coerce() ? { + mut h := new_header() + h.add_custom('accept', 'foo') ? + h.add_custom('Accept', 'bar') ? + h.add(.accept, 'baz') + h.add(.host, 'host') + + s1_0 := h.render(version: .v1_1, coerce: true) + assert s1_0.contains('accept: foo,bar,baz\r\n') + assert s1_0.contains('Host: host\r\n') + + s1_1 := h.render(version: .v1_1, coerce: true) + assert s1_1.contains('accept: foo,bar,baz\r\n') + assert s1_1.contains('Host: host\r\n') + + s2_0 := h.render(version: .v2_0, coerce: true) + assert s2_0.contains('accept: foo,bar,baz\r\n') + assert s2_0.contains('host: host\r\n') +} + +fn test_render_canonicalize() ? { + mut h := new_header() + h.add_custom('accept', 'foo') ? + h.add_custom('Accept', 'bar') ? + h.add(.accept, 'baz') + h.add(.host, 'host') + + s1_0 := h.render(version: .v1_1, canonicalize: true) + assert s1_0.contains('Accept: foo\r\n') + assert s1_0.contains('Accept: bar,baz\r\n') + assert s1_0.contains('Host: host\r\n') + + s1_1 := h.render(version: .v1_1, canonicalize: true) + assert s1_1.contains('Accept: foo\r\n') + assert s1_1.contains('Accept: bar,baz\r\n') + assert s1_1.contains('Host: host\r\n') + + s2_0 := h.render(version: .v2_0, canonicalize: true) + assert s2_0.contains('accept: foo\r\n') + assert s2_0.contains('accept: bar,baz\r\n') + assert s2_0.contains('host: host\r\n') +} + +fn test_render_coerce_canonicalize() ? { + mut h := new_header() + h.add_custom('accept', 'foo') ? + h.add_custom('Accept', 'bar') ? + h.add(.accept, 'baz') + h.add(.host, 'host') + + s1_0 := h.render(version: .v1_1, coerce: true, canonicalize: true) + assert s1_0.contains('Accept: foo,bar,baz\r\n') + assert s1_0.contains('Host: host\r\n') + + s1_1 := h.render(version: .v1_1, coerce: true, canonicalize: true) + assert s1_1.contains('Accept: foo,bar,baz\r\n') + assert s1_1.contains('Host: host\r\n') + + s2_0 := h.render(version: .v2_0, coerce: true, canonicalize: true) + assert s2_0.contains('accept: foo,bar,baz\r\n') + assert s2_0.contains('host: host\r\n') +} + +fn test_str() ? { + mut h := new_header() + h.add(.accept, 'text/html') + h.add_custom('Accept', 'image/jpeg') ? + h.add_custom('X-custom', 'Hello') ? + + // key order is not guaranteed + assert h.str() == 'Accept: text/html,image/jpeg\r\nX-custom: Hello\r\n' + || h.str() == 'X-custom: Hello\r\nAccept:text/html,image/jpeg\r\n' +} + +fn test_header_from_map() ? { + h := new_header_from_map(map{ + CommonHeader.accept: 'nothing' + CommonHeader.expires: 'yesterday' + }) + assert h.contains(.accept) + assert h.contains(.expires) + assert h.get(.accept) or { '' } == 'nothing' + assert h.get(.expires) or { '' } == 'yesterday' +} + +fn test_custom_header_from_map() ? { + h := new_custom_header_from_map(map{ + 'Server': 'VWeb' + 'foo': 'bar' + }) ? + assert h.contains_custom('server') + assert h.contains_custom('foo') + assert h.get_custom('server') or { '' } == 'VWeb' + assert h.get_custom('foo') or { '' } == 'bar' +} + +fn test_header_join() ? { + h1 := new_header_from_map(map{ + CommonHeader.accept: 'nothing' + CommonHeader.expires: 'yesterday' + }) + h2 := new_custom_header_from_map(map{ + 'Server': 'VWeb' + 'foo': 'bar' + }) ? + h3 := h1.join(h2) + // h1 is unchanged + assert h1.contains(.accept) + assert h1.contains(.expires) + assert !h1.contains_custom('Server') + assert !h1.contains_custom('foo') + // h2 is unchanged + assert !h2.contains(.accept) + assert !h2.contains(.expires) + assert h2.contains_custom('Server') + assert h2.contains_custom('foo') + // h3 has all four headers + assert h3.contains(.accept) + assert h3.contains(.expires) + assert h3.contains_custom('Server') + assert h3.contains_custom('foo') +} + +fn parse_headers_test(s string, expected map[string]string) ? { + assert parse_headers(s) ? == new_custom_header_from_map(expected) ? +} + +fn test_parse_headers() ? { + parse_headers_test('foo: bar', map{ + 'foo': 'bar' + }) ? + parse_headers_test('foo: \t bar', map{ + 'foo': 'bar' + }) ? + parse_headers_test('foo: bar\r\n\tbaz', map{ + 'foo': 'bar baz' + }) ? + parse_headers_test('foo: bar \r\n\tbaz\r\n buzz', map{ + 'foo': 'bar baz buzz' + }) ? + parse_headers_test('foo: bar\r\nbar:baz', map{ + 'foo': 'bar' + 'bar': 'baz' + }) ? + parse_headers_test('foo: bar\r\nbar:baz\r\n', map{ + 'foo': 'bar' + 'bar': 'baz' + }) ? + parse_headers_test('foo: bar\r\nbar:baz\r\n\r\n', map{ + 'foo': 'bar' + 'bar': 'baz' + }) ? + assert parse_headers('foo: bar\r\nfoo:baz') ?.custom_values('foo') == ['bar', 'baz'] + + if x := parse_headers(' oops: oh no') { + return error('should have errored, but got $x') + } +} diff --git a/v_windows/v/old/vlib/net/http/http.v b/v_windows/v/old/vlib/net/http/http.v new file mode 100644 index 0000000..4f039b9 --- /dev/null +++ b/v_windows/v/old/vlib/net/http/http.v @@ -0,0 +1,175 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module http + +import net.urllib + +const ( + max_redirects = 4 + content_type_default = 'text/plain' + bufsize = 1536 +) + +// FetchConfig holds configurations of fetch +pub struct FetchConfig { +pub mut: + method Method + header Header + data string + params map[string]string + cookies map[string]string + user_agent string = 'v.http' + verbose bool +} + +pub fn new_request(method Method, url_ string, data string) ?Request { + url := if method == .get { url_ + '?' + data } else { url_ } + // println('new req() method=$method url="$url" dta="$data"') + return Request{ + method: method + url: url + data: data + /* + headers: { + 'Accept-Encoding': 'compress' + } + */ + } +} + +// get sends a GET HTTP request to the URL +pub fn get(url string) ?Response { + return fetch_with_method(.get, url, FetchConfig{}) +} + +// post sends a POST HTTP request to the URL with a string data +pub fn post(url string, data string) ?Response { + return fetch_with_method(.post, url, + data: data + header: new_header(key: .content_type, value: http.content_type_default) + ) +} + +// post_json sends a POST HTTP request to the URL with a JSON data +pub fn post_json(url string, data string) ?Response { + return fetch_with_method(.post, url, + data: data + header: new_header(key: .content_type, value: 'application/json') + ) +} + +// post_form sends a POST HTTP request to the URL with X-WWW-FORM-URLENCODED data +pub fn post_form(url string, data map[string]string) ?Response { + return fetch_with_method(.post, url, + header: new_header(key: .content_type, value: 'application/x-www-form-urlencoded') + data: url_encode_form_data(data) + ) +} + +// put sends a PUT HTTP request to the URL with a string data +pub fn put(url string, data string) ?Response { + return fetch_with_method(.put, url, + data: data + header: new_header(key: .content_type, value: http.content_type_default) + ) +} + +// patch sends a PATCH HTTP request to the URL with a string data +pub fn patch(url string, data string) ?Response { + return fetch_with_method(.patch, url, + data: data + header: new_header(key: .content_type, value: http.content_type_default) + ) +} + +// head sends a HEAD HTTP request to the URL +pub fn head(url string) ?Response { + return fetch_with_method(.head, url, FetchConfig{}) +} + +// delete sends a DELETE HTTP request to the URL +pub fn delete(url string) ?Response { + return fetch_with_method(.delete, url, FetchConfig{}) +} + +// fetch sends an HTTP request to the URL with the given method and configurations +pub fn fetch(_url string, config FetchConfig) ?Response { + if _url == '' { + return error('http.fetch: empty url') + } + url := build_url_from_fetch(_url, config) or { return error('http.fetch: invalid url $_url') } + data := config.data + req := Request{ + method: config.method + url: url + data: data + header: config.header + cookies: config.cookies + user_agent: config.user_agent + user_ptr: 0 + verbose: config.verbose + } + res := req.do() ? + return res +} + +// get_text sends a GET HTTP request to the URL and returns the text content of the response +pub fn get_text(url string) string { + resp := fetch(url, method: .get) or { return '' } + return resp.text +} + +// url_encode_form_data converts mapped data to an URL encoded string +pub fn url_encode_form_data(data map[string]string) string { + mut pieces := []string{} + for key_, value_ in data { + key := urllib.query_escape(key_) + value := urllib.query_escape(value_) + pieces << '$key=$value' + } + return pieces.join('&') +} + +fn fetch_with_method(method Method, url string, _config FetchConfig) ?Response { + mut config := _config + config.method = method + return fetch(url, config) +} + +fn build_url_from_fetch(_url string, config FetchConfig) ?string { + mut url := urllib.parse(_url) ? + if config.params.len == 0 { + return url.str() + } + mut pieces := []string{cap: config.params.len} + for key, val in config.params { + pieces << '$key=$val' + } + mut query := pieces.join('&') + if url.raw_query.len > 1 { + query = url.raw_query + '&' + query + } + url.raw_query = query + return url.str() +} + +// unescape_url is deprecated, use urllib.query_unescape() instead +pub fn unescape_url(s string) string { + panic('http.unescape_url() was replaced with urllib.query_unescape()') +} + +// escape_url is deprecated, use urllib.query_escape() instead +pub fn escape_url(s string) string { + panic('http.escape_url() was replaced with urllib.query_escape()') +} + +// unescape is deprecated, use urllib.query_escape() instead +pub fn unescape(s string) string { + panic('http.unescape() was replaced with http.unescape_url()') +} + +// escape is deprecated, use urllib.query_unescape() instead +pub fn escape(s string) string { + panic('http.escape() was replaced with http.escape_url()') +} diff --git a/v_windows/v/old/vlib/net/http/http_httpbin_test.v b/v_windows/v/old/vlib/net/http/http_httpbin_test.v new file mode 100644 index 0000000..2023099 --- /dev/null +++ b/v_windows/v/old/vlib/net/http/http_httpbin_test.v @@ -0,0 +1,95 @@ +module http + +// internal tests have access to *everything in the module* +import json + +struct HttpbinResponseBody { + args map[string]string + data string + files map[string]string + form map[string]string + headers map[string]string + json map[string]string + origin string + url string +} + +fn http_fetch_mock(_methods []string, _config FetchConfig) ?[]Response { + url := 'https://httpbin.org/' + methods := if _methods.len == 0 { ['GET', 'POST', 'PATCH', 'PUT', 'DELETE'] } else { _methods } + mut config := _config + mut result := []Response{} + // Note: httpbin doesn't support head + for method in methods { + lmethod := method.to_lower() + config.method = method_from_str(method) + res := fetch(url + lmethod, config) ? + // TODO + // body := json.decode(HttpbinResponseBody,res.text)? + result << res + } + return result +} + +fn test_http_fetch_bare() { + $if !network ? { + return + } + responses := http_fetch_mock([], FetchConfig{}) or { panic(err) } + for response in responses { + assert response.status() == .ok + } +} + +fn test_http_fetch_with_data() { + $if !network ? { + return + } + responses := http_fetch_mock(['POST', 'PUT', 'PATCH', 'DELETE'], + data: 'hello world' + ) or { panic(err) } + for response in responses { + payload := json.decode(HttpbinResponseBody, response.text) or { panic(err) } + assert payload.data == 'hello world' + } +} + +fn test_http_fetch_with_params() { + $if !network ? { + return + } + responses := http_fetch_mock([], + params: map{ + 'a': 'b' + 'c': 'd' + } + ) or { panic(err) } + for response in responses { + // payload := json.decode(HttpbinResponseBody,response.text) or { + // panic(err) + // } + assert response.status() == .ok + // TODO + // assert payload.args['a'] == 'b' + // assert payload.args['c'] == 'd' + } +} + +fn test_http_fetch_with_headers() ? { + $if !network ? { + return + } + mut header := new_header() + header.add_custom('Test-Header', 'hello world') ? + responses := http_fetch_mock([], + header: header + ) or { panic(err) } + for response in responses { + // payload := json.decode(HttpbinResponseBody,response.text) or { + // panic(err) + // } + assert response.status() == .ok + // TODO + // assert payload.headers['Test-Header'] == 'hello world' + } +} diff --git a/v_windows/v/old/vlib/net/http/http_test.v b/v_windows/v/old/vlib/net/http/http_test.v new file mode 100644 index 0000000..8b68073 --- /dev/null +++ b/v_windows/v/old/vlib/net/http/http_test.v @@ -0,0 +1,56 @@ +import net.http + +fn test_http_get() { + $if !network ? { + return + } + assert http.get_text('https://vlang.io/version') == '0.1.5' + println('http ok') +} + +fn test_http_get_from_vlang_utc_now() { + $if !network ? { + return + } + urls := ['http://vlang.io/utc_now', 'https://vlang.io/utc_now'] + for url in urls { + println('Test getting current time from $url by http.get') + res := http.get(url) or { panic(err) } + assert res.status() == .ok + assert res.text.len > 0 + assert res.text.int() > 1566403696 + println('Current time is: $res.text.int()') + } +} + +fn test_public_servers() { + $if !network ? { + return + } + urls := [ + 'http://github.com/robots.txt', + 'http://google.com/robots.txt', + 'https://github.com/robots.txt', + 'https://google.com/robots.txt', + // 'http://yahoo.com/robots.txt', + // 'https://yahoo.com/robots.txt', + ] + for url in urls { + println('Testing http.get on public url: $url ') + res := http.get(url) or { panic(err) } + assert res.status() == .ok + assert res.text.len > 0 + } +} + +fn test_relative_redirects() { + $if !network ? { + return + } $else { + return + } // tempfix periodic: httpbin relative redirects are broken + res := http.get('https://httpbin.org/relative-redirect/3?abc=xyz') or { panic(err) } + assert res.status() == .ok + assert res.text.len > 0 + assert res.text.contains('"abc": "xyz"') +} diff --git a/v_windows/v/old/vlib/net/http/method.v b/v_windows/v/old/vlib/net/http/method.v new file mode 100644 index 0000000..91c93e1 --- /dev/null +++ b/v_windows/v/old/vlib/net/http/method.v @@ -0,0 +1,48 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module http + +// The methods listed here are some of the most used ones, ordered by +// commonality. A comprehensive list is available at: +// https://www.iana.org/assignments/http-methods/http-methods.xhtml +pub enum Method { + get + post + put + head + delete + options + trace + connect + patch +} + +pub fn (m Method) str() string { + return match m { + .get { 'GET' } + .post { 'POST' } + .put { 'PUT' } + .head { 'HEAD' } + .delete { 'DELETE' } + .options { 'OPTIONS' } + .trace { 'TRACE' } + .connect { 'CONNECT' } + .patch { 'PATCH' } + } +} + +pub fn method_from_str(m string) Method { + return match m { + 'GET' { Method.get } + 'POST' { Method.post } + 'PUT' { Method.put } + 'HEAD' { Method.head } + 'DELETE' { Method.delete } + 'OPTIONS' { Method.options } + 'TRACE' { Method.trace } + 'CONNECT' { Method.connect } + 'PATCH' { Method.patch } + else { Method.get } // should we default to GET? + } +} diff --git a/v_windows/v/old/vlib/net/http/request.v b/v_windows/v/old/vlib/net/http/request.v new file mode 100644 index 0000000..4664659 --- /dev/null +++ b/v_windows/v/old/vlib/net/http/request.v @@ -0,0 +1,324 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module http + +import io +import net +import net.urllib +import strings +import time + +// Request holds information about an HTTP request (either received by +// a server or to be sent by a client) +pub struct Request { +pub mut: + version Version = .v1_1 + method Method + header Header + cookies map[string]string + data string + url string + user_agent string = 'v.http' + verbose bool + user_ptr voidptr + // NOT implemented for ssl connections + // time = -1 for no timeout + read_timeout i64 = 30 * time.second + write_timeout i64 = 30 * time.second +} + +fn (mut req Request) free() { + unsafe { req.header.free() } +} + +// add_header adds the key and value of an HTTP request header +// To add a custom header, use add_custom_header +pub fn (mut req Request) add_header(key CommonHeader, val string) { + req.header.add(key, val) +} + +// add_custom_header adds the key and value of an HTTP request header +// This method may fail if the key contains characters that are not permitted +pub fn (mut req Request) add_custom_header(key string, val string) ? { + return req.header.add_custom(key, val) +} + +// do will send the HTTP request and returns `http.Response` as soon as the response is recevied +pub fn (req &Request) do() ?Response { + mut url := urllib.parse(req.url) or { return error('http.Request.do: invalid url $req.url') } + mut rurl := url + mut resp := Response{} + mut no_redirects := 0 + for { + if no_redirects == max_redirects { + return error('http.request.do: maximum number of redirects reached ($max_redirects)') + } + qresp := req.method_and_url_to_response(req.method, rurl) ? + resp = qresp + if resp.status() !in [.moved_permanently, .found, .see_other, .temporary_redirect, + .permanent_redirect, + ] { + break + } + // follow any redirects + mut redirect_url := resp.header.get(.location) or { '' } + if redirect_url.len > 0 && redirect_url[0] == `/` { + url.set_path(redirect_url) or { + return error('http.request.do: invalid path in redirect: "$redirect_url"') + } + redirect_url = url.str() + } + qrurl := urllib.parse(redirect_url) or { + return error('http.request.do: invalid URL in redirect "$redirect_url"') + } + rurl = qrurl + no_redirects++ + } + return resp +} + +fn (req &Request) method_and_url_to_response(method Method, url urllib.URL) ?Response { + host_name := url.hostname() + scheme := url.scheme + p := url.escaped_path().trim_left('/') + path := if url.query().len > 0 { '/$p?$url.query().encode()' } else { '/$p' } + mut nport := url.port().int() + if nport == 0 { + if scheme == 'http' { + nport = 80 + } + if scheme == 'https' { + nport = 443 + } + } + // println('fetch $method, $scheme, $host_name, $nport, $path ') + if scheme == 'https' { + // println('ssl_do( $nport, $method, $host_name, $path )') + res := req.ssl_do(nport, method, host_name, path) ? + return res + } else if scheme == 'http' { + // println('http_do( $nport, $method, $host_name, $path )') + res := req.http_do('$host_name:$nport', method, path) ? + return res + } + return error('http.request.method_and_url_to_response: unsupported scheme: "$scheme"') +} + +fn (req &Request) build_request_headers(method Method, host_name string, path string) string { + ua := req.user_agent + mut uheaders := []string{} + if !req.header.contains(.host) { + uheaders << 'Host: $host_name\r\n' + } + if !req.header.contains(.user_agent) { + uheaders << 'User-Agent: $ua\r\n' + } + if req.data.len > 0 && !req.header.contains(.content_length) { + uheaders << 'Content-Length: $req.data.len\r\n' + } + for key in req.header.keys() { + if key == CommonHeader.cookie.str() { + continue + } + val := req.header.custom_values(key).join('; ') + uheaders << '$key: $val\r\n' + } + uheaders << req.build_request_cookies_header() + version := if req.version == .unknown { Version.v1_1 } else { req.version } + return '$method $path $version\r\n' + uheaders.join('') + 'Connection: close\r\n\r\n' + req.data +} + +fn (req &Request) build_request_cookies_header() string { + if req.cookies.keys().len < 1 { + return '' + } + mut cookie := []string{} + for key, val in req.cookies { + cookie << '$key=$val' + } + cookie << req.header.values(.cookie) + return 'Cookie: ' + cookie.join('; ') + '\r\n' +} + +fn (req &Request) http_do(host string, method Method, path string) ?Response { + host_name, _ := net.split_address(host) ? + s := req.build_request_headers(method, host_name, path) + mut client := net.dial_tcp(host) ? + client.set_read_timeout(req.read_timeout) + client.set_write_timeout(req.write_timeout) + // TODO this really needs to be exposed somehow + client.write(s.bytes()) ? + $if trace_http_request ? { + eprintln('> $s') + } + mut bytes := io.read_all(reader: client) ? + client.close() ? + response_text := bytes.bytestr() + $if trace_http_response ? { + eprintln('< $response_text') + } + return parse_response(response_text) +} + +// referer returns 'Referer' header value of the given request +pub fn (req &Request) referer() string { + return req.header.get(.referer) or { '' } +} + +// Parse a raw HTTP request into a Request object +pub fn parse_request(mut reader io.BufferedReader) ?Request { + // request line + mut line := reader.read_line() ? + method, target, version := parse_request_line(line) ? + + // headers + mut header := new_header() + line = reader.read_line() ? + for line != '' { + key, value := parse_header(line) ? + header.add_custom(key, value) ? + line = reader.read_line() ? + } + header.coerce(canonicalize: true) + + // body + mut body := []byte{} + if length := header.get(.content_length) { + n := length.int() + if n > 0 { + body = []byte{len: n} + mut count := 0 + for count < body.len { + count += reader.read(mut body[count..]) or { break } + } + } + } + + return Request{ + method: method + url: target.str() + header: header + data: body.bytestr() + version: version + } +} + +fn parse_request_line(s string) ?(Method, urllib.URL, Version) { + words := s.split(' ') + if words.len != 3 { + return error('malformed request line') + } + method := method_from_str(words[0]) + target := urllib.parse(words[1]) ? + version := version_from_str(words[2]) + if version == .unknown { + return error('unsupported version') + } + + return method, target, version +} + +// Parse URL encoded key=value&key=value forms +fn parse_form(body string) map[string]string { + words := body.split('&') + mut form := map[string]string{} + for word in words { + kv := word.split_nth('=', 2) + if kv.len != 2 { + continue + } + key := urllib.query_unescape(kv[0]) or { continue } + val := urllib.query_unescape(kv[1]) or { continue } + form[key] = val + } + return form + // } + // todo: parse form-data and application/json + // ... +} + +struct FileData { +pub: + filename string + content_type string + data string +} + +struct UnexpectedExtraAttributeError { + msg string + code int +} + +struct MultiplePathAttributesError { + msg string = 'Expected at most one path attribute' + code int +} + +fn parse_multipart_form(body string, boundary string) (map[string]string, map[string][]FileData) { + sections := body.split(boundary) + fields := sections[1..sections.len - 1] + mut form := map[string]string{} + mut files := map[string][]FileData{} + + for field in fields { + // TODO: do not split into lines; do same parsing for HTTP body + lines := field.split_into_lines()[1..] + disposition := parse_disposition(lines[0]) + // Grab everything between the double quotes + name := disposition['name'] or { continue } + // Parse files + // TODO: filename* + if 'filename' in disposition { + filename := disposition['filename'] + // Parse Content-Type header + if lines.len == 1 || !lines[1].to_lower().starts_with('content-type:') { + continue + } + mut ct := lines[1].split_nth(':', 2)[1] + ct = ct.trim_left(' \t') + data := lines_to_string(field.len, lines, 3, lines.len - 1) + files[name] << FileData{ + filename: filename + content_type: ct + data: data + } + continue + } + data := lines_to_string(field.len, lines, 2, lines.len - 1) + form[name] = data + } + return form, files +} + +// Parse the Content-Disposition header of a multipart form +// Returns a map of the key="value" pairs +// Example: parse_disposition('Content-Disposition: form-data; name="a"; filename="b"') == {'name': 'a', 'filename': 'b'} +fn parse_disposition(line string) map[string]string { + mut data := map[string]string{} + for word in line.split(';') { + kv := word.split_nth('=', 2) + if kv.len != 2 { + continue + } + key, value := kv[0].to_lower().trim_left(' \t'), kv[1] + if value.starts_with('"') && value.ends_with('"') { + data[key] = value[1..value.len - 1] + } else { + data[key] = value + } + } + return data +} + +[manualfree] +fn lines_to_string(len int, lines []string, start int, end int) string { + mut sb := strings.new_builder(len) + for i in start .. end { + sb.writeln(lines[i]) + } + sb.cut_last(1) // last newline + res := sb.str() + unsafe { sb.free() } + return res +} diff --git a/v_windows/v/old/vlib/net/http/request_test.v b/v_windows/v/old/vlib/net/http/request_test.v new file mode 100644 index 0000000..d0baf51 --- /dev/null +++ b/v_windows/v/old/vlib/net/http/request_test.v @@ -0,0 +1,138 @@ +module http + +import io + +struct StringReader { + text string +mut: + place int +} + +fn (mut s StringReader) read(mut buf []byte) ?int { + if s.place >= s.text.len { + return none + } + max_bytes := 100 + end := if s.place + max_bytes >= s.text.len { s.text.len } else { s.place + max_bytes } + n := copy(buf, s.text[s.place..end].bytes()) + s.place += n + return n +} + +fn reader(s string) &io.BufferedReader { + return io.new_buffered_reader( + reader: &StringReader{ + text: s + } + ) +} + +fn test_parse_request_not_http() { + mut reader__ := reader('hello') + parse_request(mut reader__) or { return } + panic('should not have parsed') +} + +fn test_parse_request_no_headers() { + mut reader_ := reader('GET / HTTP/1.1\r\n\r\n') + req := parse_request(mut reader_) or { panic('did not parse: $err') } + assert req.method == .get + assert req.url == '/' + assert req.version == .v1_1 +} + +fn test_parse_request_two_headers() { + mut reader_ := reader('GET / HTTP/1.1\r\nTest1: a\r\nTest2: B\r\n\r\n') + req := parse_request(mut reader_) or { panic('did not parse: $err') } + assert req.header.custom_values('Test1') == ['a'] + assert req.header.custom_values('Test2') == ['B'] +} + +fn test_parse_request_two_header_values() { + mut reader_ := reader('GET / HTTP/1.1\r\nTest1: a; b\r\nTest2: c\r\nTest2: d\r\n\r\n') + req := parse_request(mut reader_) or { panic('did not parse: $err') } + assert req.header.custom_values('Test1') == ['a; b'] + assert req.header.custom_values('Test2') == ['c', 'd'] +} + +fn test_parse_request_body() { + mut reader_ := reader('GET / HTTP/1.1\r\nTest1: a\r\nTest2: b\r\nContent-Length: 4\r\n\r\nbodyabc') + req := parse_request(mut reader_) or { panic('did not parse: $err') } + assert req.data == 'body' +} + +fn test_parse_request_line() { + method, target, version := parse_request_line('GET /target HTTP/1.1') or { + panic('did not parse: $err') + } + assert method == .get + assert target.str() == '/target' + assert version == .v1_1 +} + +fn test_parse_form() { + assert parse_form('foo=bar&bar=baz') == map{ + 'foo': 'bar' + 'bar': 'baz' + } + assert parse_form('foo=bar=&bar=baz') == map{ + 'foo': 'bar=' + 'bar': 'baz' + } + assert parse_form('foo=bar%3D&bar=baz') == map{ + 'foo': 'bar=' + 'bar': 'baz' + } + assert parse_form('foo=b%26ar&bar=baz') == map{ + 'foo': 'b&ar' + 'bar': 'baz' + } + assert parse_form('a=b& c=d') == map{ + 'a': 'b' + ' c': 'd' + } + assert parse_form('a=b&c= d ') == map{ + 'a': 'b' + 'c': ' d ' + } +} + +fn test_parse_multipart_form() { + boundary := '6844a625b1f0b299' + names := ['foo', 'fooz'] + file := 'bar.v' + ct := 'application/octet-stream' + contents := ['baz', 'buzz'] + data := "--------------------------$boundary +Content-Disposition: form-data; name=\"${names[0]}\"; filename=\"$file\" +Content-Type: $ct + +${contents[0]} +--------------------------$boundary +Content-Disposition: form-data; name=\"${names[1]}\" + +${contents[1]} +--------------------------$boundary-- +" + form, files := parse_multipart_form(data, boundary) + assert files == map{ + names[0]: [FileData{ + filename: file + content_type: ct + data: contents[0] + }] + } + + assert form == map{ + names[1]: contents[1] + } +} + +fn test_parse_large_body() ? { + body := 'A'.repeat(101) // greater than max_bytes + req := 'GET / HTTP/1.1\r\nContent-Length: $body.len\r\n\r\n$body' + mut reader_ := reader(req) + result := parse_request(mut reader_) ? + assert result.data.len == body.len + assert result.data == body +} diff --git a/v_windows/v/old/vlib/net/http/response.v b/v_windows/v/old/vlib/net/http/response.v new file mode 100644 index 0000000..caa8228 --- /dev/null +++ b/v_windows/v/old/vlib/net/http/response.v @@ -0,0 +1,152 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module http + +import net.http.chunked +import strconv + +// Response represents the result of the request +pub struct Response { +pub mut: + text string + header Header + status_code int + status_msg string + http_version string +} + +fn (mut resp Response) free() { + unsafe { resp.header.free() } +} + +// Formats resp to bytes suitable for HTTP response transmission +pub fn (resp Response) bytes() []byte { + // TODO: build []byte directly; this uses two allocations + return resp.bytestr().bytes() +} + +// Formats resp to a string suitable for HTTP response transmission +pub fn (resp Response) bytestr() string { + return ('HTTP/$resp.http_version $resp.status_code $resp.status_msg\r\n' + '${resp.header.render( + version: resp.version() + )}\r\n' + '$resp.text') +} + +// Parse a raw HTTP response into a Response object +pub fn parse_response(resp string) ?Response { + version, status_code, status_msg := parse_status_line(resp.all_before('\n')) ? + // Build resp header map and separate the body + start_idx, end_idx := find_headers_range(resp) ? + header := parse_headers(resp.substr(start_idx, end_idx)) ? + mut text := resp.substr(end_idx, resp.len) + if header.get(.transfer_encoding) or { '' } == 'chunked' { + text = chunked.decode(text) + } + return Response{ + http_version: version + status_code: status_code + status_msg: status_msg + header: header + text: text + } +} + +// parse_status_line parses the first HTTP response line into the HTTP +// version, status code, and reason phrase +fn parse_status_line(line string) ?(string, int, string) { + if line.len < 5 || line[..5].to_lower() != 'http/' { + return error('response does not start with HTTP/') + } + data := line.split_nth(' ', 3) + if data.len != 3 { + return error('expected at least 3 tokens') + } + version := data[0].substr(5, data[0].len) + // validate version is 1*DIGIT "." 1*DIGIT + digits := version.split_nth('.', 3) + if digits.len != 2 { + return error('HTTP version malformed') + } + for digit in digits { + strconv.atoi(digit) or { return error('HTTP version must contain only integers') } + } + return version, strconv.atoi(data[1]) ?, data[2] +} + +// cookies parses the Set-Cookie headers into Cookie objects +pub fn (r Response) cookies() []Cookie { + mut cookies := []Cookie{} + for cookie in r.header.values(.set_cookie) { + cookies << parse_cookie(cookie) or { continue } + } + return cookies +} + +// status parses the status_code into a Status struct +pub fn (r Response) status() Status { + return status_from_int(r.status_code) +} + +// set_status sets the status_code and status_msg of the response +pub fn (mut r Response) set_status(s Status) { + r.status_code = s.int() + r.status_msg = s.str() +} + +// version parses the version +pub fn (r Response) version() Version { + return version_from_str('HTTP/$r.http_version') +} + +// set_version sets the http_version string of the response +pub fn (mut r Response) set_version(v Version) { + if v == .unknown { + r.http_version = '' + return + } + maj, min := v.protos() + r.http_version = '${maj}.$min' +} + +pub struct ResponseConfig { + version Version = .v1_1 + status Status = .ok + header Header + text string +} + +// new_response creates a Response object from the configuration. This +// function will add a Content-Length header if text is not empty. +pub fn new_response(conf ResponseConfig) Response { + mut resp := Response{ + text: conf.text + header: conf.header + } + if conf.text.len > 0 && !resp.header.contains(.content_length) { + resp.header.add(.content_length, conf.text.len.str()) + } + resp.set_status(conf.status) + resp.set_version(conf.version) + return resp +} + +// find_headers_range returns the start (inclusive) and end (exclusive) +// index of the headers in the string, including the trailing newlines. This +// helper function expects the first line in `data` to be the HTTP status line +// (HTTP/1.1 200 OK). +fn find_headers_range(data string) ?(int, int) { + start_idx := data.index('\n') or { return error('no start index found') } + 1 + mut count := 0 + for i := start_idx; i < data.len; i++ { + if data[i] == `\n` { + count++ + } else if data[i] != `\r` { + count = 0 + } + if count == 2 { + return start_idx, i + 1 + } + } + return error('no end index found') +} diff --git a/v_windows/v/old/vlib/net/http/server.v b/v_windows/v/old/vlib/net/http/server.v new file mode 100644 index 0000000..3804ae3 --- /dev/null +++ b/v_windows/v/old/vlib/net/http/server.v @@ -0,0 +1,80 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module http + +import io +import net +import time + +interface Handler { + handle(Request) Response +} + +pub struct Server { +pub mut: + port int = 8080 + handler Handler = DebugHandler{} + read_timeout time.Duration = 30 * time.second + write_timeout time.Duration = 30 * time.second +} + +pub fn (mut s Server) listen_and_serve() ? { + if s.handler is DebugHandler { + eprintln('Server handler not set, using debug handler') + } + mut l := net.listen_tcp(.ip6, ':$s.port') ? + eprintln('Listening on :$s.port') + for { + mut conn := l.accept() or { + eprintln('accept() failed: $err; skipping') + continue + } + conn.set_read_timeout(s.read_timeout) + conn.set_write_timeout(s.write_timeout) + // TODO: make concurrent + s.parse_and_respond(mut conn) + } +} + +fn (mut s Server) parse_and_respond(mut conn net.TcpConn) { + defer { + conn.close() or { eprintln('close() failed: $err') } + } + + mut reader := io.new_buffered_reader(reader: conn) + defer { + reader.free() + } + req := parse_request(mut reader) or { + $if debug { + // only show in debug mode to prevent abuse + eprintln('error parsing request: $err') + } + return + } + mut resp := s.handler.handle(req) + if resp.version() == .unknown { + resp.set_version(req.version) + } + conn.write(resp.bytes()) or { eprintln('error sending response: $err') } +} + +// DebugHandler implements the Handler interface by echoing the request +// in the response +struct DebugHandler {} + +fn (d DebugHandler) handle(req Request) Response { + $if debug { + eprintln('[$time.now()] $req.method $req.url\n\r$req.header\n\r$req.data - 200 OK') + } $else { + eprintln('[$time.now()] $req.method $req.url - 200') + } + mut r := Response{ + text: req.data + header: req.header + } + r.set_status(.ok) + r.set_version(req.version) + return r +} diff --git a/v_windows/v/old/vlib/net/http/status.v b/v_windows/v/old/vlib/net/http/status.v new file mode 100644 index 0000000..f4bc9ee --- /dev/null +++ b/v_windows/v/old/vlib/net/http/status.v @@ -0,0 +1,255 @@ +// Copyright (c) 2020 Justin E. Jones. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module http + +// The status codes listed here are based on the comprehensive list, +// available at: +// https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml +pub enum Status { + unknown = -1 + unassigned = 0 + cont = 100 + switching_protocols = 101 + processing = 102 + checkpoint_draft = 103 + ok = 200 + created = 201 + accepted = 202 + non_authoritative_information = 203 + no_content = 204 + reset_content = 205 + partial_content = 206 + multi_status = 207 + already_reported = 208 + im_used = 226 + multiple_choices = 300 + moved_permanently = 301 + found = 302 + see_other = 303 + not_modified = 304 + use_proxy = 305 + switch_proxy = 306 + temporary_redirect = 307 + permanent_redirect = 308 + bad_request = 400 + unauthorized = 401 + payment_required = 402 + forbidden = 403 + not_found = 404 + method_not_allowed = 405 + not_acceptable = 406 + proxy_authentication_required = 407 + request_timeout = 408 + conflict = 409 + gone = 410 + length_required = 411 + precondition_failed = 412 + request_entity_too_large = 413 + request_uri_too_long = 414 + unsupported_media_type = 415 + requested_range_not_satisfiable = 416 + expectation_failed = 417 + im_a_teapot = 418 + misdirected_request = 421 + unprocessable_entity = 422 + locked = 423 + failed_dependency = 424 + unordered_collection = 425 + upgrade_required = 426 + precondition_required = 428 + too_many_requests = 429 + request_header_fields_too_large = 431 + unavailable_for_legal_reasons = 451 + client_closed_request = 499 + internal_server_error = 500 + not_implemented = 501 + bad_gateway = 502 + service_unavailable = 503 + gateway_timeout = 504 + http_version_not_supported = 505 + variant_also_negotiates = 506 + insufficient_storage = 507 + loop_detected = 508 + bandwidth_limit_exceeded = 509 + not_extended = 510 + network_authentication_required = 511 +} + +pub fn status_from_int(code int) Status { + return match code { + 100 { Status.cont } + 101 { Status.switching_protocols } + 102 { Status.processing } + 103 { Status.checkpoint_draft } + 104...199 { Status.unassigned } + 200 { Status.ok } + 201 { Status.created } + 202 { Status.accepted } + 203 { Status.non_authoritative_information } + 204 { Status.no_content } + 205 { Status.reset_content } + 206 { Status.partial_content } + 207 { Status.multi_status } + 208 { Status.already_reported } + 209...225 { Status.unassigned } + 226 { Status.im_used } + 227...299 { Status.unassigned } + 300 { Status.multiple_choices } + 301 { Status.moved_permanently } + 302 { Status.found } + 303 { Status.see_other } + 304 { Status.not_modified } + 305 { Status.use_proxy } + 306 { Status.switch_proxy } + 307 { Status.temporary_redirect } + 308 { Status.permanent_redirect } + 309...399 { Status.unassigned } + 400 { Status.bad_request } + 401 { Status.unauthorized } + 402 { Status.payment_required } + 403 { Status.forbidden } + 404 { Status.not_found } + 405 { Status.method_not_allowed } + 406 { Status.not_acceptable } + 407 { Status.proxy_authentication_required } + 408 { Status.request_timeout } + 409 { Status.conflict } + 410 { Status.gone } + 411 { Status.length_required } + 412 { Status.precondition_failed } + 413 { Status.request_entity_too_large } + 414 { Status.request_uri_too_long } + 415 { Status.unsupported_media_type } + 416 { Status.requested_range_not_satisfiable } + 417 { Status.expectation_failed } + 418 { Status.im_a_teapot } + 419...420 { Status.unassigned } + 421 { Status.misdirected_request } + 422 { Status.unprocessable_entity } + 423 { Status.locked } + 424 { Status.failed_dependency } + 425 { Status.unordered_collection } + 426 { Status.upgrade_required } + 428 { Status.precondition_required } + 429 { Status.too_many_requests } + 431 { Status.request_header_fields_too_large } + 432...450 { Status.unassigned } + 451 { Status.unavailable_for_legal_reasons } + 452...499 { Status.unassigned } + 500 { Status.internal_server_error } + 501 { Status.not_implemented } + 502 { Status.bad_gateway } + 503 { Status.service_unavailable } + 504 { Status.gateway_timeout } + 505 { Status.http_version_not_supported } + 506 { Status.variant_also_negotiates } + 507 { Status.insufficient_storage } + 508 { Status.loop_detected } + 509 { Status.bandwidth_limit_exceeded } + 510 { Status.not_extended } + 511 { Status.network_authentication_required } + 512...599 { Status.unassigned } + else { Status.unknown } + } +} + +pub fn (code Status) str() string { + return match code { + .cont { 'Continue' } + .switching_protocols { 'Switching Protocols' } + .processing { 'Processing' } + .checkpoint_draft { 'Checkpoint Draft' } + .ok { 'OK' } + .created { 'Created' } + .accepted { 'Accepted' } + .non_authoritative_information { 'Non Authoritative Information' } + .no_content { 'No Content' } + .reset_content { 'Reset Content' } + .partial_content { 'Partial Content' } + .multi_status { 'Multi Status' } + .already_reported { 'Already Reported' } + .im_used { 'IM Used' } + .multiple_choices { 'Multiple Choices' } + .moved_permanently { 'Moved Permanently' } + .found { 'Found' } + .see_other { 'See Other' } + .not_modified { 'Not Modified' } + .use_proxy { 'Use Proxy' } + .switch_proxy { 'Switch Proxy' } + .temporary_redirect { 'Temporary Redirect' } + .permanent_redirect { 'Permanent Redirect' } + .bad_request { 'Bad Request' } + .unauthorized { 'Unauthorized' } + .payment_required { 'Payment Required' } + .forbidden { 'Forbidden' } + .not_found { 'Not Found' } + .method_not_allowed { 'Method Not Allowed' } + .not_acceptable { 'Not Acceptable' } + .proxy_authentication_required { 'Proxy Authentication Required' } + .request_timeout { 'Request Timeout' } + .conflict { 'Conflict' } + .gone { 'Gone' } + .length_required { 'Length Required' } + .precondition_failed { 'Precondition Failed' } + .request_entity_too_large { 'Request Entity Too Large' } + .request_uri_too_long { 'Request URI Too Long' } + .unsupported_media_type { 'Unsupported Media Type' } + .requested_range_not_satisfiable { 'Requested Range Not Satisfiable' } + .expectation_failed { 'Expectation Failed' } + .im_a_teapot { 'Im a teapot' } + .misdirected_request { 'Misdirected Request' } + .unprocessable_entity { 'Unprocessable Entity' } + .locked { 'Locked' } + .failed_dependency { 'Failed Dependency' } + .unordered_collection { 'Unordered Collection' } + .upgrade_required { 'Upgrade Required' } + .precondition_required { 'Precondition Required' } + .too_many_requests { 'Too Many Requests' } + .request_header_fields_too_large { 'Request Header Fields Too Large' } + .unavailable_for_legal_reasons { 'Unavailable For Legal Reasons' } + .internal_server_error { 'Internal Server Error' } + .not_implemented { 'Not Implemented' } + .bad_gateway { 'Bad Gateway' } + .service_unavailable { 'Service Unavailable' } + .gateway_timeout { 'Gateway Timeout' } + .http_version_not_supported { 'HTTP Version Not Supported' } + .variant_also_negotiates { 'Variant Also Negotiates' } + .insufficient_storage { 'Insufficient Storage' } + .loop_detected { 'Loop Detected' } + .bandwidth_limit_exceeded { 'Bandwidth Limit Exceeded' } + .not_extended { 'Not Extended' } + .network_authentication_required { 'Network Authentication Required' } + .unassigned { 'Unassigned' } + else { 'Unknown' } + } +} + +// int converts an assigned and known Status to its integral equivalent. +// if a Status is unknown or unassigned, this method will return zero +pub fn (code Status) int() int { + if code in [.unknown, .unassigned] { + return 0 + } + return int(code) +} + +// is_valid returns true if the status code is assigned and known +pub fn (code Status) is_valid() bool { + number := code.int() + return number >= 100 && number < 600 +} + +// is_error will return true if the status code represents either a client or +// a server error; otherwise will return false +pub fn (code Status) is_error() bool { + number := code.int() + return number >= 400 && number < 600 +} + +// is_success will return true if the status code represents either an +// informational, success, or redirection response; otherwise will return false +pub fn (code Status) is_success() bool { + number := code.int() + return number >= 100 && number < 400 +} diff --git a/v_windows/v/old/vlib/net/http/status_test.v b/v_windows/v/old/vlib/net/http/status_test.v new file mode 100644 index 0000000..154aec3 --- /dev/null +++ b/v_windows/v/old/vlib/net/http/status_test.v @@ -0,0 +1,49 @@ +module http + +fn test_str() { + code := Status.bad_gateway + actual := code.str() + assert actual == 'Bad Gateway' +} + +fn test_int() { + code := Status.see_other + actual := code.int() + assert actual == 303 +} + +fn test_is_valid() { + code := Status.gateway_timeout + actual := code.is_valid() + assert actual == true +} + +fn test_is_valid_negative() { + code := Status.unassigned + actual := code.is_valid() + assert actual == false +} + +fn test_is_error() { + code := Status.too_many_requests + actual := code.is_error() + assert actual == true +} + +fn test_is_error_negative() { + code := Status.cont + actual := code.is_error() + assert actual == false +} + +fn test_is_success() { + code := Status.accepted + actual := code.is_success() + assert actual == true +} + +fn test_is_success_negative() { + code := Status.forbidden + actual := code.is_success() + assert actual == false +} diff --git a/v_windows/v/old/vlib/net/http/version.v b/v_windows/v/old/vlib/net/http/version.v new file mode 100644 index 0000000..f4388a3 --- /dev/null +++ b/v_windows/v/old/vlib/net/http/version.v @@ -0,0 +1,40 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module http + +// The versions listed here are the most common ones. +pub enum Version { + unknown + v1_1 + v2_0 + v1_0 +} + +pub fn (v Version) str() string { + return match v { + .v1_1 { 'HTTP/1.1' } + .v2_0 { 'HTTP/2.0' } + .v1_0 { 'HTTP/1.0' } + .unknown { 'unknown' } + } +} + +pub fn version_from_str(v string) Version { + return match v.to_lower() { + 'http/1.1' { Version.v1_1 } + 'http/2.0' { Version.v2_0 } + 'http/1.0' { Version.v1_0 } + else { Version.unknown } + } +} + +// protos returns the version major and minor numbers +pub fn (v Version) protos() (int, int) { + match v { + .v1_1 { return 1, 1 } + .v2_0 { return 2, 0 } + .v1_0 { return 1, 0 } + .unknown { return 0, 0 } + } +} diff --git a/v_windows/v/old/vlib/net/ipv6_v6only.h b/v_windows/v/old/vlib/net/ipv6_v6only.h new file mode 100644 index 0000000..79393df --- /dev/null +++ b/v_windows/v/old/vlib/net/ipv6_v6only.h @@ -0,0 +1,5 @@ +#if !defined(IPV6_V6ONLY) + +#define IPV6_V6ONLY 27 + +#endif diff --git a/v_windows/v/old/vlib/net/net_nix.c.v b/v_windows/v/old/vlib/net/net_nix.c.v new file mode 100644 index 0000000..a9fa531 --- /dev/null +++ b/v_windows/v/old/vlib/net/net_nix.c.v @@ -0,0 +1,26 @@ +module net + +#include +#include +// inet.h is needed for inet_ntop on macos +#include +#include +#include +#include + +#flag solaris -lsocket + +fn error_code() int { + return C.errno +} + +fn init() { +} + +pub const ( + msg_nosignal = 0x4000 +) + +const ( + error_ewouldblock = C.EWOULDBLOCK +) diff --git a/v_windows/v/old/vlib/net/net_windows.c.v b/v_windows/v/old/vlib/net/net_windows.c.v new file mode 100644 index 0000000..337176f --- /dev/null +++ b/v_windows/v/old/vlib/net/net_windows.c.v @@ -0,0 +1,780 @@ +module net + +// WsaError is all of the socket errors that WSA provides from WSAGetLastError +pub enum WsaError { + // + // MessageId: WSAEINTR + // + // MessageText: + // + // A blocking operation was interrupted by a call to WSACancelBlockingCall. + // + wsaeintr = 10004 + // + // MessageId: WSAEBADF + // + // MessageText: + // + // The file handle supplied is not valid. + // + wsaebadf = 10009 + // + // MessageId: WSAEACCES + // + // MessageText: + // + // An attempt was made to access a socket in a way forbidden by its access permissions. + // + wsaeacces = 10013 + // + // MessageId: WSAEFAULT + // + // MessageText: + // + // The system detected an invalid pointer address in attempting to use a pointer argument in a call. + // + wsaefault = 10014 + // + // MessageId: WSAEINVAL + // + // MessageText: + // + // An invalid argument was supplied. + // + wsaeinval = 10022 + // + // MessageId: WSAEMFILE + // + // MessageText: + // + // Too many open sockets. + // + wsaemfile = 10024 + // + // MessageId: WSAEWOULDBLOCK + // + // MessageText: + // + // A non-blocking socket operation could not be completed immediately. + // + wsaewouldblock = 10035 + // + // MessageId: WSAEINPROGRESS + // + // MessageText: + // + // A blocking operation is currently executing. + // + wsaeinprogress = 10036 + // + // MessageId: WSAEALREADY + // + // MessageText: + // + // An operation was attempted on a non-blocking socket that already had an operation in progress. + // + wsaealready = 10037 + // + // MessageId: WSAENOTSOCK + // + // MessageText: + // + // An operation was attempted on something that is not a socket. + // + wsaenotsock = 10038 + // + // MessageId: WSAEDESTADDRREQ + // + // MessageText: + // + // A required address was omitted from an operation on a socket. + // + wsaedestaddrreq = 10039 + // + // MessageId: WSAEMSGSIZE + // + // MessageText: + // + // A message sent on a datagram socket was larger than the internal message buffer or some other network limit, or the buffer used to receive a datagram into was smaller than the datagram itself. + // + wsaemsgsize = 10040 + // + // MessageId: WSAEPROTOTYPE + // + // MessageText: + // + // A protocol was specified in the socket function call that does not support the semantics of the socket type requested. + // + wsaeprototype = 10041 + // + // MessageId: WSAENOPROTOOPT + // + // MessageText: + // + // An unknown, invalid, or unsupported option or level was specified in a getsockopt or setsockopt call. + // + wsaenoprotoopt = 10042 + // + // MessageId: WSAEPROTONOSUPPORT + // + // MessageText: + // + // The requested protocol has not been configured into the system, or no implementation for it exists. + // + wsaeprotonosupport = 10043 + // + // MessageId: WSAESOCKTNOSUPPORT + // + // MessageText: + // + // The support for the specified socket type does not exist in this address family. + // + wsaesocktnosupport = 10044 + // + // MessageId: WSAEOPNOTSUPP + // + // MessageText: + // + // The attempted operation is not supported for the type of object referenced. + // + wsaeopnotsupp = 10045 + // + // MessageId: WSAEPFNOSUPPORT + // + // MessageText: + // + // The protocol family has not been configured into the system or no implementation for it exists. + // + wsaepfnosupport = 10046 + // + // MessageId: WSAEAFNOSUPPORT + // + // MessageText: + // + // An address incompatible with the requested protocol was used. + // + wsaeafnosupport = 10047 + // + // MessageId: WSAEADDRINUSE + // + // MessageText: + // + // Only one usage of each socket address (protocol/network address/port) is normally permitted. + // + wsaeaddrinuse = 10048 + // + // MessageId: WSAEADDRNOTAVAIL + // + // MessageText: + // + // The requested address is not valid in its context. + // + wsaeaddrnotavail = 10049 + // + // MessageId: WSAENETDOWN + // + // MessageText: + // + // A socket operation encountered a dead network. + // + wsaenetdown = 10050 + // + // MessageId: WSAENETUNREACH + // + // MessageText: + // + // A socket operation was attempted to an unreachable network. + // + wsaenetunreach = 10051 + // + // MessageId: WSAENETRESET + // + // MessageText: + // + // The connection has been broken due to keep-alive activity detecting a failure while the operation was in progress. + // + wsaenetreset = 10052 + // + // MessageId: WSAECONNABORTED + // + // MessageText: + // + // An established connection was aborted by the software in your host machine. + // + wsaeconnaborted = 10053 + // + // MessageId: WSAECONNRESET + // + // MessageText: + // + // An existing connection was forcibly closed by the remote host. + // + wsaeconnreset = 10054 + // + // MessageId: WSAENOBUFS + // + // MessageText: + // + // An operation on a socket could not be performed because the system lacked sufficient buffer space or because a queue was full. + // + wsaenobufs = 10055 + // + // MessageId: WSAEISCONN + // + // MessageText: + // + // A connect request was made on an already connected socket. + // + wsaeisconn = 10056 + // + // MessageId: WSAENOTCONN + // + // MessageText: + // + // A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied. + // + wsaenotconn = 10057 + // + // MessageId: WSAESHUTDOWN + // + // MessageText: + // + // A request to send or receive data was disallowed because the socket had already been shut down in that direction with a previous shutdown call. + // + wsaeshutdown = 10058 + // + // MessageId: WSAETOOMANYREFS + // + // MessageText: + // + // Too many references to some kernel object. + // + wsaetoomanyrefs = 10059 + // + // MessageId: WSAETIMEDOUT + // + // MessageText: + // + // A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond. + // + wsaetimedout = 10060 + // + // MessageId: WSAECONNREFUSED + // + // MessageText: + // + // No connection could be made because the target machine actively refused it. + // + wsaeconnrefused = 10061 + // + // MessageId: WSAELOOP + // + // MessageText: + // + // Cannot translate name. + // + wsaeloop = 10062 + // + // MessageId: WSAENAMETOOLONG + // + // MessageText: + // + // Name component or name was too long. + // + wsaenametoolong = 10063 + // + // MessageId: WSAEHOSTDOWN + // + // MessageText: + // + // A socket operation failed because the destination host was down. + // + wsaehostdown = 10064 + // + // MessageId: WSAEHOSTUNREACH + // + // MessageText: + // + // A socket operation was attempted to an unreachable host. + // + wsaehostunreach = 10065 + // + // MessageId: WSAENOTEMPTY + // + // MessageText: + // + // Cannot remove a directory that is not empty. + // + wsaenotempty = 10066 + // + // MessageId: WSAEPROCLIM + // + // MessageText: + // + // A Windows Sockets implementation may have a limit on the number of applications that may use it simultaneously. + // + wsaeproclim = 10067 + // + // MessageId: WSAEUSERS + // + // MessageText: + // + // Ran out of quota. + // + wsaeusers = 10068 + // + // MessageId: WSAEDQUOT + // + // MessageText: + // + // Ran out of disk quota. + // + wsaedquot = 10069 + // + // MessageId: WSAESTALE + // + // MessageText: + // + // File handle reference is no longer available. + // + wsaestale = 10070 + // + // MessageId: WSAEREMOTE + // + // MessageText: + // + // Item is not available locally. + // + wsaeremote = 10071 + // + // MessageId: WSASYSNOTREADY + // + // MessageText: + // + // WSAStartup cannot function at this time because the underlying system it uses to provide network services is currently unavailable. + // + wsasysnotready = 10091 + // + // MessageId: WSAVERNOTSUPPORTED + // + // MessageText: + // + // The Windows Sockets version requested is not supported. + // + wsavernotsupported = 10092 + // + // MessageId: WSANOTINITIALISED + // + // MessageText: + // + // Either the application has not called WSAStartup, or WSAStartup failed. + // + wsanotinitialised = 10093 + // + // MessageId: WSAEDISCON + // + // MessageText: + // + // Returned by WSARecv or WSARecvFrom to indicate the remote party has initiated a graceful shutdown sequence. + // + wsaediscon = 10101 + // + // MessageId: WSAENOMORE + // + // MessageText: + // + // No more results can be returned by WSALookupServiceNext. + // + wsaenomore = 10102 + // + // MessageId: WSAECANCELLED + // + // MessageText: + // + // A call to WSALookupServiceEnd was made while this call was still processing. The call has been canceled. + // + wsaecancelled = 10103 + // + // MessageId: WSAEINVALIDPROCTABLE + // + // MessageText: + // + // The procedure call table is invalid. + // + wsaeinvalidproctable = 10104 + // + // MessageId: WSAEINVALIDPROVIDER + // + // MessageText: + // + // The requested service provider is invalid. + // + wsaeinvalidprovider = 10105 + // + // MessageId: WSAEPROVIDERFAILEDINIT + // + // MessageText: + // + // The requested service provider could not be loaded or initialized. + // + wsaeproviderfailedinit = 10106 + // + // MessageId: WSASYSCALLFAILURE + // + // MessageText: + // + // A system call has failed. + // + wsasyscallfailure = 10107 + // + // MessageId: WSASERVICE_NOT_FOUND + // + // MessageText: + // + // No such service is known. The service cannot be found in the specified name space. + // + wsaservice_not_found = 10108 + // + // MessageId: WSATYPE_NOT_FOUND + // + // MessageText: + // + // The specified class was not found. + // + wsatype_not_found = 10109 + // + // MessageId: WSA_E_NO_MORE + // + // MessageText: + // + // No more results can be returned by WSALookupServiceNext. + // + wsa_e_no_more = 10110 + // + // MessageId: WSA_E_CANCELLED + // + // MessageText: + // + // A call to WSALookupServiceEnd was made while this call was still processing. The call has been canceled. + // + wsa_e_cancelled = 10111 + // + // MessageId: WSAEREFUSED + // + // MessageText: + // + // A database query failed because it was actively refused. + // + wsaerefused = 10112 + // + // MessageId: WSAHOST_NOT_FOUND + // + // MessageText: + // + // No such host is known. + // + wsahost_not_found = 11001 + // + // MessageId: WSATRY_AGAIN + // + // MessageText: + // + // This is usually a temporary error during hostname resolution and means that the local server did not receive a response from an authoritative server. + // + wsatry_again = 11002 + // + // MessageId: WSANO_RECOVERY + // + // MessageText: + // + // A non-recoverable error occurred during a database lookup. + // + wsano_recovery = 11003 + // + // MessageId: WSANO_DATA + // + // MessageText: + // + // The requested name is valid, but no data of the requested type was found. + // + wsano_data = 11004 + // + // MessageId: WSA_QOS_RECEIVERS + // + // MessageText: + // + // At least one reserve has arrived. + // + wsa_qos_receivers = 11005 + // + // MessageId: WSA_QOS_SENDERS + // + // MessageText: + // + // At least one path has arrived. + // + wsa_qos_senders = 11006 + // + // MessageId: WSA_QOS_NO_SENDERS + // + // MessageText: + // + // There are no senders. + // + wsa_qos_no_senders = 11007 + // + // MessageId: WSA_QOS_NO_RECEIVERS + // + // MessageText: + // + // There are no receivers. + // + wsa_qos_no_receivers = 11008 + // + // MessageId: WSA_QOS_REQUEST_CONFIRMED + // + // MessageText: + // + // Reserve has been confirmed. + // + wsa_qos_request_confirmed = 11009 + // + // MessageId: WSA_QOS_ADMISSION_FAILURE + // + // MessageText: + // + // Error due to lack of resources. + // + wsa_qos_admission_failure = 11010 + // + // MessageId: WSA_QOS_POLICY_FAILURE + // + // MessageText: + // + // Rejected for administrative reasons - bad credentials. + // + wsa_qos_policy_failure = 11011 + // + // MessageId: WSA_QOS_BAD_STYLE + // + // MessageText: + // + // Unknown or conflicting style. + // + wsa_qos_bad_style = 11012 + // + // MessageId: WSA_QOS_BAD_OBJECT + // + // MessageText: + // + // Problem with some part of the filterspec or providerspecific buffer in general. + // + wsa_qos_bad_object = 11013 + // + // MessageId: WSA_QOS_TRAFFIC_CTRL_ERROR + // + // MessageText: + // + // Problem with some part of the flowspec. + // + wsa_qos_traffic_ctrl_error = 11014 + // + // MessageId: WSA_QOS_GENERIC_ERROR + // + // MessageText: + // + // General QOS error. + // + wsa_qos_generic_error = 11015 + // + // MessageId: WSA_QOS_ESERVICETYPE + // + // MessageText: + // + // An invalid or unrecognized service type was found in the flowspec. + // + wsa_qos_eservicetype = 11016 + // + // MessageId: WSA_QOS_EFLOWSPEC + // + // MessageText: + // + // An invalid or inconsistent flowspec was found in the QOS structure. + // + wsa_qos_eflowspec = 11017 + // + // MessageId: WSA_QOS_EPROVSPECBUF + // + // MessageText: + // + // Invalid QOS provider-specific buffer. + // + wsa_qos_eprovspecbuf = 11018 + // + // MessageId: WSA_QOS_EFILTERSTYLE + // + // MessageText: + // + // An invalid QOS filter style was used. + // + wsa_qos_efilterstyle = 11019 + // + // MessageId: WSA_QOS_EFILTERTYPE + // + // MessageText: + // + // An invalid QOS filter type was used. + // + wsa_qos_efiltertype = 11020 + // + // MessageId: WSA_QOS_EFILTERCOUNT + // + // MessageText: + // + // An incorrect number of QOS FILTERSPECs were specified in the FLOWDESCRIPTOR. + // + wsa_qos_efiltercount = 11021 + // + // MessageId: WSA_QOS_EOBJLENGTH + // + // MessageText: + // + // An object with an invalid ObjectLength field was specified in the QOS provider-specific buffer. + // + wsa_qos_eobjlength = 11022 + // + // MessageId: WSA_QOS_EFLOWCOUNT + // + // MessageText: + // + // An incorrect number of flow descriptors was specified in the QOS structure. + // + wsa_qos_eflowcount = 11023 + // + // MessageId: WSA_QOS_EUNKOWNPSOBJ + // + // MessageText: + // + // An unrecognized object was found in the QOS provider-specific buffer. + // + wsa_qos_eunkownpsobj = 11024 + // + // MessageId: WSA_QOS_EPOLICYOBJ + // + // MessageText: + // + // An invalid policy object was found in the QOS provider-specific buffer. + // + wsa_qos_epolicyobj = 11025 + // + // MessageId: WSA_QOS_EFLOWDESC + // + // MessageText: + // + // An invalid QOS flow descriptor was found in the flow descriptor list. + // + wsa_qos_eflowdesc = 11026 + // + // MessageId: WSA_QOS_EPSFLOWSPEC + // + // MessageText: + // + // An invalid or inconsistent flowspec was found in the QOS provider specific buffer. + // + wsa_qos_epsflowspec = 11027 + // + // MessageId: WSA_QOS_EPSFILTERSPEC + // + // MessageText: + // + // An invalid FILTERSPEC was found in the QOS provider-specific buffer. + // + wsa_qos_epsfilterspec = 11028 + // + // MessageId: WSA_QOS_ESDMODEOBJ + // + // MessageText: + // + // An invalid shape discard mode object was found in the QOS provider specific buffer. + // + wsa_qos_esdmodeobj = 11029 + // + // MessageId: WSA_QOS_ESHAPERATEOBJ + // + // MessageText: + // + // An invalid shaping rate object was found in the QOS provider-specific buffer. + // + wsa_qos_eshaperateobj = 11030 + // + // MessageId: WSA_QOS_RESERVED_PETYPE + // + // MessageText: + // + // A reserved policy element was found in the QOS provider-specific buffer. + // + wsa_qos_reserved_petype = 11031 + // + // MessageId: WSA_SECURE_HOST_NOT_FOUND + // + // MessageText: + // + // No such host is known securely. + // + wsa_secure_host_not_found = 11032 + // + // MessageId: WSA_IPSEC_NAME_POLICY_ERROR + // + // MessageText: + // + // Name based IPSEC policy could not be added. + // + wsa_ipsec_name_policy_error = 11033 +} + +// wsa_error casts an int to its WsaError value +pub fn wsa_error(code int) WsaError { + return WsaError(code) +} + +const ( + error_ewouldblock = WsaError.wsaewouldblock +) + +// Link to Winsock library +#flag -lws2_32 +#include +#include + +// Constants that windows needs +const ( + fionbio = C.FIONBIO + msg_nosignal = 0 + wsa_v22 = 0x202 // C.MAKEWORD(2, 2) +) + +// Error code returns the last socket error +fn error_code() int { + return C.WSAGetLastError() +} + +struct C.WSAData { +mut: + wVersion u16 + wHighVersion u16 + szDescription [257]byte + szSystemStatus [129]byte + iMaxSockets u16 + iMaxUdpDg u16 + lpVendorInfo &byte +} + +fn init() { + mut wsadata := C.WSAData{ + lpVendorInfo: 0 + } + res := C.WSAStartup(net.wsa_v22, &wsadata) + if res != 0 { + panic('socket: WSAStartup failed') + } +} diff --git a/v_windows/v/old/vlib/net/openssl/c.v b/v_windows/v/old/vlib/net/openssl/c.v new file mode 100644 index 0000000..dedba2a --- /dev/null +++ b/v_windows/v/old/vlib/net/openssl/c.v @@ -0,0 +1,120 @@ +module openssl + +// On Linux, prefer a localy built openssl, because it is +// much more likely for it to be newer, than the system +// openssl from libssl-dev. If there is no local openssl, +// the next flag is harmless, since it will still use the +// (older) system openssl. +#flag linux -I/usr/local/include/openssl -L/usr/local/lib +#flag windows -l libssl -l libcrypto +#flag -lssl -lcrypto +#flag linux -ldl -lpthread +// MacPorts +#flag darwin -I/opt/local/include +#flag darwin -L/opt/local/lib +// Brew +#flag darwin -I/usr/local/opt/openssl/include +#flag darwin -L/usr/local/opt/openssl/lib +// Brew arm64 +#flag darwin -I /opt/homebrew/opt/openssl/include +#flag darwin -L /opt/homebrew/opt/openssl/lib +// +#include # Please install OpenSSL development headers +#include +#include + +pub struct C.SSL { +} + +pub struct SSL_CTX { +} + +pub struct SSL { +} + +pub struct SSL_METHOD { +} + +pub struct OPENSSL_INIT_SETTINGS { +} + +fn C.BIO_new_ssl_connect(ctx &C.SSL_CTX) &C.BIO + +fn C.BIO_set_conn_hostname(b &C.BIO, name &char) int + +// there are actually 2 macros for BIO_get_ssl +// fn C.BIO_get_ssl(bp &C.BIO, ssl charptr, c int) +// fn C.BIO_get_ssl(bp &C.BIO, sslp charptr) +fn C.BIO_get_ssl(bp &C.BIO, vargs ...voidptr) + +fn C.BIO_do_connect(b &C.BIO) int + +fn C.BIO_do_handshake(b &C.BIO) int + +fn C.BIO_puts(b &C.BIO, buf &char) + +fn C.BIO_read(b &C.BIO, buf voidptr, len int) int + +fn C.BIO_free_all(a &C.BIO) + +fn C.SSL_CTX_new(method &C.SSL_METHOD) &C.SSL_CTX + +fn C.SSL_CTX_set_options(ctx &C.SSL_CTX, options int) + +fn C.SSL_CTX_set_verify_depth(s &C.SSL_CTX, depth int) + +fn C.SSL_CTX_load_verify_locations(ctx &C.SSL_CTX, ca_file &char, ca_path &char) int + +fn C.SSL_CTX_free(ctx &C.SSL_CTX) + +fn C.SSL_new(&C.SSL_CTX) &C.SSL + +fn C.SSL_set_fd(ssl &C.SSL, fd int) int + +fn C.SSL_connect(&C.SSL) int + +fn C.SSL_set_cipher_list(ctx &SSL, str &char) int + +fn C.SSL_get_peer_certificate(ssl &SSL) &C.X509 + +fn C.ERR_clear_error() + +fn C.SSL_get_error(ssl &C.SSL, ret int) int + +fn C.SSL_get_verify_result(ssl &SSL) int + +fn C.SSL_set_tlsext_host_name(s &SSL, name &char) int + +fn C.SSL_shutdown(&C.SSL) int + +fn C.SSL_free(&C.SSL) + +fn C.SSL_write(ssl &C.SSL, buf voidptr, buflen int) int + +fn C.SSL_read(ssl &C.SSL, buf voidptr, buflen int) int + +fn C.SSL_load_error_strings() + +fn C.SSL_library_init() int + +fn C.SSLv23_client_method() &C.SSL_METHOD + +fn C.TLS_method() voidptr + +fn C.TLSv1_2_method() voidptr + +fn C.OPENSSL_init_ssl(opts u64, settings &OPENSSL_INIT_SETTINGS) int + +fn init() { + $if ssl_pre_1_1_version ? { + // OPENSSL_VERSION_NUMBER < 0x10100000L + C.SSL_load_error_strings() + C.SSL_library_init() + } $else { + C.OPENSSL_init_ssl(C.OPENSSL_INIT_LOAD_SSL_STRINGS, 0) + } +} + +pub const ( + is_used = 1 +) diff --git a/v_windows/v/old/vlib/net/openssl/openssl.v b/v_windows/v/old/vlib/net/openssl/openssl.v new file mode 100644 index 0000000..ffcabf5 --- /dev/null +++ b/v_windows/v/old/vlib/net/openssl/openssl.v @@ -0,0 +1,32 @@ +module openssl + +// ssl_error returns non error ssl code or error if unrecoverable and we should panic +pub fn ssl_error(ret int, ssl voidptr) ?SSLError { + res := C.SSL_get_error(ssl, ret) + match SSLError(res) { + .ssl_error_syscall { + return error_with_code('unrecoverable syscall ($res)', res) + } + .ssl_error_ssl { + return error_with_code('unrecoverable ssl protocol error ($res)', res) + } + else { + return SSLError(res) + } + } +} + +pub enum SSLError { + ssl_error_none = 0 // SSL_ERROR_NONE + ssl_error_ssl = 1 // SSL_ERROR_SSL + ssl_error_want_read = 2 // SSL_ERROR_WANT_READ + ssl_error_want_write = 3 // SSL_ERROR_WANT_WRITE + ssl_error_want_x509_lookup = 4 // SSL_ERROR_WANT_X509_LOOKUP + ssl_error_syscall = 5 // SSL_ERROR_SYSCALL + ssl_error_zero_return = 6 // SSL_ERROR_ZERO_RETURN + ssl_error_want_connect = 7 // SSL_ERROR_WANT_CONNECT + ssl_error_want_accept = 8 // SSL_ERROR_WANT_ACCEPT + ssl_error_want_async = 9 // SSL_ERROR_WANT_ASYNC + ssl_error_want_async_job = 10 // SSL_ERROR_WANT_ASYNC_JOB + ssl_error_want_early = 11 // SSL_ERROR_WANT_EARLY +} diff --git a/v_windows/v/old/vlib/net/openssl/ssl_connection.v b/v_windows/v/old/vlib/net/openssl/ssl_connection.v new file mode 100644 index 0000000..58f47f6 --- /dev/null +++ b/v_windows/v/old/vlib/net/openssl/ssl_connection.v @@ -0,0 +1,268 @@ +module openssl + +import net +import time + +// SSLConn is the current connection +pub struct SSLConn { +mut: + sslctx &C.SSL_CTX + ssl &C.SSL + handle int + duration time.Duration +} + +// new_ssl_conn instance an new SSLCon struct +pub fn new_ssl_conn() &SSLConn { + return &SSLConn{ + sslctx: 0 + ssl: 0 + handle: 0 + } +} + +// Select operation +enum Select { + read + write + except +} + +// shutdown closes the ssl connection and do clean up +pub fn (mut s SSLConn) shutdown() ? { + if s.ssl != 0 { + mut res := 0 + for { + res = C.SSL_shutdown(voidptr(s.ssl)) + if res < 0 { + err_res := ssl_error(res, s.ssl) or { + break // We break to free rest of resources + } + if err_res == .ssl_error_want_read { + for { + ready := @select(s.handle, .read, s.duration) ? + if ready { + break + } + } + continue + } else if err_res == .ssl_error_want_write { + for { + ready := @select(s.handle, .write, s.duration) ? + if ready { + break + } + } + continue + } else { + unsafe { C.SSL_free(voidptr(s.ssl)) } + if s.sslctx != 0 { + C.SSL_CTX_free(s.sslctx) + } + return error('unexepedted ssl error $err_res') + } + if s.ssl != 0 { + unsafe { C.SSL_free(voidptr(s.ssl)) } + } + if s.sslctx != 0 { + C.SSL_CTX_free(s.sslctx) + } + return error('Could not connect using SSL. ($err_res),err') + } else if res == 0 { + continue + } else if res == 1 { + break + } + } + C.SSL_free(voidptr(s.ssl)) + } + if s.sslctx != 0 { + C.SSL_CTX_free(s.sslctx) + } +} + +// connect to server using open ssl +pub fn (mut s SSLConn) connect(mut tcp_conn net.TcpConn, hostname string) ? { + s.handle = tcp_conn.sock.handle + s.duration = tcp_conn.read_timeout() + + s.sslctx = unsafe { C.SSL_CTX_new(C.SSLv23_client_method()) } + if s.sslctx == 0 { + return error("Couldn't get ssl context") + } + + // TODO: Fix option to enable/disable checks for valid + // certificates to allow both secure and self signed + // for now the checks are not done at all to comply + // to current autobahn tests + + // C.SSL_CTX_set_verify_depth(s.sslctx, 4) + // flags := C.SSL_OP_NO_SSLv2 | C.SSL_OP_NO_SSLv3 | C.SSL_OP_NO_COMPRESSION + // C.SSL_CTX_set_options(s.sslctx, flags) + // mut res := C.SSL_CTX_load_verify_locations(s.sslctx, 'random-org-chain.pem', 0) + + s.ssl = unsafe { &C.SSL(C.SSL_new(s.sslctx)) } + if s.ssl == 0 { + return error("Couldn't create OpenSSL instance.") + } + + // preferred_ciphers := 'HIGH:!aNULL:!kRSA:!PSK:!SRP:!MD5:!RC4' + // mut res := C.SSL_set_cipher_list(s.ssl, preferred_ciphers.str) + // if res != 1 { + // println('http: openssl: cipher failed') + // } + + mut res := C.SSL_set_tlsext_host_name(voidptr(s.ssl), voidptr(hostname.str)) + if res != 1 { + return error('cannot set host name') + } + + if C.SSL_set_fd(voidptr(s.ssl), tcp_conn.sock.handle) != 1 { + return error("Couldn't assign ssl to socket.") + } + for { + res = C.SSL_connect(voidptr(s.ssl)) + if res != 1 { + err_res := ssl_error(res, s.ssl) ? + if err_res == .ssl_error_want_read { + for { + ready := @select(s.handle, .read, s.duration) ? + if ready { + break + } + } + continue + } else if err_res == .ssl_error_want_write { + for { + ready := @select(s.handle, .write, s.duration) ? + if ready { + break + } + } + continue + } + return error('Could not connect using SSL. ($err_res),err') + } + break + } +} + +pub fn (mut s SSLConn) socket_read_into_ptr(buf_ptr &byte, len int) ?int { + mut res := 0 + for { + res = C.SSL_read(voidptr(s.ssl), buf_ptr, len) + if res < 0 { + err_res := ssl_error(res, s.ssl) ? + if err_res == .ssl_error_want_read { + for { + ready := @select(s.handle, .read, s.duration) ? + if ready { + break + } + } + continue + } else if err_res == .ssl_error_want_write { + for { + ready := @select(s.handle, .write, s.duration) ? + if ready { + break + } + } + continue + } else if err_res == .ssl_error_zero_return { + return 0 + } + return error('Could not read using SSL. ($err_res)') + } + break + } + return res +} + +pub fn (mut s SSLConn) read_into(mut buffer []byte) ?int { + res := s.socket_read_into_ptr(&byte(buffer.data), buffer.len) ? + return res +} + +// write number of bytes to SSL connection +pub fn (mut s SSLConn) write(bytes []byte) ?int { + unsafe { + mut ptr_base := &byte(bytes.data) + mut total_sent := 0 + for total_sent < bytes.len { + ptr := ptr_base + total_sent + remaining := bytes.len - total_sent + mut sent := C.SSL_write(voidptr(s.ssl), ptr, remaining) + if sent <= 0 { + err_res := ssl_error(sent, s.ssl) ? + if err_res == .ssl_error_want_read { + for { + ready := @select(s.handle, .read, s.duration) ? + if ready { + break + } + } + } else if err_res == .ssl_error_want_write { + for { + ready := @select(s.handle, .write, s.duration) ? + if ready { + break + } + } + continue + } else if err_res == .ssl_error_zero_return { + return error('ssl write on closed connection') // Todo error_with_code close + } + return error_with_code('Could not write SSL. ($err_res),err', int(err_res)) + } + total_sent += sent + } + return total_sent + } +} + +/* +This is basically a copy of Emily socket implementation of select. + This have to be consolidated into common net lib features + when merging this to V +*/ +// [typedef] +// pub struct C.fd_set { +// } + +// Select waits for an io operation (specified by parameter `test`) to be available +fn @select(handle int, test Select, timeout time.Duration) ?bool { + set := C.fd_set{} + + C.FD_ZERO(&set) + C.FD_SET(handle, &set) + + seconds := timeout.milliseconds() / 1000 + microseconds := timeout - (seconds * time.second) + mut tt := C.timeval{ + tv_sec: u64(seconds) + tv_usec: u64(microseconds) + } + + mut timeval_timeout := &tt + + // infinite timeout is signaled by passing null as the timeout to + // select + if timeout == net.infinite_timeout { + timeval_timeout = &C.timeval(0) + } + + match test { + .read { + net.socket_error(C.@select(handle + 1, &set, C.NULL, C.NULL, timeval_timeout)) ? + } + .write { + net.socket_error(C.@select(handle + 1, C.NULL, &set, C.NULL, timeval_timeout)) ? + } + .except { + net.socket_error(C.@select(handle + 1, C.NULL, C.NULL, &set, timeval_timeout)) ? + } + } + + return C.FD_ISSET(handle, &set) +} diff --git a/v_windows/v/old/vlib/net/smtp/smtp.v b/v_windows/v/old/vlib/net/smtp/smtp.v new file mode 100644 index 0000000..50c537c --- /dev/null +++ b/v_windows/v/old/vlib/net/smtp/smtp.v @@ -0,0 +1,190 @@ +module smtp + +/* +* +* smtp module +* Created by: nedimf (07/2020) +*/ +import net +import encoding.base64 +import strings +import time +import io + +const ( + recv_size = 128 +) + +enum ReplyCode { + ready = 220 + close = 221 + auth_ok = 235 + action_ok = 250 + mail_start = 354 +} + +pub enum BodyType { + text + html +} + +pub struct Client { +mut: + conn net.TcpConn + reader io.BufferedReader +pub: + server string + port int = 25 + username string + password string + from string +pub mut: + is_open bool +} + +pub struct Mail { + from string + to string + cc string + bcc string + date time.Time = time.now() + subject string + body_type BodyType + body string +} + +// new_client returns a new SMTP client and connects to it +pub fn new_client(config Client) ?&Client { + mut c := &Client{ + ...config + } + c.reconnect() ? + return c +} + +// reconnect reconnects to the SMTP server if the connection was closed +pub fn (mut c Client) reconnect() ? { + if c.is_open { + return error('Already connected to server') + } + + conn := net.dial_tcp('$c.server:$c.port') or { return error('Connecting to server failed') } + c.conn = conn + + c.reader = io.new_buffered_reader(reader: c.conn) + + c.expect_reply(.ready) or { return error('Received invalid response from server') } + c.send_ehlo() or { return error('Sending EHLO packet failed') } + c.send_auth() or { return error('Authenticating to server failed') } + c.is_open = true +} + +// send sends an email +pub fn (mut c Client) send(config Mail) ? { + if !c.is_open { + return error('Disconnected from server') + } + from := if config.from != '' { config.from } else { c.from } + c.send_mailfrom(from) or { return error('Sending mailfrom failed') } + c.send_mailto(config.to) or { return error('Sending mailto failed') } + c.send_data() or { return error('Sending mail data failed') } + c.send_body(Mail{ + ...config + from: from + }) or { return error('Sending mail body failed') } +} + +// quit closes the connection to the server +pub fn (mut c Client) quit() ? { + c.send_str('QUIT\r\n') ? + c.expect_reply(.close) ? + c.conn.close() ? + c.is_open = false +} + +// expect_reply checks if the SMTP server replied with the expected reply code +fn (mut c Client) expect_reply(expected ReplyCode) ? { + bytes := io.read_all(reader: c.conn) ? + + str := bytes.bytestr().trim_space() + $if smtp_debug ? { + eprintln('\n\n[RECV]') + eprint(str) + } + + if str.len >= 3 { + status := str[..3].int() + if ReplyCode(status) != expected { + return error('Received unexpected status code $status, expecting $expected') + } + } else { + return error('Recieved unexpected SMTP data: $str') + } +} + +[inline] +fn (mut c Client) send_str(s string) ? { + $if smtp_debug ? { + eprintln('\n\n[SEND START]') + eprint(s.trim_space()) + eprintln('\n[SEND END]') + } + c.conn.write(s.bytes()) ? +} + +[inline] +fn (mut c Client) send_ehlo() ? { + c.send_str('EHLO $c.server\r\n') ? + c.expect_reply(.action_ok) ? +} + +[inline] +fn (mut c Client) send_auth() ? { + if c.username.len == 0 { + return + } + mut sb := strings.new_builder(100) + sb.write_b(0) + sb.write_string(c.username) + sb.write_b(0) + sb.write_string(c.password) + a := sb.str() + auth := 'AUTH PLAIN ${base64.encode_str(a)}\r\n' + c.send_str(auth) ? + c.expect_reply(.auth_ok) ? +} + +fn (mut c Client) send_mailfrom(from string) ? { + c.send_str('MAIL FROM: <$from>\r\n') ? + c.expect_reply(.action_ok) ? +} + +fn (mut c Client) send_mailto(to string) ? { + c.send_str('RCPT TO: <$to>\r\n') ? + c.expect_reply(.action_ok) ? +} + +fn (mut c Client) send_data() ? { + c.send_str('DATA\r\n') ? + c.expect_reply(.mail_start) ? +} + +fn (mut c Client) send_body(cfg Mail) ? { + is_html := cfg.body_type == .html + date := cfg.date.utc_string().trim_right(' UTC') // TODO + mut sb := strings.new_builder(200) + sb.write_string('From: $cfg.from\r\n') + sb.write_string('To: <$cfg.to>\r\n') + sb.write_string('Cc: <$cfg.cc>\r\n') + sb.write_string('Bcc: <$cfg.bcc>\r\n') + sb.write_string('Date: $date\r\n') + sb.write_string('Subject: $cfg.subject\r\n') + if is_html { + sb.write_string('Content-Type: text/html; charset=ISO-8859-1') + } + sb.write_string('\r\n\r\n') + sb.write_string(cfg.body) + sb.write_string('\r\n.\r\n') + c.send_str(sb.str()) ? + c.expect_reply(.action_ok) ? +} diff --git a/v_windows/v/old/vlib/net/smtp/smtp_test.v b/v_windows/v/old/vlib/net/smtp/smtp_test.v new file mode 100644 index 0000000..d975e57 --- /dev/null +++ b/v_windows/v/old/vlib/net/smtp/smtp_test.v @@ -0,0 +1,89 @@ +import os +import net.smtp +import time + +// Used to test that a function call returns an error +fn fn_errors(mut c smtp.Client, m smtp.Mail) bool { + c.send(m) or { return true } + return false +} + +/* +* +* smtp_test +* Created by: nedimf (07/2020) +*/ +fn test_smtp() { + $if !network ? { + return + } + + client_cfg := smtp.Client{ + server: 'smtp.mailtrap.io' + from: 'dev@vlang.io' + username: os.getenv('VSMTP_TEST_USER') + password: os.getenv('VSMTP_TEST_PASS') + } + if client_cfg.username == '' && client_cfg.password == '' { + eprintln('Please set VSMTP_TEST_USER and VSMTP_TEST_PASS before running this test') + exit(0) + } + send_cfg := smtp.Mail{ + to: 'dev@vlang.io' + subject: 'Hello from V2' + body: 'Plain text' + } + + mut client := smtp.new_client(client_cfg) or { + assert false + return + } + assert true + client.send(send_cfg) or { + assert false + return + } + assert true + client.send(smtp.Mail{ + ...send_cfg + from: 'alexander@vlang.io' + }) or { + assert false + return + } + client.send(smtp.Mail{ + ...send_cfg + cc: 'alexander@vlang.io,joe@vlang.io' + bcc: 'spytheman@vlang.io' + }) or { + assert false + return + } + client.send(smtp.Mail{ + ...send_cfg + date: time.now().add_days(1000) + }) or { + assert false + return + } + assert true + client.quit() or { + assert false + return + } + assert true + // This call should return an error, since the connection is closed + if !fn_errors(mut client, send_cfg) { + assert false + return + } + client.reconnect() or { + assert false + return + } + client.send(send_cfg) or { + assert false + return + } + assert true +} diff --git a/v_windows/v/old/vlib/net/socket_options.c.v b/v_windows/v/old/vlib/net/socket_options.c.v new file mode 100644 index 0000000..4e3240f --- /dev/null +++ b/v_windows/v/old/vlib/net/socket_options.c.v @@ -0,0 +1,50 @@ +module net + +pub enum SocketOption { + // TODO: SO_ACCEPT_CONN is not here becuase windows doesnt support it + // and there is no easy way to define it + broadcast = C.SO_BROADCAST + debug = C.SO_DEBUG + dont_route = C.SO_DONTROUTE + error = C.SO_ERROR + keep_alive = C.SO_KEEPALIVE + linger = C.SO_LINGER + oob_inline = C.SO_OOBINLINE + reuse_addr = C.SO_REUSEADDR + recieve_buf_size = C.SO_RCVBUF + recieve_low_size = C.SO_RCVLOWAT + recieve_timeout = C.SO_RCVTIMEO + send_buf_size = C.SO_SNDBUF + send_low_size = C.SO_SNDLOWAT + send_timeout = C.SO_SNDTIMEO + socket_type = C.SO_TYPE + ipv6_only = C.IPV6_V6ONLY +} + +const ( + opts_bool = [SocketOption.broadcast, .debug, .dont_route, .error, .keep_alive, .oob_inline] + opts_int = [ + .recieve_buf_size, + .recieve_low_size, + .recieve_timeout, + .send_buf_size, + .send_low_size, + .send_timeout, + ] + + opts_can_set = [ + SocketOption.broadcast, + .debug, + .dont_route, + .keep_alive, + .linger, + .oob_inline, + .recieve_buf_size, + .recieve_low_size, + .recieve_timeout, + .send_buf_size, + .send_low_size, + .send_timeout, + .ipv6_only, + ] +) diff --git a/v_windows/v/old/vlib/net/tcp.v b/v_windows/v/old/vlib/net/tcp.v new file mode 100644 index 0000000..9a11db9 --- /dev/null +++ b/v_windows/v/old/vlib/net/tcp.v @@ -0,0 +1,381 @@ +module net + +import time + +const ( + tcp_default_read_timeout = 30 * time.second + tcp_default_write_timeout = 30 * time.second +) + +[heap] +pub struct TcpConn { +pub mut: + sock TcpSocket +mut: + write_deadline time.Time + read_deadline time.Time + read_timeout time.Duration + write_timeout time.Duration +} + +pub fn dial_tcp(address string) ?&TcpConn { + addrs := resolve_addrs_fuzzy(address, .tcp) ? + + // Very simple dialer + for addr in addrs { + mut s := new_tcp_socket(addr.family()) ? + s.connect(addr) or { + // Connection failed + s.close() or { continue } + continue + } + + return &TcpConn{ + sock: s + read_timeout: net.tcp_default_read_timeout + write_timeout: net.tcp_default_write_timeout + } + } + // failed + return error('dial_tcp failed') +} + +pub fn (mut c TcpConn) close() ? { + c.sock.close() ? +} + +// write_ptr blocks and attempts to write all data +pub fn (mut c TcpConn) write_ptr(b &byte, len int) ?int { + $if trace_tcp ? { + eprintln( + '>>> TcpConn.write_ptr | c.sock.handle: $c.sock.handle | b: ${ptr_str(b)} len: $len |\n' + + unsafe { b.vstring_with_len(len) }) + } + unsafe { + mut ptr_base := &byte(b) + mut total_sent := 0 + for total_sent < len { + ptr := ptr_base + total_sent + remaining := len - total_sent + mut sent := C.send(c.sock.handle, ptr, remaining, msg_nosignal) + if sent < 0 { + code := error_code() + if code == int(error_ewouldblock) { + c.wait_for_write() ? + continue + } else { + wrap_error(code) ? + } + } + total_sent += sent + } + return total_sent + } +} + +// write blocks and attempts to write all data +pub fn (mut c TcpConn) write(bytes []byte) ?int { + return c.write_ptr(bytes.data, bytes.len) +} + +// write_string blocks and attempts to write all data +pub fn (mut c TcpConn) write_string(s string) ?int { + return c.write_ptr(s.str, s.len) +} + +pub fn (mut c TcpConn) read_ptr(buf_ptr &byte, len int) ?int { + mut res := wrap_read_result(C.recv(c.sock.handle, voidptr(buf_ptr), len, 0)) ? + $if trace_tcp ? { + eprintln('<<< TcpConn.read_ptr | c.sock.handle: $c.sock.handle | buf_ptr: ${ptr_str(buf_ptr)} len: $len | res: $res') + } + if res > 0 { + return res + } + code := error_code() + if code == int(error_ewouldblock) { + c.wait_for_read() ? + res = wrap_read_result(C.recv(c.sock.handle, voidptr(buf_ptr), len, 0)) ? + $if trace_tcp ? { + eprintln('<<< TcpConn.read_ptr | c.sock.handle: $c.sock.handle | buf_ptr: ${ptr_str(buf_ptr)} len: $len | res: $res') + } + return socket_error(res) + } else { + wrap_error(code) ? + } + return none +} + +pub fn (mut c TcpConn) read(mut buf []byte) ?int { + return c.read_ptr(buf.data, buf.len) +} + +pub fn (mut c TcpConn) read_deadline() ?time.Time { + if c.read_deadline.unix == 0 { + return c.read_deadline + } + return none +} + +pub fn (mut c TcpConn) set_read_deadline(deadline time.Time) { + c.read_deadline = deadline +} + +pub fn (mut c TcpConn) write_deadline() ?time.Time { + if c.write_deadline.unix == 0 { + return c.write_deadline + } + return none +} + +pub fn (mut c TcpConn) set_write_deadline(deadline time.Time) { + c.write_deadline = deadline +} + +pub fn (c &TcpConn) read_timeout() time.Duration { + return c.read_timeout +} + +pub fn (mut c TcpConn) set_read_timeout(t time.Duration) { + c.read_timeout = t +} + +pub fn (c &TcpConn) write_timeout() time.Duration { + return c.write_timeout +} + +pub fn (mut c TcpConn) set_write_timeout(t time.Duration) { + c.write_timeout = t +} + +[inline] +pub fn (mut c TcpConn) wait_for_read() ? { + return wait_for_read(c.sock.handle, c.read_deadline, c.read_timeout) +} + +[inline] +pub fn (mut c TcpConn) wait_for_write() ? { + return wait_for_write(c.sock.handle, c.write_deadline, c.write_timeout) +} + +pub fn (c &TcpConn) peer_addr() ?Addr { + mut addr := Addr{ + addr: AddrData{ + Ip6: Ip6{} + } + } + mut size := sizeof(Addr) + socket_error(C.getpeername(c.sock.handle, voidptr(&addr), &size)) ? + return addr +} + +pub fn (c &TcpConn) peer_ip() ?string { + return c.peer_addr() ?.str() +} + +pub fn (c &TcpConn) addr() ?Addr { + return c.sock.address() +} + +pub fn (c TcpConn) str() string { + s := c.sock.str().replace('\n', ' ').replace(' ', ' ') + return 'TcpConn{ write_deadline: $c.write_deadline, read_deadline: $c.read_deadline, read_timeout: $c.read_timeout, write_timeout: $c.write_timeout, sock: $s }' +} + +pub struct TcpListener { +pub mut: + sock TcpSocket +mut: + accept_timeout time.Duration + accept_deadline time.Time +} + +pub fn listen_tcp(family AddrFamily, saddr string) ?&TcpListener { + s := new_tcp_socket(family) ? + + addrs := resolve_addrs(saddr, family, .tcp) ? + + // TODO(logic to pick here) + addr := addrs[0] + + // cast to the correct type + alen := addr.len() + bindres := C.bind(s.handle, voidptr(&addr), alen) + socket_error(bindres) ? + socket_error(C.listen(s.handle, 128)) ? + return &TcpListener{ + sock: s + accept_deadline: no_deadline + accept_timeout: infinite_timeout + } +} + +pub fn (mut l TcpListener) accept() ?&TcpConn { + addr := Addr{ + addr: AddrData{ + Ip6: Ip6{} + } + } + size := sizeof(Addr) + mut new_handle := C.accept(l.sock.handle, voidptr(&addr), &size) + if new_handle <= 0 { + l.wait_for_accept() ? + new_handle = C.accept(l.sock.handle, voidptr(&addr), &size) + if new_handle == -1 || new_handle == 0 { + return error('accept failed') + } + } + new_sock := tcp_socket_from_handle(new_handle) ? + return &TcpConn{ + sock: new_sock + read_timeout: net.tcp_default_read_timeout + write_timeout: net.tcp_default_write_timeout + } +} + +pub fn (c &TcpListener) accept_deadline() ?time.Time { + if c.accept_deadline.unix != 0 { + return c.accept_deadline + } + return error('invalid deadline') +} + +pub fn (mut c TcpListener) set_accept_deadline(deadline time.Time) { + c.accept_deadline = deadline +} + +pub fn (c &TcpListener) accept_timeout() time.Duration { + return c.accept_timeout +} + +pub fn (mut c TcpListener) set_accept_timeout(t time.Duration) { + c.accept_timeout = t +} + +pub fn (mut c TcpListener) wait_for_accept() ? { + return wait_for_read(c.sock.handle, c.accept_deadline, c.accept_timeout) +} + +pub fn (mut c TcpListener) close() ? { + c.sock.close() ? +} + +pub fn (c &TcpListener) addr() ?Addr { + return c.sock.address() +} + +struct TcpSocket { +pub: + handle int +} + +fn new_tcp_socket(family AddrFamily) ?TcpSocket { + handle := socket_error(C.socket(family, SocketType.tcp, 0)) ? + mut s := TcpSocket{ + handle: handle + } + // TODO(emily): + // we shouldnt be using ioctlsocket in the 21st century + // use the non-blocking socket option instead please :) + + // TODO(emily): + // Move this to its own function on the socket + s.set_option_int(.reuse_addr, 1) ? + $if windows { + t := u32(1) // true + socket_error(C.ioctlsocket(handle, fionbio, &t)) ? + } $else { + socket_error(C.fcntl(handle, C.F_SETFL, C.fcntl(handle, C.F_GETFL) | C.O_NONBLOCK)) ? + } + return s +} + +fn tcp_socket_from_handle(sockfd int) ?TcpSocket { + mut s := TcpSocket{ + handle: sockfd + } + // s.set_option_bool(.reuse_addr, true)? + s.set_option_int(.reuse_addr, 1) ? + s.set_dualstack(true) or { + // Not ipv6, we dont care + } + $if windows { + t := u32(1) // true + socket_error(C.ioctlsocket(sockfd, fionbio, &t)) ? + } $else { + socket_error(C.fcntl(sockfd, C.F_SETFL, C.fcntl(sockfd, C.F_GETFL) | C.O_NONBLOCK)) ? + } + return s +} + +pub fn (mut s TcpSocket) set_option_bool(opt SocketOption, value bool) ? { + // TODO reenable when this `in` operation works again + // if opt !in opts_can_set { + // return err_option_not_settable + // } + // if opt !in opts_bool { + // return err_option_wrong_type + // } + x := int(value) + socket_error(C.setsockopt(s.handle, C.SOL_SOCKET, int(opt), &x, sizeof(int))) ? +} + +pub fn (mut s TcpSocket) set_dualstack(on bool) ? { + x := int(!on) + socket_error(C.setsockopt(s.handle, C.IPPROTO_IPV6, int(SocketOption.ipv6_only), &x, + sizeof(int))) ? +} + +pub fn (mut s TcpSocket) set_option_int(opt SocketOption, value int) ? { + socket_error(C.setsockopt(s.handle, C.SOL_SOCKET, int(opt), &value, sizeof(int))) ? +} + +fn (mut s TcpSocket) close() ? { + return shutdown(s.handle) +} + +fn (mut s TcpSocket) @select(test Select, timeout time.Duration) ?bool { + return @select(s.handle, test, timeout) +} + +const ( + connect_timeout = 5 * time.second +) + +fn (mut s TcpSocket) connect(a Addr) ? { + res := C.connect(s.handle, voidptr(&a), a.len()) + if res == 0 { + return + } + + // The socket is nonblocking and the connection cannot be completed + // immediately. (UNIX domain sockets failed with EAGAIN instead.) + // It is possible to select(2) or poll(2) for completion by selecting + // the socket for writing. After select(2) indicates writability, + // use getsockopt(2) to read the SO_ERROR option at level SOL_SOCKET to + // determine whether connect() completed successfully (SO_ERROR is zero) or + // unsuccessfully (SO_ERROR is one of the usual error codes listed here, + // ex‐ plaining the reason for the failure). + write_result := s.@select(.write, net.connect_timeout) ? + if write_result { + err := 0 + len := sizeof(err) + socket_error(C.getsockopt(s.handle, C.SOL_SOCKET, C.SO_ERROR, &err, &len)) ? + + if err != 0 { + return wrap_error(err) + } + // Succeeded + return + } + + // Get the error + socket_error(C.connect(s.handle, voidptr(&a), a.len())) ? + + // otherwise we timed out + return err_connect_timed_out +} + +// address gets the address of a socket +pub fn (s &TcpSocket) address() ?Addr { + return addr_from_socket_handle(s.handle) +} diff --git a/v_windows/v/old/vlib/net/tcp_read_line.v b/v_windows/v/old/vlib/net/tcp_read_line.v new file mode 100644 index 0000000..7641866 --- /dev/null +++ b/v_windows/v/old/vlib/net/tcp_read_line.v @@ -0,0 +1,55 @@ +module net + +const ( + crlf = '\r\n' + msg_peek = 0x02 + max_read = 400 +) + +// read_line is a *simple*, *non customizable*, blocking line reader. +// It will *always* return a line, ending with CRLF, or just '', on EOF. +// NB: if you want more control over the buffer, please use a buffered IO +// reader instead: `io.new_buffered_reader({reader: io.make_reader(con)})` +pub fn (mut con TcpConn) read_line() string { + mut buf := [net.max_read]byte{} // where C.recv will store the network data + mut res := '' // The final result, including the ending \n. + for { + mut line := '' // The current line. Can be a partial without \n in it. + n := C.recv(con.sock.handle, &buf[0], net.max_read - 1, net.msg_peek | msg_nosignal) + if n == -1 { + return res + } + if n == 0 { + return res + } + buf[n] = `\0` + mut eol_idx := -1 + for i in 0 .. n { + if int(buf[i]) == `\n` { + eol_idx = i + // Ensure that tos_clone(buf) later, + // will return *only* the first line (including \n), + // and ignore the rest + buf[i + 1] = `\0` + break + } + } + line = unsafe { tos_clone(&buf[0]) } + if eol_idx > 0 { + // At this point, we are sure that recv returned valid data, + // that contains *at least* one line. + // Ensure that the block till the first \n (including it) + // is removed from the socket's receive queue, so that it does + // not get read again. + C.recv(con.sock.handle, &buf[0], eol_idx + 1, msg_nosignal) + res += line + break + } + // recv returned a buffer without \n in it . + C.recv(con.sock.handle, &buf[0], n, msg_nosignal) + res += line + res += net.crlf + break + } + return res +} diff --git a/v_windows/v/old/vlib/net/tcp_simple_client_server_test.v b/v_windows/v/old/vlib/net/tcp_simple_client_server_test.v new file mode 100644 index 0000000..317933f --- /dev/null +++ b/v_windows/v/old/vlib/net/tcp_simple_client_server_test.v @@ -0,0 +1,150 @@ +import io +import net +import strings + +const ( + server_port = ':22443' +) + +fn accept(mut server net.TcpListener, c chan &net.TcpConn) { + c <- server.accept() or { panic(err) } +} + +fn setup() (&net.TcpListener, &net.TcpConn, &net.TcpConn) { + mut server := net.listen_tcp(.ip6, server_port) or { panic(err) } + + c := chan &net.TcpConn{} + go accept(mut server, c) + mut client := net.dial_tcp('localhost$server_port') or { panic(err) } + + socket := <-c + + $if debug_peer_ip ? { + eprintln('$server.addr()\n$client.peer_addr(), $client.addr()\n$socket.peer_addr(), $socket.addr()') + } + assert true + return server, client, socket +} + +fn cleanup(mut server net.TcpListener, mut client net.TcpConn, mut socket net.TcpConn) { + server.close() or {} + client.close() or {} + socket.close() or {} +} + +fn test_socket() { + mut server, mut client, mut socket := setup() + defer { + cleanup(mut server, mut client, mut socket) + } + message := 'Hello World' + socket.write_string(message) or { + assert false + return + } + assert true + $if debug { + println('message send: $message') + } + $if debug { + println('send socket: $socket.sock.handle') + } + mut buf := []byte{len: 1024} + nbytes := client.read(mut buf) or { + assert false + return + } + received := buf[0..nbytes].bytestr() + $if debug { + println('message received: $received') + } + $if debug { + println('client: $client.sock.handle') + } + assert message == received +} + +fn test_socket_write_and_read() { + mut server, mut client, mut socket := setup() + defer { + cleanup(mut server, mut client, mut socket) + } + message1 := 'a message 1' + socket.write_string(message1) or { assert false } + mut rbuf := []byte{len: message1.len} + client.read(mut rbuf) or { + assert false + return + } + line := rbuf.bytestr() + assert line == message1 +} + +fn test_socket_read_line() { + mut server, mut client, mut socket := setup() + mut reader := io.new_buffered_reader(reader: client) + defer { + cleanup(mut server, mut client, mut socket) + } + message1, message2 := 'message1', 'message2' + message := '$message1\n$message2\n' + socket.write_string(message) or { assert false } + assert true + // + line1 := reader.read_line() or { + // println(reader.buf) + assert false + return + } + line2 := reader.read_line() or { + // println(reader.buf) + assert false + return + } + assert line1 == message1 + assert line2 == message2 +} + +fn test_socket_write_fail_without_panic() { + mut server, mut client, mut socket := setup() + defer { + cleanup(mut server, mut client, mut socket) + } + message2 := 'a message 2' + // ensure that socket.write (i.e. done on the server side) + // continues to work, even when the client side has been disconnected + // this test is important for a stable long standing server + client.close() or {} + $if solaris { + return + } + // TODO: fix segfaulting on Solaris + for i := 0; i < 3; i++ { + socket.write_string(message2) or { + println('write to a socket without a recipient should produce an option fail: $err | $message2') + assert true + } + } +} + +fn test_socket_read_line_long_line_without_eol() { + mut server, mut client, mut socket := setup() + mut reader := io.new_buffered_reader(reader: client) + defer { + cleanup(mut server, mut client, mut socket) + } + message := strings.repeat_string('123', 400) + socket.write_string(message) or { + assert false + return + } + socket.write_string('\n') or { + assert false + return + } + line := reader.read_line() or { + assert false + return + } + assert line == message +} diff --git a/v_windows/v/old/vlib/net/tcp_test.v b/v_windows/v/old/vlib/net/tcp_test.v new file mode 100644 index 0000000..cbd2aa4 --- /dev/null +++ b/v_windows/v/old/vlib/net/tcp_test.v @@ -0,0 +1,100 @@ +import net +import os + +const ( + test_port = 45123 +) + +fn handle_conn(mut c net.TcpConn) { + for { + mut buf := []byte{len: 100, init: 0} + read := c.read(mut buf) or { + println('Server: connection dropped') + return + } + c.write(buf[..read]) or { + println('Server: connection dropped') + return + } + } +} + +fn one_shot_echo_server(mut l net.TcpListener, ch_started chan int) ? { + eprintln('> one_shot_echo_server') + ch_started <- 1 + mut new_conn := l.accept() or { return error('could not accept') } + eprintln(' > new_conn: $new_conn') + handle_conn(mut new_conn) + new_conn.close() or {} +} + +fn echo(address string) ? { + mut c := net.dial_tcp(address) ? + defer { + c.close() or {} + } + + println('local: ' + c.addr() ?.str()) + println(' peer: ' + c.peer_addr() ?.str()) + + data := 'Hello from vlib/net!' + c.write_string(data) ? + mut buf := []byte{len: 4096} + read := c.read(mut buf) ? + assert read == data.len + for i := 0; i < read; i++ { + assert buf[i] == data[i] + } + println('Got "$buf.bytestr()"') +} + +fn test_tcp_ip6() { + eprintln('\n>>> ${@FN}') + address := 'localhost:$test_port' + mut l := net.listen_tcp(.ip6, ':$test_port') or { panic(err) } + dump(l) + start_echo_server(mut l) + echo(address) or { panic(err) } + l.close() or {} + // ensure there is at least one new socket created before the next test + l = net.listen_tcp(.ip6, ':${test_port + 1}') or { panic(err) } +} + +fn start_echo_server(mut l net.TcpListener) { + ch_server_started := chan int{} + go one_shot_echo_server(mut l, ch_server_started) + _ := <-ch_server_started +} + +fn test_tcp_ip() { + eprintln('\n>>> ${@FN}') + address := 'localhost:$test_port' + mut l := net.listen_tcp(.ip, address) or { panic(err) } + dump(l) + start_echo_server(mut l) + echo(address) or { panic(err) } + l.close() or {} +} + +fn test_tcp_unix() { + eprintln('\n>>> ${@FN}') + // TODO(emily): + // whilst windows supposedly supports unix sockets + // this doesnt work (wsaeopnotsupp at the call to bind()) + $if !windows { + address := os.real_path('tcp-test.sock') + // address := 'tcp-test.sock' + println('$address') + + mut l := net.listen_tcp(.unix, address) or { panic(err) } + start_echo_server(mut l) + echo(address) or { panic(err) } + l.close() or {} + + os.rm(address) or { panic('failed to remove socket file') } + } +} + +fn testsuite_end() { + eprintln('\ndone') +} diff --git a/v_windows/v/old/vlib/net/udp.v b/v_windows/v/old/vlib/net/udp.v new file mode 100644 index 0000000..874b2b7 --- /dev/null +++ b/v_windows/v/old/vlib/net/udp.v @@ -0,0 +1,287 @@ +module net + +import time + +const ( + udp_default_read_timeout = time.second / 10 + udp_default_write_timeout = time.second / 10 +) + +struct UdpSocket { + handle int + l Addr + // TODO(emily): replace with option again + // when i figure out how to coerce it properly +mut: + has_r bool + r Addr +} + +pub struct UdpConn { +pub mut: + sock UdpSocket +mut: + write_deadline time.Time + read_deadline time.Time + read_timeout time.Duration + write_timeout time.Duration +} + +pub fn dial_udp(raddr string) ?&UdpConn { + addrs := resolve_addrs_fuzzy(raddr, .udp) ? + + for addr in addrs { + // create a local socket for this + // bind to any port (or file) (we dont care in this + // case because we only care about the remote) + if sock := new_udp_socket_for_remote(addr) { + return &UdpConn{ + sock: sock + read_timeout: net.udp_default_read_timeout + write_timeout: net.udp_default_write_timeout + } + } + } + + return none +} + +// pub fn dial_udp(laddr string, raddr string) ?&UdpConn { +// local := resolve_addr(laddr, .inet, .udp) ? + +// sbase := new_udp_socket() ? + +// sock := UdpSocket{ +// handle: sbase.handle +// l: local +// r: resolve_wrapper(raddr) +// } +// } + +pub fn (mut c UdpConn) write_ptr(b &byte, len int) ?int { + remote := c.sock.remote() or { return err_no_udp_remote } + return c.write_to_ptr(remote, b, len) +} + +pub fn (mut c UdpConn) write(buf []byte) ?int { + return c.write_ptr(buf.data, buf.len) +} + +pub fn (mut c UdpConn) write_string(s string) ?int { + return c.write_ptr(s.str, s.len) +} + +pub fn (mut c UdpConn) write_to_ptr(addr Addr, b &byte, len int) ?int { + res := C.sendto(c.sock.handle, b, len, 0, voidptr(&addr), addr.len()) + if res >= 0 { + return res + } + code := error_code() + if code == int(error_ewouldblock) { + c.wait_for_write() ? + socket_error(C.sendto(c.sock.handle, b, len, 0, voidptr(&addr), addr.len())) ? + } else { + wrap_error(code) ? + } + return none +} + +// write_to blocks and writes the buf to the remote addr specified +pub fn (mut c UdpConn) write_to(addr Addr, buf []byte) ?int { + return c.write_to_ptr(addr, buf.data, buf.len) +} + +// write_to_string blocks and writes the buf to the remote addr specified +pub fn (mut c UdpConn) write_to_string(addr Addr, s string) ?int { + return c.write_to_ptr(addr, s.str, s.len) +} + +// read reads from the socket into buf up to buf.len returning the number of bytes read +pub fn (mut c UdpConn) read(mut buf []byte) ?(int, Addr) { + mut addr := Addr{ + addr: AddrData{ + Ip6: Ip6{} + } + } + len := sizeof(Addr) + mut res := wrap_read_result(C.recvfrom(c.sock.handle, voidptr(buf.data), buf.len, + 0, voidptr(&addr), &len)) ? + if res > 0 { + return res, addr + } + code := error_code() + if code == int(error_ewouldblock) { + c.wait_for_read() ? + // same setup as in tcp + res = wrap_read_result(C.recvfrom(c.sock.handle, voidptr(buf.data), buf.len, 0, + voidptr(&addr), &len)) ? + res2 := socket_error(res) ? + return res2, addr + } else { + wrap_error(code) ? + } + return none +} + +pub fn (c &UdpConn) read_deadline() ?time.Time { + if c.read_deadline.unix == 0 { + return c.read_deadline + } + return none +} + +pub fn (mut c UdpConn) set_read_deadline(deadline time.Time) { + c.read_deadline = deadline +} + +pub fn (c &UdpConn) write_deadline() ?time.Time { + if c.write_deadline.unix == 0 { + return c.write_deadline + } + return none +} + +pub fn (mut c UdpConn) set_write_deadline(deadline time.Time) { + c.write_deadline = deadline +} + +pub fn (c &UdpConn) read_timeout() time.Duration { + return c.read_timeout +} + +pub fn (mut c UdpConn) set_read_timeout(t time.Duration) { + c.read_timeout = t +} + +pub fn (c &UdpConn) write_timeout() time.Duration { + return c.write_timeout +} + +pub fn (mut c UdpConn) set_write_timeout(t time.Duration) { + c.write_timeout = t +} + +[inline] +pub fn (mut c UdpConn) wait_for_read() ? { + return wait_for_read(c.sock.handle, c.read_deadline, c.read_timeout) +} + +[inline] +pub fn (mut c UdpConn) wait_for_write() ? { + return wait_for_write(c.sock.handle, c.write_deadline, c.write_timeout) +} + +pub fn (c &UdpConn) str() string { + // TODO + return 'UdpConn' +} + +pub fn (mut c UdpConn) close() ? { + return c.sock.close() +} + +pub fn listen_udp(laddr string) ?&UdpConn { + addrs := resolve_addrs_fuzzy(laddr, .udp) ? + // TODO(emily): + // here we are binding to the first address + // and that is probably not ideal + addr := addrs[0] + return &UdpConn{ + sock: new_udp_socket(addr) ? + read_timeout: net.udp_default_read_timeout + write_timeout: net.udp_default_write_timeout + } +} + +fn new_udp_socket(local_addr Addr) ?&UdpSocket { + family := local_addr.family() + + sockfd := socket_error(C.socket(family, SocketType.udp, 0)) ? + mut s := &UdpSocket{ + handle: sockfd + l: local_addr + r: Addr{ + addr: AddrData{ + Ip6: Ip6{} + } + } + } + + s.set_option_bool(.reuse_addr, true) ? + + if family == .ip6 { + s.set_dualstack(true) ? + } + + // NOTE: refer to comments in tcp.v + $if windows { + t := u32(1) // true + socket_error(C.ioctlsocket(sockfd, fionbio, &t)) ? + } $else { + socket_error(C.fcntl(sockfd, C.F_SETFD, C.O_NONBLOCK)) ? + } + + // cast to the correct type + socket_error(C.bind(s.handle, voidptr(&local_addr), local_addr.len())) ? + return s +} + +fn new_udp_socket_for_remote(raddr Addr) ?&UdpSocket { + // Invent a sutible local address for this remote addr + // Appease compiler + mut addr := Addr{ + addr: AddrData{ + Ip6: Ip6{} + } + } + match raddr.family() { + .ip, .ip6 { + // Use ip6 dualstack + addr = new_ip6(0, addr_ip6_any) + } + .unix { + addr = temp_unix() ? + } + else { + panic('Invalid family') + } + } + mut sock := new_udp_socket(addr) ? + sock.has_r = true + sock.r = raddr + + return sock +} + +pub fn (mut s UdpSocket) set_option_bool(opt SocketOption, value bool) ? { + // TODO reenable when this `in` operation works again + // if opt !in opts_can_set { + // return err_option_not_settable + // } + // if opt !in opts_bool { + // return err_option_wrong_type + // } + x := int(value) + socket_error(C.setsockopt(s.handle, C.SOL_SOCKET, int(opt), &x, sizeof(int))) ? +} + +pub fn (mut s UdpSocket) set_dualstack(on bool) ? { + x := int(!on) + socket_error(C.setsockopt(s.handle, C.IPPROTO_IPV6, int(SocketOption.ipv6_only), &x, + sizeof(int))) ? +} + +fn (mut s UdpSocket) close() ? { + return shutdown(s.handle) +} + +fn (mut s UdpSocket) @select(test Select, timeout time.Duration) ?bool { + return @select(s.handle, test, timeout) +} + +fn (s &UdpSocket) remote() ?Addr { + if s.has_r { + return s.r + } + return none +} diff --git a/v_windows/v/old/vlib/net/udp_test.v b/v_windows/v/old/vlib/net/udp_test.v new file mode 100644 index 0000000..83675a2 --- /dev/null +++ b/v_windows/v/old/vlib/net/udp_test.v @@ -0,0 +1,67 @@ +import net + +fn echo_server(mut c net.UdpConn) { + for { + mut buf := []byte{len: 100, init: 0} + read, addr := c.read(mut buf) or { continue } + + println('Server got addr $addr') + + c.write_to(addr, buf[..read]) or { + println('Server: connection dropped') + return + } + } +} + +const ( + local_addr = ':40003' + remote_addr = 'localhost:40003' +) + +fn echo() ? { + mut c := net.dial_udp(remote_addr) ? + defer { + c.close() or {} + } + data := 'Hello from vlib/net!' + + c.write_string(data) ? + + mut buf := []byte{len: 100, init: 0} + read, addr := c.read(mut buf) ? + + assert read == data.len + println('Got address $addr') + // Can't test this here because loopback addresses + // are mapped to other addresses + // assert addr.str() == '127.0.0.1:30001' + + for i := 0; i < read; i++ { + assert buf[i] == data[i] + } + + println('Got "$buf.bytestr()"') + + c.close() ? +} + +fn test_udp() { + mut l := net.listen_udp(local_addr) or { + println(err) + assert false + panic('') + } + + go echo_server(mut l) + echo() or { + println(err) + assert false + } + + l.close() or {} +} + +fn main() { + test_udp() +} diff --git a/v_windows/v/old/vlib/net/unix/aasocket.c.v b/v_windows/v/old/vlib/net/unix/aasocket.c.v new file mode 100644 index 0000000..7f762a5 --- /dev/null +++ b/v_windows/v/old/vlib/net/unix/aasocket.c.v @@ -0,0 +1,104 @@ +module unix + +#include + +// Select represents a select operation +enum Select { + read + write + except +} + +// SocketType are the available sockets +// enum SocketType { +// dgram = C.SOCK_DGRAM +// stream = C.SOCK_STREAM +// seqpacket = C.SOCK_SEQPACKET +// } + +struct C.sockaddr { + sa_family u16 +} + +const max_sun_path = 104 + +// 104 for macos, 108 for linux => use the minimum + +struct C.sockaddr_un { +mut: + // sun_len byte // only on macos + sun_family int + sun_path [104]char // on linux that is 108 +} + +struct C.addrinfo { +mut: + ai_family int + ai_socktype int + ai_flags int + ai_protocol int + ai_addrlen int + ai_addr voidptr + ai_canonname voidptr + ai_next voidptr +} + +struct C.sockaddr_storage { +} + +// fn C.socket() int + +// fn C.setsockopt() int + +// fn C.htonl() int + +// fn C.htons() int + +// fn C.bind() int + +// fn C.listen() int + +// fn C.accept() int + +// fn C.getaddrinfo() int + +// fn C.connect() int + +// fn C.send() int + +// fn C.sendto() int + +// fn C.recv() int + +// fn C.recvfrom() int + +// fn C.shutdown() int + +// fn C.ntohs() int + +// fn C.getpeername() int + +// fn C.inet_ntop(af int, src voidptr, dst charptr, dst_size int) charptr + +fn C.WSAAddressToStringA() int + +// fn C.getsockname() int + +// defined in builtin +// fn C.read() int +// fn C.close() int + +fn C.ioctlsocket() int + +// fn C.fcntl() int + +// fn C.@select() int + +// fn C.FD_ZERO() + +// fn C.FD_SET() + +// fn C.FD_ISSET() bool + +[typedef] +struct C.fd_set {} diff --git a/v_windows/v/old/vlib/net/unix/common.v b/v_windows/v/old/vlib/net/unix/common.v new file mode 100644 index 0000000..75e591f --- /dev/null +++ b/v_windows/v/old/vlib/net/unix/common.v @@ -0,0 +1,128 @@ +module unix + +import time +import net + +const ( + error_ewouldblock = C.EWOULDBLOCK +) + +fn C.SUN_LEN(ptr &C.sockaddr_un) int + +fn C.strncpy(&char, &char, int) + +// Shutdown shutsdown a socket and closes it +fn shutdown(handle int) ? { + $if windows { + C.shutdown(handle, C.SD_BOTH) + net.socket_error(C.closesocket(handle)) ? + } $else { + C.shutdown(handle, C.SHUT_RDWR) + net.socket_error(C.close(handle)) ? + } +} + +// Select waits for an io operation (specified by parameter `test`) to be available +fn @select(handle int, test Select, timeout time.Duration) ?bool { + set := C.fd_set{} + + C.FD_ZERO(&set) + C.FD_SET(handle, &set) + + seconds := timeout / time.second + microseconds := time.Duration(timeout - (seconds * time.second)).microseconds() + + mut tt := C.timeval{ + tv_sec: u64(seconds) + tv_usec: u64(microseconds) + } + + mut timeval_timeout := &tt + + // infinite timeout is signaled by passing null as the timeout to + // select + if timeout == unix.infinite_timeout { + timeval_timeout = &C.timeval(0) + } + + match test { + .read { + net.socket_error(C.@select(handle + 1, &set, C.NULL, C.NULL, timeval_timeout)) ? + } + .write { + net.socket_error(C.@select(handle + 1, C.NULL, &set, C.NULL, timeval_timeout)) ? + } + .except { + net.socket_error(C.@select(handle + 1, C.NULL, C.NULL, &set, timeval_timeout)) ? + } + } + + return C.FD_ISSET(handle, &set) +} + +// wait_for_common wraps the common wait code +fn wait_for_common(handle int, deadline time.Time, timeout time.Duration, test Select) ? { + if deadline.unix == 0 { + // do not accept negative timeout + if timeout < 0 { + return net.err_timed_out + } + ready := @select(handle, test, timeout) ? + if ready { + return + } + return net.err_timed_out + } + // Convert the deadline into a timeout + // and use that + d_timeout := deadline.unix - time.now().unix + if d_timeout < 0 { + // deadline is in the past so this has already + // timed out + return net.err_timed_out + } + + ready := @select(handle, test, d_timeout) ? + if ready { + return + } + return net.err_timed_out +} + +// wait_for_write waits for a write io operation to be available +fn wait_for_write(handle int, deadline time.Time, timeout time.Duration) ? { + return wait_for_common(handle, deadline, timeout, .write) +} + +// wait_for_read waits for a read io operation to be available +fn wait_for_read(handle int, deadline time.Time, timeout time.Duration) ? { + return wait_for_common(handle, deadline, timeout, .read) +} + +// no_deadline should be given to functions when no deadline is wanted (i.e. all functions +// return instantly) +const ( + no_deadline = time.Time{ + unix: 0 + } +) + +// no_timeout should be given to functions when no timeout is wanted (i.e. all functions +// return instantly) +const ( + no_timeout = time.Duration(0) +) + +// infinite_timeout should be given to functions when an infinite_timeout is wanted (i.e. functions +// only ever return with data) +const ( + infinite_timeout = time.infinite +) + +[inline] +fn wrap_read_result(result int) ?int { + if result != 0 { + return result + } + return none +} diff --git a/v_windows/v/old/vlib/net/unix/stream_nix.v b/v_windows/v/old/vlib/net/unix/stream_nix.v new file mode 100644 index 0000000..d13c811 --- /dev/null +++ b/v_windows/v/old/vlib/net/unix/stream_nix.v @@ -0,0 +1,284 @@ +module unix + +import time +import os +import net + +const ( + unix_default_read_timeout = 30 * time.second + unix_default_write_timeout = 30 * time.second + connect_timeout = 5 * time.second + msg_nosignal = 0x4000 +) + +struct StreamSocket { +pub: + handle int +mut: + path string +} + +struct StreamConn { +pub mut: + sock StreamSocket +mut: + write_deadline time.Time + read_deadline time.Time + read_timeout time.Duration + write_timeout time.Duration +} + +struct StreamListener { +pub mut: + sock StreamSocket +mut: + accept_timeout time.Duration + accept_deadline time.Time +} + +fn error_code() int { + return C.errno +} + +fn new_stream_socket() ?StreamSocket { + sockfd := net.socket_error(C.socket(net.AddrFamily.unix, net.SocketType.tcp, 0)) ? + mut s := StreamSocket{ + handle: sockfd + } + return s +} + +fn (mut s StreamSocket) close() ? { + os.rm(s.path) ? + return shutdown(s.handle) +} + +fn (mut s StreamSocket) @select(test Select, timeout time.Duration) ?bool { + return @select(s.handle, test, timeout) +} + +fn (mut s StreamSocket) connect(a string) ? { + if a.len >= max_sun_path { + return error('Socket path too long! Max length: ${max_sun_path - 1} chars.') + } + mut addr := C.sockaddr_un{} + unsafe { C.memset(&addr, 0, sizeof(C.sockaddr_un)) } + addr.sun_family = C.AF_UNIX + unsafe { C.strncpy(&addr.sun_path[0], &char(a.str), max_sun_path) } + size := C.SUN_LEN(&addr) + res := C.connect(s.handle, voidptr(&addr), size) + // if res != 1 { + // return none + //} + if res == 0 { + return + } + _ := error_code() + write_result := s.@select(.write, unix.connect_timeout) ? + if write_result { + // succeeded + return + } + except_result := s.@select(.except, unix.connect_timeout) ? + if except_result { + return net.err_connect_failed + } + // otherwise we timed out + return net.err_connect_timed_out +} + +pub fn listen_stream(sock string) ?&StreamListener { + if sock.len >= max_sun_path { + return error('Socket path too long! Max length: ${max_sun_path - 1} chars.') + } + mut s := new_stream_socket() ? + s.path = sock + mut addr := C.sockaddr_un{} + unsafe { C.memset(&addr, 0, sizeof(C.sockaddr_un)) } + addr.sun_family = C.AF_UNIX + unsafe { C.strncpy(&addr.sun_path[0], &char(sock.str), max_sun_path) } + size := C.SUN_LEN(&addr) + net.socket_error(C.bind(s.handle, voidptr(&addr), size)) ? + net.socket_error(C.listen(s.handle, 128)) ? + return &StreamListener{ + sock: s + } +} + +pub fn connect_stream(path string) ?&StreamConn { + mut s := new_stream_socket() ? + s.connect(path) ? + return &StreamConn{ + sock: s + read_timeout: unix.unix_default_read_timeout + write_timeout: unix.unix_default_write_timeout + } +} + +pub fn (mut l StreamListener) accept() ?&StreamConn { + mut new_handle := C.accept(l.sock.handle, 0, 0) + if new_handle <= 0 { + l.wait_for_accept() ? + new_handle = C.accept(l.sock.handle, 0, 0) + if new_handle == -1 || new_handle == 0 { + return error('accept failed') + } + } + new_sock := StreamSocket{ + handle: new_handle + } + return &StreamConn{ + sock: new_sock + read_timeout: unix.unix_default_read_timeout + write_timeout: unix.unix_default_write_timeout + } +} + +pub fn (c &StreamListener) accept_deadline() ?time.Time { + if c.accept_deadline.unix != 0 { + return c.accept_deadline + } + return error('no deadline') +} + +pub fn (mut c StreamListener) set_accept_deadline(deadline time.Time) { + c.accept_deadline = deadline +} + +pub fn (c &StreamListener) accept_timeout() time.Duration { + return c.accept_timeout +} + +pub fn (mut c StreamListener) set_accept_timeout(t time.Duration) { + c.accept_timeout = t +} + +pub fn (mut c StreamListener) wait_for_accept() ? { + return wait_for_read(c.sock.handle, c.accept_deadline, c.accept_timeout) +} + +pub fn (mut c StreamListener) close() ? { + c.sock.close() ? +} + +pub fn (mut c StreamConn) close() ? { + c.sock.close() ? +} + +// write_ptr blocks and attempts to write all data +pub fn (mut c StreamConn) write_ptr(b &byte, len int) ?int { + $if trace_unix ? { + eprintln( + '>>> StreamConn.write_ptr | c.sock.handle: $c.sock.handle | b: ${ptr_str(b)} len: $len |\n' + + unsafe { b.vstring_with_len(len) }) + } + unsafe { + mut ptr_base := &byte(b) + mut total_sent := 0 + for total_sent < len { + ptr := ptr_base + total_sent + remaining := len - total_sent + mut sent := C.send(c.sock.handle, ptr, remaining, unix.msg_nosignal) + if sent < 0 { + code := error_code() + if code == int(error_ewouldblock) { + c.wait_for_write() ? + continue + } else { + net.wrap_error(code) ? + } + } + total_sent += sent + } + return total_sent + } +} + +// write blocks and attempts to write all data +pub fn (mut c StreamConn) write(bytes []byte) ?int { + return c.write_ptr(bytes.data, bytes.len) +} + +// write_string blocks and attempts to write all data +pub fn (mut c StreamConn) write_string(s string) ?int { + return c.write_ptr(s.str, s.len) +} + +pub fn (mut c StreamConn) read_ptr(buf_ptr &byte, len int) ?int { + mut res := wrap_read_result(C.recv(c.sock.handle, voidptr(buf_ptr), len, 0)) ? + $if trace_unix ? { + eprintln('<<< StreamConn.read_ptr | c.sock.handle: $c.sock.handle | buf_ptr: ${ptr_str(buf_ptr)} len: $len | res: $res') + } + if res > 0 { + return res + } + code := error_code() + if code == int(error_ewouldblock) { + c.wait_for_read() ? + res = wrap_read_result(C.recv(c.sock.handle, voidptr(buf_ptr), len, 0)) ? + $if trace_unix ? { + eprintln('<<< StreamConn.read_ptr | c.sock.handle: $c.sock.handle | buf_ptr: ${ptr_str(buf_ptr)} len: $len | res: $res') + } + return net.socket_error(res) + } else { + net.wrap_error(code) ? + } + return net.socket_error(code) +} + +pub fn (mut c StreamConn) read(mut buf []byte) ?int { + return c.read_ptr(buf.data, buf.len) +} + +pub fn (mut c StreamConn) read_deadline() ?time.Time { + if c.read_deadline.unix == 0 { + return c.read_deadline + } + return none +} + +pub fn (mut c StreamConn) set_read_deadline(deadline time.Time) { + c.read_deadline = deadline +} + +pub fn (mut c StreamConn) write_deadline() ?time.Time { + if c.write_deadline.unix == 0 { + return c.write_deadline + } + return none +} + +pub fn (mut c StreamConn) set_write_deadline(deadline time.Time) { + c.write_deadline = deadline +} + +pub fn (c &StreamConn) read_timeout() time.Duration { + return c.read_timeout +} + +pub fn (mut c StreamConn) set_read_timeout(t time.Duration) { + c.read_timeout = t +} + +pub fn (c &StreamConn) write_timeout() time.Duration { + return c.write_timeout +} + +pub fn (mut c StreamConn) set_write_timeout(t time.Duration) { + c.write_timeout = t +} + +[inline] +pub fn (mut c StreamConn) wait_for_read() ? { + return wait_for_read(c.sock.handle, c.read_deadline, c.read_timeout) +} + +[inline] +pub fn (mut c StreamConn) wait_for_write() ? { + return wait_for_write(c.sock.handle, c.write_deadline, c.write_timeout) +} + +pub fn (c StreamConn) str() string { + s := c.sock.str().replace('\n', ' ').replace(' ', ' ') + return 'StreamConn{ write_deadline: $c.write_deadline, read_deadline: $c.read_deadline, read_timeout: $c.read_timeout, write_timeout: $c.write_timeout, sock: $s }' +} diff --git a/v_windows/v/old/vlib/net/unix/unix_test.v b/v_windows/v/old/vlib/net/unix/unix_test.v new file mode 100644 index 0000000..007d62d --- /dev/null +++ b/v_windows/v/old/vlib/net/unix/unix_test.v @@ -0,0 +1,57 @@ +import os +import net.unix + +const test_port = os.join_path(os.temp_dir(), 'unix_domain_socket') + +fn testsuite_begin() { + os.rm(test_port) or {} +} + +fn testsuite_end() { + os.rm(test_port) or {} +} + +fn handle_conn(mut c unix.StreamConn) { + for { + mut buf := []byte{len: 100, init: 0} + read := c.read(mut buf) or { + println('Server: connection dropped') + return + } + c.write(buf[..read]) or { + println('Server: connection dropped') + return + } + } +} + +fn echo_server(mut l unix.StreamListener) ? { + for { + mut new_conn := l.accept() or { continue } + go handle_conn(mut new_conn) + } +} + +fn echo() ? { + mut c := unix.connect_stream(test_port) ? + defer { + c.close() or {} + } + data := 'Hello from vlib/net!' + c.write_string(data) ? + mut buf := []byte{len: 4096} + read := c.read(mut buf) ? + assert read == data.len + for i := 0; i < read; i++ { + assert buf[i] == data[i] + } + println('Got "$buf.bytestr()"') + return +} + +fn test_tcp() { + mut l := unix.listen_stream(test_port) or { panic(err) } + go echo_server(mut l) + echo() or { panic(err) } + l.close() or {} +} diff --git a/v_windows/v/old/vlib/net/urllib/urllib.v b/v_windows/v/old/vlib/net/urllib/urllib.v new file mode 100644 index 0000000..3b02ef6 --- /dev/null +++ b/v_windows/v/old/vlib/net/urllib/urllib.v @@ -0,0 +1,1095 @@ +// urllib parses URLs and implements query escaping. +// See RFC 3986. This module generally follows RFC 3986, except where +// it deviates for compatibility reasons. +// Based off: https://github.com/golang/go/blob/master/src/net/url/url.go +// Last commit: https://github.com/golang/go/commit/fe2ed5054176935d4adcf13e891715ccf2ee3cce +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. +module urllib + +import strings + +enum EncodingMode { + encode_path + encode_path_segment + encode_host + encode_zone + encode_user_password + encode_query_component + encode_fragment +} + +const ( + err_msg_escape = 'unescape: invalid URL escape' + err_msg_parse = 'parse: failed parsing url' +) + +fn error_msg(message string, val string) string { + mut msg := 'net.urllib.$message' + if val != '' { + msg = '$msg ($val)' + } + return msg +} + +// Return true if the specified character should be escaped when +// appearing in a URL string, according to RFC 3986. +// +// Please be informed that for now should_escape does not check all +// reserved characters correctly. See golang.org/issue/5684. +fn should_escape(c byte, mode EncodingMode) bool { + // §2.3 Unreserved characters (alphanum) + if (`a` <= c && c <= `z`) || (`A` <= c && c <= `Z`) || (`0` <= c && c <= `9`) { + return false + } + if mode == .encode_host || mode == .encode_zone { + // §3.2.2 host allows + // sub-delims = `!` / `$` / `&` / ``` / `(` / `)` / `*` / `+` / `,` / `;` / `=` + // as part of reg-name. + // We add : because we include :port as part of host. + // We add [ ] because we include [ipv6]:port as part of host. + // We add < > because they`re the only characters left that + // we could possibly allow, and parse will reject them if we + // escape them (because hosts can`t use %-encoding for + // ASCII bytes). + if c in [`!`, `$`, `&`, `\\`, `(`, `)`, `*`, `+`, `,`, `;`, `=`, `:`, `[`, `]`, `<`, `>`, + `"`, + ] { + return false + } + } + match c { + `-`, `_`, `.`, `~` { + // §2.3 Unreserved characters (mark) + return false + } + `$`, `&`, `+`, `,`, `/`, `:`, `;`, `=`, `?`, `@` { + // §2.2 Reserved characters (reserved) + // Different sections of the URL allow a few of + // the reserved characters to appear unescaped. + match mode { + .encode_path { + // §3.3 + // The RFC allows : @ & = + $ but saves / ; , for assigning + // meaning to individual path segments. This package + // only manipulates the path as a whole, so we allow those + // last three as well. That leaves only ? to escape. + return c == `?` + } + .encode_path_segment { + // §3.3 + // The RFC allows : @ & = + $ but saves / ; , for assigning + // meaning to individual path segments. + return c == `/` || c == `;` || c == `,` || c == `?` + } + .encode_user_password { + // §3.2.1 + // The RFC allows `;`, `:`, `&`, `=`, `+`, `$`, and `,` in + // userinfo, so we must escape only `@`, `/`, and `?`. + // The parsing of userinfo treats `:` as special so we must escape + // that too. + return c == `@` || c == `/` || c == `?` || c == `:` + } + .encode_query_component { + // §3.4 + // The RFC reserves (so we must escape) everything. + return true + } + .encode_fragment { + // §4.1 + // The RFC text is silent but the grammar allows + // everything, so escape nothing. + return false + } + else {} + } + } + else {} + } + if mode == .encode_fragment { + // RFC 3986 §2.2 allows not escaping sub-delims. A subset of sub-delims are + // included in reserved from RFC 2396 §2.2. The remaining sub-delims do not + // need to be escaped. To minimize potential breakage, we apply two restrictions: + // (1) we always escape sub-delims outside of the fragment, and (2) we always + // escape single quote to avoid breaking callers that had previously assumed that + // single quotes would be escaped. See issue #19917. + match c { + `!`, `(`, `)`, `*` { return false } + else {} + } + } + // Everything else must be escaped. + return true +} + +// query_unescape does the inverse transformation of query_escape, +// converting each 3-byte encoded substring of the form '%AB' into the +// hex-decoded byte 0xAB. +// It returns an error if any % is not followed by two hexadecimal +// digits. +pub fn query_unescape(s string) ?string { + return unescape(s, .encode_query_component) +} + +// path_unescape does the inverse transformation of path_escape, +// converting each 3-byte encoded substring of the form '%AB' into the +// hex-decoded byte 0xAB. It returns an error if any % is not followed +// by two hexadecimal digits. +// +// path_unescape is identical to query_unescape except that it does not +// unescape '+' to ' ' (space). +pub fn path_unescape(s string) ?string { + return unescape(s, .encode_path_segment) +} + +// unescape unescapes a string; the mode specifies +// which section of the URL string is being unescaped. +fn unescape(s_ string, mode EncodingMode) ?string { + mut s := s_ + // Count %, check that they're well-formed. + mut n := 0 + mut has_plus := false + for i := 0; i < s.len; { + x := s[i] + match x { + `%` { + if s == '' { + break + } + n++ + if i + 2 >= s.len || !ishex(s[i + 1]) || !ishex(s[i + 2]) { + if mode == .encode_query_component && i + 1 < s.len { + s = s[..i] + '%25' + s[(i + 1)..] + i += 4 // skip the %25 and the next character + continue + } + s = s[i..] + if s.len > 3 { + s = s[..3] + } + return error(error_msg(urllib.err_msg_escape, s)) + } + // Per https://tools.ietf.org/html/rfc3986#page-21 + // in the host component %-encoding can only be used + // for non-ASCII bytes. + // But https://tools.ietf.org/html/rfc6874#section-2 + // introduces %25 being allowed to escape a percent sign + // in IPv6 scoped-address literals. Yay. + if i + 3 >= s.len && mode == .encode_host && unhex(s[i + 1]) < 8 + && s[i..i + 3] != '%25' { + return error(error_msg(urllib.err_msg_escape, s[i..i + 3])) + } + if mode == .encode_zone { + // RFC 6874 says basically 'anything goes' for zone identifiers + // and that even non-ASCII can be redundantly escaped, + // but it seems prudent to restrict %-escaped bytes here to those + // that are valid host name bytes in their unescaped form. + // That is, you can use escaping in the zone identifier but not + // to introduce bytes you couldn't just write directly. + // But Windows puts spaces here! Yay. + if i + 3 >= s.len { + return error(error_msg('unescape: invalid escape sequence', '')) + } + v := ((unhex(s[i + 1]) << byte(4)) | unhex(s[i + 2])) + if s[i..i + 3] != '%25' && v != ` ` && should_escape(v, .encode_host) { + error(error_msg(urllib.err_msg_escape, s[i..i + 3])) + } + } + i += 3 + } + `+` { + has_plus = mode == .encode_query_component + i++ + } + else { + if (mode == .encode_host || mode == .encode_zone) && s[i] < 0x80 + && should_escape(s[i], mode) { + error(error_msg('unescape: invalid character in host name', s[i..i + 1])) + } + i++ + } + } + } + if n == 0 && !has_plus { + return s + } + if s.len < 2 * n { + return error(error_msg('unescape: invalid escape sequence', '')) + } + mut t := strings.new_builder(s.len - 2 * n) + for i := 0; i < s.len; i++ { + x := s[i] + match x { + `%` { + if i + 2 >= s.len { + return error(error_msg('unescape: invalid escape sequence', '')) + } + t.write_string(((unhex(s[i + 1]) << byte(4)) | unhex(s[i + 2])).ascii_str()) + i += 2 + } + `+` { + if mode == .encode_query_component { + t.write_string(' ') + } else { + t.write_string('+') + } + } + else { + t.write_string(s[i].ascii_str()) + } + } + } + return t.str() +} + +// query_escape escapes the string so it can be safely placed +// inside a URL query. +pub fn query_escape(s string) string { + return escape(s, .encode_query_component) +} + +// path_escape escapes the string so it can be safely placed inside a URL path segment, +// replacing special characters (including /) with %XX sequences as needed. +pub fn path_escape(s string) string { + return escape(s, .encode_path_segment) +} + +fn escape(s string, mode EncodingMode) string { + mut space_count := 0 + mut hex_count := 0 + mut c := byte(0) + for i in 0 .. s.len { + c = s[i] + if should_escape(c, mode) { + if c == ` ` && mode == .encode_query_component { + space_count++ + } else { + hex_count++ + } + } + } + if space_count == 0 && hex_count == 0 { + return s + } + buf := []byte{len: (64)} + mut t := []byte{} + required := s.len + 2 * hex_count + if required <= buf.len { + t = buf[..required] + } else { + t = []byte{len: required} + } + if hex_count == 0 { + copy(t, s.bytes()) + for i in 0 .. s.len { + if s[i] == ` ` { + t[i] = `+` + } + } + return t.bytestr() + } + upperhex := '0123456789ABCDEF' + mut j := 0 + for i in 0 .. s.len { + c1 := s[i] + if c1 == ` ` && mode == .encode_query_component { + t[j] = `+` + j++ + } else if should_escape(c1, mode) { + t[j] = `%` + t[j + 1] = upperhex[c1 >> 4] + t[j + 2] = upperhex[c1 & 15] + j += 3 + } else { + t[j] = s[i] + j++ + } + } + return t.bytestr() +} + +// A URL represents a parsed URL (technically, a URI reference). +// +// The general form represented is: +// +// [scheme:][//[userinfo@]host][/]path[?query][#fragment] +// +// URLs that do not start with a slash after the scheme are interpreted as: +// +// scheme:opaque[?query][#fragment] +// +// Note that the path field is stored in decoded form: /%47%6f%2f becomes /Go/. +// A consequence is that it is impossible to tell which slashes in the path were +// slashes in the raw URL and which were %2f. This distinction is rarely important, +// but when it is, the code should use raw_path, an optional field which only gets +// set if the default encoding is different from path. +// +// URL's String method uses the escaped_path method to obtain the path. See the +// escaped_path method for more details. +pub struct URL { +pub mut: + scheme string + opaque string // encoded opaque data + user &Userinfo // username and password information + host string // host or host:port + path string // path (relative paths may omit leading slash) + raw_path string // encoded path hint (see escaped_path method) + force_query bool // append a query ('?') even if raw_query is empty + raw_query string // encoded query values, without '?' + fragment string // fragment for references, without '#' +} + +// user returns a Userinfo containing the provided username +// and no password set. +pub fn user(username string) &Userinfo { + return &Userinfo{ + username: username + password: '' + password_set: false + } +} + +// user_password returns a Userinfo containing the provided username +// and password. +// +// This functionality should only be used with legacy web sites. +// RFC 2396 warns that interpreting Userinfo this way +// ``is NOT RECOMMENDED, because the passing of authentication +// information in clear text (such as URI) has proven to be a +// security risk in almost every case where it has been used.'' +fn user_password(username string, password string) &Userinfo { + return &Userinfo{username, password, true} +} + +// The Userinfo type is an immutable encapsulation of username and +// password details for a URL. An existing Userinfo value is guaranteed +// to have a username set (potentially empty, as allowed by RFC 2396), +// and optionally a password. +struct Userinfo { +pub: + username string + password string + password_set bool +} + +fn (u &Userinfo) empty() bool { + return isnil(u) || (u.username == '' && u.password == '') +} + +// string returns the encoded userinfo information in the standard form +// of 'username[:password]'. +fn (u &Userinfo) str() string { + if u.empty() { + return '' + } + mut s := escape(u.username, .encode_user_password) + if u.password_set { + s += ':' + escape(u.password, .encode_user_password) + } + return s +} + +// Maybe rawurl is of the form scheme:path. +// (scheme must be [a-zA-Z][a-zA-Z0-9+-.]*) +// If so, return [scheme, path]; else return ['', rawurl] +fn split_by_scheme(rawurl string) ?[]string { + for i in 0 .. rawurl.len { + c := rawurl[i] + if (`a` <= c && c <= `z`) || (`A` <= c && c <= `Z`) { + // do nothing + } else if (`0` <= c && c <= `9`) || (c == `+` || c == `-` || c == `.`) { + if i == 0 { + return ['', rawurl] + } + } else if c == `:` { + if i == 0 { + return error(error_msg('split_by_scheme: missing protocol scheme', '')) + } + return [rawurl[..i], rawurl[i + 1..]] + } else { + // we have encountered an invalid character, + // so there is no valid scheme + return ['', rawurl] + } + } + return ['', rawurl] +} + +fn get_scheme(rawurl string) ?string { + split := split_by_scheme(rawurl) or { return err.msg } + return split[0] +} + +// split slices s into two substrings separated by the first occurence of +// sep. If cutc is true then sep is included with the second substring. +// If sep does not occur in s then s and the empty string is returned. +fn split(s string, sep byte, cutc bool) (string, string) { + i := s.index_byte(sep) + if i < 0 { + return s, '' + } + if cutc { + return s[..i], s[i + 1..] + } + return s[..i], s[i..] +} + +// parse parses rawurl into a URL structure. +// +// The rawurl may be relative (a path, without a host) or absolute +// (starting with a scheme). Trying to parse a hostname and path +// without a scheme is invalid but may not necessarily return an +// error, due to parsing ambiguities. +pub fn parse(rawurl string) ?URL { + // Cut off #frag + u, frag := split(rawurl, `#`, true) + mut url := parse_url(u, false) or { return error(error_msg(urllib.err_msg_parse, u)) } + if frag == '' { + return url + } + f := unescape(frag, .encode_fragment) or { return error(error_msg(urllib.err_msg_parse, + u)) } + url.fragment = f + return url +} + +// parse_request_uri parses rawurl into a URL structure. It assumes that +// rawurl was received in an HTTP request, so the rawurl is interpreted +// only as an absolute URI or an absolute path. +// The string rawurl is assumed not to have a #fragment suffix. +// (Web browsers strip #fragment before sending the URL to a web server.) +fn parse_request_uri(rawurl string) ?URL { + return parse_url(rawurl, true) +} + +// parse_url parses a URL from a string in one of two contexts. If +// via_request is true, the URL is assumed to have arrived via an HTTP request, +// in which case only absolute URLs or path-absolute relative URLs are allowed. +// If via_request is false, all forms of relative URLs are allowed. +[manualfree] +fn parse_url(rawurl string, via_request bool) ?URL { + if string_contains_ctl_byte(rawurl) { + return error(error_msg('parse_url: invalid control character in URL', rawurl)) + } + if rawurl == '' && via_request { + return error(error_msg('parse_url: empty URL', rawurl)) + } + mut url := URL{ + user: 0 + } + if rawurl == '*' { + url.path = '*' + return url + } + // Split off possible leading 'http:', 'mailto:', etc. + // Cannot contain escaped characters. + p := split_by_scheme(rawurl) ? + url.scheme = p[0] + mut rest := p[1] + url.scheme = url.scheme.to_lower() + // if rest.ends_with('?') && strings.count(rest, '?') == 1 { + if rest.ends_with('?') && !rest[..1].contains('?') { + url.force_query = true + rest = rest[..rest.len - 1] + } else { + r, raw_query := split(rest, `?`, true) + rest = r + url.raw_query = raw_query + } + if !rest.starts_with('/') { + if url.scheme != '' { + // We consider rootless paths per RFC 3986 as opaque. + url.opaque = rest + return url + } + if via_request { + return error(error_msg('parse_url: invalid URI for request', '')) + } + // Avoid confusion with malformed schemes, like cache_object:foo/bar. + // See golang.org/issue/16822. + // + // RFC 3986, §3.3: + // In addition, a URI reference (Section 4.1) may be a relative-path reference, + // in which case the first path segment cannot contain a colon (':') character. + colon := rest.index(':') or { return error('there should be a : in the URL') } + slash := rest.index('/') or { return error('there should be a / in the URL') } + if colon >= 0 && (slash < 0 || colon < slash) { + // First path segment has colon. Not allowed in relative URL. + return error(error_msg('parse_url: first path segment in URL cannot contain colon', + '')) + } + } + if ((url.scheme != '' || !via_request) && !rest.starts_with('///')) && rest.starts_with('//') { + authority, r := split(rest[2..], `/`, false) + rest = r + a := parse_authority(authority) ? + url.user = a.user + url.host = a.host + } + // Set path and, optionally, raw_path. + // raw_path is a hint of the encoding of path. We don't want to set it if + // the default escaping of path is equivalent, to help make sure that people + // don't rely on it in general. + url.set_path(rest) ? + return url +} + +struct ParseAuthorityRes { + user &Userinfo + host string +} + +fn parse_authority(authority string) ?ParseAuthorityRes { + i := authority.last_index('@') or { -1 } + mut host := '' + mut zuser := user('') + if i < 0 { + h := parse_host(authority) ? + host = h + } else { + h := parse_host(authority[i + 1..]) ? + host = h + } + if i < 0 { + return ParseAuthorityRes{ + host: host + user: zuser + } + } + mut userinfo := authority[..i] + if !valid_userinfo(userinfo) { + return error(error_msg('parse_authority: invalid userinfo', '')) + } + if !userinfo.contains(':') { + u := unescape(userinfo, .encode_user_password) ? + userinfo = u + zuser = user(userinfo) + } else { + mut username, mut password := split(userinfo, `:`, true) + u := unescape(username, .encode_user_password) ? + username = u + p := unescape(password, .encode_user_password) ? + password = p + zuser = user_password(username, password) + } + return ParseAuthorityRes{ + user: zuser + host: host + } +} + +// parse_host parses host as an authority without user +// information. That is, as host[:port]. +fn parse_host(host string) ?string { + if host.starts_with('[') { + // parse an IP-Literal in RFC 3986 and RFC 6874. + // E.g., '[fe80::1]', '[fe80::1%25en0]', '[fe80::1]:80'. + mut i := host.last_index(']') or { + return error(error_msg("parse_host: missing ']' in host", '')) + } + mut colon_port := host[i + 1..] + if !valid_optional_port(colon_port) { + return error(error_msg('parse_host: invalid port $colon_port after host ', + '')) + } + // RFC 6874 defines that %25 (%-encoded percent) introduces + // the zone identifier, and the zone identifier can use basically + // any %-encoding it likes. That's different from the host, which + // can only %-encode non-ASCII bytes. + // We do impose some restrictions on the zone, to avoid stupidity + // like newlines. + if zone := host[..i].index('%25') { + host1 := unescape(host[..zone], .encode_host) or { return err.msg } + host2 := unescape(host[zone..i], .encode_zone) or { return err.msg } + host3 := unescape(host[i..], .encode_host) or { return err.msg } + return host1 + host2 + host3 + } + if idx := host.last_index(':') { + colon_port = host[idx..] + if !valid_optional_port(colon_port) { + return error(error_msg('parse_host: invalid port $colon_port after host ', + '')) + } + } + } + h := unescape(host, .encode_host) or { return err.msg } + return h + // host = h + // return host +} + +// set_path sets the path and raw_path fields of the URL based on the provided +// escaped path p. It maintains the invariant that raw_path is only specified +// when it differs from the default encoding of the path. +// For example: +// - set_path('/foo/bar') will set path='/foo/bar' and raw_path='' +// - set_path('/foo%2fbar') will set path='/foo/bar' and raw_path='/foo%2fbar' +// set_path will return an error only if the provided path contains an invalid +// escaping. +pub fn (mut u URL) set_path(p string) ?bool { + path := unescape(p, .encode_path) ? + u.path = path + escp := escape(path, .encode_path) + if p == escp { + // Default encoding is fine. + u.raw_path = '' + } else { + u.raw_path = p + } + return true +} + +// escaped_path returns the escaped form of u.path. +// In general there are multiple possible escaped forms of any path. +// escaped_path returns u.raw_path when it is a valid escaping of u.path. +// Otherwise escaped_path ignores u.raw_path and computes an escaped +// form on its own. +// The String and request_uri methods use escaped_path to construct +// their results. +// In general, code should call escaped_path instead of +// reading u.raw_path directly. +pub fn (u &URL) escaped_path() string { + if u.raw_path != '' && valid_encoded_path(u.raw_path) { + unescape(u.raw_path, .encode_path) or { return '' } + return u.raw_path + } + if u.path == '*' { + return '*' // don't escape (Issue 11202) + } + return escape(u.path, .encode_path) +} + +// valid_encoded_path reports whether s is a valid encoded path. +// It must not contain any bytes that require escaping during path encoding. +fn valid_encoded_path(s string) bool { + for i in 0 .. s.len { + // RFC 3986, Appendix A. + // pchar = unreserved / pct-encoded / sub-delims / ':' / '@'. + // should_escape is not quite compliant with the RFC, + // so we check the sub-delims ourselves and let + // should_escape handle the others. + x := s[i] + match x { + `!`, `$`, `&`, `\\`, `(`, `)`, `*`, `+`, `,`, `;`, `=`, `:`, `@` { + // ok + } + `[`, `]` { + // ok - not specified in RFC 3986 but left alone by modern browsers + } + `%` { + // ok - percent encoded, will decode + } + else { + if should_escape(s[i], .encode_path) { + return false + } + } + } + } + return true +} + +// valid_optional_port reports whether port is either an empty string +// or matches /^:\d*$/ +fn valid_optional_port(port string) bool { + if port == '' { + return true + } + if port[0] != `:` { + return false + } + for b in port[1..] { + if b < `0` || b > `9` { + return false + } + } + return true +} + +// str reassembles the URL into a valid URL string. +// The general form of the result is one of: +// +// scheme:opaque?query#fragment +// scheme://userinfo@host/path?query#fragment +// +// If u.opaque is non-empty, String uses the first form; +// otherwise it uses the second form. +// Any non-ASCII characters in host are escaped. +// To obtain the path, String uses u.escaped_path(). +// +// In the second form, the following rules apply: +// - if u.scheme is empty, scheme: is omitted. +// - if u.user is nil, userinfo@ is omitted. +// - if u.host is empty, host/ is omitted. +// - if u.scheme and u.host are empty and u.user is nil, +// the entire scheme://userinfo@host/ is omitted. +// - if u.host is non-empty and u.path begins with a /, +// the form host/path does not add its own /. +// - if u.raw_query is empty, ?query is omitted. +// - if u.fragment is empty, #fragment is omitted. +pub fn (u URL) str() string { + mut buf := strings.new_builder(200) + if u.scheme != '' { + buf.write_string(u.scheme) + buf.write_string(':') + } + if u.opaque != '' { + buf.write_string(u.opaque) + } else { + if u.scheme != '' || u.host != '' || !u.user.empty() { + if u.host != '' || u.path != '' || !u.user.empty() { + buf.write_string('//') + } + if !u.user.empty() { + buf.write_string(u.user.str()) + buf.write_string('@') + } + if u.host != '' { + buf.write_string(escape(u.host, .encode_host)) + } + } + path := u.escaped_path() + if path != '' && path[0] != `/` && u.host != '' { + buf.write_string('/') + } + if buf.len == 0 { + // RFC 3986 §4.2 + // A path segment that contains a colon character (e.g., 'this:that') + // cannot be used as the first segment of a relative-path reference, as + // it would be mistaken for a scheme name. Such a segment must be + // preceded by a dot-segment (e.g., './this:that') to make a relative- + // path reference. + i := path.index_byte(`:`) + if i > -1 { + // TODO remove this when autofree handles tmp + // expressions like this + if i > -1 && path[..i].index_byte(`/`) == -1 { + buf.write_string('./') + } + } + } + buf.write_string(path) + } + if u.force_query || u.raw_query != '' { + buf.write_string('?') + buf.write_string(u.raw_query) + } + if u.fragment != '' { + buf.write_string('#') + buf.write_string(escape(u.fragment, .encode_fragment)) + } + return buf.str() +} + +// Values maps a string key to a list of values. +// It is typically used for query parameters and form values. +// Unlike in the http.Header map, the keys in a Values map +// are case-sensitive. +// parseQuery parses the URL-encoded query string and returns +// a map listing the values specified for each key. +// parseQuery always returns a non-nil map containing all the +// valid query parameters found; err describes the first decoding error +// encountered, if any. +// +// Query is expected to be a list of key=value settings separated by +// ampersands or semicolons. A setting without an equals sign is +// interpreted as a key set to an empty value. +pub fn parse_query(query string) ?Values { + mut m := new_values() + parse_query_values(mut m, query) ? + return m +} + +// parse_query_silent is the same as parse_query +// but any errors will be silent +fn parse_query_silent(query string) Values { + mut m := new_values() + parse_query_values(mut m, query) or {} + return m +} + +fn parse_query_values(mut m Values, query string) ?bool { + mut had_error := false + mut q := query + for q != '' { + mut key := q + mut i := key.index_any('&;') + if i >= 0 { + q = key[i + 1..] + key = key[..i] + } else { + q = '' + } + if key == '' { + continue + } + mut value := '' + if idx := key.index('=') { + i = idx + value = key[i + 1..] + key = key[..i] + } + k := query_unescape(key) or { + had_error = true + continue + } + key = k + v := query_unescape(value) or { + had_error = true + continue + } + value = v + m.add(key, value) + } + if had_error { + return error(error_msg('parse_query_values: failed parsing query string', '')) + } + return true +} + +// encode encodes the values into ``URL encoded'' form +// ('bar=baz&foo=quux') sorted by key. +pub fn (v Values) encode() string { + if v.len == 0 { + return '' + } + mut buf := strings.new_builder(200) + mut keys := []string{} + for k, _ in v.data { + keys << k + } + keys.sort() + for k in keys { + vs := v.data[k] + key_kscaped := query_escape(k) + for _, val in vs.data { + if buf.len > 0 { + buf.write_string('&') + } + buf.write_string(key_kscaped) + buf.write_string('=') + buf.write_string(query_escape(val)) + } + } + return buf.str() +} + +// resolve_path applies special path segments from refs and applies +// them to base, per RFC 3986. +fn resolve_path(base string, ref string) string { + mut full := '' + if ref == '' { + full = base + } else if ref[0] != `/` { + i := base.last_index('/') or { -1 } + full = base[..i + 1] + ref + } else { + full = ref + } + if full == '' { + return '' + } + mut dst := []string{} + src := full.split('/') + for _, elem in src { + match elem { + '.' { + // drop + } + '..' { + if dst.len > 0 { + dst = dst[..dst.len - 1] + } + } + else { + dst << elem + } + } + } + last := src[src.len - 1] + if last == '.' || last == '..' { + // Add final slash to the joined path. + dst << '' + } + return '/' + dst.join('/').trim_left('/') +} + +// is_abs reports whether the URL is absolute. +// Absolute means that it has a non-empty scheme. +pub fn (u &URL) is_abs() bool { + return u.scheme != '' +} + +// parse parses a URL in the context of the receiver. The provided URL +// may be relative or absolute. parse returns nil, err on parse +// failure, otherwise its return value is the same as resolve_reference. +pub fn (u &URL) parse(ref string) ?URL { + refurl := parse(ref) ? + return u.resolve_reference(refurl) +} + +// resolve_reference resolves a URI reference to an absolute URI from +// an absolute base URI u, per RFC 3986 Section 5.2. The URI reference +// may be relative or absolute. resolve_reference always returns a new +// URL instance, even if the returned URL is identical to either the +// base or reference. If ref is an absolute URL, then resolve_reference +// ignores base and returns a copy of ref. +pub fn (u &URL) resolve_reference(ref &URL) ?URL { + mut url := *ref + if ref.scheme == '' { + url.scheme = u.scheme + } + if ref.scheme != '' || ref.host != '' || !ref.user.empty() { + // The 'absoluteURI' or 'net_path' cases. + // We can ignore the error from set_path since we know we provided a + // validly-escaped path. + url.set_path(resolve_path(ref.escaped_path(), '')) ? + return url + } + if ref.opaque != '' { + url.user = user('') + url.host = '' + url.path = '' + return url + } + if ref.path == '' && ref.raw_query == '' { + url.raw_query = u.raw_query + if ref.fragment == '' { + url.fragment = u.fragment + } + } + // The 'abs_path' or 'rel_path' cases. + url.host = u.host + url.user = u.user + url.set_path(resolve_path(u.escaped_path(), ref.escaped_path())) ? + return url +} + +// query parses raw_query and returns the corresponding values. +// It silently discards malformed value pairs. +// To check errors use parseQuery. +pub fn (u &URL) query() Values { + v := parse_query_silent(u.raw_query) + return v +} + +// request_uri returns the encoded path?query or opaque?query +// string that would be used in an HTTP request for u. +pub fn (u &URL) request_uri() string { + mut result := u.opaque + if result == '' { + result = u.escaped_path() + if result == '' { + result = '/' + } + } else { + if result.starts_with('//') { + result = u.scheme + ':' + result + } + } + if u.force_query || u.raw_query != '' { + result += '?' + u.raw_query + } + return result +} + +// hostname returns u.host, stripping any valid port number if present. +// +// If the result is enclosed in square brackets, as literal IPv6 addresses are, +// the square brackets are removed from the result. +pub fn (u &URL) hostname() string { + host, _ := split_host_port(u.host) + return host +} + +// port returns the port part of u.host, without the leading colon. +// If u.host doesn't contain a port, port returns an empty string. +pub fn (u &URL) port() string { + _, port := split_host_port(u.host) + return port +} + +// split_host_port separates host and port. If the port is not valid, it returns +// the entire input as host, and it doesn't check the validity of the host. +// Per RFC 3986, it requires ports to be numeric. +fn split_host_port(hostport string) (string, string) { + mut host := hostport + mut port := '' + colon := host.last_index_byte(`:`) + if colon != -1 { + if valid_optional_port(host[colon..]) { + port = host[colon + 1..] + host = host[..colon] + } + } + if host.starts_with('[') && host.ends_with(']') { + host = host[1..host.len - 1] + } + return host, port +} + +// valid_userinfo reports whether s is a valid userinfo string per RFC 3986 +// Section 3.2.1: +// userinfo = *( unreserved / pct-encoded / sub-delims / ':' ) +// unreserved = ALPHA / DIGIT / '-' / '.' / '_' / '~' +// sub-delims = '!' / '$' / '&' / ''' / '(' / ')' +// / '*' / '+' / ',' / ';' / '=' +// +// It doesn't validate pct-encoded. The caller does that via fn unescape. +pub fn valid_userinfo(s string) bool { + for r in s { + if `A` <= r && r <= `Z` { + continue + } + if `a` <= r && r <= `z` { + continue + } + if `0` <= r && r <= `9` { + continue + } + match r { + `-`, `.`, `_`, `:`, `~`, `!`, `$`, `&`, `\\`, `(`, `)`, `*`, `+`, `,`, `;`, `=`, `%`, + `@` { + continue + } + else { + return false + } + } + } + return true +} + +// string_contains_ctl_byte reports whether s contains any ASCII control character. +fn string_contains_ctl_byte(s string) bool { + for i in 0 .. s.len { + b := s[i] + if b < ` ` || b == 0x7f { + return true + } + } + return false +} + +pub fn ishex(c byte) bool { + if `0` <= c && c <= `9` { + return true + } else if `a` <= c && c <= `f` { + return true + } else if `A` <= c && c <= `F` { + return true + } + return false +} + +fn unhex(c byte) byte { + if `0` <= c && c <= `9` { + return c - `0` + } else if `a` <= c && c <= `f` { + return c - `a` + 10 + } else if `A` <= c && c <= `F` { + return c - `A` + 10 + } + return 0 +} diff --git a/v_windows/v/old/vlib/net/urllib/urllib_test.v b/v_windows/v/old/vlib/net/urllib/urllib_test.v new file mode 100644 index 0000000..0870c81 --- /dev/null +++ b/v_windows/v/old/vlib/net/urllib/urllib_test.v @@ -0,0 +1,51 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +import net.urllib + +fn test_net_urllib() { + test_query := 'Hellö Wörld@vlang' + assert urllib.query_escape(test_query) == 'Hell%C3%B6+W%C3%B6rld%40vlang' + + test_url := 'https://joe:pass@www.mydomain.com:8080/som/url?param1=test1¶m2=test2&foo=bar#testfragment' + u := urllib.parse(test_url) or { + assert false + return + } + assert u.scheme == 'https' && u.hostname() == 'www.mydomain.com' && u.port() == '8080' + && u.path == '/som/url' && u.fragment == 'testfragment' && u.user.username == 'joe' + && u.user.password == 'pass' +} + +fn test_str() { + url := urllib.parse('https://en.wikipedia.org/wiki/Brazil_(1985_film)') or { + panic('unable to parse URL') + } + assert url.str() == 'https://en.wikipedia.org/wiki/Brazil_(1985_film)' +} + +fn test_escape_unescape() { + original := 'те ст: т\\%' + escaped := urllib.query_escape(original) + assert escaped == '%D1%82%D0%B5+%D1%81%D1%82%3A+%D1%82%5C%25' + unescaped := urllib.query_unescape(escaped) or { + assert false + return + } + assert unescaped == original +} + +fn test_parse_query() ? { + q1 := urllib.parse_query('format=%22%25l%3A+%25c+%25t%22') ? + q2 := urllib.parse_query('format="%l:+%c+%t"') ? + // dump(q1) + // dump(q2) + assert q1.data['format'].data == ['"%l: %c %t"'] + assert q2.data['format'].data == ['"%l: %c %t"'] +} + +fn test_parse_missing_host() ? { + // issue #10311 + url := urllib.parse('http:///') ? + assert url.str() == 'http://///' +} diff --git a/v_windows/v/old/vlib/net/urllib/values.v b/v_windows/v/old/vlib/net/urllib/values.v new file mode 100644 index 0000000..ee5c329 --- /dev/null +++ b/v_windows/v/old/vlib/net/urllib/values.v @@ -0,0 +1,87 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module urllib + +struct Value { +pub mut: + data []string +} + +struct Values { +pub mut: + data map[string]Value + len int +} + +// new_values returns a new Values struct for creating +// urlencoded query string parameters. it can also be to +// post form data with application/x-www-form-urlencoded. +// values.encode() will return the encoded data +pub fn new_values() Values { + return Values{ + data: map[string]Value{} + } +} + +// Currently you will need to use all()[key].data +// once map[string][]string is implemented +// this will be fixed +pub fn (v &Value) all() []string { + return v.data +} + +// get gets the first value associated with the given key. +// If there are no values associated with the key, get returns +// a empty string. +pub fn (v &Values) get(key string) string { + if v.data.len == 0 { + return '' + } + vs := v.data[key] + if vs.data.len == 0 { + return '' + } + return vs.data[0] +} + +// get_all gets the all the values associated with the given key. +// If there are no values associated with the key, get returns +// a empty []string. +pub fn (v &Values) get_all(key string) []string { + if v.data.len == 0 { + return [] + } + vs := v.data[key] + if vs.data.len == 0 { + return [] + } + return vs.data +} + +// set sets the key to value. It replaces any existing +// values. +pub fn (mut v Values) set(key string, value string) { + mut a := v.data[key] + a.data = [value] + v.data[key] = a + v.len = v.data.len +} + +// add adds the value to key. It appends to any existing +// values associated with key. +pub fn (mut v Values) add(key string, value string) { + mut a := v.data[key] + if a.data.len == 0 { + a.data = [] + } + a.data << value + v.data[key] = a + v.len = v.data.len +} + +// del deletes the values associated with key. +pub fn (mut v Values) del(key string) { + v.data.delete(key) + v.len = v.data.len +} diff --git a/v_windows/v/old/vlib/net/util.v b/v_windows/v/old/vlib/net/util.v new file mode 100644 index 0000000..33d7cec --- /dev/null +++ b/v_windows/v/old/vlib/net/util.v @@ -0,0 +1,27 @@ +module net + +const ( + socket_max_port = u16(0xFFFF) +) + +// validate_port checks whether a port is valid +// and returns the port or an error +pub fn validate_port(port int) ?u16 { + if port <= net.socket_max_port { + return u16(port) + } else { + return err_port_out_of_range + } +} + +// split address splits an address into its host name and its port +pub fn split_address(addr string) ?(string, u16) { + port := addr.all_after_last(':').int() + address := addr.all_before_last(':') + + // TODO(emily): Maybe do some more checking here + // to validate ipv6 address sanity? + + p := validate_port(port) ? + return address, p +} diff --git a/v_windows/v/old/vlib/net/websocket/events.v b/v_windows/v/old/vlib/net/websocket/events.v new file mode 100644 index 0000000..a442daf --- /dev/null +++ b/v_windows/v/old/vlib/net/websocket/events.v @@ -0,0 +1,227 @@ +module websocket + +// MessageEventHandler represents a callback on a new message +struct MessageEventHandler { + handler SocketMessageFn // callback function + handler2 SocketMessageFn2 // callback function with reference + is_ref bool // true if has a reference object + ref voidptr // referenced object +} + +// ErrorEventHandler represents a callback on error +struct ErrorEventHandler { + handler SocketErrorFn // callback function + handler2 SocketErrorFn2 // callback function with reference + is_ref bool // true if has a reference object + ref voidptr // referenced object +} + +// OpenEventHandler represents a callback when connection is opened +struct OpenEventHandler { + handler SocketOpenFn // callback function + handler2 SocketOpenFn2 // callback function with reference + is_ref bool // true if has a reference object + ref voidptr // referenced object +} + +// CloseEventHandler represents a callback on a closing event +struct CloseEventHandler { + handler SocketCloseFn // callback function + handler2 SocketCloseFn2 // callback function with reference + is_ref bool // true if has a reference object + ref voidptr // referenced object +} + +pub type AcceptClientFn = fn (mut c ServerClient) ?bool + +pub type SocketMessageFn = fn (mut c Client, msg &Message) ? + +pub type SocketMessageFn2 = fn (mut c Client, msg &Message, v voidptr) ? + +pub type SocketErrorFn = fn (mut c Client, err string) ? + +pub type SocketErrorFn2 = fn (mut c Client, err string, v voidptr) ? + +pub type SocketOpenFn = fn (mut c Client) ? + +pub type SocketOpenFn2 = fn (mut c Client, v voidptr) ? + +pub type SocketCloseFn = fn (mut c Client, code int, reason string) ? + +pub type SocketCloseFn2 = fn (mut c Client, code int, reason string, v voidptr) ? + +// on_connect registers a callback when client connects to the server +pub fn (mut s Server) on_connect(fun AcceptClientFn) ? { + if s.accept_client_callbacks.len > 0 { + return error('only one callback can be registered for accept client') + } + s.accept_client_callbacks << fun +} + +// on_message registers a callback on new messages +pub fn (mut s Server) on_message(fun SocketMessageFn) { + s.message_callbacks << MessageEventHandler{ + handler: fun + } +} + +// on_message_ref registers a callback on new messages and provides a reference object +pub fn (mut s Server) on_message_ref(fun SocketMessageFn2, ref voidptr) { + s.message_callbacks << MessageEventHandler{ + handler2: fun + ref: ref + is_ref: true + } +} + +// on_close registers a callback on closed socket +pub fn (mut s Server) on_close(fun SocketCloseFn) { + s.close_callbacks << CloseEventHandler{ + handler: fun + } +} + +// on_close_ref registers a callback on closed socket and provides a reference object +pub fn (mut s Server) on_close_ref(fun SocketCloseFn2, ref voidptr) { + s.close_callbacks << CloseEventHandler{ + handler2: fun + ref: ref + is_ref: true + } +} + +// on_message registers a callback on new messages +pub fn (mut ws Client) on_message(fun SocketMessageFn) { + ws.message_callbacks << MessageEventHandler{ + handler: fun + } +} + +// on_message_ref registers a callback on new messages and provides a reference object +pub fn (mut ws Client) on_message_ref(fun SocketMessageFn2, ref voidptr) { + ws.message_callbacks << MessageEventHandler{ + handler2: fun + ref: ref + is_ref: true + } +} + +// on_error registers a callback on errors +pub fn (mut ws Client) on_error(fun SocketErrorFn) { + ws.error_callbacks << ErrorEventHandler{ + handler: fun + } +} + +// on_error_ref registers a callback on errors and provides a reference object +pub fn (mut ws Client) on_error_ref(fun SocketErrorFn2, ref voidptr) { + ws.error_callbacks << ErrorEventHandler{ + handler2: fun + ref: ref + is_ref: true + } +} + +// on_open registers a callback on successful opening the websocket +pub fn (mut ws Client) on_open(fun SocketOpenFn) { + ws.open_callbacks << OpenEventHandler{ + handler: fun + } +} + +// on_open_ref registers a callback on successful opening the websocket +// and provides a reference object +pub fn (mut ws Client) on_open_ref(fun SocketOpenFn2, ref voidptr) { + ws.open_callbacks << OpenEventHandler{ + handler2: fun + ref: ref + is_ref: true + } +} + +// on_close registers a callback on closed socket +pub fn (mut ws Client) on_close(fun SocketCloseFn) { + ws.close_callbacks << CloseEventHandler{ + handler: fun + } +} + +// on_close_ref registers a callback on closed socket and provides a reference object +pub fn (mut ws Client) on_close_ref(fun SocketCloseFn2, ref voidptr) { + ws.close_callbacks << CloseEventHandler{ + handler2: fun + ref: ref + is_ref: true + } +} + +// send_connect_event invokes the on_connect callback +fn (mut s Server) send_connect_event(mut c ServerClient) ?bool { + if s.accept_client_callbacks.len == 0 { + // If no callback all client will be accepted + return true + } + fun := s.accept_client_callbacks[0] + res := fun(mut c) ? + return res +} + +// send_message_event invokes the on_message callback +fn (mut ws Client) send_message_event(msg &Message) { + ws.debug_log('sending on_message event') + for ev_handler in ws.message_callbacks { + if !ev_handler.is_ref { + ev_handler.handler(ws, msg) or { ws.logger.error('send_message_event error: $err') } + } else { + ev_handler.handler2(ws, msg, ev_handler.ref) or { + ws.logger.error('send_message_event error: $err') + } + } + } +} + +// send_error_event invokes the on_error callback +fn (mut ws Client) send_error_event(error string) { + ws.debug_log('sending on_error event') + for ev_handler in ws.error_callbacks { + if !ev_handler.is_ref { + ev_handler.handler(mut ws, error) or { + ws.logger.error('send_error_event error: $error, err: $err') + } + } else { + ev_handler.handler2(mut ws, error, ev_handler.ref) or { + ws.logger.error('send_error_event error: $error, err: $err') + } + } + } +} + +// send_close_event invokes the on_close callback +fn (mut ws Client) send_close_event(code int, reason string) { + ws.debug_log('sending on_close event') + for ev_handler in ws.close_callbacks { + if !ev_handler.is_ref { + ev_handler.handler(mut ws, code, reason) or { + ws.logger.error('send_close_event error: $err') + } + } else { + ev_handler.handler2(mut ws, code, reason, ev_handler.ref) or { + ws.logger.error('send_close_event error: $err') + } + } + } +} + +// send_open_event invokes the on_open callback +fn (mut ws Client) send_open_event() { + ws.debug_log('sending on_open event') + for ev_handler in ws.open_callbacks { + if !ev_handler.is_ref { + ev_handler.handler(mut ws) or { ws.logger.error('send_open_event error: $err') } + } else { + ev_handler.handler2(mut ws, ev_handler.ref) or { + ws.logger.error('send_open_event error: $err') + } + } + } +} diff --git a/v_windows/v/old/vlib/net/websocket/handshake.v b/v_windows/v/old/vlib/net/websocket/handshake.v new file mode 100644 index 0000000..9f3ab00 --- /dev/null +++ b/v_windows/v/old/vlib/net/websocket/handshake.v @@ -0,0 +1,185 @@ +[manualfree] +module websocket + +import encoding.base64 +import strings + +// handshake manages the websocket handshake process +fn (mut ws Client) handshake() ? { + nonce := get_nonce(ws.nonce_size) + seckey := base64.encode_str(nonce) + mut sb := strings.new_builder(1024) + defer { + unsafe { sb.free() } + } + sb.write_string('GET ') + sb.write_string(ws.uri.resource) + sb.write_string(ws.uri.querystring) + sb.write_string(' HTTP/1.1\r\nHost: ') + sb.write_string(ws.uri.hostname) + sb.write_string(':') + sb.write_string(ws.uri.port) + sb.write_string('\r\nUpgrade: websocket\r\nConnection: Upgrade\r\n') + sb.write_string('Sec-WebSocket-Key: ') + sb.write_string(seckey) + sb.write_string('\r\nSec-WebSocket-Version: 13') + for key in ws.header.keys() { + val := ws.header.custom_values(key).join(',') + sb.write_string('\r\n$key:$val') + } + sb.write_string('\r\n\r\n') + handshake := sb.str() + defer { + unsafe { handshake.free() } + } + handshake_bytes := handshake.bytes() + ws.debug_log('sending handshake: $handshake') + ws.socket_write(handshake_bytes) ? + ws.read_handshake(seckey) ? + unsafe { handshake_bytes.free() } +} + +// handle_server_handshake manages websocket server handshake process +fn (mut s Server) handle_server_handshake(mut c Client) ?(string, &ServerClient) { + msg := c.read_handshake_str() ? + handshake_response, client := s.parse_client_handshake(msg, mut c) ? + unsafe { msg.free() } + return handshake_response, client +} + +// parse_client_handshake parses result from handshake process +fn (mut s Server) parse_client_handshake(client_handshake string, mut c Client) ?(string, &ServerClient) { + s.logger.debug('server-> client handshake:\n$client_handshake') + lines := client_handshake.split_into_lines() + get_tokens := lines[0].split(' ') + if get_tokens.len < 3 { + return error_with_code('unexpected get operation, $get_tokens', 1) + } + if get_tokens[0].trim_space() != 'GET' { + return error_with_code("unexpected request '${get_tokens[0]}', expected 'GET'", + 2) + } + if get_tokens[2].trim_space() != 'HTTP/1.1' { + return error_with_code("unexpected request $get_tokens, expected 'HTTP/1.1'", + 3) + } + mut seckey := '' + mut flags := []Flag{} + mut key := '' + for i in 1 .. lines.len { + if lines[i].len <= 0 || lines[i] == '\r\n' { + continue + } + keys := lines[i].split(':') + match keys[0] { + 'Upgrade', 'upgrade' { + flags << .has_upgrade + } + 'Connection', 'connection' { + flags << .has_connection + } + 'Sec-WebSocket-Key', 'sec-websocket-key' { + key = keys[1].trim_space() + s.logger.debug('server-> got key: $key') + seckey = create_key_challenge_response(key) ? + s.logger.debug('server-> challenge: $seckey, response: ${keys[1]}') + flags << .has_accept + } + else { + // we ignore other headers like protocol for now + } + } + unsafe { keys.free() } + } + if flags.len < 3 { + return error_with_code('invalid client handshake, $client_handshake', 4) + } + server_handshake := 'HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: $seckey\r\n\r\n' + server_client := &ServerClient{ + resource_name: get_tokens[1] + client_key: key + client: unsafe { c } + server: unsafe { s } + } + unsafe { + lines.free() + flags.free() + get_tokens.free() + seckey.free() + key.free() + } + return server_handshake, server_client +} + +// read_handshake_str returns the handshake response +fn (mut ws Client) read_handshake_str() ?string { + mut total_bytes_read := 0 + mut msg := [1024]byte{} + mut buffer := [1]byte{} + for total_bytes_read < 1024 { + bytes_read := ws.socket_read_ptr(&buffer[0], 1) ? + if bytes_read == 0 { + return error_with_code('unexpected no response from handshake', 5) + } + msg[total_bytes_read] = buffer[0] + total_bytes_read++ + if total_bytes_read > 5 && msg[total_bytes_read - 1] == `\n` + && msg[total_bytes_read - 2] == `\r` && msg[total_bytes_read - 3] == `\n` + && msg[total_bytes_read - 4] == `\r` { + break + } + } + res := msg[..total_bytes_read].bytestr() + return res +} + +// read_handshake reads the handshake result and check if valid +fn (mut ws Client) read_handshake(seckey string) ? { + mut msg := ws.read_handshake_str() ? + ws.check_handshake_response(msg, seckey) ? + unsafe { msg.free() } +} + +// check_handshake_response checks the response from handshake and returns +// the response and secure key provided by the websocket client +fn (mut ws Client) check_handshake_response(handshake_response string, seckey string) ? { + ws.debug_log('handshake response:\n$handshake_response') + lines := handshake_response.split_into_lines() + header := lines[0] + if !header.starts_with('HTTP/1.1 101') && !header.starts_with('HTTP/1.0 101') { + return error_with_code('handshake_handler: invalid HTTP status response code, $header', + 6) + } + for i in 1 .. lines.len { + if lines[i].len <= 0 || lines[i] == '\r\n' { + continue + } + keys := lines[i].split(':') + match keys[0] { + 'Upgrade', 'upgrade' { + ws.flags << .has_upgrade + } + 'Connection', 'connection' { + ws.flags << .has_connection + } + 'Sec-WebSocket-Accept', 'sec-websocket-accept' { + ws.debug_log('seckey: $seckey') + challenge := create_key_challenge_response(seckey) ? + ws.debug_log('challenge: $challenge, response: ${keys[1]}') + if keys[1].trim_space() != challenge { + return error_with_code('handshake_handler: Sec-WebSocket-Accept header does not match computed sha1/base64 response.', + 7) + } + ws.flags << .has_accept + unsafe { challenge.free() } + } + else {} + } + unsafe { keys.free() } + } + unsafe { lines.free() } + if ws.flags.len < 3 { + ws.close(1002, 'invalid websocket HTTP headers') ? + return error_with_code('invalid websocket HTTP headers', 8) + } +} diff --git a/v_windows/v/old/vlib/net/websocket/io.v b/v_windows/v/old/vlib/net/websocket/io.v new file mode 100644 index 0000000..5408a4e --- /dev/null +++ b/v_windows/v/old/vlib/net/websocket/io.v @@ -0,0 +1,100 @@ +module websocket + +import net +import time + +// socket_read reads from socket into the provided buffer +fn (mut ws Client) socket_read(mut buffer []byte) ?int { + lock { + if ws.state in [.closed, .closing] || ws.conn.sock.handle <= 1 { + return error('socket_read: trying to read a closed socket') + } + if ws.is_ssl { + r := ws.ssl_conn.read_into(mut buffer) ? + return r + } else { + for { + r := ws.conn.read(mut buffer) or { + if err.code == net.err_timed_out_code { + continue + } + return err + } + return r + } + } + } + return none +} + +// socket_read reads from socket into the provided byte pointer and length +fn (mut ws Client) socket_read_ptr(buf_ptr &byte, len int) ?int { + lock { + if ws.state in [.closed, .closing] || ws.conn.sock.handle <= 1 { + return error('socket_read_ptr: trying to read a closed socket') + } + if ws.is_ssl { + r := ws.ssl_conn.socket_read_into_ptr(buf_ptr, len) ? + return r + } else { + for { + r := ws.conn.read_ptr(buf_ptr, len) or { + if err.code == net.err_timed_out_code { + continue + } + return err + } + return r + } + } + } + return none +} + +// socket_write writes the provided byte array to the socket +fn (mut ws Client) socket_write(bytes []byte) ?int { + lock { + if ws.state == .closed || ws.conn.sock.handle <= 1 { + ws.debug_log('socket_write: Socket allready closed') + return error('socket_write: trying to write on a closed socket') + } + if ws.is_ssl { + return ws.ssl_conn.write(bytes) + } else { + for { + n := ws.conn.write(bytes) or { + if err.code == net.err_timed_out_code { + continue + } + return err + } + return n + } + panic('reached unreachable code') + } + } +} + +// shutdown_socket shuts down the socket properly when connection is closed +fn (mut ws Client) shutdown_socket() ? { + ws.debug_log('shutting down socket') + if ws.is_ssl { + ws.ssl_conn.shutdown() ? + } else { + ws.conn.close() ? + } +} + +// dial_socket connects tcp socket and initializes default configurations +fn (mut ws Client) dial_socket() ?&net.TcpConn { + tcp_address := '$ws.uri.hostname:$ws.uri.port' + mut t := net.dial_tcp(tcp_address) ? + optval := int(1) + t.sock.set_option_int(.keep_alive, optval) ? + t.set_read_timeout(30 * time.second) + t.set_write_timeout(30 * time.second) + if ws.is_ssl { + ws.ssl_conn.connect(mut t, ws.uri.hostname) ? + } + return t +} diff --git a/v_windows/v/old/vlib/net/websocket/message.v b/v_windows/v/old/vlib/net/websocket/message.v new file mode 100644 index 0000000..4c57232 --- /dev/null +++ b/v_windows/v/old/vlib/net/websocket/message.v @@ -0,0 +1,295 @@ +module websocket + +import encoding.utf8 + +const ( + header_len_offset = 2 // offset for lengthpart of websocket header + buffer_size = 256 // default buffer size + extended_payload16_end_byte = 4 // header length with 16-bit extended payload + extended_payload64_end_byte = 10 // header length with 64-bit extended payload +) + +// Fragment represents a websocket data fragment +struct Fragment { + data []byte // included data payload data in a fragment + opcode OPCode // interpretation of the payload data +} + +// Frame represents a data frame header +struct Frame { +mut: + // length of the websocket header part + header_len int = 2 + // size of total frame + frame_size int = 2 + fin bool // true if final fragment of message + rsv1 bool // reserved for future use in websocket RFC + rsv2 bool // reserved for future use in websocket RFC + rsv3 bool // reserved for future use in websocket RFC + opcode OPCode // interpretation of the payload data + has_mask bool // true if the payload data is masked + payload_len int // payload length + masking_key [4]byte // all frames from client to server is masked with this key +} + +const ( + invalid_close_codes = [999, 1004, 1005, 1006, 1014, 1015, 1016, 1100, 2000, 2999, 5000, 65536] +) + +// validate_client validates client frame rules from RFC6455 +pub fn (mut ws Client) validate_frame(frame &Frame) ? { + if frame.rsv1 || frame.rsv2 || frame.rsv3 { + ws.close(1002, 'rsv cannot be other than 0, not negotiated') ? + return error('rsv cannot be other than 0, not negotiated') + } + if (int(frame.opcode) >= 3 && int(frame.opcode) <= 7) + || (int(frame.opcode) >= 11 && int(frame.opcode) <= 15) { + ws.close(1002, 'use of reserved opcode') ? + return error('use of reserved opcode') + } + if frame.has_mask && !ws.is_server { + // server should never send masked frames + // to client, close connection + ws.close(1002, 'client got masked frame') ? + return error('client sent masked frame') + } + if is_control_frame(frame.opcode) { + if !frame.fin { + ws.close(1002, 'control message must not be fragmented') ? + return error('unexpected control frame with no fin') + } + if frame.payload_len > 125 { + ws.close(1002, 'control frames must not exceed 125 bytes') ? + return error('unexpected control frame payload length') + } + } + if frame.fin == false && ws.fragments.len == 0 && frame.opcode == .continuation { + err_msg := 'unexecpected continuation, there are no frames to continue, $frame' + ws.close(1002, err_msg) ? + return error(err_msg) + } +} + +// is_control_frame returns true if the frame is a control frame +fn is_control_frame(opcode OPCode) bool { + return opcode !in [.text_frame, .binary_frame, .continuation] +} + +// is_data_frame returns true if the frame is a control frame +fn is_data_frame(opcode OPCode) bool { + return opcode in [.text_frame, .binary_frame] +} + +// read_payload reads the message payload from the socket +fn (mut ws Client) read_payload(frame &Frame) ?[]byte { + if frame.payload_len == 0 { + return []byte{} + } + mut buffer := []byte{cap: frame.payload_len} + mut read_buf := [1]byte{} + mut bytes_read := 0 + for bytes_read < frame.payload_len { + len := ws.socket_read_ptr(&read_buf[0], 1) ? + if len != 1 { + return error('expected read all message, got zero') + } + bytes_read += len + buffer << read_buf[0] + } + if bytes_read != frame.payload_len { + return error('failed to read payload') + } + if frame.has_mask { + for i in 0 .. frame.payload_len { + buffer[i] ^= frame.masking_key[i % 4] & 0xff + } + } + return buffer +} + +// validate_utf_8 validates payload for valid utf8 encoding +// - Future implementation needs to support fail fast utf errors for strict autobahn conformance +fn (mut ws Client) validate_utf_8(opcode OPCode, payload []byte) ? { + if opcode in [.text_frame, .close] && !utf8.validate(payload.data, payload.len) { + ws.logger.error('malformed utf8 payload, payload len: ($payload.len)') + ws.send_error_event('Recieved malformed utf8.') + ws.close(1007, 'malformed utf8 payload') ? + return error('malformed utf8 payload') + } +} + +// read_next_message reads 1 to n frames to compose a message +pub fn (mut ws Client) read_next_message() ?Message { + for { + frame := ws.parse_frame_header() ? + ws.validate_frame(&frame) ? + frame_payload := ws.read_payload(&frame) ? + if is_control_frame(frame.opcode) { + // Control frames can interject other frames + // and need to be returned immediately + msg := Message{ + opcode: OPCode(frame.opcode) + payload: frame_payload.clone() + } + unsafe { frame_payload.free() } + return msg + } + // if the message is fragmented we just put it on fragments + // a fragment is allowed to have zero size payload + if !frame.fin { + ws.fragments << &Fragment{ + data: frame_payload.clone() + opcode: frame.opcode + } + unsafe { frame_payload.free() } + continue + } + if ws.fragments.len == 0 { + ws.validate_utf_8(frame.opcode, frame_payload) or { + ws.logger.error('UTF8 validation error: $err, len of payload($frame_payload.len)') + ws.send_error_event('UTF8 validation error: $err, len of payload($frame_payload.len)') + return err + } + msg := Message{ + opcode: OPCode(frame.opcode) + payload: frame_payload.clone() + } + unsafe { frame_payload.free() } + return msg + } + defer { + ws.fragments = [] + } + if is_data_frame(frame.opcode) { + ws.close(0, '') ? + return error('Unexpected frame opcode') + } + payload := ws.payload_from_fragments(frame_payload) ? + opcode := ws.opcode_from_fragments() + ws.validate_utf_8(opcode, payload) ? + msg := Message{ + opcode: opcode + payload: payload.clone() + } + unsafe { + frame_payload.free() + payload.free() + } + return msg + } + return none +} + +// payload_from_fragments returs the whole paylaod from fragmented message +fn (ws Client) payload_from_fragments(fin_payload []byte) ?[]byte { + mut total_size := 0 + for f in ws.fragments { + if f.data.len > 0 { + total_size += f.data.len + } + } + total_size += fin_payload.len + if total_size == 0 { + return []byte{} + } + mut total_buffer := []byte{cap: total_size} + for f in ws.fragments { + if f.data.len > 0 { + total_buffer << f.data + } + } + total_buffer << fin_payload + return total_buffer +} + +// opcode_from_fragments returns the opcode for message from the first fragment sent +fn (ws Client) opcode_from_fragments() OPCode { + return OPCode(ws.fragments[0].opcode) +} + +// parse_frame_header parses next message by decoding the incoming frames +pub fn (mut ws Client) parse_frame_header() ?Frame { + mut buffer := [256]byte{} + mut bytes_read := 0 + mut frame := Frame{} + mut rbuff := [1]byte{} + mut mask_end_byte := 0 + for ws.state == .open { + read_bytes := ws.socket_read_ptr(&rbuff[0], 1) ? + if read_bytes == 0 { + // this is probably a timeout or close + continue + } + buffer[bytes_read] = rbuff[0] + bytes_read++ + // parses the first two header bytes to get basic frame information + if bytes_read == u64(websocket.header_len_offset) { + frame.fin = (buffer[0] & 0x80) == 0x80 + frame.rsv1 = (buffer[0] & 0x40) == 0x40 + frame.rsv2 = (buffer[0] & 0x20) == 0x20 + frame.rsv3 = (buffer[0] & 0x10) == 0x10 + frame.opcode = OPCode(int(buffer[0] & 0x7F)) + frame.has_mask = (buffer[1] & 0x80) == 0x80 + frame.payload_len = buffer[1] & 0x7F + // if has mask set the byte postition where mask ends + if frame.has_mask { + mask_end_byte = if frame.payload_len < 126 { + websocket.header_len_offset + 4 + } else if frame.payload_len == 126 { + websocket.header_len_offset + 6 + } else if frame.payload_len == 127 { + websocket.header_len_offset + 12 + } else { + 0 + } // impossible + } + frame.payload_len = frame.payload_len + frame.frame_size = frame.header_len + frame.payload_len + if !frame.has_mask && frame.payload_len < 126 { + break + } + } + if frame.payload_len == 126 && bytes_read == u64(websocket.extended_payload16_end_byte) { + frame.header_len += 2 + frame.payload_len = 0 + frame.payload_len |= buffer[2] << 8 + frame.payload_len |= buffer[3] + frame.frame_size = frame.header_len + frame.payload_len + if !frame.has_mask { + break + } + } + if frame.payload_len == 127 && bytes_read == u64(websocket.extended_payload64_end_byte) { + frame.header_len += 8 + // these shift operators needs 64 bit on clang with -prod flag + mut payload_len := u64(0) + payload_len |= u64(buffer[2]) << 56 + payload_len |= u64(buffer[3]) << 48 + payload_len |= u64(buffer[4]) << 40 + payload_len |= u64(buffer[5]) << 32 + payload_len |= u64(buffer[6]) << 24 + payload_len |= u64(buffer[7]) << 16 + payload_len |= u64(buffer[8]) << 8 + payload_len |= u64(buffer[9]) + frame.payload_len = int(payload_len) + if !frame.has_mask { + break + } + } + if frame.has_mask && bytes_read == mask_end_byte { + frame.masking_key[0] = buffer[mask_end_byte - 4] + frame.masking_key[1] = buffer[mask_end_byte - 3] + frame.masking_key[2] = buffer[mask_end_byte - 2] + frame.masking_key[3] = buffer[mask_end_byte - 1] + break + } + } + return frame +} + +// unmask_sequence unmask any given sequence +fn (f Frame) unmask_sequence(mut buffer []byte) { + for i in 0 .. buffer.len { + buffer[i] ^= f.masking_key[i % 4] & 0xff + } +} diff --git a/v_windows/v/old/vlib/net/websocket/tests/autobahn/README.md b/v_windows/v/old/vlib/net/websocket/tests/autobahn/README.md new file mode 100644 index 0000000..40724ee --- /dev/null +++ b/v_windows/v/old/vlib/net/websocket/tests/autobahn/README.md @@ -0,0 +1,20 @@ +# Autobahn tests + +This is the autobahn automatic tests on build. +The performance tests are skipped due to timeouts in Github actions. + +## Run it locally + +### Test the client + +This is how to test the client: + +1. Run the docker autobahn test suite by running the `docker-compose up` +2. From the `local_run` folder, compile and run `autobahn_client.v` to test non ws (no TLS) and +`autobahn_client_wss.v` to run the TLS tests +3. Open `http://localhost:8080` and browse client test results for non TLS and `https://localhost:8081` +if you ran the wss tests (it uses local certificat so you will get trust error but just accept use) + +### Test the server + +Todo: add information here \ No newline at end of file diff --git a/v_windows/v/old/vlib/net/websocket/tests/autobahn/autobahn_client.v b/v_windows/v/old/vlib/net/websocket/tests/autobahn/autobahn_client.v new file mode 100644 index 0000000..c65fdab --- /dev/null +++ b/v_windows/v/old/vlib/net/websocket/tests/autobahn/autobahn_client.v @@ -0,0 +1,33 @@ +// use this test to test the websocket client in the autobahn test +module main + +import net.websocket + +fn main() { + for i in 1 .. 304 { + println('\ncase: $i') + handle_case(i) or { println('error should be ok: $err') } + } + // update the reports + uri := 'ws://autobahn_server:9001/updateReports?agent=v-client' + mut ws := websocket.new_client(uri) ? + ws.connect() ? + ws.listen() ? +} + +fn handle_case(case_nr int) ? { + uri := 'ws://autobahn_server:9001/runCase?case=$case_nr&agent=v-client' + mut ws := websocket.new_client(uri) ? + ws.on_message(on_message) + ws.connect() ? + ws.listen() ? +} + +fn on_message(mut ws websocket.Client, msg &websocket.Message) ? { + // autobahn tests expects to send same message back + if msg.opcode == .pong { + // We just wanna pass text and binary message back to autobahn + return + } + ws.write(msg.payload, msg.opcode) or { panic(err) } +} diff --git a/v_windows/v/old/vlib/net/websocket/tests/autobahn/autobahn_client_wss.v b/v_windows/v/old/vlib/net/websocket/tests/autobahn/autobahn_client_wss.v new file mode 100644 index 0000000..c7a3c25 --- /dev/null +++ b/v_windows/v/old/vlib/net/websocket/tests/autobahn/autobahn_client_wss.v @@ -0,0 +1,35 @@ +// use this test to test the websocket client in the autobahn test +module main + +import net.websocket + +fn main() { + for i in 1 .. 304 { + println('\ncase: $i') + handle_case(i) or { println('error should be ok: $err') } + } + // update the reports + // uri := 'wss://localhost:9002/updateReports?agent=v-client' + uri := 'wss://autobahn_server_wss:9002/updateReports?agent=v-client' + mut ws := websocket.new_client(uri) ? + ws.connect() ? + ws.listen() ? +} + +fn handle_case(case_nr int) ? { + uri := 'wss://autobahn_server_wss:9002/runCase?case=$case_nr&agent=v-client' + // uri := 'wss://localhost:9002/runCase?case=$case_nr&agent=v-client' + mut ws := websocket.new_client(uri) ? + ws.on_message(on_message) + ws.connect() ? + ws.listen() ? +} + +fn on_message(mut ws websocket.Client, msg &websocket.Message) ? { + // autobahn tests expects to send same message back + if msg.opcode == .pong { + // We just wanna pass text and binary message back to autobahn + return + } + ws.write(msg.payload, msg.opcode) or { panic(err) } +} diff --git a/v_windows/v/old/vlib/net/websocket/tests/autobahn/autobahn_server.v b/v_windows/v/old/vlib/net/websocket/tests/autobahn/autobahn_server.v new file mode 100644 index 0000000..0493ca9 --- /dev/null +++ b/v_windows/v/old/vlib/net/websocket/tests/autobahn/autobahn_server.v @@ -0,0 +1,27 @@ +// use this to test websocket server to the autobahn test +module main + +import net.websocket + +fn main() { + mut s := websocket.new_server(.ip6, 9002, '/') + s.on_message(on_message) + s.listen() or { panic(err) } +} + +fn handle_case(case_nr int) ? { + uri := 'ws://localhost:9002/runCase?case=$case_nr&agent=v-client' + mut ws := websocket.new_client(uri) ? + ws.on_message(on_message) + ws.connect() ? + ws.listen() ? +} + +fn on_message(mut ws websocket.Client, msg &websocket.Message) ? { + // autobahn tests expects to send same message back + if msg.opcode == .pong { + // We just wanna pass text and binary message back to autobahn + return + } + ws.write(msg.payload, msg.opcode) or { panic(err) } +} diff --git a/v_windows/v/old/vlib/net/websocket/tests/autobahn/docker-compose.yml b/v_windows/v/old/vlib/net/websocket/tests/autobahn/docker-compose.yml new file mode 100644 index 0000000..30b58ec --- /dev/null +++ b/v_windows/v/old/vlib/net/websocket/tests/autobahn/docker-compose.yml @@ -0,0 +1,21 @@ +version: '3' +services: + server: + container_name: autobahn_server + build: fuzzing_server + + ports: + - "9001:9001" + - "8080:8080" + server_wss: + container_name: autobahn_server_wss + build: fuzzing_server_wss + + ports: + - "9002:9002" + - "8081:8080" + client: + container_name: autobahn_client + build: + dockerfile: vlib/net/websocket/tests/autobahn/ws_test/Dockerfile + context: ../../../../../ diff --git a/v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server/Dockerfile b/v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server/Dockerfile new file mode 100644 index 0000000..ca5201b --- /dev/null +++ b/v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server/Dockerfile @@ -0,0 +1,5 @@ +FROM crossbario/autobahn-testsuite +COPY check_results.py /check_results.py +RUN chmod +x /check_results.py + +COPY config /config diff --git a/v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server/check_results.py b/v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server/check_results.py new file mode 100644 index 0000000..9275c3c --- /dev/null +++ b/v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server/check_results.py @@ -0,0 +1,46 @@ +import json + +nr_of_client_errs = 0 +nr_of_client_tests = 0 + +nr_of_server_errs = 0 +nr_of_server_tests = 0 + +with open("/reports/clients/index.json") as f: + data = json.load(f) + + for i in data["v-client"]: + # Count errors + if ( + data["v-client"][i]["behavior"] == "FAILED" + or data["v-client"][i]["behaviorClose"] == "FAILED" + ): + nr_of_client_errs = nr_of_client_errs + 1 + + nr_of_client_tests = nr_of_client_tests + 1 + +with open("/reports/servers/index.json") as f: + data = json.load(f) + + for i in data["AutobahnServer"]: + if ( + data["AutobahnServer"][i]["behavior"] == "FAILED" + or data["AutobahnServer"][i]["behaviorClose"] == "FAILED" + ): + nr_of_server_errs = nr_of_server_errs + 1 + + nr_of_server_tests = nr_of_server_tests + 1 + +if nr_of_client_errs > 0 or nr_of_server_errs > 0: + print( + "FAILED AUTOBAHN TESTS, CLIENT ERRORS {0}(of {1}), SERVER ERRORS {2}(of {3})".format( + nr_of_client_errs, nr_of_client_tests, nr_of_server_errs, nr_of_server_tests + ) + ) + exit(1) + +print( + "TEST SUCCESS!, CLIENT TESTS({0}), SERVER TESTS ({1})".format( + nr_of_client_tests, nr_of_server_tests + ) +) diff --git a/v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server/config/fuzzingclient.json b/v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server/config/fuzzingclient.json new file mode 100644 index 0000000..b5efbb8 --- /dev/null +++ b/v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server/config/fuzzingclient.json @@ -0,0 +1,22 @@ +{ + "options": { + "failByDrop": false + }, + "outdir": "./reports/servers", + "servers": [ + { + "agent": "AutobahnServer", + "url": "ws://autobahn_client:9002" + } + ], + "cases": [ + "*" + ], + "exclude-cases": [ + "9.*", + "11.*", + "12.*", + "13.*" + ], + "exclude-agent-cases": {} +} \ No newline at end of file diff --git a/v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server/config/fuzzingserver.json b/v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server/config/fuzzingserver.json new file mode 100644 index 0000000..3b044a1 --- /dev/null +++ b/v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server/config/fuzzingserver.json @@ -0,0 +1,14 @@ +{ + "url": "ws://127.0.0.1:9001", + "outdir": "./reports/clients", + "cases": [ + "*" + ], + "exclude-cases": [ + "9.*", + "11.*", + "12.*", + "13.*" + ], + "exclude-agent-cases": {} +} \ No newline at end of file diff --git a/v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server_wss/Dockerfile b/v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server_wss/Dockerfile new file mode 100644 index 0000000..67114c4 --- /dev/null +++ b/v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server_wss/Dockerfile @@ -0,0 +1,9 @@ +FROM crossbario/autobahn-testsuite +COPY check_results.py /check_results.py +RUN chmod +x /check_results.py + +COPY config /config +RUN chmod +rx /config/server.crt +RUN chmod +rx /config/server.key + +EXPOSE 9002 9002 \ No newline at end of file diff --git a/v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server_wss/check_results.py b/v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server_wss/check_results.py new file mode 100644 index 0000000..d75904c --- /dev/null +++ b/v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server_wss/check_results.py @@ -0,0 +1,35 @@ +import json + +nr_of_client_errs = 0 +nr_of_client_tests = 0 + +nr_of_server_errs = 0 +nr_of_server_tests = 0 + +with open("/reports/clients/index.json") as f: + data = json.load(f) + + for i in data["v-client"]: + # Count errors + if ( + data["v-client"][i]["behavior"] == "FAILED" + or data["v-client"][i]["behaviorClose"] == "FAILED" + ): + nr_of_client_errs = nr_of_client_errs + 1 + + nr_of_client_tests = nr_of_client_tests + 1 + + +if nr_of_client_errs > 0 or nr_of_server_errs > 0: + print( + "FAILED AUTOBAHN TESTS, CLIENT ERRORS {0}(of {1}), SERVER ERRORS {2}(of {3})".format( + nr_of_client_errs, nr_of_client_tests, nr_of_server_errs, nr_of_server_tests + ) + ) + exit(1) + +print( + "TEST SUCCESS!, CLIENT TESTS({0}), SERVER TESTS ({1})".format( + nr_of_client_tests, nr_of_server_tests + ) +) diff --git a/v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server_wss/config/fuzzingserver.json b/v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server_wss/config/fuzzingserver.json new file mode 100644 index 0000000..494dfff --- /dev/null +++ b/v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server_wss/config/fuzzingserver.json @@ -0,0 +1,16 @@ +{ + "url": "wss://127.0.0.1:9002", + "outdir": "./reports/clients", + "key": "/config/server.key", + "cert": "/config/server.crt", + "cases": [ + "*" + ], + "exclude-cases": [ + "9.*", + "11.*", + "12.*", + "13.*" + ], + "exclude-agent-cases": {} +} \ No newline at end of file diff --git a/v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server_wss/config/server.crt b/v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server_wss/config/server.crt new file mode 100644 index 0000000..d4071d1 --- /dev/null +++ b/v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server_wss/config/server.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDETCCAfkCFAtFKlcdB3jhD+AXPul81dwmZcs/MA0GCSqGSIb3DQEBCwUAMEUx +CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl +cm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMjAxMTIxMDgyNjQ5WhcNMzAxMTE5MDgy +NjQ5WjBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UE +CgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAnbysLfcIr9+wpoJjb5r728j2e07agedOzh8VLuGnHqmKOUPN +f8Ik707kEoBcFY7UM2A9G/1RMIysGp8eleQLMtNdeYc3KlKHBGFrOM3i4gCd7G44 +lERuKP1PKzRQ6RdVNUXn51XjfxjHWo7kHCEVvZowxvzxLxhwbSwmEmgzcQ1T6vj6 +Cdop87sdq00F+eOCfTdy+cl+R65sbImVdfY4EQ0QWAVdF3X6njLjpdmteppggbEa +ECv3R3qNIV7/rflIPm1efbqp7R1ugvjLPJZ1u12ovtqkgsWbnEyzST8hbEEjsOTJ +/cPkH2DaLdh7fMgfcVmqnYXd9T+gpsNGv98DjwIDAQABMA0GCSqGSIb3DQEBCwUA +A4IBAQBG9GxUOjcrFd1ept9AOTzbxvIUvBiqIEzrL2/+3T1yPPAWQzOmBfZhIVVm +EZeeU3xcvd7+AmX+2FPCAD+evjSHjKY048X1YksQS7mYChSgeJiknoJi3mAEAyw6 +oYGVkETksZLQfXtWTjgljbIQrwTA1s+EW0jvmvaJnWD3/8nFqmfly2/kxVsTcGEa +wJGEUS53Cq6y6lLZ+ojjjj1iVCQ94U6L/0xPB9hgXOyL2+iQj+n38ruatnUNF77C +UKS7N9BFF42eqVY83Xab0m25s93m8Z7J/63qu0eeA8p5t7+8lbGvOYpwReknLRMf +pJfgSEWqWfSaetihbJl2Fmzg2SeJ +-----END CERTIFICATE----- diff --git a/v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server_wss/config/server.csr b/v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server_wss/config/server.csr new file mode 100644 index 0000000..6013ea9 --- /dev/null +++ b/v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server_wss/config/server.csr @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE REQUEST----- +MIICijCCAXICAQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx +ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBAJ28rC33CK/fsKaCY2+a+9vI9ntO2oHnTs4fFS7h +px6pijlDzX/CJO9O5BKAXBWO1DNgPRv9UTCMrBqfHpXkCzLTXXmHNypShwRhazjN +4uIAnexuOJREbij9Tys0UOkXVTVF5+dV438Yx1qO5BwhFb2aMMb88S8YcG0sJhJo +M3ENU+r4+gnaKfO7HatNBfnjgn03cvnJfkeubGyJlXX2OBENEFgFXRd1+p4y46XZ +rXqaYIGxGhAr90d6jSFe/635SD5tXn26qe0dboL4yzyWdbtdqL7apILFm5xMs0k/ +IWxBI7Dkyf3D5B9g2i3Ye3zIH3FZqp2F3fU/oKbDRr/fA48CAwEAAaAAMA0GCSqG +SIb3DQEBCwUAA4IBAQARfNhaiioyJPZZ8Hkf9UPbi85djYLDYCC9EqBPHpYpGh15 +WdRsTModg/X5DeGwtWwRyGSP2ROMWa1NB5RHZ9buIgCIOeszhAvXVaQMlHmpNhSD +/hWKGGpAEq12TKHxgi9eTOE2u9MhoJf1G6iGffVsHc8r52THvGqKBp3Bi8G1Pl6L +2J1f5qX42K1DEnCx0gGnQkydO6E4UnMbsaDSFSODQwg5LpzSYoYUfpYHstMpqAqL +rcEt869YKjemKuTCzHODWxfqlvVr9GctNjKG2WtoqnX+10x3tw/9lsNRKUelCQxb +E56eujAoQdMxQ4OjwSnc/gbpWa5gXKYjpgAfx2kY +-----END CERTIFICATE REQUEST----- diff --git a/v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server_wss/config/server.key b/v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server_wss/config/server.key new file mode 100644 index 0000000..05c9d77 --- /dev/null +++ b/v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server_wss/config/server.key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEAnbysLfcIr9+wpoJjb5r728j2e07agedOzh8VLuGnHqmKOUPN +f8Ik707kEoBcFY7UM2A9G/1RMIysGp8eleQLMtNdeYc3KlKHBGFrOM3i4gCd7G44 +lERuKP1PKzRQ6RdVNUXn51XjfxjHWo7kHCEVvZowxvzxLxhwbSwmEmgzcQ1T6vj6 +Cdop87sdq00F+eOCfTdy+cl+R65sbImVdfY4EQ0QWAVdF3X6njLjpdmteppggbEa +ECv3R3qNIV7/rflIPm1efbqp7R1ugvjLPJZ1u12ovtqkgsWbnEyzST8hbEEjsOTJ +/cPkH2DaLdh7fMgfcVmqnYXd9T+gpsNGv98DjwIDAQABAoIBAE+IFfiHGiYzT0pl +a+WV62+CAGVj+OCO1Dkxiui8dhsLuNnuyeqk5SKUUILTnZpxDaVp3OYD76/e/dfe +avmApfTWhccE2lfIjLM0u29EwCTb0sSnPnfjmPep4QUTt8gPL7NQsAEAWVh4Eewj +J/jW5bNXz0hFuQXZ+LXTEM8vIuDY4M0RX/jhEcCVr3QH8Sp/6JEeRY2Mbn5Z6LZ+ +BVuu8e4sCpamWOOWfoIQq3e3TbATFSNP9vzPLKvxwwAw9g5dAKPn3dvem8ofzaaF +MeJ6T485mnx0naBrI+1qHLb3QcRpSZp6uEOp/4uvkCFm9S3dBGIwOGwHcybWFfFr +StPfccECgYEAzN2f1BcvL3rt4970lG/MGNeLMpF7h7aWca0DzUNY5sCh+kvENHrD +U4nH5EHoqxB1c036LKBhsrrrk5F/eQ8M+QEqpKUfqAYUrfy+HRAAeTYbhLkCysrL ++X/mlqYeyzMHj4Pjy5rqoy2TnJFnfIZYwYOL/OfA9IPwGpW2rxVSk1cCgYEAxRul +9j0Ii3Te08TprfriDpAFQyLH54vqVwe8mkox3cdOyYvUNHdEmDNh3/7dadxVKsIx +gIkPdGcizOw4elLKWnNFQN3+dCc3LN/zhsop0a6Ow2IatWQ8qOSqNYtD2DGj0w3j +cJ/BZfacpr/OkAv0kjanYw4+ZSIH/r3Vjdli5okCgYBXltni4Ba4giJ7rrN7U2E7 +rcxBzpm2KIaiC4r4k7bK0clvLj2xAlvIt7vTB6rmmJ7esZQoyFl9BRX7fdW2eIzf +WXRV+JNUT2VADjNqUZEiQdP6Ju/erF4RSnHYLyYzUpoE7irSvmVbZv0Zj8FjKD2C +Xy/W7W8+G7roYuI8cS1g+QKBgQCDoHwK3SU4o9ouB0CZ64FMgkbRV4exi9D5P3Rm +gIeed/uYQiV6x+7pyN5ijDtl9zp0rGwMTvsgG8O0n0b0AReaoYGs2NKU1J9W+1MQ +Py8AFJbHyVrWqVKM4u77hL3QwQ2K4qpwym6HXdGs1UfnD+TKQ28yig+Gz9wQ9MqI +yJPwKQKBgQCmZxhmX1SUe3DVnVulMHDLUldbRbFns0VZLiSDhY+hjOAEmnvEdEHp +6L8/gvdTqUPF/VZQSQiZlii1oTIapQClI2oLfHcGytSorB+bpL7PxAKABp0pA6BS +JkXzEiV1h5anbxiwid5ZICt6QGQvGvBF7b1VSb+8p9WglLBWZo36pw== +-----END RSA PRIVATE KEY----- diff --git a/v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server_wss/config/server.pem b/v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server_wss/config/server.pem new file mode 100644 index 0000000..d4071d1 --- /dev/null +++ b/v_windows/v/old/vlib/net/websocket/tests/autobahn/fuzzing_server_wss/config/server.pem @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDETCCAfkCFAtFKlcdB3jhD+AXPul81dwmZcs/MA0GCSqGSIb3DQEBCwUAMEUx +CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl +cm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMjAxMTIxMDgyNjQ5WhcNMzAxMTE5MDgy +NjQ5WjBFMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UE +CgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAnbysLfcIr9+wpoJjb5r728j2e07agedOzh8VLuGnHqmKOUPN +f8Ik707kEoBcFY7UM2A9G/1RMIysGp8eleQLMtNdeYc3KlKHBGFrOM3i4gCd7G44 +lERuKP1PKzRQ6RdVNUXn51XjfxjHWo7kHCEVvZowxvzxLxhwbSwmEmgzcQ1T6vj6 +Cdop87sdq00F+eOCfTdy+cl+R65sbImVdfY4EQ0QWAVdF3X6njLjpdmteppggbEa +ECv3R3qNIV7/rflIPm1efbqp7R1ugvjLPJZ1u12ovtqkgsWbnEyzST8hbEEjsOTJ +/cPkH2DaLdh7fMgfcVmqnYXd9T+gpsNGv98DjwIDAQABMA0GCSqGSIb3DQEBCwUA +A4IBAQBG9GxUOjcrFd1ept9AOTzbxvIUvBiqIEzrL2/+3T1yPPAWQzOmBfZhIVVm +EZeeU3xcvd7+AmX+2FPCAD+evjSHjKY048X1YksQS7mYChSgeJiknoJi3mAEAyw6 +oYGVkETksZLQfXtWTjgljbIQrwTA1s+EW0jvmvaJnWD3/8nFqmfly2/kxVsTcGEa +wJGEUS53Cq6y6lLZ+ojjjj1iVCQ94U6L/0xPB9hgXOyL2+iQj+n38ruatnUNF77C +UKS7N9BFF42eqVY83Xab0m25s93m8Z7J/63qu0eeA8p5t7+8lbGvOYpwReknLRMf +pJfgSEWqWfSaetihbJl2Fmzg2SeJ +-----END CERTIFICATE----- diff --git a/v_windows/v/old/vlib/net/websocket/tests/autobahn/local_run/Dockerfile b/v_windows/v/old/vlib/net/websocket/tests/autobahn/local_run/Dockerfile new file mode 100644 index 0000000..ee39644 --- /dev/null +++ b/v_windows/v/old/vlib/net/websocket/tests/autobahn/local_run/Dockerfile @@ -0,0 +1,12 @@ +# Use this as docker builder with https://github.com/nektos/act +# build with: docker build tests/autobahn/. -t myimage +# use in act: act -P ubuntu-latest=myimage + +FROM node:12.6-buster-slim + +COPY config/fuzzingserver.json /config/fuzzingserver.json +RUN chmod +775 /config/fuzzingserver.json +RUN apt-get update && \ + apt-get install -y \ + docker \ + docker-compose \ No newline at end of file diff --git a/v_windows/v/old/vlib/net/websocket/tests/autobahn/local_run/autobahn_client.v b/v_windows/v/old/vlib/net/websocket/tests/autobahn/local_run/autobahn_client.v new file mode 100644 index 0000000..ef5b281 --- /dev/null +++ b/v_windows/v/old/vlib/net/websocket/tests/autobahn/local_run/autobahn_client.v @@ -0,0 +1,33 @@ +// use this test to test the websocket client in the autobahn test +module main + +import net.websocket + +fn main() { + for i in 1 .. 304 { + println('\ncase: $i') + handle_case(i) or { println('error should be ok: $err') } + } + // update the reports + uri := 'ws://localhost:9001/updateReports?agent=v-client' + mut ws := websocket.new_client(uri) ? + ws.connect() ? + ws.listen() ? +} + +fn handle_case(case_nr int) ? { + uri := 'ws://localhost:9001/runCase?case=$case_nr&agent=v-client' + mut ws := websocket.new_client(uri) ? + ws.on_message(on_message) + ws.connect() ? + ws.listen() ? +} + +fn on_message(mut ws websocket.Client, msg &websocket.Message) ? { + // autobahn tests expects to send same message back + if msg.opcode == .pong { + // We just wanna pass text and binary message back to autobahn + return + } + ws.write(msg.payload, msg.opcode) or { panic(err) } +} diff --git a/v_windows/v/old/vlib/net/websocket/tests/autobahn/local_run/autobahn_client_wss.v b/v_windows/v/old/vlib/net/websocket/tests/autobahn/local_run/autobahn_client_wss.v new file mode 100644 index 0000000..c7a3c25 --- /dev/null +++ b/v_windows/v/old/vlib/net/websocket/tests/autobahn/local_run/autobahn_client_wss.v @@ -0,0 +1,35 @@ +// use this test to test the websocket client in the autobahn test +module main + +import net.websocket + +fn main() { + for i in 1 .. 304 { + println('\ncase: $i') + handle_case(i) or { println('error should be ok: $err') } + } + // update the reports + // uri := 'wss://localhost:9002/updateReports?agent=v-client' + uri := 'wss://autobahn_server_wss:9002/updateReports?agent=v-client' + mut ws := websocket.new_client(uri) ? + ws.connect() ? + ws.listen() ? +} + +fn handle_case(case_nr int) ? { + uri := 'wss://autobahn_server_wss:9002/runCase?case=$case_nr&agent=v-client' + // uri := 'wss://localhost:9002/runCase?case=$case_nr&agent=v-client' + mut ws := websocket.new_client(uri) ? + ws.on_message(on_message) + ws.connect() ? + ws.listen() ? +} + +fn on_message(mut ws websocket.Client, msg &websocket.Message) ? { + // autobahn tests expects to send same message back + if msg.opcode == .pong { + // We just wanna pass text and binary message back to autobahn + return + } + ws.write(msg.payload, msg.opcode) or { panic(err) } +} diff --git a/v_windows/v/old/vlib/net/websocket/tests/autobahn/ws_test/Dockerfile b/v_windows/v/old/vlib/net/websocket/tests/autobahn/ws_test/Dockerfile new file mode 100644 index 0000000..b57cffd --- /dev/null +++ b/v_windows/v/old/vlib/net/websocket/tests/autobahn/ws_test/Dockerfile @@ -0,0 +1,12 @@ +FROM thevlang/vlang:buster-build + + +COPY ./ /src/ + +WORKDIR /src + +RUN make CC=clang + +RUN /src/v /src/vlib/net/websocket/tests/autobahn/autobahn_server.v +RUN chmod +x /src/vlib/net/websocket/tests/autobahn/autobahn_server +ENTRYPOINT [ "/src/vlib/net/websocket/tests/autobahn/autobahn_server" ] diff --git a/v_windows/v/old/vlib/net/websocket/uri.v b/v_windows/v/old/vlib/net/websocket/uri.v new file mode 100644 index 0000000..7d388e1 --- /dev/null +++ b/v_windows/v/old/vlib/net/websocket/uri.v @@ -0,0 +1,16 @@ +module websocket + +// Uri represents an Uri for websocket connections +struct Uri { +mut: + url string // url to the websocket endpoint + hostname string // hostname of the websocket endpoint + port string // port of the websocket endpoint + resource string // resource of the websocket endpoint + querystring string // query string of the websocket endpoint +} + +// str returns the string representation of the Uri +pub fn (u Uri) str() string { + return u.url +} diff --git a/v_windows/v/old/vlib/net/websocket/utils.v b/v_windows/v/old/vlib/net/websocket/utils.v new file mode 100644 index 0000000..4e48359 --- /dev/null +++ b/v_windows/v/old/vlib/net/websocket/utils.v @@ -0,0 +1,54 @@ +module websocket + +import rand +import crypto.sha1 +import encoding.base64 + +// htonl64 converts payload length to header bits +fn htonl64(payload_len u64) []byte { + mut ret := []byte{len: 8} + ret[0] = byte(((payload_len & (u64(0xff) << 56)) >> 56) & 0xff) + ret[1] = byte(((payload_len & (u64(0xff) << 48)) >> 48) & 0xff) + ret[2] = byte(((payload_len & (u64(0xff) << 40)) >> 40) & 0xff) + ret[3] = byte(((payload_len & (u64(0xff) << 32)) >> 32) & 0xff) + ret[4] = byte(((payload_len & (u64(0xff) << 24)) >> 24) & 0xff) + ret[5] = byte(((payload_len & (u64(0xff) << 16)) >> 16) & 0xff) + ret[6] = byte(((payload_len & (u64(0xff) << 8)) >> 8) & 0xff) + ret[7] = byte(((payload_len & (u64(0xff) << 0)) >> 0) & 0xff) + return ret +} + +// create_masking_key returs a new masking key to use when masking websocket messages +fn create_masking_key() []byte { + mask_bit := byte(rand.intn(255)) + buf := []byte{len: 4, init: `0`} + unsafe { C.memcpy(buf.data, &mask_bit, 4) } + return buf +} + +// create_key_challenge_response creates a key challange response from security key +fn create_key_challenge_response(seckey string) ?string { + if seckey.len == 0 { + return error('unexpected seckey lengt zero') + } + guid := '258EAFA5-E914-47DA-95CA-C5AB0DC85B11' + sha1buf := seckey + guid + shabytes := sha1buf.bytes() + hash := sha1.sum(shabytes) + b64 := base64.encode(hash) + unsafe { + hash.free() + shabytes.free() + } + return b64 +} + +// get_nonce creates a randomized array used in handshake process +fn get_nonce(nonce_size int) string { + mut nonce := []byte{len: nonce_size, cap: nonce_size} + alphanum := '0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz' + for i in 0 .. nonce_size { + nonce[i] = alphanum[rand.intn(alphanum.len)] + } + return unsafe { tos(nonce.data, nonce.len) }.clone() +} diff --git a/v_windows/v/old/vlib/net/websocket/websocket_client.v b/v_windows/v/old/vlib/net/websocket/websocket_client.v new file mode 100644 index 0000000..4408c7e --- /dev/null +++ b/v_windows/v/old/vlib/net/websocket/websocket_client.v @@ -0,0 +1,488 @@ +// websocket module implements websocket client and a websocket server +// attribution: @thecoderr the author of original websocket client +[manualfree] +module websocket + +import net +import net.http +import net.openssl +import net.urllib +import time +import log +import rand + +const ( + empty_bytearr = []byte{} // used as empty response to avoid allocation +) + +// Client represents websocket client +pub struct Client { + is_server bool +mut: + ssl_conn &openssl.SSLConn // secure connection used when wss is used + flags []Flag // flags used in handshake + fragments []Fragment // current fragments + message_callbacks []MessageEventHandler // all callbacks on_message + error_callbacks []ErrorEventHandler // all callbacks on_error + open_callbacks []OpenEventHandler // all callbacks on_open + close_callbacks []CloseEventHandler // all callbacks on_close +pub: + is_ssl bool // true if secure socket is used + uri Uri // uri of current connection + id string // unique id of client +pub mut: + header http.Header // headers that will be passed when connecting + conn &net.TcpConn // underlying TCP socket connection + nonce_size int = 16 // size of nounce used for masking + panic_on_callback bool // set to true of callbacks can panic + state State // current state of connection + logger &log.Log // logger used to log messages + resource_name string // name of current resource + last_pong_ut u64 // last time in unix time we got a pong message +} + +// Flag represents different types of headers in websocket handshake +enum Flag { + has_accept // Webs + has_connection + has_upgrade +} + +// State represents the state of the websocket connection. +pub enum State { + connecting = 0 + open + closing + closed +} + +// Message represents a whole message combined from 1 to n frames +pub struct Message { +pub: + opcode OPCode // websocket frame type of this message + payload []byte // payload of the message +} + +// OPCode represents the supported websocket frame types +pub enum OPCode { + continuation = 0x00 + text_frame = 0x01 + binary_frame = 0x02 + close = 0x08 + ping = 0x09 + pong = 0x0A +} + +// new_client instance a new websocket client +pub fn new_client(address string) ?&Client { + uri := parse_uri(address) ? + return &Client{ + conn: 0 + is_server: false + ssl_conn: openssl.new_ssl_conn() + is_ssl: address.starts_with('wss') + logger: &log.Log{ + level: .info + } + uri: uri + state: .closed + id: rand.uuid_v4() + header: http.new_header() + } +} + +// connect connects to remote websocket server +pub fn (mut ws Client) connect() ? { + ws.assert_not_connected() ? + ws.set_state(.connecting) + ws.logger.info('connecting to host $ws.uri') + ws.conn = ws.dial_socket() ? + // Todo: make setting configurable + ws.conn.set_read_timeout(time.second * 30) + ws.conn.set_write_timeout(time.second * 30) + ws.handshake() ? + ws.set_state(.open) + ws.logger.info('successfully connected to host $ws.uri') + ws.send_open_event() +} + +// listen listens and processes incoming messages +pub fn (mut ws Client) listen() ? { + mut log := 'Starting client listener, server($ws.is_server)...' + ws.logger.info(log) + unsafe { log.free() } + defer { + ws.logger.info('Quit client listener, server($ws.is_server)...') + if ws.state == .open { + ws.close(1000, 'closed by client') or {} + } + } + for ws.state == .open { + msg := ws.read_next_message() or { + if ws.state in [.closed, .closing] { + return + } + ws.debug_log('failed to read next message: $err') + ws.send_error_event('failed to read next message: $err') + return err + } + if ws.state in [.closed, .closing] { + return + } + ws.debug_log('got message: $msg.opcode') + match msg.opcode { + .text_frame { + log = 'read: text' + ws.debug_log(log) + unsafe { log.free() } + ws.send_message_event(msg) + unsafe { msg.free() } + } + .binary_frame { + ws.debug_log('read: binary') + ws.send_message_event(msg) + unsafe { msg.free() } + } + .ping { + ws.debug_log('read: ping, sending pong') + ws.send_control_frame(.pong, 'PONG', msg.payload) or { + ws.logger.error('error in message callback sending PONG: $err') + ws.send_error_event('error in message callback sending PONG: $err') + if ws.panic_on_callback { + panic(err) + } + continue + } + if msg.payload.len > 0 { + unsafe { msg.free() } + } + } + .pong { + ws.debug_log('read: pong') + ws.last_pong_ut = time.now().unix + ws.send_message_event(msg) + if msg.payload.len > 0 { + unsafe { msg.free() } + } + } + .close { + log = 'read: close' + ws.debug_log(log) + unsafe { log.free() } + defer { + ws.manage_clean_close() + } + if msg.payload.len > 0 { + if msg.payload.len == 1 { + ws.close(1002, 'close payload cannot be 1 byte') ? + return error('close payload cannot be 1 byte') + } + code := (int(msg.payload[0]) << 8) + int(msg.payload[1]) + if code in invalid_close_codes { + ws.close(1002, 'invalid close code: $code') ? + return error('invalid close code: $code') + } + reason := if msg.payload.len > 2 { msg.payload[2..] } else { []byte{} } + if reason.len > 0 { + ws.validate_utf_8(.close, reason) ? + } + if ws.state !in [.closing, .closed] { + // sending close back according to spec + ws.debug_log('close with reason, code: $code, reason: $reason') + r := reason.bytestr() + ws.close(code, r) ? + } + unsafe { msg.free() } + } else { + if ws.state !in [.closing, .closed] { + ws.debug_log('close with reason, no code') + // sending close back according to spec + ws.close(1000, 'normal') ? + } + unsafe { msg.free() } + } + return + } + .continuation { + ws.logger.error('unexpected opcode continuation, nothing to continue') + ws.send_error_event('unexpected opcode continuation, nothing to continue') + ws.close(1002, 'nothing to continue') ? + return error('unexpected opcode continuation, nothing to continue') + } + } + } +} + +// manage_clean_close closes connection in a clean websocket way +fn (mut ws Client) manage_clean_close() { + ws.send_close_event(1000, 'closed by client') +} + +// ping sends ping message to server +pub fn (mut ws Client) ping() ? { + ws.send_control_frame(.ping, 'PING', []) ? +} + +// pong sends pong message to server, +pub fn (mut ws Client) pong() ? { + ws.send_control_frame(.pong, 'PONG', []) ? +} + +// write_ptr writes len bytes provided a byteptr with a websocket messagetype +pub fn (mut ws Client) write_ptr(bytes &byte, payload_len int, code OPCode) ?int { + // ws.debug_log('write_ptr code: $code') + if ws.state != .open || ws.conn.sock.handle < 1 { + // todo: send error here later + return error('trying to write on a closed socket!') + } + mut header_len := 2 + if payload_len > 125 { 2 } else { 0 } + + if payload_len > 0xffff { 6 } else { 0 } + if !ws.is_server { + header_len += 4 + } + mut header := []byte{len: header_len, init: `0`} // [`0`].repeat(header_len) + header[0] = byte(int(code)) | 0x80 + masking_key := create_masking_key() + if ws.is_server { + if payload_len <= 125 { + header[1] = byte(payload_len) + } else if payload_len > 125 && payload_len <= 0xffff { + len16 := C.htons(payload_len) + header[1] = 126 + unsafe { C.memcpy(&header[2], &len16, 2) } + } else if payload_len > 0xffff && payload_len <= 0x7fffffff { + len_bytes := htonl64(u64(payload_len)) + header[1] = 127 + unsafe { C.memcpy(&header[2], len_bytes.data, 8) } + } + } else { + if payload_len <= 125 { + header[1] = byte(payload_len | 0x80) + header[2] = masking_key[0] + header[3] = masking_key[1] + header[4] = masking_key[2] + header[5] = masking_key[3] + } else if payload_len > 125 && payload_len <= 0xffff { + len16 := C.htons(payload_len) + header[1] = (126 | 0x80) + unsafe { C.memcpy(&header[2], &len16, 2) } + header[4] = masking_key[0] + header[5] = masking_key[1] + header[6] = masking_key[2] + header[7] = masking_key[3] + } else if payload_len > 0xffff && payload_len <= 0x7fffffff { + len64 := htonl64(u64(payload_len)) + header[1] = (127 | 0x80) + unsafe { C.memcpy(&header[2], len64.data, 8) } + header[10] = masking_key[0] + header[11] = masking_key[1] + header[12] = masking_key[2] + header[13] = masking_key[3] + } else { + ws.close(1009, 'frame too large') ? + return error('frame too large') + } + } + len := header.len + payload_len + mut frame_buf := []byte{len: len} + unsafe { + C.memcpy(&frame_buf[0], &byte(header.data), header.len) + if payload_len > 0 { + C.memcpy(&frame_buf[header.len], bytes, payload_len) + } + } + if !ws.is_server { + for i in 0 .. payload_len { + frame_buf[header_len + i] ^= masking_key[i % 4] & 0xff + } + } + written_len := ws.socket_write(frame_buf) ? + unsafe { + frame_buf.free() + masking_key.free() + header.free() + } + return written_len +} + +// write writes a byte array with a websocket messagetype to socket +pub fn (mut ws Client) write(bytes []byte, code OPCode) ?int { + return ws.write_ptr(&byte(bytes.data), bytes.len, code) +} + +// write_str, writes a string with a websocket texttype to socket +pub fn (mut ws Client) write_string(str string) ?int { + return ws.write_ptr(str.str, str.len, .text_frame) +} + +// close closes the websocket connection +pub fn (mut ws Client) close(code int, message string) ? { + ws.debug_log('sending close, $code, $message') + if ws.state in [.closed, .closing] || ws.conn.sock.handle <= 1 { + ws.debug_log('close: Websocket allready closed ($ws.state), $message, $code handle($ws.conn.sock.handle)') + err_msg := 'Socket allready closed: $code' + return error(err_msg) + } + defer { + ws.shutdown_socket() or {} + ws.reset_state() + } + ws.set_state(.closing) + // mut code32 := 0 + if code > 0 { + code_ := C.htons(code) + message_len := message.len + 2 + mut close_frame := []byte{len: message_len} + close_frame[0] = byte(code_ & 0xFF) + close_frame[1] = byte(code_ >> 8) + // code32 = (close_frame[0] << 8) + close_frame[1] + for i in 0 .. message.len { + close_frame[i + 2] = message[i] + } + ws.send_control_frame(.close, 'CLOSE', close_frame) ? + unsafe { close_frame.free() } + } else { + ws.send_control_frame(.close, 'CLOSE', []) ? + } + ws.fragments = [] +} + +// send_control_frame sends a control frame to the server +fn (mut ws Client) send_control_frame(code OPCode, frame_typ string, payload []byte) ? { + ws.debug_log('send control frame $code, frame_type: $frame_typ') + if ws.state !in [.open, .closing] && ws.conn.sock.handle > 1 { + return error('socket is not connected') + } + header_len := if ws.is_server { 2 } else { 6 } + frame_len := header_len + payload.len + mut control_frame := []byte{len: frame_len} + mut masking_key := if !ws.is_server { create_masking_key() } else { websocket.empty_bytearr } + defer { + unsafe { + control_frame.free() + if masking_key.len > 0 { + masking_key.free() + } + } + } + control_frame[0] = byte(int(code) | 0x80) + if !ws.is_server { + control_frame[1] = byte(payload.len | 0x80) + control_frame[2] = masking_key[0] + control_frame[3] = masking_key[1] + control_frame[4] = masking_key[2] + control_frame[5] = masking_key[3] + } else { + control_frame[1] = byte(payload.len) + } + if code == .close { + if payload.len >= 2 { + if !ws.is_server { + mut parsed_payload := []byte{len: payload.len + 1} + unsafe { C.memcpy(parsed_payload.data, &payload[0], payload.len) } + parsed_payload[payload.len] = `\0` + for i in 0 .. payload.len { + control_frame[6 + i] = (parsed_payload[i] ^ masking_key[i % 4]) & 0xff + } + unsafe { parsed_payload.free() } + } else { + unsafe { C.memcpy(&control_frame[2], &payload[0], payload.len) } + } + } + } else { + if !ws.is_server { + if payload.len > 0 { + for i in 0 .. payload.len { + control_frame[header_len + i] = (payload[i] ^ masking_key[i % 4]) & 0xff + } + } + } else { + if payload.len > 0 { + unsafe { C.memcpy(&control_frame[2], &payload[0], payload.len) } + } + } + } + ws.socket_write(control_frame) or { + return error('send_control_frame: error sending $frame_typ control frame.') + } +} + +// parse_uri parses the url to a Uri +fn parse_uri(url string) ?&Uri { + u := urllib.parse(url) ? + request_uri := u.request_uri() + v := request_uri.split('?') + mut port := u.port() + uri := u.str() + if port == '' { + port = if uri.starts_with('ws://') { + '80' + } else if uri.starts_with('wss://') { + '443' + } else { + u.port() + } + } + querystring := if v.len > 1 { '?' + v[1] } else { '' } + return &Uri{ + url: url + hostname: u.hostname() + port: port + resource: v[0] + querystring: querystring + } +} + +// set_state sets current state of the websocket connection +fn (mut ws Client) set_state(state State) { + lock { + ws.state = state + } +} + +// assert_not_connected returns error if the connection is not connected +fn (ws Client) assert_not_connected() ? { + match ws.state { + .connecting { return error('connect: websocket is connecting') } + .open { return error('connect: websocket already open') } + .closing { return error('connect: reconnect on closing websocket not supported, please use new client') } + else {} + } +} + +// reset_state resets the websocket and initialize default settings +fn (mut ws Client) reset_state() { + lock { + ws.state = .closed + ws.ssl_conn = openssl.new_ssl_conn() + ws.flags = [] + ws.fragments = [] + } +} + +// debug_log handles debug logging output for client and server +fn (mut ws Client) debug_log(text string) { + if ws.is_server { + ws.logger.debug('server-> $text') + } else { + ws.logger.debug('client-> $text') + } +} + +// free handles manual free memory of Message struct +pub fn (m &Message) free() { + unsafe { m.payload.free() } +} + +// free handles manual free memory of Client struct +pub fn (c &Client) free() { + unsafe { + c.flags.free() + c.fragments.free() + c.message_callbacks.free() + c.error_callbacks.free() + c.open_callbacks.free() + c.close_callbacks.free() + c.header.free() + } +} diff --git a/v_windows/v/old/vlib/net/websocket/websocket_nix.c.v b/v_windows/v/old/vlib/net/websocket/websocket_nix.c.v new file mode 100644 index 0000000..f986b98 --- /dev/null +++ b/v_windows/v/old/vlib/net/websocket/websocket_nix.c.v @@ -0,0 +1,10 @@ +module websocket + +// error_code returns the error code +fn error_code() int { + return C.errno +} + +const ( + error_ewouldblock = C.EWOULDBLOCK // blocking error code +) diff --git a/v_windows/v/old/vlib/net/websocket/websocket_server.v b/v_windows/v/old/vlib/net/websocket/websocket_server.v new file mode 100644 index 0000000..99af3e0 --- /dev/null +++ b/v_windows/v/old/vlib/net/websocket/websocket_server.v @@ -0,0 +1,189 @@ +module websocket + +import net +import net.openssl +import log +import time +import rand + +// Server represents a websocket server connection +pub struct Server { +mut: + logger &log.Log // logger used to log + ls &net.TcpListener // listener used to get incoming connection to socket + accept_client_callbacks []AcceptClientFn // accept client callback functions + message_callbacks []MessageEventHandler // new message callback functions + close_callbacks []CloseEventHandler // close message callback functions +pub: + family net.AddrFamily = .ip + port int // port used as listen to incoming connections + is_ssl bool // true if secure connection (not supported yet on server) +pub mut: + clients map[string]&ServerClient // clients connected to this server + ping_interval int = 30 // interval for sending ping to clients (seconds) + state State // current state of connection +} + +// ServerClient represents a connected client +struct ServerClient { +pub: + resource_name string // resource that the client access + client_key string // unique key of client +pub mut: + server &Server + client &Client +} + +// new_server instance a new websocket server on provided port and route +pub fn new_server(family net.AddrFamily, port int, route string) &Server { + return &Server{ + ls: 0 + family: family + port: port + logger: &log.Log{ + level: .info + } + state: .closed + } +} + +// set_ping_interval sets the interval that the server will send ping messages to clients +pub fn (mut s Server) set_ping_interval(seconds int) { + s.ping_interval = seconds +} + +// listen start listen and process to incoming connections from websocket clients +pub fn (mut s Server) listen() ? { + s.logger.info('websocket server: start listen on port $s.port') + s.ls = net.listen_tcp(s.family, ':$s.port') ? + s.set_state(.open) + go s.handle_ping() + for { + mut c := s.accept_new_client() or { continue } + go s.serve_client(mut c) + } + s.logger.info('websocket server: end listen on port $s.port') +} + +// Close closes server (not implemented yet) +fn (mut s Server) close() { + // TODO: implement close when moving to net from x.net +} + +// handle_ping sends ping to all clients every set interval +fn (mut s Server) handle_ping() { + mut clients_to_remove := []string{} + for s.state == .open { + time.sleep(s.ping_interval * time.second) + for i, _ in s.clients { + mut c := s.clients[i] + if c.client.state == .open { + c.client.ping() or { + s.logger.debug('server-> error sending ping to client') + c.client.close(1002, 'Closing connection: ping send error') or { + // we want to continue even if error + continue + } + clients_to_remove << c.client.id + } + if (time.now().unix - c.client.last_pong_ut) > s.ping_interval * 2 { + clients_to_remove << c.client.id + c.client.close(1000, 'no pong received') or { continue } + } + } + } + // TODO: replace for with s.clients.delete_all(clients_to_remove) if (https://github.com/vlang/v/pull/6020) merges + for client in clients_to_remove { + lock { + s.clients.delete(client) + } + } + clients_to_remove.clear() + } +} + +// serve_client accepts incoming connection and sets up the callbacks +fn (mut s Server) serve_client(mut c Client) ? { + c.logger.debug('server-> Start serve client ($c.id)') + defer { + c.logger.debug('server-> End serve client ($c.id)') + } + mut handshake_response, mut server_client := s.handle_server_handshake(mut c) ? + accept := s.send_connect_event(mut server_client) ? + if !accept { + s.logger.debug('server-> client not accepted') + c.shutdown_socket() ? + return + } + // the client is accepted + c.socket_write(handshake_response.bytes()) ? + lock { + s.clients[server_client.client.id] = server_client + } + s.setup_callbacks(mut server_client) + c.listen() or { + s.logger.error(err.msg) + return err + } +} + +// setup_callbacks initialize all callback functions +fn (mut s Server) setup_callbacks(mut sc ServerClient) { + if s.message_callbacks.len > 0 { + for cb in s.message_callbacks { + if cb.is_ref { + sc.client.on_message_ref(cb.handler2, cb.ref) + } else { + sc.client.on_message(cb.handler) + } + } + } + if s.close_callbacks.len > 0 { + for cb in s.close_callbacks { + if cb.is_ref { + sc.client.on_close_ref(cb.handler2, cb.ref) + } else { + sc.client.on_close(cb.handler) + } + } + } + // set standard close so we can remove client if closed + sc.client.on_close_ref(fn (mut c Client, code int, reason string, mut sc ServerClient) ? { + c.logger.debug('server-> Delete client') + lock { + sc.server.clients.delete(sc.client.id) + } + }, sc) +} + +// accept_new_client creates a new client instance for client that connects to the socket +fn (mut s Server) accept_new_client() ?&Client { + mut new_conn := s.ls.accept() ? + c := &Client{ + is_server: true + conn: new_conn + ssl_conn: openssl.new_ssl_conn() + logger: s.logger + state: .open + last_pong_ut: time.now().unix + id: rand.uuid_v4() + } + return c +} + +// set_state sets current state in a thread safe way +fn (mut s Server) set_state(state State) { + lock { + s.state = state + } +} + +// free manages manual free of memory for Server instance +pub fn (mut s Server) free() { + unsafe { + s.clients.free() + s.accept_client_callbacks.free() + s.message_callbacks.free() + s.close_callbacks.free() + } +} diff --git a/v_windows/v/old/vlib/net/websocket/websocket_test.v b/v_windows/v/old/vlib/net/websocket/websocket_test.v new file mode 100644 index 0000000..35e15d3 --- /dev/null +++ b/v_windows/v/old/vlib/net/websocket/websocket_test.v @@ -0,0 +1,122 @@ +import os +import net +import net.websocket +import time +import rand + +// TODO: fix connecting to ipv4 websockets +// (the server seems to work with .ip, but +// Client can not connect, it needs to be passed +// .ip too?) + +struct WebsocketTestResults { +pub mut: + nr_messages int + nr_pong_received int +} + +// Do not run these tests everytime, since they are flaky. +// They have their own specialized CI runner. +const github_job = os.getenv('GITHUB_JOB') + +const should_skip = github_job != '' && github_job != 'websocket_tests' + +// tests with internal ws servers +fn test_ws_ipv6() { + if should_skip { + return + } + port := 30000 + rand.intn(1024) + go start_server(.ip6, port) + time.sleep(500 * time.millisecond) + ws_test(.ip6, 'ws://localhost:$port') or { assert false } +} + +// tests with internal ws servers +fn test_ws_ipv4() { + // TODO: fix client + if true || should_skip { + return + } + port := 30000 + rand.intn(1024) + go start_server(.ip, port) + time.sleep(500 * time.millisecond) + ws_test(.ip, 'ws://localhost:$port') or { assert false } +} + +fn start_server(family net.AddrFamily, listen_port int) ? { + mut s := websocket.new_server(family, listen_port, '') + // make that in execution test time give time to execute at least one time + s.ping_interval = 1 + + s.on_connect(fn (mut s websocket.ServerClient) ?bool { + // here you can look att the client info and accept or not accept + // just returning a true/false + if s.resource_name != '/' { + panic('unexpected resource name in test') + return false + } + return true + }) ? + s.on_message(fn (mut ws websocket.Client, msg &websocket.Message) ? { + match msg.opcode { + .pong { ws.write_string('pong') or { panic(err) } } + else { ws.write(msg.payload, msg.opcode) or { panic(err) } } + } + }) + + s.on_close(fn (mut ws websocket.Client, code int, reason string) ? { + // not used + }) + s.listen() or {} +} + +// ws_test tests connect to the websocket server from websocket client +fn ws_test(family net.AddrFamily, uri string) ? { + eprintln('connecting to $uri ...') + + mut test_results := WebsocketTestResults{} + mut ws := websocket.new_client(uri) ? + ws.on_open(fn (mut ws websocket.Client) ? { + ws.pong() ? + assert true + }) + ws.on_error(fn (mut ws websocket.Client, err string) ? { + println('error: $err') + // this can be thrown by internet connection problems + assert false + }) + + ws.on_message_ref(fn (mut ws websocket.Client, msg &websocket.Message, mut res WebsocketTestResults) ? { + println('client got type: $msg.opcode payload:\n$msg.payload') + if msg.opcode == .text_frame { + smessage := msg.payload.bytestr() + match smessage { + 'pong' { + res.nr_pong_received++ + } + 'a' { + res.nr_messages++ + } + else { + assert false + } + } + } else { + println('Binary message: $msg') + } + }, test_results) + ws.connect() or { panic('fail to connect') } + go ws.listen() + text := ['a'].repeat(2) + for msg in text { + ws.write(msg.bytes(), .text_frame) or { panic('fail to write to websocket') } + // sleep to give time to recieve response before send a new one + time.sleep(100 * time.millisecond) + } + // sleep to give time to recieve response before asserts + time.sleep(1500 * time.millisecond) + // We expect at least 2 pongs, one sent directly and one indirectly + assert test_results.nr_pong_received >= 2 + assert test_results.nr_messages == 2 +} diff --git a/v_windows/v/old/vlib/net/websocket/websocket_windows.c.v b/v_windows/v/old/vlib/net/websocket/websocket_windows.c.v new file mode 100644 index 0000000..e9f4fc3 --- /dev/null +++ b/v_windows/v/old/vlib/net/websocket/websocket_windows.c.v @@ -0,0 +1,12 @@ +module websocket + +import net + +// error_code returns the error code +fn error_code() int { + return C.WSAGetLastError() +} + +const ( + error_ewouldblock = net.WsaError.wsaewouldblock // blocking error code +) diff --git a/v_windows/v/old/vlib/orm/README.md b/v_windows/v/old/vlib/orm/README.md new file mode 100644 index 0000000..5cfa1fd --- /dev/null +++ b/v_windows/v/old/vlib/orm/README.md @@ -0,0 +1,85 @@ +# ORM + +## Attributes + +### Structs + +- `[table: 'name']` sets a custom table name + +### Fields + +- `[primary]` sets the field as the primary key +- `[unique]` sets the field as unique +- `[unique: 'foo']` adds the field to a unique group +- `[skip]` field will be skipped +- `[sql: type]` sets the type which is used in sql (special type `serial`) +- `[sql: 'name']` sets a custom column name for the field + +## Usage + +```v ignore +struct Foo { + id int [primary; sql: serial] + name string [nonull] +} +``` + +### Create + +```v ignore +sql db { + create table Foo +} +``` + +### Drop + +```v ignore +sql db { + drop table Foo +} +``` + +### Insert + +```v ignore +var := Foo{ + name: 'abc' +} + +sql db { + insert var into Foo +} +``` + +### Update + +```v ignore +sql db { + update Foo set name = 'cde' where name == 'abc' +} +``` + +### Delete +```v ignore +sql db { + delete from Foo where id > 10 +} +``` + +### Select +```v ignore +result := sql db { + select from Foo where id == 1 +} +``` +```v ignore +result := sql db { + select from Foo where id > 1 limit 5 +} +``` +```v ignore +result := sql db { + select from Foo where id > 1 order by id +} +``` diff --git a/v_windows/v/old/vlib/orm/orm.v b/v_windows/v/old/vlib/orm/orm.v new file mode 100644 index 0000000..e866960 --- /dev/null +++ b/v_windows/v/old/vlib/orm/orm.v @@ -0,0 +1,475 @@ +module orm + +import time + +pub const ( + num64 = [8, 12] + nums = [5, 6, 7, 9, 10, 11, 16] + float = [13, 14] + string = 18 + time = -2 + type_idx = map{ + 'i8': 5 + 'i16': 6 + 'int': 7 + 'i64': 8 + 'byte': 9 + 'u16': 10 + 'u32': 11 + 'u64': 12 + 'f32': 13 + 'f64': 14 + 'bool': 16 + 'string': 18 + } + string_max_len = 2048 +) + +pub type Primitive = InfixType | bool | byte | f32 | f64 | i16 | i64 | i8 | int | string | + time.Time | u16 | u32 | u64 + +pub enum OperationKind { + neq // != + eq // == + gt // > + lt // < + ge // >= + le // <= +} + +pub enum MathOperationKind { + add // + + sub // - + mul // * + div // / +} + +pub enum StmtKind { + insert + update + delete +} + +pub enum OrderType { + asc + desc +} + +fn (kind OperationKind) to_str() string { + str := match kind { + .neq { '!=' } + .eq { '=' } + .gt { '>' } + .lt { '<' } + .ge { '>=' } + .le { '<=' } + } + return str +} + +fn (kind OrderType) to_str() string { + return match kind { + .desc { + 'DESC' + } + .asc { + 'ASC' + } + } +} + +pub struct QueryData { +pub: + fields []string + data []Primitive + types []int + kinds []OperationKind + is_and []bool +} + +pub struct InfixType { +pub: + name string + operator MathOperationKind + right Primitive +} + +pub struct TableField { +pub: + name string + typ int + is_time bool + default_val string + is_arr bool + attrs []StructAttribute +} + +pub struct SelectConfig { +pub: + table string + is_count bool + has_where bool + has_order bool + order string + order_type OrderType + has_limit bool + primary string = 'id' // should be set if primary is different than 'id' and 'has_limit' is false + has_offset bool + fields []string + types []int +} + +pub interface Connection { + @select(config SelectConfig, data QueryData, where QueryData) ?[][]Primitive + insert(table string, data QueryData) ? + update(table string, data QueryData, where QueryData) ? + delete(table string, where QueryData) ? + create(table string, fields []TableField) ? + drop(talbe string) ? + last_id() Primitive +} + +pub fn orm_stmt_gen(table string, para string, kind StmtKind, num bool, qm string, start_pos int, data QueryData, where QueryData) string { + mut str := '' + + mut c := start_pos + + match kind { + .insert { + str += 'INSERT INTO $para$table$para (' + for i, field in data.fields { + str += '$para$field$para' + if i < data.fields.len - 1 { + str += ', ' + } + } + str += ') VALUES (' + for i, _ in data.fields { + str += qm + if num { + str += '$c' + c++ + } + if i < data.fields.len - 1 { + str += ', ' + } + } + str += ')' + } + .update { + str += 'UPDATE $para$table$para SET ' + for i, field in data.fields { + str += '$para$field$para = ' + if data.data.len > i { + d := data.data[i] + if d is InfixType { + op := match d.operator { + .add { + '+' + } + .sub { + '-' + } + .mul { + '*' + } + .div { + '/' + } + } + str += '$d.name $op $qm' + } else { + str += '$qm' + } + } else { + str += '$qm' + } + if num { + str += '$c' + c++ + } + if i < data.fields.len - 1 { + str += ', ' + } + } + str += ' WHERE ' + } + .delete { + str += 'DELETE FROM $para$table$para WHERE ' + } + } + if kind == .update || kind == .delete { + for i, field in where.fields { + str += '$para$field$para ${where.kinds[i].to_str()} $qm' + if num { + str += '$c' + c++ + } + if i < where.fields.len - 1 { + str += ' AND ' + } + } + } + str += ';' + return str +} + +pub fn orm_select_gen(orm SelectConfig, para string, num bool, qm string, start_pos int, where QueryData) string { + mut str := 'SELECT ' + + if orm.is_count { + str += 'COUNT(*)' + } else { + for i, field in orm.fields { + str += '$para$field$para' + if i < orm.fields.len - 1 { + str += ', ' + } + } + } + + str += ' FROM $para$orm.table$para' + + mut c := start_pos + + if orm.has_where { + str += ' WHERE ' + for i, field in where.fields { + str += '$para$field$para ${where.kinds[i].to_str()} $qm' + if num { + str += '$c' + c++ + } + if i < where.fields.len - 1 { + if where.is_and[i] { + str += ' AND ' + } else { + str += ' OR ' + } + } + } + } + + str += ' ORDER BY ' + if orm.has_order { + str += '$para$orm.order$para ' + str += orm.order_type.to_str() + } else { + str += '$para$orm.primary$para ' + str += orm.order_type.to_str() + } + + if orm.has_limit { + str += ' LIMIT ?' + if num { + str += '$c' + c++ + } + } + + if orm.has_offset { + str += ' OFFSET ?' + if num { + str += '$c' + c++ + } + } + + str += ';' + return str +} + +pub fn orm_table_gen(table string, para string, defaults bool, def_unique_len int, fields []TableField, sql_from_v fn (int) ?string, alternative bool) ?string { + mut str := 'CREATE TABLE IF NOT EXISTS $para$table$para (' + + if alternative { + str = 'IF NOT EXISTS (SELECT * FROM sysobjects WHERE name=$para$table$para and xtype=${para}U$para) CREATE TABLE $para$table$para (' + } + + mut fs := []string{} + mut unique_fields := []string{} + mut unique := map[string][]string{} + mut primary := '' + + for field in fields { + if field.is_arr { + continue + } + mut no_null := false + mut is_unique := false + mut is_skip := false + mut unique_len := 0 + // mut fkey := '' + for attr in field.attrs { + match attr.name { + 'primary' { + primary = field.name + } + 'unique' { + if attr.arg != '' { + if attr.kind == .string { + unique[attr.arg] << field.name + continue + } else if attr.kind == .number { + unique_len = attr.arg.int() + is_unique = true + continue + } + } + is_unique = true + } + 'nonull' { + no_null = true + } + 'skip' { + is_skip = true + } + /*'fkey' { + if attr.arg != '' { + if attr.kind == .string { + fkey = attr.arg + continue + } + } + }*/ + else {} + } + } + if is_skip { + continue + } + mut stmt := '' + mut field_name := sql_field_name(field) + mut ctyp := sql_from_v(sql_field_type(field)) or { + field_name = '${field_name}_id' + sql_from_v(8) ? + } + if ctyp == '' { + return error('Unknown type ($field.typ) for field $field.name in struct $table') + } + stmt = '$para$field_name$para $ctyp' + if defaults && field.default_val != '' { + stmt += ' DEFAULT $field.default_val' + } + if no_null { + stmt += ' NOT NULL' + } + if is_unique { + mut f := 'UNIQUE KEY($para$field.name$para' + if ctyp == 'TEXT' && def_unique_len > 0 { + if unique_len > 0 { + f += '($unique_len)' + } else { + f += '($def_unique_len)' + } + } + f += ')' + unique_fields << f + } + fs << stmt + } + if primary == '' { + return error('A primary key is required for $table') + } + if unique.len > 0 { + for k, v in unique { + mut tmp := []string{} + for f in v { + tmp << '$para$f$para' + } + fs << '/* $k */UNIQUE(${tmp.join(', ')})' + } + } + fs << 'PRIMARY KEY($para$primary$para)' + fs << unique_fields + str += fs.join(', ') + str += ');' + return str +} + +fn sql_field_type(field TableField) int { + mut typ := field.typ + if field.is_time { + return -2 + } + for attr in field.attrs { + if attr.kind == .plain && attr.name == 'sql' && attr.arg != '' { + if attr.arg.to_lower() == 'serial' { + typ = -1 + break + } + typ = orm.type_idx[attr.arg] + break + } + } + return typ +} + +fn sql_field_name(field TableField) string { + mut name := field.name + for attr in field.attrs { + if attr.name == 'sql' && attr.has_arg && attr.kind == .string { + name = attr.arg + break + } + } + return name +} + +// needed for backend functions + +pub fn bool_to_primitive(b bool) Primitive { + return Primitive(b) +} + +pub fn f32_to_primitive(b f32) Primitive { + return Primitive(b) +} + +pub fn f64_to_primitive(b f64) Primitive { + return Primitive(b) +} + +pub fn i8_to_primitive(b i8) Primitive { + return Primitive(b) +} + +pub fn i16_to_primitive(b i16) Primitive { + return Primitive(b) +} + +pub fn int_to_primitive(b int) Primitive { + return Primitive(b) +} + +pub fn i64_to_primitive(b i64) Primitive { + return Primitive(b) +} + +pub fn byte_to_primitive(b byte) Primitive { + return Primitive(b) +} + +pub fn u16_to_primitive(b u16) Primitive { + return Primitive(b) +} + +pub fn u32_to_primitive(b u32) Primitive { + return Primitive(b) +} + +pub fn u64_to_primitive(b u64) Primitive { + return Primitive(b) +} + +pub fn string_to_primitive(b string) Primitive { + return Primitive(b) +} + +pub fn time_to_primitive(b time.Time) Primitive { + return Primitive(b) +} + +pub fn infix_to_primitive(b InfixType) Primitive { + return Primitive(b) +} diff --git a/v_windows/v/old/vlib/orm/orm_fn_test.v b/v_windows/v/old/vlib/orm/orm_fn_test.v new file mode 100644 index 0000000..d74a0cb --- /dev/null +++ b/v_windows/v/old/vlib/orm/orm_fn_test.v @@ -0,0 +1,193 @@ +import orm + +fn test_orm_stmt_gen_update() { + query := orm.orm_stmt_gen('Test', "'", .update, true, '?', 0, orm.QueryData{ + fields: ['test', 'a'] + data: [] + types: [] + kinds: [] + }, orm.QueryData{ + fields: ['id', 'name'] + data: [] + types: [] + kinds: [.ge, .eq] + }) + assert query == "UPDATE 'Test' SET 'test' = ?0, 'a' = ?1 WHERE 'id' >= ?2 AND 'name' = ?3;" +} + +fn test_orm_stmt_gen_insert() { + query := orm.orm_stmt_gen('Test', "'", .insert, true, '?', 0, orm.QueryData{ + fields: ['test', 'a'] + data: [] + types: [] + kinds: [] + }, orm.QueryData{}) + assert query == "INSERT INTO 'Test' ('test', 'a') VALUES (?0, ?1);" +} + +fn test_orm_stmt_gen_delete() { + query := orm.orm_stmt_gen('Test', "'", .delete, true, '?', 0, orm.QueryData{ + fields: ['test', 'a'] + data: [] + types: [] + kinds: [] + }, orm.QueryData{ + fields: ['id', 'name'] + data: [] + types: [] + kinds: [.ge, .eq] + }) + assert query == "DELETE FROM 'Test' WHERE 'id' >= ?0 AND 'name' = ?1;" +} + +fn get_select_fields() []string { + return ['id', 'test', 'abc'] +} + +fn test_orm_select_gen() { + query := orm.orm_select_gen(orm.SelectConfig{ + table: 'test_table' + fields: get_select_fields() + }, "'", true, '?', 0, orm.QueryData{}) + + assert query == "SELECT 'id', 'test', 'abc' FROM 'test_table' ORDER BY 'id' ASC;" +} + +fn test_orm_select_gen_with_limit() { + query := orm.orm_select_gen(orm.SelectConfig{ + table: 'test_table' + fields: get_select_fields() + has_limit: true + }, "'", true, '?', 0, orm.QueryData{}) + + assert query == "SELECT 'id', 'test', 'abc' FROM 'test_table' ORDER BY 'id' ASC LIMIT ?0;" +} + +fn test_orm_select_gen_with_where() { + query := orm.orm_select_gen(orm.SelectConfig{ + table: 'test_table' + fields: get_select_fields() + has_where: true + }, "'", true, '?', 0, orm.QueryData{ + fields: ['abc', 'test'] + kinds: [.eq, .gt] + is_and: [true] + }) + + assert query == "SELECT 'id', 'test', 'abc' FROM 'test_table' WHERE 'abc' = ?0 AND 'test' > ?1 ORDER BY 'id' ASC;" +} + +fn test_orm_select_gen_with_order() { + query := orm.orm_select_gen(orm.SelectConfig{ + table: 'test_table' + fields: get_select_fields() + has_order: true + order_type: .desc + }, "'", true, '?', 0, orm.QueryData{}) + + assert query == "SELECT 'id', 'test', 'abc' FROM 'test_table' ORDER BY '' DESC;" +} + +fn test_orm_select_gen_with_offset() { + query := orm.orm_select_gen(orm.SelectConfig{ + table: 'test_table' + fields: get_select_fields() + has_offset: true + }, "'", true, '?', 0, orm.QueryData{}) + + assert query == "SELECT 'id', 'test', 'abc' FROM 'test_table' ORDER BY 'id' ASC OFFSET ?0;" +} + +fn test_orm_select_gen_with_all() { + query := orm.orm_select_gen(orm.SelectConfig{ + table: 'test_table' + fields: get_select_fields() + has_limit: true + has_order: true + order_type: .desc + has_offset: true + has_where: true + }, "'", true, '?', 0, orm.QueryData{ + fields: ['abc', 'test'] + kinds: [.eq, .gt] + is_and: [true] + }) + + assert query == "SELECT 'id', 'test', 'abc' FROM 'test_table' WHERE 'abc' = ?0 AND 'test' > ?1 ORDER BY '' DESC LIMIT ?2 OFFSET ?3;" +} + +fn test_orm_table_gen() { + query := orm.orm_table_gen('test_table', "'", true, 0, [ + orm.TableField{ + name: 'id' + typ: 7 + default_val: '10' + attrs: [ + StructAttribute{ + name: 'primary' + }, + StructAttribute{ + name: 'sql' + has_arg: true + arg: 'serial' + kind: .plain + }, + ] + }, + orm.TableField{ + name: 'test' + typ: 18 + }, + orm.TableField{ + name: 'abc' + typ: 8 + default_val: '6754' + }, + ], sql_type_from_v, false) or { panic(err) } + assert query == "CREATE TABLE IF NOT EXISTS 'test_table' ('id' SERIAL DEFAULT 10, 'test' TEXT, 'abc' INT64 DEFAULT 6754, PRIMARY KEY('id'));" + + alt_query := orm.orm_table_gen('test_table', "'", true, 0, [ + orm.TableField{ + name: 'id' + typ: 7 + default_val: '10' + attrs: [ + StructAttribute{ + name: 'primary' + }, + StructAttribute{ + name: 'sql' + has_arg: true + arg: 'serial' + kind: .plain + }, + ] + }, + orm.TableField{ + name: 'test' + typ: 18 + }, + orm.TableField{ + name: 'abc' + typ: 8 + default_val: '6754' + }, + ], sql_type_from_v, true) or { panic(err) } + assert alt_query == "IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='test_table' and xtype='U') CREATE TABLE 'test_table' ('id' SERIAL DEFAULT 10, 'test' TEXT, 'abc' INT64 DEFAULT 6754, PRIMARY KEY('id'));" +} + +fn sql_type_from_v(typ int) ?string { + return if typ in orm.nums { + 'INT' + } else if typ in orm.num64 { + 'INT64' + } else if typ in orm.float { + 'DOUBLE' + } else if typ == orm.string { + 'TEXT' + } else if typ == -1 { + 'SERIAL' + } else { + error('Unknown type $typ') + } +} diff --git a/v_windows/v/old/vlib/orm/orm_test.v b/v_windows/v/old/vlib/orm/orm_test.v new file mode 100644 index 0000000..5be8fe2 --- /dev/null +++ b/v_windows/v/old/vlib/orm/orm_test.v @@ -0,0 +1,290 @@ +// import os +// import pg +// import term +import sqlite + +struct Module { + id int [primary; sql: serial] + name string + nr_downloads int + user User +} + +[table: 'userlist'] +struct User { + id int [primary; sql: serial] + age int + name string [sql: 'username'] + is_customer bool + skipped_string string [skip] +} + +struct Foo { + age int +} + +fn test_orm_sqlite() { + db := sqlite.connect(':memory:') or { panic(err) } + db.exec('drop table if exists User') + sql db { + create table User + } + + name := 'Peter' + + sam := User{ + age: 29 + name: 'Sam' + } + + peter := User{ + age: 31 + name: 'Peter' + } + + k := User{ + age: 30 + name: 'Kate' + is_customer: true + } + + sql db { + insert sam into User + insert peter into User + insert k into User + } + + c := sql db { + select count from User where id != 1 + } + assert c == 2 + + nr_all_users := sql db { + select count from User + } + assert nr_all_users == 3 + println('nr_all_users=$nr_all_users') + // + nr_users1 := sql db { + select count from User where id == 1 + } + assert nr_users1 == 1 + println('nr_users1=$nr_users1') + // + nr_peters := sql db { + select count from User where id == 2 && name == 'Peter' + } + assert nr_peters == 1 + println('nr_peters=$nr_peters') + // + nr_peters2 := sql db { + select count from User where id == 2 && name == name + } + assert nr_peters2 == 1 + nr_peters3 := sql db { + select count from User where name == name + } + assert nr_peters3 == 1 + peters := sql db { + select from User where name == name + } + assert peters.len == 1 + assert peters[0].name == 'Peter' + one_peter := sql db { + select from User where name == name limit 1 + } + assert one_peter.name == 'Peter' + assert one_peter.id == 2 + // + user := sql db { + select from User where id == 1 + } + println(user) + assert user.name == 'Sam' + assert user.id == 1 + assert user.age == 29 + // + users := sql db { + select from User where id > 0 + } + println(users) + assert users.len == 3 + assert users[0].name == 'Sam' + assert users[1].name == 'Peter' + assert users[1].age == 31 + // + users2 := sql db { + select from User where id < 0 + } + println(users2) + assert users2.len == 0 + // + users3 := sql db { + select from User where age == 29 || age == 31 + } + println(users3) + assert users3.len == 2 + assert users3[0].age == 29 + assert users3[1].age == 31 + // + missing_user := sql db { + select from User where id == 8777 + } + println('missing_user:') + println(missing_user) // zero struct + // + new_user := User{ + name: 'New user' + age: 30 + } + sql db { + insert new_user into User + } + + // db.insert(user2) + x := sql db { + select from User where id == 4 + } + println(x) + assert x.age == 30 + assert x.id == 4 + assert x.name == 'New user' + // + kate := sql db { + select from User where id == 3 + } + assert kate.is_customer == true + // + customer := sql db { + select from User where is_customer == true limit 1 + } + assert customer.is_customer == true + assert customer.name == 'Kate' + // + sql db { + update User set age = 31 where name == 'Kate' + } + + kate2 := sql db { + select from User where id == 3 + } + assert kate2.age == 31 + assert kate2.name == 'Kate' + // + sql db { + update User set age = 32, name = 'Kate N' where name == 'Kate' + } + + mut kate3 := sql db { + select from User where id == 3 + } + assert kate3.age == 32 + assert kate3.name == 'Kate N' + // + /* + sql db { + update User set age = age + 1, name = 'Kate N' where name == 'Kate' + } + kate3 = sql db { + select from User where id == 3 + } + println(kate3) + assert kate3.age == 32 + assert kate3.name == 'Kate N' + */ + new_age := 33 + sql db { + update User set age = new_age, name = 'Kate N' where id == 3 + } + + kate3 = sql db { + select from User where id == 3 + } + assert kate3.age == 33 + assert kate3.name == 'Kate N' + // + foo := Foo{34} + sql db { + update User set age = foo.age, name = 'Kate N' where id == 3 + } + + kate3 = sql db { + select from User where id == 3 + } + assert kate3.age == 34 + assert kate3.name == 'Kate N' + // + no_user := sql db { + select from User where id == 30 + } + assert no_user.name == '' // TODO optional + assert no_user.age == 0 + // + two_users := sql db { + select from User limit 2 + } + assert two_users.len == 2 + assert two_users[0].id == 1 + // + y := sql db { + select from User limit 2 offset 1 + } + assert y.len == 2 + assert y[0].id == 2 + // + offset_const := 2 + z := sql db { + select from User limit 2 offset offset_const + } + assert z.len == 2 + assert z[0].id == 3 + oldest := sql db { + select from User order by age desc limit 1 + } + assert oldest.age == 34 + offs := 1 + second_oldest := sql db { + select from User order by age desc limit 1 offset offs + } + assert second_oldest.age == 31 + sql db { + delete from User where age == 34 + } + + updated_oldest := sql db { + select from User order by age desc limit 1 + } + assert updated_oldest.age == 31 + + db.exec('insert into User (name, age) values (NULL, 31)') + null_user := sql db { + select from User where id == 5 + } + assert null_user.name == '' + + age_test := sql db { + select from User where id == 1 + } + + assert age_test.age == 29 + + sql db { + update User set age = age + 1 where id == 1 + } + + mut first := sql db { + select from User where id == 1 + } + + assert first.age == 30 + + sql db { + update User set age = age * 2 where id == 1 + } + + first = sql db { + select from User where id == 1 + } + + assert first.age == 60 +} diff --git a/v_windows/v/old/vlib/os/args.v b/v_windows/v/old/vlib/os/args.v new file mode 100644 index 0000000..597637c --- /dev/null +++ b/v_windows/v/old/vlib/os/args.v @@ -0,0 +1,51 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module os + +// args_after returns all os.args, located *after* a specified `cut_word`. +// When `cut_word` is NOT found, os.args is returned unmodified. +pub fn args_after(cut_word string) []string { + if args.len == 0 { + return []string{} + } + mut cargs := []string{} + if cut_word !in args { + cargs = args.clone() + } else { + mut found := false + cargs << args[0] + for a in args[1..] { + if a == cut_word { + found = true + continue + } + if !found { + continue + } + cargs << a + } + } + return cargs +} + +// args_after returns all os.args, located *before* a specified `cut_word`. +// When `cut_word` is NOT found, os.args is returned unmodified. +pub fn args_before(cut_word string) []string { + if args.len == 0 { + return []string{} + } + mut cargs := []string{} + if cut_word !in args { + cargs = args.clone() + } else { + cargs << args[0] + for a in args[1..] { + if a == cut_word { + break + } + cargs << a + } + } + return cargs +} diff --git a/v_windows/v/old/vlib/os/bare/bare_example_linux.v b/v_windows/v/old/vlib/os/bare/bare_example_linux.v new file mode 100644 index 0000000..0aa92dd --- /dev/null +++ b/v_windows/v/old/vlib/os/bare/bare_example_linux.v @@ -0,0 +1,8 @@ +fn main() { + sys_write(1, 'hello\n'.str, 6) + s := 'test string\n' + sys_write(1, s.str, u64(s.len)) + a := s[0] + println('Hello freestanding!') + println(a) +} diff --git a/v_windows/v/old/vlib/os/cmdline/cmdline.v b/v_windows/v/old/vlib/os/cmdline/cmdline.v new file mode 100644 index 0000000..96bcb27 --- /dev/null +++ b/v_windows/v/old/vlib/os/cmdline/cmdline.v @@ -0,0 +1,82 @@ +module cmdline + +// Fetch multiple option by param, e.g. +// args: ['v', '-d', 'aa', '-d', 'bb', '-d', 'cc'] +// param: '-d' +// ret: ['aa', 'bb', 'cc'] +pub fn options(args []string, param string) []string { + mut flags := []string{} + for i, v in args { + if v == param { + if i + 1 < args.len { + flags << args[i + 1] + } + } + } + return flags +} + +// Fetch option by param, e.g. +// args: ['v', '-d', 'aa'] +// param: '-d' +// def: '' +// ret: 'aa' +pub fn option(args []string, param string, def string) string { + mut found := false + for arg in args { + if found { + return arg + } else if param == arg { + found = true + } + } + return def +} + +// Fetch all options before what params, e.g. +// args: ['-stat', 'test', 'aaa.v'] +// what: ['test'] +// ret: ['-stat'] +pub fn options_before(args []string, what []string) []string { + mut args_before := []string{} + for a in args { + if a in what { + break + } + args_before << a + } + return args_before +} + +// Fetch all options after what params, e.g. +// args: ['-stat', 'test', 'aaa.v'] +// what: ['test'] +// ret: ['aaa.v'] +pub fn options_after(args []string, what []string) []string { + mut found := false + mut args_after := []string{} + for a in args { + if a in what { + found = true + continue + } + if found { + args_after << a + } + } + return args_after +} + +// Fetch all options not start with '-', e.g. +// args: ['-d', 'aa', '--help', 'bb'] +// ret: ['aa', 'bb'] +pub fn only_non_options(args []string) []string { + return args.filter(!it.starts_with('-')) +} + +// Fetch all options start with '-', e.g. +// args: ['-d', 'aa', '--help', 'bb'] +// ret: ['-d', '--help'] +pub fn only_options(args []string) []string { + return args.filter(it.starts_with('-')) +} diff --git a/v_windows/v/old/vlib/os/cmdline/cmdline_test.v b/v_windows/v/old/vlib/os/cmdline/cmdline_test.v new file mode 100644 index 0000000..5c34f3f --- /dev/null +++ b/v_windows/v/old/vlib/os/cmdline/cmdline_test.v @@ -0,0 +1,37 @@ +import os.cmdline + +fn test_options() { + args := ['v', '-d', 'aa', '-d', 'bb', '-d', 'cc'] + ret := cmdline.options(args, '-d') + assert ret.eq(['aa', 'bb', 'cc']) +} + +fn test_option() { + args := ['v', '-d', 'aa'] + ret := cmdline.option(args, '-d', '') + assert ret == 'aa' +} + +fn test_options_before() { + args := ['-stat', 'test', 'aaa.v'] + ret := cmdline.options_before(args, ['test']) + assert ret.eq(['-stat']) +} + +fn test_options_after() { + args := ['-stat', 'test', 'aaa.v'] + ret := cmdline.options_after(args, ['test']) + assert ret.eq(['aaa.v']) +} + +fn test_only_non_options() { + args := ['-d', 'aa', '--help', 'bb'] + ret := cmdline.only_non_options(args) + assert ret.eq(['aa', 'bb']) +} + +fn test_only_options() { + args := ['-d', 'aa', '--help', 'bb'] + ret := cmdline.only_options(args) + assert ret.eq(['-d', '--help']) +} diff --git a/v_windows/v/old/vlib/os/const.v b/v_windows/v/old/vlib/os/const.v new file mode 100644 index 0000000..bcf59cf --- /dev/null +++ b/v_windows/v/old/vlib/os/const.v @@ -0,0 +1 @@ +module os diff --git a/v_windows/v/old/vlib/os/const_nix.c.v b/v_windows/v/old/vlib/os/const_nix.c.v new file mode 100644 index 0000000..275df70 --- /dev/null +++ b/v_windows/v/old/vlib/os/const_nix.c.v @@ -0,0 +1,16 @@ +module os + +// File modes +const ( + o_rdonly = 0o00000000 // open the file read-only. + o_wronly = 0o00000001 // open the file write-only. + o_rdwr = 0o00000002 // open the file read-write. + o_binary = 0o00000000 // input and output is not translated; the default on unix + o_create = 0o00000100 // create a new file if none exists. + o_excl = 0o00000200 // used with o_create, file must not exist. + o_noctty = 0o00000400 // if file is terminal, don't make it the controller terminal + o_trunc = 0o00001000 // truncate regular writable file when opened. + o_append = 0o00002000 // append data to the file when writing. + o_nonblock = 0o00004000 // prevents blocking when opening files + o_sync = 0o04010000 // open for synchronous I/O. +) diff --git a/v_windows/v/old/vlib/os/const_windows.c.v b/v_windows/v/old/vlib/os/const_windows.c.v new file mode 100644 index 0000000..4b87c8b --- /dev/null +++ b/v_windows/v/old/vlib/os/const_windows.c.v @@ -0,0 +1,161 @@ +module os + +// Ref - winnt.h +const ( + success = 0x0000 // ERROR_SUCCESS + error_insufficient_buffer = 0x0082 +) + +const ( + handle_generic_read = 0x80000000 + handle_open_existing = 0x00000003 +) + +const ( + file_share_read = 0x01 + file_share_write = 0x02 + file_share_delete = 0x04 +) + +const ( + file_notify_change_file_name = 0x01 + file_notify_change_dir_name = 0x02 + file_notify_change_attributes = 0x04 + file_notify_change_size = 0x08 + file_notify_change_last_write = 0x10 + file_notify_change_last_access = 0x20 + file_notify_change_creation = 0x40 + file_notify_change_security = 0x80 +) + +const ( + file_action_added = 0x01 + file_action_removed = 0x02 + file_action_modified = 0x03 + file_action_renamed_old_name = 0x04 + file_action_renamed_new_name = 0x05 +) + +const ( + file_attr_readonly = 0x00000001 + file_attr_hidden = 0x00000002 + file_attr_system = 0x00000004 + file_attr_directory = 0x00000010 + file_attr_archive = 0x00000020 + file_attr_device = 0x00000040 + file_attr_normal = 0x00000080 + file_attr_temporary = 0x00000100 + file_attr_sparse_file = 0x00000200 + file_attr_reparse_point = 0x00000400 + file_attr_compressed = 0x00000800 + file_attr_offline = 0x00001000 + file_attr_not_content_indexed = 0x00002000 + file_attr_encrypted = 0x00004000 + file_attr_integrity_stream = 0x00008000 + file_attr_virtual = 0x00010000 + file_attr_no_scrub_data = 0x00020000 + // file_attr_recall_on_open = u32(0x...) + // file_attr_recall_on_data_access = u32(0x...) +) + +const ( + file_type_unknown = 0x00 + file_type_disk = 0x01 + file_type_char = 0x02 + file_type_pipe = 0x03 +) + +const ( + file_invalid_file_id = (-1) +) + +const ( + invalid_handle_value = voidptr(-1) +) + +// https://docs.microsoft.com/en-us/windows/console/setconsolemode +const ( + // Input Buffer + enable_echo_input = 0x0004 + enable_extended_flags = 0x0080 + enable_insert_mode = 0x0020 + enable_line_input = 0x0002 + enable_mouse_input = 0x0010 + enable_processed_input = 0x0001 + enable_quick_edit_mode = 0x0040 + enable_window_input = 0x0008 + enable_virtual_terminal_input = 0x0200 + // Output Screen Buffer + enable_processed_output = 0x01 + enable_wrap_at_eol_output = 0x02 + enable_virtual_terminal_processing = 0x04 + disable_newline_auto_return = 0x08 + enable_lvb_grid_worldwide = 0x10 +) + +// File modes +const ( + o_rdonly = 0x0000 // open the file read-only. + o_wronly = 0x0001 // open the file write-only. + o_rdwr = 0x0002 // open the file read-write. + o_append = 0x0008 // append data to the file when writing. + o_create = 0x0100 // create a new file if none exists. + o_binary = 0x8000 // input and output is not translated. + o_trunc = 0x0200 // truncate regular writable file when opened. + o_excl = 0x0400 // used with o_create, file must not exist. + o_sync = 0x0000 // open for synchronous I/O (ignored on Windows) + o_noctty = 0x0000 // make file non-controlling tty (ignored on Windows) + o_nonblock = 0x0000 // don't block on opening file (ignored on Windows) +) + +const ( + status_access_violation = 0xC0000005 + status_in_page_error = 0xC0000006 + status_invalid_handle = 0xC0000008 + status_invalid_parameter = 0xC000000D + status_no_memory = 0xC0000017 + status_illegal_instruction = 0xC000001D + status_noncontinuable_exception = 0xC0000025 + status_invalid_disposition = 0xC0000026 + status_array_bounds_exceeded = 0xC000008C + status_float_denormal_operand = 0xC000008D + status_float_divide_by_zero = 0xC000008E + status_float_inexact_result = 0xC000008F + status_float_invalid_operation = 0xC0000090 + status_float_overflow = 0xC0000091 + status_float_stack_check = 0xC0000092 + status_float_underflow = 0xC0000093 + status_integer_divide_by_zero = 0xC0000094 + status_integer_overflow = 0xC0000095 + status_privileged_instruction = 0xC0000096 + status_stack_overflow = 0xC00000FD + status_dll_not_found = 0xC0000135 + status_ordinal_not_found = 0xC0000138 + status_entrypoint_not_found = 0xC0000139 + status_control_c_exit = 0xC000013A + status_dll_init_failed = 0xC0000142 + status_float_multiple_faults = 0xC00002B4 + status_float_multiple_traps = 0xC00002B5 + status_reg_nat_consumption = 0xC00002C9 + status_heap_corruption = 0xC0000374 + status_stack_buffer_overrun = 0xC0000409 + status_invalid_cruntime_parameter = 0xC0000417 + status_assertion_failure = 0xC0000420 +) + +// Windows Registry Constants +pub const ( + hkey_local_machine = voidptr(0x80000002) + hkey_current_user = voidptr(0x80000001) + key_query_value = 0x0001 + key_set_value = 0x0002 + key_enumerate_sub_keys = 0x0008 + key_wow64_32key = 0x0200 +) + +// Windows Messages +pub const ( + hwnd_broadcast = voidptr(0xFFFF) + wm_settingchange = 0x001A + smto_abortifhung = 0x0002 +) diff --git a/v_windows/v/old/vlib/os/environment.c.v b/v_windows/v/old/vlib/os/environment.c.v new file mode 100644 index 0000000..1b65a27 --- /dev/null +++ b/v_windows/v/old/vlib/os/environment.c.v @@ -0,0 +1,108 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module os + +fn C.getenv(&char) &char + +// C.GetEnvironmentStringsW & C.FreeEnvironmentStringsW are defined only on windows +fn C.GetEnvironmentStringsW() &u16 + +fn C.FreeEnvironmentStringsW(&u16) int + +// `getenv` returns the value of the environment variable named by the key. +pub fn getenv(key string) string { + unsafe { + $if windows { + s := C._wgetenv(key.to_wide()) + if s == 0 { + return '' + } + return string_from_wide(s) + } $else { + s := C.getenv(&char(key.str)) + if s == voidptr(0) { + return '' + } + // NB: C.getenv *requires* that the result be copied. + return cstring_to_vstring(s) + } + } +} + +// os.setenv sets the value of an environment variable with `name` to `value`. +pub fn setenv(name string, value string, overwrite bool) int { + $if windows { + format := '$name=$value' + if overwrite { + unsafe { + return C._putenv(&char(format.str)) + } + } else { + if getenv(name).len == 0 { + unsafe { + return C._putenv(&char(format.str)) + } + } + } + return -1 + } $else { + unsafe { + return C.setenv(&char(name.str), &char(value.str), overwrite) + } + } +} + +// os.unsetenv clears an environment variable with `name`. +pub fn unsetenv(name string) int { + $if windows { + format := '$name=' + return C._putenv(&char(format.str)) + } $else { + return C.unsetenv(&char(name.str)) + } +} + +// See: https://linux.die.net/man/5/environ for unix platforms. +// See: https://docs.microsoft.com/bg-bg/windows/win32/api/processenv/nf-processenv-getenvironmentstrings +// os.environ returns a map of all the current environment variables + +fn unix_environ() &&char { + // TODO: remove this helper function, when `&&char(C.environ)` works properly + return voidptr(C.environ) +} + +pub fn environ() map[string]string { + mut res := map[string]string{} + $if windows { + mut estrings := C.GetEnvironmentStringsW() + mut eline := '' + for c := estrings; *c != 0; { + eline = unsafe { string_from_wide(c) } + eq_index := eline.index_byte(`=`) + if eq_index > 0 { + res[eline[0..eq_index]] = eline[eq_index + 1..] + } + unsafe { + c = c + eline.len + 1 + } + } + C.FreeEnvironmentStringsW(estrings) + } $else { + start := unix_environ() + mut i := 0 + for { + x := unsafe { start[i] } + if x == 0 { + break + } + eline := unsafe { cstring_to_vstring(x) } + eq_index := eline.index_byte(`=`) + if eq_index > 0 { + res[eline[0..eq_index]] = eline[eq_index + 1..] + } + i++ + } + } + return res +} diff --git a/v_windows/v/old/vlib/os/environment_test.v b/v_windows/v/old/vlib/os/environment_test.v new file mode 100644 index 0000000..5324371 --- /dev/null +++ b/v_windows/v/old/vlib/os/environment_test.v @@ -0,0 +1,49 @@ +import os +import time + +fn test_getenv() { + // VEXE is set by the V builtin test runner + assert os.getenv('VEXE').len > 0 + assert os.getenv('PATH').len > 0 +} + +fn test_setenv() { + os.setenv('foo', 'bar', true) + assert os.getenv('foo') == 'bar' + // `setenv` should not set if `overwrite` is false + os.setenv('foo', 'bar2', false) + assert os.getenv('foo') == 'bar' + // `setenv` should overwrite if `overwrite` is true + os.setenv('foo', 'bar2', true) + assert os.getenv('foo') == 'bar2' +} + +fn test_unsetenv() { + os.setenv('foo', 'bar', true) + os.unsetenv('foo') + assert os.getenv('foo') == '' +} + +fn test_environ() { + os.setenv('myvar1', 'bar1', true) + os.setenv('myvar2', 'bar2', true) + assert os.getenv('myvar1') == 'bar1' + assert os.getenv('myvar2') == 'bar2' + assert os.getenv('myvar_not_defined') == '' + all := os.environ() + assert all['myvar1'] == 'bar1' + assert all['myvar2'] == 'bar2' + assert all['myvar_not_defined'] == '' +} + +fn test_setenv_var_not_exists() { + key := time.new_time(time.now()).unix + os.setenv('foo$key', 'bar', false) + assert os.getenv('foo$key') == 'bar' +} + +fn test_getenv_empty_var() { + key := time.new_time(time.now()).unix + os.setenv('empty$key', '""', false) + assert os.getenv('empty$key') == '""' +} diff --git a/v_windows/v/old/vlib/os/fd.c.v b/v_windows/v/old/vlib/os/fd.c.v new file mode 100644 index 0000000..de69e38 --- /dev/null +++ b/v_windows/v/old/vlib/os/fd.c.v @@ -0,0 +1,61 @@ +module os + +// file descriptor based operations: + +// close filedescriptor +pub fn fd_close(fd int) int { + if fd == -1 { + return 0 + } + return C.close(fd) +} + +pub fn fd_write(fd int, s string) { + if fd == -1 { + return + } + mut sp := s.str + mut remaining := s.len + for remaining > 0 { + written := C.write(fd, sp, remaining) + if written < 0 { + return + } + remaining = remaining - written + sp = unsafe { sp + written } + } +} + +// read from filedescriptor, block until data +pub fn fd_slurp(fd int) []string { + mut res := []string{} + if fd == -1 { + return res + } + for { + s, b := fd_read(fd, 4096) + if b <= 0 { + break + } + res << s + } + return res +} + +// read from filedescriptor, don't block +// return [bytestring,nrbytes] +pub fn fd_read(fd int, maxbytes int) (string, int) { + if fd == -1 { + return '', 0 + } + unsafe { + mut buf := malloc_noscan(maxbytes + 1) + nbytes := C.read(fd, buf, maxbytes) + if nbytes < 0 { + free(buf) + return '', nbytes + } + buf[nbytes] = 0 + return tos(buf, nbytes), nbytes + } +} diff --git a/v_windows/v/old/vlib/os/file.c.v b/v_windows/v/old/vlib/os/file.c.v new file mode 100644 index 0000000..7383f5a --- /dev/null +++ b/v_windows/v/old/vlib/os/file.c.v @@ -0,0 +1,781 @@ +module os + +pub struct File { + cfile voidptr // Using void* instead of FILE* +pub: + fd int +pub mut: + is_opened bool +} + +struct FileInfo { + name string + size int +} + +fn C.fseeko(&C.FILE, u64, int) int + +fn C._fseeki64(&C.FILE, u64, int) int + +fn C.getc(&C.FILE) int + +// open_file can be used to open or create a file with custom flags and permissions and returns a `File` object. +pub fn open_file(path string, mode string, options ...int) ?File { + mut flags := 0 + for m in mode { + match m { + `w` { flags |= o_create | o_trunc } + `a` { flags |= o_create | o_append } + `r` { flags |= o_rdonly } + `b` { flags |= o_binary } + `s` { flags |= o_sync } + `n` { flags |= o_nonblock } + `c` { flags |= o_noctty } + `+` { flags |= o_rdwr } + else {} + } + } + if mode == 'r+' { + flags = o_rdwr + } + if mode == 'w' { + flags = o_wronly | o_create | o_trunc + } + if mode == 'a' { + flags = o_wronly | o_create | o_append + } + mut permission := 0o666 + if options.len > 0 { + permission = options[0] + } + $if windows { + if permission < 0o600 { + permission = 0x0100 + } else { + permission = 0x0100 | 0x0080 + } + } + mut p := path + $if windows { + p = path.replace('/', '\\') + } + fd := C.open(&char(p.str), flags, permission) + if fd == -1 { + return error(posix_get_error_msg(C.errno)) + } + cfile := C.fdopen(fd, &char(mode.str)) + if isnil(cfile) { + return error('Failed to open or create file "$path"') + } + return File{ + cfile: cfile + fd: fd + is_opened: true + } +} + +// open tries to open a file for reading and returns back a read-only `File` object. +pub fn open(path string) ?File { + /* + $if linux { + $if !android { + fd := C.syscall(sys_open, path.str, 511) + if fd == -1 { + return error('failed to open file "$path"') + } + return File{ + fd: fd + is_opened: true + } + } + } + */ + cfile := vfopen(path, 'rb') ? + fd := fileno(cfile) + return File{ + cfile: cfile + fd: fd + is_opened: true + } +} + +// create creates or opens a file at a specified location and returns a write-only `File` object. +pub fn create(path string) ?File { + /* + // NB: android/termux/bionic is also a kind of linux, + // but linux syscalls there sometimes fail, + // while the libc version should work. + $if linux { + $if !android { + //$if macos { + // fd = C.syscall(398, path.str, 0x601, 0x1b6) + //} + //$if linux { + fd = C.syscall(sys_creat, path.str, 511) + //} + if fd == -1 { + return error('failed to create file "$path"') + } + file = File{ + fd: fd + is_opened: true + } + return file + } + } + */ + cfile := vfopen(path, 'wb') ? + fd := fileno(cfile) + return File{ + cfile: cfile + fd: fd + is_opened: true + } +} + +// stdin - return an os.File for stdin, so that you can use .get_line on it too. +pub fn stdin() File { + return File{ + fd: 0 + cfile: C.stdin + is_opened: true + } +} + +// stdout - return an os.File for stdout +pub fn stdout() File { + return File{ + fd: 1 + cfile: C.stdout + is_opened: true + } +} + +// stderr - return an os.File for stderr +pub fn stderr() File { + return File{ + fd: 2 + cfile: C.stderr + is_opened: true + } +} + +// read implements the Reader interface. +pub fn (f &File) read(mut buf []byte) ?int { + if buf.len == 0 { + return 0 + } + nbytes := fread(buf.data, 1, buf.len, f.cfile) ? + return nbytes +} + +// **************************** Write ops *************************** +// write implements the Writer interface. +// It returns how many bytes were actually written. +pub fn (mut f File) write(buf []byte) ?int { + if !f.is_opened { + return error_file_not_opened() + } + /* + $if linux { + $if !android { + res := C.syscall(sys_write, f.fd, s.str, s.len) + return res + } + } + */ + written := int(C.fwrite(buf.data, 1, buf.len, f.cfile)) + if written == 0 && buf.len != 0 { + return error('0 bytes written') + } + return written +} + +// writeln writes the string `s` into the file, and appends a \n character. +// It returns how many bytes were written, including the \n character. +pub fn (mut f File) writeln(s string) ?int { + if !f.is_opened { + return error_file_not_opened() + } + /* + $if linux { + $if !android { + snl := s + '\n' + C.syscall(sys_write, f.fd, snl.str, snl.len) + return + } + } + */ + // TODO perf + written := int(C.fwrite(s.str, 1, s.len, f.cfile)) + if written == 0 && s.len != 0 { + return error('0 bytes written') + } + x := C.fputs(c'\n', f.cfile) + if x < 0 { + return error('could not add newline') + } + return (written + 1) +} + +// write_string writes the string `s` into the file +// It returns how many bytes were actually written. +pub fn (mut f File) write_string(s string) ?int { + unsafe { f.write_full_buffer(s.str, size_t(s.len)) ? } + return s.len +} + +// write_to implements the RandomWriter interface. +// It returns how many bytes were actually written. +// It resets the seek position to the end of the file. +pub fn (mut f File) write_to(pos u64, buf []byte) ?int { + if !f.is_opened { + return error_file_not_opened() + } + $if x64 { + $if windows { + C._fseeki64(f.cfile, pos, C.SEEK_SET) + res := int(C.fwrite(buf.data, 1, buf.len, f.cfile)) + if res == 0 && buf.len != 0 { + return error('0 bytes written') + } + C._fseeki64(f.cfile, 0, C.SEEK_END) + return res + } $else { + C.fseeko(f.cfile, pos, C.SEEK_SET) + res := int(C.fwrite(buf.data, 1, buf.len, f.cfile)) + if res == 0 && buf.len != 0 { + return error('0 bytes written') + } + C.fseeko(f.cfile, 0, C.SEEK_END) + return res + } + } + $if x32 { + C.fseek(f.cfile, pos, C.SEEK_SET) + res := int(C.fwrite(buf.data, 1, buf.len, f.cfile)) + if res == 0 && buf.len != 0 { + return error('0 bytes written') + } + C.fseek(f.cfile, 0, C.SEEK_END) + return res + } + return error('Could not write to file') +} + +// write_ptr writes `size` bytes to the file, starting from the address in `data`. +// NB: write_ptr is unsafe and should be used carefully, since if you pass invalid +// pointers to it, it will cause your programs to segfault. +[unsafe] +pub fn (mut f File) write_ptr(data voidptr, size int) int { + return int(C.fwrite(data, 1, size, f.cfile)) +} + +// write_full_buffer writes a whole buffer of data to the file, starting from the +// address in `buffer`, no matter how many tries/partial writes it would take. +[unsafe] +pub fn (mut f File) write_full_buffer(buffer voidptr, buffer_len size_t) ? { + if buffer_len <= size_t(0) { + return + } + if !f.is_opened { + return error_file_not_opened() + } + mut ptr := &byte(buffer) + mut remaining_bytes := i64(buffer_len) + for remaining_bytes > 0 { + unsafe { + x := i64(C.fwrite(ptr, 1, remaining_bytes, f.cfile)) + ptr += x + remaining_bytes -= x + if x <= 0 { + return error('C.fwrite returned 0') + } + } + } +} + +// write_ptr_at writes `size` bytes to the file, starting from the address in `data`, +// at byte offset `pos`, counting from the start of the file (pos 0). +// NB: write_ptr_at is unsafe and should be used carefully, since if you pass invalid +// pointers to it, it will cause your programs to segfault. +[unsafe] +pub fn (mut f File) write_ptr_at(data voidptr, size int, pos u64) int { + $if x64 { + $if windows { + C._fseeki64(f.cfile, pos, C.SEEK_SET) + res := int(C.fwrite(data, 1, size, f.cfile)) + C._fseeki64(f.cfile, 0, C.SEEK_END) + return res + } $else { + C.fseeko(f.cfile, pos, C.SEEK_SET) + res := int(C.fwrite(data, 1, size, f.cfile)) + C.fseeko(f.cfile, 0, C.SEEK_END) + return res + } + } + $if x32 { + C.fseek(f.cfile, pos, C.SEEK_SET) + res := int(C.fwrite(data, 1, size, f.cfile)) + C.fseek(f.cfile, 0, C.SEEK_END) + return res + } + return 0 +} + +// **************************** Read ops *************************** + +// fread wraps C.fread and handles error and end-of-file detection. +fn fread(ptr voidptr, item_size int, items int, stream &C.FILE) ?int { + nbytes := int(C.fread(ptr, item_size, items, stream)) + // If no bytes were read, check for errors and end-of-file. + if nbytes <= 0 { + // If fread encountered end-of-file return the none error. Note that fread + // may read data and encounter the end-of-file, but we shouldn't return none + // in that case which is why we only check for end-of-file if no data was + // read. The caller will get none on their next call because there will be + // no data available and the end-of-file will be encountered again. + if C.feof(stream) != 0 { + return none + } + // If fread encountered an error, return it. Note that fread and ferror do + // not tell us what the error was, so we can't return anything more specific + // than there was an error. This is because fread and ferror do not set + // errno. + if C.ferror(stream) != 0 { + return error('file read error') + } + } + return nbytes +} + +// read_bytes reads bytes from the beginning of the file. +// Utility method, same as .read_bytes_at(size, 0). +pub fn (f &File) read_bytes(size int) []byte { + return f.read_bytes_at(size, 0) +} + +// read_bytes_at reads `size` bytes at the given position in the file. +pub fn (f &File) read_bytes_at(size int, pos u64) []byte { + mut arr := []byte{len: size} + nreadbytes := f.read_bytes_into(pos, mut arr) or { + // return err + return [] + } + return arr[0..nreadbytes] +} + +// read_bytes_into_newline reads from the beginning of the file into the provided buffer. +// Each consecutive call on the same file continues reading where it previously ended. +// A read call is either stopped, if the buffer is full, a newline was read or EOF. +pub fn (f &File) read_bytes_into_newline(mut buf []byte) ?int { + if buf.len == 0 { + panic(@FN + ': `buf.len` == 0') + } + newline := 10 + mut c := 0 + mut buf_ptr := 0 + mut nbytes := 0 + + stream := &C.FILE(f.cfile) + for (buf_ptr < buf.len) { + c = C.getc(stream) + match c { + C.EOF { + if C.feof(stream) != 0 { + return nbytes + } + if C.ferror(stream) != 0 { + return error('file read error') + } + } + newline { + buf[buf_ptr] = byte(c) + nbytes++ + return nbytes + } + else { + buf[buf_ptr] = byte(c) + buf_ptr++ + nbytes++ + } + } + } + return nbytes +} + +// read_bytes_into fills `buf` with bytes at the given position in the file. +// `buf` *must* have length greater than zero. +// Returns the number of read bytes, or an error. +pub fn (f &File) read_bytes_into(pos u64, mut buf []byte) ?int { + if buf.len == 0 { + panic(@FN + ': `buf.len` == 0') + } + $if x64 { + $if windows { + // Note: fseek errors if pos == os.file_size, which we accept + C._fseeki64(f.cfile, pos, C.SEEK_SET) + nbytes := fread(buf.data, 1, buf.len, f.cfile) ? + $if debug { + C._fseeki64(f.cfile, 0, C.SEEK_SET) + } + return nbytes + } $else { + C.fseeko(f.cfile, pos, C.SEEK_SET) + nbytes := fread(buf.data, 1, buf.len, f.cfile) ? + $if debug { + C.fseeko(f.cfile, 0, C.SEEK_SET) + } + return nbytes + } + } + $if x32 { + C.fseek(f.cfile, pos, C.SEEK_SET) + nbytes := fread(buf.data, 1, buf.len, f.cfile) ? + $if debug { + C.fseek(f.cfile, 0, C.SEEK_SET) + } + return nbytes + } + return error('Could not read file') +} + +// read_from implements the RandomReader interface. +pub fn (f &File) read_from(pos u64, mut buf []byte) ?int { + if buf.len == 0 { + return 0 + } + $if x64 { + $if windows { + C._fseeki64(f.cfile, pos, C.SEEK_SET) + } $else { + C.fseeko(f.cfile, pos, C.SEEK_SET) + } + + nbytes := fread(buf.data, 1, buf.len, f.cfile) ? + return nbytes + } + $if x32 { + C.fseek(f.cfile, pos, C.SEEK_SET) + nbytes := fread(buf.data, 1, buf.len, f.cfile) ? + return nbytes + } + return error('Could not read file') +} + +// **************************** Utility ops *********************** +// flush writes any buffered unwritten data left in the file stream. +pub fn (mut f File) flush() { + if !f.is_opened { + return + } + C.fflush(f.cfile) +} + +pub struct ErrFileNotOpened { + msg string = 'os: file not opened' + code int +} + +pub struct ErrSizeOfTypeIs0 { + msg string = 'os: size of type is 0' + code int +} + +fn error_file_not_opened() IError { + return IError(&ErrFileNotOpened{}) +} + +fn error_size_of_type_0() IError { + return IError(&ErrSizeOfTypeIs0{}) +} + +// read_struct reads a single struct of type `T` +pub fn (mut f File) read_struct(mut t T) ? { + if !f.is_opened { + return error_file_not_opened() + } + tsize := int(sizeof(*t)) + if tsize == 0 { + return error_size_of_type_0() + } + nbytes := fread(t, 1, tsize, f.cfile) ? + if nbytes != tsize { + return error_with_code('incomplete struct read', nbytes) + } +} + +// read_struct_at reads a single struct of type `T` at position specified in file +pub fn (mut f File) read_struct_at(mut t T, pos u64) ? { + if !f.is_opened { + return error_file_not_opened() + } + tsize := int(sizeof(*t)) + if tsize == 0 { + return error_size_of_type_0() + } + mut nbytes := 0 + $if x64 { + $if windows { + C._fseeki64(f.cfile, pos, C.SEEK_SET) + nbytes = fread(t, 1, tsize, f.cfile) ? + C._fseeki64(f.cfile, 0, C.SEEK_END) + } $else { + C.fseeko(f.cfile, pos, C.SEEK_SET) + nbytes = fread(t, 1, tsize, f.cfile) ? + C.fseeko(f.cfile, 0, C.SEEK_END) + } + } + $if x32 { + C.fseek(f.cfile, pos, C.SEEK_SET) + nbytes = fread(t, 1, tsize, f.cfile) ? + C.fseek(f.cfile, 0, C.SEEK_END) + } + if nbytes != tsize { + return error_with_code('incomplete struct read', nbytes) + } +} + +// read_raw reads and returns a single instance of type `T` +pub fn (mut f File) read_raw() ?T { + if !f.is_opened { + return error_file_not_opened() + } + tsize := int(sizeof(T)) + if tsize == 0 { + return error_size_of_type_0() + } + mut t := T{} + nbytes := fread(&t, 1, tsize, f.cfile) ? + if nbytes != tsize { + return error_with_code('incomplete struct read', nbytes) + } + return t +} + +// read_raw_at reads and returns a single instance of type `T` starting at file byte offset `pos` +pub fn (mut f File) read_raw_at(pos u64) ?T { + if !f.is_opened { + return error_file_not_opened() + } + tsize := int(sizeof(T)) + if tsize == 0 { + return error_size_of_type_0() + } + mut nbytes := 0 + mut t := T{} + $if x64 { + $if windows { + if C._fseeki64(f.cfile, pos, C.SEEK_SET) != 0 { + return error(posix_get_error_msg(C.errno)) + } + nbytes = fread(&t, 1, tsize, f.cfile) ? + if C._fseeki64(f.cfile, 0, C.SEEK_END) != 0 { + return error(posix_get_error_msg(C.errno)) + } + } $else { + if C.fseeko(f.cfile, pos, C.SEEK_SET) != 0 { + return error(posix_get_error_msg(C.errno)) + } + nbytes = fread(&t, 1, tsize, f.cfile) ? + if C.fseeko(f.cfile, 0, C.SEEK_END) != 0 { + return error(posix_get_error_msg(C.errno)) + } + } + } + $if x32 { + if C.fseek(f.cfile, pos, C.SEEK_SET) != 0 { + return error(posix_get_error_msg(C.errno)) + } + nbytes = fread(&t, 1, tsize, f.cfile) ? + if C.fseek(f.cfile, 0, C.SEEK_END) != 0 { + return error(posix_get_error_msg(C.errno)) + } + } + + if nbytes != tsize { + return error_with_code('incomplete struct read', nbytes) + } + return t +} + +// write_struct writes a single struct of type `T` +pub fn (mut f File) write_struct(t &T) ? { + if !f.is_opened { + return error_file_not_opened() + } + tsize := int(sizeof(T)) + if tsize == 0 { + return error_size_of_type_0() + } + C.errno = 0 + nbytes := int(C.fwrite(t, 1, tsize, f.cfile)) + if C.errno != 0 { + return error(posix_get_error_msg(C.errno)) + } + if nbytes != tsize { + return error_with_code('incomplete struct write', nbytes) + } +} + +// write_struct_at writes a single struct of type `T` at position specified in file +pub fn (mut f File) write_struct_at(t &T, pos u64) ? { + if !f.is_opened { + return error_file_not_opened() + } + tsize := int(sizeof(T)) + if tsize == 0 { + return error_size_of_type_0() + } + C.errno = 0 + mut nbytes := 0 + $if x64 { + $if windows { + C._fseeki64(f.cfile, pos, C.SEEK_SET) + nbytes = int(C.fwrite(t, 1, tsize, f.cfile)) + C._fseeki64(f.cfile, 0, C.SEEK_END) + } $else { + C.fseeko(f.cfile, pos, C.SEEK_SET) + nbytes = int(C.fwrite(t, 1, tsize, f.cfile)) + C.fseeko(f.cfile, 0, C.SEEK_END) + } + } + $if x32 { + C.fseek(f.cfile, pos, C.SEEK_SET) + nbytes = int(C.fwrite(t, 1, tsize, f.cfile)) + C.fseek(f.cfile, 0, C.SEEK_END) + } + if C.errno != 0 { + return error(posix_get_error_msg(C.errno)) + } + if nbytes != tsize { + return error_with_code('incomplete struct write', nbytes) + } +} + +// TODO `write_raw[_at]` implementations are copy-pasted from `write_struct[_at]` + +// write_raw writes a single instance of type `T` +pub fn (mut f File) write_raw(t &T) ? { + if !f.is_opened { + return error_file_not_opened() + } + tsize := int(sizeof(T)) + if tsize == 0 { + return error_size_of_type_0() + } + C.errno = 0 + nbytes := int(C.fwrite(t, 1, tsize, f.cfile)) + if C.errno != 0 { + return error(posix_get_error_msg(C.errno)) + } + if nbytes != tsize { + return error_with_code('incomplete struct write', nbytes) + } +} + +// write_raw_at writes a single instance of type `T` starting at file byte offset `pos` +pub fn (mut f File) write_raw_at(t &T, pos u64) ? { + if !f.is_opened { + return error_file_not_opened() + } + tsize := int(sizeof(T)) + if tsize == 0 { + return error_size_of_type_0() + } + mut nbytes := 0 + + $if x64 { + $if windows { + if C._fseeki64(f.cfile, pos, C.SEEK_SET) != 0 { + return error(posix_get_error_msg(C.errno)) + } + nbytes = int(C.fwrite(t, 1, tsize, f.cfile)) + if C.errno != 0 { + return error(posix_get_error_msg(C.errno)) + } + if C._fseeki64(f.cfile, 0, C.SEEK_END) != 0 { + return error(posix_get_error_msg(C.errno)) + } + } $else { + if C.fseeko(f.cfile, pos, C.SEEK_SET) != 0 { + return error(posix_get_error_msg(C.errno)) + } + nbytes = int(C.fwrite(t, 1, tsize, f.cfile)) + if C.errno != 0 { + return error(posix_get_error_msg(C.errno)) + } + if C.fseeko(f.cfile, 0, C.SEEK_END) != 0 { + return error(posix_get_error_msg(C.errno)) + } + } + } + $if x32 { + if C.fseek(f.cfile, pos, C.SEEK_SET) != 0 { + return error(posix_get_error_msg(C.errno)) + } + nbytes = int(C.fwrite(t, 1, tsize, f.cfile)) + if C.errno != 0 { + return error(posix_get_error_msg(C.errno)) + } + if C.fseek(f.cfile, 0, C.SEEK_END) != 0 { + return error(posix_get_error_msg(C.errno)) + } + } + + if nbytes != tsize { + return error_with_code('incomplete struct write', nbytes) + } +} + +pub enum SeekMode { + start + current + end +} + +// seek moves the file cursor (if any) associated with a file +// to a new location, offset `pos` bytes from the origin. The origin +// is dependent on the `mode` and can be: +// .start -> the origin is the start of the file +// .current -> the current position/cursor in the file +// .end -> the end of the file +// If the file is not seek-able, or an error occures, the error will +// be returned to the caller. +// A successful call to the fseek() function clears the end-of-file +// indicator for the file. +pub fn (mut f File) seek(pos i64, mode SeekMode) ? { + if !f.is_opened { + return error_file_not_opened() + } + whence := int(mode) + mut res := 0 + $if x64 { + $if windows { + res = C._fseeki64(f.cfile, pos, whence) + } $else { + res = C.fseeko(f.cfile, pos, whence) + } + } + $if x32 { + res = C.fseek(f.cfile, pos, whence) + } + if res == -1 { + return error(posix_get_error_msg(C.errno)) + } +} + +// tell will return the current offset of the file cursor measured from +// the start of the file, in bytes. It is complementary to seek, i.e. +// you can use the return value as the `pos` parameter to .seek( pos, .start ), +// so that your next read will happen from the same place. +pub fn (f &File) tell() ?i64 { + if !f.is_opened { + return error_file_not_opened() + } + pos := C.ftell(f.cfile) + if pos == -1 { + return error(posix_get_error_msg(C.errno)) + } + return pos +} diff --git a/v_windows/v/old/vlib/os/file_test.v b/v_windows/v/old/vlib/os/file_test.v new file mode 100644 index 0000000..648c81c --- /dev/null +++ b/v_windows/v/old/vlib/os/file_test.v @@ -0,0 +1,372 @@ +import os + +struct Point { + x f64 + y f64 + z f64 +} + +struct Extended_Point { + a f64 + b f64 + c f64 + d f64 + e f64 + f f64 + g f64 + h f64 + i f64 +} + +enum Color { + red + green + blue +} + +[flag] +enum Permissions { + read + write + execute +} + +const ( + unit_point = Point{1.0, 1.0, 1.0} + another_point = Point{0.25, 2.25, 6.25} + extended_point = Extended_Point{1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0} + another_byte = byte(123) + another_color = Color.red + another_permission = Permissions.read | .write +) + +const ( + tfolder = os.join_path(os.temp_dir(), 'os_file_test') + tfile = os.join_path(tfolder, 'test_file') +) + +fn testsuite_begin() ? { + os.rmdir_all(tfolder) or {} + assert !os.is_dir(tfolder) + os.mkdir_all(tfolder) ? + os.chdir(tfolder) + assert os.is_dir(tfolder) +} + +fn testsuite_end() ? { + os.chdir(os.wd_at_startup) + os.rmdir_all(tfolder) ? + assert !os.is_dir(tfolder) +} + +// test_read_bytes_into_newline_text tests reading text from a file with newlines. +// This test simulates reading a larger text file step by step into a buffer and +// returning on each newline, even before the buffer is full, and reaching EOF before +// the buffer is completely filled. +fn test_read_bytes_into_newline_text() ? { + mut f := os.open_file(tfile, 'w') ? + f.write_string('Hello World!\nGood\r morning.') ? + f.close() + + f = os.open_file(tfile, 'r') ? + mut buf := []byte{len: 8} + + n0 := f.read_bytes_into_newline(mut buf) ? + assert n0 == 8 + + n1 := f.read_bytes_into_newline(mut buf) ? + assert n1 == 5 + + n2 := f.read_bytes_into_newline(mut buf) ? + assert n2 == 8 + + n3 := f.read_bytes_into_newline(mut buf) ? + assert n3 == 6 + + f.close() +} + +// test_read_bytes_into_newline_binary tests reading a binary file with NUL bytes. +// This test simulates the scenario when a byte stream is read and a newline byte +// appears in that stream and an EOF occurs before the buffer is full. +fn test_read_bytes_into_newline_binary() ? { + os.rm(tfile) or {} // FIXME This is a workaround for macos, because the file isn't truncated when open with 'w' + mut bw := []byte{len: 15} + bw[9] = 0xff + bw[12] = 10 // newline + + n0_bytes := bw[0..10] + n1_bytes := bw[10..13] + n2_bytes := bw[13..] + + mut f := os.open_file(tfile, 'w') ? + f.write(bw) ? + f.close() + + f = os.open_file(tfile, 'r') ? + mut buf := []byte{len: 10} + + n0 := f.read_bytes_into_newline(mut buf) ? + assert n0 == 10 + assert buf[..n0] == n0_bytes + + n1 := f.read_bytes_into_newline(mut buf) ? + assert n1 == 3 + assert buf[..n1] == n1_bytes + + n2 := f.read_bytes_into_newline(mut buf) ? + assert n2 == 2 + assert buf[..n2] == n2_bytes + f.close() +} + +// test_read_eof_last_read_partial_buffer_fill tests that when reading a file +// the end-of-file is detected and results in a none error being returned. This +// test simulates file reading where the end-of-file is reached inside an fread +// containing data. +fn test_read_eof_last_read_partial_buffer_fill() ? { + mut f := os.open_file(tfile, 'w') ? + bw := []byte{len: 199, init: 5} + f.write(bw) ? + f.close() + + f = os.open_file(tfile, 'r') ? + mut br := []byte{len: 100} + // Read first 100 bytes of 199 byte file, should fill buffer with no error. + n0 := f.read(mut br) ? + assert n0 == 100 + // Read remaining 99 bytes of 199 byte file, should fill buffer with no + // error, even though end-of-file was reached. + n1 := f.read(mut br) ? + assert n1 == 99 + // Read again, end-of-file was previously reached so should return none + // error. + if _ := f.read(mut br) { + // This is not intended behavior because the read function should + // not return a number of bytes read when end-of-file is reached. + assert false + } else { + // Expect none to have been returned when end-of-file. + assert err is none + } + f.close() +} + +// test_read_eof_last_read_full_buffer_fill tests that when reading a file the +// end-of-file is detected and results in a none error being returned. This test +// simulates file reading where the end-of-file is reached at the beinning of an +// fread that returns no data. +fn test_read_eof_last_read_full_buffer_fill() ? { + mut f := os.open_file(tfile, 'w') ? + bw := []byte{len: 200, init: 5} + f.write(bw) ? + f.close() + + f = os.open_file(tfile, 'r') ? + mut br := []byte{len: 100} + // Read first 100 bytes of 200 byte file, should fill buffer with no error. + n0 := f.read(mut br) ? + assert n0 == 100 + // Read remaining 100 bytes of 200 byte file, should fill buffer with no + // error. The end-of-file isn't reached yet, but there is no more data. + n1 := f.read(mut br) ? + assert n1 == 100 + // Read again, end-of-file was previously reached so should return none + // error. + if _ := f.read(mut br) { + // This is not intended behavior because the read function should + // not return a number of bytes read when end-of-file is reached. + assert false + } else { + // Expect none to have been returned when end-of-file. + assert err is none + } + f.close() +} + +fn test_write_struct() ? { + os.rm(tfile) or {} // FIXME This is a workaround for macos, because the file isn't truncated when open with 'w' + size_of_point := int(sizeof(Point)) + mut f := os.open_file(tfile, 'w') ? + f.write_struct(another_point) ? + f.close() + x := os.read_file(tfile) ? + pcopy := unsafe { &byte(memdup(&another_point, size_of_point)) } + y := unsafe { pcopy.vstring_with_len(size_of_point) } + assert x == y + $if debug { + eprintln(x.bytes()) + eprintln(y.bytes()) + } +} + +fn test_write_struct_at() ? { + mut f := os.open_file(tfile, 'w') ? + f.write_struct(extended_point) ? + f.write_struct_at(another_point, 3) ? + f.close() + f = os.open_file(tfile, 'r') ? + mut p := Point{} + f.read_struct_at(mut p, 3) ? + f.close() + + assert p == another_point +} + +fn test_read_struct() ? { + mut f := os.open_file(tfile, 'w') ? + f.write_struct(another_point) ? + f.close() + + f = os.open_file(tfile, 'r') ? + mut p := Point{} + f.read_struct(mut p) ? + f.close() + + assert p == another_point +} + +fn test_read_struct_at() ? { + mut f := os.open_file(tfile, 'w') ? + f.write([byte(1), 2, 3]) ? + f.write_struct(another_point) ? + f.close() + f = os.open_file(tfile, 'r') ? + mut p := Point{} + f.read_struct_at(mut p, 3) ? + f.close() + + assert p == another_point +} + +fn test_write_raw() ? { + os.rm(tfile) or {} // FIXME This is a workaround for macos, because the file isn't truncated when open with 'w' + size_of_point := int(sizeof(Point)) + mut f := os.open_file(tfile, 'w') ? + f.write_raw(another_point) ? + f.close() + x := os.read_file(tfile) ? + pcopy := unsafe { &byte(memdup(&another_point, size_of_point)) } + y := unsafe { pcopy.vstring_with_len(size_of_point) } + assert x == y + $if debug { + eprintln(x.bytes()) + eprintln(y.bytes()) + } +} + +fn test_write_raw_at() ? { + mut f := os.open_file(tfile, 'w') ? + f.write_raw(extended_point) ? + f.write_raw_at(another_point, 3) ? + f.close() + f = os.open_file(tfile, 'r') ? + mut p := Point{} + f.read_struct_at(mut p, 3) ? + f.close() + + assert p == another_point +} + +fn test_write_raw_at_negative_pos() ? { + mut f := os.open_file(tfile, 'w') ? + if _ := f.write_raw_at(another_point, -1) { + assert false + } + f.write_raw_at(another_point, -234) or { assert err.msg == 'Invalid argument' } + f.close() +} + +fn test_read_raw() ? { + mut f := os.open_file(tfile, 'w') ? + f.write_raw(another_point) ? + f.write_raw(another_byte) ? + f.write_raw(another_color) ? + f.write_raw(another_permission) ? + f.close() + f = os.open_file(tfile, 'r') ? + p := f.read_raw() ? + b := f.read_raw() ? + c := f.read_raw() ? + x := f.read_raw() ? + f.close() + + assert p == another_point + assert b == another_byte + assert c == another_color + assert x == another_permission +} + +fn test_read_raw_at() ? { + mut f := os.open_file(tfile, 'w') ? + f.write([byte(1), 2, 3]) ? + f.write_raw(another_point) ? + f.write_raw(another_byte) ? + f.write_raw(another_color) ? + f.write_raw(another_permission) ? + f.close() + f = os.open_file(tfile, 'r') ? + mut at := u64(3) + p := f.read_raw_at(at) ? + at += sizeof(Point) + b := f.read_raw_at(at) ? + at += sizeof(byte) + c := f.read_raw_at(at) ? + at += sizeof(Color) + x := f.read_raw_at(at) ? + at += sizeof(Permissions) + f.close() + + assert p == another_point + assert b == another_byte + assert c == another_color + assert x == another_permission +} + +fn test_read_raw_at_negative_pos() ? { + mut f := os.open_file(tfile, 'r') ? + if _ := f.read_raw_at(-1) { + assert false + } + f.read_raw_at(-234) or { assert err.msg == 'Invalid argument' } + f.close() +} + +fn test_seek() ? { + mut f := os.open_file(tfile, 'w') ? + f.write_raw(another_point) ? + f.write_raw(another_byte) ? + f.write_raw(another_color) ? + f.write_raw(another_permission) ? + f.close() + + // println('> ${sizeof(Point)} ${sizeof(byte)} ${sizeof(Color)} ${sizeof(Permissions)}') + f = os.open_file(tfile, 'r') ? + // + f.seek(i64(sizeof(Point)), .start) ? + assert f.tell() ? == sizeof(Point) + b := f.read_raw() ? + assert b == another_byte + + f.seek(i64(sizeof(Color)), .current) ? + x := f.read_raw() ? + assert x == another_permission + // + f.close() +} + +fn test_tell() ? { + for size in 10 .. 30 { + s := 'x'.repeat(size) + os.write_file(tfile, s) ? + fs := os.file_size(tfile) + assert int(fs) == size + // + mut f := os.open_file(tfile, 'r') ? + f.seek(-5, .end) ? + pos := f.tell() ? + f.close() + // dump(pos) + assert pos == size - 5 + } +} diff --git a/v_windows/v/old/vlib/os/glob_test.v b/v_windows/v/old/vlib/os/glob_test.v new file mode 100644 index 0000000..c47311b --- /dev/null +++ b/v_windows/v/old/vlib/os/glob_test.v @@ -0,0 +1,80 @@ +import os + +fn deep_glob() ? { + os.chdir(@VMODROOT) + matches := os.glob('vlib/v/*/*.v') or { panic(err) } + assert matches.len > 10 + assert 'vlib/v/ast/ast.v' in matches + assert 'vlib/v/ast/table.v' in matches + assert 'vlib/v/token/token.v' in matches + for f in matches { + if !f.starts_with('vlib/v/') { + assert false + } + assert f.ends_with('.v') + } +} + +fn redeep_glob() ? { + os.chdir(@VMODROOT) + matches := os.glob('vlib/v/**/*.v') or { panic(err) } + assert matches.len > 10 + assert 'vlib/v/ast/ast.v' in matches + assert 'vlib/v/ast/table.v' in matches + assert 'vlib/v/token/token.v' in matches + for f in matches { + if !f.starts_with('vlib/v/') { + assert false + } + assert f.ends_with('.v') + } +} + +fn test_glob_can_find_v_files_3_levels_deep() ? { + $if !windows { + deep_glob() ? + redeep_glob() ? + } + assert true +} + +fn test_glob_can_find_files_in_current_folder() ? { + os.chdir(@VMODROOT) + matches := os.glob('*') ? + assert '.gitignore' in matches + assert 'make.bat' in matches + assert 'Makefile' in matches + assert 'Dockerfile' in matches + assert 'README.md' in matches + assert 'v.mod' in matches + assert 'cmd/' in matches + assert 'vlib/' in matches + assert 'thirdparty/' in matches +} + +fn test_glob_can_be_used_with_multiple_patterns() ? { + os.chdir(@VMODROOT) + matches := os.glob('*', 'cmd/tools/*') ? + assert 'README.md' in matches + assert 'Makefile' in matches + $if !windows { + assert 'cmd/tools/test_if_v_test_system_works.v' in matches + } + $if windows { + assert 'test_if_v_test_system_works.v' in matches + } +} + +fn test_glob_star() ? { + os.chdir(@VMODROOT) + matches := os.glob('*ake*') ? + assert 'Makefile' in matches + assert 'make.bat' in matches +} + +fn test_glob_not_found() ? { + os.glob('an_unknown_folder/*.v') or { + assert true + return + } +} diff --git a/v_windows/v/old/vlib/os/inode.c.v b/v_windows/v/old/vlib/os/inode.c.v new file mode 100644 index 0000000..3c6b19b --- /dev/null +++ b/v_windows/v/old/vlib/os/inode.c.v @@ -0,0 +1,92 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module os + +enum FileType { + regular + directory + character_device + block_device + fifo + symbolic_link + socket +} + +struct FilePermission { +pub: + read bool + write bool + execute bool +} + +struct FileMode { +pub: + typ FileType + owner FilePermission + group FilePermission + others FilePermission +} + +// inode returns the mode of the file/inode containing inode type and permission information +// it supports windows for regular files but it doesn't matter if you use owner, group or others when checking permissions on windows +pub fn inode(path string) FileMode { + mut attr := C.stat{} + unsafe { C.stat(&char(path.str), &attr) } + mut typ := FileType.regular + if attr.st_mode & u32(C.S_IFMT) == u32(C.S_IFDIR) { + typ = .directory + } + $if !windows { + if attr.st_mode & u32(C.S_IFMT) == u32(C.S_IFCHR) { + typ = .character_device + } else if attr.st_mode & u32(C.S_IFMT) == u32(C.S_IFBLK) { + typ = .block_device + } else if attr.st_mode & u32(C.S_IFMT) == u32(C.S_IFIFO) { + typ = .fifo + } else if attr.st_mode & u32(C.S_IFMT) == u32(C.S_IFLNK) { + typ = .symbolic_link + } else if attr.st_mode & u32(C.S_IFMT) == u32(C.S_IFSOCK) { + typ = .socket + } + } + $if windows { + return FileMode{ + typ: typ + owner: FilePermission{ + read: (attr.st_mode & u32(C.S_IREAD)) != 0 + write: (attr.st_mode & u32(C.S_IWRITE)) != 0 + execute: (attr.st_mode & u32(C.S_IEXEC)) != 0 + } + group: FilePermission{ + read: (attr.st_mode & u32(C.S_IREAD)) != 0 + write: (attr.st_mode & u32(C.S_IWRITE)) != 0 + execute: (attr.st_mode & u32(C.S_IEXEC)) != 0 + } + others: FilePermission{ + read: (attr.st_mode & u32(C.S_IREAD)) != 0 + write: (attr.st_mode & u32(C.S_IWRITE)) != 0 + execute: (attr.st_mode & u32(C.S_IEXEC)) != 0 + } + } + } $else { + return FileMode{ + typ: typ + owner: FilePermission{ + read: (attr.st_mode & u32(C.S_IRUSR)) != 0 + write: (attr.st_mode & u32(C.S_IWUSR)) != 0 + execute: (attr.st_mode & u32(C.S_IXUSR)) != 0 + } + group: FilePermission{ + read: (attr.st_mode & u32(C.S_IRGRP)) != 0 + write: (attr.st_mode & u32(C.S_IWGRP)) != 0 + execute: (attr.st_mode & u32(C.S_IXGRP)) != 0 + } + others: FilePermission{ + read: (attr.st_mode & u32(C.S_IROTH)) != 0 + write: (attr.st_mode & u32(C.S_IWOTH)) != 0 + execute: (attr.st_mode & u32(C.S_IXOTH)) != 0 + } + } + } +} diff --git a/v_windows/v/old/vlib/os/inode_test.v b/v_windows/v/old/vlib/os/inode_test.v new file mode 100644 index 0000000..8cd8307 --- /dev/null +++ b/v_windows/v/old/vlib/os/inode_test.v @@ -0,0 +1,43 @@ +module os + +const ( + // tfolder will contain all the temporary files/subfolders made by + // the different tests. It would be removed in testsuite_end(), so + // individual os tests do not need to clean up after themselves. + tfolder = join_path(temp_dir(), 'v', 'tests', 'inode_test') +) + +fn testsuite_begin() { + eprintln('testsuite_begin, tfolder = $os.tfolder') + rmdir_all(os.tfolder) or {} + assert !is_dir(os.tfolder) + mkdir_all(os.tfolder) or { panic(err) } + chdir(os.tfolder) + assert is_dir(os.tfolder) +} + +fn testsuite_end() { + chdir(wd_at_startup) + rmdir_all(os.tfolder) or { panic(err) } + assert !is_dir(os.tfolder) +} + +fn test_inode_file_type() { + filename := './test1.txt' + mut file := open_file(filename, 'w', 0o600) or { return } + file.close() + mode := inode(filename) + rm(filename) or { panic(err) } + assert mode.typ == .regular +} + +fn test_inode_file_owner_permission() { + filename := './test2.txt' + mut file := open_file(filename, 'w', 0o600) or { return } + file.close() + mode := inode(filename) + rm(filename) or {} + assert mode.owner.read + assert mode.owner.write + assert !mode.owner.execute +} diff --git a/v_windows/v/old/vlib/os/notify/backend_default.c.v b/v_windows/v/old/vlib/os/notify/backend_default.c.v new file mode 100644 index 0000000..1a35c50 --- /dev/null +++ b/v_windows/v/old/vlib/os/notify/backend_default.c.v @@ -0,0 +1,6 @@ +module notify + +// Implement the API +pub fn new() ?FdNotifier { + panic('unsupported') +} diff --git a/v_windows/v/old/vlib/os/notify/backend_linux.c.v b/v_windows/v/old/vlib/os/notify/backend_linux.c.v new file mode 100644 index 0000000..1913913 --- /dev/null +++ b/v_windows/v/old/vlib/os/notify/backend_linux.c.v @@ -0,0 +1,206 @@ +module notify + +import time +import os + +#include + +struct C.epoll_event { + events u32 + data C.epoll_data_t +} + +[typedef] +union C.epoll_data_t { + ptr voidptr + fd int + u32 u32 + u64 u64 +} + +fn C.epoll_create1(int) int + +fn C.epoll_ctl(int, int, int, &C.epoll_event) int + +fn C.epoll_wait(int, &C.epoll_event, int, int) int + +// EpollNotifier provides methods that implement FdNotifier using the +// epoll I/O event notification facility (linux only) +[noinit] +struct EpollNotifier { + epoll_fd int +} + +// EpollEvent describes an event that occurred for a file descriptor in +// the watch list +[noinit] +struct EpollEvent { +pub: + fd int + kind FdEventType +} + +// new creates a new EpollNotifier +// The FdNotifier interface is returned to allow OS specific +// implementations without exposing the concrete type +pub fn new() ?FdNotifier { + fd := C.epoll_create1(0) // 0 indicates default behavior + if fd == -1 { + return error(os.posix_get_error_msg(C.errno)) + } + // Needed to circumvent V limitations + x := &EpollNotifier{ + epoll_fd: fd + } + return x +} + +const ( + epoll_read = u32(C.EPOLLIN) + epoll_write = u32(C.EPOLLOUT) + epoll_peer_hangup = u32(C.EPOLLRDHUP) + epoll_exception = u32(C.EPOLLPRI) + epoll_error = u32(C.EPOLLERR) + epoll_hangup = u32(C.EPOLLHUP) + epoll_edge_trigger = u32(C.EPOLLET) + epoll_one_shot = u32(C.EPOLLONESHOT) + epoll_wake_up = u32(C.EPOLLWAKEUP) + epoll_exclusive = u32(C.EPOLLEXCLUSIVE) +) + +// ctl is a helper method for add, modify, and remove +fn (mut en EpollNotifier) ctl(fd int, op int, mask u32) ? { + event := C.epoll_event{ + events: mask + data: C.epoll_data_t{ + fd: fd + } + } + if C.epoll_ctl(en.epoll_fd, op, fd, &event) == -1 { + return error(os.posix_get_error_msg(C.errno)) + } +} + +// add adds a file descriptor to the watch list +fn (mut en EpollNotifier) add(fd int, events FdEventType, conf ...FdConfigFlags) ? { + mask := flags_to_mask(events, ...conf) + en.ctl(fd, C.EPOLL_CTL_ADD, mask) ? +} + +// modify sets an existing entry in the watch list to the provided events and configuration +fn (mut en EpollNotifier) modify(fd int, events FdEventType, conf ...FdConfigFlags) ? { + mask := flags_to_mask(events, ...conf) + en.ctl(fd, C.EPOLL_CTL_MOD, mask) ? +} + +// remove removes a file descriptor from the watch list +fn (mut en EpollNotifier) remove(fd int) ? { + en.ctl(fd, C.EPOLL_CTL_DEL, 0) ? +} + +// wait waits to be notified of events on the watch list, +// returns at most 512 events +fn (mut en EpollNotifier) wait(timeout time.Duration) []FdEvent { + // arbitrary 512 limit; events will round robin on successive + // waits if the number exceeds this + // NOTE: we use a fixed size array here for stack allocation; this has + // the added bonus of making EpollNotifier thread safe + events := [512]C.epoll_event{} + // populate events with the new events + to := timeout.sys_milliseconds() + count := C.epoll_wait(en.epoll_fd, &events[0], events.len, to) + + if count > 0 { + mut arr := []FdEvent{cap: count} + for i := 0; i < count; i++ { + fd := unsafe { events[i].data.fd } + kind := event_mask_to_flag(events[i].events) + if kind.is_empty() { + // NOTE: tcc only reports the first event for some + // reason, leaving subsequent structs in the array as 0 + // (or possibly garbage) + panic('encountered an empty event kind; this is most likely due to using tcc') + } + arr << &EpollEvent{ + fd: fd + kind: kind + } + } + return arr + } + return [] +} + +// close closes the EpollNotifier, +// any successive calls to add, modify, remove, and wait should fail +fn (mut en EpollNotifier) close() ? { + if C.close(en.epoll_fd) == -1 { + return error(os.posix_get_error_msg(C.errno)) + } +} + +// event_mask_to_flag is a helper function that converts a bitmask +// returned by epoll_wait to FdEventType +fn event_mask_to_flag(mask u32) FdEventType { + mut flags := FdEventType{} + + if mask & notify.epoll_read != 0 { + flags.set(.read) + } + if mask & notify.epoll_write != 0 { + flags.set(.write) + } + if mask & notify.epoll_peer_hangup != 0 { + flags.set(.peer_hangup) + } + if mask & notify.epoll_exception != 0 { + flags.set(.exception) + } + if mask & notify.epoll_error != 0 { + flags.set(.error) + } + if mask & notify.epoll_hangup != 0 { + flags.set(.hangup) + } + + return flags +} + +// flags_to_mask is a helper function that converts FdEventType and +// FdConfigFlags to a bitmask used by the C functions +fn flags_to_mask(events FdEventType, confs ...FdConfigFlags) u32 { + mut mask := u32(0) + if events.has(.read) { + mask |= notify.epoll_read + } + if events.has(.write) { + mask |= notify.epoll_write + } + if events.has(.peer_hangup) { + mask |= notify.epoll_peer_hangup + } + if events.has(.exception) { + mask |= notify.epoll_exception + } + if events.has(.error) { + mask |= notify.epoll_error + } + if events.has(.hangup) { + mask |= notify.epoll_hangup + } + for conf in confs { + if conf.has(.edge_trigger) { + mask |= notify.epoll_edge_trigger + } + if conf.has(.one_shot) { + mask |= notify.epoll_one_shot + } + if conf.has(.wake_up) { + mask |= notify.epoll_wake_up + } + if conf.has(.exclusive) { + mask |= notify.epoll_exclusive + } + } + return mask +} diff --git a/v_windows/v/old/vlib/os/notify/notify.v b/v_windows/v/old/vlib/os/notify/notify.v new file mode 100644 index 0000000..b49dad3 --- /dev/null +++ b/v_windows/v/old/vlib/os/notify/notify.v @@ -0,0 +1,35 @@ +module notify + +import time + +// Backends should provide a `new() ?FdNotifier` function +pub interface FdNotifier { + add(fd int, events FdEventType, conf ...FdConfigFlags) ? + modify(fd int, events FdEventType, conf ...FdConfigFlags) ? + remove(fd int) ? + wait(timeout time.Duration) []FdEvent + close() ? +} + +pub interface FdEvent { + fd int + kind FdEventType +} + +[flag] +pub enum FdEventType { + read + write + peer_hangup + exception + error + hangup +} + +[flag] +pub enum FdConfigFlags { + edge_trigger + one_shot + wake_up + exclusive +} diff --git a/v_windows/v/old/vlib/os/notify/notify_test.v b/v_windows/v/old/vlib/os/notify/notify_test.v new file mode 100644 index 0000000..253c94f --- /dev/null +++ b/v_windows/v/old/vlib/os/notify/notify_test.v @@ -0,0 +1,155 @@ +import os +import os.notify + +// make a pipe and return the (read, write) file descriptors +fn make_pipe() ?(int, int) { + $if linux { + pipefd := [2]int{} + if C.pipe(&pipefd[0]) != 0 { + return error('error $C.errno: ' + os.posix_get_error_msg(C.errno)) + } + return pipefd[0], pipefd[1] + } + return -1, -1 +} + +fn test_level_trigger() ? { + // currently only linux is supported + $if linux { + mut notifier := notify.new() ? + reader, writer := make_pipe() ? + defer { + os.fd_close(reader) + os.fd_close(writer) + notifier.close() or {} + } + notifier.add(reader, .read) ? + + os.fd_write(writer, 'foobar') + check_read_event(notifier, reader, 'foo') + check_read_event(notifier, reader, 'bar') + + assert notifier.wait(0).len == 0 + } +} + +fn test_edge_trigger() ? { + // currently only linux is supported + $if linux { + mut notifier := notify.new() ? + reader, writer := make_pipe() ? + defer { + os.fd_close(reader) + os.fd_close(writer) + notifier.close() or {} + } + notifier.add(reader, .read, .edge_trigger) ? + + os.fd_write(writer, 'foobar') + check_read_event(notifier, reader, 'foo') + + assert notifier.wait(0).len == 0 + + os.fd_write(writer, 'baz') + // we do not get an event because there is still data + // to be read + assert notifier.wait(0).len == 0 + } +} + +fn test_one_shot() ? { + $if linux { + mut notifier := notify.new() ? + reader, writer := make_pipe() ? + defer { + os.fd_close(reader) + os.fd_close(writer) + notifier.close() or {} + } + notifier.add(reader, .read, .one_shot) ? + + os.fd_write(writer, 'foobar') + check_read_event(notifier, reader, 'foo') + os.fd_write(writer, 'baz') + + assert notifier.wait(0).len == 0 + + // rearm + notifier.modify(reader, .read) ? + check_read_event(notifier, reader, 'barbaz') + } +} + +fn test_hangup() ? { + $if linux { + mut notifier := notify.new() ? + reader, writer := make_pipe() ? + defer { + os.fd_close(reader) + notifier.close() or {} + } + notifier.add(reader, .hangup) ? + + assert notifier.wait(0).len == 0 + + // closing on the writer end of the pipe will + // cause a hangup on the reader end + os.fd_close(writer) + events := notifier.wait(0) + assert events.len == 1 + assert events[0].fd == reader + assert events[0].kind.has(.hangup) + } +} + +fn test_write() ? { + $if linux { + mut notifier := notify.new() ? + reader, writer := make_pipe() ? + defer { + os.fd_close(reader) + os.fd_close(writer) + notifier.close() or {} + } + + notifier.add(reader, .write) ? + assert notifier.wait(0).len == 0 + + notifier.add(writer, .write) ? + events := notifier.wait(0) + assert events.len == 1 + assert events[0].fd == writer + assert events[0].kind.has(.write) + } +} + +fn test_remove() ? { + $if linux { + mut notifier := notify.new() ? + reader, writer := make_pipe() ? + defer { + os.fd_close(reader) + os.fd_close(writer) + notifier.close() or {} + } + + // level triggered - will keep getting events while + // there is data to read + notifier.add(reader, .read) ? + os.fd_write(writer, 'foobar') + assert notifier.wait(0).len == 1 + assert notifier.wait(0).len == 1 + + notifier.remove(reader) ? + assert notifier.wait(0).len == 0 + } +} + +fn check_read_event(notifier notify.FdNotifier, reader_fd int, expected string) { + events := notifier.wait(0) + assert events.len == 1 + assert events[0].fd == reader_fd + assert events[0].kind.has(.read) + s, _ := os.fd_read(events[0].fd, expected.len) + assert s == expected +} diff --git a/v_windows/v/old/vlib/os/os.v b/v_windows/v/old/vlib/os/os.v new file mode 100644 index 0000000..088aeb7 --- /dev/null +++ b/v_windows/v/old/vlib/os/os.v @@ -0,0 +1,662 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module os + +pub const ( + args = []string{} + max_path_len = 4096 + wd_at_startup = getwd() +) + +const ( + f_ok = 0 + x_ok = 1 + w_ok = 2 + r_ok = 4 +) + +pub struct Result { +pub: + exit_code int + output string + // stderr string // TODO +} + +[unsafe] +pub fn (mut result Result) free() { + unsafe { result.output.free() } +} + +// cp_all will recursively copy `src` to `dst`, +// optionally overwriting files or dirs in `dst`. +pub fn cp_all(src string, dst string, overwrite bool) ? { + source_path := real_path(src) + dest_path := real_path(dst) + if !exists(source_path) { + return error("Source path doesn't exist") + } + // single file copy + if !is_dir(source_path) { + adjusted_path := if is_dir(dest_path) { + join_path(dest_path, file_name(source_path)) + } else { + dest_path + } + if exists(adjusted_path) { + if overwrite { + rm(adjusted_path) ? + } else { + return error('Destination file path already exist') + } + } + cp(source_path, adjusted_path) ? + return + } + if !exists(dest_path) { + mkdir(dest_path) ? + } + if !is_dir(dest_path) { + return error('Destination path is not a valid directory') + } + files := ls(source_path) ? + for file in files { + sp := join_path(source_path, file) + dp := join_path(dest_path, file) + if is_dir(sp) { + if !exists(dp) { + mkdir(dp) ? + } + } + cp_all(sp, dp, overwrite) or { + rmdir(dp) or { return err } + return err + } + } +} + +// mv_by_cp first copies the source file, and if it is copied successfully, deletes the source file. +// may be used when you are not sure that the source and target are on the same mount/partition. +pub fn mv_by_cp(source string, target string) ? { + cp(source, target) ? + rm(source) ? +} + +// read_lines reads the file in `path` into an array of lines. +pub fn read_lines(path string) ?[]string { + buf := read_file(path) ? + res := buf.split_into_lines() + unsafe { buf.free() } + return res +} + +// sigint_to_signal_name will translate `si` signal integer code to it's string code representation. +pub fn sigint_to_signal_name(si int) string { + // POSIX signals: + match si { + 1 { return 'SIGHUP' } + 2 { return 'SIGINT' } + 3 { return 'SIGQUIT' } + 4 { return 'SIGILL' } + 6 { return 'SIGABRT' } + 8 { return 'SIGFPE' } + 9 { return 'SIGKILL' } + 11 { return 'SIGSEGV' } + 13 { return 'SIGPIPE' } + 14 { return 'SIGALRM' } + 15 { return 'SIGTERM' } + else {} + } + $if linux { + // From `man 7 signal` on linux: + match si { + // TODO dependent on platform + // works only on x86/ARM/most others + 10 /* , 30, 16 */ { return 'SIGUSR1' } + 12 /* , 31, 17 */ { return 'SIGUSR2' } + 17 /* , 20, 18 */ { return 'SIGCHLD' } + 18 /* , 19, 25 */ { return 'SIGCONT' } + 19 /* , 17, 23 */ { return 'SIGSTOP' } + 20 /* , 18, 24 */ { return 'SIGTSTP' } + 21 /* , 26 */ { return 'SIGTTIN' } + 22 /* , 27 */ { return 'SIGTTOU' } + // ///////////////////////////// + 5 { return 'SIGTRAP' } + 7 { return 'SIGBUS' } + else {} + } + } + return 'unknown' +} + +// rmdir_all recursively removes the specified directory. +pub fn rmdir_all(path string) ? { + mut ret_err := '' + items := ls(path) ? + for item in items { + fullpath := join_path(path, item) + if is_dir(fullpath) { + rmdir_all(fullpath) or { ret_err = err.msg } + } else { + rm(fullpath) or { ret_err = err.msg } + } + } + rmdir(path) or { ret_err = err.msg } + if ret_err.len > 0 { + return error(ret_err) + } +} + +// is_dir_empty will return a `bool` whether or not `path` is empty. +pub fn is_dir_empty(path string) bool { + items := ls(path) or { return true } + return items.len == 0 +} + +// file_ext will return the part after the last occurence of `.` in `path`. +// The `.` is included. +pub fn file_ext(path string) string { + pos := path.last_index('.') or { return '' } + return path[pos..] +} + +// dir returns all but the last element of path, typically the path's directory. +// After dropping the final element, trailing slashes are removed. +// If the path is empty, dir returns ".". If the path consists entirely of separators, +// dir returns a single separator. +// The returned path does not end in a separator unless it is the root directory. +pub fn dir(opath string) string { + if opath == '' { + return '.' + } + path := opath.replace_each(['/', path_separator, r'\', path_separator]) + pos := path.last_index(path_separator) or { return '.' } + if pos == 0 && path_separator == '/' { + return '/' + } + return path[..pos] +} + +// base returns the last element of path. +// Trailing path separators are removed before extracting the last element. +// If the path is empty, base returns ".". If the path consists entirely of separators, base returns a +// single separator. +pub fn base(opath string) string { + if opath == '' { + return '.' + } + path := opath.replace_each(['/', path_separator, r'\', path_separator]) + if path == path_separator { + return path_separator + } + if path.ends_with(path_separator) { + path2 := path[..path.len - 1] + pos := path2.last_index(path_separator) or { return path2.clone() } + return path2[pos + 1..] + } + pos := path.last_index(path_separator) or { return path.clone() } + return path[pos + 1..] +} + +// file_name will return all characters found after the last occurence of `path_separator`. +// file extension is included. +pub fn file_name(opath string) string { + path := opath.replace_each(['/', path_separator, r'\', path_separator]) + return path.all_after_last(path_separator) +} + +// input_opt returns a one-line string from stdin, after printing a prompt. +// In the event of error (end of input), it returns `none`. +pub fn input_opt(prompt string) ?string { + print(prompt) + flush() + res := get_raw_line() + if res.len > 0 { + return res.trim_right('\r\n') + } + return none +} + +// input returns a one-line string from stdin, after printing a prompt. +// In the event of error (end of input), it returns ''. +pub fn input(prompt string) string { + res := input_opt(prompt) or { return '' } + return res +} + +// get_line returns a one-line string from stdin +pub fn get_line() string { + str := get_raw_line() + $if windows { + return str.trim_right('\r\n') + } + return str.trim_right('\n') +} + +// get_lines returns an array of strings read from from stdin. +// reading is stopped when an empty line is read. +pub fn get_lines() []string { + mut line := '' + mut inputstr := []string{} + for { + line = get_line() + if line.len <= 0 { + break + } + line = line.trim_space() + inputstr << line + } + return inputstr +} + +// get_lines_joined returns a string of the values read from from stdin. +// reading is stopped when an empty line is read. +pub fn get_lines_joined() string { + mut line := '' + mut inputstr := '' + for { + line = get_line() + if line.len <= 0 { + break + } + line = line.trim_space() + inputstr += line + } + return inputstr +} + +// get_raw_lines_joined reads *all* input lines from stdin. +// It returns them as one large string. NB: unlike os.get_lines_joined, +// empty lines (that contain only `\r\n` or `\n`), will be present in +// the output. +// Reading is stopped, only on EOF of stdin. +pub fn get_raw_lines_joined() string { + mut line := '' + mut lines := []string{} + for { + line = get_raw_line() + if line.len <= 0 { + break + } + lines << line + } + res := lines.join('') + return res +} + +// user_os returns current user operating system name. +pub fn user_os() string { + $if linux { + return 'linux' + } + $if macos { + return 'macos' + } + $if windows { + return 'windows' + } + $if freebsd { + return 'freebsd' + } + $if openbsd { + return 'openbsd' + } + $if netbsd { + return 'netbsd' + } + $if dragonfly { + return 'dragonfly' + } + $if android { + return 'android' + } + $if solaris { + return 'solaris' + } + $if haiku { + return 'haiku' + } + $if serenity { + return 'serenity' + } + $if vinix { + return 'vinix' + } + return 'unknown' +} + +// home_dir returns path to the user's home directory. +pub fn home_dir() string { + $if windows { + return getenv('USERPROFILE') + } $else { + // println('home_dir() call') + // res:= os.getenv('HOME') + // println('res="$res"') + return getenv('HOME') + } +} + +// write_file writes `text` data to a file in `path`. +pub fn write_file(path string, text string) ? { + mut f := create(path) ? + unsafe { f.write_full_buffer(text.str, size_t(text.len)) ? } + f.close() +} + +// write_file_array writes the data in `buffer` to a file in `path`. +pub fn write_file_array(path string, buffer array) ? { + mut f := create(path) ? + unsafe { f.write_full_buffer(buffer.data, size_t(buffer.len * buffer.element_size)) ? } + f.close() +} + +// executable_fallback is used when there is not a more platform specific and accurate implementation. +// It relies on path manipulation of os.args[0] and os.wd_at_startup, so it may not work properly in +// all cases, but it should be better, than just using os.args[0] directly. +fn executable_fallback() string { + if os.args.len == 0 { + // we are early in the bootstrap, os.args has not been initialized yet :-| + return '' + } + mut exepath := os.args[0] + $if windows { + if !exepath.contains('.exe') { + exepath += '.exe' + } + } + if !is_abs_path(exepath) { + rexepath := exepath.replace_each(['/', path_separator, r'\', path_separator]) + if rexepath.contains(path_separator) { + exepath = join_path(os.wd_at_startup, exepath) + } else { + // no choice but to try to walk the PATH folders :-| ... + foundpath := find_abs_path_of_executable(exepath) or { '' } + if foundpath.len > 0 { + exepath = foundpath + } + } + } + exepath = real_path(exepath) + return exepath +} + +// find_exe_path walks the environment PATH, just like most shell do, it returns +// the absolute path of the executable if found +pub fn find_abs_path_of_executable(exepath string) ?string { + if exepath == '' { + return error('expected non empty `exepath`') + } + if is_abs_path(exepath) { + return real_path(exepath) + } + mut res := '' + paths := getenv('PATH').split(path_delimiter) + for p in paths { + found_abs_path := join_path(p, exepath) + if exists(found_abs_path) && is_executable(found_abs_path) { + res = found_abs_path + break + } + } + if res.len > 0 { + return real_path(res) + } + return error('failed to find executable') +} + +// exists_in_system_path returns `true` if `prog` exists in the system's PATH +pub fn exists_in_system_path(prog string) bool { + find_abs_path_of_executable(prog) or { return false } + return true +} + +// is_file returns a `bool` indicating whether the given `path` is a file. +pub fn is_file(path string) bool { + return exists(path) && !is_dir(path) +} + +// is_abs_path returns `true` if `path` is absolute. +pub fn is_abs_path(path string) bool { + if path.len == 0 { + return false + } + $if windows { + return path[0] == `/` || // incase we're in MingGW bash + (path[0].is_letter() && path.len > 1 && path[1] == `:`) + } + return path[0] == `/` +} + +// join_path returns a path as string from input string parameter(s). +[manualfree] +pub fn join_path(base string, dirs ...string) string { + mut result := []string{} + result << base.trim_right('\\/') + for d in dirs { + result << d + } + res := result.join(path_separator) + unsafe { result.free() } + return res +} + +// walk_ext returns a recursive list of all files in `path` ending with `ext`. +pub fn walk_ext(path string, ext string) []string { + if !is_dir(path) { + return [] + } + mut files := ls(path) or { return [] } + mut res := []string{} + separator := if path.ends_with(path_separator) { '' } else { path_separator } + for file in files { + if file.starts_with('.') { + continue + } + p := path + separator + file + if is_dir(p) && !is_link(p) { + res << walk_ext(p, ext) + } else if file.ends_with(ext) { + res << p + } + } + return res +} + +// walk recursively traverses the given directory `path`. +// When a file is encountred it will call the callback function with current file as argument. +pub fn walk(path string, f fn (string)) { + if !is_dir(path) { + return + } + mut files := ls(path) or { return } + mut local_path_separator := path_separator + if path.ends_with(path_separator) { + local_path_separator = '' + } + for file in files { + p := path + local_path_separator + file + if is_dir(p) && !is_link(p) { + walk(p, f) + } else if exists(p) { + f(p) + } + } + return +} + +// log will print "os.log: "+`s` ... +pub fn log(s string) { + //$if macos { + // Use NSLog() on macos + // C.darwin_log(s) + //} $else { + println('os.log: ' + s) + //} +} + +// mkdir_all will create a valid full path of all directories given in `path`. +pub fn mkdir_all(path string) ? { + mut p := if path.starts_with(path_separator) { path_separator } else { '' } + path_parts := path.trim_left(path_separator).split(path_separator) + for subdir in path_parts { + p += subdir + path_separator + if exists(p) && is_dir(p) { + continue + } + mkdir(p) or { return error('folder: $p, error: $err') } + } +} + +// cache_dir returns the path to a *writable* user specific folder, suitable for writing non-essential data. +pub fn cache_dir() string { + // See: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html + // There is a single base directory relative to which user-specific non-essential + // (cached) data should be written. This directory is defined by the environment + // variable $XDG_CACHE_HOME. + // $XDG_CACHE_HOME defines the base directory relative to which user specific + // non-essential data files should be stored. If $XDG_CACHE_HOME is either not set + // or empty, a default equal to $HOME/.cache should be used. + $if !windows { + xdg_cache_home := getenv('XDG_CACHE_HOME') + if xdg_cache_home != '' { + return xdg_cache_home + } + } + cdir := join_path(home_dir(), '.cache') + if !is_dir(cdir) && !is_link(cdir) { + mkdir(cdir) or { panic(err) } + } + return cdir +} + +// temp_dir returns the path to a folder, that is suitable for storing temporary files. +pub fn temp_dir() string { + mut path := getenv('TMPDIR') + $if windows { + if path == '' { + // TODO see Qt's implementation? + // https://doc.qt.io/qt-5/qdir.html#tempPath + // https://github.com/qt/qtbase/blob/e164d61ca8263fc4b46fdd916e1ea77c7dd2b735/src/corelib/io/qfilesystemengine_win.cpp#L1275 + path = getenv('TEMP') + if path == '' { + path = getenv('TMP') + } + if path == '' { + path = 'C:/tmp' + } + } + } + $if macos { + // avoid /var/folders/6j/cmsk8gd90pd.... on macs + return '/tmp' + } + $if android { + // TODO test+use '/data/local/tmp' on Android before using cache_dir() + if path == '' { + path = cache_dir() + } + } + if path == '' { + path = '/tmp' + } + return path +} + +fn default_vmodules_path() string { + return join_path(home_dir(), '.vmodules') +} + +// vmodules_dir returns the path to a folder, where v stores its global modules. +pub fn vmodules_dir() string { + paths := vmodules_paths() + if paths.len > 0 { + return paths[0] + } + return default_vmodules_path() +} + +// vmodules_paths returns a list of paths, where v looks up for modules. +// You can customize it through setting the environment variable VMODULES +pub fn vmodules_paths() []string { + mut path := getenv('VMODULES') + if path == '' { + path = default_vmodules_path() + } + list := path.split(path_delimiter).map(it.trim_right(path_separator)) + return list +} + +// resource_abs_path returns an absolute path, for the given `path`. +// (the path is expected to be relative to the executable program) +// See https://discordapp.com/channels/592103645835821068/592294828432424960/630806741373943808 +// It gives a convenient way to access program resources like images, fonts, sounds and so on, +// *no matter* how the program was started, and what is the current working directory. +[manualfree] +pub fn resource_abs_path(path string) string { + exe := executable() + dexe := dir(exe) + mut base_path := real_path(dexe) + vresource := getenv('V_RESOURCE_PATH') + if vresource.len != 0 { + base_path = vresource + } + fp := join_path(base_path, path) + res := real_path(fp) + unsafe { + fp.free() + base_path.free() + } + return res +} + +pub struct Uname { +pub mut: + sysname string + nodename string + release string + version string + machine string +} + +pub fn execute_or_panic(cmd string) Result { + res := execute(cmd) + if res.exit_code != 0 { + eprintln('failed cmd: $cmd') + eprintln('failed code: $res.exit_code') + panic(res.output) + } + return res +} + +pub fn execute_or_exit(cmd string) Result { + res := execute(cmd) + if res.exit_code != 0 { + eprintln('failed cmd: $cmd') + eprintln('failed code: $res.exit_code') + eprintln(res.output) + exit(1) + } + return res +} + +// is_atty returns 1 if the `fd` file descriptor is open and refers to a terminal +pub fn is_atty(fd int) int { + $if windows { + mut mode := u32(0) + osfh := voidptr(C._get_osfhandle(fd)) + C.GetConsoleMode(osfh, voidptr(&mode)) + return int(mode) + } $else { + return C.isatty(fd) + } +} + +pub fn glob(patterns ...string) ?[]string { + mut matches := []string{} + for pattern in patterns { + native_glob_pattern(pattern, mut matches) ? + } + matches.sort() + return matches +} diff --git a/v_windows/v/old/vlib/os/os_android.c.v b/v_windows/v/old/vlib/os/os_android.c.v new file mode 100644 index 0000000..30825ea --- /dev/null +++ b/v_windows/v/old/vlib/os/os_android.c.v @@ -0,0 +1,39 @@ +module os + +struct C.AAsset { +} + +struct C.AAssetManager { +} + +struct C.ANativeActivity { + assetManager voidptr +} + +fn C.AAssetManager_open(&C.AAssetManager, &char, int) &C.AAsset + +fn C.AAsset_getLength(&C.AAsset) int + +fn C.AAsset_read(&C.AAsset, voidptr, int) int + +fn C.AAsset_close(&C.AAsset) + +pub fn read_apk_asset(file string) ?[]byte { + act := &C.ANativeActivity(C.sapp_android_get_native_activity()) + if isnil(act) { + return error('Could not get reference to Android activity') + } + asset := C.AAssetManager_open(&C.AAssetManager(act.assetManager), file.str, C.AASSET_MODE_STREAMING) + if isnil(asset) { + return error('File `$file` not found') + } + len := C.AAsset_getLength(asset) + buf := []byte{len: len} + for { + if C.AAsset_read(asset, buf.data, len) > 0 { + break + } + } + C.AAsset_close(asset) + return buf +} diff --git a/v_windows/v/old/vlib/os/os_c.v b/v_windows/v/old/vlib/os/os_c.v new file mode 100644 index 0000000..8275a9f --- /dev/null +++ b/v_windows/v/old/vlib/os/os_c.v @@ -0,0 +1,979 @@ +module os + +#include // #include +#include + +struct C.dirent { + d_name [256]char +} + +fn C.readdir(voidptr) &C.dirent + +fn C.readlink(pathname &char, buf &char, bufsiz size_t) int + +fn C.getline(voidptr, voidptr, voidptr) int + +fn C.ftell(fp voidptr) i64 + +fn C.sigaction(int, voidptr, int) int + +fn C.open(&char, int, ...int) int + +fn C.fdopen(fd int, mode &char) &C.FILE + +fn C.ferror(stream &C.FILE) int + +fn C.feof(stream &C.FILE) int + +fn C.CopyFile(&u16, &u16, bool) int + +// fn C.lstat(charptr, voidptr) u64 + +fn C._wstat64(&u16, voidptr) u64 + +fn C.chown(&char, int, int) int + +fn C.ftruncate(voidptr, u64) int + +fn C._chsize_s(voidptr, u64) int + +// fn C.proc_pidpath(int, byteptr, int) int +struct C.stat { + st_size u64 + st_mode u32 + st_mtime int +} + +struct C.__stat64 { + st_size u64 + st_mode u32 + st_mtime int +} + +struct C.DIR { +} + +type FN_SA_Handler = fn (sig int) + +struct C.sigaction { +mut: + sa_mask int + sa_sigaction int + sa_flags int + sa_handler FN_SA_Handler +} + +struct C.dirent { + d_name &byte +} + +// read_bytes returns all bytes read from file in `path`. +[manualfree] +pub fn read_bytes(path string) ?[]byte { + mut fp := vfopen(path, 'rb') ? + defer { + C.fclose(fp) + } + cseek := C.fseek(fp, 0, C.SEEK_END) + if cseek != 0 { + return error('fseek failed') + } + fsize := C.ftell(fp) + if fsize < 0 { + return error('ftell failed') + } + C.rewind(fp) + mut res := []byte{len: int(fsize)} + nr_read_elements := int(C.fread(res.data, fsize, 1, fp)) + if nr_read_elements == 0 && fsize > 0 { + return error('fread failed') + } + res.trim(nr_read_elements * int(fsize)) + return res +} + +// read_file reads the file in `path` and returns the contents. +pub fn read_file(path string) ?string { + mode := 'rb' + mut fp := vfopen(path, mode) ? + defer { + C.fclose(fp) + } + cseek := C.fseek(fp, 0, C.SEEK_END) + if cseek != 0 { + return error('fseek failed') + } + fsize := C.ftell(fp) + if fsize < 0 { + return error('ftell failed') + } + // C.fseek(fp, 0, SEEK_SET) // same as `C.rewind(fp)` below + C.rewind(fp) + unsafe { + mut str := malloc_noscan(int(fsize) + 1) + nelements := int(C.fread(str, 1, fsize, fp)) + is_eof := int(C.feof(fp)) + is_error := int(C.ferror(fp)) + if is_eof == 0 && is_error != 0 { + free(str) + return error('fread failed') + } + str[nelements] = 0 + if nelements == 0 { + // It is highly likely that the file was a virtual file from + // /sys or /proc, with information generated on the fly, so + // fsize was not reliably reported. Using vstring() here is + // slower (it calls strlen internally), but will return more + // consistent results. + // For example reading from /sys/class/sound/card0/id produces + // a `PCH\n` string, but fsize is 4096, and otherwise you would + // get a V string with .len = 4096 and .str = "PCH\n\\000". + return str.vstring() + } + return str.vstring_with_len(nelements) + } +} + +// ***************************** OS ops ************************ +// +// truncate changes the size of the file located in `path` to `len`. +// Note that changing symbolic links on Windows only works as admin. +pub fn truncate(path string, len u64) ? { + fp := C.open(&char(path.str), o_wronly | o_trunc) + defer { + C.close(fp) + } + if fp < 0 { + return error_with_code(posix_get_error_msg(C.errno), C.errno) + } + $if windows { + if C._chsize_s(fp, len) != 0 { + return error_with_code(posix_get_error_msg(C.errno), C.errno) + } + } $else { + if C.ftruncate(fp, len) != 0 { + return error_with_code(posix_get_error_msg(C.errno), C.errno) + } + } +} + +fn eprintln_unknown_file_size() { + eprintln('os.file_size() Cannot determine file-size: ' + posix_get_error_msg(C.errno)) +} + +// file_size returns the size of the file located in `path`. +// If an error occurs it returns 0. +// Note that use of this on symbolic links on Windows returns always 0. +pub fn file_size(path string) u64 { + mut s := C.stat{} + unsafe { + $if x64 { + $if windows { + mut swin := C.__stat64{} + if C._wstat64(path.to_wide(), voidptr(&swin)) != 0 { + eprintln_unknown_file_size() + return 0 + } + return swin.st_size + } $else { + if C.stat(&char(path.str), &s) != 0 { + eprintln_unknown_file_size() + return 0 + } + return u64(s.st_size) + } + } + $if x32 { + $if debug { + eprintln('Using os.file_size() on 32bit systems may not work on big files.') + } + $if windows { + if C._wstat(path.to_wide(), voidptr(&s)) != 0 { + eprintln_unknown_file_size() + return 0 + } + return u64(s.st_size) + } $else { + if C.stat(&char(path.str), &s) != 0 { + eprintln_unknown_file_size() + return 0 + } + return u64(s.st_size) + } + } + } + return 0 +} + +// mv moves files or folders from `src` to `dst`. +pub fn mv(src string, dst string) ? { + mut rdst := dst + if is_dir(rdst) { + rdst = join_path(rdst.trim_right(path_separator), file_name(src.trim_right(path_separator))) + } + $if windows { + w_src := src.replace('/', '\\') + w_dst := rdst.replace('/', '\\') + ret := C._wrename(w_src.to_wide(), w_dst.to_wide()) + if ret != 0 { + return error_with_code('failed to rename $src to $dst', int(ret)) + } + } $else { + ret := C.rename(&char(src.str), &char(rdst.str)) + if ret != 0 { + return error_with_code('failed to rename $src to $dst', int(ret)) + } + } +} + +// cp copies files or folders from `src` to `dst`. +pub fn cp(src string, dst string) ? { + $if windows { + w_src := src.replace('/', '\\') + w_dst := dst.replace('/', '\\') + if C.CopyFile(w_src.to_wide(), w_dst.to_wide(), false) == 0 { + result := C.GetLastError() + return error_with_code('failed to copy $src to $dst', int(result)) + } + } $else { + fp_from := C.open(&char(src.str), C.O_RDONLY) + if fp_from < 0 { // Check if file opened + return error_with_code('cp: failed to open $src', int(fp_from)) + } + fp_to := C.open(&char(dst.str), C.O_WRONLY | C.O_CREAT | C.O_TRUNC, C.S_IWUSR | C.S_IRUSR) + if fp_to < 0 { // Check if file opened (permissions problems ...) + C.close(fp_from) + return error_with_code('cp (permission): failed to write to $dst (fp_to: $fp_to)', + int(fp_to)) + } + // TODO use defer{} to close files in case of error or return. + // Currently there is a C-Error when building. + mut buf := [1024]byte{} + mut count := 0 + for { + count = C.read(fp_from, &buf[0], sizeof(buf)) + if count == 0 { + break + } + if C.write(fp_to, &buf[0], count) < 0 { + C.close(fp_to) + C.close(fp_from) + return error_with_code('cp: failed to write to $dst', int(-1)) + } + } + from_attr := C.stat{} + unsafe { + C.stat(&char(src.str), &from_attr) + } + if C.chmod(&char(dst.str), from_attr.st_mode) < 0 { + C.close(fp_to) + C.close(fp_from) + return error_with_code('failed to set permissions for $dst', int(-1)) + } + C.close(fp_to) + C.close(fp_from) + } +} + +// vfopen returns an opened C file, given its path and open mode. +// NB: os.vfopen is useful for compatibility with C libraries, that expect `FILE *`. +// If you write pure V code, os.create or os.open are more convenient. +pub fn vfopen(path string, mode string) ?&C.FILE { + if path.len == 0 { + return error('vfopen called with ""') + } + mut fp := voidptr(0) + $if windows { + fp = C._wfopen(path.to_wide(), mode.to_wide()) + } $else { + fp = C.fopen(&char(path.str), &char(mode.str)) + } + if isnil(fp) { + return error('failed to open file "$path"') + } else { + return fp + } +} + +// fileno returns the file descriptor of an opened C file. +pub fn fileno(cfile voidptr) int { + $if windows { + return C._fileno(cfile) + } $else { + mut cfile_casted := &C.FILE(0) // FILE* cfile_casted = 0; + cfile_casted = cfile + // Required on FreeBSD/OpenBSD/NetBSD as stdio.h defines fileno(..) with a macro + // that performs a field access on its argument without casting from void*. + return C.fileno(cfile_casted) + } +} + +// vpopen system starts the specified command, waits for it to complete, and returns its code. +fn vpopen(path string) voidptr { + // *C.FILE { + $if windows { + mode := 'rb' + wpath := path.to_wide() + return C._wpopen(wpath, mode.to_wide()) + } $else { + cpath := path.str + return C.popen(&char(cpath), c'r') + } +} + +fn posix_wait4_to_exit_status(waitret int) (int, bool) { + $if windows { + return waitret, false + } $else { + mut ret := 0 + mut is_signaled := true + // (see man system, man 2 waitpid: C macro WEXITSTATUS section) + if C.WIFEXITED(waitret) { + ret = C.WEXITSTATUS(waitret) + is_signaled = false + } else if C.WIFSIGNALED(waitret) { + ret = C.WTERMSIG(waitret) + is_signaled = true + } + return ret, is_signaled + } +} + +// posix_get_error_msg return error code representation in string. +pub fn posix_get_error_msg(code int) string { + ptr_text := C.strerror(code) // voidptr? + if ptr_text == 0 { + return '' + } + return unsafe { tos3(ptr_text) } +} + +// vpclose will close a file pointer opened with `vpopen`. +fn vpclose(f voidptr) int { + $if windows { + return C._pclose(f) + } $else { + ret, _ := posix_wait4_to_exit_status(C.pclose(f)) + return ret + } +} + +// system works like `exec`, but only returns a return code. +pub fn system(cmd string) int { + // if cmd.contains(';') || cmd.contains('&&') || cmd.contains('||') || cmd.contains('\n') { + // TODO remove panic + // panic(';, &&, || and \\n are not allowed in shell commands') + // } + mut ret := 0 + $if windows { + // overcome bug in system & _wsystem (cmd) when first char is quote `"` + wcmd := if cmd.len > 1 && cmd[0] == `"` && cmd[1] != `"` { '"$cmd"' } else { cmd } + unsafe { + ret = C._wsystem(wcmd.to_wide()) + } + } $else { + $if ios { + unsafe { + arg := [c'/bin/sh', c'-c', &byte(cmd.str), 0] + pid := 0 + ret = C.posix_spawn(&pid, c'/bin/sh', 0, 0, arg.data, 0) + status := 0 + ret = C.waitpid(pid, &status, 0) + if C.WIFEXITED(status) { + ret = C.WEXITSTATUS(status) + } + } + } $else { + unsafe { + ret = C.system(&char(cmd.str)) + } + } + } + if ret == -1 { + print_c_errno() + } + $if !windows { + pret, is_signaled := posix_wait4_to_exit_status(ret) + if is_signaled { + println('Terminated by signal ${ret:2d} (' + sigint_to_signal_name(pret) + ')') + } + ret = pret + } + return ret +} + +// exists returns true if `path` (file or directory) exists. +pub fn exists(path string) bool { + $if windows { + p := path.replace('/', '\\') + return C._waccess(p.to_wide(), f_ok) != -1 + } $else { + return C.access(&char(path.str), f_ok) != -1 + } +} + +// is_executable returns `true` if `path` is executable. +pub fn is_executable(path string) bool { + $if windows { + // NB: https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/access-waccess?view=vs-2019 + // i.e. there is no X bit there, the modes can be: + // 00 Existence only + // 02 Write-only + // 04 Read-only + // 06 Read and write + p := real_path(path) + return (exists(p) && p.ends_with('.exe')) + } + $if solaris { + statbuf := C.stat{} + unsafe { + if C.stat(&char(path.str), &statbuf) != 0 { + return false + } + } + return (int(statbuf.st_mode) & (s_ixusr | s_ixgrp | s_ixoth)) != 0 + } + return C.access(&char(path.str), x_ok) != -1 +} + +// is_writable returns `true` if `path` is writable. +pub fn is_writable(path string) bool { + $if windows { + p := path.replace('/', '\\') + return C._waccess(p.to_wide(), w_ok) != -1 + } $else { + return C.access(&char(path.str), w_ok) != -1 + } +} + +// is_readable returns `true` if `path` is readable. +pub fn is_readable(path string) bool { + $if windows { + p := path.replace('/', '\\') + return C._waccess(p.to_wide(), r_ok) != -1 + } $else { + return C.access(&char(path.str), r_ok) != -1 + } +} + +// rm removes file in `path`. +pub fn rm(path string) ? { + mut rc := 0 + $if windows { + rc = C._wremove(path.to_wide()) + } $else { + rc = C.remove(&char(path.str)) + } + if rc == -1 { + return error('Failed to remove "$path": ' + posix_get_error_msg(C.errno)) + } + // C.unlink(path.cstr()) +} + +// rmdir removes a specified directory. +pub fn rmdir(path string) ? { + $if windows { + rc := C.RemoveDirectory(path.to_wide()) + if rc == 0 { + // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-removedirectorya - 0 is failure + return error('Failed to remove "$path": ' + posix_get_error_msg(C.errno)) + } + } $else { + rc := C.rmdir(&char(path.str)) + if rc == -1 { + return error(posix_get_error_msg(C.errno)) + } + } +} + +// print_c_errno will print the current value of `C.errno`. +fn print_c_errno() { + e := C.errno + se := unsafe { tos_clone(&byte(C.strerror(e))) } + println('errno=$e err=$se') +} + +// get_raw_line returns a one-line string from stdin along with '\n' if there is any. +pub fn get_raw_line() string { + $if windows { + unsafe { + max_line_chars := 256 + buf := malloc_noscan(max_line_chars * 2) + h_input := C.GetStdHandle(C.STD_INPUT_HANDLE) + mut bytes_read := u32(0) + if is_atty(0) > 0 { + x := C.ReadConsole(h_input, buf, max_line_chars * 2, &bytes_read, 0) + if !x { + return tos(buf, 0) + } + return string_from_wide2(&u16(buf), int(bytes_read)) + } + mut offset := 0 + for { + pos := buf + offset + res := C.ReadFile(h_input, pos, 1, C.LPDWORD(&bytes_read), 0) + if !res && offset == 0 { + return tos(buf, 0) + } + if !res || bytes_read == 0 { + break + } + if *pos == `\n` || *pos == `\r` { + offset++ + break + } + offset++ + } + return buf.vstring_with_len(offset) + } + } $else { + max := size_t(0) + buf := &char(0) + nr_chars := unsafe { C.getline(&buf, &max, C.stdin) } + return unsafe { tos(&byte(buf), if nr_chars < 0 { 0 } else { nr_chars }) } + } +} + +// get_raw_stdin will get the raw input from stdin. +pub fn get_raw_stdin() []byte { + $if windows { + unsafe { + block_bytes := 512 + mut old_size := block_bytes + mut buf := malloc_noscan(block_bytes) + h_input := C.GetStdHandle(C.STD_INPUT_HANDLE) + mut bytes_read := 0 + mut offset := 0 + for { + pos := buf + offset + res := C.ReadFile(h_input, pos, block_bytes, C.LPDWORD(&bytes_read), 0) + offset += bytes_read + if !res { + break + } + new_size := offset + block_bytes + (block_bytes - bytes_read) + buf = realloc_data(buf, old_size, new_size) + old_size = new_size + } + return array{ + element_size: 1 + data: voidptr(buf) + len: offset + cap: offset + } + } + } $else { + max := size_t(0) + buf := &char(0) + nr_chars := unsafe { C.getline(&buf, &max, C.stdin) } + return array{ + element_size: 1 + data: voidptr(buf) + len: if nr_chars < 0 { 0 } else { nr_chars } + cap: int(max) + } + } +} + +// read_file_array reads an array of `T` values from file `path`. +pub fn read_file_array(path string) []T { + a := T{} + tsize := int(sizeof(a)) + // prepare for reading, get current file size + mut fp := vfopen(path, 'rb') or { return []T{} } + C.fseek(fp, 0, C.SEEK_END) + fsize := C.ftell(fp) + C.rewind(fp) + // read the actual data from the file + len := fsize / tsize + buf := unsafe { malloc_noscan(int(fsize)) } + nread := C.fread(buf, tsize, len, fp) + C.fclose(fp) + return unsafe { + array{ + element_size: tsize + data: buf + len: int(nread) + cap: int(len) + } + } +} + +pub fn on_segfault(f voidptr) { + $if windows { + return + } + $if macos { + C.printf(c'TODO') + /* + mut sa := C.sigaction{} + C.memset(&sa, 0, sizeof(C.sigaction_size)) + C.sigemptyset(&sa.sa_mask) + sa.sa_sigaction = f + sa.sa_flags = C.SA_SIGINFO + C.sigaction(C.SIGSEGV, &sa, 0) + */ + } +} + +// executable returns the path name of the executable that started the current +// process. +[manualfree] +pub fn executable() string { + $if linux { + mut xresult := vcalloc_noscan(max_path_len) + count := C.readlink(c'/proc/self/exe', &char(xresult), max_path_len) + if count < 0 { + eprintln('os.executable() failed at reading /proc/self/exe to get exe path') + return executable_fallback() + } + return unsafe { xresult.vstring() } + } + $if windows { + max := 512 + size := max * 2 // max_path_len * sizeof(wchar_t) + mut result := unsafe { &u16(vcalloc_noscan(size)) } + len := C.GetModuleFileName(0, result, max) + // determine if the file is a windows symlink + attrs := C.GetFileAttributesW(result) + is_set := attrs & 0x400 // FILE_ATTRIBUTE_REPARSE_POINT + if is_set != 0 { // it's a windows symlink + // gets handle with GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 + file := C.CreateFile(result, 0x80000000, 1, 0, 3, 0x80, 0) + if file != voidptr(-1) { + final_path := unsafe { &u16(vcalloc_noscan(size)) } + // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlew + final_len := C.GetFinalPathNameByHandleW(file, final_path, size, 0) + if final_len < size { + ret := unsafe { string_from_wide2(final_path, final_len) } + // remove '\\?\' from beginning (see link above) + return ret[4..] + } else { + eprintln('os.executable() saw that the executable file path was too long') + } + } + C.CloseHandle(file) + } + return unsafe { string_from_wide2(result, len) } + } + $if macos { + mut result := vcalloc_noscan(max_path_len) + pid := C.getpid() + ret := proc_pidpath(pid, result, max_path_len) + if ret <= 0 { + eprintln('os.executable() failed at calling proc_pidpath with pid: $pid . proc_pidpath returned $ret ') + return executable_fallback() + } + return unsafe { result.vstring() } + } + $if freebsd { + mut result := vcalloc_noscan(max_path_len) + mib := [1 /* CTL_KERN */, 14 /* KERN_PROC */, 12 /* KERN_PROC_PATHNAME */, -1] + size := max_path_len + unsafe { C.sysctl(mib.data, 4, result, &size, 0, 0) } + return unsafe { result.vstring() } + } + // "Sadly there is no way to get the full path of the executed file in OpenBSD." + $if openbsd { + } + $if solaris { + } + $if haiku { + } + $if netbsd { + mut result := vcalloc_noscan(max_path_len) + count := C.readlink(c'/proc/curproc/exe', &char(result), max_path_len) + if count < 0 { + eprintln('os.executable() failed at reading /proc/curproc/exe to get exe path') + return executable_fallback() + } + return unsafe { result.vstring_with_len(count) } + } + $if dragonfly { + mut result := vcalloc_noscan(max_path_len) + count := C.readlink(c'/proc/curproc/file', &char(result), max_path_len) + if count < 0 { + eprintln('os.executable() failed at reading /proc/curproc/file to get exe path') + return executable_fallback() + } + return unsafe { result.vstring_with_len(count) } + } + return executable_fallback() +} + +// is_dir returns a `bool` indicating whether the given `path` is a directory. +pub fn is_dir(path string) bool { + $if windows { + w_path := path.replace('/', '\\') + attr := C.GetFileAttributesW(w_path.to_wide()) + if attr == u32(C.INVALID_FILE_ATTRIBUTES) { + return false + } + if int(attr) & C.FILE_ATTRIBUTE_DIRECTORY != 0 { + return true + } + return false + } $else { + statbuf := C.stat{} + if unsafe { C.stat(&char(path.str), &statbuf) } != 0 { + return false + } + // ref: https://code.woboq.org/gcc/include/sys/stat.h.html + val := int(statbuf.st_mode) & s_ifmt + return val == s_ifdir + } +} + +// is_link returns a boolean indicating whether `path` is a link. +pub fn is_link(path string) bool { + $if windows { + path_ := path.replace('/', '\\') + attr := C.GetFileAttributesW(path_.to_wide()) + return int(attr) != int(C.INVALID_FILE_ATTRIBUTES) && (attr & 0x400) != 0 + } $else { + statbuf := C.stat{} + if C.lstat(&char(path.str), &statbuf) != 0 { + return false + } + return int(statbuf.st_mode) & s_ifmt == s_iflnk + } +} + +// chdir changes the current working directory to the new directory in `path`. +pub fn chdir(path string) { + $if windows { + C._wchdir(path.to_wide()) + } $else { + _ = C.chdir(&char(path.str)) + } +} + +// getwd returns the absolute path of the current directory. +pub fn getwd() string { + $if windows { + max := 512 // max_path_len * sizeof(wchar_t) + unsafe { + buf := &u16(vcalloc_noscan(max * 2)) + if C._wgetcwd(buf, max) == 0 { + free(buf) + return '' + } + return string_from_wide(buf) + } + } $else { + buf := vcalloc_noscan(max_path_len) + unsafe { + if C.getcwd(&char(buf), max_path_len) == 0 { + free(buf) + return '' + } + return buf.vstring() + } + } +} + +// real_path returns the full absolute path for fpath, with all relative ../../, symlinks and so on resolved. +// See http://pubs.opengroup.org/onlinepubs/9699919799/functions/realpath.html +// Also https://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html +// and https://insanecoding.blogspot.com/2007/11/implementing-realpath-in-c.html +// NB: this particular rabbit hole is *deep* ... +[manualfree] +pub fn real_path(fpath string) string { + mut res := '' + $if windows { + size := max_path_len * 2 + // gets handle with GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 + // use C.CreateFile(fpath.to_wide(), 0x80000000, 1, 0, 3, 0x80, 0) instead of get_file_handle + // try to open the file to get symbolic link path + file := C.CreateFile(fpath.to_wide(), 0x80000000, 1, 0, 3, 0x80, 0) + if file != voidptr(-1) { + mut fullpath := unsafe { &u16(vcalloc_noscan(size)) } + // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlew + final_len := C.GetFinalPathNameByHandleW(file, fullpath, size, 0) + C.CloseHandle(file) + if final_len < size { + rt := unsafe { string_from_wide2(fullpath, final_len) } + res = rt[4..] + } else { + unsafe { free(fullpath) } + eprintln('os.real_path() saw that the file path was too long') + return fpath.clone() + } + } else { + // if it is not a file C.CreateFile doesn't gets a file handle, use GetFullPath instead + mut fullpath := unsafe { &u16(vcalloc_noscan(max_path_len * 2)) } + // TODO: check errors if path len is not enough + ret := C.GetFullPathName(fpath.to_wide(), max_path_len, fullpath, 0) + if ret == 0 { + unsafe { free(fullpath) } + return fpath.clone() + } + res = unsafe { string_from_wide(fullpath) } + } + } $else { + mut fullpath := vcalloc_noscan(max_path_len) + ret := &char(C.realpath(&char(fpath.str), &char(fullpath))) + if ret == 0 { + unsafe { free(fullpath) } + return fpath.clone() + } + res = unsafe { fullpath.vstring() } + } + unsafe { normalize_drive_letter(res) } + return res +} + +[direct_array_access; manualfree; unsafe] +fn normalize_drive_letter(path string) { + $if !windows { + return + } + // normalize_drive_letter is needed, because + // a path like c:\nv\.bin (note the small `c`) in %PATH, + // is NOT recognized by cmd.exe (and probably other programs too)... + // Capital drive letters do work fine. + if path.len > 2 && path[0] >= `a` && path[0] <= `z` && path[1] == `:` + && path[2] == path_separator[0] { + unsafe { + x := &path.str[0] + (*x) = *x - 32 + } + } +} + +// fork will fork the current system process and return the pid of the fork. +pub fn fork() int { + mut pid := -1 + $if !windows { + pid = C.fork() + } + $if windows { + panic('os.fork not supported in windows') // TODO + } + return pid +} + +// wait blocks the calling process until one of its child processes exits or a signal is received. +// After child process terminates, parent continues its execution after wait system call instruction. +pub fn wait() int { + mut pid := -1 + $if !windows { + pid = C.wait(0) + } + $if windows { + panic('os.wait not supported in windows') // TODO + } + return pid +} + +// file_last_mod_unix returns the "last modified" time stamp of file in `path`. +pub fn file_last_mod_unix(path string) int { + attr := C.stat{} + // # struct stat attr; + unsafe { C.stat(&char(path.str), &attr) } + // # stat(path.str, &attr); + return attr.st_mtime + // # return attr.st_mtime ; +} + +// flush will flush the stdout buffer. +pub fn flush() { + C.fflush(C.stdout) +} + +// chmod change file access attributes of `path` to `mode`. +// Octals like `0o600` can be used. +pub fn chmod(path string, mode int) { + if C.chmod(&char(path.str), mode) != 0 { + panic('chmod failed: ' + posix_get_error_msg(C.errno)) + } +} + +// chown changes the owner and group attributes of `path` to `owner` and `group`. +pub fn chown(path string, owner int, group int) ? { + $if windows { + return error('os.chown() not implemented for Windows') + } $else { + if C.chown(&char(path.str), owner, group) != 0 { + return error_with_code(posix_get_error_msg(C.errno), C.errno) + } + } +} + +// open_append opens `path` file for appending. +pub fn open_append(path string) ?File { + mut file := File{} + $if windows { + wpath := path.replace('/', '\\').to_wide() + mode := 'ab' + file = File{ + cfile: C._wfopen(wpath, mode.to_wide()) + } + } $else { + cpath := path.str + file = File{ + cfile: C.fopen(&char(cpath), c'ab') + } + } + if isnil(file.cfile) { + return error('failed to create(append) file "$path"') + } + file.is_opened = true + return file +} + +// execvp - loads and executes a new child process, *in place* of the current process. +// The child process executable is located in `cmdpath`. +// The arguments, that will be passed to it are in `args`. +// NB: this function will NOT return when successfull, since +// the child process will take control over execution. +pub fn execvp(cmdpath string, args []string) ? { + mut cargs := []&char{} + cargs << &char(cmdpath.str) + for i in 0 .. args.len { + cargs << &char(args[i].str) + } + cargs << &char(0) + mut res := int(0) + $if windows { + res = C._execvp(&char(cmdpath.str), cargs.data) + } $else { + res = C.execvp(&char(cmdpath.str), cargs.data) + } + if res == -1 { + return error_with_code(posix_get_error_msg(C.errno), C.errno) + } + // just in case C._execvp returned ... that happens on windows ... + exit(res) +} + +// execve - loads and executes a new child process, *in place* of the current process. +// The child process executable is located in `cmdpath`. +// The arguments, that will be passed to it are in `args`. +// You can pass environment variables to through `envs`. +// NB: this function will NOT return when successfull, since +// the child process will take control over execution. +pub fn execve(cmdpath string, args []string, envs []string) ? { + mut cargv := []&char{} + mut cenvs := []&char{} + cargv << &char(cmdpath.str) + for i in 0 .. args.len { + cargv << &char(args[i].str) + } + for i in 0 .. envs.len { + cenvs << &char(envs[i].str) + } + cargv << &char(0) + cenvs << &char(0) + mut res := int(0) + $if windows { + res = C._execve(&char(cmdpath.str), cargv.data, cenvs.data) + } $else { + res = C.execve(&char(cmdpath.str), cargv.data, cenvs.data) + } + // NB: normally execve does not return at all. + // If it returns, then something went wrong... + if res == -1 { + return error_with_code(posix_get_error_msg(C.errno), C.errno) + } +} diff --git a/v_windows/v/old/vlib/os/os_darwin.c.v b/v_windows/v/old/vlib/os/os_darwin.c.v new file mode 100644 index 0000000..8635c63 --- /dev/null +++ b/v_windows/v/old/vlib/os/os_darwin.c.v @@ -0,0 +1,18 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module os + +#include "@VROOT/vlib/os/os_darwin.m" + +pub const ( + sys_write = 4 + sys_open = 5 + sys_close = 6 + sys_mkdir = 136 + sys_creat = 8 + sys_open_nocancel = 398 + sys_stat64 = 338 +) + +fn C.darwin_log(s string) diff --git a/v_windows/v/old/vlib/os/os_darwin.m b/v_windows/v/old/vlib/os/os_darwin.m new file mode 100644 index 0000000..a1de752 --- /dev/null +++ b/v_windows/v/old/vlib/os/os_darwin.m @@ -0,0 +1,7 @@ +/* +NSString* nsstring(string s); + +void darwin_log(string s) { + NSLog(nsstring(s)); +} +*/ diff --git a/v_windows/v/old/vlib/os/os_linux.c.v b/v_windows/v/old/vlib/os/os_linux.c.v new file mode 100644 index 0000000..e178795 --- /dev/null +++ b/v_windows/v/old/vlib/os/os_linux.c.v @@ -0,0 +1,19 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module os + +const ( + prot_read = 1 + prot_write = 2 + map_private = 0x02 + map_anonymous = 0x20 +) + +pub const ( + sys_write = 1 + sys_open = 2 + sys_close = 3 + sys_mkdir = 83 + sys_creat = 85 +) diff --git a/v_windows/v/old/vlib/os/os_nix.c.v b/v_windows/v/old/vlib/os/os_nix.c.v new file mode 100644 index 0000000..cc3869d --- /dev/null +++ b/v_windows/v/old/vlib/os/os_nix.c.v @@ -0,0 +1,549 @@ +module os + +import strings + +#include +#include +#include +#include +#include +#include +$if !solaris && !haiku { + #include +} + +pub const ( + path_separator = '/' + path_delimiter = ':' +) + +const ( + stdin_value = 0 + stdout_value = 1 + stderr_value = 2 +) + +// (Must be realized in Syscall) (Must be specified) +// ref: http://www.ccfit.nsu.ru/~deviv/courses/unix/unix/ng7c229.html +pub const ( + s_ifmt = 0xF000 // type of file + s_ifdir = 0x4000 // directory + s_iflnk = 0xa000 // link + s_isuid = 0o4000 // SUID + s_isgid = 0o2000 // SGID + s_isvtx = 0o1000 // Sticky + s_irusr = 0o0400 // Read by owner + s_iwusr = 0o0200 // Write by owner + s_ixusr = 0o0100 // Execute by owner + s_irgrp = 0o0040 // Read by group + s_iwgrp = 0o0020 // Write by group + s_ixgrp = 0o0010 // Execute by group + s_iroth = 0o0004 // Read by others + s_iwoth = 0o0002 // Write by others + s_ixoth = 0o0001 // Execute by others +) + +struct C.utsname { +mut: + sysname &char + nodename &char + release &char + version &char + machine &char +} + +struct C.utimbuf { + actime int + modtime int +} + +fn C.utime(&char, voidptr) int + +fn C.uname(name voidptr) int + +fn C.symlink(&char, &char) int + +fn C.link(&char, &char) int + +fn C.gethostname(&char, int) int + +// NB: not available on Android fn C.getlogin_r(&char, int) int +fn C.getlogin() &char + +fn C.getppid() int + +fn C.getgid() int + +fn C.getegid() int + +fn C.ptrace(u32, u32, voidptr, int) u64 + +enum GlobMatch { + exact + ends_with + starts_with + start_and_ends_with + contains + any +} + +fn glob_match(dir string, pattern string, next_pattern string, mut matches []string) []string { + mut subdirs := []string{} + if is_file(dir) { + return subdirs + } + mut files := ls(dir) or { return subdirs } + mut mode := GlobMatch.exact + mut pat := pattern + if pat == '*' { + mode = GlobMatch.any + if next_pattern != pattern && next_pattern != '' { + for file in files { + if is_dir('$dir/$file') { + subdirs << '$dir/$file' + } + } + return subdirs + } + } + if pat == '**' { + files = walk_ext(dir, '') + pat = next_pattern + } + if pat.starts_with('*') { + mode = .ends_with + pat = pat[1..] + } + if pat.ends_with('*') { + mode = if mode == .ends_with { GlobMatch.contains } else { GlobMatch.starts_with } + pat = pat[..pat.len - 1] + } + if pat.contains('*') { + mode = .start_and_ends_with + } + for file in files { + mut fpath := file + f := if file.contains(os.path_separator) { + pathwalk := file.split(os.path_separator) + pathwalk[pathwalk.len - 1] + } else { + fpath = if dir == '.' { file } else { '$dir/$file' } + file + } + if f in ['.', '..'] || f == '' { + continue + } + hit := match mode { + .any { + true + } + .exact { + f == pat + } + .starts_with { + f.starts_with(pat) + } + .ends_with { + f.ends_with(pat) + } + .start_and_ends_with { + p := pat.split('*') + f.starts_with(p[0]) && f.ends_with(p[1]) + } + .contains { + f.contains(pat) + } + } + if hit { + if is_dir(fpath) { + subdirs << fpath + if next_pattern == pattern && next_pattern != '' { + matches << '$fpath$os.path_separator' + } + } else { + matches << fpath + } + } + } + return subdirs +} + +fn native_glob_pattern(pattern string, mut matches []string) ? { + steps := pattern.split(os.path_separator) + mut cwd := if pattern.starts_with(os.path_separator) { os.path_separator } else { '.' } + mut subdirs := [cwd] + for i := 0; i < steps.len; i++ { + step := steps[i] + step2 := if i + 1 == steps.len { step } else { steps[i + 1] } + if step == '' { + continue + } + if is_dir('$cwd$os.path_separator$step') { + dd := if cwd == '/' { + step + } else { + if cwd == '.' || cwd == '' { + step + } else { + if step == '.' || step == '/' { cwd } else { '$cwd/$step' } + } + } + if i + 1 != steps.len { + if dd !in subdirs { + subdirs << dd + } + } + } + mut subs := []string{} + for sd in subdirs { + d := if cwd == '/' { + sd + } else { + if cwd == '.' || cwd == '' { + sd + } else { + if sd == '.' || sd == '/' { cwd } else { '$cwd/$sd' } + } + } + subs << glob_match(d.replace('//', '/'), step, step2, mut matches) + } + subdirs = subs.clone() + } +} + +pub fn utime(path string, actime int, modtime int) ? { + mut u := C.utimbuf{actime, modtime} + if C.utime(&char(path.str), voidptr(&u)) != 0 { + return error_with_code(posix_get_error_msg(C.errno), C.errno) + } +} + +pub fn uname() Uname { + mut u := Uname{} + utsize := sizeof(C.utsname) + unsafe { + x := malloc_noscan(int(utsize)) + d := &C.utsname(x) + if C.uname(d) == 0 { + u.sysname = cstring_to_vstring(d.sysname) + u.nodename = cstring_to_vstring(d.nodename) + u.release = cstring_to_vstring(d.release) + u.version = cstring_to_vstring(d.version) + u.machine = cstring_to_vstring(d.machine) + } + free(d) + } + return u +} + +pub fn hostname() string { + mut hstnme := '' + size := 256 + mut buf := unsafe { &char(malloc_noscan(size)) } + if C.gethostname(buf, size) == 0 { + hstnme = unsafe { cstring_to_vstring(buf) } + unsafe { free(buf) } + return hstnme + } + return '' +} + +pub fn loginname() string { + x := C.getlogin() + if !isnil(x) { + return unsafe { cstring_to_vstring(x) } + } + return '' +} + +fn init_os_args(argc int, argv &&byte) []string { + mut args_ := []string{} + // mut args := []string(make(0, argc, sizeof(string))) + // mut args := []string{len:argc} + for i in 0 .. argc { + // args [i] = argv[i].vstring() + unsafe { args_ << (&byte(argv[i])).vstring_literal() } + } + return args_ +} + +pub fn ls(path string) ?[]string { + mut res := []string{} + dir := unsafe { C.opendir(&char(path.str)) } + if isnil(dir) { + return error('ls() couldnt open dir "$path"') + } + mut ent := &C.dirent(0) + // mut ent := &C.dirent{!} + for { + ent = C.readdir(dir) + if isnil(ent) { + break + } + unsafe { + bptr := &byte(&ent.d_name[0]) + if bptr[0] == 0 || (bptr[0] == `.` && bptr[1] == 0) + || (bptr[0] == `.` && bptr[1] == `.` && bptr[2] == 0) { + continue + } + res << tos_clone(bptr) + } + } + C.closedir(dir) + return res +} + +/* +pub fn is_dir(path string) bool { + //$if linux { + //C.syscall(4, path.str) // sys_newstat + //} + dir := C.opendir(path.str) + res := !isnil(dir) + if res { + C.closedir(dir) + } + return res +} +*/ + +// mkdir creates a new directory with the specified path. +pub fn mkdir(path string) ?bool { + if path == '.' { + return true + } + /* + mut k := 0 + defer { + k = 1 + } + */ + apath := real_path(path) + // defer { + // apath.free() + //} + /* + $if linux { + $if !android { + ret := C.syscall(sys_mkdir, apath.str, 511) + if ret == -1 { + return error(posix_get_error_msg(C.errno)) + } + return true + } + } + */ + r := unsafe { C.mkdir(&char(apath.str), 511) } + if r == -1 { + return error(posix_get_error_msg(C.errno)) + } + return true +} + +// execute starts the specified command, waits for it to complete, and returns its output. +[manualfree] +pub fn execute(cmd string) Result { + // if cmd.contains(';') || cmd.contains('&&') || cmd.contains('||') || cmd.contains('\n') { + // return Result{ exit_code: -1, output: ';, &&, || and \\n are not allowed in shell commands' } + // } + pcmd := '$cmd 2>&1' + f := vpopen(pcmd) + if isnil(f) { + return Result{ + exit_code: -1 + output: 'exec("$cmd") failed' + } + } + buf := unsafe { malloc_noscan(4096) } + mut res := strings.new_builder(1024) + defer { + unsafe { res.free() } + } + unsafe { + bufbp := buf + for C.fgets(&char(bufbp), 4096, f) != 0 { + buflen := vstrlen(bufbp) + res.write_ptr(bufbp, buflen) + } + } + soutput := res.str() + exit_code := vpclose(f) + unsafe { free(buf) } + return Result{ + exit_code: exit_code + output: soutput + } +} + +pub struct Command { +mut: + f voidptr +pub mut: + eof bool +pub: + path string + redirect_stdout bool +} + +[manualfree] +pub fn (mut c Command) start() ? { + pcmd := c.path + ' 2>&1' + defer { + unsafe { pcmd.free() } + } + c.f = vpopen(pcmd) + if isnil(c.f) { + return error('exec("$c.path") failed') + } +} + +[manualfree] +pub fn (mut c Command) read_line() string { + buf := [4096]byte{} + mut res := strings.new_builder(1024) + defer { + unsafe { res.free() } + } + unsafe { + bufbp := &buf[0] + for C.fgets(&char(bufbp), 4096, c.f) != 0 { + len := vstrlen(bufbp) + for i in 0 .. len { + if bufbp[i] == `\n` { + res.write_ptr(bufbp, i) + final := res.str() + return final + } + } + res.write_ptr(bufbp, len) + } + } + c.eof = true + final := res.str() + return final +} + +pub fn (c &Command) close() ? { + exit_code := vpclose(c.f) + if exit_code == 127 { + return error_with_code('error', 127) + } +} + +pub fn symlink(origin string, target string) ?bool { + res := C.symlink(&char(origin.str), &char(target.str)) + if res == 0 { + return true + } + return error(posix_get_error_msg(C.errno)) +} + +pub fn link(origin string, target string) ?bool { + res := C.link(&char(origin.str), &char(target.str)) + if res == 0 { + return true + } + return error(posix_get_error_msg(C.errno)) +} + +// get_error_msg return error code representation in string. +pub fn get_error_msg(code int) string { + return posix_get_error_msg(code) +} + +pub fn (mut f File) close() { + if !f.is_opened { + return + } + f.is_opened = false + /* + $if linux { + $if !android { + C.syscall(sys_close, f.fd) + return + } + } + */ + C.fflush(f.cfile) + C.fclose(f.cfile) +} + +[inline] +pub fn debugger_present() bool { + // check if the parent could trace its process, + // if not a debugger must be present + $if linux { + return C.ptrace(C.PTRACE_TRACEME, 0, 1, 0) == -1 + } $else $if macos { + return C.ptrace(C.PT_TRACE_ME, 0, voidptr(1), 0) == -1 + } + return false +} + +fn C.mkstemp(stemplate &byte) int + +// `is_writable_folder` - `folder` exists and is writable to the process +pub fn is_writable_folder(folder string) ?bool { + if !exists(folder) { + return error('`$folder` does not exist') + } + if !is_dir(folder) { + return error('`folder` is not a folder') + } + tmp_perm_check := join_path(folder, 'XXXXXX') + unsafe { + x := C.mkstemp(&char(tmp_perm_check.str)) + if -1 == x { + return error('folder `$folder` is not writable') + } + C.close(x) + } + rm(tmp_perm_check) ? + return true +} + +[inline] +pub fn getpid() int { + return C.getpid() +} + +[inline] +pub fn getppid() int { + return C.getppid() +} + +[inline] +pub fn getuid() int { + return C.getuid() +} + +[inline] +pub fn geteuid() int { + return C.geteuid() +} + +[inline] +pub fn getgid() int { + return C.getgid() +} + +[inline] +pub fn getegid() int { + return C.getegid() +} + +// Turns the given bit on or off, depending on the `enable` parameter +pub fn posix_set_permission_bit(path_s string, mode u32, enable bool) { + mut s := C.stat{} + mut new_mode := u32(0) + path := &char(path_s.str) + unsafe { + C.stat(path, &s) + new_mode = s.st_mode + } + match enable { + true { new_mode |= mode } + false { new_mode &= (0o7777 - mode) } + } + C.chmod(path, int(new_mode)) +} diff --git a/v_windows/v/old/vlib/os/os_test.v b/v_windows/v/old/vlib/os/os_test.v new file mode 100644 index 0000000..04c14e7 --- /dev/null +++ b/v_windows/v/old/vlib/os/os_test.v @@ -0,0 +1,752 @@ +import os +import time + +const ( + // tfolder will contain all the temporary files/subfolders made by + // the different tests. It would be removed in testsuite_end(), so + // individual os tests do not need to clean up after themselves. + tfolder = os.join_path(os.temp_dir(), 'v', 'tests', 'os_test') +) + +// os.args has to be *already initialized* with the program's argc/argv at this point +// thus it can be used for other consts too: +const args_at_start = os.args.clone() + +fn testsuite_begin() { + eprintln('testsuite_begin, tfolder = $tfolder') + os.rmdir_all(tfolder) or {} + assert !os.is_dir(tfolder) + os.mkdir_all(tfolder) or { panic(err) } + os.chdir(tfolder) + assert os.is_dir(tfolder) + // println('args_at_start: $args_at_start') + assert args_at_start.len > 0 + assert args_at_start == os.args +} + +fn testsuite_end() { + os.chdir(os.wd_at_startup) + os.rmdir_all(tfolder) or {} + assert !os.is_dir(tfolder) + // eprintln('testsuite_end , tfolder = $tfolder removed.') +} + +fn test_open_file() { + filename := './test1.txt' + hello := 'hello world!' + os.open_file(filename, 'r+', 0o666) or { + assert err.msg == 'No such file or directory' + os.File{} + } + mut file := os.open_file(filename, 'w+', 0o666) or { panic(err) } + file.write_string(hello) or { panic(err) } + file.close() + assert hello.len == os.file_size(filename) + read_hello := os.read_file(filename) or { panic('error reading file $filename') } + assert hello == read_hello + os.rm(filename) or { panic(err) } +} + +fn test_open_file_binary() { + filename := './test1.dat' + hello := 'hello \n world!' + os.open_file(filename, 'r+', 0o666) or { + assert err.msg == 'No such file or directory' + os.File{} + } + mut file := os.open_file(filename, 'wb+', 0o666) or { panic(err) } + bytes := hello.bytes() + unsafe { file.write_ptr(bytes.data, bytes.len) } + file.close() + assert hello.len == os.file_size(filename) + read_hello := os.read_bytes(filename) or { panic('error reading file $filename') } + assert bytes == read_hello + os.rm(filename) or { panic(err) } +} + +// fn test_file_get_line() { +// filename := './fgetline.txt' +// os.write_file(filename, 'line 1\nline 2') +// mut f := os.open_file(filename, 'r', 0) or { +// assert false +// return +// } +// line1 := f.get_line() or { +// '' +// } +// line2 := f.get_line() or { +// '' +// } +// f.close() +// // +// eprintln('line1: $line1 $line1.bytes()') +// eprintln('line2: $line2 $line2.bytes()') +// assert line1 == 'line 1\n' +// assert line2 == 'line 2' +// } +fn test_create_file() { + filename := './test1.txt' + hello := 'hello world!' + mut f := os.create(filename) or { panic(err) } + f.write_string(hello) or { panic(err) } + f.close() + assert hello.len == os.file_size(filename) + os.rm(filename) or { panic(err) } +} + +fn test_is_file() { + // Setup + work_dir := os.join_path(os.getwd(), 'is_file_test') + os.mkdir_all(work_dir) or { panic(err) } + tfile := os.join_path(work_dir, 'tmp_file') + // Test things that shouldn't be a file + assert os.is_file(work_dir) == false + assert os.is_file('non-existent_file.tmp') == false + // Test file + tfile_content := 'temporary file' + os.write_file(tfile, tfile_content) or { panic(err) } + assert os.is_file(tfile) + // Test dir symlinks + $if windows { + assert true + } $else { + dsymlink := os.join_path(work_dir, 'dir_symlink') + os.symlink(work_dir, dsymlink) or { panic(err) } + assert os.is_file(dsymlink) == false + } + // Test file symlinks + $if windows { + assert true + } $else { + fsymlink := os.join_path(work_dir, 'file_symlink') + os.symlink(tfile, fsymlink) or { panic(err) } + assert os.is_file(fsymlink) + } +} + +fn test_write_and_read_string_to_file() { + filename := './test1.txt' + hello := 'hello world!' + os.write_file(filename, hello) or { panic(err) } + assert hello.len == os.file_size(filename) + read_hello := os.read_file(filename) or { panic('error reading file $filename') } + assert hello == read_hello + os.rm(filename) or { panic(err) } +} + +// test_write_and_read_bytes checks for regressions made in the functions +// read_bytes, read_bytes_at and write_bytes. +fn test_write_and_read_bytes() { + file_name := './byte_reader_writer.tst' + payload := [byte(`I`), `D`, `D`, `Q`, `D`] + mut file_write := os.create(os.real_path(file_name)) or { + eprintln('failed to create file $file_name') + return + } + // We use the standard write_bytes function to write the payload and + // compare the length of the array with the file size (have to match). + unsafe { file_write.write_ptr(payload.data, 5) } + file_write.close() + assert payload.len == os.file_size(file_name) + mut file_read := os.open(os.real_path(file_name)) or { + eprintln('failed to open file $file_name') + return + } + // We only need to test read_bytes because this function calls + // read_bytes_at with second parameter zeroed (size, 0). + rbytes := file_read.read_bytes(5) + // eprintln('rbytes: $rbytes') + // eprintln('payload: $payload') + assert rbytes == payload + // check that trying to read data from EOF doesn't error and returns 0 + mut a := []byte{len: 5} + nread := file_read.read_bytes_into(5, mut a) or { + n := if err is none { + int(0) + } else { + eprintln(err) + int(-1) + } + n + } + assert nread == 0 + file_read.close() + // We finally delete the test file. + os.rm(file_name) or { panic(err) } +} + +fn test_create_and_delete_folder() { + folder := './test1' + os.mkdir(folder) or { panic(err) } + assert os.is_dir(folder) + folder_contents := os.ls(folder) or { panic(err) } + assert folder_contents.len == 0 + os.rmdir(folder) or { panic(err) } + folder_exists := os.is_dir(folder) + assert folder_exists == false +} + +fn walk_callback(file string) { + if file == '.' || file == '..' { + return + } + assert file == 'test_walk' + os.path_separator + 'test1' +} + +fn test_walk() { + folder := 'test_walk' + os.mkdir(folder) or { panic(err) } + file1 := folder + os.path_separator + 'test1' + os.write_file(file1, 'test-1') or { panic(err) } + os.walk(folder, walk_callback) + os.rm(file1) or { panic(err) } + os.rmdir(folder) or { panic(err) } +} + +fn test_cp() { + old_file_name := 'cp_example.txt' + new_file_name := 'cp_new_example.txt' + os.write_file(old_file_name, 'Test data 1 2 3, V is awesome #$%^[]!~⭐') or { panic(err) } + os.cp(old_file_name, new_file_name) or { panic('$err') } + old_file := os.read_file(old_file_name) or { panic(err) } + new_file := os.read_file(new_file_name) or { panic(err) } + assert old_file == new_file + os.rm(old_file_name) or { panic(err) } + os.rm(new_file_name) or { panic(err) } +} + +fn test_mv() { + work_dir := os.join_path(os.getwd(), 'mv_test') + os.mkdir_all(work_dir) or { panic(err) } + // Setup test files + tfile1 := os.join_path(work_dir, 'file') + tfile2 := os.join_path(work_dir, 'file.test') + tfile3 := os.join_path(work_dir, 'file.3') + tfile_content := 'temporary file' + os.write_file(tfile1, tfile_content) or { panic(err) } + os.write_file(tfile2, tfile_content) or { panic(err) } + // Setup test dirs + tdir1 := os.join_path(work_dir, 'dir') + tdir2 := os.join_path(work_dir, 'dir2') + tdir3 := os.join_path(work_dir, 'dir3') + os.mkdir(tdir1) or { panic(err) } + os.mkdir(tdir2) or { panic(err) } + // Move file with no extension to dir + os.mv(tfile1, tdir1) or { panic(err) } + mut expected := os.join_path(tdir1, 'file') + assert os.exists(expected) + assert !os.is_dir(expected) + // Move dir with contents to other dir + os.mv(tdir1, tdir2) or { panic(err) } + expected = os.join_path(tdir2, 'dir') + assert os.exists(expected) + assert os.is_dir(expected) + expected = os.join_path(tdir2, 'dir', 'file') + assert os.exists(expected) + assert !os.is_dir(expected) + // Move dir with contents to other dir (by renaming) + os.mv(os.join_path(tdir2, 'dir'), tdir3) or { panic(err) } + expected = tdir3 + assert os.exists(expected) + assert os.is_dir(expected) + assert os.is_dir_empty(tdir2) + // Move file with extension to dir + os.mv(tfile2, tdir2) or { panic(err) } + expected = os.join_path(tdir2, 'file.test') + assert os.exists(expected) + assert !os.is_dir(expected) + // Move file to dir (by renaming) + os.mv(os.join_path(tdir2, 'file.test'), tfile3) or { panic(err) } + expected = tfile3 + assert os.exists(expected) + assert !os.is_dir(expected) +} + +fn test_cp_all() { + // fileX -> dir/fileX + // NB: clean up of the files happens inside the cleanup_leftovers function + os.write_file('ex1.txt', 'wow!') or { panic(err) } + os.mkdir('ex') or { panic(err) } + os.cp_all('ex1.txt', 'ex', false) or { panic(err) } + old := os.read_file('ex1.txt') or { panic(err) } + new := os.read_file('ex/ex1.txt') or { panic(err) } + assert old == new + os.mkdir('ex/ex2') or { panic(err) } + os.write_file('ex2.txt', 'great!') or { panic(err) } + os.cp_all('ex2.txt', 'ex/ex2', false) or { panic(err) } + old2 := os.read_file('ex2.txt') or { panic(err) } + new2 := os.read_file('ex/ex2/ex2.txt') or { panic(err) } + assert old2 == new2 + // recurring on dir -> local dir + os.cp_all('ex', './', true) or { panic(err) } + // regression test for executive runs with overwrite := true + os.cp_all('ex', './', true) or { panic(err) } + os.cp_all('ex', 'nonexisting', true) or { panic(err) } + assert os.exists(os.join_path('nonexisting', 'ex1.txt')) +} + +fn test_realpath_of_empty_string_works() { + assert os.real_path('') == '' +} + +fn test_realpath_non_existing() { + non_existing_path := 'sdyfuisd_non_existing_file' + rpath := os.real_path(non_existing_path) + $if windows { + // on windows, the workdir is prepended, so the result is absolute: + assert rpath.len > non_existing_path.len + } + $if !windows { + // on unix, the workdir is NOT prepended for now, so the result remains the same. + // TODO: the windows behaviour seems saner, think about normalising the unix case to do the same. + assert os.real_path(non_existing_path) == non_existing_path + } +} + +fn test_realpath_existing() { + existing_file_name := 'existing_file.txt' + existing_file := os.join_path(os.temp_dir(), existing_file_name) + os.rm(existing_file) or {} + os.write_file(existing_file, 'abc') or {} + assert os.exists(existing_file) + rpath := os.real_path(existing_file) + assert os.is_abs_path(rpath) + assert rpath.ends_with(existing_file_name) + os.rm(existing_file) or {} +} + +fn test_realpath_removes_dots() { + examples_folder := os.join_path(@VEXEROOT, 'vlib', 'v', '..', '..', 'cmd', '.', '..', + 'examples') + real_path_of_examples_folder := os.real_path(examples_folder) + assert real_path_of_examples_folder.len < examples_folder.len + assert !real_path_of_examples_folder.contains('..') +} + +fn test_realpath_absolutizes_existing_relative_paths() { + old_wd := os.getwd() + defer { + os.chdir(old_wd) + } + os.chdir(@VEXEROOT) + examples_folder := os.join_path('vlib', 'v', '..', '..', 'cmd', '.', '..', 'examples') + real_path_of_examples_folder := os.real_path(examples_folder) + assert os.is_abs_path(real_path_of_examples_folder) +} + +// TODO: think much more about whether this is desirable: +fn test_realpath_does_not_absolutize_non_existing_relative_paths() { + relative_path := os.join_path('one', 'nonexisting_folder', '..', 'something') + $if !windows { + assert os.real_path(relative_path).contains('..') + assert os.real_path(relative_path) == relative_path + } +} + +fn test_realpath_absolutepath_symlink() ? { + file_name := 'tolink_file.txt' + symlink_name := 'symlink.txt' + mut f := os.create(file_name) ? + f.close() + assert os.symlink(file_name, symlink_name) ? + rpath := os.real_path(symlink_name) + println(rpath) + assert os.is_abs_path(rpath) + assert rpath.ends_with(file_name) + os.rm(symlink_name) or {} + os.rm(file_name) or {} +} + +fn test_tmpdir() { + t := os.temp_dir() + assert t.len > 0 + assert os.is_dir(t) + tfile := t + os.path_separator + 'tmpfile.txt' + os.rm(tfile) or {} // just in case + tfile_content := 'this is a temporary file' + os.write_file(tfile, tfile_content) or { panic(err) } + tfile_content_read := os.read_file(tfile) or { panic(err) } + assert tfile_content_read == tfile_content + os.rm(tfile) or { panic(err) } +} + +fn test_is_writable_folder() { + tmp := os.temp_dir() + f := os.is_writable_folder(tmp) or { + eprintln('err: $err') + false + } + assert f +} + +fn test_make_symlink_check_is_link_and_remove_symlink() { + folder := 'tfolder' + symlink := 'tsymlink' + // windows creates a directory symlink, so delete it with rmdir() + $if windows { + os.rmdir(symlink) or {} + } $else { + os.rm(symlink) or {} + } + os.rmdir(folder) or {} + os.mkdir(folder) or { panic(err) } + folder_contents := os.ls(folder) or { panic(err) } + assert folder_contents.len == 0 + os.symlink(folder, symlink) or { panic(err) } + assert os.is_link(symlink) + $if windows { + os.rmdir(symlink) or { panic(err) } + } $else { + os.rm(symlink) or { panic(err) } + } + os.rmdir(folder) or { panic(err) } + folder_exists := os.is_dir(folder) + assert folder_exists == false + symlink_exists := os.is_link(symlink) + assert symlink_exists == false +} + +fn test_make_symlink_check_is_link_and_remove_symlink_with_file() { + file := 'tfile' + symlink := 'tsymlink' + os.rm(symlink) or {} + os.rm(file) or {} + mut f := os.create(file) or { panic(err) } + f.close() + os.symlink(file, symlink) or { panic(err) } + assert os.is_link(symlink) + os.rm(symlink) or { panic(err) } + os.rm(file) or { panic(err) } + symlink_exists := os.is_link(symlink) + assert symlink_exists == false +} + +fn test_make_hardlink_check_is_link_and_remove_hardlink_with_file() { + file := 'tfile' + symlink := 'tsymlink' + os.rm(symlink) or {} + os.rm(file) or {} + mut f := os.create(file) or { panic(err) } + f.close() + os.link(file, symlink) or { panic(err) } + assert os.exists(symlink) + os.rm(symlink) or { panic(err) } + os.rm(file) or { panic(err) } + symlink_exists := os.is_link(symlink) + assert symlink_exists == false +} + +// fn test_fork() { +// pid := os.fork() +// if pid == 0 { +// println('Child') +// } +// else { +// println('Parent') +// } +// } +// fn test_wait() { +// pid := os.fork() +// if pid == 0 { +// println('Child') +// exit(0) +// } +// else { +// cpid := os.wait() +// println('Parent') +// println(cpid) +// } +// } +fn test_symlink() { + os.mkdir('symlink') or { panic(err) } + os.symlink('symlink', 'symlink2') or { panic(err) } + assert os.exists('symlink2') + // cleanup + os.rmdir('symlink') or { panic(err) } + $if windows { + os.rmdir('symlink2') or { panic(err) } + } $else { + os.rm('symlink2') or { panic(err) } + } +} + +fn test_is_executable_writable_readable() { + file_name := 'rwxfile.exe' + mut f := os.create(file_name) or { + eprintln('failed to create file $file_name') + return + } + f.close() + $if !windows { + os.chmod(file_name, 0o600) // mark as readable && writable, but NOT executable + assert os.is_writable(file_name) + assert os.is_readable(file_name) + assert !os.is_executable(file_name) + os.chmod(file_name, 0o700) // mark as executable too + assert os.is_executable(file_name) + } $else { + assert os.is_writable(file_name) + assert os.is_readable(file_name) + assert os.is_executable(file_name) + } + // We finally delete the test file. + os.rm(file_name) or { panic(err) } +} + +fn test_ext() { + assert os.file_ext('file.v') == '.v' + assert os.file_ext('file') == '' +} + +fn test_is_abs() { + assert os.is_abs_path('/home/user') + assert os.is_abs_path('v/vlib') == false + $if windows { + assert os.is_abs_path('C:\\Windows\\') + } +} + +fn test_join() { + $if windows { + assert os.join_path('v', 'vlib', 'os') == 'v\\vlib\\os' + } $else { + assert os.join_path('v', 'vlib', 'os') == 'v/vlib/os' + } +} + +fn test_rmdir_all() { + mut dirs := ['some/dir', 'some/.hidden/directory'] + $if windows { + for mut d in dirs { + d = d.replace('/', '\\') + } + } + for d in dirs { + os.mkdir_all(d) or { panic(err) } + assert os.is_dir(d) + } + os.rmdir_all('some') or { assert false } + assert !os.exists('some') +} + +fn test_dir() { + $if windows { + assert os.dir('C:\\a\\b\\c') == 'C:\\a\\b' + assert os.dir('C:\\a\\b\\') == 'C:\\a\\b' + assert os.dir('C:/a/b/c') == 'C:\\a\\b' + assert os.dir('C:/a/b/') == 'C:\\a\\b' + } $else { + assert os.dir('/') == '/' + assert os.dir('/abc') == '/' + assert os.dir('/var/tmp/foo') == '/var/tmp' + assert os.dir('/var/tmp/') == '/var/tmp' + assert os.dir('C:\\a\\b\\c') == 'C:/a/b' + assert os.dir('C:\\a\\b\\') == 'C:/a/b' + } + assert os.dir('os') == '.' +} + +fn test_base() { + $if windows { + assert os.base('v\\vlib\\os') == 'os' + assert os.base('v\\vlib\\os\\') == 'os' + assert os.base('v/vlib/os') == 'os' + assert os.base('v/vlib/os/') == 'os' + } $else { + assert os.base('v/vlib/os') == 'os' + assert os.base('v/vlib/os/') == 'os' + assert os.base('v\\vlib\\os') == 'os' + assert os.base('v\\vlib\\os\\') == 'os' + } + assert os.base('filename') == 'filename' +} + +fn test_file_name() { + $if windows { + assert os.file_name('v\\vlib\\os\\os.v') == 'os.v' + assert os.file_name('v\\vlib\\os\\') == '' + assert os.file_name('v\\vlib\\os') == 'os' + } $else { + assert os.file_name('v/vlib/os/os.v') == 'os.v' + assert os.file_name('v/vlib/os/') == '' + assert os.file_name('v/vlib/os') == 'os' + } + assert os.file_name('filename') == 'filename' +} + +fn test_uname() { + u := os.uname() + assert u.sysname.len > 0 + assert u.nodename.len > 0 + assert u.release.len > 0 + assert u.version.len > 0 + assert u.machine.len > 0 +} + +// tests for write_file_array and read_file_array: +const ( + maxn = 3 +) + +struct IntPoint { + x int + y int +} + +fn test_write_file_array_bytes() { + fpath := './abytes.bin' + mut arr := []byte{len: maxn} + for i in 0 .. maxn { + arr[i] = 65 + byte(i) + } + os.write_file_array(fpath, arr) or { panic(err) } + rarr := os.read_bytes(fpath) or { panic(err) } + assert arr == rarr + // eprintln(arr.str()) + // eprintln(rarr.str()) +} + +fn test_write_file_array_structs() { + fpath := './astructs.bin' + mut arr := []IntPoint{len: maxn} + for i in 0 .. maxn { + arr[i] = IntPoint{65 + i, 65 + i + 10} + } + os.write_file_array(fpath, arr) or { panic(err) } + rarr := os.read_file_array(fpath) + assert rarr == arr + assert rarr.len == maxn + // eprintln( rarr.str().replace('\n', ' ').replace('},', '},\n')) +} + +fn test_stdout_capture() { + /* + mut cmd := os.Command{ + path:'cat' + redirect_stdout: true +} +cmd.start() +for !cmd.eof { + line := cmd.read_line() + println('line="$line"') +} +cmd.close() + */ +} + +fn test_posix_set_bit() { + $if windows { + assert true + } $else { + fpath := '/tmp/permtest' + os.create(fpath) or { panic("Couldn't create file") } + os.chmod(fpath, 0o0777) + c_fpath := &char(fpath.str) + mut s := C.stat{} + unsafe { + C.stat(c_fpath, &s) + } + // Take the permissions part of the mode + mut mode := u32(s.st_mode) & 0o0777 + assert mode == 0o0777 + // `chmod u-r` + os.posix_set_permission_bit(fpath, os.s_irusr, false) + unsafe { + C.stat(c_fpath, &s) + } + mode = u32(s.st_mode) & 0o0777 + assert mode == 0o0377 + // `chmod u+r` + os.posix_set_permission_bit(fpath, os.s_irusr, true) + unsafe { + C.stat(c_fpath, &s) + } + mode = u32(s.st_mode) & 0o0777 + assert mode == 0o0777 + // NB: setting the sticky bit is platform dependend + // `chmod -s -g -t` + os.posix_set_permission_bit(fpath, os.s_isuid, false) + os.posix_set_permission_bit(fpath, os.s_isgid, false) + os.posix_set_permission_bit(fpath, os.s_isvtx, false) + unsafe { + C.stat(c_fpath, &s) + } + mode = u32(s.st_mode) & 0o0777 + assert mode == 0o0777 + // `chmod g-w o-w` + os.posix_set_permission_bit(fpath, os.s_iwgrp, false) + os.posix_set_permission_bit(fpath, os.s_iwoth, false) + unsafe { + C.stat(c_fpath, &s) + } + mode = u32(s.st_mode) & 0o7777 + assert mode == 0o0755 + os.rm(fpath) or {} + } +} + +fn test_exists_in_system_path() { + assert os.exists_in_system_path('') == false + $if windows { + assert os.exists_in_system_path('cmd.exe') + return + } + assert os.exists_in_system_path('ls') +} + +fn test_truncate() { + filename := './test_trunc.txt' + hello := 'hello world!' + mut f := os.create(filename) or { panic(err) } + f.write_string(hello) or { panic(err) } + f.close() + assert hello.len == os.file_size(filename) + newlen := u64(40000) + os.truncate(filename, newlen) or { panic(err) } + assert newlen == os.file_size(filename) + os.rm(filename) or { panic(err) } +} + +fn test_hostname() { + assert os.hostname().len > 2 +} + +fn test_glob() { + os.mkdir('test_dir') or { panic(err) } + for i in 0 .. 4 { + if i == 3 { + mut f := os.create('test_dir/test0_another') or { panic(err) } + f.close() + mut f1 := os.create('test_dir/test') or { panic(err) } + f1.close() + } else { + mut f := os.create('test_dir/test' + i.str()) or { panic(err) } + f.close() + } + } + files := os.glob('test_dir/t*') or { panic(err) } + assert files.len == 5 + assert os.base(files[0]) == 'test' + + for i in 0 .. 3 { + os.rm('test_dir/test' + i.str()) or { panic(err) } + } + os.rm('test_dir/test0_another') or { panic(err) } + os.rm('test_dir/test') or { panic(err) } + os.rmdir_all('test_dir') or { panic(err) } +} + +fn test_utime() { + filename := './test_utime.txt' + hello := 'hello world!' + mut f := os.create(filename) or { panic(err) } + defer { + f.close() + os.rm(filename) or { panic(err) } + } + f.write_string(hello) or { panic(err) } + atime := time.now().add_days(2).unix_time() + mtime := time.now().add_days(4).unix_time() + os.utime(filename, atime, mtime) or { panic(err) } + assert os.file_last_mod_unix(filename) == mtime +} diff --git a/v_windows/v/old/vlib/os/os_windows.c.v b/v_windows/v/old/vlib/os/os_windows.c.v new file mode 100644 index 0000000..a920601 --- /dev/null +++ b/v_windows/v/old/vlib/os/os_windows.c.v @@ -0,0 +1,544 @@ +module os + +import strings + +#flag windows -l advapi32 +#include +#include + +// See https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createsymboliclinkw +fn C.CreateSymbolicLinkW(&u16, &u16, u32) int + +// See https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createhardlinkw +fn C.CreateHardLinkW(&u16, &u16, C.SECURITY_ATTRIBUTES) int + +fn C._getpid() int + +pub const ( + path_separator = '\\' + path_delimiter = ';' +) + +// Ref - https://docs.microsoft.com/en-us/windows/desktop/winprog/windows-data-types +// A handle to an object. +pub type HANDLE = voidptr +pub type HMODULE = voidptr + +// win: FILETIME +// https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime +struct Filetime { + dw_low_date_time u32 + dw_high_date_time u32 +} + +// win: WIN32_FIND_DATA +// https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-win32_find_dataw +struct Win32finddata { +mut: + dw_file_attributes u32 + ft_creation_time Filetime + ft_last_access_time Filetime + ft_last_write_time Filetime + n_file_size_high u32 + n_file_size_low u32 + dw_reserved0 u32 + dw_reserved1 u32 + c_file_name [260]u16 // max_path_len = 260 + c_alternate_file_name [14]u16 // 14 + dw_file_type u32 + dw_creator_type u32 + w_finder_flags u16 +} + +struct ProcessInformation { +mut: + h_process voidptr + h_thread voidptr + dw_process_id u32 + dw_thread_id u32 +} + +struct StartupInfo { +mut: + cb u32 + lp_reserved &u16 + lp_desktop &u16 + lp_title &u16 + dw_x u32 + dw_y u32 + dw_x_size u32 + dw_y_size u32 + dw_x_count_chars u32 + dw_y_count_chars u32 + dw_fill_attributes u32 + dw_flags u32 + w_show_window u16 + cb_reserved2 u16 + lp_reserved2 &byte + h_std_input voidptr + h_std_output voidptr + h_std_error voidptr +} + +struct SecurityAttributes { +mut: + n_length u32 + lp_security_descriptor voidptr + b_inherit_handle bool +} + +struct C._utimbuf { + actime int + modtime int +} + +fn C._utime(&char, voidptr) int + +fn init_os_args_wide(argc int, argv &&byte) []string { + mut args_ := []string{} + for i in 0 .. argc { + args_ << unsafe { string_from_wide(&u16(argv[i])) } + } + return args_ +} + +fn native_glob_pattern(pattern string, mut matches []string) ? { + $if debug { + // FindFirstFile() and FindNextFile() both have a globbing function. + // Unfortunately this is not as pronounced as under Unix, but should provide some functionality + eprintln('os.glob() does not have all the features on Windows as it has on Unix operating systems') + } + mut find_file_data := Win32finddata{} + wpattern := pattern.replace('/', '\\').to_wide() + h_find_files := C.FindFirstFile(wpattern, voidptr(&find_file_data)) + + defer { + C.FindClose(h_find_files) + } + + if h_find_files == C.INVALID_HANDLE_VALUE { + return error('os.glob(): Could not get a file handle: ' + + get_error_msg(int(C.GetLastError()))) + } + + // save first finding + fname := unsafe { string_from_wide(&find_file_data.c_file_name[0]) } + if fname !in ['.', '..'] { + mut fp := fname.replace('\\', '/') + if find_file_data.dw_file_attributes & u32(C.FILE_ATTRIBUTE_DIRECTORY) > 0 { + fp += '/' + } + matches << fp + } + + // check and save next findings + for i := 0; C.FindNextFile(h_find_files, voidptr(&find_file_data)) > 0; i++ { + filename := unsafe { string_from_wide(&find_file_data.c_file_name[0]) } + if filename in ['.', '..'] { + continue + } + mut fpath := filename.replace('\\', '/') + if find_file_data.dw_file_attributes & u32(C.FILE_ATTRIBUTE_DIRECTORY) > 0 { + fpath += '/' + } + matches << fpath + } +} + +pub fn utime(path string, actime int, modtime int) ? { + mut u := C._utimbuf{actime, modtime} + if C._utime(&char(path.str), voidptr(&u)) != 0 { + return error_with_code(posix_get_error_msg(C.errno), C.errno) + } +} + +pub fn ls(path string) ?[]string { + mut find_file_data := Win32finddata{} + mut dir_files := []string{} + // We can also check if the handle is valid. but using is_dir instead + // h_find_dir := C.FindFirstFile(path.str, &find_file_data) + // if (invalid_handle_value == h_find_dir) { + // return dir_files + // } + // C.FindClose(h_find_dir) + if !is_dir(path) { + return error('ls() couldnt open dir "$path": directory does not exist') + } + // NOTE: Should eventually have path struct & os dependant path seperator (eg os.PATH_SEPERATOR) + // we need to add files to path eg. c:\windows\*.dll or :\windows\* + path_files := '$path\\*' + // NOTE:TODO: once we have a way to convert utf16 wide character to utf8 + // we should use FindFirstFileW and FindNextFileW + h_find_files := C.FindFirstFile(path_files.to_wide(), voidptr(&find_file_data)) + first_filename := unsafe { string_from_wide(&find_file_data.c_file_name[0]) } + if first_filename != '.' && first_filename != '..' { + dir_files << first_filename + } + for C.FindNextFile(h_find_files, voidptr(&find_file_data)) > 0 { + filename := unsafe { string_from_wide(&find_file_data.c_file_name[0]) } + if filename != '.' && filename != '..' { + dir_files << filename.clone() + } + } + C.FindClose(h_find_files) + return dir_files +} + +/* +pub fn is_dir(path string) bool { + _path := path.replace('/', '\\') + attr := C.GetFileAttributesW(_path.to_wide()) + if int(attr) == int(C.INVALID_FILE_ATTRIBUTES) { + return false + } + if (int(attr) & C.FILE_ATTRIBUTE_DIRECTORY) != 0 { + return true + } + return false +} +*/ +// mkdir creates a new directory with the specified path. +pub fn mkdir(path string) ?bool { + if path == '.' { + return true + } + apath := real_path(path) + if !C.CreateDirectory(apath.to_wide(), 0) { + return error('mkdir failed for "$apath", because CreateDirectory returned: ' + + get_error_msg(int(C.GetLastError()))) + } + return true +} + +// Ref - https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/get-osfhandle?view=vs-2019 +// get_file_handle retrieves the operating-system file handle that is associated with the specified file descriptor. +pub fn get_file_handle(path string) HANDLE { + cfile := vfopen(path, 'rb') or { return HANDLE(invalid_handle_value) } + handle := HANDLE(C._get_osfhandle(fileno(cfile))) // CreateFile? - hah, no -_- + return handle +} + +// Ref - https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulefilenamea +// get_module_filename retrieves the fully qualified path for the file that contains the specified module. +// The module must have been loaded by the current process. +pub fn get_module_filename(handle HANDLE) ?string { + unsafe { + mut sz := 4096 // Optimized length + mut buf := &u16(malloc_noscan(4096)) + for { + status := int(C.GetModuleFileNameW(handle, voidptr(&buf), sz)) + match status { + success { + return string_from_wide2(buf, sz) + } + else { + // Must handled with GetLastError and converted by FormatMessage + return error('Cannot get file name from handle') + } + } + } + } + panic('this should be unreachable') // TODO remove unreachable after loop +} + +// Ref - https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-formatmessagea#parameters +const ( + format_message_allocate_buffer = 0x00000100 + format_message_argument_array = 0x00002000 + format_message_from_hmodule = 0x00000800 + format_message_from_string = 0x00000400 + format_message_from_system = 0x00001000 + format_message_ignore_inserts = 0x00000200 +) + +// Ref - winnt.h +const ( + sublang_neutral = 0x00 + sublang_default = 0x01 + lang_neutral = sublang_neutral +) + +// Ref - https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--12000-15999- +const ( + max_error_code = 15841 // ERROR_API_UNAVAILABLE +) + +// ptr_win_get_error_msg return string (voidptr) +// representation of error, only for windows. +fn ptr_win_get_error_msg(code u32) voidptr { + mut buf := voidptr(0) + // Check for code overflow + if code > u32(os.max_error_code) { + return buf + } + C.FormatMessage(os.format_message_allocate_buffer | os.format_message_from_system | os.format_message_ignore_inserts, + 0, code, C.MAKELANGID(os.lang_neutral, os.sublang_default), voidptr(&buf), 0, + 0) + return buf +} + +// get_error_msg return error code representation in string. +pub fn get_error_msg(code int) string { + if code < 0 { // skip negative + return '' + } + ptr_text := ptr_win_get_error_msg(u32(code)) + if ptr_text == 0 { // compare with null + return '' + } + return unsafe { string_from_wide(ptr_text) } +} + +// execute starts the specified command, waits for it to complete, and returns its output. +pub fn execute(cmd string) Result { + if cmd.contains(';') || cmd.contains('&&') || cmd.contains('||') || cmd.contains('\n') { + return Result{ + exit_code: -1 + output: ';, &&, || and \\n are not allowed in shell commands' + } + } + mut child_stdin := &u32(0) + mut child_stdout_read := &u32(0) + mut child_stdout_write := &u32(0) + mut sa := SecurityAttributes{} + sa.n_length = sizeof(C.SECURITY_ATTRIBUTES) + sa.b_inherit_handle = true + create_pipe_ok := C.CreatePipe(voidptr(&child_stdout_read), voidptr(&child_stdout_write), + voidptr(&sa), 0) + if !create_pipe_ok { + error_num := int(C.GetLastError()) + error_msg := get_error_msg(error_num) + return Result{ + exit_code: error_num + output: 'exec failed (CreatePipe): $error_msg' + } + } + set_handle_info_ok := C.SetHandleInformation(child_stdout_read, C.HANDLE_FLAG_INHERIT, + 0) + if !set_handle_info_ok { + error_num := int(C.GetLastError()) + error_msg := get_error_msg(error_num) + return Result{ + exit_code: error_num + output: 'exec failed (SetHandleInformation): $error_msg' + } + } + proc_info := ProcessInformation{} + start_info := StartupInfo{ + lp_reserved2: 0 + lp_reserved: 0 + lp_desktop: 0 + lp_title: 0 + cb: sizeof(C.PROCESS_INFORMATION) + h_std_input: child_stdin + h_std_output: child_stdout_write + h_std_error: child_stdout_write + dw_flags: u32(C.STARTF_USESTDHANDLES) + } + command_line := [32768]u16{} + C.ExpandEnvironmentStringsW(cmd.to_wide(), voidptr(&command_line), 32768) + create_process_ok := C.CreateProcessW(0, &command_line[0], 0, 0, C.TRUE, 0, 0, 0, + voidptr(&start_info), voidptr(&proc_info)) + if !create_process_ok { + error_num := int(C.GetLastError()) + error_msg := get_error_msg(error_num) + return Result{ + exit_code: error_num + output: 'exec failed (CreateProcess) with code $error_num: $error_msg cmd: $cmd' + } + } + C.CloseHandle(child_stdin) + C.CloseHandle(child_stdout_write) + buf := [4096]byte{} + mut bytes_read := u32(0) + mut read_data := strings.new_builder(1024) + for { + mut result := false + unsafe { + result = C.ReadFile(child_stdout_read, &buf[0], 1000, voidptr(&bytes_read), + 0) + read_data.write_ptr(&buf[0], int(bytes_read)) + } + if result == false || int(bytes_read) == 0 { + break + } + } + soutput := read_data.str().trim_space() + unsafe { read_data.free() } + exit_code := u32(0) + C.WaitForSingleObject(proc_info.h_process, C.INFINITE) + C.GetExitCodeProcess(proc_info.h_process, voidptr(&exit_code)) + C.CloseHandle(proc_info.h_process) + C.CloseHandle(proc_info.h_thread) + return Result{ + output: soutput + exit_code: int(exit_code) + } +} + +pub fn symlink(origin string, target string) ?bool { + // this is a temporary fix for TCC32 due to runtime error + // TODO: find the cause why TCC32 for Windows does not work without the compiletime option + $if x64 || x32 { + mut flags := 0 + if is_dir(origin) { + flags ^= 1 + } + + flags ^= 2 // SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE + res := C.CreateSymbolicLinkW(target.to_wide(), origin.to_wide(), flags) + + // 1 = success, != 1 failure => https://stackoverflow.com/questions/33010440/createsymboliclink-on-windows-10 + if res != 1 { + return error(get_error_msg(int(C.GetLastError()))) + } + if !exists(target) { + return error('C.CreateSymbolicLinkW reported success, but symlink still does not exist') + } + return true + } + return false +} + +pub fn link(origin string, target string) ?bool { + res := C.CreateHardLinkW(target.to_wide(), origin.to_wide(), C.NULL) + // 1 = success, != 1 failure => https://stackoverflow.com/questions/33010440/createsymboliclink-on-windows-10 + if res != 1 { + return error(get_error_msg(int(C.GetLastError()))) + } + if !exists(target) { + return error('C.CreateHardLinkW reported success, but link still does not exist') + } + return true +} + +pub fn (mut f File) close() { + if !f.is_opened { + return + } + f.is_opened = false + C.fflush(f.cfile) + C.fclose(f.cfile) +} + +pub struct ExceptionRecord { +pub: + // status_ constants + code u32 + flags u32 + record &ExceptionRecord + address voidptr + param_count u32 + // params []voidptr +} + +pub struct ContextRecord { + // TODO +} + +pub struct ExceptionPointers { +pub: + exception_record &ExceptionRecord + context_record &ContextRecord +} + +pub type VectoredExceptionHandler = fn (&ExceptionPointers) u32 + +// This is defined in builtin because we use vectored exception handling +// for our unhandled exception handler on windows +// As a result this definition is commented out to prevent +// duplicate definitions from displeasing the compiler +// fn C.AddVectoredExceptionHandler(u32, VectoredExceptionHandler) +pub fn add_vectored_exception_handler(first bool, handler VectoredExceptionHandler) { + C.AddVectoredExceptionHandler(u32(first), C.PVECTORED_EXCEPTION_HANDLER(handler)) +} + +// this is defined in builtin_windows.c.v in builtin +// fn C.IsDebuggerPresent() bool +pub fn debugger_present() bool { + return C.IsDebuggerPresent() +} + +pub fn uname() Uname { + sys_and_ver := execute('cmd /c ver').output.split('[') + nodename := hostname() + machine := getenv('PROCESSOR_ARCHITECTURE') + return Uname{ + sysname: sys_and_ver[0].trim_space() + nodename: nodename + release: sys_and_ver[1].replace(']', '') + version: sys_and_ver[0] + '[' + sys_and_ver[1] + machine: machine + } +} + +pub fn hostname() string { + hostname := [255]u16{} + size := u32(255) + res := C.GetComputerNameW(&hostname[0], &size) + if !res { + return get_error_msg(int(C.GetLastError())) + } + return unsafe { string_from_wide(&hostname[0]) } +} + +pub fn loginname() string { + loginname := [255]u16{} + size := u32(255) + res := C.GetUserNameW(&loginname[0], &size) + if !res { + return get_error_msg(int(C.GetLastError())) + } + return unsafe { string_from_wide(&loginname[0]) } +} + +// `is_writable_folder` - `folder` exists and is writable to the process +pub fn is_writable_folder(folder string) ?bool { + if !exists(folder) { + return error('`$folder` does not exist') + } + if !is_dir(folder) { + return error('`folder` is not a folder') + } + tmp_perm_check := join_path(folder, 'tmp_perm_check_pid_' + getpid().str()) + mut f := open_file(tmp_perm_check, 'w+', 0o700) or { + return error('cannot write to folder $folder: $err') + } + f.close() + rm(tmp_perm_check) ? + return true +} + +[inline] +pub fn getpid() int { + return C._getpid() +} + +[inline] +pub fn getppid() int { + return 0 +} + +[inline] +pub fn getuid() int { + return 0 +} + +[inline] +pub fn geteuid() int { + return 0 +} + +[inline] +pub fn getgid() int { + return 0 +} + +[inline] +pub fn getegid() int { + return 0 +} + +pub fn posix_set_permission_bit(path_s string, mode u32, enable bool) { + // windows has no concept of a permission mask, so do nothing +} diff --git a/v_windows/v/old/vlib/os/process.v b/v_windows/v/old/vlib/os/process.v new file mode 100644 index 0000000..8fa5e76 --- /dev/null +++ b/v_windows/v/old/vlib/os/process.v @@ -0,0 +1,317 @@ +module os + +// ProcessState.not_started - the process has not yet started +// ProcessState.running - the process is currently running +// ProcessState.stopped - the process was running, but was stopped temporarily +// ProcessState.exited - the process has finished/exited +// ProcessState.aborted - the process was terminated by a signal +// ProcessState.closed - the process resources like opened file descriptors were freed/discarded, final state. +pub enum ProcessState { + not_started + running + stopped + exited + aborted + closed +} + +[heap] +pub struct Process { +pub: + filename string // the process's command file path +pub mut: + pid int // the PID of the process + code int = -1 + // the exit code of the process, != -1 *only* when status is .exited *and* the process was not aborted + status ProcessState = .not_started + // the current status of the process + err string // if the process fails, contains the reason why + args []string // the arguments that the command takes + env_is_custom bool // true, when the environment was customized with .set_environment + env []string // the environment with which the process was started (list of 'var=val') + use_stdio_ctl bool // when true, then you can use p.stdin_write(), p.stdout_slurp() and p.stderr_slurp() + use_pgroup bool // when true, the process will create a new process group, enabling .signal_pgkill() + stdio_fd [3]int // the stdio file descriptors for the child process, used only by the nix implementation + wdata voidptr // the WProcess; used only by the windows implementation +} + +// new_process - create a new process descriptor +// NB: new does NOT start the new process. +// That is done because you may want to customize it first, +// by calling different set_ methods on it. +// In order to start it, call p.run() or p.wait() +pub fn new_process(filename string) &Process { + return &Process{ + filename: filename + stdio_fd: [-1, -1, -1]! + } +} + +// set_args - set the arguments for the new process +pub fn (mut p Process) set_args(pargs []string) { + if p.status != .not_started { + return + } + p.args = pargs + return +} + +// set_environment - set a custom environment variable mapping for the new process +pub fn (mut p Process) set_environment(envs map[string]string) { + if p.status != .not_started { + return + } + p.env_is_custom = true + p.env = []string{} + for k, v in envs { + p.env << '$k=$v' + } + return +} + +// run - starts the new process +pub fn (mut p Process) run() { + if p.status != .not_started { + return + } + p._spawn() + return +} + +// signal_kill - kills the process, after that it is no longer running +pub fn (mut p Process) signal_kill() { + if p.status !in [.running, .stopped] { + return + } + p._signal_kill() + p.status = .aborted + return +} + +// signal_pgkill - kills the whole process group +pub fn (mut p Process) signal_pgkill() { + if p.status !in [.running, .stopped] { + return + } + p._signal_pgkill() + return +} + +// signal_stop - stops the process, you can resume it with p.signal_continue() +pub fn (mut p Process) signal_stop() { + if p.status != .running { + return + } + p._signal_stop() + p.status = .stopped + return +} + +// signal_continue - tell a stopped process to continue/resume its work +pub fn (mut p Process) signal_continue() { + if p.status != .stopped { + return + } + p._signal_continue() + p.status = .running + return +} + +// wait - wait for a process to finish. +// NB: You have to call p.wait(), otherwise a finished process +// would get to a zombie state, and its resources will not get +// released fully, until its parent process exits. +// NB: This call will block the calling process until the child +// process is finished. +pub fn (mut p Process) wait() { + if p.status == .not_started { + p._spawn() + } + if p.status !in [.running, .stopped] { + return + } + p._wait() + return +} + +// close - free the OS resources associated with the process. +// Can be called multiple times, but will free the resources just once. +// This sets the process state to .closed, which is final. +pub fn (mut p Process) close() { + if p.status in [.not_started, .closed] { + return + } + p.status = .closed + $if !windows { + for i in 0 .. 3 { + if p.stdio_fd[i] != 0 { + fd_close(p.stdio_fd[i]) + } + } + } +} + +[unsafe] +pub fn (mut p Process) free() { + p.close() + unsafe { + p.filename.free() + p.err.free() + p.args.free() + p.env.free() + } +} + +// +// _spawn - should not be called directly, but only by p.run()/p.wait() . +// It encapsulates the fork/execve mechanism that allows the +// asynchronous starting of the new child process. +fn (mut p Process) _spawn() int { + if !p.env_is_custom { + p.env = []string{} + current_environment := environ() + for k, v in current_environment { + p.env << '$k=$v' + } + } + mut pid := 0 + $if windows { + pid = p.win_spawn_process() + } $else { + pid = p.unix_spawn_process() + } + p.pid = pid + p.status = .running + return 0 +} + +// is_alive - query whether the process p.pid is still alive +pub fn (mut p Process) is_alive() bool { + if p.status in [.running, .stopped] { + return p._is_alive() + } + return false +} + +// +pub fn (mut p Process) set_redirect_stdio() { + p.use_stdio_ctl = true + return +} + +pub fn (mut p Process) stdin_write(s string) { + p._check_redirection_call('stdin_write') + $if windows { + p.win_write_string(0, s) + } $else { + fd_write(p.stdio_fd[0], s) + } +} + +// will read from stdout pipe, will only return when EOF (end of file) or data +// means this will block unless there is data +pub fn (mut p Process) stdout_slurp() string { + p._check_redirection_call('stdout_slurp') + $if windows { + return p.win_slurp(1) + } $else { + return fd_slurp(p.stdio_fd[1]).join('') + } +} + +// read from stderr pipe, wait for data or EOF +pub fn (mut p Process) stderr_slurp() string { + p._check_redirection_call('stderr_slurp') + $if windows { + return p.win_slurp(2) + } $else { + return fd_slurp(p.stdio_fd[2]).join('') + } +} + +// read from stdout, return if data or not +pub fn (mut p Process) stdout_read() string { + p._check_redirection_call('stdout_read') + $if windows { + s, _ := p.win_read_string(1, 4096) + return s + } $else { + s, _ := fd_read(p.stdio_fd[1], 4096) + return s + } +} + +pub fn (mut p Process) stderr_read() string { + p._check_redirection_call('stderr_read') + $if windows { + s, _ := p.win_read_string(2, 4096) + return s + } $else { + s, _ := fd_read(p.stdio_fd[2], 4096) + return s + } +} + +// _check_redirection_call - should be called just by stdxxx methods +fn (mut p Process) _check_redirection_call(fn_name string) { + if !p.use_stdio_ctl { + panic('Call p.set_redirect_stdio() before calling p.$fn_name') + } + if p.status == .not_started { + panic('Call p.${fn_name}() after you have called p.run()') + } +} + +// _signal_stop - should not be called directly, except by p.signal_stop +fn (mut p Process) _signal_stop() { + $if windows { + p.win_stop_process() + } $else { + p.unix_stop_process() + } +} + +// _signal_continue - should not be called directly, just by p.signal_continue +fn (mut p Process) _signal_continue() { + $if windows { + p.win_resume_process() + } $else { + p.unix_resume_process() + } +} + +// _signal_kill - should not be called directly, except by p.signal_kill +fn (mut p Process) _signal_kill() { + $if windows { + p.win_kill_process() + } $else { + p.unix_kill_process() + } +} + +// _signal_pgkill - should not be called directly, except by p.signal_pgkill +fn (mut p Process) _signal_pgkill() { + $if windows { + p.win_kill_pgroup() + } $else { + p.unix_kill_pgroup() + } +} + +// _wait - should not be called directly, except by p.wait() +fn (mut p Process) _wait() { + $if windows { + p.win_wait() + } $else { + p.unix_wait() + } +} + +// _is_alive - should not be called directly, except by p.is_alive() +fn (mut p Process) _is_alive() bool { + $if windows { + return p.win_is_alive() + } $else { + return p.unix_is_alive() + } +} diff --git a/v_windows/v/old/vlib/os/process_nix.c.v b/v_windows/v/old/vlib/os/process_nix.c.v new file mode 100644 index 0000000..74140d1 --- /dev/null +++ b/v_windows/v/old/vlib/os/process_nix.c.v @@ -0,0 +1,146 @@ +module os + +fn C.setpgid(pid int, pgid int) int + +fn (mut p Process) unix_spawn_process() int { + mut pipeset := [6]int{} + if p.use_stdio_ctl { + _ = C.pipe(&pipeset[0]) // pipe read end 0 <- 1 pipe write end + _ = C.pipe(&pipeset[2]) // pipe read end 2 <- 3 pipe write end + _ = C.pipe(&pipeset[4]) // pipe read end 4 <- 5 pipe write end + } + pid := fork() + if pid != 0 { + // This is the parent process after the fork. + // NB: pid contains the process ID of the child process + if p.use_stdio_ctl { + p.stdio_fd[0] = pipeset[1] // store the write end of child's in + p.stdio_fd[1] = pipeset[2] // store the read end of child's out + p.stdio_fd[2] = pipeset[4] // store the read end of child's err + // close the rest of the pipe fds, the parent does not need them + fd_close(pipeset[0]) + fd_close(pipeset[3]) + fd_close(pipeset[5]) + } + return pid + } + // + // Here, we are in the child process. + // It still shares file descriptors with the parent process, + // but it is otherwise independant and can do stuff *without* + // affecting the parent process. + // + if p.use_pgroup { + C.setpgid(0, 0) + } + if p.use_stdio_ctl { + // Redirect the child standart in/out/err to the pipes that + // were created in the parent. + // Close the parent's pipe fds, the child do not need them: + fd_close(pipeset[1]) + fd_close(pipeset[2]) + fd_close(pipeset[4]) + // redirect the pipe fds to the child's in/out/err fds: + C.dup2(pipeset[0], 0) + C.dup2(pipeset[3], 1) + C.dup2(pipeset[5], 2) + // close the pipe fdsx after the redirection + fd_close(pipeset[0]) + fd_close(pipeset[3]) + fd_close(pipeset[5]) + } + execve(p.filename, p.args, p.env) or { + eprintln(err) + exit(1) + } + return 0 +} + +fn (mut p Process) unix_stop_process() { + C.kill(p.pid, C.SIGSTOP) +} + +fn (mut p Process) unix_resume_process() { + C.kill(p.pid, C.SIGCONT) +} + +fn (mut p Process) unix_kill_process() { + C.kill(p.pid, C.SIGKILL) +} + +fn (mut p Process) unix_kill_pgroup() { + C.kill(-p.pid, C.SIGKILL) +} + +fn (mut p Process) unix_wait() { + cstatus := 0 + ret := C.waitpid(p.pid, &cstatus, 0) + if ret == -1 { + p.err = posix_get_error_msg(C.errno) + return + } + pret, is_signaled := posix_wait4_to_exit_status(cstatus) + if is_signaled { + p.status = .aborted + p.err = 'Terminated by signal ${ret:2d} (${sigint_to_signal_name(pret)})' + } else { + p.status = .exited + } + p.code = pret +} + +fn (mut p Process) unix_is_alive() bool { + cstatus := 0 + ret := C.waitpid(p.pid, &cstatus, C.WNOHANG) + if ret == -1 { + p.err = posix_get_error_msg(C.errno) + return false + } + if ret == 0 { + return true + } + pret, is_signaled := posix_wait4_to_exit_status(cstatus) + if is_signaled { + p.status = .aborted + p.err = 'Terminated by signal ${ret:2d} (${sigint_to_signal_name(pret)})' + } else { + p.status = .exited + } + p.code = pret + return false +} + +// these are here to make v_win.c/v.c generation work in all cases: +fn (mut p Process) win_spawn_process() int { + return 0 +} + +fn (mut p Process) win_stop_process() { +} + +fn (mut p Process) win_resume_process() { +} + +fn (mut p Process) win_kill_process() { +} + +fn (mut p Process) win_kill_pgroup() { +} + +fn (mut p Process) win_wait() { +} + +fn (mut p Process) win_is_alive() bool { + return false +} + +fn (mut p Process) win_write_string(idx int, s string) { +} + +fn (mut p Process) win_read_string(idx int, maxbytes int) (string, int) { + return '', 0 +} + +fn (mut p Process) win_slurp(idx int) string { + return '' +} diff --git a/v_windows/v/old/vlib/os/process_test.v b/v_windows/v/old/vlib/os/process_test.v new file mode 100644 index 0000000..5301472 --- /dev/null +++ b/v_windows/v/old/vlib/os/process_test.v @@ -0,0 +1,96 @@ +import os +import time + +const ( + vexe = os.getenv('VEXE') + vroot = os.dir(vexe) + test_os_process = os.join_path(os.temp_dir(), 'v', 'test_os_process.exe') + test_os_process_source = os.join_path(vroot, 'cmd/tools/test_os_process.v') +) + +fn testsuite_begin() ? { + os.rm(test_os_process) or {} + if os.getenv('WINE_TEST_OS_PROCESS_EXE') != '' { + // Make it easier to run the test under wine emulation, by just + // prebuilding the executable with: + // v -os windows -o x.exe cmd/tools/test_os_process.v + // WINE_TEST_OS_PROCESS_EXE=x.exe ./v -os windows vlib/os/process_test.v + os.cp(os.getenv('WINE_TEST_OS_PROCESS_EXE'), test_os_process) ? + } else { + os.system('$vexe -o $test_os_process $test_os_process_source') + } + assert os.exists(test_os_process) +} + +fn test_getpid() { + pid := os.getpid() + eprintln('current pid: $pid') + assert pid != 0 +} + +fn test_run() { + mut p := os.new_process(test_os_process) + p.set_args(['-timeout_ms', '150', '-period_ms', '50']) + p.run() + assert p.status == .running + assert p.pid > 0 + assert p.pid != os.getpid() + mut i := 0 + for { + if !p.is_alive() { + break + } + $if trace_process_output ? { + os.system('ps -opid= -oppid= -ouser= -onice= -of= -ovsz= -orss= -otime= -oargs= -p $p.pid') + } + time.sleep(50 * time.millisecond) + i++ + } + p.wait() + assert p.code == 0 + assert p.status == .exited + // + eprintln('polling iterations: $i') + assert i < 50 + p.close() +} + +fn test_wait() { + mut p := os.new_process(test_os_process) + assert p.status != .exited + p.wait() + assert p.status == .exited + assert p.code == 0 + assert p.pid != os.getpid() + p.close() +} + +fn test_slurping_output() { + mut p := os.new_process(test_os_process) + p.set_args(['-timeout_ms', '500', '-period_ms', '50']) + p.set_redirect_stdio() + assert p.status != .exited + p.wait() + assert p.status == .exited + assert p.code == 0 + output := p.stdout_slurp().trim_space() + errors := p.stderr_slurp().trim_space() + p.close() + $if trace_process_output ? { + eprintln('---------------------------') + eprintln('p output: "$output"') + eprintln('p errors: "$errors"') + eprintln('---------------------------') + } + // dump(output) + assert output.contains('stdout, 1') + assert output.contains('stdout, 2') + assert output.contains('stdout, 3') + assert output.contains('stdout, 4') + // + // dump(errors) + assert errors.contains('stderr, 1') + assert errors.contains('stderr, 2') + assert errors.contains('stderr, 3') + assert errors.contains('stderr, 4') +} diff --git a/v_windows/v/old/vlib/os/process_windows.c.v b/v_windows/v/old/vlib/os/process_windows.c.v new file mode 100644 index 0000000..bcdf971 --- /dev/null +++ b/v_windows/v/old/vlib/os/process_windows.c.v @@ -0,0 +1,243 @@ +module os + +import strings + +fn C.GenerateConsoleCtrlEvent(event u32, pgid u32) bool +fn C.GetModuleHandleA(name &char) HMODULE +fn C.GetProcAddress(handle voidptr, procname &byte) voidptr +fn C.TerminateProcess(process HANDLE, exit_code u32) bool + +type FN_NTSuspendResume = fn (voidptr) + +fn ntdll_fn(name &char) FN_NTSuspendResume { + ntdll := C.GetModuleHandleA(c'NTDLL') + if ntdll == 0 { + return FN_NTSuspendResume(0) + } + the_fn := FN_NTSuspendResume(C.GetProcAddress(ntdll, name)) + return the_fn +} + +fn failed_cfn_report_error(ok bool, label string) { + if ok { + return + } + error_num := int(C.GetLastError()) + error_msg := get_error_msg(error_num) + eprintln('failed $label: $error_msg') + exit(1) +} + +type PU32 = &u32 + +// TODO: the PU32 alias is used to compensate for the wrong number of &/* +// that V does when doing: `h := &&u32(p)`, which should have casted +// p to a double pointer. +fn close_valid_handle(p voidptr) { + h := &PU32(p) + if *h != &u32(0) { + C.CloseHandle(*h) + unsafe { + *h = &u32(0) + } + } +} + +pub struct WProcess { +pub mut: + proc_info ProcessInformation + command_line [65536]byte + child_stdin &u32 + // + child_stdout_read &u32 + child_stdout_write &u32 + // + child_stderr_read &u32 + child_stderr_write &u32 +} + +fn (mut p Process) win_spawn_process() int { + mut wdata := &WProcess{ + child_stdin: 0 + child_stdout_read: 0 + child_stdout_write: 0 + child_stderr_read: 0 + child_stderr_write: 0 + } + p.wdata = voidptr(wdata) + mut start_info := StartupInfo{ + lp_reserved2: 0 + lp_reserved: 0 + lp_desktop: 0 + lp_title: 0 + cb: sizeof(C.PROCESS_INFORMATION) + } + if p.use_stdio_ctl { + mut sa := SecurityAttributes{} + sa.n_length = sizeof(C.SECURITY_ATTRIBUTES) + sa.b_inherit_handle = true + create_pipe_ok1 := C.CreatePipe(voidptr(&wdata.child_stdout_read), voidptr(&wdata.child_stdout_write), + voidptr(&sa), 0) + failed_cfn_report_error(create_pipe_ok1, 'CreatePipe stdout') + set_handle_info_ok1 := C.SetHandleInformation(wdata.child_stdout_read, C.HANDLE_FLAG_INHERIT, + 0) + failed_cfn_report_error(set_handle_info_ok1, 'SetHandleInformation') + create_pipe_ok2 := C.CreatePipe(voidptr(&wdata.child_stderr_read), voidptr(&wdata.child_stderr_write), + voidptr(&sa), 0) + failed_cfn_report_error(create_pipe_ok2, 'CreatePipe stderr') + set_handle_info_ok2 := C.SetHandleInformation(wdata.child_stderr_read, C.HANDLE_FLAG_INHERIT, + 0) + failed_cfn_report_error(set_handle_info_ok2, 'SetHandleInformation stderr') + start_info.h_std_input = wdata.child_stdin + start_info.h_std_output = wdata.child_stdout_write + start_info.h_std_error = wdata.child_stderr_write + start_info.dw_flags = u32(C.STARTF_USESTDHANDLES) + } + cmd := '$p.filename ' + p.args.join(' ') + C.ExpandEnvironmentStringsW(cmd.to_wide(), voidptr(&wdata.command_line[0]), 32768) + + mut creation_flags := int(C.NORMAL_PRIORITY_CLASS) + if p.use_pgroup { + creation_flags |= C.CREATE_NEW_PROCESS_GROUP + } + create_process_ok := C.CreateProcessW(0, &wdata.command_line[0], 0, 0, C.TRUE, creation_flags, + 0, 0, voidptr(&start_info), voidptr(&wdata.proc_info)) + failed_cfn_report_error(create_process_ok, 'CreateProcess') + if p.use_stdio_ctl { + close_valid_handle(&wdata.child_stdout_write) + close_valid_handle(&wdata.child_stderr_write) + } + p.pid = int(wdata.proc_info.dw_process_id) + return p.pid +} + +fn (mut p Process) win_stop_process() { + the_fn := ntdll_fn(c'NtSuspendProcess') + if voidptr(the_fn) == 0 { + return + } + wdata := &WProcess(p.wdata) + the_fn(wdata.proc_info.h_process) +} + +fn (mut p Process) win_resume_process() { + the_fn := ntdll_fn(c'NtResumeProcess') + if voidptr(the_fn) == 0 { + return + } + wdata := &WProcess(p.wdata) + the_fn(wdata.proc_info.h_process) +} + +fn (mut p Process) win_kill_process() { + wdata := &WProcess(p.wdata) + C.TerminateProcess(wdata.proc_info.h_process, 3) +} + +fn (mut p Process) win_kill_pgroup() { + wdata := &WProcess(p.wdata) + C.GenerateConsoleCtrlEvent(C.CTRL_BREAK_EVENT, wdata.proc_info.dw_process_id) + C.Sleep(20) + C.TerminateProcess(wdata.proc_info.h_process, 3) +} + +fn (mut p Process) win_wait() { + exit_code := u32(1) + mut wdata := &WProcess(p.wdata) + if p.wdata != 0 { + C.WaitForSingleObject(wdata.proc_info.h_process, C.INFINITE) + C.GetExitCodeProcess(wdata.proc_info.h_process, voidptr(&exit_code)) + close_valid_handle(&wdata.child_stdin) + close_valid_handle(&wdata.child_stdout_write) + close_valid_handle(&wdata.child_stderr_write) + close_valid_handle(&wdata.proc_info.h_process) + close_valid_handle(&wdata.proc_info.h_thread) + } + p.status = .exited + p.code = int(exit_code) +} + +fn (mut p Process) win_is_alive() bool { + exit_code := u32(0) + wdata := &WProcess(p.wdata) + C.GetExitCodeProcess(wdata.proc_info.h_process, voidptr(&exit_code)) + if exit_code == C.STILL_ACTIVE { + return true + } + return false +} + +/////////////// + +fn (mut p Process) win_write_string(idx int, s string) { + panic('Process.write_string $idx is not implemented yet') +} + +fn (mut p Process) win_read_string(idx int, maxbytes int) (string, int) { + panic('WProcess.read_string $idx is not implemented yet') + return '', 0 +} + +fn (mut p Process) win_slurp(idx int) string { + mut wdata := &WProcess(p.wdata) + if wdata == 0 { + return '' + } + mut rhandle := &u32(0) + if idx == 1 { + rhandle = wdata.child_stdout_read + } + if idx == 2 { + rhandle = wdata.child_stderr_read + } + if rhandle == 0 { + return '' + } + mut bytes_read := u32(0) + buf := [4096]byte{} + mut read_data := strings.new_builder(1024) + for { + mut result := false + unsafe { + result = C.ReadFile(rhandle, &buf[0], 1000, voidptr(&bytes_read), 0) + read_data.write_ptr(&buf[0], int(bytes_read)) + } + if result == false || int(bytes_read) == 0 { + break + } + } + soutput := read_data.str() + unsafe { read_data.free() } + if idx == 1 { + close_valid_handle(&wdata.child_stdout_read) + } + if idx == 2 { + close_valid_handle(&wdata.child_stderr_read) + } + return soutput +} + +// +// these are here to make v_win.c/v.c generation work in all cases: +fn (mut p Process) unix_spawn_process() int { + return 0 +} + +fn (mut p Process) unix_stop_process() { +} + +fn (mut p Process) unix_resume_process() { +} + +fn (mut p Process) unix_kill_process() { +} + +fn (mut p Process) unix_kill_pgroup() { +} + +fn (mut p Process) unix_wait() { +} + +fn (mut p Process) unix_is_alive() bool { + return false +} diff --git a/v_windows/v/old/vlib/os/signal.c.v b/v_windows/v/old/vlib/os/signal.c.v new file mode 100644 index 0000000..5dda3bd --- /dev/null +++ b/v_windows/v/old/vlib/os/signal.c.v @@ -0,0 +1,58 @@ +module os + +#include + +// os.Signal - enumerate possible POSIX signals and +// their integer codes. +// NB: the integer codes are given here explicitly, +// to make it easier to lookup, without needing to +// consult man pages / signal.h . + +pub enum Signal { + hup = 1 + int = 2 + quit = 3 + ill = 4 + trap = 5 + abrt = 6 + bus = 7 + fpe = 8 + kill = 9 + usr1 = 10 + segv = 11 + usr2 = 12 + pipe = 13 + alrm = 14 + term = 15 + stkflt = 16 + chld = 17 + cont = 18 + stop = 19 + tstp = 20 + ttin = 21 + ttou = 22 + urg = 23 + xcpu = 24 + xfsz = 25 + vtalrm = 26 + prof = 27 + winch = 28 + poll = 29 + pwr = 30 + sys = 31 +} + +type SignalHandler = fn (Signal) + +fn C.signal(signal int, handlercb SignalHandler) voidptr + +// signal will assign `handler` callback to be called when `signum` signal is received. +pub fn signal_opt(signum Signal, handler SignalHandler) ?SignalHandler { + C.errno = 0 + prev_handler := C.signal(int(signum), handler) + if prev_handler == C.SIG_ERR { + // errno isn't correctly set on Windows, but EINVAL is this only possible value it can take anyway + return error_with_code(posix_get_error_msg(C.EINVAL), C.EINVAL) + } + return SignalHandler(prev_handler) +} diff --git a/v_windows/v/old/vlib/os/signal_test.v b/v_windows/v/old/vlib/os/signal_test.v new file mode 100644 index 0000000..1c56540 --- /dev/null +++ b/v_windows/v/old/vlib/os/signal_test.v @@ -0,0 +1,35 @@ +import os + +fn former_handler(signal os.Signal) { + println('former_handler') + exit(0) +} + +fn default_handler(signal os.Signal) { + println('default_handler') + exit(0) +} + +fn test_signal_opt() { + os.signal_opt(.int, default_handler) or { assert false } +} + +fn test_signal_opt_invalid_argument() { + // Can't register a signal on SIGKILL + if _ := os.signal_opt(.kill, default_handler) { + assert false + } + os.signal_opt(.kill, default_handler) or { + assert err.msg == 'Invalid argument' + assert err.code == 22 + } +} + +fn test_signal_opt_return_former_handler() { + func1 := os.signal_opt(.term, former_handler) or { panic('unexpected error') } + assert isnil(func1) + func2 := os.signal_opt(.term, default_handler) or { panic('unexpected error') } + assert !isnil(func2) + // this should work, but makes the CI fail because of a bug in clang -fsanitize=memory + // assert func2 == former_handler +} diff --git a/v_windows/v/old/vlib/os2/keep_vfmt_happy.v b/v_windows/v/old/vlib/os2/keep_vfmt_happy.v new file mode 100644 index 0000000..4b719f9 --- /dev/null +++ b/v_windows/v/old/vlib/os2/keep_vfmt_happy.v @@ -0,0 +1,2 @@ +// this keeps vfmt happy +module os2 diff --git a/v_windows/v/old/vlib/os2/os2_darwin.c.v b/v_windows/v/old/vlib/os2/os2_darwin.c.v new file mode 100644 index 0000000..b04ee11 --- /dev/null +++ b/v_windows/v/old/vlib/os2/os2_darwin.c.v @@ -0,0 +1,36 @@ +module os2 + +#include + +struct File { + fd int +} + +fn C.perror(&char) + +fn C.open(&byte, int, int) int + +fn C.write(voidptr, &byte, int) int + +fn C.close(int) int + +pub fn create(path string) ?File { + fd := C.open(path.str, C.O_CREAT | C.O_TRUNC | C.O_WRONLY, o644) // 511 + if fd == -1 { + return error('failed to create "$path":') + // os.print_c_errno() + } + return File{fd} +} + +pub fn (f File) writeln(s string) { + ss := s + '\n' + ret := C.write(f.fd, ss.str, s.len + 1) + if ret == -1 { + C.perror('failed to write') + } +} + +pub fn (f File) close() { + C.close(f.fd) +} diff --git a/v_windows/v/old/vlib/os2/os2_test.v b/v_windows/v/old/vlib/os2/os2_test.v new file mode 100644 index 0000000..997e341 --- /dev/null +++ b/v_windows/v/old/vlib/os2/os2_test.v @@ -0,0 +1,11 @@ +// import os2 + +fn test_open() { + /* + $if macos { + f := os2.create('os2.test') + f.writeln('hello world!') + f.close() + } + */ +} diff --git a/v_windows/v/old/vlib/os_js/environment.js.v b/v_windows/v/old/vlib/os_js/environment.js.v new file mode 100644 index 0000000..0f69f09 --- /dev/null +++ b/v_windows/v/old/vlib/os_js/environment.js.v @@ -0,0 +1,31 @@ +module os_js + +// setenv sets the value of an environment variable with `name` to `value`. +pub fn setenv(key string, val string, overwrite bool) { + #if ($process.env[key] && !(overwrite.valueOf())) return; + #$process.env[key] = val + ''; +} + +// `getenv` returns the value of the environment variable named by the key. +pub fn getenv(key string) string { + mut res := '' + #if ($process.env[key]) res = new builtin.string($process.env[key]) + + return res +} + +// unsetenv clears an environment variable with `name`. +pub fn unsetenv(name string) int { + #$process.env[name] = "" + + return 1 +} + +pub fn environ() map[string]string { + mut res := map[string]string{} + #for (const key in $process.env) { + #res.map.set(key,$process.env[key]) + #} + + return res +} diff --git a/v_windows/v/old/vlib/os_js/file.js.v b/v_windows/v/old/vlib/os_js/file.js.v new file mode 100644 index 0000000..a7541c5 --- /dev/null +++ b/v_windows/v/old/vlib/os_js/file.js.v @@ -0,0 +1,130 @@ +module os_js + +pub struct File { +pub: + fd int +pub mut: + is_opened bool +} + +#const $buffer = require('buffer'); + +// todo(playX): __as_cast is broken here +/* +pub struct ErrFileNotOpened { + msg string = 'os: file not opened' + code int +} +pub struct ErrSizeOfTypeIs0 { + msg string = 'os: size of type is 0' + code int +} +fn error_file_not_opened() IError { + return IError(&ErrFileNotOpened{}) +} +fn error_size_of_type_0() IError { + return IError(&ErrSizeOfTypeIs0{}) +} +*/ +pub fn open_file(path string, mode string, options ...int) ?File { + mut res := File{} + $if js_node { + #if (!options) { options = new array([]); } + #let permissions = 0o666 + #if (options.arr.length > 0) { permissions = options.arr[0]; } + #try { + #res.fd = new int($fs.openSync(''+path,''+mode,permissions)) + #} catch (e) { + #return builtin.error('' + e); + #} + + res.is_opened = true + } $else { + error('cannot open file on non NodeJS runtime') + } + return res +} + +// open tries to open a file for reading and returns back a read-only `File` object. +pub fn open(path string) ?File { + f := open_file(path, 'r') ? + return f +} + +pub fn create(path string) ?File { + f := open_file(path, 'w') ? + return f +} + +pub fn stdin() File { + return File{ + fd: 0 + is_opened: true + } +} + +pub fn stdout() File { + return File{ + fd: 1 + is_opened: true + } +} + +pub fn stderr() File { + return File{ + fd: 2 + is_opened: true + } +} + +pub fn (f &File) read(mut buf []byte) ?int { + if buf.len == 0 { + return 0 + } + mut nbytes := 0 + #try { + #let buffer = $fs.readFileSync(f.fd.valueOf()); + # + #for (const val of buffer.values()) { buf.arr[nbytes++] = val; } + #} + #catch (e) { return builtin.error('' + e); } + + return nbytes +} + +pub fn (mut f File) write(buf []byte) ?int { + if !f.is_opened { + return error('file is not opened') + } + mut nbytes := 0 + #const b = $buffer.Buffer.from(buf.arr.map((x) => x.valueOf())) + #try { $fs.writeSync(f.fd.valueOf(),b,0,buf.len.valueOf(),0); } catch (e) { return builtin.error('' + e); } + + return nbytes +} + +// writeln writes the string `s` into the file, and appends a \n character. +// It returns how many bytes were written, including the \n character. +pub fn (mut f File) writeln(s string) ?int { + mut nbytes := f.write(s.bytes()) ? + nbytes += f.write('\n'.bytes()) ? + return nbytes +} + +pub fn (mut f File) write_to(pos u64, buf []byte) ?int { + if !f.is_opened { + return error('file is not opened') + } + mut nbytes := 0 + #const b = $buffer.Buffer.from(buf.arr.map((x) => x.valueOf())) + #try { $fs.writeSync(f.fd.valueOf(),b,0,buf.len.valueOf(),pos.valueOf()); } catch (e) { return builtin.error('' + e); } + + return nbytes +} + +// write_string writes the string `s` into the file +// It returns how many bytes were actually written. +pub fn (mut f File) write_string(s string) ?int { + nbytes := f.write(s.bytes()) ? + return nbytes +} diff --git a/v_windows/v/old/vlib/os_js/os.js.v b/v_windows/v/old/vlib/os_js/os.js.v new file mode 100644 index 0000000..2fb088a --- /dev/null +++ b/v_windows/v/old/vlib/os_js/os.js.v @@ -0,0 +1,95 @@ +module os_js + +#const $fs = require('fs'); +#const $path = require('path'); + +pub const ( + args = []string{} +) + +$if js_node { + #$process.argv.forEach(function(val,index) { args.arr[index] = new string(val); }) +} + +// real_path returns the full absolute path for fpath, with all relative ../../, symlinks and so on resolved. +// See http://pubs.opengroup.org/onlinepubs/9699919799/functions/realpath.html +// Also https://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html +// and https://insanecoding.blogspot.com/2007/11/implementing-realpath-in-c.html +// NB: this particular rabbit hole is *deep* ... +pub fn real_path(fpath string) string { + $if js_node { + mut res := '' + #res = new string( $fs.realpathSync(fpath)) + + return res + } $else { + return fpath + } +} + +// flush will flush the stdout buffer. +pub fn flush() { + $if js_node { + #$process.stdout.write('') + } +} + +// chmod change file access attributes of `path` to `mode`. +// Octals like `0o600` can be used. +pub fn chmod(path string, mode int) { + $if js_node { + #$fs.chmodSync(''+path,mode.valueOf()) + } +} + +// chown changes the owner and group attributes of `path` to `owner` and `group`. +// Octals like `0o600` can be used. +pub fn chown(path string, owner int, group int) { + $if js_node { + #$fs.chownSync(''+path,owner.valueOf(),group.valueOf()) + } +} + +pub fn temp_dir() string { + mut res := '' + $if js_node { + #res = new builtin.string($os.tmpdir()) + } + return res +} + +pub fn home_dir() string { + mut res := '' + $if js_node { + #res = new builtin.string($os.homedir()) + } + return res +} + +// join_path returns a path as string from input string parameter(s). +pub fn join_path(base string, dirs ...string) string { + mut result := []string{} + result << base.trim_right('\\/') + for d in dirs { + result << d + } + mut path_sep := '' + #path_sep = $path.sep; + + res := result.join(path_sep) + return res +} + +pub fn execute(cmd string) Result { + mut exit_code := 0 + mut stdout := '' + #let commands = cmd.str.split(' '); + #let output = $child_process.spawnSync(commands[0],commands.slice(1,commands.length)); + #exit_code = new builtin.int(output.status) + #stdout = new builtin.string(output.stdout + '') + + return Result{ + exit_code: exit_code + output: stdout + } +} diff --git a/v_windows/v/old/vlib/os_js/os.v b/v_windows/v/old/vlib/os_js/os.v new file mode 100644 index 0000000..8b4e160 --- /dev/null +++ b/v_windows/v/old/vlib/os_js/os.v @@ -0,0 +1,13 @@ +module os_js + +pub const ( + // todo(playX): NodeJS does not seem to have any path limit? + max_path_len = 4096 +) + +pub struct Result { +pub: + exit_code int + output string + // stderr string // TODO +} diff --git a/v_windows/v/old/vlib/os_js/process.js.v b/v_windows/v/old/vlib/os_js/process.js.v new file mode 100644 index 0000000..e5574d8 --- /dev/null +++ b/v_windows/v/old/vlib/os_js/process.js.v @@ -0,0 +1,173 @@ +module os_js + +#const $child_process = require('child_process') + +// ProcessState.not_started - the process has not yet started +// ProcessState.running - the process is currently running +// ProcessState.stopped - the process was running, but was stopped temporarily +// ProcessState.exited - the process has finished/exited +// ProcessState.aborted - the process was terminated by a signal +// ProcessState.closed - the process resources like opened file descriptors were freed/discarded, final state. +pub enum ProcessState { + not_started + running + stopped + exited + aborted + closed +} + +// todo(playX): fix reference member access in JS backend +[heap] +pub struct Process { +pub: + filename string +pub mut: + pid voidptr + code int = -1 + status ProcessState = .not_started + // the current status of the process + err string // if the process fails, contains the reason why + args []string // the arguments that the command takes + env_is_custom bool // true, when the environment was customized with .set_environment + env []string // the environment with which the process was started (list of 'var=val') + use_stdio_ctl bool // when true, then you can use p.stdin_write(), p.stdout_slurp() and p.stderr_slurp() + use_pgroup bool // when true, the process will create a new process group, enabling .signal_pgkill() + stdio_fd [3]int // the stdio file descriptors for the child process, used only by the nix implementation +} + +// new_process - create a new process descriptor +// NB: new does NOT start the new process. +// That is done because you may want to customize it first, +// by calling different set_ methods on it. +// In order to start it, call p.run() or p.wait() +pub fn new_process(filename string) &Process { + return &Process{ + filename: filename + stdio_fd: [-1, -1, -1]! + } +} + +// set_args - set the arguments for the new process +pub fn (mut p Process) set_args(pargs []string) { + if p.status != .not_started { + return + } + p.args = pargs + return +} + +// set_environment - set a custom environment variable mapping for the new process +pub fn (mut p Process) set_environment(envs map[string]string) { + if p.status != .not_started { + return + } + p.env_is_custom = true + p.env = []string{} + for k, v in envs { + p.env << '$k=$v' + } + return +} + +fn (mut p Process) spawn_internal() { + #p.val.pid = $child_process.spawn( + #p.val.filename+'', + #p.val.args.arr.map((x) => x.valueOf() + ''), + #{ + #env: (p.val.env_is_custom ? p.val.env : $process.env), + #}) + #p.val.pid.on('error', function (err) { builtin.panic('Failed to start subprocess') }) + + p.status = .running + // todo(playX): stderr,stdin + if p.use_stdio_ctl { + #p.val.pid.stdout.pipe(process.stdout) + #p.val.pid.stdin.pipe(process.stdin) + #p.val.pid.stderr.pipe(process.stderr) + } +} + +pub fn (mut p Process) run() { + if p.status != .not_started { + return + } + p.spawn_internal() + return +} + +pub fn (mut p Process) signal_kill() { + if p.status !in [.running, .stopped] { + return + } + #p.val.pid.kill('SIGKILL'); + + p.status = .aborted +} + +pub fn (mut p Process) signal_stop() { + if p.status !in [.running, .stopped] { + return + } + #p.val.pid.kill('SIGSTOP'); + + p.status = .aborted +} + +pub fn (mut p Process) signal_continue() { + if p.status != .stopped { + return + } + #p.val.pid.kill('SIGCONT'); + + p.status = .running + return +} + +pub fn (mut p Process) wait() { + if p.status == .not_started { + p.spawn_internal() + } + if p.status !in [.running, .stopped] { + return + } + + p.wait_internal() + return +} + +fn (mut p Process) wait_internal() { + #p.val.pid.on('exit', function (code) { console.log(code) }) +} + +pub fn (mut p Process) set_redirect_stdio() { + p.use_stdio_ctl = true + return +} + +pub fn (mut p Process) stdin_write(s string) { + p.check_redirection_call('stdin_write') + #p.val.pid.stdin.write(s) +} + +// todo(playX): probably does not work + +// will read from stdout pipe, will only return when EOF (end of file) or data +// means this will block unless there is data +pub fn (mut p Process) stdout_slurp() string { + p.check_redirection_call('stdout_slurp') + mut res := '' + #p.val.pid.stdout.on('data', function (data) { res = new builtin.string(data) }) + + return res +} + +// _check_redirection_call - should be called just by stdxxx methods +fn (mut p Process) check_redirection_call(fn_name string) { + if !p.use_stdio_ctl { + panic('Call p.set_redirect_stdio() before calling p.$fn_name') + } + if p.status == .not_started { + panic('Call p.${fn_name}() after you have called p.run()') + } +} diff --git a/v_windows/v/old/vlib/pg/README.md b/v_windows/v/old/vlib/pg/README.md new file mode 100644 index 0000000..bcdae98 --- /dev/null +++ b/v_windows/v/old/vlib/pg/README.md @@ -0,0 +1,25 @@ +Before you can use this module, you must first have PostgreSQL installed on +your system. To do this, find your OS and perform the actions listed. + +**NOTE**: These instructions are meant only as a convenience. If your OS is not +listed or you need extra help, [go here](https://www.postgresql.org/download/). + +### Fedora 31 +``` +sudo dnf install postgresql-server postgresql-contrib +sudo systemctl enable postgresql # to autostart on startup +sudo systemctl start postgresql +``` + +### Debian 10/11 +``` +sudo apt-get install postgresql postgresql-client +sudo systemctl enable postgresql # to autostart on startup +sudo systemctl start postgresql +``` + +### MacOSX (Homebrew) +``` +brew install postgresql +brew services start postgresql +``` diff --git a/v_windows/v/old/vlib/pg/oid.v b/v_windows/v/old/vlib/pg/oid.v new file mode 100644 index 0000000..2f8004d --- /dev/null +++ b/v_windows/v/old/vlib/pg/oid.v @@ -0,0 +1,171 @@ +module pg + +pub enum Oid { + t_bool = 16 + t_bytea = 17 + t_char = 18 + t_name = 19 + t_int8 = 20 + t_int2 = 21 + t_int2vector = 22 + t_int4 = 23 + t_regproc = 24 + t_text = 25 + t_oid = 26 + t_tid = 27 + t_xid = 28 + t_cid = 29 + t_vector = 30 + t_pg_ddl_command = 32 + t_pg_type = 71 + t_pg_attribute = 75 + t_pg_proc = 81 + t_pg_class = 83 + t_json = 114 + t_xml = 142 + t__xml = 143 + t_pg_node_tree = 194 + t__json = 199 + t_smgr = 210 + t_index_am_handler = 325 + t_point = 600 + t_lseg = 601 + t_path = 602 + t_box = 603 + t_polygon = 604 + t_line = 628 + t__line = 629 + t_cidr = 650 + t__cidr = 651 + t_float4 = 700 + t_float8 = 701 + t_abstime = 702 + t_reltime = 703 + t_tinterval = 704 + t_unknown = 705 + t_circle = 718 + t__circle = 719 + t_money = 790 + t__money = 791 + t_macaddr = 829 + t_inet = 869 + t__bool = 1000 + t__bytea = 1001 + t__char = 1002 + t__name = 1003 + t__int2 = 1005 + t__int2vector = 1006 + t__int4 = 1007 + t__regproc = 1008 + t__text = 1009 + t__tid = 1010 + t__xid = 1011 + t__cid = 1012 + t__vector = 1013 + t__bpchar = 1014 + t__varchar = 1015 + t__int8 = 1016 + t__point = 1017 + t__lseg = 1018 + t__path = 1019 + t__box = 1020 + t__float4 = 1021 + t__float8 = 1022 + t__abstime = 1023 + t__reltime = 1024 + t__tinterval = 1025 + t__polygon = 1027 + t__ = 1028 + t_aclitem = 1033 + t__aclitem = 1034 + t__macaddr = 1040 + t__inet = 1041 + t_bpchar = 1042 + t_varchar = 1043 + t_date = 1082 + t_time = 1083 + t_timestamp = 1114 + t__timestamp = 1115 + t__date = 1182 + t__time = 1183 + t_timestamptz = 1184 + t__timestamptz = 1185 + t_interval = 1186 + t__interval = 1187 + t__numeric = 1231 + t_pg_database = 1248 + t__cstring = 1263 + t_timetz = 1266 + t__timetz = 1270 + t_bit = 1560 + t__bit = 1561 + t_varbit = 1562 + t__varbit = 1563 + t_numeric = 1700 + t_refcursor = 1790 + t__refcursor = 2201 + t_regprocedure = 2202 + t_regoper = 2203 + t_regoperator = 2204 + t_regclass = 2205 + t_regtype = 2206 + t__regprocedure = 2207 + t__regoper = 2208 + t__regoperator = 2209 + t__regclass = 2210 + t__regtype = 2211 + t_record = 2249 + t_cstring = 2275 + t_any = 2276 + t_anyarray = 2277 + t_v = 2278 + t_trigger = 2279 + t_language_handler = 2280 + t_internal = 2281 + t_opaque = 2282 + t_anyelement = 2283 + t__record = 2287 + t_anynonarray = 2776 + t_pg_authid = 2842 + t_pg_auth_members = 2843 + t__txid_snapshot = 2949 + t_uuid = 2950 + t__uuid = 2951 + t_txid_snapshot = 2970 + t_fdw_handler = 3115 + t_pg_lsn = 3220 + t__pg_lsn = 3221 + t_tsm_handler = 3310 + t_anyenum = 3500 + t_tsvector = 3614 + t_tsquery = 3615 + t_gtsvector = 3642 + t__tsvector = 3643 + t__gtsvector = 3644 + t__tsquery = 3645 + t_regconfig = 3734 + t__regconfig = 3735 + t_regdictionary = 3769 + t__regdictionary = 3770 + t_jsonb = 3802 + t__jsonb = 3807 + t_anyrange = 3831 + t_event_trigger = 3838 + t_int4range = 3904 + t__int4range = 3905 + t_numrange = 3906 + t__numrange = 3907 + t_tsrange = 3908 + t__tsrange = 3909 + t_tstzrange = 3910 + t__tstzrange = 3911 + t_daterange = 3912 + t__daterange = 3913 + t_int8range = 3926 + t__int8range = 3927 + t_pg_shseclabel = 4066 + t_regnamespace = 4089 + t__regnamespace = 4090 + t_regrole = 4096 + t__regrole = 4097 +} diff --git a/v_windows/v/old/vlib/pg/orm.v b/v_windows/v/old/vlib/pg/orm.v new file mode 100644 index 0000000..f40e643 --- /dev/null +++ b/v_windows/v/old/vlib/pg/orm.v @@ -0,0 +1,272 @@ +module pg + +import orm +import time +import net.conv + +// sql expr + +pub fn (db DB) @select(config orm.SelectConfig, data orm.QueryData, where orm.QueryData) ?[][]orm.Primitive { + query := orm.orm_select_gen(config, '"', true, '$', 1, where) + mut ret := [][]orm.Primitive{} + + res := pg_stmt_worker(db, query, orm.QueryData{}, where) ? + + for row in res { + mut row_data := []orm.Primitive{} + for i, val in row.vals { + field := str_to_primitive(val, config.types[i]) ? + row_data << field + } + ret << row_data + } + + return ret +} + +// sql stmt + +pub fn (db DB) insert(table string, data orm.QueryData) ? { + query := orm.orm_stmt_gen(table, '"', .insert, true, '$', 1, data, orm.QueryData{}) + pg_stmt_worker(db, query, data, orm.QueryData{}) ? +} + +pub fn (db DB) update(table string, data orm.QueryData, where orm.QueryData) ? { + query := orm.orm_stmt_gen(table, '"', .update, true, '$', 1, data, where) + pg_stmt_worker(db, query, data, where) ? +} + +pub fn (db DB) delete(table string, where orm.QueryData) ? { + query := orm.orm_stmt_gen(table, '"', .delete, true, '$', 1, orm.QueryData{}, where) + pg_stmt_worker(db, query, orm.QueryData{}, where) ? +} + +pub fn (db DB) last_id() orm.Primitive { + query := 'SELECT LASTVAL();' + id := db.q_int(query) or { 0 } + return orm.Primitive(id) +} + +// table + +pub fn (db DB) create(table string, fields []orm.TableField) ? { + query := orm.orm_table_gen(table, '"', true, 0, fields, pg_type_from_v, false) or { return err } + pg_stmt_worker(db, query, orm.QueryData{}, orm.QueryData{}) ? +} + +pub fn (db DB) drop(table string) ? { + query := 'DROP TABLE "$table";' + pg_stmt_worker(db, query, orm.QueryData{}, orm.QueryData{}) ? +} + +// utils + +fn pg_stmt_worker(db DB, query string, data orm.QueryData, where orm.QueryData) ?[]Row { + mut param_types := []u32{} + mut param_vals := []&char{} + mut param_lens := []int{} + mut param_formats := []int{} + + pg_stmt_binder(mut param_types, mut param_vals, mut param_lens, mut param_formats, + data) + pg_stmt_binder(mut param_types, mut param_vals, mut param_lens, mut param_formats, + where) + + res := C.PQexecParams(db.conn, query.str, param_vals.len, param_types.data, param_vals.data, + param_lens.data, param_formats.data, 0) + return db.handle_error_or_result(res, 'orm_stmt_worker') +} + +fn pg_stmt_binder(mut types []u32, mut vals []&char, mut lens []int, mut formats []int, d orm.QueryData) { + for data in d.data { + pg_stmt_match(mut types, mut vals, mut lens, mut formats, data) + } +} + +fn pg_stmt_match(mut types []u32, mut vals []&char, mut lens []int, mut formats []int, data orm.Primitive) { + d := data + match data { + bool { + types << u32(Oid.t_bool) + vals << &char(&(d as bool)) + lens << int(sizeof(bool)) + formats << 1 + } + byte { + types << u32(Oid.t_char) + vals << &char(&(d as byte)) + lens << int(sizeof(byte)) + formats << 1 + } + u16 { + types << u32(Oid.t_int2) + num := conv.htn16(&data) + vals << &char(&num) + lens << int(sizeof(u16)) + formats << 1 + } + u32 { + types << u32(Oid.t_int4) + num := conv.htn32(&data) + vals << &char(&num) + lens << int(sizeof(u32)) + formats << 1 + } + u64 { + types << u32(Oid.t_int8) + num := conv.htn64(&data) + vals << &char(&num) + lens << int(sizeof(u64)) + formats << 1 + } + i8 { + types << u32(Oid.t_char) + vals << &char(&(d as i8)) + lens << int(sizeof(i8)) + formats << 1 + } + i16 { + types << u32(Oid.t_int2) + num := conv.htn16(unsafe { &u16(&data) }) + vals << &char(&num) + lens << int(sizeof(i16)) + formats << 1 + } + int { + types << u32(Oid.t_int4) + num := conv.htn32(unsafe { &u32(&data) }) + vals << &char(&num) + lens << int(sizeof(int)) + formats << 1 + } + i64 { + types << u32(Oid.t_int8) + num := conv.htn64(unsafe { &u64(&data) }) + vals << &char(&num) + lens << int(sizeof(i64)) + formats << 1 + } + f32 { + types << u32(Oid.t_float4) + vals << &char(unsafe { &f32(&(d as f32)) }) + lens << int(sizeof(f32)) + formats << 1 + } + f64 { + types << u32(Oid.t_float8) + vals << &char(unsafe { &f64(&(d as f64)) }) + lens << int(sizeof(f64)) + formats << 1 + } + string { + types << u32(Oid.t_text) + vals << data.str + lens << data.len + formats << 0 + } + time.Time { + types << u32(Oid.t_int4) + vals << &char(&int(data.unix)) + lens << int(sizeof(u32)) + formats << 1 + } + orm.InfixType { + pg_stmt_match(mut types, mut vals, mut lens, mut formats, data.right) + } + } +} + +fn pg_type_from_v(typ int) ?string { + str := match typ { + 6, 10 { + 'SMALLINT' + } + 7, 11 { + 'INT' + } + 8, 12 { + 'BIGINT' + } + 13 { + 'REAL' + } + 14 { + 'DOUBLE PRECISION' + } + orm.string { + 'TEXT' + } + -1 { + 'SERIAL' + } + else { + '' + } + } + if str == '' { + return error('Unknown type $typ') + } + return str +} + +fn str_to_primitive(str string, typ int) ?orm.Primitive { + match typ { + // bool + 16 { + return orm.Primitive(str.i8() == 1) + } + // i8 + 5 { + return orm.Primitive(str.i8()) + } + // i16 + 6 { + return orm.Primitive(str.i16()) + } + // int + 7 { + return orm.Primitive(str.int()) + } + // i64 + 8 { + return orm.Primitive(str.i64()) + } + // byte + 9 { + data := str.i8() + return orm.Primitive(*unsafe { &byte(&data) }) + } + // u16 + 10 { + data := str.i16() + return orm.Primitive(*unsafe { &u16(&data) }) + } + // u32 + 11 { + data := str.int() + return orm.Primitive(*unsafe { &u32(&data) }) + } + // u64 + 12 { + data := str.i64() + return orm.Primitive(*unsafe { &u64(&data) }) + } + // f32 + 13 { + return orm.Primitive(str.f32()) + } + // f64 + 14 { + return orm.Primitive(str.f64()) + } + orm.string { + return orm.Primitive(str) + } + orm.time { + timestamp := str.int() + return orm.Primitive(time.unix(timestamp)) + } + else {} + } + return error('Unknown field type $typ') +} diff --git a/v_windows/v/old/vlib/pg/pg.v b/v_windows/v/old/vlib/pg/pg.v new file mode 100644 index 0000000..9e2d7e7 --- /dev/null +++ b/v_windows/v/old/vlib/pg/pg.v @@ -0,0 +1,277 @@ +module pg + +import io + +#flag -lpq +#flag linux -I/usr/include/postgresql +#flag darwin -I/opt/local/include/postgresql11 +#flag windows -I @VEXEROOT/thirdparty/pg/include +#flag windows -L @VEXEROOT/thirdparty/pg/win64 + +// PostgreSQL Source Code +// https://doxygen.postgresql.org/libpq-fe_8h.html +#include +// for orm +#include + +pub struct DB { +mut: + conn &C.PGconn +} + +pub struct Row { +pub mut: + vals []string +} + +struct C.PGResult { +} + +pub struct Config { +pub: + host string + port int = 5432 + user string + password string + dbname string +} + +fn C.PQconnectdb(a &byte) &C.PGconn + +fn C.PQerrorMessage(voidptr) &byte + +fn C.PQgetvalue(&C.PGResult, int, int) &byte + +fn C.PQstatus(voidptr) int + +fn C.PQresultStatus(voidptr) int + +fn C.PQntuples(&C.PGResult) int + +fn C.PQnfields(&C.PGResult) int + +fn C.PQexec(voidptr, &byte) &C.PGResult + +// Params: +// const Oid *paramTypes +// const char *const *paramValues +// const int *paramLengths +// const int *paramFormats +fn C.PQexecParams(conn voidptr, command &byte, nParams int, paramTypes int, paramValues &byte, paramLengths int, paramFormats int, resultFormat int) &C.PGResult + +fn C.PQputCopyData(conn voidptr, buffer &byte, nbytes int) int + +fn C.PQputCopyEnd(voidptr, &byte) int + +fn C.PQgetCopyData(conn voidptr, buffer &&byte, async int) int + +fn C.PQclear(&C.PGResult) voidptr + +fn C.PQfreemem(voidptr) + +fn C.PQfinish(voidptr) + +// connect makes a new connection to the database server using +// the parameters from the `Config` structure, returning +// a connection error when something goes wrong +pub fn connect(config Config) ?DB { + conninfo := 'host=$config.host port=$config.port user=$config.user dbname=$config.dbname password=$config.password' + conn := C.PQconnectdb(conninfo.str) + if conn == 0 { + return error('libpq memory allocation error') + } + status := C.PQstatus(conn) + if status != C.CONNECTION_OK { + // We force the construction of a new string as the + // error message will be freed by the next `PQfinish` + // call + c_error_msg := unsafe { C.PQerrorMessage(conn).vstring() } + error_msg := '$c_error_msg' + C.PQfinish(conn) + return error('Connection to a PG database failed: $error_msg') + } + return DB{ + conn: conn + } +} + +fn res_to_rows(res voidptr) []Row { + nr_rows := C.PQntuples(res) + nr_cols := C.PQnfields(res) + + mut rows := []Row{} + for i in 0 .. nr_rows { + mut row := Row{} + for j in 0 .. nr_cols { + val := C.PQgetvalue(res, i, j) + sval := unsafe { val.vstring() } + row.vals << sval + } + rows << row + } + + C.PQclear(res) + return rows +} + +// close frees the underlying resource allocated by the database connection +pub fn (db DB) close() { + C.PQfinish(db.conn) +} + +// q_int submit a command to the database server and +// returns an the first field in the first tuple +// converted to an int. If no row is found or on +// command failure, an error is returned +pub fn (db DB) q_int(query string) ?int { + rows := db.exec(query) ? + if rows.len == 0 { + return error('q_int "$query" not found') + } + row := rows[0] + if row.vals.len == 0 { + return 0 + } + val := row.vals[0] + return val.int() +} + +// q_string submit a command to the database server and +// returns an the first field in the first tuple +// as a string. If no row is found or on +// command failure, an error is returned +pub fn (db DB) q_string(query string) ?string { + rows := db.exec(query) ? + if rows.len == 0 { + return error('q_string "$query" not found') + } + row := rows[0] + if row.vals.len == 0 { + return '' + } + val := row.vals[0] + return val +} + +// q_strings submit a command to the database server and +// returns the resulting row set. Alias of `exec` +pub fn (db DB) q_strings(query string) ?[]Row { + return db.exec(query) +} + +// exec submit a command to the database server and wait +// for the result, returning an error on failure and a +// row set on success +pub fn (db DB) exec(query string) ?[]Row { + res := C.PQexec(db.conn, query.str) + return db.handle_error_or_result(res, 'exec') +} + +fn rows_first_or_empty(rows []Row) ?Row { + if rows.len == 0 { + return error('no row') + } + return rows[0] +} + +pub fn (db DB) exec_one(query string) ?Row { + res := C.PQexec(db.conn, query.str) + e := unsafe { C.PQerrorMessage(db.conn).vstring() } + if e != '' { + return error('pg exec error: "$e"') + } + row := rows_first_or_empty(res_to_rows(res)) ? + return row +} + +// exec_param_many executes a query with the provided parameters +pub fn (db DB) exec_param_many(query string, params []string) ?[]Row { + mut param_vals := []&char{len: params.len} + for i in 0 .. params.len { + param_vals[i] = params[i].str + } + + res := C.PQexecParams(db.conn, query.str, params.len, 0, param_vals.data, 0, 0, 0) + return db.handle_error_or_result(res, 'exec_param_many') +} + +pub fn (db DB) exec_param2(query string, param string, param2 string) ?[]Row { + return db.exec_param_many(query, [param, param2]) +} + +pub fn (db DB) exec_param(query string, param string) ?[]Row { + return db.exec_param_many(query, [param]) +} + +fn (db DB) handle_error_or_result(res voidptr, elabel string) ?[]Row { + e := unsafe { C.PQerrorMessage(db.conn).vstring() } + if e != '' { + C.PQclear(res) + return error('pg $elabel error:\n$e') + } + return res_to_rows(res) +} + +// copy_expert execute COPY commands +// https://www.postgresql.org/docs/9.5/libpq-copy.html +pub fn (db DB) copy_expert(query string, file io.ReaderWriter) ?int { + res := C.PQexec(db.conn, query.str) + status := C.PQresultStatus(res) + + defer { + C.PQclear(res) + } + + e := unsafe { C.PQerrorMessage(db.conn).vstring() } + if e != '' { + return error('pg copy error:\n$e') + } + + if status == C.PGRES_COPY_IN { + mut buf := []byte{len: 4 * 1024} + for { + n := file.read(mut buf) or { + msg := 'pg copy error: Failed to read from input' + C.PQputCopyEnd(db.conn, msg.str) + return err + } + if n <= 0 { + break + } + + code := C.PQputCopyData(db.conn, buf.data, n) + if code == -1 { + return error('pg copy error: Failed to send data, code=$code') + } + } + + code := C.PQputCopyEnd(db.conn, 0) + + if code != 1 { + return error('pg copy error: Failed to finish copy command, code: $code') + } + } else if status == C.PGRES_COPY_OUT { + for { + address := &byte(0) + n_bytes := C.PQgetCopyData(db.conn, &address, 0) + if n_bytes > 0 { + mut local_buf := []byte{len: n_bytes} + unsafe { C.memcpy(&byte(local_buf.data), address, n_bytes) } + file.write(local_buf) or { + C.PQfreemem(address) + return err + } + } else if n_bytes == -1 { + break + } else if n_bytes == -2 { + // consult PQerrorMessage for the reason + return error('pg copy error: read error') + } + if address != 0 { + C.PQfreemem(address) + } + } + } + + return 0 +} diff --git a/v_windows/v/old/vlib/pg/pg_orm_test.v b/v_windows/v/old/vlib/pg/pg_orm_test.v new file mode 100644 index 0000000..eb0fd12 --- /dev/null +++ b/v_windows/v/old/vlib/pg/pg_orm_test.v @@ -0,0 +1,77 @@ +module main + +import orm +import pg + +fn test_pg_orm() { + mut db := pg.connect( + host: 'localhost' + user: 'postgres' + password: '' + dbname: 'postgres' + ) or { panic(err) } + + db.create('Test', [ + orm.TableField{ + name: 'id' + typ: 7 + attrs: [ + StructAttribute{ + name: 'primary' + }, + StructAttribute{ + name: 'sql' + has_arg: true + kind: .plain + arg: 'serial' + }, + ] + }, + orm.TableField{ + name: 'name' + typ: 18 + attrs: [] + }, + orm.TableField{ + name: 'age' + typ: 7 + }, + ]) or { panic(err) } + + db.insert('Test', orm.QueryData{ + fields: ['name', 'age'] + data: [orm.string_to_primitive('Louis'), orm.int_to_primitive(101)] + }) or { panic(err) } + + res := db.@select(orm.SelectConfig{ + table: 'Test' + has_where: true + fields: ['id', 'name', 'age'] + types: [7, 18, 8] + }, orm.QueryData{}, orm.QueryData{ + fields: ['name'] + data: [orm.Primitive('Louis'), i64(101)] + types: [18] + is_and: [true] + kinds: [.eq] + }) or { panic(err) } + + id := res[0][0] + name := res[0][1] + age := res[0][2] + + assert id is int + if id is int { + assert id == 1 + } + + assert name is string + if name is string { + assert name == 'Louis' + } + + assert age is i64 + if age is i64 { + assert age == 101 + } +} diff --git a/v_windows/v/old/vlib/picoev/picoev.v b/v_windows/v/old/vlib/picoev/picoev.v new file mode 100644 index 0000000..7c11d03 --- /dev/null +++ b/v_windows/v/old/vlib/picoev/picoev.v @@ -0,0 +1,265 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module picoev + +import net +import picohttpparser + +#include +#include +#include +#flag -I @VEXEROOT/thirdparty/picoev +#flag -L @VEXEROOT/thirdparty/picoev +#flag @VEXEROOT/thirdparty/picoev/picoev.o +#include "src/picoev.h" + +struct C.in_addr { +mut: + s_addr int +} + +struct C.sockaddr_in { +mut: + sin_family int + sin_port int + sin_addr C.in_addr +} + +struct C.sockaddr_storage {} + +fn C.atoi() int + +fn C.strncasecmp(s1 &char, s2 &char, n size_t) int + +struct C.picoev_loop {} + +fn C.picoev_del(&C.picoev_loop, int) int + +fn C.picoev_set_timeout(&C.picoev_loop, int, int) + +// fn C.picoev_handler(loop &C.picoev_loop, fd int, revents int, cb_arg voidptr) +// TODO: (sponge) update to C.picoev_handler with C type def update +type Cpicoev_handler = fn (loop &C.picoev_loop, fd int, revents int, context voidptr) + +fn C.picoev_add(&C.picoev_loop, int, int, int, &Cpicoev_handler, voidptr) int + +fn C.picoev_init(int) int + +fn C.picoev_create_loop(int) &C.picoev_loop + +fn C.picoev_loop_once(&C.picoev_loop, int) int + +fn C.picoev_destroy_loop(&C.picoev_loop) int + +fn C.picoev_deinit() int + +const ( + max_fds = 1024 + max_timeout = 10 + max_read = 4096 + max_write = 8192 +) + +enum Event { + read = C.PICOEV_READ + write = C.PICOEV_WRITE + timeout = C.PICOEV_TIMEOUT + add = C.PICOEV_ADD + del = C.PICOEV_DEL + readwrite = C.PICOEV_READWRITE +} + +pub struct Config { +pub: + port int = 8080 + cb fn (voidptr, picohttpparser.Request, mut picohttpparser.Response) + err_cb fn (voidptr, picohttpparser.Request, mut picohttpparser.Response, IError) = default_err_cb + user_data voidptr = voidptr(0) + timeout_secs int = 8 + max_headers int = 100 +} + +struct Picoev { + loop &C.picoev_loop + cb fn (voidptr, picohttpparser.Request, mut picohttpparser.Response) + err_cb fn (voidptr, picohttpparser.Request, mut picohttpparser.Response, IError) + user_data voidptr + timeout_secs int + max_headers int +mut: + date &byte + buf &byte + idx [1024]int + out &byte +} + +[inline] +fn setup_sock(fd int) ? { + flag := 1 + if C.setsockopt(fd, C.IPPROTO_TCP, C.TCP_NODELAY, &flag, sizeof(int)) < 0 { + return error('setup_sock.setup_sock failed') + } + if C.fcntl(fd, C.F_SETFL, C.O_NONBLOCK) != 0 { + return error('fcntl failed') + } +} + +[inline] +fn close_conn(loop &C.picoev_loop, fd int) { + C.picoev_del(loop, fd) + C.close(fd) +} + +[inline] +fn req_read(fd int, b &byte, max_len int, idx int) int { + unsafe { + return C.read(fd, b + idx, max_len - idx) + } +} + +fn rw_callback(loop &C.picoev_loop, fd int, events int, context voidptr) { + mut p := unsafe { &Picoev(context) } + defer { + p.idx[fd] = 0 + } + if (events & int(Event.timeout)) != 0 { + close_conn(loop, fd) + return + } else if (events & int(Event.read)) != 0 { + C.picoev_set_timeout(loop, fd, p.timeout_secs) + + // Request init + mut buf := p.buf + unsafe { + buf += fd * picoev.max_read // pointer magic + } + mut req := picohttpparser.Request{} + + // Response init + mut out := p.out + unsafe { + out += fd * picoev.max_write // pointer magic + } + mut res := picohttpparser.Response{ + fd: fd + date: p.date + buf_start: out + buf: out + } + + for { + // Request parsing loop + r := req_read(fd, buf, picoev.max_read, p.idx[fd]) // Get data from socket + if r == 0 { + // connection closed by peer + close_conn(loop, fd) + return + } else if r == -1 { + // error + if C.errno == C.EAGAIN || C.errno == C.EWOULDBLOCK { + // try again later + return + } else { + // fatal error + close_conn(loop, fd) + return + } + } + p.idx[fd] += r + + mut s := unsafe { tos(buf, p.idx[fd]) } + pret := req.parse_request(s, p.max_headers) // Parse request via picohttpparser + if pret > 0 { // Success + break + } else if pret == -1 { // Parse error + p.err_cb(mut p.user_data, req, mut &res, error('ParseError')) + return + } + + assert pret == -2 + // request is incomplete, continue the loop + if p.idx[fd] == sizeof(buf) { + p.err_cb(mut p.user_data, req, mut &res, error('RequestIsTooLongError')) + return + } + } + + // Callback (should call .end() itself) + p.cb(mut p.user_data, req, mut &res) + } +} + +fn accept_callback(loop &C.picoev_loop, fd int, events int, cb_arg voidptr) { + mut p := unsafe { &Picoev(cb_arg) } + newfd := C.accept(fd, 0, 0) + if newfd != -1 { + setup_sock(newfd) or { + p.err_cb(mut p.user_data, picohttpparser.Request{}, mut &picohttpparser.Response{}, + err) + } + C.picoev_add(loop, newfd, int(Event.read), p.timeout_secs, rw_callback, cb_arg) + } +} + +fn default_err_cb(data voidptr, req picohttpparser.Request, mut res picohttpparser.Response, error IError) { + eprintln('picoev: $error') + res.end() +} + +pub fn new(config Config) &Picoev { + fd := C.socket(net.AddrFamily.ip, net.SocketType.tcp, 0) + assert fd != -1 + flag := 1 + assert C.setsockopt(fd, C.SOL_SOCKET, C.SO_REUSEADDR, &flag, sizeof(int)) == 0 + assert C.setsockopt(fd, C.SOL_SOCKET, C.SO_REUSEPORT, &flag, sizeof(int)) == 0 + $if linux { + assert C.setsockopt(fd, C.IPPROTO_TCP, C.TCP_QUICKACK, &flag, sizeof(int)) == 0 + timeout := 10 + assert C.setsockopt(fd, C.IPPROTO_TCP, C.TCP_DEFER_ACCEPT, &timeout, sizeof(int)) == 0 + queue_len := 4096 + assert C.setsockopt(fd, C.IPPROTO_TCP, C.TCP_FASTOPEN, &queue_len, sizeof(int)) == 0 + } + mut addr := C.sockaddr_in{} + addr.sin_family = C.AF_INET + addr.sin_port = C.htons(config.port) + addr.sin_addr.s_addr = C.htonl(C.INADDR_ANY) + size := 16 // sizeof(C.sockaddr_in) + bind_res := C.bind(fd, unsafe { &net.Addr(&addr) }, size) + assert bind_res == 0 + listen_res := C.listen(fd, C.SOMAXCONN) + assert listen_res == 0 + setup_sock(fd) or { + config.err_cb(mut config.user_data, picohttpparser.Request{}, mut &picohttpparser.Response{}, + err) + } + C.picoev_init(picoev.max_fds) + loop := C.picoev_create_loop(picoev.max_timeout) + mut pv := &Picoev{ + loop: loop + cb: config.cb + err_cb: config.err_cb + user_data: config.user_data + timeout_secs: config.timeout_secs + max_headers: config.max_headers + date: C.get_date() + buf: unsafe { malloc_noscan(picoev.max_fds * picoev.max_read + 1) } + out: unsafe { malloc_noscan(picoev.max_fds * picoev.max_write + 1) } + } + C.picoev_add(loop, fd, int(Event.read), 0, accept_callback, pv) + go update_date(mut pv) + return pv +} + +pub fn (p Picoev) serve() { + for { + C.picoev_loop_once(p.loop, 1) + } +} + +fn update_date(mut p Picoev) { + for { + p.date = C.get_date() + C.usleep(1000000) + } +} diff --git a/v_windows/v/old/vlib/picohttpparser/misc.v b/v_windows/v/old/vlib/picohttpparser/misc.v new file mode 100644 index 0000000..6c8e1e7 --- /dev/null +++ b/v_windows/v/old/vlib/picohttpparser/misc.v @@ -0,0 +1,20 @@ +module picohttpparser + +[inline; unsafe] +fn cpy(dst &byte, src &byte, len int) int { + unsafe { C.memcpy(dst, src, len) } + return len +} + +[inline] +pub fn cmp(dst string, src string) bool { + if dst.len != src.len { + return false + } + return unsafe { C.memcmp(dst.str, src.str, src.len) == 0 } +} + +[inline] +pub fn cmpn(dst string, src string, n int) bool { + return unsafe { C.memcmp(dst.str, src.str, n) == 0 } +} diff --git a/v_windows/v/old/vlib/picohttpparser/picohttpparser.v b/v_windows/v/old/vlib/picohttpparser/picohttpparser.v new file mode 100644 index 0000000..a8c93e6 --- /dev/null +++ b/v_windows/v/old/vlib/picohttpparser/picohttpparser.v @@ -0,0 +1,35 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module picohttpparser + +#flag -I @VEXEROOT/thirdparty/picohttpparser +#flag -L @VEXEROOT/thirdparty/picohttpparser +#flag @VEXEROOT/thirdparty/picohttpparser/picohttpparser.o + +#include "picohttpparser.h" + +struct C.phr_header { +pub: + name &char + name_len int + value &char + value_len int +} + +type PPchar = &&char + +struct C.phr_header_t {} + +fn C.phr_parse_request(buf &char, len size_t, method PPchar, method_len &size_t, path PPchar, path_len &size_t, minor_version &int, headers &C.phr_header, num_headers &size_t, last_len size_t) int + +fn C.phr_parse_response(buf &char, len size_t, minor_version &int, status &int, msg PPchar, msg_len &size_t, headers &C.phr_header, num_headers &size_t, last_len size_t) int + +fn C.phr_parse_headers(buf &char, len size_t, headers &C.phr_header, num_headers &size_t, last_len size_t) int + +fn C.phr_parse_request_path(buf_start &char, len size_t, method PPchar, method_len &size_t, path PPchar, path_len &size_t) int +fn C.phr_parse_request_path_pipeline(buf_start &char, len size_t, method PPchar, method_len &size_t, path PPchar, path_len &size_t) int +fn C.get_date() &byte + +// static inline int u64toa(char* buf, uint64_t value) { +fn C.u64toa(buffer &char, value u64) int diff --git a/v_windows/v/old/vlib/picohttpparser/request.v b/v_windows/v/old/vlib/picohttpparser/request.v new file mode 100644 index 0000000..98667be --- /dev/null +++ b/v_windows/v/old/vlib/picohttpparser/request.v @@ -0,0 +1,65 @@ +module picohttpparser + +pub struct Request { +mut: + prev_len int +pub mut: + method string + path string + headers [100]C.phr_header + num_headers u64 + body string +} + +[inline] +pub fn (mut r Request) parse_request(s string, max_headers int) int { + method_len := size_t(0) + path_len := size_t(0) + minor_version := 0 + num_headers := size_t(max_headers) + + pret := C.phr_parse_request(s.str, s.len, PPchar(&r.method.str), &method_len, PPchar(&r.path.str), + &path_len, &minor_version, &r.headers[0], &num_headers, r.prev_len) + if pret > 0 { + unsafe { + r.method = tos(r.method.str, int(method_len)) + r.path = tos(r.path.str, int(path_len)) + } + r.num_headers = u64(num_headers) + } + r.body = unsafe { (&s.str[pret]).vstring_literal_with_len(s.len - pret) } + r.prev_len = s.len + return pret +} + +[inline] +pub fn (mut r Request) parse_request_path(s string) int { + method_len := size_t(0) + path_len := size_t(0) + + pret := C.phr_parse_request_path(s.str, s.len, PPchar(&r.method.str), &method_len, + PPchar(&r.path.str), &path_len) + if pret > 0 { + unsafe { + r.method = tos(r.method.str, int(method_len)) + r.path = tos(r.path.str, int(path_len)) + } + } + return pret +} + +[inline] +pub fn (mut r Request) parse_request_path_pipeline(s string) int { + method_len := size_t(0) + path_len := size_t(0) + + pret := C.phr_parse_request_path_pipeline(s.str, s.len, PPchar(&r.method.str), &method_len, + PPchar(&r.path.str), &path_len) + if pret > 0 { + unsafe { + r.method = tos(r.method.str, int(method_len)) + r.path = tos(r.path.str, int(path_len)) + } + } + return pret +} diff --git a/v_windows/v/old/vlib/picohttpparser/response.v b/v_windows/v/old/vlib/picohttpparser/response.v new file mode 100644 index 0000000..5c490ea --- /dev/null +++ b/v_windows/v/old/vlib/picohttpparser/response.v @@ -0,0 +1,114 @@ +module picohttpparser + +pub struct Response { + fd int +pub: + date &byte = 0 + buf_start &byte = 0 +pub mut: + buf &byte = 0 +} + +[inline] +pub fn (mut r Response) write_string(s string) { + unsafe { + C.memcpy(r.buf, s.str, s.len) + r.buf += s.len + } +} + +[inline] +pub fn (mut r Response) http_ok() &Response { + r.write_string('HTTP/1.1 200 OK\r\n') + return unsafe { r } +} + +[inline] +pub fn (mut r Response) header(k string, v string) &Response { + r.write_string(k) + r.write_string(': ') + r.write_string(v) + r.write_string('\r\n') + return unsafe { r } +} + +[inline] +pub fn (mut r Response) header_date() &Response { + r.write_string('Date: ') + unsafe { + r.buf += cpy(r.buf, r.date, 29) + } + r.write_string('\r\n') + return unsafe { r } +} + +[inline] +pub fn (mut r Response) header_server() &Response { + r.write_string('Server: V\r\n') + return unsafe { r } +} + +[inline] +pub fn (mut r Response) content_type(s string) &Response { + r.write_string('Content-Type: ') + r.write_string(s) + r.write_string('\r\n') + return unsafe { r } +} + +[inline] +pub fn (mut r Response) html() &Response { + r.write_string('Content-Type: text/html\r\n') + return unsafe { r } +} + +[inline] +pub fn (mut r Response) plain() &Response { + r.write_string('Content-Type: text/plain\r\n') + return unsafe { r } +} + +[inline] +pub fn (mut r Response) json() &Response { + r.write_string('Content-Type: application/json\r\n') + return unsafe { r } +} + +[inline] +pub fn (mut r Response) body(body string) { + r.write_string('Content-Length: ') + unsafe { + r.buf += C.u64toa(r.buf, body.len) + } + r.write_string('\r\n\r\n') + r.write_string(body) +} + +[inline] +pub fn (mut r Response) http_404() { + r.write_string('HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\n\r\n') +} + +[inline] +pub fn (mut r Response) http_405() { + r.write_string('HTTP/1.1 405 Method Not Allowed\r\nContent-Length: 0\r\n\r\n') +} + +[inline] +pub fn (mut r Response) http_500() { + r.write_string('HTTP/1.1 500 Internal Server Error\r\nContent-Length: 0\r\n\r\n') +} + +[inline] +pub fn (mut r Response) raw(response string) { + r.write_string(response) +} + +[inline] +pub fn (mut r Response) end() int { + n := int(r.buf) - int(r.buf_start) + if C.write(r.fd, r.buf_start, n) != n { + return -1 + } + return n +} diff --git a/v_windows/v/old/vlib/rand/README.md b/v_windows/v/old/vlib/rand/README.md new file mode 100644 index 0000000..b5b917f --- /dev/null +++ b/v_windows/v/old/vlib/rand/README.md @@ -0,0 +1,87 @@ +# Quickstart + +The V `rand` module provides two main ways in which users can generate pseudorandom numbers: + +1. Through top-level functions in the `rand` module. + - `import rand` - Import the `rand` module. + - `rand.seed(seed_data)` to seed (optional). + - Use `rand.int()`, `rand.u32n(max)`, etc. +2. Through a generator of choice. The PRNGs are included in their respective submodules. + - `import rand.pcg32` - Import the module of the PRNG required. + - `mut rng := pcg32.PCG32RNG{}` - Initialize the struct. Note that the **`mut`** is important. + - `rng.seed(seed_data)` - optionally seed it with an array of `u32` values. + - Use `rng.int()`, `rng.u32n(max)`, etc. + +You can change the default generator to a different one. The only requirement is that +the generator must implement the `PRNG` interface. See `get_current_rng()` and `set_rng()`. + +For non-uniform distributions, refer to the `rand.dist` module which defined functions for +sampling from non-uniform distributions. These functions make use of the global RNG. + +**Note:** The global PRNG is not thread safe. It is recommended to use separate generators for +separate threads in multi-threaded applications. If you need to use non-uniform sampling functions, +it is recommended to generate them before use in a multi-threaded context. + +For sampling functions and generating random strings, see `string_from_set()` and other related +functions defined in this top-level module. + +For arrays, see `rand.util`. + +# General Background + +A PRNG is a Pseudo Random Number Generator. +Computers cannot generate truly random numbers without an external source of noise or entropy. +We can use algorithms to generate sequences of seemingly random numbers, +but their outputs will always be deterministic. +This is often useful for simulations that need the same starting seed. + +If you need truly random numbers that are going to be used for cryptography, +use the `crypto.rand` module. + +# Guaranteed functions + +The following 21 functions are guaranteed to be supported by `rand` +as well as the individual PRNGs. + +- `seed(seed_data)` where `seed_data` is an array of `u32` values. + Different generators require different number of bits as the initial seed. + The smallest is 32-bits, required by `sys.SysRNG`. + Most others require 64-bits or 2 `u32` values. +- `u32()`, `u64()`, `int()`, `i64()`, `f32()`, `f64()` +- `u32n(max)`, `u64n(max)`, `intn(max)`, `i64n(max)`, `f32n(max)`, `f64n(max)` +- `u32_in_range(min, max)`, `u64_in_range(min, max)`, `int_in_range(min, max)`, + `i64_in_range(min, max)`, `f32_in_range(min, max)`, `f64_in_range(min, max)` +- `int31()`, `int63()` + +There are several additional functions defined in the top-level module that rely +on the global RNG. If you want to make use of those functions with a different +PRNG, you can can change the global RNG to do so. + +# Seeding Functions + +All the generators are time-seeded. +The helper functions publicly available in `rand.seed` module are: + +1. `time_seed_array()` - returns a `[]u32` that can be directly plugged into the `seed()` functions. +2. `time_seed_32()` and `time_seed_64()` - 32-bit and 64-bit values respectively + that are generated from the current time. + +# Caveats + +Note that the `sys.SysRNG` struct (in the C backend) uses `C.srand()` which sets the seed globally. +Consequently, all instances of the RNG will be affected. +This problem does not arise for the other RNGs. +A workaround (if you _must_ use the libc RNG) is to: + +1. Seed the first instance. +2. Generate all values required. +3. Seed the second instance. +4. Generate all values required. +5. And so on... + +# Notes + +Please note that [math interval](https://en.wikipedia.org/wiki/Interval_(mathematics)#Including_or_excluding_endpoints) notation is used throughout +the function documentation to denote what numbers ranges include. +An example of `[0, max)` thus denotes a range with all posible values +between `0` and `max` **including** 0 but **excluding** `max`. diff --git a/v_windows/v/old/vlib/rand/constants/constants.v b/v_windows/v/old/vlib/rand/constants/constants.v new file mode 100644 index 0000000..76fe211 --- /dev/null +++ b/v_windows/v/old/vlib/rand/constants/constants.v @@ -0,0 +1,12 @@ +module constants + +// Commonly used constants across RNGs - some taken from "Numerical Recipes". +pub const ( + lower_mask = u64(0x00000000FFFFFFFF) + max_u32 = 0xFFFFFFFF + max_u64 = 0xFFFFFFFFFFFFFFFF + max_u32_as_f32 = f32(max_u32) + 1 + max_u64_as_f64 = f64(max_u64) + 1 + u31_mask = u32(0x7FFFFFFF) + u63_mask = u64(0x7FFFFFFFFFFFFFFF) +) diff --git a/v_windows/v/old/vlib/rand/dist/README.md b/v_windows/v/old/vlib/rand/dist/README.md new file mode 100644 index 0000000..85b7177 --- /dev/null +++ b/v_windows/v/old/vlib/rand/dist/README.md @@ -0,0 +1,10 @@ +# Non-Uniform Distribution Functions + +This module contains functions for sampling from non-uniform distributions. + +All implementations of the `rand.PRNG` interface generate numbers from uniform +distributions. This library exists to allow the generation of pseudorandom numbers +sampled from non-uniform distributions. Additionally, it allows the user to use any +PRNG of their choice. This is because the default RNG can be reassigned to a different +generator. It can either be one of the pre-existing one (which are well-tested and +recommended) or a custom user-defined one. See `rand.set_rng()`. \ No newline at end of file diff --git a/v_windows/v/old/vlib/rand/dist/dist.v b/v_windows/v/old/vlib/rand/dist/dist.v new file mode 100644 index 0000000..b2d9055 --- /dev/null +++ b/v_windows/v/old/vlib/rand/dist/dist.v @@ -0,0 +1,85 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module dist + +import math +import rand + +fn check_probability_range(p f64) { + if p < 0 || p > 1 { + panic('$p is not a valid probability value.') + } +} + +// bernoulli returns true with a probability p. Note that 0 <= p <= 1. +pub fn bernoulli(p f64) bool { + check_probability_range(p) + return rand.f64() <= p +} + +// binomial returns the number of successful trials out of n when the +// probability of success for each trial is p. +pub fn binomial(n int, p f64) int { + check_probability_range(p) + mut count := 0 + for _ in 0 .. n { + if bernoulli(p) { + count++ + } + } + return count +} + +// Configuration struct for the `normal_pair` function. The default value for +// `mu` is 0 and the default value for `sigma` is 1. +pub struct NormalConfigStruct { + mu f64 = 0.0 + sigma f64 = 1.0 +} + +// normal_pair returns a pair of normally distributed random numbers with the mean mu +// and standard deviation sigma. If not specified, mu is 0 and sigma is 1. Intended usage is +// `x, y := normal_pair(mu: mean, sigma: stdev)`, or `x, y := normal_pair()`. +pub fn normal_pair(config NormalConfigStruct) (f64, f64) { + if config.sigma <= 0 { + panic('The standard deviation has to be positive.') + } + // This is an implementation of the Marsaglia polar method + // See: https://doi.org/10.1137%2F1006063 + // Also: https://en.wikipedia.org/wiki/Marsaglia_polar_method + for { + u := rand.f64_in_range(-1, 1) + v := rand.f64_in_range(-1, 1) + + s := u * u + v * v + if s >= 1 || s == 0 { + continue + } + t := math.sqrt(-2 * math.log(s) / s) + x := config.mu + config.sigma * t * u + y := config.mu + config.sigma * t * v + return x, y + } + return config.mu, config.mu +} + +// normal returns a normally distributed random number with the mean mu and standard deviation +// sigma. If not specified, mu is 0 and sigma is 1. Intended usage is +// `x := normal(mu: mean, sigma: etdev)` or `x := normal()`. +// **NOTE:** If you are generating a lot of normal variates, use `the normal_pair` function +// instead. This function discards one of the two variates generated by the `normal_pair` function. +pub fn normal(config NormalConfigStruct) f64 { + x, _ := normal_pair(config) + return x +} + +// exponential returns an exponentially distributed random number with the rate paremeter +// lambda. It is expected that lambda is positive. +pub fn exponential(lambda f64) f64 { + if lambda <= 0 { + panic('The rate (lambda) must be positive.') + } + // Use the inverse transform sampling method + return -math.log(rand.f64()) / lambda +} diff --git a/v_windows/v/old/vlib/rand/dist/dist_test.v b/v_windows/v/old/vlib/rand/dist/dist_test.v new file mode 100644 index 0000000..a7c565e --- /dev/null +++ b/v_windows/v/old/vlib/rand/dist/dist_test.v @@ -0,0 +1,134 @@ +import math +import rand +import rand.dist + +const ( + // The sample size to be used + count = 2000 + // Accepted error is within 5% of the actual values. + error = 0.05 + // The seeds used (for reproducible testing) + seeds = [[u32(0xffff24), 0xabcd], [u32(0x141024), 0x42851], + [u32(0x1452), 0x90cd], + ] +) + +fn test_bernoulli() { + ps := [0.0, 0.1, 1.0 / 3.0, 0.5, 0.8, 17.0 / 18.0, 1.0] + + for seed in seeds { + rand.seed(seed) + for p in ps { + mut successes := 0 + for _ in 0 .. count { + if dist.bernoulli(p) { + successes++ + } + } + assert math.abs(f64(successes) / count - p) < error + } + } +} + +fn test_binomial() { + ns := [100, 200, 1000] + ps := [0.0, 0.5, 0.95, 1.0] + + for seed in seeds { + rand.seed(seed) + for n in ns { + for p in ps { + np := n * p + npq := np * (1 - p) + + mut sum := 0 + mut var := 0.0 + for _ in 0 .. count { + x := dist.binomial(n, p) + sum += x + dist := (x - np) + var += dist * dist + } + + assert math.abs(f64(sum / count) - np) / n < error + assert math.abs(f64(var / count) - npq) / n < error + } + } + } +} + +fn test_normal_pair() { + mus := [0, 10, 100, -40] + sigmas := [1, 2, 40, 5] + total := 2 * count + + for seed in seeds { + rand.seed(seed) + for mu in mus { + for sigma in sigmas { + mut sum := 0.0 + mut var := 0.0 + for _ in 0 .. count { + x, y := dist.normal_pair(mu: mu, sigma: sigma) + sum += x + y + dist_x := x - mu + dist_y := y - mu + var += dist_x * dist_x + var += dist_y * dist_y + } + + variance := sigma * sigma + assert math.abs(f64(sum / total) - mu) / sigma < 1 + assert math.abs(f64(var / total) - variance) / variance < 2 * error + } + } + } +} + +fn test_normal() { + mus := [0, 10, 100, -40, 20] + sigmas := [1, 2, 5] + + for seed in seeds { + rand.seed(seed) + for mu in mus { + for sigma in sigmas { + mut sum := 0.0 + mut var := 0.0 + for _ in 0 .. count { + x := dist.normal(mu: mu, sigma: sigma) + sum += x + dist := x - mu + var += dist * dist + } + + variance := sigma * sigma + assert math.abs(f64(sum / count) - mu) / sigma < 1 + assert math.abs(f64(var / count) - variance) / variance < 2 * error + } + } + } +} + +fn test_exponential() { + lambdas := [1.0, 10, 1 / 20.0, 1 / 10000.0, 1 / 524.0, 200] + + for seed in seeds { + rand.seed(seed) + for lambda in lambdas { + mu := 1 / lambda + variance := mu * mu + mut sum := 0.0 + mut var := 0.0 + for _ in 0 .. count { + x := dist.exponential(lambda) + sum += x + dist := x - mu + var += dist * dist + } + + assert math.abs((f64(sum / count) - mu) / mu) < error + assert math.abs((f64(var / count) - variance) / variance) < 2 * error + } + } +} diff --git a/v_windows/v/old/vlib/rand/mt19937/mt19937.v b/v_windows/v/old/vlib/rand/mt19937/mt19937.v new file mode 100644 index 0000000..256fc06 --- /dev/null +++ b/v_windows/v/old/vlib/rand/mt19937/mt19937.v @@ -0,0 +1,325 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module mt19937 + +import math.bits + +/* +C++ functions for MT19937, with initialization improved 2002/2/10. + Coded by Takuji Nishimura and Makoto Matsumoto. + This is a faster version by taking Shawn Cokus's optimization, + Matthe Bellew's simplification, Isaku Wada's real version. + + Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. The names of its contributors may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + Any feedback is very welcome. + http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html + email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space) +*/ +const ( + nn = 312 + mm = 156 + matrix_a = 0xB5026F5AA96619E9 + um = 0xFFFFFFFF80000000 + lm = 0x7FFFFFFF + inv_f64_limit = 1.0 / 9007199254740992.0 +) + +// MT19937RNG is generator that uses the Mersenne Twister algorithm with period 2^19937. +// **NOTE**: The RNG is not seeded when instantiated so remember to seed it before use. +pub struct MT19937RNG { +mut: + state []u64 = []u64{len: mt19937.nn} + mti int = mt19937.nn + next_rnd u32 + has_next bool +} + +// calculate_state returns a random state array calculated from the `seed_data`. +fn calculate_state(seed_data []u32, mut state []u64) []u64 { + lo := u64(seed_data[0]) + hi := u64(seed_data[1]) + state[0] = u64((hi << 32) | lo) + for j := 1; j < mt19937.nn; j++ { + state[j] = u64(6364136223846793005) * (state[j - 1] ^ (state[j - 1] >> 62)) + u64(j) + } + return *state +} + +// seed sets the current random state based on `seed_data`. +// seed expects `seed_data` to be only two `u32`s in little-endian format as [lower, higher]. +pub fn (mut rng MT19937RNG) seed(seed_data []u32) { + if seed_data.len != 2 { + eprintln('mt19937 needs only two 32bit integers as seed: [lower, higher]') + exit(1) + } + // calculate 2 times because MT19937RNG init didn't call calculate_state. + rng.state = calculate_state(seed_data, mut rng.state) + rng.state = calculate_state(seed_data, mut rng.state) + rng.mti = mt19937.nn + rng.next_rnd = 0 + rng.has_next = false +} + +// u32 returns a pseudorandom 32bit int in range `[0, 2³²)`. +[inline] +pub fn (mut rng MT19937RNG) u32() u32 { + if rng.has_next { + rng.has_next = false + return rng.next_rnd + } + ans := rng.u64() + rng.next_rnd = u32(ans >> 32) + rng.has_next = true + return u32(ans & 0xffffffff) +} + +// u64 returns a pseudorandom 64bit int in range `[0, 2⁶⁴)`. +[inline] +pub fn (mut rng MT19937RNG) u64() u64 { + mag01 := [u64(0), u64(mt19937.matrix_a)] + mut x := u64(0) + mut i := int(0) + if rng.mti >= mt19937.nn { + for i = 0; i < mt19937.nn - mt19937.mm; i++ { + x = (rng.state[i] & mt19937.um) | (rng.state[i + 1] & mt19937.lm) + rng.state[i] = rng.state[i + mt19937.mm] ^ (x >> 1) ^ mag01[int(x & 1)] + } + for i < mt19937.nn - 1 { + x = (rng.state[i] & mt19937.um) | (rng.state[i + 1] & mt19937.lm) + rng.state[i] = rng.state[i + (mt19937.mm - mt19937.nn)] ^ (x >> 1) ^ mag01[int(x & 1)] + i++ + } + x = (rng.state[mt19937.nn - 1] & mt19937.um) | (rng.state[0] & mt19937.lm) + rng.state[mt19937.nn - 1] = rng.state[mt19937.mm - 1] ^ (x >> 1) ^ mag01[int(x & 1)] + rng.mti = 0 + } + x = rng.state[rng.mti] + rng.mti++ + x ^= (x >> 29) & 0x5555555555555555 + x ^= (x << 17) & 0x71D67FFFEDA60000 + x ^= (x << 37) & 0xFFF7EEE000000000 + x ^= (x >> 43) + return x +} + +// int returns a 32-bit signed (possibly negative) `int`. +[inline] +pub fn (mut rng MT19937RNG) int() int { + return int(rng.u32()) +} + +// i64 returns a 64-bit signed (possibly negative) `i64`. +[inline] +pub fn (mut rng MT19937RNG) i64() i64 { + return i64(rng.u64()) +} + +// int31 returns a 31bit positive pseudorandom `int`. +[inline] +pub fn (mut rng MT19937RNG) int31() int { + return int(rng.u32() >> 1) +} + +// int63 returns a 63bit positive pseudorandom `i64`. +[inline] +pub fn (mut rng MT19937RNG) int63() i64 { + return i64(rng.u64() >> 1) +} + +// u32n returns a 32bit `u32` in range `[0, max)`. +[inline] +pub fn (mut rng MT19937RNG) u32n(max u32) u32 { + if max == 0 { + eprintln('max must be positive integer.') + exit(1) + } + // Check SysRNG in system_rng.c.v for explanation + bit_len := bits.len_32(max) + if bit_len == 32 { + for { + value := rng.u32() + if value < max { + return value + } + } + } else { + mask := (u32(1) << (bit_len + 1)) - 1 + for { + value := rng.u32() & mask + if value < max { + return value + } + } + } + return u32(0) +} + +// u64n returns a 64bit `u64` in range `[0, max)`. +[inline] +pub fn (mut rng MT19937RNG) u64n(max u64) u64 { + if max == 0 { + eprintln('max must be positive integer.') + exit(1) + } + bit_len := bits.len_64(max) + if bit_len == 64 { + for { + value := rng.u64() + if value < max { + return value + } + } + } else { + mask := (u64(1) << (bit_len + 1)) - 1 + for { + value := rng.u64() & mask + if value < max { + return value + } + } + } + return u64(0) +} + +// u32n returns a pseudorandom `u32` value that is guaranteed to be in range `[min, max)`. +[inline] +pub fn (mut rng MT19937RNG) u32_in_range(min u32, max u32) u32 { + if max <= min { + eprintln('max must be greater than min.') + exit(1) + } + return min + rng.u32n(max - min) +} + +// u64n returns a pseudorandom `u64` value that is guaranteed to be in range `[min, max)`. +[inline] +pub fn (mut rng MT19937RNG) u64_in_range(min u64, max u64) u64 { + if max <= min { + eprintln('max must be greater than min.') + exit(1) + } + return min + rng.u64n(max - min) +} + +// intn returns a 32bit positive `int` in range `[0, max)`. +[inline] +pub fn (mut rng MT19937RNG) intn(max int) int { + if max <= 0 { + eprintln('max has to be positive.') + exit(1) + } + return int(rng.u32n(u32(max))) +} + +// i64n returns a 64bit positive `i64` in range `[0, max)`. +[inline] +pub fn (mut rng MT19937RNG) i64n(max i64) i64 { + if max <= 0 { + eprintln('max has to be positive.') + exit(1) + } + return i64(rng.u64n(u64(max))) +} + +// int_in_range returns a 32bit positive `int` in range `[min, max)`. +[inline] +pub fn (mut rng MT19937RNG) int_in_range(min int, max int) int { + if max <= min { + eprintln('max must be greater than min.') + exit(1) + } + return min + rng.intn(max - min) +} + +// i64_in_range returns a 64bit positive `i64` in range `[min, max)`. +[inline] +pub fn (mut rng MT19937RNG) i64_in_range(min i64, max i64) i64 { + if max <= min { + eprintln('max must be greater than min.') + exit(1) + } + return min + rng.i64n(max - min) +} + +// f32 returns a 32bit real (`f32`) in range `[0, 1)`. +[inline] +pub fn (mut rng MT19937RNG) f32() f32 { + return f32(rng.f64()) +} + +// f64 returns 64bit real (`f64`) in range `[0, 1)`. +[inline] +pub fn (mut rng MT19937RNG) f64() f64 { + return f64(rng.u64() >> 11) * mt19937.inv_f64_limit +} + +// f32n returns a 32bit real (`f32`) in range [0, max)`. +[inline] +pub fn (mut rng MT19937RNG) f32n(max f32) f32 { + if max <= 0 { + eprintln('max has to be positive.') + exit(1) + } + return rng.f32() * max +} + +// f64n returns a 64bit real (`f64`) in range `[0, max)`. +[inline] +pub fn (mut rng MT19937RNG) f64n(max f64) f64 { + if max <= 0 { + eprintln('max has to be positive.') + exit(1) + } + return rng.f64() * max +} + +// f32_in_range returns a pseudorandom `f32` that lies in range `[min, max)`. +[inline] +pub fn (mut rng MT19937RNG) f32_in_range(min f32, max f32) f32 { + if max <= min { + eprintln('max must be greater than min.') + exit(1) + } + return min + rng.f32n(max - min) +} + +// i64_in_range returns a pseudorandom `i64` that lies in range `[min, max)`. +[inline] +pub fn (mut rng MT19937RNG) f64_in_range(min f64, max f64) f64 { + if max <= min { + eprintln('max must be greater than min.') + exit(1) + } + return min + rng.f64n(max - min) +} diff --git a/v_windows/v/old/vlib/rand/mt19937/mt19937_test.v b/v_windows/v/old/vlib/rand/mt19937/mt19937_test.v new file mode 100644 index 0000000..f2a41de --- /dev/null +++ b/v_windows/v/old/vlib/rand/mt19937/mt19937_test.v @@ -0,0 +1,341 @@ +import math +import rand.mt19937 +import rand.seed + +const ( + range_limit = 40 + value_count = 1000 + seeds = [[u32(0xcafebabe), u32(0xdeadbeef)], [u32(0xc0de), u32(0xfeed)]] +) + +const ( + sample_size = 1000 + stats_epsilon = 0.05 + inv_sqrt_12 = 1.0 / math.sqrt(12) +) + +fn mt19937_basic_test() { + mut rng := mt19937.MT19937RNG{} + rng.seed([u32(0xdeadbeef)]) + target := [u32(956529277), 3842322136, 3319553134, 1843186657, 2704993644, 595827513, 938518626, + 1676224337, 3221315650, 1819026461] + for i := 0; i < 10; i++ { + assert target[i] == rng.u32() + } +} + +fn gen_randoms(seed_data []u32, bound int) []u64 { + bound_u64 := u64(bound) + mut randoms := []u64{len: (20)} + mut rnd := mt19937.MT19937RNG{} + rnd.seed(seed_data) + for i in 0 .. 20 { + randoms[i] = rnd.u64n(bound_u64) + } + return randoms +} + +fn test_mt19937_reproducibility() { + seed_data := seed.time_seed_array(2) + randoms1 := gen_randoms(seed_data, 1000) + randoms2 := gen_randoms(seed_data, 1000) + assert randoms1.len == randoms2.len + len := randoms1.len + for i in 0 .. len { + assert randoms1[i] == randoms2[i] + } +} + +// TODO: use the `in` syntax and remove this function +// after generics has been completely implemented +fn found(value u64, arr []u64) bool { + for item in arr { + if value == item { + return true + } + } + return false +} + +fn test_mt19937_variability() { + // If this test fails and if it is certainly not the implementation + // at fault, try changing the seed values. Repeated values are + // improbable but not impossible. + for seed in seeds { + mut rng := mt19937.MT19937RNG{} + rng.seed(seed) + mut values := []u64{cap: value_count} + for i in 0 .. value_count { + value := rng.u64() + assert !found(value, values) + assert values.len == i + values << value + } + } +} + +fn check_uniformity_u64(mut rng mt19937.MT19937RNG, range u64) { + range_f64 := f64(range) + expected_mean := range_f64 / 2.0 + mut variance := 0.0 + for _ in 0 .. sample_size { + diff := f64(rng.u64n(range)) - expected_mean + variance += diff * diff + } + variance /= sample_size - 1 + sigma := math.sqrt(variance) + expected_sigma := range_f64 * inv_sqrt_12 + error := (sigma - expected_sigma) / expected_sigma + assert math.abs(error) < stats_epsilon +} + +fn test_mt19937_uniformity_u64() { + ranges := [14019545, 80240, 130] + for seed in seeds { + mut rng := mt19937.MT19937RNG{} + rng.seed(seed) + for range in ranges { + check_uniformity_u64(mut rng, u64(range)) + } + } +} + +fn check_uniformity_f64(mut rng mt19937.MT19937RNG) { + expected_mean := 0.5 + mut variance := 0.0 + for _ in 0 .. sample_size { + diff := rng.f64() - expected_mean + variance += diff * diff + } + variance /= sample_size - 1 + sigma := math.sqrt(variance) + expected_sigma := inv_sqrt_12 + error := (sigma - expected_sigma) / expected_sigma + assert math.abs(error) < stats_epsilon +} + +fn test_mt19937_uniformity_f64() { + // The f64 version + for seed in seeds { + mut rng := mt19937.MT19937RNG{} + rng.seed(seed) + check_uniformity_f64(mut rng) + } +} + +fn test_mt19937_u32n() { + max := u32(16384) + for seed in seeds { + mut rng := mt19937.MT19937RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.u32n(max) + assert value >= 0 + assert value < max + } + } +} + +fn test_mt19937_u64n() { + max := u64(379091181005) + for seed in seeds { + mut rng := mt19937.MT19937RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.u64n(max) + assert value >= 0 + assert value < max + } + } +} + +fn test_mt19937_u32_in_range() { + max := u32(484468466) + min := u32(316846) + for seed in seeds { + mut rng := mt19937.MT19937RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.u32_in_range(min, max) + assert value >= min + assert value < max + } + } +} + +fn test_mt19937_u64_in_range() { + max := u64(216468454685163) + min := u64(6848646868) + for seed in seeds { + mut rng := mt19937.MT19937RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.u64_in_range(min, max) + assert value >= min + assert value < max + } + } +} + +fn test_mt19937_int31() { + max_u31 := int(0x7FFFFFFF) + sign_mask := int(0x80000000) + for seed in seeds { + mut rng := mt19937.MT19937RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.int31() + assert value >= 0 + assert value <= max_u31 + // This statement ensures that the sign bit is zero + assert (value & sign_mask) == 0 + } + } +} + +fn test_mt19937_int63() { + max_u63 := i64(0x7FFFFFFFFFFFFFFF) + sign_mask := i64(0x8000000000000000) + for seed in seeds { + mut rng := mt19937.MT19937RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.int63() + assert value >= 0 + assert value <= max_u63 + assert (value & sign_mask) == 0 + } + } +} + +fn test_mt19937_intn() { + max := 2525642 + for seed in seeds { + mut rng := mt19937.MT19937RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.intn(max) + assert value >= 0 + assert value < max + } + } +} + +fn test_mt19937_i64n() { + max := i64(3246727724653636) + for seed in seeds { + mut rng := mt19937.MT19937RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.i64n(max) + assert value >= 0 + assert value < max + } + } +} + +fn test_mt19937_int_in_range() { + min := -4252 + max := 1034 + for seed in seeds { + mut rng := mt19937.MT19937RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.int_in_range(min, max) + assert value >= min + assert value < max + } + } +} + +fn test_mt19937_i64_in_range() { + min := i64(-24095) + max := i64(324058) + for seed in seeds { + mut rng := mt19937.MT19937RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.i64_in_range(min, max) + assert value >= min + assert value < max + } + } +} + +fn test_mt19937_f32() { + for seed in seeds { + mut rng := mt19937.MT19937RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.f32() + assert value >= 0.0 + assert value < 1.0 + } + } +} + +fn test_mt19937_f64() { + for seed in seeds { + mut rng := mt19937.MT19937RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.f64() + assert value >= 0.0 + assert value < 1.0 + } + } +} + +fn test_mt19937_f32n() { + max := f32(357.0) + for seed in seeds { + mut rng := mt19937.MT19937RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.f32n(max) + assert value >= 0.0 + assert value < max + } + } +} + +fn test_mt19937_f64n() { + max := 1.52e6 + for seed in seeds { + mut rng := mt19937.MT19937RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.f64n(max) + assert value >= 0.0 + assert value < max + } + } +} + +fn test_mt19937_f32_in_range() { + min := f32(-24.0) + max := f32(125.0) + for seed in seeds { + mut rng := mt19937.MT19937RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.f32_in_range(min, max) + assert value >= min + assert value < max + } + } +} + +fn test_mt19937_f64_in_range() { + min := -548.7 + max := 5015.2 + for seed in seeds { + mut rng := mt19937.MT19937RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.f64_in_range(min, max) + assert value >= min + assert value < max + } + } +} diff --git a/v_windows/v/old/vlib/rand/musl/musl_rng.v b/v_windows/v/old/vlib/rand/musl/musl_rng.v new file mode 100644 index 0000000..fdfb733 --- /dev/null +++ b/v_windows/v/old/vlib/rand/musl/musl_rng.v @@ -0,0 +1,241 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module musl + +import math.bits +import rand.seed +import rand.constants + +// MuslRNG ported from https://git.musl-libc.org/cgit/musl/tree/src/prng/rand_r.c +pub struct MuslRNG { +mut: + state u32 = seed.time_seed_32() +} + +// seed sets the current random state based on `seed_data`. +// seed expects `seed_data` to be only one `u32`. +pub fn (mut rng MuslRNG) seed(seed_data []u32) { + if seed_data.len != 1 { + eprintln('MuslRNG needs only one unsigned 32-bit integer as a seed.') + exit(1) + } + rng.state = seed_data[0] +} + +// temper returns a tempered value based on `prev` value. +[inline] +fn temper(prev u32) u32 { + mut x := prev + x ^= x >> 11 + x ^= (x << 7) & 0x9D2C5680 + x ^= (x << 15) & 0xEFC60000 + x ^= (x >> 18) + return x +} + +// u32 returns a pseudorandom 32-bit unsigned integer (`u32`). +[inline] +pub fn (mut rng MuslRNG) u32() u32 { + rng.state = rng.state * 1103515245 + 12345 + // We are not dividing by 2 (or shifting right by 1) + // because we want all 32-bits of random data + return temper(rng.state) +} + +// u64 returns a pseudorandom 64-bit unsigned integer (`u64`). +[inline] +pub fn (mut rng MuslRNG) u64() u64 { + return u64(rng.u32()) | (u64(rng.u32()) << 32) +} + +// u32n returns a pseudorandom 32-bit unsigned integer `u32` in range `[0, max)`. +[inline] +pub fn (mut rng MuslRNG) u32n(max u32) u32 { + if max == 0 { + eprintln('max must be positive integer.') + exit(1) + } + // Check SysRNG in system_rng.c.v for explanation + bit_len := bits.len_32(max) + if bit_len == 32 { + for { + value := rng.u32() + if value < max { + return value + } + } + } else { + mask := (u32(1) << (bit_len + 1)) - 1 + for { + value := rng.u32() & mask + if value < max { + return value + } + } + } + return u32(0) +} + +// u64n returns a pseudorandom 64-bit unsigned integer (`u64`) in range `[0, max)`. +[inline] +pub fn (mut rng MuslRNG) u64n(max u64) u64 { + if max == 0 { + eprintln('max must be positive integer.') + exit(1) + } + bit_len := bits.len_64(max) + if bit_len == 64 { + for { + value := rng.u64() + if value < max { + return value + } + } + } else { + mask := (u64(1) << (bit_len + 1)) - 1 + for { + value := rng.u64() & mask + if value < max { + return value + } + } + } + return u64(0) +} + +// u32_in_range returns a pseudorandom 32-bit unsigned integer (`u32`) in range `[min, max)`. +[inline] +pub fn (mut rng MuslRNG) u32_in_range(min u32, max u32) u32 { + if max <= min { + eprintln('max must be greater than min.') + exit(1) + } + return min + rng.u32n(u32(max - min)) +} + +// u64_in_range returns a pseudorandom 64-bit unsigned integer (`u64`) in range `[min, max)`. +[inline] +pub fn (mut rng MuslRNG) u64_in_range(min u64, max u64) u64 { + if max <= min { + eprintln('max must be greater than min.') + exit(1) + } + return min + rng.u64n(max - min) +} + +// int returns a 32-bit signed (possibly negative) integer (`int`). +[inline] +pub fn (mut rng MuslRNG) int() int { + return int(rng.u32()) +} + +// i64 returns a 64-bit signed (possibly negative) integer (`i64`). +[inline] +pub fn (mut rng MuslRNG) i64() i64 { + return i64(rng.u64()) +} + +// int31 returns a 31-bit positive pseudorandom integer (`int`). +[inline] +pub fn (mut rng MuslRNG) int31() int { + return int(rng.u32() >> 1) +} + +// int63 returns a 63-bit positive pseudorandom integer (`i64`). +[inline] +pub fn (mut rng MuslRNG) int63() i64 { + return i64(rng.u64() >> 1) +} + +// intn returns a 32-bit positive int in range `[0, max)`. +[inline] +pub fn (mut rng MuslRNG) intn(max int) int { + if max <= 0 { + eprintln('max has to be positive.') + exit(1) + } + return int(rng.u32n(u32(max))) +} + +// i64n returns a 64-bit positive integer `i64` in range `[0, max)`. +[inline] +pub fn (mut rng MuslRNG) i64n(max i64) i64 { + if max <= 0 { + eprintln('max has to be positive.') + exit(1) + } + return i64(rng.u64n(u64(max))) +} + +// int_in_range returns a 32-bit positive integer `int` in range `[0, max)`. +[inline] +pub fn (mut rng MuslRNG) int_in_range(min int, max int) int { + if max <= min { + eprintln('max must be greater than min.') + exit(1) + } + return min + rng.intn(max - min) +} + +// i64_in_range returns a 64-bit positive integer `i64` in range `[0, max)`. +[inline] +pub fn (mut rng MuslRNG) i64_in_range(min i64, max i64) i64 { + if max <= min { + eprintln('max must be greater than min.') + exit(1) + } + return min + rng.i64n(max - min) +} + +// f32 returns a pseudorandom `f32` value in range `[0, 1)`. +[inline] +pub fn (mut rng MuslRNG) f32() f32 { + return f32(rng.u32()) / constants.max_u32_as_f32 +} + +// f64 returns a pseudorandom `f64` value in range `[0, 1)`. +[inline] +pub fn (mut rng MuslRNG) f64() f64 { + return f64(rng.u64()) / constants.max_u64_as_f64 +} + +// f32n returns a pseudorandom `f32` value in range `[0, max)`. +[inline] +pub fn (mut rng MuslRNG) f32n(max f32) f32 { + if max <= 0 { + eprintln('max has to be positive.') + exit(1) + } + return rng.f32() * max +} + +// f64n returns a pseudorandom `f64` value in range `[0, max)`. +[inline] +pub fn (mut rng MuslRNG) f64n(max f64) f64 { + if max <= 0 { + eprintln('max has to be positive.') + exit(1) + } + return rng.f64() * max +} + +// f32_in_range returns a pseudorandom `f32` in range `[min, max)`. +[inline] +pub fn (mut rng MuslRNG) f32_in_range(min f32, max f32) f32 { + if max <= min { + eprintln('max must be greater than min.') + exit(1) + } + return min + rng.f32n(max - min) +} + +// i64_in_range returns a pseudorandom `i64` in range `[min, max)`. +[inline] +pub fn (mut rng MuslRNG) f64_in_range(min f64, max f64) f64 { + if max <= min { + eprintln('max must be greater than min.') + exit(1) + } + return min + rng.f64n(max - min) +} diff --git a/v_windows/v/old/vlib/rand/musl/musl_rng_test.v b/v_windows/v/old/vlib/rand/musl/musl_rng_test.v new file mode 100644 index 0000000..e6406f9 --- /dev/null +++ b/v_windows/v/old/vlib/rand/musl/musl_rng_test.v @@ -0,0 +1,331 @@ +import math +import rand.musl +import rand.seed + +const ( + range_limit = 40 + value_count = 1000 + seeds = [[u32(42)], [u32(256)]] +) + +const ( + sample_size = 1000 + stats_epsilon = 0.05 + inv_sqrt_12 = 1.0 / math.sqrt(12) +) + +fn gen_randoms(seed_data []u32, bound int) []u64 { + bound_u64 := u64(bound) + mut randoms := []u64{len: (20)} + mut rnd := musl.MuslRNG{} + rnd.seed(seed_data) + for i in 0 .. 20 { + randoms[i] = rnd.u64n(bound_u64) + } + return randoms +} + +fn test_musl_reproducibility() { + seed_data := seed.time_seed_array(1) + randoms1 := gen_randoms(seed_data, 1000) + randoms2 := gen_randoms(seed_data, 1000) + assert randoms1.len == randoms2.len + len := randoms1.len + for i in 0 .. len { + assert randoms1[i] == randoms2[i] + } +} + +// TODO: use the `in` syntax and remove this function +// after generics has been completely implemented +fn found(value u64, arr []u64) bool { + for item in arr { + if value == item { + return true + } + } + return false +} + +fn test_musl_variability() { + // If this test fails and if it is certainly not the implementation + // at fault, try changing the seed values. Repeated values are + // improbable but not impossible. + for seed in seeds { + mut rng := musl.MuslRNG{} + rng.seed(seed) + mut values := []u64{cap: value_count} + for i in 0 .. value_count { + value := rng.u64() + assert !found(value, values) + assert values.len == i + values << value + } + } +} + +fn check_uniformity_u64(mut rng musl.MuslRNG, range u64) { + range_f64 := f64(range) + expected_mean := range_f64 / 2.0 + mut variance := 0.0 + for _ in 0 .. sample_size { + diff := f64(rng.u64n(range)) - expected_mean + variance += diff * diff + } + variance /= sample_size - 1 + sigma := math.sqrt(variance) + expected_sigma := range_f64 * inv_sqrt_12 + error := (sigma - expected_sigma) / expected_sigma + assert math.abs(error) < stats_epsilon +} + +fn test_musl_uniformity_u64() { + ranges := [14019545, 80240, 130] + for seed in seeds { + mut rng := musl.MuslRNG{} + rng.seed(seed) + for range in ranges { + check_uniformity_u64(mut rng, u64(range)) + } + } +} + +fn check_uniformity_f64(mut rng musl.MuslRNG) { + expected_mean := 0.5 + mut variance := 0.0 + for _ in 0 .. sample_size { + diff := rng.f64() - expected_mean + variance += diff * diff + } + variance /= sample_size - 1 + sigma := math.sqrt(variance) + expected_sigma := inv_sqrt_12 + error := (sigma - expected_sigma) / expected_sigma + assert math.abs(error) < stats_epsilon +} + +fn test_musl_uniformity_f64() { + // The f64 version + for seed in seeds { + mut rng := musl.MuslRNG{} + rng.seed(seed) + check_uniformity_f64(mut rng) + } +} + +fn test_musl_u32n() { + max := u32(16384) + for seed in seeds { + mut rng := musl.MuslRNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.u32n(max) + assert value >= 0 + assert value < max + } + } +} + +fn test_musl_u64n() { + max := u64(379091181005) + for seed in seeds { + mut rng := musl.MuslRNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.u64n(max) + assert value >= 0 + assert value < max + } + } +} + +fn test_musl_u32_in_range() { + max := u32(484468466) + min := u32(316846) + for seed in seeds { + mut rng := musl.MuslRNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.u32_in_range(min, max) + assert value >= min + assert value < max + } + } +} + +fn test_musl_u64_in_range() { + max := u64(216468454685163) + min := u64(6848646868) + for seed in seeds { + mut rng := musl.MuslRNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.u64_in_range(min, max) + assert value >= min + assert value < max + } + } +} + +fn test_musl_int31() { + max_u31 := int(0x7FFFFFFF) + sign_mask := int(0x80000000) + for seed in seeds { + mut rng := musl.MuslRNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.int31() + assert value >= 0 + assert value <= max_u31 + // This statement ensures that the sign bit is zero + assert (value & sign_mask) == 0 + } + } +} + +fn test_musl_int63() { + max_u63 := i64(0x7FFFFFFFFFFFFFFF) + sign_mask := i64(0x8000000000000000) + for seed in seeds { + mut rng := musl.MuslRNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.int63() + assert value >= 0 + assert value <= max_u63 + assert (value & sign_mask) == 0 + } + } +} + +fn test_musl_intn() { + max := 2525642 + for seed in seeds { + mut rng := musl.MuslRNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.intn(max) + assert value >= 0 + assert value < max + } + } +} + +fn test_musl_i64n() { + max := i64(3246727724653636) + for seed in seeds { + mut rng := musl.MuslRNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.i64n(max) + assert value >= 0 + assert value < max + } + } +} + +fn test_musl_int_in_range() { + min := -4252 + max := 1034 + for seed in seeds { + mut rng := musl.MuslRNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.int_in_range(min, max) + assert value >= min + assert value < max + } + } +} + +fn test_musl_i64_in_range() { + min := i64(-24095) + max := i64(324058) + for seed in seeds { + mut rng := musl.MuslRNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.i64_in_range(min, max) + assert value >= min + assert value < max + } + } +} + +fn test_musl_f32() { + for seed in seeds { + mut rng := musl.MuslRNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.f32() + assert value >= 0.0 + assert value < 1.0 + } + } +} + +fn test_musl_f64() { + for seed in seeds { + mut rng := musl.MuslRNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.f64() + assert value >= 0.0 + assert value < 1.0 + } + } +} + +fn test_musl_f32n() { + max := f32(357.0) + for seed in seeds { + mut rng := musl.MuslRNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.f32n(max) + assert value >= 0.0 + assert value < max + } + } +} + +fn test_musl_f64n() { + max := 1.52e6 + for seed in seeds { + mut rng := musl.MuslRNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.f64n(max) + assert value >= 0.0 + assert value < max + } + } +} + +fn test_musl_f32_in_range() { + min := f32(-24.0) + max := f32(125.0) + for seed in seeds { + mut rng := musl.MuslRNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.f32_in_range(min, max) + assert value >= min + assert value < max + } + } +} + +fn test_musl_f64_in_range() { + min := -548.7 + max := 5015.2 + for seed in seeds { + mut rng := musl.MuslRNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.f64_in_range(min, max) + assert value >= min + assert value < max + } + } +} diff --git a/v_windows/v/old/vlib/rand/pcg32/pcg32.v b/v_windows/v/old/vlib/rand/pcg32/pcg32.v new file mode 100644 index 0000000..25a83f7 --- /dev/null +++ b/v_windows/v/old/vlib/rand/pcg32/pcg32.v @@ -0,0 +1,226 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module pcg32 + +import rand.seed +import rand.constants + +// PCG32RNG ported from http://www.pcg-random.org/download.html, +// https://github.com/imneme/pcg-c-basic/blob/master/pcg_basic.c, and +// https://github.com/imneme/pcg-c-basic/blob/master/pcg_basic.h +pub struct PCG32RNG { +mut: + state u64 = u64(0x853c49e6748fea9b) ^ seed.time_seed_64() + inc u64 = u64(0xda3e39cb94b95bdb) ^ seed.time_seed_64() +} + +// seed seeds the PCG32RNG with 4 `u32` values. +// The first 2 represent the 64-bit initial state as `[lower 32 bits, higher 32 bits]` +// The last 2 represent the 64-bit stream/step of the PRNG. +pub fn (mut rng PCG32RNG) seed(seed_data []u32) { + if seed_data.len != 4 { + eprintln('PCG32RNG needs 4 u32s to be seeded. First two the initial state and the last two the stream/step. Both in little endian format: [lower, higher].') + exit(1) + } + init_state := u64(seed_data[0]) | (u64(seed_data[1]) << 32) + init_seq := u64(seed_data[2]) | (u64(seed_data[3]) << 32) + rng.state = u64(0) + rng.inc = (init_seq << u64(1)) | u64(1) + rng.u32() + rng.state += init_state + rng.u32() +} + +// u32 returns a pseudorandom unsigned `u32`. +[inline] +pub fn (mut rng PCG32RNG) u32() u32 { + oldstate := rng.state + rng.state = oldstate * (6364136223846793005) + rng.inc + xorshifted := u32(((oldstate >> u64(18)) ^ oldstate) >> u64(27)) + rot := u32(oldstate >> u64(59)) + return ((xorshifted >> rot) | (xorshifted << ((-rot) & u32(31)))) +} + +// u64 returns a pseudorandom 64-bit unsigned `u64`. +[inline] +pub fn (mut rng PCG32RNG) u64() u64 { + return u64(rng.u32()) | (u64(rng.u32()) << 32) +} + +// u32n returns a pseudorandom 32-bit unsigned `u32` in range `[0, max)`. +[inline] +pub fn (mut rng PCG32RNG) u32n(max u32) u32 { + if max == 0 { + eprintln('max must be positive') + exit(1) + } + // To avoid bias, we need to make the range of the RNG a multiple of + // max, which we do by dropping output less than a threshold. + threshold := (-max % max) + // Uniformity guarantees that loop below will terminate. In practice, it + // should usually terminate quickly; on average (assuming all max's are + // equally likely), 82.25% of the time, we can expect it to require just + // one iteration. In practice, max's are typically small and only a + // tiny amount of the range is eliminated. + for { + r := rng.u32() + if r >= threshold { + return (r % max) + } + } + return u32(0) +} + +// u64n returns a pseudorandom 64-bit unsigned `u64` in range `[0, max)`. +[inline] +pub fn (mut rng PCG32RNG) u64n(max u64) u64 { + if max == 0 { + eprintln('max must be positive') + exit(1) + } + threshold := (-max % max) + for { + r := rng.u64() + if r >= threshold { + return (r % max) + } + } + return u64(0) +} + +// u32_in_range returns a pseudorandom 32-bit unsigned `u32` in range `[min, max)`. +[inline] +pub fn (mut rng PCG32RNG) u32_in_range(min u32, max u32) u32 { + if max <= min { + eprintln('max must be greater than min') + exit(1) + } + return min + rng.u32n(u32(max - min)) +} + +// u64_in_range returns a pseudorandom 64-bit unsigned `u64` in range `[min, max)`. +[inline] +pub fn (mut rng PCG32RNG) u64_in_range(min u64, max u64) u64 { + if max <= min { + eprintln('max must be greater than min') + exit(1) + } + return min + rng.u64n(max - min) +} + +// int returns a 32-bit signed (possibly negative) `int`. +[inline] +pub fn (mut rng PCG32RNG) int() int { + return int(rng.u32()) +} + +// i64 returns a 64-bit signed (possibly negative) `i64`. +[inline] +pub fn (mut rng PCG32RNG) i64() i64 { + return i64(rng.u64()) +} + +// int31 returns a 31-bit positive pseudorandom `int`. +[inline] +pub fn (mut rng PCG32RNG) int31() int { + return int(rng.u32() >> 1) +} + +// int63 returns a 63-bit positive pseudorandom `i64`. +[inline] +pub fn (mut rng PCG32RNG) int63() i64 { + return i64(rng.u64() >> 1) +} + +// intn returns a 32-bit positive `int` in range `[0, max)`. +[inline] +pub fn (mut rng PCG32RNG) intn(max int) int { + if max <= 0 { + eprintln('max has to be positive.') + exit(1) + } + return int(rng.u32n(u32(max))) +} + +// i64n returns a 64-bit positive `i64` in range `[0, max)`. +[inline] +pub fn (mut rng PCG32RNG) i64n(max i64) i64 { + if max <= 0 { + eprintln('max has to be positive.') + exit(1) + } + return i64(rng.u64n(u64(max))) +} + +// int_in_range returns a 32-bit positive `int` in range `[0, max)`. +[inline] +pub fn (mut rng PCG32RNG) int_in_range(min int, max int) int { + if max <= min { + eprintln('max must be greater than min.') + exit(1) + } + return min + rng.intn(max - min) +} + +// i64_in_range returns a 64-bit positive `i64` in range `[0, max)`. +[inline] +pub fn (mut rng PCG32RNG) i64_in_range(min i64, max i64) i64 { + if max <= min { + eprintln('max must be greater than min.') + exit(1) + } + return min + rng.i64n(max - min) +} + +// f32 returns a pseudorandom `f32` value in range `[0, 1)`. +[inline] +pub fn (mut rng PCG32RNG) f32() f32 { + return f32(rng.u32()) / constants.max_u32_as_f32 +} + +// f64 returns a pseudorandom `f64` value in range `[0, 1)`. +[inline] +pub fn (mut rng PCG32RNG) f64() f64 { + return f64(rng.u64()) / constants.max_u64_as_f64 +} + +// f32n returns a pseudorandom `f32` value in range `[0, max)`. +[inline] +pub fn (mut rng PCG32RNG) f32n(max f32) f32 { + if max <= 0 { + eprintln('max has to be positive.') + exit(1) + } + return rng.f32() * max +} + +// f64n returns a pseudorandom `f64` value in range `[0, max)`. +[inline] +pub fn (mut rng PCG32RNG) f64n(max f64) f64 { + if max <= 0 { + eprintln('max has to be positive.') + exit(1) + } + return rng.f64() * max +} + +// f32_in_range returns a pseudorandom `f32` in range `[min, max)`. +[inline] +pub fn (mut rng PCG32RNG) f32_in_range(min f32, max f32) f32 { + if max <= min { + eprintln('max must be greater than min') + exit(1) + } + return min + rng.f32n(max - min) +} + +// i64_in_range returns a pseudorandom `i64` in range `[min, max)`. +[inline] +pub fn (mut rng PCG32RNG) f64_in_range(min f64, max f64) f64 { + if max <= min { + eprintln('max must be greater than min') + exit(1) + } + return min + rng.f64n(max - min) +} diff --git a/v_windows/v/old/vlib/rand/pcg32/pcg32_test.v b/v_windows/v/old/vlib/rand/pcg32/pcg32_test.v new file mode 100644 index 0000000..17048a0 --- /dev/null +++ b/v_windows/v/old/vlib/rand/pcg32/pcg32_test.v @@ -0,0 +1,337 @@ +import math +import rand +import rand.pcg32 +import rand.seed + +const ( + range_limit = 40 + value_count = 1000 + seeds = [[u32(42), 242, 267, 14195], [u32(256), 340, 1451, 1505]] +) + +const ( + sample_size = 1000 + stats_epsilon = 0.05 + inv_sqrt_12 = 1.0 / math.sqrt(12) +) + +fn gen_randoms(seed_data []u32, bound int) []u32 { + mut randoms := []u32{len: 20} + mut rng := pcg32.PCG32RNG{} + rng.seed(seed_data) + for i in 0 .. 20 { + randoms[i] = rng.u32n(u32(bound)) + } + return randoms +} + +fn test_pcg32_reproducibility() { + seed_data := seed.time_seed_array(4) + randoms1 := gen_randoms(seed_data, 1000) + randoms2 := gen_randoms(seed_data, 1000) + assert randoms1.len == randoms2.len + len := randoms1.len + for i in 0 .. len { + r1 := randoms1[i] + r2 := randoms2[i] + assert r1 == r2 + } +} + +// TODO: use the `in` syntax and remove this function +// after generics has been completely implemented +fn found(value u64, arr []u64) bool { + for item in arr { + if value == item { + return true + } + } + return false +} + +fn test_pcg32_variability() { + // If this test fails and if it is certainly not the implementation + // at fault, try changing the seed values. Repeated values are + // improbable but not impossible. + for seed in seeds { + mut rng := pcg32.PCG32RNG{} + rng.seed(seed) + mut values := []u64{cap: value_count} + for i in 0 .. value_count { + value := rng.u64() + assert !found(value, values) + assert values.len == i + values << value + } + } +} + +fn check_uniformity_u64(mut rng pcg32.PCG32RNG, range u64) { + range_f64 := f64(range) + expected_mean := range_f64 / 2.0 + mut variance := 0.0 + for _ in 0 .. sample_size { + diff := f64(rng.u64n(range)) - expected_mean + variance += diff * diff + } + variance /= sample_size - 1 + sigma := math.sqrt(variance) + expected_sigma := range_f64 * inv_sqrt_12 + error := (sigma - expected_sigma) / expected_sigma + assert math.abs(error) < stats_epsilon +} + +fn test_pcg32_uniformity_u64() { + ranges := [14019545, 80240, 130] + for seed in seeds { + mut rng := pcg32.PCG32RNG{} + rng.seed(seed) + for range in ranges { + check_uniformity_u64(mut rng, u64(range)) + } + } +} + +fn check_uniformity_f64(mut rng pcg32.PCG32RNG) { + expected_mean := 0.5 + mut variance := 0.0 + for _ in 0 .. sample_size { + diff := rng.f64() - expected_mean + variance += diff * diff + } + variance /= sample_size - 1 + sigma := math.sqrt(variance) + expected_sigma := inv_sqrt_12 + error := (sigma - expected_sigma) / expected_sigma + assert math.abs(error) < stats_epsilon +} + +fn test_pcg32_uniformity_f64() { + // The f64 version + for seed in seeds { + mut rng := pcg32.PCG32RNG{} + rng.seed(seed) + check_uniformity_f64(mut rng) + } +} + +fn test_pcg32_u32n() { + max := u32(16384) + for seed in seeds { + mut rng := pcg32.PCG32RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.u32n(max) + assert value >= 0 + assert value < max + } + } +} + +fn test_pcg32_u64n() { + max := u64(379091181005) + for seed in seeds { + mut rng := pcg32.PCG32RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.u64n(max) + assert value >= 0 + assert value < max + } + } +} + +fn test_pcg32_u32_in_range() { + max := u32(484468466) + min := u32(316846) + for seed in seeds { + mut rng := pcg32.PCG32RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.u32_in_range(u32(min), u32(max)) + assert value >= min + assert value < max + } + } +} + +fn test_pcg32_u64_in_range() { + max := u64(216468454685163) + min := u64(6848646868) + for seed in seeds { + mut rng := pcg32.PCG32RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.u64_in_range(min, max) + assert value >= min + assert value < max + } + } +} + +fn test_pcg32_int31() { + max_u31 := int(0x7FFFFFFF) + sign_mask := int(0x80000000) + for seed in seeds { + mut rng := pcg32.PCG32RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.int31() + assert value >= 0 + assert value <= max_u31 + // This statement ensures that the sign bit is zero + assert (value & sign_mask) == 0 + } + } +} + +fn test_pcg32_int63() { + max_u63 := i64(0x7FFFFFFFFFFFFFFF) + sign_mask := i64(0x8000000000000000) + for seed in seeds { + mut rng := pcg32.PCG32RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.int63() + assert value >= 0 + assert value <= max_u63 + assert (value & sign_mask) == 0 + } + } +} + +fn test_pcg32_intn() { + max := 2525642 + for seed in seeds { + mut rng := pcg32.PCG32RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.intn(max) + assert value >= 0 + assert value < max + } + } +} + +fn test_pcg32_i64n() { + max := i64(3246727724653636) + for seed in seeds { + mut rng := pcg32.PCG32RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.i64n(max) + assert value >= 0 + assert value < max + } + } +} + +fn test_pcg32_int_in_range() { + min := -4252 + max := 1034 + for seed in seeds { + mut rng := pcg32.PCG32RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.int_in_range(min, max) + assert value >= min + assert value < max + } + } +} + +fn test_pcg32_i64_in_range() { + min := i64(-24095) + max := i64(324058) + for seed in seeds { + mut rng := pcg32.PCG32RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.i64_in_range(min, max) + assert value >= min + assert value < max + } + } +} + +fn test_pcg32_f32() { + for seed in seeds { + mut rng := pcg32.PCG32RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.f32() + assert value >= 0.0 + assert value < 1.0 + } + } +} + +fn test_pcg32_f64() { + for seed in seeds { + mut rng := pcg32.PCG32RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.f64() + assert value >= 0.0 + assert value < 1.0 + } + } +} + +fn test_pcg32_f32n() { + max := f32(357.0) + for seed in seeds { + mut rng := pcg32.PCG32RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.f32n(max) + assert value >= 0.0 + assert value < max + } + } +} + +fn test_pcg32_f64n() { + max := 1.52e6 + for seed in seeds { + mut rng := pcg32.PCG32RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.f64n(max) + assert value >= 0.0 + assert value < max + } + } +} + +fn test_pcg32_f32_in_range() { + min := f32(-24.0) + max := f32(125.0) + for seed in seeds { + mut rng := pcg32.PCG32RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.f32_in_range(min, max) + assert value >= min + assert value < max + } + } +} + +fn test_pcg32_f64_in_range() { + min := -548.7 + max := 5015.2 + for seed in seeds { + mut rng := pcg32.PCG32RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.f64_in_range(min, max) + assert value >= min + assert value < max + } + } +} + +fn test_change_default_random_generator() { + rand.set_rng(pcg32.PCG32RNG{}) +} diff --git a/v_windows/v/old/vlib/rand/rand.v b/v_windows/v/old/vlib/rand/rand.v new file mode 100644 index 0000000..0136ec1 --- /dev/null +++ b/v_windows/v/old/vlib/rand/rand.v @@ -0,0 +1,317 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module rand + +import rand.seed +import rand.wyrand +import time + +// PRNGConfigStruct is a configuration struct for creating a new instance of the default RNG. +// Note that the RNGs may have a different number of u32s required for seeding. The default +// generator WyRand used 64 bits, ie. 2 u32s so that is the default. In case your desired generator +// uses a different number of u32s, use the `seed.time_seed_array()` method with the correct +// number of u32s. +pub struct PRNGConfigStruct { + seed []u32 = seed.time_seed_array(2) +} + +// PRNG is a common interface for all PRNGs that can be used seamlessly with the rand +// modules's API. It defines all the methods that a PRNG (in the vlib or custom made) must +// implement in order to ensure that _all_ functions can be used with the generator. +pub interface PRNG { + seed(seed_data []u32) + u32() u32 + u64() u64 + u32n(max u32) u32 + u64n(max u64) u64 + u32_in_range(min u32, max u32) u32 + u64_in_range(min u64, max u64) u64 + int() int + i64() i64 + int31() int + int63() i64 + intn(max int) int + i64n(max i64) i64 + int_in_range(min int, max int) int + i64_in_range(min i64, max i64) i64 + f32() f32 + f64() f64 + f32n(max f32) f32 + f64n(max f64) f64 + f32_in_range(min f32, max f32) f32 + f64_in_range(min f64, max f64) f64 +} + +__global ( + default_rng &PRNG +) + +// init initializes the default RNG. +fn init() { + default_rng = new_default() +} + +// new_default returns a new instance of the default RNG. If the seed is not provided, the current time will be used to seed the instance. +pub fn new_default(config PRNGConfigStruct) &PRNG { + mut rng := &wyrand.WyRandRNG{} + rng.seed(config.seed) + return rng +} + +// get_current_rng returns the PRNG instance currently in use. If it is not changed, it will be an instance of wyrand.WyRandRNG. +pub fn get_current_rng() &PRNG { + return default_rng +} + +// set_rng changes the default RNG from wyrand.WyRandRNG (or whatever the last RNG was) to the one +// provided by the user. Note that this new RNG must be seeded manually with a constant seed or the +// `seed.time_seed_array()` method. Also, it is recommended to store the old RNG in a variable and +// should be restored if work with the custom RNG is complete. It is not necessary to restore if the +// program terminates soon afterwards. +pub fn set_rng(rng &PRNG) { + default_rng = unsafe { rng } +} + +// seed sets the given array of `u32` values as the seed for the `default_rng`. The default_rng is +// an instance of WyRandRNG which takes 2 u32 values. When using a custom RNG, make sure to use +// the correct number of u32s. +pub fn seed(seed []u32) { + default_rng.seed(seed) +} + +// u32 returns a uniformly distributed `u32` in range `[0, 2³²)`. +pub fn u32() u32 { + return default_rng.u32() +} + +// u64 returns a uniformly distributed `u64` in range `[0, 2⁶⁴)`. +pub fn u64() u64 { + return default_rng.u64() +} + +// u32n returns a uniformly distributed pseudorandom 32-bit signed positive `u32` in range `[0, max)`. +pub fn u32n(max u32) u32 { + return default_rng.u32n(max) +} + +// u64n returns a uniformly distributed pseudorandom 64-bit signed positive `u64` in range `[0, max)`. +pub fn u64n(max u64) u64 { + return default_rng.u64n(max) +} + +// u32_in_range returns a uniformly distributed pseudorandom 32-bit unsigned `u32` in range `[min, max)`. +pub fn u32_in_range(min u32, max u32) u32 { + return default_rng.u32_in_range(min, max) +} + +// u64_in_range returns a uniformly distributed pseudorandom 64-bit unsigned `u64` in range `[min, max)`. +pub fn u64_in_range(min u64, max u64) u64 { + return default_rng.u64_in_range(min, max) +} + +// int returns a uniformly distributed pseudorandom 32-bit signed (possibly negative) `int`. +pub fn int() int { + return default_rng.int() +} + +// intn returns a uniformly distributed pseudorandom 32-bit signed positive `int` in range `[0, max)`. +pub fn intn(max int) int { + return default_rng.intn(max) +} + +// byte returns a uniformly distributed pseudorandom 8-bit unsigned positive `byte`. +pub fn byte() byte { + return byte(default_rng.u32() & 0xff) +} + +// int_in_range returns a uniformly distributed pseudorandom 32-bit signed int in range `[min, max)`. +// Both `min` and `max` can be negative, but we must have `min < max`. +pub fn int_in_range(min int, max int) int { + return default_rng.int_in_range(min, max) +} + +// int31 returns a uniformly distributed pseudorandom 31-bit signed positive `int`. +pub fn int31() int { + return default_rng.int31() +} + +// i64 returns a uniformly distributed pseudorandom 64-bit signed (possibly negative) `i64`. +pub fn i64() i64 { + return default_rng.i64() +} + +// i64n returns a uniformly distributed pseudorandom 64-bit signed positive `i64` in range `[0, max)`. +pub fn i64n(max i64) i64 { + return default_rng.i64n(max) +} + +// i64_in_range returns a uniformly distributed pseudorandom 64-bit signed `i64` in range `[min, max)`. +pub fn i64_in_range(min i64, max i64) i64 { + return default_rng.i64_in_range(min, max) +} + +// int63 returns a uniformly distributed pseudorandom 63-bit signed positive `i64`. +pub fn int63() i64 { + return default_rng.int63() +} + +// f32 returns a uniformly distributed 32-bit floating point in range `[0, 1)`. +pub fn f32() f32 { + return default_rng.f32() +} + +// f64 returns a uniformly distributed 64-bit floating point in range `[0, 1)`. +pub fn f64() f64 { + return default_rng.f64() +} + +// f32n returns a uniformly distributed 32-bit floating point in range `[0, max)`. +pub fn f32n(max f32) f32 { + return default_rng.f32n(max) +} + +// f64n returns a uniformly distributed 64-bit floating point in range `[0, max)`. +pub fn f64n(max f64) f64 { + return default_rng.f64n(max) +} + +// f32_in_range returns a uniformly distributed 32-bit floating point in range `[min, max)`. +pub fn f32_in_range(min f32, max f32) f32 { + return default_rng.f32_in_range(min, max) +} + +// f64_in_range returns a uniformly distributed 64-bit floating point in range `[min, max)`. +pub fn f64_in_range(min f64, max f64) f64 { + return default_rng.f64_in_range(min, max) +} + +const ( + english_letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' + hex_chars = 'abcdef0123456789' + ascii_chars = '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ\\^_`abcdefghijklmnopqrstuvwxyz{|}~' +) + +// string_from_set returns a string of length `len` containing random characters sampled from the given `charset` +pub fn string_from_set(charset string, len int) string { + if len == 0 { + return '' + } + mut buf := unsafe { malloc_noscan(len + 1) } + for i in 0 .. len { + unsafe { + buf[i] = charset[intn(charset.len)] + } + } + unsafe { + buf[len] = 0 + } + return unsafe { buf.vstring_with_len(len) } +} + +// string returns a string of length `len` containing random characters in range `[a-zA-Z]`. +pub fn string(len int) string { + return string_from_set(rand.english_letters, len) +} + +// hex returns a hexadecimal number of length `len` containing random characters in range `[a-f0-9]`. +pub fn hex(len int) string { + return string_from_set(rand.hex_chars, len) +} + +// ascii returns a random string of the printable ASCII characters with length `len`. +pub fn ascii(len int) string { + return string_from_set(rand.ascii_chars, len) +} + +// uuid_v4 generates a random (v4) UUID +// See https://en.wikipedia.org/wiki/Universally_unique_identifier#Version_4_(random) +pub fn uuid_v4() string { + buflen := 36 + mut buf := unsafe { malloc_noscan(37) } + mut i_buf := 0 + mut x := u64(0) + mut d := byte(0) + for i_buf < buflen { + mut c := 0 + x = default_rng.u64() + // do most of the bit manipulation at once: + x &= 0x0F0F0F0F0F0F0F0F + x += 0x3030303030303030 + // write the ASCII codes to the buffer: + for c < 8 && i_buf < buflen { + d = byte(x) + unsafe { + buf[i_buf] = if d > 0x39 { d + 0x27 } else { d } + } + i_buf++ + c++ + x = x >> 8 + } + } + // there are still some random bits in x: + x = x >> 8 + d = byte(x) + unsafe { + buf[19] = if d > 0x39 { d + 0x27 } else { d } + buf[8] = `-` + buf[13] = `-` + buf[18] = `-` + buf[23] = `-` + buf[14] = `4` + buf[buflen] = 0 + return buf.vstring_with_len(buflen) + } +} + +const ( + ulid_encoding = '0123456789ABCDEFGHJKMNPQRSTVWXYZ' +) + +// ulid generates an Unique Lexicographically sortable IDentifier. +// See https://github.com/ulid/spec . +// NB: ULIDs can leak timing information, if you make them public, because +// you can infer the rate at which some resource is being created, like +// users or business transactions. +// (https://news.ycombinator.com/item?id=14526173) +pub fn ulid() string { + return ulid_at_millisecond(time.utc().unix_time_milli()) +} + +// ulid_at_millisecond does the same as `ulid` but takes a custom Unix millisecond timestamp via `unix_time_milli`. +pub fn ulid_at_millisecond(unix_time_milli u64) string { + buflen := 26 + mut buf := unsafe { malloc_noscan(27) } + mut t := unix_time_milli + mut i := 9 + for i >= 0 { + unsafe { + buf[i] = rand.ulid_encoding[t & 0x1F] + } + t = t >> 5 + i-- + } + // first rand set + mut x := default_rng.u64() + i = 10 + for i < 19 { + unsafe { + buf[i] = rand.ulid_encoding[x & 0x1F] + } + x = x >> 5 + i++ + } + // second rand set + x = default_rng.u64() + for i < 26 { + unsafe { + buf[i] = rand.ulid_encoding[x & 0x1F] + } + x = x >> 5 + i++ + } + unsafe { + buf[26] = 0 + return buf.vstring_with_len(buflen) + } +} diff --git a/v_windows/v/old/vlib/rand/random_identifiers_test.v b/v_windows/v/old/vlib/rand/random_identifiers_test.v new file mode 100644 index 0000000..3daee4d --- /dev/null +++ b/v_windows/v/old/vlib/rand/random_identifiers_test.v @@ -0,0 +1,70 @@ +import time +import rand + +// uuid_v4: +fn test_rand_uuid_v4() { + uuid1 := rand.uuid_v4() + uuid2 := rand.uuid_v4() + uuid3 := rand.uuid_v4() + assert uuid1 != uuid2 + assert uuid1 != uuid3 + assert uuid2 != uuid3 + assert uuid1.len == 36 + assert uuid2.len == 36 + assert uuid3.len == 36 + assert uuid1[14] == `4` + assert uuid2[14] == `4` + assert uuid3[14] == `4` +} + +// ulids: +fn test_ulids_are_unique() { + ulid1 := rand.ulid() + ulid2 := rand.ulid() + ulid3 := rand.ulid() + assert ulid1.len == 26 + assert ulid2.len == 26 + assert ulid3.len == 26 + assert ulid1 != ulid2 + assert ulid1 != ulid3 + assert ulid2 != ulid3 +} + +fn test_ulids_max_start_character_is_ok() { + ulid1 := rand.ulid() + // the largest valid ULID encoded in Base32 is 7ZZZZZZZZZZZZZZZZZZZZZZZZZ + assert (int(ulid1[0]) - 48) <= 7 +} + +fn test_ulids_generated_in_the_same_millisecond_have_the_same_prefix() { + t := time.utc().unix_time_milli() + mut ulid1 := '' + mut ulid2 := '' + mut ulid3 := '' + ulid1 = rand.ulid_at_millisecond(t) + ulid2 = rand.ulid_at_millisecond(t) + ulid3 = rand.ulid_at_millisecond(t) + ulid1_prefix := ulid1[0..10] + ulid2_prefix := ulid2[0..10] + ulid3_prefix := ulid3[0..10] + assert ulid1_prefix == ulid2_prefix + assert ulid1_prefix == ulid3_prefix +} + +fn test_ulids_should_be_lexicographically_ordered_when_not_in_same_millisecond() { + ulid1 := rand.ulid() + time.sleep(1 * time.millisecond) + ulid2 := rand.ulid() + time.sleep(1 * time.millisecond) + ulid3 := rand.ulid() + mut all := [ulid3, ulid2, ulid1] + // eprintln('all before: $all') + all.sort() + // eprintln('all after: $all') + s1 := all[0] + s2 := all[1] + s3 := all[2] + assert s1 == ulid1 + assert s2 == ulid2 + assert s3 == ulid3 +} diff --git a/v_windows/v/old/vlib/rand/random_numbers_test.v b/v_windows/v/old/vlib/rand/random_numbers_test.v new file mode 100644 index 0000000..f09fa3c --- /dev/null +++ b/v_windows/v/old/vlib/rand/random_numbers_test.v @@ -0,0 +1,319 @@ +import rand +import rand.splitmix64 +import rand.musl +import rand.mt19937 + +const ( + rnd_count = 40 + seeds = [[u32(42), 0], [u32(256), 0]] +) + +fn get_n_random_ints(seed_data []u32, n int) []int { + mut values := []int{cap: n} + rand.seed(seed_data) + for _ in 0 .. n { + values << rand.intn(n) + } + return values +} + +fn test_rand_reproducibility() { + for seed in seeds { + array1 := get_n_random_ints(seed, 1000) + array2 := get_n_random_ints(seed, 1000) + assert array1.len == array2.len + assert array1 == array2 + } +} + +fn test_rand_u32n() { + max := u32(16384) + for _ in 0 .. rnd_count { + value := rand.u32n(max) + assert value >= 0 + assert value < max + } +} + +fn test_rand_u64n() { + max := u64(379091181005) + for _ in 0 .. rnd_count { + value := rand.u64n(max) + assert value >= 0 + assert value < max + } +} + +fn test_rand_u32_in_range() { + max := u32(484468466) + min := u32(316846) + for _ in 0 .. rnd_count { + value := rand.u32_in_range(min, max) + assert value >= min + assert value < max + } +} + +fn test_rand_u64_in_range() { + max := u64(216468454685163) + min := u64(6848646868) + for _ in 0 .. rnd_count { + value := rand.u64_in_range(min, max) + assert value >= min + assert value < max + } +} + +fn test_rand_intn() { + max := 2525642 + for _ in 0 .. rnd_count { + value := rand.intn(max) + assert value >= 0 + assert value < max + } +} + +fn test_rand_i64n() { + max := i64(3246727724653636) + for _ in 0 .. rnd_count { + value := rand.i64n(max) + assert value >= 0 + assert value < max + } +} + +fn test_rand_int_in_range() { + min := -4252 + max := 23054962 + for _ in 0 .. rnd_count { + value := rand.int_in_range(min, max) + assert value >= min + assert value < max + } +} + +fn test_rand_i64_in_range() { + min := i64(-24095) + max := i64(324058) + for _ in 0 .. rnd_count { + value := rand.i64_in_range(min, max) + assert value >= min + assert value < max + } +} + +fn test_rand_int31() { + max_u31 := int(0x7FFFFFFF) + sign_mask := int(0x80000000) + for _ in 0 .. rnd_count { + value := rand.int31() + assert value >= 0 + assert value <= max_u31 + // This statement ensures that the sign bit is zero + assert (value & sign_mask) == 0 + } +} + +fn test_rand_int63() { + max_u63 := i64(0x7FFFFFFFFFFFFFFF) + sign_mask := i64(0x8000000000000000) + for _ in 0 .. rnd_count { + value := rand.int63() + assert value >= 0 + assert value <= max_u63 + assert (value & sign_mask) == 0 + } +} + +fn test_rand_f32() { + for _ in 0 .. rnd_count { + value := rand.f32() + assert value >= 0.0 + assert value < 1.0 + } +} + +fn test_rand_f64() { + for _ in 0 .. rnd_count { + value := rand.f64() + assert value >= 0.0 + assert value < 1.0 + } +} + +fn test_rand_f32n() { + max := f32(357.0) + for _ in 0 .. rnd_count { + value := rand.f32n(max) + assert value >= 0.0 + assert value < max + } +} + +fn test_rand_f64n() { + max := f64(1.52e6) + for _ in 0 .. rnd_count { + value := rand.f64n(max) + assert value >= 0.0 + assert value < max + } +} + +fn test_rand_f32_in_range() { + min := f32(-24.0) + max := f32(125.0) + for _ in 0 .. rnd_count { + value := rand.f32_in_range(min, max) + assert value >= min + assert value < max + } +} + +fn test_rand_f64_in_range() { + min := f64(-548.7) + max := f64(5015.2) + for _ in 0 .. rnd_count { + value := rand.f64_in_range(min, max) + assert value >= min + assert value < max + } +} + +fn test_rand_byte() { + mut all := []byte{} + for _ in 0 .. 256 { + x := rand.byte() + assert x >= 0 + assert x <= 255 + all << x + } + all.sort(a < b) + assert all[0] != all[255] + assert all[0] != all[128] +} + +const ( + string_count = 25 +) + +fn test_rand_string_from_set() { + sets := [ + '0123456789', + 'qwertyuiop', + 'abcdefghijklmnopqrstuvwxyz', + ] + for charset in sets { + for _ in 0 .. string_count { + len := rand.intn(rnd_count) + str := rand.string_from_set(charset, len) + assert str.len == len + for character in str { + position := charset.index(character.ascii_str()) or { -1 } + assert position > -1 + } + } + } +} + +fn test_rand_string() { + rand.seed([u32(0), 1]) + outputs := [ + 'rzJfVBJgvAyCNpEdXIteDQezg', + 'AJOeswgoelDOCfcrSUWzVPjeL', + 'NQfKauQqsXYXSUMFPGnXXPJIn', + 'vfBGUKbpLoBMQVYXfkvRplWih', + 'aYHLjMJqvUJmJJHGxEnrEmQGl', + 'rBJXkQZcembAteaRFoxXmECJo', + 'HYVLfHmDOCTlSbiSzHrsAIaBH', + 'zgOiwyISjLSdLGhLzJsSKHVBi', + 'UiAtobWXGcHsEtgzuNatxfkoI', + 'NisnYlffJgFEcIdcgzWcGjnHy', + ] + for output in outputs { + assert rand.string(25) == output + } +} + +fn test_rand_hex() { + rand.seed([u32(0), 1]) + outputs := [ + 'fc30e495deee09e008e15ffc3', + '4320efa837788397fb59b28f4', + '4995210abf33b6765c240ce62', + 'f3d20dbe0a8aa6b9c88cd1f6f', + '8d7d58b256ab00213dd519cf7', + 'fa2251284bc20a21eff48127c', + '5fef90cdc0c37143117599092', + '2a6170531c76dfb50c54126bc', + 'a686dfd536042d1c1a9afdaf4', + '7f12013f6e1177e2d63726de3', + ] + for output in outputs { + assert rand.hex(25) == output + } +} + +fn test_rand_ascii() { + rand.seed([u32(0), 1]) + outputs := [ + "2Z:&PeD'V;9=mn\$C>yKg'DIr%", + 'Ub7ix,}>I=QJki{%FHKv&K', + '1WStRylMO|p.R~qqRtr&AOEsd', + 'yka> 32 ^ (ctime & 0x0000_0000_FFFF_FFFF)) + mut seed_data := []u32{cap: count} + for _ in 0 .. count { + seed = nr_next(seed) + seed_data << nr_next(seed) + } + return seed_data +} + +// time_seed_32 returns a 32-bit seed generated from system time. +[inline] +pub fn time_seed_32() u32 { + return time_seed_array(1)[0] +} + +// time_seed_64 returns a 64-bit seed generated from system time. +[inline] +pub fn time_seed_64() u64 { + seed_data := time_seed_array(2) + lower := u64(seed_data[0]) + upper := u64(seed_data[1]) + return lower | (upper << 32) +} diff --git a/v_windows/v/old/vlib/rand/splitmix64/splitmix64.v b/v_windows/v/old/vlib/rand/splitmix64/splitmix64.v new file mode 100644 index 0000000..d3cb9d1 --- /dev/null +++ b/v_windows/v/old/vlib/rand/splitmix64/splitmix64.v @@ -0,0 +1,225 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module splitmix64 + +import rand.seed +import rand.constants + +// SplitMix64RNG ported from http://xoshiro.di.unimi.it/splitmix64.c +pub struct SplitMix64RNG { +mut: + state u64 = seed.time_seed_64() + has_extra bool + extra u32 +} + +// seed sets the seed of the accepting SplitMix64RNG to the given data +// in little-endian format (i.e. lower 32 bits are in [0] and higher 32 bits in [1]). +pub fn (mut rng SplitMix64RNG) seed(seed_data []u32) { + if seed_data.len != 2 { + eprintln('SplitMix64RNG needs 2 32-bit unsigned integers as the seed.') + exit(1) + } + rng.state = seed_data[0] | (u64(seed_data[1]) << 32) + rng.has_extra = false +} + +// u32 updates the PRNG state and returns the next pseudorandom `u32`. +[inline] +pub fn (mut rng SplitMix64RNG) u32() u32 { + if rng.has_extra { + rng.has_extra = false + return rng.extra + } + full_value := rng.u64() + lower := u32(full_value & constants.lower_mask) + upper := u32(full_value >> 32) + rng.extra = upper + rng.has_extra = true + return lower +} + +// u64 updates the PRNG state and returns the next pseudorandom `u64`. +[inline] +pub fn (mut rng SplitMix64RNG) u64() u64 { + rng.state += (0x9e3779b97f4a7c15) + mut z := rng.state + z = (z ^ ((z >> u64(30)))) * (0xbf58476d1ce4e5b9) + z = (z ^ ((z >> u64(27)))) * (0x94d049bb133111eb) + return z ^ (z >> (31)) +} + +// u32n returns a pseudorandom `u32` less than `bound`. +[inline] +pub fn (mut rng SplitMix64RNG) u32n(bound u32) u32 { + // This function is kept similar to the u64 version + if bound == 0 { + eprintln('max must be non-zero') + exit(1) + } + threshold := -bound % bound + for { + r := rng.u32() + if r >= threshold { + return r % bound + } + } + return u32(0) +} + +// u64n returns a pseudorandom `u64` less than `bound`. +[inline] +pub fn (mut rng SplitMix64RNG) u64n(bound u64) u64 { + // See pcg32.v for explanation of comment. This algorithm + // existed before the refactoring. + if bound == 0 { + eprintln('max must be non-zero') + exit(1) + } + threshold := -bound % bound + for { + r := rng.u64() + if r >= threshold { + return r % bound + } + } + return u64(0) +} + +// u32n returns a pseudorandom `u32` value that is guaranteed to be in range `[min, max)`. +[inline] +pub fn (mut rng SplitMix64RNG) u32_in_range(min u32, max u32) u32 { + if max <= min { + eprintln('max must be greater than min') + exit(1) + } + return min + rng.u32n(max - min) +} + +// u64n returns a pseudorandom `u64` value that is guaranteed to be in range `[min, max)`. +[inline] +pub fn (mut rng SplitMix64RNG) u64_in_range(min u64, max u64) u64 { + if max <= min { + eprintln('max must be greater than min') + exit(1) + } + return min + rng.u64n(max - min) +} + +// int returns a pseudorandom 32-bit (possibly negative) `int`. +[inline] +pub fn (mut rng SplitMix64RNG) int() int { + return int(rng.u32()) +} + +// i64 returns a pseudorandom 64-bit (possibly negative) `i64`. +[inline] +pub fn (mut rng SplitMix64RNG) i64() i64 { + return i64(rng.u64()) +} + +// int31 returns a positive pseudorandom 31-bit `int`. +[inline] +pub fn (mut rng SplitMix64RNG) int31() int { + return int(rng.u32() & constants.u31_mask) // Set the 32nd bit to 0. +} + +// int63 returns a positive pseudorandom 63-bit `i64`. +[inline] +pub fn (mut rng SplitMix64RNG) int63() i64 { + return i64(rng.u64() & constants.u63_mask) // Set the 64th bit to 0. +} + +// intn returns a pseudorandom `int` in range `[0, max)`. +[inline] +pub fn (mut rng SplitMix64RNG) intn(max int) int { + if max <= 0 { + eprintln('max has to be positive.') + exit(1) + } + return int(rng.u32n(u32(max))) +} + +// i64n returns a pseudorandom `i64` in range `[0, max)`. +[inline] +pub fn (mut rng SplitMix64RNG) i64n(max i64) i64 { + if max <= 0 { + eprintln('max has to be positive.') + exit(1) + } + return i64(rng.u64n(u64(max))) +} + +// int_in_range returns a pseudorandom `int` in range `[min, max)`. +[inline] +pub fn (mut rng SplitMix64RNG) int_in_range(min int, max int) int { + if max <= min { + eprintln('max must be greater than min') + exit(1) + } + // This supports negative ranges like [-10, -5) because the difference is positive + return min + rng.intn(max - min) +} + +// i64_in_range returns a pseudorandom `i64` in range `[min, max)`. +[inline] +pub fn (mut rng SplitMix64RNG) i64_in_range(min i64, max i64) i64 { + if max <= min { + eprintln('max must be greater than min') + exit(1) + } + return min + rng.i64n(max - min) +} + +// f32 returns a pseudorandom `f32` value in range `[0, 1)`. +[inline] +pub fn (mut rng SplitMix64RNG) f32() f32 { + return f32(rng.u32()) / constants.max_u32_as_f32 +} + +// f64 returns a pseudorandom `f64` value in range `[0, 1)`. +[inline] +pub fn (mut rng SplitMix64RNG) f64() f64 { + return f64(rng.u64()) / constants.max_u64_as_f64 +} + +// f32n returns a pseudorandom `f32` value in range `[0, max)`. +[inline] +pub fn (mut rng SplitMix64RNG) f32n(max f32) f32 { + if max <= 0 { + eprintln('max has to be positive.') + exit(1) + } + return rng.f32() * max +} + +// f64n returns a pseudorandom `f64` value in range `[0, max)`. +[inline] +pub fn (mut rng SplitMix64RNG) f64n(max f64) f64 { + if max <= 0 { + eprintln('max has to be positive.') + exit(1) + } + return rng.f64() * max +} + +// f32_in_range returns a pseudorandom `f32` in range `[min, max)`. +[inline] +pub fn (mut rng SplitMix64RNG) f32_in_range(min f32, max f32) f32 { + if max <= min { + eprintln('max must be greater than min') + exit(1) + } + return min + rng.f32n(max - min) +} + +// i64_in_range returns a pseudorandom `i64` in range `[min, max)`. +[inline] +pub fn (mut rng SplitMix64RNG) f64_in_range(min f64, max f64) f64 { + if max <= min { + eprintln('max must be greater than min') + exit(1) + } + return min + rng.f64n(max - min) +} diff --git a/v_windows/v/old/vlib/rand/splitmix64/splitmix64_test.v b/v_windows/v/old/vlib/rand/splitmix64/splitmix64_test.v new file mode 100644 index 0000000..669da3c --- /dev/null +++ b/v_windows/v/old/vlib/rand/splitmix64/splitmix64_test.v @@ -0,0 +1,331 @@ +import math +import rand.splitmix64 +import rand.seed + +const ( + range_limit = 40 + value_count = 1000 + seeds = [[u32(42), 0], [u32(256), 0]] +) + +const ( + sample_size = 1000 + stats_epsilon = 0.05 + inv_sqrt_12 = 1.0 / math.sqrt(12) +) + +fn gen_randoms(seed_data []u32, bound int) []u64 { + bound_u64 := u64(bound) + mut randoms := []u64{len: (20)} + mut rnd := splitmix64.SplitMix64RNG{} + rnd.seed(seed_data) + for i in 0 .. 20 { + randoms[i] = rnd.u64n(bound_u64) + } + return randoms +} + +fn test_splitmix64_reproducibility() { + seed_data := seed.time_seed_array(2) + randoms1 := gen_randoms(seed_data, 1000) + randoms2 := gen_randoms(seed_data, 1000) + assert randoms1.len == randoms2.len + len := randoms1.len + for i in 0 .. len { + assert randoms1[i] == randoms2[i] + } +} + +// TODO: use the `in` syntax and remove this function +// after generics has been completely implemented +fn found(value u64, arr []u64) bool { + for item in arr { + if value == item { + return true + } + } + return false +} + +fn test_splitmix64_variability() { + // If this test fails and if it is certainly not the implementation + // at fault, try changing the seed values. Repeated values are + // improbable but not impossible. + for seed in seeds { + mut rng := splitmix64.SplitMix64RNG{} + rng.seed(seed) + mut values := []u64{cap: value_count} + for i in 0 .. value_count { + value := rng.u64() + assert !found(value, values) + assert values.len == i + values << value + } + } +} + +fn check_uniformity_u64(mut rng splitmix64.SplitMix64RNG, range u64) { + range_f64 := f64(range) + expected_mean := range_f64 / 2.0 + mut variance := 0.0 + for _ in 0 .. sample_size { + diff := f64(rng.u64n(range)) - expected_mean + variance += diff * diff + } + variance /= sample_size - 1 + sigma := math.sqrt(variance) + expected_sigma := range_f64 * inv_sqrt_12 + error := (sigma - expected_sigma) / expected_sigma + assert math.abs(error) < stats_epsilon +} + +fn test_splitmix64_uniformity_u64() { + ranges := [14019545, 80240, 130] + for seed in seeds { + mut rng := splitmix64.SplitMix64RNG{} + rng.seed(seed) + for range in ranges { + check_uniformity_u64(mut rng, u64(range)) + } + } +} + +fn check_uniformity_f64(mut rng splitmix64.SplitMix64RNG) { + expected_mean := 0.5 + mut variance := 0.0 + for _ in 0 .. sample_size { + diff := rng.f64() - expected_mean + variance += diff * diff + } + variance /= sample_size - 1 + sigma := math.sqrt(variance) + expected_sigma := inv_sqrt_12 + error := (sigma - expected_sigma) / expected_sigma + assert math.abs(error) < stats_epsilon +} + +fn test_splitmix64_uniformity_f64() { + // The f64 version + for seed in seeds { + mut rng := splitmix64.SplitMix64RNG{} + rng.seed(seed) + check_uniformity_f64(mut rng) + } +} + +fn test_splitmix64_u32n() { + max := u32(16384) + for seed in seeds { + mut rng := splitmix64.SplitMix64RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.u32n(max) + assert value >= 0 + assert value < max + } + } +} + +fn test_splitmix64_u64n() { + max := u64(379091181005) + for seed in seeds { + mut rng := splitmix64.SplitMix64RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.u64n(max) + assert value >= 0 + assert value < max + } + } +} + +fn test_splitmix64_u32_in_range() { + max := u32(484468466) + min := u32(316846) + for seed in seeds { + mut rng := splitmix64.SplitMix64RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.u32_in_range(min, max) + assert value >= min + assert value < max + } + } +} + +fn test_splitmix64_u64_in_range() { + max := u64(216468454685163) + min := u64(6848646868) + for seed in seeds { + mut rng := splitmix64.SplitMix64RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.u64_in_range(min, max) + assert value >= min + assert value < max + } + } +} + +fn test_splitmix64_int31() { + max_u31 := int(0x7FFFFFFF) + sign_mask := int(0x80000000) + for seed in seeds { + mut rng := splitmix64.SplitMix64RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.int31() + assert value >= 0 + assert value <= max_u31 + // This statement ensures that the sign bit is zero + assert (value & sign_mask) == 0 + } + } +} + +fn test_splitmix64_int63() { + max_u63 := i64(0x7FFFFFFFFFFFFFFF) + sign_mask := i64(0x8000000000000000) + for seed in seeds { + mut rng := splitmix64.SplitMix64RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.int63() + assert value >= 0 + assert value <= max_u63 + assert (value & sign_mask) == 0 + } + } +} + +fn test_splitmix64_intn() { + max := 2525642 + for seed in seeds { + mut rng := splitmix64.SplitMix64RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.intn(max) + assert value >= 0 + assert value < max + } + } +} + +fn test_splitmix64_i64n() { + max := i64(3246727724653636) + for seed in seeds { + mut rng := splitmix64.SplitMix64RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.i64n(max) + assert value >= 0 + assert value < max + } + } +} + +fn test_splitmix64_int_in_range() { + min := -4252 + max := 230549862 + for seed in seeds { + mut rng := splitmix64.SplitMix64RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.int_in_range(min, max) + assert value >= min + assert value < max + } + } +} + +fn test_splitmix64_i64_in_range() { + min := i64(-24095) + max := i64(324058) + for seed in seeds { + mut rng := splitmix64.SplitMix64RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.i64_in_range(min, max) + assert value >= min + assert value < max + } + } +} + +fn test_splitmix64_f32() { + for seed in seeds { + mut rng := splitmix64.SplitMix64RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.f32() + assert value >= 0.0 + assert value < 1.0 + } + } +} + +fn test_splitmix64_f64() { + for seed in seeds { + mut rng := splitmix64.SplitMix64RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.f64() + assert value >= 0.0 + assert value < 1.0 + } + } +} + +fn test_splitmix64_f32n() { + max := f32(357.0) + for seed in seeds { + mut rng := splitmix64.SplitMix64RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.f32n(max) + assert value >= 0.0 + assert value < max + } + } +} + +fn test_splitmix64_f64n() { + max := 1.52e6 + for seed in seeds { + mut rng := splitmix64.SplitMix64RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.f64n(max) + assert value >= 0.0 + assert value < max + } + } +} + +fn test_splitmix64_f32_in_range() { + min := f32(-24.0) + max := f32(125.0) + for seed in seeds { + mut rng := splitmix64.SplitMix64RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.f32_in_range(min, max) + assert value >= min + assert value < max + } + } +} + +fn test_splitmix64_f64_in_range() { + min := -548.7 + max := 5015.2 + for seed in seeds { + mut rng := splitmix64.SplitMix64RNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.f64_in_range(min, max) + assert value >= min + assert value < max + } + } +} diff --git a/v_windows/v/old/vlib/rand/sys/system_rng.c.v b/v_windows/v/old/vlib/rand/sys/system_rng.c.v new file mode 100644 index 0000000..f1c701d --- /dev/null +++ b/v_windows/v/old/vlib/rand/sys/system_rng.c.v @@ -0,0 +1,275 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module sys + +import math.bits +import rand.seed +import rand.constants + +// Implementation note: +// ==================== +// C.rand returns a pseudorandom integer from 0 (inclusive) to C.RAND_MAX (exclusive) +// C.rand() is okay to use within its defined range. +// (See: https://web.archive.org/web/20180801210127/http://eternallyconfuzzled.com/arts/jsw_art_rand.aspx) +// The problem is, this value varies with the libc implementation. On windows, +// for example, RAND_MAX is usually a measly 32767, whereas on (newer) linux it's generally +// 2147483647. The repetition period also varies wildly. In order to provide more entropy +// without altering the underlying algorithm too much, this implementation simply +// requests for more random bits until the necessary width for the integers is achieved. +const ( + rand_limit = u64(C.RAND_MAX) + rand_bitsize = bits.len_64(rand_limit) + u32_iter_count = calculate_iterations_for(32) + u64_iter_count = calculate_iterations_for(64) +) + +fn calculate_iterations_for(bits int) int { + base := bits / sys.rand_bitsize + extra := if bits % sys.rand_bitsize == 0 { 0 } else { 1 } + return base + extra +} + +// SysRNG is the PRNG provided by default in the libc implementiation that V uses. +pub struct SysRNG { +mut: + seed u32 = seed.time_seed_32() +} + +// r.seed() sets the seed of the accepting SysRNG to the given data. +pub fn (mut r SysRNG) seed(seed_data []u32) { + if seed_data.len != 1 { + eprintln('SysRNG needs one 32-bit unsigned integer as the seed.') + exit(1) + } + r.seed = seed_data[0] + C.srand(r.seed) +} + +// r.default_rand() exposes the default behavior of the system's RNG +// (equivalent to calling C.rand()). Recommended for testing/comparison +// b/w V and other languages using libc and not for regular use. +// This is also a one-off feature of SysRNG, similar to the global seed +// situation. Other generators will not have this. +[inline] +pub fn (r SysRNG) default_rand() int { + return C.rand() +} + +// r.u32() returns a pseudorandom u32 value less than 2^32 +[inline] +pub fn (r SysRNG) u32() u32 { + mut result := u32(C.rand()) + for i in 1 .. sys.u32_iter_count { + result = result ^ (u32(C.rand()) << (sys.rand_bitsize * i)) + } + return result +} + +// r.u64() returns a pseudorandom u64 value less than 2^64 +[inline] +pub fn (r SysRNG) u64() u64 { + mut result := u64(C.rand()) + for i in 1 .. sys.u64_iter_count { + result = result ^ (u64(C.rand()) << (sys.rand_bitsize * i)) + } + return result +} + +// r.u32n(max) returns a pseudorandom u32 value that is guaranteed to be less than max +[inline] +pub fn (r SysRNG) u32n(max u32) u32 { + if max == 0 { + eprintln('max must be positive integer') + exit(1) + } + // Owing to the pigeon-hole principle, we can't simply do + // val := rng.u32() % max. + // It'll wreck the properties of the distribution unless + // max evenly divides 2^32. So we divide evenly to + // the closest power of two. Then we loop until we find + // an int in the required range + bit_len := bits.len_32(max) + if bit_len == 32 { + for { + value := r.u32() + if value < max { + return value + } + } + } else { + mask := (u32(1) << (bit_len + 1)) - 1 + for { + value := r.u32() & mask + if value < max { + return value + } + } + } + return u32(0) +} + +// r.u64n(max) returns a pseudorandom u64 value that is guaranteed to be less than max +[inline] +pub fn (r SysRNG) u64n(max u64) u64 { + if max == 0 { + eprintln('max must be positive integer') + exit(1) + } + // Similar procedure for u64s + bit_len := bits.len_64(max) + if bit_len == 64 { + for { + value := r.u64() + if value < max { + return value + } + } + } else { + mask := (u64(1) << (bit_len + 1)) - 1 + for { + value := r.u64() & mask + if value < max { + return value + } + } + } + return u64(0) +} + +// r.u32n(min, max) returns a pseudorandom u32 value that is guaranteed to be in [min, max) +[inline] +pub fn (r SysRNG) u32_in_range(min u32, max u32) u32 { + if max <= min { + eprintln('max must be greater than min') + exit(1) + } + return min + r.u32n(max - min) +} + +// r.u64n(min, max) returns a pseudorandom u64 value that is guaranteed to be in [min, max) +[inline] +pub fn (r SysRNG) u64_in_range(min u64, max u64) u64 { + if max <= min { + eprintln('max must be greater than min') + exit(1) + } + return min + r.u64n(max - min) +} + +// r.int() returns a pseudorandom 32-bit int (which may be negative) +[inline] +pub fn (r SysRNG) int() int { + return int(r.u32()) +} + +// r.i64() returns a pseudorandom 64-bit i64 (which may be negative) +[inline] +pub fn (r SysRNG) i64() i64 { + return i64(r.u64()) +} + +// r.int31() returns a pseudorandom 31-bit int which is non-negative +[inline] +pub fn (r SysRNG) int31() int { + return int(r.u32() & constants.u31_mask) // Set the 32nd bit to 0. +} + +// r.int63() returns a pseudorandom 63-bit int which is non-negative +[inline] +pub fn (r SysRNG) int63() i64 { + return i64(r.u64() & constants.u63_mask) // Set the 64th bit to 0. +} + +// r.intn(max) returns a pseudorandom int that lies in [0, max) +[inline] +pub fn (r SysRNG) intn(max int) int { + if max <= 0 { + eprintln('max has to be positive.') + exit(1) + } + return int(r.u32n(u32(max))) +} + +// r.i64n(max) returns a pseudorandom i64 that lies in [0, max) +[inline] +pub fn (r SysRNG) i64n(max i64) i64 { + if max <= 0 { + eprintln('max has to be positive.') + exit(1) + } + return i64(r.u64n(u64(max))) +} + +// r.int_in_range(min, max) returns a pseudorandom int that lies in [min, max) +[inline] +pub fn (r SysRNG) int_in_range(min int, max int) int { + if max <= min { + eprintln('max must be greater than min') + exit(1) + } + // This supports negative ranges like [-10, -5) because the difference is positive + return min + r.intn(max - min) +} + +// r.i64_in_range(min, max) returns a pseudorandom i64 that lies in [min, max) +[inline] +pub fn (r SysRNG) i64_in_range(min i64, max i64) i64 { + if max <= min { + eprintln('max must be greater than min') + exit(1) + } + return min + r.i64n(max - min) +} + +// r.f32() returns a pseudorandom f32 value between 0.0 (inclusive) and 1.0 (exclusive) i.e [0, 1) +[inline] +pub fn (r SysRNG) f32() f32 { + return f32(r.u32()) / constants.max_u32_as_f32 +} + +// r.f64() returns a pseudorandom f64 value between 0.0 (inclusive) and 1.0 (exclusive) i.e [0, 1) +[inline] +pub fn (r SysRNG) f64() f64 { + return f64(r.u64()) / constants.max_u64_as_f64 +} + +// r.f32n() returns a pseudorandom f32 value in [0, max) +[inline] +pub fn (r SysRNG) f32n(max f32) f32 { + if max <= 0 { + eprintln('max has to be positive.') + exit(1) + } + return r.f32() * max +} + +// r.f64n() returns a pseudorandom f64 value in [0, max) +[inline] +pub fn (r SysRNG) f64n(max f64) f64 { + if max <= 0 { + eprintln('max has to be positive.') + exit(1) + } + return r.f64() * max +} + +// r.f32_in_range(min, max) returns a pseudorandom f32 that lies in [min, max) +[inline] +pub fn (r SysRNG) f32_in_range(min f32, max f32) f32 { + if max <= min { + eprintln('max must be greater than min') + exit(1) + } + return min + r.f32n(max - min) +} + +// r.i64_in_range(min, max) returns a pseudorandom i64 that lies in [min, max) +[inline] +pub fn (r SysRNG) f64_in_range(min f64, max f64) f64 { + if max <= min { + eprintln('max must be greater than min') + exit(1) + } + return min + r.f64n(max - min) +} diff --git a/v_windows/v/old/vlib/rand/sys/system_rng.js.v b/v_windows/v/old/vlib/rand/sys/system_rng.js.v new file mode 100644 index 0000000..496794d --- /dev/null +++ b/v_windows/v/old/vlib/rand/sys/system_rng.js.v @@ -0,0 +1,15 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module sys + +// Until there's a portable, JS has a seeded way to produce random numbers +// and not just Math.random(), use any of the existing implementations +// as the System's RNG +type SysRNG = WyRandRNG + +// In the JS version, we simply return the same int as is normally generated. +[inline] +pub fn (r SysRNG) default_rand() int { + return r.int() +} diff --git a/v_windows/v/old/vlib/rand/sys/system_rng_test.v b/v_windows/v/old/vlib/rand/sys/system_rng_test.v new file mode 100644 index 0000000..638ed10 --- /dev/null +++ b/v_windows/v/old/vlib/rand/sys/system_rng_test.v @@ -0,0 +1,354 @@ +import math +import rand.sys + +const ( + range_limit = 40 + value_count = 1000 + seeds = [u32(42), 256] +) + +const ( + sample_size = 1000 + stats_epsilon = 0.05 + inv_sqrt_12 = 1.0 / math.sqrt(12) +) + +fn get_n_randoms(n int, r sys.SysRNG) []int { + mut ints := []int{cap: n} + for _ in 0 .. n { + ints << r.int() + } + return ints +} + +fn test_sys_rng_reproducibility() { + // Note that C.srand() sets the seed globally. + // So the order of seeding matters. It is recommended + // to obtain all necessary data first, then set the + // seed for another batch of data. + for seed in seeds { + seed_data := [seed] + mut r1 := sys.SysRNG{} + mut r2 := sys.SysRNG{} + r1.seed(seed_data) + ints1 := get_n_randoms(value_count, r1) + r2.seed(seed_data) + ints2 := get_n_randoms(value_count, r2) + assert ints1 == ints2 + } +} + +// TODO: use the `in` syntax and remove this function +// after generics has been completely implemented +fn found(value u64, arr []u64) bool { + for item in arr { + if value == item { + return true + } + } + return false +} + +fn test_sys_rng_variability() { + // If this test fails and if it is certainly not the implementation + // at fault, try changing the seed values. Repeated values are + // improbable but not impossible. + for seed in seeds { + seed_data := [seed] + mut rng := sys.SysRNG{} + rng.seed(seed_data) + mut values := []u64{cap: value_count} + for i in 0 .. value_count { + value := rng.u64() + assert !found(value, values) + assert values.len == i + values << value + } + } +} + +fn check_uniformity_u64(rng sys.SysRNG, range u64) { + range_f64 := f64(range) + expected_mean := range_f64 / 2.0 + mut variance := 0.0 + for _ in 0 .. sample_size { + diff := f64(rng.u64n(range)) - expected_mean + variance += diff * diff + } + variance /= sample_size - 1 + sigma := math.sqrt(variance) + expected_sigma := range_f64 * inv_sqrt_12 + error := (sigma - expected_sigma) / expected_sigma + assert math.abs(error) < stats_epsilon +} + +fn test_sys_rng_uniformity_u64() { + // This assumes that C.rand() produces uniform results to begin with. + // If the failure persists, report an issue on GitHub + ranges := [14019545, 80240, 130] + for seed in seeds { + seed_data := [seed] + mut rng := sys.SysRNG{} + rng.seed(seed_data) + for range in ranges { + check_uniformity_u64(rng, u64(range)) + } + } +} + +fn check_uniformity_f64(rng sys.SysRNG) { + expected_mean := 0.5 + mut variance := 0.0 + for _ in 0 .. sample_size { + diff := rng.f64() - expected_mean + variance += diff * diff + } + variance /= sample_size - 1 + sigma := math.sqrt(variance) + expected_sigma := inv_sqrt_12 + error := (sigma - expected_sigma) / expected_sigma + assert math.abs(error) < stats_epsilon +} + +fn test_sys_rng_uniformity_f64() { + // The f64 version + for seed in seeds { + seed_data := [seed] + mut rng := sys.SysRNG{} + rng.seed(seed_data) + check_uniformity_f64(rng) + } +} + +fn test_sys_rng_u32n() { + max := u32(16384) + for seed in seeds { + seed_data := [seed] + mut rng := sys.SysRNG{} + rng.seed(seed_data) + for _ in 0 .. range_limit { + value := rng.u32n(max) + assert value >= 0 + assert value < max + } + } +} + +fn test_sys_rng_u64n() { + max := u64(379091181005) + for seed in seeds { + seed_data := [seed] + mut rng := sys.SysRNG{} + rng.seed(seed_data) + for _ in 0 .. range_limit { + value := rng.u64n(max) + assert value >= 0 + assert value < max + } + } +} + +fn test_sys_rng_u32_in_range() { + max := u32(484468466) + min := u32(316846) + for seed in seeds { + seed_data := [seed] + mut rng := sys.SysRNG{} + rng.seed(seed_data) + for _ in 0 .. range_limit { + value := rng.u32_in_range(min, max) + assert value >= min + assert value < max + } + } +} + +fn test_sys_rng_u64_in_range() { + max := u64(216468454685163) + min := u64(6848646868) + for seed in seeds { + seed_data := [seed] + mut rng := sys.SysRNG{} + rng.seed(seed_data) + for _ in 0 .. range_limit { + value := rng.u64_in_range(min, max) + assert value >= min + assert value < max + } + } +} + +fn test_sys_rng_intn() { + max := 2525642 + for seed in seeds { + seed_data := [seed] + mut rng := sys.SysRNG{} + rng.seed(seed_data) + for _ in 0 .. range_limit { + value := rng.intn(max) + assert value >= 0 + assert value < max + } + } +} + +fn test_sys_rng_i64n() { + max := i64(3246727724653636) + for seed in seeds { + seed_data := [seed] + mut rng := sys.SysRNG{} + rng.seed(seed_data) + for _ in 0 .. range_limit { + value := rng.i64n(max) + assert value >= 0 + assert value < max + } + } +} + +fn test_sys_rng_int_in_range() { + min := -4252 + max := 23054962 + for seed in seeds { + seed_data := [seed] + mut rng := sys.SysRNG{} + rng.seed(seed_data) + for _ in 0 .. range_limit { + value := rng.int_in_range(min, max) + assert value >= min + assert value < max + } + } +} + +fn test_sys_rng_i64_in_range() { + min := i64(-24095) + max := i64(324058) + for seed in seeds { + seed_data := [seed] + mut rng := sys.SysRNG{} + rng.seed(seed_data) + for _ in 0 .. range_limit { + value := rng.i64_in_range(min, max) + assert value >= min + assert value < max + } + } +} + +fn test_sys_rng_int31() { + max_u31 := int(0x7FFFFFFF) + sign_mask := int(0x80000000) + for seed in seeds { + seed_data := [seed] + mut rng := sys.SysRNG{} + rng.seed(seed_data) + for _ in 0 .. range_limit { + value := rng.int31() + assert value >= 0 + assert value <= max_u31 + // This statement ensures that the sign bit is zero + assert (value & sign_mask) == 0 + } + } +} + +fn test_sys_rng_int63() { + max_u63 := i64(0x7FFFFFFFFFFFFFFF) + sign_mask := i64(0x8000000000000000) + for seed in seeds { + seed_data := [seed] + mut rng := sys.SysRNG{} + rng.seed(seed_data) + for _ in 0 .. range_limit { + value := rng.int63() + assert value >= 0 + assert value <= max_u63 + assert (value & sign_mask) == 0 + } + } +} + +fn test_sys_rng_f32() { + for seed in seeds { + seed_data := [seed] + mut rng := sys.SysRNG{} + rng.seed(seed_data) + for _ in 0 .. range_limit { + value := rng.f32() + assert value >= 0.0 + assert value < 1.0 + } + } +} + +fn test_sys_rng_f64() { + for seed in seeds { + seed_data := [seed] + mut rng := sys.SysRNG{} + rng.seed(seed_data) + for _ in 0 .. range_limit { + value := rng.f64() + assert value >= 0.0 + assert value < 1.0 + } + } +} + +fn test_sys_rng_f32n() { + max := f32(357.0) + for seed in seeds { + seed_data := [seed] + mut rng := sys.SysRNG{} + rng.seed(seed_data) + for _ in 0 .. range_limit { + value := rng.f32n(max) + assert value >= 0.0 + assert value < max + } + } +} + +fn test_sys_rng_f64n() { + max := 1.52e6 + for seed in seeds { + seed_data := [seed] + mut rng := sys.SysRNG{} + rng.seed(seed_data) + for _ in 0 .. range_limit { + value := rng.f64n(max) + assert value >= 0.0 + assert value < max + } + } +} + +fn test_sys_rng_f32_in_range() { + min := f32(-24.0) + max := f32(125.0) + for seed in seeds { + seed_data := [seed] + mut rng := sys.SysRNG{} + rng.seed(seed_data) + for _ in 0 .. range_limit { + value := rng.f32_in_range(min, max) + assert value >= min + assert value < max + } + } +} + +fn test_sys_rng_f64_in_range() { + min := -548.7 + max := 5015.2 + for seed in seeds { + seed_data := [seed] + mut rng := sys.SysRNG{} + rng.seed(seed_data) + for _ in 0 .. range_limit { + value := rng.f64_in_range(min, max) + assert value >= min + assert value < max + } + } +} diff --git a/v_windows/v/old/vlib/rand/util/util.v b/v_windows/v/old/vlib/rand/util/util.v new file mode 100644 index 0000000..e1c30ca --- /dev/null +++ b/v_windows/v/old/vlib/rand/util/util.v @@ -0,0 +1,52 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module util + +import rand + +// sample_nr returns a sample of the array without replacement. This means the indices cannot repeat and it restricts the sample size to be less than or equal to the size of the given array. Note that if the array has repeating elements, then the sample may have repeats as well. +pub fn sample_nr(array []T, k int) []T { + n := array.len + if k > n { + panic('Cannot sample $k elements without replacement from a $n-element array.') + } + mut results := []T{len: k} + mut indices := []int{len: n} + // Initialize with all indices + // temporary workaround for issue #10343 + for i in 0 .. indices.len { + indices[i] = i + } + shuffle(mut indices, k) + for i in 0 .. k { + results[i] = array[indices[i]] + } + return results +} + +// sample_r returns a sample of the array with replacement. This means the elements can repeat and the size of the sample may exceed the size of the array +pub fn sample_r(array []T, k int) []T { + n := array.len + mut results := []T{len: k} + for i in 0 .. k { + results[i] = array[rand.intn(n)] + } + return results +} + +// shuffle randomizes the first `n` items of an array in place (all if `n` is 0) +[direct_array_access] +pub fn shuffle(mut a []T, n int) { + if n < 0 || n > a.len { + panic("argument 'n' must be in range [0, a.len]") + } + cnt := if n == 0 { a.len - 1 } else { n } + for i in 0 .. cnt { + x := rand.int_in_range(i, a.len) + // swap + a_i := a[i] + a[i] = a[x] + a[x] = a_i + } +} diff --git a/v_windows/v/old/vlib/rand/util/util_test.v b/v_windows/v/old/vlib/rand/util/util_test.v new file mode 100644 index 0000000..f92e70b --- /dev/null +++ b/v_windows/v/old/vlib/rand/util/util_test.v @@ -0,0 +1,57 @@ +import rand +import rand.util + +fn test_sample_nr() { + lengths := [1, 3, 4, 5, 6, 7] + a := ['one', 'two', 'three', 'four', 'five', 'six', 'seven'] + for length in lengths { + b := util.sample_nr(a, length) + assert b.len == length + for element in b { + assert element in a + // make sure every element occurs once + mut count := 0 + for e in b { + if e == element { + count++ + } + } + assert count == 1 + } + } +} + +fn test_sample_r() { + k := 20 + a := ['heads', 'tails'] + b := util.sample_r(a, k) + assert b.len == k + for element in b { + assert element in a + } +} + +fn test_shuffle() { + rand.seed([u32(1), 2]) // set seed to produce same results in order + a := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + mut b := a.clone() + mut c := a.clone() + util.shuffle(mut b, 0) + util.shuffle(mut c, 0) + assert b == [6, 4, 5, 1, 9, 2, 10, 3, 8, 7] + assert c == [1, 6, 5, 8, 7, 2, 10, 9, 3, 4] + // test shuffling a slice + mut d := a.clone() + util.shuffle(mut d[..5], 0) + assert d == [5, 2, 1, 3, 4, 6, 7, 8, 9, 10] + assert d[5..] == a[5..] + // test shuffling n items + mut e := a.clone() + util.shuffle(mut e, 5) + assert e[..5] == [10, 3, 1, 8, 4] + assert e[5..] == [6, 7, 5, 9, 2] + // test shuffling empty array + mut f := a[..0] + util.shuffle(mut f, 0) + assert f == []int{} +} diff --git a/v_windows/v/old/vlib/rand/wyrand/wyrand.v b/v_windows/v/old/vlib/rand/wyrand/wyrand.v new file mode 100644 index 0000000..22b71af --- /dev/null +++ b/v_windows/v/old/vlib/rand/wyrand/wyrand.v @@ -0,0 +1,252 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module wyrand + +import math.bits +import rand.seed +import rand.constants +import hash + +// Redefinition of some constants that we will need for pseudorandom number generation. +const ( + wyp0 = u64(0xa0761d6478bd642f) + wyp1 = u64(0xe7037ed1a0b428db) +) + +// WyRandRNG is a RNG based on the WyHash hashing algorithm. +pub struct WyRandRNG { +mut: + state u64 = seed.time_seed_64() + has_extra bool + extra u32 +} + +// seed sets the seed, needs only two `u32`s in little-endian format as [lower, higher]. +pub fn (mut rng WyRandRNG) seed(seed_data []u32) { + if seed_data.len != 2 { + eprintln('WyRandRNG needs 2 32-bit unsigned integers as the seed.') + exit(1) + } + rng.state = seed_data[0] | (u64(seed_data[1]) << 32) + rng.has_extra = false +} + +// u32 updates the PRNG state and returns the next pseudorandom `u32`. +[inline] +pub fn (mut rng WyRandRNG) u32() u32 { + if rng.has_extra { + rng.has_extra = false + return rng.extra + } + full_value := rng.u64() + lower := u32(full_value & constants.lower_mask) + upper := u32(full_value >> 32) + rng.extra = upper + rng.has_extra = true + return lower +} + +// u64 updates the PRNG state and returns the next pseudorandom `u64`. +[inline] +pub fn (mut rng WyRandRNG) u64() u64 { + unsafe { + mut seed1 := rng.state + seed1 += wyrand.wyp0 + rng.state = seed1 + return hash.wymum(seed1 ^ wyrand.wyp1, seed1) + } + return 0 +} + +// u32n returns a pseudorandom `u32` less than `max`. +[inline] +pub fn (mut rng WyRandRNG) u32n(max u32) u32 { + if max == 0 { + eprintln('max must be positive integer') + exit(1) + } + // Check SysRNG in system_rng.c.v for explanation + bit_len := bits.len_32(max) + if bit_len == 32 { + for { + value := rng.u32() + if value < max { + return value + } + } + } else { + mask := (u32(1) << (bit_len + 1)) - 1 + for { + value := rng.u32() & mask + if value < max { + return value + } + } + } + return u32(0) +} + +// u64n returns a pseudorandom `u64` less than `max`. +[inline] +pub fn (mut rng WyRandRNG) u64n(max u64) u64 { + if max == 0 { + eprintln('max must be positive integer') + exit(1) + } + bit_len := bits.len_64(max) + if bit_len == 64 { + for { + value := rng.u64() + if value < max { + return value + } + } + } else { + mask := (u64(1) << (bit_len + 1)) - 1 + for { + value := rng.u64() & mask + if value < max { + return value + } + } + } + return u64(0) +} + +// u32n returns a pseudorandom `u32` value that is guaranteed to be in range `[min, max)`. +[inline] +pub fn (mut rng WyRandRNG) u32_in_range(min u32, max u32) u32 { + if max <= min { + eprintln('max must be greater than min') + exit(1) + } + return min + rng.u32n(max - min) +} + +// u64n returns a pseudorandom `u64` value that is guaranteed to be in range `[min, max)`. +[inline] +pub fn (mut rng WyRandRNG) u64_in_range(min u64, max u64) u64 { + if max <= min { + eprintln('max must be greater than min') + exit(1) + } + return min + rng.u64n(max - min) +} + +// int returns a (possibly negative) pseudorandom 32-bit `int`. +[inline] +pub fn (mut rng WyRandRNG) int() int { + return int(rng.u32()) +} + +// i64 returns a (possibly negative) pseudorandom 64-bit `i64`. +[inline] +pub fn (mut rng WyRandRNG) i64() i64 { + return i64(rng.u64()) +} + +// int31 returns a positive pseudorandom 31-bit `int`. +[inline] +pub fn (mut rng WyRandRNG) int31() int { + return int(rng.u32() & constants.u31_mask) // Set the 32nd bit to 0. +} + +// int63 returns a positive pseudorandom 63-bit `i64`. +[inline] +pub fn (mut rng WyRandRNG) int63() i64 { + return i64(rng.u64() & constants.u63_mask) // Set the 64th bit to 0. +} + +// intn returns a pseudorandom `int` in range `[0, max)`. +[inline] +pub fn (mut rng WyRandRNG) intn(max int) int { + if max <= 0 { + eprintln('max has to be positive.') + exit(1) + } + return int(rng.u32n(u32(max))) +} + +// i64n returns a pseudorandom int that lies in `[0, max)`. +[inline] +pub fn (mut rng WyRandRNG) i64n(max i64) i64 { + if max <= 0 { + eprintln('max has to be positive.') + exit(1) + } + return i64(rng.u64n(u64(max))) +} + +// int_in_range returns a pseudorandom `int` in range `[min, max)`. +[inline] +pub fn (mut rng WyRandRNG) int_in_range(min int, max int) int { + if max <= min { + eprintln('max must be greater than min') + exit(1) + } + // This supports negative ranges like [-10, -5) because the difference is positive + return min + rng.intn(max - min) +} + +// i64_in_range returns a pseudorandom `i64` in range `[min, max)`. +[inline] +pub fn (mut rng WyRandRNG) i64_in_range(min i64, max i64) i64 { + if max <= min { + eprintln('max must be greater than min') + exit(1) + } + return min + rng.i64n(max - min) +} + +// f32 returns a pseudorandom `f32` value in range `[0, 1)`. +[inline] +pub fn (mut rng WyRandRNG) f32() f32 { + return f32(rng.u32()) / constants.max_u32_as_f32 +} + +// f64 returns a pseudorandom `f64` value in range `[0, 1)`. +[inline] +pub fn (mut rng WyRandRNG) f64() f64 { + return f64(rng.u64()) / constants.max_u64_as_f64 +} + +// f32n returns a pseudorandom `f32` value in range `[0, max)`. +[inline] +pub fn (mut rng WyRandRNG) f32n(max f32) f32 { + if max <= 0 { + eprintln('max has to be positive.') + exit(1) + } + return rng.f32() * max +} + +// f64n returns a pseudorandom `f64` value in range `[0, max)`. +[inline] +pub fn (mut rng WyRandRNG) f64n(max f64) f64 { + if max <= 0 { + eprintln('max has to be positive.') + exit(1) + } + return rng.f64() * max +} + +// f32_in_range returns a pseudorandom `f32` in range `[min, max)`. +[inline] +pub fn (mut rng WyRandRNG) f32_in_range(min f32, max f32) f32 { + if max <= min { + eprintln('max must be greater than min') + exit(1) + } + return min + rng.f32n(max - min) +} + +// i64_in_range returns a pseudorandom `i64` in range `[min, max)`. +[inline] +pub fn (mut rng WyRandRNG) f64_in_range(min f64, max f64) f64 { + if max <= min { + eprintln('max must be greater than min') + exit(1) + } + return min + rng.f64n(max - min) +} diff --git a/v_windows/v/old/vlib/rand/wyrand/wyrand_test.v b/v_windows/v/old/vlib/rand/wyrand/wyrand_test.v new file mode 100644 index 0000000..4cdfdb6 --- /dev/null +++ b/v_windows/v/old/vlib/rand/wyrand/wyrand_test.v @@ -0,0 +1,331 @@ +import math +import rand.seed +import rand.wyrand + +const ( + range_limit = 40 + value_count = 1000 + seeds = [[u32(42), 0], [u32(256), 0]] +) + +const ( + sample_size = 1000 + stats_epsilon = 0.05 + inv_sqrt_12 = 1.0 / math.sqrt(12) +) + +fn gen_randoms(seed_data []u32, bound int) []u64 { + bound_u64 := u64(bound) + mut randoms := []u64{len: (20)} + mut rnd := wyrand.WyRandRNG{} + rnd.seed(seed_data) + for i in 0 .. 20 { + randoms[i] = rnd.u64n(bound_u64) + } + return randoms +} + +fn test_wyrand_reproducibility() { + seed_data := seed.time_seed_array(2) + randoms1 := gen_randoms(seed_data, 1000) + randoms2 := gen_randoms(seed_data, 1000) + assert randoms1.len == randoms2.len + len := randoms1.len + for i in 0 .. len { + assert randoms1[i] == randoms2[i] + } +} + +// TODO: use the `in` syntax and remove this function +// after generics has been completely implemented +fn found(value u64, arr []u64) bool { + for item in arr { + if value == item { + return true + } + } + return false +} + +fn test_wyrand_variability() { + // If this test fails and if it is certainly not the implementation + // at fault, try changing the seed values. Repeated values are + // improbable but not impossible. + for seed in seeds { + mut rng := wyrand.WyRandRNG{} + rng.seed(seed) + mut values := []u64{cap: value_count} + for i in 0 .. value_count { + value := rng.u64() + assert !found(value, values) + assert values.len == i + values << value + } + } +} + +fn check_uniformity_u64(mut rng wyrand.WyRandRNG, range u64) { + range_f64 := f64(range) + expected_mean := range_f64 / 2.0 + mut variance := 0.0 + for _ in 0 .. sample_size { + diff := f64(rng.u64n(range)) - expected_mean + variance += diff * diff + } + variance /= sample_size - 1 + sigma := math.sqrt(variance) + expected_sigma := range_f64 * inv_sqrt_12 + error := (sigma - expected_sigma) / expected_sigma + assert math.abs(error) < stats_epsilon +} + +fn test_wyrand_uniformity_u64() { + ranges := [14019545, 80240, 130] + for seed in seeds { + mut rng := wyrand.WyRandRNG{} + rng.seed(seed) + for range in ranges { + check_uniformity_u64(mut rng, u64(range)) + } + } +} + +fn check_uniformity_f64(mut rng wyrand.WyRandRNG) { + expected_mean := 0.5 + mut variance := 0.0 + for _ in 0 .. sample_size { + diff := rng.f64() - expected_mean + variance += diff * diff + } + variance /= sample_size - 1 + sigma := math.sqrt(variance) + expected_sigma := inv_sqrt_12 + error := (sigma - expected_sigma) / expected_sigma + assert math.abs(error) < stats_epsilon +} + +fn test_wyrand_uniformity_f64() { + // The f64 version + for seed in seeds { + mut rng := wyrand.WyRandRNG{} + rng.seed(seed) + check_uniformity_f64(mut rng) + } +} + +fn test_wyrand_u32n() { + max := u32(16384) + for seed in seeds { + mut rng := wyrand.WyRandRNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.u32n(max) + assert value >= 0 + assert value < max + } + } +} + +fn test_wyrand_u64n() { + max := u64(379091181005) + for seed in seeds { + mut rng := wyrand.WyRandRNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.u64n(max) + assert value >= 0 + assert value < max + } + } +} + +fn test_wyrand_u32_in_range() { + max := u32(484468466) + min := u32(316846) + for seed in seeds { + mut rng := wyrand.WyRandRNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.u32_in_range(min, max) + assert value >= min + assert value < max + } + } +} + +fn test_wyrand_u64_in_range() { + max := u64(216468454685163) + min := u64(6848646868) + for seed in seeds { + mut rng := wyrand.WyRandRNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.u64_in_range(min, max) + assert value >= min + assert value < max + } + } +} + +fn test_wyrand_int31() { + max_u31 := int(0x7FFFFFFF) + sign_mask := int(0x80000000) + for seed in seeds { + mut rng := wyrand.WyRandRNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.int31() + assert value >= 0 + assert value <= max_u31 + // This statement ensures that the sign bit is zero + assert (value & sign_mask) == 0 + } + } +} + +fn test_wyrand_int63() { + max_u63 := i64(0x7FFFFFFFFFFFFFFF) + sign_mask := i64(0x8000000000000000) + for seed in seeds { + mut rng := wyrand.WyRandRNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.int63() + assert value >= 0 + assert value <= max_u63 + assert (value & sign_mask) == 0 + } + } +} + +fn test_wyrand_intn() { + max := 2525642 + for seed in seeds { + mut rng := wyrand.WyRandRNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.intn(max) + assert value >= 0 + assert value < max + } + } +} + +fn test_wyrand_i64n() { + max := i64(3246727724653636) + for seed in seeds { + mut rng := wyrand.WyRandRNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.i64n(max) + assert value >= 0 + assert value < max + } + } +} + +fn test_wyrand_int_in_range() { + min := -4252 + max := 1034 + for seed in seeds { + mut rng := wyrand.WyRandRNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.int_in_range(min, max) + assert value >= min + assert value < max + } + } +} + +fn test_wyrand_i64_in_range() { + min := i64(-24095) + max := i64(324058) + for seed in seeds { + mut rng := wyrand.WyRandRNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.i64_in_range(min, max) + assert value >= min + assert value < max + } + } +} + +fn test_wyrand_f32() { + for seed in seeds { + mut rng := wyrand.WyRandRNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.f32() + assert value >= 0.0 + assert value < 1.0 + } + } +} + +fn test_wyrand_f64() { + for seed in seeds { + mut rng := wyrand.WyRandRNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.f64() + assert value >= 0.0 + assert value < 1.0 + } + } +} + +fn test_wyrand_f32n() { + max := f32(357.0) + for seed in seeds { + mut rng := wyrand.WyRandRNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.f32n(max) + assert value >= 0.0 + assert value < max + } + } +} + +fn test_wyrand_f64n() { + max := 1.52e6 + for seed in seeds { + mut rng := wyrand.WyRandRNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.f64n(max) + assert value >= 0.0 + assert value < max + } + } +} + +fn test_wyrand_f32_in_range() { + min := f32(-24.0) + max := f32(125.0) + for seed in seeds { + mut rng := wyrand.WyRandRNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.f32_in_range(min, max) + assert value >= min + assert value < max + } + } +} + +fn test_wyrand_f64_in_range() { + min := -548.7 + max := 5015.2 + for seed in seeds { + mut rng := wyrand.WyRandRNG{} + rng.seed(seed) + for _ in 0 .. range_limit { + value := rng.f64_in_range(min, max) + assert value >= min + assert value < max + } + } +} diff --git a/v_windows/v/old/vlib/readline/README.md b/v_windows/v/old/vlib/readline/README.md new file mode 100644 index 0000000..edd4682 --- /dev/null +++ b/v_windows/v/old/vlib/readline/README.md @@ -0,0 +1,20 @@ +# Readline + +The `readline` module let you await and read user input +from a terminal in an easy and structured manner. + +The module provides an easy way to prompt the user for +questions or even make a REPL or an embedded console. + +Use `readline.Readline` if you want to include more +advanced features such as history or simply use +`readline.read_line('Please confirm (y/n):')` directly +for one-off user interactions. + +# Usage + +```v ignore +import readline { Readline } + +Readline.read_line('Continue?: (y/n)') +``` diff --git a/v_windows/v/old/vlib/readline/readline.v b/v_windows/v/old/vlib/readline/readline.v new file mode 100644 index 0000000..4f0ebf7 --- /dev/null +++ b/v_windows/v/old/vlib/readline/readline.v @@ -0,0 +1,45 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +// +// Serves as a more advanced input method +// based on the work of https://github.com/AmokHuginnsson/replxx +// +module readline + +// Termios stores the terminal options on Linux. +struct C.termios {} + +struct Termios { +mut: + c_iflag int + c_oflag int + c_cflag int + c_lflag int + c_cc [12]int // NCCS == 12. Can't use the defined value here +} + +// Winsize stores the screen information on Linux. +struct Winsize { + ws_row u16 + ws_col u16 + ws_xpixel u16 + ws_ypixel u16 +} + +// Readline is the key struct for reading and holding user input via a terminal. +// Example: import readline { Readline } +pub struct Readline { +mut: + is_raw bool + orig_termios Termios // Linux + current []rune // Line being edited + cursor int // Cursor position + overwrite bool + cursor_row_offset int + prompt string + prompt_offset int + previous_lines [][]rune + search_index int + is_tty bool +} diff --git a/v_windows/v/old/vlib/readline/readline_default.c.v b/v_windows/v/old/vlib/readline/readline_default.c.v new file mode 100644 index 0000000..fcbbbb6 --- /dev/null +++ b/v_windows/v/old/vlib/readline/readline_default.c.v @@ -0,0 +1,78 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +// +// TODO Mac version needs to be implemented +// Will serve as more advanced input method +// based on the work of https://github.com/AmokHuginnsson/replxx +// +module readline + +import os + +#include +// Only use standard os.get_line +// Need implementation for readline capabilities +// +// read_line_utf8 blocks execution in a loop and awaits user input +// characters from a terminal until `EOF` or `Enter` key is encountered +// in the input stream. +// read_line_utf8 returns the complete input line as an UTF-8 encoded `[]rune` or +// an error if the line is empty. +// The `prompt` `string` is output as a prefix text for the input capturing. +// read_line_utf8 is the main method of the `readline` module and `Readline` struct. +pub fn (mut r Readline) read_line_utf8(prompt string) ?[]rune { + r.current = []rune{} + r.cursor = 0 + r.prompt = prompt + r.search_index = 0 + if r.previous_lines.len <= 1 { + r.previous_lines << []rune{} + r.previous_lines << []rune{} + } else { + r.previous_lines[0] = []rune{} + } + print(r.prompt) + line := os.get_raw_line() + if line.len >= 0 { + r.current = line.runes() + } + r.previous_lines[0] = []rune{} + r.search_index = 0 + if r.current.len == 0 { + return error('empty line') + } + return r.current +} + +// read_line does the same as `read_line_utf8` but returns user input as a `string`. +// (As opposed to `[]rune` returned by `read_line_utf8`). +pub fn (mut r Readline) read_line(prompt string) ?string { + s := r.read_line_utf8(prompt) ? + return s.string() +} + +// read_line_utf8 blocks execution in a loop and awaits user input +// characters from a terminal until `EOF` or `Enter` key is encountered +// in the input stream. +// read_line_utf8 returns the complete input line as an UTF-8 encoded `[]rune` or +// an error if the line is empty. +// The `prompt` `string` is output as a prefix text for the input capturing. +// read_line_utf8 is the main method of the `readline` module and `Readline` struct. +// NOTE that this version of `read_line_utf8` is a standalone function without +// persistent functionalities (e.g. history). +pub fn read_line_utf8(prompt string) ?[]rune { + mut r := Readline{} + s := r.read_line_utf8(prompt) ? + return s +} + +// read_line does the same as `read_line_utf8` but returns user input as a `string`. +// (As opposed to `[]rune` as returned by `read_line_utf8`). +// NOTE that this version of `read_line` is a standalone function without +// persistent functionalities (e.g. history). +pub fn read_line(prompt string) ?string { + mut r := Readline{} + s := r.read_line(prompt) ? + return s +} diff --git a/v_windows/v/old/vlib/readline/readline_linux.c.v b/v_windows/v/old/vlib/readline/readline_linux.c.v new file mode 100644 index 0000000..f96b613 --- /dev/null +++ b/v_windows/v/old/vlib/readline/readline_linux.c.v @@ -0,0 +1,554 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +// +// Serves as more advanced input method +// based on the work of https://github.com/AmokHuginnsson/replxx +// +module readline + +import term +import os + +#include +#include + +fn C.tcgetattr(fd int, termios_p &C.termios) int + +fn C.tcsetattr(fd int, optional_actions int, termios_p &C.termios) int + +fn C.raise(sig int) + +fn C.getppid() int + +// Action defines what actions to be executed. +enum Action { + eof + nothing + insert_character + commit_line + delete_left + delete_right + move_cursor_left + move_cursor_right + move_cursor_begining + move_cursor_end + move_cursor_word_left + move_cursor_word_right + history_previous + history_next + overwrite + clear_screen + suspend +} + +// enable_raw_mode enables the raw mode of the terminal. +// In raw mode all key presses are directly sent to the program and no interpretation is done. +// Please note that `enable_raw_mode` catches the `SIGUSER` (CTRL + C) signal. +// For a method that does please see `enable_raw_mode_nosig`. +pub fn (mut r Readline) enable_raw_mode() { + if C.tcgetattr(0, unsafe { &C.termios(&r.orig_termios) }) == -1 { + r.is_tty = false + r.is_raw = false + return + } + mut raw := r.orig_termios + raw.c_iflag &= ~(C.BRKINT | C.ICRNL | C.INPCK | C.ISTRIP | C.IXON) + raw.c_cflag |= C.CS8 + raw.c_lflag &= ~(C.ECHO | C.ICANON | C.IEXTEN | C.ISIG) + raw.c_cc[C.VMIN] = 1 + raw.c_cc[C.VTIME] = 0 + C.tcsetattr(0, C.TCSADRAIN, unsafe { &C.termios(&raw) }) + r.is_raw = true + r.is_tty = true +} + +// enable_raw_mode_nosig enables the raw mode of the terminal. +// In raw mode all key presses are directly sent to the program and no interpretation is done. +// Please note that `enable_raw_mode_nosig` does not catch the `SIGUSER` (CTRL + C) signal +// as opposed to `enable_raw_mode`. +pub fn (mut r Readline) enable_raw_mode_nosig() { + if C.tcgetattr(0, unsafe { &C.termios(&r.orig_termios) }) == -1 { + r.is_tty = false + r.is_raw = false + return + } + mut raw := r.orig_termios + raw.c_iflag &= ~(C.BRKINT | C.ICRNL | C.INPCK | C.ISTRIP | C.IXON) + raw.c_cflag |= C.CS8 + raw.c_lflag &= ~(C.ECHO | C.ICANON | C.IEXTEN) + raw.c_cc[C.VMIN] = 1 + raw.c_cc[C.VTIME] = 0 + C.tcsetattr(0, C.TCSADRAIN, unsafe { &C.termios(&raw) }) + r.is_raw = true + r.is_tty = true +} + +// disable_raw_mode disables the raw mode of the terminal. +// For a description of raw mode please see the `enable_raw_mode` method. +pub fn (mut r Readline) disable_raw_mode() { + if r.is_raw { + C.tcsetattr(0, C.TCSADRAIN, unsafe { &C.termios(&r.orig_termios) }) + r.is_raw = false + } +} + +// read_char reads a single character. +pub fn (r Readline) read_char() int { + return utf8_getchar() +} + +// read_line_utf8 blocks execution in a loop and awaits user input +// characters from a terminal until `EOF` or `Enter` key is encountered +// in the input stream. +// read_line_utf8 returns the complete input line as an UTF-8 encoded `[]rune` or +// an error if the line is empty. +// The `prompt` `string` is output as a prefix text for the input capturing. +// read_line_utf8 is the main method of the `readline` module and `Readline` struct. +pub fn (mut r Readline) read_line_utf8(prompt string) ?[]rune { + r.current = []rune{} + r.cursor = 0 + r.prompt = prompt + r.search_index = 0 + r.prompt_offset = get_prompt_offset(prompt) + if r.previous_lines.len <= 1 { + r.previous_lines << []rune{} + r.previous_lines << []rune{} + } else { + r.previous_lines[0] = []rune{} + } + if !r.is_raw { + r.enable_raw_mode() + } + print(r.prompt) + for { + C.fflush(C.stdout) + c := r.read_char() + a := r.analyse(c) + if r.execute(a, c) { + break + } + } + r.previous_lines[0] = []rune{} + r.search_index = 0 + r.disable_raw_mode() + if r.current.len == 0 { + return error('empty line') + } + return r.current +} + +// read_line does the same as `read_line_utf8` but returns user input as a `string`. +// (As opposed to `[]rune` returned by `read_line_utf8`). +pub fn (mut r Readline) read_line(prompt string) ?string { + s := r.read_line_utf8(prompt) ? + return s.string() +} + +// read_line_utf8 blocks execution in a loop and awaits user input +// characters from a terminal until `EOF` or `Enter` key is encountered +// in the input stream. +// read_line_utf8 returns the complete input line as an UTF-8 encoded `[]rune` or +// an error if the line is empty. +// The `prompt` `string` is output as a prefix text for the input capturing. +// read_line_utf8 is the main method of the `readline` module and `Readline` struct. +// NOTE that this version of `read_line_utf8` is a standalone function without +// persistent functionalities (e.g. history). +pub fn read_line_utf8(prompt string) ?[]rune { + mut r := Readline{} + s := r.read_line_utf8(prompt) ? + return s +} + +// read_line does the same as `read_line_utf8` but returns user input as a `string`. +// (As opposed to `[]rune` as returned by `read_line_utf8`). +// NOTE that this version of `read_line` is a standalone function without +// persistent functionalities (e.g. history). +pub fn read_line(prompt string) ?string { + mut r := Readline{} + s := r.read_line(prompt) ? + return s +} + +// analyse returns an `Action` based on the type of input byte given in `c`. +fn (r Readline) analyse(c int) Action { + match byte(c) { + `\0`, 0x3, 0x4, 255 { + return .eof + } // NUL, End of Text, End of Transmission + `\n`, `\r` { + return .commit_line + } + `\f` { + return .clear_screen + } // CTRL + L + `\b`, 127 { + return .delete_left + } // BS, DEL + 27 { + return r.analyse_control() + } // ESC + 1 { + return .move_cursor_begining + } // ^A + 5 { + return .move_cursor_end + } // ^E + 26 { + return .suspend + } // CTRL + Z, SUB + else { + if c >= ` ` { + return Action.insert_character + } + return Action.nothing + } + } +} + +// analyse_control returns an `Action` based on the type of input read by `read_char`. +fn (r Readline) analyse_control() Action { + c := r.read_char() + match byte(c) { + `[` { + sequence := r.read_char() + match byte(sequence) { + `C` { return .move_cursor_right } + `D` { return .move_cursor_left } + `B` { return .history_next } + `A` { return .history_previous } + `1` { return r.analyse_extended_control() } + `2`, `3` { return r.analyse_extended_control_no_eat(byte(sequence)) } + else {} + } + } + else {} + } + /* + //TODO +match c { + case `[`: + sequence := r.read_char() + match sequence { + case `C`: return .move_cursor_right + case `D`: return .move_cursor_left + case `B`: return .history_next + case `A`: return .history_previous + case `1`: return r.analyse_extended_control() + case `2`: return r.analyse_extended_control_no_eat(sequence) + case `3`: return r.analyse_extended_control_no_eat(sequence) + case `9`: + foo() + bar() + else: + } + else: +} + */ + return .nothing +} + +// analyse_extended_control returns an `Action` based on the type of input read by `read_char`. +// analyse_extended_control specialises in cursor control. +fn (r Readline) analyse_extended_control() Action { + r.read_char() // Removes ; + c := r.read_char() + match byte(c) { + `5` { + direction := r.read_char() + match byte(direction) { + `C` { return .move_cursor_word_right } + `D` { return .move_cursor_word_left } + else {} + } + } + else {} + } + return .nothing +} + +// analyse_extended_control_no_eat returns an `Action` based on the type of input byte given in `c`. +// analyse_extended_control_no_eat specialises in detection of delete and insert keys. +fn (r Readline) analyse_extended_control_no_eat(last_c byte) Action { + c := r.read_char() + match byte(c) { + `~` { + match last_c { + `3` { return .delete_right } // Suppr key + `2` { return .overwrite } + else {} + } + } + else {} + } + return .nothing +} + +// execute executes the corresponding methods on `Readline` based on `a Action` and `c int` arguments. +fn (mut r Readline) execute(a Action, c int) bool { + match a { + .eof { return r.eof() } + .insert_character { r.insert_character(c) } + .commit_line { return r.commit_line() } + .delete_left { r.delete_character() } + .delete_right { r.suppr_character() } + .move_cursor_left { r.move_cursor_left() } + .move_cursor_right { r.move_cursor_right() } + .move_cursor_begining { r.move_cursor_begining() } + .move_cursor_end { r.move_cursor_end() } + .move_cursor_word_left { r.move_cursor_word_left() } + .move_cursor_word_right { r.move_cursor_word_right() } + .history_previous { r.history_previous() } + .history_next { r.history_next() } + .overwrite { r.switch_overwrite() } + .clear_screen { r.clear_screen() } + .suspend { r.suspend() } + else {} + } + return false +} + +// get_screen_columns returns the number of columns (`width`) in the terminal. +fn get_screen_columns() int { + ws := Winsize{} + cols := if C.ioctl(1, C.TIOCGWINSZ, &ws) == -1 { 80 } else { int(ws.ws_col) } + return cols +} + +// shift_cursor warps the cursor to `xpos` with `yoffset`. +fn shift_cursor(xpos int, yoffset int) { + if yoffset != 0 { + if yoffset > 0 { + term.cursor_down(yoffset) + } else { + term.cursor_up(-yoffset) + } + } + // Absolute X position + print('\x1b[${xpos + 1}G') +} + +// calculate_screen_position returns a position `[x, y]int` based on various terminal attributes. +fn calculate_screen_position(x_in int, y_in int, screen_columns int, char_count int, inp []int) []int { + mut out := inp.clone() + mut x := x_in + mut y := y_in + out[0] = x + out[1] = y + for chars_remaining := char_count; chars_remaining > 0; { + chars_this_row := if (x + chars_remaining) < screen_columns { + chars_remaining + } else { + screen_columns - x + } + out[0] = x + chars_this_row + out[1] = y + chars_remaining -= chars_this_row + x = 0 + y++ + } + if out[0] == screen_columns { + out[0] = 0 + out[1]++ + } + return out +} + +// get_prompt_offset computes the length of the `prompt` `string` argument. +fn get_prompt_offset(prompt string) int { + mut len := 0 + for i := 0; i < prompt.len; i++ { + if prompt[i] == `\e` { + for ; i < prompt.len && prompt[i] != `m`; i++ { + } + } else { + len = len + 1 + } + } + return prompt.len - len +} + +// refresh_line redraws the current line, including the prompt. +fn (mut r Readline) refresh_line() { + mut end_of_input := [0, 0] + end_of_input = calculate_screen_position(r.prompt.len, 0, get_screen_columns(), r.current.len, + end_of_input) + end_of_input[1] += r.current.filter(it == `\n`).len + mut cursor_pos := [0, 0] + cursor_pos = calculate_screen_position(r.prompt.len, 0, get_screen_columns(), r.cursor, + cursor_pos) + shift_cursor(0, -r.cursor_row_offset) + term.erase_toend() + print(r.prompt) + print(r.current.string()) + if end_of_input[0] == 0 && end_of_input[1] > 0 { + print('\n') + } + shift_cursor(cursor_pos[0] - r.prompt_offset, -(end_of_input[1] - cursor_pos[1])) + r.cursor_row_offset = cursor_pos[1] +} + +// eof ends the line *without* a newline. +fn (mut r Readline) eof() bool { + r.previous_lines.insert(1, r.current) + r.cursor = r.current.len + if r.is_tty { + r.refresh_line() + } + return true +} + +// insert_character inserts the character `c` at current cursor position. +fn (mut r Readline) insert_character(c int) { + if !r.overwrite || r.cursor == r.current.len { + r.current.insert(r.cursor, c) + } else { + r.current[r.cursor] = rune(c) + } + r.cursor++ + // Refresh the line to add the new character + if r.is_tty { + r.refresh_line() + } +} + +// Removes the character behind cursor. +fn (mut r Readline) delete_character() { + if r.cursor <= 0 { + return + } + r.cursor-- + r.current.delete(r.cursor) + r.refresh_line() +} + +// suppr_character removes (suppresses) the character in front of the cursor. +fn (mut r Readline) suppr_character() { + if r.cursor > r.current.len { + return + } + r.current.delete(r.cursor) + r.refresh_line() +} + +// commit_line adds a line break and then stops the main loop. +fn (mut r Readline) commit_line() bool { + r.previous_lines.insert(1, r.current) + r.current << `\n` + r.cursor = r.current.len + if r.is_tty { + r.refresh_line() + println('') + } + return true +} + +// move_cursor_left moves the cursor relative one cell to the left. +fn (mut r Readline) move_cursor_left() { + if r.cursor > 0 { + r.cursor-- + r.refresh_line() + } +} + +// move_cursor_right moves the cursor relative one cell to the right. +fn (mut r Readline) move_cursor_right() { + if r.cursor < r.current.len { + r.cursor++ + r.refresh_line() + } +} + +// move_cursor_begining moves the cursor to the beginning of the current line. +fn (mut r Readline) move_cursor_begining() { + r.cursor = 0 + r.refresh_line() +} + +// move_cursor_end moves the cursor to the end of the current line. +fn (mut r Readline) move_cursor_end() { + r.cursor = r.current.len + r.refresh_line() +} + +// is_break_character returns true if the character is considered as a word-breaking character. +fn (r Readline) is_break_character(c string) bool { + break_characters := ' \t\v\f\a\b\r\n`~!@#$%^&*()-=+[{]}\\|;:\'",<.>/?' + return break_characters.contains(c) +} + +// move_cursor_word_left moves the cursor relative one word length worth to the left. +fn (mut r Readline) move_cursor_word_left() { + if r.cursor > 0 { + for ; r.cursor > 0 && r.is_break_character(r.current[r.cursor - 1].str()); r.cursor-- { + } + for ; r.cursor > 0 && !r.is_break_character(r.current[r.cursor - 1].str()); r.cursor-- { + } + r.refresh_line() + } +} + +// move_cursor_word_right moves the cursor relative one word length worth to the right. +fn (mut r Readline) move_cursor_word_right() { + if r.cursor < r.current.len { + for ; r.cursor < r.current.len && r.is_break_character(r.current[r.cursor].str()); r.cursor++ { + } + for ; r.cursor < r.current.len && !r.is_break_character(r.current[r.cursor].str()); r.cursor++ { + } + r.refresh_line() + } +} + +// switch_overwrite toggles Readline `overwrite` mode on/off. +fn (mut r Readline) switch_overwrite() { + r.overwrite = !r.overwrite +} + +// clear_screen clears the current terminal window contents and positions the cursor at top left. +fn (mut r Readline) clear_screen() { + term.set_cursor_position(x: 1, y: 1) + term.erase_clear() + r.refresh_line() +} + +// history_previous sets current line to the content of the previous line in the history buffer. +fn (mut r Readline) history_previous() { + if r.search_index + 2 >= r.previous_lines.len { + return + } + if r.search_index == 0 { + r.previous_lines[0] = r.current + } + r.search_index++ + r.current = r.previous_lines[r.search_index] + r.cursor = r.current.len + r.refresh_line() +} + +// history_next sets current line to the content of the next line in the history buffer. +fn (mut r Readline) history_next() { + if r.search_index <= 0 { + return + } + r.search_index-- + r.current = r.previous_lines[r.search_index] + r.cursor = r.current.len + r.refresh_line() +} + +// suspend sends the `SIGSTOP` signal to the terminal. +fn (mut r Readline) suspend() { + is_standalone := os.getenv('VCHILD') != 'true' + r.disable_raw_mode() + if !is_standalone { + // We have to SIGSTOP the parent v process + ppid := C.getppid() + C.kill(ppid, C.SIGSTOP) + } + C.raise(C.SIGSTOP) + r.enable_raw_mode() + r.refresh_line() + if r.is_tty { + r.refresh_line() + } +} diff --git a/v_windows/v/old/vlib/readline/readline_test.v b/v_windows/v/old/vlib/readline/readline_test.v new file mode 100644 index 0000000..ab5154f --- /dev/null +++ b/v_windows/v/old/vlib/readline/readline_test.v @@ -0,0 +1,20 @@ +import readline { Readline } + +fn no_lines(s string) string { + return s.replace('\n', ' ') +} + +fn test_struct_readline() { + // mut rl := Readline{} + // eprintln('rl: $rl') + // line := rl.read_line('Please, enter your name: ') or { panic(err) } + // eprintln('line: $line') + mut methods := []string{} + $for method in Readline.methods { + // eprintln(' method: $method.name | ' + no_lines('$method')) + methods << method.name + } + // eprintln('methods: $methods') + assert 'read_line_utf8' in methods + assert 'read_line' in methods +} diff --git a/v_windows/v/old/vlib/readline/readline_windows.c.v b/v_windows/v/old/vlib/readline/readline_windows.c.v new file mode 100644 index 0000000..887efdb --- /dev/null +++ b/v_windows/v/old/vlib/readline/readline_windows.c.v @@ -0,0 +1,74 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +// +// TODO Windows version needs to be implemented. +// Will serve as more advanced input method +// based on the work of https://github.com/AmokHuginnsson/replxx +// +module readline + +import os + +// Only use standard os.get_line +// Need implementation for readline capabilities +// +// read_line_utf8 blocks execution in a loop and awaits user input +// characters from a terminal until `EOF` or `Enter` key is encountered +// in the input stream. +// read_line_utf8 returns the complete input line as an UTF-8 encoded `[]rune` or +// an error if the line is empty. +// The `prompt` `string` is output as a prefix text for the input capturing. +// read_line_utf8 is the main method of the `readline` module and `Readline` struct. +pub fn (mut r Readline) read_line_utf8(prompt string) ?[]rune { + r.current = []rune{} + r.cursor = 0 + r.prompt = prompt + r.search_index = 0 + if r.previous_lines.len <= 1 { + r.previous_lines << []rune{} + r.previous_lines << []rune{} + } else { + r.previous_lines[0] = []rune{} + } + print(r.prompt) + r.current = os.get_raw_line().runes() + r.previous_lines[0] = []rune{} + r.search_index = 0 + if r.current.len == 0 { + return error('empty line') + } + return r.current +} + +// read_line does the same as `read_line_utf8` but returns user input as a `string`. +// (As opposed to `[]rune` returned by `read_line_utf8`). +pub fn (mut r Readline) read_line(prompt string) ?string { + s := r.read_line_utf8(prompt) ? + return s.string() +} + +// read_line_utf8 blocks execution in a loop and awaits user input +// characters from a terminal until `EOF` or `Enter` key is encountered +// in the input stream. +// read_line_utf8 returns the complete input line as an UTF-8 encoded `[]rune` or +// an error if the line is empty. +// The `prompt` `string` is output as a prefix text for the input capturing. +// read_line_utf8 is the main method of the `readline` module and `Readline` struct. +// NOTE that this version of `read_line_utf8` is a standalone function without +// persistent functionalities (e.g. history). +pub fn read_line_utf8(prompt string) ?[]rune { + mut r := Readline{} + s := r.read_line_utf8(prompt) ? + return s +} + +// read_line does the same as `read_line_utf8` but returns user input as a `string`. +// (As opposed to `[]rune` as returned by `read_line_utf8`). +// NOTE that this version of `read_line` is a standalone function without +// persistent functionalities (e.g. history). +pub fn read_line(prompt string) ?string { + mut r := Readline{} + s := r.read_line(prompt) ? + return s +} diff --git a/v_windows/v/old/vlib/regex/README.md b/v_windows/v/old/vlib/regex/README.md new file mode 100644 index 0000000..ae8c41a --- /dev/null +++ b/v_windows/v/old/vlib/regex/README.md @@ -0,0 +1,874 @@ +# V RegEx (Regular expression) 1.0 alpha + +[TOC] + +## Introduction + +Here are the assumptions made during the writing of the implementation, that +are valid for all the `regex` module features: + +1. The matching stops at the end of the string, *not* at newline characters. + +2. The basic atomic elements of this regex engine are the tokens. +In a query string a simple character is a token. + + +## Differences with PCRE: + +NB: We must point out that the **V-Regex module is not PCRE compliant** and thus +some behaviour will be different. This difference is due to the V philosophy, +to have one way and keep it simple. + +The main differences can be summarized in the following points: + +- The basic element **is the token not the sequence of symbols**, and the most +simple token, is a single character. + +- `|` **the OR operator acts on tokens,** for example `abc|ebc` is not +`abc` OR `ebc`. Instead it is evaluated like `ab`, followed by `c OR e`, +followed by `bc`, because the **token is the base element**, +not the sequence of symbols. + +- The **match operation stops at the end of the string**. It does *NOT* stop +at new line characters. + + +## Tokens + +The tokens are the atomic units, used by this regex engine. +They can be one of the following: + + +### Simple char + +This token is a simple single character like `a` or `b` etc. + + +### Match positional delimiters + +`^` Matches the start of the string. + +`$` Matches the end of the string. + + +### Char class (cc) + +The character classes match all the chars specified inside. Use square +brackets `[ ]` to enclose them. + +The sequence of the chars in the character class, is evaluated with an OR op. + +For example, the cc `[abc]`, matches any character, that is `a` or `b` or `c`, +but it doesn't match `C` or `z`. + +Inside a cc, it is possible to specify a "range" of characters, for example +`[ad-h]` is equivalent to writing `[adefgh]`. + +A cc can have different ranges at the same time, for example `[a-zA-z0-9]` +matches all the latin lowercase, uppercase and numeric characters. + +It is possible to negate the meaning of a cc, using the caret char at the +start of the cc like this: `[^abc]` . That matches every char that is NOT +`a` or `b` or `c`. + +A cc can contain meta-chars like: `[a-z\d]`, that match all the lowercase +latin chars `a-z` and all the digits `\d`. + +It is possible to mix all the properties of the char class together. + +NB: In order to match the `-` (minus) char, it must be preceded by + a backslash in the cc, for example `[\-_\d\a]` will match: + `-` minus, + `_` underscore, + `\d` numeric chars, + `\a` lower case chars. + +### Meta-chars + +A meta-char is specified by a backslash, before a character. +For example `\w` is the meta-char `w`. + +A meta-char can match different types of characters. + +* `\w` matches an alphanumeric char `[a-zA-Z0-9_]` +* `\W` matches a non alphanumeric char +* `\d` matches a digit `[0-9]` +* `\D` matches a non digit +* `\s` matches a space char, one of `[' ','\t','\n','\r','\v','\f']` +* `\S` matches a non space char +* `\a` matches only a lowercase char `[a-z]` +* `\A` matches only an uppercase char `[A-Z]` + +### Quantifier + +Each token can have a quantifier, that specifies how many times the character +must be matched. + +#### **Short quantifiers** + +- `?` matches 0 or 1 time, `a?b` matches both `ab` or `b` +- `+` matches *at least* 1 time, for example, `a+` matches both `aaa` or `a` +- `*` matches 0 or more times, for example, `a*b` matches `aaab`, `ab` or `b` + +#### **Long quantifiers** + +- `{x}` matches exactly x times, `a{2}` matches `aa`, but not `aaa` or `a` +- `{min,}` matches at least min times, `a{2,}` matches `aaa` or `aa`, not `a` +- `{,max}` matches at least 0 times and at maximum max times, + for example, `a{,2}` matches `a` and `aa`, but doesn't match `aaa` +- `{min,max}` matches from min times, to max times, for example + `a{2,3}` matches `aa` and `aaa`, but doesn't match `a` or `aaaa` + +A long quantifier, may have a `greedy off` flag, that is the `?` +character after the brackets. `{2,4}?` means to match the minimum +number of possible tokens, in this case 2. + +### Dot char + +The dot is a particular meta-char, that matches "any char". + +It is simpler to explain it with an example: + +Suppose you have `abccc ddeef` as a source string, that you want to parse +with a regex. The following table show the query strings and the result of +parsing source string. + ++--------------+-------------+ +| query string | result | +|--------------|-------------| +| `.*c` | `abc` | +| `.*dd` | `abcc dd` | +| `ab.*e` | `abccc dde` | +| `ab.{3} .*e` | `abccc dde` | ++--------------+-------------+ + +The dot matches any character, until the next token match is satisfied. + +### OR token + +The token `|`, means a logic OR operation between two consecutive tokens, +i.e. `a|b` matches a character that is `a` or `b`. + +The OR token can work in a "chained way": `a|(b)|cd ` means test first `a`, +if the char is not `a`, then test the group `(b)`, and if the group doesn't +match too, finally test the token `c`. + +NB: ** unlike in PCRE, the OR operation works at token level!** +It doesn't work at concatenation level! + +That also means, that a query string like `abc|bde` is not equal to +`(abc)|(bde)`, but instead to `ab(c|b)de. +The OR operation works only for `c|b`, not at char concatenation level. + +### Groups + +Groups are a method to create complex patterns with repetitions of blocks +of tokens. The groups are delimited by round brackets `( )`. Groups can be +nested. Like all other tokens, groups can have a quantifier too. + +`c(pa)+z` match `cpapaz` or `cpaz` or `cpapapaz` . + +`(c(pa)+z ?)+` matches `cpaz cpapaz cpapapaz` or `cpapaz` + +Lets analyze this last case, first we have the group `#0`, that is the most +outer round brackets `(...)+`. This group has a quantifier `+`, that say to +match its content *at least one time*. + +Then we have a simple char token `c`, and a second group `#1`: `(pa)+`. +This group also tries to match the sequence `pa`, *at least one time*, +as specified by the `+` quantifier. + +Then, we have another simple token `z` and another simple token ` ?`, +i.e. the space char (ascii code 32) followed by the `?` quantifier, +which means that the preceding space should be matched 0 or 1 time. + +This explains why the `(c(pa)+z ?)+` query string, +can match `cpaz cpapaz cpapapaz` . + +In this implementation the groups are "capture groups". This means that the +last temporal result for each group, can be retrieved from the `RE` struct. + +The "capture groups" are stored as indexes in the field `groups`, +that is an `[]int` inside the `RE` struct. + +**example:** + +```v oksyntax +text := 'cpaz cpapaz cpapapaz' +query := r'(c(pa)+z ?)+' +mut re := regex.regex_opt(query) or { panic(err) } +println(re.get_query()) +// #0(c#1(pa)+z ?)+ +// #0 and #1 are the ids of the groups, are shown if re.debug is 1 or 2 +start, end := re.match_string(text) +// [start=0, end=20] match => [cpaz cpapaz cpapapaz] +mut gi := 0 +for gi < re.groups.len { + if re.groups[gi] >= 0 { + println('${gi / 2} :[${text[re.groups[gi]..re.groups[gi + 1]]}]') + } + gi += 2 +} +// groups captured +// 0 :[cpapapaz] +// 1 :[pa] +``` + +**note:** *to show the `group id number` in the result of the `get_query()`* +*the flag `debug` of the RE object must be `1` or `2`* + +In order to simplify the use of the captured groups, it possible to use the +utility function: `get_group_list`. + +This function return a list of groups using this support struct: + +```v oksyntax +pub struct Re_group { +pub: + start int = -1 + end int = -1 +} +``` + +Here an example of use: + +```v oksyntax +/* +This simple function converts an HTML RGB value with 3 or 6 hex digits to +an u32 value, this function is not optimized and it is only for didatical +purpose. Example: #A0B0CC #A9F +*/ +fn convert_html_rgb(in_col string) u32 { + mut n_digit := if in_col.len == 4 { 1 } else { 2 } + mut col_mul := if in_col.len == 4 { 4 } else { 0 } + // this is the regex query, it use the V string interpolation to customize the regex query + // NOTE: if you want use escaped code you must use the r"" (raw) strings, + // *** please remember that the V interpoaltion doesn't work on raw strings. *** + query := '#([a-fA-F0-9]{$n_digit})([a-fA-F0-9]{$n_digit})([a-fA-F0-9]{$n_digit})' + mut re := regex.regex_opt(query) or { panic(err) } + start, end := re.match_string(in_col) + println('start: $start, end: $end') + mut res := u32(0) + if start >= 0 { + group_list := re.get_group_list() // this is the utility function + r := ('0x' + in_col[group_list[0].start..group_list[0].end]).int() << col_mul + g := ('0x' + in_col[group_list[1].start..group_list[1].end]).int() << col_mul + b := ('0x' + in_col[group_list[2].start..group_list[2].end]).int() << col_mul + println('r: $r g: $g b: $b') + res = u32(r) << 16 | u32(g) << 8 | u32(b) + } + return res +} +``` + +Others utility functions are `get_group_by_id` and `get_group_bounds_by_id` +that get directly the string of a group using its `id`: + +```v ignore +txt := "my used string...." +for g_index := 0; g_index < re.group_count ; g_index++ { + println("#${g_index} [${re.get_group_by_id(txt, g_index)}] \ + bounds: ${re.get_group_bounds_by_id(g_index)}") +} +``` + +More helper functions are listed in the **Groups query functions** section. + +### Groups Continuous saving + +In particular situations, it is useful to have a continuous group saving. +This is possible by initializing the `group_csave` field in the `RE` struct. + +This feature allows you to collect data in a continuous/streaming way. + +In the example, we can pass a text, followed by an integer list, +that we wish to collect. To achieve this task, we can use the continuous +group saving, by enabling the right flag: `re.group_csave_flag = true`. + +The `.group_csave` array will be filled then, following this logic: + +`re.group_csave[0]` - number of total saved records +`re.group_csave[1+n*3]` - id of the saved group +`re.group_csave[1+n*3]` - start index in the source string of the saved group +`re.group_csave[1+n*3]` - end index in the source string of the saved group + +The regex will save groups, until it finishes, or finds that the array has no +more space. If the space ends, no error is raised, and further records will +not be saved. + +```v ignore +import regex +fn main(){ + txt := "http://www.ciao.mondo/hello/pippo12_/pera.html" + query := r"(?Phttps?)|(?Pftps?)://(?P[\w_]+.)+" + + mut re := regex.regex_opt(query) or { panic(err) } + //println(re.get_code()) // uncomment to see the print of the regex execution code + re.debug=2 // enable maximum log + println("String: ${txt}") + println("Query : ${re.get_query()}") + re.debug=0 // disable log + re.group_csave_flag = true + start, end := re.match_string(txt) + if start >= 0 { + println("Match ($start, $end) => [${txt[start..end]}]") + } else { + println("No Match") + } + + if re.group_csave_flag == true && start >= 0 && re.group_csave.len > 0{ + println("cg: $re.group_csave") + mut cs_i := 1 + for cs_i < re.group_csave[0]*3 { + g_id := re.group_csave[cs_i] + st := re.group_csave[cs_i+1] + en := re.group_csave[cs_i+2] + println("cg[$g_id] $st $en:[${txt[st..en]}]") + cs_i += 3 + } + } +} +``` + +The output will be: + +``` +String: http://www.ciao.mondo/hello/pippo12_/pera.html +Query : #0(?Phttps?)|{8,14}#0(?Pftps?)://#1(?P[\w_]+.)+ +Match (0, 46) => [http://www.ciao.mondo/hello/pippo12_/pera.html] +cg: [8, 0, 0, 4, 1, 7, 11, 1, 11, 16, 1, 16, 22, 1, 22, 28, 1, 28, 37, 1, 37, 42, 1, 42, 46] +cg[0] 0 4:[http] +cg[1] 7 11:[www.] +cg[1] 11 16:[ciao.] +cg[1] 16 22:[mondo/] +cg[1] 22 28:[hello/] +cg[1] 28 37:[pippo12_/] +cg[1] 37 42:[pera.] +cg[1] 42 46:[html] +``` + +### Named capturing groups + +This regex module supports partially the question mark `?` PCRE syntax for groups. + +`(?:abcd)` **non capturing group**: the content of the group will not be saved. + +`(?Pabcdef)` **named group:** the group content is saved and labeled +as `mygroup`. + +The label of the groups is saved in the `group_map` of the `RE` struct, +that is a map from `string` to `int`, where the value is the index in +`group_csave` list of indexes. + +Here is an example for how to use them: +```v ignore +import regex +fn main(){ + txt := "http://www.ciao.mondo/hello/pippo12_/pera.html" + query := r"(?Phttps?)|(?Pftps?)://(?P[\w_]+.)+" + + mut re := regex.regex_opt(query) or { panic(err) } + //println(re.get_code()) // uncomment to see the print of the regex execution code + re.debug=2 // enable maximum log + println("String: ${txt}") + println("Query : ${re.get_query()}") + re.debug=0 // disable log + start, end := re.match_string(txt) + if start >= 0 { + println("Match ($start, $end) => [${txt[start..end]}]") + } else { + println("No Match") + } + + for name in re.group_map.keys() { + println("group:'$name' \t=> [${re.get_group_by_name(txt, name)}] \ + bounds: ${re.get_group_bounds_by_name(name)}") + } +} +``` + +Output: + +``` +String: http://www.ciao.mondo/hello/pippo12_/pera.html +Query : #0(?Phttps?)|{8,14}#0(?Pftps?)://#1(?P[\w_]+.)+ +Match (0, 46) => [http://www.ciao.mondo/hello/pippo12_/pera.html] +group:'format' => [http] bounds: (0, 4) +group:'token' => [html] bounds: (42, 46) +``` + +In order to simplify the use of the named groups, it is possible to +use a name map in the `re` struct, using the function `re.get_group_by_name`. + +Here is a more complex example of using them: +```v oksyntax +// This function demostrate the use of the named groups +fn convert_html_rgb_n(in_col string) u32 { + mut n_digit := if in_col.len == 4 { 1 } else { 2 } + mut col_mul := if in_col.len == 4 { 4 } else { 0 } + query := '#(?P[a-fA-F0-9]{$n_digit})' + '(?P[a-fA-F0-9]{$n_digit})' + + '(?P[a-fA-F0-9]{$n_digit})' + mut re := regex.regex_opt(query) or { panic(err) } + start, end := re.match_string(in_col) + println('start: $start, end: $end') + mut res := u32(0) + if start >= 0 { + red_s, red_e := re.get_group_by_name('red') + r := ('0x' + in_col[red_s..red_e]).int() << col_mul + green_s, green_e := re.get_group_by_name('green') + g := ('0x' + in_col[green_s..green_e]).int() << col_mul + blue_s, blue_e := re.get_group_by_name('blue') + b := ('0x' + in_col[blue_s..blue_e]).int() << col_mul + println('r: $r g: $g b: $b') + res = u32(r) << 16 | u32(g) << 8 | u32(b) + } + return res +} +``` + +Other utilities are `get_group_by_name` and `get_group_bounds_by_name`, +that return the string of a group using its `name`: + +```v ignore +txt := "my used string...." +for name in re.group_map.keys() { + println("group:'$name' \t=> [${re.get_group_by_name(txt, name)}] \ + bounds: ${re.get_group_bounds_by_name(name)}") +} +``` + + + +### Groups query functions + +These functions are helpers to query the captured groups + +```v ignore +// get_group_bounds_by_name get a group boundaries by its name +pub fn (re RE) get_group_bounds_by_name(group_name string) (int, int) + +// get_group_by_name get a group string by its name +pub fn (re RE) get_group_by_name(group_name string) string + +// get_group_by_id get a group boundaries by its id +pub fn (re RE) get_group_bounds_by_id(group_id int) (int,int) + +// get_group_by_id get a group string by its id +pub fn (re RE) get_group_by_id(in_txt string, group_id int) string + +struct Re_group { +pub: + start int = -1 + end int = -1 +} + +// get_group_list return a list of Re_group for the found groups +pub fn (re RE) get_group_list() []Re_group +``` + +## Flags + +It is possible to set some flags in the regex parser, that change +the behavior of the parser itself. + +```v ignore +// example of flag settings +mut re := regex.new() +re.flag = regex.F_BIN +``` + +- `F_BIN`: parse a string as bytes, utf-8 management disabled. + +- `F_EFM`: exit on the first char matches in the query, used by the + find function. + +- `F_MS`: matches only if the index of the start match is 0, + same as `^` at the start of the query string. + +- `F_ME`: matches only if the end index of the match is the last char + of the input string, same as `$` end of query string. + +- `F_NL`: stop the matching if found a new line char `\n` or `\r` + +## Functions + +### Initializer + +These functions are helper that create the `RE` struct, +a `RE` struct can be created manually if you needed. + +#### **Simplified initializer** + +```v ignore +// regex create a regex object from the query string and compile it +pub fn regex_opt(in_query string) ?RE +``` + +#### **Base initializer** + +```v ignore +// new_regex create a REgex of small size, usually sufficient for ordinary use +pub fn new() RE + +``` +#### **Custom initialization** +For some particular needs, it is possible to initialize a fully customized regex: +```v ignore +pattern = r"ab(.*)(ac)" +// init custom regex +mut re := regex.RE{} +// max program length, can not be longer then the pattern +re.prog = []Token {len: pattern.len + 1} +// can not be more char class the the length of the pattern +re.cc = []CharClass{len: pattern.len} + +re.group_csave_flag = false // true enable continuos group saving if needed +re.group_max_nested = 128 // set max 128 group nested possible +re.group_max = pattern.len>>1 // we can't have more groups than the half of the pattern legth +re.group_stack = []int{len: re.group_max, init: -1} +re.group_data = []int{len: re.group_max, init: -1} +``` +### Compiling + +After an initializer is used, the regex expression must be compiled with: + +```v ignore +// compile compiles the REgex returning an error if the compilation fails +pub fn (re mut RE) compile_opt(in_txt string) ? +``` + +### Matching Functions + +These are the matching functions + +```v ignore +// match_string try to match the input string, return start and end index if found else start is -1 +pub fn (re mut RE) match_string(in_txt string) (int,int) + +``` + +## Find and Replace + +There are the following find and replace functions: + +#### Find functions + +```v ignore +// find try to find the first match in the input string +// return start and end index if found else start is -1 +pub fn (re mut RE) find(in_txt string) (int,int) + +// find_all find all the "non overlapping" occurrences of the matching pattern +// return a list of start end indexes like: [3,4,6,8] +// the matches are [3,4] and [6,8] +pub fn (re mut RE) find_all(in_txt string) []int + +// find_all find all the "non overlapping" occurrences of the matching pattern +// return a list of strings +// the result is like ["first match","secon match"] +pub fn (mut re RE) find_all_str(in_txt string) []string +``` + +#### Replace functions + +```v ignore +// replace return a string where the matches are replaced with the repl_str string, +// this function support groups in the replace string +pub fn (re mut RE) replace(in_txt string, repl string) string +``` + +replace string can include groups references: + +```v ignore +txt := "Today it is a good day." +query := r'(a\w)[ ,.]' +mut re := regex.regex_opt(query)? +res := re.replace(txt, r"__[\0]__") +``` + +in this example we used the group `0` in the replace string: `\0`, the result will be: + +``` +Today it is a good day. => Tod__[ay]__it is a good d__[ay]__ +``` + +**Note:** in the replace strings can be used only groups from `0` to `9`. + +If the usage of `groups` in the replace process, is not needed, it is possible +to use a quick function: + +```v ignore +// replace_simple return a string where the matches are replaced with the replace string +pub fn (mut re RE) replace_simple(in_txt string, repl string) string +``` + +#### Custom replace function + +For complex find and replace operations, you can use `replace_by_fn` . +The `replace_by_fn`, uses a custom replace callback function, thus +allowing customizations. The custom callback function is called for +every non overlapped find. + +The custom callback function must be of the type: + +```v ignore +// type of function used for custom replace +// in_txt source text +// start index of the start of the match in in_txt +// end index of the end of the match in in_txt +// --- the match is in in_txt[start..end] --- +fn (re RE, in_txt string, start int, end int) string +``` + +The following example will clarify its usage: + +```v ignore +import regex +// customized replace functions +// it will be called on each non overlapped find +fn my_repl(re regex.RE, in_txt string, start int, end int) string { + g0 := re.get_group_by_id(in_txt, 0) + g1 := re.get_group_by_id(in_txt, 1) + g2 := re.get_group_by_id(in_txt, 2) + return "*$g0*$g1*$g2*" +} + +fn main(){ + txt := "today [John] is gone to his house with (Jack) and [Marie]." + query := r"(.)(\A\w+)(.)" + + mut re := regex.regex_opt(query) or { panic(err) } + + result := re.replace_by_fn(txt, my_repl) + println(result) +} +``` + +Output: + +``` +today *[*John*]* is gone to his house with *(*Jack*)* and *[*Marie*]*. +``` + + + +## Debugging + +This module has few small utilities to you write regex patterns. + +### **Syntax errors highlight** + +The next example code shows how to visualize regex pattern syntax errors +in the compilation phase: + +```v oksyntax +query := r'ciao da ab[ab-]' +// there is an error, a range not closed!! +mut re := new() +re.compile_opt(query) or { println(err) } +// output!! +// query: ciao da ab[ab-] +// err : ----------^ +// ERROR: ERR_SYNTAX_ERROR +``` + +### **Compiled code** + +It is possible to view the compiled code calling the function `get_query()`. +The result will be something like this: + +``` +======================================== +v RegEx compiler v 1.0 alpha output: +PC: 0 ist: 92000000 ( GROUP_START #:0 { 1, 1} +PC: 1 ist: 98000000 . DOT_CHAR nx chk: 4 { 1, 1} +PC: 2 ist: 94000000 ) GROUP_END #:0 { 1, 1} +PC: 3 ist: 92000000 ( GROUP_START #:1 { 1, 1} +PC: 4 ist: 90000000 [\A] BSLS { 1, 1} +PC: 5 ist: 90000000 [\w] BSLS { 1,MAX} +PC: 6 ist: 94000000 ) GROUP_END #:1 { 1, 1} +PC: 7 ist: 92000000 ( GROUP_START #:2 { 1, 1} +PC: 8 ist: 98000000 . DOT_CHAR nx chk: -1 last! { 1, 1} +PC: 9 ist: 94000000 ) GROUP_END #:2 { 1, 1} +PC: 10 ist: 88000000 PROG_END { 0, 0} +======================================== + +``` + +`PC`:`int` is the program counter or step of execution, each single step is a token. + +`ist`:`hex` is the token instruction id. + +`[a]` is the char used by the token. + +`query_ch` is the type of token. + +`{m,n}` is the quantifier, the greedy off flag `?` will be showed if present in the token + +### **Log debug** + +The log debugger allow to print the status of the regex parser when the +parser is running. It is possible to have two different levels of +debug information: 1 is normal, while 2 is verbose. + +Here is an example: + +*normal* - list only the token instruction with their values + +```ignore +// re.flag = 1 // log level normal +flags: 00000000 +# 2 s: ist_load PC: i,ch,len:[ 0,'a',1] f.m:[ -1, -1] query_ch: [a]{1,1}:0 (#-1) +# 5 s: ist_load PC: i,ch,len:[ 1,'b',1] f.m:[ 0, 0] query_ch: [b]{2,3}:0? (#-1) +# 7 s: ist_load PC: i,ch,len:[ 2,'b',1] f.m:[ 0, 1] query_ch: [b]{2,3}:1? (#-1) +# 10 PROG_END +``` + +*verbose* - list all the instructions and states of the parser + +```ignore +flags: 00000000 +# 0 s: start PC: NA +# 1 s: ist_next PC: NA +# 2 s: ist_load PC: i,ch,len:[ 0,'a',1] f.m:[ -1, -1] query_ch: [a]{1,1}:0 (#-1) +# 3 s: ist_quant_p PC: i,ch,len:[ 1,'b',1] f.m:[ 0, 0] query_ch: [a]{1,1}:1 (#-1) +# 4 s: ist_next PC: NA +# 5 s: ist_load PC: i,ch,len:[ 1,'b',1] f.m:[ 0, 0] query_ch: [b]{2,3}:0? (#-1) +# 6 s: ist_quant_p PC: i,ch,len:[ 2,'b',1] f.m:[ 0, 1] query_ch: [b]{2,3}:1? (#-1) +# 7 s: ist_load PC: i,ch,len:[ 2,'b',1] f.m:[ 0, 1] query_ch: [b]{2,3}:1? (#-1) +# 8 s: ist_quant_p PC: i,ch,len:[ 3,'b',1] f.m:[ 0, 2] query_ch: [b]{2,3}:2? (#-1) +# 9 s: ist_next PC: NA +# 10 PROG_END +# 11 PROG_END +``` + +the columns have the following meaning: + +`# 2` number of actual steps from the start of parsing + +`s: ist_next` state of the present step + +`PC: 1` program counter of the step + +`=>7fffffff ` hex code of the instruction + +`i,ch,len:[ 0,'a',1]` `i` index in the source string, `ch` the char parsed, +`len` the length in byte of the char parsed + +`f.m:[ 0, 1]` `f` index of the first match in the source string, `m` index that is actual matching + +`query_ch: [b]` token in use and its char + +`{2,3}:1?` quantifier `{min,max}`, `:1` is the actual counter of repetition, +`?` is the greedy off flag if present. + +### **Custom Logger output** + +The debug functions output uses the `stdout` as default, +it is possible to provide an alternative output, by setting a custom +output function: + +```v oksyntax +// custom print function, the input will be the regex debug string +fn custom_print(txt string) { + println('my log: $txt') +} + +mut re := new() +re.log_func = custom_print +// every debug output from now will call this function +``` + +## Example code + +Here an example that perform some basically match of strings + +```v ignore +import regex + +fn main(){ + txt := "http://www.ciao.mondo/hello/pippo12_/pera.html" + query := r"(?Phttps?)|(?Pftps?)://(?P[\w_]+.)+" + + mut re := regex.regex_opt(query) or { panic(err) } + + start, end := re.match_string(txt) + if start >= 0 { + println("Match ($start, $end) => [${txt[start..end]}]") + for g_index := 0; g_index < re.group_count ; g_index++ { + println("#${g_index} [${re.get_group_by_id(txt, g_index)}] \ + bounds: ${re.get_group_bounds_by_id(g_index)}") + } + for name in re.group_map.keys() { + println("group:'$name' \t=> [${re.get_group_by_name(txt, name)}] \ + bounds: ${re.get_group_bounds_by_name(name)}") + } + } else { + println("No Match") + } +} +``` +Here an example of total customization of the regex environment creation: +```v ignore +import regex + +fn main(){ + txt := "today John is gone to his house with Jack and Marie." + query := r"(?:(?P\A\w+)|(?:\a\w+)[\s.]?)+" + + // init regex + mut re := regex.RE{} + // max program length, can not be longer then the query + re.prog = []regex.Token {len: query.len + 1} + // can not be more char class the the length of the query + re.cc = []regex.CharClass{len: query.len} + re.prog = []regex.Token {len: query.len+1} + // enable continuos group saving + re.group_csave_flag = true + // set max 128 group nested + re.group_max_nested = 128 + // we can't have more groups than the half of the query legth + re.group_max = query.len>>1 + + // compile the query + re.compile_opt(query) or { panic(err) } + + start, end := re.match_string(txt) + if start >= 0 { + println("Match ($start, $end) => [${txt[start..end]}]") + } else { + println("No Match") + } + + // show results for continuos group saving + if re.group_csave_flag == true && start >= 0 && re.group_csave.len > 0{ + println("cg: $re.group_csave") + mut cs_i := 1 + for cs_i < re.group_csave[0]*3 { + g_id := re.group_csave[cs_i] + st := re.group_csave[cs_i+1] + en := re.group_csave[cs_i+2] + println("cg[$g_id] $st $en:[${txt[st..en]}]") + cs_i += 3 + } + } + + // show results for captured groups + if start >= 0 { + println("Match ($start, $end) => [${txt[start..end]}]") + for g_index := 0; g_index < re.group_count ; g_index++ { + println("#${g_index} [${re.get_group_by_id(txt, g_index)}] \ + bounds: ${re.get_group_bounds_by_id(g_index)}") + } + for name in re.group_map.keys() { + println("group:'$name' \t=> [${re.get_group_by_name(txt, name)}] \ + bounds: ${re.get_group_bounds_by_name(name)}") + } + } else { + println("No Match") + } +} +``` + +More examples are available in the test code for the `regex` module, +see `vlib/regex/regex_test.v`. diff --git a/v_windows/v/old/vlib/regex/regex.v b/v_windows/v/old/vlib/regex/regex.v new file mode 100644 index 0000000..12e86da --- /dev/null +++ b/v_windows/v/old/vlib/regex/regex.v @@ -0,0 +1,2322 @@ +/* +regex 1.0 alpha + +Copyright (c) 2019-2021 Dario Deledda. All rights reserved. +Use of this source code is governed by an MIT license +that can be found in the LICENSE file. + +This file contains regex module + +Know limitation: +- find is implemented in a trivial way +- not full compliant PCRE +- not compliant POSIX ERE +*/ +module regex + +import strings + +pub const ( + v_regex_version = '1.0 alpha' // regex module version + + max_code_len = 256 // default small base code len for the regex programs + max_quantifier = 1073741824 // default max repetitions allowed for the quantifiers = 2^30 + // spaces chars (here only westerns!!) TODO: manage all the spaces from unicode + spaces = [` `, `\t`, `\n`, `\r`, `\v`, `\f`] + // new line chars for now only '\n' + new_line_list = [`\n`, `\r`] + + // Results + no_match_found = -1 + + // Errors + compile_ok = 0 // the regex string compiled, all ok + err_char_unknown = -2 // the char used is unknow to the system + err_undefined = -3 // the compiler symbol is undefined + err_internal_error = -4 // Bug in the regex system!! + err_cc_alloc_overflow = -5 // memory for char class full!! + err_syntax_error = -6 // syntax error in regex compiling + err_groups_overflow = -7 // max number of groups reached + err_groups_max_nested = -8 // max number of nested group reached + err_group_not_balanced = -9 // group not balanced + err_group_qm_notation = -10 // group invalid notation +) + +const ( + //************************************* + // regex program instructions + //************************************* + ist_simple_char = u32(0x7FFFFFFF) // single char instruction, 31 bit available to char + // char class 11 0100 AA xxxxxxxx + // AA = 00 regular class + // AA = 01 Negated class ^ char + ist_char_class = 0xD1000000 // MASK + ist_char_class_pos = 0xD0000000 // char class normal [abc] + ist_char_class_neg = 0xD1000000 // char class negate [^abc] + // dot char 10 0110 xx xxxxxxxx + ist_dot_char = 0x98000000 // match any char except \n + // backslash chars 10 0100 xx xxxxxxxx + ist_bsls_char = 0x90000000 // backslash char + // OR | 10 010Y xx xxxxxxxx + ist_or_branch = 0x91000000 // OR case + // groups 10 010Y xx xxxxxxxx + ist_group_start = 0x92000000 // group start ( + ist_group_end = 0x94000000 // group end ) + // control instructions + ist_prog_end = u32(0x88000000) // 10 0010 xx xxxxxxxx + //************************************* +) + +/* +General Utilities +*/ +// utf8util_char_len calculate the length in bytes of a utf8 char +[inline] +fn utf8util_char_len(b byte) int { + return ((0xe5000000 >> ((b >> 3) & 0x1e)) & 3) + 1 +} + +// get_char get a char from position i and return an u32 with the unicode code +[direct_array_access; inline] +fn (re RE) get_char(in_txt string, i int) (u32, int) { + ini := unsafe { in_txt.str[i] } + // ascii 8 bit + if (re.flag & regex.f_bin) != 0 || ini & 0x80 == 0 { + return u32(ini), 1 + } + // unicode char + char_len := utf8util_char_len(ini) + mut tmp := 0 + mut ch := u32(0) + for tmp < char_len { + ch = (ch << 8) | unsafe { in_txt.str[i + tmp] } + tmp++ + } + return ch, char_len +} + +// get_charb get a char from position i and return an u32 with the unicode code +[direct_array_access; inline] +fn (re RE) get_charb(in_txt &byte, i int) (u32, int) { + // ascii 8 bit + if (re.flag & regex.f_bin) != 0 || unsafe { in_txt[i] } & 0x80 == 0 { + return u32(unsafe { in_txt[i] }), 1 + } + // unicode char + char_len := utf8util_char_len(unsafe { in_txt[i] }) + mut tmp := 0 + mut ch := u32(0) + for tmp < char_len { + ch = (ch << 8) | unsafe { in_txt[i + tmp] } + tmp++ + } + return ch, char_len +} + +[inline] +fn is_alnum(in_char byte) bool { + mut tmp := in_char - `A` + if tmp <= 25 { + return true + } + tmp = in_char - `a` + if tmp <= 25 { + return true + } + tmp = in_char - `0` + if tmp <= 9 { + return true + } + if tmp == `_` { + return true + } + return false +} + +[inline] +fn is_not_alnum(in_char byte) bool { + return !is_alnum(in_char) +} + +[inline] +fn is_space(in_char byte) bool { + return in_char in regex.spaces +} + +[inline] +fn is_not_space(in_char byte) bool { + return !is_space(in_char) +} + +[inline] +fn is_digit(in_char byte) bool { + tmp := in_char - `0` + return tmp <= 0x09 +} + +[inline] +fn is_not_digit(in_char byte) bool { + return !is_digit(in_char) +} + +[inline] +fn is_wordchar(in_char byte) bool { + return is_alnum(in_char) || in_char == `_` +} + +[inline] +fn is_not_wordchar(in_char byte) bool { + return !is_alnum(in_char) +} + +[inline] +fn is_lower(in_char byte) bool { + tmp := in_char - `a` + return tmp <= 25 +} + +[inline] +fn is_upper(in_char byte) bool { + tmp := in_char - `A` + return tmp <= 25 +} + +pub fn (re RE) get_parse_error_string(err int) string { + match err { + regex.compile_ok { return 'compile_ok' } + regex.no_match_found { return 'no_match_found' } + regex.err_char_unknown { return 'err_char_unknown' } + regex.err_undefined { return 'err_undefined' } + regex.err_internal_error { return 'err_internal_error' } + regex.err_cc_alloc_overflow { return 'err_cc_alloc_overflow' } + regex.err_syntax_error { return 'err_syntax_error' } + regex.err_groups_overflow { return 'err_groups_overflow' } + regex.err_groups_max_nested { return 'err_groups_max_nested' } + regex.err_group_not_balanced { return 'err_group_not_balanced' } + regex.err_group_qm_notation { return 'err_group_qm_notation' } + else { return 'err_unknown' } + } +} + +// utf8_str convert and utf8 sequence to a printable string +[inline] +fn utf8_str(ch rune) string { + mut i := 4 + mut res := '' + for i > 0 { + v := byte((ch >> ((i - 1) * 8)) & 0xFF) + if v != 0 { + res += '${v:1c}' + } + i-- + } + return res +} + +// simple_log default log function +fn simple_log(txt string) { + print(txt) +} + +/****************************************************************************** +* +* Token Structs +* +******************************************************************************/ +pub type FnValidator = fn (byte) bool + +struct Token { +mut: + ist rune + // char + ch rune // char of the token if any + ch_len byte // char len + // Quantifiers / branch + rep_min int // used also for jump next in the OR branch [no match] pc jump + rep_max int // used also for jump next in the OR branch [ match] pc jump + greedy bool // greedy quantifier flag + // Char class + cc_index int = -1 + // counters for quantifier check (repetitions) + rep int + // validator function pointer + validator FnValidator + // groups variables + group_rep int // repetition of the group + group_id int = -1 // id of the group + goto_pc int = -1 // jump to this PC if is needed + // OR flag for the token + next_is_or bool // true if the next token is an OR + // dot_char token variables + dot_check_pc int = -1 // pc of the next token to check + last_dot_flag bool // if true indicate that is the last dot_char in the regex +} + +[inline] +fn (mut tok Token) reset() { + tok.rep = 0 +} + +/****************************************************************************** +* +* Regex struct +* +******************************************************************************/ +pub const ( + f_nl = 0x00000001 // end the match when find a new line symbol + f_ms = 0x00000002 // match true only if the match is at the start of the string + f_me = 0x00000004 // match true only if the match is at the end of the string + + f_efm = 0x00000100 // exit on first token matched, used by search + f_bin = 0x00000200 // work only on bytes, ignore utf-8 + // behaviour modifier flags + f_src = 0x00020000 // search mode enabled +) + +struct StateDotObj { +mut: + i int = -1 // char index in the input buffer + pc int = -1 // program counter saved + mi int = -1 // match_index saved + group_stack_index int = -1 // continuous save on capturing groups +} + +pub type FnLog = fn (string) + +pub struct RE { +pub mut: + prog []Token + prog_len int // regex program len + // char classes storage + cc []CharClass // char class list + cc_index int // index + // groups + group_count int // number of groups in this regex struct + groups []int // groups index results + group_max_nested int = 3 // max nested group + group_max int = 8 // max allowed number of different groups + + state_list []StateObj + + group_csave_flag bool // flag to enable continuous saving + group_csave []int //= []int{} // groups continuous save list + + group_map map[string]int // groups names map + + group_stack []int + group_data []int + // flags + flag int // flag for optional parameters + // Debug/log + debug int // enable in order to have the unroll of the code 0 = NO_DEBUG, 1 = LIGHT 2 = VERBOSE + log_func FnLog = simple_log // log function, can be customized by the user + query string // query string +} + +// Reset RE object +[direct_array_access; inline] +fn (mut re RE) reset() { + re.cc_index = 0 + + mut i := 0 + for i < re.prog_len { + re.prog[i].group_rep = 0 // clear repetition of the group + re.prog[i].rep = 0 // clear repetition of the token + i++ + } + + // init groups array + if re.group_count > 0 { + re.groups = []int{len: re.group_count * 2, init: -1} + } + + // reset group_csave + if re.group_csave_flag == true { + re.group_csave.clear() // = []int{} + } + + // reset state list + re.state_list.clear() + re.group_stack.clear() +} + +// reset for search mode fail +// gcc bug, dont use [inline] or go 5 time slower +//[inline] +[direct_array_access] +fn (mut re RE) reset_src() { + mut i := 0 + for i < re.prog_len { + re.prog[i].group_rep = 0 // clear repetition of the group + re.prog[i].rep = 0 // clear repetition of the token + i++ + } +} + +/****************************************************************************** +* +* Backslashes chars +* +******************************************************************************/ +struct BslsStruct { + ch rune // meta char + validator FnValidator // validator function pointer +} + +const ( + bsls_validator_array = [ + BslsStruct{`w`, is_alnum}, + BslsStruct{`W`, is_not_alnum}, + BslsStruct{`s`, is_space}, + BslsStruct{`S`, is_not_space}, + BslsStruct{`d`, is_digit}, + BslsStruct{`D`, is_not_digit}, + BslsStruct{`a`, is_lower}, + BslsStruct{`A`, is_upper}, + ] + + // these chars are escape if preceded by a \ + bsls_escape_list = [`\\`, `|`, `.`, `:`, `*`, `+`, `-`, `{`, `}`, `[`, `]`, `(`, `)`, `?`, + `^`, `!`] +) + +enum BSLS_parse_state { + start + bsls_found + bsls_char + normal_char +} + +// parse_bsls return (index, str_len) bsls_validator_array index, len of the backslash sequence if present +fn (re RE) parse_bsls(in_txt string, in_i int) (int, int) { + mut status := BSLS_parse_state.start + mut i := in_i + + for i < in_txt.len { + // get our char + char_tmp, char_len := re.get_char(in_txt, i) + ch := byte(char_tmp) + + if status == .start && ch == `\\` { + status = .bsls_found + i += char_len + continue + } + + // check if is our bsls char, for now only one length sequence + if status == .bsls_found { + for c, x in regex.bsls_validator_array { + if x.ch == ch { + return c, i - in_i + 1 + } + } + status = .normal_char + continue + } + + // no BSLS validator, manage as normal escape char char + if status == .normal_char { + if ch in regex.bsls_escape_list { + return regex.no_match_found, i - in_i + 1 + } + return regex.err_syntax_error, i - in_i + 1 + } + + // at the present time we manage only one char after the \ + break + } + // not our bsls return KO + return regex.err_syntax_error, i +} + +/****************************************************************************** +* +* Char class +* +******************************************************************************/ +const ( + cc_null = 0 // empty cc token + cc_char = 1 // simple char: a + cc_int = 2 // char interval: a-z + cc_bsls = 3 // backslash char + cc_end = 4 // cc sequence terminator +) + +struct CharClass { +mut: + cc_type int = regex.cc_null // type of cc token + ch0 rune // first char of the interval a-b a in this case + ch1 rune // second char of the interval a-b b in this case + validator FnValidator // validator function pointer +} + +enum CharClass_parse_state { + start + in_char + in_bsls + separator + finish +} + +fn (re RE) get_char_class(pc int) string { + buf := []byte{len: (re.cc.len)} + mut buf_ptr := unsafe { &byte(&buf) } + + mut cc_i := re.prog[pc].cc_index + mut i := 0 + mut tmp := 0 + for cc_i >= 0 && cc_i < re.cc.len && re.cc[cc_i].cc_type != regex.cc_end { + if re.cc[cc_i].cc_type == regex.cc_bsls { + unsafe { + buf_ptr[i] = `\\` + i++ + buf_ptr[i] = byte(re.cc[cc_i].ch0) + i++ + } + } else if re.cc[cc_i].ch0 == re.cc[cc_i].ch1 { + tmp = 3 + for tmp >= 0 { + x := byte((re.cc[cc_i].ch0 >> (tmp * 8)) & 0xFF) + if x != 0 { + unsafe { + buf_ptr[i] = x + i++ + } + } + tmp-- + } + } else { + tmp = 3 + for tmp >= 0 { + x := byte((re.cc[cc_i].ch0 >> (tmp * 8)) & 0xFF) + if x != 0 { + unsafe { + buf_ptr[i] = x + i++ + } + } + tmp-- + } + unsafe { + buf_ptr[i] = `-` + i++ + } + tmp = 3 + for tmp >= 0 { + x := byte((re.cc[cc_i].ch1 >> (tmp * 8)) & 0xFF) + if x != 0 { + unsafe { + buf_ptr[i] = x + i++ + } + } + tmp-- + } + } + cc_i++ + } + unsafe { + buf_ptr[i] = byte(0) + } + return unsafe { tos_clone(buf_ptr) } +} + +fn (re RE) check_char_class(pc int, ch rune) bool { + mut cc_i := re.prog[pc].cc_index + for cc_i >= 0 && cc_i < re.cc.len && re.cc[cc_i].cc_type != regex.cc_end { + if re.cc[cc_i].cc_type == regex.cc_bsls { + if re.cc[cc_i].validator(byte(ch)) { + return true + } + } else if ch >= re.cc[cc_i].ch0 && ch <= re.cc[cc_i].ch1 { + return true + } + cc_i++ + } + return false +} + +// parse_char_class return (index, str_len, cc_type) of a char class [abcm-p], char class start after the [ char +fn (mut re RE) parse_char_class(in_txt string, in_i int) (int, int, rune) { + mut status := CharClass_parse_state.start + mut i := in_i + + mut tmp_index := re.cc_index + res_index := re.cc_index + + mut cc_type := u32(regex.ist_char_class_pos) + + for i < in_txt.len { + // check if we are out of memory for char classes + if tmp_index >= re.cc.len { + return regex.err_cc_alloc_overflow, 0, u32(0) + } + + // get our char + char_tmp, char_len := re.get_char(in_txt, i) + ch := byte(char_tmp) + + // println("CC #${i:3d} ch: ${ch:c}") + + // negation + if status == .start && ch == `^` { + cc_type = u32(regex.ist_char_class_neg) + i += char_len + continue + } + + // minus symbol + if status == .start && ch == `-` { + re.cc[tmp_index].cc_type = regex.cc_char + re.cc[tmp_index].ch0 = char_tmp + re.cc[tmp_index].ch1 = char_tmp + i += char_len + tmp_index++ + continue + } + + // bsls + if (status == .start || status == .in_char) && ch == `\\` { + // println("CC bsls.") + status = .in_bsls + i += char_len + continue + } + + if status == .in_bsls { + // println("CC bsls validation.") + for c, x in regex.bsls_validator_array { + if x.ch == ch { + // println("CC bsls found [${ch:c}]") + re.cc[tmp_index].cc_type = regex.cc_bsls + re.cc[tmp_index].ch0 = regex.bsls_validator_array[c].ch + re.cc[tmp_index].ch1 = regex.bsls_validator_array[c].ch + re.cc[tmp_index].validator = regex.bsls_validator_array[c].validator + i += char_len + tmp_index++ + status = .in_char + break + } + } + if status == .in_bsls { + // manage as a simple char + // println("CC bsls not found [${ch:c}]") + re.cc[tmp_index].cc_type = regex.cc_char + re.cc[tmp_index].ch0 = char_tmp + re.cc[tmp_index].ch1 = char_tmp + i += char_len + tmp_index++ + status = .in_char + continue + } else { + continue + } + } + + // simple char + if (status == .start || status == .in_char) && ch != `-` && ch != `]` { + status = .in_char + + re.cc[tmp_index].cc_type = regex.cc_char + re.cc[tmp_index].ch0 = char_tmp + re.cc[tmp_index].ch1 = char_tmp + + i += char_len + tmp_index++ + continue + } + + // check range separator + if status == .in_char && ch == `-` { + status = .separator + i += char_len + continue + } + + // check range end + if status == .separator && ch != `]` && ch != `-` { + status = .in_char + re.cc[tmp_index - 1].cc_type = regex.cc_int + re.cc[tmp_index - 1].ch1 = char_tmp + i += char_len + continue + } + + // char class end + if status == .in_char && ch == `]` { + re.cc[tmp_index].cc_type = regex.cc_end + re.cc[tmp_index].ch0 = 0 + re.cc[tmp_index].ch1 = 0 + re.cc_index = tmp_index + 1 + + return res_index, i - in_i + 2, cc_type + } + + i++ + } + return regex.err_syntax_error, 0, u32(0) +} + +/****************************************************************************** +* +* Re Compiler +* +******************************************************************************/ +// +// Quantifier +// +enum Quant_parse_state { + start + min_parse + comma_checked + max_parse + greedy + gredy_parse + finish +} + +// parse_quantifier return (min, max, str_len, greedy_flag) of a {min,max}? quantifier starting after the { char +fn (re RE) parse_quantifier(in_txt string, in_i int) (int, int, int, bool) { + mut status := Quant_parse_state.start + mut i := in_i + + mut q_min := 0 // default min in a {} quantifier is 1 + mut q_max := 0 // deafult max in a {} quantifier is max_quantifier + + mut ch := byte(0) + + for i < in_txt.len { + unsafe { + ch = in_txt.str[i] + } + // println("${ch:c} status: $status") + + // exit on no compatible char with {} quantifier + if utf8util_char_len(ch) != 1 { + return regex.err_syntax_error, i, 0, false + } + + // min parsing skip if comma present + if status == .start && ch == `,` { + q_min = 0 // default min in a {} quantifier is 0 + status = .comma_checked + i++ + continue + } + + if status == .start && is_digit(ch) { + status = .min_parse + q_min *= 10 + q_min += int(ch - `0`) + i++ + continue + } + + if status == .min_parse && is_digit(ch) { + q_min *= 10 + q_min += int(ch - `0`) + i++ + continue + } + + // we have parsed the min, now check the max + if status == .min_parse && ch == `,` { + status = .comma_checked + i++ + continue + } + + // single value {4} + if status == .min_parse && ch == `}` { + q_max = q_min + status = .greedy + continue + } + + // end without max + if status == .comma_checked && ch == `}` { + q_max = regex.max_quantifier + status = .greedy + continue + } + + // start max parsing + if status == .comma_checked && is_digit(ch) { + status = .max_parse + q_max *= 10 + q_max += int(ch - `0`) + i++ + continue + } + + // parse the max + if status == .max_parse && is_digit(ch) { + q_max *= 10 + q_max += int(ch - `0`) + i++ + continue + } + + // finished the quantifier + if status == .max_parse && ch == `}` { + status = .greedy + continue + } + + // check if greedy flag char ? is present + if status == .greedy { + if i + 1 < in_txt.len { + i++ + status = .gredy_parse + continue + } + return q_min, q_max, i - in_i + 2, false + } + + // check the greedy flag + if status == .gredy_parse { + if ch == `?` { + return q_min, q_max, i - in_i + 2, true + } else { + i-- + return q_min, q_max, i - in_i + 2, false + } + } + + // not a {} quantifier, exit + return regex.err_syntax_error, i, 0, false + } + + // not a conform {} quantifier + return regex.err_syntax_error, i, 0, false +} + +// +// Groups +// +enum Group_parse_state { + start + q_mark // (? + q_mark1 // (?:|P checking + p_status // (?P + p_start // (?P< + p_end // (?P<...> + p_in_name // (?P<... + finish +} + +// parse_groups parse a group for ? (question mark) syntax, if found, return (error, capture_flag, name_of_the_group, next_index) +fn (re RE) parse_groups(in_txt string, in_i int) (int, bool, string, int) { + mut status := Group_parse_state.start + mut i := in_i + mut name := '' + + for i < in_txt.len && status != .finish { + // get our char + char_tmp, char_len := re.get_char(in_txt, i) + ch := byte(char_tmp) + + // start + if status == .start && ch == `(` { + status = .q_mark + i += char_len + continue + } + + // check for question marks + if status == .q_mark && ch == `?` { + status = .q_mark1 + i += char_len + continue + } + + // non capturing group + if status == .q_mark1 && ch == `:` { + i += char_len + return 0, false, name, i + } + + // enter in P section + if status == .q_mark1 && ch == `P` { + status = .p_status + i += char_len + continue + } + + // not a valid q mark found + if status == .q_mark1 { + // println("NO VALID Q MARK") + return -2, true, name, i + } + + if status == .p_status && ch == `<` { + status = .p_start + i += char_len + continue + } + + if status == .p_start && ch != `>` { + status = .p_in_name + name += '${ch:1c}' // TODO: manage utf8 chars + i += char_len + continue + } + + // colect name + if status == .p_in_name && ch != `>` && is_alnum(ch) { + name += '${ch:1c}' // TODO: manage utf8 chars + i += char_len + continue + } + + // end name + if status == .p_in_name && ch == `>` { + i += char_len + return 0, true, name, i + } + + // error on name group + if status == .p_in_name { + return -2, true, name, i + } + + // normal group, nothig to do, exit + return 0, true, name, i + } + // UNREACHABLE + // println("ERROR!! NOT MEANT TO BE HERE!!1") + return -2, true, name, i +} + +// +// main compiler +// +// compile return (return code, index) where index is the index of the error in the query string if return code is an error code +fn (mut re RE) impl_compile(in_txt string) (int, int) { + mut i := 0 // input string index + mut pc := 0 // program counter + + // group management variables + mut group_count := -1 + mut group_stack := []int{len: re.group_max_nested, init: 0} + mut group_stack_txt_index := []int{len: re.group_max_nested, init: -1} + mut group_stack_index := -1 + + re.query = in_txt // save the query string + + i = 0 + for i < in_txt.len { + mut char_tmp := u32(0) + mut char_len := 0 + // println("i: ${i:3d} ch: ${in_txt.str[i]:c}") + + char_tmp, char_len = re.get_char(in_txt, i) + + // + // check special cases: $ ^ + // + if char_len == 1 && i == 0 && byte(char_tmp) == `^` { + re.flag = regex.f_ms + i = i + char_len + continue + } + if char_len == 1 && i == (in_txt.len - 1) && byte(char_tmp) == `$` { + re.flag = regex.f_me + i = i + char_len + continue + } + + // ist_group_start + if char_len == 1 && pc >= 0 && byte(char_tmp) == `(` { + // check max groups allowed + if group_count > re.group_max { + return regex.err_groups_overflow, i + 1 + } + group_stack_index++ + + // check max nested groups allowed + if group_stack_index > re.group_max_nested { + return regex.err_groups_max_nested, i + 1 + } + + tmp_res, cgroup_flag, cgroup_name, next_i := re.parse_groups(in_txt, i) + + // manage question mark format error + if tmp_res < -1 { + return regex.err_group_qm_notation, next_i + } + + // println("Parse group: [$tmp_res, $cgroup_flag, ($i,$next_i), '${in_txt[i..next_i]}' ]") + i = next_i + + if cgroup_flag == true { + group_count++ + } + + // calculate the group id + // if it is a named group, recycle the group id + // NOTE: **** the group index is +1 because map return 0 when not found!! **** + mut group_id := group_count + if cgroup_name.len > 0 { + // println("GROUP NAME: ${cgroup_name}") + if cgroup_name in re.group_map { + group_id = re.group_map[cgroup_name] - 1 + group_count-- + } else { + re.group_map[cgroup_name] = group_id + 1 + } + } + + group_stack_txt_index[group_stack_index] = i + group_stack[group_stack_index] = pc + + re.prog[pc].ist = u32(0) | regex.ist_group_start + re.prog[pc].rep_min = 1 + re.prog[pc].rep_max = 1 + + // set the group id + if cgroup_flag == false { + // println("NO CAPTURE GROUP") + re.prog[pc].group_id = -1 + } else { + re.prog[pc].group_id = group_id + } + + pc = pc + 1 + continue + } + + // ist_group_end + if char_len == 1 && pc > 0 && byte(char_tmp) == `)` { + if group_stack_index < 0 { + return regex.err_group_not_balanced, i + 1 + } + + goto_pc := group_stack[group_stack_index] + group_stack_index-- + + re.prog[pc].ist = u32(0) | regex.ist_group_end + re.prog[pc].rep_min = 1 + re.prog[pc].rep_max = 1 + + re.prog[pc].goto_pc = goto_pc // PC where to jump if a group need + re.prog[pc].group_id = re.prog[goto_pc].group_id // id of this group, used for storing data + + re.prog[goto_pc].goto_pc = pc // start goto point to the end group pc + // re.prog[goto_pc].group_id = group_count // id of this group, used for storing data + + pc = pc + 1 + i = i + char_len + continue + } + + // ist_dot_char match any char except the following token + if char_len == 1 && pc >= 0 && byte(char_tmp) == `.` { + re.prog[pc].ist = u32(0) | regex.ist_dot_char + re.prog[pc].rep_min = 1 + re.prog[pc].rep_max = 1 + pc = pc + 1 + i = i + char_len + continue + } + + // OR branch + if char_len == 1 && pc > 0 && byte(char_tmp) == `|` { + // two consecutive ist_dot_char are an error + if pc > 0 && re.prog[pc - 1].ist == regex.ist_or_branch { + return regex.err_syntax_error, i + } + re.prog[pc].ist = u32(0) | regex.ist_or_branch + pc = pc + 1 + i = i + char_len + continue + } + + // Quantifiers + if char_len == 1 && pc > 0 { + mut quant_flag := true + match byte(char_tmp) { + `?` { + // println("q: ${char_tmp:c}") + re.prog[pc - 1].rep_min = 0 + re.prog[pc - 1].rep_max = 1 + } + `+` { + // println("q: ${char_tmp:c}") + re.prog[pc - 1].rep_min = 1 + re.prog[pc - 1].rep_max = regex.max_quantifier + } + `*` { + // println("q: ${char_tmp:c}") + re.prog[pc - 1].rep_min = 0 + re.prog[pc - 1].rep_max = regex.max_quantifier + } + `{` { + min, max, tmp, greedy := re.parse_quantifier(in_txt, i + 1) + // it is a quantifier + if min >= 0 { + // println("{$min,$max}\n str:[${in_txt[i..i+tmp]}] greedy:$greedy") + i = i + tmp + re.prog[pc - 1].rep_min = min + re.prog[pc - 1].rep_max = max + re.prog[pc - 1].greedy = greedy + continue + } else { + return min, i + } + // TODO: decide if the open bracket can be conform without the close bracket + /* + // no conform, parse as normal char + else { + quant_flag = false + } + */ + } + else { + quant_flag = false + } + } + + if quant_flag { + i = i + char_len + continue + } + } + + // IST_CHAR_CLASS_* + if char_len == 1 && pc >= 0 { + if byte(char_tmp) == `[` { + cc_index, tmp, cc_type := re.parse_char_class(in_txt, i + 1) + if cc_index >= 0 { + // println("index: $cc_index str:${in_txt[i..i+tmp]}") + i = i + tmp + re.prog[pc].ist = u32(0) | cc_type + re.prog[pc].cc_index = cc_index + re.prog[pc].rep_min = 1 + re.prog[pc].rep_max = 1 + pc = pc + 1 + continue + } + // cc_class vector memory full + else if cc_index < 0 { + return cc_index, i + } + } + } + + // ist_bsls_char + if char_len == 1 && pc >= 0 { + if byte(char_tmp) == `\\` { + bsls_index, tmp := re.parse_bsls(in_txt, i) + // println("index: $bsls_index str:${in_txt[i..i+tmp]}") + if bsls_index >= 0 { + i = i + tmp + re.prog[pc].ist = u32(0) | regex.ist_bsls_char + re.prog[pc].rep_min = 1 + re.prog[pc].rep_max = 1 + re.prog[pc].validator = regex.bsls_validator_array[bsls_index].validator + re.prog[pc].ch = regex.bsls_validator_array[bsls_index].ch + pc = pc + 1 + continue + } + // this is an escape char, skip the bsls and continue as a normal char + else if bsls_index == regex.no_match_found { + i += char_len + char_tmp, char_len = re.get_char(in_txt, i) + // continue as simple char + } + // if not an escape or a bsls char then it is an error (at least for now!) + else { + return bsls_index, i + tmp + } + } + } + + // ist_simple_char + re.prog[pc].ist = regex.ist_simple_char + re.prog[pc].ch = char_tmp + re.prog[pc].ch_len = byte(char_len) + re.prog[pc].rep_min = 1 + re.prog[pc].rep_max = 1 + // println("char: ${char_tmp:c}") + pc = pc + 1 + + i += char_len + } + + // add end of the program + re.prog[pc].ist = regex.ist_prog_end + re.prog_len = pc + + // check for unbalanced groups + if group_stack_index != -1 { + return regex.err_group_not_balanced, group_stack_txt_index[group_stack_index] + 1 + } + + // check for OR at the end of the program + if pc > 0 && re.prog[pc - 1].ist == regex.ist_or_branch { + return regex.err_syntax_error, in_txt.len + } + + // store the number of groups in the query + re.group_count = group_count + 1 + + //****************************************** + // Post processing + //****************************************** + + // + // manage ist_dot_char + // + + // find the checks for dot chars, if any... + mut pc1 := 0 + mut dot_char_count := 0 + mut last_dot_char_pc := -1 + for pc1 < pc { + if re.prog[pc1].ist == regex.ist_dot_char { + // println("Dot_char pc: $pc1") + last_dot_char_pc = pc1 + dot_char_count++ + mut pc2 := pc1 + 1 + for pc2 < pc { + if re.prog[pc2].ist == regex.ist_dot_char { + return regex.err_syntax_error, 0 + } + if re.prog[pc2].ist !in [rune(regex.ist_prog_end), regex.ist_group_end, + regex.ist_group_start, + ] { + // println("Next dot char check is PC: ${pc2}") + re.prog[pc1].dot_check_pc = pc2 + break + } + pc2++ + } + } + pc1++ + } + + // println("last_dot_char_pc: $last_dot_char_pc") + if last_dot_char_pc >= 0 { + pc1 = last_dot_char_pc + 1 + mut is_last_dot := true + for pc1 < pc { + if re.prog[pc1].ist !in [rune(regex.ist_prog_end), regex.ist_group_end] { + is_last_dot = false + break + } + pc1++ + } + if is_last_dot { + re.prog[last_dot_char_pc].last_dot_flag = true + } + } + + //****************************************** + + // OR branch + // a|b|cd + // d exit point + // a,b,c branches + // set the jump in the right places + pc1 = 0 + for pc1 < pc - 2 { + // println("Here $pc1 ${pc-2}") + // two consecutive OR are a syntax error + if re.prog[pc1 + 1].ist == regex.ist_or_branch + && re.prog[pc1 + 2].ist == regex.ist_or_branch { + return regex.err_syntax_error, i + } + + // manange a|b chains like a|(b)|c|d... + // standard solution + if re.prog[pc1].ist != regex.ist_or_branch && re.prog[pc1 + 1].ist == regex.ist_or_branch + && re.prog[pc1 + 2].ist != regex.ist_or_branch { + re.prog[pc1].next_is_or = true // set that the next token is an OR + re.prog[pc1 + 1].rep_min = pc1 + 2 // failed match jump + + // match jump, if an OR chain the next token will be an OR token + mut pc2 := pc1 + 2 + for pc2 < pc - 1 { + ist := re.prog[pc2].ist + if ist == regex.ist_group_start { + re.prog[pc1 + 1].rep_max = re.prog[pc2].goto_pc + 1 + break + } + if ist != regex.ist_or_branch { + re.prog[pc1 + 1].rep_max = pc2 + 1 + break + } + + pc2++ + } + // special case query of few chars, teh true can't go on the first instruction + if re.prog[pc1 + 1].rep_max == pc1 { + re.prog[pc1 + 1].rep_max = 3 + } + // println("Compile OR postproc. [$pc1,OR ${pc1+1},$pc2]") + pc1 = pc2 + continue + } + + pc1++ + } + + //****************************************** + // DEBUG PRINT REGEX GENERATED CODE + //****************************************** + if re.debug > 0 { + gc := re.get_code() + re.log_func(gc) + } + //****************************************** + + return regex.compile_ok, 0 +} + +// get_code return the compiled code as regex string, note: may be different from the source! +pub fn (re RE) get_code() string { + mut pc1 := 0 + mut res := strings.new_builder(re.cc.len * 2 * re.prog.len) + res.write_string('========================================\nv RegEx compiler v $regex.v_regex_version output:\n') + + mut stop_flag := false + + for pc1 <= re.prog.len { + tk := re.prog[pc1] + res.write_string('PC:${pc1:3d}') + + res.write_string(' ist: ') + res.write_string('${tk.ist:8x}'.replace(' ', '0')) + res.write_string(' ') + ist := tk.ist + if ist == regex.ist_bsls_char { + res.write_string('[\\${tk.ch:1c}] BSLS') + } else if ist == regex.ist_prog_end { + res.write_string('PROG_END') + stop_flag = true + } else if ist == regex.ist_or_branch { + res.write_string('OR ') + } else if ist == regex.ist_char_class_pos { + res.write_string('[${re.get_char_class(pc1)}] CHAR_CLASS_POS') + } else if ist == regex.ist_char_class_neg { + res.write_string('[^${re.get_char_class(pc1)}] CHAR_CLASS_NEG') + } else if ist == regex.ist_dot_char { + res.write_string('. DOT_CHAR nx chk: $tk.dot_check_pc') + if tk.last_dot_flag == true { + res.write_string(' last!') + } + } else if ist == regex.ist_group_start { + res.write_string('( GROUP_START #:$tk.group_id') + if tk.group_id == -1 { + res.write_string(' ?:') + } else { + for x in re.group_map.keys() { + if re.group_map[x] == (tk.group_id + 1) { + res.write_string(' ?P<$x>') + break + } + } + } + } else if ist == regex.ist_group_end { + res.write_string(') GROUP_END #:$tk.group_id') + } else if ist == regex.ist_simple_char { + res.write_string('[${tk.ch:1c}] query_ch') + } + + if tk.rep_max == regex.max_quantifier { + res.write_string(' {${tk.rep_min:3d},MAX}') + } else { + if ist == regex.ist_or_branch { + res.write_string(' if false go: ${tk.rep_min:3d} if true go: ${tk.rep_max:3d}') + } else { + res.write_string(' {${tk.rep_min:3d},${tk.rep_max:3d}}') + } + if tk.greedy == true { + res.write_string('?') + } + } + + res.write_string('\n') + if stop_flag { + break + } + pc1++ + } + + res.write_string('========================================\n') + return res.str() +} + +// get_query return a string with a reconstruction of the query starting from the regex program code +pub fn (re RE) get_query() string { + mut res := strings.new_builder(re.query.len * 2) + + if (re.flag & regex.f_ms) != 0 { + res.write_string('^') + } + + mut i := 0 + for i < re.prog.len && re.prog[i].ist != regex.ist_prog_end && re.prog[i].ist != 0 { + tk := unsafe { &re.prog[i] } + ch := tk.ist + + // GROUP start + if ch == regex.ist_group_start { + if re.debug == 0 { + res.write_string('(') + } else { + if tk.group_id == -1 { + res.write_string('(?:') // non capturing group + } else { + res.write_string('#${tk.group_id}(') + } + } + + for x in re.group_map.keys() { + if re.group_map[x] == (tk.group_id + 1) { + res.write_string('?P<$x>') + break + } + } + + i++ + continue + } + + // GROUP end + if ch == regex.ist_group_end { + res.write_string(')') + } + + // OR branch + if ch == regex.ist_or_branch { + res.write_string('|') + if re.debug > 0 { + res.write_string('{$tk.rep_min,$tk.rep_max}') + } + i++ + continue + } + + // char class + if ch == regex.ist_char_class_neg || ch == regex.ist_char_class_pos { + res.write_string('[') + if ch == regex.ist_char_class_neg { + res.write_string('^') + } + res.write_string('${re.get_char_class(i)}') + res.write_string(']') + } + + // bsls char + if ch == regex.ist_bsls_char { + res.write_string('\\${tk.ch:1c}') + } + + // ist_dot_char + if ch == regex.ist_dot_char { + res.write_string('.') + } + + // char alone + if ch == regex.ist_simple_char { + if byte(ch) in regex.bsls_escape_list { + res.write_string('\\') + } + res.write_string('${tk.ch:c}') + } + + // quantifier + if !(tk.rep_min == 1 && tk.rep_max == 1) { + if tk.rep_min == 0 && tk.rep_max == 1 { + res.write_string('?') + } else if tk.rep_min == 1 && tk.rep_max == regex.max_quantifier { + res.write_string('+') + } else if tk.rep_min == 0 && tk.rep_max == regex.max_quantifier { + res.write_string('*') + } else { + if tk.rep_max == regex.max_quantifier { + res.write_string('{$tk.rep_min,MAX}') + } else { + res.write_string('{$tk.rep_min,$tk.rep_max}') + } + if tk.greedy == true { + res.write_string('?') + } + } + } + i++ + } + if (re.flag & regex.f_me) != 0 { + res.write_string('$') + } + + return res.str() +} + +/****************************************************************************** +* +* Groups saving utilities +* +******************************************************************************/ +[direct_array_access] +fn (mut re RE) group_continuous_save(g_index int) { + if re.group_csave_flag == true { + // continuous save, save until we have space + + // init the first element as counter + if re.group_csave.len == 0 { + re.group_csave << 0 + } + + gi := g_index >> 1 + start := re.groups[g_index] + end := re.groups[g_index + 1] + + // check if we are simply increasing the size ot the found group + if re.group_csave.len >= 4 && gi == re.group_csave[re.group_csave.len - 3] + && start == re.group_csave[re.group_csave.len - 2] { + re.group_csave[re.group_csave.len - 1] = end + return + } + + // otherwise append a new group to the list + + // increment counter + re.group_csave[0]++ + // save the record + re.group_csave << (g_index >> 1) // group id + re.group_csave << re.groups[g_index] // start + re.group_csave << re.groups[g_index + 1] // end + } +} + +/****************************************************************************** +* +* Matching +* +******************************************************************************/ +enum Match_state { + start = 0 + stop + end + new_line + ist_load // load and execute instruction + ist_next // go to next instruction + ist_next_ks // go to next instruction without clenaning the state + ist_quant_p // match positive ,quantifier check + ist_quant_n // match negative, quantifier check + ist_quant_pg // match positive ,group quantifier check + ist_quant_ng // match negative ,group quantifier check +} + +fn state_str(s Match_state) string { + match s { + .start { return 'start' } + .stop { return 'stop' } + .end { return 'end' } + .new_line { return 'new line' } + .ist_load { return 'ist_load' } + .ist_next { return 'ist_next' } + .ist_next_ks { return 'ist_next_ks' } + .ist_quant_p { return 'ist_quant_p' } + .ist_quant_n { return 'ist_quant_n' } + .ist_quant_pg { return 'ist_quant_pg' } + .ist_quant_ng { return 'ist_quant_ng' } + } +} + +struct StateObj { +pub mut: + group_index int = -1 // group id used to know how many groups are open + match_flag bool // indicate if we are in a match condition + match_index int = -1 // index of the last match + first_match int = -1 // index of the first match + pc int = -1 // program counter + i int = -1 // source string index + char_len int // last char legth + last_dot_pc int = -1 // last dot chat pc +} + +[direct_array_access] +pub fn (mut re RE) match_base(in_txt &byte, in_txt_len int) (int, int) { + // result status + mut result := regex.no_match_found // function return + + mut ch := rune(0) // examinated char + mut char_len := 0 // utf8 examinated char len + mut m_state := Match_state.start // start point for the matcher FSM + mut src_end := false + mut last_fnd_pc := -1 + + mut state := StateObj{} // actual state + mut ist := rune(0) // actual instruction + mut l_ist := rune(0) // last matched instruction + + mut step_count := 0 // stats for debug + mut dbg_line := 0 // count debug line printed + + re.reset() + + if re.debug > 0 { + // print header + mut h_buf := strings.new_builder(32) + h_buf.write_string('flags: ') + h_buf.write_string('${re.flag:8x}'.replace(' ', '0')) + h_buf.write_string('\n') + sss := h_buf.str() + re.log_func(sss) + } + + for m_state != .end { + if state.pc >= 0 && state.pc < re.prog.len { + ist = re.prog[state.pc].ist + } else if state.pc >= re.prog.len { + // println("ERROR!! PC overflow!!") + return regex.err_internal_error, state.i + } + + //****************************************** + // DEBUG LOG + //****************************************** + if re.debug > 0 { + mut buf2 := strings.new_builder(re.cc.len + 128) + + // print all the instructions + + // end of the input text + if state.i >= in_txt_len { + buf2.write_string('# ${step_count:3d} END OF INPUT TEXT\n') + sss := buf2.str() + re.log_func(sss) + } else { + // print only the exe instruction + if (re.debug == 1 && m_state == .ist_load) || re.debug == 2 { + if ist == regex.ist_prog_end { + buf2.write_string('# ${step_count:3d} PROG_END\n') + } else if ist == 0 || m_state in [.start, .ist_next, .stop] { + buf2.write_string('# ${step_count:3d} s: ${state_str(m_state):12s} PC: NA\n') + } else { + ch, char_len = re.get_charb(in_txt, state.i) + + buf2.write_string('# ${step_count:3d} s: ${state_str(m_state):12s} PC: ${state.pc:3d}=>') + buf2.write_string('${ist:8x}'.replace(' ', '0')) + buf2.write_string(" i,ch,len:[${state.i:3d},'${utf8_str(ch)}',$char_len] f.m:[${state.first_match:3d},${state.match_index:3d}] ") + + if ist == regex.ist_simple_char { + buf2.write_string('query_ch: [${re.prog[state.pc].ch:1c}]') + } else { + if ist == regex.ist_bsls_char { + buf2.write_string('BSLS [\\${re.prog[state.pc].ch:1c}]') + } else if ist == regex.ist_prog_end { + buf2.write_string('PROG_END') + } else if ist == regex.ist_or_branch { + buf2.write_string('OR') + } else if ist == regex.ist_char_class_pos { + buf2.write_string('CHAR_CLASS_POS[${re.get_char_class(state.pc)}]') + } else if ist == regex.ist_char_class_neg { + buf2.write_string('CHAR_CLASS_NEG[${re.get_char_class(state.pc)}]') + } else if ist == regex.ist_dot_char { + buf2.write_string('DOT_CHAR') + } else if ist == regex.ist_group_start { + tmp_gi := re.prog[state.pc].group_id + tmp_gr := re.prog[re.prog[state.pc].goto_pc].group_rep + buf2.write_string('GROUP_START #:$tmp_gi rep:$tmp_gr ') + } else if ist == regex.ist_group_end { + buf2.write_string('GROUP_END #:${re.prog[state.pc].group_id} deep:$state.group_index') + } + } + if re.prog[state.pc].rep_max == regex.max_quantifier { + buf2.write_string('{${re.prog[state.pc].rep_min},MAX}:${re.prog[state.pc].rep}') + } else { + buf2.write_string('{${re.prog[state.pc].rep_min},${re.prog[state.pc].rep_max}}:${re.prog[state.pc].rep}') + } + if re.prog[state.pc].greedy == true { + buf2.write_string('?') + } + buf2.write_string(' (#$state.group_index)') + + if ist == regex.ist_dot_char { + buf2.write_string(' last!') + } + + buf2.write_string('\n') + } + sss2 := buf2.str() + re.log_func(sss2) + } + } + step_count++ + dbg_line++ + } + //****************************************** + + if ist == regex.ist_prog_end { + // println("HERE we end!") + break + } + + // we're out of text, manage it + if state.i >= in_txt_len || m_state == .new_line { + // println("Finished text!!") + src_end = true + + // manage groups + if state.group_index >= 0 && state.match_index >= 0 { + // println("End text with open groups!") + // close the groups + for state.group_index >= 0 { + tmp_pc := re.group_data[state.group_index] + re.prog[tmp_pc].group_rep++ + // println("Closing group $state.group_index {${re.prog[tmp_pc].rep_min},${re.prog[tmp_pc].rep_max}}:${re.prog[tmp_pc].group_rep}") + + if re.prog[tmp_pc].group_rep >= re.prog[tmp_pc].rep_min + && re.prog[tmp_pc].group_id >= 0 { + start_i := re.group_stack[state.group_index] + re.group_stack[state.group_index] = -1 + + // save group results + g_index := re.prog[tmp_pc].group_id * 2 + if start_i >= 0 { + re.groups[g_index] = start_i + } else { + re.groups[g_index] = 0 + } + // we have fished the text, we must manage out pf bound indexes + if state.i >= in_txt_len { + state.i = in_txt_len - 1 + } + re.groups[g_index + 1] = state.i + + if re.groups[g_index + 1] >= in_txt_len { + // println("clamp group on stop!") + re.groups[g_index + 1] = in_txt_len - 1 + } + + // continuous save, save until we have space + re.group_continuous_save(g_index) + } + state.group_index-- + } + } + + // the text is finished and the groups closed and we are the last group, ok exit + if ist == regex.ist_group_end && re.prog[state.pc + 1].ist == regex.ist_prog_end { + // println("Last group end") + return state.first_match, state.i + } + + if state.pc == -1 { + state.pc = last_fnd_pc + } + + // println("Finished text!!") + // println("Instruction: ${ist:08x} pc: $state.pc") + // println("min_rep: ${re.prog[state.pc].rep_min} max_rep: ${re.prog[state.pc].rep_max} rep: ${re.prog[state.pc].rep}") + + // program end + if ist == regex.ist_prog_end { + // println("Program end on end of text!") + return state.first_match, state.i + } + + // we are in a last dot_ char case + if l_ist == regex.ist_dot_char { + // println("***** We have a last dot_char") + // println("PC: ${state.pc} last_dot_flag:${re.prog[state.pc].last_dot_flag}") + // println("rep: ${re.prog[state.pc].group_rep} min: ${re.prog[state.pc].rep_min} max: ${re.prog[state.pc].rep_max}") + // println("first match: ${state.first_match}") + if re.prog[state.pc].last_dot_flag == true + && re.prog[state.pc].rep >= re.prog[state.pc].rep_min + && re.prog[state.pc].rep <= re.prog[state.pc].rep_max { + return state.first_match, state.i + } + // println("Not fitted!!") + } + + // m_state = .end + // break + return regex.no_match_found, 0 + } + + // starting and init + if m_state == .start { + state.pc = -1 + state.i = 0 + m_state = .ist_next + continue + } + // ist_next, next instruction reseting its state + else if m_state == .ist_next { + state.pc = state.pc + 1 + re.prog[state.pc].reset() + // check if we are in the program bounds + if state.pc < 0 || state.pc > re.prog.len { + // println("ERROR!! PC overflow!!") + return regex.err_internal_error, state.i + } + m_state = .ist_load + continue + } + // ist_next_ks, next instruction keeping its state + else if m_state == .ist_next_ks { + state.pc = state.pc + 1 + // check if we are in the program bounds + if state.pc < 0 || state.pc > re.prog.len { + // println("ERROR!! PC overflow!!") + return regex.err_internal_error, state.i + } + m_state = .ist_load + continue + } + + // load the char + ch, char_len = re.get_charb(in_txt, state.i) + + // check new line if flag f_nl enabled + if (re.flag & regex.f_nl) != 0 && char_len == 1 && byte(ch) in regex.new_line_list { + m_state = .new_line + continue + } + // check if stop + else if m_state == .stop { + // we are in search mode, don't exit until the end + if ((re.flag & regex.f_src) != 0) && (ist != regex.ist_prog_end) { + last_fnd_pc = state.pc + state.pc = -1 + state.i += char_len + + m_state = .ist_next + re.reset_src() + state.match_index = -1 + state.first_match = -1 + + // reset state list + re.reset() + + continue + } + + if ist == regex.ist_prog_end { + return state.first_match, state.i + } + + // manage here dot char + + if re.state_list.len > 0 { + // println("Here we are, with stop: state buffer: [${re.state_list.len}]") + state = re.state_list.pop() + + state.match_flag = true + l_ist = u32(regex.ist_dot_char) + + if state.first_match < 0 { + state.first_match = state.i + } + state.match_index = state.i + re.prog[state.pc].rep++ // increase repetitions + + state.i += char_len + m_state = .ist_quant_p + continue + } + + // exit on no match + return result, 0 + } + // ist_load + else if m_state == .ist_load { + // program end + if ist == regex.ist_prog_end { + // if we are in match exit well + + if state.group_index >= 0 && state.match_index >= 0 { + state.group_index = -1 + } + + m_state = .stop + continue + } + // check GROUP start, no quantifier is checkd for this token!! + else if ist == regex.ist_group_start { + state.group_index++ + re.group_data[state.group_index] = re.prog[state.pc].goto_pc // save where is ist_group_end, we will use it for escape + re.group_stack[state.group_index] = state.i // index where we start to manage + // println("group_index $state.group_index rep ${re.prog[re.prog[state.pc].goto_pc].group_rep}") + + m_state = .ist_next + continue + } + // check GROUP end + else if ist == regex.ist_group_end { + // we are in matching streak + // println("Group END!! last ist: ${l_ist:08x}") + if state.match_index >= 0 { + // restore txt index stack and save the group data + + // println("g.id: ${re.prog[state.pc].group_id} group_index: ${state.group_index}") + if state.group_index >= 0 && re.prog[state.pc].group_id >= 0 { + start_i := re.group_stack[state.group_index] + + // save group results + g_index := re.prog[state.pc].group_id * 2 + + if start_i >= 0 { + re.groups[g_index] = start_i + } else { + re.groups[g_index] = 0 + } + + re.groups[g_index + 1] = state.i + + if g_index > 0 && re.groups[g_index] <= re.groups[g_index - 1] { + re.groups[g_index] = re.groups[g_index - 1] + } + + if re.groups[g_index + 1] >= in_txt_len { + // println("clamp group!") + re.groups[g_index + 1] = in_txt_len - 1 + } + + // println("GROUP ${re.prog[state.pc].group_id} END [${re.groups[g_index]}, ${re.groups[g_index+1]}] i: $state.i in_txt_len: $in_txt_len") + + // continuous save, save until we have space + re.group_continuous_save(g_index) + } + + re.prog[state.pc].group_rep++ // increase repetitions + // println("GROUP $group_index END ${re.prog[state.pc].group_rep}") + m_state = .ist_quant_pg + continue + } + + m_state = .ist_quant_ng + continue + } + // check OR + else if ist == regex.ist_or_branch { + if state.match_index >= 0 { + state.pc = re.prog[state.pc].rep_max + // println("ist_or_branch True pc: $state.pc") + } else { + state.pc = re.prog[state.pc].rep_min + // println("ist_or_branch False pc: $state.pc") + } + re.prog[state.pc].reset() + m_state = .ist_load + continue + } + // check ist_dot_char + else if ist == regex.ist_dot_char { + // println("ist_dot_char rep: ${re.prog[state.pc].rep}") + + // check next token to be false + mut next_check_flag := false + + // if we are done with max go on dot char are dedicated case!! + if re.prog[state.pc].rep >= re.prog[state.pc].rep_max { + re.state_list.pop() + m_state = .ist_next + continue + } + + if re.prog[state.pc].dot_check_pc >= 0 + && re.prog[state.pc].rep >= re.prog[state.pc].rep_min { + // load the char + // ch_t, _ := re.get_charb(in_txt, state.i+char_len) + ch_t := ch + chk_pc := re.prog[state.pc].dot_check_pc + + // simple char + if re.prog[chk_pc].ist == regex.ist_simple_char { + if re.prog[chk_pc].ch == ch_t { + next_check_flag = true + } + // println("Check [ist_simple_char] [${re.prog[chk_pc].ch}]==[${ch_t:c}] => $next_check_flag") + } + // char char_class + else if re.prog[chk_pc].ist == regex.ist_char_class_pos + || re.prog[chk_pc].ist == regex.ist_char_class_neg { + mut cc_neg := false + if re.prog[chk_pc].ist == regex.ist_char_class_neg { + cc_neg = true + } + mut cc_res := re.check_char_class(chk_pc, ch_t) + + if cc_neg { + cc_res = !cc_res + } + next_check_flag = cc_res + // println("Check [ist_char_class] => $next_check_flag") + } + // check bsls + else if re.prog[chk_pc].ist == regex.ist_bsls_char { + next_check_flag = re.prog[chk_pc].validator(byte(ch_t)) + // println("Check [ist_bsls_char] => $next_check_flag") + } + } + + // check if we must continue or pass to the next IST + if next_check_flag == true && re.prog[state.pc + 1].ist != regex.ist_prog_end { + // println("save the state!!") + mut dot_state := StateObj{ + group_index: state.group_index + match_flag: state.match_flag + match_index: state.match_index + first_match: state.first_match + pc: state.pc + i: state.i + char_len + char_len: char_len + last_dot_pc: state.pc + } + // if we are mananging a .* stay on the same char on return + if re.prog[state.pc].rep_min == 0 { + dot_state.i -= char_len + } + + re.state_list << dot_state + + m_state = .ist_quant_n + // println("dot_char stack len: ${re.state_list.len}") + continue + } + + state.match_flag = true + l_ist = u32(regex.ist_dot_char) + + if state.first_match < 0 { + state.first_match = state.i + } + state.match_index = state.i + re.prog[state.pc].rep++ // increase repetitions + + state.i += char_len + m_state = .ist_quant_p + continue + } + // char class IST + else if ist == regex.ist_char_class_pos || ist == regex.ist_char_class_neg { + state.match_flag = false + mut cc_neg := false + + if ist == regex.ist_char_class_neg { + cc_neg = true + } + mut cc_res := re.check_char_class(state.pc, ch) + + if cc_neg { + cc_res = !cc_res + } + + if cc_res { + state.match_flag = true + l_ist = u32(regex.ist_char_class_pos) + + if state.first_match < 0 { + state.first_match = state.i + } + + state.match_index = state.i + + re.prog[state.pc].rep++ // increase repetitions + state.i += char_len // next char + m_state = .ist_quant_p + continue + } + m_state = .ist_quant_n + continue + } + // check bsls + else if ist == regex.ist_bsls_char { + state.match_flag = false + tmp_res := re.prog[state.pc].validator(byte(ch)) + // println("BSLS in_ch: ${ch:c} res: $tmp_res") + if tmp_res { + state.match_flag = true + l_ist = u32(regex.ist_bsls_char) + + if state.first_match < 0 { + state.first_match = state.i + } + + state.match_index = state.i + + re.prog[state.pc].rep++ // increase repetitions + state.i += char_len // next char + m_state = .ist_quant_p + continue + } + m_state = .ist_quant_n + continue + } + // simple char IST + else if ist == regex.ist_simple_char { + // println("ist_simple_char") + state.match_flag = false + + if re.prog[state.pc].ch == ch { + state.match_flag = true + l_ist = regex.ist_simple_char + + if state.first_match < 0 { + state.first_match = state.i + } + // println("state.match_index: ${state.match_index}") + state.match_index = state.i + + re.prog[state.pc].rep++ // increase repetitions + state.i += char_len // next char + m_state = .ist_quant_p + continue + } + m_state = .ist_quant_n + continue + } + // UNREACHABLE + // println("PANIC2!! state: $m_state") + return regex.err_internal_error, state.i + } + /*********************************** + * Quantifier management + ***********************************/ + // ist_quant_ng => quantifier negative test on group + else if m_state == .ist_quant_ng { + // we are finished here + if state.group_index < 0 { + // println("Early stop!") + result = regex.no_match_found + m_state = .stop + continue + } + + tmp_pc := re.group_data[state.group_index] // PC to the end of the group token + rep := re.prog[tmp_pc].group_rep // use a temp variable + re.prog[tmp_pc].group_rep = 0 // clear the repetitions + + // println(".ist_quant_ng group_pc_end: $tmp_pc rep: $rep") + + if rep >= re.prog[tmp_pc].rep_min { + // println("ist_quant_ng GROUP CLOSED OK group_index: $state.group_index") + + state.i = re.group_stack[state.group_index] + state.pc = tmp_pc + state.group_index-- + m_state = .ist_next + continue + } else if re.prog[tmp_pc].next_is_or { + // println("ist_quant_ng OR Negative branch") + + state.i = re.group_stack[state.group_index] + state.pc = re.prog[tmp_pc + 1].rep_min - 1 + state.group_index-- + m_state = .ist_next + continue + } else if rep > 0 && rep < re.prog[tmp_pc].rep_min { + // println("ist_quant_ng UNDER THE MINIMUM g.i: $state.group_index") + + // check if we are inside a group, if yes exit from the nested groups + if state.group_index > 0 { + state.group_index-- + state.pc = tmp_pc + m_state = .ist_quant_ng //.ist_next + continue + } + + if state.group_index == 0 { + state.group_index-- + state.pc = tmp_pc // TEST + m_state = .ist_next + continue + } + + result = regex.no_match_found + m_state = .stop + continue + } else if rep == 0 && rep < re.prog[tmp_pc].rep_min { + // println("ist_quant_ng c_zero UNDER THE MINIMUM g.i: $state.group_index") + + if state.group_index > 0 { + state.group_index-- + state.pc = tmp_pc + m_state = .ist_quant_ng //.ist_next + continue + } + + result = regex.no_match_found + m_state = .stop + continue + } + + // println("DO NOT STAY HERE!! {${re.prog[tmp_pc].rep_min},${re.prog[tmp_pc].rep_max}}:$rep") + // UNREACHABLE + return regex.err_internal_error, state.i + } + // ist_quant_pg => quantifier positive test on group + else if m_state == .ist_quant_pg { + // println(".ist_quant_pg") + mut tmp_pc := state.pc + if state.group_index >= 0 { + tmp_pc = re.group_data[state.group_index] + } + + rep := re.prog[tmp_pc].group_rep + + if rep < re.prog[tmp_pc].rep_min { + // println("ist_quant_pg UNDER RANGE") + state.pc = re.prog[tmp_pc].goto_pc + m_state = .ist_next + continue + } else if rep == re.prog[tmp_pc].rep_max { + // println("ist_quant_pg MAX RANGE") + re.prog[tmp_pc].group_rep = 0 // clear the repetitions + state.group_index-- + m_state = .ist_next + + continue + } else if rep >= re.prog[tmp_pc].rep_min { + // println("ist_quant_pg IN RANGE group_index:$state.group_index") + + // check greedy flag, if true exit on minimum + if re.prog[tmp_pc].greedy == true { + re.prog[tmp_pc].group_rep = 0 // clear the repetitions + state.group_index-- + m_state = .ist_next + continue + } + + state.pc = re.prog[tmp_pc].goto_pc - 1 + state.group_index-- + m_state = .ist_next + continue + } + + // UNREACHABLE + // println("PANIC3!! state: $m_state") + return regex.err_internal_error, state.i + } + // ist_quant_n => quantifier negative test on token + else if m_state == .ist_quant_n { + rep := re.prog[state.pc].rep + // println("Here!! PC $state.pc is_next_or: ${re.prog[state.pc].next_is_or}") + + // zero quantifier * or ? + if rep == 0 && re.prog[state.pc].rep_min == 0 { + // println("ist_quant_n c_zero RANGE MIN") + m_state = .ist_next // go to next ist + continue + } + // match + or * + else if rep >= re.prog[state.pc].rep_min { + // println("ist_quant_n MATCH RANGE") + m_state = .ist_next + continue + } + + // check the OR if present + if re.prog[state.pc].next_is_or { + // println("OR present on failing") + state.match_index = -1 + m_state = .ist_next + continue + } + + // we are in a group manage no match from here + if state.group_index >= 0 { + // println("ist_quant_n FAILED insied a GROUP group_index:$state.group_index") + m_state = .ist_quant_ng + continue + } + + // no other options + // println("ist_quant_n no_match_found") + result = regex.no_match_found + m_state = .stop + continue + // return no_match_found, 0 + } + // ist_quant_p => quantifier positive test on token + else if m_state == .ist_quant_p { + // exit on first match + if (re.flag & regex.f_efm) != 0 { + return state.i, state.i + 1 + } + + rep := re.prog[state.pc].rep + + // under range + if rep > 0 && rep < re.prog[state.pc].rep_min { + // println("ist_quant_p UNDER RANGE") + m_state = .ist_load // continue the loop + continue + } + // range ok, continue loop + else if rep >= re.prog[state.pc].rep_min && rep < re.prog[state.pc].rep_max { + // println("ist_quant_p IN RANGE") + + // check greedy flag, if true exit on minimum + if re.prog[state.pc].greedy == true { + m_state = .ist_next + continue + } + m_state = .ist_load + continue + } + // max reached + else if rep == re.prog[state.pc].rep_max { + // println("ist_quant_p MAX RANGE") + m_state = .ist_next + continue + } + } + // UNREACHABLE + // println("PANIC4!! state: $m_state") + return regex.err_internal_error, state.i + } + + // println("Check end of text!") + // Check the results + if state.match_index >= 0 { + if state.group_index < 0 { + if re.prog[state.pc].ist == regex.ist_prog_end { + // println("program ended!!") + + if (re.flag & regex.f_src) != 0 { + // println("find return") + return state.first_match, state.i + } else { + // println("Here!!") + return 0, state.i + } + } + + // println("No Group here, natural end [$state.first_match,$state.i] state: ${state_str(m_state)} ist: $ist pgr_end: $re.prog.len") + + if re.prog[state.pc + 1].ist == regex.ist_prog_end + || re.prog[state.pc].ist == regex.ist_prog_end { + rep := re.prog[state.pc].rep + // println("rep: $rep re.prog[state.pc].rep_min: ${re.prog[state.pc].rep_min} re.prog[state.pc].rep_max: ${re.prog[state.pc].rep_max}") + if rep >= re.prog[state.pc].rep_min && rep <= re.prog[state.pc].rep_max { + return state.first_match, state.i + } + // println("Program not finished! ") + return regex.no_match_found, 0 + } + if src_end { + // println("program end") + return state.first_match, state.i + } + // print("No match found!!") + return regex.no_match_found, 0 + } else { + // println("Group match! OK") + // println("first_match: $state.first_match, i: $state.i") + + // println("Skip last group") + return state.first_match, state.i + // return state.first_match,re.group_stack[state.group_index--] + } + } + // println("no_match_found, natural end") + return regex.no_match_found, 0 +} diff --git a/v_windows/v/old/vlib/regex/regex_opt.v b/v_windows/v/old/vlib/regex/regex_opt.v new file mode 100644 index 0000000..2aaa88f --- /dev/null +++ b/v_windows/v/old/vlib/regex/regex_opt.v @@ -0,0 +1,53 @@ +module regex + +import strings + +// compile_opt compile RE pattern string +pub fn (mut re RE) compile_opt(pattern string) ? { + re_err, err_pos := re.impl_compile(pattern) + + if re_err != compile_ok { + mut err_msg := strings.new_builder(300) + err_msg.write_string('\nquery: $pattern\n') + line := '-'.repeat(err_pos) + err_msg.write_string('err : $line^\n') + err_str := re.get_parse_error_string(re_err) + err_msg.write_string('ERROR: $err_str\n') + return error_with_code(err_msg.str(), re_err) + } +} + +// new_regex create a RE of small size, usually sufficient for ordinary use +pub fn new() RE { + // init regex + mut re := RE{} + re.prog = []Token{len: max_code_len + 1} // max program length, can not be longer then the pattern + re.cc = []CharClass{len: max_code_len} // can not be more char class the the length of the pattern + re.group_csave_flag = false // enable continuos group saving + re.group_max_nested = 128 // set max 128 group nested + re.group_max = max_code_len >> 1 // we can't have more groups than the half of the pattern legth + + re.group_stack = []int{len: re.group_max, init: -1} + re.group_data = []int{len: re.group_max, init: -1} + + return re +} + +// regex_opt create new RE object from RE pattern string +pub fn regex_opt(pattern string) ?RE { + // init regex + mut re := RE{} + re.prog = []Token{len: pattern.len + 1} // max program length, can not be longer then the pattern + re.cc = []CharClass{len: pattern.len} // can not be more char class the the length of the pattern + re.group_csave_flag = false // enable continuos group saving + re.group_max_nested = 128 // set max 128 group nested + re.group_max = pattern.len >> 1 // we can't have more groups than the half of the pattern legth + + re.group_stack = []int{len: re.group_max, init: -1} + re.group_data = []int{len: re.group_max, init: -1} + + // compile the pattern + re.compile_opt(pattern) ? + + return re +} diff --git a/v_windows/v/old/vlib/regex/regex_test.v b/v_windows/v/old/vlib/regex/regex_test.v new file mode 100644 index 0000000..242dc84 --- /dev/null +++ b/v_windows/v/old/vlib/regex/regex_test.v @@ -0,0 +1,606 @@ +import regex +import rand + +/****************************************************************************** +* +* Test section +* +******************************************************************************/ +struct TestItem { + src string + q string + s int + e int +} + +const( +match_test_suite = [ + // minus in CC + TestItem{"d.def",r"abc.\.[\w\-]{,100}",-1,0}, + TestItem{"abc12345.asd",r"abc.\.[\w\-]{,100}",-1,0}, + TestItem{"abca.exe",r"abc.\.[\w\-]{,100}",0,8}, + TestItem{"abc2.exe-test_12",r"abc.\.[\w\-]{,100}",0,13}, + TestItem{"abcdefGHK",r"[a-f]+\A+",0,9}, + TestItem{"ab-cd-efGHK",r"[a-f\-g]+\A+",0,11}, + + // base OR + TestItem{"a",r"a|b",0,1}, + TestItem{"a",r"b|a",0,1}, + TestItem{"b",r"a|b",0,1}, + TestItem{"b",r"b|a",0,1}, + TestItem{"c",r"b|a",-1,0}, + + // test base + TestItem{"[ciao]",r"(.)ciao(.)",0,6}, + TestItem{"[ciao] da me",r"(.)ciao(.)",0,6}, + + // positive + TestItem{"this is a good.",r"this",0,4}, + TestItem{"this is a good.",r"good",10,14}, + TestItem{"this is a good.",r"go+d",10,14}, + TestItem{"this is a good.",r"g[oae]+d",10,14}, + TestItem{"this is a goed.",r"g[oae]+d",10,14}, + TestItem{"this is a good.",r"g[oae]*d",10,14}, + TestItem{"this is a goaezd.",r"g[ea-cm-z]*d",10,16}, + TestItem{"this is a good.",r"this (\w+) a",0,9}, + TestItem{"this is a good.",r"this( \w+){2} g",0,11}, + TestItem{"this is a good.",r"( ?\w+){,1}",0,4}, + TestItem{"this is a good.",r"( ?\w+)+",0,14}, + TestItem{"this is a good.",r"this( \w+)+",0,14}, + TestItem{"this is a good sample.",r"( ?\w+){,2}",0,7}, + TestItem{"this is a good sample.",r"( ?\w+){,3}",0,9}, + TestItem{"this is a good sample.",r"( ?\w+){,4}",0,14}, + TestItem{"this is a good sample.",r"( ?\w+){,5}",0,21}, + TestItem{"this is a good sample.",r"( ?\w+){2,3}",0,9}, + TestItem{"this is a good sample.",r"(\s?\w+){2,3}",0,9}, + TestItem{"this these those.",r"(th[ei]se?\s|\.)+",0,11}, + TestItem{"this these those ",r"(th[eio]se? ?)+",0,17}, + TestItem{"this these those ",r"(th[eio]se? )+",0,17}, + TestItem{"this,these,those. over",r"(th[eio]se?[,. ])+",0,17}, + TestItem{"soday,this,these,those. over",r".+(th[eio]se?[,. ])+",0,23}, + + TestItem{"cpapaz",r"(c(pa)+z)",0,6}, + TestItem{"this is a cpapaz over",r"(c(pa)+z)",10,16}, + TestItem{"this is a cpapapez over",r"(c(p[ae])+z)",10,18}, + TestItem{"test@post.pip.com",r"[a-z0-9_]+@([a-z0-9_]+\.?)+",0,17}, + TestItem{"test1@post.pip.com, pera",r"[\w]+@([\w]+\.)+\w+",0,18}, + TestItem{"pippo@pera.com ",r"[a-z0-9_]+@([a-z0-9_]+\.?)+",0,14}, + TestItem{"adce aabe",r"(a(ab)+)|(a(dc)+)e",0,4}, + TestItem{"zadce aabe",r"(a(ab)+)|(a(dc)+)e",1,5}, + TestItem{"abbz accz addz.",r"c|(d)|e|(ab+)",0,3}, + TestItem{"this those these ciao",r"((t[hieo]+se?)\s*)+",0,17}, + TestItem{"this ciao",r"((t[hieo]+se?)\s*)+",0,5}, + TestItem{"this cpapaz adce aabe",r"(c(pa)+z)(\s[\a]+){2}",5,21}, + TestItem{"1234this cpapaz adce aabe",r"(c(pa)+z)(\s[\a]+){2}$",9,25}, + TestItem{"this cpapaz adce aabe third",r"(c(pa)+z)(\s[\a]+){2}",5,21}, + TestItem{"123cpapaz ole. pippo",r"(c(pa)+z)(\s+\a+[\.,]?)+",3,20}, + + TestItem{"this is a good sample.",r".*i(\w)+",0,4}, + TestItem{"soday,this,these,those. over",r".*,(th[eio]se?[,. ])+",0,23}, + TestItem{"soday,this,these,thesa.thesi over",r".*,(th[ei]se?[,. ])+(thes[ai][,. ])+",0,29}, + TestItem{"cpapaz ole. pippo,",r".*(c(pa)+z)(\s+\a+[\.,]?)+",0,18}, + TestItem{"cpapaz ole. pippo",r"(c(pa)+z)(\s+\a+[\.,]?)+",0,17}, + TestItem{"cpapaz ole. pippo, 852",r".*(c(pa)+z)(\s+\a+[\.,]?)+",0,18}, + TestItem{"123cpapaz ole. pippo",r".*(c(pa)+z)(\s+\a+[\.,]?)+",0,20}, + TestItem{"...cpapaz ole. pippo",r".*(c(pa)+z)(\s+\a+[\.,]?)+",0,20}, + + TestItem{"cpapaz ole. pippo,",r".*c.+ole.*pi",0,14}, + TestItem{"cpapaz ole. pipipo,",r".*c.+ole.*p([ip])+o",0,18}, + TestItem{"cpapaz ole. pipipo",r"^.*c.+ol?e.*p([ip])+o$",0,18}, + TestItem{"abbb",r"ab{2,3}?",0,3}, + TestItem{" pippo pera",r"\s(.*)pe(.*)",0,11}, + TestItem{" abb",r"\s(.*)",0,4}, + + TestItem{"/home/us_er/pippo/info-01.txt", r"(/?[-\w_]+)*\.txt$",0,29} + + // negative + TestItem{"zthis ciao",r"((t[hieo]+se?)\s*)+",-1,0}, + TestItem{"this is a good.",r"thes",-1,0}, + TestItem{"test1post.pip.com, pera",r"[\w]+@([\w]+\.)+\w+",-1,0}, + TestItem{"this cpapaz adce",r"(c(pa)+z)(\s[\a]+){2}",-1,0}, + TestItem{"this cpapaz adce aabe third",r"(c(pa)+z)(\s[\a]+){2}$",-1,0}, + TestItem{"1234this cpapaz adce aabe ter",r"(c(pa)+z)(\s[\a]+){2}$",-1,0}, + TestItem{"cpapaz ole. pipipo,",r"^.*c.+ol?e.*p([ip])+o$",-1,0}, + TestItem{"/home/us_er/pippo/info-01.jpeg", r"(/?[-\w_]+)*\.txt$",-1,0} + + // check unicode + TestItem{"this is a Ⅰ Ⅱ Ⅲ Ⅳ Ⅴ Ⅵ test",r".*a [Ⅰ-Ⅵ ]+",0,34}, + TestItem{"123Ⅰ Ⅱ Ⅲ Ⅳ Ⅴ Ⅵ test",r"[Ⅰ-Ⅴ\s]+",3,23}, + + // new edge cases + TestItem{"12345678", r"[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]",-1,0}, + TestItem{"12345678", r"[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]",0,8}, + TestItem{"123456789", r"^[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]$",0,9} + TestItem{"12345678", r"^\d{8}$",0,8}, + TestItem{"12345678", r"^\d{7}$",-1,0}, + TestItem{"12345678", r"^\d{9}$",-1,0}, + + TestItem{"eth", r"(oth)|(eth)",0,3}, + TestItem{"et", r"(oth)|(eth)",-1,0}, + TestItem{"et", r".*(oth)|(eth)",-1,0}, + TestItem{"peoth", r".*(ith)|(eth)",-1,0}, + + TestItem{"poth", r"(eth)|(oth)",1,4}, + TestItem{"poth", r"(oth)|(eth)",1,4}, + TestItem{"poth", r".(oth)|(eth)$",0,4}, + TestItem{"poth", r"^.(oth)|(eth)$",0,4}, + TestItem{"poth", r"^\w+$",0,4}, + + // test dot_char + TestItem{"8-11 l: qllllqllklhlvtl", r"^(\d+)-(\d+) ([a-z]): (.*)$",0,23}, + TestItem{"accccb deer", r"^a(.*)b d(.+)r",0,11}, + TestItem{"accccb deer", r"^a(.*)b d(.+)",0,11}, + TestItem{"accccb deer", r"^(.*)$",0,11}, + TestItem{"accccb deer", r"^a(.*)b d(.+)p",-1,0}, + TestItem{"##.#....#.##.####...#.##", r".{18}[.#]",0,19}, + TestItem{"#.#......##.#..#..##........##....###...##...######.......#.....#..#......#...#........###.#..#.", r'.*#[.#]{4}##[.#]{4}##[.#]{4}###',0,49}, + + // test bcksls chars + TestItem{"[ an s. s! ]( wi4ki:something )", r"\[.*\]\( *(\w*:*\w+) *\)",0,31}, + TestItem{"[ an s. s! ](wiki:something)", r"\[.*\]\( *(\w*:*\w+) *\)",0,28}, + + // Crazywulf tests (?:^|[()])(\d+)(*)(\d+)(?:$|[()]) + TestItem{"1*1", r"(\d+)([*])(\d+)",0,3}, + TestItem{"+1*1", r"^(\d+)([*])(\d+)",-1,0}, + TestItem{"*1*1", r"(?:^|[*])(\d+)([*])(\d+)",0,4}, + TestItem{"*1*1", r"(?:^|[*()])(\d+)([*])(\d+)",0,4}, + TestItem{")1*1", r"(?:^|[*()])(\d+)([*])(\d+)",0,4}, + TestItem{"(1*1", r"(?:^|[*()])(\d+)([*])(\d+)",0,4}, + TestItem{"*1*1(", r"(?:^|[*()])(\d+)([*])(\d+)(?:$|[*()])",0,5}, + TestItem{" 1*1(", r"(?:^|[*()])(\d+)([*])(\d+)(?:$|[*()])",-1,0}, + TestItem{"1*1 ", r"(?:^|[*()])(\d+)([*])(\d+)(?:$|[*()])",-1,0}, + + // particular groups + TestItem{"ababababac", r"ab(.*)(ac)",0,10}, + +] +) + +struct TestItemRe { + src string + q string + rep string + r string +} +const ( +match_test_suite_replace = [ + // replace tests + TestItemRe{ + "oggi pibao è andato a casa di pbababao ed ha trovato pibabababao", + r"(pi?(ba)+o)", + "CIAO", + "oggi CIAO è andato a casa di CIAO ed ha trovato CIAO" + }, + TestItemRe{ + "Today is a good day and tomorrow will be for sure.", + r"[Tt]o\w+", + "CIAO", + "CIAO is a good day and CIAO will be for sure." + }, + TestItemRe{ + "Today is a good day and tomorrow will be for sure.", + r"(a\w) ", + r"[\0] ", + "Tod[ay] is a good d[ay] and tomorrow will be for sure." + }, + TestItemRe{ + "Today is a good day and tomorrow will be for sure.", + r"(a\w) ", + r"[\0_\0] ", + "Tod[ay_ay] is a good d[ay_ay] and tomorrow will be for sure." + }, + TestItemRe{ + "Today is a good day and tomorrow will be for sure.", + r"(a\w) ", + r"[\0\1] ", + "Tod[ay] is a good d[ay] and tomorrow will be for sure." + }, +] + +match_test_suite_replace_simple = [ + // replace tests + TestItemRe{ + "oggi pibao è andato a casa di pbababao ed ha trovato pibabababao", + r"(pi?(ba)+o)", + "CIAO", + "oggi CIAO è andato a casa di CIAO ed ha trovato CIAO" + }, + TestItemRe{ + "Today is a good day and tomorrow will be for sure.", + r"[Tt]o\w+", + "CIAO", + "CIAO is a good day and CIAO will be for sure." + }, +] +) + +struct TestItemCGroup { + src string + q string + s int + e int + cg []int // [number of items (3*# item), id_group_0, start_0, end_0, id_group_1, start1, start2,... ] + cgn map[string]int +} +const ( +cgroups_test_suite = [ + TestItemCGroup{ + "http://www.ciao.mondo/hello/pippo12_/pera.html", + r"(?Phttps?)|(?:ftps?)://(?P[\w_]+[\.|/])+",0,42, + [7, 0, 0, 4, 1, 7, 11, 1, 11, 16, 1, 16, 22, 1, 22, 28, 1, 28, 37, 1, 37, 42], + {'format':int(0),'token':1} + }, + TestItemCGroup{ + "http://www.ciao.mondo/hello/pippo12_/pera.html", + r"(?Phttps?)|(?Pftps?)://(?P[\w_]+.)+",0,46, + [8, 0, 0, 4, 1, 7, 11, 1, 11, 16, 1, 16, 22, 1, 22, 28, 1, 28, 37, 1, 37, 42, 1, 42, 46] + //[8, 0, 0, 4, 1, 7, 10, 1, 11, 15, 1, 16, 21, 1, 22, 27, 1, 28, 36, 1, 37, 41, 1, 42, 46], + {'format':int(0),'token':1} + }, + TestItemCGroup{ + "http://www.ciao.mondo/hello/pippo12_/pera.html", + r"(?Phttps?)|(?Pftps?)://([\w_]+\.)+",0,16, + [3, 0, 0, 4, 1, 7, 11, 1, 11, 16], + {'format':int(0)} + }, + TestItemCGroup{ + "acc +13 pippo", + r"(\w+)\s(.)([0-9]+) \w+",0,13, + [0, 3, 4, 5, 5, 7], + map[string]int{} + }, + TestItemCGroup{ + "acc +13", + r"(\w+)\s(.)([0-9]+)",0,7, + [0, 3, 4, 5, 5, 7], + map[string]int{} + }, + TestItemCGroup{ + "ababababac", + r"ab(.*)(ac)",0,10, + [2, 8, 8, 10], + map[string]int{} + }, +] +) + + +struct Test_find_all { + src string + q string + res []int // [0,4,5,6...] + res_str []string // ['find0','find1'...] +} +const ( +find_all_test_suite = [ + Test_find_all{ + "abcd 1234 efgh 1234 ghkl1234 ab34546df", + r"\d+", + [5, 9, 15, 19, 24, 28, 31, 36], + ['1234', '1234', '1234', '34546'] + }, + Test_find_all{ + "abcd 1234 efgh 1234 ghkl1234 ab34546df", + r"\a+", + [0, 4, 10, 14, 20, 24, 29, 31, 36, 38], + ['abcd', 'efgh', 'ghkl', 'ab', 'df'] + }, + Test_find_all{ + "oggi pippo è andato a casa di pluto ed ha trovato pippo", + r"p[iplut]+o", + [5, 10, 31, 36, 51, 56], + ['pippo', 'pluto', 'pippo'] + }, + Test_find_all{ + "oggi pibao è andato a casa di pbababao ed ha trovato pibabababao", + r"(pi?(ba)+o)", + [5, 10, 31, 39, 54, 65], + ['pibao', 'pbababao', 'pibabababao'] + }, + Test_find_all{ + "Today is a good day and tomorrow will be for sure.", + r"[Tt]o\w+", + [0, 5, 24, 32], + ['Today', 'tomorrow'] + }, + Test_find_all{ + "pera\nurl = https://github.com/dario/pig.html\npippo", + r"url *= *https?://[\w./]+", + [5, 44], + ['url = https://github.com/dario/pig.html'] + }, + Test_find_all{ + "pera\nurl = https://github.com/dario/pig.html\npippo", + r"url *= *https?://.*"+'\n', + [5, 45], + ['url = https://github.com/dario/pig.html\n'] + }, + Test_find_all{ + "#.#......##.#..#..##........##....###...##...######.......#.....#..#......#...#........###.#..#.", + r"#[.#]{4}##[.#]{4}##[.#]{4}###", + [29, 49], + ['#....###...##...####'] + }, + Test_find_all{ + "#.#......##.#..#..##........##....###...##...######.......#.....#..#......#...#........###.#..#.", + r".*#[.#]{4}##[.#]{4}##[.#]{4}###", + [0, 49], + ['#.#......##.#..#..##........##....###...##...####'] + }, + Test_find_all{ + "1234 Aa dddd Aaf 12334 Aa opopo Aaf", + r"Aa.+Aaf", + [5, 16, 23, 35], + ['Aa dddd Aaf', 'Aa opopo Aaf'] + }, + Test_find_all{ + "@for something @endfor @for something else @endfor altro testo @for body @endfor uno due @for senza dire più @endfor pippo", + r"@for.+@endfor", + [0, 22, 23, 50, 63, 80, 89, 117], + ['@for something @endfor', '@for something else @endfor', '@for body @endfor', '@for senza dire più @endfor'] + } + +] +) + +const ( + debug = true // true for debug println +) + +fn test_regex(){ + // check capturing groups + for c,to in cgroups_test_suite { + // debug print + if debug { + println("$c [${to.src}] [q${to.q}] (${to.s}, ${to.e})") + } + + mut re := regex.regex_opt(to.q) or { + eprintln('err: $err') + assert false + continue + } + + if to.cgn.len > 0 { + re.group_csave_flag = true + //re.group_csave = [-1].repeat(3*20+1) + if debug { println("continuous save")} + } else { + if debug { println("NO continuous save")} + } + + start, end := re.match_string(to.src) + + mut tmp_str := "" + if start >= 0 && end > start{ + tmp_str = to.src[start..end] + } + + if start != to.s || end != to.e { + //println("#$c [$to.src] q[$to.q] res[$tmp_str] $start, $end") + eprintln("ERROR!") + //C.printf("ERROR!! res:(%d, %d) refh:(%d, %d)\n",start, end, to.s, to.e) + assert false + continue + } + + // check cgroups + if to.cgn.len > 0 { + if re.group_csave.len == 0 || re.group_csave[0] != to.cg[0] { + eprintln("Capturing group len error! found: ${re.group_csave[0]} true ground: ${to.cg[0]}") + assert false + continue + } + + // check captured groups + mut ln := re.group_csave[0]*3 + for ln > 0 { + if re.group_csave[ln] != to.cg[ln] { + eprintln("Capturing group failed on $ln item!") + assert false + } + ln-- + } + + // check named captured groups + for k in to.cgn.keys() { + if to.cgn[k] != (re.group_map[k]-1) { // we have -1 because the map not found is 0, in groups we start from 0 and we store using +1 + eprintln("Named capturing group error! [$k]") + assert false + continue + } + } + } else { + // check normal captured groups + if re.groups.len != to.cg.len { + assert false + } + for ln:=0; ln < re.groups.len; ln++ { + if re.groups[ln] != to.cg[ln] { + eprintln("Capture group doesn't match:") + eprintln("true ground: [${to.cg}]") + eprintln("elaborated : [${re.groups}]") + assert false + } + } + } + } + + // check find_all + for c,to in find_all_test_suite { + // debug print + if debug { println("#$c [$to.src] q[$to.q] ($to.res, $to.res_str)") } + + mut re := regex.regex_opt(to.q) or { + eprintln('err: $err') + assert false + continue + } + + re.reset() + res := re.find_all(to.src) + if res != to.res { + eprintln('err: find_all !!') + if debug { println("#$c exp: $to.res calculated: $res") } + assert false + } + + res_str := re.find_all_str(to.src) + if res_str != to.res_str { + eprintln('err: find_all_str !!') + if debug { println("#$c exp: $to.res_str calculated: $res_str") } + assert false + } + } + + // check replace + for c,to in match_test_suite_replace{ + // debug print + if debug { println("#$c [$to.src] q[$to.q] $to.r") } + + mut re := regex.regex_opt(to.q) or { + eprintln('err: $err') + assert false + continue + } + + res := re.replace(to.src,to.rep) + if res != to.r { + eprintln("ERROR: replace.") + assert false + continue + } + } + + // check replace simple + for c,to in match_test_suite_replace_simple{ + // debug print + if debug { println("#$c [$to.src] q[$to.q] $to.r") } + + mut re := regex.regex_opt(to.q) or { + eprintln('err: $err') + assert false + continue + } + + res := re.replace_simple(to.src,to.rep) + if res != to.r { + eprintln("ERROR: replace.") + assert false + continue + } + } + + // check match and find + for c,to in match_test_suite { + // debug print + if debug { println("#$c [$to.src] q[$to.q] $to.s $to.e") } + + // test the find + if to.s > 0 { + mut re := regex.regex_opt(to.q) or { + eprintln('err: $err') + assert false + continue + } + // q_str := re.get_query() + // eprintln("Query: $q_str") + start,end := re.find(to.src) + + if start != to.s || end != to.e { + err_str := re.get_parse_error_string(start) + eprintln("ERROR : $err_str start: ${start} end: ${end}") + assert false + } else { + //tmp_str := text[start..end] + //println("found in [$start, $end] => [$tmp_str]") + assert true + } + continue + } + + // test the match + mut re := regex.new() + //re.debug = true + + re.compile_opt(to.q) or { + eprintln('err: $err') + assert false + continue + } + //println("#$c [$to.src] q[$to.q]") + start, end := re.match_string(to.src) + + mut tmp_str := "" + if start >= 0 && end > start{ + tmp_str = to.src[start..end] + } + + if start != to.s || end != to.e { + eprintln("#$c [$to.src] q[$to.q] res[$tmp_str] $start, $end") + eprintln("ERROR!") + //C.printf("ERROR!! res:(%d, %d) refh:(%d, %d)\n",start, end, to.s, to.e) + assert false + continue + } + + // rerun to test consistency + tmp_str1 := to.src.clone() + start1, end1 := re.match_string(tmp_str1) + if start1 != start || end1 != end { + eprintln("two run ERROR!!") + assert false + continue + } + + } + + if debug { println("DONE!") } +} + +// test regex_base function +fn test_regex_func(){ + query := r"\d\dabcd" + test_str := "78abcd" + mut re, re_err, err_pos := regex.regex_base(query) + if re_err == regex.compile_ok { + start, end := re.match_string(test_str) + assert (start == 0) && (end == 6) + } else { + eprintln("Error in query string in pos ${err_pos}") + eprintln("Error: ${re.get_parse_error_string(re_err)}") + assert false + } +} + +fn my_repl(re regex.RE, in_txt string, start int, end int) string { + s0 := re.get_group_by_id(in_txt,0)[0..1] + "X" + s1 := re.get_group_by_id(in_txt,1)[0..1] + "X" + s2 := re.get_group_by_id(in_txt,2)[0..1] + "X" + return "${s0}${s1}${s2}" +} + + +// test regex replace function +fn test_regex_func_replace(){ + filler := "E il primo dei tre regni dell'Oltretomba cristiano visitato da Dante nel corso del viaggio, con la guida di Virgilio." + txt := r'"content": "They dont necessarily flag "you will be buying these shares on margin!"", "channel_id"' + query := r'"(content":\s+")(.*)(, "channel_id")' + mut re := regex.regex_opt(query) or { panic(err) } + + mut txt1 := "" + mut txt2 := "" + + for _ in 0..3 { + rnd := int(10+rand.u32() % 20) + txt1 += txt + filler[0..rnd] + "\n" + txt2 += "cXTX,X" + filler[0..rnd] + "\n" + } + + result := re.replace_by_fn(txt1, my_repl) + if debug { + eprintln(result) + eprintln(txt2) + } + assert result == txt2 +} \ No newline at end of file diff --git a/v_windows/v/old/vlib/regex/regex_util.v b/v_windows/v/old/vlib/regex/regex_util.v new file mode 100644 index 0000000..0bf1a81 --- /dev/null +++ b/v_windows/v/old/vlib/regex/regex_util.v @@ -0,0 +1,436 @@ +/* +regex 1.0 alpha + +Copyright (c) 2019-2021 Dario Deledda. All rights reserved. +Use of this source code is governed by an MIT license +that can be found in the LICENSE file. +*/ +module regex + +import strings + +/****************************************************************************** +* +* Inits +* +******************************************************************************/ +// regex create a regex object from the query string, retunr RE object and errors as re_err, err_pos +pub fn regex_base(pattern string) (RE, int, int) { + // init regex + mut re := RE{} + re.prog = []Token{len: pattern.len + 1} // max program length, can not be longer then the pattern + re.cc = []CharClass{len: pattern.len} // can not be more char class the the length of the pattern + re.group_csave_flag = false // enable continuos group saving + re.group_max_nested = 128 // set max 128 group nested + re.group_max = pattern.len >> 1 // we can't have more groups than the half of the pattern legth + + re.group_stack = []int{len: re.group_max, init: -1} + re.group_data = []int{len: re.group_max, init: -1} + + re_err, err_pos := re.impl_compile(pattern) + return re, re_err, err_pos +} + +/****************************************************************************** +* +* Utilities +* +******************************************************************************/ +// get_group_bounds_by_name get a group boundaries by its name +pub fn (re RE) get_group_bounds_by_name(group_name string) (int, int) { + if group_name in re.group_map { + tmp_index := re.group_map[group_name] - 1 + start := re.groups[tmp_index * 2] + end := re.groups[tmp_index * 2 + 1] + return start, end + } + return -1, -1 +} + +// get_group_by_name get a group boundaries by its name +pub fn (re RE) get_group_by_name(in_txt string, group_name string) string { + if group_name in re.group_map { + tmp_index := re.group_map[group_name] - 1 + start := re.groups[tmp_index * 2] + end := re.groups[tmp_index * 2 + 1] + if start >= 0 && end > start { + return in_txt[start..end] + } + } + return '' +} + +// get_group_by_id get a group string by its id +pub fn (re RE) get_group_by_id(in_txt string, group_id int) string { + if group_id < (re.groups.len >> 1) { + index := group_id << 1 + start := re.groups[index] + end := re.groups[index + 1] + if start >= 0 && end > start { + return in_txt[start..end] + } + } + return '' +} + +// get_group_by_id get a group boundaries by its id +pub fn (re RE) get_group_bounds_by_id(group_id int) (int, int) { + if group_id < re.group_count { + index := group_id << 1 + return re.groups[index], re.groups[index + 1] + } + return -1, -1 +} + +pub struct Re_group { +pub: + start int = -1 + end int = -1 +} + +// get_group_list return a list of Re_group for the found groups +pub fn (re RE) get_group_list() []Re_group { + mut res := []Re_group{len: re.groups.len >> 1} + mut gi := 0 + // println("len: ${re.groups.len} groups: ${re.groups}") + + for gi < re.groups.len { + if re.groups[gi] >= 0 { + txt_st := re.groups[gi] + txt_en := re.groups[gi + 1] + + // println("#${gi/2} start: ${re.groups[gi]} end: ${re.groups[gi + 1]} ") + if txt_st >= 0 && txt_en > txt_st { + tmp := Re_group{ + start: re.groups[gi] + end: re.groups[gi + 1] + } + // println(tmp) + res[gi >> 1] = tmp + } else { + res[gi >> 1] = Re_group{} + } + } + gi += 2 + } + return res +} + +/****************************************************************************** +* +* Matchers +* +******************************************************************************/ +// match_string Match the pattern with the in_txt string +[direct_array_access] +pub fn (mut re RE) match_string(in_txt string) (int, int) { + start, mut end := re.match_base(in_txt.str, in_txt.len + 1) + if end > in_txt.len { + end = in_txt.len + } + + if start >= 0 && end > start { + if (re.flag & f_ms) != 0 && start > 0 { + return no_match_found, 0 + } + if (re.flag & f_me) != 0 && end < in_txt.len { + if in_txt[end] in new_line_list { + return start, end + } + return no_match_found, 0 + } + return start, end + } + return start, end +} + +/****************************************************************************** +* +* Finders +* +******************************************************************************/ +/* +// find internal implementation HERE for reference do not remove!! +[direct_array_access] +fn (mut re RE) find_imp(in_txt string) (int,int) { + old_flag := re.flag + re.flag |= f_src // enable search mode + + start, mut end := re.match_base(in_txt.str, in_txt.len + 1) + //print("Find [$start,$end] '${in_txt[start..end]}'") + if end > in_txt.len { + end = in_txt.len + } + re.flag = old_flag + + if start >= 0 && end > start { + return start, end + } + return no_match_found, 0 +} +*/ + +// find try to find the first match in the input string +[direct_array_access] +pub fn (mut re RE) find(in_txt string) (int, int) { + // old_flag := re.flag + // re.flag |= f_src // enable search mode + + mut i := 0 + for i < in_txt.len { + mut s := -1 + mut e := -1 + unsafe { + // tmp_str := tos(in_txt.str + i, in_txt.len - i) + // println("Check: [$tmp_str]") + s, e = re.match_base(in_txt.str + i, in_txt.len - i + 1) + + if s >= 0 && e > s { + // println("find match in: ${i+s},${i+e} [${in_txt[i+s..i+e]}]") + // re.flag = old_flag + return i + s, i + e + } + i++ + } + } + // re.flag = old_flag + return -1, -1 +} + +// find try to find the first match in the input string strarting from start index +[direct_array_access] +pub fn (mut re RE) find_from(in_txt string, start int) (int, int) { + old_flag := re.flag + re.flag |= f_src // enable search mode + + mut i := start + if i < 0 { + return -1, -1 + } + for i < in_txt.len { + //--- speed references --- + + mut s := -1 + mut e := -1 + + unsafe { + tmp_str := tos(in_txt.str + i, in_txt.len - i) + s, e = re.match_string(tmp_str) + } + //------------------------ + // s,e = re.find_imp(in_txt[i..]) + //------------------------ + if s >= 0 && e > s { + // println("find match in: ${i+s},${i+e} [${in_txt[i+s..i+e]}]") + re.flag = old_flag + return i + s, i + e + } else { + i++ + } + } + re.flag = old_flag + return -1, -1 +} + +// find_all find all the non overlapping occurrences of the match pattern +[direct_array_access] +pub fn (mut re RE) find_all(in_txt string) []int { + // old_flag := re.flag + // re.flag |= f_src // enable search mode + + mut i := 0 + mut res := []int{} + + for i < in_txt.len { + mut s := -1 + mut e := -1 + unsafe { + // tmp_str := in_txt[i..] + // tmp_str := tos(in_txt.str + i, in_txt.len - i) + // println("Check: [$tmp_str]") + s, e = re.match_base(in_txt.str + i, in_txt.len + 1 - i) + + if s >= 0 && e > s { + res << i + s + res << i + e + i += e + continue + } + } + i++ + } + // re.flag = old_flag + return res +} + +// find_all_str find all the non overlapping occurrences of the match pattern, return a string list +[direct_array_access] +pub fn (mut re RE) find_all_str(in_txt string) []string { + // old_flag := re.flag + // re.flag |= f_src // enable search mode + + mut i := 0 + mut res := []string{} + + for i < in_txt.len { + mut s := -1 + mut e := -1 + unsafe { + // tmp_str := in_txt[i..] + // tmp_str := tos(in_txt.str + i, in_txt.len - i) + // println("Check: [$tmp_str]") + s, e = re.match_base(in_txt.str + i, in_txt.len + 1 - i) + + if s >= 0 && e > s { + tmp_str := tos(in_txt.str + i, in_txt.len - i) + // println("Found: $s:$e [${tmp_str[s..e]}]") + res << tmp_str[..e] + i += e + continue + } + } + i++ + } + // re.flag = old_flag + return res +} + +/****************************************************************************** +* +* Replacers +* +******************************************************************************/ +// replace_simple return a string where the matches are replaced with the replace string +pub fn (mut re RE) replace_simple(in_txt string, repl string) string { + pos := re.find_all(in_txt) + + if pos.len > 0 { + mut res := '' + mut i := 0 + + mut s1 := 0 + mut e1 := in_txt.len + + for i < pos.len { + e1 = pos[i] + res += in_txt[s1..e1] + repl + s1 = pos[i + 1] + i += 2 + } + + res += in_txt[s1..] + return res + } + return in_txt +} + +// type of function used for custom replace +// in_txt source text +// start index of the start of the match in in_txt +// end index of the end of the match in in_txt +// the match is in in_txt[start..end] +pub type FnReplace = fn (re RE, in_txt string, start int, end int) string + +// replace_by_fn return a string where the matches are replaced with the string from the repl_fn callback function +pub fn (mut re RE) replace_by_fn(in_txt string, repl_fn FnReplace) string { + mut i := 0 + mut res := strings.new_builder(in_txt.len) + mut last_end := 0 + + for i < in_txt.len { + // println("Find Start. $i [${in_txt[i..]}]") + s, e := re.find_from(in_txt, i) + // println("Find End.") + if s >= 0 && e > s { + // println("find match in: ${s},${e} [${in_txt[s..e]}]") + + if last_end < s { + res.write_string(in_txt[last_end..s]) + } + + for g_i in 0 .. re.group_count { + re.groups[g_i << 1] += i + re.groups[(g_i << 1) + 1] += i + } + + repl := repl_fn(re, in_txt, s, e) + // println("repl res: $repl") + res.write_string(repl) + // res.write_string("[[${in_txt[s..e]}]]") + + last_end = e + i = e + } else { + break + // i++ + } + // println(i) + } + if last_end >= 0 && last_end < in_txt.len { + res.write_string(in_txt[last_end..]) + } + return res.str() +} + +fn (re RE) parsed_replace_string(in_txt string, repl string) string { + str_lst := repl.split('\\') + mut res := str_lst[0] + mut i := 1 + for i < str_lst.len { + tmp := str_lst[i] + // println("tmp: ${tmp}") + if tmp.len > 0 && tmp[0] >= `0` && tmp[0] <= `9` { + group_id := int(tmp[0] - `0`) + group := re.get_group_by_id(in_txt, group_id) + // println("group: $group_id [$group]") + res += '$group${tmp[1..]}' + } else { + res += '\\' + tmp + } + i++ + } + return res +} + +// replace return a string where the matches are replaced with the repl_str string, +// this function support use groups in the replace string +pub fn (mut re RE) replace(in_txt string, repl_str string) string { + mut i := 0 + mut res := strings.new_builder(in_txt.len) + mut last_end := 0 + + for i < in_txt.len { + // println("Find Start. $i [${in_txt[i..]}]") + s, e := re.find_from(in_txt, i) + // println("Find End.") + if s >= 0 && e > s { + // println("find match in: ${s},${e} [${in_txt[s..e]}]") + + if last_end < s { + res.write_string(in_txt[last_end..s]) + } + + for g_i in 0 .. re.group_count { + re.groups[g_i << 1] += i + re.groups[(g_i << 1) + 1] += i + } + + // repl := repl_fn(re, in_txt, s, e) + repl := re.parsed_replace_string(in_txt, repl_str) + // println("repl res: $repl") + res.write_string(repl) + // res.write_string("[[${in_txt[s..e]}]]") + + last_end = e + i = e + } else { + break + // i++ + } + // println(i) + } + if last_end >= 0 && last_end < in_txt.len { + res.write_string(in_txt[last_end..]) + } + return res.str() +} diff --git a/v_windows/v/old/vlib/runtime/runtime.v b/v_windows/v/old/vlib/runtime/runtime.v new file mode 100644 index 0000000..4f92fe1 --- /dev/null +++ b/v_windows/v/old/vlib/runtime/runtime.v @@ -0,0 +1,56 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. + +module runtime + +import os + +// nr_jobs returns the same as `nr_cpus` with the difference that if an +// environment variable `VJOBS` is set, and has a value > 0, +// then `nr_jobs` will return that number instead. +// This is useful for runtime tweaking of e.g. threaded or concurrent code. +pub fn nr_jobs() int { + mut cpus := nr_cpus() - 1 + // allow for overrides, for example using `VJOBS=32 ./v test .` + vjobs := os.getenv('VJOBS').int() + if vjobs > 0 { + cpus = vjobs + } + if cpus == 0 { + return 1 + } + return cpus +} + +// is_32bit returns true if the current executable is running on a 32 bit system. +pub fn is_32bit() bool { + $if x32 { + return true + } + return false +} + +// is_64bit returns true if the current executable is running on a 64 bit system. +pub fn is_64bit() bool { + $if x64 { + return true + } + return false +} + +// is_little_endian returns true if the current executable is running on a little-endian system. +pub fn is_little_endian() bool { + $if little_endian { + return true + } + return false +} + +// is_big_endian returns true if the current executable is running on a big-endian system. +pub fn is_big_endian() bool { + $if big_endian { + return true + } + return false +} diff --git a/v_windows/v/old/vlib/runtime/runtime_nix.c.v b/v_windows/v/old/vlib/runtime/runtime_nix.c.v new file mode 100644 index 0000000..a5c69b0 --- /dev/null +++ b/v_windows/v/old/vlib/runtime/runtime_nix.c.v @@ -0,0 +1,11 @@ +module runtime + +fn C.sysconf(name int) i64 + +// nr_cpus returns the number of virtual CPU cores found on the system. +pub fn nr_cpus() int { + $if linux || macos || solaris { + return int(C.sysconf(C._SC_NPROCESSORS_ONLN)) + } + return 1 +} diff --git a/v_windows/v/old/vlib/runtime/runtime_test.v b/v_windows/v/old/vlib/runtime/runtime_test.v new file mode 100644 index 0000000..d180890 --- /dev/null +++ b/v_windows/v/old/vlib/runtime/runtime_test.v @@ -0,0 +1,39 @@ +import runtime + +fn test_nr_cpus() { + nr_cpus := runtime.nr_cpus() + assert nr_cpus > 0 +} + +fn test_nr_jobs() { + nr_jobs := runtime.nr_jobs() + assert nr_jobs > 0 +} + +fn test_is_32bit() { + x := runtime.is_32bit().str() + assert x == 'true' || x == 'false' +} + +fn test_is_64bit() { + x := runtime.is_64bit().str() + assert x == 'true' || x == 'false' +} + +fn test_is_little_endian() { + x := runtime.is_little_endian().str() + assert x == 'true' || x == 'false' +} + +fn test_is_big_endian() { + x := runtime.is_big_endian().str() + assert x == 'true' || x == 'false' +} + +fn test_is_big_endian_different_than_is_little_endian() { + assert runtime.is_big_endian() != runtime.is_little_endian() +} + +fn test_is_32bit_different_than_is_64bit() { + assert runtime.is_32bit() != runtime.is_64bit() +} diff --git a/v_windows/v/old/vlib/runtime/runtime_windows.c.v b/v_windows/v/old/vlib/runtime/runtime_windows.c.v new file mode 100644 index 0000000..de5b2ce --- /dev/null +++ b/v_windows/v/old/vlib/runtime/runtime_windows.c.v @@ -0,0 +1,21 @@ +module runtime + +import os + +[typedef] +struct C.SYSTEM_INFO { + dwNumberOfProcessors u32 +} + +fn C.GetSystemInfo(&C.SYSTEM_INFO) + +// nr_cpus returns the number of virtual CPU cores found on the system. +pub fn nr_cpus() int { + sinfo := C.SYSTEM_INFO{} + C.GetSystemInfo(&sinfo) + mut nr := int(sinfo.dwNumberOfProcessors) + if nr == 0 { + nr = os.getenv('NUMBER_OF_PROCESSORS').int() + } + return nr +} diff --git a/v_windows/v/old/vlib/semver/LICENSE.md b/v_windows/v/old/vlib/semver/LICENSE.md new file mode 100644 index 0000000..8d5ff71 --- /dev/null +++ b/v_windows/v/old/vlib/semver/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 alexesprit + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/v_windows/v/old/vlib/semver/README.md b/v_windows/v/old/vlib/semver/README.md new file mode 100644 index 0000000..e7c8d20 --- /dev/null +++ b/v_windows/v/old/vlib/semver/README.md @@ -0,0 +1,37 @@ +# semver + +A library for working with versions in [semver][semver] format. + +## Usage + +```v +import semver + +fn main() { + ver1 := semver.from('1.2.4') or { + println('Invalid version') + return + } + ver2 := semver.from('2.3.4') or { + println('Invalid version') + return + } + println(ver1.gt(ver2)) + println(ver2.gt(ver1)) + println(ver1.satisfies('>=1.1.0 <2.0.0')) + println(ver2.satisfies('>=1.1.0 <2.0.0')) + println(ver2.satisfies('>=1.1.0 <2.0.0 || >2.2.0')) +} +``` + +``` +false +true +true +false +true +``` + +For more details see `semver.v` file. + +[semver]: https://semver.org/ diff --git a/v_windows/v/old/vlib/semver/compare.v b/v_windows/v/old/vlib/semver/compare.v new file mode 100644 index 0000000..0b51d29 --- /dev/null +++ b/v_windows/v/old/vlib/semver/compare.v @@ -0,0 +1,59 @@ +module semver + +// * Private functions. +[inline] +fn version_satisfies(ver Version, input string) bool { + range := parse_range(input) or { return false } + return range.satisfies(ver) +} + +fn compare_eq(v1 Version, v2 Version) bool { + return v1.major == v2.major && v1.minor == v2.minor && v1.patch == v2.patch + && v1.prerelease == v2.prerelease +} + +fn compare_gt(v1 Version, v2 Version) bool { + if v1.major < v2.major { + return false + } + if v1.major > v2.major { + return true + } + if v1.minor < v2.minor { + return false + } + if v1.minor > v2.minor { + return true + } + return v1.patch > v2.patch +} + +fn compare_lt(v1 Version, v2 Version) bool { + if v1.major > v2.major { + return false + } + if v1.major < v2.major { + return true + } + if v1.minor > v2.minor { + return false + } + if v1.minor < v2.minor { + return true + } + return v1.patch < v2.patch +} + +fn compare_ge(v1 Version, v2 Version) bool { + if compare_eq(v1, v2) { + return true + } + return compare_gt(v1, v2) +} + +fn compare_le(v1 Version, v2 Version) bool { + if compare_eq(v1, v2) { + return true + } + return compare_lt(v1, v2) +} diff --git a/v_windows/v/old/vlib/semver/parse.v b/v_windows/v/old/vlib/semver/parse.v new file mode 100644 index 0000000..3443f59 --- /dev/null +++ b/v_windows/v/old/vlib/semver/parse.v @@ -0,0 +1,85 @@ +module semver + +// * Private structs and functions. +struct RawVersion { + prerelease string + metadata string +mut: + raw_ints []string +} + +const ( + ver_major = 0 + ver_minor = 1 + ver_patch = 2 + versions = [ver_major, ver_minor, ver_patch] +) + +// TODO: Rewrite using regexps? +// /(\d+)\.(\d+)\.(\d+)(?:\-([0-9A-Za-z-.]+))?(?:\+([0-9A-Za-z-]+))?/ +fn parse(input string) RawVersion { + mut raw_version := input + mut prerelease := '' + mut metadata := '' + plus_idx := raw_version.last_index('+') or { -1 } + if plus_idx > 0 { + metadata = raw_version[(plus_idx + 1)..] + raw_version = raw_version[0..plus_idx] + } + hyphen_idx := raw_version.index('-') or { -1 } + if hyphen_idx > 0 { + prerelease = raw_version[(hyphen_idx + 1)..] + raw_version = raw_version[0..hyphen_idx] + } + raw_ints := raw_version.split('.') + return RawVersion{ + prerelease: prerelease + metadata: metadata + raw_ints: raw_ints + } +} + +fn (ver RawVersion) is_valid() bool { + if ver.raw_ints.len != 3 { + return false + } + return is_valid_number(ver.raw_ints[semver.ver_major]) + && is_valid_number(ver.raw_ints[semver.ver_minor]) + && is_valid_number(ver.raw_ints[semver.ver_patch]) && is_valid_string(ver.prerelease) + && is_valid_string(ver.metadata) +} + +fn (ver RawVersion) is_missing(typ int) bool { + return typ >= ver.raw_ints.len - 1 +} + +fn (raw_ver RawVersion) coerce() ?Version { + ver := raw_ver.complete() + if !is_valid_number(ver.raw_ints[semver.ver_major]) { + return error('Invalid major version: $ver.raw_ints[ver_major]') + } + return ver.to_version() +} + +fn (raw_ver RawVersion) complete() RawVersion { + mut raw_ints := raw_ver.raw_ints + for raw_ints.len < 3 { + raw_ints << '0' + } + return RawVersion{ + prerelease: raw_ver.prerelease + metadata: raw_ver.metadata + raw_ints: raw_ints + } +} + +fn (raw_ver RawVersion) validate() ?Version { + if !raw_ver.is_valid() { + return none + } + return raw_ver.to_version() +} + +fn (raw_ver RawVersion) to_version() Version { + return Version{raw_ver.raw_ints[semver.ver_major].int(), raw_ver.raw_ints[semver.ver_minor].int(), raw_ver.raw_ints[semver.ver_patch].int(), raw_ver.prerelease, raw_ver.metadata} +} diff --git a/v_windows/v/old/vlib/semver/range.v b/v_windows/v/old/vlib/semver/range.v new file mode 100644 index 0000000..fd0a769 --- /dev/null +++ b/v_windows/v/old/vlib/semver/range.v @@ -0,0 +1,252 @@ +module semver + +// * Private functions. +const ( + comparator_sep = ' ' + comparator_set_sep = ' || ' + hyphen_range_sep = ' - ' + x_range_symbols = 'Xx*' +) + +enum Operator { + gt + lt + ge + le + eq +} + +struct Comparator { + ver Version + op Operator +} + +struct ComparatorSet { + comparators []Comparator +} + +struct Range { + comparator_sets []ComparatorSet +} + +struct InvalidComparatorCountError { + msg string + code int +} + +struct InvalidComparatorFormatError { + msg string + code int +} + +fn (r Range) satisfies(ver Version) bool { + mut final_result := false + for set in r.comparator_sets { + final_result = final_result || set.satisfies(ver) + } + return final_result +} + +fn (set ComparatorSet) satisfies(ver Version) bool { + for comp in set.comparators { + if !comp.satisfies(ver) { + return false + } + } + return true +} + +fn (c Comparator) satisfies(ver Version) bool { + if c.op == .gt { + return ver.gt(c.ver) + } + if c.op == .lt { + return ver.lt(c.ver) + } + if c.op == .ge { + return ver.ge(c.ver) + } + if c.op == .le { + return ver.le(c.ver) + } + if c.op == .eq { + return ver.eq(c.ver) + } + return false +} + +fn parse_range(input string) ?Range { + raw_comparator_sets := input.split(semver.comparator_set_sep) + mut comparator_sets := []ComparatorSet{} + for raw_comp_set in raw_comparator_sets { + if can_expand(raw_comp_set) { + s := expand_comparator_set(raw_comp_set) or { return err } + comparator_sets << s + } else { + s := parse_comparator_set(raw_comp_set) or { return err } + comparator_sets << s + } + } + return Range{comparator_sets} +} + +fn parse_comparator_set(input string) ?ComparatorSet { + raw_comparators := input.split(semver.comparator_sep) + if raw_comparators.len > 2 { + return IError(&InvalidComparatorFormatError{ + msg: 'Invalid format of comparator set for input "$input"' + }) + } + mut comparators := []Comparator{} + for raw_comp in raw_comparators { + c := parse_comparator(raw_comp) or { + return IError(&InvalidComparatorFormatError{ + msg: 'Invalid comparator "$raw_comp" in input "$input"' + }) + } + comparators << c + } + return ComparatorSet{comparators} +} + +fn parse_comparator(input string) ?Comparator { + mut op := Operator.eq + mut raw_version := '' + if input.starts_with('>=') { + op = .ge + raw_version = input[2..] + } else if input.starts_with('<=') { + op = .le + raw_version = input[2..] + } else if input.starts_with('>') { + op = .gt + raw_version = input[1..] + } else if input.starts_with('<') { + op = .lt + raw_version = input[1..] + } else if input.starts_with('=') { + raw_version = input[1..] + } else { + raw_version = input + } + version := coerce_version(raw_version) or { return none } + return Comparator{version, op} +} + +fn parse_xrange(input string) ?Version { + mut raw_ver := parse(input).complete() + for typ in versions { + if raw_ver.raw_ints[typ].index_any(semver.x_range_symbols) == -1 { + continue + } + match typ { + ver_major { + raw_ver.raw_ints[ver_major] = '0' + raw_ver.raw_ints[ver_minor] = '0' + raw_ver.raw_ints[ver_patch] = '0' + } + ver_minor { + raw_ver.raw_ints[ver_minor] = '0' + raw_ver.raw_ints[ver_patch] = '0' + } + ver_patch { + raw_ver.raw_ints[ver_patch] = '0' + } + else {} + } + } + if !raw_ver.is_valid() { + return none + } + return raw_ver.to_version() +} + +fn can_expand(input string) bool { + return input[0] == `~` || input[0] == `^` || input.contains(semver.hyphen_range_sep) + || input.index_any(semver.x_range_symbols) > -1 +} + +fn expand_comparator_set(input string) ?ComparatorSet { + match input[0] { + `~` { return expand_tilda(input[1..]) } + `^` { return expand_caret(input[1..]) } + else {} + } + if input.contains(semver.hyphen_range_sep) { + return expand_hyphen(input) + } + return expand_xrange(input) +} + +fn expand_tilda(raw_version string) ?ComparatorSet { + min_ver := coerce_version(raw_version) or { return none } + mut max_ver := min_ver + if min_ver.minor == 0 && min_ver.patch == 0 { + max_ver = min_ver.increment(.major) + } else { + max_ver = min_ver.increment(.minor) + } + return make_comparator_set_ge_lt(min_ver, max_ver) +} + +fn expand_caret(raw_version string) ?ComparatorSet { + min_ver := coerce_version(raw_version) or { return none } + mut max_ver := min_ver + if min_ver.major == 0 { + max_ver = min_ver.increment(.minor) + } else { + max_ver = min_ver.increment(.major) + } + return make_comparator_set_ge_lt(min_ver, max_ver) +} + +fn expand_hyphen(raw_range string) ?ComparatorSet { + raw_versions := raw_range.split(semver.hyphen_range_sep) + if raw_versions.len != 2 { + return none + } + min_ver := coerce_version(raw_versions[0]) or { return none } + raw_max_ver := parse(raw_versions[1]) + if raw_max_ver.is_missing(ver_major) { + return none + } + mut max_ver := raw_max_ver.coerce() or { return none } + if raw_max_ver.is_missing(ver_minor) { + max_ver = max_ver.increment(.minor) + return make_comparator_set_ge_lt(min_ver, max_ver) + } + return make_comparator_set_ge_le(min_ver, max_ver) +} + +fn expand_xrange(raw_range string) ?ComparatorSet { + min_ver := parse_xrange(raw_range) or { return none } + if min_ver.major == 0 { + comparators := [ + Comparator{min_ver, Operator.ge}, + ] + return ComparatorSet{comparators} + } + mut max_ver := min_ver + if min_ver.minor == 0 { + max_ver = min_ver.increment(.major) + } else { + max_ver = min_ver.increment(.minor) + } + return make_comparator_set_ge_lt(min_ver, max_ver) +} + +fn make_comparator_set_ge_lt(min Version, max Version) ComparatorSet { + comparators := [ + Comparator{min, Operator.ge}, + Comparator{max, Operator.lt}, + ] + return ComparatorSet{comparators} +} + +fn make_comparator_set_ge_le(min Version, max Version) ComparatorSet { + comparators := [ + Comparator{min, Operator.ge}, + Comparator{max, Operator.le}, + ] + return ComparatorSet{comparators} +} diff --git a/v_windows/v/old/vlib/semver/semver.v b/v_windows/v/old/vlib/semver/semver.v new file mode 100644 index 0000000..c35bbfb --- /dev/null +++ b/v_windows/v/old/vlib/semver/semver.v @@ -0,0 +1,110 @@ +// * Documentation: https://docs.npmjs.com/misc/semver +module semver + +// * Structures. +// `Version` represents a semantic version in semver format. +pub struct Version { +pub: + major int + minor int + patch int + prerelease string + metadata string +} + +// Increment represents the different types of version increments. +pub enum Increment { + major + minor + patch +} + +struct EmptyInputError { + msg string = 'Empty input' + code int +} + +struct InvalidVersionFormatError { + msg string + code int +} + +// * Constructor. +// from returns a `Version` structure parsed from `input` `string`. +pub fn from(input string) ?Version { + if input.len == 0 { + return IError(&EmptyInputError{}) + } + raw_version := parse(input) + version := raw_version.validate() or { + return IError(&InvalidVersionFormatError{ + msg: 'Invalid version format for input "$input"' + }) + } + return version +} + +// build returns a `Version` structure with given `major`, `minor` and `patch` versions. +pub fn build(major int, minor int, patch int) Version { + // TODO Check if versions are greater than zero. + return Version{major, minor, patch, '', ''} +} + +// * Transformation. +// increment returns a `Version` structure with incremented values. +pub fn (ver Version) increment(typ Increment) Version { + return increment_version(ver, typ) +} + +// * Comparison. +// satisfies returns `true` if the `input` expression can be validated to `true` +// when run against this `Version`. +// Example: assert semver.build(1,0,0).satisfies('<=2.0.0') == true +// Example: assert semver.build(1,0,0).satisfies('>=2.0.0') == false +pub fn (ver Version) satisfies(input string) bool { + return version_satisfies(ver, input) +} + +// eq returns `true` if `v1` is equal to `v2`. +pub fn (v1 Version) eq(v2 Version) bool { + return compare_eq(v1, v2) +} + +// gt returns `true` if `v1` is greater than `v2`. +pub fn (v1 Version) gt(v2 Version) bool { + return compare_gt(v1, v2) +} + +// lt returns `true` if `v1` is less than `v2`. +pub fn (v1 Version) lt(v2 Version) bool { + return compare_lt(v1, v2) +} + +// ge returns `true` if `v1` is greater than or equal to `v2`. +pub fn (v1 Version) ge(v2 Version) bool { + return compare_ge(v1, v2) +} + +// le returns `true` if `v1` is less than or equal to `v2`. +pub fn (v1 Version) le(v2 Version) bool { + return compare_le(v1, v2) +} + +// * Utilites. +// coerce converts the `input` version to a `Version` struct. +// coerce will strip any contents *after* the parsed version string: +/* +Example: +import semver +v := semver.coerce('1.3-RC1-b2') or { semver.Version{} } +assert v.satisfies('>1.0 <2.0') == true // 1.3.0 +*/ +pub fn coerce(input string) ?Version { + return coerce_version(input) +} + +// is_valid returns `true` if the `input` `string` can be converted to +// a (semantic) `Version` struct. +pub fn is_valid(input string) bool { + return is_version_valid(input) +} diff --git a/v_windows/v/old/vlib/semver/semver_test.v b/v_windows/v/old/vlib/semver/semver_test.v new file mode 100644 index 0000000..77c8cd7 --- /dev/null +++ b/v_windows/v/old/vlib/semver/semver_test.v @@ -0,0 +1,192 @@ +import semver + +struct TestVersion { + raw string + major int + minor int + patch int + prerelease string + metadata string +} + +struct TestRange { + raw_version string + range_satisfied string + range_unsatisfied string +} + +struct TestCoerce { + invalid string + valid string +} + +const ( + versions_to_test = [ + TestVersion{'1.2.4', 1, 2, 4, '', ''}, + TestVersion{'1.2.4-prerelease-1', 1, 2, 4, 'prerelease-1', ''}, + TestVersion{'1.2.4+20191231', 1, 2, 4, '', '20191231'}, + TestVersion{'1.2.4-prerelease-1+20191231', 1, 2, 4, 'prerelease-1', '20191231'}, + TestVersion{'1.2.4+20191231-prerelease-1', 1, 2, 4, '', '20191231-prerelease-1'}, + ] + ranges_to_test = [ + TestRange{'1.1.0', '1.1.0', '1.1.1'}, + TestRange{'1.1.0', '=1.1.0', '=1.1.1'}, + TestRange{'1.1.0', '>=1.0.0', '<1.1.0'}, + TestRange{'1.1.0', '>=1.0.0 <=1.1.0', '>=1.0.0 <1.1.0'}, + TestRange{'2.3.1', '>=1.0.0 <=1.1.0 || >2.0.0 <2.3.4', '>=1.0.0 <1.1.0'}, + TestRange{'2.3.1', '>=1.0.0 <=1.1.0 || >2.0.0 <2.3.4', '>=1.0.0 <1.1.0 || >4.0.0 <5.0.0'}, + TestRange{'2.3.1', '~2.3.0', '~2.4.0'}, + TestRange{'3.0.0', '~3.0.0', '~4.0.0'}, + TestRange{'2.3.1', '^2.0.0', '^2.4.0'}, + TestRange{'0.3.1', '^0.3.0', '^2.4.0'}, + TestRange{'0.0.4', '^0.0.1', '^0.1.0'}, + TestRange{'2.3.4', '^0.0.1 || ^2.3.0', '^3.1.0 || ^4.2.0'}, + TestRange{'2.3.4', '>2 || <3', '>3 || >4'}, + TestRange{'2.3.4', '2.3.4 - 2.3.5', '2.5.1 - 2.8.3'}, + TestRange{'2.3.4', '2.2 - 2.3', '2.4 - 2.8'}, + TestRange{'2.3.4', '2.3.x', '2.4.x'}, + TestRange{'2.3.4', '2.x', '3.x'}, + TestRange{'2.3.4', '*', '3.x'}, + ] + coerce_to_test = [ + TestCoerce{'1.2.0.4', '1.2.0'}, + TestCoerce{'1.2.0', '1.2.0'}, + TestCoerce{'1.2', '1.2.0'}, + TestCoerce{'1', '1.0.0'}, + TestCoerce{'1-alpha', '1.0.0-alpha'}, + TestCoerce{'1+meta', '1.0.0+meta'}, + TestCoerce{'1-alpha+meta', '1.0.0-alpha+meta'}, + ] + invalid_versions_to_test = [ + 'a.b.c', + '1.2', + '1.2.x', + '1.2.3.4', + '1.2.3-alpha@', + '1.2.3+meta%', + ] + invalid_ranges_to_test = [ + '^a', + '~b', + 'a - c', + '>a', + 'a', + 'a.x', + ] +) + +fn test_from() { + for item in versions_to_test { + ver := semver.from(item.raw) or { + assert false + return + } + assert ver.major == item.major + assert ver.minor == item.minor + assert ver.patch == item.patch + assert ver.metadata == item.metadata + assert ver.prerelease == item.prerelease + } + for ver in invalid_versions_to_test { + semver.from(ver) or { + assert true + continue + } + assert false + } +} + +fn test_increment() { + version1 := semver.build(1, 2, 3) + version1_inc := version1.increment(.major) + assert version1_inc.major == 2 + assert version1_inc.minor == 0 + assert version1_inc.patch == 0 + version2_inc := version1.increment(.minor) + assert version2_inc.major == 1 + assert version2_inc.minor == 3 + assert version2_inc.patch == 0 + version3_inc := version1.increment(.patch) + assert version3_inc.major == 1 + assert version3_inc.minor == 2 + assert version3_inc.patch == 4 +} + +fn test_compare() { + first := semver.build(1, 0, 0) + patch := semver.build(1, 0, 1) + minor := semver.build(1, 2, 3) + major := semver.build(2, 0, 0) + assert first.le(first) + assert first.ge(first) + assert !first.lt(first) + assert !first.gt(first) + assert patch.ge(first) + assert first.le(patch) + assert !first.ge(patch) + assert !patch.le(first) + assert patch.gt(first) + assert first.lt(patch) + assert !first.gt(patch) + assert !patch.lt(first) + assert minor.gt(patch) + assert patch.lt(minor) + assert !patch.gt(minor) + assert !minor.lt(patch) + assert major.gt(minor) + assert minor.lt(major) + assert !minor.gt(major) + assert !major.lt(minor) +} + +fn test_satisfies() { + for item in ranges_to_test { + ver := semver.from(item.raw_version) or { + assert false + return + } + assert ver.satisfies(item.range_satisfied) + assert !ver.satisfies(item.range_unsatisfied) + } +} + +fn test_satisfies_invalid() { + ver := semver.from('1.0.0') or { + assert false + return + } + for item in invalid_ranges_to_test { + assert ver.satisfies(item) == false + } +} + +fn test_coerce() { + for item in coerce_to_test { + valid := semver.from(item.valid) or { + assert false + return + } + fixed := semver.coerce(item.invalid) or { + assert false + return + } + assert fixed.eq(valid) + } +} + +fn test_coerce_invalid() { + semver.coerce('a') or { + assert true + return + } + assert false +} + +fn test_is_valid() { + for item in versions_to_test { + assert semver.is_valid(item.raw) + } + for item in invalid_versions_to_test { + assert semver.is_valid(item) == false + } +} diff --git a/v_windows/v/old/vlib/semver/util.v b/v_windows/v/old/vlib/semver/util.v new file mode 100644 index 0000000..142ce19 --- /dev/null +++ b/v_windows/v/old/vlib/semver/util.v @@ -0,0 +1,55 @@ +module semver + +// * Private functions. +[inline] +fn is_version_valid(input string) bool { + raw_ver := parse(input) + return raw_ver.is_valid() +} + +[inline] +fn coerce_version(input string) ?Version { + raw_ver := parse(input) + ver := raw_ver.coerce() or { return error('Invalid version for input "$input"') } + return ver +} + +[inline] +fn increment_version(ver Version, typ Increment) Version { + mut major := ver.major + mut minor := ver.minor + mut patch := ver.patch + match typ { + .major { + major++ + minor = 0 + patch = 0 + } + .minor { + minor++ + patch = 0 + } + .patch { + patch++ + } + } + return Version{major, minor, patch, ver.prerelease, ver.metadata} +} + +fn is_valid_string(input string) bool { + for c in input { + if !(c.is_letter() || c.is_digit() || c == `.` || c == `-`) { + return false + } + } + return true +} + +fn is_valid_number(input string) bool { + for c in input { + if !c.is_digit() { + return false + } + } + return true +} diff --git a/v_windows/v/old/vlib/semver/v.mod b/v_windows/v/old/vlib/semver/v.mod new file mode 100644 index 0000000..dd7671c --- /dev/null +++ b/v_windows/v/old/vlib/semver/v.mod @@ -0,0 +1,5 @@ +Module { + name: 'semver' + version: '0.3.0' + deps: [] +} diff --git a/v_windows/v/old/vlib/sokol/audio/audio.v b/v_windows/v/old/vlib/sokol/audio/audio.v new file mode 100644 index 0000000..50acb7f --- /dev/null +++ b/v_windows/v/old/vlib/sokol/audio/audio.v @@ -0,0 +1,134 @@ +module audio + +$if linux { + // provide a nicer error for the user that does not have ALSA installed + #include # Please install the `libasound2-dev` package +} + +#flag -I @VEXEROOT/thirdparty/sokol +#define SOKOL_IMPL +#include "sokol_audio.h" +#flag linux -lasound +#flag darwin -framework AudioToolbox +#flag windows -lole32 + +// +pub type FNStreamingCB = fn (buffer &f32, num_frames int, num_channels int) + +pub type FnStreamingCBWithUserData = fn (buffer &f32, num_frames int, num_channels int, user_data voidptr) + +pub fn (x FNStreamingCB) str() string { + return '&FNStreamingCB{ ${ptr_str(x)} }' +} + +pub fn (x FnStreamingCBWithUserData) str() string { + return '&FnStreamingCBWithUserData{ ${ptr_str(x)} }' +} + +// +pub struct C.saudio_desc { + sample_rate int + num_channels int + buffer_frames int + packet_frames int + num_packets int + stream_cb FNStreamingCB + stream_userdata_cb FnStreamingCBWithUserData + user_data voidptr +} + +fn C.saudio_setup(desc &C.saudio_desc) + +fn C.saudio_shutdown() + +fn C.saudio_isvalid() bool + +fn C.saudio_userdata() voidptr + +fn C.saudio_query_desc() C.saudio_desc + +fn C.saudio_sample_rate() int + +fn C.saudio_buffer_frames() int + +fn C.saudio_channels() int + +fn C.saudio_expect() int + +fn C.saudio_push(frames &f32, num_frames int) int + +// audio.setup - setup sokol-audio +pub fn setup(desc C.saudio_desc) { + C.saudio_setup(&desc) +} + +// audio.shutdown - shutdown sokol-audio +pub fn shutdown() { + C.saudio_shutdown() +} + +// audio.is_valid - true after setup if audio backend was successfully initialized +pub fn is_valid() bool { + return C.saudio_isvalid() +} + +// audio.userdata - return the saudio_desc.user_data pointer +pub fn user_data() voidptr { + return C.saudio_userdata() +} + +// audio.query - return a copy of the original saudio_desc struct +pub fn query() C.saudio_desc { + return C.saudio_query_desc() +} + +// audio.sample_rate - actual sample rate +pub fn sample_rate() int { + return C.saudio_sample_rate() +} + +// audio.buffer_frames - return actual backend buffer size in number of frames +pub fn buffer_frames() int { + return C.saudio_buffer_frames() +} + +// audio.channels - actual number of channels +pub fn channels() int { + return C.saudio_channels() +} + +// audio.expect - get current number of frames to fill packet queue; use in combination with audio.push/2 +pub fn expect() int { + return C.saudio_expect() +} + +// audio.push - push sample frames from main thread, returns number of frames actually pushed +pub fn push(frames &f32, num_frames int) int { + return C.saudio_push(frames, num_frames) +} + +// +[inline] +pub fn fclamp(x f32, flo f32, fhi f32) f32 { + if x > fhi { + return fhi + } + if x < flo { + return flo + } + return x +} + +pub fn min(x int, y int) int { + if x < y { + return x + } + return y +} + +pub fn max(x int, y int) int { + if x < y { + return y + } + return x +} diff --git a/v_windows/v/old/vlib/sokol/c/declaration.c.v b/v_windows/v/old/vlib/sokol/c/declaration.c.v new file mode 100644 index 0000000..c2e435a --- /dev/null +++ b/v_windows/v/old/vlib/sokol/c/declaration.c.v @@ -0,0 +1,58 @@ +module c + +pub const ( + used_import = 1 +) + +#flag -I @VEXEROOT/thirdparty/sokol +#flag -I @VEXEROOT/thirdparty/sokol/util +#flag freebsd -I /usr/local/include +#flag darwin -fobjc-arc +#flag linux -lX11 -lGL -lXcursor -lXi -lpthread +#flag freebsd -L/usr/local/lib -lX11 -lGL -lXcursor -lXi +#flag windows -lgdi32 +// METAL +$if macos { + #flag -DSOKOL_METAL + #flag -framework Metal -framework Cocoa -framework MetalKit -framework QuartzCore +} +$if ios { + #flag -DSOKOL_METAL + #flag -framework Foundation -framework Metal -framework MetalKit -framework UIKit +} +// OPENGL +#flag linux -DSOKOL_GLCORE33 +#flag freebsd -DSOKOL_GLCORE33 +//#flag darwin -framework OpenGL -framework Cocoa -framework QuartzCore +// D3D +#flag windows -DSOKOL_GLCORE33 +//#flag windows -DSOKOL_D3D11 +// for simplicity, all header includes are here because import order matters and we dont have any way +// to ensure import order with V yet +#define SOKOL_IMPL +// TODO should not be defined for android graphic (apk/aab using sokol) builds, but we have no ways to undefine +//#define SOKOL_NO_ENTRY +#flag linux -DSOKOL_NO_ENTRY +#flag darwin -DSOKOL_NO_ENTRY +#flag windows -DSOKOL_NO_ENTRY +#flag windows -DSOKOL_WIN32_FORCE_MAIN +#flag freebsd -DSOKOL_NO_ENTRY +#flag solaris -DSOKOL_NO_ENTRY +// TODO end + +#flag linux -ldl + +$if gcboehm ? { + #define SOKOL_MALLOC GC_MALLOC + #define SOKOL_CALLOC(n,m) GC_MALLOC((n)*(m)) + #define SOKOL_REALLOC GC_REALLOC + #define SOKOL_FREE GC_FREE +} + +#include "sokol_v.h" +#include "sokol_app.h" +#define SOKOL_IMPL +#define SOKOL_NO_DEPRECATED +#include "sokol_gfx.h" +#define SOKOL_GL_IMPL +#include "util/sokol_gl.h" diff --git a/v_windows/v/old/vlib/sokol/f/f.v b/v_windows/v/old/vlib/sokol/f/f.v new file mode 100644 index 0000000..5c5e714 --- /dev/null +++ b/v_windows/v/old/vlib/sokol/f/f.v @@ -0,0 +1,15 @@ +module f + +import fontstash +import sokol.c + +pub const ( + used_import = fontstash.used_import + c.used_import +) + +#flag linux -I. + +//#include "ft2build.h" + +#define SOKOL_FONTSTASH_IMPL +#include "util/sokol_fontstash.h" diff --git a/v_windows/v/old/vlib/sokol/gfx/enums.v b/v_windows/v/old/vlib/sokol/gfx/enums.v new file mode 100644 index 0000000..fbe336e --- /dev/null +++ b/v_windows/v/old/vlib/sokol/gfx/enums.v @@ -0,0 +1,297 @@ +module gfx + +pub enum Backend { + glcore33 + gles2 + gles3 + d3d11 + metal_ios + metal_macos + metal_simulator + dummy +} + +pub enum PixelFormat { + _default // value 0 reserved for default-init + @none + r8 + r8sn + r8ui + r8si + r16 + r16sn + r16ui + r16si + r16f + rg8 + rg8sn + rg8ui + rg8si + r32ui + r32si + r32f + rg16 + rg16sn + rg16ui + rg16si + rg16f + rgba8 + rgba8sn + rgba8ui + rgba8si + bgra8 + rgb10a2 + rg11b10f + rg32ui + rg32si + rg32f + rgba16 + rgba16sn + rgba16ui + rgba16si + rgba16f + rgba32ui + rgba32si + rgba32f + depth + depth_stencil + bc1_rgba + bc2_rgba + bc3_rgba + bc4_r + bc4_rsn + bc5_rg + bc5_rgsn + bc6h_rgbf + bc6h_rgbuf + bc7_rgba + pvrtc_rgb_2bpp + pvrtc_rgb_4bpp + pvrtc_rgba_2bpp + pvrtc_rgba_4bpp + etc2_rgb8 + etc2_rgb8a1 + etc2_rgba8 + etc2_rg11 + etc2_rg11sn + _num +} + +pub enum ResourceState { + initial + alloc + valid + failed + invalid +} + +pub enum Usage { + _default // value 0 reserved for default-init + immutable + dynamic + stream + _num +} + +pub enum BufferType { + _default // value 0 reserved for default-init + vertexbuffer + indexbuffer + _num +} + +pub enum IndexType { + _default // value 0 reserved for default-init + @none + uint16 + uint32 + _num +} + +pub enum ImageType { + _default // value 0 reserved for default-init + _2d + cube + _3d + array + _num +} + +pub enum CubeFace { + pos_x + neg_x + pos_y + neg_y + pos_z + neg_z + num + _force_u32 = 0x7fffffff +} + +pub enum ShaderStage { + vs + fs +} + +pub enum PrimitiveType { + _default // value 0 reserved for default-init + points + lines + line_strip + triangles + triangle_strip + _num +} + +pub enum Filter { + _default // value 0 reserved for default-init + nearest + linear + nearest_mipmap_nearest + nearest_mipmap_linear + linear_mipmap_nearest + linear_mipmap_linear + _num +} + +pub enum Wrap { + _default // value 0 reserved for default-init + repeat + clamp_to_edge + clamp_to_border + mirrored_repeat + _num +} + +pub enum BorderColor { + _default // value 0 reserved for default-init + transparent_black + opaque_black + opaque_white + _num +} + +pub enum VertexFormat { + invalid + float + float2 + float3 + float4 + byte4 + byte4n + ubyte4 + ubyte4n + short2 + short2n + ushort2n + short4 + short4n + ushort4n + uint10_n2 + _num +} + +pub enum VertexStep { + _default // value 0 reserved for default-init + per_vertex + per_instance + _num +} + +pub enum UniformType { + invalid + float + float2 + float3 + float4 + mat4 + _num +} + +pub enum CullMode { + _default // value 0 reserved for default-init + @none + front + back + _num +} + +pub enum FaceWinding { + _facewinding_default // value 0 reserved for default-init + facewinding_ccw + facewinding_cw + _facewinding_num +} + +pub enum CompareFunc { + _default // value 0 reserved for default-init + never + less + equal + less_equal + greater + not_equal + greater_equal + always + _num +} + +pub enum StencilOp { + _default // value 0 reserved for default-init + keep + zero + replace + incr_clamp + decr_clamp + invert + incr_wrap + decr_wrap + _num +} + +pub enum BlendFactor { + _default // value 0 reserved for default-init + zero + one + src_color + one_minus_src_color + src_alpha + one_minus_src_alpha + dst_color + one_minus_dst_color + dst_alpha + one_minus_dst_alpha + src_alpha_saturated + blend_color + one_minus_blend_color + blend_alpha + one_minus_blend_alpha + _num +} + +pub enum BlendOp { + _default // value 0 reserved for default-init + add + subtract + reverse_subtract + _num +} + +pub enum ColorMask { + _default = 0 // value 0 reserved for default-init + @none = 0x10 // special value for 'all channels disabled + r = 1 + g = 2 + b = 4 + a = 8 + rgb = 0x7 + rgba = 0xF +} + +pub enum Action { + _default + clear + load + dontcare + _num +} diff --git a/v_windows/v/old/vlib/sokol/gfx/gfx.v b/v_windows/v/old/vlib/sokol/gfx/gfx.v new file mode 100644 index 0000000..9480173 --- /dev/null +++ b/v_windows/v/old/vlib/sokol/gfx/gfx.v @@ -0,0 +1,266 @@ +module gfx + +import sokol.c + +pub const ( + version = 1 + used_import = c.used_import +) + +// setup and misc functions +[inline] +pub fn setup(desc &C.sg_desc) { + C.sg_setup(desc) +} + +[inline] +pub fn shutdown() { + C.sg_shutdown() +} + +[inline] +pub fn reset_state_cache() { + C.sg_reset_state_cache() +} + +// resource creation, destruction and updating +[inline] +pub fn make_buffer(desc &C.sg_buffer_desc) C.sg_buffer { + return C.sg_make_buffer(desc) +} + +[inline] +pub fn make_image(desc &C.sg_image_desc) C.sg_image { + return C.sg_make_image(desc) +} + +[inline] +pub fn make_shader(desc &C.sg_shader_desc) C.sg_shader { + return C.sg_make_shader(desc) +} + +[inline] +pub fn make_pipeline(desc &C.sg_pipeline_desc) C.sg_pipeline { + return C.sg_make_pipeline(desc) +} + +[inline] +pub fn make_pass(desc &C.sg_pass_desc) C.sg_pass { + return C.sg_make_pass(desc) +} + +[inline] +pub fn destroy_buffer(buf C.sg_buffer) { + C.sg_destroy_buffer(buf) +} + +[inline] +pub fn destroy_image(img C.sg_image) { + C.sg_destroy_image(img) +} + +[inline] +pub fn destroy_shader(shd C.sg_shader) { + C.sg_destroy_shader(shd) +} + +[inline] +pub fn destroy_pipeline(pip C.sg_pipeline) { + C.sg_destroy_pipeline(pip) +} + +[inline] +pub fn destroy_pass(pass C.sg_pass) { + C.sg_destroy_pass(pass) +} + +[inline] +pub fn update_buffer(buf C.sg_buffer, data &C.sg_range) { + C.sg_update_buffer(buf, data) +} + +[inline] +pub fn update_image(img C.sg_image, data &C.sg_image_data) { + C.sg_update_image(img, data) +} + +[inline] +pub fn append_buffer(buf C.sg_buffer, data &C.sg_range) int { + return C.sg_append_buffer(buf, data) +} + +[inline] +pub fn query_buffer_overflow(buf C.sg_buffer) bool { + return C.sg_query_buffer_overflow(buf) +} + +// rendering functions +[inline] +pub fn begin_default_pass(actions &C.sg_pass_action, width int, height int) { + C.sg_begin_default_pass(actions, width, height) +} + +[inline] +pub fn begin_pass(pass C.sg_pass, actions &C.sg_pass_action) { + C.sg_begin_pass(pass, actions) +} + +[inline] +pub fn apply_viewport(x int, y int, width int, height int, origin_top_left bool) { + C.sg_apply_viewport(x, y, width, height, origin_top_left) +} + +[inline] +pub fn apply_scissor_rect(x int, y int, width int, height int, origin_top_left bool) { + C.sg_apply_scissor_rect(x, y, width, height, origin_top_left) +} + +[inline] +pub fn apply_pipeline(pip C.sg_pipeline) { + C.sg_apply_pipeline(pip) +} + +[inline] +pub fn apply_bindings(bindings &C.sg_bindings) { + C.sg_apply_bindings(bindings) +} + +[inline] +pub fn apply_uniforms(stage int, ub_index int, data &C.sg_range) { + C.sg_apply_uniforms(stage, ub_index, data) +} + +[inline] +pub fn draw(base_element int, num_elements int, num_instances int) { + C.sg_draw(base_element, num_elements, num_instances) +} + +[inline] +pub fn end_pass() { + C.sg_end_pass() +} + +[inline] +pub fn commit() { + C.sg_commit() +} + +// getting information +[inline] +pub fn query_desc() C.sg_desc { + return C.sg_query_desc() +} + +[inline] +pub fn query_backend() Backend { + return Backend(C.sg_query_backend()) +} + +[inline] +pub fn query_features() C.sg_features { + return C.sg_query_features() +} + +[inline] +pub fn query_limits() C.sg_limits { + return C.sg_query_limits() +} + +[inline] +pub fn query_pixelformat(fmt PixelFormat) C.sg_pixelformat_info { + return C.sg_query_pixelformat(fmt) +} + +// get current state of a resource (INITIAL, ALLOC, VALID, FAILED, INVALID) +[inline] +pub fn query_buffer_state(buf C.sg_buffer) C.sg_resource_state { + return C.sg_query_buffer_state(buf) +} + +[inline] +pub fn query_image_state(img C.sg_image) C.sg_resource_state { + return C.sg_query_image_state(img) +} + +[inline] +pub fn query_shader_state(shd C.sg_shader) C.sg_resource_state { + return C.sg_query_shader_state(shd) +} + +[inline] +pub fn query_pipeline_state(pip C.sg_pipeline) C.sg_resource_state { + return C.sg_query_pipeline_state(pip) +} + +[inline] +pub fn query_pass_state(pass C.sg_pass) C.sg_resource_state { + return C.sg_query_pass_state(pass) +} + +// get runtime information about a resource +[inline] +pub fn query_buffer_info(buf C.sg_buffer) C.sg_buffer_info { + return C.sg_query_buffer_info(buf) +} + +[inline] +pub fn query_image_info(img C.sg_image) C.sg_image_info { + return C.sg_query_image_info(img) +} + +[inline] +pub fn query_shader_info(shd C.sg_shader) C.sg_shader_info { + return C.sg_query_shader_info(shd) +} + +[inline] +pub fn query_pipeline_info(pip C.sg_pipeline) C.sg_pipeline_info { + return C.sg_query_pipeline_info(pip) +} + +[inline] +pub fn query_pass_info(pass C.sg_pass) C.sg_pass_info { + return C.sg_query_pass_info(pass) +} + +// get resource creation desc struct with their default values replaced +[inline] +pub fn query_buffer_defaults(desc &C.sg_buffer) C.sg_buffer_desc { + return C.sg_query_buffer_defaults(unsafe { &C.sg_buffer_desc(desc) }) +} + +[inline] +pub fn query_image_defaults(desc &C.sg_image) C.sg_image_desc { + return C.sg_query_image_defaults(unsafe { &C.sg_image_desc(desc) }) +} + +[inline] +pub fn query_shader_defaults(desc &C.sg_shader) C.sg_shader_desc { + return C.sg_query_shader_defaults(unsafe { &C.sg_shader_desc(desc) }) +} + +[inline] +pub fn query_pipeline_defaults(desc &C.sg_pipeline) C.sg_pipeline_desc { + return C.sg_query_pipeline_defaults(unsafe { &C.sg_pipeline_desc(desc) }) +} + +[inline] +pub fn query_pass_defaults(desc &C.sg_pass) C.sg_pass_desc { + return C.sg_query_pass_defaults(unsafe { &C.sg_pass_desc(desc) }) +} + +// rendering contexts (optional) +[inline] +pub fn setup_context() C.sg_context { + return C.sg_setup_context() +} + +[inline] +pub fn activate_context(ctx_id C.sg_context) { + C.sg_activate_context(ctx_id) +} + +[inline] +pub fn discard_context(ctx_id C.sg_context) { + C.sg_discard_context(ctx_id) +} diff --git a/v_windows/v/old/vlib/sokol/gfx/gfx_funcs.v b/v_windows/v/old/vlib/sokol/gfx/gfx_funcs.v new file mode 100644 index 0000000..eb4e99b --- /dev/null +++ b/v_windows/v/old/vlib/sokol/gfx/gfx_funcs.v @@ -0,0 +1,71 @@ +module gfx + +// setup and misc functions +fn C.sg_setup(desc &C.sg_desc) +fn C.sg_shutdown() +fn C.sg_reset_state_cache() + +// resource creation, destruction and updating +fn C.sg_make_buffer(desc &C.sg_buffer_desc) C.sg_buffer +fn C.sg_make_image(desc &C.sg_image_desc) C.sg_image +fn C.sg_make_shader(desc &C.sg_shader_desc) C.sg_shader +fn C.sg_make_pipeline(desc &C.sg_pipeline_desc) C.sg_pipeline +fn C.sg_make_pass(desc &C.sg_pass_desc) C.sg_pass +fn C.sg_destroy_buffer(buf C.sg_buffer) +fn C.sg_destroy_image(img C.sg_image) +fn C.sg_destroy_shader(shd C.sg_shader) +fn C.sg_destroy_pipeline(pip C.sg_pipeline) +fn C.sg_destroy_pass(pass C.sg_pass) +fn C.sg_update_buffer(buf C.sg_buffer, data &C.sg_range) +fn C.sg_update_image(img C.sg_image, data &C.sg_image_data) +fn C.sg_append_buffer(buf C.sg_buffer, data &C.sg_range) int +fn C.sg_query_buffer_overflow(buf C.sg_buffer) bool + +// rendering functions +fn C.sg_begin_default_pass(actions &C.sg_pass_action, width int, height int) +fn C.sg_begin_pass(pass C.sg_pass, actions &C.sg_pass_action) +fn C.sg_apply_viewport(x int, y int, width int, height int, origin_top_left bool) +fn C.sg_apply_viewportf(x f32, y f32, width f32, height f32, origin_top_left bool) +fn C.sg_apply_scissor_rect(x int, y int, width int, height int, origin_top_left bool) +fn C.sg_apply_scissor_rectf(x f32, y f32, width f32, height f32, origin_top_left bool) +fn C.sg_apply_pipeline(pip C.sg_pipeline) +fn C.sg_apply_bindings(bindings &C.sg_bindings) + +// stage == sg_shader_stage +fn C.sg_apply_uniforms(stage int, ub_index int, data &C.sg_range) +fn C.sg_draw(base_element int, num_elements int, num_instances int) +fn C.sg_end_pass() +fn C.sg_commit() + +// getting information +fn C.sg_query_desc() C.sg_desc +fn C.sg_query_backend() Backend +fn C.sg_query_features() C.sg_features +fn C.sg_query_limits() C.sg_limits +fn C.sg_query_pixelformat(fmt PixelFormat) C.sg_pixelformat_info + +// get current state of a resource (INITIAL, ALLOC, VALID, FAILED, INVALID) +fn C.sg_query_buffer_state(buf C.sg_buffer) C.sg_resource_state +fn C.sg_query_image_state(img C.sg_image) C.sg_resource_state +fn C.sg_query_shader_state(shd C.sg_shader) C.sg_resource_state +fn C.sg_query_pipeline_state(pip C.sg_pipeline) C.sg_resource_state +fn C.sg_query_pass_state(pass C.sg_pass) C.sg_resource_state + +// get runtime information about a resource +fn C.sg_query_buffer_info(buf C.sg_buffer) C.sg_buffer_info +fn C.sg_query_image_info(img C.sg_image) C.sg_image_info +fn C.sg_query_shader_info(shd C.sg_shader) C.sg_shader_info +fn C.sg_query_pipeline_info(pip C.sg_pipeline) C.sg_pipeline_info +fn C.sg_query_pass_info(pass C.sg_pass) C.sg_pass_info + +// get resource creation desc struct with their default values replaced +fn C.sg_query_buffer_defaults(desc &C.sg_buffer_desc) C.sg_buffer_desc +fn C.sg_query_image_defaults(desc &C.sg_image_desc) C.sg_image_desc +fn C.sg_query_shader_defaults(desc &C.sg_shader_desc) C.sg_shader_desc +fn C.sg_query_pipeline_defaults(desc &C.sg_pipeline_desc) C.sg_pipeline_desc +fn C.sg_query_pass_defaults(desc &C.sg_pass_desc) C.sg_pass_desc + +// rendering contexts (optional) +fn C.sg_setup_context() C.sg_context +fn C.sg_activate_context(ctx_id C.sg_context) +fn C.sg_discard_context(ctx_id C.sg_context) diff --git a/v_windows/v/old/vlib/sokol/gfx/gfx_structs.v b/v_windows/v/old/vlib/sokol/gfx/gfx_structs.v new file mode 100644 index 0000000..1bf6c3a --- /dev/null +++ b/v_windows/v/old/vlib/sokol/gfx/gfx_structs.v @@ -0,0 +1,535 @@ +module gfx + +pub struct C.sg_desc { + _start_canary u32 + buffer_pool_size int + image_pool_size int + shader_pool_size int + pipeline_pool_size int + pass_pool_size int + context_pool_size int + context C.sg_context_desc + /* + // GL specific + gl_force_gles2 bool + // Metal-specific + mtl_device voidptr + mtl_renderpass_descriptor_cb fn() voidptr + mtl_drawable_cb fn() voidptr + mtl_global_uniform_buffer_size int + mtl_sampler_cache_size int + // D3D11-specific + d3d11_device voidptr + d3d11_device_context voidptr + d3d11_render_target_view_cb fn() voidptr + d3d11_depth_stencil_view_cb fn() voidptr + */ + _end_canary u32 +} + +pub struct C.sg_context_desc { + /* + sg_pixel_format color_format; + sg_pixel_format depth_format; + int sample_count; + sg_wgpu_context_desc wgpu; + */ + sample_count int + gl C.sg_gl_context_desc + metal C.sg_metal_context_desc + d3d11 C.sg_d3d11_context_desc + color_format PixelFormat + depth_format PixelFormat +} + +pub struct C.sg_gl_context_desc { + force_gles2 bool +} + +pub struct C.sg_metal_context_desc { + device voidptr + renderpass_descriptor_cb fn () voidptr + drawable_cb fn () voidptr +} + +pub struct C.sg_d3d11_context_desc { + device voidptr + device_context voidptr + render_target_view_cb fn () voidptr + depth_stencil_view_cb fn () voidptr +} + +pub struct C.sg_color_state { +pub mut: + pixel_format PixelFormat + write_mask ColorMask + blend C.sg_blend_state +} + +pub struct C.sg_pipeline_desc { +pub mut: + _start_canary u32 + shader C.sg_shader + layout C.sg_layout_desc + depth C.sg_depth_state + stencil C.sg_stencil_state + color_count int + colors [4]C.sg_color_state // C.SG_MAX_COLOR_ATTACHMENTS + primitive_type PrimitiveType + index_type IndexType + cull_mode CullMode + face_winding FaceWinding + sample_count int + blend_color C.sg_color + alpha_to_coverage_enabled bool + label &char = &char(0) + _end_canary u32 +} + +pub struct C.sg_pipeline_info { +} + +pub struct C.sg_pipeline { +pub: + id u32 +} + +pub fn (p C.sg_pipeline) free() { + C.sg_destroy_pipeline(p) +} + +pub struct C.sg_bindings { +pub mut: + _start_canary u32 + vertex_buffers [8]C.sg_buffer + vertex_buffer_offsets [8]int + index_buffer C.sg_buffer + index_buffer_offset int + vs_images [8]C.sg_image + fs_images [8]C.sg_image + _end_canary u32 +} + +pub fn (mut b C.sg_bindings) set_vert_image(index int, img C.sg_image) { + b.vs_images[index] = img +} + +pub fn (mut b C.sg_bindings) set_frag_image(index int, img C.sg_image) { + b.fs_images[index] = img +} + +pub fn (b &C.sg_bindings) update_vert_buffer(index int, data voidptr, element_size int, element_count int) { + range := C.sg_range{ + ptr: data + size: size_t(element_size * element_count) + } + C.sg_update_buffer(b.vertex_buffers[index], &range) +} + +pub fn (b &C.sg_bindings) append_vert_buffer(index int, data voidptr, element_size int, element_count int) int { + range := C.sg_range{ + ptr: data + size: size_t(element_size * element_count) + } + return C.sg_append_buffer(b.vertex_buffers[index], &range) +} + +pub fn (b &C.sg_bindings) update_index_buffer(data voidptr, element_size int, element_count int) { + range := C.sg_range{ + ptr: data + size: size_t(element_size * element_count) + } + C.sg_update_buffer(b.index_buffer, &range) +} + +pub fn (b &C.sg_bindings) append_index_buffer(data voidptr, element_size int, element_count int) int { + range := C.sg_range{ + ptr: data + size: size_t(element_size * element_count) + } + return C.sg_append_buffer(b.index_buffer, &range) +} + +[heap] +pub struct C.sg_shader_desc { +pub mut: + _start_canary u32 + attrs [16]C.sg_shader_attr_desc + vs C.sg_shader_stage_desc + fs C.sg_shader_stage_desc + label &char + _end_canary u32 +} + +pub fn (mut desc C.sg_shader_desc) set_vert_src(src string) &C.sg_shader_desc { + desc.vs.source = &char(src.str) + return desc +} + +pub fn (mut desc C.sg_shader_desc) set_frag_src(src string) &C.sg_shader_desc { + desc.fs.source = &char(src.str) + return desc +} + +pub fn (mut desc C.sg_shader_desc) set_vert_image(index int, name string) &C.sg_shader_desc { + desc.vs.images[index].name = &char(name.str) + desc.vs.images[index].image_type = ._2d + return desc +} + +pub fn (mut desc C.sg_shader_desc) set_frag_image(index int, name string) &C.sg_shader_desc { + desc.fs.images[index].name = &char(name.str) + desc.fs.images[index].image_type = ._2d + return desc +} + +pub fn (mut desc C.sg_shader_desc) set_vert_uniform_block_size(block_index int, size size_t) &C.sg_shader_desc { + desc.vs.uniform_blocks[block_index].size = size + return desc +} + +pub fn (mut desc C.sg_shader_desc) set_frag_uniform_block_size(block_index int, size size_t) &C.sg_shader_desc { + desc.fs.uniform_blocks[block_index].size = size + return desc +} + +pub fn (mut desc C.sg_shader_desc) set_vert_uniform(block_index int, uniform_index int, name string, @type UniformType, array_count int) &C.sg_shader_desc { + desc.vs.uniform_blocks[block_index].uniforms[uniform_index].name = &char(name.str) + desc.vs.uniform_blocks[block_index].uniforms[uniform_index].@type = @type + return desc +} + +pub fn (mut desc C.sg_shader_desc) set_frag_uniform(block_index int, uniform_index int, name string, @type UniformType, array_count int) &C.sg_shader_desc { + desc.fs.uniform_blocks[block_index].uniforms[uniform_index].name = &char(name.str) + desc.fs.uniform_blocks[block_index].uniforms[uniform_index].@type = @type + return desc +} + +pub fn (desc &C.sg_shader_desc) make_shader() C.sg_shader { + return C.sg_make_shader(desc) +} + +pub struct C.sg_shader_attr_desc { +pub mut: + name &char // GLSL vertex attribute name (only required for GLES2) + sem_name &char // HLSL semantic name + sem_index int // HLSL semantic index +} + +pub struct C.sg_shader_stage_desc { +pub mut: + source &char + bytecode C.sg_range + entry &char + uniform_blocks [4]C.sg_shader_uniform_block_desc + images [12]C.sg_shader_image_desc +} + +pub fn (mut desc C.sg_shader_stage_desc) set_image(index int, name string) C.sg_shader_stage_desc { + desc.images[index].name = &char(name.str) + desc.images[index].image_type = ._2d + return *desc +} + +pub struct C.sg_shader_uniform_block_desc { +pub mut: + size size_t + uniforms [16]C.sg_shader_uniform_desc +} + +pub struct C.sg_shader_uniform_desc { +pub mut: + name &char + @type UniformType + array_count int +} + +pub struct C.sg_shader_image_desc { +pub mut: + name &char + image_type ImageType +} + +pub struct C.sg_shader_info { +} + +pub struct C.sg_context { + id u32 +} + +pub struct C.sg_range { +pub mut: + ptr voidptr + size size_t +} + +pub struct C.sg_color { +pub mut: + r f32 + g f32 + b f32 + a f32 +} + +pub struct C.sg_shader { +pub: + id u32 +} + +pub fn (s C.sg_shader) free() { + C.sg_destroy_shader(s) +} + +pub struct C.sg_pass_desc { +pub mut: + _start_canary u32 + color_attachments [4]C.sg_pass_attachment_desc + depth_stencil_attachment C.sg_pass_attachment_desc + label &char + _end_canary u32 +} + +pub struct C.sg_pass_info { + info C.sg_slot_info +} + +pub struct C.sg_pass_action { +pub mut: + _start_canary u32 + colors [4]C.sg_color_attachment_action + depth C.sg_depth_attachment_action + stencil C.sg_stencil_attachment_action + _end_canary u32 +} + +pub struct C.sg_pass { + id u32 +} + +pub fn (p C.sg_pass) free() { + C.sg_destroy_pass(p) +} + +pub struct C.sg_buffer_desc { +pub mut: + _start_canary u32 + size size_t + @type BufferType + usage Usage + data C.sg_range + label &char + // GL specific + gl_buffers [2]u32 + // Metal specific + mtl_buffers [2]voidptr + // D3D11 specific + d3d11_buffer voidptr + _end_canary u32 +} + +pub struct C.sg_buffer_info { +} + +pub struct C.sg_buffer { + id u32 +} + +pub fn (b C.sg_buffer) free() { + C.sg_destroy_buffer(b) +} + +pub struct DepthLayers { + depth int + layers int +} + +pub struct C.sg_image_desc { +pub mut: + _start_canary u32 + @type ImageType + render_target bool + width int + height int + num_slices int + num_mipmaps int + usage Usage + pixel_format PixelFormat + sample_count int + min_filter Filter + mag_filter Filter + wrap_u Wrap + wrap_v Wrap + wrap_w Wrap + border_color BorderColor + max_anisotropy u32 + min_lod f32 + max_lod f32 + data C.sg_image_data + label &char + // GL specific + gl_textures [2]u32 + gl_texture_target u32 + // Metal specific + mtl_textures [2]voidptr + // D3D11 specific + d3d11_texture voidptr + d3d11_shader_resource_view voidptr + // WebGPU specific + wgpu_texture voidptr + _end_canary u32 +} + +pub struct C.sg_image_info { +pub mut: + slot C.sg_slot_info // resource pool slot info + upd_frame_index u32 // frame index of last sg_update_image() + num_slots int // number of renaming-slots for dynamically updated images + active_slot int // currently active write-slot for dynamically updated images +} + +pub struct C.sg_image { +pub: + id u32 +} + +pub fn (i C.sg_image) free() { + C.sg_destroy_image(i) +} + +pub const sg_cubeface_num = 6 + +pub const sg_max_mipmaps = 16 + +pub struct C.sg_image_data { +pub mut: + subimage [sg_cubeface_num][sg_max_mipmaps]C.sg_range +} + +pub struct C.sg_features { +pub: + instancing bool // hardware instancing supported + origin_top_left bool // framebuffer and texture origin is in top left corner + multiple_render_targets bool // offscreen render passes can have multiple render targets attached + msaa_render_targets bool // offscreen render passes support MSAA antialiasing + imagetype_3d bool // creation of SG_IMAGETYPE_3D images is supported + imagetype_array bool // creation of SG_IMAGETYPE_ARRAY images is supported + image_clamp_to_border bool // border color and clamp-to-border UV-wrap mode is supported + mrt_independent_blend_state bool // multiple-render-target rendering can use per-render-target blend state + mrt_independent_write_mask bool // multiple-render-target rendering can use per-render-target color write masks +} + +pub struct C.sg_limits { +pub: + max_image_size_2d u32 // max width/height of SG_IMAGETYPE_2D images + max_image_size_cube u32 // max width/height of SG_IMAGETYPE_CUBE images + max_image_size_3d u32 // max width/height/depth of SG_IMAGETYPE_3D images + max_image_size_array u32 // max width/height pf SG_IMAGETYPE_ARRAY images + max_image_array_layers u32 // max number of layers in SG_IMAGETYPE_ARRAY images + max_vertex_attrs u32 // <= SG_MAX_VERTEX_ATTRIBUTES (only on some GLES2 impls) +} + +pub struct C.sg_layout_desc { +pub mut: + buffers [8]C.sg_buffer_layout_desc + attrs [16]C.sg_vertex_attr_desc +} + +pub struct C.sg_buffer_layout_desc { +pub mut: + stride int + step_func VertexStep + step_rate int +} + +pub struct C.sg_vertex_attr_desc { +pub mut: + buffer_index int + offset int + format VertexFormat +} + +pub struct C.sg_stencil_state { + enabled bool + front C.sg_stencil_face_state + back C.sg_stencil_face_state + read_mask byte + write_mask byte + ref byte +} + +pub struct C.sg_depth_state { + pixel_format PixelFormat + compare CompareFunc + write_enabled bool + bias f32 + bias_slope_scale f32 + bias_clamp f32 +} + +pub struct C.sg_stencil_face_state { + fail_op StencilOp + depth_fail_op StencilOp + pass_op StencilOp + compare_func CompareFunc +} + +pub struct C.sg_blend_state { +pub mut: + enabled bool + src_factor_rgb BlendFactor + dst_factor_rgb BlendFactor + op_rgb BlendOp + src_factor_alpha BlendFactor + dst_factor_alpha BlendFactor + op_alpha BlendOp +} + +pub struct C.sg_color_attachment_action { +pub mut: + action Action + value C.sg_color +} + +/* +pub fn (mut action C.sg_color_attachment_action) set_color_values(r, g, b, a f32) { + action.val[0] = r + action.val[1] = g + action.val[2] = b + action.val[3] = a +} +*/ +pub struct C.sg_depth_attachment_action { +pub mut: + action Action + value f32 +} + +pub struct C.sg_stencil_attachment_action { +pub mut: + action Action + value byte +} + +pub struct C.sg_pixelformat_info { +pub: + sample bool // pixel format can be sampled in shaders + filter bool // pixel format can be sampled with filtering + render bool // pixel format can be used as render target + blend bool // alpha-blending is supported + msaa bool // pixel format can be used as MSAA render target + depth bool // pixel format is a depth format +} + +pub struct C.sg_pass_attachment_desc { +pub mut: + image C.sg_image + mip_level int + face int + // image sg_image + // mip_level int + // union { + // face int + // layer int + // slice int + // } +} diff --git a/v_windows/v/old/vlib/sokol/gfx/gfx_utils.v b/v_windows/v/old/vlib/sokol/gfx/gfx_utils.v new file mode 100644 index 0000000..c95ff69 --- /dev/null +++ b/v_windows/v/old/vlib/sokol/gfx/gfx_utils.v @@ -0,0 +1,17 @@ +module gfx + +pub fn create_clear_pass(r f32, g f32, b f32, a f32) C.sg_pass_action { + mut color_action := C.sg_color_attachment_action{ + action: Action(C.SG_ACTION_CLEAR) + value: C.sg_color{ + r: r + g: g + b: b + a: a + } + } + // color_action.set_color_values(r, g, b, a) + mut pass_action := C.sg_pass_action{} + pass_action.colors[0] = color_action + return pass_action +} diff --git a/v_windows/v/old/vlib/sokol/sapp/enums.v b/v_windows/v/old/vlib/sokol/sapp/enums.v new file mode 100644 index 0000000..8e0efd7 --- /dev/null +++ b/v_windows/v/old/vlib/sokol/sapp/enums.v @@ -0,0 +1,165 @@ +module sapp + +pub enum EventType { + invalid + key_down + key_up + char + mouse_down + mouse_up + mouse_scroll + mouse_move + mouse_enter + mouse_leave + touches_began + touches_moved + touches_ended + touches_cancelled + resized + iconified + restored + suspended + resumed + update_cursor + quit_requested + clipboard_pasted + num +} + +pub enum MouseButton { + invalid = -1 + left = 0 + right = 1 + middle = 2 +} + +pub enum Modifier { + shift = 1 //(1<<0) + ctrl = 2 //(1<<1) + alt = 4 //(1<<2) + super = 8 //(1<<3) +} + +pub enum KeyCode { + invalid = 0 + space = 32 + apostrophe = 39 //' + comma = 44 //, + minus = 45 //- + period = 46 //. + slash = 47 /// + _0 = 48 + _1 = 49 + _2 = 50 + _3 = 51 + _4 = 52 + _5 = 53 + _6 = 54 + _7 = 55 + _8 = 56 + _9 = 57 + semicolon = 59 //; + equal = 61 //= + a = 65 + b = 66 + c = 67 + d = 68 + e = 69 + f = 70 + g = 71 + h = 72 + i = 73 + j = 74 + k = 75 + l = 76 + m = 77 + n = 78 + o = 79 + p = 80 + q = 81 + r = 82 + s = 83 + t = 84 + u = 85 + v = 86 + w = 87 + x = 88 + y = 89 + z = 90 + left_bracket = 91 //[ + backslash = 92 //\ + right_bracket = 93 //] + grave_accent = 96 //` + world_1 = 161 // non-us #1 + world_2 = 162 // non-us #2 + escape = 256 + enter = 257 + tab = 258 + backspace = 259 + insert = 260 + delete = 261 + right = 262 + left = 263 + down = 264 + up = 265 + page_up = 266 + page_down = 267 + home = 268 + end = 269 + caps_lock = 280 + scroll_lock = 281 + num_lock = 282 + print_screen = 283 + pause = 284 + f1 = 290 + f2 = 291 + f3 = 292 + f4 = 293 + f5 = 294 + f6 = 295 + f7 = 296 + f8 = 297 + f9 = 298 + f10 = 299 + f11 = 300 + f12 = 301 + f13 = 302 + f14 = 303 + f15 = 304 + f16 = 305 + f17 = 306 + f18 = 307 + f19 = 308 + f20 = 309 + f21 = 310 + f22 = 311 + f23 = 312 + f24 = 313 + f25 = 314 + kp_0 = 320 + kp_1 = 321 + kp_2 = 322 + kp_3 = 323 + kp_4 = 324 + kp_5 = 325 + kp_6 = 326 + kp_7 = 327 + kp_8 = 328 + kp_9 = 329 + kp_decimal = 330 + kp_divide = 331 + kp_multiply = 332 + kp_subtract = 333 + kp_add = 334 + kp_enter = 335 + kp_equal = 336 + left_shift = 340 + left_control = 341 + left_alt = 342 + left_super = 343 + right_shift = 344 + right_control = 345 + right_alt = 346 + right_super = 347 + menu = 348 +} diff --git a/v_windows/v/old/vlib/sokol/sapp/sapp.v b/v_windows/v/old/vlib/sokol/sapp/sapp.v new file mode 100644 index 0000000..7f60dc7 --- /dev/null +++ b/v_windows/v/old/vlib/sokol/sapp/sapp.v @@ -0,0 +1,237 @@ +module sapp + +import sokol.gfx + +pub const ( + used_import = gfx.used_import +) + +// Android needs a global reference to `g_desc` +__global ( + g_desc C.sapp_desc +) + +pub fn create_desc() C.sg_desc { + metal_desc := C.sg_metal_context_desc{ + device: metal_get_device() + renderpass_descriptor_cb: metal_get_renderpass_descriptor + drawable_cb: metal_get_drawable + } + d3d11_desc := C.sg_d3d11_context_desc{ + device: d3d11_get_device() + device_context: d3d11_get_device_context() + render_target_view_cb: d3d11_get_render_target_view + depth_stencil_view_cb: d3d11_get_depth_stencil_view + } + return C.sg_desc{ + context: C.sg_context_desc{ + metal: metal_desc + d3d11: d3d11_desc + color_format: .bgra8 + } + image_pool_size: 1000 + } +} + +// returns true after sokol-app has been initialized +[inline] +pub fn isvalid() bool { + return C.sapp_isvalid() +} + +// returns the current framebuffer width in pixels +[inline] +pub fn width() int { + return C.sapp_width() +} + +// returns the current framebuffer height in pixels +[inline] +pub fn height() int { + return C.sapp_height() +} + +// returns true when high_dpi was requested and actually running in a high-dpi scenario +[inline] +pub fn high_dpi() bool { + return C.sapp_high_dpi() +} + +// returns the dpi scaling factor (window pixels to framebuffer pixels) +[inline] +pub fn dpi_scale() f32 { + return C.sapp_dpi_scale() +} + +// show or hide the mobile device onscreen keyboard +[inline] +pub fn show_keyboard(visible bool) { + C.sapp_show_keyboard(visible) +} + +// return true if the mobile device onscreen keyboard is currently shown +[inline] +pub fn keyboard_shown() bool { + return C.sapp_keyboard_shown() +} + +// show or hide the mouse cursor +[inline] +pub fn show_mouse(visible bool) { + C.sapp_show_mouse(visible) +} + +// show or hide the mouse cursor +[inline] +pub fn mouse_shown() bool { + return C.sapp_mouse_shown() +} + +[inline] +pub fn lock_mouse(locked bool) { + C.sapp_lock_mouse(locked) +} + +[inline] +pub fn mouse_locked() bool { + return C.sapp_mouse_locked() +} + +// return the userdata pointer optionally provided in sapp_desc +[inline] +pub fn userdata() voidptr { + return C.sapp_userdata() +} + +// return a copy of the sapp_desc structure +[inline] +pub fn query_desc() C.sapp_desc { + return C.sapp_query_desc() +} + +// initiate a "soft quit" (sends SAPP_EVENTTYPE_QUIT_REQUESTED) +[inline] +pub fn request_quit() { + C.sapp_request_quit() +} + +// cancel a pending quit (when SAPP_EVENTTYPE_QUIT_REQUESTED has been received) +[inline] +pub fn cancel_quit() { + C.sapp_cancel_quit() +} + +// intiate a "hard quit" (quit application without sending SAPP_EVENTTYPE_QUIT_REQUSTED) +[inline] +pub fn quit() { + C.sapp_quit() +} + +// call from inside event callback to consume the current event (don't forward to platform) +[inline] +pub fn consume_event() { + C.sapp_consume_event() +} + +// get the current frame counter (for comparison with sapp_event.frame_count) +[inline] +pub fn frame_count() u64 { + return C.sapp_frame_count() +} + +// write string into clipboard +[inline] +pub fn set_clipboard_string(str &char) { + C.sapp_set_clipboard_string(str) +} + +// read string from clipboard (usually during SAPP_EVENTTYPE_CLIPBOARD_PASTED) +[inline] +pub fn get_clipboard_string() &char { + return &char(C.sapp_get_clipboard_string()) +} + +// special run-function for SOKOL_NO_ENTRY (in standard mode this is an empty stub) +[inline] +pub fn run(desc &C.sapp_desc) { + g_desc = desc + C.sapp_run(desc) +} + +// GL: return true when GLES2 fallback is active (to detect fallback from GLES3) +[inline] +pub fn gles2() bool { + return C.sapp_gles2() +} + +// HTML5: enable or disable the hardwired "Leave Site?" dialog box +[inline] +pub fn html5_ask_leave_site(ask bool) { + C.sapp_html5_ask_leave_site(ask) +} + +// Metal: get ARC-bridged pointer to Metal device object +[inline] +pub fn metal_get_device() voidptr { + return voidptr(C.sapp_metal_get_device()) +} + +// Metal: get ARC-bridged pointer to this frame's renderpass descriptor +[inline] +pub fn metal_get_renderpass_descriptor() voidptr { + return voidptr(C.sapp_metal_get_renderpass_descriptor()) +} + +// Metal: get ARC-bridged pointer to current drawable +[inline] +pub fn metal_get_drawable() voidptr { + return voidptr(C.sapp_metal_get_drawable()) +} + +// macOS: get ARC-bridged pointer to macOS NSWindow +[inline] +pub fn macos_get_window() voidptr { + return voidptr(C.sapp_macos_get_window()) +} + +// iOS: get ARC-bridged pointer to iOS UIWindow +[inline] +pub fn ios_get_window() voidptr { + return voidptr(C.sapp_ios_get_window()) +} + +// D3D11: get pointer to ID3D11Device object +[inline] +pub fn d3d11_get_device() voidptr { + return voidptr(C.sapp_d3d11_get_device()) +} + +// D3D11: get pointer to ID3D11DeviceContext object +[inline] +pub fn d3d11_get_device_context() voidptr { + return voidptr(C.sapp_d3d11_get_device_context()) +} + +// D3D11: get pointer to ID3D11RenderTargetView object +[inline] +pub fn d3d11_get_render_target_view() voidptr { + return voidptr(C.sapp_d3d11_get_render_target_view()) +} + +// D3D11: get pointer to ID3D11DepthStencilView +[inline] +pub fn d3d11_get_depth_stencil_view() voidptr { + return voidptr(C.sapp_d3d11_get_depth_stencil_view()) +} + +// Win32: get the HWND window handle +[inline] +pub fn win32_get_hwnd() voidptr { + return voidptr(C.sapp_win32_get_hwnd()) +} + +// Android: get native activity handle +[inline] +pub fn android_get_native_activity() voidptr { + return voidptr(C.sapp_android_get_native_activity()) +} diff --git a/v_windows/v/old/vlib/sokol/sapp/sapp_funcs.v b/v_windows/v/old/vlib/sokol/sapp/sapp_funcs.v new file mode 100644 index 0000000..aae95c8 --- /dev/null +++ b/v_windows/v/old/vlib/sokol/sapp/sapp_funcs.v @@ -0,0 +1,105 @@ +module sapp + +// returns true after sokol-app has been initialized +fn C.sapp_isvalid() bool + +// returns the current framebuffer width in pixels +fn C.sapp_width() int +fn C.sapp_widthf() f32 + +// returns the current framebuffer height in pixels +fn C.sapp_height() int +fn C.sapp_heightf() f32 + +// returns true when high_dpi was requested and actually running in a high-dpi scenario +fn C.sapp_high_dpi() bool + +// returns the dpi scaling factor (window pixels to framebuffer pixels) +fn C.sapp_dpi_scale() f32 + +// show or hide the mobile device onscreen keyboard +fn C.sapp_show_keyboard(visible bool) + +// return true if the mobile device onscreen keyboard is currently shown +fn C.sapp_keyboard_shown() bool + +// show or hide the mouse cursor +fn C.sapp_show_mouse(visible bool) + +// return true if the mouse cursor is shown +fn C.sapp_mouse_shown() bool + +// lock or unlock the mouse cursor +fn C.sapp_lock_mouse(locked bool) + +// return true if the mouse cursor is locked +fn C.sapp_mouse_locked() bool + +// return the userdata pointer optionally provided in sapp_desc +fn C.sapp_userdata() voidptr + +// return a copy of the sapp_desc structure +fn C.sapp_query_desc() C.sapp_desc + +// initiate a "soft quit" (sends SAPP_EVENTTYPE_QUIT_REQUESTED) +fn C.sapp_request_quit() + +// cancel a pending quit (when SAPP_EVENTTYPE_QUIT_REQUESTED has been received) +fn C.sapp_cancel_quit() + +// intiate a "hard quit" (quit application without sending SAPP_EVENTTYPE_QUIT_REQUSTED) +fn C.sapp_quit() + +// call from inside event callback to consume the current event (don't forward to platform) +fn C.sapp_consume_event() + +// get the current frame counter (for comparison with sapp_event.frame_count) +fn C.sapp_frame_count() u64 + +// write string into clipboard +fn C.sapp_set_clipboard_string(str &byte) + +// read string from clipboard (usually during SAPP_EVENTTYPE_CLIPBOARD_PASTED) +fn C.sapp_get_clipboard_string() &byte + +// special run-function for SOKOL_NO_ENTRY (in standard mode this is an empty stub) +fn C.sapp_run(desc &C.sapp_desc) int + +// GL: return true when GLES2 fallback is active (to detect fallback from GLES3) +fn C.sapp_gles2() bool + +// HTML5: enable or disable the hardwired "Leave Site?" dialog box +fn C.sapp_html5_ask_leave_site(ask bool) + +// Metal: get ARC-bridged pointer to Metal device object +fn C.sapp_metal_get_device() voidptr + +// Metal: get ARC-bridged pointer to this frame's renderpass descriptor +fn C.sapp_metal_get_renderpass_descriptor() voidptr + +// Metal: get ARC-bridged pointer to current drawable +fn C.sapp_metal_get_drawable() voidptr + +// macOS: get ARC-bridged pointer to macOS NSWindow +fn C.sapp_macos_get_window() voidptr + +// iOS: get ARC-bridged pointer to iOS UIWindow +fn C.sapp_ios_get_window() voidptr + +// D3D11: get pointer to ID3D11Device object +fn C.sapp_d3d11_get_device() voidptr + +// D3D11: get pointer to ID3D11DeviceContext object +fn C.sapp_d3d11_get_device_context() voidptr + +// D3D11: get pointer to ID3D11RenderTargetView object +fn C.sapp_d3d11_get_render_target_view() voidptr + +// D3D11: get pointer to ID3D11DepthStencilView +fn C.sapp_d3d11_get_depth_stencil_view() voidptr + +// Win32: get the HWND window handle +fn C.sapp_win32_get_hwnd() voidptr + +// Android: get native activity handle +fn C.sapp_android_get_native_activity() voidptr diff --git a/v_windows/v/old/vlib/sokol/sapp/sapp_structs.v b/v_windows/v/old/vlib/sokol/sapp/sapp_structs.v new file mode 100644 index 0000000..a8ff8b1 --- /dev/null +++ b/v_windows/v/old/vlib/sokol/sapp/sapp_structs.v @@ -0,0 +1,104 @@ +module sapp + +pub struct C.sapp_desc { +pub: + init_cb fn () // these are the user-provided callbacks without user data + frame_cb fn () + cleanup_cb fn () + event_cb fn (&C.sapp_event) //&sapp_event) + fail_cb fn (&byte) + + user_data voidptr // these are the user-provided callbacks with user data + init_userdata_cb fn (voidptr) + frame_userdata_cb fn (voidptr) + cleanup_userdata_cb fn (voidptr) + event_userdata_cb fn (&C.sapp_event, voidptr) + fail_userdata_cb fn (&char, voidptr) + + width int // the preferred width of the window / canvas + height int // the preferred height of the window / canvas + sample_count int // MSAA sample count + swap_interval int // the preferred swap interval (ignored on some platforms) + high_dpi bool // whether the rendering canvas is full-resolution on HighDPI displays + fullscreen bool // whether the window should be created in fullscreen mode + alpha bool // whether the framebuffer should have an alpha channel (ignored on some platforms) + window_title &char // the window title as UTF-8 encoded string + user_cursor bool // if true, user is expected to manage cursor image in SAPP_EVENTTYPE_UPDATE_CURSOR + enable_clipboard bool // enable clipboard access, default is false + clipboard_size int // max size of clipboard content in bytes + enable_dragndrop bool // enable file dropping (drag'n'drop), default is false + max_dropped_files int // max number of dropped files to process (default: 1) + max_dropped_file_path_length int // max length in bytes of a dropped UTF-8 file path (default: 2048) + // backend-specific options + gl_force_gles2 bool // if true, setup GLES2/WebGL even if GLES3/WebGL2 is available + win32_console_utf8 bool // if true, set the output console codepage to UTF-8 + win32_console_create bool // if true, attach stdout/stderr to a new console window + win32_console_attach bool // if true, attach stdout/stderr to parent process + html5_canvas_name &char // the name (id) of the HTML5 canvas element, default is "canvas" + html5_canvas_resize bool // if true, the HTML5 canvas size is set to sapp_desc.width/height, otherwise canvas size is tracked + html5_preserve_drawing_buffer bool // HTML5 only: whether to preserve default framebuffer content between frames + html5_premultiplied_alpha bool // HTML5 only: whether the rendered pixels use premultiplied alpha convention + html5_ask_leave_site bool // initial state of the internal html5_ask_leave_site flag (see sapp_html5_ask_leave_site()) + ios_keyboard_resizes_canvas bool // if true, showing the iOS keyboard shrinks the canvas + // V patches + __v_native_render bool // V patch to allow for native rendering +} + +pub struct Event { +pub: + frame_count u64 + typ EventType + key_code KeyCode + char_code u32 + key_repeat bool + modifiers u32 + mouse_button MouseButton + mouse_x f32 + mouse_y f32 + mouse_dx f32 + mouse_dy f32 + scroll_x f32 + scroll_y f32 + num_touches int + touches [8]C.sapp_touchpoint + window_width int + window_height int + framebuffer_width int + framebuffer_height int +} + +pub struct C.sapp_event { +pub: + frame_count u64 + @type EventType + key_code KeyCode + char_code u32 + key_repeat bool + modifiers u32 + mouse_button MouseButton + mouse_x f32 + mouse_y f32 + mouse_dx f32 + mouse_dy f32 + scroll_x f32 + scroll_y f32 + num_touches int + touches [8]C.sapp_touchpoint + window_width int + window_height int + framebuffer_width int + framebuffer_height int +} + +pub fn (e &C.sapp_event) str() string { + t := e.@type + return 'evt: frame_count=$e.frame_count, type=$t' +} + +pub struct C.sapp_touchpoint { +pub: + identifier u64 + pos_x f32 + pos_y f32 + changed bool +} diff --git a/v_windows/v/old/vlib/sokol/sfons/sfons.v b/v_windows/v/old/vlib/sokol/sfons/sfons.v new file mode 100644 index 0000000..08d2dc6 --- /dev/null +++ b/v_windows/v/old/vlib/sokol/sfons/sfons.v @@ -0,0 +1,28 @@ +module sfons + +import fontstash + +const ( + // keep v from warning about unused imports + used_import = fontstash.used_import + 1 +) + +[inline] +pub fn create(width int, height int, flags int) &C.FONScontext { + return C.sfons_create(width, height, flags) +} + +[inline] +pub fn destroy(ctx &C.FONScontext) { + C.sfons_destroy(ctx) +} + +[inline] +pub fn rgba(r byte, g byte, b byte, a byte) u32 { + return C.sfons_rgba(r, g, b, a) +} + +[inline] +pub fn flush(ctx &C.FONScontext) { + C.sfons_flush(ctx) +} diff --git a/v_windows/v/old/vlib/sokol/sfons/sfons_funcs.v b/v_windows/v/old/vlib/sokol/sfons/sfons_funcs.v new file mode 100644 index 0000000..cec30d6 --- /dev/null +++ b/v_windows/v/old/vlib/sokol/sfons/sfons_funcs.v @@ -0,0 +1,6 @@ +module sfons + +fn C.sfons_create(width int, height int, flags int) &C.FONScontext +fn C.sfons_destroy(ctx &C.FONScontext) +fn C.sfons_rgba(r byte, g byte, b byte, a byte) u32 +fn C.sfons_flush(ctx &C.FONScontext) diff --git a/v_windows/v/old/vlib/sokol/sgl/sgl.v b/v_windows/v/old/vlib/sokol/sgl/sgl.v new file mode 100644 index 0000000..0129cbf --- /dev/null +++ b/v_windows/v/old/vlib/sokol/sgl/sgl.v @@ -0,0 +1,375 @@ +module sgl + +import sokol.gfx + +pub const ( + version = gfx.version + 1 +) + +// setup/shutdown/misc +[inline] +pub fn setup(desc &C.sgl_desc_t) { + C.sgl_setup(desc) +} + +[inline] +pub fn shutdown() { + C.sgl_shutdown() +} + +[inline] +pub fn error() C.sgl_error_t { + return C.sgl_error() +} + +[inline] +pub fn defaults() { + C.sgl_defaults() +} + +[inline] +pub fn rad(deg f32) f32 { + return C.sgl_rad(deg) +} + +[inline] +pub fn deg(rad f32) f32 { + return C.sgl_deg(rad) +} + +// create and destroy pipeline objects +[inline] +pub fn make_pipeline(desc &C.sg_pipeline_desc) C.sgl_pipeline { + return C.sgl_make_pipeline(desc) +} + +[inline] +pub fn destroy_pipeline(pip C.sgl_pipeline) { + C.sgl_destroy_pipeline(pip) +} + +// render state functions +[inline] +pub fn viewport(x int, y int, w int, h int, origin_top_left bool) { + C.sgl_viewport(x, y, w, h, origin_top_left) +} + +[inline] +pub fn scissor_rect(x int, y int, w int, h int, origin_top_left bool) { + C.sgl_scissor_rect(x, y, w, h, origin_top_left) +} + +[inline] +pub fn enable_texture() { + C.sgl_enable_texture() +} + +[inline] +pub fn disable_texture() { + C.sgl_disable_texture() +} + +[inline] +pub fn texture(img C.sg_image) { + C.sgl_texture(img) +} + +// pipeline stack functions +[inline] +pub fn default_pipeline() { + C.sgl_default_pipeline() +} + +[inline] +pub fn load_pipeline(pip C.sgl_pipeline) { + C.sgl_load_pipeline(pip) +} + +[inline] +pub fn push_pipeline() { + C.sgl_push_pipeline() +} + +[inline] +pub fn pop_pipeline() { + C.sgl_pop_pipeline() +} + +// matrix stack functions +[inline] +pub fn matrix_mode_modelview() { + C.sgl_matrix_mode_modelview() +} + +[inline] +pub fn matrix_mode_projection() { + C.sgl_matrix_mode_projection() +} + +[inline] +pub fn matrix_mode_texture() { + C.sgl_matrix_mode_texture() +} + +[inline] +pub fn load_identity() { + C.sgl_load_identity() +} + +[inline] +pub fn load_matrix(m []f32) { + C.sgl_load_matrix(m.data) +} + +[inline] +pub fn load_transpose_matrix(m []f32) { + C.sgl_load_transpose_matrix(m.data) +} + +[inline] +pub fn mult_matrix(m []f32) { + C.sgl_mult_matrix(m.data) +} + +[inline] +pub fn mult_transpose_matrix(m []f32) { + C.sgl_mult_transpose_matrix(m.data) +} + +[inline] +pub fn rotate(angle_rad f32, x f32, y f32, z f32) { + C.sgl_rotate(angle_rad, x, y, z) +} + +[inline] +pub fn scale(x f32, y f32, z f32) { + C.sgl_scale(x, y, z) +} + +[inline] +pub fn translate(x f32, y f32, z f32) { + C.sgl_translate(x, y, z) +} + +[inline] +pub fn frustum(l f32, r f32, b f32, t f32, n f32, f f32) { + C.sgl_frustum(l, r, b, t, n, f) +} + +[inline] +pub fn ortho(l f32, r f32, b f32, t f32, n f32, f f32) { + C.sgl_ortho(l, r, b, t, n, f) +} + +[inline] +pub fn perspective(fov_y f32, aspect f32, z_near f32, z_far f32) { + C.sgl_perspective(fov_y, aspect, z_near, z_far) +} + +[inline] +pub fn lookat(eye_x f32, eye_y f32, eye_z f32, center_x f32, center_y f32, center_z f32, up_x f32, up_y f32, up_z f32) { + C.sgl_lookat(eye_x, eye_y, eye_z, center_x, center_y, center_z, up_x, up_y, up_z) +} + +[inline] +pub fn push_matrix() { + C.sgl_push_matrix() +} + +[inline] +pub fn pop_matrix() { + C.sgl_pop_matrix() +} + +// these functions only set the internal 'current texcoord / color' (valid inside or outside begin/end) +[inline] +pub fn t2f(u f32, v f32) { + C.sgl_t2f(u, v) +} + +[inline] +pub fn c3f(r f32, g f32, b f32) { + C.sgl_c3f(r, g, b) +} + +[inline] +pub fn c4f(r f32, g f32, b f32, a f32) { + C.sgl_c4f(r, g, b, a) +} + +[inline] +pub fn c3b(r byte, g byte, b byte) { + C.sgl_c3b(r, g, b) +} + +[inline] +pub fn c4b(r byte, g byte, b byte, a byte) { + C.sgl_c4b(r, g, b, a) +} + +[inline] +pub fn c1i(rgba u32) { + C.sgl_c1i(rgba) +} + +// define primitives, each begin/end is one draw command +[inline] +pub fn begin_points() { + C.sgl_begin_points() +} + +[inline] +pub fn begin_lines() { + C.sgl_begin_lines() +} + +[inline] +pub fn begin_line_strip() { + C.sgl_begin_line_strip() +} + +[inline] +pub fn begin_triangles() { + C.sgl_begin_triangles() +} + +[inline] +pub fn begin_triangle_strip() { + C.sgl_begin_triangle_strip() +} + +[inline] +pub fn begin_quads() { + C.sgl_begin_quads() +} + +[inline] +pub fn v2f(x f32, y f32) { + C.sgl_v2f(x, y) +} + +[inline] +pub fn v3f(x f32, y f32, z f32) { + C.sgl_v3f(x, y, z) +} + +[inline] +pub fn v2f_t2f(x f32, y f32, u f32, v f32) { + C.sgl_v2f_t2f(x, y, u, v) +} + +[inline] +pub fn v3f_t2f(x f32, y f32, z f32, u f32, v f32) { + C.sgl_v3f_t2f(x, y, z, u, v) +} + +[inline] +pub fn v2f_c3f(x f32, y f32, r f32, g f32, b f32) { + C.sgl_v2f_c3f(x, y, r, g, b) +} + +[inline] +pub fn v2f_c3b(x f32, y f32, r byte, g byte, b byte) { + C.sgl_v2f_c3b(x, y, r, g, b) +} + +[inline] +pub fn v2f_c4f(x f32, y f32, r f32, g f32, b f32, a f32) { + C.sgl_v2f_c4f(x, y, r, g, b, a) +} + +[inline] +pub fn v2f_c4b(x f32, y f32, r byte, g byte, b byte, a byte) { + C.sgl_v2f_c4b(x, y, r, g, b, a) +} + +[inline] +pub fn v2f_c1i(x f32, y f32, rgba u32) { + C.sgl_v2f_c1i(x, y, rgba) +} + +[inline] +pub fn v3f_c3f(x f32, y f32, z f32, r f32, g f32, b f32) { + C.sgl_v3f_c3f(x, y, z, r, g, b) +} + +[inline] +pub fn v3f_c3b(x f32, y f32, z f32, r byte, g byte, b byte) { + C.sgl_v3f_c3b(x, y, z, r, g, b) +} + +[inline] +pub fn v3f_c4f(x f32, y f32, z f32, r f32, g f32, b f32, a f32) { + C.sgl_v3f_c4f(x, y, z, r, g, b, a) +} + +[inline] +pub fn v3f_c4b(x f32, y f32, z f32, r byte, g byte, b byte, a byte) { + C.sgl_v3f_c4b(x, y, z, r, g, b, a) +} + +[inline] +pub fn v3f_c1i(x f32, y f32, z f32, rgba u32) { + C.sgl_v3f_c1i(x, y, z, rgba) +} + +[inline] +pub fn v2f_t2f_c3f(x f32, y f32, u f32, v f32, r f32, g f32, b f32) { + C.sgl_v2f_t2f_c3f(x, y, u, v, r, g, b) +} + +[inline] +pub fn v2f_t2f_c3b(x f32, y f32, u f32, v f32, r byte, g byte, b byte) { + C.sgl_v2f_t2f_c3b(x, y, u, v, r, g, b) +} + +[inline] +pub fn v2f_t2f_c4f(x f32, y f32, u f32, v f32, r f32, g f32, b f32, a f32) { + C.sgl_v2f_t2f_c4f(x, y, u, v, r, g, b, a) +} + +[inline] +pub fn v2f_t2f_c4b(x f32, y f32, u f32, v f32, r byte, g byte, b byte, a byte) { + C.sgl_v2f_t2f_c4b(x, y, u, v, r, g, b, a) +} + +[inline] +pub fn v2f_t2f_c1i(x f32, y f32, u f32, v f32, rgba u32) { + C.sgl_v2f_t2f_c1i(x, y, u, v, rgba) +} + +[inline] +pub fn v3f_t2f_c3f(x f32, y f32, z f32, u f32, v f32, r f32, g f32, b f32) { + C.sgl_v3f_t2f_c3f(x, y, z, u, v, r, g, b) +} + +[inline] +pub fn v3f_t2f_c3b(x f32, y f32, z f32, u f32, v f32, r byte, g byte, b byte) { + C.sgl_v3f_t2f_c3b(x, y, z, u, v, r, g, b) +} + +[inline] +pub fn v3f_t2f_c4f(x f32, y f32, z f32, u f32, v f32, r f32, g f32, b f32, a f32) { + C.sgl_v3f_t2f_c4f(x, y, z, u, v, r, g, b, a) +} + +[inline] +pub fn v3f_t2f_c4b(x f32, y f32, z f32, u f32, v f32, r byte, g byte, b byte, a byte) { + C.sgl_v3f_t2f_c4b(x, y, z, u, v, r, g, b, a) +} + +[inline] +pub fn v3f_t2f_c1i(x f32, y f32, z f32, u f32, v f32, rgba u32) { + C.sgl_v3f_t2f_c1i(x, y, z, u, v, rgba) +} + +[inline] +pub fn end() { + C.sgl_end() +} + +// render everything +[inline] +pub fn draw() { + C.sgl_draw() +} diff --git a/v_windows/v/old/vlib/sokol/sgl/sgl_funcs.v b/v_windows/v/old/vlib/sokol/sgl/sgl_funcs.v new file mode 100644 index 0000000..16e980d --- /dev/null +++ b/v_windows/v/old/vlib/sokol/sgl/sgl_funcs.v @@ -0,0 +1,91 @@ +module sgl + +// setup/shutdown/misc +fn C.sgl_setup(desc &C.sgl_desc_t) +fn C.sgl_shutdown() +fn C.sgl_error() C.sgl_error_t +fn C.sgl_defaults() +fn C.sgl_rad(deg f32) f32 +fn C.sgl_deg(rad f32) f32 + +// create and destroy pipeline objects +fn C.sgl_make_pipeline(desc &C.sg_pipeline_desc) C.sgl_pipeline +fn C.sgl_destroy_pipeline(pip C.sgl_pipeline) + +// render state functions +fn C.sgl_viewport(x int, y int, w int, h int, origin_top_left bool) +fn C.sgl_viewportf(x f32, y f32, w f32, h f32, origin_top_left bool) +fn C.sgl_scissor_rect(x int, y int, w int, h int, origin_top_left bool) +fn C.sgl_scissor_rectf(x f32, y f32, w f32, h f32, origin_top_left bool) +fn C.sgl_enable_texture() +fn C.sgl_disable_texture() +fn C.sgl_texture(img C.sg_image) + +// pipeline stack functions +fn C.sgl_default_pipeline() +fn C.sgl_load_pipeline(pip C.sgl_pipeline) +fn C.sgl_push_pipeline() +fn C.sgl_pop_pipeline() + +// matrix stack functions +fn C.sgl_matrix_mode_modelview() +fn C.sgl_matrix_mode_projection() +fn C.sgl_matrix_mode_texture() +fn C.sgl_load_identity() +fn C.sgl_load_matrix(m [16]f32) +fn C.sgl_load_transpose_matrix(m [16]f32) +fn C.sgl_mult_matrix(m [16]f32) +fn C.sgl_mult_transpose_matrix(m [16]f32) +fn C.sgl_rotate(angle_rad f32, x f32, y f32, z f32) +fn C.sgl_scale(x f32, y f32, z f32) +fn C.sgl_translate(x f32, y f32, z f32) +fn C.sgl_frustum(l f32, r f32, b f32, t f32, n f32, f f32) +fn C.sgl_ortho(l f32, r f32, b f32, t f32, n f32, f f32) +fn C.sgl_perspective(fov_y f32, aspect f32, z_near f32, z_far f32) +fn C.sgl_lookat(eye_x f32, eye_y f32, eye_z f32, center_x f32, center_y f32, center_z f32, up_x f32, up_y f32, up_z f32) +fn C.sgl_push_matrix() +fn C.sgl_pop_matrix() + +// these functions only set the internal 'current texcoord / color' (valid inside or outside begin/end) +fn C.sgl_t2f(u f32, v f32) +fn C.sgl_c3f(r f32, g f32, b f32) +fn C.sgl_c4f(r f32, g f32, b f32, a f32) +fn C.sgl_c3b(r byte, g byte, b byte) +fn C.sgl_c4b(r byte, g byte, b byte, a byte) +fn C.sgl_c1i(rgba u32) + +// define primitives, each begin/end is one draw command +fn C.sgl_begin_points() +fn C.sgl_begin_lines() +fn C.sgl_begin_line_strip() +fn C.sgl_begin_triangles() +fn C.sgl_begin_triangle_strip() +fn C.sgl_begin_quads() +fn C.sgl_v2f(x f32, y f32) +fn C.sgl_v3f(x f32, y f32, z f32) +fn C.sgl_v2f_t2f(x f32, y f32, u f32, v f32) +fn C.sgl_v3f_t2f(x f32, y f32, z f32, u f32, v f32) +fn C.sgl_v2f_c3f(x f32, y f32, r f32, g f32, b f32) +fn C.sgl_v2f_c3b(x f32, y f32, r byte, g byte, b byte) +fn C.sgl_v2f_c4f(x f32, y f32, r f32, g f32, b f32, a f32) +fn C.sgl_v2f_c4b(x f32, y f32, r byte, g byte, b byte, a byte) +fn C.sgl_v2f_c1i(x f32, y f32, rgba u32) +fn C.sgl_v3f_c3f(x f32, y f32, z f32, r f32, g f32, b f32) +fn C.sgl_v3f_c3b(x f32, y f32, z f32, r byte, g byte, b byte) +fn C.sgl_v3f_c4f(x f32, y f32, z f32, r f32, g f32, b f32, a f32) +fn C.sgl_v3f_c4b(x f32, y f32, z f32, r byte, g byte, b byte, a byte) +fn C.sgl_v3f_c1i(x f32, y f32, z f32, rgba u32) +fn C.sgl_v2f_t2f_c3f(x f32, y f32, u f32, v f32, r f32, g f32, b f32) +fn C.sgl_v2f_t2f_c3b(x f32, y f32, u f32, v f32, r byte, g byte, b byte) +fn C.sgl_v2f_t2f_c4f(x f32, y f32, u f32, v f32, r f32, g f32, b f32, a f32) +fn C.sgl_v2f_t2f_c4b(x f32, y f32, u f32, v f32, r byte, g byte, b byte, a byte) +fn C.sgl_v2f_t2f_c1i(x f32, y f32, u f32, v f32, rgba u32) +fn C.sgl_v3f_t2f_c3f(x f32, y f32, z f32, u f32, v f32, r f32, g f32, b f32) +fn C.sgl_v3f_t2f_c3b(x f32, y f32, z f32, u f32, v f32, r byte, g byte, b byte) +fn C.sgl_v3f_t2f_c4f(x f32, y f32, z f32, u f32, v f32, r f32, g f32, b f32, a f32) +fn C.sgl_v3f_t2f_c4b(x f32, y f32, z f32, u f32, v f32, r byte, g byte, b byte, a byte) +fn C.sgl_v3f_t2f_c1i(x f32, y f32, z f32, u f32, v f32, rgba u32) +fn C.sgl_end() + +// render everything +fn C.sgl_draw() diff --git a/v_windows/v/old/vlib/sokol/sgl/sgl_structs.v b/v_windows/v/old/vlib/sokol/sgl/sgl_structs.v new file mode 100644 index 0000000..25753c4 --- /dev/null +++ b/v_windows/v/old/vlib/sokol/sgl/sgl_structs.v @@ -0,0 +1,24 @@ +module sgl + +// should be in a proper module +pub enum SglError { + no_error + vertices_full + commands_full + stack_overflow + stack_underfloat +} + +pub struct C.sgl_pipeline { + id u32 +} + +pub struct C.sgl_desc_t { + max_vertices int // size for vertex buffer + max_commands int // size of uniform- and command-buffers + pipeline_pool_size int // size of the internal pipeline pool, default is 64 + color_format C.sg_pixel_format + depth_format C.sg_pixel_format + sample_count int + face_winding C.sg_face_winding // default front face winding is CCW +} diff --git a/v_windows/v/old/vlib/sokol/sokol.v b/v_windows/v/old/vlib/sokol/sokol.v new file mode 100644 index 0000000..8c9a632 --- /dev/null +++ b/v_windows/v/old/vlib/sokol/sokol.v @@ -0,0 +1,19 @@ +module sokol + +import sokol.c +import sokol.f + +pub const ( + used_import = c.used_import + f.used_import +) + +/* +pub enum Key { + up=C.SAPP_KEYCODE_UP + left = C.SAPP_KEYCODE_LEFT + right =C.SAPP_KEYCODE_RIGHT + down = C.SAPP_KEYCODE_DOWN + escape = C.SAPP_KEYCODE_ESCAPE + space = C.SAPP_KEYCODE_SPACE +} +*/ diff --git a/v_windows/v/old/vlib/sqlite/README.md b/v_windows/v/old/vlib/sqlite/README.md new file mode 100644 index 0000000..d7462c6 --- /dev/null +++ b/v_windows/v/old/vlib/sqlite/README.md @@ -0,0 +1,16 @@ +# Install SQLite Dependency + +**Fedora 31**: + +`sudo dnf -y install sqlite-devel` + + +**Ubuntu 20.04**: + +`sudo apt install -y libsqlite3-dev` + + +**Windows**: +- Download the source zip from [SQLite Downloads](https://sqlite.org/download.html) +- Create a new `sqlite` subfolder inside `v/thirdparty` +- Extract the zip into that folder diff --git a/v_windows/v/old/vlib/sqlite/orm.v b/v_windows/v/old/vlib/sqlite/orm.v new file mode 100644 index 0000000..a1df1c7 --- /dev/null +++ b/v_windows/v/old/vlib/sqlite/orm.v @@ -0,0 +1,161 @@ +module sqlite + +import orm +import time + +// sql expr + +pub fn (db DB) @select(config orm.SelectConfig, data orm.QueryData, where orm.QueryData) ?[][]orm.Primitive { + query := orm.orm_select_gen(config, '`', true, '?', 1, where) + stmt := db.new_init_stmt(query) ? + mut c := 1 + sqlite_stmt_binder(stmt, where, query, mut c) ? + sqlite_stmt_binder(stmt, data, query, mut c) ? + + defer { + stmt.finalize() + } + + mut ret := [][]orm.Primitive{} + + if config.is_count { + step := stmt.step() + if step !in [sqlite_row, sqlite_ok, sqlite_done] { + return db.error_message(step, query) + } + count := stmt.sqlite_select_column(0, 8) ? + ret << [count] + return ret + } + for { + step := stmt.step() + if step == sqlite_done { + break + } + if step != sqlite_ok && step != sqlite_row { + break + } + mut row := []orm.Primitive{} + for i, typ in config.types { + primitive := stmt.sqlite_select_column(i, typ) ? + row << primitive + } + ret << row + } + return ret +} + +// sql stmt + +pub fn (db DB) insert(table string, data orm.QueryData) ? { + query := orm.orm_stmt_gen(table, '`', .insert, true, '?', 1, data, orm.QueryData{}) + sqlite_stmt_worker(db, query, data, orm.QueryData{}) ? +} + +pub fn (db DB) update(table string, data orm.QueryData, where orm.QueryData) ? { + query := orm.orm_stmt_gen(table, '`', .update, true, '?', 1, data, where) + sqlite_stmt_worker(db, query, data, where) ? +} + +pub fn (db DB) delete(table string, where orm.QueryData) ? { + query := orm.orm_stmt_gen(table, '`', .delete, true, '?', 1, orm.QueryData{}, where) + sqlite_stmt_worker(db, query, orm.QueryData{}, where) ? +} + +pub fn (db DB) last_id() orm.Primitive { + query := 'SELECT last_insert_rowid();' + id := db.q_int(query) + return orm.Primitive(id) +} + +// table +pub fn (db DB) create(table string, fields []orm.TableField) ? { + query := orm.orm_table_gen(table, '`', true, 0, fields, sqlite_type_from_v, false) or { + return err + } + sqlite_stmt_worker(db, query, orm.QueryData{}, orm.QueryData{}) ? +} + +pub fn (db DB) drop(table string) ? { + query := 'DROP TABLE `$table`;' + sqlite_stmt_worker(db, query, orm.QueryData{}, orm.QueryData{}) ? +} + +// helper + +fn sqlite_stmt_worker(db DB, query string, data orm.QueryData, where orm.QueryData) ? { + stmt := db.new_init_stmt(query) ? + mut c := 1 + sqlite_stmt_binder(stmt, data, query, mut c) ? + sqlite_stmt_binder(stmt, where, query, mut c) ? + stmt.orm_step(query) ? + stmt.finalize() +} + +fn sqlite_stmt_binder(stmt Stmt, d orm.QueryData, query string, mut c &int) ? { + for data in d.data { + err := bind(stmt, c, data) + + if err != 0 { + return stmt.db.error_message(err, query) + } + c++ + } +} + +fn bind(stmt Stmt, c &int, data orm.Primitive) int { + mut err := 0 + match data { + i8, i16, int, byte, u16, u32, bool { + err = stmt.bind_int(c, int(data)) + } + i64, u64 { + err = stmt.bind_i64(c, i64(data)) + } + f32, f64 { + err = stmt.bind_f64(c, unsafe { *(&f64(&data)) }) + } + string { + err = stmt.bind_text(c, data) + } + time.Time { + err = stmt.bind_int(c, int(data.unix)) + } + orm.InfixType { + err = bind(stmt, c, data.right) + } + } + return err +} + +fn (stmt Stmt) sqlite_select_column(idx int, typ int) ?orm.Primitive { + mut primitive := orm.Primitive(0) + + if typ in orm.nums || typ == -1 { + primitive = stmt.get_int(idx) + } else if typ in orm.num64 { + primitive = stmt.get_i64(idx) + } else if typ in orm.float { + primitive = stmt.get_f64(idx) + } else if typ == orm.string { + primitive = stmt.get_text(idx).clone() + } else if typ == orm.time { + primitive = time.unix(stmt.get_int(idx)) + } else { + return error('Unknown type $typ') + } + + return primitive +} + +fn sqlite_type_from_v(typ int) ?string { + return if typ in orm.nums || typ < 0 || typ in orm.num64 { + 'INTEGER' + } else if typ in orm.float { + 'REAL' + } else if typ == orm.string { + 'TEXT' + } else { + error('Unknown type $typ') + } +} diff --git a/v_windows/v/old/vlib/sqlite/sqlite.v b/v_windows/v/old/vlib/sqlite/sqlite.v new file mode 100644 index 0000000..4bbe40e --- /dev/null +++ b/v_windows/v/old/vlib/sqlite/sqlite.v @@ -0,0 +1,236 @@ +module sqlite + +$if freebsd || openbsd { + #flag -I/usr/local/include + #flag -L/usr/local/lib +} +$if windows { + #flag windows -I@VEXEROOT/thirdparty/sqlite + #flag windows -L@VEXEROOT/thirdparty/sqlite + #flag windows @VEXEROOT/thirdparty/sqlite/sqlite3.o +} $else { + #flag -lsqlite3 +} + +#include "sqlite3.h" + +const ( + sqlite_ok = 0 + sqlite_error = 1 + sqlite_row = 100 + sqlite_done = 101 +) + +struct C.sqlite3 { +} + +struct C.sqlite3_stmt { +} + +struct Stmt { + stmt &C.sqlite3_stmt + db &DB +} + +struct SQLError { + msg string + code int +} + +// +pub struct DB { +pub mut: + is_open bool +mut: + conn &C.sqlite3 +} + +pub fn (db DB) str() string { + return 'sqlite.DB{ conn: ' + ptr_str(db.conn) + ' }' +} + +pub struct Row { +pub mut: + vals []string +} + +// +fn C.sqlite3_open(&char, &&C.sqlite3) int + +fn C.sqlite3_close(&C.sqlite3) int + +fn C.sqlite3_last_insert_rowid(&C.sqlite3) i64 + +// +fn C.sqlite3_prepare_v2(&C.sqlite3, &char, int, &&C.sqlite3_stmt, &&char) int + +fn C.sqlite3_step(&C.sqlite3_stmt) int + +fn C.sqlite3_finalize(&C.sqlite3_stmt) int + +// +fn C.sqlite3_column_name(&C.sqlite3_stmt, int) &char + +fn C.sqlite3_column_text(&C.sqlite3_stmt, int) &byte + +fn C.sqlite3_column_int(&C.sqlite3_stmt, int) int + +fn C.sqlite3_column_int64(&C.sqlite3_stmt, int) i64 + +fn C.sqlite3_column_double(&C.sqlite3_stmt, int) f64 + +fn C.sqlite3_column_count(&C.sqlite3_stmt) int + +// +fn C.sqlite3_errstr(int) &char + +fn C.sqlite3_errmsg(&C.sqlite3) &char + +fn C.sqlite3_free(voidptr) + +// connect Opens the connection with a database. +pub fn connect(path string) ?DB { + db := &C.sqlite3(0) + code := C.sqlite3_open(&char(path.str), &db) + if code != 0 { + return IError(&SQLError{ + msg: unsafe { cstring_to_vstring(&char(C.sqlite3_errstr(code))) } + code: code + }) + } + return DB{ + conn: db + is_open: true + } +} + +// close Closes the DB. +// TODO: For all functions, determine whether the connection is +// closed first, and determine what to do if it is +pub fn (mut db DB) close() ?bool { + code := C.sqlite3_close(db.conn) + if code == 0 { + db.is_open = false + } else { + return IError(&SQLError{ + msg: unsafe { cstring_to_vstring(&char(C.sqlite3_errstr(code))) } + code: code + }) + } + return true // successfully closed +} + +// Only for V ORM +fn get_int_from_stmt(stmt &C.sqlite3_stmt) int { + x := C.sqlite3_step(stmt) + if x != C.SQLITE_OK && x != C.SQLITE_DONE { + C.puts(C.sqlite3_errstr(x)) + } + + res := C.sqlite3_column_int(stmt, 0) + C.sqlite3_finalize(stmt) + return res +} + +// Returns last insert rowid +// https://www.sqlite.org/c3ref/last_insert_rowid.html +pub fn (db DB) last_insert_rowid() i64 { + return C.sqlite3_last_insert_rowid(db.conn) +} + +// Returns a single cell with value int. +pub fn (db DB) q_int(query string) int { + stmt := &C.sqlite3_stmt(0) + C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0) + C.sqlite3_step(stmt) + + res := C.sqlite3_column_int(stmt, 0) + C.sqlite3_finalize(stmt) + return res +} + +// Returns a single cell with value string. +pub fn (db DB) q_string(query string) string { + stmt := &C.sqlite3_stmt(0) + defer { + C.sqlite3_finalize(stmt) + } + C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0) + C.sqlite3_step(stmt) + + val := unsafe { &byte(C.sqlite3_column_text(stmt, 0)) } + return if val != &byte(0) { unsafe { tos_clone(val) } } else { '' } +} + +// Execute the query on db, return an array of all the results, alongside any result code. +// Result codes: https://www.sqlite.org/rescode.html +pub fn (db DB) exec(query string) ([]Row, int) { + stmt := &C.sqlite3_stmt(0) + C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0) + nr_cols := C.sqlite3_column_count(stmt) + mut res := 0 + mut rows := []Row{} + for { + res = C.sqlite3_step(stmt) + // Result Code SQLITE_ROW; Another row is available + if res != 100 { + // C.puts(C.sqlite3_errstr(res)) + break + } + mut row := Row{} + for i in 0 .. nr_cols { + val := unsafe { &byte(C.sqlite3_column_text(stmt, i)) } + if val == &byte(0) { + row.vals << '' + } else { + row.vals << unsafe { tos_clone(val) } + } + } + rows << row + } + C.sqlite3_finalize(stmt) + return rows, res +} + +// Execute a query, handle error code +// Return the first row from the resulting table +pub fn (db DB) exec_one(query string) ?Row { + rows, code := db.exec(query) + if rows.len == 0 { + return IError(&SQLError{ + msg: 'No rows' + code: code + }) + } else if code != 101 { + return IError(&SQLError{ + msg: unsafe { cstring_to_vstring(&char(C.sqlite3_errstr(code))) } + code: code + }) + } + return rows[0] +} + +pub fn (db DB) error_message(code int, query string) IError { + msg := unsafe { cstring_to_vstring(&char(C.sqlite3_errmsg(db.conn))) } + return IError(&SQLError{ + msg: '$msg ($code) ($query)' + code: code + }) +} + +// In case you don't expect any result, but still want an error code +// e.g. INSERT INTO ... VALUES (...) +pub fn (db DB) exec_none(query string) int { + _, code := db.exec(query) + return code +} + +/* +TODO +pub fn (db DB) exec_param(query string, param string) []Row { +} +*/ + +pub fn (db DB) create_table(table_name string, columns []string) { + db.exec('create table if not exists $table_name (' + columns.join(',\n') + ')') +} diff --git a/v_windows/v/old/vlib/sqlite/sqlite_orm_test.v b/v_windows/v/old/vlib/sqlite/sqlite_orm_test.v new file mode 100644 index 0000000..efbd9fe --- /dev/null +++ b/v_windows/v/old/vlib/sqlite/sqlite_orm_test.v @@ -0,0 +1,70 @@ +import orm +import sqlite + +fn test_sqlite_orm() { + sdb := sqlite.connect(':memory:') or { panic(err) } + db := orm.Connection(sdb) + db.create('Test', [ + orm.TableField{ + name: 'id' + typ: 7 + attrs: [ + StructAttribute{ + name: 'primary' + }, + StructAttribute{ + name: 'sql' + has_arg: true + kind: .plain + arg: 'serial' + }, + ] + }, + orm.TableField{ + name: 'name' + typ: 18 + attrs: [] + }, + orm.TableField{ + name: 'age' + typ: 8 + }, + ]) or { panic(err) } + + db.insert('Test', orm.QueryData{ + fields: ['name', 'age'] + data: [orm.string_to_primitive('Louis'), orm.i64_to_primitive(100)] + }) or { panic(err) } + + res := db.@select(orm.SelectConfig{ + table: 'Test' + has_where: true + fields: ['id', 'name', 'age'] + types: [7, 18, 8] + }, orm.QueryData{}, orm.QueryData{ + fields: ['name', 'age'] + data: [orm.Primitive('Louis'), i64(100)] + types: [18, 8] + is_and: [true, true] + kinds: [.eq, .eq] + }) or { panic(err) } + + id := res[0][0] + name := res[0][1] + age := res[0][2] + + assert id is int + if id is int { + assert id == 1 + } + + assert name is string + if name is string { + assert name == 'Louis' + } + + assert age is i64 + if age is i64 { + assert age == 100 + } +} diff --git a/v_windows/v/old/vlib/sqlite/sqlite_test.v b/v_windows/v/old/vlib/sqlite/sqlite_test.v new file mode 100644 index 0000000..f1e9db3 --- /dev/null +++ b/v_windows/v/old/vlib/sqlite/sqlite_test.v @@ -0,0 +1,31 @@ +import sqlite + +fn test_sqlite() { + $if !linux { + return + } + mut db := sqlite.connect(':memory:') or { panic(err) } + assert db.is_open + db.exec('drop table if exists users') + db.exec("create table users (id integer primary key, name text default '');") + db.exec("insert into users (name) values ('Sam')") + assert db.last_insert_rowid() == 1 + db.exec("insert into users (name) values ('Peter')") + assert db.last_insert_rowid() == 2 + db.exec("insert into users (name) values ('Kate')") + assert db.last_insert_rowid() == 3 + nr_users := db.q_int('select count(*) from users') + assert nr_users == 3 + name := db.q_string('select name from users where id = 1') + assert name == 'Sam' + users, mut code := db.exec('select * from users') + assert users.len == 3 + assert code == 101 + code = db.exec_none('vacuum') + assert code == 101 + user := db.exec_one('select * from users where id = 3') or { panic(err) } + println(user) + assert user.vals.len == 2 + db.close() or { panic(err) } + assert !db.is_open +} diff --git a/v_windows/v/old/vlib/sqlite/stmt.v b/v_windows/v/old/vlib/sqlite/stmt.v new file mode 100644 index 0000000..da07e82 --- /dev/null +++ b/v_windows/v/old/vlib/sqlite/stmt.v @@ -0,0 +1,74 @@ +module sqlite + +fn C.sqlite3_bind_double(&C.sqlite3_stmt, int, f64) int +fn C.sqlite3_bind_int(&C.sqlite3_stmt, int, int) int +fn C.sqlite3_bind_int64(&C.sqlite3_stmt, int, i64) int +fn C.sqlite3_bind_text(&C.sqlite3_stmt, int, &char, int, voidptr) int + +// Only for V ORM +fn (db DB) init_stmt(query string) (&C.sqlite3_stmt, int) { + // println('init_stmt("$query")') + stmt := &C.sqlite3_stmt(0) + err := C.sqlite3_prepare_v2(db.conn, &char(query.str), query.len, &stmt, 0) + return stmt, err +} + +fn (db DB) new_init_stmt(query string) ?Stmt { + stmt, err := db.init_stmt(query) + if err != sqlite_ok { + return db.error_message(err, query) + } + return Stmt{stmt, unsafe { &db }} +} + +fn (stmt Stmt) bind_int(idx int, v int) int { + return C.sqlite3_bind_int(stmt.stmt, idx, v) +} + +fn (stmt Stmt) bind_i64(idx int, v i64) int { + return C.sqlite3_bind_int64(stmt.stmt, idx, v) +} + +fn (stmt Stmt) bind_f64(idx int, v f64) int { + return C.sqlite3_bind_double(stmt.stmt, idx, v) +} + +fn (stmt Stmt) bind_text(idx int, s string) int { + return C.sqlite3_bind_text(stmt.stmt, idx, voidptr(s.str), s.len, 0) +} + +fn (stmt Stmt) get_int(idx int) int { + return C.sqlite3_column_int(stmt.stmt, idx) +} + +fn (stmt Stmt) get_i64(idx int) i64 { + return C.sqlite3_column_int64(stmt.stmt, idx) +} + +fn (stmt Stmt) get_f64(idx int) f64 { + return C.sqlite3_column_double(stmt.stmt, idx) +} + +fn (stmt Stmt) get_text(idx int) string { + b := &char(C.sqlite3_column_text(stmt.stmt, idx)) + return unsafe { b.vstring() } +} + +fn (stmt Stmt) get_count() int { + return C.sqlite3_column_count(stmt.stmt) +} + +fn (stmt Stmt) step() int { + return C.sqlite3_step(stmt.stmt) +} + +fn (stmt Stmt) orm_step(query string) ? { + res := stmt.step() + if res != sqlite_ok && res != sqlite_done && res != sqlite_row { + return stmt.db.error_message(res, query) + } +} + +fn (stmt Stmt) finalize() { + C.sqlite3_finalize(stmt.stmt) +} diff --git a/v_windows/v/old/vlib/stbi/stbi.v b/v_windows/v/old/vlib/stbi/stbi.v new file mode 100644 index 0000000..da72dca --- /dev/null +++ b/v_windows/v/old/vlib/stbi/stbi.v @@ -0,0 +1,87 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. + +module stbi + +#flag -I @VEXEROOT/thirdparty/stb_image +#include "stb_image.h" +#flag @VEXEROOT/thirdparty/stb_image/stbi.o + +pub struct Image { +pub mut: + width int + height int + nr_channels int + ok bool + data voidptr + ext string +} + +fn C.stbi_load(filename &char, x &int, y &int, channels_in_file &int, desired_channels int) &byte + +fn C.stbi_load_from_file(f voidptr, x &int, y &int, channels_in_file &int, desired_channels int) &byte + +fn C.stbi_load_from_memory(buffer &byte, len int, x &int, y &int, channels_in_file &int, desired_channels int) &byte + +fn C.stbi_image_free(retval_from_stbi_load &byte) + +fn C.stbi_set_flip_vertically_on_load(should_flip int) + +fn init() { + set_flip_vertically_on_load(false) +} + +pub fn load(path string) ?Image { + ext := path.all_after_last('.') + mut res := Image{ + ok: true + ext: ext + data: 0 + } + // flag := if ext == 'png' { C.STBI_rgb_alpha } else { 0 } + desired_channels := if ext == 'png' { 4 } else { 0 } + res.data = C.stbi_load(&char(path.str), &res.width, &res.height, &res.nr_channels, + desired_channels) + if desired_channels == 4 && res.nr_channels == 3 { + // Fix an alpha png bug + res.nr_channels = 4 + } + if isnil(res.data) { + return error('stbi image failed to load from "$path"') + } + return res +} + +pub fn load_from_memory(buf &byte, bufsize int) ?Image { + mut res := Image{ + ok: true + data: 0 + } + flag := C.STBI_rgb_alpha + res.data = C.stbi_load_from_memory(buf, bufsize, &res.width, &res.height, &res.nr_channels, + flag) + if isnil(res.data) { + return error('stbi image failed to load from memory') + } + return res +} + +pub fn (img &Image) free() { + C.stbi_image_free(img.data) +} + +/* +pub fn (img Image) tex_image_2d() { + mut rgb_flag := C.GL_RGB + if img.ext == 'png' { + rgb_flag = C.GL_RGBA + } + C.glTexImage2D(C.GL_TEXTURE_2D, 0, rgb_flag, img.width, img.height, 0, + rgb_flag, C.GL_UNSIGNED_BYTE, img.data) +} +*/ + +pub fn set_flip_vertically_on_load(val bool) { + C.stbi_set_flip_vertically_on_load(val) +} diff --git a/v_windows/v/old/vlib/strconv/atof.v b/v_windows/v/old/vlib/strconv/atof.v new file mode 100644 index 0000000..dd994bd --- /dev/null +++ b/v_windows/v/old/vlib/strconv/atof.v @@ -0,0 +1,441 @@ +module strconv + +/* +atof util + +Copyright (c) 2019-2021 Dario Deledda. All rights reserved. +Use of this source code is governed by an MIT license +that can be found in the LICENSE file. + +This file contains utilities for convert a string in a f64 variable +IEEE 754 standard is used + +Know limitation: +- limited to 18 significant digits + +The code is inspired by: +Grzegorz Kraszewski krashan@teleinfo.pb.edu.pl +URL: http://krashan.ppa.pl/articles/stringtofloat/ +Original license: MIT + +96 bit operation utilities +Note: when u128 will be available these function can be refactored +*/ + +// right logical shift 96 bit +fn lsr96(s2 u32, s1 u32, s0 u32) (u32, u32, u32) { + mut r0 := u32(0) + mut r1 := u32(0) + mut r2 := u32(0) + r0 = (s0 >> 1) | ((s1 & u32(1)) << 31) + r1 = (s1 >> 1) | ((s2 & u32(1)) << 31) + r2 = s2 >> 1 + return r2, r1, r0 +} + +// left logical shift 96 bit +fn lsl96(s2 u32, s1 u32, s0 u32) (u32, u32, u32) { + mut r0 := u32(0) + mut r1 := u32(0) + mut r2 := u32(0) + r2 = (s2 << 1) | ((s1 & (u32(1) << 31)) >> 31) + r1 = (s1 << 1) | ((s0 & (u32(1) << 31)) >> 31) + r0 = s0 << 1 + return r2, r1, r0 +} + +// sum on 96 bit +fn add96(s2 u32, s1 u32, s0 u32, d2 u32, d1 u32, d0 u32) (u32, u32, u32) { + mut w := u64(0) + mut r0 := u32(0) + mut r1 := u32(0) + mut r2 := u32(0) + w = u64(s0) + u64(d0) + r0 = u32(w) + w >>= 32 + w += u64(s1) + u64(d1) + r1 = u32(w) + w >>= 32 + w += u64(s2) + u64(d2) + r2 = u32(w) + return r2, r1, r0 +} + +// subtraction on 96 bit +fn sub96(s2 u32, s1 u32, s0 u32, d2 u32, d1 u32, d0 u32) (u32, u32, u32) { + mut w := u64(0) + mut r0 := u32(0) + mut r1 := u32(0) + mut r2 := u32(0) + w = u64(s0) - u64(d0) + r0 = u32(w) + w >>= 32 + w += u64(s1) - u64(d1) + r1 = u32(w) + w >>= 32 + w += u64(s2) - u64(d2) + r2 = u32(w) + return r2, r1, r0 +} + +/* +Constants +*/ + +pub const ( + // + // f32 constants + // + single_plus_zero = u32(0x0000_0000) + single_minus_zero = u32(0x8000_0000) + single_plus_infinity = u32(0x7F80_0000) + single_minus_infinity = u32(0xFF80_0000) + // + // f64 constants + // + digits = 18 + double_plus_zero = u64(0x0000000000000000) + double_minus_zero = u64(0x8000000000000000) + double_plus_infinity = u64(0x7FF0000000000000) + double_minus_infinity = u64(0xFFF0000000000000) + // + // Possible parser return values. + // + parser_ok = 0 // parser finished OK + parser_pzero = 1 // no digits or number is smaller than +-2^-1022 + parser_mzero = 2 // number is negative, module smaller + parser_pinf = 3 // number is higher than +HUGE_VAL + parser_minf = 4 // number is lower than -HUGE_VAL + // + // char constants + // Note: Modify these if working with non-ASCII encoding + // + c_dpoint = `.` + c_plus = `+` + c_minus = `-` + c_zero = `0` + c_nine = `9` + c_ten = u32(10) +) + +/* +Utility +*/ + +// NOTE: Modify these if working with non-ASCII encoding +fn is_digit(x byte) bool { + return (x >= strconv.c_zero && x <= strconv.c_nine) == true +} + +fn is_space(x byte) bool { + return (x == `\t` || x == `\n` || x == `\v` || x == `\f` || x == `\r` || x == ` `) +} + +fn is_exp(x byte) bool { + return (x == `E` || x == `e`) == true +} + +/* +Support struct +*/ + +/* +String parser +NOTE: #TOFIX need one char after the last char of the number +*/ + +fn parser(s string) (int, PrepNumber) { + mut digx := 0 + mut result := strconv.parser_ok + mut expneg := false + mut expexp := 0 + mut i := 0 + mut pn := PrepNumber{} + + // skip spaces + for i < s.len && s[i].is_space() { + i++ + } + + // check negatives + if s[i] == `-` { + pn.negative = true + i++ + } + + // positive sign ignore it + if s[i] == `+` { + i++ + } + + // read mantissa + for i < s.len && s[i].is_digit() { + // println("$i => ${s[i]}") + if digx < strconv.digits { + pn.mantissa *= 10 + pn.mantissa += u64(s[i] - strconv.c_zero) + digx++ + } else if pn.exponent < 2147483647 { + pn.exponent++ + } + i++ + } + + // read mantissa decimals + if (i < s.len) && (s[i] == `.`) { + i++ + for i < s.len && s[i].is_digit() { + if digx < strconv.digits { + pn.mantissa *= 10 + pn.mantissa += u64(s[i] - strconv.c_zero) + pn.exponent-- + digx++ + } + i++ + } + } + + // read exponent + if (i < s.len) && ((s[i] == `e`) || (s[i] == `E`)) { + i++ + if i < s.len { + // esponent sign + if s[i] == strconv.c_plus { + i++ + } else if s[i] == strconv.c_minus { + expneg = true + i++ + } + + for i < s.len && s[i].is_digit() { + if expexp < 214748364 { + expexp *= 10 + expexp += int(s[i] - strconv.c_zero) + } + i++ + } + } + } + + if expneg { + expexp = -expexp + } + pn.exponent += expexp + if pn.mantissa == 0 { + if pn.negative { + result = strconv.parser_mzero + } else { + result = strconv.parser_pzero + } + } else if pn.exponent > 309 { + if pn.negative { + result = strconv.parser_minf + } else { + result = strconv.parser_pinf + } + } else if pn.exponent < -328 { + if pn.negative { + result = strconv.parser_mzero + } else { + result = strconv.parser_pzero + } + } + return result, pn +} + +/* +Converter to the bit form of the f64 number +*/ + +// converter return a u64 with the bit image of the f64 number +fn converter(mut pn PrepNumber) u64 { + mut binexp := 92 + mut s2 := u32(0) // 96-bit precision integer + mut s1 := u32(0) + mut s0 := u32(0) + mut q2 := u32(0) // 96-bit precision integer + mut q1 := u32(0) + mut q0 := u32(0) + mut r2 := u32(0) // 96-bit precision integer + mut r1 := u32(0) + mut r0 := u32(0) + mask28 := u32(u64(0xF) << 28) + mut result := u64(0) + // working on 3 u32 to have 96 bit precision + s0 = u32(pn.mantissa & u64(0x00000000FFFFFFFF)) + s1 = u32(pn.mantissa >> 32) + s2 = u32(0) + // so we take the decimal exponent off + for pn.exponent > 0 { + q2, q1, q0 = lsl96(s2, s1, s0) // q = s * 2 + r2, r1, r0 = lsl96(q2, q1, q0) // r = s * 4 <=> q * 2 + s2, s1, s0 = lsl96(r2, r1, r0) // s = s * 8 <=> r * 2 + s2, s1, s0 = add96(s2, s1, s0, q2, q1, q0) // s = (s * 8) + (s * 2) <=> s*10 + pn.exponent-- + for (s2 & mask28) != 0 { + q2, q1, q0 = lsr96(s2, s1, s0) + binexp++ + s2 = q2 + s1 = q1 + s0 = q0 + } + } + for pn.exponent < 0 { + for !((s2 & (u32(1) << 31)) != 0) { + q2, q1, q0 = lsl96(s2, s1, s0) + binexp-- + s2 = q2 + s1 = q1 + s0 = q0 + } + q2 = s2 / strconv.c_ten + r1 = s2 % strconv.c_ten + r2 = (s1 >> 8) | (r1 << 24) + q1 = r2 / strconv.c_ten + r1 = r2 % strconv.c_ten + r2 = ((s1 & u32(0xFF)) << 16) | (s0 >> 16) | (r1 << 24) + r0 = r2 / strconv.c_ten + r1 = r2 % strconv.c_ten + q1 = (q1 << 8) | ((r0 & u32(0x00FF0000)) >> 16) + q0 = r0 << 16 + r2 = (s0 & u32(0xFFFF)) | (r1 << 16) + q0 |= r2 / strconv.c_ten + s2 = q2 + s1 = q1 + s0 = q0 + pn.exponent++ + } + // C.printf("mantissa before normalization: %08x%08x%08x binexp: %d \n", s2,s1,s0,binexp) + // normalization, the 28 bit in s2 must the leftest one in the variable + if s2 != 0 || s1 != 0 || s0 != 0 { + for (s2 & mask28) == 0 { + q2, q1, q0 = lsl96(s2, s1, s0) + binexp-- + s2 = q2 + s1 = q1 + s0 = q0 + } + } + // rounding if needed + /* + * "round half to even" algorithm + * Example for f32, just a reminder + * + * If bit 54 is 0, round down + * If bit 54 is 1 + * If any bit beyond bit 54 is 1, round up + * If all bits beyond bit 54 are 0 (meaning the number is halfway between two floating-point numbers) + * If bit 53 is 0, round down + * If bit 53 is 1, round up + */ + /* + test case 1 complete + s2=0x1FFFFFFF + s1=0xFFFFFF80 + s0=0x0 + */ + + /* + test case 1 check_round_bit + s2=0x18888888 + s1=0x88888880 + s0=0x0 + */ + + /* + test case check_round_bit + normalization + s2=0x18888888 + s1=0x88888F80 + s0=0x0 + */ + + // C.printf("mantissa before rounding: %08x%08x%08x binexp: %d \n", s2,s1,s0,binexp) + // s1 => 0xFFFFFFxx only F are rapresented + nbit := 7 + check_round_bit := u32(1) << u32(nbit) + check_round_mask := u32(0xFFFFFFFF) << u32(nbit) + if (s1 & check_round_bit) != 0 { + // C.printf("need round!! cehck mask: %08x\n", s1 & ~check_round_mask ) + if (s1 & ~check_round_mask) != 0 { + // C.printf("Add 1!\n") + s2, s1, s0 = add96(s2, s1, s0, 0, check_round_bit, 0) + } else { + // C.printf("All 0!\n") + if (s1 & (check_round_bit << u32(1))) != 0 { + // C.printf("Add 1 form -1 bit control!\n") + s2, s1, s0 = add96(s2, s1, s0, 0, check_round_bit, 0) + } + } + s1 = s1 & check_round_mask + s0 = u32(0) + // recheck normalization + if s2 & (mask28 << u32(1)) != 0 { + // C.printf("Renormalize!!") + q2, q1, q0 = lsr96(s2, s1, s0) + binexp-- + s2 = q2 + s1 = q1 + s0 = q0 + } + } + // tmp := ( u64(s2 & ~mask28) << 24) | ((u64(s1) + u64(128)) >> 8) + // C.printf("mantissa after rounding : %08x%08x%08x binexp: %d \n", s2,s1,s0,binexp) + // C.printf("Tmp result: %016x\n",tmp) + // end rounding + // offset the binary exponent IEEE 754 + binexp += 1023 + if binexp > 2046 { + if pn.negative { + result = strconv.double_minus_infinity + } else { + result = strconv.double_plus_infinity + } + } else if binexp < 1 { + if pn.negative { + result = strconv.double_minus_zero + } else { + result = strconv.double_plus_zero + } + } else if s2 != 0 { + mut q := u64(0) + binexs2 := u64(binexp) << 52 + q = (u64(s2 & ~mask28) << 24) | ((u64(s1) + u64(128)) >> 8) | binexs2 + if pn.negative { + q |= (u64(1) << 63) + } + result = q + } + return result +} + +/* +Public functions +*/ + +// atof64 return a f64 from a string doing a parsing operation +pub fn atof64(s string) f64 { + mut pn := PrepNumber{} + mut res_parsing := 0 + mut res := Float64u{} + + res_parsing, pn = parser(s) + match res_parsing { + strconv.parser_ok { + res.u = converter(mut pn) + } + strconv.parser_pzero { + res.u = strconv.double_plus_zero + } + strconv.parser_mzero { + res.u = strconv.double_minus_zero + } + strconv.parser_pinf { + res.u = strconv.double_plus_infinity + } + strconv.parser_minf { + res.u = strconv.double_minus_infinity + } + else {} + } + return unsafe { res.f } +} diff --git a/v_windows/v/old/vlib/strconv/atof_test.v b/v_windows/v/old/vlib/strconv/atof_test.v new file mode 100644 index 0000000..ca286c8 --- /dev/null +++ b/v_windows/v/old/vlib/strconv/atof_test.v @@ -0,0 +1,75 @@ +import strconv + +/********************************************************************** +* +* String to float Test +* +**********************************************************************/ + +fn test_atof() { + // + // test set + // + + // float64 + src_num := [ + f64(0.3), + -0.3, + 0.004, + -0.004, + 0.0, + -0.0, + 31234567890123, + ] + + // strings + src_num_str := [ + '0.3', + '-0.3', + '0.004', + '-0.004', + '0.0', + '-0.0', + '31234567890123', + ] + + // check conversion case 1 string <=> string + for c, x in src_num { + // slow atof + assert strconv.atof64(src_num_str[c]).strlong() == x.strlong() + + // quick atof + mut s1 := (strconv.atof_quick(src_num_str[c]).str()) + mut s2 := (x.str()) + delta := s1.f64() - s2.f64() + // println("$s1 $s2 $delta") + assert delta < f64(1e-16) + + // test C.atof + n1 := x.strsci(18) + n2 := f64(C.atof(&char(src_num_str[c].str))).strsci(18) + // println("$n1 $n2") + assert n1 == n2 + } + + // check conversion case 2 string <==> f64 + // we don't test atof_quick beacuse we already know the rounding error + for c, x in src_num_str { + b := src_num[c].strlong() + a1 := strconv.atof64(x).strlong() + assert a1 == b + } + + // special cases + mut f1 := f64(0.0) + mut ptr := unsafe { &u64(&f1) } + ptr = unsafe { &u64(&f1) } + + // double_plus_zero + f1 = 0.0 + assert *ptr == u64(0x0000000000000000) + // double_minus_zero + f1 = -0.0 + assert *ptr == u64(0x8000000000000000) + println('DONE!') +} diff --git a/v_windows/v/old/vlib/strconv/atofq.v b/v_windows/v/old/vlib/strconv/atofq.v new file mode 100644 index 0000000..07bb7b3 --- /dev/null +++ b/v_windows/v/old/vlib/strconv/atofq.v @@ -0,0 +1,348 @@ +module strconv + +/* +atof util + +Copyright (c) 2019 Dario Deledda. All rights reserved. +Use of this source code is governed by an MIT license +that can be found in the LICENSE file. + +This file contains utilities for convert a string in a f64 variable in a very quick way +IEEE 754 standard is used + +Know limitation: +- round to 0 approximation +- loos of precision with big exponents +*/ + +// atof_quick return a f64 number from a string in a quick way +pub fn atof_quick(s string) f64 { + mut f := Float64u{} // result + mut sign := f64(1.0) // result sign + mut i := 0 // index + // skip white spaces + for i < s.len && s[i] == ` ` { + i++ + } + // check sign + if i < s.len { + if s[i] == `-` { + sign = -1.0 + i++ + } else if s[i] == `+` { + i++ + } + } + // infinite + if s[i] == `i` && i + 2 < s.len && s[i + 1] == `n` && s[i + 2] == `f` { + if sign > 0.0 { + f.u = double_plus_infinity + } else { + f.u = double_minus_infinity + } + return unsafe { f.f } + } + // skip zeros + for i < s.len && s[i] == `0` { + i++ + // we have a zero, manage it + if i >= s.len { + if sign > 0.0 { + f.u = double_plus_zero + } else { + f.u = double_minus_zero + } + return unsafe { f.f } + } + } + // integer part + for i < s.len && (s[i] >= `0` && s[i] <= `9`) { + f.f *= f64(10.0) + f.f += f64(s[i] - `0`) + i++ + } + // decimal point + if i < s.len && s[i] == `.` { + i++ + mut frac_mul := f64(0.1) + for i < s.len && (s[i] >= `0` && s[i] <= `9`) { + f.f += f64(s[i] - `0`) * frac_mul + frac_mul *= f64(0.1) + i++ + } + } + // exponent management + if i < s.len && (s[i] == `e` || s[i] == `E`) { + i++ + mut exp := 0 + mut exp_sign := 1 + // negative exponent + if i < s.len { + if s[i] == `-` { + exp_sign = -1 + i++ + } else if s[i] == `+` { + i++ + } + } + // skip zeros + for i < s.len && s[i] == `0` { + i++ + } + for i < s.len && (s[i] >= `0` && s[i] <= `9`) { + exp *= 10 + exp += int(s[i] - `0`) + i++ + } + if exp_sign == 1 { + if exp > strconv.pos_exp.len { + if sign > 0 { + f.u = double_plus_infinity + } else { + f.u = double_minus_infinity + } + return unsafe { f.f } + } + tmp_mul := Float64u{ + u: strconv.pos_exp[exp] + } + // C.printf("exp: %d [0x%016llx] %f,",exp,pos_exp[exp],tmp_mul) + f.f = unsafe { f.f * tmp_mul.f } + } else { + if exp > strconv.neg_exp.len { + if sign > 0 { + f.u = double_plus_zero + } else { + f.u = double_minus_zero + } + return unsafe { f.f } + } + tmp_mul := Float64u{ + u: strconv.neg_exp[exp] + } + + // C.printf("exp: %d [0x%016llx] %f,",exp,pos_exp[exp],tmp_mul) + f.f = unsafe { f.f * tmp_mul.f } + } + } + unsafe { + f.f = f.f * sign + return f.f + } +} + +const ( + // positive exp of 10 binary form + pos_exp = [u64(0x3ff0000000000000), u64(0x4024000000000000), u64(0x4059000000000000), + u64(0x408f400000000000), u64(0x40c3880000000000), u64(0x40f86a0000000000), + u64(0x412e848000000000), u64(0x416312d000000000), u64(0x4197d78400000000), + u64(0x41cdcd6500000000), u64(0x4202a05f20000000), u64(0x42374876e8000000), + u64(0x426d1a94a2000000), u64(0x42a2309ce5400000), u64(0x42d6bcc41e900000), + u64(0x430c6bf526340000), u64(0x4341c37937e08000), u64(0x4376345785d8a000), + u64(0x43abc16d674ec800), u64(0x43e158e460913d00), u64(0x4415af1d78b58c40), + u64(0x444b1ae4d6e2ef50), u64(0x4480f0cf064dd592), u64(0x44b52d02c7e14af6), + u64(0x44ea784379d99db4), u64(0x45208b2a2c280291), u64(0x4554adf4b7320335), + u64(0x4589d971e4fe8402), u64(0x45c027e72f1f1281), u64(0x45f431e0fae6d721), + u64(0x46293e5939a08cea), u64(0x465f8def8808b024), u64(0x4693b8b5b5056e17), + u64(0x46c8a6e32246c99c), u64(0x46fed09bead87c03), u64(0x4733426172c74d82), + u64(0x476812f9cf7920e3), u64(0x479e17b84357691b), u64(0x47d2ced32a16a1b1), + u64(0x48078287f49c4a1d), u64(0x483d6329f1c35ca5), u64(0x48725dfa371a19e7), + u64(0x48a6f578c4e0a061), u64(0x48dcb2d6f618c879), u64(0x4911efc659cf7d4c), + u64(0x49466bb7f0435c9e), u64(0x497c06a5ec5433c6), u64(0x49b18427b3b4a05c), + u64(0x49e5e531a0a1c873), u64(0x4a1b5e7e08ca3a8f), u64(0x4a511b0ec57e649a), + u64(0x4a8561d276ddfdc0), u64(0x4ababa4714957d30), u64(0x4af0b46c6cdd6e3e), + u64(0x4b24e1878814c9ce), u64(0x4b5a19e96a19fc41), u64(0x4b905031e2503da9), + u64(0x4bc4643e5ae44d13), u64(0x4bf97d4df19d6057), u64(0x4c2fdca16e04b86d), + u64(0x4c63e9e4e4c2f344), u64(0x4c98e45e1df3b015), u64(0x4ccf1d75a5709c1b), + u64(0x4d03726987666191), u64(0x4d384f03e93ff9f5), u64(0x4d6e62c4e38ff872), + u64(0x4da2fdbb0e39fb47), u64(0x4dd7bd29d1c87a19), u64(0x4e0dac74463a989f), + u64(0x4e428bc8abe49f64), u64(0x4e772ebad6ddc73d), u64(0x4eacfa698c95390c), + u64(0x4ee21c81f7dd43a7), u64(0x4f16a3a275d49491), u64(0x4f4c4c8b1349b9b5), + u64(0x4f81afd6ec0e1411), u64(0x4fb61bcca7119916), u64(0x4feba2bfd0d5ff5b), + u64(0x502145b7e285bf99), u64(0x50559725db272f7f), u64(0x508afcef51f0fb5f), + u64(0x50c0de1593369d1b), u64(0x50f5159af8044462), u64(0x512a5b01b605557b), + u64(0x516078e111c3556d), u64(0x5194971956342ac8), u64(0x51c9bcdfabc1357a), + u64(0x5200160bcb58c16c), u64(0x52341b8ebe2ef1c7), u64(0x526922726dbaae39), + u64(0x529f6b0f092959c7), u64(0x52d3a2e965b9d81d), u64(0x53088ba3bf284e24), + u64(0x533eae8caef261ad), u64(0x53732d17ed577d0c), u64(0x53a7f85de8ad5c4f), + u64(0x53ddf67562d8b363), u64(0x5412ba095dc7701e), u64(0x5447688bb5394c25), + u64(0x547d42aea2879f2e), u64(0x54b249ad2594c37d), u64(0x54e6dc186ef9f45c), + u64(0x551c931e8ab87173), u64(0x5551dbf316b346e8), u64(0x558652efdc6018a2), + u64(0x55bbe7abd3781eca), u64(0x55f170cb642b133f), u64(0x5625ccfe3d35d80e), + u64(0x565b403dcc834e12), u64(0x569108269fd210cb), u64(0x56c54a3047c694fe), + u64(0x56fa9cbc59b83a3d), u64(0x5730a1f5b8132466), u64(0x5764ca732617ed80), + u64(0x5799fd0fef9de8e0), u64(0x57d03e29f5c2b18c), u64(0x58044db473335def), + u64(0x583961219000356b), u64(0x586fb969f40042c5), u64(0x58a3d3e2388029bb), + u64(0x58d8c8dac6a0342a), u64(0x590efb1178484135), u64(0x59435ceaeb2d28c1), + u64(0x59783425a5f872f1), u64(0x59ae412f0f768fad), u64(0x59e2e8bd69aa19cc), + u64(0x5a17a2ecc414a03f), u64(0x5a4d8ba7f519c84f), u64(0x5a827748f9301d32), + u64(0x5ab7151b377c247e), u64(0x5aecda62055b2d9e), u64(0x5b22087d4358fc82), + u64(0x5b568a9c942f3ba3), u64(0x5b8c2d43b93b0a8c), u64(0x5bc19c4a53c4e697), + u64(0x5bf6035ce8b6203d), u64(0x5c2b843422e3a84d), u64(0x5c6132a095ce4930), + u64(0x5c957f48bb41db7c), u64(0x5ccadf1aea12525b), u64(0x5d00cb70d24b7379), + u64(0x5d34fe4d06de5057), u64(0x5d6a3de04895e46d), u64(0x5da066ac2d5daec4), + u64(0x5dd4805738b51a75), u64(0x5e09a06d06e26112), u64(0x5e400444244d7cab), + u64(0x5e7405552d60dbd6), u64(0x5ea906aa78b912cc), u64(0x5edf485516e7577f), + u64(0x5f138d352e5096af), u64(0x5f48708279e4bc5b), u64(0x5f7e8ca3185deb72), + u64(0x5fb317e5ef3ab327), u64(0x5fe7dddf6b095ff1), u64(0x601dd55745cbb7ed), + u64(0x6052a5568b9f52f4), u64(0x60874eac2e8727b1), u64(0x60bd22573a28f19d), + u64(0x60f2357684599702), u64(0x6126c2d4256ffcc3), u64(0x615c73892ecbfbf4), + u64(0x6191c835bd3f7d78), u64(0x61c63a432c8f5cd6), u64(0x61fbc8d3f7b3340c), + u64(0x62315d847ad00087), u64(0x6265b4e5998400a9), u64(0x629b221effe500d4), + u64(0x62d0f5535fef2084), u64(0x630532a837eae8a5), u64(0x633a7f5245e5a2cf), + u64(0x63708f936baf85c1), u64(0x63a4b378469b6732), u64(0x63d9e056584240fe), + u64(0x64102c35f729689f), u64(0x6444374374f3c2c6), u64(0x647945145230b378), + u64(0x64af965966bce056), u64(0x64e3bdf7e0360c36), u64(0x6518ad75d8438f43), + u64(0x654ed8d34e547314), u64(0x6583478410f4c7ec), u64(0x65b819651531f9e8), + u64(0x65ee1fbe5a7e7861), u64(0x6622d3d6f88f0b3d), u64(0x665788ccb6b2ce0c), + u64(0x668d6affe45f818f), u64(0x66c262dfeebbb0f9), u64(0x66f6fb97ea6a9d38), + u64(0x672cba7de5054486), u64(0x6761f48eaf234ad4), u64(0x679671b25aec1d89), + u64(0x67cc0e1ef1a724eb), u64(0x680188d357087713), u64(0x6835eb082cca94d7), + u64(0x686b65ca37fd3a0d), u64(0x68a11f9e62fe4448), u64(0x68d56785fbbdd55a), + u64(0x690ac1677aad4ab1), u64(0x6940b8e0acac4eaf), u64(0x6974e718d7d7625a), + u64(0x69aa20df0dcd3af1), u64(0x69e0548b68a044d6), u64(0x6a1469ae42c8560c), + u64(0x6a498419d37a6b8f), u64(0x6a7fe52048590673), u64(0x6ab3ef342d37a408), + u64(0x6ae8eb0138858d0a), u64(0x6b1f25c186a6f04c), u64(0x6b537798f4285630), + u64(0x6b88557f31326bbb), u64(0x6bbe6adefd7f06aa), u64(0x6bf302cb5e6f642a), + u64(0x6c27c37e360b3d35), u64(0x6c5db45dc38e0c82), u64(0x6c9290ba9a38c7d1), + u64(0x6cc734e940c6f9c6), u64(0x6cfd022390f8b837), u64(0x6d3221563a9b7323), + u64(0x6d66a9abc9424feb), u64(0x6d9c5416bb92e3e6), u64(0x6dd1b48e353bce70), + u64(0x6e0621b1c28ac20c), u64(0x6e3baa1e332d728f), u64(0x6e714a52dffc6799), + u64(0x6ea59ce797fb817f), u64(0x6edb04217dfa61df), u64(0x6f10e294eebc7d2c), + u64(0x6f451b3a2a6b9c76), u64(0x6f7a6208b5068394), u64(0x6fb07d457124123d), + u64(0x6fe49c96cd6d16cc), u64(0x7019c3bc80c85c7f), u64(0x70501a55d07d39cf), + u64(0x708420eb449c8843), u64(0x70b9292615c3aa54), u64(0x70ef736f9b3494e9), + u64(0x7123a825c100dd11), u64(0x7158922f31411456), u64(0x718eb6bafd91596b), + u64(0x71c33234de7ad7e3), u64(0x71f7fec216198ddc), u64(0x722dfe729b9ff153), + u64(0x7262bf07a143f6d4), u64(0x72976ec98994f489), u64(0x72cd4a7bebfa31ab), + u64(0x73024e8d737c5f0b), u64(0x7336e230d05b76cd), u64(0x736c9abd04725481), + u64(0x73a1e0b622c774d0), u64(0x73d658e3ab795204), u64(0x740bef1c9657a686), + u64(0x74417571ddf6c814), u64(0x7475d2ce55747a18), u64(0x74ab4781ead1989e), + u64(0x74e10cb132c2ff63), u64(0x75154fdd7f73bf3c), u64(0x754aa3d4df50af0b), + u64(0x7580a6650b926d67), u64(0x75b4cffe4e7708c0), u64(0x75ea03fde214caf1), + u64(0x7620427ead4cfed6), u64(0x7654531e58a03e8c), u64(0x768967e5eec84e2f), + u64(0x76bfc1df6a7a61bb), u64(0x76f3d92ba28c7d15), u64(0x7728cf768b2f9c5a), + u64(0x775f03542dfb8370), u64(0x779362149cbd3226), u64(0x77c83a99c3ec7eb0), + u64(0x77fe494034e79e5c), u64(0x7832edc82110c2f9), u64(0x7867a93a2954f3b8), + u64(0x789d9388b3aa30a5), u64(0x78d27c35704a5e67), u64(0x79071b42cc5cf601), + u64(0x793ce2137f743382), u64(0x79720d4c2fa8a031), u64(0x79a6909f3b92c83d), + u64(0x79dc34c70a777a4d), u64(0x7a11a0fc668aac70), u64(0x7a46093b802d578c), + u64(0x7a7b8b8a6038ad6f), u64(0x7ab137367c236c65), u64(0x7ae585041b2c477f), + u64(0x7b1ae64521f7595e), u64(0x7b50cfeb353a97db), u64(0x7b8503e602893dd2), + u64(0x7bba44df832b8d46), u64(0x7bf06b0bb1fb384c), u64(0x7c2485ce9e7a065f), + u64(0x7c59a742461887f6), u64(0x7c9008896bcf54fa), u64(0x7cc40aabc6c32a38), + u64(0x7cf90d56b873f4c7), u64(0x7d2f50ac6690f1f8), u64(0x7d63926bc01a973b), + u64(0x7d987706b0213d0a), u64(0x7dce94c85c298c4c), u64(0x7e031cfd3999f7b0), + u64(0x7e37e43c8800759c), u64(0x7e6ddd4baa009303), u64(0x7ea2aa4f4a405be2), + u64(0x7ed754e31cd072da), u64(0x7f0d2a1be4048f90), u64(0x7f423a516e82d9ba), + u64(0x7f76c8e5ca239029), u64(0x7fac7b1f3cac7433), u64(0x7fe1ccf385ebc8a0)] + // negative exp of 10 binary form + neg_exp = [u64(0x3ff0000000000000), u64(0x3fb999999999999a), u64(0x3f847ae147ae147b), + u64(0x3f50624dd2f1a9fc), u64(0x3f1a36e2eb1c432d), u64(0x3ee4f8b588e368f1), + u64(0x3eb0c6f7a0b5ed8d), u64(0x3e7ad7f29abcaf48), u64(0x3e45798ee2308c3a), + u64(0x3e112e0be826d695), u64(0x3ddb7cdfd9d7bdbb), u64(0x3da5fd7fe1796495), + u64(0x3d719799812dea11), u64(0x3d3c25c268497682), u64(0x3d06849b86a12b9b), + u64(0x3cd203af9ee75616), u64(0x3c9cd2b297d889bc), u64(0x3c670ef54646d497), + u64(0x3c32725dd1d243ac), u64(0x3bfd83c94fb6d2ac), u64(0x3bc79ca10c924223), + u64(0x3b92e3b40a0e9b4f), u64(0x3b5e392010175ee6), u64(0x3b282db34012b251), + u64(0x3af357c299a88ea7), u64(0x3abef2d0f5da7dd9), u64(0x3a88c240c4aecb14), + u64(0x3a53ce9a36f23c10), u64(0x3a1fb0f6be506019), u64(0x39e95a5efea6b347), + u64(0x39b4484bfeebc2a0), u64(0x398039d665896880), u64(0x3949f623d5a8a733), + u64(0x3914c4e977ba1f5c), u64(0x38e09d8792fb4c49), u64(0x38aa95a5b7f87a0f), + u64(0x38754484932d2e72), u64(0x3841039d428a8b8f), u64(0x380b38fb9daa78e4), + u64(0x37d5c72fb1552d83), u64(0x37a16c262777579c), u64(0x376be03d0bf225c7), + u64(0x37364cfda3281e39), u64(0x3701d7314f534b61), u64(0x36cc8b8218854567), + u64(0x3696d601ad376ab9), u64(0x366244ce242c5561), u64(0x362d3ae36d13bbce), + u64(0x35f7624f8a762fd8), u64(0x35c2b50c6ec4f313), u64(0x358dee7a4ad4b81f), + u64(0x3557f1fb6f10934c), u64(0x352327fc58da0f70), u64(0x34eea6608e29b24d), + u64(0x34b8851a0b548ea4), u64(0x34839dae6f76d883), u64(0x344f62b0b257c0d2), + u64(0x34191bc08eac9a41), u64(0x33e41633a556e1ce), u64(0x33b011c2eaabe7d8), + u64(0x3379b604aaaca626), u64(0x3344919d5556eb52), u64(0x3310747ddddf22a8), + u64(0x32da53fc9631d10d), u64(0x32a50ffd44f4a73d), u64(0x3270d9976a5d5297), + u64(0x323af5bf109550f2), u64(0x32059165a6ddda5b), u64(0x31d1411e1f17e1e3), + u64(0x319b9b6364f30304), u64(0x316615e91d8f359d), u64(0x3131ab20e472914a), + u64(0x30fc45016d841baa), u64(0x30c69d9abe034955), u64(0x309217aefe690777), + u64(0x305cf2b1970e7258), u64(0x3027288e1271f513), u64(0x2ff286d80ec190dc), + u64(0x2fbda48ce468e7c7), u64(0x2f87b6d71d20b96c), u64(0x2f52f8ac174d6123), + u64(0x2f1e5aacf2156838), u64(0x2ee8488a5b445360), u64(0x2eb36d3b7c36a91a), + u64(0x2e7f152bf9f10e90), u64(0x2e48ddbcc7f40ba6), u64(0x2e13e497065cd61f), + u64(0x2ddfd424d6faf031), u64(0x2da97683df2f268d), u64(0x2d745ecfe5bf520b), + u64(0x2d404bd984990e6f), u64(0x2d0a12f5a0f4e3e5), u64(0x2cd4dbf7b3f71cb7), + u64(0x2ca0aff95cc5b092), u64(0x2c6ab328946f80ea), u64(0x2c355c2076bf9a55), + u64(0x2c0116805effaeaa), u64(0x2bcb5733cb32b111), u64(0x2b95df5ca28ef40d), + u64(0x2b617f7d4ed8c33e), u64(0x2b2bff2ee48e0530), u64(0x2af665bf1d3e6a8d), + u64(0x2ac1eaff4a98553d), u64(0x2a8cab3210f3bb95), u64(0x2a56ef5b40c2fc77), + u64(0x2a225915cd68c9f9), u64(0x29ed5b561574765b), u64(0x29b77c44ddf6c516), + u64(0x2982c9d0b1923745), u64(0x294e0fb44f50586e), u64(0x29180c903f7379f2), + u64(0x28e33d4032c2c7f5), u64(0x28aec866b79e0cba), u64(0x2878a0522c7e7095), + u64(0x2843b374f06526de), u64(0x280f8587e7083e30), u64(0x27d9379fec069826), + u64(0x27a42c7ff0054685), u64(0x277023998cd10537), u64(0x2739d28f47b4d525), + u64(0x2704a8729fc3ddb7), u64(0x26d086c219697e2c), u64(0x269a71368f0f3047), + u64(0x2665275ed8d8f36c), u64(0x2630ec4be0ad8f89), u64(0x25fb13ac9aaf4c0f), + u64(0x25c5a956e225d672), u64(0x2591544581b7dec2), u64(0x255bba08cf8c979d), + u64(0x25262e6d72d6dfb0), u64(0x24f1bebdf578b2f4), u64(0x24bc6463225ab7ec), + u64(0x2486b6b5b5155ff0), u64(0x24522bc490dde65a), u64(0x241d12d41afca3c3), + u64(0x23e7424348ca1c9c), u64(0x23b29b69070816e3), u64(0x237dc574d80cf16b), + u64(0x2347d12a4670c123), u64(0x23130dbb6b8d674f), u64(0x22de7c5f127bd87e), + u64(0x22a8637f41fcad32), u64(0x227382cc34ca2428), u64(0x223f37ad21436d0c), + u64(0x2208f9574dcf8a70), u64(0x21d3faac3e3fa1f3), u64(0x219ff779fd329cb9), + u64(0x216992c7fdc216fa), u64(0x2134756ccb01abfb), u64(0x21005df0a267bcc9), + u64(0x20ca2fe76a3f9475), u64(0x2094f31f8832dd2a), u64(0x2060c27fa028b0ef), + u64(0x202ad0cc33744e4b), u64(0x1ff573d68f903ea2), u64(0x1fc1297872d9cbb5), + u64(0x1f8b758d848fac55), u64(0x1f55f7a46a0c89dd), u64(0x1f2192e9ee706e4b), + u64(0x1eec1e43171a4a11), u64(0x1eb67e9c127b6e74), u64(0x1e81fee341fc585d), + u64(0x1e4ccb0536608d61), u64(0x1e1708d0f84d3de7), u64(0x1de26d73f9d764b9), + u64(0x1dad7becc2f23ac2), u64(0x1d779657025b6235), u64(0x1d42deac01e2b4f7), + u64(0x1d0e3113363787f2), u64(0x1cd8274291c6065b), u64(0x1ca3529ba7d19eaf), + u64(0x1c6eea92a61c3118), u64(0x1c38bba884e35a7a), u64(0x1c03c9539d82aec8), + u64(0x1bcfa885c8d117a6), u64(0x1b99539e3a40dfb8), u64(0x1b6442e4fb671960), + u64(0x1b303583fc527ab3), u64(0x1af9ef3993b72ab8), u64(0x1ac4bf6142f8eefa), + u64(0x1a90991a9bfa58c8), u64(0x1a5a8e90f9908e0d), u64(0x1a253eda614071a4), + u64(0x19f0ff151a99f483), u64(0x19bb31bb5dc320d2), u64(0x1985c162b168e70e), + u64(0x1951678227871f3e), u64(0x191bd8d03f3e9864), u64(0x18e6470cff6546b6), + u64(0x18b1d270cc51055f), u64(0x187c83e7ad4e6efe), u64(0x1846cfec8aa52598), + u64(0x18123ff06eea847a), u64(0x17dd331a4b10d3f6), u64(0x17a75c1508da432b), + u64(0x1772b010d3e1cf56), u64(0x173de6815302e556), u64(0x1707eb9aa8cf1dde), + u64(0x16d322e220a5b17e), u64(0x169e9e369aa2b597), u64(0x16687e92154ef7ac), + u64(0x16339874ddd8c623), u64(0x15ff5a549627a36c), u64(0x15c91510781fb5f0), + u64(0x159410d9f9b2f7f3), u64(0x15600d7b2e28c65c), u64(0x1529af2b7d0e0a2d), + u64(0x14f48c22ca71a1bd), u64(0x14c0701bd527b498), u64(0x148a4cf9550c5426), + u64(0x14550a6110d6a9b8), u64(0x1420d51a73deee2d), u64(0x13eaee90b964b047), + u64(0x13b58ba6fab6f36c), u64(0x13813c85955f2923), u64(0x134b9408eefea839), + u64(0x1316100725988694), u64(0x12e1a66c1e139edd), u64(0x12ac3d79c9b8fe2e), + u64(0x12769794a160cb58), u64(0x124212dd4de70913), u64(0x120ceafbafd80e85), + u64(0x11d72262f3133ed1), u64(0x11a281e8c275cbda), u64(0x116d9ca79d89462a), + u64(0x1137b08617a104ee), u64(0x1102f39e794d9d8b), u64(0x10ce5297287c2f45), + u64(0x1098421286c9bf6b), u64(0x1063680ed23aff89), u64(0x102f0ce4839198db), + u64(0x0ff8d71d360e13e2), u64(0x0fc3df4a91a4dcb5), u64(0x0f8fcbaa82a16121), + u64(0x0f596fbb9bb44db4), u64(0x0f245962e2f6a490), u64(0x0ef047824f2bb6da), + u64(0x0eba0c03b1df8af6), u64(0x0e84d6695b193bf8), u64(0x0e50ab877c142ffa), + u64(0x0e1aac0bf9b9e65c), u64(0x0de5566ffafb1eb0), u64(0x0db111f32f2f4bc0), + u64(0x0d7b4feb7eb212cd), u64(0x0d45d98932280f0a), u64(0x0d117ad428200c08), + u64(0x0cdbf7b9d9cce00d), u64(0x0ca65fc7e170b33e), u64(0x0c71e6398126f5cb), + u64(0x0c3ca38f350b22df), u64(0x0c06e93f5da2824c), u64(0x0bd25432b14ecea3), + u64(0x0b9d53844ee47dd1), u64(0x0b677603725064a8), u64(0x0b32c4cf8ea6b6ec), + u64(0x0afe07b27dd78b14), u64(0x0ac8062864ac6f43), u64(0x0a9338205089f29c), + u64(0x0a5ec033b40fea93), u64(0x0a2899c2f6732210), u64(0x09f3ae3591f5b4d9), + u64(0x09bf7d228322baf5), u64(0x098930e868e89591), u64(0x0954272053ed4474), + u64(0x09201f4d0ff10390), u64(0x08e9cbae7fe805b3), u64(0x08b4a2f1ffecd15c), + u64(0x0880825b3323dab0), u64(0x084a6a2b85062ab3), u64(0x081521bc6a6b555c), + u64(0x07e0e7c9eebc444a), u64(0x07ab0c764ac6d3a9), u64(0x0775a391d56bdc87), + u64(0x07414fa7ddefe3a0), u64(0x070bb2a62fe638ff), u64(0x06d62884f31e93ff), + u64(0x06a1ba03f5b21000), u64(0x066c5cd322b67fff), u64(0x0636b0a8e891ffff), + u64(0x060226ed86db3333), u64(0x05cd0b15a491eb84), u64(0x05973c115074bc6a), + u64(0x05629674405d6388), u64(0x052dbd86cd6238d9), u64(0x04f7cad23de82d7b), + u64(0x04c308a831868ac9), u64(0x048e74404f3daadb), u64(0x04585d003f6488af), + u64(0x04237d99cc506d59), u64(0x03ef2f5c7a1a488e), u64(0x03b8f2b061aea072), + u64(0x0383f559e7bee6c1), u64(0x034feef63f97d79c), u64(0x03198bf832dfdfb0), + u64(0x02e46ff9c24cb2f3), u64(0x02b059949b708f29), u64(0x027a28edc580e50e), + u64(0x0244ed8b04671da5), u64(0x0210be08d0527e1d), u64(0x01dac9a7b3b7302f), + u64(0x01a56e1fc2f8f359), u64(0x017124e63593f5e1), u64(0x013b6e3d22865634), + u64(0x0105f1ca820511c3), u64(0x00d18e3b9b374169), u64(0x009c16c5c5253575), + u64(0x0066789e3750f791), u64(0x0031fa182c40c60d), u64(0x000730d67819e8d2), + u64(0x0000b8157268fdaf), u64(0x000012688b70e62b), u64(0x000001d74124e3d1), + u64(0x0000002f201d49fb), u64(0x00000004b6695433), u64(0x0000000078a42205), + u64(0x000000000c1069cd), u64(0x000000000134d761), u64(0x00000000001ee257), + u64(0x00000000000316a2), u64(0x0000000000004f10), u64(0x00000000000007e8), + u64(0x00000000000000ca), u64(0x0000000000000014), u64(0x0000000000000002)] +) diff --git a/v_windows/v/old/vlib/strconv/atoi.v b/v_windows/v/old/vlib/strconv/atoi.v new file mode 100644 index 0000000..3334919 --- /dev/null +++ b/v_windows/v/old/vlib/strconv/atoi.v @@ -0,0 +1,261 @@ +module strconv + +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +// TODO: use optionals, or some way to return default with error. +const ( + // int_size is the size in bits of an int or uint value. + // int_size = 32 << (~u32(0) >> 63) + // max_u64 = u64(u64(1 << 63) - 1) + int_size = 32 + max_u64 = u64(18446744073709551615) // as u64 // use this until we add support +) + +pub fn byte_to_lower(c byte) byte { + return c | (`x` - `X`) +} + +// common_parse_uint is called by parse_uint and allows the parsing +// to stop on non or invalid digit characters and return with an error +pub fn common_parse_uint(s string, _base int, _bit_size int, error_on_non_digit bool, error_on_high_digit bool) ?u64 { + result, err := common_parse_uint2(s, _base, _bit_size) + // TODO: error_on_non_digit and error_on_high_digit have no difference + if err != 0 && (error_on_non_digit || error_on_high_digit) { + match err { + -1 { return error('common_parse_uint: wrong base $_base for $s') } + -2 { return error('common_parse_uint: wrong bit size $_bit_size for $s') } + -3 { return error('common_parse_uint: integer overflow $s') } + else { return error('common_parse_uint: syntax error $s') } + } + } + return result +} + +// the first returned value contains the parsed value, +// the second returned value contains the error code (0 = OK, >1 = index of first non-parseable character + 1, -1 = wrong base, -2 = wrong bit size, -3 = overflow) +pub fn common_parse_uint2(s string, _base int, _bit_size int) (u64, int) { + mut bit_size := _bit_size + mut base := _base + if s.len < 1 || !underscore_ok(s) { + // return error('parse_uint: syntax error $s') + return u64(0), 1 + } + base0 := base == 0 + mut start_index := 0 + if 2 <= base && base <= 36 { + // valid base; nothing to do + } else if base == 0 { + // Look for octal, hex prefix. + base = 10 + if s[0] == `0` { + if s.len >= 3 && byte_to_lower(s[1]) == `b` { + base = 2 + start_index += 2 + } else if s.len >= 3 && byte_to_lower(s[1]) == `o` { + base = 8 + start_index += 2 + } else if s.len >= 3 && byte_to_lower(s[1]) == `x` { + base = 16 + start_index += 2 + } + // manage leading zeros in decimal base's numbers + else if s.len >= 2 && (s[1] >= `0` && s[1] <= `9`) { + base = 10 + start_index++ + } else { + base = 8 + start_index++ + } + } + } else { + // return error('parse_uint: base error $s - $base') + return u64(0), -1 + } + if bit_size == 0 { + bit_size = strconv.int_size + } else if bit_size < 0 || bit_size > 64 { + // return error('parse_uint: bitsize error $s - $bit_size') + return u64(0), -2 + } + // Cutoff is the smallest number such that cutoff*base > maxUint64. + // Use compile-time constants for common cases. + cutoff := strconv.max_u64 / u64(base) + u64(1) + max_val := if bit_size == 64 { strconv.max_u64 } else { (u64(1) << u64(bit_size)) - u64(1) } + mut n := u64(0) + for i in start_index .. s.len { + c := s[i] + cl := byte_to_lower(c) + mut d := byte(0) + if c == `_` && base0 { + // underscore_ok already called + continue + } else if `0` <= c && c <= `9` { + d = c - `0` + } else if `a` <= cl && cl <= `z` { + d = cl - `a` + 10 + } else { + return n, i + 1 + } + if d >= byte(base) { + return n, i + 1 + } + if n >= cutoff { + // n*base overflows + // return error('parse_uint: range error $s') + return max_val, -3 + } + n *= u64(base) + n1 := n + u64(d) + if n1 < n || n1 > max_val { + // n+v overflows + // return error('parse_uint: range error $s') + return max_val, -3 + } + n = n1 + } + return n, 0 +} + +// parse_uint is like parse_int but for unsigned numbers. +pub fn parse_uint(s string, _base int, _bit_size int) ?u64 { + return common_parse_uint(s, _base, _bit_size, true, true) +} + +// common_parse_int is called by parse int and allows the parsing +// to stop on non or invalid digit characters and return with an error +pub fn common_parse_int(_s string, base int, _bit_size int, error_on_non_digit bool, error_on_high_digit bool) ?i64 { + mut s := _s + mut bit_size := _bit_size + if s.len < 1 { + // return error('parse_int: syntax error $s') + return i64(0) + } + // Pick off leading sign. + mut neg := false + if s[0] == `+` { + s = s[1..] + } else if s[0] == `-` { + neg = true + s = s[1..] + } + // Convert unsigned and check range. + // un := parse_uint(s, base, bit_size) or { + // return i64(0) + // } + un := common_parse_uint(s, base, bit_size, error_on_non_digit, error_on_high_digit) ? + if un == 0 { + return i64(0) + } + if bit_size == 0 { + bit_size = strconv.int_size + } + // TODO: check should u64(bit_size-1) be size of int (32)? + cutoff := u64(1) << u64(bit_size - 1) + if !neg && un >= cutoff { + // return error('parse_int: range error $s0') + return i64(cutoff - u64(1)) + } + if neg && un > cutoff { + // return error('parse_int: range error $s0') + return -i64(cutoff) + } + return if neg { -i64(un) } else { i64(un) } +} + +// parse_int interprets a string s in the given base (0, 2 to 36) and +// bit size (0 to 64) and returns the corresponding value i. +// +// If the base argument is 0, the true base is implied by the string's +// prefix: 2 for "0b", 8 for "0" or "0o", 16 for "0x", and 10 otherwise. +// Also, for argument base 0 only, underscore characters are permitted +// as defined by the Go syntax for integer literals. +// +// The bitSize argument specifies the integer type +// that the result must fit into. Bit sizes 0, 8, 16, 32, and 64 +// correspond to int, int8, int16, int32, and int64. +// If bitSize is below 0 or above 64, an error is returned. +pub fn parse_int(_s string, base int, _bit_size int) ?i64 { + return common_parse_int(_s, base, _bit_size, true, true) +} + +// atoi is equivalent to parse_int(s, 10, 0), converted to type int. +pub fn atoi(s string) ?int { + if s == '' { + return error('strconv.atoi: parsing "$s": invalid syntax ') + } + if (strconv.int_size == 32 && (0 < s.len && s.len < 10)) + || (strconv.int_size == 64 && (0 < s.len && s.len < 19)) { + // Fast path for small integers that fit int type. + mut start_idx := 0 + if s[0] == `-` || s[0] == `+` { + start_idx++ + if s.len - start_idx < 1 { + // return 0, &NumError{fnAtoi, s0, ErrSyntax} + return error('strconv.atoi: parsing "$s": invalid syntax ') + } + } + mut n := 0 + for i in start_idx .. s.len { + ch := s[i] - `0` + if ch > 9 { + // return 0, &NumError{fnAtoi, s0, ErrSyntax} + return error('strconv.atoi: parsing "$s": invalid syntax ') + } + n = n * 10 + int(ch) + } + return if s[0] == `-` { -n } else { n } + } + // Slow path for invalid, big, or underscored integers. + int64 := parse_int(s, 10, 0) ? + return int(int64) +} + +// underscore_ok reports whether the underscores in s are allowed. +// Checking them in this one function lets all the parsers skip over them simply. +// Underscore must appear only between digits or between a base prefix and a digit. +fn underscore_ok(s string) bool { + // saw tracks the last character (class) we saw: + // ^ for beginning of number, + // 0 for a digit or base prefix, + // _ for an underscore, + // ! for none of the above. + mut saw := `^` + mut i := 0 + // Optional sign. + if s.len >= 1 && (s[0] == `-` || s[0] == `+`) { + i++ + } + // Optional base prefix. + mut hex := false + if s.len - i >= 2 && s[i] == `0` && (byte_to_lower(s[i + 1]) == `b` + || byte_to_lower(s[i + 1]) == `o` || byte_to_lower(s[i + 1]) == `x`) { + saw = `0` // base prefix counts as a digit for "underscore as digit separator" + hex = byte_to_lower(s[i + 1]) == `x` + i += 2 + } + // Number proper. + for ; i < s.len; i++ { + // Digits are always okay. + if (`0` <= s[i] && s[i] <= `9`) || (hex && `a` <= byte_to_lower(s[i]) + && byte_to_lower(s[i]) <= `f`) { + saw = `0` + continue + } + // Underscore must follow digit. + if s[i] == `_` { + if saw != `0` { + return false + } + saw = `_` + continue + } + // Underscore must also be followed by digit. + if saw == `_` { + return false + } + // Saw non-digit, non-underscore. + saw = `!` + } + return saw != `_` +} diff --git a/v_windows/v/old/vlib/strconv/atoi_test.v b/v_windows/v/old/vlib/strconv/atoi_test.v new file mode 100644 index 0000000..b356db6 --- /dev/null +++ b/v_windows/v/old/vlib/strconv/atoi_test.v @@ -0,0 +1,84 @@ +import strconv + +fn test_atoi() ? { + assert strconv.atoi('16') ? == 16 + assert strconv.atoi('+16') ? == 16 + assert strconv.atoi('-16') ? == -16 + + // invalid strings + if x := strconv.atoi('str') { + println(x) + assert false + } else { + assert true + } + if x := strconv.atoi('string_longer_than_10_chars') { + println(x) + assert false + } else { + assert true + } + if x := strconv.atoi('') { + println(x) + assert false + } else { + assert true + } +} + +fn test_parse_int() ? { + // Different bases + assert strconv.parse_int('16', 16, 0) ? == 0x16 + assert strconv.parse_int('16', 8, 0) ? == 0o16 + assert strconv.parse_int('11', 2, 0) ? == 3 + // Different bit sizes + assert strconv.parse_int('127', 10, 8) ? == 127 + assert strconv.parse_int('128', 10, 8) ? == 127 + assert strconv.parse_int('32767', 10, 16) ? == 32767 + assert strconv.parse_int('32768', 10, 16) ? == 32767 + assert strconv.parse_int('2147483647', 10, 32) ? == 2147483647 + assert strconv.parse_int('2147483648', 10, 32) ? == 2147483647 + assert strconv.parse_int('9223372036854775807', 10, 64) ? == 9223372036854775807 + assert strconv.parse_int('9223372036854775808', 10, 64) ? == 9223372036854775807 + assert strconv.parse_int('baobab', 36, 64) ? == 683058467 + // Invalid bit sizes + if x := strconv.parse_int('123', 10, -1) { + println(x) + assert false + } else { + assert true + } + if x := strconv.parse_int('123', 10, 65) { + println(x) + assert false + } else { + assert true + } +} + +fn test_common_parse_uint2() { + mut result, mut error := strconv.common_parse_uint2('1', 10, 8) + assert result == 1 + assert error == 0 + result, error = strconv.common_parse_uint2('123', 10, 8) + assert result == 123 + assert error == 0 + result, error = strconv.common_parse_uint2('123', 10, 65) + assert result == 0 + assert error == -2 + result, error = strconv.common_parse_uint2('123', 10, -1) + assert result == 0 + assert error == -2 + result, error = strconv.common_parse_uint2('', 10, 8) + assert result == 0 + assert error == 1 + result, error = strconv.common_parse_uint2('1a', 10, 8) + assert result == 1 + assert error == 2 + result, error = strconv.common_parse_uint2('12a', 10, 8) + assert result == 12 + assert error == 3 + result, error = strconv.common_parse_uint2('123a', 10, 8) + assert result == 123 + assert error == 4 +} diff --git a/v_windows/v/old/vlib/strconv/f32_f64_to_string_test.v b/v_windows/v/old/vlib/strconv/f32_f64_to_string_test.v new file mode 100644 index 0000000..1ebf16b --- /dev/null +++ b/v_windows/v/old/vlib/strconv/f32_f64_to_string_test.v @@ -0,0 +1,171 @@ +/********************************************************************** +* +* Float to string Test +* +**********************************************************************/ +import strconv +import math + +union Ufloat32 { +mut: + f f32 = f32(0) + b u32 +} + +union Ufloat64 { +mut: + f f64 = f64(0) + b u64 +} + +fn f64_from_bits1(b u64) f64 { + mut x := Ufloat64{} + x.b = b + // C.printf("bin: %016llx\n",x.f) + return unsafe { x.f } +} + +fn f32_from_bits1(b u32) f32 { + mut x := Ufloat32{} + x.b = b + // C.printf("bin: %08x\n",x.f) + return unsafe { x.f } +} + +fn test_float_to_str() { + test_cases_f32 := [ + f32_from_bits1(0x0000_0000), // +0 + f32_from_bits1(0x8000_0000), // -0 + f32_from_bits1(0xFFC0_0001), // sNan + f32_from_bits1(0xFF80_0001), // qNan + f32_from_bits1(0x7F80_0000), // +inf + f32_from_bits1(0xFF80_0000), // -inf + 1, + -1, + 10, + -10, + 0.3, + -0.3, + 1000000, + 123456.7, + 123e35, + -123.45, + 1e23, + f32_from_bits1(0x0080_0000), // smallest float32 + math.max_f32, + 383260575764816448., + ] + + exp_result_f32 := [ + '0e+00', + '-0e+00', + 'nan', + 'nan', + '+inf', + '-inf', + '1.e+00', + '-1.e+00', + '1.e+01', + '-1.e+01', + '3.e-01', + '-3.e-01', + '1.e+06', + '1.234567e+05', + '1.23e+37', + '-1.2345e+02', + '1.e+23', + '1.1754944e-38', // aprox from 1.1754943508 × 10−38, + '3.4028235e+38', + '3.8326058e+17', + ] + + test_cases_f64 := [ + f64_from_bits1(0x0000_0000_0000_0000), // +0 + f64_from_bits1(0x8000_0000_0000_0000), // -0 + f64_from_bits1(0x7FF0_0000_0000_0001), // sNan + f64_from_bits1(0x7FF8_0000_0000_0001), // qNan + f64_from_bits1(0x7FF0_0000_0000_0000), // +inf + f64_from_bits1(0xFFF0_0000_0000_0000), // -inf + 1, + -1, + 10, + -10, + 0.3, + -0.3, + 1000000, + 123456.7, + 123e45, + -123.45, + 1e23, + f64_from_bits1(0x0010_0000_0000_0000), // smallest float64 + math.max_f32, + 383260575764816448, + 383260575764816448, + // C failing cases + 123e300, + 123e-300, + 5.e-324, + -5.e-324, + ] + + exp_result_f64 := [ + '0e+00', + '-0e+00', + 'nan', + 'nan', + '+inf', + '-inf', + '1.e+00', + '-1.e+00', + '1.e+01', + '-1.e+01', + '3.e-01', + '-3.e-01', + '1.e+06', + '1.234567e+05', + '1.23e+47', + '-1.2345e+02', + '1.e+23', + '2.2250738585072014e-308', + '3.4028234663852886e+38', + '3.8326057576481645e+17', + '3.8326057576481645e+17', + '1.23e+302', // this test is failed from C sprintf!! + '1.23e-298', + '5.e-324', + '-5.e-324', + ] + + // test f32 + for c, x in test_cases_f32 { + println(x) + s := strconv.f32_to_str(x, 8) + s1 := exp_result_f32[c] + // println("$s1 $s") + assert s == s1 + } + + // test f64 + for c, x in test_cases_f64 { + s := strconv.f64_to_str(x, 17) + s1 := exp_result_f64[c] + // println("$s1 $s") + assert s == s1 + } + + // test long format + for exp := 1; exp < 120; exp++ { + a := strconv.f64_to_str_l(('1e' + exp.str()).f64()) + // println(a) + assert a.len == exp + 1 + + b := strconv.f64_to_str_l(('1e-' + exp.str()).f64()) + // println(b) + assert b.len == exp + 2 + } + + // test rounding str conversion + // println( ftoa.f64_to_str(0.3456789123456, 4) ) + // assert ftoa.f64_to_str(0.3456789123456, 4)=="3.4568e-01" + // assert ftoa.f32_to_str(0.345678, 3)=="3.457e-01" +} diff --git a/v_windows/v/old/vlib/strconv/f32_str.v b/v_windows/v/old/vlib/strconv/f32_str.v new file mode 100644 index 0000000..8e17c89 --- /dev/null +++ b/v_windows/v/old/vlib/strconv/f32_str.v @@ -0,0 +1,377 @@ +module strconv + +/*============================================================================= + +f32 to string + +Copyright (c) 2019-2021 Dario Deledda. All rights reserved. +Use of this source code is governed by an MIT license +that can be found in the LICENSE file. + +This file contains the f32 to string functions + +These functions are based on the work of: +Publication:PLDI 2018: Proceedings of the 39th ACM SIGPLAN +Conference on Programming Language Design and ImplementationJune 2018 +Pages 270–282 https://doi.org/10.1145/3192366.3192369 + +inspired by the Go version here: +https://github.com/cespare/ryu/tree/ba56a33f39e3bbbfa409095d0f9ae168a595feea + +=============================================================================*/ + +// pow of ten table used by n_digit reduction +const ( + ten_pow_table_32 = [ + u32(1), + u32(10), + u32(100), + u32(1000), + u32(10000), + u32(100000), + u32(1000000), + u32(10000000), + u32(100000000), + u32(1000000000), + u32(10000000000), + u32(100000000000), + ] +) + +//============================================================================= +// Conversion Functions +//============================================================================= +const ( + mantbits32 = u32(23) + expbits32 = u32(8) + bias32 = 127 // f32 exponent bias + maxexp32 = 255 +) + +// max 46 char +// -3.40282346638528859811704183484516925440e+38 +[direct_array_access] +pub fn (d Dec32) get_string_32(neg bool, i_n_digit int, i_pad_digit int) string { + n_digit := i_n_digit + 1 + pad_digit := i_pad_digit + 1 + mut out := d.m + // mut out_len := decimal_len_32(out) + mut out_len := dec_digits(out) + out_len_original := out_len + + mut fw_zeros := 0 + if pad_digit > out_len { + fw_zeros = pad_digit - out_len + } + + mut buf := []byte{len: int(out_len + 5 + 1 + 1)} // sign + mant_len + . + e + e_sign + exp_len(2) + \0} + mut i := 0 + + if neg { + if buf.data != 0 { + // The buf.data != 0 check here, is needed for clean compilation + // with `-cc gcc -cstrict -prod`. Without it, gcc produces: + // error: potential null pointer dereference + buf[i] = `-` + } + i++ + } + + mut disp := 0 + if out_len <= 1 { + disp = 1 + } + + if n_digit < out_len { + // println("orig: ${out_len_original}") + out += strconv.ten_pow_table_32[out_len - n_digit - 1] * 5 // round to up + out /= strconv.ten_pow_table_32[out_len - n_digit] + out_len = n_digit + } + + y := i + out_len + mut x := 0 + for x < (out_len - disp - 1) { + buf[y - x] = `0` + byte(out % 10) + out /= 10 + i++ + x++ + } + + // no decimal digits needed, end here + if i_n_digit == 0 { + unsafe { + buf[i] = 0 + return tos(&byte(&buf[0]), i) + } + } + + if out_len >= 1 { + buf[y - x] = `.` + x++ + i++ + } + + if y - x >= 0 { + buf[y - x] = `0` + byte(out % 10) + i++ + } + + for fw_zeros > 0 { + buf[i] = `0` + i++ + fw_zeros-- + } + + buf[i] = `e` + i++ + + mut exp := d.e + out_len_original - 1 + if exp < 0 { + buf[i] = `-` + i++ + exp = -exp + } else { + buf[i] = `+` + i++ + } + + // Always print two digits to match strconv's formatting. + d1 := exp % 10 + d0 := exp / 10 + buf[i] = `0` + byte(d0) + i++ + buf[i] = `0` + byte(d1) + i++ + buf[i] = 0 + + return unsafe { + tos(&byte(&buf[0]), i) + } +} + +fn f32_to_decimal_exact_int(i_mant u32, exp u32) (Dec32, bool) { + mut d := Dec32{} + e := exp - strconv.bias32 + if e > strconv.mantbits32 { + return d, false + } + shift := strconv.mantbits32 - e + mant := i_mant | 0x0080_0000 // implicit 1 + // mant := i_mant | (1 << mantbits32) // implicit 1 + d.m = mant >> shift + if (d.m << shift) != mant { + return d, false + } + for (d.m % 10) == 0 { + d.m /= 10 + d.e++ + } + return d, true +} + +fn f32_to_decimal(mant u32, exp u32) Dec32 { + mut e2 := 0 + mut m2 := u32(0) + if exp == 0 { + // We subtract 2 so that the bounds computation has + // 2 additional bits. + e2 = 1 - strconv.bias32 - int(strconv.mantbits32) - 2 + m2 = mant + } else { + e2 = int(exp) - strconv.bias32 - int(strconv.mantbits32) - 2 + m2 = (u32(1) << strconv.mantbits32) | mant + } + even := (m2 & 1) == 0 + accept_bounds := even + + // Step 2: Determine the interval of valid decimal representations. + mv := u32(4 * m2) + mp := u32(4 * m2 + 2) + mm_shift := bool_to_u32(mant != 0 || exp <= 1) + mm := u32(4 * m2 - 1 - mm_shift) + + mut vr := u32(0) + mut vp := u32(0) + mut vm := u32(0) + mut e10 := 0 + mut vm_is_trailing_zeros := false + mut vr_is_trailing_zeros := false + mut last_removed_digit := byte(0) + + if e2 >= 0 { + q := log10_pow2(e2) + e10 = int(q) + k := pow5_inv_num_bits_32 + pow5_bits(int(q)) - 1 + i := -e2 + int(q) + k + + vr = mul_pow5_invdiv_pow2(mv, q, i) + vp = mul_pow5_invdiv_pow2(mp, q, i) + vm = mul_pow5_invdiv_pow2(mm, q, i) + if q != 0 && (vp - 1) / 10 <= vm / 10 { + // We need to know one removed digit even if we are not + // going to loop below. We could use q = X - 1 above, + // except that would require 33 bits for the result, and + // we've found that 32-bit arithmetic is faster even on + // 64-bit machines. + l := pow5_inv_num_bits_32 + pow5_bits(int(q - 1)) - 1 + last_removed_digit = byte(mul_pow5_invdiv_pow2(mv, q - 1, -e2 + int(q - 1) + l) % 10) + } + if q <= 9 { + // The largest power of 5 that fits in 24 bits is 5^10, + // but q <= 9 seems to be safe as well. Only one of mp, + // mv, and mm can be a multiple of 5, if any. + if mv % 5 == 0 { + vr_is_trailing_zeros = multiple_of_power_of_five_32(mv, q) + } else if accept_bounds { + vm_is_trailing_zeros = multiple_of_power_of_five_32(mm, q) + } else if multiple_of_power_of_five_32(mp, q) { + vp-- + } + } + } else { + q := log10_pow5(-e2) + e10 = int(q) + e2 + i := -e2 - int(q) + k := pow5_bits(i) - pow5_num_bits_32 + mut j := int(q) - k + vr = mul_pow5_div_pow2(mv, u32(i), j) + vp = mul_pow5_div_pow2(mp, u32(i), j) + vm = mul_pow5_div_pow2(mm, u32(i), j) + if q != 0 && ((vp - 1) / 10) <= vm / 10 { + j = int(q) - 1 - (pow5_bits(i + 1) - pow5_num_bits_32) + last_removed_digit = byte(mul_pow5_div_pow2(mv, u32(i + 1), j) % 10) + } + if q <= 1 { + // {vr,vp,vm} is trailing zeros if {mv,mp,mm} has at + // least q trailing 0 bits. mv = 4 * m2, so it always + // has at least two trailing 0 bits. + vr_is_trailing_zeros = true + if accept_bounds { + // mm = mv - 1 - mm_shift, so it has 1 trailing 0 bit + // if mm_shift == 1. + vm_is_trailing_zeros = mm_shift == 1 + } else { + // mp = mv + 2, so it always has at least one + // trailing 0 bit. + vp-- + } + } else if q < 31 { + vr_is_trailing_zeros = multiple_of_power_of_two_32(mv, q - 1) + } + } + + // Step 4: Find the shortest decimal representation + // in the interval of valid representations. + mut removed := 0 + mut out := u32(0) + if vm_is_trailing_zeros || vr_is_trailing_zeros { + // General case, which happens rarely (~4.0%). + for vp / 10 > vm / 10 { + vm_is_trailing_zeros = vm_is_trailing_zeros && (vm % 10) == 0 + vr_is_trailing_zeros = vr_is_trailing_zeros && (last_removed_digit == 0) + last_removed_digit = byte(vr % 10) + vr /= 10 + vp /= 10 + vm /= 10 + removed++ + } + if vm_is_trailing_zeros { + for vm % 10 == 0 { + vr_is_trailing_zeros = vr_is_trailing_zeros && (last_removed_digit == 0) + last_removed_digit = byte(vr % 10) + vr /= 10 + vp /= 10 + vm /= 10 + removed++ + } + } + if vr_is_trailing_zeros && (last_removed_digit == 5) && (vr % 2) == 0 { + // Round even if the exact number is .....50..0. + last_removed_digit = 4 + } + out = vr + // We need to take vr + 1 if vr is outside bounds + // or we need to round up. + if (vr == vm && (!accept_bounds || !vm_is_trailing_zeros)) || last_removed_digit >= 5 { + out++ + } + } else { + // Specialized for the common case (~96.0%). Percentages below + // are relative to this. Loop iterations below (approximately): + // 0: 13.6%, 1: 70.7%, 2: 14.1%, 3: 1.39%, 4: 0.14%, 5+: 0.01% + for vp / 10 > vm / 10 { + last_removed_digit = byte(vr % 10) + vr /= 10 + vp /= 10 + vm /= 10 + removed++ + } + // We need to take vr + 1 if vr is outside bounds + // or we need to round up. + out = vr + bool_to_u32(vr == vm || last_removed_digit >= 5) + } + + return Dec32{ + m: out + e: e10 + removed + } +} + +//============================================================================= +// String Functions +//============================================================================= + +// f32_to_str return a string in scientific notation with max n_digit after the dot +pub fn f32_to_str(f f32, n_digit int) string { + mut u1 := Uf32{} + u1.f = f + u := unsafe { u1.u } + + neg := (u >> (strconv.mantbits32 + strconv.expbits32)) != 0 + mant := u & ((u32(1) << strconv.mantbits32) - u32(1)) + exp := (u >> strconv.mantbits32) & ((u32(1) << strconv.expbits32) - u32(1)) + + // println("${neg} ${mant} e ${exp-bias32}") + + // Exit early for easy cases. + if (exp == strconv.maxexp32) || (exp == 0 && mant == 0) { + return get_string_special(neg, exp == 0, mant == 0) + } + + mut d, ok := f32_to_decimal_exact_int(mant, exp) + if !ok { + // println("with exp form") + d = f32_to_decimal(mant, exp) + } + + // println("${d.m} ${d.e}") + return d.get_string_32(neg, n_digit, 0) +} + +// f32_to_str return a string in scientific notation with max n_digit after the dot +pub fn f32_to_str_pad(f f32, n_digit int) string { + mut u1 := Uf32{} + u1.f = f + u := unsafe { u1.u } + + neg := (u >> (strconv.mantbits32 + strconv.expbits32)) != 0 + mant := u & ((u32(1) << strconv.mantbits32) - u32(1)) + exp := (u >> strconv.mantbits32) & ((u32(1) << strconv.expbits32) - u32(1)) + + // println("${neg} ${mant} e ${exp-bias32}") + + // Exit early for easy cases. + if (exp == strconv.maxexp32) || (exp == 0 && mant == 0) { + return get_string_special(neg, exp == 0, mant == 0) + } + + mut d, ok := f32_to_decimal_exact_int(mant, exp) + if !ok { + // println("with exp form") + d = f32_to_decimal(mant, exp) + } + + // println("${d.m} ${d.e}") + return d.get_string_32(neg, n_digit, n_digit) +} diff --git a/v_windows/v/old/vlib/strconv/f64_str.v b/v_windows/v/old/vlib/strconv/f64_str.v new file mode 100644 index 0000000..6be4354 --- /dev/null +++ b/v_windows/v/old/vlib/strconv/f64_str.v @@ -0,0 +1,418 @@ +module strconv + +/*============================================================================= + +f64 to string + +Copyright (c) 2019-2021 Dario Deledda. All rights reserved. +Use of this source code is governed by an MIT license +that can be found in the LICENSE file. + +This file contains the f64 to string functions + +These functions are based on the work of: +Publication:PLDI 2018: Proceedings of the 39th ACM SIGPLAN +Conference on Programming Language Design and ImplementationJune 2018 +Pages 270–282 https://doi.org/10.1145/3192366.3192369 + +inspired by the Go version here: +https://github.com/cespare/ryu/tree/ba56a33f39e3bbbfa409095d0f9ae168a595feea + +=============================================================================*/ + +// pow of ten table used by n_digit reduction +const ( + ten_pow_table_64 = [ + u64(1), + u64(10), + u64(100), + u64(1000), + u64(10000), + u64(100000), + u64(1000000), + u64(10000000), + u64(100000000), + u64(1000000000), + u64(10000000000), + u64(100000000000), + u64(1000000000000), + u64(10000000000000), + u64(100000000000000), + u64(1000000000000000), + u64(10000000000000000), + u64(100000000000000000), + u64(1000000000000000000), + u64(10000000000000000000), + ] +) + +//============================================================================= +// Conversion Functions +//============================================================================= +const ( + mantbits64 = u32(52) + expbits64 = u32(11) + bias64 = 1023 // f64 exponent bias + maxexp64 = 2047 +) + +[direct_array_access] +fn (d Dec64) get_string_64(neg bool, i_n_digit int, i_pad_digit int) string { + mut n_digit := i_n_digit + 1 + pad_digit := i_pad_digit + 1 + mut out := d.m + mut d_exp := d.e + // mut out_len := decimal_len_64(out) + mut out_len := dec_digits(out) + out_len_original := out_len + + mut fw_zeros := 0 + if pad_digit > out_len { + fw_zeros = pad_digit - out_len + } + + mut buf := []byte{len: (out_len + 6 + 1 + 1 + fw_zeros)} // sign + mant_len + . + e + e_sign + exp_len(2) + \0} + mut i := 0 + + if neg { + buf[i] = `-` + i++ + } + + mut disp := 0 + if out_len <= 1 { + disp = 1 + } + + // rounding last used digit + if n_digit < out_len { + // println("out:[$out]") + out += strconv.ten_pow_table_64[out_len - n_digit - 1] * 5 // round to up + out /= strconv.ten_pow_table_64[out_len - n_digit] + // println("out1:[$out] ${d.m / ten_pow_table_64[out_len - n_digit ]}") + if d.m / strconv.ten_pow_table_64[out_len - n_digit] < out { + d_exp++ + n_digit++ + } + + // println("cmp: ${d.m/ten_pow_table_64[out_len - n_digit ]} ${out/ten_pow_table_64[out_len - n_digit ]}") + + out_len = n_digit + // println("orig: ${out_len_original} new len: ${out_len} out:[$out]") + } + + y := i + out_len + mut x := 0 + for x < (out_len - disp - 1) { + buf[y - x] = `0` + byte(out % 10) + out /= 10 + i++ + x++ + } + + // no decimal digits needed, end here + if i_n_digit == 0 { + unsafe { + buf[i] = 0 + return tos(&byte(&buf[0]), i) + } + } + + if out_len >= 1 { + buf[y - x] = `.` + x++ + i++ + } + + if y - x >= 0 { + buf[y - x] = `0` + byte(out % 10) + i++ + } + + for fw_zeros > 0 { + buf[i] = `0` + i++ + fw_zeros-- + } + + buf[i] = `e` + i++ + + mut exp := d_exp + out_len_original - 1 + if exp < 0 { + buf[i] = `-` + i++ + exp = -exp + } else { + buf[i] = `+` + i++ + } + + // Always print at least two digits to match strconv's formatting. + d2 := exp % 10 + exp /= 10 + d1 := exp % 10 + d0 := exp / 10 + if d0 > 0 { + buf[i] = `0` + byte(d0) + i++ + } + buf[i] = `0` + byte(d1) + i++ + buf[i] = `0` + byte(d2) + i++ + buf[i] = 0 + + return unsafe { + tos(&byte(&buf[0]), i) + } +} + +fn f64_to_decimal_exact_int(i_mant u64, exp u64) (Dec64, bool) { + mut d := Dec64{} + e := exp - strconv.bias64 + if e > strconv.mantbits64 { + return d, false + } + shift := strconv.mantbits64 - e + mant := i_mant | u64(0x0010_0000_0000_0000) // implicit 1 + // mant := i_mant | (1 << mantbits64) // implicit 1 + d.m = mant >> shift + if (d.m << shift) != mant { + return d, false + } + + for (d.m % 10) == 0 { + d.m /= 10 + d.e++ + } + return d, true +} + +fn f64_to_decimal(mant u64, exp u64) Dec64 { + mut e2 := 0 + mut m2 := u64(0) + if exp == 0 { + // We subtract 2 so that the bounds computation has + // 2 additional bits. + e2 = 1 - strconv.bias64 - int(strconv.mantbits64) - 2 + m2 = mant + } else { + e2 = int(exp) - strconv.bias64 - int(strconv.mantbits64) - 2 + m2 = (u64(1) << strconv.mantbits64) | mant + } + even := (m2 & 1) == 0 + accept_bounds := even + + // Step 2: Determine the interval of valid decimal representations. + mv := u64(4 * m2) + mm_shift := bool_to_u64(mant != 0 || exp <= 1) + + // Step 3: Convert to a decimal power base uing 128-bit arithmetic. + mut vr := u64(0) + mut vp := u64(0) + mut vm := u64(0) + mut e10 := 0 + mut vm_is_trailing_zeros := false + mut vr_is_trailing_zeros := false + + if e2 >= 0 { + // This expression is slightly faster than max(0, log10Pow2(e2) - 1). + q := log10_pow2(e2) - bool_to_u32(e2 > 3) + e10 = int(q) + k := pow5_inv_num_bits_64 + pow5_bits(int(q)) - 1 + i := -e2 + int(q) + k + + mul := pow5_inv_split_64[q] + vr = mul_shift_64(u64(4) * m2, mul, i) + vp = mul_shift_64(u64(4) * m2 + u64(2), mul, i) + vm = mul_shift_64(u64(4) * m2 - u64(1) - mm_shift, mul, i) + if q <= 21 { + // This should use q <= 22, but I think 21 is also safe. + // Smaller values may still be safe, but it's more + // difficult to reason about them. Only one of mp, mv, + // and mm can be a multiple of 5, if any. + if mv % 5 == 0 { + vr_is_trailing_zeros = multiple_of_power_of_five_64(mv, q) + } else if accept_bounds { + // Same as min(e2 + (^mm & 1), pow5Factor64(mm)) >= q + // <=> e2 + (^mm & 1) >= q && pow5Factor64(mm) >= q + // <=> true && pow5Factor64(mm) >= q, since e2 >= q. + vm_is_trailing_zeros = multiple_of_power_of_five_64(mv - 1 - mm_shift, + q) + } else if multiple_of_power_of_five_64(mv + 2, q) { + vp-- + } + } + } else { + // This expression is slightly faster than max(0, log10Pow5(-e2) - 1). + q := log10_pow5(-e2) - bool_to_u32(-e2 > 1) + e10 = int(q) + e2 + i := -e2 - int(q) + k := pow5_bits(i) - pow5_num_bits_64 + j := int(q) - k + mul := pow5_split_64[i] + vr = mul_shift_64(u64(4) * m2, mul, j) + vp = mul_shift_64(u64(4) * m2 + u64(2), mul, j) + vm = mul_shift_64(u64(4) * m2 - u64(1) - mm_shift, mul, j) + if q <= 1 { + // {vr,vp,vm} is trailing zeros if {mv,mp,mm} has at least q trailing 0 bits. + // mv = 4 * m2, so it always has at least two trailing 0 bits. + vr_is_trailing_zeros = true + if accept_bounds { + // mm = mv - 1 - mmShift, so it has 1 trailing 0 bit iff mmShift == 1. + vm_is_trailing_zeros = (mm_shift == 1) + } else { + // mp = mv + 2, so it always has at least one trailing 0 bit. + vp-- + } + } else if q < 63 { // TODO(ulfjack/cespare): Use a tighter bound here. + // We need to compute min(ntz(mv), pow5Factor64(mv) - e2) >= q - 1 + // <=> ntz(mv) >= q - 1 && pow5Factor64(mv) - e2 >= q - 1 + // <=> ntz(mv) >= q - 1 (e2 is negative and -e2 >= q) + // <=> (mv & ((1 << (q - 1)) - 1)) == 0 + // We also need to make sure that the left shift does not overflow. + vr_is_trailing_zeros = multiple_of_power_of_two_64(mv, q - 1) + } + } + + // Step 4: Find the shortest decimal representation + // in the interval of valid representations. + mut removed := 0 + mut last_removed_digit := byte(0) + mut out := u64(0) + // On average, we remove ~2 digits. + if vm_is_trailing_zeros || vr_is_trailing_zeros { + // General case, which happens rarely (~0.7%). + for { + vp_div_10 := vp / 10 + vm_div_10 := vm / 10 + if vp_div_10 <= vm_div_10 { + break + } + vm_mod_10 := vm % 10 + vr_div_10 := vr / 10 + vr_mod_10 := vr % 10 + vm_is_trailing_zeros = vm_is_trailing_zeros && vm_mod_10 == 0 + vr_is_trailing_zeros = vr_is_trailing_zeros && (last_removed_digit == 0) + last_removed_digit = byte(vr_mod_10) + vr = vr_div_10 + vp = vp_div_10 + vm = vm_div_10 + removed++ + } + if vm_is_trailing_zeros { + for { + vm_div_10 := vm / 10 + vm_mod_10 := vm % 10 + if vm_mod_10 != 0 { + break + } + vp_div_10 := vp / 10 + vr_div_10 := vr / 10 + vr_mod_10 := vr % 10 + vr_is_trailing_zeros = vr_is_trailing_zeros && (last_removed_digit == 0) + last_removed_digit = byte(vr_mod_10) + vr = vr_div_10 + vp = vp_div_10 + vm = vm_div_10 + removed++ + } + } + if vr_is_trailing_zeros && (last_removed_digit == 5) && (vr % 2) == 0 { + // Round even if the exact number is .....50..0. + last_removed_digit = 4 + } + out = vr + // We need to take vr + 1 if vr is outside bounds + // or we need to round up. + if (vr == vm && (!accept_bounds || !vm_is_trailing_zeros)) || last_removed_digit >= 5 { + out++ + } + } else { + // Specialized for the common case (~99.3%). + // Percentages below are relative to this. + mut round_up := false + for vp / 100 > vm / 100 { + // Optimization: remove two digits at a time (~86.2%). + round_up = (vr % 100) >= 50 + vr /= 100 + vp /= 100 + vm /= 100 + removed += 2 + } + // Loop iterations below (approximately), without optimization above: + // 0: 0.03%, 1: 13.8%, 2: 70.6%, 3: 14.0%, 4: 1.40%, 5: 0.14%, 6+: 0.02% + // Loop iterations below (approximately), with optimization above: + // 0: 70.6%, 1: 27.8%, 2: 1.40%, 3: 0.14%, 4+: 0.02% + for vp / 10 > vm / 10 { + round_up = (vr % 10) >= 5 + vr /= 10 + vp /= 10 + vm /= 10 + removed++ + } + // We need to take vr + 1 if vr is outside bounds + // or we need to round up. + out = vr + bool_to_u64(vr == vm || round_up) + } + + return Dec64{ + m: out + e: e10 + removed + } +} + +//============================================================================= +// String Functions +//============================================================================= + +// f64_to_str return a string in scientific notation with max n_digit after the dot +pub fn f64_to_str(f f64, n_digit int) string { + mut u1 := Uf64{} + u1.f = f + u := unsafe { u1.u } + + neg := (u >> (strconv.mantbits64 + strconv.expbits64)) != 0 + mant := u & ((u64(1) << strconv.mantbits64) - u64(1)) + exp := (u >> strconv.mantbits64) & ((u64(1) << strconv.expbits64) - u64(1)) + // println("s:${neg} mant:${mant} exp:${exp} float:${f} byte:${u1.u:016lx}") + + // Exit early for easy cases. + if (exp == strconv.maxexp64) || (exp == 0 && mant == 0) { + return get_string_special(neg, exp == 0, mant == 0) + } + + mut d, ok := f64_to_decimal_exact_int(mant, exp) + if !ok { + // println("to_decimal") + d = f64_to_decimal(mant, exp) + } + // println("${d.m} ${d.e}") + return d.get_string_64(neg, n_digit, 0) +} + +// f64_to_str return a string in scientific notation with max n_digit after the dot +pub fn f64_to_str_pad(f f64, n_digit int) string { + mut u1 := Uf64{} + u1.f = f + u := unsafe { u1.u } + + neg := (u >> (strconv.mantbits64 + strconv.expbits64)) != 0 + mant := u & ((u64(1) << strconv.mantbits64) - u64(1)) + exp := (u >> strconv.mantbits64) & ((u64(1) << strconv.expbits64) - u64(1)) + // println("s:${neg} mant:${mant} exp:${exp} float:${f} byte:${u1.u:016lx}") + + // Exit early for easy cases. + if (exp == strconv.maxexp64) || (exp == 0 && mant == 0) { + return get_string_special(neg, exp == 0, mant == 0) + } + + mut d, ok := f64_to_decimal_exact_int(mant, exp) + if !ok { + // println("to_decimal") + d = f64_to_decimal(mant, exp) + } + // println("DEBUG: ${d.m} ${d.e}") + return d.get_string_64(neg, n_digit, n_digit) +} diff --git a/v_windows/v/old/vlib/strconv/format.md b/v_windows/v/old/vlib/strconv/format.md new file mode 100644 index 0000000..1563ce9 --- /dev/null +++ b/v_windows/v/old/vlib/strconv/format.md @@ -0,0 +1,250 @@ +# v_printf/v_sprintf + +These are v implementations of the C language `printf` and `sprintf` functions. + +***Note: These functions are platform dependent in C, but in V they are platform independent.*** + +### v_sprintf + +`v_sprintf` has a variable number of parameters. +The first is a format string to control the appearance of the final string. +Each format specifier (%s, %d, etc.) in the format string +is replaced by the textual version of the following parameters. + +```v +import strconv + +fn main() { + a := 'World' + s := strconv.v_sprintf('Hello %s!', a) + println(s) +} +``` + +``` +Hello World! +``` + +### v_printf + +`v_printf` creates the same modified string as `v_sprintf`, using the same format specifiers, +but it will immediately print the modified string to stdout instead of returning a string. + +### Syntax + +The syntax for a format specifier is: + +``` +%[parameter][flags][width][.precision][length]type +``` + +#### Flags field + +The Flags field may be zero or more (in any order) of: + +| Character | Description | +| ----------- | ------------------------------------------------------------ | +| `-` (minus) | Left-align the output of this specifier. (The default is to right-align the output.) | +| `+` (plus) | Prepends a plus for positive signed-numeric types. positive = `+`, negative = `-`. (The default doesn't prepend anything to positive numbers.) | +| `0` (zero) | When the 'width' option is specified, prepends zeros for numeric types. (The default prepends spaces.) For example, `printf("%4X",3)` produces ` 3`, while `printf("%04X",3)` produces `0003`. | + +#### Width field + +The Width field specifies a *maximum* number of characters to output, +and is typically used to pad fixed-width fields in tabulated output, +it causes truncation of oversized fields. + +The width field may be omitted, or it may be a numeric integer value, +or may also be specified by a parameter when indicated by an asterisk `*`. +For example, `v_printf("%*.s", 5, my_string)` will result in ` mystring` being printed, +with a total width of 5 characters. + +#### Length field + +The Length field can be omitted or be any of: + +| Character | Description | +| --------- | ------------------------------------------------------------ | +| `hh` | For integer types, causes `printf` to expect an `byte` or `i8` argument. | +| `h` | For integer types, causes `printf` to expect an `int16` or `u16` argument. | +| `l` | For integer types, causes `printf` to expect an `i64` or `u64` argument. | +| `ll` | For integer types, causes `printf` to expect an `i64` or `u64` argument. | +| | | +| | | + +#### Type field + +The Type field can be any of: + +| Character | Description | +| --------- | ------------------------------------------------------------ | +| `%` | Prints a literal `%` character (this type doesn't accept any flags, width, precision, length fields). | +| `d`, `i` | `int` as a signed `int` `%d` and `%i` are synonymous for output. The size of the argument is specified by the length field. | +| `u` | `unsigned int`. The size of the argument is specified by the length field. | +| `f`, `F` | `double` in normal notation. `f` and `F` only differs in how the strings are printed: lowercase or uppercase. | +| `e`, `E` | `double` in scientific notation.`e` and `E` only differs in how the strings are printed: lowercase or uppercase. | +| `g`, `G` | `double` in automatic notation.`g` and `G` only differs in how the strings are printed: lowercase or uppercase. | +| `x`, `X` | `unsigned int` as a hexadecimal number. `x` uses lower-case letters and `X` uses upper-case. | +| `s` | string | +| `p` | `void *` (pointer to void) in an implementation-defined format. | +| `c` | `char` (character). | + +## Examples + +various types + +```v oksyntax +a0 := u32(10) +b0 := 200 +c0 := byte(12) +s0 := 'ciAo' +ch0 := `B` +f0 := 0.312345 +f1 := 200000.0 +sc0 := 'ciao: [%-08u] %d %hhd [%8s] [%08X] [%-20.4f] [%-20.4f] [%c]' +temp_s = strconv.v_sprintf(sc0, a0, b0, c0, s0, b0, f0, f1, ch0) +println(temp_s) +``` + +``` +ciao: [10 ] 200 12 [ ciAo] [000000C8] [0.3123 ] [200000.0000 ] [B] +``` + +integer + +```v oksyntax +a := byte(12) +b := i16(13) +c := 14 +d := i64(15) +sc1 := '==>%hhd %hd %d %ld' +temp_s = strconv.v_sprintf(sc1, a, b, c, d) +println(temp_s) +``` + +``` +==>12 13 14 15 +``` + +unsigned integer + +```v oksyntax +a1 := byte(0xff) +b1 := u16(0xffff) +c1 := u32(0xffffffff) +d1 := u64(-1) +sc2 := '%hhu %hu %u %lu' +temp_s = strconv.v_sprintf(sc2, a1, b1, c1, d1) +println(temp_s) +``` + +``` +255 65535 4294967295 18446744073709551615 +``` + +hexadecimal + +```v oksyntax +a1 := byte(0xff) +b1 := i16(0xffff) +c1 := u32(0xffffffff) +d1 := u64(-1) +sc3 := '%hhx %hx %x %lx' +temp_s = strconv.v_sprintf(sc3, a1, b1, c1, d1) +println(temp_s) +``` + +``` +ff ffff ffffffff ffffffffffffffff +``` + +hexadecimal + +```v oksyntax +a2 := 125 +sc7 := '[%9x] [%9X] [%-9x] [%-9X] [%09x] [%09X]' +temp_s = strconv.v_sprintf(sc7, a2, a2, a2, a2, a2, a2) +println(temp_s) +``` + +``` +[ 7d] [ 7D] [7d ] [7D ] [00000007d] [00000007D] +``` + +floating points + +```v oksyntax +f0 := 0.312345 +f1 := 200000.0 +f2 := -1234.300e6 +f3 := 1234.300e-6 +sc4 := '[%-20.3e] [%20.3e] [%-020.3e] [%-020.3E] [%-020.3e] [%-020.3e]' +temp_s = strconv.v_sprintf(sc4, f0, f1, f1, f1, f2, f3) +println(temp_s) +``` + +``` +[3.123e-01 ] [ 2.000e+05] [2.000e+05 ] [2.000E+05 ] [-1.234e+09 ] [1.234e-03 ] +``` + +float automatic notations + +```v oksyntax +mut ft := -1e-7 +mut x := 0 +sc8 := '[%20g][%20G]|' +for x < 12 { + temp_s = strconv.v_sprintf(sc8, ft, ft) + println('$temp_s\n') + ft = ft * 10.0 + x++ +} +``` + +``` +[ -1e-07][ -1E-07]| +[ -1e-06][ -1E-06]| +[ -1e-05][ -1E-05]| +[ -0.0001][ -0.0001]| +[ -0.001][ -0.001]| +[ -0.01][ -0.01]| +[ -0.1][ -0.1]| +[ -1][ -1]| +[ -10][ -10]| +[ -100][ -100]| +[ -1000][ -1000]| +[ -10000][ -10000]| + +``` + +## Utility functions + +The format module also has some utility functions: + +```v oksyntax nofmt +// calling struct +struct BF_param { + pad_ch byte = ` ` // padding char + len0 int = -1 // default len for whole the number or string + len1 int = 6 // number of decimal digits, if needed + positive bool = true // mandatory: the sign of the number passed + sign_flag bool = false // flag for print sign as prefix in padding + allign Align_text = .right // alignment of the string + rm_tail_zero bool = false // remove the tail zeros from floats +} + +// utilities +fn format_dec(d u64, p BF_param) string +fn format_fl(f f64, p BF_param) string +fn format_es(f f64, p BF_param) string +fn remove_tail_zeros(s string) string +``` + +`format_dec` format the integer number using the parameters in the `BF_param` struct. + +`format_fl` format a float number in normal notation using the parameters in the `BF_param` struct. + +`format_es format a float number in scientific notation using the parameters in the BF_param` +struct. + +`remove_tail_zeros` removes the tailing zeros from a floating point number as string. diff --git a/v_windows/v/old/vlib/strconv/format.v b/v_windows/v/old/vlib/strconv/format.v new file mode 100644 index 0000000..e11a604 --- /dev/null +++ b/v_windows/v/old/vlib/strconv/format.v @@ -0,0 +1,113 @@ +module strconv + +/* +printf/sprintf V implementation + +Copyright (c) 2020 Dario Deledda. All rights reserved. +Use of this source code is governed by an MIT license +that can be found in the LICENSE file. + +This file contains the printf/sprintf functions +*/ +import strings + +pub enum Align_text { + right = 0 + left + center +} + +/* +Float conversion utility +*/ +const ( + // rounding value + dec_round = [ + f64(0.5), + 0.05, + 0.005, + 0.0005, + 0.00005, + 0.000005, + 0.0000005, + 0.00000005, + 0.000000005, + 0.0000000005, + 0.00000000005, + 0.000000000005, + 0.0000000000005, + 0.00000000000005, + 0.000000000000005, + 0.0000000000000005, + 0.00000000000000005, + 0.000000000000000005, + 0.0000000000000000005, + 0.00000000000000000005, + ] +) + +/* +const( + // rounding value + dec_round = [ + f64(0.44), + 0.044, + 0.0044, + 0.00044, + 0.000044, + 0.0000044, + 0.00000044, + 0.000000044, + 0.0000000044, + 0.00000000044, + 0.000000000044, + 0.0000000000044, + 0.00000000000044, + 0.000000000000044, + 0.0000000000000044, + 0.00000000000000044, + 0.000000000000000044, + 0.0000000000000000044, + 0.00000000000000000044, + 0.000000000000000000044, + ] +) +*/ +// max float 1.797693134862315708145274237317043567981e+308 + +/* +Single format functions +*/ +pub struct BF_param { +pub mut: + pad_ch byte = byte(` `) // padding char + len0 int = -1 // default len for whole the number or string + len1 int = 6 // number of decimal digits, if needed + positive bool = true // mandatory: the sign of the number passed + sign_flag bool // flag for print sign as prefix in padding + allign Align_text = .right // alignment of the string + rm_tail_zero bool // remove the tail zeros from floats +} + +pub fn format_str(s string, p BF_param) string { + if p.len0 <= 0 { + return s.clone() + } + dif := p.len0 - utf8_str_visible_length(s) + if dif <= 0 { + return s.clone() + } + mut res := strings.new_builder(s.len + dif) + if p.allign == .right { + for i1 := 0; i1 < dif; i1++ { + res.write_b(p.pad_ch) + } + } + res.write_string(s) + if p.allign == .left { + for i1 := 0; i1 < dif; i1++ { + res.write_b(p.pad_ch) + } + } + return res.str() +} diff --git a/v_windows/v/old/vlib/strconv/format_mem.v b/v_windows/v/old/vlib/strconv/format_mem.v new file mode 100644 index 0000000..f3a11cb --- /dev/null +++ b/v_windows/v/old/vlib/strconv/format_mem.v @@ -0,0 +1,498 @@ +/*============================================================================= +Copyright (c) 2019-2021 Dario Deledda. All rights reserved. +Use of this source code is governed by an MIT license +that can be found in the LICENSE file. + +This file contains string interpolation V functions +=============================================================================*/ +module strconv + +import strings + +// strings.Builder version of format_str +pub fn format_str_sb(s string, p BF_param, mut sb strings.Builder) { + if p.len0 <= 0 { + sb.write_string(s) + return + } + dif := p.len0 - utf8_str_visible_length(s) + if dif <= 0 { + sb.write_string(s) + return + } + + if p.allign == .right { + for i1 := 0; i1 < dif; i1++ { + sb.write_b(p.pad_ch) + } + } + sb.write_string(s) + if p.allign == .left { + for i1 := 0; i1 < dif; i1++ { + sb.write_b(p.pad_ch) + } + } +} + +const ( + // digit pairs in reverse order + digit_pairs = '00102030405060708090011121314151617181910212223242526272829203132333435363738393041424344454647484940515253545556575859506162636465666768696071727374757677787970818283848586878889809192939495969798999' +) + +// format_dec_sb format a u64 +[direct_array_access] +pub fn format_dec_sb(d u64, p BF_param, mut res strings.Builder) { + mut n_char := dec_digits(d) + sign_len := if !p.positive || p.sign_flag { 1 } else { 0 } + number_len := sign_len + n_char + dif := p.len0 - number_len + mut sign_written := false + + if p.allign == .right { + if p.pad_ch == `0` { + if p.positive { + if p.sign_flag { + res.write_b(`+`) + sign_written = true + } + } else { + res.write_b(`-`) + sign_written = true + } + } + // write the pad chars + for i1 := 0; i1 < dif; i1++ { + res.write_b(p.pad_ch) + } + } + + if !sign_written { + // no pad char, write the sign before the number + if p.positive { + if p.sign_flag { + res.write_b(`+`) + } + } else { + res.write_b(`-`) + } + } + + /* + // Legacy version + // max u64 18446744073709551615 => 20 byte + mut buf := [32]byte{} + mut i := 20 + mut d1 := d + for i >= (21 - n_char) { + buf[i] = byte(d1 % 10) + `0` + d1 = d1 / 10 + i-- + } + i++ + */ + + //=========================================== + // Speed version + // max u64 18446744073709551615 => 20 byte + mut buf := [32]byte{} + mut i := 20 + mut n := d + mut d_i := u64(0) + if n > 0 { + for n > 0 { + n1 := n / 100 + // calculate the digit_pairs start index + d_i = (n - (n1 * 100)) << 1 + n = n1 + unsafe { + buf[i] = strconv.digit_pairs.str[d_i] + } + i-- + d_i++ + unsafe { + buf[i] = strconv.digit_pairs.str[d_i] + } + i-- + } + i++ + // remove head zero + if d_i < 20 { + i++ + } + unsafe { res.write_ptr(&buf[i], n_char) } + } else { + // we have a zero no need of more code! + res.write_b(`0`) + } + //=========================================== + + if p.allign == .left { + for i1 := 0; i1 < dif; i1++ { + res.write_b(p.pad_ch) + } + } + return +} + +[direct_array_access; manualfree] +pub fn f64_to_str_lnd1(f f64, dec_digit int) string { + unsafe { + // we add the rounding value + s := f64_to_str(f + dec_round[dec_digit], 18) + // check for +inf -inf Nan + if s.len > 2 && (s[0] == `n` || s[1] == `i`) { + return s + } + + m_sgn_flag := false + mut sgn := 1 + mut b := [26]byte{} + mut d_pos := 1 + mut i := 0 + mut i1 := 0 + mut exp := 0 + mut exp_sgn := 1 + + mut dot_res_sp := -1 + + // get sign and deciaml parts + for c in s { + if c == `-` { + sgn = -1 + i++ + } else if c == `+` { + sgn = 1 + i++ + } else if c >= `0` && c <= `9` { + b[i1] = c + i1++ + i++ + } else if c == `.` { + if sgn > 0 { + d_pos = i + } else { + d_pos = i - 1 + } + i++ + } else if c == `e` { + i++ + break + } else { + s.free() + return '[Float conversion error!!]' + } + } + b[i1] = 0 + + // get exponent + if s[i] == `-` { + exp_sgn = -1 + i++ + } else if s[i] == `+` { + exp_sgn = 1 + i++ + } + + mut c := i + for c < s.len { + exp = exp * 10 + int(s[c] - `0`) + c++ + } + + // allocate exp+32 chars for the return string + // mut res := []byte{len:exp+32,init:`0`} + mut res := []byte{len: exp + 32, init: 0} + mut r_i := 0 // result string buffer index + + // println("s:${sgn} b:${b[0]} es:${exp_sgn} exp:${exp}") + + // s no more needed + s.free() + + if sgn == 1 { + if m_sgn_flag { + res[r_i] = `+` + r_i++ + } + } else { + res[r_i] = `-` + r_i++ + } + + i = 0 + if exp_sgn >= 0 { + for b[i] != 0 { + res[r_i] = b[i] + r_i++ + i++ + if i >= d_pos && exp >= 0 { + if exp == 0 { + dot_res_sp = r_i + res[r_i] = `.` + r_i++ + } + exp-- + } + } + for exp >= 0 { + res[r_i] = `0` + r_i++ + exp-- + } + // println("exp: $exp $r_i $dot_res_sp") + } else { + mut dot_p := true + for exp > 0 { + res[r_i] = `0` + r_i++ + exp-- + if dot_p { + dot_res_sp = r_i + res[r_i] = `.` + r_i++ + dot_p = false + } + } + for b[i] != 0 { + res[r_i] = b[i] + r_i++ + i++ + } + } + + // no more digits needed, stop here + if dec_digit <= 0 { + tmp_res := tos(res.data, dot_res_sp).clone() + res.free() + return tmp_res + } + + // println("r_i-d_pos: ${r_i - d_pos}") + if dot_res_sp >= 0 { + if (r_i - dot_res_sp) > dec_digit { + r_i = dot_res_sp + dec_digit + 1 + } + res[r_i] = 0 + // println("result: [${tos(&res[0],r_i)}]") + tmp_res := tos(res.data, r_i).clone() + res.free() + return tmp_res + } else { + if dec_digit > 0 { + mut c1 := 0 + res[r_i] = `.` + r_i++ + for c1 < dec_digit { + res[r_i] = `0` + r_i++ + c1++ + } + res[r_i] = 0 + } + tmp_res := tos(res.data, r_i).clone() + res.free() + return tmp_res + } + } +} + +// strings.Builder version of format_fl +[manualfree] +pub fn format_fl(f f64, p BF_param) string { + unsafe { + mut s := '' + // mut fs := "1.2343" + mut fs := f64_to_str_lnd1(if f >= 0.0 { f } else { -f }, p.len1) + // println("Dario") + // println(fs) + + // error!! + if fs[0] == `[` { + s.free() + return fs + } + + if p.rm_tail_zero { + tmp := fs + fs = remove_tail_zeros(fs) + tmp.free() + } + mut res := strings.new_builder(if p.len0 > fs.len { p.len0 } else { fs.len }) + + mut sign_len_diff := 0 + if p.pad_ch == `0` { + if p.positive { + if p.sign_flag { + res.write_b(`+`) + sign_len_diff = -1 + } + } else { + res.write_b(`-`) + sign_len_diff = -1 + } + tmp := s + s = fs.clone() + tmp.free() + } else { + if p.positive { + if p.sign_flag { + tmp := s + s = '+' + fs + tmp.free() + } else { + tmp := s + s = fs.clone() + tmp.free() + } + } else { + tmp := s + s = '-' + fs + tmp.free() + } + } + + dif := p.len0 - s.len + sign_len_diff + + if p.allign == .right { + for i1 := 0; i1 < dif; i1++ { + res.write_b(p.pad_ch) + } + } + res.write_string(s) + if p.allign == .left { + for i1 := 0; i1 < dif; i1++ { + res.write_b(p.pad_ch) + } + } + + s.free() + fs.free() + tmp_res := res.str() + res.free() + return tmp_res + } +} + +[manualfree] +pub fn format_es(f f64, p BF_param) string { + unsafe { + mut s := '' + mut fs := f64_to_str_pad(if f > 0 { f } else { -f }, p.len1) + if p.rm_tail_zero { + fs = remove_tail_zeros(fs) + } + mut res := strings.new_builder(if p.len0 > fs.len { p.len0 } else { fs.len }) + + mut sign_len_diff := 0 + if p.pad_ch == `0` { + if p.positive { + if p.sign_flag { + res.write_b(`+`) + sign_len_diff = -1 + } + } else { + res.write_b(`-`) + sign_len_diff = -1 + } + tmp := s + s = fs.clone() + tmp.free() + } else { + if p.positive { + if p.sign_flag { + tmp := s + s = '+' + fs + tmp.free() + } else { + tmp := s + s = fs.clone() + tmp.free() + } + } else { + tmp := s + s = '-' + fs + tmp.free() + } + } + + dif := p.len0 - s.len + sign_len_diff + if p.allign == .right { + for i1 := 0; i1 < dif; i1++ { + res.write_b(p.pad_ch) + } + } + res.write_string(s) + if p.allign == .left { + for i1 := 0; i1 < dif; i1++ { + res.write_b(p.pad_ch) + } + } + s.free() + fs.free() + tmp_res := res.str() + res.free() + return tmp_res + } +} + +[direct_array_access] +pub fn remove_tail_zeros(s string) string { + unsafe { + mut buf := malloc_noscan(s.len + 1) + mut i_d := 0 + mut i_s := 0 + + // skip spaces + for i_s < s.len && s[i_s] !in [`-`, `+`] && (s[i_s] > `9` || s[i_s] < `0`) { + buf[i_d] = s[i_s] + i_s++ + i_d++ + } + // sign + if i_s < s.len && s[i_s] in [`-`, `+`] { + buf[i_d] = s[i_s] + i_s++ + i_d++ + } + + // integer part + for i_s < s.len && s[i_s] >= `0` && s[i_s] <= `9` { + buf[i_d] = s[i_s] + i_s++ + i_d++ + } + + // check decimals + if i_s < s.len && s[i_s] == `.` { + mut i_s1 := i_s + 1 + mut sum := 0 + for i_s1 < s.len && s[i_s1] >= `0` && s[i_s1] <= `9` { + sum += s[i_s1] - byte(`0`) + i_s1++ + } + // decimal part must be copied + if sum > 0 { + for c_i in i_s .. i_s1 { + buf[i_d] = s[c_i] + i_d++ + } + } + i_s = i_s1 + } + + if i_s < s.len && s[i_s] != `.` { + // check exponent + for { + buf[i_d] = s[i_s] + i_s++ + i_d++ + if i_s >= s.len { + break + } + } + } + + buf[i_d] = 0 + return tos(buf, i_d) + } +} diff --git a/v_windows/v/old/vlib/strconv/format_test.v b/v_windows/v/old/vlib/strconv/format_test.v new file mode 100644 index 0000000..745318b --- /dev/null +++ b/v_windows/v/old/vlib/strconv/format_test.v @@ -0,0 +1,110 @@ +import strconv + +fn test_format() { + mut temp_s := '' + mut tmp_str := '' + a0 := u32(10) + b0 := 200 + c0 := byte(12) + s0 := 'ciAo' + ch0 := `B` + f0 := 0.312345 + f1 := 200000.0 + f2 := -1234.300e6 + f3 := 1234.300e-6 + + sc0 := 'ciao: [%-08u] %d %hhd [%8s] [%08X] [%-20.4f] [%-20.4f] [%c]' + temp_s = strconv.v_sprintf(sc0, a0, b0, c0, s0, b0, f0, f1, ch0) + tmp_str = 'ciao: [10 ] 200 12 [ ciAo] [000000C8] [0.3123 ] [200000.0000 ] [B]' + // C.printf(sc0.str,a0 ,b0 ,c0 ,s0.str ,b0 ,f0, f1, ch0) + // println("\n$temp_s") + assert tmp_str == temp_s + + a := byte(12) + b := i16(13) + c := 14 + d := i64(15) + sc1 := '==>%hhd %hd %d %ld' + temp_s = strconv.v_sprintf(sc1, a, b, c, d) + tmp_str = '==>12 13 14 15' + // C.printf(sc1.str, a ,b ,c, d) + // println("\n$temp_s") + assert tmp_str == temp_s + + a1 := byte(0xff) + b1 := i16(0xffff) + c1 := u32(0xffff_ffff) + d1 := u64(-1) + sc2 := '%hhu %hu %u %lu' + temp_s = strconv.v_sprintf(sc2, a1, b1, c1, d1) + tmp_str = '255 65535 4294967295 18446744073709551615' + // C.printf(sc2.str, a1 ,b1 ,c1, d1) + // println("\n$temp_s") + assert tmp_str == temp_s + + sc3 := '%hhx %hx %x %lx' + temp_s = strconv.v_sprintf(sc3, a1, b1, c1, d1) + tmp_str = 'ff ffff ffffffff ffffffffffffffff' + // C.printf(sc3.str, a1 ,b1 ,c1, d1) + // println("\n$temp_s") + assert tmp_str == temp_s + + sc4 := '[%-20.3e] [%20.3e] [%-020.3e] [%-020.3E] [%-020.3e] [%-020.3e]' + temp_s = strconv.v_sprintf(sc4, f0, f1, f1, f1, f2, f3) + tmp_str = '[3.123e-01 ] [ 2.000e+05] [2.000e+05 ] [2.000E+05 ] [-1.234e+09 ] [1.234e-03 ]' + // C.printf(sc4.str, f0, f1, f1, f1, f2, f3) + // println("\n$temp_s") + assert tmp_str == temp_s + + sc5 := '[%.3f] [%0.3f] [%0.3F] [%0.3f] [%0.3F]' + temp_s = strconv.v_sprintf(sc5, f0, f1, f1, f2, f3) + tmp_str = '[0.312] [200000.000] [200000.000] [-1234300000.000] [0.001]' + // C.printf(sc5.str, f0, f1, f1, f2, f3, f3) + // println("\n$temp_s") + assert tmp_str == temp_s + + ml := 3 + sc6 := '%.*s [%05hhX]' + temp_s = strconv.v_sprintf(sc6, ml, s0, a) + tmp_str = 'ciA [0000C]' + // C.printf(sc6.str, ml, s0.str, a) + // println("\n$temp_s") + assert tmp_str == temp_s + + a2 := 125 + sc7 := '[%9x] [%9X] [%-9x] [%-9X] [%09x] [%09X]' + temp_s = strconv.v_sprintf(sc7, a2, a2, a2, a2, a2, a2) + tmp_str = '[ 7d] [ 7D] [7d ] [7D ] [00000007d] [00000007D]' + // C.printf(sc7.str, a2, a2, a2, a2, a2, a2) + // println("\n$temp_s") + assert tmp_str == temp_s + + g_test := [ + '[ -1e-07][ -1E-07]|', + '[ -1e-06][ -1E-06]|', + '[ -1e-05][ -1E-05]|', + '[ -0.0001][ -0.0001]|', + '[ -0.001][ -0.001]|', + '[ -0.01][ -0.01]|', + '[ -0.1][ -0.1]|', + '[ -1][ -1]|', + '[ -10][ -10]|', + '[ -100][ -100]|', + '[ -1000][ -1000]|', + '[ -10000][ -10000]|', + ] + + mut ft := -1e-7 + mut x := 0 + mut cnt := 0 + sc8 := '[%20g][%20G]|' + for x < 12 { + temp_s = strconv.v_sprintf(sc8, ft, ft) + // C.printf(sc8.str, ft, ft) + // println("\n$temp_s") + assert temp_s == g_test[cnt] + ft = ft * 10.0 + x++ + cnt++ + } +} diff --git a/v_windows/v/old/vlib/strconv/ftoa.v b/v_windows/v/old/vlib/strconv/ftoa.v new file mode 100644 index 0000000..700ecc7 --- /dev/null +++ b/v_windows/v/old/vlib/strconv/ftoa.v @@ -0,0 +1,39 @@ +module strconv + +/* +f32/f64 ftoa functions + +Copyright (c) 2019-2021 Dario Deledda. All rights reserved. +Use of this source code is governed by an MIT license +that can be found in the LICENSE file. + +This file contains the f32/f64 ftoa functions + +These functions are based on the work of: +Publication:PLDI 2018: Proceedings of the 39th ACM SIGPLAN +Conference on Programming Language Design and ImplementationJune 2018 +Pages 270–282 https://doi.org/10.1145/3192366.3192369 + +inspired by the Go version here: +https://github.com/cespare/ryu/tree/ba56a33f39e3bbbfa409095d0f9ae168a595feea +*/ + +[inline] +pub fn ftoa_64(f f64) string { + return f64_to_str(f, 17) +} + +[inline] +pub fn ftoa_long_64(f f64) string { + return f64_to_str_l(f) +} + +[inline] +pub fn ftoa_32(f f32) string { + return f32_to_str(f, 8) +} + +[inline] +pub fn ftoa_long_32(f f32) string { + return f32_to_str_l(f) +} diff --git a/v_windows/v/old/vlib/strconv/number_to_base.v b/v_windows/v/old/vlib/strconv/number_to_base.v new file mode 100644 index 0000000..0ca7e9c --- /dev/null +++ b/v_windows/v/old/vlib/strconv/number_to_base.v @@ -0,0 +1,61 @@ +module strconv + +const base_digits = '0123456789abcdefghijklmnopqrstuvwxyz' + +// format_int returns the string representation of the number n in base `radix` +// for digit values > 10, this function uses the small latin leters a-z. +[manualfree] +pub fn format_int(n i64, radix int) string { + unsafe { + if radix < 2 || radix > 36 { + panic('invalid radix: $radix . It should be => 2 and <= 36') + } + if n == 0 { + return '0' + } + mut n_copy := n + mut sign := '' + if n < 0 { + sign = '-' + n_copy = -n_copy + } + mut res := '' + for n_copy != 0 { + tmp_0 := res + tmp_1 := strconv.base_digits[n_copy % radix].ascii_str() + res = tmp_1 + res + tmp_0.free() + tmp_1.free() + // res = base_digits[n_copy % radix].ascii_str() + res + n_copy /= radix + } + return '$sign$res' + } +} + +// format_uint returns the string representation of the number n in base `radix` +// for digit values > 10, this function uses the small latin leters a-z. +[manualfree] +pub fn format_uint(n u64, radix int) string { + unsafe { + if radix < 2 || radix > 36 { + panic('invalid radix: $radix . It should be => 2 and <= 36') + } + if n == 0 { + return '0' + } + mut n_copy := n + mut res := '' + uradix := u64(radix) + for n_copy != 0 { + tmp_0 := res + tmp_1 := strconv.base_digits[n_copy % uradix].ascii_str() + res = tmp_1 + res + tmp_0.free() + tmp_1.free() + // res = base_digits[n_copy % uradix].ascii_str() + res + n_copy /= uradix + } + return res + } +} diff --git a/v_windows/v/old/vlib/strconv/number_to_base_test.v b/v_windows/v/old/vlib/strconv/number_to_base_test.v new file mode 100644 index 0000000..b313b0f --- /dev/null +++ b/v_windows/v/old/vlib/strconv/number_to_base_test.v @@ -0,0 +1,38 @@ +import strconv + +fn test_format_int() { + assert strconv.format_int(0, 2) == '0' + assert strconv.format_int(0, 10) == '0' + assert strconv.format_int(0, 16) == '0' + assert strconv.format_int(0, 36) == '0' + assert strconv.format_int(1, 2) == '1' + assert strconv.format_int(1, 10) == '1' + assert strconv.format_int(1, 16) == '1' + assert strconv.format_int(1, 36) == '1' + assert strconv.format_int(-1, 2) == '-1' + assert strconv.format_int(-1, 10) == '-1' + assert strconv.format_int(-1, 16) == '-1' + assert strconv.format_int(-1, 36) == '-1' + assert strconv.format_int(255, 2) == '11111111' + assert strconv.format_int(255, 8) == '377' + assert strconv.format_int(255, 10) == '255' + assert strconv.format_int(255, 16) == 'ff' + assert strconv.format_int(-255, 2) == '-11111111' + assert strconv.format_int(-255, 8) == '-377' + assert strconv.format_int(-255, 10) == '-255' + assert strconv.format_int(-255, 16) == '-ff' + for i in -256 .. 256 { + assert strconv.format_int(i, 10) == i.str() + } +} + +fn test_format_uint() { + assert strconv.format_uint(0, 2) == '0' + assert strconv.format_int(255, 2) == '11111111' + assert strconv.format_int(255, 8) == '377' + assert strconv.format_int(255, 10) == '255' + assert strconv.format_int(255, 16) == 'ff' + assert strconv.format_uint(18446744073709551615, 2) == '1111111111111111111111111111111111111111111111111111111111111111' + assert strconv.format_uint(18446744073709551615, 16) == 'ffffffffffffffff' + assert strconv.format_uint(683058467, 36) == 'baobab' +} diff --git a/v_windows/v/old/vlib/strconv/structs.v b/v_windows/v/old/vlib/strconv/structs.v new file mode 100644 index 0000000..35ade80 --- /dev/null +++ b/v_windows/v/old/vlib/strconv/structs.v @@ -0,0 +1,55 @@ +module strconv + +// The structure is filled by parser, then given to converter. +pub struct PrepNumber { +pub mut: + negative bool // 0 if positive number, 1 if negative + exponent int // power of 10 exponent + mantissa u64 // integer mantissa +} + +// dec32 is a floating decimal type representing m * 10^e. +struct Dec32 { +mut: + m u32 + e int +} + +// dec64 is a floating decimal type representing m * 10^e. +struct Dec64 { +mut: + m u64 + e int +} + +struct Uint128 { +mut: + lo u64 + hi u64 +} + +// support union for convert f32 to u32 +union Uf32 { +mut: + f f32 + u u32 +} + +// support union for convert f64 to u64 +union Uf64 { +mut: + f f64 + u u64 +} + +pub union Float64u { +pub mut: + f f64 + u u64 +} + +pub union Float32u { +pub mut: + f f32 + u u32 +} diff --git a/v_windows/v/old/vlib/strconv/tables.v b/v_windows/v/old/vlib/strconv/tables.v new file mode 100644 index 0000000..4f6d3ac --- /dev/null +++ b/v_windows/v/old/vlib/strconv/tables.v @@ -0,0 +1,738 @@ +module strconv + +const ( + pow5_num_bits_32 = 61 + pow5_inv_num_bits_32 = 59 + pow5_num_bits_64 = 121 + pow5_inv_num_bits_64 = 122 + + powers_of_10 = [ + u64(1e0), + u64(1e1), + u64(1e2), + u64(1e3), + u64(1e4), + u64(1e5), + u64(1e6), + u64(1e7), + u64(1e8), + u64(1e9), + u64(1e10), + u64(1e11), + u64(1e12), + u64(1e13), + u64(1e14), + u64(1e15), + u64(1e16), + u64(1e17) + // We only need to find the length of at most 17 digit numbers. + ] + + pow5_split_32 = [ + u64(1152921504606846976), + u64(1441151880758558720), + u64(1801439850948198400), + u64(2251799813685248000), + u64(1407374883553280000), + u64(1759218604441600000), + u64(2199023255552000000), + u64(1374389534720000000), + u64(1717986918400000000), + u64(2147483648000000000), + u64(1342177280000000000), + u64(1677721600000000000), + u64(2097152000000000000), + u64(1310720000000000000), + u64(1638400000000000000), + u64(2048000000000000000), + u64(1280000000000000000), + u64(1600000000000000000), + u64(2000000000000000000), + u64(1250000000000000000), + u64(1562500000000000000), + u64(1953125000000000000), + u64(1220703125000000000), + u64(1525878906250000000), + u64(1907348632812500000), + u64(1192092895507812500), + u64(1490116119384765625), + u64(1862645149230957031), + u64(1164153218269348144), + u64(1455191522836685180), + u64(1818989403545856475), + u64(2273736754432320594), + u64(1421085471520200371), + u64(1776356839400250464), + u64(2220446049250313080), + u64(1387778780781445675), + u64(1734723475976807094), + u64(2168404344971008868), + u64(1355252715606880542), + u64(1694065894508600678), + u64(2117582368135750847), + u64(1323488980084844279), + u64(1654361225106055349), + u64(2067951531382569187), + u64(1292469707114105741), + u64(1615587133892632177), + u64(2019483917365790221), + ] + + pow5_inv_split_32 = [ + u64(576460752303423489), + u64(461168601842738791), + u64(368934881474191033), + u64(295147905179352826), + u64(472236648286964522), + u64(377789318629571618), + u64(302231454903657294), + u64(483570327845851670), + u64(386856262276681336), + u64(309485009821345069), + u64(495176015714152110), + u64(396140812571321688), + u64(316912650057057351), + u64(507060240091291761), + u64(405648192073033409), + u64(324518553658426727), + u64(519229685853482763), + u64(415383748682786211), + u64(332306998946228969), + u64(531691198313966350), + u64(425352958651173080), + u64(340282366920938464), + u64(544451787073501542), + u64(435561429658801234), + u64(348449143727040987), + u64(557518629963265579), + u64(446014903970612463), + u64(356811923176489971), + u64(570899077082383953), + u64(456719261665907162), + u64(365375409332725730), + ] + + pow5_split_64 = [ + Uint128{u64(0x0000000000000000), u64(0x0100000000000000)}, + Uint128{u64(0x0000000000000000), u64(0x0140000000000000)}, + Uint128{u64(0x0000000000000000), u64(0x0190000000000000)}, + Uint128{u64(0x0000000000000000), u64(0x01f4000000000000)}, + Uint128{u64(0x0000000000000000), u64(0x0138800000000000)}, + Uint128{u64(0x0000000000000000), u64(0x0186a00000000000)}, + Uint128{u64(0x0000000000000000), u64(0x01e8480000000000)}, + Uint128{u64(0x0000000000000000), u64(0x01312d0000000000)}, + Uint128{u64(0x0000000000000000), u64(0x017d784000000000)}, + Uint128{u64(0x0000000000000000), u64(0x01dcd65000000000)}, + Uint128{u64(0x0000000000000000), u64(0x012a05f200000000)}, + Uint128{u64(0x0000000000000000), u64(0x0174876e80000000)}, + Uint128{u64(0x0000000000000000), u64(0x01d1a94a20000000)}, + Uint128{u64(0x0000000000000000), u64(0x012309ce54000000)}, + Uint128{u64(0x0000000000000000), u64(0x016bcc41e9000000)}, + Uint128{u64(0x0000000000000000), u64(0x01c6bf5263400000)}, + Uint128{u64(0x0000000000000000), u64(0x011c37937e080000)}, + Uint128{u64(0x0000000000000000), u64(0x016345785d8a0000)}, + Uint128{u64(0x0000000000000000), u64(0x01bc16d674ec8000)}, + Uint128{u64(0x0000000000000000), u64(0x01158e460913d000)}, + Uint128{u64(0x0000000000000000), u64(0x015af1d78b58c400)}, + Uint128{u64(0x0000000000000000), u64(0x01b1ae4d6e2ef500)}, + Uint128{u64(0x0000000000000000), u64(0x010f0cf064dd5920)}, + Uint128{u64(0x0000000000000000), u64(0x0152d02c7e14af68)}, + Uint128{u64(0x0000000000000000), u64(0x01a784379d99db42)}, + Uint128{u64(0x4000000000000000), u64(0x0108b2a2c2802909)}, + Uint128{u64(0x9000000000000000), u64(0x014adf4b7320334b)}, + Uint128{u64(0x7400000000000000), u64(0x019d971e4fe8401e)}, + Uint128{u64(0x0880000000000000), u64(0x01027e72f1f12813)}, + Uint128{u64(0xcaa0000000000000), u64(0x01431e0fae6d7217)}, + Uint128{u64(0xbd48000000000000), u64(0x0193e5939a08ce9d)}, + Uint128{u64(0x2c9a000000000000), u64(0x01f8def8808b0245)}, + Uint128{u64(0x3be0400000000000), u64(0x013b8b5b5056e16b)}, + Uint128{u64(0x0ad8500000000000), u64(0x018a6e32246c99c6)}, + Uint128{u64(0x8d8e640000000000), u64(0x01ed09bead87c037)}, + Uint128{u64(0xb878fe8000000000), u64(0x013426172c74d822)}, + Uint128{u64(0x66973e2000000000), u64(0x01812f9cf7920e2b)}, + Uint128{u64(0x403d0da800000000), u64(0x01e17b84357691b6)}, + Uint128{u64(0xe826288900000000), u64(0x012ced32a16a1b11)}, + Uint128{u64(0x622fb2ab40000000), u64(0x0178287f49c4a1d6)}, + Uint128{u64(0xfabb9f5610000000), u64(0x01d6329f1c35ca4b)}, + Uint128{u64(0x7cb54395ca000000), u64(0x0125dfa371a19e6f)}, + Uint128{u64(0x5be2947b3c800000), u64(0x016f578c4e0a060b)}, + Uint128{u64(0x32db399a0ba00000), u64(0x01cb2d6f618c878e)}, + Uint128{u64(0xdfc9040047440000), u64(0x011efc659cf7d4b8)}, + Uint128{u64(0x17bb450059150000), u64(0x0166bb7f0435c9e7)}, + Uint128{u64(0xddaa16406f5a4000), u64(0x01c06a5ec5433c60)}, + Uint128{u64(0x8a8a4de845986800), u64(0x0118427b3b4a05bc)}, + Uint128{u64(0xad2ce16256fe8200), u64(0x015e531a0a1c872b)}, + Uint128{u64(0x987819baecbe2280), u64(0x01b5e7e08ca3a8f6)}, + Uint128{u64(0x1f4b1014d3f6d590), u64(0x0111b0ec57e6499a)}, + Uint128{u64(0xa71dd41a08f48af4), u64(0x01561d276ddfdc00)}, + Uint128{u64(0xd0e549208b31adb1), u64(0x01aba4714957d300)}, + Uint128{u64(0x828f4db456ff0c8e), u64(0x010b46c6cdd6e3e0)}, + Uint128{u64(0xa33321216cbecfb2), u64(0x014e1878814c9cd8)}, + Uint128{u64(0xcbffe969c7ee839e), u64(0x01a19e96a19fc40e)}, + Uint128{u64(0x3f7ff1e21cf51243), u64(0x0105031e2503da89)}, + Uint128{u64(0x8f5fee5aa43256d4), u64(0x014643e5ae44d12b)}, + Uint128{u64(0x7337e9f14d3eec89), u64(0x0197d4df19d60576)}, + Uint128{u64(0x1005e46da08ea7ab), u64(0x01fdca16e04b86d4)}, + Uint128{u64(0x8a03aec4845928cb), u64(0x013e9e4e4c2f3444)}, + Uint128{u64(0xac849a75a56f72fd), u64(0x018e45e1df3b0155)}, + Uint128{u64(0x17a5c1130ecb4fbd), u64(0x01f1d75a5709c1ab)}, + Uint128{u64(0xeec798abe93f11d6), u64(0x013726987666190a)}, + Uint128{u64(0xaa797ed6e38ed64b), u64(0x0184f03e93ff9f4d)}, + Uint128{u64(0x1517de8c9c728bde), u64(0x01e62c4e38ff8721)}, + Uint128{u64(0xad2eeb17e1c7976b), u64(0x012fdbb0e39fb474)}, + Uint128{u64(0xd87aa5ddda397d46), u64(0x017bd29d1c87a191)}, + Uint128{u64(0x4e994f5550c7dc97), u64(0x01dac74463a989f6)}, + Uint128{u64(0xf11fd195527ce9de), u64(0x0128bc8abe49f639)}, + Uint128{u64(0x6d67c5faa71c2456), u64(0x0172ebad6ddc73c8)}, + Uint128{u64(0x88c1b77950e32d6c), u64(0x01cfa698c95390ba)}, + Uint128{u64(0x957912abd28dfc63), u64(0x0121c81f7dd43a74)}, + Uint128{u64(0xbad75756c7317b7c), u64(0x016a3a275d494911)}, + Uint128{u64(0x298d2d2c78fdda5b), u64(0x01c4c8b1349b9b56)}, + Uint128{u64(0xd9f83c3bcb9ea879), u64(0x011afd6ec0e14115)}, + Uint128{u64(0x50764b4abe865297), u64(0x0161bcca7119915b)}, + Uint128{u64(0x2493de1d6e27e73d), u64(0x01ba2bfd0d5ff5b2)}, + Uint128{u64(0x56dc6ad264d8f086), u64(0x01145b7e285bf98f)}, + Uint128{u64(0x2c938586fe0f2ca8), u64(0x0159725db272f7f3)}, + Uint128{u64(0xf7b866e8bd92f7d2), u64(0x01afcef51f0fb5ef)}, + Uint128{u64(0xfad34051767bdae3), u64(0x010de1593369d1b5)}, + Uint128{u64(0x79881065d41ad19c), u64(0x015159af80444623)}, + Uint128{u64(0x57ea147f49218603), u64(0x01a5b01b605557ac)}, + Uint128{u64(0xb6f24ccf8db4f3c1), u64(0x01078e111c3556cb)}, + Uint128{u64(0xa4aee003712230b2), u64(0x014971956342ac7e)}, + Uint128{u64(0x4dda98044d6abcdf), u64(0x019bcdfabc13579e)}, + Uint128{u64(0xf0a89f02b062b60b), u64(0x010160bcb58c16c2)}, + Uint128{u64(0xacd2c6c35c7b638e), u64(0x0141b8ebe2ef1c73)}, + Uint128{u64(0x98077874339a3c71), u64(0x01922726dbaae390)}, + Uint128{u64(0xbe0956914080cb8e), u64(0x01f6b0f092959c74)}, + Uint128{u64(0xf6c5d61ac8507f38), u64(0x013a2e965b9d81c8)}, + Uint128{u64(0x34774ba17a649f07), u64(0x0188ba3bf284e23b)}, + Uint128{u64(0x01951e89d8fdc6c8), u64(0x01eae8caef261aca)}, + Uint128{u64(0x40fd3316279e9c3d), u64(0x0132d17ed577d0be)}, + Uint128{u64(0xd13c7fdbb186434c), u64(0x017f85de8ad5c4ed)}, + Uint128{u64(0x458b9fd29de7d420), u64(0x01df67562d8b3629)}, + Uint128{u64(0xcb7743e3a2b0e494), u64(0x012ba095dc7701d9)}, + Uint128{u64(0x3e5514dc8b5d1db9), u64(0x017688bb5394c250)}, + Uint128{u64(0x4dea5a13ae346527), u64(0x01d42aea2879f2e4)}, + Uint128{u64(0xb0b2784c4ce0bf38), u64(0x01249ad2594c37ce)}, + Uint128{u64(0x5cdf165f6018ef06), u64(0x016dc186ef9f45c2)}, + Uint128{u64(0xf416dbf7381f2ac8), u64(0x01c931e8ab871732)}, + Uint128{u64(0xd88e497a83137abd), u64(0x011dbf316b346e7f)}, + Uint128{u64(0xceb1dbd923d8596c), u64(0x01652efdc6018a1f)}, + Uint128{u64(0xc25e52cf6cce6fc7), u64(0x01be7abd3781eca7)}, + Uint128{u64(0xd97af3c1a40105dc), u64(0x01170cb642b133e8)}, + Uint128{u64(0x0fd9b0b20d014754), u64(0x015ccfe3d35d80e3)}, + Uint128{u64(0xd3d01cde90419929), u64(0x01b403dcc834e11b)}, + Uint128{u64(0x6462120b1a28ffb9), u64(0x01108269fd210cb1)}, + Uint128{u64(0xbd7a968de0b33fa8), u64(0x0154a3047c694fdd)}, + Uint128{u64(0x2cd93c3158e00f92), u64(0x01a9cbc59b83a3d5)}, + Uint128{u64(0x3c07c59ed78c09bb), u64(0x010a1f5b81324665)}, + Uint128{u64(0x8b09b7068d6f0c2a), u64(0x014ca732617ed7fe)}, + Uint128{u64(0x2dcc24c830cacf34), u64(0x019fd0fef9de8dfe)}, + Uint128{u64(0xdc9f96fd1e7ec180), u64(0x0103e29f5c2b18be)}, + Uint128{u64(0x93c77cbc661e71e1), u64(0x0144db473335deee)}, + Uint128{u64(0x38b95beb7fa60e59), u64(0x01961219000356aa)}, + Uint128{u64(0xc6e7b2e65f8f91ef), u64(0x01fb969f40042c54)}, + Uint128{u64(0xfc50cfcffbb9bb35), u64(0x013d3e2388029bb4)}, + Uint128{u64(0x3b6503c3faa82a03), u64(0x018c8dac6a0342a2)}, + Uint128{u64(0xca3e44b4f9523484), u64(0x01efb1178484134a)}, + Uint128{u64(0xbe66eaf11bd360d2), u64(0x0135ceaeb2d28c0e)}, + Uint128{u64(0x6e00a5ad62c83907), u64(0x0183425a5f872f12)}, + Uint128{u64(0x0980cf18bb7a4749), u64(0x01e412f0f768fad7)}, + Uint128{u64(0x65f0816f752c6c8d), u64(0x012e8bd69aa19cc6)}, + Uint128{u64(0xff6ca1cb527787b1), u64(0x017a2ecc414a03f7)}, + Uint128{u64(0xff47ca3e2715699d), u64(0x01d8ba7f519c84f5)}, + Uint128{u64(0xbf8cde66d86d6202), u64(0x0127748f9301d319)}, + Uint128{u64(0x2f7016008e88ba83), u64(0x017151b377c247e0)}, + Uint128{u64(0x3b4c1b80b22ae923), u64(0x01cda62055b2d9d8)}, + Uint128{u64(0x250f91306f5ad1b6), u64(0x012087d4358fc827)}, + Uint128{u64(0xee53757c8b318623), u64(0x0168a9c942f3ba30)}, + Uint128{u64(0x29e852dbadfde7ac), u64(0x01c2d43b93b0a8bd)}, + Uint128{u64(0x3a3133c94cbeb0cc), u64(0x0119c4a53c4e6976)}, + Uint128{u64(0xc8bd80bb9fee5cff), u64(0x016035ce8b6203d3)}, + Uint128{u64(0xbaece0ea87e9f43e), u64(0x01b843422e3a84c8)}, + Uint128{u64(0x74d40c9294f238a7), u64(0x01132a095ce492fd)}, + Uint128{u64(0xd2090fb73a2ec6d1), u64(0x0157f48bb41db7bc)}, + Uint128{u64(0x068b53a508ba7885), u64(0x01adf1aea12525ac)}, + Uint128{u64(0x8417144725748b53), u64(0x010cb70d24b7378b)}, + Uint128{u64(0x651cd958eed1ae28), u64(0x014fe4d06de5056e)}, + Uint128{u64(0xfe640faf2a8619b2), u64(0x01a3de04895e46c9)}, + Uint128{u64(0x3efe89cd7a93d00f), u64(0x01066ac2d5daec3e)}, + Uint128{u64(0xcebe2c40d938c413), u64(0x014805738b51a74d)}, + Uint128{u64(0x426db7510f86f518), u64(0x019a06d06e261121)}, + Uint128{u64(0xc9849292a9b4592f), u64(0x0100444244d7cab4)}, + Uint128{u64(0xfbe5b73754216f7a), u64(0x01405552d60dbd61)}, + Uint128{u64(0x7adf25052929cb59), u64(0x01906aa78b912cba)}, + Uint128{u64(0x1996ee4673743e2f), u64(0x01f485516e7577e9)}, + Uint128{u64(0xaffe54ec0828a6dd), u64(0x0138d352e5096af1)}, + Uint128{u64(0x1bfdea270a32d095), u64(0x018708279e4bc5ae)}, + Uint128{u64(0xa2fd64b0ccbf84ba), u64(0x01e8ca3185deb719)}, + Uint128{u64(0x05de5eee7ff7b2f4), u64(0x01317e5ef3ab3270)}, + Uint128{u64(0x0755f6aa1ff59fb1), u64(0x017dddf6b095ff0c)}, + Uint128{u64(0x092b7454a7f3079e), u64(0x01dd55745cbb7ecf)}, + Uint128{u64(0x65bb28b4e8f7e4c3), u64(0x012a5568b9f52f41)}, + Uint128{u64(0xbf29f2e22335ddf3), u64(0x0174eac2e8727b11)}, + Uint128{u64(0x2ef46f9aac035570), u64(0x01d22573a28f19d6)}, + Uint128{u64(0xdd58c5c0ab821566), u64(0x0123576845997025)}, + Uint128{u64(0x54aef730d6629ac0), u64(0x016c2d4256ffcc2f)}, + Uint128{u64(0x29dab4fd0bfb4170), u64(0x01c73892ecbfbf3b)}, + Uint128{u64(0xfa28b11e277d08e6), u64(0x011c835bd3f7d784)}, + Uint128{u64(0x38b2dd65b15c4b1f), u64(0x0163a432c8f5cd66)}, + Uint128{u64(0xc6df94bf1db35de7), u64(0x01bc8d3f7b3340bf)}, + Uint128{u64(0xdc4bbcf772901ab0), u64(0x0115d847ad000877)}, + Uint128{u64(0xd35eac354f34215c), u64(0x015b4e5998400a95)}, + Uint128{u64(0x48365742a30129b4), u64(0x01b221effe500d3b)}, + Uint128{u64(0x0d21f689a5e0ba10), u64(0x010f5535fef20845)}, + Uint128{u64(0x506a742c0f58e894), u64(0x01532a837eae8a56)}, + Uint128{u64(0xe4851137132f22b9), u64(0x01a7f5245e5a2ceb)}, + Uint128{u64(0x6ed32ac26bfd75b4), u64(0x0108f936baf85c13)}, + Uint128{u64(0x4a87f57306fcd321), u64(0x014b378469b67318)}, + Uint128{u64(0x5d29f2cfc8bc07e9), u64(0x019e056584240fde)}, + Uint128{u64(0xfa3a37c1dd7584f1), u64(0x0102c35f729689ea)}, + Uint128{u64(0xb8c8c5b254d2e62e), u64(0x014374374f3c2c65)}, + Uint128{u64(0x26faf71eea079fb9), u64(0x01945145230b377f)}, + Uint128{u64(0xf0b9b4e6a48987a8), u64(0x01f965966bce055e)}, + Uint128{u64(0x5674111026d5f4c9), u64(0x013bdf7e0360c35b)}, + Uint128{u64(0x2c111554308b71fb), u64(0x018ad75d8438f432)}, + Uint128{u64(0xb7155aa93cae4e7a), u64(0x01ed8d34e547313e)}, + Uint128{u64(0x326d58a9c5ecf10c), u64(0x013478410f4c7ec7)}, + Uint128{u64(0xff08aed437682d4f), u64(0x01819651531f9e78)}, + Uint128{u64(0x3ecada89454238a3), u64(0x01e1fbe5a7e78617)}, + Uint128{u64(0x873ec895cb496366), u64(0x012d3d6f88f0b3ce)}, + Uint128{u64(0x290e7abb3e1bbc3f), u64(0x01788ccb6b2ce0c2)}, + Uint128{u64(0xb352196a0da2ab4f), u64(0x01d6affe45f818f2)}, + Uint128{u64(0xb0134fe24885ab11), u64(0x01262dfeebbb0f97)}, + Uint128{u64(0x9c1823dadaa715d6), u64(0x016fb97ea6a9d37d)}, + Uint128{u64(0x031e2cd19150db4b), u64(0x01cba7de5054485d)}, + Uint128{u64(0x21f2dc02fad2890f), u64(0x011f48eaf234ad3a)}, + Uint128{u64(0xaa6f9303b9872b53), u64(0x01671b25aec1d888)}, + Uint128{u64(0xd50b77c4a7e8f628), u64(0x01c0e1ef1a724eaa)}, + Uint128{u64(0xc5272adae8f199d9), u64(0x01188d357087712a)}, + Uint128{u64(0x7670f591a32e004f), u64(0x015eb082cca94d75)}, + Uint128{u64(0xd40d32f60bf98063), u64(0x01b65ca37fd3a0d2)}, + Uint128{u64(0xc4883fd9c77bf03e), u64(0x0111f9e62fe44483)}, + Uint128{u64(0xb5aa4fd0395aec4d), u64(0x0156785fbbdd55a4)}, + Uint128{u64(0xe314e3c447b1a760), u64(0x01ac1677aad4ab0d)}, + Uint128{u64(0xaded0e5aaccf089c), u64(0x010b8e0acac4eae8)}, + Uint128{u64(0xd96851f15802cac3), u64(0x014e718d7d7625a2)}, + Uint128{u64(0x8fc2666dae037d74), u64(0x01a20df0dcd3af0b)}, + Uint128{u64(0x39d980048cc22e68), u64(0x010548b68a044d67)}, + Uint128{u64(0x084fe005aff2ba03), u64(0x01469ae42c8560c1)}, + Uint128{u64(0x4a63d8071bef6883), u64(0x0198419d37a6b8f1)}, + Uint128{u64(0x9cfcce08e2eb42a4), u64(0x01fe52048590672d)}, + Uint128{u64(0x821e00c58dd309a7), u64(0x013ef342d37a407c)}, + Uint128{u64(0xa2a580f6f147cc10), u64(0x018eb0138858d09b)}, + Uint128{u64(0x8b4ee134ad99bf15), u64(0x01f25c186a6f04c2)}, + Uint128{u64(0x97114cc0ec80176d), u64(0x0137798f428562f9)}, + Uint128{u64(0xfcd59ff127a01d48), u64(0x018557f31326bbb7)}, + Uint128{u64(0xfc0b07ed7188249a), u64(0x01e6adefd7f06aa5)}, + Uint128{u64(0xbd86e4f466f516e0), u64(0x01302cb5e6f642a7)}, + Uint128{u64(0xace89e3180b25c98), u64(0x017c37e360b3d351)}, + Uint128{u64(0x1822c5bde0def3be), u64(0x01db45dc38e0c826)}, + Uint128{u64(0xcf15bb96ac8b5857), u64(0x01290ba9a38c7d17)}, + Uint128{u64(0xc2db2a7c57ae2e6d), u64(0x01734e940c6f9c5d)}, + Uint128{u64(0x3391f51b6d99ba08), u64(0x01d022390f8b8375)}, + Uint128{u64(0x403b393124801445), u64(0x01221563a9b73229)}, + Uint128{u64(0x904a077d6da01956), u64(0x016a9abc9424feb3)}, + Uint128{u64(0x745c895cc9081fac), u64(0x01c5416bb92e3e60)}, + Uint128{u64(0x48b9d5d9fda513cb), u64(0x011b48e353bce6fc)}, + Uint128{u64(0x5ae84b507d0e58be), u64(0x01621b1c28ac20bb)}, + Uint128{u64(0x31a25e249c51eeee), u64(0x01baa1e332d728ea)}, + Uint128{u64(0x5f057ad6e1b33554), u64(0x0114a52dffc67992)}, + Uint128{u64(0xf6c6d98c9a2002aa), u64(0x0159ce797fb817f6)}, + Uint128{u64(0xb4788fefc0a80354), u64(0x01b04217dfa61df4)}, + Uint128{u64(0xf0cb59f5d8690214), u64(0x010e294eebc7d2b8)}, + Uint128{u64(0x2cfe30734e83429a), u64(0x0151b3a2a6b9c767)}, + Uint128{u64(0xf83dbc9022241340), u64(0x01a6208b50683940)}, + Uint128{u64(0x9b2695da15568c08), u64(0x0107d457124123c8)}, + Uint128{u64(0xc1f03b509aac2f0a), u64(0x0149c96cd6d16cba)}, + Uint128{u64(0x726c4a24c1573acd), u64(0x019c3bc80c85c7e9)}, + Uint128{u64(0xe783ae56f8d684c0), u64(0x0101a55d07d39cf1)}, + Uint128{u64(0x616499ecb70c25f0), u64(0x01420eb449c8842e)}, + Uint128{u64(0xf9bdc067e4cf2f6c), u64(0x019292615c3aa539)}, + Uint128{u64(0x782d3081de02fb47), u64(0x01f736f9b3494e88)}, + Uint128{u64(0x4b1c3e512ac1dd0c), u64(0x013a825c100dd115)}, + Uint128{u64(0x9de34de57572544f), u64(0x018922f31411455a)}, + Uint128{u64(0x455c215ed2cee963), u64(0x01eb6bafd91596b1)}, + Uint128{u64(0xcb5994db43c151de), u64(0x0133234de7ad7e2e)}, + Uint128{u64(0x7e2ffa1214b1a655), u64(0x017fec216198ddba)}, + Uint128{u64(0x1dbbf89699de0feb), u64(0x01dfe729b9ff1529)}, + Uint128{u64(0xb2957b5e202ac9f3), u64(0x012bf07a143f6d39)}, + Uint128{u64(0x1f3ada35a8357c6f), u64(0x0176ec98994f4888)}, + Uint128{u64(0x270990c31242db8b), u64(0x01d4a7bebfa31aaa)}, + Uint128{u64(0x5865fa79eb69c937), u64(0x0124e8d737c5f0aa)}, + Uint128{u64(0xee7f791866443b85), u64(0x016e230d05b76cd4)}, + Uint128{u64(0x2a1f575e7fd54a66), u64(0x01c9abd04725480a)}, + Uint128{u64(0x5a53969b0fe54e80), u64(0x011e0b622c774d06)}, + Uint128{u64(0xf0e87c41d3dea220), u64(0x01658e3ab7952047)}, + Uint128{u64(0xed229b5248d64aa8), u64(0x01bef1c9657a6859)}, + Uint128{u64(0x3435a1136d85eea9), u64(0x0117571ddf6c8138)}, + Uint128{u64(0x4143095848e76a53), u64(0x015d2ce55747a186)}, + Uint128{u64(0xd193cbae5b2144e8), u64(0x01b4781ead1989e7)}, + Uint128{u64(0xe2fc5f4cf8f4cb11), u64(0x0110cb132c2ff630)}, + Uint128{u64(0x1bbb77203731fdd5), u64(0x0154fdd7f73bf3bd)}, + Uint128{u64(0x62aa54e844fe7d4a), u64(0x01aa3d4df50af0ac)}, + Uint128{u64(0xbdaa75112b1f0e4e), u64(0x010a6650b926d66b)}, + Uint128{u64(0xad15125575e6d1e2), u64(0x014cffe4e7708c06)}, + Uint128{u64(0x585a56ead360865b), u64(0x01a03fde214caf08)}, + Uint128{u64(0x37387652c41c53f8), u64(0x010427ead4cfed65)}, + Uint128{u64(0x850693e7752368f7), u64(0x014531e58a03e8be)}, + Uint128{u64(0x264838e1526c4334), u64(0x01967e5eec84e2ee)}, + Uint128{u64(0xafda4719a7075402), u64(0x01fc1df6a7a61ba9)}, + Uint128{u64(0x0de86c7008649481), u64(0x013d92ba28c7d14a)}, + Uint128{u64(0x9162878c0a7db9a1), u64(0x018cf768b2f9c59c)}, + Uint128{u64(0xb5bb296f0d1d280a), u64(0x01f03542dfb83703)}, + Uint128{u64(0x5194f9e568323906), u64(0x01362149cbd32262)}, + Uint128{u64(0xe5fa385ec23ec747), u64(0x0183a99c3ec7eafa)}, + Uint128{u64(0x9f78c67672ce7919), u64(0x01e494034e79e5b9)}, + Uint128{u64(0x03ab7c0a07c10bb0), u64(0x012edc82110c2f94)}, + Uint128{u64(0x04965b0c89b14e9c), u64(0x017a93a2954f3b79)}, + Uint128{u64(0x45bbf1cfac1da243), u64(0x01d9388b3aa30a57)}, + Uint128{u64(0x8b957721cb92856a), u64(0x0127c35704a5e676)}, + Uint128{u64(0x2e7ad4ea3e7726c4), u64(0x0171b42cc5cf6014)}, + Uint128{u64(0x3a198a24ce14f075), u64(0x01ce2137f7433819)}, + Uint128{u64(0xc44ff65700cd1649), u64(0x0120d4c2fa8a030f)}, + Uint128{u64(0xb563f3ecc1005bdb), u64(0x016909f3b92c83d3)}, + Uint128{u64(0xa2bcf0e7f14072d2), u64(0x01c34c70a777a4c8)}, + Uint128{u64(0x65b61690f6c847c3), u64(0x011a0fc668aac6fd)}, + Uint128{u64(0xbf239c35347a59b4), u64(0x016093b802d578bc)}, + Uint128{u64(0xeeec83428198f021), u64(0x01b8b8a6038ad6eb)}, + Uint128{u64(0x7553d20990ff9615), u64(0x01137367c236c653)}, + Uint128{u64(0x52a8c68bf53f7b9a), u64(0x01585041b2c477e8)}, + Uint128{u64(0x6752f82ef28f5a81), u64(0x01ae64521f7595e2)}, + Uint128{u64(0x8093db1d57999890), u64(0x010cfeb353a97dad)}, + Uint128{u64(0xe0b8d1e4ad7ffeb4), u64(0x01503e602893dd18)}, + Uint128{u64(0x18e7065dd8dffe62), u64(0x01a44df832b8d45f)}, + Uint128{u64(0x6f9063faa78bfefd), u64(0x0106b0bb1fb384bb)}, + Uint128{u64(0x4b747cf9516efebc), u64(0x01485ce9e7a065ea)}, + Uint128{u64(0xde519c37a5cabe6b), u64(0x019a742461887f64)}, + Uint128{u64(0x0af301a2c79eb703), u64(0x01008896bcf54f9f)}, + Uint128{u64(0xcdafc20b798664c4), u64(0x0140aabc6c32a386)}, + Uint128{u64(0x811bb28e57e7fdf5), u64(0x0190d56b873f4c68)}, + Uint128{u64(0xa1629f31ede1fd72), u64(0x01f50ac6690f1f82)}, + Uint128{u64(0xa4dda37f34ad3e67), u64(0x013926bc01a973b1)}, + Uint128{u64(0x0e150c5f01d88e01), u64(0x0187706b0213d09e)}, + Uint128{u64(0x919a4f76c24eb181), u64(0x01e94c85c298c4c5)}, + Uint128{u64(0x7b0071aa39712ef1), u64(0x0131cfd3999f7afb)}, + Uint128{u64(0x59c08e14c7cd7aad), u64(0x017e43c8800759ba)}, + Uint128{u64(0xf030b199f9c0d958), u64(0x01ddd4baa0093028)}, + Uint128{u64(0x961e6f003c1887d7), u64(0x012aa4f4a405be19)}, + Uint128{u64(0xfba60ac04b1ea9cd), u64(0x01754e31cd072d9f)}, + Uint128{u64(0xfa8f8d705de65440), u64(0x01d2a1be4048f907)}, + Uint128{u64(0xfc99b8663aaff4a8), u64(0x0123a516e82d9ba4)}, + Uint128{u64(0x3bc0267fc95bf1d2), u64(0x016c8e5ca239028e)}, + Uint128{u64(0xcab0301fbbb2ee47), u64(0x01c7b1f3cac74331)}, + Uint128{u64(0x1eae1e13d54fd4ec), u64(0x011ccf385ebc89ff)}, + Uint128{u64(0xe659a598caa3ca27), u64(0x01640306766bac7e)}, + Uint128{u64(0x9ff00efefd4cbcb1), u64(0x01bd03c81406979e)}, + Uint128{u64(0x23f6095f5e4ff5ef), u64(0x0116225d0c841ec3)}, + Uint128{u64(0xecf38bb735e3f36a), u64(0x015baaf44fa52673)}, + Uint128{u64(0xe8306ea5035cf045), u64(0x01b295b1638e7010)}, + Uint128{u64(0x911e4527221a162b), u64(0x010f9d8ede39060a)}, + Uint128{u64(0x3565d670eaa09bb6), u64(0x015384f295c7478d)}, + Uint128{u64(0x82bf4c0d2548c2a3), u64(0x01a8662f3b391970)}, + Uint128{u64(0x51b78f88374d79a6), u64(0x01093fdd8503afe6)}, + Uint128{u64(0xe625736a4520d810), u64(0x014b8fd4e6449bdf)}, + Uint128{u64(0xdfaed044d6690e14), u64(0x019e73ca1fd5c2d7)}, + Uint128{u64(0xebcd422b0601a8cc), u64(0x0103085e53e599c6)}, + Uint128{u64(0xa6c092b5c78212ff), u64(0x0143ca75e8df0038)}, + Uint128{u64(0xd070b763396297bf), u64(0x0194bd136316c046)}, + Uint128{u64(0x848ce53c07bb3daf), u64(0x01f9ec583bdc7058)}, + Uint128{u64(0x52d80f4584d5068d), u64(0x013c33b72569c637)}, + Uint128{u64(0x278e1316e60a4831), u64(0x018b40a4eec437c5)}, + ] + + pow5_inv_split_64 = [ + Uint128{u64(0x0000000000000001), u64(0x0400000000000000)}, + Uint128{u64(0x3333333333333334), u64(0x0333333333333333)}, + Uint128{u64(0x28f5c28f5c28f5c3), u64(0x028f5c28f5c28f5c)}, + Uint128{u64(0xed916872b020c49c), u64(0x020c49ba5e353f7c)}, + Uint128{u64(0xaf4f0d844d013a93), u64(0x0346dc5d63886594)}, + Uint128{u64(0x8c3f3e0370cdc876), u64(0x029f16b11c6d1e10)}, + Uint128{u64(0xd698fe69270b06c5), u64(0x0218def416bdb1a6)}, + Uint128{u64(0xf0f4ca41d811a46e), u64(0x035afe535795e90a)}, + Uint128{u64(0xf3f70834acdae9f1), u64(0x02af31dc4611873b)}, + Uint128{u64(0x5cc5a02a23e254c1), u64(0x0225c17d04dad296)}, + Uint128{u64(0xfad5cd10396a2135), u64(0x036f9bfb3af7b756)}, + Uint128{u64(0xfbde3da69454e75e), u64(0x02bfaffc2f2c92ab)}, + Uint128{u64(0x2fe4fe1edd10b918), u64(0x0232f33025bd4223)}, + Uint128{u64(0x4ca19697c81ac1bf), u64(0x0384b84d092ed038)}, + Uint128{u64(0x3d4e1213067bce33), u64(0x02d09370d4257360)}, + Uint128{u64(0x643e74dc052fd829), u64(0x024075f3dceac2b3)}, + Uint128{u64(0x6d30baf9a1e626a7), u64(0x039a5652fb113785)}, + Uint128{u64(0x2426fbfae7eb5220), u64(0x02e1dea8c8da92d1)}, + Uint128{u64(0x1cebfcc8b9890e80), u64(0x024e4bba3a487574)}, + Uint128{u64(0x94acc7a78f41b0cc), u64(0x03b07929f6da5586)}, + Uint128{u64(0xaa23d2ec729af3d7), u64(0x02f394219248446b)}, + Uint128{u64(0xbb4fdbf05baf2979), u64(0x025c768141d369ef)}, + Uint128{u64(0xc54c931a2c4b758d), u64(0x03c7240202ebdcb2)}, + Uint128{u64(0x9dd6dc14f03c5e0b), u64(0x0305b66802564a28)}, + Uint128{u64(0x4b1249aa59c9e4d6), u64(0x026af8533511d4ed)}, + Uint128{u64(0x44ea0f76f60fd489), u64(0x03de5a1ebb4fbb15)}, + Uint128{u64(0x6a54d92bf80caa07), u64(0x0318481895d96277)}, + Uint128{u64(0x21dd7a89933d54d2), u64(0x0279d346de4781f9)}, + Uint128{u64(0x362f2a75b8622150), u64(0x03f61ed7ca0c0328)}, + Uint128{u64(0xf825bb91604e810d), u64(0x032b4bdfd4d668ec)}, + Uint128{u64(0xc684960de6a5340b), u64(0x0289097fdd7853f0)}, + Uint128{u64(0xd203ab3e521dc33c), u64(0x02073accb12d0ff3)}, + Uint128{u64(0xe99f7863b696052c), u64(0x033ec47ab514e652)}, + Uint128{u64(0x87b2c6b62bab3757), u64(0x02989d2ef743eb75)}, + Uint128{u64(0xd2f56bc4efbc2c45), u64(0x0213b0f25f69892a)}, + Uint128{u64(0x1e55793b192d13a2), u64(0x0352b4b6ff0f41de)}, + Uint128{u64(0x4b77942f475742e8), u64(0x02a8909265a5ce4b)}, + Uint128{u64(0xd5f9435905df68ba), u64(0x022073a8515171d5)}, + Uint128{u64(0x565b9ef4d6324129), u64(0x03671f73b54f1c89)}, + Uint128{u64(0xdeafb25d78283421), u64(0x02b8e5f62aa5b06d)}, + Uint128{u64(0x188c8eb12cecf681), u64(0x022d84c4eeeaf38b)}, + Uint128{u64(0x8dadb11b7b14bd9b), u64(0x037c07a17e44b8de)}, + Uint128{u64(0x7157c0e2c8dd647c), u64(0x02c99fb46503c718)}, + Uint128{u64(0x8ddfcd823a4ab6ca), u64(0x023ae629ea696c13)}, + Uint128{u64(0x1632e269f6ddf142), u64(0x0391704310a8acec)}, + Uint128{u64(0x44f581ee5f17f435), u64(0x02dac035a6ed5723)}, + Uint128{u64(0x372ace584c1329c4), u64(0x024899c4858aac1c)}, + Uint128{u64(0xbeaae3c079b842d3), u64(0x03a75c6da27779c6)}, + Uint128{u64(0x6555830061603576), u64(0x02ec49f14ec5fb05)}, + Uint128{u64(0xb7779c004de6912b), u64(0x0256a18dd89e626a)}, + Uint128{u64(0xf258f99a163db512), u64(0x03bdcf495a9703dd)}, + Uint128{u64(0x5b7a614811caf741), u64(0x02fe3f6de212697e)}, + Uint128{u64(0xaf951aa00e3bf901), u64(0x0264ff8b1b41edfe)}, + Uint128{u64(0x7f54f7667d2cc19b), u64(0x03d4cc11c5364997)}, + Uint128{u64(0x32aa5f8530f09ae3), u64(0x0310a3416a91d479)}, + Uint128{u64(0xf55519375a5a1582), u64(0x0273b5cdeedb1060)}, + Uint128{u64(0xbbbb5b8bc3c3559d), u64(0x03ec56164af81a34)}, + Uint128{u64(0x2fc916096969114a), u64(0x03237811d593482a)}, + Uint128{u64(0x596dab3ababa743c), u64(0x0282c674aadc39bb)}, + Uint128{u64(0x478aef622efb9030), u64(0x0202385d557cfafc)}, + Uint128{u64(0xd8de4bd04b2c19e6), u64(0x0336c0955594c4c6)}, + Uint128{u64(0xad7ea30d08f014b8), u64(0x029233aaaadd6a38)}, + Uint128{u64(0x24654f3da0c01093), u64(0x020e8fbbbbe454fa)}, + Uint128{u64(0x3a3bb1fc346680eb), u64(0x034a7f92c63a2190)}, + Uint128{u64(0x94fc8e635d1ecd89), u64(0x02a1ffa89e94e7a6)}, + Uint128{u64(0xaa63a51c4a7f0ad4), u64(0x021b32ed4baa52eb)}, + Uint128{u64(0xdd6c3b607731aaed), u64(0x035eb7e212aa1e45)}, + Uint128{u64(0x1789c919f8f488bd), u64(0x02b22cb4dbbb4b6b)}, + Uint128{u64(0xac6e3a7b2d906d64), u64(0x022823c3e2fc3c55)}, + Uint128{u64(0x13e390c515b3e23a), u64(0x03736c6c9e606089)}, + Uint128{u64(0xdcb60d6a77c31b62), u64(0x02c2bd23b1e6b3a0)}, + Uint128{u64(0x7d5e7121f968e2b5), u64(0x0235641c8e52294d)}, + Uint128{u64(0xc8971b698f0e3787), u64(0x0388a02db0837548)}, + Uint128{u64(0xa078e2bad8d82c6c), u64(0x02d3b357c0692aa0)}, + Uint128{u64(0xe6c71bc8ad79bd24), u64(0x0242f5dfcd20eee6)}, + Uint128{u64(0x0ad82c7448c2c839), u64(0x039e5632e1ce4b0b)}, + Uint128{u64(0x3be023903a356cfa), u64(0x02e511c24e3ea26f)}, + Uint128{u64(0x2fe682d9c82abd95), u64(0x0250db01d8321b8c)}, + Uint128{u64(0x4ca4048fa6aac8ee), u64(0x03b4919c8d1cf8e0)}, + Uint128{u64(0x3d5003a61eef0725), u64(0x02f6dae3a4172d80)}, + Uint128{u64(0x9773361e7f259f51), u64(0x025f1582e9ac2466)}, + Uint128{u64(0x8beb89ca6508fee8), u64(0x03cb559e42ad070a)}, + Uint128{u64(0x6fefa16eb73a6586), u64(0x0309114b688a6c08)}, + Uint128{u64(0xf3261abef8fb846b), u64(0x026da76f86d52339)}, + Uint128{u64(0x51d691318e5f3a45), u64(0x03e2a57f3e21d1f6)}, + Uint128{u64(0x0e4540f471e5c837), u64(0x031bb798fe8174c5)}, + Uint128{u64(0xd8376729f4b7d360), u64(0x027c92e0cb9ac3d0)}, + Uint128{u64(0xf38bd84321261eff), u64(0x03fa849adf5e061a)}, + Uint128{u64(0x293cad0280eb4bff), u64(0x032ed07be5e4d1af)}, + Uint128{u64(0xedca240200bc3ccc), u64(0x028bd9fcb7ea4158)}, + Uint128{u64(0xbe3b50019a3030a4), u64(0x02097b309321cde0)}, + Uint128{u64(0xc9f88002904d1a9f), u64(0x03425eb41e9c7c9a)}, + Uint128{u64(0x3b2d3335403daee6), u64(0x029b7ef67ee396e2)}, + Uint128{u64(0x95bdc291003158b8), u64(0x0215ff2b98b6124e)}, + Uint128{u64(0x892f9db4cd1bc126), u64(0x035665128df01d4a)}, + Uint128{u64(0x07594af70a7c9a85), u64(0x02ab840ed7f34aa2)}, + Uint128{u64(0x6c476f2c0863aed1), u64(0x0222d00bdff5d54e)}, + Uint128{u64(0x13a57eacda3917b4), u64(0x036ae67966562217)}, + Uint128{u64(0x0fb7988a482dac90), u64(0x02bbeb9451de81ac)}, + Uint128{u64(0xd95fad3b6cf156da), u64(0x022fefa9db1867bc)}, + Uint128{u64(0xf565e1f8ae4ef15c), u64(0x037fe5dc91c0a5fa)}, + Uint128{u64(0x911e4e608b725ab0), u64(0x02ccb7e3a7cd5195)}, + Uint128{u64(0xda7ea51a0928488d), u64(0x023d5fe9530aa7aa)}, + Uint128{u64(0xf7310829a8407415), u64(0x039566421e7772aa)}, + Uint128{u64(0x2c2739baed005cde), u64(0x02ddeb68185f8eef)}, + Uint128{u64(0xbcec2e2f24004a4b), u64(0x024b22b9ad193f25)}, + Uint128{u64(0x94ad16b1d333aa11), u64(0x03ab6ac2ae8ecb6f)}, + Uint128{u64(0xaa241227dc2954db), u64(0x02ef889bbed8a2bf)}, + Uint128{u64(0x54e9a81fe35443e2), u64(0x02593a163246e899)}, + Uint128{u64(0x2175d9cc9eed396a), u64(0x03c1f689ea0b0dc2)}, + Uint128{u64(0xe7917b0a18bdc788), u64(0x03019207ee6f3e34)}, + Uint128{u64(0xb9412f3b46fe393a), u64(0x0267a8065858fe90)}, + Uint128{u64(0xf535185ed7fd285c), u64(0x03d90cd6f3c1974d)}, + Uint128{u64(0xc42a79e57997537d), u64(0x03140a458fce12a4)}, + Uint128{u64(0x03552e512e12a931), u64(0x02766e9e0ca4dbb7)}, + Uint128{u64(0x9eeeb081e3510eb4), u64(0x03f0b0fce107c5f1)}, + Uint128{u64(0x4bf226ce4f740bc3), u64(0x0326f3fd80d304c1)}, + Uint128{u64(0xa3281f0b72c33c9c), u64(0x02858ffe00a8d09a)}, + Uint128{u64(0x1c2018d5f568fd4a), u64(0x020473319a20a6e2)}, + Uint128{u64(0xf9ccf48988a7fba9), u64(0x033a51e8f69aa49c)}, + Uint128{u64(0xfb0a5d3ad3b99621), u64(0x02950e53f87bb6e3)}, + Uint128{u64(0x2f3b7dc8a96144e7), u64(0x0210d8432d2fc583)}, + Uint128{u64(0xe52bfc7442353b0c), u64(0x034e26d1e1e608d1)}, + Uint128{u64(0xb756639034f76270), u64(0x02a4ebdb1b1e6d74)}, + Uint128{u64(0x2c451c735d92b526), u64(0x021d897c15b1f12a)}, + Uint128{u64(0x13a1c71efc1deea3), u64(0x0362759355e981dd)}, + Uint128{u64(0x761b05b2634b2550), u64(0x02b52adc44bace4a)}, + Uint128{u64(0x91af37c1e908eaa6), u64(0x022a88b036fbd83b)}, + Uint128{u64(0x82b1f2cfdb417770), u64(0x03774119f192f392)}, + Uint128{u64(0xcef4c23fe29ac5f3), u64(0x02c5cdae5adbf60e)}, + Uint128{u64(0x3f2a34ffe87bd190), u64(0x0237d7beaf165e72)}, + Uint128{u64(0x984387ffda5fb5b2), u64(0x038c8c644b56fd83)}, + Uint128{u64(0xe0360666484c915b), u64(0x02d6d6b6a2abfe02)}, + Uint128{u64(0x802b3851d3707449), u64(0x024578921bbccb35)}, + Uint128{u64(0x99dec082ebe72075), u64(0x03a25a835f947855)}, + Uint128{u64(0xae4bcd358985b391), u64(0x02e8486919439377)}, + Uint128{u64(0xbea30a913ad15c74), u64(0x02536d20e102dc5f)}, + Uint128{u64(0xfdd1aa81f7b560b9), u64(0x03b8ae9b019e2d65)}, + Uint128{u64(0x97daeece5fc44d61), u64(0x02fa2548ce182451)}, + Uint128{u64(0xdfe258a51969d781), u64(0x0261b76d71ace9da)}, + Uint128{u64(0x996a276e8f0fbf34), u64(0x03cf8be24f7b0fc4)}, + Uint128{u64(0xe121b9253f3fcc2a), u64(0x030c6fe83f95a636)}, + Uint128{u64(0xb41afa8432997022), u64(0x02705986994484f8)}, + Uint128{u64(0xecf7f739ea8f19cf), u64(0x03e6f5a4286da18d)}, + Uint128{u64(0x23f99294bba5ae40), u64(0x031f2ae9b9f14e0b)}, + Uint128{u64(0x4ffadbaa2fb7be99), u64(0x027f5587c7f43e6f)}, + Uint128{u64(0x7ff7c5dd1925fdc2), u64(0x03feef3fa6539718)}, + Uint128{u64(0xccc637e4141e649b), u64(0x033258ffb842df46)}, + Uint128{u64(0xd704f983434b83af), u64(0x028ead9960357f6b)}, + Uint128{u64(0x126a6135cf6f9c8c), u64(0x020bbe144cf79923)}, + Uint128{u64(0x83dd685618b29414), u64(0x0345fced47f28e9e)}, + Uint128{u64(0x9cb12044e08edcdd), u64(0x029e63f1065ba54b)}, + Uint128{u64(0x16f419d0b3a57d7d), u64(0x02184ff405161dd6)}, + Uint128{u64(0x8b20294dec3bfbfb), u64(0x035a19866e89c956)}, + Uint128{u64(0x3c19baa4bcfcc996), u64(0x02ae7ad1f207d445)}, + Uint128{u64(0xc9ae2eea30ca3adf), u64(0x02252f0e5b39769d)}, + Uint128{u64(0x0f7d17dd1add2afd), u64(0x036eb1b091f58a96)}, + Uint128{u64(0x3f97464a7be42264), u64(0x02bef48d41913bab)}, + Uint128{u64(0xcc790508631ce850), u64(0x02325d3dce0dc955)}, + Uint128{u64(0xe0c1a1a704fb0d4d), u64(0x0383c862e3494222)}, + Uint128{u64(0x4d67b4859d95a43e), u64(0x02cfd3824f6dce82)}, + Uint128{u64(0x711fc39e17aae9cb), u64(0x023fdc683f8b0b9b)}, + Uint128{u64(0xe832d2968c44a945), u64(0x039960a6cc11ac2b)}, + Uint128{u64(0xecf575453d03ba9e), u64(0x02e11a1f09a7bcef)}, + Uint128{u64(0x572ac4376402fbb1), u64(0x024dae7f3aec9726)}, + Uint128{u64(0x58446d256cd192b5), u64(0x03af7d985e47583d)}, + Uint128{u64(0x79d0575123dadbc4), u64(0x02f2cae04b6c4697)}, + Uint128{u64(0x94a6ac40e97be303), u64(0x025bd5803c569edf)}, + Uint128{u64(0x8771139b0f2c9e6c), u64(0x03c62266c6f0fe32)}, + Uint128{u64(0x9f8da948d8f07ebd), u64(0x0304e85238c0cb5b)}, + Uint128{u64(0xe60aedd3e0c06564), u64(0x026a5374fa33d5e2)}, + Uint128{u64(0xa344afb9679a3bd2), u64(0x03dd5254c3862304)}, + Uint128{u64(0xe903bfc78614fca8), u64(0x031775109c6b4f36)}, + Uint128{u64(0xba6966393810ca20), u64(0x02792a73b055d8f8)}, + Uint128{u64(0x2a423d2859b4769a), u64(0x03f510b91a22f4c1)}, + Uint128{u64(0xee9b642047c39215), u64(0x032a73c7481bf700)}, + Uint128{u64(0xbee2b680396941aa), u64(0x02885c9f6ce32c00)}, + Uint128{u64(0xff1bc53361210155), u64(0x0206b07f8a4f5666)}, + Uint128{u64(0x31c6085235019bbb), u64(0x033de73276e5570b)}, + Uint128{u64(0x27d1a041c4014963), u64(0x0297ec285f1ddf3c)}, + Uint128{u64(0xeca7b367d0010782), u64(0x021323537f4b18fc)}, + Uint128{u64(0xadd91f0c8001a59d), u64(0x0351d21f3211c194)}, + Uint128{u64(0xf17a7f3d3334847e), u64(0x02a7db4c280e3476)}, + Uint128{u64(0x279532975c2a0398), u64(0x021fe2a3533e905f)}, + Uint128{u64(0xd8eeb75893766c26), u64(0x0366376bb8641a31)}, + Uint128{u64(0x7a5892ad42c52352), u64(0x02b82c562d1ce1c1)}, + Uint128{u64(0xfb7a0ef102374f75), u64(0x022cf044f0e3e7cd)}, + Uint128{u64(0xc59017e8038bb254), u64(0x037b1a07e7d30c7c)}, + Uint128{u64(0x37a67986693c8eaa), u64(0x02c8e19feca8d6ca)}, + Uint128{u64(0xf951fad1edca0bbb), u64(0x023a4e198a20abd4)}, + Uint128{u64(0x28832ae97c76792b), u64(0x03907cf5a9cddfbb)}, + Uint128{u64(0x2068ef21305ec756), u64(0x02d9fd9154a4b2fc)}, + Uint128{u64(0x19ed8c1a8d189f78), u64(0x0247fe0ddd508f30)}, + Uint128{u64(0x5caf4690e1c0ff26), u64(0x03a66349621a7eb3)}, + Uint128{u64(0x4a25d20d81673285), u64(0x02eb82a11b48655c)}, + Uint128{u64(0x3b5174d79ab8f537), u64(0x0256021a7c39eab0)}, + Uint128{u64(0x921bee25c45b21f1), u64(0x03bcd02a605caab3)}, + Uint128{u64(0xdb498b5169e2818e), u64(0x02fd735519e3bbc2)}, + Uint128{u64(0x15d46f7454b53472), u64(0x02645c4414b62fcf)}, + Uint128{u64(0xefba4bed545520b6), u64(0x03d3c6d35456b2e4)}, + Uint128{u64(0xf2fb6ff110441a2b), u64(0x030fd242a9def583)}, + Uint128{u64(0x8f2f8cc0d9d014ef), u64(0x02730e9bbb18c469)}, + Uint128{u64(0xb1e5ae015c80217f), u64(0x03eb4a92c4f46d75)}, + Uint128{u64(0xc1848b344a001acc), u64(0x0322a20f03f6bdf7)}, + Uint128{u64(0xce03a2903b3348a3), u64(0x02821b3f365efe5f)}, + Uint128{u64(0xd802e873628f6d4f), u64(0x0201af65c518cb7f)}, + Uint128{u64(0x599e40b89db2487f), u64(0x0335e56fa1c14599)}, + Uint128{u64(0xe14b66fa17c1d399), u64(0x029184594e3437ad)}, + Uint128{u64(0x81091f2e7967dc7a), u64(0x020e037aa4f692f1)}, + Uint128{u64(0x9b41cb7d8f0c93f6), u64(0x03499f2aa18a84b5)}, + Uint128{u64(0xaf67d5fe0c0a0ff8), u64(0x02a14c221ad536f7)}, + Uint128{u64(0xf2b977fe70080cc7), u64(0x021aa34e7bddc592)}, + Uint128{u64(0x1df58cca4cd9ae0b), u64(0x035dd2172c9608eb)}, + Uint128{u64(0xe4c470a1d7148b3c), u64(0x02b174df56de6d88)}, + Uint128{u64(0x83d05a1b1276d5ca), u64(0x022790b2abe5246d)}, + Uint128{u64(0x9fb3c35e83f1560f), u64(0x0372811ddfd50715)}, + Uint128{u64(0xb2f635e5365aab3f), u64(0x02c200e4b310d277)}, + Uint128{u64(0xf591c4b75eaeef66), u64(0x0234cd83c273db92)}, + Uint128{u64(0xef4fa125644b18a3), u64(0x0387af39371fc5b7)}, + Uint128{u64(0x8c3fb41de9d5ad4f), u64(0x02d2f2942c196af9)}, + Uint128{u64(0x3cffc34b2177bdd9), u64(0x02425ba9bce12261)}, + Uint128{u64(0x94cc6bab68bf9628), u64(0x039d5f75fb01d09b)}, + Uint128{u64(0x10a38955ed6611b9), u64(0x02e44c5e6267da16)}, + Uint128{u64(0xda1c6dde5784dafb), u64(0x02503d184eb97b44)}, + Uint128{u64(0xf693e2fd58d49191), u64(0x03b394f3b128c53a)}, + Uint128{u64(0xc5431bfde0aa0e0e), u64(0x02f610c2f4209dc8)}, + Uint128{u64(0x6a9c1664b3bb3e72), u64(0x025e73cf29b3b16d)}, + Uint128{u64(0x10f9bd6dec5eca4f), u64(0x03ca52e50f85e8af)}, + Uint128{u64(0xda616457f04bd50c), u64(0x03084250d937ed58)}, + Uint128{u64(0xe1e783798d09773d), u64(0x026d01da475ff113)}, + Uint128{u64(0x030c058f480f252e), u64(0x03e19c9072331b53)}, + Uint128{u64(0x68d66ad906728425), u64(0x031ae3a6c1c27c42)}, + Uint128{u64(0x8711ef14052869b7), u64(0x027be952349b969b)}, + Uint128{u64(0x0b4fe4ecd50d75f2), u64(0x03f97550542c242c)}, + Uint128{u64(0xa2a650bd773df7f5), u64(0x032df7737689b689)}, + Uint128{u64(0xb551da312c31932a), u64(0x028b2c5c5ed49207)}, + Uint128{u64(0x5ddb14f4235adc22), u64(0x0208f049e576db39)}, + Uint128{u64(0x2fc4ee536bc49369), u64(0x034180763bf15ec2)}, + Uint128{u64(0xbfd0bea92303a921), u64(0x029acd2b63277f01)}, + Uint128{u64(0x9973cbba8269541a), u64(0x021570ef8285ff34)}, + Uint128{u64(0x5bec792a6a42202a), u64(0x0355817f373ccb87)}, + Uint128{u64(0xe3239421ee9b4cef), u64(0x02aacdff5f63d605)}, + Uint128{u64(0xb5b6101b25490a59), u64(0x02223e65e5e97804)}, + Uint128{u64(0x22bce691d541aa27), u64(0x0369fd6fd64259a1)}, + Uint128{u64(0xb563eba7ddce21b9), u64(0x02bb31264501e14d)}, + Uint128{u64(0xf78322ecb171b494), u64(0x022f5a850401810a)}, + Uint128{u64(0x259e9e47824f8753), u64(0x037ef73b399c01ab)}, + Uint128{u64(0x1e187e9f9b72d2a9), u64(0x02cbf8fc2e1667bc)}, + Uint128{u64(0x4b46cbb2e2c24221), u64(0x023cc73024deb963)}, + Uint128{u64(0x120adf849e039d01), u64(0x039471e6a1645bd2)}, + Uint128{u64(0xdb3be603b19c7d9a), u64(0x02dd27ebb4504974)}, + Uint128{u64(0x7c2feb3627b0647c), u64(0x024a865629d9d45d)}, + Uint128{u64(0x2d197856a5e7072c), u64(0x03aa7089dc8fba2f)}, + Uint128{u64(0x8a7ac6abb7ec05bd), u64(0x02eec06e4a0c94f2)}, + Uint128{u64(0xd52f05562cbcd164), u64(0x025899f1d4d6dd8e)}, + Uint128{u64(0x21e4d556adfae8a0), u64(0x03c0f64fbaf1627e)}, + Uint128{u64(0xe7ea444557fbed4d), u64(0x0300c50c958de864)}, + Uint128{u64(0xecbb69d1132ff10a), u64(0x0267040a113e5383)}, + Uint128{u64(0xadf8a94e851981aa), u64(0x03d8067681fd526c)}, + Uint128{u64(0x8b2d543ed0e13488), u64(0x0313385ece6441f0)}, + Uint128{u64(0xd5bddcff0d80f6d3), u64(0x0275c6b23eb69b26)}, + Uint128{u64(0x892fc7fe7c018aeb), u64(0x03efa45064575ea4)}, + Uint128{u64(0x3a8c9ffec99ad589), u64(0x03261d0d1d12b21d)}, + Uint128{u64(0xc8707fff07af113b), u64(0x0284e40a7da88e7d)}, + Uint128{u64(0x39f39998d2f2742f), u64(0x0203e9a1fe2071fe)}, + Uint128{u64(0x8fec28f484b7204b), u64(0x033975cffd00b663)}, + Uint128{u64(0xd989ba5d36f8e6a2), u64(0x02945e3ffd9a2b82)}, + Uint128{u64(0x47a161e42bfa521c), u64(0x02104b66647b5602)}, + Uint128{u64(0x0c35696d132a1cf9), u64(0x034d4570a0c5566a)}, + Uint128{u64(0x09c454574288172d), u64(0x02a4378d4d6aab88)}, + Uint128{u64(0xa169dd129ba0128b), u64(0x021cf93dd7888939)}, + Uint128{u64(0x0242fb50f9001dab), u64(0x03618ec958da7529)}, + Uint128{u64(0x9b68c90d940017bc), u64(0x02b4723aad7b90ed)}, + Uint128{u64(0x4920a0d7a999ac96), u64(0x0229f4fbbdfc73f1)}, + Uint128{u64(0x750101590f5c4757), u64(0x037654c5fcc71fe8)}, + Uint128{u64(0x2a6734473f7d05df), u64(0x02c5109e63d27fed)}, + Uint128{u64(0xeeb8f69f65fd9e4c), u64(0x0237407eb641fff0)}, + Uint128{u64(0xe45b24323cc8fd46), u64(0x038b9a6456cfffe7)}, + Uint128{u64(0xb6af502830a0ca9f), u64(0x02d6151d123fffec)}, + Uint128{u64(0xf88c402026e7087f), u64(0x0244ddb0db666656)}, + Uint128{u64(0x2746cd003e3e73fe), u64(0x03a162b4923d708b)}, + Uint128{u64(0x1f6bd73364fec332), u64(0x02e7822a0e978d3c)}, + Uint128{u64(0xe5efdf5c50cbcf5b), u64(0x0252ce880bac70fc)}, + Uint128{u64(0x3cb2fefa1adfb22b), u64(0x03b7b0d9ac471b2e)}, + Uint128{u64(0x308f3261af195b56), u64(0x02f95a47bd05af58)}, + Uint128{u64(0x5a0c284e25ade2ab), u64(0x0261150630d15913)}, + Uint128{u64(0x29ad0d49d5e30445), u64(0x03ce8809e7b55b52)}, + Uint128{u64(0x548a7107de4f369d), u64(0x030ba007ec9115db)}, + Uint128{u64(0xdd3b8d9fe50c2bb1), u64(0x026fb3398a0dab15)}, + Uint128{u64(0x952c15cca1ad12b5), u64(0x03e5eb8f434911bc)}, + Uint128{u64(0x775677d6e7bda891), u64(0x031e560c35d40e30)}, + Uint128{u64(0xc5dec645863153a7), u64(0x027eab3cf7dcd826)}, + ] +) diff --git a/v_windows/v/old/vlib/strconv/utilities.v b/v_windows/v/old/vlib/strconv/utilities.v new file mode 100644 index 0000000..2098a73 --- /dev/null +++ b/v_windows/v/old/vlib/strconv/utilities.v @@ -0,0 +1,556 @@ +module strconv + +import math.bits +// import math + +/* +f32/f64 to string utilities + +Copyright (c) 2019-2021 Dario Deledda. All rights reserved. +Use of this source code is governed by an MIT license +that can be found in the LICENSE file. + +This file contains the f32/f64 to string utilities functions + +These functions are based on the work of: +Publication:PLDI 2018: Proceedings of the 39th ACM SIGPLAN +Conference on Programming Language Design and ImplementationJune 2018 +Pages 270–282 https://doi.org/10.1145/3192366.3192369 + +inspired by the Go version here: +https://github.com/cespare/ryu/tree/ba56a33f39e3bbbfa409095d0f9ae168a595feea +*/ + +// General Utilities +[if debug_strconv ?] +fn assert1(t bool, msg string) { + if !t { + panic(msg) + } +} + +[inline] +fn bool_to_int(b bool) int { + if b { + return 1 + } + return 0 +} + +[inline] +fn bool_to_u32(b bool) u32 { + if b { + return u32(1) + } + return u32(0) +} + +[inline] +fn bool_to_u64(b bool) u64 { + if b { + return u64(1) + } + return u64(0) +} + +fn get_string_special(neg bool, expZero bool, mantZero bool) string { + if !mantZero { + return 'nan' + } + if !expZero { + if neg { + return '-inf' + } else { + return '+inf' + } + } + if neg { + return '-0e+00' + } + return '0e+00' +} + +/* +32 bit functions +*/ + +fn mul_shift_32(m u32, mul u64, ishift int) u32 { + // QTODO + // assert ishift > 32 + + hi, lo := bits.mul_64(u64(m), mul) + shifted_sum := (lo >> u64(ishift)) + (hi << u64(64 - ishift)) + assert1(shifted_sum <= 2147483647, 'shiftedSum <= math.max_u32') + return u32(shifted_sum) +} + +fn mul_pow5_invdiv_pow2(m u32, q u32, j int) u32 { + return mul_shift_32(m, pow5_inv_split_32[q], j) +} + +fn mul_pow5_div_pow2(m u32, i u32, j int) u32 { + return mul_shift_32(m, pow5_split_32[i], j) +} + +fn pow5_factor_32(i_v u32) u32 { + mut v := i_v + for n := u32(0); true; n++ { + q := v / 5 + r := v % 5 + if r != 0 { + return n + } + v = q + } + return v +} + +// multiple_of_power_of_five_32 reports whether v is divisible by 5^p. +fn multiple_of_power_of_five_32(v u32, p u32) bool { + return pow5_factor_32(v) >= p +} + +// multiple_of_power_of_two_32 reports whether v is divisible by 2^p. +fn multiple_of_power_of_two_32(v u32, p u32) bool { + return u32(bits.trailing_zeros_32(v)) >= p +} + +// log10_pow2 returns floor(log_10(2^e)). +fn log10_pow2(e int) u32 { + // The first value this approximation fails for is 2^1651 + // which is just greater than 10^297. + assert1(e >= 0, 'e >= 0') + assert1(e <= 1650, 'e <= 1650') + return (u32(e) * 78913) >> 18 +} + +// log10_pow5 returns floor(log_10(5^e)). +fn log10_pow5(e int) u32 { + // The first value this approximation fails for is 5^2621 + // which is just greater than 10^1832. + assert1(e >= 0, 'e >= 0') + assert1(e <= 2620, 'e <= 2620') + return (u32(e) * 732923) >> 20 +} + +// pow5_bits returns ceil(log_2(5^e)), or else 1 if e==0. +fn pow5_bits(e int) int { + // This approximation works up to the point that the multiplication + // overflows at e = 3529. If the multiplication were done in 64 bits, + // it would fail at 5^4004 which is just greater than 2^9297. + assert1(e >= 0, 'e >= 0') + assert1(e <= 3528, 'e <= 3528') + return int(((u32(e) * 1217359) >> 19) + 1) +} + +/* +64 bit functions +*/ + +fn shift_right_128(v Uint128, shift int) u64 { + // The shift value is always modulo 64. + // In the current implementation of the 64-bit version + // of Ryu, the shift value is always < 64. + // (It is in the range [2, 59].) + // Check this here in case a future change requires larger shift + // values. In this case this function needs to be adjusted. + assert1(shift < 64, 'shift < 64') + return (v.hi << u64(64 - shift)) | (v.lo >> u32(shift)) +} + +fn mul_shift_64(m u64, mul Uint128, shift int) u64 { + hihi, hilo := bits.mul_64(m, mul.hi) + lohi, _ := bits.mul_64(m, mul.lo) + mut sum := Uint128{ + lo: lohi + hilo + hi: hihi + } + if sum.lo < lohi { + sum.hi++ // overflow + } + return shift_right_128(sum, shift - 64) +} + +fn pow5_factor_64(v_i u64) u32 { + mut v := v_i + for n := u32(0); true; n++ { + q := v / 5 + r := v % 5 + if r != 0 { + return n + } + v = q + } + return u32(0) +} + +fn multiple_of_power_of_five_64(v u64, p u32) bool { + return pow5_factor_64(v) >= p +} + +fn multiple_of_power_of_two_64(v u64, p u32) bool { + return u32(bits.trailing_zeros_64(v)) >= p +} + +/* +f64 to string with string format +*/ + +// TODO: Investigate precision issues +// f32_to_str_l return a string with the f32 converted in a string in decimal notation +[manualfree] +pub fn f32_to_str_l(f f32) string { + s := f32_to_str(f, 6) + res := fxx_to_str_l_parse(s) + unsafe { s.free() } + return res +} + +[manualfree] +pub fn f32_to_str_l_no_dot(f f32) string { + s := f32_to_str(f, 6) + res := fxx_to_str_l_parse_no_dot(s) + unsafe { s.free() } + return res +} + +[manualfree] +pub fn f64_to_str_l(f f64) string { + s := f64_to_str(f, 18) + res := fxx_to_str_l_parse(s) + unsafe { s.free() } + return res +} + +[manualfree] +pub fn f64_to_str_l_no_dot(f f64) string { + s := f64_to_str(f, 18) + res := fxx_to_str_l_parse_no_dot(s) + unsafe { s.free() } + return res +} + +// f64_to_str_l return a string with the f64 converted in a string in decimal notation +[manualfree] +pub fn fxx_to_str_l_parse(s string) string { + // check for +inf -inf Nan + if s.len > 2 && (s[0] == `n` || s[1] == `i`) { + return s.clone() + } + + m_sgn_flag := false + mut sgn := 1 + mut b := [26]byte{} + mut d_pos := 1 + mut i := 0 + mut i1 := 0 + mut exp := 0 + mut exp_sgn := 1 + + // get sign and decimal parts + for c in s { + if c == `-` { + sgn = -1 + i++ + } else if c == `+` { + sgn = 1 + i++ + } else if c >= `0` && c <= `9` { + b[i1] = c + i1++ + i++ + } else if c == `.` { + if sgn > 0 { + d_pos = i + } else { + d_pos = i - 1 + } + i++ + } else if c == `e` { + i++ + break + } else { + return 'Float conversion error!!' + } + } + b[i1] = 0 + + // get exponent + if s[i] == `-` { + exp_sgn = -1 + i++ + } else if s[i] == `+` { + exp_sgn = 1 + i++ + } + + mut c := i + for c < s.len { + exp = exp * 10 + int(s[c] - `0`) + c++ + } + + // allocate exp+32 chars for the return string + mut res := []byte{len: exp + 32, init: 0} + mut r_i := 0 // result string buffer index + + // println("s:${sgn} b:${b[0]} es:${exp_sgn} exp:${exp}") + + if sgn == 1 { + if m_sgn_flag { + res[r_i] = `+` + r_i++ + } + } else { + res[r_i] = `-` + r_i++ + } + + i = 0 + if exp_sgn >= 0 { + for b[i] != 0 { + res[r_i] = b[i] + r_i++ + i++ + if i >= d_pos && exp >= 0 { + if exp == 0 { + res[r_i] = `.` + r_i++ + } + exp-- + } + } + for exp >= 0 { + res[r_i] = `0` + r_i++ + exp-- + } + } else { + mut dot_p := true + for exp > 0 { + res[r_i] = `0` + r_i++ + exp-- + if dot_p { + res[r_i] = `.` + r_i++ + dot_p = false + } + } + for b[i] != 0 { + res[r_i] = b[i] + r_i++ + i++ + } + } + /* + // remove the dot form the numbers like 2. + if r_i > 1 && res[r_i-1] == `.` { + r_i-- + } + */ + res[r_i] = 0 + return unsafe { tos(res.data, r_i) } +} + +// f64_to_str_l return a string with the f64 converted in a string in decimal notation +[manualfree] +pub fn fxx_to_str_l_parse_no_dot(s string) string { + // check for +inf -inf Nan + if s.len > 2 && (s[0] == `n` || s[1] == `i`) { + return s.clone() + } + + m_sgn_flag := false + mut sgn := 1 + mut b := [26]byte{} + mut d_pos := 1 + mut i := 0 + mut i1 := 0 + mut exp := 0 + mut exp_sgn := 1 + + // get sign and decimal parts + for c in s { + if c == `-` { + sgn = -1 + i++ + } else if c == `+` { + sgn = 1 + i++ + } else if c >= `0` && c <= `9` { + b[i1] = c + i1++ + i++ + } else if c == `.` { + if sgn > 0 { + d_pos = i + } else { + d_pos = i - 1 + } + i++ + } else if c == `e` { + i++ + break + } else { + return 'Float conversion error!!' + } + } + b[i1] = 0 + + // get exponent + if s[i] == `-` { + exp_sgn = -1 + i++ + } else if s[i] == `+` { + exp_sgn = 1 + i++ + } + + mut c := i + for c < s.len { + exp = exp * 10 + int(s[c] - `0`) + c++ + } + + // allocate exp+32 chars for the return string + mut res := []byte{len: exp + 32, init: 0} + mut r_i := 0 // result string buffer index + + // println("s:${sgn} b:${b[0]} es:${exp_sgn} exp:${exp}") + + if sgn == 1 { + if m_sgn_flag { + res[r_i] = `+` + r_i++ + } + } else { + res[r_i] = `-` + r_i++ + } + + i = 0 + if exp_sgn >= 0 { + for b[i] != 0 { + res[r_i] = b[i] + r_i++ + i++ + if i >= d_pos && exp >= 0 { + if exp == 0 { + res[r_i] = `.` + r_i++ + } + exp-- + } + } + for exp >= 0 { + res[r_i] = `0` + r_i++ + exp-- + } + } else { + mut dot_p := true + for exp > 0 { + res[r_i] = `0` + r_i++ + exp-- + if dot_p { + res[r_i] = `.` + r_i++ + dot_p = false + } + } + for b[i] != 0 { + res[r_i] = b[i] + r_i++ + i++ + } + } + + // remove the dot form the numbers like 2. + if r_i > 1 && res[r_i - 1] == `.` { + r_i-- + } + + res[r_i] = 0 + return unsafe { tos(res.data, r_i) } +} + +// dec_digits return the number of decimal digit of an u64 +pub fn dec_digits(n u64) int { + if n <= 9_999_999_999 { // 1-10 + if n <= 99_999 { // 5 + if n <= 99 { // 2 + if n <= 9 { // 1 + return 1 + } else { + return 2 + } + } else { + if n <= 999 { // 3 + return 3 + } else { + if n <= 9999 { // 4 + return 4 + } else { + return 5 + } + } + } + } else { + if n <= 9_999_999 { // 7 + if n <= 999_999 { // 6 + return 6 + } else { + return 7 + } + } else { + if n <= 99_999_999 { // 8 + return 8 + } else { + if n <= 999_999_999 { // 9 + return 9 + } + return 10 + } + } + } + } else { + if n <= 999_999_999_999_999 { // 5 + if n <= 999_999_999_999 { // 2 + if n <= 99_999_999_999 { // 1 + return 11 + } else { + return 12 + } + } else { + if n <= 9_999_999_999_999 { // 3 + return 13 + } else { + if n <= 99_999_999_999_999 { // 4 + return 14 + } else { + return 15 + } + } + } + } else { + if n <= 99_999_999_999_999_999 { // 7 + if n <= 9_999_999_999_999_999 { // 6 + return 16 + } else { + return 17 + } + } else { + if n <= 999_999_999_999_999_999 { // 8 + return 18 + } else { + if n <= 9_999_999_999_999_999_999 { // 9 + return 19 + } + return 20 + } + } + } + } +} diff --git a/v_windows/v/old/vlib/strconv/vprintf.v b/v_windows/v/old/vlib/strconv/vprintf.v new file mode 100644 index 0000000..971477d --- /dev/null +++ b/v_windows/v/old/vlib/strconv/vprintf.v @@ -0,0 +1,726 @@ +/*============================================================================= +Copyright (c) 2019-2021 Dario Deledda. All rights reserved. +Use of this source code is governed by an MIT license +that can be found in the LICENSE file. + +This file contains string interpolation V functions +=============================================================================*/ +module strconv + +import strings + +enum Char_parse_state { + start + norm_char + field_char + pad_ch + len_set_start + len_set_in + check_type + check_float + check_float_in + reset_params +} + +pub fn v_printf(str string, pt ...voidptr) { + print(v_sprintf(str, pt)) +} + +pub fn v_sprintf(str string, pt ...voidptr) string { + mut res := strings.new_builder(pt.len * 16) + + mut i := 0 // main string index + mut p_index := 0 // parameter index + mut sign := false // sign flag + mut allign := Align_text.right + mut len0 := -1 // forced length, if -1 free length + mut len1 := -1 // decimal part for floats + def_len1 := 6 // default value for len1 + mut pad_ch := byte(` `) // pad char + + // prefix chars for Length field + mut ch1 := `0` // +1 char if present else `0` + mut ch2 := `0` // +2 char if present else `0` + + mut status := Char_parse_state.norm_char + for i < str.len { + if status == .reset_params { + sign = false + allign = .right + len0 = -1 + len1 = -1 + pad_ch = ` ` + status = .norm_char + ch1 = `0` + ch2 = `0` + continue + } + + ch := str[i] + if ch != `%` && status == .norm_char { + res.write_b(ch) + i++ + continue + } + if ch == `%` && status == .norm_char { + status = .field_char + i++ + continue + } + + // single char, manage it here + if ch == `c` && status == .field_char { + v_sprintf_panic(p_index, pt.len) + d1 := unsafe { *(&byte(pt[p_index])) } + res.write_b(d1) + status = .reset_params + p_index++ + i++ + continue + } + + // pointer, manage it here + if ch == `p` && status == .field_char { + v_sprintf_panic(p_index, pt.len) + res.write_string('0x') + res.write_string(ptr_str(unsafe { pt[p_index] })) + status = .reset_params + p_index++ + i++ + continue + } + + if status == .field_char { + mut fc_ch1 := `0` + mut fc_ch2 := `0` + if (i + 1) < str.len { + fc_ch1 = str[i + 1] + if (i + 2) < str.len { + fc_ch2 = str[i + 2] + } + } + if ch == `+` { + sign = true + i++ + continue + } else if ch == `-` { + allign = .left + i++ + continue + } else if ch in [`0`, ` `] { + if allign == .right { + pad_ch = ch + } + i++ + continue + } else if ch == `\'` { + i++ + continue + } else if ch == `.` && fc_ch1 >= `1` && fc_ch1 <= `9` { + status = .check_float + i++ + continue + } + // manage "%.*s" precision field + else if ch == `.` && fc_ch1 == `*` && fc_ch2 == `s` { + v_sprintf_panic(p_index, pt.len) + len := unsafe { *(&int(pt[p_index])) } + p_index++ + v_sprintf_panic(p_index, pt.len) + mut s := unsafe { *(&string(pt[p_index])) } + s = s[..len] + p_index++ + res.write_string(s) + status = .reset_params + i += 3 + continue + } + status = .len_set_start + continue + } + + if status == .len_set_start { + if ch >= `1` && ch <= `9` { + len0 = int(ch - `0`) + status = .len_set_in + i++ + continue + } + if ch == `.` { + status = .check_float + i++ + continue + } + status = .check_type + continue + } + + if status == .len_set_in { + if ch >= `0` && ch <= `9` { + len0 *= 10 + len0 += int(ch - `0`) + i++ + continue + } + if ch == `.` { + status = .check_float + i++ + continue + } + status = .check_type + continue + } + + if status == .check_float { + if ch >= `0` && ch <= `9` { + len1 = int(ch - `0`) + status = .check_float_in + i++ + continue + } + status = .check_type + continue + } + + if status == .check_float_in { + if ch >= `0` && ch <= `9` { + len1 *= 10 + len1 += int(ch - `0`) + i++ + continue + } + status = .check_type + continue + } + + if status == .check_type { + if ch == `l` { + if ch1 == `0` { + ch1 = `l` + i++ + continue + } else { + ch2 = `l` + i++ + continue + } + } else if ch == `h` { + if ch1 == `0` { + ch1 = `h` + i++ + continue + } else { + ch2 = `h` + i++ + continue + } + } + // signed integer + else if ch in [`d`, `i`] { + mut d1 := u64(0) + mut positive := true + + // println("$ch1 $ch2") + match ch1 { + // h for 16 bit int + // hh fot 8 bit int + `h` { + if ch2 == `h` { + v_sprintf_panic(p_index, pt.len) + x := unsafe { *(&i8(pt[p_index])) } + positive = if x >= 0 { true } else { false } + d1 = if positive { u64(x) } else { u64(-x) } + } else { + x := unsafe { *(&i16(pt[p_index])) } + positive = if x >= 0 { true } else { false } + d1 = if positive { u64(x) } else { u64(-x) } + } + } + // l i64 + // ll i64 for now + `l` { + // placeholder for future 128bit integer code + /* + if ch2 == `l` { + v_sprintf_panic(p_index, pt.len) + x := *(&i128(pt[p_index])) + positive = if x >= 0 { true } else { false } + d1 = if positive { u128(x) } else { u128(-x) } + } else { + v_sprintf_panic(p_index, pt.len) + x := *(&i64(pt[p_index])) + positive = if x >= 0 { true } else { false } + d1 = if positive { u64(x) } else { u64(-x) } + } + */ + v_sprintf_panic(p_index, pt.len) + x := unsafe { *(&i64(pt[p_index])) } + positive = if x >= 0 { true } else { false } + d1 = if positive { u64(x) } else { u64(-x) } + } + // default int + else { + v_sprintf_panic(p_index, pt.len) + x := unsafe { *(&int(pt[p_index])) } + positive = if x >= 0 { true } else { false } + d1 = if positive { u64(x) } else { u64(-x) } + } + } + res.write_string(format_dec_old(d1, + pad_ch: pad_ch + len0: len0 + len1: 0 + positive: positive + sign_flag: sign + allign: allign + )) + status = .reset_params + p_index++ + i++ + ch1 = `0` + ch2 = `0` + continue + } + // unsigned integer + else if ch == `u` { + mut d1 := u64(0) + positive := true + v_sprintf_panic(p_index, pt.len) + match ch1 { + // h for 16 bit unsigned int + // hh fot 8 bit unsigned int + `h` { + if ch2 == `h` { + d1 = u64(unsafe { *(&byte(pt[p_index])) }) + } else { + d1 = u64(unsafe { *(&u16(pt[p_index])) }) + } + } + // l u64 + // ll u64 for now + `l` { + // placeholder for future 128bit integer code + /* + if ch2 == `l` { + d1 = u128(*(&u128(pt[p_index]))) + } else { + d1 = u64(*(&u64(pt[p_index]))) + } + */ + d1 = u64(unsafe { *(&u64(pt[p_index])) }) + } + // default int + else { + d1 = u64(unsafe { *(&u32(pt[p_index])) }) + } + } + + res.write_string(format_dec_old(d1, + pad_ch: pad_ch + len0: len0 + len1: 0 + positive: positive + sign_flag: sign + allign: allign + )) + status = .reset_params + p_index++ + i++ + continue + } + // hex + else if ch in [`x`, `X`] { + v_sprintf_panic(p_index, pt.len) + mut s := '' + match ch1 { + // h for 16 bit int + // hh fot 8 bit int + `h` { + if ch2 == `h` { + x := unsafe { *(&i8(pt[p_index])) } + s = x.hex() + } else { + x := unsafe { *(&i16(pt[p_index])) } + s = x.hex() + } + } + // l i64 + // ll i64 for now + `l` { + // placeholder for future 128bit integer code + /* + if ch2 == `l` { + x := *(&i128(pt[p_index])) + s = x.hex() + } else { + x := *(&i64(pt[p_index])) + s = x.hex() + } + */ + x := unsafe { *(&i64(pt[p_index])) } + s = x.hex() + } + else { + x := unsafe { *(&int(pt[p_index])) } + s = x.hex() + } + } + + if ch == `X` { + s = s.to_upper() + } + + res.write_string(format_str(s, + pad_ch: pad_ch + len0: len0 + len1: 0 + positive: true + sign_flag: false + allign: allign + )) + status = .reset_params + p_index++ + i++ + continue + } + + // float and double + if ch in [`f`, `F`] { + v_sprintf_panic(p_index, pt.len) + x := unsafe { *(&f64(pt[p_index])) } + positive := x >= f64(0.0) + len1 = if len1 >= 0 { len1 } else { def_len1 } + s := format_fl_old(f64(x), + pad_ch: pad_ch + len0: len0 + len1: len1 + positive: positive + sign_flag: sign + allign: allign + ) + res.write_string(if ch == `F` { s.to_upper() } else { s }) + status = .reset_params + p_index++ + i++ + continue + } else if ch in [`e`, `E`] { + v_sprintf_panic(p_index, pt.len) + x := unsafe { *(&f64(pt[p_index])) } + positive := x >= f64(0.0) + len1 = if len1 >= 0 { len1 } else { def_len1 } + s := format_es_old(f64(x), + pad_ch: pad_ch + len0: len0 + len1: len1 + positive: positive + sign_flag: sign + allign: allign + ) + res.write_string(if ch == `E` { s.to_upper() } else { s }) + status = .reset_params + p_index++ + i++ + continue + } else if ch in [`g`, `G`] { + v_sprintf_panic(p_index, pt.len) + x := unsafe { *(&f64(pt[p_index])) } + positive := x >= f64(0.0) + mut s := '' + tx := fabs(x) + if tx < 999_999.0 && tx >= 0.00001 { + // println("Here g format_fl [$tx]") + len1 = if len1 >= 0 { len1 + 1 } else { def_len1 } + s = format_fl_old(x, + pad_ch: pad_ch + len0: len0 + len1: len1 + positive: positive + sign_flag: sign + allign: allign + rm_tail_zero: true + ) + } else { + len1 = if len1 >= 0 { len1 + 1 } else { def_len1 } + s = format_es_old(x, + pad_ch: pad_ch + len0: len0 + len1: len1 + positive: positive + sign_flag: sign + allign: allign + rm_tail_zero: true + ) + } + res.write_string(if ch == `G` { s.to_upper() } else { s }) + status = .reset_params + p_index++ + i++ + continue + } + // string + else if ch == `s` { + v_sprintf_panic(p_index, pt.len) + s1 := unsafe { *(&string(pt[p_index])) } + pad_ch = ` ` + res.write_string(format_str(s1, + pad_ch: pad_ch + len0: len0 + len1: 0 + positive: true + sign_flag: false + allign: allign + )) + status = .reset_params + p_index++ + i++ + continue + } + } + + status = .reset_params + p_index++ + i++ + } + + if p_index != pt.len { + panic('$p_index % conversion specifiers, but given $pt.len args') + } + + return res.str() +} + +[inline] +fn v_sprintf_panic(idx int, len int) { + if idx >= len { + panic('${idx + 1} % conversion specifiers, but given only $len args') + } +} + +fn fabs(x f64) f64 { + if x < 0.0 { + return -x + } + return x +} + +// strings.Builder version of format_fl +[manualfree] +pub fn format_fl_old(f f64, p BF_param) string { + unsafe { + mut s := '' + // mut fs := "1.2343" + mut fs := f64_to_str_lnd1(if f >= 0.0 { f } else { -f }, p.len1) + // println("Dario") + // println(fs) + + // error!! + if fs[0] == `[` { + s.free() + return fs + } + + if p.rm_tail_zero { + tmp := fs + fs = remove_tail_zeros_old(fs) + tmp.free() + } + mut res := strings.new_builder(if p.len0 > fs.len { p.len0 } else { fs.len }) + + mut sign_len_diff := 0 + if p.pad_ch == `0` { + if p.positive { + if p.sign_flag { + res.write_b(`+`) + sign_len_diff = -1 + } + } else { + res.write_b(`-`) + sign_len_diff = -1 + } + tmp := s + s = fs.clone() + tmp.free() + } else { + if p.positive { + if p.sign_flag { + tmp := s + s = '+' + fs + tmp.free() + } else { + tmp := s + s = fs.clone() + tmp.free() + } + } else { + tmp := s + s = '-' + fs + tmp.free() + } + } + + dif := p.len0 - s.len + sign_len_diff + + if p.allign == .right { + for i1 := 0; i1 < dif; i1++ { + res.write_b(p.pad_ch) + } + } + res.write_string(s) + if p.allign == .left { + for i1 := 0; i1 < dif; i1++ { + res.write_b(p.pad_ch) + } + } + + s.free() + fs.free() + tmp_res := res.str() + res.free() + return tmp_res + } +} + +[manualfree] +pub fn format_es_old(f f64, p BF_param) string { + unsafe { + mut s := '' + mut fs := f64_to_str_pad(if f > 0 { f } else { -f }, p.len1) + if p.rm_tail_zero { + fs = remove_tail_zeros_old(fs) + } + mut res := strings.new_builder(if p.len0 > fs.len { p.len0 } else { fs.len }) + + mut sign_len_diff := 0 + if p.pad_ch == `0` { + if p.positive { + if p.sign_flag { + res.write_b(`+`) + sign_len_diff = -1 + } + } else { + res.write_b(`-`) + sign_len_diff = -1 + } + tmp := s + s = fs.clone() + tmp.free() + } else { + if p.positive { + if p.sign_flag { + tmp := s + s = '+' + fs + tmp.free() + } else { + tmp := s + s = fs.clone() + tmp.free() + } + } else { + tmp := s + s = '-' + fs + tmp.free() + } + } + + dif := p.len0 - s.len + sign_len_diff + if p.allign == .right { + for i1 := 0; i1 < dif; i1++ { + res.write_b(p.pad_ch) + } + } + res.write_string(s) + if p.allign == .left { + for i1 := 0; i1 < dif; i1++ { + res.write_b(p.pad_ch) + } + } + s.free() + fs.free() + tmp_res := res.str() + res.free() + return tmp_res + } +} + +pub fn remove_tail_zeros_old(s string) string { + mut i := 0 + mut last_zero_start := -1 + mut dot_pos := -1 + mut in_decimal := false + mut prev_ch := byte(0) + for i < s.len { + ch := unsafe { s.str[i] } + if ch == `.` { + in_decimal = true + dot_pos = i + } else if in_decimal { + if ch == `0` && prev_ch != `0` { + last_zero_start = i + } else if ch >= `1` && ch <= `9` { + last_zero_start = -1 + } else if ch == `e` { + break + } + } + prev_ch = ch + i++ + } + + mut tmp := '' + if last_zero_start > 0 { + if last_zero_start == dot_pos + 1 { + tmp = s[..dot_pos] + s[i..] + } else { + tmp = s[..last_zero_start] + s[i..] + } + } else { + tmp = s + } + if unsafe { tmp.str[tmp.len - 1] } == `.` { + return tmp[..tmp.len - 1] + } + return tmp +} + +// max int64 9223372036854775807 +pub fn format_dec_old(d u64, p BF_param) string { + mut s := '' + mut res := strings.new_builder(20) + mut sign_len_diff := 0 + if p.pad_ch == `0` { + if p.positive { + if p.sign_flag { + res.write_b(`+`) + sign_len_diff = -1 + } + } else { + res.write_b(`-`) + sign_len_diff = -1 + } + s = d.str() + } else { + if p.positive { + if p.sign_flag { + s = '+' + d.str() + } else { + s = d.str() + } + } else { + s = '-' + d.str() + } + } + dif := p.len0 - s.len + sign_len_diff + + if p.allign == .right { + for i1 := 0; i1 < dif; i1++ { + res.write_b(p.pad_ch) + } + } + res.write_string(s) + if p.allign == .left { + for i1 := 0; i1 < dif; i1++ { + res.write_b(p.pad_ch) + } + } + return res.str() +} diff --git a/v_windows/v/old/vlib/strings/builder.js.v b/v_windows/v/old/vlib/strings/builder.js.v new file mode 100644 index 0000000..e445804 --- /dev/null +++ b/v_windows/v/old/vlib/strings/builder.js.v @@ -0,0 +1,51 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module strings + +pub struct Builder { +mut: + buf []byte +pub mut: + len int + initial_size int = 1 +} + +pub fn new_builder(initial_size int) Builder { + return Builder{ + buf: make(0, initial_size, sizeof(byte)) + initial_size: initial_size + } +} + +pub fn (mut b Builder) write_b(data byte) { + b.buf << data + b.len++ +} + +pub fn (mut b Builder) write_string(s string) { + b.buf.push_many(s.str, s.len) + // b.buf << []byte(s) // TODO + b.len += s.len +} + +pub fn (mut b Builder) writeln(s string) { + b.buf.push_many(s.str, s.len) + // b.buf << []byte(s) // TODO + b.buf << `\n` + b.len += s.len + 1 +} + +pub fn (b Builder) str() string { + x := &byte(b.buf.data) + return unsafe { x.vstring_with_len(b.len) } +} + +pub fn (mut b Builder) cut(n int) { + b.len -= n +} + +pub fn (mut b Builder) free() { + b.buf = make(0, b.initial_size, 1) + b.len = 0 +} diff --git a/v_windows/v/old/vlib/strings/builder.v b/v_windows/v/old/vlib/strings/builder.v new file mode 100644 index 0000000..cec4f6c --- /dev/null +++ b/v_windows/v/old/vlib/strings/builder.v @@ -0,0 +1,140 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module strings + +// strings.Builder is used to efficiently append many strings to a large +// dynamically growing buffer, then use the resulting large string. Using +// a string builder is much better for performance/memory usage than doing +// constantly string concatenation. +pub type Builder = []byte + +// new_builder returns a new string builder, with an initial capacity of `initial_size` +pub fn new_builder(initial_size int) Builder { + return Builder([]byte{cap: initial_size}) +} + +// write_ptr writes `len` bytes provided byteptr to the accumulated buffer +[unsafe] +pub fn (mut b Builder) write_ptr(ptr &byte, len int) { + if len == 0 { + return + } + unsafe { b.push_many(ptr, len) } +} + +// write_b appends a single `data` byte to the accumulated buffer +pub fn (mut b Builder) write_b(data byte) { + b << data +} + +// write implements the Writer interface +pub fn (mut b Builder) write(data []byte) ?int { + if data.len == 0 { + return 0 + } + b << data + return data.len +} + +[inline] +pub fn (b &Builder) byte_at(n int) byte { + return unsafe { (&[]byte(b))[n] } +} + +// write appends the string `s` to the buffer +[inline] +pub fn (mut b Builder) write_string(s string) { + if s.len == 0 { + return + } + unsafe { b.push_many(s.str, s.len) } + // for c in s { + // b.buf << c + // } + // b.buf << []byte(s) // TODO +} + +// go_back discards the last `n` bytes from the buffer +pub fn (mut b Builder) go_back(n int) { + b.trim(b.len - n) +} + +// cut_last cuts the last `n` bytes from the buffer and returns them +pub fn (mut b Builder) cut_last(n int) string { + cut_pos := b.len - n + x := unsafe { (&[]byte(b))[cut_pos..] } + res := x.bytestr() + b.trim(cut_pos) + return res +} + +// cut_to cuts the string after `pos` and returns it. +// if `pos` is superior to builder length, returns an empty string +// and cancel further operations +pub fn (mut b Builder) cut_to(pos int) string { + if pos > b.len { + return '' + } + return b.cut_last(b.len - pos) +} + +// go_back_to resets the buffer to the given position `pos` +// NB: pos should be < than the existing buffer length. +pub fn (mut b Builder) go_back_to(pos int) { + b.trim(pos) +} + +// writeln appends the string `s`, and then a newline character. +[inline] +pub fn (mut b Builder) writeln(s string) { + // for c in s { + // b.buf << c + // } + if s.len > 0 { + unsafe { b.push_many(s.str, s.len) } + } + // b.buf << []byte(s) // TODO + b << byte(`\n`) +} + +// last_n(5) returns 'world' +// buf == 'hello world' +pub fn (b &Builder) last_n(n int) string { + if n > b.len { + return '' + } + x := unsafe { (&[]byte(b))[b.len - n..] } + return x.bytestr() +} + +// after(6) returns 'world' +// buf == 'hello world' +pub fn (b &Builder) after(n int) string { + if n >= b.len { + return '' + } + x := unsafe { (&[]byte(b))[n..] } + return x.bytestr() +} + +// str returns a copy of all of the accumulated buffer content. +// NB: after a call to b.str(), the builder b should not be +// used again, you need to call b.free() first, or just leave +// it to be freed by -autofree when it goes out of scope. +// The returned string *owns* its own separate copy of the +// accumulated data that was in the string builder, before the +// .str() call. +pub fn (mut b Builder) str() string { + b << byte(0) + bcopy := unsafe { &byte(memdup(b.data, b.len)) } + s := unsafe { bcopy.vstring_with_len(b.len - 1) } + b.trim(0) + return s +} + +// free is for manually freeing the contents of the buffer +[unsafe] +pub fn (mut b Builder) free() { + unsafe { free(b.data) } +} diff --git a/v_windows/v/old/vlib/strings/builder_test.v b/v_windows/v/old/vlib/strings/builder_test.v new file mode 100644 index 0000000..a714722 --- /dev/null +++ b/v_windows/v/old/vlib/strings/builder_test.v @@ -0,0 +1,96 @@ +import strings + +type MyInt = int + +fn test_sb() { + mut sb := strings.new_builder(100) + sb.write_string('hi') + sb.write_string('!') + sb.write_string('hello') + assert sb.len == 8 + sb_end := sb.str() + assert sb_end == 'hi!hello' + assert sb.len == 0 + /// + sb = strings.new_builder(10) + sb.write_string('a') + sb.write_string('b') + assert sb.len == 2 + assert sb.str() == 'ab' + // Test interpolation optimization + sb = strings.new_builder(10) + x := 10 + y := MyInt(20) + sb.writeln('x = $x y = $y') + res := sb.str() + assert res[res.len - 1] == `\n` + println('"$res"') + assert res.trim_space() == 'x = 10 y = 20' + // + sb = strings.new_builder(10) + sb.write_string('x = $x y = $y') + assert sb.str() == 'x = 10 y = 20' + //$if !windows { + sb = strings.new_builder(10) + sb.write_string('123456') + last_2 := sb.cut_last(2) + assert last_2 == '56' + final_sb := sb.str() + assert final_sb == '1234' + //} +} + +const ( + maxn = 100000 +) + +fn test_big_sb() { + mut sb := strings.new_builder(100) + mut sb2 := strings.new_builder(10000) + for i in 0 .. maxn { + sb.writeln(i.str()) + sb2.write_string('+') + } + s := sb.str() + lines := s.split_into_lines() + assert lines.len == maxn + assert lines[0] == '0' + assert lines[1] == '1' + assert lines[777] == '777' + assert lines[98765] == '98765' + println(sb2.len) + assert sb2.len == maxn +} + +fn test_byte_write() { + mut sb := strings.new_builder(100) + temp_str := 'byte testing' + mut count := 0 + for word in temp_str { + sb.write_b(word) + count++ + assert count == sb.len + } + sb_final := sb.str() + assert sb_final == temp_str +} + +fn test_strings_builder_reuse() { + mut sb := strings.new_builder(256) + sb.write_string('world') + assert sb.str() == 'world' + sb.write_string('hello') + assert sb.str() == 'hello' +} + +fn test_cut_to() { + mut sb := strings.new_builder(16) + sb.write_string('hello') + assert sb.cut_to(3) == 'lo' + assert sb.len == 3 + assert sb.cut_to(3) == '' + assert sb.len == 3 + assert sb.cut_to(0) == 'hel' + assert sb.cut_to(32) == '' + assert sb.len == 0 +} diff --git a/v_windows/v/old/vlib/strings/similarity.v b/v_windows/v/old/vlib/strings/similarity.v new file mode 100644 index 0000000..8d8de95 --- /dev/null +++ b/v_windows/v/old/vlib/strings/similarity.v @@ -0,0 +1,69 @@ +module strings + +// #-js +// use levenshtein distance algorithm to calculate +// the distance between between two strings (lower is closer) +pub fn levenshtein_distance(a string, b string) int { + mut f := [0].repeat(b.len + 1) + for j in 0 .. f.len { + f[j] = j + } + for ca in a { + mut j := 1 + mut fj1 := f[0] + f[0]++ + for cb in b { + mut mn := if f[j] + 1 <= f[j - 1] + 1 { f[j] + 1 } else { f[j - 1] + 1 } + if cb != ca { + mn = if mn <= fj1 + 1 { mn } else { fj1 + 1 } + } else { + mn = if mn <= fj1 { mn } else { fj1 } + } + fj1 = f[j] + f[j] = mn + j++ + } + } + return f[f.len - 1] +} + +// use levenshtein distance algorithm to calculate +// how similar two strings are as a percentage (higher is closer) +pub fn levenshtein_distance_percentage(a string, b string) f32 { + d := levenshtein_distance(a, b) + l := if a.len >= b.len { a.len } else { b.len } + return (1.00 - f32(d) / f32(l)) * 100.00 +} + +// implementation of Sørensen–Dice coefficient. +// find the similarity between two strings. +// returns coefficient between 0.0 (not similar) and 1.0 (exact match). +pub fn dice_coefficient(s1 string, s2 string) f32 { + if s1.len == 0 || s2.len == 0 { + return 0.0 + } + if s1 == s2 { + return 1.0 + } + if s1.len < 2 || s2.len < 2 { + return 0.0 + } + a := if s1.len > s2.len { s1 } else { s2 } + b := if a == s1 { s2 } else { s1 } + mut first_bigrams := map[string]int{} + for i in 0 .. a.len - 1 { + bigram := a[i..i + 2] + q := if bigram in first_bigrams { first_bigrams[bigram] + 1 } else { 1 } + first_bigrams[bigram] = q + } + mut intersection_size := 0 + for i in 0 .. b.len - 1 { + bigram := b[i..i + 2] + count := if bigram in first_bigrams { first_bigrams[bigram] } else { 0 } + if count > 0 { + first_bigrams[bigram] = count - 1 + intersection_size++ + } + } + return (2.0 * f32(intersection_size)) / (f32(a.len) + f32(b.len) - 2) +} diff --git a/v_windows/v/old/vlib/strings/similarity_test.v b/v_windows/v/old/vlib/strings/similarity_test.v new file mode 100644 index 0000000..965da45 --- /dev/null +++ b/v_windows/v/old/vlib/strings/similarity_test.v @@ -0,0 +1,13 @@ +import strings + +fn test_levenshtein_distance() { + assert strings.levenshtein_distance('', '') == 0 + assert strings.levenshtein_distance('one', 'one') == 0 + assert strings.levenshtein_distance('', 'two') == 3 + assert strings.levenshtein_distance('three', '') == 5 + assert strings.levenshtein_distance('bananna', '') == 7 + assert strings.levenshtein_distance('cats', 'hats') == 1 + assert strings.levenshtein_distance('hugs', 'shrugs') == 2 + assert strings.levenshtein_distance('broom', 'shroom') == 2 + assert strings.levenshtein_distance('flomax', 'volmax') == 3 +} diff --git a/v_windows/v/old/vlib/strings/strings.c.v b/v_windows/v/old/vlib/strings/strings.c.v new file mode 100644 index 0000000..c020f5b --- /dev/null +++ b/v_windows/v/old/vlib/strings/strings.c.v @@ -0,0 +1,38 @@ +module strings + +// strings.repeat - fill a string with `n` repetitions of the character `c` +pub fn repeat(c byte, n int) string { + if n <= 0 { + return '' + } + mut bytes := unsafe { malloc_noscan(n + 1) } + unsafe { + C.memset(bytes, c, n) + bytes[n] = `0` + } + return unsafe { bytes.vstring_with_len(n) } +} + +// strings.repeat_string - gives you `n` repetitions of the substring `s` +// NB: strings.repeat, that repeats a single byte, is between 2x +// and 24x faster than strings.repeat_string called for a 1 char string. +pub fn repeat_string(s string, n int) string { + if n <= 0 || s.len == 0 { + return '' + } + slen := s.len + blen := slen * n + mut bytes := unsafe { malloc_noscan(blen + 1) } + for bi in 0 .. n { + bislen := bi * slen + for si in 0 .. slen { + unsafe { + bytes[bislen + si] = s[si] + } + } + } + unsafe { + bytes[blen] = `0` + } + return unsafe { bytes.vstring_with_len(blen) } +} diff --git a/v_windows/v/old/vlib/strings/strings.js.v b/v_windows/v/old/vlib/strings/strings.js.v new file mode 100644 index 0000000..2681b94 --- /dev/null +++ b/v_windows/v/old/vlib/strings/strings.js.v @@ -0,0 +1,17 @@ +module strings + +pub fn repeat(c byte, n int) string { + if n <= 0 { + return '' + } + arr := [c].repeat(n) + return arr.bytestr() +} + +pub fn repeat_string(s string, n int) string { + /* + // TODO: uncomment this. It is commented for now, so that `v doc strings` works + res := # s.repeat(n) + return res + */ +} diff --git a/v_windows/v/old/vlib/strings/strings.v b/v_windows/v/old/vlib/strings/strings.v new file mode 100644 index 0000000..c01dd90 --- /dev/null +++ b/v_windows/v/old/vlib/strings/strings.v @@ -0,0 +1,13 @@ +module strings + +// import rand +// random returns a random string with `n` characters +/* +pub fn random(n int) string { + buf := vmalloc(n) + for i in 0..n { + buf[i] = rand.next() + } + return tos(buf) +} +*/ diff --git a/v_windows/v/old/vlib/strings/strings_test.v b/v_windows/v/old/vlib/strings/strings_test.v new file mode 100644 index 0000000..ff5ddf5 --- /dev/null +++ b/v_windows/v/old/vlib/strings/strings_test.v @@ -0,0 +1,14 @@ +import strings + +fn test_repeat() { + assert strings.repeat(`x`, 10) == 'xxxxxxxxxx' + assert strings.repeat(`a`, 1) == 'a' + assert strings.repeat(`a`, 0) == '' +} + +fn test_repeat_string() { + assert strings.repeat_string('abc', 3) == 'abcabcabc' + assert strings.repeat_string('abc', 1) == 'abc' + assert strings.repeat_string('abc', 0) == '' + assert strings.repeat_string('', 200) == '' +} diff --git a/v_windows/v/old/vlib/strings/textscanner/textscanner.v b/v_windows/v/old/vlib/strings/textscanner/textscanner.v new file mode 100644 index 0000000..5525137 --- /dev/null +++ b/v_windows/v/old/vlib/strings/textscanner/textscanner.v @@ -0,0 +1,154 @@ +module textscanner + +// TextScanner simplifies writing small scanners/parsers +// by providing safe methods to scan texts character by +// character, peek for the next characters, go back, etc. +pub struct TextScanner { +pub: + input string + ilen int +mut: + pos int // current position; pos is *always* kept in [0,ilen] +} + +// new returns a stack allocated instance of TextScanner. +pub fn new(input string) TextScanner { + return TextScanner{ + input: input + ilen: input.len + } +} + +// free frees all allocated resources. +[unsafe] +pub fn (mut ss TextScanner) free() { + unsafe { + ss.input.free() + } +} + +// remaining returns how many characters remain from current position. +[inline] +pub fn (ss &TextScanner) remaining() int { + return ss.ilen - ss.pos +} + +// next returns the next character code from the input text. +// next returns `-1` if it can't reach the next character. +// next advances the scanner position. +[direct_array_access; inline] +pub fn (mut ss TextScanner) next() int { + if ss.pos < ss.ilen { + opos := ss.pos + ss.pos++ + return ss.input[opos] + } + return -1 +} + +// skip skips one character ahead; `skip()` is slightly faster than `.next()`. +// `skip()` does not return a result. +[inline] +pub fn (mut ss TextScanner) skip() { + if ss.pos + 1 < ss.ilen { + ss.pos++ + } +} + +// skip_n skips ahead `n` characters, stopping at the end of the input. +[inline] +pub fn (mut ss TextScanner) skip_n(n int) { + ss.pos += n + if ss.pos > ss.ilen { + ss.pos = ss.ilen + } +} + +// peek returns the *next* character code from the input text. +// peek returns `-1` if it can't peek the next character. +// unlike `next()`, `peek()` does not change the state of the scanner. +[direct_array_access; inline] +pub fn (ss &TextScanner) peek() int { + if ss.pos < ss.ilen { + return ss.input[ss.pos] + } + return -1 +} + +// peek_n returns the character code from the input text at position + `n`. +// peek_n returns `-1` if it can't peek `n` characters ahead. +// ts.peek_n(0) == ts.current() . +// ts.peek_n(1) == ts.peek() . +[direct_array_access; inline] +pub fn (ss &TextScanner) peek_n(n int) int { + if ss.pos + n < ss.ilen { + return ss.input[ss.pos + n] + } + return -1 +} + +// back goes back one character from the current scanner position. +[inline] +pub fn (mut ss TextScanner) back() { + if ss.pos > 0 { + ss.pos-- + } +} + +// back_n goes back `n` characters from the current scanner position. +pub fn (mut ss TextScanner) back_n(n int) { + ss.pos -= n + if ss.pos < 0 { + ss.pos = 0 + } + if ss.pos > ss.ilen { + ss.pos = ss.ilen + } +} + +// peek_back returns the *previous* character code from the input text. +// peek_back returns `-1` if it can't peek the previous character. +// unlike `back()`, `peek_back()` does not change the state of the scanner. +[direct_array_access; inline] +pub fn (ss &TextScanner) peek_back() int { + return ss.peek_back_n(1) +} + +// peek_back_n returns the character code from the input text at position - `n`. +// peek_back_n returns `-1` if it can't peek `n` characters back. +// ts.peek_back_n(0) == ts.current() +// ts.peek_back_n(1) == ts.peek_back() +[direct_array_access; inline] +pub fn (ss &TextScanner) peek_back_n(n int) int { + offset := n + 1 + if ss.pos >= offset { + return ss.input[ss.pos - offset] + } + return -1 +} + +// current returns the current character code from the input text. +// current returns `-1` at the start of the input text. +// NB: after `c := ts.next()`, `ts.current()` will also return `c`. +[direct_array_access; inline] +pub fn (mut ss TextScanner) current() int { + if ss.pos > 0 { + return ss.input[ss.pos - 1] + } + return -1 +} + +// reset resets the internal state of the scanner +// After calling .reset(), .next() will start reading +// again from the start of the input text. +pub fn (mut ss TextScanner) reset() { + ss.pos = 0 +} + +// goto_end has the same effect as `for ts.next() != -1 {}` +// i.e. after calling .goto_end(), the scanner will be at +// the end of the input text. Further .next() calls will +// return -1, unless you go back. +pub fn (mut ss TextScanner) goto_end() { + ss.pos = ss.ilen +} diff --git a/v_windows/v/old/vlib/strings/textscanner/textscanner_test.v b/v_windows/v/old/vlib/strings/textscanner/textscanner_test.v new file mode 100644 index 0000000..e9d2487 --- /dev/null +++ b/v_windows/v/old/vlib/strings/textscanner/textscanner_test.v @@ -0,0 +1,159 @@ +import strings.textscanner + +fn test_remaining() { + mut s := textscanner.new('abc') + assert s.remaining() == 3 + s.next() + s.next() + assert s.remaining() == 1 + s.next() + assert s.remaining() == 0 + s.next() + s.next() + assert s.remaining() == 0 + s.reset() + assert s.remaining() == 3 +} + +fn test_next() { + mut s := textscanner.new('abc') + assert s.next() == `a` + assert s.next() == `b` + assert s.next() == `c` + assert s.next() == -1 + assert s.next() == -1 + assert s.next() == -1 +} + +fn test_skip() { + mut s := textscanner.new('abc') + assert s.next() == `a` + s.skip() + assert s.next() == `c` + assert s.next() == -1 +} + +fn test_skip_n() { + mut s := textscanner.new('abc') + s.skip_n(2) + assert s.next() == `c` + assert s.next() == -1 +} + +fn test_peek() { + mut s := textscanner.new('abc') + assert s.peek() == `a` + assert s.peek() == `a` + assert s.peek() == `a` + // + assert s.next() == `a` + assert s.next() == `b` + assert s.next() == `c` + assert s.next() == -1 +} + +fn test_peek_n() { + mut s := textscanner.new('abc') + assert s.peek_n(0) == `a` + assert s.peek_n(1) == `b` + assert s.peek_n(2) == `c` + assert s.peek_n(3) == -1 + assert s.peek_n(4) == -1 + // + assert s.next() == `a` + assert s.next() == `b` + assert s.next() == `c` + assert s.next() == -1 +} + +fn test_back() { + mut s := textscanner.new('abc') + assert s.next() == `a` + s.back() + assert s.next() == `a` + assert s.next() == `b` + s.back() + assert s.next() == `b` + assert s.next() == `c` + assert s.next() == -1 +} + +fn test_back_n() { + mut s := textscanner.new('abc') + assert s.next() == `a` + s.back_n(10) + assert s.next() == `a` + assert s.next() == `b` + assert s.next() == `c` + s.back_n(2) + assert s.next() == `b` +} + +fn test_peek_back() { + mut s := textscanner.new('abc') + assert s.next() == `a` + assert s.next() == `b` + // check that calling .peek_back() multiple times + // does not change the state: + assert s.peek_back() == `a` + assert s.peek_back() == `a` + assert s.peek_back() == `a` + // advance, then peek_back again: + assert s.next() == `c` + assert s.peek_back() == `b` + // peeking before the start: + s.reset() + assert s.peek_back() == -1 + // peeking right at the end: + s.goto_end() + assert s.peek_back() == `b` +} + +fn test_peek_back_n() { + mut s := textscanner.new('abc') + s.goto_end() + assert s.peek_back_n(0) == `c` + assert s.peek_back_n(1) == `b` + assert s.peek_back_n(2) == `a` + assert s.peek_back_n(3) == -1 + assert s.peek_back_n(4) == -1 +} + +fn test_reset() { + mut s := textscanner.new('abc') + assert s.next() == `a` + s.next() + s.next() + assert s.next() == -1 + s.reset() + assert s.next() == `a` +} + +fn test_current() { + mut s := textscanner.new('abc') + assert s.current() == -1 + assert s.next() == `a` + assert s.current() == `a` + assert s.current() == `a` + assert s.peek_back() == -1 + assert s.next() == `b` + assert s.current() == `b` + assert s.current() == `b` + assert s.peek_back() == `a` + assert s.next() == `c` + assert s.current() == `c` + assert s.next() == -1 + assert s.current() == `c` + assert s.next() == -1 + assert s.current() == `c` + s.reset() + assert s.current() == -1 + assert s.next() == `a` + assert s.current() == `a` +} + +fn test_goto_end() { + mut s := textscanner.new('abc') + s.goto_end() + assert s.current() == `c` +} diff --git a/v_windows/v/old/vlib/sync/array_rlock_test.v b/v_windows/v/old/vlib/sync/array_rlock_test.v new file mode 100644 index 0000000..ad4f778 --- /dev/null +++ b/v_windows/v/old/vlib/sync/array_rlock_test.v @@ -0,0 +1,38 @@ +fn test_shared_modification() { + shared foo := &[2, 0, 5] + lock foo { + unsafe { + foo[1] = 3 + foo[0] *= 7 + foo[1]-- + foo[2] -= 2 + } + } + rlock foo { + unsafe { + assert foo[0] == 14 + assert foo[1] == 2 + assert foo[2] == 3 + } + } +} + +[direct_array_access] +fn test_shared_direct_modification() { + shared foo := &[2, 0, 5] + lock foo { + unsafe { + foo[1] = 3 + foo[0] *= 7 + foo[1]-- + foo[2] -= 2 + } + } + rlock foo { + unsafe { + assert foo[0] == 14 + assert foo[1] == 2 + assert foo[2] == 3 + } + } +} diff --git a/v_windows/v/old/vlib/sync/atomic2/atomic.v b/v_windows/v/old/vlib/sync/atomic2/atomic.v new file mode 100644 index 0000000..2ff64f2 --- /dev/null +++ b/v_windows/v/old/vlib/sync/atomic2/atomic.v @@ -0,0 +1,88 @@ +module atomic2 + +/* +Implements the atomic operations. For now TCC does not support +the atomic versions on nix so it uses locks to simulate the same behavor. +On windows tcc can simulate with other atomic operations. + +The @VEXEROOT/thirdparty/stdatomic contains compability header files +for stdatomic that supports both nix, windows and c++. + +This implementations should be regarded as alpha stage and be +further tested. +*/ + +#flag windows -I @VEXEROOT/thirdparty/stdatomic/win +#flag linux -I @VEXEROOT/thirdparty/stdatomic/nix +#flag darwin -I @VEXEROOT/thirdparty/stdatomic/nix +#flag freebsd -I @VEXEROOT/thirdparty/stdatomic/nix +#flag solaris -I @VEXEROOT/thirdparty/stdatomic/nix + +$if linux { + $if tinyc { + $if amd64 { + // most Linux distributions have /usr/lib/libatomic.so, but Ubuntu uses gcc version specific dir + #flag -L/usr/lib/gcc/x86_64-linux-gnu/6 + #flag -L/usr/lib/gcc/x86_64-linux-gnu/7 + #flag -L/usr/lib/gcc/x86_64-linux-gnu/8 + #flag -L/usr/lib/gcc/x86_64-linux-gnu/9 + #flag -L/usr/lib/gcc/x86_64-linux-gnu/10 + #flag -L/usr/lib/gcc/x86_64-linux-gnu/11 + #flag -L/usr/lib/gcc/x86_64-linux-gnu/12 + } $else $if arm64 { + #flag -L/usr/lib/gcc/aarch64-linux-gnu/6 + #flag -L/usr/lib/gcc/aarch64-linux-gnu/7 + #flag -L/usr/lib/gcc/aarch64-linux-gnu/8 + #flag -L/usr/lib/gcc/aarch64-linux-gnu/9 + #flag -L/usr/lib/gcc/aarch64-linux-gnu/10 + #flag -L/usr/lib/gcc/aarch64-linux-gnu/11 + #flag -L/usr/lib/gcc/aarch64-linux-gnu/12 + } + #flag -latomic + } +} +#include +// add_u64 adds provided delta as an atomic operation +pub fn add_u64(ptr &u64, delta int) bool { + res := C.atomic_fetch_add_u64(voidptr(ptr), delta) + return res == 0 +} + +// sub_u64 subtracts provided delta as an atomic operation +pub fn sub_u64(ptr &u64, delta int) bool { + res := C.atomic_fetch_sub_u64(voidptr(ptr), delta) + return res == 0 +} + +// add_i64 adds provided delta as an atomic operation +pub fn add_i64(ptr &i64, delta int) bool { + res := C.atomic_fetch_add_u64(voidptr(ptr), delta) + return res == 0 +} + +// add_i64 subtracts provided delta as an atomic operation +pub fn sub_i64(ptr &i64, delta int) bool { + res := C.atomic_fetch_sub_u64(voidptr(ptr), delta) + return res == 0 +} + +// atomic store/load operations have to be used when there might be another concurrent access +// atomicall set a value +pub fn store_u64(ptr &u64, val u64) { + C.atomic_store_u64(voidptr(ptr), val) +} + +// atomicall get a value +pub fn load_u64(ptr &u64) u64 { + return C.atomic_load_u64(voidptr(ptr)) +} + +// atomicall set a value +pub fn store_i64(ptr &i64, val i64) { + C.atomic_store_u64(voidptr(ptr), val) +} + +// atomicall get a value +pub fn load_i64(ptr &i64) i64 { + return i64(C.atomic_load_u64(voidptr(ptr))) +} diff --git a/v_windows/v/old/vlib/sync/atomic2/atomic_test.v b/v_windows/v/old/vlib/sync/atomic2/atomic_test.v new file mode 100644 index 0000000..7a5ffd8 --- /dev/null +++ b/v_windows/v/old/vlib/sync/atomic2/atomic_test.v @@ -0,0 +1,105 @@ +import sync.atomic2 +import sync + +const ( + iterations_per_cycle = 100_000 +) + +struct Counter { +mut: + counter u64 +} + +// without proper syncronization this would fail +fn test_count_10_times_1_cycle_should_result_10_cycles_with_sync() { + desired_iterations := 10 * iterations_per_cycle + mut wg := sync.new_waitgroup() + mut counter := &Counter{} + wg.add(10) + for i := 0; i < 10; i++ { + go count_one_cycle(mut counter, mut wg) + } + wg.wait() + assert counter.counter == desired_iterations + eprintln(' with synchronization the counter is: ${counter.counter:10} , expectedly == ${desired_iterations:10}') +} + +// This test just to make sure that we have an anti-test to prove it works +fn test_count_10_times_1_cycle_should_not_be_10_cycles_without_sync() { + desired_iterations := 10 * iterations_per_cycle + mut wg := sync.new_waitgroup() + mut counter := &Counter{} + wg.add(10) + for i := 0; i < 10; i++ { + go count_one_cycle_without_sync(mut counter, mut wg) + } + wg.wait() + // NB: we do not assert here, just print, because sometimes by chance counter.counter may be == desired_iterations + eprintln('without synchronization the counter is: ${counter.counter:10} , expectedly != ${desired_iterations:10}') +} + +fn test_count_plus_one_u64() { + mut c := u64(0) + atomic2.add_u64(&c, 1) + assert atomic2.load_u64(&c) == 1 +} + +fn test_count_plus_one_i64() { + mut c := i64(0) + atomic2.add_i64(&c, 1) + assert atomic2.load_i64(&c) == 1 +} + +fn test_count_plus_greater_than_one_u64() { + mut c := u64(0) + atomic2.add_u64(&c, 10) + assert atomic2.load_u64(&c) == 10 +} + +fn test_count_plus_greater_than_one_i64() { + mut c := i64(0) + atomic2.add_i64(&c, 10) + assert atomic2.load_i64(&c) == 10 +} + +fn test_count_minus_one_u64() { + mut c := u64(1) + atomic2.sub_u64(&c, 1) + assert atomic2.load_u64(&c) == 0 +} + +fn test_count_minus_one_i64() { + mut c := i64(0) + atomic2.sub_i64(&c, 1) + assert atomic2.load_i64(&c) == -1 +} + +fn test_count_minus_greater_than_one_u64() { + mut c := u64(0) + atomic2.store_u64(&c, 10) + atomic2.sub_u64(&c, 10) + assert atomic2.load_u64(&c) == 0 +} + +fn test_count_minus_greater_than_one_i64() { + mut c := i64(0) + atomic2.store_i64(&c, 10) + atomic2.sub_i64(&c, 20) + assert atomic2.load_i64(&c) == -10 +} + +// count_one_cycle counts the common counter iterations_per_cycle times in thread-safe way +fn count_one_cycle(mut counter Counter, mut group sync.WaitGroup) { + for i := 0; i < iterations_per_cycle; i++ { + atomic2.add_u64(&counter.counter, 1) + } + group.done() +} + +// count_one_cycle_without_sync counts the common counter iterations_per_cycle times in none thread-safe way +fn count_one_cycle_without_sync(mut counter Counter, mut group sync.WaitGroup) { + for i := 0; i < iterations_per_cycle; i++ { + counter.counter++ + } + group.done() +} diff --git a/v_windows/v/old/vlib/sync/bench/channel_bench_go.go b/v_windows/v/old/vlib/sync/bench/channel_bench_go.go new file mode 100644 index 0000000..a0afbbc --- /dev/null +++ b/v_windows/v/old/vlib/sync/bench/channel_bench_go.go @@ -0,0 +1,68 @@ +package main + +import "fmt" +import "log" +import "os" +import "time" +import "strconv" + +func assert_eq(a, b int64) { + if a != b { + log.Fatalf("assertion failed\nleft: %d, right: %d\n", a, b) + } +} + +func do_rec(ch chan int32, resch chan int64, n int32) { + var sum int64 + var i int32 + for i = 0; i < n; i++ { + sum += int64(<- ch) + } + fmt.Println(sum) + resch <- sum +} + +func do_send(ch chan int32, start, end int32) { + for i := start; i < end; i++ { + ch <- i + } +} + +func main() { + if len(os.Args) != 5 { + log.Fatalf("usage:\n\t%s \n", os.Args[0]) + } + nsend, _ := strconv.Atoi(os.Args[1]) + nrec, _ := strconv.Atoi(os.Args[2]) + buflen, _ := strconv.Atoi(os.Args[3]) + nobj, _ := strconv.Atoi(os.Args[4]) + stopwatch := time.Now() + ch := make(chan int32, buflen) + resch := make(chan int64, 0) + no := nobj + for i := 0; i < nrec; i++ { + n := no / (nrec - i) + go do_rec(ch, resch, int32(n)) + no -= n + } + assert_eq(int64(no), 0) + no = nobj + for i := 0; i < nsend; i++ { + n := no / (nsend - i) + end := no + no -= n + go do_send(ch, int32(no), int32(end)) + } + assert_eq(int64(no), 0) + var sum int64 + for i := 0; i < nrec; i++ { + sum += <-resch + } + elapsed := time.Now().Sub(stopwatch) + rate := float64(nobj)/float64(elapsed.Nanoseconds())*1000.0 + duration := 1.0e-09 * float64(elapsed.Nanoseconds()) + fmt.Printf("%d objects in %g s (%.2f objs/µs)\n", nobj, duration, rate) + expected_sum := int64(nobj)*int64(nobj-1)/2 + fmt.Printf("got: %d, expected: %d\n", sum, expected_sum) + assert_eq(sum, expected_sum) +} diff --git a/v_windows/v/old/vlib/sync/bench/channel_bench_v.v b/v_windows/v/old/vlib/sync/bench/channel_bench_v.v new file mode 100644 index 0000000..54dcfe9 --- /dev/null +++ b/v_windows/v/old/vlib/sync/bench/channel_bench_v.v @@ -0,0 +1,64 @@ +// Channel Benchmark +// +// `nobj` integers are sent thru a channel with queue length`buflen` +// using `nsend` sender threads and `nrec` receiver threads. +// +// The receive threads add all received numbers and send them to the +// main thread where the total sum is compare to the expected value. +import time +import os + +fn do_rec(ch chan int, resch chan i64, n int) { + mut sum := i64(0) + for _ in 0 .. n { + sum += <-ch + } + println(sum) + resch <- sum +} + +fn do_send(ch chan int, start int, end int) { + for i in start .. end { + ch <- i + } +} + +fn main() { + if os.args.len != 5 { + eprintln('usage:\n\t${os.args[0]} ') + exit(1) + } + nsend := os.args[1].int() + nrec := os.args[2].int() + buflen := os.args[3].int() + nobj := os.args[4].int() + stopwatch := time.new_stopwatch() + ch := chan int{cap: buflen} + resch := chan i64{} + mut no := nobj + for i in 0 .. nrec { + n := no / (nrec - i) + go do_rec(ch, resch, n) + no -= n + } + assert no == 0 + no = nobj + for i in 0 .. nsend { + n := no / (nsend - i) + end := no + no -= n + go do_send(ch, no, end) + } + assert no == 0 + mut sum := i64(0) + for _ in 0 .. nrec { + sum += <-resch + } + elapsed := stopwatch.elapsed() + rate := f64(nobj) / elapsed * time.microsecond + println('$nobj objects in ${f64(elapsed) / time.second} s (${rate:.2f} objs/µs)') + // use sum formula by Gauß to calculate the expected result + expected_sum := i64(nobj) * (nobj - 1) / 2 + println('got: $sum, expected: $expected_sum') + assert sum == expected_sum +} diff --git a/v_windows/v/old/vlib/sync/bench/many_writers_and_receivers_on_1_channel.v b/v_windows/v/old/vlib/sync/bench/many_writers_and_receivers_on_1_channel.v new file mode 100644 index 0000000..999bb1d --- /dev/null +++ b/v_windows/v/old/vlib/sync/bench/many_writers_and_receivers_on_1_channel.v @@ -0,0 +1,150 @@ +import os +import os.cmdline +import time +import sync + +// Usage: +// many_writers_and_receivers_on_1_channel [-readers 1] [-writers 4] [-chan_cap 100] [-iterations 25000] > results.csv +// +// You can then open results.csv in Excel/Calc and for example plot the first vs the second column. +enum EventKind { + push + pop +} + +struct Event { + is_set bool + id int + gtime u64 // nanoseconds + i int + kind EventKind + elapsed i64 // nanoseconds, elapsed after the previous event of the same kind +} + +struct Context { +mut: + n_iters int + n_readers int + n_writers int + // + pops_wg &sync.WaitGroup + pops []Event + // + pushes_wg &sync.WaitGroup + pushes []Event +} + +fn do_rec(ch chan int, id int, mut ctx Context) { + eprintln('start of do_rec id: $id') + mut timer_sw_x := time.new_stopwatch() + mut tmp := int(0) + mut i := int(0) + // NB: a single receiver thread can get slightly more + // than its fair share of sends, that is why + // the receiver's Event array is much larger, + // enough so a single receiver can potentially process all + // writers pushes, and it is partitioned over all of + // id, ctx.n_writers and n_iters: + n_iters := ctx.n_iters + base := id * n_iters * ctx.n_writers + for { + for ch.try_pop(tmp) == .success { + ctx.pops[base + i] = Event{ + is_set: true + id: id + gtime: time.sys_mono_now() + i: i + kind: .pop + elapsed: timer_sw_x.elapsed().nanoseconds() + } + timer_sw_x.restart() + i++ + if tmp == 1 { + ctx.pops_wg.done() + return + } + } + } +} + +fn do_send(ch chan int, id int, mut ctx Context) { + eprintln('start of do_send id: $id') + mut timer_sw_x := time.new_stopwatch() + n_iters := ctx.n_iters + base := n_iters * id // sender events can not overlap + for i := 0; i < n_iters; i++ { + idx := base + i + ctx.pushes[idx] = Event{ + is_set: true + id: id + gtime: time.sys_mono_now() + i: i + kind: .push + elapsed: timer_sw_x.elapsed().nanoseconds() + } + timer_sw_x.restart() + tmp := int(0) + ch <- tmp + } + ctx.pushes_wg.done() +} + +fn main() { + // + args := os.args[1..] + if '-h' in args || '--help' in args { + eprintln('Usage:\n many_writers_and_receivers_on_1_channel [-readers 1] [-writers 4] [-chan_cap 100] [-iterations 25000]') + exit(0) + } + n_iters := cmdline.option(args, '-iterations', '25000').int() + n_readers := cmdline.option(args, '-readers', '1').int() + n_writers := cmdline.option(args, '-writers', '4').int() + chan_cap := cmdline.option(args, '-chan_cap', '100').int() + eprintln('> n_iters, $n_iters, n_writers, $n_writers, n_readers, $n_readers, chan_cap, $chan_cap') + // + ch := chan int{cap: chan_cap} + max_number_of_pushes := n_writers * (n_iters + 2) + max_number_of_pops := max_number_of_pushes * n_readers + eprintln('> max_number_of_pushes, $max_number_of_pushes, max_number_of_pops (per receiver), $max_number_of_pops') + mut ctx := &Context{ + n_iters: n_iters + n_readers: n_readers + n_writers: n_writers + pushes_wg: sync.new_waitgroup() + pops_wg: sync.new_waitgroup() + pushes: []Event{len: max_number_of_pushes} + pops: []Event{len: max_number_of_pops} + } + ctx.pops_wg.add(n_readers) + for i := 0; i < n_readers; i++ { + go do_rec(ch, i, mut ctx) + } + ctx.pushes_wg.add(n_writers) + for i := 0; i < n_writers; i++ { + go do_send(ch, i, mut ctx) + } + ctx.pushes_wg.wait() + eprintln('>> all pushes done') + for i := 0; i < n_readers; i++ { + ch <- 1 + } + ctx.pops_wg.wait() + eprintln('>> all pops done') + mut all_events := []Event{} + all_events << ctx.pops + all_events << ctx.pushes + all_events.sort(a.elapsed < b.elapsed) + mut i := 0 + for e in all_events { + if !e.is_set { + continue + } + i++ + if e.kind == .pop { + println('${i:8} , ${e.elapsed:10}, ns , do_rec id:, ${e.id:3} , i=, ${e.i:5} , ${e.gtime:20}') + } + if e.kind == .push { + println('${i:8} , ${e.elapsed:10}, ns , do_send id:, ${e.id:3} , i=, ${e.i:5} , ${e.gtime:20}') + } + } +} diff --git a/v_windows/v/old/vlib/sync/bench/results.md b/v_windows/v/old/vlib/sync/bench/results.md new file mode 100644 index 0000000..882471c --- /dev/null +++ b/v_windows/v/old/vlib/sync/bench/results.md @@ -0,0 +1,48 @@ +# Channel Benchmark Results + +This documents lists several benchmark results for different platforms in order to +identify performance regressions and improvements. + +The are measured using the command + +``` +> channel_bench_* + +nsend ... number of threads that push objects into the channel +nrec .... number of threads that pop objects from the channel +buflen .. length of channel buffer queue - `0` means unbuffered channel +nobj .... number of objects to pass thru the channel +``` + +## AMD Ryzen 7 3800X, Ubuntu-20.04 x86_64 + +10000000 Objects transfered, results in Objects/µs + +| nsend | nrec | buflen | **V (gcc -O2)** | **V (clang)** | **V (tcc)** | **Go (golang)** | **Go (gccgo -O2)** | +| :---: | :---:| :---: | :---: | :---: | :---: | :---: | :---: | +| 1 | 1 | 0 | 1.97 | 1.63 | 2.08 | 4.65 | 0.56 | +| 1 | 1 | 100 | 3.05 | 2.29 | 1.93 | 18.90 | 6.08 | +| 4 | 4 | 0 | 0.87 | 0.90 | 0.99 | 1.84 | 0.84 | +| 4 | 4 | 100 | 3.35 | 3.07 | 2.92 | 7.43 | 3.71 | + +## AMD Ryzen 7 3800X, Windows 10 2004 x64 + +| nsend | nrec | buflen | **V (gcc -O2)** | **V (msvc /O2)** | **V (tcc)** | **Go (golang)** | +| :---: | :---:| :---: | :---: | :---: | :---: | :---: | +| 1 | 1 | 0 | 2.30 | 3.76 | 2.02 | 4.67 | +| 1 | 1 | 100 | 2.96 | 3.12 | 2.26 | 23.31 | +| 4 | 4 | 0 | 0.90 | 1.05 | 0.83 | 1.38 | +| 4 | 4 | 100 | 2.28 | 2.16 | 2.43 | 4.63 | + +## Raspberry Pi 3B+, Void Linux musl 32 bit + +10000000 Objects transfered, results in Objects/µs + +| nsend | nrec | buflen | **V (gcc -O2)** | **Go (golang)** | +| :---: | :---:| :---: | :---: | :---: | +| 1 | 1 | 0 | 0.37 | 0.21 | +| 1 | 1 | 100 | 1.03 | 0.74 | +| 4 | 4 | 0 | 0.04 | 0.38 | +| 4 | 4 | 100 | 2.78 | 2.63 | +| 2 | 2 | 0 | 0.05 | 0.38 | +| 2 | 2 | 100 | 1.26 | 0.75 | diff --git a/v_windows/v/old/vlib/sync/channel_1_test.v b/v_windows/v/old/vlib/sync/channel_1_test.v new file mode 100644 index 0000000..17588fd --- /dev/null +++ b/v_windows/v/old/vlib/sync/channel_1_test.v @@ -0,0 +1,25 @@ +const ( + num_iterations = 10000 +) + +fn do_send(ch chan int) { + for i in 0 .. num_iterations { + ch <- i + } +} + +fn test_channel_buffered() { + ch := chan int{cap: 1000} + go do_send(ch) + mut sum := i64(0) + for _ in 0 .. num_iterations { + sum += <-ch + } + assert sum == u64(num_iterations) * (num_iterations - 1) / 2 +} + +fn test_builtin_enum() { + x := ChanState.closed + assert x == .closed + println(x) +} diff --git a/v_windows/v/old/vlib/sync/channel_2_test.v b/v_windows/v/old/vlib/sync/channel_2_test.v new file mode 100644 index 0000000..5e8251d --- /dev/null +++ b/v_windows/v/old/vlib/sync/channel_2_test.v @@ -0,0 +1,19 @@ +const ( + num_iterations = 10000 +) + +fn do_send(ch chan int) { + for i in 0 .. num_iterations { + ch <- i + } +} + +fn test_channel_unbuffered() { + ch := chan int{} + go do_send(ch) + mut sum := i64(0) + for _ in 0 .. num_iterations { + sum += <-ch + } + assert sum == u64(num_iterations) * (num_iterations - 1) / 2 +} diff --git a/v_windows/v/old/vlib/sync/channel_3_test.v b/v_windows/v/old/vlib/sync/channel_3_test.v new file mode 100644 index 0000000..d07276b --- /dev/null +++ b/v_windows/v/old/vlib/sync/channel_3_test.v @@ -0,0 +1,32 @@ +fn do_rec(ch chan int, resch chan i64) { + mut sum := i64(0) + for _ in 0 .. 2000 { + sum += <-ch + } + println(sum) + resch <- sum +} + +fn do_send(ch chan int) { + for i in 0 .. 2000 { + ch <- i + } +} + +fn test_channel_multi_unbuffered() { + ch := chan int{} + resch := chan i64{} + go do_rec(ch, resch) + go do_rec(ch, resch) + go do_rec(ch, resch) + go do_rec(ch, resch) + go do_send(ch) + go do_send(ch) + go do_send(ch) + go do_send(ch) + mut sum := i64(0) + for _ in 0 .. 4 { + sum += <-resch + } + assert sum == i64(4) * 2000 * (2000 - 1) / 2 +} diff --git a/v_windows/v/old/vlib/sync/channel_4_test.v b/v_windows/v/old/vlib/sync/channel_4_test.v new file mode 100644 index 0000000..3792668 --- /dev/null +++ b/v_windows/v/old/vlib/sync/channel_4_test.v @@ -0,0 +1,32 @@ +fn do_rec(ch chan int, resch chan i64) { + mut sum := i64(0) + for _ in 0 .. 2000 { + sum += <-ch + } + println(sum) + resch <- sum +} + +fn do_send(ch chan int) { + for i in 0 .. 2000 { + ch <- i + } +} + +fn test_channel_multi_buffered() { + ch := chan int{cap: 100} + resch := chan i64{} + go do_rec(ch, resch) + go do_rec(ch, resch) + go do_rec(ch, resch) + go do_rec(ch, resch) + go do_send(ch) + go do_send(ch) + go do_send(ch) + go do_send(ch) + mut sum := i64(0) + for _ in 0 .. 4 { + sum += <-resch + } + assert sum == i64(4) * 2000 * (2000 - 1) / 2 +} diff --git a/v_windows/v/old/vlib/sync/channel_array_mut_test.v b/v_windows/v/old/vlib/sync/channel_array_mut_test.v new file mode 100644 index 0000000..bfd53a1 --- /dev/null +++ b/v_windows/v/old/vlib/sync/channel_array_mut_test.v @@ -0,0 +1,35 @@ +const ( + num_iterations = 10000 +) + +struct St { +mut: + dummy i64 + dummy2 u32 + dummy3 i64 + n int + dummy4 int +} + +// this function gets an array of channels for `St` references +fn do_rec_calc_send(chs []chan mut St) { + for { + mut s := <-chs[0] or { break } + s.n++ + chs[1] <- s + } +} + +fn test_channel_array_mut() { + mut chs := [chan mut St{cap: 1}, chan mut St{}] + go do_rec_calc_send(chs) + mut t := &St{ + n: 100 + } + for _ in 0 .. num_iterations { + chs[0] <- t + t = <-chs[1] + } + chs[0].close() + assert t.n == 100 + num_iterations +} diff --git a/v_windows/v/old/vlib/sync/channel_close_test.v b/v_windows/v/old/vlib/sync/channel_close_test.v new file mode 100644 index 0000000..a31bfa9 --- /dev/null +++ b/v_windows/v/old/vlib/sync/channel_close_test.v @@ -0,0 +1,104 @@ +import time + +fn do_rec(ch chan int, resch chan i64) { + mut sum := i64(0) + for { + a := <-ch or { break } + sum += a + } + assert ch.closed == true + println(sum) + resch <- sum +} + +fn do_send(ch chan int) { + for i in 0 .. 8000 { + ch <- i + } + assert ch.closed == false + ch.close() + assert ch.closed == true +} + +fn test_channel_close_buffered_multi() { + ch := chan int{cap: 10} + resch := chan i64{} + go do_rec(ch, resch) + go do_rec(ch, resch) + go do_rec(ch, resch) + go do_rec(ch, resch) + go do_send(ch) + mut sum := i64(0) + for _ in 0 .. 4 { + sum += <-resch + } + assert sum == i64(8000) * (8000 - 1) / 2 +} + +fn test_channel_close_unbuffered_multi() { + ch := chan int{} + resch := chan i64{} + go do_rec(ch, resch) + go do_rec(ch, resch) + go do_rec(ch, resch) + go do_rec(ch, resch) + go do_send(ch) + mut sum := i64(0) + for _ in 0 .. 4 { + sum += <-resch + } + assert sum == i64(8000) * (8000 - 1) / 2 +} + +fn test_channel_close_buffered() { + ch := chan int{cap: 100} + resch := chan i64{} + go do_rec(ch, resch) + go do_send(ch) + mut sum := i64(0) + sum += <-resch + assert sum == i64(8000) * (8000 - 1) / 2 +} + +fn test_channel_close_unbuffered() { + ch := chan int{} + resch := chan i64{cap: 100} + go do_rec(ch, resch) + go do_send(ch) + mut sum := i64(0) + sum += <-resch + assert sum == i64(8000) * (8000 - 1) / 2 +} + +fn test_channel_send_close_buffered() { + ch := chan int{cap: 1} + t := go fn (ch chan int) { + ch <- 31 + mut x := 45 + ch <- 17 or { x = -133 } + + assert x == -133 + }(ch) + time.sleep(100 * time.millisecond) + ch.close() + mut r := <-ch + r = <-ch or { 23 } + assert r == 23 + t.wait() +} + +fn test_channel_send_close_unbuffered() { + time.sleep(1 * time.second) + ch := chan int{} + t := go fn (ch chan int) { + mut x := 31 + ch <- 177 or { x = -71 } + + assert x == -71 + }(ch) + time.sleep(100 * time.millisecond) + ch.close() + r := <-ch or { 238 } + assert r == 238 + t.wait() +} diff --git a/v_windows/v/old/vlib/sync/channel_fill_test.v b/v_windows/v/old/vlib/sync/channel_fill_test.v new file mode 100644 index 0000000..b4eabc0 --- /dev/null +++ b/v_windows/v/old/vlib/sync/channel_fill_test.v @@ -0,0 +1,22 @@ +import sync + +const ( + queue_len = 1000 + queue_fill = 763 +) + +fn do_send(ch chan int, mut fin sync.Semaphore) { + for i in 0 .. queue_fill { + ch <- i + } + fin.post() +} + +fn test_channel_len_cap() { + ch := chan int{cap: queue_len} + mut sem := sync.new_semaphore() + go do_send(ch, mut sem) + sem.wait() + assert ch.cap == queue_len + assert ch.len == queue_fill +} diff --git a/v_windows/v/old/vlib/sync/channel_opt_propagate_test.v b/v_windows/v/old/vlib/sync/channel_opt_propagate_test.v new file mode 100644 index 0000000..a7e26fe --- /dev/null +++ b/v_windows/v/old/vlib/sync/channel_opt_propagate_test.v @@ -0,0 +1,39 @@ +import sync + +const ( + num_iterations = 10000 +) + +fn get_val_from_chan(ch chan i64) ?i64 { + r := <-ch ? + return r +} + +// this function gets an array of channels for `i64` +fn do_rec_calc_send(chs []chan i64, mut sem sync.Semaphore) { + mut msg := '' + for { + mut s := get_val_from_chan(chs[0]) or { + msg = err.msg + break + } + s++ + chs[1] <- s + } + assert msg == 'channel closed' + sem.post() +} + +fn test_channel_array_mut() { + mut chs := [chan i64{}, chan i64{cap: 10}] + mut sem := sync.new_semaphore() + go do_rec_calc_send(chs, mut sem) + mut t := i64(100) + for _ in 0 .. num_iterations { + chs[0] <- t + t = <-chs[1] + } + chs[0].close() + sem.wait() + assert t == 100 + num_iterations +} diff --git a/v_windows/v/old/vlib/sync/channel_polling_test.v b/v_windows/v/old/vlib/sync/channel_polling_test.v new file mode 100644 index 0000000..846dcfd --- /dev/null +++ b/v_windows/v/old/vlib/sync/channel_polling_test.v @@ -0,0 +1,56 @@ +// Channel Benchmark +// +// `nobj` integers are sent thru a channel with queue length`buflen` +// using `nsend` sender threads and `nrec` receiver threads. +// +// The receive threads add all received numbers and send them to the +// main thread where the total sum is compare to the expected value. +const ( + nsend = 2 + nrec = 2 + buflen = 100 + nobj = 10000 + objs_per_thread = 5000 +) + +fn do_rec(ch chan int, resch chan i64, n int) { + mut sum := i64(0) + for _ in 0 .. n { + mut r := 0 + for ch.try_pop(mut r) != .success { + } + sum += r + } + println(sum) + resch <- sum +} + +fn do_send(ch chan int, start int, end int) { + for i in start .. end { + for ch.try_push(i) != .success { + } + } +} + +fn test_channel_polling() { + ch := chan int{cap: buflen} + resch := chan i64{} + for _ in 0 .. nrec { + go do_rec(ch, resch, objs_per_thread) + } + mut n := nobj + for _ in 0 .. nsend { + end := n + n -= objs_per_thread + go do_send(ch, n, end) + } + mut sum := i64(0) + for _ in 0 .. nrec { + sum += <-resch + println('> running sum: $sum') + } + // use sum formula by Gauß to calculate the expected result + expected_sum := i64(nobj) * (nobj - 1) / 2 + println('expected sum: $expected_sum | sum: $sum') + assert sum == expected_sum +} diff --git a/v_windows/v/old/vlib/sync/channel_push_or_1_test.v b/v_windows/v/old/vlib/sync/channel_push_or_1_test.v new file mode 100644 index 0000000..1551d83 --- /dev/null +++ b/v_windows/v/old/vlib/sync/channel_push_or_1_test.v @@ -0,0 +1,65 @@ +const n = 1000 + +const c = 100 + +fn f(ch chan int) { + for _ in 0 .. n { + _ := <-ch + } + ch.close() +} + +fn test_push_or_unbuffered() { + ch := chan int{} + go f(ch) + mut j := 0 + for { + ch <- j or { break } + + j++ + } + assert j == n +} + +fn test_push_or_buffered() { + ch := chan int{cap: c} + go f(ch) + mut j := 0 + for { + ch <- j or { break } + + j++ + } + // we don't know how many elements are in the buffer when the channel + // is closed, so check j against an interval + assert j >= n + assert j <= n + c +} + +fn g(ch chan int, res chan int) { + mut j := 0 + for { + ch <- j or { break } + + j++ + } + println('done $j') + res <- j +} + +fn test_many_senders() { + ch := chan int{} + res := chan int{} + go g(ch, res) + go g(ch, res) + go g(ch, res) + mut k := 0 + for _ in 0 .. 3 * n { + k = <-ch + } + ch.close() + mut sum := <-res + sum += <-res + sum += <-res + assert sum == 3 * n +} diff --git a/v_windows/v/old/vlib/sync/channel_push_or_2_test.v b/v_windows/v/old/vlib/sync/channel_push_or_2_test.v new file mode 100644 index 0000000..8f95395 --- /dev/null +++ b/v_windows/v/old/vlib/sync/channel_push_or_2_test.v @@ -0,0 +1,25 @@ +const n = 1000 + +fn f(ch chan f64) { + mut s := 0. + for _ in 0 .. n { + s += <-ch + } + assert s == f64(n * (n + 1) / 2) + ch.close() +} + +fn do_send(ch chan f64, val f64) ?f64 { + ch <- val ? + return val + 1.0 +} + +fn test_push_propargate() { + ch := chan f64{} + go f(ch) + mut s := 1.0 + for { + s = do_send(ch, s) or { break } + } + assert s == f64(n + 1) +} diff --git a/v_windows/v/old/vlib/sync/channel_select_2_test.v b/v_windows/v/old/vlib/sync/channel_select_2_test.v new file mode 100644 index 0000000..189aaf6 --- /dev/null +++ b/v_windows/v/old/vlib/sync/channel_select_2_test.v @@ -0,0 +1,62 @@ +import time + +fn do_rec_i64(ch chan i64) { + mut sum := i64(0) + for _ in 0 .. 300 { + sum += <-ch + } + assert sum == 300 * (300 - 1) / 2 +} + +fn do_send_int(ch chan int) { + for i in 0 .. 300 { + ch <- i + } +} + +fn do_send_byte(ch chan byte) { + for i in 0 .. 300 { + ch <- byte(i) + } +} + +fn do_send_i64(ch chan i64) { + for i in 0 .. 300 { + ch <- i + } +} + +fn test_select() { + chi := chan int{} + chl := chan i64{cap: 1} + chb := chan byte{cap: 10} + recch := chan i64{cap: 0} + go do_rec_i64(recch) + go do_send_int(chi) + go do_send_byte(chb) + go do_send_i64(chl) + mut sum := i64(0) + mut rl := i64(0) + mut sl := i64(0) + for _ in 0 .. 1200 { + select { + ri := <-chi { + sum += ri + } + recch <- sl { + sl++ + } + rl = <-chl { + sum += rl + } + rb := <-chb { + sum += rb + } + } + } + // Use Gauß' formula for the first 2 contributions + // the 3rd contribution is `byte` and must be seen modulo 256 + expected_sum := 2 * (300 * (300 - 1) / 2) + 256 * (256 - 1) / 2 + 44 * (44 - 1) / 2 + assert sum == expected_sum + time.sleep(20 * time.millisecond) // to give assert in coroutine enough time +} diff --git a/v_windows/v/old/vlib/sync/channel_select_3_test.v b/v_windows/v/old/vlib/sync/channel_select_3_test.v new file mode 100644 index 0000000..fdf6096 --- /dev/null +++ b/v_windows/v/old/vlib/sync/channel_select_3_test.v @@ -0,0 +1,122 @@ +import time +import sync + +struct St { + a int +} + +fn getint() int { + return 8 +} + +fn f1(ch1 chan int, ch2 chan St, ch3 chan int, ch4 chan int, ch5 chan int, mut sem sync.Semaphore) { + mut a := 5 + select { + a = <-ch3 { + a = 0 + } + b := <-ch2 { + a = b.a + } + ch3 <- 5 { + a = 1 + } + ch2 <- St{ + a: 37 + } { + a = 2 + } + ch4 <- (6 + 7 * 9) { + a = 8 + } + ch5 <- getint() { + a = 9 + } + 300 * time.millisecond { + a = 3 + } + } + assert a == 3 + sem.post() +} + +fn f2(ch1 chan St, ch2 chan int, mut sem sync.Semaphore) { + mut r := 23 + for i in 0 .. 2 { + select { + b := <-ch1 { + r = b.a + } + ch2 <- r { + r = 17 + } + } + if i == 0 { + assert r == 17 + } else { + assert r == 13 + } + } + sem.post() +} + +fn test_select_blocks() { + ch1 := chan int{cap: 1} + ch2 := chan St{} + ch3 := chan int{} + ch4 := chan int{} + ch5 := chan int{} + mut sem := sync.new_semaphore() + mut r := false + t := select { + b := <-ch1 { + println(b) + } + else { + // no channel ready + r = true + } + } + assert r == true + assert t == true + go f2(ch2, ch3, mut sem) + n := <-ch3 + assert n == 23 + ch2 <- St{ + a: 13 + } + sem.wait() + stopwatch := time.new_stopwatch() + go f1(ch1, ch2, ch3, ch4, ch5, mut sem) + sem.wait() + elapsed_ms := f64(stopwatch.elapsed()) / time.millisecond + // https://docs.microsoft.com/en-us/windows-hardware/drivers/kernel/high-resolution-timers + // > For example, for Windows running on an x86 processor, the default interval between + // > system clock ticks is typically about 15 milliseconds, and the minimum interval + // > between system clock ticks is about 1 millisecond. + assert elapsed_ms >= 280.0 // 300 - (15ms + 5ms just in case) + + ch1.close() + ch2.close() + mut h := 7 + mut is_open := true + if select { + _ := <-ch2 { + h = 0 + } + ch1 <- h { + h = 1 + } + else { + h = 2 + } + } { + panic('channel is still open') + } else { + is_open = false + } + // no branch should have run + assert h == 7 + // since all channels are closed `select` should return `false` + assert is_open == false +} diff --git a/v_windows/v/old/vlib/sync/channel_select_4_test.v b/v_windows/v/old/vlib/sync/channel_select_4_test.v new file mode 100644 index 0000000..77cd557 --- /dev/null +++ b/v_windows/v/old/vlib/sync/channel_select_4_test.v @@ -0,0 +1,43 @@ +fn do_rec_i64(ch chan i64, sumch chan i64) { + mut sum := i64(0) + for _ in 0 .. 30000 { + sum += <-ch + } + sumch <- sum +} + +fn do_send_int(ch chan int) { + for i in 0 .. 30000 { + ch <- i + } +} + +fn test_select() { + chi := chan int{cap: 10} + recch := chan i64{cap: 10} + chsum := chan i64{} + go do_rec_i64(recch, chsum) + go do_send_int(chi) + mut sum := i64(0) + mut sl := i64(0) + for _ in 0 .. 60000 + recch.cap { + select { + ri := <-chi { + sum += ri + } + recch <- sl { + sl++ + } + } + } + // Use Gauß' formula + expected_sum := i64(30000) * (30000 - 1) / 2 + assert sum == expected_sum + + mut sumrec := <-chsum + // Empty receive buffer + for _ in 0 .. recch.cap { + sumrec += <-recch + } + assert sumrec == i64(30000 + recch.cap) * (30000 + recch.cap - 1) / 2 +} diff --git a/v_windows/v/old/vlib/sync/channel_select_5_test.v b/v_windows/v/old/vlib/sync/channel_select_5_test.v new file mode 100644 index 0000000..97228fe --- /dev/null +++ b/v_windows/v/old/vlib/sync/channel_select_5_test.v @@ -0,0 +1,61 @@ +fn do_rec_i64(ch chan i64, sumch chan i64) { + mut sum := i64(0) + for _ in 0 .. 10000 { + sum += <-ch + } + sumch <- sum +} + +fn do_send_int(ch chan int) { + for i in 0 .. 10000 { + ch <- i + } +} + +fn do_send_int2(ch chan int) { + for i in 10000 .. 20000 { + ch <- i + } +} + +fn do_send_int3(ch chan int) { + for i in 20000 .. 30000 { + ch <- i + } +} + +fn test_select() { + chi := chan int{cap: 10} + recch := chan i64{cap: 10} + chsum := chan i64{} + go do_rec_i64(recch, chsum) + go do_rec_i64(recch, chsum) + go do_rec_i64(recch, chsum) + go do_send_int(chi) + go do_send_int2(chi) + go do_send_int3(chi) + mut sum := i64(0) + mut sl := i64(0) + for _ in 0 .. 60000 + recch.cap { + select { + ri := <-chi { + sum += ri + } + recch <- sl { + sl++ + } + } + } + // Use Gauß' formula + expected_sum := i64(30000) * (30000 - 1) / 2 + assert sum == expected_sum + + mut sumrec := <-chsum + sumrec += <-chsum + sumrec += <-chsum + // Empty receive buffer + for _ in 0 .. recch.cap { + sumrec += <-recch + } + assert sumrec == i64(30000 + recch.cap) * (30000 + recch.cap - 1) / 2 +} diff --git a/v_windows/v/old/vlib/sync/channel_select_6_test.v b/v_windows/v/old/vlib/sync/channel_select_6_test.v new file mode 100644 index 0000000..e1b4ff8 --- /dev/null +++ b/v_windows/v/old/vlib/sync/channel_select_6_test.v @@ -0,0 +1,75 @@ +// This test case runs concurrent 3 instances of `do_select` that +// communicate with 6 other threads doing send and receive operations. +// There are buffered and unbuffered channels - handled by one or two +// concurrend threads on the other side + +fn do_select(ch1 chan int, ch2 chan int, chf1 chan f64, chf2 chan f64, sumch1 chan i64, sumch2 chan i64) { + mut sum1 := i64(0) + mut sum2 := i64(0) + f1 := 17. + f2 := 7. + for _ in 0 .. 20000 + chf1.cap / 3 { + select { + chf1 <- f1 {} + i := <-ch1 { + sum1 += i + } + j := <-ch2 { + sum2 += j + } + chf2 <- f2 {} + } + } + sumch1 <- sum1 + sumch2 <- sum2 +} + +fn do_send_int(ch chan int, factor int) { + for i in 0 .. 10000 { + ch <- (i * factor) + } +} + +fn do_rec_f64(ch chan f64, sumch chan f64) { + mut sum := 0. + for _ in 0 .. 10000 { + sum += <-ch + } + sumch <- sum +} + +fn test_select() { + ch1 := chan int{cap: 3} + ch2 := chan int{} + // buffer length of chf1 mus be mutiple of 3 (# select threads) + chf1 := chan f64{cap: 30} + chf2 := chan f64{} + chsum1 := chan i64{} + chsum2 := chan i64{} + chsumf1 := chan f64{} + chsumf2 := chan f64{} + go do_send_int(ch1, 3) + go do_select(ch1, ch2, chf1, chf2, chsum1, chsum2) + go do_rec_f64(chf1, chsumf1) + go do_rec_f64(chf2, chsumf2) + go do_rec_f64(chf2, chsumf2) + go do_select(ch1, ch2, chf1, chf2, chsum1, chsum2) + go do_send_int(ch2, 7) + go do_send_int(ch2, 17) + go do_select(ch1, ch2, chf1, chf2, chsum1, chsum2) + + sum1 := <-chsum1 + <-chsum1 + <-chsum1 + sum2 := <-chsum2 + <-chsum2 + <-chsum2 + mut sumf1 := <-chsumf1 + // empty channel buffer + for _ in 0 .. chf1.cap { + sumf1 += <-chf1 + } + sumf2 := <-chsumf2 + <-chsumf2 + // Use Gauß' formula + expected_sum := i64(10000) * (10000 - 1) / 2 + assert sum1 == 3 * expected_sum + assert sum2 == (7 + 17) * expected_sum + assert sumf1 == 17. * f64(10000 + chf1.cap) + assert sumf2 == 7. * 20000 +} diff --git a/v_windows/v/old/vlib/sync/channel_select_test.v b/v_windows/v/old/vlib/sync/channel_select_test.v new file mode 100644 index 0000000..26ed641 --- /dev/null +++ b/v_windows/v/old/vlib/sync/channel_select_test.v @@ -0,0 +1,84 @@ +/* +* ATTENTION! Do not use this file as an example! + * For that, please look at `channel_select_2_test.v` or `channel_select_3_test.v` + * + * This test case uses the implementation in `sync/channels.v` directly + * in order to test it independently from the support in the core language +*/ + +module sync + +import time + +fn do_rec_i64(mut ch Channel) { + mut sum := i64(0) + for _ in 0 .. 300 { + mut a := i64(0) + ch.pop(&a) + sum += a + } + assert sum == 300 * (300 - 1) / 2 +} + +fn do_send_int(mut ch Channel) { + for i in 0 .. 300 { + ch.push(&i) + } +} + +fn do_send_byte(mut ch Channel) { + for i in 0 .. 300 { + ii := byte(i) + ch.push(&ii) + } +} + +fn do_send_i64(mut ch Channel) { + for i in 0 .. 300 { + ii := i64(i) + ch.push(&ii) + } +} + +fn test_select() { + mut chi := new_channel(0) + mut chl := new_channel(1) + mut chb := new_channel(10) + mut recch := new_channel(0) + go do_rec_i64(mut recch) + go do_send_int(mut chi) + go do_send_byte(mut chb) + go do_send_i64(mut chl) + mut channels := [chi, recch, chl, chb] + directions := [Direction.pop, .push, .pop, .pop] + mut sum := i64(0) + mut rl := i64(0) + mut ri := int(0) + mut rb := byte(0) + mut sl := i64(0) + mut objs := [voidptr(&ri), &sl, &rl, &rb] + for _ in 0 .. 1200 { + idx := channel_select(mut channels, directions, mut objs, time.infinite) + match idx { + 0 { + sum += ri + } + 1 { + sl++ + } + 2 { + sum += rl + } + 3 { + sum += rb + } + else { + println('got $idx (timeout)') + } + } + } + // Use Gauß' formula for the first 2 contributions + // the 3rd contribution is `byte` and must be seen modulo 256 + expected_sum := 2 * (300 * (300 - 1) / 2) + 256 * (256 - 1) / 2 + 44 * (44 - 1) / 2 + assert sum == expected_sum +} diff --git a/v_windows/v/old/vlib/sync/channel_try_buf_test.v b/v_windows/v/old/vlib/sync/channel_try_buf_test.v new file mode 100644 index 0000000..e521314 --- /dev/null +++ b/v_windows/v/old/vlib/sync/channel_try_buf_test.v @@ -0,0 +1,17 @@ +fn test_channel_try_buffered() { + ch := chan int{cap: 5} + for z in 2 .. 13 { + if ch.try_push(z) == .not_ready { + assert z == 7 + break + } + } + mut obj := int(0) + for ch.try_pop(mut obj) == .success { + println(obj) + } + assert obj == 6 + ch <- 17 + obj = <-ch + assert obj == 17 +} diff --git a/v_windows/v/old/vlib/sync/channel_try_unbuf_test.v b/v_windows/v/old/vlib/sync/channel_try_unbuf_test.v new file mode 100644 index 0000000..ee11468 --- /dev/null +++ b/v_windows/v/old/vlib/sync/channel_try_unbuf_test.v @@ -0,0 +1,13 @@ +fn test_channel_try_unbuffered() { + ch := chan int{} + for z in 5 .. 8 { + if ch.try_push(z) == .not_ready { + assert z == 5 + break + } + panic('push on non-ready channel not detected') + } + mut obj := -17 + for ch.try_pop(mut obj) == .success {} + assert obj == -17 +} diff --git a/v_windows/v/old/vlib/sync/channels.v b/v_windows/v/old/vlib/sync/channels.v new file mode 100644 index 0000000..49f1396 --- /dev/null +++ b/v_windows/v/old/vlib/sync/channels.v @@ -0,0 +1,730 @@ +module sync + +import time +import rand + +$if windows { + #flag -I @VEXEROOT/thirdparty/stdatomic/win +} $else { + #flag -I @VEXEROOT/thirdparty/stdatomic/nix +} + +$if linux { + $if tinyc { + $if amd64 { + // most Linux distributions have /usr/lib/libatomic.so, but Ubuntu uses gcc version specific dir + #flag -L/usr/lib/gcc/x86_64-linux-gnu/6 + #flag -L/usr/lib/gcc/x86_64-linux-gnu/7 + #flag -L/usr/lib/gcc/x86_64-linux-gnu/8 + #flag -L/usr/lib/gcc/x86_64-linux-gnu/9 + #flag -L/usr/lib/gcc/x86_64-linux-gnu/10 + #flag -L/usr/lib/gcc/x86_64-linux-gnu/11 + #flag -L/usr/lib/gcc/x86_64-linux-gnu/12 + } $else $if arm64 { + #flag -L/usr/lib/gcc/aarch64-linux-gnu/6 + #flag -L/usr/lib/gcc/aarch64-linux-gnu/7 + #flag -L/usr/lib/gcc/aarch64-linux-gnu/8 + #flag -L/usr/lib/gcc/aarch64-linux-gnu/9 + #flag -L/usr/lib/gcc/aarch64-linux-gnu/10 + #flag -L/usr/lib/gcc/aarch64-linux-gnu/11 + #flag -L/usr/lib/gcc/aarch64-linux-gnu/12 + } + #flag -latomic + } +} + +#include +// The following functions are actually generic in C +fn C.atomic_load_ptr(voidptr) voidptr +fn C.atomic_store_ptr(voidptr, voidptr) +fn C.atomic_compare_exchange_weak_ptr(voidptr, voidptr, voidptr) bool +fn C.atomic_compare_exchange_strong_ptr(voidptr, voidptr, voidptr) bool +fn C.atomic_exchange_ptr(voidptr, voidptr) voidptr +fn C.atomic_fetch_add_ptr(voidptr, voidptr) voidptr +fn C.atomic_fetch_sub_ptr(voidptr, voidptr) voidptr + +fn C.atomic_load_u16(voidptr) u16 +fn C.atomic_store_u16(voidptr, u16) +fn C.atomic_compare_exchange_weak_u16(voidptr, voidptr, u16) bool +fn C.atomic_compare_exchange_strong_u16(voidptr, voidptr, u16) bool +fn C.atomic_exchange_u16(voidptr, u16) u16 +fn C.atomic_fetch_add_u16(voidptr, u16) u16 +fn C.atomic_fetch_sub_u16(voidptr, u16) u16 + +fn C.atomic_load_u32(voidptr) u32 +fn C.atomic_store_u32(voidptr, u32) +fn C.atomic_compare_exchange_weak_u32(voidptr, voidptr, u32) bool +fn C.atomic_compare_exchange_strong_u32(voidptr, voidptr, u32) bool +fn C.atomic_exchange_u32(voidptr, u32) u32 +fn C.atomic_fetch_add_u32(voidptr, u32) u32 +fn C.atomic_fetch_sub_u32(voidptr, u32) u32 + +fn C.atomic_load_u64(voidptr) u64 +fn C.atomic_store_u64(voidptr, u64) +fn C.atomic_compare_exchange_weak_u64(voidptr, voidptr, u64) bool +fn C.atomic_compare_exchange_strong_u64(voidptr, voidptr, u64) bool +fn C.atomic_exchange_u64(voidptr, u64) u64 +fn C.atomic_fetch_add_u64(voidptr, u64) u64 +fn C.atomic_fetch_sub_u64(voidptr, u64) u64 + +const ( + // how often to try to get data without blocking before to wait for semaphore + spinloops = 750 + spinloops_sem = 4000 +) + +enum BufferElemStat { + unused = 0 + writing + written + reading +} + +struct Subscription { +mut: + sem &Semaphore + prev &&Subscription + nxt &Subscription +} + +enum Direction { + pop + push +} + +struct Channel { + ringbuf &byte // queue for buffered channels + statusbuf &byte // flags to synchronize write/read in ringbuf + objsize u32 +mut: // atomic + writesem Semaphore // to wake thread that wanted to write, but buffer was full + readsem Semaphore // to wake thread that wanted to read, but buffer was empty + writesem_im Semaphore + readsem_im Semaphore + write_adr C.atomic_uintptr_t // if != NULL the next obj can be written here without wait + read_adr C.atomic_uintptr_t // if != NULL an obj can be read from here without wait + adr_read C.atomic_uintptr_t // used to identify origin of writesem + adr_written C.atomic_uintptr_t // used to identify origin of readsem + write_free u32 // for queue state + read_avail u32 + buf_elem_write_idx u32 + buf_elem_read_idx u32 + // for select + write_subscriber &Subscription + read_subscriber &Subscription + write_sub_mtx u16 + read_sub_mtx u16 + closed u16 +pub: + cap u32 // queue length in #objects +} + +pub fn new_channel(n u32) &Channel { + st := sizeof(T) + if isreftype(T) { + return new_channel_st(n, st) + } else { + return new_channel_st_noscan(n, st) + } +} + +fn new_channel_st(n u32, st u32) &Channel { + wsem := if n > 0 { n } else { 1 } + rsem := if n > 0 { u32(0) } else { 1 } + rbuf := if n > 0 { unsafe { malloc(int(n * st)) } } else { &byte(0) } + sbuf := if n > 0 { vcalloc_noscan(int(n * 2)) } else { &byte(0) } + mut ch := Channel{ + objsize: st + cap: n + write_free: n + read_avail: 0 + ringbuf: rbuf + statusbuf: sbuf + write_subscriber: 0 + read_subscriber: 0 + } + ch.writesem.init(wsem) + ch.readsem.init(rsem) + ch.writesem_im.init(0) + ch.readsem_im.init(0) + return &ch +} + +fn new_channel_st_noscan(n u32, st u32) &Channel { + $if gcboehm_opt ? { + wsem := if n > 0 { n } else { 1 } + rsem := if n > 0 { u32(0) } else { 1 } + rbuf := if n > 0 { unsafe { malloc_noscan(int(n * st)) } } else { &byte(0) } + sbuf := if n > 0 { vcalloc_noscan(int(n * 2)) } else { &byte(0) } + mut ch := Channel{ + objsize: st + cap: n + write_free: n + read_avail: 0 + ringbuf: rbuf + statusbuf: sbuf + write_subscriber: 0 + read_subscriber: 0 + } + ch.writesem.init(wsem) + ch.readsem.init(rsem) + ch.writesem_im.init(0) + ch.readsem_im.init(0) + return &ch + } $else { + return new_channel_st(n, st) + } +} + +pub fn (ch &Channel) auto_str(typename string) string { + return 'chan $typename{cap: $ch.cap, closed: $ch.closed}' +} + +pub fn (mut ch Channel) close() { + open_val := u16(0) + if !C.atomic_compare_exchange_strong_u16(&ch.closed, &open_val, 1) { + return + } + mut nulladr := voidptr(0) + for !C.atomic_compare_exchange_weak_ptr(unsafe { &voidptr(&ch.adr_written) }, &nulladr, + voidptr(-1)) { + nulladr = voidptr(0) + } + ch.readsem_im.post() + ch.readsem.post() + mut null16 := u16(0) + for !C.atomic_compare_exchange_weak_u16(&ch.read_sub_mtx, &null16, u16(1)) { + null16 = u16(0) + } + if ch.read_subscriber != voidptr(0) { + ch.read_subscriber.sem.post() + } + C.atomic_store_u16(&ch.read_sub_mtx, u16(0)) + null16 = u16(0) + for !C.atomic_compare_exchange_weak_u16(&ch.write_sub_mtx, &null16, u16(1)) { + null16 = u16(0) + } + if ch.write_subscriber != voidptr(0) { + ch.write_subscriber.sem.post() + } + C.atomic_store_u16(&ch.write_sub_mtx, u16(0)) + ch.writesem.post() + if ch.cap == 0 { + C.atomic_store_ptr(unsafe { &voidptr(&ch.read_adr) }, voidptr(0)) + } + ch.writesem_im.post() +} + +[inline] +pub fn (mut ch Channel) len() int { + return int(C.atomic_load_u32(&ch.read_avail)) +} + +[inline] +pub fn (mut ch Channel) closed() bool { + return C.atomic_load_u16(&ch.closed) != 0 +} + +[inline] +pub fn (mut ch Channel) push(src voidptr) { + if ch.try_push_priv(src, false) == .closed { + panic('push on closed channel') + } +} + +[inline] +pub fn (mut ch Channel) try_push(src voidptr) ChanState { + return ch.try_push_priv(src, true) +} + +fn (mut ch Channel) try_push_priv(src voidptr, no_block bool) ChanState { + if C.atomic_load_u16(&ch.closed) != 0 { + return .closed + } + spinloops_sem_, spinloops_ := if no_block { 1, 1 } else { sync.spinloops, sync.spinloops_sem } + mut have_swapped := false + for { + mut got_sem := false + mut wradr := C.atomic_load_ptr(unsafe { &voidptr(&ch.write_adr) }) + for wradr != C.NULL { + if C.atomic_compare_exchange_strong_ptr(unsafe { &voidptr(&ch.write_adr) }, + &wradr, voidptr(0)) + { + // there is a reader waiting for us + unsafe { C.memcpy(wradr, src, ch.objsize) } + mut nulladr := voidptr(0) + for !C.atomic_compare_exchange_weak_ptr(unsafe { &voidptr(&ch.adr_written) }, + &nulladr, wradr) { + nulladr = voidptr(0) + } + ch.readsem_im.post() + return .success + } + } + if no_block && ch.cap == 0 { + return .not_ready + } + // get token to read + for _ in 0 .. spinloops_sem_ { + if got_sem { + break + } + got_sem = ch.writesem.try_wait() + } + if !got_sem { + if no_block { + return .not_ready + } + ch.writesem.wait() + } + if C.atomic_load_u16(&ch.closed) != 0 { + ch.writesem.post() + return .closed + } + if ch.cap == 0 { + // try to advertise current object as readable + mut read_in_progress := false + C.atomic_store_ptr(unsafe { &voidptr(&ch.read_adr) }, src) + wradr = C.atomic_load_ptr(unsafe { &voidptr(&ch.write_adr) }) + if wradr != C.NULL { + mut src2 := src + if C.atomic_compare_exchange_strong_ptr(unsafe { &voidptr(&ch.read_adr) }, + &src2, voidptr(0)) + { + ch.writesem.post() + continue + } else { + read_in_progress = true + } + } + if !read_in_progress { + mut null16 := u16(0) + for !C.atomic_compare_exchange_weak_u16(voidptr(&ch.read_sub_mtx), &null16, + u16(1)) { + null16 = u16(0) + } + if ch.read_subscriber != voidptr(0) { + ch.read_subscriber.sem.post() + } + C.atomic_store_u16(&ch.read_sub_mtx, u16(0)) + } + mut src2 := src + for sp := u32(0); sp < spinloops_ || read_in_progress; sp++ { + if C.atomic_compare_exchange_strong_ptr(unsafe { &voidptr(&ch.adr_read) }, + &src2, voidptr(0)) + { + have_swapped = true + read_in_progress = true + break + } + src2 = src + } + mut got_im_sem := false + for sp := u32(0); sp < spinloops_sem_ || read_in_progress; sp++ { + got_im_sem = ch.writesem_im.try_wait() + if got_im_sem { + break + } + } + for { + if got_im_sem { + got_im_sem = false + } else { + ch.writesem_im.wait() + } + if C.atomic_load_u16(&ch.closed) != 0 { + if have_swapped + || C.atomic_compare_exchange_strong_ptr(unsafe { &voidptr(&ch.adr_read) }, &src2, voidptr(0)) { + ch.writesem.post() + return .success + } else { + return .closed + } + } + if have_swapped + || C.atomic_compare_exchange_strong_ptr(unsafe { &voidptr(&ch.adr_read) }, &src2, voidptr(0)) { + ch.writesem.post() + break + } else { + // this semaphore was not for us - repost in + ch.writesem_im.post() + if src2 == voidptr(-1) { + ch.readsem.post() + return .closed + } + src2 = src + } + } + return .success + } else { + // buffered channel + mut space_in_queue := false + mut wr_free := C.atomic_load_u32(&ch.write_free) + for wr_free > 0 { + space_in_queue = C.atomic_compare_exchange_weak_u32(&ch.write_free, &wr_free, + wr_free - 1) + if space_in_queue { + break + } + } + if space_in_queue { + mut wr_idx := C.atomic_load_u32(&ch.buf_elem_write_idx) + for { + mut new_wr_idx := wr_idx + 1 + for new_wr_idx >= ch.cap { + new_wr_idx -= ch.cap + } + if C.atomic_compare_exchange_strong_u32(&ch.buf_elem_write_idx, &wr_idx, + new_wr_idx) + { + break + } + } + mut wr_ptr := ch.ringbuf + mut status_adr := ch.statusbuf + unsafe { + wr_ptr += wr_idx * ch.objsize + status_adr += wr_idx * sizeof(u16) + } + mut expected_status := u16(BufferElemStat.unused) + for !C.atomic_compare_exchange_weak_u16(status_adr, &expected_status, + u16(BufferElemStat.writing)) { + expected_status = u16(BufferElemStat.unused) + } + unsafe { + C.memcpy(wr_ptr, src, ch.objsize) + } + C.atomic_store_u16(unsafe { &u16(status_adr) }, u16(BufferElemStat.written)) + C.atomic_fetch_add_u32(&ch.read_avail, 1) + ch.readsem.post() + mut null16 := u16(0) + for !C.atomic_compare_exchange_weak_u16(&ch.read_sub_mtx, &null16, u16(1)) { + null16 = u16(0) + } + if ch.read_subscriber != voidptr(0) { + ch.read_subscriber.sem.post() + } + C.atomic_store_u16(&ch.read_sub_mtx, u16(0)) + return .success + } else { + if no_block { + return .not_ready + } + ch.writesem.post() + } + } + } + // we should not get here but the V compiler want's to see a return statement + assert false + return .success +} + +[inline] +pub fn (mut ch Channel) pop(dest voidptr) bool { + return ch.try_pop_priv(dest, false) == .success +} + +[inline] +pub fn (mut ch Channel) try_pop(dest voidptr) ChanState { + return ch.try_pop_priv(dest, true) +} + +fn (mut ch Channel) try_pop_priv(dest voidptr, no_block bool) ChanState { + spinloops_sem_, spinloops_ := if no_block { 1, 1 } else { sync.spinloops, sync.spinloops_sem } + mut have_swapped := false + mut write_in_progress := false + for { + mut got_sem := false + if ch.cap == 0 { + // unbuffered channel - first see if a `push()` has adversized + mut rdadr := C.atomic_load_ptr(unsafe { &voidptr(&ch.read_adr) }) + for rdadr != C.NULL { + if C.atomic_compare_exchange_strong_ptr(unsafe { &voidptr(&ch.read_adr) }, + &rdadr, voidptr(0)) + { + // there is a writer waiting for us + unsafe { C.memcpy(dest, rdadr, ch.objsize) } + mut nulladr := voidptr(0) + for !C.atomic_compare_exchange_weak_ptr(unsafe { &voidptr(&ch.adr_read) }, + &nulladr, rdadr) { + nulladr = voidptr(0) + } + ch.writesem_im.post() + return .success + } + } + if no_block { + if C.atomic_load_u16(&ch.closed) == 0 { + return .not_ready + } else { + return .closed + } + } + } + // get token to read + for _ in 0 .. spinloops_sem_ { + if got_sem { + break + } + got_sem = ch.readsem.try_wait() + } + if !got_sem { + if no_block { + if C.atomic_load_u16(&ch.closed) == 0 { + return .not_ready + } else { + return .closed + } + } + ch.readsem.wait() + } + if ch.cap > 0 { + // try to get buffer token + mut obj_in_queue := false + mut rd_avail := C.atomic_load_u32(&ch.read_avail) + for rd_avail > 0 { + obj_in_queue = C.atomic_compare_exchange_weak_u32(&ch.read_avail, &rd_avail, + rd_avail - 1) + if obj_in_queue { + break + } + } + if obj_in_queue { + mut rd_idx := C.atomic_load_u32(&ch.buf_elem_read_idx) + for { + mut new_rd_idx := rd_idx + 1 + for new_rd_idx >= ch.cap { + new_rd_idx -= ch.cap + } + if C.atomic_compare_exchange_weak_u32(&ch.buf_elem_read_idx, &rd_idx, + new_rd_idx) + { + break + } + } + mut rd_ptr := ch.ringbuf + mut status_adr := ch.statusbuf + unsafe { + rd_ptr += rd_idx * ch.objsize + status_adr += rd_idx * sizeof(u16) + } + mut expected_status := u16(BufferElemStat.written) + for !C.atomic_compare_exchange_weak_u16(status_adr, &expected_status, + u16(BufferElemStat.reading)) { + expected_status = u16(BufferElemStat.written) + } + unsafe { + C.memcpy(dest, rd_ptr, ch.objsize) + } + C.atomic_store_u16(unsafe { &u16(status_adr) }, u16(BufferElemStat.unused)) + C.atomic_fetch_add_u32(&ch.write_free, 1) + ch.writesem.post() + mut null16 := u16(0) + for !C.atomic_compare_exchange_weak_u16(&ch.write_sub_mtx, &null16, u16(1)) { + null16 = u16(0) + } + if ch.write_subscriber != voidptr(0) { + ch.write_subscriber.sem.post() + } + C.atomic_store_u16(&ch.write_sub_mtx, u16(0)) + return .success + } + } + // try to advertise `dest` as writable + C.atomic_store_ptr(unsafe { &voidptr(&ch.write_adr) }, dest) + if ch.cap == 0 { + mut rdadr := C.atomic_load_ptr(unsafe { &voidptr(&ch.read_adr) }) + if rdadr != C.NULL { + mut dest2 := dest + if C.atomic_compare_exchange_strong_ptr(unsafe { &voidptr(&ch.write_adr) }, + &dest2, voidptr(0)) + { + ch.readsem.post() + continue + } else { + write_in_progress = true + } + } + } + if ch.cap == 0 && !write_in_progress { + mut null16 := u16(0) + for !C.atomic_compare_exchange_weak_u16(&ch.write_sub_mtx, &null16, u16(1)) { + null16 = u16(0) + } + if ch.write_subscriber != voidptr(0) { + ch.write_subscriber.sem.post() + } + C.atomic_store_u16(&ch.write_sub_mtx, u16(0)) + } + mut dest2 := dest + for sp := u32(0); sp < spinloops_ || write_in_progress; sp++ { + if C.atomic_compare_exchange_strong_ptr(unsafe { &voidptr(&ch.adr_written) }, + &dest2, voidptr(0)) + { + have_swapped = true + break + } else if dest2 == voidptr(-1) { + ch.readsem.post() + return .closed + } + dest2 = dest + } + mut got_im_sem := false + for sp := u32(0); sp < spinloops_sem_ || write_in_progress; sp++ { + got_im_sem = ch.readsem_im.try_wait() + if got_im_sem { + break + } + } + for { + if got_im_sem { + got_im_sem = false + } else { + ch.readsem_im.wait() + } + if have_swapped + || C.atomic_compare_exchange_strong_ptr(unsafe { &voidptr(&ch.adr_written) }, &dest2, voidptr(0)) { + ch.readsem.post() + break + } else { + // this semaphore was not for us - repost in + ch.readsem_im.post() + if dest2 == voidptr(-1) { + ch.readsem.post() + return .closed + } + dest2 = dest + } + } + break + } + return .success +} + +// Wait `timeout` on any of `channels[i]` until one of them can push (`is_push[i] = true`) or pop (`is_push[i] = false`) +// object referenced by `objrefs[i]`. `timeout = time.infinite` means wait unlimited time. `timeout <= 0` means return +// immediately if no transaction can be performed without waiting. +// return value: the index of the channel on which a transaction has taken place +// -1 if waiting for a transaction has exceeded timeout +// -2 if all channels are closed + +pub fn channel_select(mut channels []&Channel, dir []Direction, mut objrefs []voidptr, timeout time.Duration) int { + assert channels.len == dir.len + assert dir.len == objrefs.len + mut subscr := []Subscription{len: channels.len} + mut sem := unsafe { Semaphore{} } + sem.init(0) + for i, ch in channels { + subscr[i].sem = unsafe { &sem } + if dir[i] == .push { + mut null16 := u16(0) + for !C.atomic_compare_exchange_weak_u16(&ch.write_sub_mtx, &null16, u16(1)) { + null16 = u16(0) + } + subscr[i].prev = unsafe { &ch.write_subscriber } + unsafe { + subscr[i].nxt = C.atomic_exchange_ptr(&voidptr(&ch.write_subscriber), + &subscr[i]) + } + if voidptr(subscr[i].nxt) != voidptr(0) { + subscr[i].nxt.prev = unsafe { &subscr[i].nxt } + } + C.atomic_store_u16(&ch.write_sub_mtx, u16(0)) + } else { + mut null16 := u16(0) + for !C.atomic_compare_exchange_weak_u16(&ch.read_sub_mtx, &null16, u16(1)) { + null16 = u16(0) + } + subscr[i].prev = unsafe { &ch.read_subscriber } + unsafe { + subscr[i].nxt = C.atomic_exchange_ptr(&voidptr(&ch.read_subscriber), &subscr[i]) + } + if voidptr(subscr[i].nxt) != voidptr(0) { + subscr[i].nxt.prev = unsafe { &subscr[i].nxt } + } + C.atomic_store_u16(&ch.read_sub_mtx, u16(0)) + } + } + stopwatch := if timeout == time.infinite || timeout <= 0 { + time.StopWatch{} + } else { + time.new_stopwatch() + } + mut event_idx := -1 // negative index means `timed out` + + outer: for { + rnd := rand.u32_in_range(0, u32(channels.len)) + mut num_closed := 0 + for j, _ in channels { + mut i := j + int(rnd) + if i >= channels.len { + i -= channels.len + } + if dir[i] == .push { + stat := channels[i].try_push_priv(objrefs[i], true) + if stat == .success { + event_idx = i + break outer + } else if stat == .closed { + num_closed++ + } + } else { + stat := channels[i].try_pop_priv(objrefs[i], true) + if stat == .success { + event_idx = i + break outer + } else if stat == .closed { + num_closed++ + } + } + } + if num_closed == channels.len { + event_idx = -2 + break outer + } + if timeout <= 0 { + break outer + } + if timeout != time.infinite { + remaining := timeout - stopwatch.elapsed() + if !sem.timed_wait(remaining) { + break outer + } + } else { + sem.wait() + } + } + // reset subscribers + for i, ch in channels { + if dir[i] == .push { + mut null16 := u16(0) + for !C.atomic_compare_exchange_weak_u16(&ch.write_sub_mtx, &null16, u16(1)) { + null16 = u16(0) + } + unsafe { + *subscr[i].prev = subscr[i].nxt + } + if subscr[i].nxt != 0 { + subscr[i].nxt.prev = subscr[i].prev + // just in case we have missed a semaphore during restore + subscr[i].nxt.sem.post() + } + C.atomic_store_u16(&ch.write_sub_mtx, u16(0)) + } else { + mut null16 := u16(0) + for !C.atomic_compare_exchange_weak_u16(&ch.read_sub_mtx, &null16, u16(1)) { + null16 = u16(0) + } + unsafe { + *subscr[i].prev = subscr[i].nxt + } + if subscr[i].nxt != 0 { + subscr[i].nxt.prev = subscr[i].prev + subscr[i].nxt.sem.post() + } + C.atomic_store_u16(&ch.read_sub_mtx, u16(0)) + } + } + sem.destroy() + return event_idx +} diff --git a/v_windows/v/old/vlib/sync/pool/README.md b/v_windows/v/old/vlib/sync/pool/README.md new file mode 100644 index 0000000..bdea5b3 --- /dev/null +++ b/v_windows/v/old/vlib/sync/pool/README.md @@ -0,0 +1,36 @@ + +The `sync.pool` module provides a convenient way to run identical tasks over +an array of items *in parallel*, without worrying about thread synchronization, +waitgroups, mutexes etc.., you just need to supply a callback function, that +will be called once per each item in your input array. + +After all the work is done in parallel by the worker threads in the pool, +pool.work_on_items will return. You can then call pool.get_results() +to retrieve a list of all the results, that the worker callbacks returned +for each input item. Example: + +```v +import sync.pool + +struct SResult { + s string +} + +fn sprocess(pp &pool.PoolProcessor, idx int, wid int) &SResult { + item := pp.get_item(idx) + println('idx: $idx, wid: $wid, item: ' + item) + return &SResult{item.reverse()} +} + +fn main() { + mut pp := pool.new_pool_processor(callback: sprocess) + pp.work_on_items(['1abc', '2abc', '3abc', '4abc', '5abc', '6abc', '7abc']) + // optionally, you can iterate over the results too: + for x in pp.get_results() { + println('result: $x.s') + } +} +``` + +See https://github.com/vlang/v/blob/master/vlib/sync/pool/pool_test.v for a +more detailed usage example. diff --git a/v_windows/v/old/vlib/sync/pool/pool.v b/v_windows/v/old/vlib/sync/pool/pool.v new file mode 100644 index 0000000..b2c5340 --- /dev/null +++ b/v_windows/v/old/vlib/sync/pool/pool.v @@ -0,0 +1,165 @@ +module pool + +import sync +import runtime + +[trusted] +fn C.atomic_fetch_add_u32(voidptr, u32) u32 + +pub const ( + no_result = voidptr(0) +) + +pub struct PoolProcessor { + thread_cb voidptr +mut: + njobs int + items []voidptr + results []voidptr + ntask u32 // reading/writing to this should be atomic + waitgroup sync.WaitGroup + shared_context voidptr + thread_contexts []voidptr +} + +pub type ThreadCB = fn (p &PoolProcessor, idx int, task_id int) voidptr + +pub struct PoolProcessorConfig { + maxjobs int + callback ThreadCB +} + +// new_pool_processor returns a new PoolProcessor instance. +// The parameters of new_pool_processor are: +// context.maxjobs: when 0 (the default), the PoolProcessor will use a +// number of threads, that is optimal for your system to process your items. +// context.callback: this should be a callback function, that each worker +// thread in the pool will run for each item. +// The callback function will receive as parameters: +// 1) the PoolProcessor instance, so it can call +// p.get_item(idx) to get the actual item at index idx +// 2) idx - the index of the currently processed item +// 3) task_id - the index of the worker thread in which the callback +// function is running. +pub fn new_pool_processor(context PoolProcessorConfig) &PoolProcessor { + if isnil(context.callback) { + panic('You need to pass a valid callback to new_pool_processor.') + } + mut pool := PoolProcessor{ + items: [] + results: [] + shared_context: voidptr(0) + thread_contexts: [] + njobs: context.maxjobs + ntask: 0 + thread_cb: voidptr(context.callback) + } + pool.waitgroup.init() + return &pool +} + +// set_max_jobs gives you the ability to override the number +// of jobs *after* the PoolProcessor had been created already. +pub fn (mut pool PoolProcessor) set_max_jobs(njobs int) { + pool.njobs = njobs +} + +// work_on_items receives a list of items of type T, +// then starts a work pool of pool.njobs threads, each running +// pool.thread_cb in a loop, untill all items in the list, +// are processed. +// When pool.njobs is 0, the number of jobs is determined +// by the number of available cores on the system. +// work_on_items returns *after* all threads finish. +// You can optionally call get_results after that. +pub fn (mut pool PoolProcessor) work_on_items(items []T) { + pool.work_on_pointers(unsafe { items.pointers() }) +} + +pub fn (mut pool PoolProcessor) work_on_pointers(items []voidptr) { + mut njobs := runtime.nr_jobs() + if pool.njobs > 0 { + njobs = pool.njobs + } + pool.items = [] + pool.results = [] + pool.thread_contexts = [] + pool.items << items + pool.results = []voidptr{len: (pool.items.len)} + pool.thread_contexts << []voidptr{len: (pool.items.len)} + pool.waitgroup.add(njobs) + for i := 0; i < njobs; i++ { + if njobs > 1 { + go process_in_thread(mut pool, i) + } else { + // do not run concurrently, just use the same thread: + process_in_thread(mut pool, i) + } + } + pool.waitgroup.wait() +} + +// process_in_thread does the actual work of worker thread. +// It is a workaround for the current inability to pass a +// method in a callback. +fn process_in_thread(mut pool PoolProcessor, task_id int) { + cb := ThreadCB(pool.thread_cb) + ilen := pool.items.len + for { + idx := int(C.atomic_fetch_add_u32(&pool.ntask, 1)) + if idx >= ilen { + break + } + pool.results[idx] = cb(pool, idx, task_id) + } + pool.waitgroup.done() +} + +// get_item - called by the worker callback. +// Retrieves a type safe instance of the currently processed item +pub fn (pool &PoolProcessor) get_item(idx int) T { + return *(&T(pool.items[idx])) +} + +// get_result - called by the main thread to get a specific result. +// Retrieves a type safe instance of the produced result. +pub fn (pool &PoolProcessor) get_result(idx int) T { + return *(&T(pool.results[idx])) +} + +// get_results - get a list of type safe results in the main thread. +pub fn (pool &PoolProcessor) get_results() []T { + mut res := []T{} + for i in 0 .. pool.results.len { + res << *(&T(pool.results[i])) + } + return res +} + +// set_shared_context - can be called during the setup so that you can +// provide a context that is shared between all worker threads, like +// common options/settings. +pub fn (mut pool PoolProcessor) set_shared_context(context voidptr) { + pool.shared_context = context +} + +// get_shared_context - can be called in each worker callback, to get +// the context set by pool.set_shared_context +pub fn (pool &PoolProcessor) get_shared_context() voidptr { + return pool.shared_context +} + +// set_thread_context - can be called during the setup at the start of +// each worker callback, so that the worker callback can have some thread +// local storage area where it can write/read information that is private +// to the given thread, without worrying that it will get overwritten by +// another thread +pub fn (mut pool PoolProcessor) set_thread_context(idx int, context voidptr) { + pool.thread_contexts[idx] = context +} + +// get_thread_context - returns a pointer, that was set with +// pool.set_thread_context . This pointer is private to each thread. +pub fn (pool &PoolProcessor) get_thread_context(idx int) voidptr { + return pool.thread_contexts[idx] +} diff --git a/v_windows/v/old/vlib/sync/pool/pool_test.v b/v_windows/v/old/vlib/sync/pool/pool_test.v new file mode 100644 index 0000000..629b524 --- /dev/null +++ b/v_windows/v/old/vlib/sync/pool/pool_test.v @@ -0,0 +1,52 @@ +import time +import sync.pool + +pub struct SResult { + s string +} + +pub struct IResult { + i int +} + +fn worker_s(p &pool.PoolProcessor, idx int, worker_id int) &SResult { + item := p.get_item(idx) + println('worker_s worker_id: $worker_id | idx: $idx | item: $item') + time.sleep(3 * time.millisecond) + return &SResult{'$item $item'} +} + +fn worker_i(p &pool.PoolProcessor, idx int, worker_id int) &IResult { + item := p.get_item(idx) + println('worker_i worker_id: $worker_id | idx: $idx | item: $item') + time.sleep(5 * time.millisecond) + return &IResult{item * 1000} +} + +fn test_work_on_strings() { + mut pool_s := pool.new_pool_processor( + callback: worker_s + maxjobs: 8 + ) + + pool_s.work_on_items(['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']) + for x in pool_s.get_results() { + println(x.s) + assert x.s.len > 1 + } +} + +fn test_work_on_ints() { + // NB: since maxjobs is left empty here, + // the pool processor will use njobs = runtime.nr_jobs so that + // it will work optimally without overloading the system + mut pool_i := pool.new_pool_processor( + callback: worker_i + ) + + pool_i.work_on_items([1, 2, 3, 4, 5, 6, 7, 8]) + for x in pool_i.get_results() { + println(x.i) + assert x.i > 100 + } +} diff --git a/v_windows/v/old/vlib/sync/select_close_test.v b/v_windows/v/old/vlib/sync/select_close_test.v new file mode 100644 index 0000000..3a40e2d --- /dev/null +++ b/v_windows/v/old/vlib/sync/select_close_test.v @@ -0,0 +1,92 @@ +module sync + +import time + +fn do_rec_i64(mut ch Channel) { + mut sum := i64(0) + for i in 0 .. 300 { + if i == 200 { + ch.close() + } + mut a := i64(0) + if ch.pop(&a) { + sum += a + } + } + assert sum == 200 * (200 - 1) / 2 +} + +fn do_send_int(mut ch Channel) { + for i in 0 .. 300 { + ch.push(&i) + } + ch.close() +} + +fn do_send_byte(mut ch Channel) { + for i in 0 .. 300 { + ii := byte(i) + ch.push(&ii) + } + ch.close() +} + +fn do_send_i64(mut ch Channel) { + for i in 0 .. 300 { + ii := i64(i) + ch.push(&ii) + } + ch.close() +} + +fn test_select() { + mut chi := new_channel(0) + mut chl := new_channel(1) + mut chb := new_channel(10) + mut recch := new_channel(0) + go do_rec_i64(mut recch) + go do_send_int(mut chi) + go do_send_byte(mut chb) + go do_send_i64(mut chl) + mut channels := [chi, recch, chl, chb] + directions := [Direction.pop, .push, .pop, .pop] + mut sum := i64(0) + mut rl := i64(0) + mut ri := int(0) + mut rb := byte(0) + mut sl := i64(0) + mut objs := [voidptr(&ri), &sl, &rl, &rb] + for j in 0 .. 1101 { + idx := channel_select(mut channels, directions, mut objs, time.infinite) + match idx { + 0 { + sum += ri + } + 1 { + sl++ + } + 2 { + sum += rl + } + 3 { + sum += rb + } + -2 { + // channel was closed - last item + assert j == 1100 + } + else { + println('got $idx (timeout)') + assert false + } + } + if j == 1100 { + // check also in other direction + assert idx == -2 + } + } + // Use Gauß' formula for the first 2 contributions + // the 3rd contribution is `byte` and must be seen modulo 256 + expected_sum := 2 * (300 * (300 - 1) / 2) + 256 * (256 - 1) / 2 + 44 * (44 - 1) / 2 + assert sum == expected_sum +} diff --git a/v_windows/v/old/vlib/sync/struct_chan_init_test.v b/v_windows/v/old/vlib/sync/struct_chan_init_test.v new file mode 100644 index 0000000..a51ea4b --- /dev/null +++ b/v_windows/v/old/vlib/sync/struct_chan_init_test.v @@ -0,0 +1,14 @@ +struct Abc { + ch chan int +} + +fn f(st Abc) { + st.ch <- 47 +} + +fn test_chan_init() { + st := Abc{} + go f(st) + i := <-st.ch + assert i == 47 +} diff --git a/v_windows/v/old/vlib/sync/sync_default.c.v b/v_windows/v/old/vlib/sync/sync_default.c.v new file mode 100644 index 0000000..76f0b44 --- /dev/null +++ b/v_windows/v/old/vlib/sync/sync_default.c.v @@ -0,0 +1,193 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module sync + +import time + +// There's no additional linking (-lpthread) needed for Android. +// See https://stackoverflow.com/a/31277163/1904615 +$if !android { + #flag -lpthread +} + +#include + +[trusted] +fn C.pthread_mutex_init(voidptr, voidptr) int +fn C.pthread_mutex_lock(voidptr) int +fn C.pthread_mutex_unlock(voidptr) int +fn C.pthread_mutex_destroy(voidptr) int +fn C.pthread_rwlockattr_init(voidptr) int +fn C.pthread_rwlockattr_setkind_np(voidptr, int) int +fn C.pthread_rwlockattr_setpshared(voidptr, int) int +fn C.pthread_rwlock_init(voidptr, voidptr) int +fn C.pthread_rwlock_rdlock(voidptr) int +fn C.pthread_rwlock_wrlock(voidptr) int +fn C.pthread_rwlock_unlock(voidptr) int +fn C.sem_init(voidptr, int, u32) int +fn C.sem_post(voidptr) int +fn C.sem_wait(voidptr) int +fn C.sem_trywait(voidptr) int +fn C.sem_timedwait(voidptr, voidptr) int +fn C.sem_destroy(voidptr) int + +// [init_with=new_mutex] // TODO: implement support for this struct attribute, and disallow Mutex{} from outside the sync.new_mutex() function. +[heap] +pub struct Mutex { + mutex C.pthread_mutex_t +} + +[heap] +pub struct RwMutex { + mutex C.pthread_rwlock_t +} + +struct RwMutexAttr { + attr C.pthread_rwlockattr_t +} + +[heap] +struct Semaphore { + sem C.sem_t +} + +pub fn new_mutex() &Mutex { + mut m := &Mutex{} + m.init() + return m +} + +pub fn (mut m Mutex) init() { + C.pthread_mutex_init(&m.mutex, C.NULL) +} + +pub fn new_rwmutex() &RwMutex { + mut m := &RwMutex{} + m.init() + return m +} + +pub fn (mut m RwMutex) init() { + a := RwMutexAttr{} + C.pthread_rwlockattr_init(&a.attr) + // Give writer priority over readers + C.pthread_rwlockattr_setkind_np(&a.attr, C.PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP) + C.pthread_rwlockattr_setpshared(&a.attr, C.PTHREAD_PROCESS_PRIVATE) + C.pthread_rwlock_init(&m.mutex, &a.attr) +} + +// @lock(), for *manual* mutex handling, since `lock` is a keyword +pub fn (mut m Mutex) @lock() { + C.pthread_mutex_lock(&m.mutex) +} + +pub fn (mut m Mutex) unlock() { + C.pthread_mutex_unlock(&m.mutex) +} + +// RwMutex has separate read- and write locks +pub fn (mut m RwMutex) @rlock() { + C.pthread_rwlock_rdlock(&m.mutex) +} + +pub fn (mut m RwMutex) @lock() { + C.pthread_rwlock_wrlock(&m.mutex) +} + +// Windows SRWLocks have different function to unlock +// So provide two functions here, too, to have a common interface +pub fn (mut m RwMutex) runlock() { + C.pthread_rwlock_unlock(&m.mutex) +} + +pub fn (mut m RwMutex) unlock() { + C.pthread_rwlock_unlock(&m.mutex) +} + +[inline] +pub fn new_semaphore() &Semaphore { + return new_semaphore_init(0) +} + +pub fn new_semaphore_init(n u32) &Semaphore { + mut sem := &Semaphore{} + sem.init(n) + return sem +} + +pub fn (mut sem Semaphore) init(n u32) { + C.sem_init(&sem.sem, 0, n) +} + +pub fn (mut sem Semaphore) post() { + C.sem_post(&sem.sem) +} + +pub fn (mut sem Semaphore) wait() { + for { + if C.sem_wait(&sem.sem) == 0 { + return + } + e := C.errno + match e { + C.EINTR { + continue // interrupted by signal + } + else { + panic(unsafe { tos_clone(&byte(C.strerror(C.errno))) }) + } + } + } +} + +// `try_wait()` should return as fast as possible so error handling is only +// done when debugging +pub fn (mut sem Semaphore) try_wait() bool { + $if !debug { + return C.sem_trywait(&sem.sem) == 0 + } $else { + if C.sem_trywait(&sem.sem) != 0 { + e := C.errno + match e { + C.EAGAIN { + return false + } + else { + panic(unsafe { tos_clone(&byte(C.strerror(C.errno))) }) + } + } + } + return true + } +} + +pub fn (mut sem Semaphore) timed_wait(timeout time.Duration) bool { + t_spec := timeout.timespec() + for { + if C.sem_timedwait(&sem.sem, &t_spec) == 0 { + return true + } + e := C.errno + match e { + C.EINTR { + continue // interrupted by signal + } + C.ETIMEDOUT { + break + } + else { + panic(unsafe { tos_clone(&byte(C.strerror(e))) }) + } + } + } + return false +} + +pub fn (sem Semaphore) destroy() { + res := C.sem_destroy(&sem.sem) + if res == 0 { + return + } + panic(unsafe { tos_clone(&byte(C.strerror(res))) }) +} diff --git a/v_windows/v/old/vlib/sync/sync_macos.c.v b/v_windows/v/old/vlib/sync/sync_macos.c.v new file mode 100644 index 0000000..3f83198 --- /dev/null +++ b/v_windows/v/old/vlib/sync/sync_macos.c.v @@ -0,0 +1,232 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module sync + +import time + +#flag -lpthread +#include +#include + +[trusted] +fn C.pthread_mutex_init(voidptr, voidptr) int +fn C.pthread_mutex_lock(voidptr) int +fn C.pthread_mutex_unlock(voidptr) int +fn C.pthread_mutex_destroy(voidptr) int +fn C.pthread_rwlockattr_init(voidptr) int +fn C.pthread_rwlockattr_setkind_np(voidptr, int) int +fn C.pthread_rwlockattr_setpshared(voidptr, int) int +fn C.pthread_rwlock_init(voidptr, voidptr) int +fn C.pthread_rwlock_rdlock(voidptr) int +fn C.pthread_rwlock_wrlock(voidptr) int +fn C.pthread_rwlock_unlock(voidptr) int +fn C.pthread_condattr_init(voidptr) int +fn C.pthread_condattr_setpshared(voidptr, int) int +fn C.pthread_condattr_destroy(voidptr) int +fn C.pthread_cond_init(voidptr, voidptr) int +fn C.pthread_cond_signal(voidptr) int +fn C.pthread_cond_wait(voidptr, voidptr) int +fn C.pthread_cond_timedwait(voidptr, voidptr, voidptr) int +fn C.pthread_cond_destroy(voidptr) int + +// [init_with=new_mutex] // TODO: implement support for this struct attribute, and disallow Mutex{} from outside the sync.new_mutex() function. +[heap] +pub struct Mutex { + mutex C.pthread_mutex_t +} + +[heap] +pub struct RwMutex { + mutex C.pthread_rwlock_t +} + +struct RwMutexAttr { + attr C.pthread_rwlockattr_t +} + +struct CondAttr { + attr C.pthread_condattr_t +} + +/* +MacOSX has no unnamed semaphores and no `timed_wait()` at all + so we emulate the behaviour with other devices +*/ +[heap] +struct Semaphore { + mtx C.pthread_mutex_t + cond C.pthread_cond_t +mut: + count u32 +} + +pub fn new_mutex() &Mutex { + mut m := &Mutex{} + m.init() + return m +} + +pub fn (mut m Mutex) init() { + C.pthread_mutex_init(&m.mutex, C.NULL) +} + +pub fn new_rwmutex() &RwMutex { + mut m := &RwMutex{} + m.init() + return m +} + +pub fn (mut m RwMutex) init() { + a := RwMutexAttr{} + C.pthread_rwlockattr_init(&a.attr) + // Give writer priority over readers + C.pthread_rwlockattr_setkind_np(&a.attr, C.PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP) + C.pthread_rwlockattr_setpshared(&a.attr, C.PTHREAD_PROCESS_PRIVATE) + C.pthread_rwlock_init(&m.mutex, &a.attr) +} + +// @lock(), for *manual* mutex handling, since `lock` is a keyword +pub fn (mut m Mutex) @lock() { + C.pthread_mutex_lock(&m.mutex) +} + +pub fn (mut m Mutex) unlock() { + C.pthread_mutex_unlock(&m.mutex) +} + +// RwMutex has separate read- and write locks +pub fn (mut m RwMutex) @rlock() { + C.pthread_rwlock_rdlock(&m.mutex) +} + +pub fn (mut m RwMutex) @lock() { + C.pthread_rwlock_wrlock(&m.mutex) +} + +// Windows SRWLocks have different function to unlock +// So provide two functions here, too, to have a common interface +pub fn (mut m RwMutex) runlock() { + C.pthread_rwlock_unlock(&m.mutex) +} + +pub fn (mut m RwMutex) unlock() { + C.pthread_rwlock_unlock(&m.mutex) +} + +[inline] +pub fn new_semaphore() &Semaphore { + return new_semaphore_init(0) +} + +pub fn new_semaphore_init(n u32) &Semaphore { + mut sem := &Semaphore{} + sem.init(n) + return sem +} + +pub fn (mut sem Semaphore) init(n u32) { + C.atomic_store_u32(&sem.count, n) + C.pthread_mutex_init(&sem.mtx, C.NULL) + attr := CondAttr{} + C.pthread_condattr_init(&attr.attr) + C.pthread_condattr_setpshared(&attr.attr, C.PTHREAD_PROCESS_PRIVATE) + C.pthread_cond_init(&sem.cond, &attr.attr) + C.pthread_condattr_destroy(&attr.attr) +} + +pub fn (mut sem Semaphore) post() { + mut c := C.atomic_load_u32(&sem.count) + for c > 1 { + if C.atomic_compare_exchange_weak_u32(&sem.count, &c, c + 1) { + return + } + } + C.pthread_mutex_lock(&sem.mtx) + c = C.atomic_fetch_add_u32(&sem.count, 1) + if c == 0 { + C.pthread_cond_signal(&sem.cond) + } + C.pthread_mutex_unlock(&sem.mtx) +} + +pub fn (mut sem Semaphore) wait() { + mut c := C.atomic_load_u32(&sem.count) + for c > 0 { + if C.atomic_compare_exchange_weak_u32(&sem.count, &c, c - 1) { + return + } + } + C.pthread_mutex_lock(&sem.mtx) + c = C.atomic_load_u32(&sem.count) + + outer: for { + if c == 0 { + C.pthread_cond_wait(&sem.cond, &sem.mtx) + c = C.atomic_load_u32(&sem.count) + } + for c > 0 { + if C.atomic_compare_exchange_weak_u32(&sem.count, &c, c - 1) { + if c > 1 { + C.pthread_cond_signal(&sem.cond) + } + break outer + } + } + } + C.pthread_mutex_unlock(&sem.mtx) +} + +pub fn (mut sem Semaphore) try_wait() bool { + mut c := C.atomic_load_u32(&sem.count) + for c > 0 { + if C.atomic_compare_exchange_weak_u32(&sem.count, &c, c - 1) { + return true + } + } + return false +} + +pub fn (mut sem Semaphore) timed_wait(timeout time.Duration) bool { + mut c := C.atomic_load_u32(&sem.count) + for c > 0 { + if C.atomic_compare_exchange_weak_u32(&sem.count, &c, c - 1) { + return true + } + } + C.pthread_mutex_lock(&sem.mtx) + t_spec := timeout.timespec() + mut res := 0 + c = C.atomic_load_u32(&sem.count) + + outer: for { + if c == 0 { + res = C.pthread_cond_timedwait(&sem.cond, &sem.mtx, &t_spec) + if res == C.ETIMEDOUT { + break outer + } + c = C.atomic_load_u32(&sem.count) + } + for c > 0 { + if C.atomic_compare_exchange_weak_u32(&sem.count, &c, c - 1) { + if c > 1 { + C.pthread_cond_signal(&sem.cond) + } + break outer + } + } + } + C.pthread_mutex_unlock(&sem.mtx) + return res == 0 +} + +pub fn (mut sem Semaphore) destroy() { + mut res := C.pthread_cond_destroy(&sem.cond) + if res == 0 { + res = C.pthread_mutex_destroy(&sem.mtx) + if res == 0 { + return + } + } + panic(unsafe { tos_clone(&byte(C.strerror(res))) }) +} diff --git a/v_windows/v/old/vlib/sync/sync_windows.c.v b/v_windows/v/old/vlib/sync/sync_windows.c.v new file mode 100644 index 0000000..d0942b7 --- /dev/null +++ b/v_windows/v/old/vlib/sync/sync_windows.c.v @@ -0,0 +1,212 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module sync + +import time + +#include +#include + +fn C.GetSystemTimeAsFileTime(lpSystemTimeAsFileTime &C._FILETIME) +fn C.InitializeConditionVariable(voidptr) +fn C.WakeConditionVariable(voidptr) +fn C.SleepConditionVariableSRW(voidptr, voidptr, u32, u32) int + +// TODO: The suggestion of using CriticalSection instead of mutex +// was discussed. Needs consideration. + +// Mutex HANDLE +type MHANDLE = voidptr + +// Semaphore HANDLE +type SHANDLE = voidptr + +//[init_with=new_mutex] // TODO: implement support for this struct attribute, and disallow Mutex{} from outside the sync.new_mutex() function. + +// `SRWLOCK` is much more performant that `Mutex` on Windows, so use that in both cases since we don't want to share with other processes +[heap] +pub struct Mutex { +mut: + mx C.SRWLOCK // mutex handle +} + +[heap] +pub struct RwMutex { +mut: + mx C.SRWLOCK // mutex handle +} + +[heap] +struct Semaphore { + mtx C.SRWLOCK + cond C.CONDITION_VARIABLE +mut: + count u32 +} + +pub fn new_mutex() &Mutex { + mut m := &Mutex{} + m.init() + return m +} + +pub fn new_rwmutex() &RwMutex { + mut m := &RwMutex{} + m.init() + return m +} + +pub fn (mut m Mutex) init() { + C.InitializeSRWLock(&m.mx) +} + +pub fn (mut m RwMutex) init() { + C.InitializeSRWLock(&m.mx) +} + +pub fn (mut m Mutex) @lock() { + C.AcquireSRWLockExclusive(&m.mx) +} + +pub fn (mut m Mutex) unlock() { + C.ReleaseSRWLockExclusive(&m.mx) +} + +// RwMutex has separate read- and write locks +pub fn (mut m RwMutex) @rlock() { + C.AcquireSRWLockShared(&m.mx) +} + +pub fn (mut m RwMutex) @lock() { + C.AcquireSRWLockExclusive(&m.mx) +} + +// Windows SRWLocks have different function to unlock +// So provide two functions here, too, to have a common interface +pub fn (mut m RwMutex) runlock() { + C.ReleaseSRWLockShared(&m.mx) +} + +pub fn (mut m RwMutex) unlock() { + C.ReleaseSRWLockExclusive(&m.mx) +} + +pub fn (mut m Mutex) destroy() { + // nothing to do +} + +[inline] +pub fn new_semaphore() &Semaphore { + return new_semaphore_init(0) +} + +pub fn new_semaphore_init(n u32) &Semaphore { + mut sem := &Semaphore{} + sem.init(n) + return sem +} + +pub fn (mut sem Semaphore) init(n u32) { + C.atomic_store_u32(&sem.count, n) + C.InitializeSRWLock(&sem.mtx) + C.InitializeConditionVariable(&sem.cond) +} + +pub fn (mut sem Semaphore) post() { + mut c := C.atomic_load_u32(&sem.count) + for c > 1 { + if C.atomic_compare_exchange_weak_u32(&sem.count, &c, c + 1) { + return + } + } + C.AcquireSRWLockExclusive(&sem.mtx) + c = C.atomic_fetch_add_u32(&sem.count, 1) + if c == 0 { + C.WakeConditionVariable(&sem.cond) + } + C.ReleaseSRWLockExclusive(&sem.mtx) +} + +pub fn (mut sem Semaphore) wait() { + mut c := C.atomic_load_u32(&sem.count) + for c > 0 { + if C.atomic_compare_exchange_weak_u32(&sem.count, &c, c - 1) { + return + } + } + C.AcquireSRWLockExclusive(&sem.mtx) + c = C.atomic_load_u32(&sem.count) + + outer: for { + if c == 0 { + C.SleepConditionVariableSRW(&sem.cond, &sem.mtx, C.INFINITE, 0) + c = C.atomic_load_u32(&sem.count) + } + for c > 0 { + if C.atomic_compare_exchange_weak_u32(&sem.count, &c, c - 1) { + if c > 1 { + C.WakeConditionVariable(&sem.cond) + } + break outer + } + } + } + C.ReleaseSRWLockExclusive(&sem.mtx) +} + +pub fn (mut sem Semaphore) try_wait() bool { + mut c := C.atomic_load_u32(&sem.count) + for c > 0 { + if C.atomic_compare_exchange_weak_u32(&sem.count, &c, c - 1) { + return true + } + } + return false +} + +pub fn (mut sem Semaphore) timed_wait(timeout time.Duration) bool { + mut c := C.atomic_load_u32(&sem.count) + for c > 0 { + if C.atomic_compare_exchange_weak_u32(&sem.count, &c, c - 1) { + return true + } + } + mut ft_start := C._FILETIME{} + C.GetSystemTimeAsFileTime(&ft_start) + time_end := ((u64(ft_start.dwHighDateTime) << 32) | ft_start.dwLowDateTime) + + u64(timeout / (100 * time.nanosecond)) + mut t_ms := timeout.sys_milliseconds() + C.AcquireSRWLockExclusive(&sem.mtx) + mut res := 0 + c = C.atomic_load_u32(&sem.count) + + outer: for { + if c == 0 { + res = C.SleepConditionVariableSRW(&sem.cond, &sem.mtx, t_ms, 0) + if res == 0 { + break outer + } + c = C.atomic_load_u32(&sem.count) + } + for c > 0 { + if C.atomic_compare_exchange_weak_u32(&sem.count, &c, c - 1) { + if c > 1 { + C.WakeConditionVariable(&sem.cond) + } + break outer + } + } + C.GetSystemTimeAsFileTime(&ft_start) + time_now := ((u64(ft_start.dwHighDateTime) << 32) | ft_start.dwLowDateTime) // in 100ns + if time_now > time_end { + break outer // timeout exceeded + } + t_ms = u32((time_end - time_now) / 10000) + } + C.ReleaseSRWLockExclusive(&sem.mtx) + return res != 0 +} + +pub fn (s Semaphore) destroy() { +} diff --git a/v_windows/v/old/vlib/sync/threads/threads.c.v b/v_windows/v/old/vlib/sync/threads/threads.c.v new file mode 100644 index 0000000..02506c2 --- /dev/null +++ b/v_windows/v/old/vlib/sync/threads/threads.c.v @@ -0,0 +1,13 @@ +module threads + +// This module adds the necessary compiler flags for using threads. +// It is automatically imported by code that does `go func()` . +// See vlib/v/parser/pratt.v, search for ast.GoExpr . +// The goal is that programs, that do not use threads at all will not need +// to link to -lpthread etc. +// NB: on some platforms like Android, linking -lpthread is not needed too. +// See https://stackoverflow.com/a/31277163/1904615 + +$if !windows && !android { + #flag -lpthread +} diff --git a/v_windows/v/old/vlib/sync/threads/threads.v b/v_windows/v/old/vlib/sync/threads/threads.v new file mode 100644 index 0000000..f20fc0e --- /dev/null +++ b/v_windows/v/old/vlib/sync/threads/threads.v @@ -0,0 +1,4 @@ +module threads + +// This file is just a placeholder. +// The actual implementation is backend/platform specific, so see threads.c.v, threads.js.v etc. diff --git a/v_windows/v/old/vlib/sync/waitgroup.v b/v_windows/v/old/vlib/sync/waitgroup.v new file mode 100644 index 0000000..3e9ac39 --- /dev/null +++ b/v_windows/v/old/vlib/sync/waitgroup.v @@ -0,0 +1,84 @@ +// Copyright (c) 2019 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module sync + +[trusted] +fn C.atomic_fetch_add_u32(voidptr, u32) u32 + +[trusted] +fn C.atomic_load_u32(voidptr) u32 + +[trusted] +fn C.atomic_compare_exchange_weak_u32(voidptr, voidptr, u32) bool + +// WaitGroup +// Do not copy an instance of WaitGroup, use a ref instead. +// +// usage: in main thread: +// `wg := sync.new_waitgroup() +// `wg.add(nr_jobs)` before starting jobs with `go ...` +// `wg.wait()` to wait for all jobs to have finished +// +// in each parallel job: +// `wg.done()` when finished +// +// [init_with=new_waitgroup] // TODO: implement support for init_with struct attribute, and disallow WaitGroup{} from outside the sync.new_waitgroup() function. +[heap] +struct WaitGroup { +mut: + task_count u32 // current task count - reading/writing should be atomic + wait_count u32 // current wait count - reading/writing should be atomic + sem Semaphore // This blocks wait() until tast_countreleased by add() +} + +pub fn new_waitgroup() &WaitGroup { + mut wg := WaitGroup{} + wg.init() + return &wg +} + +pub fn (mut wg WaitGroup) init() { + wg.sem.init(0) +} + +// add increments (+ve delta) or decrements (-ve delta) task count by delta +// and unblocks any wait() calls if task count becomes zero. +// add panics if task count drops below zero. +pub fn (mut wg WaitGroup) add(delta int) { + old_nrjobs := int(C.atomic_fetch_add_u32(&wg.task_count, u32(delta))) + new_nrjobs := old_nrjobs + delta + mut num_waiters := C.atomic_load_u32(&wg.wait_count) + if new_nrjobs < 0 { + panic('Negative number of jobs in waitgroup') + } + + if new_nrjobs == 0 && num_waiters > 0 { + // clear waiters + for !C.atomic_compare_exchange_weak_u32(&wg.wait_count, &num_waiters, 0) { + if num_waiters == 0 { + return + } + } + for (num_waiters > 0) { + wg.sem.post() + num_waiters-- + } + } +} + +// done is a convenience fn for add(-1) +pub fn (mut wg WaitGroup) done() { + wg.add(-1) +} + +// wait blocks until all tasks are done (task count becomes zero) +pub fn (mut wg WaitGroup) wait() { + nrjobs := int(C.atomic_load_u32(&wg.task_count)) + if nrjobs == 0 { + // no need to wait + return + } + C.atomic_fetch_add_u32(&wg.wait_count, 1) + wg.sem.wait() // blocks until task_count becomes 0 +} diff --git a/v_windows/v/old/vlib/sync/waitgroup_test.v b/v_windows/v/old/vlib/sync/waitgroup_test.v new file mode 100644 index 0000000..493665f --- /dev/null +++ b/v_windows/v/old/vlib/sync/waitgroup_test.v @@ -0,0 +1,41 @@ +module sync + +import time + +fn test_waitgroup_reuse() { + mut wg := new_waitgroup() + + wg.add(1) + wg.done() + + wg.add(1) + mut executed := false + go fn (mut wg WaitGroup, executed voidptr) { + defer { + wg.done() + } + unsafe { + *(&bool(executed)) = true + } + time.sleep(100 * time.millisecond) + assert wg.wait_count == 1 + }(mut wg, voidptr(&executed)) + + wg.wait() + assert executed + assert wg.wait_count == 0 +} + +fn test_waitgroup_no_use() { + mut done := false + go fn (done voidptr) { + time.sleep(1 * time.second) + if *(&bool(done)) == false { + panic('test_waitgroup_no_use did not complete in time') + } + }(voidptr(&done)) + + mut wg := new_waitgroup() + wg.wait() + done = true +} diff --git a/v_windows/v/old/vlib/szip/szip.v b/v_windows/v/old/vlib/szip/szip.v new file mode 100644 index 0000000..9cb157d --- /dev/null +++ b/v_windows/v/old/vlib/szip/szip.v @@ -0,0 +1,296 @@ +module szip + +import os + +#flag -I @VEXEROOT/thirdparty/zip +#include "zip.c" +#include "zip.h" + +struct C.zip_t { +} + +type Zip = C.zip_t + +fn C.zip_open(&char, int, char) &Zip + +fn C.zip_close(&Zip) + +fn C.zip_entry_open(&Zip, &byte) int + +fn C.zip_entry_openbyindex(&Zip, int) int + +fn C.zip_entry_close(&Zip) int + +fn C.zip_entry_name(&Zip) &byte + +fn C.zip_entry_index(&Zip) int + +fn C.zip_entry_isdir(&Zip) int + +fn C.zip_entry_size(&Zip) u64 + +fn C.zip_entry_crc32(&Zip) u32 + +fn C.zip_entry_write(&Zip, voidptr, size_t) int + +fn C.zip_entry_fwrite(&Zip, &char) int + +fn C.zip_entry_read(&Zip, &voidptr, &size_t) int + +fn C.zip_entry_noallocread(&Zip, voidptr, size_t) int + +fn C.zip_entry_fread(&Zip, &char) int + +fn C.zip_total_entries(&Zip) int + +fn C.zip_extract_without_callback(&char, &char) int + +// CompressionLevel lists compression levels, see in "thirdparty/zip/miniz.h" +pub enum CompressionLevel { + no_compression = 0 + best_speed = 1 + best_compression = 9 + uber_compression = 10 + default_level = 6 + default_compression = -1 +} + +// OpenMode lists the opening modes +// .write: opens a file for reading/extracting (the file must exists). +// .read_only: creates an empty file for writing. +// .append: appends to an existing archive. +pub enum OpenMode { + write + read_only + append +} + +[inline] +fn (om OpenMode) to_byte() byte { + return match om { + .write { + `w` + } + .read_only { + `r` + } + .append { + `a` + } + } +} + +// open opens zip archive with compression level using the given mode. +// name: the name of the zip file to open. +// level: can be any value of the CompressionLevel enum. +// mode: can be any value of the OpenMode enum. +pub fn open(name string, level CompressionLevel, mode OpenMode) ?&Zip { + if name.len == 0 { + return error('szip: name of file empty') + } + p_zip := &Zip(C.zip_open(&char(name.str), int(level), char(mode.to_byte()))) + if isnil(p_zip) { + return error('szip: cannot open/create/append new zip archive') + } + return p_zip +} + +// close closes the zip archive, releases resources - always finalize. +[inline] +pub fn (mut z Zip) close() { + C.zip_close(z) +} + +// open_entry opens an entry by name in the zip archive. +// For zip archive opened in 'w' or 'a' mode the function will append +// a new entry. In readonly mode the function tries to locate the entry +// in global dictionary. +pub fn (mut zentry Zip) open_entry(name string) ? { + res := C.zip_entry_open(zentry, &char(name.str)) + if res == -1 { + return error('szip: cannot open archive entry') + } +} + +// open_entry_by_index opens an entry by index in the archive. +pub fn (mut z Zip) open_entry_by_index(index int) ? { + res := C.zip_entry_openbyindex(z, index) + if res == -1 { + return error('szip: cannot open archive entry at index $index') + } +} + +// close_entry closes a zip entry, flushes buffer and releases resources. +[inline] +pub fn (mut zentry Zip) close_entry() { + C.zip_entry_close(zentry) +} + +// name returns a local name of the current zip entry. +// The main difference between user's entry name and local entry name +// is optional relative path. +// Following .ZIP File Format Specification - the path stored MUST not contain +// a drive or device letter, or a leading slash. +// All slashes MUST be forward slashes '/' as opposed to backwards slashes '\' +// for compatibility with Amiga and UNIX file systems etc. +pub fn (mut zentry Zip) name() string { + name := unsafe { &byte(C.zip_entry_name(zentry)) } + if name == 0 { + return '' + } + return unsafe { name.vstring() } +} + +// index returns an index of the current zip entry. +pub fn (mut zentry Zip) index() ?int { + index := int(C.zip_entry_index(zentry)) + if index == -1 { + return error('szip: cannot get current index of zip entry') + } + return index // must be check for INVALID_VALUE +} + +// is_dir determines if the current zip entry is a directory entry. +pub fn (mut zentry Zip) is_dir() ?bool { + isdir := C.zip_entry_isdir(zentry) + if isdir < 0 { + return error('szip: cannot check entry type') + } + return isdir == 1 +} + +// size returns an uncompressed size of the current zip entry. +[inline] +pub fn (mut zentry Zip) size() u64 { + return C.zip_entry_size(zentry) +} + +// crc32 returns CRC-32 checksum of the current zip entry. +[inline] +pub fn (mut zentry Zip) crc32() u32 { + return C.zip_entry_crc32(zentry) +} + +// write_entry compresses an input buffer for the current zip entry. +pub fn (mut zentry Zip) write_entry(data []byte) ? { + if (data[0] & 0xff) == -1 { + return error('szip: cannot write entry') + } + res := C.zip_entry_write(zentry, data.data, data.len) + if res != 0 { + return error('szip: failed to write entry') + } +} + +// create_entry compresses a file for the current zip entry. +pub fn (mut zentry Zip) create_entry(name string) ? { + res := C.zip_entry_fwrite(zentry, &char(name.str)) + if res != 0 { + return error('szip: failed to create entry') + } +} + +// read_entry extracts the current zip entry into output buffer. +// The function allocates sufficient memory for an output buffer. +// NOTE: remember to release the memory allocated for an output buffer. +// for large entries, please take a look at zip_entry_extract function. +pub fn (mut zentry Zip) read_entry() ?voidptr { + mut buf := &byte(0) + mut bsize := size_t(0) + res := C.zip_entry_read(zentry, unsafe { &voidptr(&buf) }, &bsize) + if res == -1 { + return error('szip: cannot read properly data from entry') + } + return buf +} + +// read_entry_buf extracts the current zip entry into user specified buffer +pub fn (mut zentry Zip) read_entry_buf(buf voidptr, in_bsize int) ?int { + bsize := size_t(in_bsize) + res := C.zip_entry_noallocread(zentry, buf, bsize) + if res == -1 { + return error('szip: cannot read properly data from entry') + } + return res +} + +// extract_entry extracts the current zip entry into output file. +pub fn (mut zentry Zip) extract_entry(path string) ? { + if !os.is_file(path) { + return error('szip: cannot open file for extracting, "$path" not exists') + } + res := C.zip_entry_fread(zentry, &char(path.str)) + if res != 0 { + return error('szip: failed to extract entry') + } +} + +// extract zip file to directory +pub fn extract_zip_to_dir(file string, dir string) ?bool { + if C.access(&char(dir.str), 0) == -1 { + return error('szip: cannot open directory for extracting, directory not exists') + } + res := C.zip_extract_without_callback(&char(file.str), &char(dir.str)) + return res == 0 +} + +// zip files (full path) to zip file +pub fn zip_files(path_to_file []string, path_to_export_zip string) ? { + // open or create new zip + mut zip := open(path_to_export_zip, .no_compression, .write) or { panic(err) } + + // add all files from the directory to the archive + for file in path_to_file { + // add file to zip + zip.open_entry(os.base(file)) or { panic(err) } + file_as_byte := os.read_bytes(file) or { panic(err) } + zip.write_entry(file_as_byte) or { panic(err) } + + zip.close_entry() + } + + // close zip + defer { + zip.close() + } +} + +/* +TODO add +// zip all files in directory to zip file +pub fn zip_folder(path_to_dir string, path_to_export_zip string) { + + // get list files from directory + files := os.ls(path_to_dir) or { panic(err) } + + // open or create new zip + mut zip := szip.open(path_to_export_zip, .no_compression, .write) or { panic(err) } + + // add all files from the directory to the archive + for file in files { + eprintln('Zipping $file to ${path_to_export_zip}...') + println(path_to_dir + file) + + // add file to zip + zip.open_entry(file) or { panic(err) } + file_as_byte := os.read_bytes(path_to_dir + '/'+ file) or { panic(err) } + zip.write_entry(file_as_byte) or { panic(err) } + + zip.close_entry() + } + + // close zip + zip.close() + + eprintln('Successfully') +} +*/ + +// total returns the number of all entries (files and directories) in the zip archive. +pub fn (mut zentry Zip) total() ?int { + tentry := int(C.zip_total_entries(zentry)) + if tentry == -1 { + return error('szip: cannot count total entries') + } + return tentry +} diff --git a/v_windows/v/old/vlib/szip/szip_test.v b/v_windows/v/old/vlib/szip/szip_test.v new file mode 100644 index 0000000..a20f638 --- /dev/null +++ b/v_windows/v/old/vlib/szip/szip_test.v @@ -0,0 +1,88 @@ +import szip +import os + +const ( + test_out_zip = 'v_test_zip.zip' + test_path = 'zip files' + fname1 = 'file_1.txt' + fpath1 = os.join_path(test_path, fname1) + fname2 = 'file_2.txt' + fpath2 = os.join_path(test_path, fname2) +) + +fn test_szip_create_temp_files() ? { + os.chdir(os.temp_dir()) + os.rmdir_all(test_path) or {} + os.mkdir(test_path) ? + os.write_file(fpath1, 'file one') ? + os.write_file(fpath2, 'file two') ? + assert os.exists(fpath1) + assert os.exists(fpath2) +} + +fn test_zipping_files() ? { + files := (os.ls(test_path) ?).map(os.join_path(test_path, it)) + szip.zip_files(files, test_out_zip) ? + assert os.exists(test_out_zip) +} + +fn test_extract_zipped_files() ? { + os.rm(fpath1) ? + os.rm(fpath2) ? + szip.extract_zip_to_dir(test_out_zip, test_path) ? + assert os.exists(fpath1) + assert os.exists(fpath2) + assert (os.read_file(fpath1) ?) == 'file one' + assert (os.read_file(fpath2) ?) == 'file two' + os.rmdir_all(test_path) ? + os.rm(test_out_zip) or {} +} + +fn test_reading_zipping_files() ? { + n_files := 2 + mut file_name_list := []string{} + for i in 0 .. n_files { + file_name_list << 'file_${i:02}.txt' + } + + os.chdir(os.temp_dir()) + os.rmdir_all(test_path) or {} + os.mkdir(test_path) ? + for c, f_name in file_name_list { + tmp_path := os.join_path(test_path, f_name) + os.write_file(tmp_path, 'file ${c:02}') ? + assert os.exists(tmp_path) + } + files := (os.ls(test_path) ?).map(os.join_path(test_path, it)) + + szip.zip_files(files, test_out_zip) ? + assert os.exists(test_out_zip) + + mut zp := szip.open(test_out_zip, szip.CompressionLevel.no_compression, szip.OpenMode.read_only) ? + n_entries := zp.total() ? + assert n_entries == n_files + + unsafe { + data_len := 'file XX'.len + buf_size := 32 + buf := malloc(data_len * 2) + + for _ in 0 .. n_files { + zp.open_entry_by_index(0) ? + name := zp.name() + assert name in file_name_list + + zp.read_entry_buf(buf, buf_size) ? + buf[data_len] = 0 + tmp_str := tos(buf, data_len) + + assert tmp_str[0..4] == 'file' + assert tmp_str[5..7] == name[5..7] + + zp.close_entry() + } + + free(buf) + } + zp.close() +} diff --git a/v_windows/v/old/vlib/term/README.md b/v_windows/v/old/vlib/term/README.md new file mode 100644 index 0000000..8708226 --- /dev/null +++ b/v_windows/v/old/vlib/term/README.md @@ -0,0 +1,84 @@ +# Quickstart + +The V `term` module is a module designed to provide the building blocks +for building very simple TUI apps. +For more complex apps, you should really look at the `term.input` module, +as it includes terminal events, is easier to use and is much more performant for large draws. + +# Use + +You can use the `term` module to either color the output on a terminal +or to decide on where to put the output in your terminal. + +For example let's make a simple program which prints colored text in the middle of the terminal. + +```v +import term +import os + +fn main() { + term.clear() // clears the content in the terminal + width, height := term.get_terminal_size() // get the size of the terminal + term.set_cursor_position(x: width / 2, y: height / 2) // now we point the cursor to the middle of the terminal + println(term.strikethrough(term.bright_green('hello world'))) // Print green text + term.set_cursor_position(x: 0, y: height) // Sets the position of the cursor to the bottom of the terminal + // Keep prompting until the user presses the q key + for { + if var := os.input_opt('press q to quit: ') { + if var != 'q' { + continue + } + break + } + println('') + break + } + println('Goodbye.') +} +``` + +This simple program covers many of the principal aspects of the `term ` module. + +# API + +Here are some functions you should be aware of in the `term `module: + +```v oksyntax +import term + +// returns the height and the width of the terminal +width, height := term.get_terminal_size() +// returns the string as green text to be printed on stdout +term.ok_message('cool') +// returns the string as red text to be printed on stdout +term.fail_message('oh, no') +// returns the string as yellow text to be printed on stdout +term.warning_message('be warned') +// clears the entire terminal and leaves a blank one +term.clear() +// colors the output of the output, the available colors are: black,blue,yellow,green,cyan,gray,bright_blue,bright_green,bright_red,bright_black,bright_cyan +term.yellow('submarine') +// transforms the given string into bold text +term.bold('and beautiful') +// puts a strikethrough into the given string +term.strikethrough('the core of the problem') +// underlines the given string +term.underline('important') +// colors the background of the output following the given color +// the available colors are: black, blue, yellow, green, cyan, gray +term.bg_green('field') +// sets the position of the cursor at a given place in the terminal +term.set_cursor_position(x: 5, y: 10) +// moves the cursor up +term.cursor_up() +// moves the cursor down +term.cursor_down() +// moves the cursor to the right +term.cursor_forward() +// moves the cursor to the left +term.cursor_back() +// shows the cursor +term.show_cursor() +// hides the cursor +term.hide_cursor() +``` diff --git a/v_windows/v/old/vlib/term/colors.v b/v_windows/v/old/vlib/term/colors.v new file mode 100644 index 0000000..f7662ee --- /dev/null +++ b/v_windows/v/old/vlib/term/colors.v @@ -0,0 +1,199 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module term + +pub fn format(msg string, open string, close string) string { + return '\x1b[' + open + 'm' + msg + '\x1b[' + close + 'm' +} + +pub fn format_rgb(r int, g int, b int, msg string, open string, close string) string { + return '\x1b[' + open + ';2;' + r.str() + ';' + g.str() + ';' + b.str() + 'm' + msg + '\x1b[' + + close + 'm' +} + +pub fn rgb(r int, g int, b int, msg string) string { + return format_rgb(r, g, b, msg, '38', '39') +} + +pub fn bg_rgb(r int, g int, b int, msg string) string { + return format_rgb(r, g, b, msg, '48', '49') +} + +pub fn hex(hex int, msg string) string { + return format_rgb(hex >> 16, hex >> 8 & 0xFF, hex & 0xFF, msg, '38', '39') +} + +pub fn bg_hex(hex int, msg string) string { + return format_rgb(hex >> 16, hex >> 8 & 0xFF, hex & 0xFF, msg, '48', '49') +} + +pub fn bg_black(msg string) string { + return format(msg, '40', '49') +} + +pub fn bright_bg_black(msg string) string { + return format(msg, '100', '49') +} + +pub fn bg_blue(msg string) string { + return format(msg, '44', '49') +} + +pub fn bright_bg_blue(msg string) string { + return format(msg, '104', '49') +} + +pub fn bg_cyan(msg string) string { + return format(msg, '46', '49') +} + +pub fn bright_bg_cyan(msg string) string { + return format(msg, '106', '49') +} + +pub fn bg_green(msg string) string { + return format(msg, '42', '49') +} + +pub fn bright_bg_green(msg string) string { + return format(msg, '102', '49') +} + +pub fn bg_magenta(msg string) string { + return format(msg, '45', '49') +} + +pub fn bright_bg_magenta(msg string) string { + return format(msg, '105', '49') +} + +pub fn bg_red(msg string) string { + return format(msg, '41', '49') +} + +pub fn bright_bg_red(msg string) string { + return format(msg, '101', '49') +} + +pub fn bg_white(msg string) string { + return format(msg, '47', '49') +} + +pub fn bright_bg_white(msg string) string { + return format(msg, '107', '49') +} + +pub fn bg_yellow(msg string) string { + return format(msg, '43', '49') +} + +pub fn bright_bg_yellow(msg string) string { + return format(msg, '103', '49') +} + +pub fn black(msg string) string { + return format(msg, '30', '39') +} + +pub fn bright_black(msg string) string { + return format(msg, '90', '39') +} + +pub fn blue(msg string) string { + return format(msg, '34', '39') +} + +pub fn bright_blue(msg string) string { + return format(msg, '94', '39') +} + +pub fn bold(msg string) string { + return format(msg, '1', '22') +} + +pub fn cyan(msg string) string { + return format(msg, '36', '39') +} + +pub fn bright_cyan(msg string) string { + return format(msg, '96', '39') +} + +pub fn dim(msg string) string { + return format(msg, '2', '22') +} + +pub fn green(msg string) string { + return format(msg, '32', '39') +} + +pub fn bright_green(msg string) string { + return format(msg, '92', '39') +} + +pub fn gray(msg string) string { + return bright_black(msg) +} + +pub fn hidden(msg string) string { + return format(msg, '8', '28') +} + +pub fn italic(msg string) string { + return format(msg, '3', '23') +} + +pub fn inverse(msg string) string { + return format(msg, '7', '27') +} + +pub fn magenta(msg string) string { + return format(msg, '35', '39') +} + +pub fn bright_magenta(msg string) string { + return format(msg, '95', '39') +} + +pub fn reset(msg string) string { + return format(msg, '0', '0') +} + +pub fn red(msg string) string { + return format(msg, '31', '39') +} + +pub fn bright_red(msg string) string { + return format(msg, '91', '39') +} + +pub fn strikethrough(msg string) string { + return format(msg, '9', '29') +} + +pub fn underline(msg string) string { + return format(msg, '4', '24') +} + +pub fn white(msg string) string { + return format(msg, '37', '39') +} + +pub fn bright_white(msg string) string { + return format(msg, '97', '39') +} + +pub fn yellow(msg string) string { + return format(msg, '33', '39') +} + +pub fn bright_yellow(msg string) string { + return format(msg, '93', '39') +} + +// highlight_command highlights the command with an on-brand background +// to make CLI commands immediately recognizable. +pub fn highlight_command(command string) string { + return bright_white(bg_cyan(' $command ')) +} diff --git a/v_windows/v/old/vlib/term/control.v b/v_windows/v/old/vlib/term/control.v new file mode 100644 index 0000000..377cc42 --- /dev/null +++ b/v_windows/v/old/vlib/term/control.v @@ -0,0 +1,108 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module term + +// Sources for ANSI Control Sequences +// https://github.com/RajeshPatkarInstitute/Panim +// https://www.gnu.org/software/screen/manual/html_node/Control-Sequences.html +// https://en.wikipedia.org/wiki/ANSI_escape_code +// Support for Windows +// https://en.wikipedia.org/wiki/ANSI.SYS +// #include +// C.SetConsoleMode(C.ENABLE_VIRTUAL_TERMINAL_INPUT) +// Setting cursor to the given position +// x is the x coordinate +// y is the y coordinate +pub fn set_cursor_position(c Coord) { + print('\x1b[$c.y;$c.x' + 'H') +} + +// n is number of cells +// direction: A is up / North +// direction: B is down / South +// direction: C is forward / East +// direction: D is backward / West +pub fn move(n int, direction string) { + print('\x1b[$n$direction') +} + +pub fn cursor_up(n int) { + move(n, 'A') +} + +pub fn cursor_down(n int) { + move(n, 'B') +} + +pub fn cursor_forward(n int) { + move(n, 'C') +} + +pub fn cursor_back(n int) { + move(n, 'D') +} + +// type: 0 -> current cursor position to end of the screen +// type: 1 -> current cursor position to beginning of the screen +// type: 2 -> clears entire screen +// type: 3 -> clears entire screen and also delete scrollback buffer + +pub fn erase_display(t string) { + print('\x1b[' + t + 'J') +} + +pub fn erase_toend() { + erase_display('0') +} + +pub fn erase_tobeg() { + erase_display('1') +} + +// clears entire screen and returns cursor to top left-corner +pub fn erase_clear() { + print('\033[H\033[J') +} + +pub fn erase_del_clear() { + erase_display('3') +} + +// type: 0 -> current cursor position to end of the line +// type: 1 -> current cursor position to beginning of the line +// type: 2 -> clears entire line +// Note: Cursor position does not change +pub fn erase_line(t string) { + print('\x1b[' + t + 'K') +} + +pub fn erase_line_toend() { + erase_line('0') +} + +pub fn erase_line_tobeg() { + erase_line('1') +} + +pub fn erase_line_clear() { + erase_line('2') +} + +// Will make cursor appear if not visible +pub fn show_cursor() { + print('\x1b[?25h') +} + +// Will make cursor invisible +pub fn hide_cursor() { + print('\x1b[?25l') +} + +// clear_previous_line - useful for progressbars. +// It moves the cursor to start of line, then 1 line above, +// then erases the line. In effect the next println will overwrite +// the previous content. +pub fn clear_previous_line() { + print('\r\x1b[1A\x1b[2K') +} diff --git a/v_windows/v/old/vlib/term/term.js.v b/v_windows/v/old/vlib/term/term.js.v new file mode 100644 index 0000000..be85eca --- /dev/null +++ b/v_windows/v/old/vlib/term/term.js.v @@ -0,0 +1,8 @@ +module term + +// get_terminal_size returns a number of colums and rows of terminal window. +pub fn get_terminal_size() (int, int) { + // TODO Find a way to get proper width & height of the terminal + // on a Javascript environment + return default_columns_size, default_rows_size +} diff --git a/v_windows/v/old/vlib/term/term.v b/v_windows/v/old/vlib/term/term.v new file mode 100644 index 0000000..390b3be --- /dev/null +++ b/v_windows/v/old/vlib/term/term.v @@ -0,0 +1,196 @@ +module term + +import os +import strings.textscanner + +const ( + default_columns_size = 80 + default_rows_size = 25 +) + +// Coord - used by term.get_cursor_position and term.set_cursor_position +pub struct Coord { +pub mut: + x int + y int +} + +// can_show_color_on_stdout returns true if colors are allowed in stdout; +// returns false otherwise. +pub fn can_show_color_on_stdout() bool { + return supports_escape_sequences(1) +} + +// can_show_color_on_stderr returns true if colors are allowed in stderr; +// returns false otherwise. +pub fn can_show_color_on_stderr() bool { + return supports_escape_sequences(2) +} + +// failed returns a bold white on red version of the string `s` +// If colors are not allowed, returns the string `s` +pub fn failed(s string) string { + if can_show_color_on_stdout() { + return bg_red(bold(white(s))) + } + return s +} + +// ok_message returns a colored string with green color. +// If colors are not allowed, returns a given string. +pub fn ok_message(s string) string { + if can_show_color_on_stdout() { + return green(' $s ') + } + return s +} + +// fail_message returns a colored string with red color. +// If colors are not allowed, returns a given string. +pub fn fail_message(s string) string { + return failed(' $s ') +} + +// warn_message returns a colored string with yellow color. +// If colors are not allowed, returns a given string. +pub fn warn_message(s string) string { + if can_show_color_on_stdout() { + return bright_yellow(' $s ') + } + return s +} + +// colorize returns a colored string by running the specified `cfn` over +// the message `s`, only if colored output is supported by the terminal. +// Example: term.colorize(term.yellow, 'the message') +pub fn colorize(cfn fn (string) string, s string) string { + if can_show_color_on_stdout() { + return cfn(s) + } + return s +} + +// strip_ansi removes any ANSI sequences in the `text` +pub fn strip_ansi(text string) string { + // This is a port of https://github.com/kilobyte/colorized-logs/blob/master/ansi2txt.c + // \e, [, 1, m, a, b, c, \e, [, 2, 2, m => abc + mut input := textscanner.new(text) + mut output := []byte{cap: text.len} + mut ch := 0 + for ch != -1 { + ch = input.next() + if ch == 27 { + ch = input.next() + if ch == `[` { + for { + ch = input.next() + if ch in [`;`, `?`] || (ch >= `0` && ch <= `9`) { + continue + } + break + } + } else if ch == `]` { + ch = input.next() + if ch >= `0` && ch <= `9` { + for { + ch = input.next() + if ch == -1 || ch == 7 { + break + } + if ch == 27 { + ch = input.next() + break + } + } + } + } else if ch == `%` { + ch = input.next() + } + } else if ch != -1 { + output << byte(ch) + } + } + return output.bytestr() +} + +// h_divider returns a horizontal divider line with a dynamic width, +// that depends on the current terminal settings. +// If an empty string is passed in, print enough spaces to make a new line +pub fn h_divider(divider string) string { + cols, _ := get_terminal_size() + mut result := '' + if divider.len > 0 { + result = divider.repeat(1 + (cols / divider.len)) + } else { + result = ' '.repeat(1 + cols) + } + return result[0..cols] +} + +// header_left returns a horizontal divider line with a title text on the left. +// e.g: term.header_left('TITLE', '=') +// ==== TITLE ========================= +pub fn header_left(text string, divider string) string { + plain_text := strip_ansi(text) + xcols, _ := get_terminal_size() + cols := imax(1, xcols) + relement := if divider.len > 0 { divider } else { ' ' } + hstart := relement.repeat(4)[0..4] + remaining_cols := (cols - (hstart.len + 1 + plain_text.len + 1)) + hend := relement.repeat((remaining_cols + 1) / relement.len)[0..remaining_cols] + return '$hstart $text $hend' +} + +// header returns a horizontal divider line with a centered text in the middle. +// e.g: term.header('TEXT', '=') +// =============== TEXT =============== +pub fn header(text string, divider string) string { + if text.len == 0 { + return h_divider(divider) + } + xcols, _ := get_terminal_size() + cols := imax(1, xcols) + tlimit := imax(1, if cols > text.len + 2 + 2 * divider.len { + text.len + } else { + cols - 3 - 2 * divider.len + }) + tlimit_alligned := if (tlimit % 2) != (cols % 2) { tlimit + 1 } else { tlimit } + tstart := imax(0, (cols - tlimit_alligned) / 2) + mut ln := '' + if divider.len > 0 { + ln = divider.repeat(1 + cols / divider.len)[0..cols] + } else { + ln = ' '.repeat(1 + cols) + } + if ln.len == 1 { + return ln + ' ' + text[0..tlimit] + ' ' + ln + } + return ln[0..tstart] + ' ' + text[0..tlimit] + ' ' + ln[tstart + tlimit + 2..cols] +} + +fn imax(x int, y int) int { + return if x > y { x } else { y } +} + +fn supports_escape_sequences(fd int) bool { + vcolors_override := os.getenv('VCOLORS') + if vcolors_override == 'always' { + return true + } + if vcolors_override == 'never' { + return false + } + if os.getenv('TERM') == 'dumb' { + return false + } + $if windows { + if os.getenv('ConEmuANSI') == 'ON' { + return true + } + // 4 is enable_virtual_terminal_processing + return (os.is_atty(fd) & 0x0004) > 0 + } $else { + return os.is_atty(fd) > 0 + } +} diff --git a/v_windows/v/old/vlib/term/term_nix.c.v b/v_windows/v/old/vlib/term/term_nix.c.v new file mode 100644 index 0000000..45a0a9b --- /dev/null +++ b/v_windows/v/old/vlib/term/term_nix.c.v @@ -0,0 +1,105 @@ +module term + +import os + +#include +#include // TIOCGWINSZ + +pub struct C.winsize { +pub: + ws_row u16 + ws_col u16 + ws_xpixel u16 + ws_ypixel u16 +} + +fn C.ioctl(fd int, request u64, arg voidptr) int + +// get_terminal_size returns a number of colums and rows of terminal window. +pub fn get_terminal_size() (int, int) { + if os.is_atty(1) <= 0 || os.getenv('TERM') == 'dumb' { + return default_columns_size, default_rows_size + } + w := C.winsize{} + C.ioctl(1, u64(C.TIOCGWINSZ), &w) + return int(w.ws_col), int(w.ws_row) +} + +// get_cursor_position returns a Coord containing the current cursor position +pub fn get_cursor_position() Coord { + if os.is_atty(1) <= 0 || os.getenv('TERM') == 'dumb' { + return Coord{ + x: 0 + y: 0 + } + } + // TODO: use termios.h, C.tcgetattr & C.tcsetattr directly, + // instead of using `stty` + mut oldsettings := os.execute('stty -g') + if oldsettings.exit_code < 0 { + oldsettings = os.Result{} + } + os.system('stty -echo -icanon time 0') + print('\033[6n') + mut ch := int(0) + mut i := 0 + // ESC [ YYY `;` XXX `R` + mut reading_x := false + mut reading_y := false + mut x := 0 + mut y := 0 + for { + ch = C.getchar() + b := byte(ch) + i++ + if i >= 15 { + panic('C.getchar() called too many times') + } + // state management: + if b == `R` { + break + } + if b == `[` { + reading_y = true + reading_x = false + continue + } + if b == `;` { + reading_y = false + reading_x = true + continue + } + // converting string vals to ints: + if reading_x { + x *= 10 + x += (b - byte(`0`)) + } + if reading_y { + y *= 10 + y += (b - byte(`0`)) + } + } + // restore the old terminal settings: + os.system('stty $oldsettings.output') + return Coord{ + x: x + y: y + } +} + +// set_terminal_title change the terminal title +pub fn set_terminal_title(title string) bool { + if os.is_atty(1) <= 0 || os.getenv('TERM') == 'dumb' { + return true + } + print('\033]0') + print(title) + print('\007') + return true +} + +// clear clears current terminal screen. +pub fn clear() { + print('\x1b[2J') + print('\x1b[H') +} diff --git a/v_windows/v/old/vlib/term/term_test.v b/v_windows/v/old/vlib/term/term_test.v new file mode 100644 index 0000000..00f9293 --- /dev/null +++ b/v_windows/v/old/vlib/term/term_test.v @@ -0,0 +1,115 @@ +import os +import term + +fn test_get_terminal_size() { + cols, _ := term.get_terminal_size() + assert cols > 0 +} + +fn test_h_divider() { + divider := term.h_divider('-') + assert divider.len > 0 + assert divider[0] == `-` + assert divider[divider.len - 1] == `-` +} + +fn test_h_divider_multiple_characters() { + xdivider := term.h_divider('abc') + assert xdivider.len > 0 + assert xdivider.contains('abcabc') +} + +fn test_header() { + divider := term.h_divider('-') + term_width := divider.len + assert term_width > 0 + empty_header := term.header('', '-') + short_header := term.header('reasonable header', '-') + very_long_header := term.header(['abc'].repeat(500).join(' '), '-') + very_long_header_2 := term.header(['abcd'].repeat(500).join(' '), '-') + /* + eprintln(divider) + eprintln(empty_header) + eprintln(short_header) + eprintln(term.header('another longer header', '_-/\\')) + eprintln(term.header('another longer header', '-')) + eprintln(term.header('short', '-')) + eprintln(term.header('12345', '-')) + eprintln(term.header('1234', '-')) + eprintln(term.header('123', '-')) + eprintln(term.header('12', '-')) + eprintln(term.header('1', '-')) + eprintln(very_long_header) + eprintln(divider) + eprintln(very_long_header_2) + eprintln(term.header(['abcd'].repeat(500).join(' '), '_-/\\')) + eprintln(term.header(['abcd'].repeat(500).join(' '), '_-//')) + eprintln(term.header('1', '_-/\\\/')) + eprintln(term.header('12', '_-/\\\/')) + eprintln(term.header('123', '_-/\\\/')) + eprintln(term.header('1234', '_-/\\/\\')) + eprintln(term.header('', '-')) + */ + assert term_width == empty_header.len + assert term_width == short_header.len + assert term_width == very_long_header.len + assert term_width == very_long_header_2.len + assert term_width == term.header('1234', '_-/\\/\\').len +} + +fn test_get_cursor_position() { + original_position := term.get_cursor_position() + cursor_position_1 := term.get_cursor_position() + assert original_position.x == cursor_position_1.x + assert original_position.y == cursor_position_1.y + // + term.set_cursor_position( + x: 10 + y: 11 + ) + cursor_position_2 := term.get_cursor_position() + // + term.set_cursor_position( + x: 5 + y: 6 + ) + cursor_position_3 := term.get_cursor_position() + // + term.set_cursor_position(original_position) + eprintln('original_position: $original_position') + eprintln('cursor_position_2: $cursor_position_2') + eprintln('cursor_position_3: $cursor_position_3') + // 0,0 is returned on dumb terminals + if cursor_position_2.x == 0 && cursor_position_2.y == 0 { + return + } + if cursor_position_3.x == 0 && cursor_position_3.y == 0 { + return + } + assert cursor_position_2.x == 10 + assert cursor_position_2.y == 11 + assert cursor_position_3.x == 5 + assert cursor_position_3.y == 6 +} + +fn test_set_terminal_title() { + // do not change the current terminal title outside of CI: + if os.getenv('CI') != 'true' { + return + } + title_change := term.set_terminal_title('v is awesome!') + assert title_change == true +} + +fn test_strip_ansi() { + strings := [ + 'abc', + term.bold('abc'), + term.yellow('abc'), + term.bold(term.red('abc')), + term.strikethrough(term.inverse(term.dim(term.bold(term.bright_bg_blue('abc'))))), + ] + for s in strings { + assert term.strip_ansi(s) == 'abc' + } +} diff --git a/v_windows/v/old/vlib/term/term_windows.c.v b/v_windows/v/old/vlib/term/term_windows.c.v new file mode 100644 index 0000000..1d2f032 --- /dev/null +++ b/v_windows/v/old/vlib/term/term_windows.c.v @@ -0,0 +1,125 @@ +module term + +import os + +[typedef] +struct C.COORD { +mut: + X i16 + Y i16 +} + +[typedef] +struct C.SMALL_RECT { +mut: + Left u16 + Top u16 + Right u16 + Bottom u16 +} + +// win: CONSOLE_SCREEN_BUFFER_INFO +// https://docs.microsoft.com/en-us/windows/console/console-screen-buffer-info-str +[typedef] +struct C.CONSOLE_SCREEN_BUFFER_INFO { +mut: + dwSize C.COORD + dwCursorPosition C.COORD + wAttributes u16 + srWindow C.SMALL_RECT + dwMaximumWindowSize C.COORD +} + +union C.uChar { +mut: + UnicodeChar rune + AsciiChar byte +} + +[typedef] +struct C.CHAR_INFO { +mut: + Char C.uChar + Attributes u16 +} + +// ref - https://docs.microsoft.com/en-us/windows/console/getconsolescreenbufferinfo +fn C.GetConsoleScreenBufferInfo(handle C.HANDLE, info &C.CONSOLE_SCREEN_BUFFER_INFO) bool + +// ref - https://docs.microsoft.com/en-us/windows/console/setconsoletitle +fn C.SetConsoleTitle(title &u16) bool + +// ref - https://docs.microsoft.com/en-us/windows/console/setconsolecursorposition +fn C.SetConsoleCursorPosition(handle C.HANDLE, coord C.COORD) bool + +// ref - https://docs.microsoft.com/en-us/windows/console/scrollconsolescreenbuffer +fn C.ScrollConsoleScreenBuffer(output C.HANDLE, scroll_rect &C.SMALL_RECT, clip_rect &C.SMALL_RECT, des C.COORD, fill &C.CHAR_INFO) bool + +// get_terminal_size returns a number of colums and rows of terminal window. +pub fn get_terminal_size() (int, int) { + if os.is_atty(1) > 0 && os.getenv('TERM') != 'dumb' { + info := C.CONSOLE_SCREEN_BUFFER_INFO{} + if C.GetConsoleScreenBufferInfo(C.GetStdHandle(C.STD_OUTPUT_HANDLE), &info) { + columns := int(info.srWindow.Right - info.srWindow.Left + 1) + rows := int(info.srWindow.Bottom - info.srWindow.Top + 1) + return columns, rows + } + } + return default_columns_size, default_rows_size +} + +// get_cursor_position returns a Coord containing the current cursor position +pub fn get_cursor_position() Coord { + mut res := Coord{} + if os.is_atty(1) > 0 && os.getenv('TERM') != 'dumb' { + info := C.CONSOLE_SCREEN_BUFFER_INFO{} + if C.GetConsoleScreenBufferInfo(C.GetStdHandle(C.STD_OUTPUT_HANDLE), &info) { + res.x = info.dwCursorPosition.X + res.y = info.dwCursorPosition.Y + } + } + return res +} + +// set_terminal_title change the terminal title +pub fn set_terminal_title(title string) bool { + title_change := C.SetConsoleTitle(title.to_wide()) + return title_change +} + +// clear clears current terminal screen. +// Implementation taken from https://docs.microsoft.com/en-us/windows/console/clearing-the-screen#example-2. +pub fn clear() { + hconsole := C.GetStdHandle(C.STD_OUTPUT_HANDLE) + mut csbi := C.CONSOLE_SCREEN_BUFFER_INFO{} + mut scrollrect := C.SMALL_RECT{} + mut scrolltarget := C.COORD{} + mut fill := C.CHAR_INFO{} + + // Get the number of character cells in the current buffer. + if !C.GetConsoleScreenBufferInfo(hconsole, &csbi) { + return + } + // Scroll the rectangle of the entire buffer. + scrollrect.Left = 0 + scrollrect.Top = 0 + scrollrect.Right = u16(csbi.dwSize.X) + scrollrect.Bottom = u16(csbi.dwSize.Y) + + // Scroll it upwards off the top of the buffer with a magnitude of the entire height. + scrolltarget.X = 0 + scrolltarget.Y = (0 - csbi.dwSize.Y) + + // Fill with empty spaces with the buffer's default text attribute. + fill.Char.UnicodeChar = rune(` `) + fill.Attributes = csbi.wAttributes + + // Do the scroll + C.ScrollConsoleScreenBuffer(hconsole, &scrollrect, C.NULL, scrolltarget, &fill) + + // Move the cursor to the top left corner too. + csbi.dwCursorPosition.X = 0 + csbi.dwCursorPosition.Y = 0 + + C.SetConsoleCursorPosition(hconsole, csbi.dwCursorPosition) +} diff --git a/v_windows/v/old/vlib/term/ui/README.md b/v_windows/v/old/vlib/term/ui/README.md new file mode 100644 index 0000000..6bce054 --- /dev/null +++ b/v_windows/v/old/vlib/term/ui/README.md @@ -0,0 +1,99 @@ +## `term.ui` + +A V module for designing terminal UI apps + +#### Quickstart + +```v +import term.ui as tui + +struct App { +mut: + tui &tui.Context = 0 +} + +fn event(e &tui.Event, x voidptr) { + mut app := &App(x) + println(e) + if e.typ == .key_down && e.code == .escape { + exit(0) + } +} + +fn frame(x voidptr) { + mut app := &App(x) + + app.tui.clear() + app.tui.set_bg_color(r: 63, g: 81, b: 181) + app.tui.draw_rect(20, 6, 41, 10) + app.tui.draw_text(24, 8, 'Hello from V!') + app.tui.set_cursor_position(0, 0) + + app.tui.reset() + app.tui.flush() +} + +mut app := &App{} +app.tui = tui.init( + user_data: app + event_fn: event + frame_fn: frame + hide_cursor: true +) +app.tui.run() ? +``` + +See the `/examples/term.ui/` folder for more usage examples. + +#### Configuration + +- `user_data voidptr` - a pointer to any `user_data`, it will be passed as the last argument to + each callback. Used for accessing your app context from the different callbacks. +- `init_fn fn(voidptr)` - a callback that will be called after initialization + and before the first event / frame. Useful for initializing any user data. +- `frame_fn fn(voidptr)` - a callback that will be fired on each frame, + at a rate of `frame_rate` frames per second. +`event_fn fn(&Event, voidptr)` - a callback that will be fired for every event received. +- `cleanup_fn fn(voidptr)` - a callback that will be fired once, before the application exits. +- `fail_fn fn(string)` - a callback that will be fired + if a fatal error occurs during app initialization. +- `buffer_size int = 256` - the internal size of the read buffer. + Increasing it may help in case you're missing events, but you probably shouldn't lower + this value unless you make sure you're still receiving all events. In general, + higher frame rates work better with lower buffer sizes, and vice versa. +- `frame_rate int = 30` - the number of times per second that the `frame` callback will be fired. + 30fps is a nice balance between smoothness and performance, + but you can increase or lower it as you wish. +- `hide_cursor bool` - whether to hide the mouse cursor. Useful if you want to use your own. +- `capture_events bool` - sets the terminal into raw mode, which makes it intercept some + escape codes such as `ctrl + c` and `ctrl + z`. + Useful if you want to use those key combinations in your app. +- `window_title string` - sets the title of the terminal window. + This may be changed later, by calling the `set_window_title()` method. +- `reset []int = [1, 2, 3, 4, 6, 7, 8, 9, 11, 13, 14, 15, 19]` - a list of reset signals, + to setup handlers to cleanup the terminal state when they're received. + You should not need to change this, unless you know what you're doing. + +All of these fields may be omitted, in which case, the default value will be used. +In the case of the various callbacks, they will not be fired if a handler has not been specified. + + +#### FAQ + +Q: My terminal (doesn't receive events / doesn't print anything / prints gibberish characters), +what's up with that? +A: Please check if your terminal. The module has been tested with `xterm`-based terminals on Linux +(like `gnome-terminal` and `konsole`), and `Terminal.app` and `iterm2` on macOS. +If your terminal does not work, open an issue with the output of `echo $TERM`. + +Q: There are screen tearing issues when doing large prints +A: This is an issue with how terminals render frames, +as they may decide to do so in the middle of receiving a frame, +and cannot be fully fixed unless your console implements the [synchronized updates spec](https://gitlab.com/gnachman/iterm2/-/wikis/synchronized-updates-spec). +It can be reduced *drastically*, though, by using the rendering methods built in to the module, +and by only painting frames when your app's content has actually changed. + +Q: Why does the module only emit `keydown` events, and not `keyup` like `sokol`/`gg`? +A: It's because of the way terminals emit events. Every key event is received as a keypress, +and there isn't a way of telling terminals to send keyboard events differently, +nor a reliable way of converting these into `keydown` / `keyup` events. diff --git a/v_windows/v/old/vlib/term/ui/color.v b/v_windows/v/old/vlib/term/ui/color.v new file mode 100644 index 0000000..3e0a0bb --- /dev/null +++ b/v_windows/v/old/vlib/term/ui/color.v @@ -0,0 +1,88 @@ +// radare - LGPL - Copyright 2013-2020 - pancake, xarkes +// ansi 256 color extension for r_cons +// https://en.wikipedia.org/wiki/ANSI_color + +module ui + +const ( + value_range = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff]! + color_table = init_color_table() +) + +[direct_array_access] +fn init_color_table() []int { + mut color_table_ := []int{len: 256} + // ansi colors + color_table_[0] = 0x000000 + color_table_[1] = 0x800000 + color_table_[2] = 0x008000 + color_table_[3] = 0x808000 + color_table_[4] = 0x000080 + color_table_[5] = 0x800080 + color_table_[6] = 0x008080 + color_table_[7] = 0xc0c0c0 + color_table_[8] = 0x808080 + color_table_[9] = 0xff0000 + color_table_[10] = 0x00ff00 + color_table_[11] = 0xffff00 + color_table_[12] = 0x0000ff + color_table_[13] = 0xff00ff + color_table_[14] = 0x00ffff + color_table_[15] = 0xffffff + // color palette + for i in 0 .. 216 { + r := ui.value_range[(i / 36) % 6] + g := ui.value_range[(i / 6) % 6] + b := ui.value_range[i % 6] + color_table_[i + 16] = ((r << 16) & 0xffffff) + ((g << 8) & 0xffff) + (b & 0xff) + } + // grayscale + for i in 0 .. 24 { + r := 8 + (i * 10) + color_table_[i + 232] = ((r << 16) & 0xffffff) + ((r << 8) & 0xffff) + (r & 0xff) + } + return color_table_ +} + +fn clamp(x int, y int, z int) int { + if x < y { + return y + } + if x > z { + return z + } + return x +} + +fn approximate_rgb(r int, g int, b int) int { + grey := r > 0 && r < 255 && r == g && r == b + if grey { + return 232 + int(f64(r) / (255 / 24.1)) + } + k := int(256.0 / 6) + r2 := clamp(r / k, 0, 5) + g2 := clamp(g / k, 0, 5) + b2 := clamp(b / k, 0, 5) + return 16 + (r2 * 36) + (g2 * 6) + b2 +} + +fn lookup_rgb(r int, g int, b int) int { + color := (r << 16) + (g << 8) + b + // lookup extended colors only, coz non-extended can be changed by users. + for i in 16 .. 256 { + if ui.color_table[i] == color { + return i + } + } + return -1 +} + +// converts an RGB color to an ANSI 256-color, approximating it to the nearest available color +// if an exact match is not found +fn rgb2ansi(r int, g int, b int) int { + c := lookup_rgb(r, g, b) + if c == -1 { + return approximate_rgb(r, g, b) + } + return c +} diff --git a/v_windows/v/old/vlib/term/ui/consoleapi_windows.c.v b/v_windows/v/old/vlib/term/ui/consoleapi_windows.c.v new file mode 100644 index 0000000..a6002a6 --- /dev/null +++ b/v_windows/v/old/vlib/term/ui/consoleapi_windows.c.v @@ -0,0 +1,82 @@ +module ui + +union C.Event { + KeyEvent C.KEY_EVENT_RECORD + MouseEvent C.MOUSE_EVENT_RECORD + WindowBufferSizeEvent C.WINDOW_BUFFER_SIZE_RECORD + MenuEvent C.MENU_EVENT_RECORD + FocusEvent C.FOCUS_EVENT_RECORD +} + +[typedef] +struct C.INPUT_RECORD { + EventType u16 + Event C.Event +} + +union C.uChar { + UnicodeChar rune + AsciiChar byte +} + +[typedef] +struct C.KEY_EVENT_RECORD { + bKeyDown int + wRepeatCount u16 + wVirtualKeyCode u16 + wVirtualScanCode u16 + uChar C.uChar + dwControlKeyState u32 +} + +[typedef] +struct C.MOUSE_EVENT_RECORD { + dwMousePosition C.COORD + dwButtonState u32 + dwControlKeyState u32 + dwEventFlags u32 +} + +[typedef] +struct C.WINDOW_BUFFER_SIZE_RECORD { + dwSize C.COORD +} + +[typedef] +struct C.MENU_EVENT_RECORD { + dwCommandId u32 +} + +[typedef] +struct C.FOCUS_EVENT_RECORD { + bSetFocus int +} + +[typedef] +struct C.COORD { + X i16 + Y i16 +} + +[typedef] +struct C.SMALL_RECT { + Left u16 + Top u16 + Right u16 + Bottom u16 +} + +[typedef] +struct C.CONSOLE_SCREEN_BUFFER_INFO { + dwSize C.COORD + dwCursorPosition C.COORD + wAttributes u16 + srWindow C.SMALL_RECT + dwMaximumWindowSize C.COORD +} + +fn C.ReadConsoleInput(hConsoleInput C.HANDLE, lpBuffer &C.INPUT_RECORD, nLength u32, lpNumberOfEventsRead &u32) bool + +fn C.GetNumberOfConsoleInputEvents(hConsoleInput C.HANDLE, lpcNumberOfEvents &u32) bool + +fn C.GetConsoleScreenBufferInfo(handle C.HANDLE, info &C.CONSOLE_SCREEN_BUFFER_INFO) bool diff --git a/v_windows/v/old/vlib/term/ui/input.v b/v_windows/v/old/vlib/term/ui/input.v new file mode 100644 index 0000000..0532b39 --- /dev/null +++ b/v_windows/v/old/vlib/term/ui/input.v @@ -0,0 +1,241 @@ +// Copyright (c) 2020-2021 Raúl Hernández. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module ui + +import os + +pub enum KeyCode { + null = 0 + tab = 9 + enter = 10 + escape = 27 + space = 32 + backspace = 127 + exclamation = 33 + double_quote = 34 + hashtag = 35 + dollar = 36 + percent = 37 + ampersand = 38 + single_quote = 39 + left_paren = 40 + right_paren = 41 + asterisk = 42 + plus = 43 + comma = 44 + minus = 45 + period = 46 + slash = 47 + _0 = 48 + _1 = 49 + _2 = 50 + _3 = 51 + _4 = 52 + _5 = 53 + _6 = 54 + _7 = 55 + _8 = 56 + _9 = 57 + colon = 58 + semicolon = 59 + less_than = 60 + equal = 61 + greater_than = 62 + question_mark = 63 + at = 64 + a = 97 + b = 98 + c = 99 + d = 100 + e = 101 + f = 102 + g = 103 + h = 104 + i = 105 + j = 106 + k = 107 + l = 108 + m = 109 + n = 110 + o = 111 + p = 112 + q = 113 + r = 114 + s = 115 + t = 116 + u = 117 + v = 118 + w = 119 + x = 120 + y = 121 + z = 122 + left_square_bracket = 91 + backslash = 92 + right_square_bracket = 93 + caret = 94 + underscore = 95 + backtick = 96 + left_curly_bracket = 123 + vertical_bar = 124 + right_curly_bracket = 125 + tilde = 126 + insert = 260 + delete = 261 + up = 262 + down = 263 + right = 264 + left = 265 + page_up = 266 + page_down = 267 + home = 268 + end = 269 + f1 = 290 + f2 = 291 + f3 = 292 + f4 = 293 + f5 = 294 + f6 = 295 + f7 = 296 + f8 = 297 + f9 = 298 + f10 = 299 + f11 = 300 + f12 = 301 + f13 = 302 + f14 = 303 + f15 = 304 + f16 = 305 + f17 = 306 + f18 = 307 + f19 = 308 + f20 = 309 + f21 = 310 + f22 = 311 + f23 = 312 + f24 = 313 +} + +pub enum Direction { + unknown + up + down + left + right +} + +pub enum MouseButton { + unknown + left + middle + right +} + +pub enum EventType { + unknown + mouse_down + mouse_up + mouse_move + mouse_drag + mouse_scroll + key_down + resized +} + +[flag] +pub enum Modifiers { + ctrl + shift + alt +} + +pub struct Event { +pub: + typ EventType + // Mouse event info + x int + y int + button MouseButton + direction Direction + // Keyboard event info + code KeyCode + modifiers Modifiers + ascii byte + utf8 string + // Resized event info + width int + height int +} + +pub struct Context { + ExtraContext // contains fields specific to an implementation +pub: + cfg Config // adsasdas +mut: + print_buf []byte + paused bool + enable_su bool + enable_rgb bool +pub mut: + frame_count u64 + window_width int + window_height int +} + +pub struct Config { + user_data voidptr + init_fn fn (voidptr) + frame_fn fn (voidptr) + cleanup_fn fn (voidptr) + event_fn fn (&Event, voidptr) + fail_fn fn (string) + + buffer_size int = 256 + frame_rate int = 30 + use_x11 bool + + window_title string + hide_cursor bool + capture_events bool + use_alternate_buffer bool = true + skip_init_checks bool + // All kill signals to set up exit listeners on: + reset []os.Signal = [.hup, .int, .quit, .ill, .abrt, .bus, .fpe, .kill, .segv, .pipe, .alrm, .term, + .stop, +] +} + +[inline] +fn (ctx &Context) init() { + if ctx.cfg.init_fn != voidptr(0) { + ctx.cfg.init_fn(ctx.cfg.user_data) + } +} + +[inline] +fn (ctx &Context) frame() { + if ctx.cfg.frame_fn != voidptr(0) { + ctx.cfg.frame_fn(ctx.cfg.user_data) + } +} + +[inline] +fn (ctx &Context) cleanup() { + if ctx.cfg.cleanup_fn != voidptr(0) { + ctx.cfg.cleanup_fn(ctx.cfg.user_data) + } +} + +[inline] +fn (ctx &Context) fail(error string) { + if ctx.cfg.fail_fn != voidptr(0) { + ctx.cfg.fail_fn(error) + } +} + +[inline] +fn (ctx &Context) event(event &Event) { + if ctx.cfg.event_fn != voidptr(0) { + ctx.cfg.event_fn(event, ctx.cfg.user_data) + } +} diff --git a/v_windows/v/old/vlib/term/ui/input_nix.c.v b/v_windows/v/old/vlib/term/ui/input_nix.c.v new file mode 100644 index 0000000..e806fb8 --- /dev/null +++ b/v_windows/v/old/vlib/term/ui/input_nix.c.v @@ -0,0 +1,70 @@ +// Copyright (c) 2020-2021 Raúl Hernández. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module ui + +struct ExtraContext { +mut: + read_buf []byte +} + +const ( + ctx_ptr = &Context(0) +) + +pub fn init(cfg Config) &Context { + mut ctx := &Context{ + cfg: cfg + } + ctx.read_buf = []byte{cap: cfg.buffer_size} + + // lmao + unsafe { + x := &ui.ctx_ptr + *x = ctx + _ = x + } + return ctx +} + +[inline] +fn save_title() { + // restore the previously saved terminal title + print('\x1b[22;0t') +} + +[inline] +fn load_title() { + // restore the previously saved terminal title + print('\x1b[23;0t') +} + +pub fn (mut ctx Context) run() ? { + if ctx.cfg.use_x11 { + ctx.fail('error: x11 backend not implemented yet') + exit(1) + } else { + ctx.termios_setup() ? + ctx.termios_loop() + } +} + +// shifts the array left, to remove any data that was just read, and updates its len +// TODO: remove +[inline] +fn (mut ctx Context) shift(len int) { + unsafe { + C.memmove(ctx.read_buf.data, &byte(ctx.read_buf.data) + len, ctx.read_buf.cap - len) + ctx.resize_arr(ctx.read_buf.len - len) + } +} + +// TODO: don't actually do this, lmao +[inline] +fn (mut ctx Context) resize_arr(size int) { + mut l := unsafe { &ctx.read_buf.len } + unsafe { + *l = size + _ = l + } +} diff --git a/v_windows/v/old/vlib/term/ui/input_windows.c.v b/v_windows/v/old/vlib/term/ui/input_windows.c.v new file mode 100644 index 0000000..bd9782d --- /dev/null +++ b/v_windows/v/old/vlib/term/ui/input_windows.c.v @@ -0,0 +1,326 @@ +// Copyright (c) 2020-2021 Raúl Hernández. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module ui + +import os +import time + +const ( + buf_size = 64 + ctx_ptr = &Context(0) + stdin_at_startup = u32(0) +) + +struct ExtraContext { +mut: + stdin_handle C.HANDLE + stdout_handle C.HANDLE + read_buf [buf_size]C.INPUT_RECORD + mouse_down MouseButton +} + +fn restore_terminal_state() { + if ui.ctx_ptr != 0 { + if ui.ctx_ptr.cfg.use_alternate_buffer { + // clear the terminal and set the cursor to the origin + print('\x1b[2J\x1b[3J') + print('\x1b[?1049l') + } + C.SetConsoleMode(ui.ctx_ptr.stdin_handle, ui.stdin_at_startup) + } + load_title() + os.flush() +} + +pub fn init(cfg Config) &Context { + mut ctx := &Context{ + cfg: cfg + } + // get the standard input handle + stdin_handle := C.GetStdHandle(C.STD_INPUT_HANDLE) + stdout_handle := C.GetStdHandle(C.STD_OUTPUT_HANDLE) + if stdin_handle == C.INVALID_HANDLE_VALUE { + panic('could not get stdin handle') + } + // save the current input mode, to be restored on exit + if C.GetConsoleMode(stdin_handle, &ui.stdin_at_startup) == 0 { + panic('could not get stdin console mode') + } + + // enable extended input flags (see https://stackoverflow.com/a/46802726) + // 0x80 == C.ENABLE_EXTENDED_FLAGS + if C.SetConsoleMode(stdin_handle, 0x80) == 0 { + panic('could not set raw input mode') + } + // enable window and mouse input events. + if C.SetConsoleMode(stdin_handle, C.ENABLE_WINDOW_INPUT | C.ENABLE_MOUSE_INPUT) == 0 { + panic('could not set raw input mode') + } + // store the current title, so restore_terminal_state can get it back + save_title() + + if ctx.cfg.use_alternate_buffer { + // switch to the alternate buffer + print('\x1b[?1049h') + // clear the terminal and set the cursor to the origin + print('\x1b[2J\x1b[3J\x1b[1;1H') + } + + if ctx.cfg.hide_cursor { + ctx.hide_cursor() + ctx.flush() + } + + if ctx.cfg.window_title != '' { + print('\x1b]0;$ctx.cfg.window_title\x07') + } + + unsafe { + x := &ui.ctx_ptr + *x = ctx + } + C.atexit(restore_terminal_state) + for code in ctx.cfg.reset { + os.signal_opt(code, fn (_ os.Signal) { + mut c := ui.ctx_ptr + if c != 0 { + c.cleanup() + } + exit(0) + }) or {} + } + + ctx.stdin_handle = stdin_handle + ctx.stdout_handle = stdout_handle + return ctx +} + +pub fn (mut ctx Context) run() ? { + frame_time := 1_000_000 / ctx.cfg.frame_rate + mut init_called := false + mut sw := time.new_stopwatch(auto_start: false) + mut sleep_len := 0 + for { + if !init_called { + ctx.init() + init_called = true + } + if sleep_len > 0 { + time.sleep(sleep_len * time.microsecond) + } + if !ctx.paused { + sw.restart() + if ctx.cfg.event_fn != voidptr(0) { + ctx.parse_events() + } + ctx.frame() + sw.pause() + e := sw.elapsed().microseconds() + sleep_len = frame_time - int(e) + ctx.frame_count++ + } + } +} + +fn (mut ctx Context) parse_events() { + nr_events := u32(0) + if !C.GetNumberOfConsoleInputEvents(ctx.stdin_handle, &nr_events) { + panic('could not get number of events in stdin') + } + if nr_events < 1 { + return + } + + // print('$nr_events | ') + if !C.ReadConsoleInput(ctx.stdin_handle, &ctx.read_buf[0], ui.buf_size, &nr_events) { + panic('could not read from stdin') + } + for i in 0 .. nr_events { + // print('E ') + match int(ctx.read_buf[i].EventType) { + C.KEY_EVENT { + e := unsafe { ctx.read_buf[i].Event.KeyEvent } + ch := e.wVirtualKeyCode + ascii := unsafe { e.uChar.AsciiChar } + if e.bKeyDown == 0 { + continue + } + // we don't handle key_up events because they don't exist on linux... + // see: https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes + code := match int(ch) { + C.VK_BACK { KeyCode.backspace } + C.VK_RETURN { KeyCode.enter } + C.VK_PRIOR { KeyCode.page_up } + 14...20 { KeyCode.null } + C.VK_NEXT { KeyCode.page_down } + C.VK_END { KeyCode.end } + C.VK_HOME { KeyCode.home } + C.VK_LEFT { KeyCode.left } + C.VK_UP { KeyCode.up } + C.VK_RIGHT { KeyCode.right } + C.VK_DOWN { KeyCode.down } + C.VK_INSERT { KeyCode.insert } + C.VK_DELETE { KeyCode.delete } + 65...90 { KeyCode(ch + 32) } // letters + 91...93 { KeyCode.null } // special keys + 96...105 { KeyCode(ch - 48) } // numpad numbers + 112...135 { KeyCode(ch + 178) } // f1 - f24 + else { KeyCode(ascii) } + } + + mut modifiers := Modifiers{} + if e.dwControlKeyState & (0x1 | 0x2) != 0 { + modifiers.set(.alt) + } + if e.dwControlKeyState & (0x4 | 0x8) != 0 { + modifiers.set(.ctrl) + } + if e.dwControlKeyState & 0x10 != 0 { + modifiers.set(.shift) + } + + mut event := &Event{ + typ: .key_down + modifiers: modifiers + code: code + ascii: ascii + width: int(e.dwControlKeyState) + height: int(e.wVirtualKeyCode) + utf8: unsafe { e.uChar.UnicodeChar.str() } + } + ctx.event(event) + } + C.MOUSE_EVENT { + e := unsafe { ctx.read_buf[i].Event.MouseEvent } + sb_info := C.CONSOLE_SCREEN_BUFFER_INFO{} + if !C.GetConsoleScreenBufferInfo(ctx.stdout_handle, &sb_info) { + panic('could not get screenbuffer info') + } + x := e.dwMousePosition.X + 1 + y := int(e.dwMousePosition.Y) - sb_info.srWindow.Top + 1 + mut modifiers := Modifiers{} + if e.dwControlKeyState & (0x1 | 0x2) != 0 { + modifiers.set(.alt) + } + if e.dwControlKeyState & (0x4 | 0x8) != 0 { + modifiers.set(.ctrl) + } + if e.dwControlKeyState & 0x10 != 0 { + modifiers.set(.shift) + } + // TODO: handle capslock/numlock/etc?? events exist for those keys + match int(e.dwEventFlags) { + C.MOUSE_MOVED { + mut button := match int(e.dwButtonState) { + 0 { MouseButton.unknown } + 1 { MouseButton.left } + 2 { MouseButton.right } + else { MouseButton.middle } + } + typ := if e.dwButtonState == 0 { + if ctx.mouse_down != .unknown { + button = ctx.mouse_down + ctx.mouse_down = .unknown + EventType.mouse_up + } else { + EventType.mouse_move + } + } else { + EventType.mouse_drag + } + ctx.event(&Event{ + typ: typ + x: x + y: y + button: button + modifiers: modifiers + }) + } + C.MOUSE_WHEELED { + ctx.event(&Event{ + typ: .mouse_scroll + direction: if i16(e.dwButtonState >> 16) < 0 { + Direction.up + } else { + Direction.down + } + x: x + y: y + modifiers: modifiers + }) + } + 0x0008 /* C.MOUSE_HWHEELED */ { + ctx.event(&Event{ + typ: .mouse_scroll + direction: if i16(e.dwButtonState >> 16) < 0 { + Direction.right + } else { + Direction.left + } + x: x + y: y + modifiers: modifiers + }) + } + 0 /* CLICK */, C.DOUBLE_CLICK { + button := match int(e.dwButtonState) { + 0 { ctx.mouse_down } + 1 { MouseButton.left } + 2 { MouseButton.right } + else { MouseButton.middle } + } + ctx.mouse_down = button + ctx.event(&Event{ + typ: .mouse_down + x: x + y: y + button: button + modifiers: modifiers + }) + } + else {} + } + } + C.WINDOW_BUFFER_SIZE_EVENT { + // e := unsafe { ctx.read_buf[i].Event.WindowBufferSizeEvent } + sb := C.CONSOLE_SCREEN_BUFFER_INFO{} + if !C.GetConsoleScreenBufferInfo(ctx.stdout_handle, &sb) { + panic('could not get screenbuffer info') + } + w := sb.srWindow.Right - sb.srWindow.Left + 1 + h := sb.srWindow.Bottom - sb.srWindow.Top + 1 + utf8 := '($ctx.window_width, $ctx.window_height) -> ($w, $h)' + if w != ctx.window_width || h != ctx.window_height { + ctx.window_width, ctx.window_height = w, h + mut event := &Event{ + typ: .resized + width: ctx.window_width + height: ctx.window_height + utf8: utf8 + } + ctx.event(event) + } + } + // C.MENU_EVENT { + // e := unsafe { ctx.read_buf[i].Event.MenuEvent } + // } + // C.FOCUS_EVENT { + // e := unsafe { ctx.read_buf[i].Event.FocusEvent } + // } + else {} + } + } +} + +[inline] +fn save_title() { + // restore the previously saved terminal title + print('\x1b[22;0t') +} + +[inline] +fn load_title() { + // restore the previously saved terminal title + print('\x1b[23;0t') +} diff --git a/v_windows/v/old/vlib/term/ui/termios_nix.c.v b/v_windows/v/old/vlib/term/ui/termios_nix.c.v new file mode 100644 index 0000000..fb5ff76 --- /dev/null +++ b/v_windows/v/old/vlib/term/ui/termios_nix.c.v @@ -0,0 +1,530 @@ +// Copyright (c) 2020-2021 Raúl Hernández. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module ui + +import os +import time + +#include +#include +#include + +fn C.tcgetattr(fd int, termios_p &C.termios) int + +fn C.tcsetattr(fd int, optional_actions int, termios_p &C.termios) int + +fn C.ioctl(fd int, request u64, arg voidptr) int + +struct C.termios { +mut: + c_iflag u32 + c_lflag u32 + c_cc [32]byte +} + +struct C.winsize { + ws_row u16 + ws_col u16 +} + +const ( + termios_at_startup = get_termios() +) + +[inline] +fn get_termios() C.termios { + mut t := C.termios{} + C.tcgetattr(C.STDIN_FILENO, &t) + return t +} + +[inline] +fn get_terminal_size() (u16, u16) { + winsz := C.winsize{} + C.ioctl(0, C.TIOCGWINSZ, &winsz) + return winsz.ws_row, winsz.ws_col +} + +fn restore_terminal_state_signal(_ os.Signal) { + restore_terminal_state() +} + +fn restore_terminal_state() { + termios_reset() + mut c := ctx_ptr + if c != 0 { + c.paused = true + load_title() + } + os.flush() +} + +fn (mut ctx Context) termios_setup() ? { + // store the current title, so restore_terminal_state can get it back + save_title() + + if !ctx.cfg.skip_init_checks && !(os.is_atty(C.STDIN_FILENO) != 0 + && os.is_atty(C.STDOUT_FILENO) != 0) { + return error('not running under a TTY') + } + + mut termios := get_termios() + + if ctx.cfg.capture_events { + // Set raw input mode by unsetting ICANON and ECHO, + // as well as disable e.g. ctrl+c and ctrl.z + termios.c_iflag &= ~u32(C.IGNBRK | C.BRKINT | C.PARMRK | C.IXON) + termios.c_lflag &= ~u32(C.ICANON | C.ISIG | C.ECHO | C.IEXTEN | C.TOSTOP) + } else { + // Set raw input mode by unsetting ICANON and ECHO + termios.c_lflag &= ~u32(C.ICANON | C.ECHO) + } + + if ctx.cfg.hide_cursor { + ctx.hide_cursor() + ctx.flush() + } + + if ctx.cfg.window_title != '' { + print('\x1b]0;$ctx.cfg.window_title\x07') + } + + if !ctx.cfg.skip_init_checks { + // prevent blocking during the feature detections, but allow enough time for the terminal + // to send back the relevant input data + termios.c_cc[C.VTIME] = 1 + termios.c_cc[C.VMIN] = 0 + C.tcsetattr(C.STDIN_FILENO, C.TCSAFLUSH, &termios) + // feature-test the SU spec + sx, sy := get_cursor_position() + print('$bsu$esu') + ex, ey := get_cursor_position() + if sx == ex && sy == ey { + // the terminal either ignored or handled the sequence properly, enable SU + ctx.enable_su = true + } else { + ctx.draw_line(sx, sy, ex, ey) + ctx.set_cursor_position(sx, sy) + ctx.flush() + } + // feature-test rgb (truecolor) support + ctx.enable_rgb = supports_truecolor() + } + // Prevent stdin from blocking by making its read time 0 + termios.c_cc[C.VTIME] = 0 + termios.c_cc[C.VMIN] = 0 + C.tcsetattr(C.STDIN_FILENO, C.TCSAFLUSH, &termios) + // enable mouse input + print('\x1b[?1003h\x1b[?1006h') + if ctx.cfg.use_alternate_buffer { + // switch to the alternate buffer + print('\x1b[?1049h') + // clear the terminal and set the cursor to the origin + print('\x1b[2J\x1b[3J\x1b[1;1H') + } + ctx.window_height, ctx.window_width = get_terminal_size() + + // Reset console on exit + C.atexit(restore_terminal_state) + os.signal_opt(.tstp, restore_terminal_state_signal) or {} + os.signal_opt(.cont, fn (_ os.Signal) { + mut c := ctx_ptr + if c != 0 { + c.termios_setup() or { panic(err) } + c.window_height, c.window_width = get_terminal_size() + mut event := &Event{ + typ: .resized + width: c.window_width + height: c.window_height + } + c.paused = false + c.event(event) + } + }) or {} + for code in ctx.cfg.reset { + os.signal_opt(code, fn (_ os.Signal) { + mut c := ctx_ptr + if c != 0 { + c.cleanup() + } + exit(0) + }) or {} + } + + os.signal_opt(.winch, fn (_ os.Signal) { + mut c := ctx_ptr + if c != 0 { + c.window_height, c.window_width = get_terminal_size() + + mut event := &Event{ + typ: .resized + width: c.window_width + height: c.window_height + } + c.event(event) + } + }) or {} + + os.flush() +} + +fn get_cursor_position() (int, int) { + print('\033[6n') + mut s := '' + unsafe { + buf := malloc_noscan(25) + len := C.read(C.STDIN_FILENO, buf, 24) + buf[len] = 0 + s = tos(buf, len) + } + a := s[2..].split(';') + if a.len != 2 { + return -1, -1 + } + return a[0].int(), a[1].int() +} + +fn supports_truecolor() bool { + // faster/simpler, but less reliable, check + if os.getenv('COLORTERM') in ['truecolor', '24bit'] { + return true + } + // set the bg color to some arbirtrary value (#010203), assumed not to be the default + print('\x1b[48:2:1:2:3m') + // andquery the current color + print('\x1bP\$qm\x1b\\') + mut s := '' + unsafe { + buf := malloc_noscan(25) + len := C.read(C.STDIN_FILENO, buf, 24) + buf[len] = 0 + s = tos(buf, len) + } + return s.contains('1:2:3') +} + +fn termios_reset() { + // C.TCSANOW ?? + C.tcsetattr(C.STDIN_FILENO, C.TCSAFLUSH, &ui.termios_at_startup) + print('\x1b[?1003l\x1b[?1006l\x1b[?25h') + c := ctx_ptr + if c != 0 && c.cfg.use_alternate_buffer { + print('\x1b[?1049l') + } + os.flush() +} + +/////////////////////////////////////////// +// TODO: do multiple sleep/read cycles, rather than one big one +fn (mut ctx Context) termios_loop() { + frame_time := 1_000_000 / ctx.cfg.frame_rate + mut init_called := false + mut sw := time.new_stopwatch(auto_start: false) + mut sleep_len := 0 + for { + if !init_called { + ctx.init() + init_called = true + } + // println('SLEEPING: $sleep_len') + if sleep_len > 0 { + time.sleep(sleep_len * time.microsecond) + } + if !ctx.paused { + sw.restart() + if ctx.cfg.event_fn != voidptr(0) { + unsafe { + len := C.read(C.STDIN_FILENO, &byte(ctx.read_buf.data) + ctx.read_buf.len, + ctx.read_buf.cap - ctx.read_buf.len) + ctx.resize_arr(ctx.read_buf.len + len) + } + if ctx.read_buf.len > 0 { + ctx.parse_events() + } + } + ctx.frame() + sw.pause() + e := sw.elapsed().microseconds() + sleep_len = frame_time - int(e) + + ctx.frame_count++ + } + } +} + +fn (mut ctx Context) parse_events() { + // Stop this from getting stuck in rare cases where something isn't parsed correctly + mut nr_iters := 0 + for ctx.read_buf.len > 0 { + nr_iters++ + if nr_iters > 100 { + ctx.shift(1) + } + mut event := &Event(0) + if ctx.read_buf[0] == 0x1b { + e, len := escape_sequence(ctx.read_buf.bytestr()) + event = e + ctx.shift(len) + } else { + event = single_char(ctx.read_buf.bytestr()) + ctx.shift(1) + } + if event != 0 { + ctx.event(event) + nr_iters = 0 + } + } +} + +fn single_char(buf string) &Event { + ch := buf[0] + + mut event := &Event{ + typ: .key_down + ascii: ch + code: KeyCode(ch) + utf8: buf + } + + match ch { + // special handling for `ctrl + letter` + // TODO: Fix assoc in V and remove this workaround :/ + // 1 ... 26 { event = Event{ ...event, code: KeyCode(96 | ch), modifiers: .ctrl } } + // 65 ... 90 { event = Event{ ...event, code: KeyCode(32 | ch), modifiers: .shift } } + // The bit `or`s here are really just `+`'s, just written in this way for a tiny performance improvement + // don't treat tab, enter as ctrl+i, ctrl+j + 1...8, 11...26 { + event = &Event{ + typ: event.typ + ascii: event.ascii + utf8: event.utf8 + code: KeyCode(96 | ch) + modifiers: .ctrl + } + } + 65...90 { + event = &Event{ + typ: event.typ + ascii: event.ascii + utf8: event.utf8 + code: KeyCode(32 | ch) + modifiers: .shift + } + } + else {} + } + + return event +} + +// Gets an entire, independent escape sequence from the buffer +// Normally, this just means reading until the first letter, but there are some exceptions... +fn escape_end(buf string) int { + mut i := 0 + for { + if i + 1 == buf.len { + return buf.len + } + + if buf[i].is_letter() || buf[i] == `~` { + if buf[i] == `O` && i + 2 <= buf.len { + n := buf[i + 1] + if (n >= `A` && n <= `D`) || (n >= `P` && n <= `S`) || n == `F` || n == `H` { + return i + 2 + } + } + return i + 1 + // escape hatch to avoid potential issues/crashes, although ideally this should never eval to true + } else if buf[i + 1] == 0x1b { + return i + 1 + } + i++ + } + // this point should be unreachable + assert false + return 0 +} + +fn escape_sequence(buf_ string) (&Event, int) { + end := escape_end(buf_) + single := buf_[..end] // read until the end of the sequence + buf := single[1..] // skip the escape character + + if buf.len == 0 { + return &Event{ + typ: .key_down + ascii: 27 + code: .escape + utf8: single + }, 1 + } + + if buf.len == 1 { + c := single_char(buf) + mut modifiers := c.modifiers + modifiers.set(.alt) + return &Event{ + typ: c.typ + ascii: c.ascii + code: c.code + utf8: single + modifiers: modifiers + }, 2 + } + // ---------------- + // Mouse events + // ---------------- + // Documentation: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking + if buf.len > 2 && buf[1] == `<` { + split := buf[2..].split(';') + if split.len < 3 { + return &Event(0), 0 + } + + typ, x, y := split[0].int(), split[1].int(), split[2].int() + lo := typ & 0b00011 + hi := typ & 0b11100 + + mut modifiers := Modifiers{} + if hi & 4 != 0 { + modifiers.set(.shift) + } + if hi & 8 != 0 { + modifiers.set(.alt) + } + if hi & 16 != 0 { + modifiers.set(.ctrl) + } + + match typ { + 0...31 { + last := buf[buf.len - 1] + button := if lo < 3 { MouseButton(lo + 1) } else { MouseButton.unknown } + event := if last == `m` || lo == 3 { + EventType.mouse_up + } else { + EventType.mouse_down + } + + return &Event{ + typ: event + x: x + y: y + button: button + modifiers: modifiers + utf8: single + }, end + } + 32...63 { + button, event := if lo < 3 { + MouseButton(lo + 1), EventType.mouse_drag + } else { + MouseButton.unknown, EventType.mouse_move + } + + return &Event{ + typ: event + x: x + y: y + button: button + modifiers: modifiers + utf8: single + }, end + } + 64...95 { + direction := if typ & 1 == 0 { Direction.down } else { Direction.up } + return &Event{ + typ: .mouse_scroll + x: x + y: y + direction: direction + modifiers: modifiers + utf8: single + }, end + } + else { + return &Event{ + typ: .unknown + utf8: single + }, end + } + } + } + // ---------------------------- + // Special key combinations + // ---------------------------- + + mut code := KeyCode.null + mut modifiers := Modifiers{} + match buf { + '[A', 'OA' { code = .up } + '[B', 'OB' { code = .down } + '[C', 'OC' { code = .right } + '[D', 'OD' { code = .left } + '[5~', '[[5~' { code = .page_up } + '[6~', '[[6~' { code = .page_down } + '[F', 'OF', '[4~', '[[8~' { code = .end } + '[H', 'OH', '[1~', '[[7~' { code = .home } + '[2~' { code = .insert } + '[3~' { code = .delete } + 'OP', '[11~' { code = .f1 } + 'OQ', '[12~' { code = .f2 } + 'OR', '[13~' { code = .f3 } + 'OS', '[14~' { code = .f4 } + '[15~' { code = .f5 } + '[17~' { code = .f6 } + '[18~' { code = .f7 } + '[19~' { code = .f8 } + '[20~' { code = .f9 } + '[21~' { code = .f10 } + '[23~' { code = .f11 } + '[24~' { code = .f12 } + else {} + } + + if buf == '[Z' { + code = .tab + modifiers.set(.shift) + } + + if buf.len == 5 && buf[0] == `[` && buf[1].is_digit() && buf[2] == `;` { + match buf[3] { + `2` { modifiers = .shift } + `3` { modifiers = .alt } + `4` { modifiers = .shift | .alt } + `5` { modifiers = .ctrl } + `6` { modifiers = .ctrl | .shift } + `7` { modifiers = .ctrl | .alt } + `8` { modifiers = .ctrl | .alt | .shift } + else {} + } + + if buf[1] == `1` { + match buf[4] { + `A` { code = KeyCode.up } + `B` { code = KeyCode.down } + `C` { code = KeyCode.right } + `D` { code = KeyCode.left } + `F` { code = KeyCode.end } + `H` { code = KeyCode.home } + `P` { code = KeyCode.f1 } + `Q` { code = KeyCode.f2 } + `R` { code = KeyCode.f3 } + `S` { code = KeyCode.f4 } + else {} + } + } else if buf[1] == `5` { + code = KeyCode.page_up + } else if buf[1] == `6` { + code = KeyCode.page_down + } + } + + return &Event{ + typ: .key_down + code: code + utf8: single + modifiers: modifiers + }, end +} diff --git a/v_windows/v/old/vlib/term/ui/ui.v b/v_windows/v/old/vlib/term/ui/ui.v new file mode 100644 index 0000000..6ba3d7c --- /dev/null +++ b/v_windows/v/old/vlib/term/ui/ui.v @@ -0,0 +1,256 @@ +// Copyright (c) 2020-2021 Raúl Hernández. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module ui + +import strings + +pub struct Color { +pub: + r byte + g byte + b byte +} + +pub fn (c Color) hex() string { + return '#$c.r.hex()$c.g.hex()$c.b.hex()' +} + +// Synchronized Updates spec, designed to avoid tearing during renders +// https://gitlab.com/gnachman/iterm2/-/wikis/synchronized-updates-spec +const ( + bsu = '\x1bP=1s\x1b\\' + esu = '\x1bP=2s\x1b\\' +) + +// write puts the string `s` into the print buffer. +[inline] +pub fn (mut ctx Context) write(s string) { + if s == '' { + return + } + unsafe { ctx.print_buf.push_many(s.str, s.len) } +} + +// flush displays the accumulated print buffer to the screen. +[inline] +pub fn (mut ctx Context) flush() { + // TODO: Diff the previous frame against this one, and only render things that changed? + if !ctx.enable_su { + C.write(1, ctx.print_buf.data, ctx.print_buf.len) + } else { + C.write(1, ui.bsu.str, ui.bsu.len) + C.write(1, ctx.print_buf.data, ctx.print_buf.len) + C.write(1, ui.esu.str, ui.esu.len) + } + ctx.print_buf.clear() +} + +// bold sets the character state to bold. +[inline] +pub fn (mut ctx Context) bold() { + ctx.write('\x1b[1m') +} + +// set_cursor_position positions the cusor at the given coordinates `x`,`y`. +[inline] +pub fn (mut ctx Context) set_cursor_position(x int, y int) { + ctx.write('\x1b[$y;${x}H') +} + +// show_cursor will make the cursor appear if it is not already visible +[inline] +pub fn (mut ctx Context) show_cursor() { + ctx.write('\x1b[?25h') +} + +// hide_cursor will make the cursor invisible +[inline] +pub fn (mut ctx Context) hide_cursor() { + ctx.write('\x1b[?25l') +} + +// set_color sets the current foreground color used by any succeeding `draw_*` calls. +[inline] +pub fn (mut ctx Context) set_color(c Color) { + if ctx.enable_rgb { + ctx.write('\x1b[38;2;${int(c.r)};${int(c.g)};${int(c.b)}m') + } else { + ctx.write('\x1b[38;5;${rgb2ansi(c.r, c.g, c.b)}m') + } +} + +// set_color sets the current background color used by any succeeding `draw_*` calls. +[inline] +pub fn (mut ctx Context) set_bg_color(c Color) { + if ctx.enable_rgb { + ctx.write('\x1b[48;2;${int(c.r)};${int(c.g)};${int(c.b)}m') + } else { + ctx.write('\x1b[48;5;${rgb2ansi(c.r, c.g, c.b)}m') + } +} + +// reset_color sets the current foreground color back to it's default value. +[inline] +pub fn (mut ctx Context) reset_color() { + ctx.write('\x1b[39m') +} + +// reset_bg_color sets the current background color back to it's default value. +[inline] +pub fn (mut ctx Context) reset_bg_color() { + ctx.write('\x1b[49m') +} + +// reset restores the state of all colors and text formats back to their default values. +[inline] +pub fn (mut ctx Context) reset() { + ctx.write('\x1b[0m') +} + +[inline] +pub fn (mut ctx Context) clear() { + ctx.write('\x1b[2J\x1b[3J') +} + +// set_window_title sets the string `s` as the window title. +[inline] +pub fn (mut ctx Context) set_window_title(s string) { + print('\x1b]0;$s\x07') +} + +// draw_point draws a point at position `x`,`y`. +[inline] +pub fn (mut ctx Context) draw_point(x int, y int) { + ctx.set_cursor_position(x, y) + ctx.write(' ') +} + +// draw_text draws the string `s`, starting from position `x`,`y`. +[inline] +pub fn (mut ctx Context) draw_text(x int, y int, s string) { + ctx.set_cursor_position(x, y) + ctx.write(s) +} + +// draw_line draws a line segment, starting at point `x`,`y`, and ending at point `x2`,`y2`. +pub fn (mut ctx Context) draw_line(x int, y int, x2 int, y2 int) { + min_x, min_y := if x < x2 { x } else { x2 }, if y < y2 { y } else { y2 } + max_x, _ := if x > x2 { x } else { x2 }, if y > y2 { y } else { y2 } + if y == y2 { + // Horizontal line, performance improvement + ctx.set_cursor_position(min_x, min_y) + ctx.write(strings.repeat(` `, max_x + 1 - min_x)) + return + } + // Draw the various points with Bresenham's line algorithm: + mut x0, x1 := x, x2 + mut y0, y1 := y, y2 + sx := if x0 < x1 { 1 } else { -1 } + sy := if y0 < y1 { 1 } else { -1 } + dx := if x0 < x1 { x1 - x0 } else { x0 - x1 } + dy := if y0 < y1 { y0 - y1 } else { y1 - y0 } // reversed + mut err := dx + dy + for { + // res << Segment{ x0, y0 } + ctx.draw_point(x0, y0) + if x0 == x1 && y0 == y1 { + break + } + e2 := 2 * err + if e2 >= dy { + err += dy + x0 += sx + } + if e2 <= dx { + err += dx + y0 += sy + } + } +} + +// draw_dashed_line draws a dashed line segment, starting at point `x`,`y`, and ending at point `x2`,`y2`. +pub fn (mut ctx Context) draw_dashed_line(x int, y int, x2 int, y2 int) { + // Draw the various points with Bresenham's line algorithm: + mut x0, x1 := x, x2 + mut y0, y1 := y, y2 + sx := if x0 < x1 { 1 } else { -1 } + sy := if y0 < y1 { 1 } else { -1 } + dx := if x0 < x1 { x1 - x0 } else { x0 - x1 } + dy := if y0 < y1 { y0 - y1 } else { y1 - y0 } // reversed + mut err := dx + dy + mut i := 0 + for { + if i % 2 == 0 { + ctx.draw_point(x0, y0) + } + if x0 == x1 && y0 == y1 { + break + } + e2 := 2 * err + if e2 >= dy { + err += dy + x0 += sx + } + if e2 <= dx { + err += dx + y0 += sy + } + i++ + } +} + +// draw_rect draws a rectangle, starting at top left `x`,`y`, and ending at bottom right `x2`,`y2`. +pub fn (mut ctx Context) draw_rect(x int, y int, x2 int, y2 int) { + if y == y2 || x == x2 { + ctx.draw_line(x, y, x2, y2) + return + } + min_y, max_y := if y < y2 { y, y2 } else { y2, y } + for y_pos in min_y .. max_y + 1 { + ctx.draw_line(x, y_pos, x2, y_pos) + } +} + +// draw_empty_dashed_rect draws a rectangle with dashed lines, starting at top left `x`,`y`, and ending at bottom right `x2`,`y2`. +pub fn (mut ctx Context) draw_empty_dashed_rect(x int, y int, x2 int, y2 int) { + if y == y2 || x == x2 { + ctx.draw_dashed_line(x, y, x2, y2) + return + } + + min_x, max_x := if x < x2 { x, x2 } else { x2, x } + min_y, max_y := if y < y2 { y, y2 } else { y2, y } + + ctx.draw_dashed_line(min_x, min_y, max_x, min_y) + ctx.draw_dashed_line(min_x, min_y, min_x, max_y) + if (max_y - min_y) & 1 == 0 { + ctx.draw_dashed_line(min_x, max_y, max_x, max_y) + } else { + ctx.draw_dashed_line(min_x + 1, max_y, max_x, max_y) + } + if (max_x - min_x) & 1 == 0 { + ctx.draw_dashed_line(max_x, min_y, max_x, max_y) + } else { + ctx.draw_dashed_line(max_x, min_y + 1, max_x, max_y) + } +} + +// draw_empty_rect draws a rectangle with no fill, starting at top left `x`,`y`, and ending at bottom right `x2`,`y2`. +pub fn (mut ctx Context) draw_empty_rect(x int, y int, x2 int, y2 int) { + if y == y2 || x == x2 { + ctx.draw_line(x, y, x2, y2) + return + } + ctx.draw_line(x, y, x2, y) + ctx.draw_line(x, y2, x2, y2) + ctx.draw_line(x, y, x, y2) + ctx.draw_line(x2, y, x2, y2) +} + +// horizontal_separator draws a horizontal separator, spanning the width of the screen. +[inline] +pub fn (mut ctx Context) horizontal_separator(y int) { + ctx.set_cursor_position(0, y) + ctx.write(strings.repeat(`-`, ctx.window_width)) // /* `⎽` */ +} diff --git a/v_windows/v/old/vlib/time/Y2K38_test.v b/v_windows/v/old/vlib/time/Y2K38_test.v new file mode 100644 index 0000000..0ebc0ef --- /dev/null +++ b/v_windows/v/old/vlib/time/Y2K38_test.v @@ -0,0 +1,13 @@ +import time + +fn test_time_after_2038_works() { + after_time := time.parse_iso8601('2037-07-23') or { time.now() } + dump(after_time) + error_time := after_time.add_days(180) + dump(error_time) + assert error_time.str() == '2038-01-19 00:00:00' + // NB: the next date is after Y2K38, it should NOT wrap: + error_time2 := after_time.add_days(181) + dump(error_time2) + assert error_time2.str() == '2038-01-20 00:00:00' +} diff --git a/v_windows/v/old/vlib/time/format.v b/v_windows/v/old/vlib/time/format.v new file mode 100644 index 0000000..6304cbc --- /dev/null +++ b/v_windows/v/old/vlib/time/format.v @@ -0,0 +1,172 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module time + +// format returns a date string in "YYYY-MM-DD HH:MM" format (24h). +pub fn (t Time) format() string { + return t.get_fmt_str(.hyphen, .hhmm24, .yyyymmdd) +} + +// format_ss returns a date string in "YYYY-MM-DD HH:MM:SS" format (24h). +pub fn (t Time) format_ss() string { + return t.get_fmt_str(.hyphen, .hhmmss24, .yyyymmdd) +} + +// format_ss_milli returns a date string in "YYYY-MM-DD HH:MM:SS.123" format (24h). +pub fn (t Time) format_ss_milli() string { + return t.get_fmt_str(.hyphen, .hhmmss24_milli, .yyyymmdd) +} + +// format_ss_micro returns a date string in "YYYY-MM-DD HH:MM:SS.123456" format (24h). +pub fn (t Time) format_ss_micro() string { + return t.get_fmt_str(.hyphen, .hhmmss24_micro, .yyyymmdd) +} + +// hhmm returns a date string in "HH:MM" format (24h). +pub fn (t Time) hhmm() string { + return t.get_fmt_time_str(.hhmm24) +} + +// hhmmss returns a date string in "HH:MM:SS" format (24h). +pub fn (t Time) hhmmss() string { + return t.get_fmt_time_str(.hhmmss24) +} + +// hhmm12 returns a date string in "HH:MM" format (12h). +pub fn (t Time) hhmm12() string { + return t.get_fmt_time_str(.hhmm12) +} + +// ymmdd returns a date string in "YYYY-MM-DD" format. +pub fn (t Time) ymmdd() string { + return t.get_fmt_date_str(.hyphen, .yyyymmdd) +} + +// ddmmy returns a date string in "DD.MM.YYYY" format. +pub fn (t Time) ddmmy() string { + return t.get_fmt_date_str(.dot, .ddmmyyyy) +} + +// md returns a date string in "MMM D" format. +pub fn (t Time) md() string { + return t.get_fmt_date_str(.space, .mmmd) +} + +// clean returns a date string in a following format: +// - a date string in "HH:MM" format (24h) for current day +// - a date string in "MMM D HH:MM" format (24h) for date of current year +// - a date string formatted with format function for other dates +pub fn (t Time) clean() string { + znow := now() + // Today + if t.month == znow.month && t.year == znow.year && t.day == znow.day { + return t.get_fmt_time_str(.hhmm24) + } + // This year + if t.year == znow.year { + return t.get_fmt_str(.space, .hhmm24, .mmmd) + } + return t.format() +} + +// clean12 returns a date string in a following format: +// - a date string in "HH:MM" format (12h) for current day +// - a date string in "MMM D HH:MM" format (12h) for date of current year +// - a date string formatted with format function for other dates +pub fn (t Time) clean12() string { + znow := now() + // Today + if t.month == znow.month && t.year == znow.year && t.day == znow.day { + return t.get_fmt_time_str(.hhmm12) + } + // This year + if t.year == znow.year { + return t.get_fmt_str(.space, .hhmm12, .mmmd) + } + return t.format() +} + +// get_fmt_time_str returns a date string with specified FormatTime type. +pub fn (t Time) get_fmt_time_str(fmt_time FormatTime) string { + if fmt_time == .no_time { + return '' + } + tp := if t.hour > 11 { 'p.m.' } else { 'a.m.' } + hour_ := if t.hour > 12 { + t.hour - 12 + } else if t.hour == 0 { + 12 + } else { + t.hour + } + return match fmt_time { + .hhmm12 { '$hour_:${t.minute:02d} $tp' } + .hhmm24 { '${t.hour:02d}:${t.minute:02d}' } + .hhmmss12 { '$hour_:${t.minute:02d}:${t.second:02d} $tp' } + .hhmmss24 { '${t.hour:02d}:${t.minute:02d}:${t.second:02d}' } + .hhmmss24_milli { '${t.hour:02d}:${t.minute:02d}:${t.second:02d}.${(t.microsecond / 1000):03d}' } + .hhmmss24_micro { '${t.hour:02d}:${t.minute:02d}:${t.second:02d}.${t.microsecond:06d}' } + else { 'unknown enumeration $fmt_time' } + } +} + +// get_fmt_time_str returns a date string with specified +// FormatDelimiter and FormatDate type. +pub fn (t Time) get_fmt_date_str(fmt_dlmtr FormatDelimiter, fmt_date FormatDate) string { + if fmt_date == .no_date { + return '' + } + month := '$t.smonth()' + year := '${(t.year % 100):02d}' + mut res := match fmt_date { + .ddmmyy { '${t.day:02d}|${t.month:02d}|$year' } + .ddmmyyyy { '${t.day:02d}|${t.month:02d}|${t.year:04d}' } + .mmddyy { '${t.month:02d}|${t.day:02d}|$year' } + .mmddyyyy { '${t.month:02d}|${t.day:02d}|${t.year:04d}' } + .mmmd { '$month|$t.day' } + .mmmdd { '$month|${t.day:02d}' } + .mmmddyy { '$month|${t.day:02d}|$year' } + .mmmddyyyy { '$month|${t.day:02d}|${t.year:04d}' } + .yyyymmdd { '${t.year:04d}|${t.month:02d}|${t.day:02d}' } + .yymmdd { '$year|${t.month:02d}|${t.day:02d}' } + else { 'unknown enumeration $fmt_date' } + } + del := match fmt_dlmtr { + .dot { '.' } + .hyphen { '-' } + .slash { '/' } + .space { ' ' } + .no_delimiter { '' } + } + res = res.replace('|', del) + return res +} + +// get_fmt_str returns a date string with specified FormatDelimiter, +// FormatTime type, and FormatDate type. +pub fn (t Time) get_fmt_str(fmt_dlmtr FormatDelimiter, fmt_time FormatTime, fmt_date FormatDate) string { + if fmt_date == .no_date { + if fmt_time == .no_time { + // saving one function call although it's checked in + // t.get_fmt_time_str(fmt_time) in the beginning + return '' + } else { + return t.get_fmt_time_str(fmt_time) + } + } else { + if fmt_time != .no_time { + return t.get_fmt_date_str(fmt_dlmtr, fmt_date) + ' ' + t.get_fmt_time_str(fmt_time) + } else { + return t.get_fmt_date_str(fmt_dlmtr, fmt_date) + } + } +} + +// This is just a TEMPORARY function for cookies and their expire dates +pub fn (t Time) utc_string() string { + day_str := t.weekday_str() + month_str := t.smonth() + utc_string := '$day_str, $t.day $month_str $t.year ${t.hour:02d}:${t.minute:02d}:${t.second:02d} UTC' + return utc_string +} diff --git a/v_windows/v/old/vlib/time/misc/misc.v b/v_windows/v/old/vlib/time/misc/misc.v new file mode 100644 index 0000000..0ae7b94 --- /dev/null +++ b/v_windows/v/old/vlib/time/misc/misc.v @@ -0,0 +1,13 @@ +module misc + +import rand +import time + +const ( + start_time_unix = time.now().unix // start_time_unix is set when the program is started. +) + +// random returns a random time struct in *the past*. +pub fn random() time.Time { + return time.unix(int(rand.u64n(misc.start_time_unix))) +} diff --git a/v_windows/v/old/vlib/time/misc/misc_test.v b/v_windows/v/old/vlib/time/misc/misc_test.v new file mode 100644 index 0000000..9bcc8ad --- /dev/null +++ b/v_windows/v/old/vlib/time/misc/misc_test.v @@ -0,0 +1,17 @@ +import time.misc as tmisc +import rand + +fn test_random() { + // guarantee CI test stability, by seeding the random number generator with a known seed + rand.seed([u32(0), 0]) + t1 := tmisc.random() + t2 := tmisc.random() + t3 := tmisc.random() + t4 := tmisc.random() + assert t1.unix != t2.unix + assert t1.unix != t3.unix + assert t1.unix != t4.unix + assert t2.unix != t3.unix + assert t2.unix != t4.unix + assert t3.unix != t4.unix +} diff --git a/v_windows/v/old/vlib/time/operator.v b/v_windows/v/old/vlib/time/operator.v new file mode 100644 index 0000000..7c6e616 --- /dev/null +++ b/v_windows/v/old/vlib/time/operator.v @@ -0,0 +1,21 @@ +module time + +// operator `==` returns true if provided time is equal to time +[inline] +pub fn (t1 Time) == (t2 Time) bool { + return t1.unix == t2.unix && t1.microsecond == t2.microsecond +} + +// operator `<` returns true if provided time is less than time +[inline] +pub fn (t1 Time) < (t2 Time) bool { + return t1.unix < t2.unix || (t1.unix == t2.unix && t1.microsecond < t2.microsecond) +} + +// Time subtract using operator overloading. +[inline] +pub fn (lhs Time) - (rhs Time) Duration { + lhs_micro := lhs.unix * 1000 * 1000 + u64(lhs.microsecond) + rhs_micro := rhs.unix * 1000 * 1000 + u64(rhs.microsecond) + return (i64(lhs_micro) - i64(rhs_micro)) * microsecond +} diff --git a/v_windows/v/old/vlib/time/operator_test.v b/v_windows/v/old/vlib/time/operator_test.v new file mode 100644 index 0000000..5f3e1b7 --- /dev/null +++ b/v_windows/v/old/vlib/time/operator_test.v @@ -0,0 +1,391 @@ +module time + +fn assert_greater_time(ms int, t1 Time) { + sleep(ms * millisecond) + t2 := now() + assert t2 > t1 +} + +// Tests the now in all platform and the gt operator function with at least ms resolution +fn test_now_always_results_in_greater_time() { + t1 := now() + $if macos { + assert_greater_time(1, t1) + return + } + $if windows { + // Lower resolution of time for windows + assert_greater_time(15, t1) + return + } + $if linux { + assert_greater_time(1, t1) + return + } + $if solaris { + assert_greater_time(1, t1) + return + } + // other platforms may have more accurate resolution, + // but we do not know that ... so wait at least 1s: + assert_greater_time(1001, t1) +} + +fn test_time1_should_be_same_as_time2() { + t1 := new_time(Time{ + year: 2000 + month: 5 + day: 10 + hour: 22 + minute: 11 + second: 3 + microsecond: 100 + }) + t2 := new_time(Time{ + year: 2000 + month: 5 + day: 10 + hour: 22 + minute: 11 + second: 3 + microsecond: 100 + }) + assert t1 == t2 +} + +fn test_time1_should_not_be_same_as_time2() { + t1 := new_time(Time{ + year: 2000 + month: 5 + day: 10 + hour: 22 + minute: 11 + second: 3 + microsecond: 100 + }) + // Difference is one microsecond + t2 := new_time(Time{ + year: 2000 + month: 5 + day: 10 + hour: 22 + minute: 11 + second: 3 + microsecond: 101 + }) + t3 := new_time(Time{ + year: 2000 + month: 5 + day: 10 + hour: 22 + minute: 11 + second: 3 + microsecond: 0 + }) + // Difference is one second + t4 := new_time(Time{ + year: 2000 + month: 5 + day: 10 + hour: 22 + minute: 11 + second: 4 + microsecond: 0 + }) + assert t1 != t2 + assert t3 != t4 +} + +fn test_time1_should_be_greater_than_time2() { + t1 := new_time(Time{ + year: 2000 + month: 5 + day: 10 + hour: 22 + minute: 11 + second: 3 + microsecond: 102 + }) + // Difference is one microsecond + t2 := new_time(Time{ + year: 2000 + month: 5 + day: 10 + hour: 22 + minute: 11 + second: 3 + microsecond: 101 + }) + t3 := new_time(Time{ + year: 2000 + month: 5 + day: 10 + hour: 22 + minute: 11 + second: 5 + microsecond: 0 + }) + // Difference is one second + t4 := new_time(Time{ + year: 2000 + month: 5 + day: 10 + hour: 22 + minute: 11 + second: 4 + microsecond: 0 + }) + assert t1 > t2 + assert t3 > t4 +} + +fn test_time2_should_be_less_than_time1() { + t1 := new_time(Time{ + year: 2000 + month: 5 + day: 10 + hour: 22 + minute: 11 + second: 3 + microsecond: 102 + }) + // Difference is one microsecond + t2 := new_time(Time{ + year: 2000 + month: 5 + day: 10 + hour: 22 + minute: 11 + second: 3 + microsecond: 101 + }) + t3 := new_time(Time{ + year: 2000 + month: 5 + day: 10 + hour: 22 + minute: 11 + second: 3 + microsecond: 0 + }) + // Difference is one second + t4 := new_time(Time{ + year: 2000 + month: 5 + day: 10 + hour: 22 + minute: 11 + second: 2 + microsecond: 0 + }) + assert t2 < t1 + assert t4 < t3 +} + +fn test_time1_should_be_greater_or_equal_to_time2_when_gt() { + t1 := new_time(Time{ + year: 2000 + month: 5 + day: 10 + hour: 22 + minute: 11 + second: 3 + microsecond: 102 + }) + // Difference is one microsecond + t2 := new_time(Time{ + year: 2000 + month: 5 + day: 10 + hour: 22 + minute: 11 + second: 3 + microsecond: 101 + }) + t3 := new_time(Time{ + year: 2000 + month: 5 + day: 10 + hour: 22 + minute: 11 + second: 5 + microsecond: 0 + }) + // Difference is one second + t4 := new_time(Time{ + year: 2000 + month: 5 + day: 10 + hour: 22 + minute: 11 + second: 4 + microsecond: 0 + }) + assert t1 >= t2 + assert t3 >= t4 +} + +fn test_time1_should_be_greater_or_equal_to_time2_when_eq() { + t1 := new_time(Time{ + year: 2000 + month: 5 + day: 10 + hour: 22 + minute: 11 + second: 3 + microsecond: 100 + }) + // Difference is one microsecond + t2 := new_time(Time{ + year: 2000 + month: 5 + day: 10 + hour: 22 + minute: 11 + second: 3 + microsecond: 100 + }) + t3 := new_time(Time{ + year: 2000 + month: 5 + day: 10 + hour: 22 + minute: 11 + second: 3 + microsecond: 0 + }) + // Difference is one second + t4 := new_time(Time{ + year: 2000 + month: 5 + day: 10 + hour: 22 + minute: 11 + second: 3 + microsecond: 0 + }) + assert t1 >= t2 + assert t3 >= t4 +} + +fn test_time1_should_be_less_or_equal_to_time2_when_lt() { + t1 := new_time(Time{ + year: 2000 + month: 5 + day: 10 + hour: 22 + minute: 11 + second: 3 + microsecond: 100 + }) + // Difference is one microsecond + t2 := new_time(Time{ + year: 2000 + month: 5 + day: 10 + hour: 22 + minute: 11 + second: 3 + microsecond: 101 + }) + t3 := new_time(Time{ + year: 2000 + month: 5 + day: 10 + hour: 22 + minute: 11 + second: 3 + microsecond: 0 + }) + // Difference is one second + t4 := new_time(Time{ + year: 2000 + month: 5 + day: 10 + hour: 22 + minute: 11 + second: 4 + microsecond: 0 + }) + assert t1 <= t2 + assert t3 <= t4 +} + +fn test_time1_should_be_less_or_equal_to_time2_when_eq() { + t1 := new_time(Time{ + year: 2000 + month: 5 + day: 10 + hour: 22 + minute: 11 + second: 3 + microsecond: 100 + }) + // Difference is one microsecond + t2 := new_time(Time{ + year: 2000 + month: 5 + day: 10 + hour: 22 + minute: 11 + second: 3 + microsecond: 100 + }) + t3 := new_time(Time{ + year: 2000 + month: 5 + day: 10 + hour: 22 + minute: 11 + second: 3 + microsecond: 0 + }) + // Difference is one second + t4 := new_time(Time{ + year: 2000 + month: 5 + day: 10 + hour: 22 + minute: 11 + second: 3 + microsecond: 0 + }) + assert t1 <= t2 + assert t3 <= t4 +} + +fn test_time2_copied_from_time1_should_be_equal() { + t1 := new_time(Time{ + year: 2000 + month: 5 + day: 10 + hour: 22 + minute: 11 + second: 3 + microsecond: 100 + }) + t2 := new_time(t1) + assert t2 == t1 +} + +fn test_subtract() { + d_seconds := 3 + d_microseconds := 13 + duration := d_seconds * second + d_microseconds * microsecond + t1 := new_time(Time{ + year: 2000 + month: 5 + day: 10 + hour: 22 + minute: 11 + second: 3 + microsecond: 100 + }) + t2 := unix2(i64(t1.unix) + d_seconds, t1.microsecond + d_microseconds) + d1 := t2 - t1 + d2 := t1 - t2 + assert d1 > 0 + assert d1 == duration + assert d2 < 0 + assert d2 == -duration +} diff --git a/v_windows/v/old/vlib/time/parse.v b/v_windows/v/old/vlib/time/parse.v new file mode 100644 index 0000000..95c5352 --- /dev/null +++ b/v_windows/v/old/vlib/time/parse.v @@ -0,0 +1,140 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module time + +// parse returns time from a date string in "YYYY-MM-DD HH:MM:SS" format. +pub fn parse(s string) ?Time { + pos := s.index(' ') or { return error('Invalid time format: $s') } + symd := s[..pos] + ymd := symd.split('-') + if ymd.len != 3 { + return error('Invalid time format: $s') + } + shms := s[pos..] + hms := shms.split(':') + hour_ := hms[0][1..] + minute_ := hms[1] + second_ := hms[2] + res := new_time(Time{ + year: ymd[0].int() + month: ymd[1].int() + day: ymd[2].int() + hour: hour_.int() + minute: minute_.int() + second: second_.int() + }) + return res +} + +// parse_rfc2822 returns time from a date string in RFC 2822 datetime format. +pub fn parse_rfc2822(s string) ?Time { + fields := s.split(' ') + if fields.len < 5 { + return error('Invalid time format: $s') + } + pos := months_string.index(fields[2]) or { return error('Invalid time format: $s') } + mm := pos / 3 + 1 + unsafe { + tmstr := malloc_noscan(s.len * 2) + count := C.snprintf(&char(tmstr), (s.len * 2), c'%s-%02d-%s %s', fields[3].str, + mm, fields[1].str, fields[4].str) + return parse(tos(tmstr, count)) + } +} + +// ----- iso8601 ----- +const ( + err_invalid_8601 = 'Invalid 8601 Format' +) + +fn parse_iso8601_date(s string) ?(int, int, int) { + year, month, day, dummy := 0, 0, 0, byte(0) + count := unsafe { C.sscanf(&char(s.str), c'%4d-%2d-%2d%c', &year, &month, &day, &dummy) } + if count != 3 { + return error(time.err_invalid_8601) + } + return year, month, day +} + +fn parse_iso8601_time(s string) ?(int, int, int, int, i64, bool) { + hour_ := 0 + minute_ := 0 + second_ := 0 + microsecond_ := 0 + plus_min_z := `a` + offset_hour := 0 + offset_minute := 0 + mut count := unsafe { + C.sscanf(&char(s.str), c'%2d:%2d:%2d.%6d%c%2d:%2d', &hour_, &minute_, &second_, + µsecond_, &char(&plus_min_z), &offset_hour, &offset_minute) + } + // Missread microsecond ([Sec Hour Minute].len == 3 < 4) + if count < 4 { + count = unsafe { + C.sscanf(&char(s.str), c'%2d:%2d:%2d%c%2d:%2d', &hour_, &minute_, &second_, + &char(&plus_min_z), &offset_hour, &offset_minute) + } + count++ // Increment count because skipped microsecond + } + if count < 4 { + return error(time.err_invalid_8601) + } + is_local_time := plus_min_z == `a` && count == 4 + is_utc := plus_min_z == `Z` && count == 5 + if !(count == 7 || is_local_time || is_utc) { + return error(time.err_invalid_8601) + } + if plus_min_z != `+` && plus_min_z != `-` && !is_utc && !is_local_time { + return error('Invalid 8601 format, expected `Z` or `+` or `-` as time separator') + } + mut unix_offset := 0 + if offset_hour > 0 { + unix_offset += 3600 * offset_hour + } + if offset_minute > 0 { + unix_offset += 60 * offset_minute + } + if plus_min_z == `+` { + unix_offset *= -1 + } + return hour_, minute_, second_, microsecond_, unix_offset, is_local_time +} + +// parse_iso8601 parses rfc8601 time format yyyy-MM-ddTHH:mm:ss.dddddd+dd:dd as local time +// the fraction part is difference in milli seconds and the last part is offset +// from UTC time and can be both +/- HH:mm +// remarks: not all iso8601 is supported +// also checks and support for leapseconds should be added in future PR +pub fn parse_iso8601(s string) ?Time { + t_i := s.index('T') or { -1 } + parts := if t_i != -1 { [s[..t_i], s[t_i + 1..]] } else { s.split(' ') } + if !(parts.len == 1 || parts.len == 2) { + return error(time.err_invalid_8601) + } + year, month, day := parse_iso8601_date(parts[0]) ? + mut hour_, mut minute_, mut second_, mut microsecond_, mut unix_offset, mut is_local_time := 0, 0, 0, 0, i64(0), true + if parts.len == 2 { + hour_, minute_, second_, microsecond_, unix_offset, is_local_time = parse_iso8601_time(parts[1]) ? + } + mut t := new_time(Time{ + year: year + month: month + day: day + hour: hour_ + minute: minute_ + second: second_ + microsecond: microsecond_ + }) + if is_local_time { + return t // Time already local time + } + mut unix_time := t.unix + if unix_offset < 0 { + unix_time -= u64(-unix_offset) + } else if unix_offset > 0 { + unix_time += u64(unix_offset) + } + t = unix2(i64(unix_time), t.microsecond) + return t +} diff --git a/v_windows/v/old/vlib/time/parse_test.v b/v_windows/v/old/vlib/time/parse_test.v new file mode 100644 index 0000000..de7df37 --- /dev/null +++ b/v_windows/v/old/vlib/time/parse_test.v @@ -0,0 +1,138 @@ +import time + +fn test_parse() { + s := '2018-01-27 12:48:34' + t := time.parse(s) or { + assert false + return + } + assert t.year == 2018 && t.month == 1 && t.day == 27 && t.hour == 12 && t.minute == 48 + && t.second == 34 + assert t.unix == 1517057314 +} + +fn test_parse_invalid() { + s := 'Invalid time string' + time.parse(s) or { + assert true + return + } + assert false +} + +fn test_parse_rfc2822() { + s1 := 'Thu, 12 Dec 2019 06:07:45 GMT' + t1 := time.parse_rfc2822(s1) or { + assert false + return + } + assert t1.year == 2019 && t1.month == 12 && t1.day == 12 && t1.hour == 6 && t1.minute == 7 + && t1.second == 45 + assert t1.unix == 1576130865 + s2 := 'Thu 12 Dec 2019 06:07:45 +0800' + t2 := time.parse_rfc2822(s2) or { + assert false + return + } + assert t2.year == 2019 && t2.month == 12 && t2.day == 12 && t2.hour == 6 && t2.minute == 7 + && t2.second == 45 + assert t2.unix == 1576130865 +} + +fn test_parse_rfc2822_invalid() { + s3 := 'Thu 12 Foo 2019 06:07:45 +0800' + time.parse_rfc2822(s3) or { + assert true + return + } + assert false +} + +fn test_parse_iso8601() { + formats := [ + '2020-06-05T15:38:06Z', + '2020-06-05T15:38:06.015959Z', + '2020-06-05T15:38:06.015959+00:00', + '2020-06-05T15:38:06.015959+02:00', + '2020-06-05T15:38:06.015959-02:00', + '2020-11-05T15:38:06.015959Z', + ] + times := [ + [2020, 6, 5, 15, 38, 6, 0], + [2020, 6, 5, 15, 38, 6, 15959], + [2020, 6, 5, 15, 38, 6, 15959], + [2020, 6, 5, 13, 38, 6, 15959], + [2020, 6, 5, 17, 38, 6, 15959], + [2020, 11, 5, 15, 38, 6, 15959], + ] + for i, format in formats { + t := time.parse_iso8601(format) or { + assert false + continue + } + year := times[i][0] + assert t.year == year + month := times[i][1] + assert t.month == month + day := times[i][2] + assert t.day == day + hour := times[i][3] + assert t.hour == hour + minute := times[i][4] + assert t.minute == minute + second := times[i][5] + assert t.second == second + microsecond := times[i][6] + assert t.microsecond == microsecond + } +} + +fn test_parse_iso8601_local() { + format := '2020-06-05T15:38:06.015959' + t := time.parse_iso8601(format) or { + assert false + return + } + assert t.year == 2020 + assert t.month == 6 + assert t.day == 5 + assert t.hour == 15 + assert t.minute == 38 + assert t.second == 6 + assert t.microsecond == 15959 +} + +fn test_parse_iso8601_invalid() { + formats := [ + '', + '2020-06-05X15:38:06.015959Z', + '2020-06-05T15:38:06.015959X', + '2020-06-05T15:38:06.015959+0000', + '2020-06-05T', + '2020-06-05Z', + '2020-06-05+00:00', + '15:38:06', + ] + for format in formats { + time.parse_iso8601(format) or { + assert true + continue + } + assert false + } +} + +fn test_parse_iso8601_date_only() { + format := '2020-06-05' + t := time.parse_iso8601(format) or { + assert false + return + } + assert t.year == 2020 + assert t.month == 6 + assert t.day == 5 + assert t.hour == 0 + assert t.minute == 0 + assert t.second == 0 + assert t.microsecond == 0 +} diff --git a/v_windows/v/old/vlib/time/private_test.v b/v_windows/v/old/vlib/time/private_test.v new file mode 100644 index 0000000..8dde561 --- /dev/null +++ b/v_windows/v/old/vlib/time/private_test.v @@ -0,0 +1,13 @@ +// tests that use and test private functions +module time + +// test the old behavor is same as new, the unix time should always be local time +fn test_new_is_same_as_old_for_all_platforms() { + t := C.time(0) + tm := C.localtime(&t) + old_time := convert_ctime(tm, 0) + new_time := now() + diff := new_time.unix - old_time.unix + // could in very rare cases be that the second changed between calls + assert (diff >= 0 && diff <= 1) == true +} diff --git a/v_windows/v/old/vlib/time/stopwatch.v b/v_windows/v/old/vlib/time/stopwatch.v new file mode 100644 index 0000000..569e10c --- /dev/null +++ b/v_windows/v/old/vlib/time/stopwatch.v @@ -0,0 +1,72 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module time + +pub struct StopWatchOptions { + auto_start bool = true +} + +// StopWatch is used to measure elapsed time. +pub struct StopWatch { +mut: + elapsed u64 +pub mut: + start u64 + end u64 +} + +// new_stopwatch initializes a new StopWatch with the current time as start. +pub fn new_stopwatch(opts StopWatchOptions) StopWatch { + mut initial := u64(0) + if opts.auto_start { + initial = sys_mono_now() + } + return StopWatch{ + elapsed: 0 + start: initial + end: 0 + } +} + +// start starts the stopwatch. If the timer was paused, restarts counting. +pub fn (mut t StopWatch) start() { + t.start = sys_mono_now() + t.end = 0 +} + +// restart restarts the stopwatch. If the timer was paused, restarts counting. +pub fn (mut t StopWatch) restart() { + t.start = sys_mono_now() + t.end = 0 + t.elapsed = 0 +} + +// stop stops the timer, by setting the end time to the current time. +pub fn (mut t StopWatch) stop() { + t.end = sys_mono_now() +} + +// pause resets the `start` time and adds the current elapsed time to `elapsed`. +pub fn (mut t StopWatch) pause() { + if t.start > 0 { + if t.end == 0 { + t.elapsed += sys_mono_now() - t.start + } else { + t.elapsed += t.end - t.start + } + } + t.start = 0 +} + +// elapsed returns the Duration since the last start call +pub fn (t StopWatch) elapsed() Duration { + if t.start > 0 { + if t.end == 0 { + return Duration(i64(sys_mono_now() - t.start + t.elapsed)) + } else { + return Duration(i64(t.end - t.start + t.elapsed)) + } + } + return Duration(i64(t.elapsed)) +} diff --git a/v_windows/v/old/vlib/time/stopwatch_test.v b/v_windows/v/old/vlib/time/stopwatch_test.v new file mode 100644 index 0000000..49d005a --- /dev/null +++ b/v_windows/v/old/vlib/time/stopwatch_test.v @@ -0,0 +1,36 @@ +import time + +// NB: on CI jobs, especially msvc ones, sleep_ms may sleep for much more +// time than you have specified. To avoid false positives from CI test +// failures, some of the asserts will be run only if you pass `-d stopwatch` +fn test_stopwatch_works_as_intended() { + mut sw := time.new_stopwatch() + // sample code that you want to measure: + println('Hello world') + time.sleep(1 * time.millisecond) + // + println('Greeting the world took: ${sw.elapsed().nanoseconds()}ns') + assert sw.elapsed().nanoseconds() > 0 +} + +fn test_stopwatch_time_between_pause_and_start_should_be_skipped_in_elapsed() { + println('Testing pause function') + mut sw := time.new_stopwatch() + time.sleep(10 * time.millisecond) // A + eprintln('Elapsed after 10ms nap: ${sw.elapsed().milliseconds()}ms') + assert sw.elapsed().milliseconds() >= 8 // sometimes it sleeps for 9ms on windows.. + sw.pause() + time.sleep(10 * time.millisecond) + eprintln('Elapsed after pause and another 10ms nap: ${sw.elapsed().milliseconds()}ms') + assert sw.elapsed().milliseconds() >= 8 + $if stopwatch ? { + assert sw.elapsed().milliseconds() < 20 + } + sw.start() + time.sleep(10 * time.millisecond) // B + eprintln('Elapsed after resume and another 10ms nap: ${sw.elapsed().milliseconds()}ms') + assert sw.elapsed().milliseconds() >= 18 + $if stopwatch ? { + assert sw.elapsed().milliseconds() < 30 + } +} diff --git a/v_windows/v/old/vlib/time/time.v b/v_windows/v/old/vlib/time/time.v new file mode 100644 index 0000000..14f0c7a --- /dev/null +++ b/v_windows/v/old/vlib/time/time.v @@ -0,0 +1,431 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module time + +#include + +pub const ( + days_string = 'MonTueWedThuFriSatSun' + month_days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] + months_string = 'JanFebMarAprMayJunJulAugSepOctNovDec' + // The unsigned zero year for internal calculations. + // Must be 1 mod 400, and times before it will not compute correctly, + // but otherwise can be changed at will. + absolute_zero_year = i64(-292277022399) // as i64 + seconds_per_minute = 60 + seconds_per_hour = 60 * seconds_per_minute + seconds_per_day = 24 * seconds_per_hour + seconds_per_week = 7 * seconds_per_day + days_per_400_years = 365 * 400 + 97 + days_per_100_years = 365 * 100 + 24 + days_per_4_years = 365 * 4 + 1 + days_before = [ + 0, + 31, + 31 + 28, + 31 + 28 + 31, + 31 + 28 + 31 + 30, + 31 + 28 + 31 + 30 + 31, + 31 + 28 + 31 + 30 + 31 + 30, + 31 + 28 + 31 + 30 + 31 + 30 + 31, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30, + 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31, + ] + long_days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', + 'Sunday', + ] +) + +// Time contains various time units for a point in time. +pub struct Time { +pub: + year int + month int + day int + hour int + minute int + second int + microsecond int + unix u64 +} + +// FormatDelimiter contains different time formats. +pub enum FormatTime { + hhmm12 + hhmm24 + hhmmss12 + hhmmss24 + hhmmss24_milli + hhmmss24_micro + no_time +} + +// FormatDelimiter contains different date formats. +pub enum FormatDate { + ddmmyy + ddmmyyyy + mmddyy + mmddyyyy + mmmd + mmmdd + mmmddyy + mmmddyyyy + no_date + yyyymmdd + yymmdd +} + +// FormatDelimiter contains different time/date delimiters. +pub enum FormatDelimiter { + dot + hyphen + slash + space + no_delimiter +} + +// C.timeval represents a C time value. +pub struct C.timeval { + tv_sec u64 + tv_usec u64 +} + +fn C.localtime(t &C.time_t) &C.tm + +fn C.time(t &C.time_t) C.time_t + +// now returns current local time. +pub fn now() Time { + $if macos { + return darwin_now() + } + $if windows { + return win_now() + } + $if solaris { + return solaris_now() + } + $if linux || android { + return linux_now() + } + // defaults to most common feature, the microsecond precision is not available + // in this API call + t := C.time(0) + now := C.localtime(&t) + return convert_ctime(*now, 0) +} + +// utc returns the current UTC time. +pub fn utc() Time { + $if macos { + return darwin_utc() + } + $if windows { + return win_utc() + } + $if solaris { + return solaris_utc() + } + $if linux || android { + return linux_utc() + } + // defaults to most common feature, the microsecond precision is not available + // in this API call + t := C.time(0) + _ = C.time(&t) + return unix2(i64(t), 0) +} + +// smonth returns month name. +pub fn (t Time) smonth() string { + if t.month <= 0 || t.month > 12 { + return '---' + } + i := t.month - 1 + return time.months_string[i * 3..(i + 1) * 3] +} + +// new_time returns a time struct with calculated Unix time. +pub fn new_time(t Time) Time { + if t.unix != 0 { + return t + } + tt := C.tm{ + tm_sec: t.second + tm_min: t.minute + tm_hour: t.hour + tm_mday: t.day + tm_mon: t.month - 1 + tm_year: t.year - 1900 + } + utime := u64(make_unix_time(tt)) + return Time{ + ...t + unix: utime + } +} + +// unix_time returns Unix time. +[inline] +pub fn (t Time) unix_time() int { + return int(t.unix) +} + +// unix_time_milli returns Unix time with millisecond resolution. +[inline] +pub fn (t Time) unix_time_milli() u64 { + return t.unix * 1000 + u64(t.microsecond / 1000) +} + +// add returns a new time that duration is added +pub fn (t Time) add(d Duration) Time { + microseconds := i64(t.unix) * 1000 * 1000 + t.microsecond + d.microseconds() + unix := microseconds / (1000 * 1000) + micro := microseconds % (1000 * 1000) + return unix2(unix, int(micro)) +} + +// add_seconds returns a new time struct with an added number of seconds. +pub fn (t Time) add_seconds(seconds int) Time { + return t.add(seconds * time.second) +} + +// add_days returns a new time struct with an added number of days. +pub fn (t Time) add_days(days int) Time { + return t.add(days * 24 * time.hour) +} + +// since returns a number of seconds elapsed since a given time. +fn since(t Time) int { + // TODO Use time.Duration instead of seconds + return 0 +} + +// relative returns a string representation of the difference between t +// and the current time. +pub fn (t Time) relative() string { + znow := now() + secs := znow.unix - t.unix + if secs <= 30 { + // right now or in the future + // TODO handle time in the future + return 'now' + } + if secs < 60 { + return '1m' + } + if secs < 3600 { + m := secs / 60 + if m == 1 { + return '1 minute ago' + } + return '$m minutes ago' + } + if secs < 3600 * 24 { + h := secs / 3600 + if h == 1 { + return '1 hour ago' + } + return '$h hours ago' + } + if secs < 3600 * 24 * 5 { + d := secs / 3600 / 24 + if d == 1 { + return '1 day ago' + } + return '$d days ago' + } + if secs > 3600 * 24 * 10000 { + return '' + } + return t.md() +} + +// relative_short returns a string saying how long ago a time occured as follows: +// 0-30 seconds: `"now"`; 30-60 seconds: `"1m"`; anything else is rounded to the +// nearest minute, hour or day; anything higher than 10000 days (about 27 years) +// years returns an empty string. +// Some Examples: +// `0s -> 'now'`; +// `20s -> 'now'`; +// `47s -> '1m'`; +// `456s -> '7m'`; +// `1234s -> '20m'`; +// `16834s -> '4h'`; +// `1687440s -> '33d'`; +// `15842354871s -> ''` +pub fn (t Time) relative_short() string { + znow := now() + secs := znow.unix - t.unix + if secs <= 30 { + // right now or in the future + // TODO handle time in the future + return 'now' + } + if secs < 60 { + return '1m' + } + if secs < 3600 { + return '${secs / 60}m' + } + if secs < 3600 * 24 { + return '${secs / 3600}h' + } + if secs < 3600 * 24 * 5 { + return '${secs / 3600 / 24}d' + } + if secs > 3600 * 24 * 10000 { + return '' + } + return t.md() +} + +// day_of_week returns the current day of a given year, month, and day, +// as an integer. +pub fn day_of_week(y int, m int, d int) int { + // Sakomotho's algorithm is explained here: + // https://stackoverflow.com/a/6385934 + t := [0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4] + mut sy := y + if m < 3 { + sy = sy - 1 + } + return (sy + sy / 4 - sy / 100 + sy / 400 + t[m - 1] + d - 1) % 7 + 1 +} + +// day_of_week returns the current day as an integer. +pub fn (t Time) day_of_week() int { + return day_of_week(t.year, t.month, t.day) +} + +// weekday_str returns the current day as a string. +pub fn (t Time) weekday_str() string { + i := t.day_of_week() - 1 + return time.days_string[i * 3..(i + 1) * 3] +} + +// weekday_str returns the current day as a string. +pub fn (t Time) long_weekday_str() string { + i := t.day_of_week() - 1 + return time.long_days[i] +} + +// ticks returns a number of milliseconds elapsed since system start. +pub fn ticks() i64 { + $if windows { + return C.GetTickCount() + } $else { + ts := C.timeval{} + C.gettimeofday(&ts, 0) + return i64(ts.tv_sec * u64(1000) + (ts.tv_usec / u64(1000))) + } + // t := i64(C.mach_absolute_time()) + // # Nanoseconds elapsedNano = AbsoluteToNanoseconds( *(AbsoluteTime *) &t ); + // # return (double)(* (uint64_t *) &elapsedNano) / 1000000; +} + +/* +// sleep makes the calling thread sleep for a given number of seconds. +[deprecated: 'call time.sleep(n * time.second)'] +pub fn sleep(seconds int) { + wait(seconds * time.second) +} +*/ + +// is_leap_year checks if a given a year is a leap year. +pub fn is_leap_year(year int) bool { + return (year % 4 == 0) && (year % 100 != 0 || year % 400 == 0) +} + +// days_in_month returns a number of days in a given month. +pub fn days_in_month(month int, year int) ?int { + if month > 12 || month < 1 { + return error('Invalid month: $month') + } + extra := if month == 2 && is_leap_year(year) { 1 } else { 0 } + res := time.month_days[month - 1] + extra + return res +} + +// str returns time in the same format as `parse` expects ("YYYY-MM-DD HH:MM:SS"). +pub fn (t Time) str() string { + // TODO Define common default format for + // `str` and `parse` and use it in both ways + return t.format_ss() +} + +// convert_ctime converts a C time to V time. +fn convert_ctime(t C.tm, microsecond int) Time { + return Time{ + year: t.tm_year + 1900 + month: t.tm_mon + 1 + day: t.tm_mday + hour: t.tm_hour + minute: t.tm_min + second: t.tm_sec + microsecond: time.microsecond + unix: u64(make_unix_time(t)) + } +} + +// A lot of these are taken from the Go library. +pub type Duration = i64 + +pub const ( + nanosecond = Duration(1) + microsecond = Duration(1000 * nanosecond) + millisecond = Duration(1000 * microsecond) + second = Duration(1000 * millisecond) + minute = Duration(60 * second) + hour = Duration(60 * minute) + infinite = Duration(C.INT64_MAX) +) + +// nanoseconds returns the duration as an integer number of nanoseconds. +pub fn (d Duration) nanoseconds() i64 { + return i64(d) +} + +// microseconds returns the duration as an integer number of microseconds. +pub fn (d Duration) microseconds() i64 { + return i64(d) / 1000 +} + +// milliseconds returns the duration as an integer number of milliseconds. +pub fn (d Duration) milliseconds() i64 { + return i64(d) / 1000000 +} + +// The following functions return floating point numbers because it's common to +// consider all of them in sub-one intervals +// seconds returns the duration as a floating point number of seconds. +pub fn (d Duration) seconds() f64 { + sec := d / time.second + nsec := d % time.second + return f64(sec) + f64(nsec) / 1e9 +} + +// minutes returns the duration as a floating point number of minutes. +pub fn (d Duration) minutes() f64 { + min := d / time.minute + nsec := d % time.minute + return f64(min) + f64(nsec) / (60 * 1e9) +} + +// hours returns the duration as a floating point number of hours. +pub fn (d Duration) hours() f64 { + hr := d / time.hour + nsec := d % time.hour + return f64(hr) + f64(nsec) / (60 * 60 * 1e9) +} + +// offset returns time zone UTC offset in seconds. +pub fn offset() int { + t := now() + local := t.local() + return int(local.unix - t.unix) +} diff --git a/v_windows/v/old/vlib/time/time_darwin.c.v b/v_windows/v/old/vlib/time/time_darwin.c.v new file mode 100644 index 0000000..d16c66b --- /dev/null +++ b/v_windows/v/old/vlib/time/time_darwin.c.v @@ -0,0 +1,84 @@ +module time + +#include + +const ( + // start_time is needed on Darwin and Windows because of potential overflows + start_time = C.mach_absolute_time() + time_base = init_time_base() +) + +[typedef] +struct C.mach_timebase_info_data_t { + numer u32 + denom u32 +} + +fn C.mach_absolute_time() u64 + +fn C.mach_timebase_info(&C.mach_timebase_info_data_t) + +fn C.clock_gettime_nsec_np(int) u64 + +struct InternalTimeBase { + numer u32 = 1 + denom u32 = 1 +} + +pub struct C.timeval { + tv_sec u64 + tv_usec u64 +} + +fn init_time_base() C.mach_timebase_info_data_t { + tb := C.mach_timebase_info_data_t{} + C.mach_timebase_info(&tb) + return C.mach_timebase_info_data_t{ + numer: tb.numer + denom: tb.denom + } +} + +fn sys_mono_now_darwin() u64 { + tm := C.mach_absolute_time() + if time.time_base.denom == 0 { + C.mach_timebase_info(&time.time_base) + } + return (tm - time.start_time) * time.time_base.numer / time.time_base.denom +} + +// NB: vpc_now_darwin is used by `v -profile` . +// It should NOT call *any other v function*, just C functions and casts. +[inline] +fn vpc_now_darwin() u64 { + tm := C.mach_absolute_time() + if time.time_base.denom == 0 { + C.mach_timebase_info(&time.time_base) + } + return (tm - time.start_time) * time.time_base.numer / time.time_base.denom +} + +// darwin_now returns a better precision current time for Darwin based operating system +// this should be implemented with native system calls eventually +// but for now a bit tweaky. It uses the deprecated gettimeofday clock to get +// the microseconds seconds part and converts to local time +fn darwin_now() Time { + // get the high precision time as UTC clock + tv := C.timeval{} + C.gettimeofday(&tv, 0) + loc_tm := C.tm{} + asec := voidptr(&tv.tv_sec) + C.localtime_r(asec, &loc_tm) + return convert_ctime(loc_tm, int(tv.tv_usec)) +} + +// darwin_utc returns a better precision current time for Darwin based operating system +// this should be implemented with native system calls eventually +// but for now a bit tweaky. It uses the deprecated gettimeofday clock to get +// the microseconds seconds part and normal local time to get correct local time +fn darwin_utc() Time { + // get the high precision time as UTC clock + tv := C.timeval{} + C.gettimeofday(&tv, 0) + return unix2(i64(tv.tv_sec), int(tv.tv_usec)) +} diff --git a/v_windows/v/old/vlib/time/time_format_test.v b/v_windows/v/old/vlib/time/time_format_test.v new file mode 100644 index 0000000..6042cf9 --- /dev/null +++ b/v_windows/v/old/vlib/time/time_format_test.v @@ -0,0 +1,90 @@ +import time + +const ( + time_to_test = time.Time{ + year: 1980 + month: 7 + day: 11 + hour: 21 + minute: 23 + second: 42 + unix: 332198622 + } +) + +fn test_now_format() { + t := time.now() + u := t.unix + assert t.format() == time.unix(int(u)).format() +} + +fn test_format() { + assert '11.07.1980 21:23' == time_to_test.get_fmt_str(.dot, .hhmm24, .ddmmyyyy) +} + +fn test_hhmm() { + assert '21:23' == time_to_test.hhmm() +} + +fn test_hhmm12() { + assert '9:23 p.m.' == time_to_test.hhmm12() +} + +fn test_hhmmss() { + assert '21:23:42' == time_to_test.hhmmss() +} + +fn test_ymmdd() { + assert '1980-07-11' == time_to_test.ymmdd() +} + +fn test_ddmmy() { + assert '11.07.1980' == time_to_test.ddmmy() +} + +fn test_md() { + assert 'Jul 11' == time_to_test.md() +} + +fn test_get_fmt_time_str() { + assert '21:23:42' == time_to_test.get_fmt_time_str(.hhmmss24) + assert '21:23' == time_to_test.get_fmt_time_str(.hhmm24) + assert '9:23:42 p.m.' == time_to_test.get_fmt_time_str(.hhmmss12) + assert '9:23 p.m.' == time_to_test.get_fmt_time_str(.hhmm12) +} + +fn test_get_fmt_date_str() { + assert '11.07.1980' == time_to_test.get_fmt_date_str(.dot, .ddmmyyyy) + assert '11/07/1980' == time_to_test.get_fmt_date_str(.slash, .ddmmyyyy) + assert '11-07-1980' == time_to_test.get_fmt_date_str(.hyphen, .ddmmyyyy) + assert '11 07 1980' == time_to_test.get_fmt_date_str(.space, .ddmmyyyy) + assert '07.11.1980' == time_to_test.get_fmt_date_str(.dot, .mmddyyyy) + assert '07/11/1980' == time_to_test.get_fmt_date_str(.slash, .mmddyyyy) + assert '07-11-1980' == time_to_test.get_fmt_date_str(.hyphen, .mmddyyyy) + assert '07 11 1980' == time_to_test.get_fmt_date_str(.space, .mmddyyyy) + assert '11.07.80' == time_to_test.get_fmt_date_str(.dot, .ddmmyy) + assert '11/07/80' == time_to_test.get_fmt_date_str(.slash, .ddmmyy) + assert '11-07-80' == time_to_test.get_fmt_date_str(.hyphen, .ddmmyy) + assert '11 07 80' == time_to_test.get_fmt_date_str(.space, .ddmmyy) + assert '07.11.80' == time_to_test.get_fmt_date_str(.dot, .mmddyy) + assert '07/11/80' == time_to_test.get_fmt_date_str(.slash, .mmddyy) + assert '07-11-80' == time_to_test.get_fmt_date_str(.hyphen, .mmddyy) + assert '07 11 80' == time_to_test.get_fmt_date_str(.space, .mmddyy) + assert 'Jul 11' == time_to_test.get_fmt_date_str(.space, .mmmd) + assert 'Jul 11' == time_to_test.get_fmt_date_str(.space, .mmmdd) + assert 'Jul 11 80' == time_to_test.get_fmt_date_str(.space, .mmmddyy) + assert 'Jul 11 1980' == time_to_test.get_fmt_date_str(.space, .mmmddyyyy) + assert '1980-07-11' == time_to_test.get_fmt_date_str(.hyphen, .yyyymmdd) + assert '80.07.11' == time_to_test.get_fmt_date_str(.dot, .yymmdd) +} + +fn test_get_fmt_str() { + // Since get_fmt_time_str and get_fmt_date_str do have comprehensive + // tests I don't want to exaggerate here with all possible + // combinations. + assert '11.07.1980 21:23:42' == time_to_test.get_fmt_str(.dot, .hhmmss24, .ddmmyyyy) +} + +fn test_utc_string() { + assert 'Fri, 11 Jul 1980 21:23:42 UTC' == time_to_test.utc_string() +} diff --git a/v_windows/v/old/vlib/time/time_linux.c.v b/v_windows/v/old/vlib/time/time_linux.c.v new file mode 100644 index 0000000..b0ab7ad --- /dev/null +++ b/v_windows/v/old/vlib/time/time_linux.c.v @@ -0,0 +1,26 @@ +module time + +// sys_mono_now_darwin - dummy fn to compile on all platforms/compilers +fn sys_mono_now_darwin() u64 { + return 0 +} + +// darwin_now - dummy fn to compile on all platforms/compilers +pub fn darwin_now() Time { + return Time{} +} + +// solaris_now - dummy fn to compile on all platforms/compilers +pub fn solaris_now() Time { + return Time{} +} + +// darwin_utc - dummy fn to compile on all platforms/compilers +pub fn darwin_utc() Time { + return Time{} +} + +// solaris_utc - dummy fn to compile on all platforms/compilers +pub fn solaris_utc() Time { + return Time{} +} diff --git a/v_windows/v/old/vlib/time/time_nix.c.v b/v_windows/v/old/vlib/time/time_nix.c.v new file mode 100644 index 0000000..82ff548 --- /dev/null +++ b/v_windows/v/old/vlib/time/time_nix.c.v @@ -0,0 +1,156 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module time + +#include +#include + +struct C.tm { + tm_sec int + tm_min int + tm_hour int + tm_mday int + tm_mon int + tm_year int + tm_wday int + tm_yday int + tm_isdst int +} + +fn C.timegm(&C.tm) C.time_t + +// fn C.gmtime_r(&tm, &gbuf) +fn C.localtime_r(t &time_t, tm &C.tm) + +fn make_unix_time(t C.tm) int { + return int(C.timegm(&t)) +} + +// local returns t with the location set to local time. +pub fn (t Time) local() Time { + loc_tm := C.tm{} + C.localtime_r(voidptr(&t.unix), &loc_tm) + return convert_ctime(loc_tm, t.microsecond) +} + +// in most systems, these are __quad_t, which is an i64 +struct C.timespec { +mut: + tv_sec i64 + tv_nsec i64 +} + +// the first arg is defined in include/bits/types.h as `__S32_TYPE`, which is `int` +fn C.clock_gettime(int, &C.timespec) + +fn C.nanosleep(req &C.timespec, rem &C.timespec) int + +// sys_mono_now returns a *monotonically increasing time*, NOT a time adjusted for daylight savings, location etc. +pub fn sys_mono_now() u64 { + $if macos { + return sys_mono_now_darwin() + } $else { + ts := C.timespec{} + C.clock_gettime(C.CLOCK_MONOTONIC, &ts) + return u64(ts.tv_sec) * 1000000000 + u64(ts.tv_nsec) + } +} + +// NB: vpc_now is used by `v -profile` . +// It should NOT call *any other v function*, just C functions and casts. +[inline] +fn vpc_now() u64 { + ts := C.timespec{} + C.clock_gettime(C.CLOCK_MONOTONIC, &ts) + return u64(ts.tv_sec) * 1000000000 + u64(ts.tv_nsec) +} + +// The linux_* functions are placed here, since they're used on Android as well +// TODO: should `$if linux {}` be parsed on Android as well? (Android runs under the Linux kernel) +// linux_now returns the local time with high precision for most os:es +// this should be implemented properly with support for leap seconds. +// It uses the realtime clock to get and converts it to local time +fn linux_now() Time { + // get the high precision time as UTC realtime clock + // and use the nanoseconds part + mut ts := C.timespec{} + C.clock_gettime(C.CLOCK_REALTIME, &ts) + loc_tm := C.tm{} + C.localtime_r(voidptr(&ts.tv_sec), &loc_tm) + return convert_ctime(loc_tm, int(ts.tv_nsec / 1000)) +} + +fn linux_utc() Time { + // get the high precision time as UTC realtime clock + // and use the nanoseconds part + mut ts := C.timespec{} + C.clock_gettime(C.CLOCK_REALTIME, &ts) + return unix2(i64(ts.tv_sec), int(ts.tv_nsec / 1000)) +} + +// dummy to compile with all compilers +pub fn win_now() Time { + return Time{} +} + +// dummy to compile with all compilers +pub fn win_utc() Time { + return Time{} +} + +// dummy to compile with all compilers +pub struct C.timeval { + tv_sec u64 + tv_usec u64 +} + +// return absolute timespec for now()+d +pub fn (d Duration) timespec() C.timespec { + mut ts := C.timespec{} + C.clock_gettime(C.CLOCK_REALTIME, &ts) + d_sec := d / second + d_nsec := d % second + ts.tv_sec += d_sec + ts.tv_nsec += d_nsec + if ts.tv_nsec > i64(second) { + ts.tv_nsec -= i64(second) + ts.tv_sec++ + } + return ts +} + +// return timespec of 1970/1/1 +pub fn zero_timespec() C.timespec { + ts := C.timespec{ + tv_sec: 0 + tv_nsec: 0 + } + return ts +} + +// sleep makes the calling thread sleep for a given duration (in nanoseconds). +pub fn sleep(duration Duration) { + mut req := C.timespec{duration / second, duration % second} + rem := C.timespec{} + for C.nanosleep(&req, &rem) < 0 { + if C.errno == C.EINTR { + // Interrupted by a signal handler + req = rem + } else { + break + } + } +} + +// some *nix system functions (e.g. `C.poll()`, C.epoll_wait()) accept an `int` +// value as *timeout in milliseconds* with the special value `-1` meaning "infinite" +pub fn (d Duration) sys_milliseconds() int { + if d > C.INT32_MAX * millisecond { // treat 2147483647000001 .. C.INT64_MAX as "infinite" + return -1 + } else if d <= 0 { + return 0 // treat negative timeouts as 0 - consistent with Unix behaviour + } else { + return int(d / millisecond) + } +} diff --git a/v_windows/v/old/vlib/time/time_solaris.c.v b/v_windows/v/old/vlib/time/time_solaris.c.v new file mode 100644 index 0000000..b87f1c8 --- /dev/null +++ b/v_windows/v/old/vlib/time/time_solaris.c.v @@ -0,0 +1,32 @@ +module time + +// solaris_now returns the local time with high precision for most os:es +// this should be implemented properly with support for leap seconds. +// It uses the realtime clock to get and converts it to local time +fn solaris_now() Time { + // get the high precision time as UTC realtime clock + // and use the nanoseconds part + mut ts := C.timespec{} + C.clock_gettime(C.CLOCK_REALTIME, &ts) + loc_tm := C.tm{} + C.localtime_r(voidptr(&ts.tv_sec), &loc_tm) + return convert_ctime(loc_tm, int(ts.tv_nsec / 1000)) +} + +fn solaris_utc() Time { + // get the high precision time as UTC realtime clock + // and use the nanoseconds part + mut ts := C.timespec{} + C.clock_gettime(C.CLOCK_REALTIME, &ts) + return unix2(i64(ts.tv_sec), int(ts.tv_nsec / 1000)) +} + +// dummy to compile with all compilers +pub fn darwin_now() Time { + return Time{} +} + +// dummy to compile with all compilers +pub fn darwin_utc() Time { + return Time{} +} diff --git a/v_windows/v/old/vlib/time/time_test.v b/v_windows/v/old/vlib/time/time_test.v new file mode 100644 index 0000000..0511916 --- /dev/null +++ b/v_windows/v/old/vlib/time/time_test.v @@ -0,0 +1,247 @@ +import time +import math + +const ( + time_to_test = time.Time{ + year: 1980 + month: 7 + day: 11 + hour: 21 + minute: 23 + second: 42 + microsecond: 123456 + unix: 332198622 + } +) + +fn test_is_leap_year() { + // 1996 % 4 = 0 and 1996 % 100 > 0 + assert time.is_leap_year(1996) == true + // 2000 % 4 = 0 and 2000 % 400 = 0 + assert time.is_leap_year(2000) == true + // 1996 % 4 > 0 + assert time.is_leap_year(1997) == false + // 2000 % 4 = 0 and 2000 % 100 = 0 + assert time.is_leap_year(2100) == false +} + +fn check_days_in_month(month int, year int, expected int) bool { + res := time.days_in_month(month, year) or { return false } + return res == expected +} + +fn test_days_in_month() { + days_in_month := [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] + for i, days in days_in_month { + month := i + 1 + assert check_days_in_month(month, 2001, days) + } +} + +fn test_unix() { + t := time.unix(1564366499) + assert t.year == 2019 + assert t.month == 7 + assert t.day == 29 + assert t.hour == 2 + assert t.minute == 14 + assert t.second == 59 + t2 := time.unix(1078058096) + assert t2.year == 2004 + assert t2.month == 2 + assert t2.day == 29 + assert t2.hour == 12 + assert t2.minute == 34 + assert t2.second == 56 + t3 := time.unix(1070236799) + assert t3.year == 2003 + assert t3.month == 11 + assert t3.day == 30 + assert t3.hour == 23 + assert t3.minute == 59 + assert t3.second == 59 + t4 := time.unix(1577783439) + assert t4.year == 2019 + assert t4.month == 12 + assert t4.day == 31 + assert t4.hour == 9 + assert t4.minute == 10 + assert t4.second == 39 + t5 := time.unix(-1824922433) + assert t5.year == 1912 + assert t5.month == 3 + assert t5.day == 4 + assert t5.hour == 5 + assert t5.minute == 6 + assert t5.second == 7 + t6 := time.unix(1577858969) + assert t6.year == 2020 + assert t6.month == 1 + assert t6.day == 1 + assert t6.hour == 6 + assert t6.minute == 9 + assert t6.second == 29 +} + +fn test_format_ss() { + assert '11.07.1980 21:23:42' == time_to_test.get_fmt_str(.dot, .hhmmss24, .ddmmyyyy) +} + +fn test_format_ss_milli() { + assert '11.07.1980 21:23:42.123' == time_to_test.get_fmt_str(.dot, .hhmmss24_milli, + .ddmmyyyy) + assert '1980-07-11 21:23:42.123' == time_to_test.format_ss_milli() +} + +fn test_format_ss_micro() { + assert '11.07.1980 21:23:42.123456' == time_to_test.get_fmt_str(.dot, .hhmmss24_micro, + .ddmmyyyy) + assert '1980-07-11 21:23:42.123456' == time_to_test.format_ss_micro() +} + +fn test_smonth() { + month_names := ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', + 'Dec', + ] + for i, name in month_names { + month_num := i + 1 + t := time.Time{ + year: 1980 + month: month_num + day: 1 + hour: 0 + minute: 0 + second: 0 + unix: 0 + } + assert t.smonth() == name + } +} + +fn test_day_of_week() { + for i in 0 .. 7 { + day_of_week := i + 1 + // 2 Dec 2019 is Monday + t := time.Time{ + year: 2019 + month: 12 + day: 2 + i + hour: 0 + minute: 0 + second: 0 + unix: 0 + } + assert day_of_week == t.day_of_week() + } +} + +fn test_weekday_str() { + day_names := ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] + for i, name in day_names { + // 2 Dec 2019 is Monday + t := time.Time{ + year: 2019 + month: 12 + day: 2 + i + hour: 0 + minute: 0 + second: 0 + unix: 0 + } + assert t.weekday_str() == name + } +} + +fn test_add() { + d_seconds := 3 + d_microseconds := 13 + duration := time.Duration(d_seconds * time.second + d_microseconds * time.microsecond) + t1 := time_to_test + t2 := time_to_test.add(duration) + assert t2.second == t1.second + d_seconds + assert t2.microsecond == t1.microsecond + d_microseconds + assert t2.unix == t1.unix + u64(d_seconds) + t3 := time_to_test.add(-duration) + assert t3.second == t1.second - d_seconds + assert t3.microsecond == t1.microsecond - d_microseconds + assert t3.unix == t1.unix - u64(d_seconds) +} + +fn test_add_days() { + num_of_days := 3 + t := time_to_test.add_days(num_of_days) + assert t.day == time_to_test.day + num_of_days + assert t.unix == time_to_test.unix + 86400 * u64(num_of_days) +} + +fn test_str() { + assert '1980-07-11 21:23:42' == time_to_test.str() +} + +// not optimal test but will find obvious bugs +fn test_now() { + now := time.now() + // The year the test was built + assert now.year >= 2020 + assert now.month > 0 + assert now.month <= 12 + assert now.minute >= 0 + assert now.minute < 60 + assert now.second >= 0 + assert now.second <= 60 // <= 60 cause of leap seconds + assert now.microsecond >= 0 + assert now.microsecond < 1000000 +} + +fn test_utc() { + now := time.utc() + // The year the test was built + assert now.year >= 2020 + assert now.month > 0 + assert now.month <= 12 + assert now.minute >= 0 + assert now.minute < 60 + assert now.second >= 0 + assert now.second <= 60 // <= 60 cause of leap seconds + assert now.microsecond >= 0 + assert now.microsecond < 1000000 +} + +fn test_unix_time() { + t1 := time.utc() + time.sleep(50 * time.millisecond) + t2 := time.utc() + ut1 := t1.unix_time() + ut2 := t2.unix_time() + assert ut2 - ut1 < 2 + // + utm1 := t1.unix_time_milli() + utm2 := t2.unix_time_milli() + assert (utm1 - u64(ut1) * 1000) < 1000 + assert (utm2 - u64(ut2) * 1000) < 1000 + // + // println('utm1: $utm1 | utm2: $utm2') + assert utm2 - utm1 > 2 + assert utm2 - utm1 < 999 +} + +fn test_offset() { + u := time.utc() + n := time.now() + // + mut diff_seconds := 0 + if u.day != n.day { + if u.day > n.day { + diff_seconds = int(math.abs(((u.hour * 60 + u.minute) - (n.hour * 60 + n.minute)) * 60)) - 86400 + } else { + diff_seconds = 86400 - int(math.abs(((u.hour * 60 + u.minute) - (n.hour * 60 + n.minute)) * 60)) + } + if math.abs(u.day - n.day) > 1 { // different month + diff_seconds = diff_seconds * -1 + } + } else { // same day + diff_seconds = ((n.hour * 60 + n.minute) - (u.hour * 60 + u.minute)) * 60 + } + + assert diff_seconds == time.offset() +} diff --git a/v_windows/v/old/vlib/time/time_windows.c.v b/v_windows/v/old/vlib/time/time_windows.c.v new file mode 100644 index 0000000..d44e6c9 --- /dev/null +++ b/v_windows/v/old/vlib/time/time_windows.c.v @@ -0,0 +1,233 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module time + +#include +// #include + +struct C.tm { + tm_year int + tm_mon int + tm_mday int + tm_hour int + tm_min int + tm_sec int +} + +struct C._FILETIME { + dwLowDateTime u32 + dwHighDateTime u32 +} + +struct SystemTime { + year u16 + month u16 + day_of_week u16 + day u16 + hour u16 + minute u16 + second u16 + millisecond u16 +} + +fn C.GetSystemTimeAsFileTime(lpSystemTimeAsFileTime &C._FILETIME) + +fn C.FileTimeToSystemTime(lpFileTime &C._FILETIME, lpSystemTime &SystemTime) + +fn C.SystemTimeToTzSpecificLocalTime(lpTimeZoneInformation &C.TIME_ZONE_INFORMATION, lpUniversalTime &SystemTime, lpLocalTime &SystemTime) + +fn C.localtime_s(t &C.time_t, tm &C.tm) + +const ( + // start_time is needed on Darwin and Windows because of potential overflows + start_time = init_win_time_start() + freq_time = init_win_time_freq() + start_local_time = local_as_unix_time() +) + +// in most systems, these are __quad_t, which is an i64 +struct C.timespec { + tv_sec i64 + tv_nsec i64 +} + +fn C._mkgmtime(&C.tm) C.time_t + +fn C.QueryPerformanceCounter(&u64) C.BOOL + +fn C.QueryPerformanceFrequency(&u64) C.BOOL + +fn make_unix_time(t C.tm) int { + return int(C._mkgmtime(&t)) +} + +fn init_win_time_freq() u64 { + f := u64(0) + C.QueryPerformanceFrequency(&f) + return f +} + +fn init_win_time_start() u64 { + s := u64(0) + C.QueryPerformanceCounter(&s) + return s +} + +// sys_mono_now returns a *monotonically increasing time*, NOT a time adjusted for daylight savings, location etc. +pub fn sys_mono_now() u64 { + tm := u64(0) + C.QueryPerformanceCounter(&tm) // XP or later never fail + return (tm - time.start_time) * 1000000000 / time.freq_time +} + +// NB: vpc_now is used by `v -profile` . +// It should NOT call *any other v function*, just C functions and casts. +[inline] +fn vpc_now() u64 { + tm := u64(0) + C.QueryPerformanceCounter(&tm) + return tm +} + +// local_as_unix_time returns the current local time as unix time +fn local_as_unix_time() int { + t := C.time(0) + tm := C.localtime(&t) + return make_unix_time(tm) +} + +// local - return the time `t`, converted to the currently active local timezone +pub fn (t Time) local() Time { + st_utc := SystemTime{ + year: u16(t.year) + month: u16(t.month) + day: u16(t.day) + hour: u16(t.hour) + minute: u16(t.minute) + second: u16(t.second) + } + st_local := SystemTime{} + C.SystemTimeToTzSpecificLocalTime(voidptr(0), &st_utc, &st_local) + t_local := Time{ + year: st_local.year + month: st_local.month + day: st_local.day + hour: st_local.hour + minute: st_local.minute + second: st_local.second // These are the same + microsecond: st_local.millisecond * 1000 + unix: u64(st_local.unix_time()) + } + return t_local +} + +// win_now calculates current time using winapi to get higher resolution on windows +// GetSystemTimeAsFileTime is used and converted to local time. It can resolve time +// down to millisecond. Other more precice methods can be implemented in the future +fn win_now() Time { + ft_utc := C._FILETIME{} + C.GetSystemTimeAsFileTime(&ft_utc) + st_utc := SystemTime{} + C.FileTimeToSystemTime(&ft_utc, &st_utc) + st_local := SystemTime{} + C.SystemTimeToTzSpecificLocalTime(voidptr(0), &st_utc, &st_local) + t := Time{ + year: st_local.year + month: st_local.month + day: st_local.day + hour: st_local.hour + minute: st_local.minute + second: st_local.second + microsecond: st_local.millisecond * 1000 + unix: u64(st_local.unix_time()) + } + return t +} + +// win_utc calculates current time using winapi to get higher resolution on windows +// GetSystemTimeAsFileTime is used. It can resolve time down to millisecond +// other more precice methods can be implemented in the future +fn win_utc() Time { + ft_utc := C._FILETIME{} + C.GetSystemTimeAsFileTime(&ft_utc) + st_utc := SystemTime{} + C.FileTimeToSystemTime(&ft_utc, &st_utc) + t := Time{ + year: st_utc.year + month: st_utc.month + day: st_utc.day + hour: st_utc.hour + minute: st_utc.minute + second: st_utc.second + microsecond: st_utc.millisecond * 1000 + unix: u64(st_utc.unix_time()) + } + return t +} + +// unix_time returns Unix time. +pub fn (st SystemTime) unix_time() int { + tt := C.tm{ + tm_sec: st.second + tm_min: st.minute + tm_hour: st.hour + tm_mday: st.day + tm_mon: st.month - 1 + tm_year: st.year - 1900 + } + return make_unix_time(tt) +} + +// dummy to compile with all compilers +pub fn darwin_now() Time { + return Time{} +} + +// dummy to compile with all compilers +pub fn linux_now() Time { + return Time{} +} + +// dummy to compile with all compilers +pub fn solaris_now() Time { + return Time{} +} + +// dummy to compile with all compilers +pub fn darwin_utc() Time { + return Time{} +} + +// dummy to compile with all compilers +pub fn linux_utc() Time { + return Time{} +} + +// dummy to compile with all compilers +pub fn solaris_utc() Time { + return Time{} +} + +// dummy to compile with all compilers +pub struct C.timeval { + tv_sec u64 + tv_usec u64 +} + +// sleep makes the calling thread sleep for a given duration (in nanoseconds). +pub fn sleep(duration Duration) { + C.Sleep(int(duration / millisecond)) +} + +// some Windows system functions (e.g. `C.WaitForSingleObject()`) accept an `u32` +// value as *timeout in milliseconds* with the special value `u32(-1)` meaning "infinite" +pub fn (d Duration) sys_milliseconds() u32 { + if d >= u32(-1) * millisecond { // treat 4294967295000000 .. C.INT64_MAX as "infinite" + return u32(-1) + } else if d <= 0 { + return 0 // treat negative timeouts as 0 - consistent with Unix behaviour + } else { + return u32(d / millisecond) + } +} diff --git a/v_windows/v/old/vlib/time/unix.v b/v_windows/v/old/vlib/time/unix.v new file mode 100644 index 0000000..ac9d010 --- /dev/null +++ b/v_windows/v/old/vlib/time/unix.v @@ -0,0 +1,124 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module time + +// unix returns a time struct from Unix time. +pub fn unix(abs int) Time { + // Split into day and time + mut day_offset := abs / seconds_per_day + if abs % seconds_per_day < 0 { + // Compensate for round towards zero on integers as we want floored instead + day_offset-- + } + year, month, day := calculate_date_from_offset(day_offset) + hr, min, sec := calculate_time_from_offset(abs % seconds_per_day) + return Time{ + year: year + month: month + day: day + hour: hr + minute: min + second: sec + unix: u64(abs) + } +} + +// unix2 returns a time struct from Unix time and microsecond value +pub fn unix2(abs i64, microsecond int) Time { + // Split into day and time + mut day_offset := abs / seconds_per_day + if abs % seconds_per_day < 0 { + // Compensate for round towards zero on integers as we want floored instead + day_offset-- + } + year, month, day := calculate_date_from_offset(day_offset) + hr, min, sec := calculate_time_from_offset(abs % seconds_per_day) + return Time{ + year: year + month: month + day: day + hour: hr + minute: min + second: sec + microsecond: microsecond + unix: u64(abs) + } +} + +fn calculate_date_from_offset(day_offset_ i64) (int, int, int) { + mut day_offset := day_offset_ + // Move offset to year 2001 as it's the start of a new 400-year cycle + // Code below this rely on the fact that the day_offset is lined up with the 400-year cycle + // 1970-2000 (inclusive) has 31 years (8 of which are leap years) + mut year := 2001 + day_offset -= 31 * 365 + 8 + // Account for 400 year cycle + year += int(day_offset / days_per_400_years) * 400 + day_offset %= days_per_400_years + // Account for 100 year cycle + if day_offset == days_per_100_years * 4 { + year += 300 + day_offset -= days_per_100_years * 3 + } else { + year += int(day_offset / days_per_100_years) * 100 + day_offset %= days_per_100_years + } + // Account for 4 year cycle + if day_offset == days_per_4_years * 25 { + year += 96 + day_offset -= days_per_4_years * 24 + } else { + year += int(day_offset / days_per_4_years) * 4 + day_offset %= days_per_4_years + } + // Account for every year + if day_offset == 365 * 4 { + year += 3 + day_offset -= 365 * 3 + } else { + year += int(day_offset / 365) + day_offset %= 365 + } + if day_offset < 0 { + year-- + if is_leap_year(year) { + day_offset += 366 + } else { + day_offset += 365 + } + } + if is_leap_year(year) { + if day_offset > 31 + 29 - 1 { + // After leap day; pretend it wasn't there. + day_offset-- + } else if day_offset == 31 + 29 - 1 { + // Leap day. + return year, 2, 29 + } + } + mut estimated_month := day_offset / 31 + for day_offset >= days_before[estimated_month + 1] { + estimated_month++ + } + for day_offset < days_before[estimated_month] { + if estimated_month == 0 { + break + } + estimated_month-- + } + day_offset -= days_before[estimated_month] + return year, int(estimated_month + 1), int(day_offset + 1) +} + +fn calculate_time_from_offset(second_offset_ i64) (int, int, int) { + mut second_offset := second_offset_ + if second_offset < 0 { + second_offset += seconds_per_day + } + hour_ := second_offset / seconds_per_hour + second_offset %= seconds_per_hour + min := second_offset / seconds_per_minute + second_offset %= seconds_per_minute + return int(hour_), int(min), int(second_offset) +} diff --git a/v_windows/v/old/vlib/v/README.md b/v_windows/v/old/vlib/v/README.md new file mode 100644 index 0000000..c5360f9 --- /dev/null +++ b/v_windows/v/old/vlib/v/README.md @@ -0,0 +1,119 @@ +# Compiler pipeline +A simple high level explanation +how the compiler pipeline (`parser` -> `checker` -> `generator`) works. + +## Reading files +### Getting builtin files +To load all builtin files, +a preference `Preferences.lookup_path` for the path where to look for exists. +See `Builder.get_builtin_files` as example. +If the file is a `.vsh` file and the backend is C, `vlib/os` will also be loaded as builtin. + +### Getting project files +Either there is a specific file: `my_file.v` or a directory containing V files. +In the last case it scans that directory for all files. +See `Builder.v_files_from_dir` as the helper method. +This list of files needs to be filtered so that only `*.v` files exist. + +Skips the following file types: +- `*_test.v` +- either `*.c.v` or `*.c.js` depending on the backend +- all files that doesn't end with `.v` +- Files that are not defined in `Preferences.compile_defines` +or `Preferences.compile_defines_all` **if any file is defined**. + +## Parsing files +To parse something a new template is created as the first step: +```v +import v.ast + +table := ast.new_table() +``` + +a new preference is created: +```v +import v.pref + +pref := &pref.Preferences{} +``` + +and a new scope is created: +```v +import v.ast + +scope := ast.Scope{ + parent: 0 +} +``` +after that, you can parse your files. + +## Parse text +If you want to parse only text which isn't saved on the disk you can use this function. +```v oksyntax +import v.parser + +code := '' +// table, pref and scope needs to be passed as reference +parsed_file := parser.parse_text(code, table, .parse_comments, &pref, &scope) +``` + +## Parse a single file +For parsing files on disk, a path needs to be provided. +The paths are collected one step earlier. +```v oksyntax +import v.parser + +path := '' +// table, pref and scope needs to be passed as reference +parsed_file := parser.parse_file(path, table, .parse_comments, &pref, &scope) +``` + +## Parse a set of files +If you have a batch of paths available which should be parsed, +there is also a function which does all the work. +```v oksyntax +import v.parser + +paths := [''] +// table, pref and scope needs to be passed as reference +parsed_files := parser.parse_files(paths, table, &pref, &scope) +``` + +## Parse imports +A file often contains imports. These imports might need to be parsed as well. +The builder contains a method which does this: `Builder.parse_imports`. + +If the module which is imported isn't parsed already, +you have to collect it relatively from the main file. +For this the `ast.File` contains a list of imports. +Those imports needs to be found on disk. +`.` is just replaced with seperators in the relative location of the main file. +Then all files from that directory are collected and parsed again like the previous steps explained. + +## Checking AST +A new checker is created: +```v oksyntax +import v.checker + +mut checker := checker.new_checker(table, &pref) +``` + +After checking your files in `checker.errors` and `checker.warnings` you can see the results. + +### Check `ast.File` +```v oksyntax +checker.check(parsed_file) +``` + +### Check a list of `ast.File` +```v oksyntax +checker.check_files(parsed_files) +``` + +## Generate target from AST +Generating C code works just as this: +```v oksyntax +import v.gen.c + +res := c.gen(parsed_files, table, &pref) +``` diff --git a/v_windows/v/old/vlib/v/TEMPLATES.md b/v_windows/v/old/vlib/v/TEMPLATES.md new file mode 100644 index 0000000..25806d2 --- /dev/null +++ b/v_windows/v/old/vlib/v/TEMPLATES.md @@ -0,0 +1,129 @@ +V allows for easily using text templates, expanded at compile time to +V functions, that efficiently produce text output. This is especially +usefull for templated HTML views, but the mechanism is general enough +to be used for other kinds of text output also. + +# Template directives +Each template directive begins with an `@` sign. +Some directives contain a `{}` block, others only have `''` (string) parameters. + +Newlines on the beginning and end are ignored in `{}` blocks, +otherwise this (see [if](#if) for this syntax): +```html +@if bool_val { + This is shown if bool_val is true +} +``` +... would output: +```html + + This is shown if bool_val is true + +``` +... which is less readable. + +## if +The if directive, consists of three parts, the `@if` tag, the condition (same syntax like in V) +and the `{}` block, where you can write html, which will be rendered if the condition is true: +``` +@if {} +``` + +### Example +```html +@if bool_val { + This is shown if bool_val is true +} +``` +One-liner: +```html +@if bool_val { This is shown if bool_val is true } +``` + +The first example would result in: +```html + This is shown if bool_val is true +``` +... while the one-liner results in: +```html +This is shown if bool_val is true +``` + +## for +The for directive consists of three parts, the `@for` tag, +the condition (same syntax like in V) and the `{}` block, +where you can write text, rendered for each iteration of the loop: +``` +@for {} +``` + +### Example for @for +```html +@for i, val in my_vals { + $i - $val +} +``` +One-liner: +```html +@for i, val in my_vals { $i - $val } +``` + +The first example would result in: +```html + 0 - "First" + 1 - "Second" + 2 - "Third" + ... +``` +... while the one-liner results in: +```html +0 - "First" +1 - "Second" +2 - "Third" +... +``` + +You can also write (and all other for condition syntaxes that are allowed in V): +```html +@for i = 0; i < 5; i++ { + $i +} +``` + +## include +The include directive is for including other html files (which will be processed as well) +and consists of two parts, the `@include` tag and a following `''` string. +The path parameter is relative to the `/templates` directory in the corresponding project. + +### Example for the folder structure of a project using templates: +``` +Project root +/templates + - index.html + /headers + - base.html +``` + +`index.html` +```html +
      @include 'header/base'
      +``` +> Note that there shouldn't be a file suffix, + it is automatically appended and only allows `html` files. + + +## js +The js directive consists of two parts, the `@js` tag and `''` string, +where you can insert your src +``` +@js '' +``` + +### Example for the @js directive: +```html +@js 'myscripts.js' +``` + +# Variables +All variables, which are declared before the $tmpl can be used through the `@{my_var}` syntax. +It's also possible to use properties of structs here like `@{my_struct.prop}`. diff --git a/v_windows/v/old/vlib/v/ast/ast.v b/v_windows/v/old/vlib/v/ast/ast.v new file mode 100644 index 0000000..b2e1e3b --- /dev/null +++ b/v_windows/v/old/vlib/v/ast/ast.v @@ -0,0 +1,2020 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module ast + +import v.token +import v.errors +import v.pref + +pub type TypeDecl = AliasTypeDecl | FnTypeDecl | SumTypeDecl + +pub type Expr = AnonFn | ArrayDecompose | ArrayInit | AsCast | Assoc | AtExpr | BoolLiteral | + CTempVar | CallExpr | CastExpr | ChanInit | CharLiteral | Comment | ComptimeCall | + ComptimeSelector | ConcatExpr | DumpExpr | EmptyExpr | EnumVal | FloatLiteral | GoExpr | + Ident | IfExpr | IfGuardExpr | IndexExpr | InfixExpr | IntegerLiteral | IsRefType | + Likely | LockExpr | MapInit | MatchExpr | NodeError | None | OffsetOf | OrExpr | ParExpr | + PostfixExpr | PrefixExpr | RangeExpr | SelectExpr | SelectorExpr | SizeOf | SqlExpr | + StringInterLiteral | StringLiteral | StructInit | TypeNode | TypeOf | UnsafeExpr + +pub type Stmt = AsmStmt | AssertStmt | AssignStmt | Block | BranchStmt | CompFor | ConstDecl | + DeferStmt | EmptyStmt | EnumDecl | ExprStmt | FnDecl | ForCStmt | ForInStmt | ForStmt | + GlobalDecl | GotoLabel | GotoStmt | HashStmt | Import | InterfaceDecl | Module | NodeError | + Return | SqlStmt | StructDecl | TypeDecl + +// NB: when you add a new Expr or Stmt type with a .pos field, remember to update +// the .position() token.Position methods too. +pub type ScopeObject = AsmRegister | ConstField | GlobalField | Var + +// TODO: replace Param +pub type Node = CallArg | ConstField | EmptyNode | EnumField | Expr | File | GlobalField | + IfBranch | MatchBranch | NodeError | Param | ScopeObject | SelectBranch | Stmt | StructField | + StructInitField + +pub struct TypeNode { +pub: + typ Type + pos token.Position +} + +pub struct EmptyExpr { + x int +} + +pub fn empty_expr() Expr { + return EmptyExpr{} +} + +pub struct EmptyStmt { +pub: + pos token.Position +} + +pub fn empty_stmt() Stmt { + return EmptyStmt{} +} + +pub struct EmptyNode { + x int +} + +pub fn empty_node() Node { + return EmptyNode{} +} + +// `{stmts}` or `unsafe {stmts}` +pub struct Block { +pub: + stmts []Stmt + is_unsafe bool + pos token.Position +} + +// | IncDecStmt k +// Stand-alone expression in a statement list. +pub struct ExprStmt { +pub: + expr Expr + pos token.Position + comments []Comment +pub mut: + is_expr bool + typ Type +} + +pub struct IntegerLiteral { +pub: + val string + pos token.Position +} + +pub struct FloatLiteral { +pub: + val string + pos token.Position +} + +pub struct StringLiteral { +pub: + val string + is_raw bool + language Language + pos token.Position +} + +// 'name: $name' +pub struct StringInterLiteral { +pub: + vals []string + exprs []Expr + fwidths []int + precisions []int + pluss []bool + fills []bool + fmt_poss []token.Position + pos token.Position +pub mut: + expr_types []Type + fmts []byte + need_fmts []bool // an explicit non-default fmt required, e.g. `x` +} + +pub struct CharLiteral { +pub: + val string + pos token.Position +} + +pub struct BoolLiteral { +pub: + val bool + pos token.Position +} + +// `foo.bar` +pub struct SelectorExpr { +pub: + pos token.Position + field_name string + is_mut bool // is used for the case `if mut ident.selector is MyType {`, it indicates if the root ident is mutable + mut_pos token.Position + next_token token.Kind +pub mut: + expr Expr // expr.field_name + expr_type Type // type of `Foo` in `Foo.bar` + typ Type // type of the entire thing (`Foo.bar`) + name_type Type // T in `T.name` or typeof in `typeof(expr).name` + scope &Scope + from_embed_type Type // holds the type of the embed that the method is called from +} + +// root_ident returns the origin ident where the selector started. +pub fn (e &SelectorExpr) root_ident() ?Ident { + mut root := e.expr + for root is SelectorExpr { + // TODO: remove this line + selector_expr := root as SelectorExpr + root = selector_expr.expr + } + if root is Ident { + return root as Ident + } + + return none +} + +// module declaration +pub struct Module { +pub: + name string // encoding.base64 + short_name string // base64 + attrs []Attr + pos token.Position + name_pos token.Position // `name` in import name + is_skipped bool // module main can be skipped in single file programs +} + +pub struct StructField { +pub: + pos token.Position + type_pos token.Position + comments []Comment + has_default_expr bool + attrs []Attr + is_pub bool + default_val string + is_mut bool + is_global bool +pub mut: + default_expr Expr + default_expr_typ Type + name string + typ Type +} + +/* +pub struct Field { +pub: + name string + pos token.Position +pub mut: + typ Type +} +*/ + +// const field in const declaration group +pub struct ConstField { +pub: + mod string + name string + expr Expr // the value expr of field; everything after `=` + is_pub bool + pos token.Position +pub mut: + typ Type // the type of the const field, it can be any type in V + comments []Comment // comments before current const field + // the comptime_expr_value field is filled by the checker, when it has enough + // info to evaluate the constant at compile time + comptime_expr_value ComptTimeConstValue = empty_comptime_const_expr() +} + +// const declaration +pub struct ConstDecl { +pub: + is_pub bool + pos token.Position +pub mut: + fields []ConstField // all the const fields in the `const (...)` block + end_comments []Comment // comments that after last const field + is_block bool // const() block +} + +pub struct StructDecl { +pub: + pos token.Position + name string + generic_types []Type + is_pub bool + // _pos fields for vfmt + mut_pos int // mut: + pub_pos int // pub: + pub_mut_pos int // pub mut: + global_pos int // __global: + module_pos int // module: + language Language + is_union bool + attrs []Attr + end_comments []Comment + embeds []Embed +pub mut: + fields []StructField +} + +pub struct Embed { +pub: + typ Type + pos token.Position + comments []Comment +} + +pub struct InterfaceEmbedding { +pub: + name string + typ Type + pos token.Position + comments []Comment +} + +pub struct InterfaceDecl { +pub: + name string + typ Type + name_pos token.Position + language Language + field_names []string + is_pub bool + mut_pos int // mut: + pos token.Position + pre_comments []Comment + generic_types []Type +pub mut: + methods []FnDecl + fields []StructField + // + ifaces []InterfaceEmbedding + are_ifaces_expanded bool +} + +pub struct StructInitField { +pub: + pos token.Position + name_pos token.Position + comments []Comment + next_comments []Comment +pub mut: + expr Expr + name string + typ Type + expected_type Type + parent_type Type +} + +pub struct StructInitEmbed { +pub: + expr Expr + pos token.Position + comments []Comment + next_comments []Comment +pub mut: + name string + typ Type + expected_type Type +} + +pub struct StructInit { +pub: + pos token.Position + name_pos token.Position + is_short bool +pub mut: + unresolved bool + pre_comments []Comment + typ Type + update_expr Expr + update_expr_type Type + update_expr_comments []Comment + has_update_expr bool + fields []StructInitField + embeds []StructInitEmbed +} + +// import statement +pub struct Import { +pub: + mod string // the module name of the import + alias string // the `x` in `import xxx as x` + pos token.Position + mod_pos token.Position + alias_pos token.Position + syms_pos token.Position +pub mut: + syms []ImportSymbol // the list of symbols in `import {symbol1, symbol2}` + comments []Comment + next_comments []Comment +} + +// import symbol,for import {symbol} syntax +pub struct ImportSymbol { +pub: + pos token.Position + name string +} + +// anonymous function +pub struct AnonFn { +pub mut: + decl FnDecl + typ Type // the type of anonymous fn. Both .typ and .decl.name are auto generated + has_gen bool // has been generated +} + +// function or method declaration +pub struct FnDecl { +pub: + name string + mod string + is_deprecated bool + is_pub bool + is_variadic bool + is_anon bool + is_noreturn bool // true, when [noreturn] is used on a fn + is_manualfree bool // true, when [manualfree] is used on a fn + is_main bool // true for `fn main()` + is_test bool // true for `fn test_abcde` + is_conditional bool // true for `[if abc] fn abc(){}` + is_exported bool // true for `[export: 'exact_C_name']` + is_keep_alive bool // passed memory must not be freed (by GC) before function returns + is_unsafe bool // true, when [unsafe] is used on a fn + receiver StructField // TODO this is not a struct field + receiver_pos token.Position // `(u User)` in `fn (u User) name()` position + is_method bool + method_type_pos token.Position // `User` in ` fn (u User)` position + method_idx int + rec_mut bool // is receiver mutable + rec_share ShareType + language Language + no_body bool // just a definition `fn C.malloc()` + is_builtin bool // this function is defined in builtin/strconv + body_pos token.Position // function bodys position + file string + generic_names []string + is_direct_arr bool // direct array access + attrs []Attr + ctdefine_idx int = -1 // the index in fn.attrs of `[if xyz]`, when such attribute exists +pub mut: + params []Param + stmts []Stmt + defer_stmts []DeferStmt + return_type Type + return_type_pos token.Position // `string` in `fn (u User) name() string` position + has_return bool + // + comments []Comment // comments *after* the header, but *before* `{`; used for InterfaceDecl + next_comments []Comment // coments that are one line after the decl; used for InterfaceDecl + // + source_file &File = 0 + scope &Scope + label_names []string + pos token.Position // function declaration position +} + +// break, continue +pub struct BranchStmt { +pub: + kind token.Kind + label string + pos token.Position +} + +// function or method call expr +pub struct CallExpr { +pub: + pos token.Position + name_pos token.Position + mod string +pub mut: + name string // left.name() + is_method bool + is_field bool // temp hack, remove ASAP when re-impl CallExpr / Selector (joe) + is_keep_alive bool // GC must not free arguments before fn returns + is_noreturn bool // whether the function/method is marked as [noreturn] + args []CallArg + expected_arg_types []Type + language Language + or_block OrExpr + left Expr // `user` in `user.register()` + left_type Type // type of `user` + receiver_type Type // User + return_type Type + should_be_skipped bool + concrete_types []Type // concrete types, e.g. + concrete_list_pos token.Position + free_receiver bool // true if the receiver expression needs to be freed + scope &Scope + from_embed_type Type // holds the type of the embed that the method is called from + comments []Comment +} + +/* +pub struct AutofreeArgVar { + name string + idx int +} +*/ +// function call argument: `f(callarg)` +pub struct CallArg { +pub: + is_mut bool + share ShareType + comments []Comment +pub mut: + expr Expr + typ Type + is_tmp_autofree bool // this tells cgen that a tmp variable has to be used for the arg expression in order to free it after the call + pos token.Position + // tmp_name string // for autofree +} + +// function return statement +pub struct Return { +pub: + pos token.Position + comments []Comment +pub mut: + exprs []Expr + types []Type +} + +/* +pub enum Expr { + Binary(InfixExpr) + If(IfExpr) + Integer(IntegerExpr) +} +*/ +/* +pub struct Stmt { + pos int + //end int +} +*/ +pub struct Var { +pub: + name string + expr Expr + share ShareType + is_mut bool + is_autofree_tmp bool + is_arg bool // fn args should not be autofreed + is_auto_deref bool +pub mut: + typ Type + orig_type Type // original sumtype type; 0 if it's not a sumtype + smartcasts []Type // nested sum types require nested smart casting, for that a list of types is needed + // TODO: move this to a real docs site later + // 10 <- original type (orig_type) + // [11, 12, 13] <- cast order (smartcasts) + // 12 <- the current casted type (typ) + pos token.Position + is_used bool + is_changed bool // to detect mutable vars that are never changed + // + // (for setting the position after the or block for autofree) + is_or bool // `x := foo() or { ... }` + is_tmp bool // for tmp for loop vars, so that autofree can skip them + is_auto_heap bool // value whoes address goes out of scope + is_stack_obj bool // may be pointer to stack value (`mut` or `&` arg and not [heap] struct) +} + +// used for smartcasting only +// struct fields change type in scopes +pub struct ScopeStructField { +pub: + struct_type Type // type of struct + name string + pos token.Position + typ Type + smartcasts []Type // nested sum types require nested smart casting, for that a list of types is needed + orig_type Type // original sumtype type; 0 if it's not a sumtype + // TODO: move this to a real docs site later + // 10 <- original type (orig_type) + // [11, 12, 13] <- cast order (smartcasts) + // 12 <- the current casted type (typ) +} + +pub struct GlobalField { +pub: + name string + has_expr bool + pos token.Position + typ_pos token.Position +pub mut: + expr Expr + typ Type + comments []Comment +} + +pub struct GlobalDecl { +pub: + mod string + pos token.Position + is_block bool // __global() block +pub mut: + fields []GlobalField + end_comments []Comment +} + +pub struct EmbeddedFile { +pub: + rpath string // used in the source code, as an ID/key to the embed + apath string // absolute path during compilation to the resource +} + +// Each V source file is represented by one File structure. +// When the V compiler runs, the parser will fill an []File. +// That array is then passed to V's checker. +[heap] +pub struct File { +pub: + nr_lines int // number of source code lines in the file (including newlines and comments) + nr_bytes int // number of processed source code bytes + mod Module // the module of the source file (from `module xyz` at the top) + global_scope &Scope + is_test bool // true for _test.v files +pub mut: + path string // absolute path of the source file - '/projects/v/file.v' + path_base string // file name - 'file.v' (useful for tracing) + scope &Scope + stmts []Stmt // all the statements in the source file + imports []Import // all the imports + auto_imports []string // imports that were implicitely added + embedded_files []EmbeddedFile // list of files to embed in the binary + imported_symbols map[string]string // used for `import {symbol}`, it maps symbol => module.symbol + errors []errors.Error // all the checker errors in the file + warnings []errors.Warning // all the checker warnings in the file + notices []errors.Notice // all the checker notices in the file + generic_fns []&FnDecl + global_labels []string // from `asm { .globl labelname }` +} + +[unsafe] +pub fn (f &File) free() { + unsafe { + f.path.free() + f.path_base.free() + f.scope.free() + f.stmts.free() + f.imports.free() + f.auto_imports.free() + f.embedded_files.free() + f.imported_symbols.free() + f.errors.free() + f.warnings.free() + f.notices.free() + f.global_labels.free() + } +} + +pub struct IdentFn { +pub mut: + typ Type +} + +// TODO: (joe) remove completely, use ident.obj +// instead which points to the scope object +pub struct IdentVar { +pub mut: + typ Type + is_mut bool + is_static bool + is_optional bool + share ShareType +} + +pub type IdentInfo = IdentFn | IdentVar + +pub enum IdentKind { + unresolved + blank_ident + variable + constant + global + function +} + +// A single identifier +pub struct Ident { +pub: + language Language + tok_kind token.Kind + pos token.Position + mut_pos token.Position + comptime bool +pub mut: + scope &Scope + obj ScopeObject + mod string + name string + kind IdentKind + info IdentInfo + is_mut bool +} + +pub fn (i &Ident) var_info() IdentVar { + match mut i.info { + IdentVar { + return i.info + } + else { + // return IdentVar{} + panic('Ident.var_info(): info is not IdentVar variant') + } + } +} + +// left op right +// See: token.Kind.is_infix +pub struct InfixExpr { +pub: + op token.Kind + pos token.Position + is_stmt bool +pub mut: + left Expr + right Expr + left_type Type + right_type Type + auto_locked string + or_block OrExpr +} + +// ++, -- +pub struct PostfixExpr { +pub: + op token.Kind + expr Expr + pos token.Position +pub mut: + auto_locked string +} + +// See: token.Kind.is_prefix +pub struct PrefixExpr { +pub: + op token.Kind + pos token.Position +pub mut: + right_type Type + right Expr + or_block OrExpr + is_option bool // IfGuard +} + +pub struct IndexExpr { +pub: + pos token.Position + index Expr // [0], RangeExpr [start..end] or map[key] + or_expr OrExpr +pub mut: + left Expr + left_type Type // array, map, fixed array + is_setter bool + is_map bool + is_array bool + is_farray bool + is_option bool // IfGuard +} + +pub struct IfExpr { +pub: + is_comptime bool + tok_kind token.Kind + left Expr // `a` in `a := if ...` + pos token.Position + post_comments []Comment +pub mut: + branches []IfBranch // includes all `else if` branches + is_expr bool + typ Type + has_else bool + // implements bool // comptime $if implements interface +} + +pub struct IfBranch { +pub: + cond Expr + pos token.Position + body_pos token.Position + comments []Comment +pub mut: + pkg_exist bool + stmts []Stmt + scope &Scope +} + +pub struct UnsafeExpr { +pub: + expr Expr + pos token.Position +} + +pub struct LockExpr { +pub: + stmts []Stmt + is_rlock []bool + pos token.Position +pub mut: + lockeds []Expr // `x`, `y.z` in `lock x, y.z {` + comments []Comment + is_expr bool + typ Type + scope &Scope +} + +pub struct MatchExpr { +pub: + tok_kind token.Kind + cond Expr + branches []MatchBranch + pos token.Position + comments []Comment // comments before the first branch +pub mut: + is_expr bool // returns a value + return_type Type + cond_type Type // type of `x` in `match x {` + expected_type Type // for debugging only + is_sum_type bool +} + +pub struct MatchBranch { +pub: + ecmnts [][]Comment // inline comments for each left side expr + stmts []Stmt // right side + pos token.Position + is_else bool + post_comments []Comment // comments below ´... }´ + branch_pos token.Position // for checker errors about invalid branches +pub mut: + exprs []Expr // left side + scope &Scope +} + +pub struct SelectExpr { +pub: + branches []SelectBranch + pos token.Position + has_exception bool +pub mut: + is_expr bool // returns a value + expected_type Type // for debugging only +} + +pub struct SelectBranch { +pub: + stmt Stmt // `a := <-ch` or `ch <- a` + stmts []Stmt // right side + pos token.Position + comment Comment // comment above `select {` + is_else bool + is_timeout bool + post_comments []Comment +} + +pub enum CompForKind { + methods + fields + attributes +} + +pub struct CompFor { +pub: + val_var string + stmts []Stmt + kind CompForKind + pos token.Position + typ_pos token.Position +pub mut: + // expr Expr + typ Type +} + +pub struct ForStmt { +pub: + cond Expr + stmts []Stmt + is_inf bool // `for {}` + pos token.Position +pub mut: + label string // `label: for {` + scope &Scope +} + +pub struct ForInStmt { +pub: + key_var string + val_var string + cond Expr + is_range bool + high Expr // `10` in `for i in 0..10 {` + stmts []Stmt + pos token.Position + val_is_mut bool // `for mut val in vals {` means that modifying `val` will modify the array + // and the array cannot be indexed inside the loop +pub mut: + key_type Type + val_type Type + cond_type Type + kind Kind // array/map/string + label string // `label: for {` + scope &Scope +} + +pub struct ForCStmt { +pub: + init Stmt // i := 0; + has_init bool + cond Expr // i < 10; + has_cond bool + inc Stmt // i++; i += 2 + has_inc bool + is_multi bool // for a,b := 0,1; a < 10; a,b = a+b, a {...} + stmts []Stmt + pos token.Position +pub mut: + label string // `label: for {` + scope &Scope +} + +// #include, #define etc +pub struct HashStmt { +pub: + mod string + pos token.Position + source_file string +pub mut: + val string // example: 'include # please install openssl // comment' + kind string // : 'include' + main string // : '' + msg string // : 'please install openssl' + ct_conds []Expr // *all* comptime conditions, that must be true, for the hash to be processed + // ct_conds is filled by the checker, based on the current nesting of `$if cond1 {}` blocks +} + +/* +// filter(), map(), sort() +pub struct Lambda { +pub: + name string +} +*/ +// variable assign statement +pub struct AssignStmt { +pub: + op token.Kind // include: =,:=,+=,-=,*=,/= and so on; for a list of all the assign operators, see vlib/token/token.v + pos token.Position + comments []Comment + end_comments []Comment +pub mut: + right []Expr + left []Expr + left_types []Type + right_types []Type + is_static bool // for translated code only + is_simple bool // `x+=2` in `for x:=1; ; x+=2` + has_cross_var bool +} + +// `expr as Ident` +pub struct AsCast { +pub: + expr Expr // from expr: `expr` in `expr as Ident` + typ Type // to type + pos token.Position +pub mut: + expr_type Type // from type +} + +// an enum value, like OS.macos or .macos +pub struct EnumVal { +pub: + enum_name string + val string + mod string // for full path `mod_Enum_val` + pos token.Position +pub mut: + typ Type +} + +// enum field in enum declaration +pub struct EnumField { +pub: + name string + pos token.Position + comments []Comment // comment after Enumfield in the same line + next_comments []Comment // comments between current EnumField and next EnumField + expr Expr // the value of current EnumField; 123 in `ename = 123` + has_expr bool // true, when .expr has a value +} + +// enum declaration +pub struct EnumDecl { +pub: + name string + is_pub bool + is_flag bool // true when the enum has [flag] tag,for bit field enum + is_multi_allowed bool // true when the enum has [_allow_multiple_values] tag + comments []Comment // comments before the first EnumField + fields []EnumField // all the enum fields + attrs []Attr // attributes of enum declaration + pos token.Position +} + +pub struct AliasTypeDecl { +pub: + name string + is_pub bool + parent_type Type + pos token.Position + type_pos token.Position + comments []Comment +} + +// New implementation of sum types +pub struct SumTypeDecl { +pub: + name string + is_pub bool + pos token.Position + comments []Comment + typ Type + generic_types []Type +pub mut: + variants []TypeNode +} + +pub struct FnTypeDecl { +pub: + name string + is_pub bool + typ Type + pos token.Position + type_pos token.Position + comments []Comment +} + +// TODO: handle this differently +// v1 excludes non current os ifdefs so +// the defer's never get added in the first place +pub struct DeferStmt { +pub: + stmts []Stmt + pos token.Position +pub mut: + defer_vars []Ident + ifdef string + idx_in_fn int = -1 // index in FnDecl.defer_stmts +} + +// `(3+4)` +pub struct ParExpr { +pub: + expr Expr + pos token.Position +} + +pub struct GoExpr { +pub: + pos token.Position +pub mut: + call_expr CallExpr + is_expr bool +} + +pub struct GotoLabel { +pub: + name string + pos token.Position +} + +pub struct GotoStmt { +pub: + name string + pos token.Position +} + +pub struct ArrayInit { +pub: + pos token.Position // `[]` in []Type{} position + elem_type_pos token.Position // `Type` in []Type{} position + exprs []Expr // `[expr, expr]` or `[expr]Type{}` for fixed array + ecmnts [][]Comment // optional iembed comments after each expr + pre_cmnts []Comment + is_fixed bool + has_val bool // fixed size literal `[expr, expr]!` + mod string + len_expr Expr // len: expr + cap_expr Expr // cap: expr + default_expr Expr // init: expr + has_len bool + has_cap bool + has_default bool +pub mut: + expr_types []Type // [Dog, Cat] // also used for interface_types + elem_type Type // element type + typ Type // array type +} + +pub struct ArrayDecompose { +pub: + expr Expr + pos token.Position +pub mut: + expr_type Type + arg_type Type +} + +pub struct ChanInit { +pub: + pos token.Position + cap_expr Expr + has_cap bool +pub mut: + typ Type + elem_type Type +} + +pub struct MapInit { +pub: + pos token.Position + keys []Expr + vals []Expr + comments [][]Comment // comments after key-value pairs + pre_cmnts []Comment // comments before the first key-value pair +pub mut: + typ Type + key_type Type + value_type Type +} + +// s[10..20] +pub struct RangeExpr { +pub: + low Expr + high Expr + has_high bool + has_low bool + pos token.Position +} + +// NB: &string(x) gets parsed as PrefixExpr{ right: CastExpr{...} } +// TODO: that is very likely a parsing bug. It should get parsed as just +// CastExpr{...}, where .typname is '&string' instead. +// The current situation leads to special cases in vfmt and cgen +// (see prefix_expr_cast_expr in fmt.v, and .is_amp in cgen.v) +// .in_prexpr is also needed because of that, because the checker needs to +// show warnings about the deprecated C->V conversions `string(x)` and +// `string(x,y)`, while skipping the real pointer casts like `&string(x)`. +// 2021/07/17: TODO: since 6edfb2c, the above is fixed at the parser level, +// we need to remove the hacks/special cases in vfmt and the checker too. +pub struct CastExpr { +pub: + arg Expr // `n` in `string(buf, n)` +pub mut: + typ Type // `string` + expr Expr // `buf` in `string(buf, n)` and `&Type(buf)` + typname string // `&Type` in `&Type(buf)` + expr_type Type // `byteptr`, the type of the `buf` expression + has_arg bool // true for `string(buf, n)`, false for `&Type(buf)` + pos token.Position +} + +pub struct AsmStmt { +pub: + arch pref.Arch + is_basic bool + is_volatile bool + is_goto bool + clobbered []AsmClobbered + pos token.Position +pub mut: + templates []AsmTemplate + scope &Scope + output []AsmIO + input []AsmIO + global_labels []string // labels defined in assembly block, exported with `.globl` + local_labels []string // local to the assembly block +} + +pub struct AsmTemplate { +pub mut: + name string + is_label bool // `example_label:` + is_directive bool // .globl assembly_function + args []AsmArg + comments []Comment + pos token.Position +} + +// [eax+5] | j | displacement literal (e.g. 123 in [rax + 123] ) | eax | true | `a` | 0.594 | 123 | label_name +pub type AsmArg = AsmAddressing | AsmAlias | AsmDisp | AsmRegister | BoolLiteral | CharLiteral | + FloatLiteral | IntegerLiteral | string + +pub struct AsmRegister { +pub mut: + name string // eax or r12d etc. + typ Type + size int +} + +pub struct AsmDisp { +pub: + val string + pos token.Position +} + +pub struct AsmAlias { +pub: + pos token.Position +pub mut: + name string // a +} + +pub struct AsmAddressing { +pub: + scale int = -1 // 1, 2, 4, or 8 literal + mode AddressingMode + pos token.Position +pub mut: + displacement AsmArg // 8, 16 or 32 bit literal value + base AsmArg // gpr + index AsmArg // gpr +} + +// adressing modes: +pub enum AddressingMode { + invalid + displacement // displacement + base // base + base_plus_displacement // base + displacement + index_times_scale_plus_displacement // (index ∗ scale) + displacement + base_plus_index_plus_displacement // base + (index ∗ scale) + displacement + base_plus_index_times_scale_plus_displacement // base + index + displacement + rip_plus_displacement // rip + displacement +} + +pub struct AsmClobbered { +pub mut: + reg AsmRegister + comments []Comment +} + +// : [alias_a] '=r' (a) // this is a comment +pub struct AsmIO { +pub: + alias string // [alias_a] + constraint string // '=r' TODO: allow all backends to easily use this with a struct + expr Expr // (a) + comments []Comment // // this is a comment + typ Type + pos token.Position +} + +pub const ( + // reference: https://en.wikipedia.org/wiki/X86#/media/File:Table_of_x86_Registers_svg.svg + // map register size -> register name + x86_no_number_register_list = map{ + 8: ['al', 'ah', 'bl', 'bh', 'cl', 'ch', 'dl', 'dh', 'bpl', 'sil', 'dil', 'spl'] + 16: ['ax', 'bx', 'cx', 'dx', 'bp', 'si', 'di', 'sp', /* segment registers */ 'cs', 'ss', + 'ds', 'es', 'fs', 'gs', 'flags', 'ip', /* task registers */ 'gdtr', 'idtr', 'tr', 'ldtr', + // CSR register 'msw', /* FP core registers */ 'cw', 'sw', 'tw', 'fp_ip', 'fp_dp', + 'fp_cs', 'fp_ds', 'fp_opc'] + 32: [ + 'eax', + 'ebx', + 'ecx', + 'edx', + 'ebp', + 'esi', + 'edi', + 'esp', + 'eflags', + 'eip', /* CSR register */ + 'mxcsr' /* 32-bit FP core registers 'fp_dp', 'fp_ip' (TODO: why are there duplicates?) */, + ] + 64: ['rax', 'rbx', 'rcx', 'rdx', 'rbp', 'rsi', 'rdi', 'rsp', 'rflags', 'rip'] + } + // no comments because maps do not support comments + // r#*: gp registers added in 64-bit extensions, can only be from 8-15 actually + // *mm#: vector/simd registors + // st#: floating point numbers + // cr#: control/status registers + // dr#: debug registers + x86_with_number_register_list = map{ + 8: map{ + 'r#b': 16 + } + 16: map{ + 'r#w': 16 + } + 32: map{ + 'r#d': 16 + } + 64: map{ + 'r#': 16 + 'mm#': 16 + 'cr#': 16 + 'dr#': 16 + } + 80: map{ + 'st#': 16 + } + 128: map{ + 'xmm#': 32 + } + 256: map{ + 'ymm#': 32 + } + 512: map{ + 'zmm#': 32 + } + } +) + +// TODO: saved priviled registers for arm +pub const ( + arm_no_number_register_list = ['fp' /* aka r11 */, /* not instruction pointer: */ 'ip' /* aka r12 */, + 'sp' /* aka r13 */, 'lr' /* aka r14 */, /* this is instruction pointer ('program counter'): */ + 'pc' /* aka r15 */, + ] // 'cpsr' and 'apsr' are special flags registers, but cannot be referred to directly + arm_with_number_register_list = map{ + 'r#': 16 + } +) + +pub const ( + riscv_no_number_register_list = ['zero', 'ra', 'sp', 'gp', 'tp'] + riscv_with_number_register_list = map{ + 'x#': 32 + 't#': 3 + 's#': 12 + 'a#': 8 + } +) + +pub struct AssertStmt { +pub: + pos token.Position +pub mut: + expr Expr + is_used bool // asserts are used in _test.v files, as well as in non -prod builds of all files +} + +// `if [x := opt()] {` +pub struct IfGuardExpr { +pub: + var_name string + pos token.Position +pub mut: + expr Expr + expr_type Type +} + +pub enum OrKind { + absent + block + propagate +} + +// `or { ... }` +pub struct OrExpr { +pub: + stmts []Stmt + kind OrKind + pos token.Position +} + +/* +// `or { ... }` +pub struct OrExpr2 { +pub: + call_expr CallExpr + stmts []Stmt // inside `or { }` + kind OrKind + pos token.Position +} +*/ + +// deprecated +pub struct Assoc { +pub: + var_name string + fields []string + exprs []Expr + pos token.Position +pub mut: + typ Type + scope &Scope +} + +pub struct SizeOf { +pub: + is_type bool + expr Expr // checker uses this to set typ + pos token.Position +pub mut: + typ Type +} + +pub struct IsRefType { +pub: + is_type bool + expr Expr // checker uses this to set typ + pos token.Position +pub mut: + typ Type +} + +pub struct OffsetOf { +pub: + struct_type Type + field string + pos token.Position +} + +pub struct Likely { +pub: + expr Expr + pos token.Position + is_likely bool // false for _unlikely_ +} + +pub struct TypeOf { +pub: + expr Expr + pos token.Position +pub mut: + expr_type Type +} + +pub struct DumpExpr { +pub: + expr Expr + pos token.Position +pub mut: + expr_type Type + cname string // filled in the checker +} + +pub struct Comment { +pub: + text string + is_multi bool // true only for /* comment */, that use many lines + is_inline bool // true for all /* comment */ comments + pos token.Position +} + +pub struct ConcatExpr { +pub: + vals []Expr + pos token.Position +pub mut: + return_type Type +} + +// @FN, @STRUCT, @MOD etc. See full list in token.valid_at_tokens +pub struct AtExpr { +pub: + name string + pos token.Position + kind token.AtKind +pub mut: + val string +} + +pub struct ComptimeSelector { +pub: + has_parens bool // if $() is used, for vfmt + left Expr + field_expr Expr + pos token.Position +pub mut: + left_type Type + typ Type +} + +pub struct ComptimeCall { +pub: + pos token.Position + has_parens bool // if $() is used, for vfmt + method_name string + method_pos token.Position + scope &Scope + left Expr + args_var string + // + is_vweb bool + vweb_tmpl File + // + is_embed bool + embed_file EmbeddedFile + // + is_env bool + env_pos token.Position + // + is_pkgconfig bool +pub mut: + sym TypeSymbol + result_type Type + env_value string + args []CallArg +} + +pub struct None { +pub: + pos token.Position +} + +pub enum SqlStmtKind { + insert + update + delete + create + drop +} + +pub struct SqlStmt { +pub: + pos token.Position + db_expr Expr // `db` in `sql db {` +pub mut: + lines []SqlStmtLine +} + +pub struct SqlStmtLine { +pub: + kind SqlStmtKind + pos token.Position + where_expr Expr + update_exprs []Expr // for `update` +pub mut: + object_var_name string // `user` + updated_columns []string // for `update set x=y` + table_expr TypeNode + fields []StructField + sub_structs map[int]SqlStmtLine +} + +pub struct SqlExpr { +pub: + typ Type + is_count bool + db_expr Expr // `db` in `sql db {` + has_where bool + has_offset bool + offset_expr Expr + has_order bool + order_expr Expr + has_desc bool + is_array bool + pos token.Position + has_limit bool + limit_expr Expr +pub mut: + where_expr Expr + table_expr TypeNode + fields []StructField + sub_structs map[int]SqlExpr +} + +pub struct NodeError { +pub: + idx int // index for referencing the related File error + pos token.Position +} + +[inline] +pub fn (expr Expr) is_blank_ident() bool { + match expr { + Ident { return expr.kind == .blank_ident } + else { return false } + } +} + +pub fn (expr Expr) position() token.Position { + // all uncommented have to be implemented + // NB: please do not print here. the language server will hang + // as it uses STDIO primarly to communicate ~Ned + match expr { + AnonFn { + return expr.decl.pos + } + EmptyExpr { + // println('compiler bug, unhandled EmptyExpr position()') + return token.Position{} + } + NodeError, ArrayDecompose, ArrayInit, AsCast, Assoc, AtExpr, BoolLiteral, CallExpr, + CastExpr, ChanInit, CharLiteral, ConcatExpr, Comment, ComptimeCall, ComptimeSelector, + EnumVal, DumpExpr, FloatLiteral, GoExpr, Ident, IfExpr, IntegerLiteral, IsRefType, Likely, + LockExpr, MapInit, MatchExpr, None, OffsetOf, OrExpr, ParExpr, PostfixExpr, PrefixExpr, + RangeExpr, SelectExpr, SelectorExpr, SizeOf, SqlExpr, StringInterLiteral, StringLiteral, + StructInit, TypeNode, TypeOf, UnsafeExpr { + return expr.pos + } + IndexExpr { + if expr.or_expr.kind != .absent { + return expr.or_expr.pos + } + return expr.pos + } + IfGuardExpr { + return expr.expr.position() + } + InfixExpr { + left_pos := expr.left.position() + right_pos := expr.right.position() + return token.Position{ + line_nr: expr.pos.line_nr + pos: left_pos.pos + len: right_pos.pos - left_pos.pos + right_pos.len + col: left_pos.col + last_line: right_pos.last_line + } + } + CTempVar { + return token.Position{} + } + // Please, do NOT use else{} here. + // This match is exhaustive *on purpose*, to help force + // maintaining/implementing proper .pos fields. + } +} + +pub fn (expr Expr) is_lvalue() bool { + match expr { + Ident { return true } + CTempVar { return true } + IndexExpr { return expr.left.is_lvalue() } + SelectorExpr { return expr.expr.is_lvalue() } + ParExpr { return expr.expr.is_lvalue() } // for var := &{...(*pointer_var)} + PrefixExpr { return expr.right.is_lvalue() } + else {} + } + return false +} + +pub fn (expr Expr) is_expr() bool { + match expr { + IfExpr { return expr.is_expr } + LockExpr { return expr.is_expr } + MatchExpr { return expr.is_expr } + SelectExpr { return expr.is_expr } + else {} + } + return true +} + +pub fn (expr Expr) is_lit() bool { + return match expr { + BoolLiteral, CharLiteral, StringLiteral, IntegerLiteral { true } + else { false } + } +} + +pub fn (expr Expr) is_auto_deref_var() bool { + match expr { + Ident { + if expr.obj is Var { + if expr.obj.is_auto_deref { + return true + } + } + } + PrefixExpr { + if expr.op == .amp && expr.right.is_auto_deref_var() { + return true + } + } + else {} + } + return false +} + +// returns if an expression can be used in `lock x, y.z {` +pub fn (e &Expr) is_lockable() bool { + match e { + Ident { + return true + } + SelectorExpr { + return e.expr.is_lockable() + } + else { + return false + } + } +} + +// check if stmt can be an expression in C +pub fn (stmt Stmt) check_c_expr() ? { + match stmt { + AssignStmt { + return + } + ExprStmt { + if stmt.expr.is_expr() { + return + } + return error('unsupported statement (`$stmt.expr.type_name()`)') + } + else {} + } + return error('unsupported statement (`$stmt.type_name()`)') +} + +// CTempVar is used in cgen only, to hold nodes for temporary variables +pub struct CTempVar { +pub: + name string // the name of the C temporary variable; used by g.expr(x) + orig Expr // the original expression, which produced the C temp variable; used by x.str() + typ Type // the type of the original expression + is_ptr bool // whether the type is a pointer +} + +pub fn (node Node) position() token.Position { + match node { + NodeError { + return token.Position{} + } + EmptyNode { + return token.Position{} + } + Stmt { + mut pos := node.pos + if node is Import { + for sym in node.syms { + pos = pos.extend(sym.pos) + } + } else if node is TypeDecl { + match node { + FnTypeDecl, AliasTypeDecl { + pos = pos.extend(node.type_pos) + } + SumTypeDecl { + for variant in node.variants { + pos = pos.extend(variant.pos) + } + } + } + } + if node is AssignStmt { + return pos.extend(node.right.last().position()) + } + if node is AssertStmt { + return pos.extend(node.expr.position()) + } + return pos + } + Expr { + return node.position() + } + StructField { + return node.pos.extend(node.type_pos) + } + MatchBranch, SelectBranch, EnumField, ConstField, StructInitField, GlobalField, CallArg { + return node.pos + } + Param { + return node.pos.extend(node.type_pos) + } + IfBranch { + return node.pos.extend(node.body_pos) + } + ScopeObject { + match node { + ConstField, GlobalField, Var { + return node.pos + } + AsmRegister { + return token.Position{ + len: -1 + line_nr: -1 + pos: -1 + last_line: -1 + col: -1 + } + } + } + } + File { + mut pos := token.Position{} + if node.stmts.len > 0 { + first_pos := node.stmts.first().pos + last_pos := node.stmts.last().pos + pos = first_pos.extend_with_last_line(last_pos, last_pos.line_nr) + } + return pos + } + } +} + +pub fn (node Node) children() []Node { + mut children := []Node{} + if node is Expr { + match node { + StringInterLiteral, Assoc, ArrayInit { + return node.exprs.map(Node(it)) + } + SelectorExpr, PostfixExpr, UnsafeExpr, AsCast, ParExpr, IfGuardExpr, SizeOf, Likely, + TypeOf, ArrayDecompose { + children << node.expr + } + LockExpr, OrExpr { + return node.stmts.map(Node(it)) + } + StructInit { + return node.fields.map(Node(it)) + } + AnonFn { + children << Stmt(node.decl) + } + CallExpr { + children << node.left + children << node.args.map(Node(it)) + children << Expr(node.or_block) + } + InfixExpr { + children << node.left + children << node.right + } + PrefixExpr { + children << node.right + } + IndexExpr { + children << node.left + children << node.index + } + IfExpr { + children << node.left + children << node.branches.map(Node(it)) + } + MatchExpr { + children << node.cond + children << node.branches.map(Node(it)) + } + SelectExpr { + return node.branches.map(Node(it)) + } + ChanInit { + children << node.cap_expr + } + MapInit { + children << node.keys.map(Node(it)) + children << node.vals.map(Node(it)) + } + RangeExpr { + children << node.low + children << node.high + } + CastExpr { + children << node.expr + children << node.arg + } + ConcatExpr { + return node.vals.map(Node(it)) + } + ComptimeCall, ComptimeSelector { + children << node.left + } + else {} + } + } else if node is Stmt { + match node { + Block, DeferStmt, ForCStmt, ForInStmt, ForStmt, CompFor { + return node.stmts.map(Node(it)) + } + ExprStmt, AssertStmt { + children << node.expr + } + InterfaceDecl { + children << node.methods.map(Node(Stmt(it))) + children << node.fields.map(Node(it)) + } + AssignStmt { + children << node.left.map(Node(it)) + children << node.right.map(Node(it)) + } + Return { + return node.exprs.map(Node(it)) + } + // NB: these four decl nodes cannot be merged as one branch + StructDecl { + return node.fields.map(Node(it)) + } + GlobalDecl { + return node.fields.map(Node(it)) + } + ConstDecl { + return node.fields.map(Node(it)) + } + EnumDecl { + return node.fields.map(Node(it)) + } + FnDecl { + if node.is_method { + children << Node(node.receiver) + } + children << node.params.map(Node(it)) + children << node.stmts.map(Node(it)) + } + TypeDecl { + if node is SumTypeDecl { + children << node.variants.map(Node(Expr(it))) + } + } + else {} + } + } else if node is ScopeObject { + match node { + GlobalField, ConstField, Var { children << node.expr } + AsmRegister {} + } + } else { + match node { + GlobalField, ConstField, EnumField, StructInitField, CallArg { + children << node.expr + } + SelectBranch { + children << node.stmt + children << node.stmts.map(Node(it)) + } + IfBranch, File { + return node.stmts.map(Node(it)) + } + MatchBranch { + children << node.stmts.map(Node(it)) + children << node.exprs.map(Node(it)) + } + else {} + } + } + return children +} + +// helper for dealing with `m[k1][k2][k3][k3] = value` +pub fn (mut lx IndexExpr) recursive_mapset_is_setter(val bool) { + lx.is_setter = val + if mut lx.left is IndexExpr { + if lx.left.is_map { + lx.left.recursive_mapset_is_setter(val) + } + } +} + +// return all the registers for a give architecture +pub fn all_registers(mut t Table, arch pref.Arch) map[string]ScopeObject { + mut res := map[string]ScopeObject{} + match arch { + .amd64, .i386 { + for bit_size, array in ast.x86_no_number_register_list { + for name in array { + res[name] = AsmRegister{ + name: name + typ: t.bitsize_to_type(bit_size) + size: bit_size + } + } + } + for bit_size, array in ast.x86_with_number_register_list { + for name, max_num in array { + for i in 0 .. max_num { + hash_index := name.index('#') or { + panic('all_registers: no hashtag found') + } + assembled_name := '${name[..hash_index]}$i${name[hash_index + 1..]}' + res[assembled_name] = AsmRegister{ + name: assembled_name + typ: t.bitsize_to_type(bit_size) + size: bit_size + } + } + } + } + } + .arm32 { + arm32 := gen_all_registers(mut t, ast.arm_no_number_register_list, ast.arm_with_number_register_list, + 32) + for k, v in arm32 { + res[k] = v + } + } + .arm64 { + arm64 := gen_all_registers(mut t, ast.arm_no_number_register_list, ast.arm_with_number_register_list, + 64) + for k, v in arm64 { + res[k] = v + } + } + .rv32 { + rv32 := gen_all_registers(mut t, ast.riscv_no_number_register_list, ast.riscv_with_number_register_list, + 32) + for k, v in rv32 { + res[k] = v + } + } + .rv64 { + rv64 := gen_all_registers(mut t, ast.riscv_no_number_register_list, ast.riscv_with_number_register_list, + 64) + for k, v in rv64 { + res[k] = v + } + } + else { // TODO + panic('all_registers: unhandled arch') + } + } + + return res +} + +// only for arm and riscv because x86 has different sized registers +fn gen_all_registers(mut t Table, without_numbers []string, with_numbers map[string]int, bit_size int) map[string]ScopeObject { + mut res := map[string]ScopeObject{} + for name in without_numbers { + res[name] = AsmRegister{ + name: name + typ: t.bitsize_to_type(bit_size) + size: bit_size + } + } + for name, max_num in with_numbers { + for i in 0 .. max_num { + hash_index := name.index('#') or { panic('all_registers: no hashtag found') } + assembled_name := '${name[..hash_index]}$i${name[hash_index + 1..]}' + res[assembled_name] = AsmRegister{ + name: assembled_name + typ: t.bitsize_to_type(bit_size) + size: bit_size + } + } + } + return res +} + +// is `expr` a literal, i.e. it does not depend on any other declarations (C compile time constant) +pub fn (expr Expr) is_literal() bool { + match expr { + BoolLiteral, CharLiteral, FloatLiteral, IntegerLiteral { + return true + } + PrefixExpr { + return expr.right.is_literal() + } + InfixExpr { + return expr.left.is_literal() && expr.right.is_literal() + } + ParExpr { + return expr.expr.is_literal() + } + CastExpr { + return !expr.has_arg && expr.expr.is_literal() + && (expr.typ.is_ptr() || expr.typ.is_pointer() + || expr.typ in [i8_type, i16_type, int_type, i64_type, byte_type, u8_type, u16_type, u32_type, u64_type, f32_type, f64_type, char_type, bool_type, rune_type]) + } + SizeOf, IsRefType { + return expr.is_type || expr.expr.is_literal() + } + else { + return false + } + } +} diff --git a/v_windows/v/old/vlib/v/ast/attr.v b/v_windows/v/old/vlib/v/ast/attr.v new file mode 100644 index 0000000..f91cdb8 --- /dev/null +++ b/v_windows/v/old/vlib/v/ast/attr.v @@ -0,0 +1,62 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module ast + +import v.token + +pub enum AttrKind { + plain // [name] + string // ['name'] + number // [123] + comptime_define // [if name] +} + +// e.g. `[unsafe]` +pub struct Attr { +pub: + name string // [name] + has_arg bool + arg string // [name: arg] + kind AttrKind + ct_expr Expr // .kind == comptime_define, for [if !name] + ct_opt bool // true for [if user_defined_name?] + pos token.Position +pub mut: + ct_evaled bool // whether ct_skip has been evaluated already + ct_skip bool // is the comptime expr *false*, filled by checker +} + +pub fn (a Attr) debug() string { + return 'Attr{ name: "$a.name", has_arg: $a.has_arg, arg: "$a.arg", kind: $a.kind, ct_expr: $a.ct_expr, ct_opt: $a.ct_opt, ct_skip: $a.ct_skip}' +} + +// str returns the string representation without square brackets +pub fn (a Attr) str() string { + mut s := '' + mut arg := if a.has_arg { + s += '$a.name: ' + a.arg + } else { + a.name + } + s += match a.kind { + .plain, .number { arg } + .string { "'$arg'" } + .comptime_define { 'if $arg' } + } + return s +} + +pub fn (attrs []Attr) contains(str string) bool { + return attrs.any(it.name == str) +} + +pub fn (attrs []Attr) find_comptime_define() ?int { + for idx in 0 .. attrs.len { + if attrs[idx].kind == .comptime_define { + return idx + } + } + return none +} diff --git a/v_windows/v/old/vlib/v/ast/cflags.v b/v_windows/v/old/vlib/v/ast/cflags.v new file mode 100644 index 0000000..77b1555 --- /dev/null +++ b/v_windows/v/old/vlib/v/ast/cflags.v @@ -0,0 +1,89 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module ast + +import v.cflag + +// check if cflag is in table +fn (t &Table) has_cflag(flag cflag.CFlag) bool { + for cf in t.cflags { + if cf.os == flag.os && cf.name == flag.name && cf.value == flag.value { + return true + } + } + return false +} + +// parse the flags to (ast.cflags) []CFlag +// Note: clean up big time (joe-c) +pub fn (mut t Table) parse_cflag(cflg string, mod string, ctimedefines []string) ?bool { + allowed_flags := ['framework', 'library', 'Wa', 'Wl', 'Wp', 'I', 'l', 'L'] + flag_orig := cflg.trim_space() + mut flag := flag_orig + if flag == '' { + return none + } + mut fos := '' + mut allowed_os_overrides := ['linux', 'darwin', 'freebsd', 'windows', 'mingw', 'solaris'] + allowed_os_overrides << ctimedefines + for os_override in allowed_os_overrides { + if !flag.starts_with(os_override) { + continue + } + pos := flag.index(' ') or { return none } + fos = flag[..pos].trim_space() + flag = flag[pos..].trim_space() + } + for { + mut name := '' + mut value := '' + if flag[0] == `-` { + for f in allowed_flags { + i := 1 + f.len + if i <= flag.len && f == flag[1..i] { + name = flag[..i].trim_space() + flag = flag[i..].trim_space() + break + } + } + } + mut index := flag.index(' -') or { -1 } + for index > -1 { + mut has_next := false + for f in allowed_flags { + i := index + 2 + f.len + if i <= flag.len && f == flag[index + 2..i] { + value = flag[..index + 1].trim_space() + flag = flag[index + 1..].trim_space() + has_next = true + break + } + } + if has_next { + break + } + index = flag.index_after(' -', index + 1) + } + if index == -1 { + value = flag.trim_space() + } + if (name in ['-I', '-l', '-L']) && value == '' { + hint := if name == '-l' { 'library name' } else { 'path' } + return error('bad #flag `$flag_orig`: missing $hint after `$name`') + } + cf := cflag.CFlag{ + mod: mod + os: fos + name: name + value: value + } + if !t.has_cflag(cf) { + t.cflags << cf + } + if index == -1 { + break + } + } + return true +} diff --git a/v_windows/v/old/vlib/v/ast/cflags_test.v b/v_windows/v/old/vlib/v/ast/cflags_test.v new file mode 100644 index 0000000..a64fe8c --- /dev/null +++ b/v_windows/v/old/vlib/v/ast/cflags_test.v @@ -0,0 +1,72 @@ +import v.ast +import v.cflag + +const ( + module_name = 'main' + cdefines = []string{} + no_name = '' + no_flag = '' + no_os = '' +) + +fn test_parse_valid_cflags() { + mut t := ast.new_table() + expected_flags := [ + make_flag('freebsd', '-I', '/usr/local/include/freetype2'), + make_flag('linux', '-l', 'glfw'), + make_flag('mingw', no_name, '-mwindows'), + make_flag('solaris', '-L', '/opt/local/lib'), + make_flag('darwin', '-framework', 'Cocoa'), + make_flag('windows', '-l', 'gdi32'), + make_flag(no_os, '-l', 'mysqlclient'), + make_flag(no_os, no_name, '-test'), + ] + parse_valid_flag(mut t, '-lmysqlclient') + parse_valid_flag(mut t, '-test') + parse_valid_flag(mut t, 'darwin -framework Cocoa') + parse_valid_flag(mut t, 'freebsd -I/usr/local/include/freetype2') + parse_valid_flag(mut t, 'linux -lglfw') + parse_valid_flag(mut t, 'mingw -mwindows') + parse_valid_flag(mut t, 'solaris -L/opt/local/lib') + parse_valid_flag(mut t, 'windows -lgdi32') + assert t.cflags.len == expected_flags.len + for f in expected_flags { + assert t.has_cflag(f) + } +} + +fn test_parse_invalid_cflags() { + mut t := ast.new_table() + // -I, -L, -l must have values + assert_parse_invalid_flag(mut t, 'windows -l') + assert_parse_invalid_flag(mut t, '-I') + assert_parse_invalid_flag(mut t, '-L') + // OS/compiler name only is not allowed + assert_parse_invalid_flag(mut t, 'darwin') + assert_parse_invalid_flag(mut t, 'freebsd') + assert_parse_invalid_flag(mut t, 'linux') + assert_parse_invalid_flag(mut t, 'mingw') + assert_parse_invalid_flag(mut t, 'solaris') + assert_parse_invalid_flag(mut t, 'windows') + // Empty flag is not allowed + assert_parse_invalid_flag(mut t, no_flag) + assert t.cflags.len == 0 +} + +fn parse_valid_flag(mut t ast.Table, flag string) { + t.parse_cflag(flag, module_name, cdefines) or {} +} + +fn assert_parse_invalid_flag(mut t ast.Table, flag string) { + t.parse_cflag(flag, module_name, cdefines) or { return } + assert false +} + +fn make_flag(os string, name string, value string) cflag.CFlag { + return cflag.CFlag{ + mod: module_name + os: os + name: name + value: value + } +} diff --git a/v_windows/v/old/vlib/v/ast/comptime_const_values.v b/v_windows/v/old/vlib/v/ast/comptime_const_values.v new file mode 100644 index 0000000..6f9ef6a --- /dev/null +++ b/v_windows/v/old/vlib/v/ast/comptime_const_values.v @@ -0,0 +1,271 @@ +module ast + +pub type ComptTimeConstValue = EmptyExpr | byte | f32 | f64 | i16 | i64 | i8 | int | rune | + string | u16 | u32 | u64 + +pub fn empty_comptime_const_expr() ComptTimeConstValue { + return EmptyExpr{} +} + +pub fn (val ComptTimeConstValue) i8() ?i8 { + x := val.i64() ? + if x > -129 && x < 128 { + return i8(x) + } + return none +} + +pub fn (val ComptTimeConstValue) i16() ?i16 { + x := val.i64() ? + if x > -32769 && x < 32768 { + return i16(x) + } + return none +} + +pub fn (val ComptTimeConstValue) int() ?int { + x := val.i64() ? + if x > -2147483649 && x < 2147483648 { + return int(x) + } + return none +} + +pub fn (val ComptTimeConstValue) i64() ?i64 { + match val { + i8 { + return i64(val) + } + i16 { + return i64(val) + } + int { + return i64(val) + } + i64 { + return i64(val) + } + // + byte { + return i64(val) + } + u16 { + return i64(val) + } + u32 { + return i64(val) + } + u64 { + if val <= 9223372036854775807 { + return i64(val) + } + } + // + f32 { + if -9223372036854775808.0 <= val && val <= 9223372036854775807.0 { + return i64(val) + } + } + f64 { + if -9223372036854775808.0 <= val && val <= 9223372036854775807.0 { + return i64(val) + } + } + // + string { + return val.i64() + } + rune { + return int(val) + } + EmptyExpr {} + } + return none +} + +pub fn (val ComptTimeConstValue) byte() ?byte { + x := val.u64() ? + if x < 256 { + return byte(x) + } + return none +} + +pub fn (val ComptTimeConstValue) u16() ?u16 { + x := val.u64() ? + if x < 65536 { + return u16(x) + } + return none +} + +pub fn (val ComptTimeConstValue) u32() ?u32 { + x := val.u64() ? + if x < 4294967296 { + return u32(x) + } + return none +} + +pub fn (val ComptTimeConstValue) u64() ?u64 { + match val { + i8 { + if val >= 0 { + return u64(val) + } + } + i16 { + if val >= 0 { + return u64(val) + } + } + int { + if val >= 0 { + return u64(val) + } + } + i64 { + if val >= 0 { + return u64(val) + } + } + byte { + return u64(val) + } + u16 { + return u64(val) + } + u32 { + return u64(val) + } + u64 { + return val + } + f32 { + if val <= 18446744073709551615.0 { + return u64(val) + } + } + f64 { + if val <= 18446744073709551615.0 { + return u64(val) + } + } + string { + return val.u64() + } + rune {} + EmptyExpr {} + } + return none +} + +pub fn (val ComptTimeConstValue) f32() ?f32 { + x := val.f64() ? + return f32(x) +} + +pub fn (val ComptTimeConstValue) f64() ?f64 { + match val { + i8 { + return f64(val) + } + i16 { + return f64(val) + } + int { + return f64(val) + } + i64 { + return f64(val) + } + byte { + return f64(val) + } + u16 { + return f64(val) + } + u32 { + return f64(val) + } + u64 { + return f64(val) + } + f32 { + return f64(val) + } + f64 { + return val + } + string { + return val.f64() + } + rune {} + EmptyExpr {} + } + return none +} + +pub fn (val ComptTimeConstValue) string() ?string { + match val { + i8 { + return val.str() + } + i16 { + return val.str() + } + int { + return val.str() + } + i64 { + return val.str() + } + byte { + return val.str() + } + u16 { + return val.str() + } + u32 { + return val.str() + } + u64 { + return val.str() + } + f32 { + return val.str() + } + f64 { + return val.str() + } + rune { + return val.str() + } + string { + return val + } + EmptyExpr {} + } + return none +} + +pub fn (obj ConstField) comptime_expr_value() ?ComptTimeConstValue { + if obj.comptime_expr_value !is EmptyExpr { + return obj.comptime_expr_value + } + return none +} + +pub fn (obj ConstField) is_simple_define_const() bool { + return match obj.expr { + CharLiteral, FloatLiteral, IntegerLiteral { true } + else { false } + } +} + +pub fn (obj ScopeObject) is_simple_define_const() bool { + if obj is ConstField { + return obj.is_simple_define_const() + } + return false +} diff --git a/v_windows/v/old/vlib/v/ast/init.v b/v_windows/v/old/vlib/v/ast/init.v new file mode 100644 index 0000000..4bda1ac --- /dev/null +++ b/v_windows/v/old/vlib/v/ast/init.v @@ -0,0 +1,70 @@ +module ast + +pub fn resolve_init(node StructInit, typ Type, t &Table) Expr { + type_sym := t.get_type_symbol(typ) + if type_sym.kind == .array { + array_info := type_sym.info as Array + mut has_len := false + mut has_cap := false + mut has_default := false + mut len_expr := empty_expr() + mut cap_expr := empty_expr() + mut default_expr := empty_expr() + mut exprs := []Expr{} + for field in node.fields { + match field.name { + 'len' { + has_len = true + len_expr = field.expr + } + 'cap' { + has_cap = true + len_expr = field.expr + } + 'default' { + has_default = true + len_expr = field.expr + } + else { + exprs << field.expr + } + } + } + return ArrayInit{ + // TODO: mod is not being set for now, we could need this in future + // mod: mod + pos: node.pos + typ: typ + elem_type: array_info.elem_type + has_len: has_len + has_cap: has_cap + has_default: has_default + len_expr: len_expr + cap_expr: cap_expr + default_expr: default_expr + exprs: exprs + } + } else if type_sym.kind == .map { + map_info := type_sym.info as Map + mut keys := []Expr{} + mut vals := []Expr{} + for field in node.fields { + keys << StringLiteral{ + val: field.name + } + vals << field.expr + } + return MapInit{ + typ: typ + key_type: map_info.key_type + value_type: map_info.value_type + keys: keys + vals: vals + } + } + // struct / other (sumtype?) + return StructInit{ + ...node + unresolved: false + } +} diff --git a/v_windows/v/old/vlib/v/ast/scope.v b/v_windows/v/old/vlib/v/ast/scope.v new file mode 100644 index 0000000..a02460c --- /dev/null +++ b/v_windows/v/old/vlib/v/ast/scope.v @@ -0,0 +1,241 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module ast + +[heap] +pub struct Scope { +pub mut: + // mut: + objects map[string]ScopeObject + struct_fields map[string]ScopeStructField + parent &Scope + detached_from_parent bool + children []&Scope + start_pos int + end_pos int +} + +[unsafe] +pub fn (s &Scope) free() { + unsafe { + s.objects.free() + s.struct_fields.free() + for child in s.children { + child.free() + } + s.children.free() + } +} + +/* +pub fn new_scope(parent &Scope, start_pos int) &Scope { + return &Scope{ + parent: parent + start_pos: start_pos + } +} +*/ + +fn (s &Scope) dont_lookup_parent() bool { + return isnil(s.parent) || s.detached_from_parent +} + +pub fn (s &Scope) find_with_scope(name string) ?(ScopeObject, &Scope) { + mut sc := s + for { + if name in sc.objects { + return sc.objects[name], sc + } + if sc.dont_lookup_parent() { + break + } + sc = sc.parent + } + return none +} + +pub fn (s &Scope) find(name string) ?ScopeObject { + for sc := s; true; sc = sc.parent { + if name in sc.objects { + return sc.objects[name] + } + if sc.dont_lookup_parent() { + break + } + } + return none +} + +// selector_expr: name.field_name +pub fn (s &Scope) find_struct_field(name string, struct_type Type, field_name string) ?ScopeStructField { + for sc := s; true; sc = sc.parent { + if field := sc.struct_fields[name] { + if field.struct_type == struct_type && field.name == field_name { + return field + } + } + if sc.dont_lookup_parent() { + break + } + } + return none +} + +pub fn (s &Scope) is_known(name string) bool { + if _ := s.find(name) { + return true + } else { + } + return false +} + +pub fn (s &Scope) find_var(name string) ?&Var { + if obj := s.find(name) { + match obj { + Var { return &obj } + else {} + } + } + return none +} + +pub fn (s &Scope) find_global(name string) ?&GlobalField { + if obj := s.find(name) { + match obj { + GlobalField { return &obj } + else {} + } + } + return none +} + +pub fn (s &Scope) find_const(name string) ?&ConstField { + if obj := s.find(name) { + match obj { + ConstField { return &obj } + else {} + } + } + return none +} + +pub fn (s &Scope) known_var(name string) bool { + if _ := s.find_var(name) { + return true + } + return false +} + +pub fn (mut s Scope) update_var_type(name string, typ Type) { + s.end_pos = s.end_pos // TODO mut bug + mut obj := s.objects[name] + match mut obj { + Var { + if obj.typ == typ { + return + } + obj.typ = typ + } + else {} + } +} + +// selector_expr: name.field_name +pub fn (mut s Scope) register_struct_field(name string, field ScopeStructField) { + if f := s.struct_fields[name] { + if f.struct_type == field.struct_type && f.name == field.name { + return + } + } + s.struct_fields[name] = field +} + +pub fn (mut s Scope) register(obj ScopeObject) { + name := if obj is ConstField { + obj.name + } else if obj is GlobalField { + obj.name + } else { + (obj as Var).name + } + if name == '_' { + return + } + if name in s.objects { + // println('existing obect: $name') + return + } + s.objects[name] = obj +} + +pub fn (s &Scope) outermost() &Scope { + mut sc := s + for !sc.dont_lookup_parent() { + sc = sc.parent + } + return sc +} + +// returns the innermost scope containing pos +// pub fn (s &Scope) innermost(pos int) ?&Scope { +pub fn (s &Scope) innermost(pos int) &Scope { + if s.contains(pos) { + // binary search + mut first := 0 + mut last := s.children.len - 1 + mut middle := last / 2 + for first <= last { + // println('FIRST: $first, LAST: $last, LEN: $s.children.len-1') + s1 := s.children[middle] + if s1.end_pos < pos { + first = middle + 1 + } else if s1.contains(pos) { + return s1.innermost(pos) + } else { + last = middle - 1 + } + middle = (first + last) / 2 + if first > last { + break + } + } + return s + } + // return none + return s +} + +[inline] +pub fn (s &Scope) contains(pos int) bool { + return pos >= s.start_pos && pos <= s.end_pos +} + +pub fn (sc Scope) show(depth int, max_depth int) string { + mut out := '' + mut indent := '' + for _ in 0 .. depth * 4 { + indent += ' ' + } + out += '$indent# $sc.start_pos - $sc.end_pos\n' + for _, obj in sc.objects { + match obj { + ConstField { out += '$indent * const: $obj.name - $obj.typ\n' } + Var { out += '$indent * var: $obj.name - $obj.typ\n' } + else {} + } + } + for _, field in sc.struct_fields { + out += '$indent * struct_field: $field.struct_type $field.name - $field.typ\n' + } + if max_depth == 0 || depth < max_depth - 1 { + for i, _ in sc.children { + out += sc.children[i].show(depth + 1, max_depth) + } + } + return out +} + +pub fn (sc Scope) str() string { + return sc.show(0, 0) +} diff --git a/v_windows/v/old/vlib/v/ast/str.v b/v_windows/v/old/vlib/v/ast/str.v new file mode 100644 index 0000000..2e2510d --- /dev/null +++ b/v_windows/v/old/vlib/v/ast/str.v @@ -0,0 +1,577 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module ast + +import v.util +import strings + +pub fn (node &FnDecl) modname() string { + if node.mod != '' { + return node.mod + } + mut pamod := node.name.all_before_last('.') + if pamod == node.name.after('.') { + pamod = if node.is_builtin { 'builtin' } else { 'main' } + } + return pamod +} + +// fkey returns a unique name of the function/method. +// it is used in table.used_fns and v.markused. +pub fn (node &FnDecl) fkey() string { + if node.is_method { + return '${int(node.receiver.typ)}.$node.name' + } + return node.name +} + +pub fn (node &CallExpr) fkey() string { + if node.is_method { + return '${int(node.receiver_type)}.$node.name' + } + return node.name +} + +// These methods are used only by vfmt, vdoc, and for debugging. +pub fn (node &FnDecl) stringify(t &Table, cur_mod string, m2a map[string]string) string { + mut f := strings.new_builder(30) + if node.is_pub { + f.write_string('pub ') + } + mut receiver := '' + if node.is_method { + mut styp := util.no_cur_mod(t.type_to_code(node.receiver.typ.clear_flag(.shared_f)), + cur_mod) + m := if node.rec_mut { node.receiver.typ.share().str() + ' ' } else { '' } + if node.rec_mut { + styp = styp[1..] // remove & + } + styp = util.no_cur_mod(styp, cur_mod) + if node.params[0].is_auto_rec { + styp = styp.trim('&') + } + receiver = '($m$node.receiver.name $styp) ' + /* + sym := t.get_type_symbol(node.receiver.typ) + name := sym.name.after('.') + mut m := if node.rec_mut { 'mut ' } else { '' } + if !node.rec_mut && node.receiver.typ.is_ptr() { + m = '&' + } + receiver = '($node.receiver.name $m$name) ' + */ + } + mut name := if node.is_anon { '' } else { node.name } + if !node.is_anon && !node.is_method && node.language == .v { + name = node.name.all_after_last('.') + } + // mut name := if node.is_anon { '' } else { node.name.after_char(`.`) } + // if !node.is_method { + // if node.language == .c { + // name = 'C.$name' + // } else if node.language == .js { + // name = 'JS.$name' + // } + // } + f.write_string('fn $receiver$name') + if name in ['+', '-', '*', '/', '%', '<', '>', '==', '!=', '>=', '<='] { + f.write_string(' ') + } + mut add_para_types := true + if node.generic_names.len > 0 { + if node.is_method { + sym := t.get_type_symbol(node.params[0].typ) + if sym.info is Struct { + generic_names := sym.info.generic_types.map(t.get_type_symbol(it).name) + if generic_names == node.generic_names { + add_para_types = false + } + } + } + if add_para_types { + f.write_string('<') + for i, gname in node.generic_names { + is_last := i == node.generic_names.len - 1 + f.write_string(gname) + if !is_last { + f.write_string(', ') + } + } + f.write_string('>') + } + } + f.write_string('(') + for i, arg in node.params { + // skip receiver + // if (node.is_method || node.is_interface) && i == 0 { + if node.is_method && i == 0 { + continue + } + if arg.is_hidden { + continue + } + is_last_arg := i == node.params.len - 1 + is_type_only := arg.name == '' + should_add_type := true // is_last_arg || is_type_only || node.params[i + 1].typ != arg.typ || + // (node.is_variadic && i == node.params.len - 2) + if arg.is_mut { + f.write_string(arg.typ.share().str() + ' ') + } + f.write_string(arg.name) + mut s := t.type_to_str(arg.typ.clear_flag(.shared_f)) + if arg.is_mut { + // f.write_string(' mut') + if s.starts_with('&') { + s = s[1..] + } + } + s = util.no_cur_mod(s, cur_mod) + for mod, alias in m2a { + s = s.replace(mod, alias) + } + if should_add_type { + if !is_type_only { + f.write_string(' ') + } + if node.is_variadic && is_last_arg { + f.write_string('...') + } + f.write_string(s) + } + if !is_last_arg { + f.write_string(', ') + } + } + f.write_string(')') + if node.return_type != void_type { + mut rs := util.no_cur_mod(t.type_to_str(node.return_type), cur_mod) + for mod, alias in m2a { + rs = rs.replace(mod, alias) + } + f.write_string(' ' + rs) + } + return f.str() +} + +// Expressions in string interpolations may have to be put in braces if they +// are non-trivial, if they would interfere with the next character or if a +// format specification is given. In the latter case +// the format specifier must be appended, separated by a colon: +// '$z $z.b $z.c.x ${x[4]} ${z:8.3f} ${a:-20} ${a>b+2}' +// This method creates the format specifier (including the colon) or an empty +// string if none is needed and also returns (as bool) if the expression +// must be enclosed in braces. +pub fn (lit &StringInterLiteral) get_fspec_braces(i int) (string, bool) { + mut res := []string{} + needs_fspec := lit.need_fmts[i] || lit.pluss[i] + || (lit.fills[i] && lit.fwidths[i] >= 0) || lit.fwidths[i] != 0 + || lit.precisions[i] != 987698 + mut needs_braces := needs_fspec + sx := lit.exprs[i].str() + if sx.contains(r'"') || sx.contains(r"'") { + needs_braces = true + } + if !needs_braces { + if i + 1 < lit.vals.len && lit.vals[i + 1].len > 0 { + next_char := lit.vals[i + 1][0] + if util.is_func_char(next_char) || next_char == `.` || next_char == `(` { + needs_braces = true + } + } + } + if !needs_braces { + mut sub_expr := lit.exprs[i] + for { + match mut sub_expr { + Ident { + if sub_expr.name[0] == `@` { + needs_braces = true + } + break + } + CallExpr { + if sub_expr.args.len != 0 { + needs_braces = true + } else if sub_expr.left is CallExpr { + sub_expr = sub_expr.left + continue + } else if sub_expr.left is CastExpr || sub_expr.left is IndexExpr { + needs_braces = true + } + break + } + SelectorExpr { + if sub_expr.field_name[0] == `@` { + needs_braces = true + break + } + sub_expr = sub_expr.expr + continue + } + else { + needs_braces = true + break + } + } + } + } + if needs_fspec { + res << ':' + if lit.pluss[i] { + res << '+' + } + if lit.fills[i] && lit.fwidths[i] >= 0 { + res << '0' + } + if lit.fwidths[i] != 0 { + res << '${lit.fwidths[i]}' + } + if lit.precisions[i] != 987698 { + res << '.${lit.precisions[i]}' + } + if lit.need_fmts[i] { + res << '${lit.fmts[i]:c}' + } + } + return res.join(''), needs_braces +} + +// string representation of expr +pub fn (x Expr) str() string { + match x { + AnonFn { + return 'anon_fn' + } + DumpExpr { + return 'dump($x.expr.str())' + } + ArrayInit { + mut fields := []string{} + if x.has_len { + fields << 'len: $x.len_expr.str()' + } + if x.has_cap { + fields << 'cap: $x.cap_expr.str()' + } + if x.has_default { + fields << 'init: $x.default_expr.str()' + } + if fields.len > 0 { + return '[]T{${fields.join(', ')}}' + } else { + return x.exprs.str() + } + } + AsCast { + return '$x.expr.str() as ${global_table.type_to_str(x.typ)}' + } + AtExpr { + return '$x.val' + } + CTempVar { + return x.orig.str() + } + BoolLiteral { + return x.val.str() + } + CastExpr { + return '${x.typname}($x.expr.str())' + } + CallExpr { + sargs := args2str(x.args) + if x.is_method { + return '${x.left.str()}.${x.name}($sargs)' + } + if x.name.starts_with('${x.mod}.') { + return util.strip_main_name('${x.name}($sargs)') + } + if x.mod == '' && x.name == '' { + return x.left.str() + '($sargs)' + } + return '${x.mod}.${x.name}($sargs)' + } + CharLiteral { + return '`$x.val`' + } + Comment { + if x.is_multi { + lines := x.text.split_into_lines() + return '/* $lines.len lines comment */' + } else { + text := x.text.trim('\x01').trim_space() + return '´// $text´' + } + } + ComptimeSelector { + return '${x.left}.$$x.field_expr' + } + ConcatExpr { + return x.vals.map(it.str()).join(',') + } + EnumVal { + return '.$x.val' + } + FloatLiteral, IntegerLiteral { + return x.val + } + GoExpr { + return 'go $x.call_expr' + } + Ident { + return x.name + } + IfExpr { + mut parts := []string{} + dollar := if x.is_comptime { '$' } else { '' } + for i, branch in x.branches { + if i != 0 { + parts << ' } ${dollar}else ' + } + if i < x.branches.len - 1 || !x.has_else { + parts << ' ${dollar}if ' + branch.cond.str() + ' { ' + } + for stmt in branch.stmts { + parts << stmt.str() + } + } + parts << ' }' + return parts.join('') + } + IndexExpr { + return '$x.left.str()[$x.index.str()]' + } + InfixExpr { + return '$x.left.str() $x.op.str() $x.right.str()' + } + MapInit { + mut pairs := []string{} + for ik, kv in x.keys { + mv := x.vals[ik].str() + pairs << '$kv: $mv' + } + return 'map{ ${pairs.join(' ')} }' + } + ParExpr { + return '($x.expr)' + } + PostfixExpr { + if x.op == .question { + return '$x.expr ?' + } + return '$x.expr$x.op' + } + PrefixExpr { + return x.op.str() + x.right.str() + } + RangeExpr { + mut s := '..' + if x.has_low { + s = '$x.low ' + s + } + if x.has_high { + s = s + ' $x.high' + } + return s + } + SelectExpr { + return 'ast.SelectExpr' + } + SelectorExpr { + return '${x.expr.str()}.$x.field_name' + } + SizeOf { + if x.is_type { + return 'sizeof(${global_table.type_to_str(x.typ)})' + } + return 'sizeof($x.expr)' + } + OffsetOf { + return '__offsetof(${global_table.type_to_str(x.struct_type)}, $x.field)' + } + StringInterLiteral { + mut res := strings.new_builder(50) + res.write_string("'") + for i, val in x.vals { + res.write_string(val) + if i >= x.exprs.len { + break + } + res.write_string('$') + fspec_str, needs_braces := x.get_fspec_braces(i) + if needs_braces { + res.write_string('{') + res.write_string(x.exprs[i].str()) + res.write_string(fspec_str) + res.write_string('}') + } else { + res.write_string(x.exprs[i].str()) + } + } + res.write_string("'") + return res.str() + } + StringLiteral { + return "'$x.val'" + } + TypeNode { + return 'TypeNode($x.typ)' + } + TypeOf { + return 'typeof($x.expr.str())' + } + Likely { + return '_likely_($x.expr.str())' + } + UnsafeExpr { + return 'unsafe { $x.expr }' + } + None { + return 'none' + } + IsRefType { + return 'isreftype(' + if x.is_type { + global_table.type_to_str(x.typ) + } else { + x.expr.str() + } + ')' + } + IfGuardExpr { + return x.var_name + ' := ' + x.expr.str() + } + StructInit { + sname := global_table.get_type_symbol(x.typ).name + return '$sname{....}' + } + ArrayDecompose { + return 'ast.ArrayDecompose' + } + Assoc { + return 'ast.Assoc' + } + ChanInit { + return 'ast.ChanInit' + } + ComptimeCall { + return 'ast.ComptimeCall' + } + EmptyExpr { + return 'ast.EmptyExpr' + } + LockExpr { + return 'ast.LockExpr' + } + MatchExpr { + return 'ast.MatchExpr' + } + NodeError { + return 'ast.NodeError' + } + OrExpr { + return 'ast.OrExpr' + } + SqlExpr { + return 'ast.SqlExpr' + } + } + return '[unhandled expr type $x.type_name()]' +} + +pub fn (a CallArg) str() string { + if a.is_mut { + return 'mut $a.expr.str()' + } + return '$a.expr.str()' +} + +pub fn args2str(args []CallArg) string { + mut res := []string{} + for a in args { + res << a.str() + } + return res.join(', ') +} + +pub fn (node &BranchStmt) str() string { + mut s := '$node.kind' + if node.label.len > 0 { + s += ' $node.label' + } + return s +} + +pub fn (node Stmt) str() string { + match node { + AssertStmt { + return 'assert $node.expr' + } + AssignStmt { + mut out := '' + for i, left in node.left { + if left is Ident { + var_info := left.var_info() + if var_info.is_mut { + out += 'mut ' + } + } + out += left.str() + if i < node.left.len - 1 { + out += ',' + } + } + out += ' $node.op.str() ' + for i, val in node.right { + out += val.str() + if i < node.right.len - 1 { + out += ',' + } + } + return out + } + BranchStmt { + return node.str() + } + ConstDecl { + fields := node.fields.map(field_to_string) + return 'const (${fields.join(' ')})' + } + ExprStmt { + return node.expr.str() + } + FnDecl { + return 'fn ${node.name}( $node.params.len params ) { $node.stmts.len stmts }' + } + EnumDecl { + return 'enum $node.name { $node.fields.len fields }' + } + Module { + return 'module $node.name' + } + Import { + mut out := 'import $node.mod' + if node.alias.len > 0 { + out += ' as $node.alias' + } + return out + } + StructDecl { + return 'struct $node.name { $node.fields.len fields }' + } + else { + return '[unhandled stmt str type: $node.type_name() ]' + } + } +} + +fn field_to_string(f ConstField) string { + x := f.name.trim_prefix(f.mod + '.') + return '$x = $f.expr' +} + +pub fn (e CompForKind) str() string { + match e { + .methods { return 'methods' } + .fields { return 'fields' } + .attributes { return 'attributes' } + } +} diff --git a/v_windows/v/old/vlib/v/ast/table.v b/v_windows/v/old/vlib/v/ast/table.v new file mode 100644 index 0000000..f97238a --- /dev/null +++ b/v_windows/v/old/vlib/v/ast/table.v @@ -0,0 +1,1371 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module ast + +import v.cflag +import v.token +import v.util + +[heap] +pub struct Table { +pub mut: + type_symbols []TypeSymbol + type_idxs map[string]int + fns map[string]Fn + iface_types map[string][]Type + dumps map[int]string // needed for efficiently generating all _v_dump_expr_TNAME() functions + imports []string // List of all imports + modules []string // Topologically sorted list of all modules registered by the application + global_scope &Scope + cflags []cflag.CFlag + redefined_fns []string + fn_generic_types map[string][][]Type // for generic functions + interfaces map[int]InterfaceDecl + cmod_prefix string // needed for ast.type_to_str(Type) while vfmt; contains `os.` + is_fmt bool + used_fns map[string]bool // filled in by the checker, when pref.skip_unused = true; + used_consts map[string]bool // filled in by the checker, when pref.skip_unused = true; + used_globals map[string]bool // filled in by the checker, when pref.skip_unused = true; + used_vweb_types []Type // vweb context types, filled in by checker, when pref.skip_unused = true; + used_maps int // how many times maps were used, filled in by checker, when pref.skip_unused = true; + panic_handler FnPanicHandler = default_table_panic_handler + panic_userdata voidptr = voidptr(0) // can be used to pass arbitrary data to panic_handler; + panic_npanics int + cur_fn &FnDecl = 0 // previously stored in Checker.cur_fn and Gen.cur_fn + cur_concrete_types []Type // current concrete types, e.g. + gostmts int // how many `go` statements there were in the parsed files. + enum_decls map[string]EnumDecl + // When table.gostmts > 0, __VTHREADS__ is defined, which can be checked with `$if threads {` +} + +[unsafe] +pub fn (t &Table) free() { + unsafe { + t.type_symbols.free() + t.type_idxs.free() + t.fns.free() + t.dumps.free() + t.imports.free() + t.modules.free() + t.cflags.free() + t.redefined_fns.free() + t.fn_generic_types.free() + t.cmod_prefix.free() + t.used_fns.free() + t.used_consts.free() + t.used_globals.free() + t.used_vweb_types.free() + } +} + +pub type FnPanicHandler = fn (&Table, string) + +fn default_table_panic_handler(t &Table, message string) { + panic(message) +} + +pub fn (t &Table) panic(message string) { + mut mt := unsafe { &Table(t) } + mt.panic_npanics++ + t.panic_handler(t, message) +} + +pub struct Fn { +pub: + is_variadic bool + language Language + is_pub bool + is_deprecated bool // `[deprecated] fn abc(){}` + is_noreturn bool // `[noreturn] fn abc(){}` + is_unsafe bool // `[unsafe] fn abc(){}` + is_placeholder bool + is_main bool // `fn main(){}` + is_test bool // `fn test_abc(){}` + is_keep_alive bool // passed memory must not be freed (by GC) before function returns + no_body bool // a pure declaration like `fn abc(x int)`; used in .vh files, C./JS. fns. + mod string + pos token.Position + return_type_pos token.Position +pub mut: + return_type Type + name string + params []Param + source_fn voidptr // set in the checker, while processing fn declarations + usages int + // + generic_names []string + attrs []Attr // all fn attributes + is_conditional bool // true for `[if abc]fn(){}` + ctdefine_idx int // the index of the attribute, containing the compile time define [if mytag] +} + +fn (f &Fn) method_equals(o &Fn) bool { + return f.params[1..].equals(o.params[1..]) && f.return_type == o.return_type + && f.is_variadic == o.is_variadic && f.language == o.language + && f.generic_names == o.generic_names && f.is_pub == o.is_pub && f.mod == o.mod + && f.name == o.name +} + +pub struct Param { +pub: + pos token.Position + name string + is_mut bool + is_auto_rec bool + type_pos token.Position + is_hidden bool // interface first arg +pub mut: + typ Type +} + +pub fn (f Fn) new_method_with_receiver_type(new_type Type) Fn { + mut new_method := f + new_method.params = f.params.clone() + for i in 1 .. new_method.params.len { + if new_method.params[i].typ == new_method.params[0].typ { + new_method.params[i].typ = new_type + } + } + new_method.params[0].typ = new_type + return new_method +} + +pub fn (f FnDecl) new_method_with_receiver_type(new_type Type) FnDecl { + mut new_method := f + new_method.params = f.params.clone() + for i in 1 .. new_method.params.len { + if new_method.params[i].typ == new_method.params[0].typ { + new_method.params[i].typ = new_type + } + } + new_method.params[0].typ = new_type + return new_method +} + +fn (p &Param) equals(o &Param) bool { + return p.name == o.name && p.is_mut == o.is_mut && p.typ == o.typ && p.is_hidden == o.is_hidden +} + +fn (p []Param) equals(o []Param) bool { + if p.len != o.len { + return false + } + for i in 0 .. p.len { + if !p[i].equals(o[i]) { + return false + } + } + return true +} + +/* +pub struct Var { +pub: + name string + is_mut bool +mut: + typ Type +} +*/ + +pub fn new_table() &Table { + mut t := &Table{ + type_symbols: []TypeSymbol{cap: 64000} + global_scope: &Scope{ + parent: 0 + } + cur_fn: 0 + } + t.register_builtin_type_symbols() + t.is_fmt = true + set_global_table(t) + return t +} + +const global_table = &Table(0) + +pub fn set_global_table(t &Table) { + unsafe { + mut pg := &ast.global_table + *pg = t + } +} + +// used to compare fn's & for naming anon fn's +pub fn (t &Table) fn_type_signature(f &Fn) string { + mut sig := '' + for i, arg in f.params { + // TODO: for now ignore mut/pts in sig for now + typ := arg.typ.set_nr_muls(0) + arg_type_sym := t.get_type_symbol(typ) + sig += arg_type_sym.str().to_lower().replace_each(['.', '__', '&', '', '[]', 'arr_', 'chan ', + 'chan_', 'map[', 'map_of_', ']', '_to_', '<', '_T_', ',', '_', '>', '']) + if i < f.params.len - 1 { + sig += '_' + } + } + if f.return_type != 0 && f.return_type != void_type { + sym := t.get_type_symbol(f.return_type) + opt := if f.return_type.has_flag(.optional) { 'option_' } else { '' } + sig += '__$opt$sym.kind' + } + return sig +} + +// source_signature generates the signature of a function which looks like in the V source +pub fn (t &Table) fn_type_source_signature(f &Fn) string { + mut sig := '(' + for i, arg in f.params { + if arg.is_mut { + sig += 'mut ' + } + // NB: arg name is only added for fmt, else it would causes errors with generics + if t.is_fmt && arg.name.len > 0 { + sig += '$arg.name ' + } + arg_type_sym := t.get_type_symbol(arg.typ) + sig += arg_type_sym.name + if i < f.params.len - 1 { + sig += ', ' + } + } + sig += ')' + if f.return_type == ovoid_type { + sig += ' ?' + } else if f.return_type != void_type { + return_type_sym := t.get_type_symbol(f.return_type) + if f.return_type.has_flag(.optional) { + sig += ' ?$return_type_sym.name' + } else { + sig += ' $return_type_sym.name' + } + } + return sig +} + +pub fn (t &Table) is_same_method(f &Fn, func &Fn) string { + if f.return_type != func.return_type { + s := t.type_to_str(f.return_type) + return 'expected return type `$s`' + } + if f.params.len != func.params.len { + return 'expected $f.params.len parameter(s), not $func.params.len' + } + for i in 1 .. f.params.len { + if f.params[i].typ != func.params[i].typ { + exps := t.type_to_str(f.params[i].typ) + gots := t.type_to_str(func.params[i].typ) + return 'expected `$exps`, not `$gots` for parameter $i' + } + } + return '' +} + +pub fn (t &Table) find_fn(name string) ?Fn { + if f := t.fns[name] { + return f + } + return none +} + +pub fn (t &Table) known_fn(name string) bool { + t.find_fn(name) or { return false } + return true +} + +pub fn (mut t Table) register_fn(new_fn Fn) { + // println('reg fn $new_fn.name nr_args=$new_fn.args.len') + t.fns[new_fn.name] = new_fn +} + +pub fn (mut t Table) register_interface(idecl InterfaceDecl) { + t.interfaces[idecl.typ] = idecl +} + +pub fn (mut t TypeSymbol) register_method(new_fn Fn) int { + // returns a method index, stored in the ast.FnDecl + // for faster lookup in the checker's fn_decl method + // println('reg me $new_fn.name nr_args=$new_fn.args.len') + t.methods << new_fn + return t.methods.len - 1 +} + +pub fn (t &Table) register_aggregate_method(mut sym TypeSymbol, name string) ?Fn { + if sym.kind != .aggregate { + t.panic('Unexpected type symbol: $sym.kind') + } + agg_info := sym.info as Aggregate + // an aggregate always has at least 2 types + mut found_once := false + mut new_fn := Fn{} + for typ in agg_info.types { + ts := t.get_type_symbol(typ) + if type_method := ts.find_method(name) { + if !found_once { + found_once = true + new_fn = type_method + } else if !new_fn.method_equals(type_method) { + return error('method `${t.type_to_str(typ)}.$name` signature is different') + } + } else { + return error('unknown method: `${t.type_to_str(typ)}.$name`') + } + } + // register the method in the aggregate, so lookup is faster next time + sym.register_method(new_fn) + return new_fn +} + +pub fn (t &Table) type_has_method(s &TypeSymbol, name string) bool { + // println('type_has_method($s.name, $name) types.len=$t.types.len s.parent_idx=$s.parent_idx') + if _ := t.type_find_method(s, name) { + return true + } + return false +} + +// type_find_method searches from current type up through each parent looking for method +pub fn (t &Table) type_find_method(s &TypeSymbol, name string) ?Fn { + // println('type_find_method($s.name, $name) types.len=$t.types.len s.parent_idx=$s.parent_idx') + mut ts := unsafe { s } + for { + if method := ts.find_method(name) { + return method + } + if ts.kind == .aggregate { + return t.register_aggregate_method(mut ts, name) + } + if ts.parent_idx == 0 { + break + } + ts = unsafe { &t.type_symbols[ts.parent_idx] } + } + return none +} + +pub fn (t &Table) type_find_method_from_embeds(sym &TypeSymbol, method_name string) ?(Fn, Type) { + if sym.info is Struct { + mut found_methods := []Fn{} + mut embed_of_found_methods := []Type{} + for embed in sym.info.embeds { + embed_sym := t.get_type_symbol(embed) + if m := t.type_find_method(embed_sym, method_name) { + found_methods << m + embed_of_found_methods << embed + } + } + if found_methods.len == 1 { + return found_methods[0], embed_of_found_methods[0] + } else if found_methods.len > 1 { + return error('ambiguous method `$method_name`') + } + } else if sym.info is Aggregate { + for typ in sym.info.types { + agg_sym := t.get_type_symbol(typ) + method, embed_type := t.type_find_method_from_embeds(agg_sym, method_name) or { + return err + } + if embed_type != 0 { + return method, embed_type + } + } + } + return none +} + +fn (t &Table) register_aggregate_field(mut sym TypeSymbol, name string) ?StructField { + if sym.kind != .aggregate { + t.panic('Unexpected type symbol: $sym.kind') + } + mut agg_info := sym.info as Aggregate + // an aggregate always has at least 2 types + mut found_once := false + mut new_field := StructField{} + for typ in agg_info.types { + ts := t.get_type_symbol(typ) + if type_field := t.find_field(ts, name) { + if !found_once { + found_once = true + new_field = type_field + } else if !new_field.equals(type_field) { + return error('field `${t.type_to_str(typ)}.$name` type is different') + } + } else { + return error('type `${t.type_to_str(typ)}` has no field or method `$name`') + } + } + agg_info.fields << new_field + return new_field +} + +pub fn (t &Table) struct_has_field(struct_ &TypeSymbol, name string) bool { + // println('struct_has_field($s.name, $name) types.len=$t.types.len s.parent_idx=$s.parent_idx') + if _ := t.find_field(struct_, name) { + return true + } + return false +} + +// search from current type up through each parent looking for field +pub fn (t &Table) find_field(s &TypeSymbol, name string) ?StructField { + // println('find_field($s.name, $name) types.len=$t.types.len s.parent_idx=$s.parent_idx') + mut ts := unsafe { s } + for { + match mut ts.info { + Struct { + if field := ts.info.find_field(name) { + return field + } + } + Aggregate { + if field := ts.info.find_field(name) { + return field + } + field := t.register_aggregate_field(mut ts, name) or { return err } + return field + } + Interface { + if field := ts.info.find_field(name) { + return field + } + } + SumType { + t.resolve_common_sumtype_fields(s) + if field := ts.info.find_field(name) { + return field + } + return error('field `$name` does not exist or have the same type in all sumtype variants') + } + else {} + } + if ts.parent_idx == 0 { + break + } + ts = unsafe { &t.type_symbols[ts.parent_idx] } + } + return none +} + +// find_field_from_embeds finds and returns a field in the embeddings of a struct and the embedding type +pub fn (t &Table) find_field_from_embeds(sym &TypeSymbol, field_name string) ?(StructField, Type) { + if sym.info is Struct { + mut found_fields := []StructField{} + mut embed_of_found_fields := []Type{} + for embed in sym.info.embeds { + embed_sym := t.get_type_symbol(embed) + if field := t.find_field(embed_sym, field_name) { + found_fields << field + embed_of_found_fields << embed + } + } + if found_fields.len == 1 { + return found_fields[0], embed_of_found_fields[0] + } else if found_fields.len > 1 { + return error('ambiguous field `$field_name`') + } + } else if sym.info is Aggregate { + for typ in sym.info.types { + agg_sym := t.get_type_symbol(typ) + field, embed_type := t.find_field_from_embeds(agg_sym, field_name) or { return err } + if embed_type != 0 { + return field, embed_type + } + } + } + return none +} + +// find_field_with_embeds searches for a given field, also looking through embedded fields +pub fn (t &Table) find_field_with_embeds(sym &TypeSymbol, field_name string) ?StructField { + if field := t.find_field(sym, field_name) { + return field + } else { + // look for embedded field + first_err := err + field, _ := t.find_field_from_embeds(sym, field_name) or { return first_err } + return field + } +} + +pub fn (t &Table) resolve_common_sumtype_fields(sym_ &TypeSymbol) { + mut sym := unsafe { sym_ } + mut info := sym.info as SumType + if info.found_fields { + return + } + mut field_map := map[string]StructField{} + mut field_usages := map[string]int{} + for variant in info.variants { + mut v_sym := t.get_type_symbol(variant) + fields := match mut v_sym.info { + Struct { + v_sym.info.fields + } + SumType { + t.resolve_common_sumtype_fields(v_sym) + v_sym.info.fields + } + else { + []StructField{} + } + } + for field in fields { + if field.name !in field_map { + field_map[field.name] = field + field_usages[field.name]++ + } else if field.equals(field_map[field.name]) { + field_usages[field.name]++ + } + } + } + for field, nr_definitions in field_usages { + if nr_definitions == info.variants.len { + info.fields << field_map[field] + } + } + info.found_fields = true + sym.info = info +} + +[inline] +pub fn (t &Table) find_type_idx(name string) int { + return t.type_idxs[name] +} + +[inline] +pub fn (t &Table) find_type(name string) ?TypeSymbol { + idx := t.type_idxs[name] + if idx > 0 { + return t.type_symbols[idx] + } + return none +} + +pub const invalid_type_symbol = &TypeSymbol{ + parent_idx: -1 + language: .v + mod: 'builtin' + kind: .placeholder + name: 'InvalidType' + cname: 'InvalidType' +} + +[inline] +pub fn (t &Table) get_type_symbol(typ Type) &TypeSymbol { + idx := typ.idx() + if idx > 0 { + return unsafe { &t.type_symbols[idx] } + } + // this should never happen + t.panic('get_type_symbol: invalid type (typ=$typ idx=$idx). Compiler bug. This should never happen. Please report the bug using `v bug file.v`. +') + return ast.invalid_type_symbol +} + +// get_final_type_symbol follows aliases until it gets to a "real" Type +[inline] +pub fn (t &Table) get_final_type_symbol(typ Type) &TypeSymbol { + mut idx := typ.idx() + if idx > 0 { + current_type := t.type_symbols[idx] + if current_type.kind == .alias { + idx = (current_type.info as Alias).parent_type.idx() + } + return unsafe { &t.type_symbols[idx] } + } + // this should never happen + t.panic('get_final_type_symbol: invalid type (typ=$typ idx=$idx). Compiler bug. This should never happen. Please report the bug using `v bug file.v`.') + return ast.invalid_type_symbol +} + +[inline] +pub fn (t &Table) get_type_name(typ Type) string { + typ_sym := t.get_type_symbol(typ) + return typ_sym.name +} + +[inline] +pub fn (t &Table) unalias_num_type(typ Type) Type { + sym := t.get_type_symbol(typ) + if sym.kind == .alias { + pt := (sym.info as Alias).parent_type + if pt <= f64_type && pt >= void_type { + return pt + } + } + return typ +} + +fn (mut t Table) check_for_already_registered_symbol(typ TypeSymbol, existing_idx int) int { + ex_type := t.type_symbols[existing_idx] + match ex_type.kind { + .placeholder { + // override placeholder + // println('overriding type placeholder `$typ.name`') + t.type_symbols[existing_idx] = TypeSymbol{ + ...typ + methods: ex_type.methods + } + return existing_idx + } + else { + // builtin + // this will override the already registered builtin types + // with the actual v struct declaration in the source + if (existing_idx >= string_type_idx && existing_idx <= map_type_idx) + || existing_idx == error_type_idx { + if existing_idx == string_type_idx { + // existing_type := t.type_symbols[existing_idx] + t.type_symbols[existing_idx] = TypeSymbol{ + ...typ + kind: ex_type.kind + } + } else { + t.type_symbols[existing_idx] = typ + } + return existing_idx + } + return -1 + } + } + return -2 +} + +[inline] +pub fn (mut t Table) register_type_symbol(typ TypeSymbol) int { + mut typ_idx := -2 + mut existing_idx := t.type_idxs[typ.name] + if existing_idx > 0 { + typ_idx = t.check_for_already_registered_symbol(typ, existing_idx) + if typ_idx != -2 { + return typ_idx + } + } + if typ.mod == 'main' { + existing_idx = t.type_idxs[typ.name.trim_prefix('main.')] + if existing_idx > 0 { + typ_idx = t.check_for_already_registered_symbol(typ, existing_idx) + if typ_idx != -2 { + return typ_idx + } + } + } + typ_idx = t.type_symbols.len + t.type_symbols << typ + t.type_symbols[typ_idx].idx = typ_idx + t.type_idxs[typ.name] = typ_idx + return typ_idx +} + +[inline] +pub fn (mut t Table) register_enum_decl(enum_decl EnumDecl) { + t.enum_decls[enum_decl.name] = enum_decl +} + +pub fn (t &Table) known_type(name string) bool { + return t.find_type_idx(name) != 0 +} + +pub fn (t &Table) known_type_idx(typ Type) bool { + if typ == 0 { + return false + } + sym := t.get_type_symbol(typ) + match sym.kind { + .placeholder { + return sym.language != .v || sym.name.starts_with('C.') + } + .array { + return t.known_type_idx((sym.info as Array).elem_type) + } + .array_fixed { + return t.known_type_idx((sym.info as ArrayFixed).elem_type) + } + .map { + info := sym.info as Map + return t.known_type_idx(info.key_type) && t.known_type_idx(info.value_type) + } + else {} + } + return true +} + +// array_source_name generates the original name for the v source. +// e. g. []int +[inline] +pub fn (t &Table) array_name(elem_type Type) string { + elem_type_sym := t.get_type_symbol(elem_type) + ptr := if elem_type.is_ptr() { '&'.repeat(elem_type.nr_muls()) } else { '' } + return '[]$ptr$elem_type_sym.name' +} + +[inline] +pub fn (t &Table) array_cname(elem_type Type) string { + elem_type_sym := t.get_type_symbol(elem_type) + mut res := '' + if elem_type.is_ptr() { + res = '_ptr'.repeat(elem_type.nr_muls()) + } + return 'Array_$elem_type_sym.cname' + res +} + +// array_fixed_source_name generates the original name for the v source. +// e. g. [16][8]int +[inline] +pub fn (t &Table) array_fixed_name(elem_type Type, size int, size_expr Expr) string { + elem_type_sym := t.get_type_symbol(elem_type) + ptr := if elem_type.is_ptr() { '&'.repeat(elem_type.nr_muls()) } else { '' } + size_str := if size_expr is EmptyExpr || size != 987654321 { + size.str() + } else { + size_expr.str() + } + return '[$size_str]$ptr$elem_type_sym.name' +} + +[inline] +pub fn (t &Table) array_fixed_cname(elem_type Type, size int) string { + elem_type_sym := t.get_type_symbol(elem_type) + mut res := '' + if elem_type.is_ptr() { + res = '_ptr$elem_type.nr_muls()' + } + return 'Array_fixed_${elem_type_sym.cname}_$size' + res +} + +[inline] +pub fn (t &Table) chan_name(elem_type Type, is_mut bool) string { + elem_type_sym := t.get_type_symbol(elem_type) + mut ptr := '' + if is_mut { + ptr = 'mut ' + } else if elem_type.is_ptr() { + ptr = '&' + } + return 'chan $ptr$elem_type_sym.name' +} + +[inline] +pub fn (t &Table) chan_cname(elem_type Type, is_mut bool) string { + elem_type_sym := t.get_type_symbol(elem_type) + mut suffix := '' + if is_mut { + suffix = '_mut' + } else if elem_type.is_ptr() { + suffix = '_ptr' + } + return 'chan_$elem_type_sym.cname' + suffix +} + +[inline] +pub fn (t &Table) thread_name(return_type Type) string { + if return_type.idx() == void_type_idx { + if return_type.has_flag(.optional) { + return 'thread ?' + } else { + return 'thread' + } + } + return_type_sym := t.get_type_symbol(return_type) + ptr := if return_type.is_ptr() { '&' } else { '' } + opt := if return_type.has_flag(.optional) { '?' } else { '' } + return 'thread $opt$ptr$return_type_sym.name' +} + +[inline] +pub fn (t &Table) thread_cname(return_type Type) string { + if return_type == void_type { + if return_type.has_flag(.optional) { + return '__v_thread_Option_void' + } else { + return '__v_thread' + } + } + return_type_sym := t.get_type_symbol(return_type) + suffix := if return_type.is_ptr() { '_ptr' } else { '' } + prefix := if return_type.has_flag(.optional) { 'Option_' } else { '' } + return '__v_thread_$prefix$return_type_sym.cname$suffix' +} + +// map_source_name generates the original name for the v source. +// e. g. map[string]int +[inline] +pub fn (t &Table) map_name(key_type Type, value_type Type) string { + key_type_sym := t.get_type_symbol(key_type) + value_type_sym := t.get_type_symbol(value_type) + ptr := if value_type.is_ptr() { '&' } else { '' } + return 'map[$key_type_sym.name]$ptr$value_type_sym.name' +} + +[inline] +pub fn (t &Table) map_cname(key_type Type, value_type Type) string { + key_type_sym := t.get_type_symbol(key_type) + value_type_sym := t.get_type_symbol(value_type) + suffix := if value_type.is_ptr() { '_ptr' } else { '' } + return 'Map_${key_type_sym.cname}_$value_type_sym.cname' + suffix + // return 'map_${value_type_sym.name}' + suffix +} + +pub fn (mut t Table) find_or_register_chan(elem_type Type, is_mut bool) int { + name := t.chan_name(elem_type, is_mut) + cname := t.chan_cname(elem_type, is_mut) + // existing + existing_idx := t.type_idxs[name] + if existing_idx > 0 { + return existing_idx + } + // register + chan_typ := TypeSymbol{ + parent_idx: chan_type_idx + kind: .chan + name: name + cname: cname + info: Chan{ + elem_type: elem_type + is_mut: is_mut + } + } + return t.register_type_symbol(chan_typ) +} + +pub fn (mut t Table) find_or_register_map(key_type Type, value_type Type) int { + name := t.map_name(key_type, value_type) + cname := t.map_cname(key_type, value_type) + // existing + existing_idx := t.type_idxs[name] + if existing_idx > 0 { + return existing_idx + } + // register + map_typ := TypeSymbol{ + parent_idx: map_type_idx + kind: .map + name: name + cname: cname + info: Map{ + key_type: key_type + value_type: value_type + } + } + return t.register_type_symbol(map_typ) +} + +pub fn (mut t Table) find_or_register_thread(return_type Type) int { + name := t.thread_name(return_type) + cname := t.thread_cname(return_type) + // existing + existing_idx := t.type_idxs[name] + if existing_idx > 0 { + return existing_idx + } + // register + thread_typ := TypeSymbol{ + parent_idx: thread_type_idx + kind: .thread + name: name + cname: cname + info: Thread{ + return_type: return_type + } + } + return t.register_type_symbol(thread_typ) +} + +pub fn (mut t Table) find_or_register_array(elem_type Type) int { + name := t.array_name(elem_type) + // existing + existing_idx := t.type_idxs[name] + if existing_idx > 0 { + return existing_idx + } + cname := t.array_cname(elem_type) + // register + array_type_ := TypeSymbol{ + parent_idx: array_type_idx + kind: .array + name: name + cname: cname + info: Array{ + nr_dims: 1 + elem_type: elem_type + } + } + return t.register_type_symbol(array_type_) +} + +pub fn (mut t Table) find_or_register_array_with_dims(elem_type Type, nr_dims int) int { + if nr_dims == 1 { + return t.find_or_register_array(elem_type) + } + return t.find_or_register_array(t.find_or_register_array_with_dims(elem_type, nr_dims - 1)) +} + +pub fn (mut t Table) find_or_register_array_fixed(elem_type Type, size int, size_expr Expr) int { + name := t.array_fixed_name(elem_type, size, size_expr) + // existing + existing_idx := t.type_idxs[name] + if existing_idx > 0 { + return existing_idx + } + cname := t.array_fixed_cname(elem_type, size) + // register + array_fixed_type := TypeSymbol{ + kind: .array_fixed + name: name + cname: cname + info: ArrayFixed{ + elem_type: elem_type + size: size + size_expr: size_expr + } + } + return t.register_type_symbol(array_fixed_type) +} + +pub fn (mut t Table) find_or_register_multi_return(mr_typs []Type) int { + mut name := '(' + mut cname := 'multi_return' + for i, mr_typ in mr_typs { + mr_type_sym := t.get_type_symbol(mr_typ) + name += mr_type_sym.name + cname += '_$mr_type_sym.cname' + if i < mr_typs.len - 1 { + name += ', ' + } + } + name += ')' + // existing + existing_idx := t.type_idxs[name] + if existing_idx > 0 { + return existing_idx + } + // register + mr_type := TypeSymbol{ + kind: .multi_return + name: name + cname: cname + info: MultiReturn{ + types: mr_typs + } + } + return t.register_type_symbol(mr_type) +} + +pub fn (mut t Table) find_or_register_fn_type(mod string, f Fn, is_anon bool, has_decl bool) int { + name := if f.name.len == 0 { 'fn ${t.fn_type_source_signature(f)}' } else { f.name.clone() } + cname := if f.name.len == 0 { + 'anon_fn_${t.fn_type_signature(f)}' + } else { + util.no_dots(f.name.clone()) + } + anon := f.name.len == 0 || is_anon + existing_idx := t.type_idxs[name] + if existing_idx > 0 && t.type_symbols[existing_idx].kind != .placeholder { + return existing_idx + } + return t.register_type_symbol( + kind: .function + name: name + cname: cname + mod: mod + info: FnType{ + is_anon: anon + has_decl: has_decl + func: f + } + ) +} + +pub fn (mut t Table) add_placeholder_type(name string, language Language) int { + mut modname := '' + if name.contains('.') { + modname = name.all_before_last('.') + } + ph_type := TypeSymbol{ + kind: .placeholder + name: name + cname: util.no_dots(name) + language: language + mod: modname + } + // println('added placeholder: $name - $ph_type.idx') + return t.register_type_symbol(ph_type) +} + +[inline] +pub fn (t &Table) value_type(typ Type) Type { + typ_sym := t.get_final_type_symbol(typ) + if typ.has_flag(.variadic) { + // ...string => string + // return typ.clear_flag(.variadic) + array_info := typ_sym.info as Array + return array_info.elem_type + } + if typ_sym.kind == .array { + // Check index type + info := typ_sym.info as Array + return info.elem_type + } + if typ_sym.kind == .array_fixed { + info := typ_sym.info as ArrayFixed + return info.elem_type + } + if typ_sym.kind == .map { + info := typ_sym.info as Map + return info.value_type + } + if typ_sym.kind == .string && typ.is_ptr() { + // (&string)[i] => string + return string_type + } + if typ_sym.kind in [.byteptr, .string] { + return byte_type + } + if typ.is_ptr() { + // byte* => byte + // bytes[0] is a byte, not byte* + return typ.deref() + } + // TODO: remove when map_string is removed + if typ_sym.name == 'map_string' { + return string_type + } + return void_type +} + +[inline] +pub fn (t &Table) mktyp(typ Type) Type { + match typ { + float_literal_type { return f64_type } + int_literal_type { return int_type } + else { return typ } + } +} + +pub fn (mut t Table) register_fn_concrete_types(fn_name string, types []Type) bool { + mut a := t.fn_generic_types[fn_name] + if types in a { + return false + } + a << types + t.fn_generic_types[fn_name] = a + return true +} + +// TODO: there is a bug when casting sumtype the other way if its pointer +// so until fixed at least show v (not C) error `x(variant) = y(SumType*)` +pub fn (t &Table) sumtype_has_variant(parent Type, variant Type) bool { + parent_sym := t.get_type_symbol(parent) + if parent_sym.kind == .sum_type { + parent_info := parent_sym.info as SumType + for v in parent_info.variants { + if v.idx() == variant.idx() { + return true + } + } + } + return false +} + +// only used for debugging V compiler type bugs +pub fn (t &Table) known_type_names() []string { + mut res := []string{cap: t.type_idxs.len} + for _, idx in t.type_idxs { + // Skip `int_literal_type_idx` and `float_literal_type_idx` because they shouldn't be visible to the User. + if idx !in [0, int_literal_type_idx, float_literal_type_idx] && t.known_type_idx(idx) + && t.get_type_symbol(idx).kind != .function { + res << t.type_to_str(idx) + } + } + return res +} + +// has_deep_child_no_ref returns true if type is struct and has any child or nested child with the type of the given name +// the given name consists of module and name (`mod.Name`) +// it doesn't care about childs that are references +pub fn (t &Table) has_deep_child_no_ref(ts &TypeSymbol, name string) bool { + if ts.info is Struct { + for field in ts.info.fields { + sym := t.get_type_symbol(field.typ) + if !field.typ.is_ptr() && (sym.name == name || t.has_deep_child_no_ref(sym, name)) { + return true + } + } + } + return false +} + +// complete_interface_check does a MxN check for all M interfaces vs all N types, to determine what types implement what interfaces. +// It short circuits most checks when an interface can not possibly be implemented by a type. +pub fn (mut t Table) complete_interface_check() { + util.timing_start(@METHOD) + defer { + util.timing_measure(@METHOD) + } + for tk, mut tsym in t.type_symbols { + if tsym.kind != .struct_ { + continue + } + info := tsym.info as Struct + for _, mut idecl in t.interfaces { + if idecl.methods.len > tsym.methods.len { + continue + } + if idecl.fields.len > info.fields.len { + continue + } + // empty interface only generate type cast functions of the current module + if idecl.methods.len == 0 && idecl.fields.len == 0 + && tsym.mod != t.get_type_symbol(idecl.typ).mod { + continue + } + if t.does_type_implement_interface(tk, idecl.typ) { + $if trace_types_implementing_each_interface ? { + eprintln('>>> tsym.mod: $tsym.mod | tsym.name: $tsym.name | tk: $tk | idecl.name: $idecl.name | idecl.typ: $idecl.typ') + } + t.iface_types[idecl.name] << tk + } + } + } +} + +// bitsize_to_type returns a type corresponding to the bit_size +// Examples: +// +// `8 > i8` +// +// `32 > int` +// +// `123 > panic()` +// +// `128 > [16]byte` +// +// `608 > [76]byte` +pub fn (mut t Table) bitsize_to_type(bit_size int) Type { + match bit_size { + 8 { + return i8_type + } + 16 { + return i16_type + } + 32 { + return int_type + } + 64 { + return i64_type + } + else { + if bit_size % 8 != 0 { // there is no way to do `i2131(32)` so this should never be reached + t.panic('compiler bug: bitsizes must be multiples of 8') + } + return new_type(t.find_or_register_array_fixed(byte_type, bit_size / 8, empty_expr())) + } + } +} + +pub fn (mut t Table) does_type_implement_interface(typ Type, inter_typ Type) bool { + if typ.idx() == inter_typ.idx() { + // same type -> already casted to the interface + return true + } + if inter_typ.idx() == error_type_idx && typ.idx() == none_type_idx { + // `none` "implements" the Error interface + return true + } + typ_sym := t.get_type_symbol(typ) + if typ_sym.language != .v { + return false + } + // generic struct don't generate cast interface fn + if typ_sym.info is Struct { + if typ_sym.info.is_generic { + return false + } + } + mut inter_sym := t.get_type_symbol(inter_typ) + if typ_sym.kind == .interface_ && inter_sym.kind == .interface_ { + return false + } + if mut inter_sym.info is Interface { + // do not check the same type more than once + for tt in inter_sym.info.types { + if tt.idx() == typ.idx() { + return true + } + } + // verify methods + for imethod in inter_sym.info.methods { + if method := typ_sym.find_method(imethod.name) { + msg := t.is_same_method(imethod, method) + if msg.len > 0 { + return false + } + continue + } + return false + } + // verify fields + for ifield in inter_sym.info.fields { + if ifield.typ == voidptr_type { + // Allow `voidptr` fields in interfaces for now. (for example + // to enable .db check in vweb) + if t.struct_has_field(typ_sym, ifield.name) { + continue + } else { + return false + } + } + if field := t.find_field_with_embeds(typ_sym, ifield.name) { + if ifield.typ != field.typ { + return false + } else if ifield.is_mut && !(field.is_mut || field.is_global) { + return false + } + continue + } + return false + } + inter_sym.info.types << typ + if !inter_sym.info.types.contains(voidptr_type) { + inter_sym.info.types << voidptr_type + } + return true + } + return false +} + +// resolve_generic_to_concrete resolves generics to real types T => int. +// Even map[string]map[string]T can be resolved. +// This is used for resolving the generic return type of CallExpr white `unwrap_generic` is used to resolve generic usage in FnDecl. +pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_names []string, concrete_types []Type) ?Type { + mut sym := t.get_type_symbol(generic_type) + if sym.name in generic_names { + index := generic_names.index(sym.name) + if index >= concrete_types.len { + return none + } + typ := concrete_types[index] + if typ == 0 { + return none + } + return typ.derive_add_muls(generic_type).clear_flag(.generic) + } + match mut sym.info { + Array { + mut elem_type := sym.info.elem_type + mut elem_sym := t.get_type_symbol(elem_type) + mut dims := 1 + for mut elem_sym.info is Array { + info := elem_sym.info as Array + elem_type = info.elem_type + elem_sym = t.get_type_symbol(elem_type) + dims++ + } + if typ := t.resolve_generic_to_concrete(elem_type, generic_names, concrete_types) { + idx := t.find_or_register_array_with_dims(typ, dims) + return new_type(idx).derive_add_muls(generic_type).clear_flag(.generic) + } + } + ArrayFixed { + if typ := t.resolve_generic_to_concrete(sym.info.elem_type, generic_names, + concrete_types) + { + idx := t.find_or_register_array_fixed(typ, sym.info.size, None{}) + return new_type(idx).derive_add_muls(generic_type).clear_flag(.generic) + } + } + Chan { + if typ := t.resolve_generic_to_concrete(sym.info.elem_type, generic_names, + concrete_types) + { + idx := t.find_or_register_chan(typ, typ.nr_muls() > 0) + return new_type(idx).derive_add_muls(generic_type).clear_flag(.generic) + } + } + FnType { + mut func := sym.info.func + if func.return_type.has_flag(.generic) { + if typ := t.resolve_generic_to_concrete(func.return_type, generic_names, + concrete_types) + { + func.return_type = typ + } + } + func.params = func.params.clone() + for mut param in func.params { + if param.typ.has_flag(.generic) { + if typ := t.resolve_generic_to_concrete(param.typ, generic_names, + concrete_types) + { + param.typ = typ + } + } + } + idx := t.find_or_register_fn_type('', func, true, false) + return new_type(idx).derive_add_muls(generic_type).clear_flag(.generic) + } + MultiReturn { + mut types := []Type{} + mut type_changed := false + for ret_type in sym.info.types { + if typ := t.resolve_generic_to_concrete(ret_type, generic_names, concrete_types) { + types << typ + type_changed = true + } else { + types << ret_type + } + } + if type_changed { + idx := t.find_or_register_multi_return(types) + return new_type(idx).derive_add_muls(generic_type).clear_flag(.generic) + } + } + Map { + mut type_changed := false + mut unwrapped_key_type := sym.info.key_type + mut unwrapped_value_type := sym.info.value_type + if typ := t.resolve_generic_to_concrete(sym.info.key_type, generic_names, + concrete_types) + { + unwrapped_key_type = typ + type_changed = true + } + if typ := t.resolve_generic_to_concrete(sym.info.value_type, generic_names, + concrete_types) + { + unwrapped_value_type = typ + type_changed = true + } + if type_changed { + idx := t.find_or_register_map(unwrapped_key_type, unwrapped_value_type) + return new_type(idx).derive_add_muls(generic_type).clear_flag(.generic) + } + } + Struct, Interface, SumType { + if sym.info.is_generic { + mut nrt := '$sym.name<' + for i in 0 .. sym.info.generic_types.len { + if ct := t.resolve_generic_to_concrete(sym.info.generic_types[i], + generic_names, concrete_types) + { + gts := t.get_type_symbol(ct) + nrt += gts.name + if i != sym.info.generic_types.len - 1 { + nrt += ',' + } + } + } + nrt += '>' + mut idx := t.type_idxs[nrt] + if idx == 0 { + idx = t.add_placeholder_type(nrt, .v) + } + return new_type(idx).derive_add_muls(generic_type).clear_flag(.generic) + } + } + else {} + } + return none +} diff --git a/v_windows/v/old/vlib/v/ast/types.v b/v_windows/v/old/vlib/v/ast/types.v new file mode 100644 index 0000000..bf01029 --- /dev/null +++ b/v_windows/v/old/vlib/v/ast/types.v @@ -0,0 +1,1308 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +// +// Type layout information (32 bits) +// flag (8 bits) | nr_muls (8 bits) | idx (16 bits) +// pack: (int(flag)<<24) | (nr_muls<<16) | u16(idx) +// unpack: +// flag: (int(type)>>24) & 0xff +// nr_muls: (int(type)>>16) & 0xff +// idx: u16(type) & 0xffff +module ast + +import strings +import v.pref + +pub type Type = int + +pub type TypeInfo = Aggregate | Alias | Array | ArrayFixed | Chan | Enum | FnType | GenericStructInst | + Interface | Map | MultiReturn | Struct | SumType | Thread + +pub enum Language { + v + c + js + amd64 // aka x86_64 + i386 + arm64 // 64-bit arm + arm32 // 32-bit arm + rv64 // 64-bit risc-v + rv32 // 32-bit risc-v +} + +pub fn pref_arch_to_table_language(pref_arch pref.Arch) Language { + return match pref_arch { + .amd64 { + Language.amd64 + } + .arm64 { + Language.arm64 + } + .arm32 { + Language.arm32 + } + .rv64 { + Language.rv64 + } + .rv32 { + Language.rv32 + } + .i386 { + Language.i386 + } + ._auto, ._max { + Language.v + } + } +} + +// Represents a type that only needs an identifier, e.g. int, array_int. +// A pointer type `&T` would have a TypeSymbol `T`. +// Note: For a Type, use: +// * Table.type_to_str(typ) not TypeSymbol.name. +// * Table.type_kind(typ) not TypeSymbol.kind. +// Each TypeSymbol is entered into `Table.types`. +// See also: Table.get_type_symbol. + +pub struct TypeSymbol { +pub: + parent_idx int +pub mut: + info TypeInfo + kind Kind + name string // the internal & source name of the type, i.e. `[5]int`. + cname string // the name with no dots for use in the generated C code + methods []Fn + mod string + is_public bool + language Language + idx int +} + +// max of 8 +pub enum TypeFlag { + optional + variadic + generic + shared_f + atomic_f +} + +/* +To save precious TypeFlag bits the 4 possible ShareTypes are coded in the two + bits `shared` and `atomic_or_rw` (see sharetype_from_flags() below). +*/ +pub enum ShareType { + mut_t + shared_t + atomic_t +} + +pub fn (t ShareType) str() string { + match t { + .mut_t { return 'mut' } + .shared_t { return 'shared' } + .atomic_t { return 'atomic' } + } +} + +// defines special typenames +pub fn (t Type) atomic_typename() string { + idx := t.idx() + match idx { + ast.u32_type_idx { return 'atomic_uint' } + ast.int_type_idx { return 'atomic_int' } + ast.u64_type_idx { return 'atomic_ullong' } + ast.i64_type_idx { return 'atomic_llong' } + else { return 'unknown_atomic' } + } +} + +pub fn sharetype_from_flags(is_shared bool, is_atomic bool) ShareType { + return ShareType((int(is_atomic) << 1) | int(is_shared)) +} + +pub fn (t Type) share() ShareType { + return sharetype_from_flags(t.has_flag(.shared_f), t.has_flag(.atomic_f)) +} + +// return TypeSymbol idx for `t` +[inline] +pub fn (t Type) idx() int { + return u16(t) & 0xffff +} + +[inline] +pub fn (t Type) is_void() bool { + return t == ast.void_type +} + +[inline] +pub fn (t Type) is_full() bool { + return t != 0 && t != ast.void_type +} + +// return nr_muls for `t` +[inline] +pub fn (t Type) nr_muls() int { + return (int(t) >> 16) & 0xff +} + +// return true if `t` is a pointer (nr_muls>0) +[inline] +pub fn (t Type) is_ptr() bool { + // any normal pointer, i.e. &Type, &&Type etc; + // NB: voidptr, charptr and byteptr are NOT included! + return (int(t) >> 16) & 0xff > 0 +} + +[inline] +pub fn (t Type) is_any_kind_of_pointer() bool { + return (int(t) >> 16) & 0xff > 0 || (u16(t) & 0xffff) in ast.pointer_type_idxs +} + +// set nr_muls on `t` and return it +[inline] +pub fn (t Type) set_nr_muls(nr_muls int) Type { + if nr_muls < 0 || nr_muls > 255 { + panic('set_nr_muls: nr_muls must be between 0 & 255') + } + return int(t) & 0xff00ffff | (nr_muls << 16) +} + +// increments nr_nuls on `t` and return it +[inline] +pub fn (t Type) to_ptr() Type { + nr_muls := (int(t) >> 16) & 0xff + if nr_muls == 255 { + panic('to_ptr: nr_muls is already at max of 255') + } + return int(t) & 0xff00ffff | ((nr_muls + 1) << 16) +} + +// decrement nr_muls on `t` and return it +[inline] +pub fn (t Type) deref() Type { + nr_muls := (int(t) >> 16) & 0xff + if nr_muls == 0 { + panic('deref: type `$t` is not a pointer') + } + return int(t) & 0xff00ffff | ((nr_muls - 1) << 16) +} + +// set `flag` on `t` and return `t` +[inline] +pub fn (t Type) set_flag(flag TypeFlag) Type { + return int(t) | (1 << (int(flag) + 24)) +} + +// clear `flag` on `t` and return `t` +[inline] +pub fn (t Type) clear_flag(flag TypeFlag) Type { + return int(t) & ~(1 << (int(flag) + 24)) +} + +// clear all flags +[inline] +pub fn (t Type) clear_flags() Type { + return int(t) & 0xffffff +} + +// return true if `flag` is set on `t` +[inline] +pub fn (t Type) has_flag(flag TypeFlag) bool { + return int(t) & (1 << (int(flag) + 24)) > 0 +} + +// debug returns a verbose representation of the information in ts, useful for tracing/debugging +pub fn (ts TypeSymbol) debug() []string { + mut res := []string{} + ts.dbg_common(mut res) + res << 'info: $ts.info' + res << 'methods ($ts.methods.len): ' + ts.methods.map(it.str()).join(', ') + return res +} + +// same as .debug(), but without the verbose .info and .methods fields +pub fn (ts TypeSymbol) dbg() []string { + mut res := []string{} + ts.dbg_common(mut res) + return res +} + +fn (ts TypeSymbol) dbg_common(mut res []string) { + res << 'idx: 0x$ts.idx.hex()' + res << 'parent_idx: 0x$ts.parent_idx.hex()' + res << 'mod: $ts.mod' + res << 'name: $ts.name' + res << 'cname: $ts.cname' + res << 'kind: $ts.kind' + res << 'is_public: $ts.is_public' + res << 'language: $ts.language' +} + +pub fn (t Type) str() string { + return 'ast.Type(0x$t.hex() = ${u32(t)})' +} + +// debug returns a verbose representation of the information in the type `t`, useful for tracing/debugging +pub fn (t Type) debug() []string { + mut res := []string{} + res << 'idx: 0x${t.idx().hex():-8}' + res << 'type: 0x${t.hex():-8}' + res << 'nr_muls: $t.nr_muls()' + if t.has_flag(.optional) { + res << 'optional' + } + if t.has_flag(.variadic) { + res << 'variadic' + } + if t.has_flag(.generic) { + res << 'generic' + } + if t.has_flag(.shared_f) { + res << 'shared_f' + } + if t.has_flag(.atomic_f) { + res << 'atomic_f' + } + return res +} + +// copy flags & nr_muls from `t_from` to `t` and return `t` +[inline] +pub fn (t Type) derive(t_from Type) Type { + return (0xffff0000 & t_from) | u16(t) +} + +// copy flags from `t_from` to `t` and return `t` +[inline] +pub fn (t Type) derive_add_muls(t_from Type) Type { + return Type((0xff000000 & t_from) | u16(t)).set_nr_muls(t.nr_muls() + t_from.nr_muls()) +} + +// return new type with TypeSymbol idx set to `idx` +[inline] +pub fn new_type(idx int) Type { + if idx < 1 || idx > 65535 { + panic('new_type: idx must be between 1 & 65535') + } + return idx +} + +// return new type with TypeSymbol idx set to `idx` & nr_muls set to `nr_muls` +[inline] +pub fn new_type_ptr(idx int, nr_muls int) Type { + if idx < 1 || idx > 65535 { + panic('new_type_ptr: idx must be between 1 & 65535') + } + if nr_muls < 0 || nr_muls > 255 { + panic('new_type_ptr: nr_muls must be between 0 & 255') + } + return (nr_muls << 16) | u16(idx) +} + +[inline] +pub fn (typ Type) is_pointer() bool { + // builtin pointer types (voidptr, byteptr, charptr) + return typ.idx() in ast.pointer_type_idxs +} + +[inline] +pub fn (typ Type) is_float() bool { + return typ.clear_flags() in ast.float_type_idxs +} + +[inline] +pub fn (typ Type) is_int() bool { + return typ.clear_flags() in ast.integer_type_idxs +} + +[inline] +pub fn (typ Type) is_int_valptr() bool { + return typ.idx() in ast.integer_type_idxs +} + +[inline] +pub fn (typ Type) is_float_valptr() bool { + return typ.idx() in ast.float_type_idxs +} + +[inline] +pub fn (typ Type) is_pure_int() bool { + return int(typ) in ast.integer_type_idxs +} + +[inline] +pub fn (typ Type) is_pure_float() bool { + return int(typ) in ast.float_type_idxs +} + +[inline] +pub fn (typ Type) is_signed() bool { + return typ.idx() in ast.signed_integer_type_idxs +} + +[inline] +pub fn (typ Type) is_unsigned() bool { + return typ.idx() in ast.unsigned_integer_type_idxs +} + +[inline] +pub fn (typ Type) is_int_literal() bool { + return int(typ) == ast.int_literal_type_idx +} + +[inline] +pub fn (typ Type) is_number() bool { + return typ.clear_flags() in ast.number_type_idxs +} + +[inline] +pub fn (typ Type) is_string() bool { + return typ.idx() in ast.string_type_idxs +} + +[inline] +pub fn (typ Type) is_bool() bool { + return typ.idx() == ast.bool_type_idx +} + +pub const ( + void_type_idx = 1 + voidptr_type_idx = 2 + byteptr_type_idx = 3 + charptr_type_idx = 4 + i8_type_idx = 5 + i16_type_idx = 6 + int_type_idx = 7 + i64_type_idx = 8 + byte_type_idx = 9 + u16_type_idx = 10 + u32_type_idx = 11 + u64_type_idx = 12 + f32_type_idx = 13 + f64_type_idx = 14 + char_type_idx = 15 + bool_type_idx = 16 + none_type_idx = 17 + string_type_idx = 18 + rune_type_idx = 19 + array_type_idx = 20 + map_type_idx = 21 + chan_type_idx = 22 + size_t_type_idx = 23 + any_type_idx = 24 + float_literal_type_idx = 25 + int_literal_type_idx = 26 + thread_type_idx = 27 + error_type_idx = 28 + u8_type_idx = 29 +) + +pub const ( + integer_type_idxs = [i8_type_idx, i16_type_idx, int_type_idx, i64_type_idx, + byte_type_idx, u8_type_idx, u16_type_idx, u32_type_idx, u64_type_idx, int_literal_type_idx, + rune_type_idx, + ] + signed_integer_type_idxs = [i8_type_idx, i16_type_idx, int_type_idx, i64_type_idx] + unsigned_integer_type_idxs = [byte_type_idx, u16_type_idx, u32_type_idx, u64_type_idx] + float_type_idxs = [f32_type_idx, f64_type_idx, float_literal_type_idx] + number_type_idxs = [i8_type_idx, i16_type_idx, int_type_idx, i64_type_idx, + byte_type_idx, u16_type_idx, u32_type_idx, u64_type_idx, f32_type_idx, f64_type_idx, + int_literal_type_idx, float_literal_type_idx, rune_type_idx] + pointer_type_idxs = [voidptr_type_idx, byteptr_type_idx, charptr_type_idx] + string_type_idxs = [string_type_idx] +) + +pub const ( + void_type = new_type(void_type_idx) + ovoid_type = new_type(void_type_idx).set_flag(.optional) // the return type of `fn () ?` + voidptr_type = new_type(voidptr_type_idx) + byteptr_type = new_type(byteptr_type_idx) + charptr_type = new_type(charptr_type_idx) + i8_type = new_type(i8_type_idx) + int_type = new_type(int_type_idx) + i16_type = new_type(i16_type_idx) + i64_type = new_type(i64_type_idx) + byte_type = new_type(byte_type_idx) + u8_type = new_type(u8_type_idx) + u16_type = new_type(u16_type_idx) + u32_type = new_type(u32_type_idx) + u64_type = new_type(u64_type_idx) + f32_type = new_type(f32_type_idx) + f64_type = new_type(f64_type_idx) + char_type = new_type(char_type_idx) + bool_type = new_type(bool_type_idx) + none_type = new_type(none_type_idx) + string_type = new_type(string_type_idx) + rune_type = new_type(rune_type_idx) + array_type = new_type(array_type_idx) + map_type = new_type(map_type_idx) + chan_type = new_type(chan_type_idx) + any_type = new_type(any_type_idx) + float_literal_type = new_type(float_literal_type_idx) + int_literal_type = new_type(int_literal_type_idx) + thread_type = new_type(thread_type_idx) + error_type = new_type(error_type_idx) + charptr_types = [charptr_type, new_type(char_type_idx).set_nr_muls(1)] + byteptr_types = [byteptr_type, new_type(byte_type_idx).set_nr_muls(1)] + voidptr_types = [voidptr_type, new_type(voidptr_type_idx).set_nr_muls(1)] + cptr_types = merge_types(voidptr_types, byteptr_types, charptr_types) +) + +pub fn merge_types(params ...[]Type) []Type { + mut res := []Type{} + for types in params { + res << types + } + return res +} + +pub const ( + // must be in the same order as the idx consts above + builtin_type_names = ['void', 'voidptr', 'charptr', 'byteptr', 'i8', 'i16', 'int', 'i64', 'u16', + 'u32', 'u64', 'int_literal', 'f32', 'f64', 'float_literal', 'string', 'char', 'byte', 'bool', + 'none', 'array', 'array_fixed', 'map', 'chan', 'any', 'struct', 'mapnode', 'size_t', 'rune', + 'thread', 'Error', 'u8'] +) + +pub struct MultiReturn { +pub mut: + types []Type +} + +pub struct FnType { +pub mut: + is_anon bool + has_decl bool + func Fn +} + +// returns TypeSymbol kind only if there are no type modifiers +pub fn (t &Table) type_kind(typ Type) Kind { + if typ.nr_muls() > 0 || typ.has_flag(.optional) { + return Kind.placeholder + } + return t.get_type_symbol(typ).kind +} + +pub enum Kind { + placeholder + void + voidptr + byteptr + charptr + i8 + i16 + int + i64 + byte + u8 + u16 + u32 + u64 + f32 + f64 + char + size_t + rune + bool + none_ + string + array + array_fixed + map + chan + any + struct_ + generic_struct_inst + multi_return + sum_type + alias + enum_ + function + interface_ + float_literal + int_literal + aggregate + thread +} + +pub fn (t &TypeSymbol) str() string { + return t.name +} + +[inline] +pub fn (t &TypeSymbol) enum_info() Enum { + match mut t.info { + Enum { return t.info } + else { panic('TypeSymbol.enum_info(): no enum info for type: $t.name') } + } +} + +[inline] +pub fn (t &TypeSymbol) mr_info() MultiReturn { + match mut t.info { + MultiReturn { return t.info } + else { panic('TypeSymbol.mr_info(): no multi return info for type: $t.name') } + } +} + +[inline] +pub fn (t &TypeSymbol) array_info() Array { + match mut t.info { + Array { return t.info } + else { panic('TypeSymbol.array_info(): no array info for type: $t.name') } + } +} + +[inline] +pub fn (t &TypeSymbol) array_fixed_info() ArrayFixed { + match mut t.info { + ArrayFixed { return t.info } + else { panic('TypeSymbol.array_fixed(): no array fixed info for type: $t.name') } + } +} + +[inline] +pub fn (t &TypeSymbol) chan_info() Chan { + match mut t.info { + Chan { return t.info } + else { panic('TypeSymbol.chan_info(): no chan info for type: $t.name') } + } +} + +[inline] +pub fn (t &TypeSymbol) thread_info() Thread { + match mut t.info { + Thread { return t.info } + else { panic('TypeSymbol.thread_info(): no thread info for type: $t.name') } + } +} + +[inline] +pub fn (t &TypeSymbol) map_info() Map { + match mut t.info { + Map { return t.info } + else { panic('TypeSymbol.map_info(): no map info for type: $t.name') } + } +} + +[inline] +pub fn (t &TypeSymbol) struct_info() Struct { + match mut t.info { + Struct { return t.info } + else { panic('TypeSymbol.struct_info(): no struct info for type: $t.name') } + } +} + +[inline] +pub fn (t &TypeSymbol) sumtype_info() SumType { + match mut t.info { + SumType { return t.info } + else { panic('TypeSymbol.sumtype_info(): no sumtype info for type: $t.name') } + } +} + +pub fn (t &TypeSymbol) is_heap() bool { + if t.kind == .struct_ { + info := t.info as Struct + return info.is_heap + } else { + return false + } +} + +/* +pub fn (t TypeSymbol) str() string { + return t.name +} +*/ +pub fn (mut t Table) register_builtin_type_symbols() { + // reserve index 0 so nothing can go there + // save index check, 0 will mean not found + t.register_type_symbol(kind: .placeholder, name: 'reserved_0') + t.register_type_symbol(kind: .void, name: 'void', cname: 'void', mod: 'builtin') + t.register_type_symbol(kind: .voidptr, name: 'voidptr', cname: 'voidptr', mod: 'builtin') + t.register_type_symbol(kind: .byteptr, name: 'byteptr', cname: 'byteptr', mod: 'builtin') + t.register_type_symbol(kind: .charptr, name: 'charptr', cname: 'charptr', mod: 'builtin') + t.register_type_symbol(kind: .i8, name: 'i8', cname: 'i8', mod: 'builtin') + t.register_type_symbol(kind: .i16, name: 'i16', cname: 'i16', mod: 'builtin') + t.register_type_symbol(kind: .int, name: 'int', cname: 'int', mod: 'builtin') + t.register_type_symbol(kind: .i64, name: 'i64', cname: 'i64', mod: 'builtin') + t.register_type_symbol(kind: .byte, name: 'byte', cname: 'byte', mod: 'builtin') + t.register_type_symbol(kind: .u16, name: 'u16', cname: 'u16', mod: 'builtin') + t.register_type_symbol(kind: .u32, name: 'u32', cname: 'u32', mod: 'builtin') + t.register_type_symbol(kind: .u64, name: 'u64', cname: 'u64', mod: 'builtin') + t.register_type_symbol(kind: .f32, name: 'f32', cname: 'f32', mod: 'builtin') + t.register_type_symbol(kind: .f64, name: 'f64', cname: 'f64', mod: 'builtin') + t.register_type_symbol(kind: .char, name: 'char', cname: 'char', mod: 'builtin') + t.register_type_symbol(kind: .bool, name: 'bool', cname: 'bool', mod: 'builtin') + t.register_type_symbol(kind: .none_, name: 'none', cname: 'none', mod: 'builtin') + t.register_type_symbol(kind: .string, name: 'string', cname: 'string', mod: 'builtin') + t.register_type_symbol(kind: .rune, name: 'rune', cname: 'rune', mod: 'builtin') + t.register_type_symbol(kind: .array, name: 'array', cname: 'array', mod: 'builtin') + t.register_type_symbol(kind: .map, name: 'map', cname: 'map', mod: 'builtin') + t.register_type_symbol(kind: .chan, name: 'chan', cname: 'chan', mod: 'builtin') + t.register_type_symbol(kind: .size_t, name: 'size_t', cname: 'size_t', mod: 'builtin') + t.register_type_symbol(kind: .any, name: 'any', cname: 'any', mod: 'builtin') + t.register_type_symbol( + kind: .float_literal + name: 'float literal' + cname: 'float_literal' + mod: 'builtin' + ) + t.register_type_symbol( + kind: .int_literal + name: 'int literal' + cname: 'int_literal' + mod: 'builtin' + ) + t.register_type_symbol( + kind: .thread + name: 'thread' + cname: '__v_thread' + mod: 'builtin' + info: Thread{ + return_type: ast.void_type + } + ) + t.register_type_symbol(kind: .interface_, name: 'IError', cname: 'IError', mod: 'builtin') + t.register_type_symbol(kind: .u8, name: 'zu8', cname: 'zu8', mod: 'builtin') +} + +[inline] +pub fn (t &TypeSymbol) is_pointer() bool { + return t.kind in [.byteptr, .charptr, .voidptr] +} + +[inline] +pub fn (t &TypeSymbol) is_int() bool { + res := t.kind in [.i8, .i16, .int, .i64, .byte, .u16, .u32, .u64, .int_literal, .rune] + if !res && t.kind == .alias { + return (t.info as Alias).parent_type.is_number() + } + return res +} + +[inline] +pub fn (t &TypeSymbol) is_float() bool { + return t.kind in [.f32, .f64, .float_literal] +} + +[inline] +pub fn (t &TypeSymbol) is_string() bool { + return t.kind == .string +} + +[inline] +pub fn (t &TypeSymbol) is_number() bool { + return t.is_int() || t.is_float() +} + +[inline] +pub fn (t &TypeSymbol) is_primitive() bool { + return t.is_number() || t.is_pointer() || t.is_string() +} + +[inline] +pub fn (t &TypeSymbol) is_builtin() bool { + return t.mod == 'builtin' +} + +// for debugging/errors only, perf is not an issue +pub fn (k Kind) str() string { + k_str := match k { + .placeholder { 'placeholder' } + .void { 'void' } + .voidptr { 'voidptr' } + .charptr { 'charptr' } + .byteptr { 'byteptr' } + .struct_ { 'struct' } + .int { 'int' } + .i8 { 'i8' } + .i16 { 'i16' } + .i64 { 'i64' } + .byte { 'byte' } + .u8 { 'u8' } + .u16 { 'u16' } + .u32 { 'u32' } + .u64 { 'u64' } + .int_literal { 'int_literal' } + .f32 { 'f32' } + .f64 { 'f64' } + .float_literal { 'float_literal' } + .string { 'string' } + .char { 'char' } + .bool { 'bool' } + .size_t { 'size_t' } + .none_ { 'none' } + .array { 'array' } + .array_fixed { 'array_fixed' } + .map { 'map' } + .chan { 'chan' } + .multi_return { 'multi_return' } + .sum_type { 'sum_type' } + .alias { 'alias' } + .enum_ { 'enum' } + .any { 'any' } + .function { 'function' } + .interface_ { 'interface' } + .generic_struct_inst { 'generic_struct_inst' } + .rune { 'rune' } + .aggregate { 'aggregate' } + .thread { 'thread' } + } + return k_str +} + +pub fn (kinds []Kind) str() string { + mut kinds_str := '' + for i, k in kinds { + kinds_str += k.str() + if i < kinds.len - 1 { + kinds_str += '_' + } + } + return kinds_str +} + +pub struct Struct { +pub: + attrs []Attr +pub mut: + embeds []Type + fields []StructField + is_typedef bool // C. [typedef] + is_union bool + is_heap bool + is_generic bool + generic_types []Type + concrete_types []Type + parent_type Type +} + +// instantiation of a generic struct +pub struct GenericStructInst { +pub mut: + parent_idx int // idx of the base generic struct + concrete_types []Type // concrete types, e.g. +} + +pub struct Interface { +pub mut: + types []Type // all types that implement this interface + fields []StructField + methods []Fn + ifaces []Type + // generic interface support + is_generic bool + generic_types []Type + concrete_types []Type + parent_type Type +} + +pub struct Enum { +pub: + vals []string + is_flag bool + is_multi_allowed bool +} + +pub struct Alias { +pub: + parent_type Type + language Language + is_import bool +} + +pub struct Aggregate { +mut: + fields []StructField // used for faster lookup inside the module +pub: + types []Type +} + +/* +pub struct Field { +pub: + name string +pub mut: + typ Type + default_expr Expr + has_default_expr bool + default_expr_typ Type + default_val string + attrs []Attr + is_pub bool + is_mut bool + is_global bool +} +*/ + +pub fn (f &StructField) equals(o &StructField) bool { + // TODO: f.is_mut == o.is_mut was removed here to allow read only access + // to (mut/not mut), but otherwise equal fields; some other new checks are needed: + // - if node is declared mut, and we mutate node.stmts, all stmts fields must be mutable + // - same goes for pub and global, if we call the field from another module + return f.name == o.name && f.typ == o.typ && f.is_pub == o.is_pub && f.is_global == o.is_global +} + +pub struct Array { +pub: + nr_dims int +pub mut: + elem_type Type +} + +pub struct ArrayFixed { +pub: + size int + size_expr Expr // used by fmt for e.g. ´[my_const]byte´ +pub mut: + elem_type Type +} + +pub struct Chan { +pub mut: + elem_type Type + is_mut bool +} + +pub struct Thread { +pub mut: + return_type Type +} + +pub struct Map { +pub mut: + key_type Type + value_type Type +} + +pub struct SumType { +pub: + variants []Type +pub mut: + fields []StructField + found_fields bool + // generic sumtype support + is_generic bool + generic_types []Type + concrete_types []Type + parent_type Type +} + +// human readable type name +pub fn (t &Table) type_to_str(typ Type) string { + return t.type_to_str_using_aliases(typ, map[string]string{}) +} + +// type name in code (for builtin) +pub fn (mytable &Table) type_to_code(t Type) string { + match t { + ast.int_literal_type, ast.float_literal_type { return mytable.get_type_symbol(t).kind.str() } + else { return mytable.type_to_str_using_aliases(t, map[string]string{}) } + } +} + +// import_aliases is a map of imported symbol aliases 'module.Type' => 'Type' +pub fn (t &Table) type_to_str_using_aliases(typ Type, import_aliases map[string]string) string { + sym := t.get_type_symbol(typ) + mut res := sym.name + // Note, that the duplication of code in some of the match branches here + // is VERY deliberate. DO NOT be tempted to use `else {}` instead, because + // that strongly reduces the usefullness of the exhaustive checking that + // match does. + // Using else{} here led to subtle bugs in vfmt discovered *months* + // after the original code was written. + // It is important that each case here is handled *explicitly* and + // *clearly*, and that when a new kind is added, it should also be handled + // explicitly. + match sym.kind { + .int_literal, .float_literal { + res = sym.name + } + .i8, .i16, .int, .i64, .byte, .u8, .u16, .u32, .u64, .f32, .f64, .char, .rune, .string, + .bool, .none_, .byteptr, .voidptr, .charptr { + // primitive types + if sym.kind == .byteptr { + res = '&byte' + } else if sym.kind == .charptr { + res = '&char' + } else { + res = sym.kind.str() + } + } + .array { + if typ == ast.array_type { + return 'array' + } + if typ.has_flag(.variadic) { + res = t.type_to_str_using_aliases(t.value_type(typ), import_aliases) + } else { + if sym.info is Array { + elem_str := t.type_to_str_using_aliases(sym.info.elem_type, import_aliases) + res = '[]$elem_str' + } else { + res = 'array' + } + } + } + .array_fixed { + info := sym.info as ArrayFixed + elem_str := t.type_to_str_using_aliases(info.elem_type, import_aliases) + if info.size_expr is EmptyExpr { + res = '[$info.size]$elem_str' + } else { + res = '[$info.size_expr]$elem_str' + } + } + .chan { + // TODO currently the `chan` struct in builtin is not considered a struct but a chan + if sym.mod != 'builtin' && sym.name != 'chan' { + info := sym.info as Chan + mut elem_type := info.elem_type + mut mut_str := '' + if info.is_mut { + mut_str = 'mut ' + elem_type = elem_type.set_nr_muls(elem_type.nr_muls() - 1) + } + elem_str := t.type_to_str_using_aliases(elem_type, import_aliases) + res = 'chan $mut_str$elem_str' + } + } + .function { + info := sym.info as FnType + if !t.is_fmt { + res = t.fn_signature(info.func, type_only: true) + } else { + if res.starts_with('fn (') { + // fn foo () + has_names := info.func.params.any(it.name.len > 0) + res = t.fn_signature_using_aliases(info.func, import_aliases, + type_only: !has_names + ) + } else { + // FnFoo + res = t.shorten_user_defined_typenames(res, import_aliases) + } + } + } + .map { + if int(typ) == ast.map_type_idx { + return 'map' + } + info := sym.info as Map + key_str := t.type_to_str_using_aliases(info.key_type, import_aliases) + val_str := t.type_to_str_using_aliases(info.value_type, import_aliases) + res = 'map[$key_str]$val_str' + } + .multi_return { + res = '(' + info := sym.info as MultiReturn + for i, typ2 in info.types { + if i > 0 { + res += ', ' + } + res += t.type_to_str_using_aliases(typ2, import_aliases) + } + res += ')' + } + .struct_, .interface_, .sum_type { + if typ.has_flag(.generic) { + match sym.info { + Struct, Interface, SumType { + res += '<' + for i, gtyp in sym.info.generic_types { + res += t.get_type_symbol(gtyp).name + if i != sym.info.generic_types.len - 1 { + res += ', ' + } + } + res += '>' + } + else {} + } + } else { + res = t.shorten_user_defined_typenames(res, import_aliases) + } + } + .generic_struct_inst { + info := sym.info as GenericStructInst + res = sym.name.all_before('<') + res += '<' + for i, ctyp in info.concrete_types { + res += t.get_type_symbol(ctyp).name + if i != info.concrete_types.len - 1 { + res += ', ' + } + } + res += '>' + res = t.shorten_user_defined_typenames(res, import_aliases) + } + .void { + if typ.has_flag(.optional) { + return '?' + } + return 'void' + } + .thread { + rtype := sym.thread_info().return_type + if rtype != 1 { + res = 'thread ' + t.type_to_str_using_aliases(rtype, import_aliases) + } + } + .alias, .any, .size_t, .aggregate, .placeholder, .enum_ { + res = t.shorten_user_defined_typenames(res, import_aliases) + } + } + mut nr_muls := typ.nr_muls() + if typ.has_flag(.shared_f) { + nr_muls-- + res = 'shared ' + res + } + if nr_muls > 0 { + res = strings.repeat(`&`, nr_muls) + res + } + if typ.has_flag(.optional) { + res = '?' + res + } + return res +} + +fn (t Table) shorten_user_defined_typenames(originalname string, import_aliases map[string]string) string { + mut res := originalname + if t.cmod_prefix.len > 0 && res.starts_with(t.cmod_prefix) { + // cur_mod.Type => Type + res = res.replace_once(t.cmod_prefix, '') + } else if res in import_aliases { + res = import_aliases[res] + } else { + // types defined by the user + // mod.submod.submod2.Type => submod2.Type + mut parts := res.split('.') + if parts.len > 1 { + ind := parts.len - 2 + if t.is_fmt { + // Rejoin the module parts for correct usage of aliases + parts[ind] = parts[..ind + 1].join('.') + } + if parts[ind] in import_aliases { + parts[ind] = import_aliases[parts[ind]] + } + + res = parts[ind..].join('.') + } else { + res = parts[0] + } + } + return res +} + +pub struct FnSignatureOpts { + skip_receiver bool + type_only bool +} + +pub fn (t &Table) fn_signature(func &Fn, opts FnSignatureOpts) string { + return t.fn_signature_using_aliases(func, map[string]string{}, opts) +} + +pub fn (t &Table) fn_signature_using_aliases(func &Fn, import_aliases map[string]string, opts FnSignatureOpts) string { + mut sb := strings.new_builder(20) + if !opts.skip_receiver { + sb.write_string('fn ') + // TODO write receiver + } + if !opts.type_only { + sb.write_string('$func.name') + } + sb.write_string('(') + start := int(opts.skip_receiver) + for i in start .. func.params.len { + if i != start { + sb.write_string(', ') + } + param := func.params[i] + mut typ := param.typ + if param.is_mut { + typ = typ.deref() + sb.write_string('mut ') + } + if !opts.type_only { + sb.write_string('$param.name ') + } + styp := t.type_to_str_using_aliases(typ, import_aliases) + if i == func.params.len - 1 && func.is_variadic { + sb.write_string('...$styp') + } else { + sb.write_string('$styp') + } + } + sb.write_string(')') + if func.return_type != ast.void_type { + sb.write_string(' ${t.type_to_str_using_aliases(func.return_type, import_aliases)}') + } + return sb.str() +} + +pub fn (t &TypeSymbol) embed_name() string { + // main.Abc => Abc + mut embed_name := t.name.split('.').last() + // remove generic part from name + // Abc => Abc + if embed_name.contains('<') { + embed_name = embed_name.split('<')[0] + } + return embed_name +} + +pub fn (t &TypeSymbol) has_method(name string) bool { + t.find_method(name) or { return false } + return true +} + +pub fn (t &TypeSymbol) find_method(name string) ?Fn { + for method in t.methods { + if method.name == name { + return method + } + } + return none +} + +pub fn (t &TypeSymbol) find_method_with_generic_parent(name string) ?Fn { + if m := t.find_method(name) { + return m + } + mut table := global_table + match t.info { + Struct, Interface, SumType { + if t.info.parent_type.has_flag(.generic) { + parent_sym := table.get_type_symbol(t.info.parent_type) + if x := parent_sym.find_method(name) { + match parent_sym.info { + Struct, Interface, SumType { + mut method := x + generic_names := parent_sym.info.generic_types.map(table.get_type_symbol(it).name) + if rt := table.resolve_generic_to_concrete(method.return_type, + generic_names, t.info.concrete_types) + { + method.return_type = rt + } + method.params = method.params.clone() + for mut param in method.params { + if pt := table.resolve_generic_to_concrete(param.typ, + generic_names, t.info.concrete_types) + { + param.typ = pt + } + } + method.generic_names.clear() + return method + } + else {} + } + } else { + } + } + } + else {} + } + return none +} + +pub fn (t &TypeSymbol) str_method_info() (bool, bool, int) { + mut has_str_method := false + mut expects_ptr := false + mut nr_args := 0 + if sym_str_method := t.find_method('str') { + has_str_method = true + nr_args = sym_str_method.params.len + if nr_args > 0 { + expects_ptr = sym_str_method.params[0].typ.is_ptr() + } + } + return has_str_method, expects_ptr, nr_args +} + +pub fn (t &TypeSymbol) find_field(name string) ?StructField { + match t.info { + Aggregate { return t.info.find_field(name) } + Struct { return t.info.find_field(name) } + Interface { return t.info.find_field(name) } + SumType { return t.info.find_field(name) } + else { return none } + } +} + +fn (a &Aggregate) find_field(name string) ?StructField { + for field in a.fields { + if field.name == name { + return field + } + } + return none +} + +pub fn (i &Interface) find_field(name string) ?StructField { + for field in i.fields { + if field.name == name { + return field + } + } + return none +} + +pub fn (i &Interface) find_method(name string) ?Fn { + for method in i.methods { + if method.name == name { + return method + } + } + return none +} + +pub fn (i &Interface) has_method(name string) bool { + if _ := i.find_method(name) { + return true + } + return false +} + +pub fn (s Struct) find_field(name string) ?StructField { + for field in s.fields { + if field.name == name { + return field + } + } + return none +} + +pub fn (s Struct) get_field(name string) StructField { + if field := s.find_field(name) { + return field + } + panic('unknown field `$name`') +} + +pub fn (s &SumType) find_field(name string) ?StructField { + for field in s.fields { + if field.name == name { + return field + } + } + return none +} + +pub fn (i Interface) defines_method(name string) bool { + for method in i.methods { + if method.name == name { + return true + } + } + return false +} diff --git a/v_windows/v/old/vlib/v/ast/types_test.v b/v_windows/v/old/vlib/v/ast/types_test.v new file mode 100644 index 0000000..67e1adc --- /dev/null +++ b/v_windows/v/old/vlib/v/ast/types_test.v @@ -0,0 +1,81 @@ +import v.ast + +fn test_idx() { + mut t := ast.new_type(ast.void_type_idx) + assert t.idx() == ast.void_type_idx + t = ast.new_type(ast.i8_type_idx) + assert t.idx() == ast.i8_type_idx +} + +fn test_muls() { + mut t := ast.new_type(ast.void_type_idx) + idx := t.idx() + assert t.nr_muls() == 0 + for i in 0 .. 32 { + t = t.set_nr_muls(i) + assert t.nr_muls() == i + } + t = t.set_nr_muls(0) + assert t.nr_muls() == 0 + assert t.is_ptr() == false + t = t.to_ptr() + assert t.nr_muls() == 1 + assert t.is_ptr() == true + t = t.to_ptr() + assert t.nr_muls() == 2 + assert t.is_ptr() == true + t = t.deref() + assert t.nr_muls() == 1 + assert t.is_ptr() == true + t = t.deref() + assert t.nr_muls() == 0 + assert t.is_ptr() == false + assert t.idx() == idx +} + +fn test_flags() { + mut t := ast.new_type(ast.void_type_idx) + idx := t.idx() + nr_muls := t.nr_muls() + t = t.set_flag(ast.TypeFlag.optional) + assert t.has_flag(ast.TypeFlag.optional) == true + assert t.has_flag(ast.TypeFlag.variadic) == false + assert t.has_flag(ast.TypeFlag.generic) == false + t = t.set_flag(ast.TypeFlag.variadic) + assert t.has_flag(ast.TypeFlag.optional) == true + assert t.has_flag(ast.TypeFlag.variadic) == true + assert t.has_flag(ast.TypeFlag.generic) == false + t = t.set_flag(ast.TypeFlag.generic) + assert t.has_flag(ast.TypeFlag.optional) == true + assert t.has_flag(ast.TypeFlag.variadic) == true + assert t.has_flag(ast.TypeFlag.generic) == true + assert t.idx() == idx + assert t.nr_muls() == nr_muls + t = t.clear_flag(ast.TypeFlag.optional) + assert t.has_flag(ast.TypeFlag.optional) == false + assert t.has_flag(ast.TypeFlag.variadic) == true + assert t.has_flag(ast.TypeFlag.generic) == true + t = t.clear_flag(ast.TypeFlag.variadic) + assert t.has_flag(ast.TypeFlag.optional) == false + assert t.has_flag(ast.TypeFlag.variadic) == false + assert t.has_flag(ast.TypeFlag.generic) == true + t = t.clear_flag(ast.TypeFlag.generic) + assert t.has_flag(ast.TypeFlag.optional) == false + assert t.has_flag(ast.TypeFlag.variadic) == false + assert t.has_flag(ast.TypeFlag.generic) == false + assert t.idx() == idx + assert t.nr_muls() == nr_muls +} + +fn test_derive() { + mut t := ast.new_type(ast.i8_type_idx) + t = t.set_flag(ast.TypeFlag.generic) + t = t.set_flag(ast.TypeFlag.variadic) + t = t.set_nr_muls(10) + mut t2 := ast.new_type(ast.i16_type_idx) + t2 = t2.derive(t) + assert t2.has_flag(ast.TypeFlag.optional) == false + assert t2.has_flag(ast.TypeFlag.variadic) == true + assert t2.has_flag(ast.TypeFlag.generic) == true + assert t2.nr_muls() == 10 +} diff --git a/v_windows/v/old/vlib/v/ast/walker/walker.v b/v_windows/v/old/vlib/v/ast/walker/walker.v new file mode 100644 index 0000000..75e6c17 --- /dev/null +++ b/v_windows/v/old/vlib/v/ast/walker/walker.v @@ -0,0 +1,37 @@ +module walker + +import v.ast + +// Visitor defines a visit method which is invoked by the walker in each node it encounters. +pub interface Visitor { + visit(node &ast.Node) ? +} + +pub type InspectorFn = fn (node &ast.Node, data voidptr) bool + +struct Inspector { + inspector_callback InspectorFn +mut: + data voidptr +} + +pub fn (i &Inspector) visit(node &ast.Node) ? { + if i.inspector_callback(node, i.data) { + return + } + return error('') +} + +// inspect traverses and checks the AST node on a depth-first order and based on the data given +pub fn inspect(node &ast.Node, data voidptr, inspector_callback InspectorFn) { + walk(Inspector{inspector_callback, data}, node) +} + +// walk traverses the AST using the given visitor +pub fn walk(visitor Visitor, node &ast.Node) { + visitor.visit(node) or { return } + children := node.children() + for child_node in children { + walk(visitor, &child_node) + } +} diff --git a/v_windows/v/old/vlib/v/ast/walker/walker_test.v b/v_windows/v/old/vlib/v/ast/walker/walker_test.v new file mode 100644 index 0000000..6d90fe9 --- /dev/null +++ b/v_windows/v/old/vlib/v/ast/walker/walker_test.v @@ -0,0 +1,66 @@ +import v.ast +import v.ast.walker +import v.parser +import v.pref + +fn parse_text(text string) &ast.File { + tbl := ast.new_table() + prefs := pref.new_preferences() + return parser.parse_text(text, '', tbl, .skip_comments, prefs) +} + +struct NodeByOffset { + pos int +mut: + node ast.Node +} + +fn (mut n NodeByOffset) visit(node &ast.Node) ? { + node_pos := node.position() + if n.pos >= node_pos.pos && n.pos <= node_pos.pos + node_pos.len && node !is ast.File { + n.node = node + return error('') + } + return +} + +fn test_walk() { + source := ' +module main +struct Foo { + name string +} + ' + file := parse_text(source) + mut nbo := NodeByOffset{ + pos: 13 + } + walker.walk(nbo, file) + assert nbo.node is ast.Stmt + stmt := nbo.node as ast.Stmt + assert stmt is ast.StructDecl +} + +fn test_inspect() { + source := ' +module main + ' + file := parse_text(source) + walker.inspect(file, voidptr(0), fn (node &ast.Node, data voidptr) bool { + // Second visit must be ast.Stmt + if node is ast.Stmt { + if node !is ast.Module { + // Proceed to another node + return false + } + assert node is ast.Module + mod := node as ast.Module + assert mod.name == 'main' + return false + } + assert node is ast.File + // True means that the inspector must now + // inspect the ast.File's children + return true + }) +} diff --git a/v_windows/v/old/vlib/v/builder/builder.v b/v_windows/v/old/vlib/v/builder/builder.v new file mode 100644 index 0000000..79598dd --- /dev/null +++ b/v_windows/v/old/vlib/v/builder/builder.v @@ -0,0 +1,475 @@ +module builder + +import os +import v.token +import v.pref +import v.util +import v.ast +import v.vmod +import v.checker +import v.parser +import v.markused +import v.depgraph +import v.callgraph +import v.dotgraph + +pub struct Builder { +pub: + compiled_dir string // contains os.real_path() of the dir of the final file beeing compiled, or the dir itself when doing `v .` + module_path string +mut: + pref &pref.Preferences + checker &checker.Checker + out_name_c string + out_name_js string + max_nr_errors int = 100 + stats_lines int // size of backend generated source code in lines + stats_bytes int // size of backend generated source code in bytes +pub mut: + module_search_paths []string + parsed_files []&ast.File + cached_msvc MsvcResult + table &ast.Table + ccoptions CcompilerOptions +} + +pub fn new_builder(pref &pref.Preferences) Builder { + rdir := os.real_path(pref.path) + compiled_dir := if os.is_dir(rdir) { rdir } else { os.dir(rdir) } + mut table := ast.new_table() + table.is_fmt = false + if pref.use_color == .always { + util.emanager.set_support_color(true) + } + if pref.use_color == .never { + util.emanager.set_support_color(false) + } + msvc := find_msvc(pref.m64) or { + if pref.ccompiler == 'msvc' { + // verror('Cannot find MSVC on this OS') + } + MsvcResult{ + valid: false + } + } + util.timing_set_should_print(pref.show_timings || pref.is_verbose) + if pref.show_callgraph || pref.show_depgraph { + dotgraph.start_digraph() + } + return Builder{ + pref: pref + table: table + checker: checker.new_checker(table, pref) + compiled_dir: compiled_dir + max_nr_errors: if pref.error_limit > 0 { pref.error_limit } else { 100 } + cached_msvc: msvc + } + // max_nr_errors: pref.error_limit ?? 100 TODO potential syntax? +} + +pub fn (mut b Builder) front_stages(v_files []string) ? { + util.timing_start('PARSE') + b.parsed_files = parser.parse_files(v_files, b.table, b.pref) + b.parse_imports() + mut timers := util.get_timers() + timers.show('SCAN') + timers.show('PARSE') + timers.show_if_exists('PARSE stmt') + if b.pref.only_check_syntax { + return error('stop_after_parser') + } +} + +pub fn (mut b Builder) middle_stages() ? { + util.timing_start('CHECK') + b.checker.generic_insts_to_concrete() + b.checker.check_files(b.parsed_files) + util.timing_measure('CHECK') + b.print_warnings_and_errors() + // + b.table.complete_interface_check() + if b.pref.skip_unused { + markused.mark_used(mut b.table, b.pref, b.parsed_files) + } + if b.pref.show_callgraph { + callgraph.show(mut b.table, b.pref, b.parsed_files) + } +} + +pub fn (mut b Builder) front_and_middle_stages(v_files []string) ? { + b.front_stages(v_files) ? + b.middle_stages() ? +} + +// parse all deps from already parsed files +pub fn (mut b Builder) parse_imports() { + mut done_imports := []string{} + if b.pref.is_vsh { + done_imports << 'os' + } + // TODO (joe): decide if this is correct solution. + // in the case of building a module, the actual module files + // are passed via cmd line, so they have already been parsed + // by this stage. note that if one files from a module was + // parsed (but not all of them), then this will cause a problem. + // we could add a list of parsed files instead, but I think + // there is a better solution all around, I will revisit this. + // NOTE: there is a very similar occurance with the way + // internal module test's work, and this was the reason there + // were issues with duplicate declarations, so we should sort + // that out in a similar way. + for file in b.parsed_files { + if file.mod.name != 'main' && file.mod.name !in done_imports { + done_imports << file.mod.name + } + } + // NB: b.parsed_files is appended in the loop, + // so we can not use the shorter `for in` form. + for i := 0; i < b.parsed_files.len; i++ { + ast_file := b.parsed_files[i] + for imp in ast_file.imports { + mod := imp.mod + if mod == 'builtin' { + error_with_pos('cannot import module "builtin"', ast_file.path, imp.pos) + break + } + if mod in done_imports { + continue + } + import_path := b.find_module_path(mod, ast_file.path) or { + // v.parsers[i].error_with_token_index('cannot import module "$mod" (not found)', v.parsers[i].import_ast.get_import_tok_idx(mod)) + // break + error_with_pos('cannot import module "$mod" (not found)', ast_file.path, + imp.pos) + break + } + v_files := b.v_files_from_dir(import_path) + if v_files.len == 0 { + // v.parsers[i].error_with_token_index('cannot import module "$mod" (no .v files in "$import_path")', v.parsers[i].import_ast.get_import_tok_idx(mod)) + error_with_pos('cannot import module "$mod" (no .v files in "$import_path")', + ast_file.path, imp.pos) + } + // Add all imports referenced by these libs + parsed_files := parser.parse_files(v_files, b.table, b.pref) + for file in parsed_files { + mut name := file.mod.name + if name == '' { + name = file.mod.short_name + } + if name != mod { + // v.parsers[pidx].error_with_token_index('bad module definition: ${v.parsers[pidx].file_path} imports module "$mod" but $file is defined as module `$p_mod`', 1 + error_with_pos('bad module definition: $ast_file.path imports module "$mod" but $file.path is defined as module `$name`', + ast_file.path, imp.pos) + } + } + b.parsed_files << parsed_files + done_imports << mod + } + } + b.resolve_deps() + // + if b.pref.print_v_files { + for p in b.parsed_files { + println(p.path) + } + exit(0) + } +} + +pub fn (mut b Builder) resolve_deps() { + graph := b.import_graph() + deps_resolved := graph.resolve() + if b.pref.is_verbose { + eprintln('------ resolved dependencies graph: ------') + eprintln(deps_resolved.display()) + eprintln('------------------------------------------') + } + if b.pref.show_depgraph { + depgraph.show(deps_resolved, b.pref.path) + } + cycles := deps_resolved.display_cycles() + if cycles.len > 1 { + verror('error: import cycle detected between the following modules: \n' + cycles) + } + mut mods := []string{} + for node in deps_resolved.nodes { + mods << node.name + } + if b.pref.is_verbose { + eprintln('------ imported modules: ------') + eprintln(mods.str()) + eprintln('-------------------------------') + } + mut reordered_parsed_files := []&ast.File{} + for m in mods { + for pf in b.parsed_files { + if m == pf.mod.name { + reordered_parsed_files << pf + // eprintln('pf.mod.name: $pf.mod.name | pf.path: $pf.path') + } + } + } + b.table.modules = mods + b.parsed_files = reordered_parsed_files +} + +// graph of all imported modules +pub fn (b &Builder) import_graph() &depgraph.DepGraph { + builtins := util.builtin_module_parts.clone() + mut graph := depgraph.new_dep_graph() + for p in b.parsed_files { + // eprintln('p.path: $p.path') + mut deps := []string{} + if p.mod.name !in builtins { + deps << 'builtin' + if b.pref.backend == .c { + // TODO JavaScript backend doesn't handle os for now + if b.pref.is_vsh && p.mod.name != 'os' { + deps << 'os' + } + } + } + for m in p.imports { + if m.mod == p.mod.name { + continue + } + deps << m.mod + } + graph.add(p.mod.name, deps) + } + $if trace_import_graph ? { + eprintln(graph.display()) + } + return graph +} + +pub fn (b Builder) v_files_from_dir(dir string) []string { + if !os.exists(dir) { + if dir == 'compiler' && os.is_dir('vlib') { + println('looks like you are trying to build V with an old command') + println('use `v -o v cmd/v` instead of `v -o v compiler`') + } + verror("$dir doesn't exist") + } else if !os.is_dir(dir) { + verror("$dir isn't a directory!") + } + mut files := os.ls(dir) or { panic(err) } + if b.pref.is_verbose { + println('v_files_from_dir ("$dir")') + } + return b.pref.should_compile_filtered_files(dir, files) +} + +pub fn (b Builder) log(s string) { + if b.pref.is_verbose { + println(s) + } +} + +pub fn (b Builder) info(s string) { + if b.pref.is_verbose { + println(s) + } +} + +[inline] +fn module_path(mod string) string { + // submodule support + return mod.replace('.', os.path_separator) +} + +// TODO: try to merge this & util.module functions to create a +// reliable multi use function. see comments in util/module.v +pub fn (b &Builder) find_module_path(mod string, fpath string) ?string { + // support @VROOT/v.mod relative paths: + mut mcache := vmod.get_cache() + vmod_file_location := mcache.get_by_file(fpath) + mod_path := module_path(mod) + mut module_lookup_paths := []string{} + if vmod_file_location.vmod_file.len != 0 + && vmod_file_location.vmod_folder !in b.module_search_paths { + module_lookup_paths << vmod_file_location.vmod_folder + } + module_lookup_paths << b.module_search_paths + module_lookup_paths << os.getwd() + // go up through parents looking for modules a folder. + // we need a proper solution that works most of the time. look at vdoc.get_parent_mod + if fpath.contains(os.path_separator + 'modules' + os.path_separator) { + parts := fpath.split(os.path_separator) + for i := parts.len - 2; i >= 0; i-- { + if parts[i] == 'modules' { + module_lookup_paths << parts[0..i + 1].join(os.path_separator) + break + } + } + } + for search_path in module_lookup_paths { + try_path := os.join_path(search_path, mod_path) + if b.pref.is_verbose { + println(' >> trying to find $mod in $try_path ..') + } + if os.is_dir(try_path) { + if b.pref.is_verbose { + println(' << found $try_path .') + } + return try_path + } + } + // look up through parents + path_parts := fpath.split(os.path_separator) + for i := path_parts.len - 2; i > 0; i-- { + p1 := path_parts[0..i].join(os.path_separator) + try_path := os.join_path(p1, mod_path) + if b.pref.is_verbose { + println(' >> trying to find $mod in $try_path ..') + } + if os.is_dir(try_path) { + return try_path + } + } + smodule_lookup_paths := module_lookup_paths.join(', ') + return error('module "$mod" not found in:\n$smodule_lookup_paths') +} + +fn (b &Builder) show_total_warns_and_errors_stats() { + if b.checker.nr_errors == 0 && b.checker.nr_warnings == 0 && b.checker.nr_notices == 0 { + return + } + if b.pref.is_stats { + estring := util.bold(b.checker.nr_errors.str()) + wstring := util.bold(b.checker.nr_warnings.str()) + nstring := util.bold(b.checker.nr_notices.str()) + println('checker summary: $estring V errors, $wstring V warnings, $nstring V notices') + } +} + +fn (b &Builder) print_warnings_and_errors() { + defer { + b.show_total_warns_and_errors_stats() + } + if b.pref.output_mode == .silent { + if b.checker.nr_errors > 0 { + exit(1) + } + return + } + if b.pref.is_verbose && b.checker.nr_warnings > 1 { + println('$b.checker.nr_warnings warnings') + } + if b.pref.is_verbose && b.checker.nr_notices > 1 { + println('$b.checker.nr_notices notices') + } + if b.checker.nr_notices > 0 && !b.pref.skip_warnings { + for i, err in b.checker.notices { + kind := if b.pref.is_verbose { + '$err.reporter notice #$b.checker.nr_notices:' + } else { + 'notice:' + } + ferror := util.formatted_error(kind, err.message, err.file_path, err.pos) + eprintln(ferror) + if err.details.len > 0 { + eprintln('Details: $err.details') + } + if i > b.max_nr_errors { + return + } + } + } + if b.checker.nr_warnings > 0 && !b.pref.skip_warnings { + for i, err in b.checker.warnings { + kind := if b.pref.is_verbose { + '$err.reporter warning #$b.checker.nr_warnings:' + } else { + 'warning:' + } + ferror := util.formatted_error(kind, err.message, err.file_path, err.pos) + eprintln(ferror) + if err.details.len > 0 { + eprintln('Details: $err.details') + } + // eprintln('') + if i > b.max_nr_errors { + return + } + } + } + // + if b.pref.is_verbose && b.checker.nr_errors > 1 { + println('$b.checker.nr_errors errors') + } + if b.checker.nr_errors > 0 { + for i, err in b.checker.errors { + kind := if b.pref.is_verbose { + '$err.reporter error #$b.checker.nr_errors:' + } else { + 'error:' + } + ferror := util.formatted_error(kind, err.message, err.file_path, err.pos) + eprintln(ferror) + if err.details.len > 0 { + eprintln('Details: $err.details') + } + // eprintln('') + if i > b.max_nr_errors { + return + } + } + b.show_total_warns_and_errors_stats() + exit(1) + } + if b.table.redefined_fns.len > 0 { + mut total_conflicts := 0 + for fn_name in b.table.redefined_fns { + // Find where this function was already declared + mut redefines := []FunctionRedefinition{} + mut redefine_conflicts := map[string]int{} + for file in b.parsed_files { + for stmt in file.stmts { + if stmt is ast.FnDecl { + if stmt.name == fn_name { + fheader := stmt.stringify(b.table, 'main', map[string]string{}) + redefines << FunctionRedefinition{ + fpath: file.path + fline: stmt.pos.line_nr + f: stmt + fheader: fheader + } + redefine_conflicts[fheader]++ + } + } + } + } + if redefines.len > 0 { + eprintln('redefinition of function `$fn_name`') + for redefine in redefines { + eprintln(util.formatted_error('conflicting declaration:', redefine.fheader, + redefine.fpath, redefine.f.pos)) + } + total_conflicts++ + } + } + if total_conflicts > 0 { + b.show_total_warns_and_errors_stats() + exit(1) + } + } +} + +struct FunctionRedefinition { + fpath string + fline int + fheader string + f ast.FnDecl +} + +fn error_with_pos(s string, fpath string, pos token.Position) { + ferror := util.formatted_error('builder error:', s, fpath, pos) + eprintln(ferror) + exit(1) +} + +[noreturn] +fn verror(s string) { + util.verror('builder error', s) +} diff --git a/v_windows/v/old/vlib/v/builder/c.v b/v_windows/v/old/vlib/v/builder/c.v new file mode 100644 index 0000000..20f9f67 --- /dev/null +++ b/v_windows/v/old/vlib/v/builder/c.v @@ -0,0 +1,63 @@ +module builder + +import os +import v.pref +import v.util +import v.gen.c + +pub fn (mut b Builder) gen_c(v_files []string) string { + b.front_and_middle_stages(v_files) or { return '' } + // TODO: move gen.cgen() to c.gen() + util.timing_start('C GEN') + res := c.gen(b.parsed_files, b.table, b.pref) + util.timing_measure('C GEN') + // println('cgen done') + // println(res) + return res +} + +pub fn (mut b Builder) build_c(v_files []string, out_file string) { + b.out_name_c = out_file + b.pref.out_name_c = os.real_path(out_file) + b.info('build_c($out_file)') + output2 := b.gen_c(v_files) + os.write_file(out_file, output2) or { panic(err) } + if b.pref.is_stats { + b.stats_lines = output2.count('\n') + 1 + b.stats_bytes = output2.len + } +} + +pub fn (mut b Builder) compile_c() { + if os.user_os() != 'windows' && b.pref.ccompiler == 'msvc' && !b.pref.out_name.ends_with('.c') { + verror('Cannot build with msvc on $os.user_os()') + } + // cgen.genln('// Generated by V') + // println('compile2()') + if b.pref.is_verbose { + println('all .v files before:') + // println(files) + } + $if windows { + b.find_win_cc() or { verror(no_compiler_error) } + // TODO Probably extend this to other OS's? + } + // v1 compiler files + // v.add_v_files_to_compile() + // v.files << v.dir + // v2 compiler + // b.set_module_lookup_paths() + mut files := b.get_builtin_files() + files << b.get_user_files() + b.set_module_lookup_paths() + if b.pref.is_verbose { + println('all .v files:') + println(files) + } + mut out_name_c := b.get_vtmp_filename(b.pref.out_name, '.tmp.c') + if b.pref.is_shared { + out_name_c = b.get_vtmp_filename(b.pref.out_name, '.tmp.so.c') + } + b.build_c(files, out_name_c) + b.cc() +} diff --git a/v_windows/v/old/vlib/v/builder/cc.v b/v_windows/v/old/vlib/v/builder/cc.v new file mode 100644 index 0000000..95f77ab --- /dev/null +++ b/v_windows/v/old/vlib/v/builder/cc.v @@ -0,0 +1,1005 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module builder + +import os +import v.cflag +import v.pref +import v.util +import v.vcache +import term + +const ( + c_verror_message_marker = 'VERROR_MESSAGE ' + c_error_info = ' +================== +C error. This should never happen. + +If you were not working with C interop, this is a compiler bug, please report the bug using `v bug file.v`. + +https://github.com/vlang/v/issues/new/choose + +You can also use #help on Discord: https://discord.gg/vlang +' + no_compiler_error = ' +================== +Error: no C compiler detected. + +You can find instructions on how to install one in the V wiki: +https://github.com/vlang/v/wiki/Installing-a-C-compiler-on-Windows + +If you think you have one installed, make sure it is in your PATH. +If you do have one in your PATH, please raise an issue on GitHub: +https://github.com/vlang/v/issues/new/choose + +You can also use `v doctor`, to see what V knows about your current environment. + +You can also seek #help on Discord: https://discord.gg/vlang +' +) + +const ( + mingw_cc = 'x86_64-w64-mingw32-gcc' +) + +fn (mut v Builder) find_win_cc() ? { + $if !windows { + return + } + ccompiler_version_res := os.execute('$v.pref.ccompiler -v') + if ccompiler_version_res.exit_code != 0 { + if v.pref.is_verbose { + println('$v.pref.ccompiler not found, looking for msvc...') + } + find_msvc(v.pref.m64) or { + if v.pref.is_verbose { + println('msvc not found, looking for thirdparty/tcc...') + } + vpath := os.dir(pref.vexe_path()) + thirdparty_tcc := os.join_path(vpath, 'thirdparty', 'tcc', 'tcc.exe') + tcc_version_res := os.execute('$thirdparty_tcc -v') + if tcc_version_res.exit_code != 0 { + if v.pref.is_verbose { + println('tcc not found') + } + return error('tcc not found') + } + v.pref.ccompiler = thirdparty_tcc + v.pref.ccompiler_type = .tinyc + return + } + v.pref.ccompiler = 'msvc' + v.pref.ccompiler_type = .msvc + return + } + v.pref.ccompiler_type = pref.cc_from_string(v.pref.ccompiler) +} + +fn (mut v Builder) show_c_compiler_output(res os.Result) { + println('======== C Compiler output ========') + println(res.output) + println('=================================') +} + +fn (mut v Builder) post_process_c_compiler_output(res os.Result) { + if res.exit_code == 0 { + if v.pref.reuse_tmpc { + return + } + for tmpfile in v.pref.cleanup_files { + if os.is_file(tmpfile) { + if v.pref.is_verbose { + eprintln('>> remove tmp file: $tmpfile') + } + os.rm(tmpfile) or { panic(err) } + } + } + return + } + for emsg_marker in [builder.c_verror_message_marker, 'error: include file '] { + if res.output.contains(emsg_marker) { + emessage := res.output.all_after(emsg_marker).all_before('\n').all_before('\r').trim_right('\r\n') + verror(emessage) + } + } + if v.pref.is_debug { + eword := 'error:' + khighlight := if term.can_show_color_on_stdout() { term.red(eword) } else { eword } + println(res.output.trim_right('\r\n').replace(eword, khighlight)) + } else { + if res.output.len < 30 { + println(res.output) + } else { + elines := error_context_lines(res.output, 'error:', 1, 12) + println('==================') + for eline in elines { + println(eline) + } + println('...') + println('==================') + println('(Use `v -cg` to print the entire error message)\n') + } + } + verror(builder.c_error_info) +} + +fn (mut v Builder) rebuild_cached_module(vexe string, imp_path string) string { + res := v.pref.cache_manager.exists('.o', imp_path) or { + if v.pref.is_verbose { + println('Cached $imp_path .o file not found... Building .o file for $imp_path') + } + // do run `v build-module x` always in main vfolder; x can be a relative path + pwd := os.getwd() + vroot := os.dir(vexe) + os.chdir(vroot) + boptions := v.pref.build_options.join(' ') + rebuild_cmd := '$vexe $boptions build-module $imp_path' + vcache.dlog('| Builder.' + @FN, 'vexe: $vexe | imp_path: $imp_path | rebuild_cmd: $rebuild_cmd') + os.system(rebuild_cmd) + rebuilded_o := v.pref.cache_manager.exists('.o', imp_path) or { + panic('could not rebuild cache module for $imp_path, error: $err.msg') + } + os.chdir(pwd) + return rebuilded_o + } + return res +} + +fn (mut v Builder) show_cc(cmd string, response_file string, response_file_content string) { + if v.pref.is_verbose || v.pref.show_cc { + println('') + println('=====================') + println('> C compiler cmd: $cmd') + if v.pref.show_cc { + println('> C compiler response file $response_file:') + println(response_file_content) + } + println('=====================') + } +} + +struct CcompilerOptions { +mut: + guessed_compiler string + shared_postfix string // .so, .dll + // + // + debug_mode bool + is_cc_tcc bool + is_cc_gcc bool + is_cc_msvc bool + is_cc_clang bool + // + env_cflags string // prepended *before* everything else + env_ldflags string // appended *after* everything else + // + args []string // ordinary C options like `-O2` + wargs []string // for `-Wxyz` *exclusively* + pre_args []string // options that should go before .o_args + o_args []string // for `-o target` + source_args []string // for `x.tmp.c` + post_args []string // options that should go after .o_args + linker_flags []string // `-lm` +} + +fn (mut v Builder) setup_ccompiler_options(ccompiler string) { + mut ccoptions := CcompilerOptions{} + // + mut debug_options := ['-g'] + mut optimization_options := ['-O2'] + // arguments for the C compiler + ccoptions.args = [v.pref.cflags, '-std=gnu99'] + ccoptions.wargs = [ + '-Wall', + '-Wextra', + '-Werror', + // if anything, these should be a `v vet` warning instead: + '-Wno-unused-parameter', + '-Wno-unused', + '-Wno-type-limits', + '-Wno-tautological-compare', + // these cause various issues: + '-Wno-shadow' /* the V compiler already catches this for user code, and enabling this causes issues with e.g. the `it` variable */, + '-Wno-int-to-pointer-cast' /* gcc version of the above */, + '-Wno-trigraphs' /* see stackoverflow.com/a/8435413 */, + '-Wno-missing-braces' /* see stackoverflow.com/q/13746033 */, + // enable additional warnings: + '-Wno-unknown-warning' /* if a C compiler does not understand a certain flag, it should just ignore it */, + '-Wno-unknown-warning-option' /* clang equivalent of the above */, + '-Wdate-time', + '-Wduplicated-branches', + '-Wduplicated-cond', + '-Winit-self', + '-Winvalid-pch', + '-Wjump-misses-init', + '-Wlogical-op', + '-Wmultichar', + '-Wnested-externs', + '-Wnull-dereference', + '-Wpacked', + '-Wpointer-arith', + '-Wswitch-enum', + ] + if v.pref.os == .ios { + ccoptions.args << '-fobjc-arc' + } + ccoptions.debug_mode = v.pref.is_debug + ccoptions.guessed_compiler = v.pref.ccompiler + if ccoptions.guessed_compiler == 'cc' && v.pref.is_prod { + // deliberately guessing only for -prod builds for performance reasons + ccversion := os.execute('cc --version') + if ccversion.exit_code == 0 { + if ccversion.output.contains('This is free software;') + && ccversion.output.contains('Free Software Foundation, Inc.') { + ccoptions.guessed_compiler = 'gcc' + } + if ccversion.output.contains('clang version ') { + ccoptions.guessed_compiler = 'clang' + } + } + } + // + ccoptions.is_cc_tcc = ccompiler.contains('tcc') || ccoptions.guessed_compiler == 'tcc' + ccoptions.is_cc_gcc = ccompiler.contains('gcc') || ccoptions.guessed_compiler == 'gcc' + ccoptions.is_cc_msvc = ccompiler.contains('msvc') || ccoptions.guessed_compiler == 'msvc' + ccoptions.is_cc_clang = ccompiler.contains('clang') || ccoptions.guessed_compiler == 'clang' + // For C++ we must be very tolerant + if ccoptions.guessed_compiler.contains('++') { + ccoptions.args << '-fpermissive' + ccoptions.args << '-w' + } + if ccoptions.is_cc_clang { + if ccoptions.debug_mode { + debug_options = ['-g', '-O0'] + } + optimization_options = ['-O3'] + mut have_flto := true + $if openbsd { + have_flto = false + } + if have_flto { + optimization_options << '-flto' + } + ccoptions.wargs << [ + '-Wno-tautological-bitwise-compare', + '-Wno-enum-conversion' /* used in vlib/sokol, where C enums in C structs are typed as V structs instead */, + '-Wno-sometimes-uninitialized' /* produced after exhaustive matches */, + '-Wno-int-to-void-pointer-cast', + ] + } + if ccoptions.is_cc_gcc { + if ccoptions.debug_mode { + debug_options = ['-g', '-no-pie'] + } + optimization_options = ['-O3', '-fno-strict-aliasing', '-flto'] + } + // + if ccoptions.debug_mode { + ccoptions.args << debug_options + // $if macos { + // args << '-ferror-limit=5000' + // } + } + if v.pref.is_prod { + // don't warn for vlib tests + if ccoptions.is_cc_tcc && !(v.parsed_files.len > 0 + && v.parsed_files.last().path.contains('vlib')) { + eprintln('Note: tcc is not recommended for -prod builds') + } + ccoptions.args << optimization_options + } + if v.pref.is_prod && !ccoptions.debug_mode { + // sokol and other C libraries that use asserts + // have much better performance when NDEBUG is defined + // See also http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf + ccoptions.args << '-DNDEBUG' + } + if v.pref.sanitize { + ccoptions.args << '-fsanitize=leak' + } + // + ccoptions.shared_postfix = '.so' + $if macos { + ccoptions.shared_postfix = '.dylib' + } $else $if windows { + ccoptions.shared_postfix = '.dll' + } + if v.pref.is_shared { + ccoptions.linker_flags << '-shared' + ccoptions.args << '-fPIC' // -Wl,-z,defs' + } + if v.pref.is_bare { + ccoptions.args << '-fno-stack-protector' + ccoptions.args << '-ffreestanding' + ccoptions.linker_flags << '-static' + ccoptions.linker_flags << '-nostdlib' + } + if ccoptions.debug_mode && os.user_os() != 'windows' && v.pref.build_mode != .build_module { + ccoptions.linker_flags << '-rdynamic' // needed for nicer symbolic backtraces + } + + if ccompiler != 'msvc' && v.pref.os != .freebsd { + ccoptions.wargs << '-Werror=implicit-function-declaration' + } + if v.pref.is_liveshared || v.pref.is_livemain { + if (v.pref.os == .linux || os.user_os() == 'linux') && v.pref.build_mode != .build_module { + ccoptions.linker_flags << '-rdynamic' + } + if v.pref.os == .macos || os.user_os() == 'macos' { + ccoptions.args << '-flat_namespace' + } + } + // macOS code can include objective C TODO remove once objective C is replaced with C + if v.pref.os == .macos || v.pref.os == .ios { + if !ccoptions.is_cc_tcc { + ccoptions.source_args << '-x objective-c' + } + } + // The C file we are compiling + ccoptions.source_args << '"$v.out_name_c"' + if v.pref.os == .macos { + ccoptions.source_args << '-x none' + } + // Min macos version is mandatory I think? + if v.pref.os == .macos { + ccoptions.post_args << '-mmacosx-version-min=10.7' + } else if v.pref.os == .ios { + ccoptions.post_args << '-miphoneos-version-min=10.0' + } else if v.pref.os == .windows { + ccoptions.post_args << '-municode' + } + cflags := v.get_os_cflags() + ccoptions.o_args << cflags.c_options_only_object_files() + defines, others, libs := cflags.defines_others_libs() + ccoptions.pre_args << defines + ccoptions.pre_args << others + ccoptions.linker_flags << libs + // TODO: why is this duplicated from above? + if v.pref.use_cache && v.pref.build_mode != .build_module { + // vexe := pref.vexe_path() + // cached_modules := ['builtin', 'os', 'math', 'strconv', 'strings', 'hash'], // , 'strconv.ftoa'] + // for cfile in cached_modules { + // ofile := os.join_path(pref.default_module_path, 'cache', 'vlib', cfile.replace('.', '/') + + // '.o') + // if !os.exists(ofile) { + // println('${cfile}.o is missing. Building...') + // println('$vexe build-module vlib/$cfile') + // os.system('$vexe build-module vlib/$cfile') + // } + // args << ofile + // } + if !ccoptions.is_cc_tcc { + $if linux { + ccoptions.linker_flags << '-Xlinker -z' + ccoptions.linker_flags << '-Xlinker muldefs' + } + } + } + if ccoptions.is_cc_tcc && 'no_backtrace' !in v.pref.compile_defines { + ccoptions.post_args << '-bt25' + } + // Without these libs compilation will fail on Linux + // || os.user_os() == 'linux' + if !v.pref.is_bare && v.pref.build_mode != .build_module + && v.pref.os in [.linux, .freebsd, .openbsd, .netbsd, .dragonfly, .solaris, .haiku] { + if v.pref.os in [.freebsd, .netbsd] { + // Free/NetBSD: backtrace needs execinfo library while linking + ccoptions.linker_flags << '-lexecinfo' + } + } + ccoptions.env_cflags = os.getenv('CFLAGS') + ccoptions.env_ldflags = os.getenv('LDFLAGS') + $if trace_ccoptions ? { + println('>>> setup_ccompiler_options ccompiler: $ccompiler') + println('>>> setup_ccompiler_options ccoptions: $ccoptions') + } + v.ccoptions = ccoptions + // setup the cache too, so that different compilers/options do not interfere: + v.pref.cache_manager.set_temporary_options(v.thirdparty_object_args(v.ccoptions, [ + ccoptions.guessed_compiler, + ])) +} + +fn (v &Builder) all_args(ccoptions CcompilerOptions) []string { + mut all := []string{} + all << ccoptions.env_cflags + if v.pref.is_cstrict { + all << ccoptions.wargs + } + all << ccoptions.args + all << ccoptions.o_args + all << ccoptions.pre_args + all << ccoptions.source_args + all << ccoptions.post_args + all << ccoptions.linker_flags + all << ccoptions.env_ldflags + return all +} + +fn (v &Builder) thirdparty_object_args(ccoptions CcompilerOptions, middle []string) []string { + mut all := []string{} + all << ccoptions.env_cflags + all << ccoptions.args + all << middle + all << ccoptions.env_ldflags + return all +} + +fn (mut v Builder) setup_output_name() { + if !v.pref.is_shared && v.pref.build_mode != .build_module && os.user_os() == 'windows' + && !v.pref.out_name.ends_with('.exe') { + v.pref.out_name += '.exe' + } + // Output executable name + v.log('cc() isprod=$v.pref.is_prod outname=$v.pref.out_name') + if v.pref.is_shared { + if !v.pref.out_name.ends_with(v.ccoptions.shared_postfix) { + v.pref.out_name += v.ccoptions.shared_postfix + } + } + if v.pref.build_mode == .build_module { + v.pref.out_name = v.pref.cache_manager.postfix_with_key2cpath('.o', v.pref.path) // v.out_name + if v.pref.is_verbose { + println('Building $v.pref.path to $v.pref.out_name ...') + } + v.pref.cache_manager.save('.description.txt', v.pref.path, '${v.pref.path:-30} @ $v.pref.cache_manager.vopts\n') or { + panic(err) + } + // println('v.ast.imports:') + // println(v.ast.imports) + } + if os.is_dir(v.pref.out_name) { + verror("'$v.pref.out_name' is a directory") + } + v.ccoptions.o_args << '-o "$v.pref.out_name"' +} + +fn (mut v Builder) dump_c_options(all_args []string) { + if v.pref.dump_c_flags != '' { + non_empty_args := all_args.filter(it != '').join('\n') + '\n' + if v.pref.dump_c_flags == '-' { + print(non_empty_args) + } else { + os.write_file(v.pref.dump_c_flags, non_empty_args) or { panic(err) } + } + } +} + +fn (mut v Builder) cc() { + if os.executable().contains('vfmt') { + return + } + if v.pref.is_verbose { + println('builder.cc() pref.out_name="$v.pref.out_name"') + } + if v.pref.only_check_syntax { + if v.pref.is_verbose { + println('builder.cc returning early, since pref.only_check_syntax is true') + } + return + } + if v.pref.should_output_to_stdout() { + // output to stdout + content := os.read_file(v.out_name_c) or { panic(err) } + println(content) + os.rm(v.out_name_c) or {} + return + } + // whether to just create a .c or .js file and exit, for example: `v -o v.c cmd.v` + ends_with_c := v.pref.out_name.ends_with('.c') + ends_with_js := v.pref.out_name.ends_with('.js') + if ends_with_c || ends_with_js { + v.pref.skip_running = true + msg_mv := 'os.mv_by_cp $v.out_name_c => $v.pref.out_name' + util.timing_start(msg_mv) + // v.out_name_c may be on a different partition than v.out_name + os.mv_by_cp(v.out_name_c, v.pref.out_name) or { panic(err) } + util.timing_measure(msg_mv) + return + } + // Cross compiling for Windows + if v.pref.os == .windows { + $if !windows { + v.cc_windows_cross() + return + } + } + // Cross compiling for Linux + if v.pref.os == .linux { + $if !linux { + v.cc_linux_cross() + return + } + } + // + vexe := pref.vexe_path() + vdir := os.dir(vexe) + mut tried_compilation_commands := []string{} + mut tcc_output := os.Result{} + original_pwd := os.getwd() + for { + // try to compile with the choosen compiler + // if compilation fails, retry again with another + mut ccompiler := v.pref.ccompiler + if v.pref.os == .ios { + ios_sdk := if v.pref.is_ios_simulator { 'iphonesimulator' } else { 'iphoneos' } + ios_sdk_path_res := os.execute_or_exit('xcrun --sdk $ios_sdk --show-sdk-path') + mut isysroot := ios_sdk_path_res.output.replace('\n', '') + arch := if v.pref.is_ios_simulator { + '-arch x86_64' + } else { + '-arch armv7 -arch armv7s -arch arm64' + } + ccompiler = 'xcrun --sdk iphoneos clang -isysroot $isysroot $arch' + } + v.setup_ccompiler_options(ccompiler) + v.build_thirdparty_obj_files() + v.setup_output_name() + // + mut libs := []string{} // builtin.o os.o http.o etc + if v.pref.build_mode == .build_module { + v.ccoptions.pre_args << '-c' + } else if v.pref.use_cache { + mut built_modules := []string{} + builtin_obj_path := v.rebuild_cached_module(vexe, 'vlib/builtin') + libs << builtin_obj_path + for ast_file in v.parsed_files { + if v.pref.is_test && ast_file.mod.name != 'main' { + imp_path := v.find_module_path(ast_file.mod.name, ast_file.path) or { + verror('cannot import module "$ast_file.mod.name" (not found)') + break + } + obj_path := v.rebuild_cached_module(vexe, imp_path) + libs << obj_path + built_modules << ast_file.mod.name + } + for imp_stmt in ast_file.imports { + imp := imp_stmt.mod + // strconv is already imported inside builtin, so skip generating its object file + // TODO: incase we have other modules with the same name, make sure they are vlib + // is this even doign anything? + if imp in ['strconv', 'strings'] { + continue + } + if imp in built_modules { + continue + } + if util.should_bundle_module(imp) { + continue + } + // not working + if imp == 'webview' { + continue + } + // The problem is cmd/v is in module main and imports + // the relative module named help, which is built as cmd.v.help not help + // currently this got this workign by building into main, see ast.FnDecl in cgen + if imp == 'help' { + continue + } + // we are skipping help manually above, this code will skip all relative imports + // if os.is_dir(af_base_dir + os.path_separator + mod_path) { + // continue + // } + // mod_path := imp.replace('.', os.path_separator) + // imp_path := os.join_path('vlib', mod_path) + imp_path := v.find_module_path(imp, ast_file.path) or { + verror('cannot import module "$imp" (not found)') + break + } + obj_path := v.rebuild_cached_module(vexe, imp_path) + libs << obj_path + if obj_path.ends_with('vlib/ui.o') { + v.ccoptions.post_args << '-framework Cocoa' + v.ccoptions.post_args << '-framework Carbon' + } + built_modules << imp + } + } + v.ccoptions.post_args << libs + } + // + $if windows { + if ccompiler == 'msvc' { + v.cc_msvc() + return + } + } + // + all_args := v.all_args(v.ccoptions) + v.dump_c_options(all_args) + str_args := all_args.join(' ') + // write args to response file + response_file := '${v.out_name_c}.rsp' + response_file_content := str_args.replace('\\', '\\\\') + os.write_file(response_file, response_file_content) or { + verror('Unable to write response file "$response_file"') + } + if !v.ccoptions.debug_mode { + v.pref.cleanup_files << v.out_name_c + v.pref.cleanup_files << response_file + } + $if windows { + if v.ccoptions.is_cc_tcc { + def_name := v.pref.out_name[0..v.pref.out_name.len - 4] + v.pref.cleanup_files << '${def_name}.def' + } + } + // + os.chdir(vdir) + cmd := '$ccompiler "@$response_file"' + tried_compilation_commands << cmd + v.show_cc(cmd, response_file, response_file_content) + // Run + ccompiler_label := 'C ${os.file_name(ccompiler):3}' + util.timing_start(ccompiler_label) + res := os.execute(cmd) + util.timing_measure(ccompiler_label) + if v.pref.show_c_output { + v.show_c_compiler_output(res) + } + os.chdir(original_pwd) + vcache.dlog('| Builder.' + @FN, '> v.pref.use_cache: $v.pref.use_cache | v.pref.retry_compilation: $v.pref.retry_compilation') + vcache.dlog('| Builder.' + @FN, '> cmd res.exit_code: $res.exit_code | cmd: $cmd') + vcache.dlog('| Builder.' + @FN, '> response_file_content:\n$response_file_content') + if res.exit_code != 0 { + if ccompiler.contains('tcc.exe') { + // a TCC problem? Retry with the system cc: + if tried_compilation_commands.len > 1 { + eprintln('Recompilation loop detected (ccompiler: $ccompiler):') + for recompile_command in tried_compilation_commands { + eprintln(' $recompile_command') + } + exit(101) + } + if v.pref.retry_compilation { + tcc_output = res + v.pref.ccompiler = pref.default_c_compiler() + if v.pref.is_verbose { + eprintln('Compilation with tcc failed. Retrying with $v.pref.ccompiler ...') + } + continue + } + } + if res.exit_code == 127 { + verror('C compiler error, while attempting to run: \n' + + '-----------------------------------------------------------\n' + '$cmd\n' + + '-----------------------------------------------------------\n' + + 'Probably your C compiler is missing. \n' + + 'Please reinstall it, or make it available in your PATH.\n\n' + + missing_compiler_info()) + } + } + if !v.pref.show_c_output { + // if tcc failed once, and the system C compiler has failed as well, + // print the tcc error instead since it may contain more useful information + // see https://discord.com/channels/592103645835821068/592115457029308427/811956304314761228 + if res.exit_code != 0 && tcc_output.output != '' { + v.post_process_c_compiler_output(tcc_output) + } else { + v.post_process_c_compiler_output(res) + } + } + // Print the C command + if v.pref.is_verbose { + println('$ccompiler') + println('=========\n') + } + break + } + if v.pref.compress { + $if windows { + println('-compress does not work on Windows for now') + return + } + ret := os.system('strip $v.pref.out_name') + if ret != 0 { + println('strip failed') + return + } + // NB: upx --lzma can sometimes fail with NotCompressibleException + // See https://github.com/vlang/v/pull/3528 + mut ret2 := os.system('upx --lzma -qqq $v.pref.out_name') + if ret2 != 0 { + ret2 = os.system('upx -qqq $v.pref.out_name') + } + if ret2 != 0 { + println('upx failed') + $if macos { + println('install upx with `brew install upx`') + } + $if linux { + println('install upx\n' + 'for example, on Debian/Ubuntu run `sudo apt install upx`') + } + $if windows { + // :) + } + } + } + // if v.pref.os == .ios { + // ret := os.system('ldid2 -S $v.pref.out_name') + // if ret != 0 { + // eprintln('failed to run ldid2, try: brew install ldid') + // } + // } +} + +fn (mut b Builder) ensure_linuxroot_exists(sysroot string) { + crossrepo_url := 'https://github.com/spytheman/vlinuxroot' + sysroot_git_config_path := os.join_path(sysroot, '.git', 'config') + if os.is_dir(sysroot) && !os.exists(sysroot_git_config_path) { + // remove existing obsolete unarchived .zip file content + os.rmdir_all(sysroot) or {} + } + if !os.is_dir(sysroot) { + println('Downloading files for Linux cross compilation (~22MB) ...') + os.system('git clone $crossrepo_url $sysroot') + if !os.exists(sysroot_git_config_path) { + verror('Failed to clone `$crossrepo_url` to `$sysroot`') + } + os.chmod(os.join_path(sysroot, 'ld.lld'), 0o755) + } +} + +fn (mut b Builder) cc_linux_cross() { + b.setup_ccompiler_options(b.pref.ccompiler) + b.build_thirdparty_obj_files() + b.setup_output_name() + parent_dir := os.vmodules_dir() + if !os.exists(parent_dir) { + os.mkdir(parent_dir) or { panic(err) } + } + sysroot := os.join_path(os.vmodules_dir(), 'linuxroot') + b.ensure_linuxroot_exists(sysroot) + obj_file := b.out_name_c + '.o' + cflags := b.get_os_cflags() + defines, others, libs := cflags.defines_others_libs() + mut cc_args := []string{} + cc_args << '-w' + cc_args << '-fPIC' + cc_args << '-c' + cc_args << '-target x86_64-linux-gnu' + cc_args << defines + cc_args << '-I $sysroot/include ' + cc_args << others + cc_args << '-o "$obj_file"' + cc_args << '-c "$b.out_name_c"' + cc_args << libs + b.dump_c_options(cc_args) + cc_cmd := 'cc ' + cc_args.join(' ') + if b.pref.show_cc { + println(cc_cmd) + } + cc_res := os.execute(cc_cmd) + if cc_res.exit_code != 0 { + println('Cross compilation for Linux failed (first step, cc). Make sure you have clang installed.') + verror(cc_res.output) + return + } + mut linker_args := ['-L $sysroot/usr/lib/x86_64-linux-gnu/', '--sysroot=$sysroot', '-v', + '-o $b.pref.out_name', '-m elf_x86_64', + '-dynamic-linker /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2', + '$sysroot/crt1.o $sysroot/crti.o $obj_file', '-lc', '-lcrypto', '-lssl', '-lpthread', + '$sysroot/crtn.o', + ] + linker_args << cflags.c_options_only_object_files() + // -ldl + b.dump_c_options(linker_args) + linker_cmd := '$sysroot/ld.lld ' + linker_args.join(' ') + // s = s.replace('SYSROOT', sysroot) // TODO $ inter bug + // s = s.replace('-o hi', '-o ' + c.pref.out_name) + if b.pref.show_cc { + println(linker_cmd) + } + res := os.execute(linker_cmd) + if res.exit_code != 0 { + println('Cross compilation for Linux failed (second step, lld).') + verror(res.output) + return + } + println(b.pref.out_name + ' has been successfully compiled') +} + +fn (mut c Builder) cc_windows_cross() { + println('Cross compiling for Windows...') + c.setup_ccompiler_options(c.pref.ccompiler) + c.build_thirdparty_obj_files() + c.setup_output_name() + if !c.pref.out_name.ends_with('.exe') { + c.pref.out_name += '.exe' + } + mut args := []string{} + args << '$c.pref.cflags' + args << '-o $c.pref.out_name' + args << '-w -L.' + // + cflags := c.get_os_cflags() + // -I flags + if c.pref.ccompiler == 'msvc' { + args << cflags.c_options_before_target_msvc() + } else { + args << cflags.c_options_before_target() + } + mut optimization_options := []string{} + mut debug_options := []string{} + if c.pref.is_prod { + if c.pref.ccompiler != 'msvc' { + optimization_options = ['-O3', '-fno-strict-aliasing', '-flto'] + } + } + if c.pref.is_debug { + if c.pref.ccompiler != 'msvc' { + debug_options = ['-O0', '-g', '-gdwarf-2'] + } + } + mut libs := []string{} + if false && c.pref.build_mode == .default_mode { + builtin_o := '"$pref.default_module_path/vlib/builtin.o"' + libs << builtin_o + if !os.exists(builtin_o) { + verror('$builtin_o not found') + } + for imp in c.table.imports { + libs << '"$pref.default_module_path/vlib/${imp}.o"' + } + } + // add the thirdparty .o files, produced by all the #flag directives: + args << cflags.c_options_only_object_files() + args << c.out_name_c + if c.pref.ccompiler == 'msvc' { + args << cflags.c_options_after_target_msvc() + } else { + args << cflags.c_options_after_target() + } + /* + winroot := '${pref.default_module_path}/winroot' + if !os.is_dir(winroot) { + winroot_url := 'https://github.com/vlang/v/releases/download/v0.1.10/winroot.zip' + println('"$winroot" not found.') + println('Download it from $winroot_url and save it in ${pref.default_module_path}') + println('Unzip it afterwards.\n') + println('winroot.zip contains all library and header files needed ' + 'to cross-compile for Windows.') + exit(1) + } + mut obj_name := c.out_name + obj_name = obj_name.replace('.exe', '') + obj_name = obj_name.replace('.o.o', '.o') + include := '-I $winroot/include ' + */ + if os.user_os() !in ['macos', 'linux'] { + println(os.user_os()) + panic('your platform is not supported yet') + } + mut all_args := []string{} + all_args << optimization_options + all_args << debug_options + all_args << '-std=gnu11' + all_args << args + all_args << '-municode' + c.dump_c_options(all_args) + mut cmd := '$builder.mingw_cc ' + all_args.join(' ') + // cmd := 'clang -o $obj_name -w $include -m32 -c -target x86_64-win32 ${pref.default_module_path}/$c.out_name_c' + if c.pref.is_verbose || c.pref.show_cc { + println(cmd) + } + if os.system(cmd) != 0 { + println('Cross compilation for Windows failed. Make sure you have mingw-w64 installed.') + $if macos { + println('brew install mingw-w64') + } + $if linux { + println('Try `sudo apt install -y mingw-w64` on Debian based distros, or `sudo pacman -S mingw-w64-gcc` on Arch, etc...') + } + exit(1) + } + /* + if c.pref.build_mode != .build_module { + link_cmd := 'lld-link $obj_name $winroot/lib/libcmt.lib ' + '$winroot/lib/libucrt.lib $winroot/lib/kernel32.lib $winroot/lib/libvcruntime.lib ' + '$winroot/lib/uuid.lib' + if c.pref.show_cc { + println(link_cmd) + } + if os.system(link_cmd) != 0 { + println('Cross compilation for Windows failed. Make sure you have lld linker installed.') + exit(1) + } + // os.rm(obj_name) + } + */ + println(c.pref.out_name + ' has been successfully compiled') +} + +fn (mut b Builder) build_thirdparty_obj_files() { + b.log('build_thirdparty_obj_files: v.ast.cflags: $b.table.cflags') + for flag in b.get_os_cflags() { + if flag.value.ends_with('.o') { + rest_of_module_flags := b.get_rest_of_module_cflags(flag) + if b.pref.ccompiler == 'msvc' { + b.build_thirdparty_obj_file_with_msvc(flag.value, rest_of_module_flags) + } else { + b.build_thirdparty_obj_file(flag.value, rest_of_module_flags) + } + } + } +} + +fn (mut v Builder) build_thirdparty_obj_file(path string, moduleflags []cflag.CFlag) { + obj_path := os.real_path(path) + cfile := '${obj_path[..obj_path.len - 2]}.c' + opath := v.pref.cache_manager.postfix_with_key2cpath('.o', obj_path) + mut rebuild_reason_message := '$obj_path not found, building it in $opath ...' + if os.exists(opath) { + if os.exists(cfile) && os.file_last_mod_unix(opath) < os.file_last_mod_unix(cfile) { + rebuild_reason_message = '$opath is older than $cfile, rebuilding ...' + } else { + return + } + } + if os.exists(obj_path) { + // Some .o files are distributed with no source + // for example thirdparty\tcc\lib\openlibm.o + // the best we can do for them is just copy them, + // and hope that they work with any compiler... + os.cp(obj_path, opath) or { panic(err) } + return + } + println(rebuild_reason_message) + // + // prepare for tcc, it needs relative paths to thirdparty/tcc to work: + current_folder := os.getwd() + os.chdir(os.dir(pref.vexe_path())) + // + mut all_options := []string{} + all_options << v.pref.third_party_option + all_options << moduleflags.c_options_before_target() + all_options << '-o "$opath"' + all_options << '-c "$cfile"' + cc_options := v.thirdparty_object_args(v.ccoptions, all_options).join(' ') + cmd := '$v.pref.ccompiler $cc_options' + $if trace_thirdparty_obj_files ? { + println('>>> build_thirdparty_obj_files cmd: $cmd') + } + res := os.execute(cmd) + os.chdir(current_folder) + if res.exit_code != 0 { + eprintln('failed thirdparty object build cmd:\n$cmd') + verror(res.output) + return + } + v.pref.cache_manager.save('.description.txt', obj_path, '${obj_path:-30} @ $cmd\n') or { + panic(err) + } + if res.output != '' { + println(res.output) + } +} + +fn missing_compiler_info() string { + $if windows { + return 'https://github.com/vlang/v/wiki/Installing-a-C-compiler-on-Windows' + } + $if linux { + return 'On Debian/Ubuntu, run `sudo apt install build-essential`' + } + $if macos { + return 'Install command line XCode tools with `xcode-select --install`' + } + return '' +} + +fn error_context_lines(text string, keyword string, before int, after int) []string { + khighlight := if term.can_show_color_on_stdout() { term.red(keyword) } else { keyword } + mut eline_idx := 0 + mut lines := text.split_into_lines() + for idx, eline in lines { + if eline.contains(keyword) { + lines[idx] = lines[idx].replace(keyword, khighlight) + if eline_idx == 0 { + eline_idx = idx + } + } + } + idx_s := if eline_idx - before >= 0 { eline_idx - before } else { 0 } + idx_e := if idx_s + after < lines.len { idx_s + after } else { lines.len } + return lines[idx_s..idx_e] +} diff --git a/v_windows/v/old/vlib/v/builder/cflags.v b/v_windows/v/old/vlib/v/builder/cflags.v new file mode 100644 index 0000000..1486f69 --- /dev/null +++ b/v_windows/v/old/vlib/v/builder/cflags.v @@ -0,0 +1,45 @@ +module builder + +import os +import v.cflag + +// get flags for current os +fn (mut v Builder) get_os_cflags() []cflag.CFlag { + mut flags := []cflag.CFlag{} + mut ctimedefines := []string{} + if v.pref.compile_defines.len > 0 { + ctimedefines << v.pref.compile_defines + } + for mut flag in v.table.cflags { + if flag.value.ends_with('.o') { + flag.cached = v.pref.cache_manager.postfix_with_key2cpath('.o', os.real_path(flag.value)) + } + if flag.os == '' || (flag.os == 'linux' && v.pref.os == .linux) + || (flag.os == 'macos' && v.pref.os == .macos) + || (flag.os == 'darwin' && v.pref.os == .macos) + || (flag.os == 'freebsd' && v.pref.os == .freebsd) + || (flag.os == 'windows' && v.pref.os == .windows) + || (flag.os == 'mingw' && v.pref.os == .windows && v.pref.ccompiler != 'msvc') + || (flag.os == 'solaris' && v.pref.os == .solaris) { + flags << flag + } + if flag.os in ctimedefines { + flags << flag + } + } + return flags +} + +fn (mut v Builder) get_rest_of_module_cflags(c &cflag.CFlag) []cflag.CFlag { + mut flags := []cflag.CFlag{} + cflags := v.get_os_cflags() + for flag in cflags { + if c.mod == flag.mod { + if c.name == flag.name && c.value == flag.value && c.os == flag.os { + continue + } + flags << flag + } + } + return flags +} diff --git a/v_windows/v/old/vlib/v/builder/compile.v b/v_windows/v/old/vlib/v/builder/compile.v new file mode 100644 index 0000000..2c2c020 --- /dev/null +++ b/v_windows/v/old/vlib/v/builder/compile.v @@ -0,0 +1,324 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module builder + +import time +import os +import rand +import v.pref +import v.util + +fn (mut b Builder) get_vtmp_filename(base_file_name string, postfix string) string { + vtmp := util.get_vtmp_folder() + mut uniq := '' + if !b.pref.reuse_tmpc { + uniq = '.$rand.u64()' + } + fname := os.file_name(os.real_path(base_file_name)) + '$uniq$postfix' + return os.real_path(os.join_path(vtmp, fname)) +} + +pub fn compile(command string, pref &pref.Preferences) { + odir := os.dir(pref.out_name) + // When pref.out_name is just the name of an executable, i.e. `./v -o executable main.v` + // without a folder component, just use the current folder instead: + mut output_folder := odir + if odir.len == pref.out_name.len { + output_folder = os.getwd() + } + os.is_writable_folder(output_folder) or { + // An early error here, is better than an unclear C error later: + verror(err.msg) + } + // Construct the V object from command line arguments + mut b := new_builder(pref) + if pref.is_verbose { + println('builder.compile() pref:') + // println(pref) + } + mut sw := time.new_stopwatch() + match pref.backend { + .c { b.compile_c() } + .js_node, .js_freestanding, .js_browser { b.compile_js() } + .native { b.compile_native() } + } + mut timers := util.get_timers() + timers.show_remaining() + if pref.is_stats { + compilation_time_micros := 1 + sw.elapsed().microseconds() + scompilation_time_ms := util.bold('${f64(compilation_time_micros) / 1000.0:6.3f}') + mut all_v_source_lines, mut all_v_source_bytes := 0, 0 + for pf in b.parsed_files { + all_v_source_lines += pf.nr_lines + all_v_source_bytes += pf.nr_bytes + } + mut sall_v_source_lines := all_v_source_lines.str() + mut sall_v_source_bytes := all_v_source_bytes.str() + sall_v_source_lines = util.bold('${sall_v_source_lines:10s}') + sall_v_source_bytes = util.bold('${sall_v_source_bytes:10s}') + println(' V source code size: $sall_v_source_lines lines, $sall_v_source_bytes bytes') + // + mut slines := b.stats_lines.str() + mut sbytes := b.stats_bytes.str() + slines = util.bold('${slines:10s}') + sbytes = util.bold('${sbytes:10s}') + println('generated target code size: $slines lines, $sbytes bytes') + // + vlines_per_second := int(1_000_000.0 * f64(all_v_source_lines) / f64(compilation_time_micros)) + svlines_per_second := util.bold(vlines_per_second.str()) + println('compilation took: $scompilation_time_ms ms, compilation speed: $svlines_per_second vlines/s') + } + b.exit_on_invalid_syntax() + // running does not require the parsers anymore + unsafe { b.myfree() } + if pref.is_test || pref.is_run { + b.run_compiled_executable_and_exit() + } +} + +// Temporary, will be done by -autofree +[unsafe] +fn (mut b Builder) myfree() { + // for file in b.parsed_files { + // } + unsafe { b.parsed_files.free() } +} + +fn (b &Builder) exit_on_invalid_syntax() { + // V should exit with an exit code of 1, when there are errors, + // even when -silent is passed in combination to -check-syntax: + if b.pref.only_check_syntax { + for pf in b.parsed_files { + if pf.errors.len > 0 { + exit(1) + } + } + if b.checker.nr_errors > 0 { + exit(1) + } + } +} + +fn (mut b Builder) run_compiled_executable_and_exit() { + if b.pref.skip_running { + return + } + if b.pref.only_check_syntax { + return + } + if b.pref.should_output_to_stdout() { + return + } + if b.pref.os == .ios { + panic('Running iOS apps is not supported yet.') + } + if b.pref.is_verbose { + println('============ running $b.pref.out_name ============') + } + mut exefile := os.real_path(b.pref.out_name) + mut cmd := '"$exefile"' + if b.pref.backend.is_js() { + exefile = os.real_path('${b.pref.out_name}.js') + cmd = 'node "$exefile"' + } + for arg in b.pref.run_args { + // Determine if there are spaces in the parameters + if arg.index_byte(` `) > 0 { + cmd += ' "' + arg + '"' + } else { + cmd += ' ' + arg + } + } + if b.pref.is_verbose { + println('command to run executable: $cmd') + } + if b.pref.is_test || b.pref.is_run { + ret := os.system(cmd) + b.cleanup_run_executable_after_exit(exefile) + exit(ret) + } + exit(0) +} + +fn (mut v Builder) cleanup_run_executable_after_exit(exefile string) { + if v.pref.reuse_tmpc { + v.pref.vrun_elog('keeping executable: $exefile , because -keepc was passed') + return + } + v.pref.vrun_elog('remove run executable: $exefile') + os.rm(exefile) or { panic(err) } +} + +// 'strings' => 'VROOT/vlib/strings' +// 'installed_mod' => '~/.vmodules/installed_mod' +// 'local_mod' => '/path/to/current/dir/local_mod' +fn (mut v Builder) set_module_lookup_paths() { + // Module search order: + // 0) V test files are very commonly located right inside the folder of the + // module, which they test. Adding the parent folder of the module folder + // with the _test.v files, *guarantees* that the tested module can be found + // without needing to set custom options/flags. + // 1) search in the *same* directory, as the compiled final v program source + // (i.e. the . in `v .` or file.v in `v file.v`) + // 2) search in the modules/ in the same directory. + // 3) search in the provided paths + // By default, these are what (3) contains: + // 3.1) search in vlib/ + // 3.2) search in ~/.vmodules/ (i.e. modules installed with vpm) + v.module_search_paths = [] + if v.pref.is_test { + v.module_search_paths << os.dir(v.compiled_dir) // pdir of _test.v + } + v.module_search_paths << v.compiled_dir + x := os.join_path(v.compiled_dir, 'modules') + if v.pref.is_verbose { + println('x: "$x"') + } + v.module_search_paths << os.join_path(v.compiled_dir, 'modules') + v.module_search_paths << v.pref.lookup_path + if v.pref.is_verbose { + v.log('v.module_search_paths:') + println(v.module_search_paths) + } +} + +pub fn (v Builder) get_builtin_files() []string { + /* + // if v.pref.build_mode == .build_module && v.pref.path == 'vlib/builtin' { // .contains('builtin/' + location { + if v.pref.build_mode == .build_module && v.pref.path == 'vlib/strconv' { // .contains('builtin/' + location { + // We are already building builtin.o, no need to import them again + if v.pref.is_verbose { + println('skipping builtin modules for builtin.o') + } + return [] + } + */ + v.log('v.pref.lookup_path: $v.pref.lookup_path') + // Lookup for built-in folder in lookup path. + // Assumption: `builtin/` folder implies usable implementation of builtin + for location in v.pref.lookup_path { + if os.exists(os.join_path(location, 'builtin')) { + mut builtin_files := []string{} + if v.pref.backend.is_js() { + builtin_files << v.v_files_from_dir(os.join_path(location, 'builtin', + 'js')) + } else { + builtin_files << v.v_files_from_dir(os.join_path(location, 'builtin')) + } + if v.pref.is_bare { + builtin_files << v.v_files_from_dir(v.pref.bare_builtin_dir) + } + if v.pref.backend == .c { + // TODO JavaScript backend doesn't handle os for now + if v.pref.is_vsh && os.exists(os.join_path(location, 'os')) { + builtin_files << v.v_files_from_dir(os.join_path(location, 'os')) + } + } + return builtin_files + } + } + // Panic. We couldn't find the folder. + verror('`builtin/` not included on module lookup path.\nDid you forget to add vlib to the path? (Use @vlib for default vlib)') +} + +pub fn (v &Builder) get_user_files() []string { + if v.pref.path in ['vlib/builtin', 'vlib/strconv', 'vlib/strings', 'vlib/hash'] { + // This means we are building a builtin module with `v build-module vlib/strings` etc + // get_builtin_files() has already added the files in this module, + // do nothing here to avoid duplicate definition errors. + v.log('Skipping user files.') + return [] + } + mut dir := v.pref.path + v.log('get_v_files($dir)') + // Need to store user files separately, because they have to be added after + // libs, but we dont know which libs need to be added yet + mut user_files := []string{} + // See cmd/tools/preludes/README.md for more info about what preludes are + vroot := os.dir(pref.vexe_path()) + mut preludes_path := os.join_path(vroot, 'vlib', 'v', 'preludes') + if v.pref.backend == .js_node { + preludes_path = os.join_path(vroot, 'vlib', 'v', 'preludes_js') + } + if v.pref.is_livemain || v.pref.is_liveshared { + user_files << os.join_path(preludes_path, 'live.v') + } + if v.pref.is_livemain { + user_files << os.join_path(preludes_path, 'live_main.v') + } + if v.pref.is_liveshared { + user_files << os.join_path(preludes_path, 'live_shared.v') + } + if v.pref.is_test { + user_files << os.join_path(preludes_path, 'tests_assertions.v') + } + if v.pref.is_test && v.pref.is_stats { + user_files << os.join_path(preludes_path, 'tests_with_stats.v') + } + if v.pref.is_prof { + user_files << os.join_path(preludes_path, 'profiled_program.v') + } + is_test := v.pref.is_test + mut is_internal_module_test := false + if is_test { + tcontent := os.read_file(dir) or { verror('$dir does not exist') } + slines := tcontent.trim_space().split_into_lines() + for sline in slines { + line := sline.trim_space() + if line.len > 2 { + if line[0] == `/` && line[1] == `/` { + continue + } + if line.starts_with('module ') { + is_internal_module_test = true + break + } + } + } + } + if is_internal_module_test { + // v volt/slack_test.v: compile all .v files to get the environment + single_test_v_file := os.real_path(dir) + if v.pref.is_verbose { + v.log('> Compiling an internal module _test.v file $single_test_v_file .') + v.log('> That brings in all other ordinary .v files in the same module too .') + } + user_files << single_test_v_file + dir = os.dir(single_test_v_file) + } + does_exist := os.exists(dir) + if !does_exist { + verror("$dir doesn't exist") + } + is_real_file := does_exist && !os.is_dir(dir) + resolved_link := if is_real_file && os.is_link(dir) { os.real_path(dir) } else { dir } + if is_real_file && (dir.ends_with('.v') || resolved_link.ends_with('.vsh') + || dir.ends_with('.vv')) { + single_v_file := if resolved_link.ends_with('.vsh') { resolved_link } else { dir } + // Just compile one file and get parent dir + user_files << single_v_file + if v.pref.is_verbose { + v.log('> just compile one file: "$single_v_file"') + } + } else if os.is_dir(dir) { + if v.pref.is_verbose { + v.log('> add all .v files from directory "$dir" ...') + } + // Add .v files from the directory being compiled + user_files << v.v_files_from_dir(dir) + } else { + println('usage: `v file.v` or `v directory`') + ext := os.file_ext(dir) + println('unknown file extension `$ext`') + exit(1) + } + if user_files.len == 0 { + println('No input .v files') + exit(1) + } + if v.pref.is_verbose { + v.log('user_files: $user_files') + } + return user_files +} diff --git a/v_windows/v/old/vlib/v/builder/js.v b/v_windows/v/old/vlib/v/builder/js.v new file mode 100644 index 0000000..241f693 --- /dev/null +++ b/v_windows/v/old/vlib/v/builder/js.v @@ -0,0 +1,51 @@ +module builder + +import os +import v.pref +import v.util +import v.gen.js + +pub fn (mut b Builder) gen_js(v_files []string) string { + b.front_and_middle_stages(v_files) or { return '' } + util.timing_start('JS GEN') + res := js.gen(b.parsed_files, b.table, b.pref) + util.timing_measure('JS GEN') + return res +} + +pub fn (mut b Builder) build_js(v_files []string, out_file string) { + b.out_name_js = out_file + b.info('build_js($out_file)') + output := b.gen_js(v_files) + os.write_file(out_file, output) or { panic(err) } + if b.pref.is_stats { + b.stats_lines = output.count('\n') + 1 + b.stats_bytes = output.len + } +} + +pub fn (mut b Builder) compile_js() { + mut files := b.get_user_files() + files << b.get_builtin_files() + b.set_module_lookup_paths() + if b.pref.is_verbose { + println('all .v files:') + println(files) + } + mut name := b.pref.out_name + if !name.ends_with('.js') { + name += '.js' + } + b.build_js(files, name) +} + +fn (mut b Builder) run_js() { + cmd := 'node ' + b.pref.out_name + '.js' + res := os.execute(cmd) + if res.exit_code != 0 { + eprintln('JS compilation failed:') + verror(res.output) + return + } + println(res.output) +} diff --git a/v_windows/v/old/vlib/v/builder/msvc.v b/v_windows/v/old/vlib/v/builder/msvc.v new file mode 100644 index 0000000..903c0fb --- /dev/null +++ b/v_windows/v/old/vlib/v/builder/msvc.v @@ -0,0 +1,505 @@ +module builder + +import os +import v.pref +import v.util +import v.cflag + +#flag windows -l shell32 +#flag windows -l dbghelp +#flag windows -l advapi32 + +struct MsvcResult { + full_cl_exe_path string + exe_path string + um_lib_path string + ucrt_lib_path string + vs_lib_path string + um_include_path string + ucrt_include_path string + vs_include_path string + shared_include_path string + valid bool +} + +// shell32 for RegOpenKeyExW etc +// Mimics a HKEY +type RegKey = voidptr + +// Taken from the windows SDK +const ( + hkey_local_machine = RegKey(0x80000002) + key_query_value = (0x0001) + key_wow64_32key = (0x0200) + key_enumerate_sub_keys = (0x0008) +) + +// Given a root key look for one of the subkeys in 'versions' and get the path +fn find_windows_kit_internal(key RegKey, versions []string) ?string { + $if windows { + unsafe { + for version in versions { + required_bytes := u32(0) // TODO mut + result := C.RegQueryValueEx(key, version.to_wide(), 0, 0, 0, &required_bytes) + length := required_bytes / 2 + if result != 0 { + continue + } + alloc_length := (required_bytes + 2) + mut value := &u16(malloc_noscan(int(alloc_length))) + if isnil(value) { + continue + } + // + else { + } + result2 := C.RegQueryValueEx(key, version.to_wide(), 0, 0, value, &alloc_length) + if result2 != 0 { + continue + } + // We might need to manually null terminate this thing + // So just make sure that we do that + if value[length - 1] != u16(0) { + value[length] = u16(0) + } + res := string_from_wide(value) + return res + } + } + } + return error('windows kit not found') +} + +struct WindowsKit { + um_lib_path string + ucrt_lib_path string + um_include_path string + ucrt_include_path string + shared_include_path string +} + +// Try and find the root key for installed windows kits +fn find_windows_kit_root(target_arch string) ?WindowsKit { + $if windows { + wkroot := find_windows_kit_root_by_reg(target_arch) or { + if wkroot := find_windows_kit_root_by_env(target_arch) { + return wkroot + } + return err + } + + return wkroot + } $else { + return error('Host OS does not support finding a windows kit') + } +} + +// Try to find the root key for installed windows kits from registry +fn find_windows_kit_root_by_reg(target_arch string) ?WindowsKit { + $if windows { + root_key := RegKey(0) + path := 'SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots' + rc := C.RegOpenKeyEx(builder.hkey_local_machine, path.to_wide(), 0, builder.key_query_value | builder.key_wow64_32key | builder.key_enumerate_sub_keys, + &root_key) + + if rc != 0 { + return error('Unable to open root key') + } + // Try and find win10 kit + kit_root := find_windows_kit_internal(root_key, ['KitsRoot10', 'KitsRoot81']) or { + C.RegCloseKey(root_key) + return error('Unable to find a windows kit') + } + C.RegCloseKey(root_key) + return new_windows_kit(kit_root, target_arch) + } $else { + return error('Host OS does not support finding a windows kit') + } +} + +fn new_windows_kit(kit_root string, target_arch string) ?WindowsKit { + kit_lib := kit_root + 'Lib' + files := os.ls(kit_lib) ? + mut highest_path := '' + mut highest_int := 0 + for f in files { + no_dot := f.replace('.', '') + v_int := no_dot.int() + if v_int > highest_int { + highest_int = v_int + highest_path = f + } + } + kit_lib_highest := kit_lib + '\\$highest_path' + kit_include_highest := kit_lib_highest.replace('Lib', 'Include') + return WindowsKit{ + um_lib_path: kit_lib_highest + '\\um\\$target_arch' + ucrt_lib_path: kit_lib_highest + '\\ucrt\\$target_arch' + um_include_path: kit_include_highest + '\\um' + ucrt_include_path: kit_include_highest + '\\ucrt' + shared_include_path: kit_include_highest + '\\shared' + } +} + +fn find_windows_kit_root_by_env(target_arch string) ?WindowsKit { + kit_root := os.getenv('WindowsSdkDir') + if kit_root == '' { + return error('empty WindowsSdkDir') + } + return new_windows_kit(kit_root, target_arch) +} + +struct VsInstallation { + include_path string + lib_path string + exe_path string +} + +fn find_vs(vswhere_dir string, host_arch string, target_arch string) ?VsInstallation { + $if windows { + vsinst := find_vs_by_reg(vswhere_dir, host_arch, target_arch) or { + if vsinst := find_vs_by_env(host_arch, target_arch) { + return vsinst + } + return err + } + return vsinst + } $else { + return error('Host OS does not support finding a Visual Studio installation') + } +} + +fn find_vs_by_reg(vswhere_dir string, host_arch string, target_arch string) ?VsInstallation { + $if windows { + // Emily: + // VSWhere is guaranteed to be installed at this location now + // If its not there then end user needs to update their visual studio + // installation! + res := os.execute('"$vswhere_dir\\Microsoft Visual Studio\\Installer\\vswhere.exe" -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath') + if res.exit_code != 0 { + return error_with_code(res.output, res.exit_code) + } + res_output := res.output.trim_right('\r\n') + // println('res: "$res"') + version := os.read_file('$res_output\\VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt') or { + // println('Unable to find msvc version') + return error('Unable to find vs installation') + } + version2 := version // TODO remove. cgen option bug if expr + // println('version: $version') + v := if version.ends_with('\n') { version2[..version.len - 2] } else { version2 } + lib_path := '$res.output\\VC\\Tools\\MSVC\\$v\\lib\\$target_arch' + include_path := '$res.output\\VC\\Tools\\MSVC\\$v\\include' + if os.exists('$lib_path\\vcruntime.lib') { + p := '$res.output\\VC\\Tools\\MSVC\\$v\\bin\\Host$host_arch\\$target_arch' + // println('$lib_path $include_path') + return VsInstallation{ + exe_path: p + lib_path: lib_path + include_path: include_path + } + } + println('Unable to find vs installation (attempted to use lib path "$lib_path")') + return error('Unable to find vs exe folder') + } $else { + return error('Host OS does not support finding a Visual Studio installation') + } +} + +fn find_vs_by_env(host_arch string, target_arch string) ?VsInstallation { + vs_dir := os.getenv('VSINSTALLDIR') + if vs_dir == '' { + return error('empty VSINSTALLDIR') + } + + vc_tools_dir := os.getenv('VCToolsInstallDir') + if vc_tools_dir == '' { + return error('empty VCToolsInstallDir') + } + + bin_dir := '${vc_tools_dir}bin\\Host$host_arch\\$target_arch' + lib_path := '${vc_tools_dir}lib\\$target_arch' + include_path := '${vc_tools_dir}include' + + return VsInstallation{ + exe_path: bin_dir + lib_path: lib_path + include_path: include_path + } +} + +fn find_msvc(m64_target bool) ?MsvcResult { + $if windows { + processor_architecture := os.getenv('PROCESSOR_ARCHITECTURE') + vswhere_dir := if processor_architecture == 'x86' { + '%ProgramFiles%' + } else { + '%ProgramFiles(x86)%' + } + host_arch := if processor_architecture == 'x86' { 'X86' } else { 'X64' } + mut target_arch := 'X64' + if host_arch == 'X86' { + if !m64_target { + target_arch = 'X86' + } + } else if host_arch == 'X64' { + if !m64_target { + target_arch = 'X86' + } + } + wk := find_windows_kit_root(target_arch) or { return error('Unable to find windows sdk') } + vs := find_vs(vswhere_dir, host_arch, target_arch) or { + return error('Unable to find visual studio') + } + return MsvcResult{ + full_cl_exe_path: os.real_path(vs.exe_path + os.path_separator + 'cl.exe') + exe_path: vs.exe_path + um_lib_path: wk.um_lib_path + ucrt_lib_path: wk.ucrt_lib_path + vs_lib_path: vs.lib_path + um_include_path: wk.um_include_path + ucrt_include_path: wk.ucrt_include_path + vs_include_path: vs.include_path + shared_include_path: wk.shared_include_path + valid: true + } + } $else { + // This hack allows to at least see the generated .c file with `-os windows -cc msvc -o x.c` + // Please do not remove it, unless you also check that the above continues to work. + return MsvcResult{ + full_cl_exe_path: '/usr/bin/true' + valid: true + } + } +} + +pub fn (mut v Builder) cc_msvc() { + r := v.cached_msvc + if r.valid == false { + verror('Cannot find MSVC on this OS') + return + } + out_name_obj := os.real_path(v.out_name_c + '.obj') + out_name_pdb := os.real_path(v.out_name_c + '.pdb') + out_name_cmd_line := os.real_path(v.out_name_c + '.rsp') + // Default arguments + // volatile:ms enables atomic volatile (gcc _Atomic) + // -w: no warnings + // 2 unicode defines + // /Fo sets the object file name - needed so we can clean up after ourselves properly + mut a := ['-w', '/we4013', '/volatile:ms', '/Fo"$out_name_obj"'] + if v.pref.is_prod { + a << '/O2' + a << '/MD' + a << '/DNDEBUG' + } else { + a << '/MDd' + a << '/D_DEBUG' + } + if v.pref.is_debug { + // /Zi generates a .pdb + // /Fd sets the pdb file name (so its not just vc140 all the time) + a << ['/Zi', '/Fd"$out_name_pdb"'] + } + if v.pref.is_shared { + if !v.pref.out_name.ends_with('.dll') { + v.pref.out_name += '.dll' + } + // Build dll + a << '/LD' + } else if !v.pref.out_name.ends_with('.exe') { + v.pref.out_name += '.exe' + } + v.pref.out_name = os.real_path(v.pref.out_name) + // alibs := []string{} // builtin.o os.o http.o etc + if v.pref.build_mode == .build_module { + // Compile only + a << '/c' + } else if v.pref.build_mode == .default_mode { + /* + b := os.real_path( '${pref.default_module_path}/vlib/builtin.obj' ) + alibs << '"$b"' + if !os.exists(b) { + println('`builtin.obj` not found') + exit(1) + } + for imp in v.ast.imports { + if imp == 'webview' { + continue + } + alibs << '"' + os.real_path( '${pref.default_module_path}/vlib/${imp}.obj' ) + '"' + } + */ + } + if v.pref.sanitize { + println('Sanitize not supported on msvc.') + } + // The C file we are compiling + // a << '"$TmpPath/$v.out_name_c"' + a << '"' + os.real_path(v.out_name_c) + '"' + // Emily: + // Not all of these are needed (but the compiler should discard them if they are not used) + // these are the defaults used by msbuild and visual studio + mut real_libs := ['kernel32.lib', 'user32.lib', 'advapi32.lib'] + // sflags := v.get_os_cflags().msvc_string_flags() + sflags := msvc_string_flags(v.get_os_cflags()) + real_libs << sflags.real_libs + inc_paths := sflags.inc_paths + lib_paths := sflags.lib_paths + defines := sflags.defines + other_flags := sflags.other_flags + // Include the base paths + a << '-I "$r.ucrt_include_path"' + a << '-I "$r.vs_include_path"' + a << '-I "$r.um_include_path"' + a << '-I "$r.shared_include_path"' + a << defines + a << inc_paths + a << other_flags + // Libs are passed to cl.exe which passes them to the linker + a << real_libs.join(' ') + a << '/link' + a << '/NOLOGO' + a << '/OUT:"$v.pref.out_name"' + a << '/LIBPATH:"$r.ucrt_lib_path"' + a << '/LIBPATH:"$r.um_lib_path"' + a << '/LIBPATH:"$r.vs_lib_path"' + a << '/DEBUG:FULL' // required for prod builds to generate PDB + if v.pref.is_prod { + a << '/INCREMENTAL:NO' // Disable incremental linking + a << '/OPT:REF' + a << '/OPT:ICF' + } + a << lib_paths + args := a.join(' ') + // write args to a file so that we dont smash createprocess + os.write_file(out_name_cmd_line, args) or { + verror('Unable to write response file to "$out_name_cmd_line"') + } + cmd := '"$r.full_cl_exe_path" "@$out_name_cmd_line"' + // It is hard to see it at first, but the quotes above ARE balanced :-| ... + // Also the double quotes at the start ARE needed. + v.show_cc(cmd, out_name_cmd_line, args) + util.timing_start('C msvc') + res := os.execute(cmd) + if res.exit_code != 0 { + eprintln(res.output) + verror('msvc error') + return + } + util.timing_measure('C msvc') + if v.pref.show_c_output { + v.show_c_compiler_output(res) + } else { + v.post_process_c_compiler_output(res) + } + // println(res) + // println('C OUTPUT:') + // Always remove the object file - it is completely unnecessary + os.rm(out_name_obj) or { panic(err) } +} + +fn (mut v Builder) build_thirdparty_obj_file_with_msvc(path string, moduleflags []cflag.CFlag) { + msvc := v.cached_msvc + if msvc.valid == false { + verror('Cannot find MSVC on this OS') + return + } + // msvc expects .obj not .o + mut obj_path := '${path}bj' + obj_path = os.real_path(obj_path) + if os.exists(obj_path) { + // println('$obj_path already built.') + return + } + println('$obj_path not found, building it (with msvc)...') + cfiles := '${path[..path.len - 2]}.c' + flags := msvc_string_flags(moduleflags) + inc_dirs := flags.inc_paths.join(' ') + defines := flags.defines.join(' ') + include_string := '-I "$msvc.ucrt_include_path" -I "$msvc.vs_include_path" -I "$msvc.um_include_path" -I "$msvc.shared_include_path" $inc_dirs' + // println('cfiles: $cfiles') + mut oargs := []string{} + if v.pref.is_prod { + oargs << '/O2' + oargs << '/MD' + oargs << '/DNDEBUG' + } else { + oargs << '/MDd' + oargs << '/D_DEBUG' + } + str_oargs := oargs.join(' ') + cmd := '"$msvc.full_cl_exe_path" /volatile:ms $str_oargs $defines $include_string /c $cfiles /Fo"$obj_path"' + // NB: the quotes above ARE balanced. + $if trace_thirdparty_obj_files ? { + println('>>> build_thirdparty_obj_file_with_msvc cmd: $cmd') + } + res := os.execute(cmd) + if res.exit_code != 0 { + println('msvc: failed to build a thirdparty object; cmd: $cmd') + verror(res.output) + return + } + println(res.output) +} + +struct MsvcStringFlags { +mut: + real_libs []string + inc_paths []string + lib_paths []string + defines []string + other_flags []string +} + +// pub fn (cflags []CFlag) msvc_string_flags() MsvcStringFlags { +pub fn msvc_string_flags(cflags []cflag.CFlag) MsvcStringFlags { + mut real_libs := []string{} + mut inc_paths := []string{} + mut lib_paths := []string{} + mut defines := []string{} + mut other_flags := []string{} + for flag in cflags { + // println('fl: $flag.name | flag arg: $flag.value') + // We need to see if the flag contains -l + // -l isnt recognised and these libs will be passed straight to the linker + // by the compiler + if flag.name == '-l' { + if flag.value.ends_with('.dll') { + verror('MSVC cannot link against a dll (`#flag -l $flag.value`)') + } + // MSVC has no method of linking against a .dll + // TODO: we should look for .defs aswell + lib_lib := flag.value + '.lib' + real_libs << lib_lib + } else if flag.name == '-I' { + inc_paths << flag.format() + } else if flag.name == '-L' { + lib_paths << flag.value + lib_paths << flag.value + os.path_separator + 'msvc' + // The above allows putting msvc specific .lib files in a subfolder msvc/ , + // where gcc will NOT find them, but cl will do... + // NB: gcc is smart enough to not need .lib files at all in most cases, the .dll is enough. + // When both a msvc .lib file and .dll file are present in the same folder, + // as for example for glfw3, compilation with gcc would fail. + } else if flag.value.ends_with('.o') { + // msvc expects .obj not .o + other_flags << '"${flag.value}bj"' + } else if flag.value.starts_with('-D') { + defines << '/D${flag.value[2..]}' + } else { + other_flags << flag.value + } + } + mut lpaths := []string{} + for l in lib_paths { + lpaths << '/LIBPATH:"' + os.real_path(l) + '"' + } + return MsvcStringFlags{ + real_libs: real_libs + inc_paths: inc_paths + lib_paths: lpaths + defines: defines + other_flags: other_flags + } +} diff --git a/v_windows/v/old/vlib/v/builder/native.v b/v_windows/v/old/vlib/v/builder/native.v new file mode 100644 index 0000000..c2176be --- /dev/null +++ b/v_windows/v/old/vlib/v/builder/native.v @@ -0,0 +1,22 @@ +module builder + +import v.pref +import v.util +import v.gen.native + +pub fn (mut b Builder) build_native(v_files []string, out_file string) { + if b.pref.os !in [.linux, .macos] { + eprintln('Warning: v -native can only generate macOS and Linux binaries for now') + } + b.front_and_middle_stages(v_files) or { return } + util.timing_start('Native GEN') + b.stats_lines, b.stats_bytes = native.gen(b.parsed_files, b.table, out_file, b.pref) + util.timing_measure('Native GEN') +} + +pub fn (mut b Builder) compile_native() { + // v.files << v.v_files_from_dir(os.join_path(v.pref.vlib_path,'builtin','bare')) + files := [b.pref.path] + b.set_module_lookup_paths() + b.build_native(files, b.pref.out_name) +} diff --git a/v_windows/v/old/vlib/v/callgraph/callgraph.v b/v_windows/v/old/vlib/v/callgraph/callgraph.v new file mode 100644 index 0000000..da57bc7 --- /dev/null +++ b/v_windows/v/old/vlib/v/callgraph/callgraph.v @@ -0,0 +1,129 @@ +module callgraph + +import v.ast +import v.ast.walker +import v.pref +import v.dotgraph + +// callgraph.show walks the AST, starting at main() and prints a DOT output describing the calls +// that function make transitively +pub fn show(mut table ast.Table, pref &pref.Preferences, ast_files []&ast.File) { + mut mapper := &Mapper{ + pref: pref + table: table + dg: dotgraph.new('CallGraph', 'CallGraph for $pref.path', 'green') + } + // Node14 [shape="box",label="PrivateBase",URL="$classPrivateBase.html"]; + // Node15 -> Node9 [dir=back,color="midnightblue",fontsize=10,style="solid"]; + for afile in ast_files { + walker.walk(mapper, afile) + } + mapper.dg.finish() +} + +[heap] +struct Mapper { + pos int +mut: + pref &pref.Preferences + table &ast.Table + file &ast.File = 0 + node &ast.Node = 0 + fn_decl &ast.FnDecl = 0 + caller_name string + dot_caller_name string + is_caller_used bool + dg dotgraph.DotGraph +} + +fn (mut m Mapper) dot_normalise_node_name(name string) string { + res := name.replace_each([ + '.', + '_', + '==', + 'op_eq', + '>=', + 'op_greater_eq', + '<=', + 'op_lesser_eq', + '>', + 'op_greater', + '<', + 'op_lesser', + '+', + 'op_plus', + '-', + 'op_minus', + '/', + 'op_divide', + '*', + 'op_multiply', + '^', + 'op_xor', + '|', + 'op_or', + '&', + 'op_and', + ]) + return res +} + +fn (mut m Mapper) fn_name(fname string, receiver_type ast.Type, is_method bool) string { + if !is_method { + return fname + } + rec_sym := m.table.get_type_symbol(receiver_type) + return '${rec_sym.name}.$fname' +} + +fn (mut m Mapper) dot_fn_name(fname string, recv_type ast.Type, is_method bool) string { + if is_method { + return 'Node_method_' + int(recv_type).str() + '_' + m.dot_normalise_node_name(fname) + } + return 'Node_fn_' + m.dot_normalise_node_name(fname) +} + +fn (mut m Mapper) visit(node &ast.Node) ? { + m.node = unsafe { node } + match node { + ast.File { + m.file = unsafe { &node } + } + ast.Stmt { + match node { + ast.FnDecl { + m.is_caller_used = true + if m.pref.skip_unused { + m.is_caller_used = m.table.used_fns[node.fkey()] + } + m.fn_decl = unsafe { &node } + m.caller_name = m.fn_name(node.name, node.receiver.typ, node.is_method) + m.dot_caller_name = m.dot_fn_name(node.name, node.receiver.typ, node.is_method) + if m.is_caller_used { + m.dg.new_node(m.caller_name, + node_name: m.dot_caller_name + should_highlight: m.caller_name == 'main.main' + ) + } + } + else {} + } + } + ast.Expr { + match node { + ast.CallExpr { + if m.is_caller_used { + dot_called_name := m.dot_fn_name(node.name, node.receiver_type, + node.is_method) + // Node15 -> Node9 [dir=back,color="midnightblue",fontsize=10,style="solid"]; + m.dg.new_edge(m.dot_caller_name, dot_called_name, + should_highlight: m.caller_name == 'main.main' + ) + } + } + else {} + } + } + else {} + } +} diff --git a/v_windows/v/old/vlib/v/cflag/cflags.v b/v_windows/v/old/vlib/v/cflag/cflags.v new file mode 100644 index 0000000..30da3b6 --- /dev/null +++ b/v_windows/v/old/vlib/v/cflag/cflags.v @@ -0,0 +1,131 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module cflag + +import os + +// parsed cflag +pub struct CFlag { +pub: + mod string // the module in which the flag was given + os string // eg. windows | darwin | linux + name string // eg. -I + value string // eg. /path/to/include +pub mut: + cached string // eg. ~/.vmodules/cache/ea/ea9878886727367672163.o (for .o files) +} + +pub fn (c &CFlag) str() string { + return 'CFlag{ name: "$c.name" value: "$c.value" mod: "$c.mod" os: "$c.os" cached: "$c.cached" }' +} + +const fexisting_literal = r'$first_existing' + +// expand the flag value +pub fn (cf &CFlag) eval() string { + mut value := '' + cflag_eval_outer_loop: for i := 0; i < cf.value.len; i++ { + x := cf.value[i] + if x == `$` { + remainder := cf.value[i..] + if remainder.starts_with(cflag.fexisting_literal) { + sparams := remainder[cflag.fexisting_literal.len + 1..].all_before(')') + i += sparams.len + cflag.fexisting_literal.len + 1 + svalues := sparams.replace(',', '\n').split_into_lines().map(it.trim(' \'"')) + // mut found_spath := '' + for spath in svalues { + if os.exists(spath) { + // found_spath = spath + value += spath + continue cflag_eval_outer_loop + } + } + panic('>> error: none of the paths $svalues exist') + continue + } + } + value += x.ascii_str() + } + return value +} + +// format flag +pub fn (cf &CFlag) format() string { + mut value := '' + if cf.cached != '' { + value = cf.cached + } else { + value = cf.eval() + } + if cf.name in ['-l', '-Wa', '-Wl', '-Wp'] && value.len > 0 { + return '$cf.name$value'.trim_space() + } + // convert to absolute path + if cf.name == '-I' || cf.name == '-L' || value.ends_with('.o') { + value = '"' + os.real_path(value) + '"' + } + return '$cf.name $value'.trim_space() +} + +// TODO: implement msvc specific c_options_before_target and c_options_after_target ... +pub fn (cflags []CFlag) c_options_before_target_msvc() []string { + return [] +} + +pub fn (cflags []CFlag) c_options_after_target_msvc() []string { + return [] +} + +pub fn (cflags []CFlag) c_options_before_target() []string { + defines, others, _ := cflags.defines_others_libs() + mut args := []string{} + args << defines + args << others + return args +} + +pub fn (cflags []CFlag) c_options_after_target() []string { + _, _, libs := cflags.defines_others_libs() + return libs +} + +pub fn (cflags []CFlag) c_options_without_object_files() []string { + mut args := []string{} + for flag in cflags { + if flag.value.ends_with('.o') || flag.value.ends_with('.obj') { + continue + } + args << flag.format() + } + return args +} + +pub fn (cflags []CFlag) c_options_only_object_files() []string { + mut args := []string{} + for flag in cflags { + if flag.value.ends_with('.o') || flag.value.ends_with('.obj') { + args << flag.format() + } + } + return args +} + +pub fn (cflags []CFlag) defines_others_libs() ([]string, []string, []string) { + copts_without_obj_files := cflags.c_options_without_object_files() + mut defines := []string{} + mut others := []string{} + mut libs := []string{} + for copt in copts_without_obj_files { + if copt.starts_with('-l') { + libs << copt + continue + } + if copt.starts_with('-D') { + defines << copt + continue + } + others << copt + } + return defines, others, libs +} diff --git a/v_windows/v/old/vlib/v/checker/check_types.v b/v_windows/v/old/vlib/v/checker/check_types.v new file mode 100644 index 0000000..553a1ae --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/check_types.v @@ -0,0 +1,733 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module checker + +import v.ast +import v.token + +pub fn (mut c Checker) check_expected_call_arg(got ast.Type, expected_ ast.Type, language ast.Language) ? { + mut expected := expected_ + // variadic + if expected.has_flag(.variadic) { + exp_type_sym := c.table.get_type_symbol(expected_) + exp_info := exp_type_sym.info as ast.Array + expected = exp_info.elem_type + } + if language == .c { + // allow number types to be used interchangeably + if got.is_number() && expected.is_number() { + return + } + // mode_t - currently using u32 as mode_t for C fns + // if got.idx() in [ast.int_type_idx, ast.u32_type_idx] && expected.idx() in [ast.int_type_idx, ast.u32_type_idx] { + // return + // } + // allow number to be used as size_t + if got.is_number() && expected.idx() == ast.size_t_type_idx { + return + } + // allow bool & int to be used interchangeably for C functions + if (got.idx() == ast.bool_type_idx + && expected.idx() in [ast.int_type_idx, ast.int_literal_type_idx]) + || (expected.idx() == ast.bool_type_idx + && got.idx() in [ast.int_type_idx, ast.int_literal_type_idx]) { + return + } + exp_sym := c.table.get_type_symbol(expected) + // unknown C types are set to int, allow int to be used for types like `&C.FILE` + // eg. `C.fflush(C.stderr)` - error: cannot use `int` as `&C.FILE` in argument 1 to `C.fflush` + if expected.is_ptr() && exp_sym.language == .c && exp_sym.kind in [.placeholder, .struct_] + && got == ast.int_type_idx { + return + } + } + if c.check_types(got, expected) { + return + } + idx_got := got.idx() + idx_expected := expected.idx() + if idx_got in [ast.byteptr_type_idx, ast.charptr_type_idx] + || idx_expected in [ast.byteptr_type_idx, ast.charptr_type_idx] { + igot := int(got) + iexpected := int(expected) + // TODO: remove; transitional compatibility for byteptr === &byte + if (igot == ast.byteptr_type_idx && iexpected == 65545) + || (iexpected == ast.byteptr_type_idx && igot == 65545) { + return + } + // TODO: remove; transitional compatibility for charptr === &char + if (igot == ast.charptr_type_idx && iexpected == 65551) + || (iexpected == ast.charptr_type_idx && igot == 65551) { + return + } + muls_got := got.nr_muls() + muls_expected := expected.nr_muls() + if idx_got == ast.byteptr_type_idx && idx_expected == ast.byte_type_idx + && muls_got + 1 == muls_expected { + return + } + if idx_expected == ast.byteptr_type_idx && idx_got == ast.byte_type_idx + && muls_expected + 1 == muls_got { + return + } + if idx_got == ast.charptr_type_idx && idx_expected == ast.char_type_idx + && muls_got + 1 == muls_expected { + return + } + if idx_expected == ast.charptr_type_idx && idx_got == ast.char_type_idx + && muls_expected + 1 == muls_got { + return + } + } + return error('cannot use `${c.table.type_to_str(got.clear_flag(.variadic))}` as `${c.table.type_to_str(expected.clear_flag(.variadic))}`') +} + +pub fn (mut c Checker) check_basic(got ast.Type, expected ast.Type) bool { + unalias_got, unalias_expected := c.table.unalias_num_type(got), c.table.unalias_num_type(expected) + if unalias_got.idx() == unalias_expected.idx() { + // this is returning true even if one type is a ptr + // and the other is not, is this correct behaviour? + return true + } + if (unalias_expected.is_pointer() || unalias_expected.is_number()) + && (unalias_got.is_pointer() || unalias_got.is_number()) { + return true + } + // allow pointers to be initialized with 0. TODO: use none instead + if expected.is_ptr() && unalias_got == ast.int_literal_type { + return true + } + // TODO: use sym so it can be absorbed into below [.voidptr, .any] logic + if expected.idx() == ast.array_type_idx || got.idx() == ast.array_type_idx { + return true + } + got_sym, exp_sym := c.table.get_type_symbol(got), c.table.get_type_symbol(expected) + // array/map as argument + if got_sym.kind in [.array, .map, .array_fixed] && exp_sym.kind == got_sym.kind { + if c.table.type_to_str(got) == c.table.type_to_str(expected).trim('&') { + return true + } + } + if !unalias_got.is_ptr() && got_sym.kind == .array_fixed + && (unalias_expected.is_pointer() || unalias_expected.is_ptr()) { + // fixed array needs to be a struct, not a pointer + return false + } + if exp_sym.kind in [.voidptr, .any] || got_sym.kind in [.voidptr, .any] { + return true + } + // sum type + if c.table.sumtype_has_variant(expected, c.table.mktyp(got)) { + return true + } + // type alias + if (got_sym.kind == .alias && got_sym.parent_idx == expected.idx()) + || (exp_sym.kind == .alias && exp_sym.parent_idx == got.idx()) { + return true + } + // fn type + if got_sym.kind == .function && exp_sym.kind == .function { + return c.check_matching_function_symbols(got_sym, exp_sym) + } + // allow `return 0` in a function with `?int` return type + expected_nonflagged := expected.clear_flags() + if got == ast.int_literal_type && expected_nonflagged.is_int() { + return true + } + // allow `return 0` in a function with `?f32` return type + if got == ast.float_literal_type && expected_nonflagged.is_float() { + return true + } + return false +} + +pub fn (mut c Checker) check_matching_function_symbols(got_type_sym &ast.TypeSymbol, exp_type_sym &ast.TypeSymbol) bool { + got_info := got_type_sym.info as ast.FnType + exp_info := exp_type_sym.info as ast.FnType + got_fn := got_info.func + exp_fn := exp_info.func + // we are using check() to compare return type & args as they might include + // functions themselves. TODO: optimize, only use check() when needed + if got_fn.params.len != exp_fn.params.len { + return false + } + if !c.check_basic(got_fn.return_type, exp_fn.return_type) { + return false + } + for i, got_arg in got_fn.params { + exp_arg := exp_fn.params[i] + exp_arg_is_ptr := exp_arg.typ.is_ptr() || exp_arg.typ.is_pointer() + got_arg_is_ptr := got_arg.typ.is_ptr() || got_arg.typ.is_pointer() + if exp_arg_is_ptr != got_arg_is_ptr { + exp_arg_pointedness := if exp_arg_is_ptr { 'a pointer' } else { 'NOT a pointer' } + got_arg_pointedness := if got_arg_is_ptr { 'a pointer' } else { 'NOT a pointer' } + c.add_error_detail('`$exp_fn.name`\'s expected fn argument: `$exp_arg.name` is $exp_arg_pointedness, but the passed fn argument: `$got_arg.name` is $got_arg_pointedness') + return false + } + if !c.check_basic(got_arg.typ, exp_arg.typ) { + return false + } + } + return true +} + +[inline] +fn (mut c Checker) check_shift(left_type ast.Type, right_type ast.Type, left_pos token.Position, right_pos token.Position) ast.Type { + if !left_type.is_int() { + // maybe it's an int alias? TODO move this to is_int() ? + sym := c.table.get_type_symbol(left_type) + if sym.kind == .alias && (sym.info as ast.Alias).parent_type.is_int() { + return left_type + } + if c.pref.translated && left_type == ast.bool_type { + // allow `bool << 2` in translated C code + return ast.int_type + } + c.error('invalid operation: shift on type `$sym.name`', left_pos) + return ast.void_type + } else if !right_type.is_int() { + c.error('cannot shift non-integer type `${c.table.get_type_symbol(right_type).name}` into type `${c.table.get_type_symbol(left_type).name}`', + right_pos) + return ast.void_type + } + return left_type +} + +pub fn (mut c Checker) promote(left_type ast.Type, right_type ast.Type) ast.Type { + if left_type.is_any_kind_of_pointer() { + if right_type.is_int() { + return left_type + } else { + return ast.void_type + } + } else if right_type.is_any_kind_of_pointer() { + if left_type.is_int() { + return right_type + } else { + return ast.void_type + } + } + if left_type == right_type { + return left_type // strings, self defined operators + } + if right_type.is_number() && left_type.is_number() { + return c.promote_num(left_type, right_type) + } else if left_type.has_flag(.optional) != right_type.has_flag(.optional) { + // incompatible + return ast.void_type + } else { + return left_type // default to left if not automatic promotion possible + } +} + +fn (c &Checker) promote_num(left_type ast.Type, right_type ast.Type) ast.Type { + // sort the operands to save time + mut type_hi := left_type + mut type_lo := right_type + if type_hi.idx() < type_lo.idx() { + type_hi, type_lo = type_lo, type_hi + } + idx_hi := type_hi.idx() + idx_lo := type_lo.idx() + // the following comparisons rely on the order of the indices in table/types.v + if idx_hi == ast.int_literal_type_idx { + return type_lo + } else if idx_hi == ast.float_literal_type_idx { + if idx_lo in ast.float_type_idxs { + return type_lo + } else { + return ast.void_type + } + } else if type_hi.is_float() { + if idx_hi == ast.f32_type_idx { + if idx_lo in [ast.i64_type_idx, ast.u64_type_idx] { + return ast.void_type + } else { + return type_hi + } + } else { // f64, float_literal + return type_hi + } + } else if idx_lo >= ast.byte_type_idx { // both operands are unsigned + return type_hi + } else if idx_lo >= ast.i8_type_idx + && (idx_hi <= ast.i64_type_idx || idx_hi == ast.rune_type_idx) { // both signed + return if idx_lo == ast.i64_type_idx { type_lo } else { type_hi } + } else if idx_hi - idx_lo < (ast.byte_type_idx - ast.i8_type_idx) { + return type_lo // conversion unsigned -> signed if signed type is larger + } else { + return ast.void_type // conversion signed -> unsigned not allowed + } +} + +// TODO: promote(), check_types(), symmetric_check() and check() overlap - should be rearranged +pub fn (mut c Checker) check_types(got ast.Type, expected ast.Type) bool { + if got == expected { + return true + } + got_is_ptr := got.is_ptr() + exp_is_ptr := expected.is_ptr() + if got_is_ptr && exp_is_ptr { + if got.nr_muls() != expected.nr_muls() { + return false + } + } + exp_idx := expected.idx() + got_idx := got.idx() + if exp_idx == got_idx { + return true + } + if exp_idx == ast.voidptr_type_idx || exp_idx == ast.byteptr_type_idx + || (expected.is_ptr() && expected.deref().idx() == ast.byte_type_idx) { + if got.is_ptr() || got.is_pointer() { + return true + } + } + // allow direct int-literal assignment for pointers for now + // maybe in the future optionals should be used for that + if expected.is_ptr() || expected.is_pointer() { + if got == ast.int_literal_type { + return true + } + } + if got_idx == ast.voidptr_type_idx || got_idx == ast.byteptr_type_idx + || (got_idx == ast.byte_type_idx && got.is_ptr()) { + if expected.is_ptr() || expected.is_pointer() { + return true + } + } + if expected == ast.charptr_type && got == ast.char_type.to_ptr() { + return true + } + if expected.has_flag(.optional) { + sym := c.table.get_type_symbol(got) + if (sym.kind == .interface_ && sym.name == 'IError') + || got in [ast.none_type, ast.error_type] { + return true + } else if !c.check_basic(got, expected.clear_flag(.optional)) { + return false + } + } + if !c.check_basic(got, expected) { // TODO: this should go away... + return false + } + if got.is_number() && expected.is_number() { + if got == ast.rune_type && expected == ast.byte_type { + return true + } else if expected == ast.rune_type && got == ast.byte_type { + return true + } + if c.promote_num(expected, got) != expected { + // println('could not promote ${c.table.get_type_symbol(got).name} to ${c.table.get_type_symbol(expected).name}') + return false + } + } + if expected.has_flag(.generic) { + return false + } + return true +} + +pub fn (mut c Checker) check_expected(got ast.Type, expected ast.Type) ? { + if !c.check_types(got, expected) { + return error(c.expected_msg(got, expected)) + } +} + +[inline] +fn (c &Checker) expected_msg(got ast.Type, expected ast.Type) string { + exps := c.table.type_to_str(expected) + gots := c.table.type_to_str(got) + return 'expected `$exps`, not `$gots`' +} + +pub fn (mut c Checker) symmetric_check(left ast.Type, right ast.Type) bool { + // allow direct int-literal assignment for pointers for now + // maybe in the future optionals should be used for that + if right.is_ptr() || right.is_pointer() { + if left == ast.int_literal_type { + return true + } + } + // allow direct int-literal assignment for pointers for now + if left.is_ptr() || left.is_pointer() { + if right == ast.int_literal_type { + return true + } + } + return c.check_basic(left, right) +} + +pub fn (mut c Checker) get_default_fmt(ftyp ast.Type, typ ast.Type) byte { + if ftyp.has_flag(.optional) { + return `s` + } else if typ.is_float() { + return `g` + } else if typ.is_signed() || typ.is_int_literal() { + return `d` + } else if typ.is_unsigned() { + return `u` + } else if typ.is_pointer() { + return `p` + } else { + mut sym := c.table.get_type_symbol(c.unwrap_generic(ftyp)) + if sym.kind == .alias { + // string aliases should be printable + info := sym.info as ast.Alias + sym = c.table.get_type_symbol(info.parent_type) + if info.parent_type == ast.string_type { + return `s` + } + } + if sym.kind == .function { + return `s` + } + if ftyp in [ast.string_type, ast.bool_type] + || sym.kind in [.enum_, .array, .array_fixed, .struct_, .map, .multi_return, .sum_type, .interface_, .none_] + || ftyp.has_flag(.optional) || sym.has_method('str') { + return `s` + } else { + return `_` + } + } +} + +pub fn (mut c Checker) fail_if_unreadable(expr ast.Expr, typ ast.Type, what string) { + mut pos := token.Position{} + match expr { + ast.Ident { + if typ.has_flag(.shared_f) { + if expr.name !in c.rlocked_names && expr.name !in c.locked_names { + action := if what == 'argument' { 'passed' } else { 'used' } + c.error('`$expr.name` is `shared` and must be `rlock`ed or `lock`ed to be $action as non-mut $what', + expr.pos) + } + } + return + } + ast.SelectorExpr { + pos = expr.pos + if typ.has_flag(.shared_f) { + expr_name := '${expr.expr}.$expr.field_name' + if expr_name !in c.rlocked_names && expr_name !in c.locked_names { + action := if what == 'argument' { 'passed' } else { 'used' } + c.error('`$expr_name` is `shared` and must be `rlock`ed or `lock`ed to be $action as non-mut $what', + expr.pos) + } + return + } else { + c.fail_if_unreadable(expr.expr, expr.expr_type, what) + } + } + ast.IndexExpr { + pos = expr.left.position().extend(expr.pos) + c.fail_if_unreadable(expr.left, expr.left_type, what) + } + else {} + } + if typ.has_flag(.shared_f) { + c.error('you have to create a handle and `rlock` it to use a `shared` element as non-mut $what', + pos) + } +} + +pub fn (mut c Checker) string_inter_lit(mut node ast.StringInterLiteral) ast.Type { + inside_println_arg_save := c.inside_println_arg + c.inside_println_arg = true + for i, expr in node.exprs { + ftyp := c.expr(expr) + if ftyp == ast.void_type { + c.error('expression does not return a value', expr.position()) + } else if ftyp == ast.char_type && ftyp.nr_muls() == 0 { + c.error('expression returning type `char` cannot be used in string interpolation directly, print its address or cast it to an integer instead', + expr.position()) + } + c.fail_if_unreadable(expr, ftyp, 'interpolation object') + node.expr_types << ftyp + typ := c.table.unalias_num_type(ftyp) + mut fmt := node.fmts[i] + // analyze and validate format specifier + if fmt !in [`E`, `F`, `G`, `e`, `f`, `g`, `d`, `u`, `x`, `X`, `o`, `c`, `s`, `S`, `p`, + `_`, + ] { + c.error('unknown format specifier `${fmt:c}`', node.fmt_poss[i]) + } + if fmt == `_` { // set default representation for type if none has been given + fmt = c.get_default_fmt(ftyp, typ) + if fmt == `_` { + if typ != ast.void_type { + c.error('no known default format for type `${c.table.get_type_name(ftyp)}`', + node.fmt_poss[i]) + } + } else { + node.fmts[i] = fmt + node.need_fmts[i] = false + } + } else { // check if given format specifier is valid for type + if node.precisions[i] != 987698 && !typ.is_float() { + c.error('precision specification only valid for float types', node.fmt_poss[i]) + } + if node.pluss[i] && !typ.is_number() { + c.error('plus prefix only allowed for numbers', node.fmt_poss[i]) + } + if (typ.is_unsigned() && fmt !in [`u`, `x`, `X`, `o`, `c`]) + || (typ.is_signed() && fmt !in [`d`, `x`, `X`, `o`, `c`]) + || (typ.is_int_literal() && fmt !in [`d`, `c`, `x`, `X`, `o`, `u`, `x`, `X`, `o`]) + || (typ.is_float() && fmt !in [`E`, `F`, `G`, `e`, `f`, `g`]) + || (typ.is_pointer() && fmt !in [`p`, `x`, `X`]) + || (typ.is_string() && fmt !in [`s`, `S`]) + || (typ.idx() in [ast.i64_type_idx, ast.f64_type_idx] && fmt == `c`) { + c.error('illegal format specifier `${fmt:c}` for type `${c.table.get_type_name(ftyp)}`', + node.fmt_poss[i]) + } + node.need_fmts[i] = fmt != c.get_default_fmt(ftyp, typ) + } + // check recursive str + if c.table.cur_fn.is_method && c.table.cur_fn.name == 'str' + && c.table.cur_fn.receiver.name == expr.str() { + c.error('cannot call `str()` method recursively', expr.position()) + } + } + c.inside_println_arg = inside_println_arg_save + return ast.string_type +} + +const hex_lit_overflow_message = 'hex character literal overflows string' + +pub fn (mut c Checker) string_lit(mut node ast.StringLiteral) ast.Type { + mut idx := 0 + for idx < node.val.len { + match node.val[idx] { + `\\` { + mut start_pos := token.Position{ + ...node.pos + col: node.pos.col + 1 + idx + } + start_idx := idx + idx++ + next_ch := node.val[idx] or { return ast.string_type } + if next_ch == `x` { + idx++ + mut ch := node.val[idx] or { return ast.string_type } + mut hex_char_count := 0 + for ch.is_hex_digit() { + hex_char_count++ + end_pos := token.Position{ + ...start_pos + len: idx + 1 - start_idx + } + match hex_char_count { + 1...5 {} + 6 { + first_digit := node.val[idx - 5] - 48 + second_digit := node.val[idx - 4] - 48 + if first_digit > 1 { + c.error(checker.hex_lit_overflow_message, end_pos) + } else if first_digit == 1 && second_digit > 0 { + c.error(checker.hex_lit_overflow_message, end_pos) + } + } + else { + c.error(checker.hex_lit_overflow_message, end_pos) + } + } + idx++ + ch = node.val[idx] or { return ast.string_type } + } + } + } + else { + idx++ + } + } + } + return ast.string_type +} + +pub fn (mut c Checker) int_lit(mut node ast.IntegerLiteral) ast.Type { + if node.val.len < 17 { + // can not be a too large number, no need for more expensive checks + return ast.int_literal_type + } + lit := node.val.replace('_', '').all_after('-') + is_neg := node.val.starts_with('-') + limit := if is_neg { '9223372036854775808' } else { '18446744073709551615' } + message := 'integer literal $node.val overflows int' + + if lit.len > limit.len { + c.error(message, node.pos) + } else if lit.len == limit.len { + for i, digit in lit { + if digit > limit[i] { + c.error(message, node.pos) + } else if digit < limit[i] { + break + } + } + } + + return ast.int_literal_type +} + +pub fn (mut c Checker) infer_fn_generic_types(f ast.Fn, mut call_expr ast.CallExpr) { + mut inferred_types := []ast.Type{} + for gi, gt_name in f.generic_names { + // skip known types + if gi < call_expr.concrete_types.len { + inferred_types << call_expr.concrete_types[gi] + continue + } + mut typ := ast.void_type + for i, param in f.params { + mut to_set := ast.void_type + // resolve generic struct receiver + if i == 0 && call_expr.is_method && param.typ.has_flag(.generic) { + sym := c.table.get_type_symbol(call_expr.receiver_type) + match sym.info { + ast.Struct, ast.Interface, ast.SumType { + if c.table.cur_fn.generic_names.len > 0 { // in generic fn + if gt_name in c.table.cur_fn.generic_names + && c.table.cur_fn.generic_names.len == c.table.cur_concrete_types.len { + idx := c.table.cur_fn.generic_names.index(gt_name) + typ = c.table.cur_concrete_types[idx] + } + } else { // in non-generic fn + receiver_generic_names := sym.info.generic_types.map(c.table.get_type_symbol(it).name) + if gt_name in receiver_generic_names + && sym.info.generic_types.len == sym.info.concrete_types.len { + idx := receiver_generic_names.index(gt_name) + typ = sym.info.concrete_types[idx] + } + } + } + else {} + } + } + arg_i := if i != 0 && call_expr.is_method { i - 1 } else { i } + if call_expr.args.len <= arg_i { + break + } + arg := call_expr.args[arg_i] + param_type_sym := c.table.get_type_symbol(param.typ) + + if param.typ.has_flag(.generic) && param_type_sym.name == gt_name { + to_set = c.table.mktyp(arg.typ) + sym := c.table.get_type_symbol(arg.typ) + if sym.info is ast.FnType { + mut func := sym.info.func + func.name = '' + idx := c.table.find_or_register_fn_type(c.mod, func, true, false) + to_set = ast.new_type(idx).derive(arg.typ) + } + if arg.expr.is_auto_deref_var() { + to_set = to_set.deref() + } + // resolve &T &&T ... + if param.typ.nr_muls() > 0 && to_set.nr_muls() > 0 { + to_set = to_set.set_nr_muls(0) + } + // If the parent fn param is a generic too + if to_set.has_flag(.generic) { + to_set = c.unwrap_generic(to_set) + } + } else if param.typ.has_flag(.generic) { + arg_sym := c.table.get_type_symbol(arg.typ) + if arg_sym.kind == .array && param_type_sym.kind == .array { + mut arg_elem_info := arg_sym.info as ast.Array + mut param_elem_info := param_type_sym.info as ast.Array + mut arg_elem_sym := c.table.get_type_symbol(arg_elem_info.elem_type) + mut param_elem_sym := c.table.get_type_symbol(param_elem_info.elem_type) + for { + if arg_elem_sym.kind == .array && param_elem_sym.kind == .array + && param_elem_sym.name !in c.table.cur_fn.generic_names { + arg_elem_info = arg_elem_sym.info as ast.Array + arg_elem_sym = c.table.get_type_symbol(arg_elem_info.elem_type) + param_elem_info = param_elem_sym.info as ast.Array + param_elem_sym = c.table.get_type_symbol(param_elem_info.elem_type) + } else { + to_set = arg_elem_info.elem_type + break + } + } + } else if arg_sym.kind == .array_fixed && param_type_sym.kind == .array_fixed { + mut arg_elem_info := arg_sym.info as ast.ArrayFixed + mut param_elem_info := param_type_sym.info as ast.ArrayFixed + mut arg_elem_sym := c.table.get_type_symbol(arg_elem_info.elem_type) + mut param_elem_sym := c.table.get_type_symbol(param_elem_info.elem_type) + for { + if arg_elem_sym.kind == .array_fixed && param_elem_sym.kind == .array_fixed + && param_elem_sym.name !in c.table.cur_fn.generic_names { + arg_elem_info = arg_elem_sym.info as ast.ArrayFixed + arg_elem_sym = c.table.get_type_symbol(arg_elem_info.elem_type) + param_elem_info = param_elem_sym.info as ast.ArrayFixed + param_elem_sym = c.table.get_type_symbol(param_elem_info.elem_type) + } else { + to_set = arg_elem_info.elem_type + break + } + } + } else if arg_sym.kind == .map && param_type_sym.kind == .map { + arg_map_info := arg_sym.info as ast.Map + param_map_info := param_type_sym.info as ast.Map + if param_map_info.key_type.has_flag(.generic) + && c.table.get_type_symbol(param_map_info.key_type).name == gt_name { + typ = arg_map_info.key_type + } + if param_map_info.value_type.has_flag(.generic) + && c.table.get_type_symbol(param_map_info.value_type).name == gt_name { + typ = arg_map_info.value_type + } + } else if param.typ.has_flag(.variadic) { + to_set = c.table.mktyp(arg.typ) + } else if arg_sym.kind in [.struct_, .interface_, .sum_type] { + mut generic_types := []ast.Type{} + mut concrete_types := []ast.Type{} + match mut arg_sym.info { + ast.Struct, ast.Interface, ast.SumType { + generic_types = arg_sym.info.generic_types + concrete_types = arg_sym.info.concrete_types + } + else {} + } + generic_names := generic_types.map(c.table.get_type_symbol(it).name) + if gt_name in generic_names && generic_types.len == concrete_types.len { + idx := generic_names.index(gt_name) + typ = concrete_types[idx] + } + } + } + + if to_set != ast.void_type { + if typ != ast.void_type { + // try to promote + // only numbers so we don't promote pointers + if typ.is_number() && to_set.is_number() { + promoted := c.promote_num(typ, to_set) + if promoted != ast.void_type { + to_set = promoted + } + } + if !c.check_types(typ, to_set) { + c.error('inferred generic type `$gt_name` is ambiguous: got `${c.table.get_type_symbol(to_set).name}`, expected `${c.table.get_type_symbol(typ).name}`', + arg.pos) + } + } + typ = to_set + } + } + if typ == ast.void_type { + c.error('could not infer generic type `$gt_name` in call to `$f.name`', call_expr.pos) + return + } + if c.pref.is_verbose { + s := c.table.type_to_str(typ) + println('inferred `$f.name<$s>`') + } + inferred_types << typ + call_expr.concrete_types << typ + } + if c.table.register_fn_concrete_types(f.name, inferred_types) { + c.need_recheck_generic_fns = true + } +} diff --git a/v_windows/v/old/vlib/v/checker/checker.v b/v_windows/v/old/vlib/v/checker/checker.v new file mode 100644 index 0000000..dd179c9 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/checker.v @@ -0,0 +1,8283 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license that can be found in the LICENSE file. +module checker + +import os +import strings +import time +import v.ast +import v.vmod +import v.token +import v.pref +import v.util +import v.util.version +import v.errors +import v.pkgconfig +import v.gen.native + +const int_min = int(0x80000000) + +const int_max = int(0x7FFFFFFF) + +const ( + valid_comp_if_os = ['windows', 'ios', 'macos', 'mach', 'darwin', 'hpux', 'gnu', + 'qnx', 'linux', 'freebsd', 'openbsd', 'netbsd', 'bsd', 'dragonfly', 'android', 'solaris', + 'haiku', 'serenity', 'vinix'] + valid_comp_if_compilers = ['gcc', 'tinyc', 'clang', 'mingw', 'msvc', 'cplusplus'] + valid_comp_if_platforms = ['amd64', 'i386', 'aarch64', 'arm64', 'arm32', 'rv64', 'rv32'] + valid_comp_if_cpu_features = ['x64', 'x32', 'little_endian', 'big_endian'] + valid_comp_if_other = ['js', 'debug', 'prod', 'test', 'glibc', 'prealloc', + 'no_bounds_checking', 'freestanding', 'threads', 'js_node', 'js_browser', 'js_freestanding'] + valid_comp_not_user_defined = all_valid_comptime_idents() + array_builtin_methods = ['filter', 'clone', 'repeat', 'reverse', 'map', 'slice', 'sort', + 'contains', 'index', 'wait', 'any', 'all', 'first', 'last', 'pop'] + vroot_is_deprecated_message = '@VROOT is deprecated, use @VMODROOT or @VEXEROOT instead' +) + +fn all_valid_comptime_idents() []string { + mut res := []string{} + res << checker.valid_comp_if_os + res << checker.valid_comp_if_compilers + res << checker.valid_comp_if_platforms + res << checker.valid_comp_if_cpu_features + res << checker.valid_comp_if_other + return res +} + +[heap] +pub struct Checker { + pref &pref.Preferences // Preferences shared from V struct +pub mut: + table &ast.Table + file &ast.File = 0 + nr_errors int + nr_warnings int + nr_notices int + errors []errors.Error + warnings []errors.Warning + notices []errors.Notice + error_lines []int // to avoid printing multiple errors for the same line + expected_type ast.Type + expected_or_type ast.Type // fn() or { 'this type' } eg. string. expected or block type + const_decl string + const_deps []string + const_names []string + global_names []string + locked_names []string // vars that are currently locked + rlocked_names []string // vars that are currently read-locked + in_for_count int // if checker is currently in a for loop + // checked_ident string // to avoid infinite checker loops + returns bool + scope_returns bool + mod string // current module name + is_builtin_mod bool // are we in `builtin`? + inside_unsafe bool + inside_const bool + inside_anon_fn bool + inside_ref_lit bool + inside_defer bool + inside_fn_arg bool // `a`, `b` in `a.f(b)` + inside_ct_attr bool // true inside [if expr] + skip_flags bool // should `#flag` and `#include` be skipped + fn_level int // 0 for the top level, 1 for `fn abc() {}`, 2 for a nested fn, etc + ct_cond_stack []ast.Expr +mut: + files []ast.File + expr_level int // to avoid infinite recursion segfaults due to compiler bugs + inside_sql bool // to handle sql table fields pseudo variables + cur_orm_ts ast.TypeSymbol + error_details []string + vmod_file_content string // needed for @VMOD_FILE, contents of the file, *NOT its path** + vweb_gen_types []ast.Type // vweb route checks + prevent_sum_type_unwrapping_once bool // needed for assign new values to sum type, stopping unwrapping then + loop_label string // set when inside a labelled for loop + timers &util.Timers = util.new_timers(false) + comptime_fields_type map[string]ast.Type + fn_scope &ast.Scope = voidptr(0) + main_fn_decl_node ast.FnDecl + match_exhaustive_cutoff_limit int = 10 + // TODO: these are here temporarily and used for deprecations; remove soon + using_new_err_struct bool + inside_selector_expr bool + inside_println_arg bool + inside_decl_rhs bool + need_recheck_generic_fns bool // need recheck generic fns because there are cascaded nested generic fn +} + +pub fn new_checker(table &ast.Table, pref &pref.Preferences) &Checker { + mut timers_should_print := false + $if time_checking ? { + timers_should_print = true + } + return &Checker{ + table: table + pref: pref + timers: util.new_timers(timers_should_print) + match_exhaustive_cutoff_limit: pref.checker_match_exhaustive_cutoff_limit + } +} + +pub fn (mut c Checker) check(ast_file &ast.File) { + c.change_current_file(ast_file) + for i, ast_import in ast_file.imports { + for sym in ast_import.syms { + full_name := ast_import.mod + '.' + sym.name + if full_name in c.const_names { + c.error('cannot selectively import constant `$sym.name` from `$ast_import.mod`, import `$ast_import.mod` and use `$full_name` instead', + sym.pos) + } + } + for j in 0 .. i { + if ast_import.mod == ast_file.imports[j].mod { + c.error('`$ast_import.mod` was already imported on line ${ + ast_file.imports[j].mod_pos.line_nr + 1}', ast_import.mod_pos) + } + } + } + for mut stmt in ast_file.stmts { + if stmt is ast.ConstDecl || stmt is ast.ExprStmt { + c.expr_level = 0 + c.stmt(stmt) + } + } + for mut stmt in ast_file.stmts { + if stmt is ast.GlobalDecl { + c.expr_level = 0 + c.stmt(stmt) + } + } + for mut stmt in ast_file.stmts { + if stmt !is ast.ConstDecl && stmt !is ast.GlobalDecl && stmt !is ast.ExprStmt { + c.expr_level = 0 + c.stmt(stmt) + } + } + c.check_scope_vars(c.file.scope) +} + +pub fn (mut c Checker) check_scope_vars(sc &ast.Scope) { + if !c.pref.is_repl && !c.file.is_test { + for _, obj in sc.objects { + match obj { + ast.Var { + if !obj.is_used && obj.name[0] != `_` { + c.warn('unused variable: `$obj.name`', obj.pos) + } + if obj.is_mut && !obj.is_changed && !c.is_builtin_mod && obj.name != 'it' { + // if obj.is_mut && !obj.is_changed && !c.is_builtin { //TODO C error bad field not checked + // c.warn('`$obj.name` is declared as mutable, but it was never changed', + // obj.pos) + } + } + else {} + } + } + } + for child in sc.children { + c.check_scope_vars(child) + } +} + +// not used right now +pub fn (mut c Checker) check2(ast_file &ast.File) []errors.Error { + c.change_current_file(ast_file) + for stmt in ast_file.stmts { + c.stmt(stmt) + } + return c.errors +} + +pub fn (mut c Checker) change_current_file(file &ast.File) { + c.file = unsafe { file } + c.vmod_file_content = '' + c.mod = file.mod.name +} + +pub fn (mut c Checker) check_files(ast_files []&ast.File) { + // c.files = ast_files + mut has_main_mod_file := false + mut has_main_fn := false + mut files_from_main_module := []&ast.File{} + for i in 0 .. ast_files.len { + file := unsafe { ast_files[i] } + c.timers.start('checker_check $file.path') + c.check(file) + if file.mod.name == 'main' { + files_from_main_module << file + has_main_mod_file = true + if c.file_has_main_fn(file) { + has_main_fn = true + } + } + c.timers.show('checker_check $file.path') + } + if has_main_mod_file && !has_main_fn && files_from_main_module.len > 0 { + if c.pref.is_script && !c.pref.is_test { + // files_from_main_module contain preludes at the start + mut the_main_file := files_from_main_module.last() + the_main_file.stmts << ast.FnDecl{ + name: 'main.main' + mod: 'main' + is_main: true + file: the_main_file.path + return_type: ast.void_type + scope: &ast.Scope{ + parent: 0 + } + } + has_main_fn = true + } + } + c.timers.start('checker_post_process_generic_fns') + last_file := c.file + // post process generic functions. must be done after all files have been + // checked, to eunsure all generic calls are processed as this information + // is needed when the generic type is auto inferred from the call argument + // Check more times if there are more new registered fn concrete types + for { + for file in ast_files { + if file.generic_fns.len > 0 { + c.change_current_file(file) + c.post_process_generic_fns() + } + } + if !c.need_recheck_generic_fns { + break + } + c.need_recheck_generic_fns = false + } + // restore the original c.file && c.mod after post processing + c.change_current_file(last_file) + c.timers.show('checker_post_process_generic_fns') + + c.timers.start('checker_verify_all_vweb_routes') + c.verify_all_vweb_routes() + c.timers.show('checker_verify_all_vweb_routes') + + if c.pref.is_test { + mut n_test_fns := 0 + for _, f in c.table.fns { + if f.is_test { + n_test_fns++ + } + } + if n_test_fns == 0 { + c.add_error_detail('The name of a test function in V, should start with `test_`.') + c.add_error_detail('The test function should take 0 parameters, and no return type. Example:') + c.add_error_detail('fn test_xyz(){ assert 2 + 2 == 4 }') + c.error('a _test.v file should have *at least* one `test_` function', token.Position{}) + } + } + // Make sure fn main is defined in non lib builds + if c.pref.build_mode == .build_module || c.pref.is_test { + return + } + if c.pref.is_shared { + // shared libs do not need to have a main + return + } + if !has_main_mod_file { + c.error('project must include a `main` module or be a shared library (compile with `v -shared`)', + token.Position{}) + } else if !has_main_fn { + c.error('function `main` must be declared in the main module', token.Position{}) + } +} + +// do checks specific to files in main module +// returns `true` if a main function is in the file +fn (mut c Checker) file_has_main_fn(file &ast.File) bool { + mut has_main_fn := false + for stmt in file.stmts { + if stmt is ast.FnDecl { + if stmt.name == 'main.main' { + if has_main_fn { + c.error('function `main` is already defined', stmt.pos) + } + has_main_fn = true + if stmt.params.len > 0 { + c.error('function `main` cannot have arguments', stmt.pos) + } + if stmt.return_type != ast.void_type { + c.error('function `main` cannot return values', stmt.pos) + } + if stmt.no_body { + c.error('function `main` must declare a body', stmt.pos) + } + } else if stmt.attrs.contains('console') { + c.error('only `main` can have the `[console]` attribute', stmt.pos) + } + } + } + return has_main_fn +} + +fn (mut c Checker) check_valid_snake_case(name string, identifier string, pos token.Position) { + if !c.pref.is_vweb && !c.pref.translated && name.len > 0 + && (name[0] == `_` || name.contains('._')) { + c.error('$identifier `$name` cannot start with `_`', pos) + } + if !c.pref.experimental && !c.pref.translated && util.contains_capital(name) { + c.error('$identifier `$name` cannot contain uppercase letters, use snake_case instead', + pos) + } +} + +fn stripped_name(name string) string { + idx := name.last_index('.') or { -1 } + return name[(idx + 1)..] +} + +fn (mut c Checker) check_valid_pascal_case(name string, identifier string, pos token.Position) { + sname := stripped_name(name) + if sname.len > 0 && !sname[0].is_capital() && !c.pref.translated { + c.error('$identifier `$name` must begin with capital letter', pos) + } +} + +pub fn (mut c Checker) type_decl(node ast.TypeDecl) { + match node { + ast.AliasTypeDecl { c.alias_type_decl(node) } + ast.FnTypeDecl { c.fn_type_decl(node) } + ast.SumTypeDecl { c.sum_type_decl(node) } + } +} + +pub fn (mut c Checker) alias_type_decl(node ast.AliasTypeDecl) { + // TODO Remove when `u8` isn't an alias in builtin anymore + if c.file.mod.name != 'builtin' { + c.check_valid_pascal_case(node.name, 'type alias', node.pos) + } + c.ensure_type_exists(node.parent_type, node.type_pos) or { return } + typ_sym := c.table.get_type_symbol(node.parent_type) + if typ_sym.kind in [.placeholder, .int_literal, .float_literal] { + c.error('unknown type `$typ_sym.name`', node.type_pos) + } else if typ_sym.kind == .alias { + orig_sym := c.table.get_type_symbol((typ_sym.info as ast.Alias).parent_type) + c.error('type `$typ_sym.str()` is an alias, use the original alias type `$orig_sym.name` instead', + node.type_pos) + } else if typ_sym.kind == .chan { + c.error('aliases of `chan` types are not allowed.', node.type_pos) + } +} + +pub fn (mut c Checker) fn_type_decl(node ast.FnTypeDecl) { + c.check_valid_pascal_case(node.name, 'fn type', node.pos) + typ_sym := c.table.get_type_symbol(node.typ) + fn_typ_info := typ_sym.info as ast.FnType + fn_info := fn_typ_info.func + c.ensure_type_exists(fn_info.return_type, fn_info.return_type_pos) or {} + ret_sym := c.table.get_type_symbol(fn_info.return_type) + if ret_sym.kind == .placeholder { + c.error('unknown type `$ret_sym.name`', fn_info.return_type_pos) + } + for arg in fn_info.params { + c.ensure_type_exists(arg.typ, arg.type_pos) or { return } + arg_sym := c.table.get_type_symbol(arg.typ) + if arg_sym.kind == .placeholder { + c.error('unknown type `$arg_sym.name`', arg.type_pos) + } + } +} + +pub fn (mut c Checker) sum_type_decl(node ast.SumTypeDecl) { + c.check_valid_pascal_case(node.name, 'sum type', node.pos) + mut names_used := []string{} + for variant in node.variants { + if variant.typ.is_ptr() { + c.error('sum type cannot hold a reference type', variant.pos) + } + c.ensure_type_exists(variant.typ, variant.pos) or {} + mut sym := c.table.get_type_symbol(variant.typ) + if sym.name in names_used { + c.error('sum type $node.name cannot hold the type `$sym.name` more than once', + variant.pos) + } else if sym.kind in [.placeholder, .int_literal, .float_literal] { + c.error('unknown type `$sym.name`', variant.pos) + } else if sym.kind == .interface_ { + c.error('sum type cannot hold an interface', variant.pos) + } + if sym.name.trim_prefix(sym.mod + '.') == node.name { + c.error('sum type cannot hold itself', variant.pos) + } + names_used << sym.name + } +} + +pub fn (mut c Checker) expand_iface_embeds(idecl &ast.InterfaceDecl, level int, iface_embeds []ast.InterfaceEmbedding) []ast.InterfaceEmbedding { + // eprintln('> expand_iface_embeds: idecl.name: $idecl.name | level: $level | iface_embeds.len: $iface_embeds.len') + if level > 100 { + c.error('too many interface embedding levels: $level, for interface `$idecl.name`', + idecl.pos) + return [] + } + if iface_embeds.len == 0 { + return [] + } + mut res := map[int]ast.InterfaceEmbedding{} + mut ares := []ast.InterfaceEmbedding{} + for ie in iface_embeds { + if iface_decl := c.table.interfaces[ie.typ] { + mut list := iface_decl.ifaces + if !iface_decl.are_ifaces_expanded { + list = c.expand_iface_embeds(idecl, level + 1, iface_decl.ifaces) + c.table.interfaces[ie.typ].ifaces = list + c.table.interfaces[ie.typ].are_ifaces_expanded = true + } + for partial in list { + res[partial.typ] = partial + } + } + res[ie.typ] = ie + } + for _, v in res { + ares << v + } + return ares +} + +pub fn (mut c Checker) interface_decl(mut decl ast.InterfaceDecl) { + c.check_valid_pascal_case(decl.name, 'interface name', decl.pos) + mut decl_sym := c.table.get_type_symbol(decl.typ) + if mut decl_sym.info is ast.Interface { + if decl.ifaces.len > 0 { + all_ifaces := c.expand_iface_embeds(decl, 0, decl.ifaces) + // eprintln('> decl.name: $decl.name | decl.ifaces.len: $decl.ifaces.len | all_ifaces: $all_ifaces.len') + decl.ifaces = all_ifaces + mut emnames := map[string]int{} + mut emnames_ds := map[string]bool{} + mut emnames_ds_info := map[string]bool{} + mut efnames := map[string]int{} + mut efnames_ds_info := map[string]bool{} + for i, m in decl.methods { + emnames[m.name] = i + emnames_ds[m.name] = true + emnames_ds_info[m.name] = true + } + for i, f in decl.fields { + efnames[f.name] = i + efnames_ds_info[f.name] = true + } + // + for iface in all_ifaces { + isym := c.table.get_type_symbol(iface.typ) + if isym.kind != .interface_ { + c.error('interface `$decl.name` tries to embed `$isym.name`, but `$isym.name` is not an interface, but `$isym.kind`', + iface.pos) + continue + } + isym_info := isym.info as ast.Interface + for f in isym_info.fields { + if !efnames_ds_info[f.name] { + efnames_ds_info[f.name] = true + decl_sym.info.fields << f + } + } + for m in isym_info.methods { + if !emnames_ds_info[m.name] { + emnames_ds_info[m.name] = true + decl_sym.info.methods << m.new_method_with_receiver_type(decl.typ) + } + } + for m in isym.methods { + if !emnames_ds[m.name] { + emnames_ds[m.name] = true + decl_sym.methods << m.new_method_with_receiver_type(decl.typ) + } + } + if iface_decl := c.table.interfaces[iface.typ] { + for f in iface_decl.fields { + if f.name in efnames { + // already existing method name, check for conflicts + ifield := decl.fields[efnames[f.name]] + if field := c.table.find_field_with_embeds(isym, f.name) { + if ifield.typ != field.typ { + exp := c.table.type_to_str(ifield.typ) + got := c.table.type_to_str(field.typ) + c.error('embedded interface `$iface_decl.name` conflicts existing field: `$ifield.name`, expecting type: `$exp`, got type: `$got`', + ifield.pos) + } + } + } else { + efnames[f.name] = decl.fields.len + decl.fields << f + } + } + for m in iface_decl.methods { + if m.name in emnames { + // already existing field name, check for conflicts + imethod := decl.methods[emnames[m.name]] + if em_fn := decl_sym.find_method(imethod.name) { + if m_fn := isym.find_method(m.name) { + msg := c.table.is_same_method(m_fn, em_fn) + if msg.len > 0 { + em_sig := c.table.fn_signature(em_fn, skip_receiver: true) + m_sig := c.table.fn_signature(m_fn, skip_receiver: true) + c.error('embedded interface `$iface_decl.name` causes conflict: $msg, for interface method `$em_sig` vs `$m_sig`', + imethod.pos) + } + } + } + } else { + emnames[m.name] = decl.methods.len + mut new_method := m.new_method_with_receiver_type(decl.typ) + new_method.pos = iface.pos + decl.methods << new_method + } + } + } + } + } + for i, method in decl.methods { + if decl.language == .v { + c.check_valid_snake_case(method.name, 'method name', method.pos) + } + c.ensure_type_exists(method.return_type, method.return_type_pos) or { return } + for param in method.params { + c.ensure_type_exists(param.typ, param.pos) or { return } + } + for field in decl.fields { + field_sym := c.table.get_type_symbol(field.typ) + if field.name == method.name && field_sym.kind == .function { + c.error('type `$decl_sym.name` has both field and method named `$method.name`', + method.pos) + } + } + for j in 0 .. i { + if method.name == decl.methods[j].name { + c.error('duplicate method name `$method.name`', method.pos) + } + } + } + for i, field in decl.fields { + if decl.language == .v { + c.check_valid_snake_case(field.name, 'field name', field.pos) + } + c.ensure_type_exists(field.typ, field.pos) or { return } + if field.typ == decl.typ { + c.error('recursive interface fields are not allowed because they cannot be initialised', + field.type_pos) + } + for j in 0 .. i { + if field.name == decl.fields[j].name { + c.error('field name `$field.name` duplicate', field.pos) + } + } + } + } +} + +pub fn (mut c Checker) struct_decl(mut decl ast.StructDecl) { + if decl.language == .v && !c.is_builtin_mod { + c.check_valid_pascal_case(decl.name, 'struct name', decl.pos) + } + mut struct_sym := c.table.find_type(decl.name) or { ast.TypeSymbol{} } + mut has_generic_types := false + if mut struct_sym.info is ast.Struct { + for embed in decl.embeds { + if embed.typ.has_flag(.generic) { + has_generic_types = true + } + embed_sym := c.table.get_type_symbol(embed.typ) + if embed_sym.kind != .struct_ { + c.error('`$embed_sym.name` is not a struct', embed.pos) + } else { + info := embed_sym.info as ast.Struct + if info.is_heap && !embed.typ.is_ptr() { + struct_sym.info.is_heap = true + } + } + } + for attr in decl.attrs { + if attr.name == 'typedef' && decl.language != .c { + c.error('`typedef` attribute can only be used with C structs', decl.pos) + } + } + for i, field in decl.fields { + c.ensure_type_exists(field.typ, field.type_pos) or { return } + if field.typ.has_flag(.generic) { + has_generic_types = true + } + if decl.language == .v { + c.check_valid_snake_case(field.name, 'field name', field.pos) + } + sym := c.table.get_type_symbol(field.typ) + for j in 0 .. i { + if field.name == decl.fields[j].name { + c.error('field name `$field.name` duplicate', field.pos) + } + } + if sym.kind == .struct_ { + info := sym.info as ast.Struct + if info.is_heap && !field.typ.is_ptr() { + struct_sym.info.is_heap = true + } + } + if field.has_default_expr { + c.expected_type = field.typ + mut field_expr_type := c.expr(field.default_expr) + if !field.typ.has_flag(.optional) { + c.check_expr_opt_call(field.default_expr, field_expr_type) + } + struct_sym.info.fields[i].default_expr_typ = field_expr_type + c.check_expected(field_expr_type, field.typ) or { + if sym.kind == .interface_ + && c.type_implements(field_expr_type, field.typ, field.pos) { + if !field_expr_type.is_ptr() && !field_expr_type.is_pointer() + && !c.inside_unsafe { + field_expr_type_sym := c.table.get_type_symbol(field_expr_type) + if field_expr_type_sym.kind != .interface_ { + c.mark_as_referenced(mut &decl.fields[i].default_expr, + true) + } + } + } else { + c.error('incompatible initializer for field `$field.name`: $err.msg', + field.default_expr.position()) + } + } + // Check for unnecessary inits like ` = 0` and ` = ''` + if field.typ.is_ptr() { + continue + } + if field.default_expr is ast.IntegerLiteral { + if field.default_expr.val == '0' { + c.warn('unnecessary default value of `0`: struct fields are zeroed by default', + field.default_expr.pos) + } + } else if field.default_expr is ast.StringLiteral { + if field.default_expr.val == '' { + c.warn("unnecessary default value of '': struct fields are zeroed by default", + field.default_expr.pos) + } + } else if field.default_expr is ast.BoolLiteral { + if field.default_expr.val == false { + c.warn('unnecessary default value `false`: struct fields are zeroed by default', + field.default_expr.pos) + } + } + } + } + if decl.generic_types.len == 0 && has_generic_types { + c.error('generic struct declaration must specify the generic type names, e.g. Foo', + decl.pos) + } + } +} + +fn (mut c Checker) unwrap_generic_type(typ ast.Type, generic_names []string, concrete_types []ast.Type) ast.Type { + mut final_concrete_types := []ast.Type{} + mut fields := []ast.StructField{} + mut nrt := '' + mut c_nrt := '' + ts := c.table.get_type_symbol(typ) + match mut ts.info { + ast.Struct, ast.Interface, ast.SumType { + if !ts.info.is_generic { + return typ + } + nrt = '$ts.name<' + c_nrt = '${ts.cname}_T_' + for i in 0 .. ts.info.generic_types.len { + if ct := c.table.resolve_generic_to_concrete(ts.info.generic_types[i], + generic_names, concrete_types) + { + gts := c.table.get_type_symbol(ct) + nrt += gts.name + c_nrt += gts.cname + if i != ts.info.generic_types.len - 1 { + nrt += ',' + c_nrt += '_' + } + } + } + nrt += '>' + idx := c.table.type_idxs[nrt] + if idx != 0 && c.table.type_symbols[idx].kind != .placeholder { + return ast.new_type(idx).derive(typ).clear_flag(.generic) + } else { + // fields type translate to concrete type + fields = ts.info.fields.clone() + for i in 0 .. fields.len { + if fields[i].typ.has_flag(.generic) { + sym := c.table.get_type_symbol(fields[i].typ) + if sym.kind == .struct_ && fields[i].typ.idx() != typ.idx() { + fields[i].typ = c.unwrap_generic_type(fields[i].typ, generic_names, + concrete_types) + } else { + if t_typ := c.table.resolve_generic_to_concrete(fields[i].typ, + generic_names, concrete_types) + { + fields[i].typ = t_typ + } + } + } + } + // update concrete types + for i in 0 .. ts.info.generic_types.len { + if t_typ := c.table.resolve_generic_to_concrete(ts.info.generic_types[i], + generic_names, concrete_types) + { + final_concrete_types << t_typ + } + } + } + } + else {} + } + match mut ts.info { + ast.Struct { + mut info := ts.info + info.is_generic = false + info.concrete_types = final_concrete_types + info.parent_type = typ + info.fields = fields + new_idx := c.table.register_type_symbol( + kind: .struct_ + name: nrt + cname: util.no_dots(c_nrt) + mod: c.mod + info: info + ) + return ast.new_type(new_idx).derive(typ).clear_flag(.generic) + } + ast.Interface { + // resolve generic types inside methods + mut imethods := ts.info.methods.clone() + for mut method in imethods { + if t := c.table.resolve_generic_to_concrete(method.return_type, generic_names, + concrete_types) + { + method.return_type = t + } + for mut param in method.params { + if t := c.table.resolve_generic_to_concrete(param.typ, generic_names, + concrete_types) + { + param.typ = t + } + } + } + mut all_methods := ts.methods + for imethod in imethods { + for mut method in all_methods { + if imethod.name == method.name { + method = imethod + } + } + } + mut info := ts.info + info.is_generic = false + info.concrete_types = final_concrete_types + info.parent_type = typ + info.fields = fields + info.methods = imethods + new_idx := c.table.register_type_symbol( + kind: .interface_ + name: nrt + cname: util.no_dots(c_nrt) + mod: c.mod + info: info + ) + mut ts_copy := c.table.get_type_symbol(new_idx) + for method in all_methods { + ts_copy.register_method(method) + } + return ast.new_type(new_idx).derive(typ).clear_flag(.generic) + } + else {} + } + return typ +} + +// generic struct instantiations to concrete types +pub fn (mut c Checker) generic_insts_to_concrete() { + for mut typ in c.table.type_symbols { + if typ.kind == .generic_struct_inst { + info := typ.info as ast.GenericStructInst + parent := c.table.type_symbols[info.parent_idx] + if parent.kind == .placeholder { + typ.kind = .placeholder + continue + } + match parent.info { + ast.Struct { + mut parent_info := parent.info as ast.Struct + mut fields := parent_info.fields.clone() + if parent_info.generic_types.len == info.concrete_types.len { + generic_names := parent_info.generic_types.map(c.table.get_type_symbol(it).name) + for i in 0 .. fields.len { + if fields[i].typ.has_flag(.generic) { + sym := c.table.get_type_symbol(fields[i].typ) + if sym.kind == .struct_ && fields[i].typ.idx() != info.parent_idx { + fields[i].typ = c.unwrap_generic_type(fields[i].typ, + generic_names, info.concrete_types) + } else { + if t_typ := c.table.resolve_generic_to_concrete(fields[i].typ, + generic_names, info.concrete_types) + { + fields[i].typ = t_typ + } + } + } + } + parent_info.is_generic = false + parent_info.concrete_types = info.concrete_types.clone() + parent_info.fields = fields + parent_info.parent_type = ast.new_type(info.parent_idx).set_flag(.generic) + typ.info = ast.Struct{ + ...parent_info + is_generic: false + concrete_types: info.concrete_types.clone() + fields: fields + parent_type: ast.new_type(info.parent_idx).set_flag(.generic) + } + typ.is_public = true + typ.kind = parent.kind + } + } + ast.Interface { + mut parent_info := parent.info as ast.Interface + if parent_info.generic_types.len == info.concrete_types.len { + mut fields := parent_info.fields.clone() + generic_names := parent_info.generic_types.map(c.table.get_type_symbol(it).name) + for i in 0 .. fields.len { + if t_typ := c.table.resolve_generic_to_concrete(fields[i].typ, + generic_names, info.concrete_types) + { + fields[i].typ = t_typ + } + } + mut imethods := parent_info.methods.clone() + for mut method in imethods { + method.generic_names.clear() + if pt := c.table.resolve_generic_to_concrete(method.return_type, + generic_names, info.concrete_types) + { + method.return_type = pt + } + method.params = method.params.clone() + for mut param in method.params { + if pt := c.table.resolve_generic_to_concrete(param.typ, + generic_names, info.concrete_types) + { + param.typ = pt + } + } + typ.register_method(method) + } + mut all_methods := parent.methods + for imethod in imethods { + for mut method in all_methods { + if imethod.name == method.name { + method = imethod + } + } + } + typ.info = ast.Interface{ + ...parent_info + is_generic: false + concrete_types: info.concrete_types.clone() + fields: fields + methods: imethods + parent_type: ast.new_type(info.parent_idx).set_flag(.generic) + } + typ.is_public = true + typ.kind = parent.kind + typ.methods = all_methods + } + } + ast.SumType { + mut parent_info := parent.info as ast.SumType + if parent_info.generic_types.len == info.concrete_types.len { + mut fields := parent_info.fields.clone() + mut variants := parent_info.variants.clone() + generic_names := parent_info.generic_types.map(c.table.get_type_symbol(it).name) + for i in 0 .. fields.len { + if t_typ := c.table.resolve_generic_to_concrete(fields[i].typ, + generic_names, info.concrete_types) + { + fields[i].typ = t_typ + } + } + for i in 0 .. variants.len { + if t_typ := c.table.resolve_generic_to_concrete(variants[i], + generic_names, info.concrete_types) + { + variants[i] = t_typ + } + } + typ.info = ast.SumType{ + ...parent_info + is_generic: false + concrete_types: info.concrete_types.clone() + fields: fields + variants: variants + parent_type: ast.new_type(info.parent_idx).set_flag(.generic) + } + typ.is_public = true + typ.kind = parent.kind + } + } + else {} + } + } + } +} + +pub fn (mut c Checker) struct_init(mut node ast.StructInit) ast.Type { + if node.typ == ast.void_type { + // Short syntax `({foo: bar})` + if c.expected_type == ast.void_type { + c.error('unexpected short struct syntax', node.pos) + return ast.void_type + } + sym := c.table.get_type_symbol(c.expected_type) + if sym.kind == .array { + node.typ = c.table.value_type(c.expected_type) + } else { + node.typ = c.expected_type + } + } + struct_sym := c.table.get_type_symbol(node.typ) + if struct_sym.info is ast.Struct { + if struct_sym.info.generic_types.len > 0 && struct_sym.info.concrete_types.len == 0 + && c.table.cur_concrete_types.len == 0 { + c.error('generic struct init must specify type parameter, e.g. Foo', + node.pos) + } + } else if struct_sym.info is ast.Alias { + parent_sym := c.table.get_type_symbol(struct_sym.info.parent_type) + // e.g. ´x := MyMapAlias{}´, should be a cast to alias type ´x := MyMapAlias(map[...]...)´ + if parent_sym.kind == .map { + alias_str := c.table.type_to_str(node.typ) + map_str := c.table.type_to_str(struct_sym.info.parent_type) + c.error('direct map alias init is not possible, use `${alias_str}($map_str{})` instead', + node.pos) + return ast.void_type + } + } + unwrapped_struct_type := c.unwrap_generic_type(node.typ, c.table.cur_fn.generic_names, + c.table.cur_concrete_types) + c.ensure_type_exists(unwrapped_struct_type, node.pos) or {} + type_sym := c.table.get_type_symbol(unwrapped_struct_type) + if !c.inside_unsafe && type_sym.kind == .sum_type { + c.note('direct sum type init (`x := SumType{}`) will be removed soon', node.pos) + } + // Make sure the first letter is capital, do not allow e.g. `x := string{}`, + // but `x := T{}` is ok. + if !c.is_builtin_mod && !c.inside_unsafe && type_sym.language == .v + && c.table.cur_concrete_types.len == 0 { + pos := type_sym.name.last_index('.') or { -1 } + first_letter := type_sym.name[pos + 1] + if !first_letter.is_capital() { + c.error('cannot initialize builtin type `$type_sym.name`', node.pos) + } + } + if type_sym.kind == .sum_type && node.fields.len == 1 { + sexpr := node.fields[0].expr.str() + c.error('cast to sum type using `${type_sym.name}($sexpr)` not `$type_sym.name{$sexpr}`', + node.pos) + } + if type_sym.kind == .interface_ { + c.error('cannot instantiate interface `$type_sym.name`', node.pos) + } + if type_sym.info is ast.Alias { + if type_sym.info.parent_type.is_number() { + c.error('cannot instantiate number type alias `$type_sym.name`', node.pos) + return ast.void_type + } + } + // allow init structs from generic if they're private except the type is from builtin module + if !type_sym.is_public && type_sym.kind != .placeholder && type_sym.language != .c + && (type_sym.mod != c.mod && !(node.typ.has_flag(.generic) && type_sym.mod != 'builtin')) { + c.error('type `$type_sym.name` is private', node.pos) + } + if type_sym.kind == .struct_ { + info := type_sym.info as ast.Struct + if info.attrs.len > 0 && info.attrs[0].name == 'noinit' && type_sym.mod != c.mod { + c.error('struct `$type_sym.name` is declared with a `[noinit]` attribute, so ' + + 'it cannot be initialized with `$type_sym.name{}`', node.pos) + } + } + if type_sym.name.len == 1 && c.table.cur_fn.generic_names.len == 0 { + c.error('unknown struct `$type_sym.name`', node.pos) + return 0 + } + match type_sym.kind { + .placeholder { + c.error('unknown struct: $type_sym.name', node.pos) + return ast.void_type + } + // string & array are also structs but .kind of string/array + .struct_, .string, .array, .alias { + mut info := ast.Struct{} + if type_sym.kind == .alias { + info_t := type_sym.info as ast.Alias + sym := c.table.get_type_symbol(info_t.parent_type) + if sym.kind == .placeholder { // pending import symbol did not resolve + c.error('unknown struct: $type_sym.name', node.pos) + return ast.void_type + } + if sym.kind == .struct_ { + info = sym.info as ast.Struct + } else { + c.error('alias type name: $sym.name is not struct type', node.pos) + } + } else { + info = type_sym.info as ast.Struct + } + if node.is_short { + exp_len := info.fields.len + got_len := node.fields.len + if exp_len != got_len { + amount := if exp_len < got_len { 'many' } else { 'few' } + c.error('too $amount fields in `$type_sym.name` literal (expecting $exp_len, got $got_len)', + node.pos) + } + } + mut inited_fields := []string{} + for i, mut field in node.fields { + mut field_info := ast.StructField{} + mut embed_type := ast.Type(0) + mut is_embed := false + mut field_name := '' + if node.is_short { + if i >= info.fields.len { + // It doesn't make sense to check for fields that don't exist. + // We should just stop here. + break + } + field_info = info.fields[i] + field_name = field_info.name + node.fields[i].name = field_name + } else { + field_name = field.name + mut exists := true + field_info = info.find_field(field_name) or { + exists = false + ast.StructField{} + } + if !exists { + for embed in info.embeds { + embed_sym := c.table.get_type_symbol(embed) + if embed_sym.embed_name() == field_name { + exists = true + embed_type = embed + is_embed = true + break + } + embed_struct_info := embed_sym.info as ast.Struct + if embed_field_info := embed_struct_info.find_field(field_name) { + exists = true + field_info = embed_field_info + break + } + } + } + if !exists { + c.error('unknown field `$field.name` in struct literal of type `$type_sym.name`', + field.pos) + continue + } + if field_name in inited_fields { + c.error('duplicate field name in struct literal: `$field_name`', + field.pos) + continue + } + } + mut expr_type := ast.Type(0) + mut expected_type := ast.Type(0) + if is_embed { + expected_type = embed_type + c.expected_type = expected_type + expr_type = c.unwrap_generic(c.expr(field.expr)) + expr_type_sym := c.table.get_type_symbol(expr_type) + if expr_type != ast.void_type && expr_type_sym.kind != .placeholder { + c.check_expected(expr_type, embed_type) or { + c.error('cannot assign to field `$field_info.name`: $err.msg', + field.pos) + } + } + node.fields[i].typ = expr_type + node.fields[i].expected_type = embed_type + } else { + inited_fields << field_name + field_type_sym := c.table.get_type_symbol(field_info.typ) + expected_type = field_info.typ + c.expected_type = expected_type + expr_type = c.unwrap_generic(c.expr(field.expr)) + if !field_info.typ.has_flag(.optional) { + expr_type = c.check_expr_opt_call(field.expr, expr_type) + } + expr_type_sym := c.table.get_type_symbol(expr_type) + if field_type_sym.kind == .interface_ { + if c.type_implements(expr_type, field_info.typ, field.pos) { + if !expr_type.is_ptr() && !expr_type.is_pointer() + && expr_type_sym.kind != .interface_ && !c.inside_unsafe { + c.mark_as_referenced(mut &field.expr, true) + } + } + } else if expr_type != ast.void_type && expr_type_sym.kind != .placeholder { + c.check_expected(expr_type, field_info.typ) or { + c.error('cannot assign to field `$field_info.name`: $err.msg', + field.pos) + } + } + if field_info.typ.has_flag(.shared_f) { + if !expr_type.has_flag(.shared_f) && expr_type.is_ptr() { + c.error('`shared` field must be initialized with `shared` or value', + field.pos) + } + } else { + if field_info.typ.is_ptr() && !expr_type.is_ptr() && !expr_type.is_pointer() + && !expr_type.is_number() { + c.error('reference field must be initialized with reference', + field.pos) + } + } + node.fields[i].typ = expr_type + node.fields[i].expected_type = field_info.typ + } + if expr_type.is_ptr() && expected_type.is_ptr() { + if mut field.expr is ast.Ident { + if mut field.expr.obj is ast.Var { + mut obj := unsafe { &field.expr.obj } + if c.fn_scope != voidptr(0) { + obj = c.fn_scope.find_var(obj.name) or { obj } + } + if obj.is_stack_obj && !c.inside_unsafe { + sym := c.table.get_type_symbol(obj.typ.set_nr_muls(0)) + if !sym.is_heap() && !c.pref.translated { + suggestion := if sym.kind == .struct_ { + 'declaring `$sym.name` as `[heap]`' + } else { + 'wrapping the `$sym.name` object in a `struct` declared as `[heap]`' + } + c.error('`$field.expr.name` cannot be assigned outside `unsafe` blocks as it might refer to an object stored on stack. Consider ${suggestion}.', + field.expr.pos) + } + } + } + } + } + } + // Check uninitialized refs/sum types + for field in info.fields { + if field.has_default_expr || field.name in inited_fields { + continue + } + if field.typ.is_ptr() && !field.typ.has_flag(.shared_f) && !node.has_update_expr + && !c.pref.translated { + c.error('reference field `${type_sym.name}.$field.name` must be initialized', + node.pos) + } + // Do not allow empty uninitialized interfaces + sym := c.table.get_type_symbol(field.typ) + if sym.kind == .interface_ { + // TODO error + c.warn('interface field `${type_sym.name}.$field.name` must be initialized', + node.pos) + } + // Do not allow empty uninitialized sum types + /* + sym := c.table.get_type_symbol(field.typ) + if sym.kind == .sum_type { + c.warn('sum type field `${type_sym.name}.$field.name` must be initialized', + node.pos) + } + */ + // Check for `[required]` struct attr + if field.attrs.contains('required') && !node.is_short && !node.has_update_expr { + mut found := false + for init_field in node.fields { + if field.name == init_field.name { + found = true + break + } + } + if !found { + c.error('field `${type_sym.name}.$field.name` must be initialized', + node.pos) + } + } + } + } + else {} + } + if node.has_update_expr { + update_type := c.expr(node.update_expr) + node.update_expr_type = update_type + if c.table.type_kind(update_type) != .struct_ { + s := c.table.type_to_str(update_type) + c.error('expected struct, found `$s`', node.update_expr.position()) + } else if update_type != node.typ { + from_sym := c.table.get_type_symbol(update_type) + to_sym := c.table.get_type_symbol(node.typ) + from_info := from_sym.info as ast.Struct + to_info := to_sym.info as ast.Struct + // TODO this check is too strict + if !c.check_struct_signature(from_info, to_info) { + c.error('struct `$from_sym.name` is not compatible with struct `$to_sym.name`', + node.update_expr.position()) + } + } + if !node.update_expr.is_lvalue() { + // cgen will repeat `update_expr` for each field + // so enforce an lvalue for efficiency + c.error('expression is not an lvalue', node.update_expr.position()) + } + } + return unwrapped_struct_type +} + +fn (mut c Checker) check_div_mod_by_zero(expr ast.Expr, op_kind token.Kind) { + match mut expr { + ast.FloatLiteral { + if expr.val.f64() == 0.0 { + oper := if op_kind == .div { 'division' } else { 'modulo' } + c.error('$oper by zero', expr.pos) + } + } + ast.IntegerLiteral { + if expr.val.int() == 0 { + oper := if op_kind == .div { 'division' } else { 'modulo' } + c.error('$oper by zero', expr.pos) + } + } + ast.CastExpr { + c.check_div_mod_by_zero(expr.expr, op_kind) + } + else {} + } +} + +pub fn (mut c Checker) infix_expr(mut node ast.InfixExpr) ast.Type { + former_expected_type := c.expected_type + defer { + c.expected_type = former_expected_type + } + left_type := c.expr(node.left) + node.left_type = left_type + c.expected_type = left_type + right_type := c.expr(node.right) + node.right_type = right_type + if left_type.is_number() && !left_type.is_ptr() + && right_type in [ast.int_literal_type, ast.float_literal_type] { + node.right_type = left_type + } + if right_type.is_number() && !right_type.is_ptr() + && left_type in [ast.int_literal_type, ast.float_literal_type] { + node.left_type = right_type + } + mut right_sym := c.table.get_type_symbol(right_type) + right_final := c.table.get_final_type_symbol(right_type) + mut left_sym := c.table.get_type_symbol(left_type) + left_final := c.table.get_final_type_symbol(left_type) + left_pos := node.left.position() + right_pos := node.right.position() + left_right_pos := left_pos.extend(right_pos) + if left_type.is_any_kind_of_pointer() + && node.op in [.plus, .minus, .mul, .div, .mod, .xor, .amp, .pipe] { + if (right_type.is_any_kind_of_pointer() && node.op != .minus) + || (!right_type.is_any_kind_of_pointer() && node.op !in [.plus, .minus]) { + left_name := c.table.type_to_str(left_type) + right_name := c.table.type_to_str(right_type) + c.error('invalid operator `$node.op` to `$left_name` and `$right_name`', left_right_pos) + } else if node.op in [.plus, .minus] { + if !c.inside_unsafe && !node.left.is_auto_deref_var() && !node.right.is_auto_deref_var() { + c.warn('pointer arithmetic is only allowed in `unsafe` blocks', left_right_pos) + } + if left_type == ast.voidptr_type { + c.error('`$node.op` cannot be used with `voidptr`', left_pos) + } + } + } + mut return_type := left_type + if node.op != .key_is { + match mut node.left { + ast.Ident, ast.SelectorExpr { + if node.left.is_mut { + c.error('remove unnecessary `mut`', node.left.mut_pos) + } + } + else {} + } + } + eq_ne := node.op in [.eq, .ne] + // Single side check + // Place these branches according to ops' usage frequency to accelerate. + // TODO: First branch includes ops where single side check is not needed, or needed but hasn't been implemented. + // TODO: Some of the checks are not single side. Should find a better way to organize them. + match node.op { + // .eq, .ne, .gt, .lt, .ge, .le, .and, .logical_or, .dot, .key_as, .right_shift {} + .eq, .ne { + is_mismatch := + (left_sym.kind == .alias && right_sym.kind in [.struct_, .array, .sum_type]) + || (right_sym.kind == .alias && left_sym.kind in [.struct_, .array, .sum_type]) + if is_mismatch { + c.error('possible type mismatch of compared values of `$node.op` operation', + left_right_pos) + } + } + .key_in, .not_in { + match right_final.kind { + .array { + elem_type := right_final.array_info().elem_type + // if left_default.kind != right_sym.kind { + c.check_expected(left_type, elem_type) or { + c.error('left operand to `$node.op` does not match the array element type: $err.msg', + left_right_pos) + } + } + .map { + map_info := right_final.map_info() + c.check_expected(left_type, map_info.key_type) or { + c.error('left operand to `$node.op` does not match the map key type: $err.msg', + left_right_pos) + } + node.left_type = map_info.key_type + } + .string { + c.warn('use `str.contains(substr)` instead of `substr in str`', left_right_pos) + c.check_expected(left_type, right_type) or { + c.error('left operand to `$node.op` does not match: $err.msg', + left_right_pos) + } + } + else { + c.error('`$node.op.str()` can only be used with an array/map/string', + node.pos) + } + } + return ast.bool_type + } + .plus, .minus, .mul, .div, .mod, .xor, .amp, .pipe { // binary operators that expect matching types + if right_sym.info is ast.Alias && (right_sym.info as ast.Alias).language != .c + && c.mod == c.table.type_to_str(right_type).split('.')[0] + && c.table.get_type_symbol((right_sym.info as ast.Alias).parent_type).is_primitive() { + right_sym = c.table.get_type_symbol((right_sym.info as ast.Alias).parent_type) + } + if left_sym.info is ast.Alias && (left_sym.info as ast.Alias).language != .c + && c.mod == c.table.type_to_str(left_type).split('.')[0] + && c.table.get_type_symbol((left_sym.info as ast.Alias).parent_type).is_primitive() { + left_sym = c.table.get_type_symbol((left_sym.info as ast.Alias).parent_type) + } + // Check if the alias type is not a primitive then allow using operator overloading for aliased `arrays` and `maps` + if left_sym.kind == .alias && left_sym.info is ast.Alias + && !(c.table.get_type_symbol((left_sym.info as ast.Alias).parent_type).is_primitive()) { + if left_sym.has_method(node.op.str()) { + if method := left_sym.find_method(node.op.str()) { + return_type = method.return_type + } else { + return_type = left_type + } + } else { + left_name := c.table.type_to_str(left_type) + right_name := c.table.type_to_str(right_type) + if left_name == right_name { + c.error('undefined operation `$left_name` $node.op.str() `$right_name`', + left_right_pos) + } else { + c.error('mismatched types `$left_name` and `$right_name`', left_right_pos) + } + } + } else if right_sym.kind == .alias && right_sym.info is ast.Alias + && !(c.table.get_type_symbol((right_sym.info as ast.Alias).parent_type).is_primitive()) { + if right_sym.has_method(node.op.str()) { + if method := right_sym.find_method(node.op.str()) { + return_type = method.return_type + } else { + return_type = right_type + } + } else { + left_name := c.table.type_to_str(left_type) + right_name := c.table.type_to_str(right_type) + if left_name == right_name { + c.error('undefined operation `$left_name` $node.op.str() `$right_name`', + left_right_pos) + } else { + c.error('mismatched types `$left_name` and `$right_name`', left_right_pos) + } + } + } + if left_sym.kind in [.array, .array_fixed, .map, .struct_] { + if left_sym.has_method(node.op.str()) { + if method := left_sym.find_method(node.op.str()) { + return_type = method.return_type + } else { + return_type = left_type + } + } else { + left_name := c.table.type_to_str(left_type) + right_name := c.table.type_to_str(right_type) + if left_name == right_name { + c.error('undefined operation `$left_name` $node.op.str() `$right_name`', + left_right_pos) + } else { + c.error('mismatched types `$left_name` and `$right_name`', left_right_pos) + } + } + } else if right_sym.kind in [.array, .array_fixed, .map, .struct_] { + if right_sym.has_method(node.op.str()) { + if method := right_sym.find_method(node.op.str()) { + return_type = method.return_type + } else { + return_type = right_type + } + } else { + left_name := c.table.type_to_str(left_type) + right_name := c.table.type_to_str(right_type) + if left_name == right_name { + c.error('undefined operation `$left_name` $node.op.str() `$right_name`', + left_right_pos) + } else { + c.error('mismatched types `$left_name` and `$right_name`', left_right_pos) + } + } + } else if node.left.is_auto_deref_var() || node.right.is_auto_deref_var() { + deref_left_type := if node.left.is_auto_deref_var() { + left_type.deref() + } else { + left_type + } + deref_right_type := if node.right.is_auto_deref_var() { + right_type.deref() + } else { + right_type + } + left_name := c.table.type_to_str(c.table.mktyp(deref_left_type)) + right_name := c.table.type_to_str(c.table.mktyp(deref_right_type)) + if left_name != right_name { + c.error('mismatched types `$left_name` and `$right_name`', left_right_pos) + } + } else { + unaliased_left_type := c.table.unalias_num_type(left_type) + unalias_right_type := c.table.unalias_num_type(right_type) + mut promoted_type := c.promote(unaliased_left_type, unalias_right_type) + // substract pointers is allowed in unsafe block + is_allowed_pointer_arithmetic := left_type.is_any_kind_of_pointer() + && right_type.is_any_kind_of_pointer() && node.op == .minus + if is_allowed_pointer_arithmetic { + promoted_type = ast.int_type + } + if promoted_type.idx() == ast.void_type_idx { + left_name := c.table.type_to_str(left_type) + right_name := c.table.type_to_str(right_type) + c.error('mismatched types `$left_name` and `$right_name`', left_right_pos) + } else if promoted_type.has_flag(.optional) { + s := c.table.type_to_str(promoted_type) + c.error('`$node.op` cannot be used with `$s`', node.pos) + } else if promoted_type.is_float() { + if node.op in [.mod, .xor, .amp, .pipe] { + side := if left_type == promoted_type { 'left' } else { 'right' } + pos := if left_type == promoted_type { left_pos } else { right_pos } + name := if left_type == promoted_type { + left_sym.name + } else { + right_sym.name + } + if node.op == .mod { + c.error('float modulo not allowed, use math.fmod() instead', + pos) + } else { + c.error('$side type of `$node.op.str()` cannot be non-integer type `$name`', + pos) + } + } + } + if node.op in [.div, .mod] { + c.check_div_mod_by_zero(node.right, node.op) + } + return_type = promoted_type + } + } + .gt, .lt, .ge, .le { + if left_sym.kind in [.array, .array_fixed] && right_sym.kind in [.array, .array_fixed] { + c.error('only `==` and `!=` are defined on arrays', node.pos) + } else if left_sym.kind == .struct_ && right_sym.kind == .struct_ + && node.op in [.eq, .lt] { + if !(left_sym.has_method(node.op.str()) && right_sym.has_method(node.op.str())) { + left_name := c.table.type_to_str(left_type) + right_name := c.table.type_to_str(right_type) + if left_name == right_name { + c.error('undefined operation `$left_name` $node.op.str() `$right_name`', + left_right_pos) + } else { + c.error('mismatched types `$left_name` and `$right_name`', left_right_pos) + } + } + } + if left_sym.kind == .struct_ && right_sym.kind == .struct_ { + if !left_sym.has_method('<') && node.op in [.ge, .le] { + c.error('cannot use `$node.op` as `<` operator method is not defined', + left_right_pos) + } else if !left_sym.has_method('<') && node.op == .gt { + c.error('cannot use `>` as `<=` operator method is not defined', left_right_pos) + } + } + } + .left_shift { + if left_final.kind == .array { + if !node.is_stmt { + c.error('array append cannot be used in an expression', node.pos) + } + // `array << elm` + c.check_expr_opt_call(node.right, right_type) + node.auto_locked, _ = c.fail_if_immutable(node.left) + left_value_type := c.table.value_type(c.unwrap_generic(left_type)) + left_value_sym := c.table.get_type_symbol(c.unwrap_generic(left_value_type)) + if left_value_sym.kind == .interface_ { + if right_final.kind != .array { + // []Animal << Cat + if c.type_implements(right_type, left_value_type, right_pos) { + if !right_type.is_ptr() && !right_type.is_pointer() && !c.inside_unsafe + && right_sym.kind != .interface_ { + c.mark_as_referenced(mut &node.right, true) + } + } + } else { + // []Animal << []Cat + c.type_implements(c.table.value_type(right_type), left_value_type, + right_pos) + } + return ast.void_type + } + // []T << T or []T << []T + unwrapped_right_type := c.unwrap_generic(right_type) + if c.check_types(unwrapped_right_type, left_value_type) + || c.check_types(unwrapped_right_type, left_type) { + return ast.void_type + } + c.error('cannot append `$right_sym.name` to `$left_sym.name`', right_pos) + return ast.void_type + } else { + return c.check_shift(left_type, right_type, left_pos, right_pos) + } + } + .right_shift { + return c.check_shift(left_type, right_type, left_pos, right_pos) + } + .key_is, .not_is { + right_expr := node.right + mut typ := match right_expr { + ast.TypeNode { + right_expr.typ + } + ast.None { + ast.none_type_idx + } + else { + c.error('invalid type `$right_expr`', right_expr.position()) + ast.Type(0) + } + } + if typ != ast.Type(0) { + typ_sym := c.table.get_type_symbol(typ) + op := node.op.str() + if typ_sym.kind == .placeholder { + c.error('$op: type `$typ_sym.name` does not exist', right_expr.position()) + } + if left_sym.kind !in [.interface_, .sum_type] { + c.error('`$op` can only be used with interfaces and sum types', node.pos) + } else if mut left_sym.info is ast.SumType { + if typ !in left_sym.info.variants { + c.error('`$left_sym.name` has no variant `$right_sym.name`', node.pos) + } + } + } + return ast.bool_type + } + .arrow { // `chan <- elem` + if left_sym.kind == .chan { + chan_info := left_sym.chan_info() + elem_type := chan_info.elem_type + if !c.check_types(right_type, elem_type) { + c.error('cannot push `$right_sym.name` on `$left_sym.name`', right_pos) + } + if chan_info.is_mut { + // TODO: The error message of the following could be more specific... + c.fail_if_immutable(node.right) + } + if elem_type.is_ptr() && !right_type.is_ptr() { + c.error('cannot push non-reference `$right_sym.name` on `$left_sym.name`', + right_pos) + } + c.stmts(node.or_block.stmts) + } else { + c.error('cannot push on non-channel `$left_sym.name`', left_pos) + } + return ast.void_type + } + .and, .logical_or { + if !c.pref.translated { + if node.left_type != ast.bool_type_idx { + c.error('left operand for `$node.op` is not a boolean', node.left.position()) + } + if node.right_type != ast.bool_type_idx { + c.error('right operand for `$node.op` is not a boolean', node.right.position()) + } + } + if mut node.left is ast.InfixExpr { + if node.left.op != node.op && node.left.op in [.logical_or, .and] { + // for example: `(a && b) || c` instead of `a && b || c` + c.error('ambiguous boolean expression. use `()` to ensure correct order of operations', + node.pos) + } + } + } + else {} + } + // TODO: Absorb this block into the above single side check block to accelerate. + if left_type == ast.bool_type && node.op !in [.eq, .ne, .logical_or, .and] { + c.error('bool types only have the following operators defined: `==`, `!=`, `||`, and `&&`', + node.pos) + } else if left_type == ast.string_type && node.op !in [.plus, .eq, .ne, .lt, .gt, .le, .ge] { + // TODO broken !in + c.error('string types only have the following operators defined: `==`, `!=`, `<`, `>`, `<=`, `>=`, and `+`', + node.pos) + } else if left_sym.kind == .enum_ && right_sym.kind == .enum_ && !eq_ne { + left_enum := left_sym.info as ast.Enum + right_enum := right_sym.info as ast.Enum + if left_enum.is_flag && right_enum.is_flag { + // `[flag]` tagged enums are a special case that allow also `|` and `&` binary operators + if node.op !in [.pipe, .amp] { + c.error('only `==`, `!=`, `|` and `&` are defined on `[flag]` tagged `enum`, use an explicit cast to `int` if needed', + node.pos) + } + } else if !c.pref.translated { + // Regular enums + c.error('only `==` and `!=` are defined on `enum`, use an explicit cast to `int` if needed', + node.pos) + } + } + // sum types can't have any infix operation except of `is`, `eq`, `ne`. + // `is` is checked before and doesn't reach this. + if c.table.type_kind(left_type) == .sum_type && !eq_ne { + c.error('cannot use operator `$node.op` with `$left_sym.name`', node.pos) + } else if c.table.type_kind(right_type) == .sum_type && !eq_ne { + c.error('cannot use operator `$node.op` with `$right_sym.name`', node.pos) + } + // TODO move this to symmetric_check? Right now it would break `return 0` for `fn()?int ` + left_is_optional := left_type.has_flag(.optional) + right_is_optional := right_type.has_flag(.optional) + if (left_is_optional && !right_is_optional) || (!left_is_optional && right_is_optional) { + c.error('unwrapped optional cannot be used in an infix expression', left_right_pos) + } + // Dual sides check (compatibility check) + if !(c.symmetric_check(left_type, right_type) && c.symmetric_check(right_type, left_type)) + && !c.pref.translated && !node.left.is_auto_deref_var() && !node.right.is_auto_deref_var() { + // for type-unresolved consts + if left_type == ast.void_type || right_type == ast.void_type { + return ast.void_type + } + if left_type.nr_muls() > 0 && right_type.is_int() { + // pointer arithmetic is fine, it is checked in other places + return return_type + } + c.error('infix expr: cannot use `$right_sym.name` (right expression) as `$left_sym.name`', + left_right_pos) + } + /* + if (node.left is ast.InfixExpr && + (node.left as ast.InfixExpr).op == .inc) || + (node.right is ast.InfixExpr && (node.right as ast.InfixExpr).op == .inc) { + c.warn('`++` and `--` are statements, not expressions', node.pos) + } + */ + return if node.op.is_relational() { ast.bool_type } else { return_type } +} + +// returns name and position of variable that needs write lock +// also sets `is_changed` to true (TODO update the name to reflect this?) +fn (mut c Checker) fail_if_immutable(expr ast.Expr) (string, token.Position) { + mut to_lock := '' // name of variable that needs lock + mut pos := token.Position{} // and its position + mut explicit_lock_needed := false + match mut expr { + ast.CastExpr { + // TODO + return '', pos + } + ast.ComptimeSelector { + return '', pos + } + ast.Ident { + if expr.obj is ast.Var { + mut v := expr.obj as ast.Var + if !v.is_mut && !c.pref.translated && !c.inside_unsafe { + c.error('`$expr.name` is immutable, declare it with `mut` to make it mutable', + expr.pos) + } + v.is_changed = true + if v.typ.share() == .shared_t { + if expr.name !in c.locked_names { + if c.locked_names.len > 0 || c.rlocked_names.len > 0 { + if expr.name in c.rlocked_names { + c.error('$expr.name has an `rlock` but needs a `lock`', + expr.pos) + } else { + c.error('$expr.name must be added to the `lock` list above', + expr.pos) + } + } + to_lock = expr.name + pos = expr.pos + } + } + } else if expr.obj is ast.ConstField && expr.name in c.const_names { + c.error('cannot modify constant `$expr.name`', expr.pos) + } + } + ast.IndexExpr { + left_sym := c.table.get_type_symbol(expr.left_type) + mut elem_type := ast.Type(0) + mut kind := '' + match left_sym.info { + ast.Array { + elem_type, kind = left_sym.info.elem_type, 'array' + } + ast.ArrayFixed { + elem_type, kind = left_sym.info.elem_type, 'fixed array' + } + ast.Map { + elem_type, kind = left_sym.info.value_type, 'map' + } + else {} + } + if elem_type.has_flag(.shared_f) { + c.error('you have to create a handle and `lock` it to modify `shared` $kind element', + expr.left.position().extend(expr.pos)) + } + to_lock, pos = c.fail_if_immutable(expr.left) + } + ast.ParExpr { + to_lock, pos = c.fail_if_immutable(expr.expr) + } + ast.PrefixExpr { + to_lock, pos = c.fail_if_immutable(expr.right) + } + ast.SelectorExpr { + if expr.expr_type == 0 { + return '', pos + } + // retrieve ast.Field + c.ensure_type_exists(expr.expr_type, expr.pos) or { return '', pos } + mut typ_sym := c.table.get_final_type_symbol(c.unwrap_generic(expr.expr_type)) + match typ_sym.kind { + .struct_ { + struct_info := typ_sym.info as ast.Struct + mut has_field := true + mut field_info := struct_info.find_field(expr.field_name) or { + has_field = false + ast.StructField{} + } + if !has_field { + for embed in struct_info.embeds { + embed_sym := c.table.get_type_symbol(embed) + embed_struct_info := embed_sym.info as ast.Struct + if embed_field_info := embed_struct_info.find_field(expr.field_name) { + has_field = true + field_info = embed_field_info + break + } + } + } + if !has_field { + type_str := c.table.type_to_str(expr.expr_type) + c.error('unknown field `${type_str}.$expr.field_name`', expr.pos) + return '', pos + } + if field_info.typ.has_flag(.shared_f) { + expr_name := '${expr.expr}.$expr.field_name' + if expr_name !in c.locked_names { + if c.locked_names.len > 0 || c.rlocked_names.len > 0 { + if expr_name in c.rlocked_names { + c.error('$expr_name has an `rlock` but needs a `lock`', + expr.pos) + } else { + c.error('$expr_name must be added to the `lock` list above', + expr.pos) + } + } + to_lock = expr_name + pos = expr.pos + } + } else { + if !field_info.is_mut && !c.pref.translated { + type_str := c.table.type_to_str(expr.expr_type) + c.error('field `$expr.field_name` of struct `$type_str` is immutable', + expr.pos) + } + to_lock, pos = c.fail_if_immutable(expr.expr) + } + if to_lock != '' { + // No automatic lock for struct access + explicit_lock_needed = true + } + } + .interface_ { + interface_info := typ_sym.info as ast.Interface + mut field_info := interface_info.find_field(expr.field_name) or { + type_str := c.table.type_to_str(expr.expr_type) + c.error('unknown field `${type_str}.$expr.field_name`', expr.pos) + return '', pos + } + if !field_info.is_mut { + type_str := c.table.type_to_str(expr.expr_type) + c.error('field `$expr.field_name` of interface `$type_str` is immutable', + expr.pos) + } + c.fail_if_immutable(expr.expr) + } + .array, .string { + // This should only happen in `builtin` + if c.file.mod.name != 'builtin' { + c.error('`$typ_sym.kind` can not be modified', expr.pos) + } + } + .aggregate, .placeholder { + c.fail_if_immutable(expr.expr) + } + else { + c.error('unexpected symbol `$typ_sym.kind`', expr.pos) + } + } + } + ast.CallExpr { + // TODO: should only work for builtin method + if expr.name == 'slice' { + to_lock, pos = c.fail_if_immutable(expr.left) + if to_lock != '' { + // No automatic lock for array slicing (yet(?)) + explicit_lock_needed = true + } + } + } + ast.ArrayInit { + c.error('array literal can not be modified', expr.pos) + return '', pos + } + ast.StructInit { + return '', pos + } + ast.InfixExpr { + return '', pos + } + else { + if !expr.is_lit() { + c.error('unexpected expression `$expr.type_name()`', expr.position()) + } + } + } + if explicit_lock_needed { + c.error('`$to_lock` is `shared` and needs explicit lock for `$expr.type_name()`', + pos) + to_lock = '' + } + return to_lock, pos +} + +pub fn (mut c Checker) call_expr(mut call_expr ast.CallExpr) ast.Type { + // First check everything that applies to both fns and methods + // TODO merge logic from method_call and fn_call + /* + for i, call_arg in call_expr.args { + if call_arg.is_mut { + c.fail_if_immutable(call_arg.expr) + if !arg.is_mut { + tok := call_arg.share.str() + c.error('`$call_expr.name` parameter `$arg.name` is not `$tok`, `$tok` is not needed`', + call_arg.expr.position()) + } else if arg.typ.share() != call_arg.share { + c.error('wrong shared type', call_arg.expr.position()) + } + } else { + if arg.is_mut && (!call_arg.is_mut || arg.typ.share() != call_arg.share) { + tok := call_arg.share.str() + c.error('`$call_expr.name` parameter `$arg.name` is `$tok`, you need to provide `$tok` e.g. `$tok arg${i+1}`', + call_arg.expr.position()) + } + } + } + */ + // Now call `method_call` or `fn_call` for specific checks. + old_inside_fn_arg := c.inside_fn_arg + c.inside_fn_arg = true + typ := if call_expr.is_method { c.method_call(mut call_expr) } else { c.fn_call(mut call_expr) } + c.inside_fn_arg = old_inside_fn_arg + // autofree: mark args that have to be freed (after saving them in tmp exprs) + free_tmp_arg_vars := c.pref.autofree && !c.is_builtin_mod && call_expr.args.len > 0 + && !call_expr.args[0].typ.has_flag(.optional) + if free_tmp_arg_vars && !c.inside_const { + for i, arg in call_expr.args { + if arg.typ != ast.string_type { + continue + } + if arg.expr is ast.Ident || arg.expr is ast.StringLiteral + || arg.expr is ast.SelectorExpr { + // Simple expressions like variables, string literals, selector expressions + // (`x.field`) can't result in allocations and don't need to be assigned to + // temporary vars. + // Only expressions like `str + 'b'` need to be freed. + continue + } + call_expr.args[i].is_tmp_autofree = true + } + // TODO copy pasta from above + if call_expr.receiver_type == ast.string_type && !(call_expr.left is ast.Ident + || call_expr.left is ast.StringLiteral + || call_expr.left is ast.SelectorExpr) { + call_expr.free_receiver = true + } + } + c.expected_or_type = call_expr.return_type.clear_flag(.optional) + c.stmts(call_expr.or_block.stmts) + c.expected_or_type = ast.void_type + if call_expr.or_block.kind == .propagate && !c.table.cur_fn.return_type.has_flag(.optional) + && !c.inside_const { + if !c.table.cur_fn.is_main { + c.error('to propagate the optional call, `$c.table.cur_fn.name` must return an optional', + call_expr.or_block.pos) + } + } + return typ +} + +fn (mut c Checker) check_map_and_filter(is_map bool, elem_typ ast.Type, call_expr ast.CallExpr) { + if call_expr.args.len != 1 { + c.error('expected 1 argument, but got $call_expr.args.len', call_expr.pos) + // Finish early so that it doesn't fail later + return + } + elem_sym := c.table.get_type_symbol(elem_typ) + arg_expr := call_expr.args[0].expr + match arg_expr { + ast.AnonFn { + if arg_expr.decl.params.len > 1 { + c.error('function needs exactly 1 argument', arg_expr.decl.pos) + } else if is_map && (arg_expr.decl.return_type == ast.void_type + || arg_expr.decl.params[0].typ != elem_typ) { + c.error('type mismatch, should use `fn(a $elem_sym.name) T {...}`', arg_expr.decl.pos) + } else if !is_map && (arg_expr.decl.return_type != ast.bool_type + || arg_expr.decl.params[0].typ != elem_typ) { + c.error('type mismatch, should use `fn(a $elem_sym.name) bool {...}`', + arg_expr.decl.pos) + } + } + ast.Ident { + if arg_expr.kind == .function { + func := c.table.find_fn(arg_expr.name) or { + c.error('$arg_expr.name does not exist', arg_expr.pos) + return + } + if func.params.len > 1 { + c.error('function needs exactly 1 argument', call_expr.pos) + } else if is_map + && (func.return_type == ast.void_type || func.params[0].typ != elem_typ) { + c.error('type mismatch, should use `fn(a $elem_sym.name) T {...}`', + arg_expr.pos) + } else if !is_map + && (func.return_type != ast.bool_type || func.params[0].typ != elem_typ) { + c.error('type mismatch, should use `fn(a $elem_sym.name) bool {...}`', + arg_expr.pos) + } + } else if arg_expr.kind == .variable { + if arg_expr.obj is ast.Var { + expr := arg_expr.obj.expr + if expr is ast.AnonFn { + // copied from above + if expr.decl.params.len > 1 { + c.error('function needs exactly 1 argument', expr.decl.pos) + } else if is_map && (expr.decl.return_type == ast.void_type + || expr.decl.params[0].typ != elem_typ) { + c.error('type mismatch, should use `fn(a $elem_sym.name) T {...}`', + expr.decl.pos) + } else if !is_map && (expr.decl.return_type != ast.bool_type + || expr.decl.params[0].typ != elem_typ) { + c.error('type mismatch, should use `fn(a $elem_sym.name) bool {...}`', + expr.decl.pos) + } + return + } + } + // NOTE: bug accessing typ field on sumtype variant (not cast properly). + // leaving this here as the resulting issue is notoriously hard to debug. + // if !is_map && arg_expr.info.typ != ast.bool_type { + if !is_map && arg_expr.var_info().typ != ast.bool_type { + c.error('type mismatch, should be bool', arg_expr.pos) + } + } + } + ast.CallExpr { + if is_map && arg_expr.return_type in [ast.void_type, 0] { + c.error('type mismatch, `$arg_expr.name` does not return anything', arg_expr.pos) + } else if !is_map && arg_expr.return_type != ast.bool_type { + c.error('type mismatch, `$arg_expr.name` must return a bool', arg_expr.pos) + } + } + else {} + } +} + +pub fn (mut c Checker) check_expected_arg_count(mut call_expr ast.CallExpr, f &ast.Fn) ? { + nr_args := call_expr.args.len + nr_params := if call_expr.is_method && f.params.len > 0 { + f.params.len - 1 + } else { + f.params.len + } + mut min_required_params := f.params.len + if call_expr.is_method { + min_required_params-- + } + if f.is_variadic { + min_required_params-- + } + if min_required_params < 0 { + min_required_params = 0 + } + if nr_args < min_required_params { + if min_required_params == nr_args + 1 { + last_typ := f.params.last().typ + last_sym := c.table.get_type_symbol(last_typ) + if last_sym.kind == .struct_ { + // allow empty trailing struct syntax arg (`f()` where `f` is `fn(ConfigStruct)`) + call_expr.args << ast.CallArg{ + expr: ast.StructInit{ + typ: last_typ + } + typ: last_typ + } + return + } + } + c.error('expected $min_required_params arguments, but got $nr_args', call_expr.pos) + return error('') + } else if !f.is_variadic && nr_args > nr_params { + unexpected_args_pos := call_expr.args[min_required_params].pos.extend(call_expr.args.last().pos) + c.error('expected $min_required_params arguments, but got $nr_args', unexpected_args_pos) + return error('') + } +} + +pub fn (mut c Checker) method_call(mut call_expr ast.CallExpr) ast.Type { + left_type := c.expr(call_expr.left) + c.expected_type = left_type + mut is_generic := left_type.has_flag(.generic) + // x is Bar, x.foo() -> x.foo() + if is_generic && call_expr.concrete_types.len == 0 { + rec_sym := c.table.get_type_symbol(left_type) + if rec_sym.info is ast.Struct { + call_expr.concrete_types = rec_sym.info.generic_types + } + } + call_expr.left_type = left_type + // Set default values for .return_type & .receiver_type too, + // or there will be hard to diagnose 0 type panics in cgen. + call_expr.return_type = left_type + call_expr.receiver_type = left_type + left_type_sym := c.table.get_type_symbol(c.unwrap_generic(left_type)) + method_name := call_expr.name + mut unknown_method_msg := if field := c.table.find_field(left_type_sym, method_name) { + 'unknown method `$field.name` did you mean to access the field with the same name instead?' + } else { + 'unknown method or field: `${left_type_sym.name}.$method_name`' + } + if left_type.has_flag(.optional) { + c.error('optional type cannot be called directly', call_expr.left.position()) + return ast.void_type + } + if left_type_sym.kind in [.sum_type, .interface_] && method_name == 'type_name' { + return ast.string_type + } + if left_type == ast.void_type { + c.error('`void` type has no methods', call_expr.left.position()) + return ast.void_type + } + mut has_generic := false // x.foo() instead of x.foo() + mut concrete_types := []ast.Type{} + for concrete_type in call_expr.concrete_types { + if concrete_type.has_flag(.generic) { + has_generic = true + concrete_types << c.unwrap_generic(concrete_type) + } else { + concrete_types << concrete_type + } + } + if has_generic { + if c.table.register_fn_concrete_types(call_expr.name, concrete_types) { + c.need_recheck_generic_fns = true + } + } + // TODO: remove this for actual methods, use only for compiler magic + // FIXME: Argument count != 1 will break these + if left_type_sym.kind == .array && method_name in checker.array_builtin_methods { + return c.array_builtin_method_call(mut call_expr, left_type, left_type_sym) + } else if left_type_sym.kind == .map && method_name in ['clone', 'keys', 'move', 'delete'] { + return c.map_builtin_method_call(mut call_expr, left_type, left_type_sym) + } else if left_type_sym.kind == .array && method_name in ['insert', 'prepend'] { + info := left_type_sym.info as ast.Array + arg_expr := if method_name == 'insert' { + call_expr.args[1].expr + } else { + call_expr.args[0].expr + } + arg_type := c.expr(arg_expr) + arg_sym := c.table.get_type_symbol(arg_type) + if !c.check_types(arg_type, info.elem_type) && !c.check_types(left_type, arg_type) { + c.error('cannot $method_name `$arg_sym.name` to `$left_type_sym.name`', arg_expr.position()) + } + } else if c.table.get_final_type_symbol(left_type).kind == .array + && method_name in ['first', 'last', 'pop'] { + info := c.table.get_final_type_symbol(left_type).info + if info is ast.Array { + call_expr.return_type = info.elem_type + return info.elem_type + } + } else if left_type_sym.kind == .thread && method_name == 'wait' { + info := left_type_sym.info as ast.Thread + if call_expr.args.len > 0 { + c.error('wait() does not have any arguments', call_expr.args[0].pos) + } + call_expr.return_type = info.return_type + return info.return_type + } else if left_type_sym.kind == .char && left_type.nr_muls() == 0 && method_name == 'str' { + c.error('calling `.str()` on type `char` is not allowed, use its address or cast it to an integer instead', + call_expr.left.position().extend(call_expr.pos)) + return ast.void_type + } + mut method := ast.Fn{} + mut has_method := false + mut is_method_from_embed := false + if m := c.table.type_find_method(left_type_sym, method_name) { + method = m + has_method = true + } else { + if left_type_sym.info is ast.Struct { + if left_type_sym.info.parent_type != 0 { + type_sym := c.table.get_type_symbol(left_type_sym.info.parent_type) + if m := c.table.type_find_method(type_sym, method_name) { + method = m + has_method = true + is_generic = true + } + } + } + if !has_method { + has_method = true + mut embed_type := ast.Type(0) + method, embed_type = c.table.type_find_method_from_embeds(left_type_sym, method_name) or { + if err.msg != '' { + c.error(err.msg, call_expr.pos) + } + has_method = false + ast.Fn{}, ast.Type(0) + } + if embed_type != 0 { + is_method_from_embed = true + call_expr.from_embed_type = embed_type + } + } + if left_type_sym.kind == .aggregate { + // the error message contains the problematic type + unknown_method_msg = err.msg + } + } + if has_method { + call_expr.is_noreturn = method.is_noreturn + if !method.is_pub && !c.pref.is_test && method.mod != c.mod { + // If a private method is called outside of the module + // its receiver type is defined in, show an error. + // println('warn $method_name lef.mod=$left_type_sym.mod c.mod=$c.mod') + c.error('method `${left_type_sym.name}.$method_name` is private', call_expr.pos) + } + rec_share := method.params[0].typ.share() + if rec_share == .shared_t && (c.locked_names.len > 0 || c.rlocked_names.len > 0) { + c.error('method with `shared` receiver cannot be called inside `lock`/`rlock` block', + call_expr.pos) + } + if method.params[0].is_mut { + to_lock, pos := c.fail_if_immutable(call_expr.left) + if !call_expr.left.is_lvalue() { + c.error('cannot pass expression as `mut`', call_expr.left.position()) + } + // call_expr.is_mut = true + if to_lock != '' && rec_share != .shared_t { + c.error('$to_lock is `shared` and must be `lock`ed to be passed as `mut`', + pos) + } + } else { + c.fail_if_unreadable(call_expr.left, left_type, 'receiver') + } + if (!left_type_sym.is_builtin() && method.mod != 'builtin') && method.language == .v + && method.no_body { + c.error('cannot call a method that does not have a body', call_expr.pos) + } + if method.return_type == ast.void_type && method.is_conditional && method.ctdefine_idx != -1 { + call_expr.should_be_skipped = c.evaluate_once_comptime_if_attribute(mut method.attrs[method.ctdefine_idx]) + } + c.check_expected_arg_count(mut call_expr, method) or { return method.return_type } + mut exp_arg_typ := ast.Type(0) // type of 1st arg for special builtin methods + mut param_is_mut := false + mut no_type_promotion := false + if left_type_sym.kind == .chan { + elem_typ := (left_type_sym.info as ast.Chan).elem_type + if method_name == 'try_push' { + exp_arg_typ = elem_typ.to_ptr() + } else if method_name == 'try_pop' { + exp_arg_typ = elem_typ + param_is_mut = true + no_type_promotion = true + } + } + // if method_name == 'clone' { + // println('CLONE nr args=$method.args.len') + // } + // call_expr.args << method.args[0].typ + // call_expr.exp_arg_types << method.args[0].typ + for i, mut arg in call_expr.args { + if i > 0 || exp_arg_typ == ast.Type(0) { + exp_arg_typ = if method.is_variadic && i >= method.params.len - 1 { + method.params[method.params.len - 1].typ + } else { + method.params[i + 1].typ + } + param_is_mut = false + no_type_promotion = false + } + exp_arg_sym := c.table.get_type_symbol(exp_arg_typ) + c.expected_type = exp_arg_typ + got_arg_typ := c.check_expr_opt_call(arg.expr, c.expr(arg.expr)) + call_expr.args[i].typ = got_arg_typ + if no_type_promotion { + if got_arg_typ != exp_arg_typ { + c.error('cannot use `${c.table.get_type_symbol(got_arg_typ).name}` as argument for `$method.name` (`$exp_arg_sym.name` expected)', + arg.pos) + } + } + if method.is_variadic && got_arg_typ.has_flag(.variadic) && call_expr.args.len - 1 > i { + c.error('when forwarding a variadic variable, it must be the final argument', + arg.pos) + } + mut final_arg_sym := exp_arg_sym + if method.is_variadic && exp_arg_sym.info is ast.Array { + final_arg_sym = c.table.get_type_symbol(exp_arg_sym.array_info().elem_type) + } + // Handle expected interface + if final_arg_sym.kind == .interface_ { + if c.type_implements(got_arg_typ, exp_arg_typ, arg.expr.position()) { + if !got_arg_typ.is_ptr() && !got_arg_typ.is_pointer() && !c.inside_unsafe { + got_arg_typ_sym := c.table.get_type_symbol(got_arg_typ) + if got_arg_typ_sym.kind != .interface_ { + c.mark_as_referenced(mut &arg.expr, true) + } + } + } + continue + } + if exp_arg_typ.has_flag(.generic) { + continue + } + c.check_expected_call_arg(got_arg_typ, c.unwrap_generic(exp_arg_typ), call_expr.language) or { + // str method, allow type with str method if fn arg is string + // Passing an int or a string array produces a c error here + // Deleting this condition results in propper V error messages + // if arg_typ_sym.kind == .string && typ_sym.has_method('str') { + // continue + // } + if got_arg_typ != ast.void_type { + c.error('$err.msg in argument ${i + 1} to `${left_type_sym.name}.$method_name`', + arg.pos) + } + } + param := if method.is_variadic && i >= method.params.len - 1 { + method.params[method.params.len - 1] + } else { + method.params[i + 1] + } + param_is_mut = param_is_mut || param.is_mut + param_share := param.typ.share() + if param_share == .shared_t && (c.locked_names.len > 0 || c.rlocked_names.len > 0) { + c.error('method with `shared` arguments cannot be called inside `lock`/`rlock` block', + arg.pos) + } + if arg.is_mut { + to_lock, pos := c.fail_if_immutable(arg.expr) + if !param_is_mut { + tok := arg.share.str() + c.error('`$call_expr.name` parameter `$param.name` is not `$tok`, `$tok` is not needed`', + arg.expr.position()) + } else { + if param_share != arg.share { + c.error('wrong shared type', arg.expr.position()) + } + if to_lock != '' && param_share != .shared_t { + c.error('$to_lock is `shared` and must be `lock`ed to be passed as `mut`', + pos) + } + } + } else { + if param_is_mut { + tok := arg.share.str() + c.error('`$call_expr.name` parameter `$param.name` is `$tok`, you need to provide `$tok` e.g. `$tok arg${ + i + 1}`', arg.expr.position()) + } else { + c.fail_if_unreadable(arg.expr, got_arg_typ, 'argument') + } + } + } + if method.is_unsafe && !c.inside_unsafe { + c.warn('method `${left_type_sym.name}.$method_name` must be called from an `unsafe` block', + call_expr.pos) + } + if !c.table.cur_fn.is_deprecated && method.is_deprecated { + c.deprecate_fnmethod('method', '${left_type_sym.name}.$method.name', method, + call_expr) + } + // TODO: typ optimize.. this node can get processed more than once + if call_expr.expected_arg_types.len == 0 { + for i in 1 .. method.params.len { + call_expr.expected_arg_types << method.params[i].typ + } + } + if is_method_from_embed { + call_expr.receiver_type = call_expr.from_embed_type.derive(method.params[0].typ) + } else if is_generic { + // We need the receiver to be T in cgen. + // TODO: cant we just set all these to the concrete type in checker? then no need in gen + call_expr.receiver_type = left_type.derive(method.params[0].typ).set_flag(.generic) + } else { + call_expr.receiver_type = method.params[0].typ + } + if method.generic_names.len != call_expr.concrete_types.len { + // no type arguments given in call, attempt implicit instantiation + c.infer_fn_generic_types(method, mut call_expr) + concrete_types = call_expr.concrete_types + } + // resolve return generics struct to concrete type + if method.generic_names.len > 0 && method.return_type.has_flag(.generic) { + call_expr.return_type = c.unwrap_generic_type(method.return_type, method.generic_names, + concrete_types) + } else { + call_expr.return_type = method.return_type + } + if call_expr.concrete_types.len > 0 && method.return_type != 0 { + if typ := c.table.resolve_generic_to_concrete(method.return_type, method.generic_names, + concrete_types) + { + call_expr.return_type = typ + return typ + } + } + if call_expr.concrete_types.len > 0 && method.generic_names.len == 0 { + c.error('a non generic function called like a generic one', call_expr.concrete_list_pos) + } + if call_expr.concrete_types.len > method.generic_names.len { + c.error('too many generic parameters got $call_expr.concrete_types.len, expected $method.generic_names.len', + call_expr.concrete_list_pos) + } + if method.generic_names.len > 0 { + return call_expr.return_type + } + return method.return_type + } + // TODO: str methods + if method_name == 'str' { + if left_type_sym.kind == .interface_ { + iname := left_type_sym.name + c.error('interface `$iname` does not have a .str() method. Use typeof() instead', + call_expr.pos) + } + call_expr.receiver_type = left_type + call_expr.return_type = ast.string_type + if call_expr.args.len > 0 { + c.error('.str() method calls should have no arguments', call_expr.pos) + } + c.fail_if_unreadable(call_expr.left, left_type, 'receiver') + return ast.string_type + } + // call struct field fn type + // TODO: can we use SelectorExpr for all? this dosent really belong here + if field := c.table.find_field(left_type_sym, method_name) { + field_type_sym := c.table.get_type_symbol(c.unwrap_generic(field.typ)) + if field_type_sym.kind == .function { + // call_expr.is_method = false + call_expr.is_field = true + info := field_type_sym.info as ast.FnType + call_expr.return_type = info.func.return_type + mut earg_types := []ast.Type{} + for mut arg in call_expr.args { + targ := c.check_expr_opt_call(arg.expr, c.expr(arg.expr)) + arg.typ = targ + earg_types << targ + } + call_expr.expected_arg_types = earg_types + return info.func.return_type + } + } + if left_type != ast.void_type { + suggestion := util.new_suggestion(method_name, left_type_sym.methods.map(it.name)) + c.error(suggestion.say(unknown_method_msg), call_expr.pos) + } + return ast.void_type +} + +fn (mut c Checker) map_builtin_method_call(mut call_expr ast.CallExpr, left_type ast.Type, left_type_sym ast.TypeSymbol) ast.Type { + method_name := call_expr.name + mut ret_type := ast.void_type + match method_name { + 'clone', 'move' { + if method_name[0] == `m` { + c.fail_if_immutable(call_expr.left) + } + if call_expr.left.is_auto_deref_var() { + ret_type = left_type.deref() + } else { + ret_type = left_type + } + } + 'keys' { + info := left_type_sym.info as ast.Map + typ := c.table.find_or_register_array(info.key_type) + ret_type = ast.Type(typ) + } + 'delete' { + c.fail_if_immutable(call_expr.left) + if call_expr.args.len != 1 { + c.error('expected 1 argument, but got $call_expr.args.len', call_expr.pos) + } + info := left_type_sym.info as ast.Map + arg_type := c.expr(call_expr.args[0].expr) + c.check_expected_call_arg(arg_type, info.key_type, call_expr.language) or { + c.error('$err.msg in argument 1 to `Map.delete`', call_expr.args[0].pos) + } + } + else {} + } + call_expr.receiver_type = left_type.to_ptr() + call_expr.return_type = ret_type + return call_expr.return_type +} + +fn (mut c Checker) array_builtin_method_call(mut call_expr ast.CallExpr, left_type ast.Type, left_type_sym ast.TypeSymbol) ast.Type { + method_name := call_expr.name + mut elem_typ := ast.void_type + if method_name == 'slice' && !c.is_builtin_mod { + c.error('.slice() is a private method, use `x[start..end]` instead', call_expr.pos) + } + array_info := left_type_sym.info as ast.Array + elem_typ = array_info.elem_type + if method_name in ['filter', 'map', 'any', 'all'] { + // position of `it` doesn't matter + scope_register_it(mut call_expr.scope, call_expr.pos, elem_typ) + } else if method_name == 'sort' { + c.fail_if_immutable(call_expr.left) + // position of `a` and `b` doesn't matter, they're the same + scope_register_a_b(mut call_expr.scope, call_expr.pos, elem_typ) + + if call_expr.args.len > 1 { + c.error('expected 0 or 1 argument, but got $call_expr.args.len', call_expr.pos) + } else if call_expr.args.len == 1 { + if call_expr.args[0].expr is ast.InfixExpr { + if call_expr.args[0].expr.op !in [.gt, .lt] { + c.error('`.sort()` can only use `<` or `>` comparison', call_expr.pos) + } + left_name := '${call_expr.args[0].expr.left}'[0] + right_name := '${call_expr.args[0].expr.right}'[0] + if left_name !in [`a`, `b`] || right_name !in [`a`, `b`] { + c.error('`.sort()` can only use `a` or `b` as argument, e.g. `arr.sort(a < b)`', + call_expr.pos) + } else if left_name == right_name { + c.error('`.sort()` cannot use same argument', call_expr.pos) + } + } else { + c.error( + '`.sort()` requires a `<` or `>` comparison as the first and only argument' + + '\ne.g. `users.sort(a.id < b.id)`', call_expr.pos) + } + } else if !(c.table.get_type_symbol(elem_typ).has_method('<') + || c.table.unalias_num_type(elem_typ) in [ast.int_type, ast.int_type.to_ptr(), ast.string_type, ast.string_type.to_ptr(), ast.i8_type, ast.i16_type, ast.i64_type, ast.byte_type, ast.rune_type, ast.u16_type, ast.u32_type, ast.u64_type, ast.f32_type, ast.f64_type, ast.char_type, ast.bool_type, ast.float_literal_type, ast.int_literal_type, ast.size_t_type_idx]) { + c.error('custom sorting condition must be supplied for type `${c.table.type_to_str(elem_typ)}`', + call_expr.pos) + } + } else if method_name == 'wait' { + elem_sym := c.table.get_type_symbol(elem_typ) + if elem_sym.kind == .thread { + if call_expr.args.len != 0 { + c.error('`.wait()` does not have any arguments', call_expr.args[0].pos) + } + thread_ret_type := elem_sym.thread_info().return_type + if thread_ret_type.has_flag(.optional) { + c.error('`.wait()` cannot be called for an array when thread functions return optionals. Iterate over the arrays elements instead and handle each returned optional with `or`.', + call_expr.pos) + } + call_expr.return_type = c.table.find_or_register_array(thread_ret_type) + } else { + c.error('`$left_type_sym.name` has no method `wait()` (only thread handles and arrays of them have)', + call_expr.left.position()) + } + } + // map/filter are supposed to have 1 arg only + mut arg_type := left_type + for arg in call_expr.args { + arg_type = c.check_expr_opt_call(arg.expr, c.expr(arg.expr)) + } + if method_name == 'map' { + // check fn + c.check_map_and_filter(true, elem_typ, call_expr) + arg_sym := c.table.get_type_symbol(arg_type) + ret_type := match arg_sym.info { + ast.FnType { arg_sym.info.func.return_type } + else { arg_type } + } + call_expr.return_type = c.table.find_or_register_array(ret_type) + } else if method_name == 'filter' { + // check fn + c.check_map_and_filter(false, elem_typ, call_expr) + } else if method_name in ['any', 'all'] { + c.check_map_and_filter(false, elem_typ, call_expr) + call_expr.return_type = ast.bool_type + } else if method_name == 'clone' { + // need to return `array_xxx` instead of `array` + // in ['clone', 'str'] { + call_expr.receiver_type = left_type.to_ptr() + if call_expr.left.is_auto_deref_var() { + call_expr.return_type = left_type.deref() + } else { + call_expr.return_type = call_expr.receiver_type.set_nr_muls(0) + } + } else if method_name == 'sort' { + call_expr.return_type = ast.void_type + } else if method_name == 'contains' { + // c.warn('use `value in arr` instead of `arr.contains(value)`', call_expr.pos) + call_expr.return_type = ast.bool_type + } else if method_name == 'index' { + call_expr.return_type = ast.int_type + } else if method_name in ['first', 'last', 'pop'] { + call_expr.return_type = array_info.elem_type + if method_name == 'pop' { + call_expr.receiver_type = left_type.to_ptr() + } else { + call_expr.receiver_type = left_type + } + } + return call_expr.return_type +} + +pub fn (mut c Checker) fn_call(mut call_expr ast.CallExpr) ast.Type { + fn_name := call_expr.name + if fn_name == 'main' { + c.error('the `main` function cannot be called in the program', call_expr.pos) + } + mut has_generic := false // foo() instead of foo() + mut concrete_types := []ast.Type{} + for concrete_type in call_expr.concrete_types { + if concrete_type.has_flag(.generic) { + has_generic = true + concrete_types << c.unwrap_generic(concrete_type) + } else { + concrete_types << concrete_type + } + } + if !isnil(c.table.cur_fn) && c.table.cur_concrete_types.len == 0 && has_generic { + c.error('generic fn using generic types cannot be called outside of generic fn', + call_expr.pos) + } + if has_generic { + mut no_exists := true + if c.mod != '' && !fn_name.contains('.') { + // Need to prepend the module when adding a generic type to a function + no_exists = c.table.register_fn_concrete_types(c.mod + '.' + fn_name, concrete_types) + } else { + no_exists = c.table.register_fn_concrete_types(fn_name, concrete_types) + } + if no_exists { + c.need_recheck_generic_fns = true + } + } + if fn_name == 'json.encode' { + } else if fn_name == 'json.decode' && call_expr.args.len > 0 { + if call_expr.args.len != 2 { + c.error("json.decode expects 2 arguments, a type and a string (e.g `json.decode(T, '')`)", + call_expr.pos) + return ast.void_type + } + expr := call_expr.args[0].expr + if expr !is ast.TypeNode { + typ := expr.type_name() + c.error('json.decode: first argument needs to be a type, got `$typ`', call_expr.pos) + return ast.void_type + } + c.expected_type = ast.string_type + call_expr.args[1].typ = c.expr(call_expr.args[1].expr) + if call_expr.args[1].typ != ast.string_type { + c.error('json.decode: second argument needs to be a string', call_expr.pos) + } + typ := expr as ast.TypeNode + ret_type := typ.typ.set_flag(.optional) + call_expr.return_type = ret_type + return ret_type + } + // look for function in format `mod.fn` or `fn` (builtin) + mut func := ast.Fn{} + mut found := false + mut found_in_args := false + // anon fn direct call + if mut call_expr.left is ast.AnonFn { + // it was set to anon for checker errors, clear for gen + call_expr.name = '' + c.expr(call_expr.left) + if call_expr.left.typ != ast.Type(0) { + anon_fn_sym := c.table.get_type_symbol(call_expr.left.typ) + func = (anon_fn_sym.info as ast.FnType).func + found = true + } + } + // try prefix with current module as it would have never gotten prefixed + if !found && !fn_name.contains('.') && call_expr.mod != 'builtin' { + name_prefixed := '${call_expr.mod}.$fn_name' + if f := c.table.find_fn(name_prefixed) { + call_expr.name = name_prefixed + found = true + func = f + c.table.fns[name_prefixed].usages++ + } + } + if !found && call_expr.left is ast.IndexExpr { + c.expr(call_expr.left) + expr := call_expr.left as ast.IndexExpr + sym := c.table.get_type_symbol(expr.left_type) + if sym.kind == .array { + info := sym.info as ast.Array + elem_typ := c.table.get_type_symbol(info.elem_type) + if elem_typ.info is ast.FnType { + return elem_typ.info.func.return_type + } + } else if sym.kind == .map { + info := sym.info as ast.Map + value_typ := c.table.get_type_symbol(info.value_type) + if value_typ.info is ast.FnType { + return value_typ.info.func.return_type + } + } else if sym.kind == .array_fixed { + info := sym.info as ast.ArrayFixed + elem_typ := c.table.get_type_symbol(info.elem_type) + if elem_typ.info is ast.FnType { + return elem_typ.info.func.return_type + } + } + found = true + return ast.string_type + } + // already prefixed (mod.fn) or C/builtin/main + if !found { + if f := c.table.find_fn(fn_name) { + found = true + func = f + c.table.fns[fn_name].usages++ + } + } + mut is_native_builtin := false + if !found && c.pref.backend == .native { + if fn_name in native.builtins { + c.table.fns[fn_name].usages++ + found = true + func = c.table.fns[fn_name] + is_native_builtin = true + } + } + if !found && c.pref.is_vsh { + os_name := 'os.$fn_name' + if f := c.table.find_fn(os_name) { + if f.generic_names.len == call_expr.concrete_types.len { + c.table.fn_generic_types[os_name] = c.table.fn_generic_types['${call_expr.mod}.$call_expr.name'] + } + call_expr.name = os_name + found = true + func = f + c.table.fns[os_name].usages++ + } + } + if is_native_builtin { + return ast.void_type + } + // check for arg (var) of fn type + if !found { + if v := call_expr.scope.find_var(fn_name) { + if v.typ != 0 { + generic_vts := c.table.get_type_symbol(v.typ) + if generic_vts.kind == .function { + info := generic_vts.info as ast.FnType + func = info.func + found = true + found_in_args = true + } else { + vts := c.table.get_type_symbol(c.unwrap_generic(v.typ)) + if vts.kind == .function { + info := vts.info as ast.FnType + func = info.func + found = true + found_in_args = true + } + } + } + } + } + // global fn? + if !found { + if obj := c.file.global_scope.find(fn_name) { + sym := c.table.get_type_symbol(obj.typ) + if sym.kind == .function { + found = true + func = (sym.info as ast.FnType).func + } + } + } + if !found { + c.error('unknown function: $fn_name', call_expr.pos) + return ast.void_type + } + call_expr.is_noreturn = func.is_noreturn + if !found_in_args { + if _ := call_expr.scope.find_var(fn_name) { + c.error('ambiguous call to: `$fn_name`, may refer to fn `$fn_name` or variable `$fn_name`', + call_expr.pos) + } + } + if !func.is_pub && func.language == .v && func.name.len > 0 && func.mod.len > 0 + && func.mod != c.mod { + c.error('function `$func.name` is private', call_expr.pos) + } + if !isnil(c.table.cur_fn) && !c.table.cur_fn.is_deprecated && func.is_deprecated { + c.deprecate_fnmethod('function', func.name, func, call_expr) + } + if func.is_unsafe && !c.inside_unsafe + && (func.language != .c || (func.name[2] in [`m`, `s`] && func.mod == 'builtin')) { + // builtin C.m*, C.s* only - temp + c.warn('function `$func.name` must be called from an `unsafe` block', call_expr.pos) + } + call_expr.is_keep_alive = func.is_keep_alive + if func.mod != 'builtin' && func.language == .v && func.no_body && !c.pref.translated + && !func.is_unsafe { + c.error('cannot call a function that does not have a body', call_expr.pos) + } + for concrete_type in call_expr.concrete_types { + c.ensure_type_exists(concrete_type, call_expr.concrete_list_pos) or {} + } + if func.generic_names.len > 0 && call_expr.args.len == 0 && call_expr.concrete_types.len == 0 { + c.error('no argument generic function must add concrete types, e.g. foo()', + call_expr.pos) + return func.return_type + } + if func.return_type == ast.void_type && func.is_conditional && func.ctdefine_idx != -1 { + call_expr.should_be_skipped = c.evaluate_once_comptime_if_attribute(mut func.attrs[func.ctdefine_idx]) + } + // dont check number of args for JS functions since arguments are not required + if call_expr.language != .js { + c.check_expected_arg_count(mut call_expr, func) or { return func.return_type } + } + // println / eprintln / panic can print anything + if fn_name in ['println', 'print', 'eprintln', 'eprint', 'panic'] && call_expr.args.len > 0 { + c.inside_println_arg = true + c.expected_type = ast.string_type + call_expr.args[0].typ = c.expr(call_expr.args[0].expr) + arg := call_expr.args[0] + c.check_expr_opt_call(arg.expr, arg.typ) + if arg.typ.is_void() { + c.error('`$fn_name` can not print void expressions', call_expr.pos) + } else if arg.typ == ast.char_type && arg.typ.nr_muls() == 0 { + c.error('`$fn_name` cannot print type `char` directly, print its address or cast it to an integer instead', + call_expr.pos) + } + c.fail_if_unreadable(arg.expr, arg.typ, 'argument to print') + c.inside_println_arg = false + call_expr.return_type = ast.void_type + /* + // TODO: optimize `struct T{} fn (t &T) str() string {return 'abc'} mut a := []&T{} a << &T{} println(a[0])` + // It currently generates: + // `println(T_str_no_ptr(*(*(T**)array_get(a, 0))));` + // ... which works, but could be just: + // `println(T_str(*(T**)array_get(a, 0)));` + prexpr := call_expr.args[0].expr + prtyp := call_expr.args[0].typ + prtyp_sym := c.table.get_type_symbol(prtyp) + prtyp_is_ptr := prtyp.is_ptr() + prhas_str, prexpects_ptr, prnr_args := prtyp_sym.str_method_info() + eprintln('>>> println hack typ: ${prtyp} | sym.name: ${prtyp_sym.name} | is_ptr: $prtyp_is_ptr | has_str: $prhas_str | expects_ptr: $prexpects_ptr | nr_args: $prnr_args | expr: ${prexpr.str()} ') + */ + return func.return_type + } + // `return error(err)` -> `return err` + if fn_name == 'error' && call_expr.args.len == 1 { + arg := call_expr.args[0] + call_expr.args[0].typ = c.expr(arg.expr) + if call_expr.args[0].typ == ast.error_type { + c.warn('`error($arg)` can be shortened to just `$arg`', call_expr.pos) + } + } + // TODO: typ optimize.. this node can get processed more than once + if call_expr.expected_arg_types.len == 0 { + for param in func.params { + call_expr.expected_arg_types << param.typ + } + } + for i, mut call_arg in call_expr.args { + param := if func.is_variadic && i >= func.params.len - 1 { + func.params[func.params.len - 1] + } else { + func.params[i] + } + if func.is_variadic && call_arg.expr is ast.ArrayDecompose { + if i > func.params.len - 1 { + c.error('too many arguments in call to `$func.name`', call_expr.pos) + } + } + c.expected_type = param.typ + + e_sym := c.table.get_type_symbol(c.expected_type) + if call_arg.expr is ast.MapInit && e_sym.kind == .struct_ { + c.error('cannot initialize a struct with a map', call_arg.pos) + continue + } else if call_arg.expr is ast.StructInit && e_sym.kind == .map { + c.error('cannot initialize a map with a struct', call_arg.pos) + continue + } + + typ := c.check_expr_opt_call(call_arg.expr, c.expr(call_arg.expr)) + call_expr.args[i].typ = typ + typ_sym := c.table.get_type_symbol(typ) + param_typ_sym := c.table.get_type_symbol(param.typ) + if func.is_variadic && typ.has_flag(.variadic) && call_expr.args.len - 1 > i { + c.error('when forwarding a variadic variable, it must be the final argument', + call_arg.pos) + } + arg_share := param.typ.share() + if arg_share == .shared_t && (c.locked_names.len > 0 || c.rlocked_names.len > 0) { + c.error('function with `shared` arguments cannot be called inside `lock`/`rlock` block', + call_arg.pos) + } + if call_arg.is_mut && func.language == .v { + to_lock, pos := c.fail_if_immutable(call_arg.expr) + if !call_arg.expr.is_lvalue() { + c.error('cannot pass expression as `mut`', call_arg.expr.position()) + } + if !param.is_mut { + tok := call_arg.share.str() + c.error('`$call_expr.name` parameter `$param.name` is not `$tok`, `$tok` is not needed`', + call_arg.expr.position()) + } else { + if param.typ.share() != call_arg.share { + c.error('wrong shared type', call_arg.expr.position()) + } + if to_lock != '' && !param.typ.has_flag(.shared_f) { + c.error('$to_lock is `shared` and must be `lock`ed to be passed as `mut`', + pos) + } + } + } else { + if param.is_mut { + tok := call_arg.share.str() + c.error('`$call_expr.name` parameter `$param.name` is `$tok`, you need to provide `$tok` e.g. `$tok arg${ + i + 1}`', call_arg.expr.position()) + } else { + c.fail_if_unreadable(call_arg.expr, typ, 'argument') + } + } + mut final_param_sym := param_typ_sym + if func.is_variadic && param_typ_sym.info is ast.Array { + final_param_sym = c.table.get_type_symbol(param_typ_sym.array_info().elem_type) + } + // NB: Casting to voidptr is used as an escape mechanism, so: + // 1. allow passing *explicit* voidptr (native or through cast) to functions + // expecting voidptr or ...voidptr + // ... but 2. disallow passing non-pointers - that is very rarely what the user wanted, + // it can lead to codegen errors (except for 'magic' functions like `json.encode` that, + // the compiler has special codegen support for), so it should be opt in, that is it + // shoould require an explicit voidptr(x) cast (and probably unsafe{} ?) . + if call_arg.typ != param.typ + && (param.typ == ast.voidptr_type || final_param_sym.idx == ast.voidptr_type_idx) + && !call_arg.typ.is_any_kind_of_pointer() && func.language == .v + && !call_arg.expr.is_lvalue() && func.name != 'json.encode' { + c.error('expression cannot be passed as `voidptr`', call_arg.expr.position()) + } + // Handle expected interface + if final_param_sym.kind == .interface_ { + if c.type_implements(typ, param.typ, call_arg.expr.position()) { + if !typ.is_ptr() && !typ.is_pointer() && !c.inside_unsafe + && typ_sym.kind != .interface_ { + c.mark_as_referenced(mut &call_arg.expr, true) + } + } + continue + } + c.check_expected_call_arg(typ, c.unwrap_generic(param.typ), call_expr.language) or { + // str method, allow type with str method if fn arg is string + // Passing an int or a string array produces a c error here + // Deleting this condition results in propper V error messages + // if arg_typ_sym.kind == .string && typ_sym.has_method('str') { + // continue + // } + if typ_sym.kind == .void && param_typ_sym.kind == .string { + continue + } + if param.typ.has_flag(.generic) { + continue + } + if c.pref.translated { + // Allow enums to be used as ints and vice versa in translated code + if param.typ == ast.int_type && typ_sym.kind == .enum_ { + continue + } + if typ == ast.int_type && param_typ_sym.kind == .enum_ { + continue + } + } + c.error('$err.msg in argument ${i + 1} to `$fn_name`', call_arg.pos) + } + // Warn about automatic (de)referencing, which will be removed soon. + if func.language != .c && !c.inside_unsafe && typ.nr_muls() != param.typ.nr_muls() + && !(call_arg.is_mut && param.is_mut) && !(!call_arg.is_mut && !param.is_mut) + && param.typ !in [ast.byteptr_type, ast.charptr_type, ast.voidptr_type] { + // sym := c.table.get_type_symbol(typ) + c.warn('automatic referencing/dereferencing is deprecated and will be removed soon (got: $typ.nr_muls() references, expected: $param.typ.nr_muls() references)', + call_arg.pos) + } + } + if func.generic_names.len != call_expr.concrete_types.len { + // no type arguments given in call, attempt implicit instantiation + c.infer_fn_generic_types(func, mut call_expr) + concrete_types = call_expr.concrete_types + } + if func.generic_names.len > 0 { + for i, mut call_arg in call_expr.args { + param := if func.is_variadic && i >= func.params.len - 1 { + func.params[func.params.len - 1] + } else { + func.params[i] + } + c.expected_type = param.typ + typ := c.check_expr_opt_call(call_arg.expr, c.expr(call_arg.expr)) + + if param.typ.has_flag(.generic) + && func.generic_names.len == call_expr.concrete_types.len { + if unwrap_typ := c.table.resolve_generic_to_concrete(param.typ, func.generic_names, + concrete_types) + { + utyp := c.unwrap_generic(typ) + unwrap_sym := c.table.get_type_symbol(unwrap_typ) + if unwrap_sym.kind == .interface_ { + if c.type_implements(utyp, unwrap_typ, call_arg.expr.position()) { + if !utyp.is_ptr() && !utyp.is_pointer() && !c.inside_unsafe + && c.table.get_type_symbol(utyp).kind != .interface_ { + c.mark_as_referenced(mut &call_arg.expr, true) + } + } + continue + } + c.check_expected_call_arg(utyp, unwrap_typ, call_expr.language) or { + c.error('$err.msg in argument ${i + 1} to `$fn_name`', call_arg.pos) + } + } + } + } + } + // resolve return generics struct to concrete type + if func.generic_names.len > 0 && func.return_type.has_flag(.generic) { + call_expr.return_type = c.unwrap_generic_type(func.return_type, func.generic_names, + concrete_types) + } else { + call_expr.return_type = func.return_type + } + if call_expr.concrete_types.len > 0 && func.return_type != 0 { + if typ := c.table.resolve_generic_to_concrete(func.return_type, func.generic_names, + concrete_types) + { + call_expr.return_type = typ + return typ + } + } + if call_expr.concrete_types.len > 0 && func.generic_names.len == 0 { + c.error('a non generic function called like a generic one', call_expr.concrete_list_pos) + } + + if call_expr.concrete_types.len > func.generic_names.len { + c.error('too many generic parameters got $call_expr.concrete_types.len, expected $func.generic_names.len', + call_expr.concrete_list_pos) + } + if func.generic_names.len > 0 { + return call_expr.return_type + } + return func.return_type +} + +fn (mut c Checker) deprecate_fnmethod(kind string, name string, the_fn ast.Fn, call_expr ast.CallExpr) { + start_message := '$kind `$name`' + mut deprecation_message := '' + now := time.now() + mut after_time := now + for attr in the_fn.attrs { + if attr.name == 'deprecated' && attr.arg != '' { + deprecation_message = attr.arg + } + if attr.name == 'deprecated_after' && attr.arg != '' { + after_time = time.parse_iso8601(attr.arg) or { + c.error('invalid time format', attr.pos) + time.now() + } + } + } + if after_time < now { + c.warn(semicolonize('$start_message has been deprecated since $after_time.ymmdd()', + deprecation_message), call_expr.pos) + } else if after_time == now { + c.warn(semicolonize('$start_message has been deprecated', deprecation_message), + call_expr.pos) + } else { + c.note(semicolonize('$start_message will be deprecated after $after_time.ymmdd()', + deprecation_message), call_expr.pos) + } +} + +fn semicolonize(main string, details string) string { + if details == '' { + return main + } + return '$main; $details' +} + +fn (mut c Checker) resolve_generic_interface(typ ast.Type, interface_type ast.Type, pos token.Position) ast.Type { + utyp := c.unwrap_generic(typ) + typ_sym := c.table.get_type_symbol(utyp) + mut inter_sym := c.table.get_type_symbol(interface_type) + + if mut inter_sym.info is ast.Interface { + if inter_sym.info.is_generic { + mut inferred_types := []ast.Type{} + generic_names := inter_sym.info.generic_types.map(c.table.get_type_name(it)) + // inferring interface generic types + for gt_name in generic_names { + mut inferred_type := ast.void_type + for ifield in inter_sym.info.fields { + if ifield.typ.has_flag(.generic) && c.table.get_type_name(ifield.typ) == gt_name { + if field := c.table.find_field_with_embeds(typ_sym, ifield.name) { + inferred_type = field.typ + } + } + } + for imethod in inter_sym.info.methods { + method := typ_sym.find_method(imethod.name) or { + typ_sym.find_method_with_generic_parent(imethod.name) or { ast.Fn{} } + } + if imethod.return_type.has_flag(.generic) { + imret_sym := c.table.get_type_symbol(imethod.return_type) + mret_sym := c.table.get_type_symbol(method.return_type) + if imret_sym.info is ast.MultiReturn && mret_sym.info is ast.MultiReturn { + for i, mr_typ in imret_sym.info.types { + if mr_typ.has_flag(.generic) + && c.table.get_type_name(mr_typ) == gt_name { + inferred_type = mret_sym.info.types[i] + } + } + } else if c.table.get_type_name(imethod.return_type) == gt_name { + mut ret_typ := method.return_type + if imethod.return_type.has_flag(.optional) { + ret_typ = ret_typ.clear_flag(.optional) + } + inferred_type = ret_typ + } + } + for i, iparam in imethod.params { + param := method.params[i] or { ast.Param{} } + if iparam.typ.has_flag(.generic) + && c.table.get_type_name(iparam.typ) == gt_name { + inferred_type = param.typ + } + } + } + if inferred_type == ast.void_type { + c.error('could not infer generic type `$gt_name` in interface', pos) + return interface_type + } + inferred_types << inferred_type + } + // add concrete types to method + for imethod in inter_sym.info.methods { + if inferred_types !in c.table.fn_generic_types[imethod.name] { + c.table.fn_generic_types[imethod.name] << inferred_types + } + } + inter_sym.info.concrete_types = inferred_types + return c.unwrap_generic_type(interface_type, generic_names, inter_sym.info.concrete_types) + } + } + return interface_type +} + +fn (mut c Checker) type_implements(typ ast.Type, interface_type ast.Type, pos token.Position) bool { + $if debug_interface_type_implements ? { + eprintln('> type_implements typ: $typ.debug() (`${c.table.type_to_str(typ)}`) | inter_typ: $interface_type.debug() (`${c.table.type_to_str(interface_type)}`)') + } + utyp := c.unwrap_generic(typ) + typ_sym := c.table.get_type_symbol(utyp) + mut inter_sym := c.table.get_type_symbol(interface_type) + if mut inter_sym.info is ast.Interface { + mut generic_type := interface_type + mut generic_info := inter_sym.info + if inter_sym.info.parent_type.has_flag(.generic) { + parent_sym := c.table.get_type_symbol(inter_sym.info.parent_type) + if parent_sym.info is ast.Interface { + generic_type = inter_sym.info.parent_type + generic_info = parent_sym.info + } + } + mut inferred_type := interface_type + if generic_info.is_generic { + inferred_type = c.resolve_generic_interface(typ, generic_type, pos) + if inferred_type == 0 { + return false + } + } + if inter_sym.info.is_generic { + return c.type_implements(typ, inferred_type, pos) + } + } + // do not check the same type more than once + if mut inter_sym.info is ast.Interface { + for t in inter_sym.info.types { + if t.idx() == utyp.idx() { + return true + } + } + } + styp := c.table.type_to_str(utyp) + if utyp.idx() == interface_type.idx() { + // same type -> already casted to the interface + return true + } + if interface_type.idx() == ast.error_type_idx && utyp.idx() == ast.none_type_idx { + // `none` "implements" the Error interface + return true + } + if typ_sym.kind == .interface_ && inter_sym.kind == .interface_ { + c.error('cannot implement interface `$inter_sym.name` with a different interface `$styp`', + pos) + } + imethods := if inter_sym.kind == .interface_ { + (inter_sym.info as ast.Interface).methods + } else { + inter_sym.methods + } + // voidptr is an escape hatch, it should be allowed to be passed + if utyp != ast.voidptr_type { + // Verify methods + for imethod in imethods { + method := typ_sym.find_method(imethod.name) or { + typ_sym.find_method_with_generic_parent(imethod.name) or { + c.error("`$styp` doesn't implement method `$imethod.name` of interface `$inter_sym.name`", + pos) + continue + } + } + msg := c.table.is_same_method(imethod, method) + if msg.len > 0 { + sig := c.table.fn_signature(imethod, skip_receiver: true) + c.add_error_detail('$inter_sym.name has `$sig`') + c.error('`$styp` incorrectly implements method `$imethod.name` of interface `$inter_sym.name`: $msg', + pos) + return false + } + } + } + // Verify fields + if mut inter_sym.info is ast.Interface { + for ifield in inter_sym.info.fields { + if field := c.table.find_field_with_embeds(typ_sym, ifield.name) { + if ifield.typ != field.typ { + exp := c.table.type_to_str(ifield.typ) + got := c.table.type_to_str(field.typ) + c.error('`$styp` incorrectly implements field `$ifield.name` of interface `$inter_sym.name`, expected `$exp`, got `$got`', + pos) + return false + } else if ifield.is_mut && !(field.is_mut || field.is_global) { + c.error('`$styp` incorrectly implements interface `$inter_sym.name`, field `$ifield.name` must be mutable', + pos) + return false + } + continue + } + // voidptr is an escape hatch, it should be allowed to be passed + if utyp != ast.voidptr_type { + c.error("`$styp` doesn't implement field `$ifield.name` of interface `$inter_sym.name`", + pos) + } + } + inter_sym.info.types << utyp + } + return true +} + +// return the actual type of the expression, once the optional is handled +pub fn (mut c Checker) check_expr_opt_call(expr ast.Expr, ret_type ast.Type) ast.Type { + if expr is ast.CallExpr { + if expr.return_type.has_flag(.optional) { + if expr.or_block.kind == .absent { + if c.inside_defer { + c.error('${expr.name}() returns an option, so it should have an `or {}` block at the end', + expr.pos) + } else { + c.error('${expr.name}() returns an option, so it should have either an `or {}` block, or `?` at the end', + expr.pos) + } + } else { + c.check_or_expr(expr.or_block, ret_type, expr.return_type.clear_flag(.optional)) + } + return ret_type.clear_flag(.optional) + } else if expr.or_block.kind == .block { + c.error('unexpected `or` block, the function `$expr.name` does not return an optional', + expr.or_block.pos) + } else if expr.or_block.kind == .propagate { + c.error('unexpected `?`, the function `$expr.name` does not return an optional', + expr.or_block.pos) + } + } else if expr is ast.IndexExpr { + if expr.or_expr.kind != .absent { + c.check_or_expr(expr.or_expr, ret_type, ret_type) + } + } + return ret_type +} + +pub fn (mut c Checker) check_or_expr(or_expr ast.OrExpr, ret_type ast.Type, expr_return_type ast.Type) { + if or_expr.kind == .propagate { + if !c.table.cur_fn.return_type.has_flag(.optional) && c.table.cur_fn.name != 'main.main' + && !c.inside_const { + c.error('to propagate the optional call, `$c.table.cur_fn.name` must return an optional', + or_expr.pos) + } + return + } + stmts_len := or_expr.stmts.len + if stmts_len == 0 { + if ret_type != ast.void_type { + // x := f() or {} + c.error('assignment requires a non empty `or {}` block', or_expr.pos) + } + // allow `f() or {}` + return + } + last_stmt := or_expr.stmts[stmts_len - 1] + if ret_type != ast.void_type { + match last_stmt { + ast.ExprStmt { + c.expected_type = ret_type + c.expected_or_type = ret_type.clear_flag(.optional) + last_stmt_typ := c.expr(last_stmt.expr) + c.expected_or_type = ast.void_type + type_fits := c.check_types(last_stmt_typ, ret_type) + && last_stmt_typ.nr_muls() == ret_type.nr_muls() + is_noreturn := is_noreturn_callexpr(last_stmt.expr) + if type_fits || is_noreturn { + return + } + expected_type_name := c.table.type_to_str(ret_type.clear_flag(.optional)) + if last_stmt.typ == ast.void_type { + c.error('`or` block must provide a default value of type `$expected_type_name`, or return/continue/break or call a [noreturn] function like panic(err) or exit(1)', + last_stmt.pos) + } else { + type_name := c.table.type_to_str(last_stmt_typ) + c.error('wrong return type `$type_name` in the `or {}` block, expected `$expected_type_name`', + last_stmt.pos) + } + return + } + ast.BranchStmt { + if last_stmt.kind !in [.key_continue, .key_break] { + c.error('only break/continue is allowed as a branch statement in the end of an `or {}` block', + last_stmt.pos) + return + } + } + ast.Return {} + else { + expected_type_name := c.table.type_to_str(ret_type.clear_flag(.optional)) + c.error('last statement in the `or {}` block should be an expression of type `$expected_type_name` or exit parent scope', + or_expr.pos) + return + } + } + } else { + match last_stmt { + ast.ExprStmt { + if last_stmt.typ == ast.void_type { + return + } + if is_noreturn_callexpr(last_stmt.expr) { + return + } + if c.check_types(last_stmt.typ, expr_return_type) { + return + } + // opt_returning_string() or { ... 123 } + type_name := c.table.type_to_str(last_stmt.typ) + expr_return_type_name := c.table.type_to_str(expr_return_type) + c.error('the default expression type in the `or` block should be `$expr_return_type_name`, instead you gave a value of type `$type_name`', + last_stmt.expr.position()) + } + else {} + } + } +} + +fn is_noreturn_callexpr(expr ast.Expr) bool { + if expr is ast.CallExpr { + return expr.is_noreturn + } + return false +} + +pub fn (mut c Checker) selector_expr(mut node ast.SelectorExpr) ast.Type { + prevent_sum_type_unwrapping_once := c.prevent_sum_type_unwrapping_once + c.prevent_sum_type_unwrapping_once = false + + using_new_err_struct_save := c.using_new_err_struct + // TODO remove; this avoids a breaking change in syntax + if '$node.expr' == 'err' { + c.using_new_err_struct = true + } + + // T.name, typeof(expr).name + mut name_type := 0 + match mut node.expr { + ast.Ident { + name := node.expr.name + valid_generic := util.is_generic_type_name(name) && name in c.table.cur_fn.generic_names + if valid_generic { + name_type = ast.Type(c.table.find_type_idx(name)).set_flag(.generic) + } + } + // Note: in future typeof() should be a type known at compile-time + // sum types should not be handled dynamically + ast.TypeOf { + name_type = c.expr(node.expr.expr) + } + else {} + } + if name_type > 0 { + if node.field_name != 'name' { + c.error('invalid field `.$node.field_name` for type `$node.expr`', node.pos) + } + node.name_type = name_type + return ast.string_type + } + + // + old_selector_expr := c.inside_selector_expr + c.inside_selector_expr = true + typ := c.expr(node.expr) + c.inside_selector_expr = old_selector_expr + // + c.using_new_err_struct = using_new_err_struct_save + if typ == ast.void_type_idx { + c.error('`void` type has no fields', node.pos) + return ast.void_type + } + node.expr_type = typ + if node.expr_type.has_flag(.optional) && !((node.expr is ast.Ident + && (node.expr as ast.Ident).kind == .constant)) { + c.error('cannot access fields of an optional, handle the error with `or {...}` or propagate it with `?`', + node.pos) + } + field_name := node.field_name + utyp := c.unwrap_generic(typ) + sym := c.table.get_type_symbol(utyp) + if (typ.has_flag(.variadic) || sym.kind == .array_fixed) && field_name == 'len' { + node.typ = ast.int_type + return ast.int_type + } + if sym.kind == .chan { + if field_name == 'closed' { + node.typ = ast.bool_type + return ast.bool_type + } else if field_name in ['len', 'cap'] { + node.typ = ast.u32_type + return ast.u32_type + } + } + mut unknown_field_msg := 'type `$sym.name` has no field named `$field_name`' + mut has_field := false + mut field := ast.StructField{} + if field_name.len > 0 && field_name[0].is_capital() && sym.info is ast.Struct + && sym.language == .v { + // x.Foo.y => access the embedded struct + for embed in sym.info.embeds { + embed_sym := c.table.get_type_symbol(embed) + if embed_sym.embed_name() == field_name { + node.typ = embed + return embed + } + } + } else { + if f := c.table.find_field(sym, field_name) { + has_field = true + field = f + } else { + // look for embedded field + has_field = true + mut embed_type := ast.Type(0) + field, embed_type = c.table.find_field_from_embeds(sym, field_name) or { + if err.msg != '' { + c.error(err.msg, node.pos) + } + has_field = false + ast.StructField{}, ast.Type(0) + } + node.from_embed_type = embed_type + if sym.kind in [.aggregate, .sum_type] { + unknown_field_msg = err.msg + } + } + if !c.inside_unsafe { + if sym.info is ast.Struct { + if sym.info.is_union && node.next_token !in token.assign_tokens { + c.warn('reading a union field (or its address) requires `unsafe`', + node.pos) + } + } + } + if typ.has_flag(.generic) && !has_field { + gs := c.table.get_type_symbol(typ) + if f := c.table.find_field(gs, field_name) { + has_field = true + field = f + } else { + // look for embedded field + has_field = true + mut embed_type := ast.Type(0) + field, embed_type = c.table.find_field_from_embeds(gs, field_name) or { + if err.msg != '' { + c.error(err.msg, node.pos) + } + has_field = false + ast.StructField{}, ast.Type(0) + } + node.from_embed_type = embed_type + } + } + } + if has_field { + if sym.mod != c.mod && !field.is_pub && sym.language != .c { + c.error('field `${sym.name}.$field_name` is not public', node.pos) + } + field_sym := c.table.get_type_symbol(field.typ) + if field_sym.kind in [.sum_type, .interface_] { + if !prevent_sum_type_unwrapping_once { + if scope_field := node.scope.find_struct_field(node.expr.str(), utyp, + field_name) + { + return scope_field.smartcasts.last() + } + } + } + node.typ = field.typ + return field.typ + } + if sym.kind !in [.struct_, .aggregate, .interface_, .sum_type] { + if sym.kind != .placeholder { + c.error('`$sym.name` has no property `$node.field_name`', node.pos) + } + } else { + if sym.info is ast.Struct { + suggestion := util.new_suggestion(field_name, sym.info.fields.map(it.name)) + c.error(suggestion.say(unknown_field_msg), node.pos) + } + c.error(unknown_field_msg, node.pos) + } + return ast.void_type +} + +// TODO: non deferred +pub fn (mut c Checker) return_stmt(mut node ast.Return) { + c.expected_type = c.table.cur_fn.return_type + mut expected_type := c.unwrap_generic(c.expected_type) + expected_type_sym := c.table.get_type_symbol(expected_type) + if node.exprs.len > 0 && c.table.cur_fn.return_type == ast.void_type { + c.error('unexpected argument, current function does not return anything', node.exprs[0].position()) + return + } else if node.exprs.len == 0 && !(c.expected_type == ast.void_type + || expected_type_sym.kind == .void) { + stype := c.table.type_to_str(expected_type) + arg := if expected_type_sym.kind == .multi_return { 'arguments' } else { 'argument' } + c.error('expected `$stype` $arg', node.pos) + return + } + if node.exprs.len == 0 { + return + } + exp_is_optional := expected_type.has_flag(.optional) + mut expected_types := [expected_type] + if expected_type_sym.info is ast.MultiReturn { + expected_types = expected_type_sym.info.types + if c.table.cur_concrete_types.len > 0 { + expected_types = expected_types.map(c.unwrap_generic(it)) + } + } + mut got_types := []ast.Type{} + for expr in node.exprs { + typ := c.expr(expr) + // Unpack multi return types + sym := c.table.get_type_symbol(typ) + if sym.kind == .multi_return { + for t in sym.mr_info().types { + got_types << t + } + } else { + got_types << typ + } + } + node.types = got_types + $if debug_manualfree ? { + cfn := c.table.cur_fn + if cfn.is_manualfree { + pnames := cfn.params.map(it.name) + for expr in node.exprs { + if expr is ast.Ident { + if expr.name in pnames { + c.note('returning a parameter in a fn marked with `[manualfree]` can cause double freeing in the caller', + node.pos) + } + } + } + } + } + // allow `none` & `error` return types for function that returns optional + option_type_idx := c.table.type_idxs['Option'] + got_types_0_idx := got_types[0].idx() + if exp_is_optional + && got_types_0_idx in [ast.none_type_idx, ast.error_type_idx, option_type_idx] { + if got_types_0_idx == ast.none_type_idx && expected_type == ast.ovoid_type { + c.error('returning `none` in functions, that have a `?` result type is not allowed anymore, either `return error(message)` or just `return` instead', + node.pos) + } + return + } + if expected_types.len > 0 && expected_types.len != got_types.len { + arg := if expected_types.len == 1 { 'argument' } else { 'arguments' } + c.error('expected $expected_types.len $arg, but got $got_types.len', node.pos) + return + } + for i, exp_type in expected_types { + got_typ := c.unwrap_generic(got_types[i]) + if got_typ.has_flag(.optional) && (!exp_type.has_flag(.optional) + || c.table.type_to_str(got_typ) != c.table.type_to_str(exp_type)) { + pos := node.exprs[i].position() + c.error('cannot use `${c.table.type_to_str(got_typ)}` as type `${c.table.type_to_str(exp_type)}` in return argument', + pos) + } + if !c.check_types(got_typ, exp_type) { + got_typ_sym := c.table.get_type_symbol(got_typ) + mut exp_typ_sym := c.table.get_type_symbol(exp_type) + pos := node.exprs[i].position() + if node.exprs[i].is_auto_deref_var() { + continue + } + if exp_typ_sym.kind == .interface_ { + if c.type_implements(got_typ, exp_type, node.pos) { + if !got_typ.is_ptr() && !got_typ.is_pointer() && got_typ_sym.kind != .interface_ + && !c.inside_unsafe { + c.mark_as_referenced(mut &node.exprs[i], true) + } + } + continue + } + c.error('cannot use `$got_typ_sym.name` as type `${c.table.type_to_str(exp_type)}` in return argument', + pos) + } + if (got_typ.is_ptr() || got_typ.is_pointer()) + && (!exp_type.is_ptr() && !exp_type.is_pointer()) { + pos := node.exprs[i].position() + if node.exprs[i].is_auto_deref_var() { + continue + } + c.error('fn `$c.table.cur_fn.name` expects you to return a non reference type `${c.table.type_to_str(exp_type)}`, but you are returning `${c.table.type_to_str(got_typ)}` instead', + pos) + } + if (exp_type.is_ptr() || exp_type.is_pointer()) + && (!got_typ.is_ptr() && !got_typ.is_pointer()) && got_typ != ast.int_literal_type { + pos := node.exprs[i].position() + if node.exprs[i].is_auto_deref_var() { + continue + } + c.error('fn `$c.table.cur_fn.name` expects you to return a reference type `${c.table.type_to_str(exp_type)}`, but you are returning `${c.table.type_to_str(got_typ)}` instead', + pos) + } + if exp_type.is_ptr() && got_typ.is_ptr() { + mut r_expr := &node.exprs[i] + if mut r_expr is ast.Ident { + if mut r_expr.obj is ast.Var { + mut obj := unsafe { &r_expr.obj } + if c.fn_scope != voidptr(0) { + obj = c.fn_scope.find_var(r_expr.obj.name) or { obj } + } + if obj.is_stack_obj && !c.inside_unsafe { + type_sym := c.table.get_type_symbol(obj.typ.set_nr_muls(0)) + if !type_sym.is_heap() && !c.pref.translated { + suggestion := if type_sym.kind == .struct_ { + 'declaring `$type_sym.name` as `[heap]`' + } else { + 'wrapping the `$type_sym.name` object in a `struct` declared as `[heap]`' + } + c.error('`$r_expr.name` cannot be returned outside `unsafe` blocks as it might refer to an object stored on stack. Consider ${suggestion}.', + r_expr.pos) + } + } + } + } + } + } + if exp_is_optional && node.exprs.len > 0 { + expr0 := node.exprs[0] + if expr0 is ast.CallExpr { + if expr0.or_block.kind == .propagate { + c.error('`?` is not needed, use `return ${expr0.name}()`', expr0.pos) + } + } + } +} + +pub fn (mut c Checker) const_decl(mut node ast.ConstDecl) { + mut field_names := []string{} + mut field_order := []int{} + if node.fields.len == 0 { + c.warn('const block must have at least 1 declaration', node.pos) + } + for i, field in node.fields { + // TODO Check const name once the syntax is decided + if field.name in c.const_names { + name_pos := token.Position{ + ...field.pos + len: util.no_cur_mod(field.name, c.mod).len + } + c.error('duplicate const `$field.name`', name_pos) + } + c.const_names << field.name + field_names << field.name + field_order << i + } + mut needs_order := false + mut done_fields := []int{} + for i, mut field in node.fields { + c.const_decl = field.name + c.const_deps << field.name + mut typ := c.check_expr_opt_call(field.expr, c.expr(field.expr)) + if ct_value := eval_comptime_const_expr(field.expr, 0) { + field.comptime_expr_value = ct_value + if ct_value is u64 { + typ = ast.u64_type + } + } + node.fields[i].typ = c.table.mktyp(typ) + for cd in c.const_deps { + for j, f in node.fields { + if j != i && cd in field_names && cd == f.name && j !in done_fields { + needs_order = true + x := field_order[j] + field_order[j] = field_order[i] + field_order[i] = x + break + } + } + } + done_fields << i + c.const_deps = [] + } + if needs_order { + mut ordered_fields := []ast.ConstField{} + for order in field_order { + ordered_fields << node.fields[order] + } + node.fields = ordered_fields + } +} + +pub fn (mut c Checker) enum_decl(decl ast.EnumDecl) { + c.check_valid_pascal_case(decl.name, 'enum name', decl.pos) + mut seen := []i64{} + if decl.fields.len == 0 { + c.error('enum cannot be empty', decl.pos) + } + /* + if decl.is_pub && c.mod == 'builtin' { + c.error('`builtin` module cannot have enums', decl.pos) + } + */ + for i, field in decl.fields { + if !c.pref.experimental && util.contains_capital(field.name) { + // TODO C2V uses hundreds of enums with capitals, remove -experimental check once it's handled + c.error('field name `$field.name` cannot contain uppercase letters, use snake_case instead', + field.pos) + } + for j in 0 .. i { + if field.name == decl.fields[j].name { + c.error('field name `$field.name` duplicate', field.pos) + } + } + if field.has_expr { + match field.expr { + ast.IntegerLiteral { + val := field.expr.val.i64() + if val < checker.int_min || val > checker.int_max { + c.error('enum value `$val` overflows int', field.expr.pos) + } else if !decl.is_multi_allowed && i64(val) in seen { + c.error('enum value `$val` already exists', field.expr.pos) + } + seen << i64(val) + } + ast.PrefixExpr {} + else { + if field.expr is ast.Ident { + if field.expr.language == .c { + continue + } + } + mut pos := field.expr.position() + if pos.pos == 0 { + pos = field.pos + } + c.error('default value for enum has to be an integer', pos) + } + } + } else { + if seen.len > 0 { + last := seen[seen.len - 1] + if last == checker.int_max { + c.error('enum value overflows', field.pos) + } else if !decl.is_multi_allowed && last + 1 in seen { + c.error('enum value `${last + 1}` already exists', field.pos) + } + seen << last + 1 + } else { + seen << 0 + } + } + } +} + +pub fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) { + c.expected_type = ast.none_type // TODO a hack to make `x := if ... work` + defer { + c.expected_type = ast.void_type + } + right_first := node.right[0] + node.left_types = [] + mut right_len := node.right.len + mut right_type0 := ast.void_type + for i, right in node.right { + if right is ast.CallExpr || right is ast.IfExpr || right is ast.LockExpr + || right is ast.MatchExpr { + right_type := c.expr(right) + if i == 0 { + right_type0 = right_type + node.right_types = [ + c.check_expr_opt_call(right, right_type0), + ] + } + right_type_sym := c.table.get_type_symbol(right_type) + if right_type_sym.kind == .multi_return { + if node.right.len > 1 { + c.error('cannot use multi-value $right_type_sym.name in single-value context', + right.position()) + } + node.right_types = right_type_sym.mr_info().types + right_len = node.right_types.len + } else if right_type == ast.void_type { + right_len = 0 + } + } + } + if node.left.len != right_len { + if right_first is ast.CallExpr { + c.error('assignment mismatch: $node.left.len variable(s) but `${right_first.name}()` returns $right_len value(s)', + node.pos) + } else { + c.error('assignment mismatch: $node.left.len variable(s) $right_len value(s)', + node.pos) + } + return + } + + is_decl := node.op == .decl_assign + for i, left in node.left { + if left is ast.CallExpr { + // ban `foo() = 10` + c.error('cannot call function `${left.name}()` on the left side of an assignment', + left.pos) + } else if left is ast.PrefixExpr { + // ban `*foo() = 10` + if left.right is ast.CallExpr && left.op == .mul { + c.error('cannot dereference a function call on the left side of an assignment, use a temporary variable', + left.pos) + } + } else if left is ast.IndexExpr { + if left.index is ast.RangeExpr { + c.error('cannot reassign using range expression on the left side of an assignment', + left.pos) + } + } + is_blank_ident := left.is_blank_ident() + mut left_type := ast.void_type + if !is_decl && !is_blank_ident { + if left is ast.Ident || left is ast.SelectorExpr { + c.prevent_sum_type_unwrapping_once = true + } + left_type = c.expr(left) + c.expected_type = c.unwrap_generic(left_type) + // `map = {}` + sym := c.table.get_type_symbol(left_type) + if sym.kind == .map && node.right[i] is ast.StructInit { + c.warn('assigning a struct literal to a map is deprecated - use `map{}` instead', + node.right[i].position()) + node.right[i] = ast.MapInit{} + } + } + if node.right_types.len < node.left.len { // first type or multi return types added above + old_inside_ref_lit := c.inside_ref_lit + if left is ast.Ident { + if left.info is ast.IdentVar { + c.inside_ref_lit = c.inside_ref_lit || left.info.share == .shared_t + } + } + c.inside_decl_rhs = is_decl + right_type := c.expr(node.right[i]) + c.inside_decl_rhs = false + c.inside_ref_lit = old_inside_ref_lit + if node.right_types.len == i { + node.right_types << c.check_expr_opt_call(node.right[i], right_type) + } + } + right := if i < node.right.len { node.right[i] } else { node.right[0] } + mut right_type := node.right_types[i] + if right is ast.Ident { + right_sym := c.table.get_type_symbol(right_type) + if right_sym.info is ast.Struct { + if right_sym.info.generic_types.len > 0 { + if obj := right.scope.find(right.name) { + right_type = obj.typ + } + } + } + } + if is_decl { + // check generic struct init and return unwrap generic struct type + if right is ast.StructInit { + if right.typ.has_flag(.generic) { + c.expr(right) + right_type = right.typ + } + } else if right is ast.PrefixExpr { + if right.op == .amp && right.right is ast.StructInit { + right_type = c.expr(right) + } + } + if right.is_auto_deref_var() { + left_type = c.table.mktyp(right_type.deref()) + } else { + left_type = c.table.mktyp(right_type) + } + if left_type == ast.int_type { + if right is ast.IntegerLiteral { + mut is_large := right.val.len > 13 + if !is_large && right.val.len > 8 { + val := right.val.i64() + is_large = val > checker.int_max || val < checker.int_min + } + if is_large { + c.error('overflow in implicit type `int`, use explicit type casting instead', + right.pos) + } + } + } + } else { + // Make sure the variable is mutable + c.fail_if_immutable(left) + // left_type = c.expr(left) + } + if right_type.is_ptr() && left_type.is_ptr() { + if mut right is ast.Ident { + if mut right.obj is ast.Var { + mut obj := unsafe { &right.obj } + if c.fn_scope != voidptr(0) { + obj = c.fn_scope.find_var(right.obj.name) or { obj } + } + if obj.is_stack_obj && !c.inside_unsafe { + type_sym := c.table.get_type_symbol(obj.typ.set_nr_muls(0)) + if !type_sym.is_heap() && !c.pref.translated { + suggestion := if type_sym.kind == .struct_ { + 'declaring `$type_sym.name` as `[heap]`' + } else { + 'wrapping the `$type_sym.name` object in a `struct` declared as `[heap]`' + } + c.error('`$right.name` cannot be assigned outside `unsafe` blocks as it might refer to an object stored on stack. Consider ${suggestion}.', + right.pos) + } + } + } + } + } + node.left_types << left_type + match mut left { + ast.Ident { + if left.kind == .blank_ident { + left_type = right_type + node.left_types[i] = right_type + if node.op !in [.assign, .decl_assign] { + c.error('cannot modify blank `_` identifier', left.pos) + } + } else if left.info !is ast.IdentVar { + c.error('cannot assign to $left.kind `$left.name`', left.pos) + } else { + if is_decl { + c.check_valid_snake_case(left.name, 'variable name', left.pos) + } + mut ident_var_info := left.info as ast.IdentVar + if ident_var_info.share == .shared_t { + left_type = left_type.set_flag(.shared_f) + if is_decl { + if left_type.nr_muls() > 1 { + c.error('shared cannot be multi level reference', left.pos) + } + left_type = left_type.set_nr_muls(1) + } + } else if left_type.has_flag(.shared_f) { + left_type = left_type.clear_flag(.shared_f) + } + if ident_var_info.share == .atomic_t { + left_type = left_type.set_flag(.atomic_f) + } + node.left_types[i] = left_type + ident_var_info.typ = left_type + left.info = ident_var_info + if left_type != 0 { + match mut left.obj { + ast.Var { + left.obj.typ = left_type + if left.obj.is_auto_deref { + left.obj.is_used = true + } + if !left_type.is_ptr() { + if c.table.get_type_symbol(left_type).is_heap() { + left.obj.is_auto_heap = true + } + } + if left_type in ast.unsigned_integer_type_idxs { + if right is ast.IntegerLiteral { + if right.val[0] == `-` { + c.error('Cannot assign negative value to unsigned integer type', + right.pos) + } + } + } + } + ast.GlobalField { + left.obj.typ = left_type + } + else {} + } + } + if is_decl { + full_name := '${left.mod}.$left.name' + if obj := c.file.global_scope.find(full_name) { + if obj is ast.ConstField { + c.warn('duplicate of a const name `$full_name`', left.pos) + } + } + } + } + } + ast.PrefixExpr { + // Do now allow `*x = y` outside `unsafe` + if left.op == .mul { + if !c.inside_unsafe && !c.pref.translated { + c.error('modifying variables via dereferencing can only be done in `unsafe` blocks', + node.pos) + } else { + // mark `p` in `*p = val` as used: + match mut left.right { + ast.Ident { + match mut left.right.obj { + ast.Var { + left.right.obj.is_used = true + } + else {} + } + } + else {} + } + } + } + if is_decl { + c.error('non-name on the left side of `:=`', left.pos) + } + } + else { + if mut left is ast.IndexExpr { + // eprintln('>>> left.is_setter: ${left.is_setter:10} | left.is_map: ${left.is_map:10} | left.is_array: ${left.is_array:10}') + if left.is_map && left.is_setter { + left.recursive_mapset_is_setter(true) + } + } + if is_decl { + c.error('non-name `$left` on left side of `:=`', left.position()) + } + } + } + left_type_unwrapped := c.unwrap_generic(left_type) + right_type_unwrapped := c.unwrap_generic(right_type) + if right_type_unwrapped == 0 { + // right type was a generic `T` + continue + } + left_sym := c.table.get_type_symbol(left_type_unwrapped) + right_sym := c.table.get_type_symbol(right_type_unwrapped) + + if c.pref.translated { + // TODO fix this in C2V instead, for example cast enums to int before using `|` on them. + // TODO replace all c.pref.translated checks with `$if !translated` for performance + continue + } + if left_sym.kind == .array && !c.inside_unsafe && node.op in [.assign, .decl_assign] + && right_sym.kind == .array && (left is ast.Ident && !left.is_blank_ident()) + && right is ast.Ident { + // Do not allow `a = b`, only `a = b.clone()` + c.error('use `array2 $node.op.str() array1.clone()` instead of `array2 $node.op.str() array1` (or use `unsafe`)', + node.pos) + } + if left_sym.kind == .array_fixed && !c.inside_unsafe && node.op in [.assign, .decl_assign] + && right_sym.kind == .array_fixed && (left is ast.Ident && !left.is_blank_ident()) + && right is ast.Ident { + if right_sym.info is ast.ArrayFixed { + if right_sym.info.elem_type.is_ptr() { + c.error('assignment from one fixed array to another with a pointer element type is prohibited outside of `unsafe`', + node.pos) + } + } + } + if left_sym.kind == .map && node.op in [.assign, .decl_assign] && right_sym.kind == .map + && ((right is ast.Ident && right.is_auto_deref_var()) + || !right_type.is_ptr()) && !left.is_blank_ident() && right.is_lvalue() { + // Do not allow `a = b` + c.error('cannot copy map: call `move` or `clone` method (or use a reference)', + right.position()) + } + left_is_ptr := left_type.is_ptr() || left_sym.is_pointer() + if left_is_ptr && !left.is_auto_deref_var() { + if !c.inside_unsafe && node.op !in [.assign, .decl_assign] { + // ptr op= + c.warn('pointer arithmetic is only allowed in `unsafe` blocks', node.pos) + } + right_is_ptr := right_type.is_ptr() || right_sym.is_pointer() + if !right_is_ptr && node.op == .assign && right_type_unwrapped.is_number() { + c.error('cannot assign to `$left`: ' + + c.expected_msg(right_type_unwrapped, left_type_unwrapped), right.position()) + } + if (right is ast.StructInit || !right_is_ptr) && !(right_sym.is_number() + || left_type.has_flag(.shared_f)) { + left_name := c.table.type_to_str(left_type_unwrapped) + mut rtype := right_type_unwrapped + if rtype.is_ptr() { + rtype = rtype.deref() + } + right_name := c.table.type_to_str(rtype) + c.error('mismatched types `$left_name` and `$right_name`', node.pos) + } + } + // Single side check + match node.op { + .assign {} // No need to do single side check for =. But here put it first for speed. + .plus_assign, .minus_assign { + if left_type == ast.string_type { + if node.op != .plus_assign { + c.error('operator `$node.op` not defined on left operand type `$left_sym.name`', + left.position()) + } + if right_type != ast.string_type { + c.error('invalid right operand: $left_sym.name $node.op $right_sym.name', + right.position()) + } + } else if !left_sym.is_number() + && left_sym.kind !in [.byteptr, .charptr, .struct_, .alias] { + c.error('operator `$node.op` not defined on left operand type `$left_sym.name`', + left.position()) + } else if !right_sym.is_number() + && left_sym.kind !in [.byteptr, .charptr, .struct_, .alias] { + c.error('invalid right operand: $left_sym.name $node.op $right_sym.name', + right.position()) + } + } + .mult_assign, .div_assign { + if !left_sym.is_number() + && !c.table.get_final_type_symbol(left_type_unwrapped).is_int() + && left_sym.kind !in [.struct_, .alias] { + c.error('operator $node.op.str() not defined on left operand type `$left_sym.name`', + left.position()) + } else if !right_sym.is_number() + && !c.table.get_final_type_symbol(left_type_unwrapped).is_int() + && left_sym.kind !in [.struct_, .alias] { + c.error('operator $node.op.str() not defined on right operand type `$right_sym.name`', + right.position()) + } + } + .and_assign, .or_assign, .xor_assign, .mod_assign, .left_shift_assign, + .right_shift_assign { + if !left_sym.is_int() + && !c.table.get_final_type_symbol(left_type_unwrapped).is_int() { + c.error('operator $node.op.str() not defined on left operand type `$left_sym.name`', + left.position()) + } else if !right_sym.is_int() + && !c.table.get_final_type_symbol(right_type_unwrapped).is_int() { + c.error('operator $node.op.str() not defined on right operand type `$right_sym.name`', + right.position()) + } + } + else {} + } + if node.op in [.plus_assign, .minus_assign, .mod_assign, .mult_assign, .div_assign] + && ((left_sym.kind == .struct_ && right_sym.kind == .struct_) + || left_sym.kind == .alias) { + left_name := c.table.type_to_str(left_type) + right_name := c.table.type_to_str(right_type) + parent_sym := c.table.get_final_type_symbol(left_type) + if left_sym.kind == .alias && right_sym.kind != .alias { + c.error('mismatched types `$left_name` and `$right_name`', node.pos) + } + extracted_op := match node.op { + .plus_assign { '+' } + .minus_assign { '-' } + .div_assign { '/' } + .mod_assign { '%' } + .mult_assign { '*' } + else { 'unknown op' } + } + if method := left_sym.find_method(extracted_op) { + if method.return_type != left_type { + c.error('operator `$extracted_op` must return `$left_name` to be used as an assignment operator', + node.pos) + } + } else { + if parent_sym.is_primitive() { + c.error('cannot use operator methods on type alias for `$parent_sym.name`', + node.pos) + } + if left_name == right_name { + c.error('undefined operation `$left_name` $extracted_op `$right_name`', + node.pos) + } else { + c.error('mismatched types `$left_name` and `$right_name`', node.pos) + } + } + } + if !is_blank_ident && !left.is_auto_deref_var() && !right.is_auto_deref_var() + && right_sym.kind != .placeholder && left_sym.kind != .interface_ + && !right_type_unwrapped.has_flag(.generic) && !left_type_unwrapped.has_flag(.generic) { + // Dual sides check (compatibility check) + c.check_expected(right_type_unwrapped, left_type_unwrapped) or { + // allow for ptr += 2 + if left_type_unwrapped.is_ptr() && right_type_unwrapped.is_int() + && node.op in [.plus_assign, .minus_assign] { + if !c.inside_unsafe { + c.warn('pointer arithmetic is only allowed in `unsafe` blocks', + node.pos) + } + } else { + c.error('cannot assign to `$left`: $err.msg', right.position()) + } + } + } + if left_sym.kind == .interface_ { + if c.type_implements(right_type, left_type, right.position()) { + if !right_type.is_ptr() && !right_type.is_pointer() && right_sym.kind != .interface_ + && !c.inside_unsafe { + c.mark_as_referenced(mut &node.right[i], true) + } + } + } + } + // this needs to run after the assign stmt left exprs have been run through checker + // so that ident.obj is set + // Check `x := &y` and `mut x := <-ch` + if right_first is ast.PrefixExpr { + right_node := right_first + left_first := node.left[0] + if left_first is ast.Ident { + assigned_var := left_first + mut is_shared := false + if left_first.info is ast.IdentVar { + is_shared = left_first.info.share == .shared_t + } + old_inside_ref_lit := c.inside_ref_lit + c.inside_ref_lit = (c.inside_ref_lit || right_node.op == .amp || is_shared) + c.expr(right_node.right) + c.inside_ref_lit = old_inside_ref_lit + if right_node.op == .amp { + if right_node.right is ast.Ident { + if right_node.right.obj is ast.Var { + v := right_node.right.obj + right_type0 = v.typ + if !v.is_mut && assigned_var.is_mut && !c.inside_unsafe { + c.error('`$right_node.right.name` is immutable, cannot have a mutable reference to it', + right_node.pos) + } + } else if right_node.right.obj is ast.ConstField { + if assigned_var.is_mut && !c.inside_unsafe { + c.error('`$right_node.right.name` is immutable, cannot have a mutable reference to it', + right_node.pos) + } + } + } + } + if right_node.op == .arrow { + if assigned_var.is_mut { + right_sym := c.table.get_type_symbol(right_type0) + if right_sym.kind == .chan { + chan_info := right_sym.chan_info() + if chan_info.elem_type.is_ptr() && !chan_info.is_mut { + c.error('cannot have a mutable reference to object from `$right_sym.name`', + right_node.pos) + } + } + } + } + } + } + if node.left_types.len != node.left.len { + c.error('assign statement left type number mismatch', node.pos) + } +} + +fn scope_register_it(mut s ast.Scope, pos token.Position, typ ast.Type) { + s.register(ast.Var{ + name: 'it' + pos: pos + typ: typ + is_used: true + }) +} + +fn scope_register_a_b(mut s ast.Scope, pos token.Position, typ ast.Type) { + s.register(ast.Var{ + name: 'a' + pos: pos + typ: typ.to_ptr() + is_used: true + }) + s.register(ast.Var{ + name: 'b' + pos: pos + typ: typ.to_ptr() + is_used: true + }) +} + +fn (mut c Checker) check_array_init_para_type(para string, expr ast.Expr, pos token.Position) { + sym := c.table.get_type_symbol(c.expr(expr)) + if sym.kind !in [.int, .int_literal] { + c.error('array $para needs to be an int', pos) + } +} + +pub fn (mut c Checker) ensure_sumtype_array_has_default_value(array_init ast.ArrayInit) { + sym := c.table.get_type_symbol(array_init.elem_type) + if sym.kind == .sum_type && !array_init.has_default { + c.error('cannot initialize sum type array without default value', array_init.pos) + } +} + +pub fn (mut c Checker) array_init(mut array_init ast.ArrayInit) ast.Type { + // println('checker: array init $array_init.pos.line_nr $c.file.path') + mut elem_type := ast.void_type + // []string - was set in parser + if array_init.typ != ast.void_type { + if array_init.exprs.len == 0 { + if array_init.has_cap { + c.check_array_init_para_type('cap', array_init.cap_expr, array_init.pos) + } + if array_init.has_len { + c.check_array_init_para_type('len', array_init.len_expr, array_init.pos) + } + } + if array_init.has_default { + default_expr := array_init.default_expr + default_typ := c.check_expr_opt_call(default_expr, c.expr(default_expr)) + c.check_expected(default_typ, array_init.elem_type) or { + c.error(err.msg, default_expr.position()) + } + } + if array_init.has_len { + if array_init.has_len && !array_init.has_default { + elem_type_sym := c.table.get_type_symbol(array_init.elem_type) + if elem_type_sym.kind == .interface_ { + c.error('cannot instantiate an array of interfaces without also giving a default `init:` value', + array_init.len_expr.position()) + } + } + c.ensure_sumtype_array_has_default_value(array_init) + } + c.ensure_type_exists(array_init.elem_type, array_init.elem_type_pos) or {} + return array_init.typ + } + if array_init.is_fixed { + c.ensure_sumtype_array_has_default_value(array_init) + c.ensure_type_exists(array_init.elem_type, array_init.elem_type_pos) or {} + } + // a = [] + if array_init.exprs.len == 0 { + // a := fn_returing_opt_array() or { [] } + if c.expected_type == ast.void_type && c.expected_or_type != ast.void_type { + c.expected_type = c.expected_or_type + } + mut type_sym := c.table.get_type_symbol(c.expected_type) + if type_sym.kind != .array || type_sym.array_info().elem_type == ast.void_type { + c.error('array_init: no type specified (maybe: `[]Type{}` instead of `[]`)', + array_init.pos) + return ast.void_type + } + // TODO: seperate errors once bug is fixed with `x := if expr { ... } else { ... }` + // if c.expected_type == ast.void_type { + // c.error('array_init: use `[]Type{}` instead of `[]`', array_init.pos) + // return ast.void_type + // } + array_info := type_sym.array_info() + array_init.elem_type = array_info.elem_type + // clear optional flag incase of: `fn opt_arr ?[]int { return [] }` + return c.expected_type.clear_flag(.optional) + } + // [1,2,3] + if array_init.exprs.len > 0 && array_init.elem_type == ast.void_type { + mut expected_value_type := ast.void_type + mut expecting_interface_array := false + if c.expected_type != 0 { + expected_value_type = c.table.value_type(c.expected_type) + if c.table.get_type_symbol(expected_value_type).kind == .interface_ { + // Array of interfaces? (`[dog, cat]`) Save the interface type (`Animal`) + expecting_interface_array = true + } + } + // expecting_interface_array := c.expected_type != 0 && + // c.table.get_type_symbol(c.table.value_type(c.expected_type)).kind == .interface_ + // + // if expecting_interface_array { + // println('ex $c.expected_type') + // } + for i, mut expr in array_init.exprs { + typ := c.check_expr_opt_call(expr, c.expr(expr)) + array_init.expr_types << typ + // The first element's type + if expecting_interface_array { + if i == 0 { + elem_type = expected_value_type + c.expected_type = elem_type + c.type_implements(typ, elem_type, expr.position()) + } + if !typ.is_ptr() && !typ.is_pointer() && !c.inside_unsafe { + typ_sym := c.table.get_type_symbol(typ) + if typ_sym.kind != .interface_ { + c.mark_as_referenced(mut &expr, true) + } + } + continue + } + // The first element's type + if i == 0 { + if expr.is_auto_deref_var() { + elem_type = c.table.mktyp(typ.deref()) + } else { + elem_type = c.table.mktyp(typ) + } + c.expected_type = elem_type + continue + } + c.check_expected(typ, elem_type) or { + c.error('invalid array element: $err.msg', expr.position()) + } + } + if array_init.is_fixed { + idx := c.table.find_or_register_array_fixed(elem_type, array_init.exprs.len, + ast.empty_expr()) + if elem_type.has_flag(.generic) { + array_init.typ = ast.new_type(idx).set_flag(.generic) + } else { + array_init.typ = ast.new_type(idx) + } + } else { + idx := c.table.find_or_register_array(elem_type) + if elem_type.has_flag(.generic) { + array_init.typ = ast.new_type(idx).set_flag(.generic) + } else { + array_init.typ = ast.new_type(idx) + } + } + array_init.elem_type = elem_type + } else if array_init.is_fixed && array_init.exprs.len == 1 + && array_init.elem_type != ast.void_type { + // [50]byte + mut fixed_size := i64(0) + init_expr := array_init.exprs[0] + c.expr(init_expr) + match init_expr { + ast.IntegerLiteral { + fixed_size = init_expr.val.int() + } + ast.Ident { + if init_expr.obj is ast.ConstField { + if comptime_value := eval_comptime_const_expr(init_expr.obj.expr, + 0) + { + fixed_size = comptime_value.i64() or { fixed_size } + } + } else { + c.error('non-constant array bound `$init_expr.name`', init_expr.pos) + } + } + ast.InfixExpr { + if comptime_value := eval_comptime_const_expr(init_expr, 0) { + fixed_size = comptime_value.i64() or { fixed_size } + } + } + else { + c.error('expecting `int` for fixed size', array_init.pos) + } + } + if fixed_size <= 0 { + c.error('fixed size cannot be zero or negative (fixed_size: $fixed_size)', + init_expr.position()) + } + idx := c.table.find_or_register_array_fixed(array_init.elem_type, int(fixed_size), + init_expr) + if array_init.elem_type.has_flag(.generic) { + array_init.typ = ast.new_type(idx).set_flag(.generic) + } else { + array_init.typ = ast.new_type(idx) + } + if array_init.has_default { + c.expr(array_init.default_expr) + } + } + return array_init.typ +} + +[inline] +fn (mut c Checker) check_loop_label(label string, pos token.Position) { + if label.len == 0 { + // ignore + return + } + if c.loop_label.len != 0 { + c.error('nesting of labelled `for` loops is not supported', pos) + return + } + c.loop_label = label +} + +fn (mut c Checker) stmt(node ast.Stmt) { + $if trace_checker ? { + stmt_pos := node.pos + eprintln('checking file: ${c.file.path:-30} | stmt pos: ${stmt_pos.str():-45} | stmt') + } + // c.expected_type = ast.void_type + match mut node { + ast.EmptyStmt { + if c.pref.is_verbose { + eprintln('Checker.stmt() EmptyStmt') + print_backtrace() + } + } + ast.NodeError {} + ast.AsmStmt { + c.asm_stmt(mut node) + } + ast.AssertStmt { + c.assert_stmt(node) + } + ast.AssignStmt { + c.assign_stmt(mut node) + } + ast.Block { + c.block(node) + } + ast.BranchStmt { + c.branch_stmt(node) + } + ast.CompFor { + c.comp_for(node) + } + ast.ConstDecl { + c.inside_const = true + c.const_decl(mut node) + c.inside_const = false + } + ast.DeferStmt { + if node.idx_in_fn < 0 { + node.idx_in_fn = c.table.cur_fn.defer_stmts.len + c.table.cur_fn.defer_stmts << unsafe { &node } + } + if c.locked_names.len != 0 || c.rlocked_names.len != 0 { + c.error('defers are not allowed in lock statements', node.pos) + } + for i, ident in node.defer_vars { + mut id := ident + if id.info is ast.IdentVar { + if id.comptime && id.name in checker.valid_comp_not_user_defined { + node.defer_vars[i] = ast.Ident{ + scope: 0 + name: '' + } + continue + } + mut info := id.info as ast.IdentVar + typ := c.ident(mut id) + if typ == ast.error_type_idx { + continue + } + info.typ = typ + id.info = info + node.defer_vars[i] = id + } + } + c.inside_defer = true + c.stmts(node.stmts) + c.inside_defer = false + } + ast.EnumDecl { + c.enum_decl(node) + } + ast.ExprStmt { + node.typ = c.expr(node.expr) + c.expected_type = ast.void_type + mut or_typ := ast.void_type + match node.expr { + ast.IndexExpr { + if node.expr.or_expr.kind != .absent { + node.is_expr = true + or_typ = node.typ + } + } + ast.PrefixExpr { + if node.expr.or_block.kind != .absent { + node.is_expr = true + or_typ = node.typ + } + } + else {} + } + c.check_expr_opt_call(node.expr, or_typ) + // TODO This should work, even if it's prolly useless .-. + // node.typ = c.check_expr_opt_call(node.expr, ast.void_type) + } + ast.FnDecl { + c.fn_decl(mut node) + } + ast.ForCStmt { + c.for_c_stmt(node) + } + ast.ForInStmt { + c.for_in_stmt(mut node) + } + ast.ForStmt { + c.for_stmt(mut node) + } + ast.GlobalDecl { + c.global_decl(mut node) + } + ast.GotoLabel {} + ast.GotoStmt { + if c.inside_defer { + c.error('goto is not allowed in defer statements', node.pos) + } + if !c.inside_unsafe { + c.warn('`goto` requires `unsafe` (consider using labelled break/continue)', + node.pos) + } + if node.name !in c.table.cur_fn.label_names { + c.error('unknown label `$node.name`', node.pos) + } + // TODO: check label doesn't bypass variable declarations + } + ast.HashStmt { + c.hash_stmt(mut node) + } + ast.Import { + c.import_stmt(node) + } + ast.InterfaceDecl { + c.interface_decl(mut node) + } + ast.Module { + c.mod = node.name + c.is_builtin_mod = node.name in ['builtin', 'os', 'strconv'] + c.check_valid_snake_case(node.name, 'module name', node.pos) + } + ast.Return { + // c.returns = true + c.return_stmt(mut node) + c.scope_returns = true + } + ast.SqlStmt { + c.sql_stmt(mut node) + } + ast.StructDecl { + c.struct_decl(mut node) + } + ast.TypeDecl { + c.type_decl(node) + } + } +} + +fn (mut c Checker) assert_stmt(node ast.AssertStmt) { + cur_exp_typ := c.expected_type + assert_type := c.check_expr_opt_call(node.expr, c.expr(node.expr)) + if assert_type != ast.bool_type_idx { + atype_name := c.table.get_type_symbol(assert_type).name + c.error('assert can be used only with `bool` expressions, but found `$atype_name` instead', + node.pos) + } + c.expected_type = cur_exp_typ +} + +fn (mut c Checker) block(node ast.Block) { + if node.is_unsafe { + c.inside_unsafe = true + c.stmts(node.stmts) + c.inside_unsafe = false + } else { + c.stmts(node.stmts) + } +} + +fn (mut c Checker) branch_stmt(node ast.BranchStmt) { + if c.inside_defer { + c.error('`$node.kind.str()` is not allowed in defer statements', node.pos) + } + if c.in_for_count == 0 { + c.error('$node.kind.str() statement not within a loop', node.pos) + } + if node.label.len > 0 { + if node.label != c.loop_label { + c.error('invalid label name `$node.label`', node.pos) + } + } +} + +fn (mut c Checker) for_c_stmt(node ast.ForCStmt) { + c.in_for_count++ + prev_loop_label := c.loop_label + if node.has_init { + c.stmt(node.init) + } + c.expr(node.cond) + if node.has_inc { + c.stmt(node.inc) + } + c.check_loop_label(node.label, node.pos) + c.stmts(node.stmts) + c.loop_label = prev_loop_label + c.in_for_count-- +} + +fn (mut c Checker) comp_for(node ast.CompFor) { + typ := c.unwrap_generic(node.typ) + sym := c.table.get_type_symbol(typ) + if sym.kind == .placeholder || typ.has_flag(.generic) { + c.error('unknown type `$sym.name`', node.typ_pos) + } + if node.kind == .fields { + c.comptime_fields_type[node.val_var] = node.typ + } + c.stmts(node.stmts) +} + +fn (mut c Checker) for_in_stmt(mut node ast.ForInStmt) { + c.in_for_count++ + prev_loop_label := c.loop_label + typ := c.expr(node.cond) + typ_idx := typ.idx() + if node.key_var.len > 0 && node.key_var != '_' { + c.check_valid_snake_case(node.key_var, 'variable name', node.pos) + } + if node.val_var.len > 0 && node.val_var != '_' { + c.check_valid_snake_case(node.val_var, 'variable name', node.pos) + } + if node.is_range { + high_type := c.expr(node.high) + high_type_idx := high_type.idx() + if typ_idx in ast.integer_type_idxs && high_type_idx !in ast.integer_type_idxs { + c.error('range types do not match', node.cond.position()) + } else if typ_idx in ast.float_type_idxs || high_type_idx in ast.float_type_idxs { + c.error('range type can not be float', node.cond.position()) + } else if typ_idx == ast.bool_type_idx || high_type_idx == ast.bool_type_idx { + c.error('range type can not be bool', node.cond.position()) + } else if typ_idx == ast.string_type_idx || high_type_idx == ast.string_type_idx { + c.error('range type can not be string', node.cond.position()) + } + if high_type in [ast.int_type, ast.int_literal_type] { + node.val_type = typ + } else { + node.val_type = high_type + } + node.scope.update_var_type(node.val_var, node.val_type) + } else { + sym := c.table.get_final_type_symbol(typ) + if sym.kind == .struct_ { + // iterators + next_fn := sym.find_method('next') or { + c.error('a struct must have a `next()` method to be an iterator', node.cond.position()) + return + } + if !next_fn.return_type.has_flag(.optional) { + c.error('iterator method `next()` must return an optional', node.cond.position()) + } + // the receiver + if next_fn.params.len != 1 { + c.error('iterator method `next()` must have 0 parameters', node.cond.position()) + } + val_type := next_fn.return_type.clear_flag(.optional) + node.cond_type = typ + node.kind = sym.kind + node.val_type = val_type + node.scope.update_var_type(node.val_var, val_type) + } else { + if sym.kind == .map && !(node.key_var.len > 0 && node.val_var.len > 0) { + c.error( + 'declare a key and a value variable when ranging a map: `for key, val in map {`\n' + + 'use `_` if you do not need the variable', node.pos) + } + if node.key_var.len > 0 { + key_type := match sym.kind { + .map { sym.map_info().key_type } + else { ast.int_type } + } + node.key_type = key_type + node.scope.update_var_type(node.key_var, key_type) + } + mut value_type := c.table.value_type(typ) + if value_type == ast.void_type || typ.has_flag(.optional) { + if typ != ast.void_type { + c.error('for in: cannot index `${c.table.type_to_str(typ)}`', node.cond.position()) + } + } + if node.val_is_mut { + value_type = value_type.to_ptr() + match node.cond { + ast.Ident { + if node.cond.obj is ast.Var { + obj := node.cond.obj as ast.Var + if !obj.is_mut { + c.error('`$obj.name` is immutable, it cannot be changed', + node.cond.pos) + } + } + } + ast.ArrayInit { + c.error('array literal is immutable, it cannot be changed', node.cond.pos) + } + ast.MapInit { + c.error('map literal is immutable, it cannot be changed', node.cond.pos) + } + else {} + } + } + node.cond_type = typ + node.kind = sym.kind + node.val_type = value_type + node.scope.update_var_type(node.val_var, value_type) + } + } + c.check_loop_label(node.label, node.pos) + c.stmts(node.stmts) + c.loop_label = prev_loop_label + c.in_for_count-- +} + +fn (mut c Checker) for_stmt(mut node ast.ForStmt) { + c.in_for_count++ + prev_loop_label := c.loop_label + c.expected_type = ast.bool_type + typ := c.expr(node.cond) + if !node.is_inf && typ.idx() != ast.bool_type_idx && !c.pref.translated { + c.error('non-bool used as for condition', node.pos) + } + if node.cond is ast.InfixExpr { + infix := node.cond + if infix.op == .key_is { + if (infix.left is ast.Ident || infix.left is ast.SelectorExpr) + && infix.right is ast.TypeNode { + is_variable := if mut infix.left is ast.Ident { + infix.left.kind == .variable + } else { + true + } + left_type := c.expr(infix.left) + left_sym := c.table.get_type_symbol(left_type) + if is_variable { + if left_sym.kind in [.sum_type, .interface_] { + c.smartcast(infix.left, infix.left_type, infix.right.typ, mut + node.scope) + } + } + } + } + } + // TODO: update loop var type + // how does this work currenly? + c.check_loop_label(node.label, node.pos) + c.stmts(node.stmts) + c.loop_label = prev_loop_label + c.in_for_count-- +} + +fn (mut c Checker) global_decl(mut node ast.GlobalDecl) { + for mut field in node.fields { + c.check_valid_snake_case(field.name, 'global name', field.pos) + if field.name in c.global_names { + c.error('duplicate global `$field.name`', field.pos) + } + sym := c.table.get_type_symbol(field.typ) + if sym.kind == .placeholder { + c.error('unknown type `$sym.name`', field.typ_pos) + } + if field.has_expr { + field.typ = c.expr(field.expr) + mut v := c.file.global_scope.find_global(field.name) or { + panic('internal compiler error - could not find global in scope') + } + v.typ = c.table.mktyp(field.typ) + } + c.global_names << field.name + } +} + +fn (mut c Checker) go_expr(mut node ast.GoExpr) ast.Type { + ret_type := c.call_expr(mut node.call_expr) + if node.call_expr.or_block.kind != .absent { + c.error('optional handling cannot be done in `go` call. Do it when calling `.wait()`', + node.call_expr.or_block.pos) + } + // Make sure there are no mutable arguments + for arg in node.call_expr.args { + if arg.is_mut && !arg.typ.is_ptr() { + c.error('function in `go` statement cannot contain mutable non-reference arguments', + arg.expr.position()) + } + } + if node.call_expr.is_method && node.call_expr.receiver_type.is_ptr() + && !node.call_expr.left_type.is_ptr() { + c.error('method in `go` statement cannot have non-reference mutable receiver', + node.call_expr.left.position()) + } + return c.table.find_or_register_thread(ret_type) +} + +fn (mut c Checker) asm_stmt(mut stmt ast.AsmStmt) { + if stmt.is_goto { + c.warn('inline assembly goto is not supported, it will most likely not work', + stmt.pos) + } + if c.pref.backend.is_js() { + c.error('inline assembly is not supported in the js backend', stmt.pos) + } + if c.pref.backend == .c && c.pref.ccompiler_type == .msvc { + c.error('msvc compiler does not support inline assembly', stmt.pos) + } + mut aliases := c.asm_ios(stmt.output, mut stmt.scope, true) + aliases2 := c.asm_ios(stmt.input, mut stmt.scope, false) + aliases << aliases2 + for template in stmt.templates { + if template.is_directive { + /* + align n[,value] + .skip n[,value] + .space n[,value] + .byte value1[,...] + .word value1[,...] + .short value1[,...] + .int value1[,...] + .long value1[,...] + .quad immediate_value1[,...] + .globl symbol + .global symbol + .section section + .text + .data + .bss + .fill repeat[,size[,value]] + .org n + .previous + .string string[,...] + .asciz string[,...] + .ascii string[,...] + */ + if template.name !in ['skip', 'space', 'byte', 'word', 'short', 'int', 'long', 'quad', + 'globl', 'global', 'section', 'text', 'data', 'bss', 'fill', 'org', 'previous', + 'string', 'asciz', 'ascii'] { // all tcc-supported assembler directives + c.error('unknown assembler directive: `$template.name`', template.pos) + } + } + for mut arg in template.args { + c.asm_arg(arg, stmt, aliases) + } + } + for mut clob in stmt.clobbered { + c.asm_arg(clob.reg, stmt, aliases) + } +} + +fn (mut c Checker) asm_arg(arg ast.AsmArg, stmt ast.AsmStmt, aliases []string) { + match mut arg { + ast.AsmAlias {} + ast.AsmAddressing { + if arg.scale !in [-1, 1, 2, 4, 8] { + c.error('scale must be one of 1, 2, 4, or 8', arg.pos) + } + c.asm_arg(arg.displacement, stmt, aliases) + c.asm_arg(arg.base, stmt, aliases) + c.asm_arg(arg.index, stmt, aliases) + } + ast.BoolLiteral {} // all of these are guarented to be correct. + ast.FloatLiteral {} + ast.CharLiteral {} + ast.IntegerLiteral {} + ast.AsmRegister {} // if the register is not found, the parser will register it as an alias + ast.AsmDisp {} + string {} + } +} + +fn (mut c Checker) asm_ios(ios []ast.AsmIO, mut scope ast.Scope, output bool) []string { + mut aliases := []string{} + for io in ios { + typ := c.expr(io.expr) + if output { + c.fail_if_immutable(io.expr) + } + if io.alias != '' { + aliases << io.alias + if io.alias in scope.objects { + scope.objects[io.alias] = ast.Var{ + name: io.alias + expr: io.expr + is_arg: true + typ: typ + orig_type: typ + pos: io.pos + } + } + } + } + + return aliases +} + +fn (mut c Checker) hash_stmt(mut node ast.HashStmt) { + if c.skip_flags { + return + } + if c.ct_cond_stack.len > 0 { + node.ct_conds = c.ct_cond_stack.clone() + } + if c.pref.backend.is_js() { + if !c.file.path.ends_with('.js.v') { + c.error('hash statements are only allowed in backend specific files such "x.js.v"', + node.pos) + } + if c.mod == 'main' { + c.error('hash statements are not allowed in the main module. Place them in a separate module.', + node.pos) + } + return + } + match node.kind { + 'include' { + mut flag := node.main + if flag.contains('@VROOT') { + // c.note(checker.vroot_is_deprecated_message, node.pos) + vroot := util.resolve_vmodroot(flag.replace('@VROOT', '@VMODROOT'), c.file.path) or { + c.error(err.msg, node.pos) + return + } + node.val = 'include $vroot' + node.main = vroot + flag = vroot + } + if flag.contains('@VEXEROOT') { + vroot := flag.replace('@VEXEROOT', os.dir(pref.vexe_path())) + node.val = 'include $vroot' + node.main = vroot + flag = vroot + } + if flag.contains('@VMODROOT') { + vroot := util.resolve_vmodroot(flag, c.file.path) or { + c.error(err.msg, node.pos) + return + } + node.val = 'include $vroot' + node.main = vroot + flag = vroot + } + if flag.contains('\$env(') { + env := util.resolve_env_value(flag, true) or { + c.error(err.msg, node.pos) + return + } + node.main = env + } + flag_no_comment := flag.all_before('//').trim_space() + if !((flag_no_comment.starts_with('"') && flag_no_comment.ends_with('"')) + || (flag_no_comment.starts_with('<') && flag_no_comment.ends_with('>'))) { + c.error('including C files should use either `"header_file.h"` or `` quoting', + node.pos) + } + } + 'pkgconfig' { + args := if node.main.contains('--') { + node.main.split(' ') + } else { + '--cflags --libs $node.main'.split(' ') + } + mut m := pkgconfig.main(args) or { + c.error(err.msg, node.pos) + return + } + cflags := m.run() or { + c.error(err.msg, node.pos) + return + } + c.table.parse_cflag(cflags, c.mod, c.pref.compile_defines_all) or { + c.error(err.msg, node.pos) + return + } + } + 'flag' { + // #flag linux -lm + mut flag := node.main + if flag.contains('@VROOT') { + // c.note(checker.vroot_is_deprecated_message, node.pos) + flag = util.resolve_vmodroot(flag.replace('@VROOT', '@VMODROOT'), c.file.path) or { + c.error(err.msg, node.pos) + return + } + } + if flag.contains('@VEXEROOT') { + // expand `@VEXEROOT` to its absolute path + flag = flag.replace('@VEXEROOT', os.dir(pref.vexe_path())) + } + if flag.contains('@VMODROOT') { + flag = util.resolve_vmodroot(flag, c.file.path) or { + c.error(err.msg, node.pos) + return + } + } + if flag.contains('\$env(') { + flag = util.resolve_env_value(flag, true) or { + c.error(err.msg, node.pos) + return + } + } + for deprecated in ['@VMOD', '@VMODULE', '@VPATH', '@VLIB_PATH'] { + if flag.contains(deprecated) { + if !flag.contains('@VMODROOT') { + c.error('$deprecated had been deprecated, use @VMODROOT instead.', + node.pos) + } + } + } + // println('adding flag "$flag"') + c.table.parse_cflag(flag, c.mod, c.pref.compile_defines_all) or { + c.error(err.msg, node.pos) + } + } + else { + if node.kind != 'define' { + c.error('expected `#define`, `#flag`, `#include` or `#pkgconfig` not $node.val', + node.pos) + } + } + } +} + +fn (mut c Checker) import_stmt(imp ast.Import) { + c.check_valid_snake_case(imp.alias, 'module alias', imp.pos) + for sym in imp.syms { + name := '${imp.mod}.$sym.name' + if sym.name[0].is_capital() { + if type_sym := c.table.find_type(name) { + if type_sym.kind != .placeholder { + if !type_sym.is_public { + c.error('module `$imp.mod` type `$sym.name` is private', sym.pos) + } + continue + } + } + c.error('module `$imp.mod` has no type `$sym.name`', sym.pos) + continue + } + if func := c.table.find_fn(name) { + if !func.is_pub { + c.error('module `$imp.mod` function `${sym.name}()` is private', sym.pos) + } + continue + } + if _ := c.file.global_scope.find_const(name) { + continue + } + c.error('module `$imp.mod` has no constant or function `$sym.name`', sym.pos) + } +} + +fn (mut c Checker) stmts(stmts []ast.Stmt) { + mut unreachable := token.Position{ + line_nr: -1 + } + c.expected_type = ast.void_type + for stmt in stmts { + if c.scope_returns { + if unreachable.line_nr == -1 { + unreachable = stmt.pos + } + } + c.stmt(stmt) + } + if unreachable.line_nr >= 0 { + c.error('unreachable code', unreachable) + } + c.find_unreachable_statements_after_noreturn_calls(stmts) + c.scope_returns = false + c.expected_type = ast.void_type +} + +pub fn (mut c Checker) find_unreachable_statements_after_noreturn_calls(stmts []ast.Stmt) { + mut prev_stmt_was_noreturn_call := false + for stmt in stmts { + match stmt { + ast.ExprStmt { + if stmt.expr is ast.CallExpr { + if prev_stmt_was_noreturn_call { + c.error('unreachable code after a [noreturn] call', stmt.pos) + return + } + prev_stmt_was_noreturn_call = stmt.expr.is_noreturn + } + } + else { + prev_stmt_was_noreturn_call = false + } + } + } +} + +pub fn (mut c Checker) unwrap_generic(typ ast.Type) ast.Type { + if typ.has_flag(.generic) { + if t_typ := c.table.resolve_generic_to_concrete(typ, c.table.cur_fn.generic_names, + c.table.cur_concrete_types) + { + return t_typ + } + } + return typ +} + +// TODO node must be mut +pub fn (mut c Checker) expr(node ast.Expr) ast.Type { + c.expr_level++ + defer { + c.expr_level-- + } + // c.expr_level set to 150 so that stack overflow does not occur on windows + if c.expr_level > 150 { + c.error('checker: too many expr levels: $c.expr_level ', node.position()) + return ast.void_type + } + match mut node { + ast.NodeError {} + ast.EmptyExpr { + c.error('checker.expr(): unhandled EmptyExpr', token.Position{}) + } + ast.CTempVar { + return node.typ + } + ast.AnonFn { + c.inside_anon_fn = true + keep_fn := c.table.cur_fn + c.table.cur_fn = unsafe { &node.decl } + c.stmts(node.decl.stmts) + c.fn_decl(mut node.decl) + c.table.cur_fn = keep_fn + c.inside_anon_fn = false + return node.typ + } + ast.ArrayDecompose { + typ := c.expr(node.expr) + type_sym := c.table.get_type_symbol(typ) + if type_sym.kind != .array { + c.error('decomposition can only be used on arrays', node.expr.position()) + return ast.void_type + } + array_info := type_sym.info as ast.Array + elem_type := array_info.elem_type.set_flag(.variadic) + node.expr_type = typ + node.arg_type = elem_type + return elem_type + } + ast.ArrayInit { + return c.array_init(mut node) + } + ast.AsCast { + node.expr_type = c.expr(node.expr) + expr_type_sym := c.table.get_type_symbol(node.expr_type) + type_sym := c.table.get_type_symbol(node.typ) + if expr_type_sym.kind == .sum_type { + c.ensure_type_exists(node.typ, node.pos) or {} + if !c.table.sumtype_has_variant(node.expr_type, node.typ) { + c.error('cannot cast `$expr_type_sym.name` to `$type_sym.name`', node.pos) + } + } else if node.expr_type != node.typ { + mut s := 'cannot cast non-sum type `$expr_type_sym.name` using `as`' + if type_sym.kind == .sum_type { + s += ' - use e.g. `${type_sym.name}(some_expr)` instead.' + } + c.error(s, node.pos) + } + return node.typ + } + ast.Assoc { + v := node.scope.find_var(node.var_name) or { panic(err) } + for i, _ in node.fields { + c.expr(node.exprs[i]) + } + node.typ = v.typ + return v.typ + } + ast.BoolLiteral { + return ast.bool_type + } + ast.CastExpr { + return c.cast_expr(mut node) + } + ast.CallExpr { + mut ret_type := c.call_expr(mut node) + if !ret_type.has_flag(.optional) { + if node.or_block.kind == .block { + c.error('unexpected `or` block, the function `$node.name` does not return an optional', + node.or_block.pos) + } else if node.or_block.kind == .propagate { + c.error('unexpected `?`, the function `$node.name` does not return an optional', + node.or_block.pos) + } + } + if ret_type.has_flag(.optional) && node.or_block.kind != .absent { + ret_type = ret_type.clear_flag(.optional) + } + return ret_type + } + ast.ChanInit { + return c.chan_init(mut node) + } + ast.CharLiteral { + // return int_literal, not rune, so that we can do "bytes << `A`" without a cast etc + // return ast.int_literal_type + return ast.rune_type + // return ast.byte_type + } + ast.Comment { + return ast.void_type + } + ast.AtExpr { + return c.at_expr(mut node) + } + ast.ComptimeCall { + return c.comptime_call(mut node) + } + ast.ComptimeSelector { + node.left_type = c.unwrap_generic(c.expr(node.left)) + expr_type := c.unwrap_generic(c.expr(node.field_expr)) + expr_sym := c.table.get_type_symbol(expr_type) + if expr_type != ast.string_type { + c.error('expected `string` instead of `$expr_sym.name` (e.g. `field.name`)', + node.field_expr.position()) + } + if node.field_expr is ast.SelectorExpr { + left_pos := node.field_expr.expr.position() + if c.comptime_fields_type.len == 0 { + c.error('compile time field access can only be used when iterating over `T.fields`', + left_pos) + } + expr_name := node.field_expr.expr.str() + if expr_name in c.comptime_fields_type { + return c.comptime_fields_type[expr_name] + } + c.error('unknown `\$for` variable `$expr_name`', left_pos) + } else { + c.error('expected selector expression e.g. `$(field.name)`', node.field_expr.position()) + } + return ast.void_type + } + ast.ConcatExpr { + return c.concat_expr(mut node) + } + ast.DumpExpr { + node.expr_type = c.expr(node.expr) + if node.expr_type.idx() == ast.void_type_idx { + c.error('dump expression can not be void', node.expr.position()) + return ast.void_type + } + tsym := c.table.get_type_symbol(node.expr_type) + c.table.dumps[int(node.expr_type)] = tsym.cname + node.cname = tsym.cname + return node.expr_type + } + ast.EnumVal { + return c.enum_val(mut node) + } + ast.FloatLiteral { + return ast.float_literal_type + } + ast.GoExpr { + return c.go_expr(mut node) + } + ast.Ident { + // c.checked_ident = node.name + res := c.ident(mut node) + // c.checked_ident = '' + return res + } + ast.IfExpr { + return c.if_expr(mut node) + } + ast.IfGuardExpr { + node.expr_type = c.expr(node.expr) + if !node.expr_type.has_flag(.optional) { + mut no_opt := true + match mut node.expr { + ast.IndexExpr { + no_opt = false + node.expr_type = node.expr_type.set_flag(.optional) + node.expr.is_option = true + } + ast.PrefixExpr { + if node.expr.op == .arrow { + no_opt = false + node.expr_type = node.expr_type.set_flag(.optional) + node.expr.is_option = true + } + } + else {} + } + if no_opt { + c.error('expression should return an option', node.expr.position()) + } + } + return ast.bool_type + } + ast.IndexExpr { + return c.index_expr(mut node) + } + ast.InfixExpr { + return c.infix_expr(mut node) + } + ast.IntegerLiteral { + return c.int_lit(mut node) + } + ast.LockExpr { + return c.lock_expr(mut node) + } + ast.MapInit { + return c.map_init(mut node) + } + ast.MatchExpr { + return c.match_expr(mut node) + } + ast.PostfixExpr { + return c.postfix_expr(mut node) + } + ast.PrefixExpr { + return c.prefix_expr(mut node) + } + ast.None { + return ast.none_type + } + ast.OrExpr { + // never happens + return ast.void_type + } + // ast.OrExpr2 { + // return node.typ + // } + ast.ParExpr { + return c.expr(node.expr) + } + ast.RangeExpr { + // never happens + return ast.void_type + } + ast.SelectExpr { + return c.select_expr(mut node) + } + ast.SelectorExpr { + return c.selector_expr(mut node) + } + ast.SizeOf { + if !node.is_type { + node.typ = c.expr(node.expr) + } + return ast.u32_type + } + ast.IsRefType { + if !node.is_type { + node.typ = c.expr(node.expr) + } + return ast.bool_type + } + ast.OffsetOf { + return c.offset_of(node) + } + ast.SqlExpr { + return c.sql_expr(mut node) + } + ast.StringLiteral { + if node.language == .c { + // string literal starts with "c": `C.printf(c'hello')` + return ast.byte_type.set_nr_muls(1) + } + return c.string_lit(mut node) + } + ast.StringInterLiteral { + return c.string_inter_lit(mut node) + } + ast.StructInit { + if node.unresolved { + return c.expr(ast.resolve_init(node, c.unwrap_generic(node.typ), c.table)) + } + return c.struct_init(mut node) + } + ast.TypeNode { + return node.typ + } + ast.TypeOf { + node.expr_type = c.expr(node.expr) + return ast.string_type + } + ast.UnsafeExpr { + return c.unsafe_expr(mut node) + } + ast.Likely { + ltype := c.expr(node.expr) + if !c.check_types(ltype, ast.bool_type) { + ltype_sym := c.table.get_type_symbol(ltype) + lname := if node.is_likely { '_likely_' } else { '_unlikely_' } + c.error('`${lname}()` expects a boolean expression, instead it got `$ltype_sym.name`', + node.pos) + } + return ast.bool_type + } + } + return ast.void_type +} + +// pub fn (mut c Checker) asm_reg(mut node ast.AsmRegister) ast.Type { +// name := node.name + +// for bit_size, array in ast.x86_no_number_register_list { +// if name in array { +// return c.table.bitsize_to_type(bit_size) +// } +// } +// for bit_size, array in ast.x86_with_number_register_list { +// if name in array { +// return c.table.bitsize_to_type(bit_size) +// } +// } +// c.error('invalid register name: `$name`', node.pos) +// return ast.void_type +// } + +pub fn (mut c Checker) cast_expr(mut node ast.CastExpr) ast.Type { + node.expr_type = c.expr(node.expr) // type to be casted + from_type_sym := c.table.get_type_symbol(node.expr_type) + to_type_sym := c.table.get_type_symbol(node.typ) // type to be used as cast + if to_type_sym.language != .c { + c.ensure_type_exists(node.typ, node.pos) or {} + } + if from_type_sym.kind == .byte && node.expr_type.is_ptr() && to_type_sym.kind == .string + && !node.typ.is_ptr() { + c.error('to convert a C string buffer pointer to a V string, use x.vstring() instead of string(x)', + node.pos) + } + if node.expr_type == ast.void_type { + c.error('expression does not return a value so it cannot be cast', node.expr.position()) + } + if node.expr_type == ast.byte_type && to_type_sym.kind == .string { + c.error('can not cast type `byte` to string, use `${node.expr.str()}.str()` instead.', + node.pos) + } + if to_type_sym.kind == .sum_type { + if node.expr_type in [ast.int_literal_type, ast.float_literal_type] { + node.expr_type = c.promote_num(node.expr_type, if node.expr_type == ast.int_literal_type { + ast.int_type + } else { + ast.f64_type + }) + } + if !c.table.sumtype_has_variant(node.typ, node.expr_type) && !node.typ.has_flag(.optional) { + c.error('cannot cast `$from_type_sym.name` to `$to_type_sym.name`', node.pos) + } + } else if mut to_type_sym.info is ast.Alias { + if !c.check_types(node.expr_type, to_type_sym.info.parent_type) { + parent_type_sym := c.table.get_type_symbol(to_type_sym.info.parent_type) + c.error('cannot convert type `$from_type_sym.name` to `$to_type_sym.name` (alias to `$parent_type_sym.name`)', + node.pos) + } + } else if node.typ == ast.string_type + && (from_type_sym.kind in [.int_literal, .int, .byte, .byteptr, .bool] + || (from_type_sym.kind == .array && from_type_sym.name == 'array_byte')) { + type_name := c.table.type_to_str(node.expr_type) + c.error('cannot cast type `$type_name` to string, use `x.str()` instead', node.pos) + } else if node.expr_type == ast.string_type { + if to_type_sym.kind != .alias { + mut error_msg := 'cannot cast a string' + if mut node.expr is ast.StringLiteral { + if node.expr.val.len == 1 { + error_msg += ", for denoting characters use `$node.expr.val` instead of '$node.expr.val'" + } + } + c.error(error_msg, node.pos) + } + } else if to_type_sym.kind == .byte && node.expr_type != ast.voidptr_type + && from_type_sym.kind != .enum_ && !node.expr_type.is_int() && !node.expr_type.is_float() + && node.expr_type != ast.bool_type && !node.expr_type.is_ptr() { + type_name := c.table.type_to_str(node.expr_type) + c.error('cannot cast type `$type_name` to `byte`', node.pos) + } else if to_type_sym.kind == .struct_ && !node.typ.is_ptr() + && !(to_type_sym.info as ast.Struct).is_typedef { + // For now we ignore C typedef because of `C.Window(C.None)` in vlib/clipboard + if from_type_sym.kind == .struct_ && !node.expr_type.is_ptr() { + c.warn('casting to struct is deprecated, use e.g. `Struct{...expr}` instead', + node.pos) + from_type_info := from_type_sym.info as ast.Struct + to_type_info := to_type_sym.info as ast.Struct + if !c.check_struct_signature(from_type_info, to_type_info) { + c.error('cannot convert struct `$from_type_sym.name` to struct `$to_type_sym.name`', + node.pos) + } + } else { + type_name := c.table.type_to_str(node.expr_type) + // dump(node.typ) + // dump(node.expr_type) + // dump(type_name) + // dump(to_type_sym.debug()) + c.error('cannot cast `$type_name` to struct', node.pos) + } + } else if to_type_sym.kind == .interface_ { + if c.type_implements(node.expr_type, node.typ, node.pos) { + if !node.expr_type.is_ptr() && !node.expr_type.is_pointer() + && from_type_sym.kind != .interface_ && !c.inside_unsafe { + c.mark_as_referenced(mut &node.expr, true) + } + } + } else if node.typ == ast.bool_type && node.expr_type != ast.bool_type && !c.inside_unsafe { + c.error('cannot cast to bool - use e.g. `some_int != 0` instead', node.pos) + } else if node.expr_type == ast.none_type && !node.typ.has_flag(.optional) { + type_name := c.table.type_to_str(node.typ) + c.error('cannot cast `none` to `$type_name`', node.pos) + } else if from_type_sym.kind == .struct_ && !node.expr_type.is_ptr() { + if (node.typ.is_ptr() || to_type_sym.kind !in [.sum_type, .interface_]) && !c.is_builtin_mod { + type_name := c.table.type_to_str(node.typ) + c.error('cannot cast struct to `$type_name`', node.pos) + } + } else if node.expr_type.has_flag(.optional) || node.expr_type.has_flag(.variadic) { + // variadic case can happen when arrays are converted into variadic + msg := if node.expr_type.has_flag(.optional) { 'an optional' } else { 'a variadic' } + c.error('cannot type cast $msg', node.pos) + } else if !c.inside_unsafe && node.typ.is_ptr() && node.expr_type.is_ptr() + && node.typ.deref() != ast.char_type && node.expr_type.deref() != ast.char_type { + ft := c.table.type_to_str(node.expr_type) + tt := c.table.type_to_str(node.typ) + c.warn('casting `$ft` to `$tt` is only allowed in `unsafe` code', node.pos) + } else if from_type_sym.kind == .array_fixed && !node.expr_type.is_ptr() { + c.warn('cannot cast a fixed array (use e.g. `&arr[0]` instead)', node.pos) + } + if node.has_arg { + c.expr(node.arg) + } + + // checks on int literal to enum cast if the value represents a value on the enum + if to_type_sym.kind == .enum_ { + if node.expr is ast.IntegerLiteral { + enum_typ_name := c.table.get_type_name(node.typ) + node_val := (node.expr as ast.IntegerLiteral).val.int() + + if enum_decl := c.table.enum_decls[to_type_sym.name] { + mut in_range := false + mut enum_val := 0 + + for enum_field in enum_decl.fields { + // check if the field of the enum value is an integer literal + if enum_field.expr is ast.IntegerLiteral { + enum_val = enum_field.expr.val.int() + } + + if node_val == enum_val { + in_range = true + break + } + + enum_val += 1 + } + + if !in_range { + c.warn('$node_val does not represents a value of enum $enum_typ_name', + node.pos) + } + } + } + } + + node.typname = c.table.get_type_symbol(node.typ).name + + return node.typ +} + +fn (mut c Checker) comptime_call(mut node ast.ComptimeCall) ast.Type { + node.sym = c.table.get_type_symbol(c.unwrap_generic(c.expr(node.left))) + if node.is_env { + env_value := util.resolve_env_value("\$env('$node.args_var')", false) or { + c.error(err.msg, node.env_pos) + return ast.string_type + } + node.env_value = env_value + return ast.string_type + } + if node.is_embed { + c.file.embedded_files << node.embed_file + return c.table.find_type_idx('v.embed_file.EmbedFileData') + } + if node.is_vweb { + // TODO assoc parser bug + pref_ := *c.pref + pref2 := &pref.Preferences{ + ...pref_ + is_vweb: true + } + mut c2 := new_checker(c.table, pref2) + c2.check(node.vweb_tmpl) + mut i := 0 // tmp counter var for skipping first three tmpl vars + for k, _ in c2.file.scope.children[0].objects { + if i < 2 { + // Skip first three because they are tmpl vars see vlib/vweb/tmpl/tmpl.v + i++ + continue + } + if k in c.fn_scope.objects && c.fn_scope.objects[k] is ast.Var { + mut vsc := c.fn_scope.objects[k] as ast.Var + vsc.is_used = true + c.fn_scope.objects[k] = vsc + } + } + c.warnings << c2.warnings + c.errors << c2.errors + c.notices << c2.notices + c.nr_warnings += c2.nr_warnings + c.nr_errors += c2.nr_errors + c.nr_notices += c2.nr_notices + } + if node.method_name == 'html' { + rtyp := c.table.find_type_idx('vweb.Result') + node.result_type = rtyp + return rtyp + } + if node.method_name == 'method' { + for i, arg in node.args { + // check each arg expression + node.args[i].typ = c.expr(arg.expr) + } + // assume string for now + return ast.string_type + } + if node.is_vweb { + return ast.string_type + } + // s.$my_str() + v := node.scope.find_var(node.method_name) or { + c.error('unknown identifier `$node.method_name`', node.method_pos) + return ast.void_type + } + if v.typ != ast.string_type { + s := c.expected_msg(v.typ, ast.string_type) + c.error('invalid string method call: $s', node.method_pos) + return ast.void_type + } + // note: we should use a compile-time evaluation function rather than handle here + // mut variables will not work after init + mut method_name := '' + if v.expr is ast.StringLiteral { + method_name = v.expr.val + } else { + c.error('todo: not a string literal', node.method_pos) + } + f := node.sym.find_method(method_name) or { + c.error('could not find method `$method_name`', node.method_pos) + return ast.void_type + } + // println(f.name + ' ' + c.table.type_to_str(f.return_type)) + node.result_type = f.return_type + return f.return_type +} + +fn (mut c Checker) at_expr(mut node ast.AtExpr) ast.Type { + match node.kind { + .fn_name { + node.val = c.table.cur_fn.name.all_after_last('.') + } + .method_name { + fname := c.table.cur_fn.name.all_after_last('.') + if c.table.cur_fn.is_method { + node.val = c.table.type_to_str(c.table.cur_fn.receiver.typ).all_after_last('.') + + '.' + fname + } else { + node.val = fname + } + } + .mod_name { + node.val = c.table.cur_fn.mod + } + .struct_name { + if c.table.cur_fn.is_method { + node.val = c.table.type_to_str(c.table.cur_fn.receiver.typ).all_after_last('.') + } else { + node.val = '' + } + } + .vexe_path { + node.val = pref.vexe_path() + } + .file_path { + node.val = os.real_path(c.file.path) + } + .line_nr { + node.val = (node.pos.line_nr + 1).str() + } + .column_nr { + node.val = (node.pos.col + 1).str() + } + .vhash { + node.val = version.vhash() + } + .vmod_file { + // cache the vmod content, do not read it many times + if c.vmod_file_content.len == 0 { + mut mcache := vmod.get_cache() + vmod_file_location := mcache.get_by_file(c.file.path) + if vmod_file_location.vmod_file.len == 0 { + c.error('@VMOD_FILE can be used only in projects, that have v.mod file', + node.pos) + } + vmod_content := os.read_file(vmod_file_location.vmod_file) or { '' } + c.vmod_file_content = vmod_content.replace('\r\n', '\n') // normalise EOLs just in case + } + node.val = c.vmod_file_content + } + .vroot_path { + node.val = os.dir(pref.vexe_path()) + } + .vexeroot_path { + node.val = os.dir(pref.vexe_path()) + } + .vmodroot_path { + mut mcache := vmod.get_cache() + vmod_file_location := mcache.get_by_file(c.file.path) + node.val = os.dir(vmod_file_location.vmod_file) + } + .unknown { + c.error('unknown @ identifier: ${node.name}. Available identifiers: $token.valid_at_tokens', + node.pos) + } + } + return ast.string_type +} + +pub fn (mut c Checker) ident(mut ident ast.Ident) ast.Type { + // TODO: move this + if c.const_deps.len > 0 { + mut name := ident.name + if !name.contains('.') && ident.mod != 'builtin' { + name = '${ident.mod}.$ident.name' + } + if name == c.const_decl { + c.error('cycle in constant `$c.const_decl`', ident.pos) + return ast.void_type + } + c.const_deps << name + } + if ident.kind == .blank_ident { + if ident.tok_kind !in [.assign, .decl_assign] { + c.error('undefined ident: `_` (may only be used in assignments)', ident.pos) + } + return ast.void_type + } + // second use + if ident.kind in [.constant, .global, .variable] { + info := ident.info as ast.IdentVar + // Got a var with type T, return current generic type + return info.typ + } else if ident.kind == .function { + info := ident.info as ast.IdentFn + return info.typ + } else if ident.kind == .unresolved { + // first use + if ident.tok_kind == .assign && ident.is_mut { + c.error('`mut` not allowed with `=` (use `:=` to declare a variable)', ident.pos) + } + if obj := ident.scope.find(ident.name) { + match mut obj { + ast.GlobalField { + ident.kind = .global + ident.info = ast.IdentVar{ + typ: obj.typ + } + ident.obj = obj + return obj.typ + } + ast.Var { + // incase var was not marked as used yet (vweb tmpl) + // obj.is_used = true + if ident.pos.pos < obj.pos.pos { + c.error('undefined variable `$ident.name` (used before declaration)', + ident.pos) + } + is_sum_type_cast := obj.smartcasts.len != 0 + && !c.prevent_sum_type_unwrapping_once + c.prevent_sum_type_unwrapping_once = false + mut typ := if is_sum_type_cast { obj.smartcasts.last() } else { obj.typ } + if typ == 0 { + if mut obj.expr is ast.Ident { + if obj.expr.kind == .unresolved { + c.error('unresolved variable: `$ident.name`', ident.pos) + return ast.void_type + } + } + if mut obj.expr is ast.IfGuardExpr { + // new variable from if guard shouldn't have the optional flag for further use + // a temp variable will be generated which unwraps it + if_guard_var_type := c.expr(obj.expr.expr) + typ = if_guard_var_type.clear_flag(.optional) + } else { + typ = c.expr(obj.expr) + } + } + is_optional := typ.has_flag(.optional) + ident.kind = .variable + ident.info = ast.IdentVar{ + typ: typ + is_optional: is_optional + } + if typ == ast.error_type && c.expected_type == ast.string_type + && !c.using_new_err_struct && !c.inside_selector_expr + && !c.inside_println_arg && !c.file.mod.name.contains('v.') + && !c.is_builtin_mod { + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ <- TODO: remove; this prevents a failure in the `performance-regressions` CI job + c.warn('string errors are deprecated; use `err.msg` instead', + ident.pos) + } + // if typ == ast.t_type { + // sym := c.table.get_type_symbol(c.cur_generic_type) + // println('IDENT T unresolved $ident.name typ=$sym.name') + // Got a var with type T, return current generic type + // typ = c.cur_generic_type + // } + // } else { + if !is_sum_type_cast { + obj.typ = typ + } + ident.obj = obj + // unwrap optional (`println(x)`) + if is_optional { + return typ.clear_flag(.optional) + } + return typ + } + else {} + } + } + mut name := ident.name + // check for imported symbol + if name in c.file.imported_symbols { + name = c.file.imported_symbols[name] + } + // prepend mod to look for fn call or const + else if !name.contains('.') && ident.mod != 'builtin' { + name = '${ident.mod}.$ident.name' + } + if obj := c.file.global_scope.find(name) { + match mut obj { + ast.ConstField { + if !(obj.is_pub || obj.mod == c.mod || c.pref.is_test) { + c.error('constant `$obj.name` is private', ident.pos) + } + mut typ := obj.typ + if typ == 0 { + c.inside_const = true + typ = c.expr(obj.expr) + c.inside_const = false + if obj.expr is ast.CallExpr { + if obj.expr.or_block.kind != .absent { + typ = typ.clear_flag(.optional) + } + } + } + ident.name = name + ident.kind = .constant + ident.info = ast.IdentVar{ + typ: typ + } + obj.typ = typ + ident.obj = obj + return typ + } + else {} + } + } + // Non-anon-function object (not a call), e.g. `onclick(my_click)` + if func := c.table.find_fn(name) { + fn_type := ast.new_type(c.table.find_or_register_fn_type(ident.mod, func, + false, true)) + ident.name = name + ident.kind = .function + ident.info = ast.IdentFn{ + typ: fn_type + } + return fn_type + } + } + if ident.language == .c { + if ident.name == 'C.NULL' { + return ast.voidptr_type + } + return ast.int_type + } + if c.inside_sql { + if field := c.table.find_field(c.cur_orm_ts, ident.name) { + return field.typ + } + } + if ident.kind == .unresolved && ident.mod != 'builtin' { + // search in the `builtin` idents, for example + // main.compare_f32 may actually be builtin.compare_f32 + saved_mod := ident.mod + ident.mod = 'builtin' + builtin_type := c.ident(mut ident) + if builtin_type != ast.void_type { + return builtin_type + } + ident.mod = saved_mod + } + if ident.tok_kind == .assign { + c.error('undefined ident: `$ident.name` (use `:=` to declare a variable)', ident.pos) + } else if ident.name == 'errcode' { + c.error('undefined ident: `errcode`; did you mean `err.code`?', ident.pos) + } else { + if c.inside_ct_attr { + c.note('`[if $ident.name]` is deprecated. Use `[if $ident.name?]` instead', + ident.pos) + } else { + c.error('undefined ident: `$ident.name`', ident.pos) + } + } + if c.table.known_type(ident.name) { + // e.g. `User` in `json.decode(User, '...')` + return ast.void_type + } + return ast.void_type +} + +pub fn (mut c Checker) concat_expr(mut concat_expr ast.ConcatExpr) ast.Type { + mut mr_types := []ast.Type{} + for expr in concat_expr.vals { + mr_types << c.expr(expr) + } + if concat_expr.vals.len == 1 { + typ := mr_types[0] + concat_expr.return_type = typ + return typ + } else { + typ := c.table.find_or_register_multi_return(mr_types) + ast.new_type(typ) + concat_expr.return_type = typ + return typ + } +} + +pub fn (mut c Checker) match_expr(mut node ast.MatchExpr) ast.Type { + node.is_expr = c.expected_type != ast.void_type + node.expected_type = c.expected_type + cond_type := c.expr(node.cond) + // we setting this here rather than at the end of the method + // since it is used in c.match_exprs() it saves checking twice + node.cond_type = c.table.mktyp(cond_type) + c.ensure_type_exists(node.cond_type, node.pos) or { return ast.void_type } + c.check_expr_opt_call(node.cond, cond_type) + cond_type_sym := c.table.get_type_symbol(cond_type) + node.is_sum_type = cond_type_sym.kind in [.interface_, .sum_type] + c.match_exprs(mut node, cond_type_sym) + c.expected_type = cond_type + mut first_iteration := true + mut ret_type := ast.void_type + mut nbranches_with_return := 0 + mut nbranches_without_return := 0 + for branch in node.branches { + c.stmts(branch.stmts) + if node.is_expr { + if branch.stmts.len > 0 { + // ignore last statement - workaround + // currently the last statement in a match branch does not have an + // expected value set, so e.g. IfExpr.is_expr is not set. + // probably any mismatch will be caught by not producing a value instead + for st in branch.stmts[0..branch.stmts.len - 1] { + // must not contain C statements + st.check_c_expr() or { + c.error('`match` expression branch has $err.msg', st.pos) + } + } + } else if ret_type != ast.void_type { + c.error('`match` expression requires an expression as the last statement of every branch', + branch.branch_pos) + } + } + // If the last statement is an expression, return its type + if branch.stmts.len > 0 { + mut stmt := branch.stmts[branch.stmts.len - 1] + match mut stmt { + ast.ExprStmt { + if node.is_expr { + c.expected_type = node.expected_type + } + expr_type := c.expr(stmt.expr) + if first_iteration { + if node.is_expr && !node.expected_type.has_flag(.optional) + && c.table.get_type_symbol(node.expected_type).kind == .sum_type { + ret_type = node.expected_type + } else { + ret_type = expr_type + } + stmt.typ = expr_type + } else if node.is_expr && ret_type != expr_type { + if !c.check_types(ret_type, expr_type) { + ret_sym := c.table.get_type_symbol(ret_type) + if !(node.is_expr && ret_sym.kind == .sum_type) { + c.error('return type mismatch, it should be `$ret_sym.name`', + stmt.expr.position()) + } + } + } + } + else { + if node.is_expr && ret_type != ast.void_type { + c.error('`match` expression requires an expression as the last statement of every branch', + stmt.pos) + } + } + } + } + first_iteration = false + if has_return := c.has_return(branch.stmts) { + if has_return { + nbranches_with_return++ + } else { + nbranches_without_return++ + } + } + } + if nbranches_with_return > 0 { + if nbranches_with_return == node.branches.len { + // an exhaustive match, and all branches returned + c.returns = true + } + if nbranches_without_return > 0 { + // some of the branches did not return + c.returns = false + } + } + // if ret_type != ast.void_type { + // node.is_expr = c.expected_type != ast.void_type + // node.expected_type = c.expected_type + // } + node.return_type = ret_type + cond_var := c.get_base_name(&node.cond) + if cond_var != '' { + mut cond_is_auto_heap := false + for branch in node.branches { + if v := branch.scope.find_var(cond_var) { + if v.is_auto_heap { + cond_is_auto_heap = true + break + } + } + } + if cond_is_auto_heap { + for branch in node.branches { + mut v := branch.scope.find_var(cond_var) or { continue } + v.is_auto_heap = true + } + } + } + return ret_type +} + +fn (mut c Checker) match_exprs(mut node ast.MatchExpr, cond_type_sym ast.TypeSymbol) { + // branch_exprs is a histogram of how many times + // an expr was used in the match + mut branch_exprs := map[string]int{} + for branch_i, _ in node.branches { + mut branch := node.branches[branch_i] + mut expr_types := []ast.TypeNode{} + for k, expr in branch.exprs { + mut key := '' + if expr is ast.RangeExpr { + mut low := i64(0) + mut high := i64(0) + c.expected_type = node.expected_type + low_expr := expr.low + high_expr := expr.high + if low_expr is ast.IntegerLiteral { + if high_expr is ast.IntegerLiteral { + low = low_expr.val.i64() + high = high_expr.val.i64() + } else { + c.error('mismatched range types', low_expr.pos) + } + } else if low_expr is ast.CharLiteral { + if high_expr is ast.CharLiteral { + low = low_expr.val[0] + high = high_expr.val[0] + } else { + c.error('mismatched range types', low_expr.pos) + } + } else { + typ := c.table.type_to_str(c.expr(expr.low)) + c.error('cannot use type `$typ` in match range', branch.pos) + } + high_low_cutoff := 1000 + if high - low > high_low_cutoff { + c.warn('more than $high_low_cutoff possibilities ($low ... $high) in match range', + branch.pos) + } + for i in low .. high + 1 { + key = i.str() + val := if key in branch_exprs { branch_exprs[key] } else { 0 } + if val == 1 { + c.error('match case `$key` is handled more than once', branch.pos) + } + branch_exprs[key] = val + 1 + } + continue + } + match expr { + ast.TypeNode { + key = c.table.type_to_str(expr.typ) + expr_types << expr + } + ast.EnumVal { + key = expr.val + } + else { + key = expr.str() + } + } + val := if key in branch_exprs { branch_exprs[key] } else { 0 } + if val == 1 { + c.error('match case `$key` is handled more than once', branch.pos) + } + c.expected_type = node.cond_type + expr_type := c.expr(expr) + if expr_type.idx() == 0 { + // parser failed, stop checking + return + } + expr_type_sym := c.table.get_type_symbol(expr_type) + if cond_type_sym.kind == .interface_ { + // TODO + // This generates a memory issue with TCC + // Needs to be checked later when TCC errors are fixed + // Current solution is to move expr.position() to its own statement + // c.type_implements(expr_type, c.expected_type, expr.position()) + expr_pos := expr.position() + if c.type_implements(expr_type, c.expected_type, expr_pos) { + if !expr_type.is_ptr() && !expr_type.is_pointer() && !c.inside_unsafe { + if expr_type_sym.kind != .interface_ { + c.mark_as_referenced(mut &branch.exprs[k], true) + } + } + } + } else if mut cond_type_sym.info is ast.SumType { + if expr_type !in cond_type_sym.info.variants { + expr_str := c.table.type_to_str(expr_type) + expect_str := c.table.type_to_str(node.cond_type) + c.error('`$expect_str` has no variant `$expr_str`', expr.position()) + } + } else if cond_type_sym.info is ast.Alias && expr_type_sym.info is ast.Struct { + expr_str := c.table.type_to_str(expr_type) + expect_str := c.table.type_to_str(node.cond_type) + c.error('cannot match alias type `$expect_str` with `$expr_str`', expr.position()) + } else if !c.check_types(expr_type, node.cond_type) { + expr_str := c.table.type_to_str(expr_type) + expect_str := c.table.type_to_str(node.cond_type) + c.error('cannot match `$expect_str` with `$expr_str`', expr.position()) + } + branch_exprs[key] = val + 1 + } + // when match is type matching, then register smart cast for every branch + if expr_types.len > 0 { + if cond_type_sym.kind in [.sum_type, .interface_] { + mut expr_type := ast.Type(0) + if expr_types.len > 1 { + mut agg_name := strings.new_builder(20) + mut agg_cname := strings.new_builder(20) + agg_name.write_string('(') + for i, expr in expr_types { + if i > 0 { + agg_name.write_string(' | ') + agg_cname.write_string('___') + } + type_str := c.table.type_to_str(expr.typ) + name := if c.is_builtin_mod { type_str } else { '${c.mod}.$type_str' } + agg_name.write_string(name) + agg_cname.write_string(util.no_dots(name)) + } + agg_name.write_string(')') + name := agg_name.str() + existing_idx := c.table.type_idxs[name] + if existing_idx > 0 { + expr_type = existing_idx + } else { + expr_type = c.table.register_type_symbol(ast.TypeSymbol{ + name: name + cname: agg_cname.str() + kind: .aggregate + mod: c.mod + info: ast.Aggregate{ + types: expr_types.map(it.typ) + } + }) + } + } else { + expr_type = expr_types[0].typ + } + c.smartcast(node.cond, node.cond_type, expr_type, mut branch.scope) + } + } + } + // check that expressions are exhaustive + // this is achieved either by putting an else + // or, when the match is on a sum type or an enum + // by listing all variants or values + mut is_exhaustive := true + mut unhandled := []string{} + if node.cond_type == ast.bool_type { + variants := ['true', 'false'] + for v in variants { + if v !in branch_exprs { + is_exhaustive = false + unhandled << '`$v`' + } + } + } else { + match mut cond_type_sym.info { + ast.SumType { + for v in cond_type_sym.info.variants { + v_str := c.table.type_to_str(v) + if v_str !in branch_exprs { + is_exhaustive = false + unhandled << '`$v_str`' + } + } + } + // + ast.Enum { + for v in cond_type_sym.info.vals { + if v !in branch_exprs { + is_exhaustive = false + unhandled << '`.$v`' + } + } + } + else { + is_exhaustive = false + } + } + } + mut else_branch := node.branches[node.branches.len - 1] + mut has_else := else_branch.is_else + if !has_else { + for i, branch in node.branches { + if branch.is_else && i != node.branches.len - 1 { + c.error('`else` must be the last branch of `match`', branch.pos) + else_branch = branch + has_else = true + } + } + } + if is_exhaustive { + if has_else { + c.error('match expression is exhaustive, `else` is unnecessary', else_branch.pos) + } + return + } + if has_else { + return + } + mut err_details := 'match must be exhaustive' + if unhandled.len > 0 { + err_details += ' (add match branches for: ' + if unhandled.len < c.match_exhaustive_cutoff_limit { + err_details += unhandled.join(', ') + } else { + remaining := unhandled.len - c.match_exhaustive_cutoff_limit + err_details += unhandled[0..c.match_exhaustive_cutoff_limit].join(', ') + if remaining > 0 { + err_details += ', and $remaining others ...' + } + } + err_details += ' or `else {}` at the end)' + } else { + err_details += ' (add `else {}` at the end)' + } + c.error(err_details, node.pos) +} + +// smartcast takes the expression with the current type which should be smartcasted to the target type in the given scope +fn (c Checker) smartcast(expr ast.Expr, cur_type ast.Type, to_type_ ast.Type, mut scope ast.Scope) { + sym := c.table.get_type_symbol(cur_type) + to_type := if sym.kind == .interface_ { to_type_.to_ptr() } else { to_type_ } + match expr { + ast.SelectorExpr { + mut is_mut := false + mut smartcasts := []ast.Type{} + expr_sym := c.table.get_type_symbol(expr.expr_type) + mut orig_type := 0 + if field := c.table.find_field(expr_sym, expr.field_name) { + if field.is_mut { + if root_ident := expr.root_ident() { + if v := scope.find_var(root_ident.name) { + is_mut = v.is_mut + } + } + } + if orig_type == 0 { + orig_type = field.typ + } + } + if field := scope.find_struct_field(expr.expr.str(), expr.expr_type, expr.field_name) { + smartcasts << field.smartcasts + } + // smartcast either if the value is immutable or if the mut argument is explicitly given + if !is_mut || expr.is_mut { + smartcasts << to_type + scope.register_struct_field(expr.expr.str(), ast.ScopeStructField{ + struct_type: expr.expr_type + name: expr.field_name + typ: cur_type + smartcasts: smartcasts + pos: expr.pos + orig_type: orig_type + }) + } + } + ast.Ident { + mut is_mut := false + mut smartcasts := []ast.Type{} + mut is_already_casted := false + mut orig_type := 0 + if mut expr.obj is ast.Var { + is_mut = expr.obj.is_mut + smartcasts << expr.obj.smartcasts + is_already_casted = expr.obj.pos.pos == expr.pos.pos + if orig_type == 0 { + orig_type = expr.obj.typ + } + } + // smartcast either if the value is immutable or if the mut argument is explicitly given + if (!is_mut || expr.is_mut) && !is_already_casted { + smartcasts << to_type + scope.register(ast.Var{ + name: expr.name + typ: cur_type + pos: expr.pos + is_used: true + is_mut: expr.is_mut + smartcasts: smartcasts + orig_type: orig_type + }) + } + } + else {} + } +} + +pub fn (mut c Checker) select_expr(mut node ast.SelectExpr) ast.Type { + node.is_expr = c.expected_type != ast.void_type + node.expected_type = c.expected_type + for branch in node.branches { + c.stmt(branch.stmt) + match branch.stmt { + ast.ExprStmt { + if branch.is_timeout { + if !branch.stmt.typ.is_int() { + tsym := c.table.get_type_symbol(branch.stmt.typ) + c.error('invalid type `$tsym.name` for timeout - expected integer number of nanoseconds aka `time.Duration`', + branch.stmt.pos) + } + } else { + if branch.stmt.expr is ast.InfixExpr { + if branch.stmt.expr.left !is ast.Ident + && branch.stmt.expr.left !is ast.SelectorExpr + && branch.stmt.expr.left !is ast.IndexExpr { + c.error('channel in `select` key must be predefined', branch.stmt.expr.left.position()) + } + } else { + c.error('invalid expression for `select` key', branch.stmt.expr.position()) + } + } + } + ast.AssignStmt { + expr := branch.stmt.right[0] + match expr { + ast.PrefixExpr { + if expr.right !is ast.Ident && expr.right !is ast.SelectorExpr + && expr.right !is ast.IndexExpr { + c.error('channel in `select` key must be predefined', expr.right.position()) + } + if expr.or_block.kind != .absent { + err_prefix := if expr.or_block.kind == .block { + 'or block' + } else { + 'error propagation' + } + c.error('$err_prefix not allowed in `select` key', expr.or_block.pos) + } + } + else { + c.error('`<-` receive expression expected', branch.stmt.right[0].position()) + } + } + } + else { + if !branch.is_else { + c.error('receive or send statement expected as `select` key', branch.stmt.pos) + } + } + } + c.stmts(branch.stmts) + } + return ast.bool_type +} + +pub fn (mut c Checker) lock_expr(mut node ast.LockExpr) ast.Type { + if c.rlocked_names.len > 0 || c.locked_names.len > 0 { + c.error('nested `lock`/`rlock` not allowed', node.pos) + } + for i in 0 .. node.lockeds.len { + e_typ := c.expr(node.lockeds[i]) + id_name := node.lockeds[i].str() + if !e_typ.has_flag(.shared_f) { + obj_type := if node.lockeds[i] is ast.Ident { 'variable' } else { 'struct element' } + c.error('`$id_name` must be declared as `shared` $obj_type to be locked', + node.lockeds[i].position()) + } + if id_name in c.locked_names { + c.error('`$id_name` is already locked', node.lockeds[i].position()) + } else if id_name in c.rlocked_names { + c.error('`$id_name` is already read-locked', node.lockeds[i].position()) + } + if node.is_rlock[i] { + c.rlocked_names << id_name + } else { + c.locked_names << id_name + } + } + c.stmts(node.stmts) + c.rlocked_names = [] + c.locked_names = [] + // handle `x := rlock a { a.getval() }` + mut ret_type := ast.void_type + if node.stmts.len > 0 { + last_stmt := node.stmts[node.stmts.len - 1] + if last_stmt is ast.ExprStmt { + ret_type = last_stmt.typ + } + } + if ret_type != ast.void_type { + node.is_expr = true + } + node.typ = ret_type + return ret_type +} + +pub fn (mut c Checker) unsafe_expr(mut node ast.UnsafeExpr) ast.Type { + c.inside_unsafe = true + t := c.expr(node.expr) + c.inside_unsafe = false + return t +} + +fn (mut c Checker) smartcast_if_conds(node ast.Expr, mut scope ast.Scope) { + if node is ast.InfixExpr { + if node.op == .and { + c.smartcast_if_conds(node.left, mut scope) + c.smartcast_if_conds(node.right, mut scope) + } else if node.op == .key_is { + right_expr := node.right + mut right_type := match right_expr { + ast.TypeNode { + right_expr.typ + } + ast.None { + ast.none_type_idx + } + else { + c.error('invalid type `$right_expr`', right_expr.position()) + ast.Type(0) + } + } + right_type = c.unwrap_generic(right_type) + if right_type != ast.Type(0) { + left_sym := c.table.get_type_symbol(node.left_type) + expr_type := c.unwrap_generic(c.expr(node.left)) + if left_sym.kind == .interface_ { + c.type_implements(right_type, expr_type, node.pos) + } else if !c.check_types(right_type, expr_type) { + expect_str := c.table.type_to_str(right_type) + expr_str := c.table.type_to_str(expr_type) + c.error('cannot use type `$expect_str` as type `$expr_str`', node.pos) + } + if (node.left is ast.Ident || node.left is ast.SelectorExpr) + && node.right is ast.TypeNode { + is_variable := if mut node.left is ast.Ident { + node.left.kind == .variable + } else { + true + } + if is_variable { + if left_sym.kind in [.interface_, .sum_type] { + c.smartcast(node.left, node.left_type, right_type, mut scope) + } + } + } + } + } + } +} + +pub fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type { + if_kind := if node.is_comptime { '\$if' } else { 'if' } + mut node_is_expr := false + if node.branches.len > 0 && node.has_else { + stmts := node.branches[0].stmts + if stmts.len > 0 && stmts[stmts.len - 1] is ast.ExprStmt + && (stmts[stmts.len - 1] as ast.ExprStmt).typ != ast.void_type { + node_is_expr = true + } + } + if c.expected_type == ast.void_type && node_is_expr { + c.expected_type = c.expected_or_type + } + expr_required := c.expected_type != ast.void_type + former_expected_type := c.expected_type + node.typ = ast.void_type + mut nbranches_with_return := 0 + mut nbranches_without_return := 0 + mut should_skip := false // Whether the current branch should be skipped + mut found_branch := false // Whether a matching branch was found- skip the rest + mut is_comptime_type_is_expr := false // if `$if T is string` + for i in 0 .. node.branches.len { + mut branch := node.branches[i] + if branch.cond is ast.ParExpr { + c.error('unnecessary `()` in `$if_kind` condition, use `$if_kind expr {` instead of `$if_kind (expr) {`.', + branch.pos) + } + if !node.has_else || i < node.branches.len - 1 { + if node.is_comptime { + should_skip = c.comp_if_branch(branch.cond, branch.pos) + node.branches[i].pkg_exist = !should_skip + } else { + // check condition type is boolean + c.expected_type = ast.bool_type + cond_typ := c.expr(branch.cond) + if (cond_typ.idx() != ast.bool_type_idx || cond_typ.has_flag(.optional)) + && !c.pref.translated { + c.error('non-bool type `${c.table.type_to_str(cond_typ)}` used as if condition', + branch.cond.position()) + } + } + } + if node.is_comptime { // Skip checking if needed + // smartcast field type on comptime if + mut comptime_field_name := '' + if branch.cond is ast.InfixExpr { + if branch.cond.op == .key_is { + if branch.cond.right !is ast.TypeNode { + c.error('invalid `\$if` condition: expected a type', branch.cond.right.position()) + return 0 + } + got_type := c.unwrap_generic((branch.cond.right as ast.TypeNode).typ) + sym := c.table.get_type_symbol(got_type) + if sym.kind == .placeholder || got_type.has_flag(.generic) { + c.error('unknown type `$sym.name`', branch.cond.right.position()) + } + left := branch.cond.left + if left is ast.SelectorExpr { + comptime_field_name = left.expr.str() + c.comptime_fields_type[comptime_field_name] = got_type + is_comptime_type_is_expr = true + } else if branch.cond.right is ast.TypeNode && left is ast.TypeNode + && sym.kind == .interface_ { + // is interface + checked_type := c.unwrap_generic(left.typ) + should_skip = !c.table.does_type_implement_interface(checked_type, + got_type) + } else if left is ast.TypeNode { + is_comptime_type_is_expr = true + left_type := c.unwrap_generic(left.typ) + if left_type != got_type { + should_skip = true + } + } + } + } + cur_skip_flags := c.skip_flags + if found_branch { + c.skip_flags = true + } else if should_skip { + c.skip_flags = true + should_skip = false // Reset the value of `should_skip` for the next branch + } else if !is_comptime_type_is_expr { + found_branch = true // If a branch wasn't skipped, the rest must be + } + if c.fn_level == 0 && c.pref.output_cross_c { + // do not skip any of the branches for top level `$if OS {` + // statements, in `-os cross` mode + found_branch = false + c.skip_flags = false + c.ct_cond_stack << branch.cond + } + if !c.skip_flags { + c.stmts(branch.stmts) + } else if c.pref.output_cross_c { + mut is_freestanding_block := false + if branch.cond is ast.Ident { + if branch.cond.name == 'freestanding' { + is_freestanding_block = true + } + } + if is_freestanding_block { + branch.stmts = [] + node.branches[i].stmts = [] + } + c.stmts(branch.stmts) + } else if !is_comptime_type_is_expr { + node.branches[i].stmts = [] + } + if comptime_field_name.len > 0 { + c.comptime_fields_type.delete(comptime_field_name) + } + c.skip_flags = cur_skip_flags + if c.fn_level == 0 && c.pref.output_cross_c && c.ct_cond_stack.len > 0 { + c.ct_cond_stack.delete_last() + } + } else { + // smartcast sumtypes and interfaces when using `is` + c.smartcast_if_conds(branch.cond, mut branch.scope) + c.stmts(branch.stmts) + } + if expr_required { + if branch.stmts.len > 0 && branch.stmts[branch.stmts.len - 1] is ast.ExprStmt { + mut last_expr := branch.stmts[branch.stmts.len - 1] as ast.ExprStmt + c.expected_type = former_expected_type + if c.expected_type.has_flag(.optional) { + if node.typ == ast.void_type { + node.is_expr = true + node.typ = c.expected_type + } + } + if c.expected_type.has_flag(.generic) { + if node.typ == ast.void_type { + node.is_expr = true + node.typ = c.unwrap_generic(c.expected_type) + } + continue + } + last_expr.typ = c.expr(last_expr.expr) + if !c.check_types(last_expr.typ, node.typ) { + if node.typ == ast.void_type { + // first branch of if expression + node.is_expr = true + node.typ = last_expr.typ + continue + } else if node.typ in [ast.float_literal_type, ast.int_literal_type] { + if node.typ == ast.int_literal_type { + if last_expr.typ.is_int() || last_expr.typ.is_float() { + node.typ = last_expr.typ + continue + } + } else { // node.typ == float_literal + if last_expr.typ.is_float() { + node.typ = last_expr.typ + continue + } + } + } + if last_expr.typ in [ast.float_literal_type, ast.int_literal_type] { + if last_expr.typ == ast.int_literal_type { + if node.typ.is_int() || node.typ.is_float() { + continue + } + } else { // expr_type == float_literal + if node.typ.is_float() { + continue + } + } + } + if node.is_expr + && c.table.get_type_symbol(former_expected_type).kind == .sum_type { + continue + } + c.error('mismatched types `${c.table.type_to_str(node.typ)}` and `${c.table.type_to_str(last_expr.typ)}`', + node.pos) + } + } else { + c.error('`$if_kind` expression requires an expression as the last statement of every branch', + branch.pos) + } + for st in branch.stmts { + // must not contain C statements + st.check_c_expr() or { c.error('`if` expression branch has $err.msg', st.pos) } + } + } + // Also check for returns inside a comp.if's statements, even if its contents aren't parsed + if has_return := c.has_return(branch.stmts) { + if has_return { + nbranches_with_return++ + } else { + nbranches_without_return++ + } + } + } + if nbranches_with_return > 0 { + if nbranches_with_return == node.branches.len { + // if/else... where all branches returned + c.returns = true + } + if !node.has_else { + // `if cond { return ... }` means that when cond is false, execution continues + c.returns = false + } + if nbranches_without_return > 0 { + // some of the branches did not return + c.returns = false + } + } + // if only untyped literals were given default to int/f64 + if node.typ == ast.int_literal_type { + node.typ = ast.int_type + } else if node.typ == ast.float_literal_type { + node.typ = ast.f64_type + } + if expr_required && !node.has_else { + d := if node.is_comptime { '$' } else { '' } + c.error('`$if_kind` expression needs `${d}else` clause', node.pos) + } + return node.typ +} + +// comp_if_branch checks the condition of a compile-time `if` branch. It returns `true` +// if that branch's contents should be skipped (targets a different os for example) +fn (mut c Checker) comp_if_branch(cond ast.Expr, pos token.Position) bool { + // TODO: better error messages here + match cond { + ast.BoolLiteral { + return !cond.val + } + ast.ParExpr { + return c.comp_if_branch(cond.expr, pos) + } + ast.PrefixExpr { + if cond.op != .not { + c.error('invalid `\$if` condition', cond.pos) + } + return !c.comp_if_branch(cond.right, cond.pos) + } + ast.PostfixExpr { + if cond.op != .question { + c.error('invalid \$if postfix operator', cond.pos) + } else if cond.expr is ast.Ident { + return cond.expr.name !in c.pref.compile_defines_all + } else { + c.error('invalid `\$if` condition', cond.pos) + } + } + ast.InfixExpr { + match cond.op { + .and { + l := c.comp_if_branch(cond.left, cond.pos) + r := c.comp_if_branch(cond.right, cond.pos) + return l || r // skip (return true) if at least one should be skipped + } + .logical_or { + l := c.comp_if_branch(cond.left, cond.pos) + r := c.comp_if_branch(cond.right, cond.pos) + return l && r // skip (return true) only if both should be skipped + } + .key_is, .not_is { + if cond.left is ast.TypeNode && cond.right is ast.TypeNode { + // `$if Foo is Interface {` + sym := c.table.get_type_symbol(cond.right.typ) + if sym.kind != .interface_ { + c.expr(cond.left) + // c.error('`$sym.name` is not an interface', cond.right.position()) + } + return false + } else if cond.left is ast.SelectorExpr || cond.left is ast.TypeNode { + // `$if method.@type is string` + c.expr(cond.left) + return false + } else { + c.error('invalid `\$if` condition: expected a type or a selector expression or an interface check', + cond.left.position()) + } + } + .eq, .ne { + if cond.left is ast.SelectorExpr && cond.right is ast.IntegerLiteral { + // $if method.args.len == 1 + } else if cond.left is ast.Ident { + // $if version == 2 + left_type := c.expr(cond.left) + right_type := c.expr(cond.right) + expr := c.find_definition(cond.left) or { + c.error(err.msg, cond.left.pos) + return false + } + if !c.check_types(right_type, left_type) { + left_name := c.table.type_to_str(left_type) + right_name := c.table.type_to_str(right_type) + c.error('mismatched types `$left_name` and `$right_name`', + cond.pos) + } + // :) + // until `v.eval` is stable, I can't think of a better way to do this + different := expr.str() != cond.right.str() + return if cond.op == .eq { different } else { !different } + } else { + c.error('invalid `\$if` condition: ${cond.left.type_name()}1', + cond.pos) + } + } + else { + c.error('invalid `\$if` condition', cond.pos) + } + } + } + ast.Ident { + cname := cond.name + if cname in checker.valid_comp_if_os { + mut is_os_target_different := false + if !c.pref.output_cross_c { + target_os := c.pref.os.str().to_lower() + is_os_target_different = cname != target_os + } + return is_os_target_different + } else if cname in checker.valid_comp_if_compilers { + return pref.cc_from_string(cname) != c.pref.ccompiler_type + } else if cname in checker.valid_comp_if_platforms { + if cname == 'aarch64' { + c.note('use `arm64` instead of `aarch64`', pos) + } + match cname { + 'amd64' { return c.pref.arch != .amd64 } + 'i386' { return c.pref.arch != .i386 } + 'aarch64' { return c.pref.arch != .arm64 } + 'arm64' { return c.pref.arch != .arm64 } + 'arm32' { return c.pref.arch != .arm32 } + 'rv64' { return c.pref.arch != .rv64 } + 'rv32' { return c.pref.arch != .rv32 } + else { return false } + } + } else if cname in checker.valid_comp_if_cpu_features { + return false + } else if cname in checker.valid_comp_if_other { + match cname { + 'js' { return !c.pref.backend.is_js() } + 'debug' { return !c.pref.is_debug } + 'prod' { return !c.pref.is_prod } + 'test' { return !c.pref.is_test } + 'glibc' { return false } // TODO + 'threads' { return c.table.gostmts == 0 } + 'prealloc' { return !c.pref.prealloc } + 'no_bounds_checking' { return cname !in c.pref.compile_defines_all } + 'freestanding' { return !c.pref.is_bare || c.pref.output_cross_c } + else { return false } + } + } else if cname !in c.pref.compile_defines_all { + if cname == 'linux_or_macos' { + c.error('linux_or_macos is deprecated, use `\$if linux || macos {` instead', + cond.pos) + return false + } + // `$if some_var {}`, or `[if user_defined_tag] fn abc(){}` + typ := c.expr(cond) + if cond.obj !is ast.Var && cond.obj !is ast.ConstField + && cond.obj !is ast.GlobalField { + if !c.inside_ct_attr { + c.error('unknown var: `$cname`', pos) + } + return false + } + expr := c.find_obj_definition(cond.obj) or { + c.error(err.msg, cond.pos) + return false + } + if !c.check_types(typ, ast.bool_type) { + type_name := c.table.type_to_str(typ) + c.error('non-bool type `$type_name` used as \$if condition', cond.pos) + } + // :) + // until `v.eval` is stable, I can't think of a better way to do this + return !(expr as ast.BoolLiteral).val + } + } + ast.ComptimeCall { + if cond.is_pkgconfig { + mut m := pkgconfig.main([cond.args_var]) or { + c.error(err.msg, cond.pos) + return true + } + m.run() or { return true } + } + } + else { + c.error('invalid `\$if` condition', pos) + } + } + return false +} + +fn (mut c Checker) find_definition(ident ast.Ident) ?ast.Expr { + match ident.kind { + .unresolved, .blank_ident { return none } + .variable, .constant { return c.find_obj_definition(ident.obj) } + .global { return error('$ident.name is a global variable') } + .function { return error('$ident.name is a function') } + } +} + +fn (mut c Checker) find_obj_definition(obj ast.ScopeObject) ?ast.Expr { + // TODO: remove once we have better type inference + mut name := '' + match obj { + ast.Var, ast.ConstField, ast.GlobalField, ast.AsmRegister { name = obj.name } + } + mut expr := ast.empty_expr() + if obj is ast.Var { + if obj.is_mut { + return error('`$name` is mut and may have changed since its definition') + } + expr = obj.expr + } else if obj is ast.ConstField { + expr = obj.expr + } else { + return error('`$name` is a global variable and is unknown at compile time') + } + if expr is ast.Ident { + return c.find_definition(expr as ast.Ident) // TODO: smartcast + } + if !expr.is_lit() { + return error('definition of `$name` is unknown at compile time') + } + return expr +} + +fn (c &Checker) has_return(stmts []ast.Stmt) ?bool { + // complexity means either more match or ifs + mut has_complexity := false + for s in stmts { + if s is ast.ExprStmt { + if s.expr is ast.IfExpr || s.expr is ast.MatchExpr { + has_complexity = true + break + } + } + } + // if the inner complexity covers all paths with returns there is no need for further checks + if !has_complexity || !c.returns { + return has_top_return(stmts) + } + return none +} + +pub fn (mut c Checker) postfix_expr(mut node ast.PostfixExpr) ast.Type { + typ := c.unwrap_generic(c.expr(node.expr)) + typ_sym := c.table.get_type_symbol(typ) + is_non_void_pointer := (typ.is_ptr() || typ.is_pointer()) && typ_sym.kind != .voidptr + if !c.inside_unsafe && is_non_void_pointer && !node.expr.is_auto_deref_var() { + c.warn('pointer arithmetic is only allowed in `unsafe` blocks', node.pos) + } + if !(typ_sym.is_number() || (c.inside_unsafe && is_non_void_pointer)) { + c.error('invalid operation: $node.op.str() (non-numeric type `$typ_sym.name`)', + node.pos) + } else { + node.auto_locked, _ = c.fail_if_immutable(node.expr) + } + return typ +} + +pub fn (mut c Checker) mark_as_referenced(mut node ast.Expr, as_interface bool) { + match mut node { + ast.Ident { + if mut node.obj is ast.Var { + mut obj := unsafe { &node.obj } + if c.fn_scope != voidptr(0) { + obj = c.fn_scope.find_var(node.obj.name) or { obj } + } + type_sym := c.table.get_type_symbol(obj.typ.set_nr_muls(0)) + if obj.is_stack_obj && !type_sym.is_heap() && !c.pref.translated { + suggestion := if type_sym.kind == .struct_ { + 'declaring `$type_sym.name` as `[heap]`' + } else { + 'wrapping the `$type_sym.name` object in a `struct` declared as `[heap]`' + } + if !c.pref.translated { + mischief := if as_interface { + 'used as interface object' + } else { + 'referenced' + } + c.error('`$node.name` cannot be $mischief outside `unsafe` blocks as it might be stored on stack. Consider ${suggestion}.', + node.pos) + } + } else if type_sym.kind == .array_fixed { + c.error('cannot reference fixed array `$node.name` outside `unsafe` blocks as it is supposed to be stored on stack', + node.pos) + } else { + match type_sym.kind { + .struct_ { + info := type_sym.info as ast.Struct + if !info.is_heap { + node.obj.is_auto_heap = true + } + } + else { + node.obj.is_auto_heap = true + } + } + } + } + } + ast.SelectorExpr { + if !node.expr_type.is_ptr() { + c.mark_as_referenced(mut &node.expr, as_interface) + } + } + ast.IndexExpr { + c.mark_as_referenced(mut &node.left, as_interface) + } + else {} + } +} + +pub fn (mut c Checker) get_base_name(node &ast.Expr) string { + match node { + ast.Ident { + return node.name + } + ast.SelectorExpr { + return c.get_base_name(&node.expr) + } + ast.IndexExpr { + return c.get_base_name(&node.left) + } + else { + return '' + } + } +} + +pub fn (mut c Checker) prefix_expr(mut node ast.PrefixExpr) ast.Type { + old_inside_ref_lit := c.inside_ref_lit + c.inside_ref_lit = c.inside_ref_lit || node.op == .amp + right_type := c.expr(node.right) + c.inside_ref_lit = old_inside_ref_lit + node.right_type = right_type + // TODO: testing ref/deref strategy + if node.op == .amp && !right_type.is_ptr() { + mut expr := node.right + // if ParExpr get the innermost expr + for mut expr is ast.ParExpr { + expr = expr.expr + } + match expr { + ast.BoolLiteral, ast.CallExpr, ast.CharLiteral, ast.FloatLiteral, ast.IntegerLiteral, + ast.InfixExpr, ast.StringLiteral, ast.StringInterLiteral { + c.error('cannot take the address of $expr', node.pos) + } + else {} + } + if mut node.right is ast.IndexExpr { + typ_sym := c.table.get_type_symbol(node.right.left_type) + mut is_mut := false + if mut node.right.left is ast.Ident { + ident := node.right.left + // TODO: temporary, remove this + ident_obj := ident.obj + if ident_obj is ast.Var { + is_mut = ident_obj.is_mut + } + } + if typ_sym.kind == .map { + c.error('cannot take the address of map values', node.right.pos) + } + if !c.inside_unsafe { + if typ_sym.kind == .array && is_mut { + c.error('cannot take the address of mutable array elements outside unsafe blocks', + node.right.pos) + } + } + } + if !c.inside_fn_arg && !c.inside_unsafe { + c.mark_as_referenced(mut &node.right, false) + } + return right_type.to_ptr() + } else if node.op == .amp && node.right !is ast.CastExpr { + if !c.inside_fn_arg && !c.inside_unsafe { + c.mark_as_referenced(mut &node.right, false) + } + if node.right.is_auto_deref_var() { + return right_type + } else { + return right_type.to_ptr() + } + } + if node.op == .mul { + if right_type.is_ptr() { + return right_type.deref() + } + if !right_type.is_pointer() { + s := c.table.type_to_str(right_type) + c.error('invalid indirect of `$s`', node.pos) + } + } + if node.op == .bit_not && !right_type.is_int() && !c.pref.translated { + c.error('operator ~ only defined on int types', node.pos) + } + if node.op == .not && right_type != ast.bool_type_idx && !c.pref.translated { + c.error('! operator can only be used with bool types', node.pos) + } + // FIXME + // there are currently other issues to investigate if right_type + // is unwraped directly as initialization, so do it here + right_sym := c.table.get_final_type_symbol(c.unwrap_generic(right_type)) + if node.op == .minus && !right_sym.is_number() { + c.error('- operator can only be used with numeric types', node.pos) + } + if node.op == .arrow { + if right_sym.kind == .chan { + c.stmts(node.or_block.stmts) + return right_sym.chan_info().elem_type + } + c.error('<- operator can only be used with `chan` types', node.pos) + } + return right_type +} + +fn (mut c Checker) check_index(typ_sym &ast.TypeSymbol, index ast.Expr, index_type ast.Type, pos token.Position, range_index bool) { + index_type_sym := c.table.get_type_symbol(index_type) + // println('index expr left=$typ_sym.name $node.pos.line_nr') + // if typ_sym.kind == .array && (!(ast.type_idx(index_type) in ast.number_type_idxs) && + // index_type_sym.kind != .enum_) { + if typ_sym.kind in [.array, .array_fixed, .string] { + if !(index_type.is_int() || index_type_sym.kind == .enum_) { + type_str := if typ_sym.kind == .string { + 'non-integer string index `$index_type_sym.name`' + } else { + 'non-integer index `$index_type_sym.name` (array type `$typ_sym.name`)' + } + c.error('$type_str', pos) + } + if index is ast.IntegerLiteral { + if index.val[0] == `-` { + c.error('negative index `$index.val`', index.pos) + } else if typ_sym.kind == .array_fixed { + i := index.val.int() + info := typ_sym.info as ast.ArrayFixed + if (!range_index && i >= info.size) || (range_index && i > info.size) { + c.error('index out of range (index: $i, len: $info.size)', index.pos) + } + } + } + if index_type.has_flag(.optional) { + type_str := if typ_sym.kind == .string { + '(type `$typ_sym.name`)' + } else { + '(array type `$typ_sym.name`)' + } + c.error('cannot use optional as index $type_str', pos) + } + } +} + +pub fn (mut c Checker) index_expr(mut node ast.IndexExpr) ast.Type { + mut typ := c.expr(node.left) + mut typ_sym := c.table.get_final_type_symbol(typ) + node.left_type = typ + for { + match typ_sym.kind { + .map { + node.is_map = true + break + } + .array { + node.is_array = true + break + } + .array_fixed { + node.is_farray = true + break + } + .any { + gname := typ_sym.name + typ = c.unwrap_generic(typ) + node.left_type = typ + typ_sym = c.table.get_final_type_symbol(typ) + if typ.is_ptr() { + continue + } else { + c.error('generic type $gname does not support indexing, pass an array, or a reference instead, e.g. []$gname or &$gname', + node.pos) + } + } + else { + break + } + } + } + if typ_sym.kind !in [.array, .array_fixed, .string, .map] && !typ.is_ptr() + && typ !in [ast.byteptr_type, ast.charptr_type] && !typ.has_flag(.variadic) { + c.error('type `$typ_sym.name` does not support indexing', node.pos) + } + if typ_sym.kind == .string && !typ.is_ptr() && node.is_setter { + c.error('cannot assign to s[i] since V strings are immutable\n' + + '(note, that variables may be mutable but string values are always immutable, like in Go and Java)', + node.pos) + } + if !c.inside_unsafe && ((typ.is_ptr() && !typ.has_flag(.shared_f) + && !node.left.is_auto_deref_var()) || typ.is_pointer()) { + mut is_ok := false + if mut node.left is ast.Ident { + if node.left.obj is ast.Var { + v := node.left.obj as ast.Var + // `mut param []T` function parameter + is_ok = ((v.is_mut && v.is_arg)) && !typ.deref().is_ptr() + } + } + if !is_ok && !c.pref.translated { + c.warn('pointer indexing is only allowed in `unsafe` blocks', node.pos) + } + } + if mut node.index is ast.RangeExpr { // [1..2] + if node.index.has_low { + index_type := c.expr(node.index.low) + c.check_index(typ_sym, node.index.low, index_type, node.pos, true) + } + if node.index.has_high { + index_type := c.expr(node.index.high) + c.check_index(typ_sym, node.index.high, index_type, node.pos, true) + } + // array[1..2] => array + // fixed_array[1..2] => array + if typ_sym.kind == .array_fixed { + elem_type := c.table.value_type(typ) + idx := c.table.find_or_register_array(elem_type) + typ = ast.new_type(idx) + } else { + typ = typ.set_nr_muls(0) + } + } else { // [1] + if typ_sym.kind == .map { + info := typ_sym.info as ast.Map + c.expected_type = info.key_type + index_type := c.expr(node.index) + if !c.check_types(index_type, info.key_type) { + err := c.expected_msg(index_type, info.key_type) + c.error('invalid key: $err', node.pos) + } + } else { + index_type := c.expr(node.index) + c.check_index(typ_sym, node.index, index_type, node.pos, false) + } + value_type := c.table.value_type(typ) + if value_type != ast.void_type { + typ = value_type + } + } + c.stmts(node.or_expr.stmts) + c.check_expr_opt_call(node, typ) + return typ +} + +// `.green` or `Color.green` +// If a short form is used, `expected_type` needs to be an enum +// with this value. +pub fn (mut c Checker) enum_val(mut node ast.EnumVal) ast.Type { + mut typ_idx := if node.enum_name == '' { + c.expected_type.idx() + } else { + c.table.find_type_idx(node.enum_name) + } + if typ_idx == 0 { + // Handle `builtin` enums like `ChanState`, so that `x := ChanState.closed` works. + // In the checker the name for such enums was set to `main.ChanState` instead of + // just `ChanState`. + if node.enum_name.starts_with('${c.mod}.') { + typ_idx = c.table.find_type_idx(node.enum_name['${c.mod}.'.len..]) + if typ_idx == 0 { + c.error('unknown enum `$node.enum_name` (type_idx=0)', node.pos) + return ast.void_type + } + } + } + mut typ := ast.new_type(typ_idx) + if c.pref.translated { + // TODO make more strict + node.typ = typ + return typ + } + if typ == ast.void_type { + c.error('not an enum', node.pos) + return ast.void_type + } + mut typ_sym := c.table.get_type_symbol(typ) + if typ_sym.kind == .array && node.enum_name.len == 0 { + array_info := typ_sym.info as ast.Array + typ = array_info.elem_type + typ_sym = c.table.get_type_symbol(typ) + } + if typ_sym.kind != .enum_ && !c.pref.translated { + // TODO in C int fields can be compared to enums, need to handle that in C2V + c.error('expected type is not an enum (`$typ_sym.name`)', node.pos) + return ast.void_type + } + if typ_sym.info !is ast.Enum { + c.error('not an enum', node.pos) + return ast.void_type + } + if !(typ_sym.is_public || typ_sym.mod == c.mod) { + c.error('enum `$typ_sym.name` is private', node.pos) + } + info := typ_sym.enum_info() + if node.val !in info.vals { + suggestion := util.new_suggestion(node.val, info.vals) + c.error(suggestion.say('enum `$typ_sym.name` does not have a value `$node.val`'), + node.pos) + } + node.typ = typ + return typ +} + +pub fn (mut c Checker) chan_init(mut node ast.ChanInit) ast.Type { + if node.typ != 0 { + info := c.table.get_type_symbol(node.typ).chan_info() + node.elem_type = info.elem_type + if node.has_cap { + c.check_array_init_para_type('cap', node.cap_expr, node.pos) + } + return node.typ + } else { + c.error('`chan` of unknown type', node.pos) + return node.typ + } +} + +pub fn (mut c Checker) offset_of(node ast.OffsetOf) ast.Type { + sym := c.table.get_final_type_symbol(node.struct_type) + if sym.kind != .struct_ { + c.error('first argument of __offsetof must be struct', node.pos) + return ast.u32_type + } + if !c.table.struct_has_field(sym, node.field) { + c.error('struct `$sym.name` has no field called `$node.field`', node.pos) + } + return ast.u32_type +} + +pub fn (mut c Checker) check_dup_keys(node &ast.MapInit, i int) { + key_i := node.keys[i] + if key_i is ast.StringLiteral { + for j in 0 .. i { + key_j := node.keys[j] + if key_j is ast.StringLiteral { + if key_i.val == key_j.val { + c.error('duplicate key "$key_i.val" in map literal', key_i.pos) + } + } + } + } else if key_i is ast.IntegerLiteral { + for j in 0 .. i { + key_j := node.keys[j] + if key_j is ast.IntegerLiteral { + if key_i.val == key_j.val { + c.error('duplicate key "$key_i.val" in map literal', key_i.pos) + } + } + } + } +} + +pub fn (mut c Checker) map_init(mut node ast.MapInit) ast.Type { + // `map = {}` + if node.keys.len == 0 && node.vals.len == 0 && node.typ == 0 { + sym := c.table.get_type_symbol(c.expected_type) + if sym.kind == .map { + info := sym.map_info() + node.typ = c.expected_type + node.key_type = info.key_type + node.value_type = info.value_type + return node.typ + } else { + c.error('invalid empty map initilization syntax, use e.g. map[string]int{} instead', + node.pos) + } + } + // `x := map[string]string` - set in parser + if node.typ != 0 { + info := c.table.get_type_symbol(node.typ).map_info() + c.ensure_type_exists(info.key_type, node.pos) or {} + c.ensure_type_exists(info.value_type, node.pos) or {} + node.key_type = c.unwrap_generic(info.key_type) + node.value_type = c.unwrap_generic(info.value_type) + return node.typ + } + if node.keys.len > 0 && node.vals.len > 0 { + mut key0_type := ast.void_type + mut val0_type := ast.void_type + use_expected_type := c.expected_type != ast.void_type && !c.inside_const + && c.table.get_type_symbol(c.expected_type).kind == .map + if use_expected_type { + sym := c.table.get_type_symbol(c.expected_type) + info := sym.map_info() + key0_type = c.unwrap_generic(info.key_type) + val0_type = c.unwrap_generic(info.value_type) + } else { + // `{'age': 20}` + key0_type = c.table.mktyp(c.expr(node.keys[0])) + if node.keys[0].is_auto_deref_var() { + key0_type = key0_type.deref() + } + val0_type = c.table.mktyp(c.expr(node.vals[0])) + if node.vals[0].is_auto_deref_var() { + val0_type = val0_type.deref() + } + } + mut same_key_type := true + for i, key in node.keys { + if i == 0 && !use_expected_type { + continue + } + val := node.vals[i] + key_type := c.expr(key) + c.expected_type = val0_type + val_type := c.expr(val) + if !c.check_types(key_type, key0_type) || (i == 0 && key_type.is_number() + && key0_type.is_number() && key0_type != c.table.mktyp(key_type)) { + msg := c.expected_msg(key_type, key0_type) + c.error('invalid map key: $msg', key.position()) + same_key_type = false + } + if !c.check_types(val_type, val0_type) || (i == 0 && val_type.is_number() + && val0_type.is_number() && val0_type != c.table.mktyp(val_type)) { + msg := c.expected_msg(val_type, val0_type) + c.error('invalid map value: $msg', val.position()) + } + } + if same_key_type { + for i in 1 .. node.keys.len { + c.check_dup_keys(node, i) + } + } + key0_type = c.unwrap_generic(key0_type) + val0_type = c.unwrap_generic(val0_type) + mut map_type := ast.new_type(c.table.find_or_register_map(key0_type, val0_type)) + node.typ = map_type + node.key_type = key0_type + node.value_type = val0_type + return map_type + } + return node.typ +} + +// call this *before* calling error or warn +pub fn (mut c Checker) add_error_detail(s string) { + c.error_details << s +} + +pub fn (mut c Checker) warn(s string, pos token.Position) { + allow_warnings := !(c.pref.is_prod || c.pref.warns_are_errors) // allow warnings only in dev builds + c.warn_or_error(s, pos, allow_warnings) +} + +pub fn (mut c Checker) error(message string, pos token.Position) { + $if checker_exit_on_first_error ? { + eprintln('\n\n>> checker error: $message, pos: $pos') + print_backtrace() + exit(1) + } + if c.pref.translated && message.starts_with('mismatched types') { + // TODO move this + return + } + if c.pref.is_verbose { + print_backtrace() + } + msg := message.replace('`Array_', '`[]') + c.warn_or_error(msg, pos, false) +} + +// check `to` has all fields of `from` +fn (c &Checker) check_struct_signature(from ast.Struct, to ast.Struct) bool { + // Note: `to` can have extra fields + if from.fields.len == 0 { + return false + } + for field in from.fields { + filtered := to.fields.filter(it.name == field.name) + if filtered.len != 1 { + // field doesn't exist + return false + } + counterpart := filtered[0] + if field.typ != counterpart.typ { + // field has different tye + return false + } + if field.is_pub != counterpart.is_pub { + // field is not public while the other one is + return false + } + if field.is_mut != counterpart.is_mut { + // field is not mutable while the other one is + return false + } + } + return true +} + +pub fn (mut c Checker) note(message string, pos token.Position) { + mut details := '' + if c.error_details.len > 0 { + details = c.error_details.join('\n') + c.error_details = [] + } + wrn := errors.Notice{ + reporter: errors.Reporter.checker + pos: pos + file_path: c.file.path + message: message + details: details + } + c.file.notices << wrn + c.notices << wrn + c.nr_notices++ +} + +fn (mut c Checker) warn_or_error(message string, pos token.Position, warn bool) { + // add backtrace to issue struct, how? + // if c.pref.is_verbose { + // print_backtrace() + // } + mut details := '' + if c.error_details.len > 0 { + details = c.error_details.join('\n') + c.error_details = [] + } + if warn && !c.pref.skip_warnings { + c.nr_warnings++ + wrn := errors.Warning{ + reporter: errors.Reporter.checker + pos: pos + file_path: c.file.path + message: message + details: details + } + c.file.warnings << wrn + c.warnings << wrn + return + } + if !warn { + if c.pref.fatal_errors { + exit(1) + } + c.nr_errors++ + if pos.line_nr !in c.error_lines { + err := errors.Error{ + reporter: errors.Reporter.checker + pos: pos + file_path: c.file.path + message: message + details: details + } + c.file.errors << err + c.errors << err + c.error_lines << pos.line_nr + } + } +} + +// for debugging only +fn (c &Checker) fileis(s string) bool { + return c.file.path.contains(s) +} + +fn (mut c Checker) sql_expr(mut node ast.SqlExpr) ast.Type { + c.inside_sql = true + defer { + c.inside_sql = false + } + sym := c.table.get_type_symbol(node.table_expr.typ) + c.ensure_type_exists(node.table_expr.typ, node.pos) or { return ast.void_type } + c.cur_orm_ts = sym + info := sym.info as ast.Struct + fields := c.fetch_and_verify_orm_fields(info, node.table_expr.pos, sym.name) + mut sub_structs := map[int]ast.SqlExpr{} + for f in fields.filter(c.table.type_symbols[int(it.typ)].kind == .struct_ + || (c.table.get_type_symbol(it.typ).kind == .array + && c.table.get_type_symbol(c.table.get_type_symbol(it.typ).array_info().elem_type).kind == .struct_)) { + typ := if c.table.get_type_symbol(f.typ).kind == .struct_ { + f.typ + } else if c.table.get_type_symbol(f.typ).kind == .array { + c.table.get_type_symbol(f.typ).array_info().elem_type + } else { + ast.Type(0) + } + mut n := ast.SqlExpr{ + pos: node.pos + has_where: true + typ: typ + db_expr: node.db_expr + table_expr: ast.TypeNode{ + pos: node.table_expr.pos + typ: typ + } + } + tmp_inside_sql := c.inside_sql + c.sql_expr(mut n) + c.inside_sql = tmp_inside_sql + n.where_expr = ast.InfixExpr{ + op: .eq + pos: n.pos + left: ast.Ident{ + language: .v + tok_kind: .eq + scope: c.fn_scope + obj: ast.Var{} + mod: 'main' + name: 'id' + is_mut: false + kind: .unresolved + info: ast.IdentVar{} + } + right: ast.Ident{ + language: .c + mod: 'main' + tok_kind: .eq + obj: ast.Var{} + is_mut: false + scope: c.fn_scope + info: ast.IdentVar{ + typ: ast.int_type + } + } + left_type: ast.int_type + right_type: ast.int_type + auto_locked: '' + or_block: ast.OrExpr{} + } + + sub_structs[int(typ)] = n + } + node.fields = fields + node.sub_structs = sub_structs.move() + if node.has_where { + c.expr(node.where_expr) + } + if node.has_offset { + c.expr(node.offset_expr) + } + if node.has_limit { + c.expr(node.limit_expr) + } + if node.has_order { + c.expr(node.order_expr) + } + c.expr(node.db_expr) + return node.typ +} + +fn (mut c Checker) sql_stmt(mut node ast.SqlStmt) ast.Type { + c.expr(node.db_expr) + mut typ := ast.void_type + for mut line in node.lines { + a := c.sql_stmt_line(mut line) + if a != ast.void_type { + typ = a + } + } + return typ +} + +fn (mut c Checker) sql_stmt_line(mut node ast.SqlStmtLine) ast.Type { + c.inside_sql = true + defer { + c.inside_sql = false + } + c.ensure_type_exists(node.table_expr.typ, node.pos) or { return ast.void_type } + table_sym := c.table.get_type_symbol(node.table_expr.typ) + c.cur_orm_ts = table_sym + if table_sym.info !is ast.Struct { + c.error('unknown type `$table_sym.name`', node.pos) + return ast.void_type + } + info := table_sym.info as ast.Struct + fields := c.fetch_and_verify_orm_fields(info, node.table_expr.pos, table_sym.name) + mut sub_structs := map[int]ast.SqlStmtLine{} + for f in fields.filter((c.table.type_symbols[int(it.typ)].kind == .struct_) + || (c.table.get_type_symbol(it.typ).kind == .array + && c.table.get_type_symbol(c.table.get_type_symbol(it.typ).array_info().elem_type).kind == .struct_)) { + typ := if c.table.get_type_symbol(f.typ).kind == .struct_ { + f.typ + } else if c.table.get_type_symbol(f.typ).kind == .array { + c.table.get_type_symbol(f.typ).array_info().elem_type + } else { + ast.Type(0) + } + mut object_var_name := '${node.object_var_name}.$f.name' + if typ != f.typ { + object_var_name = node.object_var_name + } + mut n := ast.SqlStmtLine{ + pos: node.pos + kind: node.kind + table_expr: ast.TypeNode{ + pos: node.table_expr.pos + typ: typ + } + object_var_name: object_var_name + } + tmp_inside_sql := c.inside_sql + c.sql_stmt_line(mut n) + c.inside_sql = tmp_inside_sql + sub_structs[typ] = n + } + node.fields = fields + node.sub_structs = sub_structs.move() + for i, column in node.updated_columns { + field := node.fields.filter(it.name == column)[0] + node.updated_columns[i] = c.fetch_field_name(field) + } + if node.kind == .update { + for expr in node.update_exprs { + c.expr(expr) + } + } + if node.where_expr !is ast.EmptyExpr { + c.expr(node.where_expr) + } + + return ast.void_type +} + +fn (mut c Checker) fetch_and_verify_orm_fields(info ast.Struct, pos token.Position, table_name string) []ast.StructField { + fields := info.fields.filter((it.typ in [ast.string_type, ast.int_type, ast.bool_type] + || c.table.type_symbols[int(it.typ)].kind == .struct_ + || (c.table.get_type_symbol(it.typ).kind == .array + && c.table.get_type_symbol(c.table.get_type_symbol(it.typ).array_info().elem_type).kind == .struct_)) + && !it.attrs.contains('skip')) + if fields.len == 0 { + c.error('V orm: select: empty fields in `$table_name`', pos) + return []ast.StructField{} + } + if fields[0].name != 'id' { + c.error('V orm: `id int` must be the first field in `$table_name`', pos) + } + return fields +} + +fn (mut c Checker) fetch_field_name(field ast.StructField) string { + mut name := field.name + for attr in field.attrs { + if attr.kind == .string && attr.name == 'sql' && attr.arg != '' { + name = attr.arg + break + } + } + sym := c.table.get_type_symbol(field.typ) + if sym.kind == .struct_ { + name = '${name}_id' + } + return name +} + +fn (mut c Checker) post_process_generic_fns() { + // Loop thru each generic function concrete type. + // Check each specific fn instantiation. + for i in 0 .. c.file.generic_fns.len { + mut node := c.file.generic_fns[i] + c.mod = node.mod + for concrete_types in c.table.fn_generic_types[node.name] { + c.table.cur_concrete_types = concrete_types + c.fn_decl(mut node) + if node.name == 'vweb.run' { + for ct in concrete_types { + if ct !in c.vweb_gen_types { + c.vweb_gen_types << ct + } + } + } + } + c.table.cur_concrete_types = [] + } +} + +fn (mut c Checker) evaluate_once_comptime_if_attribute(mut a ast.Attr) bool { + if a.ct_evaled { + return a.ct_skip + } + if a.ct_expr is ast.Ident { + if a.ct_opt { + if a.ct_expr.name in checker.valid_comp_not_user_defined { + c.error('optional `[if expression ?]` tags, can be used only for user defined identifiers', + a.pos) + a.ct_skip = true + } else { + a.ct_skip = a.ct_expr.name !in c.pref.compile_defines + } + a.ct_evaled = true + return a.ct_skip + } else { + if a.ct_expr.name !in checker.valid_comp_not_user_defined { + c.note('`[if $a.ct_expr.name]` is deprecated. Use `[if $a.ct_expr.name ?]` instead', + a.pos) + a.ct_skip = a.ct_expr.name !in c.pref.compile_defines + a.ct_evaled = true + return a.ct_skip + } else { + if a.ct_expr.name in c.pref.compile_defines { + // explicitly allow custom user overrides with `-d linux` for example, for easier testing: + a.ct_skip = false + a.ct_evaled = true + return a.ct_skip + } + } + } + } + c.inside_ct_attr = true + a.ct_skip = c.comp_if_branch(a.ct_expr, a.pos) + c.inside_ct_attr = false + a.ct_evaled = true + return a.ct_skip +} + +fn (mut c Checker) fn_decl(mut node ast.FnDecl) { + if node.generic_names.len > 0 && c.table.cur_concrete_types.len == 0 { + // Just remember the generic function for now. + // It will be processed later in c.post_process_generic_fns, + // after all other normal functions are processed. + // This is done so that all generic function calls can + // have a chance to populate c.table.fn_generic_types with + // the correct concrete types. + c.file.generic_fns << node + return + } + // save all the state that fn_decl or inner statements/expressions + // could potentially modify, since functions can be nested, due to + // anonymous function support, and ensure that it is restored, when + // fn_decl returns: + prev_fn_scope := c.fn_scope + prev_in_for_count := c.in_for_count + prev_inside_defer := c.inside_defer + prev_inside_unsafe := c.inside_unsafe + prev_inside_anon_fn := c.inside_anon_fn + prev_returns := c.returns + c.fn_level++ + c.in_for_count = 0 + c.inside_defer = false + c.inside_unsafe = false + c.returns = false + defer { + c.fn_level-- + c.returns = prev_returns + c.inside_anon_fn = prev_inside_anon_fn + c.inside_unsafe = prev_inside_unsafe + c.inside_defer = prev_inside_defer + c.in_for_count = prev_in_for_count + c.fn_scope = prev_fn_scope + } + // Check generics fn/method without generic type parameters + mut need_generic_names := false + if node.generic_names.len == 0 { + if node.return_type.has_flag(.generic) { + need_generic_names = true + } else { + for param in node.params { + if param.typ.has_flag(.generic) { + need_generic_names = true + break + } + } + } + if need_generic_names { + c.error('generic function declaration must specify generic type names, e.g. foo', + node.pos) + } + } + if node.language == .v && !c.is_builtin_mod && !node.is_anon { + c.check_valid_snake_case(node.name, 'function name', node.pos) + } + if node.name == 'main.main' { + c.main_fn_decl_node = node + } + if node.return_type != ast.void_type { + if ct_attr_idx := node.attrs.find_comptime_define() { + sexpr := node.attrs[ct_attr_idx].ct_expr.str() + c.error('only functions that do NOT return values can have `[if $sexpr]` tags', + node.pos) + } + if node.generic_names.len > 0 { + gs := c.table.get_type_symbol(node.return_type) + if gs.info is ast.Struct { + if gs.info.is_generic && !node.return_type.has_flag(.generic) { + c.error('return generic struct in fn declaration must specify the generic type names, e.g. Foo', + node.return_type_pos) + } + } + } + return_sym := c.table.get_type_symbol(node.return_type) + if return_sym.info is ast.MultiReturn { + for multi_type in return_sym.info.types { + multi_sym := c.table.get_type_symbol(multi_type) + if multi_type == ast.error_type { + c.error('type `IError` cannot be used in multi-return, return an option instead', + node.return_type_pos) + } else if multi_type.has_flag(.optional) { + c.error('option cannot be used in multi-return, return an option instead', + node.return_type_pos) + } else if multi_sym.kind == .array_fixed { + c.error('fixed array cannot be used in multi-return', node.return_type_pos) + } + } + } else if return_sym.kind == .array_fixed { + c.error('fixed array cannot be returned by function', node.return_type_pos) + } + } else { + for mut a in node.attrs { + if a.kind == .comptime_define { + c.evaluate_once_comptime_if_attribute(mut a) + } + } + } + if node.is_method { + mut sym := c.table.get_type_symbol(node.receiver.typ) + if sym.kind == .array && !c.is_builtin_mod && node.name == 'map' { + // TODO `node.map in array_builtin_methods` + c.error('method overrides built-in array method', node.pos) + } else if sym.kind == .sum_type && node.name == 'type_name' { + c.error('method overrides built-in sum type method', node.pos) + } else if sym.kind == .multi_return { + c.error('cannot define method on multi-value', node.method_type_pos) + } + if sym.name.len == 1 { + // One letter types are reserved for generics. + c.error('unknown type `$sym.name`', node.receiver_pos) + return + } + // make sure interface does not implement its own interface methods + if sym.kind == .interface_ && sym.has_method(node.name) { + if mut sym.info is ast.Interface { + // if the method is in info.methods then it is an interface method + if sym.info.has_method(node.name) { + c.error('interface `$sym.name` cannot implement its own interface method `$node.name`', + node.pos) + } + } + } + if mut sym.info is ast.Struct { + if field := c.table.find_field(sym, node.name) { + field_sym := c.table.get_type_symbol(field.typ) + if field_sym.kind == .function { + c.error('type `$sym.name` has both field and method named `$node.name`', + node.pos) + } + } + } + // needed for proper error reporting during vweb route checking + if node.method_idx < sym.methods.len { + sym.methods[node.method_idx].source_fn = voidptr(node) + } else { + c.error('method index: $node.method_idx >= sym.methods.len: $sym.methods.len', + node.pos) + } + } + if node.language == .v { + // Make sure all types are valid + for arg in node.params { + c.ensure_type_exists(arg.typ, arg.type_pos) or { return } + if !arg.typ.is_ptr() { // value parameter, i.e. on stack - check for `[heap]` + arg_typ_sym := c.table.get_type_symbol(arg.typ) + if arg_typ_sym.kind == .struct_ { + info := arg_typ_sym.info as ast.Struct + if info.is_heap { // set auto_heap to promote value parameter + mut v := node.scope.find_var(arg.name) or { continue } + v.is_auto_heap = true + } + } + } + } + } + if node.language == .v && node.name.after_char(`.`) == 'init' && !node.is_method + && node.params.len == 0 { + if node.is_pub { + c.error('fn `init` must not be public', node.pos) + } + if node.return_type != ast.void_type { + c.error('fn `init` cannot have a return type', node.pos) + } + } + if node.return_type != ast.Type(0) { + c.ensure_type_exists(node.return_type, node.return_type_pos) or { return } + if node.language == .v && node.is_method && node.name == 'str' { + if node.return_type != ast.string_type { + c.error('.str() methods should return `string`', node.pos) + } + if node.params.len != 1 { + c.error('.str() methods should have 0 arguments', node.pos) + } + } + if node.language == .v && node.is_method + && node.name in ['+', '-', '*', '%', '/', '<', '=='] { + if node.params.len != 2 { + c.error('operator methods should have exactly 1 argument', node.pos) + } else { + receiver_sym := c.table.get_type_symbol(node.receiver.typ) + param_sym := c.table.get_type_symbol(node.params[1].typ) + if param_sym.kind == .string && receiver_sym.kind == .string { + // bypass check for strings + // TODO there must be a better way to handle that + } else if param_sym.kind !in [.struct_, .alias] + || receiver_sym.kind !in [.struct_, .alias] { + c.error('operator methods are only allowed for struct and type alias', + node.pos) + } else { + parent_sym := c.table.get_final_type_symbol(node.receiver.typ) + if node.rec_mut { + c.error('receiver cannot be `mut` for operator overloading', node.receiver_pos) + } else if node.params[1].is_mut { + c.error('argument cannot be `mut` for operator overloading', node.pos) + } else if node.receiver.typ != node.params[1].typ { + c.error('expected `$receiver_sym.name` not `$param_sym.name` - both operands must be the same type for operator overloading', + node.params[1].type_pos) + } else if node.name in ['<', '=='] && node.return_type != ast.bool_type { + c.error('operator comparison methods should return `bool`', node.pos) + } else if parent_sym.is_primitive() { + c.error('cannot define operator methods on type alias for `$parent_sym.name`', + node.pos) + } + } + } + } + } + // TODO c.pref.is_vet + if node.language == .v && !node.is_method && node.params.len == 0 && node.is_test { + if !c.pref.is_test { + // simple heuristic + for st in node.stmts { + if st is ast.AssertStmt { + c.warn('tests will not be run, because filename does not end with `_test.v`', + node.pos) + break + } + } + } + if node.return_type != ast.void_type_idx + && node.return_type.clear_flag(.optional) != ast.void_type_idx { + c.error('test functions should either return nothing at all, or be marked to return `?`', + node.pos) + } + } + c.expected_type = ast.void_type + c.table.cur_fn = unsafe { node } + // c.table.cur_fn = node + // Add return if `fn(...) ? {...}` have no return at end + if node.return_type != ast.void_type && node.return_type.has_flag(.optional) + && (node.stmts.len == 0 || node.stmts[node.stmts.len - 1] !is ast.Return) { + sym := c.table.get_type_symbol(node.return_type) + if sym.kind == .void { + node.stmts << ast.Return{ + pos: node.pos + } + } + } + c.fn_scope = node.scope + c.stmts(node.stmts) + node_has_top_return := has_top_return(node.stmts) + node.has_return = c.returns || node_has_top_return + c.check_noreturn_fn_decl(mut node) + if node.language == .v && !node.no_body && node.return_type != ast.void_type && !node.has_return + && !node.is_noreturn { + if c.inside_anon_fn { + c.error('missing return at the end of an anonymous function', node.pos) + } else if !node.attrs.contains('_naked') { + c.error('missing return at end of function `$node.name`', node.pos) + } + } + if node.is_method { + sym := c.table.get_type_symbol(node.receiver.typ) + if sym.kind == .struct_ { + info := sym.info as ast.Struct + if info.is_generic && c.table.cur_fn.generic_names.len == 0 { + c.error('receiver must specify the generic type names, e.g. Foo', node.method_type_pos) + } + } + } + node.source_file = c.file +} + +// NB: has_top_return/1 should be called on *already checked* stmts, +// which do have their stmt.expr.is_noreturn set properly: +fn has_top_return(stmts []ast.Stmt) bool { + for stmt in stmts { + match stmt { + ast.Return { + return true + } + ast.Block { + if has_top_return(stmt.stmts) { + return true + } + } + ast.ExprStmt { + if stmt.expr is ast.CallExpr { + if stmt.expr.is_noreturn { + return true + } + } + } + else {} + } + } + return false +} + +fn (mut c Checker) verify_vweb_params_for_method(m ast.Fn) (bool, int, int) { + margs := m.params.len - 1 // first arg is the receiver/this + if m.attrs.len == 0 { + // allow non custom routed methods, with 1:1 mapping + return true, -1, margs + } + if m.params.len > 1 { + for param in m.params[1..] { + param_sym := c.table.get_final_type_symbol(param.typ) + if !(param_sym.is_string() || param_sym.is_number() || param_sym.is_float() + || param_sym.kind == .bool) { + c.error('invalid type `$param_sym.name` for parameter `$param.name` in vweb app method `$m.name`', + param.pos) + } + } + } + mut route_attributes := 0 + for a in m.attrs { + if a.name.starts_with('/') { + route_attributes += a.name.count(':') + } + } + return route_attributes == margs, route_attributes, margs +} + +fn (mut c Checker) verify_all_vweb_routes() { + if c.vweb_gen_types.len == 0 { + return + } + c.table.used_vweb_types = c.vweb_gen_types + typ_vweb_result := c.table.find_type_idx('vweb.Result') + old_file := c.file + for vgt in c.vweb_gen_types { + sym_app := c.table.get_type_symbol(vgt) + for m in sym_app.methods { + if m.return_type == typ_vweb_result { + is_ok, nroute_attributes, nargs := c.verify_vweb_params_for_method(m) + if !is_ok { + f := &ast.FnDecl(m.source_fn) + if isnil(f) { + continue + } + if f.return_type == typ_vweb_result && f.receiver.typ == m.params[0].typ + && f.name == m.name { + c.change_current_file(f.source_file) // setup of file path for the warning + c.warn('mismatched parameters count between vweb method `${sym_app.name}.$m.name` ($nargs) and route attribute $m.attrs ($nroute_attributes)', + f.pos) + } + } + } + } + } + c.change_current_file(old_file) +} + +fn (mut c Checker) trace(fbase string, message string) { + if c.file.path_base == fbase { + println('> c.trace | ${fbase:-10s} | $message') + } +} + +fn (mut c Checker) ensure_type_exists(typ ast.Type, pos token.Position) ? { + if typ == 0 { + c.error('unknown type', pos) + return + } + sym := c.table.get_type_symbol(typ) + match sym.kind { + .placeholder { + if sym.language == .v && !sym.name.starts_with('C.') { + c.error(util.new_suggestion(sym.name, c.table.known_type_names()).say('unknown type `$sym.name`'), + pos) + return + } + } + .int_literal, .float_literal { + // Separate error condition for `int_literal` and `float_literal` because `util.suggestion` may give different + // suggestions due to f32 comparision issue. + if !c.is_builtin_mod { + msg := if sym.kind == .int_literal { + 'unknown type `$sym.name`.\nDid you mean `int`?' + } else { + 'unknown type `$sym.name`.\nDid you mean `f64`?' + } + c.error(msg, pos) + return + } + } + .array { + c.ensure_type_exists((sym.info as ast.Array).elem_type, pos) ? + } + .array_fixed { + c.ensure_type_exists((sym.info as ast.ArrayFixed).elem_type, pos) ? + } + .map { + info := sym.info as ast.Map + c.ensure_type_exists(info.key_type, pos) ? + c.ensure_type_exists(info.value_type, pos) ? + } + else {} + } +} diff --git a/v_windows/v/old/vlib/v/checker/comptime_const_eval.v b/v_windows/v/old/vlib/v/checker/comptime_const_eval.v new file mode 100644 index 0000000..5bb0781 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/comptime_const_eval.v @@ -0,0 +1,159 @@ +module checker + +import v.ast + +fn eval_comptime_const_expr(expr ast.Expr, nlevel int) ?ast.ComptTimeConstValue { + if nlevel > 100 { + // protect against a too deep comptime eval recursion + return none + } + match expr { + ast.IntegerLiteral { + x := expr.val.u64() + if x > 9223372036854775807 { + return x + } + return expr.val.i64() + } + ast.StringLiteral { + return expr.val + } + ast.CharLiteral { + runes := expr.val.runes() + if runes.len > 0 { + return runes[0] + } + return none + } + ast.Ident { + if expr.obj is ast.ConstField { + // an existing constant? + return eval_comptime_const_expr(expr.obj.expr, nlevel + 1) + } + } + ast.CastExpr { + cast_expr_value := eval_comptime_const_expr(expr.expr, nlevel + 1) or { return none } + if expr.typ == ast.i8_type { + return cast_expr_value.i8() or { return none } + } + if expr.typ == ast.i16_type { + return cast_expr_value.i16() or { return none } + } + if expr.typ == ast.int_type { + return cast_expr_value.int() or { return none } + } + if expr.typ == ast.i64_type { + return cast_expr_value.i64() or { return none } + } + // + if expr.typ == ast.byte_type { + return cast_expr_value.byte() or { return none } + } + if expr.typ == ast.u16_type { + return cast_expr_value.u16() or { return none } + } + if expr.typ == ast.u32_type { + return cast_expr_value.u32() or { return none } + } + if expr.typ == ast.u64_type { + return cast_expr_value.u64() or { return none } + } + // + if expr.typ == ast.f32_type { + return cast_expr_value.f32() or { return none } + } + if expr.typ == ast.f64_type { + return cast_expr_value.f64() or { return none } + } + } + ast.InfixExpr { + left := eval_comptime_const_expr(expr.left, nlevel + 1) ? + right := eval_comptime_const_expr(expr.right, nlevel + 1) ? + if left is string && right is string { + match expr.op { + .plus { + return left + right + } + else { + return none + } + } + } else if left is u64 && right is i64 { + match expr.op { + .plus { return i64(left) + i64(right) } + .minus { return i64(left) - i64(right) } + .mul { return i64(left) * i64(right) } + .div { return i64(left) / i64(right) } + .mod { return i64(left) % i64(right) } + .xor { return i64(left) ^ i64(right) } + .pipe { return i64(left) | i64(right) } + .amp { return i64(left) & i64(right) } + .left_shift { return i64(left) << i64(right) } + .right_shift { return i64(left) >> i64(right) } + else { return none } + } + } else if left is i64 && right is u64 { + match expr.op { + .plus { return i64(left) + i64(right) } + .minus { return i64(left) - i64(right) } + .mul { return i64(left) * i64(right) } + .div { return i64(left) / i64(right) } + .mod { return i64(left) % i64(right) } + .xor { return i64(left) ^ i64(right) } + .pipe { return i64(left) | i64(right) } + .amp { return i64(left) & i64(right) } + .left_shift { return i64(left) << i64(right) } + .right_shift { return i64(left) >> i64(right) } + else { return none } + } + } else if left is u64 && right is u64 { + match expr.op { + .plus { return left + right } + .minus { return left - right } + .mul { return left * right } + .div { return left / right } + .mod { return left % right } + .xor { return left ^ right } + .pipe { return left | right } + .amp { return left & right } + .left_shift { return left << right } + .right_shift { return left >> right } + else { return none } + } + } else if left is i64 && right is i64 { + match expr.op { + .plus { return left + right } + .minus { return left - right } + .mul { return left * right } + .div { return left / right } + .mod { return left % right } + .xor { return left ^ right } + .pipe { return left | right } + .amp { return left & right } + .left_shift { return left << right } + .right_shift { return left >> right } + else { return none } + } + } else if left is byte && right is byte { + match expr.op { + .plus { return left + right } + .minus { return left - right } + .mul { return left * right } + .div { return left / right } + .mod { return left % right } + .xor { return left ^ right } + .pipe { return left | right } + .amp { return left & right } + .left_shift { return left << right } + .right_shift { return left >> right } + else { return none } + } + } + } + else { + // eprintln('>>> nlevel: $nlevel | another $expr.type_name() | $expr ') + return none + } + } + return none +} diff --git a/v_windows/v/old/vlib/v/checker/noreturn.v b/v_windows/v/old/vlib/v/checker/noreturn.v new file mode 100644 index 0000000..ca4a288 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/noreturn.v @@ -0,0 +1,112 @@ +module checker + +import v.ast + +fn (mut c Checker) check_noreturn_fn_decl(mut node ast.FnDecl) { + if !node.is_noreturn { + return + } + if node.no_body { + return + } + if uses_return_stmt(node.stmts) { + c.error('[noreturn] functions cannot use return statements', node.pos) + } + if node.return_type != ast.void_type { + c.error('[noreturn] functions cannot have return types', node.pos) + } else { + if node.stmts.len != 0 { + mut is_valid_end_of_noreturn_fn := false + last_stmt := node.stmts.last() + match last_stmt { + ast.ExprStmt { + if last_stmt.expr is ast.CallExpr { + if last_stmt.expr.should_be_skipped { + c.error('[noreturn] functions cannot end with a skippable `[if ..]` call', + last_stmt.pos) + } + if last_stmt.expr.is_noreturn { + is_valid_end_of_noreturn_fn = true + } + } + } + ast.ForStmt { + if last_stmt.is_inf && last_stmt.stmts.len == 0 { + is_valid_end_of_noreturn_fn = true + } + } + else {} + } + if !is_valid_end_of_noreturn_fn { + c.error('[noreturn] functions should end with a call to another [noreturn] function, or with an infinite `for {}` loop', + last_stmt.pos) + } + } + } +} + +fn uses_return_stmt(stmts []ast.Stmt) bool { + if stmts.len == 0 { + return false + } + for stmt in stmts { + match stmt { + ast.Return { + return true + } + ast.Block { + if uses_return_stmt(stmt.stmts) { + return true + } + } + ast.ExprStmt { + match stmt.expr { + ast.CallExpr { + if uses_return_stmt(stmt.expr.or_block.stmts) { + return true + } + } + ast.MatchExpr { + for b in stmt.expr.branches { + if uses_return_stmt(b.stmts) { + return true + } + } + } + ast.SelectExpr { + for b in stmt.expr.branches { + if uses_return_stmt(b.stmts) { + return true + } + } + } + ast.IfExpr { + for b in stmt.expr.branches { + if uses_return_stmt(b.stmts) { + return true + } + } + } + else {} + } + } + ast.ForStmt { + if uses_return_stmt(stmt.stmts) { + return true + } + } + ast.ForCStmt { + if uses_return_stmt(stmt.stmts) { + return true + } + } + ast.ForInStmt { + if uses_return_stmt(stmt.stmts) { + return true + } + } + else {} + } + } + return false +} diff --git a/v_windows/v/old/vlib/v/checker/tests/.gitattributes b/v_windows/v/old/vlib/v/checker/tests/.gitattributes new file mode 100644 index 0000000..20f8e01 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/.gitattributes @@ -0,0 +1,2 @@ +*_crlf_* binary +*_lf_* binary diff --git a/v_windows/v/old/vlib/v/checker/tests/.gitignore b/v_windows/v/old/vlib/v/checker/tests/.gitignore new file mode 100644 index 0000000..868d19e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/.gitignore @@ -0,0 +1,4 @@ +*.v +*.c +!*_test.v +!modules/**/*.v \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/a_test_file_with_0_test_fns_test.out b/v_windows/v/old/vlib/v/checker/tests/a_test_file_with_0_test_fns_test.out new file mode 100644 index 0000000..3734061 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/a_test_file_with_0_test_fns_test.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/a_test_file_with_0_test_fns_test.vv:1:1: error: a _test.v file should have *at least* one `test_` function + 1 | fn abc() {} + | ^ +Details: The name of a test function in V, should start with `test_`. +The test function should take 0 parameters, and no return type. Example: +fn test_xyz(){ assert 2 + 2 == 4 } diff --git a/v_windows/v/old/vlib/v/checker/tests/a_test_file_with_0_test_fns_test.vv b/v_windows/v/old/vlib/v/checker/tests/a_test_file_with_0_test_fns_test.vv new file mode 100644 index 0000000..45f81ae --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/a_test_file_with_0_test_fns_test.vv @@ -0,0 +1 @@ +fn abc() {} diff --git a/v_windows/v/old/vlib/v/checker/tests/add_op_wrong_type_err.out b/v_windows/v/old/vlib/v/checker/tests/add_op_wrong_type_err.out new file mode 100644 index 0000000..c1c8699 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/add_op_wrong_type_err.out @@ -0,0 +1,42 @@ +vlib/v/checker/tests/add_op_wrong_type_err.vv:3:13: error: mismatched types `Aaa` and `int literal` + 1 | struct Aaa{} + 2 | fn main() { + 3 | println(Aaa{} + 10) + | ~~~~~~~~~~ + 4 | println(10 + Aaa{}) + 5 | println([1,2,3] + 10) +vlib/v/checker/tests/add_op_wrong_type_err.vv:4:13: error: mismatched types `int literal` and `Aaa` + 2 | fn main() { + 3 | println(Aaa{} + 10) + 4 | println(10 + Aaa{}) + | ~~~~~~~~~~ + 5 | println([1,2,3] + 10) + 6 | println(10 + [1,2,3]) +vlib/v/checker/tests/add_op_wrong_type_err.vv:5:13: error: mismatched types `[]int` and `int literal` + 3 | println(Aaa{} + 10) + 4 | println(10 + Aaa{}) + 5 | println([1,2,3] + 10) + | ~~~~~~~~~~~~ + 6 | println(10 + [1,2,3]) + 7 | a := map[string]int +vlib/v/checker/tests/add_op_wrong_type_err.vv:6:13: error: mismatched types `int literal` and `[]int` + 4 | println(10 + Aaa{}) + 5 | println([1,2,3] + 10) + 6 | println(10 + [1,2,3]) + | ~~~~~~~~~~~~ + 7 | a := map[string]int + 8 | println(a + 10) +vlib/v/checker/tests/add_op_wrong_type_err.vv:8:13: error: mismatched types `map[string]int` and `int literal` + 6 | println(10 + [1,2,3]) + 7 | a := map[string]int + 8 | println(a + 10) + | ~~~~~~ + 9 | println(10 + a) + 10 | } +vlib/v/checker/tests/add_op_wrong_type_err.vv:9:13: error: mismatched types `int literal` and `map[string]int` + 7 | a := map[string]int + 8 | println(a + 10) + 9 | println(10 + a) + | ~~~~~~ + 10 | } + diff --git a/v_windows/v/old/vlib/v/checker/tests/add_op_wrong_type_err.vv b/v_windows/v/old/vlib/v/checker/tests/add_op_wrong_type_err.vv new file mode 100644 index 0000000..f02798b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/add_op_wrong_type_err.vv @@ -0,0 +1,10 @@ +struct Aaa{} +fn main() { + println(Aaa{} + 10) + println(10 + Aaa{}) + println([1,2,3] + 10) + println(10 + [1,2,3]) + a := map[string]int + println(a + 10) + println(10 + a) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/alias_array_unknown_op_overloading_err.out b/v_windows/v/old/vlib/v/checker/tests/alias_array_unknown_op_overloading_err.out new file mode 100644 index 0000000..34cfbdb --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/alias_array_unknown_op_overloading_err.out @@ -0,0 +1,13 @@ +vlib/v/checker/tests/alias_array_unknown_op_overloading_err.vv:17:7: error: undefined operation `Tuple` - `Tuple` + 15 | mut a := new_tuple(12, 4.5, 6.7, 6) + 16 | b := new_tuple(12, 4.5, 6.7, 6) + 17 | a -= b + | ~~ + 18 | println(a - b) + 19 | } +vlib/v/checker/tests/alias_array_unknown_op_overloading_err.vv:18:13: error: undefined operation `Tuple` - `Tuple` + 16 | b := new_tuple(12, 4.5, 6.7, 6) + 17 | a -= b + 18 | println(a - b) + | ~~~~~ + 19 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/alias_array_unknown_op_overloading_err.vv b/v_windows/v/old/vlib/v/checker/tests/alias_array_unknown_op_overloading_err.vv new file mode 100644 index 0000000..84d7034 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/alias_array_unknown_op_overloading_err.vv @@ -0,0 +1,19 @@ +type Tuple = []f64 + +fn new_tuple(x f64, y f64, z f64, w f64) Tuple { + return Tuple([x, y, z, w]) +} + +fn (a Tuple) + (b Tuple) Tuple { + mut res := []f64{len: a.len} + for i := 0; i < a.len; i++ { + res[i] = a[i] + b[i] + } + return Tuple(res) +} +fn main() { + mut a := new_tuple(12, 4.5, 6.7, 6) + b := new_tuple(12, 4.5, 6.7, 6) + a -= b + println(a - b) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/alias_map_unknown_op_overloading_err.out b/v_windows/v/old/vlib/v/checker/tests/alias_map_unknown_op_overloading_err.out new file mode 100644 index 0000000..6a93514 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/alias_map_unknown_op_overloading_err.out @@ -0,0 +1,13 @@ +vlib/v/checker/tests/alias_map_unknown_op_overloading_err.vv:19:10: error: undefined operation `Map` - `Map` + 17 | mut a := new_map() + 18 | b := new_map() + 19 | println(a - b) + | ~~~~~ + 20 | a -= b + 21 | } +vlib/v/checker/tests/alias_map_unknown_op_overloading_err.vv:20:7: error: undefined operation `Map` - `Map` + 18 | b := new_map() + 19 | println(a - b) + 20 | a -= b + | ~~ + 21 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/alias_map_unknown_op_overloading_err.vv b/v_windows/v/old/vlib/v/checker/tests/alias_map_unknown_op_overloading_err.vv new file mode 100644 index 0000000..bea0261 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/alias_map_unknown_op_overloading_err.vv @@ -0,0 +1,21 @@ +type Map = map[string]string + +pub fn new_map() Map { + return Map(map{ + '23': 'str' + }) +} + +fn (a Map) + (b Map) Map { + str := b['23'] + return Map(map{ + '34': str + '12' + }) +} + +fn main() { + mut a := new_map() + b := new_map() + println(a - b) + a -= b +} diff --git a/v_windows/v/old/vlib/v/checker/tests/alias_type_exists.out b/v_windows/v/old/vlib/v/checker/tests/alias_type_exists.out new file mode 100644 index 0000000..d79df8a --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/alias_type_exists.out @@ -0,0 +1,3 @@ +vlib/v/checker/tests/alias_type_exists.vv:1:15: error: unknown type `Bird` + 1 | type Pigeon = Bird + | ~~~~ \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/alias_type_exists.vv b/v_windows/v/old/vlib/v/checker/tests/alias_type_exists.vv new file mode 100644 index 0000000..da63abd --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/alias_type_exists.vv @@ -0,0 +1 @@ +type Pigeon = Bird diff --git a/v_windows/v/old/vlib/v/checker/tests/ambiguous_field_method_err.out b/v_windows/v/old/vlib/v/checker/tests/ambiguous_field_method_err.out new file mode 100644 index 0000000..019eef5 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/ambiguous_field_method_err.out @@ -0,0 +1,13 @@ +vlib/v/checker/tests/ambiguous_field_method_err.vv:22:4: error: ambiguous method `test` + 20 | fn main() { + 21 | b := Bar{} + 22 | b.test() + | ~~~~~~ + 23 | n := b.name + 24 | } +vlib/v/checker/tests/ambiguous_field_method_err.vv:23:9: error: ambiguous field `name` + 21 | b := Bar{} + 22 | b.test() + 23 | n := b.name + | ~~~~ + 24 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/ambiguous_field_method_err.vv b/v_windows/v/old/vlib/v/checker/tests/ambiguous_field_method_err.vv new file mode 100644 index 0000000..fa32755 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/ambiguous_field_method_err.vv @@ -0,0 +1,24 @@ +struct Foo { + name int = 5 +} +struct Bar { + Foo + Foo2 +} + +struct Foo2 { + name string +} +fn (f Foo2) test() { + println(f) +} + +fn (f Foo) test() { + println(f) +} + +fn main() { + b := Bar{} + b.test() + n := b.name +} \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/ambiguous_function_call.out b/v_windows/v/old/vlib/v/checker/tests/ambiguous_function_call.out new file mode 100644 index 0000000..b88ef1a --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/ambiguous_function_call.out @@ -0,0 +1,13 @@ +vlib/v/checker/tests/ambiguous_function_call.vv:2:2: error: ambiguous call to: `foo1`, may refer to fn `foo1` or variable `foo1` + 1 | fn foo1(foo1 int) { + 2 | foo1(foo1 + 1) + | ~~~~~~~~~~~~~~ + 3 | } + 4 | +vlib/v/checker/tests/ambiguous_function_call.vv:7:2: error: ambiguous call to: `foo2`, may refer to fn `foo2` or variable `foo2` + 5 | fn foo2() { + 6 | foo2 := 1 + 7 | foo2(foo2) + | ~~~~~~~~~~ + 8 | } + 9 | diff --git a/v_windows/v/old/vlib/v/checker/tests/ambiguous_function_call.vv b/v_windows/v/old/vlib/v/checker/tests/ambiguous_function_call.vv new file mode 100644 index 0000000..2d21224 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/ambiguous_function_call.vv @@ -0,0 +1,13 @@ +fn foo1(foo1 int) { + foo1(foo1 + 1) +} + +fn foo2() { + foo2 := 1 + foo2(foo2) +} + +fn main() { + foo1(5) + foo2() +} diff --git a/v_windows/v/old/vlib/v/checker/tests/any_int_float_ban_err.out b/v_windows/v/old/vlib/v/checker/tests/any_int_float_ban_err.out new file mode 100644 index 0000000..8b9eb5d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/any_int_float_ban_err.out @@ -0,0 +1,45 @@ +vlib/v/checker/tests/any_int_float_ban_err.vv:1:12: error: unknown type `int_literal` + 1 | type Foo = int_literal | float_literal + | ~~~~~~~~~~~ + 2 | type Fo2 = int_literal + 3 | +vlib/v/checker/tests/any_int_float_ban_err.vv:2:12: error: unknown type `int_literal` + 1 | type Foo = int_literal | float_literal + 2 | type Fo2 = int_literal + | ~~~~~~~~~~~ + 3 | + 4 | struct Int { +vlib/v/checker/tests/any_int_float_ban_err.vv:5:7: error: unknown type `int_literal` + 3 | + 4 | struct Int { + 5 | i int_literal + | ~~~~~~~~~~~ + 6 | f float_literal + 7 | } +vlib/v/checker/tests/any_int_float_ban_err.vv:6:7: error: unknown type `float_literal` + 4 | struct Int { + 5 | i int_literal + 6 | f float_literal + | ~~~~~~~~~~~~~ + 7 | } + 8 | +vlib/v/checker/tests/any_int_float_ban_err.vv:9:10: error: unknown type `int_literal` + 7 | } + 8 | + 9 | fn foo(i int_literal) int_literal { + | ~~~~~~~~~~~ + 10 | return i + 11 | } +vlib/v/checker/tests/any_int_float_ban_err.vv:13:11: error: unknown type `int_literal` + 11 | } + 12 | + 13 | fn foo2() int_literal { + | ~~~~~~~~~~~ + 14 | return 1 + 15 | } +vlib/v/checker/tests/any_int_float_ban_err.vv:14:12: error: cannot use `int literal` as type `int_literal` in return argument + 12 | + 13 | fn foo2() int_literal { + 14 | return 1 + | ^ + 15 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/any_int_float_ban_err.vv b/v_windows/v/old/vlib/v/checker/tests/any_int_float_ban_err.vv new file mode 100644 index 0000000..15fab98 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/any_int_float_ban_err.vv @@ -0,0 +1,15 @@ +type Foo = int_literal | float_literal +type Fo2 = int_literal + +struct Int { + i int_literal + f float_literal +} + +fn foo(i int_literal) int_literal { + return i +} + +fn foo2() int_literal { + return 1 +} diff --git a/v_windows/v/old/vlib/v/checker/tests/append_err.out b/v_windows/v/old/vlib/v/checker/tests/append_err.out new file mode 100644 index 0000000..4c87f24 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/append_err.out @@ -0,0 +1,20 @@ +vlib/v/checker/tests/append_err.vv:3:7: error: cannot append `string` to `[]int` + 1 | fn main() { + 2 | mut l := []int{} + 3 | l << 'test' + | ~~~~~~ + 4 | + 5 | _ = l << 3 +vlib/v/checker/tests/append_err.vv:5:8: error: array append cannot be used in an expression + 3 | l << 'test' + 4 | + 5 | _ = l << 3 + | ~~ + 6 | _ = (l << 3).len + 7 | } +vlib/v/checker/tests/append_err.vv:6:9: error: array append cannot be used in an expression + 4 | + 5 | _ = l << 3 + 6 | _ = (l << 3).len + | ~~ + 7 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/append_err.vv b/v_windows/v/old/vlib/v/checker/tests/append_err.vv new file mode 100644 index 0000000..bc506b6 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/append_err.vv @@ -0,0 +1,7 @@ +fn main() { + mut l := []int{} + l << 'test' + + _ = l << 3 + _ = (l << 3).len +} diff --git a/v_windows/v/old/vlib/v/checker/tests/array_append_array_type_mismatch_err.out b/v_windows/v/old/vlib/v/checker/tests/array_append_array_type_mismatch_err.out new file mode 100644 index 0000000..0cd37f3 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_append_array_type_mismatch_err.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/array_append_array_type_mismatch_err.vv:3:8: error: cannot append `[]int` to `[]byte` + 1 | fn main() { + 2 | mut bc := []byte{} + 3 | bc << [0xCA, 0xFE, 0xBA, 0xBE] + | ~~~~~~~~~~~~~~~~~~~~~~~~ + 4 | println(bc) + 5 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/array_append_array_type_mismatch_err.vv b/v_windows/v/old/vlib/v/checker/tests/array_append_array_type_mismatch_err.vv new file mode 100644 index 0000000..45de913 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_append_array_type_mismatch_err.vv @@ -0,0 +1,5 @@ +fn main() { + mut bc := []byte{} + bc << [0xCA, 0xFE, 0xBA, 0xBE] + println(bc) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/array_builtin_redefinition.out b/v_windows/v/old/vlib/v/checker/tests/array_builtin_redefinition.out new file mode 100644 index 0000000..da5c05d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_builtin_redefinition.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/array_builtin_redefinition.vv:7:1: error: method overrides built-in array method + 5 | // fn (a []Abc) repeat() int { return 0 } + 6 | // fn (a []Abc) reverse() int { return 0 } + 7 | fn (a []Abc) map() int { return 0 } + | ~~~~~~~~~~~~~~~~~~~~~~ + 8 | // fn (a []Abc) slice() int { return 0 } + 9 | // fn (a []Abc) sort() int { return 0 } diff --git a/v_windows/v/old/vlib/v/checker/tests/array_builtin_redefinition.vv b/v_windows/v/old/vlib/v/checker/tests/array_builtin_redefinition.vv new file mode 100644 index 0000000..809b86a --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_builtin_redefinition.vv @@ -0,0 +1,11 @@ +type Abc = int + +// fn (a []Abc) filter() int { return 0 } +// fn (a []Abc) clone() int { return 0 } +// fn (a []Abc) repeat() int { return 0 } +// fn (a []Abc) reverse() int { return 0 } +fn (a []Abc) map() int { return 0 } +// fn (a []Abc) slice() int { return 0 } +// fn (a []Abc) sort() int { return 0 } +// fn (a []Abc) contains() int { return 0 } +// fn (a []Abc) index() int { return 0 } diff --git a/v_windows/v/old/vlib/v/checker/tests/array_cmp_err.out b/v_windows/v/old/vlib/v/checker/tests/array_cmp_err.out new file mode 100644 index 0000000..885a514 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_cmp_err.out @@ -0,0 +1,26 @@ +vlib/v/checker/tests/array_cmp_err.vv:2:26: error: only `==` and `!=` are defined on arrays + 1 | fn main() { + 2 | println([2, 3, 4, 6] < [2, 5]) + | ^ + 3 | println([2, 3, 4, 6] > [2, 5]) + 4 | println([2, 3, 1, 6] >= [3, 5, 7]) +vlib/v/checker/tests/array_cmp_err.vv:3:26: error: only `==` and `!=` are defined on arrays + 1 | fn main() { + 2 | println([2, 3, 4, 6] < [2, 5]) + 3 | println([2, 3, 4, 6] > [2, 5]) + | ^ + 4 | println([2, 3, 1, 6] >= [3, 5, 7]) + 5 | println([2, 3, 6, 8] <= [2, 5, 8, 9]) +vlib/v/checker/tests/array_cmp_err.vv:4:26: error: only `==` and `!=` are defined on arrays + 2 | println([2, 3, 4, 6] < [2, 5]) + 3 | println([2, 3, 4, 6] > [2, 5]) + 4 | println([2, 3, 1, 6] >= [3, 5, 7]) + | ~~ + 5 | println([2, 3, 6, 8] <= [2, 5, 8, 9]) + 6 | } +vlib/v/checker/tests/array_cmp_err.vv:5:26: error: only `==` and `!=` are defined on arrays + 3 | println([2, 3, 4, 6] > [2, 5]) + 4 | println([2, 3, 1, 6] >= [3, 5, 7]) + 5 | println([2, 3, 6, 8] <= [2, 5, 8, 9]) + | ~~ + 6 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/array_cmp_err.vv b/v_windows/v/old/vlib/v/checker/tests/array_cmp_err.vv new file mode 100644 index 0000000..ec60eb9 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_cmp_err.vv @@ -0,0 +1,6 @@ +fn main() { + println([2, 3, 4, 6] < [2, 5]) + println([2, 3, 4, 6] > [2, 5]) + println([2, 3, 1, 6] >= [3, 5, 7]) + println([2, 3, 6, 8] <= [2, 5, 8, 9]) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/array_declare_element_a.out b/v_windows/v/old/vlib/v/checker/tests/array_declare_element_a.out new file mode 100644 index 0000000..15335cc --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_declare_element_a.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/array_declare_element_a.vv:2:5: error: non-name `arr[0]` on left side of `:=` + 1 | fn main() { + 2 | arr[0] := 2 + | ~~~ + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/array_declare_element_a.vv b/v_windows/v/old/vlib/v/checker/tests/array_declare_element_a.vv new file mode 100644 index 0000000..ac07498 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_declare_element_a.vv @@ -0,0 +1,3 @@ +fn main() { + arr[0] := 2 +} diff --git a/v_windows/v/old/vlib/v/checker/tests/array_declare_element_b.out b/v_windows/v/old/vlib/v/checker/tests/array_declare_element_b.out new file mode 100644 index 0000000..a898165 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_declare_element_b.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/array_declare_element_b.vv:3:5: error: non-name `arr[1]` on left side of `:=` + 1 | fn main() { + 2 | arr := [1, 2] + 3 | arr[1] := 1 + | ~~~ + 4 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/array_declare_element_b.vv b/v_windows/v/old/vlib/v/checker/tests/array_declare_element_b.vv new file mode 100644 index 0000000..a87a0e5 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_declare_element_b.vv @@ -0,0 +1,4 @@ +fn main() { + arr := [1, 2] + arr[1] := 1 +} diff --git a/v_windows/v/old/vlib/v/checker/tests/array_declare_element_c.out b/v_windows/v/old/vlib/v/checker/tests/array_declare_element_c.out new file mode 100644 index 0000000..d6e2aaf --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_declare_element_c.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/array_declare_element_c.vv:3:8: error: non-name `arr[1][0]` on left side of `:=` + 1 | fn main() { + 2 | arr := [[1, 2], [0, 3]] + 3 | arr[1][0] := 1 + | ~~~ + 4 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/array_declare_element_c.vv b/v_windows/v/old/vlib/v/checker/tests/array_declare_element_c.vv new file mode 100644 index 0000000..aacad55 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_declare_element_c.vv @@ -0,0 +1,4 @@ +fn main() { + arr := [[1, 2], [0, 3]] + arr[1][0] := 1 +} diff --git a/v_windows/v/old/vlib/v/checker/tests/array_element_type.out b/v_windows/v/old/vlib/v/checker/tests/array_element_type.out new file mode 100644 index 0000000..71a50e2 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_element_type.out @@ -0,0 +1,12 @@ +vlib/v/checker/tests/array_element_type.vv:2:8: error: unknown type `abc` + 1 | fn main() { + 2 | _ = []abc{} + | ~~~ + 3 | _ = [2, ''] + 4 | } +vlib/v/checker/tests/array_element_type.vv:3:10: error: invalid array element: expected `int`, not `string` + 1 | fn main() { + 2 | _ = []abc{} + 3 | _ = [2, ''] + | ~~ + 4 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/array_element_type.vv b/v_windows/v/old/vlib/v/checker/tests/array_element_type.vv new file mode 100644 index 0000000..95fd275 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_element_type.vv @@ -0,0 +1,4 @@ +fn main() { + _ = []abc{} + _ = [2, ''] +} diff --git a/v_windows/v/old/vlib/v/checker/tests/array_filter_fn_err.out b/v_windows/v/old/vlib/v/checker/tests/array_filter_fn_err.out new file mode 100644 index 0000000..cd0b1fc --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_filter_fn_err.out @@ -0,0 +1,27 @@ +vlib/v/checker/tests/array_filter_fn_err.vv:2:25: error: function needs exactly 1 argument + 1 | fn main() { + 2 | a1 := [1,2,3,4].filter(fn(a int, b int) bool { return a > 0 }) + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 3 | println(a1) + 4 | +vlib/v/checker/tests/array_filter_fn_err.vv:5:25: error: type mismatch, should use `fn(a int) bool {...}` + 3 | println(a1) + 4 | + 5 | a2 := [1,2,3,4].filter(fn(a string) bool { return a.len > 0 }) + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 6 | println(a2) + 7 | +vlib/v/checker/tests/array_filter_fn_err.vv:8:18: error: function needs exactly 1 argument + 6 | println(a2) + 7 | + 8 | a3 := [1,2,3,4].filter(fil1) + | ~~~~~~~~~~~~ + 9 | println(a3) + 10 | +vlib/v/checker/tests/array_filter_fn_err.vv:11:25: error: type mismatch, should use `fn(a int) bool {...}` + 9 | println(a3) + 10 | + 11 | a4 := [1,2,3,4].filter(fil2) + | ~~~~ + 12 | println(a4) + 13 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/array_filter_fn_err.vv b/v_windows/v/old/vlib/v/checker/tests/array_filter_fn_err.vv new file mode 100644 index 0000000..27c3867 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_filter_fn_err.vv @@ -0,0 +1,21 @@ +fn main() { + a1 := [1,2,3,4].filter(fn(a int, b int) bool { return a > 0 }) + println(a1) + + a2 := [1,2,3,4].filter(fn(a string) bool { return a.len > 0 }) + println(a2) + + a3 := [1,2,3,4].filter(fil1) + println(a3) + + a4 := [1,2,3,4].filter(fil2) + println(a4) +} + +fn fil1(a int, b int) bool { + return a > 0 +} + +fn fil2(a string) bool { + return a.len > 0 +} diff --git a/v_windows/v/old/vlib/v/checker/tests/array_index.out b/v_windows/v/old/vlib/v/checker/tests/array_index.out new file mode 100644 index 0000000..d2e6795 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_index.out @@ -0,0 +1,34 @@ +vlib/v/checker/tests/array_index.vv:3:7: error: non-integer index `float literal` (array type `[2]int`) + 1 | fn fixed() { + 2 | a := [1,2]! + 3 | _ = a[0.2] + | ~~~~~ + 4 | _ = a[1] // OK + 5 | _ = a[-1] +vlib/v/checker/tests/array_index.vv:5:8: error: negative index `-1` + 3 | _ = a[0.2] + 4 | _ = a[1] // OK + 5 | _ = a[-1] + | ~~ + 6 | _ = a[2] + 7 | _ = a[1..2] // OK +vlib/v/checker/tests/array_index.vv:6:8: error: index out of range (index: 2, len: 2) + 4 | _ = a[1] // OK + 5 | _ = a[-1] + 6 | _ = a[2] + | ^ + 7 | _ = a[1..2] // OK + 8 | _ = a[2..2] // empty, OK +vlib/v/checker/tests/array_index.vv:9:11: error: index out of range (index: 3, len: 2) + 7 | _ = a[1..2] // OK + 8 | _ = a[2..2] // empty, OK + 9 | _ = a[1..3] + | ^ + 10 | _ = a[3..3] + 11 | } +vlib/v/checker/tests/array_index.vv:10:8: error: index out of range (index: 3, len: 2) + 8 | _ = a[2..2] // empty, OK + 9 | _ = a[1..3] + 10 | _ = a[3..3] + | ^ + 11 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/array_index.vv b/v_windows/v/old/vlib/v/checker/tests/array_index.vv new file mode 100644 index 0000000..74200ff --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_index.vv @@ -0,0 +1,11 @@ +fn fixed() { + a := [1,2]! + _ = a[0.2] + _ = a[1] // OK + _ = a[-1] + _ = a[2] + _ = a[1..2] // OK + _ = a[2..2] // empty, OK + _ = a[1..3] + _ = a[3..3] +} diff --git a/v_windows/v/old/vlib/v/checker/tests/array_init_sum_type_without_init_value_and_len_err.out b/v_windows/v/old/vlib/v/checker/tests/array_init_sum_type_without_init_value_and_len_err.out new file mode 100644 index 0000000..129d9db --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_init_sum_type_without_init_value_and_len_err.out @@ -0,0 +1,14 @@ +vlib/v/checker/tests/array_init_sum_type_without_init_value_and_len_err.vv:4:7: error: cannot initialize sum type array without default value + 2 | + 3 | fn main() { + 4 | a := []Foo{len: 10} + | ~~~~~~ + 5 | println(a) + 6 | +vlib/v/checker/tests/array_init_sum_type_without_init_value_and_len_err.vv:7:13: error: cannot initialize sum type array without default value + 5 | println(a) + 6 | + 7 | fixed_a := [10]Foo{} + | ~~~~~~~~~ + 8 | println(fixed_a) + 9 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/array_init_sum_type_without_init_value_and_len_err.vv b/v_windows/v/old/vlib/v/checker/tests/array_init_sum_type_without_init_value_and_len_err.vv new file mode 100644 index 0000000..2494d52 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_init_sum_type_without_init_value_and_len_err.vv @@ -0,0 +1,9 @@ +type Foo = int | string + +fn main() { + a := []Foo{len: 10} + println(a) + + fixed_a := [10]Foo{} + println(fixed_a) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/array_insert_type_mismatch.out b/v_windows/v/old/vlib/v/checker/tests/array_insert_type_mismatch.out new file mode 100644 index 0000000..78f0a64 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_insert_type_mismatch.out @@ -0,0 +1,56 @@ +vlib/v/checker/tests/array_insert_type_mismatch.vv:3:14: error: cannot insert `float literal` to `[]int` + 1 | fn main() { + 2 | mut a := [1, 2] + 3 | a.insert(1, 2.3) + | ~~~ + 4 | a.insert(1, 'abc') + 5 | a.insert(1, [1.1, 2.2]) +vlib/v/checker/tests/array_insert_type_mismatch.vv:4:14: error: cannot insert `string` to `[]int` + 2 | mut a := [1, 2] + 3 | a.insert(1, 2.3) + 4 | a.insert(1, 'abc') + | ~~~~~ + 5 | a.insert(1, [1.1, 2.2]) + 6 | a.insert(1, ['aa', 'bb', 'cc']) +vlib/v/checker/tests/array_insert_type_mismatch.vv:5:14: error: cannot insert `[]f64` to `[]int` + 3 | a.insert(1, 2.3) + 4 | a.insert(1, 'abc') + 5 | a.insert(1, [1.1, 2.2]) + | ~~~~~~~~~~ + 6 | a.insert(1, ['aa', 'bb', 'cc']) + 7 | a.insert(1, [[1]]) +vlib/v/checker/tests/array_insert_type_mismatch.vv:6:14: error: cannot insert `[]string` to `[]int` + 4 | a.insert(1, 'abc') + 5 | a.insert(1, [1.1, 2.2]) + 6 | a.insert(1, ['aa', 'bb', 'cc']) + | ~~~~~~~~~~~~~~~~~~ + 7 | a.insert(1, [[1]]) + 8 | a.insert(1, [[['aa']]]) +vlib/v/checker/tests/array_insert_type_mismatch.vv:7:14: error: cannot insert `[][]int` to `[]int` + 5 | a.insert(1, [1.1, 2.2]) + 6 | a.insert(1, ['aa', 'bb', 'cc']) + 7 | a.insert(1, [[1]]) + | ~~~~~ + 8 | a.insert(1, [[['aa']]]) + 9 | println(a) +vlib/v/checker/tests/array_insert_type_mismatch.vv:8:14: error: cannot insert `[][][]string` to `[]int` + 6 | a.insert(1, ['aa', 'bb', 'cc']) + 7 | a.insert(1, [[1]]) + 8 | a.insert(1, [[['aa']]]) + | ~~~~~~~~~~ + 9 | println(a) + 10 | +vlib/v/checker/tests/array_insert_type_mismatch.vv:12:14: error: cannot insert `[][][]int` to `[][]int` + 10 | + 11 | mut b := [[1, 2, 3]] + 12 | b.insert(0, [[[2]]]) + | ~~~~~~~ + 13 | b.insert(0, [[[['aa']]]]) + 14 | println(b) +vlib/v/checker/tests/array_insert_type_mismatch.vv:13:14: error: cannot insert `[][][][]string` to `[][]int` + 11 | mut b := [[1, 2, 3]] + 12 | b.insert(0, [[[2]]]) + 13 | b.insert(0, [[[['aa']]]]) + | ~~~~~~~~~~~~ + 14 | println(b) + 15 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/array_insert_type_mismatch.vv b/v_windows/v/old/vlib/v/checker/tests/array_insert_type_mismatch.vv new file mode 100644 index 0000000..518b44a --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_insert_type_mismatch.vv @@ -0,0 +1,15 @@ +fn main() { + mut a := [1, 2] + a.insert(1, 2.3) + a.insert(1, 'abc') + a.insert(1, [1.1, 2.2]) + a.insert(1, ['aa', 'bb', 'cc']) + a.insert(1, [[1]]) + a.insert(1, [[['aa']]]) + println(a) + + mut b := [[1, 2, 3]] + b.insert(0, [[[2]]]) + b.insert(0, [[[['aa']]]]) + println(b) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/array_literal_modify_err.out b/v_windows/v/old/vlib/v/checker/tests/array_literal_modify_err.out new file mode 100644 index 0000000..eaec4f9 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_literal_modify_err.out @@ -0,0 +1,12 @@ +vlib/v/checker/tests/array_literal_modify_err.vv:2:24: error: array append cannot be used in an expression + 1 | fn main() { + 2 | mut nums := [1, 2, 3] << 4 + | ~~ + 3 | println(nums) + 4 | } +vlib/v/checker/tests/array_literal_modify_err.vv:3:2: error: `println` can not print void expressions + 1 | fn main() { + 2 | mut nums := [1, 2, 3] << 4 + 3 | println(nums) + | ~~~~~~~~~~~~~ + 4 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/array_literal_modify_err.vv b/v_windows/v/old/vlib/v/checker/tests/array_literal_modify_err.vv new file mode 100644 index 0000000..79c7f97 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_literal_modify_err.vv @@ -0,0 +1,4 @@ +fn main() { + mut nums := [1, 2, 3] << 4 + println(nums) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/array_map_arg_mismatch.out b/v_windows/v/old/vlib/v/checker/tests/array_map_arg_mismatch.out new file mode 100644 index 0000000..8e06495 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_map_arg_mismatch.out @@ -0,0 +1,13 @@ +vlib/v/checker/tests/array_map_arg_mismatch.vv:3:4: error: expected 1 argument, but got 0 + 1 | fn main() { + 2 | a := [1, 2, 3] + 3 | a.map() + | ~~~~~ + 4 | a.map(it * 2, 3) + 5 | } +vlib/v/checker/tests/array_map_arg_mismatch.vv:4:4: error: expected 1 argument, but got 2 + 2 | a := [1, 2, 3] + 3 | a.map() + 4 | a.map(it * 2, 3) + | ~~~~~~~~~~~~~~ + 5 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/array_map_arg_mismatch.vv b/v_windows/v/old/vlib/v/checker/tests/array_map_arg_mismatch.vv new file mode 100644 index 0000000..629707b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_map_arg_mismatch.vv @@ -0,0 +1,5 @@ +fn main() { + a := [1, 2, 3] + a.map() + a.map(it * 2, 3) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/array_map_fn_err.out b/v_windows/v/old/vlib/v/checker/tests/array_map_fn_err.out new file mode 100644 index 0000000..b4df650 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_map_fn_err.out @@ -0,0 +1,41 @@ +vlib/v/checker/tests/array_map_fn_err.vv:2:22: error: function needs exactly 1 argument + 1 | fn main() { + 2 | a1 := [1,2,3,4].map(fn(a int, b int) int {return a + b}) + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 3 | println(a1) + 4 | +vlib/v/checker/tests/array_map_fn_err.vv:5:22: error: type mismatch, should use `fn(a int) T {...}` + 3 | println(a1) + 4 | + 5 | a2 := [1,2,3,4].map(fn(a string) string { return a }) + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 6 | println(a2) + 7 | +vlib/v/checker/tests/array_map_fn_err.vv:8:22: error: type mismatch, should use `fn(a int) T {...}` + 6 | println(a2) + 7 | + 8 | a3 := [1,2,3,4].map(fn(a string) {}) + | ~~~~~~~~~~~~~~~ + 9 | println(a3) + 10 | +vlib/v/checker/tests/array_map_fn_err.vv:11:18: error: function needs exactly 1 argument + 9 | println(a3) + 10 | + 11 | a4 := [1,2,3,4].map(add1) + | ~~~~~~~~~ + 12 | println(a4) + 13 | +vlib/v/checker/tests/array_map_fn_err.vv:14:22: error: type mismatch, should use `fn(a int) T {...}` + 12 | println(a4) + 13 | + 14 | a5 := [1,2,3,4].map(add2) + | ~~~~ + 15 | println(a5) + 16 | +vlib/v/checker/tests/array_map_fn_err.vv:17:22: error: type mismatch, should use `fn(a int) T {...}` + 15 | println(a5) + 16 | + 17 | a6 := [1,2,3,4].map(do_nothing) + | ~~~~~~~~~~ + 18 | println(a6) + 19 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/array_map_fn_err.vv b/v_windows/v/old/vlib/v/checker/tests/array_map_fn_err.vv new file mode 100644 index 0000000..50d0241 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_map_fn_err.vv @@ -0,0 +1,30 @@ +fn main() { + a1 := [1,2,3,4].map(fn(a int, b int) int {return a + b}) + println(a1) + + a2 := [1,2,3,4].map(fn(a string) string { return a }) + println(a2) + + a3 := [1,2,3,4].map(fn(a string) {}) + println(a3) + + a4 := [1,2,3,4].map(add1) + println(a4) + + a5 := [1,2,3,4].map(add2) + println(a5) + + a6 := [1,2,3,4].map(do_nothing) + println(a6) +} + +fn add1(a int, b int) int { + return a + b +} + +fn add2(a string) string { + return a +} + +fn do_nothing(a string) { +} diff --git a/v_windows/v/old/vlib/v/checker/tests/array_map_void_fn_err.out b/v_windows/v/old/vlib/v/checker/tests/array_map_void_fn_err.out new file mode 100644 index 0000000..9bd41e1 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_map_void_fn_err.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/array_map_void_fn_err.vv:3:12: error: type mismatch, `println` does not return anything + 1 | fn main(){ + 2 | array := [1,2,3,4] + 3 | array.map(println(it)) + | ~~~~~~~~~~~ + 4 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/array_map_void_fn_err.vv b/v_windows/v/old/vlib/v/checker/tests/array_map_void_fn_err.vv new file mode 100644 index 0000000..e429c8a --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_map_void_fn_err.vv @@ -0,0 +1,4 @@ +fn main(){ + array := [1,2,3,4] + array.map(println(it)) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/array_of_interfaces_with_len_without_init.out b/v_windows/v/old/vlib/v/checker/tests/array_of_interfaces_with_len_without_init.out new file mode 100644 index 0000000..bfd51b0 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_of_interfaces_with_len_without_init.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/array_of_interfaces_with_len_without_init.vv:14:37: error: cannot instantiate an array of interfaces without also giving a default `init:` value + 12 | + 13 | fn main() { + 14 | mut parsed_lines := []MObject{len: 9} + | ^ + 15 | println(parsed_lines) + 16 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/array_of_interfaces_with_len_without_init.vv b/v_windows/v/old/vlib/v/checker/tests/array_of_interfaces_with_len_without_init.vv new file mode 100644 index 0000000..d9bc2d7 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_of_interfaces_with_len_without_init.vv @@ -0,0 +1,16 @@ +interface MObject { + give_string() string +} + +struct LeStruct { + le_string string +} + +fn (a LeStruct) give_string() string { + return 'V' +} + +fn main() { + mut parsed_lines := []MObject{len: 9} + println(parsed_lines) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/array_or_map_assign_err.out b/v_windows/v/old/vlib/v/checker/tests/array_or_map_assign_err.out new file mode 100644 index 0000000..fca758b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_or_map_assign_err.out @@ -0,0 +1,35 @@ +vlib/v/checker/tests/array_or_map_assign_err.vv:3:5: error: use `array2 := array1.clone()` instead of `array2 := array1` (or use `unsafe`) + 1 | fn main() { + 2 | a1 := [1, 2, 3] + 3 | a2 := a1 + | ~~ + 4 | mut a3 := []int{} + 5 | a3 = a1 +vlib/v/checker/tests/array_or_map_assign_err.vv:5:5: error: use `array2 = array1.clone()` instead of `array2 = array1` (or use `unsafe`) + 3 | a2 := a1 + 4 | mut a3 := []int{} + 5 | a3 = a1 + | ^ + 6 | + 7 | m1 := map{'one': 1} +vlib/v/checker/tests/array_or_map_assign_err.vv:8:8: error: cannot copy map: call `move` or `clone` method (or use a reference) + 6 | + 7 | m1 := map{'one': 1} + 8 | m2 := m1 + | ~~ + 9 | mut m3 := map[string]int{} + 10 | m3 = m1 +vlib/v/checker/tests/array_or_map_assign_err.vv:10:7: error: cannot copy map: call `move` or `clone` method (or use a reference) + 8 | m2 := m1 + 9 | mut m3 := map[string]int{} + 10 | m3 = m1 + | ~~ + 11 | + 12 | _ = a2 +vlib/v/checker/tests/array_or_map_assign_err.vv:25:8: error: cannot copy map: call `move` or `clone` method (or use a reference) + 23 | + 24 | fn foo(mut m map[string]int) { + 25 | m2 := m + | ^ + 26 | m['foo'] = 100 + 27 | println(m) diff --git a/v_windows/v/old/vlib/v/checker/tests/array_or_map_assign_err.vv b/v_windows/v/old/vlib/v/checker/tests/array_or_map_assign_err.vv new file mode 100644 index 0000000..517a25d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_or_map_assign_err.vv @@ -0,0 +1,29 @@ +fn main() { + a1 := [1, 2, 3] + a2 := a1 + mut a3 := []int{} + a3 = a1 + + m1 := map{'one': 1} + m2 := m1 + mut m3 := map[string]int{} + m3 = m1 + + _ = a2 + _ = m2 + + mut m := map{'foo':1} + foo(mut m) + + _ = a3 + _ = m1 + _ = m2 + _ = m3 +} + +fn foo(mut m map[string]int) { + m2 := m + m['foo'] = 100 + println(m) + println(m2) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/array_prepend_type_mismatch.out b/v_windows/v/old/vlib/v/checker/tests/array_prepend_type_mismatch.out new file mode 100644 index 0000000..8e74b7f --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_prepend_type_mismatch.out @@ -0,0 +1,56 @@ +vlib/v/checker/tests/array_prepend_type_mismatch.vv:3:12: error: cannot prepend `float literal` to `[]int` + 1 | fn main() { + 2 | mut a := [1, 2] + 3 | a.prepend(2.3) + | ~~~ + 4 | a.prepend('abc') + 5 | a.prepend([2.2, 3.3]) +vlib/v/checker/tests/array_prepend_type_mismatch.vv:4:12: error: cannot prepend `string` to `[]int` + 2 | mut a := [1, 2] + 3 | a.prepend(2.3) + 4 | a.prepend('abc') + | ~~~~~ + 5 | a.prepend([2.2, 3.3]) + 6 | a.prepend(['aa', 'bb', 'cc']) +vlib/v/checker/tests/array_prepend_type_mismatch.vv:5:12: error: cannot prepend `[]f64` to `[]int` + 3 | a.prepend(2.3) + 4 | a.prepend('abc') + 5 | a.prepend([2.2, 3.3]) + | ~~~~~~~~~~ + 6 | a.prepend(['aa', 'bb', 'cc']) + 7 | a.prepend([[1]]) +vlib/v/checker/tests/array_prepend_type_mismatch.vv:6:12: error: cannot prepend `[]string` to `[]int` + 4 | a.prepend('abc') + 5 | a.prepend([2.2, 3.3]) + 6 | a.prepend(['aa', 'bb', 'cc']) + | ~~~~~~~~~~~~~~~~~~ + 7 | a.prepend([[1]]) + 8 | a.prepend([[['aa']]]) +vlib/v/checker/tests/array_prepend_type_mismatch.vv:7:12: error: cannot prepend `[][]int` to `[]int` + 5 | a.prepend([2.2, 3.3]) + 6 | a.prepend(['aa', 'bb', 'cc']) + 7 | a.prepend([[1]]) + | ~~~~~ + 8 | a.prepend([[['aa']]]) + 9 | println(a) +vlib/v/checker/tests/array_prepend_type_mismatch.vv:8:12: error: cannot prepend `[][][]string` to `[]int` + 6 | a.prepend(['aa', 'bb', 'cc']) + 7 | a.prepend([[1]]) + 8 | a.prepend([[['aa']]]) + | ~~~~~~~~~~ + 9 | println(a) + 10 | +vlib/v/checker/tests/array_prepend_type_mismatch.vv:12:12: error: cannot prepend `[][][]int` to `[][]int` + 10 | + 11 | mut b := [[1, 2, 3]] + 12 | b.prepend([[[2]]]) + | ~~~~~~~ + 13 | b.prepend([[[['aa']]]]) + 14 | println(b) +vlib/v/checker/tests/array_prepend_type_mismatch.vv:13:12: error: cannot prepend `[][][][]string` to `[][]int` + 11 | mut b := [[1, 2, 3]] + 12 | b.prepend([[[2]]]) + 13 | b.prepend([[[['aa']]]]) + | ~~~~~~~~~~~~ + 14 | println(b) + 15 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/array_prepend_type_mismatch.vv b/v_windows/v/old/vlib/v/checker/tests/array_prepend_type_mismatch.vv new file mode 100644 index 0000000..925156e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_prepend_type_mismatch.vv @@ -0,0 +1,15 @@ +fn main() { + mut a := [1, 2] + a.prepend(2.3) + a.prepend('abc') + a.prepend([2.2, 3.3]) + a.prepend(['aa', 'bb', 'cc']) + a.prepend([[1]]) + a.prepend([[['aa']]]) + println(a) + + mut b := [[1, 2, 3]] + b.prepend([[[2]]]) + b.prepend([[[['aa']]]]) + println(b) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/array_sort_err.out b/v_windows/v/old/vlib/v/checker/tests/array_sort_err.out new file mode 100644 index 0000000..04c3dbf --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_sort_err.out @@ -0,0 +1,27 @@ +vlib/v/checker/tests/array_sort_err.vv:3:6: error: expected 0 or 1 argument, but got 2 + 1 | fn main() { + 2 | mut arr := [3, 2, 1] + 3 | arr.sort(a < b, a) + | ~~~~~~~~~~~~~~ + 4 | arr.sort(a == b) + 5 | arr.sort(a > a) +vlib/v/checker/tests/array_sort_err.vv:4:9: error: `.sort()` can only use `<` or `>` comparison + 2 | mut arr := [3, 2, 1] + 3 | arr.sort(a < b, a) + 4 | arr.sort(a == b) + | ~~~~~~~~~~~~ + 5 | arr.sort(a > a) + 6 | arr.sort(c > d) +vlib/v/checker/tests/array_sort_err.vv:5:9: error: `.sort()` cannot use same argument + 3 | arr.sort(a < b, a) + 4 | arr.sort(a == b) + 5 | arr.sort(a > a) + | ~~~~~~~~~~~ + 6 | arr.sort(c > d) + 7 | } +vlib/v/checker/tests/array_sort_err.vv:6:9: error: `.sort()` can only use `a` or `b` as argument, e.g. `arr.sort(a < b)` + 4 | arr.sort(a == b) + 5 | arr.sort(a > a) + 6 | arr.sort(c > d) + | ~~~~~~~~~~~ + 7 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/array_sort_err.vv b/v_windows/v/old/vlib/v/checker/tests/array_sort_err.vv new file mode 100644 index 0000000..821cfd0 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_sort_err.vv @@ -0,0 +1,7 @@ +fn main() { + mut arr := [3, 2, 1] + arr.sort(a < b, a) + arr.sort(a == b) + arr.sort(a > a) + arr.sort(c > d) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/array_sort_struct_no_body_err.out b/v_windows/v/old/vlib/v/checker/tests/array_sort_struct_no_body_err.out new file mode 100644 index 0000000..dd83a0b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_sort_struct_no_body_err.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/array_sort_struct_no_body_err.vv:6:5: error: custom sorting condition must be supplied for type `Foo` + 4 | + 5 | mut arr := []Foo{} + 6 | arr.sort() + | ~~~~~~ diff --git a/v_windows/v/old/vlib/v/checker/tests/array_sort_struct_no_body_err.vv b/v_windows/v/old/vlib/v/checker/tests/array_sort_struct_no_body_err.vv new file mode 100644 index 0000000..6b53ea9 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/array_sort_struct_no_body_err.vv @@ -0,0 +1,6 @@ +struct Foo { + bar int +} + +mut arr := []Foo{} +arr.sort() diff --git a/v_windows/v/old/vlib/v/checker/tests/arrow_op_wrong_left_type_err_a.out b/v_windows/v/old/vlib/v/checker/tests/arrow_op_wrong_left_type_err_a.out new file mode 100644 index 0000000..9f838fd --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/arrow_op_wrong_left_type_err_a.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/arrow_op_wrong_left_type_err_a.vv:4:2: error: cannot push on non-channel `i64` + 2 | ch := i64(3) + 3 | obj := 5 + 4 | ch <- obj + | ~~ + 5 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/arrow_op_wrong_left_type_err_a.vv b/v_windows/v/old/vlib/v/checker/tests/arrow_op_wrong_left_type_err_a.vv new file mode 100644 index 0000000..d205205 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/arrow_op_wrong_left_type_err_a.vv @@ -0,0 +1,5 @@ +fn main() { + ch := i64(3) + obj := 5 + ch <- obj +} diff --git a/v_windows/v/old/vlib/v/checker/tests/arrow_op_wrong_left_type_err_b.out b/v_windows/v/old/vlib/v/checker/tests/arrow_op_wrong_left_type_err_b.out new file mode 100644 index 0000000..61d6c99 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/arrow_op_wrong_left_type_err_b.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/arrow_op_wrong_left_type_err_b.vv:4:8: error: cannot assign to `obj`: expected `int`, not `string` + 2 | ch := chan string{} + 3 | mut obj := 9 + 4 | obj = <-ch + | ~~ + 5 | _ = obj + 6 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/arrow_op_wrong_left_type_err_b.vv b/v_windows/v/old/vlib/v/checker/tests/arrow_op_wrong_left_type_err_b.vv new file mode 100644 index 0000000..3e842d9 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/arrow_op_wrong_left_type_err_b.vv @@ -0,0 +1,6 @@ +fn main() { + ch := chan string{} + mut obj := 9 + obj = <-ch + _ = obj +} diff --git a/v_windows/v/old/vlib/v/checker/tests/arrow_op_wrong_right_type_err_a.out b/v_windows/v/old/vlib/v/checker/tests/arrow_op_wrong_right_type_err_a.out new file mode 100644 index 0000000..e16afc8 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/arrow_op_wrong_right_type_err_a.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/arrow_op_wrong_right_type_err_a.vv:4:8: error: cannot push `string` on `chan u64` + 2 | ch := chan u64{cap: 10} + 3 | obj := 'test' + 4 | ch <- obj + | ~~~ + 5 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/arrow_op_wrong_right_type_err_a.vv b/v_windows/v/old/vlib/v/checker/tests/arrow_op_wrong_right_type_err_a.vv new file mode 100644 index 0000000..f74184d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/arrow_op_wrong_right_type_err_a.vv @@ -0,0 +1,5 @@ +fn main() { + ch := chan u64{cap: 10} + obj := 'test' + ch <- obj +} diff --git a/v_windows/v/old/vlib/v/checker/tests/arrow_op_wrong_right_type_err_b.out b/v_windows/v/old/vlib/v/checker/tests/arrow_op_wrong_right_type_err_b.out new file mode 100644 index 0000000..ae9be38 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/arrow_op_wrong_right_type_err_b.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/arrow_op_wrong_right_type_err_b.vv:3:9: error: <- operator can only be used with `chan` types + 1 | fn main() { + 2 | ch := i64(3) + 3 | obj := <-ch + | ~~ + 4 | println(obj) + 5 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/arrow_op_wrong_right_type_err_b.vv b/v_windows/v/old/vlib/v/checker/tests/arrow_op_wrong_right_type_err_b.vv new file mode 100644 index 0000000..48de2bf --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/arrow_op_wrong_right_type_err_b.vv @@ -0,0 +1,5 @@ +fn main() { + ch := i64(3) + obj := <-ch + println(obj) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/asm_immutable_err.out b/v_windows/v/old/vlib/v/checker/tests/asm_immutable_err.out new file mode 100644 index 0000000..2153ae9 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/asm_immutable_err.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/asm_immutable_err.vv:9:9: error: `c` is immutable, declare it with `mut` to make it mutable + 7 | add eax, b + 8 | mov c, eax + 9 | ; =r (c) // output + | ^ + 10 | ; r (a) // input + 11 | r (b) \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/asm_immutable_err.vv b/v_windows/v/old/vlib/v/checker/tests/asm_immutable_err.vv new file mode 100644 index 0000000..e991206 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/asm_immutable_err.vv @@ -0,0 +1,16 @@ +fn main() { + a := 100 + b := 20 + c := 0 + asm amd64 { + mov eax, a + add eax, b + mov c, eax + ; =r (c) // output + ; r (a) // input + r (b) + } + println('a: $a') // 100 + println('b: $b') // 20 + println('c: $c') // 120 +} diff --git a/v_windows/v/old/vlib/v/checker/tests/assert_optional_err.out b/v_windows/v/old/vlib/v/checker/tests/assert_optional_err.out new file mode 100644 index 0000000..9d2f49a --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assert_optional_err.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/assert_optional_err.vv:4:9: error: assert can be used only with `bool` expressions, but found `void` instead + 2 | + 3 | fn main(){ + 4 | assert os.truncate("testfile.txt", 6666) or { panic(err) } + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 5 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/assert_optional_err.vv b/v_windows/v/old/vlib/v/checker/tests/assert_optional_err.vv new file mode 100644 index 0000000..7036616 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assert_optional_err.vv @@ -0,0 +1,5 @@ +import os + +fn main(){ + assert os.truncate("testfile.txt", 6666) or { panic(err) } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_array_init_with_no_type.out b/v_windows/v/old/vlib/v/checker/tests/assign_array_init_with_no_type.out new file mode 100644 index 0000000..b04c3f7 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_array_init_with_no_type.out @@ -0,0 +1,12 @@ +vlib/v/checker/tests/assign_array_init_with_no_type.vv:2:11: error: array_init: no type specified (maybe: `[]Type{}` instead of `[]`) + 1 | fn main() { + 2 | mut x := [] + | ~~ + 3 | println(x) + 4 | } +vlib/v/checker/tests/assign_array_init_with_no_type.vv:3:2: error: `println` can not print void expressions + 1 | fn main() { + 2 | mut x := [] + 3 | println(x) + | ~~~~~~~~~~ + 4 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_array_init_with_no_type.vv b/v_windows/v/old/vlib/v/checker/tests/assign_array_init_with_no_type.vv new file mode 100644 index 0000000..cc06a55 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_array_init_with_no_type.vv @@ -0,0 +1,4 @@ +fn main() { + mut x := [] + println(x) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_deref_fn_call_on_left_side_err.out b/v_windows/v/old/vlib/v/checker/tests/assign_deref_fn_call_on_left_side_err.out new file mode 100644 index 0000000..d26ab1b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_deref_fn_call_on_left_side_err.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/assign_deref_fn_call_on_left_side_err.vv:8:2: error: cannot dereference a function call on the left side of an assignment, use a temporary variable + 6 | + 7 | fn main() { + 8 | *foo('s') = 1 + | ^ + 9 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_deref_fn_call_on_left_side_err.vv b/v_windows/v/old/vlib/v/checker/tests/assign_deref_fn_call_on_left_side_err.vv new file mode 100644 index 0000000..50aa22b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_deref_fn_call_on_left_side_err.vv @@ -0,0 +1,9 @@ +struct Foo{} + +fn foo(s string) &Foo { + return &Foo{} +} + +fn main() { + *foo('s') = 1 +} diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_a.out b/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_a.out new file mode 100644 index 0000000..cba2860 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_a.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/assign_expr_type_err_a.vv:3:2: error: operator <<= not defined on left operand type `f64` + 1 | fn main() { + 2 | mut foo := 0.5 + 3 | foo <<= 1 + | ~~~ + 4 | _ = foo + 5 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_a.vv b/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_a.vv new file mode 100644 index 0000000..8ea403d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_a.vv @@ -0,0 +1,5 @@ +fn main() { + mut foo := 0.5 + foo <<= 1 + _ = foo +} diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_b.out b/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_b.out new file mode 100644 index 0000000..e5f01ca --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_b.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/assign_expr_type_err_b.vv:3:9: error: operator %= not defined on right operand type `string` + 1 | fn main() { + 2 | mut foo := 10 + 3 | foo %= 'hello' + | ~~~~~~~ + 4 | _ = foo + 5 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_b.vv b/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_b.vv new file mode 100644 index 0000000..db28266 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_b.vv @@ -0,0 +1,5 @@ +fn main() { + mut foo := 10 + foo %= 'hello' + _ = foo +} diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_c.out b/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_c.out new file mode 100644 index 0000000..338ce5b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_c.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/assign_expr_type_err_c.vv:3:2: error: operator *= not defined on left operand type `string` + 1 | fn main() { + 2 | mut foo := 'hello' + 3 | foo *= 10 + | ~~~ + 4 | _ = foo + 5 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_c.vv b/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_c.vv new file mode 100644 index 0000000..98409ac --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_c.vv @@ -0,0 +1,5 @@ +fn main() { + mut foo := 'hello' + foo *= 10 + _ = foo +} diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_d.out b/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_d.out new file mode 100644 index 0000000..7753629 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_d.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/assign_expr_type_err_d.vv:3:9: error: operator /= not defined on right operand type `bool` + 1 | fn main() { + 2 | mut foo := 1.5 + 3 | foo /= true + | ~~~~ + 4 | _ = foo + 5 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_d.vv b/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_d.vv new file mode 100644 index 0000000..ca5c0ee --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_d.vv @@ -0,0 +1,5 @@ +fn main() { + mut foo := 1.5 + foo /= true + _ = foo +} diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_e.out b/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_e.out new file mode 100644 index 0000000..b8acd99 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_e.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/assign_expr_type_err_e.vv:3:2: error: operator `-=` not defined on left operand type `string` + 1 | fn main() { + 2 | mut foo := 'hello' + 3 | foo -= `a` + | ~~~ + 4 | _ = foo + 5 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_e.vv b/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_e.vv new file mode 100644 index 0000000..b6a4473 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_e.vv @@ -0,0 +1,5 @@ +fn main() { + mut foo := 'hello' + foo -= `a` + _ = foo +} diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_f.out b/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_f.out new file mode 100644 index 0000000..7cdaa51 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_f.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/assign_expr_type_err_f.vv:3:9: error: invalid right operand: int -= bool + 1 | fn main() { + 2 | mut foo := 10 + 3 | foo -= false + | ~~~~~ + 4 | _ = foo + 5 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_f.vv b/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_f.vv new file mode 100644 index 0000000..70213c4 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_f.vv @@ -0,0 +1,5 @@ +fn main() { + mut foo := 10 + foo -= false + _ = foo +} diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_g.out b/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_g.out new file mode 100644 index 0000000..e017d7b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_g.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/assign_expr_type_err_g.vv:3:2: error: operator `+=` not defined on left operand type `bool` + 1 | fn main() { + 2 | mut foo := true + 3 | foo += false + | ~~~ + 4 | _ = foo + 5 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_g.vv b/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_g.vv new file mode 100644 index 0000000..c9380f4 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_g.vv @@ -0,0 +1,5 @@ +fn main() { + mut foo := true + foo += false + _ = foo +} diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_h.out b/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_h.out new file mode 100644 index 0000000..07d2b6e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_h.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/assign_expr_type_err_h.vv:3:9: error: invalid right operand: string += bool + 1 | fn main() { + 2 | mut foo := 'hello' + 3 | foo += false + | ~~~~~ + 4 | _ = foo + 5 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_h.vv b/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_h.vv new file mode 100644 index 0000000..51b2207 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_h.vv @@ -0,0 +1,5 @@ +fn main() { + mut foo := 'hello' + foo += false + _ = foo +} diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_i.out b/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_i.out new file mode 100644 index 0000000..c8450f0 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_i.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/assign_expr_type_err_i.vv:3:9: error: invalid right operand: f64 += string + 1 | fn main() { + 2 | mut foo := 1.5 + 3 | foo += 'hello' + | ~~~~~~~ + 4 | _ = foo + 5 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_i.vv b/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_i.vv new file mode 100644 index 0000000..2debbae --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_type_err_i.vv @@ -0,0 +1,5 @@ +fn main() { + mut foo := 1.5 + foo += 'hello' + _ = foo +} diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_a.out b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_a.out new file mode 100644 index 0000000..d7aaac0 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_a.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/assign_expr_undefined_err_a.vv:2:7: error: undefined variable: `a` + 1 | fn main() { + 2 | a := a + | ^ + 3 | println(a) + 4 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_a.vv b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_a.vv new file mode 100644 index 0000000..05ffdf4 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_a.vv @@ -0,0 +1,4 @@ +fn main() { + a := a + println(a) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_b.out b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_b.out new file mode 100644 index 0000000..f43a641 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_b.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/assign_expr_undefined_err_b.vv:2:10: error: undefined variable: `a` + 1 | fn main() { + 2 | a, b := a, b + | ^ + 3 | println('$a, $b') + 4 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_b.vv b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_b.vv new file mode 100644 index 0000000..b77ab6c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_b.vv @@ -0,0 +1,4 @@ +fn main() { + a, b := a, b + println('$a, $b') +} diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_c.out b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_c.out new file mode 100644 index 0000000..a68f329 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_c.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/assign_expr_undefined_err_c.vv:2:10: error: undefined variable: `a` + 1 | fn main() { + 2 | a, b := a + 1, b * 3 + | ^ + 3 | println('$a, $b') + 4 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_c.vv b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_c.vv new file mode 100644 index 0000000..b845893 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_c.vv @@ -0,0 +1,4 @@ +fn main() { + a, b := a + 1, b * 3 + println('$a, $b') +} diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_d.out b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_d.out new file mode 100644 index 0000000..4a8df88 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_d.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/assign_expr_undefined_err_d.vv:2:9: error: undefined variable: `s` + 1 | fn main() { + 2 | s := '$s' + | ^ + 3 | println(s) + 4 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_d.vv b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_d.vv new file mode 100644 index 0000000..f9acc47 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_d.vv @@ -0,0 +1,4 @@ +fn main() { + s := '$s' + println(s) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_e.out b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_e.out new file mode 100644 index 0000000..e505e4c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_e.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/assign_expr_undefined_err_e.vv:2:11: error: undefined variable: `a` + 1 | fn main() { + 2 | a, b := -a, -b + | ^ + 3 | println(s) + 4 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_e.vv b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_e.vv new file mode 100644 index 0000000..47e4ed5 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_e.vv @@ -0,0 +1,4 @@ +fn main() { + a, b := -a, -b + println(s) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_f.out b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_f.out new file mode 100644 index 0000000..1b1f926 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_f.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/assign_expr_undefined_err_f.vv:2:12: error: undefined variable: `a` + 1 | fn main() { + 2 | a, b := (-a + 1), 1 + | ^ + 3 | println('$a, $b') + 4 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_f.vv b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_f.vv new file mode 100644 index 0000000..79ec348 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_f.vv @@ -0,0 +1,4 @@ +fn main() { + a, b := (-a + 1), 1 + println('$a, $b') +} diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_g.out b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_g.out new file mode 100644 index 0000000..277bbaf --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_g.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/assign_expr_undefined_err_g.vv:2:14: error: undefined variable: `file` + 1 | fn main() { + 2 | mut file := file.open_file('bees.pdf', 'rw', 0o666) + | ~~~~ + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_g.vv b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_g.vv new file mode 100644 index 0000000..b589a78 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_g.vv @@ -0,0 +1,3 @@ +fn main() { + mut file := file.open_file('bees.pdf', 'rw', 0o666) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_h.out b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_h.out new file mode 100644 index 0000000..9c647c8 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_h.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/assign_expr_undefined_err_h.vv:6:9: error: undefined variable: `n` + 4 | + 5 | fn main() { + 6 | n := f(n) + | ^ + 7 | println(n) + 8 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_h.vv b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_h.vv new file mode 100644 index 0000000..d872d7e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_h.vv @@ -0,0 +1,8 @@ +fn f(i int) int { + return i +} + +fn main() { + n := f(n) + println(n) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_i.out b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_i.out new file mode 100644 index 0000000..74705af --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_i.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/assign_expr_undefined_err_i.vv:2:23: error: undefined variable: `a` + 1 | fn main() { + 2 | mut a := []int{init: a} + | ^ + 3 | println(a) + 4 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_i.vv b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_i.vv new file mode 100644 index 0000000..26d689e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_i.vv @@ -0,0 +1,4 @@ +fn main() { + mut a := []int{init: a} + println(a) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_j.out b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_j.out new file mode 100644 index 0000000..5ca8f5a --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_j.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/assign_expr_undefined_err_j.vv:2:12: error: undefined variable: `a` + 1 | fn main() { + 2 | mut a := [a] + | ^ + 3 | println(a) + 4 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_j.vv b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_j.vv new file mode 100644 index 0000000..0406da7 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_j.vv @@ -0,0 +1,4 @@ +fn main() { + mut a := [a] + println(a) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_k.out b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_k.out new file mode 100644 index 0000000..0dd8e1c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_k.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/assign_expr_undefined_err_k.vv:2:22: error: undefined variable: `a` + 1 | fn main() { + 2 | mut a := map{'one': a} + | ^ + 3 | println(a) + 4 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_k.vv b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_k.vv new file mode 100644 index 0000000..e4d1e89 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_undefined_err_k.vv @@ -0,0 +1,4 @@ +fn main() { + mut a := map{'one': a} + println(a) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_unresolved_variables_err_chain.out b/v_windows/v/old/vlib/v/checker/tests/assign_expr_unresolved_variables_err_chain.out new file mode 100644 index 0000000..5c7e5cf --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_unresolved_variables_err_chain.out @@ -0,0 +1,13 @@ +vlib/v/checker/tests/assign_expr_unresolved_variables_err_chain.vv:2:7: error: undefined variable `b` (used before declaration) + 1 | fn main() { + 2 | a := b + | ^ + 3 | b := c + 4 | c := a +vlib/v/checker/tests/assign_expr_unresolved_variables_err_chain.vv:3:7: error: undefined variable `c` (used before declaration) + 1 | fn main() { + 2 | a := b + 3 | b := c + | ^ + 4 | c := a + 5 | _ = a diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_expr_unresolved_variables_err_chain.vv b/v_windows/v/old/vlib/v/checker/tests/assign_expr_unresolved_variables_err_chain.vv new file mode 100644 index 0000000..279d6f5 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_expr_unresolved_variables_err_chain.vv @@ -0,0 +1,8 @@ +fn main() { + a := b + b := c + c := a + _ = a + _ = b + _ = c +} diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_fn_call_on_left_side_err.out b/v_windows/v/old/vlib/v/checker/tests/assign_fn_call_on_left_side_err.out new file mode 100644 index 0000000..6a1a663 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_fn_call_on_left_side_err.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/assign_fn_call_on_left_side_err.vv:6:2: error: cannot call function `foo()` on the left side of an assignment + 4 | + 5 | fn main() { + 6 | foo('s') = 1 + | ~~~~~~~~ + 7 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_fn_call_on_left_side_err.vv b/v_windows/v/old/vlib/v/checker/tests/assign_fn_call_on_left_side_err.vv new file mode 100644 index 0000000..69c26dd --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_fn_call_on_left_side_err.vv @@ -0,0 +1,7 @@ +fn foo(s string) int { + return 1 +} + +fn main() { + foo('s') = 1 +} diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_multi_immutable_err.out b/v_windows/v/old/vlib/v/checker/tests/assign_multi_immutable_err.out new file mode 100644 index 0000000..635e020 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_multi_immutable_err.out @@ -0,0 +1,21 @@ +vlib/v/checker/tests/assign_multi_immutable_err.vv:4:2: error: `a` is immutable, declare it with `mut` to make it mutable + 2 | a := 10 + 3 | b := 20 + 4 | a, b = 1, 2 + | ^ + 5 | + 6 | println('$a, $b') +vlib/v/checker/tests/assign_multi_immutable_err.vv:18:5: error: cannot assign to function `error` + 16 | + 17 | fn assign_fn() { + 18 | _, error = g() + | ~~~~~ + 19 | g = f() + 20 | } +vlib/v/checker/tests/assign_multi_immutable_err.vv:19:2: error: cannot assign to function `g` + 17 | fn assign_fn() { + 18 | _, error = g() + 19 | g = f() + | ^ + 20 | } + 21 | diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_multi_immutable_err.vv b/v_windows/v/old/vlib/v/checker/tests/assign_multi_immutable_err.vv new file mode 100644 index 0000000..cfed449 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_multi_immutable_err.vv @@ -0,0 +1,21 @@ +fn main() { + a := 10 + b := 20 + a, b = 1, 2 + + println('$a, $b') +} + +fn f() int { + return 2 +} + +fn g() (int, int) { + return 1, 2 +} + +fn assign_fn() { + _, error = g() + g = f() +} + diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_multi_mismatch.out b/v_windows/v/old/vlib/v/checker/tests/assign_multi_mismatch.out new file mode 100644 index 0000000..ded3db7 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_multi_mismatch.out @@ -0,0 +1,81 @@ +vlib/v/checker/tests/assign_multi_mismatch.vv:5:3: error: assignment mismatch: 1 variable(s) 2 value(s) + 3 | } + 4 | + 5 | _ := 0, 0 + | ~~ + 6 | _ := f() + 7 | _, _ := f() +vlib/v/checker/tests/assign_multi_mismatch.vv:6:3: error: assignment mismatch: 1 variable(s) but `f()` returns 2 value(s) + 4 | + 5 | _ := 0, 0 + 6 | _ := f() + | ~~ + 7 | _, _ := f() + 8 | _, _ := 0, f() +vlib/v/checker/tests/assign_multi_mismatch.vv:8:12: error: cannot use multi-value (int, int) in single-value context + 6 | _ := f() + 7 | _, _ := f() + 8 | _, _ := 0, f() + | ~~~ + 9 | _, _ := f(), 0 + 10 | _, _, _ := 0, f() +vlib/v/checker/tests/assign_multi_mismatch.vv:9:9: error: cannot use multi-value (int, int) in single-value context + 7 | _, _ := f() + 8 | _, _ := 0, f() + 9 | _, _ := f(), 0 + | ~~~ + 10 | _, _, _ := 0, f() + 11 | _, _, _ := f(), 0 +vlib/v/checker/tests/assign_multi_mismatch.vv:10:15: error: cannot use multi-value (int, int) in single-value context + 8 | _, _ := 0, f() + 9 | _, _ := f(), 0 + 10 | _, _, _ := 0, f() + | ~~~ + 11 | _, _, _ := f(), 0 + 12 | _, _ := f(), f() +vlib/v/checker/tests/assign_multi_mismatch.vv:11:12: error: cannot use multi-value (int, int) in single-value context + 9 | _, _ := f(), 0 + 10 | _, _, _ := 0, f() + 11 | _, _, _ := f(), 0 + | ~~~ + 12 | _, _ := f(), f() + 13 | _, _, _, _ := f(), f() +vlib/v/checker/tests/assign_multi_mismatch.vv:12:9: error: cannot use multi-value (int, int) in single-value context + 10 | _, _, _ := 0, f() + 11 | _, _, _ := f(), 0 + 12 | _, _ := f(), f() + | ~~~ + 13 | _, _, _, _ := f(), f() + 14 | +vlib/v/checker/tests/assign_multi_mismatch.vv:13:15: error: cannot use multi-value (int, int) in single-value context + 11 | _, _, _ := f(), 0 + 12 | _, _ := f(), f() + 13 | _, _, _, _ := f(), f() + | ~~~ + 14 | + 15 | _, _ := 0, match 4 { +vlib/v/checker/tests/assign_multi_mismatch.vv:19:3: error: assignment mismatch: 1 variable(s) 2 value(s) + 17 | else { 1 } + 18 | } + 19 | _ := match 4 { + | ~~ + 20 | 1 { f() } + 21 | else { f() } +vlib/v/checker/tests/assign_multi_mismatch.vv:23:12: error: cannot use multi-value (int, int) in single-value context + 21 | else { f() } + 22 | } + 23 | _, _ := 0, match 4 { + | ~~~~~~~~~ + 24 | 1 { f() } + 25 | else { f() } +vlib/v/checker/tests/assign_multi_mismatch.vv:29:3: error: assignment mismatch: 1 variable(s) 2 value(s) + 27 | + 28 | _, _ := 0, if true { 0 } else { 1 } + 29 | _ := if true { f() } else { f() } + | ~~ + 30 | _, _ := 0, if true { f() } else { f() } +vlib/v/checker/tests/assign_multi_mismatch.vv:30:12: error: cannot use multi-value (int, int) in single-value context + 28 | _, _ := 0, if true { 0 } else { 1 } + 29 | _ := if true { f() } else { f() } + 30 | _, _ := 0, if true { f() } else { f() } + | ~~ diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_multi_mismatch.vv b/v_windows/v/old/vlib/v/checker/tests/assign_multi_mismatch.vv new file mode 100644 index 0000000..d25815c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_multi_mismatch.vv @@ -0,0 +1,30 @@ +fn f() (int, int) { + return 0, 0 +} + +_ := 0, 0 +_ := f() +_, _ := f() +_, _ := 0, f() +_, _ := f(), 0 +_, _, _ := 0, f() +_, _, _ := f(), 0 +_, _ := f(), f() +_, _, _, _ := f(), f() + +_, _ := 0, match 4 { + 1 { 0 } + else { 1 } +} +_ := match 4 { + 1 { f() } + else { f() } +} +_, _ := 0, match 4 { + 1 { f() } + else { f() } +} + +_, _ := 0, if true { 0 } else { 1 } +_ := if true { f() } else { f() } +_, _ := 0, if true { f() } else { f() } diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_mut.out b/v_windows/v/old/vlib/v/checker/tests/assign_mut.out new file mode 100644 index 0000000..93a1d06 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_mut.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/assign_mut.vv:3:11: error: expecting `:=` (e.g. `mut x :=`) + 1 | fn main() { + 2 | mut z := 1 + 3 | mut z = 1 + | ^ + 4 | mut i := 2 + 5 | i, mut z = 2,3 diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_mut.vv b/v_windows/v/old/vlib/v/checker/tests/assign_mut.vv new file mode 100644 index 0000000..be80643 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_mut.vv @@ -0,0 +1,6 @@ +fn main() { + mut z := 1 + mut z = 1 + mut i := 2 + i, mut z = 2,3 +} diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_sumtype2_err.out b/v_windows/v/old/vlib/v/checker/tests/assign_sumtype2_err.out new file mode 100644 index 0000000..9e1b60a --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_sumtype2_err.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/assign_sumtype2_err.vv:13:3: error: cannot assign to field `decl`: expected `Decl`, not `Stmt` + 11 | stmt := Stmt(Decl{}) + 12 | _ := File{ + 13 | decl: stmt + | ~~~~~~~~~~ + 14 | } + 15 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_sumtype2_err.vv b/v_windows/v/old/vlib/v/checker/tests/assign_sumtype2_err.vv new file mode 100644 index 0000000..29e36ce --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_sumtype2_err.vv @@ -0,0 +1,15 @@ +type Stmt = Decl | Expr + +struct Decl {} +struct Expr {} + +struct File { + decl Decl +} + +fn main() { + stmt := Stmt(Decl{}) + _ := File{ + decl: stmt + } +} \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_sumtype_err.out b/v_windows/v/old/vlib/v/checker/tests/assign_sumtype_err.out new file mode 100644 index 0000000..ebb4b7a --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_sumtype_err.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/assign_sumtype_err.vv:13:9: error: cannot assign to `decl`: expected `Decl`, not `Stmt` + 11 | stmt := Stmt(Decl{}) + 12 | mut decl := Decl{} + 13 | decl = stmt + | ~~~~ + 14 | _ = decl + 15 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/assign_sumtype_err.vv b/v_windows/v/old/vlib/v/checker/tests/assign_sumtype_err.vv new file mode 100644 index 0000000..94e6fa1 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/assign_sumtype_err.vv @@ -0,0 +1,15 @@ +type Stmt = Decl | Expr + +struct Decl {} +struct Expr {} + +struct File { + decl Decl +} + +fn main() { + stmt := Stmt(Decl{}) + mut decl := Decl{} + decl = stmt + _ = decl +} diff --git a/v_windows/v/old/vlib/v/checker/tests/bad_types_in_string_inter_lit.out b/v_windows/v/old/vlib/v/checker/tests/bad_types_in_string_inter_lit.out new file mode 100644 index 0000000..e6a1cac --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/bad_types_in_string_inter_lit.out @@ -0,0 +1,8 @@ +vlib/v/checker/tests/bad_types_in_string_inter_lit.vv:1:12: error: expression does not return a value + 1 | println('${exit(0)}') + | ~~~~~~~ + 2 | println('${char(48)}') +vlib/v/checker/tests/bad_types_in_string_inter_lit.vv:2:12: error: expression returning type `char` cannot be used in string interpolation directly, print its address or cast it to an integer instead + 1 | println('${exit(0)}') + 2 | println('${char(48)}') + | ~~~~~~~~ diff --git a/v_windows/v/old/vlib/v/checker/tests/bad_types_in_string_inter_lit.vv b/v_windows/v/old/vlib/v/checker/tests/bad_types_in_string_inter_lit.vv new file mode 100644 index 0000000..e4dc26c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/bad_types_in_string_inter_lit.vv @@ -0,0 +1,2 @@ +println('${exit(0)}') +println('${char(48)}') diff --git a/v_windows/v/old/vlib/v/checker/tests/bin_lit_without_digit_err.out b/v_windows/v/old/vlib/v/checker/tests/bin_lit_without_digit_err.out new file mode 100644 index 0000000..772f15d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/bin_lit_without_digit_err.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/bin_lit_without_digit_err.vv:2:14: error: number part of this binary is not provided + 1 | fn main() { + 2 | println(0b**) + | ^ + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/bin_lit_without_digit_err.vv b/v_windows/v/old/vlib/v/checker/tests/bin_lit_without_digit_err.vv new file mode 100644 index 0000000..a85579d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/bin_lit_without_digit_err.vv @@ -0,0 +1,3 @@ +fn main() { + println(0b**) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/bin_lit_wrong_digit_err.out b/v_windows/v/old/vlib/v/checker/tests/bin_lit_wrong_digit_err.out new file mode 100644 index 0000000..e2f993a --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/bin_lit_wrong_digit_err.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/bin_lit_wrong_digit_err.vv:2:18: error: this binary number has unsuitable digit `2` + 1 | fn main() { + 2 | println(0b1112) + | ^ + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/bin_lit_wrong_digit_err.vv b/v_windows/v/old/vlib/v/checker/tests/bin_lit_wrong_digit_err.vv new file mode 100644 index 0000000..b8347fa --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/bin_lit_wrong_digit_err.vv @@ -0,0 +1,3 @@ +fn main() { + println(0b1112) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/bit_op_wrong_left_type_err.out b/v_windows/v/old/vlib/v/checker/tests/bit_op_wrong_left_type_err.out new file mode 100644 index 0000000..e8d7a31 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/bit_op_wrong_left_type_err.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/bit_op_wrong_left_type_err.vv:2:10: error: left type of `&` cannot be non-integer type `float literal` + 1 | fn main() { + 2 | println(0.5 & 1) + | ~~~ + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/bit_op_wrong_left_type_err.vv b/v_windows/v/old/vlib/v/checker/tests/bit_op_wrong_left_type_err.vv new file mode 100644 index 0000000..60b0da5 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/bit_op_wrong_left_type_err.vv @@ -0,0 +1,3 @@ +fn main() { + println(0.5 & 1) +} \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/bit_op_wrong_right_type_err.out b/v_windows/v/old/vlib/v/checker/tests/bit_op_wrong_right_type_err.out new file mode 100644 index 0000000..7b24344 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/bit_op_wrong_right_type_err.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/bit_op_wrong_right_type_err.vv:2:14: error: right type of `|` cannot be non-integer type `float literal` + 1 | fn main() { + 2 | println(1 | 0.5) + | ~~~ + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/bit_op_wrong_right_type_err.vv b/v_windows/v/old/vlib/v/checker/tests/bit_op_wrong_right_type_err.vv new file mode 100644 index 0000000..d9da95c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/bit_op_wrong_right_type_err.vv @@ -0,0 +1,3 @@ +fn main() { + println(1 | 0.5) +} \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/blank_ident_invalid_use.out b/v_windows/v/old/vlib/v/checker/tests/blank_ident_invalid_use.out new file mode 100644 index 0000000..9385664 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/blank_ident_invalid_use.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/blank_ident_invalid_use.vv:2:8: error: undefined ident: `_` (may only be used in assignments) + 1 | fn main() { + 2 | _ := [_] + | ^ + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/blank_ident_invalid_use.vv b/v_windows/v/old/vlib/v/checker/tests/blank_ident_invalid_use.vv new file mode 100644 index 0000000..4be90be --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/blank_ident_invalid_use.vv @@ -0,0 +1,3 @@ +fn main() { + _ := [_] +} diff --git a/v_windows/v/old/vlib/v/checker/tests/blank_modify.out b/v_windows/v/old/vlib/v/checker/tests/blank_modify.out new file mode 100644 index 0000000..e0d02e2 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/blank_modify.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/blank_modify.vv:2:2: error: cannot modify blank `_` identifier + 1 | fn main() { + 2 | _ += 1 + | ^ + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/blank_modify.vv b/v_windows/v/old/vlib/v/checker/tests/blank_modify.vv new file mode 100644 index 0000000..0a22021 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/blank_modify.vv @@ -0,0 +1,3 @@ +fn main() { + _ += 1 +} diff --git a/v_windows/v/old/vlib/v/checker/tests/bool_string_cast_err.out b/v_windows/v/old/vlib/v/checker/tests/bool_string_cast_err.out new file mode 100644 index 0000000..44a1643 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/bool_string_cast_err.out @@ -0,0 +1,12 @@ +vlib/v/checker/tests/bool_string_cast_err.vv:2:13: error: cannot cast type `bool` to string, use `x.str()` instead + 1 | fn main() { + 2 | println(string(true)) + | ~~~~~~~~~~~~ + 3 | println(string(false)) + 4 | } +vlib/v/checker/tests/bool_string_cast_err.vv:3:13: error: cannot cast type `bool` to string, use `x.str()` instead + 1 | fn main() { + 2 | println(string(true)) + 3 | println(string(false)) + | ~~~~~~~~~~~~~ + 4 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/bool_string_cast_err.vv b/v_windows/v/old/vlib/v/checker/tests/bool_string_cast_err.vv new file mode 100644 index 0000000..c261155 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/bool_string_cast_err.vv @@ -0,0 +1,4 @@ +fn main() { + println(string(true)) + println(string(false)) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/break_anon_fn_err.out b/v_windows/v/old/vlib/v/checker/tests/break_anon_fn_err.out new file mode 100644 index 0000000..82b7ab5 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/break_anon_fn_err.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/break_anon_fn_err.vv:4:4: error: break statement not within a loop + 2 | for true { + 3 | _ := fn () int { + 4 | break + | ~~~~~ + 5 | return 3 + 6 | }() diff --git a/v_windows/v/old/vlib/v/checker/tests/break_anon_fn_err.vv b/v_windows/v/old/vlib/v/checker/tests/break_anon_fn_err.vv new file mode 100644 index 0000000..26586e6 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/break_anon_fn_err.vv @@ -0,0 +1,8 @@ +fn main() { + for true { + _ := fn () int { + break + return 3 + }() + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/c_fn_surplus_args.out b/v_windows/v/old/vlib/v/checker/tests/c_fn_surplus_args.out new file mode 100644 index 0000000..a462c84 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/c_fn_surplus_args.out @@ -0,0 +1,42 @@ +vlib/v/checker/tests/c_fn_surplus_args.vv:6:7: error: expected 0 arguments, but got 1 + 4 | + 5 | fn main() { + 6 | C.no(1) // allowed + | ^ + 7 | C.y1() + 8 | C.y1(1) // ok +vlib/v/checker/tests/c_fn_surplus_args.vv:7:4: error: expected 1 arguments, but got 0 + 5 | fn main() { + 6 | C.no(1) // allowed + 7 | C.y1() + | ~~~~ + 8 | C.y1(1) // ok + 9 | C.y1(1, 2) +vlib/v/checker/tests/c_fn_surplus_args.vv:9:10: error: expected 1 arguments, but got 2 + 7 | C.y1() + 8 | C.y1(1) // ok + 9 | C.y1(1, 2) + | ^ + 10 | C.ret() // ok + 11 | C.ret(1) +vlib/v/checker/tests/c_fn_surplus_args.vv:11:8: error: expected 0 arguments, but got 1 + 9 | C.y1(1, 2) + 10 | C.ret() // ok + 11 | C.ret(1) + | ^ + 12 | // avoid cgen whilst warning, later above should error + 13 | main() +vlib/v/checker/tests/c_fn_surplus_args.vv:13:2: error: the `main` function cannot be called in the program + 11 | C.ret(1) + 12 | // avoid cgen whilst warning, later above should error + 13 | main() + | ~~~~~~ + 14 | C.af() // ok + 15 | C.af(3) +vlib/v/checker/tests/c_fn_surplus_args.vv:15:7: error: expected 0 arguments, but got 1 + 13 | main() + 14 | C.af() // ok + 15 | C.af(3) + | ^ + 16 | } + 17 | diff --git a/v_windows/v/old/vlib/v/checker/tests/c_fn_surplus_args.vv b/v_windows/v/old/vlib/v/checker/tests/c_fn_surplus_args.vv new file mode 100644 index 0000000..e7c6466 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/c_fn_surplus_args.vv @@ -0,0 +1,19 @@ +fn C.no() // untyped +fn C.y1(int) +fn C.ret()byte + +fn main() { + C.no(1) // allowed + C.y1() + C.y1(1) // ok + C.y1(1, 2) + C.ret() // ok + C.ret(1) + // avoid cgen whilst warning, later above should error + main() + C.af() // ok + C.af(3) +} + +[trusted] +fn C.af()int diff --git a/v_windows/v/old/vlib/v/checker/tests/cannot_assign_array.out b/v_windows/v/old/vlib/v/checker/tests/cannot_assign_array.out new file mode 100644 index 0000000..fc82be4 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/cannot_assign_array.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/cannot_assign_array.vv:9:11: error: cannot assign to `ctx.vb`: expected `string`, not `[8]f64` + 7 | mut ctx := Context{} + 8 | x := 2.32 + 9 | ctx.vb = [1.1, x, 3.3, 4.4, 5.0, 6.0, 7.0, 8.9]! + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 10 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/cannot_assign_array.vv b/v_windows/v/old/vlib/v/checker/tests/cannot_assign_array.vv new file mode 100644 index 0000000..d6aba2d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/cannot_assign_array.vv @@ -0,0 +1,10 @@ +struct Context { + pub mut: + vb string +} + +fn main() { + mut ctx := Context{} + x := 2.32 + ctx.vb = [1.1, x, 3.3, 4.4, 5.0, 6.0, 7.0, 8.9]! +} diff --git a/v_windows/v/old/vlib/v/checker/tests/cannot_cast_to_alias.out b/v_windows/v/old/vlib/v/checker/tests/cannot_cast_to_alias.out new file mode 100644 index 0000000..6a7960c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/cannot_cast_to_alias.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/cannot_cast_to_alias.vv:6:7: error: cannot convert type `int literal` to `MyType` (alias to `string`) + 4 | + 5 | fn main() { + 6 | _ := MyType(5) + | ~~~~~~~~~ + 7 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/cannot_cast_to_alias.vv b/v_windows/v/old/vlib/v/checker/tests/cannot_cast_to_alias.vv new file mode 100644 index 0000000..1198570 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/cannot_cast_to_alias.vv @@ -0,0 +1,7 @@ +module main + +type MyType = string + +fn main() { + _ := MyType(5) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/cannot_cast_to_struct.out b/v_windows/v/old/vlib/v/checker/tests/cannot_cast_to_struct.out new file mode 100644 index 0000000..9fe4c10 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/cannot_cast_to_struct.out @@ -0,0 +1,27 @@ +vlib/v/checker/tests/cannot_cast_to_struct.vv:10:7: error: casting to struct is deprecated, use e.g. `Struct{...expr}` instead + 8 | + 9 | fn main() { + 10 | _ := Test(Abc{}) + | ~~~~~~~~~~~ + 11 | sum := Alphabet(Xyz{}) + 12 | _ = Xyz(sum) +vlib/v/checker/tests/cannot_cast_to_struct.vv:12:6: error: cannot cast `Alphabet` to struct + 10 | _ := Test(Abc{}) + 11 | sum := Alphabet(Xyz{}) + 12 | _ = Xyz(sum) + | ~~~~~~~~ + 13 | _ = Xyz(5) + 14 | s := Abc{} +vlib/v/checker/tests/cannot_cast_to_struct.vv:13:6: error: cannot cast `int literal` to struct + 11 | sum := Alphabet(Xyz{}) + 12 | _ = Xyz(sum) + 13 | _ = Xyz(5) + | ~~~~~~ + 14 | s := Abc{} + 15 | _ = Xyz(&s) +vlib/v/checker/tests/cannot_cast_to_struct.vv:15:6: error: cannot cast `&Abc` to struct + 13 | _ = Xyz(5) + 14 | s := Abc{} + 15 | _ = Xyz(&s) + | ~~~~~~~ + 16 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/cannot_cast_to_struct.vv b/v_windows/v/old/vlib/v/checker/tests/cannot_cast_to_struct.vv new file mode 100644 index 0000000..64cf7fa --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/cannot_cast_to_struct.vv @@ -0,0 +1,16 @@ +struct Abc {} +struct Xyz {} +type Alphabet = Abc | Xyz + +struct Test { + abc Alphabet +} + +fn main() { + _ := Test(Abc{}) + sum := Alphabet(Xyz{}) + _ = Xyz(sum) + _ = Xyz(5) + s := Abc{} + _ = Xyz(&s) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/cast_err.out b/v_windows/v/old/vlib/v/checker/tests/cast_err.out new file mode 100644 index 0000000..c78a99d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/cast_err.out @@ -0,0 +1,27 @@ +vlib/v/checker/tests/cast_err.vv:3:6: error: cannot cast to bool - use e.g. `some_int != 0` instead + 1 | fn test_bool_cast() { + 2 | v := 3 + 3 | _ = bool(v) + | ~~~~~~~ + 4 | _ = bool(&v) + 5 | _ = bool([2]) +vlib/v/checker/tests/cast_err.vv:4:6: error: cannot cast to bool - use e.g. `some_int != 0` instead + 2 | v := 3 + 3 | _ = bool(v) + 4 | _ = bool(&v) + | ~~~~~~~~ + 5 | _ = bool([2]) + 6 | } +vlib/v/checker/tests/cast_err.vv:5:6: error: cannot cast to bool - use e.g. `some_int != 0` instead + 3 | _ = bool(v) + 4 | _ = bool(&v) + 5 | _ = bool([2]) + | ~~~~~~~~~ + 6 | } + 7 | +vlib/v/checker/tests/cast_err.vv:9:6: error: unknown type `Foo` + 7 | + 8 | fn unknown() { + 9 | _ = Foo(3) + | ~~~~~~ + 10 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/cast_err.vv b/v_windows/v/old/vlib/v/checker/tests/cast_err.vv new file mode 100644 index 0000000..bcf8ad3 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/cast_err.vv @@ -0,0 +1,10 @@ +fn test_bool_cast() { + v := 3 + _ = bool(v) + _ = bool(&v) + _ = bool([2]) +} + +fn unknown() { + _ = Foo(3) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/cast_string_err.out b/v_windows/v/old/vlib/v/checker/tests/cast_string_err.out new file mode 100644 index 0000000..3c09bf4 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/cast_string_err.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/cast_string_err.vv:2:7: error: cannot cast type `int literal` to string, use `x.str()` instead + 1 | fn main() { + 2 | a := string(1) + | ~~~~~~~~~ + 3 | println(a) + 4 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/cast_string_err.vv b/v_windows/v/old/vlib/v/checker/tests/cast_string_err.vv new file mode 100644 index 0000000..56247ce --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/cast_string_err.vv @@ -0,0 +1,4 @@ +fn main() { + a := string(1) + println(a) +} \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/cast_string_with_byte_err.out b/v_windows/v/old/vlib/v/checker/tests/cast_string_with_byte_err.out new file mode 100644 index 0000000..1b04218 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/cast_string_with_byte_err.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/cast_string_with_byte_err.vv:2:12: error: can not cast type `byte` to string, use `by.str()` instead. + 1 | for by in 'abc' { + 2 | println(string(by)) + | ~~~~~~~~~~ + 3 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/cast_string_with_byte_err.vv b/v_windows/v/old/vlib/v/checker/tests/cast_string_with_byte_err.vv new file mode 100644 index 0000000..4626e28 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/cast_string_with_byte_err.vv @@ -0,0 +1,3 @@ +for by in 'abc' { + println(string(by)) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/cast_void.out b/v_windows/v/old/vlib/v/checker/tests/cast_void.out new file mode 100644 index 0000000..ddd389a --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/cast_void.out @@ -0,0 +1,4 @@ +vlib/v/checker/tests/cast_void.vv:1:12: error: expression does not return a value so it cannot be cast + 1 | num := int(print('')) + | ~~~~~~~~~ + 2 | println(num) diff --git a/v_windows/v/old/vlib/v/checker/tests/cast_void.vv b/v_windows/v/old/vlib/v/checker/tests/cast_void.vv new file mode 100644 index 0000000..1b61f14 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/cast_void.vv @@ -0,0 +1,2 @@ +num := int(print('')) +println(num) diff --git a/v_windows/v/old/vlib/v/checker/tests/chan_args.out b/v_windows/v/old/vlib/v/checker/tests/chan_args.out new file mode 100644 index 0000000..605bcfe --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/chan_args.out @@ -0,0 +1,28 @@ +vlib/v/checker/tests/chan_args.vv:4:19: error: cannot use `int` as `&f64` in argument 1 to `chan f64.try_push` + 2 | ch := chan f64{cap: 5} + 3 | a := 2 + 4 | _ := ch.try_push(a) + | ^ + 5 | _ := ch.try_push(2.5) + 6 | b := 2.5 +vlib/v/checker/tests/chan_args.vv:5:19: error: cannot use `float literal` as `&f64` in argument 1 to `chan f64.try_push` + 3 | a := 2 + 4 | _ := ch.try_push(a) + 5 | _ := ch.try_push(2.5) + | ~~~ + 6 | b := 2.5 + 7 | _ := ch.try_pop(b) +vlib/v/checker/tests/chan_args.vv:7:18: error: `try_pop` parameter `obj` is `mut`, you need to provide `mut` e.g. `mut arg1` + 5 | _ := ch.try_push(2.5) + 6 | b := 2.5 + 7 | _ := ch.try_pop(b) + | ^ + 8 | // this should work: + 9 | _ := ch.try_push(b) +vlib/v/checker/tests/chan_args.vv:11:22: error: cannot use `int` as argument for `try_pop` (`f64` expected) + 9 | _ := ch.try_push(b) + 10 | mut c := 7 + 11 | _ := ch.try_pop(mut c) + | ^ + 12 | mut x := 12.5 + 13 | // this should work: diff --git a/v_windows/v/old/vlib/v/checker/tests/chan_args.vv b/v_windows/v/old/vlib/v/checker/tests/chan_args.vv new file mode 100644 index 0000000..ce16cd8 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/chan_args.vv @@ -0,0 +1,15 @@ +fn main() { + ch := chan f64{cap: 5} + a := 2 + _ := ch.try_push(a) + _ := ch.try_push(2.5) + b := 2.5 + _ := ch.try_pop(b) + // this should work: + _ := ch.try_push(b) + mut c := 7 + _ := ch.try_pop(mut c) + mut x := 12.5 + // this should work: + _ := ch.try_pop(mut x) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/chan_mut.out b/v_windows/v/old/vlib/v/checker/tests/chan_mut.out new file mode 100644 index 0000000..e82419b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/chan_mut.out @@ -0,0 +1,28 @@ +vlib/v/checker/tests/chan_mut.vv:8:8: error: `v` is immutable, declare it with `mut` to make it mutable + 6 | fn f(ch chan mut St) { + 7 | v := St{} + 8 | ch <- v + | ^ + 9 | mut w := St{} + 10 | ch <- w +vlib/v/checker/tests/chan_mut.vv:10:8: error: cannot push non-reference `St` on `chan mut St` + 8 | ch <- v + 9 | mut w := St{} + 10 | ch <- w + | ^ + 11 | x := &St{} + 12 | ch <- x +vlib/v/checker/tests/chan_mut.vv:12:8: error: `x` is immutable, declare it with `mut` to make it mutable + 10 | ch <- w + 11 | x := &St{} + 12 | ch <- x + | ^ + 13 | mut y := St{} + 14 | ch <- y +vlib/v/checker/tests/chan_mut.vv:14:8: error: cannot push non-reference `St` on `chan mut St` + 12 | ch <- x + 13 | mut y := St{} + 14 | ch <- y + | ^ + 15 | mut z := &St{n: 7} + 16 | // this works diff --git a/v_windows/v/old/vlib/v/checker/tests/chan_mut.vv b/v_windows/v/old/vlib/v/checker/tests/chan_mut.vv new file mode 100644 index 0000000..1250c51 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/chan_mut.vv @@ -0,0 +1,27 @@ +struct St{ +mut: + n int +} + +fn f(ch chan mut St) { + v := St{} + ch <- v + mut w := St{} + ch <- w + x := &St{} + ch <- x + mut y := St{} + ch <- y + mut z := &St{n: 7} + // this works + ch <- z +} + +fn main() { + c := chan mut St{} + go f(c) + mut y := <-c + z := <-c + println(y) + println(z) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/chan_ref.out b/v_windows/v/old/vlib/v/checker/tests/chan_ref.out new file mode 100644 index 0000000..3eeb76b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/chan_ref.out @@ -0,0 +1,14 @@ +vlib/v/checker/tests/chan_ref.vv:10:8: error: cannot push non-reference `St` on `chan &St` + 8 | fn f(ch chan &St, mut sem sync.Semaphore) { + 9 | w := St{} + 10 | ch <- w + | ^ + 11 | mut x := St{} + 12 | ch <- x +vlib/v/checker/tests/chan_ref.vv:12:8: error: cannot push non-reference `St` on `chan &St` + 10 | ch <- w + 11 | mut x := St{} + 12 | ch <- x + | ^ + 13 | // the following works + 14 | y := &St{} diff --git a/v_windows/v/old/vlib/v/checker/tests/chan_ref.vv b/v_windows/v/old/vlib/v/checker/tests/chan_ref.vv new file mode 100644 index 0000000..ad3dcec --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/chan_ref.vv @@ -0,0 +1,33 @@ +import sync + +struct St{ +mut: + n int +} + +fn f(ch chan &St, mut sem sync.Semaphore) { + w := St{} + ch <- w + mut x := St{} + ch <- x + // the following works + y := &St{} + ch <- y + mut z := &St{} + ch <- z + sem.wait() + println(z) +} + +fn main() { + c := chan &St{} + mut sem := sync.new_semaphore() + go f(c, mut sem) + y := <-c + // this should fail + mut z := <-c + z.n = 9 + sem.post() + println(y) + println(z) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/char_str.out b/v_windows/v/old/vlib/v/checker/tests/char_str.out new file mode 100644 index 0000000..9779e64 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/char_str.out @@ -0,0 +1,3 @@ +vlib/v/checker/tests/char_str.vv:1:1: error: calling `.str()` on type `char` is not allowed, use its address or cast it to an integer instead + 1 | char(91).str() + | ~~~~~~~~~~~~~~ diff --git a/v_windows/v/old/vlib/v/checker/tests/char_str.vv b/v_windows/v/old/vlib/v/checker/tests/char_str.vv new file mode 100644 index 0000000..88d9a9b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/char_str.vv @@ -0,0 +1 @@ +char(91).str() diff --git a/v_windows/v/old/vlib/v/checker/tests/comparing_typesymbol_to_a_type_should_not_compile.out b/v_windows/v/old/vlib/v/checker/tests/comparing_typesymbol_to_a_type_should_not_compile.out new file mode 100644 index 0000000..3b8aae9 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/comparing_typesymbol_to_a_type_should_not_compile.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/comparing_typesymbol_to_a_type_should_not_compile.vv:12:7: error: possible type mismatch of compared values of `==` operation + 10 | x := ityp == ast.string_type + 11 | // the next line should produce at least a warning, or even an error, without an explicit cast: + 12 | z := isym == ast.string_type + | ~~~~~~~~~~~~~~~~~~~~~~~ + 13 | println(typeof(isym).name) + 14 | println(typeof(ast.string_type).name) diff --git a/v_windows/v/old/vlib/v/checker/tests/comparing_typesymbol_to_a_type_should_not_compile.vv b/v_windows/v/old/vlib/v/checker/tests/comparing_typesymbol_to_a_type_should_not_compile.vv new file mode 100644 index 0000000..6a307d3 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/comparing_typesymbol_to_a_type_should_not_compile.vv @@ -0,0 +1,17 @@ +import v.ast + +fn main() { + t := ast.new_table() + ityp := ast.int_type + isym := t.get_type_symbol(ityp) + println(ityp.debug()) + println(isym) + println(isym.debug()) + x := ityp == ast.string_type + // the next line should produce at least a warning, or even an error, without an explicit cast: + z := isym == ast.string_type + println(typeof(isym).name) + println(typeof(ast.string_type).name) + println(x) + println(z) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/comptime_call_method.out b/v_windows/v/old/vlib/v/checker/tests/comptime_call_method.out new file mode 100644 index 0000000..de71203 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/comptime_call_method.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/comptime_call_method.vv:10:14: error: undefined ident: `wrong` + 8 | s1 := S1{} + 9 | $for method in S1.methods { + 10 | s1.$method(wrong) + | ~~~~~ + 11 | } + 12 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/comptime_call_method.vv b/v_windows/v/old/vlib/v/checker/tests/comptime_call_method.vv new file mode 100644 index 0000000..2c21789 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/comptime_call_method.vv @@ -0,0 +1,12 @@ +struct S1 {} + +fn (t S1) m(s string) int { + return 7 +} + +fn test_methods_arg() { + s1 := S1{} + $for method in S1.methods { + s1.$method(wrong) + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/comptime_call_no_unused_var.out b/v_windows/v/old/vlib/v/checker/tests/comptime_call_no_unused_var.out new file mode 100644 index 0000000..17aae7c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/comptime_call_no_unused_var.out @@ -0,0 +1,27 @@ +vlib/v/checker/tests/comptime_call_no_unused_var.vv:11:8: error: unknown identifier `w` + 9 | abc := 'print' + 10 | test.$abc() // OK + 11 | test.$w() + | ^ + 12 | v := 4 + 13 | test.$v() +vlib/v/checker/tests/comptime_call_no_unused_var.vv:13:8: error: invalid string method call: expected `string`, not `int` + 11 | test.$w() + 12 | v := 4 + 13 | test.$v() + | ^ + 14 | s := 'x' + 'y' + 15 | test.$s() +vlib/v/checker/tests/comptime_call_no_unused_var.vv:15:8: error: todo: not a string literal + 13 | test.$v() + 14 | s := 'x' + 'y' + 15 | test.$s() + | ^ + 16 | s2 := 'x' + 17 | test.$s2() +vlib/v/checker/tests/comptime_call_no_unused_var.vv:17:8: error: could not find method `x` + 15 | test.$s() + 16 | s2 := 'x' + 17 | test.$s2() + | ~~ + 18 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/comptime_call_no_unused_var.vv b/v_windows/v/old/vlib/v/checker/tests/comptime_call_no_unused_var.vv new file mode 100644 index 0000000..b38c031 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/comptime_call_no_unused_var.vv @@ -0,0 +1,18 @@ +struct Test {} + +fn (test Test) print() { + println('test') +} + +fn main() { + test := Test{} + abc := 'print' + test.$abc() // OK + test.$w() + v := 4 + test.$v() + s := 'x' + 'y' + test.$s() + s2 := 'x' + test.$s2() +} diff --git a/v_windows/v/old/vlib/v/checker/tests/comptime_env/env_parser_errors_1.run.out b/v_windows/v/old/vlib/v/checker/tests/comptime_env/env_parser_errors_1.run.out new file mode 100644 index 0000000..098d716 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/comptime_env/env_parser_errors_1.run.out @@ -0,0 +1,3 @@ +vlib/v/checker/tests/comptime_env/env_parser_errors_1.vv:1:1: error: supply an env variable name like HOME, PATH or USER + 1 | #flag -I $env('')/xyz + | ~~~~~~~~~~~~~~~~~~~~~ diff --git a/v_windows/v/old/vlib/v/checker/tests/comptime_env/env_parser_errors_1.vv b/v_windows/v/old/vlib/v/checker/tests/comptime_env/env_parser_errors_1.vv new file mode 100644 index 0000000..83b2ff3 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/comptime_env/env_parser_errors_1.vv @@ -0,0 +1 @@ +#flag -I $env('')/xyz diff --git a/v_windows/v/old/vlib/v/checker/tests/comptime_env/env_parser_errors_2.run.out b/v_windows/v/old/vlib/v/checker/tests/comptime_env/env_parser_errors_2.run.out new file mode 100644 index 0000000..0cc6a11 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/comptime_env/env_parser_errors_2.run.out @@ -0,0 +1,3 @@ +vlib/v/checker/tests/comptime_env/env_parser_errors_2.vv:1:1: error: cannot use string interpolation in compile time $env() expression + 1 | #flag -I $env('$ABC')/xyz + | ~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/v_windows/v/old/vlib/v/checker/tests/comptime_env/env_parser_errors_2.vv b/v_windows/v/old/vlib/v/checker/tests/comptime_env/env_parser_errors_2.vv new file mode 100644 index 0000000..abd0b76 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/comptime_env/env_parser_errors_2.vv @@ -0,0 +1 @@ +#flag -I $env('$ABC')/xyz diff --git a/v_windows/v/old/vlib/v/checker/tests/comptime_env/env_parser_errors_3.run.out b/v_windows/v/old/vlib/v/checker/tests/comptime_env/env_parser_errors_3.run.out new file mode 100644 index 0000000..eab0231 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/comptime_env/env_parser_errors_3.run.out @@ -0,0 +1,3 @@ +vlib/v/checker/tests/comptime_env/env_parser_errors_3.vv:1:1: error: no "$env('...')" could be found in "-I $env()/xyz". + 1 | #flag -I $env()/xyz + | ~~~~~~~~~~~~~~~~~~~ diff --git a/v_windows/v/old/vlib/v/checker/tests/comptime_env/env_parser_errors_3.vv b/v_windows/v/old/vlib/v/checker/tests/comptime_env/env_parser_errors_3.vv new file mode 100644 index 0000000..98fc6fe --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/comptime_env/env_parser_errors_3.vv @@ -0,0 +1 @@ +#flag -I $env()/xyz diff --git a/v_windows/v/old/vlib/v/checker/tests/comptime_env/using_comptime_env.run.out b/v_windows/v/old/vlib/v/checker/tests/comptime_env/using_comptime_env.run.out new file mode 100644 index 0000000..962652a --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/comptime_env/using_comptime_env.run.out @@ -0,0 +1,11 @@ +vlib/v/checker/tests/comptime_env/using_comptime_env.vv:1:1: error: the environment variable "VAR" does not exist. + 1 | #flag -I $env('VAR')/xyz + | ~~~~~~~~~~~~~~~~~~~~~~~~ + 2 | #include "$env('VAR')/stdio.h" + 3 | +vlib/v/checker/tests/comptime_env/using_comptime_env.vv:2:1: error: the environment variable "VAR" does not exist. + 1 | #flag -I $env('VAR')/xyz + 2 | #include "$env('VAR')/stdio.h" + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 3 | + 4 | fn main() { diff --git a/v_windows/v/old/vlib/v/checker/tests/comptime_env/using_comptime_env.var.run.out b/v_windows/v/old/vlib/v/checker/tests/comptime_env/using_comptime_env.var.run.out new file mode 100644 index 0000000..012aa03 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/comptime_env/using_comptime_env.var.run.out @@ -0,0 +1,2 @@ +/usr/include +done diff --git a/v_windows/v/old/vlib/v/checker/tests/comptime_env/using_comptime_env.var_invalid.run.out b/v_windows/v/old/vlib/v/checker/tests/comptime_env/using_comptime_env.var_invalid.run.out new file mode 100644 index 0000000..0739bb2 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/comptime_env/using_comptime_env.var_invalid.run.out @@ -0,0 +1 @@ +builder error: '/opt/invalid/path/stdio.h' not found diff --git a/v_windows/v/old/vlib/v/checker/tests/comptime_env/using_comptime_env.vv b/v_windows/v/old/vlib/v/checker/tests/comptime_env/using_comptime_env.vv new file mode 100644 index 0000000..f49508c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/comptime_env/using_comptime_env.vv @@ -0,0 +1,8 @@ +#flag -I $env('VAR')/xyz +#include "$env('VAR')/stdio.h" + +fn main() { + env := $env('VAR') + println(env) + println('done') +} diff --git a/v_windows/v/old/vlib/v/checker/tests/comptime_field_selector_not_in_for_err.out b/v_windows/v/old/vlib/v/checker/tests/comptime_field_selector_not_in_for_err.out new file mode 100644 index 0000000..bc3e1f9 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/comptime_field_selector_not_in_for_err.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/comptime_field_selector_not_in_for_err.vv:9:6: error: expected selector expression e.g. `$(field.name)` + 7 | mut t := T{} + 8 | name := 'test' + 9 | t.$(name) = '3' + | ~~~~ + 10 | } + 11 | diff --git a/v_windows/v/old/vlib/v/checker/tests/comptime_field_selector_not_in_for_err.vv b/v_windows/v/old/vlib/v/checker/tests/comptime_field_selector_not_in_for_err.vv new file mode 100644 index 0000000..bbbf854 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/comptime_field_selector_not_in_for_err.vv @@ -0,0 +1,14 @@ +struct Foo { + test int + name string +} + +fn test() { + mut t := T{} + name := 'test' + t.$(name) = '3' +} + +fn main() { + test() +} diff --git a/v_windows/v/old/vlib/v/checker/tests/comptime_field_selector_not_name_err.out b/v_windows/v/old/vlib/v/checker/tests/comptime_field_selector_not_name_err.out new file mode 100644 index 0000000..c53955c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/comptime_field_selector_not_name_err.out @@ -0,0 +1,21 @@ +vlib/v/checker/tests/comptime_field_selector_not_name_err.vv:10:8: error: expected `string` instead of `FieldData` (e.g. `field.name`) + 8 | $for f in T.fields { + 9 | $if f.typ is string { + 10 | t.$(f) = '3' + | ^ + 11 | fv := Foo{} + 12 | _ = t.$(fv.name) +vlib/v/checker/tests/comptime_field_selector_not_name_err.vv:12:12: error: unknown `$for` variable `fv` + 10 | t.$(f) = '3' + 11 | fv := Foo{} + 12 | _ = t.$(fv.name) + | ~~ + 13 | } + 14 | } +vlib/v/checker/tests/comptime_field_selector_not_name_err.vv:15:10: error: undefined ident: `f` + 13 | } + 14 | } + 15 | _ = t.$(f.name) + | ^ + 16 | } + 17 | diff --git a/v_windows/v/old/vlib/v/checker/tests/comptime_field_selector_not_name_err.vv b/v_windows/v/old/vlib/v/checker/tests/comptime_field_selector_not_name_err.vv new file mode 100644 index 0000000..9efd144 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/comptime_field_selector_not_name_err.vv @@ -0,0 +1,20 @@ +struct Foo { + test int + name string +} + +fn test() { + mut t := T{} + $for f in T.fields { + $if f.typ is string { + t.$(f) = '3' + fv := Foo{} + _ = t.$(fv.name) + } + } + _ = t.$(f.name) +} + +fn main() { + test() +} diff --git a/v_windows/v/old/vlib/v/checker/tests/comptime_for.out b/v_windows/v/old/vlib/v/checker/tests/comptime_for.out new file mode 100644 index 0000000..0eea091 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/comptime_for.out @@ -0,0 +1,55 @@ +vlib/v/checker/tests/comptime_for.vv:2:12: error: unknown type `Huh` + 1 | fn unknown() { + 2 | $for m in Huh.methods {} + | ~~~ + 3 | $for f in Huh.fields {} + 4 | $for f in T.fields { +vlib/v/checker/tests/comptime_for.vv:3:12: error: unknown type `Huh` + 1 | fn unknown() { + 2 | $for m in Huh.methods {} + 3 | $for f in Huh.fields {} + | ~~~ + 4 | $for f in T.fields { + 5 | $if f.typ is Huh {} +vlib/v/checker/tests/comptime_for.vv:4:12: error: unknown type `T` + 2 | $for m in Huh.methods {} + 3 | $for f in Huh.fields {} + 4 | $for f in T.fields { + | ^ + 5 | $if f.typ is Huh {} + 6 | $if f.typ is T {} +vlib/v/checker/tests/comptime_for.vv:5:16: error: unknown type `Huh` + 3 | $for f in Huh.fields {} + 4 | $for f in T.fields { + 5 | $if f.typ is Huh {} + | ~~~ + 6 | $if f.typ is T {} + 7 | } +vlib/v/checker/tests/comptime_for.vv:6:16: error: unknown type `T` + 4 | $for f in T.fields { + 5 | $if f.typ is Huh {} + 6 | $if f.typ is T {} + | ^ + 7 | } + 8 | _ = m +vlib/v/checker/tests/comptime_for.vv:8:6: error: undefined ident: `m` + 6 | $if f.typ is T {} + 7 | } + 8 | _ = m + | ^ + 9 | } + 10 | +vlib/v/checker/tests/comptime_for.vv:14:16: error: unknown type `U` + 12 | $for f in T.fields { + 13 | $if f.typ is T {} + 14 | $if f.typ is U {} + | ^ + 15 | } + 16 | _ = f +vlib/v/checker/tests/comptime_for.vv:16:6: error: undefined ident: `f` + 14 | $if f.typ is U {} + 15 | } + 16 | _ = f + | ^ + 17 | } + 18 | diff --git a/v_windows/v/old/vlib/v/checker/tests/comptime_for.vv b/v_windows/v/old/vlib/v/checker/tests/comptime_for.vv new file mode 100644 index 0000000..a6d67a0 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/comptime_for.vv @@ -0,0 +1,23 @@ +fn unknown() { + $for m in Huh.methods {} + $for f in Huh.fields {} + $for f in T.fields { + $if f.typ is Huh {} + $if f.typ is T {} + } + _ = m +} + +fn gf() { + $for f in T.fields { + $if f.typ is T {} + $if f.typ is U {} + } + _ = f +} + +struct S1 { + i int +} + +gf() diff --git a/v_windows/v/old/vlib/v/checker/tests/const_array_unknown_type_err.out b/v_windows/v/old/vlib/v/checker/tests/const_array_unknown_type_err.out new file mode 100644 index 0000000..8b55d3f --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/const_array_unknown_type_err.out @@ -0,0 +1,4 @@ +vlib/v/checker/tests/const_array_unknown_type_err.vv:1:11: error: unknown type `BB`. +Did you mean `AA`? + 1 | type AA = [20]BB + | ~~~~~~ diff --git a/v_windows/v/old/vlib/v/checker/tests/const_array_unknown_type_err.vv b/v_windows/v/old/vlib/v/checker/tests/const_array_unknown_type_err.vv new file mode 100644 index 0000000..88a2bf7 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/const_array_unknown_type_err.vv @@ -0,0 +1 @@ +type AA = [20]BB diff --git a/v_windows/v/old/vlib/v/checker/tests/const_define_in_function_err.out b/v_windows/v/old/vlib/v/checker/tests/const_define_in_function_err.out new file mode 100644 index 0000000..e3b3544 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/const_define_in_function_err.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/const_define_in_function_err.vv:2:2: error: const can only be defined at the top level (outside of functions) + 1 | fn main() { + 2 | const (a = 1) + | ~~~~~ + 3 | println(a) + 4 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/const_define_in_function_err.vv b/v_windows/v/old/vlib/v/checker/tests/const_define_in_function_err.vv new file mode 100644 index 0000000..e6da548 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/const_define_in_function_err.vv @@ -0,0 +1,4 @@ +fn main() { + const (a = 1) + println(a) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/const_field_add_err.out b/v_windows/v/old/vlib/v/checker/tests/const_field_add_err.out new file mode 100644 index 0000000..1250582 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/const_field_add_err.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/const_field_add_err.vv:6:2: error: cannot modify constant `a` + 4 | + 5 | fn main() { + 6 | a += 1 + | ^ + 7 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/const_field_add_err.vv b/v_windows/v/old/vlib/v/checker/tests/const_field_add_err.vv new file mode 100644 index 0000000..24a6bb5 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/const_field_add_err.vv @@ -0,0 +1,7 @@ +const ( + a = 1 +) + +fn main() { + a += 1 +} diff --git a/v_windows/v/old/vlib/v/checker/tests/const_field_dec_err.out b/v_windows/v/old/vlib/v/checker/tests/const_field_dec_err.out new file mode 100644 index 0000000..e0b9acb --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/const_field_dec_err.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/const_field_dec_err.vv:6:2: error: cannot modify constant `a` + 4 | + 5 | fn main() { + 6 | a-- + | ^ + 7 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/const_field_dec_err.vv b/v_windows/v/old/vlib/v/checker/tests/const_field_dec_err.vv new file mode 100644 index 0000000..0f99839 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/const_field_dec_err.vv @@ -0,0 +1,7 @@ +const ( + a = 1 +) + +fn main() { + a-- +} diff --git a/v_windows/v/old/vlib/v/checker/tests/const_field_inc_err.out b/v_windows/v/old/vlib/v/checker/tests/const_field_inc_err.out new file mode 100644 index 0000000..adeee60 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/const_field_inc_err.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/const_field_inc_err.vv:6:2: error: cannot modify constant `a` + 4 | + 5 | fn main() { + 6 | a++ + | ^ + 7 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/const_field_inc_err.vv b/v_windows/v/old/vlib/v/checker/tests/const_field_inc_err.vv new file mode 100644 index 0000000..9d0d96e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/const_field_inc_err.vv @@ -0,0 +1,7 @@ +const ( + a = 1 +) + +fn main() { + a++ +} diff --git a/v_windows/v/old/vlib/v/checker/tests/const_field_name_duplicate_err.out b/v_windows/v/old/vlib/v/checker/tests/const_field_name_duplicate_err.out new file mode 100644 index 0000000..e887c13 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/const_field_name_duplicate_err.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/const_field_name_duplicate_err.vv:3:2: error: duplicate const `aaa` + 1 | const ( + 2 | aaa = 1 + 3 | aaa = 2 + | ~~~ + 4 | ) + 5 | fn main() { diff --git a/v_windows/v/old/vlib/v/checker/tests/const_field_name_duplicate_err.vv b/v_windows/v/old/vlib/v/checker/tests/const_field_name_duplicate_err.vv new file mode 100644 index 0000000..f489f51 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/const_field_name_duplicate_err.vv @@ -0,0 +1,7 @@ +const ( + aaa = 1 + aaa = 2 +) +fn main() { + println(aaa) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/const_field_name_snake_case.out b/v_windows/v/old/vlib/v/checker/tests/const_field_name_snake_case.out new file mode 100644 index 0000000..ff997e4 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/const_field_name_snake_case.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/const_field_name_snake_case.vv:2:2: warning: const names cannot contain uppercase letters, use snake_case instead + 1 | const ( + 2 | Red = 1 + | ~~~ + 3 | ) + 4 | fn main() { println(Red) } diff --git a/v_windows/v/old/vlib/v/checker/tests/const_field_name_snake_case.vv b/v_windows/v/old/vlib/v/checker/tests/const_field_name_snake_case.vv new file mode 100644 index 0000000..cd8af8e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/const_field_name_snake_case.vv @@ -0,0 +1,4 @@ +const ( + Red = 1 +) +fn main() { println(Red) } diff --git a/v_windows/v/old/vlib/v/checker/tests/const_field_sub_err.out b/v_windows/v/old/vlib/v/checker/tests/const_field_sub_err.out new file mode 100644 index 0000000..782520b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/const_field_sub_err.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/const_field_sub_err.vv:6:2: error: cannot modify constant `a` + 4 | + 5 | fn main() { + 6 | a -= 1 + | ^ + 7 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/const_field_sub_err.vv b/v_windows/v/old/vlib/v/checker/tests/const_field_sub_err.vv new file mode 100644 index 0000000..e6c021b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/const_field_sub_err.vv @@ -0,0 +1,7 @@ +const ( + a = 1 +) + +fn main() { + a -= 1 +} diff --git a/v_windows/v/old/vlib/v/checker/tests/ctdefine.out b/v_windows/v/old/vlib/v/checker/tests/ctdefine.out new file mode 100644 index 0000000..008a7d6 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/ctdefine.out @@ -0,0 +1,12 @@ +vlib/v/checker/tests/ctdefine.vv:4:1: error: only functions that do NOT return values can have `[if test]` tags + 2 | + 3 | [if test] + 4 | fn only_called_in_test() string { + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 5 | return 'bah' + 6 | } +vlib/v/checker/tests/ctdefine.vv:1:1: error: project must include a `main` module or be a shared library (compile with `v -shared`) + 1 | module notmain + | ^ + 2 | + 3 | [if test] diff --git a/v_windows/v/old/vlib/v/checker/tests/ctdefine.vv b/v_windows/v/old/vlib/v/checker/tests/ctdefine.vv new file mode 100644 index 0000000..20d1eb0 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/ctdefine.vv @@ -0,0 +1,6 @@ +module notmain + +[if test] +fn only_called_in_test() string { + return 'bah' +} diff --git a/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_error.mysymbol.run.out b/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_error.mysymbol.run.out new file mode 100644 index 0000000..97a3e70 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_error.mysymbol.run.out @@ -0,0 +1,2 @@ +optional compitme define works +non optional comptime define works diff --git a/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_error.out b/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_error.out new file mode 100644 index 0000000..b916d1d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_error.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/custom_comptime_define_error.vv:6:13: error: undefined ident: `mysymbol` + 4 | println('optional compitme define works') + 5 | } + 6 | $if mysymbol { + | ~~~~~~~~ + 7 | // this will produce a checker error when `-d mysymbol` is not given on the CLI + 8 | println('non optional comptime define works') diff --git a/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_error.vv b/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_error.vv new file mode 100644 index 0000000..34f9967 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_error.vv @@ -0,0 +1,10 @@ +fn main() { + $if mysymbol? { + // this should not produce checker errors, but will print only with `-d mysymbol` + println('optional compitme define works') + } + $if mysymbol { + // this will produce a checker error when `-d mysymbol` is not given on the CLI + println('non optional comptime define works') + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_debug.cg.run.out b/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_debug.cg.run.out new file mode 100644 index 0000000..b8cdbba --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_debug.cg.run.out @@ -0,0 +1,3 @@ +main with debug +foo, x: 123 +done diff --git a/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_debug.debug.bar.run.out b/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_debug.debug.bar.run.out new file mode 100644 index 0000000..702459b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_debug.debug.bar.run.out @@ -0,0 +1,3 @@ +foo, x: 123 +bar, x: 456 +done diff --git a/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_debug.debug.run.out b/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_debug.debug.run.out new file mode 100644 index 0000000..8cdd401 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_debug.debug.run.out @@ -0,0 +1,2 @@ +foo, x: 123 +done diff --git a/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_debug.g.run.out b/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_debug.g.run.out new file mode 100644 index 0000000..b8cdbba --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_debug.g.run.out @@ -0,0 +1,3 @@ +main with debug +foo, x: 123 +done diff --git a/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_debug.out b/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_debug.out new file mode 100644 index 0000000..e69de29 diff --git a/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_debug.run.out b/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_debug.run.out new file mode 100644 index 0000000..19f86f4 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_debug.run.out @@ -0,0 +1 @@ +done diff --git a/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_debug.vv b/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_debug.vv new file mode 100644 index 0000000..380f738 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_debug.vv @@ -0,0 +1,18 @@ +[if debug] +fn foo(x int) { + println('foo, x: $x') +} + +[if bar ?] +fn bar(x int) { + println('bar, x: $x') +} + +fn main() { + $if debug { + println('main with debug') + } + foo(123) // will not be called if `-d debug` is not passed + bar(456) // will not be called if `-d bar` is not passed + println('done') +} diff --git a/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_flag.mydebug.run.out b/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_flag.mydebug.run.out new file mode 100644 index 0000000..7cf4d92 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_flag.mydebug.run.out @@ -0,0 +1,4 @@ +start +message: verbose message +message: debugging system +end diff --git a/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_flag.nodebug.run.out b/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_flag.nodebug.run.out new file mode 100644 index 0000000..5d0fb3b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_flag.nodebug.run.out @@ -0,0 +1,2 @@ +start +end diff --git a/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_flag.out b/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_flag.out new file mode 100644 index 0000000..e69de29 diff --git a/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_flag.vv b/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_flag.vv new file mode 100644 index 0000000..23e946a --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/custom_comptime_define_if_flag.vv @@ -0,0 +1,13 @@ +// Calls to edebug/1 should be executed only when `-d mydebug` is passed on the CLI +// Otherwise they will not be present *at all* in the generated code. +[if mydebug ?] +fn edebug(message string) { + println('message: $message') +} + +fn main() { + println('start') + edebug('verbose message') + edebug('debugging system') + println('end') +} diff --git a/v_windows/v/old/vlib/v/checker/tests/dec_lit_wrong_digit_err.out b/v_windows/v/old/vlib/v/checker/tests/dec_lit_wrong_digit_err.out new file mode 100644 index 0000000..9166f4c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/dec_lit_wrong_digit_err.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/dec_lit_wrong_digit_err.vv:2:18: error: this number has unsuitable digit `q` + 1 | fn main() { + 2 | println(12345qrst+10) + | ^ + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/dec_lit_wrong_digit_err.vv b/v_windows/v/old/vlib/v/checker/tests/dec_lit_wrong_digit_err.vv new file mode 100644 index 0000000..c8c9583 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/dec_lit_wrong_digit_err.vv @@ -0,0 +1,3 @@ +fn main() { + println(12345qrst+10) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/decompose_type_err.out b/v_windows/v/old/vlib/v/checker/tests/decompose_type_err.out new file mode 100644 index 0000000..95d56f6 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/decompose_type_err.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/decompose_type_err.vv:4:13: error: decomposition can only be used on arrays + 2 | + 3 | fn main() { + 4 | varargs(...123) + | ~~~ + 5 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/decompose_type_err.vv b/v_windows/v/old/vlib/v/checker/tests/decompose_type_err.vv new file mode 100644 index 0000000..d5b43ba --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/decompose_type_err.vv @@ -0,0 +1,5 @@ +fn varargs(a ...int) { println(a) } + +fn main() { + varargs(...123) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/defer_in_for.out b/v_windows/v/old/vlib/v/checker/tests/defer_in_for.out new file mode 100644 index 0000000..59faded --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/defer_in_for.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/defer_in_for.vv:4:13: error: `break` is not allowed in defer statements + 2 | for true { + 3 | defer { + 4 | break + | ~~~~~ + 5 | } + 6 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/defer_in_for.vv b/v_windows/v/old/vlib/v/checker/tests/defer_in_for.vv new file mode 100644 index 0000000..bb2525f --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/defer_in_for.vv @@ -0,0 +1,7 @@ +fn main() { + for true { + defer { + break + } + } +} \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/defer_optional.out b/v_windows/v/old/vlib/v/checker/tests/defer_optional.out new file mode 100644 index 0000000..184e35e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/defer_optional.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/defer_optional.vv:5:3: error: opt() returns an option, so it should have an `or {}` block at the end + 3 | fn thing() ?string { + 4 | defer { + 5 | opt() + | ~~~~~ + 6 | } + 7 | return 'ok' \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/defer_optional.vv b/v_windows/v/old/vlib/v/checker/tests/defer_optional.vv new file mode 100644 index 0000000..f153430 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/defer_optional.vv @@ -0,0 +1,8 @@ +fn opt() ? {} + +fn thing() ?string { + defer { + opt() + } + return 'ok' +} \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/deprecations.out b/v_windows/v/old/vlib/v/checker/tests/deprecations.out new file mode 100644 index 0000000..55b205f --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/deprecations.out @@ -0,0 +1,48 @@ +vlib/v/checker/tests/deprecations.vv:54:2: notice: function `future` will be deprecated after 9999-12-30; custom message 4 + 52 | + 53 | fn main() { + 54 | future() + | ~~~~~~~~ + 55 | past() + 56 | simply_deprecated() +vlib/v/checker/tests/deprecations.vv:60:4: notice: method `Abc.future` will be deprecated after 9999-11-01; custom message 1 + 58 | // + 59 | a := Abc{} + 60 | a.future() + | ~~~~~~~~ + 61 | a.past() + 62 | a.simply_deprecated() +vlib/v/checker/tests/deprecations.vv:55:2: error: function `past` has been deprecated since 2021-03-01; custom message 5 + 53 | fn main() { + 54 | future() + 55 | past() + | ~~~~~~ + 56 | simply_deprecated() + 57 | just_deprecated() +vlib/v/checker/tests/deprecations.vv:56:2: error: function `simply_deprecated` has been deprecated; custom message 6 + 54 | future() + 55 | past() + 56 | simply_deprecated() + | ~~~~~~~~~~~~~~~~~~~ + 57 | just_deprecated() + 58 | // +vlib/v/checker/tests/deprecations.vv:57:2: error: function `just_deprecated` has been deprecated + 55 | past() + 56 | simply_deprecated() + 57 | just_deprecated() + | ~~~~~~~~~~~~~~~~~ + 58 | // + 59 | a := Abc{} +vlib/v/checker/tests/deprecations.vv:61:4: error: method `Abc.past` has been deprecated since 2021-03-01; custom message 2 + 59 | a := Abc{} + 60 | a.future() + 61 | a.past() + | ~~~~~~ + 62 | a.simply_deprecated() + 63 | } +vlib/v/checker/tests/deprecations.vv:62:4: error: method `Abc.simply_deprecated` has been deprecated; custom message 3 + 60 | a.future() + 61 | a.past() + 62 | a.simply_deprecated() + | ~~~~~~~~~~~~~~~~~~~ + 63 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/deprecations.vv b/v_windows/v/old/vlib/v/checker/tests/deprecations.vv new file mode 100644 index 0000000..4a731ce --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/deprecations.vv @@ -0,0 +1,63 @@ +// methods using [deprecated_after]: +struct Abc { + x int +} + +fn (a Abc) str() string { + return 'Abc { x: $a.x }' +} + +[deprecated: 'custom message 1'] +[deprecated_after: '9999-11-01'] +fn (a Abc) future() { + dump(@METHOD) + dump(a) +} + +[deprecated: 'custom message 2'] +[deprecated_after: '2021-03-01'] +fn (a Abc) past() { + dump(@METHOD) + dump(a) +} + +[deprecated: 'custom message 3'] +fn (a Abc) simply_deprecated() { + dump(@METHOD) + dump(a) +} + +// functions using [deprecated_after]: +[deprecated: 'custom message 4'] +[deprecated_after: '9999-12-30'] +fn future() { + dump(@FN) +} + +[deprecated: 'custom message 5'] +[deprecated_after: '2021-03-01'] +fn past() { + dump(@FN) +} + +[deprecated: 'custom message 6'] +fn simply_deprecated() { + dump(@FN) +} + +[deprecated] +fn just_deprecated() { + dump(@FN) +} + +fn main() { + future() + past() + simply_deprecated() + just_deprecated() + // + a := Abc{} + a.future() + a.past() + a.simply_deprecated() +} diff --git a/v_windows/v/old/vlib/v/checker/tests/direct_map_alias_init_err.out b/v_windows/v/old/vlib/v/checker/tests/direct_map_alias_init_err.out new file mode 100644 index 0000000..d198c66 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/direct_map_alias_init_err.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/direct_map_alias_init_err.vv:4:7: error: direct map alias init is not possible, use `WordSet(map[string]bool{})` instead + 2 | + 3 | fn main() { + 4 | _ := WordSet{} + | ~~~~~~~~~ + 5 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/direct_map_alias_init_err.vv b/v_windows/v/old/vlib/v/checker/tests/direct_map_alias_init_err.vv new file mode 100644 index 0000000..5921f5b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/direct_map_alias_init_err.vv @@ -0,0 +1,5 @@ +type WordSet = map[string]bool + +fn main() { + _ := WordSet{} +} diff --git a/v_windows/v/old/vlib/v/checker/tests/disallow_pointer_arithmetic_err.out b/v_windows/v/old/vlib/v/checker/tests/disallow_pointer_arithmetic_err.out new file mode 100644 index 0000000..3e4422f --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/disallow_pointer_arithmetic_err.out @@ -0,0 +1,34 @@ +vlib/v/checker/tests/disallow_pointer_arithmetic_err.vv:4:7: error: invalid operator `+` to `&int` and `&int` + 2 | x := 5 + 3 | p := &x + 4 | _ := p + p //should be error + | ~~~~~ + 5 | _ := p * p //should be error + 6 | _ := p * 2 //should be error +vlib/v/checker/tests/disallow_pointer_arithmetic_err.vv:5:7: error: invalid operator `*` to `&int` and `&int` + 3 | p := &x + 4 | _ := p + p //should be error + 5 | _ := p * p //should be error + | ~~~~~ + 6 | _ := p * 2 //should be error + 7 | _ := p + 5 //OK but only in unsafe block, r is *int +vlib/v/checker/tests/disallow_pointer_arithmetic_err.vv:6:7: error: invalid operator `*` to `&int` and `int literal` + 4 | _ := p + p //should be error + 5 | _ := p * p //should be error + 6 | _ := p * 2 //should be error + | ~~~~~ + 7 | _ := p + 5 //OK but only in unsafe block, r is *int + 8 | _ := p - p //OK even in safe code, but n should be isize +vlib/v/checker/tests/disallow_pointer_arithmetic_err.vv:7:7: error: pointer arithmetic is only allowed in `unsafe` blocks + 5 | _ := p * p //should be error + 6 | _ := p * 2 //should be error + 7 | _ := p + 5 //OK but only in unsafe block, r is *int + | ~~~~~ + 8 | _ := p - p //OK even in safe code, but n should be isize + 9 | } +vlib/v/checker/tests/disallow_pointer_arithmetic_err.vv:8:7: error: pointer arithmetic is only allowed in `unsafe` blocks + 6 | _ := p * 2 //should be error + 7 | _ := p + 5 //OK but only in unsafe block, r is *int + 8 | _ := p - p //OK even in safe code, but n should be isize + | ~~~~~ + 9 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/disallow_pointer_arithmetic_err.vv b/v_windows/v/old/vlib/v/checker/tests/disallow_pointer_arithmetic_err.vv new file mode 100644 index 0000000..0fc91cd --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/disallow_pointer_arithmetic_err.vv @@ -0,0 +1,9 @@ +fn main() { + x := 5 + p := &x + _ := p + p //should be error + _ := p * p //should be error + _ := p * 2 //should be error + _ := p + 5 //OK but only in unsafe block, r is *int + _ := p - p //OK even in safe code, but n should be isize +} diff --git a/v_windows/v/old/vlib/v/checker/tests/div_mod_by_cast_zero_int_err.out b/v_windows/v/old/vlib/v/checker/tests/div_mod_by_cast_zero_int_err.out new file mode 100644 index 0000000..b29d963 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/div_mod_by_cast_zero_int_err.out @@ -0,0 +1,40 @@ +vlib/v/checker/tests/div_mod_by_cast_zero_int_err.vv:2:21: error: division by zero + 1 | fn main() { + 2 | println(u64(1)/u64(0)) + | ^ + 3 | println(u64(1)%u64(0)) + 4 | +vlib/v/checker/tests/div_mod_by_cast_zero_int_err.vv:3:21: error: modulo by zero + 1 | fn main() { + 2 | println(u64(1)/u64(0)) + 3 | println(u64(1)%u64(0)) + | ^ + 4 | + 5 | println(u32(1)/u32(0x0)) +vlib/v/checker/tests/div_mod_by_cast_zero_int_err.vv:5:21: error: division by zero + 3 | println(u64(1)%u64(0)) + 4 | + 5 | println(u32(1)/u32(0x0)) + | ~~~ + 6 | println(u32(1)%u32(0x0)) + 7 | +vlib/v/checker/tests/div_mod_by_cast_zero_int_err.vv:6:21: error: modulo by zero + 4 | + 5 | println(u32(1)/u32(0x0)) + 6 | println(u32(1)%u32(0x0)) + | ~~~ + 7 | + 8 | println(u16(1)/u16(0b0)) +vlib/v/checker/tests/div_mod_by_cast_zero_int_err.vv:8:21: error: division by zero + 6 | println(u32(1)%u32(0x0)) + 7 | + 8 | println(u16(1)/u16(0b0)) + | ~~~ + 9 | println(u16(1)%u16(0b0)) + 10 | } +vlib/v/checker/tests/div_mod_by_cast_zero_int_err.vv:9:21: error: modulo by zero + 7 | + 8 | println(u16(1)/u16(0b0)) + 9 | println(u16(1)%u16(0b0)) + | ~~~ + 10 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/div_mod_by_cast_zero_int_err.vv b/v_windows/v/old/vlib/v/checker/tests/div_mod_by_cast_zero_int_err.vv new file mode 100644 index 0000000..215ae9e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/div_mod_by_cast_zero_int_err.vv @@ -0,0 +1,10 @@ +fn main() { + println(u64(1)/u64(0)) + println(u64(1)%u64(0)) + + println(u32(1)/u32(0x0)) + println(u32(1)%u32(0x0)) + + println(u16(1)/u16(0b0)) + println(u16(1)%u16(0b0)) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/div_op_wrong_type_err.out b/v_windows/v/old/vlib/v/checker/tests/div_op_wrong_type_err.out new file mode 100644 index 0000000..f79c286 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/div_op_wrong_type_err.out @@ -0,0 +1,42 @@ +vlib/v/checker/tests/div_op_wrong_type_err.vv:3:13: error: mismatched types `Aaa` and `int literal` + 1 | struct Aaa{} + 2 | fn main() { + 3 | println(Aaa{} / 10) + | ~~~~~~~~~~ + 4 | println(10 / Aaa{}) + 5 | println([1,2,3] / 10) +vlib/v/checker/tests/div_op_wrong_type_err.vv:4:13: error: mismatched types `int literal` and `Aaa` + 2 | fn main() { + 3 | println(Aaa{} / 10) + 4 | println(10 / Aaa{}) + | ~~~~~~~~~~ + 5 | println([1,2,3] / 10) + 6 | println(10 / [1,2,3]) +vlib/v/checker/tests/div_op_wrong_type_err.vv:5:13: error: mismatched types `[]int` and `int literal` + 3 | println(Aaa{} / 10) + 4 | println(10 / Aaa{}) + 5 | println([1,2,3] / 10) + | ~~~~~~~~~~~~ + 6 | println(10 / [1,2,3]) + 7 | a := map[string]int +vlib/v/checker/tests/div_op_wrong_type_err.vv:6:13: error: mismatched types `int literal` and `[]int` + 4 | println(10 / Aaa{}) + 5 | println([1,2,3] / 10) + 6 | println(10 / [1,2,3]) + | ~~~~~~~~~~~~ + 7 | a := map[string]int + 8 | println(a / 10) +vlib/v/checker/tests/div_op_wrong_type_err.vv:8:13: error: mismatched types `map[string]int` and `int literal` + 6 | println(10 / [1,2,3]) + 7 | a := map[string]int + 8 | println(a / 10) + | ~~~~~~ + 9 | println(10 / a) + 10 | } +vlib/v/checker/tests/div_op_wrong_type_err.vv:9:13: error: mismatched types `int literal` and `map[string]int` + 7 | a := map[string]int + 8 | println(a / 10) + 9 | println(10 / a) + | ~~~~~~ + 10 | } + diff --git a/v_windows/v/old/vlib/v/checker/tests/div_op_wrong_type_err.vv b/v_windows/v/old/vlib/v/checker/tests/div_op_wrong_type_err.vv new file mode 100644 index 0000000..fb2f1cd --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/div_op_wrong_type_err.vv @@ -0,0 +1,10 @@ +struct Aaa{} +fn main() { + println(Aaa{} / 10) + println(10 / Aaa{}) + println([1,2,3] / 10) + println(10 / [1,2,3]) + a := map[string]int + println(a / 10) + println(10 / a) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/division_by_cast_zero_float_err.out b/v_windows/v/old/vlib/v/checker/tests/division_by_cast_zero_float_err.out new file mode 100644 index 0000000..f6bbda2 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/division_by_cast_zero_float_err.out @@ -0,0 +1,12 @@ +vlib/v/checker/tests/division_by_cast_zero_float_err.vv:2:23: error: division by zero + 1 | fn main() { + 2 | println(f32(1.0)/f32(0.0)) + | ~~~ + 3 | println(f64(1.0)/f64(0.0)) + 4 | } +vlib/v/checker/tests/division_by_cast_zero_float_err.vv:3:23: error: division by zero + 1 | fn main() { + 2 | println(f32(1.0)/f32(0.0)) + 3 | println(f64(1.0)/f64(0.0)) + | ~~~ + 4 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/division_by_cast_zero_float_err.vv b/v_windows/v/old/vlib/v/checker/tests/division_by_cast_zero_float_err.vv new file mode 100644 index 0000000..6feee15 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/division_by_cast_zero_float_err.vv @@ -0,0 +1,4 @@ +fn main() { + println(f32(1.0)/f32(0.0)) + println(f64(1.0)/f64(0.0)) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/division_by_zero_float_err.out b/v_windows/v/old/vlib/v/checker/tests/division_by_zero_float_err.out new file mode 100644 index 0000000..7a2ee8d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/division_by_zero_float_err.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/division_by_zero_float_err.vv:2:14: error: division by zero + 1 | fn main() { + 2 | println(1.0/0.0) + | ~~~ + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/division_by_zero_float_err.vv b/v_windows/v/old/vlib/v/checker/tests/division_by_zero_float_err.vv new file mode 100644 index 0000000..b5c1e7c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/division_by_zero_float_err.vv @@ -0,0 +1,3 @@ +fn main() { + println(1.0/0.0) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/division_by_zero_int_err.out b/v_windows/v/old/vlib/v/checker/tests/division_by_zero_int_err.out new file mode 100644 index 0000000..6b2ed19 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/division_by_zero_int_err.out @@ -0,0 +1,26 @@ +vlib/v/checker/tests/division_by_zero_int_err.vv:2:12: error: division by zero + 1 | fn main() { + 2 | println(1/0) + | ^ + 3 | println(1/0x0) + 4 | println(1/0b0) +vlib/v/checker/tests/division_by_zero_int_err.vv:3:12: error: division by zero + 1 | fn main() { + 2 | println(1/0) + 3 | println(1/0x0) + | ~~~ + 4 | println(1/0b0) + 5 | println(1/0o0) +vlib/v/checker/tests/division_by_zero_int_err.vv:4:12: error: division by zero + 2 | println(1/0) + 3 | println(1/0x0) + 4 | println(1/0b0) + | ~~~ + 5 | println(1/0o0) + 6 | } +vlib/v/checker/tests/division_by_zero_int_err.vv:5:12: error: division by zero + 3 | println(1/0x0) + 4 | println(1/0b0) + 5 | println(1/0o0) + | ~~~ + 6 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/division_by_zero_int_err.vv b/v_windows/v/old/vlib/v/checker/tests/division_by_zero_int_err.vv new file mode 100644 index 0000000..4316fda --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/division_by_zero_int_err.vv @@ -0,0 +1,6 @@ +fn main() { + println(1/0) + println(1/0x0) + println(1/0b0) + println(1/0o0) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/dump_of_void_expr.out b/v_windows/v/old/vlib/v/checker/tests/dump_of_void_expr.out new file mode 100644 index 0000000..ac6f1b5 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/dump_of_void_expr.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/dump_of_void_expr.vv:3:6: error: dump expression can not be void + 1 | fn abc() {} + 2 | + 3 | dump(abc()) + | ~~~~~ diff --git a/v_windows/v/old/vlib/v/checker/tests/dump_of_void_expr.vv b/v_windows/v/old/vlib/v/checker/tests/dump_of_void_expr.vv new file mode 100644 index 0000000..57e8597 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/dump_of_void_expr.vv @@ -0,0 +1,3 @@ +fn abc() {} + +dump(abc()) diff --git a/v_windows/v/old/vlib/v/checker/tests/duplicate_field_method_err.out b/v_windows/v/old/vlib/v/checker/tests/duplicate_field_method_err.out new file mode 100644 index 0000000..b4befdd --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/duplicate_field_method_err.out @@ -0,0 +1,13 @@ +vlib/v/checker/tests/duplicate_field_method_err.vv:5:1: error: type `St` has both field and method named `attr` + 3 | } + 4 | + 5 | fn (s St) attr() {} + | ~~~~~~~~~~~~~~~~ + 6 | + 7 | interface Foo { +vlib/v/checker/tests/duplicate_field_method_err.vv:9:2: error: type `Foo` has both field and method named `bar` + 7 | interface Foo { + 8 | bar fn () + 9 | bar() + | ~~~~~ + 10 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/duplicate_field_method_err.vv b/v_windows/v/old/vlib/v/checker/tests/duplicate_field_method_err.vv new file mode 100644 index 0000000..2c34f78 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/duplicate_field_method_err.vv @@ -0,0 +1,10 @@ +struct St{ + attr fn() +} + +fn (s St) attr() {} + +interface Foo { + bar fn () + bar() +} diff --git a/v_windows/v/old/vlib/v/checker/tests/enum_as_int_err.out b/v_windows/v/old/vlib/v/checker/tests/enum_as_int_err.out new file mode 100644 index 0000000..cef283d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/enum_as_int_err.out @@ -0,0 +1,21 @@ +vlib/v/checker/tests/enum_as_int_err.vv:9:10: error: cannot assign to `color`: expected `Color`, not `int literal` + 7 | mut color := Color.red + 8 | mut foo := 1 + 9 | color = 1 + | ^ + 10 | foo = Color.red + 11 | println(color == 0) +vlib/v/checker/tests/enum_as_int_err.vv:10:8: error: cannot assign to `foo`: expected `int`, not `Color` + 8 | mut foo := 1 + 9 | color = 1 + 10 | foo = Color.red + | ~~~~~~~~~ + 11 | println(color == 0) + 12 | _ = foo +vlib/v/checker/tests/enum_as_int_err.vv:11:10: error: infix expr: cannot use `int literal` (right expression) as `Color` + 9 | color = 1 + 10 | foo = Color.red + 11 | println(color == 0) + | ~~~~~~~~~~ + 12 | _ = foo + 13 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/enum_as_int_err.vv b/v_windows/v/old/vlib/v/checker/tests/enum_as_int_err.vv new file mode 100644 index 0000000..47605ae --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/enum_as_int_err.vv @@ -0,0 +1,13 @@ +enum Color { + red + blue +} + +fn main() { + mut color := Color.red + mut foo := 1 + color = 1 + foo = Color.red + println(color == 0) + _ = foo +} diff --git a/v_windows/v/old/vlib/v/checker/tests/enum_cast.out b/v_windows/v/old/vlib/v/checker/tests/enum_cast.out new file mode 100644 index 0000000..053fe1b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/enum_cast.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/enum_cast.vv:6:13: error: 12 does not represents a value of enum Color + 4 | println(Color(0)) + 5 | println(Color(10)) + 6 | println(Color(12)) + | ~~~~~~~~~ + 7 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/enum_cast.vv b/v_windows/v/old/vlib/v/checker/tests/enum_cast.vv new file mode 100644 index 0000000..40e9ec7 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/enum_cast.vv @@ -0,0 +1,7 @@ +enum Color { red green = 10 blue } + +fn main() { + println(Color(0)) + println(Color(10)) + println(Color(12)) +} \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/enum_empty.out b/v_windows/v/old/vlib/v/checker/tests/enum_empty.out new file mode 100644 index 0000000..b2b432d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/enum_empty.out @@ -0,0 +1,3 @@ +vlib/v/checker/tests/enum_empty.vv:1:1: error: enum cannot be empty + 1 | enum Empty {} + | ~~~~~~~~~~ diff --git a/v_windows/v/old/vlib/v/checker/tests/enum_empty.vv b/v_windows/v/old/vlib/v/checker/tests/enum_empty.vv new file mode 100644 index 0000000..7afdbc0 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/enum_empty.vv @@ -0,0 +1 @@ +enum Empty {} diff --git a/v_windows/v/old/vlib/v/checker/tests/enum_err.out b/v_windows/v/old/vlib/v/checker/tests/enum_err.out new file mode 100644 index 0000000..9256d7f --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/enum_err.out @@ -0,0 +1,14 @@ +vlib/v/checker/tests/enum_err.vv:4:13: error: default value for enum has to be an integer + 2 | + 3 | enum Color { + 4 | green = 'green' + | ~~~~~~~ + 5 | yellow = 1+1 + 6 | blue +vlib/v/checker/tests/enum_err.vv:5:14: error: default value for enum has to be an integer + 3 | enum Color { + 4 | green = 'green' + 5 | yellow = 1+1 + | ~~~ + 6 | blue + 7 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/enum_err.vv b/v_windows/v/old/vlib/v/checker/tests/enum_err.vv new file mode 100644 index 0000000..1710b84 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/enum_err.vv @@ -0,0 +1,11 @@ +module main + +enum Color { + green = 'green' + yellow = 1+1 + blue +} + +fn main(){ + println('hello') +} diff --git a/v_windows/v/old/vlib/v/checker/tests/enum_field_name_duplicate_err.out b/v_windows/v/old/vlib/v/checker/tests/enum_field_name_duplicate_err.out new file mode 100644 index 0000000..6f1de8d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/enum_field_name_duplicate_err.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/enum_field_name_duplicate_err.vv:5:2: error: field name `green` duplicate + 3 | yellow + 4 | blue + 5 | green + | ~~~~~ + 6 | } + 7 | diff --git a/v_windows/v/old/vlib/v/checker/tests/enum_field_name_duplicate_err.vv b/v_windows/v/old/vlib/v/checker/tests/enum_field_name_duplicate_err.vv new file mode 100644 index 0000000..790d39d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/enum_field_name_duplicate_err.vv @@ -0,0 +1,10 @@ +enum Color { + green + yellow + blue + green +} + +fn main(){ + println('hello') +} diff --git a/v_windows/v/old/vlib/v/checker/tests/enum_field_overflow.out b/v_windows/v/old/vlib/v/checker/tests/enum_field_overflow.out new file mode 100644 index 0000000..26f5843 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/enum_field_overflow.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/enum_field_overflow.vv:4:2: error: enum value overflows + 2 | red + 3 | green = 2147483647 + 4 | blue + | ~~~~ + 5 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/enum_field_overflow.vv b/v_windows/v/old/vlib/v/checker/tests/enum_field_overflow.vv new file mode 100644 index 0000000..8c79d29 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/enum_field_overflow.vv @@ -0,0 +1,5 @@ +enum Color { + red + green = 2147483647 + blue +} diff --git a/v_windows/v/old/vlib/v/checker/tests/enum_field_value_duplicate_a.out b/v_windows/v/old/vlib/v/checker/tests/enum_field_value_duplicate_a.out new file mode 100644 index 0000000..ce72e69 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/enum_field_value_duplicate_a.out @@ -0,0 +1,13 @@ +vlib/v/checker/tests/enum_field_value_duplicate_a.vv:3:10: error: enum value `0` already exists + 1 | enum Color { + 2 | red + 3 | green = 0 + | ^ + 4 | blue = 1 + 5 | alpha = 1 +vlib/v/checker/tests/enum_field_value_duplicate_a.vv:5:10: error: enum value `1` already exists + 3 | green = 0 + 4 | blue = 1 + 5 | alpha = 1 + | ^ + 6 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/enum_field_value_duplicate_a.vv b/v_windows/v/old/vlib/v/checker/tests/enum_field_value_duplicate_a.vv new file mode 100644 index 0000000..49cf3c2 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/enum_field_value_duplicate_a.vv @@ -0,0 +1,6 @@ +enum Color { + red + green = 0 + blue = 1 + alpha = 1 +} diff --git a/v_windows/v/old/vlib/v/checker/tests/enum_field_value_duplicate_b.out b/v_windows/v/old/vlib/v/checker/tests/enum_field_value_duplicate_b.out new file mode 100644 index 0000000..c1a9ae2 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/enum_field_value_duplicate_b.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/enum_field_value_duplicate_b.vv:4:2: error: enum value `0` already exists + 2 | red // 0 + 3 | green = -1 + 4 | blue // -1 + 1 = 0 + | ~~~~ + 5 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/enum_field_value_duplicate_b.vv b/v_windows/v/old/vlib/v/checker/tests/enum_field_value_duplicate_b.vv new file mode 100644 index 0000000..232a265 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/enum_field_value_duplicate_b.vv @@ -0,0 +1,5 @@ +enum Color { + red // 0 + green = -1 + blue // -1 + 1 = 0 +} diff --git a/v_windows/v/old/vlib/v/checker/tests/enum_field_value_overflow.out b/v_windows/v/old/vlib/v/checker/tests/enum_field_value_overflow.out new file mode 100644 index 0000000..c6bb9c0 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/enum_field_value_overflow.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/enum_field_value_overflow.vv:3:10: error: enum value `2147483648` overflows int + 1 | enum Color { + 2 | red + 3 | green = 2147483648 + | ~~~~~~~~~~ + 4 | blue + 5 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/enum_field_value_overflow.vv b/v_windows/v/old/vlib/v/checker/tests/enum_field_value_overflow.vv new file mode 100644 index 0000000..cb9fb9a --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/enum_field_value_overflow.vv @@ -0,0 +1,5 @@ +enum Color { + red + green = 2147483648 + blue +} diff --git a/v_windows/v/old/vlib/v/checker/tests/enum_op_err.out b/v_windows/v/old/vlib/v/checker/tests/enum_op_err.out new file mode 100644 index 0000000..1392251 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/enum_op_err.out @@ -0,0 +1,34 @@ +vlib/v/checker/tests/enum_op_err.vv:8:20: error: only `==` and `!=` are defined on `enum`, use an explicit cast to `int` if needed + 6 | + 7 | fn main() { + 8 | println(Color.red > Color.green) + | ^ + 9 | println(Color.red + Color.green) + 10 | println(Color.red && Color.green) +vlib/v/checker/tests/enum_op_err.vv:9:20: error: only `==` and `!=` are defined on `enum`, use an explicit cast to `int` if needed + 7 | fn main() { + 8 | println(Color.red > Color.green) + 9 | println(Color.red + Color.green) + | ^ + 10 | println(Color.red && Color.green) + 11 | println(Color.red | Color.green) +vlib/v/checker/tests/enum_op_err.vv:10:10: error: left operand for `&&` is not a boolean + 8 | println(Color.red > Color.green) + 9 | println(Color.red + Color.green) + 10 | println(Color.red && Color.green) + | ~~~~~~~~~ + 11 | println(Color.red | Color.green) + 12 | println(Color.red & Color.green) +vlib/v/checker/tests/enum_op_err.vv:11:20: error: only `==` and `!=` are defined on `enum`, use an explicit cast to `int` if needed + 9 | println(Color.red + Color.green) + 10 | println(Color.red && Color.green) + 11 | println(Color.red | Color.green) + | ^ + 12 | println(Color.red & Color.green) + 13 | } +vlib/v/checker/tests/enum_op_err.vv:12:20: error: only `==` and `!=` are defined on `enum`, use an explicit cast to `int` if needed + 10 | println(Color.red && Color.green) + 11 | println(Color.red | Color.green) + 12 | println(Color.red & Color.green) + | ^ + 13 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/enum_op_err.vv b/v_windows/v/old/vlib/v/checker/tests/enum_op_err.vv new file mode 100644 index 0000000..7fa48e4 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/enum_op_err.vv @@ -0,0 +1,13 @@ +enum Color { + red + blue + green +} + +fn main() { + println(Color.red > Color.green) + println(Color.red + Color.green) + println(Color.red && Color.green) + println(Color.red | Color.green) + println(Color.red & Color.green) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/enum_op_flag_err.out b/v_windows/v/old/vlib/v/checker/tests/enum_op_flag_err.out new file mode 100644 index 0000000..fb3e228 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/enum_op_flag_err.out @@ -0,0 +1,20 @@ +vlib/v/checker/tests/enum_op_flag_err.vv:9:24: error: only `==`, `!=`, `|` and `&` are defined on `[flag]` tagged `enum`, use an explicit cast to `int` if needed + 7 | + 8 | fn main() { + 9 | println(FilePerm.read > FilePerm.write) + | ^ + 10 | println(FilePerm.write + FilePerm.exec) + 11 | println(FilePerm.write && FilePerm.exec) +vlib/v/checker/tests/enum_op_flag_err.vv:10:25: error: only `==`, `!=`, `|` and `&` are defined on `[flag]` tagged `enum`, use an explicit cast to `int` if needed + 8 | fn main() { + 9 | println(FilePerm.read > FilePerm.write) + 10 | println(FilePerm.write + FilePerm.exec) + | ^ + 11 | println(FilePerm.write && FilePerm.exec) + 12 | } +vlib/v/checker/tests/enum_op_flag_err.vv:11:10: error: left operand for `&&` is not a boolean + 9 | println(FilePerm.read > FilePerm.write) + 10 | println(FilePerm.write + FilePerm.exec) + 11 | println(FilePerm.write && FilePerm.exec) + | ~~~~~~~~~~~~~~ + 12 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/enum_op_flag_err.vv b/v_windows/v/old/vlib/v/checker/tests/enum_op_flag_err.vv new file mode 100644 index 0000000..0701255 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/enum_op_flag_err.vv @@ -0,0 +1,12 @@ +[flag] +enum FilePerm { + read + write + exec +} + +fn main() { + println(FilePerm.read > FilePerm.write) + println(FilePerm.write + FilePerm.exec) + println(FilePerm.write && FilePerm.exec) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/enum_single_letter.out b/v_windows/v/old/vlib/v/checker/tests/enum_single_letter.out new file mode 100644 index 0000000..b509c5e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/enum_single_letter.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/enum_single_letter.vv:1:6: error: single letter capital names are reserved for generic template types. + 1 | enum E { + | ^ + 2 | v w + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/enum_single_letter.vv b/v_windows/v/old/vlib/v/checker/tests/enum_single_letter.vv new file mode 100644 index 0000000..ef7bc65 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/enum_single_letter.vv @@ -0,0 +1,4 @@ +enum E { + v w +} +println(E.v) diff --git a/v_windows/v/old/vlib/v/checker/tests/eq_ne_op_wrong_type_err.out b/v_windows/v/old/vlib/v/checker/tests/eq_ne_op_wrong_type_err.out new file mode 100644 index 0000000..d5f43cf --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/eq_ne_op_wrong_type_err.out @@ -0,0 +1,139 @@ +vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:12:10: error: infix expr: cannot use `int literal` (right expression) as `Aaa` + 10 | + 11 | fn main() { + 12 | println(Aaa{} == 10) + | ~~~~~~~~~~~ + 13 | println(10 == Aaa{}) + 14 | println(Aaa{} != 10) +vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:13:10: error: infix expr: cannot use `Aaa` (right expression) as `int literal` + 11 | fn main() { + 12 | println(Aaa{} == 10) + 13 | println(10 == Aaa{}) + | ~~~~~~~~~~~ + 14 | println(Aaa{} != 10) + 15 | println(10 != Aaa{}) +vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:14:10: error: infix expr: cannot use `int literal` (right expression) as `Aaa` + 12 | println(Aaa{} == 10) + 13 | println(10 == Aaa{}) + 14 | println(Aaa{} != 10) + | ~~~~~~~~~~~ + 15 | println(10 != Aaa{}) + 16 | +vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:15:10: error: infix expr: cannot use `Aaa` (right expression) as `int literal` + 13 | println(10 == Aaa{}) + 14 | println(Aaa{} != 10) + 15 | println(10 != Aaa{}) + | ~~~~~~~~~~~ + 16 | + 17 | println(Aaa{0} == AAaa{0}) +vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:17:10: error: possible type mismatch of compared values of `==` operation + 15 | println(10 != Aaa{}) + 16 | + 17 | println(Aaa{0} == AAaa{0}) + | ~~~~~~~~~~~~~~~~~ + 18 | println(AAaa{0} == Aaa{0}) + 19 | println(AAaa{1} != Aaa{1}) +vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:18:10: error: possible type mismatch of compared values of `==` operation + 16 | + 17 | println(Aaa{0} == AAaa{0}) + 18 | println(AAaa{0} == Aaa{0}) + | ~~~~~~~~~~~~~~~~~ + 19 | println(AAaa{1} != Aaa{1}) + 20 | println(Aaa{1} != AAaa{1}) +vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:19:10: error: possible type mismatch of compared values of `!=` operation + 17 | println(Aaa{0} == AAaa{0}) + 18 | println(AAaa{0} == Aaa{0}) + 19 | println(AAaa{1} != Aaa{1}) + | ~~~~~~~~~~~~~~~~~ + 20 | println(Aaa{1} != AAaa{1}) + 21 | +vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:20:10: error: possible type mismatch of compared values of `!=` operation + 18 | println(AAaa{0} == Aaa{0}) + 19 | println(AAaa{1} != Aaa{1}) + 20 | println(Aaa{1} != AAaa{1}) + | ~~~~~~~~~~~~~~~~~ + 21 | + 22 | arr := Arr([0]) +vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:23:10: error: possible type mismatch of compared values of `==` operation + 21 | + 22 | arr := Arr([0]) + 23 | println(arr == [0]) + | ~~~~~~~~~~ + 24 | println([1] == arr) + 25 | println(arr != [0]) +vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:24:10: error: possible type mismatch of compared values of `==` operation + 22 | arr := Arr([0]) + 23 | println(arr == [0]) + 24 | println([1] == arr) + | ~~~~~~~~~~ + 25 | println(arr != [0]) + 26 | println([1] != arr) +vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:25:10: error: possible type mismatch of compared values of `!=` operation + 23 | println(arr == [0]) + 24 | println([1] == arr) + 25 | println(arr != [0]) + | ~~~~~~~~~~ + 26 | println([1] != arr) + 27 | +vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:26:10: error: possible type mismatch of compared values of `!=` operation + 24 | println([1] == arr) + 25 | println(arr != [0]) + 26 | println([1] != arr) + | ~~~~~~~~~~ + 27 | + 28 | arr_aaa := ArrAaa(arr) +vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:29:10: error: possible type mismatch of compared values of `==` operation + 27 | + 28 | arr_aaa := ArrAaa(arr) + 29 | println(arr_aaa == arr) + | ~~~~~~~~~~~~~~ + 30 | println(arr == arr_aaa) + 31 | println(arr_aaa != arr) +vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:30:10: error: possible type mismatch of compared values of `==` operation + 28 | arr_aaa := ArrAaa(arr) + 29 | println(arr_aaa == arr) + 30 | println(arr == arr_aaa) + | ~~~~~~~~~~~~~~ + 31 | println(arr_aaa != arr) + 32 | println(arr != arr_aaa) +vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:31:10: error: possible type mismatch of compared values of `!=` operation + 29 | println(arr_aaa == arr) + 30 | println(arr == arr_aaa) + 31 | println(arr_aaa != arr) + | ~~~~~~~~~~~~~~ + 32 | println(arr != arr_aaa) + 33 | +vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:32:10: error: possible type mismatch of compared values of `!=` operation + 30 | println(arr == arr_aaa) + 31 | println(arr_aaa != arr) + 32 | println(arr != arr_aaa) + | ~~~~~~~~~~~~~~ + 33 | + 34 | println(arr_aaa == [0]) +vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:34:10: error: infix expr: cannot use `[]int` (right expression) as `ArrAaa` + 32 | println(arr != arr_aaa) + 33 | + 34 | println(arr_aaa == [0]) + | ~~~~~~~~~~~~~~ + 35 | println([1] == arr_aaa) + 36 | println(arr_aaa != [0]) +vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:35:10: error: infix expr: cannot use `ArrAaa` (right expression) as `[]int` + 33 | + 34 | println(arr_aaa == [0]) + 35 | println([1] == arr_aaa) + | ~~~~~~~~~~~~~~ + 36 | println(arr_aaa != [0]) + 37 | println([1] != arr_aaa) +vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:36:10: error: infix expr: cannot use `[]int` (right expression) as `ArrAaa` + 34 | println(arr_aaa == [0]) + 35 | println([1] == arr_aaa) + 36 | println(arr_aaa != [0]) + | ~~~~~~~~~~~~~~ + 37 | println([1] != arr_aaa) + 38 | } +vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv:37:10: error: infix expr: cannot use `ArrAaa` (right expression) as `[]int` + 35 | println([1] == arr_aaa) + 36 | println(arr_aaa != [0]) + 37 | println([1] != arr_aaa) + | ~~~~~~~~~~~~~~ + 38 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv b/v_windows/v/old/vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv new file mode 100644 index 0000000..36e7d17 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/eq_ne_op_wrong_type_err.vv @@ -0,0 +1,38 @@ +struct Aaa { + i int +} + +type AAaa = Aaa + +type Arr = []int + +type ArrAaa = Aaa | Arr + +fn main() { + println(Aaa{} == 10) + println(10 == Aaa{}) + println(Aaa{} != 10) + println(10 != Aaa{}) + + println(Aaa{0} == AAaa{0}) + println(AAaa{0} == Aaa{0}) + println(AAaa{1} != Aaa{1}) + println(Aaa{1} != AAaa{1}) + + arr := Arr([0]) + println(arr == [0]) + println([1] == arr) + println(arr != [0]) + println([1] != arr) + + arr_aaa := ArrAaa(arr) + println(arr_aaa == arr) + println(arr == arr_aaa) + println(arr_aaa != arr) + println(arr != arr_aaa) + + println(arr_aaa == [0]) + println([1] == arr_aaa) + println(arr_aaa != [0]) + println([1] != arr_aaa) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/error_fn_with_0_args.out b/v_windows/v/old/vlib/v/checker/tests/error_fn_with_0_args.out new file mode 100644 index 0000000..c4aceee --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/error_fn_with_0_args.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/error_fn_with_0_args.vv:2:9: error: expected 1 arguments, but got 0 + 1 | fn abc() ? { + 2 | return error() + | ~~~~~~~ + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/error_fn_with_0_args.vv b/v_windows/v/old/vlib/v/checker/tests/error_fn_with_0_args.vv new file mode 100644 index 0000000..ebbfe47 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/error_fn_with_0_args.vv @@ -0,0 +1,3 @@ +fn abc() ? { + return error() +} diff --git a/v_windows/v/old/vlib/v/checker/tests/error_with_comment_with_crlf_ending.out b/v_windows/v/old/vlib/v/checker/tests/error_with_comment_with_crlf_ending.out new file mode 100644 index 0000000..5e4630e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/error_with_comment_with_crlf_ending.out @@ -0,0 +1,4 @@ +vlib/v/checker/tests/error_with_comment_with_crlf_ending.vv:2:6: error: unexpected name `should` + 1 | // Empty lines don't cause an issue but as soon as there's a comment with a CRLF ending, it fails + 2 | This should cause an error! + | ~~~~~~ diff --git a/v_windows/v/old/vlib/v/checker/tests/error_with_comment_with_crlf_ending.vv b/v_windows/v/old/vlib/v/checker/tests/error_with_comment_with_crlf_ending.vv new file mode 100644 index 0000000..256462a --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/error_with_comment_with_crlf_ending.vv @@ -0,0 +1,2 @@ +// Empty lines don't cause an issue but as soon as there's a comment with a CRLF ending, it fails +This should cause an error! \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/error_with_comment_with_lf_ending.out b/v_windows/v/old/vlib/v/checker/tests/error_with_comment_with_lf_ending.out new file mode 100644 index 0000000..fcf3524 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/error_with_comment_with_lf_ending.out @@ -0,0 +1,4 @@ +vlib/v/checker/tests/error_with_comment_with_lf_ending.vv:2:6: error: unexpected name `should` + 1 | // Empty lines don't cause an issue but as soon as there's a comment with a CRLF ending, it fails + 2 | This should cause an error! + | ~~~~~~ diff --git a/v_windows/v/old/vlib/v/checker/tests/error_with_comment_with_lf_ending.vv b/v_windows/v/old/vlib/v/checker/tests/error_with_comment_with_lf_ending.vv new file mode 100644 index 0000000..003d44e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/error_with_comment_with_lf_ending.vv @@ -0,0 +1,2 @@ +// Empty lines don't cause an issue but as soon as there's a comment with a CRLF ending, it fails +This should cause an error! \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/error_with_several_comments_with_crlf_ending.out b/v_windows/v/old/vlib/v/checker/tests/error_with_several_comments_with_crlf_ending.out new file mode 100644 index 0000000..d1b88fa --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/error_with_several_comments_with_crlf_ending.out @@ -0,0 +1,13 @@ +vlib/v/checker/tests/error_with_several_comments_with_crlf_ending.vv:9:2: error: undefined ident: `a` (use `:=` to declare a variable) + 7 | fn main() { + 8 | func1() + 9 | a = 2 + | ^ + 10 | println('a is $a') + 11 | } +vlib/v/checker/tests/error_with_several_comments_with_crlf_ending.vv:10:17: error: undefined ident: `a` + 8 | func1() + 9 | a = 2 + 10 | println('a is $a') + | ^ + 11 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/error_with_several_comments_with_crlf_ending.vv b/v_windows/v/old/vlib/v/checker/tests/error_with_several_comments_with_crlf_ending.vv new file mode 100644 index 0000000..09b22be --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/error_with_several_comments_with_crlf_ending.vv @@ -0,0 +1,11 @@ +// Comment line 1 +// Comment line 2 +fn func1() { + println('Inside func 1') +} + +fn main() { + func1() + a = 2 + println('a is $a') +} diff --git a/v_windows/v/old/vlib/v/checker/tests/error_with_unicode.out b/v_windows/v/old/vlib/v/checker/tests/error_with_unicode.out new file mode 100644 index 0000000..78ea832 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/error_with_unicode.out @@ -0,0 +1,56 @@ +vlib/v/checker/tests/error_with_unicode.vv:5:17: error: cannot use `int literal` as `string` in argument 2 to `f1` + 3 | + 4 | fn main() { + 5 | f1('🐀🐈', 0) + | ^ + 6 | f2(0, '🐟🐧') + 7 | mut n := 0 +vlib/v/checker/tests/error_with_unicode.vv:6:8: error: cannot use `string` as `int` in argument 2 to `f2` + 4 | fn main() { + 5 | f1('🐀🐈', 0) + 6 | f2(0, '🐟🐧') + | ~~~~~~ + 7 | mut n := 0 + 8 | n = '漢字' +vlib/v/checker/tests/error_with_unicode.vv:8:6: error: cannot assign to `n`: expected `int`, not `string` + 6 | f2(0, '🐟🐧') + 7 | mut n := 0 + 8 | n = '漢字' + | ~~~~~~ + 9 | n = 'ひらがな' + 10 | n = '简体字' +vlib/v/checker/tests/error_with_unicode.vv:9:6: error: cannot assign to `n`: expected `int`, not `string` + 7 | mut n := 0 + 8 | n = '漢字' + 9 | n = 'ひらがな' + | ~~~~~~~~~~ + 10 | n = '简体字' + 11 | n = '繁體字' +vlib/v/checker/tests/error_with_unicode.vv:10:6: error: cannot assign to `n`: expected `int`, not `string` + 8 | n = '漢字' + 9 | n = 'ひらがな' + 10 | n = '简体字' + | ~~~~~~~~ + 11 | n = '繁體字' + 12 | n = '한글' +vlib/v/checker/tests/error_with_unicode.vv:11:6: error: cannot assign to `n`: expected `int`, not `string` + 9 | n = 'ひらがな' + 10 | n = '简体字' + 11 | n = '繁體字' + | ~~~~~~~~ + 12 | n = '한글' + 13 | n = 'Кириллица' +vlib/v/checker/tests/error_with_unicode.vv:12:6: error: cannot assign to `n`: expected `int`, not `string` + 10 | n = '简体字' + 11 | n = '繁體字' + 12 | n = '한글' + | ~~~~~~ + 13 | n = 'Кириллица' + 14 | _ = n +vlib/v/checker/tests/error_with_unicode.vv:13:6: error: cannot assign to `n`: expected `int`, not `string` + 11 | n = '繁體字' + 12 | n = '한글' + 13 | n = 'Кириллица' + | ~~~~~~~~~~~ + 14 | _ = n + 15 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/error_with_unicode.vv b/v_windows/v/old/vlib/v/checker/tests/error_with_unicode.vv new file mode 100644 index 0000000..8299e4e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/error_with_unicode.vv @@ -0,0 +1,15 @@ +fn f1(_ string, _ string) {} +fn f2(_ int, _ int) {} + +fn main() { + f1('🐀🐈', 0) + f2(0, '🐟🐧') + mut n := 0 + n = '漢字' + n = 'ひらがな' + n = '简体字' + n = '繁體字' + n = '한글' + n = 'Кириллица' + _ = n +} diff --git a/v_windows/v/old/vlib/v/checker/tests/expression_should_return_an_option.out b/v_windows/v/old/vlib/v/checker/tests/expression_should_return_an_option.out new file mode 100644 index 0000000..e150341 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/expression_should_return_an_option.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/expression_should_return_an_option.vv:28:10: error: expression should return an option + 26 | } + 27 | // should be an checker error: + 28 | if x := return_string() { + | ~~~~~~~~~~~~~~~ + 29 | println('x: $x') + 30 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/expression_should_return_an_option.vv b/v_windows/v/old/vlib/v/checker/tests/expression_should_return_an_option.vv new file mode 100644 index 0000000..6b25dbc --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/expression_should_return_an_option.vv @@ -0,0 +1,31 @@ +fn return_optional(fail bool) ?string { + if fail { + return error('nope') + } + return 'foo' +} + +fn return_string() string { + return 'foo' +} + +fn main() { + // works + if r := return_optional(false) { + println(r) + } + // works + if r := return_optional(false) { + println(r) + } else { + println(err) + } + // works + return_optional(true) or { + println(err) + } + // should be an checker error: + if x := return_string() { + println('x: $x') + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/filter_func_return_nonbool_err.out b/v_windows/v/old/vlib/v/checker/tests/filter_func_return_nonbool_err.out new file mode 100644 index 0000000..e62f5db --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/filter_func_return_nonbool_err.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/filter_func_return_nonbool_err.vv:2:25: error: type mismatch, `stringsss` must return a bool + 1 | fn main() { + 2 | list := [1,2,3].filter(stringsss(it)) + | ~~~~~~~~~~~~~ + 3 | } + 4 | diff --git a/v_windows/v/old/vlib/v/checker/tests/filter_func_return_nonbool_err.vv b/v_windows/v/old/vlib/v/checker/tests/filter_func_return_nonbool_err.vv new file mode 100644 index 0000000..a6ecd7a --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/filter_func_return_nonbool_err.vv @@ -0,0 +1,7 @@ +fn main() { + list := [1,2,3].filter(stringsss(it)) +} + +fn stringsss(arg int) string { + return '' +} \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/filter_on_non_arr_err.out b/v_windows/v/old/vlib/v/checker/tests/filter_on_non_arr_err.out new file mode 100644 index 0000000..d01f0fd --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/filter_on_non_arr_err.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/filter_on_non_arr_err.vv:2:14: error: unknown method or field: `string.filter` + 1 | fn main() { + 2 | _ := 'test'.filter(it == `t`) + | ~~~~~~~~~~~~~~~~~ + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/filter_on_non_arr_err.vv b/v_windows/v/old/vlib/v/checker/tests/filter_on_non_arr_err.vv new file mode 100644 index 0000000..ee1bb7c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/filter_on_non_arr_err.vv @@ -0,0 +1,3 @@ +fn main() { + _ := 'test'.filter(it == `t`) +} \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/fixed_array_conv.out b/v_windows/v/old/vlib/v/checker/tests/fixed_array_conv.out new file mode 100644 index 0000000..211f4b5 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/fixed_array_conv.out @@ -0,0 +1,41 @@ +vlib/v/checker/tests/fixed_array_conv.vv:3:3: error: mismatched types `voidptr` and `[2]int` + 1 | arr := [2,3]! + 2 | mut p := voidptr(0) + 3 | p = arr + | ^ + 4 | mut ip := &int(0) + 5 | ip = arr +vlib/v/checker/tests/fixed_array_conv.vv:5:4: error: mismatched types `&int` and `[2]int` + 3 | p = arr + 4 | mut ip := &int(0) + 5 | ip = arr + | ^ + 6 | _ = &int(arr) + 7 | _ = p +vlib/v/checker/tests/fixed_array_conv.vv:6:5: error: cannot cast a fixed array (use e.g. `&arr[0]` instead) + 4 | mut ip := &int(0) + 5 | ip = arr + 6 | _ = &int(arr) + | ~~~~~~~~~ + 7 | _ = p + 8 | _ = ip +vlib/v/checker/tests/fixed_array_conv.vv:11:13: error: cannot use `[2]int` as `voidptr` in argument 1 to `memdup` + 9 | + 10 | unsafe { + 11 | _ = memdup(arr, 1) + | ~~~ + 12 | _ = tos(arr, 1) + 13 | fn (p &int){}(arr) +vlib/v/checker/tests/fixed_array_conv.vv:12:10: error: cannot use `[2]int` as `&byte` in argument 1 to `tos` + 10 | unsafe { + 11 | _ = memdup(arr, 1) + 12 | _ = tos(arr, 1) + | ~~~ + 13 | fn (p &int){}(arr) + 14 | } +vlib/v/checker/tests/fixed_array_conv.vv:13:16: error: cannot use `[2]int` as `&int` in argument 1 to `anon` + 11 | _ = memdup(arr, 1) + 12 | _ = tos(arr, 1) + 13 | fn (p &int){}(arr) + | ~~~ + 14 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/fixed_array_conv.vv b/v_windows/v/old/vlib/v/checker/tests/fixed_array_conv.vv new file mode 100644 index 0000000..8c343c4 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/fixed_array_conv.vv @@ -0,0 +1,14 @@ +arr := [2,3]! +mut p := voidptr(0) +p = arr +mut ip := &int(0) +ip = arr +_ = &int(arr) +_ = p +_ = ip + +unsafe { + _ = memdup(arr, 1) + _ = tos(arr, 1) + fn (p &int){}(arr) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/fixed_array_non_const_size_err.out b/v_windows/v/old/vlib/v/checker/tests/fixed_array_non_const_size_err.out new file mode 100644 index 0000000..0734920 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/fixed_array_non_const_size_err.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/fixed_array_non_const_size_err.vv:4:12: error: non-constant array bound `size` + 2 | size := 2 + 3 | + 4 | array := [size]int{} + | ~~~~ + 5 | + 6 | println(array) diff --git a/v_windows/v/old/vlib/v/checker/tests/fixed_array_non_const_size_err.vv b/v_windows/v/old/vlib/v/checker/tests/fixed_array_non_const_size_err.vv new file mode 100644 index 0000000..eada0cd --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/fixed_array_non_const_size_err.vv @@ -0,0 +1,7 @@ +fn main() { + size := 2 + + array := [size]int{} + + println(array) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/fixed_array_size_err.out b/v_windows/v/old/vlib/v/checker/tests/fixed_array_size_err.out new file mode 100644 index 0000000..8e3c9ea --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/fixed_array_size_err.out @@ -0,0 +1,14 @@ +vlib/v/checker/tests/fixed_array_size_err.vv:4:8: error: fixed size cannot be zero or negative (fixed_size: -1) + 2 | + 3 | fn main() { + 4 | a := [size]int{} + | ~~~~ + 5 | b := [0]byte{} + 6 | println(a) +vlib/v/checker/tests/fixed_array_size_err.vv:5:8: error: fixed size cannot be zero or negative (fixed_size: 0) + 3 | fn main() { + 4 | a := [size]int{} + 5 | b := [0]byte{} + | ^ + 6 | println(a) + 7 | println(b) diff --git a/v_windows/v/old/vlib/v/checker/tests/fixed_array_size_err.vv b/v_windows/v/old/vlib/v/checker/tests/fixed_array_size_err.vv new file mode 100644 index 0000000..eb27c2b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/fixed_array_size_err.vv @@ -0,0 +1,8 @@ +const size = -1 + +fn main() { + a := [size]int{} + b := [0]byte{} + println(a) + println(b) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/float_lit_exp_not_integer_err.out b/v_windows/v/old/vlib/v/checker/tests/float_lit_exp_not_integer_err.out new file mode 100644 index 0000000..8d7b1d8 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/float_lit_exp_not_integer_err.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/float_lit_exp_not_integer_err.vv:2:17: error: exponential part should be integer + 1 | fn main() { + 2 | println(45e2.5) + | ^ + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/float_lit_exp_not_integer_err.vv b/v_windows/v/old/vlib/v/checker/tests/float_lit_exp_not_integer_err.vv new file mode 100644 index 0000000..a1bd559 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/float_lit_exp_not_integer_err.vv @@ -0,0 +1,3 @@ +fn main() { + println(45e2.5) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/float_lit_exp_without_digit_err.out b/v_windows/v/old/vlib/v/checker/tests/float_lit_exp_without_digit_err.out new file mode 100644 index 0000000..c5be9e2 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/float_lit_exp_without_digit_err.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/float_lit_exp_without_digit_err.vv:2:14: error: exponent has no digits + 1 | fn main() { + 2 | println(2E) + | ^ + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/float_lit_exp_without_digit_err.vv b/v_windows/v/old/vlib/v/checker/tests/float_lit_exp_without_digit_err.vv new file mode 100644 index 0000000..6d3d9f1 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/float_lit_exp_without_digit_err.vv @@ -0,0 +1,3 @@ +fn main() { + println(2E) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/float_lit_too_many_points_err.out b/v_windows/v/old/vlib/v/checker/tests/float_lit_too_many_points_err.out new file mode 100644 index 0000000..cbf085b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/float_lit_too_many_points_err.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/float_lit_too_many_points_err.vv:2:19: error: too many decimal points in number + 1 | fn main() { + 2 | println(123.45.67) + | ^ + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/float_lit_too_many_points_err.vv b/v_windows/v/old/vlib/v/checker/tests/float_lit_too_many_points_err.vv new file mode 100644 index 0000000..e787cab --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/float_lit_too_many_points_err.vv @@ -0,0 +1,3 @@ +fn main() { + println(123.45.67) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/float_modulo_err.out b/v_windows/v/old/vlib/v/checker/tests/float_modulo_err.out new file mode 100644 index 0000000..a3ee9bf --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/float_modulo_err.out @@ -0,0 +1,12 @@ +vlib/v/checker/tests/float_modulo_err.vv:2:13: error: float modulo not allowed, use math.fmod() instead + 1 | fn main() { + 2 | println(3.0 % 2.0) + | ~~~ + 3 | println(f32(3.0) % f32(2.0)) + 4 | } +vlib/v/checker/tests/float_modulo_err.vv:3:13: error: float modulo not allowed, use math.fmod() instead + 1 | fn main() { + 2 | println(3.0 % 2.0) + 3 | println(f32(3.0) % f32(2.0)) + | ~~~~~~~~ + 4 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/float_modulo_err.vv b/v_windows/v/old/vlib/v/checker/tests/float_modulo_err.vv new file mode 100644 index 0000000..6a4d87d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/float_modulo_err.vv @@ -0,0 +1,4 @@ +fn main() { + println(3.0 % 2.0) + println(f32(3.0) % f32(2.0)) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/fn_args.out b/v_windows/v/old/vlib/v/checker/tests/fn_args.out new file mode 100644 index 0000000..2df630b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/fn_args.out @@ -0,0 +1,35 @@ +vlib/v/checker/tests/fn_args.vv:9:6: error: cannot use `&int` as `byte` in argument 1 to `uu8` + 7 | fn basic() { + 8 | v := 4 + 9 | uu8(&v) + | ~~ + 10 | arr([5]!) + 11 | fun(fn (i &int) {}) +vlib/v/checker/tests/fn_args.vv:10:6: error: cannot use `[1]int` as `[]int` in argument 1 to `arr` + 8 | v := 4 + 9 | uu8(&v) + 10 | arr([5]!) + | ~~~~ + 11 | fun(fn (i &int) {}) + 12 | fun(fn (ii ...int) {}) +vlib/v/checker/tests/fn_args.vv:11:6: error: cannot use `fn (&int)` as `fn (int)` in argument 1 to `fun` + 9 | uu8(&v) + 10 | arr([5]!) + 11 | fun(fn (i &int) {}) + | ~~~~~~~~~~~~~~ + 12 | fun(fn (ii ...int) {}) + 13 | } +Details: ``'s expected fn argument: `` is NOT a pointer, but the passed fn argument: `i` is a pointer +vlib/v/checker/tests/fn_args.vv:12:6: error: cannot use `fn (...int)` as `fn (int)` in argument 1 to `fun` + 10 | arr([5]!) + 11 | fun(fn (i &int) {}) + 12 | fun(fn (ii ...int) {}) + | ~~~~~~~~~~~~~~~~~ + 13 | } + 14 | +vlib/v/checker/tests/fn_args.vv:22:4: error: cannot use `int` as `&S1` in argument 1 to `f` + 20 | fn ptr() { + 21 | v := 4 + 22 | f(v) + | ^ + 23 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/fn_args.vv b/v_windows/v/old/vlib/v/checker/tests/fn_args.vv new file mode 100644 index 0000000..5cfdff5 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/fn_args.vv @@ -0,0 +1,23 @@ +fn uu8(a byte) {} + +fn arr(a []int) {} + +fn fun(a fn (int)) {} + +fn basic() { + v := 4 + uu8(&v) + arr([5]!) + fun(fn (i &int) {}) + fun(fn (ii ...int) {}) +} + +struct S1 { +} + +fn f(p &S1) {} + +fn ptr() { + v := 4 + f(v) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/fn_call_no_body.out b/v_windows/v/old/vlib/v/checker/tests/fn_call_no_body.out new file mode 100644 index 0000000..376fc56 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/fn_call_no_body.out @@ -0,0 +1,13 @@ +vlib/v/checker/tests/fn_call_no_body.vv:7:9: error: cannot call a method that does not have a body + 5 | + 6 | fn main() { + 7 | Foo(0).f() + | ~~~ + 8 | f() + 9 | } +vlib/v/checker/tests/fn_call_no_body.vv:8:2: error: cannot call a function that does not have a body + 6 | fn main() { + 7 | Foo(0).f() + 8 | f() + | ~~~ + 9 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/fn_call_no_body.vv b/v_windows/v/old/vlib/v/checker/tests/fn_call_no_body.vv new file mode 100644 index 0000000..1058977 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/fn_call_no_body.vv @@ -0,0 +1,9 @@ +type Foo = int +fn (_ Foo) f() + +fn f() + +fn main() { + Foo(0).f() + f() +} diff --git a/v_windows/v/old/vlib/v/checker/tests/fn_duplicate.out b/v_windows/v/old/vlib/v/checker/tests/fn_duplicate.out new file mode 100644 index 0000000..3cf949c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/fn_duplicate.out @@ -0,0 +1,13 @@ +redefinition of function `main.f` +vlib/v/checker/tests/fn_duplicate.vv:1:1: conflicting declaration: fn f() + 1 | fn f() { + | ~~~~~~ + 2 | + 3 | } +vlib/v/checker/tests/fn_duplicate.vv:5:1: conflicting declaration: fn f(i int) + 3 | } + 4 | + 5 | fn f(i int) { + | ~~~~~~~~~~~ + 6 | + 7 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/fn_duplicate.vv b/v_windows/v/old/vlib/v/checker/tests/fn_duplicate.vv new file mode 100644 index 0000000..08c1dfb --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/fn_duplicate.vv @@ -0,0 +1,7 @@ +fn f() { + +} + +fn f(i int) { + +} diff --git a/v_windows/v/old/vlib/v/checker/tests/fn_init_sig.out b/v_windows/v/old/vlib/v/checker/tests/fn_init_sig.out new file mode 100644 index 0000000..08cb17b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/fn_init_sig.out @@ -0,0 +1,12 @@ +vlib/v/checker/tests/fn_init_sig.vv:1:1: error: fn `init` cannot have a return type + 1 | fn init() int { + | ~~~~~~~~~~~~~ + 2 | return 1 + 3 | } +vlib/v/checker/tests/fn_init_sig.vv:4:1: error: fn `init` must not be public + 2 | return 1 + 3 | } + 4 | pub fn init() int { + | ~~~~~~~~~~~~~~~~~ + 5 | return 1 + 6 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/fn_init_sig.vv b/v_windows/v/old/vlib/v/checker/tests/fn_init_sig.vv new file mode 100644 index 0000000..bff6d49 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/fn_init_sig.vv @@ -0,0 +1,6 @@ +fn init() int { + return 1 +} +pub fn init() int { + return 1 +} diff --git a/v_windows/v/old/vlib/v/checker/tests/fn_return_or_err.out b/v_windows/v/old/vlib/v/checker/tests/fn_return_or_err.out new file mode 100644 index 0000000..ebb4494 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/fn_return_or_err.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/fn_return_or_err.vv:6:17: error: unexpected `or` block, the function `pop` does not return an optional + 4 | + 5 | pub fn next(mut v []Typ) Typ { + 6 | return v.pop() or { Typ{} } + | ~~~~~~~~~~~~ + 7 | } + 8 | diff --git a/v_windows/v/old/vlib/v/checker/tests/fn_return_or_err.vv b/v_windows/v/old/vlib/v/checker/tests/fn_return_or_err.vv new file mode 100644 index 0000000..c303b0a --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/fn_return_or_err.vv @@ -0,0 +1,13 @@ +module main + +pub struct Typ {} + +pub fn next(mut v []Typ) Typ { + return v.pop() or { Typ{} } +} + +fn main() { + mut v := [Typ{}] + last := next(mut v) + println('$last') +} diff --git a/v_windows/v/old/vlib/v/checker/tests/fn_type_exists.out b/v_windows/v/old/vlib/v/checker/tests/fn_type_exists.out new file mode 100644 index 0000000..c6aa40f --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/fn_type_exists.out @@ -0,0 +1,10 @@ +vlib/v/checker/tests/fn_type_exists.vv:1:34: error: unknown type `Pants` + 1 | type PantsCreator = fn (a Shirt) Pants + | ~~~~~ + 2 | + 3 | type PantsConsumer = fn (p Pants) +vlib/v/checker/tests/fn_type_exists.vv:3:28: error: unknown type `Pants` + 1 | type PantsCreator = fn (a Shirt) Pants + 2 | + 3 | type PantsConsumer = fn (p Pants) + | ~~~~~ \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/fn_type_exists.vv b/v_windows/v/old/vlib/v/checker/tests/fn_type_exists.vv new file mode 100644 index 0000000..04ead19 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/fn_type_exists.vv @@ -0,0 +1,3 @@ +type PantsCreator = fn (a Shirt) Pants + +type PantsConsumer = fn (p Pants) diff --git a/v_windows/v/old/vlib/v/checker/tests/fn_var.out b/v_windows/v/old/vlib/v/checker/tests/fn_var.out new file mode 100644 index 0000000..d3487a1 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/fn_var.out @@ -0,0 +1,23 @@ +vlib/v/checker/tests/fn_var.vv:1:10: error: missing return at the end of an anonymous function + 1 | mut f := fn(i int) byte {} + | ~~~~~~~~~~~~~~~~~ + 2 | f = 4 + 3 | mut p := &f +vlib/v/checker/tests/fn_var.vv:2:5: error: cannot assign to `f`: expected `fn (int) byte`, not `int literal` + 1 | mut f := fn(i int) byte {} + 2 | f = 4 + | ^ + 3 | mut p := &f + 4 | p = &[f] +vlib/v/checker/tests/fn_var.vv:4:5: error: cannot assign to `p`: expected `&fn (int) byte`, not `&[]fn (int) byte` + 2 | f = 4 + 3 | mut p := &f + 4 | p = &[f] + | ^ + 5 | _ = p + 6 | i := 0 +vlib/v/checker/tests/fn_var.vv:8:31: error: undefined ident: `i` + 6 | i := 0 + 7 | println(i) + 8 | f = fn(mut a []int) { println(i) } + | ^ diff --git a/v_windows/v/old/vlib/v/checker/tests/fn_var.vv b/v_windows/v/old/vlib/v/checker/tests/fn_var.vv new file mode 100644 index 0000000..0a48b5d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/fn_var.vv @@ -0,0 +1,8 @@ +mut f := fn(i int) byte {} +f = 4 +mut p := &f +p = &[f] +_ = p +i := 0 +println(i) +f = fn(mut a []int) { println(i) } diff --git a/v_windows/v/old/vlib/v/checker/tests/fn_variadic.out b/v_windows/v/old/vlib/v/checker/tests/fn_variadic.out new file mode 100644 index 0000000..f14183a --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/fn_variadic.out @@ -0,0 +1,14 @@ +vlib/v/checker/tests/fn_variadic.vv:3:8: error: when forwarding a variadic variable, it must be the final argument + 1 | fn f(vi ...int) int { + 2 | _ = f(...vi) // OK + 3 | _ = f(...vi, 2) + | ~~~~~ + 4 | return 0 + 5 | } +vlib/v/checker/tests/fn_variadic.vv:11:10: error: when forwarding a variadic variable, it must be the final argument + 9 | fn (s S1) m(vi ...int) int { + 10 | _ = s.m(...vi) // OK + 11 | _ = s.m(...vi, 2) + | ~~~~~ + 12 | return 0 + 13 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/fn_variadic.vv b/v_windows/v/old/vlib/v/checker/tests/fn_variadic.vv new file mode 100644 index 0000000..b7faad7 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/fn_variadic.vv @@ -0,0 +1,13 @@ +fn f(vi ...int) int { + _ = f(...vi) // OK + _ = f(...vi, 2) + return 0 +} + +struct S1 {} + +fn (s S1) m(vi ...int) int { + _ = s.m(...vi) // OK + _ = s.m(...vi, 2) + return 0 +} diff --git a/v_windows/v/old/vlib/v/checker/tests/for_in_index_optional.out b/v_windows/v/old/vlib/v/checker/tests/for_in_index_optional.out new file mode 100644 index 0000000..bbab32a --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/for_in_index_optional.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/for_in_index_optional.vv:3:18: error: for in: cannot index `?[]string` + 1 | import os + 2 | fn main() { + 3 | for file in os.ls('.') { + | ~~~~~~~ + 4 | println(file) + 5 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/for_in_index_optional.vv b/v_windows/v/old/vlib/v/checker/tests/for_in_index_optional.vv new file mode 100644 index 0000000..c384c79 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/for_in_index_optional.vv @@ -0,0 +1,6 @@ +import os +fn main() { + for file in os.ls('.') { + println(file) + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/for_in_index_type.out b/v_windows/v/old/vlib/v/checker/tests/for_in_index_type.out new file mode 100644 index 0000000..376ba73 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/for_in_index_type.out @@ -0,0 +1,13 @@ +vlib/v/checker/tests/for_in_index_type.vv:2:11: error: for in: cannot index `int literal` + 1 | fn main() { + 2 | for a in 52 { + | ~~ + 3 | println(a) + 4 | } +vlib/v/checker/tests/for_in_index_type.vv:3:3: error: `println` can not print void expressions + 1 | fn main() { + 2 | for a in 52 { + 3 | println(a) + | ~~~~~~~~~~ + 4 | } + 5 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/for_in_index_type.vv b/v_windows/v/old/vlib/v/checker/tests/for_in_index_type.vv new file mode 100644 index 0000000..cbc26ae --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/for_in_index_type.vv @@ -0,0 +1,5 @@ +fn main() { + for a in 52 { + println(a) + } +} \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/for_in_map_one_variable_err.out b/v_windows/v/old/vlib/v/checker/tests/for_in_map_one_variable_err.out new file mode 100644 index 0000000..d7c6579 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/for_in_map_one_variable_err.out @@ -0,0 +1,8 @@ +vlib/v/checker/tests/for_in_map_one_variable_err.vv:3:6: error: declare a key and a value variable when ranging a map: `for key, val in map {` +use `_` if you do not need the variable + 1 | fn main() { + 2 | kvs := map{'foo':'bar'} + 3 | for k in kvs { + | ^ + 4 | println('$k') + 5 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/for_in_map_one_variable_err.vv b/v_windows/v/old/vlib/v/checker/tests/for_in_map_one_variable_err.vv new file mode 100644 index 0000000..9297280 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/for_in_map_one_variable_err.vv @@ -0,0 +1,6 @@ +fn main() { + kvs := map{'foo':'bar'} + for k in kvs { + println('$k') + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/for_in_mut_val_type.out b/v_windows/v/old/vlib/v/checker/tests/for_in_mut_val_type.out new file mode 100644 index 0000000..d898cf7 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/for_in_mut_val_type.out @@ -0,0 +1,42 @@ +vlib/v/checker/tests/for_in_mut_val_type.vv:3:15: error: `a1` is immutable, it cannot be changed + 1 | fn main() { + 2 | a1 := [1, 2, 3] + 3 | for mut j in a1 { + | ~~ + 4 | j *= 2 + 5 | } +vlib/v/checker/tests/for_in_mut_val_type.vv:7:15: error: `a2` is immutable, it cannot be changed + 5 | } + 6 | a2 := [1, 2, 3]! + 7 | for mut j in a2 { + | ~~ + 8 | j *= 2 + 9 | } +vlib/v/checker/tests/for_in_mut_val_type.vv:11:18: error: `m` is immutable, it cannot be changed + 9 | } + 10 | m := map{'aa': 1, 'bb': 2} + 11 | for _, mut j in m { + | ^ + 12 | j *= 2 + 13 | } +vlib/v/checker/tests/for_in_mut_val_type.vv:14:15: error: array literal is immutable, it cannot be changed + 12 | j *= 2 + 13 | } + 14 | for mut j in [1, 2, 3] { + | ~~~~~~~~~ + 15 | j *= 2 + 16 | } +vlib/v/checker/tests/for_in_mut_val_type.vv:17:15: error: array literal is immutable, it cannot be changed + 15 | j *= 2 + 16 | } + 17 | for mut j in [1, 2, 3]! { + | ~~~~~~~~~~ + 18 | j *= 2 + 19 | } +vlib/v/checker/tests/for_in_mut_val_type.vv:20:21: error: map literal is immutable, it cannot be changed + 18 | j *= 2 + 19 | } + 20 | for _, mut j in map{'aa': 1, 'bb': 2} { + | ~~~~~~~~~~~~~~~~~~ + 21 | j *= 2 + 22 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/for_in_mut_val_type.vv b/v_windows/v/old/vlib/v/checker/tests/for_in_mut_val_type.vv new file mode 100644 index 0000000..664fb66 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/for_in_mut_val_type.vv @@ -0,0 +1,23 @@ +fn main() { + a1 := [1, 2, 3] + for mut j in a1 { + j *= 2 + } + a2 := [1, 2, 3]! + for mut j in a2 { + j *= 2 + } + m := map{'aa': 1, 'bb': 2} + for _, mut j in m { + j *= 2 + } + for mut j in [1, 2, 3] { + j *= 2 + } + for mut j in [1, 2, 3]! { + j *= 2 + } + for _, mut j in map{'aa': 1, 'bb': 2} { + j *= 2 + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/for_in_range_not_match_type.out b/v_windows/v/old/vlib/v/checker/tests/for_in_range_not_match_type.out new file mode 100644 index 0000000..d969830 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/for_in_range_not_match_type.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/for_in_range_not_match_type.vv:2:11: error: range types do not match + 1 | fn main() { + 2 | for i in 10..10.5 { + | ~~ + 3 | println(i) + 4 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/for_in_range_not_match_type.vv b/v_windows/v/old/vlib/v/checker/tests/for_in_range_not_match_type.vv new file mode 100644 index 0000000..8b15863 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/for_in_range_not_match_type.vv @@ -0,0 +1,5 @@ +fn main() { + for i in 10..10.5 { + println(i) + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/for_in_range_string_type.out b/v_windows/v/old/vlib/v/checker/tests/for_in_range_string_type.out new file mode 100644 index 0000000..4d3d531 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/for_in_range_string_type.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/for_in_range_string_type.vv:2:11: error: range type can not be string + 1 | fn main() { + 2 | for i in 'a'..'b' { + | ~~~ + 3 | println(i) + 4 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/for_in_range_string_type.vv b/v_windows/v/old/vlib/v/checker/tests/for_in_range_string_type.vv new file mode 100644 index 0000000..a459398 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/for_in_range_string_type.vv @@ -0,0 +1,5 @@ +fn main() { + for i in 'a'..'b' { + println(i) + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/for_match_err.out b/v_windows/v/old/vlib/v/checker/tests/for_match_err.out new file mode 100644 index 0000000..9c90a9e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/for_match_err.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/for_match_err.vv:3:7: error: cannot use `match` in `for` loop + 1 | fn main() { + 2 | mut a := 2 + 3 | for match a { + | ~~~~~ + 4 | 2 { + 5 | println('a == 2') diff --git a/v_windows/v/old/vlib/v/checker/tests/for_match_err.vv b/v_windows/v/old/vlib/v/checker/tests/for_match_err.vv new file mode 100644 index 0000000..910f39f --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/for_match_err.vv @@ -0,0 +1,15 @@ +fn main() { + mut a := 2 + for match a { + 2 { + println('a == 2') + a = 0 + } + 0 { + println('a == 0') + } + else { + println('unexpected branch') + } + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/function_arg_mutable_err.out b/v_windows/v/old/vlib/v/checker/tests/function_arg_mutable_err.out new file mode 100644 index 0000000..e676a78 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/function_arg_mutable_err.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/function_arg_mutable_err.vv:1:18: error: mutable arguments are only allowed for arrays, interfaces, maps, pointers, structs or their aliases +return values instead: `fn foo(mut n int) {` => `fn foo(n int) int {` + 1 | fn mod_ptr(mut a int) { + | ~~~ + 2 | a = 77 + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/function_arg_mutable_err.vv b/v_windows/v/old/vlib/v/checker/tests/function_arg_mutable_err.vv new file mode 100644 index 0000000..30165cb --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/function_arg_mutable_err.vv @@ -0,0 +1,8 @@ +fn mod_ptr(mut a int) { + a = 77 +} + +fn main() { + println('hello') +} + diff --git a/v_windows/v/old/vlib/v/checker/tests/function_arg_redefinition.out b/v_windows/v/old/vlib/v/checker/tests/function_arg_redefinition.out new file mode 100644 index 0000000..e540131 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/function_arg_redefinition.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/function_arg_redefinition.vv:1:17: error: redefinition of parameter `para1` + 1 | fn f(para1 int, para1 f32) int { + | ~~~~~ + 2 | return para1 + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/function_arg_redefinition.vv b/v_windows/v/old/vlib/v/checker/tests/function_arg_redefinition.vv new file mode 100644 index 0000000..0618fba --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/function_arg_redefinition.vv @@ -0,0 +1,7 @@ +fn f(para1 int, para1 f32) int { + return para1 +} + +fn main() { + a := f(11, 1.1) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/function_count_of_args_mismatch_err.out b/v_windows/v/old/vlib/v/checker/tests/function_count_of_args_mismatch_err.out new file mode 100644 index 0000000..d9ad12e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/function_count_of_args_mismatch_err.out @@ -0,0 +1,27 @@ +vlib/v/checker/tests/function_count_of_args_mismatch_err.vv:8:13: error: expected 1 arguments, but got 3 + 6 | + 7 | fn main() { + 8 | test(true, false, 1) + | ~~~~~~~~ + 9 | test() + 10 | test2(true, false, 1) +vlib/v/checker/tests/function_count_of_args_mismatch_err.vv:9:2: error: expected 1 arguments, but got 0 + 7 | fn main() { + 8 | test(true, false, 1) + 9 | test() + | ~~~~~~ + 10 | test2(true, false, 1) + 11 | test2(true) +vlib/v/checker/tests/function_count_of_args_mismatch_err.vv:10:21: error: expected 2 arguments, but got 3 + 8 | test(true, false, 1) + 9 | test() + 10 | test2(true, false, 1) + | ^ + 11 | test2(true) + 12 | } +vlib/v/checker/tests/function_count_of_args_mismatch_err.vv:11:2: error: expected 2 arguments, but got 1 + 9 | test() + 10 | test2(true, false, 1) + 11 | test2(true) + | ~~~~~~~~~~~ + 12 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/function_count_of_args_mismatch_err.vv b/v_windows/v/old/vlib/v/checker/tests/function_count_of_args_mismatch_err.vv new file mode 100644 index 0000000..00fe212 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/function_count_of_args_mismatch_err.vv @@ -0,0 +1,12 @@ +fn test(b bool) { +} + +fn test2(b bool, v T) { +} + +fn main() { + test(true, false, 1) + test() + test2(true, false, 1) + test2(true) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/function_missing_return_type.out b/v_windows/v/old/vlib/v/checker/tests/function_missing_return_type.out new file mode 100644 index 0000000..73292af --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/function_missing_return_type.out @@ -0,0 +1,12 @@ +vlib/v/checker/tests/function_missing_return_type.vv:1:1: error: missing return at end of function `h` + 1 | fn h() int { + | ~~~~~~~~~~ + 2 | } + 3 | +vlib/v/checker/tests/function_missing_return_type.vv:12:1: error: missing return at end of function `abc` + 10 | } + 11 | + 12 | fn (s Abc) abc() &int { + | ~~~~~~~~~~~~~~~~~~~~~ + 13 | if true { + 14 | return &s.x diff --git a/v_windows/v/old/vlib/v/checker/tests/function_missing_return_type.vv b/v_windows/v/old/vlib/v/checker/tests/function_missing_return_type.vv new file mode 100644 index 0000000..74d8cc1 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/function_missing_return_type.vv @@ -0,0 +1,22 @@ +fn h() int { +} + +struct Abc { + x int +} + +fn (s Abc) panic(message string) { + println('called ${@METHOD} with message: $message') +} + +fn (s Abc) abc() &int { + if true { + return &s.x + } + s.panic(@FN) +} + +fn main() { + d := h() + println('$d') +} diff --git a/v_windows/v/old/vlib/v/checker/tests/function_variadic_arg_array_decompose.out b/v_windows/v/old/vlib/v/checker/tests/function_variadic_arg_array_decompose.out new file mode 100644 index 0000000..f72b6e0 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/function_variadic_arg_array_decompose.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/function_variadic_arg_array_decompose.vv:11:10: error: too many arguments in call to `sum` + 9 | fn main() { + 10 | b := [5, 6, 7] + 11 | println(sum(1, 2, ...b)) + | ~~~~~~~~~~~~~~~ + 12 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/function_variadic_arg_array_decompose.vv b/v_windows/v/old/vlib/v/checker/tests/function_variadic_arg_array_decompose.vv new file mode 100644 index 0000000..27fa0c2 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/function_variadic_arg_array_decompose.vv @@ -0,0 +1,12 @@ +fn sum(a ...int) int { + mut total := 0 + for x in a { + total += x + } + return total +} + +fn main() { + b := [5, 6, 7] + println(sum(1, 2, ...b)) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/function_wrong_arg_type.out b/v_windows/v/old/vlib/v/checker/tests/function_wrong_arg_type.out new file mode 100644 index 0000000..07e4fc8 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/function_wrong_arg_type.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/function_wrong_arg_type.vv:7:9: error: cannot use `f64` as `int` in argument 1 to `f` + 5 | fn main() { + 6 | a := 12.3 + 7 | q := f(a) + | ^ + 8 | println('$q') + 9 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/function_wrong_arg_type.vv b/v_windows/v/old/vlib/v/checker/tests/function_wrong_arg_type.vv new file mode 100644 index 0000000..6cd3fbb --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/function_wrong_arg_type.vv @@ -0,0 +1,9 @@ +fn f(x int) int { + return x+x +} + +fn main() { + a := 12.3 + q := f(a) + println('$q') +} diff --git a/v_windows/v/old/vlib/v/checker/tests/function_wrong_return_type.out b/v_windows/v/old/vlib/v/checker/tests/function_wrong_return_type.out new file mode 100644 index 0000000..070d573 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/function_wrong_return_type.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/function_wrong_return_type.vv:2:9: error: cannot use `float literal` as type `int` in return argument + 1 | fn h() int { + 2 | return 3.14 + | ~~~~ + 3 | } + 4 | diff --git a/v_windows/v/old/vlib/v/checker/tests/function_wrong_return_type.vv b/v_windows/v/old/vlib/v/checker/tests/function_wrong_return_type.vv new file mode 100644 index 0000000..0c47628 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/function_wrong_return_type.vv @@ -0,0 +1,8 @@ +fn h() int { + return 3.14 +} + +fn main() { + d := h() + println('$d') +} diff --git a/v_windows/v/old/vlib/v/checker/tests/generic_fn_decl_without_generic_names_err.out b/v_windows/v/old/vlib/v/checker/tests/generic_fn_decl_without_generic_names_err.out new file mode 100644 index 0000000..502f2b8 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/generic_fn_decl_without_generic_names_err.out @@ -0,0 +1,14 @@ +vlib/v/checker/tests/generic_fn_decl_without_generic_names_err.vv:26:1: error: generic function declaration must specify generic type names, e.g. foo + 24 | } + 25 | + 26 | fn g_worker(g Generic) { + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + 27 | t := <-g.ch + 28 | handle(t) +vlib/v/checker/tests/generic_fn_decl_without_generic_names_err.vv:32:1: error: generic function declaration must specify generic type names, e.g. foo + 30 | } + 31 | + 32 | fn handle(t T) { + | ~~~~~~~~~~~~~~ + 33 | println("hi") + 34 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/generic_fn_decl_without_generic_names_err.vv b/v_windows/v/old/vlib/v/checker/tests/generic_fn_decl_without_generic_names_err.vv new file mode 100644 index 0000000..cc001e9 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/generic_fn_decl_without_generic_names_err.vv @@ -0,0 +1,34 @@ +struct Generic { + ch chan T +} + +struct Concrete { + msg string +} + +fn main() { + g := create_generic_t() + g.ch <- Concrete{ + msg: 'hello' + } +} + +fn create_generic_t() Generic { + g := Generic{ + ch: chan T{} + } + + go g_worker(g) + + return g +} + +fn g_worker(g Generic) { + t := <-g.ch + handle(t) + // println("${t.msg}") +} + +fn handle(t T) { + println("hi") +} diff --git a/v_windows/v/old/vlib/v/checker/tests/generic_param_used_as_an_array_err.out b/v_windows/v/old/vlib/v/checker/tests/generic_param_used_as_an_array_err.out new file mode 100644 index 0000000..eb4ee7c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/generic_param_used_as_an_array_err.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/generic_param_used_as_an_array_err.vv:2:10: error: generic type T does not support indexing, pass an array, or a reference instead, e.g. []T or &T + 1 | fn test(arr T) { + 2 | a := arr[1] + | ~~~ + 3 | println(a) + 4 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/generic_param_used_as_an_array_err.vv b/v_windows/v/old/vlib/v/checker/tests/generic_param_used_as_an_array_err.vv new file mode 100644 index 0000000..f545b77 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/generic_param_used_as_an_array_err.vv @@ -0,0 +1,12 @@ +fn test(arr T) { + a := arr[1] + println(a) +} + +fn main() { + a := [1, 2, 3]! + test(a) + + b := ['a', 'b', 'c']! + test(b) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/generic_sumtype_invalid_variant.out b/v_windows/v/old/vlib/v/checker/tests/generic_sumtype_invalid_variant.out new file mode 100644 index 0000000..c82df9e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/generic_sumtype_invalid_variant.out @@ -0,0 +1,14 @@ +vlib/v/checker/tests/generic_sumtype_invalid_variant.vv:5:7: error: `MultiGeneric` has no variant `u64` + 3 | fn main() { + 4 | mut m := MultiGeneric(true) + 5 | if m is u64 { + | ~~ + 6 | println('hi') + 7 | } +vlib/v/checker/tests/generic_sumtype_invalid_variant.vv:8:7: error: `MultiGeneric` has no variant `X` + 6 | println('hi') + 7 | } + 8 | if m is X { + | ~~ + 9 | println('hi again') + 10 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/generic_sumtype_invalid_variant.vv b/v_windows/v/old/vlib/v/checker/tests/generic_sumtype_invalid_variant.vv new file mode 100644 index 0000000..3cde35b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/generic_sumtype_invalid_variant.vv @@ -0,0 +1,11 @@ +type MultiGeneric = X | Y | Z + +fn main() { + mut m := MultiGeneric(true) + if m is u64 { + println('hi') + } + if m is X { + println('hi again') + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/generics_fn_called_arg_mismatch.out b/v_windows/v/old/vlib/v/checker/tests/generics_fn_called_arg_mismatch.out new file mode 100644 index 0000000..f6913f5 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/generics_fn_called_arg_mismatch.out @@ -0,0 +1,27 @@ +vlib/v/checker/tests/generics_fn_called_arg_mismatch.vv:6:15: error: cannot use `int literal` as `bool` in argument 1 to `foo` + 4 | + 5 | fn main() { + 6 | foo(1) + | ^ + 7 | foo(2.2) + 8 | foo(true) +vlib/v/checker/tests/generics_fn_called_arg_mismatch.vv:7:15: error: cannot use `float literal` as `bool` in argument 1 to `foo` + 5 | fn main() { + 6 | foo(1) + 7 | foo(2.2) + | ~~~ + 8 | foo(true) + 9 | foo('aaa') +vlib/v/checker/tests/generics_fn_called_arg_mismatch.vv:8:17: error: cannot use `bool` as `string` in argument 1 to `foo` + 6 | foo(1) + 7 | foo(2.2) + 8 | foo(true) + | ~~~~ + 9 | foo('aaa') + 10 | } +vlib/v/checker/tests/generics_fn_called_arg_mismatch.vv:9:14: error: cannot use `string` as `int` in argument 1 to `foo` + 7 | foo(2.2) + 8 | foo(true) + 9 | foo('aaa') + | ~~~~~ + 10 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/generics_fn_called_arg_mismatch.vv b/v_windows/v/old/vlib/v/checker/tests/generics_fn_called_arg_mismatch.vv new file mode 100644 index 0000000..1960a54 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/generics_fn_called_arg_mismatch.vv @@ -0,0 +1,10 @@ +fn foo(b T) { + println(b) +} + +fn main() { + foo(1) + foo(2.2) + foo(true) + foo('aaa') +} diff --git a/v_windows/v/old/vlib/v/checker/tests/generics_fn_called_multi_args_mismatch.out b/v_windows/v/old/vlib/v/checker/tests/generics_fn_called_multi_args_mismatch.out new file mode 100644 index 0000000..3f23d6b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/generics_fn_called_multi_args_mismatch.out @@ -0,0 +1,14 @@ +vlib/v/checker/tests/generics_fn_called_multi_args_mismatch.vv:3:13: error: cannot use `[]rune` as `string` in argument 2 to `foo_str` + 1 | fn main() { + 2 | x := 'ab'.runes()[..1] + 3 | foo_str(1, x) + | ^ + 4 | + 5 | foo := Foo{} +vlib/v/checker/tests/generics_fn_called_multi_args_mismatch.vv:6:11: error: cannot use `[]rune` as `string` in argument 1 to `Foo.info` + 4 | + 5 | foo := Foo{} + 6 | foo.info(x) + | ^ + 7 | } + 8 | diff --git a/v_windows/v/old/vlib/v/checker/tests/generics_fn_called_multi_args_mismatch.vv b/v_windows/v/old/vlib/v/checker/tests/generics_fn_called_multi_args_mismatch.vv new file mode 100644 index 0000000..fdbdf4d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/generics_fn_called_multi_args_mismatch.vv @@ -0,0 +1,17 @@ +fn main() { + x := 'ab'.runes()[..1] + foo_str(1, x) + + foo := Foo{} + foo.info(x) +} + +fn foo_str(b T, a string) { +} + +struct Foo { + t T +} + +fn (f Foo) info(a string) { +} diff --git a/v_windows/v/old/vlib/v/checker/tests/generics_fn_called_no_arg_err.out b/v_windows/v/old/vlib/v/checker/tests/generics_fn_called_no_arg_err.out new file mode 100644 index 0000000..4212808 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/generics_fn_called_no_arg_err.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/generics_fn_called_no_arg_err.vv:13:10: error: no argument generic function must add concrete types, e.g. foo() + 11 | + 12 | fn main() { + 13 | q := new_queue() + | ~~~~~~~~~~~ + 14 | println(q) + 15 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/generics_fn_called_no_arg_err.vv b/v_windows/v/old/vlib/v/checker/tests/generics_fn_called_no_arg_err.vv new file mode 100644 index 0000000..a05e1d5 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/generics_fn_called_no_arg_err.vv @@ -0,0 +1,15 @@ +struct Queue{ + buffer []T +} + +fn new_queue() Queue { + q := Queue{ + buffer: []T{cap: 1024} + } + return q +} + +fn main() { + q := new_queue() + println(q) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/generics_fn_called_outside_of_generic_fn.out b/v_windows/v/old/vlib/v/checker/tests/generics_fn_called_outside_of_generic_fn.out new file mode 100644 index 0000000..ec84b67 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/generics_fn_called_outside_of_generic_fn.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/generics_fn_called_outside_of_generic_fn.vv:4:2: error: generic fn using generic types cannot be called outside of generic fn + 2 | + 3 | fn main() { + 4 | foo() + | ~~~~~~~~ + 5 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/generics_fn_called_outside_of_generic_fn.vv b/v_windows/v/old/vlib/v/checker/tests/generics_fn_called_outside_of_generic_fn.vv new file mode 100644 index 0000000..c8b72c2 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/generics_fn_called_outside_of_generic_fn.vv @@ -0,0 +1,5 @@ +fn foo() {} + +fn main() { + foo() +} diff --git a/v_windows/v/old/vlib/v/checker/tests/generics_fn_called_variadic_arg_mismatch.out b/v_windows/v/old/vlib/v/checker/tests/generics_fn_called_variadic_arg_mismatch.out new file mode 100644 index 0000000..cf4d3b2 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/generics_fn_called_variadic_arg_mismatch.out @@ -0,0 +1,14 @@ +vlib/v/checker/tests/generics_fn_called_variadic_arg_mismatch.vv:12:11: error: cannot use `[]int` as `int` in argument 1 to `max` + 10 | + 11 | fn main() { + 12 | a := max([1, 2, 3, 4]) + | ~~~~~~~~~~~~ + 13 | println(a) + 14 | +vlib/v/checker/tests/generics_fn_called_variadic_arg_mismatch.vv:15:16: error: cannot use `[]int` as `int` in argument 1 to `max` + 13 | println(a) + 14 | + 15 | b := max([1, 2, 3, 4]) + | ~~~~~~~~~~~~ + 16 | println(b) + 17 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/generics_fn_called_variadic_arg_mismatch.vv b/v_windows/v/old/vlib/v/checker/tests/generics_fn_called_variadic_arg_mismatch.vv new file mode 100644 index 0000000..e322a64 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/generics_fn_called_variadic_arg_mismatch.vv @@ -0,0 +1,17 @@ +fn max(a ...T) T { + mut max := a[0] + for item in a[1..] { + if max < item { + max = item + } + } + return max +} + +fn main() { + a := max([1, 2, 3, 4]) + println(a) + + b := max([1, 2, 3, 4]) + println(b) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/generics_fn_return_generic_struct_err.out b/v_windows/v/old/vlib/v/checker/tests/generics_fn_return_generic_struct_err.out new file mode 100644 index 0000000..b6efe45 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/generics_fn_return_generic_struct_err.out @@ -0,0 +1,13 @@ +vlib/v/checker/tests/generics_fn_return_generic_struct_err.vv:13:32: error: return generic struct in fn declaration must specify the generic type names, e.g. Foo + 11 | } + 12 | + 13 | pub fn new_channel_struct() GenericChannelStruct { + | ~~~~~~~~~~~~~~~~~~~~ + 14 | d := GenericChannelStruct{ + 15 | ch: chan T{} +vlib/v/checker/tests/generics_fn_return_generic_struct_err.vv:17:9: error: cannot use `GenericChannelStruct` as type `GenericChannelStruct` in return argument + 15 | ch: chan T{} + 16 | } + 17 | return d + | ^ + 18 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/generics_fn_return_generic_struct_err.vv b/v_windows/v/old/vlib/v/checker/tests/generics_fn_return_generic_struct_err.vv new file mode 100644 index 0000000..0f9a5d5 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/generics_fn_return_generic_struct_err.vv @@ -0,0 +1,18 @@ +struct GenericChannelStruct { + ch chan T +} + +struct Simple { + msg string +} + +fn main() { + new_channel_struct() +} + +pub fn new_channel_struct() GenericChannelStruct { + d := GenericChannelStruct{ + ch: chan T{} + } + return d +} diff --git a/v_windows/v/old/vlib/v/checker/tests/generics_method_receiver_type_err.out b/v_windows/v/old/vlib/v/checker/tests/generics_method_receiver_type_err.out new file mode 100644 index 0000000..1a8b84d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/generics_method_receiver_type_err.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/generics_method_receiver_type_err.vv:6:11: error: receiver must specify the generic type names, e.g. Foo + 4 | } + 5 | + 6 | pub fn (x Node) str() string { + | ~~~~ + 7 | return 'Value is : ${u16(x.val)}\nName is : $x.name' + 8 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/generics_method_receiver_type_err.vv b/v_windows/v/old/vlib/v/checker/tests/generics_method_receiver_type_err.vv new file mode 100644 index 0000000..8a6763c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/generics_method_receiver_type_err.vv @@ -0,0 +1,16 @@ +struct Node { + val T + name string +} + +pub fn (x Node) str() string { + return 'Value is : ${u16(x.val)}\nName is : $x.name' +} + +fn main() { + xx := Node{ + val: u16(11) + name: 'man' + } + println(xx.str()) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/generics_non_generic_fn_called_like_a_generic_one.out b/v_windows/v/old/vlib/v/checker/tests/generics_non_generic_fn_called_like_a_generic_one.out new file mode 100644 index 0000000..0c2c68e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/generics_non_generic_fn_called_like_a_generic_one.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/generics_non_generic_fn_called_like_a_generic_one.vv:4:15: error: a non generic function called like a generic one + 2 | + 3 | fn main() { + 4 | x := math.sin(1.0) + | ~~~~~ + 5 | println(x) + 6 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/generics_non_generic_fn_called_like_a_generic_one.vv b/v_windows/v/old/vlib/v/checker/tests/generics_non_generic_fn_called_like_a_generic_one.vv new file mode 100644 index 0000000..d25ddd1 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/generics_non_generic_fn_called_like_a_generic_one.vv @@ -0,0 +1,6 @@ +import math + +fn main() { + x := math.sin(1.0) + println(x) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/generics_struct_declaration_err.out b/v_windows/v/old/vlib/v/checker/tests/generics_struct_declaration_err.out new file mode 100644 index 0000000..bd3b2c3 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/generics_struct_declaration_err.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/generics_struct_declaration_err.vv:5:1: error: generic struct declaration must specify the generic type names, e.g. Foo + 3 | } + 4 | + 5 | struct MyGenericChannelStruct { + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 6 | GenericChannelStruct + 7 | msg string diff --git a/v_windows/v/old/vlib/v/checker/tests/generics_struct_declaration_err.vv b/v_windows/v/old/vlib/v/checker/tests/generics_struct_declaration_err.vv new file mode 100644 index 0000000..4828ea3 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/generics_struct_declaration_err.vv @@ -0,0 +1,24 @@ +struct GenericChannelStruct { + ch chan T +} + +struct MyGenericChannelStruct { + GenericChannelStruct + msg string +} + +struct Simple { + msg string +} + +fn main() { + new_channel_struct() +} + +pub fn new_channel_struct() GenericChannelStruct { + d := GenericChannelStruct{ + ch: chan T{} + } + + return d +} diff --git a/v_windows/v/old/vlib/v/checker/tests/generics_struct_init_err.out b/v_windows/v/old/vlib/v/checker/tests/generics_struct_init_err.out new file mode 100644 index 0000000..c55c336 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/generics_struct_init_err.out @@ -0,0 +1,14 @@ +vlib/v/checker/tests/generics_struct_init_err.vv:58:8: error: generic struct init must specify type parameter, e.g. Foo + 56 | ret = holder_call_12(neg, 3) + 57 | assert ret == -3 + 58 | ret = FnHolder1{neg}.call(4) + | ~~~~~~~~~~~~~~ + 59 | assert ret == -4 + 60 | +vlib/v/checker/tests/generics_struct_init_err.vv:67:8: error: generic struct init must specify type parameter, e.g. Foo + 65 | ret = holder_call_22(neg, 5) + 66 | assert ret == -5 + 67 | ret = FnHolder2{neg}.call(6) + | ~~~~~~~~~~~~~~ + 68 | assert ret == -6 + 69 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/generics_struct_init_err.vv b/v_windows/v/old/vlib/v/checker/tests/generics_struct_init_err.vv new file mode 100644 index 0000000..4116799 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/generics_struct_init_err.vv @@ -0,0 +1,69 @@ +fn neg(a int) int { + return -a +} + +struct FnHolder1 { + func T +} + +fn (self FnHolder1) call(a int) int { + return self.func(a) +} + +struct FnHolder2 { + func fn (int) int +} + +fn (self FnHolder2) call(a int) int { + return self.func(a) +} + +fn holder_call_1(func T, a int) int { + h := FnHolder1{func} + return h.call(a) +} + +fn holder_call_2(func T, a int) int { + h := FnHolder2{func} + return h.call(a) +} + +fn holder_call_11(func T, a int) int { + f := func + h := FnHolder1{f} + return h.call(a) +} + +fn holder_call_21(func T, a int) int { + f := func + h := FnHolder2{f} + return h.call(a) +} + +fn holder_call_12(func T, a int) int { + return FnHolder1{func}.call(a) +} + +fn holder_call_22(func T, a int) int { + return FnHolder2{func}.call(a) +} + +fn main() { + mut ret := holder_call_1(neg, 1) + assert ret == -1 + ret = holder_call_11(neg, 2) + assert ret == -2 + ret = holder_call_12(neg, 3) + assert ret == -3 + ret = FnHolder1{neg}.call(4) + assert ret == -4 + + ret = holder_call_2(neg, 3) + assert ret == -3 + ret = holder_call_21(neg, 4) + assert ret == -4 + ret = holder_call_22(neg, 5) + assert ret == -5 + ret = FnHolder2{neg}.call(6) + assert ret == -6 +} diff --git a/v_windows/v/old/vlib/v/checker/tests/generics_too_many_parameters.out b/v_windows/v/old/vlib/v/checker/tests/generics_too_many_parameters.out new file mode 100644 index 0000000..caea12a --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/generics_too_many_parameters.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/generics_too_many_parameters.vv:6:8: error: too many generic parameters got 5, expected 1 + 4 | + 5 | fn main() { + 6 | foo(1) + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 7 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/generics_too_many_parameters.vv b/v_windows/v/old/vlib/v/checker/tests/generics_too_many_parameters.vv new file mode 100644 index 0000000..980e1ff --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/generics_too_many_parameters.vv @@ -0,0 +1,7 @@ +fn foo(b T) { + println(b) +} + +fn main() { + foo(1) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/generics_type_ambiguous.out b/v_windows/v/old/vlib/v/checker/tests/generics_type_ambiguous.out new file mode 100644 index 0000000..affad87 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/generics_type_ambiguous.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/generics_type_ambiguous.vv:7:19: error: inferred generic type `B` is ambiguous: got `int`, expected `string` + 5 | + 6 | fn main() { + 7 | test(2, 2, "2", 2) + | ^ + 8 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/generics_type_ambiguous.vv b/v_windows/v/old/vlib/v/checker/tests/generics_type_ambiguous.vv new file mode 100644 index 0000000..9eb0f39 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/generics_type_ambiguous.vv @@ -0,0 +1,8 @@ +fn test (a T, b T, c B, d B) { + println("$a $b $c $d") +} + + +fn main() { + test(2, 2, "2", 2) +} \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/globals/assign_no_value.out b/v_windows/v/old/vlib/v/checker/tests/globals/assign_no_value.out new file mode 100644 index 0000000..2c3865e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/globals/assign_no_value.out @@ -0,0 +1,3 @@ +vlib/v/checker/tests/globals/assign_no_value.vv:1:19: error: undefined ident: `int` + 1 | __global ( test = int ) + | ~~~ diff --git a/v_windows/v/old/vlib/v/checker/tests/globals/assign_no_value.vv b/v_windows/v/old/vlib/v/checker/tests/globals/assign_no_value.vv new file mode 100644 index 0000000..352847e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/globals/assign_no_value.vv @@ -0,0 +1 @@ +__global ( test = int ) \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/globals/incorrect_name_global.out b/v_windows/v/old/vlib/v/checker/tests/globals/incorrect_name_global.out new file mode 100644 index 0000000..05d2e26 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/globals/incorrect_name_global.out @@ -0,0 +1,3 @@ +vlib/v/checker/tests/globals/incorrect_name_global.vv:1:12: error: global name `A` cannot contain uppercase letters, use snake_case instead + 1 | __global ( A = int(1) ) + | ^ diff --git a/v_windows/v/old/vlib/v/checker/tests/globals/incorrect_name_global.vv b/v_windows/v/old/vlib/v/checker/tests/globals/incorrect_name_global.vv new file mode 100644 index 0000000..c9550d8 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/globals/incorrect_name_global.vv @@ -0,0 +1 @@ +__global ( A = int(1) ) diff --git a/v_windows/v/old/vlib/v/checker/tests/globals/no_type.out b/v_windows/v/old/vlib/v/checker/tests/globals/no_type.out new file mode 100644 index 0000000..71899d9 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/globals/no_type.out @@ -0,0 +1,3 @@ +vlib/v/checker/tests/globals/no_type.vv:1:17: error: expecting type declaration + 1 | __global ( test ) + | ^ diff --git a/v_windows/v/old/vlib/v/checker/tests/globals/no_type.vv b/v_windows/v/old/vlib/v/checker/tests/globals/no_type.vv new file mode 100644 index 0000000..264ed9d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/globals/no_type.vv @@ -0,0 +1 @@ +__global ( test ) diff --git a/v_windows/v/old/vlib/v/checker/tests/globals/unexpected_eof.out b/v_windows/v/old/vlib/v/checker/tests/globals/unexpected_eof.out new file mode 100644 index 0000000..f0a6cbc --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/globals/unexpected_eof.out @@ -0,0 +1,3 @@ +vlib/v/checker/tests/globals/unexpected_eof.vv:3:1: error: unexpected eof, expecting ´)´ + 1 | __global ( + 2 | x string diff --git a/v_windows/v/old/vlib/v/checker/tests/globals/unexpected_eof.vv b/v_windows/v/old/vlib/v/checker/tests/globals/unexpected_eof.vv new file mode 100644 index 0000000..ab9c9c6 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/globals/unexpected_eof.vv @@ -0,0 +1,2 @@ +__global ( + x string diff --git a/v_windows/v/old/vlib/v/checker/tests/globals/unknown_typ.out b/v_windows/v/old/vlib/v/checker/tests/globals/unknown_typ.out new file mode 100644 index 0000000..60b44bb --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/globals/unknown_typ.out @@ -0,0 +1,11 @@ +vlib/v/checker/tests/globals/unknown_typ.vv:1:12: error: unknown type `foo` + 1 | __global x foo + | ~~~ + 2 | __global ( + 3 | y = float(5.0) +vlib/v/checker/tests/globals/unknown_typ.vv:3:6: error: unknown function: float + 1 | __global x foo + 2 | __global ( + 3 | y = float(5.0) + | ~~~~~~~~~~ + 4 | ) diff --git a/v_windows/v/old/vlib/v/checker/tests/globals/unknown_typ.vv b/v_windows/v/old/vlib/v/checker/tests/globals/unknown_typ.vv new file mode 100644 index 0000000..9d7fdd1 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/globals/unknown_typ.vv @@ -0,0 +1,4 @@ +__global x foo +__global ( + y = float(5.0) +) diff --git a/v_windows/v/old/vlib/v/checker/tests/globals_error.out b/v_windows/v/old/vlib/v/checker/tests/globals_error.out new file mode 100644 index 0000000..1dcbb36 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/globals_error.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/globals_error.vv:2:1: error: use `v -enable-globals ...` to enable globals + 1 | + 2 | __global ( rfcnt int ) + | ~~~~~~~~ + 3 | + 4 | fn abc(){ diff --git a/v_windows/v/old/vlib/v/checker/tests/globals_error.run.out b/v_windows/v/old/vlib/v/checker/tests/globals_error.run.out new file mode 100644 index 0000000..525f325 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/globals_error.run.out @@ -0,0 +1 @@ +rfcnt: 2 diff --git a/v_windows/v/old/vlib/v/checker/tests/globals_error.vv b/v_windows/v/old/vlib/v/checker/tests/globals_error.vv new file mode 100644 index 0000000..51048dc --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/globals_error.vv @@ -0,0 +1,12 @@ + +__global ( rfcnt int ) + +fn abc(){ + rfcnt = 2 +} + +fn main(){ + rfcnt = 1 + abc() + println('rfcnt: $rfcnt') +} diff --git a/v_windows/v/old/vlib/v/checker/tests/globals_run/function_stored_in_global.run.out b/v_windows/v/old/vlib/v/checker/tests/globals_run/function_stored_in_global.run.out new file mode 100644 index 0000000..586eac7 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/globals_run/function_stored_in_global.run.out @@ -0,0 +1,4 @@ +[vlib/v/checker/tests/globals_run/function_stored_in_global.vv:14] voidptr(main.abc) == voidptr(cpu_get_id): true +[vlib/v/checker/tests/globals_run/function_stored_in_global.vv:15] main.cpu_get_id(): 123 +[vlib/v/checker/tests/globals_run/function_stored_in_global.vv:16] abc(): 123 +[vlib/v/checker/tests/globals_run/function_stored_in_global.vv:17] abc() == main.cpu_get_id(): true diff --git a/v_windows/v/old/vlib/v/checker/tests/globals_run/function_stored_in_global.vv b/v_windows/v/old/vlib/v/checker/tests/globals_run/function_stored_in_global.vv new file mode 100644 index 0000000..e74825e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/globals_run/function_stored_in_global.vv @@ -0,0 +1,20 @@ +__global ( + cpu_get_id fn () u64 +) +fn current() u64 { + return cpu_get_id() +} + +fn abc() u64 { + return 123 +} + +fn main() { + cpu_get_id = abc + dump(voidptr(abc) == voidptr(cpu_get_id)) + dump(cpu_get_id()) + dump(abc()) + dump(abc() == cpu_get_id()) + assert abc() == cpu_get_id() + assert voidptr(abc) == voidptr(cpu_get_id) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/globals_run/global_array_indexed_by_global_fn.run.out b/v_windows/v/old/vlib/v/checker/tests/globals_run/global_array_indexed_by_global_fn.run.out new file mode 100644 index 0000000..7968dd1 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/globals_run/global_array_indexed_by_global_fn.run.out @@ -0,0 +1,3 @@ +[vlib/v/checker/tests/globals_run/global_array_indexed_by_global_fn.vv:30] cpu_locals.map(it.x): [123, 456] +[vlib/v/checker/tests/globals_run/global_array_indexed_by_global_fn.vv:33] x.x: 123 +[vlib/v/checker/tests/globals_run/global_array_indexed_by_global_fn.vv:36] y.x: 456 diff --git a/v_windows/v/old/vlib/v/checker/tests/globals_run/global_array_indexed_by_global_fn.vv b/v_windows/v/old/vlib/v/checker/tests/globals_run/global_array_indexed_by_global_fn.vv new file mode 100644 index 0000000..272df4c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/globals_run/global_array_indexed_by_global_fn.vv @@ -0,0 +1,40 @@ +struct Local { + x int +} + +__global ( + cpu_locals []&Local +) + +__global ( + cpu_get_id fn () u64 + cpu_set_id fn (u64) +) + +fn abc0() u64 { + return 0 +} + +fn abc1() u64 { + return 1 +} + +pub fn current() &Local { + return cpu_locals[cpu_get_id()] +} + +fn main() { + cpu_locals = []&Local{} + cpu_locals << &Local{123} + cpu_locals << &Local{456} + dump(cpu_locals.map(it.x)) + cpu_get_id = abc0 + x := current() + dump(x.x) + cpu_get_id = abc1 + y := current() + dump(y.x) + assert x != y + assert x.x == 123 + assert y.x == 456 +} diff --git a/v_windows/v/old/vlib/v/checker/tests/go_expr.out b/v_windows/v/old/vlib/v/checker/tests/go_expr.out new file mode 100644 index 0000000..e19c961 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/go_expr.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/go_expr.vv:2:5: error: expression in `go` must be a function call + 1 | fn main() { + 2 | go 1 + | ^ + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/go_expr.vv b/v_windows/v/old/vlib/v/checker/tests/go_expr.vv new file mode 100644 index 0000000..969b29d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/go_expr.vv @@ -0,0 +1,3 @@ +fn main() { + go 1 +} diff --git a/v_windows/v/old/vlib/v/checker/tests/go_mut_arg.out b/v_windows/v/old/vlib/v/checker/tests/go_mut_arg.out new file mode 100644 index 0000000..70271b1 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/go_mut_arg.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/go_mut_arg.vv:14:16: error: function in `go` statement cannot contain mutable non-reference arguments + 12 | a: 0 + 13 | } + 14 | go change(mut x) + | ^ + 15 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/go_mut_arg.vv b/v_windows/v/old/vlib/v/checker/tests/go_mut_arg.vv new file mode 100644 index 0000000..af86f15 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/go_mut_arg.vv @@ -0,0 +1,15 @@ +struct St { +mut: + a int +} + +fn change(mut a St) { + a.a++ +} + +fn main() { + mut x := St{ + a: 0 + } + go change(mut x) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/go_mut_receiver.out b/v_windows/v/old/vlib/v/checker/tests/go_mut_receiver.out new file mode 100644 index 0000000..f8b2681 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/go_mut_receiver.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/go_mut_receiver.vv:14:5: error: method in `go` statement cannot have non-reference mutable receiver + 12 | a: 0 + 13 | } + 14 | go x.change() + | ^ + 15 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/go_mut_receiver.vv b/v_windows/v/old/vlib/v/checker/tests/go_mut_receiver.vv new file mode 100644 index 0000000..0b64d51 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/go_mut_receiver.vv @@ -0,0 +1,15 @@ +struct St { +mut: + a int +} + +fn (mut a St) change() { + a.a++ +} + +fn main() { + mut x := St{ + a: 0 + } + go x.change() +} diff --git a/v_windows/v/old/vlib/v/checker/tests/go_wait_or.out b/v_windows/v/old/vlib/v/checker/tests/go_wait_or.out new file mode 100644 index 0000000..146f4fb --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/go_wait_or.out @@ -0,0 +1,69 @@ +vlib/v/checker/tests/go_wait_or.vv:11:17: error: unexpected `?`, the function `wait` does not return an optional + 9 | go d(1) + 10 | ] + 11 | r := tg.wait() ? + | ^ + 12 | println(r) + 13 | s := tg[0].wait() or { panic('problem') } +vlib/v/checker/tests/go_wait_or.vv:13:20: error: unexpected `or` block, the function `wait` does not return an optional + 11 | r := tg.wait() ? + 12 | println(r) + 13 | s := tg[0].wait() or { panic('problem') } + | ~~~~~~~~~~~~~~~~~~~~~~~ + 14 | println(s) + 15 | tg2 := [ +vlib/v/checker/tests/go_wait_or.vv:19:13: error: unexpected `or` block, the function `wait` does not return an optional + 17 | go e(1) + 18 | ] + 19 | tg2.wait() or { panic('problem') } + | ~~~~~~~~~~~~~~~~~~~~~~~ + 20 | tg2[0].wait() ? + 21 | tg3 := [ +vlib/v/checker/tests/go_wait_or.vv:20:16: error: unexpected `?`, the function `wait` does not return an optional + 18 | ] + 19 | tg2.wait() or { panic('problem') } + 20 | tg2[0].wait() ? + | ^ + 21 | tg3 := [ + 22 | go f(0) +vlib/v/checker/tests/go_wait_or.vv:25:6: error: `.wait()` cannot be called for an array when thread functions return optionals. Iterate over the arrays elements instead and handle each returned optional with `or`. + 23 | go f(1) + 24 | ] + 25 | tg3.wait() or { panic('problem') } + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 26 | for t in tg3 { + 27 | a := t.wait() +vlib/v/checker/tests/go_wait_or.vv:27:10: error: wait() returns an option, so it should have either an `or {}` block, or `?` at the end + 25 | tg3.wait() or { panic('problem') } + 26 | for t in tg3 { + 27 | a := t.wait() + | ~~~~~~ + 28 | println(a) + 29 | } +vlib/v/checker/tests/go_wait_or.vv:31:15: error: wait() returns an option, so it should have either an `or {}` block, or `?` at the end + 29 | } + 30 | for i, _ in tg3 { + 31 | a := tg3[i].wait() + | ~~~~~~ + 32 | println(a) + 33 | } +vlib/v/checker/tests/go_wait_or.vv:38:6: error: `.wait()` cannot be called for an array when thread functions return optionals. Iterate over the arrays elements instead and handle each returned optional with `or`. + 36 | go g(1) + 37 | ] + 38 | tg4.wait() + | ~~~~~~ + 39 | tg4[0].wait() + 40 | go g(3) or { panic('problem') } +vlib/v/checker/tests/go_wait_or.vv:39:9: error: wait() returns an option, so it should have either an `or {}` block, or `?` at the end + 37 | ] + 38 | tg4.wait() + 39 | tg4[0].wait() + | ~~~~~~ + 40 | go g(3) or { panic('problem') } + 41 | } +vlib/v/checker/tests/go_wait_or.vv:40:10: error: optional handling cannot be done in `go` call. Do it when calling `.wait()` + 38 | tg4.wait() + 39 | tg4[0].wait() + 40 | go g(3) or { panic('problem') } + | ~~~~~~~~~~~~~~~~~~~~~~~ + 41 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/go_wait_or.vv b/v_windows/v/old/vlib/v/checker/tests/go_wait_or.vv new file mode 100644 index 0000000..3522f90 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/go_wait_or.vv @@ -0,0 +1,41 @@ +fn d(n int) f64 { return f64(n) } +fn e(n int) { } +fn f(n int) ?f64 { return f64(n) } +fn g(n int) ? { } + +fn main() { + tg := [ + go d(0) + go d(1) + ] + r := tg.wait() ? + println(r) + s := tg[0].wait() or { panic('problem') } + println(s) + tg2 := [ + go e(0) + go e(1) + ] + tg2.wait() or { panic('problem') } + tg2[0].wait() ? + tg3 := [ + go f(0) + go f(1) + ] + tg3.wait() or { panic('problem') } + for t in tg3 { + a := t.wait() + println(a) + } + for i, _ in tg3 { + a := tg3[i].wait() + println(a) + } + tg4 := [ + go g(0) + go g(1) + ] + tg4.wait() + tg4[0].wait() + go g(3) or { panic('problem') } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/goto_label.out b/v_windows/v/old/vlib/v/checker/tests/goto_label.out new file mode 100644 index 0000000..9312085 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/goto_label.out @@ -0,0 +1,63 @@ +vlib/v/checker/tests/goto_label.vv:5:7: error: unknown label `a1` + 3 | goto f2 + 4 | f1: + 5 | goto a1 + | ~~ + 6 | _ = fn(){ + 7 | goto f1 +vlib/v/checker/tests/goto_label.vv:7:8: error: unknown label `f1` + 5 | goto a1 + 6 | _ = fn(){ + 7 | goto f1 + | ~~ + 8 | goto f2 + 9 | goto a1 +vlib/v/checker/tests/goto_label.vv:8:8: error: unknown label `f2` + 6 | _ = fn(){ + 7 | goto f1 + 8 | goto f2 + | ~~ + 9 | goto a1 + 10 | a1: +vlib/v/checker/tests/goto_label.vv:9:8: error: `goto` requires `unsafe` (consider using labelled break/continue) + 7 | goto f1 + 8 | goto f2 + 9 | goto a1 + | ~~ + 10 | a1: + 11 | goto a1 +vlib/v/checker/tests/goto_label.vv:11:8: error: `goto` requires `unsafe` (consider using labelled break/continue) + 9 | goto a1 + 10 | a1: + 11 | goto a1 + | ~~ + 12 | } + 13 | f2: +vlib/v/checker/tests/goto_label.vv:14:7: error: unknown label `a1` + 12 | } + 13 | f2: + 14 | goto a1 + | ~~ + 15 | goto f1 // back + 16 | goto f2 +vlib/v/checker/tests/goto_label.vv:22:7: error: unknown label `f1` + 20 | goto g1 // forward + 21 | g1: + 22 | goto f1 + | ~~ + 23 | goto a1 + 24 | goto g1 // back +vlib/v/checker/tests/goto_label.vv:23:7: error: unknown label `a1` + 21 | g1: + 22 | goto f1 + 23 | goto a1 + | ~~ + 24 | goto g1 // back + 25 | goto undefined +vlib/v/checker/tests/goto_label.vv:25:7: error: unknown label `undefined` + 23 | goto a1 + 24 | goto g1 // back + 25 | goto undefined + | ~~~~~~~~~ + 26 | }} + 27 | diff --git a/v_windows/v/old/vlib/v/checker/tests/goto_label.vv b/v_windows/v/old/vlib/v/checker/tests/goto_label.vv new file mode 100644 index 0000000..23b2cd7 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/goto_label.vv @@ -0,0 +1,33 @@ +fn f() {unsafe { + goto f1 // forward + goto f2 + f1: + goto a1 + _ = fn(){ + goto f1 + goto f2 + goto a1 + a1: + goto a1 + } + f2: + goto a1 + goto f1 // back + goto f2 +}} + +fn g() {unsafe { + goto g1 // forward + g1: + goto f1 + goto a1 + goto g1 // back + goto undefined +}} + +// implicit main +unsafe { + goto m1 + m1: + goto m1 +} diff --git a/v_windows/v/old/vlib/v/checker/tests/hex_lit_without_digit_err.out b/v_windows/v/old/vlib/v/checker/tests/hex_lit_without_digit_err.out new file mode 100644 index 0000000..f6407ba --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/hex_lit_without_digit_err.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/hex_lit_without_digit_err.vv:2:14: error: number part of this hexadecimal is not provided + 1 | fn main() { + 2 | println(0x) + | ^ + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/hex_lit_without_digit_err.vv b/v_windows/v/old/vlib/v/checker/tests/hex_lit_without_digit_err.vv new file mode 100644 index 0000000..7063035 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/hex_lit_without_digit_err.vv @@ -0,0 +1,3 @@ +fn main() { + println(0x) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/hex_lit_wrong_digit_err.out b/v_windows/v/old/vlib/v/checker/tests/hex_lit_wrong_digit_err.out new file mode 100644 index 0000000..16ebb52 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/hex_lit_wrong_digit_err.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/hex_lit_wrong_digit_err.vv:2:18: error: this hexadecimal number has unsuitable digit `g` + 1 | fn main() { + 2 | println(0x111ghi) + | ^ + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/hex_lit_wrong_digit_err.vv b/v_windows/v/old/vlib/v/checker/tests/hex_lit_wrong_digit_err.vv new file mode 100644 index 0000000..1b7b07d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/hex_lit_wrong_digit_err.vv @@ -0,0 +1,3 @@ +fn main() { + println(0x111ghi) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/hex_literal_overflow.out b/v_windows/v/old/vlib/v/checker/tests/hex_literal_overflow.out new file mode 100644 index 0000000..9475150 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/hex_literal_overflow.out @@ -0,0 +1,18 @@ +vlib/v/checker/tests/hex_literal_overflow.vv:1:7: error: hex character literal overflows string + 1 | a := '\x11ffff' + | ~~~~~~~~ + 2 | b := '\x20ffff' + 3 | c := '\x10fffff' +vlib/v/checker/tests/hex_literal_overflow.vv:2:7: error: hex character literal overflows string + 1 | a := '\x11ffff' + 2 | b := '\x20ffff' + | ~~~~~~~~ + 3 | c := '\x10fffff' + 4 | println(a) +vlib/v/checker/tests/hex_literal_overflow.vv:3:7: error: hex character literal overflows string + 1 | a := '\x11ffff' + 2 | b := '\x20ffff' + 3 | c := '\x10fffff' + | ~~~~~~~~~ + 4 | println(a) + 5 | println(b) diff --git a/v_windows/v/old/vlib/v/checker/tests/hex_literal_overflow.vv b/v_windows/v/old/vlib/v/checker/tests/hex_literal_overflow.vv new file mode 100644 index 0000000..60903f8 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/hex_literal_overflow.vv @@ -0,0 +1,6 @@ +a := '\x11ffff' +b := '\x20ffff' +c := '\x10fffff' +println(a) +println(b) +println(c) diff --git a/v_windows/v/old/vlib/v/checker/tests/ierror_in_return_tuple.out b/v_windows/v/old/vlib/v/checker/tests/ierror_in_return_tuple.out new file mode 100644 index 0000000..60d6483 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/ierror_in_return_tuple.out @@ -0,0 +1,12 @@ +vlib/v/checker/tests/ierror_in_return_tuple.vv:1:29: error: type `IError` cannot be used in multi-return, return an option instead + 1 | fn return_ierror_in_tuple() (bool, IError) { + | ~~~~~~~~~~~~~~ + 2 | return false, error('') + 3 | } +vlib/v/checker/tests/ierror_in_return_tuple.vv:5:29: error: option cannot be used in multi-return, return an option instead + 3 | } + 4 | + 5 | fn return_option_in_tuple() (bool, ?bool) { + | ~~~~~~~~~~~~~ + 6 | return false, error('') + 7 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/ierror_in_return_tuple.vv b/v_windows/v/old/vlib/v/checker/tests/ierror_in_return_tuple.vv new file mode 100644 index 0000000..4717528 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/ierror_in_return_tuple.vv @@ -0,0 +1,7 @@ +fn return_ierror_in_tuple() (bool, IError) { + return false, error('') +} + +fn return_option_in_tuple() (bool, ?bool) { + return false, error('') +} diff --git a/v_windows/v/old/vlib/v/checker/tests/if_expr_last_stmt.out b/v_windows/v/old/vlib/v/checker/tests/if_expr_last_stmt.out new file mode 100644 index 0000000..2fdeabc --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/if_expr_last_stmt.out @@ -0,0 +1,14 @@ +vlib/v/checker/tests/if_expr_last_stmt.vv:4:7: error: `if` expression requires an expression as the last statement of every branch + 2 | _ = if true { + 3 | 1 + 4 | } else if false { + | ~~~~~~~~~~~~~ + 5 | } else { + 6 | } +vlib/v/checker/tests/if_expr_last_stmt.vv:5:7: error: `if` expression requires an expression as the last statement of every branch + 3 | 1 + 4 | } else if false { + 5 | } else { + | ~~~~ + 6 | } + 7 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/if_expr_last_stmt.vv b/v_windows/v/old/vlib/v/checker/tests/if_expr_last_stmt.vv new file mode 100644 index 0000000..eb5dd0c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/if_expr_last_stmt.vv @@ -0,0 +1,7 @@ +fn main() { + _ = if true { + 1 + } else if false { + } else { + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/if_expr_mismatch.out b/v_windows/v/old/vlib/v/checker/tests/if_expr_mismatch.out new file mode 100644 index 0000000..0c45b88 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/if_expr_mismatch.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/if_expr_mismatch.vv:2:7: error: mismatched types `string` and `int literal` + 1 | fn main() { + 2 | s := if true { '12' } else { 12 } + | ~~ + 3 | println(s) + 4 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/if_expr_mismatch.vv b/v_windows/v/old/vlib/v/checker/tests/if_expr_mismatch.vv new file mode 100644 index 0000000..597135b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/if_expr_mismatch.vv @@ -0,0 +1,4 @@ +fn main() { + s := if true { '12' } else { 12 } + println(s) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/if_expr_no_else.out b/v_windows/v/old/vlib/v/checker/tests/if_expr_no_else.out new file mode 100644 index 0000000..87fb3e7 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/if_expr_no_else.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/if_expr_no_else.vv:2:9: error: `if` expression needs `else` clause + 1 | fn main() { + 2 | _ = if true { 1 } + | ~~ + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/if_expr_no_else.vv b/v_windows/v/old/vlib/v/checker/tests/if_expr_no_else.vv new file mode 100644 index 0000000..d416cc9 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/if_expr_no_else.vv @@ -0,0 +1,3 @@ +fn main() { + _ = if true { 1 } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/if_expr_optional_err.out b/v_windows/v/old/vlib/v/checker/tests/if_expr_optional_err.out new file mode 100644 index 0000000..9adbc4a --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/if_expr_optional_err.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/if_expr_optional_err.vv:7:5: error: non-bool type `?bool` used as if condition + 5 | fn main() { + 6 | + 7 | if get_bool() { + | ~~~~~~~~~~ + 8 | println("Using plain lists") + 9 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/if_expr_optional_err.vv b/v_windows/v/old/vlib/v/checker/tests/if_expr_optional_err.vv new file mode 100644 index 0000000..88d617d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/if_expr_optional_err.vv @@ -0,0 +1,10 @@ +fn get_bool() ?bool { + return true +} + +fn main() { + + if get_bool() { + println("Using plain lists") + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/if_match_expr.out b/v_windows/v/old/vlib/v/checker/tests/if_match_expr.out new file mode 100644 index 0000000..9c65976 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/if_match_expr.out @@ -0,0 +1,49 @@ +vlib/v/checker/tests/if_match_expr.vv:8:6: error: `if` expression branch has unsupported statement (`v.ast.ForStmt`) + 6 | if true {1} else {-1} // result + 7 | } else { + 8 | for {break} + | ^ + 9 | {} + 10 | match true {true {} else {}} // statement not expression +vlib/v/checker/tests/if_match_expr.vv:9:2: error: `if` expression branch has unsupported statement (`v.ast.Block`) + 7 | } else { + 8 | for {break} + 9 | {} + | ^ + 10 | match true {true {} else {}} // statement not expression + 11 | _ = match true {true {1} else {-1}} // OK +vlib/v/checker/tests/if_match_expr.vv:10:2: error: `if` expression branch has unsupported statement (`v.ast.MatchExpr`) + 8 | for {break} + 9 | {} + 10 | match true {true {} else {}} // statement not expression + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 11 | _ = match true {true {1} else {-1}} // OK + 12 | match true {true {1} else {-1}} // result +vlib/v/checker/tests/if_match_expr.vv:17:3: error: `match` expression branch has unsupported statement (`v.ast.IfExpr`) + 15 | _ = match true { + 16 | true { + 17 | if true {} // statement not expression + | ~~ + 18 | _ = if true {1} else {-1} // OK + 19 | if true {1} else {-1} // result +vlib/v/checker/tests/if_match_expr.vv:22:10: error: `match` expression branch has unsupported statement (`v.ast.AssertStmt`) + 20 | } + 21 | else { + 22 | assert true + | ~~~~ + 23 | match true {true {} else {}} // statement not expression + 24 | _ = match true {true {1} else {-1}} // OK +vlib/v/checker/tests/if_match_expr.vv:23:3: error: `match` expression branch has unsupported statement (`v.ast.MatchExpr`) + 21 | else { + 22 | assert true + 23 | match true {true {} else {}} // statement not expression + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 24 | _ = match true {true {1} else {-1}} // OK + 25 | match true {true {1} else {-1}} // result +vlib/v/checker/tests/if_match_expr.vv:25:3: error: return type mismatch, it should be `int` + 23 | match true {true {} else {}} // statement not expression + 24 | _ = match true {true {1} else {-1}} // OK + 25 | match true {true {1} else {-1}} // result + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 26 | } + 27 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/if_match_expr.vv b/v_windows/v/old/vlib/v/checker/tests/if_match_expr.vv new file mode 100644 index 0000000..655f25a --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/if_match_expr.vv @@ -0,0 +1,27 @@ +// only C expressions are allowed for each statement + +_ = if true { + if true {} // FIXME should error, if statement + _ = if true {1} else {-1} // OK + if true {1} else {-1} // result +} else { + for {break} + {} + match true {true {} else {}} // statement not expression + _ = match true {true {1} else {-1}} // OK + match true {true {1} else {-1}} // result +} + +_ = match true { + true { + if true {} // statement not expression + _ = if true {1} else {-1} // OK + if true {1} else {-1} // result + } + else { + assert true + match true {true {} else {}} // statement not expression + _ = match true {true {1} else {-1}} // OK + match true {true {1} else {-1}} // result + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/if_match_expr_err.out b/v_windows/v/old/vlib/v/checker/tests/if_match_expr_err.out new file mode 100644 index 0000000..2c16775 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/if_match_expr_err.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/if_match_expr_err.vv:3:8: error: cannot use `match` with `if` statements + 1 | fn main() { + 2 | a := 0 + 3 | if match a { + | ~~~~~ + 4 | 0 { + 5 | println('a is zero') diff --git a/v_windows/v/old/vlib/v/checker/tests/if_match_expr_err.vv b/v_windows/v/old/vlib/v/checker/tests/if_match_expr_err.vv new file mode 100644 index 0000000..511bbc7 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/if_match_expr_err.vv @@ -0,0 +1,13 @@ +fn main() { + a := 0 + if match a { + 0 { + println('a is zero') + } + else { + println('unreachable branch') + } + } 5 < 7 { + println('5 is less than 7') + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/if_match_result.out b/v_windows/v/old/vlib/v/checker/tests/if_match_result.out new file mode 100644 index 0000000..53a154e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/if_match_result.out @@ -0,0 +1,34 @@ +vlib/v/checker/tests/if_match_result.vv:2:3: error: assignment mismatch: 1 variable(s) 0 value(s) + 1 | // missing results + 2 | _ = match 4 { + | ^ + 3 | 1 {} + 4 | else {} +vlib/v/checker/tests/if_match_result.vv:6:5: error: `if` expression requires an expression as the last statement of every branch + 4 | else {} + 5 | } + 6 | _ = if true { + | ~~~~~~~ + 7 | } else { + 8 | } +vlib/v/checker/tests/if_match_result.vv:7:3: error: `if` expression requires an expression as the last statement of every branch + 5 | } + 6 | _ = if true { + 7 | } else { + | ~~~~ + 8 | } + 9 | +vlib/v/checker/tests/if_match_result.vv:11:3: error: assignment mismatch: 1 variable(s) 0 value(s) + 9 | + 10 | // void results + 11 | _ = match 4 { + | ^ + 12 | 1 {println('')} + 13 | else {exit(0)} +vlib/v/checker/tests/if_match_result.vv:15:3: error: assignment mismatch: 1 variable(s) 0 value(s) + 13 | else {exit(0)} + 14 | } + 15 | _ = if true { + | ^ + 16 | println('') + 17 | } else { diff --git a/v_windows/v/old/vlib/v/checker/tests/if_match_result.vv b/v_windows/v/old/vlib/v/checker/tests/if_match_result.vv new file mode 100644 index 0000000..ad85b13 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/if_match_result.vv @@ -0,0 +1,19 @@ +// missing results +_ = match 4 { + 1 {} + else {} +} +_ = if true { +} else { +} + +// void results +_ = match 4 { + 1 {println('')} + else {exit(0)} +} +_ = if true { + println('') +} else { + exit(0) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/if_non_bool_cond.out b/v_windows/v/old/vlib/v/checker/tests/if_non_bool_cond.out new file mode 100644 index 0000000..08c9765 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/if_non_bool_cond.out @@ -0,0 +1,20 @@ +vlib/v/checker/tests/if_non_bool_cond.vv:2:5: error: non-bool type `string` used as if condition + 1 | fn main() { + 2 | if '10' { + | ~~~~ + 3 | println('10') + 4 | } +vlib/v/checker/tests/if_non_bool_cond.vv:6:5: error: non-bool type `int literal` used as if condition + 4 | } + 5 | + 6 | if 5 { + | ^ + 7 | println(5) + 8 | } +vlib/v/checker/tests/if_non_bool_cond.vv:10:5: error: non-bool type `void` used as if condition + 8 | } + 9 | + 10 | if println('v') { + | ~~~~~~~~~~~~ + 11 | println('println') + 12 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/if_non_bool_cond.vv b/v_windows/v/old/vlib/v/checker/tests/if_non_bool_cond.vv new file mode 100644 index 0000000..626aafe --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/if_non_bool_cond.vv @@ -0,0 +1,13 @@ +fn main() { + if '10' { + println('10') + } + + if 5 { + println(5) + } + + if println('v') { + println('println') + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/immutable_arg.out b/v_windows/v/old/vlib/v/checker/tests/immutable_arg.out new file mode 100644 index 0000000..6f46625 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/immutable_arg.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/immutable_arg.vv:13:8: error: `a` is immutable, declare it with `mut` to make it mutable + 11 | fn main() { + 12 | a := St{e: 2} + 13 | f(mut a) + | ^ + 14 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/immutable_arg.vv b/v_windows/v/old/vlib/v/checker/tests/immutable_arg.vv new file mode 100644 index 0000000..08e799d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/immutable_arg.vv @@ -0,0 +1,14 @@ +struct St { +mut: + e int +} + +fn f(mut x St) { + x.e++ + println(x) +} + +fn main() { + a := St{e: 2} + f(mut a) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/immutable_array_field_assign.out b/v_windows/v/old/vlib/v/checker/tests/immutable_array_field_assign.out new file mode 100644 index 0000000..b091e67 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/immutable_array_field_assign.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/immutable_array_field_assign.vv:9:4: error: field `i` of struct `Aaa` is immutable + 7 | i: [0] + 8 | } + 9 | a.i[0] = 3 + | ^ + 10 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/immutable_array_field_assign.vv b/v_windows/v/old/vlib/v/checker/tests/immutable_array_field_assign.vv new file mode 100644 index 0000000..adadfd6 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/immutable_array_field_assign.vv @@ -0,0 +1,10 @@ +struct Aaa { + i []int +} + +fn main() { + mut a := Aaa{ + i: [0] + } + a.i[0] = 3 +} diff --git a/v_windows/v/old/vlib/v/checker/tests/immutable_array_field_shift.out b/v_windows/v/old/vlib/v/checker/tests/immutable_array_field_shift.out new file mode 100644 index 0000000..ec34ee2 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/immutable_array_field_shift.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/immutable_array_field_shift.vv:14:4: error: field `a` of struct `Bbb` is immutable + 12 | a: Aaa{} + 13 | } + 14 | b.a.i << 3 + | ^ + 15 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/immutable_array_field_shift.vv b/v_windows/v/old/vlib/v/checker/tests/immutable_array_field_shift.vv new file mode 100644 index 0000000..f04054b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/immutable_array_field_shift.vv @@ -0,0 +1,15 @@ +struct Aaa { +mut: + i []int +} + +struct Bbb { + a Aaa +} + +fn main() { + mut b := Bbb{ + a: Aaa{} + } + b.a.i << 3 +} diff --git a/v_windows/v/old/vlib/v/checker/tests/immutable_array_struct_assign.out b/v_windows/v/old/vlib/v/checker/tests/immutable_array_struct_assign.out new file mode 100644 index 0000000..3675fe2 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/immutable_array_struct_assign.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/immutable_array_struct_assign.vv:8:2: error: `a` is immutable, declare it with `mut` to make it mutable + 6 | fn main() { + 7 | a := Aaa{} + 8 | a.i[0] += 3 + | ^ + 9 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/immutable_array_struct_assign.vv b/v_windows/v/old/vlib/v/checker/tests/immutable_array_struct_assign.vv new file mode 100644 index 0000000..b150d9d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/immutable_array_struct_assign.vv @@ -0,0 +1,9 @@ +struct Aaa { +pub mut: + i []int +} + +fn main() { + a := Aaa{} + a.i[0] += 3 +} diff --git a/v_windows/v/old/vlib/v/checker/tests/immutable_array_struct_shift.out b/v_windows/v/old/vlib/v/checker/tests/immutable_array_struct_shift.out new file mode 100644 index 0000000..b9a25fc --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/immutable_array_struct_shift.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/immutable_array_struct_shift.vv:8:2: error: `a` is immutable, declare it with `mut` to make it mutable + 6 | fn main() { + 7 | a := []Aaa{} + 8 | a[0].i << 3 + | ^ + 9 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/immutable_array_struct_shift.vv b/v_windows/v/old/vlib/v/checker/tests/immutable_array_struct_shift.vv new file mode 100644 index 0000000..89e4d6c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/immutable_array_struct_shift.vv @@ -0,0 +1,9 @@ +struct Aaa { +pub mut: + i []int +} + +fn main() { + a := []Aaa{} + a[0].i << 3 +} diff --git a/v_windows/v/old/vlib/v/checker/tests/immutable_array_var.out b/v_windows/v/old/vlib/v/checker/tests/immutable_array_var.out new file mode 100644 index 0000000..6e45b41 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/immutable_array_var.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/immutable_array_var.vv:3:2: error: `a` is immutable, declare it with `mut` to make it mutable + 1 | fn main() { + 2 | a := [1, 2] + 3 | a << 3 + | ^ + 4 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/immutable_array_var.vv b/v_windows/v/old/vlib/v/checker/tests/immutable_array_var.vv new file mode 100644 index 0000000..1afa343 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/immutable_array_var.vv @@ -0,0 +1,4 @@ +fn main() { + a := [1, 2] + a << 3 +} diff --git a/v_windows/v/old/vlib/v/checker/tests/immutable_builtin_modify.out b/v_windows/v/old/vlib/v/checker/tests/immutable_builtin_modify.out new file mode 100644 index 0000000..059e3bc --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/immutable_builtin_modify.out @@ -0,0 +1,11 @@ +vlib/v/checker/tests/immutable_builtin_modify.vv:2:3: error: `string` can not be modified + 1 | s := '' + 2 | s.len = 123 + | ~~~ + 3 | // + 4 | b := []byte{} +vlib/v/checker/tests/immutable_builtin_modify.vv:5:3: error: `array` can not be modified + 3 | // + 4 | b := []byte{} + 5 | b.len = 34 + | ~~~ diff --git a/v_windows/v/old/vlib/v/checker/tests/immutable_builtin_modify.vv b/v_windows/v/old/vlib/v/checker/tests/immutable_builtin_modify.vv new file mode 100644 index 0000000..e6cb706 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/immutable_builtin_modify.vv @@ -0,0 +1,5 @@ +s := '' +s.len = 123 +// +b := []byte{} +b.len = 34 diff --git a/v_windows/v/old/vlib/v/checker/tests/immutable_field.out b/v_windows/v/old/vlib/v/checker/tests/immutable_field.out new file mode 100644 index 0000000..05b6214 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/immutable_field.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/immutable_field.vv:8:4: error: field `i1` of struct `Aaa` is immutable + 6 | fn main() { + 7 | a := Aaa{1} + 8 | a.i1 = 2 + | ~~ + 9 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/immutable_field.vv b/v_windows/v/old/vlib/v/checker/tests/immutable_field.vv new file mode 100644 index 0000000..487d913 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/immutable_field.vv @@ -0,0 +1,9 @@ +struct Aaa { +pub: + i1 int +} + +fn main() { + a := Aaa{1} + a.i1 = 2 +} diff --git a/v_windows/v/old/vlib/v/checker/tests/immutable_field_postfix.out b/v_windows/v/old/vlib/v/checker/tests/immutable_field_postfix.out new file mode 100644 index 0000000..fb2c4d8 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/immutable_field_postfix.out @@ -0,0 +1,13 @@ +vlib/v/checker/tests/immutable_field_postfix.vv:7:4: error: field `i` of struct `Aaa` is immutable + 5 | fn main() { + 6 | mut a := Aaa{} + 7 | a.i++ + | ^ + 8 | a.i-- + 9 | } +vlib/v/checker/tests/immutable_field_postfix.vv:8:4: error: field `i` of struct `Aaa` is immutable + 6 | mut a := Aaa{} + 7 | a.i++ + 8 | a.i-- + | ^ + 9 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/immutable_field_postfix.vv b/v_windows/v/old/vlib/v/checker/tests/immutable_field_postfix.vv new file mode 100644 index 0000000..0689c70 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/immutable_field_postfix.vv @@ -0,0 +1,9 @@ +struct Aaa { + i int +} + +fn main() { + mut a := Aaa{} + a.i++ + a.i-- +} diff --git a/v_windows/v/old/vlib/v/checker/tests/immutable_interface_field.out b/v_windows/v/old/vlib/v/checker/tests/immutable_interface_field.out new file mode 100644 index 0000000..0e421af --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/immutable_interface_field.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/immutable_interface_field.vv:10:4: error: field `i1` of interface `&Bbb` is immutable + 8 | + 9 | fn mutate_interface(mut b Bbb) { + 10 | b.i1 = 2 + | ~~ + 11 | } + 12 | \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/immutable_interface_field.vv b/v_windows/v/old/vlib/v/checker/tests/immutable_interface_field.vv new file mode 100644 index 0000000..c3aabef --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/immutable_interface_field.vv @@ -0,0 +1,16 @@ +struct Aaa { + i1 int +} + +interface Bbb { + i1 int +} + +fn mutate_interface(mut b Bbb) { + b.i1 = 2 +} + +fn main() { + mut a := Aaa{1} + mutate_interface(mut a) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/immutable_map.out b/v_windows/v/old/vlib/v/checker/tests/immutable_map.out new file mode 100644 index 0000000..3c5f7e3 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/immutable_map.out @@ -0,0 +1,27 @@ +vlib/v/checker/tests/immutable_map.vv:3:2: error: `m` is immutable, declare it with `mut` to make it mutable + 1 | fn main() { + 2 | m := map[string]int + 3 | m['test']++ + | ^ + 4 | m['test']-- + 5 | _ = m.move() +vlib/v/checker/tests/immutable_map.vv:4:2: error: `m` is immutable, declare it with `mut` to make it mutable + 2 | m := map[string]int + 3 | m['test']++ + 4 | m['test']-- + | ^ + 5 | _ = m.move() + 6 | m.delete('s') +vlib/v/checker/tests/immutable_map.vv:5:6: error: `m` is immutable, declare it with `mut` to make it mutable + 3 | m['test']++ + 4 | m['test']-- + 5 | _ = m.move() + | ^ + 6 | m.delete('s') + 7 | } +vlib/v/checker/tests/immutable_map.vv:6:2: error: `m` is immutable, declare it with `mut` to make it mutable + 4 | m['test']-- + 5 | _ = m.move() + 6 | m.delete('s') + | ^ + 7 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/immutable_map.vv b/v_windows/v/old/vlib/v/checker/tests/immutable_map.vv new file mode 100644 index 0000000..f3e0008 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/immutable_map.vv @@ -0,0 +1,7 @@ +fn main() { + m := map[string]int + m['test']++ + m['test']-- + _ = m.move() + m.delete('s') +} diff --git a/v_windows/v/old/vlib/v/checker/tests/immutable_rec.out b/v_windows/v/old/vlib/v/checker/tests/immutable_rec.out new file mode 100644 index 0000000..4c76ed4 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/immutable_rec.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/immutable_rec.vv:13:2: error: `a` is immutable, declare it with `mut` to make it mutable + 11 | fn main() { + 12 | a := St{e: 2} + 13 | a.f() + | ^ + 14 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/immutable_rec.vv b/v_windows/v/old/vlib/v/checker/tests/immutable_rec.vv new file mode 100644 index 0000000..acbc007 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/immutable_rec.vv @@ -0,0 +1,14 @@ +struct St { +mut: + e int +} + +fn (mut x St) f() { + x.e++ + println(x) +} + +fn main() { + a := St{e: 2} + a.f() +} diff --git a/v_windows/v/old/vlib/v/checker/tests/immutable_struct_postfix.out b/v_windows/v/old/vlib/v/checker/tests/immutable_struct_postfix.out new file mode 100644 index 0000000..86c87c2 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/immutable_struct_postfix.out @@ -0,0 +1,13 @@ +vlib/v/checker/tests/immutable_struct_postfix.vv:8:2: error: `a` is immutable, declare it with `mut` to make it mutable + 6 | fn main() { + 7 | a := Aaa{} + 8 | a.i++ + | ^ + 9 | a.i-- + 10 | } +vlib/v/checker/tests/immutable_struct_postfix.vv:9:2: error: `a` is immutable, declare it with `mut` to make it mutable + 7 | a := Aaa{} + 8 | a.i++ + 9 | a.i-- + | ^ + 10 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/immutable_struct_postfix.vv b/v_windows/v/old/vlib/v/checker/tests/immutable_struct_postfix.vv new file mode 100644 index 0000000..dbd3e78 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/immutable_struct_postfix.vv @@ -0,0 +1,10 @@ +struct Aaa { +mut: + i int +} + +fn main() { + a := Aaa{} + a.i++ + a.i-- +} diff --git a/v_windows/v/old/vlib/v/checker/tests/immutable_var.out b/v_windows/v/old/vlib/v/checker/tests/immutable_var.out new file mode 100644 index 0000000..012ef94 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/immutable_var.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/immutable_var.vv:3:2: error: `a` is immutable, declare it with `mut` to make it mutable + 1 | fn main() { + 2 | a := 1 + 3 | a = 2 + | ^ + 4 | _ = a + 5 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/immutable_var.vv b/v_windows/v/old/vlib/v/checker/tests/immutable_var.vv new file mode 100644 index 0000000..aeeaef1 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/immutable_var.vv @@ -0,0 +1,5 @@ +fn main() { + a := 1 + a = 2 + _ = a +} diff --git a/v_windows/v/old/vlib/v/checker/tests/immutable_var_postfix.out b/v_windows/v/old/vlib/v/checker/tests/immutable_var_postfix.out new file mode 100644 index 0000000..ca05696 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/immutable_var_postfix.out @@ -0,0 +1,13 @@ +vlib/v/checker/tests/immutable_var_postfix.vv:3:2: error: `a` is immutable, declare it with `mut` to make it mutable + 1 | fn main() { + 2 | a := 1 + 3 | a++ + | ^ + 4 | a-- + 5 | } +vlib/v/checker/tests/immutable_var_postfix.vv:4:2: error: `a` is immutable, declare it with `mut` to make it mutable + 2 | a := 1 + 3 | a++ + 4 | a-- + | ^ + 5 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/immutable_var_postfix.vv b/v_windows/v/old/vlib/v/checker/tests/immutable_var_postfix.vv new file mode 100644 index 0000000..ff1e94d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/immutable_var_postfix.vv @@ -0,0 +1,5 @@ +fn main() { + a := 1 + a++ + a-- +} diff --git a/v_windows/v/old/vlib/v/checker/tests/import_duplicate_err.out b/v_windows/v/old/vlib/v/checker/tests/import_duplicate_err.out new file mode 100644 index 0000000..3c04980 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/import_duplicate_err.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/import_duplicate_err.vv:2:8: error: `time` was already imported on line 1 + 1 | import time + 2 | import time + | ~~~~ + 3 | fn main() { + 4 | println(time.now().unix_time()) diff --git a/v_windows/v/old/vlib/v/checker/tests/import_duplicate_err.vv b/v_windows/v/old/vlib/v/checker/tests/import_duplicate_err.vv new file mode 100644 index 0000000..f0f7d8d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/import_duplicate_err.vv @@ -0,0 +1,5 @@ +import time +import time +fn main() { + println(time.now().unix_time()) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/import_middle_err.out b/v_windows/v/old/vlib/v/checker/tests/import_middle_err.out new file mode 100644 index 0000000..81c6639 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/import_middle_err.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/import_middle_err.vv:5:1: error: `import x` can only be declared at the beginning of the file + 3 | println('hello, world') + 4 | } + 5 | import os + | ~~~~~~ + 6 | fn main() { + 7 | println(time.now()) diff --git a/v_windows/v/old/vlib/v/checker/tests/import_middle_err.vv b/v_windows/v/old/vlib/v/checker/tests/import_middle_err.vv new file mode 100644 index 0000000..f38e462 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/import_middle_err.vv @@ -0,0 +1,8 @@ +import time +fn show() { + println('hello, world') +} +import os +fn main() { + println(time.now()) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/import_mod_as_mod_err.out b/v_windows/v/old/vlib/v/checker/tests/import_mod_as_mod_err.out new file mode 100644 index 0000000..5a5315b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/import_mod_as_mod_err.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/import_mod_as_mod_err.vv:1:16: error: import alias `math as math` is redundant + 1 | import math as math + | ~~~~ + 2 | + 3 | fn main() { diff --git a/v_windows/v/old/vlib/v/checker/tests/import_mod_as_mod_err.vv b/v_windows/v/old/vlib/v/checker/tests/import_mod_as_mod_err.vv new file mode 100644 index 0000000..2a12534 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/import_mod_as_mod_err.vv @@ -0,0 +1,5 @@ +import math as math + +fn main() { + println(math.e) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/import_mod_sub_as_sub_err.out b/v_windows/v/old/vlib/v/checker/tests/import_mod_sub_as_sub_err.out new file mode 100644 index 0000000..4caef12 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/import_mod_sub_as_sub_err.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/import_mod_sub_as_sub_err.vv:1:25: error: import alias `encoding.utf8 as utf8` is redundant + 1 | import encoding.utf8 as utf8 + | ~~~~ + 2 | + 3 | fn main() { diff --git a/v_windows/v/old/vlib/v/checker/tests/import_mod_sub_as_sub_err.vv b/v_windows/v/old/vlib/v/checker/tests/import_mod_sub_as_sub_err.vv new file mode 100644 index 0000000..81846bf --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/import_mod_sub_as_sub_err.vv @@ -0,0 +1,5 @@ +import encoding.utf8 as utf8 + +fn main() { + println(utf8.validate_str('añçá')) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/import_multiple_modules_err.out b/v_windows/v/old/vlib/v/checker/tests/import_multiple_modules_err.out new file mode 100644 index 0000000..7688794 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/import_multiple_modules_err.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/import_multiple_modules_err.vv:1:13: error: cannot import multiple modules at a time + 1 | import time math + | ~~~~ + 2 | fn main() { + 3 | println(time.now().unix_time()) diff --git a/v_windows/v/old/vlib/v/checker/tests/import_multiple_modules_err.vv b/v_windows/v/old/vlib/v/checker/tests/import_multiple_modules_err.vv new file mode 100644 index 0000000..d5d8033 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/import_multiple_modules_err.vv @@ -0,0 +1,4 @@ +import time math +fn main() { + println(time.now().unix_time()) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/import_not_found_err.out b/v_windows/v/old/vlib/v/checker/tests/import_not_found_err.out new file mode 100644 index 0000000..a41edfc --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/import_not_found_err.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/import_not_found_err.vv:1:1: builder error: cannot import module "notexist" (not found) + 1 | import notexist + | ~~~~~~~~~~~~~~~ + 2 | fn main() { + 3 | println(notexist.name) diff --git a/v_windows/v/old/vlib/v/checker/tests/import_not_found_err.vv b/v_windows/v/old/vlib/v/checker/tests/import_not_found_err.vv new file mode 100644 index 0000000..5e89bde --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/import_not_found_err.vv @@ -0,0 +1,4 @@ +import notexist +fn main() { + println(notexist.name) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/import_not_same_line_err.out b/v_windows/v/old/vlib/v/checker/tests/import_not_same_line_err.out new file mode 100644 index 0000000..14a935e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/import_not_same_line_err.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/import_not_same_line_err.vv:2:2: error: `import` statements must be a single line + 1 | import + 2 | time + | ~~~~ + 3 | fn main() { + 4 | println(time.now()) diff --git a/v_windows/v/old/vlib/v/checker/tests/import_not_same_line_err.vv b/v_windows/v/old/vlib/v/checker/tests/import_not_same_line_err.vv new file mode 100644 index 0000000..ad6401e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/import_not_same_line_err.vv @@ -0,0 +1,5 @@ +import + time +fn main() { + println(time.now()) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/import_symbol_empty.out b/v_windows/v/old/vlib/v/checker/tests/import_symbol_empty.out new file mode 100644 index 0000000..0592fed --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/import_symbol_empty.out @@ -0,0 +1,3 @@ +vlib/v/checker/tests/import_symbol_empty.vv:1:12: error: empty `os` import set, remove `{}` + 1 | import os {} + | ^ diff --git a/v_windows/v/old/vlib/v/checker/tests/import_symbol_empty.vv b/v_windows/v/old/vlib/v/checker/tests/import_symbol_empty.vv new file mode 100644 index 0000000..252aceb --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/import_symbol_empty.vv @@ -0,0 +1 @@ +import os {} diff --git a/v_windows/v/old/vlib/v/checker/tests/import_symbol_fn_err.out b/v_windows/v/old/vlib/v/checker/tests/import_symbol_fn_err.out new file mode 100644 index 0000000..5d74a05 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/import_symbol_fn_err.out @@ -0,0 +1,11 @@ +vlib/v/checker/tests/import_symbol_fn_err.vv:1:17: error: module `crypto` has no constant or function `userper` + 1 | import crypto { userper } + | ~~~~~~~ + 2 | fn main() { + 3 | usurper() +vlib/v/checker/tests/import_symbol_fn_err.vv:3:3: error: unknown function: usurper + 1 | import crypto { userper } + 2 | fn main() { + 3 | usurper() + | ~~~~~~~~~ + 4 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/import_symbol_fn_err.vv b/v_windows/v/old/vlib/v/checker/tests/import_symbol_fn_err.vv new file mode 100644 index 0000000..04ba99b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/import_symbol_fn_err.vv @@ -0,0 +1,4 @@ +import crypto { userper } +fn main() { + usurper() +} diff --git a/v_windows/v/old/vlib/v/checker/tests/import_symbol_invalid.out b/v_windows/v/old/vlib/v/checker/tests/import_symbol_invalid.out new file mode 100644 index 0000000..2193b15 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/import_symbol_invalid.out @@ -0,0 +1,3 @@ +vlib/v/checker/tests/import_symbol_invalid.vv:1:17: error: import syntax error, please specify a valid fn or type name + 1 | import crypto { *_v } + | ^ diff --git a/v_windows/v/old/vlib/v/checker/tests/import_symbol_invalid.vv b/v_windows/v/old/vlib/v/checker/tests/import_symbol_invalid.vv new file mode 100644 index 0000000..485c151 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/import_symbol_invalid.vv @@ -0,0 +1 @@ +import crypto { *_v } diff --git a/v_windows/v/old/vlib/v/checker/tests/import_symbol_private_err.out b/v_windows/v/old/vlib/v/checker/tests/import_symbol_private_err.out new file mode 100644 index 0000000..2d3d1e2 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/import_symbol_private_err.out @@ -0,0 +1,48 @@ +vlib/v/checker/tests/import_symbol_private_err.vv:3:20: error: module `time` function `since()` is private + 1 | import v.scanner + 2 | import v.parser + 3 | import time { now, since } + | ~~~~~ + 4 | import io { ReaderWriterImpl } + 5 | +vlib/v/checker/tests/import_symbol_private_err.vv:4:13: error: module `io` type `ReaderWriterImpl` is private + 2 | import v.parser + 3 | import time { now, since } + 4 | import io { ReaderWriterImpl } + | ~~~~~~~~~~~~~~~~ + 5 | + 6 | fn main() { +vlib/v/checker/tests/import_symbol_private_err.vv:7:18: error: constant `v.scanner.single_quote` is private + 5 | + 6 | fn main() { + 7 | println(scanner.single_quote) + | ~~~~~~~~~~~~ + 8 | println(parser.State.html) + 9 | since(now()) +vlib/v/checker/tests/import_symbol_private_err.vv:8:17: error: enum `v.parser.State` is private + 6 | fn main() { + 7 | println(scanner.single_quote) + 8 | println(parser.State.html) + | ~~~~~~~~~~ + 9 | since(now()) + 10 | _ = map{'h': 2}.exists('h') +vlib/v/checker/tests/import_symbol_private_err.vv:9:2: error: function `time.since` is private + 7 | println(scanner.single_quote) + 8 | println(parser.State.html) + 9 | since(now()) + | ~~~~~~~~~~~~ + 10 | _ = map{'h': 2}.exists('h') + 11 | _ = ReaderWriterImpl{} +vlib/v/checker/tests/import_symbol_private_err.vv:10:18: error: method `map[string]int.exists` is private + 8 | println(parser.State.html) + 9 | since(now()) + 10 | _ = map{'h': 2}.exists('h') + | ~~~~~~~~~~~ + 11 | _ = ReaderWriterImpl{} + 12 | } +vlib/v/checker/tests/import_symbol_private_err.vv:11:6: error: type `io.ReaderWriterImpl` is private + 9 | since(now()) + 10 | _ = map{'h': 2}.exists('h') + 11 | _ = ReaderWriterImpl{} + | ~~~~~~~~~~~~~~~~~~ + 12 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/import_symbol_private_err.vv b/v_windows/v/old/vlib/v/checker/tests/import_symbol_private_err.vv new file mode 100644 index 0000000..0957e86 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/import_symbol_private_err.vv @@ -0,0 +1,12 @@ +import v.scanner +import v.parser +import time { now, since } +import io { ReaderWriterImpl } + +fn main() { + println(scanner.single_quote) + println(parser.State.html) + since(now()) + _ = map{'h': 2}.exists('h') + _ = ReaderWriterImpl{} +} diff --git a/v_windows/v/old/vlib/v/checker/tests/import_symbol_type_err.out b/v_windows/v/old/vlib/v/checker/tests/import_symbol_type_err.out new file mode 100644 index 0000000..2c1cd9b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/import_symbol_type_err.out @@ -0,0 +1,12 @@ +vlib/v/checker/tests/import_symbol_type_err.vv:1:17: error: module `crypto` has no type `Coin` + 1 | import crypto { Coin } + | ~~~~ + 2 | fn main() { + 3 | println(Coin{}) +vlib/v/checker/tests/import_symbol_type_err.vv:3:11: error: unknown type `crypto.Coin`. +Did you mean `crypto.Hash`? + 1 | import crypto { Coin } + 2 | fn main() { + 3 | println(Coin{}) + | ~~~~~~ + 4 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/import_symbol_type_err.vv b/v_windows/v/old/vlib/v/checker/tests/import_symbol_type_err.vv new file mode 100644 index 0000000..7eccd97 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/import_symbol_type_err.vv @@ -0,0 +1,4 @@ +import crypto { Coin } +fn main() { + println(Coin{}) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/import_symbol_unclosed.out b/v_windows/v/old/vlib/v/checker/tests/import_symbol_unclosed.out new file mode 100644 index 0000000..2ab6a66 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/import_symbol_unclosed.out @@ -0,0 +1,3 @@ +vlib/v/checker/tests/import_symbol_unclosed.vv:1:28: error: import syntax error, no closing `}` + 1 | import crypto.sha256 { sum ] + | ^ diff --git a/v_windows/v/old/vlib/v/checker/tests/import_symbol_unclosed.vv b/v_windows/v/old/vlib/v/checker/tests/import_symbol_unclosed.vv new file mode 100644 index 0000000..799a636 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/import_symbol_unclosed.vv @@ -0,0 +1 @@ +import crypto.sha256 { sum ] diff --git a/v_windows/v/old/vlib/v/checker/tests/import_syntax_err.out b/v_windows/v/old/vlib/v/checker/tests/import_syntax_err.out new file mode 100644 index 0000000..06b3499 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/import_syntax_err.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/import_syntax_err.vv:1:12: error: cannot import multiple modules at a time + 1 | import time, os + | ^ + 2 | fn main() { + 3 | println(time.now()) diff --git a/v_windows/v/old/vlib/v/checker/tests/import_syntax_err.vv b/v_windows/v/old/vlib/v/checker/tests/import_syntax_err.vv new file mode 100644 index 0000000..2649ca6 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/import_syntax_err.vv @@ -0,0 +1,4 @@ +import time, os +fn main() { + println(time.now()) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/import_unused_warning.out b/v_windows/v/old/vlib/v/checker/tests/import_unused_warning.out new file mode 100644 index 0000000..38df8cd --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/import_unused_warning.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/import_unused_warning.vv:1:8: warning: module 'time' is imported but never used + 1 | import time + | ~~~~ + 2 | fn main() { + 3 | println('hello, world') diff --git a/v_windows/v/old/vlib/v/checker/tests/import_unused_warning.vv b/v_windows/v/old/vlib/v/checker/tests/import_unused_warning.vv new file mode 100644 index 0000000..1fb573f --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/import_unused_warning.vv @@ -0,0 +1,4 @@ +import time +fn main() { + println('hello, world') +} diff --git a/v_windows/v/old/vlib/v/checker/tests/in_mismatch_type.out b/v_windows/v/old/vlib/v/checker/tests/in_mismatch_type.out new file mode 100644 index 0000000..d6e9e12 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/in_mismatch_type.out @@ -0,0 +1,77 @@ +vlib/v/checker/tests/in_mismatch_type.vv:10:5: error: left operand to `in` does not match the array element type: expected `string`, not `int literal` + 8 | } + 9 | s := 'abcd' + 10 | if 1 in a_s { + | ~~~~~~~~ + 11 | println('ok') + 12 | } +vlib/v/checker/tests/in_mismatch_type.vv:13:5: error: left operand to `in` does not match the map key type: expected `string`, not `int literal` + 11 | println('ok') + 12 | } + 13 | if 2 in m { + | ~~~~~~ + 14 | println('yeah') + 15 | } +vlib/v/checker/tests/in_mismatch_type.vv:16:5: error: use `str.contains(substr)` instead of `substr in str` + 14 | println('yeah') + 15 | } + 16 | if 3 in s { + | ~~~~~~ + 17 | println('dope') + 18 | } +vlib/v/checker/tests/in_mismatch_type.vv:19:5: error: use `str.contains(substr)` instead of `substr in str` + 17 | println('dope') + 18 | } + 19 | if `a` in s { + | ~~~~~~~~ + 20 | println("oh no :'(") + 21 | } +vlib/v/checker/tests/in_mismatch_type.vv:22:7: error: `in` can only be used with an array/map/string + 20 | println("oh no :'(") + 21 | } + 22 | if 1 in 12 { + | ~~ + 23 | println('right') + 24 | } +vlib/v/checker/tests/in_mismatch_type.vv:25:5: error: left operand to `in` does not match the map key type: expected `string`, not `Int` + 23 | println('right') + 24 | } + 25 | if Int(2) in m { + | ~~~~~~~~~~~ + 26 | println('yeah') + 27 | } +vlib/v/checker/tests/in_mismatch_type.vv:28:5: error: left operand to `in` does not match the array element type: expected `int`, not `string` + 26 | println('yeah') + 27 | } + 28 | if '3' in a_i { + | ~~~~~~~~~~ + 29 | println('sure') + 30 | } +vlib/v/checker/tests/in_mismatch_type.vv:31:5: error: left operand to `in` does not match the array element type: expected `int`, not `string` + 29 | println('sure') + 30 | } + 31 | if '2' in a_i { + | ~~~~~~~~~~ + 32 | println('all right') + 33 | } +vlib/v/checker/tests/in_mismatch_type.vv:34:5: error: left operand to `!in` does not match the array element type: expected `string`, not `int literal` + 32 | println('all right') + 33 | } + 34 | if 1 !in a_s { + | ~~~~~~~~~ + 35 | println('ok') + 36 | } +vlib/v/checker/tests/in_mismatch_type.vv:37:5: error: left operand to `!in` does not match the array element type: expected `int`, not `string` + 35 | println('ok') + 36 | } + 37 | if '1' !in a_i { + | ~~~~~~~~~~~ + 38 | println('good') + 39 | } +vlib/v/checker/tests/in_mismatch_type.vv:41:5: error: left operand to `!in` does not match the map key type: expected `string`, not `int literal` + 39 | } + 40 | + 41 | if 5 !in m { + | ~~~~~~~ + 42 | println('yay') + 43 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/in_mismatch_type.vv b/v_windows/v/old/vlib/v/checker/tests/in_mismatch_type.vv new file mode 100644 index 0000000..4a5dc69 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/in_mismatch_type.vv @@ -0,0 +1,44 @@ +type Int = int + +fn main() { + a_i := [1, 2, 3] + a_s := ['1', '2', '3'] + m := map{ + 'test': 1 + } + s := 'abcd' + if 1 in a_s { + println('ok') + } + if 2 in m { + println('yeah') + } + if 3 in s { + println('dope') + } + if `a` in s { + println("oh no :'(") + } + if 1 in 12 { + println('right') + } + if Int(2) in m { + println('yeah') + } + if '3' in a_i { + println('sure') + } + if '2' in a_i { + println('all right') + } + if 1 !in a_s { + println('ok') + } + if '1' !in a_i { + println('good') + } + + if 5 !in m { + println('yay') + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/incorrect_for_in_name_variable.out b/v_windows/v/old/vlib/v/checker/tests/incorrect_for_in_name_variable.out new file mode 100644 index 0000000..610ef46 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/incorrect_for_in_name_variable.out @@ -0,0 +1,8 @@ +vlib/v/checker/tests/incorrect_for_in_name_variable.vv:3:6: error: variable name `_aa` cannot start with `_` + 1 | fn main() { + 2 | a := [1,2,3] + 3 | for _aa in a { + | ~~~ + 4 | println(_aa) + 5 | } + \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/incorrect_for_in_name_variable.vv b/v_windows/v/old/vlib/v/checker/tests/incorrect_for_in_name_variable.vv new file mode 100644 index 0000000..888fd60 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/incorrect_for_in_name_variable.vv @@ -0,0 +1,6 @@ +fn main() { + a := [1,2,3] + for _aa in a { + println(_aa) + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/incorrect_name_alias_type.out b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_alias_type.out new file mode 100644 index 0000000..820e8d5 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_alias_type.out @@ -0,0 +1,3 @@ +vlib/v/checker/tests/incorrect_name_alias_type.vv:1:1: error: type alias `integer` must begin with capital letter + 1 | type integer = int + | ~~~~~~~~~~~~ \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/incorrect_name_alias_type.vv b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_alias_type.vv new file mode 100644 index 0000000..7e2d363 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_alias_type.vv @@ -0,0 +1 @@ +type integer = int diff --git a/v_windows/v/old/vlib/v/checker/tests/incorrect_name_const.out b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_const.out new file mode 100644 index 0000000..e69de29 diff --git a/v_windows/v/old/vlib/v/checker/tests/incorrect_name_const.vv b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_const.vv new file mode 100644 index 0000000..50f553f --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_const.vv @@ -0,0 +1,3 @@ +const ( + _my_const = 0 +) diff --git a/v_windows/v/old/vlib/v/checker/tests/incorrect_name_enum.out b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_enum.out new file mode 100644 index 0000000..27a4392 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_enum.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/incorrect_name_enum.vv:1:1: error: enum name `color` must begin with capital letter + 1 | enum color { + | ~~~~~~~~~~ + 2 | green + 3 | yellow \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/incorrect_name_enum.vv b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_enum.vv new file mode 100644 index 0000000..90c4d4d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_enum.vv @@ -0,0 +1,4 @@ +enum color { + green + yellow +} diff --git a/v_windows/v/old/vlib/v/checker/tests/incorrect_name_enum_field.out b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_enum_field.out new file mode 100644 index 0000000..04f2748 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_enum_field.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/incorrect_name_enum_field.vv:2:5: error: field name `Green` cannot contain uppercase letters, use snake_case instead + 1 | enum Color { + 2 | Green + | ~~~~~ + 3 | red + 4 | blue \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/incorrect_name_enum_field.vv b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_enum_field.vv new file mode 100644 index 0000000..8cb0031 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_enum_field.vv @@ -0,0 +1,5 @@ +enum Color { + Green + red + blue +} diff --git a/v_windows/v/old/vlib/v/checker/tests/incorrect_name_fn_type.out b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_fn_type.out new file mode 100644 index 0000000..4ded743 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_fn_type.out @@ -0,0 +1,3 @@ +vlib/v/checker/tests/incorrect_name_fn_type.vv:1:1: error: fn type `callback` must begin with capital letter + 1 | type callback = fn () + | ~~~~~~~~~~~~~ diff --git a/v_windows/v/old/vlib/v/checker/tests/incorrect_name_fn_type.vv b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_fn_type.vv new file mode 100644 index 0000000..f565315 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_fn_type.vv @@ -0,0 +1 @@ +type callback = fn () diff --git a/v_windows/v/old/vlib/v/checker/tests/incorrect_name_function.out b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_function.out new file mode 100644 index 0000000..0826b36 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_function.out @@ -0,0 +1,3 @@ +vlib/v/checker/tests/incorrect_name_function.vv:1:1: error: function name `_my_fn` cannot start with `_` + 1 | fn _my_fn() {} + | ~~~~~~~~~~~ \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/incorrect_name_function.vv b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_function.vv new file mode 100644 index 0000000..b75a1a2 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_function.vv @@ -0,0 +1 @@ +fn _my_fn() {} diff --git a/v_windows/v/old/vlib/v/checker/tests/incorrect_name_interface.out b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_interface.out new file mode 100644 index 0000000..95a49b9 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_interface.out @@ -0,0 +1,3 @@ +vlib/v/checker/tests/incorrect_name_interface.vv:1:1: error: interface name `_MyInterface` must begin with capital letter + 1 | interface _MyInterface {} + | ~~~~~~~~~~~~~~~~~~~~~~~~~ \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/incorrect_name_interface.vv b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_interface.vv new file mode 100644 index 0000000..abe6cb8 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_interface.vv @@ -0,0 +1 @@ +interface _MyInterface {} diff --git a/v_windows/v/old/vlib/v/checker/tests/incorrect_name_interface_method.out b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_interface_method.out new file mode 100644 index 0000000..daeda3e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_interface_method.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/incorrect_name_interface_method.vv:2:5: error: method name `_speak` cannot start with `_` + 1 | interface MyInterface { + 2 | _speak() + | ~~~~~~~~ + 3 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/incorrect_name_interface_method.vv b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_interface_method.vv new file mode 100644 index 0000000..2ab12be --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_interface_method.vv @@ -0,0 +1,3 @@ +interface MyInterface { + _speak() +} diff --git a/v_windows/v/old/vlib/v/checker/tests/incorrect_name_module.out b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_module.out new file mode 100644 index 0000000..4edc08e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_module.out @@ -0,0 +1,3 @@ +vlib/v/checker/tests/incorrect_name_module.vv:1:1: error: module name `_A` cannot start with `_` + 1 | module _A + | ~~~~~~~~~ \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/incorrect_name_module.vv b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_module.vv new file mode 100644 index 0000000..50526ca --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_module.vv @@ -0,0 +1 @@ +module _A diff --git a/v_windows/v/old/vlib/v/checker/tests/incorrect_name_struct.out b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_struct.out new file mode 100644 index 0000000..03f1a93 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_struct.out @@ -0,0 +1,3 @@ +vlib/v/checker/tests/incorrect_name_struct.vv:1:8: error: struct name `abc` must begin with capital letter + 1 | struct abc {} + | ~~~ \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/incorrect_name_struct.vv b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_struct.vv new file mode 100644 index 0000000..0c77ebc --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_struct.vv @@ -0,0 +1 @@ +struct abc {} diff --git a/v_windows/v/old/vlib/v/checker/tests/incorrect_name_struct_field.out b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_struct_field.out new file mode 100644 index 0000000..22fddfc --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_struct_field.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/incorrect_name_struct_field.vv:2:5: error: field name `_a` cannot start with `_` + 1 | struct Abc { + 2 | _a int + | ~~~~~~ + 3 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/incorrect_name_struct_field.vv b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_struct_field.vv new file mode 100644 index 0000000..b1b2205 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_struct_field.vv @@ -0,0 +1,3 @@ +struct Abc { + _a int +} diff --git a/v_windows/v/old/vlib/v/checker/tests/incorrect_name_sum_type.out b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_sum_type.out new file mode 100644 index 0000000..91a0412 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_sum_type.out @@ -0,0 +1,12 @@ +vlib/v/checker/tests/incorrect_name_sum_type.vv:1:1: error: sum type `integer` must begin with capital letter + 1 | type integer = i8 | i16 | int | i64 + | ~~~~~~~~~~~~ + 2 | type Integer = i8 | i16 | int | i64 + 3 | +vlib/v/checker/tests/incorrect_name_sum_type.vv:4:1: error: method overrides built-in sum type method + 2 | type Integer = i8 | i16 | int | i64 + 3 | + 4 | fn (i Integer) type_name() { + | ~~~~~~~~~~~~~~~~~~~~~~~~~~ + 5 | } + 6 | diff --git a/v_windows/v/old/vlib/v/checker/tests/incorrect_name_sum_type.vv b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_sum_type.vv new file mode 100644 index 0000000..e73300d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_sum_type.vv @@ -0,0 +1,6 @@ +type integer = i8 | i16 | int | i64 +type Integer = i8 | i16 | int | i64 + +fn (i Integer) type_name() { +} + diff --git a/v_windows/v/old/vlib/v/checker/tests/incorrect_name_variable.out b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_variable.out new file mode 100644 index 0000000..7a3de82 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_variable.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/incorrect_name_variable.vv:2:2: error: variable name `_abc` cannot start with `_` + 1 | fn main() { + 2 | _abc := 1 + | ~~~~ + 3 | _ = _abc + 4 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/incorrect_name_variable.vv b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_variable.vv new file mode 100644 index 0000000..d4d4295 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/incorrect_name_variable.vv @@ -0,0 +1,4 @@ +fn main() { + _abc := 1 + _ = _abc +} diff --git a/v_windows/v/old/vlib/v/checker/tests/index_expr.out b/v_windows/v/old/vlib/v/checker/tests/index_expr.out new file mode 100644 index 0000000..90aa0a2 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/index_expr.out @@ -0,0 +1,69 @@ +vlib/v/checker/tests/index_expr.vv:3:7: error: type `int` does not support indexing + 1 | fn test_invalid_index() { + 2 | v := 4 + 3 | _ = v[0] + | ~~~ + 4 | + 5 | a := [2] +vlib/v/checker/tests/index_expr.vv:6:7: error: non-integer index `[]int` (array type `[]int`) + 4 | + 5 | a := [2] + 6 | _ = a[a] + | ~~~ + 7 | _ = a[-1] + 8 | } +vlib/v/checker/tests/index_expr.vv:7:8: error: negative index `-1` + 5 | a := [2] + 6 | _ = a[a] + 7 | _ = a[-1] + | ~~ + 8 | } + 9 | +vlib/v/checker/tests/index_expr.vv:12:7: error: type `int` does not support indexing + 10 | fn test_invalid_slice() { + 11 | v := 4 + 12 | _ = v[1..] + | ~~~~~ + 13 | _ = v[..1] + 14 | +vlib/v/checker/tests/index_expr.vv:13:7: error: type `int` does not support indexing + 11 | v := 4 + 12 | _ = v[1..] + 13 | _ = v[..1] + | ~~~~~ + 14 | + 15 | a := [2] +vlib/v/checker/tests/index_expr.vv:16:7: error: non-integer index `[]int` (array type `[]int`) + 14 | + 15 | a := [2] + 16 | _ = a[a..] + | ~~~~~ + 17 | _ = a[..a] + 18 | _ = a[-1..] +vlib/v/checker/tests/index_expr.vv:17:7: error: non-integer index `[]int` (array type `[]int`) + 15 | a := [2] + 16 | _ = a[a..] + 17 | _ = a[..a] + | ~~~~~ + 18 | _ = a[-1..] + 19 | _ = a[..-1] +vlib/v/checker/tests/index_expr.vv:18:8: error: negative index `-1` + 16 | _ = a[a..] + 17 | _ = a[..a] + 18 | _ = a[-1..] + | ~~ + 19 | _ = a[..-1] + 20 | _ = a[-1..-2] +vlib/v/checker/tests/index_expr.vv:19:10: error: negative index `-1` + 17 | _ = a[..a] + 18 | _ = a[-1..] + 19 | _ = a[..-1] + | ~~ + 20 | _ = a[-1..-2] + 21 | } +vlib/v/checker/tests/index_expr.vv:20:8: error: negative index `-1` + 18 | _ = a[-1..] + 19 | _ = a[..-1] + 20 | _ = a[-1..-2] + | ~~ + 21 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/index_expr.vv b/v_windows/v/old/vlib/v/checker/tests/index_expr.vv new file mode 100644 index 0000000..072661d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/index_expr.vv @@ -0,0 +1,21 @@ +fn test_invalid_index() { + v := 4 + _ = v[0] + + a := [2] + _ = a[a] + _ = a[-1] +} + +fn test_invalid_slice() { + v := 4 + _ = v[1..] + _ = v[..1] + + a := [2] + _ = a[a..] + _ = a[..a] + _ = a[-1..] + _ = a[..-1] + _ = a[-1..-2] +} diff --git a/v_windows/v/old/vlib/v/checker/tests/infix_err.out b/v_windows/v/old/vlib/v/checker/tests/infix_err.out new file mode 100644 index 0000000..3637169 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/infix_err.out @@ -0,0 +1,81 @@ +vlib/v/checker/tests/infix_err.vv:7:5: error: mismatched types `string` and `?string` + 5 | return none + 6 | } + 7 | _ = '' + f() + | ~~~~~~~~ + 8 | _ = f() + '' + 9 | _ = f() + f() +vlib/v/checker/tests/infix_err.vv:8:5: error: mismatched types `?string` and `string` + 6 | } + 7 | _ = '' + f() + 8 | _ = f() + '' + | ~~~~~~~~ + 9 | _ = f() + f() + 10 | +vlib/v/checker/tests/infix_err.vv:9:9: error: `+` cannot be used with `?string` + 7 | _ = '' + f() + 8 | _ = f() + '' + 9 | _ = f() + f() + | ^ + 10 | + 11 | _ = 4 + g() +vlib/v/checker/tests/infix_err.vv:11:7: error: `+` cannot be used with `?int` + 9 | _ = f() + f() + 10 | + 11 | _ = 4 + g() + | ^ + 12 | _ = int(0) + g() // FIXME not detected + 13 | _ = g() + int(3) +vlib/v/checker/tests/infix_err.vv:12:5: error: unwrapped optional cannot be used in an infix expression + 10 | + 11 | _ = 4 + g() + 12 | _ = int(0) + g() // FIXME not detected + | ~~~~~~~~~~~~ + 13 | _ = g() + int(3) + 14 | _ = g() + 3 +vlib/v/checker/tests/infix_err.vv:13:9: error: `+` cannot be used with `?int` + 11 | _ = 4 + g() + 12 | _ = int(0) + g() // FIXME not detected + 13 | _ = g() + int(3) + | ^ + 14 | _ = g() + 3 + 15 | +vlib/v/checker/tests/infix_err.vv:14:9: error: `+` cannot be used with `?int` + 12 | _ = int(0) + g() // FIXME not detected + 13 | _ = g() + int(3) + 14 | _ = g() + 3 + | ^ + 15 | + 16 | // binary operands +vlib/v/checker/tests/infix_err.vv:17:5: error: left operand for `&&` is not a boolean + 15 | + 16 | // binary operands + 17 | _ = 1 && 2 + | ^ + 18 | _ = true || 2 + 19 | +vlib/v/checker/tests/infix_err.vv:18:13: error: right operand for `||` is not a boolean + 16 | // binary operands + 17 | _ = 1 && 2 + 18 | _ = true || 2 + | ^ + 19 | + 20 | // boolean expressions +vlib/v/checker/tests/infix_err.vv:21:22: error: ambiguous boolean expression. use `()` to ensure correct order of operations + 19 | + 20 | // boolean expressions + 21 | _ = 1 == 1 && 2 == 2 || 3 == 3 + | ~~ + 22 | _ = 1 == 1 + 23 | && 2 == 2 || 3 == 3 +vlib/v/checker/tests/infix_err.vv:23:12: error: ambiguous boolean expression. use `()` to ensure correct order of operations + 21 | _ = 1 == 1 && 2 == 2 || 3 == 3 + 22 | _ = 1 == 1 + 23 | && 2 == 2 || 3 == 3 + | ~~ + 24 | && 4 == 4 +vlib/v/checker/tests/infix_err.vv:24:2: error: ambiguous boolean expression. use `()` to ensure correct order of operations + 22 | _ = 1 == 1 + 23 | && 2 == 2 || 3 == 3 + 24 | && 4 == 4 + | ~~ diff --git a/v_windows/v/old/vlib/v/checker/tests/infix_err.vv b/v_windows/v/old/vlib/v/checker/tests/infix_err.vv new file mode 100644 index 0000000..13381b4 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/infix_err.vv @@ -0,0 +1,24 @@ +fn f() ?string { + return none +} +fn g() ?int { + return none +} +_ = '' + f() +_ = f() + '' +_ = f() + f() + +_ = 4 + g() +_ = int(0) + g() // FIXME not detected +_ = g() + int(3) +_ = g() + 3 + +// binary operands +_ = 1 && 2 +_ = true || 2 + +// boolean expressions +_ = 1 == 1 && 2 == 2 || 3 == 3 +_ = 1 == 1 + && 2 == 2 || 3 == 3 + && 4 == 4 diff --git a/v_windows/v/old/vlib/v/checker/tests/int_modulo_by_zero_err.out b/v_windows/v/old/vlib/v/checker/tests/int_modulo_by_zero_err.out new file mode 100644 index 0000000..c9d0e2d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/int_modulo_by_zero_err.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/int_modulo_by_zero_err.vv:2:17: error: modulo by zero + 1 | fn main() { + 2 | println(3 % 0) + | ^ + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/int_modulo_by_zero_err.vv b/v_windows/v/old/vlib/v/checker/tests/int_modulo_by_zero_err.vv new file mode 100644 index 0000000..8945861 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/int_modulo_by_zero_err.vv @@ -0,0 +1,3 @@ +fn main() { + println(3 % 0) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/interface_implementing_interface.out b/v_windows/v/old/vlib/v/checker/tests/interface_implementing_interface.out new file mode 100644 index 0000000..b2476ce --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/interface_implementing_interface.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/interface_implementing_interface.vv:15:10: error: cannot implement interface `Thing` with a different interface `Animal` + 13 | dog := Dog{} + 14 | animal := Animal(dog) + 15 | thing := Thing(animal) + | ~~~~~~~~~~~~~ + 16 | println(thing) \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/interface_implementing_interface.vv b/v_windows/v/old/vlib/v/checker/tests/interface_implementing_interface.vv new file mode 100644 index 0000000..a71e9ea --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/interface_implementing_interface.vv @@ -0,0 +1,16 @@ +interface Thing { + kind string +} + +interface Animal { + kind string +} + +struct Dog { + kind string = 'labrador' +} + +dog := Dog{} +animal := Animal(dog) +thing := Thing(animal) +println(thing) diff --git a/v_windows/v/old/vlib/v/checker/tests/interface_implementing_own_interface_method.out b/v_windows/v/old/vlib/v/checker/tests/interface_implementing_own_interface_method.out new file mode 100644 index 0000000..afa4876 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/interface_implementing_own_interface_method.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/interface_implementing_own_interface_method.vv:5:1: error: interface `Animal` cannot implement its own interface method `speak` + 3 | } + 4 | + 5 | fn (a Animal) speak() { + | ~~~~~~~~~~~~~~~~~~~~~ + 6 | println('speaking') + 7 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/interface_implementing_own_interface_method.vv b/v_windows/v/old/vlib/v/checker/tests/interface_implementing_own_interface_method.vv new file mode 100644 index 0000000..9881bd7 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/interface_implementing_own_interface_method.vv @@ -0,0 +1,7 @@ +interface Animal { + speak() +} + +fn (a Animal) speak() { + println('speaking') +} \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/interface_init_err.out b/v_windows/v/old/vlib/v/checker/tests/interface_init_err.out new file mode 100644 index 0000000..238395c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/interface_init_err.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/interface_init_err.vv:15:7: error: interface field `Server.handler` must be initialized + 13 | + 14 | fn main() { + 15 | _ := Server{} + | ~~~~~~~~ + 16 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/interface_init_err.vv b/v_windows/v/old/vlib/v/checker/tests/interface_init_err.vv new file mode 100644 index 0000000..e2def73 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/interface_init_err.vv @@ -0,0 +1,16 @@ +interface Handler { + foo string + handle(int) int +} + +struct Server { + handler Handler +} + +fn (s Server) handle(x int) int { + return x +} + +fn main() { + _ := Server{} +} diff --git a/v_windows/v/old/vlib/v/checker/tests/interface_return_parameter_err.out b/v_windows/v/old/vlib/v/checker/tests/interface_return_parameter_err.out new file mode 100644 index 0000000..ac3f23d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/interface_return_parameter_err.out @@ -0,0 +1,14 @@ +vlib/v/checker/tests/interface_return_parameter_err.vv:2:17: error: unknown type `Baz`. +Did you mean `Foo`? + 1 | interface Foo { + 2 | bar(string) []Baz + | ~~~~~ + 3 | bar2(Bax) string + 4 | } +vlib/v/checker/tests/interface_return_parameter_err.vv:3:10: error: unknown type `Bax`. +Did you mean `Foo`? + 1 | interface Foo { + 2 | bar(string) []Baz + 3 | bar2(Bax) string + | ~~~ + 4 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/interface_return_parameter_err.vv b/v_windows/v/old/vlib/v/checker/tests/interface_return_parameter_err.vv new file mode 100644 index 0000000..e6168cf --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/interface_return_parameter_err.vv @@ -0,0 +1,4 @@ +interface Foo { + bar(string) []Baz + bar2(Bax) string +} diff --git a/v_windows/v/old/vlib/v/checker/tests/interface_too_many_embedding_levels.out b/v_windows/v/old/vlib/v/checker/tests/interface_too_many_embedding_levels.out new file mode 100644 index 0000000..669fb01 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/interface_too_many_embedding_levels.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/interface_too_many_embedding_levels.vv:9:1: error: too many interface embedding levels: 101, for interface `I103` + 7 | } + 8 | + 9 | interface I103 { + | ~~~~~~~~~~~~~~~~ + 10 | I102 + 11 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/interface_too_many_embedding_levels.vv b/v_windows/v/old/vlib/v/checker/tests/interface_too_many_embedding_levels.vv new file mode 100644 index 0000000..6975ef0 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/interface_too_many_embedding_levels.vv @@ -0,0 +1,431 @@ +interface I1 { + I0 +} + +interface I2 { + I1 +} + +interface I103 { + I102 +} + +interface I102 { + I101 +} + +interface I101 { + I100 +} + +interface I3 { + I2 +} + +interface I4 { + I3 +} + +interface I5 { + I4 +} + +interface I6 { + I5 +} + +interface I7 { + I6 +} + +interface I8 { + I7 +} + +interface I9 { + I8 +} + +interface I10 { + I9 +} + +interface I11 { + I10 +} + +interface I12 { + I11 +} + +interface I13 { + I12 +} + +interface I14 { + I13 +} + +interface I15 { + I14 +} + +interface I16 { + I15 +} + +interface I17 { + I16 +} + +interface I18 { + I17 +} + +interface I19 { + I18 +} + +interface I20 { + I19 +} + +interface I21 { + I20 +} + +interface I22 { + I21 +} + +interface I23 { + I22 +} + +interface I24 { + I23 +} + +interface I25 { + I24 +} + +interface I26 { + I25 +} + +interface I27 { + I26 +} + +interface I28 { + I27 +} + +interface I29 { + I28 +} + +interface I30 { + I29 +} + +interface I31 { + I30 +} + +interface I32 { + I31 +} + +interface I33 { + I32 +} + +interface I34 { + I33 +} + +interface I35 { + I34 +} + +interface I36 { + I35 +} + +interface I37 { + I36 +} + +interface I38 { + I37 +} + +interface I39 { + I38 +} + +interface I40 { + I39 +} + +interface I41 { + I40 +} + +interface I42 { + I41 +} + +interface I43 { + I42 +} + +interface I44 { + I43 +} + +interface I45 { + I44 +} + +interface I46 { + I45 +} + +interface I47 { + I46 +} + +interface I48 { + I47 +} + +interface I49 { + I48 +} + +interface I50 { + I49 +} + +interface I51 { + I50 +} + +interface I52 { + I51 +} + +interface I53 { + I52 +} + +interface I54 { + I53 +} + +interface I55 { + I54 +} + +interface I56 { + I55 +} + +interface I57 { + I56 +} + +interface I58 { + I57 +} + +interface I59 { + I58 +} + +interface I60 { + I59 +} + +interface I61 { + I60 +} + +interface I62 { + I61 +} + +interface I63 { + I62 +} + +interface I64 { + I63 +} + +interface I65 { + I64 +} + +interface I66 { + I65 +} + +interface I67 { + I66 +} + +interface I68 { + I67 +} + +interface I69 { + I68 +} + +interface I70 { + I69 +} + +interface I71 { + I70 +} + +interface I72 { + I71 +} + +interface I73 { + I72 +} + +interface I74 { + I73 +} + +interface I75 { + I74 +} + +interface I76 { + I75 +} + +interface I77 { + I76 +} + +interface I78 { + I77 +} + +interface I79 { + I78 +} + +interface I80 { + I79 +} + +interface I81 { + I80 +} + +interface I82 { + I81 +} + +interface I83 { + I82 +} + +interface I84 { + I83 +} + +interface I85 { + I84 +} + +interface I86 { + I85 +} + +interface I87 { + I86 +} + +interface I88 { + I87 +} + +interface I89 { + I88 +} + +interface I90 { + I89 +} + +interface I91 { + I90 +} + +interface I92 { + I91 +} + +interface I93 { + I92 +} + +interface I94 { + I93 +} + +interface I95 { + I94 +} + +interface I96 { + I95 +} + +interface I97 { + I96 +} + +interface I98 { + I97 +} + +interface I99 { + I98 +} + +interface I100 { + I99 +} + +interface I0 { + m999() int +} + +struct Abc { + x int = 123 +} + +fn (s Abc) m999() int { + return 999 +} + +fn main() { + a := Abc{} + dump(a) + i := I103(a) + dump(i) + assert i.m999() == 999 +} diff --git a/v_windows/v/old/vlib/v/checker/tests/interpolation_recursive_str_err.out b/v_windows/v/old/vlib/v/checker/tests/interpolation_recursive_str_err.out new file mode 100644 index 0000000..c47f72f --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/interpolation_recursive_str_err.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/interpolation_recursive_str_err.vv:8:8: error: cannot call `str()` method recursively + 6 | + 7 | fn (t Test) str() string { + 8 | _ = '$t' + | ^ + 9 | return 'test' + 10 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/interpolation_recursive_str_err.vv b/v_windows/v/old/vlib/v/checker/tests/interpolation_recursive_str_err.vv new file mode 100644 index 0000000..4f3f3d8 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/interpolation_recursive_str_err.vv @@ -0,0 +1,15 @@ +module main + +struct Test { + a int +} + +fn (t Test) str() string { + _ = '$t' + return 'test' +} + +fn main() { + a := Test{} + println(a) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/invalid_char_err.out b/v_windows/v/old/vlib/v/checker/tests/invalid_char_err.out new file mode 100644 index 0000000..e4a2b5c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/invalid_char_err.out @@ -0,0 +1,3 @@ +vlib/v/checker/tests/invalid_char_err.vv:1:1: error: invalid character `🐈` + 1 | 🐈println('') + | ^ \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/invalid_char_err.vv b/v_windows/v/old/vlib/v/checker/tests/invalid_char_err.vv new file mode 100644 index 0000000..0a27589 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/invalid_char_err.vv @@ -0,0 +1 @@ +🐈println('') diff --git a/v_windows/v/old/vlib/v/checker/tests/invalid_property.out b/v_windows/v/old/vlib/v/checker/tests/invalid_property.out new file mode 100644 index 0000000..e7839a4 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/invalid_property.out @@ -0,0 +1,18 @@ +vlib/v/checker/tests/invalid_property.vv:2:7: error: `string` has no property `length` + 1 | s :='' + 2 | _ = s.length + | ~~~~~~ + 3 | _ = [1,2].foo + 4 | +vlib/v/checker/tests/invalid_property.vv:3:11: error: `[]int` has no property `foo` + 1 | s :='' + 2 | _ = s.length + 3 | _ = [1,2].foo + | ~~~ + 4 | + 5 | mut fa := [3,4]! +vlib/v/checker/tests/invalid_property.vv:6:8: error: `[2]int` has no property `bar` + 4 | + 5 | mut fa := [3,4]! + 6 | _ = fa.bar + | ~~~ diff --git a/v_windows/v/old/vlib/v/checker/tests/invalid_property.vv b/v_windows/v/old/vlib/v/checker/tests/invalid_property.vv new file mode 100644 index 0000000..427c2e7 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/invalid_property.vv @@ -0,0 +1,6 @@ +s :='' +_ = s.length +_ = [1,2].foo + +mut fa := [3,4]! +_ = fa.bar diff --git a/v_windows/v/old/vlib/v/checker/tests/invalid_vweb_param_type.out b/v_windows/v/old/vlib/v/checker/tests/invalid_vweb_param_type.out new file mode 100644 index 0000000..7333400 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/invalid_vweb_param_type.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/invalid_vweb_param_type.vv:8:24: error: invalid type `[]bool` for parameter `list` in vweb app method `index` + 6 | + 7 | ['/:list'; get] + 8 | fn (mut app App) index(list []bool) vweb.Result { + | ~~~~ + 9 | return app.text('') + 10 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/invalid_vweb_param_type.vv b/v_windows/v/old/vlib/v/checker/tests/invalid_vweb_param_type.vv new file mode 100644 index 0000000..6274830 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/invalid_vweb_param_type.vv @@ -0,0 +1,12 @@ +import vweb + +struct App { + vweb.Context +} + +['/:list'; get] +fn (mut app App) index(list []bool) vweb.Result { + return app.text('') +} + +vweb.run(&App{}, 5000) diff --git a/v_windows/v/old/vlib/v/checker/tests/invert_other_types_bits_error.out b/v_windows/v/old/vlib/v/checker/tests/invert_other_types_bits_error.out new file mode 100644 index 0000000..ad8c86f --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/invert_other_types_bits_error.out @@ -0,0 +1,26 @@ +vlib/v/checker/tests/invert_other_types_bits_error.vv:2:13: error: operator ~ only defined on int types + 1 | fn main() { + 2 | println(~3.0) + | ^ + 3 | println(~10.5) + 4 | println(~'2') +vlib/v/checker/tests/invert_other_types_bits_error.vv:3:13: error: operator ~ only defined on int types + 1 | fn main() { + 2 | println(~3.0) + 3 | println(~10.5) + | ^ + 4 | println(~'2') + 5 | println(~[2, 4, 6]) +vlib/v/checker/tests/invert_other_types_bits_error.vv:4:13: error: operator ~ only defined on int types + 2 | println(~3.0) + 3 | println(~10.5) + 4 | println(~'2') + | ^ + 5 | println(~[2, 4, 6]) + 6 | } +vlib/v/checker/tests/invert_other_types_bits_error.vv:5:13: error: operator ~ only defined on int types + 3 | println(~10.5) + 4 | println(~'2') + 5 | println(~[2, 4, 6]) + | ^ + 6 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/invert_other_types_bits_error.vv b/v_windows/v/old/vlib/v/checker/tests/invert_other_types_bits_error.vv new file mode 100644 index 0000000..ad74f64 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/invert_other_types_bits_error.vv @@ -0,0 +1,6 @@ +fn main() { + println(~3.0) + println(~10.5) + println(~'2') + println(~[2, 4, 6]) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/is_type_invalid.out b/v_windows/v/old/vlib/v/checker/tests/is_type_invalid.out new file mode 100644 index 0000000..0459e96 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/is_type_invalid.out @@ -0,0 +1,14 @@ +vlib/v/checker/tests/is_type_invalid.vv:14:12: error: `IoS` has no variant `byte` + 12 | + 13 | fn main() { + 14 | if IoS(1) is byte { + | ~~ + 15 | println('not cool') + 16 | } +vlib/v/checker/tests/is_type_invalid.vv:18:7: error: `Cat` doesn't implement method `speak` of interface `Animal` + 16 | } + 17 | a := Animal(Dog{}) + 18 | if a is Cat { + | ~~ + 19 | println('not cool either') + 20 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/is_type_invalid.vv b/v_windows/v/old/vlib/v/checker/tests/is_type_invalid.vv new file mode 100644 index 0000000..2e7f774 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/is_type_invalid.vv @@ -0,0 +1,21 @@ +type IoS = int | string + +interface Animal { + speak() +} + +struct Dog {} + +fn (d Dog) speak() {} + +struct Cat {} + +fn main() { + if IoS(1) is byte { + println('not cool') + } + a := Animal(Dog{}) + if a is Cat { + println('not cool either') + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/is_type_not_exist.out b/v_windows/v/old/vlib/v/checker/tests/is_type_not_exist.out new file mode 100644 index 0000000..1c52d7a --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/is_type_not_exist.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/is_type_not_exist.vv:8:10: error: is: type `SomethingThatDontExist` does not exist + 6 | + 7 | fn fn_with_sum_type_param(i Integer) { + 8 | if i is SomethingThatDontExist { + | ~~~~~~~~~~~~~~~~~~~~~~ + 9 | println('It should fail !') + 10 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/is_type_not_exist.vv b/v_windows/v/old/vlib/v/checker/tests/is_type_not_exist.vv new file mode 100644 index 0000000..e50859b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/is_type_not_exist.vv @@ -0,0 +1,11 @@ +type Integer = i8 | i16 | int | i64 + +fn main() { + fn_with_sum_type_param(1) +} + +fn fn_with_sum_type_param(i Integer) { + if i is SomethingThatDontExist { + println('It should fail !') + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/labelled_break_continue.out b/v_windows/v/old/vlib/v/checker/tests/labelled_break_continue.out new file mode 100644 index 0000000..caadcfc --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/labelled_break_continue.out @@ -0,0 +1,35 @@ +vlib/v/checker/tests/labelled_break_continue.vv:7:14: error: invalid label name `L2` + 5 | i++ + 6 | for { + 7 | if i < 7 {continue L2} + | ~~~~~~~~ + 8 | else {break L2} + 9 | } +vlib/v/checker/tests/labelled_break_continue.vv:8:10: error: invalid label name `L2` + 6 | for { + 7 | if i < 7 {continue L2} + 8 | else {break L2} + | ~~~~~ + 9 | } + 10 | } +vlib/v/checker/tests/labelled_break_continue.vv:15:14: error: invalid label name `L1` + 13 | i = e + 14 | for { + 15 | if i < 3 {continue L1} + | ~~~~~~~~ + 16 | else {break L1} + 17 | } +vlib/v/checker/tests/labelled_break_continue.vv:16:10: error: invalid label name `L1` + 14 | for { + 15 | if i < 3 {continue L1} + 16 | else {break L1} + | ~~~~~ + 17 | } + 18 | } +vlib/v/checker/tests/labelled_break_continue.vv:21:11: error: nesting of labelled `for` loops is not supported + 19 | // check nested loops (not supported ATM) + 20 | L3: for ;; i++ { + 21 | L4: for { + | ^ + 22 | if i < 17 {continue L3} + 23 | else {break L3} diff --git a/v_windows/v/old/vlib/v/checker/tests/labelled_break_continue.vv b/v_windows/v/old/vlib/v/checker/tests/labelled_break_continue.vv new file mode 100644 index 0000000..3447ab1 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/labelled_break_continue.vv @@ -0,0 +1,26 @@ +fn main() { + mut i := 4 + // check branching to a later loop + L1: for { + i++ + for { + if i < 7 {continue L2} + else {break L2} + } + } + // check branching to an earlier loop + L2: for e in [1,2,3,4] { + i = e + for { + if i < 3 {continue L1} + else {break L1} + } + } + // check nested loops (not supported ATM) + L3: for ;; i++ { + L4: for { + if i < 17 {continue L3} + else {break L3} + } + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/lock_already_locked.out b/v_windows/v/old/vlib/v/checker/tests/lock_already_locked.out new file mode 100644 index 0000000..29696aa --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/lock_already_locked.out @@ -0,0 +1,13 @@ +vlib/v/checker/tests/lock_already_locked.vv:11:3: error: nested `lock`/`rlock` not allowed + 9 | } + 10 | lock a { + 11 | rlock a { + | ~~~~~ + 12 | a.x++ + 13 | } +vlib/v/checker/tests/lock_already_locked.vv:15:10: error: `a` is `shared` and must be `rlock`ed or `lock`ed to be used as non-mut argument to print + 13 | } + 14 | } + 15 | println(a.x) + | ^ + 16 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/lock_already_locked.vv b/v_windows/v/old/vlib/v/checker/tests/lock_already_locked.vv new file mode 100644 index 0000000..7bb6365 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/lock_already_locked.vv @@ -0,0 +1,16 @@ +struct St { +mut: + x int +} + +fn main() { + shared a := &St{ + x: 5 + } + lock a { + rlock a { + a.x++ + } + } + println(a.x) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/lock_already_rlocked.out b/v_windows/v/old/vlib/v/checker/tests/lock_already_rlocked.out new file mode 100644 index 0000000..b7e3fda --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/lock_already_rlocked.out @@ -0,0 +1,13 @@ +vlib/v/checker/tests/lock_already_rlocked.vv:11:3: error: nested `lock`/`rlock` not allowed + 9 | } + 10 | rlock a { + 11 | lock a { + | ~~~~ + 12 | a.x++ + 13 | } +vlib/v/checker/tests/lock_already_rlocked.vv:15:10: error: `a` is `shared` and must be `rlock`ed or `lock`ed to be used as non-mut argument to print + 13 | } + 14 | } + 15 | println(a.x) + | ^ + 16 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/lock_already_rlocked.vv b/v_windows/v/old/vlib/v/checker/tests/lock_already_rlocked.vv new file mode 100644 index 0000000..c8e8236 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/lock_already_rlocked.vv @@ -0,0 +1,16 @@ +struct St { +mut: + x int +} + +fn main() { + shared a := &St{ + x: 5 + } + rlock a { + lock a { + a.x++ + } + } + println(a.x) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/lock_const.out b/v_windows/v/old/vlib/v/checker/tests/lock_const.out new file mode 100644 index 0000000..2b923da --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/lock_const.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/lock_const.vv:7:8: error: `a` must be declared as `shared` variable to be locked + 5 | fn main() { + 6 | mut c := 0 + 7 | rlock a { + | ^ + 8 | c = a + 9 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/lock_const.vv b/v_windows/v/old/vlib/v/checker/tests/lock_const.vv new file mode 100644 index 0000000..df16a6a --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/lock_const.vv @@ -0,0 +1,11 @@ +const ( + a = 5 +) + +fn main() { + mut c := 0 + rlock a { + c = a + } + println(c) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/lock_needed.out b/v_windows/v/old/vlib/v/checker/tests/lock_needed.out new file mode 100644 index 0000000..637cbb9 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/lock_needed.out @@ -0,0 +1,27 @@ +vlib/v/checker/tests/lock_needed.vv:10:2: error: `abc` is `shared` and needs explicit lock for `v.ast.SelectorExpr` + 8 | x: 5 + 9 | } + 10 | abc.x++ + | ~~~ + 11 | println(abc.x) + 12 | } +vlib/v/checker/tests/lock_needed.vv:11:10: error: `abc` is `shared` and must be `rlock`ed or `lock`ed to be used as non-mut argument to print + 9 | } + 10 | abc.x++ + 11 | println(abc.x) + | ~~~ + 12 | } + 13 | +vlib/v/checker/tests/lock_needed.vv:25:12: error: `a.st` is `shared` and must be `rlock`ed or `lock`ed to be used as non-mut argument to print + 23 | } + 24 | } + 25 | println(a.st.x) + | ~~ + 26 | } + 27 | +vlib/v/checker/tests/lock_needed.vv:30:10: error: `a` is `shared` and must be `rlock`ed or `lock`ed to be used as non-mut argument to print + 28 | fn g() { + 29 | shared a := []f64{len: 10, init: 7.5} + 30 | println(a[3]) + | ^ + 31 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/lock_needed.vv b/v_windows/v/old/vlib/v/checker/tests/lock_needed.vv new file mode 100644 index 0000000..001aa7f --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/lock_needed.vv @@ -0,0 +1,31 @@ +struct St { +mut: + x int +} + +fn main() { + shared abc := &St{ + x: 5 + } + abc.x++ + println(abc.x) +} + +struct Abc { +mut: + st shared St +} + +fn f() { + mut a := Abc{ + st: St{ + x: 9 + } + } + println(a.st.x) +} + +fn g() { + shared a := []f64{len: 10, init: 7.5} + println(a[3]) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/lock_nonshared.out b/v_windows/v/old/vlib/v/checker/tests/lock_nonshared.out new file mode 100644 index 0000000..00a05c2 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/lock_nonshared.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/lock_nonshared.vv:10:7: error: `a` must be declared as `shared` variable to be locked + 8 | x: 5 + 9 | } + 10 | lock a { + | ^ + 11 | a.x++ + 12 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/lock_nonshared.vv b/v_windows/v/old/vlib/v/checker/tests/lock_nonshared.vv new file mode 100644 index 0000000..1524285 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/lock_nonshared.vv @@ -0,0 +1,14 @@ +struct St { +mut: + x int +} + +fn main() { + mut a := &St{ + x: 5 + } + lock a { + a.x++ + } + println(a) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/main_and_script_err.out b/v_windows/v/old/vlib/v/checker/tests/main_and_script_err.out new file mode 100644 index 0000000..71b5f75 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/main_and_script_err.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/main_and_script_err.vv:1:1: error: function `main` is already defined + 1 | fn main() { + | ^ + 2 | println('main') + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/main_and_script_err.vv b/v_windows/v/old/vlib/v/checker/tests/main_and_script_err.vv new file mode 100644 index 0000000..e746460 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/main_and_script_err.vv @@ -0,0 +1,4 @@ +fn main() { + println('main') +} +println('out') diff --git a/v_windows/v/old/vlib/v/checker/tests/main_args_err.out b/v_windows/v/old/vlib/v/checker/tests/main_args_err.out new file mode 100644 index 0000000..ee5f426 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/main_args_err.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/main_args_err.vv:1:1: error: function `main` cannot have arguments + 1 | fn main(a string) { + | ~~~~~~~~~~~~~~~~~ + 2 | println(a) + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/main_args_err.vv b/v_windows/v/old/vlib/v/checker/tests/main_args_err.vv new file mode 100644 index 0000000..69c9508 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/main_args_err.vv @@ -0,0 +1,3 @@ +fn main(a string) { + println(a) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/main_called_err.out b/v_windows/v/old/vlib/v/checker/tests/main_called_err.out new file mode 100644 index 0000000..1f1e253 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/main_called_err.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/main_called_err.vv:2:2: error: the `main` function cannot be called in the program + 1 | fn main() { + 2 | main() + | ~~~~~~ + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/main_called_err.vv b/v_windows/v/old/vlib/v/checker/tests/main_called_err.vv new file mode 100644 index 0000000..2be2db1 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/main_called_err.vv @@ -0,0 +1,3 @@ +fn main() { + main() +} diff --git a/v_windows/v/old/vlib/v/checker/tests/main_no_body_err.out b/v_windows/v/old/vlib/v/checker/tests/main_no_body_err.out new file mode 100644 index 0000000..09e4a9f --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/main_no_body_err.out @@ -0,0 +1,3 @@ +vlib/v/checker/tests/main_no_body_err.vv:1:1: error: function `main` must declare a body + 1 | fn main() + | ~~~~~~~~~ diff --git a/v_windows/v/old/vlib/v/checker/tests/main_no_body_err.vv b/v_windows/v/old/vlib/v/checker/tests/main_no_body_err.vv new file mode 100644 index 0000000..d955fb2 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/main_no_body_err.vv @@ -0,0 +1 @@ +fn main() diff --git a/v_windows/v/old/vlib/v/checker/tests/main_return_err.out b/v_windows/v/old/vlib/v/checker/tests/main_return_err.out new file mode 100644 index 0000000..c9ce435 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/main_return_err.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/main_return_err.vv:1:1: error: function `main` cannot return values + 1 | fn main() f64 { + | ~~~~~~~~~~~~~ + 2 | return 1.23 + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/main_return_err.vv b/v_windows/v/old/vlib/v/checker/tests/main_return_err.vv new file mode 100644 index 0000000..de52fe0 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/main_return_err.vv @@ -0,0 +1,3 @@ +fn main() f64 { + return 1.23 +} diff --git a/v_windows/v/old/vlib/v/checker/tests/map_delete.out b/v_windows/v/old/vlib/v/checker/tests/map_delete.out new file mode 100644 index 0000000..c3a1822 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/map_delete.out @@ -0,0 +1,20 @@ +vlib/v/checker/tests/map_delete.vv:5:11: error: cannot use `int literal` as `string` in argument 1 to `Map.delete` + 3 | '1': 1 + 4 | } + 5 | m.delete(1) + | ^ + 6 | m.delete(1, 2) + 7 | m2 := map{ +vlib/v/checker/tests/map_delete.vv:6:4: error: expected 1 argument, but got 2 + 4 | } + 5 | m.delete(1) + 6 | m.delete(1, 2) + | ~~~~~~~~~~~~ + 7 | m2 := map{ + 8 | '1': 1 +vlib/v/checker/tests/map_delete.vv:10:2: error: `m2` is immutable, declare it with `mut` to make it mutable + 8 | '1': 1 + 9 | } + 10 | m2.delete('1') + | ~~ + 11 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/map_delete.vv b/v_windows/v/old/vlib/v/checker/tests/map_delete.vv new file mode 100644 index 0000000..26b2606 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/map_delete.vv @@ -0,0 +1,11 @@ +fn main() { + mut m := map{ + '1': 1 + } + m.delete(1) + m.delete(1, 2) + m2 := map{ + '1': 1 + } + m2.delete('1') +} diff --git a/v_windows/v/old/vlib/v/checker/tests/map_func_void_return_err.out b/v_windows/v/old/vlib/v/checker/tests/map_func_void_return_err.out new file mode 100644 index 0000000..943e7b1 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/map_func_void_return_err.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/map_func_void_return_err.vv:2:22: error: type mismatch, `voids` does not return anything + 1 | fn main() { + 2 | list := [1,2,3].map(voids(it)) + | ~~~~~~~~~ + 3 | } + 4 | diff --git a/v_windows/v/old/vlib/v/checker/tests/map_func_void_return_err.vv b/v_windows/v/old/vlib/v/checker/tests/map_func_void_return_err.vv new file mode 100644 index 0000000..bd5446e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/map_func_void_return_err.vv @@ -0,0 +1,7 @@ +fn main() { + list := [1,2,3].map(voids(it)) +} + +fn voids(arg int) { + println(arg) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/map_init_invalid_syntax.out b/v_windows/v/old/vlib/v/checker/tests/map_init_invalid_syntax.out new file mode 100644 index 0000000..e18f40d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/map_init_invalid_syntax.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/map_init_invalid_syntax.vv:2:10: error: invalid empty map initilization syntax, use e.g. map[string]int{} instead + 1 | fn main() { + 2 | a := map{} + | ~~ + 3 | println(a) + 4 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/map_init_invalid_syntax.vv b/v_windows/v/old/vlib/v/checker/tests/map_init_invalid_syntax.vv new file mode 100644 index 0000000..a976d70 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/map_init_invalid_syntax.vv @@ -0,0 +1,4 @@ +fn main() { + a := map{} + println(a) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/map_init_key_duplicate_err.out b/v_windows/v/old/vlib/v/checker/tests/map_init_key_duplicate_err.out new file mode 100644 index 0000000..d560b64 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/map_init_key_duplicate_err.out @@ -0,0 +1,13 @@ +vlib/v/checker/tests/map_init_key_duplicate_err.vv:5:3: error: duplicate key "foo" in map literal + 3 | 'foo': 'bar' + 4 | 'abc': 'abc' + 5 | 'foo': 'bar' + | ~~~~~ + 6 | } + 7 | println(a) +vlib/v/checker/tests/map_init_key_duplicate_err.vv:9:18: error: duplicate key "2" in map literal + 7 | println(a) + 8 | + 9 | _ = map{2:0 3:0 2:0} + | ^ + 10 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/map_init_key_duplicate_err.vv b/v_windows/v/old/vlib/v/checker/tests/map_init_key_duplicate_err.vv new file mode 100644 index 0000000..cd6efe6 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/map_init_key_duplicate_err.vv @@ -0,0 +1,10 @@ +fn main() { + a := map{ + 'foo': 'bar' + 'abc': 'abc' + 'foo': 'bar' + } + println(a) + + _ = map{2:0 3:0 2:0} +} diff --git a/v_windows/v/old/vlib/v/checker/tests/map_init_wrong_type.out b/v_windows/v/old/vlib/v/checker/tests/map_init_wrong_type.out new file mode 100644 index 0000000..117e787 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/map_init_wrong_type.out @@ -0,0 +1,21 @@ +vlib/v/checker/tests/map_init_wrong_type.vv:3:18: error: invalid map value: expected `f32`, not `float literal` + 1 | fn main() { + 2 | mut a := map[string]f32{} + 3 | a = map{ 'x': 12.3 } + | ~~~~ + 4 | _ = map{2:0 3:0 "hi":0} + 5 | _ = map{2:0 3:`@` 4:0} +vlib/v/checker/tests/map_init_wrong_type.vv:4:20: error: invalid map key: expected `int`, not `string` + 2 | mut a := map[string]f32{} + 3 | a = map{ 'x': 12.3 } + 4 | _ = map{2:0 3:0 "hi":0} + | ~~~~ + 5 | _ = map{2:0 3:`@` 4:0} + 6 | _ = a +vlib/v/checker/tests/map_init_wrong_type.vv:5:18: error: invalid map value: expected `int`, not `rune` + 3 | a = map{ 'x': 12.3 } + 4 | _ = map{2:0 3:0 "hi":0} + 5 | _ = map{2:0 3:`@` 4:0} + | ~~~ + 6 | _ = a + 7 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/map_init_wrong_type.vv b/v_windows/v/old/vlib/v/checker/tests/map_init_wrong_type.vv new file mode 100644 index 0000000..dc41230 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/map_init_wrong_type.vv @@ -0,0 +1,7 @@ +fn main() { + mut a := map[string]f32{} + a = map{ 'x': 12.3 } + _ = map{2:0 3:0 "hi":0} + _ = map{2:0 3:`@` 4:0} + _ = a +} diff --git a/v_windows/v/old/vlib/v/checker/tests/map_ops.out b/v_windows/v/old/vlib/v/checker/tests/map_ops.out new file mode 100644 index 0000000..d30508b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/map_ops.out @@ -0,0 +1,20 @@ +vlib/v/checker/tests/map_ops.vv:3:7: error: invalid key: expected `int`, not `rune` + 1 | fn test_map() { + 2 | mut m := map[int]string + 3 | _ = m[`!`] + | ~~~~~ + 4 | m['hi'] = 8 + 5 | m[&m] += 4 +vlib/v/checker/tests/map_ops.vv:4:3: error: invalid key: expected `int`, not `string` + 2 | mut m := map[int]string + 3 | _ = m[`!`] + 4 | m['hi'] = 8 + | ~~~~~~ + 5 | m[&m] += 4 + 6 | } +vlib/v/checker/tests/map_ops.vv:5:3: error: invalid key: expected `int`, not `&map[int]string` + 3 | _ = m[`!`] + 4 | m['hi'] = 8 + 5 | m[&m] += 4 + | ~~~~ + 6 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/map_ops.vv b/v_windows/v/old/vlib/v/checker/tests/map_ops.vv new file mode 100644 index 0000000..ac71947 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/map_ops.vv @@ -0,0 +1,6 @@ +fn test_map() { + mut m := map[int]string + _ = m[`!`] + m['hi'] = 8 + m[&m] += 4 +} diff --git a/v_windows/v/old/vlib/v/checker/tests/map_unknown_value.out b/v_windows/v/old/vlib/v/checker/tests/map_unknown_value.out new file mode 100644 index 0000000..d5f268b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/map_unknown_value.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/map_unknown_value.vv:2:23: error: unknown type `DoesNotExist` + 1 | struct App { + 2 | my_map map[string]DoesNotExist + | ~~~~~~~~~~~~ + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/map_unknown_value.vv b/v_windows/v/old/vlib/v/checker/tests/map_unknown_value.vv new file mode 100644 index 0000000..90cb87d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/map_unknown_value.vv @@ -0,0 +1,3 @@ +struct App { + my_map map[string]DoesNotExist +} diff --git a/v_windows/v/old/vlib/v/checker/tests/match_alias_type_err.out b/v_windows/v/old/vlib/v/checker/tests/match_alias_type_err.out new file mode 100644 index 0000000..49ddf1f --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/match_alias_type_err.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/match_alias_type_err.vv:13:3: error: cannot match alias type `Stmt` with `SelectStmt` + 11 | + 12 | match stmt { + 13 | SelectStmt { panic('select') } + | ~~~~~~~~~~ + 14 | else { /* why? */ } + 15 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/match_alias_type_err.vv b/v_windows/v/old/vlib/v/checker/tests/match_alias_type_err.vv new file mode 100644 index 0000000..2673231 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/match_alias_type_err.vv @@ -0,0 +1,16 @@ +type Stmt = SelectStmt + +struct SelectStmt {} + +fn parse(sql string) Stmt { + return SelectStmt{} +} + +fn main() { + stmt := parse('select 123') + + match stmt { + SelectStmt { panic('select') } + else { /* why? */ } + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/match_duplicate_branch.out b/v_windows/v/old/vlib/v/checker/tests/match_duplicate_branch.out new file mode 100644 index 0000000..11263d5 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/match_duplicate_branch.out @@ -0,0 +1,42 @@ +vlib/v/checker/tests/match_duplicate_branch.vv:15:3: error: match case `St1` is handled more than once + 13 | match i { + 14 | St1 { println('St1') } + 15 | St1 { println('St1') } + | ~~~ + 16 | St2 { println('St2') } + 17 | } +vlib/v/checker/tests/match_duplicate_branch.vv:20:3: error: match case `St1` is handled more than once + 18 | match i { + 19 | St1 { println('St1') } + 20 | St1 { println('St1') } + | ~~~ + 21 | else { println('else') } + 22 | } +vlib/v/checker/tests/match_duplicate_branch.vv:29:3: error: match case `green` is handled more than once + 27 | .red { println('red') } + 28 | .green { println('green') } + 29 | .green { println('green') } + | ~~~~~~ + 30 | .blue { println('blue') } + 31 | } +vlib/v/checker/tests/match_duplicate_branch.vv:34:3: error: match case `green` is handled more than once + 32 | match c { + 33 | .red, .green { println('red green') } + 34 | .green { println('green') } + | ~~~~~~ + 35 | else { println('else') } + 36 | } +vlib/v/checker/tests/match_duplicate_branch.vv:43:3: error: match case `2` is handled more than once + 41 | 1 { println('1') } + 42 | 2 { println('2') } + 43 | 2 { println('3') } + | ^ + 44 | else { println('else') } + 45 | } +vlib/v/checker/tests/match_duplicate_branch.vv:51:3: error: match case `3` is handled more than once + 49 | match i { + 50 | 1...5 { println('1 to 5') } + 51 | 3 { println('3') } + | ^ + 52 | else { println('else') } + 53 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/match_duplicate_branch.vv b/v_windows/v/old/vlib/v/checker/tests/match_duplicate_branch.vv new file mode 100644 index 0000000..dd5a92c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/match_duplicate_branch.vv @@ -0,0 +1,61 @@ +enum Color { + red + green + blue +} + +struct St1 {} +struct St2 {} + +type St = St1 | St2 + +fn test_sum_type(i St) { + match i { + St1 { println('St1') } + St1 { println('St1') } + St2 { println('St2') } + } + match i { + St1 { println('St1') } + St1 { println('St1') } + else { println('else') } + } +} + +fn test_enum(c Color) { + match c { + .red { println('red') } + .green { println('green') } + .green { println('green') } + .blue { println('blue') } + } + match c { + .red, .green { println('red green') } + .green { println('green') } + else { println('else') } + } +} + +fn test_int(i int) { + match i { + 1 { println('1') } + 2 { println('2') } + 2 { println('3') } + else { println('else') } + } +} + +fn test_range(i int) { + match i { + 1...5 { println('1 to 5') } + 3 { println('3') } + else { println('else') } + } +} + +fn main() { + test_sum_type(St1{}) + test_enum(.red) + test_int(2) + test_range(4) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/match_else_last_expr.out b/v_windows/v/old/vlib/v/checker/tests/match_else_last_expr.out new file mode 100644 index 0000000..c3331f1 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/match_else_last_expr.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/match_else_last_expr.vv:4:3: error: `else` must be the last branch of `match` + 2 | match 1 { + 3 | 1 { println('1') } + 4 | else { println('else') } + | ~~~~ + 5 | 4 { println('4') } + 6 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/match_else_last_expr.vv b/v_windows/v/old/vlib/v/checker/tests/match_else_last_expr.vv new file mode 100644 index 0000000..3aa5cc5 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/match_else_last_expr.vv @@ -0,0 +1,7 @@ +fn main() { + match 1 { + 1 { println('1') } + else { println('else') } + 4 { println('4') } + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/match_expr_and_expected_type_error.out b/v_windows/v/old/vlib/v/checker/tests/match_expr_and_expected_type_error.out new file mode 100644 index 0000000..e42355f --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/match_expr_and_expected_type_error.out @@ -0,0 +1,14 @@ +vlib/v/checker/tests/match_expr_and_expected_type_error.vv:3:3: error: cannot match `rune` with `string` + 1 | ch := `a` + 2 | match ch { + 3 | 'a' {} + | ~~~ + 4 | else {} + 5 | } +vlib/v/checker/tests/match_expr_and_expected_type_error.vv:9:3: error: cannot match `int` with `string` + 7 | i := 123 + 8 | match i { + 9 | 'a' {} + | ~~~ + 10 | else {} + 11 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/match_expr_and_expected_type_error.vv b/v_windows/v/old/vlib/v/checker/tests/match_expr_and_expected_type_error.vv new file mode 100644 index 0000000..ef1e62c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/match_expr_and_expected_type_error.vv @@ -0,0 +1,11 @@ +ch := `a` +match ch { + 'a' {} + else {} +} + +i := 123 +match i { + 'a' {} + else {} +} diff --git a/v_windows/v/old/vlib/v/checker/tests/match_expr_else.out b/v_windows/v/old/vlib/v/checker/tests/match_expr_else.out new file mode 100644 index 0000000..cf70833 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/match_expr_else.out @@ -0,0 +1,21 @@ +vlib/v/checker/tests/match_expr_else.vv:5:6: error: match must be exhaustive (add match branches for: `f64` or `else {}` at the end) + 3 | fn main() { + 4 | x := AA('test') + 5 | _ = match x { + | ~~~~~~~~~ + 6 | int { + 7 | 'int' +vlib/v/checker/tests/match_expr_else.vv:23:3: error: match expression is exhaustive, `else` is unnecessary + 21 | 'f64' + 22 | } + 23 | else { + | ~~~~ + 24 | 'else' + 25 | } +vlib/v/checker/tests/match_expr_else.vv:34:3: error: `else` must be the last branch of `match` + 32 | 'string' + 33 | } + 34 | else { + | ~~~~ + 35 | 'else' + 36 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/match_expr_else.vv b/v_windows/v/old/vlib/v/checker/tests/match_expr_else.vv new file mode 100644 index 0000000..7c5551e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/match_expr_else.vv @@ -0,0 +1,41 @@ +type AA = int | string | f64 + +fn main() { + x := AA('test') + _ = match x { + int { + 'int' + } + string { + 'string' + } + } + _ = match x { + int { + 'int' + } + string { + 'string' + } + f64 { + 'f64' + } + else { + 'else' + } + } + _ = match x { + int { + 'int' + } + string { + 'string' + } + else { + 'else' + } + f64 { + 'f64' + } + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/match_expr_empty_branch.out b/v_windows/v/old/vlib/v/checker/tests/match_expr_empty_branch.out new file mode 100644 index 0000000..927da5a --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/match_expr_empty_branch.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/match_expr_empty_branch.vv:3:2: error: `match` expression requires an expression as the last statement of every branch + 1 | _ := match true { + 2 | true { 0 } + 3 | false {} + | ~~~~~~~~ + 4 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/match_expr_empty_branch.vv b/v_windows/v/old/vlib/v/checker/tests/match_expr_empty_branch.vv new file mode 100644 index 0000000..2a8650d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/match_expr_empty_branch.vv @@ -0,0 +1,4 @@ +_ := match true { + true { 0 } + false {} +} diff --git a/v_windows/v/old/vlib/v/checker/tests/match_expr_non_void_stmt_last.out b/v_windows/v/old/vlib/v/checker/tests/match_expr_non_void_stmt_last.out new file mode 100644 index 0000000..6b5e161 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/match_expr_non_void_stmt_last.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/match_expr_non_void_stmt_last.vv:9:4: error: `match` expression requires an expression as the last statement of every branch + 7 | } + 8 | []int { + 9 | return arr.str() + | ~~~~~~~~~~~~~~~~ + 10 | } + 11 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/match_expr_non_void_stmt_last.vv b/v_windows/v/old/vlib/v/checker/tests/match_expr_non_void_stmt_last.vv new file mode 100644 index 0000000..0737152 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/match_expr_non_void_stmt_last.vv @@ -0,0 +1,17 @@ +type Arr = []int | []string + +fn (arr Arr) str() string { + return match arr { + []string { + arr.join(' ') + } + []int { + return arr.str() + } + } +} + +fn main() { + println(Arr([0, 0])) + println(Arr(['0', '0'])) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/match_invalid_type.out b/v_windows/v/old/vlib/v/checker/tests/match_invalid_type.out new file mode 100644 index 0000000..4c6b98b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/match_invalid_type.out @@ -0,0 +1,21 @@ +vlib/v/checker/tests/match_invalid_type.vv:5:3: error: `IoS` has no variant `byte` + 3 | fn sum() { + 4 | match IoS(1) { + 5 | byte { + | ~~~~ + 6 | println('not cool') + 7 | } +vlib/v/checker/tests/match_invalid_type.vv:4:2: error: match must be exhaustive (add match branches for: `int`, `string` or `else {}` at the end) + 2 | + 3 | fn sum() { + 4 | match IoS(1) { + | ~~~~~~~~~~~~~~ + 5 | byte { + 6 | println('not cool') +vlib/v/checker/tests/match_invalid_type.vv:24:3: error: `Cat` doesn't implement method `speak` of interface `Animal` + 22 | a := Animal(Dog{}) + 23 | match a { + 24 | Cat { + | ~~~ + 25 | println('not cool either') + 26 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/match_invalid_type.vv b/v_windows/v/old/vlib/v/checker/tests/match_invalid_type.vv new file mode 100644 index 0000000..3a5dca6 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/match_invalid_type.vv @@ -0,0 +1,29 @@ +type IoS = int | string + +fn sum() { + match IoS(1) { + byte { + println('not cool') + } + } +} + +interface Animal { + speak() +} + +struct Dog {} + +fn (d Dog) speak() {} + +struct Cat {} + +fn iface() { + a := Animal(Dog{}) + match a { + Cat { + println('not cool either') + } + else {} + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/match_return_mismatch_type_err.out b/v_windows/v/old/vlib/v/checker/tests/match_return_mismatch_type_err.out new file mode 100644 index 0000000..3a8e8fe --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/match_return_mismatch_type_err.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/match_return_mismatch_type_err.vv:4:10: error: return type mismatch, it should be `string` + 2 | a := match 1 { + 3 | 1 { 'aa' } + 4 | else { 22 } + | ~~ + 5 | } + 6 | println(a) diff --git a/v_windows/v/old/vlib/v/checker/tests/match_return_mismatch_type_err.vv b/v_windows/v/old/vlib/v/checker/tests/match_return_mismatch_type_err.vv new file mode 100644 index 0000000..c422126 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/match_return_mismatch_type_err.vv @@ -0,0 +1,7 @@ +fn main() { + a := match 1 { + 1 { 'aa' } + else { 22 } + } + println(a) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/match_sumtype_multiple_types.out b/v_windows/v/old/vlib/v/checker/tests/match_sumtype_multiple_types.out new file mode 100644 index 0000000..9f2201f --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/match_sumtype_multiple_types.out @@ -0,0 +1,14 @@ +vlib/v/checker/tests/match_sumtype_multiple_types.vv:26:13: error: type `Charlie` has no field or method `char` + 24 | match l { + 25 | Alfa, Charlie { + 26 | assert l.char == `a` + | ~~~~ + 27 | assert l.letter() == 'a' + 28 | } +vlib/v/checker/tests/match_sumtype_multiple_types.vv:27:13: error: unknown method: `Charlie.letter` + 25 | Alfa, Charlie { + 26 | assert l.char == `a` + 27 | assert l.letter() == 'a' + | ~~~~~~~~ + 28 | } + 29 | Bravo { \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/match_sumtype_multiple_types.vv b/v_windows/v/old/vlib/v/checker/tests/match_sumtype_multiple_types.vv new file mode 100644 index 0000000..eca5954 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/match_sumtype_multiple_types.vv @@ -0,0 +1,33 @@ +struct Alfa { + char rune +} + +fn (a Alfa) letter() rune { + return a.char +} + +struct Bravo { + char rune +} + +fn (b Bravo) letter() rune { + return b.char +} + +struct Charlie {} + +type NATOAlphabet = Alfa | Bravo | Charlie + +fn method_not_exists() { + a := Alfa{} + l := NATOAlphabet(a) + match l { + Alfa, Charlie { + assert l.char == `a` + assert l.letter() == 'a' + } + Bravo { + assert false + } + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/match_undefined_cond.out b/v_windows/v/old/vlib/v/checker/tests/match_undefined_cond.out new file mode 100644 index 0000000..ddfed08 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/match_undefined_cond.out @@ -0,0 +1,21 @@ +vlib/v/checker/tests/match_undefined_cond.vv:4:15: error: undefined ident: `Asd` + 2 | + 3 | fn main() { + 4 | res := match Asd { + | ~~~ + 5 | 1 { 'foo' } + 6 | 2 { 'test' } +vlib/v/checker/tests/match_undefined_cond.vv:5:3: error: cannot match `void` with `int literal` + 3 | fn main() { + 4 | res := match Asd { + 5 | 1 { 'foo' } + | ^ + 6 | 2 { 'test' } + 7 | else { '' } +vlib/v/checker/tests/match_undefined_cond.vv:6:3: error: cannot match `void` with `int literal` + 4 | res := match Asd { + 5 | 1 { 'foo' } + 6 | 2 { 'test' } + | ^ + 7 | else { '' } + 8 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/match_undefined_cond.vv b/v_windows/v/old/vlib/v/checker/tests/match_undefined_cond.vv new file mode 100644 index 0000000..952e4b7 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/match_undefined_cond.vv @@ -0,0 +1,10 @@ +type Asd = int + +fn main() { + res := match Asd { + 1 { 'foo' } + 2 { 'test' } + else { '' } + } + _ = res +} diff --git a/v_windows/v/old/vlib/v/checker/tests/method_array_slice.out b/v_windows/v/old/vlib/v/checker/tests/method_array_slice.out new file mode 100644 index 0000000..dde9055 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/method_array_slice.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/method_array_slice.vv:5:12: error: .slice() is a private method, use `x[start..end]` instead + 3 | fn main() { + 4 | a := os.args.clone() + 5 | println(a.slice(1)) + | ~~~~~~~~ + 6 | println(a[1..]) + 7 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/method_array_slice.vv b/v_windows/v/old/vlib/v/checker/tests/method_array_slice.vv new file mode 100644 index 0000000..cfb77f3 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/method_array_slice.vv @@ -0,0 +1,7 @@ +import os + +fn main() { + a := os.args.clone() + println(a.slice(1)) + println(a[1..]) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/method_generic_infer_err.out b/v_windows/v/old/vlib/v/checker/tests/method_generic_infer_err.out new file mode 100644 index 0000000..2017a6f --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/method_generic_infer_err.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/method_generic_infer_err.vv:9:7: error: could not infer generic type `T` in call to `func` + 7 | fn main() { + 8 | data := Data{} + 9 | data.func() + | ~~~~~~ + 10 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/method_generic_infer_err.vv b/v_windows/v/old/vlib/v/checker/tests/method_generic_infer_err.vv new file mode 100644 index 0000000..226c4c2 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/method_generic_infer_err.vv @@ -0,0 +1,10 @@ +struct Data {} + +fn (_ Data) func() T { + return T{} +} + +fn main() { + data := Data{} + data.func() +} diff --git a/v_windows/v/old/vlib/v/checker/tests/method_op_alias_err.out b/v_windows/v/old/vlib/v/checker/tests/method_op_alias_err.out new file mode 100644 index 0000000..195ab56 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/method_op_alias_err.out @@ -0,0 +1,42 @@ +vlib/v/checker/tests/method_op_alias_err.vv:4:18: error: expected `Foo` not `Foo2` - both operands must be the same type for operator overloading + 2 | type Foo2 = string + 3 | + 4 | fn (f Foo) + (f1 Foo2) Foo2 { + | ~~~~ + 5 | return Foo2(f + f1) + 6 | } +vlib/v/checker/tests/method_op_alias_err.vv:5:17: error: infix expr: cannot use `string` (right expression) as `string` + 3 | + 4 | fn (f Foo) + (f1 Foo2) Foo2 { + 5 | return Foo2(f + f1) + | ~~~~~~ + 6 | } + 7 | +vlib/v/checker/tests/method_op_alias_err.vv:8:1: error: cannot define operator methods on type alias for `string` + 6 | } + 7 | + 8 | fn (f Foo) * (f1 Foo) Foo { + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + 9 | return Foo(f + f1) + 10 | } +vlib/v/checker/tests/method_op_alias_err.vv:14:6: error: mismatched types `Foo` and `string` + 12 | fn main() { + 13 | mut f := Foo('fg') + 14 | f += 'fg' + | ~~ + 15 | f *= Foo2('2') + 16 | f -= Foo('fo') +vlib/v/checker/tests/method_op_alias_err.vv:15:9: error: cannot assign to `f`: expected `Foo`, not `Foo2` + 13 | mut f := Foo('fg') + 14 | f += 'fg' + 15 | f *= Foo2('2') + | ~~~~~~~~~ + 16 | f -= Foo('fo') + 17 | println(f) +vlib/v/checker/tests/method_op_alias_err.vv:16:6: error: cannot use operator methods on type alias for `string` + 14 | f += 'fg' + 15 | f *= Foo2('2') + 16 | f -= Foo('fo') + | ~~ + 17 | println(f) + 18 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/method_op_alias_err.vv b/v_windows/v/old/vlib/v/checker/tests/method_op_alias_err.vv new file mode 100644 index 0000000..d5b4ecd --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/method_op_alias_err.vv @@ -0,0 +1,18 @@ +type Foo = string +type Foo2 = string + +fn (f Foo) + (f1 Foo2) Foo2 { + return Foo2(f + f1) +} + +fn (f Foo) * (f1 Foo) Foo { + return Foo(f + f1) +} + +fn main() { + mut f := Foo('fg') + f += 'fg' + f *= Foo2('2') + f -= Foo('fo') + println(f) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/method_op_err.out b/v_windows/v/old/vlib/v/checker/tests/method_op_err.out new file mode 100644 index 0000000..547ceea --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/method_op_err.out @@ -0,0 +1,69 @@ +vlib/v/checker/tests/method_op_err.vv:11:1: error: operator methods should have exactly 1 argument + 9 | } + 10 | + 11 | fn (u User) + () { + | ~~~~~~~~~~~~~~~~ + 12 | } + 13 | +vlib/v/checker/tests/method_op_err.vv:14:18: error: expected `User` not `Foo` - both operands must be the same type for operator overloading + 12 | } + 13 | + 14 | fn (u User) - (f Foo) User { + | ~~~ + 15 | return User{u.a - f.a, u.b-f.a} + 16 | } +vlib/v/checker/tests/method_op_err.vv:18:9: error: receiver cannot be `mut` for operator overloading + 16 | } + 17 | + 18 | fn (mut u User) * (u1 User) User { + | ~~~~~~ + 19 | return User{} + 20 | } +vlib/v/checker/tests/method_op_err.vv:22:1: error: argument cannot be `mut` for operator overloading + 20 | } + 21 | + 22 | fn (u User) / (mut u1 User) User { + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 23 | return User{} + 24 | } +vlib/v/checker/tests/method_op_err.vv:32:13: error: infix expr: cannot use `Foo` (right expression) as `User` + 30 | fn main() { + 31 | println(User{3, 4}) + 32 | println(User{3, 4} - Foo{3, 3}) + | ~~~~~~~~~~~~~~~~~~~~~~ + 33 | println(User{3, 2} < User{2, 4}) + 34 | println(User{3, 4} < Foo{3, 4}) +vlib/v/checker/tests/method_op_err.vv:33:13: error: undefined operation `User` < `User` + 31 | println(User{3, 4}) + 32 | println(User{3, 4} - Foo{3, 3}) + 33 | println(User{3, 2} < User{2, 4}) + | ~~~~~~~~~~~~~~~~~~~~~~~ + 34 | println(User{3, 4} < Foo{3, 4}) + 35 | mut u := User{3, 4} +vlib/v/checker/tests/method_op_err.vv:34:13: error: mismatched types `User` and `Foo` + 32 | println(User{3, 4} - Foo{3, 3}) + 33 | println(User{3, 2} < User{2, 4}) + 34 | println(User{3, 4} < Foo{3, 4}) + | ~~~~~~~~~~~~~~~~~~~~~~ + 35 | mut u := User{3, 4} + 36 | _ = u +vlib/v/checker/tests/method_op_err.vv:37:10: error: cannot assign to `u`: expected `User`, not `int literal` + 35 | mut u := User{3, 4} + 36 | _ = u + 37 | u += 12 + | ~~ + 38 | u %= User{1, 3} + 39 | u += User{2, 3} +vlib/v/checker/tests/method_op_err.vv:38:5: error: operator %= not defined on left operand type `User` + 36 | _ = u + 37 | u += 12 + 38 | u %= User{1, 3} + | ^ + 39 | u += User{2, 3} + 40 | } +vlib/v/checker/tests/method_op_err.vv:39:7: error: operator `+` must return `User` to be used as an assignment operator + 37 | u += 12 + 38 | u %= User{1, 3} + 39 | u += User{2, 3} + | ~~ + 40 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/method_op_err.vv b/v_windows/v/old/vlib/v/checker/tests/method_op_err.vv new file mode 100644 index 0000000..24a82dd --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/method_op_err.vv @@ -0,0 +1,40 @@ +struct User { + a int + b int +} + +struct Foo { + a int + b int +} + +fn (u User) + () { +} + +fn (u User) - (f Foo) User { + return User{u.a - f.a, u.b-f.a} +} + +fn (mut u User) * (u1 User) User { + return User{} +} + +fn (u User) / (mut u1 User) User { + return User{} +} + +fn (u User) + (u1 User) Foo { + return Foo{a: u.a + u1.a, b: u.b + u1.b} +} + +fn main() { + println(User{3, 4}) + println(User{3, 4} - Foo{3, 3}) + println(User{3, 2} < User{2, 4}) + println(User{3, 4} < Foo{3, 4}) + mut u := User{3, 4} + _ = u + u += 12 + u %= User{1, 3} + u += User{2, 3} +} diff --git a/v_windows/v/old/vlib/v/checker/tests/method_wrong_arg_type.out b/v_windows/v/old/vlib/v/checker/tests/method_wrong_arg_type.out new file mode 100644 index 0000000..c5175b3 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/method_wrong_arg_type.out @@ -0,0 +1,13 @@ +vlib/v/checker/tests/method_wrong_arg_type.vv:10:9: error: cannot use `MyEnum` as `string` in argument 1 to `Sss.info` + 8 | e := MyEnum.x + 9 | s := Sss{} + 10 | s.info(e) + | ^ + 11 | } + 12 | +vlib/v/checker/tests/method_wrong_arg_type.vv:18:8: error: cannot use `int` as `&Sss` in argument 1 to `Sss.ptr` + 16 | s := Sss{} + 17 | v := 4 + 18 | s.ptr(v) + | ^ + 19 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/method_wrong_arg_type.vv b/v_windows/v/old/vlib/v/checker/tests/method_wrong_arg_type.vv new file mode 100644 index 0000000..d44a4cd --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/method_wrong_arg_type.vv @@ -0,0 +1,19 @@ +enum MyEnum { x y z } +pub fn (e MyEnum) str() string { return int(e).str() } + +struct Sss { } +fn (s Sss) info(msg string) { println(msg) } + +fn enum_str() { + e := MyEnum.x + s := Sss{} + s.info(e) +} + +fn (s Sss) ptr(p &Sss) {} + +fn ptr_arg() { + s := Sss{} + v := 4 + s.ptr(v) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/minus_op_wrong_type_err.out b/v_windows/v/old/vlib/v/checker/tests/minus_op_wrong_type_err.out new file mode 100644 index 0000000..b6895aa --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/minus_op_wrong_type_err.out @@ -0,0 +1,62 @@ +vlib/v/checker/tests/minus_op_wrong_type_err.vv:10:10: error: mismatched types `Aaa` and `int literal` + 8 | + 9 | fn main() { + 10 | println(Aaa{} - 10) + | ~~~~~~~~~~ + 11 | println(10 - Aaa{}) + 12 | println([1, 2, 3] - 10) +vlib/v/checker/tests/minus_op_wrong_type_err.vv:11:10: error: mismatched types `int literal` and `Aaa` + 9 | fn main() { + 10 | println(Aaa{} - 10) + 11 | println(10 - Aaa{}) + | ~~~~~~~~~~ + 12 | println([1, 2, 3] - 10) + 13 | println(10 - [1, 2, 3]) +vlib/v/checker/tests/minus_op_wrong_type_err.vv:12:10: error: mismatched types `[]int` and `int literal` + 10 | println(Aaa{} - 10) + 11 | println(10 - Aaa{}) + 12 | println([1, 2, 3] - 10) + | ~~~~~~~~~~~~~~ + 13 | println(10 - [1, 2, 3]) + 14 | a := map[string]int{} +vlib/v/checker/tests/minus_op_wrong_type_err.vv:13:10: error: mismatched types `int literal` and `[]int` + 11 | println(10 - Aaa{}) + 12 | println([1, 2, 3] - 10) + 13 | println(10 - [1, 2, 3]) + | ~~~~~~~~~~~~~~ + 14 | a := map[string]int{} + 15 | println(a - 10) +vlib/v/checker/tests/minus_op_wrong_type_err.vv:15:10: error: mismatched types `map[string]int` and `int literal` + 13 | println(10 - [1, 2, 3]) + 14 | a := map[string]int{} + 15 | println(a - 10) + | ~~~~~~ + 16 | println(10 - a) + 17 | println(-Aaa{}) +vlib/v/checker/tests/minus_op_wrong_type_err.vv:16:10: error: mismatched types `int literal` and `map[string]int` + 14 | a := map[string]int{} + 15 | println(a - 10) + 16 | println(10 - a) + | ~~~~~~ + 17 | println(-Aaa{}) + 18 | println(-a) +vlib/v/checker/tests/minus_op_wrong_type_err.vv:17:10: error: - operator can only be used with numeric types + 15 | println(a - 10) + 16 | println(10 - a) + 17 | println(-Aaa{}) + | ^ + 18 | println(-a) + 19 | println(-Color.red) +vlib/v/checker/tests/minus_op_wrong_type_err.vv:18:10: error: - operator can only be used with numeric types + 16 | println(10 - a) + 17 | println(-Aaa{}) + 18 | println(-a) + | ^ + 19 | println(-Color.red) + 20 | } +vlib/v/checker/tests/minus_op_wrong_type_err.vv:19:10: error: - operator can only be used with numeric types + 17 | println(-Aaa{}) + 18 | println(-a) + 19 | println(-Color.red) + | ^ + 20 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/minus_op_wrong_type_err.vv b/v_windows/v/old/vlib/v/checker/tests/minus_op_wrong_type_err.vv new file mode 100644 index 0000000..e015064 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/minus_op_wrong_type_err.vv @@ -0,0 +1,20 @@ +struct Aaa {} + +enum Color { + red + green + blue +} + +fn main() { + println(Aaa{} - 10) + println(10 - Aaa{}) + println([1, 2, 3] - 10) + println(10 - [1, 2, 3]) + a := map[string]int{} + println(a - 10) + println(10 - a) + println(-Aaa{}) + println(-a) + println(-Color.red) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/mismatched_ptr_op_ptr.out b/v_windows/v/old/vlib/v/checker/tests/mismatched_ptr_op_ptr.out new file mode 100644 index 0000000..a1a1d8a --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/mismatched_ptr_op_ptr.out @@ -0,0 +1,14 @@ +vlib/v/checker/tests/mismatched_ptr_op_ptr.vv:5:17: error: mismatched types `&string` and `string` + 3 | unsafe { + 4 | b := &a + 5 | println(b+*b) + | ~~~ + 6 | println(b+b) + 7 | } +vlib/v/checker/tests/mismatched_ptr_op_ptr.vv:6:17: error: invalid operator `+` to `&string` and `&string` + 4 | b := &a + 5 | println(b+*b) + 6 | println(b+b) + | ~~~ + 7 | } + 8 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/mismatched_ptr_op_ptr.vv b/v_windows/v/old/vlib/v/checker/tests/mismatched_ptr_op_ptr.vv new file mode 100644 index 0000000..21a859d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/mismatched_ptr_op_ptr.vv @@ -0,0 +1,8 @@ +fn main() { + a := '1' + unsafe { + b := &a + println(b+*b) + println(b+b) + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/missing_c_lib_header_1.out b/v_windows/v/old/vlib/v/checker/tests/missing_c_lib_header_1.out new file mode 100644 index 0000000..c6dc4b2 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/missing_c_lib_header_1.out @@ -0,0 +1 @@ +builder error: Header file , needed for module `main` was not found. Please install the corresponding development headers. diff --git a/v_windows/v/old/vlib/v/checker/tests/missing_c_lib_header_1.vv b/v_windows/v/old/vlib/v/checker/tests/missing_c_lib_header_1.vv new file mode 100644 index 0000000..d8bdb47 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/missing_c_lib_header_1.vv @@ -0,0 +1,6 @@ +module main + +// The following header file is intentionally missing. +// The #include does not have the optional explanation part +// after a `#` sign: +#include diff --git a/v_windows/v/old/vlib/v/checker/tests/missing_c_lib_header_with_explanation_2.out b/v_windows/v/old/vlib/v/checker/tests/missing_c_lib_header_with_explanation_2.out new file mode 100644 index 0000000..176ee7b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/missing_c_lib_header_with_explanation_2.out @@ -0,0 +1 @@ +builder error: Header file , needed for module `main` was not found. Please install missing C library. diff --git a/v_windows/v/old/vlib/v/checker/tests/missing_c_lib_header_with_explanation_2.vv b/v_windows/v/old/vlib/v/checker/tests/missing_c_lib_header_with_explanation_2.vv new file mode 100644 index 0000000..a76cc0b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/missing_c_lib_header_with_explanation_2.vv @@ -0,0 +1,6 @@ +module main + +// The following header file is intentionally missing. +// The part after `#` is an explanation message, that V will +// show, when it is not found: +#include # Please install missing C library diff --git a/v_windows/v/old/vlib/v/checker/tests/mod_op_wrong_type_err.out b/v_windows/v/old/vlib/v/checker/tests/mod_op_wrong_type_err.out new file mode 100644 index 0000000..6ae53be --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/mod_op_wrong_type_err.out @@ -0,0 +1,56 @@ +vlib/v/checker/tests/mod_op_wrong_type_err.vv:3:10: error: float modulo not allowed, use math.fmod() instead + 1 | struct Aaa{} + 2 | fn main() { + 3 | println(0.5 % 1) + | ~~~ + 4 | println(1 % 0.5) + 5 | println([1,2,3] % 1) +vlib/v/checker/tests/mod_op_wrong_type_err.vv:4:14: error: float modulo not allowed, use math.fmod() instead + 2 | fn main() { + 3 | println(0.5 % 1) + 4 | println(1 % 0.5) + | ~~~ + 5 | println([1,2,3] % 1) + 6 | println(1 % [1,2,3]) +vlib/v/checker/tests/mod_op_wrong_type_err.vv:5:10: error: mismatched types `[]int` and `int literal` + 3 | println(0.5 % 1) + 4 | println(1 % 0.5) + 5 | println([1,2,3] % 1) + | ~~~~~~~~~~~ + 6 | println(1 % [1,2,3]) + 7 | a := Aaa{} +vlib/v/checker/tests/mod_op_wrong_type_err.vv:6:10: error: mismatched types `int literal` and `[]int` + 4 | println(1 % 0.5) + 5 | println([1,2,3] % 1) + 6 | println(1 % [1,2,3]) + | ~~~~~~~~~~~ + 7 | a := Aaa{} + 8 | println(a % 1) +vlib/v/checker/tests/mod_op_wrong_type_err.vv:8:10: error: mismatched types `Aaa` and `int literal` + 6 | println(1 % [1,2,3]) + 7 | a := Aaa{} + 8 | println(a % 1) + | ~~~~~ + 9 | println(1 % a) + 10 | b := map[string]int +vlib/v/checker/tests/mod_op_wrong_type_err.vv:9:10: error: mismatched types `int literal` and `Aaa` + 7 | a := Aaa{} + 8 | println(a % 1) + 9 | println(1 % a) + | ~~~~~ + 10 | b := map[string]int + 11 | println(b % 1) +vlib/v/checker/tests/mod_op_wrong_type_err.vv:11:10: error: mismatched types `map[string]int` and `int literal` + 9 | println(1 % a) + 10 | b := map[string]int + 11 | println(b % 1) + | ~~~~~ + 12 | println(1 % b) + 13 | } +vlib/v/checker/tests/mod_op_wrong_type_err.vv:12:10: error: mismatched types `int literal` and `map[string]int` + 10 | b := map[string]int + 11 | println(b % 1) + 12 | println(1 % b) + | ~~~~~ + 13 | } + diff --git a/v_windows/v/old/vlib/v/checker/tests/mod_op_wrong_type_err.vv b/v_windows/v/old/vlib/v/checker/tests/mod_op_wrong_type_err.vv new file mode 100644 index 0000000..b699714 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/mod_op_wrong_type_err.vv @@ -0,0 +1,13 @@ +struct Aaa{} +fn main() { + println(0.5 % 1) + println(1 % 0.5) + println([1,2,3] % 1) + println(1 % [1,2,3]) + a := Aaa{} + println(a % 1) + println(1 % a) + b := map[string]int + println(b % 1) + println(1 % b) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/modify_const_with_ref.out b/v_windows/v/old/vlib/v/checker/tests/modify_const_with_ref.out new file mode 100644 index 0000000..c54a42d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/modify_const_with_ref.out @@ -0,0 +1,14 @@ +vlib/v/checker/tests/modify_const_with_ref.vv:11:11: error: `constant` is immutable, cannot have a mutable reference to it + 9 | mut unused_var := Foo{} + 10 | unused_var = Foo{} + 11 | mut c := &constant + | ^ + 12 | c.value = 200 + 13 | } +vlib/v/checker/tests/modify_const_with_ref.vv:9:6: error: unused variable: `unused_var` + 7 | + 8 | fn main() { + 9 | mut unused_var := Foo{} + | ~~~~~~~~~~ + 10 | unused_var = Foo{} + 11 | mut c := &constant diff --git a/v_windows/v/old/vlib/v/checker/tests/modify_const_with_ref.vv b/v_windows/v/old/vlib/v/checker/tests/modify_const_with_ref.vv new file mode 100644 index 0000000..872460e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/modify_const_with_ref.vv @@ -0,0 +1,13 @@ +struct Foo { +mut: + value int +} + +const constant = Foo{ 100 } + +fn main() { + mut unused_var := Foo{} + unused_var = Foo{} + mut c := &constant + c.value = 200 +} diff --git a/v_windows/v/old/vlib/v/checker/tests/module_not_at_same_line_err.out b/v_windows/v/old/vlib/v/checker/tests/module_not_at_same_line_err.out new file mode 100644 index 0000000..6efd5f2 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/module_not_at_same_line_err.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/module_not_at_same_line_err.vv:2:1: error: `module` and `main` must be at same line + 1 | module + 2 | main + | ~~~~ + 3 | fn main() { + 4 | println('hello, world') diff --git a/v_windows/v/old/vlib/v/checker/tests/module_not_at_same_line_err.vv b/v_windows/v/old/vlib/v/checker/tests/module_not_at_same_line_err.vv new file mode 100644 index 0000000..a0e52f9 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/module_not_at_same_line_err.vv @@ -0,0 +1,5 @@ +module +main +fn main() { + println('hello, world') +} diff --git a/v_windows/v/old/vlib/v/checker/tests/modules/module_alias_started_with_underscore.out b/v_windows/v/old/vlib/v/checker/tests/modules/module_alias_started_with_underscore.out new file mode 100644 index 0000000..a0cc315 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/modules/module_alias_started_with_underscore.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/modules/module_alias_started_with_underscore/main.v:3:1: error: module alias `_` cannot start with `_` + 1 | module main + 2 | + 3 | import underscore as _ + | ~~~~~~~~~~~~~~~~~~~~~~ + 4 | + 5 | fn main() { diff --git a/v_windows/v/old/vlib/v/checker/tests/modules/module_alias_started_with_underscore/main.v b/v_windows/v/old/vlib/v/checker/tests/modules/module_alias_started_with_underscore/main.v new file mode 100644 index 0000000..39f80a1 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/modules/module_alias_started_with_underscore/main.v @@ -0,0 +1,7 @@ +module main + +import underscore as _ + +fn main() { + _.foo() +} diff --git a/v_windows/v/old/vlib/v/checker/tests/modules/module_alias_started_with_underscore/underscore.v b/v_windows/v/old/vlib/v/checker/tests/modules/module_alias_started_with_underscore/underscore.v new file mode 100644 index 0000000..ec83c1c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/modules/module_alias_started_with_underscore/underscore.v @@ -0,0 +1,5 @@ +module underscore + +pub fn foo() { + println('bar') +} diff --git a/v_windows/v/old/vlib/v/checker/tests/modules/overload_return_type.out b/v_windows/v/old/vlib/v/checker/tests/modules/overload_return_type.out new file mode 100644 index 0000000..dbdf6a5 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/modules/overload_return_type.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/modules/overload_return_type/main.v:14:8: error: cannot assign to `two`: expected `point.Point`, not `int` + 12 | y: 1 + 13 | } + 14 | two = one + two + | ~~~~~~~~~ + 15 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/modules/overload_return_type/main.v b/v_windows/v/old/vlib/v/checker/tests/modules/overload_return_type/main.v new file mode 100644 index 0000000..b682c49 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/modules/overload_return_type/main.v @@ -0,0 +1,15 @@ +module main + +import point { Point } + +fn main() { + one := Point{ + x: 1 + y: 2 + } + mut two := Point{ + x: 5 + y: 1 + } + two = one + two +} diff --git a/v_windows/v/old/vlib/v/checker/tests/modules/overload_return_type/point.v b/v_windows/v/old/vlib/v/checker/tests/modules/overload_return_type/point.v new file mode 100644 index 0000000..a26932b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/modules/overload_return_type/point.v @@ -0,0 +1,11 @@ +module point + +pub struct Point { +mut: + x int + y int +} + +pub fn (a Point) + (b Point) int { + return a.x + b.x +} diff --git a/v_windows/v/old/vlib/v/checker/tests/mul_op_wrong_type_err.out b/v_windows/v/old/vlib/v/checker/tests/mul_op_wrong_type_err.out new file mode 100644 index 0000000..6bd5ff9 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/mul_op_wrong_type_err.out @@ -0,0 +1,57 @@ +vlib/v/checker/tests/mul_op_wrong_type_err.vv:5:13: error: mismatched types `Aaa` and `int literal` + 3 | struct Aaa{} + 4 | fn main() { + 5 | println(Aaa{} * 10) + | ~~~~~~~~~~ + 6 | println(10 * Aaa{}) + 7 | println([1,2,3] * 10) +vlib/v/checker/tests/mul_op_wrong_type_err.vv:6:13: error: mismatched types `int literal` and `Aaa` + 4 | fn main() { + 5 | println(Aaa{} * 10) + 6 | println(10 * Aaa{}) + | ~~~~~~~~~~ + 7 | println([1,2,3] * 10) + 8 | println(10 * [1,2,3]) +vlib/v/checker/tests/mul_op_wrong_type_err.vv:7:13: error: mismatched types `[]int` and `int literal` + 5 | println(Aaa{} * 10) + 6 | println(10 * Aaa{}) + 7 | println([1,2,3] * 10) + | ~~~~~~~~~~~~ + 8 | println(10 * [1,2,3]) + 9 | a := map[string]int +vlib/v/checker/tests/mul_op_wrong_type_err.vv:8:13: error: mismatched types `int literal` and `[]int` + 6 | println(10 * Aaa{}) + 7 | println([1,2,3] * 10) + 8 | println(10 * [1,2,3]) + | ~~~~~~~~~~~~ + 9 | a := map[string]int + 10 | println(a * 10) +vlib/v/checker/tests/mul_op_wrong_type_err.vv:10:13: error: mismatched types `map[string]int` and `int literal` + 8 | println(10 * [1,2,3]) + 9 | a := map[string]int + 10 | println(a * 10) + | ~~~~~~ + 11 | println(10 * a) + 12 | c1 := cmplx.complex(1,-2) +vlib/v/checker/tests/mul_op_wrong_type_err.vv:11:13: error: mismatched types `int literal` and `map[string]int` + 9 | a := map[string]int + 10 | println(a * 10) + 11 | println(10 * a) + | ~~~~~~ + 12 | c1 := cmplx.complex(1,-2) + 13 | c2 := c1 * 2.0 +vlib/v/checker/tests/mul_op_wrong_type_err.vv:13:8: error: infix expr: cannot use `float literal` (right expression) as `math.complex.Complex` + 11 | println(10 * a) + 12 | c1 := cmplx.complex(1,-2) + 13 | c2 := c1 * 2.0 + | ~~~~~~~~ + 14 | println(c2) + 15 | c3 := 2.0 * c1 +vlib/v/checker/tests/mul_op_wrong_type_err.vv:15:8: error: infix expr: cannot use `math.complex.Complex` (right expression) as `float literal` + 13 | c2 := c1 * 2.0 + 14 | println(c2) + 15 | c3 := 2.0 * c1 + | ~~~~~~~~ + 16 | println(c3) + 17 | } + diff --git a/v_windows/v/old/vlib/v/checker/tests/mul_op_wrong_type_err.vv b/v_windows/v/old/vlib/v/checker/tests/mul_op_wrong_type_err.vv new file mode 100644 index 0000000..a1e3394 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/mul_op_wrong_type_err.vv @@ -0,0 +1,17 @@ +import math +import math.complex as cmplx +struct Aaa{} +fn main() { + println(Aaa{} * 10) + println(10 * Aaa{}) + println([1,2,3] * 10) + println(10 * [1,2,3]) + a := map[string]int + println(a * 10) + println(10 * a) + c1 := cmplx.complex(1,-2) + c2 := c1 * 2.0 + println(c2) + c3 := 2.0 * c1 + println(c3) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/multi_const_field_name_duplicate_err.out b/v_windows/v/old/vlib/v/checker/tests/multi_const_field_name_duplicate_err.out new file mode 100644 index 0000000..731b9f7 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/multi_const_field_name_duplicate_err.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/multi_const_field_name_duplicate_err.vv:2:8: error: duplicate const `aaa` + 1 | const (aaa = 1) + 2 | const (aaa = 2) + | ~~~ + 3 | fn main() { + 4 | println(aaa) diff --git a/v_windows/v/old/vlib/v/checker/tests/multi_const_field_name_duplicate_err.vv b/v_windows/v/old/vlib/v/checker/tests/multi_const_field_name_duplicate_err.vv new file mode 100644 index 0000000..883abac --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/multi_const_field_name_duplicate_err.vv @@ -0,0 +1,5 @@ +const (aaa = 1) +const (aaa = 2) +fn main() { + println(aaa) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/multi_names_err.out b/v_windows/v/old/vlib/v/checker/tests/multi_names_err.out new file mode 100644 index 0000000..a022208 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/multi_names_err.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/multi_names_err.vv:2:4: error: unexpected name `a` + 1 | fn main() { + 2 | a a a a := 1 + | ^ + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/multi_names_err.vv b/v_windows/v/old/vlib/v/checker/tests/multi_names_err.vv new file mode 100644 index 0000000..cff1441 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/multi_names_err.vv @@ -0,0 +1,3 @@ +fn main() { + a a a a := 1 +} diff --git a/v_windows/v/old/vlib/v/checker/tests/multi_value_method_err.out b/v_windows/v/old/vlib/v/checker/tests/multi_value_method_err.out new file mode 100644 index 0000000..f5be76a --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/multi_value_method_err.out @@ -0,0 +1,3 @@ +vlib/v/checker/tests/multi_value_method_err.vv:1:7: error: cannot define method on multi-value + 1 | fn (v (int, int)) f() {} + | ~~~~~~~~~~ diff --git a/v_windows/v/old/vlib/v/checker/tests/multi_value_method_err.vv b/v_windows/v/old/vlib/v/checker/tests/multi_value_method_err.vv new file mode 100644 index 0000000..a84f0b6 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/multi_value_method_err.vv @@ -0,0 +1 @@ +fn (v (int, int)) f() {} diff --git a/v_windows/v/old/vlib/v/checker/tests/mut_arg.out b/v_windows/v/old/vlib/v/checker/tests/mut_arg.out new file mode 100644 index 0000000..f18c285 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/mut_arg.out @@ -0,0 +1,25 @@ +vlib/v/checker/tests/mut_arg.vv:6:3: error: `f` parameter `par` is `mut`, you need to provide `mut` e.g. `mut arg1` + 4 | } + 5 | + 6 | f([3,4]) + | ~~~~~ + 7 | mut a := [1,2] + 8 | f(a) +vlib/v/checker/tests/mut_arg.vv:8:3: error: `f` parameter `par` is `mut`, you need to provide `mut` e.g. `mut arg1` + 6 | f([3,4]) + 7 | mut a := [1,2] + 8 | f(a) + | ^ + 9 | + 10 | g(mut [3,4]) +vlib/v/checker/tests/mut_arg.vv:10:7: error: array literal can not be modified + 8 | f(a) + 9 | + 10 | g(mut [3,4]) + | ~~~~~ + 11 | g(mut a) +vlib/v/checker/tests/mut_arg.vv:11:7: error: `g` parameter `par` is not `mut`, `mut` is not needed` + 9 | + 10 | g(mut [3,4]) + 11 | g(mut a) + | ^ diff --git a/v_windows/v/old/vlib/v/checker/tests/mut_arg.vv b/v_windows/v/old/vlib/v/checker/tests/mut_arg.vv new file mode 100644 index 0000000..0a2afdd --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/mut_arg.vv @@ -0,0 +1,11 @@ +fn f(mut par []int) { +} +fn g(par []int) { +} + +f([3,4]) +mut a := [1,2] +f(a) + +g(mut [3,4]) +g(mut a) diff --git a/v_windows/v/old/vlib/v/checker/tests/mut_args_warning.out b/v_windows/v/old/vlib/v/checker/tests/mut_args_warning.out new file mode 100644 index 0000000..726f4b8 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/mut_args_warning.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/mut_args_warning.vv:1:8: warning: use `mut f Foo` instead of `f mut Foo` + 1 | fn f(x mut []int) { x[0] = 1 } + | ~~~ + 2 | fn main() { + 3 | mut x := [0] diff --git a/v_windows/v/old/vlib/v/checker/tests/mut_args_warning.vv b/v_windows/v/old/vlib/v/checker/tests/mut_args_warning.vv new file mode 100644 index 0000000..c29b27d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/mut_args_warning.vv @@ -0,0 +1,5 @@ +fn f(x mut []int) { x[0] = 1 } +fn main() { + mut x := [0] + f(mut x) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/mut_array_get_element_address_err.out b/v_windows/v/old/vlib/v/checker/tests/mut_array_get_element_address_err.out new file mode 100644 index 0000000..a98d17b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/mut_array_get_element_address_err.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/mut_array_get_element_address_err.vv:3:20: error: cannot take the address of mutable array elements outside unsafe blocks + 1 | fn main() { + 2 | mut arr_int := [int(23), 45, 7, 8] + 3 | ele := &arr_int[1] + | ~~~ + 4 | println(ele) + 5 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/mut_array_get_element_address_err.vv b/v_windows/v/old/vlib/v/checker/tests/mut_array_get_element_address_err.vv new file mode 100644 index 0000000..1afbcb3 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/mut_array_get_element_address_err.vv @@ -0,0 +1,5 @@ +fn main() { + mut arr_int := [int(23), 45, 7, 8] + ele := &arr_int[1] + println(ele) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/mut_int.out b/v_windows/v/old/vlib/v/checker/tests/mut_int.out new file mode 100644 index 0000000..20a092c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/mut_int.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/mut_int.vv:1:14: error: mutable arguments are only allowed for arrays, interfaces, maps, pointers, structs or their aliases +return values instead: `fn foo(mut n int) {` => `fn foo(n int) int {` + 1 | fn foo(mut x int) { + | ~~~ + 2 | } + 3 | diff --git a/v_windows/v/old/vlib/v/checker/tests/mut_int.vv b/v_windows/v/old/vlib/v/checker/tests/mut_int.vv new file mode 100644 index 0000000..a268c5a --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/mut_int.vv @@ -0,0 +1,5 @@ +fn foo(mut x int) { +} + +fn main() { +} diff --git a/v_windows/v/old/vlib/v/checker/tests/mut_map_get_value_address_err.out b/v_windows/v/old/vlib/v/checker/tests/mut_map_get_value_address_err.out new file mode 100644 index 0000000..297c228 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/mut_map_get_value_address_err.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/mut_map_get_value_address_err.vv:3:12: error: cannot take the address of map values + 1 | fn main() { + 2 | mut m := map{'key' : 3} + 3 | a := &m['key'] + | ~~~~~~~ + 4 | println(a) + 5 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/mut_map_get_value_address_err.vv b/v_windows/v/old/vlib/v/checker/tests/mut_map_get_value_address_err.vv new file mode 100644 index 0000000..46e5b77 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/mut_map_get_value_address_err.vv @@ -0,0 +1,5 @@ +fn main() { + mut m := map{'key' : 3} + a := &m['key'] + println(a) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/mut_receiver.out b/v_windows/v/old/vlib/v/checker/tests/mut_receiver.out new file mode 100644 index 0000000..fb160a4 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/mut_receiver.out @@ -0,0 +1,14 @@ +vlib/v/checker/tests/mut_receiver.vv:5:4: warning: use `(mut f Foo)` instead of `(f mut Foo)` + 3 | name string + 4 | } + 5 | fn (f mut Foo) info() { + | ~~~~~~~~~~~ + 6 | f.name = 'foo' + 7 | } +vlib/v/checker/tests/mut_receiver.vv:8:4: error: use `(mut f Foo)` or `(f &Foo)` instead of `(mut f &Foo)` + 6 | f.name = 'foo' + 7 | } + 8 | fn (mut f &Foo) info2() { + | ~~~~~~~~~~~~ + 9 | f.name = 'foo' + 10 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/mut_receiver.vv b/v_windows/v/old/vlib/v/checker/tests/mut_receiver.vv new file mode 100644 index 0000000..fae05df --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/mut_receiver.vv @@ -0,0 +1,13 @@ +struct Foo{ +mut: + name string +} +fn (f mut Foo) info() { + f.name = 'foo' +} +fn (mut f &Foo) info2() { + f.name = 'foo' +} +fn main() { + println('hello, world') +} diff --git a/v_windows/v/old/vlib/v/checker/tests/mut_receiver_lit.out b/v_windows/v/old/vlib/v/checker/tests/mut_receiver_lit.out new file mode 100644 index 0000000..e0cd490 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/mut_receiver_lit.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/mut_receiver_lit.vv:10:1: error: cannot pass expression as `mut` + 8 | } + 9 | + 10 | Box{}.set(0) + | ~~~~~ diff --git a/v_windows/v/old/vlib/v/checker/tests/mut_receiver_lit.vv b/v_windows/v/old/vlib/v/checker/tests/mut_receiver_lit.vv new file mode 100644 index 0000000..e015e71 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/mut_receiver_lit.vv @@ -0,0 +1,10 @@ +struct Box { +mut: + value int +} + +fn (mut box Box) set(value int) { + box.value = value +} + +Box{}.set(0) diff --git a/v_windows/v/old/vlib/v/checker/tests/negative_assign_to_unsigned.out b/v_windows/v/old/vlib/v/checker/tests/negative_assign_to_unsigned.out new file mode 100644 index 0000000..1927447 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/negative_assign_to_unsigned.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/negative_assign_to_unsigned.vv:3:9: error: Cannot assign negative value to unsigned integer type + 1 | fn main() { + 2 | mut u := u32(10) + 3 | u = -10 + | ~~~ + 4 | eprintln(u) + 5 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/negative_assign_to_unsigned.vv b/v_windows/v/old/vlib/v/checker/tests/negative_assign_to_unsigned.vv new file mode 100644 index 0000000..da7646d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/negative_assign_to_unsigned.vv @@ -0,0 +1,5 @@ +fn main() { + mut u := u32(10) + u = -10 + eprintln(u) +} \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/nested_aliases.out b/v_windows/v/old/vlib/v/checker/tests/nested_aliases.out new file mode 100644 index 0000000..da93210 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/nested_aliases.out @@ -0,0 +1,4 @@ +vlib/v/checker/tests/nested_aliases.vv:2:16: error: type `MyInt` is an alias, use the original alias type `int` instead + 1 | type MyInt = int + 2 | type MyMyInt = MyInt + | ~~~~~ diff --git a/v_windows/v/old/vlib/v/checker/tests/nested_aliases.vv b/v_windows/v/old/vlib/v/checker/tests/nested_aliases.vv new file mode 100644 index 0000000..a21d0d3 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/nested_aliases.vv @@ -0,0 +1,2 @@ +type MyInt = int +type MyMyInt = MyInt diff --git a/v_windows/v/old/vlib/v/checker/tests/no_heap_struct.out b/v_windows/v/old/vlib/v/checker/tests/no_heap_struct.out new file mode 100644 index 0000000..8a996a1 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/no_heap_struct.out @@ -0,0 +1,21 @@ +vlib/v/checker/tests/no_heap_struct.vv:13:6: error: `x` cannot be assigned outside `unsafe` blocks as it might refer to an object stored on stack. Consider declaring `Abc` as `[heap]`. + 11 | fn f(x &Abc) St { + 12 | s := St{ + 13 | a: x + | ^ + 14 | } + 15 | return s +vlib/v/checker/tests/no_heap_struct.vv:19:9: error: `x` cannot be returned outside `unsafe` blocks as it might refer to an object stored on stack. Consider declaring `Abc` as `[heap]`. + 17 | + 18 | fn g(mut x Abc) &Abc { + 19 | return x + | ^ + 20 | } + 21 | +vlib/v/checker/tests/no_heap_struct.vv:23:7: error: `x` cannot be assigned outside `unsafe` blocks as it might refer to an object stored on stack. Consider declaring `Abc` as `[heap]`. + 21 | + 22 | fn h(x &Abc) &Abc { + 23 | y := x + | ^ + 24 | return y + 25 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/no_heap_struct.vv b/v_windows/v/old/vlib/v/checker/tests/no_heap_struct.vv new file mode 100644 index 0000000..ce1d474 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/no_heap_struct.vv @@ -0,0 +1,25 @@ +struct Abc { +mut: + n int +} + +struct St { +mut: + a &Abc +} + +fn f(x &Abc) St { + s := St{ + a: x + } + return s +} + +fn g(mut x Abc) &Abc { + return x +} + +fn h(x &Abc) &Abc { + y := x + return y +} diff --git a/v_windows/v/old/vlib/v/checker/tests/no_interface_instantiation_a.out b/v_windows/v/old/vlib/v/checker/tests/no_interface_instantiation_a.out new file mode 100644 index 0000000..e01c3a7 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/no_interface_instantiation_a.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/no_interface_instantiation_a.vv:4:9: error: cannot instantiate interface `Speaker` + 2 | + 3 | fn main() { + 4 | _ = Speaker{} + | ~~~~~~~~~ + 5 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/no_interface_instantiation_a.vv b/v_windows/v/old/vlib/v/checker/tests/no_interface_instantiation_a.vv new file mode 100644 index 0000000..1d98644 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/no_interface_instantiation_a.vv @@ -0,0 +1,5 @@ +interface Speaker {} + +fn main() { + _ = Speaker{} +} diff --git a/v_windows/v/old/vlib/v/checker/tests/no_interface_instantiation_b.out b/v_windows/v/old/vlib/v/checker/tests/no_interface_instantiation_b.out new file mode 100644 index 0000000..0a8f1b9 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/no_interface_instantiation_b.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/no_interface_instantiation_b.vv:6:5: error: expected 1 arguments, but got 0 + 4 | + 5 | fn main() { + 6 | my_fn() + | ~~~~~~~ + 7 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/no_interface_instantiation_b.vv b/v_windows/v/old/vlib/v/checker/tests/no_interface_instantiation_b.vv new file mode 100644 index 0000000..4c73538 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/no_interface_instantiation_b.vv @@ -0,0 +1,7 @@ +interface Speaker {} + +fn my_fn(s Speaker) {} + +fn main() { + my_fn() +} diff --git a/v_windows/v/old/vlib/v/checker/tests/no_interface_instantiation_c.out b/v_windows/v/old/vlib/v/checker/tests/no_interface_instantiation_c.out new file mode 100644 index 0000000..11f0568 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/no_interface_instantiation_c.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/no_interface_instantiation_c.vv:9:9: error: cannot instantiate interface `Speaker` + 7 | fn main() { + 8 | my_fn( + 9 | speak: 1 + | ~~~~~~~~ + 10 | ) + 11 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/no_interface_instantiation_c.vv b/v_windows/v/old/vlib/v/checker/tests/no_interface_instantiation_c.vv new file mode 100644 index 0000000..0eb6eec --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/no_interface_instantiation_c.vv @@ -0,0 +1,11 @@ +interface Speaker { + speak() +} + +fn my_fn(s Speaker) {} + +fn main() { + my_fn( + speak: 1 + ) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/no_interface_str.out b/v_windows/v/old/vlib/v/checker/tests/no_interface_str.out new file mode 100644 index 0000000..37b33f2 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/no_interface_str.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/no_interface_str.vv:18:12: error: interface `Animal` does not have a .str() method. Use typeof() instead + 16 | fn moin() { + 17 | a := get_animal() + 18 | println(a.str()) + | ~~~~~ + 19 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/no_interface_str.vv b/v_windows/v/old/vlib/v/checker/tests/no_interface_str.vv new file mode 100644 index 0000000..b0da84a --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/no_interface_str.vv @@ -0,0 +1,19 @@ +interface Animal { + speak() +} + +struct Cow { +} + +fn (c Cow)speak() { + println('moo') +} + +fn get_animal() Animal { + return Cow{} +} + +fn moin() { + a := get_animal() + println(a.str()) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/no_main_mod.out b/v_windows/v/old/vlib/v/checker/tests/no_main_mod.out new file mode 100644 index 0000000..549575d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/no_main_mod.out @@ -0,0 +1,3 @@ +vlib/v/checker/tests/no_main_mod.vv:1:1: error: project must include a `main` module or be a shared library (compile with `v -shared`) + 1 | module a + | ^ diff --git a/v_windows/v/old/vlib/v/checker/tests/no_main_mod.vv b/v_windows/v/old/vlib/v/checker/tests/no_main_mod.vv new file mode 100644 index 0000000..a32281d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/no_main_mod.vv @@ -0,0 +1 @@ +module a diff --git a/v_windows/v/old/vlib/v/checker/tests/no_main_println_err.out b/v_windows/v/old/vlib/v/checker/tests/no_main_println_err.out new file mode 100644 index 0000000..5b0d71e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/no_main_println_err.out @@ -0,0 +1,3 @@ +vlib/v/checker/tests/no_main_println_err.vv:1:5: error: expected 1 arguments, but got 0 + 1 | println() + | ~~~~~~~~~ \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/no_main_println_err.vv b/v_windows/v/old/vlib/v/checker/tests/no_main_println_err.vv new file mode 100644 index 0000000..2ac13fb --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/no_main_println_err.vv @@ -0,0 +1 @@ + println() diff --git a/v_windows/v/old/vlib/v/checker/tests/no_method_on_interface_propagation.out b/v_windows/v/old/vlib/v/checker/tests/no_method_on_interface_propagation.out new file mode 100644 index 0000000..b55e1d4 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/no_method_on_interface_propagation.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/no_method_on_interface_propagation.vv:18:5: error: unknown method or field: `Cat.foo` + 16 | a := new_animal('persian') + 17 | if a is Cat { + 18 | a.foo() + | ~~~~~ + 19 | } + 20 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/no_method_on_interface_propagation.vv b/v_windows/v/old/vlib/v/checker/tests/no_method_on_interface_propagation.vv new file mode 100644 index 0000000..a9673df --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/no_method_on_interface_propagation.vv @@ -0,0 +1,20 @@ +struct Cat { + breed string +} + +interface Animal { + breed string +} + +fn (a Animal) foo() {} + +fn new_animal(breed string) Animal { + return &Cat{breed} +} + +fn test_methods_on_interfaces_dont_exist_on_implementers() { + a := new_animal('persian') + if a is Cat { + a.foo() + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/no_pub_in_main.out b/v_windows/v/old/vlib/v/checker/tests/no_pub_in_main.out new file mode 100644 index 0000000..e1da080 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/no_pub_in_main.out @@ -0,0 +1,49 @@ +vlib/v/checker/tests/no_pub_in_main.vv:3:1: error: type alias `Integer` in module main cannot be declared public + 1 | module main + 2 | + 3 | pub type Integer = int + | ~~~~~~~~~~~~~~~~ + 4 | + 5 | pub type Float = f32 | f64 +vlib/v/checker/tests/no_pub_in_main.vv:5:1: error: sum type `Float` in module main cannot be declared public + 3 | pub type Integer = int + 4 | + 5 | pub type Float = f32 | f64 + | ~~~~~~~~~~~~~~ + 6 | + 7 | // Buggy ATM +vlib/v/checker/tests/no_pub_in_main.vv:10:1: error: enum `Color` in module main cannot be declared public + 8 | // pub type Fn = fn () int + 9 | + 10 | pub enum Color { + | ~~~~~~~~~~~~~~ + 11 | red + 12 | green +vlib/v/checker/tests/no_pub_in_main.vv:16:1: error: const in module main cannot be declared public + 14 | } + 15 | + 16 | pub const ( + | ~~~~~~~~~ + 17 | w = 'world' + 18 | ) +vlib/v/checker/tests/no_pub_in_main.vv:20:1: error: function `my_fn` in module main cannot be declared public + 18 | ) + 19 | + 20 | pub fn my_fn() int { + | ~~~~~~~~~~~~~~~~~~ + 21 | return 1 + 22 | } +vlib/v/checker/tests/no_pub_in_main.vv:24:1: error: function `main` cannot be declared public + 22 | } + 23 | + 24 | pub fn main() { + | ~~~~~~~~~~~~~ + 25 | println('main') + 26 | } +vlib/v/checker/tests/no_pub_in_main.vv:28:1: error: struct `MyStruct` in module main cannot be declared public + 26 | } + 27 | + 28 | pub struct MyStruct { + | ~~~~~~~~~~~~~~~~~~~ + 29 | field int + 30 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/no_warning_for_in_mut_var_unused.out b/v_windows/v/old/vlib/v/checker/tests/no_warning_for_in_mut_var_unused.out new file mode 100644 index 0000000..e69de29 diff --git a/v_windows/v/old/vlib/v/checker/tests/no_warning_for_in_mut_var_unused.vv b/v_windows/v/old/vlib/v/checker/tests/no_warning_for_in_mut_var_unused.vv new file mode 100644 index 0000000..3b66ce2 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/no_warning_for_in_mut_var_unused.vv @@ -0,0 +1,7 @@ +fn main() { + mut arr := [1, 2, 3] + for mut v in arr { + v = 2 + } + println(arr) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/non_lvalue_as_voidptr.out b/v_windows/v/old/vlib/v/checker/tests/non_lvalue_as_voidptr.out new file mode 100644 index 0000000..a885eb7 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/non_lvalue_as_voidptr.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/non_lvalue_as_voidptr.vv:5:13: error: expression cannot be passed as `voidptr` + 3 | } + 4 | + 5 | println(add(5, 10)) + | ^ diff --git a/v_windows/v/old/vlib/v/checker/tests/non_lvalue_as_voidptr.vv b/v_windows/v/old/vlib/v/checker/tests/non_lvalue_as_voidptr.vv new file mode 100644 index 0000000..55de9f3 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/non_lvalue_as_voidptr.vv @@ -0,0 +1,5 @@ +fn add(a voidptr, b voidptr) int { + return int(a) + int(b) +} + +println(add(5, 10)) diff --git a/v_windows/v/old/vlib/v/checker/tests/non_matching_functional_args.out b/v_windows/v/old/vlib/v/checker/tests/non_matching_functional_args.out new file mode 100644 index 0000000..7d155d0 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/non_matching_functional_args.out @@ -0,0 +1,16 @@ +vlib/v/checker/tests/non_matching_functional_args.vv:27:6: error: cannot use `fn (mut Table)` as `fn (Table)` in argument 1 to `sum` + 25 | + 26 | fn main() { + 27 | sum(fn (mut t Table) { + | ~~~~~~~~~~~~~~~~~~ + 28 | t.rename() + 29 | println(t.name) +Details: `main.MyFn`'s expected fn argument: `zzzz` is NOT a pointer, but the passed fn argument: `t` is a pointer +vlib/v/checker/tests/non_matching_functional_args.vv:31:6: error: cannot use `fn (mut Table)` as `fn (Table)` in argument 1 to `sum` + 29 | println(t.name) + 30 | }) + 31 | sum(xxx) + | ~~~ + 32 | sum(yyy) + 33 | } +Details: `main.MyFn`'s expected fn argument: `zzzz` is NOT a pointer, but the passed fn argument: `mytable` is a pointer diff --git a/v_windows/v/old/vlib/v/checker/tests/non_matching_functional_args.vv b/v_windows/v/old/vlib/v/checker/tests/non_matching_functional_args.vv new file mode 100644 index 0000000..a0e90ff --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/non_matching_functional_args.vv @@ -0,0 +1,33 @@ +struct Table { +pub mut: + name string +} + +type MyFn = fn (zzzz Table) + +fn (mut t Table) rename() { + t.name = 'abc' +} + +fn yyy(t Table) { + println(t.name) +} + +fn xxx(mut mytable Table) { + mytable.rename() + println(mytable.name) +} + +fn sum(myfn MyFn) { + mut t := Table{} + myfn(t) +} + +fn main() { + sum(fn (mut t Table) { + t.rename() + println(t.name) + }) + sum(xxx) + sum(yyy) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/none_type_cast_err.out b/v_windows/v/old/vlib/v/checker/tests/none_type_cast_err.out new file mode 100644 index 0000000..0d0ba86 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/none_type_cast_err.out @@ -0,0 +1,75 @@ +vlib/v/checker/tests/none_type_cast_err.vv:2:7: error: cannot cast `none` to `string` + 1 | fn main() { + 2 | _ := string(none) + | ~~~~~~~~~~~~ + 3 | _ := int(none) + 4 | _ := i8(none) +vlib/v/checker/tests/none_type_cast_err.vv:3:7: error: cannot cast `none` to `int` + 1 | fn main() { + 2 | _ := string(none) + 3 | _ := int(none) + | ~~~~~~~~~ + 4 | _ := i8(none) + 5 | _ := i16(none) +vlib/v/checker/tests/none_type_cast_err.vv:4:7: error: cannot cast `none` to `i8` + 2 | _ := string(none) + 3 | _ := int(none) + 4 | _ := i8(none) + | ~~~~~~~~ + 5 | _ := i16(none) + 6 | _ := i64(none) +vlib/v/checker/tests/none_type_cast_err.vv:5:7: error: cannot cast `none` to `i16` + 3 | _ := int(none) + 4 | _ := i8(none) + 5 | _ := i16(none) + | ~~~~~~~~~ + 6 | _ := i64(none) + 7 | _ := u16(none) +vlib/v/checker/tests/none_type_cast_err.vv:6:7: error: cannot cast `none` to `i64` + 4 | _ := i8(none) + 5 | _ := i16(none) + 6 | _ := i64(none) + | ~~~~~~~~~ + 7 | _ := u16(none) + 8 | _ := u32(none) +vlib/v/checker/tests/none_type_cast_err.vv:7:7: error: cannot cast `none` to `u16` + 5 | _ := i16(none) + 6 | _ := i64(none) + 7 | _ := u16(none) + | ~~~~~~~~~ + 8 | _ := u32(none) + 9 | _ := u64(none) +vlib/v/checker/tests/none_type_cast_err.vv:8:7: error: cannot cast `none` to `u32` + 6 | _ := i64(none) + 7 | _ := u16(none) + 8 | _ := u32(none) + | ~~~~~~~~~ + 9 | _ := u64(none) + 10 | _ := rune(none) +vlib/v/checker/tests/none_type_cast_err.vv:9:7: error: cannot cast `none` to `u64` + 7 | _ := u16(none) + 8 | _ := u32(none) + 9 | _ := u64(none) + | ~~~~~~~~~ + 10 | _ := rune(none) + 11 | _ := f32(none) +vlib/v/checker/tests/none_type_cast_err.vv:10:7: error: cannot cast `none` to `rune` + 8 | _ := u32(none) + 9 | _ := u64(none) + 10 | _ := rune(none) + | ~~~~~~~~~~ + 11 | _ := f32(none) + 12 | _ := f64(none) +vlib/v/checker/tests/none_type_cast_err.vv:11:7: error: cannot cast `none` to `f32` + 9 | _ := u64(none) + 10 | _ := rune(none) + 11 | _ := f32(none) + | ~~~~~~~~~ + 12 | _ := f64(none) + 13 | } +vlib/v/checker/tests/none_type_cast_err.vv:12:7: error: cannot cast `none` to `f64` + 10 | _ := rune(none) + 11 | _ := f32(none) + 12 | _ := f64(none) + | ~~~~~~~~~ + 13 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/none_type_cast_err.vv b/v_windows/v/old/vlib/v/checker/tests/none_type_cast_err.vv new file mode 100644 index 0000000..953b369 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/none_type_cast_err.vv @@ -0,0 +1,13 @@ +fn main() { + _ := string(none) + _ := int(none) + _ := i8(none) + _ := i16(none) + _ := i64(none) + _ := u16(none) + _ := u32(none) + _ := u64(none) + _ := rune(none) + _ := f32(none) + _ := f64(none) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/noreturn_with_non_empty_loop_at_end.out b/v_windows/v/old/vlib/v/checker/tests/noreturn_with_non_empty_loop_at_end.out new file mode 100644 index 0000000..02fbccc --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/noreturn_with_non_empty_loop_at_end.out @@ -0,0 +1,13 @@ +vlib/v/checker/tests/noreturn_with_non_empty_loop_at_end.vv:4:6: error: [noreturn] functions should end with a call to another [noreturn] function, or with an infinite `for {}` loop + 2 | fn another() { + 3 | eprintln(@FN) + 4 | for { + | ^ + 5 | break + 6 | } +vlib/v/checker/tests/noreturn_with_non_empty_loop_at_end.vv:18:2: error: unreachable code after a [noreturn] call + 16 | eprintln('start') + 17 | abc() + 18 | eprintln('done') + | ~~~~~~~~~~~~~~~~ + 19 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/noreturn_with_non_empty_loop_at_end.vv b/v_windows/v/old/vlib/v/checker/tests/noreturn_with_non_empty_loop_at_end.vv new file mode 100644 index 0000000..84f413c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/noreturn_with_non_empty_loop_at_end.vv @@ -0,0 +1,19 @@ +[noreturn] +fn another() { + eprintln(@FN) + for { + break + } +} + +[noreturn] +fn abc() { + eprintln(@FN) + another() +} + +fn main() { + eprintln('start') + abc() + eprintln('done') +} diff --git a/v_windows/v/old/vlib/v/checker/tests/noreturn_with_return.out b/v_windows/v/old/vlib/v/checker/tests/noreturn_with_return.out new file mode 100644 index 0000000..d8c3279 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/noreturn_with_return.out @@ -0,0 +1,19 @@ +vlib/v/checker/tests/noreturn_with_return.vv:2:1: error: [noreturn] functions cannot use return statements + 1 | [noreturn] + 2 | fn another() { + | ~~~~~~~~~~~~ + 3 | eprintln(@FN) + 4 | // for{} +vlib/v/checker/tests/noreturn_with_return.vv:6:2: error: [noreturn] functions should end with a call to another [noreturn] function, or with an infinite `for {}` loop + 4 | // for{} + 5 | // exit(0) + 6 | return + | ~~~~~~ + 7 | } + 8 | +vlib/v/checker/tests/noreturn_with_return.vv:18:2: error: unreachable code after a [noreturn] call + 16 | eprintln('start') + 17 | abc() + 18 | eprintln('done') + | ~~~~~~~~~~~~~~~~ + 19 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/noreturn_with_return.vv b/v_windows/v/old/vlib/v/checker/tests/noreturn_with_return.vv new file mode 100644 index 0000000..c5fdaa7 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/noreturn_with_return.vv @@ -0,0 +1,19 @@ +[noreturn] +fn another() { + eprintln(@FN) + // for{} + // exit(0) + return +} + +[noreturn] +fn abc() { + eprintln(@FN) + another() +} + +fn main() { + eprintln('start') + abc() + eprintln('done') +} diff --git a/v_windows/v/old/vlib/v/checker/tests/noreturn_without_loop_or_another_noreturn_at_end.out b/v_windows/v/old/vlib/v/checker/tests/noreturn_without_loop_or_another_noreturn_at_end.out new file mode 100644 index 0000000..6215215 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/noreturn_without_loop_or_another_noreturn_at_end.out @@ -0,0 +1,13 @@ +vlib/v/checker/tests/noreturn_without_loop_or_another_noreturn_at_end.vv:3:2: error: [noreturn] functions should end with a call to another [noreturn] function, or with an infinite `for {}` loop + 1 | [noreturn] + 2 | fn another() { + 3 | eprintln(@FN) + | ~~~~~~~~~~~~~ + 4 | } + 5 | +vlib/v/checker/tests/noreturn_without_loop_or_another_noreturn_at_end.vv:15:2: error: unreachable code after a [noreturn] call + 13 | eprintln('start') + 14 | abc() + 15 | eprintln('done') + | ~~~~~~~~~~~~~~~~ + 16 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/noreturn_without_loop_or_another_noreturn_at_end.vv b/v_windows/v/old/vlib/v/checker/tests/noreturn_without_loop_or_another_noreturn_at_end.vv new file mode 100644 index 0000000..d764d7d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/noreturn_without_loop_or_another_noreturn_at_end.vv @@ -0,0 +1,16 @@ +[noreturn] +fn another() { + eprintln(@FN) +} + +[noreturn] +fn abc() { + eprintln(@FN) + another() +} + +fn main() { + eprintln('start') + abc() + eprintln('done') +} diff --git a/v_windows/v/old/vlib/v/checker/tests/oct_lit_without_digit_err.out b/v_windows/v/old/vlib/v/checker/tests/oct_lit_without_digit_err.out new file mode 100644 index 0000000..29eac2d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/oct_lit_without_digit_err.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/oct_lit_without_digit_err.vv:2:14: error: number part of this octal is not provided + 1 | fn main() { + 2 | println(0o) + | ^ + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/oct_lit_without_digit_err.vv b/v_windows/v/old/vlib/v/checker/tests/oct_lit_without_digit_err.vv new file mode 100644 index 0000000..195a010 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/oct_lit_without_digit_err.vv @@ -0,0 +1,3 @@ +fn main() { + println(0o) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/oct_lit_wrong_digit_err.out b/v_windows/v/old/vlib/v/checker/tests/oct_lit_wrong_digit_err.out new file mode 100644 index 0000000..0dbccf9 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/oct_lit_wrong_digit_err.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/oct_lit_wrong_digit_err.vv:2:18: error: this octal number has unsuitable digit `8` + 1 | fn main() { + 2 | println(0o1118) + | ^ + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/oct_lit_wrong_digit_err.vv b/v_windows/v/old/vlib/v/checker/tests/oct_lit_wrong_digit_err.vv new file mode 100644 index 0000000..235e1a9 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/oct_lit_wrong_digit_err.vv @@ -0,0 +1,3 @@ +fn main() { + println(0o1118) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/optional_fn_err.out b/v_windows/v/old/vlib/v/checker/tests/optional_fn_err.out new file mode 100644 index 0000000..3e47b49 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/optional_fn_err.out @@ -0,0 +1,168 @@ +vlib/v/checker/tests/optional_fn_err.vv:13:16: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 11 | + 12 | const ( + 13 | const_value = bar(0) + | ~~~~~~ + 14 | ) + 15 | +vlib/v/checker/tests/optional_fn_err.vv:19:14: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 17 | f fn (int) + 18 | mut: + 19 | value int = bar(0) + | ~~~~~~ + 20 | opt ?int = bar(0) + 21 | } +vlib/v/checker/tests/optional_fn_err.vv:33:2: error: foo() returns an option, so it should have either an `or {}` block, or `?` at the end + 31 | fn main() { + 32 | // call fn + 33 | foo() + | ~~~~~ + 34 | _ := bar(0) + 35 | println(twice(bar(0))) +vlib/v/checker/tests/optional_fn_err.vv:34:7: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 32 | // call fn + 33 | foo() + 34 | _ := bar(0) + | ~~~~~~ + 35 | println(twice(bar(0))) + 36 | +vlib/v/checker/tests/optional_fn_err.vv:35:16: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 33 | foo() + 34 | _ := bar(0) + 35 | println(twice(bar(0))) + | ~~~~~~ + 36 | + 37 | // anon fn +vlib/v/checker/tests/optional_fn_err.vv:38:16: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 36 | + 37 | // anon fn + 38 | fn (_ int) {}(bar(0)) + | ~~~~~~ + 39 | + 40 | // assert +vlib/v/checker/tests/optional_fn_err.vv:41:9: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 39 | + 40 | // assert + 41 | assert bar(true) + | ~~~~~~~~~ + 42 | + 43 | // struct +vlib/v/checker/tests/optional_fn_err.vv:46:10: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 44 | mut v := Data{ + 45 | f: fn (_ int) {}, + 46 | value: bar(0), + | ~~~~~~ + 47 | opt: bar(0), + 48 | } +vlib/v/checker/tests/optional_fn_err.vv:49:8: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 47 | opt: bar(0), + 48 | } + 49 | v.add(bar(0)) // call method + | ~~~~~~ + 50 | v.f(bar(0)) // call fn field + 51 | +vlib/v/checker/tests/optional_fn_err.vv:50:6: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 48 | } + 49 | v.add(bar(0)) // call method + 50 | v.f(bar(0)) // call fn field + | ~~~~~~ + 51 | + 52 | // array +vlib/v/checker/tests/optional_fn_err.vv:54:9: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 52 | // array + 53 | mut arr := [1, 2] + 54 | arr << bar(0) + | ~~~~~~ + 55 | // init + 56 | _ := [bar(0)] +vlib/v/checker/tests/optional_fn_err.vv:56:8: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 54 | arr << bar(0) + 55 | // init + 56 | _ := [bar(0)] + | ~~~~~~ + 57 | _ := []int{init: bar(0)} + 58 | _ := [bar(0)]! +vlib/v/checker/tests/optional_fn_err.vv:57:19: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 55 | // init + 56 | _ := [bar(0)] + 57 | _ := []int{init: bar(0)} + | ~~~~~~ + 58 | _ := [bar(0)]! + 59 | _ := [1]int{init: bar(0)} +vlib/v/checker/tests/optional_fn_err.vv:58:8: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 56 | _ := [bar(0)] + 57 | _ := []int{init: bar(0)} + 58 | _ := [bar(0)]! + | ~~~~~~ + 59 | _ := [1]int{init: bar(0)} + 60 | // index +vlib/v/checker/tests/optional_fn_err.vv:61:13: error: cannot use optional as index (array type `[]int`) + 59 | _ := [1]int{init: bar(0)} + 60 | // index + 61 | println(arr[bar(0)]) + | ~~~~~~~~ + 62 | // array builtin methods + 63 | arr.insert(0, bar(0)) +vlib/v/checker/tests/optional_fn_err.vv:63:16: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 61 | println(arr[bar(0)]) + 62 | // array builtin methods + 63 | arr.insert(0, bar(0)) + | ~~~~~~ + 64 | arr.prepend(bar(0)) + 65 | arr.contains(bar(0)) +vlib/v/checker/tests/optional_fn_err.vv:64:14: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 62 | // array builtin methods + 63 | arr.insert(0, bar(0)) + 64 | arr.prepend(bar(0)) + | ~~~~~~ + 65 | arr.contains(bar(0)) + 66 | arr.index(bar(0)) +vlib/v/checker/tests/optional_fn_err.vv:65:15: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 63 | arr.insert(0, bar(0)) + 64 | arr.prepend(bar(0)) + 65 | arr.contains(bar(0)) + | ~~~~~~ + 66 | arr.index(bar(0)) + 67 | println(arr.map(bar(0))) +vlib/v/checker/tests/optional_fn_err.vv:66:12: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 64 | arr.prepend(bar(0)) + 65 | arr.contains(bar(0)) + 66 | arr.index(bar(0)) + | ~~~~~~ + 67 | println(arr.map(bar(0))) + 68 | println(arr.filter(bar(true))) +vlib/v/checker/tests/optional_fn_err.vv:67:18: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 65 | arr.contains(bar(0)) + 66 | arr.index(bar(0)) + 67 | println(arr.map(bar(0))) + | ~~~~~~ + 68 | println(arr.filter(bar(true))) + 69 | println(arr.any(bar(true))) +vlib/v/checker/tests/optional_fn_err.vv:68:21: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 66 | arr.index(bar(0)) + 67 | println(arr.map(bar(0))) + 68 | println(arr.filter(bar(true))) + | ~~~~~~~~~ + 69 | println(arr.any(bar(true))) + 70 | println(arr.all(bar(true))) +vlib/v/checker/tests/optional_fn_err.vv:69:18: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 67 | println(arr.map(bar(0))) + 68 | println(arr.filter(bar(true))) + 69 | println(arr.any(bar(true))) + | ~~~~~~~~~ + 70 | println(arr.all(bar(true))) + 71 | +vlib/v/checker/tests/optional_fn_err.vv:70:18: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 68 | println(arr.filter(bar(true))) + 69 | println(arr.any(bar(true))) + 70 | println(arr.all(bar(true))) + | ~~~~~~~~~ + 71 | + 72 | match bar(0) { +vlib/v/checker/tests/optional_fn_err.vv:72:8: error: bar() returns an option, so it should have either an `or {}` block, or `?` at the end + 70 | println(arr.all(bar(true))) + 71 | + 72 | match bar(0) { + | ~~~~~~ + 73 | 0 { } + 74 | else { } diff --git a/v_windows/v/old/vlib/v/checker/tests/optional_fn_err.vv b/v_windows/v/old/vlib/v/checker/tests/optional_fn_err.vv new file mode 100644 index 0000000..b3d82d1 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/optional_fn_err.vv @@ -0,0 +1,76 @@ + +// use optional without ? or an or block in places where it is not allowed + +fn foo() ? { + println('foo is called') +} + +fn bar(v T) ?T { + return none +} + +const ( + const_value = bar(0) +) + +struct Data { + f fn (int) +mut: + value int = bar(0) + opt ?int = bar(0) +} + +fn (mut v Data) add(n int) { + v.value += n +} + +fn twice(n int) int { + return n * 2 +} + +fn main() { + // call fn + foo() + _ := bar(0) + println(twice(bar(0))) + + // anon fn + fn (_ int) {}(bar(0)) + + // assert + assert bar(true) + + // struct + mut v := Data{ + f: fn (_ int) {}, + value: bar(0), + opt: bar(0), + } + v.add(bar(0)) // call method + v.f(bar(0)) // call fn field + + // array + mut arr := [1, 2] + arr << bar(0) + // init + _ := [bar(0)] + _ := []int{init: bar(0)} + _ := [bar(0)]! + _ := [1]int{init: bar(0)} + // index + println(arr[bar(0)]) + // array builtin methods + arr.insert(0, bar(0)) + arr.prepend(bar(0)) + arr.contains(bar(0)) + arr.index(bar(0)) + println(arr.map(bar(0))) + println(arr.filter(bar(true))) + println(arr.any(bar(true))) + println(arr.all(bar(true))) + + match bar(0) { + 0 { } + else { } + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/optional_in_println_mismatch.out b/v_windows/v/old/vlib/v/checker/tests/optional_in_println_mismatch.out new file mode 100644 index 0000000..8f13586 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/optional_in_println_mismatch.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/optional_in_println_mismatch.vv:6:23: error: wrong return type `string` in the `or {}` block, expected `int` + 4 | + 5 | fn main() { + 6 | println(funcy() or { '' }) + | ~~ + 7 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/optional_in_println_mismatch.vv b/v_windows/v/old/vlib/v/checker/tests/optional_in_println_mismatch.vv new file mode 100644 index 0000000..fd3d067 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/optional_in_println_mismatch.vv @@ -0,0 +1,7 @@ +fn funcy() ?int { + return none +} + +fn main() { + println(funcy() or { '' }) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/optional_interface_mismatch.out b/v_windows/v/old/vlib/v/checker/tests/optional_interface_mismatch.out new file mode 100644 index 0000000..64fe585 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/optional_interface_mismatch.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/optional_interface_mismatch.vv:11:9: error: mismatched types `?MObject` and `string` + 9 | + 10 | fn give_string(line string) ?MObject { + 11 | return if true { 'string' } else { 'string' } + | ~~ + 12 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/optional_interface_mismatch.vv b/v_windows/v/old/vlib/v/checker/tests/optional_interface_mismatch.vv new file mode 100644 index 0000000..1138fa3 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/optional_interface_mismatch.vv @@ -0,0 +1,12 @@ +fn main() { + le_string := give_string('string') or { return } + le_string.unimplemented() +} + +interface MObject { + unimplemented() string +} + +fn give_string(line string) ?MObject { + return if true { 'string' } else { 'string' } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/optional_or_block_mismatch.out b/v_windows/v/old/vlib/v/checker/tests/optional_or_block_mismatch.out new file mode 100644 index 0000000..d48fd8f --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/optional_or_block_mismatch.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/optional_or_block_mismatch.vv:10:18: error: wrong return type `Bar` in the `or {}` block, expected `&Bar` + 8 | + 9 | fn main() { + 10 | x := foo() or { Bar{} } + | ~~~~~ + 11 | println(x) + 12 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/optional_or_block_mismatch.vv b/v_windows/v/old/vlib/v/checker/tests/optional_or_block_mismatch.vv new file mode 100644 index 0000000..849a7aa --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/optional_or_block_mismatch.vv @@ -0,0 +1,12 @@ +module main + +struct Bar {} + +fn foo() ?&Bar { + return none +} + +fn main() { + x := foo() or { Bar{} } + println(x) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/optional_or_block_none_err.out b/v_windows/v/old/vlib/v/checker/tests/optional_or_block_none_err.out new file mode 100644 index 0000000..ed608c3 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/optional_or_block_none_err.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/optional_or_block_none_err.vv:18:3: error: wrong return type `none` in the `or {}` block, expected `Animal` + 16 | fn main() { + 17 | mut dog := new_animal(9) or { + 18 | none + | ~~~~ + 19 | } + 20 | diff --git a/v_windows/v/old/vlib/v/checker/tests/optional_or_block_none_err.vv b/v_windows/v/old/vlib/v/checker/tests/optional_or_block_none_err.vv new file mode 100644 index 0000000..c1970a3 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/optional_or_block_none_err.vv @@ -0,0 +1,22 @@ +module main + +struct Animal { + mut: + height byte +} + +fn new_animal(height byte) ?Animal { + if height < 10 { + return error('Too small to be an animal!') + } + + return Animal{ height: height } +} + +fn main() { + mut dog := new_animal(9) or { + none + } + + println(dog) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/optional_or_block_returns_value_of_incompatible_type.out b/v_windows/v/old/vlib/v/checker/tests/optional_or_block_returns_value_of_incompatible_type.out new file mode 100644 index 0000000..0cd1b38 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/optional_or_block_returns_value_of_incompatible_type.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/optional_or_block_returns_value_of_incompatible_type.vv:13:3: error: the default expression type in the `or` block should be `string`, instead you gave a value of type `int literal` + 11 | // must be of the same type of the return + 12 | // type of the `test_optional` function + 13 | 123 + | ~~~ + 14 | // 'I break things' + 15 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/optional_or_block_returns_value_of_incompatible_type.vv b/v_windows/v/old/vlib/v/checker/tests/optional_or_block_returns_value_of_incompatible_type.vv new file mode 100644 index 0000000..9935bb7 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/optional_or_block_returns_value_of_incompatible_type.vv @@ -0,0 +1,16 @@ +fn test_optional(fail bool) ?string { + if fail { + return error('false') + } + return 'fff' +} + +fn main() { + // a := test_optional(false) or { println(err) } + test_optional(true) or { + // must be of the same type of the return + // type of the `test_optional` function + 123 + // 'I break things' + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/optional_propagate_nested.out b/v_windows/v/old/vlib/v/checker/tests/optional_propagate_nested.out new file mode 100644 index 0000000..f113a89 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/optional_propagate_nested.out @@ -0,0 +1,14 @@ +vlib/v/checker/tests/optional_propagate_nested.vv:10:19: error: to propagate the optional call, `xx_prop` must return an optional + 8 | + 9 | fn xx_prop() string { + 10 | s := ret(raise() ?) + | ^ + 11 | return s + 12 | } +vlib/v/checker/tests/optional_propagate_nested.vv:28:22: error: to propagate the optional call, `aa_propagate` must return an optional + 26 | + 27 | fn (mut s St) aa_propagate() { + 28 | f := retf(s.raise() ?) + | ^ + 29 | s.z = 7.5 + 30 | println(f) diff --git a/v_windows/v/old/vlib/v/checker/tests/optional_propagate_nested.vv b/v_windows/v/old/vlib/v/checker/tests/optional_propagate_nested.vv new file mode 100644 index 0000000..d00ef53 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/optional_propagate_nested.vv @@ -0,0 +1,31 @@ +fn ret(s string) string { + return s +} + +fn raise() ?string { + return none +} + +fn xx_prop() string { + s := ret(raise() ?) + return s +} + +struct St { +mut: + z f64 +} + +fn (mut s St) raise() ?f64 { + return error('some error') +} + +fn retf(f f64) f64 { + return f +} + +fn (mut s St) aa_propagate() { + f := retf(s.raise() ?) + s.z = 7.5 + println(f) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/optional_type_call_err.out b/v_windows/v/old/vlib/v/checker/tests/optional_type_call_err.out new file mode 100644 index 0000000..6550765 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/optional_type_call_err.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/optional_type_call_err.vv:4:5: error: optional type cannot be called directly + 2 | + 3 | fn main() { + 4 | os.ls('.').filter(it.ends_with('.v')) or { return } + | ~~~~~~~ + 5 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/optional_type_call_err.vv b/v_windows/v/old/vlib/v/checker/tests/optional_type_call_err.vv new file mode 100644 index 0000000..1b0cfa0 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/optional_type_call_err.vv @@ -0,0 +1,5 @@ +import os + +fn main() { + os.ls('.').filter(it.ends_with('.v')) or { return } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/or_err.out b/v_windows/v/old/vlib/v/checker/tests/or_err.out new file mode 100644 index 0000000..5aaf5f2 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/or_err.out @@ -0,0 +1,14 @@ +vlib/v/checker/tests/or_err.vv:4:10: error: last statement in the `or {}` block should be an expression of type `&int` or exit parent scope + 2 | return none + 3 | } + 4 | a := f() or { + | ~~~~ + 5 | {} + 6 | } +vlib/v/checker/tests/or_err.vv:11:2: error: wrong return type `rune` in the `or {}` block, expected `&int` + 9 | } + 10 | _ = f() or { + 11 | `.` + | ~~~ + 12 | } + 13 | diff --git a/v_windows/v/old/vlib/v/checker/tests/or_err.vv b/v_windows/v/old/vlib/v/checker/tests/or_err.vv new file mode 100644 index 0000000..035b87e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/or_err.vv @@ -0,0 +1,13 @@ +fn f() ?&int { + return none +} +a := f() or { + {} +} +_ = f() or { + a +} +_ = f() or { + `.` +} + diff --git a/v_windows/v/old/vlib/v/checker/tests/or_expr_types_mismatch.out b/v_windows/v/old/vlib/v/checker/tests/or_expr_types_mismatch.out new file mode 100644 index 0000000..c84faa5 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/or_expr_types_mismatch.out @@ -0,0 +1,14 @@ +vlib/v/checker/tests/or_expr_types_mismatch.vv:3:19: error: wrong return type `none` in the `or {}` block, expected `string` + 1 | fn get_map() ?string { + 2 | m := map{1: 'a', 2: 'b'} + 3 | return m[1] or { none } + | ~~~~ + 4 | } + 5 | +vlib/v/checker/tests/or_expr_types_mismatch.vv:8:19: error: wrong return type `none` in the `or {}` block, expected `int` + 6 | fn get_array() ?int { + 7 | a := [1, 2, 3] + 8 | return a[4] or { none } + | ~~~~ + 9 | } + 10 | diff --git a/v_windows/v/old/vlib/v/checker/tests/or_expr_types_mismatch.vv b/v_windows/v/old/vlib/v/checker/tests/or_expr_types_mismatch.vv new file mode 100644 index 0000000..1b557c3 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/or_expr_types_mismatch.vv @@ -0,0 +1,17 @@ +fn get_map() ?string { + m := map{1: 'a', 2: 'b'} + return m[1] or { none } +} + +fn get_array() ?int { + a := [1, 2, 3] + return a[4] or { none } +} + +fn main() { + map_result := get_map() or { return } + println(map_result) + + array_result := get_array() or { return } + println(array_result) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/orm_empty_struct.out b/v_windows/v/old/vlib/v/checker/tests/orm_empty_struct.out new file mode 100644 index 0000000..26e5131 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/orm_empty_struct.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/orm_empty_struct.vv:9:15: error: V orm: select: empty fields in `Person` + 7 | db := sqlite.connect(':memory:')? + 8 | _ := sql db { + 9 | select from Person + | ~~~~~~ + 10 | } + 11 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/orm_empty_struct.vv b/v_windows/v/old/vlib/v/checker/tests/orm_empty_struct.vv new file mode 100644 index 0000000..ba7b4e7 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/orm_empty_struct.vv @@ -0,0 +1,11 @@ +import sqlite + +struct Person { +} + +fn main() { + db := sqlite.connect(':memory:')? + _ := sql db { + select from Person + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/os_prefix.out b/v_windows/v/old/vlib/v/checker/tests/os_prefix.out new file mode 100644 index 0000000..4b87a6f --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/os_prefix.out @@ -0,0 +1,18 @@ +vlib/v/checker/tests/os_prefix.vv:1:8: warning: module 'os' is imported but never used + 1 | import os + | ~~ + 2 | + 3 | fn main() { +vlib/v/checker/tests/os_prefix.vv:5:12: error: unknown function: execute + 3 | fn main() { + 4 | cmd := "ls" + 5 | result := execute(cmd) + | ~~~~~~~~~~~~ + 6 | println(result) + 7 | } +vlib/v/checker/tests/os_prefix.vv:6:2: error: `println` can not print void expressions + 4 | cmd := "ls" + 5 | result := execute(cmd) + 6 | println(result) + | ~~~~~~~~~~~~~~~ + 7 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/os_prefix.vv b/v_windows/v/old/vlib/v/checker/tests/os_prefix.vv new file mode 100644 index 0000000..b9342d3 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/os_prefix.vv @@ -0,0 +1,7 @@ +import os + +fn main() { + cmd := "ls" + result := execute(cmd) + println(result) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/overflow_int_err.out b/v_windows/v/old/vlib/v/checker/tests/overflow_int_err.out new file mode 100644 index 0000000..b26b097 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/overflow_int_err.out @@ -0,0 +1,14 @@ +vlib/v/checker/tests/overflow_int_err.vv:4:7: error: overflow in implicit type `int`, use explicit type casting instead + 2 | a := -2147483648 + 3 | b := 2147483647 + 4 | c := -2147483649 + | ~~~~~~~~~~~ + 5 | d := 2147483648 + 6 | println(a) +vlib/v/checker/tests/overflow_int_err.vv:5:7: error: overflow in implicit type `int`, use explicit type casting instead + 3 | b := 2147483647 + 4 | c := -2147483649 + 5 | d := 2147483648 + | ~~~~~~~~~~ + 6 | println(a) + 7 | println(b) diff --git a/v_windows/v/old/vlib/v/checker/tests/overflow_int_err.vv b/v_windows/v/old/vlib/v/checker/tests/overflow_int_err.vv new file mode 100644 index 0000000..0537ae3 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/overflow_int_err.vv @@ -0,0 +1,10 @@ +fn main() { + a := -2147483648 + b := 2147483647 + c := -2147483649 + d := 2147483648 + println(a) + println(b) + println(c) + println(d) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/overload_return_type.out b/v_windows/v/old/vlib/v/checker/tests/overload_return_type.out new file mode 100644 index 0000000..547519c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/overload_return_type.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/overload_return_type.vv:14:11: error: cannot assign to `two`: expected `Point`, not `int` + 12 | mut one := Point {x:1, y:2} + 13 | mut two := Point {x:5, y:1} + 14 | two = one + two + | ~~~~~~~~~ + 15 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/overload_return_type.vv b/v_windows/v/old/vlib/v/checker/tests/overload_return_type.vv new file mode 100644 index 0000000..f9ae2f1 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/overload_return_type.vv @@ -0,0 +1,15 @@ +struct Point { + mut: + x int + y int +} + +fn (a Point) +(b Point) int { + return a.x + b.x +} + +fn main() { + mut one := Point {x:1, y:2} + mut two := Point {x:5, y:1} + two = one + two +} diff --git a/v_windows/v/old/vlib/v/checker/tests/oversized_int_lit.out b/v_windows/v/old/vlib/v/checker/tests/oversized_int_lit.out new file mode 100644 index 0000000..30a9622 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/oversized_int_lit.out @@ -0,0 +1,8 @@ +vlib/v/checker/tests/oversized_int_lit.vv:1:8: error: integer literal 18446744073709551616 overflows int + 1 | assert 18446744073709551616 > 0 + | ~~~~~~~~~~~~~~~~~~~~ + 2 | assert -9223372036854775809 < 0 +vlib/v/checker/tests/oversized_int_lit.vv:2:8: error: integer literal -9223372036854775809 overflows int + 1 | assert 18446744073709551616 > 0 + 2 | assert -9223372036854775809 < 0 + | ~~~~~~~~~~~~~~~~~~~~ diff --git a/v_windows/v/old/vlib/v/checker/tests/oversized_int_lit.vv b/v_windows/v/old/vlib/v/checker/tests/oversized_int_lit.vv new file mode 100644 index 0000000..d6e3a27 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/oversized_int_lit.vv @@ -0,0 +1,2 @@ +assert 18446744073709551616 > 0 +assert -9223372036854775809 < 0 diff --git a/v_windows/v/old/vlib/v/checker/tests/pass_mut_lit.out b/v_windows/v/old/vlib/v/checker/tests/pass_mut_lit.out new file mode 100644 index 0000000..09a0343 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/pass_mut_lit.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/pass_mut_lit.vv:10:12: error: cannot pass expression as `mut` + 8 | } + 9 | + 10 | modify(mut Box{}, 10) + | ~~~~~ diff --git a/v_windows/v/old/vlib/v/checker/tests/pass_mut_lit.vv b/v_windows/v/old/vlib/v/checker/tests/pass_mut_lit.vv new file mode 100644 index 0000000..7d358f9 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/pass_mut_lit.vv @@ -0,0 +1,10 @@ +struct Box { +mut: + value int +} + +fn modify(mut box Box, value int) { + box.value = value +} + +modify(mut Box{}, 10) diff --git a/v_windows/v/old/vlib/v/checker/tests/passing_expr_to_fn_expecting_voidptr.out b/v_windows/v/old/vlib/v/checker/tests/passing_expr_to_fn_expecting_voidptr.out new file mode 100644 index 0000000..1acbdb5 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/passing_expr_to_fn_expecting_voidptr.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/passing_expr_to_fn_expecting_voidptr.vv:3:31: error: expression cannot be passed as `voidptr` + 1 | import strconv + 2 | + 3 | strconv.v_printf('%02.02f\n', 1.1) + | ~~~ diff --git a/v_windows/v/old/vlib/v/checker/tests/passing_expr_to_fn_expecting_voidptr.vv b/v_windows/v/old/vlib/v/checker/tests/passing_expr_to_fn_expecting_voidptr.vv new file mode 100644 index 0000000..1b3bc39 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/passing_expr_to_fn_expecting_voidptr.vv @@ -0,0 +1,3 @@ +import strconv + +strconv.v_printf('%02.02f\n', 1.1) diff --git a/v_windows/v/old/vlib/v/checker/tests/pointer_ops.out b/v_windows/v/old/vlib/v/checker/tests/pointer_ops.out new file mode 100644 index 0000000..0221116 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/pointer_ops.out @@ -0,0 +1,49 @@ +vlib/v/checker/tests/pointer_ops.vv:5:7: error: `+` cannot be used with `voidptr` + 3 | unsafe { + 4 | mut p := voidptr(0) + 5 | _ = p + 1 + | ^ + 6 | p++ + 7 | p += 3 +vlib/v/checker/tests/pointer_ops.vv:6:4: error: invalid operation: ++ (non-numeric type `voidptr`) + 4 | mut p := voidptr(0) + 5 | _ = p + 1 + 6 | p++ + | ~~ + 7 | p += 3 + 8 | _ = p - 1 +vlib/v/checker/tests/pointer_ops.vv:7:3: error: operator `+=` not defined on left operand type `voidptr` + 5 | _ = p + 1 + 6 | p++ + 7 | p += 3 + | ^ + 8 | _ = p - 1 + 9 | p-- +vlib/v/checker/tests/pointer_ops.vv:8:7: error: `-` cannot be used with `voidptr` + 6 | p++ + 7 | p += 3 + 8 | _ = p - 1 + | ^ + 9 | p-- + 10 | p -= 3 +vlib/v/checker/tests/pointer_ops.vv:9:4: error: invalid operation: -- (non-numeric type `voidptr`) + 7 | p += 3 + 8 | _ = p - 1 + 9 | p-- + | ~~ + 10 | p -= 3 + 11 | _ = p[3] +vlib/v/checker/tests/pointer_ops.vv:10:3: error: operator `-=` not defined on left operand type `voidptr` + 8 | _ = p - 1 + 9 | p-- + 10 | p -= 3 + | ^ + 11 | _ = p[3] + 12 | } +vlib/v/checker/tests/pointer_ops.vv:11:8: error: type `voidptr` does not support indexing + 9 | p-- + 10 | p -= 3 + 11 | _ = p[3] + | ~~~ + 12 | } + 13 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/pointer_ops.vv b/v_windows/v/old/vlib/v/checker/tests/pointer_ops.vv new file mode 100644 index 0000000..e93161b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/pointer_ops.vv @@ -0,0 +1,13 @@ +// void* arithmetic is not allowed in MSVC, so ban it +fn test_voidptr() { + unsafe { + mut p := voidptr(0) + _ = p + 1 + p++ + p += 3 + _ = p - 1 + p-- + p -= 3 + _ = p[3] + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/prefix_err.out b/v_windows/v/old/vlib/v/checker/tests/prefix_err.out new file mode 100644 index 0000000..528ddd0 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/prefix_err.out @@ -0,0 +1,95 @@ +vlib/v/checker/tests/prefix_err.vv:5:6: error: cannot take the address of true + 3 | } + 4 | + 5 | b := &true + | ^ + 6 | _ := &get() + 7 | _ := &(get()) +vlib/v/checker/tests/prefix_err.vv:6:6: error: cannot take the address of get() + 4 | + 5 | b := &true + 6 | _ := &get() + | ^ + 7 | _ := &(get()) + 8 | _ := &(get() + 1) +vlib/v/checker/tests/prefix_err.vv:7:6: error: cannot take the address of get() + 5 | b := &true + 6 | _ := &get() + 7 | _ := &(get()) + | ^ + 8 | _ := &(get() + 1) + 9 | _ := &10 +vlib/v/checker/tests/prefix_err.vv:8:6: error: cannot take the address of get() + 1 + 6 | _ := &get() + 7 | _ := &(get()) + 8 | _ := &(get() + 1) + | ^ + 9 | _ := &10 + 10 | _ := &"Hi" +vlib/v/checker/tests/prefix_err.vv:9:6: error: cannot take the address of 10 + 7 | _ := &(get()) + 8 | _ := &(get() + 1) + 9 | _ := &10 + | ^ + 10 | _ := &"Hi" + 11 | _ := &"${b}" +vlib/v/checker/tests/prefix_err.vv:10:6: error: cannot take the address of 'Hi' + 8 | _ := &(get() + 1) + 9 | _ := &10 + 10 | _ := &"Hi" + | ^ + 11 | _ := &"${b}" + 12 | _ := &`c` +vlib/v/checker/tests/prefix_err.vv:11:6: error: cannot take the address of '$b' + 9 | _ := &10 + 10 | _ := &"Hi" + 11 | _ := &"${b}" + | ^ + 12 | _ := &`c` + 13 | _ := &1.2 +vlib/v/checker/tests/prefix_err.vv:12:6: error: cannot take the address of `c` + 10 | _ := &"Hi" + 11 | _ := &"${b}" + 12 | _ := &`c` + | ^ + 13 | _ := &1.2 + 14 | _ := &(1 + 2) +vlib/v/checker/tests/prefix_err.vv:13:6: error: cannot take the address of 1.2 + 11 | _ := &"${b}" + 12 | _ := &`c` + 13 | _ := &1.2 + | ^ + 14 | _ := &(1 + 2) + 15 | _ := 12.3 +vlib/v/checker/tests/prefix_err.vv:14:6: error: cannot take the address of 1 + 2 + 12 | _ := &`c` + 13 | _ := &1.2 + 14 | _ := &(1 + 2) + | ^ + 15 | _ := 12.3 + 16 | +vlib/v/checker/tests/prefix_err.vv:18:5: error: invalid indirect of `int` + 16 | + 17 | a := 1 + 18 | _ = *a + | ^ + 19 | a <- 4 + 20 | +vlib/v/checker/tests/prefix_err.vv:19:1: error: cannot push on non-channel `int` + 17 | a := 1 + 18 | _ = *a + 19 | a <- 4 + | ^ + 20 | + 21 | _ = ~true +vlib/v/checker/tests/prefix_err.vv:21:5: error: operator ~ only defined on int types + 19 | a <- 4 + 20 | + 21 | _ = ~true + | ^ + 22 | _ = !4 +vlib/v/checker/tests/prefix_err.vv:22:5: error: ! operator can only be used with bool types + 20 | + 21 | _ = ~true + 22 | _ = !4 + | ^ diff --git a/v_windows/v/old/vlib/v/checker/tests/prefix_err.vv b/v_windows/v/old/vlib/v/checker/tests/prefix_err.vv new file mode 100644 index 0000000..6472a0b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/prefix_err.vv @@ -0,0 +1,22 @@ +fn get() int { + return 1 +} + +b := &true +_ := &get() +_ := &(get()) +_ := &(get() + 1) +_ := &10 +_ := &"Hi" +_ := &"${b}" +_ := &`c` +_ := &1.2 +_ := &(1 + 2) +_ := 12.3 + +a := 1 +_ = *a +a <- 4 + +_ = ~true +_ = !4 diff --git a/v_windows/v/old/vlib/v/checker/tests/prefix_expr_decl_assign_err.out b/v_windows/v/old/vlib/v/checker/tests/prefix_expr_decl_assign_err.out new file mode 100644 index 0000000..575fc0f --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/prefix_expr_decl_assign_err.out @@ -0,0 +1,12 @@ +vlib/v/checker/tests/prefix_expr_decl_assign_err.vv:2:5: error: non-name on the left side of `:=` + 1 | fn main() { + 2 | &a := 12 + | ^ + 3 | (*d) := 14 + 4 | } +vlib/v/checker/tests/prefix_expr_decl_assign_err.vv:3:5: error: non-name `(*d)` on left side of `:=` + 1 | fn main() { + 2 | &a := 12 + 3 | (*d) := 14 + | ~~~~ + 4 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/prefix_expr_decl_assign_err.vv b/v_windows/v/old/vlib/v/checker/tests/prefix_expr_decl_assign_err.vv new file mode 100644 index 0000000..3cfff48 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/prefix_expr_decl_assign_err.vv @@ -0,0 +1,4 @@ +fn main() { + &a := 12 + (*d) := 14 +} diff --git a/v_windows/v/old/vlib/v/checker/tests/print_char.out b/v_windows/v/old/vlib/v/checker/tests/print_char.out new file mode 100644 index 0000000..055ec11 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/print_char.out @@ -0,0 +1,3 @@ +vlib/v/checker/tests/print_char.vv:1:1: error: `println` cannot print type `char` directly, print its address or cast it to an integer instead + 1 | println(char(91)) + | ~~~~~~~~~~~~~~~~~ diff --git a/v_windows/v/old/vlib/v/checker/tests/print_char.vv b/v_windows/v/old/vlib/v/checker/tests/print_char.vv new file mode 100644 index 0000000..9ddba8a --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/print_char.vv @@ -0,0 +1 @@ +println(char(91)) diff --git a/v_windows/v/old/vlib/v/checker/tests/println_can_not_print_void_expressions.out b/v_windows/v/old/vlib/v/checker/tests/println_can_not_print_void_expressions.out new file mode 100644 index 0000000..674f2a5 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/println_can_not_print_void_expressions.out @@ -0,0 +1,27 @@ +vlib/v/checker/tests/println_can_not_print_void_expressions.vv:3:2: error: `print` can not print void expressions + 1 | fn blabla() {} + 2 | fn main() { + 3 | print(blabla()) + | ~~~~~~~~~~~~~~~ + 4 | println(blabla()) + 5 | eprint(blabla()) +vlib/v/checker/tests/println_can_not_print_void_expressions.vv:4:2: error: `println` can not print void expressions + 2 | fn main() { + 3 | print(blabla()) + 4 | println(blabla()) + | ~~~~~~~~~~~~~~~~~ + 5 | eprint(blabla()) + 6 | eprintln(blabla()) +vlib/v/checker/tests/println_can_not_print_void_expressions.vv:5:2: error: `eprint` can not print void expressions + 3 | print(blabla()) + 4 | println(blabla()) + 5 | eprint(blabla()) + | ~~~~~~~~~~~~~~~~ + 6 | eprintln(blabla()) + 7 | } +vlib/v/checker/tests/println_can_not_print_void_expressions.vv:6:2: error: `eprintln` can not print void expressions + 4 | println(blabla()) + 5 | eprint(blabla()) + 6 | eprintln(blabla()) + | ~~~~~~~~~~~~~~~~~~ + 7 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/println_can_not_print_void_expressions.vv b/v_windows/v/old/vlib/v/checker/tests/println_can_not_print_void_expressions.vv new file mode 100644 index 0000000..e412697 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/println_can_not_print_void_expressions.vv @@ -0,0 +1,7 @@ +fn blabla() {} +fn main() { + print(blabla()) + println(blabla()) + eprint(blabla()) + eprintln(blabla()) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/ptr_assign.out b/v_windows/v/old/vlib/v/checker/tests/ptr_assign.out new file mode 100644 index 0000000..5afa1d2 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/ptr_assign.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/ptr_assign.vv:3:5: error: cannot assign to `p`: expected `&int`, not `int literal` + 1 | mut v := 43 + 2 | mut p := &v + 3 | p = 4 + | ^ + 4 | _ = p diff --git a/v_windows/v/old/vlib/v/checker/tests/ptr_assign.vv b/v_windows/v/old/vlib/v/checker/tests/ptr_assign.vv new file mode 100644 index 0000000..562f196 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/ptr_assign.vv @@ -0,0 +1,4 @@ +mut v := 43 +mut p := &v +p = 4 +_ = p diff --git a/v_windows/v/old/vlib/v/checker/tests/receiver_unknown_type_single_letter.out b/v_windows/v/old/vlib/v/checker/tests/receiver_unknown_type_single_letter.out new file mode 100644 index 0000000..667a0d2 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/receiver_unknown_type_single_letter.out @@ -0,0 +1,4 @@ +vlib/v/checker/tests/receiver_unknown_type_single_letter.vv:1:7: error: unknown type `Abc` + 1 | fn (p Abc) foo() {} + | ~~~ + 2 | diff --git a/v_windows/v/old/vlib/v/checker/tests/receiver_unknown_type_single_letter.vv b/v_windows/v/old/vlib/v/checker/tests/receiver_unknown_type_single_letter.vv new file mode 100644 index 0000000..703cf29 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/receiver_unknown_type_single_letter.vv @@ -0,0 +1,2 @@ +fn (p Abc) foo() {} + diff --git a/v_windows/v/old/vlib/v/checker/tests/recursive_interface_err.out b/v_windows/v/old/vlib/v/checker/tests/recursive_interface_err.out new file mode 100644 index 0000000..f3ec2a7 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/recursive_interface_err.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/recursive_interface_err.vv:2:6: error: recursive interface fields are not allowed because they cannot be initialised + 1 | interface Foo { + 2 | foo Foo + | ~~~ + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/recursive_interface_err.vv b/v_windows/v/old/vlib/v/checker/tests/recursive_interface_err.vv new file mode 100644 index 0000000..74a3dd9 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/recursive_interface_err.vv @@ -0,0 +1,3 @@ +interface Foo { + foo Foo +} diff --git a/v_windows/v/old/vlib/v/checker/tests/reference_field_must_be_initialized.out b/v_windows/v/old/vlib/v/checker/tests/reference_field_must_be_initialized.out new file mode 100644 index 0000000..846e5b7 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/reference_field_must_be_initialized.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/reference_field_must_be_initialized.vv:8:7: error: reference field `Node.next` must be initialized + 6 | + 7 | fn main(){ + 8 | n := Node{ data: 123 } + | ~~~~~~~~~~~~~~~~~ + 9 | eprintln('n.data: $n.data') + 10 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/reference_field_must_be_initialized.vv b/v_windows/v/old/vlib/v/checker/tests/reference_field_must_be_initialized.vv new file mode 100644 index 0000000..a308173 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/reference_field_must_be_initialized.vv @@ -0,0 +1,10 @@ +module main +struct Node { + data int + next &Node +} + +fn main(){ + n := Node{ data: 123 } + eprintln('n.data: $n.data') +} diff --git a/v_windows/v/old/vlib/v/checker/tests/reference_return.out b/v_windows/v/old/vlib/v/checker/tests/reference_return.out new file mode 100644 index 0000000..4c9b3c2 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/reference_return.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/reference_return.vv:6:9: error: fn `f` expects you to return a reference type `&Aa`, but you are returning `Aa` instead + 4 | + 5 | fn f() &Aa { + 6 | return Aa{ + | ~~~ + 7 | a: 1 + 8 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/reference_return.vv b/v_windows/v/old/vlib/v/checker/tests/reference_return.vv new file mode 100644 index 0000000..42c1e5a --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/reference_return.vv @@ -0,0 +1,14 @@ +struct Aa { + a int +} + +fn f() &Aa { + return Aa{ + a: 1 + } +} + +fn main() { + x := f() + println(x.a) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/return_count_mismatch.out b/v_windows/v/old/vlib/v/checker/tests/return_count_mismatch.out new file mode 100644 index 0000000..8182870 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/return_count_mismatch.out @@ -0,0 +1,34 @@ +vlib/v/checker/tests/return_count_mismatch.vv:2:9: error: unexpected argument, current function does not return anything + 1 | fn v() { + 2 | return 3 + | ^ + 3 | } + 4 | +vlib/v/checker/tests/return_count_mismatch.vv:7:2: error: expected `int` argument + 5 | fn f() int + 6 | { + 7 | return + | ~~~~~~ + 8 | } + 9 | +vlib/v/checker/tests/return_count_mismatch.vv:12:2: error: expected `(int, string)` arguments + 10 | fn g() (int, string) + 11 | { + 12 | return + | ~~~~~~ + 13 | } + 14 | +vlib/v/checker/tests/return_count_mismatch.vv:17:2: error: expected 1 argument, but got 2 + 15 | fn ff() int + 16 | { + 17 | return 2, '' + | ~~~~~~~~~~~~ + 18 | } + 19 | +vlib/v/checker/tests/return_count_mismatch.vv:22:2: error: expected 2 arguments, but got 1 + 20 | fn gg() (int, string) + 21 | { + 22 | return 3 + | ~~~~~~~~ + 23 | } + 24 | diff --git a/v_windows/v/old/vlib/v/checker/tests/return_count_mismatch.vv b/v_windows/v/old/vlib/v/checker/tests/return_count_mismatch.vv new file mode 100644 index 0000000..15f4116 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/return_count_mismatch.vv @@ -0,0 +1,24 @@ +fn v() { + return 3 +} + +fn f() int +{ + return +} + +fn g() (int, string) +{ + return +} + +fn ff() int +{ + return 2, '' +} + +fn gg() (int, string) +{ + return 3 +} + diff --git a/v_windows/v/old/vlib/v/checker/tests/return_duplicate_with_none_err_a.out b/v_windows/v/old/vlib/v/checker/tests/return_duplicate_with_none_err_a.out new file mode 100644 index 0000000..ea41c8b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/return_duplicate_with_none_err_a.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/return_duplicate_with_none_err_a.vv:3:2: error: unreachable code + 1 | fn f() ?bool { + 2 | return true + 3 | return none + | ~~~~~~~~~~~ + 4 | } + 5 | fn main() { diff --git a/v_windows/v/old/vlib/v/checker/tests/return_duplicate_with_none_err_a.vv b/v_windows/v/old/vlib/v/checker/tests/return_duplicate_with_none_err_a.vv new file mode 100644 index 0000000..c356eb8 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/return_duplicate_with_none_err_a.vv @@ -0,0 +1,9 @@ +fn f() ?bool { + return true + return none +} +fn main() { + f() or { + println('fail') + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/return_duplicate_with_none_err_b.out b/v_windows/v/old/vlib/v/checker/tests/return_duplicate_with_none_err_b.out new file mode 100644 index 0000000..7bc37bb --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/return_duplicate_with_none_err_b.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/return_duplicate_with_none_err_b.vv:3:2: error: unreachable code + 1 | fn f() ?bool { + 2 | return none + 3 | return true + | ~~~~~~~~~~~ + 4 | } + 5 | fn main() { diff --git a/v_windows/v/old/vlib/v/checker/tests/return_duplicate_with_none_err_b.vv b/v_windows/v/old/vlib/v/checker/tests/return_duplicate_with_none_err_b.vv new file mode 100644 index 0000000..aaf39a1 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/return_duplicate_with_none_err_b.vv @@ -0,0 +1,9 @@ +fn f() ?bool { + return none + return true +} +fn main() { + f() or { + println('fail') + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/return_fixed_array.out b/v_windows/v/old/vlib/v/checker/tests/return_fixed_array.out new file mode 100644 index 0000000..3ac7df0 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/return_fixed_array.out @@ -0,0 +1,12 @@ +vlib/v/checker/tests/return_fixed_array.vv:1:25: error: fixed array cannot be returned by function + 1 | fn return_fixed_array() [3]int { + | ~~~~~~ + 2 | return [1, 2, 3]! + 3 | } +vlib/v/checker/tests/return_fixed_array.vv:5:41: error: fixed array cannot be used in multi-return + 3 | } + 4 | + 5 | fn return_fixed_array_in_multi_return() ([3]int, [3]int) { + | ~~~~~~~~~~~~~~~~ + 6 | return [1, 2, 3]!, [4, 5, 6]! + 7 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/return_fixed_array.vv b/v_windows/v/old/vlib/v/checker/tests/return_fixed_array.vv new file mode 100644 index 0000000..dc24da1 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/return_fixed_array.vv @@ -0,0 +1,7 @@ +fn return_fixed_array() [3]int { + return [1, 2, 3]! +} + +fn return_fixed_array_in_multi_return() ([3]int, [3]int) { + return [1, 2, 3]!, [4, 5, 6]! +} diff --git a/v_windows/v/old/vlib/v/checker/tests/return_missing_comp_if.out b/v_windows/v/old/vlib/v/checker/tests/return_missing_comp_if.out new file mode 100644 index 0000000..f8315ac --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/return_missing_comp_if.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/return_missing_comp_if.vv:3:1: error: missing return at end of function `foo` + 1 | fn main() {} + 2 | + 3 | fn foo() string { + | ~~~~~~~~~~~~~~~ + 4 | $if windows { + 5 | \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/return_missing_comp_if.vv b/v_windows/v/old/vlib/v/checker/tests/return_missing_comp_if.vv new file mode 100644 index 0000000..e6e6d79 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/return_missing_comp_if.vv @@ -0,0 +1,9 @@ +fn main() {} + +fn foo() string { + $if windows { + + } $else { + + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/return_missing_comp_if_nested.out b/v_windows/v/old/vlib/v/checker/tests/return_missing_comp_if_nested.out new file mode 100644 index 0000000..4c24e85 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/return_missing_comp_if_nested.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/return_missing_comp_if_nested.vv:3:1: error: missing return at end of function `foo` + 1 | fn main() {} + 2 | + 3 | fn foo() string { + | ~~~~~~~~~~~~~~~ + 4 | $if windows { + 5 | if true { \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/return_missing_comp_if_nested.vv b/v_windows/v/old/vlib/v/checker/tests/return_missing_comp_if_nested.vv new file mode 100644 index 0000000..08fada9 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/return_missing_comp_if_nested.vv @@ -0,0 +1,17 @@ +fn main() {} + +fn foo() string { + $if windows { + if true { + + } else { + return '' + } + } $else { + if true { + + } else { + return '' + } + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/return_missing_if_else_simple.out b/v_windows/v/old/vlib/v/checker/tests/return_missing_if_else_simple.out new file mode 100644 index 0000000..41eb21b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/return_missing_if_else_simple.out @@ -0,0 +1,12 @@ +vlib/v/checker/tests/return_missing_if_else_simple.vv:1:1: error: missing return at end of function `if_no_returns` + 1 | fn if_no_returns() int { + | ~~~~~~~~~~~~~~~~~~~~~~ + 2 | if true { + 3 | println('no return in then') +vlib/v/checker/tests/return_missing_if_else_simple.vv:10:1: error: missing return at end of function `if_no_return_in_else` + 8 | } + 9 | + 10 | fn if_no_return_in_else() int { + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 11 | if true { + 12 | return 123 diff --git a/v_windows/v/old/vlib/v/checker/tests/return_missing_if_else_simple.vv b/v_windows/v/old/vlib/v/checker/tests/return_missing_if_else_simple.vv new file mode 100644 index 0000000..802a7d9 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/return_missing_if_else_simple.vv @@ -0,0 +1,35 @@ +fn if_no_returns() int { + if true { + println('no return in then') + } else { + println('no return in else') + } + println('hello') +} + +fn if_no_return_in_else() int { + if true { + return 123 + } else { + println('no return in else') + } + println('hello') +} + +fn if_returns_in_both_branches() int { + // The if here always returns, so the function + // returns too: + if true { + return 123 + } else { + return 456 + } + // TODO: should produce a warning/error about a statement after a return. + println('hello') +} + +fn main() { + println(if_no_returns()) + println(if_no_return_in_else()) + println(if_returns_in_both_branches()) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/return_missing_if_match.out b/v_windows/v/old/vlib/v/checker/tests/return_missing_if_match.out new file mode 100644 index 0000000..d4877d8 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/return_missing_if_match.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/return_missing_if_match.vv:3:1: error: missing return at end of function `foo` + 1 | fn main() {} + 2 | + 3 | fn foo() string { + | ~~~~~~~~~~~~~~~ + 4 | if true { + 5 | match 1 { \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/return_missing_if_match.vv b/v_windows/v/old/vlib/v/checker/tests/return_missing_if_match.vv new file mode 100644 index 0000000..88ab70c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/return_missing_if_match.vv @@ -0,0 +1,13 @@ +fn main() {} + +fn foo() string { + if true { + match 1 { + 1 { return '' } + 2 { } + else { return '' } + } + } else { + return '' + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/return_missing_match_if.out b/v_windows/v/old/vlib/v/checker/tests/return_missing_match_if.out new file mode 100644 index 0000000..196b321 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/return_missing_match_if.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/return_missing_match_if.vv:3:1: error: missing return at end of function `foo` + 1 | fn main() {} + 2 | + 3 | fn foo() string { + | ~~~~~~~~~~~~~~~ + 4 | match 1 { + 5 | 1 { return '' } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/return_missing_match_if.vv b/v_windows/v/old/vlib/v/checker/tests/return_missing_match_if.vv new file mode 100644 index 0000000..4ff9345 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/return_missing_match_if.vv @@ -0,0 +1,15 @@ +fn main() {} + +fn foo() string { + match 1 { + 1 { return '' } + 2 { + if true { + + } else { + return '' + } + } + else { return '' } + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/return_missing_match_simple.out b/v_windows/v/old/vlib/v/checker/tests/return_missing_match_simple.out new file mode 100644 index 0000000..6e8306b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/return_missing_match_simple.out @@ -0,0 +1,12 @@ +vlib/v/checker/tests/return_missing_match_simple.vv:4:3: warning: `match` must have at least one non `else` branch + 2 | a := 1 + 3 | match a { + 4 | else { println('abc') } + | ~~~~ + 5 | } + 6 | println('hello') +vlib/v/checker/tests/return_missing_match_simple.vv:1:1: error: missing return at end of function `h` + 1 | fn h() int { + | ~~~~~~~~~~ + 2 | a := 1 + 3 | match a { diff --git a/v_windows/v/old/vlib/v/checker/tests/return_missing_match_simple.vv b/v_windows/v/old/vlib/v/checker/tests/return_missing_match_simple.vv new file mode 100644 index 0000000..463c2b2 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/return_missing_match_simple.vv @@ -0,0 +1,12 @@ +fn h() int { + a := 1 + match a { + else { println('abc') } + } + println('hello') +} + +fn main() { + d := h() + println('h returned: $d') +} diff --git a/v_windows/v/old/vlib/v/checker/tests/return_missing_nested.out b/v_windows/v/old/vlib/v/checker/tests/return_missing_nested.out new file mode 100644 index 0000000..fce3245 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/return_missing_nested.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/return_missing_nested.vv:3:1: error: missing return at end of function `foo` + 1 | fn main() {} + 2 | + 3 | fn foo() string { + | ~~~~~~~~~~~~~~~ + 4 | if true { + 5 | return '' \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/return_missing_nested.vv b/v_windows/v/old/vlib/v/checker/tests/return_missing_nested.vv new file mode 100644 index 0000000..41b4c7f --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/return_missing_nested.vv @@ -0,0 +1,11 @@ +fn main() {} + +fn foo() string { + if true { + return '' + } else { + if true { + return '' + } + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/return_missing_simple.out b/v_windows/v/old/vlib/v/checker/tests/return_missing_simple.out new file mode 100644 index 0000000..8016345 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/return_missing_simple.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/return_missing_simple.vv:3:1: error: missing return at end of function `foo` + 1 | fn main() {} + 2 | + 3 | fn foo() string { + | ~~~~~~~~~~~~~~~ + 4 | if true { + 5 | return '' \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/return_missing_simple.vv b/v_windows/v/old/vlib/v/checker/tests/return_missing_simple.vv new file mode 100644 index 0000000..ed200fe --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/return_missing_simple.vv @@ -0,0 +1,7 @@ +fn main() {} + +fn foo() string { + if true { + return '' + } else {} +} diff --git a/v_windows/v/old/vlib/v/checker/tests/return_ref_as_no_ref_bug.out b/v_windows/v/old/vlib/v/checker/tests/return_ref_as_no_ref_bug.out new file mode 100644 index 0000000..01b9990 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/return_ref_as_no_ref_bug.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/return_ref_as_no_ref_bug.vv:11:9: error: fn `return_not_reference` expects you to return a non reference type `BugStruct`, but you are returning `&BugStruct` instead + 9 | + 10 | fn return_not_reference() BugStruct{ + 11 | return &BugStruct { + | ^ + 12 | id: 1 + 13 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/return_ref_as_no_ref_bug.vv b/v_windows/v/old/vlib/v/checker/tests/return_ref_as_no_ref_bug.vv new file mode 100644 index 0000000..8d04dc9 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/return_ref_as_no_ref_bug.vv @@ -0,0 +1,14 @@ +struct BugStruct { + id int +} + +fn main() { + x := return_not_reference() + println(x.id) +} + +fn return_not_reference() BugStruct{ + return &BugStruct { + id: 1 + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/return_type.out b/v_windows/v/old/vlib/v/checker/tests/return_type.out new file mode 100644 index 0000000..971347c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/return_type.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/return_type.vv:2:9: error: cannot use `int literal` as type `bool` in return argument + 1 | fn test() bool { + 2 | return 100 + | ~~~ + 3 | } + 4 | diff --git a/v_windows/v/old/vlib/v/checker/tests/return_type.vv b/v_windows/v/old/vlib/v/checker/tests/return_type.vv new file mode 100644 index 0000000..333ec9f --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/return_type.vv @@ -0,0 +1,5 @@ +fn test() bool { + return 100 +} + +fn main() {} diff --git a/v_windows/v/old/vlib/v/checker/tests/return_working_comp_if.out b/v_windows/v/old/vlib/v/checker/tests/return_working_comp_if.out new file mode 100644 index 0000000..e69de29 diff --git a/v_windows/v/old/vlib/v/checker/tests/return_working_comp_if.vv b/v_windows/v/old/vlib/v/checker/tests/return_working_comp_if.vv new file mode 100644 index 0000000..d6fffe5 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/return_working_comp_if.vv @@ -0,0 +1,9 @@ +fn main() {} + +fn foo() string { + $if windows { + return '' + } $else { + return '' + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/return_working_comp_if_nested.out b/v_windows/v/old/vlib/v/checker/tests/return_working_comp_if_nested.out new file mode 100644 index 0000000..e69de29 diff --git a/v_windows/v/old/vlib/v/checker/tests/return_working_comp_if_nested.vv b/v_windows/v/old/vlib/v/checker/tests/return_working_comp_if_nested.vv new file mode 100644 index 0000000..c76e981 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/return_working_comp_if_nested.vv @@ -0,0 +1,17 @@ +fn main() {} + +fn foo() string { + $if windows { + if true { + return '' + } else { + return '' + } + } $else { + if true { + return '' + } else { + return '' + } + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/return_working_if_match.out b/v_windows/v/old/vlib/v/checker/tests/return_working_if_match.out new file mode 100644 index 0000000..e69de29 diff --git a/v_windows/v/old/vlib/v/checker/tests/return_working_if_match.vv b/v_windows/v/old/vlib/v/checker/tests/return_working_if_match.vv new file mode 100644 index 0000000..5e8d1bf --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/return_working_if_match.vv @@ -0,0 +1,13 @@ +fn main() {} + +fn foo() string { + if true { + match 1 { + 1 { return '' } + 2 { return '' } + else { return '' } + } + } else { + return '' + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/return_working_match_if.out b/v_windows/v/old/vlib/v/checker/tests/return_working_match_if.out new file mode 100644 index 0000000..e69de29 diff --git a/v_windows/v/old/vlib/v/checker/tests/return_working_match_if.vv b/v_windows/v/old/vlib/v/checker/tests/return_working_match_if.vv new file mode 100644 index 0000000..2fbbcdf --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/return_working_match_if.vv @@ -0,0 +1,15 @@ +fn main() {} + +fn foo() string { + match 1 { + 1 { return '' } + 2 { + if true { + return '' + } else { + return '' + } + } + else { return '' } + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/return_working_nested.out b/v_windows/v/old/vlib/v/checker/tests/return_working_nested.out new file mode 100644 index 0000000..e69de29 diff --git a/v_windows/v/old/vlib/v/checker/tests/return_working_nested.vv b/v_windows/v/old/vlib/v/checker/tests/return_working_nested.vv new file mode 100644 index 0000000..1d72a02 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/return_working_nested.vv @@ -0,0 +1,12 @@ +fn main() {} + +fn foo() string { + if true { + return '' + } else { + if true { + return '' + } + return '' + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/return_working_simple.out b/v_windows/v/old/vlib/v/checker/tests/return_working_simple.out new file mode 100644 index 0000000..e69de29 diff --git a/v_windows/v/old/vlib/v/checker/tests/return_working_simple.vv b/v_windows/v/old/vlib/v/checker/tests/return_working_simple.vv new file mode 100644 index 0000000..271e3f6 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/return_working_simple.vv @@ -0,0 +1,9 @@ +fn main() {} + +fn foo() string { + if true { + return '' + } else { + return '' + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/return_working_two_if.out b/v_windows/v/old/vlib/v/checker/tests/return_working_two_if.out new file mode 100644 index 0000000..e69de29 diff --git a/v_windows/v/old/vlib/v/checker/tests/return_working_two_if.vv b/v_windows/v/old/vlib/v/checker/tests/return_working_two_if.vv new file mode 100644 index 0000000..a95825f --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/return_working_two_if.vv @@ -0,0 +1,17 @@ +fn main() {} + +fn foo() string { + if true { + if true { + return '' + } + + if true { + return '' + } else { + return '' + } + } else { + return '' + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/return_working_unsafe.out b/v_windows/v/old/vlib/v/checker/tests/return_working_unsafe.out new file mode 100644 index 0000000..e69de29 diff --git a/v_windows/v/old/vlib/v/checker/tests/return_working_unsafe.vv b/v_windows/v/old/vlib/v/checker/tests/return_working_unsafe.vv new file mode 100644 index 0000000..777b297 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/return_working_unsafe.vv @@ -0,0 +1,7 @@ +fn main() {} + +fn foo() string { + unsafe { + return '' + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/returns/return_missing_simple.vv b/v_windows/v/old/vlib/v/checker/tests/returns/return_missing_simple.vv new file mode 100644 index 0000000..ad62212 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/returns/return_missing_simple.vv @@ -0,0 +1,7 @@ +fn foo() string { + if true { + return '' + } else { + + } +} \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/rshift_op_wrong_left_type_err.out b/v_windows/v/old/vlib/v/checker/tests/rshift_op_wrong_left_type_err.out new file mode 100644 index 0000000..60866d5 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/rshift_op_wrong_left_type_err.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/rshift_op_wrong_left_type_err.vv:2:10: error: invalid operation: shift on type `float literal` + 1 | fn main() { + 2 | println(0.5 >> 1) + | ~~~ + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/rshift_op_wrong_left_type_err.vv b/v_windows/v/old/vlib/v/checker/tests/rshift_op_wrong_left_type_err.vv new file mode 100644 index 0000000..5ea5841 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/rshift_op_wrong_left_type_err.vv @@ -0,0 +1,3 @@ +fn main() { + println(0.5 >> 1) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/rshift_op_wrong_right_type_err.out b/v_windows/v/old/vlib/v/checker/tests/rshift_op_wrong_right_type_err.out new file mode 100644 index 0000000..7a0c5d9 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/rshift_op_wrong_right_type_err.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/rshift_op_wrong_right_type_err.vv:2:15: error: cannot shift non-integer type `float literal` into type `int literal` + 1 | fn main() { + 2 | println(1 >> 0.5) + | ~~~ + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/rshift_op_wrong_right_type_err.vv b/v_windows/v/old/vlib/v/checker/tests/rshift_op_wrong_right_type_err.vv new file mode 100644 index 0000000..cd55344 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/rshift_op_wrong_right_type_err.vv @@ -0,0 +1,3 @@ +fn main() { + println(1 >> 0.5) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/run/noreturn_fn_can_be_used_instead_of_panic.run.out b/v_windows/v/old/vlib/v/checker/tests/run/noreturn_fn_can_be_used_instead_of_panic.run.out new file mode 100644 index 0000000..f114c07 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/run/noreturn_fn_can_be_used_instead_of_panic.run.out @@ -0,0 +1 @@ +log_and_die: error: oh no diff --git a/v_windows/v/old/vlib/v/checker/tests/run/noreturn_fn_can_be_used_instead_of_panic.vv b/v_windows/v/old/vlib/v/checker/tests/run/noreturn_fn_can_be_used_instead_of_panic.vv new file mode 100644 index 0000000..10cf22c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/run/noreturn_fn_can_be_used_instead_of_panic.vv @@ -0,0 +1,14 @@ +fn abc() ?int { + return error('oh no') +} + +[noreturn] +fn log_and_die(e IError) { + eprintln('${@FN}: error: $e') + exit(77) +} + +fn main() { + x := abc() or { log_and_die(err) } + println(x) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/run/unused_variable_warning.run.out b/v_windows/v/old/vlib/v/checker/tests/run/unused_variable_warning.run.out new file mode 100644 index 0000000..9fa8e3c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/run/unused_variable_warning.run.out @@ -0,0 +1,15 @@ +vlib/v/checker/tests/run/unused_variable_warning.vv:3:2: warning: unused variable: `a` + 1 | // NB: this test should compile and run, but it also should produce a compiler warning. + 2 | fn main() { + 3 | a := 1 + | ^ + 4 | b := 2 + 5 | println('hello') +vlib/v/checker/tests/run/unused_variable_warning.vv:4:2: warning: unused variable: `b` + 2 | fn main() { + 3 | a := 1 + 4 | b := 2 + | ^ + 5 | println('hello') + 6 | } +hello diff --git a/v_windows/v/old/vlib/v/checker/tests/run/unused_variable_warning.vv b/v_windows/v/old/vlib/v/checker/tests/run/unused_variable_warning.vv new file mode 100644 index 0000000..1611e54 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/run/unused_variable_warning.vv @@ -0,0 +1,6 @@ +// NB: this test should compile and run, but it also should produce a compiler warning. +fn main() { + a := 1 + b := 2 + println('hello') +} diff --git a/v_windows/v/old/vlib/v/checker/tests/selective_const_import.out b/v_windows/v/old/vlib/v/checker/tests/selective_const_import.out new file mode 100644 index 0000000..690fc3c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/selective_const_import.out @@ -0,0 +1,3 @@ +vlib/v/checker/tests/selective_const_import.vv:1:15: error: cannot selectively import constant `second` from `time`, import `time` and use `time.second` instead + 1 | import time { second } + | ~~~~~~ diff --git a/v_windows/v/old/vlib/v/checker/tests/selective_const_import.vv b/v_windows/v/old/vlib/v/checker/tests/selective_const_import.vv new file mode 100644 index 0000000..93e7b2f --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/selective_const_import.vv @@ -0,0 +1 @@ +import time { second } diff --git a/v_windows/v/old/vlib/v/checker/tests/selector_expr_assign.out b/v_windows/v/old/vlib/v/checker/tests/selector_expr_assign.out new file mode 100644 index 0000000..9e98006 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/selector_expr_assign.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/selector_expr_assign.vv:7:6: error: struct fields can only be declared during the initialization + 5 | fn main() { + 6 | abc := Abc{} + 7 | abc.a := 2 + | ^ + 8 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/selector_expr_assign.vv b/v_windows/v/old/vlib/v/checker/tests/selector_expr_assign.vv new file mode 100644 index 0000000..024c869 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/selector_expr_assign.vv @@ -0,0 +1,8 @@ +struct Abc { + a int +} + +fn main() { + abc := Abc{} + abc.a := 2 +} diff --git a/v_windows/v/old/vlib/v/checker/tests/selector_expr_optional_err.out b/v_windows/v/old/vlib/v/checker/tests/selector_expr_optional_err.out new file mode 100644 index 0000000..51065b7 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/selector_expr_optional_err.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/selector_expr_optional_err.vv:4:46: error: cannot access fields of an optional, handle the error with `or {...}` or propagate it with `?` + 2 | + 3 | fn main() { + 4 | println(http.get('https://httpbin.org/').status_code) + | ~~~~~~~~~~~ + 5 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/selector_expr_optional_err.vv b/v_windows/v/old/vlib/v/checker/tests/selector_expr_optional_err.vv new file mode 100644 index 0000000..d277787 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/selector_expr_optional_err.vv @@ -0,0 +1,5 @@ +import net.http + +fn main() { + println(http.get('https://httpbin.org/').status_code) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/shared_bad_args.out b/v_windows/v/old/vlib/v/checker/tests/shared_bad_args.out new file mode 100644 index 0000000..e41873c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/shared_bad_args.out @@ -0,0 +1,83 @@ +vlib/v/checker/tests/shared_bad_args.vv:43:8: error: `r` is `shared` and must be `rlock`ed or `lock`ed to be used as non-mut receiver + 41 | shared r := Qr{ a: 7 } + 42 | lock s { + 43 | u := r.s_val(s) + | ^ + 44 | println(u) + 45 | } +vlib/v/checker/tests/shared_bad_args.vv:47:16: error: `s` is `shared` and must be `rlock`ed or `lock`ed to be passed as non-mut argument + 45 | } + 46 | lock r { + 47 | v := r.s_val(s) + | ^ + 48 | println(v) + 49 | } +vlib/v/checker/tests/shared_bad_args.vv:50:13: error: `m` is `shared` and must be `rlock`ed or `lock`ed to be passed as non-mut argument + 48 | println(v) + 49 | } + 50 | w := m_val(m) + | ^ + 51 | x := a_val(a) + 52 | println('$w $x') +vlib/v/checker/tests/shared_bad_args.vv:51:13: error: `a` is `shared` and must be `rlock`ed or `lock`ed to be passed as non-mut argument + 49 | } + 50 | w := m_val(m) + 51 | x := a_val(a) + | ^ + 52 | println('$w $x') + 53 | } +vlib/v/checker/tests/shared_bad_args.vv:61:3: error: r must be added to the `lock` list above + 59 | shared r := Qr{ a: 7 } + 60 | lock s { + 61 | r.s_mut(mut s) + | ^ + 62 | } + 63 | lock r { +vlib/v/checker/tests/shared_bad_args.vv:64:15: error: s must be added to the `lock` list above + 62 | } + 63 | lock r { + 64 | r.s_mut(mut s) + | ^ + 65 | } + 66 | m_mut(mut m) +vlib/v/checker/tests/shared_bad_args.vv:66:12: error: m is `shared` and must be `lock`ed to be passed as `mut` + 64 | r.s_mut(mut s) + 65 | } + 66 | m_mut(mut m) + | ^ + 67 | a_mut(mut a) + 68 | } +vlib/v/checker/tests/shared_bad_args.vv:67:12: error: a is `shared` and must be `lock`ed to be passed as `mut` + 65 | } + 66 | m_mut(mut m) + 67 | a_mut(mut a) + | ^ + 68 | } + 69 | +vlib/v/checker/tests/shared_bad_args.vv:76:10: error: `y` is `shared` and must be `rlock`ed or `lock`ed to be used as non-mut argument to print + 74 | fn main() { + 75 | shared y := St{ a: 5 } + 76 | println(y) + | ^ + 77 | println('$y') + 78 | a := Ab{ s: St{ a: 3 } } +vlib/v/checker/tests/shared_bad_args.vv:77:12: error: `y` is `shared` and must be `rlock`ed or `lock`ed to be used as non-mut interpolation object + 75 | shared y := St{ a: 5 } + 76 | println(y) + 77 | println('$y') + | ^ + 78 | a := Ab{ s: St{ a: 3 } } + 79 | println(a.s) +vlib/v/checker/tests/shared_bad_args.vv:79:12: error: `a.s` is `shared` and must be `rlock`ed or `lock`ed to be used as non-mut argument to print + 77 | println('$y') + 78 | a := Ab{ s: St{ a: 3 } } + 79 | println(a.s) + | ^ + 80 | println('$a.s') + 81 | } +vlib/v/checker/tests/shared_bad_args.vv:80:14: error: `a.s` is `shared` and must be `rlock`ed or `lock`ed to be used as non-mut interpolation object + 78 | a := Ab{ s: St{ a: 3 } } + 79 | println(a.s) + 80 | println('$a.s') + | ^ + 81 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/shared_bad_args.vv b/v_windows/v/old/vlib/v/checker/tests/shared_bad_args.vv new file mode 100644 index 0000000..f0d4913 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/shared_bad_args.vv @@ -0,0 +1,81 @@ +struct St { +mut: + a int +} + +struct Qr { +mut: + a int +} + +fn (mut r Qr) s_mut(mut s St) { + r.a = 5 + s.a = 7 +} + +fn (r Qr) s_val(s St) int { + return r.a * s.a +} + +fn m_mut(mut a map[string]f64) { + a['yxcv'] = -2.25 +} + +fn m_val(a map[string]f64) f64 { + x := a['yxcv'] + return x +} + +fn a_mut(mut a []int) { + a[2] = 42 +} + +fn a_val(a []int) int { + return a[1] +} + +fn test_shared_as_value() { + shared s := St{ a: 5 } + shared a := [3, 4, 6, 13, -23] + shared m := map{'qw': 12.75, 'yxcv': -3.125, 'poiu': 88.0625} + shared r := Qr{ a: 7 } + lock s { + u := r.s_val(s) + println(u) + } + lock r { + v := r.s_val(s) + println(v) + } + w := m_val(m) + x := a_val(a) + println('$w $x') +} + +fn test_shared_as_mut() { + shared s := St{ a: 5 } + shared a := [3, 4, 6, 13, -23] + shared m := map{'qw': 12.75, 'yxcv': -3.125, 'poiu': 88.0625} + shared r := Qr{ a: 7 } + lock s { + r.s_mut(mut s) + } + lock r { + r.s_mut(mut s) + } + m_mut(mut m) + a_mut(mut a) +} + +struct Ab { + s shared St +} + +fn main() { + shared y := St{ a: 5 } + println(y) + println('$y') + a := Ab{ s: St{ a: 3 } } + println(a.s) + println('$a.s') +} diff --git a/v_windows/v/old/vlib/v/checker/tests/shared_element_lock.out b/v_windows/v/old/vlib/v/checker/tests/shared_element_lock.out new file mode 100644 index 0000000..f392a1f --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/shared_element_lock.out @@ -0,0 +1,27 @@ +vlib/v/checker/tests/shared_element_lock.vv:36:5: error: `pr.pe` is `shared` and needs explicit lock for `v.ast.SelectorExpr` + 34 | } + 35 | } + 36 | pr.pe.color = 3 + | ~~ + 37 | shared y := pr.pe + 38 | rlock y { +vlib/v/checker/tests/shared_element_lock.vv:42:2: error: `g` is `shared` and needs explicit lock for `v.ast.SelectorExpr` + 40 | } + 41 | shared g := Pro{} + 42 | g.pers.age = 42 + | ^ + 43 | mut h := []shared Pro{len: 3} + 44 | h[2].pers.age = 42 +vlib/v/checker/tests/shared_element_lock.vv:44:2: error: you have to create a handle and `lock` it to modify `shared` array element + 42 | g.pers.age = 42 + 43 | mut h := []shared Pro{len: 3} + 44 | h[2].pers.age = 42 + | ~~~~ + 45 | println(h[2].pers.age) + 46 | } +vlib/v/checker/tests/shared_element_lock.vv:45:10: error: you have to create a handle and `rlock` it to use a `shared` element as non-mut argument to print + 43 | mut h := []shared Pro{len: 3} + 44 | h[2].pers.age = 42 + 45 | println(h[2].pers.age) + | ~~~~ + 46 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/shared_element_lock.vv b/v_windows/v/old/vlib/v/checker/tests/shared_element_lock.vv new file mode 100644 index 0000000..de61ad9 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/shared_element_lock.vv @@ -0,0 +1,46 @@ +struct Person { +mut: + name string + age int +} + +struct Pet { +mut: + name string + color int +} + +struct Programmer { +mut: + pers Person + pe shared Pet +} + +struct Pro { +mut: + pers Person + pe Pet +} + +fn main() { + mut pr := Programmer{ + pers: Person{ + name: 'Qwe' + age: 44 + } + pe: Pet{ + name: 'Ghj' + color: 7 + } + } + pr.pe.color = 3 + shared y := pr.pe + rlock y { + println(y.color) + } + shared g := Pro{} + g.pers.age = 42 + mut h := []shared Pro{len: 3} + h[2].pers.age = 42 + println(h[2].pers.age) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/shared_lock.out b/v_windows/v/old/vlib/v/checker/tests/shared_lock.out new file mode 100644 index 0000000..62d7130 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/shared_lock.out @@ -0,0 +1,21 @@ +vlib/v/checker/tests/shared_lock.vv:19:5: error: method with `shared` receiver cannot be called inside `lock`/`rlock` block + 17 | } + 18 | lock x { + 19 | x.r(x) + | ~~~~ + 20 | x.m(x) + 21 | f(0, x) +vlib/v/checker/tests/shared_lock.vv:20:7: error: method with `shared` arguments cannot be called inside `lock`/`rlock` block + 18 | lock x { + 19 | x.r(x) + 20 | x.m(x) + | ^ + 21 | f(0, x) + 22 | } +vlib/v/checker/tests/shared_lock.vv:21:8: error: function with `shared` arguments cannot be called inside `lock`/`rlock` block + 19 | x.r(x) + 20 | x.m(x) + 21 | f(0, x) + | ^ + 22 | } + 23 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/shared_lock.vv b/v_windows/v/old/vlib/v/checker/tests/shared_lock.vv new file mode 100644 index 0000000..9af8111 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/shared_lock.vv @@ -0,0 +1,23 @@ +struct St { +mut: + a int +} +fn (shared s St) r(x St) { +} +fn (s St) m(shared x St) { +} + + +fn f(w int, shared x St) { +} + +fn g() { + shared x := St{ + a: 5 + } + lock x { + x.r(x) + x.m(x) + f(0, x) + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/shared_type_mismatch.out b/v_windows/v/old/vlib/v/checker/tests/shared_type_mismatch.out new file mode 100644 index 0000000..32e16d9 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/shared_type_mismatch.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/shared_type_mismatch.vv:13:3: error: wrong return type `St` in the `or {}` block, expected `shared St` + 11 | fn test_shared_opt_bad() { + 12 | shared yy := f() or { + 13 | St{ x: 37.5 } + | ~~~~~~~~~~~~~ + 14 | } + 15 | rlock yy { diff --git a/v_windows/v/old/vlib/v/checker/tests/shared_type_mismatch.vv b/v_windows/v/old/vlib/v/checker/tests/shared_type_mismatch.vv new file mode 100644 index 0000000..9d85636 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/shared_type_mismatch.vv @@ -0,0 +1,18 @@ +struct St { +mut: + x f64 +} + +fn f() ?shared St { + shared x := St{ x: 12.75 } + return x +} + +fn test_shared_opt_bad() { + shared yy := f() or { + St{ x: 37.5 } + } + rlock yy { + println(yy.x) + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/shift_op_wrong_left_type_err.out b/v_windows/v/old/vlib/v/checker/tests/shift_op_wrong_left_type_err.out new file mode 100644 index 0000000..1bdeb5e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/shift_op_wrong_left_type_err.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/shift_op_wrong_left_type_err.vv:2:10: error: invalid operation: shift on type `float literal` + 1 | fn main() { + 2 | println(0.5 << 1) + | ~~~ + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/shift_op_wrong_left_type_err.vv b/v_windows/v/old/vlib/v/checker/tests/shift_op_wrong_left_type_err.vv new file mode 100644 index 0000000..87409eb --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/shift_op_wrong_left_type_err.vv @@ -0,0 +1,3 @@ +fn main() { + println(0.5 << 1) +} \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/shift_op_wrong_right_type_err.out b/v_windows/v/old/vlib/v/checker/tests/shift_op_wrong_right_type_err.out new file mode 100644 index 0000000..87d0a44 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/shift_op_wrong_right_type_err.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/shift_op_wrong_right_type_err.vv:2:15: error: cannot shift non-integer type `float literal` into type `int literal` + 1 | fn main() { + 2 | println(1 << 0.5) + | ~~~ + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/shift_op_wrong_right_type_err.vv b/v_windows/v/old/vlib/v/checker/tests/shift_op_wrong_right_type_err.vv new file mode 100644 index 0000000..0eb1328 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/shift_op_wrong_right_type_err.vv @@ -0,0 +1,3 @@ +fn main() { + println(1 << 0.5) +} \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/short_struct_wrong_number.out b/v_windows/v/old/vlib/v/checker/tests/short_struct_wrong_number.out new file mode 100644 index 0000000..fa6044b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/short_struct_wrong_number.out @@ -0,0 +1,13 @@ +vlib/v/checker/tests/short_struct_wrong_number.vv:7:6: error: too few fields in `Test` literal (expecting 2, got 1) + 5 | + 6 | fn main() { + 7 | _ = Test{true} + | ~~~~~~~~~~ + 8 | _ = Test{true, false, true} + 9 | } +vlib/v/checker/tests/short_struct_wrong_number.vv:8:6: error: too many fields in `Test` literal (expecting 2, got 3) + 6 | fn main() { + 7 | _ = Test{true} + 8 | _ = Test{true, false, true} + | ~~~~~~~~~~~~~~~~~~~~~~~ + 9 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/short_struct_wrong_number.vv b/v_windows/v/old/vlib/v/checker/tests/short_struct_wrong_number.vv new file mode 100644 index 0000000..393ca5e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/short_struct_wrong_number.vv @@ -0,0 +1,9 @@ +struct Test { + foo bool + bar bool +} + +fn main() { + _ = Test{true} + _ = Test{true, false, true} +} diff --git a/v_windows/v/old/vlib/v/checker/tests/slice_reassignment.out b/v_windows/v/old/vlib/v/checker/tests/slice_reassignment.out new file mode 100644 index 0000000..7ab478f --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/slice_reassignment.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/slice_reassignment.vv:3:5: error: cannot reassign using range expression on the left side of an assignment + 1 | fn main() { + 2 | mut arr := [1, 2, 3, 4, 5] + 3 | arr[..2] = [0, 0] + | ~~~~~ + 4 | println(arr) + 5 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/slice_reassignment.vv b/v_windows/v/old/vlib/v/checker/tests/slice_reassignment.vv new file mode 100644 index 0000000..602bd64 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/slice_reassignment.vv @@ -0,0 +1,5 @@ +fn main() { + mut arr := [1, 2, 3, 4, 5] + arr[..2] = [0, 0] + println(arr) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/sort_method_called_on_immutable_receiver.out b/v_windows/v/old/vlib/v/checker/tests/sort_method_called_on_immutable_receiver.out new file mode 100644 index 0000000..d588267 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/sort_method_called_on_immutable_receiver.out @@ -0,0 +1,13 @@ +vlib/v/checker/tests/sort_method_called_on_immutable_receiver.vv:2:2: error: `a` is immutable, declare it with `mut` to make it mutable + 1 | fn abc (a []int) { + 2 | a.sort() + | ^ + 3 | } + 4 | +vlib/v/checker/tests/sort_method_called_on_immutable_receiver.vv:7:2: error: `a` is immutable, declare it with `mut` to make it mutable + 5 | fn main() { + 6 | a := [2,30,10,20,1] + 7 | a.sort(a>b) + | ^ + 8 | eprintln(' a: $a') + 9 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/sort_method_called_on_immutable_receiver.vv b/v_windows/v/old/vlib/v/checker/tests/sort_method_called_on_immutable_receiver.vv new file mode 100644 index 0000000..b2dafa5 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/sort_method_called_on_immutable_receiver.vv @@ -0,0 +1,9 @@ +fn abc (a []int) { + a.sort() +} + +fn main() { + a := [2,30,10,20,1] + a.sort(a>b) + eprintln(' a: $a') +} diff --git a/v_windows/v/old/vlib/v/checker/tests/static_vars_in_translated_mode.out b/v_windows/v/old/vlib/v/checker/tests/static_vars_in_translated_mode.out new file mode 100644 index 0000000..18c297a --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/static_vars_in_translated_mode.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/static_vars_in_translated_mode.vv:2:13: error: static variables are supported only in -translated mode or in [unsafe] fn + 1 | fn counter() int { + 2 | mut static icounter := 0 + | ~~~~~~~~ + 3 | icounter++ + 4 | return icounter diff --git a/v_windows/v/old/vlib/v/checker/tests/static_vars_in_translated_mode.vv b/v_windows/v/old/vlib/v/checker/tests/static_vars_in_translated_mode.vv new file mode 100644 index 0000000..0f153c8 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/static_vars_in_translated_mode.vv @@ -0,0 +1,12 @@ +fn counter() int { + mut static icounter := 0 + icounter++ + return icounter +} + +fn main() { + println(unsafe { counter() }) + println(unsafe { counter() }) + println(unsafe { counter() }) + println(unsafe { counter() }) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/store_string_err.out b/v_windows/v/old/vlib/v/checker/tests/store_string_err.out new file mode 100644 index 0000000..b0a44bf --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/store_string_err.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/store_string_err.vv:5:26: error: wrong return type `IError` in the `or {}` block, expected `int` + 3 | } + 4 | + 5 | err := return_err() or { err } + | ~~~ + 6 | eprintln(err) diff --git a/v_windows/v/old/vlib/v/checker/tests/store_string_err.vv b/v_windows/v/old/vlib/v/checker/tests/store_string_err.vv new file mode 100644 index 0000000..7f8b3ff --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/store_string_err.vv @@ -0,0 +1,6 @@ +fn return_err() ?int { + return error('') +} + +err := return_err() or { err } +eprintln(err) diff --git a/v_windows/v/old/vlib/v/checker/tests/str_method_0_arguments.out b/v_windows/v/old/vlib/v/checker/tests/str_method_0_arguments.out new file mode 100644 index 0000000..9b8f1df --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/str_method_0_arguments.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/str_method_0_arguments.vv:5:1: error: .str() methods should have 0 arguments + 3 | } + 4 | + 5 | fn (z Zzz) str(x int) string { + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 6 | return 'z: $z.x' + 7 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/str_method_0_arguments.vv b/v_windows/v/old/vlib/v/checker/tests/str_method_0_arguments.vv new file mode 100644 index 0000000..28ef0ef --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/str_method_0_arguments.vv @@ -0,0 +1,11 @@ +struct Zzz { + x int +} + +fn (z Zzz) str(x int) string { + return 'z: $z.x' +} + +fn main() { + println(Zzz{123}) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/str_method_return_string.out b/v_windows/v/old/vlib/v/checker/tests/str_method_return_string.out new file mode 100644 index 0000000..43bb36a --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/str_method_return_string.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/str_method_return_string.vv:5:1: error: .str() methods should return `string` + 3 | } + 4 | + 5 | fn (z Zzz) str(x int) int { + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + 6 | return z.x + 7 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/str_method_return_string.vv b/v_windows/v/old/vlib/v/checker/tests/str_method_return_string.vv new file mode 100644 index 0000000..a3f6952 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/str_method_return_string.vv @@ -0,0 +1,11 @@ +struct Zzz { + x int +} + +fn (z Zzz) str(x int) int { + return z.x +} + +fn main() { + println(Zzz{123}) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/string_char_null_err.out b/v_windows/v/old/vlib/v/checker/tests/string_char_null_err.out new file mode 100644 index 0000000..60e1f7a --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/string_char_null_err.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/string_char_null_err.vv:2:31: error: cannot use `\0` (NULL character) in the string literal + 1 | fn main() { + 2 | println('Null character: \0') + | ^ + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/string_char_null_err.vv b/v_windows/v/old/vlib/v/checker/tests/string_char_null_err.vv new file mode 100644 index 0000000..d60854f --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/string_char_null_err.vv @@ -0,0 +1,3 @@ +fn main() { + println('Null character: \0') +} diff --git a/v_windows/v/old/vlib/v/checker/tests/string_escape_u_err_a.out b/v_windows/v/old/vlib/v/checker/tests/string_escape_u_err_a.out new file mode 100644 index 0000000..607c240 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/string_escape_u_err_a.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/string_escape_u_err_a.vv:2:15: error: `\u` incomplete unicode character value + 1 | fn main() { + 2 | println('\u') + | ^ + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/string_escape_u_err_a.vv b/v_windows/v/old/vlib/v/checker/tests/string_escape_u_err_a.vv new file mode 100644 index 0000000..ad7aee7 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/string_escape_u_err_a.vv @@ -0,0 +1,3 @@ +fn main() { + println('\u') +} diff --git a/v_windows/v/old/vlib/v/checker/tests/string_escape_u_err_b.out b/v_windows/v/old/vlib/v/checker/tests/string_escape_u_err_b.out new file mode 100644 index 0000000..d81d688 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/string_escape_u_err_b.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/string_escape_u_err_b.vv:2:15: error: `\u` incomplete unicode character value + 1 | fn main() { + 2 | println('\u345') + | ^ + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/string_escape_u_err_b.vv b/v_windows/v/old/vlib/v/checker/tests/string_escape_u_err_b.vv new file mode 100644 index 0000000..650f8d6 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/string_escape_u_err_b.vv @@ -0,0 +1,3 @@ +fn main() { + println('\u345') +} diff --git a/v_windows/v/old/vlib/v/checker/tests/string_escape_x_err_a.out b/v_windows/v/old/vlib/v/checker/tests/string_escape_x_err_a.out new file mode 100644 index 0000000..f441565 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/string_escape_x_err_a.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/string_escape_x_err_a.vv:2:15: error: `\x` used with no following hex digits + 1 | fn main() { + 2 | println('\x') + | ^ + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/string_escape_x_err_a.vv b/v_windows/v/old/vlib/v/checker/tests/string_escape_x_err_a.vv new file mode 100644 index 0000000..fdbd44b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/string_escape_x_err_a.vv @@ -0,0 +1,3 @@ +fn main() { + println('\x') +} diff --git a/v_windows/v/old/vlib/v/checker/tests/string_escape_x_err_b.out b/v_windows/v/old/vlib/v/checker/tests/string_escape_x_err_b.out new file mode 100644 index 0000000..da90fd1 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/string_escape_x_err_b.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/string_escape_x_err_b.vv:2:15: error: `\x` used with no following hex digits + 1 | fn main() { + 2 | println('\xhh') + | ^ + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/string_escape_x_err_b.vv b/v_windows/v/old/vlib/v/checker/tests/string_escape_x_err_b.vv new file mode 100644 index 0000000..deabe28 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/string_escape_x_err_b.vv @@ -0,0 +1,3 @@ +fn main() { + println('\xhh') +} diff --git a/v_windows/v/old/vlib/v/checker/tests/string_index_assign_error.out b/v_windows/v/old/vlib/v/checker/tests/string_index_assign_error.out new file mode 100644 index 0000000..be9369e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/string_index_assign_error.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/string_index_assign_error.vv:3:6: error: cannot assign to s[i] since V strings are immutable +(note, that variables may be mutable but string values are always immutable, like in Go and Java) + 1 | fn main() { + 2 | mut a := 'V is great!!' + 3 | a[0] = `R` + | ~~~ + 4 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/string_index_assign_error.vv b/v_windows/v/old/vlib/v/checker/tests/string_index_assign_error.vv new file mode 100644 index 0000000..161b215 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/string_index_assign_error.vv @@ -0,0 +1,4 @@ +fn main() { + mut a := 'V is great!!' + a[0] = `R` +} diff --git a/v_windows/v/old/vlib/v/checker/tests/string_index_non_int_err.out b/v_windows/v/old/vlib/v/checker/tests/string_index_non_int_err.out new file mode 100644 index 0000000..81e574a --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/string_index_non_int_err.out @@ -0,0 +1,20 @@ +vlib/v/checker/tests/string_index_non_int_err.vv:3:14: error: non-integer string index `string` + 1 | fn main() { + 2 | v := 'foo' + 3 | println(v['f']) + | ~~~~~ + 4 | println(v[true]) + 5 | println(v[[23]]) +vlib/v/checker/tests/string_index_non_int_err.vv:4:14: error: non-integer string index `bool` + 2 | v := 'foo' + 3 | println(v['f']) + 4 | println(v[true]) + | ~~~~~~ + 5 | println(v[[23]]) + 6 | } +vlib/v/checker/tests/string_index_non_int_err.vv:5:14: error: non-integer string index `[]int` + 3 | println(v['f']) + 4 | println(v[true]) + 5 | println(v[[23]]) + | ~~~~~~ + 6 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/string_index_non_int_err.vv b/v_windows/v/old/vlib/v/checker/tests/string_index_non_int_err.vv new file mode 100644 index 0000000..6619e47 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/string_index_non_int_err.vv @@ -0,0 +1,6 @@ +fn main() { + v := 'foo' + println(v['f']) + println(v[true]) + println(v[[23]]) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/string_interpolation_invalid_fmt.out b/v_windows/v/old/vlib/v/checker/tests/string_interpolation_invalid_fmt.out new file mode 100644 index 0000000..6ac3efe --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/string_interpolation_invalid_fmt.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/string_interpolation_invalid_fmt.vv:3:12: error: format specifier may only be one letter + 1 | fn interpolate_wrong() string { + 2 | a := 5 + 3 | x := '${a:xy}' + | ~~ + 4 | return x + 5 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/string_interpolation_invalid_fmt.vv b/v_windows/v/old/vlib/v/checker/tests/string_interpolation_invalid_fmt.vv new file mode 100644 index 0000000..bfc8db4 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/string_interpolation_invalid_fmt.vv @@ -0,0 +1,5 @@ +fn interpolate_wrong() string { + a := 5 + x := '${a:xy}' + return x +} diff --git a/v_windows/v/old/vlib/v/checker/tests/string_interpolation_wrong_fmt.out b/v_windows/v/old/vlib/v/checker/tests/string_interpolation_wrong_fmt.out new file mode 100644 index 0000000..4c9d7ae --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/string_interpolation_wrong_fmt.out @@ -0,0 +1,63 @@ +vlib/v/checker/tests/string_interpolation_wrong_fmt.vv:3:16: error: precision specification only valid for float types + 1 | fn interpolate_str() string { + 2 | a := 'hallo' + 3 | x := '>${a:8.3s}<' + | ^ + 4 | y := '${a:G}' + 5 | z := '${a:d}' +vlib/v/checker/tests/string_interpolation_wrong_fmt.vv:4:12: error: illegal format specifier `G` for type `string` + 2 | a := 'hallo' + 3 | x := '>${a:8.3s}<' + 4 | y := '${a:G}' + | ^ + 5 | z := '${a:d}' + 6 | return x + y + z +vlib/v/checker/tests/string_interpolation_wrong_fmt.vv:5:12: error: illegal format specifier `d` for type `string` + 3 | x := '>${a:8.3s}<' + 4 | y := '${a:G}' + 5 | z := '${a:d}' + | ^ + 6 | return x + y + z + 7 | } +vlib/v/checker/tests/string_interpolation_wrong_fmt.vv:11:15: error: illegal format specifier `s` for type `f64` + 9 | fn interpolate_f64() string { + 10 | b := 1367.57 + 11 | x := '>${b:20s}<' + | ^ + 12 | y := '${b:d}' + 13 | return x + y +vlib/v/checker/tests/string_interpolation_wrong_fmt.vv:12:12: error: illegal format specifier `d` for type `f64` + 10 | b := 1367.57 + 11 | x := '>${b:20s}<' + 12 | y := '${b:d}' + | ^ + 13 | return x + y + 14 | } +vlib/v/checker/tests/string_interpolation_wrong_fmt.vv:19:14: error: illegal format specifier `d` for type `u32` + 17 | u := u32(15) + 18 | s := -12 + 19 | x := '${u:13d}' + | ^ + 20 | y := '${s:04u}' + 21 | z := '${s:f}' +vlib/v/checker/tests/string_interpolation_wrong_fmt.vv:20:14: error: illegal format specifier `u` for type `int` + 18 | s := -12 + 19 | x := '${u:13d}' + 20 | y := '${s:04u}' + | ^ + 21 | z := '${s:f}' + 22 | q := '${u:v}' +vlib/v/checker/tests/string_interpolation_wrong_fmt.vv:21:12: error: illegal format specifier `f` for type `int` + 19 | x := '${u:13d}' + 20 | y := '${s:04u}' + 21 | z := '${s:f}' + | ^ + 22 | q := '${u:v}' + 23 | return x + y + z + q +vlib/v/checker/tests/string_interpolation_wrong_fmt.vv:22:12: error: unknown format specifier `v` + 20 | y := '${s:04u}' + 21 | z := '${s:f}' + 22 | q := '${u:v}' + | ^ + 23 | return x + y + z + q + 24 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/string_interpolation_wrong_fmt.vv b/v_windows/v/old/vlib/v/checker/tests/string_interpolation_wrong_fmt.vv new file mode 100644 index 0000000..38aa5f3 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/string_interpolation_wrong_fmt.vv @@ -0,0 +1,24 @@ +fn interpolate_str() string { + a := 'hallo' + x := '>${a:8.3s}<' + y := '${a:G}' + z := '${a:d}' + return x + y + z +} + +fn interpolate_f64() string { + b := 1367.57 + x := '>${b:20s}<' + y := '${b:d}' + return x + y +} + +fn interpolate_int() string { + u := u32(15) + s := -12 + x := '${u:13d}' + y := '${s:04u}' + z := '${s:f}' + q := '${u:v}' + return x + y + z + q +} diff --git a/v_windows/v/old/vlib/v/checker/tests/struct_assigned_to_pointer_to_struct.out b/v_windows/v/old/vlib/v/checker/tests/struct_assigned_to_pointer_to_struct.out new file mode 100644 index 0000000..54b047e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/struct_assigned_to_pointer_to_struct.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/struct_assigned_to_pointer_to_struct.vv:5:4: error: mismatched types `&Foo` and `Foo` + 3 | fn main() { + 4 | mut f := &Foo{ 10 } + 5 | f = Foo{ x: 20 } + | ^ + 6 | _ = f + 7 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/struct_assigned_to_pointer_to_struct.vv b/v_windows/v/old/vlib/v/checker/tests/struct_assigned_to_pointer_to_struct.vv new file mode 100644 index 0000000..5e96349 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/struct_assigned_to_pointer_to_struct.vv @@ -0,0 +1,7 @@ +struct Foo { x int } + +fn main() { + mut f := &Foo{ 10 } + f = Foo{ x: 20 } + _ = f +} diff --git a/v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_generic_err.out b/v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_generic_err.out new file mode 100644 index 0000000..62cb81a --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_generic_err.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/struct_cast_to_struct_generic_err.vv:11:7: error: casting to struct is deprecated, use e.g. `Struct{...expr}` instead + 9 | fn main() { + 10 | abc := Abc{} + 11 | _ := Xyz(abc) + | ~~~~~~~~ + 12 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_generic_err.vv b/v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_generic_err.vv new file mode 100644 index 0000000..ce81ba5 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_generic_err.vv @@ -0,0 +1,12 @@ +struct Abc { + name T +} + +struct Xyz { + name string +} + +fn main() { + abc := Abc{} + _ := Xyz(abc) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_mut_err_a.out b/v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_mut_err_a.out new file mode 100644 index 0000000..baafe70 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_mut_err_a.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/struct_cast_to_struct_mut_err_a.vv:12:7: error: casting to struct is deprecated, use e.g. `Struct{...expr}` instead + 10 | fn main() { + 11 | abc := Abc{} + 12 | _ := Xyz(abc) + | ~~~~~~~~ + 13 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_mut_err_a.vv b/v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_mut_err_a.vv new file mode 100644 index 0000000..7a5e4a3 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_mut_err_a.vv @@ -0,0 +1,13 @@ +struct Abc { +mut: + name string +} + +struct Xyz { + name string +} + +fn main() { + abc := Abc{} + _ := Xyz(abc) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_mut_err_b.out b/v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_mut_err_b.out new file mode 100644 index 0000000..aa6f232 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_mut_err_b.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/struct_cast_to_struct_mut_err_b.vv:12:7: error: casting to struct is deprecated, use e.g. `Struct{...expr}` instead + 10 | fn main() { + 11 | abc := Abc{} + 12 | _ := Xyz(abc) + | ~~~~~~~~ + 13 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_mut_err_b.vv b/v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_mut_err_b.vv new file mode 100644 index 0000000..ff34dca --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_mut_err_b.vv @@ -0,0 +1,13 @@ +struct Abc { + name string +} + +struct Xyz { +mut: + name string +} + +fn main() { + abc := Abc{} + _ := Xyz(abc) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_pub_err_a.out b/v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_pub_err_a.out new file mode 100644 index 0000000..0a2778b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_pub_err_a.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/struct_cast_to_struct_pub_err_a.vv:12:7: error: casting to struct is deprecated, use e.g. `Struct{...expr}` instead + 10 | fn main() { + 11 | abc := Abc{} + 12 | _ := Xyz(abc) + | ~~~~~~~~ + 13 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_pub_err_a.vv b/v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_pub_err_a.vv new file mode 100644 index 0000000..7c559e4 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_pub_err_a.vv @@ -0,0 +1,13 @@ +struct Abc { +pub: + name string +} + +struct Xyz { + name string +} + +fn main() { + abc := Abc{} + _ := Xyz(abc) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_pub_err_b.out b/v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_pub_err_b.out new file mode 100644 index 0000000..bbc7e33 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_pub_err_b.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/struct_cast_to_struct_pub_err_b.vv:12:7: error: casting to struct is deprecated, use e.g. `Struct{...expr}` instead + 10 | fn main() { + 11 | abc := Abc{} + 12 | _ := Xyz(abc) + | ~~~~~~~~ + 13 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_pub_err_b.vv b/v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_pub_err_b.vv new file mode 100644 index 0000000..aa92278 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/struct_cast_to_struct_pub_err_b.vv @@ -0,0 +1,13 @@ +struct Abc { + name string +} + +struct Xyz { +pub: + name string +} + +fn main() { + abc := Abc{} + _ := Xyz(abc) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/struct_embed_invalid_type.out b/v_windows/v/old/vlib/v/checker/tests/struct_embed_invalid_type.out new file mode 100644 index 0000000..d85190f --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/struct_embed_invalid_type.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/struct_embed_invalid_type.vv:4:2: error: `Foo` is not a struct + 2 | + 3 | struct Bar { + 4 | Foo + | ~~~ + 5 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/struct_embed_invalid_type.vv b/v_windows/v/old/vlib/v/checker/tests/struct_embed_invalid_type.vv new file mode 100644 index 0000000..13f3c52 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/struct_embed_invalid_type.vv @@ -0,0 +1,5 @@ +type Foo = int + +struct Bar { + Foo +} diff --git a/v_windows/v/old/vlib/v/checker/tests/struct_field_name_duplicate_err.out b/v_windows/v/old/vlib/v/checker/tests/struct_field_name_duplicate_err.out new file mode 100644 index 0000000..691268c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/struct_field_name_duplicate_err.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/struct_field_name_duplicate_err.vv:3:2: error: field name `a` duplicate + 1 | struct Aaa { + 2 | a int + 3 | a string + | ~~~~~~~~ + 4 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/struct_field_name_duplicate_err.vv b/v_windows/v/old/vlib/v/checker/tests/struct_field_name_duplicate_err.vv new file mode 100644 index 0000000..487b0a7 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/struct_field_name_duplicate_err.vv @@ -0,0 +1,4 @@ +struct Aaa { + a int + a string +} diff --git a/v_windows/v/old/vlib/v/checker/tests/struct_field_type_err.out b/v_windows/v/old/vlib/v/checker/tests/struct_field_type_err.out new file mode 100644 index 0000000..6c8e6df --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/struct_field_type_err.out @@ -0,0 +1,43 @@ +vlib/v/checker/tests/struct_field_type_err.vv:12:3: error: cannot assign to field `n`: expected `int`, not `bool` + 10 | fn main() { + 11 | mut data := Data{ + 12 | n: true + | ~~~~~~~ + 13 | b: 0 + 14 | f1: fn (v ...voidptr) {} +vlib/v/checker/tests/struct_field_type_err.vv:13:3: error: cannot assign to field `b`: expected `bool`, not `int literal` + 11 | mut data := Data{ + 12 | n: true + 13 | b: 0 + | ~~~~ + 14 | f1: fn (v ...voidptr) {} + 15 | f2: fn (v voidptr) {} +vlib/v/checker/tests/struct_field_type_err.vv:14:3: error: cannot assign to field `f1`: expected `fn (voidptr)`, not `fn (...voidptr)` + 12 | n: true + 13 | b: 0 + 14 | f1: fn (v ...voidptr) {} + | ~~~~~~~~~~~~~~~~~~~~~~~~ + 15 | f2: fn (v voidptr) {} + 16 | data: true +Details: ``'s expected fn argument: `` is a pointer, but the passed fn argument: `v` is NOT a pointer +vlib/v/checker/tests/struct_field_type_err.vv:15:3: error: cannot assign to field `f2`: expected `fn (...voidptr)`, not `fn (voidptr)` + 13 | b: 0 + 14 | f1: fn (v ...voidptr) {} + 15 | f2: fn (v voidptr) {} + | ~~~~~~~~~~~~~~~~~~~~~ + 16 | data: true + 17 | } +Details: ``'s expected fn argument: `` is NOT a pointer, but the passed fn argument: `v` is a pointer +vlib/v/checker/tests/struct_field_type_err.vv:16:3: error: cannot assign to field `data`: expected `&Data`, not `bool` + 14 | f1: fn (v ...voidptr) {} + 15 | f2: fn (v voidptr) {} + 16 | data: true + | ~~~~~~~~~~ + 17 | } + 18 | +vlib/v/checker/tests/struct_field_type_err.vv:19:11: error: cannot assign to `data.n`: expected `int`, not `bool` + 17 | } + 18 | + 19 | data.n = true + | ~~~~ + 20 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/struct_field_type_err.vv b/v_windows/v/old/vlib/v/checker/tests/struct_field_type_err.vv new file mode 100644 index 0000000..44850c2 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/struct_field_type_err.vv @@ -0,0 +1,20 @@ +struct Data { +mut: + n int + b bool + f1 fn (voidptr) + f2 fn (...voidptr) + data &Data +} + +fn main() { + mut data := Data{ + n: true + b: 0 + f1: fn (v ...voidptr) {} + f2: fn (v voidptr) {} + data: true + } + + data.n = true +} diff --git a/v_windows/v/old/vlib/v/checker/tests/struct_init_update_type_err.out b/v_windows/v/old/vlib/v/checker/tests/struct_init_update_type_err.out new file mode 100644 index 0000000..c0eab9e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/struct_init_update_type_err.out @@ -0,0 +1,35 @@ +vlib/v/checker/tests/struct_init_update_type_err.vv:11:6: error: expected struct, found `int` + 9 | i := 2 + 10 | _ := Foo{ + 11 | ...i + | ^ + 12 | name: 'f2' + 13 | } +vlib/v/checker/tests/struct_init_update_type_err.vv:16:6: error: expected struct, found `&int` + 14 | p := &i + 15 | _ = Foo{ + 16 | ...p + | ^ + 17 | } + 18 | f2 := Foo2{} +vlib/v/checker/tests/struct_init_update_type_err.vv:20:6: error: struct `Foo2` is not compatible with struct `Foo` + 18 | f2 := Foo2{} + 19 | _ = Foo{ + 20 | ...f2 + | ~~ + 21 | } + 22 | _ = Foo{ +vlib/v/checker/tests/struct_init_update_type_err.vv:23:6: error: expression is not an lvalue + 21 | } + 22 | _ = Foo{ + 23 | ...Foo{} + | ~~~~~ + 24 | } + 25 | } +vlib/v/checker/tests/struct_init_update_type_err.vv:32:6: error: struct `Empty` is not compatible with struct `Foo` + 30 | e := Empty{} + 31 | _ = Foo{ + 32 | ...e + | ^ + 33 | } + 34 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/struct_init_update_type_err.vv b/v_windows/v/old/vlib/v/checker/tests/struct_init_update_type_err.vv new file mode 100644 index 0000000..6f8deeb --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/struct_init_update_type_err.vv @@ -0,0 +1,35 @@ +struct Foo { + name string + age int +} + +struct Foo2 {b bool} + +fn main() { + i := 2 + _ := Foo{ + ...i + name: 'f2' + } + p := &i + _ = Foo{ + ...p + } + f2 := Foo2{} + _ = Foo{ + ...f2 + } + _ = Foo{ + ...Foo{} + } +} + +struct Empty {} + +fn empty() { + e := Empty{} + _ = Foo{ + ...e + } +} + diff --git a/v_windows/v/old/vlib/v/checker/tests/struct_pub_field.out b/v_windows/v/old/vlib/v/checker/tests/struct_pub_field.out new file mode 100644 index 0000000..c0c34c7 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/struct_pub_field.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/struct_pub_field.vv:9:4: error: field `i` of struct `Foo` is immutable + 7 | i: 1 + 8 | } + 9 | a.i = 2 + | ^ + 10 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/struct_pub_field.vv b/v_windows/v/old/vlib/v/checker/tests/struct_pub_field.vv new file mode 100644 index 0000000..1f104ca --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/struct_pub_field.vv @@ -0,0 +1,10 @@ +struct Foo { + i int +} + +fn main() { + a := Foo{ + i: 1 + } + a.i = 2 +} diff --git a/v_windows/v/old/vlib/v/checker/tests/struct_required_field.out b/v_windows/v/old/vlib/v/checker/tests/struct_required_field.out new file mode 100644 index 0000000..212ca63 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/struct_required_field.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/struct_required_field.vv:12:6: error: field `Abc.f3` must be initialized + 10 | f3: 789 + 11 | } + 12 | _ = Abc{ + | ~~~~ + 13 | f1: 123 + 14 | f2: 789 diff --git a/v_windows/v/old/vlib/v/checker/tests/struct_required_field.vv b/v_windows/v/old/vlib/v/checker/tests/struct_required_field.vv new file mode 100644 index 0000000..85b2a4d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/struct_required_field.vv @@ -0,0 +1,16 @@ +struct Abc { + f1 int [required] + f2 int + f3 int [required] +} + +fn main() { + _ = Abc{ + f1: 123 + f3: 789 + } + _ = Abc{ + f1: 123 + f2: 789 + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/struct_required_fn_field.out b/v_windows/v/old/vlib/v/checker/tests/struct_required_fn_field.out new file mode 100644 index 0000000..25d6ee8 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/struct_required_fn_field.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/struct_required_fn_field.vv:12:6: error: field `Abc.f3` must be initialized + 10 | f3: fn () {} + 11 | } + 12 | _ = Abc{ + | ~~~~ + 13 | f1: 123 + 14 | f2: 789 diff --git a/v_windows/v/old/vlib/v/checker/tests/struct_required_fn_field.vv b/v_windows/v/old/vlib/v/checker/tests/struct_required_fn_field.vv new file mode 100644 index 0000000..e72cfe6 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/struct_required_fn_field.vv @@ -0,0 +1,16 @@ +struct Abc { + f1 int [required] + f2 int + f3 fn () [attr1; required; attr2] +} + +fn main() { + _ = Abc{ + f1: 123 + f3: fn () {} + } + _ = Abc{ + f1: 123 + f2: 789 + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/struct_short_init_warning.out b/v_windows/v/old/vlib/v/checker/tests/struct_short_init_warning.out new file mode 100644 index 0000000..41daa52 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/struct_short_init_warning.out @@ -0,0 +1,13 @@ +vlib/v/checker/tests/struct_short_init_warning.vv:6:9: warning: short struct initalization is deprecated, use explicit struct name + 4 | + 5 | fn f(foo Foo) Foo { + 6 | return {v: foo.v} + | ~~~~~~~~~~ + 7 | } + 8 | +vlib/v/checker/tests/struct_short_init_warning.vv:10:4: warning: short struct initalization is deprecated, use explicit struct name + 8 | + 9 | fn main() { + 10 | f({v: 10}) + | ~~~~~~~ + 11 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/struct_short_init_warning.vv b/v_windows/v/old/vlib/v/checker/tests/struct_short_init_warning.vv new file mode 100644 index 0000000..74bd02f --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/struct_short_init_warning.vv @@ -0,0 +1,11 @@ +struct Foo { + v int +} + +fn f(foo Foo) Foo { + return {v: foo.v} +} + +fn main() { + f({v: 10}) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/struct_type_cast_err.out b/v_windows/v/old/vlib/v/checker/tests/struct_type_cast_err.out new file mode 100644 index 0000000..4648a27 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/struct_type_cast_err.out @@ -0,0 +1,63 @@ +vlib/v/checker/tests/struct_type_cast_err.vv:5:10: error: cannot cast struct to `string` + 3 | fn main() { + 4 | foo := Foo{} + 5 | _ := string(foo) + | ~~~~~~~~~~~ + 6 | _ := int(foo) + 7 | _ := u64(foo) +vlib/v/checker/tests/struct_type_cast_err.vv:6:10: error: cannot cast struct to `int` + 4 | foo := Foo{} + 5 | _ := string(foo) + 6 | _ := int(foo) + | ~~~~~~~~ + 7 | _ := u64(foo) + 8 | _ := u32(foo) +vlib/v/checker/tests/struct_type_cast_err.vv:7:10: error: cannot cast struct to `u64` + 5 | _ := string(foo) + 6 | _ := int(foo) + 7 | _ := u64(foo) + | ~~~~~~~~ + 8 | _ := u32(foo) + 9 | _ := rune(foo) +vlib/v/checker/tests/struct_type_cast_err.vv:8:10: error: cannot cast struct to `u32` + 6 | _ := int(foo) + 7 | _ := u64(foo) + 8 | _ := u32(foo) + | ~~~~~~~~ + 9 | _ := rune(foo) + 10 | _ := byte(foo) +vlib/v/checker/tests/struct_type_cast_err.vv:9:10: error: cannot cast struct to `rune` + 7 | _ := u64(foo) + 8 | _ := u32(foo) + 9 | _ := rune(foo) + | ~~~~~~~~~ + 10 | _ := byte(foo) + 11 | _ := i8(foo) +vlib/v/checker/tests/struct_type_cast_err.vv:10:10: error: cannot cast type `Foo` to `byte` + 8 | _ := u32(foo) + 9 | _ := rune(foo) + 10 | _ := byte(foo) + | ~~~~~~~~~ + 11 | _ := i8(foo) + 12 | _ := i64(foo) +vlib/v/checker/tests/struct_type_cast_err.vv:11:10: error: cannot cast struct to `i8` + 9 | _ := rune(foo) + 10 | _ := byte(foo) + 11 | _ := i8(foo) + | ~~~~~~~ + 12 | _ := i64(foo) + 13 | _ := int(foo) +vlib/v/checker/tests/struct_type_cast_err.vv:12:10: error: cannot cast struct to `i64` + 10 | _ := byte(foo) + 11 | _ := i8(foo) + 12 | _ := i64(foo) + | ~~~~~~~~ + 13 | _ := int(foo) + 14 | _ = &I1(foo) +vlib/v/checker/tests/struct_type_cast_err.vv:13:10: error: cannot cast struct to `int` + 11 | _ := i8(foo) + 12 | _ := i64(foo) + 13 | _ := int(foo) + | ~~~~~~~~ + 14 | _ = &I1(foo) + 15 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/struct_type_cast_err.vv b/v_windows/v/old/vlib/v/checker/tests/struct_type_cast_err.vv new file mode 100644 index 0000000..a27eefd --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/struct_type_cast_err.vv @@ -0,0 +1,17 @@ +struct Foo{} + +fn main() { + foo := Foo{} + _ := string(foo) + _ := int(foo) + _ := u64(foo) + _ := u32(foo) + _ := rune(foo) + _ := byte(foo) + _ := i8(foo) + _ := i64(foo) + _ := int(foo) + _ = &I1(foo) +} + +interface I1{} diff --git a/v_windows/v/old/vlib/v/checker/tests/struct_unknown_field.out b/v_windows/v/old/vlib/v/checker/tests/struct_unknown_field.out new file mode 100644 index 0000000..2c544b0 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/struct_unknown_field.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/struct_unknown_field.vv:8:3: error: unknown field `bar` in struct literal of type `Test` + 6 | t := Test{ + 7 | foo: true + 8 | bar: false + | ~~~~~~~~~~ + 9 | } + 10 | _ = t diff --git a/v_windows/v/old/vlib/v/checker/tests/struct_unknown_field.vv b/v_windows/v/old/vlib/v/checker/tests/struct_unknown_field.vv new file mode 100644 index 0000000..018bcef --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/struct_unknown_field.vv @@ -0,0 +1,11 @@ +struct Test { + foo bool +} + +fn main() { + t := Test{ + foo: true + bar: false + } + _ = t +} diff --git a/v_windows/v/old/vlib/v/checker/tests/struct_unneeded_default.out b/v_windows/v/old/vlib/v/checker/tests/struct_unneeded_default.out new file mode 100644 index 0000000..029f9fe --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/struct_unneeded_default.out @@ -0,0 +1,20 @@ +vlib/v/checker/tests/struct_unneeded_default.vv:2:10: error: unnecessary default value of `0`: struct fields are zeroed by default + 1 | struct Test { + 2 | n int = 0 + | ^ + 3 | s string = '' + 4 | b bool = false +vlib/v/checker/tests/struct_unneeded_default.vv:3:13: error: unnecessary default value of '': struct fields are zeroed by default + 1 | struct Test { + 2 | n int = 0 + 3 | s string = '' + | ~~ + 4 | b bool = false + 5 | } +vlib/v/checker/tests/struct_unneeded_default.vv:4:11: error: unnecessary default value `false`: struct fields are zeroed by default + 2 | n int = 0 + 3 | s string = '' + 4 | b bool = false + | ~~~~~ + 5 | } + 6 | \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/struct_unneeded_default.vv b/v_windows/v/old/vlib/v/checker/tests/struct_unneeded_default.vv new file mode 100644 index 0000000..7ddc066 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/struct_unneeded_default.vv @@ -0,0 +1,8 @@ +struct Test { + n int = 0 + s string = '' + b bool = false +} + +fn main() { +} diff --git a/v_windows/v/old/vlib/v/checker/tests/sum.out b/v_windows/v/old/vlib/v/checker/tests/sum.out new file mode 100644 index 0000000..5c92692 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/sum.out @@ -0,0 +1,28 @@ +vlib/v/checker/tests/sum.vv:5:8: error: cannot cast non-sum type `int` using `as` + 3 | fn non_sum() { + 4 | v := 4 + 5 | _ = v as rune + | ~~ + 6 | _ = v as Var + 7 | } +vlib/v/checker/tests/sum.vv:6:8: error: cannot cast non-sum type `int` using `as` - use e.g. `Var(some_expr)` instead. + 4 | v := 4 + 5 | _ = v as rune + 6 | _ = v as Var + | ~~ + 7 | } + 8 | +vlib/v/checker/tests/sum.vv:10:7: error: cannot cast `rune` to `Var` + 8 | + 9 | fn sum() { + 10 | _ := Var(`J`) + | ~~~~~~~~ + 11 | mut s2 := Var('') + 12 | s2 = true +vlib/v/checker/tests/sum.vv:12:7: error: cannot assign to `s2`: expected `Var`, not `bool` + 10 | _ := Var(`J`) + 11 | mut s2 := Var('') + 12 | s2 = true + | ~~~~ + 13 | _ = s2 + 14 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/sum.vv b/v_windows/v/old/vlib/v/checker/tests/sum.vv new file mode 100644 index 0000000..6796a2e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/sum.vv @@ -0,0 +1,14 @@ +type Var = int | string + +fn non_sum() { + v := 4 + _ = v as rune + _ = v as Var +} + +fn sum() { + _ := Var(`J`) + mut s2 := Var('') + s2 = true + _ = s2 +} diff --git a/v_windows/v/old/vlib/v/checker/tests/sum_type_assign_non_variant_err.out b/v_windows/v/old/vlib/v/checker/tests/sum_type_assign_non_variant_err.out new file mode 100644 index 0000000..1a9ea99 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/sum_type_assign_non_variant_err.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/sum_type_assign_non_variant_err.vv:11:6: error: cannot assign to `w`: expected `Stmt`, not `IfExpr` + 9 | fn main() { + 10 | mut w := Stmt(AnotherThing{}) + 11 | w = IfExpr{} + | ~~~~~~~~ + 12 | _ = w + 13 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/sum_type_assign_non_variant_err.vv b/v_windows/v/old/vlib/v/checker/tests/sum_type_assign_non_variant_err.vv new file mode 100644 index 0000000..1ab9a2c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/sum_type_assign_non_variant_err.vv @@ -0,0 +1,13 @@ +type Expr = IfExpr | CallExpr | MatchExpr +struct MatchExpr {} +struct IfExpr {} +struct CallExpr {} + +type Stmt = Expr | AnotherThing +struct AnotherThing {} + +fn main() { + mut w := Stmt(AnotherThing{}) + w = IfExpr{} + _ = w +} diff --git a/v_windows/v/old/vlib/v/checker/tests/sum_type_common_fields_alias_error.out b/v_windows/v/old/vlib/v/checker/tests/sum_type_common_fields_alias_error.out new file mode 100644 index 0000000..20c3b00 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/sum_type_common_fields_alias_error.out @@ -0,0 +1,20 @@ +vlib/v/checker/tests/sum_type_common_fields_alias_error.vv:35:14: error: field `name` does not exist or have the same type in all sumtype variants + 33 | } + 34 | println(m) + 35 | assert m[0].name == 'abc' + | ~~~~ + 36 | assert m[1].name == 'def' + 37 | assert m[2].name == 'xyz' +vlib/v/checker/tests/sum_type_common_fields_alias_error.vv:36:14: error: field `name` does not exist or have the same type in all sumtype variants + 34 | println(m) + 35 | assert m[0].name == 'abc' + 36 | assert m[1].name == 'def' + | ~~~~ + 37 | assert m[2].name == 'xyz' + 38 | } +vlib/v/checker/tests/sum_type_common_fields_alias_error.vv:37:14: error: field `name` does not exist or have the same type in all sumtype variants + 35 | assert m[0].name == 'abc' + 36 | assert m[1].name == 'def' + 37 | assert m[2].name == 'xyz' + | ~~~~ + 38 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/sum_type_common_fields_alias_error.vv b/v_windows/v/old/vlib/v/checker/tests/sum_type_common_fields_alias_error.vv new file mode 100644 index 0000000..9a296b8 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/sum_type_common_fields_alias_error.vv @@ -0,0 +1,38 @@ +type Main = Sub1 | Sub2 | Sub3 + +// NB: the subtypes will have a common `name` field, of the same `string` +// type, except Sub3, which has `name` of type AliasedString. + +type AliasedString = string + +struct Sub1 { +mut: + name string +} + +struct Sub2 { +mut: + name string +} + +struct Sub3 { +mut: + name AliasedString +} + +fn main() { + mut m := []Main{} + m << Sub1{ + name: 'abc' + } + m << Sub2{ + name: 'def' + } + m << Sub3{ + name: 'xyz' + } + println(m) + assert m[0].name == 'abc' + assert m[1].name == 'def' + assert m[2].name == 'xyz' +} diff --git a/v_windows/v/old/vlib/v/checker/tests/sum_type_common_fields_error.out b/v_windows/v/old/vlib/v/checker/tests/sum_type_common_fields_error.out new file mode 100644 index 0000000..9dce91a --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/sum_type_common_fields_error.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/sum_type_common_fields_error.vv:53:14: error: field `val` does not exist or have the same type in all sumtype variants + 51 | assert m[2].name == '64bit integer' + 52 | assert m[3].name == 'string' + 53 | assert m[0].val == 123 + | ~~~ + 54 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/sum_type_common_fields_error.vv b/v_windows/v/old/vlib/v/checker/tests/sum_type_common_fields_error.vv new file mode 100644 index 0000000..2da560a --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/sum_type_common_fields_error.vv @@ -0,0 +1,54 @@ +type Main = Sub1 | Sub2 | Sub3 | Sub4 + +// NB: all subtypes have a common name field, of the same `string` type +// but they also have a field `val` that is of a different type in the +// different subtypes => accessing `m[0].name` is fine, but *not* `m[0].val` +struct Sub1 { +mut: + val int + name string +} + +struct Sub2 { +mut: + val f32 + name string +} + +struct Sub3 { +mut: + val i64 + name string +} + +struct Sub4 { +mut: + val string + name string +} + +fn main() { + mut m := []Main{} + m << Sub1{ + val: 123 + name: 'integer' + } + m << Sub2{ + val: 3.14 + name: 'float' + } + m << Sub3{ + val: 9_876_543_210 + name: '64bit integer' + } + m << Sub4{ + val: 'abcd' + name: 'string' + } + println(m) + assert m[0].name == 'integer' + assert m[1].name == 'float' + assert m[2].name == '64bit integer' + assert m[3].name == 'string' + assert m[0].val == 123 +} diff --git a/v_windows/v/old/vlib/v/checker/tests/sum_type_exists.out b/v_windows/v/old/vlib/v/checker/tests/sum_type_exists.out new file mode 100644 index 0000000..a580ba0 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/sum_type_exists.out @@ -0,0 +1,3 @@ +vlib/v/checker/tests/sum_type_exists.vv:1:22: error: unknown type `Inexistant` + 1 | type Miscellaneous = Inexistant | Nope | int + | ~~~~~~~~~~ \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/sum_type_exists.vv b/v_windows/v/old/vlib/v/checker/tests/sum_type_exists.vv new file mode 100644 index 0000000..28a4475 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/sum_type_exists.vv @@ -0,0 +1 @@ +type Miscellaneous = Inexistant | Nope | int diff --git a/v_windows/v/old/vlib/v/checker/tests/sum_type_infix_err.out b/v_windows/v/old/vlib/v/checker/tests/sum_type_infix_err.out new file mode 100644 index 0000000..6a06f1b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/sum_type_infix_err.out @@ -0,0 +1,14 @@ +vlib/v/checker/tests/sum_type_infix_err.vv:5:9: error: cannot use operator `+` with `Abc` + 3 | fn main() { + 4 | x := Abc(0) + 5 | _ := x + Abc(5) + | ^ + 6 | _ := 123 + x + 7 | _ = unsafe{&x + 5} +vlib/v/checker/tests/sum_type_infix_err.vv:6:11: error: cannot use operator `+` with `Abc` + 4 | x := Abc(0) + 5 | _ := x + Abc(5) + 6 | _ := 123 + x + | ^ + 7 | _ = unsafe{&x + 5} + 8 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/sum_type_infix_err.vv b/v_windows/v/old/vlib/v/checker/tests/sum_type_infix_err.vv new file mode 100644 index 0000000..938dd6f --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/sum_type_infix_err.vv @@ -0,0 +1,8 @@ +type Abc = int | string + +fn main() { + x := Abc(0) + _ := x + Abc(5) + _ := 123 + x + _ = unsafe{&x + 5} +} diff --git a/v_windows/v/old/vlib/v/checker/tests/sum_type_multiple_type_define.out b/v_windows/v/old/vlib/v/checker/tests/sum_type_multiple_type_define.out new file mode 100644 index 0000000..7052f3b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/sum_type_multiple_type_define.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/sum_type_multiple_type_define.vv:3:22: error: sum type FooType cannot hold the type `Foo` more than once + 1 | struct Foo {} + 2 | + 3 | type FooType = Foo | Foo + | ~~~ diff --git a/v_windows/v/old/vlib/v/checker/tests/sum_type_multiple_type_define.vv b/v_windows/v/old/vlib/v/checker/tests/sum_type_multiple_type_define.vv new file mode 100644 index 0000000..7ec43d0 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/sum_type_multiple_type_define.vv @@ -0,0 +1,3 @@ +struct Foo {} + +type FooType = Foo | Foo diff --git a/v_windows/v/old/vlib/v/checker/tests/sum_type_mutable_cast_err.out b/v_windows/v/old/vlib/v/checker/tests/sum_type_mutable_cast_err.out new file mode 100644 index 0000000..a880221 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/sum_type_mutable_cast_err.out @@ -0,0 +1,14 @@ +vlib/v/checker/tests/sum_type_mutable_cast_err.vv:15:10: error: cannot use operator `+` with `Abc` + 13 | mut x := Abc(0) + 14 | if x is int { + 15 | _ := x + 5 + | ^ + 16 | } + 17 | mut f := Foo{Bar{Abc(0)}} +vlib/v/checker/tests/sum_type_mutable_cast_err.vv:19:14: error: cannot use operator `+` with `Abc` + 17 | mut f := Foo{Bar{Abc(0)}} + 18 | if f.b.a is int { + 19 | _ := f.b.a + 5 + | ^ + 20 | } + 21 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/sum_type_mutable_cast_err.vv b/v_windows/v/old/vlib/v/checker/tests/sum_type_mutable_cast_err.vv new file mode 100644 index 0000000..d122a12 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/sum_type_mutable_cast_err.vv @@ -0,0 +1,21 @@ +type Abc = int | string + +struct Bar { +mut: + a Abc +} + +struct Foo { + b Bar +} + +fn main() { + mut x := Abc(0) + if x is int { + _ := x + 5 + } + mut f := Foo{Bar{Abc(0)}} + if f.b.a is int { + _ := f.b.a + 5 + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/sum_type_ref_variant_err.out b/v_windows/v/old/vlib/v/checker/tests/sum_type_ref_variant_err.out new file mode 100644 index 0000000..d893a2b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/sum_type_ref_variant_err.out @@ -0,0 +1,18 @@ +vlib/v/checker/tests/sum_type_ref_variant_err.vv:7:33: error: sum type cannot hold a reference type + 5 | foo string + 6 | } + 7 | type Alphabet1 = Abc | string | &Xyz + | ~~~~ + 8 | type Alphabet2 = Abc | &Xyz | string + 9 | type Alphabet3 = &Xyz | Abc | string +vlib/v/checker/tests/sum_type_ref_variant_err.vv:8:24: error: sum type cannot hold a reference type + 6 | } + 7 | type Alphabet1 = Abc | string | &Xyz + 8 | type Alphabet2 = Abc | &Xyz | string + | ~~~~ + 9 | type Alphabet3 = &Xyz | Abc | string +vlib/v/checker/tests/sum_type_ref_variant_err.vv:9:18: error: sum type cannot hold a reference type + 7 | type Alphabet1 = Abc | string | &Xyz + 8 | type Alphabet2 = Abc | &Xyz | string + 9 | type Alphabet3 = &Xyz | Abc | string + | ~~~~ \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/sum_type_ref_variant_err.vv b/v_windows/v/old/vlib/v/checker/tests/sum_type_ref_variant_err.vv new file mode 100644 index 0000000..def2a54 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/sum_type_ref_variant_err.vv @@ -0,0 +1,9 @@ +struct Abc { + val string +} +struct Xyz { + foo string +} +type Alphabet1 = Abc | string | &Xyz +type Alphabet2 = Abc | &Xyz | string +type Alphabet3 = &Xyz | Abc | string \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/sumtype_in_sumtype_err.out b/v_windows/v/old/vlib/v/checker/tests/sumtype_in_sumtype_err.out new file mode 100644 index 0000000..f491079 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/sumtype_in_sumtype_err.out @@ -0,0 +1,3 @@ +vlib/v/checker/tests/sumtype_in_sumtype_err.vv:1:11: error: sum type cannot hold itself + 1 | type AA = AA | int + | ~~ \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/sumtype_in_sumtype_err.vv b/v_windows/v/old/vlib/v/checker/tests/sumtype_in_sumtype_err.vv new file mode 100644 index 0000000..7e53df8 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/sumtype_in_sumtype_err.vv @@ -0,0 +1 @@ +type AA = AA | int diff --git a/v_windows/v/old/vlib/v/checker/tests/sumtype_mismatched_type.out b/v_windows/v/old/vlib/v/checker/tests/sumtype_mismatched_type.out new file mode 100644 index 0000000..813f472 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/sumtype_mismatched_type.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/sumtype_mismatched_type.vv:4:8: error: infix expr: cannot use `int literal` (right expression) as `AA` + 2 | + 3 | a := AA(3) + 4 | assert a == 3 + | ~~~~~~ \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/sumtype_mismatched_type.vv b/v_windows/v/old/vlib/v/checker/tests/sumtype_mismatched_type.vv new file mode 100644 index 0000000..d6a998b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/sumtype_mismatched_type.vv @@ -0,0 +1,4 @@ +type AA = int | string + +a := AA(3) +assert a == 3 diff --git a/v_windows/v/old/vlib/v/checker/tests/templates/index.html b/v_windows/v/old/vlib/v/checker/tests/templates/index.html new file mode 100644 index 0000000..7624de1 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/templates/index.html @@ -0,0 +1 @@ +@test diff --git a/v_windows/v/old/vlib/v/checker/tests/test_functions_should_not_return_test.out b/v_windows/v/old/vlib/v/checker/tests/test_functions_should_not_return_test.out new file mode 100644 index 0000000..0972921 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/test_functions_should_not_return_test.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/test_functions_should_not_return_test.vv:9:1: error: test functions should either return nothing at all, or be marked to return `?` + 7 | + 8 | // should be disallowed: + 9 | fn test_returning_int() int { + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 10 | + 11 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/test_functions_should_not_return_test.vv b/v_windows/v/old/vlib/v/checker/tests/test_functions_should_not_return_test.vv new file mode 100644 index 0000000..3bebcea --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/test_functions_should_not_return_test.vv @@ -0,0 +1,17 @@ +// ordinary functions can return whatever they like, +// since they are not called by V's testing system +// in the generated main(): +fn abc() int { + return 1 +} + +// should be disallowed: +fn test_returning_int() int { + +} + +// NB: this is allowed explicitly now, to allow for shorter tests +// of functions returning optionals. +fn test_returning_opt() ? { + assert true +} diff --git a/v_windows/v/old/vlib/v/checker/tests/trailing_comma_struct_attr.out b/v_windows/v/old/vlib/v/checker/tests/trailing_comma_struct_attr.out new file mode 100644 index 0000000..eed55bd --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/trailing_comma_struct_attr.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/trailing_comma_struct_attr.vv:3:31: error: unexpected token `]`, expecting name + 1 | struct User { + 2 | name string + 3 | jobs []string [json:jobss;] + | ^ + 4 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/trailing_comma_struct_attr.vv b/v_windows/v/old/vlib/v/checker/tests/trailing_comma_struct_attr.vv new file mode 100644 index 0000000..d20a3d4 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/trailing_comma_struct_attr.vv @@ -0,0 +1,4 @@ +struct User { + name string + jobs []string [json:jobss;] +} \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/type_cast_optional_err.out b/v_windows/v/old/vlib/v/checker/tests/type_cast_optional_err.out new file mode 100644 index 0000000..bb52bad --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/type_cast_optional_err.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/type_cast_optional_err.vv:2:13: error: cannot type cast an optional + 1 | fn main() { + 2 | println(int('hi'.last_index('i'))) + | ~~~~~~~~~~~~~~~~~~~~~~~~~ + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/type_cast_optional_err.vv b/v_windows/v/old/vlib/v/checker/tests/type_cast_optional_err.vv new file mode 100644 index 0000000..982559c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/type_cast_optional_err.vv @@ -0,0 +1,3 @@ +fn main() { + println(int('hi'.last_index('i'))) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/typedef_attr_v_struct_err.out b/v_windows/v/old/vlib/v/checker/tests/typedef_attr_v_struct_err.out new file mode 100644 index 0000000..0253fba --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/typedef_attr_v_struct_err.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/typedef_attr_v_struct_err.vv:2:1: error: `typedef` attribute can only be used with C structs + 1 | [typedef] + 2 | struct Point{} + | ~~~~~~~~~~~~ + 3 | + 4 | fn main() { diff --git a/v_windows/v/old/vlib/v/checker/tests/typedef_attr_v_struct_err.vv b/v_windows/v/old/vlib/v/checker/tests/typedef_attr_v_struct_err.vv new file mode 100644 index 0000000..421b635 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/typedef_attr_v_struct_err.vv @@ -0,0 +1,7 @@ +[typedef] +struct Point{} + +fn main() { + p := Point{} + println(p) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/undefined_ident_of_struct.out b/v_windows/v/old/vlib/v/checker/tests/undefined_ident_of_struct.out new file mode 100644 index 0000000..35e0a6c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/undefined_ident_of_struct.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/undefined_ident_of_struct.vv:4:2: error: undefined ident: `f` + 2 | + 3 | fn get() { + 4 | f.a = 'test' + | ^ + 5 | } + 6 | diff --git a/v_windows/v/old/vlib/v/checker/tests/undefined_ident_of_struct.vv b/v_windows/v/old/vlib/v/checker/tests/undefined_ident_of_struct.vv new file mode 100644 index 0000000..a426195 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/undefined_ident_of_struct.vv @@ -0,0 +1,9 @@ +module main + +fn get() { + f.a = 'test' +} + +fn main() { + println('hello') +} diff --git a/v_windows/v/old/vlib/v/checker/tests/unexpected_or.out b/v_windows/v/old/vlib/v/checker/tests/unexpected_or.out new file mode 100644 index 0000000..ab1e077 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unexpected_or.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/unexpected_or.vv:6:17: error: unexpected `or` block, the function `ret_zero` does not return an optional + 4 | + 5 | fn main() { + 6 | _ = ret_zero() or { 1 } + | ~~~~~~~~ + 7 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/unexpected_or.vv b/v_windows/v/old/vlib/v/checker/tests/unexpected_or.vv new file mode 100644 index 0000000..48a8696 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unexpected_or.vv @@ -0,0 +1,7 @@ +fn ret_zero() int { + return 0 +} + +fn main() { + _ = ret_zero() or { 1 } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/unexpected_or_propagate.out b/v_windows/v/old/vlib/v/checker/tests/unexpected_or_propagate.out new file mode 100644 index 0000000..411748d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unexpected_or_propagate.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/unexpected_or_propagate.vv:6:17: error: unexpected `?`, the function `ret_zero` does not return an optional + 4 | + 5 | fn opt_fn() ?int { + 6 | a := ret_zero()? + | ^ + 7 | return a + 8 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/unexpected_or_propagate.vv b/v_windows/v/old/vlib/v/checker/tests/unexpected_or_propagate.vv new file mode 100644 index 0000000..16ef7b0 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unexpected_or_propagate.vv @@ -0,0 +1,12 @@ +fn ret_zero() int { + return 0 +} + +fn opt_fn() ?int { + a := ret_zero()? + return a +} + +fn main() { + opt_fn() or {} +} diff --git a/v_windows/v/old/vlib/v/checker/tests/unfinished_string.out b/v_windows/v/old/vlib/v/checker/tests/unfinished_string.out new file mode 100644 index 0000000..c42f70a --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unfinished_string.out @@ -0,0 +1,2 @@ +vlib/v/checker/tests/unfinished_string.vv:2:1: error: unfinished string literal + 1 | a := ' diff --git a/v_windows/v/old/vlib/v/checker/tests/unfinished_string.vv b/v_windows/v/old/vlib/v/checker/tests/unfinished_string.vv new file mode 100644 index 0000000..e6374fb --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unfinished_string.vv @@ -0,0 +1 @@ +a := ' diff --git a/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_a.out b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_a.out new file mode 100644 index 0000000..225ccd0 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_a.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/unimplemented_interface_a.vv:10:6: error: `Cat` doesn't implement method `name` of interface `Animal` + 8 | + 9 | fn main() { + 10 | foo(Cat{}) + | ~~~~~ + 11 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_a.vv b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_a.vv new file mode 100644 index 0000000..fad1194 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_a.vv @@ -0,0 +1,11 @@ +interface Animal { + name() string +} + +struct Cat {} + +fn foo(a Animal) {} + +fn main() { + foo(Cat{}) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_b.out b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_b.out new file mode 100644 index 0000000..edb00d0 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_b.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/unimplemented_interface_b.vv:13:6: error: `Cat` incorrectly implements method `name` of interface `Animal`: expected return type `string` + 11 | fn main() { + 12 | c := Cat{} + 13 | foo(c) + | ^ + 14 | } +Details: main.Animal has `name() string` diff --git a/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_b.vv b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_b.vv new file mode 100644 index 0000000..0b07a78 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_b.vv @@ -0,0 +1,14 @@ +interface Animal { + name() string +} + +struct Cat {} + +fn (c Cat) name() {} + +fn foo(a Animal) {} + +fn main() { + c := Cat{} + foo(c) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_c.out b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_c.out new file mode 100644 index 0000000..43060d4 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_c.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/unimplemented_interface_c.vv:12:6: error: `Cat` incorrectly implements method `name` of interface `Animal`: expected 1 parameter(s), not 2 + 10 | + 11 | fn main() { + 12 | foo(Cat{}) + | ~~~~~ + 13 | } +Details: main.Animal has `name()` diff --git a/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_c.vv b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_c.vv new file mode 100644 index 0000000..46960da --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_c.vv @@ -0,0 +1,13 @@ +interface Animal { + name() +} + +struct Cat {} + +fn (c Cat) name(s string) {} + +fn foo(a Animal) {} + +fn main() { + foo(Cat{}) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_d.out b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_d.out new file mode 100644 index 0000000..5e39c5e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_d.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/unimplemented_interface_d.vv:12:6: error: `Cat` incorrectly implements method `speak` of interface `Animal`: expected 2 parameter(s), not 1 + 10 | + 11 | fn main() { + 12 | foo(Cat{}) + | ~~~~~ + 13 | } +Details: main.Animal has `speak(s string)` diff --git a/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_d.vv b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_d.vv new file mode 100644 index 0000000..51b3724 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_d.vv @@ -0,0 +1,13 @@ +interface Animal { + speak(s string) +} + +struct Cat {} + +fn (c Cat) speak() {} + +fn foo(a Animal) {} + +fn main() { + foo(Cat{}) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_e.out b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_e.out new file mode 100644 index 0000000..f388cb2 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_e.out @@ -0,0 +1,15 @@ +vlib/v/checker/tests/unimplemented_interface_e.vv:12:6: error: `Cat` incorrectly implements method `speak` of interface `Animal`: expected `string`, not `&string` for parameter 1 + 10 | + 11 | fn main() { + 12 | foo(Cat{}) + | ~~~~~ + 13 | _ = Animal(Cat{}) + 14 | } +Details: main.Animal has `speak(s string)` +vlib/v/checker/tests/unimplemented_interface_e.vv:13:6: error: `Cat` incorrectly implements method `speak` of interface `Animal`: expected `string`, not `&string` for parameter 1 + 11 | fn main() { + 12 | foo(Cat{}) + 13 | _ = Animal(Cat{}) + | ~~~~~~~~~~~~~ + 14 | } +Details: main.Animal has `speak(s string)` diff --git a/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_e.vv b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_e.vv new file mode 100644 index 0000000..85cee42 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_e.vv @@ -0,0 +1,14 @@ +interface Animal { + speak(s string) +} + +struct Cat {} + +fn (c Cat) speak(s &string) {} + +fn foo(a Animal) {} + +fn main() { + foo(Cat{}) + _ = Animal(Cat{}) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_f.out b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_f.out new file mode 100644 index 0000000..3bdc2ac --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_f.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/unimplemented_interface_f.vv:11:13: error: `Cat` incorrectly implements method `speak` of interface `Animal`: expected 2 parameter(s), not 1 + 9 | fn main() { + 10 | mut animals := []Animal{} + 11 | animals << Cat{} + | ~~~~~ + 12 | } +Details: main.Animal has `speak(s string)` diff --git a/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_f.vv b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_f.vv new file mode 100644 index 0000000..3fd767d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_f.vv @@ -0,0 +1,12 @@ +interface Animal { + speak(s string) +} + +struct Cat {} + +fn (c Cat) speak() {} + +fn main() { + mut animals := []Animal{} + animals << Cat{} +} diff --git a/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_g.out b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_g.out new file mode 100644 index 0000000..61c2cd3 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_g.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/unimplemented_interface_g.vv:12:13: error: `Cat` incorrectly implements method `speak` of interface `Animal`, expected `speak(s string)` + 10 | mut animals := []Animal{} + 11 | mut cats := []Cat{} + 12 | animals << cats + | ~~~~ + 13 | } + 14 | diff --git a/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_g.vv.disabled b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_g.vv.disabled new file mode 100644 index 0000000..fca4beb --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_g.vv.disabled @@ -0,0 +1,14 @@ +interface Animal { + speak(s string) +} + +struct Cat {} + +fn (c Cat) speak() {} + +fn main() { + mut animals := []Animal{} + mut cats := []Cat{} + animals << cats +} + diff --git a/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_h.out b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_h.out new file mode 100644 index 0000000..0731050 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_h.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/unimplemented_interface_h.vv:9:13: error: `Cat` doesn't implement field `name` of interface `Animal` + 7 | fn main() { + 8 | mut animals := []Animal{} + 9 | animals << Cat{} + | ~~~~~ + 10 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_h.vv b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_h.vv new file mode 100644 index 0000000..cd00881 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_h.vv @@ -0,0 +1,10 @@ +interface Animal { + name string +} + +struct Cat {} + +fn main() { + mut animals := []Animal{} + animals << Cat{} +} diff --git a/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_i.out b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_i.out new file mode 100644 index 0000000..073e248 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_i.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/unimplemented_interface_i.vv:11:13: error: `Cat` incorrectly implements field `name` of interface `Animal`, expected `string`, got `int` + 9 | fn main() { + 10 | mut animals := []Animal{} + 11 | animals << Cat{} + | ~~~~~ + 12 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_i.vv b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_i.vv new file mode 100644 index 0000000..4c50f0e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_i.vv @@ -0,0 +1,12 @@ +interface Animal { + name string +} + +struct Cat { + name int +} + +fn main() { + mut animals := []Animal{} + animals << Cat{} +} diff --git a/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_j.out b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_j.out new file mode 100644 index 0000000..4c85238 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_j.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/unimplemented_interface_j.vv:12:13: error: `Cat` incorrectly implements interface `Animal`, field `name` must be mutable + 10 | fn main() { + 11 | mut animals := []Animal{} + 12 | animals << Cat{} + | ~~~~~ + 13 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_j.vv b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_j.vv new file mode 100644 index 0000000..f54dab3 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unimplemented_interface_j.vv @@ -0,0 +1,13 @@ +interface Animal { +mut: + name string +} + +struct Cat { + name string +} + +fn main() { + mut animals := []Animal{} + animals << Cat{} +} diff --git a/v_windows/v/old/vlib/v/checker/tests/union_unsafe_fields.out b/v_windows/v/old/vlib/v/checker/tests/union_unsafe_fields.out new file mode 100644 index 0000000..be40d1b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/union_unsafe_fields.out @@ -0,0 +1,13 @@ +vlib/v/checker/tests/union_unsafe_fields.vv:10:9: error: reading a union field (or its address) requires `unsafe` + 8 | mut u := Uf32{u: 3} + 9 | u.f = 3.3 // ok + 10 | _ := u.u + | ^ + 11 | return &u.f + 12 | } +vlib/v/checker/tests/union_unsafe_fields.vv:11:12: error: reading a union field (or its address) requires `unsafe` + 9 | u.f = 3.3 // ok + 10 | _ := u.u + 11 | return &u.f + | ^ + 12 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/union_unsafe_fields.vv b/v_windows/v/old/vlib/v/checker/tests/union_unsafe_fields.vv new file mode 100644 index 0000000..6a1bc0f --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/union_unsafe_fields.vv @@ -0,0 +1,12 @@ +union Uf32 { +mut: + f f32 + u u32 +} + +fn f() f32 { + mut u := Uf32{u: 3} + u.f = 3.3 // ok + _ := u.u + return &u.f +} diff --git a/v_windows/v/old/vlib/v/checker/tests/unknown_array_element_type_b.out b/v_windows/v/old/vlib/v/checker/tests/unknown_array_element_type_b.out new file mode 100644 index 0000000..2efc456 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unknown_array_element_type_b.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/unknown_array_element_type_b.vv:2:6: error: unknown type `abc`. +Did you mean `Aaa`? + 1 | struct Aaa { + 2 | a []abc + | ~~~ + 3 | } + 4 | diff --git a/v_windows/v/old/vlib/v/checker/tests/unknown_array_element_type_b.vv b/v_windows/v/old/vlib/v/checker/tests/unknown_array_element_type_b.vv new file mode 100644 index 0000000..0fc059f --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unknown_array_element_type_b.vv @@ -0,0 +1,9 @@ +struct Aaa { + a []abc +} + +fn main() { + s := Aaa{} + println(s) +} + diff --git a/v_windows/v/old/vlib/v/checker/tests/unknown_as_type.out b/v_windows/v/old/vlib/v/checker/tests/unknown_as_type.out new file mode 100644 index 0000000..e520655 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unknown_as_type.out @@ -0,0 +1,8 @@ +vlib/v/checker/tests/unknown_as_type.vv:7:9: error: unknown type `Stringg`. +Did you mean `String`? + 5 | + 6 | fn foo(e Expr) { + 7 | x := e as Stringg + | ~~ + 8 | println(x) + 9 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/unknown_as_type.vv b/v_windows/v/old/vlib/v/checker/tests/unknown_as_type.vv new file mode 100644 index 0000000..66c2a57 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unknown_as_type.vv @@ -0,0 +1,13 @@ +type Expr = Int | String + +struct Int {} +struct String {} + +fn foo(e Expr) { + x := e as Stringg + println(x) +} + +fn main() { + +} diff --git a/v_windows/v/old/vlib/v/checker/tests/unknown_comptime_expr.out b/v_windows/v/old/vlib/v/checker/tests/unknown_comptime_expr.out new file mode 100644 index 0000000..b97e13d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unknown_comptime_expr.out @@ -0,0 +1,42 @@ +vlib/v/checker/tests/unknown_comptime_expr.vv:5:6: error: `foo` is mut and may have changed since its definition + 3 | fn main() { + 4 | mut foo := 0 + 5 | $if foo == 0 {} + | ~~~ + 6 | + 7 | bar := unknown_at_ct() +vlib/v/checker/tests/unknown_comptime_expr.vv:8:6: error: definition of `bar` is unknown at compile time + 6 | + 7 | bar := unknown_at_ct() + 8 | $if bar == 0 {} + | ~~~ + 9 | } + 10 | +vlib/v/checker/tests/unknown_comptime_expr.vv:13:6: error: undefined ident: `huh` + 11 | fn if_is() { + 12 | s := S1{} + 13 | $if huh.typ is T {} + | ~~~ + 14 | $if s is int {} + 15 | $if s.i is 5 {} +vlib/v/checker/tests/unknown_comptime_expr.vv:14:6: error: invalid `$if` condition: expected a type or a selector expression or an interface check + 12 | s := S1{} + 13 | $if huh.typ is T {} + 14 | $if s is int {} + | ^ + 15 | $if s.i is 5 {} + 16 | $if s.i is T {} +vlib/v/checker/tests/unknown_comptime_expr.vv:15:13: error: invalid `$if` condition: expected a type + 13 | $if huh.typ is T {} + 14 | $if s is int {} + 15 | $if s.i is 5 {} + | ^ + 16 | $if s.i is T {} + 17 | } +vlib/v/checker/tests/unknown_comptime_expr.vv:16:13: error: unknown type `T` + 14 | $if s is int {} + 15 | $if s.i is 5 {} + 16 | $if s.i is T {} + | ^ + 17 | } + 18 | diff --git a/v_windows/v/old/vlib/v/checker/tests/unknown_comptime_expr.vv b/v_windows/v/old/vlib/v/checker/tests/unknown_comptime_expr.vv new file mode 100644 index 0000000..c3734ae --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unknown_comptime_expr.vv @@ -0,0 +1,22 @@ +fn unknown_at_ct() int { return 0 } + +fn main() { + mut foo := 0 + $if foo == 0 {} + + bar := unknown_at_ct() + $if bar == 0 {} +} + +fn if_is() { + s := S1{} + $if huh.typ is T {} + $if s is int {} + $if s.i is 5 {} + $if s.i is T {} +} + +struct S1 { + i int +} + diff --git a/v_windows/v/old/vlib/v/checker/tests/unknown_field.out b/v_windows/v/old/vlib/v/checker/tests/unknown_field.out new file mode 100644 index 0000000..395eedf --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unknown_field.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/unknown_field.vv:7:12: error: type `Test` has no field named `sdd` + 5 | fn main() { + 6 | t := Test{} + 7 | println(t.sdd) + | ~~~ + 8 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/unknown_field.vv b/v_windows/v/old/vlib/v/checker/tests/unknown_field.vv new file mode 100644 index 0000000..8cf14a9 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unknown_field.vv @@ -0,0 +1,8 @@ +module main + +struct Test {} + +fn main() { + t := Test{} + println(t.sdd) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/unknown_generic_type.out b/v_windows/v/old/vlib/v/checker/tests/unknown_generic_type.out new file mode 100644 index 0000000..ea09b5e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unknown_generic_type.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/unknown_generic_type.vv:6:13: error: unknown type `Foo` + 4 | + 5 | fn main() { + 6 | x := decode('{"name": "test"}')? + | ~~~~~ + 7 | println(x) + 8 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/checker/tests/unknown_generic_type.vv b/v_windows/v/old/vlib/v/checker/tests/unknown_generic_type.vv new file mode 100644 index 0000000..1b88eb2 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unknown_generic_type.vv @@ -0,0 +1,8 @@ +fn decode(raw_data string) ?T { + return none +} + +fn main() { + x := decode('{"name": "test"}')? + println(x) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/unknown_method.out b/v_windows/v/old/vlib/v/checker/tests/unknown_method.out new file mode 100644 index 0000000..037d2f3 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unknown_method.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/unknown_method.vv:7:12: error: unknown method or field: `Test.sdd` + 5 | fn main() { + 6 | t := Test{} + 7 | println(t.sdd()) + | ~~~~~ + 8 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/unknown_method.vv b/v_windows/v/old/vlib/v/checker/tests/unknown_method.vv new file mode 100644 index 0000000..879b372 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unknown_method.vv @@ -0,0 +1,8 @@ +module main + +struct Test {} + +fn main() { + t := Test{} + println(t.sdd()) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/unknown_method_suggest_name.out b/v_windows/v/old/vlib/v/checker/tests/unknown_method_suggest_name.out new file mode 100644 index 0000000..f80de09 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unknown_method_suggest_name.out @@ -0,0 +1,22 @@ +vlib/v/checker/tests/unknown_method_suggest_name.vv:13:12: error: unknown type `hash.crc32.Crc33`. +Did you mean `crc32.Crc32`? + 11 | y int + 12 | z int + 13 | ccc crc32.Crc33 + | ~~~~~ + 14 | } + 15 | +vlib/v/checker/tests/unknown_method_suggest_name.vv:27:9: error: unknown method or field: `Point.tranzlate`. +Did you mean `translate`? + 25 | p := Point{1, 2, 3} + 26 | v := Vector{x: 5, y: 5, z: 10} + 27 | z := p.tranzlate(v) + | ~~~~~~~~~~~~ + 28 | println('p: $p') + 29 | println('v: $v') +vlib/v/checker/tests/unknown_method_suggest_name.vv:30:15: error: expression does not return a value + 28 | println('p: $p') + 29 | println('v: $v') + 30 | println('z: $z') + | ^ + 31 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/unknown_method_suggest_name.vv b/v_windows/v/old/vlib/v/checker/tests/unknown_method_suggest_name.vv new file mode 100644 index 0000000..52af44c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unknown_method_suggest_name.vv @@ -0,0 +1,31 @@ +import hash.crc32 + +struct Point { + x int + y int + z int +} + +struct Vector { + x int + y int + z int + ccc crc32.Crc33 +} + +fn (p Point) translate(v Vector) Point { + return Point{p.x + v.x, p.y + v.y, p.z + v.z} +} + +fn (p Point) identity() Point { + return Point{1, 1, 1} +} + +fn main() { + p := Point{1, 2, 3} + v := Vector{x: 5, y: 5, z: 10} + z := p.tranzlate(v) + println('p: $p') + println('v: $v') + println('z: $z') +} diff --git a/v_windows/v/old/vlib/v/checker/tests/unknown_sizeof_type_err_a.out b/v_windows/v/old/vlib/v/checker/tests/unknown_sizeof_type_err_a.out new file mode 100644 index 0000000..5c6e6cb --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unknown_sizeof_type_err_a.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/unknown_sizeof_type_err_a.vv:14:34: cgen error: unknown type `T` + 12 | println("size of Abc: ${sizeof(Abc)}") + 13 | println("size of Xyz: ${sizeof(Xyz)}") + 14 | println("size of Test: ${sizeof(T)}") + | ^ + 15 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/unknown_sizeof_type_err_a.vv b/v_windows/v/old/vlib/v/checker/tests/unknown_sizeof_type_err_a.vv new file mode 100644 index 0000000..dd15843 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unknown_sizeof_type_err_a.vv @@ -0,0 +1,15 @@ +struct Abc { + i int +} + +struct Xyz { + f f64 +} + +type Test = Abc | Xyz + +fn main() { + println("size of Abc: ${sizeof(Abc)}") + println("size of Xyz: ${sizeof(Xyz)}") + println("size of Test: ${sizeof(T)}") +} diff --git a/v_windows/v/old/vlib/v/checker/tests/unknown_sizeof_type_err_b.out b/v_windows/v/old/vlib/v/checker/tests/unknown_sizeof_type_err_b.out new file mode 100644 index 0000000..7daa009 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unknown_sizeof_type_err_b.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/unknown_sizeof_type_err_b.vv:14:34: cgen error: unknown type `Zabc` + 12 | println("size of Abc: ${sizeof(Abc)}") + 13 | println("size of Xyz: ${sizeof(Xyz)}") + 14 | println("size of Test: ${sizeof(Zabc)}") + | ~~~~ + 15 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/unknown_sizeof_type_err_b.vv b/v_windows/v/old/vlib/v/checker/tests/unknown_sizeof_type_err_b.vv new file mode 100644 index 0000000..154f406 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unknown_sizeof_type_err_b.vv @@ -0,0 +1,15 @@ +struct Abc { + i int +} + +struct Xyz { + f f64 +} + +type Test = Abc | Xyz + +fn main() { + println("size of Abc: ${sizeof(Abc)}") + println("size of Xyz: ${sizeof(Xyz)}") + println("size of Test: ${sizeof(Zabc)}") +} diff --git a/v_windows/v/old/vlib/v/checker/tests/unknown_struct_field_suggest_name.out b/v_windows/v/old/vlib/v/checker/tests/unknown_struct_field_suggest_name.out new file mode 100644 index 0000000..98347d6 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unknown_struct_field_suggest_name.out @@ -0,0 +1,15 @@ +vlib/v/checker/tests/unknown_struct_field_suggest_name.vv:11:16: error: type `Points` has no field named `xxxa`. +Did you mean `xxxx`? + 9 | p := Points{[1], [2], [3]} + 10 | println('p: $p') + 11 | for x in p.xxxa { + | ~~~~ + 12 | println('x: $x') + 13 | } +vlib/v/checker/tests/unknown_struct_field_suggest_name.vv:12:19: error: expression does not return a value + 10 | println('p: $p') + 11 | for x in p.xxxa { + 12 | println('x: $x') + | ^ + 13 | } + 14 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/unknown_struct_field_suggest_name.vv b/v_windows/v/old/vlib/v/checker/tests/unknown_struct_field_suggest_name.vv new file mode 100644 index 0000000..aebdcea --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unknown_struct_field_suggest_name.vv @@ -0,0 +1,14 @@ + +struct Points { + xxxx []int + yyyy []int + zzzz []int +} + +fn main() { + p := Points{[1], [2], [3]} + println('p: $p') + for x in p.xxxa { + println('x: $x') + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/unknown_struct_name.out b/v_windows/v/old/vlib/v/checker/tests/unknown_struct_name.out new file mode 100644 index 0000000..d21f02d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unknown_struct_name.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/unknown_struct_name.vv:4:7: error: unknown struct `F` + 2 | + 3 | fn main() { + 4 | _ := F{} + | ~~~ + 5 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/unknown_struct_name.vv b/v_windows/v/old/vlib/v/checker/tests/unknown_struct_name.vv new file mode 100644 index 0000000..52a4330 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unknown_struct_name.vv @@ -0,0 +1,5 @@ +module main + +fn main() { + _ := F{} +} diff --git a/v_windows/v/old/vlib/v/checker/tests/unknown_var_assign.out b/v_windows/v/old/vlib/v/checker/tests/unknown_var_assign.out new file mode 100644 index 0000000..6a12a8c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unknown_var_assign.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/unknown_var_assign.vv:2:5: error: undefined ident: `x` (use `:=` to declare a variable) + 1 | fn main() { + 2 | x = 'hello v' + | ^ + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/unknown_var_assign.vv b/v_windows/v/old/vlib/v/checker/tests/unknown_var_assign.vv new file mode 100644 index 0000000..d7ed530 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unknown_var_assign.vv @@ -0,0 +1,3 @@ +fn main() { + x = 'hello v' +} diff --git a/v_windows/v/old/vlib/v/checker/tests/unnecessary_parenthesis.out b/v_windows/v/old/vlib/v/checker/tests/unnecessary_parenthesis.out new file mode 100644 index 0000000..eee14f7 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unnecessary_parenthesis.out @@ -0,0 +1,20 @@ +vlib/v/checker/tests/unnecessary_parenthesis.vv:2:2: error: unnecessary `()` in `if` condition, use `if expr {` instead of `if (expr) {`. + 1 | fn main() { + 2 | if (1 == 1) { + | ~~~~~~~~~~~ + 3 | println('yeay') + 4 | } else if (1 == 2) { +vlib/v/checker/tests/unnecessary_parenthesis.vv:4:4: error: unnecessary `()` in `if` condition, use `if expr {` instead of `if (expr) {`. + 2 | if (1 == 1) { + 3 | println('yeay') + 4 | } else if (1 == 2) { + | ~~~~~~~~~~~~~~~~ + 5 | println("oh no :'(") + 6 | } else if (1 == 3) { +vlib/v/checker/tests/unnecessary_parenthesis.vv:6:4: error: unnecessary `()` in `if` condition, use `if expr {` instead of `if (expr) {`. + 4 | } else if (1 == 2) { + 5 | println("oh no :'(") + 6 | } else if (1 == 3) { + | ~~~~~~~~~~~~~~~~ + 7 | println("what's wrong with physics ????") + 8 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/unnecessary_parenthesis.vv b/v_windows/v/old/vlib/v/checker/tests/unnecessary_parenthesis.vv new file mode 100644 index 0000000..c1eb1ae --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unnecessary_parenthesis.vv @@ -0,0 +1,9 @@ +fn main() { + if (1 == 1) { + println('yeay') + } else if (1 == 2) { + println("oh no :'(") + } else if (1 == 3) { + println("what's wrong with physics ????") + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/unreachable_code.out b/v_windows/v/old/vlib/v/checker/tests/unreachable_code.out new file mode 100644 index 0000000..ae0dc92 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unreachable_code.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/unreachable_code.vv:3:4: error: unreachable code + 1 | fn foo() int { + 2 | return if 1 == 1 { 1 } else { 2 } + 3 | a := 1 + | ~~ + 4 | println(a) + 5 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/unreachable_code.vv b/v_windows/v/old/vlib/v/checker/tests/unreachable_code.vv new file mode 100644 index 0000000..7da480f --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unreachable_code.vv @@ -0,0 +1,8 @@ +fn foo() int { + return if 1 == 1 { 1 } else { 2 } + a := 1 + println(a) +} +fn main() { + foo() +} diff --git a/v_windows/v/old/vlib/v/checker/tests/unsafe_c_calls_should_be_checked.out b/v_windows/v/old/vlib/v/checker/tests/unsafe_c_calls_should_be_checked.out new file mode 100644 index 0000000..3d791c4 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unsafe_c_calls_should_be_checked.out @@ -0,0 +1,13 @@ +vlib/v/checker/tests/unsafe_c_calls_should_be_checked.vv:3:16: error: function `C.malloc` must be called from an `unsafe` block + 1 | + 2 | fn test_c() { + 3 | mut p := C.malloc(4) + | ~~~~~~~~~ + 4 | s := 'hope' + 5 | C.memcpy(p, s.str, 4) +vlib/v/checker/tests/unsafe_c_calls_should_be_checked.vv:5:7: error: function `C.memcpy` must be called from an `unsafe` block + 3 | mut p := C.malloc(4) + 4 | s := 'hope' + 5 | C.memcpy(p, s.str, 4) + | ~~~~~~~~~~~~~~~~~~~ + 6 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/unsafe_c_calls_should_be_checked.vv b/v_windows/v/old/vlib/v/checker/tests/unsafe_c_calls_should_be_checked.vv new file mode 100644 index 0000000..092fe2c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unsafe_c_calls_should_be_checked.vv @@ -0,0 +1,6 @@ + +fn test_c() { + mut p := C.malloc(4) + s := 'hope' + C.memcpy(p, s.str, 4) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/unsafe_fixed_array_assign.out b/v_windows/v/old/vlib/v/checker/tests/unsafe_fixed_array_assign.out new file mode 100644 index 0000000..fae8c95 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unsafe_fixed_array_assign.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/unsafe_fixed_array_assign.vv:8:7: error: assignment from one fixed array to another with a pointer element type is prohibited outside of `unsafe` + 6 | mut box := Box { num: 10 } + 7 | a := [&box]! + 8 | mut b := a + | ~~ + 9 | b[0].num = 0 + 10 | println(a) diff --git a/v_windows/v/old/vlib/v/checker/tests/unsafe_fixed_array_assign.vv b/v_windows/v/old/vlib/v/checker/tests/unsafe_fixed_array_assign.vv new file mode 100644 index 0000000..38defbd --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unsafe_fixed_array_assign.vv @@ -0,0 +1,10 @@ +struct Box { +mut: + num int +} + +mut box := Box { num: 10 } +a := [&box]! +mut b := a +b[0].num = 0 +println(a) diff --git a/v_windows/v/old/vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.out b/v_windows/v/old/vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.out new file mode 100644 index 0000000..9ac0225 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.out @@ -0,0 +1,28 @@ +vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.vv:4:6: error: pointer arithmetic is only allowed in `unsafe` blocks + 2 | mut v := 5 + 3 | mut p := &v + 4 | p++ + | ~~ + 5 | p += 2 + 6 | _ := v +vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.vv:5:7: error: pointer arithmetic is only allowed in `unsafe` blocks + 3 | mut p := &v + 4 | p++ + 5 | p += 2 + | ~~ + 6 | _ := v + 7 | } +vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.vv:11:14: error: pointer arithmetic is only allowed in `unsafe` blocks + 9 | fn test_ptr_infix() { + 10 | v := 4 + 11 | mut q := &v - 1 + | ~~~~~~ + 12 | q = q + 3 + 13 | _ := q +vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.vv:12:9: error: pointer arithmetic is only allowed in `unsafe` blocks + 10 | v := 4 + 11 | mut q := &v - 1 + 12 | q = q + 3 + | ~~~~~ + 13 | _ := q + 14 | _ := v diff --git a/v_windows/v/old/vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.vv b/v_windows/v/old/vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.vv new file mode 100644 index 0000000..b83630b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unsafe_pointer_arithmetic_should_be_checked.vv @@ -0,0 +1,15 @@ +fn test_ptr_assign() { + mut v := 5 + mut p := &v + p++ + p += 2 + _ := v +} + +fn test_ptr_infix() { + v := 4 + mut q := &v - 1 + q = q + 3 + _ := q + _ := v +} diff --git a/v_windows/v/old/vlib/v/checker/tests/unsafe_required.out b/v_windows/v/old/vlib/v/checker/tests/unsafe_required.out new file mode 100644 index 0000000..463a6a2 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unsafe_required.out @@ -0,0 +1,20 @@ +vlib/v/checker/tests/unsafe_required.vv:8:7: error: method `S1.f` must be called from an `unsafe` block + 6 | fn test_funcs() { + 7 | s := S1{} + 8 | s.f() + | ~~~ + 9 | } + 10 | +vlib/v/checker/tests/unsafe_required.vv:16:7: error: pointer indexing is only allowed in `unsafe` blocks + 14 | _ = b[0] // OK + 15 | c := &b + 16 | _ = c[0] + | ~~~ + 17 | + 18 | v := 4 +vlib/v/checker/tests/unsafe_required.vv:20:10: error: pointer indexing is only allowed in `unsafe` blocks + 18 | v := 4 + 19 | p := &v + 20 | _ = p[0] + | ~~~ + 21 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/unsafe_required.vv b/v_windows/v/old/vlib/v/checker/tests/unsafe_required.vv new file mode 100644 index 0000000..e47a90e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unsafe_required.vv @@ -0,0 +1,21 @@ +struct S1 {} + +[unsafe] +fn (s S1) f(){} + +fn test_funcs() { + s := S1{} + s.f() +} + +fn test_ptr_index(mut a []string) { + _ = a[0] // OK + b := ['jo'] + _ = b[0] // OK + c := &b + _ = c[0] + + v := 4 + p := &v + _ = p[0] +} diff --git a/v_windows/v/old/vlib/v/checker/tests/unwrapped_optional_infix.out b/v_windows/v/old/vlib/v/checker/tests/unwrapped_optional_infix.out new file mode 100644 index 0000000..146e7e6 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unwrapped_optional_infix.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/unwrapped_optional_infix.vv:5:9: error: unwrapped optional cannot be used in an infix expression + 3 | } + 4 | + 5 | println(test() == "") + | ~~~~~~~~~~~~ + diff --git a/v_windows/v/old/vlib/v/checker/tests/unwrapped_optional_infix.vv b/v_windows/v/old/vlib/v/checker/tests/unwrapped_optional_infix.vv new file mode 100644 index 0000000..432b0cc --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/unwrapped_optional_infix.vv @@ -0,0 +1,5 @@ +fn test() ?string { + return "" +} + +println(test() == "") diff --git a/v_windows/v/old/vlib/v/checker/tests/use_deprecated_function_warning.out b/v_windows/v/old/vlib/v/checker/tests/use_deprecated_function_warning.out new file mode 100644 index 0000000..9cffcba --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/use_deprecated_function_warning.out @@ -0,0 +1,20 @@ +vlib/v/checker/tests/use_deprecated_function_warning.vv:12:2: error: function `xyz` has been deprecated + 10 | + 11 | fn main() { + 12 | xyz() + | ~~~~~ + 13 | abc() + 14 | } +vlib/v/checker/tests/use_deprecated_function_warning.vv:13:2: error: function `abc` has been deprecated; use foo2 instead + 11 | fn main() { + 12 | xyz() + 13 | abc() + | ~~~~~ + 14 | } + 15 | +vlib/v/checker/tests/use_deprecated_function_warning.vv:23:4: error: method `S1.m` has been deprecated; use bar instead + 21 | fn method() { + 22 | s := S1{} + 23 | s.m() + | ~~~ + 24 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/use_deprecated_function_warning.vv b/v_windows/v/old/vlib/v/checker/tests/use_deprecated_function_warning.vv new file mode 100644 index 0000000..d5f95ef --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/use_deprecated_function_warning.vv @@ -0,0 +1,24 @@ +[deprecated] +fn xyz() { + println('hi') +} + +[deprecated: 'use foo2 instead'] +fn abc() { + println('hi') +} + +fn main() { + xyz() + abc() +} + +struct S1 {} + +[deprecated: 'use bar instead'] +fn (s S1) m() {} + +fn method() { + s := S1{} + s.m() +} diff --git a/v_windows/v/old/vlib/v/checker/tests/var_duplicate_const.out b/v_windows/v/old/vlib/v/checker/tests/var_duplicate_const.out new file mode 100644 index 0000000..b7b4633 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/var_duplicate_const.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/var_duplicate_const.vv:4:5: error: duplicate of a const name `size` + 2 | + 3 | fn main() { + 4 | size := 11 + | ~~~~ + 5 | println(main.size) + 6 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/var_duplicate_const.vv b/v_windows/v/old/vlib/v/checker/tests/var_duplicate_const.vv new file mode 100644 index 0000000..4275e7b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/var_duplicate_const.vv @@ -0,0 +1,6 @@ +const size = 22 + +fn main() { + size := 11 + println(main.size) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/var_eval_not_used.out b/v_windows/v/old/vlib/v/checker/tests/var_eval_not_used.out new file mode 100644 index 0000000..2cb3593 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/var_eval_not_used.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/var_eval_not_used.vv:6:2: error: `c` evaluated but not used + 4 | + 5 | fn main() { + 6 | c + | ^ + 7 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/var_eval_not_used.vv b/v_windows/v/old/vlib/v/checker/tests/var_eval_not_used.vv new file mode 100644 index 0000000..57642a1 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/var_eval_not_used.vv @@ -0,0 +1,7 @@ +const ( + c = 1 +) + +fn main() { + c +} diff --git a/v_windows/v/old/vlib/v/checker/tests/var_eval_not_used_scope.out b/v_windows/v/old/vlib/v/checker/tests/var_eval_not_used_scope.out new file mode 100644 index 0000000..89c4afd --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/var_eval_not_used_scope.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/var_eval_not_used_scope.vv:7:3: error: `c` evaluated but not used + 5 | fn main() { + 6 | { + 7 | c + | ^ + 8 | } + 9 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/var_eval_not_used_scope.vv b/v_windows/v/old/vlib/v/checker/tests/var_eval_not_used_scope.vv new file mode 100644 index 0000000..298e35b --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/var_eval_not_used_scope.vv @@ -0,0 +1,9 @@ +const ( + c = 1 +) + +fn main() { + { + c + } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/var_used_before_declaration.out b/v_windows/v/old/vlib/v/checker/tests/var_used_before_declaration.out new file mode 100644 index 0000000..b3ea361 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/var_used_before_declaration.out @@ -0,0 +1,6 @@ +vlib/v/checker/tests/var_used_before_declaration.vv:2:13: error: undefined variable `x` (used before declaration) + 1 | fn main() { + 2 | println(x) + | ^ + 3 | x := 'hello v' + 4 | _ = x diff --git a/v_windows/v/old/vlib/v/checker/tests/var_used_before_declaration.vv b/v_windows/v/old/vlib/v/checker/tests/var_used_before_declaration.vv new file mode 100644 index 0000000..1a2b63d --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/var_used_before_declaration.vv @@ -0,0 +1,5 @@ +fn main() { + println(x) + x := 'hello v' + _ = x +} diff --git a/v_windows/v/old/vlib/v/checker/tests/void_fn_as_value.out b/v_windows/v/old/vlib/v/checker/tests/void_fn_as_value.out new file mode 100644 index 0000000..9898c19 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/void_fn_as_value.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/void_fn_as_value.vv:5:7: error: unknown function: x + 3 | fn main() { + 4 | mut a := 'aa' + 5 | a += x('a','b') + | ~~~~~~~~~~ + 6 | mut b := 'abcdef' + 7 | _ = b diff --git a/v_windows/v/old/vlib/v/checker/tests/void_fn_as_value.vv b/v_windows/v/old/vlib/v/checker/tests/void_fn_as_value.vv new file mode 100644 index 0000000..012019e --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/void_fn_as_value.vv @@ -0,0 +1,9 @@ +module main + +fn main() { + mut a := 'aa' + a += x('a','b') + mut b := 'abcdef' + _ = b + _ = a +} diff --git a/v_windows/v/old/vlib/v/checker/tests/void_function_assign_to_string.out b/v_windows/v/old/vlib/v/checker/tests/void_function_assign_to_string.out new file mode 100644 index 0000000..b04d39c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/void_function_assign_to_string.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/void_function_assign_to_string.vv:6:4: error: assignment mismatch: 1 variable(s) but `x()` returns 0 value(s) + 4 | fn main(){ + 5 | mut a := '' + 6 | a = x(1,2) // hello + | ^ + 7 | eprintln('a: $a') + 8 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/void_function_assign_to_string.vv b/v_windows/v/old/vlib/v/checker/tests/void_function_assign_to_string.vv new file mode 100644 index 0000000..fddd5fc --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/void_function_assign_to_string.vv @@ -0,0 +1,8 @@ +fn x(x int,y int) { + +} +fn main(){ + mut a := '' + a = x(1,2) // hello + eprintln('a: $a') +} diff --git a/v_windows/v/old/vlib/v/checker/tests/void_optional_err.out b/v_windows/v/old/vlib/v/checker/tests/void_optional_err.out new file mode 100644 index 0000000..c9efcb7 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/void_optional_err.out @@ -0,0 +1,5 @@ +vlib/v/checker/tests/void_optional_err.vv:1:16: error: use `?` instead of `?void` + 1 | fn ret_void() ?void { + | ~~~~ + 2 | return error('error') + 3 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/void_optional_err.vv b/v_windows/v/old/vlib/v/checker/tests/void_optional_err.vv new file mode 100644 index 0000000..136e6e5 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/void_optional_err.vv @@ -0,0 +1,7 @@ +fn ret_void() ?void { + return error('error') +} + +fn main() { + _ = ret_void() or { panic('$err') } +} diff --git a/v_windows/v/old/vlib/v/checker/tests/vweb_routing_checks.out b/v_windows/v/old/vlib/v/checker/tests/vweb_routing_checks.out new file mode 100644 index 0000000..141d7b5 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/vweb_routing_checks.out @@ -0,0 +1,14 @@ +vlib/v/checker/tests/vweb_routing_checks.vv:20:1: error: mismatched parameters count between vweb method `App.bar` (1) and route attribute ['/bar'] (0) + 18 | // segfault because path taks 0 vars and fcn takes 1 arg + 19 | ['/bar'] + 20 | pub fn (mut app App) bar(a string) vweb.Result { + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 21 | return app.html('works') + 22 | } +vlib/v/checker/tests/vweb_routing_checks.vv:26:1: error: mismatched parameters count between vweb method `App.cow` (0) and route attribute ['/cow/:low'] (1) + 24 | // no segfault, but it shouldnt compile + 25 | ['/cow/:low'] + 26 | pub fn (mut app App) cow() vweb.Result { + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 27 | return app.html('works') + 28 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/vweb_routing_checks.vv b/v_windows/v/old/vlib/v/checker/tests/vweb_routing_checks.vv new file mode 100644 index 0000000..53f1d09 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/vweb_routing_checks.vv @@ -0,0 +1,47 @@ +import vweb + +struct App { + vweb.Context +} + +pub fn (mut app App) no_attributes(a string) vweb.Result { + return app.text('ok') +} + +// works fine, as long as fcn gets 1 arg and route takes 1 var +['/foo/:bar'] +pub fn (mut app App) foo(a string) vweb.Result { + eprintln('foo') + return app.html('works') +} + +// segfault because path taks 0 vars and fcn takes 1 arg +['/bar'] +pub fn (mut app App) bar(a string) vweb.Result { + return app.html('works') +} + +// no segfault, but it shouldnt compile +['/cow/:low'] +pub fn (mut app App) cow() vweb.Result { + return app.html('works') +} + +/* +pub fn (app App) init_server() { + // +} + +pub fn (app App) before_request() { + // +} +*/ + +pub fn (mut app App) index() { + app.html('hello') +} + +fn main() { + port := 8181 + vweb.run(&App{}, port) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/vweb_tmpl_used_var.out b/v_windows/v/old/vlib/v/checker/tests/vweb_tmpl_used_var.out new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/vweb_tmpl_used_var.out @@ -0,0 +1 @@ + diff --git a/v_windows/v/old/vlib/v/checker/tests/vweb_tmpl_used_var.vv b/v_windows/v/old/vlib/v/checker/tests/vweb_tmpl_used_var.vv new file mode 100644 index 0000000..13cdbec --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/vweb_tmpl_used_var.vv @@ -0,0 +1,14 @@ +import vweb + +struct App { + vweb.Context +} + +pub fn (mut app App) index() vweb.Result { + test := 'test' + return $vweb.html() +} + +fn main() { + vweb.run(&App{}, 8181) +} diff --git a/v_windows/v/old/vlib/v/checker/tests/warnings_for_string_c2v_calls.out b/v_windows/v/old/vlib/v/checker/tests/warnings_for_string_c2v_calls.out new file mode 100644 index 0000000..f570847 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/warnings_for_string_c2v_calls.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/warnings_for_string_c2v_calls.vv:8:7: error: to convert a C string buffer pointer to a V string, use x.vstring() instead of string(x) + 6 | p[2] = `z` + 7 | } + 8 | x := string(p) + | ~~~~~~~~~ + 9 | eprintln('x: $x') + 10 | eprintln('x.len: $x.len') diff --git a/v_windows/v/old/vlib/v/checker/tests/warnings_for_string_c2v_calls.vv b/v_windows/v/old/vlib/v/checker/tests/warnings_for_string_c2v_calls.vv new file mode 100644 index 0000000..7f4dade --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/warnings_for_string_c2v_calls.vv @@ -0,0 +1,11 @@ +fn main() { + mut p := vcalloc(20) + unsafe { + p[0] = `A` + p[1] = `B` + p[2] = `z` + } + x := string(p) + eprintln('x: $x') + eprintln('x.len: $x.len') +} diff --git a/v_windows/v/old/vlib/v/checker/tests/wrong_propagate_ret_type.out b/v_windows/v/old/vlib/v/checker/tests/wrong_propagate_ret_type.out new file mode 100644 index 0000000..af8d737 --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/wrong_propagate_ret_type.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/wrong_propagate_ret_type.vv:6:17: error: to propagate the optional call, `opt_call` must return an optional + 4 | + 5 | fn opt_call() int { + 6 | a := ret_none()? + | ^ + 7 | return a + 8 | } diff --git a/v_windows/v/old/vlib/v/checker/tests/wrong_propagate_ret_type.vv b/v_windows/v/old/vlib/v/checker/tests/wrong_propagate_ret_type.vv new file mode 100644 index 0000000..82ef06c --- /dev/null +++ b/v_windows/v/old/vlib/v/checker/tests/wrong_propagate_ret_type.vv @@ -0,0 +1,8 @@ +fn ret_none() ?int { + return none +} + +fn opt_call() int { + a := ret_none()? + return a +} diff --git a/v_windows/v/old/vlib/v/compiler_errors_test.v b/v_windows/v/old/vlib/v/compiler_errors_test.v new file mode 100644 index 0000000..0bb0eff --- /dev/null +++ b/v_windows/v/old/vlib/v/compiler_errors_test.v @@ -0,0 +1,337 @@ +import os +import rand +import term +import v.util.diff +import v.util.vtest +import time +import sync +import runtime +import benchmark + +const skip_files = [ + 'non_existing.vv' /* minimize commit diff churn, do not remove */, +] + +const skip_on_ubuntu_musl = [ + 'vlib/v/checker/tests/vweb_tmpl_used_var.vv', +] + +const turn_off_vcolors = os.setenv('VCOLORS', 'never', true) + +const should_autofix = os.getenv('VAUTOFIX') != '' + +const github_job = os.getenv('GITHUB_JOB') + +struct TaskDescription { + vexe string + dir string + voptions string + result_extension string + path string +mut: + is_error bool + is_skipped bool + is_module bool + expected string + expected_out_path string + found___ string + took time.Duration + cli_cmd string +} + +struct Tasks { + vexe string + parallel_jobs int // 0 is using VJOBS, anything else is an override + label string +mut: + show_cmd bool + all []TaskDescription +} + +fn test_all() { + vexe := os.getenv('VEXE') + vroot := os.dir(vexe) + os.chdir(vroot) + checker_dir := 'vlib/v/checker/tests' + parser_dir := 'vlib/v/parser/tests' + scanner_dir := 'vlib/v/scanner/tests' + module_dir := '$checker_dir/modules' + global_dir := '$checker_dir/globals' + global_run_dir := '$checker_dir/globals_run' + run_dir := '$checker_dir/run' + skip_unused_dir := 'vlib/v/tests/skip_unused' + // + checker_tests := get_tests_in_dir(checker_dir, false) + parser_tests := get_tests_in_dir(parser_dir, false) + scanner_tests := get_tests_in_dir(scanner_dir, false) + global_tests := get_tests_in_dir(global_dir, false) + global_run_tests := get_tests_in_dir(global_run_dir, false) + module_tests := get_tests_in_dir(module_dir, true) + run_tests := get_tests_in_dir(run_dir, false) + skip_unused_dir_tests := get_tests_in_dir(skip_unused_dir, false) + // -prod is used for the parser and checker tests, so that warns are errors + mut tasks := Tasks{ + vexe: vexe + label: 'all tests' + } + tasks.add('', parser_dir, '-prod', '.out', parser_tests, false) + tasks.add('', checker_dir, '-prod', '.out', checker_tests, false) + tasks.add('', scanner_dir, '-prod', '.out', scanner_tests, false) + tasks.add('', checker_dir, '-enable-globals run', '.run.out', ['globals_error.vv'], + false) + tasks.add('', global_run_dir, '-enable-globals run', '.run.out', global_run_tests, + false) + tasks.add('', global_dir, '-enable-globals', '.out', global_tests, false) + tasks.add('', module_dir, '-prod run', '.out', module_tests, true) + tasks.add('', run_dir, 'run', '.run.out', run_tests, false) + tasks.run() + // + if os.user_os() == 'linux' { + mut skip_unused_tasks := Tasks{ + vexe: vexe + parallel_jobs: 1 + label: '-skip-unused tests' + } + skip_unused_tasks.add('', skip_unused_dir, 'run', '.run.out', skip_unused_dir_tests, + false) + skip_unused_tasks.add('', skip_unused_dir, '-d no_backtrace -skip-unused run', + '.skip_unused.run.out', skip_unused_dir_tests, false) + skip_unused_tasks.run() + } + // + if github_job == 'ubuntu-tcc' { + // This is done with tcc only, because the error output is compiler specific. + // NB: the tasks should be run serially, since they depend on + // setting and using environment variables. + mut cte_tasks := Tasks{ + vexe: vexe + parallel_jobs: 1 + label: 'comptime env tests' + } + cte_dir := '$checker_dir/comptime_env' + files := get_tests_in_dir(cte_dir, false) + cte_tasks.add('', cte_dir, '-no-retry-compilation run', '.run.out', files, false) + cte_tasks.add('VAR=/usr/include $vexe', cte_dir, '-no-retry-compilation run', + '.var.run.out', ['using_comptime_env.vv'], false) + cte_tasks.add('VAR=/opt/invalid/path $vexe', cte_dir, '-no-retry-compilation run', + '.var_invalid.run.out', ['using_comptime_env.vv'], false) + cte_tasks.run() + } + mut ct_tasks := Tasks{ + vexe: vexe + parallel_jobs: 1 + label: 'comptime define tests' + } + ct_tasks.add_checked_run('-d mysymbol run', '.mysymbol.run.out', [ + 'custom_comptime_define_error.vv', + ]) + ct_tasks.add_checked_run('-d mydebug run', '.mydebug.run.out', [ + 'custom_comptime_define_if_flag.vv', + ]) + ct_tasks.add_checked_run('-d nodebug run', '.nodebug.run.out', [ + 'custom_comptime_define_if_flag.vv', + ]) + ct_tasks.add_checked_run('run', '.run.out', ['custom_comptime_define_if_debug.vv']) + ct_tasks.add_checked_run('-g run', '.g.run.out', ['custom_comptime_define_if_debug.vv']) + ct_tasks.add_checked_run('-cg run', '.cg.run.out', ['custom_comptime_define_if_debug.vv']) + ct_tasks.add_checked_run('-d debug run', '.debug.run.out', ['custom_comptime_define_if_debug.vv']) + ct_tasks.add_checked_run('-d debug -d bar run', '.debug.bar.run.out', [ + 'custom_comptime_define_if_debug.vv', + ]) + ct_tasks.run() +} + +fn (mut tasks Tasks) add_checked_run(voptions string, result_extension string, tests []string) { + checker_dir := 'vlib/v/checker/tests' + tasks.add('', checker_dir, voptions, result_extension, tests, false) +} + +fn (mut tasks Tasks) add(custom_vexe string, dir string, voptions string, result_extension string, tests []string, is_module bool) { + mut vexe := tasks.vexe + if custom_vexe != '' { + vexe = custom_vexe + } + paths := vtest.filter_vtest_only(tests, basepath: dir) + for path in paths { + tasks.all << TaskDescription{ + vexe: vexe + dir: dir + voptions: voptions + result_extension: result_extension + path: path + is_module: is_module + } + } +} + +fn bstep_message(mut bench benchmark.Benchmark, label string, msg string, sduration time.Duration) string { + return bench.step_message_with_label_and_duration(label, msg, sduration) +} + +// process an array of tasks in parallel, using no more than vjobs worker threads +fn (mut tasks Tasks) run() { + tasks.show_cmd = os.getenv('VTEST_SHOW_CMD') != '' + vjobs := if tasks.parallel_jobs > 0 { tasks.parallel_jobs } else { runtime.nr_jobs() } + mut bench := benchmark.new_benchmark() + bench.set_total_expected_steps(tasks.all.len) + mut work := sync.new_channel(u32(tasks.all.len)) + mut results := sync.new_channel(u32(tasks.all.len)) + mut m_skip_files := skip_files.clone() + if os.getenv('V_CI_UBUNTU_MUSL').len > 0 { + m_skip_files << skip_on_ubuntu_musl + } + $if noskip ? { + m_skip_files = [] + } + $if tinyc { + // NB: tcc does not support __has_include, so the detection mechanism + // used for the other compilers does not work. It still provides a + // cleaner error message, than a generic C error, but without the explanation. + m_skip_files << 'vlib/v/checker/tests/missing_c_lib_header_1.vv' + m_skip_files << 'vlib/v/checker/tests/missing_c_lib_header_with_explanation_2.vv' + } + $if msvc { + m_skip_files << 'vlib/v/checker/tests/asm_alias_does_not_exist.vv' + m_skip_files << 'vlib/v/checker/tests/asm_immutable_err.vv' + // TODO: investigate why MSVC regressed + m_skip_files << 'vlib/v/checker/tests/missing_c_lib_header_1.vv' + m_skip_files << 'vlib/v/checker/tests/missing_c_lib_header_with_explanation_2.vv' + } + for i in 0 .. tasks.all.len { + if tasks.all[i].path in m_skip_files { + tasks.all[i].is_skipped = true + } + unsafe { work.push(&tasks.all[i]) } + } + work.close() + for _ in 0 .. vjobs { + go work_processor(mut work, mut results) + } + if github_job == '' { + println('') + } + mut line_can_be_erased := true + mut total_errors := 0 + for _ in 0 .. tasks.all.len { + mut task := TaskDescription{} + results.pop(&task) + bench.step() + if task.is_skipped { + bench.skip() + eprintln(bstep_message(mut bench, benchmark.b_skip, task.path, task.took)) + line_can_be_erased = false + continue + } + if task.is_error { + total_errors++ + bench.fail() + eprintln(bstep_message(mut bench, benchmark.b_fail, task.path, task.took)) + println('============') + println('failed cmd: $task.cli_cmd') + println('expected_out_path: $task.expected_out_path') + println('============') + println('expected:') + println(task.expected) + println('============') + println('found:') + println(task.found___) + println('============\n') + diff_content(task.expected, task.found___) + line_can_be_erased = false + } else { + bench.ok() + if tasks.show_cmd { + eprintln(bstep_message(mut bench, benchmark.b_ok, '$task.cli_cmd $task.path', + task.took)) + } else { + if github_job == '' { + // local mode: + if line_can_be_erased { + term.clear_previous_line() + } + println(bstep_message(mut bench, benchmark.b_ok, task.path, task.took)) + } + } + line_can_be_erased = true + } + } + bench.stop() + eprintln(term.h_divider('-')) + eprintln(bench.total_message(tasks.label)) + if total_errors != 0 { + exit(1) + } +} + +// a single worker thread spends its time getting work from the `work` channel, +// processing the task, and then putting the task in the `results` channel +fn work_processor(mut work sync.Channel, mut results sync.Channel) { + for { + mut task := TaskDescription{} + if !work.pop(&task) { + break + } + sw := time.new_stopwatch() + task.execute() + task.took = sw.elapsed() + results.push(&task) + } +} + +// actual processing; NB: no output is done here at all +fn (mut task TaskDescription) execute() { + if task.is_skipped { + return + } + program := task.path + cli_cmd := '$task.vexe $task.voptions $program' + res := os.execute(cli_cmd) + expected_out_path := program.replace('.vv', '') + task.result_extension + task.expected_out_path = expected_out_path + task.cli_cmd = cli_cmd + if should_autofix && !os.exists(expected_out_path) { + os.write_file(expected_out_path, '') or { panic(err) } + } + mut expected := os.read_file(expected_out_path) or { panic(err) } + task.expected = clean_line_endings(expected) + task.found___ = clean_line_endings(res.output) + $if windows { + if task.is_module { + task.found___ = task.found___.replace_once('\\', '/') + } + } + if task.expected != task.found___ { + task.is_error = true + if should_autofix { + os.write_file(expected_out_path, res.output) or { panic(err) } + } + } +} + +fn clean_line_endings(s string) string { + mut res := s.trim_space() + res = res.replace(' \n', '\n') + res = res.replace(' \r\n', '\n') + res = res.replace('\r\n', '\n') + res = res.trim('\n') + return res +} + +fn diff_content(s1 string, s2 string) { + diff_cmd := diff.find_working_diff_command() or { return } + println(term.bold(term.yellow('diff: '))) + println(diff.color_compare_strings(diff_cmd, rand.ulid(), s1, s2)) + println('============\n') +} + +fn get_tests_in_dir(dir string, is_module bool) []string { + files := os.ls(dir) or { panic(err) } + mut tests := files.clone() + if !is_module { + tests = files.filter(it.ends_with('.vv')) + } else { + tests = files.filter(!it.ends_with('.out')) + } + tests.sort() + return tests +} diff --git a/v_windows/v/old/vlib/v/depgraph/depgraph.v b/v_windows/v/old/vlib/v/depgraph/depgraph.v new file mode 100644 index 0000000..cc6a8eb --- /dev/null +++ b/v_windows/v/old/vlib/v/depgraph/depgraph.v @@ -0,0 +1,218 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +// Directed acyclic graph +// this implementation is specifically suited to ordering dependencies +module depgraph + +import v.dotgraph + +struct DepGraphNode { +pub mut: + name string + deps []string +} + +struct DepGraph { +pub mut: + acyclic bool + nodes []DepGraphNode +} + +struct OrderedDepMap { +mut: + keys []string + data map[string][]string +} + +pub fn (mut o OrderedDepMap) set(name string, deps []string) { + if name !in o.data { + o.keys << name + } + o.data[name] = deps +} + +pub fn (mut o OrderedDepMap) add(name string, deps []string) { + mut d := o.get(name) + for dep in deps { + if dep !in d { + d << dep + } else { + } + } + o.set(name, d) +} + +pub fn (o &OrderedDepMap) get(name string) []string { + res := o.data[name] or { []string{} } + return res +} + +pub fn (mut o OrderedDepMap) delete(name string) { + if name !in o.data { + panic('delete: no such key: $name') + } + for i, _ in o.keys { + if o.keys[i] == name { + o.keys.delete(i) + break + } + } + o.data.delete(name) +} + +pub fn (mut o OrderedDepMap) apply_diff(name string, deps []string) { + mut diff := []string{} + deps_of_name := o.get(name) + for dep in deps_of_name { + if dep !in deps { + diff << dep + } + } + o.set(name, diff) +} + +pub fn (o &OrderedDepMap) size() int { + return o.data.len +} + +pub fn new_dep_graph() &DepGraph { + return &DepGraph{ + acyclic: true + nodes: []DepGraphNode{cap: 1024} + } +} + +pub fn (mut graph DepGraph) add(mod string, deps []string) { + new_node := DepGraphNode{ + name: mod + deps: deps.clone() + } + graph.nodes << new_node +} + +pub fn (graph &DepGraph) resolve() &DepGraph { + mut node_names := OrderedDepMap{} + mut node_deps := OrderedDepMap{} + for node in graph.nodes { + node_names.add(node.name, node.deps) + node_deps.add(node.name, node.deps) + } + mut iterations := 0 + mut resolved := new_dep_graph() + for node_deps.size() != 0 { + iterations++ + mut ready_set := []string{} + for name in node_deps.keys { + deps := node_deps.get(name) + if deps.len == 0 { + ready_set << name + } + } + if ready_set.len == 0 { + mut g := new_dep_graph() + g.acyclic = false + for name in node_deps.keys { + g.add(name, node_names.get(name)) + } + return g + } + for name in ready_set { + node_deps.delete(name) + resolved_deps := node_names.get(name) + resolved.add(name, resolved_deps) + } + for name in node_deps.keys { + node_deps.apply_diff(name, ready_set) + } + } + return resolved +} + +pub fn (graph &DepGraph) last_node() DepGraphNode { + return graph.nodes[graph.nodes.len - 1] +} + +pub fn (graph &DepGraph) display() string { + mut out := []string{} + for node in graph.nodes { + for dep in node.deps { + out << ' * $node.name -> $dep' + } + } + return out.join('\n') +} + +struct NodeNames { +mut: + is_cycle map[string]bool + names map[string][]string +} + +pub fn (graph &DepGraph) display_cycles() string { + mut seen := false + mut out := []string{} + mut nn := NodeNames{} + for node in graph.nodes { + nn.names[node.name] = node.deps + } + for k, _ in nn.names { + mut cycle_names := []string{} + if k in nn.is_cycle { + continue + } + seen, cycle_names = nn.is_part_of_cycle(k, cycle_names) + if seen { + out << ' * ' + cycle_names.join(' -> ') + nn.is_cycle = map[string]bool{} + } + } + return out.join('\n') +} + +fn (mut nn NodeNames) is_part_of_cycle(name string, already_seen []string) (bool, []string) { + mut seen := false + mut new_already_seen := already_seen.clone() + if name in nn.is_cycle { + return nn.is_cycle[name], new_already_seen + } + if name in already_seen { + new_already_seen << name + nn.is_cycle[name] = true + return true, new_already_seen + } + new_already_seen << name + deps := nn.names[name] + if deps.len == 0 { + nn.is_cycle[name] = false + return false, new_already_seen + } + for d in deps { + mut d_already_seen := new_already_seen.clone() + seen, d_already_seen = nn.is_part_of_cycle(d, d_already_seen) + if seen { + new_already_seen = d_already_seen.clone() + nn.is_cycle[name] = true + return true, new_already_seen + } + } + nn.is_cycle[name] = false + return false, new_already_seen +} + +pub fn show(graph &DepGraph, path string) { + mut dg := dotgraph.new('ModGraph', 'ModGraph for $path', 'blue') + mbuiltin := 'builtin' + for node in graph.nodes { + is_main := node.name == 'main' + dg.new_node(node.name, should_highlight: is_main) + mut deps := node.deps.clone() + if node.name != mbuiltin && mbuiltin !in deps { + deps << mbuiltin + } + for dep in deps { + dg.new_edge(node.name, dep, should_highlight: is_main) + } + } + dg.finish() +} diff --git a/v_windows/v/old/vlib/v/doc/comment.v b/v_windows/v/old/vlib/v/doc/comment.v new file mode 100644 index 0000000..468f46e --- /dev/null +++ b/v_windows/v/old/vlib/v/doc/comment.v @@ -0,0 +1,25 @@ +module doc + +import v.token + +const ( + example_pattern = '\x01 Example: ' +) + +pub struct DocComment { +pub mut: + text string // Raw text content of the comment, excluding the comment token chars ('//, /*, */') + is_multi bool // Is a block / multi-line comment + pos token.Position +} + +// is_example returns true if the contents of this comment is a doc example. +// The current convention is '// Example: ' +pub fn (dc DocComment) is_example() bool { + return dc.text.starts_with(doc.example_pattern) +} + +// example returns the content of the example body +pub fn (dc DocComment) example() string { + return dc.text.all_after(doc.example_pattern) +} diff --git a/v_windows/v/old/vlib/v/doc/doc.v b/v_windows/v/old/vlib/v/doc/doc.v new file mode 100644 index 0000000..316cd33 --- /dev/null +++ b/v_windows/v/old/vlib/v/doc/doc.v @@ -0,0 +1,525 @@ +module doc + +import os +import time +import v.ast +import v.checker +import v.fmt +import v.parser +import v.pref +import v.scanner +import v.token + +// SymbolKind categorizes the symbols it documents. +// The names are intentionally not in order as a guide when sorting the nodes. +pub enum SymbolKind { + none_ + const_group + constant + variable + function + method + interface_ + typedef + enum_ + enum_field + struct_ + struct_field +} + +pub enum Platform { + auto + ios + macos + linux + windows + freebsd + openbsd + netbsd + dragonfly + js // for interoperability in prefs.OS + android + solaris + serenity + vinix + haiku + raw + cross // TODO: add functionality for v doc -os cross whenever possible +} + +// copy of pref.os_from_string +pub fn platform_from_string(platform_str string) ?Platform { + match platform_str { + 'all', 'cross' { return .cross } + 'linux' { return .linux } + 'windows' { return .windows } + 'ios' { return .ios } + 'macos' { return .macos } + 'freebsd' { return .freebsd } + 'openbsd' { return .openbsd } + 'netbsd' { return .netbsd } + 'dragonfly' { return .dragonfly } + 'js' { return .js } + 'solaris' { return .solaris } + 'serenity' { return .serenity } + 'vinix' { return .vinix } + 'android' { return .android } + 'haiku' { return .haiku } + 'nix' { return .linux } + '' { return .auto } + else { return error('vdoc: invalid platform `$platform_str`') } + } +} + +pub fn platform_from_filename(filename string) Platform { + suffix := filename.all_after_last('_').all_before('.c.v') + mut platform := platform_from_string(suffix) or { Platform.cross } + if platform == .auto { + platform = .cross + } + return platform +} + +pub fn (sk SymbolKind) str() string { + return match sk { + .const_group { 'Constants' } + .function, .method { 'fn' } + .interface_ { 'interface' } + .typedef { 'type' } + .enum_ { 'enum' } + .struct_ { 'struct' } + else { '' } + } +} + +pub struct Doc { +pub mut: + prefs &pref.Preferences = new_vdoc_preferences() + base_path string + table &ast.Table = ast.new_table() + checker checker.Checker = checker.Checker{ + table: 0 + pref: 0 + } + fmt fmt.Fmt + filename string + pos int + pub_only bool = true + with_comments bool = true + with_pos bool + with_head bool = true + is_vlib bool + time_generated time.Time + head DocNode + contents map[string]DocNode + scoped_contents map[string]DocNode + parent_mod_name string + orig_mod_name string + extract_vars bool + filter_symbol_names []string + common_symbols []string + platform Platform +} + +pub struct DocNode { +pub mut: + name string + content string + comments []DocComment + pos token.Position + file_path string + kind SymbolKind + tags []string + parent_name string + return_type string + children []DocNode + attrs map[string]string [json: attributes] + from_scope bool + is_pub bool [json: public] + platform Platform +} + +// new_vdoc_preferences creates a new instance of pref.Preferences tailored for v.doc. +pub fn new_vdoc_preferences() &pref.Preferences { + // vdoc should be able to parse as much user code as possible + // so its preferences should be permissive: + mut pref := &pref.Preferences{ + enable_globals: true + is_fmt: true + } + pref.fill_with_defaults() + return pref +} + +// new creates a new instance of a `Doc` struct. +pub fn new(input_path string) Doc { + mut d := Doc{ + base_path: os.real_path(input_path) + table: ast.new_table() + head: DocNode{} + contents: map[string]DocNode{} + time_generated: time.now() + } + d.fmt = fmt.Fmt{ + pref: d.prefs + indent: 0 + is_debug: false + table: d.table + } + d.checker = checker.new_checker(d.table, d.prefs) + return d +} + +// stmt reads the data of an `ast.Stmt` node and returns a `DocNode`. +// An option error is thrown if the symbol is not exposed to the public +// (when `pub_only` is enabled) or the content's of the AST node is empty. +pub fn (mut d Doc) stmt(stmt ast.Stmt, filename string) ?DocNode { + mut name := d.stmt_name(stmt) + if name in d.common_symbols { + return error('already documented') + } + if name.starts_with(d.orig_mod_name + '.') { + name = name.all_after(d.orig_mod_name + '.') + } + mut node := DocNode{ + name: name + content: d.stmt_signature(stmt) + pos: stmt.pos + file_path: os.join_path(d.base_path, filename) + is_pub: d.stmt_pub(stmt) + platform: platform_from_filename(filename) + } + if (!node.is_pub && d.pub_only) || stmt is ast.GlobalDecl { + return error('symbol $node.name not public') + } + if node.name.starts_with(d.orig_mod_name + '.') { + node.name = node.name.all_after(d.orig_mod_name + '.') + } + if node.name.len == 0 && node.comments.len == 0 && node.content.len == 0 { + return error('empty stmt') + } + match stmt { + ast.ConstDecl { + node.kind = .const_group + node.parent_name = 'Constants' + if d.extract_vars { + for field in stmt.fields { + ret_type := if field.typ == 0 { + d.expr_typ_to_string(field.expr) + } else { + d.type_to_str(field.typ) + } + node.children << DocNode{ + name: field.name.all_after(d.orig_mod_name + '.') + kind: .constant + pos: field.pos + return_type: ret_type + } + } + } + } + ast.EnumDecl { + node.kind = .enum_ + if d.extract_vars { + for field in stmt.fields { + ret_type := if field.has_expr { d.expr_typ_to_string(field.expr) } else { 'int' } + node.children << DocNode{ + name: field.name + kind: .enum_field + parent_name: node.name + pos: field.pos + return_type: ret_type + } + } + } + } + ast.InterfaceDecl { + node.kind = .interface_ + } + ast.StructDecl { + node.kind = .struct_ + if d.extract_vars { + for field in stmt.fields { + ret_type := if field.typ == 0 && field.has_default_expr { + d.expr_typ_to_string(field.default_expr) + } else { + d.type_to_str(field.typ) + } + node.children << DocNode{ + name: field.name + kind: .struct_field + parent_name: node.name + pos: field.pos + return_type: ret_type + } + } + } + } + ast.TypeDecl { + node.kind = .typedef + } + ast.FnDecl { + if stmt.is_deprecated { + node.tags << 'deprecated' + } + if stmt.is_unsafe { + node.tags << 'unsafe' + } + node.kind = .function + node.return_type = d.type_to_str(stmt.return_type) + if stmt.receiver.typ !in [0, 1] { + method_parent := d.type_to_str(stmt.receiver.typ) + node.kind = .method + node.parent_name = method_parent + } + if d.extract_vars { + for param in stmt.params { + node.children << DocNode{ + name: param.name + kind: .variable + parent_name: node.name + pos: param.pos + attrs: map{ + 'mut': param.is_mut.str() + } + return_type: d.type_to_str(param.typ) + } + } + } + } + else { + return error('invalid stmt type to document') + } + } + included := node.name in d.filter_symbol_names || node.parent_name in d.filter_symbol_names + if d.filter_symbol_names.len != 0 && !included { + return error('not included in the list of symbol names') + } + if d.prefs.os == .all { + d.common_symbols << node.name + } + return node +} + +// file_ast reads the contents of `ast.File` and returns a map of `DocNode`s. +pub fn (mut d Doc) file_ast(file_ast ast.File) map[string]DocNode { + mut contents := map[string]DocNode{} + stmts := file_ast.stmts + d.fmt.file = file_ast + d.fmt.set_current_module_name(d.orig_mod_name) + d.fmt.process_file_imports(file_ast) + mut last_import_stmt_idx := 0 + for sidx, stmt in stmts { + if stmt is ast.Import { + last_import_stmt_idx = sidx + } + } + mut preceeding_comments := []DocComment{} + // mut imports_section := true + for sidx, stmt in stmts { + if stmt is ast.ExprStmt { + // Collect comments + if stmt.expr is ast.Comment { + preceeding_comments << ast_comment_to_doc_comment(stmt.expr) + continue + } + } + // TODO: Fetch head comment once + if stmt is ast.Module { + if !d.with_head { + continue + } + // the previous comments were probably a copyright/license one + module_comment := merge_doc_comments(preceeding_comments) + preceeding_comments = [] + if !d.is_vlib && !module_comment.starts_with('Copyright (c)') { + if module_comment == '' { + continue + } + d.head.comments << preceeding_comments + } + continue + } + if last_import_stmt_idx > 0 && sidx == last_import_stmt_idx { + // the accumulated comments were interspersed before/between the imports; + // just add them all to the module comments: + if d.with_head { + d.head.comments << preceeding_comments + } + preceeding_comments = [] + // imports_section = false + } + if stmt is ast.Import { + continue + } + mut node := d.stmt(stmt, os.base(file_ast.path)) or { + preceeding_comments = [] + continue + } + if node.parent_name !in contents { + parent_node_kind := if node.parent_name == 'Constants' { + SymbolKind.const_group + } else { + SymbolKind.typedef + } + contents[node.parent_name] = DocNode{ + name: node.parent_name + kind: parent_node_kind + } + } + if d.with_comments && (preceeding_comments.len > 0) { + node.comments << preceeding_comments + } + preceeding_comments = [] + if node.parent_name.len > 0 { + parent_name := node.parent_name + if node.parent_name == 'Constants' { + node.parent_name = '' + } + contents[parent_name].children << node + } else { + contents[node.name] = node + } + } + d.fmt.mod2alias = map[string]string{} + if contents[''].kind != .const_group { + contents.delete('') + } + return contents +} + +// file_ast_with_pos has the same function as the `file_ast` but +// instead returns a list of variables in a given offset-based position. +pub fn (mut d Doc) file_ast_with_pos(file_ast ast.File, pos int) map[string]DocNode { + lscope := file_ast.scope.innermost(pos) + mut contents := map[string]DocNode{} + for name, val in lscope.objects { + if val !is ast.Var { + continue + } + vr_data := val as ast.Var + l_node := DocNode{ + name: name + pos: vr_data.pos + file_path: file_ast.path + from_scope: true + kind: .variable + return_type: d.expr_typ_to_string(vr_data.expr) + } + contents[l_node.name] = l_node + } + return contents +} + +// generate is a `Doc` method that will start documentation +// process based on a file path provided. +pub fn (mut d Doc) generate() ? { + // get all files + d.base_path = if os.is_dir(d.base_path) { + d.base_path + } else { + os.real_path(os.dir(d.base_path)) + } + d.is_vlib = !d.base_path.contains('vlib') + project_files := os.ls(d.base_path) or { return err } + v_files := d.prefs.should_compile_filtered_files(d.base_path, project_files) + if v_files.len == 0 { + return error_with_code('vdoc: No valid V files were found.', 1) + } + // parse files + mut comments_mode := scanner.CommentsMode.skip_comments + if d.with_comments { + comments_mode = .toplevel_comments + } + mut file_asts := []ast.File{} + for i, file_path in v_files { + if i == 0 { + d.parent_mod_name = get_parent_mod(d.base_path) or { '' } + } + file_asts << parser.parse_file(file_path, d.table, comments_mode, d.prefs) + } + return d.file_asts(file_asts) +} + +// file_asts has the same function as the `file_ast` function but +// accepts an array of `ast.File` and throws an error if necessary. +pub fn (mut d Doc) file_asts(file_asts []ast.File) ? { + mut fname_has_set := false + d.orig_mod_name = file_asts[0].mod.name + for i, file_ast in file_asts { + if d.filename.len > 0 && file_ast.path.contains(d.filename) && !fname_has_set { + d.filename = file_ast.path + fname_has_set = true + } + if d.with_head && i == 0 { + mut module_name := file_ast.mod.name + // if module_name != 'main' && d.parent_mod_name.len > 0 { + // module_name = d.parent_mod_name + '.' + module_name + // } + d.head = DocNode{ + name: module_name + content: 'module $module_name' + kind: .none_ + } + } else if file_ast.mod.name != d.orig_mod_name { + continue + } + if file_ast.path == d.filename { + d.checker.check(file_ast) + d.scoped_contents = d.file_ast_with_pos(file_ast, d.pos) + } + contents := d.file_ast(file_ast) + for name, node in contents { + if name !in d.contents { + d.contents[name] = node + continue + } + if d.contents[name].kind == .typedef && node.kind !in [.typedef, .none_] { + old_children := d.contents[name].children.clone() + d.contents[name] = node + d.contents[name].children = old_children + } + if d.contents[name].kind != .none_ || node.kind == .none_ { + d.contents[name].children << node.children + d.contents[name].children.sort_by_name() + d.contents[name].children.sort_by_kind() + } + } + } + if d.filter_symbol_names.len != 0 && d.contents.len != 0 { + for filter_name in d.filter_symbol_names { + if filter_name !in d.contents { + return error('vdoc: `$filter_name` symbol in module `$d.orig_mod_name` not found') + } + } + } + d.time_generated = time.now() +} + +// generate documents a certain file directory and returns an +// instance of `Doc` if it is successful. Otherwise, it will throw an error. +pub fn generate(input_path string, pub_only bool, with_comments bool, platform Platform, filter_symbol_names ...string) ?Doc { + if platform == .js { + return error('vdoc: Platform `$platform` is not supported.') + } + mut doc := new(input_path) + doc.pub_only = pub_only + doc.with_comments = with_comments + doc.filter_symbol_names = filter_symbol_names.filter(it.len != 0) + doc.prefs.os = if platform == .auto { pref.get_host_os() } else { pref.OS(int(platform)) } + doc.generate() ? + return doc +} + +// generate_with_pos has the same function as the `generate` function but +// accepts an offset-based position and enables the comments by default. +pub fn generate_with_pos(input_path string, filename string, pos int) ?Doc { + mut doc := new(input_path) + doc.pub_only = false + doc.with_comments = true + doc.with_pos = true + doc.filename = filename + doc.pos = pos + doc.generate() ? + return doc +} diff --git a/v_windows/v/old/vlib/v/doc/doc_test.v b/v_windows/v/old/vlib/v/doc/doc_test.v new file mode 100644 index 0000000..cdb0ef8 --- /dev/null +++ b/v_windows/v/old/vlib/v/doc/doc_test.v @@ -0,0 +1,18 @@ +// import v.ast +import v.doc + +// fn test_generate_with_pos() {} +// fn test_generate() {} +// fn test_generate_from_ast() {} +fn test_generate_from_mod() { + nested_mod_name := 'net.http.chunked' + nested_mod_doc := doc.generate_from_mod(nested_mod_name, false, true) or { + eprintln(err) + assert false + doc.Doc{} + } + assert nested_mod_doc.head.name == nested_mod_name + assert nested_mod_doc.head.content == 'module $nested_mod_name' + assert nested_mod_doc.contents.len == 3 + assert nested_mod_doc.contents['ChunkScanner'].children.len == 3 +} diff --git a/v_windows/v/old/vlib/v/doc/module.v b/v_windows/v/old/vlib/v/doc/module.v new file mode 100644 index 0000000..2b1d3e7 --- /dev/null +++ b/v_windows/v/old/vlib/v/doc/module.v @@ -0,0 +1,87 @@ +module doc + +import os +import v.ast +import v.parser +import v.pref + +// get_parent_mod returns the parent mod name, in dot format. +// It works by climbing up the folder hierarchy, until a folder, +// that either contains main .v files, or a v.mod file is reached. +// For example, given something like /languages/v/vlib/x/websocket/tests/autobahn +// it returns `x.websocket.tests`, because /languages/v/ has v.mod file in it. +// NB: calling this is expensive, so keep the result, instead of recomputing it. +fn get_parent_mod(input_dir string) ?string { + $if windows { + // windows root path is C: or D: + if input_dir.len <= 2 { + return error('root folder reached') + } + } $else { + if input_dir.len == 0 { + return error('root folder reached') + } + } + base_dir := os.dir(input_dir) + input_dir_name := os.file_name(base_dir) + prefs := new_vdoc_preferences() + fentries := os.ls(base_dir) or { []string{} } + files := fentries.filter(!os.is_dir(it)) + if 'v.mod' in files { + // the top level is reached, no point in climbing up further + return '' + } + v_files := prefs.should_compile_filtered_files(base_dir, files) + if v_files.len == 0 { + parent_mod := get_parent_mod(base_dir) or { return input_dir_name } + if parent_mod.len > 0 { + return parent_mod + '.' + input_dir_name + } + return error('No V files found.') + } + tbl := ast.new_table() + file_ast := parser.parse_file(v_files[0], tbl, .skip_comments, prefs) + if file_ast.mod.name == 'main' { + return '' + } + parent_mod := get_parent_mod(base_dir) or { return input_dir_name } + if parent_mod.len > 0 { + return '${parent_mod}.$file_ast.mod.name' + } + return file_ast.mod.name +} + +// lookup_module_with_path looks up the path of a given module name. +// Throws an error if the module was not found. +pub fn lookup_module_with_path(mod string, base_path string) ?string { + vexe := pref.vexe_path() + vroot := os.dir(vexe) + mod_path := mod.replace('.', os.path_separator) + compile_dir := os.real_path(base_path) + modules_dir := os.join_path(compile_dir, 'modules', mod_path) + vlib_path := os.join_path(vroot, 'vlib', mod_path) + mut paths := [modules_dir, vlib_path] + vmodules_paths := os.vmodules_paths() + for vmpath in vmodules_paths { + paths << os.join_path(vmpath, mod_path) + } + for path in paths { + if !os.exists(path) || os.is_dir_empty(path) { + continue + } + return path + } + return error('module "$mod" not found.') +} + +// lookup_module returns the result of the `lookup_module_with_path` +// but with the current directory as the provided base lookup path. +pub fn lookup_module(mod string) ?string { + return lookup_module_with_path(mod, os.dir('.')) +} + +// generate_from_mod generates a documentation from a specific module. +pub fn generate_from_mod(module_name string, pub_only bool, with_comments bool) ?Doc { + mod_path := lookup_module(module_name) ? + return generate(mod_path, pub_only, with_comments, .auto) +} diff --git a/v_windows/v/old/vlib/v/doc/node.v b/v_windows/v/old/vlib/v/doc/node.v new file mode 100644 index 0000000..736067f --- /dev/null +++ b/v_windows/v/old/vlib/v/doc/node.v @@ -0,0 +1,70 @@ +module doc + +pub fn (nodes []DocNode) find(symname string) ?DocNode { + for node in nodes { + if node.name != symname { + continue + } + return node + } + return error('symbol not found') +} + +// sort_by_name sorts the array based on the symbol names. +pub fn (mut nodes []DocNode) sort_by_name() { + nodes.sort_with_compare(compare_nodes_by_name) +} + +// sort_by_kind sorts the array based on the symbol kind. +pub fn (mut nodes []DocNode) sort_by_kind() { + nodes.sort_with_compare(compare_nodes_by_kind) +} + +fn compare_nodes_by_kind(a &DocNode, b &DocNode) int { + ak := int((*a).kind) + bk := int((*b).kind) + if ak < bk { + return -1 + } + if ak > bk { + return 1 + } + return 0 +} + +fn compare_nodes_by_name(a &DocNode, b &DocNode) int { + al := a.name.to_lower() + bl := b.name.to_lower() + return compare_strings(al, bl) +} + +// arr() converts the map into an array of `DocNode`. +pub fn (cnts map[string]DocNode) arr() []DocNode { + mut contents := cnts.keys().map(cnts[it]) + contents.sort_by_name() + contents.sort_by_kind() + return contents +} + +// merge_comments returns a `string` with the combined contents of `DocNode.comments`. +pub fn (dc DocNode) merge_comments() string { + return merge_doc_comments(dc.comments) +} + +// merge_comments_without_examples returns a `string` with the +// combined contents of `DocNode.comments` - excluding any examples. +pub fn (dc DocNode) merge_comments_without_examples() string { + sans_examples := dc.comments.filter(!it.is_example()) + return merge_doc_comments(sans_examples) +} + +// examples returns a `[]string` containing examples parsed from `DocNode.comments`. +pub fn (dn DocNode) examples() []string { + mut output := []string{} + for comment in dn.comments { + if comment.is_example() { + output << comment.example() + } + } + return output +} diff --git a/v_windows/v/old/vlib/v/doc/utils.v b/v_windows/v/old/vlib/v/doc/utils.v new file mode 100644 index 0000000..ee74113 --- /dev/null +++ b/v_windows/v/old/vlib/v/doc/utils.v @@ -0,0 +1,156 @@ +module doc + +import strings +import v.ast +import v.token + +// merge_comments merges all the comment contents into a single text. +pub fn merge_comments(comments []ast.Comment) string { + mut res := []string{} + for comment in comments { + res << comment.text.trim_left('\x01') + } + return res.join('\n') +} + +// ast_comment_to_doc_comment converts an `ast.Comment` node type to a `DocComment` +pub fn ast_comment_to_doc_comment(ast_node ast.Comment) DocComment { + text := ast_node.text // TODO .trim_left('\x01') // BUG why are this byte here in the first place? + return DocComment{ + text: text + is_multi: ast_node.is_multi + pos: token.Position{ + line_nr: ast_node.pos.line_nr + col: 0 // ast_node.pos.pos - ast_node.text.len + len: text.len + } + } +} + +// ast_comments_to_doc_comments converts an array of `ast.Comment` nodes to +// an array of `DocComment` nodes +pub fn ast_comments_to_doc_comments(ast_nodes []ast.Comment) []DocComment { + mut doc_comments := []DocComment{len: ast_nodes.len} + for ast_comment in ast_nodes { + doc_comments << ast_comment_to_doc_comment(ast_comment) + } + return doc_comments +} + +// merge_doc_comments merges all the comments starting from +// the last up to the first item of the array. +pub fn merge_doc_comments(comments []DocComment) string { + if comments.len == 0 { + return '' + } + mut comment := '' + mut last_comment_line_nr := 0 + for i := comments.len - 1; i >= 0; i-- { + cmt := comments[i] + if last_comment_line_nr != 0 && cmt.pos.line_nr + 1 < last_comment_line_nr - 1 { + // skip comments that are not part of a continuous block, + // located right above the top level statement. + // break + } + mut cmt_content := cmt.text.trim_left('\x01') + if cmt.is_multi { + // ignore /* */ style comments for now + continue + // if cmt_content.len == 0 { + // continue + // } + // mut new_cmt_content := '' + // mut is_codeblock := false + // // println(cmt_content) + // lines := cmt_content.split_into_lines() + // for j, line in lines { + // trimmed := line.trim_space().trim_left(cmt_prefix) + // if trimmed.starts_with('- ') || (trimmed.len >= 2 && trimmed[0].is_digit() && trimmed[1] == `.`) || is_codeblock { + // new_cmt_content += line + '\n' + // } else if line.starts_with('```') { + // is_codeblock = !is_codeblock + // new_cmt_content += line + '\n' + // } else { + // new_cmt_content += trimmed + '\n' + // } + // } + // return new_cmt_content + } + // eprintln('cmt: $cmt') + cseparator := if cmt_content.starts_with('```') { '\n' } else { ' ' } + comment = cmt_content + cseparator + comment + last_comment_line_nr = cmt.pos.line_nr + 1 + } + return comment +} + +// stmt_signature returns the signature of a given `ast.Stmt` node. +pub fn (mut d Doc) stmt_signature(stmt ast.Stmt) string { + match stmt { + ast.Module { + return 'module $stmt.name' + } + ast.FnDecl { + return stmt.stringify(d.table, d.fmt.cur_mod, d.fmt.mod2alias) + } + else { + d.fmt.out = strings.new_builder(1000) + d.fmt.stmt(stmt) + return d.fmt.out.str().trim_space() + } + } +} + +// stmt_name returns the name of a given `ast.Stmt` node. +pub fn (d Doc) stmt_name(stmt ast.Stmt) string { + match stmt { + ast.FnDecl, ast.StructDecl, ast.EnumDecl, ast.InterfaceDecl { + return stmt.name + } + ast.TypeDecl { + match stmt { + ast.FnTypeDecl, ast.AliasTypeDecl, ast.SumTypeDecl { return stmt.name } + } + } + ast.ConstDecl { + return '' + } // leave it blank + else { + return '' + } + } +} + +// stmt_pub returns a boolean if a given `ast.Stmt` node +// is exposed to the public. +pub fn (d Doc) stmt_pub(stmt ast.Stmt) bool { + match stmt { + ast.FnDecl, ast.StructDecl, ast.EnumDecl, ast.InterfaceDecl, ast.ConstDecl { + return stmt.is_pub + } + ast.TypeDecl { + match stmt { + ast.FnTypeDecl, ast.AliasTypeDecl, ast.SumTypeDecl { return stmt.is_pub } + } + } + else { + return false + } + } +} + +// type_to_str is a wrapper function around `fmt.ast.type_to_str`. +pub fn (mut d Doc) type_to_str(typ ast.Type) string { + // why is it the default behaviour of ast.type_to_str + // to convert math.bits.Type to bits.Type? + d.table.cmod_prefix = d.orig_mod_name + '.' + return d.fmt.table.type_to_str(typ).all_after('&') +} + +// expr_typ_to_string has the same function as `Doc.typ_to_str` +// but for `ast.Expr` nodes. The checker will check first the +// node and it executes the `type_to_str` method. +pub fn (mut d Doc) expr_typ_to_string(ex ast.Expr) string { + expr_typ := d.checker.expr(ex) + return d.type_to_str(expr_typ) +} diff --git a/v_windows/v/old/vlib/v/dotgraph/dotgraph.c.v b/v_windows/v/old/vlib/v/dotgraph/dotgraph.c.v new file mode 100644 index 0000000..a98f7d8 --- /dev/null +++ b/v_windows/v/old/vlib/v/dotgraph/dotgraph.c.v @@ -0,0 +1,8 @@ +module dotgraph + +pub fn start_digraph() { + println('digraph G {') + C.atexit(fn () { + println('}') + }) +} diff --git a/v_windows/v/old/vlib/v/dotgraph/dotgraph.v b/v_windows/v/old/vlib/v/dotgraph/dotgraph.v new file mode 100644 index 0000000..087aef8 --- /dev/null +++ b/v_windows/v/old/vlib/v/dotgraph/dotgraph.v @@ -0,0 +1,81 @@ +module dotgraph + +import strings + +[heap] +struct DotGraph { +mut: + sb strings.Builder +} + +pub fn new(name string, label string, color string) &DotGraph { + mut res := &DotGraph{ + sb: strings.new_builder(1024) + } + res.writeln(' subgraph cluster_$name {') + res.writeln('\tedge [fontname="Helvetica",fontsize="10",labelfontname="Helvetica",labelfontsize="10",style="solid",color="black"];') + res.writeln('\tnode [fontname="Helvetica",fontsize="10",style="filled",fontcolor="black",fillcolor="white",color="black",shape="box"];') + res.writeln('\trankdir="LR";') + res.writeln('\tcolor="$color";') + res.writeln('\tlabel="$label";') + // Node14 [shape="box",label="PrivateBase",URL="$classPrivateBase.html"]; + // Node15 -> Node9 [dir=back,color="midnightblue",fontsize=10,style="solid"]; + return res +} + +pub fn (mut d DotGraph) writeln(line string) { + d.sb.writeln(line) +} + +pub fn (mut d DotGraph) finish() { + d.sb.writeln(' }') + println(d.sb.str()) +} + +// + +pub struct NewNodeConfig { + node_name string + should_highlight bool + tooltip string + ctx voidptr = voidptr(0) + name2node_fn FnLabel2NodeName = node_name +} + +pub fn (mut d DotGraph) new_node(nlabel string, cfg NewNodeConfig) { + mut nname := cfg.name2node_fn(nlabel, cfg.ctx) + if cfg.node_name != '' { + nname = cfg.node_name + } + if cfg.should_highlight { + d.writeln('\t$nname [label="$nlabel",color="blue",height=0.2,width=0.4,fillcolor="#00FF00",tooltip="$cfg.tooltip",shape=oval];') + } else { + d.writeln('\t$nname [shape="box",label="$nlabel"];') + } +} + +// + +pub struct NewEdgeConfig { + should_highlight bool + ctx voidptr = voidptr(0) + name2node_fn FnLabel2NodeName = node_name +} + +pub fn (mut d DotGraph) new_edge(source string, target string, cfg NewEdgeConfig) { + nsource := cfg.name2node_fn(source, cfg.ctx) + ntarget := cfg.name2node_fn(target, cfg.ctx) + if cfg.should_highlight { + d.writeln('\t$nsource -> $ntarget [color="blue"];') + } else { + d.writeln('\t$nsource -> $ntarget;') + } +} + +// + +pub type FnLabel2NodeName = fn (string, voidptr) string + +pub fn node_name(name string, context voidptr) string { + return name.replace('.', '_') +} diff --git a/v_windows/v/old/vlib/v/embed_file/embed_file.v b/v_windows/v/old/vlib/v/embed_file/embed_file.v new file mode 100644 index 0000000..3c556d3 --- /dev/null +++ b/v_windows/v/old/vlib/v/embed_file/embed_file.v @@ -0,0 +1,105 @@ +module embed_file + +import os + +// https://github.com/vlang/rfcs/blob/master/embedding_resources.md +// EmbedFileData encapsulates functionality for the `$embed_file()` compile time call. +pub struct EmbedFileData { + path string + apath string +mut: + compressed &byte + uncompressed &byte + free_compressed bool + free_uncompressed bool +pub: + len int +} + +pub fn (ed EmbedFileData) str() string { + return 'embed_file.EmbedFileData{ len: $ed.len, path: "$ed.path", path: "$ed.apath", uncompressed: ${ptr_str(ed.uncompressed)} }' +} + +[unsafe] +pub fn (mut ed EmbedFileData) free() { + unsafe { + ed.path.free() + ed.apath.free() + if ed.free_compressed { + free(ed.compressed) + ed.compressed = &byte(0) + } + if ed.free_uncompressed { + free(ed.uncompressed) + ed.uncompressed = &byte(0) + } + } +} + +pub fn (original &EmbedFileData) to_string() string { + unsafe { + mut ed := &EmbedFileData(original) + the_copy := &byte(memdup(ed.data(), ed.len)) + return the_copy.vstring_with_len(ed.len) + } +} + +pub fn (original &EmbedFileData) to_bytes() []byte { + unsafe { + mut ed := &EmbedFileData(original) + the_copy := memdup(ed.data(), ed.len) + return the_copy.vbytes(ed.len) + } +} + +pub fn (mut ed EmbedFileData) data() &byte { + if !isnil(ed.uncompressed) { + return ed.uncompressed + } else { + if isnil(ed.uncompressed) && !isnil(ed.compressed) { + // TODO implement uncompression + // See also C Gen.gen_embedded_data() where the compression should occur. + ed.uncompressed = ed.compressed + } else { + mut path := os.resource_abs_path(ed.path) + if !os.is_file(path) { + path = ed.apath + if !os.is_file(path) { + panic('EmbedFileData error: files "$ed.path" and "$ed.apath" do not exist') + } + } + bytes := os.read_bytes(path) or { + panic('EmbedFileData error: "$path" could not be read: $err') + } + ed.uncompressed = bytes.data + ed.free_uncompressed = true + } + } + return ed.uncompressed +} + +////////////////////////////////////////////////////////////////////////////// +// EmbedFileIndexEntry is used internally by the V compiler when you compile a +// program that uses $embed_file('file.bin') in -prod mode. +// V will generate a static index of all embedded files, and will call the +// find_index_entry_by_path over the index and the relative paths of the embeds. +// NB: these are public on purpose, to help -usecache. +pub struct EmbedFileIndexEntry { + id int + path string + data &byte +} + +// find_index_entry_by_path is used internally by the V compiler: +pub fn find_index_entry_by_path(start voidptr, path string) &EmbedFileIndexEntry { + mut x := &EmbedFileIndexEntry(start) + for !(x.path == path || isnil(x.data)) { + unsafe { + x++ + } + } + $if debug_embed_file_in_prod ? { + eprintln('>> v.embed_file find_index_entry_by_path ${ptr_str(start)}, path: "$path" => ${ptr_str(x)}') + } + return x +} diff --git a/v_windows/v/old/vlib/v/embed_file/embed_file_test.v b/v_windows/v/old/vlib/v/embed_file/embed_file_test.v new file mode 100644 index 0000000..2af4459 --- /dev/null +++ b/v_windows/v/old/vlib/v/embed_file/embed_file_test.v @@ -0,0 +1,25 @@ +const const_file = $embed_file('v.png') + +fn test_const_embed_file() { + mut file := const_file + eprintln('file: $file') + assert file.len == 603 + fdata := file.data() + eprintln('file after .data() call: $file') + assert file.len == 603 + unsafe { + assert fdata.vbytes(4) == [byte(0x89), `P`, `N`, `G`] + } +} + +fn test_embed_file() { + mut file := $embed_file('v.png') + eprintln('file: $file') + assert file.len == 603 + fdata := file.data() + eprintln('file after .data() call: $file') + assert file.len == 603 + unsafe { + assert fdata.vbytes(4) == [byte(0x89), `P`, `N`, `G`] + } +} diff --git a/v_windows/v/old/vlib/v/embed_file/v.png b/v_windows/v/old/vlib/v/embed_file/v.png new file mode 100644 index 0000000..8739e46 Binary files /dev/null and b/v_windows/v/old/vlib/v/embed_file/v.png differ diff --git a/v_windows/v/old/vlib/v/errors/errors.v b/v_windows/v/old/vlib/v/errors/errors.v new file mode 100644 index 0000000..16f31ed --- /dev/null +++ b/v_windows/v/old/vlib/v/errors/errors.v @@ -0,0 +1,38 @@ +module errors + +import v.token + +pub enum Reporter { + scanner + parser + checker + gen +} + +pub struct Error { +pub: + message string + details string + file_path string + pos token.Position + backtrace string + reporter Reporter +} + +pub struct Warning { +pub: + message string + details string + file_path string + pos token.Position + reporter Reporter +} + +pub struct Notice { +pub: + message string + details string + file_path string + pos token.Position + reporter Reporter +} diff --git a/v_windows/v/old/vlib/v/eval/eval.v b/v_windows/v/old/vlib/v/eval/eval.v new file mode 100644 index 0000000..45bb687 --- /dev/null +++ b/v_windows/v/old/vlib/v/eval/eval.v @@ -0,0 +1,99 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module eval + +import v.ast +import v.checker +import v.pref + +pub type Object = int | string + +pub struct Eval { +mut: + checker checker.Checker + vars map[string]Var + table &ast.Table +} + +pub struct Var { + value Object +} + +pub fn (mut e Eval) eval(file ast.File, table &ast.Table) string { + vpref := &pref.Preferences{} + e.table = table + mut res := '' + e.checker = checker.new_checker(table, vpref) + for stmt in file.stmts { + res += e.stmt(stmt) + '\n' + } + return res.trim_space() +} + +fn print_object(o Object) { + match o { + int { println(o) } + else { println('unknown object') } + } +} + +pub fn (o Object) str() string { + match o { + int { return o.str() } + else { println('unknown object') } + } + return '' +} + +fn (mut e Eval) stmt(node ast.Stmt) string { + match node { + ast.AssignStmt { + // TODO; replaced VarDecl + } + ast.ExprStmt { + o := e.expr(node.expr) + print('out: ') + print_object(o) + return o.str() + } + // ast.StructDecl { + // println('s decl') + // } + // ast.VarDecl { + // e.vars[it.name] = Var{ + // value: e.expr(it.expr) + // } + // } + else {} + } + return '>>' +} + +fn (mut e Eval) expr(node ast.Expr) Object { + match node { + ast.IntegerLiteral { + return node.val + } + ast.Ident { + print_object(node.value) + // Find the variable + v := e.vars[node.name] + return v.value + } + ast.InfixExpr { + e.checker.infix_expr(mut node) + // println('bin $it.op') + left := e.expr(node.left) as int + right := e.expr(node.right) as int + match node.op { + .plus { return left + right } + .mul { return left * right } + else {} + } + } + else {} + } + return 0 + // return Object{} +} diff --git a/v_windows/v/old/vlib/v/fmt/align.v b/v_windows/v/old/vlib/v/fmt/align.v new file mode 100644 index 0000000..f767abd --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/align.v @@ -0,0 +1,56 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module fmt + +import math.mathutil + +const struct_field_align_threshold = 8 + +struct AlignInfo { +mut: + line_nr int + max_len int + max_type_len int +} + +struct AddInfoConfig { + use_threshold bool +} + +fn (mut infos []AlignInfo) add_new_info(len int, type_len int, line int) { + infos << AlignInfo{ + line_nr: line + max_len: len + max_type_len: type_len + } +} + +[direct_array_access] +fn (mut infos []AlignInfo) add_info(len int, type_len int, line int, cfg AddInfoConfig) { + if infos.len == 0 { + infos.add_new_info(len, type_len, line) + return + } + i := infos.len - 1 + if line - infos[i].line_nr > 1 { + infos.add_new_info(len, type_len, line) + return + } + if cfg.use_threshold { + len_diff := mathutil.abs(infos[i].max_len - len) + + mathutil.abs(infos[i].max_type_len - type_len) + + if len_diff >= fmt.struct_field_align_threshold { + infos.add_new_info(len, type_len, line) + return + } + } + infos[i].line_nr = line + if len > infos[i].max_len { + infos[i].max_len = len + } + if type_len > infos[i].max_type_len { + infos[i].max_type_len = type_len + } +} diff --git a/v_windows/v/old/vlib/v/fmt/asm.v b/v_windows/v/old/vlib/v/fmt/asm.v new file mode 100644 index 0000000..9de83dd --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/asm.v @@ -0,0 +1,185 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module fmt + +import v.ast + +fn (mut f Fmt) asm_stmt(stmt ast.AsmStmt) { + f.write('asm ') + if stmt.is_volatile { + f.write('volatile ') + } else if stmt.is_goto { + f.write('goto ') + } + f.writeln('$stmt.arch {') + f.indent++ + + f.asm_templates(stmt.templates) + + if stmt.output.len != 0 || stmt.input.len != 0 || stmt.clobbered.len != 0 { + f.write('; ') + } + f.asm_ios(stmt.output) + + if stmt.input.len != 0 || stmt.clobbered.len != 0 { + f.write('; ') + } + f.asm_ios(stmt.input) + + if stmt.clobbered.len != 0 { + f.write('; ') + } + f.asm_clobbered(stmt.clobbered) + + f.indent-- + f.writeln('}') + if f.indent == 0 { + f.writeln('') + } +} + +fn (mut f Fmt) asm_arg(arg ast.AsmArg) { + match arg { + ast.AsmRegister { + f.asm_reg(arg) + } + ast.AsmAlias { + f.write('$arg.name') + } + ast.IntegerLiteral, ast.FloatLiteral, ast.CharLiteral { + f.write(arg.val) + } + ast.BoolLiteral { + f.write(arg.val.str()) + } + string { + f.write(arg) + } + ast.AsmAddressing { + f.write('[') + base := arg.base + index := arg.index + displacement := arg.displacement + scale := arg.scale + match arg.mode { + .base { + f.asm_arg(base) + } + .displacement { + f.asm_arg(displacement) + } + .base_plus_displacement { + f.asm_arg(base) + f.write(' + ') + f.asm_arg(displacement) + } + .index_times_scale_plus_displacement { + f.asm_arg(index) + f.write(' * $scale + ') + f.asm_arg(displacement) + } + .base_plus_index_plus_displacement { + f.asm_arg(base) + f.write(' + ') + f.asm_arg(index) + f.write(' + ') + f.asm_arg(displacement) + } + .base_plus_index_times_scale_plus_displacement { + f.asm_arg(base) + f.write(' + ') + f.asm_arg(index) + f.write(' * $scale + ') + f.asm_arg(displacement) + } + .rip_plus_displacement { + f.asm_arg(base) + f.write(' + ') + f.asm_arg(displacement) + } + .invalid { + panic('fmt: invalid addressing mode') + } + } + f.write(']') + } + ast.AsmDisp { + if arg.val.len >= 2 && arg.val[arg.val.len - 1] in [`b`, `f`] + && arg.val[..arg.val.len - 1].bytes().all(it.is_digit()) { + f.write(arg.val[arg.val.len - 1].ascii_str()) + f.write(arg.val[..arg.val.len - 1]) + } else { + f.write(arg.val) + } + } + } +} + +fn (mut f Fmt) asm_reg(reg ast.AsmRegister) { + f.write(reg.name) +} + +fn (mut f Fmt) asm_templates(templates []ast.AsmTemplate) { + for template in templates { + if template.is_directive { + f.write('.') + } + f.write('$template.name') + if template.is_label { + f.write(':') + } else if template.args.len > 0 { + f.write(' ') + } + for i, arg in template.args { + f.asm_arg(arg) + if i + 1 < template.args.len { + f.write(', ') + } + } + if template.comments.len == 0 { + f.writeln('') + } else { + f.comments(template.comments, inline: false) + } + } +} + +fn (mut f Fmt) asm_clobbered(clobbered []ast.AsmClobbered) { + for i, clob in clobbered { + if i != 0 { + f.write(' ') + } + f.write(clob.reg.name) + + if clob.comments.len == 0 { + f.writeln('') + } else { + f.comments(clob.comments, inline: false) + } + } +} + +fn (mut f Fmt) asm_ios(ios []ast.AsmIO) { + for i, io in ios { + if i != 0 { + f.write(' ') + } + + f.write('$io.constraint ($io.expr)') + mut as_block := true + if io.expr is ast.Ident { + if io.expr.name == io.alias { + as_block = false + } + } + if as_block && io.alias != '' { + f.write(' as $io.alias') + } + if io.comments.len == 0 { + f.writeln('') + } else { + f.comments(io.comments, inline: false) + } + } +} diff --git a/v_windows/v/old/vlib/v/fmt/attrs.v b/v_windows/v/old/vlib/v/fmt/attrs.v new file mode 100644 index 0000000..798cbb2 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/attrs.v @@ -0,0 +1,60 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module fmt + +import v.ast + +pub fn (mut f Fmt) attrs(attrs []ast.Attr) { + mut sorted_attrs := attrs.clone() + // Sort the attributes. The ones with arguments come first. + sorted_attrs.sort(a.arg.len > b.arg.len) + for i, attr in sorted_attrs { + if attr.arg.len == 0 { + f.single_line_attrs(sorted_attrs[i..]) + break + } + f.writeln('[$attr]') + } +} + +pub struct AttrsOptions { + inline bool +} + +pub fn (mut f Fmt) single_line_attrs(attrs []ast.Attr, options AttrsOptions) { + if attrs.len == 0 { + return + } + mut sorted_attrs := attrs.clone() + sorted_attrs.sort(a.name < b.name) + if options.inline { + f.write(' ') + } + f.write('[') + for i, attr in sorted_attrs { + if i > 0 { + f.write('; ') + } + f.write('$attr') + } + f.write(']') + if !options.inline { + f.writeln('') + } +} + +fn inline_attrs_len(attrs []ast.Attr) int { + if attrs.len == 0 { + return 0 + } + mut n := 2 // ' ['.len + for i, attr in attrs { + if i > 0 { + n += 2 // '; '.len + } + n += '$attr'.len + } + n++ // ']'.len + return n +} diff --git a/v_windows/v/old/vlib/v/fmt/comments.v b/v_windows/v/old/vlib/v/fmt/comments.v new file mode 100644 index 0000000..8c55794 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/comments.v @@ -0,0 +1,141 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module fmt + +import v.ast + +pub enum CommentsLevel { + keep + indent +} + +// CommentsOptions defines the way comments are going to be written +// - has_nl: adds an newline at the end of a list of comments +// - inline: line comments will be on the same line as the last statement +// - level: either .keep (don't indent), or .indent (increment indentation) +// - iembed: a /* ... */ block comment used inside expressions; // comments the whole line +// - prev_line: the line number of the previous token to save linebreaks +pub struct CommentsOptions { + has_nl bool = true + inline bool + level CommentsLevel + iembed bool + prev_line int = -1 +} + +pub fn (mut f Fmt) comment(node ast.Comment, options CommentsOptions) { + // Shebang in .vsh files + if node.text.starts_with('#!') { + f.writeln(node.text) + return + } + if options.level == .indent { + f.indent++ + } + if options.iembed { + x := node.text.trim_left('\x01').trim_space() + if x.contains('\n') { + f.writeln('/*') + f.writeln(x) + f.write('*/') + } else { + f.write('/* $x */') + } + } else if !node.text.contains('\n') { + is_separate_line := !options.inline || node.text.starts_with('\x01') + mut s := node.text.trim_left('\x01').trim_right(' ') + mut out_s := '//' + if s != '' { + if is_char_alphanumeric(s[0]) { + out_s += ' ' + } + out_s += s + } + if !is_separate_line && f.indent > 0 { + f.remove_new_line() // delete the generated \n + f.write(' ') + } + f.write(out_s) + } else { + lines := node.text.trim_space().split_into_lines() + start_break := is_char_alphanumeric(node.text[0]) || node.text[0].is_space() + end_break := is_char_alphanumeric(node.text.trim('\t').bytes().last()) + || node.text.bytes().last().is_space() + f.write('/*') + if start_break { + f.writeln('') + } + for line in lines { + f.writeln(line.trim_right(' ')) + f.empty_line = false + } + if end_break { + f.empty_line = true + } else { + f.remove_new_line() + } + f.write('*/') + } + if options.level == .indent { + f.indent-- + } +} + +pub fn (mut f Fmt) comments(comments []ast.Comment, options CommentsOptions) { + mut prev_line := options.prev_line + for i, c in comments { + if options.prev_line > -1 && ((c.pos.line_nr > prev_line && f.out.last_n(1) != '\n') + || (c.pos.line_nr > prev_line + 1 && f.out.last_n(2) != '\n\n')) { + f.writeln('') + } + if !f.out.last_n(1)[0].is_space() { + f.write(' ') + } + f.comment(c, options) + if !options.iembed && (i < comments.len - 1 || options.has_nl) { + f.writeln('') + } + prev_line = c.pos.last_line + } +} + +pub fn (mut f Fmt) comments_before_field(comments []ast.Comment) { + // They behave the same as comments after the last field. This alias is just for clarity. + f.comments_after_last_field(comments) +} + +pub fn (mut f Fmt) comments_after_last_field(comments []ast.Comment) { + for comment in comments { + f.indent++ + f.empty_line = true + f.comment(comment, inline: true) + f.writeln('') + f.indent-- + } +} + +pub fn (mut f Fmt) import_comments(comments []ast.Comment, options CommentsOptions) { + if comments.len == 0 { + return + } + if options.inline { + f.remove_new_line(imports_buffer: true) + } + for c in comments { + ctext := c.text.trim_left('\x01') + if ctext == '' { + continue + } + mut out_s := if options.inline { ' ' } else { '' } + '//' + if is_char_alphanumeric(ctext[0]) { + out_s += ' ' + } + out_s += ctext + f.out_imports.writeln(out_s) + } +} + +fn is_char_alphanumeric(c byte) bool { + return c.is_letter() || c.is_digit() +} diff --git a/v_windows/v/old/vlib/v/fmt/fmt.v b/v_windows/v/old/vlib/v/fmt/fmt.v new file mode 100644 index 0000000..6de223a --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/fmt.v @@ -0,0 +1,2381 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module fmt + +import math.mathutil +import v.ast +import strings +import v.util +import v.pref + +const ( + bs = '\\' + // when to break a line dependant on penalty + max_len = [0, 35, 60, 85, 93, 100] +) + +pub struct Fmt { +pub mut: + file ast.File + table &ast.Table + pref &pref.Preferences + is_debug bool + out strings.Builder + out_imports strings.Builder + indent int + empty_line bool + line_len int // the current line length, NB: it counts \t as 4 spaces, and starts at 0 after f.writeln + buffering bool // disables line wrapping for exprs that will be analyzed later + par_level int // how many parentheses are put around the current expression + array_init_break []bool // line breaks after elements in hierarchy level of multi dimensional array + array_init_depth int // current level of hierarchy in array init + single_line_if bool + cur_mod string + did_imports bool + is_assign bool + is_struct_init bool + auto_imports []string // automatically inserted imports that the user forgot to specify + import_pos int // position of the imports in the resulting string for later autoimports insertion + used_imports []string // to remove unused imports + import_syms_used map[string]bool // to remove unused import symbols. + mod2alias map[string]string // for `import time as t`, will contain: 'time'=>'t' + use_short_fn_args bool + single_line_fields bool // should struct fields be on a single line + it_name string // the name to replace `it` with + inside_lambda bool + inside_const bool + is_mbranch_expr bool // match a { x...y { } } +} + +pub fn fmt(file ast.File, table &ast.Table, pref &pref.Preferences, is_debug bool) string { + mut f := Fmt{ + file: file + table: table + pref: pref + is_debug: is_debug + out: strings.new_builder(1000) + out_imports: strings.new_builder(200) + } + f.process_file_imports(file) + f.set_current_module_name('main') + // As these are toplevel stmts, the indent increase done in f.stmts() has to be compensated + f.indent-- + f.stmts(file.stmts) + f.indent++ + f.imports(f.file.imports) // now that we have all autoimports, handle them + res := f.out.str().trim_space() + '\n' + if res.len == 1 { + return f.out_imports.str().trim_space() + '\n' + } + bounded_import_pos := mathutil.min(res.len, f.import_pos) + return res[..bounded_import_pos] + f.out_imports.str() + res[bounded_import_pos..] +} + +pub fn (mut f Fmt) process_file_imports(file &ast.File) { + for imp in file.imports { + f.mod2alias[imp.mod] = imp.alias + for sym in imp.syms { + f.mod2alias['${imp.mod}.$sym.name'] = sym.name + f.mod2alias['${imp.mod.all_after_last('.')}.$sym.name'] = sym.name + f.mod2alias[sym.name] = sym.name + f.import_syms_used[sym.name] = false + } + } + f.auto_imports = file.auto_imports +} + +//=== Basic buffer write operations ===// + +pub fn (mut f Fmt) write(s string) { + if f.indent > 0 && f.empty_line { + f.write_indent() + } + f.out.write_string(s) + f.line_len += s.len + f.empty_line = false +} + +pub fn (mut f Fmt) writeln(s string) { + if f.indent > 0 && f.empty_line && s.len > 0 { + f.write_indent() + } + f.out.writeln(s) + f.empty_line = true + f.line_len = 0 +} + +fn (mut f Fmt) write_indent() { + f.out.write_string(util.tabs(f.indent)) + f.line_len += f.indent * 4 +} + +pub fn (mut f Fmt) wrap_long_line(penalty_idx int, add_indent bool) bool { + if f.buffering { + return false + } + if penalty_idx > 0 && f.line_len <= fmt.max_len[penalty_idx] { + return false + } + if f.out[f.out.len - 1] == ` ` { + f.out.go_back(1) + } + f.write('\n') + f.line_len = 0 + if add_indent { + f.indent++ + } + f.write_indent() + if add_indent { + f.indent-- + } + return true +} + +pub struct RemoveNewLineConfig { + imports_buffer bool // Work on f.out_imports instead of f.out +} + +pub fn (mut f Fmt) remove_new_line(cfg RemoveNewLineConfig) { + mut buffer := if cfg.imports_buffer { unsafe { &f.out_imports } } else { unsafe { &f.out } } + mut i := 0 + for i = buffer.len - 1; i >= 0; i-- { + if !buffer.byte_at(i).is_space() { // != `\n` { + break + } + } + buffer.go_back(buffer.len - i - 1) + f.empty_line = false +} + +//=== Specialized write methods ===// + +fn (mut f Fmt) write_language_prefix(lang ast.Language) { + match lang { + .c { f.write('C.') } + .js { f.write('JS.') } + else {} + } +} + +fn (mut f Fmt) write_generic_types(gtypes []ast.Type) { + if gtypes.len > 0 { + f.write('<') + gtypes_string := gtypes.map(f.table.type_to_str(it)).join(', ') + f.write(gtypes_string) + f.write('>') + } +} + +//=== Module handling helper methods ===// + +pub fn (mut f Fmt) set_current_module_name(cmodname string) { + f.cur_mod = cmodname + f.table.cmod_prefix = cmodname + '.' +} + +fn (f Fmt) get_modname_prefix(mname string) (string, string) { + // ./tests/proto_module_importing_vproto_keep.vv to know, why here is checked for ']' and '&' + if !mname.contains(']') && !mname.contains('&') { + return mname, '' + } + after_rbc := mname.all_after_last(']') + after_ref := mname.all_after_last('&') + modname := if after_rbc.len < after_ref.len { after_rbc } else { after_ref } + return modname, mname.trim_suffix(modname) +} + +fn (mut f Fmt) is_external_name(name string) bool { + if name.len > 2 && name[0] == `C` && name[1] == `.` { + return true + } + if name.len > 3 && name[0] == `J` && name[1] == `S` && name[2] == `.` { + return true + } + return false +} + +pub fn (mut f Fmt) no_cur_mod(typename string) string { + return util.no_cur_mod(typename, f.cur_mod) +} + +// foo.bar.fn() => bar.fn() +pub fn (mut f Fmt) short_module(name string) string { + if !name.contains('.') { + return name + } + if name in f.mod2alias { + return f.mod2alias[name] + } + if name.ends_with('>') { + x := name.trim_suffix('>').split('<') + if x.len == 2 { + main := f.short_module(x[0]) + genlist := x[1].split(',') + genshorts := genlist.map(f.short_module(it)).join(',') + return '$main<$genshorts>' + } + } + vals := name.split('.') + if vals.len < 2 { + return name + } + idx := vals.len - 1 + mname, tprefix := f.get_modname_prefix(vals[..idx].join('.')) + symname := vals[vals.len - 1] + aname := f.mod2alias[mname] + if aname == '' { + return symname + } + return '$tprefix${aname}.$symname' +} + +//=== Import-related methods ===// + +pub fn (mut f Fmt) mark_types_import_as_used(typ ast.Type) { + sym := f.table.get_type_symbol(typ) + f.mark_import_as_used(sym.name) +} + +// `name` is a function (`foo.bar()`) or type (`foo.Bar{}`) +pub fn (mut f Fmt) mark_import_as_used(name string) { + parts := name.split('.') + last := parts.last() + if last in f.import_syms_used { + f.import_syms_used[last] = true + } + if parts.len == 1 { + return + } + mod := parts[0..parts.len - 1].join('.') + if mod in f.used_imports { + return + } + f.used_imports << mod +} + +pub fn (mut f Fmt) imports(imports []ast.Import) { + if f.did_imports || imports.len == 0 { + return + } + f.did_imports = true + mut num_imports := 0 + mut already_imported := map[string]bool{} + + for imp in imports { + if imp.mod !in f.used_imports { + // TODO bring back once only unused imports are removed + // continue + } + if imp.mod in f.auto_imports && imp.mod !in f.used_imports { + continue + } + import_text := 'import ${f.imp_stmt_str(imp)}' + if already_imported[import_text] { + continue + } + already_imported[import_text] = true + f.out_imports.writeln(import_text) + f.import_comments(imp.comments, inline: true) + f.import_comments(imp.next_comments) + num_imports++ + } + if num_imports > 0 { + f.out_imports.writeln('') + } +} + +pub fn (f Fmt) imp_stmt_str(imp ast.Import) string { + is_diff := imp.alias != imp.mod && !imp.mod.ends_with('.' + imp.alias) + mut imp_alias_suffix := if is_diff { ' as $imp.alias' } else { '' } + + mut syms := imp.syms.map(it.name).filter(f.import_syms_used[it]) + syms.sort() + if syms.len > 0 { + imp_alias_suffix += if imp.syms[0].pos.line_nr == imp.pos.line_nr { + ' { ' + syms.join(', ') + ' }' + } else { + ' {\n\t' + syms.join(',\n\t') + ',\n}' + } + } + return '$imp.mod$imp_alias_suffix' +} + +//=== Node helpers ===// + +fn (f Fmt) should_insert_newline_before_node(node ast.Node, prev_node ast.Node) bool { + // No need to insert a newline if there is already one + if f.out.last_n(2) == '\n\n' { + return false + } + prev_line_nr := prev_node.position().last_line + // The nodes are Stmts + if node is ast.Stmt && prev_node is ast.Stmt { + stmt := node + prev_stmt := prev_node + // Force a newline after a block of HashStmts + if prev_stmt is ast.HashStmt && stmt !is ast.HashStmt && stmt !is ast.ExprStmt { + return true + } + // Force a newline after function declarations + // The only exception is inside an block of no_body functions + if prev_stmt is ast.FnDecl { + if stmt !is ast.FnDecl || !prev_stmt.no_body { + return true + } + } + // Force a newline after struct declarations + if prev_stmt is ast.StructDecl { + return true + } + // Empty line after an block of type declarations + if prev_stmt is ast.TypeDecl && stmt !is ast.TypeDecl { + return true + } + // Imports are handled special hence they are ignored here + if stmt is ast.Import || prev_stmt is ast.Import { + return false + } + // Attributes are not respected in the stmts position, so this requires manual checking + if stmt is ast.StructDecl { + if stmt.attrs.len > 0 && stmt.attrs[0].pos.line_nr - prev_line_nr <= 1 { + return false + } + } + if stmt is ast.FnDecl { + if stmt.attrs.len > 0 && stmt.attrs[0].pos.line_nr - prev_line_nr <= 1 { + return false + } + } + } + // The node shouldn't have a newline before + if node.position().line_nr - prev_line_nr <= 1 { + return false + } + return true +} + +pub fn (mut f Fmt) node_str(node ast.Node) string { + was_empty_line := f.empty_line + prev_line_len := f.line_len + pos := f.out.len + match node { + ast.Stmt { f.stmt(node) } + ast.Expr { f.expr(node) } + else { panic('´f.node_str()´ is not implemented for ${node}.') } + } + str := f.out.after(pos) + f.out.go_back_to(pos) + f.empty_line = was_empty_line + f.line_len = prev_line_len + return str +} + +//=== General Stmt-related methods and helpers ===// + +pub fn (mut f Fmt) stmts(stmts []ast.Stmt) { + mut prev_stmt := if stmts.len > 0 { stmts[0] } else { ast.empty_stmt() } + f.indent++ + for stmt in stmts { + if !f.pref.building_v && f.should_insert_newline_before_node(stmt, prev_stmt) { + f.out.writeln('') + } + f.stmt(stmt) + prev_stmt = stmt + } + f.indent-- +} + +pub fn (mut f Fmt) stmt(node ast.Stmt) { + if f.is_debug { + eprintln('stmt: ${node.pos:-42} | node: ${node.type_name():-20}') + } + match node { + ast.NodeError {} + ast.EmptyStmt {} + ast.AsmStmt { + f.asm_stmt(node) + } + ast.AssertStmt { + f.assert_stmt(node) + } + ast.AssignStmt { + f.assign_stmt(node) + } + ast.Block { + f.block(node) + } + ast.BranchStmt { + f.branch_stmt(node) + } + ast.CompFor { + f.comp_for(node) + } + ast.ConstDecl { + f.const_decl(node) + } + ast.DeferStmt { + f.defer_stmt(node) + } + ast.EnumDecl { + f.enum_decl(node) + } + ast.ExprStmt { + f.expr_stmt(node) + } + ast.FnDecl { + f.fn_decl(node) + } + ast.ForCStmt { + f.for_c_stmt(node) + } + ast.ForInStmt { + f.for_in_stmt(node) + } + ast.ForStmt { + f.for_stmt(node) + } + ast.GlobalDecl { + f.global_decl(node) + } + ast.GotoLabel { + f.goto_label(node) + } + ast.GotoStmt { + f.goto_stmt(node) + } + ast.HashStmt { + f.hash_stmt(node) + } + ast.Import { + // Imports are handled after the file is formatted, to automatically add necessary modules + // Just remember the position of the imports for now + f.import_pos = f.out.len + } + ast.InterfaceDecl { + f.interface_decl(node) + } + ast.Module { + f.mod(node) + } + ast.Return { + f.return_stmt(node) + } + ast.SqlStmt { + f.sql_stmt(node) + } + ast.StructDecl { + f.struct_decl(node) + } + ast.TypeDecl { + f.type_decl(node) + } + } +} + +fn stmt_is_single_line(stmt ast.Stmt) bool { + return match stmt { + ast.ExprStmt, ast.AssertStmt { expr_is_single_line(stmt.expr) } + ast.Return, ast.AssignStmt, ast.BranchStmt { true } + else { false } + } +} + +//=== General Expr-related methods and helpers ===// + +pub fn (mut f Fmt) expr(node ast.Expr) { + if f.is_debug { + eprintln('expr: ${node.position():-42} | node: ${node.type_name():-20} | $node.str()') + } + match mut node { + ast.NodeError {} + ast.EmptyExpr {} + ast.AnonFn { + f.fn_decl(node.decl) + } + ast.ArrayDecompose { + f.array_decompose(node) + } + ast.ArrayInit { + f.array_init(node) + } + ast.AsCast { + f.as_cast(node) + } + ast.Assoc { + f.assoc(node) + } + ast.AtExpr { + f.at_expr(node) + } + ast.BoolLiteral { + f.write(node.val.str()) + } + ast.CallExpr { + f.call_expr(node) + } + ast.CastExpr { + f.cast_expr(node) + } + ast.ChanInit { + f.chan_init(mut node) + } + ast.CharLiteral { + f.write('`$node.val`') + } + ast.Comment { + f.comment(node, inline: true) + } + ast.ComptimeCall { + f.comptime_call(node) + } + ast.ComptimeSelector { + f.comptime_selector(node) + } + ast.ConcatExpr { + f.concat_expr(node) + } + ast.CTempVar { + eprintln('ast.CTempVar of $node.orig.str() should be generated/used only in cgen') + } + ast.DumpExpr { + f.dump_expr(node) + } + ast.EnumVal { + f.enum_val(node) + } + ast.FloatLiteral { + f.write(node.val) + } + ast.GoExpr { + f.go_expr(node) + } + ast.Ident { + f.ident(node) + } + ast.IfExpr { + f.if_expr(node) + } + ast.IfGuardExpr { + f.if_guard_expr(node) + } + ast.IndexExpr { + f.index_expr(node) + } + ast.InfixExpr { + f.infix_expr(node) + } + ast.IntegerLiteral { + f.write(node.val) + } + ast.Likely { + f.likely(node) + } + ast.LockExpr { + f.lock_expr(node) + } + ast.MapInit { + f.map_init(node) + } + ast.MatchExpr { + f.match_expr(node) + } + ast.None { + f.write('none') + } + ast.OffsetOf { + f.offset_of(node) + } + ast.OrExpr { + // shouldn't happen, an or expression is always linked to a call expr or index expr + panic('fmt: OrExpr should be linked to ast.CallExpr or ast.IndexExpr') + } + ast.ParExpr { + f.par_expr(node) + } + ast.PostfixExpr { + f.postfix_expr(node) + } + ast.PrefixExpr { + f.prefix_expr(node) + } + ast.RangeExpr { + f.range_expr(node) + } + ast.SelectExpr { + f.select_expr(node) + } + ast.SelectorExpr { + f.selector_expr(node) + } + ast.SizeOf { + f.size_of(node) + } + ast.IsRefType { + f.is_ref_type(node) + } + ast.SqlExpr { + f.sql_expr(node) + } + ast.StringLiteral { + f.string_literal(node) + } + ast.StringInterLiteral { + f.string_inter_literal(node) + } + ast.StructInit { + f.struct_init(node) + } + ast.TypeNode { + f.type_expr(node) + } + ast.TypeOf { + f.type_of(node) + } + ast.UnsafeExpr { + f.unsafe_expr(node) + } + } +} + +fn expr_is_single_line(expr ast.Expr) bool { + match expr { + ast.Comment, ast.IfExpr, ast.MapInit, ast.MatchExpr { + return false + } + ast.AnonFn { + if !expr.decl.no_body { + return false + } + } + ast.StructInit { + if !expr.is_short && (expr.fields.len > 0 || expr.pre_comments.len > 0) { + return false + } + } + ast.CallExpr { + if expr.or_block.stmts.len > 1 { + return false + } + } + ast.ArrayInit { + if expr.exprs.len > 0 { + return expr_is_single_line(expr.exprs[0]) + } + } + ast.ConcatExpr { + for e in expr.vals { + if !expr_is_single_line(e) { + return false + } + } + } + ast.StringLiteral { + return expr.pos.line_nr == expr.pos.last_line + } + else {} + } + return true +} + +//=== Specific Stmt methods ===// + +pub fn (mut f Fmt) assert_stmt(node ast.AssertStmt) { + f.write('assert ') + if node.expr is ast.ParExpr { + if node.expr.expr is ast.InfixExpr { + infix := node.expr.expr + f.expr(infix) + f.writeln('') + return + } + } + f.expr(node.expr) + f.writeln('') +} + +pub fn (mut f Fmt) assign_stmt(node ast.AssignStmt) { + f.comments(node.comments) + for i, left in node.left { + f.expr(left) + if i < node.left.len - 1 { + f.write(', ') + } + } + f.is_assign = true + f.write(' $node.op.str() ') + for i, val in node.right { + f.prefix_expr_cast_expr(val) + if i < node.right.len - 1 { + f.write(', ') + } + } + f.comments(node.end_comments, has_nl: false, inline: true, level: .keep) + if !f.single_line_if { + f.writeln('') + } + f.is_assign = false +} + +pub fn (mut f Fmt) block(node ast.Block) { + if node.is_unsafe { + f.write('unsafe ') + } + f.write('{') + if node.stmts.len > 0 || node.pos.line_nr < node.pos.last_line { + f.writeln('') + f.stmts(node.stmts) + } + f.writeln('}') +} + +pub fn (mut f Fmt) branch_stmt(node ast.BranchStmt) { + f.writeln(node.str()) +} + +pub fn (mut f Fmt) comp_for(node ast.CompFor) { + typ := f.no_cur_mod(f.table.type_to_str_using_aliases(node.typ, f.mod2alias)) + f.write('\$for $node.val_var in ${typ}.$node.kind.str() {') + if node.stmts.len > 0 || node.pos.line_nr < node.pos.last_line { + f.writeln('') + f.stmts(node.stmts) + } + f.writeln('}') +} + +struct ConstAlignInfo { +mut: + max int + last_idx int +} + +pub fn (mut f Fmt) const_decl(node ast.ConstDecl) { + if node.is_pub { + f.write('pub ') + } + if node.fields.len == 0 && node.pos.line_nr == node.pos.last_line { + f.writeln('const ()\n') + return + } + f.inside_const = true + defer { + f.inside_const = false + } + f.write('const ') + mut align_infos := []ConstAlignInfo{} + if node.is_block { + f.writeln('(') + mut info := ConstAlignInfo{} + for i, field in node.fields { + if field.name.len > info.max { + info.max = field.name.len + } + if !expr_is_single_line(field.expr) { + info.last_idx = i + align_infos << info + info = ConstAlignInfo{} + } + } + info.last_idx = node.fields.len + align_infos << info + f.indent++ + } else { + align_infos << ConstAlignInfo{0, 1} + } + mut prev_field := if node.fields.len > 0 { + ast.Node(node.fields[0]) + } else { + ast.Node(ast.NodeError{}) + } + mut align_idx := 0 + for i, field in node.fields { + if i > align_infos[align_idx].last_idx { + align_idx++ + } + if field.comments.len > 0 { + if f.should_insert_newline_before_node(ast.Expr(field.comments[0]), prev_field) { + f.writeln('') + } + f.comments(field.comments, inline: true) + prev_field = ast.Expr(field.comments.last()) + } + if node.is_block && f.should_insert_newline_before_node(field, prev_field) { + f.writeln('') + } + name := field.name.after('.') + f.write('$name ') + f.write(strings.repeat(` `, align_infos[align_idx].max - field.name.len)) + f.write('= ') + f.expr(field.expr) + f.writeln('') + prev_field = field + } + f.comments_after_last_field(node.end_comments) + if node.is_block { + f.indent-- + f.writeln(')\n') + } else { + f.writeln('') + } +} + +pub fn (mut f Fmt) defer_stmt(node ast.DeferStmt) { + f.write('defer {') + if node.stmts.len > 0 || node.pos.line_nr < node.pos.last_line { + f.writeln('') + f.stmts(node.stmts) + } + f.writeln('}') +} + +pub fn (mut f Fmt) expr_stmt(node ast.ExprStmt) { + f.comments(node.comments) + f.expr(node.expr) + if !f.single_line_if { + f.writeln('') + } +} + +pub fn (mut f Fmt) enum_decl(node ast.EnumDecl) { + f.attrs(node.attrs) + if node.is_pub { + f.write('pub ') + } + name := node.name.after('.') + if node.fields.len == 0 && node.pos.line_nr == node.pos.last_line { + f.writeln('enum $name {}\n') + return + } + f.writeln('enum $name {') + f.comments(node.comments, inline: true, level: .indent) + for field in node.fields { + f.write('\t$field.name') + if field.has_expr { + f.write(' = ') + f.expr(field.expr) + } + f.comments(field.comments, inline: true, has_nl: false, level: .indent) + f.writeln('') + f.comments(field.next_comments, inline: false, has_nl: true, level: .indent) + } + f.writeln('}\n') +} + +pub fn (mut f Fmt) fn_decl(node ast.FnDecl) { + f.attrs(node.attrs) + f.write(node.stringify(f.table, f.cur_mod, f.mod2alias)) // `Expr` instead of `ast.Expr` in mod ast + if node.language == .v { + if !node.no_body { + f.write(' {') + if node.stmts.len > 0 || node.pos.line_nr < node.pos.last_line { + f.writeln('') + f.stmts(node.stmts) + } + f.write('}') + } + if !node.is_anon { + f.writeln('') + } + } else { + f.writeln('') + } + // Mark all function's used type so that they are not removed from imports + for arg in node.params { + f.mark_types_import_as_used(arg.typ) + } + f.mark_types_import_as_used(node.return_type) +} + +pub fn (mut f Fmt) for_c_stmt(node ast.ForCStmt) { + if node.label.len > 0 { + f.write('$node.label: ') + } + f.write('for ') + if node.has_init { + f.single_line_if = true // to keep all for ;; exprs on the same line + f.stmt(node.init) + f.single_line_if = false + } + f.write('; ') + f.expr(node.cond) + f.write('; ') + f.stmt(node.inc) + f.remove_new_line() + f.write(' {') + if node.stmts.len > 0 || node.pos.line_nr < node.pos.last_line { + f.writeln('') + f.stmts(node.stmts) + } + f.writeln('}') +} + +pub fn (mut f Fmt) for_in_stmt(node ast.ForInStmt) { + if node.label.len > 0 { + f.write('$node.label: ') + } + f.write('for ') + if node.key_var != '' { + f.write(node.key_var) + } + if node.val_var != '' { + if node.key_var != '' { + f.write(', ') + } + if node.val_is_mut { + f.write('mut ') + } + f.write(node.val_var) + } + f.write(' in ') + f.expr(node.cond) + if node.is_range { + f.write(' .. ') + f.expr(node.high) + } + f.write(' {') + if node.stmts.len > 0 || node.pos.line_nr < node.pos.last_line { + f.writeln('') + f.stmts(node.stmts) + } + f.writeln('}') +} + +pub fn (mut f Fmt) for_stmt(node ast.ForStmt) { + if node.label.len > 0 { + f.write('$node.label: ') + } + f.write('for ') + f.expr(node.cond) + if !node.is_inf { + f.write(' ') + } + f.write('{') + if node.stmts.len > 0 || node.pos.line_nr < node.pos.last_line { + f.writeln('') + f.stmts(node.stmts) + } + f.writeln('}') +} + +pub fn (mut f Fmt) global_decl(node ast.GlobalDecl) { + if node.fields.len == 0 && node.pos.line_nr == node.pos.last_line { + f.writeln('__global ()') + return + } + f.write('__global ') + mut max := 0 + // mut has_assign := false + if node.is_block { + f.writeln('(') + f.indent++ + for field in node.fields { + if field.name.len > max { + max = field.name.len + } + // if field.has_expr { + // has_assign = true + //} + } + } + for field in node.fields { + f.comments(field.comments, inline: true) + f.write('$field.name ') + f.write(strings.repeat(` `, max - field.name.len)) + if field.has_expr { + f.write('= ') + f.expr(field.expr) + } else { + f.write('${f.table.type_to_str_using_aliases(field.typ, f.mod2alias)}') + } + if node.is_block { + f.writeln('') + } + f.mark_types_import_as_used(field.typ) + } + f.comments_after_last_field(node.end_comments) + if node.is_block { + f.indent-- + f.writeln(')') + } else { + f.writeln('') + } +} + +pub fn (mut f Fmt) go_expr(node ast.GoExpr) { + f.write('go ') + f.expr(node.call_expr) +} + +pub fn (mut f Fmt) goto_label(node ast.GotoLabel) { + f.writeln('$node.name:') +} + +pub fn (mut f Fmt) goto_stmt(node ast.GotoStmt) { + f.writeln('goto $node.name') +} + +pub fn (mut f Fmt) hash_stmt(node ast.HashStmt) { + f.writeln('#$node.val') +} + +pub fn (mut f Fmt) interface_decl(node ast.InterfaceDecl) { + if node.is_pub { + f.write('pub ') + } + f.write('interface ') + f.write_language_prefix(node.language) + name := node.name.after('.') // strip prepended module + f.write(name) + f.write_generic_types(node.generic_types) + f.write(' {') + if node.fields.len > 0 || node.methods.len > 0 || node.pos.line_nr < node.pos.last_line { + f.writeln('') + } + f.comments_before_field(node.pre_comments) + for iface in node.ifaces { + f.write('\t$iface.name') + f.comments(iface.comments, inline: true, has_nl: false, level: .indent) + f.writeln('') + } + for i, field in node.fields { + if i == node.mut_pos { + f.writeln('mut:') + } + // TODO: alignment, comments, etc. + mut ft := f.no_cur_mod(f.table.type_to_str_using_aliases(field.typ, f.mod2alias)) + f.writeln('\t$field.name $ft') + f.mark_types_import_as_used(field.typ) + } + for method in node.methods { + f.write('\t') + f.write(method.stringify(f.table, f.cur_mod, f.mod2alias).after('fn ')) + f.comments(method.comments, inline: true, has_nl: false, level: .indent) + f.writeln('') + f.comments(method.next_comments, inline: false, has_nl: true, level: .indent) + for param in method.params { + f.mark_types_import_as_used(param.typ) + } + f.mark_types_import_as_used(method.return_type) + } + f.writeln('}\n') +} + +pub fn (mut f Fmt) mod(mod ast.Module) { + f.set_current_module_name(mod.name) + if mod.is_skipped { + return + } + f.attrs(mod.attrs) + f.writeln('module $mod.short_name\n') + if f.import_pos == 0 { + f.import_pos = f.out.len + } +} + +pub fn (mut f Fmt) return_stmt(node ast.Return) { + f.comments(node.comments) + f.write('return') + if node.exprs.len > 0 { + f.write(' ') + // Loop over all return values. In normal returns this will only run once. + for i, expr in node.exprs { + f.expr(expr) + if i < node.exprs.len - 1 { + f.write(', ') + } + } + } + f.writeln('') +} + +pub fn (mut f Fmt) sql_stmt(node ast.SqlStmt) { + f.write('sql ') + f.expr(node.db_expr) + f.writeln(' {') + + for line in node.lines { + f.sql_stmt_line(line) + } + + f.writeln('}') +} + +pub fn (mut f Fmt) sql_stmt_line(node ast.SqlStmtLine) { + table_name := util.strip_mod_name(f.table.get_type_symbol(node.table_expr.typ).name) + f.mark_types_import_as_used(node.table_expr.typ) + f.write('\t') + match node.kind { + .insert { + f.writeln('insert $node.object_var_name into $table_name') + } + .update { + f.write('update $table_name set ') + for i, col in node.updated_columns { + f.write('$col = ') + f.expr(node.update_exprs[i]) + if i < node.updated_columns.len - 1 { + f.write(', ') + } else { + f.write(' ') + } + f.wrap_long_line(3, true) + } + f.write('where ') + f.expr(node.where_expr) + f.writeln('') + } + .delete { + f.write('delete from $table_name where ') + f.expr(node.where_expr) + f.writeln('') + } + .create { + f.writeln('create table $table_name') + } + .drop { + f.writeln('drop table $table_name') + } + } +} + +pub fn (mut f Fmt) type_decl(node ast.TypeDecl) { + match node { + ast.AliasTypeDecl { f.alias_type_decl(node) } + ast.FnTypeDecl { f.fn_type_decl(node) } + ast.SumTypeDecl { f.sum_type_decl(node) } + } + f.writeln('') +} + +pub fn (mut f Fmt) alias_type_decl(node ast.AliasTypeDecl) { + if node.is_pub { + f.write('pub ') + } + ptype := f.table.type_to_str_using_aliases(node.parent_type, f.mod2alias) + f.write('type $node.name = $ptype') + + f.comments(node.comments, has_nl: false) +} + +pub fn (mut f Fmt) fn_type_decl(node ast.FnTypeDecl) { + if node.is_pub { + f.write('pub ') + } + typ_sym := f.table.get_type_symbol(node.typ) + fn_typ_info := typ_sym.info as ast.FnType + fn_info := fn_typ_info.func + fn_name := f.no_cur_mod(node.name) + f.write('type $fn_name = fn (') + for i, arg in fn_info.params { + if arg.is_mut { + f.write(arg.typ.share().str() + ' ') + } + f.write(arg.name) + mut s := f.no_cur_mod(f.table.type_to_str_using_aliases(arg.typ, f.mod2alias)) + if arg.is_mut { + if s.starts_with('&') { + s = s[1..] + } + } + is_last_arg := i == fn_info.params.len - 1 + should_add_type := true || is_last_arg + || fn_info.params[i + 1].typ != arg.typ + || (fn_info.is_variadic && i == fn_info.params.len - 2) + if should_add_type { + ns := if arg.name == '' { '' } else { ' ' } + if fn_info.is_variadic && is_last_arg { + f.write(ns + '...' + s) + } else { + f.write(ns + s) + } + } + if !is_last_arg { + f.write(', ') + } + } + f.write(')') + if fn_info.return_type.idx() != ast.void_type_idx { + ret_str := f.no_cur_mod(f.table.type_to_str_using_aliases(fn_info.return_type, + f.mod2alias)) + f.write(' $ret_str') + } else if fn_info.return_type.has_flag(.optional) { + f.write(' ?') + } + + f.comments(node.comments, has_nl: false) + f.writeln('') +} + +pub fn (mut f Fmt) sum_type_decl(node ast.SumTypeDecl) { + if node.is_pub { + f.write('pub ') + } + f.write('type $node.name') + f.write_generic_types(node.generic_types) + f.write(' = ') + + mut sum_type_names := []string{} + for t in node.variants { + sum_type_names << f.table.type_to_str_using_aliases(t.typ, f.mod2alias) + } + sum_type_names.sort() + for i, name in sum_type_names { + f.write(name) + if i < sum_type_names.len - 1 { + f.write(' | ') + } + if i < sum_type_names.len - 1 { + f.wrap_long_line(3, true) + } + } + + f.comments(node.comments, has_nl: false) +} + +//=== Specific Expr methods ===// + +pub fn (mut f Fmt) array_decompose(node ast.ArrayDecompose) { + f.write('...') + f.expr(node.expr) +} + +pub fn (mut f Fmt) array_init(node ast.ArrayInit) { + if node.exprs.len == 0 && node.typ != 0 && node.typ != ast.void_type { + // `x := []string{}` + f.mark_types_import_as_used(node.typ) + f.write(f.table.type_to_str_using_aliases(node.typ, f.mod2alias)) + f.write('{') + if node.has_len { + f.write('len: ') + f.expr(node.len_expr) + if node.has_cap || node.has_default { + f.write(', ') + } + } + if node.has_cap { + f.write('cap: ') + f.expr(node.cap_expr) + if node.has_default { + f.write(', ') + } + } + if node.has_default { + f.write('init: ') + f.expr(node.default_expr) + } + f.write('}') + return + } + // `[1,2,3]` + f.write('[') + mut inc_indent := false + mut last_line_nr := node.pos.line_nr // to have the same newlines between array elements + f.array_init_depth++ + if node.pre_cmnts.len > 0 { + if node.pre_cmnts[0].pos.line_nr > last_line_nr { + f.writeln('') + } + } + for i, c in node.pre_cmnts { + if i < node.pre_cmnts.len - 1 { + if c.pos.last_line < node.pre_cmnts[i + 1].pos.line_nr { + f.comment(c, level: .indent) + f.writeln('') + } else { + f.comment(c, level: .indent, iembed: true) + f.write(' ') + } + } else { + next_line := if node.exprs.len > 0 { + node.exprs[0].position().line_nr + } else { + node.pos.last_line + } + if c.pos.last_line < next_line { + f.comment(c, level: .indent) + if node.exprs.len == 0 { + f.writeln('') + } + } else { + f.comment(c, level: .indent, iembed: true) + if node.exprs.len > 0 { + f.write(' ') + } + } + } + last_line_nr = c.pos.last_line + } + mut set_comma := false + for i, expr in node.exprs { + pos := expr.position() + if i == 0 { + if f.array_init_depth > f.array_init_break.len { + f.array_init_break << pos.line_nr > last_line_nr + } + } + line_break := f.array_init_break[f.array_init_depth - 1] + mut penalty := if line_break { 0 } else { 4 } + if penalty > 0 { + if i == 0 || should_decrease_arr_penalty(node.exprs[i - 1]) { + penalty-- + } + if should_decrease_arr_penalty(expr) { + penalty-- + } + } + mut is_new_line := f.wrap_long_line(penalty, !inc_indent) + if is_new_line && !inc_indent { + f.indent++ + inc_indent = true + } + single_line_expr := expr_is_single_line(expr) + if single_line_expr { + estr := f.node_str(expr) + if !is_new_line && !f.buffering && f.line_len + estr.len > fmt.max_len.last() { + f.writeln('') + is_new_line = true + if !inc_indent { + f.indent++ + inc_indent = true + } + } + if !is_new_line && i > 0 { + f.write(' ') + } + f.write(estr) + } else { + if !is_new_line && i > 0 { + f.write(' ') + } + f.expr(expr) + } + mut last_comment_was_inline := false + mut has_comments := node.ecmnts[i].len > 0 + if i < node.ecmnts.len && has_comments { + expr_pos := expr.position() + for icmt, cmt in node.ecmnts[i] { + if !set_comma && cmt.pos.pos > expr_pos.pos + expr_pos.len + 2 { + if icmt > 0 { + if last_comment_was_inline { + f.write(',') + set_comma = true + } + } else { + f.write(',') // first comment needs a comma + set_comma = true + } + } + mut next_pos := expr_pos + if i + 1 < node.exprs.len { + next_pos = node.exprs[i + 1].position() + } + if cmt.pos.line_nr > expr_pos.last_line { + embed := i + 1 < node.exprs.len && next_pos.line_nr == cmt.pos.last_line + f.writeln('') + f.comment(cmt, iembed: embed) + } else { + if cmt.is_inline { + f.write(' ') + f.comment(cmt, iembed: true) + if cmt.pos.line_nr == expr_pos.last_line && cmt.pos.pos < expr_pos.pos { + f.write(',') + set_comma = true + } else { + if !cmt.is_inline { + // a // comment, transformed to a /**/ one, needs a comma too + f.write(',') + set_comma = true + } + } + } else { + f.write(', ') + f.comment(cmt, iembed: false) + set_comma = true + } + } + last_comment_was_inline = cmt.is_inline + } + } + mut put_comma := !set_comma + if has_comments && !last_comment_was_inline { + put_comma = false + } + if i == node.exprs.len - 1 { + if is_new_line { + if put_comma { + f.write(',') + } + f.writeln('') + } + } else if put_comma { + f.write(',') + } + last_line_nr = pos.last_line + set_comma = false + } + f.array_init_depth-- + if f.array_init_depth == 0 { + f.array_init_break = [] + } + if inc_indent { + f.indent-- + } + f.write(']') + // `[100]byte` + if node.is_fixed { + if node.has_val { + f.write('!') + return + } + f.write(f.table.type_to_str_using_aliases(node.elem_type, f.mod2alias)) + if node.has_default { + f.write('{init: ') + f.expr(node.default_expr) + f.write('}') + } else { + f.write('{}') + } + } +} + +fn should_decrease_arr_penalty(e ast.Expr) bool { + if e is ast.ArrayInit || e is ast.StructInit || e is ast.MapInit || e is ast.CallExpr { + return true + } + return false +} + +pub fn (mut f Fmt) as_cast(node ast.AsCast) { + f.mark_types_import_as_used(node.typ) + type_str := f.table.type_to_str_using_aliases(node.typ, f.mod2alias) + f.expr(node.expr) + f.write(' as $type_str') +} + +pub fn (mut f Fmt) assoc(node ast.Assoc) { + f.writeln('{') + f.indent++ + f.writeln('...$node.var_name') + for i, field in node.fields { + f.write('$field: ') + f.expr(node.exprs[i]) + f.writeln('') + } + f.indent-- + f.write('}') +} + +pub fn (mut f Fmt) at_expr(node ast.AtExpr) { + f.write(node.name) +} + +pub fn (mut f Fmt) call_expr(node ast.CallExpr) { + for arg in node.args { + f.comments(arg.comments) + } + if node.is_method { + if node.name in ['map', 'filter'] { + f.inside_lambda = true + defer { + f.inside_lambda = false + } + } + if node.left is ast.Ident { + // `time.now()` without `time imported` is processed as a method call with `time` being + // a `node.left` expression. Import `time` automatically. + // TODO fetch all available modules + if node.left.name in ['time', 'os', 'strings', 'math', 'json', 'base64'] { + f.file.imports << ast.Import{ + mod: node.left.name + alias: node.left.name + } + } + } + f.expr(node.left) + f.write('.' + node.name) + } else { + f.write_language_prefix(node.language) + if node.left is ast.AnonFn { + f.fn_decl(node.left.decl) + } else if node.language != .v { + f.write('${node.name.after_char(`.`)}') + } else { + mut name := f.short_module(node.name) + f.mark_import_as_used(name) + f.write('$name') + } + } + if node.mod == '' && node.name == '' { + f.write(node.left.str()) + } + f.write_generic_call_if_require(node) + f.write('(') + f.call_args(node.args) + f.write(')') + f.or_expr(node.or_block) + f.comments(node.comments, has_nl: false) +} + +fn (mut f Fmt) write_generic_call_if_require(node ast.CallExpr) { + if node.concrete_types.len > 0 { + f.write('<') + for i, concrete_type in node.concrete_types { + f.write(f.table.type_to_str_using_aliases(concrete_type, f.mod2alias)) + if i != node.concrete_types.len - 1 { + f.write(', ') + } + } + // avoid `>` => ` >` + if f.out.last_n(1) == '>' { + f.write(' ') + } + f.write('>') + } +} + +pub fn (mut f Fmt) call_args(args []ast.CallArg) { + f.single_line_fields = true + old_short_arg_state := f.use_short_fn_args + f.use_short_fn_args = false + defer { + f.single_line_fields = false + f.use_short_fn_args = old_short_arg_state + } + for i, arg in args { + if i == args.len - 1 && arg.expr is ast.StructInit { + if arg.expr.typ == ast.void_type { + f.use_short_fn_args = true + } + } + if arg.is_mut { + f.write(arg.share.str() + ' ') + } + if i > 0 && !f.single_line_if { + f.wrap_long_line(3, true) + } + f.expr(arg.expr) + if i < args.len - 1 { + f.write(', ') + } + } +} + +pub fn (mut f Fmt) cast_expr(node ast.CastExpr) { + f.write(f.table.type_to_str_using_aliases(node.typ, f.mod2alias) + '(') + f.mark_types_import_as_used(node.typ) + f.expr(node.expr) + if node.has_arg { + f.write(', ') + f.expr(node.arg) + } + f.write(')') +} + +pub fn (mut f Fmt) chan_init(mut node ast.ChanInit) { + info := f.table.get_type_symbol(node.typ).chan_info() + if node.elem_type == 0 && node.typ > 0 { + node.elem_type = info.elem_type + } + is_mut := info.is_mut + el_typ := if is_mut { + node.elem_type.set_nr_muls(node.elem_type.nr_muls() - 1) + } else { + node.elem_type + } + f.write('chan ') + if is_mut { + f.write('mut ') + } + f.write(f.table.type_to_str_using_aliases(el_typ, f.mod2alias)) + f.write('{') + if node.has_cap { + f.write('cap: ') + f.expr(node.cap_expr) + } + f.write('}') +} + +pub fn (mut f Fmt) comptime_call(node ast.ComptimeCall) { + if node.is_vweb { + if node.method_name == 'html' { + f.write('\$vweb.html()') + } else { + f.write("\$tmpl('$node.args_var')") + } + } else { + if node.is_embed { + f.write("\$embed_file('$node.embed_file.rpath')") + } else if node.is_env { + f.write("\$env('$node.args_var')") + } else if node.is_pkgconfig { + f.write("\$pkgconfig('$node.args_var')") + } else { + inner_args := if node.args_var != '' { + node.args_var + } else { + node.args.map(it.str()).join(', ') + } + method_expr := if node.has_parens { + '(${node.method_name}($inner_args))' + } else { + '${node.method_name}($inner_args)' + } + f.write('${node.left}.$$method_expr') + } + } +} + +pub fn (mut f Fmt) comptime_selector(node ast.ComptimeSelector) { + f.write('${node.left}.\$($node.field_expr)') +} + +pub fn (mut f Fmt) concat_expr(node ast.ConcatExpr) { + for i, val in node.vals { + if i != 0 { + f.write(', ') + } + f.expr(val) + } +} + +pub fn (mut f Fmt) dump_expr(node ast.DumpExpr) { + f.write('dump(') + f.expr(node.expr) + f.write(')') +} + +pub fn (mut f Fmt) enum_val(node ast.EnumVal) { + name := f.short_module(node.enum_name) + f.write(name + '.' + node.val) + f.mark_import_as_used(name) +} + +pub fn (mut f Fmt) ident(node ast.Ident) { + if mut node.info is ast.IdentVar { + if node.info.is_mut { + f.write(node.info.share.str() + ' ') + } + var_info := node.var_info() + if var_info.is_static { + f.write('static ') + } + } + f.write_language_prefix(node.language) + if node.name == 'it' && f.it_name != '' && !f.inside_lambda { // allow `it` in lambdas + f.write(f.it_name) + } else if node.kind == .blank_ident { + f.write('_') + } else { + // Force usage of full path to const in the same module: + // `println(minute)` => `println(time.minute)` + // This makes it clear that a module const is being used + // (since V's consts are no longer ALL_CAP). + // ^^^ except for `main`, where consts are allowed to not have a `main.` prefix. + if !node.name.contains('.') && !f.inside_const { + mod := f.cur_mod + full_name := mod + '.' + node.name + if obj := f.file.global_scope.find(full_name) { + if obj is ast.ConstField { + // "v.fmt.foo" => "fmt.foo" + vals := full_name.split('.') + mod_prefix := vals[vals.len - 2] + const_name := vals[vals.len - 1] + if mod_prefix == 'main' { + f.write(const_name) + return + } else { + short := mod_prefix + '.' + const_name + f.write(short) + f.mark_import_as_used(short) + return + } + } + } + } + name := f.short_module(node.name) + f.write(name) + f.mark_import_as_used(name) + } +} + +pub fn (mut f Fmt) if_expr(node ast.IfExpr) { + dollar := if node.is_comptime { '$' } else { '' } + mut is_ternary := node.branches.len == 2 && node.has_else + && branch_is_single_line(node.branches[0]) && branch_is_single_line(node.branches[1]) + && (node.is_expr || f.is_assign || f.is_struct_init || f.single_line_fields) + f.single_line_if = is_ternary + start_pos := f.out.len + start_len := f.line_len + for { + for i, branch in node.branches { + if i == 0 { + // first `if` + f.comments(branch.comments) + } else { + // `else`, close previous branch + if branch.comments.len > 0 { + f.writeln('}') + f.comments(branch.comments) + } else { + f.write('} ') + } + f.write('${dollar}else ') + } + if i < node.branches.len - 1 || !node.has_else { + f.write('${dollar}if ') + cur_pos := f.out.len + f.expr(branch.cond) + cond_len := f.out.len - cur_pos + is_cond_wrapped := cond_len > 0 + && (branch.cond is ast.IfGuardExpr || branch.cond is ast.CallExpr) + && f.out.last_n(cond_len).contains('\n') + if is_cond_wrapped { + f.writeln('') + } else { + f.write(' ') + } + } + f.write('{') + if is_ternary { + f.write(' ') + } else { + f.writeln('') + } + f.stmts(branch.stmts) + if is_ternary { + f.write(' ') + } + } + // When a single line if is really long, write it again as multiline, + // except it is part of an InfixExpr. + if is_ternary && f.line_len > fmt.max_len.last() && !f.buffering { + is_ternary = false + f.single_line_if = false + f.out.go_back_to(start_pos) + f.line_len = start_len + f.empty_line = start_len == 0 + continue + } + break + } + f.write('}') + f.single_line_if = false + if node.post_comments.len > 0 { + f.writeln('') + f.comments(node.post_comments, + has_nl: false + prev_line: node.branches.last().body_pos.last_line + ) + } +} + +fn branch_is_single_line(b ast.IfBranch) bool { + if b.stmts.len == 1 && b.comments.len == 0 && stmt_is_single_line(b.stmts[0]) { + return true + } + return false +} + +pub fn (mut f Fmt) if_guard_expr(node ast.IfGuardExpr) { + f.write(node.var_name + ' := ') + f.expr(node.expr) +} + +pub fn (mut f Fmt) index_expr(node ast.IndexExpr) { + f.expr(node.left) + f.write('[') + f.expr(node.index) + f.write(']') + if node.or_expr.kind != .absent { + f.or_expr(node.or_expr) + } +} + +pub fn (mut f Fmt) infix_expr(node ast.InfixExpr) { + buffering_save := f.buffering + if !f.buffering && node.op in [.logical_or, .and, .plus] { + f.buffering = true + } + is_assign_save := f.is_assign + if node.op == .left_shift { + f.is_assign = true // To write ternary if on a single line + } + start_pos := f.out.len + start_len := f.line_len + f.expr(node.left) + is_one_val_array_init := node.op in [.key_in, .not_in] && node.right is ast.ArrayInit + && (node.right as ast.ArrayInit).exprs.len == 1 + if is_one_val_array_init { + // `var in [val]` => `var == val` + op := if node.op == .key_in { ' == ' } else { ' != ' } + f.write(op) + } else { + f.write(' $node.op.str() ') + } + if is_one_val_array_init { + // `var in [val]` => `var == val` + f.expr((node.right as ast.ArrayInit).exprs[0]) + } else { + f.expr(node.right) + } + if !buffering_save && f.buffering { + f.buffering = false + if !f.single_line_if && f.line_len > fmt.max_len.last() { + is_cond := node.op in [.and, .logical_or] + f.wrap_infix(start_pos, start_len, is_cond) + } + } + f.is_assign = is_assign_save + f.or_expr(node.or_block) +} + +pub fn (mut f Fmt) wrap_infix(start_pos int, start_len int, is_cond bool) { + cut_span := f.out.len - start_pos + infix_str := f.out.cut_last(cut_span) + if !infix_str.contains_any_substr(['&&', '||', '+']) { + f.write(infix_str) + return + } + f.line_len = start_len + if start_len == 0 { + f.empty_line = true + } + conditions, penalties := split_up_infix(infix_str, false, is_cond) + f.write_splitted_infix(conditions, penalties, false, is_cond) +} + +fn split_up_infix(infix_str string, ignore_paren bool, is_cond_infix bool) ([]string, []int) { + mut conditions := [''] + mut penalties := [5] + or_pen := if infix_str.contains('&&') { 3 } else { 5 } + parts := infix_str.split(' ') + mut inside_paren := false + mut ind := 0 + for p in parts { + if is_cond_infix && p in ['&&', '||'] { + if inside_paren { + conditions[ind] += '$p ' + } else { + pen := if p == '||' { or_pen } else { 5 } + penalties << pen + conditions << '$p ' + ind++ + } + } else if !is_cond_infix && p == '+' { + penalties << 5 + conditions[ind] += '$p ' + conditions << '' + ind++ + } else { + conditions[ind] += '$p ' + if ignore_paren { + continue + } + if p.starts_with('(') { + inside_paren = true + } else if p.ends_with(')') { + inside_paren = false + } + } + } + return conditions, penalties +} + +fn (mut f Fmt) write_splitted_infix(conditions []string, penalties []int, ignore_paren bool, is_cond bool) { + for i, cnd in conditions { + c := cnd.trim_space() + if f.line_len + c.len < fmt.max_len[penalties[i]] { + if (i > 0 && i < conditions.len) || (ignore_paren && i == 0 && c.len > 5 && c[3] == `(`) { + f.write(' ') + } + f.write(c) + } else { + is_paren_expr := (c[0] == `(` || (c.len > 5 && c[3] == `(`)) && c.ends_with(')') + final_len := ((f.indent + 1) * 4) + c.len + if final_len > fmt.max_len.last() && is_paren_expr { + conds, pens := split_up_infix(c, true, is_cond) + f.write_splitted_infix(conds, pens, true, is_cond) + continue + } + if i == 0 { + f.remove_new_line() + } + f.writeln('') + f.indent++ + f.write(c) + f.indent-- + } + } +} + +pub fn (mut f Fmt) likely(node ast.Likely) { + if node.is_likely { + f.write('_likely_') + } else { + f.write('_unlikely_') + } + f.write('(') + f.expr(node.expr) + f.write(')') +} + +pub fn (mut f Fmt) lock_expr(node ast.LockExpr) { + mut num_locked := 0 + mut num_rlocked := 0 + for is_rlock in node.is_rlock { + if is_rlock { + num_rlocked++ + } else { + num_locked++ + } + } + if num_locked > 0 || num_rlocked == 0 { + f.write('lock ') + mut n := 0 + for i, v in node.lockeds { + if !node.is_rlock[i] { + if n > 0 { + f.write(', ') + } + f.expr(v) + n++ + } + } + } + if num_rlocked > 0 { + if num_locked > 0 { + f.write('; ') + } + f.write('rlock ') + mut n := 0 + for i, v in node.lockeds { + if node.is_rlock[i] { + if n > 0 { + f.write(', ') + } + f.expr(v) + n++ + } + } + } + f.writeln(' {') + f.stmts(node.stmts) + f.write('}') +} + +pub fn (mut f Fmt) map_init(node ast.MapInit) { + if node.keys.len == 0 { + if node.typ > ast.void_type { + sym := f.table.get_type_symbol(node.typ) + info := sym.info as ast.Map + f.mark_types_import_as_used(info.key_type) + f.write(f.table.type_to_str_using_aliases(node.typ, f.mod2alias)) + } else { + // m = map{} + f.write('map') + } + f.write('{}') + return + } + f.writeln('map{') + f.indent++ + f.comments(node.pre_cmnts) + mut max_field_len := 0 + for key in node.keys { + if key.str().len > max_field_len { + max_field_len = key.str().len + } + } + for i, key in node.keys { + f.expr(key) + f.write(': ') + f.write(strings.repeat(` `, max_field_len - key.str().len)) + f.expr(node.vals[i]) + f.comments(node.comments[i], prev_line: node.vals[i].position().last_line, has_nl: false) + f.writeln('') + } + f.indent-- + f.write('}') +} + +fn (mut f Fmt) match_branch(branch ast.MatchBranch, single_line bool) { + if !branch.is_else { + // normal branch + f.is_mbranch_expr = true + for j, expr in branch.exprs { + estr := f.node_str(expr).trim_space() + if f.line_len + estr.len + 2 > fmt.max_len[5] { + f.remove_new_line() + f.writeln('') + } + f.write(estr) + if j < branch.ecmnts.len && branch.ecmnts[j].len > 0 { + f.write(' ') + f.comments(branch.ecmnts[j], iembed: true) + } + if j < branch.exprs.len - 1 { + f.write(', ') + } + } + f.is_mbranch_expr = false + } else { + // else branch + f.write('else') + } + if branch.stmts.len == 0 { + f.writeln(' {}') + } else { + if single_line { + f.write(' { ') + } else { + f.writeln(' {') + } + f.stmts(branch.stmts) + if single_line { + f.remove_new_line() + f.writeln(' }') + } else { + f.writeln('}') + } + } + f.comments(branch.post_comments, inline: true) +} + +pub fn (mut f Fmt) match_expr(node ast.MatchExpr) { + f.write('match ') + f.expr(node.cond) + if node.cond is ast.Ident { + f.it_name = node.cond.name + } + f.writeln(' {') + f.indent++ + f.comments(node.comments) + mut single_line := true + for branch in node.branches { + if branch.stmts.len > 1 || branch.pos.line_nr < branch.pos.last_line { + single_line = false + break + } + if branch.stmts.len == 0 { + continue + } + if !stmt_is_single_line(branch.stmts[0]) { + single_line = false + break + } + } + mut else_idx := -1 + for i, branch in node.branches { + if branch.is_else { + else_idx = i + continue + } + f.match_branch(branch, single_line) + } + if else_idx >= 0 { + f.match_branch(node.branches[else_idx], single_line) + } + f.indent-- + f.write('}') + f.it_name = '' +} + +pub fn (mut f Fmt) offset_of(node ast.OffsetOf) { + f.write('__offsetof(${f.table.type_to_str_using_aliases(node.struct_type, f.mod2alias)}, $node.field)') + f.mark_types_import_as_used(node.struct_type) +} + +pub fn (mut f Fmt) or_expr(node ast.OrExpr) { + match node.kind { + .absent {} + .block { + if node.stmts.len == 0 { + f.write(' or {') + if node.pos.line_nr != node.pos.last_line { + f.writeln('') + } + f.write('}') + return + } else if node.stmts.len == 1 && stmt_is_single_line(node.stmts[0]) { + // the control stmts (return/break/continue...) print a newline inside them, + // so, since this'll all be on one line, trim any possible whitespace + str := f.node_str(node.stmts[0]).trim_space() + single_line := ' or { $str }' + if single_line.len + f.line_len <= fmt.max_len.last() { + f.write(single_line) + return + } + } + // Make it multiline if the blocks has at least two stmts + // or a single line would be too long + f.writeln(' or {') + f.stmts(node.stmts) + f.write('}') + } + .propagate { + f.write(' ?') + } + } +} + +pub fn (mut f Fmt) par_expr(node ast.ParExpr) { + requires_paren := node.expr !is ast.Ident + if requires_paren { + f.par_level++ + f.write('(') + } + f.expr(node.expr) + if requires_paren { + f.par_level-- + f.write(')') + } +} + +pub fn (mut f Fmt) postfix_expr(node ast.PostfixExpr) { + f.expr(node.expr) + // `$if foo ?` + if node.op == .question { + f.write(' ?') + } else { + f.write('$node.op') + } +} + +pub fn (mut f Fmt) prefix_expr(node ast.PrefixExpr) { + f.write(node.op.str()) + f.prefix_expr_cast_expr(node.right) + f.or_expr(node.or_block) +} + +pub fn (mut f Fmt) range_expr(node ast.RangeExpr) { + f.expr(node.low) + if f.is_mbranch_expr { + f.write('...') + } else { + f.write('..') + } + f.expr(node.high) +} + +pub fn (mut f Fmt) select_expr(node ast.SelectExpr) { + f.writeln('select {') + f.indent++ + for branch in node.branches { + if branch.comment.text != '' { + f.comment(branch.comment, inline: true) + f.writeln('') + } + if branch.is_else { + f.write('else {') + } else { + f.single_line_if = true + match branch.stmt { + ast.ExprStmt { f.expr(branch.stmt.expr) } + else { f.stmt(branch.stmt) } + } + f.single_line_if = false + f.write(' {') + } + if branch.stmts.len > 0 { + f.writeln('') + f.stmts(branch.stmts) + } + f.writeln('}') + if branch.post_comments.len > 0 { + f.comments(branch.post_comments, inline: true) + } + } + f.indent-- + f.write('}') +} + +pub fn (mut f Fmt) selector_expr(node ast.SelectorExpr) { + f.expr(node.expr) + f.write('.') + f.write(node.field_name) +} + +pub fn (mut f Fmt) size_of(node ast.SizeOf) { + f.write('sizeof(') + if node.is_type { + f.write(f.table.type_to_str_using_aliases(node.typ, f.mod2alias)) + } else { + f.expr(node.expr) + } + f.write(')') +} + +pub fn (mut f Fmt) is_ref_type(node ast.IsRefType) { + f.write('isreftype(') + if node.is_type { + f.write(f.table.type_to_str_using_aliases(node.typ, f.mod2alias)) + } else { + f.expr(node.expr) + } + f.write(')') +} + +pub fn (mut f Fmt) sql_expr(node ast.SqlExpr) { + // sql app.db { select from Contributor where repo == id && user == 0 } + f.write('sql ') + f.expr(node.db_expr) + f.writeln(' {') + f.write('\tselect ') + table_name := util.strip_mod_name(f.table.get_type_symbol(node.table_expr.typ).name) + if node.is_count { + f.write('count ') + } else { + for i, fd in node.fields { + f.write(fd.name) + if i < node.fields.len - 1 { + f.write(', ') + } + } + } + f.write('from $table_name') + if node.has_where { + f.write(' where ') + f.expr(node.where_expr) + } + if node.has_order { + f.write(' order by ') + f.expr(node.order_expr) + if node.has_desc { + f.write(' desc') + } + } + if node.has_limit { + f.write(' limit ') + f.expr(node.limit_expr) + } + if node.has_offset { + f.write(' offset ') + f.expr(node.offset_expr) + } + f.writeln('') + f.write('}') +} + +pub fn (mut f Fmt) string_literal(node ast.StringLiteral) { + use_double_quote := node.val.contains("'") && !node.val.contains('"') + if node.is_raw { + f.write('r') + } else if node.language == ast.Language.c { + f.write('c') + } + if node.is_raw { + if use_double_quote { + f.write('"$node.val"') + } else { + f.write("'$node.val'") + } + } else { + unescaped_val := node.val.replace('$fmt.bs$fmt.bs', '\x01').replace_each(["$fmt.bs'", "'", + '$fmt.bs"', '"']) + if use_double_quote { + s := unescaped_val.replace_each(['\x01', '$fmt.bs$fmt.bs', '"', '$fmt.bs"']) + f.write('"$s"') + } else { + s := unescaped_val.replace_each(['\x01', '$fmt.bs$fmt.bs', "'", "$fmt.bs'"]) + f.write("'$s'") + } + } +} + +pub fn (mut f Fmt) string_inter_literal(node ast.StringInterLiteral) { + mut quote := "'" + for val in node.vals { + if val.contains('\\"') { + quote = '"' + break + } + if val.contains("\\'") { + quote = "'" + break + } + if val.contains('"') { + quote = "'" + } + if val.contains("'") { + quote = '"' + } + } + // TODO: this code is very similar to ast.Expr.str() + // serkonda7: it can not fully be replaced tho as ´f.expr()´ and `ast.Expr.str()` + // work too different for the various exprs that are interpolated + f.write(quote) + for i, val in node.vals { + f.write(val) + if i >= node.exprs.len { + break + } + f.write('$') + fspec_str, needs_braces := node.get_fspec_braces(i) + if needs_braces { + f.write('{') + f.expr(node.exprs[i]) + f.write(fspec_str) + f.write('}') + } else { + f.expr(node.exprs[i]) + } + } + f.write(quote) +} + +pub fn (mut f Fmt) type_expr(node ast.TypeNode) { + f.mark_types_import_as_used(node.typ) + f.write(f.table.type_to_str_using_aliases(node.typ, f.mod2alias)) +} + +pub fn (mut f Fmt) type_of(node ast.TypeOf) { + f.write('typeof(') + f.expr(node.expr) + f.write(')') +} + +pub fn (mut f Fmt) unsafe_expr(node ast.UnsafeExpr) { + single_line := node.pos.line_nr >= node.pos.last_line + f.write('unsafe {') + if single_line { + f.write(' ') + } else { + f.writeln('') + f.indent++ + f.empty_line = true + } + f.expr(node.expr) + if single_line { + f.write(' ') + } else { + f.writeln('') + f.indent-- + } + f.write('}') +} + +pub fn (mut f Fmt) prefix_expr_cast_expr(node ast.Expr) { + mut is_pe_amp_ce := false + if node is ast.PrefixExpr { + if node.right is ast.CastExpr && node.op == .amp { + mut ce := node.right + ce.typname = f.table.get_type_symbol(ce.typ).name + is_pe_amp_ce = true + f.expr(ce) + } + } else if node is ast.CastExpr { + last := f.out.cut_last(1) + if last != '&' { + f.out.write_string(last) + } + } + if !is_pe_amp_ce { + f.expr(node) + } +} + +fn (mut f Fmt) trace(fbase string, message string) { + if f.file.path_base == fbase { + println('> f.trace | ${fbase:-10s} | $message') + } +} diff --git a/v_windows/v/old/vlib/v/fmt/fmt_keep_test.v b/v_windows/v/old/vlib/v/fmt/fmt_keep_test.v new file mode 100644 index 0000000..74078b4 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/fmt_keep_test.v @@ -0,0 +1,115 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +import os +import term +import benchmark +import v.fmt +import v.parser +import v.ast +import v.pref +import v.util.diff +import v.util.vtest + +const ( + error_missing_vexe = 1 + error_failed_tests = 2 + b2v_keep_path = os.join_path('vlib', 'v', 'fmt', 'tests', 'bin2v_keep.vv') + fpref = &pref.Preferences{ + is_fmt: true + } + vexe = os.getenv('VEXE') +) + +fn test_fmt() { + fmt_message := 'checking that v fmt keeps already formatted files *unchanged*' + eprintln(term.header(fmt_message, '-')) + if vexe.len == 0 || !os.exists(vexe) { + eprintln('VEXE must be set') + exit(error_missing_vexe) + } + vroot := os.dir(vexe) + os.chdir(vroot) + basepath := os.join_path(vroot, '') + tmpfolder := os.temp_dir() + diff_cmd := diff.find_working_diff_command() or { '' } + mut fmt_bench := benchmark.new_benchmark() + keep_input_files := os.walk_ext('vlib/v/fmt/tests', '_keep.vv') + expected_input_files := os.walk_ext('vlib/v/fmt/tests', '_expected.vv') + mut input_files := []string{} + input_files << keep_input_files + input_files << expected_input_files + input_files = vtest.filter_vtest_only(input_files, basepath: vroot) + fmt_bench.set_total_expected_steps(input_files.len + 1) + prepare_bin2v_file(mut fmt_bench) + for istep, ipath in input_files { + fmt_bench.cstep = istep + 1 + fmt_bench.step() + ifilename := os.file_name(ipath) + vrelpath := ipath.replace(basepath, '') + opath := ipath + expected_ocontent := os.read_file(opath) or { + fmt_bench.fail() + eprintln(fmt_bench.step_message_fail('cannot read from $vrelpath')) + continue + } + table := ast.new_table() + file_ast := parser.parse_file(ipath, table, .parse_comments, fpref) + result_ocontent := fmt.fmt(file_ast, table, fpref, false) + if expected_ocontent != result_ocontent { + fmt_bench.fail() + eprintln(fmt_bench.step_message_fail('file $vrelpath after formatting, does not look as expected.')) + if ipath.ends_with(b2v_keep_path) { + continue + } + if diff_cmd == '' { + eprintln('>> sorry, but no working "diff" CLI command can be found') + continue + } + vfmt_result_file := os.join_path(tmpfolder, 'vfmt_run_over_$ifilename') + os.write_file(vfmt_result_file, result_ocontent) or { panic(err.msg) } + eprintln(diff.color_compare_files(diff_cmd, opath, vfmt_result_file)) + continue + } + fmt_bench.ok() + eprintln(fmt_bench.step_message_ok(vrelpath)) + } + restore_bin2v_placeholder() or { + eprintln('failed restoring vbin2v_keep.vv placeholder: $err.msg') + } + fmt_bench.stop() + eprintln(term.h_divider('-')) + eprintln(fmt_bench.total_message(fmt_message)) + if fmt_bench.nfail > 0 { + exit(error_failed_tests) + } +} + +fn prepare_bin2v_file(mut fmt_bench benchmark.Benchmark) { + fmt_bench.cstep = 0 + fmt_bench.step() + write_bin2v_keep_content() or { + fmt_bench.fail() + eprintln(fmt_bench.step_message_fail('Failed preparing bin2v_keep.vv: $err.msg')) + return + } + fmt_bench.ok() + eprintln(fmt_bench.step_message_ok('Prepared bin2v_keep.vv')) +} + +fn write_bin2v_keep_content() ? { + img0 := os.join_path('vlib', 'v', 'embed_file', 'v.png') + img1 := os.join_path('tutorials', 'building_a_simple_web_blog_with_vweb', 'img', 'time.png') + os.rm(b2v_keep_path) ? + res := os.execute('$vexe bin2v -w $b2v_keep_path $img0 $img1') + if res.exit_code != 0 { + restore_bin2v_placeholder() or {} + return error_with_code(res.output.trim_space(), res.exit_code) + } +} + +fn restore_bin2v_placeholder() ? { + text := '// This is a placeholder file which will be filled with bin2v output before the test. +// HINT: do NOT delete, move or rename this file!\n' + os.write_file(b2v_keep_path, text) ? +} diff --git a/v_windows/v/old/vlib/v/fmt/fmt_test.v b/v_windows/v/old/vlib/v/fmt/fmt_test.v new file mode 100644 index 0000000..32d7445 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/fmt_test.v @@ -0,0 +1,75 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +import os +import term +import benchmark +import v.ast +import v.fmt +import v.parser +import v.pref +import v.util.diff + +const ( + error_missing_vexe = 1 + error_failed_tests = 2 + fpref = &pref.Preferences{ + is_fmt: true + } +) + +fn test_fmt() { + fmt_message := 'vfmt tests' + eprintln(term.header(fmt_message, '-')) + vexe := os.getenv('VEXE') + if vexe.len == 0 || !os.exists(vexe) { + eprintln('VEXE must be set') + exit(error_missing_vexe) + } + vroot := os.dir(vexe) + tmpfolder := os.temp_dir() + diff_cmd := diff.find_working_diff_command() or { '' } + mut fmt_bench := benchmark.new_benchmark() + // Lookup the existing test _input.vv files: + input_files := os.walk_ext('$vroot/vlib/v/fmt/tests', '_input.vv') + fmt_bench.set_total_expected_steps(input_files.len) + for istep, ipath in input_files { + fmt_bench.cstep = istep + fmt_bench.step() + ifilename := os.file_name(ipath) + opath := ipath.replace('_input.vv', '_expected.vv') + if !os.exists(opath) { + fmt_bench.fail() + eprintln(fmt_bench.step_message_fail('missing file $opath')) + continue + } + expected_ocontent := os.read_file(opath) or { + fmt_bench.fail() + eprintln(fmt_bench.step_message_fail('cannot read from $opath')) + continue + } + table := ast.new_table() + file_ast := parser.parse_file(ipath, table, .parse_comments, fpref) + result_ocontent := fmt.fmt(file_ast, table, fpref, false) + if expected_ocontent != result_ocontent { + fmt_bench.fail() + eprintln(fmt_bench.step_message_fail('file $ipath after formatting, does not look as expected.')) + if diff_cmd == '' { + eprintln('>> sorry, but no working "diff" CLI command can be found') + continue + } + vfmt_result_file := os.join_path(tmpfolder, 'vfmt_run_over_$ifilename') + os.write_file(vfmt_result_file, result_ocontent) or { panic(err) } + eprintln(diff.color_compare_files(diff_cmd, opath, vfmt_result_file)) + continue + } + fmt_bench.ok() + eprintln(fmt_bench.step_message_ok('$ipath')) + } + fmt_bench.stop() + eprintln(term.h_divider('-')) + eprintln(fmt_bench.total_message(fmt_message)) + if fmt_bench.nfail > 0 { + exit(error_failed_tests) + } +} diff --git a/v_windows/v/old/vlib/v/fmt/fmt_vlib_test.v b/v_windows/v/old/vlib/v/fmt/fmt_vlib_test.v new file mode 100644 index 0000000..00eecb0 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/fmt_vlib_test.v @@ -0,0 +1,73 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +import os +import term +import benchmark +import v.ast +import v.fmt +import v.parser +import v.pref +import v.util.diff + +const ( + error_missing_vexe = 1 + error_failed_tests = 2 + fpref = &pref.Preferences{ + is_fmt: true + } +) + +fn test_vlib_fmt() { + $if !vfmt_everything ? { + return + } + fmt_message := "checking that all V source files are vfmt'ed" + eprintln(term.header(fmt_message, '-')) + vexe := os.getenv('VEXE') + if vexe.len == 0 || !os.exists(vexe) { + eprintln('VEXE must be set') + exit(error_missing_vexe) + } + vroot := os.dir(vexe) + tmpfolder := os.temp_dir() + diff_cmd := diff.find_working_diff_command() or { '' } + mut fmt_bench := benchmark.new_benchmark() + os.chdir(vroot) + input_files := os.walk_ext('vlib/v/', '.v').filter(!it.contains('/tests/')) + fmt_bench.set_total_expected_steps(input_files.len) + for istep, ipath in input_files { + fmt_bench.cstep = istep + fmt_bench.step() + ifilename := os.file_name(ipath) + opath := ipath + expected_ocontent := os.read_file(opath) or { + fmt_bench.fail() + eprintln(fmt_bench.step_message_fail('cannot read from $opath')) + continue + } + table := ast.new_table() + file_ast := parser.parse_file(ipath, table, .parse_comments, fpref) + result_ocontent := fmt.fmt(file_ast, table, fpref, false) + if expected_ocontent != result_ocontent { + fmt_bench.fail() + eprintln(fmt_bench.step_message_fail('file $ipath after formatting, does not look as expected.')) + if diff_cmd == '' { + eprintln('>> sorry, but no working "diff" CLI command can be found') + continue + } + vfmt_result_file := os.join_path(tmpfolder, 'vfmt_run_over_$ifilename') + os.write_file(vfmt_result_file, result_ocontent) or { panic(err) } + eprintln(diff.color_compare_files(diff_cmd, opath, vfmt_result_file)) + continue + } + fmt_bench.ok() + eprintln(fmt_bench.step_message_ok('$ipath')) + } + fmt_bench.stop() + eprintln(term.h_divider('-')) + eprintln(fmt_bench.total_message(fmt_message)) + if fmt_bench.nfail > 0 { + exit(error_failed_tests) + } +} diff --git a/v_windows/v/old/vlib/v/fmt/struct.v b/v_windows/v/old/vlib/v/fmt/struct.v new file mode 100644 index 0000000..69c2c01 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/struct.v @@ -0,0 +1,288 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module fmt + +import math.mathutil as mu +import strings +import v.ast + +pub fn (mut f Fmt) struct_decl(node ast.StructDecl) { + f.attrs(node.attrs) + if node.is_pub { + f.write('pub ') + } + if node.is_union { + f.write('union ') + } else { + f.write('struct ') + } + f.write_language_prefix(node.language) + name := node.name.after('.') // strip prepended module + f.write(name) + f.write_generic_types(node.generic_types) + if node.fields.len == 0 && node.embeds.len == 0 && node.pos.line_nr == node.pos.last_line { + f.writeln(' {}') + return + } + mut field_aligns := []AlignInfo{} + mut comment_aligns := []AlignInfo{} + mut default_expr_aligns := []AlignInfo{} + mut field_types := []string{cap: node.fields.len} + for i, field in node.fields { + ft := f.no_cur_mod(f.table.type_to_str_using_aliases(field.typ, f.mod2alias)) + field_types << ft + attrs_len := inline_attrs_len(field.attrs) + end_pos := field.pos.pos + field.pos.len + mut comments_len := 0 // Length of comments between field name and type + for comment in field.comments { + if comment.pos.pos >= end_pos { + if comment.pos.line_nr == field.pos.line_nr { + comment_aligns.add_info(attrs_len, field_types[i].len, comment.pos.line_nr, + use_threshold: true) + } + continue + } + if comment.pos.pos > field.pos.pos { + comments_len += '/* ${comment.text.trim_left('\x01')} */ '.len + } + } + field_aligns.add_info(comments_len + field.name.len, ft.len, field.pos.line_nr) + if field.has_default_expr { + default_expr_aligns.add_info(attrs_len, field_types[i].len, field.pos.line_nr, + use_threshold: true) + } + } + f.writeln(' {') + for embed in node.embeds { + f.mark_types_import_as_used(embed.typ) + styp := f.table.type_to_str_using_aliases(embed.typ, f.mod2alias) + if embed.comments.len == 0 { + f.writeln('\t$styp') + } else { + f.write('\t$styp') + f.comments(embed.comments, level: .indent) + } + } + mut field_align_i := 0 + mut comment_align_i := 0 + mut default_expr_align_i := 0 + mut inc_indent := false // for correct indents with multi line default exprs + for i, field in node.fields { + if i == node.mut_pos { + f.writeln('mut:') + } else if i == node.pub_pos { + f.writeln('pub:') + } else if i == node.pub_mut_pos { + f.writeln('pub mut:') + } else if i == node.global_pos { + f.writeln('__global:') + } else if i == node.module_pos { + f.writeln('module:') + } else if i > 0 { + // keep one empty line between fields (exclude one after mut:, pub:, ...) + mut before_last_line := node.fields[i - 1].pos.line_nr + if node.fields[i - 1].comments.len > 0 { + before_last_line = mu.max(before_last_line, node.fields[i - 1].comments.last().pos.last_line) + } + if node.fields[i - 1].has_default_expr { + before_last_line = mu.max(before_last_line, node.fields[i - 1].default_expr.position().last_line) + } + + mut next_first_line := field.pos.line_nr + if field.comments.len > 0 { + next_first_line = mu.min(next_first_line, field.comments[0].pos.line_nr) + } + if next_first_line - before_last_line > 1 { + f.writeln('') + } + } + end_pos := field.pos.pos + field.pos.len + before_comments := field.comments.filter(it.pos.pos < field.pos.pos) + between_comments := field.comments[before_comments.len..].filter(it.pos.pos < end_pos) + after_type_comments := field.comments[(before_comments.len + between_comments.len)..] + // Handle comments before the field + f.comments_before_field(before_comments) + f.write('\t$field.name ') + // Handle comments between field name and type + before_len := f.line_len + f.comments(between_comments, iembed: true, has_nl: false) + comments_len := f.line_len - before_len + mut field_align := field_aligns[field_align_i] + if field_align.line_nr < field.pos.line_nr { + field_align_i++ + field_align = field_aligns[field_align_i] + } + f.write(strings.repeat(` `, field_align.max_len - field.name.len - comments_len)) + f.write(field_types[i]) + f.mark_types_import_as_used(field.typ) + attrs_len := inline_attrs_len(field.attrs) + has_attrs := field.attrs.len > 0 + if has_attrs { + f.write(strings.repeat(` `, field_align.max_type_len - field_types[i].len)) + f.single_line_attrs(field.attrs, inline: true) + } + if field.has_default_expr { + mut align := default_expr_aligns[default_expr_align_i] + if align.line_nr < field.pos.line_nr { + default_expr_align_i++ + align = default_expr_aligns[default_expr_align_i] + } + pad_len := align.max_len - attrs_len + align.max_type_len - field_types[i].len + f.write(strings.repeat(` `, pad_len)) + f.write(' = ') + if !expr_is_single_line(field.default_expr) { + f.indent++ + inc_indent = true + } + f.prefix_expr_cast_expr(field.default_expr) + if inc_indent { + f.indent-- + inc_indent = false + } + } + // Handle comments after field type + if after_type_comments.len > 0 { + if after_type_comments[0].pos.line_nr > field.pos.line_nr { + f.writeln('') + } else { + if !field.has_default_expr { + mut align := comment_aligns[comment_align_i] + if align.line_nr < field.pos.line_nr { + comment_align_i++ + align = comment_aligns[comment_align_i] + } + pad_len := align.max_len - attrs_len + align.max_type_len - field_types[i].len + f.write(strings.repeat(` `, pad_len)) + } + f.write(' ') + } + f.comments(after_type_comments, level: .indent) + } else { + f.writeln('') + } + } + f.comments_after_last_field(node.end_comments) + f.writeln('}\n') +} + +pub fn (mut f Fmt) struct_init(node ast.StructInit) { + struct_init_save := f.is_struct_init + f.is_struct_init = true + defer { + f.is_struct_init = struct_init_save + } + + type_sym := f.table.get_type_symbol(node.typ) + // f.write('') + mut name := type_sym.name + if !name.starts_with('C.') && !name.starts_with('JS.') { + name = f.no_cur_mod(f.short_module(type_sym.name)) // TODO f.type_to_str? + } + if name == 'void' { + name = '' + } + if node.fields.len == 0 && !node.has_update_expr { + // `Foo{}` on one line if there are no fields or comments + if node.pre_comments.len == 0 { + f.write('$name{}') + } else { + f.writeln('$name{') + f.comments(node.pre_comments, inline: true, has_nl: true, level: .indent) + f.write('}') + } + f.mark_import_as_used(name) + } else if node.is_short { + // `Foo{1,2,3}` (short syntax ) + f.write('$name{') + f.mark_import_as_used(name) + if node.has_update_expr { + f.write('...') + f.expr(node.update_expr) + f.write(', ') + } + for i, field in node.fields { + f.prefix_expr_cast_expr(field.expr) + if i < node.fields.len - 1 { + f.write(', ') + } + } + f.write('}') + } else { + use_short_args := f.use_short_fn_args && !node.has_update_expr + f.use_short_fn_args = false + mut single_line_fields := f.single_line_fields + f.single_line_fields = false + if node.pos.line_nr < node.pos.last_line || node.pre_comments.len > 0 { + single_line_fields = false + } + if !use_short_args { + f.write('$name{') + f.mark_import_as_used(name) + if single_line_fields { + f.write(' ') + } + } + fields_start := f.out.len + fields_loop: for { + if !single_line_fields { + if use_short_args && f.out[f.out.len - 1] == ` ` { + // v Remove space at tail of line + // f(a, b, c, \n + // f1: 0\n + // f2: 1\n + // ) + f.out.go_back(1) + } + f.writeln('') + f.indent++ + } + f.comments(node.pre_comments, inline: true, has_nl: true, level: .keep) + if node.has_update_expr { + f.write('...') + f.expr(node.update_expr) + if single_line_fields { + if node.fields.len > 0 { + f.write(', ') + } + } else { + f.writeln('') + } + f.comments(node.update_expr_comments, inline: true, has_nl: true, level: .keep) + } + for i, field in node.fields { + f.write('$field.name: ') + f.prefix_expr_cast_expr(field.expr) + f.comments(field.comments, inline: true, has_nl: false, level: .indent) + if single_line_fields { + if i < node.fields.len - 1 { + f.write(', ') + } + } else { + f.writeln('') + } + f.comments(field.next_comments, inline: false, has_nl: true, level: .keep) + if single_line_fields && (field.comments.len > 0 + || field.next_comments.len > 0 + || !expr_is_single_line(field.expr) + || f.line_len > max_len.last()) { + single_line_fields = false + f.out.go_back_to(fields_start) + f.line_len = fields_start + f.remove_new_line() + continue fields_loop + } + } + break + } + if !single_line_fields { + f.indent-- + } + if !use_short_args { + if single_line_fields { + f.write(' ') + } + f.write('}') + } + } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/anon_fn_as_param_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/anon_fn_as_param_keep.vv new file mode 100644 index 0000000..4ed7d1a --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/anon_fn_as_param_keep.vv @@ -0,0 +1,20 @@ +import v.ast + +struct Data { + a fn (stmt ast.Stmt, vp voidptr) bool +} + +pub fn (a []Data) reduce(iter fn (int, int) int, accum_start int) int { + iter(accum_start) +} + +pub fn test_anon_fn_void(func fn ()) int { + return 0 +} + +fn C.HasAnonFnWithNamedParams(cb fn (c C.bar, d voidptr)) + +// NB: the signature of both anonymus functions should only differs in the param name +fn anon_fn_param_has_no_name(f fn (int) string) {} + +fn anon_fn_with_named_param(func fn (a int) string) {} diff --git a/v_windows/v/old/vlib/v/fmt/tests/anon_fn_call_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/anon_fn_call_keep.vv new file mode 100644 index 0000000..e2661ba --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/anon_fn_call_keep.vv @@ -0,0 +1,7 @@ +fn main() { + fn () { + for { + println('Hello V!') + } + }() +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/anon_fn_expected.vv b/v_windows/v/old/vlib/v/fmt/tests/anon_fn_expected.vv new file mode 100644 index 0000000..35641ee --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/anon_fn_expected.vv @@ -0,0 +1,24 @@ +fn has_anon_fn() { + an_fn := fn () { + println('Hello there !') + } + an_fn_w_param := fn (s string) { + println('I received $s') + } + an_fn_w_multi_params := fn (s1 string, s2 string, s3 string) { + println('I received $s1, $s2, $s3') + } + an_fn_w_multi_params2 := fn (s string, i int) { + println('I received $s, $i') + } + fn_w_var_args := fn (ss ...string) { + for s in ss { + println('yo $s') + } + } + an_fn() + an_fn_w_param('a gift') + an_fn_w_multi_params('one', 'two', 'three') + an_fn_w_multi_params2('one', 1) + fn_w_var_args('one arg', 'two args', 'three args') +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/anon_fn_input.vv b/v_windows/v/old/vlib/v/fmt/tests/anon_fn_input.vv new file mode 100644 index 0000000..8884991 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/anon_fn_input.vv @@ -0,0 +1,23 @@ +fn has_anon_fn() { + an_fn := fn() { + println('Hello there !') + } + an_fn_w_param := fn ( s string ) + { + println('I received $s') + } + an_fn_w_multi_params := fn (s1 string, s2 string, s3 string) { + println('I received $s1, $s2, $s3') + } + an_fn_w_multi_params2 :=fn (s string, i int) { + println('I received $s, $i') + } + fn_w_var_args := fn (ss ...string) { + for s in ss { + println('yo $s') + } + } an_fn() + an_fn_w_param('a gift') an_fn_w_multi_params('one', 'two', 'three') + an_fn_w_multi_params2( 'one', 1) + fn_w_var_args('one arg', 'two args', 'three args') +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/array_decomposition_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/array_decomposition_keep.vv new file mode 100644 index 0000000..387997f --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/array_decomposition_keep.vv @@ -0,0 +1,8 @@ +fn varargs(a ...int) { + println(a) +} + +fn main() { + a := [1, 2, 3] + varargs(...a) +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/array_init_comment_ending_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/array_init_comment_ending_keep.vv new file mode 100644 index 0000000..9fe7624 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/array_init_comment_ending_keep.vv @@ -0,0 +1,23 @@ +import net.http + +const ( + write_set_cookie_tests = [ + ReadSetCookiesTestCase{ + header: map{ + 'Set-Cookie': ['special-7=","'] + } + cookies: [ + &http.Cookie{ + name: 'special-7' + value: ',' + raw: 'special-8=","' + }, + ] + } + // (bradfitz): users have reported seeing this in the + // wild, but do browsers handle it? RFC 6265 just says "don't + // do that" (section 3) and then never mentions header folding + // again. + // Header{"Set-Cookie": ["ASP.NET_SessionId=foo; path=/; HttpOnly, .ASPXAUTH=7E3AA; expires=Wed, 07-Mar-2012 14:25:06 GMT; path=/; HttpOnly"]}, + ] +) diff --git a/v_windows/v/old/vlib/v/fmt/tests/array_init_eol_comments_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/array_init_eol_comments_keep.vv new file mode 100644 index 0000000..2fe3d4d --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/array_init_eol_comments_keep.vv @@ -0,0 +1,104 @@ +fn abc() { + test_cases_f32 := [ + f32_from_bits1(0x0000_0000), // +0 + f32_from_bits1(0x8000_0000), // -0 + f32_from_bits1(0xFFC0_0001), // sNan + f32_from_bits1(0xFF80_0001), // qNan + f32_from_bits1(0x7F80_0000), // +inf + f32_from_bits1(0xFF80_0000), // -inf + 1, + -1, + 10, + -10, + 0.3, + -0.3, + 1000000, + 123456.7, + 123e35, + -123.45, + 1e23, + f32_from_bits1(0x0080_0000), // smallest float32 + math.max_f32, + 383260575764816448., + ] + + exp_result_f32 := [ + '0e+00', + '-0e+00', + 'nan', + 'nan', + '+inf', + '-inf', + '1.e+00', + '-1.e+00', + '1.e+01', + '-1.e+01', + '3.e-01', + '-3.e-01', + '1.e+06', + '1.234567e+05', + '1.23e+37', + '-1.2345e+02', + '1.e+23', + '1.1754944e-38', // aprox from 1.1754943508 × 10−38, + '3.4028235e+38', + '3.8326058e+17', + ] + + test_cases_f64 := [ + f64_from_bits1(0x0000_0000_0000_0000), // +0 + f64_from_bits1(0x8000_0000_0000_0000), // -0 + f64_from_bits1(0x7FF0_0000_0000_0001), // sNan + f64_from_bits1(0x7FF8_0000_0000_0001), // qNan + f64_from_bits1(0x7FF0_0000_0000_0000), // +inf + f64_from_bits1(0xFFF0_0000_0000_0000), // -inf + 1, + -1, + 10, + -10, + 0.3, + -0.3, + 1000000, + 123456.7, + 123e45, + -123.45, + 1e23, + f64_from_bits1(0x0010_0000_0000_0000), // smallest float64 + math.max_f32, + 383260575764816448, + 383260575764816448, + // C failing cases + 123e300, + 123e-300, + 5.e-324, + -5.e-324, + ] + + exp_result_f64 := [ + '0e+00', + '-0e+00', + 'nan', + 'nan', + '+inf', + '-inf', + '1.e+00', + '-1.e+00', + '1.e+01', + '-1.e+01', + '3.e-01', + '-3.e-01', + '1.e+06', + '1.234567e+05', + '1.23e+47', + '-1.2345e+02', + '1.e+23', + '2.2250738585072014e-308', + '3.4028234663852886e+38', + '3.8326057576481645e+17', + '3.8326057576481645e+17', + '1.23e+302', // this test is failed from C sprintf!! + '1.23e-298', + '5.e-324', + '-5.e-324', + ] +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/array_init_expected.vv b/v_windows/v/old/vlib/v/fmt/tests/array_init_expected.vv new file mode 100644 index 0000000..44cf30f --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/array_init_expected.vv @@ -0,0 +1,35 @@ +fn main() { + if 1 == 1 { + println('yeah !') + } + if 1 != 1 { + println("oh no :'(") + } +} + +fn wrapping_tests() { + my_arr := ['Lorem ipsum dolor sit amet, consectetur adipiscing', + 'elit. Donec varius purus leo, vel maximus diam', + 'finibus sed. Etiam eu urna ante. Nunc quis vehicula', + 'velit. Sed at mauris et quam ornare tristique.', + ] +} + +fn array_init_without_commas() { + a := [3, -4, 5, -2] + b := [5 - 2, 7, 2 * 3, -4, -7 * 8, 2 * -4] + c := [4, 6 - 7, 8, 3 * 4] + d := [&a, &b, &c] + e := u16(434) + f := u16(23) + g := u16(4324) + h := [e & f, g, g & g] + k := 323 + l := [k, -k, 5] + x := &e + y := &f + z := &g + q := [f, *y, f * g, e] + ch := chan u16{} + w := [f, 12 <- ch] +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/array_init_inline_comments_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/array_init_inline_comments_keep.vv new file mode 100644 index 0000000..d1c18e9 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/array_init_inline_comments_keep.vv @@ -0,0 +1,2 @@ +a := [1 /* y */, /* x */ 2, 3] +println(a) diff --git a/v_windows/v/old/vlib/v/fmt/tests/array_init_input.vv b/v_windows/v/old/vlib/v/fmt/tests/array_init_input.vv new file mode 100644 index 0000000..4b465d7 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/array_init_input.vv @@ -0,0 +1,31 @@ +fn main() { + if 1 in [1] { + println('yeah !') + } + if 1 !in [1] { + println("oh no :'(") + } +} + +fn wrapping_tests() { + my_arr := ['Lorem ipsum dolor sit amet, consectetur adipiscing', 'elit. Donec varius purus leo, vel maximus diam', 'finibus sed. Etiam eu urna ante. Nunc quis vehicula', 'velit. Sed at mauris et quam ornare tristique.'] +} + +fn array_init_without_commas() { + a := [3 -4 5 -2] + b := [5-2,7,2*3,-4,-7*8,2*-4] + c := [4 6 - 7 8 3 * 4] + d := [&a &b &c] + e := u16(434) + f := u16(23) + g := u16(4324) + h := [e&f g g & g] + k := 323 + l := [k -k 5] + x := &e + y := &f + z := &g + q := [f *y f * g e] + ch := chan u16{} + w := [f 12 <-ch] +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/array_init_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/array_init_keep.vv new file mode 100644 index 0000000..3888cc4 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/array_init_keep.vv @@ -0,0 +1,9 @@ +fn main() { + _ := []int{len: 10, cap: 10, init: 7} + _ := []map[string]string{len: 5, cap: 50, init: map{ + 'a': 'a' + }} + _ := []map[string][]int{len: 7, cap: 100, init: map{ + 'a': [1, 2] + }} +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/array_newlines_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/array_newlines_keep.vv new file mode 100644 index 0000000..11fded1 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/array_newlines_keep.vv @@ -0,0 +1,17 @@ +fn make_flag(a string, b string, c string) string { + return '' +} + +fn main() { + // Set up flags + expected_flags := [ + make_flag('solaris', '-L', '/opt/local/lib'), + make_flag('macos', '-framework', 'Cocoa'), + make_flag('windows', '-l', 'gdi32'), + ] + x := []int{len: 10, cap: 100, init: 1} + _ := expected_flags + buf := [100]byte{} + println(x) + println(buf) +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/array_slices_expected.vv b/v_windows/v/old/vlib/v/fmt/tests/array_slices_expected.vv new file mode 100644 index 0000000..b0bcee7 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/array_slices_expected.vv @@ -0,0 +1,12 @@ +fn fn_contains_index_expr() { + arr := [1, 2, 3, 4, 5] + a := 1 in arr[0..] + _ := a + _ := 1 in arr[..2] + _ := 1 in arr[1..3] + d := arr[2] + _ := d + _ := arr[2..] + _ := arr[..2] + _ := arr[1..3] +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/array_slices_input.vv b/v_windows/v/old/vlib/v/fmt/tests/array_slices_input.vv new file mode 100644 index 0000000..d70add5 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/array_slices_input.vv @@ -0,0 +1,14 @@ + + +fn fn_contains_index_expr() { + arr := [1, 2, 3, 4, 5] + a := 1 in arr[ 0.. ] +_ := a + _ := 1 in arr[ ..2 ] + _ := 1 in arr[1..3] + d := arr[2] + _ := d + _ := arr[2 ..] + _ := arr[.. 2 ] + _ := arr[ 1 .. 3] +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/array_static_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/array_static_keep.vv new file mode 100644 index 0000000..1555780 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/array_static_keep.vv @@ -0,0 +1,14 @@ +struct Board { +mut: + field [10][5]int + points int + shifts int +} + +struct TileLine { + ypos int +mut: + field [5]int + points int + shifts int +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/assembly/asm_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/assembly/asm_keep.vv new file mode 100644 index 0000000..a6f9e56 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/assembly/asm_keep.vv @@ -0,0 +1,10 @@ +fn main() { + asm volatile amd64 { + mov ecx, 5 + loop_start: + add j, 3 + loop loop_start + ; +r (j) + ; ; ecx + } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/asserts_expected.vv b/v_windows/v/old/vlib/v/fmt/tests/asserts_expected.vv new file mode 100644 index 0000000..08dd71a --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/asserts_expected.vv @@ -0,0 +1,5 @@ +fn f() { + assert 0 == 0 + assert 0 < 1 + assert (1 + 2) == 3 +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/asserts_input.vv b/v_windows/v/old/vlib/v/fmt/tests/asserts_input.vv new file mode 100644 index 0000000..ae2a01a --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/asserts_input.vv @@ -0,0 +1,5 @@ +fn f() { + assert (0 == 0) + assert (0 < 1) + assert ((1 + 2) == 3) +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/asserts_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/asserts_keep.vv new file mode 100644 index 0000000..999c1fe --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/asserts_keep.vv @@ -0,0 +1,6 @@ +fn test_abc() { + assert 2 < 3 + assert 2 == 2 + assert 'abc' == 'abc' + assert 'abc'.len == 3 +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/attrs_expected.vv b/v_windows/v/old/vlib/v/fmt/tests/attrs_expected.vv new file mode 100644 index 0000000..1660ee5 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/attrs_expected.vv @@ -0,0 +1,9 @@ +[export: 'JNICALL Java_io_vlang_V_callStaticMethods'] +[tom: 'jerry'] +[direct_array_access; inline; unsafe] +fn heavily_tagged() {} + +// console attribute for easier diagnostics on windows +// also it's not safe to use +[console; unsafe] +fn dangerous_console() {} diff --git a/v_windows/v/old/vlib/v/fmt/tests/attrs_input.vv b/v_windows/v/old/vlib/v/fmt/tests/attrs_input.vv new file mode 100644 index 0000000..bfffc55 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/attrs_input.vv @@ -0,0 +1,10 @@ +[inline] +[export: 'JNICALL Java_io_vlang_V_callStaticMethods'] +[direct_array_access] +[unsafe] +[tom: 'jerry'] +fn heavily_tagged() {} + +[console] // console attribute for easier diagnostics on windows +[unsafe] // also it's not safe to use +fn dangerous_console() {} diff --git a/v_windows/v/old/vlib/v/fmt/tests/attrs_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/attrs_keep.vv new file mode 100644 index 0000000..cad488c --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/attrs_keep.vv @@ -0,0 +1,32 @@ +struct AttrsWithEscpaedStringArgs { + dollar string [foo: '\$var'] + double_bs string [bar: '\\baz'] +} + +[deprecated: 'use bar() instead'] +[foo: bar] +[if debug; inline] +fn keep_attributes() { + println('hi !') +} + +[bar: 'foo'] +fn attr_with_arg() {} + +['a_string_name'] +fn name_only_attr() {} + +struct User { + age int + nums []int + last_name string [json: lastName] + is_registered bool [json: IsRegistered] + typ int [json: 'type'] + pets string [json: 'pet_animals'; raw] +} + +[_allow_multiple_values] +enum Example { + value1 = 1 + value1_again = 1 +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/bin2v_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/bin2v_keep.vv new file mode 100644 index 0000000..5c6f861 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/bin2v_keep.vv @@ -0,0 +1,2 @@ +// This is a placeholder file which will be filled with bin2v output before the test. +// HINT: do NOT delete, move or rename this file! diff --git a/v_windows/v/old/vlib/v/fmt/tests/blocks_expected.vv b/v_windows/v/old/vlib/v/fmt/tests/blocks_expected.vv new file mode 100644 index 0000000..988f8a8 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/blocks_expected.vv @@ -0,0 +1,11 @@ +fn unsafe_fn() { + unsafe { + _ = malloc(2) + } +} + +fn fn_with_defer() { + defer { + voidfn() + } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/blocks_input.vv b/v_windows/v/old/vlib/v/fmt/tests/blocks_input.vv new file mode 100644 index 0000000..6efacaa --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/blocks_input.vv @@ -0,0 +1,7 @@ +fn unsafe_fn() { +unsafe { _ = malloc(2) } +} + +fn fn_with_defer() { +defer { voidfn() } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/c_struct_init_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/c_struct_init_keep.vv new file mode 100644 index 0000000..f5c93e1 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/c_struct_init_keep.vv @@ -0,0 +1,30 @@ +module main + +fn abc() { + desc := C.sapp_desc{ + width: a.width + height: a.height + user_data: &a + init_userdata_cb: init + frame_userdata_cb: frame + event_userdata_cb: event + window_title: title.str + html5_canvas_name: title.str + cleanup_userdata_cb: cleanup + } +} + +fn init(user_data voidptr) { + desc := C.sg_desc{ + mtl_device: sapp.metal_get_device() + mtl_renderpass_descriptor_cb: sapp.metal_get_renderpass_descriptor + mtl_drawable_cb: sapp.metal_get_drawable + d3d11_device: sapp.d3d11_get_device() + d3d11_device_context: sapp.d3d11_get_device_context() + d3d11_render_target_view_cb: sapp.d3d11_get_render_target_view + d3d11_depth_stencil_view_cb: sapp.d3d11_get_depth_stencil_view + } + sgl_desc := C.sgl_desc_t{ + max_vertices: 50 * 65536 + } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/cast_expected.vv b/v_windows/v/old/vlib/v/fmt/tests/cast_expected.vv new file mode 100644 index 0000000..169f07c --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/cast_expected.vv @@ -0,0 +1,13 @@ +import v.ast { InfixExpr } +import v.table { Type } + +fn test_as() { + _ := sum_expr() as InfixExpr + _ := sum_expr() as ast.PrefixExpr +} + +fn test_cast() { + _ := f32(0) + _ := Type(0) + _ := ast.Expr(sum_expr()) +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/cast_input.vv b/v_windows/v/old/vlib/v/fmt/tests/cast_input.vv new file mode 100644 index 0000000..72b254c --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/cast_input.vv @@ -0,0 +1,14 @@ +import v.ast { InfixExpr } +import v.table { Type } + +fn test_as() { + _ := sum_expr() as ast.InfixExpr + _ := sum_expr() as ast.PrefixExpr +} + +fn test_cast() { + _ := f32(0) + _ := table.Type(0) + _ := ast.Expr(sum_expr()) +} + diff --git a/v_windows/v/old/vlib/v/fmt/tests/chan_init_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/chan_init_keep.vv new file mode 100644 index 0000000..47eaac6 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/chan_init_keep.vv @@ -0,0 +1,27 @@ +import some_mod + +struct ChanFieldType { + c chan some_mod.Type +} + +struct FSMEvent { + x int +} + +fn abc(n FSMEvent) { +} + +fn (e FSMEvent) abc(n FSMEvent) { +} + +fn (e FSMEvent) x(ch chan FSMEvent) { +} + +fn produce_events(ch chan FSMEvent) { +} + +fn main() { + ch_fsm_events := chan FSMEvent{cap: 1000} + eprintln('ch_fsm_events.len: $ch_fsm_events.len') + eprintln('ch_fsm_events.cap: $ch_fsm_events.cap') +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/chan_ops_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/chan_ops_keep.vv new file mode 100644 index 0000000..57ae616 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/chan_ops_keep.vv @@ -0,0 +1,65 @@ +import sync + +const ( + num_iterations = 10000 +) + +struct St { + a int +} + +fn get_val_from_chan(ch chan i64) ?i64 { + r := <-ch ? + return r +} + +fn get_val_from_chan2(ch chan i64) ?i64 { + r := <-ch or { + println('error') + return err + } + return r +} + +// this function gets an array of channels for `i64` +fn do_rec_calc_send(chs []chan i64, sem sync.Semaphore) { + mut msg := '' + for { + mut s := get_val_from_chan(chs[0]) or { + msg = err.str() + break + } + s++ + chs[1] <- s + } + assert msg == 'channel closed' + sem.post() +} + +fn test_channel_array_mut() { + mut chs := [chan i64{}, chan i64{cap: 10}] + sem := sync.new_semaphore() + go do_rec_calc_send(chs, sem) + mut t := i64(100) + for _ in 0 .. num_iterations { + chs[0] <- t + t = <-chs[1] + } + (&sync.Channel(chs[0])).close() + orr := &sync.Channel(chs[0]) + chs[1].close() + ch := chan int{} + ch.close() + a := ch.cap + b := ch.len + c := ch[1].cap + d := ch[o].len + sem.wait() + assert t == 100 + num_iterations + ch2 := chan mut St{cap: 10} + go g(ch2) +} + +fn g(ch chan mut St) { + return +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/chan_or_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/chan_or_keep.vv new file mode 100644 index 0000000..a6e7a90 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/chan_or_keep.vv @@ -0,0 +1,33 @@ +const n = 1000 + +fn f(ch chan int) { + mut s := 0 + for _ in 0 .. n { + s += <-ch or { + println('Something went wrong:') + println('got $err') + } + } + assert s == n * (n + 1) / 2 + ch.close() +} + +fn do_send(ch chan int, val int) ?int { + ch <- val ? + return val + 1 +} + +fn do_send_2(ch chan int, val int) ?int { + ch <- val or { return error('could not send') } + return val + 1 +} + +fn main() { + ch := chan int{} + go f(ch) + mut s := 1 + for { + s = do_send(ch, s) or { break } + } + assert s == n + 1 +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/char_literal_backtick_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/char_literal_backtick_keep.vv new file mode 100644 index 0000000..6979e95 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/char_literal_backtick_keep.vv @@ -0,0 +1,3 @@ +fn char_backtick() { + println(`\``) +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/comments_array_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/comments_array_keep.vv new file mode 100644 index 0000000..74632ed --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/comments_array_keep.vv @@ -0,0 +1,79 @@ +fn main() { + _ := [ + // pre comment + 6, + // and after + ] + _ := [ + 7, + // below expr + ] + _ := [ + 8, /* I don't know why this still is a block comment */ + 9, + ] + arr := [ + // test 0 + 1, + // test 1 + 2, + // test 2 + 3, /* 3 */ + 4, /* 4-1 */ /* 4-2 */ + ] +} + +fn only_comments_array() { + _ := [/* on a single line */ /* too */] + _ := [ + // 1, + // 2, + // 3, + ] + _ := [ + /* whatever */ /* this is */ // 3, + // 4, + ] +} + +fn single_line_array_pre_comments() { + _ := [/* 2, */ 3] + _ := [/* 4, */ /* 5, */ 6] + _ := [/* cmt */ -4] +} + +fn single_line_array_iembed_comments() { + _ := [1, /* betw single line */ 2] + // This caused a bug where the ´-´ was parsed as InfixExpr and not as part of an IntegerLiteral + _ := [1, /* cmt */ -4] +} + +fn mixed_comments() { + _ := [ + 3 /* iembed */, + // keep line comment here + // and here + 5, + ] +} + +fn keep_real_block_comment() { + _ := [ + 'foo', + /* + 'bar', + 'baz', + 'spam', + */ + 'eggs', + ] +} + +fn comment_at_line_start_with_expressions_after() { + arr := [123456789012345, 234567890123456, 678901234567890, 789012345678901, /* at the end */ + 345678901234567, /* in between */ 456789012345678, + /* line start */ 567890123456789, 890123456789012] + arr2 := [/* same line pre comment */ Foo{ + a: 123 + }] +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/comments_expected.vv b/v_windows/v/old/vlib/v/fmt/tests/comments_expected.vv new file mode 100644 index 0000000..859908f --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/comments_expected.vv @@ -0,0 +1,105 @@ +import time // foo + +/* +block +comment +*/ + +fn fun() int { + // comment zero + return 0 // another comment +} + +fn mr_fun() (int, int) { + // one comment + // another comment + return 1, 2 +} + +fn single_line_blocks() { + // 1 + println('') + // 2 + println('') + // 3 + // 4 + println('') + // 5 + // 6 +} + +fn main() { + /* + block1 + */ + /* + block2 + */ + /* + block3 + */ + // this is a comment + a := 1 + // and another comment + // just to make it worse + b, c := a, 2 + d := c // and an extra one + e := c + // more comments = more good + arr := [ + // block foo bar + // inline foo bar + 0, + ] + // before arg comment + // after arg comment + println('this is a test') + // before if expr + // after if expr + if true { + println('if') + } + // before else if + // between else if + else if false { + println('else if') + } + // before else + // after else + else { + println('else') + } + // empty return + return +} + +fn insert_space() { + // abc +} + +fn linebreaks_in_block_comments() { + /* + foo + comment goes here! + bar + */ + /* + spam + spaces make no difference there + eggs + */ +} + +fn between_if_branches() { + if spam { + } + // remove the empty line above + else if eggs { + } + + if spam2 { + } + // remove the empty line below + else { + } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/comments_input.vv b/v_windows/v/old/vlib/v/fmt/tests/comments_input.vv new file mode 100644 index 0000000..e67d070 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/comments_input.vv @@ -0,0 +1,89 @@ +import time // foo +/* +block +comment +*/ + +fn fun() int { + return /* comment zero */ 0 // another comment +} + +fn mr_fun() (int, int) { + return /* one comment */ 1, /* another comment */ 2 +} + +fn single_line_blocks() { + /* 1 */ + println('') + /* 2 */ + println('') + /* 3 */ + /* 4 */ + println('') + // 5 + /* 6 */ +} + +fn main() { + /* block1 + */ + /* + block2 */ + /* + + block3 + + */ + a := /* this is a comment */ 1 + b, c := /* and another comment */ a, /* just to make it worse */ 2 + d := c // and an extra one + e := c + // more comments = more good + arr := [ + /* block foo bar */ + // inline foo bar + 0, + ] + println(/* before arg comment */ 'this is a test' /* after arg comment */) + if /* before if expr */ true /* after if expr */ { + println('if') + } + // before else if + else /* between else if */ if false { + println('else if') + } + // before else + else /* after else */ { + println('else') + } + return // empty return +} + +fn insert_space() { + //abc +} + +fn linebreaks_in_block_comments() { + /*foo + comment goes here! + bar*/ + /* spam + spaces make no difference there + eggs */ +} + +fn between_if_branches() { + if spam { + } + + // remove the empty line above + else if eggs { + } + + if spam2 { + } + // remove the empty line below + + else { + } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/comments_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/comments_keep.vv new file mode 100644 index 0000000..a581eb2 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/comments_keep.vv @@ -0,0 +1,101 @@ +import semver // as sv + +enum Abc { + a + b // after a value + c + // between values + d +} + +struct User { + name string // name + // middle comment + age int + // last comment + // last comment2 +} + +fn main() { + u := User{ + name: 'Peter' + } + n := sizeof(User) + // else + // else { + // } + _ := User{ + name: 'Henry' // comment after name + // on the next line + age: 42 + // after age line + // after line2 + } + _ := User{ + // Just a comment + } + ////// + // / + // 123 + match 0 { + 0 { + 0 // comment after an expression inside match + } + else {} + } +} + +fn assign_comments() { + a := 123 // comment after assign + b := 'foo' // also comment after assign + c := true + // Between two assigns + d := false + // at the end +} + +fn linebreaks_in_ascii_art_block_comments() { + /* + +++ + */ + /***** + +++ + *****/ + /**** + +++ + */ + /* + +++ + ****/ +} + +fn map_comments() { + mymap := map{ + // pre + `:`: 1 + `!`: 2 // after + // and between + `%`: 3 + // between + // between second + `$`: 4 + `&`: 5 + // post + } +} + +fn ifs_comments_and_empty_lines() { + if true { + } + // some comment direct after an if without else + if false { + } else { + } + // some comment direct after an else + if false { + } + + // this is parsed as post_comment of the if but does not really belong there + // thereore keep the empty line + something_else() +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/comptime_field_selector_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/comptime_field_selector_keep.vv new file mode 100644 index 0000000..e9752de --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/comptime_field_selector_keep.vv @@ -0,0 +1,23 @@ +struct Foo { +mut: + test string + name string +} + +fn (f Foo) print() { + println('test') +} + +fn test() { + mut t := T{} + t.name = '2' + $for f in T.fields { + $if f.typ is string { + println(t.$(f.name)) + } + } +} + +fn main() { + test() +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/comptime_field_selector_parentheses_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/comptime_field_selector_parentheses_keep.vv new file mode 100644 index 0000000..e9752de --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/comptime_field_selector_parentheses_keep.vv @@ -0,0 +1,23 @@ +struct Foo { +mut: + test string + name string +} + +fn (f Foo) print() { + println('test') +} + +fn test() { + mut t := T{} + t.name = '2' + $for f in T.fields { + $if f.typ is string { + println(t.$(f.name)) + } + } +} + +fn main() { + test() +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/comptime_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/comptime_keep.vv new file mode 100644 index 0000000..987e513 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/comptime_keep.vv @@ -0,0 +1,91 @@ +import vweb + +struct App { + a string + b string +mut: + c int + d f32 +pub: + e f32 + f u64 +pub mut: + g string + h byte +} + +fn comptime_for() { + println(@FN) + $for method in App.methods { + println(' method: $method.name | $method') + } +} + +fn comptime_for_with_if() { + println(@FN) + $for method in App.methods { + println(' method: $method') + $if method.typ is fn () { + assert method.name in ['run', 'method2'] + } + $if method.return_type is int { + assert method.name in ['int_method1', 'int_method2'] + } + $if method.args[0].typ is string { + assert method.name == 'my_method' + } + } +} + +fn comptime_for_fields() { + println(@FN) + $for field in App.fields { + println(' field: $field.name | $field') + $if field.typ is string { + assert field.name in ['a', 'b', 'g'] + } + $if field.typ is f32 { + assert field.name in ['d', 'e'] + } + if field.is_mut { + assert field.name in ['c', 'd', 'g', 'h'] + } + if field.is_pub { + assert field.name in ['e', 'f', 'g', 'h'] + } + if field.is_pub && field.is_mut { + assert field.name in ['g', 'h'] + } + } +} + +struct Result {} + +fn (mut a App) my_method(p string) Result { + println('>>>> ${@FN} | p: $p') + return Result{} +} + +fn handle_conn(mut app T) { + $for method in T.methods { + $if method.return_type is Result { + app.$method('abc', 'def') + } + } +} + +fn comptime_call_dollar_method() { + mut app := App{} + handle_conn(mut app) +} + +fn (mut app App) create() vweb.Result { + return $vweb.html() +} + +fn main() { + comptime_for() + comptime_for_with_if() + comptime_for_fields() + comptime_call_dollar_method() +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/concat_expr_expected.vv b/v_windows/v/old/vlib/v/fmt/tests/concat_expr_expected.vv new file mode 100644 index 0000000..47d5872 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/concat_expr_expected.vv @@ -0,0 +1,24 @@ +fn concatenation_of_strings() { + _ := 'Simple' + 'Concat' + _ := 'Hello' + ' ' + 'World' + '!' + _ := 'There' + ' ' + 'so' + ' ' + 'many' + ' ' + 'words' + ' ' + 'they' + ' ' + "don't" + ' ' + + 'fit' + ' ' + 'in' + ' ' + 'one' + ' ' + 'line' +} + +fn concat_inside_ternary() { + { // This block is needed to force line wrapping + cline := if iline == pos.line_nr { + sline[..start_column] + color(kind, sline[start_column..end_column]) + + sline[end_column..] + } else { + sline + } + } +} + +fn concat_two_or_more_ternaries() { + x := if some_condition { 'very long string part' } else { 'def' } + + if y == 'asd' { 'qwe' } else { 'something else' } + var := if other_condition { 'concat three long ternary ifs' } else { 'def' } + + if true { 'shorter' } else { 'quite short' } + if true { 'small' } else { 'tiny' } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/concat_expr_input.vv b/v_windows/v/old/vlib/v/fmt/tests/concat_expr_input.vv new file mode 100644 index 0000000..383c52e --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/concat_expr_input.vv @@ -0,0 +1,22 @@ +fn concatenation_of_strings() { +_ := 'Simple' + 'Concat' + _ := 'Hello' + + ' ' + + 'World' + '!' + _ := 'There' + ' ' + 'so' + ' ' + 'many' + ' ' + 'words' + ' ' + 'they' + ' ' + "don't" + ' ' + 'fit' + ' ' + 'in' + ' ' + 'one' + ' ' + 'line' +} + +fn concat_inside_ternary() { + { // This block is needed to force line wrapping + cline := if iline == pos.line_nr { + sline[..start_column] + color(kind, sline[start_column..end_column]) + sline[end_column..] + } else { + sline + } + } +} + +fn concat_two_or_more_ternaries() { + x := if some_condition { 'very long string part' } else { 'def' } + if y == 'asd' { 'qwe' } else {'something else'} + var := if other_condition { 'concat three long ternary ifs' } else { 'def' } + if true {'shorter'} else {'quite short'} + if true { 'small' } else {'tiny'} +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/conditional_compilation_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/conditional_compilation_keep.vv new file mode 100644 index 0000000..3d7e1d2 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/conditional_compilation_keep.vv @@ -0,0 +1,19 @@ +// __global g_my_global_variable int +fn main() { + $if tinyc { + println('This will be compiled only when the compiler is tcc') + } + $if !windows { + println('This will get compiled on non-windows platforms.') + } + // + $if !linux { + println('Only non linux platforms will get this') + } $else { + println('This part is for linux only.') + } + // + $if network ? { + println('This will be run, only when the program is compiled with `-d network`') + } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/conditions_expected.vv b/v_windows/v/old/vlib/v/fmt/tests/conditions_expected.vv new file mode 100644 index 0000000..cee2823 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/conditions_expected.vv @@ -0,0 +1,12 @@ +fn fn_with_if_else() { + if true { + a = 10 + a++ + } else { + println('false') + } +} + +fn fn_with_if_else_oneline() { + _ := if true { 1 } else { 2 } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/conditions_input.vv b/v_windows/v/old/vlib/v/fmt/tests/conditions_input.vv new file mode 100644 index 0000000..e38b836 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/conditions_input.vv @@ -0,0 +1,10 @@ +fn fn_with_if_else() { + if true { + a = 10 + a++ + } else { println('false') } +} + +fn fn_with_if_else_oneline() { +_ := if true { 1 } else { 2 } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/consts_expected.vv b/v_windows/v/old/vlib/v/fmt/tests/consts_expected.vv new file mode 100644 index 0000000..b97e49d --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/consts_expected.vv @@ -0,0 +1,60 @@ +const ( + // pi + // pi + pi = 3.14 + // phi + // phi + // phi + phi = 1.618 + // Euler's constant + eulers = 2.7182 + supported_platforms = ['windows', 'macos', 'linux', 'freebsd', 'openbsd', 'netbsd', 'dragonfly', + 'android', 'js', 'solaris', 'haiku'] + one_line_supported = ['windows', 'macos', 'linux', 'freebsd', 'openbsd', 'netbsd', 'dragonfly', + 'android', 'js', 'solaris', 'haiku'] + another_const = ['a', 'b', 'c', 'd', 'e', 'f'] + multiline_const = [ + 'first line', + 'second line', + 'third line', + 'fourth line', + ] +) + +const ( + i_am_a_very_long_constant_name_so_i_stand_alone_and_my_length_is_over_90_characters = [ + 'testforit', + ] +) + +pub const ( + i_am_pub_const = true +) + +fn main() { + a := [ + [3, 5, 6], + [7, 9, 2], + ] + b := [[ + [2, 5, 8], + [5, 1, 3], + [2, 6, 0], + ], [ + [9, 4, 5], + [7, 2, 3], + [1, 2, 3], + ]] + c := [ + [ + [2, 5, 8], + [5, 1, 3], + [2, 6, 0], + ], + [ + [9, 4, 5], + [7, 2, 3], + [1, 2, 3], + ], + ] +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/consts_input.vv b/v_windows/v/old/vlib/v/fmt/tests/consts_input.vv new file mode 100644 index 0000000..e16942a --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/consts_input.vv @@ -0,0 +1,54 @@ +const ( +// pi +// pi +pi=3.14 +// phi +// phi +// phi +phi=1.618 + //Euler's constant +eulers=2.7182 +supported_platforms = ['windows', 'macos', 'linux', 'freebsd', 'openbsd', + 'netbsd', 'dragonfly', 'android', 'js', 'solaris', 'haiku'] +one_line_supported = ['windows', 'macos', 'linux', 'freebsd', 'openbsd', 'netbsd', 'dragonfly', 'android', 'js', 'solaris', 'haiku'] +another_const = ['a', 'b' + 'c', 'd', 'e' + 'f' + ] +multiline_const = [ + 'first line', 'second line','third line', + 'fourth line'] +) + +const ( + i_am_a_very_long_constant_name_so_i_stand_alone_and_my_length_is_over_90_characters = ['testforit'] +) + +pub const ( +i_am_pub_const=true +) + +fn main() { + a := [ + [3, +5, + 6],[7, 9, 2]] +b := [[ +[2, +5,8],[ 5, 1, +3],[ 2, 6, 0]],[ +[9, +4,5],[ +7,2,3], +[1, +2,3]]] +c := [ +[ +[2, +5,8],[ 5, 1, +3],[ 2, 6, 0]],[[ +9, +4,5],[7,2,3], +[1, +2,3]]] +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/consts_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/consts_keep.vv new file mode 100644 index 0000000..c505ad7 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/consts_keep.vv @@ -0,0 +1,27 @@ +const font = $embed_file('../assets/fonts/RobotoMono-Regular.ttf') + +const ( + test_alignment = 123 + foo = Foo{ + f: 'foo' + } + spam = 456 + egg = 'lorem ipsum' + spameggs = true +) + +const ( + bar = 'A string +spanning multiple +lines' + baz = 'short' + some_long_name = MyStruct{ + x: 42 + } +) + +const ( + a = 123 + abc = 123 + b = 123 +) diff --git a/v_windows/v/old/vlib/v/fmt/tests/consts_with_comments_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/consts_with_comments_keep.vv new file mode 100644 index 0000000..2f8a221 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/consts_with_comments_keep.vv @@ -0,0 +1,4 @@ +const ( + fsm_state_array = ['init', 'state_a', 'state_b', 'state_c', 'exit'] // use as a first half key for map see fsm_state_ev_fn, the same order as in enum FSM_state + fsm_event_array = ['ev1', 'ev2', 'ev3', 'ev4', 'ev5'] // use as a second half key for map see fsm_state_ev_fn, the same order as in enum FSM_event +) diff --git a/v_windows/v/old/vlib/v/fmt/tests/embed_file_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/embed_file_keep.vv new file mode 100644 index 0000000..2234bd7 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/embed_file_keep.vv @@ -0,0 +1,8 @@ +fn main() { + mut the_png := $embed_file('v.png') + println(the_png) + content := the_png.data() + eprintln('content: ${ptr_str(content)}') + eprintln(unsafe { the_png.data().vbytes(the_png.len) }.hex()) + println(the_png) +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/empty_curlies_and_parens_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/empty_curlies_and_parens_keep.vv new file mode 100644 index 0000000..30e4420 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/empty_curlies_and_parens_keep.vv @@ -0,0 +1,44 @@ +const () + +const ( +) + +struct Bar {} + +struct Bar2 { +} + +interface Spam {} + +interface Spam2 { +} + +enum Baz {} + +enum Baz2 { +} + +fn foo() {} + +fn foo2() { +} + +fn main() { + arr := []int{} + x := 1 + for s in arr {} + for s in arr { + } + for i := 0; i < 5; i++ {} + for j := 0; j < 5; j++ { + } + for false {} + for false { + } + defer {} + defer { + } + unsafe {} + unsafe { + } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/empty_lines_expected.vv b/v_windows/v/old/vlib/v/fmt/tests/empty_lines_expected.vv new file mode 100644 index 0000000..605c552 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/empty_lines_expected.vv @@ -0,0 +1,28 @@ +type MyInt = int +type EmptyLineAfterLastType = fn () int + +struct EmptyLineAfterStructs {} + +fn empty_line_after_functions() {} + +fn squash_multiple_empty_lines() { + println('a') + + println('b') + + c := 0 + + d := 0 +} + +fn remove_leading_and_trailing_empty_lines() { + println('a') + + println('b') + + if test { + c := 0 + } else { + d := 0 + } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/empty_lines_input.vv b/v_windows/v/old/vlib/v/fmt/tests/empty_lines_input.vv new file mode 100644 index 0000000..f8eee66 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/empty_lines_input.vv @@ -0,0 +1,34 @@ +type MyInt = int +type EmptyLineAfterLastType = fn() int +struct EmptyLineAfterStructs {} +fn empty_line_after_functions() {} +fn squash_multiple_empty_lines() { + println('a') + + + println('b') + + + c := 0 + + + d := 0 +} + +fn remove_leading_and_trailing_empty_lines() { + + println('a') + + println('b') + + if test { + + c := 0 + + } else { + + d := 0 + + } + +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/empty_lines_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/empty_lines_keep.vv new file mode 100644 index 0000000..afcd1e5 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/empty_lines_keep.vv @@ -0,0 +1,98 @@ +// Keep empty lines between types +type Expr = IfExpr | IntegerLiteral + +type Node2 = Expr | string +type MyInt = int + +// Keep empty lines in const blocks +const ( + _ = SomeStruct{ + val: 'Multiline field exprs should cause no problems' + } + _ = 1 + + _ = 'Keep this empty line before' + // Comment before a field + _ = 2 + + // The empty line above should stay too + _ = 3 + + // This comment doesn't really belong anywhere + + _ = 'A multiline string with a StringInterLiteral... + $foo + ...and the ending brace on a newline had a wrong pos.last_line. + ' + _ = 4 +) + +fn keep_single_empty_line() { + println('a') + println('b') + + println('c') + + d := 0 + + if true { + println('e') + } + + f := 0 + arr << MyStruct{} + + arr2 := [1, 2] +} + +fn prevent_empty_line_after_multi_line_statements() { + // line1 + /* + block1 + */ + /* + block2 + */ + if test { + println('a') + } + println('b') + for test { + println('c') + } + c := fn (s string) { + println('s') + } +} + +fn between_orm_blocks() { + sql db { + insert upper_1 into Upper + } + + upper_s := sql db { + select from Upper where id == 1 + } +} + +fn no_empty_lines() { + _ := $embed_file('testy.v') + mut files := map[string][]FileData{} + code := 'foo +bar' + params[0] = { + ...params[0] + typ: params[0].typ.set_nr_muls(1) + } + env_value = environ()[env_lit] or { + return error('the environment variable "$env_lit" does not exist.') + } + assert '$mr_one_two()' == "(One{ + value: 'one' +}, Two{ + value: 'two' +})" + r := m[key] ? + compile_time_env := $env('ENV_VAR') + func() +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/enum_comments_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/enum_comments_keep.vv new file mode 100644 index 0000000..f46208b --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/enum_comments_keep.vv @@ -0,0 +1,17 @@ +module main + +// Top level comment +enum EnumType { + // comment for the enum + abc + xyz // comment for the enum field +} + +// Another top level comment +enum PacketType { + // Regular data packet + data // abc def + // xyz + // another comment + abcd // jklmn +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/enums_expected.vv b/v_windows/v/old/vlib/v/fmt/tests/enums_expected.vv new file mode 100644 index 0000000..8c30ec2 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/enums_expected.vv @@ -0,0 +1,9 @@ +pub enum PubEnum { + foo + bar +} + +enum PrivateEnum { + foo + bar +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/enums_input.vv b/v_windows/v/old/vlib/v/fmt/tests/enums_input.vv new file mode 100644 index 0000000..c7af1d6 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/enums_input.vv @@ -0,0 +1,5 @@ +pub enum PubEnum{ + foo + bar +} +enum PrivateEnum { foo bar } diff --git a/v_windows/v/old/vlib/v/fmt/tests/expressions_expected.vv b/v_windows/v/old/vlib/v/fmt/tests/expressions_expected.vv new file mode 100644 index 0000000..1498e2e --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/expressions_expected.vv @@ -0,0 +1,103 @@ +import v.checker +import v.ast +import v.table +import v.gen +import v.token + +fn string_inter_lit(mut c checker.Checker, mut node ast.StringInterLiteral) table.Type { + for i, expr in node.exprs { + ftyp := c.expr(expr) + node.expr_types << ftyp + typ := c.table.unalias_num_type(ftyp) + mut fmt := node.fmts[i] + // analyze and validate format specifier + if fmt !in [`E`, `F`, `G`, `e`, `f`, `g`, `d`, `u`, `x`, `X`, `o`, `c`, `s`, `p`, `_`] { + c.error('unknown format specifier `${fmt:c}`', node.fmt_poss[i]) + } + if node.precisions[i] != 987698 && !typ.is_float() { + c.error('precision specification only valid for float types', node.fmt_poss[i]) + } + if node.pluss[i] && !typ.is_number() { + c.error('plus prefix only allowd for numbers', node.fmt_poss[i]) + } + if (typ.is_unsigned() && fmt !in [`u`, `x`, `X`, `o`, `c`]) + || (typ.is_signed() && fmt !in [`d`, `x`, `X`, `o`, `c`]) + || (typ.is_int_literal() && fmt !in [`d`, `c`, `x`, `X`, `o`, `u`, `x`, `X`, `o`]) + || (typ.is_float() && fmt !in [`E`, `F`, `G`, `e`, `f`, `g`]) + || (typ.is_pointer() && fmt !in [`p`, `x`, `X`]) + || (typ.is_string() && fmt != `s`) + || (typ.idx() in [table.i64_type_idx, table.f64_type_idx] && fmt == `c`) { + c.error('illegal format specifier `${fmt:c}` for type `${c.table.get_type_name(ftyp)}`', + node.fmt_poss[i]) + } + node.need_fmts[i] = fmt != c.get_default_fmt(ftyp, typ) + } + + return table.string_type +} + +fn get_some_val(a_test f64, b_test f64, c_test f64, d_test f64, e_test f64, f_test f64) f64 { + return a_test * b_test * c_test * d_test + e_test * f_test * a_test * d_test + + a_test * b_test * c_test +} + +fn main() { + a, b, r, d := 5.3, 7.5, 4.4, 6.6 + if a + b + r * d + a + b + r * d > a + b + r * d + a * b + r { + println('ok') + } + v_str := 'v' + s := []string{} + s << ' `$v_str`' + println(s) + println('this is quite a long string' + + ' that is followd by an even longer part that should go to another line') + if (a == b && b > r) || (d > r) || (a < b) || (b < d && a + b > r) + || (a + b + d >= 0 && r < 0) || (a > b && d - r < b) { + println('ok') + } +} + +fn gen_str_for_multi_return(mut g gen.Gen, info table.MultiReturn, styp string, str_fn_name string) { + for i, _ in info.types { + println('\tstrings__Builder_write(&sb, _STR("\'%.*s\\000\'", 2, a.arg$i));') + } +} + +struct Parser { + peek_tok token.Token + peek_tok2 token.Token + peek_tok3 token.Token +} + +fn (mut p Parser) name_expr() { + if p.peek_tok.kind == .lpar + || (p.peek_tok.kind == .lt && p.peek_tok2.kind == .name && p.peek_tok3.kind == .gt) { + println(p.peek_tok.lit) + } +} + +fn set_nr_muls(t table.Type, nr_muls int) table.Type { + return int(t) & 0xff00ffff | (nr_muls << 16) +} + +// Test what exprs are treated as multiline. The ternary if only functions as a wrapper. +// When one expr in a branch doesn't fit a single line, the whole if will be unwrapped. +fn multiline_exprs() { + // StructInit with at least one field + _ := if true { + Foo{} + } else { + Foo{ + val: 123 + } + } + // ConcatExpr with a multiline child expr + _, _ := if true { + 1, Foo{ + val: 123 + } + } else { + 2, Foo{} + } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/expressions_input.vv b/v_windows/v/old/vlib/v/fmt/tests/expressions_input.vv new file mode 100644 index 0000000..dec30ce --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/expressions_input.vv @@ -0,0 +1,102 @@ +import v.checker +import v.ast +import v.table +import v.gen +import v.token + +fn string_inter_lit(mut c checker.Checker, mut node ast.StringInterLiteral) table.Type { + for i, expr in node.exprs { + ftyp := c.expr(expr) + node.expr_types << ftyp + typ := c.table.unalias_num_type(ftyp) + mut fmt := node.fmts[i] + // analyze and validate format specifier + if fmt !in [`E`, `F`, `G`, `e`, `f`, `g`, + `d`, `u`, `x`, `X`, `o`, `c`, `s`, `p`, `_`] { + c.error('unknown format specifier `${fmt:c}`', + node.fmt_poss[i]) + } + if node.precisions[i] != 987698 && + !typ.is_float() { + c.error('precision specification only valid for float types', + node.fmt_poss[i]) + } + if node.pluss[i] && !typ.is_number() { + c.error('plus prefix only allowd for numbers', node.fmt_poss[i]) + } + if (typ.is_unsigned() && fmt !in [`u`, `x`, `X`, `o`, `c`]) || (typ.is_signed() && + fmt !in [`d`, `x`, `X`, `o`, `c`]) || (typ.is_int_literal() + && fmt !in [`d`, `c`, `x`, `X`, `o`, + `u`, `x`, `X`, `o`]) || (typ.is_float() && fmt !in [`E`, `F`, + `G`, `e`, `f`, `g`]) || (typ.is_pointer() && + fmt !in [`p`, `x`, `X`]) || (typ.is_string() && fmt != `s`) + || (typ.idx() in [table.i64_type_idx, + table.f64_type_idx + ] && fmt == `c`) { + c.error('illegal format specifier `${fmt:c}` for type `${c.table.get_type_name(ftyp)}`', + node.fmt_poss[i]) + } + node.need_fmts[i] = fmt != c.get_default_fmt(ftyp, typ) + } + + return table.string_type +} + +fn get_some_val(a_test, b_test, c_test, d_test, e_test, f_test f64) f64 { + return a_test*b_test*c_test* + d_test+e_test*f_test*a_test*d_test+a_test* + b_test*c_test +} + +fn main() { + a, b, r, d := 5.3, 7.5, 4.4, 6.6 + if a+b+ + r*d+a+b+r* + d > a+b+r*d+ + a*b+r { + println('ok') + } + v_str := 'v' + s := []string{} + s << ' `$v_str`' + println(s) + println('this is quite a long string' + ' that is followd by an even longer part that should go to another line') + if (a == b && b > r) || (d > r) || (a < b) || (b< d && a+b > r) || (a+b+d >= 0 && r < 0) || (a > b && d-r < b) { + println('ok') + } +} + +fn gen_str_for_multi_return(mut g gen.Gen, + info table.MultiReturn, styp, str_fn_name string) { +for i, _ in info.types { + println('\tstrings__Builder_write(&sb, _STR("\'%.*s\\000\'", 2, a.arg$i));') + } +} + +struct Parser { + peek_tok token.Token + peek_tok2 token.Token + peek_tok3 token.Token +} + +fn (mut p Parser) name_expr() { + if p.peek_tok.kind == + .lpar || (p.peek_tok.kind == .lt && p.peek_tok2.kind == .name && + p.peek_tok3.kind == .gt) { + println(p.peek_tok.lit) + } +} + +fn set_nr_muls(t table.Type, nr_muls int) table.Type { + return int(t) & + 0xff00ffff | (nr_muls << 16) +} + +// Test what exprs are treated as multiline. The ternary if only functions as a wrapper. +// When one expr in a branch doesn't fit a single line, the whole if will be unwrapped. +fn multiline_exprs() { + // StructInit with at least one field + _ := if true { Foo{} } else { Foo{ val: 123} } + // ConcatExpr with a multiline child expr + _, _ := if true { 1, Foo{val: 123} } else { 2, Foo{} } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/file_with_just_imports_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/file_with_just_imports_keep.vv new file mode 100644 index 0000000..122510a --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/file_with_just_imports_keep.vv @@ -0,0 +1,3 @@ +module proto +import emily33901.vproto + diff --git a/v_windows/v/old/vlib/v/fmt/tests/fixed_size_array_type_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/fixed_size_array_type_keep.vv new file mode 100644 index 0000000..fbe0001 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/fixed_size_array_type_keep.vv @@ -0,0 +1,28 @@ +const size = 5 + +struct Foo { + bar [size]int + baz [5]int +} + +fn foo() [1]f32 { + return [f32(0.0)]! +} + +fn main() { + _ := [5]string{init: 'abc'} +} + +// NB: secret_key_size is missing here on purpose +// vfmt should leave it as is, assuming it is comming +// from another .v file + +struct VerifyKey { + public_key [public_key_size]byte +} + +struct SigningKey { + secret_key [secret_key_size]byte +pub: + verify_key VerifyKey +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/fn_headers_with_no_bodies_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/fn_headers_with_no_bodies_keep.vv new file mode 100644 index 0000000..98d0476 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/fn_headers_with_no_bodies_keep.vv @@ -0,0 +1,5 @@ +fn proc_pidpath(int, voidptr, int) int + +fn C.realpath(&char, &char) &char + +fn C.chmod(&byte, int) int diff --git a/v_windows/v/old/vlib/v/fmt/tests/fn_multi_return_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/fn_multi_return_keep.vv new file mode 100644 index 0000000..c7f343a --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/fn_multi_return_keep.vv @@ -0,0 +1,13 @@ +import v.ast + +fn return_multiple_values() (int, int) { + return 0, 1 +} + +fn return_multiple_values_opt() ?(int, int) { + return none +} + +fn return_multiple_values_with_type_from_other_module() (ast.File, int) { + return ast.File{}, 0 +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/fn_return_generic_struct_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/fn_return_generic_struct_keep.vv new file mode 100644 index 0000000..b85781e --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/fn_return_generic_struct_keep.vv @@ -0,0 +1,39 @@ +pub struct Optional { +mut: + value T + some bool +} + +pub struct Foo { + foo int +} + +pub fn (f Foo) new_some(value T) Optional { + return { + value: value + some: true + } +} + +pub fn (f Foo) some(opt Optional) bool { + return opt.some +} + +pub fn (f Foo) get(opt Optional) T { + return opt.value +} + +pub fn (f Foo) set(mut opt Optional, value T) { + opt.value = value + opt.some = true +} + +fn main() { + foo := Foo{} + mut o := foo.new_some(23) + println(foo.some(o)) + assert foo.some(o) == true + foo.set(mut o, 42) + println(foo.get(o)) + assert foo.get(o) == 42 +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/fn_trailing_arg_syntax_expected.vv b/v_windows/v/old/vlib/v/fmt/tests/fn_trailing_arg_syntax_expected.vv new file mode 100644 index 0000000..255d5ca --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/fn_trailing_arg_syntax_expected.vv @@ -0,0 +1,50 @@ +struct Bar { + x string + y int + b Baz +} + +struct Baz { + x int + y int +} + +fn main() { + bar_func(x: 'this line is short enough', y: 13) + bar_func( + x: 'a very long content should cause vfmt to use multiple lines instead of one.' + y: 123456789 + ) + bar_func( + x: 'some string' + b: Baz{ + x: 0 + y: 0 + } + ) + bar2_func({}) + bar2_func({ x: 's' }, + x: 's' + ) + baz_func('foo', 'bar', + x: 0 + y: 0 + ) + ui.row( + // stretch: true + margin: { + top: 10 + left: 10 + right: 10 + bottom: 10 + } + ) +} + +fn bar_func(bar Bar) { +} + +fn bar2_func(bar1 Bar, bar2 Bar) { +} + +fn baz_func(a string, b string, baz Baz) {} diff --git a/v_windows/v/old/vlib/v/fmt/tests/fn_trailing_arg_syntax_input.vv b/v_windows/v/old/vlib/v/fmt/tests/fn_trailing_arg_syntax_input.vv new file mode 100644 index 0000000..1101619 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/fn_trailing_arg_syntax_input.vv @@ -0,0 +1,36 @@ +struct Bar { + x string + y int + b Baz +} + +struct Baz { + x int + y int +} + +fn main() { + bar_func(x: 'this line is short enough', y: 13) + bar_func(x: 'a very long content should cause vfmt to use multiple lines instead of one.', y: 123456789) + bar_func(x: 'some string', b: Baz{ + x: 0 + y: 0 + }) + bar2_func({}) + bar2_func({x: 's'}, {x: 's'}) + baz_func('foo', 'bar', x: 0 + y: 0 + ) + ui.row({ + //stretch: true + margin: {top:10,left:10,right:10,bottom:10} + }) +} + +fn bar_func(bar Bar) { +} + +fn bar2_func(bar1 Bar, bar2 Bar) { +} + +fn baz_func(a string, b string, baz Baz) {} diff --git a/v_windows/v/old/vlib/v/fmt/tests/fn_trailing_arg_syntax_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/fn_trailing_arg_syntax_keep.vv new file mode 100644 index 0000000..9a9d98c --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/fn_trailing_arg_syntax_keep.vv @@ -0,0 +1,57 @@ +type Foo = Bar | Baz + +struct Bar { + x string + y int + z int + a int +} + +struct Baz { + x string +} + +fn bar_func(bar Bar) {} + +fn foo_func(f Foo) {} + +fn main() { + bar_func(x: 'bar', y: 13, z: 42) + bar_func( + x: 'bar' + y: 13 + z: 42 + ) + foo_func(Baz{ + x: 'Baz as Foo sumtype' + }) + func_from_other_file(val: 'something') + bar_func( + // pre comment + x: 'struct has a pre comment' + ) + bar_func( + x: 'first field' + // comment between fields + y: 100 + ) + bar_func( + x: 'Look! A comment to my right.' // comment after field + ) + func_from_other_file( + xyz: AnotherStruct{ + f: 'here' + } + ) + ui.button( + width: 70 + onclick: fn (a voidptr, b voidptr) { + webview.new_window(url: 'https://vlang.io', title: 'The V programming language') + } + ) +} + +fn trailing_struct_with_update_expr() { + c.error('duplicate const `$field.name`', { ...field.pos, len: name_len }) + c.error('duplicate const `$field.name`', Position{ ...field.pos, len: name_len }) +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/fn_with_anon_params_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/fn_with_anon_params_keep.vv new file mode 100644 index 0000000..ab6a13a --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/fn_with_anon_params_keep.vv @@ -0,0 +1 @@ +fn C.PQgetvalue(voidptr, int, int) &byte diff --git a/v_windows/v/old/vlib/v/fmt/tests/fntype_alias_array_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/fntype_alias_array_keep.vv new file mode 100644 index 0000000..080fe9a --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/fntype_alias_array_keep.vv @@ -0,0 +1,4 @@ +type MyFn = fn (int) int + +mut arr := []MyFn{} +arr << MyFn(test) diff --git a/v_windows/v/old/vlib/v/fmt/tests/fntype_alias_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/fntype_alias_keep.vv new file mode 100644 index 0000000..8e530a7 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/fntype_alias_keep.vv @@ -0,0 +1,65 @@ +module ui + +import gg +import gx +import eventbus + +pub type DrawFn = fn (ctx &gg.Context, state voidptr) + +pub type ClickFn = fn (e MouseEvent, func voidptr) + +pub type KeyFn = fn (e KeyEvent, func voidptr) + +pub type ScrollFn = fn (e ScrollEvent, func voidptr) + +pub type MouseMoveFn = fn (e MouseMoveEvent, func voidptr) + +[heap] +pub struct Window { +pub mut: + ui &UI = voidptr(0) + children []Widget + child_window &Window = voidptr(0) + parent_window &Window = voidptr(0) + has_textbox bool // for initial focus + tab_index int + just_tabbed bool + state voidptr + draw_fn DrawFn + title string + mx f64 + my f64 + width int + height int + bg_color gx.Color + click_fn ClickFn + mouse_down_fn ClickFn + mouse_up_fn ClickFn + scroll_fn ScrollFn + key_down_fn KeyFn + char_fn KeyFn + mouse_move_fn MouseMoveFn + eventbus &eventbus.EventBus = eventbus.new() +} + +pub struct WindowConfig { +pub: + width int + height int + resizable bool + title string + always_on_top bool + state voidptr + draw_fn DrawFn + bg_color gx.Color = default_window_color + on_click ClickFn + on_mouse_down ClickFn + on_mouse_up ClickFn + on_key_down KeyFn + on_scroll ScrollFn + on_mouse_move MouseMoveFn + children []Widget + font_path string + // pub mut: + // parent_window &Window +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/fntype_mut_args_with_optional_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/fntype_mut_args_with_optional_keep.vv new file mode 100644 index 0000000..efb2685 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/fntype_mut_args_with_optional_keep.vv @@ -0,0 +1 @@ +pub type SocketMessageFn = fn (mut c Client, msg &Message) ? diff --git a/v_windows/v/old/vlib/v/fmt/tests/fntype_return_optional_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/fntype_return_optional_keep.vv new file mode 100644 index 0000000..35c829e --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/fntype_return_optional_keep.vv @@ -0,0 +1,3 @@ +type Foo = fn (a int) ? + +type Foo2 = fn (num int) ?int diff --git a/v_windows/v/old/vlib/v/fmt/tests/functions_expected.vv b/v_windows/v/old/vlib/v/fmt/tests/functions_expected.vv new file mode 100644 index 0000000..1d88e63 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/functions_expected.vv @@ -0,0 +1,68 @@ +fn C.func(arg int) int + +fn fn_variadic(arg int, args ...string) { + println('Do nothing') +} + +fn fn_with_assign_stmts() { + _, _ := fn_with_multi_return() +} + +fn fn_with_multi_return() (int, string) { + return 0, 'test' +} + +fn voidfn() { + println('this is a function that does not return anything') +} + +fn fn_with_1_arg(arg int) int { + return 0 +} + +fn fn_with_2a_args(arg1 int, arg2 int) int { + return 0 +} + +fn fn_with_2_args_to_be_shorten(arg1 int, arg2 int) int { + return 0 +} + +fn fn_with_2b_args(arg1 string, arg2 int) int { + return 0 +} + +fn fn_with_3_args(arg1 string, arg2 int, arg3 User) int { + return 0 +} + +fn (this User) fn_with_receiver() { + println('') +} + +fn fn_with_optional() ?int { + if true { + return error('true') + } + return 30 +} + +fn (f Foo) fn_with_optional() ?int { + if true { + return error('true') + } + return 40 +} + +fn mut_array(mut a []int) { + println(1) +} + +fn fn_with_ref_return() &Foo { + return &Foo{} +} + +[inline] +fn fn_with_flag() { + println('flag') +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/functions_input.vv b/v_windows/v/old/vlib/v/fmt/tests/functions_input.vv new file mode 100644 index 0000000..cbd9865 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/functions_input.vv @@ -0,0 +1,63 @@ +fn C.func(arg int) int + +fn fn_variadic(arg int, args... string) { + println('Do nothing') +} + +fn fn_with_assign_stmts() { + _,_ := fn_with_multi_return() +} + +fn fn_with_multi_return() (int,string) { + return 0,'test' +} + +fn voidfn(){ + println('this is a function that does not return anything') + } + +fn fn_with_1_arg(arg int) int { +return 0 +} + +fn fn_with_2a_args(arg1 int, arg2 int) int { +return 0 +} + +fn fn_with_2_args_to_be_shorten(arg1 int, arg2 int) int { +return 0 +} + +fn fn_with_2b_args(arg1 string, arg2 int) int { +return 0 +} + +fn fn_with_3_args(arg1 string, arg2 int, arg3 User) int { +return 0 +} + +fn (this User) fn_with_receiver() { +println('') +} + +fn fn_with_optional() ?int { + if true { return error('true') } + return 30 +} + +fn (f Foo) fn_with_optional() ?int { + if true { return error('true') } + return 40 +} + +fn mut_array(mut a []int) { + println(1) +} + +fn fn_with_ref_return() &Foo { + return &Foo{} +} + +[inline] fn fn_with_flag() { +println('flag') +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/generic_recursive_structs_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/generic_recursive_structs_keep.vv new file mode 100644 index 0000000..a8704c5 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/generic_recursive_structs_keep.vv @@ -0,0 +1,28 @@ +pub struct Node { + value T + points_to []&Node +} + +fn main() { + mid := &Node{ + value: 'Middle' + } + finish := &Node{ + value: 'Finish' + } + + graph := &Node{ + value: 'Start' + points_to: [ + &Node{ + value: 'TopLeft' + points_to: [ + finish, + mid, + ] + }, + ] + } + + println(graph.points_to[0].value) // 'TopLeft' +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/generic_structs_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/generic_structs_keep.vv new file mode 100644 index 0000000..a27c3fa --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/generic_structs_keep.vv @@ -0,0 +1,45 @@ +struct Foo { +pub: + data T +} + +fn (f Foo) value() string { + return f.data.str() +} + +type DB = string + +struct Repo { + db DB +pub mut: + model T + permission U +} + +struct User { +mut: + name string +} + +struct Permission { +pub mut: + name string +} + +fn main() { + foo_int := Foo{2} + assert foo_int.value() == '2' + println(foo_int) + // + x := Repo{'abc', 3, 1.5} + println(x.db) + println(x.model) + println(x.permission) + // + mut a := Repo{ + model: User{ + name: 'joe' + } + } + println(a.model.name) +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/generics_cascade_types_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/generics_cascade_types_keep.vv new file mode 100644 index 0000000..82fd8dd --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/generics_cascade_types_keep.vv @@ -0,0 +1,27 @@ +struct Foo { +pub: + data T +} + +struct Foo1 {} + +struct Foo2 {} + +fn multi_generic_args(t T, v V) bool { + return true +} + +fn main() { + v1, v2 := -1, 1 + + // not generic + a1, a2 := v1 < v2, v2 > v1 + assert a1 && a2 + b1, b2 := v1 < simplemodule.zero, v2 > v1 + assert b1 && b2 + + // generic + assert multi_generic_args(0, 's') + assert multi_generic_args(Foo1{}, Foo2{}) + assert multi_generic_args, Foo >(Foo{}, Foo{}) +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/generics_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/generics_keep.vv new file mode 100644 index 0000000..4fe55e7 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/generics_keep.vv @@ -0,0 +1,32 @@ +import mymod { ImpNode } + +fn foobar_mymod(inode ImpNode) ImpNode { + return ImpNode{} +} + +fn simple() T { + return T{} +} + +struct Foo {} + +fn (_ Foo) simple() T { + return T{} +} + +struct NonGenericStruct {} + +fn use_as_generic(ngs NonGenericStruct) NonGenericStruct { + return NonGenericStruct{} +} + +struct GenericStruct {} + +fn proper_generics(gs GenericStruct) GenericStruct { + return gs +} + +fn main() { + simple() + Foo{}.simple() +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/global_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/global_keep.vv new file mode 100644 index 0000000..e809f3d --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/global_keep.vv @@ -0,0 +1,10 @@ +__global x = bool(true) +__global y f32 +__global () +__global ( + spam string + foo = int(5) +) +__global ( + a int +) diff --git a/v_windows/v/old/vlib/v/fmt/tests/go_stmt_expected.vv b/v_windows/v/old/vlib/v/fmt/tests/go_stmt_expected.vv new file mode 100644 index 0000000..5e26722 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/go_stmt_expected.vv @@ -0,0 +1,14 @@ +fn my_thread() { + println('yo') +} + +fn my_thread_with_params(s string) { + println(s) +} + +fn my_fn_calling_threads() { + go my_thread() + go my_thread_with_params('yay') + + go my_thread_with_params('nono') +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/go_stmt_input.vv b/v_windows/v/old/vlib/v/fmt/tests/go_stmt_input.vv new file mode 100644 index 0000000..3ccaf8f --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/go_stmt_input.vv @@ -0,0 +1,16 @@ +fn my_thread() { + println('yo') +} + +fn my_thread_with_params(s string) +{ + println(s) +} + +fn my_fn_calling_threads () { +go my_thread() + go my_thread_with_params('yay') + + go + my_thread_with_params('nono') +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/go_stmt_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/go_stmt_keep.vv new file mode 100644 index 0000000..e404156 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/go_stmt_keep.vv @@ -0,0 +1,9 @@ +import sync + +fn go_with_anon_fn() { + wg.add(1) + go fn (mut wg sync.WaitGroup) { + wg.done() + }(mut wg) + wg.wait() +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/goto_expected.vv b/v_windows/v/old/vlib/v/fmt/tests/goto_expected.vv new file mode 100644 index 0000000..144a3ad --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/goto_expected.vv @@ -0,0 +1,4 @@ +fn test_goto() { + goto label + label: +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/goto_input.vv b/v_windows/v/old/vlib/v/fmt/tests/goto_input.vv new file mode 100644 index 0000000..c0740dd --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/goto_input.vv @@ -0,0 +1,4 @@ +fn test_goto() { + goto label +label: +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/hashstmt_expected.vv b/v_windows/v/old/vlib/v/fmt/tests/hashstmt_expected.vv new file mode 100644 index 0000000..057d14a --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/hashstmt_expected.vv @@ -0,0 +1,3 @@ +#include "header.h" + +struct Foo {} diff --git a/v_windows/v/old/vlib/v/fmt/tests/hashstmt_input.vv b/v_windows/v/old/vlib/v/fmt/tests/hashstmt_input.vv new file mode 100644 index 0000000..406f30a --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/hashstmt_input.vv @@ -0,0 +1,2 @@ +#include "header.h" +struct Foo {} diff --git a/v_windows/v/old/vlib/v/fmt/tests/hashstmt_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/hashstmt_keep.vv new file mode 100644 index 0000000..dd994f5 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/hashstmt_keep.vv @@ -0,0 +1,12 @@ +// comment above HashStmt +#flag -I @VROOT/c +#flag @VROOT/c/implementation.o + +#include "@VROOT/c/implementation.h" + +// comment between with newlines around + +#include "header.h" +// comment between without newlines +#include "sqlite3.h" +// comment directly after the HsahStmt diff --git a/v_windows/v/old/vlib/v/fmt/tests/if_brace_on_newline_expected.vv b/v_windows/v/old/vlib/v/fmt/tests/if_brace_on_newline_expected.vv new file mode 100644 index 0000000..6613108 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/if_brace_on_newline_expected.vv @@ -0,0 +1,12 @@ +fn get_typ() Type { + { + { + // The opening brace should be put on a new line here for readability + if typ := c.resolve_generic_type(method.return_type, method.generic_names, + call_expr.generic_types) + { + return typ + } + } + } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/if_brace_on_newline_input.vv b/v_windows/v/old/vlib/v/fmt/tests/if_brace_on_newline_input.vv new file mode 100644 index 0000000..52b9b2a --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/if_brace_on_newline_input.vv @@ -0,0 +1,10 @@ +fn get_typ() Type { + { + { + // The opening brace should be put on a new line here for readability + if typ := c.resolve_generic_type(method.return_type, method.generic_names, call_expr.generic_types) { + return typ + } + } + } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/if_expected.vv b/v_windows/v/old/vlib/v/fmt/tests/if_expected.vv new file mode 100644 index 0000000..e691055 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/if_expected.vv @@ -0,0 +1,9 @@ +fn non_ternary_return() string { + return if some_cond { + 'foo' + } else if false { + 'bar' + } else { + 'baz' + } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/if_input.vv b/v_windows/v/old/vlib/v/fmt/tests/if_input.vv new file mode 100644 index 0000000..5853876 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/if_input.vv @@ -0,0 +1,3 @@ +fn non_ternary_return() string { + return if some_cond { 'foo' } else if false { 'bar' } else { 'baz' } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/if_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/if_keep.vv new file mode 100644 index 0000000..7e8166a --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/if_keep.vv @@ -0,0 +1,11 @@ +fn void_type_array_cond() { + if fg == [] { + stack.ctx.reset_color() + } +} + +fn multi_dimensional_array_cond() { + if t.data == [][]string{} { + return error('Table.data should not be empty.') + } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/if_ternary_expected.vv b/v_windows/v/old/vlib/v/fmt/tests/if_ternary_expected.vv new file mode 100644 index 0000000..e100a4a --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/if_ternary_expected.vv @@ -0,0 +1,48 @@ +fn main() { + // This line is too long + sprogress := if b.no_cstep { + 'TMP1/${b.nexpected_steps:1d}' + } else { + '${b.cstep:1d}/${b.nexpected_steps:1d}' + } + // Normal struct inits + _ := if true { + Foo{} + } else { + Foo{ + x: 5 + } + } + _ := if some_cond { + Bar{ + a: 'bar' + b: 'also bar' + } + } else { + Bar{} + } +} + +fn condition_is_very_long_infix() { + val := if the_first_condition && this_is_required_too + && (another_cond || foobar_to_exceed_the_max_len) { + 'true' + } else { + 'false' + } +} + +fn branches_are_long_fn_calls() { + _ := if nr_dims == 1 { + t.find_or_register_array(elem_type) + } else { + t.find_or_register_arra(t.find_or_register_array_with_dims(elem_type, nr_dims - 1)) + } + // With another arg to make fn call exceed the max_len after if unwrapping + _ := if nr_dims == 1 { + t.find_or_register_array(elem_type) + } else { + t.find_or_register_arra(t.find_or_register_array_with_dims(elem_type, nr_dims - 1, + 'some string')) + } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/if_ternary_input.vv b/v_windows/v/old/vlib/v/fmt/tests/if_ternary_input.vv new file mode 100644 index 0000000..c76a8ff --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/if_ternary_input.vv @@ -0,0 +1,19 @@ +fn main() { + // This line is too long + sprogress := if b.no_cstep { 'TMP1/${b.nexpected_steps:1d}' } else { '${b.cstep:1d}/${b.nexpected_steps:1d}' } + // Normal struct inits + _ := if true { Foo{} } else { Foo{ + x: 5 + } } + _ := if some_cond { Bar{ a: 'bar', b: 'also bar'} } else { Bar{} } +} + +fn condition_is_very_long_infix() { + val := if the_first_condition && this_is_required_too && (another_cond || foobar_to_exceed_the_max_len) { 'true' } else { 'false' } +} + +fn branches_are_long_fn_calls() { + _ := if nr_dims == 1 { t.find_or_register_array(elem_type) } else { t.find_or_register_arra(t.find_or_register_array_with_dims(elem_type, nr_dims - 1)) } + // With another arg to make fn call exceed the max_len after if unwrapping + _ := if nr_dims == 1 { t.find_or_register_array(elem_type) } else { t.find_or_register_arra(t.find_or_register_array_with_dims(elem_type, nr_dims - 1, 'some string')) } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/if_ternary_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/if_ternary_keep.vv new file mode 100644 index 0000000..8352cc5 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/if_ternary_keep.vv @@ -0,0 +1,44 @@ +struct Foo { + n int + s string +} + +fn valid_single_line() { + // Variable initialization + a, b := if true { 'a', 'b' } else { 'b', 'a' } + // Variable assignment + mut x := 'abc' + x = if x == 'def' { 'ghi' } else { 'def' } + // Array pushes + [0, 1] << if true { 2 } else { 3 } + // Empty or literal syntax struct inits + _ := if false { Foo{} } else { Foo{5, 6} } + // As argument for a function call + some_func(if cond { 'param1' } else { 'param2' }) + // struct init + foo := Foo{ + n: if true { 1 } else { 0 } + s: if false { 'false' } else { 'true' } + } +} + +fn requires_multiple_lines() { + b := if bar { + // with comments inside + 'some str' + } else { + 'other str' + } +} + +fn return_ternary(cond bool) int { + return if cond { 5 } else { 12 } +} + +fn long_return_ternary() string { + return if false { + 'spam and eggs' + } else { + 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.' + } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/import_auto_added_expected.vv b/v_windows/v/old/vlib/v/fmt/tests/import_auto_added_expected.vv new file mode 100644 index 0000000..2b9dc52 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/import_auto_added_expected.vv @@ -0,0 +1,7 @@ +module main + +import time + +fn main() { + curr_time := time.now() +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/import_auto_added_input.vv b/v_windows/v/old/vlib/v/fmt/tests/import_auto_added_input.vv new file mode 100644 index 0000000..4c6a559 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/import_auto_added_input.vv @@ -0,0 +1,5 @@ +module main + +fn main() { + curr_time := time.now() +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/import_comments_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/import_comments_keep.vv new file mode 100644 index 0000000..1f64b0b --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/import_comments_keep.vv @@ -0,0 +1,11 @@ +// import time This should be commented out +import os +// between +import rand +// first between +// second between +import semver +// after the last import +// also after the last import + +// this comment does not belong to the imports anymore diff --git a/v_windows/v/old/vlib/v/fmt/tests/import_duplicate_expected.vv b/v_windows/v/old/vlib/v/fmt/tests/import_duplicate_expected.vv new file mode 100644 index 0000000..5769710 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/import_duplicate_expected.vv @@ -0,0 +1,10 @@ +import math +import os + +const ( + mypi = math.pi +) + +fn main() { + println(os.path_separator) +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/import_duplicate_input.vv b/v_windows/v/old/vlib/v/fmt/tests/import_duplicate_input.vv new file mode 100644 index 0000000..0c11afe --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/import_duplicate_input.vv @@ -0,0 +1,11 @@ +import math +import os +import math + +const ( + mypi = math.pi +) + +fn main() { + println(os.path_separator) +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/import_multiple_with_alias_expected.vv b/v_windows/v/old/vlib/v/fmt/tests/import_multiple_with_alias_expected.vv new file mode 100644 index 0000000..e2ee322 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/import_multiple_with_alias_expected.vv @@ -0,0 +1,11 @@ +import os +import math + +fn main() { + // println(m.pi) + println(os.path_separator) + println(math.pi) + // math as m + // import math.complex as c + // num := c.Complex{} TODO +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/import_multiple_with_alias_input.vv b/v_windows/v/old/vlib/v/fmt/tests/import_multiple_with_alias_input.vv new file mode 100644 index 0000000..e2ee322 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/import_multiple_with_alias_input.vv @@ -0,0 +1,11 @@ +import os +import math + +fn main() { + // println(m.pi) + println(os.path_separator) + println(math.pi) + // math as m + // import math.complex as c + // num := c.Complex{} TODO +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/import_selective_expected.vv b/v_windows/v/old/vlib/v/fmt/tests/import_selective_expected.vv new file mode 100644 index 0000000..8cdb26a --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/import_selective_expected.vv @@ -0,0 +1,67 @@ +import math { max, min } +import cli { Command } +import math.complex { Complex, complex } +import os { + file_ext, + user_os, +} +import mod { + Enum, + FnArg, + FnRet, + InterfaceField, + InterfaceMethodArg, + InterfaceMethodRet, + RightOfAs, + RightOfIs, + StructEmbed, + StructField, + StructMethodArg, + StructMethodRet, + StructRefField, +} + +struct Struct { + StructEmbed + v StructField + ref &StructRefField +} + +fn (s Struct) method(v StructMethodArg) StructMethodRet { + return {} +} + +interface Interface { + v InterfaceField + f(InterfaceMethodArg) InterfaceMethodRet +} + +fn f(v FnArg) FnRet { + if v is RightOfIs { + } + _ = v as RightOfAs + + println(Enum.val) + + return {} +} + +struct App { + command &Command +} + +struct MyCommand { + Command +} + +fn imaginary(im f64) Complex { + return complex(0, im) +} + +fn main() { + println(max(0.1, 0.2)) + println(min(0.1, 0.2)) + println(user_os()) + println(file_ext('main.v')) + println(imaginary(1)) +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/import_selective_input.vv b/v_windows/v/old/vlib/v/fmt/tests/import_selective_input.vv new file mode 100644 index 0000000..12a0812 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/import_selective_input.vv @@ -0,0 +1,71 @@ +import math { max, + min, +} + +import cli { Command } +import math.complex { complex, Complex } +import os { + input, user_os, file_ext } + +import mod { + Unused, + StructEmbed, StructField, StructRefField + StructMethodArg, + StructMethodRet + + InterfaceField, + InterfaceMethodArg, + InterfaceMethodRet, + + FnArg, + FnRet, + + RightOfIs, + RightOfAs, + + Enum +} + +struct Struct { + StructEmbed + v StructField + ref &StructRefField +} + +fn (s Struct) method(v StructMethodArg) StructMethodRet { + return {} +} + +interface Interface { + v InterfaceField + f(InterfaceMethodArg) InterfaceMethodRet +} + +fn f(v FnArg) FnRet { + if v is RightOfIs {} + _ = v as RightOfAs + + println(Enum.val) + + return {} +} + +struct App { + command &Command +} + +struct MyCommand { + Command +} + +fn imaginary(im f64) Complex { + return complex(0, im) +} + +fn main() { + println(max(0.1, 0.2)) + println(min(0.1, 0.2)) + println(user_os()) + println(file_ext('main.v')) + println(imaginary(1)) +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/import_selective_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/import_selective_keep.vv new file mode 100644 index 0000000..592a635 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/import_selective_keep.vv @@ -0,0 +1,12 @@ +import math.complex { Complex } +import gg { MouseButton } +import time { Duration } + +fn keep_imported_enum_map_key() { + bm := map[MouseButton]string{} +} + +fn main() { + _ := Duration(10) // keep cast type + assert *(&f64(&byte(&num) + __offsetof(Complex, re))) == 1.0 +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/import_single_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/import_single_keep.vv new file mode 100644 index 0000000..29d4d36 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/import_single_keep.vv @@ -0,0 +1,5 @@ +import os + +fn main() { + os.system('echo hi') +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/import_with_alias_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/import_with_alias_keep.vv new file mode 100644 index 0000000..f883f0e --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/import_with_alias_keep.vv @@ -0,0 +1,25 @@ +import os +import time as t +import some.library as slib +import crypto.sha256 +import mymod.sha256 as mysha256 + +type my_alias = fn (t slib.MyType) + +struct Foo { + bar t.Time + bars []t.Time + barref &t.Time + barrefs []&t.Time + c_type C.some_struct + js_type JS.other_struct +} + +fn main() { + println('start') + t.sleep_ms(500) + println('end') + os.system('date') + v_hash := sha256.sum('hi'.bytes()).hex() + my_hash := mysha256.sum('hi'.bytes()).hex() +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/infix_expr_expected.vv b/v_windows/v/old/vlib/v/fmt/tests/infix_expr_expected.vv new file mode 100644 index 0000000..4dfa012 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/infix_expr_expected.vv @@ -0,0 +1,33 @@ +fn grouped_cond_single_line() { + // fmt tries to keep grouped conditions together... + _ := one_condition_before && another_condition + && (inside_paren || is_kept_together || if_possible) && end_cond +} + +fn unwrap_grouped_conds() { + // ...but sometimes they have to be splitted + _ := one_condition && before_condition && (conds_inside_paren + || are_kept_together || if_possible || but_this_is_really_too_much + || for_one_line) + _ := (also_inside_parens || just_as_above || but_this_is_also_more + || than_a_single_line_could_fit) && end_cond + fields = fields.filter((it.typ in [string_type, int_type, bool_type] + || c.table.types[int(it.typ)].kind == .struct_) && !it.attrs.contains('skip')) +} + +fn main() { + clean_struct_v_type_name = + clean_struct_v_type_name.replace('_Array', '_array').replace('_T_', '<').replace('_', ', ') + + '>' + { + { + { + // Indent this much to force a break after the assign (`:=`). + // Check that the trailing space is removed + should_cast := + (g.table.type_kind(stmt.left_types.first()) in js.shallow_equatables) + && (g.cast_stack.len <= 0 || stmt.left_types.first() != g.cast_stack.last()) + } + } + } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/infix_expr_input.vv b/v_windows/v/old/vlib/v/fmt/tests/infix_expr_input.vv new file mode 100644 index 0000000..7bfec62 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/infix_expr_input.vv @@ -0,0 +1,24 @@ +fn grouped_cond_single_line() { + // fmt tries to keep grouped conditions together... + _ := one_condition_before && another_condition && (inside_paren || is_kept_together || if_possible) && end_cond +} + +fn unwrap_grouped_conds() { + // ...but sometimes they have to be splitted + _ := one_condition && before_condition && (conds_inside_paren || are_kept_together || if_possible || but_this_is_really_too_much || for_one_line) + _ := (also_inside_parens || just_as_above || but_this_is_also_more || than_a_single_line_could_fit) && end_cond + fields = fields.filter((it.typ in [string_type, int_type, bool_type] || c.table.types[int(it.typ)].kind == .struct_) && !it.attrs.contains('skip')) +} + +fn main() { + clean_struct_v_type_name = clean_struct_v_type_name.replace('_Array', '_array').replace('_T_', '<').replace('_', ', ') + '>' + { + { + { + // Indent this much to force a break after the assign (`:=`). + // Check that the trailing space is removed + should_cast := (g.table.type_kind(stmt.left_types.first()) in js.shallow_equatables) && (g.cast_stack.len <= 0 || stmt.left_types.first() != g.cast_stack.last()) + } + } + } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/infix_expr_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/infix_expr_keep.vv new file mode 100644 index 0000000..5eecc2c --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/infix_expr_keep.vv @@ -0,0 +1,15 @@ +fn infix_in_multi_assign() { + child_width, child_height = child.adj_width + child.margin(.left) + child.margin(.right), + child.adj_height + child.margin(.top) + child.margin(.bottom) +} + +fn impostor_infix() { + /* + String concatiation is an InfixExpr. The second part is so long + that it overflows a single line. Therefore the wrapping logic is run. + The problem was that `&&` and `||` inside the string were detected as infix operators. + Thus causing a totally wrong wrapping and sometimes runtime panics. + */ + x := 'foo' + + 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa || (eeeeeeeeeeeeeeeeee && ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff) && bbbbbbbbbbbbbbbbbbbbbbbbbbbb' +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/integer_literal_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/integer_literal_keep.vv new file mode 100644 index 0000000..f26d917 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/integer_literal_keep.vv @@ -0,0 +1,19 @@ +struct IfExpr { +} + +struct MatchExpr { +} + +type Expr = IfExpr | MatchExpr + +fn sum_types(a []Expr) { +} + +fn main() { + x := 0xdead_beef + u := 9_978_654_321 + o := 0o66_4 + eprintln(' hex constant in decimal: $x') + eprintln(' u constant in decimal: $u') + eprintln('octal constant in decimal: $o') +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/interface_declaration_comments_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/interface_declaration_comments_keep.vv new file mode 100644 index 0000000..60c8312 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/interface_declaration_comments_keep.vv @@ -0,0 +1,31 @@ +pub interface ReaderWriter { + read(mut buf []byte) ?int // from Reader + write(buf []byte) ?int // from Writer +} + +interface Speaker { + // first + speak() string + // between + foo() string + foo2() string + // last +} + +interface Baz { + // first + speak() string + // comment + // more between + foo() string + foo2() string + // last +} + +interface Bar { + speak() string // after + foo() string + speak2() string // also after + // and between + foo2() string +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/interface_variadic_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/interface_variadic_keep.vv new file mode 100644 index 0000000..ed288d3 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/interface_variadic_keep.vv @@ -0,0 +1,4 @@ +interface Element { + unnamed_method(...f64) + named_method(params ...f64) +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/interface_with_mut_fields_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/interface_with_mut_fields_keep.vv new file mode 100644 index 0000000..4b8595b --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/interface_with_mut_fields_keep.vv @@ -0,0 +1,6 @@ +interface Toto { + a int +mut: + b int + f() +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/labelled_break_continue_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/labelled_break_continue_keep.vv new file mode 100644 index 0000000..89fb1d2 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/labelled_break_continue_keep.vv @@ -0,0 +1,38 @@ +fn test_labelled_for() { + mut i := 4 + goto L1 + L1: for { + i++ + for { + if i < 7 { + continue L1 + } else { + break L1 + } + } + } + assert i == 7 + goto L2 + L2: for ; true; i++ { + for { + if i < 17 { + continue L2 + } else { + break L2 + } + } + } + assert i == 17 + goto L3 + L3: for e in [1, 2, 3, 4] { + i = e + for { + if i < 3 { + continue L3 + } else { + break L3 + } + } + } + assert i == 3 +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/language_prefixes_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/language_prefixes_keep.vv new file mode 100644 index 0000000..a0a25c5 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/language_prefixes_keep.vv @@ -0,0 +1,13 @@ +fn (v JS.String) toString() JS.String + +fn (v JS.String) toMultiRet() (JS.String, int) + +fn JS.Math.abs(f64) f64 + +fn main() { + JS.Math.abs(0) +} + +fn object_ref_optional() ?&C.File { + return error('') +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/loops_expected.vv b/v_windows/v/old/vlib/v/fmt/tests/loops_expected.vv new file mode 100644 index 0000000..17d3f66 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/loops_expected.vv @@ -0,0 +1,18 @@ +fn for_in_loop() { + for item in arr { + println(item) + } +} + +fn for_in_loop_with_counter() { + for i, item in arr { + println(i) + println(item) + } +} + +fn for_in_loop_with_index_expr() { + for i in 0 .. 10 { + println(i) + } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/loops_input.vv b/v_windows/v/old/vlib/v/fmt/tests/loops_input.vv new file mode 100644 index 0000000..1b271a3 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/loops_input.vv @@ -0,0 +1,18 @@ +fn for_in_loop() { + for item in arr { + println(item) + } +} + +fn for_in_loop_with_counter() { + for i, item in arr { + println(i) + println(item) + } +} + +fn for_in_loop_with_index_expr() { + for i in 0..10 { + println(i) + } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/manualfree_keep.v b/v_windows/v/old/vlib/v/fmt/tests/manualfree_keep.v new file mode 100644 index 0000000..98de3f1 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/manualfree_keep.v @@ -0,0 +1,18 @@ +[manualfree] +module main + +fn abc() { + x := 'abc should be autofreed' + println(x) +} + +[manualfree] +fn xyz() { + x := 'xyz should do its own memory management' + println(x) +} + +fn main() { + abc() + xyz() +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/maps_expected.vv b/v_windows/v/old/vlib/v/fmt/tests/maps_expected.vv new file mode 100644 index 0000000..ade554f --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/maps_expected.vv @@ -0,0 +1,16 @@ +const ( + reserved_types = map{ + 'i8': true + 'i16': true + 'int': true + 'i64': true + 'i128': true + } +) + +numbers := map{ + 'one': 1 + 'two': 2 + 'sevenhundredseventyseven': 777 + 'fivethousandthreehundredtwentyseven': 5327 +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/maps_in_fn_args__keep.vv b/v_windows/v/old/vlib/v/fmt/tests/maps_in_fn_args__keep.vv new file mode 100644 index 0000000..5968438 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/maps_in_fn_args__keep.vv @@ -0,0 +1,15 @@ +import v.ast + +fn abc_xxx(xobj ast.ScopeObject) { +} + +fn abc_map(xmap map[string]ast.ScopeObject) { +} + +fn (t Tree) objects(so map[string]ast.ScopeObject) &C.cJSON { + obj := create_object() + for key, val in so { + to_object(obj, key, t.scope_object(val)) + } + return obj +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/maps_input.vv b/v_windows/v/old/vlib/v/fmt/tests/maps_input.vv new file mode 100644 index 0000000..6eb783e --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/maps_input.vv @@ -0,0 +1,16 @@ +const ( +reserved_types = { + 'i8': true + 'i16': true + 'int': true + 'i64': true + 'i128': true +} +) + +numbers := { + 'one': 1 + 'two': 2 + 'sevenhundredseventyseven': 777 +'fivethousandthreehundredtwentyseven': 5327 +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/maps_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/maps_keep.vv new file mode 100644 index 0000000..e2cbf1e --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/maps_keep.vv @@ -0,0 +1,17 @@ +fn workaround() { + a := map[string]string{} + println(a) +} + +fn main() { + mut ams := []map[string]string{} + ams << map{ + 'a': 'b' + 'c': 'd' + } + ams << map{ + 'e': 'f' + 'g': 'h' + } + println(ams) +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/maps_of_fns_with_string_keys_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/maps_of_fns_with_string_keys_keep.vv new file mode 100644 index 0000000..bcb6aff --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/maps_of_fns_with_string_keys_keep.vv @@ -0,0 +1,13 @@ +fn sqr(n int) int { + return n * n +} + +fn main() { + fns := [sqr] + println(fns[0](10)) + fns_map := map{ + 'sqr': sqr + } + println(fns_map['sqr']) + println(fns_map['sqr'](2)) +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/match_expected.vv b/v_windows/v/old/vlib/v/fmt/tests/match_expected.vv new file mode 100644 index 0000000..556768f --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/match_expected.vv @@ -0,0 +1,38 @@ +fn match_expr_assignment() { + a := 20 + _ := match a { + 10 { 10 } + 5 { 5 } + else { 2 } + } +} + +fn match_branch_comment() { + a := 1 + match a { + 1 { + println('1') + } + 2 { + println('2') + } + else { + // do nothing + } + } +} + +fn really_long_branch_exprs() { + match x { + NodeError, ArrayDecompose, ArrayInit, AsCast, Assoc, AtExpr, BoolLiteral, CallExpr, + MapInit, MatchExpr, None, OffsetOf, OrExpr, ParExpr, PostfixExpr, PrefixExpr, RangeExpr, + SelectExpr, SelectorExpr, SizeOf, SqlExpr, StringInterLiteral, StringLiteral, StructInit { + return expr.pos + } + InfixExpr { + Foo{ + x: 3 + } + } + } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/match_input.vv b/v_windows/v/old/vlib/v/fmt/tests/match_input.vv new file mode 100644 index 0000000..3f7cbae --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/match_input.vv @@ -0,0 +1,36 @@ +fn match_expr_assignment() { + a := 20 + _ := match a { + 10 { 10 } + else { 2 } + 5 { 5 } + } +} + +fn match_branch_comment() { + a := 1 + match a { + 1 { println('1') } + 2 { + println('2') + } + else { + + +// do nothing + } + } +} + +fn really_long_branch_exprs() { + match x { + NodeError, ArrayDecompose, ArrayInit, AsCast, Assoc, AtExpr, BoolLiteral, CallExpr, MapInit, MatchExpr, None, OffsetOf, OrExpr, ParExpr, PostfixExpr, PrefixExpr, RangeExpr, SelectExpr, SelectorExpr, SizeOf, SqlExpr, StringInterLiteral, StringLiteral, StructInit { + return expr.pos + } + InfixExpr { + Foo{ + x: 3 + } + } + } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/match_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/match_keep.vv new file mode 100644 index 0000000..73326e4 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/match_keep.vv @@ -0,0 +1,76 @@ +fn nested_match() { + match 100 { + 0...1 { + println('0 to 1') + } + else { + match 200 { + 0...1 { println('0 to 1') } + else { println('unknown value') } + } + } + } +} + +fn branches_are_struct_inits() { + match 'a' { + 'b' { SpamStruct{} } + } + match 'a' { + 'b' { + SpamStruct{ + x: 42 + } + } + } + match 'a' { + 'b' { + SpamStruct{ + // comment inside init + } + } + } +} + +fn branches_are_call_exprs_with_or_blocks() { + match 'a' { + 'b' { foo() or { panic(err.msg) } } + } + match 'a' { + 'b' { + foo() or { + // do stuff + panic(err.msg) + } + } + } + match 'a' { + 'b' { + foo() or { + another_stmt() + panic(err.msg) + } + } + } +} + +fn keep_branch_linebreaks() { + a := 10 + match a { + // first comment + 10 { + println('10') + } + 20 { + println('20') + } + else {} + } + match a { + // first comment + 10 { println('10') } + // post_comment of the first branch + 20 { println('20') } + else {} + } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/match_range_expression_branches_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/match_range_expression_branches_keep.vv new file mode 100644 index 0000000..ae99b0f --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/match_range_expression_branches_keep.vv @@ -0,0 +1,15 @@ +pub fn str_escaped(b byte) string { + str := match b { + 0 { '`\\' + '0`' } // Bug is preventing \\0 in a literal + 7 { '`\\a`' } + 8 { '`\\b`' } + 9 { '`\\t`' } + 10 { '`\\n`' } + 11 { '`\\v`' } + 12 { '`\\f`' } + 13 { '`\\r`' } + 32...126 { b.str() } + else { '0x' + b.hex() } + } + return str +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/match_with_commented_branches_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/match_with_commented_branches_keep.vv new file mode 100644 index 0000000..3240eb5 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/match_with_commented_branches_keep.vv @@ -0,0 +1,57 @@ +module ast + +pub fn (stmt Stmt) position() Position { + match stmt { + AssertStmt { return stmt.pos } + AssignStmt { return stmt.pos } + /* + // Attr { + // } + // Block { + // } + // BranchStmt { + // } + */ + Comment { return stmt.pos } + ConstDecl { return stmt.pos } + /* + // DeferStmt { + // } + */ + EnumDecl { return stmt.pos } + ExprStmt { return stmt.pos } + FnDecl { return stmt.pos } + ForCStmt { return stmt.pos } + ForInStmt { return stmt.pos } + ForStmt { return stmt.pos } + /* + // GlobalDecl { + // } + // GoStmt { + // } + // GotoLabel { + // } + // GotoStmt { + // } + // HashStmt { + // } + */ + Import { return stmt.pos } + /* + // InterfaceDecl { + // } + // Module { + // } + */ + Return { return stmt.pos } + StructDecl { return stmt.pos } + /* + // TypeDecl { + // } + // UnsafeStmt { + // } + */ + // + else { return Position{} } + } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/missing_import_expected.vv b/v_windows/v/old/vlib/v/fmt/tests/missing_import_expected.vv new file mode 100644 index 0000000..c463392 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/missing_import_expected.vv @@ -0,0 +1,5 @@ +import time + +fn main() { + println(time.now()) +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/missing_import_input.vv b/v_windows/v/old/vlib/v/fmt/tests/missing_import_input.vv new file mode 100644 index 0000000..d173cb2 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/missing_import_input.vv @@ -0,0 +1,3 @@ +fn main() { + println(time.now()) +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/module_alias_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/module_alias_keep.vv new file mode 100644 index 0000000..f6f77c0 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/module_alias_keep.vv @@ -0,0 +1,36 @@ +import time +import semver as sv +import term.ui as tui +import v.ast + +interface Inter { + code tui.KeyCode +} + +struct TuiStruct { + code tui.KeyCode +} + +fn foo(f time.Time) time.Time { + f2 := time.Time{} + return f +} + +fn bar(b sv.Version) sv.Version { + b2 := sv.Version{} + return b +} + +fn bar_multi_return(b sv.Version) (sv.Version, int) { + b2 := sv.Version{} + return b, 0 +} + +struct SomeStruct { + a fn (ast.Stmt, voidptr) bool +} + +fn main() { + if x is ast.FnDecl { + } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/module_interface_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/module_interface_keep.vv new file mode 100644 index 0000000..f07e5bf --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/module_interface_keep.vv @@ -0,0 +1,5 @@ +module module_fmt + +pub interface MyInterface { + fun() +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/module_struct_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/module_struct_keep.vv new file mode 100644 index 0000000..87c6ff8 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/module_struct_keep.vv @@ -0,0 +1,11 @@ +module module_fmt + +pub struct MyStruct { +mut: + value int + foo mod.Foo +} + +pub fn (m MyStruct) foo() bool { + return true +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/multi_generic_test_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/multi_generic_test_keep.vv new file mode 100644 index 0000000..f8da457 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/multi_generic_test_keep.vv @@ -0,0 +1,2 @@ +fn test() { +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/multiline_comment_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/multiline_comment_keep.vv new file mode 100644 index 0000000..d3d1536 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/multiline_comment_keep.vv @@ -0,0 +1,22 @@ +/* +this is a very long comment +that is on multiple lines + and has some formatting in it + that should be + preserved. +*/ +fn main() { + println('hello') + /* + this comment also + has mutliple lines + but it's difference + is that it is indented ! + */ + if true { + /* + this one is even more + indented ! + */ + } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/nested_map_type_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/nested_map_type_keep.vv new file mode 100644 index 0000000..37efebc --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/nested_map_type_keep.vv @@ -0,0 +1,5 @@ +import v.ast + +fn foo(my_map map[string]map[string]int) int { + return 0 +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/newlines_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/newlines_keep.vv new file mode 100644 index 0000000..7d70d56 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/newlines_keep.vv @@ -0,0 +1,32 @@ +// Module with attribute +[manualfree] +module websocket + +fn C.no_body_function() +fn C.another_one(x int) + +fn C.separated_from_my_body_and_the_above() + +fn main() {} + +// This should stay between both functions + +fn x() {} + +// doc comment above an attributed function +[inline] +fn y_with_attr() { +} + +// doc comment above an attributed struct +[typedef] +struct FooWithAttr { +} + +fn between_assembly_blocks() { + asm amd64 { + } + + asm i386 { + } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/no_main_expected.vv b/v_windows/v/old/vlib/v/fmt/tests/no_main_expected.vv new file mode 100644 index 0000000..2a082f9 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/no_main_expected.vv @@ -0,0 +1 @@ +println('hello world') diff --git a/v_windows/v/old/vlib/v/fmt/tests/no_main_input.vv b/v_windows/v/old/vlib/v/fmt/tests/no_main_input.vv new file mode 100644 index 0000000..d774314 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/no_main_input.vv @@ -0,0 +1 @@ +println( "hello world" ) diff --git a/v_windows/v/old/vlib/v/fmt/tests/offset_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/offset_keep.vv new file mode 100644 index 0000000..df391b9 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/offset_keep.vv @@ -0,0 +1,8 @@ +struct Animal { + breed string + age u64 +} + +fn main() { + println(__offsetof(Animal, breed) + __offsetof(Animal, age)) +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/operator_overload_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/operator_overload_keep.vv new file mode 100644 index 0000000..381224a --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/operator_overload_keep.vv @@ -0,0 +1,11 @@ +struct Foo { + x int +} + +fn (a Foo) + (b Foo) Foo { + return Foo{a.x + b.x} +} + +fn (a Foo) % (b Foo) Foo { + return Foo{a.x % b.x} +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/optional_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/optional_keep.vv new file mode 100644 index 0000000..407db36 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/optional_keep.vv @@ -0,0 +1,5 @@ +pub fn test() ?&SomeType { +} + +struct SomeType { +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/optional_propagate_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/optional_propagate_keep.vv new file mode 100644 index 0000000..0cd4524 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/optional_propagate_keep.vv @@ -0,0 +1,3 @@ +fn opt_propagate() ?int { + eventual_wrong_int() ? +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/or_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/or_keep.vv new file mode 100644 index 0000000..a1d1f65 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/or_keep.vv @@ -0,0 +1,40 @@ +fn main() { + empty_or_block() or {} + empty_or_block() or { + } +} + +fn fn_with_or() int { + fn_with_optional() or { return 10 } + return 20 +} + +fn (f Foo) method_with_or() int { + f.fn_with_optional() or { return 10 } + return 20 +} + +fn unwrapped_single_line_if() { + namefound := publisher.name_fix_check(name_to_find, state.site.id, ispage) or { + if err.contains('Could not find') { + state.error('cannot find link: $name_to_find') + } else { + state.error('cannot find link: $name_to_find\n$err') + } + println('Another stmt') + } +} + +fn or_with_one_multi_line_stmt() { + b := or_func() or { + MyStruct{ + val: 'xyz' + } + } +} + +fn channel_pop() { + var_init := <-ch or { -1.25 } + var_assign = <-ch or { -2.5 } + arr_push << <-ch or { -3.75 } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/orm_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/orm_keep.vv new file mode 100644 index 0000000..26513bd --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/orm_keep.vv @@ -0,0 +1,67 @@ +import sqlite +import mymodule { ModDbStruct } + +struct Customer { + id int + name string + nr_orders int + country string +} + +fn find_all_customers(db sqlite.DB) []Customer { + return sql db { + select from Customer + } +} + +fn main() { + db := sqlite.connect('customers.db') ? + // select count(*) from Customer + nr_customers := sql db { + select count from Customer + } + println('number of all customers: $nr_customers') + // V syntax can be used to build queries + // db.select returns an array + uk_customers := sql db { + select from Customer where country == 'uk' && nr_orders > 0 + } + println(uk_customers.len) + for customer in uk_customers { + println('$customer.id - $customer.name') + } + // by adding `limit 1` we tell V that there will be only one object + customer := sql db { + select from Customer where id == 1 limit 1 + } + best_customer := sql db { + select from Customer order by nr_orders desc limit 1 + } + second_best := sql db { + select from UCustomerser order by nr_orders desc limit 1 offset 1 + } + println('$customer.id - $customer.name') + // insert a new customer + new_customer := Customer{ + name: 'Bob' + nr_orders: 10 + } + sql db { + insert new_customer into Customer + } + // delete a row + sql db { + delete from Customer where nr_orders == 10 && name == 'Bob' + } + sql db { + update Customer set name = 'Queen Elizabeth II', age = 150, nr_orders = 42, country = 'Great Britain' + where id == 5 + } + // DB is a selective import + sql db { + delete from ModDbStruct where id == 1 + } + _ := sql db { + select from ModDbStruct + } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/par_expr_expected.vv b/v_windows/v/old/vlib/v/fmt/tests/par_expr_expected.vv new file mode 100644 index 0000000..360c528 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/par_expr_expected.vv @@ -0,0 +1,3 @@ +fn main() { + _ := (cond1 && cond2) || single_ident +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/par_expr_input.vv b/v_windows/v/old/vlib/v/fmt/tests/par_expr_input.vv new file mode 100644 index 0000000..d3c8653 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/par_expr_input.vv @@ -0,0 +1,3 @@ +fn main() { + _ := (cond1 && cond2) || (single_ident) +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/pointer_casts_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/pointer_casts_keep.vv new file mode 100644 index 0000000..57b5905 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/pointer_casts_keep.vv @@ -0,0 +1,50 @@ +struct Struct { + name string + x int +} + +fn main() { + unsafe { + pb := &byte(0) + ppb := &&byte(0) + pppb := &&&byte(0) + ppppb := &&&&byte(0) + dump(voidptr(pb)) + dump(voidptr(ppb)) + dump(voidptr(pppb)) + dump(voidptr(ppppb)) + pc := &char(0) + ppc := &&char(0) + pppc := &&&char(0) + ppppc := &&&&char(0) + dump(voidptr(pc)) + dump(voidptr(ppc)) + dump(voidptr(pppc)) + dump(voidptr(ppppc)) + ps := &Struct(0) + pps := &&Struct(0) + ppps := &&&Struct(0) + pppps := &&&&Struct(0) + dump(voidptr(ps)) + dump(voidptr(pps)) + dump(voidptr(ppps)) + dump(voidptr(pppps)) + } + ss := &Struct{ + name: 'abc' + x: 123 + } + dump(ss) + pss := voidptr(ss) + if &Struct(pss).name == 'abc' { + println('ok') + } + if &Struct(pss).x == 123 { + // &Struct cast and selecting .x + println('ok') + } + if &&Struct(pss) != 0 { + // &&Struct + println('ok') + } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/proto_module_importing_vproto_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/proto_module_importing_vproto_keep.vv new file mode 100644 index 0000000..2bca234 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/proto_module_importing_vproto_keep.vv @@ -0,0 +1,35 @@ +module proto + +import vproto + +struct Xyz { + x int +} + +struct Abcde { + f1 vproto.Xyz + f2 &vproto.Xyz + f3 []vproto.Xyz + f4 []&vproto.Xyz + f5 map[string]vproto.Xyz + f6 map[string]&vproto.Xyz + // + p1 Xyz + p2 &Xyz + p3 []Xyz + p4 []&Xyz + p5 map[string]Xyz + p6 map[string]&Xyz + p7 map[string]map[string]map[string]&Xyz + // + p8 map[string]map[string]map[string]map[string]&Xyz + p9 map[string]map[string]map[string]map[string]&vproto.Xyz +} + +fn abc() { + x := vproto.Xyz{2} + mut a := []vproto.Xyz{} + a << x + a << vproto.Xyz{3} + z := map[string]map[string]map[string]map[string]&vproto.Xyz{} +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/ref_type_cast_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/ref_type_cast_keep.vv new file mode 100644 index 0000000..aba9e54 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/ref_type_cast_keep.vv @@ -0,0 +1,9 @@ +module main + +fn cleanup(user_data voidptr) { + abc := App(user_data) + xyz := &App(user_data) + // + mut app := App(user_data) + mut ref := &App(user_data) +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/select_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/select_keep.vv new file mode 100644 index 0000000..4a5abf7 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/select_keep.vv @@ -0,0 +1,128 @@ +import time +import sync + +struct St { + a int +} + +fn getint() int { + return 8 +} + +fn f1(ch1 chan int, ch2 chan St, ch3 chan int, ch4 chan int, ch5 chan int, sem sync.Semaphore) { + mut a := 5 + select { + // pre comment + a = <-ch3 { + a = 0 + } + b := <-ch2 { + a = b.a + } + ch3 <- 5 { + a = 1 + } + ch2 <- St{ + a: 37 + } { + a = 2 + } + // another comment + ch4 <- (6 + 7 * 9) { + a = 8 + } + ch5 <- getint() { + a = 9 + } + 300 * time.millisecond { + a = 3 + } + // post comment + } + assert a == 3 + sem.post() +} + +fn f2(ch1 chan St, ch2 chan int, sem sync.Semaphore) { + mut r := 23 + for i in 0 .. 2 { + select { + b := <-ch1 { + r = b.a + } + ch2 <- r { + r = 17 + } + } + if i == 0 { + assert r == 17 + } else { + assert r == 13 + } + } + sem.post() +} + +fn test_select_blocks() { + ch1 := chan int{cap: 1} + ch2 := chan St{} + ch3 := chan int{} + ch4 := chan int{} + ch5 := chan int{} + sem := sync.new_semaphore() + mut r := false + t := select { + b := <-ch1 { + println(b) + } + else { + // no channel ready + r = true + } + } + assert r == true + assert t == true + go f2(ch2, ch3, sem) + n := <-ch3 + assert n == 23 + ch2 <- St{ + a: 13 + } + sem.wait() + stopwatch := time.new_stopwatch() + go f1(ch1, ch2, ch3, ch4, ch5, sem) + sem.wait() + elapsed_ms := f64(stopwatch.elapsed()) / time.millisecond + assert elapsed_ms >= 295.0 + ch1.close() + ch2.close() + mut h := 7 + mut is_open := true + if select { + b := <-ch2 { + h = 0 + } + ch1 <- h { + h = 1 + } + else { + h = 2 + } + } { + panic('channel is still open') + } else { + is_open = false + } + // no branch should have run + for select { + b := <-ch2 { + h = 0 + } + } { + println('ch2 open') + } + println('ch2 closed') + assert h == 7 + // since all channels are closed `select` should return `false` + assert is_open == false +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/shared_expected.vv b/v_windows/v/old/vlib/v/fmt/tests/shared_expected.vv new file mode 100644 index 0000000..d077f88 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/shared_expected.vv @@ -0,0 +1,85 @@ +import time + +struct St { +mut: + a int +} + +fn (shared x St) f(shared z St) { + for _ in 0 .. reads_per_thread { + rlock x { // other instances may read at the same time + time.sleep(time.millisecond) + assert x.a == 7 || x.a == 5 + } + } + lock z { + z.a-- + } +} + +fn g() shared St { + shared x := St{ + a: 12 + } + return x +} + +fn h() ?shared St { + return error('no value') +} + +fn k() { + shared x := g() + shared y := h() or { + shared f := St{} + f + } + shared z := h() ? + shared p := v + v := rlock z { + z.a + } + lock y, z; rlock x, p { + z.a = x.a + y.a + } + println(v) +} + +const ( + reads_per_thread = 30 + read_threads = 10 + writes = 5 +) + +fn test_shared_lock() { + // object with separate read/write lock + shared x := &St{ + a: 5 + } + shared z := &St{ + a: read_threads + } + for _ in 0 .. read_threads { + go x.f(shared z) + } + for i in 0 .. writes { + lock x { // wait for ongoing reads to finish, don't start new ones + x.a = 17 // this value should never be read + time.sleep(50 * time.millisecond) + x.a = if (i & 1) == 0 { 7 } else { 5 } + } // now new reads are possible again + time.sleep(20 * time.millisecond) + } + // wait until all read threads are finished + for finished := false; true; { + mut rr := 0 + rlock z { + rr = z.a + finished = z.a == 0 + } + if finished { + break + } + time.sleep(100 * time.millisecond) + } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/shared_input.vv b/v_windows/v/old/vlib/v/fmt/tests/shared_input.vv new file mode 100644 index 0000000..dccb34a --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/shared_input.vv @@ -0,0 +1,84 @@ +import sync +import time + +struct St { +mut: + a int +} + +fn (shared x St) f(shared z St) { + for _ in 0 .. reads_per_thread { + rlock x { // other instances may read at the same time + time.sleep(time.millisecond) + assert x.a == 7 || x.a == 5 + } + } + lock z { + z.a-- + } +} + +fn g() shared St { +shared x := St{a: 12 } + return x +} + +fn h() ?shared St { + return error('no value') +} + +fn k() { + shared x := g() + shared y := h() or { + shared f := St{} + f + } + shared z := h() ? + shared p := v + v := rlock z { z.a } + rlock x; lock y, z; rlock p { + z.a = x.a + y.a + } + println(v) +} + +const ( + reads_per_thread = 30 + read_threads = 10 + writes = 5 +) + +fn test_shared_lock() { + // object with separate read/write lock +shared x := &St { +a: 5 +} + shared z := +&St{ + a: read_threads + } + for _ in 0.. read_threads { + go x.f(shared z) + } + for i in 0..writes { + lock x { // wait for ongoing reads to finish, don't start new ones + x.a = 17 // this value should never be read + time.sleep(50* time.millisecond) + x.a = if (i & 1) == 0 { +7} else {5} + } // now new reads are possible again +time.sleep(20*time.millisecond) + } + // wait until all read threads are finished +for finished:=false;; { + mut rr := 0 + rlock z { + rr = z.a + finished = z.a == 0 +} +if finished { + break + } + time.sleep(100*time.millisecond) + } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/star__amp_int__cast_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/star__amp_int__cast_keep.vv new file mode 100644 index 0000000..73312cd --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/star__amp_int__cast_keep.vv @@ -0,0 +1,5 @@ +fn main() { + body := [1, 2, 3] + size := *&int(body.data) + eprintln('size: $size') +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/static_mut_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/static_mut_keep.vv new file mode 100644 index 0000000..4d4d61b --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/static_mut_keep.vv @@ -0,0 +1,12 @@ +[unsafe] +fn foo() int { + mut static x := 42 + x++ + return x +} + +[unsafe] +fn foo() int { + static x := 42 // a immutable static is not very useful, but vfmt should support that too + return x +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/stmt_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/stmt_keep.vv new file mode 100644 index 0000000..1dcb23d --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/stmt_keep.vv @@ -0,0 +1,8 @@ +fn single_line_stmts() { + // Wouldn't be the or-block's stmt be single line, the block would be written as multi line + foo() or { assert false } + for { + foo() or { break } + } + foo() or { return } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/string_interpolation_complex_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/string_interpolation_complex_keep.vv new file mode 100644 index 0000000..470ee54 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/string_interpolation_complex_keep.vv @@ -0,0 +1,9 @@ +struct Container { + id string +} + +container := Container{} +docker_pubkey := '1234657890' + +cmd := "docker exec $container.id sh -c 'echo \"$docker_pubkey\" >> ~/.ssh/authorized_keys'" +println(cmd) diff --git a/v_windows/v/old/vlib/v/fmt/tests/string_interpolation_expected.vv b/v_windows/v/old/vlib/v/fmt/tests/string_interpolation_expected.vv new file mode 100644 index 0000000..4b0944b --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/string_interpolation_expected.vv @@ -0,0 +1,32 @@ +struct Aa { + xy int +} + +struct Bb { + a Aa +} + +struct Cc { + a []Aa +} + +fn (c &Cc) f() int { + return c.a[0].xy +} + +fn (c &Cc) g(k int, l int) int { + return c.a[k].xy + l +} + +fn main() { + st := Bb{Aa{5}} + ar := Cc{[Aa{3}, Aa{-4}, Aa{12}]} + aa := Aa{-13} + z := -14.75 + println('$st.a.xy ${ar.a[2].xy} $aa.xy $z') + println('$st.a.xy${ar.a[2].xy}$aa.xy$z') + println('${st.a.xy}ya ${ar.a[2].xy}X2 ${aa.xy}.b ${z}3') + println('${z:-5} ${z:+5.3} ${z:+09.3f} ${z:-7.2} ${z:+09} ${z:08.3f}') + println('$ar.f() ${ar.g(1, 2)} ${ar.a}() ${z}(') + println('${z > 12.3 * z - 3} ${@VEXE} ${4 * 5}') +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/string_interpolation_input.vv b/v_windows/v/old/vlib/v/fmt/tests/string_interpolation_input.vv new file mode 100644 index 0000000..891e011 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/string_interpolation_input.vv @@ -0,0 +1,32 @@ +struct Aa { + xy int +} + +struct Bb { + a Aa +} + +struct Cc { + a []Aa +} + +fn (c &Cc) f() int { + return c.a[0].xy +} + +fn (c &Cc) g(k int, l int) int { + return c.a[k].xy+l +} + +fn main() { + st := Bb{Aa{5}} + ar := Cc{[Aa{3}, Aa{-4}, Aa{12}]} + aa := Aa{-13} + z := -14.75 + println('${st.a.xy} ${ar.a[2].xy} ${aa.xy} ${z}') + println('${st.a.xy}${ar.a[2].xy}${aa.xy}${z}') + println('${st.a.xy}ya ${ar.a[2].xy}X2 ${aa.xy}.b ${z}3') + println('${z:-5} ${z:+5.3} ${z:+09.3f} ${z:-07.2} ${z:+009} ${z:008.3f}') + println('${ar.f()} ${ar.g(1, 2)} ${ar.a}() ${z}(') + println('${z > 12.3 * z - 3} ${@VEXE} ${4 * 5}') +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/string_interpolation_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/string_interpolation_keep.vv new file mode 100644 index 0000000..97a1ff5 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/string_interpolation_keep.vv @@ -0,0 +1,17 @@ +import os + +fn main() { + println('Hello world, args: $os.args') + i := 123 + a := 'abc' + b := 'xyz' + e := 'a: $a b: $b i: $i' + d := 'a: ${a:5s} b: ${b:-5s} i: ${i:20d}' + f := 'a byte string'.bytes() + println('a: $a $b xxx') + eprintln('e: $e') + _ = ' ${foo.method(bar).str()} ' + println('(${some_struct.@type}, $some_struct.y)') + _ := 'CastExpr ${int(d.e).str()}' + println('${f[0..4].bytestr()}') +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/string_quotes_expected.vv b/v_windows/v/old/vlib/v/fmt/tests/string_quotes_expected.vv new file mode 100644 index 0000000..5f61490 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/string_quotes_expected.vv @@ -0,0 +1,10 @@ +fn main() { + println('Hello world !') + println('This is correct !') + println("It's okay") + println('This is "too"') + println("I'm correctly formatted") + println('"Everything on the internet is true" - Albert Einstein, 1965') + println('I\'m out of idea "_"') + println('Definitely out ":\'("') +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/string_quotes_input.vv b/v_windows/v/old/vlib/v/fmt/tests/string_quotes_input.vv new file mode 100644 index 0000000..c8cedb5 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/string_quotes_input.vv @@ -0,0 +1,10 @@ +fn main() { + println("Hello world !") + println('This is correct !') + println("It's okay") + println('This is "too"') + println('I\'m correctly formatted') + println("\"Everything on the internet is true\" - Albert Einstein, 1965") + println('I\'m out of idea "_"') + println("Definitely out \":'(\"") +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/string_raw_and_cstr_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/string_raw_and_cstr_keep.vv new file mode 100644 index 0000000..89e3010 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/string_raw_and_cstr_keep.vv @@ -0,0 +1,6 @@ +fn main() { + raw := r'\x00' + cstr := c'foo' + println(raw) + println(cstr) +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/struct_decl_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/struct_decl_keep.vv new file mode 100644 index 0000000..cb2de8c --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/struct_decl_keep.vv @@ -0,0 +1,5 @@ +struct Foo { + a int +__global: + g string +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/struct_default_field_expressions_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/struct_default_field_expressions_keep.vv new file mode 100644 index 0000000..0b8239e --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/struct_default_field_expressions_keep.vv @@ -0,0 +1,14 @@ +struct Foo { + i int = 1 // A comment +} + +struct Bar { + f Foo = &Foo(0) + z int [skip] = -1 +} + +struct Baz { + x int = 1 // It's one + y string = 'one' // It's one written out + z bool = true // Also one +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/struct_embed_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/struct_embed_keep.vv new file mode 100644 index 0000000..9213bf7 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/struct_embed_keep.vv @@ -0,0 +1,18 @@ +struct Foo { + x int +} + +struct Test {} + +struct Bar { + Foo // comment for Foo + Test // comment for Test + // another comment for Test + y int + z string +} + +struct Baz { + Foo // Another comment for Foo + Test +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/struct_init_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/struct_init_keep.vv new file mode 100644 index 0000000..d4ec06a --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/struct_init_keep.vv @@ -0,0 +1,14 @@ +struct User { + age int + name string +} + +fn main() { + u := User{ + age: 54 + } + _ = User{ + ...u + name: 'hi' + } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/struct_init_with_comments_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/struct_init_with_comments_keep.vv new file mode 100644 index 0000000..60576b2 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/struct_init_with_comments_keep.vv @@ -0,0 +1,20 @@ +module abcde + +pub struct Builder { +pub mut: + // inline before field + buf []byte + str_calls int + len int + initial_size int = 1 +} + +pub fn new_builder(initial_size int) Builder { + return Builder{ + // buf: make(0, initial_size) + buf: []byte{cap: initial_size} + str_calls: 0 // after str_calls + len: 0 // after len + initial_size: initial_size // final + } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/struct_init_with_custom_len_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/struct_init_with_custom_len_keep.vv new file mode 100644 index 0000000..57d916d --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/struct_init_with_custom_len_keep.vv @@ -0,0 +1,17 @@ +struct Foo { + i int + a []int +} + +struct Bar { + f &Foo = &Foo(0) + d Foo = Foo{0} +} + +fn main() { + size := 5 + f := &Foo{ + a: []int{len: int(size)} + } + println('f.a: $f.a') +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/struct_init_with_ref_cast_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/struct_init_with_ref_cast_keep.vv new file mode 100644 index 0000000..006e4f7 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/struct_init_with_ref_cast_keep.vv @@ -0,0 +1,22 @@ +struct Foo { + f int = 123 +} + +struct Bar { + f &Foo = &Foo(0) +} + +struct Zar { + f Foo +} + +fn main() { + b := &Bar{ + f: &Foo(32) + } + c := &Zar{ + f: Foo{456} + } + assert ptr_str(b.f) == '20' + assert c.f.f == 456 +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/struct_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/struct_keep.vv new file mode 100644 index 0000000..68ed58b --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/struct_keep.vv @@ -0,0 +1,46 @@ +import os + +struct KeepAnyLanguagePrefixVariation { + x C.bar + y &C.bar + z []C.bar + z2 []&C.bar +} + +fn foo(a []os.File) { +} + +struct User { + age int + name string +} + +fn handle_users(users []User) { + println(users.len) +} + +fn (u &User) foo(u2 &User) { +} + +type Expr = IfExpr | IntegerLiteral + +fn exprs(e []Expr) { + println(e.len) +} + +struct KeepStructEmbed { + User +pub: + a int + b int +} + +struct KeepMultiLineDefaultExprsIndent { + buttons []PeriodButton = [PeriodButton{ + period: pr.Period.m1 + text: 'M1' + }, PeriodButton{ + period: pr.Period.m5 + text: 'M5' + }] +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/struct_no_extra_attr_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/struct_no_extra_attr_keep.vv new file mode 100644 index 0000000..8278308 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/struct_no_extra_attr_keep.vv @@ -0,0 +1,27 @@ +[typedef] +struct Foo { +} + +[typedef] +struct Bar { + x string + y int +} + +[heap] +struct Baz { + x string + y int +} + +[inline] +struct Spam { + x string + y int +} + +[deprecated] +struct Eggs { + y_y int [json: yY] + x string [deprecated] +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/struct_update_comment_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/struct_update_comment_keep.vv new file mode 100644 index 0000000..1a257d8 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/struct_update_comment_keep.vv @@ -0,0 +1,18 @@ +struct Foo { + name string + age int +} + +struct Foo2 {} + +fn main() { + f := Foo{ + name: 'test' + age: 18 + } + f2 := Foo{ + // before + ...f // after + name: 'f2' + } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/struct_update_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/struct_update_keep.vv new file mode 100644 index 0000000..e550ac1 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/struct_update_keep.vv @@ -0,0 +1,17 @@ +struct Foo { + name string + age int +} + +struct Foo2 {} + +fn main() { + f := Foo{ + name: 'test' + age: 18 + } + f2 := Foo{ + ...f + name: 'f2' + } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/struct_with_fn_fields_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/struct_with_fn_fields_keep.vv new file mode 100644 index 0000000..2f1bd98 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/struct_with_fn_fields_keep.vv @@ -0,0 +1,4 @@ +struct FieldsWithOptionalVoidReturnType { + f fn () ? + g fn () ? +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/structs_expected.vv b/v_windows/v/old/vlib/v/fmt/tests/structs_expected.vv new file mode 100644 index 0000000..ca3fb0c --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/structs_expected.vv @@ -0,0 +1,62 @@ +struct User { + name string // name + name2 []rune // name2 + very_long_field bool + age int // age + very_long_type_field1 very_looooog_type // long + very_long_type_field2 very_loooooooong_type // long +} + +struct FamousUser { + User +pub: + aka string +} + +struct Foo { + field1 int // f1 + field2 string // f2 +pub: + public_field1 int // f1 + public_field2 f64 // f2 +mut: + mut_field string +pub mut: + pub_mut_field string +} + +struct Bar { + Foo +} + +fn new_user() User { + return User{ + name: 'Serious Sam' + age: 19 + } +} + +struct SomeStruct { +mut: + // 1 + // 2 + // 3 + somefield /* 4 */ /* 5 */ int // 6 + // 7 + // 8 + /* + 9 +10 + */ + somefield2 /* 11 */ int // 12 +pub: + somefield3 int + + somefield4 int + /* + 13 +14 + */ +} + +struct C.Foo {} diff --git a/v_windows/v/old/vlib/v/fmt/tests/structs_input.vv b/v_windows/v/old/vlib/v/fmt/tests/structs_input.vv new file mode 100644 index 0000000..c17259a --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/structs_input.vv @@ -0,0 +1,65 @@ +struct User { + name string // name + name2 []rune // name2 + very_long_field bool + age int // age + very_long_type_field1 very_looooog_type // long + very_long_type_field2 very_loooooooong_type // long +} + +struct FamousUser { +pub: + User + aka string +} + +struct Foo { + field1 int // f1 + field2 string // f2 + pub: + public_field1 int // f1 + public_field2 f64 // f2 + mut: + mut_field string + pub mut: + pub_mut_field string +} + +struct Bar { Foo } + +fn new_user() +User +{ + return User{ + name: 'Serious Sam' + age: 19 + } +} + +struct SomeStruct { +// 1 +mut: +// 2 +// 3 +somefield /*4*/ /*5*/ int /*6*/ /*7*/ /*8*/ /* +9 +10 +*/ +somefield2 /*11*/ int // 12 +pub: + + + + +somefield3 int + + + +somefield4 int +/* +13 +14 +*/ +} + +struct C.Foo diff --git a/v_windows/v/old/vlib/v/fmt/tests/sum_smartcast_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/sum_smartcast_keep.vv new file mode 100644 index 0000000..eacdcbd --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/sum_smartcast_keep.vv @@ -0,0 +1,20 @@ +struct S1 { +mut: + i int +} + +struct S2 { +} + +type Sum = S1 | S2 + +fn f(sum Sum) { + if mut sum is S1 { + sum.i++ + } + if sum is S1 { + } + a := [sum] + if a[0] is S2 { + } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/thread_in_a_module_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/thread_in_a_module_keep.vv new file mode 100644 index 0000000..41e7ea6 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/thread_in_a_module_keep.vv @@ -0,0 +1,17 @@ +module my_module + +import another +import other as ooo + +pub fn do_something() { + mut simples := []MyStruct{} + // + mut threads := []thread MyStruct{} + threads.wait() + // + mut another_threads := []thread another.MyStruct{} + another_threads.wait() + // + mut other_threads := []thread ooo.MyStruct{} + other_threads.wait() +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/to_string_2_forms_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/to_string_2_forms_keep.vv new file mode 100644 index 0000000..adb49bd --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/to_string_2_forms_keep.vv @@ -0,0 +1,33 @@ +fn abc() string { + unsafe { + mut fullpath := vcalloc(4) + fullpath[0] = `a` + fullpath[1] = `b` + fullpath[2] = `c` + fullpath[3] = 0 + return fullpath.vstring() + } + return '' +} + +fn def() string { + unsafe { + mut fullpath := vcalloc(4) + fullpath[0] = `a` + fullpath[1] = `b` + fullpath[2] = `c` + fullpath[3] = 0 + return fullpath.vstring_with_len(3) + } + return '' +} + +fn main() { + assert 'abc' == abc() + assert 'abc' == def() + abc_str1 := ptr_str(abc().str) + abc_str2 := ptr_str(abc().str) + println('abc_str1: $abc_str1') + println('abc_str2: $abc_str2') + assert abc_str1 != abc_str2 +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/trailing_space_expected.vv b/v_windows/v/old/vlib/v/fmt/tests/trailing_space_expected.vv new file mode 100644 index 0000000..690540c --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/trailing_space_expected.vv @@ -0,0 +1,10 @@ +// NB: The input file has and *should* have trailing spaces. +// When making changes, please ensure these spaces are not removed. + +fn comments_with_trailing_space() { + // two spaces on the right + /* + spaces after here + and everywhere :) + */ +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/trailing_space_input.vv b/v_windows/v/old/vlib/v/fmt/tests/trailing_space_input.vv new file mode 100644 index 0000000..2b5b448 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/trailing_space_input.vv @@ -0,0 +1,10 @@ +// NB: The input file has and *should* have trailing spaces. +// When making changes, please ensure these spaces are not removed. + +fn comments_with_trailing_space() { + // two spaces on the right + /* + spaces after here + and everywhere :) + */ +} \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/fmt/tests/type_ptr_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/type_ptr_keep.vv new file mode 100644 index 0000000..338b54e --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/type_ptr_keep.vv @@ -0,0 +1,16 @@ +const ( + x = &Test{} + y = []&Test{} + xx = &&Test{} + yy = []&&Test{} +) + +fn test_type_ptr() { + _ := &Test{} + _ := []&Test{} + _ := &&Test{} + _ := []&&Test{} +} + +struct Test { +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/typeof_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/typeof_keep.vv new file mode 100644 index 0000000..93d6cdc --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/typeof_keep.vv @@ -0,0 +1,3 @@ +fn test_typeof() { + println(typeof(x).name) +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/types_expected.vv b/v_windows/v/old/vlib/v/fmt/tests/types_expected.vv new file mode 100644 index 0000000..1eed436 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/types_expected.vv @@ -0,0 +1,27 @@ +// Sumtype +type FooBar = Bar | Foo +pub type PublicBar = Bar | Foo | FooBar + +type Uint = byte | u16 | u32 | u64 // This should stay on the same line +type Float = f32 | f64 + +// Alias type +type MyInt = int + +pub type Abc = f32 + +// Fn type decl + +type EmptyFn = fn () + +type OneArgFn = fn (i int) + +type TwoDiffArgs = fn (i int, s string) bool + +type TwoSameArgs = fn (i int, j int) string // And a comment + +type VarArgs = fn (s ...string) int + +type NOVarArgs = fn (i int, s ...string) f64 + +type NoNameArgs = fn (int, string, ...string) diff --git a/v_windows/v/old/vlib/v/fmt/tests/types_input.vv b/v_windows/v/old/vlib/v/fmt/tests/types_input.vv new file mode 100644 index 0000000..6a5f947 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/types_input.vv @@ -0,0 +1,37 @@ + + + // Sumtype + type FooBar= Foo | Bar + pub type PublicBar = Foo | Bar | FooBar + +type Uint = u16 | u64 + | u32 + | byte // This should stay on the same line +type +Float = + f32 | + f64 + + // Alias type + type MyInt = int + + pub type Abc = f32 + + +// Fn type decl + + type EmptyFn = fn() +type OneArgFn = + fn (i int) +type TwoDiffArgs += fn (i int, s string) bool + + + type TwoSameArgs = fn(i int, j int) string // And a comment + +type VarArgs = fn +(s ...string) int + +type NOVarArgs = fn(i int, s ...string) f64 + +type NoNameArgs = fn( int, string , ...string) diff --git a/v_windows/v/old/vlib/v/fmt/tests/union_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/union_keep.vv new file mode 100644 index 0000000..5872d03 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/union_keep.vv @@ -0,0 +1,4 @@ +union Un { + i int + b byte +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/unsafe_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/unsafe_keep.vv new file mode 100644 index 0000000..d1d6702 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/unsafe_keep.vv @@ -0,0 +1,17 @@ +fn main() { + unsafe { + println('hi') + println('hi2') + } + unsafe { + println('qwer') + } + unsafe { 6 } + x := unsafe { + 5 + } + y := unsafe { 7 } + unsafe {} + unsafe { + } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/void_optional_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/void_optional_keep.vv new file mode 100644 index 0000000..97ed7db --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/void_optional_keep.vv @@ -0,0 +1,7 @@ +fn tt() ? { + return error('error') +} + +fn main() { + tt() or { panic('$err') } +} diff --git a/v_windows/v/old/vlib/v/fmt/tests/vscript_keep.vv b/v_windows/v/old/vlib/v/fmt/tests/vscript_keep.vv new file mode 100644 index 0000000..3f4dfe2 --- /dev/null +++ b/v_windows/v/old/vlib/v/fmt/tests/vscript_keep.vv @@ -0,0 +1,9 @@ +#!/usr/local/bin/v run + +x := 5 +println(x) // comment after +println('b') +// comment between +println('c') +mut numbers := [1, 3, 2] +numbers.sort() // 1, 2, 3 diff --git a/v_windows/v/old/vlib/v/gen/c/array.v b/v_windows/v/old/vlib/v/gen/c/array.v new file mode 100644 index 0000000..9e31f4d --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/c/array.v @@ -0,0 +1,726 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license that can be found in the LICENSE file. +module c + +import strings +import v.ast + +fn (mut g Gen) array_init(node ast.ArrayInit) { + array_type := g.unwrap(node.typ) + mut array_styp := '' + elem_type := g.unwrap(node.elem_type) + mut shared_styp := '' // only needed for shared &[]{...} + is_amp := g.is_amp + g.is_amp = false + if is_amp { + g.out.go_back(1) // delete the `&` already generated in `prefix_expr() + } + if g.is_shared { + shared_styp = g.typ(array_type.typ.set_flag(.shared_f)) + g.writeln('($shared_styp*)__dup_shared_array(&($shared_styp){.mtx = {0}, .val =') + } else if is_amp { + array_styp = g.typ(array_type.typ) + g.write('HEAP($array_styp, ') + } + if array_type.unaliased_sym.kind == .array_fixed { + g.write('{') + if node.has_val { + for i, expr in node.exprs { + if expr.is_auto_deref_var() { + g.write('*') + } + g.expr(expr) + if i != node.exprs.len - 1 { + g.write(', ') + } + } + } else if node.has_default { + g.expr(node.default_expr) + info := array_type.unaliased_sym.info as ast.ArrayFixed + for _ in 1 .. info.size { + g.write(', ') + g.expr(node.default_expr) + } + } else { + g.write('0') + } + g.write('}') + return + } + elem_styp := g.typ(elem_type.typ) + noscan := g.check_noscan(elem_type.typ) + if node.exprs.len == 0 { + is_default_array := elem_type.unaliased_sym.kind == .array && node.has_default + if is_default_array { + g.write('__new_array_with_array_default${noscan}(') + } else { + g.write('__new_array_with_default${noscan}(') + } + if node.has_len { + g.expr(node.len_expr) + g.write(', ') + } else { + g.write('0, ') + } + if node.has_cap { + g.expr(node.cap_expr) + g.write(', ') + } else { + g.write('0, ') + } + if elem_type.unaliased_sym.kind == .function { + g.write('sizeof(voidptr), ') + } else { + g.write('sizeof($elem_styp), ') + } + if is_default_array { + g.write('($elem_styp[]){') + g.expr(node.default_expr) + g.write('}[0])') + } else if node.has_default { + g.write('&($elem_styp[]){') + g.expr(node.default_expr) + g.write('})') + } else if node.has_len && node.elem_type == ast.string_type { + g.write('&($elem_styp[]){') + g.write('_SLIT("")') + g.write('})') + } else if node.has_len && elem_type.unaliased_sym.kind in [.array, .map] { + g.write('(voidptr)&($elem_styp[]){') + g.write(g.type_default(node.elem_type)) + g.write('}[0])') + } else { + g.write('0)') + } + if g.is_shared { + g.write('}, sizeof($shared_styp))') + } else if is_amp { + g.write(')') + } + return + } + len := node.exprs.len + if elem_type.unaliased_sym.kind == .function { + g.write('new_array_from_c_array($len, $len, sizeof(voidptr), _MOV((voidptr[$len]){') + } else { + g.write('new_array_from_c_array${noscan}($len, $len, sizeof($elem_styp), _MOV(($elem_styp[$len]){') + } + if len > 8 { + g.writeln('') + g.write('\t\t') + } + for i, expr in node.exprs { + g.expr_with_cast(expr, node.expr_types[i], node.elem_type) + if i != len - 1 { + if i > 0 && i & 7 == 0 { // i > 0 && i % 8 == 0 + g.writeln(',') + g.write('\t\t') + } else { + g.write(', ') + } + } + } + g.write('}))') + if g.is_shared { + g.write('}, sizeof($shared_styp))') + } else if is_amp { + g.write('), sizeof($array_styp))') + } +} + +// `nums.map(it % 2 == 0)` +fn (mut g Gen) gen_array_map(node ast.CallExpr) { + g.inside_lambda = true + tmp := g.new_tmp_var() + s := g.go_before_stmt(0) + // println('filter s="$s"') + ret_typ := g.typ(node.return_type) + // inp_typ := g.typ(node.receiver_type) + ret_sym := g.table.get_type_symbol(node.return_type) + inp_sym := g.table.get_type_symbol(node.receiver_type) + ret_info := ret_sym.info as ast.Array + ret_elem_type := g.typ(ret_info.elem_type) + inp_info := inp_sym.info as ast.Array + inp_elem_type := g.typ(inp_info.elem_type) + if inp_sym.kind != .array { + verror('map() requires an array') + } + g.empty_line = true + g.write('${g.typ(node.left_type)} ${tmp}_orig = ') + g.expr(node.left) + g.writeln(';') + g.writeln('int ${tmp}_len = ${tmp}_orig.len;') + noscan := g.check_noscan(ret_info.elem_type) + g.writeln('$ret_typ $tmp = __new_array${noscan}(0, ${tmp}_len, sizeof($ret_elem_type));\n') + i := g.new_tmp_var() + g.writeln('for (int $i = 0; $i < ${tmp}_len; ++$i) {') + g.writeln('\t$inp_elem_type it = (($inp_elem_type*) ${tmp}_orig.data)[$i];') + mut is_embed_map_filter := false + mut expr := node.args[0].expr + match mut expr { + ast.AnonFn { + g.write('\t$ret_elem_type ti = ') + g.gen_anon_fn_decl(mut expr) + g.write('${expr.decl.name}(it)') + } + ast.Ident { + g.write('\t$ret_elem_type ti = ') + if expr.kind == .function { + g.write('${c_name(expr.name)}(it)') + } else if expr.kind == .variable { + var_info := expr.var_info() + sym := g.table.get_type_symbol(var_info.typ) + if sym.kind == .function { + g.write('${c_name(expr.name)}(it)') + } else { + g.expr(node.args[0].expr) + } + } else { + g.expr(node.args[0].expr) + } + } + ast.CallExpr { + if expr.name in ['map', 'filter'] { + is_embed_map_filter = true + g.stmt_path_pos << g.out.len + } + g.write('\t$ret_elem_type ti = ') + g.expr(node.args[0].expr) + } + else { + g.write('\t$ret_elem_type ti = ') + g.expr(node.args[0].expr) + } + } + g.writeln(';') + g.writeln('\tarray_push${noscan}((array*)&$tmp, &ti);') + g.writeln('}') + if !is_embed_map_filter { + g.stmt_path_pos << g.out.len + } + g.write('\n') + g.write(s) + g.write(tmp) + g.inside_lambda = false +} + +// `users.sort(a.age < b.age)` +fn (mut g Gen) gen_array_sort(node ast.CallExpr) { + // println('filter s="$s"') + rec_sym := g.table.get_type_symbol(node.receiver_type) + if rec_sym.kind != .array { + println(node.name) + println(g.typ(node.receiver_type)) + // println(rec_sym.kind) + verror('.sort() is an array method') + } + info := rec_sym.info as ast.Array + // No arguments means we are sorting an array of builtins (e.g. `numbers.sort()`) + // The type for the comparison fns is the type of the element itself. + mut typ := info.elem_type + mut is_default := false + mut is_reverse := false + mut compare_fn := '' + if node.args.len == 0 { + is_default = true + } else { + infix_expr := node.args[0].expr as ast.InfixExpr + left_name := '$infix_expr.left' + is_default = left_name in ['a', 'b'] && '$infix_expr.right' in ['a', 'b'] + is_reverse = (left_name.starts_with('a') && infix_expr.op == .gt) + || (left_name.starts_with('b') && infix_expr.op == .lt) + } + if is_default { + // users.sort() or users.sort(a > b) + compare_fn = match typ { + ast.int_type, ast.int_type.to_ptr() { 'compare_ints' } + ast.string_type, ast.string_type.to_ptr() { 'compare_strings' } + else { '' } + } + if compare_fn != '' && is_reverse { + compare_fn += '_reverse' + } + } + if compare_fn == '' { + // `users.sort(a.age > b.age)` + // Generate a comparison function for a custom type + tmp_name := g.new_global_tmp_var() + styp := g.typ(typ).trim('*') + compare_fn = 'compare_${tmp_name}_$styp' + if is_reverse { + compare_fn += '_reverse' + } + // Register a new custom `compare_xxx` function for qsort() + // TODO: move to checker + g.table.register_fn(name: compare_fn, return_type: ast.int_type) + + if node.args.len == 0 { + styp_arg := g.typ(typ) + g.definitions.writeln('int $compare_fn ($styp_arg* a, $styp_arg* b) {') + sym := g.table.get_type_symbol(typ) + if !is_reverse && sym.has_method('<') { + g.definitions.writeln('\tif (${styp}__lt(*a, *b)) { return -1; } else { return 1; }}') + } else if is_reverse && sym.has_method('<') { + g.definitions.writeln('\tif (${styp}__lt(*b, *a)) { return -1; } else { return 1; }}') + } else { + g.definitions.writeln('if (*a < *b) return -1;') + g.definitions.writeln('if (*a > *b) return 1; else return 0; }\n') + } + } else { + infix_expr := node.args[0].expr as ast.InfixExpr + // Variables `a` and `b` are used in the `.sort(a < b)` syntax, so we can reuse them + // when generating the function as long as the args are named the same. + styp_arg := g.typ(typ) + g.definitions.writeln('int $compare_fn ($styp_arg* a, $styp_arg* b) {') + sym := g.table.get_type_symbol(typ) + if !is_reverse && sym.has_method('<') && infix_expr.left.str().len == 1 { + g.definitions.writeln('\tif (${styp}__lt(*a, *b)) { return -1; } else { return 1; }}') + } else if is_reverse && sym.has_method('<') && infix_expr.left.str().len == 1 { + g.definitions.writeln('\tif (${styp}__lt(*b, *a)) { return -1; } else { return 1; }}') + } else { + field_type := g.typ(infix_expr.left_type) + left_expr_str := g.expr_string(infix_expr.left) + right_expr_str := g.expr_string(infix_expr.right) + g.definitions.writeln('\t$field_type a_ = $left_expr_str;') + g.definitions.writeln('\t$field_type b_ = $right_expr_str;') + mut op1, mut op2 := '', '' + if infix_expr.left_type == ast.string_type { + if is_reverse { + op1 = 'string__lt(b_, a_)' + op2 = 'string__lt(a_, b_)' + } else { + op1 = 'string__lt(a_, b_)' + op2 = 'string__lt(b_, a_)' + } + } else { + deref_str := if infix_expr.left_type.is_ptr() { '*' } else { '' } + if is_reverse { + op1 = '${deref_str}a_ > ${deref_str}b_' + op2 = '${deref_str}a_ < ${deref_str}b_' + } else { + op1 = '${deref_str}a_ < ${deref_str}b_' + op2 = '${deref_str}a_ > ${deref_str}b_' + } + } + g.definitions.writeln('\tif ($op1) return -1;') + g.definitions.writeln('\tif ($op2) return 1; \n\telse return 0; \n}\n') + } + } + } + if is_reverse && !compare_fn.ends_with('_reverse') { + compare_fn += '_reverse' + } + // + deref := if node.left_type.is_ptr() || node.left_type.is_pointer() { '->' } else { '.' } + // eprintln('> qsort: pointer $node.left_type | deref: `$deref`') + g.empty_line = true + g.write('qsort(') + g.expr(node.left) + g.write('${deref}data, ') + g.expr(node.left) + g.write('${deref}len, ') + g.expr(node.left) + g.write('${deref}element_size, (int (*)(const void *, const void *))&$compare_fn)') +} + +// `nums.filter(it % 2 == 0)` +fn (mut g Gen) gen_array_filter(node ast.CallExpr) { + tmp := g.new_tmp_var() + s := g.go_before_stmt(0) + // println('filter s="$s"') + sym := g.table.get_type_symbol(node.return_type) + if sym.kind != .array { + verror('filter() requires an array') + } + info := sym.info as ast.Array + styp := g.typ(node.return_type) + elem_type_str := g.typ(info.elem_type) + g.empty_line = true + g.write('${g.typ(node.left_type)} ${tmp}_orig = ') + g.expr(node.left) + g.writeln(';') + g.writeln('int ${tmp}_len = ${tmp}_orig.len;') + noscan := g.check_noscan(info.elem_type) + g.writeln('$styp $tmp = __new_array${noscan}(0, ${tmp}_len, sizeof($elem_type_str));\n') + i := g.new_tmp_var() + g.writeln('for (int $i = 0; $i < ${tmp}_len; ++$i) {') + g.writeln('\t$elem_type_str it = (($elem_type_str*) ${tmp}_orig.data)[$i];') + mut is_embed_map_filter := false + mut expr := node.args[0].expr + match mut expr { + ast.AnonFn { + g.write('\tif (') + g.gen_anon_fn_decl(mut expr) + g.write('${expr.decl.name}(it)') + } + ast.Ident { + g.write('\tif (') + if expr.kind == .function { + g.write('${c_name(expr.name)}(it)') + } else if expr.kind == .variable { + var_info := expr.var_info() + sym_t := g.table.get_type_symbol(var_info.typ) + if sym_t.kind == .function { + g.write('${c_name(expr.name)}(it)') + } else { + g.expr(node.args[0].expr) + } + } else { + g.expr(node.args[0].expr) + } + } + ast.CallExpr { + if expr.name in ['map', 'filter'] { + is_embed_map_filter = true + g.stmt_path_pos << g.out.len + } + g.write('\tif (') + g.expr(node.args[0].expr) + } + else { + g.write('\tif (') + g.expr(node.args[0].expr) + } + } + g.writeln(') {') + g.writeln('\t\tarray_push${noscan}((array*)&$tmp, &it); \n\t\t}') + g.writeln('}') + if !is_embed_map_filter { + g.stmt_path_pos << g.out.len + } + g.write('\n') + g.write(s) + g.write(tmp) +} + +// `nums.insert(0, 2)` `nums.insert(0, [2,3,4])` +fn (mut g Gen) gen_array_insert(node ast.CallExpr) { + left_sym := g.table.get_type_symbol(node.left_type) + left_info := left_sym.info as ast.Array + elem_type_str := g.typ(left_info.elem_type) + arg2_sym := g.table.get_type_symbol(node.args[1].typ) + is_arg2_array := arg2_sym.kind == .array && node.args[1].typ == node.left_type + noscan := g.check_noscan(left_info.elem_type) + if is_arg2_array { + g.write('array_insert_many${noscan}(&') + } else { + g.write('array_insert${noscan}(&') + } + g.expr(node.left) + g.write(', ') + g.expr(node.args[0].expr) + if is_arg2_array { + g.write(', ') + g.expr(node.args[1].expr) + g.write('.data, ') + g.expr(node.args[1].expr) + g.write('.len)') + } else { + g.write(', &($elem_type_str[]){') + if left_info.elem_type == ast.string_type { + g.write('string_clone(') + } + g.expr(node.args[1].expr) + if left_info.elem_type == ast.string_type { + g.write(')') + } + g.write('})') + } +} + +// `nums.prepend(2)` `nums.prepend([2,3,4])` +fn (mut g Gen) gen_array_prepend(node ast.CallExpr) { + left_sym := g.table.get_type_symbol(node.left_type) + left_info := left_sym.info as ast.Array + elem_type_str := g.typ(left_info.elem_type) + arg_sym := g.table.get_type_symbol(node.args[0].typ) + is_arg_array := arg_sym.kind == .array && node.args[0].typ == node.left_type + noscan := g.check_noscan(left_info.elem_type) + if is_arg_array { + g.write('array_prepend_many${noscan}(&') + } else { + g.write('array_prepend${noscan}(&') + } + g.expr(node.left) + if is_arg_array { + g.write(', ') + g.expr(node.args[0].expr) + g.write('.data, ') + g.expr(node.args[0].expr) + g.write('.len)') + } else { + g.write(', &($elem_type_str[]){') + g.expr(node.args[0].expr) + g.write('})') + } +} + +fn (mut g Gen) gen_array_contains_method(left_type ast.Type) string { + unwrap_left_type := g.unwrap_generic(left_type) + mut left_sym := g.table.get_type_symbol(unwrap_left_type) + left_final_sym := g.table.get_final_type_symbol(unwrap_left_type) + mut left_type_str := g.typ(unwrap_left_type).replace('*', '') + fn_name := '${left_type_str}_contains' + if !left_sym.has_method('contains') { + left_info := left_final_sym.info as ast.Array + mut elem_type_str := g.typ(left_info.elem_type) + elem_sym := g.table.get_type_symbol(left_info.elem_type) + if elem_sym.kind == .function { + left_type_str = 'Array_voidptr' + elem_type_str = 'voidptr' + } + g.type_definitions.writeln('static bool ${fn_name}($left_type_str a, $elem_type_str v); // auto') + mut fn_builder := strings.new_builder(512) + fn_builder.writeln('static bool ${fn_name}($left_type_str a, $elem_type_str v) {') + fn_builder.writeln('\tfor (int i = 0; i < a.len; ++i) {') + if elem_sym.kind == .string { + fn_builder.writeln('\t\tif (fast_string_eq(((string*)a.data)[i], v)) {') + } else if elem_sym.kind == .array && left_info.elem_type.nr_muls() == 0 { + ptr_typ := g.gen_array_equality_fn(left_info.elem_type) + fn_builder.writeln('\t\tif (${ptr_typ}_arr_eq((($elem_type_str*)a.data)[i], v)) {') + } else if elem_sym.kind == .function { + fn_builder.writeln('\t\tif (((voidptr*)a.data)[i] == v) {') + } else if elem_sym.kind == .map && left_info.elem_type.nr_muls() == 0 { + ptr_typ := g.gen_map_equality_fn(left_info.elem_type) + fn_builder.writeln('\t\tif (${ptr_typ}_map_eq((($elem_type_str*)a.data)[i], v)) {') + } else if elem_sym.kind == .struct_ && left_info.elem_type.nr_muls() == 0 { + ptr_typ := g.gen_struct_equality_fn(left_info.elem_type) + fn_builder.writeln('\t\tif (${ptr_typ}_struct_eq((($elem_type_str*)a.data)[i], v)) {') + } else { + fn_builder.writeln('\t\tif ((($elem_type_str*)a.data)[i] == v) {') + } + fn_builder.writeln('\t\t\treturn true;') + fn_builder.writeln('\t\t}') + fn_builder.writeln('\t}') + fn_builder.writeln('\treturn false;') + fn_builder.writeln('}') + g.auto_fn_definitions << fn_builder.str() + left_sym.register_method(&ast.Fn{ + name: 'contains' + params: [ast.Param{ + typ: unwrap_left_type + }, ast.Param{ + typ: left_info.elem_type + }] + }) + } + return fn_name +} + +// `nums.contains(2)` +fn (mut g Gen) gen_array_contains(node ast.CallExpr) { + fn_name := g.gen_array_contains_method(node.left_type) + g.write('${fn_name}(') + if node.left_type.is_ptr() { + g.write('*') + } + g.expr(node.left) + g.write(', ') + g.expr(node.args[0].expr) + g.write(')') +} + +fn (mut g Gen) gen_array_index_method(left_type ast.Type) string { + unwrap_left_type := g.unwrap_generic(left_type) + mut left_sym := g.table.get_type_symbol(unwrap_left_type) + mut left_type_str := g.typ(unwrap_left_type).trim('*') + fn_name := '${left_type_str}_index' + if !left_sym.has_method('index') { + info := left_sym.info as ast.Array + mut elem_type_str := g.typ(info.elem_type) + elem_sym := g.table.get_type_symbol(info.elem_type) + if elem_sym.kind == .function { + left_type_str = 'Array_voidptr' + elem_type_str = 'voidptr' + } + g.type_definitions.writeln('static int ${fn_name}($left_type_str a, $elem_type_str v); // auto') + mut fn_builder := strings.new_builder(512) + fn_builder.writeln('static int ${fn_name}($left_type_str a, $elem_type_str v) {') + fn_builder.writeln('\t$elem_type_str* pelem = a.data;') + fn_builder.writeln('\tfor (int i = 0; i < a.len; ++i, ++pelem) {') + if elem_sym.kind == .string { + fn_builder.writeln('\t\tif (fast_string_eq(( *pelem, v))) {') + } else if elem_sym.kind == .array && !info.elem_type.is_ptr() { + ptr_typ := g.gen_array_equality_fn(info.elem_type) + fn_builder.writeln('\t\tif (${ptr_typ}_arr_eq( *pelem, v)) {') + } else if elem_sym.kind == .function && !info.elem_type.is_ptr() { + fn_builder.writeln('\t\tif ( pelem == v) {') + } else if elem_sym.kind == .map && !info.elem_type.is_ptr() { + ptr_typ := g.gen_map_equality_fn(info.elem_type) + fn_builder.writeln('\t\tif (${ptr_typ}_map_eq(( *pelem, v))) {') + } else if elem_sym.kind == .struct_ && !info.elem_type.is_ptr() { + ptr_typ := g.gen_struct_equality_fn(info.elem_type) + fn_builder.writeln('\t\tif (${ptr_typ}_struct_eq( *pelem, v)) {') + } else { + fn_builder.writeln('\t\tif (*pelem == v) {') + } + fn_builder.writeln('\t\t\treturn i;') + fn_builder.writeln('\t\t}') + fn_builder.writeln('\t}') + fn_builder.writeln('\treturn -1;') + fn_builder.writeln('}') + g.auto_fn_definitions << fn_builder.str() + left_sym.register_method(&ast.Fn{ + name: 'index' + params: [ast.Param{ + typ: unwrap_left_type + }, ast.Param{ + typ: info.elem_type + }] + }) + } + return fn_name +} + +// `nums.index(2)` +fn (mut g Gen) gen_array_index(node ast.CallExpr) { + fn_name := g.gen_array_index_method(node.left_type) + g.write('${fn_name}(') + if node.left_type.is_ptr() { + g.write('*') + } + g.expr(node.left) + g.write(', ') + g.expr(node.args[0].expr) + g.write(')') +} + +fn (mut g Gen) gen_array_wait(node ast.CallExpr) { + arr := g.table.get_type_symbol(node.receiver_type) + thread_type := arr.array_info().elem_type + thread_sym := g.table.get_type_symbol(thread_type) + thread_ret_type := thread_sym.thread_info().return_type + eltyp := g.table.get_type_symbol(thread_ret_type).cname + fn_name := g.register_thread_array_wait_call(eltyp) + g.write('${fn_name}(') + g.expr(node.left) + g.write(')') +} + +fn (mut g Gen) gen_array_any(node ast.CallExpr) { + tmp := g.new_tmp_var() + s := g.go_before_stmt(0) + sym := g.table.get_type_symbol(node.left_type) + info := sym.info as ast.Array + // styp := g.typ(node.return_type) + elem_type_str := g.typ(info.elem_type) + g.empty_line = true + g.write('${g.typ(node.left_type)} ${tmp}_orig = ') + g.expr(node.left) + g.writeln(';') + g.writeln('int ${tmp}_len = ${tmp}_orig.len;') + g.writeln('bool $tmp = false;') + i := g.new_tmp_var() + g.writeln('for (int $i = 0; $i < ${tmp}_len; ++$i) {') + g.writeln('\t$elem_type_str it = (($elem_type_str*) ${tmp}_orig.data)[$i];') + mut is_embed_map_filter := false + mut expr := node.args[0].expr + match mut expr { + ast.AnonFn { + g.write('\tif (') + g.gen_anon_fn_decl(mut expr) + g.write('${expr.decl.name}(it)') + } + ast.Ident { + g.write('\tif (') + if expr.kind == .function { + g.write('${c_name(expr.name)}(it)') + } else if expr.kind == .variable { + var_info := expr.var_info() + sym_t := g.table.get_type_symbol(var_info.typ) + if sym_t.kind == .function { + g.write('${c_name(expr.name)}(it)') + } else { + g.expr(node.args[0].expr) + } + } else { + g.expr(node.args[0].expr) + } + } + ast.CallExpr { + if expr.name in ['map', 'filter'] { + is_embed_map_filter = true + g.stmt_path_pos << g.out.len + } + g.write('\tif (') + g.expr(node.args[0].expr) + } + else { + g.write('\tif (') + g.expr(node.args[0].expr) + } + } + g.writeln(') {') + g.writeln('\t\t$tmp = true;\n\t\t\tbreak;\n\t\t}') + g.writeln('}') + if !is_embed_map_filter { + g.stmt_path_pos << g.out.len + } + g.write('\n') + g.write(s) + g.write(tmp) +} + +fn (mut g Gen) gen_array_all(node ast.CallExpr) { + tmp := g.new_tmp_var() + s := g.go_before_stmt(0) + sym := g.table.get_type_symbol(node.left_type) + info := sym.info as ast.Array + // styp := g.typ(node.return_type) + elem_type_str := g.typ(info.elem_type) + g.empty_line = true + g.write('${g.typ(node.left_type)} ${tmp}_orig = ') + g.expr(node.left) + g.writeln(';') + g.writeln('int ${tmp}_len = ${tmp}_orig.len;') + g.writeln('bool $tmp = true;') + i := g.new_tmp_var() + g.writeln('for (int $i = 0; $i < ${tmp}_len; ++$i) {') + g.writeln('\t$elem_type_str it = (($elem_type_str*) ${tmp}_orig.data)[$i];') + mut is_embed_map_filter := false + mut expr := node.args[0].expr + match mut expr { + ast.AnonFn { + g.write('\tif (!(') + g.gen_anon_fn_decl(mut expr) + g.write('${expr.decl.name}(it)') + } + ast.Ident { + g.write('\tif (!(') + if expr.kind == .function { + g.write('${c_name(expr.name)}(it)') + } else if expr.kind == .variable { + var_info := expr.var_info() + sym_t := g.table.get_type_symbol(var_info.typ) + if sym_t.kind == .function { + g.write('${c_name(expr.name)}(it)') + } else { + g.expr(node.args[0].expr) + } + } else { + g.expr(node.args[0].expr) + } + } + ast.CallExpr { + if expr.name in ['map', 'filter'] { + is_embed_map_filter = true + g.stmt_path_pos << g.out.len + } + g.write('\tif (!(') + g.expr(node.args[0].expr) + } + else { + g.write('\tif (!(') + g.expr(node.args[0].expr) + } + } + g.writeln(')) {') + g.writeln('\t\t$tmp = false;\n\t\t\tbreak;\n\t\t}') + g.writeln('}') + if !is_embed_map_filter { + g.stmt_path_pos << g.out.len + } + g.write('\n') + g.write(s) + g.write(tmp) +} diff --git a/v_windows/v/old/vlib/v/gen/c/assert.v b/v_windows/v/old/vlib/v/gen/c/assert.v new file mode 100644 index 0000000..bc20b78 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/c/assert.v @@ -0,0 +1,128 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module c + +import v.ast + +fn (mut g Gen) gen_assert_stmt(original_assert_statement ast.AssertStmt) { + if !original_assert_statement.is_used { + return + } + mut node := original_assert_statement + g.writeln('// assert') + if mut node.expr is ast.InfixExpr { + if mut node.expr.left is ast.CallExpr { + node.expr.left = g.new_ctemp_var_then_gen(node.expr.left, node.expr.left_type) + } + if mut node.expr.right is ast.CallExpr { + node.expr.right = g.new_ctemp_var_then_gen(node.expr.right, node.expr.right_type) + } + } + g.inside_ternary++ + if g.is_test { + g.write('if (') + g.expr(node.expr) + g.write(')') + g.decrement_inside_ternary() + g.writeln(' {') + g.writeln('\tg_test_oks++;') + metaname_ok := g.gen_assert_metainfo(node) + g.writeln('\tmain__cb_assertion_ok(&$metaname_ok);') + g.writeln('} else {') + g.writeln('\tg_test_fails++;') + metaname_fail := g.gen_assert_metainfo(node) + g.writeln('\tmain__cb_assertion_failed(&$metaname_fail);') + g.gen_assert_postfailure_mode(node) + g.writeln('\tlongjmp(g_jump_buffer, 1);') + g.writeln('\t// TODO') + g.writeln('\t// Maybe print all vars in a test function if it fails?') + g.writeln('}') + } else { + g.write('if (!(') + g.expr(node.expr) + g.write('))') + g.decrement_inside_ternary() + g.writeln(' {') + metaname_panic := g.gen_assert_metainfo(node) + g.writeln('\t__print_assert_failure(&$metaname_panic);') + g.gen_assert_postfailure_mode(node) + g.writeln('\t_v_panic(_SLIT("Assertion failed..."));') + g.writeln('}') + } +} + +fn (mut g Gen) gen_assert_postfailure_mode(node ast.AssertStmt) { + g.write_v_source_line_info(node.pos) + match g.pref.assert_failure_mode { + .default {} + .aborts { + g.writeln('\tabort();') + } + .backtraces { + g.writeln('\tprint_backtrace();') + } + } +} + +fn (mut g Gen) gen_assert_metainfo(node ast.AssertStmt) string { + mod_path := cestring(g.file.path) + fn_name := g.fn_decl.name + line_nr := node.pos.line_nr + src := cestring(node.expr.str()) + metaname := 'v_assert_meta_info_$g.new_tmp_var()' + g.writeln('\tVAssertMetaInfo $metaname = {0};') + g.writeln('\t${metaname}.fpath = ${ctoslit(mod_path)};') + g.writeln('\t${metaname}.line_nr = $line_nr;') + g.writeln('\t${metaname}.fn_name = ${ctoslit(fn_name)};') + metasrc := cnewlines(ctoslit(src)) + g.writeln('\t${metaname}.src = $metasrc;') + match mut node.expr { + ast.InfixExpr { + expr_op_str := ctoslit(node.expr.op.str()) + expr_left_str := cnewlines(ctoslit(node.expr.left.str())) + expr_right_str := cnewlines(ctoslit(node.expr.right.str())) + g.writeln('\t${metaname}.op = $expr_op_str;') + g.writeln('\t${metaname}.llabel = $expr_left_str;') + g.writeln('\t${metaname}.rlabel = $expr_right_str;') + g.write('\t${metaname}.lvalue = ') + g.gen_assert_single_expr(node.expr.left, node.expr.left_type) + g.writeln(';') + g.write('\t${metaname}.rvalue = ') + g.gen_assert_single_expr(node.expr.right, node.expr.right_type) + g.writeln(';') + } + ast.CallExpr { + g.writeln('\t${metaname}.op = _SLIT("call");') + } + else {} + } + return metaname +} + +fn (mut g Gen) gen_assert_single_expr(expr ast.Expr, typ ast.Type) { + unknown_value := '*unknown value*' + match expr { + ast.CastExpr, ast.IndexExpr, ast.MatchExpr { + g.write(ctoslit(unknown_value)) + } + ast.PrefixExpr { + if expr.right is ast.CastExpr { + // TODO: remove this check; + // vlib/builtin/map_test.v (a map of &int, set to &int(0)) fails + // without special casing ast.CastExpr here + g.write(ctoslit(unknown_value)) + } else { + g.gen_expr_to_string(expr, typ) + } + } + ast.TypeNode { + sym := g.table.get_type_symbol(g.unwrap_generic(typ)) + g.write(ctoslit('$sym.name')) + } + else { + g.gen_expr_to_string(expr, typ) + } + } + g.write(' /* typeof: ' + expr.type_name() + ' type: ' + typ.str() + ' */ ') +} diff --git a/v_windows/v/old/vlib/v/gen/c/auto_eq_methods.v b/v_windows/v/old/vlib/v/gen/c/auto_eq_methods.v new file mode 100644 index 0000000..16dd339 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/c/auto_eq_methods.v @@ -0,0 +1,343 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license that can be found in the LICENSE file. +module c + +import strings +import v.ast + +fn (mut g Gen) gen_sumtype_equality_fn(left_type ast.Type) string { + left := g.unwrap(left_type) + ptr_styp := g.typ(left.typ.set_nr_muls(0)) + if ptr_styp in g.sumtype_fn_definitions { + return ptr_styp + } + g.sumtype_fn_definitions << ptr_styp + info := left.sym.sumtype_info() + g.type_definitions.writeln('static bool ${ptr_styp}_sumtype_eq($ptr_styp a, $ptr_styp b); // auto') + + mut fn_builder := strings.new_builder(512) + fn_builder.writeln('static bool ${ptr_styp}_sumtype_eq($ptr_styp a, $ptr_styp b) {') + fn_builder.writeln('\tif (a._typ != b._typ) { return false; }') + for typ in info.variants { + variant := g.unwrap(typ) + fn_builder.writeln('\tif (a._typ == $variant.typ) {') + name := '_$variant.sym.cname' + if variant.sym.kind == .string { + fn_builder.writeln('\t\treturn string__eq(*a.$name, *b.$name);') + } else if variant.sym.kind == .sum_type && !typ.is_ptr() { + eq_fn := g.gen_sumtype_equality_fn(typ) + fn_builder.writeln('\t\treturn ${eq_fn}_sumtype_eq(*a.$name, *b.$name);') + } else if variant.sym.kind == .struct_ && !typ.is_ptr() { + eq_fn := g.gen_struct_equality_fn(typ) + fn_builder.writeln('\t\treturn ${eq_fn}_struct_eq(*a.$name, *b.$name);') + } else if variant.sym.kind == .array && !typ.is_ptr() { + eq_fn := g.gen_array_equality_fn(typ) + fn_builder.writeln('\t\treturn ${eq_fn}_arr_eq(*a.$name, *b.$name);') + } else if variant.sym.kind == .array_fixed && !typ.is_ptr() { + eq_fn := g.gen_fixed_array_equality_fn(typ) + fn_builder.writeln('\t\treturn ${eq_fn}_arr_eq(*a.$name, *b.$name);') + } else if variant.sym.kind == .map && !typ.is_ptr() { + eq_fn := g.gen_map_equality_fn(typ) + fn_builder.writeln('\t\treturn ${eq_fn}_map_eq(*a.$name, *b.$name);') + } else if variant.sym.kind == .alias && !typ.is_ptr() { + eq_fn := g.gen_alias_equality_fn(typ) + fn_builder.writeln('\t\treturn ${eq_fn}_alias_eq(*a.$name, *b.$name);') + } else if variant.sym.kind == .function { + fn_builder.writeln('\t\treturn *((voidptr*)(*a.$name)) == *((voidptr*)(*b.$name));') + } else { + fn_builder.writeln('\t\treturn *a.$name == *b.$name;') + } + fn_builder.writeln('\t}') + } + fn_builder.writeln('\treturn false;') + fn_builder.writeln('}') + g.auto_fn_definitions << fn_builder.str() + return ptr_styp +} + +fn (mut g Gen) gen_struct_equality_fn(left_type ast.Type) string { + left := g.unwrap(left_type) + ptr_styp := g.typ(left.typ.set_nr_muls(0)) + if ptr_styp in g.struct_fn_definitions { + return ptr_styp + } + g.struct_fn_definitions << ptr_styp + info := left.sym.struct_info() + g.type_definitions.writeln('static bool ${ptr_styp}_struct_eq($ptr_styp a, $ptr_styp b); // auto') + + mut fn_builder := strings.new_builder(512) + defer { + g.auto_fn_definitions << fn_builder.str() + } + fn_builder.writeln('static bool ${ptr_styp}_struct_eq($ptr_styp a, $ptr_styp b) {') + + // overloaded + if left.sym.has_method('==') { + fn_builder.writeln('\treturn ${ptr_styp}__eq(a, b);') + fn_builder.writeln('}') + return ptr_styp + } + + fn_builder.write_string('\treturn ') + if info.fields.len > 0 { + for i, field in info.fields { + if i > 0 { + fn_builder.write_string('\n\t\t&& ') + } + field_type := g.unwrap(field.typ) + if field_type.sym.kind == .string { + fn_builder.write_string('string__eq(a.$field.name, b.$field.name)') + } else if field_type.sym.kind == .sum_type && !field.typ.is_ptr() { + eq_fn := g.gen_sumtype_equality_fn(field.typ) + fn_builder.write_string('${eq_fn}_sumtype_eq(a.$field.name, b.$field.name)') + } else if field_type.sym.kind == .struct_ && !field.typ.is_ptr() { + eq_fn := g.gen_struct_equality_fn(field.typ) + fn_builder.write_string('${eq_fn}_struct_eq(a.$field.name, b.$field.name)') + } else if field_type.sym.kind == .array && !field.typ.is_ptr() { + eq_fn := g.gen_array_equality_fn(field.typ) + fn_builder.write_string('${eq_fn}_arr_eq(a.$field.name, b.$field.name)') + } else if field_type.sym.kind == .array_fixed && !field.typ.is_ptr() { + eq_fn := g.gen_fixed_array_equality_fn(field.typ) + fn_builder.write_string('${eq_fn}_arr_eq(a.$field.name, b.$field.name)') + } else if field_type.sym.kind == .map && !field.typ.is_ptr() { + eq_fn := g.gen_map_equality_fn(field.typ) + fn_builder.write_string('${eq_fn}_map_eq(a.$field.name, b.$field.name)') + } else if field_type.sym.kind == .alias && !field.typ.is_ptr() { + eq_fn := g.gen_alias_equality_fn(field.typ) + fn_builder.write_string('${eq_fn}_alias_eq(a.$field.name, b.$field.name)') + } else if field_type.sym.kind == .function { + fn_builder.write_string('*((voidptr*)(a.$field.name)) == *((voidptr*)(b.$field.name))') + } else { + fn_builder.write_string('a.$field.name == b.$field.name') + } + } + } else { + fn_builder.write_string('true') + } + fn_builder.writeln(';') + fn_builder.writeln('}') + return ptr_styp +} + +fn (mut g Gen) gen_alias_equality_fn(left_type ast.Type) string { + left := g.unwrap(left_type) + ptr_styp := g.typ(left.typ.set_nr_muls(0)) + if ptr_styp in g.alias_fn_definitions { + return ptr_styp + } + g.alias_fn_definitions << ptr_styp + info := left.sym.info as ast.Alias + g.type_definitions.writeln('static bool ${ptr_styp}_alias_eq($ptr_styp a, $ptr_styp b); // auto') + + mut fn_builder := strings.new_builder(512) + fn_builder.writeln('static bool ${ptr_styp}_alias_eq($ptr_styp a, $ptr_styp b) {') + sym := g.table.get_type_symbol(info.parent_type) + if sym.kind == .string { + fn_builder.writeln('\treturn string__eq(a, b);') + } else if sym.kind == .sum_type && !left.typ.is_ptr() { + eq_fn := g.gen_sumtype_equality_fn(info.parent_type) + fn_builder.writeln('\treturn ${eq_fn}_sumtype_eq(a, b);') + } else if sym.kind == .struct_ && !left.typ.is_ptr() { + eq_fn := g.gen_struct_equality_fn(info.parent_type) + fn_builder.writeln('\treturn ${eq_fn}_struct_eq(a, b);') + } else if sym.kind == .array && !left.typ.is_ptr() { + eq_fn := g.gen_array_equality_fn(info.parent_type) + fn_builder.writeln('\treturn ${eq_fn}_arr_eq(a, b);') + } else if sym.kind == .array_fixed && !left.typ.is_ptr() { + eq_fn := g.gen_fixed_array_equality_fn(info.parent_type) + fn_builder.writeln('\treturn ${eq_fn}_arr_eq(a, b);') + } else if sym.kind == .map && !left.typ.is_ptr() { + eq_fn := g.gen_map_equality_fn(info.parent_type) + fn_builder.writeln('\treturn ${eq_fn}_map_eq(a, b);') + } else if sym.kind == .function { + fn_builder.writeln('\treturn *((voidptr*)(a)) == *((voidptr*)(b));') + } else { + fn_builder.writeln('\treturn a == b;') + } + fn_builder.writeln('}') + g.auto_fn_definitions << fn_builder.str() + return ptr_styp +} + +fn (mut g Gen) gen_array_equality_fn(left_type ast.Type) string { + left := g.unwrap(left_type) + ptr_styp := g.typ(left.typ.set_nr_muls(0)) + if ptr_styp in g.array_fn_definitions { + return ptr_styp + } + g.array_fn_definitions << ptr_styp + elem := g.unwrap(left.sym.array_info().elem_type) + ptr_elem_styp := g.typ(elem.typ) + g.type_definitions.writeln('static bool ${ptr_styp}_arr_eq($ptr_styp a, $ptr_styp b); // auto') + + mut fn_builder := strings.new_builder(512) + fn_builder.writeln('static bool ${ptr_styp}_arr_eq($ptr_styp a, $ptr_styp b) {') + fn_builder.writeln('\tif (a.len != b.len) {') + fn_builder.writeln('\t\treturn false;') + fn_builder.writeln('\t}') + fn_builder.writeln('\tfor (int i = 0; i < a.len; ++i) {') + // compare every pair of elements of the two arrays + if elem.sym.kind == .string { + fn_builder.writeln('\t\tif (!string__eq(*(($ptr_elem_styp*)((byte*)a.data+(i*a.element_size))), *(($ptr_elem_styp*)((byte*)b.data+(i*b.element_size))))) {') + } else if elem.sym.kind == .sum_type && !elem.typ.is_ptr() { + eq_fn := g.gen_sumtype_equality_fn(elem.typ) + fn_builder.writeln('\t\tif (!${eq_fn}_sumtype_eq((($ptr_elem_styp*)a.data)[i], (($ptr_elem_styp*)b.data)[i])) {') + } else if elem.sym.kind == .struct_ && !elem.typ.is_ptr() { + eq_fn := g.gen_struct_equality_fn(elem.typ) + fn_builder.writeln('\t\tif (!${eq_fn}_struct_eq((($ptr_elem_styp*)a.data)[i], (($ptr_elem_styp*)b.data)[i])) {') + } else if elem.sym.kind == .array && !elem.typ.is_ptr() { + eq_fn := g.gen_array_equality_fn(elem.typ) + fn_builder.writeln('\t\tif (!${eq_fn}_arr_eq((($ptr_elem_styp*)a.data)[i], (($ptr_elem_styp*)b.data)[i])) {') + } else if elem.sym.kind == .array_fixed && !elem.typ.is_ptr() { + eq_fn := g.gen_fixed_array_equality_fn(elem.typ) + fn_builder.writeln('\t\tif (!${eq_fn}_arr_eq((($ptr_elem_styp*)a.data)[i], (($ptr_elem_styp*)b.data)[i])) {') + } else if elem.sym.kind == .map && !elem.typ.is_ptr() { + eq_fn := g.gen_map_equality_fn(elem.typ) + fn_builder.writeln('\t\tif (!${eq_fn}_map_eq((($ptr_elem_styp*)a.data)[i], (($ptr_elem_styp*)b.data)[i])) {') + } else if elem.sym.kind == .alias && !elem.typ.is_ptr() { + eq_fn := g.gen_alias_equality_fn(elem.typ) + fn_builder.writeln('\t\tif (!${eq_fn}_alias_eq((($ptr_elem_styp*)a.data)[i], (($ptr_elem_styp*)b.data)[i])) {') + } else if elem.sym.kind == .function { + fn_builder.writeln('\t\tif (*((voidptr*)((byte*)a.data+(i*a.element_size))) != *((voidptr*)((byte*)b.data+(i*b.element_size)))) {') + } else { + fn_builder.writeln('\t\tif (*(($ptr_elem_styp*)((byte*)a.data+(i*a.element_size))) != *(($ptr_elem_styp*)((byte*)b.data+(i*b.element_size)))) {') + } + fn_builder.writeln('\t\t\treturn false;') + fn_builder.writeln('\t\t}') + fn_builder.writeln('\t}') + fn_builder.writeln('\treturn true;') + fn_builder.writeln('}') + g.auto_fn_definitions << fn_builder.str() + return ptr_styp +} + +fn (mut g Gen) gen_fixed_array_equality_fn(left_type ast.Type) string { + left := g.unwrap(left_type) + ptr_styp := g.typ(left.typ.set_nr_muls(0)) + if ptr_styp in g.array_fn_definitions { + return ptr_styp + } + g.array_fn_definitions << ptr_styp + elem_info := left.sym.array_fixed_info() + elem := g.unwrap(elem_info.elem_type) + size := elem_info.size + g.type_definitions.writeln('static bool ${ptr_styp}_arr_eq($ptr_styp a, $ptr_styp b); // auto') + + mut fn_builder := strings.new_builder(512) + fn_builder.writeln('static bool ${ptr_styp}_arr_eq($ptr_styp a, $ptr_styp b) {') + fn_builder.writeln('\tfor (int i = 0; i < $size; ++i) {') + // compare every pair of elements of the two fixed arrays + if elem.sym.kind == .string { + fn_builder.writeln('\t\tif (!string__eq(a[i], b[i])) {') + } else if elem.sym.kind == .sum_type && !elem.typ.is_ptr() { + eq_fn := g.gen_sumtype_equality_fn(elem.typ) + fn_builder.writeln('\t\tif (!${eq_fn}_sumtype_eq(a[i], b[i])) {') + } else if elem.sym.kind == .struct_ && !elem.typ.is_ptr() { + eq_fn := g.gen_struct_equality_fn(elem.typ) + fn_builder.writeln('\t\tif (!${eq_fn}_struct_eq(a[i], b[i])) {') + } else if elem.sym.kind == .array && !elem.typ.is_ptr() { + eq_fn := g.gen_array_equality_fn(elem.typ) + fn_builder.writeln('\t\tif (!${eq_fn}_arr_eq(a[i], b[i])) {') + } else if elem.sym.kind == .array_fixed && !elem.typ.is_ptr() { + eq_fn := g.gen_fixed_array_equality_fn(elem.typ) + fn_builder.writeln('\t\tif (!${eq_fn}_arr_eq(a[i], b[i])) {') + } else if elem.sym.kind == .map && !elem.typ.is_ptr() { + eq_fn := g.gen_map_equality_fn(elem.typ) + fn_builder.writeln('\t\tif (!${eq_fn}_map_eq(a[i], b[i])) {') + } else if elem.sym.kind == .alias && !elem.typ.is_ptr() { + eq_fn := g.gen_alias_equality_fn(elem.typ) + fn_builder.writeln('\t\tif (!${eq_fn}_alias_eq(a[i], b[i])) {') + } else if elem.sym.kind == .function { + fn_builder.writeln('\t\tif (a[i] != b[i]) {') + } else { + fn_builder.writeln('\t\tif (a[i] != b[i]) {') + } + fn_builder.writeln('\t\t\treturn false;') + fn_builder.writeln('\t\t}') + fn_builder.writeln('\t}') + fn_builder.writeln('\treturn true;') + fn_builder.writeln('}') + g.auto_fn_definitions << fn_builder.str() + return ptr_styp +} + +fn (mut g Gen) gen_map_equality_fn(left_type ast.Type) string { + left := g.unwrap(left_type) + ptr_styp := g.typ(left.typ.set_nr_muls(0)) + if ptr_styp in g.map_fn_definitions { + return ptr_styp + } + g.map_fn_definitions << ptr_styp + value := g.unwrap(left.sym.map_info().value_type) + ptr_value_styp := g.typ(value.typ) + g.type_definitions.writeln('static bool ${ptr_styp}_map_eq($ptr_styp a, $ptr_styp b); // auto') + + mut fn_builder := strings.new_builder(512) + fn_builder.writeln('static bool ${ptr_styp}_map_eq($ptr_styp a, $ptr_styp b) {') + fn_builder.writeln('\tif (a.len != b.len) {') + fn_builder.writeln('\t\treturn false;') + fn_builder.writeln('\t}') + fn_builder.writeln('\tfor (int i = 0; i < a.key_values.len; ++i) {') + fn_builder.writeln('\t\tif (!DenseArray_has_index(&a.key_values, i)) continue;') + fn_builder.writeln('\t\tvoidptr k = DenseArray_key(&a.key_values, i);') + fn_builder.writeln('\t\tif (!map_exists(&b, k)) return false;') + kind := g.table.type_kind(value.typ) + if kind == .function { + func := value.sym.info as ast.FnType + ret_styp := g.typ(func.func.return_type) + fn_builder.write_string('\t\t$ret_styp (*v) (') + arg_len := func.func.params.len + for j, arg in func.func.params { + arg_styp := g.typ(arg.typ) + fn_builder.write_string('$arg_styp $arg.name') + if j < arg_len - 1 { + fn_builder.write_string(', ') + } + } + fn_builder.writeln(') = *(voidptr*)map_get(&a, k, &(voidptr[]){ 0 });') + } else { + fn_builder.writeln('\t\t$ptr_value_styp v = *($ptr_value_styp*)map_get(&a, k, &($ptr_value_styp[]){ 0 });') + } + match kind { + .string { + fn_builder.writeln('\t\tif (!fast_string_eq(*(string*)map_get(&b, k, &(string[]){_SLIT("")}), v)) {') + } + .sum_type { + eq_fn := g.gen_sumtype_equality_fn(value.typ) + fn_builder.writeln('\t\tif (!${eq_fn}_sumtype_eq(*($ptr_value_styp*)map_get(&b, k, &($ptr_value_styp[]){ 0 }), v)) {') + } + .struct_ { + eq_fn := g.gen_struct_equality_fn(value.typ) + fn_builder.writeln('\t\tif (!${eq_fn}_struct_eq(*($ptr_value_styp*)map_get(&b, k, &($ptr_value_styp[]){ 0 }), v)) {') + } + .array { + eq_fn := g.gen_array_equality_fn(value.typ) + fn_builder.writeln('\t\tif (!${eq_fn}_arr_eq(*($ptr_value_styp*)map_get(&b, k, &($ptr_value_styp[]){ 0 }), v)) {') + } + .array_fixed { + eq_fn := g.gen_fixed_array_equality_fn(value.typ) + fn_builder.writeln('\t\tif (!${eq_fn}_arr_eq(*($ptr_value_styp*)map_get(&b, k, &($ptr_value_styp[]){ 0 }), v)) {') + } + .map { + eq_fn := g.gen_map_equality_fn(value.typ) + fn_builder.writeln('\t\tif (!${eq_fn}_map_eq(*($ptr_value_styp*)map_get(&b, k, &($ptr_value_styp[]){ 0 }), v)) {') + } + .alias { + eq_fn := g.gen_alias_equality_fn(value.typ) + fn_builder.writeln('\t\tif (!${eq_fn}_alias_eq(*($ptr_value_styp*)map_get(&b, k, &($ptr_value_styp[]){ 0 }), v)) {') + } + .function { + fn_builder.writeln('\t\tif (*(voidptr*)map_get(&b, k, &(voidptr[]){ 0 }) != v) {') + } + else { + fn_builder.writeln('\t\tif (*($ptr_value_styp*)map_get(&b, k, &($ptr_value_styp[]){ 0 }) != v) {') + } + } + fn_builder.writeln('\t\t\treturn false;') + fn_builder.writeln('\t\t}') + fn_builder.writeln('\t}') + fn_builder.writeln('\treturn true;') + fn_builder.writeln('}') + g.auto_fn_definitions << fn_builder.str() + return ptr_styp +} diff --git a/v_windows/v/old/vlib/v/gen/c/auto_str_methods.v b/v_windows/v/old/vlib/v/gen/c/auto_str_methods.v new file mode 100644 index 0000000..ad1fd6b --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/c/auto_str_methods.v @@ -0,0 +1,896 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license that can be found in the LICENSE file. +module c + +import v.ast +import v.util +import strings + +pub enum StrIntpType { + si_no_str = 0 // no parameter to print only fix string + si_c + si_u8 + si_i8 + si_u16 + si_i16 + si_u32 + si_i32 + si_u64 + si_i64 + si_e32 + si_e64 + si_f32 + si_f64 + si_g32 + si_g64 + si_s + si_p + si_vp +} + +pub fn type_to_str(x StrIntpType) string { + match x { + .si_no_str { return 'no_str' } + .si_c { return 'c' } + .si_u8 { return 'u8' } + .si_i8 { return 'i8' } + .si_u16 { return 'u16' } + .si_i16 { return 'i16' } + .si_u32 { return 'u32' } + .si_i32 { return 'i32' } + .si_u64 { return 'u64' } + .si_i64 { return 'i64' } + .si_f32 { return 'f32' } + .si_f64 { return 'f64' } + .si_g32 { return 'f32' } // g32 format use f32 data + .si_g64 { return 'f64' } // g64 format use f64 data + .si_e32 { return 'f32' } // e32 format use f32 data + .si_e64 { return 'f64' } // e64 format use f64 data + .si_s { return 's' } + .si_p { return 'p' } + .si_vp { return 'vp' } + } +} + +pub fn data_str(x StrIntpType) string { + match x { + .si_no_str { return 'no_str' } + .si_c { return 'd_c' } + .si_u8 { return 'd_u8' } + .si_i8 { return 'd_i8' } + .si_u16 { return 'd_u16' } + .si_i16 { return 'd_i16' } + .si_u32 { return 'd_u32' } + .si_i32 { return 'd_i32' } + .si_u64 { return 'd_u64' } + .si_i64 { return 'd_i64' } + .si_f32 { return 'd_f32' } + .si_f64 { return 'd_f64' } + .si_g32 { return 'd_f32' } // g32 format use f32 data + .si_g64 { return 'd_f64' } // g64 format use f64 data + .si_e32 { return 'd_f32' } // e32 format use f32 data + .si_e64 { return 'd_f64' } // e64 format use f64 data + .si_s { return 'd_s' } + .si_p { return 'd_p' } + .si_vp { return 'd_vp' } + } +} + +const ( + // BUG: this const is not released from the memory! use a const for now + // si_s_code = "0x" + int(StrIntpType.si_s).hex() // code for a simple string + si_s_code = '0xfe10' +) + +fn should_use_indent_func(kind ast.Kind) bool { + return kind in [.struct_, .alias, .array, .array_fixed, .map, .sum_type, .interface_] +} + +fn (mut g Gen) gen_str_default(sym ast.TypeSymbol, styp string, str_fn_name string) { + mut convertor := '' + mut typename_ := '' + if sym.parent_idx in ast.integer_type_idxs { + convertor = 'int' + typename_ = 'int' + } else if sym.parent_idx == ast.f32_type_idx { + convertor = 'float' + typename_ = 'f32' + } else if sym.parent_idx == ast.f64_type_idx { + convertor = 'double' + typename_ = 'f64' + } else if sym.parent_idx == ast.bool_type_idx { + convertor = 'bool' + typename_ = 'bool' + } else { + verror("could not generate string method for type '$styp'") + } + g.type_definitions.writeln('string ${str_fn_name}($styp it); // auto') + g.auto_str_funcs.writeln('string ${str_fn_name}($styp it) {') + if convertor == 'bool' { + g.auto_str_funcs.writeln('\tstring tmp1 = string__plus(_SLIT("${styp}("), ($convertor)it ? _SLIT("true") : _SLIT("false"));') + } else { + g.auto_str_funcs.writeln('\tstring tmp1 = string__plus(_SLIT("${styp}("), tos3(${typename_}_str(($convertor)it).str));') + } + g.auto_str_funcs.writeln('\tstring tmp2 = string__plus(tmp1, _SLIT(")"));') + g.auto_str_funcs.writeln('\tstring_free(&tmp1);') + g.auto_str_funcs.writeln('\treturn tmp2;') + g.auto_str_funcs.writeln('}') +} + +fn (mut g Gen) gen_str_for_type(typ ast.Type) string { + styp := g.typ(typ).replace('*', '') + mut sym := g.table.get_type_symbol(g.unwrap_generic(typ)) + mut str_fn_name := styp_to_str_fn_name(styp) + if mut sym.info is ast.Alias { + if sym.info.is_import { + sym = g.table.get_type_symbol(sym.info.parent_type) + str_fn_name = styp_to_str_fn_name(sym.name) + } + } + sym_has_str_method, str_method_expects_ptr, str_nr_args := sym.str_method_info() + already_generated_key := '$styp:$str_fn_name' + if !sym_has_str_method && already_generated_key !in g.str_types && !typ.has_flag(.optional) { + $if debugautostr ? { + eprintln('> gen_str_for_type: |typ: ${typ:5}, ${sym.name:20}|has_str: ${sym_has_str_method:5}|expects_ptr: ${str_method_expects_ptr:5}|nr_args: ${str_nr_args:1}|fn_name: ${str_fn_name:20}') + } + g.str_types << already_generated_key + match mut sym.info { + ast.Alias { + if sym.info.is_import { + g.gen_str_default(sym, styp, str_fn_name) + } else { + g.gen_str_for_alias(sym.info, styp, str_fn_name) + } + } + ast.Array { + g.gen_str_for_array(sym.info, styp, str_fn_name) + } + ast.ArrayFixed { + g.gen_str_for_array_fixed(sym.info, styp, str_fn_name) + } + ast.Enum { + g.gen_str_for_enum(sym.info, styp, str_fn_name) + } + ast.FnType { + g.gen_str_for_fn_type(sym.info, styp, str_fn_name) + } + ast.Struct { + g.gen_str_for_struct(sym.info, styp, str_fn_name) + } + ast.Map { + g.gen_str_for_map(sym.info, styp, str_fn_name) + } + ast.MultiReturn { + g.gen_str_for_multi_return(sym.info, styp, str_fn_name) + } + ast.SumType { + g.gen_str_for_union_sum_type(sym.info, styp, str_fn_name) + } + ast.Interface { + g.gen_str_for_interface(sym.info, styp, str_fn_name) + } + ast.Chan { + g.gen_str_for_chan(sym.info, styp, str_fn_name) + } + ast.Thread { + g.gen_str_for_thread(sym.info, styp, str_fn_name) + } + else { + verror("could not generate string method $str_fn_name for type '$styp'") + } + } + } + if typ.has_flag(.optional) { + option_already_generated_key := 'option_$already_generated_key' + if option_already_generated_key !in g.str_types { + g.gen_str_for_option(typ, styp, str_fn_name) + g.str_types << option_already_generated_key + } + return str_fn_name + } + return str_fn_name +} + +fn (mut g Gen) gen_str_for_option(typ ast.Type, styp string, str_fn_name string) { + parent_type := typ.clear_flag(.optional) + sym := g.table.get_type_symbol(parent_type) + sym_has_str_method, _, _ := sym.str_method_info() + parent_str_fn_name := g.gen_str_for_type(parent_type) + + g.type_definitions.writeln('string ${str_fn_name}($styp it); // auto') + g.auto_str_funcs.writeln('string ${str_fn_name}($styp it) { return indent_${str_fn_name}(it, 0); }') + g.type_definitions.writeln('string indent_${str_fn_name}($styp it, int indent_count); // auto') + g.auto_str_funcs.writeln('string indent_${str_fn_name}($styp it, int indent_count) {') + g.auto_str_funcs.writeln('\tstring res;') + g.auto_str_funcs.writeln('\tif (it.state == 0) {') + if sym.kind == .string { + tmp_res := '${parent_str_fn_name}(*($sym.cname*)it.data)' + g.auto_str_funcs.writeln('\t\tres = ${str_intp_sq(tmp_res)};') + } else if should_use_indent_func(sym.kind) && !sym_has_str_method { + g.auto_str_funcs.writeln('\t\tres = indent_${parent_str_fn_name}(*($sym.cname*)it.data, indent_count);') + } else { + g.auto_str_funcs.writeln('\t\tres = ${parent_str_fn_name}(*($sym.cname*)it.data);') + } + g.auto_str_funcs.writeln('\t} else {') + + tmp_str := str_intp_sub('error: %%', 'IError_str(it.err)') + g.auto_str_funcs.writeln('\t\tres = $tmp_str;') + g.auto_str_funcs.writeln('\t}') + + g.auto_str_funcs.writeln('\treturn ${str_intp_sub('Option(%%)', 'res')};') + g.auto_str_funcs.writeln('}') +} + +fn (mut g Gen) gen_str_for_alias(info ast.Alias, styp string, str_fn_name string) { + parent_str_fn_name := g.gen_str_for_type(info.parent_type) + mut clean_type_v_type_name := util.strip_main_name(styp.replace('__', '.')) + g.type_definitions.writeln('static string ${str_fn_name}($styp it); // auto') + g.auto_str_funcs.writeln('static string ${str_fn_name}($styp it) { return indent_${str_fn_name}(it, 0); }') + g.type_definitions.writeln('static string indent_${str_fn_name}($styp it, int indent_count); // auto') + g.auto_str_funcs.writeln('static string indent_${str_fn_name}($styp it, int indent_count) {') + g.auto_str_funcs.writeln('\tstring indents = string_repeat(_SLIT(" "), indent_count);') + g.auto_str_funcs.writeln('\tstring tmp_ds = ${parent_str_fn_name}(it);') + g.auto_str_funcs.writeln('\tstring res = str_intp(3, _MOV((StrIntpData[]){ + {_SLIT0, $c.si_s_code, {.d_s = indents }}, + {_SLIT("${clean_type_v_type_name}("), $c.si_s_code, {.d_s = tmp_ds }}, + {_SLIT(")"), 0, {.d_c = 0 }} + }));') + g.auto_str_funcs.writeln('\tstring_free(&indents);') + g.auto_str_funcs.writeln('\tstring_free(&tmp_ds);') + g.auto_str_funcs.writeln('\treturn res;') + g.auto_str_funcs.writeln('}') +} + +fn (mut g Gen) gen_str_for_multi_return(info ast.MultiReturn, styp string, str_fn_name string) { + g.type_definitions.writeln('static string ${str_fn_name}($styp a); // auto') + mut fn_builder := strings.new_builder(512) + fn_builder.writeln('static string ${str_fn_name}($styp a) {') + fn_builder.writeln('\tstrings__Builder sb = strings__new_builder($info.types.len * 10);') + fn_builder.writeln('\tstrings__Builder_write_string(&sb, _SLIT("("));') + for i, typ in info.types { + sym := g.table.get_type_symbol(typ) + is_arg_ptr := typ.is_ptr() + sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info() + arg_str_fn_name := g.gen_str_for_type(typ) + + if should_use_indent_func(sym.kind) && !sym_has_str_method { + fn_builder.writeln('\tstrings__Builder_write_string(&sb, ${arg_str_fn_name}(a.arg$i));') + } else if sym.kind in [.f32, .f64] { + if sym.kind == .f32 { + tmp_val := str_intp_g32('a.arg$i') + fn_builder.writeln('\tstrings__Builder_write_string(&sb, $tmp_val);') + } else { + tmp_val := str_intp_g64('a.arg$i') + fn_builder.writeln('\tstrings__Builder_write_string(&sb, $tmp_val);') + } + } else if sym.kind == .string { + tmp_str := str_intp_sq('a.arg$i') + fn_builder.writeln('\tstrings__Builder_write_string(&sb, $tmp_str);') + } else if sym.kind == .function { + fn_builder.writeln('\tstrings__Builder_write_string(&sb, ${arg_str_fn_name}());') + } else { + deref, deref_label := deref_kind(str_method_expects_ptr, is_arg_ptr, typ) + fn_builder.writeln('\t\tstrings__Builder_write_string(&sb, _SLIT("$deref_label"));') + fn_builder.writeln('\tstrings__Builder_write_string(&sb, ${arg_str_fn_name}( $deref a.arg$i));') + } + if i != info.types.len - 1 { + fn_builder.writeln('\tstrings__Builder_write_string(&sb, _SLIT(", "));') + } + } + fn_builder.writeln('\tstrings__Builder_write_string(&sb, _SLIT(")"));') + fn_builder.writeln('\tstring res = strings__Builder_str(&sb);') + fn_builder.writeln('\tstrings__Builder_free(&sb);') + fn_builder.writeln('\treturn res;') + fn_builder.writeln('}') + g.auto_fn_definitions << fn_builder.str() +} + +fn (mut g Gen) gen_str_for_enum(info ast.Enum, styp string, str_fn_name string) { + s := util.no_dots(styp) + g.type_definitions.writeln('static string ${str_fn_name}($styp it); // auto') + g.auto_str_funcs.writeln('static string ${str_fn_name}($styp it) { /* gen_str_for_enum */') + // Enums tagged with `[flag]` are special in that they can be a combination of enum values + if info.is_flag { + clean_name := util.strip_main_name(styp.replace('__', '.')) + g.auto_str_funcs.writeln('\tstring ret = _SLIT("$clean_name{");') + g.auto_str_funcs.writeln('\tint first = 1;') + for i, val in info.vals { + g.auto_str_funcs.writeln('\tif (it & (1 << $i)) {if (!first) {ret = string__plus(ret, _SLIT(" | "));} ret = string__plus(ret, _SLIT(".$val")); first = 0;}') + } + g.auto_str_funcs.writeln('\tret = string__plus(ret, _SLIT("}"));') + g.auto_str_funcs.writeln('\treturn ret;') + } else { + g.auto_str_funcs.writeln('\tswitch(it) {') + // Only use the first multi value on the lookup + mut seen := []string{len: info.vals.len} + for val in info.vals { + if info.is_multi_allowed && val in seen { + continue + } else if info.is_multi_allowed { + seen << val + } + g.auto_str_funcs.writeln('\t\tcase ${s}__$val: return _SLIT("$val");') + } + g.auto_str_funcs.writeln('\t\tdefault: return _SLIT("unknown enum value");') + g.auto_str_funcs.writeln('\t}') + } + g.auto_str_funcs.writeln('}') +} + +fn (mut g Gen) gen_str_for_interface(info ast.Interface, styp string, str_fn_name string) { + // _str() functions should have a single argument, the indenting ones take 2: + g.type_definitions.writeln('static string ${str_fn_name}($styp x); // auto') + g.auto_str_funcs.writeln('static string ${str_fn_name}($styp x) { return indent_${str_fn_name}(x, 0); }') + g.type_definitions.writeln('static string indent_${str_fn_name}($styp x, int indent_count); // auto') + mut fn_builder := strings.new_builder(512) + mut clean_interface_v_type_name := styp.replace('__', '.') + if styp.ends_with('*') { + clean_interface_v_type_name = '&' + clean_interface_v_type_name.replace('*', '') + } + if clean_interface_v_type_name.contains('_T_') { + clean_interface_v_type_name = + clean_interface_v_type_name.replace('Array_', '[]').replace('_T_', '<').replace('_', ', ') + + '>' + } + clean_interface_v_type_name = util.strip_main_name(clean_interface_v_type_name) + fn_builder.writeln('static string indent_${str_fn_name}($styp x, int indent_count) { /* gen_str_for_interface */') + for typ in info.types { + subtype := g.table.get_type_symbol(typ) + mut func_name := g.gen_str_for_type(typ) + sym_has_str_method, str_method_expects_ptr, _ := subtype.str_method_info() + if should_use_indent_func(subtype.kind) && !sym_has_str_method { + func_name = 'indent_$func_name' + } + + // str_intp + deref := if sym_has_str_method && str_method_expects_ptr { ' ' } else { '*' } + if typ == ast.string_type { + mut val := '${func_name}(${deref}($subtype.cname*)x._$subtype.cname' + if should_use_indent_func(subtype.kind) && !sym_has_str_method { + val += ', indent_count' + } + val += ')' + res := 'str_intp(2, _MOV((StrIntpData[]){ + {_SLIT("${clean_interface_v_type_name}(\'"), $c.si_s_code, {.d_s = $val}}, + {_SLIT("\')"), 0, {.d_c = 0 }} + }))' + fn_builder.write_string('\tif (x._typ == _${styp}_${subtype.cname}_index)') + fn_builder.write_string(' return $res;') + } else { + mut val := '${func_name}(${deref}($subtype.cname*)x._$subtype.cname' + if should_use_indent_func(subtype.kind) && !sym_has_str_method { + val += ', indent_count' + } + val += ')' + res := 'str_intp(2, _MOV((StrIntpData[]){ + {_SLIT("${clean_interface_v_type_name}("), $c.si_s_code, {.d_s = $val}}, + {_SLIT(")"), 0, {.d_c = 0 }} + }))' + fn_builder.write_string('\tif (x._typ == _${styp}_${subtype.cname}_index)') + fn_builder.write_string(' return $res;\n') + } + } + fn_builder.writeln('\treturn _SLIT("unknown interface value");') + fn_builder.writeln('}') + g.auto_fn_definitions << fn_builder.str() +} + +fn (mut g Gen) gen_str_for_union_sum_type(info ast.SumType, styp string, str_fn_name string) { + // _str() functions should have a single argument, the indenting ones take 2: + g.type_definitions.writeln('static string ${str_fn_name}($styp x); // auto') + g.auto_str_funcs.writeln('static string ${str_fn_name}($styp x) { return indent_${str_fn_name}(x, 0); }') + g.type_definitions.writeln('static string indent_${str_fn_name}($styp x, int indent_count); // auto') + mut fn_builder := strings.new_builder(512) + fn_builder.writeln('static string indent_${str_fn_name}($styp x, int indent_count) {') + mut clean_sum_type_v_type_name := styp.replace('__', '.') + if styp.ends_with('*') { + clean_sum_type_v_type_name = '&' + clean_sum_type_v_type_name.replace('*', '') + } + if clean_sum_type_v_type_name.contains('_T_') { + clean_sum_type_v_type_name = + clean_sum_type_v_type_name.replace('Array_', '[]').replace('_T_', '<').replace('_', ', ') + + '>' + } + clean_sum_type_v_type_name = util.strip_main_name(clean_sum_type_v_type_name) + fn_builder.writeln('\tswitch(x._typ) {') + for typ in info.variants { + typ_str := g.typ(typ) + mut func_name := g.gen_str_for_type(typ) + sym := g.table.get_type_symbol(typ) + sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info() + deref := if sym_has_str_method && str_method_expects_ptr { ' ' } else { '*' } + if should_use_indent_func(sym.kind) && !sym_has_str_method { + func_name = 'indent_$func_name' + } + + // str_intp + if typ == ast.string_type { + mut val := '${func_name}(${deref}($typ_str*)x._$sym.cname' + if should_use_indent_func(sym.kind) && !sym_has_str_method { + val += ', indent_count' + } + val += ')' + res := 'str_intp(2, _MOV((StrIntpData[]){ + {_SLIT("${clean_sum_type_v_type_name}(\'"), $c.si_s_code, {.d_s = $val}}, + {_SLIT("\')"), 0, {.d_c = 0 }} + }))' + fn_builder.write_string('\t\tcase $typ: return $res;') + } else { + mut val := '${func_name}(${deref}($typ_str*)x._$sym.cname' + if should_use_indent_func(sym.kind) && !sym_has_str_method { + val += ', indent_count' + } + val += ')' + res := 'str_intp(2, _MOV((StrIntpData[]){ + {_SLIT("${clean_sum_type_v_type_name}("), $c.si_s_code, {.d_s = $val}}, + {_SLIT(")"), 0, {.d_c = 0 }} + }))' + fn_builder.write_string('\t\tcase $typ: return $res;') + } + } + fn_builder.writeln('\t\tdefault: return _SLIT("unknown sum type value");') + fn_builder.writeln('\t}') + fn_builder.writeln('}') + g.auto_fn_definitions << fn_builder.str() +} + +fn (mut g Gen) fn_decl_str(info ast.FnType) string { + mut fn_str := 'fn (' + for i, arg in info.func.params { + if arg.is_mut { + fn_str += 'mut ' + } + if i > 0 { + fn_str += ', ' + } + fn_str += util.strip_main_name(g.table.get_type_name(g.unwrap_generic(arg.typ))) + } + fn_str += ')' + if info.func.return_type == ast.ovoid_type { + fn_str += ' ?' + } else if info.func.return_type != ast.void_type { + x := util.strip_main_name(g.table.get_type_name(g.unwrap_generic(info.func.return_type))) + if info.func.return_type.has_flag(.optional) { + fn_str += ' ?$x' + } else { + fn_str += ' $x' + } + } + return fn_str +} + +fn (mut g Gen) gen_str_for_fn_type(info ast.FnType, styp string, str_fn_name string) { + g.type_definitions.writeln('static string ${str_fn_name}(); // auto') + g.auto_str_funcs.writeln('static string ${str_fn_name}() { return _SLIT("${g.fn_decl_str(info)}");}') +} + +fn (mut g Gen) gen_str_for_chan(info ast.Chan, styp string, str_fn_name string) { + elem_type_name := util.strip_main_name(g.table.get_type_name(g.unwrap_generic(info.elem_type))) + g.type_definitions.writeln('static string ${str_fn_name}($styp x); // auto') + g.auto_str_funcs.writeln('static string ${str_fn_name}($styp x) { return sync__Channel_auto_str(x, _SLIT("$elem_type_name")); }') +} + +fn (mut g Gen) gen_str_for_thread(info ast.Thread, styp string, str_fn_name string) { + ret_type_name := util.strip_main_name(g.table.get_type_name(info.return_type)) + g.type_definitions.writeln('static string ${str_fn_name}($styp _); // auto}') + g.auto_str_funcs.writeln('static string ${str_fn_name}($styp _) { return _SLIT("thread($ret_type_name)");}') +} + +[inline] +fn styp_to_str_fn_name(styp string) string { + return styp.replace_each(['*', '', '.', '__', ' ', '__']) + '_str' +} + +fn deref_kind(str_method_expects_ptr bool, is_elem_ptr bool, typ ast.Type) (string, string) { + if str_method_expects_ptr != is_elem_ptr { + if is_elem_ptr { + return '*'.repeat(typ.nr_muls()), '&'.repeat(typ.nr_muls()) + } else { + return '&', '' + } + } + return '', '' +} + +fn (mut g Gen) gen_str_for_array(info ast.Array, styp string, str_fn_name string) { + mut typ := info.elem_type + mut sym := g.table.get_type_symbol(info.elem_type) + if mut sym.info is ast.Alias { + typ = sym.info.parent_type + sym = g.table.get_type_symbol(typ) + } + field_styp := g.typ(typ) + is_elem_ptr := typ.is_ptr() + sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info() + mut elem_str_fn_name := g.gen_str_for_type(typ) + if sym.kind == .byte { + elem_str_fn_name = elem_str_fn_name + '_escaped' + } + + g.type_definitions.writeln('static string ${str_fn_name}($styp a); // auto') + g.auto_str_funcs.writeln('static string ${str_fn_name}($styp a) { return indent_${str_fn_name}(a, 0);}') + g.type_definitions.writeln('static string indent_${str_fn_name}($styp a, int indent_count); // auto') + g.auto_str_funcs.writeln('static string indent_${str_fn_name}($styp a, int indent_count) {') + g.auto_str_funcs.writeln('\tstrings__Builder sb = strings__new_builder(a.len * 10);') + g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _SLIT("["));') + g.auto_str_funcs.writeln('\tfor (int i = 0; i < a.len; ++i) {') + if sym.kind == .function { + g.auto_str_funcs.writeln('\t\tstring x = ${elem_str_fn_name}();') + } else { + if sym.kind == .array_fixed { + g.auto_str_funcs.writeln('\t\t$field_styp it;') + g.auto_str_funcs.writeln('\t\tmemcpy(*($field_styp*)it, (byte*)array_get(a, i), sizeof($field_styp));') + } else { + g.auto_str_funcs.writeln('\t\t$field_styp it = *($field_styp*)array_get(a, i);') + } + if should_use_indent_func(sym.kind) && !sym_has_str_method { + if is_elem_ptr { + g.auto_str_funcs.writeln('\t\tstring x = indent_${elem_str_fn_name}(*it, indent_count);') + } else { + g.auto_str_funcs.writeln('\t\tstring x = indent_${elem_str_fn_name}(it, indent_count);') + } + } else if sym.kind in [.f32, .f64] { + if sym.kind == .f32 { + g.auto_str_funcs.writeln('\t\tstring x = ${str_intp_g32('it')};') + } else { + g.auto_str_funcs.writeln('\t\tstring x = ${str_intp_g64('it')};') + } + } else if sym.kind == .rune { + // Rune are managed at this level as strings + g.auto_str_funcs.writeln('\t\tstring x = str_intp(2, _MOV((StrIntpData[]){{_SLIT("\`"), $c.si_s_code, {.d_s = ${elem_str_fn_name}(it) }}, {_SLIT("\`"), 0, {.d_c = 0 }}}));\n') + } else if sym.kind == .string { + g.auto_str_funcs.writeln('\t\tstring x = str_intp(2, _MOV((StrIntpData[]){{_SLIT("\'"), $c.si_s_code, {.d_s = it }}, {_SLIT("\'"), 0, {.d_c = 0 }}}));\n') + } else { + // There is a custom .str() method, so use it. + // NB: we need to take account of whether the user has defined + // `fn (x T) str() {` or `fn (x &T) str() {`, and convert accordingly + deref, deref_label := deref_kind(str_method_expects_ptr, is_elem_ptr, typ) + g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, _SLIT("$deref_label"));') + g.auto_str_funcs.writeln('\t\tstring x = ${elem_str_fn_name}( $deref it);') + } + } + g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, x);') + if g.is_autofree && typ != ast.bool_type { + // no need to free "true"/"false" literals + g.auto_str_funcs.writeln('\t\tstring_free(&x);') + } + g.auto_str_funcs.writeln('\t\tif (i < a.len-1) {') + g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write_string(&sb, _SLIT(", "));') + g.auto_str_funcs.writeln('\t\t}') + g.auto_str_funcs.writeln('\t}') + g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _SLIT("]"));') + g.auto_str_funcs.writeln('\tstring res = strings__Builder_str(&sb);') + g.auto_str_funcs.writeln('\tstrings__Builder_free(&sb);') + g.auto_str_funcs.writeln('\treturn res;') + g.auto_str_funcs.writeln('}') +} + +fn (mut g Gen) gen_str_for_array_fixed(info ast.ArrayFixed, styp string, str_fn_name string) { + mut typ := info.elem_type + mut sym := g.table.get_type_symbol(info.elem_type) + if mut sym.info is ast.Alias { + typ = sym.info.parent_type + sym = g.table.get_type_symbol(typ) + } + is_elem_ptr := typ.is_ptr() + sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info() + elem_str_fn_name := g.gen_str_for_type(typ) + + g.type_definitions.writeln('static string ${str_fn_name}($styp a); // auto') + g.auto_str_funcs.writeln('static string ${str_fn_name}($styp a) { return indent_${str_fn_name}(a, 0);}') + g.type_definitions.writeln('static string indent_${str_fn_name}($styp a, int indent_count); // auto') + g.auto_str_funcs.writeln('static string indent_${str_fn_name}($styp a, int indent_count) {') + g.auto_str_funcs.writeln('\tstrings__Builder sb = strings__new_builder($info.size * 10);') + g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _SLIT("["));') + g.auto_str_funcs.writeln('\tfor (int i = 0; i < $info.size; ++i) {') + if sym.kind == .function { + g.auto_str_funcs.writeln('\t\tstring x = ${elem_str_fn_name}();') + g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, x);') + } else { + deref, deref_label := deref_kind(str_method_expects_ptr, is_elem_ptr, typ) + if should_use_indent_func(sym.kind) && !sym_has_str_method { + if is_elem_ptr { + g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, _SLIT("$deref_label"));') + g.auto_str_funcs.writeln('\t\tif ( 0 == a[i] ) {') + g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write_string(&sb, _SLIT("0"));') + g.auto_str_funcs.writeln('\t\t}else{') + g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write_string(&sb, ${elem_str_fn_name}( $deref a[i]) );') + g.auto_str_funcs.writeln('\t\t}') + } else { + g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${elem_str_fn_name}( $deref a[i]) );') + } + } else if sym.kind in [.f32, .f64] { + if sym.kind == .f32 { + g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${str_intp_g32('a[i]')} );') + } else { + g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${str_intp_g64('a[i]')} );') + } + } else if sym.kind == .string { + g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${str_intp_sq('a[i]')});') + } else if sym.kind == .rune { + tmp_str := str_intp_rune('${elem_str_fn_name}( $deref a[i])') + g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, $tmp_str);') + } else { + g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${elem_str_fn_name}( $deref a[i]));') + } + } + g.auto_str_funcs.writeln('\t\tif (i < ${info.size - 1}) {') + g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write_string(&sb, _SLIT(", "));') + g.auto_str_funcs.writeln('\t\t}') + g.auto_str_funcs.writeln('\t}') + g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _SLIT("]"));') + g.auto_str_funcs.writeln('\tstring res = strings__Builder_str(&sb);') + g.auto_str_funcs.writeln('\tstrings__Builder_free(&sb);') + g.auto_str_funcs.writeln('\treturn res;') + g.auto_str_funcs.writeln('}') +} + +fn (mut g Gen) gen_str_for_map(info ast.Map, styp string, str_fn_name string) { + mut key_typ := info.key_type + mut key_sym := g.table.get_type_symbol(key_typ) + if mut key_sym.info is ast.Alias { + key_typ = key_sym.info.parent_type + key_sym = g.table.get_type_symbol(key_typ) + } + key_styp := g.typ(key_typ) + key_str_fn_name := key_styp.replace('*', '') + '_str' + if !key_sym.has_method('str') { + g.gen_str_for_type(key_typ) + } + + mut val_typ := info.value_type + mut val_sym := g.table.get_type_symbol(val_typ) + if mut val_sym.info is ast.Alias { + val_typ = val_sym.info.parent_type + val_sym = g.table.get_type_symbol(val_typ) + } + val_styp := g.typ(val_typ) + elem_str_fn_name := val_styp.replace('*', '') + '_str' + if !val_sym.has_method('str') { + g.gen_str_for_type(val_typ) + } + + g.type_definitions.writeln('static string ${str_fn_name}($styp m); // auto') + g.auto_str_funcs.writeln('static string ${str_fn_name}($styp m) { return indent_${str_fn_name}(m, 0);}') + g.type_definitions.writeln('static string indent_${str_fn_name}($styp m, int indent_count); // auto') + g.auto_str_funcs.writeln('static string indent_${str_fn_name}($styp m, int indent_count) { /* gen_str_for_map */') + g.auto_str_funcs.writeln('\tstrings__Builder sb = strings__new_builder(m.key_values.len*10);') + g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _SLIT("{"));') + g.auto_str_funcs.writeln('\tfor (int i = 0; i < m.key_values.len; ++i) {') + g.auto_str_funcs.writeln('\t\tif (!DenseArray_has_index(&m.key_values, i)) { continue; }') + + if key_sym.kind == .string { + g.auto_str_funcs.writeln('\t\tstring key = *(string*)DenseArray_key(&m.key_values, i);') + } else { + g.auto_str_funcs.writeln('\t\t$key_styp key = *($key_styp*)DenseArray_key(&m.key_values, i);') + } + if key_sym.kind == .string { + g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${str_intp_sq('key')});') + } else if key_sym.kind == .rune { + tmp_str := str_intp_rune('${key_str_fn_name}(key)') + g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, $tmp_str);') + } else { + g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${key_str_fn_name}(key));') + } + g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, _SLIT(": "));') + if val_sym.kind == .function { + g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${elem_str_fn_name}());') + } else if val_sym.kind == .string { + tmp_str := str_intp_sq('*($val_styp*)DenseArray_value(&m.key_values, i)') + g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, $tmp_str);') + } else if should_use_indent_func(val_sym.kind) && !val_sym.has_method('str') { + g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, indent_${elem_str_fn_name}(*($val_styp*)DenseArray_value(&m.key_values, i), indent_count));') + } else if val_sym.kind in [.f32, .f64] { + tmp_val := '*($val_styp*)DenseArray_value(&m.key_values, i)' + if val_sym.kind == .f32 { + g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${str_intp_g32(tmp_val)});') + } else { + g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${str_intp_g64(tmp_val)});') + } + } else if val_sym.kind == .rune { + tmp_str := str_intp_rune('${elem_str_fn_name}(*($val_styp*)DenseArray_value(&m.key_values, i))') + g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, $tmp_str);') + } else { + g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${elem_str_fn_name}(*($val_styp*)DenseArray_value(&m.key_values, i)));') + } + g.auto_str_funcs.writeln('\t\tif (i != m.key_values.len-1) {') + g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write_string(&sb, _SLIT(", "));') + g.auto_str_funcs.writeln('\t\t}') + g.auto_str_funcs.writeln('\t}') + g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _SLIT("}"));') + g.auto_str_funcs.writeln('\tstring res = strings__Builder_str(&sb);') + g.auto_str_funcs.writeln('\tstrings__Builder_free(&sb);') + g.auto_str_funcs.writeln('\treturn res;') + g.auto_str_funcs.writeln('}') +} + +fn (g &Gen) type_to_fmt1(typ ast.Type) StrIntpType { + if typ == ast.byte_type_idx { + return .si_u8 + } + if typ == ast.char_type_idx { + return .si_c + } + if typ in ast.voidptr_types || typ in ast.byteptr_types { + return .si_p + } + if typ in ast.charptr_types { + // return '%C\\000' // a C string + return .si_s + } + sym := g.table.get_type_symbol(typ) + if typ.is_ptr() && (typ.is_int_valptr() || typ.is_float_valptr()) { + return .si_s + } else if sym.kind in [.struct_, .array, .array_fixed, .map, .bool, .enum_, .interface_, + .sum_type, .function, .alias, .chan] { + return .si_s + } else if sym.kind == .string { + return .si_s + // return "'%.*s\\000'" + } else if sym.kind in [.f32, .f64] { + if sym.kind == .f32 { + return .si_g32 + } + return .si_g64 + } else if sym.kind == .int { + return .si_i32 + } else if sym.kind == .u32 { + return .si_u32 + } else if sym.kind == .u64 { + return .si_u64 + } else if sym.kind == .i64 { + return .si_i64 + } + return .si_i32 +} + +fn (mut g Gen) gen_str_for_struct(info ast.Struct, styp string, str_fn_name string) { + // _str() functions should have a single argument, the indenting ones take 2: + g.type_definitions.writeln('static string ${str_fn_name}($styp it); // auto') + g.auto_str_funcs.writeln('static string ${str_fn_name}($styp it) { return indent_${str_fn_name}(it, 0);}') + g.type_definitions.writeln('static string indent_${str_fn_name}($styp it, int indent_count); // auto') + mut fn_builder := strings.new_builder(512) + defer { + g.auto_fn_definitions << fn_builder.str() + } + fn_builder.writeln('static string indent_${str_fn_name}($styp it, int indent_count) {') + mut clean_struct_v_type_name := styp.replace('__', '.') + if clean_struct_v_type_name.contains('_T_') { + // TODO: this is a bit hacky. styp shouldn't be even parsed with _T_ + // use something different than g.typ for styp + clean_struct_v_type_name = + clean_struct_v_type_name.replace('Array_', '[]').replace('_T_', '<').replace('_', ', ') + + '>' + } + clean_struct_v_type_name = util.strip_main_name(clean_struct_v_type_name) + // generate ident / indent length = 4 spaces + if info.fields.len == 0 { + fn_builder.writeln('\treturn _SLIT("$clean_struct_v_type_name{}");') + fn_builder.writeln('}') + return + } + + fn_builder.writeln('\tstring indents = string_repeat(_SLIT(" "), indent_count);') + fn_builder.writeln('\tstring res = str_intp( ${info.fields.len * 4 + 3}, _MOV((StrIntpData[]){') + fn_builder.writeln('\t\t{_SLIT("$clean_struct_v_type_name{\\n"), 0, {.d_c=0}},') + + for i, field in info.fields { + mut ptr_amp := if field.typ.is_ptr() { '&' } else { '' } + base_fmt := g.type_to_fmt1(g.unwrap_generic(field.typ)) + + // manage prefix and quote symbol for the filed + mut quote_str := '' + mut prefix := '' + sym := g.table.get_type_symbol(g.unwrap_generic(field.typ)) + if sym.kind == .string { + quote_str = "'" + } else if field.typ in ast.charptr_types { + quote_str = '\\"' + prefix = 'C' + } + + // first fields doesn't need \n + if i == 0 { + fn_builder.write_string('\t\t{_SLIT0, $c.si_s_code, {.d_s=indents}}, {_SLIT(" $field.name: $ptr_amp$prefix"), 0, {.d_c=0}}, ') + } else { + fn_builder.write_string('\t\t{_SLIT("\\n"), $c.si_s_code, {.d_s=indents}}, {_SLIT(" $field.name: $ptr_amp$prefix"), 0, {.d_c=0}}, ') + } + + // custom methods management + has_custom_str := sym.has_method('str') + mut field_styp := g.typ(field.typ).replace('*', '') + field_styp_fn_name := if has_custom_str { + '${field_styp}_str' + } else { + g.gen_str_for_type(field.typ) + } + + // manage the fact hat with float we use always the g representation + if sym.kind !in [.f32, .f64] { + fn_builder.write_string('{_SLIT("$quote_str"), ${int(base_fmt)}, {.${data_str(base_fmt)}=') + } else { + g_fmt := '0x' + (u32(base_fmt) | u32(0x7F) << 9).hex() + fn_builder.write_string('{_SLIT("$quote_str"), $g_fmt, {.${data_str(base_fmt)}=') + } + + mut func := struct_auto_str_func1(sym, field.typ, field_styp_fn_name, field.name) + if field.typ in ast.cptr_types { + func = '(voidptr) it.$field.name' + } else if field.typ.is_ptr() { + // reference types can be "nil" + fn_builder.write_string('isnil(it.${c_name(field.name)})') + fn_builder.write_string(' ? _SLIT("nil") : ') + // struct, floats and ints have a special case through the _str function + if sym.kind != .struct_ && !field.typ.is_int_valptr() && !field.typ.is_float_valptr() { + fn_builder.write_string('*') + } + } + // handle circular ref type of struct to the struct itself + if styp == field_styp { + fn_builder.write_string('_SLIT("")') + } else { + // manage C charptr + if field.typ in ast.charptr_types { + fn_builder.write_string('tos2((byteptr)$func)') + } else { + fn_builder.write_string(func) + } + } + + fn_builder.writeln('}}, {_SLIT("$quote_str"), 0, {.d_c=0}},') + } + fn_builder.writeln('\t\t{_SLIT("\\n"), $c.si_s_code, {.d_s=indents}}, {_SLIT("}"), 0, {.d_c=0}},') + fn_builder.writeln('\t}));') + fn_builder.writeln('\tstring_free(&indents);') + fn_builder.writeln('\treturn res;') + fn_builder.writeln('}') +} + +fn struct_auto_str_func1(sym &ast.TypeSymbol, field_type ast.Type, fn_name string, field_name string) string { + has_custom_str, expects_ptr, _ := sym.str_method_info() + if sym.kind == .enum_ { + return '${fn_name}(it.${c_name(field_name)})' + } else if should_use_indent_func(sym.kind) { + mut obj := 'it.${c_name(field_name)}' + if field_type.is_ptr() && !expects_ptr { + obj = '*$obj' + } + if has_custom_str { + return '${fn_name}($obj)' + } + return 'indent_${fn_name}($obj, indent_count + 1)' + } else if sym.kind in [.array, .array_fixed, .map, .sum_type] { + if has_custom_str { + return '${fn_name}(it.${c_name(field_name)})' + } + return 'indent_${fn_name}(it.${c_name(field_name)}, indent_count + 1)' + } else if sym.kind == .function { + return '${fn_name}()' + } else { + if sym.kind == .chan { + return '${fn_name}(it.${c_name(field_name)})' + } + mut method_str := 'it.${c_name(field_name)}' + if sym.kind == .bool { + method_str += ' ? _SLIT("true") : _SLIT("false")' + } else if (field_type.is_int_valptr() || field_type.is_float_valptr()) + && field_type.is_ptr() && !expects_ptr { + // ptr int can be "nil", so this needs to be casted to a string + if sym.kind == .f32 { + return 'str_intp(1, _MOV((StrIntpData[]){ + {_SLIT0, $si_g32_code, {.d_f32 = *$method_str }} + }))' + } else if sym.kind == .f64 { + return 'str_intp(1, _MOV((StrIntpData[]){ + {_SLIT0, $si_g64_code, {.d_f64 = *$method_str }} + }))' + } else if sym.kind == .u64 { + fmt_type := StrIntpType.si_u64 + return 'str_intp(1, _MOV((StrIntpData[]){{_SLIT0, ${u32(fmt_type) | 0xfe00}, {.d_u64 = *$method_str }}}))' + } + fmt_type := StrIntpType.si_i32 + return 'str_intp(1, _MOV((StrIntpData[]){{_SLIT0, ${u32(fmt_type) | 0xfe00}, {.d_i32 = *$method_str }}}))' + } + return method_str + } +} diff --git a/v_windows/v/old/vlib/v/gen/c/cgen.v b/v_windows/v/old/vlib/v/gen/c/cgen.v new file mode 100644 index 0000000..6f2de5a --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/c/cgen.v @@ -0,0 +1,6698 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module c + +import os +import strings +import v.ast +import v.pref +import v.token +import v.util +import v.util.version +import v.depgraph + +const ( + // NB: some of the words in c_reserved, are not reserved in C, + // but are in C++, or have special meaning in V, thus need escaping too. + c_reserved = ['auto', 'break', 'calloc', 'case', 'char', 'class', 'const', 'continue', + 'default', 'delete', 'do', 'double', 'else', 'enum', 'error', 'exit', 'export', 'extern', + 'float', 'for', 'free', 'goto', 'if', 'inline', 'int', 'link', 'long', 'malloc', 'namespace', + 'new', 'panic', 'register', 'restrict', 'return', 'short', 'signed', 'sizeof', 'static', + 'struct', 'switch', 'typedef', 'typename', 'union', 'unix', 'unsigned', 'void', 'volatile', + 'while', 'template', 'stdout', 'stdin', 'stderr'] + c_reserved_map = string_array_to_map(c_reserved) + // same order as in token.Kind + cmp_str = ['eq', 'ne', 'gt', 'lt', 'ge', 'le'] + // when operands are switched + cmp_rev = ['eq', 'ne', 'lt', 'gt', 'le', 'ge'] +) + +fn string_array_to_map(a []string) map[string]bool { + mut res := map[string]bool{} + for x in a { + res[x] = true + } + return res +} + +struct Gen { + pref &pref.Preferences + module_built string + field_data_type ast.Type // cache her to avoid map lookups +mut: + table &ast.Table + out strings.Builder + cheaders strings.Builder + includes strings.Builder // all C #includes required by V modules + typedefs strings.Builder + typedefs2 strings.Builder + type_definitions strings.Builder // typedefs, defines etc (everything that goes to the top of the file) + definitions strings.Builder // typedefs, defines etc (everything that goes to the top of the file) + global_inits map[string]strings.Builder // default initializers for globals (goes in _vinit()) + inits map[string]strings.Builder // contents of `void _vinit/2{}` + cleanups map[string]strings.Builder // contents of `void _vcleanup(){}` + gowrappers strings.Builder // all go callsite wrappers + stringliterals strings.Builder // all string literals (they depend on tos3() beeing defined + auto_str_funcs strings.Builder // function bodies of all auto generated _str funcs + comptime_defines strings.Builder // custom defines, given by -d/-define flags on the CLI + pcs_declarations strings.Builder // -prof profile counter declarations for each function + hotcode_definitions strings.Builder // -live declarations & functions + embedded_data strings.Builder // data to embed in the executable/binary + shared_types strings.Builder // shared/lock types + shared_functions strings.Builder // shared constructors + channel_definitions strings.Builder // channel related code + options_typedefs strings.Builder // Option typedefs + options strings.Builder // `Option_xxxx` types + json_forward_decls strings.Builder // json type forward decls + enum_typedefs strings.Builder // enum types + sql_buf strings.Builder // for writing exprs to args via `sqlite3_bind_int()` etc + file &ast.File + fn_decl &ast.FnDecl // pointer to the FnDecl we are currently inside otherwise 0 + last_fn_c_name string + tmp_count int // counter for unique tmp vars (_tmp1, _tmp2 etc); resets at the start of each fn. + tmp_count2 int // a separate tmp var counter for autofree fn calls + tmp_count_declarations int // counter for unique tmp names (_d1, _d2 etc); does NOT reset, used for C declarations + global_tmp_count int // like tmp_count but global and not resetted in each function + is_assign_lhs bool // inside left part of assign expr (for array_set(), etc) + discard_or_result bool // do not safe last ExprStmt of `or` block in tmp variable to defer ongoing expr usage + is_void_expr_stmt bool // ExprStmt whos result is discarded + is_arraymap_set bool // map or array set value state + is_amp bool // for `&Foo{}` to merge PrefixExpr `&` and StructInit `Foo{}`; also for `&byte(0)` etc + is_sql bool // Inside `sql db{}` statement, generating sql instead of C (e.g. `and` instead of `&&` etc) + is_shared bool // for initialization of hidden mutex in `[rw]shared` literals + is_vlines_enabled bool // is it safe to generate #line directives when -g is passed + inside_cast_in_heap int // inside cast to interface type in heap (resolve recursive calls) + arraymap_set_pos int // map or array set value position + vlines_path string // set to the proper path for generating #line directives + optionals []string // to avoid duplicates TODO perf, use map + chan_pop_optionals []string // types for `x := <-ch or {...}` + chan_push_optionals []string // types for `ch <- x or {...}` + cur_lock ast.LockExpr + mtxs string // array of mutexes if the `lock` has multiple variables + labeled_loops map[string]&ast.Stmt + inner_loop &ast.Stmt + shareds []int // types with hidden mutex for which decl has been emitted + inside_ternary int // ?: comma separated statements on a single line + inside_map_postfix bool // inside map++/-- postfix expr + inside_map_infix bool // inside map< fn counter name + is_builtin_mod bool + hotcode_fn_names []string + embedded_files []ast.EmbeddedFile + sql_i int + sql_stmt_name string + sql_bind_name string + sql_idents []string + sql_idents_types []ast.Type + sql_left_type ast.Type + sql_table_name string + sql_fkey string + sql_parent_id string + sql_side SqlExprSide // left or right, to distinguish idents in `name == name` + inside_vweb_tmpl bool + inside_return bool + inside_or_block bool + strs_to_free0 []string // strings.Builder + // strs_to_free []string // strings.Builder + inside_call bool + has_main bool + inside_const bool + comp_for_method string // $for method in T.methods {} + comp_for_field_var string // $for field in T.fields {}; the variable name + comp_for_field_value ast.StructField // value of the field variable + comp_for_field_type ast.Type // type of the field variable inferred from `$if field.typ is T {}` + comptime_var_type_map map[string]ast.Type + // tmp_arg_vars_to_free []string + // autofree_pregen map[string]string + // autofree_pregen_buf strings.Builder + // autofree_tmp_vars []string // to avoid redefining the same tmp vars in a single function + called_fn_name string + cur_mod ast.Module + is_js_call bool // for handling a special type arg #1 `json.decode(User, ...)` + is_fn_index_call bool + // nr_vars_to_free int + // doing_autofree_tmp bool + inside_lambda bool + prevent_sum_type_unwrapping_once bool // needed for assign new values to sum type + // used in match multi branch + // TypeOne, TypeTwo {} + // where an aggregate (at least two types) is generated + // sum type deref needs to know which index to deref because unions take care of the correct field + aggregate_type_idx int + returned_var_name string // to detect that a var doesn't need to be freed since it's being returned + branch_parent_pos int // used in BranchStmt (continue/break) for autofree stop position + timers &util.Timers = util.new_timers(false) + force_main_console bool // true when [console] used on fn main() + as_cast_type_names map[string]string // table for type name lookup in runtime (for __as_cast) + obf_table map[string]string + // main_fn_decl_node ast.FnDecl + expected_cast_type ast.Type // for match expr of sumtypes + defer_vars []string + anon_fn bool +} + +pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string { + // println('start cgen2') + mut module_built := '' + if pref.build_mode == .build_module { + for file in files { + if file.path.contains(pref.path) + && file.mod.short_name == pref.path.all_after_last(os.path_separator).trim_right(os.path_separator) { + module_built = file.mod.name + break + } + } + } + mut timers_should_print := false + $if time_cgening ? { + timers_should_print = true + } + mut g := Gen{ + file: 0 + out: strings.new_builder(512000) + cheaders: strings.new_builder(15000) + includes: strings.new_builder(100) + typedefs: strings.new_builder(100) + typedefs2: strings.new_builder(100) + type_definitions: strings.new_builder(100) + definitions: strings.new_builder(100) + gowrappers: strings.new_builder(100) + stringliterals: strings.new_builder(100) + auto_str_funcs: strings.new_builder(100) + comptime_defines: strings.new_builder(100) + pcs_declarations: strings.new_builder(100) + hotcode_definitions: strings.new_builder(100) + embedded_data: strings.new_builder(1000) + options_typedefs: strings.new_builder(100) + options: strings.new_builder(100) + shared_types: strings.new_builder(100) + shared_functions: strings.new_builder(100) + channel_definitions: strings.new_builder(100) + json_forward_decls: strings.new_builder(100) + enum_typedefs: strings.new_builder(100) + sql_buf: strings.new_builder(100) + table: table + pref: pref + fn_decl: 0 + is_autofree: true + indent: -1 + module_built: module_built + timers: util.new_timers(timers_should_print) + inner_loop: &ast.EmptyStmt{} + field_data_type: ast.Type(table.find_type_idx('FieldData')) + } + g.timers.start('cgen init') + for mod in g.table.modules { + g.inits[mod] = strings.new_builder(100) + g.global_inits[mod] = strings.new_builder(100) + g.cleanups[mod] = strings.new_builder(100) + } + g.init() + g.timers.show('cgen init') + mut tests_inited := false + mut autofree_used := false + for file in files { + g.timers.start('cgen_file $file.path') + g.file = file + if g.pref.is_vlines { + g.vlines_path = util.vlines_escape_path(file.path, g.pref.ccompiler) + } + // println('\ncgen "$g.file.path" nr_stmts=$file.stmts.len') + // building_v := true && (g.file.path.contains('/vlib/') || g.file.path.contains('cmd/v')) + g.is_test = g.pref.is_test + if g.file.path == '' || !g.pref.autofree { + // cgen test or building V + // println('autofree=false') + g.is_autofree = false + } else { + g.is_autofree = true + autofree_used = true + } + // anon fn may include assert and thus this needs + // to be included before any test contents are written + if g.is_test && !tests_inited { + g.write_tests_definitions() + tests_inited = true + } + g.stmts(file.stmts) + // Transfer embedded files + if file.embedded_files.len > 0 { + for path in file.embedded_files { + if path !in g.embedded_files { + g.embedded_files << path + } + } + } + g.timers.show('cgen_file $file.path') + } + g.timers.start('cgen common') + if autofree_used { + g.is_autofree = true // so that void _vcleanup is generated + } + // to make sure type idx's are the same in cached mods + if g.pref.build_mode == .build_module { + for idx, typ in g.table.type_symbols { + if idx == 0 { + continue + } + g.definitions.writeln('int _v_type_idx_${typ.cname}();') + } + } else if g.pref.use_cache { + for idx, typ in g.table.type_symbols { + if idx == 0 { + continue + } + g.definitions.writeln('int _v_type_idx_${typ.cname}() { return $idx; };') + } + } + // + g.dump_expr_definitions() + // v files are finished, what remains is pure C code + g.gen_vlines_reset() + if g.pref.build_mode != .build_module { + // no init in builtin.o + g.write_init_function() + } + + g.finish() + + mut b := strings.new_builder(640000) + b.write_string(g.hashes()) + b.writeln('\n// V comptime_defines:') + b.write_string(g.comptime_defines.str()) + b.writeln('\n// V typedefs:') + b.write_string(g.typedefs.str()) + b.writeln('\n// V typedefs2:') + b.write_string(g.typedefs2.str()) + b.writeln('\n// V cheaders:') + b.write_string(g.cheaders.str()) + if g.pcs_declarations.len > 0 { + b.writeln('\n// V profile counters:') + b.write_string(g.pcs_declarations.str()) + } + b.writeln('\n// V includes:') + b.write_string(g.includes.str()) + b.writeln('\n// Enum definitions:') + b.write_string(g.enum_typedefs.str()) + b.writeln('\n// V type definitions:') + b.write_string(g.type_definitions.str()) + b.writeln('\n// V shared types:') + b.write_string(g.shared_types.str()) + b.writeln('\n// V Option_xxx definitions:') + b.write_string(g.options.str()) + b.writeln('\n// V json forward decls:') + b.write_string(g.json_forward_decls.str()) + b.writeln('\n// V definitions:') + b.write_string(g.definitions.str()) + interface_table := g.interface_table() + if interface_table.len > 0 { + b.writeln('\n// V interface table:') + b.write_string(interface_table) + } + if g.gowrappers.len > 0 { + b.writeln('\n// V gowrappers:') + b.write_string(g.gowrappers.str()) + } + if g.hotcode_definitions.len > 0 { + b.writeln('\n// V hotcode definitions:') + b.write_string(g.hotcode_definitions.str()) + } + if g.embedded_data.len > 0 { + b.writeln('\n// V embedded data:') + b.write_string(g.embedded_data.str()) + } + if g.options_typedefs.len > 0 { + b.writeln('\n// V option typedefs:') + b.write_string(g.options_typedefs.str()) + } + if g.shared_functions.len > 0 { + b.writeln('\n// V shared type functions:') + b.write_string(g.shared_functions.str()) + b.write_string(c_concurrency_helpers) + } + if g.channel_definitions.len > 0 { + b.writeln('\n// V channel code:') + b.write_string(g.channel_definitions.str()) + } + if g.stringliterals.len > 0 { + b.writeln('\n// V stringliterals:') + b.write_string(g.stringliterals.str()) + } + if g.auto_str_funcs.len > 0 { + // if g.pref.build_mode != .build_module { + b.writeln('\n// V auto str functions:') + b.write_string(g.auto_str_funcs.str()) + // } + } + if g.auto_fn_definitions.len > 0 { + for fn_def in g.auto_fn_definitions { + b.writeln(fn_def) + } + } + if g.anon_fn_definitions.len > 0 { + for fn_def in g.anon_fn_definitions { + b.writeln(fn_def) + } + } + b.writeln('\n// V out') + b.write_string(g.out.str()) + b.writeln('\n// THE END.') + g.timers.show('cgen common') + return b.str() +} + +pub fn (g &Gen) hashes() string { + mut res := c_commit_hash_default.replace('@@@', version.vhash()) + res += c_current_commit_hash_default.replace('@@@', version.githash(g.pref.building_v)) + return res +} + +pub fn (mut g Gen) init() { + if g.pref.custom_prelude != '' { + g.cheaders.writeln(g.pref.custom_prelude) + } else if !g.pref.no_preludes { + g.cheaders.writeln('// Generated by the V compiler') + tcc_undef_has_include := ' +#if defined(__TINYC__) && defined(__has_include) +// tcc does not support has_include properly yet, turn it off completely +#undef __has_include +#endif' + g.cheaders.writeln(tcc_undef_has_include) + g.includes.writeln(tcc_undef_has_include) + g.cheaders.writeln(get_guarded_include_text('', 'The C compiler can not find . Please install build-essentials')) // int64_t etc + g.cheaders.writeln(c_builtin_types) + if g.pref.is_bare { + g.cheaders.writeln(c_bare_headers) + } else { + g.cheaders.writeln(c_headers) + } + if !g.pref.skip_unused || g.table.used_maps > 0 { + g.cheaders.writeln(c_wyhash_headers) + } + } + if g.pref.os == .ios { + g.cheaders.writeln('#define __TARGET_IOS__ 1') + g.cheaders.writeln('#include ') + } + g.write_builtin_types() + g.write_typedef_types() + g.write_typeof_functions() + g.write_sorted_types() + g.write_multi_return_types() + g.definitions.writeln('// end of definitions #endif') + // + g.stringliterals.writeln('') + g.stringliterals.writeln('// >> string literal consts') + if g.pref.build_mode != .build_module { + g.stringliterals.writeln('void vinit_string_literals(){') + } + if g.pref.compile_defines_all.len > 0 { + g.comptime_defines.writeln('// V compile time defines by -d or -define flags:') + g.comptime_defines.writeln('// All custom defines : ' + + g.pref.compile_defines_all.join(',')) + g.comptime_defines.writeln('// Turned ON custom defines: ' + + g.pref.compile_defines.join(',')) + for cdefine in g.pref.compile_defines { + g.comptime_defines.writeln('#define CUSTOM_DEFINE_$cdefine') + } + g.comptime_defines.writeln('') + } + if g.table.gostmts > 0 { + g.comptime_defines.writeln('#define __VTHREADS__ (1)') + } + if g.pref.gc_mode in [.boehm_full, .boehm_incr, .boehm_full_opt, .boehm_incr_opt, .boehm_leak] { + g.comptime_defines.writeln('#define _VGCBOEHM (1)') + } + if g.pref.is_debug || 'debug' in g.pref.compile_defines { + g.comptime_defines.writeln('#define _VDEBUG (1)') + } + if g.pref.is_prod || 'prod' in g.pref.compile_defines { + g.comptime_defines.writeln('#define _VPROD (1)') + } + if g.pref.is_test || 'test' in g.pref.compile_defines { + g.comptime_defines.writeln('#define _VTEST (1)') + } + if g.pref.autofree { + g.comptime_defines.writeln('#define _VAUTOFREE (1)') + // g.comptime_defines.writeln('unsigned char* g_cur_str;') + } + if g.pref.prealloc { + g.comptime_defines.writeln('#define _VPREALLOC (1)') + } + if g.pref.is_livemain || g.pref.is_liveshared { + g.generate_hotcode_reloading_declarations() + } + // Obfuscate only functions in the main module for now. + // Generate the obf_ast. + if g.pref.obfuscate { + mut i := 0 + // fns + for key, f in g.table.fns { + if f.mod != 'main' && key != 'main' { // !key.starts_with('main.') { + continue + } + g.obf_table[key] = '_f$i' + i++ + } + // methods + for type_sym in g.table.type_symbols { + if type_sym.mod != 'main' { + continue + } + for method in type_sym.methods { + g.obf_table[type_sym.name + '.' + method.name] = '_f$i' + i++ + } + } + } +} + +pub fn (mut g Gen) finish() { + if g.pref.build_mode != .build_module { + g.stringliterals.writeln('}') + } + g.stringliterals.writeln('// << string literal consts') + g.stringliterals.writeln('') + if g.pref.is_prof && g.pref.build_mode != .build_module { + g.gen_vprint_profile_stats() + } + if g.pref.is_livemain || g.pref.is_liveshared { + g.generate_hotcode_reloader_code() + } + if g.embed_file_is_prod_mode() && g.embedded_files.len > 0 { + g.gen_embedded_data() + } + if g.pref.is_test { + g.gen_c_main_for_tests() + } else { + g.gen_c_main() + } +} + +pub fn (mut g Gen) write_typeof_functions() { + g.writeln('') + g.writeln('// >> typeof() support for sum types / interfaces') + for typ in g.table.type_symbols { + if typ.kind == .sum_type { + sum_info := typ.info as ast.SumType + if sum_info.is_generic { + continue + } + g.writeln('static char * v_typeof_sumtype_${typ.cname}(int sidx) { /* $typ.name */ ') + if g.pref.build_mode == .build_module { + g.writeln('\t\tif( sidx == _v_type_idx_${typ.cname}() ) return "${util.strip_main_name(typ.name)}";') + for v in sum_info.variants { + subtype := g.table.get_type_symbol(v) + g.writeln('\tif( sidx == _v_type_idx_${subtype.cname}() ) return "${util.strip_main_name(subtype.name)}";') + } + g.writeln('\treturn "unknown ${util.strip_main_name(typ.name)}";') + } else { + tidx := g.table.find_type_idx(typ.name) + g.writeln('\tswitch(sidx) {') + g.writeln('\t\tcase $tidx: return "${util.strip_main_name(typ.name)}";') + for v in sum_info.variants { + subtype := g.table.get_type_symbol(v) + g.writeln('\t\tcase $v: return "${util.strip_main_name(subtype.name)}";') + } + g.writeln('\t\tdefault: return "unknown ${util.strip_main_name(typ.name)}";') + g.writeln('\t}') + } + g.writeln('}') + } else if typ.kind == .interface_ { + inter_info := typ.info as ast.Interface + if inter_info.is_generic { + continue + } + g.writeln('static char * v_typeof_interface_${typ.cname}(int sidx) { /* $typ.name */ ') + for t in inter_info.types { + subtype := g.table.get_type_symbol(t) + g.writeln('\tif (sidx == _${typ.cname}_${subtype.cname}_index) return "${util.strip_main_name(subtype.name)}";') + } + g.writeln('\treturn "unknown ${util.strip_main_name(typ.name)}";') + g.writeln('}') + } + } + g.writeln('// << typeof() support for sum types') + g.writeln('') +} + +// V type to C typecc +fn (mut g Gen) typ(t ast.Type) string { + styp := g.base_type(t) + if t.has_flag(.optional) { + // Register an optional if it's not registered yet + return g.register_optional(t) + } + /* + if styp.starts_with('C__') { + return styp[3..] + } + */ + return styp +} + +fn (mut g Gen) base_type(t ast.Type) string { + share := t.share() + mut styp := if share == .atomic_t { t.atomic_typename() } else { g.cc_type(t, true) } + if t.has_flag(.shared_f) { + styp = g.find_or_register_shared(t, styp) + } + nr_muls := g.unwrap_generic(t).nr_muls() + if nr_muls > 0 { + styp += strings.repeat(`*`, nr_muls) + } + return styp +} + +fn (mut g Gen) generic_fn_name(types []ast.Type, before string, is_decl bool) string { + if types.len == 0 { + return before + } + // Using _T_ to differentiate between get and get_string + // `foo()` => `foo_T_int()` + mut name := before + '_T' + for typ in types { + name += '_' + strings.repeat_string('__ptr__', typ.nr_muls()) + g.typ(typ.set_nr_muls(0)) + } + return name +} + +fn (mut g Gen) expr_string(expr ast.Expr) string { + pos := g.out.len + g.expr(expr) + expr_str := g.out.after(pos) + g.out.go_back(expr_str.len) + return expr_str.trim_space() +} + +// Surround a potentially multi-statement expression safely with `prepend` and `append`. +// (and create a statement) +fn (mut g Gen) expr_string_surround(prepend string, expr ast.Expr, append string) string { + pos := g.out.len + g.stmt_path_pos << pos + g.write(prepend) + g.expr(expr) + g.write(append) + expr_str := g.out.after(pos) + g.out.go_back(expr_str.len) + g.stmt_path_pos.delete_last() + return expr_str +} + +// TODO this really shouldnt be seperate from typ +// but I(emily) would rather have this generation +// all unified in one place so that it doesnt break +// if one location changes +fn (mut g Gen) optional_type_name(t ast.Type) (string, string) { + base := g.base_type(t) + mut styp := 'Option_$base' + if t.is_ptr() { + styp = styp.replace('*', '_ptr') + } + return styp, base +} + +fn (g &Gen) optional_type_text(styp string, base string) string { + // replace void with something else + size := if base == 'void' { 'byte' } else { base } + ret := 'struct $styp { + byte state; + IError err; + byte data[sizeof($size)]; +}' + return ret +} + +fn (mut g Gen) register_optional(t ast.Type) string { + styp, base := g.optional_type_name(t) + if styp !in g.optionals { + g.typedefs2.writeln('typedef struct $styp $styp;') + g.options.write_string(g.optional_type_text(styp, base)) + g.options.writeln(';\n') + g.optionals << styp.clone() + } + return styp +} + +fn (mut g Gen) find_or_register_shared(t ast.Type, base string) string { + sh_typ := '__shared__$base' + t_idx := t.idx() + if t_idx in g.shareds { + return sh_typ + } + mtx_typ := 'sync__RwMutex' + g.shared_types.writeln('struct $sh_typ { $base val; $mtx_typ mtx; };') + g.shared_functions.writeln('static inline voidptr __dup${sh_typ}(voidptr src, int sz) {') + g.shared_functions.writeln('\t$sh_typ* dest = memdup(src, sz);') + g.shared_functions.writeln('\tsync__RwMutex_init(&dest->mtx);') + g.shared_functions.writeln('\treturn dest;') + g.shared_functions.writeln('}') + g.typedefs2.writeln('typedef struct $sh_typ $sh_typ;') + // println('registered shared type $sh_typ') + g.shareds << t_idx + return sh_typ +} + +fn (mut g Gen) register_thread_array_wait_call(eltyp string) string { + is_void := eltyp == 'void' + thread_typ := if is_void { '__v_thread' } else { '__v_thread_$eltyp' } + ret_typ := if is_void { 'void' } else { 'Array_$eltyp' } + thread_arr_typ := 'Array_$thread_typ' + fn_name := '${thread_arr_typ}_wait' + if fn_name !in g.waiter_fns { + g.waiter_fns << fn_name + if is_void { + g.gowrappers.writeln(' +void ${fn_name}($thread_arr_typ a) { + for (int i = 0; i < a.len; ++i) { + $thread_typ t = (($thread_typ*)a.data)[i]; + __v_thread_wait(t); + } +}') + } else { + g.gowrappers.writeln(' +$ret_typ ${fn_name}($thread_arr_typ a) { + $ret_typ res = __new_array_with_default(a.len, a.len, sizeof($eltyp), 0); + for (int i = 0; i < a.len; ++i) { + $thread_typ t = (($thread_typ*)a.data)[i]; + (($eltyp*)res.data)[i] = __v_thread_${eltyp}_wait(t); + } + return res; +}') + } + } + return fn_name +} + +fn (mut g Gen) register_chan_pop_optional_call(opt_el_type string, styp string) { + if opt_el_type !in g.chan_pop_optionals { + g.chan_pop_optionals << opt_el_type + g.channel_definitions.writeln(' +static inline $opt_el_type __Option_${styp}_popval($styp ch) { + $opt_el_type _tmp = {0}; + if (sync__Channel_try_pop_priv(ch, _tmp.data, false)) { + return ($opt_el_type){ .state = 2, .err = _v_error(_SLIT("channel closed")), .data = {EMPTY_STRUCT_INITIALIZATION} }; + } + return _tmp; +}') + } +} + +fn (mut g Gen) register_chan_push_optional_call(el_type string, styp string) { + if styp !in g.chan_push_optionals { + g.chan_push_optionals << styp + g.register_optional(ast.void_type.set_flag(.optional)) + g.channel_definitions.writeln(' +static inline Option_void __Option_${styp}_pushval($styp ch, $el_type e) { + if (sync__Channel_try_push_priv(ch, &e, false)) { + return (Option_void){ .state = 2, .err = _v_error(_SLIT("channel closed")), .data = {EMPTY_STRUCT_INITIALIZATION} }; + } + return (Option_void){0}; +}') + } +} + +// cc_type whether to prefix 'struct' or not (C__Foo -> struct Foo) +fn (mut g Gen) cc_type(typ ast.Type, is_prefix_struct bool) string { + sym := g.table.get_type_symbol(g.unwrap_generic(typ)) + mut styp := sym.cname + // TODO: this needs to be removed; cgen shouldn't resolve generic types (job of checker) + match mut sym.info { + ast.Struct, ast.Interface, ast.SumType { + if sym.info.is_generic { + mut sgtyps := '_T' + for gt in sym.info.generic_types { + gts := g.table.get_type_symbol(g.unwrap_generic(gt)) + sgtyps += '_$gts.cname' + } + styp += sgtyps + } + } + ast.MultiReturn { + // TODO: this doesn't belong here, but makes it working for now + mut cname := 'multi_return' + for mr_typ in sym.info.types { + mr_type_sym := g.table.get_type_symbol(g.unwrap_generic(mr_typ)) + cname += '_$mr_type_sym.cname' + } + return cname + } + else {} + } + if is_prefix_struct && styp.starts_with('C__') { + styp = styp[3..] + if sym.kind == .struct_ { + info := sym.info as ast.Struct + if !info.is_typedef { + styp = 'struct $styp' + } + } + } + return styp +} + +[inline] +fn (g &Gen) type_sidx(t ast.Type) string { + if g.pref.build_mode == .build_module { + sym := g.table.get_type_symbol(t) + return '_v_type_idx_${sym.cname}()' + } + return '$t.idx()' +} + +// +pub fn (mut g Gen) write_typedef_types() { + for typ in g.table.type_symbols { + if typ.name in c.builtins { + continue + } + match typ.kind { + .array { + info := typ.info as ast.Array + elem_sym := g.table.get_type_symbol(info.elem_type) + if elem_sym.kind != .placeholder { + g.type_definitions.writeln('typedef array $typ.cname;') + } + } + .array_fixed { + info := typ.info as ast.ArrayFixed + elem_sym := g.table.get_type_symbol(info.elem_type) + if elem_sym.is_builtin() { + // .array_fixed { + styp := typ.cname + // array_fixed_char_300 => char x[300] + mut fixed := styp[12..] + len := styp.after('_') + fixed = fixed[..fixed.len - len.len - 1] + if fixed.starts_with('C__') { + fixed = fixed[3..] + } + if elem_sym.info is ast.FnType { + pos := g.out.len + g.write_fn_ptr_decl(&elem_sym.info, '') + fixed = g.out.after(pos) + g.out.go_back(fixed.len) + mut def_str := 'typedef $fixed;' + def_str = def_str.replace_once('(*)', '(*$styp[$len])') + g.type_definitions.writeln(def_str) + } else { + g.type_definitions.writeln('typedef $fixed $styp [$len];') + } + } + } + .chan { + if typ.name != 'chan' { + g.type_definitions.writeln('typedef chan $typ.cname;') + chan_inf := typ.chan_info() + chan_elem_type := chan_inf.elem_type + if !chan_elem_type.has_flag(.generic) { + el_stype := g.typ(chan_elem_type) + g.channel_definitions.writeln(' +static inline $el_stype __${typ.cname}_popval($typ.cname ch) { + $el_stype val; + sync__Channel_try_pop_priv(ch, &val, false); + return val; +}') + g.channel_definitions.writeln(' +static inline void __${typ.cname}_pushval($typ.cname ch, $el_stype val) { + sync__Channel_try_push_priv(ch, &val, false); +}') + } + } + } + .map { + g.type_definitions.writeln('typedef map $typ.cname;') + } + else { + continue + } + } + } + for typ in g.table.type_symbols { + if typ.kind == .alias && typ.name !in c.builtins { + g.write_alias_typesymbol_declaration(typ) + } + } + for typ in g.table.type_symbols { + if typ.kind == .function && typ.name !in c.builtins { + g.write_fn_typesymbol_declaration(typ) + } + } + // Generating interfaces after all the common types have been defined + // to prevent generating interface struct before definition of field types + for typ in g.table.type_symbols { + if typ.kind == .interface_ && typ.name !in c.builtins { + g.write_interface_typedef(typ) + } + } + for typ in g.table.type_symbols { + if typ.kind == .interface_ && typ.name !in c.builtins { + g.write_interface_typesymbol_declaration(typ) + } + } +} + +pub fn (mut g Gen) write_alias_typesymbol_declaration(sym ast.TypeSymbol) { + parent := unsafe { &g.table.type_symbols[sym.parent_idx] } + is_c_parent := parent.name.len > 2 && parent.name[0] == `C` && parent.name[1] == `.` + mut is_typedef := false + if parent.info is ast.Struct { + is_typedef = parent.info.is_typedef + } + mut parent_styp := parent.cname + if is_c_parent { + if !is_typedef { + parent_styp = 'struct ' + parent.cname[3..] + } else { + parent_styp = parent.cname[3..] + } + } else { + if sym.info is ast.Alias { + parent_styp = g.typ(sym.info.parent_type) + } + } + if parent_styp == 'byte' && sym.cname == 'u8' { + // TODO: remove this check; it is here just to fix V rebuilding in -cstrict mode with clang-12 + return + } + g.type_definitions.writeln('typedef $parent_styp $sym.cname;') +} + +pub fn (mut g Gen) write_interface_typedef(sym ast.TypeSymbol) { + struct_name := c_name(sym.cname) + g.type_definitions.writeln('typedef struct $struct_name $struct_name;') +} + +pub fn (mut g Gen) write_interface_typesymbol_declaration(sym ast.TypeSymbol) { + info := sym.info as ast.Interface + if info.is_generic { + return + } + struct_name := c_name(sym.cname) + g.type_definitions.writeln('struct $struct_name {') + g.type_definitions.writeln('\tunion {') + g.type_definitions.writeln('\t\tvoid* _object;') + for variant in info.types { + vcname := g.table.get_type_symbol(variant).cname + g.type_definitions.writeln('\t\t$vcname* _$vcname;') + } + g.type_definitions.writeln('\t};') + g.type_definitions.writeln('\tint _typ;') + for field in info.fields { + styp := g.typ(field.typ) + cname := c_name(field.name) + g.type_definitions.writeln('\t$styp* $cname;') + } + g.type_definitions.writeln('};') +} + +pub fn (mut g Gen) write_fn_typesymbol_declaration(sym ast.TypeSymbol) { + info := sym.info as ast.FnType + func := info.func + is_fn_sig := func.name == '' + not_anon := !info.is_anon + mut has_generic_arg := false + for param in func.params { + if param.typ.has_flag(.generic) { + has_generic_arg = true + break + } + } + if !info.has_decl && (not_anon || is_fn_sig) && !func.return_type.has_flag(.generic) + && !has_generic_arg { + fn_name := sym.cname + g.type_definitions.write_string('typedef ${g.typ(func.return_type)} (*$fn_name)(') + for i, param in func.params { + g.type_definitions.write_string(g.typ(param.typ)) + if i < func.params.len - 1 { + g.type_definitions.write_string(',') + } + } + g.type_definitions.writeln(');') + } +} + +pub fn (mut g Gen) write_multi_return_types() { + g.typedefs.writeln('\n// BEGIN_multi_return_typedefs') + g.type_definitions.writeln('\n// BEGIN_multi_return_structs') + for sym in g.table.type_symbols { + if sym.kind != .multi_return { + continue + } + info := sym.mr_info() + if info.types.filter(it.has_flag(.generic)).len > 0 { + continue + } + g.typedefs.writeln('typedef struct $sym.cname $sym.cname;') + g.type_definitions.writeln('struct $sym.cname {') + for i, mr_typ in info.types { + type_name := g.typ(mr_typ) + g.type_definitions.writeln('\t$type_name arg$i;') + } + g.type_definitions.writeln('};\n') + } + g.typedefs.writeln('// END_multi_return_typedefs\n') + g.type_definitions.writeln('// END_multi_return_structs\n') +} + +pub fn (mut g Gen) write(s string) { + $if trace_gen ? { + eprintln('gen file: ${g.file.path:-30} | last_fn_c_name: ${g.last_fn_c_name:-45} | write: $s') + } + if g.indent > 0 && g.empty_line { + g.out.write_string(util.tabs(g.indent)) + } + g.out.write_string(s) + g.empty_line = false +} + +pub fn (mut g Gen) writeln(s string) { + $if trace_gen ? { + eprintln('gen file: ${g.file.path:-30} | last_fn_c_name: ${g.last_fn_c_name:-45} | writeln: $s') + } + if g.indent > 0 && g.empty_line { + g.out.write_string(util.tabs(g.indent)) + } + g.out.writeln(s) + g.empty_line = true +} + +pub fn (mut g Gen) new_tmp_var() string { + g.tmp_count++ + return '_t$g.tmp_count' +} + +pub fn (mut g Gen) new_global_tmp_var() string { + g.global_tmp_count++ + return '_t$g.global_tmp_count' +} + +pub fn (mut g Gen) new_tmp_declaration_name() string { + g.tmp_count_declarations++ + return '_d$g.tmp_count_declarations' +} + +pub fn (mut g Gen) current_tmp_var() string { + return '_t$g.tmp_count' +} + +/* +pub fn (mut g Gen) new_tmp_var2() string { + g.tmp_count2++ + return '_tt$g.tmp_count2' +} +*/ +pub fn (mut g Gen) reset_tmp_count() { + g.tmp_count = 0 +} + +fn (mut g Gen) decrement_inside_ternary() { + key := g.inside_ternary.str() + for name in g.ternary_level_names[key] { + g.ternary_names.delete(name) + } + g.ternary_level_names.delete(key) + g.inside_ternary-- +} + +fn (mut g Gen) stmts(stmts []ast.Stmt) { + g.stmts_with_tmp_var(stmts, '') +} + +// tmp_var is used in `if` expressions only +fn (mut g Gen) stmts_with_tmp_var(stmts []ast.Stmt, tmp_var string) { + g.indent++ + if g.inside_ternary > 0 { + g.write('(') + } + for i, stmt in stmts { + if i == stmts.len - 1 && tmp_var != '' { + // Handle if expressions, set the value of the last expression to the temp var. + if g.inside_if_optional { + g.stmt_path_pos << g.out.len + g.skip_stmt_pos = true + if stmt is ast.ExprStmt { + if stmt.typ == ast.error_type_idx || stmt.expr is ast.None { + g.writeln('${tmp_var}.state = 2;') + g.write('${tmp_var}.err = ') + g.expr(stmt.expr) + g.writeln(';') + } else { + mut styp := g.base_type(stmt.typ) + $if tinyc && x32 && windows { + if stmt.typ == ast.int_literal_type { + styp = 'int' + } else if stmt.typ == ast.float_literal_type { + styp = 'f64' + } + } + g.write('opt_ok(&($styp[]) { ') + g.stmt(stmt) + g.writeln(' }, (Option*)(&$tmp_var), sizeof($styp));') + } + } + } else { + g.stmt_path_pos << g.out.len + g.skip_stmt_pos = true + g.write('$tmp_var = ') + g.stmt(stmt) + if !g.out.last_n(2).contains(';') { + g.writeln(';') + } + } + } else { + g.stmt(stmt) + if g.inside_if_optional && stmt is ast.ExprStmt { + g.writeln(';') + } + } + g.skip_stmt_pos = false + if g.inside_ternary > 0 && i < stmts.len - 1 { + g.write(',') + } + } + g.indent-- + if g.inside_ternary > 0 { + g.write('') + g.write(')') + } + if g.is_autofree && !g.inside_vweb_tmpl && stmts.len > 0 { + // use the first stmt to get the scope + stmt := stmts[0] + // stmt := stmts[stmts.len-1] + if stmt !is ast.FnDecl && g.inside_ternary == 0 { + // g.trace_autofree('// autofree scope') + // g.trace_autofree('// autofree_scope_vars($stmt.pos.pos) | ${typeof(stmt)}') + // go back 1 position is important so we dont get the + // internal scope of for loops and possibly other nodes + // g.autofree_scope_vars(stmt.pos.pos - 1) + mut stmt_pos := stmt.pos + if stmt_pos.pos == 0 { + // Do not autofree if the position is 0, since the correct scope won't be found. + // Report a bug, since position shouldn't be 0 for most nodes. + if stmt is ast.Module { + return + } + if stmt is ast.ExprStmt { + // For some reason ExprStmt.pos is 0 when ExprStmt.expr is comp if expr + // Extract the pos. TODO figure out why and fix. + stmt_pos = stmt.expr.position() + } + if stmt_pos.pos == 0 { + $if trace_autofree ? { + println('autofree: first stmt pos = 0. $stmt.type_name()') + } + return + } + } + g.autofree_scope_vars(stmt_pos.pos - 1, stmt_pos.line_nr, false) + } + } +} + +[inline] +fn (mut g Gen) write_v_source_line_info(pos token.Position) { + if g.inside_ternary == 0 && g.pref.is_vlines && g.is_vlines_enabled { + nline := pos.line_nr + 1 + lineinfo := '\n#line $nline "$g.vlines_path"' + g.writeln(lineinfo) + } +} + +fn (mut g Gen) stmt(node ast.Stmt) { + if !g.skip_stmt_pos { + g.stmt_path_pos << g.out.len + } + defer { + } + // println('g.stmt()') + // g.writeln('//// stmt start') + match node { + ast.EmptyStmt {} + ast.AsmStmt { + g.write_v_source_line_info(node.pos) + g.gen_asm_stmt(node) + } + ast.AssertStmt { + g.write_v_source_line_info(node.pos) + g.gen_assert_stmt(node) + } + ast.AssignStmt { + g.write_v_source_line_info(node.pos) + g.gen_assign_stmt(node) + } + ast.Block { + g.write_v_source_line_info(node.pos) + if node.is_unsafe { + g.writeln('{ // Unsafe block') + } else { + g.writeln('{') + } + g.stmts(node.stmts) + g.writeln('}') + } + ast.BranchStmt { + g.write_v_source_line_info(node.pos) + + if node.label != '' { + x := g.labeled_loops[node.label] or { + panic('$node.label doesn\'t exist $g.file.path, $node.pos') + } + match x { + ast.ForCStmt { + if x.scope.contains(g.cur_lock.pos.pos) { + g.unlock_locks() + } + } + ast.ForInStmt { + if x.scope.contains(g.cur_lock.pos.pos) { + g.unlock_locks() + } + } + ast.ForStmt { + if x.scope.contains(g.cur_lock.pos.pos) { + g.unlock_locks() + } + } + else {} + } + + if node.kind == .key_break { + g.writeln('goto ${node.label}__break;') + } else { + // assert node.kind == .key_continue + g.writeln('goto ${node.label}__continue;') + } + } else { + inner_loop := g.inner_loop + match inner_loop { + ast.ForCStmt { + if inner_loop.scope.contains(g.cur_lock.pos.pos) { + g.unlock_locks() + } + } + ast.ForInStmt { + if inner_loop.scope.contains(g.cur_lock.pos.pos) { + g.unlock_locks() + } + } + ast.ForStmt { + if inner_loop.scope.contains(g.cur_lock.pos.pos) { + g.unlock_locks() + } + } + else {} + } + // continue or break + if g.is_autofree && !g.is_builtin_mod { + g.trace_autofree('// free before continue/break') + g.autofree_scope_vars_stop(node.pos.pos - 1, node.pos.line_nr, true, + g.branch_parent_pos) + } + g.writeln('$node.kind;') + } + } + ast.ConstDecl { + g.write_v_source_line_info(node.pos) + // if g.pref.build_mode != .build_module { + g.const_decl(node) + // } + } + ast.CompFor { + g.comp_for(node) + } + ast.DeferStmt { + mut defer_stmt := node + defer_stmt.ifdef = g.defer_ifdef + g.writeln('${g.defer_flag_var(defer_stmt)} = true;') + g.defer_stmts << defer_stmt + } + ast.EnumDecl { + enum_name := util.no_dots(node.name) + is_flag := node.is_flag + g.enum_typedefs.writeln('typedef enum {') + mut cur_enum_expr := '' + mut cur_enum_offset := 0 + for i, field in node.fields { + g.enum_typedefs.write_string('\t${enum_name}__$field.name') + if field.has_expr { + g.enum_typedefs.write_string(' = ') + expr_str := g.expr_string(field.expr) + g.enum_typedefs.write_string(expr_str) + cur_enum_expr = expr_str + cur_enum_offset = 0 + } else if is_flag { + g.enum_typedefs.write_string(' = ') + cur_enum_expr = '1 << $i' + g.enum_typedefs.write_string((1 << i).str()) + cur_enum_offset = 0 + } + cur_value := if cur_enum_offset > 0 { + '$cur_enum_expr+$cur_enum_offset' + } else { + cur_enum_expr + } + g.enum_typedefs.writeln(', // $cur_value') + cur_enum_offset++ + } + g.enum_typedefs.writeln('} $enum_name;\n') + } + ast.ExprStmt { + g.write_v_source_line_info(node.pos) + // af := g.autofree && node.expr is ast.CallExpr && !g.is_builtin_mod + // if af { + // g.autofree_call_pregen(node.expr as ast.CallExpr) + // } + old_is_void_expr_stmt := g.is_void_expr_stmt + g.is_void_expr_stmt = !node.is_expr + if node.typ != ast.void_type && g.expected_cast_type != 0 { + g.expr_with_cast(node.expr, node.typ, g.expected_cast_type) + } else { + g.expr(node.expr) + } + g.is_void_expr_stmt = old_is_void_expr_stmt + // if af { + // g.autofree_call_postgen() + // } + if g.inside_ternary == 0 && !g.inside_if_optional && !node.is_expr + && node.expr !is ast.IfExpr { + g.writeln(';') + } + } + ast.FnDecl { + g.process_fn_decl(node) + } + ast.ForCStmt { + prev_branch_parent_pos := g.branch_parent_pos + g.branch_parent_pos = node.pos.pos + save_inner_loop := g.inner_loop + g.inner_loop = unsafe { &node } + if node.label != '' { + g.labeled_loops[node.label] = unsafe { &node } + } + g.write_v_source_line_info(node.pos) + g.for_c_stmt(node) + g.branch_parent_pos = prev_branch_parent_pos + g.labeled_loops.delete(node.label) + g.inner_loop = save_inner_loop + } + ast.ForInStmt { + prev_branch_parent_pos := g.branch_parent_pos + g.branch_parent_pos = node.pos.pos + save_inner_loop := g.inner_loop + g.inner_loop = unsafe { &node } + if node.label != '' { + g.labeled_loops[node.label] = unsafe { &node } + } + g.write_v_source_line_info(node.pos) + g.for_in_stmt(node) + g.branch_parent_pos = prev_branch_parent_pos + g.labeled_loops.delete(node.label) + g.inner_loop = save_inner_loop + } + ast.ForStmt { + prev_branch_parent_pos := g.branch_parent_pos + g.branch_parent_pos = node.pos.pos + save_inner_loop := g.inner_loop + g.inner_loop = unsafe { &node } + if node.label != '' { + g.labeled_loops[node.label] = unsafe { &node } + } + g.write_v_source_line_info(node.pos) + g.for_stmt(node) + g.branch_parent_pos = prev_branch_parent_pos + g.labeled_loops.delete(node.label) + g.inner_loop = save_inner_loop + } + ast.GlobalDecl { + g.global_decl(node) + } + ast.GotoLabel { + g.writeln('$node.name: {}') + } + ast.GotoStmt { + g.write_v_source_line_info(node.pos) + g.writeln('goto $node.name;') + } + ast.HashStmt { + mut ct_condition := '' + if node.ct_conds.len > 0 { + ct_condition_start := g.out.len + for idx, ct_expr in node.ct_conds { + g.comp_if_cond(ct_expr, false) + if idx < node.ct_conds.len - 1 { + g.write(' && ') + } + } + ct_condition = g.out.cut_to(ct_condition_start).trim_space() + // dump(node) + // dump(ct_condition) + } + // #include etc + if node.kind == 'include' { + mut missing_message := 'Header file $node.main, needed for module `$node.mod` was not found.' + if node.msg != '' { + missing_message += ' ${node.msg}.' + } else { + missing_message += ' Please install the corresponding development headers.' + } + mut guarded_include := get_guarded_include_text(node.main, missing_message) + if node.main == '' { + // fails with musl-gcc and msvc; but an unguarded include works: + guarded_include = '#include $node.main' + } + if node.main.contains('.m') { + g.definitions.writeln('\n') + if ct_condition.len > 0 { + g.definitions.writeln('#if $ct_condition') + } + // Objective C code import, include it after V types, so that e.g. `string` is + // available there + g.definitions.writeln('// added by module `$node.mod`') + g.definitions.writeln(guarded_include) + if ct_condition.len > 0 { + g.definitions.writeln('#endif // \$if $ct_condition') + } + g.definitions.writeln('\n') + } else { + g.includes.writeln('\n') + if ct_condition.len > 0 { + g.includes.writeln('#if $ct_condition') + } + g.includes.writeln('// added by module `$node.mod`') + g.includes.writeln(guarded_include) + if ct_condition.len > 0 { + g.includes.writeln('#endif // \$if $ct_condition') + } + g.includes.writeln('\n') + } + } else if node.kind == 'define' { + if ct_condition.len > 0 { + g.includes.writeln('#if $ct_condition') + } + g.includes.writeln('// defined by module `$node.mod`') + g.includes.writeln('#define $node.main') + if ct_condition.len > 0 { + g.includes.writeln('#endif // \$if $ct_condition') + } + } + } + ast.Import {} + ast.InterfaceDecl { + // definitions are sorted and added in write_types + ts := g.table.get_type_symbol(node.typ) + if !(ts.info as ast.Interface).is_generic { + for method in node.methods { + if method.return_type.has_flag(.optional) { + // Register an optional if it's not registered yet + g.register_optional(method.return_type) + } + } + } + } + ast.Module { + // g.is_builtin_mod = node.name == 'builtin' + g.is_builtin_mod = node.name in ['builtin', 'os', 'strconv', 'strings', 'gg'] + // g.cur_mod = node.name + g.cur_mod = node + } + ast.NodeError {} + ast.Return { + g.return_stmt(node) + } + ast.SqlStmt { + g.sql_stmt(node) + } + ast.StructDecl { + name := if node.language == .c { util.no_dots(node.name) } else { c_name(node.name) } + // TODO For some reason, build fails with autofree with this line + // as it's only informative, comment it for now + // g.gen_attrs(node.attrs) + // g.writeln('typedef struct {') + // for field in it.fields { + // field_type_sym := g.table.get_type_symbol(field.typ) + // g.writeln('\t$field_type_sym.name $field.name;') + // } + // g.writeln('} $name;') + if node.language == .c { + return + } + if node.is_union { + g.typedefs.writeln('typedef union $name $name;') + } else { + /* + attrs := if node.attrs.contains('packed') { + '__attribute__((__packed__))' + } else { + '' + } + */ + g.typedefs.writeln('typedef struct $name $name;') + } + } + ast.TypeDecl { + if !g.pref.skip_unused { + g.writeln('// TypeDecl') + } + } + } + if !g.skip_stmt_pos { // && g.stmt_path_pos.len > 0 { + g.stmt_path_pos.delete_last() + } + // If we have temporary string exprs to free after this statement, do it. e.g.: + // `foo('a' + 'b')` => `tmp := 'a' + 'b'; foo(tmp); string_free(&tmp);` + if g.is_autofree { + // if node is ast.ExprStmt {&& node.expr is ast.CallExpr { + if node !is ast.FnDecl { + // p := node.position() + // g.autofree_call_postgen(p.pos) + } + } +} + +fn (mut g Gen) write_defer_stmts() { + for i := g.defer_stmts.len - 1; i >= 0; i-- { + defer_stmt := g.defer_stmts[i] + g.writeln('// Defer begin') + g.writeln('if (${g.defer_flag_var(defer_stmt)}) {') + g.indent++ + if defer_stmt.ifdef.len > 0 { + g.writeln(defer_stmt.ifdef) + g.stmts(defer_stmt.stmts) + g.writeln('') + g.writeln('#endif') + } else { + g.indent-- + g.stmts(defer_stmt.stmts) + g.indent++ + } + g.indent-- + g.writeln('}') + g.writeln('// Defer end') + } +} + +fn (mut g Gen) for_c_stmt(node ast.ForCStmt) { + if node.is_multi { + g.is_vlines_enabled = false + if node.label.len > 0 { + g.writeln('$node.label:') + } + g.writeln('{') + g.indent++ + if node.has_init { + g.stmt(node.init) + } + g.writeln('bool _is_first = true;') + g.writeln('while (true) {') + g.writeln('\tif (_is_first) {') + g.writeln('\t\t_is_first = false;') + g.writeln('\t} else {') + if node.has_inc { + g.indent++ + g.stmt(node.inc) + g.writeln(';') + g.indent-- + } + g.writeln('}') + if node.has_cond { + g.write('if (!(') + g.expr(node.cond) + g.writeln(')) break;') + } + g.is_vlines_enabled = true + g.stmts(node.stmts) + if node.label.len > 0 { + g.writeln('${node.label}__continue: {}') + } + g.writeln('}') + g.indent-- + g.writeln('}') + if node.label.len > 0 { + g.writeln('${node.label}__break: {}') + } + } else { + g.is_vlines_enabled = false + if node.label.len > 0 { + g.writeln('$node.label:') + } + g.write('for (') + if !node.has_init { + g.write('; ') + } else { + g.stmt(node.init) + // Remove excess return and add space + if g.out.last_n(1) == '\n' { + g.out.go_back(1) + g.empty_line = false + g.write(' ') + } + } + if node.has_cond { + g.expr(node.cond) + } + g.write('; ') + if node.has_inc { + g.stmt(node.inc) + } + g.writeln(') {') + g.is_vlines_enabled = true + g.stmts(node.stmts) + if node.label.len > 0 { + g.writeln('${node.label}__continue: {}') + } + g.writeln('}') + if node.label.len > 0 { + g.writeln('${node.label}__break: {}') + } + } +} + +fn (mut g Gen) for_stmt(node ast.ForStmt) { + g.is_vlines_enabled = false + if node.label.len > 0 { + g.writeln('$node.label:') + } + g.writeln('for (;;) {') + if !node.is_inf { + g.indent++ + g.stmt_path_pos << g.out.len + g.write('if (!(') + g.expr(node.cond) + g.writeln(')) break;') + g.indent-- + } + g.is_vlines_enabled = true + g.stmts(node.stmts) + if node.label.len > 0 { + g.writeln('\t${node.label}__continue: {}') + } + g.writeln('}') + if node.label.len > 0 { + g.writeln('${node.label}__break: {}') + } +} + +fn (mut g Gen) for_in_stmt(node ast.ForInStmt) { + if node.label.len > 0 { + g.writeln('\t$node.label: {}') + } + if node.is_range { + // `for x in 1..10 {` + i := if node.val_var == '_' { g.new_tmp_var() } else { c_name(node.val_var) } + val_typ := g.table.mktyp(node.val_type) + g.write('for (${g.typ(val_typ)} $i = ') + g.expr(node.cond) + g.write('; $i < ') + g.expr(node.high) + g.writeln('; ++$i) {') + } else if node.kind == .array { + // `for num in nums {` + // g.writeln('// FOR IN array') + styp := g.typ(node.val_type) + val_sym := g.table.get_type_symbol(node.val_type) + mut cond_var := '' + if node.cond is ast.Ident || node.cond is ast.SelectorExpr { + cond_var = g.expr_string(node.cond) + } else { + cond_var = g.new_tmp_var() + g.write(g.typ(node.cond_type)) + g.write(' $cond_var = ') + g.expr(node.cond) + g.writeln(';') + } + i := if node.key_var in ['', '_'] { g.new_tmp_var() } else { node.key_var } + field_accessor := if node.cond_type.is_ptr() { '->' } else { '.' } + share_accessor := if node.cond_type.share() == .shared_t { 'val.' } else { '' } + op_field := field_accessor + share_accessor + g.empty_line = true + g.writeln('for (int $i = 0; $i < $cond_var${op_field}len; ++$i) {') + if node.val_var != '_' { + if val_sym.kind == .function { + g.write('\t') + g.write_fn_ptr_decl(val_sym.info as ast.FnType, c_name(node.val_var)) + g.writeln(' = ((voidptr*)$cond_var${op_field}data)[$i];') + } else if val_sym.kind == .array_fixed && !node.val_is_mut { + right := '(($styp*)$cond_var${op_field}data)[$i]' + g.writeln('\t$styp ${c_name(node.val_var)};') + g.writeln('\tmemcpy(*($styp*)${c_name(node.val_var)}, (byte*)$right, sizeof($styp));') + } else { + // If val is mutable (pointer behind the scenes), we need to generate + // `int* val = ((int*)arr.data) + i;` + // instead of + // `int* val = ((int**)arr.data)[i];` + // right := if node.val_is_mut { styp } else { styp + '*' } + right := if node.val_is_mut { + '(($styp)$cond_var${op_field}data) + $i' + } else { + '(($styp*)$cond_var${op_field}data)[$i]' + } + g.writeln('\t$styp ${c_name(node.val_var)} = $right;') + } + } + } else if node.kind == .array_fixed { + mut cond_var := '' + cond_type_is_ptr := node.cond_type.is_ptr() + cond_is_literal := node.cond is ast.ArrayInit + if cond_is_literal { + cond_var = g.new_tmp_var() + g.write(g.typ(node.cond_type)) + g.write(' $cond_var = ') + g.expr(node.cond) + g.writeln(';') + } else if cond_type_is_ptr { + cond_var = g.new_tmp_var() + cond_var_type := g.typ(node.cond_type).trim('*') + if !node.cond.is_lvalue() { + g.write('$cond_var_type *$cond_var = (($cond_var_type)') + } else { + g.write('$cond_var_type *$cond_var = (') + } + g.expr(node.cond) + g.writeln(');') + } else { + cond_var = g.expr_string(node.cond) + } + idx := if node.key_var in ['', '_'] { g.new_tmp_var() } else { node.key_var } + cond_sym := g.table.get_type_symbol(node.cond_type) + info := cond_sym.info as ast.ArrayFixed + g.writeln('for (int $idx = 0; $idx != $info.size; ++$idx) {') + if node.val_var != '_' { + val_sym := g.table.get_type_symbol(node.val_type) + is_fixed_array := val_sym.kind == .array_fixed && !node.val_is_mut + if val_sym.kind == .function { + g.write('\t') + g.write_fn_ptr_decl(val_sym.info as ast.FnType, c_name(node.val_var)) + } else if is_fixed_array { + styp := g.typ(node.val_type) + g.writeln('\t$styp ${c_name(node.val_var)};') + g.writeln('\tmemcpy(*($styp*)${c_name(node.val_var)}, (byte*)$cond_var[$idx], sizeof($styp));') + } else { + styp := g.typ(node.val_type) + g.write('\t$styp ${c_name(node.val_var)}') + } + if !is_fixed_array { + addr := if node.val_is_mut { '&' } else { '' } + if cond_type_is_ptr { + g.writeln(' = ${addr}(*$cond_var)[$idx];') + } else if cond_is_literal { + g.writeln(' = $addr$cond_var[$idx];') + } else { + g.write(' = $addr') + g.expr(node.cond) + g.writeln('[$idx];') + } + } + } + } else if node.kind == .map { + // `for key, val in map { + // g.writeln('// FOR IN map') + mut cond_var := '' + if node.cond is ast.Ident { + cond_var = g.expr_string(node.cond) + } else { + cond_var = g.new_tmp_var() + g.write(g.typ(node.cond_type)) + g.write(' $cond_var = ') + g.expr(node.cond) + g.writeln(';') + } + mut arw_or_pt := if node.cond_type.is_ptr() { '->' } else { '.' } + if node.cond_type.has_flag(.shared_f) { + arw_or_pt = '->val.' + } + idx := g.new_tmp_var() + map_len := g.new_tmp_var() + g.empty_line = true + g.writeln('int $map_len = $cond_var${arw_or_pt}key_values.len;') + g.writeln('for (int $idx = 0; $idx < $map_len; ++$idx ) {') + // TODO: don't have this check when the map has no deleted elements + g.indent++ + diff := g.new_tmp_var() + g.writeln('int $diff = $cond_var${arw_or_pt}key_values.len - $map_len;') + g.writeln('$map_len = $cond_var${arw_or_pt}key_values.len;') + // TODO: optimize this + g.writeln('if ($diff < 0) {') + g.writeln('\t$idx = -1;') + g.writeln('\tcontinue;') + g.writeln('}') + g.writeln('if (!DenseArray_has_index(&$cond_var${arw_or_pt}key_values, $idx)) {continue;}') + if node.key_var != '_' { + key_styp := g.typ(node.key_type) + key := c_name(node.key_var) + g.writeln('$key_styp $key = /*key*/ *($key_styp*)DenseArray_key(&$cond_var${arw_or_pt}key_values, $idx);') + // TODO: analyze whether node.key_type has a .clone() method and call .clone() for all types: + if node.key_type == ast.string_type { + g.writeln('$key = string_clone($key);') + } + } + if node.val_var != '_' { + val_sym := g.table.get_type_symbol(node.val_type) + if val_sym.kind == .function { + g.write_fn_ptr_decl(val_sym.info as ast.FnType, c_name(node.val_var)) + g.write(' = (*(voidptr*)') + g.writeln('DenseArray_value(&$cond_var${arw_or_pt}key_values, $idx));') + } else if val_sym.kind == .array_fixed && !node.val_is_mut { + val_styp := g.typ(node.val_type) + g.writeln('$val_styp ${c_name(node.val_var)};') + g.writeln('memcpy(*($val_styp*)${c_name(node.val_var)}, (byte*)DenseArray_value(&$cond_var${arw_or_pt}key_values, $idx), sizeof($val_styp));') + } else { + val_styp := g.typ(node.val_type) + if node.val_type.is_ptr() { + g.write('$val_styp ${c_name(node.val_var)} = &(*($val_styp)') + } else { + g.write('$val_styp ${c_name(node.val_var)} = (*($val_styp*)') + } + g.writeln('DenseArray_value(&$cond_var${arw_or_pt}key_values, $idx));') + } + } + g.indent-- + } else if node.kind == .string { + cond := if node.cond is ast.StringLiteral || node.cond is ast.StringInterLiteral { + ast.Expr(g.new_ctemp_var_then_gen(node.cond, ast.string_type)) + } else { + node.cond + } + i := if node.key_var in ['', '_'] { g.new_tmp_var() } else { node.key_var } + g.write('for (int $i = 0; $i < ') + g.expr(cond) + g.writeln('.len; ++$i) {') + if node.val_var != '_' { + g.write('\tbyte ${c_name(node.val_var)} = ') + g.expr(cond) + g.writeln('.str[$i];') + } + } else if node.kind == .struct_ { + cond_type_sym := g.table.get_type_symbol(node.cond_type) + next_fn := cond_type_sym.find_method('next') or { + verror('`next` method not found') + return + } + ret_typ := next_fn.return_type + t_expr := g.new_tmp_var() + g.write('${g.typ(node.cond_type)} $t_expr = ') + g.expr(node.cond) + g.writeln(';') + g.writeln('while (1) {') + t_var := g.new_tmp_var() + receiver_typ := next_fn.params[0].typ + receiver_styp := g.typ(receiver_typ) + fn_name := receiver_styp.replace_each(['*', '', '.', '__']) + '_next' + g.write('\t${g.typ(ret_typ)} $t_var = ${fn_name}(') + if !node.cond_type.is_ptr() && receiver_typ.is_ptr() { + g.write('&') + } + g.writeln('$t_expr);') + g.writeln('\tif (${t_var}.state != 0) break;') + val := if node.val_var in ['', '_'] { g.new_tmp_var() } else { node.val_var } + val_styp := g.typ(node.val_type) + g.writeln('\t$val_styp $val = *($val_styp*)${t_var}.data;') + } else { + typ_str := g.table.type_to_str(node.cond_type) + g.error('for in: unhandled symbol `$node.cond` of type `$typ_str`', node.pos) + } + g.stmts(node.stmts) + if node.label.len > 0 { + g.writeln('\t${node.label}__continue: {}') + } + + if node.kind == .map { + // diff := g.new_tmp_var() + // g.writeln('int $diff = $cond_var${arw_or_pt}key_values.len - $map_len;') + // g.writeln('if ($diff < 0) {') + // g.writeln('\t$idx = -1;') + // g.writeln('\t$map_len = $cond_var${arw_or_pt}key_values.len;') + // g.writeln('}') + } + + g.writeln('}') + if node.label.len > 0 { + g.writeln('\t${node.label}__break: {}') + } +} + +fn (mut g Gen) write_sumtype_casting_fn(got_ ast.Type, exp_ ast.Type) { + got, exp := got_.idx(), exp_.idx() + i := got | (exp << 16) + if got == exp || g.sumtype_definitions[i] { + return + } + g.sumtype_definitions[i] = true + got_sym := g.table.get_type_symbol(got) + exp_sym := g.table.get_type_symbol(exp) + mut sb := strings.new_builder(128) + got_cname, exp_cname := got_sym.cname, exp_sym.cname + sb.writeln('static inline $exp_cname ${got_cname}_to_sumtype_${exp_cname}($got_cname* x) {') + sb.writeln('\t$got_cname* ptr = memdup(x, sizeof($got_cname));') + sb.write_string('\treturn ($exp_cname){ ._$got_cname = ptr, ._typ = ${g.type_sidx(got)}') + for field in (exp_sym.info as ast.SumType).fields { + field_styp := g.typ(field.typ) + if got_sym.kind in [.sum_type, .interface_] { + // the field is already a wrapped pointer; we shouldn't wrap it once again + sb.write_string(', .$field.name = ptr->$field.name') + } else { + sb.write_string(', .$field.name = ($field_styp*)((char*)ptr + __offsetof_ptr(ptr, $got_cname, $field.name))') + } + } + sb.writeln('};\n}') + g.auto_fn_definitions << sb.str() +} + +fn (mut g Gen) call_cfn_for_casting_expr(fname string, expr ast.Expr, exp_is_ptr bool, exp_styp string, got_is_ptr bool, got_styp string) { + mut rparen_n := 1 + if exp_is_ptr { + g.write('HEAP($exp_styp, ') + rparen_n++ + } + g.write('${fname}(') + if !got_is_ptr { + if !expr.is_lvalue() + || (expr is ast.Ident && (expr as ast.Ident).obj.is_simple_define_const()) { + g.write('ADDR($got_styp, (') + rparen_n += 2 + } else { + g.write('&') + } + } + g.expr(expr) + g.write(')'.repeat(rparen_n)) +} + +// use instead of expr() when you need to cast to a different type +fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_type ast.Type) { + got_type := g.table.mktyp(got_type_raw) + exp_sym := g.table.get_type_symbol(expected_type) + expected_is_ptr := expected_type.is_ptr() + got_is_ptr := got_type.is_ptr() + got_sym := g.table.get_type_symbol(got_type) + // allow using the new Error struct as a string, to avoid a breaking change + // TODO: temporary to allow people to migrate their code; remove soon + if got_type == ast.error_type_idx && expected_type == ast.string_type_idx { + g.write('(*(') + g.expr(expr) + g.write('.msg))') + return + } + if got_sym.kind == .none_ && exp_sym.name == 'IError' { + g.expr(expr) + return + } + if exp_sym.info is ast.Interface && got_type_raw.idx() != expected_type.idx() + && !expected_type.has_flag(.optional) { + if expr is ast.StructInit && !got_type.is_ptr() { + g.inside_cast_in_heap++ + got_styp := g.cc_type(got_type.to_ptr(), true) + // TODO: why does cc_type even add this in the first place? + exp_styp := exp_sym.cname + mut fname := 'I_${got_styp}_to_Interface_$exp_styp' + if exp_sym.info.is_generic { + fname = g.generic_fn_name(exp_sym.info.concrete_types, fname, false) + } + g.call_cfn_for_casting_expr(fname, expr, expected_is_ptr, exp_styp, true, + got_styp) + g.inside_cast_in_heap-- + } else { + got_styp := g.cc_type(got_type, true) + exp_styp := exp_sym.cname + mut fname := '/*$exp_sym*/I_${got_styp}_to_Interface_$exp_styp' + if exp_sym.info.is_generic { + fname = g.generic_fn_name(exp_sym.info.concrete_types, fname, false) + } + g.call_cfn_for_casting_expr(fname, expr, expected_is_ptr, exp_styp, got_is_ptr, + got_styp) + } + return + } + // cast to sum type + exp_styp := g.typ(expected_type) + got_styp := g.typ(got_type) + if expected_type != ast.void_type { + expected_deref_type := if expected_is_ptr { expected_type.deref() } else { expected_type } + got_deref_type := if got_is_ptr { got_type.deref() } else { got_type } + if g.table.sumtype_has_variant(expected_deref_type, got_deref_type) { + mut is_already_sum_type := false + scope := g.file.scope.innermost(expr.position().pos) + if expr is ast.Ident { + if v := scope.find_var(expr.name) { + if v.smartcasts.len > 0 { + is_already_sum_type = true + } + } + } else if expr is ast.SelectorExpr { + if _ := scope.find_struct_field(expr.expr.str(), expr.expr_type, expr.field_name) { + is_already_sum_type = true + } + } + if is_already_sum_type { + // Don't create a new sum type wrapper if there is already one + g.prevent_sum_type_unwrapping_once = true + g.expr(expr) + } else { + g.write_sumtype_casting_fn(got_type, expected_type) + fname := '${got_sym.cname}_to_sumtype_$exp_sym.cname' + g.call_cfn_for_casting_expr(fname, expr, expected_is_ptr, exp_sym.cname, + got_is_ptr, got_styp) + } + return + } + } + // Generic dereferencing logic + neither_void := ast.voidptr_type !in [got_type, expected_type] + to_shared := expected_type.has_flag(.shared_f) && !got_type_raw.has_flag(.shared_f) + && !expected_type.has_flag(.optional) + // from_shared := got_type_raw.has_flag(.shared_f) && !expected_type.has_flag(.shared_f) + if to_shared { + shared_styp := exp_styp[0..exp_styp.len - 1] // `shared` implies ptr, so eat one `*` + if got_type_raw.is_ptr() { + g.error('cannot convert reference to `shared`', expr.position()) + } + if exp_sym.kind == .array { + g.writeln('($shared_styp*)__dup_shared_array(&($shared_styp){.mtx = {0}, .val =') + } else if exp_sym.kind == .map { + g.writeln('($shared_styp*)__dup_shared_map(&($shared_styp){.mtx = {0}, .val =') + } else { + g.writeln('($shared_styp*)__dup${shared_styp}(&($shared_styp){.mtx = {0}, .val =') + } + g.expr(expr) + g.writeln('}, sizeof($shared_styp))') + return + } + if got_is_ptr && !expected_is_ptr && neither_void + && exp_sym.kind !in [.interface_, .placeholder] && expr !is ast.InfixExpr { + got_deref_type := got_type.deref() + deref_sym := g.table.get_type_symbol(got_deref_type) + deref_will_match := expected_type in [got_type, got_deref_type, deref_sym.parent_idx] + got_is_opt := got_type.has_flag(.optional) + if deref_will_match || got_is_opt { + g.write('*') + } + } + if expected_type.has_flag(.optional) && expr is ast.None { + g.gen_optional_error(expected_type, expr) + return + } + if expr is ast.IntegerLiteral { + if expected_type in [ast.u64_type, ast.u32_type, ast.u16_type] && expr.val[0] != `-` { + g.expr(expr) + g.write('U') + return + } + } + if exp_sym.kind == .function { + g.write('(voidptr)') + } + // no cast + g.expr(expr) +} + +// cestring returns a V string, properly escaped for embeddeding in a C string literal. +fn cestring(s string) string { + return s.replace('\\', '\\\\').replace('"', "'") +} + +// ctoslit returns a '_SLIT("$s")' call, where s is properly escaped. +fn ctoslit(s string) string { + return '_SLIT("' + cestring(s) + '")' +} + +fn (mut g Gen) gen_attrs(attrs []ast.Attr) { + if g.pref.skip_unused { + return + } + for attr in attrs { + g.writeln('// Attr: [$attr.name]') + } +} + +fn (mut g Gen) gen_asm_stmt(stmt ast.AsmStmt) { + g.write('__asm__') + if stmt.is_volatile { + g.write(' volatile') + } + if stmt.is_goto { + g.write(' goto') + } + g.writeln(' (') + g.indent++ + for template_tmp in stmt.templates { + mut template := template_tmp + g.write('"') + if template.is_directive { + g.write('.') + } + g.write(template.name) + if template.is_label { + g.write(':') + } else { + g.write(' ') + } + // swap destionation and operands for att syntax + if template.args.len != 0 && !template.is_directive { + template.args.prepend(template.args[template.args.len - 1]) + template.args.delete(template.args.len - 1) + } + + for i, arg in template.args { + if stmt.arch == .amd64 && (template.name == 'call' || template.name[0] == `j`) + && arg is ast.AsmRegister { + g.write('*') // indirect branching + } + + g.asm_arg(arg, stmt) + if i + 1 < template.args.len { + g.write(', ') + } + } + + if !template.is_label { + g.write(';') + } + g.writeln('"') + } + + if stmt.output.len != 0 || stmt.input.len != 0 || stmt.clobbered.len != 0 || stmt.is_goto { + g.write(': ') + } + g.gen_asm_ios(stmt.output) + if stmt.input.len != 0 || stmt.clobbered.len != 0 || stmt.is_goto { + g.write(': ') + } + g.gen_asm_ios(stmt.input) + if stmt.clobbered.len != 0 || stmt.is_goto { + g.write(': ') + } + for i, clob in stmt.clobbered { + g.write('"') + g.write(clob.reg.name) + g.write('"') + if i + 1 < stmt.clobbered.len { + g.writeln(',') + } else { + g.writeln('') + } + } + if stmt.is_goto { + g.write(': ') + } + for i, label in stmt.global_labels { + g.write(label) + if i + 1 < stmt.clobbered.len { + g.writeln(',') + } else { + g.writeln('') + } + } + g.indent-- + g.writeln(');') +} + +fn (mut g Gen) asm_arg(arg ast.AsmArg, stmt ast.AsmStmt) { + match arg { + ast.AsmAlias { + name := arg.name + if name in stmt.local_labels || name in stmt.global_labels + || name in g.file.global_labels || stmt.is_basic + || (name !in stmt.input.map(it.alias) && name !in stmt.output.map(it.alias)) { + asm_formatted_name := if name in stmt.global_labels { '%l[$name]' } else { name } + g.write(asm_formatted_name) + } else { + g.write('%[$name]') + } + } + ast.CharLiteral { + g.write("'$arg.val'") + } + ast.IntegerLiteral, ast.FloatLiteral { + g.write('\$$arg.val') + } + ast.BoolLiteral { + g.write('\$$arg.val.str()') + } + ast.AsmRegister { + if !stmt.is_basic { + g.write('%') // escape percent with percent in extended assembly + } + g.write('%$arg.name') + } + ast.AsmAddressing { + base := arg.base + index := arg.index + displacement := arg.displacement + scale := arg.scale + match arg.mode { + .base { + g.write('(') + g.asm_arg(base, stmt) + g.write(')') + } + .displacement { + g.asm_arg(displacement, stmt) + g.write('()') + } + .base_plus_displacement { + g.asm_arg(displacement, stmt) + g.write('(') + g.asm_arg(base, stmt) + g.write(')') + } + .index_times_scale_plus_displacement { + if displacement is ast.AsmDisp { + g.asm_arg(displacement, stmt) + g.write('(, ') + } else if displacement is ast.AsmRegister { + g.write('(') + g.asm_arg(displacement, stmt) + g.write(',') + } else { + panic('unexpected $displacement.type_name()') + } + g.asm_arg(index, stmt) + g.write(',$scale)') + } + .base_plus_index_plus_displacement { + g.asm_arg(displacement, stmt) + g.write('(') + g.asm_arg(base, stmt) + g.write(',') + g.asm_arg(index, stmt) + g.write(',1)') + } + .base_plus_index_times_scale_plus_displacement { + g.asm_arg(displacement, stmt) + g.write('(') + g.asm_arg(base, stmt) + g.write(',') + g.asm_arg(index, stmt) + g.write(',$scale)') + } + .rip_plus_displacement { + g.asm_arg(displacement, stmt) + g.write('(') + g.asm_arg(base, stmt) + g.write(')') + } + .invalid { + g.error('invalid addressing mode', arg.pos) + } + } + } + ast.AsmDisp { + g.write(arg.val) + } + string { + g.write(arg) + } + } +} + +fn (mut g Gen) gen_asm_ios(ios []ast.AsmIO) { + for i, io in ios { + if io.alias != '' { + g.write('[$io.alias] ') + } + g.write('"$io.constraint" (') + g.expr(io.expr) + g.write(')') + if i + 1 < ios.len { + g.writeln(',') + } else { + g.writeln('') + } + } +} + +fn cnewlines(s string) string { + return s.replace('\n', r'\n') +} + +fn (mut g Gen) write_fn_ptr_decl(func &ast.FnType, ptr_name string) { + ret_styp := g.typ(func.func.return_type) + g.write('$ret_styp (*$ptr_name) (') + arg_len := func.func.params.len + for i, arg in func.func.params { + arg_styp := g.typ(arg.typ) + g.write('$arg_styp $arg.name') + if i < arg_len - 1 { + g.write(', ') + } + } + g.write(')') +} + +// TODO this function is scary. Simplify/split up. +fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) { + if assign_stmt.is_static { + g.write('static ') + } + mut return_type := ast.void_type + is_decl := assign_stmt.op == .decl_assign + g.assign_op = assign_stmt.op + op := if is_decl { token.Kind.assign } else { assign_stmt.op } + right_expr := assign_stmt.right[0] + match right_expr { + ast.CallExpr { return_type = right_expr.return_type } + ast.LockExpr { return_type = right_expr.typ } + ast.MatchExpr { return_type = right_expr.return_type } + ast.IfExpr { return_type = right_expr.typ } + else {} + } + // Free the old value assigned to this string var (only if it's `str = [new value]` + // or `x.str = [new value]` ) + mut af := g.is_autofree && !g.is_builtin_mod && assign_stmt.op == .assign + && assign_stmt.left_types.len == 1 + && (assign_stmt.left[0] is ast.Ident || assign_stmt.left[0] is ast.SelectorExpr) + // assign_stmt.left_types[0] in [ast.string_type, ast.array_type] && + mut sref_name := '' + mut type_to_free := '' + if af { + first_left_type := assign_stmt.left_types[0] + first_left_sym := g.table.get_type_symbol(assign_stmt.left_types[0]) + if first_left_type == ast.string_type || first_left_sym.kind == .array { + type_to_free = if first_left_type == ast.string_type { 'string' } else { 'array' } + mut ok := true + left0 := assign_stmt.left[0] + if left0 is ast.Ident { + if left0.name == '_' { + ok = false + } + } + if ok { + sref_name = '_sref$assign_stmt.pos.pos' + g.write('$type_to_free $sref_name = (') // TODO we are copying the entire string here, optimize + // we can't just do `.str` since we need the extra data from the string struct + // doing `&string` is also not an option since the stack memory with the data will be overwritten + g.expr(left0) // assign_stmt.left[0]) + g.writeln('); // free $type_to_free on re-assignment2') + defer { + if af { + g.writeln('${type_to_free}_free(&$sref_name);') + } + } + } else { + af = false + } + } else { + af = false + } + } + // Autofree tmp arg vars + // first_right := assign_stmt.right[0] + // af := g.autofree && first_right is ast.CallExpr && !g.is_builtin_mod + // if af { + // g.autofree_call_pregen(first_right as ast.CallExpr) + // } + // + // + // Handle optionals. We need to declare a temp variable for them, that's why they are handled + // here, not in call_expr(). + // `pos := s.index('x') or { return }` + // ==========> + // Option_int _t190 = string_index(s, _STR("x")); // _STR() no more used!! + // if (_t190.state != 2) { + // Error err = _t190.err; + // return; + // } + // int pos = *(int*)_t190.data; + // mut tmp_opt := '' + /* + is_optional := false && g.is_autofree && (assign_stmt.op in [.decl_assign, .assign]) + && assign_stmt.left_types.len == 1 && assign_stmt.right[0] is ast.CallExpr + if is_optional { + // g.write('/* optional assignment */') + call_expr := assign_stmt.right[0] as ast.CallExpr + if call_expr.or_block.kind != .absent { + styp := g.typ(call_expr.return_type.set_flag(.optional)) + tmp_opt = g.new_tmp_var() + g.write('/*AF opt*/$styp $tmp_opt = ') + g.expr(assign_stmt.right[0]) + g.or_block(tmp_opt, call_expr.or_block, call_expr.return_type) + g.writeln('/*=============ret*/') + // if af && is_optional { + // g.autofree_call_postgen() + // } + // return + } + } + */ + // json_test failed w/o this check + if return_type != ast.void_type && return_type != 0 { + sym := g.table.get_type_symbol(return_type) + if sym.kind == .multi_return { + // multi return + // TODO Handle in if_expr + is_opt := return_type.has_flag(.optional) + mr_var_name := 'mr_$assign_stmt.pos.pos' + mr_styp := g.typ(return_type) + g.write('$mr_styp $mr_var_name = ') + g.expr(assign_stmt.right[0]) + g.writeln(';') + for i, lx in assign_stmt.left { + mut is_auto_heap := false + mut ident := ast.Ident{ + scope: 0 + } + if lx is ast.Ident { + ident = lx + if lx.kind == .blank_ident { + continue + } + if lx.obj is ast.Var { + is_auto_heap = lx.obj.is_auto_heap + } + } + styp := if ident.name in g.defer_vars { + '' + } else { + g.typ(assign_stmt.left_types[i]) + } + if assign_stmt.op == .decl_assign { + g.write('$styp ') + if is_auto_heap { + g.write('*') + } + } + if lx.is_auto_deref_var() { + g.write('*') + } + g.expr(lx) + noscan := if is_auto_heap { g.check_noscan(return_type) } else { '' } + if g.is_arraymap_set { + if is_opt { + mr_base_styp := g.base_type(return_type) + if is_auto_heap { + g.writeln('HEAP${noscan}($mr_base_styp, *($mr_base_styp*)${mr_var_name}.data).arg$i) });') + } else { + g.writeln('(*($mr_base_styp*)${mr_var_name}.data).arg$i });') + } + } else { + if is_auto_heap { + g.writeln('HEAP${noscan}($styp, ${mr_var_name}.arg$i) });') + } else { + g.writeln('${mr_var_name}.arg$i });') + } + } + } else { + if is_opt { + mr_base_styp := g.base_type(return_type) + if is_auto_heap { + g.writeln(' = HEAP${noscan}($mr_base_styp, *($mr_base_styp*)${mr_var_name}.data).arg$i);') + } else { + g.writeln(' = (*($mr_base_styp*)${mr_var_name}.data).arg$i;') + } + } else { + if is_auto_heap { + g.writeln(' = HEAP${noscan}($styp, ${mr_var_name}.arg$i);') + } else { + g.writeln(' = ${mr_var_name}.arg$i;') + } + } + } + } + if g.is_arraymap_set { + g.is_arraymap_set = false + } + return + } + } + // TODO: non idents on left (exprs) + if assign_stmt.has_cross_var { + for i, left in assign_stmt.left { + match left { + ast.Ident { + left_typ := assign_stmt.left_types[i] + left_sym := g.table.get_type_symbol(left_typ) + if left_sym.kind == .function { + g.write_fn_ptr_decl(left_sym.info as ast.FnType, '_var_$left.pos.pos') + g.writeln(' = $left.name;') + } else { + styp := g.typ(left_typ) + g.writeln('$styp _var_$left.pos.pos = $left.name;') + } + } + ast.IndexExpr { + sym := g.table.get_type_symbol(left.left_type) + if sym.kind == .array { + info := sym.info as ast.Array + elem_typ := g.table.get_type_symbol(info.elem_type) + if elem_typ.kind == .function { + left_typ := assign_stmt.left_types[i] + left_sym := g.table.get_type_symbol(left_typ) + g.write_fn_ptr_decl(left_sym.info as ast.FnType, '_var_$left.pos.pos') + g.write(' = *(voidptr*)array_get(') + } else { + styp := g.typ(info.elem_type) + g.write('$styp _var_$left.pos.pos = *($styp*)array_get(') + } + if left.left_type.is_ptr() { + g.write('*') + } + needs_clone := info.elem_type == ast.string_type && g.is_autofree + if needs_clone { + g.write('/*1*/string_clone(') + } + g.expr(left.left) + if needs_clone { + g.write(')') + } + g.write(', ') + g.expr(left.index) + g.writeln(');') + } else if sym.kind == .map { + info := sym.info as ast.Map + skeytyp := g.typ(info.key_type) + styp := g.typ(info.value_type) + zero := g.type_default(info.value_type) + val_typ := g.table.get_type_symbol(info.value_type) + if val_typ.kind == .function { + left_type := assign_stmt.left_types[i] + left_sym := g.table.get_type_symbol(left_type) + g.write_fn_ptr_decl(left_sym.info as ast.FnType, '_var_$left.pos.pos') + g.write(' = *(voidptr*)map_get(') + } else { + g.write('$styp _var_$left.pos.pos = *($styp*)map_get(') + } + if !left.left_type.is_ptr() { + g.write('ADDR(map, ') + g.expr(left.left) + g.write(')') + } else { + g.expr(left.left) + } + g.write(', &($skeytyp[]){') + g.expr(left.index) + g.write('}') + if val_typ.kind == .function { + g.writeln(', &(voidptr[]){ $zero });') + } else { + g.writeln(', &($styp[]){ $zero });') + } + } + } + ast.SelectorExpr { + styp := g.typ(left.typ) + g.write('$styp _var_$left.pos.pos = ') + g.expr(left.expr) + mut sel := '.' + if left.expr_type.is_ptr() { + if left.expr_type.has_flag(.shared_f) { + sel = '->val.' + } else { + sel = '->' + } + } + g.writeln('$sel$left.field_name;') + } + else {} + } + } + } + // `a := 1` | `a,b := 1,2` + for i, left in assign_stmt.left { + mut is_auto_heap := false + mut var_type := assign_stmt.left_types[i] + mut val_type := assign_stmt.right_types[i] + val := assign_stmt.right[i] + mut is_call := false + mut blank_assign := false + mut ident := ast.Ident{ + scope: 0 + } + left_sym := g.table.get_type_symbol(var_type) + if left is ast.Ident { + ident = left + // id_info := ident.var_info() + // var_type = id_info.typ + blank_assign = left.kind == .blank_ident + // TODO: temporary, remove this + left_info := left.info + if left_info is ast.IdentVar { + share := left_info.share + if share == .shared_t { + var_type = var_type.set_flag(.shared_f) + } + if share == .atomic_t { + var_type = var_type.set_flag(.atomic_f) + } + } + if left.obj is ast.Var { + is_auto_heap = left.obj.is_auto_heap + } + } + styp := g.typ(var_type) + mut is_fixed_array_init := false + mut has_val := false + match val { + ast.ArrayInit { + is_fixed_array_init = val.is_fixed + has_val = val.has_val + } + ast.CallExpr { + is_call = true + return_type = val.return_type + } + // TODO: no buffer fiddling + ast.AnonFn { + if blank_assign { + g.write('{') + } + // if it's a decl assign (`:=`) or a blank assignment `_ =`/`_ :=` then generate `void (*ident) (args) =` + if (is_decl || blank_assign) && left is ast.Ident { + ret_styp := g.typ(val.decl.return_type) + g.write('$ret_styp (*$ident.name) (') + def_pos := g.definitions.len + g.fn_args(val.decl.params, val.decl.is_variadic, voidptr(0)) + g.definitions.go_back(g.definitions.len - def_pos) + g.write(') = ') + } else { + g.is_assign_lhs = true + g.assign_op = assign_stmt.op + g.expr(left) + g.is_assign_lhs = false + g.is_arraymap_set = false + if left is ast.IndexExpr { + sym := g.table.get_type_symbol(left.left_type) + if sym.kind in [.map, .array] { + g.expr(val) + g.writeln('});') + continue + } + } + g.write(' = ') + } + g.expr(val) + g.writeln(';') + if blank_assign { + g.write('}') + } + continue + } + else {} + } + unwrapped_val_type := g.unwrap_generic(val_type) + right_sym := g.table.get_type_symbol(unwrapped_val_type) + unaliased_right_sym := g.table.get_final_type_symbol(unwrapped_val_type) + is_fixed_array_var := unaliased_right_sym.kind == .array_fixed && val !is ast.ArrayInit + && (val is ast.Ident || val is ast.IndexExpr || val is ast.CallExpr + || (val is ast.CastExpr && (val as ast.CastExpr).expr !is ast.ArrayInit) + || val is ast.SelectorExpr) + g.is_assign_lhs = true + g.assign_op = assign_stmt.op + if val_type.has_flag(.optional) { + g.right_is_opt = true + } + if blank_assign { + if is_call { + old_is_void_expr_stmt := g.is_void_expr_stmt + g.is_void_expr_stmt = true + g.expr(val) + g.is_void_expr_stmt = old_is_void_expr_stmt + } else { + g.write('{$styp _ = ') + g.expr(val) + g.writeln(';}') + } + } else if assign_stmt.op == .assign + && (is_fixed_array_init || (right_sym.kind == .array_fixed && val is ast.Ident)) { + mut v_var := '' + arr_typ := styp.trim('*') + if is_fixed_array_init { + right := val as ast.ArrayInit + v_var = g.new_tmp_var() + g.write('$arr_typ $v_var = ') + g.expr(right) + g.writeln(';') + } else { + right := val as ast.Ident + v_var = right.name + } + pos := g.out.len + g.expr(left) + + if g.is_arraymap_set && g.arraymap_set_pos > 0 { + g.out.go_back_to(g.arraymap_set_pos) + g.write(', &$v_var)') + g.is_arraymap_set = false + g.arraymap_set_pos = 0 + } else { + g.out.go_back_to(pos) + is_var_mut := !is_decl && left.is_auto_deref_var() + addr := if is_var_mut { '' } else { '&' } + g.writeln('') + g.write('memcpy($addr') + g.expr(left) + g.writeln(', &$v_var, sizeof($arr_typ));') + } + g.is_assign_lhs = false + } else { + is_inside_ternary := g.inside_ternary != 0 + cur_line := if is_inside_ternary && is_decl { + g.register_ternary_name(ident.name) + g.empty_line = false + g.go_before_ternary() + } else { + '' + } + mut str_add := false + mut op_overloaded := false + mut op_expected_left := ast.Type(0) + mut op_expected_right := ast.Type(0) + if var_type == ast.string_type_idx && assign_stmt.op == .plus_assign { + if left is ast.IndexExpr { + // a[0] += str => `array_set(&a, 0, &(string[]) {string__plus(...))})` + g.expr(left) + g.write('string__plus(') + } else { + // str += str2 => `str = string__plus(str, str2)` + g.expr(left) + g.write(' = /*f*/string__plus(') + } + g.is_assign_lhs = false + str_add = true + } + // Assignment Operator Overloading + if ((left_sym.kind == .struct_ && right_sym.kind == .struct_) + || (left_sym.kind == .alias && right_sym.kind == .alias)) + && assign_stmt.op in [.plus_assign, .minus_assign, .div_assign, .mult_assign, .mod_assign] { + extracted_op := match assign_stmt.op { + .plus_assign { '+' } + .minus_assign { '-' } + .div_assign { '/' } + .mod_assign { '%' } + .mult_assign { '*' } + else { 'unknown op' } + } + g.expr(left) + g.write(' = ${styp}_${util.replace_op(extracted_op)}(') + method := g.table.type_find_method(left_sym, extracted_op) or { + // the checker will most likely have found this, already... + g.error('assignemnt operator `$extracted_op=` used but no `$extracted_op` method defined', + assign_stmt.pos) + ast.Fn{} + } + op_expected_left = method.params[0].typ + op_expected_right = method.params[1].typ + op_overloaded = true + } + if right_sym.kind == .function && is_decl { + if is_inside_ternary && is_decl { + g.out.write_string(util.tabs(g.indent - g.inside_ternary)) + } + func := right_sym.info as ast.FnType + ret_styp := g.typ(func.func.return_type) + g.write('$ret_styp (*${g.get_ternary_name(ident.name)}) (') + def_pos := g.definitions.len + g.fn_args(func.func.params, func.func.is_variadic, voidptr(0)) + g.definitions.go_back(g.definitions.len - def_pos) + g.write(')') + } else { + if is_decl { + if is_inside_ternary { + g.out.write_string(util.tabs(g.indent - g.inside_ternary)) + } + mut is_used_var_styp := false + if ident.name !in g.defer_vars { + val_sym := g.table.get_type_symbol(val_type) + if val_sym.info is ast.Struct { + if val_sym.info.generic_types.len > 0 { + if val is ast.StructInit { + var_styp := g.typ(val.typ) + g.write('$var_styp ') + is_used_var_styp = true + } else if val is ast.PrefixExpr { + if val.op == .amp && val.right is ast.StructInit { + var_styp := g.typ(val.right.typ.to_ptr()) + g.write('$var_styp ') + is_used_var_styp = true + } + } + } + } + if !is_used_var_styp { + g.write('$styp ') + } + if is_auto_heap { + g.write('*') + } + } + } + if left is ast.Ident || left is ast.SelectorExpr { + g.prevent_sum_type_unwrapping_once = true + } + if !is_fixed_array_var || is_decl { + if op_overloaded { + g.op_arg(left, op_expected_left, var_type) + } else { + if !is_decl && left.is_auto_deref_var() { + g.write('*') + } + g.expr(left) + } + } + } + if is_inside_ternary && is_decl { + g.write(';\n$cur_line') + g.out.write_string(util.tabs(g.indent)) + g.expr(left) + } + g.is_assign_lhs = false + if is_fixed_array_var { + if is_decl { + g.writeln(';') + } + } else if !g.is_arraymap_set && !str_add && !op_overloaded { + g.write(' $op ') + } else if str_add || op_overloaded { + g.write(', ') + } + mut cloned := false + if g.is_autofree && right_sym.kind in [.array, .string] { + if g.gen_clone_assignment(val, right_sym, false) { + cloned = true + } + } + unwrap_optional := !var_type.has_flag(.optional) && val_type.has_flag(.optional) + if unwrap_optional { + // Unwrap the optional now that the testing code has been prepended. + // `pos := s.index(... + // `int pos = *(int)_t10.data;` + // if g.is_autofree { + /* + if is_optional { + g.write('*($styp*)') + g.write(tmp_opt + '.data/*FFz*/') + g.right_is_opt = false + if g.inside_ternary == 0 && !assign_stmt.is_simple { + g.writeln(';') + } + return + } + */ + } + g.is_shared = var_type.has_flag(.shared_f) + if !cloned { + if is_fixed_array_var { + typ_str := g.typ(val_type).trim('*') + ref_str := if val_type.is_ptr() { '' } else { '&' } + g.write('memcpy(($typ_str*)') + g.expr(left) + g.write(', (byte*)$ref_str') + g.expr(val) + g.write(', sizeof($typ_str))') + } else if is_decl { + if is_fixed_array_init && !has_val { + if val is ast.ArrayInit { + if val.has_default { + g.write('{') + g.expr(val.default_expr) + info := right_sym.info as ast.ArrayFixed + for _ in 1 .. info.size { + g.write(', ') + g.expr(val.default_expr) + } + g.write('}') + } else { + g.write('{0}') + } + } else { + g.write('{0}') + } + } else { + if is_auto_heap { + g.write('HEAP($styp, (') + } + if val.is_auto_deref_var() { + g.write('*') + } + g.expr(val) + if is_auto_heap { + g.write('))') + } + } + } else { + if assign_stmt.has_cross_var { + g.gen_cross_tmp_variable(assign_stmt.left, val) + } else { + if op_overloaded { + g.op_arg(val, op_expected_right, val_type) + } else { + g.expr_with_cast(val, val_type, var_type) + } + } + } + } + if str_add || op_overloaded { + g.write(')') + } + if g.is_arraymap_set { + g.write(' })') + g.is_arraymap_set = false + } + g.is_shared = false + } + g.right_is_opt = false + if g.inside_ternary == 0 && (assign_stmt.left.len > 1 || !assign_stmt.is_simple) { + g.writeln(';') + } + } +} + +fn (mut g Gen) gen_cross_tmp_variable(left []ast.Expr, val ast.Expr) { + val_ := val + match val { + ast.Ident { + mut has_var := false + for lx in left { + if lx is ast.Ident { + if val.name == lx.name { + g.write('_var_') + g.write(lx.pos.pos.str()) + has_var = true + break + } + } + } + if !has_var { + g.expr(val_) + } + } + ast.IndexExpr { + mut has_var := false + for lx in left { + if val_.str() == lx.str() { + g.write('_var_') + g.write(lx.position().pos.str()) + has_var = true + break + } + } + if !has_var { + g.expr(val_) + } + } + ast.InfixExpr { + g.gen_cross_tmp_variable(left, val.left) + g.write(val.op.str()) + g.gen_cross_tmp_variable(left, val.right) + } + ast.PrefixExpr { + g.write(val.op.str()) + g.gen_cross_tmp_variable(left, val.right) + } + ast.PostfixExpr { + g.gen_cross_tmp_variable(left, val.expr) + g.write(val.op.str()) + } + ast.SelectorExpr { + mut has_var := false + for lx in left { + if val_.str() == lx.str() { + g.write('_var_') + g.write(lx.position().pos.str()) + has_var = true + break + } + } + if !has_var { + g.expr(val_) + } + } + else { + g.expr(val_) + } + } +} + +fn (mut g Gen) register_ternary_name(name string) { + level_key := g.inside_ternary.str() + if level_key !in g.ternary_level_names { + g.ternary_level_names[level_key] = []string{} + } + new_name := g.new_tmp_var() + g.ternary_names[name] = new_name + g.ternary_level_names[level_key] << name +} + +fn (mut g Gen) get_ternary_name(name string) string { + if g.inside_ternary == 0 { + return name + } + if name !in g.ternary_names { + return name + } + return g.ternary_names[name] +} + +fn (mut g Gen) gen_clone_assignment(val ast.Expr, right_sym ast.TypeSymbol, add_eq bool) bool { + if val !is ast.Ident && val !is ast.SelectorExpr { + return false + } + if g.is_autofree && right_sym.kind == .array { + // `arr1 = arr2` => `arr1 = arr2.clone()` + if add_eq { + g.write('=') + } + g.write(' array_clone_static_to_depth(') + g.expr(val) + elem_type := (right_sym.info as ast.Array).elem_type + array_depth := g.get_array_depth(elem_type) + g.write(', $array_depth)') + } else if g.is_autofree && right_sym.kind == .string { + if add_eq { + g.write('=') + } + // `str1 = str2` => `str1 = str2.clone()` + g.write(' string_clone_static(') + g.expr(val) + g.write(')') + } + return true +} + +fn (mut g Gen) autofree_scope_vars(pos int, line_nr int, free_parent_scopes bool) { + g.autofree_scope_vars_stop(pos, line_nr, free_parent_scopes, -1) +} + +fn (mut g Gen) autofree_scope_vars_stop(pos int, line_nr int, free_parent_scopes bool, stop_pos int) { + if g.is_builtin_mod { + // In `builtin` everything is freed manually. + return + } + if pos == -1 { + // TODO why can pos be -1? + return + } + // eprintln('> free_scope_vars($pos)') + scope := g.file.scope.innermost(pos) + if scope.start_pos == 0 { + // TODO why can scope.pos be 0? (only outside fns?) + return + } + g.trace_autofree('// autofree_scope_vars(pos=$pos line_nr=$line_nr scope.pos=$scope.start_pos scope.end_pos=$scope.end_pos)') + g.autofree_scope_vars2(scope, scope.start_pos, scope.end_pos, line_nr, free_parent_scopes, + stop_pos) +} + +[if trace_autofree ?] +fn (mut g Gen) trace_autofree(line string) { + g.writeln(line) +} + +// fn (mut g Gen) autofree_scope_vars2(scope &ast.Scope, end_pos int) { +fn (mut g Gen) autofree_scope_vars2(scope &ast.Scope, start_pos int, end_pos int, line_nr int, free_parent_scopes bool, stop_pos int) { + if isnil(scope) { + return + } + for _, obj in scope.objects { + match obj { + ast.Var { + g.trace_autofree('// var "$obj.name" var.pos=$obj.pos.pos var.line_nr=$obj.pos.line_nr') + if obj.name == g.returned_var_name { + g.trace_autofree('// skipping returned var') + continue + } + if obj.is_or { + // Skip vars inited with the `or {}`, since they are generated + // after the or block in C. + g.trace_autofree('// skipping `or{}` var "$obj.name"') + continue + } + if obj.is_tmp { + // Skip for loop vars + g.trace_autofree('// skipping tmp var "$obj.name"') + continue + } + // if var.typ == 0 { + // // TODO why 0? + // continue + // } + // if v.pos.pos > end_pos { + if obj.pos.pos > end_pos || (obj.pos.pos < start_pos && obj.pos.line_nr == line_nr) { + // Do not free vars that were declared after this scope + continue + } + is_optional := obj.typ.has_flag(.optional) + if is_optional { + // TODO: free optionals + continue + } + g.autofree_variable(obj) + } + else {} + } + } + // Free all vars in parent scopes as well: + // ``` + // s := ... + // if ... { + // s.free() + // return + // } + // ``` + // if !isnil(scope.parent) && line_nr > 0 { + if free_parent_scopes && !isnil(scope.parent) + && (stop_pos == -1 || scope.parent.start_pos >= stop_pos) { + g.trace_autofree('// af parent scope:') + g.autofree_scope_vars2(scope.parent, start_pos, end_pos, line_nr, true, stop_pos) + } +} + +fn (mut g Gen) autofree_variable(v ast.Var) { + sym := g.table.get_type_symbol(v.typ) + // if v.name.contains('output2') { + // eprintln(' > var name: ${v.name:-20s} | is_arg: ${v.is_arg.str():6} | var type: ${int(v.typ):8} | type_name: ${sym.name:-33s}') + // } + if sym.kind == .array { + if sym.has_method('free') { + free_method_name := g.typ(v.typ) + '_free' + g.autofree_var_call(free_method_name, v) + return + } + g.autofree_var_call('array_free', v) + return + } + if sym.kind == .string { + // Don't free simple string literals. + match v.expr { + ast.StringLiteral { + g.trace_autofree('// str literal') + } + else { + // NOTE/TODO: assign_stmt multi returns variables have no expr + // since the type comes from the called fns return type + /* + f := v.name[0] + if + //!(f >= `a` && f <= `d`) { + //f != `c` { + v.name!='cvar_name' { + t := typeof(v.expr) + return '// other ' + t + '\n' + } + */ + } + } + g.autofree_var_call('string_free', v) + return + } + if sym.has_method('free') { + g.autofree_var_call(c_name(sym.name) + '_free', v) + } +} + +fn (mut g Gen) autofree_var_call(free_fn_name string, v ast.Var) { + if v.is_arg { + // fn args should not be autofreed + return + } + if v.is_used && v.is_autofree_tmp { + // tmp expr vars do not need to be freed again here + return + } + if g.is_builtin_mod { + return + } + if !g.is_autofree { + return + } + // if v.is_autofree_tmp && !g.doing_autofree_tmp { + // return + // } + if v.name.contains('expr_write_string_1_') { + // TODO remove this temporary hack + return + } + if v.typ.is_ptr() { + g.writeln('\t${free_fn_name}(${c_name(v.name)}); // autofreed ptr var') + } else { + if v.typ == ast.error_type && !v.is_autofree_tmp { + return + } + g.writeln('\t${free_fn_name}(&${c_name(v.name)}); // autofreed var $g.cur_mod.name $g.is_builtin_mod') + } +} + +fn (mut g Gen) gen_anon_fn_decl(mut node ast.AnonFn) { + if !node.has_gen { + pos := g.out.len + g.anon_fn = true + g.stmt(node.decl) + g.anon_fn = false + fn_body := g.out.after(pos) + g.out.go_back(fn_body.len) + g.anon_fn_definitions << fn_body + node.has_gen = true + } +} + +fn (mut g Gen) map_fn_ptrs(key_typ ast.TypeSymbol) (string, string, string, string) { + mut hash_fn := '' + mut key_eq_fn := '' + mut clone_fn := '' + mut free_fn := '&map_free_nop' + match key_typ.kind { + .byte, .i8, .char { + hash_fn = '&map_hash_int_1' + key_eq_fn = '&map_eq_int_1' + clone_fn = '&map_clone_int_1' + } + .i16, .u16 { + hash_fn = '&map_hash_int_2' + key_eq_fn = '&map_eq_int_2' + clone_fn = '&map_clone_int_2' + } + .int, .u32, .rune, .f32, .enum_ { + hash_fn = '&map_hash_int_4' + key_eq_fn = '&map_eq_int_4' + clone_fn = '&map_clone_int_4' + } + .voidptr { + ts := if g.pref.m64 { + unsafe { &g.table.type_symbols[ast.u64_type_idx] } + } else { + unsafe { &g.table.type_symbols[ast.u32_type_idx] } + } + return g.map_fn_ptrs(ts) + } + .u64, .i64, .f64 { + hash_fn = '&map_hash_int_8' + key_eq_fn = '&map_eq_int_8' + clone_fn = '&map_clone_int_8' + } + .string { + hash_fn = '&map_hash_string' + key_eq_fn = '&map_eq_string' + clone_fn = '&map_clone_string' + free_fn = '&map_free_string' + } + else { + verror('map key type not supported') + } + } + return hash_fn, key_eq_fn, clone_fn, free_fn +} + +fn (mut g Gen) expr(node ast.Expr) { + // println('cgen expr() line_nr=$node.pos.line_nr') + old_discard_or_result := g.discard_or_result + old_is_void_expr_stmt := g.is_void_expr_stmt + if g.is_void_expr_stmt { + g.discard_or_result = true + g.is_void_expr_stmt = false + } else { + g.discard_or_result = false + } + // NB: please keep the type names in the match here in alphabetical order: + match mut node { + ast.EmptyExpr { + g.error('g.expr(): unhandled EmptyExpr', token.Position{}) + } + ast.AnonFn { + // TODO: dont fiddle with buffers + g.gen_anon_fn_decl(mut node) + fsym := g.table.get_type_symbol(node.typ) + g.write(fsym.name) + } + ast.ArrayDecompose { + g.expr(node.expr) + } + ast.ArrayInit { + g.array_init(node) + } + ast.AsCast { + g.as_cast(node) + } + ast.Assoc { + g.assoc(node) + } + ast.BoolLiteral { + g.write(node.val.str()) + } + ast.CallExpr { + // if g.fileis('1.strings') { + // println('\ncall_expr()()') + // } + ret_type := if node.or_block.kind == .absent { + node.return_type + } else { + node.return_type.clear_flag(.optional) + } + mut shared_styp := '' + if g.is_shared && !ret_type.has_flag(.shared_f) { + ret_sym := g.table.get_type_symbol(ret_type) + shared_typ := ret_type.set_flag(.shared_f) + shared_styp = g.typ(shared_typ) + if ret_sym.kind == .array { + g.writeln('($shared_styp*)__dup_shared_array(&($shared_styp){.mtx = {0}, .val =') + } else if ret_sym.kind == .map { + g.writeln('($shared_styp*)__dup_shared_map(&($shared_styp){.mtx = {0}, .val =') + } else { + g.writeln('($shared_styp*)__dup${shared_styp}(&($shared_styp){.mtx = {0}, .val =') + } + } + g.call_expr(node) + // if g.fileis('1.strings') { + // println('before:' + node.autofree_pregen) + // } + if g.is_autofree && !g.is_builtin_mod && !g.is_js_call && g.strs_to_free0.len == 0 + && !g.inside_lambda { // && g.inside_ternary == + // if len != 0, that means we are handling call expr inside call expr (arg) + // and it'll get messed up here, since it's handled recursively in autofree_call_pregen() + // so just skip it + g.autofree_call_pregen(node) + if g.strs_to_free0.len > 0 { + g.insert_before_stmt(g.strs_to_free0.join('\n') + '/* inserted before */') + } + g.strs_to_free0 = [] + // println('pos=$node.pos.pos') + } + if g.is_shared && !ret_type.has_flag(.shared_f) { + g.writeln('}, sizeof($shared_styp))') + } + // if g.autofree && node.autofree_pregen != '' { // g.strs_to_free0.len != 0 { + /* + if g.autofree { + s := g.autofree_pregen[node.pos.pos.str()] + if s != '' { + // g.insert_before_stmt('/*START2*/' + g.strs_to_free0.join('\n') + '/*END*/') + // g.insert_before_stmt('/*START3*/' + node.autofree_pregen + '/*END*/') + g.insert_before_stmt('/*START3*/' + s + '/*END*/') + // for s in g.strs_to_free0 { + } + // //g.writeln(s) + // } + g.strs_to_free0 = [] + } + */ + } + ast.CastExpr { + g.cast_expr(node) + } + ast.ChanInit { + elem_typ_str := g.typ(node.elem_type) + noscan := g.check_noscan(node.elem_type) + g.write('sync__new_channel_st${noscan}(') + if node.has_cap { + g.expr(node.cap_expr) + } else { + g.write('0') + } + g.write(', sizeof(') + g.write(elem_typ_str) + g.write('))') + } + ast.CharLiteral { + if node.val == r'\`' { + g.write("'`'") + } else { + // TODO: optimize use L-char instead of u32 when possible + if utf8_str_len(node.val) < node.val.len { + g.write('((rune)0x$node.val.utf32_code().hex() /* `$node.val` */)') + } else { + g.write("'$node.val'") + } + } + } + ast.DumpExpr { + g.dump_expr(node) + } + ast.AtExpr { + g.comp_at(node) + } + ast.ComptimeCall { + g.comptime_call(node) + } + ast.ComptimeSelector { + g.comptime_selector(node) + } + ast.Comment {} + ast.ConcatExpr { + g.concat_expr(node) + } + ast.CTempVar { + // g.write('/*ctmp .orig: $node.orig.str() , ._typ: $node.typ, .is_ptr: $node.is_ptr */ ') + g.write(node.name) + } + ast.EnumVal { + // g.write('${it.mod}${it.enum_name}_$it.val') + // g.enum_expr(node) + styp := g.typ(node.typ) + g.write('${styp}__$node.val') + } + ast.FloatLiteral { + g.write(node.val) + } + ast.GoExpr { + g.go_expr(node) + } + ast.Ident { + g.ident(node) + } + ast.IfExpr { + g.if_expr(node) + } + ast.IfGuardExpr { + g.write('/* guard */') + } + ast.IndexExpr { + g.index_expr(node) + } + ast.InfixExpr { + if node.op in [.left_shift, .plus_assign, .minus_assign] { + g.inside_map_infix = true + g.infix_expr(node) + g.inside_map_infix = false + } else { + g.infix_expr(node) + } + } + ast.IntegerLiteral { + if node.val.starts_with('0o') { + g.write('0') + g.write(node.val[2..]) + } else if node.val.starts_with('-0o') { + g.write('-0') + g.write(node.val[3..]) + } else { + g.write(node.val) // .int().str()) + } + } + ast.LockExpr { + g.lock_expr(node) + } + ast.MatchExpr { + g.match_expr(node) + } + ast.MapInit { + g.map_init(node) + } + ast.NodeError {} + ast.None { + g.write('_const_none__') + } + ast.OrExpr { + // this should never appear here + } + ast.ParExpr { + g.write('(') + g.expr(node.expr) + g.write(')') + } + ast.PostfixExpr { + if node.auto_locked != '' { + g.writeln('sync__RwMutex_lock(&$node.auto_locked->mtx);') + } + g.inside_map_postfix = true + if node.expr.is_auto_deref_var() { + g.write('(*') + g.expr(node.expr) + g.write(')') + } else { + g.expr(node.expr) + } + g.inside_map_postfix = false + g.write(node.op.str()) + if node.auto_locked != '' { + g.writeln(';') + g.write('sync__RwMutex_unlock(&$node.auto_locked->mtx)') + } + } + ast.PrefixExpr { + gen_or := node.op == .arrow && (node.or_block.kind != .absent || node.is_option) + if node.op == .amp { + g.is_amp = true + } + if node.op == .arrow { + styp := g.typ(node.right_type) + right_sym := g.table.get_type_symbol(node.right_type) + mut right_inf := right_sym.info as ast.Chan + elem_type := right_inf.elem_type + is_gen_or_and_assign_rhs := gen_or && !g.discard_or_result + cur_line := if is_gen_or_and_assign_rhs { + line := g.go_before_stmt(0) + g.out.write_string(util.tabs(g.indent)) + line + } else { + '' + } + tmp_opt := if gen_or { g.new_tmp_var() } else { '' } + if gen_or { + opt_elem_type := g.typ(elem_type.set_flag(.optional)) + g.register_chan_pop_optional_call(opt_elem_type, styp) + g.write('$opt_elem_type $tmp_opt = __Option_${styp}_popval(') + } else { + g.write('__${styp}_popval(') + } + g.expr(node.right) + g.write(')') + if gen_or { + if !node.is_option { + g.or_block(tmp_opt, node.or_block, elem_type) + } + if is_gen_or_and_assign_rhs { + elem_styp := g.typ(elem_type) + g.write(';\n$cur_line*($elem_styp*)${tmp_opt}.data') + } + } + } else { + // g.write('/*pref*/') + if !(g.is_amp && node.right.is_auto_deref_var()) { + g.write(node.op.str()) + } + // g.write('(') + g.expr(node.right) + } + g.is_amp = false + } + ast.RangeExpr { + // Only used in IndexExpr + } + ast.SelectExpr { + g.select_expr(node) + } + ast.SizeOf { + typ := if node.typ == g.field_data_type { g.comp_for_field_value.typ } else { node.typ } + node_typ := g.unwrap_generic(typ) + sym := g.table.get_type_symbol(node_typ) + if sym.language == .v && sym.kind in [.placeholder, .any] { + g.error('unknown type `$sym.name`', node.pos) + } + styp := g.typ(node_typ) + g.write('/*SizeOf*/ sizeof(${util.no_dots(styp)})') + } + ast.IsRefType { + typ := if node.typ == g.field_data_type { g.comp_for_field_value.typ } else { node.typ } + node_typ := g.unwrap_generic(typ) + sym := g.table.get_type_symbol(node_typ) + if sym.language == .v && sym.kind in [.placeholder, .any] { + g.error('unknown type `$sym.name`', node.pos) + } + is_ref_type := g.contains_ptr(node_typ) + g.write('/*IsRefType*/ $is_ref_type') + } + ast.OffsetOf { + styp := g.typ(node.struct_type) + g.write('/*OffsetOf*/ (u32)(__offsetof(${util.no_dots(styp)}, $node.field))') + } + ast.SqlExpr { + g.sql_select_expr(node) + } + ast.StringLiteral { + g.string_literal(node) + } + ast.StringInterLiteral { + g.string_inter_literal(node) + } + ast.StructInit { + if node.unresolved { + g.expr(ast.resolve_init(node, g.unwrap_generic(node.typ), g.table)) + } else { + // `user := User{name: 'Bob'}` + g.struct_init(node) + } + } + ast.SelectorExpr { + g.selector_expr(node) + } + ast.TypeNode { + // match sum Type + // g.write('/* Type */') + // type_idx := node.typ.idx() + typ := g.unwrap_generic(node.typ) + sym := g.table.get_type_symbol(typ) + sidx := g.type_sidx(typ) + // g.write('$type_idx /* $sym.name */') + g.write('$sidx /* $sym.name */') + } + ast.TypeOf { + g.typeof_expr(node) + } + ast.Likely { + if node.is_likely { + g.write('_likely_') + } else { + g.write('_unlikely_') + } + g.write('(') + g.expr(node.expr) + g.write(')') + } + ast.UnsafeExpr { + g.expr(node.expr) + } + } + g.discard_or_result = old_discard_or_result + g.is_void_expr_stmt = old_is_void_expr_stmt +} + +// T.name, typeof(expr).name +fn (mut g Gen) type_name(raw_type ast.Type) { + typ := if raw_type == g.field_data_type { g.comp_for_field_value.typ } else { raw_type } + sym := g.table.get_type_symbol(typ) + mut s := '' + if sym.kind == .function { + if typ.is_ptr() { + s = '&' + g.fn_decl_str(sym.info as ast.FnType) + } else { + s = g.fn_decl_str(sym.info as ast.FnType) + } + } else { + s = g.table.type_to_str(g.unwrap_generic(typ)) + } + g.write('_SLIT("${util.strip_main_name(s)}")') +} + +fn (mut g Gen) typeof_expr(node ast.TypeOf) { + typ := if node.expr_type == g.field_data_type { + g.comp_for_field_value.typ + } else { + node.expr_type + } + sym := g.table.get_type_symbol(typ) + if sym.kind == .sum_type { + // When encountering a .sum_type, typeof() should be done at runtime, + // because the subtype of the expression may change: + g.write('tos3( /* $sym.name */ v_typeof_sumtype_${sym.cname}( (') + g.expr(node.expr) + g.write(')._typ ))') + } else if sym.kind == .array_fixed { + fixed_info := sym.info as ast.ArrayFixed + typ_name := g.table.get_type_name(fixed_info.elem_type) + g.write('_SLIT("[$fixed_info.size]${util.strip_main_name(typ_name)}")') + } else if sym.kind == .function { + info := sym.info as ast.FnType + g.write('_SLIT("${g.fn_decl_str(info)}")') + } else if typ.has_flag(.variadic) { + varg_elem_type_sym := g.table.get_type_symbol(g.table.value_type(typ)) + g.write('_SLIT("...${util.strip_main_name(varg_elem_type_sym.name)}")') + } else { + x := g.table.type_to_str(typ) + y := util.strip_main_name(x) + g.write('_SLIT("$y")') + } +} + +fn (mut g Gen) selector_expr(node ast.SelectorExpr) { + prevent_sum_type_unwrapping_once := g.prevent_sum_type_unwrapping_once + g.prevent_sum_type_unwrapping_once = false + if node.name_type > 0 { + g.type_name(node.name_type) + return + } + if node.expr_type == 0 { + g.checker_bug('unexpected SelectorExpr.expr_type = 0', node.pos) + } + sym := g.table.get_type_symbol(g.unwrap_generic(node.expr_type)) + // if node expr is a root ident and an optional + mut is_optional := node.expr is ast.Ident && node.expr_type.has_flag(.optional) + if is_optional { + opt_base_typ := g.base_type(node.expr_type) + g.writeln('(*($opt_base_typ*)') + } + if sym.kind in [.interface_, .sum_type] { + g.write('(*(') + } + if sym.kind == .array_fixed { + if node.field_name != 'len' { + g.error('field_name should be `len`', node.pos) + } + info := sym.info as ast.ArrayFixed + g.write('$info.size') + return + } + if sym.kind == .chan && (node.field_name == 'len' || node.field_name == 'closed') { + g.write('sync__Channel_${node.field_name}(') + g.expr(node.expr) + g.write(')') + return + } + mut sum_type_deref_field := '' + mut sum_type_dot := '.' + if f := g.table.find_field(sym, node.field_name) { + field_sym := g.table.get_type_symbol(f.typ) + if field_sym.kind in [.sum_type, .interface_] { + if !prevent_sum_type_unwrapping_once { + // check first if field is sum type because scope searching is expensive + scope := g.file.scope.innermost(node.pos.pos) + if field := scope.find_struct_field(node.expr.str(), node.expr_type, node.field_name) { + if field.orig_type.is_ptr() { + sum_type_dot = '->' + } + for i, typ in field.smartcasts { + g.write('(') + if field_sym.kind == .sum_type { + g.write('*') + } + cast_sym := g.table.get_type_symbol(typ) + if i != 0 { + dot := if field.typ.is_ptr() { '->' } else { '.' } + sum_type_deref_field += ')$dot' + } + if mut cast_sym.info is ast.Aggregate { + agg_sym := g.table.get_type_symbol(cast_sym.info.types[g.aggregate_type_idx]) + sum_type_deref_field += '_$agg_sym.cname' + } else { + sum_type_deref_field += '_$cast_sym.cname' + } + } + } + } + } + } + n_ptr := node.expr_type.nr_muls() - 1 + if n_ptr > 0 { + g.write('(') + g.write('*'.repeat(n_ptr)) + g.expr(node.expr) + g.write(')') + } else { + g.expr(node.expr) + } + if is_optional { + g.write('.data)') + } + // struct embedding + if sym.info is ast.Struct || sym.info is ast.Aggregate { + if node.from_embed_type != 0 { + embed_sym := g.table.get_type_symbol(node.from_embed_type) + embed_name := embed_sym.embed_name() + if node.expr_type.is_ptr() { + g.write('->') + } else { + g.write('.') + } + g.write(embed_name) + } + } + if (node.expr_type.is_ptr() || sym.kind == .chan) && node.from_embed_type == 0 { + g.write('->') + } else { + // g.write('. /*typ= $it.expr_type */') // ${g.typ(it.expr_type)} /') + g.write('.') + } + if node.expr_type.has_flag(.shared_f) { + g.write('val.') + } + if node.expr_type == 0 { + verror('cgen: SelectorExpr | expr_type: 0 | it.expr: `$node.expr` | field: `$node.field_name` | file: $g.file.path | line: $node.pos.line_nr') + } + field_name := if sym.language == .v { c_name(node.field_name) } else { node.field_name } + g.write(field_name) + if sum_type_deref_field != '' { + g.write('$sum_type_dot$sum_type_deref_field)') + } + if sym.kind in [.interface_, .sum_type] { + g.write('))') + } +} + +fn (mut g Gen) enum_expr(node ast.Expr) { + match node { + ast.EnumVal { + g.write(node.val) + } + else { + g.expr(node) + } + } +} + +fn (mut g Gen) lock_expr(node ast.LockExpr) { + g.cur_lock = unsafe { node } // is ok because it is discarded at end of fn + defer { + g.cur_lock = ast.LockExpr{ + scope: 0 + } + } + tmp_result := if node.is_expr { g.new_tmp_var() } else { '' } + mut cur_line := '' + if node.is_expr { + styp := g.typ(node.typ) + cur_line = g.go_before_stmt(0) + g.writeln('$styp $tmp_result;') + } + mut mtxs := '' + if node.lockeds.len == 0 { + // this should not happen + } else if node.lockeds.len == 1 { + lock_prefix := if node.is_rlock[0] { 'r' } else { '' } + g.write('sync__RwMutex_${lock_prefix}lock(&') + g.expr(node.lockeds[0]) + g.writeln('->mtx);') + } else { + mtxs = g.new_tmp_var() + g.writeln('uintptr_t _arr_$mtxs[$node.lockeds.len];') + g.writeln('bool _isrlck_$mtxs[$node.lockeds.len];') + mut j := 0 + for i, is_rlock in node.is_rlock { + if !is_rlock { + g.write('_arr_$mtxs[$j] = (uintptr_t)&') + g.expr(node.lockeds[i]) + g.writeln('->mtx;') + g.writeln('_isrlck_$mtxs[$j] = false;') + j++ + } + } + for i, is_rlock in node.is_rlock { + if is_rlock { + g.write('_arr_$mtxs[$j] = (uintptr_t)&') + g.expr(node.lockeds[i]) + g.writeln('->mtx;') + g.writeln('_isrlck_$mtxs[$j] = true;') + j++ + } + } + if node.lockeds.len == 2 { + g.writeln('if (_arr_$mtxs[0] > _arr_$mtxs[1]) {') + g.writeln('\tuintptr_t _ptr_$mtxs = _arr_$mtxs[0];') + g.writeln('\t_arr_$mtxs[0] = _arr_$mtxs[1];') + g.writeln('\t_arr_$mtxs[1] = _ptr_$mtxs;') + g.writeln('\tbool _bool_$mtxs = _isrlck_$mtxs[0];') + g.writeln('\t_isrlck_$mtxs[0] = _isrlck_$mtxs[1];') + g.writeln('\t_isrlck_$mtxs[1] = _bool_$mtxs;') + g.writeln('}') + } else { + g.writeln('__sort_ptr(_arr_$mtxs, _isrlck_$mtxs, $node.lockeds.len);') + } + g.writeln('for (int $mtxs=0; $mtxs<$node.lockeds.len; $mtxs++) {') + g.writeln('\tif ($mtxs && _arr_$mtxs[$mtxs] == _arr_$mtxs[$mtxs-1]) continue;') + g.writeln('\tif (_isrlck_$mtxs[$mtxs])') + g.writeln('\t\tsync__RwMutex_rlock((sync__RwMutex*)_arr_$mtxs[$mtxs]);') + g.writeln('\telse') + g.writeln('\t\tsync__RwMutex_lock((sync__RwMutex*)_arr_$mtxs[$mtxs]);') + g.writeln('}') + } + g.mtxs = mtxs + defer { + g.mtxs = '' + } + g.writeln('/*lock*/ {') + g.stmts_with_tmp_var(node.stmts, tmp_result) + if node.is_expr { + g.writeln(';') + } + g.writeln('}') + g.unlock_locks() + if node.is_expr { + g.writeln('') + g.write(cur_line) + g.write('$tmp_result') + } +} + +fn (mut g Gen) unlock_locks() { + if g.cur_lock.lockeds.len == 0 { + } else if g.cur_lock.lockeds.len == 1 { + lock_prefix := if g.cur_lock.is_rlock[0] { 'r' } else { '' } + g.write('sync__RwMutex_${lock_prefix}unlock(&') + g.expr(g.cur_lock.lockeds[0]) + g.write('->mtx);') + } else { + g.writeln('for (int $g.mtxs=${g.cur_lock.lockeds.len - 1}; $g.mtxs>=0; $g.mtxs--) {') + g.writeln('\tif ($g.mtxs && _arr_$g.mtxs[$g.mtxs] == _arr_$g.mtxs[$g.mtxs-1]) continue;') + g.writeln('\tif (_isrlck_$g.mtxs[$g.mtxs])') + g.writeln('\t\tsync__RwMutex_runlock((sync__RwMutex*)_arr_$g.mtxs[$g.mtxs]);') + g.writeln('\telse') + g.writeln('\t\tsync__RwMutex_unlock((sync__RwMutex*)_arr_$g.mtxs[$g.mtxs]);') + g.write('}') + } +} + +fn (mut g Gen) need_tmp_var_in_match(node ast.MatchExpr) bool { + if node.is_expr && node.return_type != ast.void_type && node.return_type != 0 { + sym := g.table.get_type_symbol(node.return_type) + if sym.kind == .multi_return { + return false + } + for branch in node.branches { + if branch.stmts.len > 1 { + return true + } + if branch.stmts.len == 1 { + if branch.stmts[0] is ast.ExprStmt { + stmt := branch.stmts[0] as ast.ExprStmt + if stmt.expr is ast.CallExpr || stmt.expr is ast.IfExpr + || stmt.expr is ast.MatchExpr || (stmt.expr is ast.IndexExpr + && (stmt.expr as ast.IndexExpr).or_expr.kind != .absent) { + return true + } + } + } + } + } + return false +} + +fn (mut g Gen) match_expr(node ast.MatchExpr) { + // println('match expr typ=$it.expr_type') + // TODO + if node.cond_type == 0 { + g.writeln('// match 0') + return + } + need_tmp_var := g.need_tmp_var_in_match(node) + is_expr := (node.is_expr && node.return_type != ast.void_type) || g.inside_ternary > 0 + mut cond_var := '' + mut tmp_var := '' + mut cur_line := '' + if is_expr && !need_tmp_var { + g.inside_ternary++ + } + if node.cond is ast.Ident || node.cond is ast.SelectorExpr || node.cond is ast.IntegerLiteral + || node.cond is ast.StringLiteral || node.cond is ast.FloatLiteral { + cond_var = g.expr_string(node.cond) + } else { + line := if is_expr { + g.empty_line = true + g.go_before_stmt(0) + } else { + '' + } + cond_var = g.new_tmp_var() + g.write('${g.typ(node.cond_type)} $cond_var = ') + g.expr(node.cond) + g.writeln(';') + g.stmt_path_pos << g.out.len + g.write(line) + } + if need_tmp_var { + g.empty_line = true + cur_line = g.go_before_stmt(0).trim_left(' \t') + tmp_var = g.new_tmp_var() + g.writeln('${g.typ(node.return_type)} $tmp_var = ${g.type_default(node.return_type)};') + } + + if is_expr && !need_tmp_var { + // brackets needed otherwise '?' will apply to everything on the left + g.write('(') + } + if node.is_sum_type { + g.match_expr_sumtype(node, is_expr, cond_var, tmp_var) + } else { + g.match_expr_classic(node, is_expr, cond_var, tmp_var) + } + g.write(cur_line) + if need_tmp_var { + g.write('$tmp_var') + } + if is_expr && !need_tmp_var { + g.write(')') + g.decrement_inside_ternary() + } +} + +fn (mut g Gen) match_expr_sumtype(node ast.MatchExpr, is_expr bool, cond_var string, tmp_var string) { + for j, branch in node.branches { + mut sumtype_index := 0 + // iterates through all types in sumtype branches + for { + g.aggregate_type_idx = sumtype_index + is_last := j == node.branches.len - 1 + sym := g.table.get_type_symbol(node.cond_type) + if branch.is_else || (node.is_expr && is_last && tmp_var.len == 0) { + if is_expr && tmp_var.len == 0 { + // TODO too many branches. maybe separate ?: matches + g.write(' : ') + } else { + g.writeln('') + g.write_v_source_line_info(branch.pos) + g.writeln('else {') + } + } else { + if j > 0 || sumtype_index > 0 { + if is_expr && tmp_var.len == 0 { + g.write(' : ') + } else { + g.write_v_source_line_info(branch.pos) + g.write('else ') + } + } + if is_expr && tmp_var.len == 0 { + g.write('(') + } else { + if j == 0 && sumtype_index == 0 { + g.empty_line = true + } + g.write_v_source_line_info(branch.pos) + g.write('if (') + } + g.write(cond_var) + dot_or_ptr := if node.cond_type.is_ptr() { '->' } else { '.' } + if sym.kind == .sum_type { + g.write('${dot_or_ptr}_typ == ') + g.expr(branch.exprs[sumtype_index]) + } else if sym.kind == .interface_ { + if branch.exprs[sumtype_index] is ast.TypeNode { + typ := branch.exprs[sumtype_index] as ast.TypeNode + branch_sym := g.table.get_type_symbol(g.unwrap_generic(typ.typ)) + g.write('${dot_or_ptr}_typ == _${sym.cname}_${branch_sym.cname}_index') + } else if branch.exprs[sumtype_index] is ast.None && sym.name == 'IError' { + g.write('${dot_or_ptr}_typ == _IError_None___index') + } + } + if is_expr && tmp_var.len == 0 { + g.write(') ? ') + } else { + g.writeln(') {') + } + } + if is_expr && tmp_var.len > 0 + && g.table.get_type_symbol(node.return_type).kind == .sum_type { + g.expected_cast_type = node.return_type + } + g.stmts_with_tmp_var(branch.stmts, tmp_var) + g.expected_cast_type = 0 + if g.inside_ternary == 0 { + g.writeln('}') + g.stmt_path_pos << g.out.len + } + sumtype_index++ + if branch.exprs.len == 0 || sumtype_index == branch.exprs.len { + break + } + } + // reset global field for next use + g.aggregate_type_idx = 0 + } +} + +fn (mut g Gen) match_expr_classic(node ast.MatchExpr, is_expr bool, cond_var string, tmp_var string) { + type_sym := g.table.get_type_symbol(node.cond_type) + for j, branch in node.branches { + is_last := j == node.branches.len - 1 + if branch.is_else || (node.is_expr && is_last && tmp_var.len == 0) { + if node.branches.len > 1 { + if is_expr && tmp_var.len == 0 { + // TODO too many branches. maybe separate ?: matches + g.write(' : ') + } else { + g.writeln('') + g.write_v_source_line_info(branch.pos) + g.writeln('else {') + } + } + } else { + if j > 0 { + if is_expr && tmp_var.len == 0 { + g.write(' : ') + } else { + g.writeln('') + g.write_v_source_line_info(branch.pos) + g.write('else ') + } + } + if is_expr && tmp_var.len == 0 { + g.write('(') + } else { + if j == 0 { + g.writeln('') + } + g.write_v_source_line_info(branch.pos) + g.write('if (') + } + for i, expr in branch.exprs { + if i > 0 { + g.write(' || ') + } + match type_sym.kind { + .array { + ptr_typ := g.gen_array_equality_fn(node.cond_type) + g.write('${ptr_typ}_arr_eq($cond_var, ') + g.expr(expr) + g.write(')') + } + .array_fixed { + ptr_typ := g.gen_fixed_array_equality_fn(node.cond_type) + g.write('${ptr_typ}_arr_eq($cond_var, ') + g.expr(expr) + g.write(')') + } + .map { + ptr_typ := g.gen_map_equality_fn(node.cond_type) + g.write('${ptr_typ}_map_eq($cond_var, ') + g.expr(expr) + g.write(')') + } + .string { + g.write('string__eq($cond_var, ') + g.expr(expr) + g.write(')') + } + .struct_ { + ptr_typ := g.gen_struct_equality_fn(node.cond_type) + g.write('${ptr_typ}_struct_eq($cond_var, ') + g.expr(expr) + g.write(')') + } + else { + if expr is ast.RangeExpr { + // if type is unsigned and low is 0, check is unneeded + mut skip_low := false + if expr.low is ast.IntegerLiteral { + if node.cond_type in [ast.u16_type, ast.u32_type, ast.u64_type] + && expr.low.val == '0' { + skip_low = true + } + } + g.write('(') + if !skip_low { + g.write('$cond_var >= ') + g.expr(expr.low) + g.write(' && ') + } + g.write('$cond_var <= ') + g.expr(expr.high) + g.write(')') + } else { + g.write('$cond_var == (') + g.expr(expr) + g.write(')') + } + } + } + } + if is_expr && tmp_var.len == 0 { + g.write(') ? ') + } else { + g.writeln(') {') + } + } + g.stmts_with_tmp_var(branch.stmts, tmp_var) + if g.inside_ternary == 0 && node.branches.len >= 1 { + g.write('}') + } + } +} + +fn (mut g Gen) map_init(node ast.MapInit) { + key_typ_str := g.typ(node.key_type) + value_typ_str := g.typ(node.value_type) + value_typ := g.table.get_type_symbol(node.value_type) + key_typ := g.table.get_final_type_symbol(node.key_type) + hash_fn, key_eq_fn, clone_fn, free_fn := g.map_fn_ptrs(key_typ) + size := node.vals.len + mut shared_styp := '' // only needed for shared &[]{...} + mut styp := '' + is_amp := g.is_amp + g.is_amp = false + if is_amp { + g.out.go_back(1) // delete the `&` already generated in `prefix_expr() + } + if g.is_shared { + mut shared_typ := node.typ.set_flag(.shared_f) + shared_styp = g.typ(shared_typ) + g.writeln('($shared_styp*)__dup_shared_map(&($shared_styp){.mtx = {0}, .val =') + } else if is_amp { + styp = g.typ(node.typ) + g.write('($styp*)memdup(ADDR($styp, ') + } + noscan_key := g.check_noscan(node.key_type) + noscan_value := g.check_noscan(node.value_type) + mut noscan := if noscan_key.len != 0 || noscan_value.len != 0 { '_noscan' } else { '' } + if noscan.len != 0 { + if noscan_key.len != 0 { + noscan += '_key' + } + if noscan_value.len != 0 { + noscan += '_value' + } + } + if size > 0 { + if value_typ.kind == .function { + g.write('new_map_init${noscan}($hash_fn, $key_eq_fn, $clone_fn, $free_fn, $size, sizeof($key_typ_str), sizeof(voidptr), _MOV(($key_typ_str[$size]){') + } else { + g.write('new_map_init${noscan}($hash_fn, $key_eq_fn, $clone_fn, $free_fn, $size, sizeof($key_typ_str), sizeof($value_typ_str), _MOV(($key_typ_str[$size]){') + } + for expr in node.keys { + g.expr(expr) + g.write(', ') + } + if value_typ.kind == .function { + g.write('}), _MOV((voidptr[$size]){') + } else { + g.write('}), _MOV(($value_typ_str[$size]){') + } + for expr in node.vals { + if expr.is_auto_deref_var() { + g.write('*') + } + g.expr(expr) + g.write(', ') + } + g.write('}))') + } else { + g.write('new_map${noscan}(sizeof($key_typ_str), sizeof($value_typ_str), $hash_fn, $key_eq_fn, $clone_fn, $free_fn)') + } + if g.is_shared { + g.write('}, sizeof($shared_styp))') + } else if is_amp { + g.write('), sizeof($styp))') + } +} + +fn (mut g Gen) select_expr(node ast.SelectExpr) { + is_expr := node.is_expr || g.inside_ternary > 0 + cur_line := if is_expr { + g.empty_line = true + g.go_before_stmt(0) + } else { + '' + } + n_channels := if node.has_exception { node.branches.len - 1 } else { node.branches.len } + mut channels := []ast.Expr{cap: n_channels} + mut objs := []ast.Expr{cap: n_channels} + mut tmp_objs := []string{cap: n_channels} + mut elem_types := []string{cap: n_channels} + mut is_push := []bool{cap: n_channels} + mut has_else := false + mut has_timeout := false + mut timeout_expr := ast.empty_expr() + mut exception_branch := -1 + for j, branch in node.branches { + if branch.is_else { + has_else = true + exception_branch = j + } else if branch.is_timeout { + has_timeout = true + exception_branch = j + timeout_expr = (branch.stmt as ast.ExprStmt).expr + } else { + match branch.stmt { + ast.ExprStmt { + // send expression + expr := branch.stmt.expr as ast.InfixExpr + channels << expr.left + if expr.right is ast.Ident || expr.right is ast.IndexExpr + || expr.right is ast.SelectorExpr || expr.right is ast.StructInit { + // addressable objects in the `C` output + objs << expr.right + tmp_objs << '' + elem_types << '' + } else { + // must be evaluated to tmp var before real `select` is performed + objs << ast.empty_expr() + tmp_obj := g.new_tmp_var() + tmp_objs << tmp_obj + el_stype := g.typ(g.table.mktyp(expr.right_type)) + g.writeln('$el_stype $tmp_obj;') + } + is_push << true + } + ast.AssignStmt { + rec_expr := branch.stmt.right[0] as ast.PrefixExpr + channels << rec_expr.right + is_push << false + // create tmp unless the object with *exactly* the type we need exists already + if branch.stmt.op == .decl_assign + || branch.stmt.right_types[0] != branch.stmt.left_types[0] { + tmp_obj := g.new_tmp_var() + tmp_objs << tmp_obj + el_stype := g.typ(branch.stmt.right_types[0]) + elem_types << if branch.stmt.op == .decl_assign { + el_stype + ' ' + } else { + '' + } + g.writeln('$el_stype $tmp_obj;') + } else { + tmp_objs << '' + elem_types << '' + } + objs << branch.stmt.left[0] + } + else {} + } + } + } + chan_array := g.new_tmp_var() + g.write('Array_sync__Channel_ptr $chan_array = new_array_from_c_array($n_channels, $n_channels, sizeof(sync__Channel*), _MOV((sync__Channel*[$n_channels]){') + for i in 0 .. n_channels { + if i > 0 { + g.write(', ') + } + g.write('(sync__Channel*)(') + g.expr(channels[i]) + g.write(')') + } + g.writeln('}));\n') + directions_array := g.new_tmp_var() + g.write('Array_sync__Direction $directions_array = new_array_from_c_array($n_channels, $n_channels, sizeof(sync__Direction), _MOV((sync__Direction[$n_channels]){') + for i in 0 .. n_channels { + if i > 0 { + g.write(', ') + } + if is_push[i] { + g.write('sync__Direction__push') + } else { + g.write('sync__Direction__pop') + } + } + g.writeln('}));\n') + objs_array := g.new_tmp_var() + g.write('Array_voidptr $objs_array = new_array_from_c_array($n_channels, $n_channels, sizeof(voidptr), _MOV((voidptr[$n_channels]){') + for i in 0 .. n_channels { + if i > 0 { + g.write(', &') + } else { + g.write('&') + } + if tmp_objs[i] == '' { + g.expr(objs[i]) + } else { + g.write(tmp_objs[i]) + } + } + g.writeln('}));\n') + select_result := g.new_tmp_var() + g.write('int $select_result = sync__channel_select(&/*arr*/$chan_array, $directions_array, &/*arr*/$objs_array, ') + if has_timeout { + g.expr(timeout_expr) + } else if has_else { + g.write('0') + } else { + g.write('_const_time__infinite') + } + g.writeln(');') + // free the temps that were created + g.writeln('array_free(&$objs_array);') + g.writeln('array_free(&$directions_array);') + g.writeln('array_free(&$chan_array);') + mut i := 0 + for j in 0 .. node.branches.len { + if j > 0 { + g.write('} else ') + } + g.write('if ($select_result == ') + if j == exception_branch { + g.writeln('-1) {') + } else { + g.writeln('$i) {') + if !is_push[i] && tmp_objs[i] != '' { + g.write('\t${elem_types[i]}') + g.expr(objs[i]) + g.writeln(' = ${tmp_objs[i]};') + } + i++ + } + g.stmts(node.branches[j].stmts) + } + g.writeln('}') + if is_expr { + g.empty_line = false + g.write(cur_line) + g.write('($select_result != -2)') + } +} + +fn (mut g Gen) ident(node ast.Ident) { + prevent_sum_type_unwrapping_once := g.prevent_sum_type_unwrapping_once + g.prevent_sum_type_unwrapping_once = false + if node.name == 'lld' { + return + } + if node.name.starts_with('C.') { + g.write(util.no_dots(node.name[2..])) + return + } + if node.kind == .constant { // && !node.name.starts_with('g_') { + // TODO globals hack + g.write('_const_') + } + mut name := c_name(node.name) + // TODO: temporary, remove this + node_info := node.info + mut is_auto_heap := false + if node_info is ast.IdentVar { + // x ?int + // `x = 10` => `x.data = 10` (g.right_is_opt == false) + // `x = new_opt()` => `x = new_opt()` (g.right_is_opt == true) + // `println(x)` => `println(*(int*)x.data)` + if node_info.is_optional && !(g.is_assign_lhs && g.right_is_opt) { + g.write('/*opt*/') + styp := g.base_type(node_info.typ) + g.write('(*($styp*)${name}.data)') + return + } + if !g.is_assign_lhs && node_info.share == .shared_t { + g.write('${name}.val') + return + } + scope := g.file.scope.innermost(node.pos.pos) + if v := scope.find_var(node.name) { + is_auto_heap = v.is_auto_heap && (!g.is_assign_lhs || g.assign_op != .decl_assign) + if is_auto_heap { + g.write('(*(') + } + if v.smartcasts.len > 0 { + v_sym := g.table.get_type_symbol(v.typ) + if !prevent_sum_type_unwrapping_once { + for _ in v.smartcasts { + g.write('(') + if v_sym.kind == .sum_type && !is_auto_heap { + g.write('*') + } + } + for i, typ in v.smartcasts { + cast_sym := g.table.get_type_symbol(g.unwrap_generic(typ)) + mut is_ptr := false + if i == 0 { + g.write(name) + if v.orig_type.is_ptr() { + is_ptr = true + } + } + dot := if is_ptr || is_auto_heap { '->' } else { '.' } + if mut cast_sym.info is ast.Aggregate { + sym := g.table.get_type_symbol(cast_sym.info.types[g.aggregate_type_idx]) + g.write('${dot}_$sym.cname') + } else { + g.write('${dot}_$cast_sym.cname') + } + g.write(')') + } + if is_auto_heap { + g.write('))') + } + return + } + } + } + } else if node_info is ast.IdentFn { + if g.pref.obfuscate && g.cur_mod.name == 'main' && name.starts_with('main__') { + key := node.name + g.write('/* obf identfn: $key */') + name = g.obf_table[key] or { + panic('cgen: obf name "$key" not found, this should never happen') + } + } + } + g.write(g.get_ternary_name(name)) + if is_auto_heap { + g.write('))') + } +} + +fn (mut g Gen) cast_expr(node ast.CastExpr) { + if g.is_amp { + // &Foo(0) => ((Foo*)0) + g.out.go_back(1) + } + g.is_amp = false + sym := g.table.get_type_symbol(node.typ) + if sym.kind == .string && !node.typ.is_ptr() { + // `string(x)` needs `tos()`, but not `&string(x) + // `tos(str, len)`, `tos2(str)` + if node.has_arg { + g.write('tos((byteptr)') + } else { + g.write('tos2((byteptr)') + } + g.expr(node.expr) + expr_sym := g.table.get_type_symbol(node.expr_type) + if expr_sym.kind == .array { + // if we are casting an array, we need to add `.data` + g.write('.data') + } + if node.has_arg { + // len argument + g.write(', ') + g.expr(node.arg) + } + g.write(')') + } else if sym.kind in [.sum_type, .interface_] { + g.expr_with_cast(node.expr, node.expr_type, node.typ) + } else if sym.kind == .struct_ && !node.typ.is_ptr() && !(sym.info as ast.Struct).is_typedef { + // deprecated, replaced by Struct{...exr} + styp := g.typ(node.typ) + g.write('*(($styp *)(&') + g.expr(node.expr) + g.write('))') + } else if sym.kind == .alias && g.table.get_final_type_symbol(node.typ).kind == .array_fixed { + g.expr(node.expr) + } else { + styp := g.typ(node.typ) + mut cast_label := '' + // `ast.string_type` is done for MSVC's bug + if sym.kind != .alias + || (sym.info as ast.Alias).parent_type !in [node.expr_type, ast.string_type] { + cast_label = '($styp)' + } + if node.typ.has_flag(.optional) && node.expr is ast.None { + g.gen_optional_error(node.typ, node.expr) + } else { + g.write('(${cast_label}(') + g.expr(node.expr) + if node.expr is ast.IntegerLiteral { + if node.typ in [ast.u64_type, ast.u32_type, ast.u16_type] { + if !node.expr.val.starts_with('-') { + g.write('U') + } + } + } + g.write('))') + } + } +} + +fn (mut g Gen) concat_expr(node ast.ConcatExpr) { + styp := g.typ(node.return_type) + sym := g.table.get_type_symbol(node.return_type) + is_multi := sym.kind == .multi_return + if !is_multi { + g.expr(node.vals[0]) + } else { + g.write('($styp){') + for i, expr in node.vals { + g.write('.arg$i=') + g.expr(expr) + if i < node.vals.len - 1 { + g.write(',') + } + } + g.write('}') + } +} + +fn (mut g Gen) need_tmp_var_in_if(node ast.IfExpr) bool { + if node.is_expr && g.inside_ternary == 0 { + if g.is_autofree || node.typ.has_flag(.optional) { + return true + } + for branch in node.branches { + if branch.cond is ast.IfGuardExpr { + return true + } + if branch.stmts.len == 1 { + if branch.stmts[0] is ast.ExprStmt { + stmt := branch.stmts[0] as ast.ExprStmt + if stmt.expr is ast.CallExpr { + if stmt.expr.is_method { + left_sym := g.table.get_type_symbol(stmt.expr.receiver_type) + if left_sym.kind in [.array, .array_fixed, .map] { + return true + } + } + } + } + } + } + } + return false +} + +fn (mut g Gen) if_expr(node ast.IfExpr) { + if node.is_comptime { + g.comp_if(node) + return + } + // For simpe if expressions we can use C's `?:` + // `if x > 0 { 1 } else { 2 }` => `(x > 0) ? (1) : (2)` + // For if expressions with multiple statements or another if expression inside, it's much + // easier to use a temp var, than do C tricks with commas, introduce special vars etc + // (as it used to be done). + // Always use this in -autofree, since ?: can have tmp expressions that have to be freed. + needs_tmp_var := g.need_tmp_var_in_if(node) + tmp := if needs_tmp_var { g.new_tmp_var() } else { '' } + mut cur_line := '' + if needs_tmp_var { + if node.typ.has_flag(.optional) { + g.inside_if_optional = true + } + styp := g.typ(node.typ) + cur_line = g.go_before_stmt(0) + g.empty_line = true + g.writeln('$styp $tmp; /* if prepend */') + } else if node.is_expr || g.inside_ternary != 0 { + g.inside_ternary++ + g.write('(') + for i, branch in node.branches { + if i > 0 { + g.write(' : ') + } + if i < node.branches.len - 1 || !node.has_else { + g.expr(branch.cond) + g.write(' ? ') + } + g.stmts(branch.stmts) + } + if node.branches.len == 1 { + g.write(': 0') + } + g.write(')') + g.decrement_inside_ternary() + return + } + mut is_guard := false + mut guard_idx := 0 + mut guard_vars := []string{} + for i, branch in node.branches { + cond := branch.cond + if cond is ast.IfGuardExpr { + if !is_guard { + is_guard = true + guard_idx = i + guard_vars = []string{len: node.branches.len} + } + if cond.expr !is ast.IndexExpr && cond.expr !is ast.PrefixExpr { + var_name := g.new_tmp_var() + guard_vars[i] = var_name + g.writeln('${g.typ(cond.expr_type)} $var_name;') + } else { + guard_vars[i] = '' + } + } + } + for i, branch in node.branches { + if i > 0 { + g.write('} else ') + } + // if last branch is `else {` + if i == node.branches.len - 1 && node.has_else { + g.writeln('{') + // define `err` only for simple `if val := opt {...} else {` + if is_guard && guard_idx == i - 1 { + cvar_name := guard_vars[guard_idx] + g.writeln('\tIError err = ${cvar_name}.err;') + } + } else { + match branch.cond { + ast.IfGuardExpr { + mut var_name := guard_vars[i] + mut short_opt := false + if var_name == '' { + short_opt = true // we don't need a further tmp, so use the one we'll get later + var_name = g.new_tmp_var() + guard_vars[i] = var_name // for `else` + g.tmp_count-- + g.writeln('if (${var_name}.state == 0) {') + } else { + g.write('if ($var_name = ') + g.expr(branch.cond.expr) + g.writeln(', ${var_name}.state == 0) {') + } + if short_opt || branch.cond.var_name != '_' { + base_type := g.base_type(branch.cond.expr_type) + if short_opt { + cond_var_name := if branch.cond.var_name == '_' { + '_dummy_${g.tmp_count + 1}' + } else { + branch.cond.var_name + } + g.write('\t$base_type $cond_var_name = ') + g.expr(branch.cond.expr) + g.writeln(';') + } else { + mut is_auto_heap := false + if branch.stmts.len > 0 { + scope := g.file.scope.innermost(ast.Node(branch.stmts[branch.stmts.len - 1]).position().pos) + if v := scope.find_var(branch.cond.var_name) { + is_auto_heap = v.is_auto_heap + } + } + if is_auto_heap { + g.writeln('\t$base_type* $branch.cond.var_name = HEAP($base_type, *($base_type*)${var_name}.data);') + } else { + g.writeln('\t$base_type $branch.cond.var_name = *($base_type*)${var_name}.data;') + } + } + } + } + else { + g.write('if (') + g.expr(branch.cond) + g.writeln(') {') + } + } + } + if needs_tmp_var { + g.stmts_with_tmp_var(branch.stmts, tmp) + } else { + // restore if_expr stmt header pos + stmt_pos := g.nth_stmt_pos(0) + g.stmts(branch.stmts) + g.stmt_path_pos << stmt_pos + } + } + g.writeln('}') + if needs_tmp_var { + g.empty_line = false + g.write('$cur_line $tmp') + } + if node.typ.has_flag(.optional) { + g.inside_if_optional = false + } +} + +[inline] +fn (g &Gen) expr_is_multi_return_call(expr ast.Expr) bool { + match expr { + ast.CallExpr { return g.table.get_type_symbol(expr.return_type).kind == .multi_return } + else { return false } + } +} + +fn (mut g Gen) gen_optional_error(target_type ast.Type, expr ast.Expr) { + styp := g.typ(target_type) + g.write('($styp){ .state=2, .err=') + g.expr(expr) + g.write(', .data={EMPTY_STRUCT_INITIALIZATION} }') +} + +fn (mut g Gen) return_stmt(node ast.Return) { + g.write_v_source_line_info(node.pos) + if node.exprs.len > 0 { + // skip `return $vweb.html()` + if node.exprs[0] is ast.ComptimeCall { + g.expr(node.exprs[0]) + g.writeln(';') + return + } + } + + g.inside_return = true + defer { + g.inside_return = false + } + // got to do a correct check for multireturn + sym := g.table.get_type_symbol(g.fn_decl.return_type) + fn_return_is_multi := sym.kind == .multi_return + fn_return_is_optional := g.fn_decl.return_type.has_flag(.optional) + mut has_semicolon := false + if node.exprs.len == 0 { + g.write_defer_stmts_when_needed() + if fn_return_is_optional { + styp := g.typ(g.fn_decl.return_type) + g.writeln('return ($styp){0};') + } else { + if g.is_autofree { + g.trace_autofree('// free before return (no values returned)') + g.autofree_scope_vars(node.pos.pos - 1, node.pos.line_nr, true) + } + g.writeln('return;') + } + return + } + tmpvar := g.new_tmp_var() + ret_typ := g.typ(g.fn_decl.return_type) + mut use_tmp_var := g.defer_stmts.len > 0 || g.defer_profile_code.len > 0 + // handle promoting none/error/function returning 'Option' + if fn_return_is_optional { + optional_none := node.exprs[0] is ast.None + ftyp := g.typ(node.types[0]) + mut is_regular_option := ftyp == 'Option' + if optional_none || is_regular_option || node.types[0] == ast.error_type_idx { + if !isnil(g.fn_decl) && g.fn_decl.is_test { + test_error_var := g.new_tmp_var() + g.write('$ret_typ $test_error_var = ') + g.gen_optional_error(g.fn_decl.return_type, node.exprs[0]) + g.writeln(';') + g.write_defer_stmts_when_needed() + g.gen_failing_return_error_for_test_fn(node, test_error_var) + return + } + if use_tmp_var { + g.write('$ret_typ $tmpvar = ') + } else { + g.write('return ') + } + g.gen_optional_error(g.fn_decl.return_type, node.exprs[0]) + g.writeln(';') + if use_tmp_var { + g.write_defer_stmts_when_needed() + g.writeln('return $tmpvar;') + } + return + } + } + // regular cases + if fn_return_is_multi && node.exprs.len > 0 && !g.expr_is_multi_return_call(node.exprs[0]) { + if node.exprs.len == 1 && node.exprs[0] is ast.IfExpr { + // use a temporary for `return if cond { x,y } else { a,b }` + g.write('$ret_typ $tmpvar = ') + g.expr(node.exprs[0]) + g.writeln(';') + g.write_defer_stmts_when_needed() + g.writeln('return $tmpvar;') + return + } + // typ_sym := g.table.get_type_symbol(g.fn_decl.return_type) + // mr_info := typ_sym.info as ast.MultiReturn + mut styp := '' + if fn_return_is_optional { + g.writeln('$ret_typ $tmpvar;') + styp = g.base_type(g.fn_decl.return_type) + g.write('opt_ok(&($styp/*X*/[]) { ') + } else { + if use_tmp_var { + g.write('$ret_typ $tmpvar = ') + } else { + g.write('return ') + } + styp = g.typ(g.fn_decl.return_type) + } + // Use this to keep the tmp assignments in order + mut multi_unpack := '' + g.write('($styp){') + mut arg_idx := 0 + for i, expr in node.exprs { + // Check if we are dealing with a multi return and handle it seperately + if g.expr_is_multi_return_call(expr) { + c := expr as ast.CallExpr + expr_sym := g.table.get_type_symbol(c.return_type) + // Create a tmp for this call + mut tmp := g.new_tmp_var() + if !c.return_type.has_flag(.optional) { + s := g.go_before_stmt(0) + expr_styp := g.typ(c.return_type) + g.write('$expr_styp $tmp=') + g.expr(expr) + g.writeln(';') + multi_unpack += g.go_before_stmt(0) + g.write(s) + } else { + s := g.go_before_stmt(0) + // TODO + // I (emily) am sorry for doing this + // I cant find another way to do this so right now + // this will have to do. + g.tmp_count-- + g.expr(expr) + multi_unpack += g.go_before_stmt(0) + g.write(s) + // modify tmp so that it is the opt deref + // TODO copy-paste from cgen.v:2397 + expr_styp := g.base_type(c.return_type) + tmp = ('/*opt*/(*($expr_styp*)${tmp}.data)') + } + expr_types := expr_sym.mr_info().types + for j, _ in expr_types { + g.write('.arg$arg_idx=${tmp}.arg$j') + if j < expr_types.len || i < node.exprs.len - 1 { + g.write(',') + } + arg_idx++ + } + continue + } + g.write('.arg$arg_idx=') + if expr.is_auto_deref_var() { + g.write('*') + } + g.expr(expr) + arg_idx++ + if i < node.exprs.len - 1 { + g.write(', ') + } + } + g.write('}') + if fn_return_is_optional { + g.writeln(' }, (Option*)(&$tmpvar), sizeof($styp));') + g.write_defer_stmts_when_needed() + g.write('return $tmpvar') + } + // Make sure to add our unpacks + if multi_unpack.len > 0 { + g.insert_before_stmt(multi_unpack) + } + if use_tmp_var && !fn_return_is_optional { + if !has_semicolon { + g.writeln(';') + } + g.write_defer_stmts_when_needed() + g.writeln('return $tmpvar;') + has_semicolon = true + } + } else if node.exprs.len >= 1 { + // normal return + return_sym := g.table.get_type_symbol(node.types[0]) + expr0 := node.exprs[0] + // `return opt_ok(expr)` for functions that expect an optional + expr_type_is_opt := match expr0 { + ast.CallExpr { + expr0.return_type.has_flag(.optional) && expr0.or_block.kind == .absent + } + else { + node.types[0].has_flag(.optional) + } + } + if fn_return_is_optional && !expr_type_is_opt && return_sym.name != 'Option' { + styp := g.base_type(g.fn_decl.return_type) + g.writeln('$ret_typ $tmpvar;') + g.write('opt_ok(&($styp[]) { ') + if !g.fn_decl.return_type.is_ptr() && node.types[0].is_ptr() { + if !(node.exprs[0] is ast.Ident && !g.is_amp) { + g.write('*') + } + } + for i, expr in node.exprs { + g.expr_with_cast(expr, node.types[i], g.fn_decl.return_type.clear_flag(.optional)) + if i < node.exprs.len - 1 { + g.write(', ') + } + } + g.writeln(' }, (Option*)(&$tmpvar), sizeof($styp));') + g.write_defer_stmts_when_needed() + g.autofree_scope_vars(node.pos.pos - 1, node.pos.line_nr, true) + g.writeln('return $tmpvar;') + return + } + // autofree before `return` + // set free_parent_scopes to true, since all variables defined in parent + // scopes need to be freed before the return + if g.is_autofree { + expr := node.exprs[0] + if expr is ast.Ident { + g.returned_var_name = expr.name + } + } + // free := g.is_autofree && !g.is_builtin_mod // node.exprs[0] is ast.CallExpr + // Create a temporary variable for the return expression + use_tmp_var = use_tmp_var || !g.is_builtin_mod // node.exprs[0] is ast.CallExpr + if use_tmp_var { + // `return foo(a, b, c)` + // `tmp := foo(a, b, c); free(a); free(b); free(c); return tmp;` + // Save return value in a temp var so that all args (a,b,c) can be freed + // Don't use a tmp var if a variable is simply returned: `return x` + // Just in case of defer statements exists, that the return values cannot + // be modified. + if node.exprs[0] !is ast.Ident || use_tmp_var { + g.write('$ret_typ $tmpvar = ') + } else { + use_tmp_var = false + g.write_defer_stmts_when_needed() + if !g.is_builtin_mod { + g.autofree_scope_vars(node.pos.pos - 1, node.pos.line_nr, true) + } + g.write('return ') + } + } else { + g.autofree_scope_vars(node.pos.pos - 1, node.pos.line_nr, true) + g.write('return ') + } + if expr0.is_auto_deref_var() { + if g.fn_decl.return_type.is_ptr() { + var_str := g.expr_string(expr0) + g.write(var_str.trim('&')) + } else { + g.write('*') + g.expr(expr0) + } + } else { + g.expr_with_cast(node.exprs[0], node.types[0], g.fn_decl.return_type) + } + if use_tmp_var { + g.writeln(';') + has_semicolon = true + g.write_defer_stmts_when_needed() + if !g.is_builtin_mod { + g.autofree_scope_vars(node.pos.pos - 1, node.pos.line_nr, true) + } + g.write('return $tmpvar') + has_semicolon = false + } + } else { // if node.exprs.len == 0 { + println('this should never happen') + g.write('/*F*/return') + } + if !has_semicolon { + g.writeln(';') + } +} + +fn (mut g Gen) const_decl(node ast.ConstDecl) { + g.inside_const = true + defer { + g.inside_const = false + } + for field in node.fields { + if g.pref.skip_unused { + if field.name !in g.table.used_consts { + $if trace_skip_unused_consts ? { + eprintln('>> skipping unused const name: $field.name') + } + continue + } + } + name := c_name(field.name) + field_expr := field.expr + match field.expr { + ast.ArrayInit { + if field.expr.is_fixed { + styp := g.typ(field.expr.typ) + if g.pref.build_mode != .build_module { + val := g.expr_string(field.expr) + g.definitions.writeln('$styp _const_$name = $val; // fixed array const') + } else { + g.definitions.writeln('$styp _const_$name; // fixed array const') + } + } else { + g.const_decl_init_later(field.mod, name, field.expr, field.typ, false) + } + } + ast.StringLiteral { + g.definitions.writeln('string _const_$name; // a string literal, inited later') + if g.pref.build_mode != .build_module { + val := g.expr_string(field.expr) + g.stringliterals.writeln('\t_const_$name = $val;') + } + } + ast.CallExpr { + if field.expr.return_type.has_flag(.optional) { + unwrap_option := field.expr.or_block.kind != .absent + g.const_decl_init_later(field.mod, name, field.expr, field.typ, unwrap_option) + } else { + g.const_decl_init_later(field.mod, name, field.expr, field.typ, false) + } + } + else { + if g.pref.build_mode != .build_module { + if ct_value := field.comptime_expr_value() { + if g.const_decl_precomputed(field.mod, name, ct_value, field.typ) { + continue + } + } + } + if field.is_simple_define_const() { + // "Simple" expressions are not going to need multiple statements, + // only the ones which are inited later, so it's safe to use expr_string + g.const_decl_simple_define(name, g.expr_string(field_expr)) + } else { + g.const_decl_init_later(field.mod, name, field.expr, field.typ, false) + } + } + } + } +} + +fn (mut g Gen) const_decl_precomputed(mod string, name string, ct_value ast.ComptTimeConstValue, typ ast.Type) bool { + mut styp := g.typ(typ) + cname := '_const_$name' + $if trace_const_precomputed ? { + eprintln('> styp: $styp | cname: $cname | ct_value: $ct_value | $ct_value.type_name()') + } + match ct_value { + i8 { + g.const_decl_write_precomputed(styp, cname, ct_value.str()) + } + i16 { + g.const_decl_write_precomputed(styp, cname, ct_value.str()) + } + int { + g.const_decl_write_precomputed(styp, cname, ct_value.str()) + } + i64 { + if typ == ast.i64_type { + return false + } + if typ == ast.int_type { + // TODO: use g.const_decl_write_precomputed here too. + // For now, use #define macros, so existing code compiles + // with -cstrict. Add checker errors for overflows instead, + // so V can catch them earlier, instead of relying on the + // C compiler for that. + g.const_decl_simple_define(name, ct_value.str()) + return true + } + if typ == ast.u64_type { + g.const_decl_write_precomputed(styp, cname, ct_value.str() + 'U') + } else { + g.const_decl_write_precomputed(styp, cname, ct_value.str()) + } + } + byte { + g.const_decl_write_precomputed(styp, cname, ct_value.str()) + } + u16 { + g.const_decl_write_precomputed(styp, cname, ct_value.str()) + } + u32 { + g.const_decl_write_precomputed(styp, cname, ct_value.str()) + } + u64 { + g.const_decl_write_precomputed(styp, cname, ct_value.str() + 'U') + } + f32 { + g.const_decl_write_precomputed(styp, cname, ct_value.str()) + } + f64 { + g.const_decl_write_precomputed(styp, cname, ct_value.str()) + } + rune { + rune_code := u32(ct_value) + if rune_code <= 255 { + if rune_code in [`"`, `\\`, `\'`] { + return false + } + escval := util.smart_quote(byte(rune_code).ascii_str(), false) + g.const_decl_write_precomputed(styp, cname, "'$escval'") + } else { + g.const_decl_write_precomputed(styp, cname, u32(ct_value).str()) + } + } + string { + escaped_val := util.smart_quote(ct_value, false) + // g.const_decl_write_precomputed(styp, cname, '_SLIT("$escaped_val")') + // TODO: ^ the above for strings, cause: + // `error C2099: initializer is not a constant` errors in MSVC, + // so fall back to the delayed initialisation scheme: + g.definitions.writeln('$styp $cname; // inited later') + g.inits[mod].writeln('\t$cname = _SLIT("$escaped_val");') + if g.is_autofree { + g.cleanups[mod].writeln('\tstring_free(&$cname);') + } + } + ast.EmptyExpr { + return false + } + } + return true +} + +fn (mut g Gen) const_decl_write_precomputed(styp string, cname string, ct_value string) { + g.definitions.writeln('$styp $cname = $ct_value; // precomputed') +} + +fn (mut g Gen) const_decl_simple_define(name string, val string) { + // Simple expressions should use a #define + // so that we don't pollute the binary with unnecessary global vars + // Do not do this when building a module, otherwise the consts + // will not be accessible. + g.definitions.write_string('#define _const_$name ') + g.definitions.writeln(val) +} + +fn (mut g Gen) const_decl_init_later(mod string, name string, expr ast.Expr, typ ast.Type, unwrap_option bool) { + // Initialize more complex consts in `void _vinit/2{}` + // (C doesn't allow init expressions that can't be resolved at compile time). + mut styp := g.typ(typ) + cname := '_const_$name' + g.definitions.writeln('$styp $cname; // inited later') + if cname == '_const_os__args' { + if g.pref.os == .windows { + g.inits[mod].writeln('\t_const_os__args = os__init_os_args_wide(___argc, (byteptr*)___argv);') + } else { + g.inits[mod].writeln('\t_const_os__args = os__init_os_args(___argc, (byte**)___argv);') + } + } else { + if unwrap_option { + g.inits[mod].writeln(g.expr_string_surround('\t$cname = *($styp*)', expr, + '.data;')) + } else { + g.inits[mod].writeln(g.expr_string_surround('\t$cname = ', expr, ';')) + } + } + if g.is_autofree { + sym := g.table.get_type_symbol(typ) + if styp.starts_with('Array_') { + g.cleanups[mod].writeln('\tarray_free(&$cname);') + } else if styp == 'string' { + g.cleanups[mod].writeln('\tstring_free(&$cname);') + } else if sym.kind == .map { + g.cleanups[mod].writeln('\tmap_free(&$cname);') + } else if styp == 'IError' { + g.cleanups[mod].writeln('\tIError_free(&$cname);') + } + } +} + +fn (mut g Gen) global_decl(node ast.GlobalDecl) { + mod := if g.pref.build_mode == .build_module && g.is_builtin_mod { 'static ' } else { '' } + key := node.mod // module name + for field in node.fields { + if g.pref.skip_unused { + if field.name !in g.table.used_globals { + $if trace_skip_unused_globals ? { + eprintln('>> skipping unused global name: $field.name') + } + continue + } + } + styp := g.typ(field.typ) + if field.has_expr { + g.definitions.write_string('$mod$styp $field.name') + if field.expr.is_literal() { + g.definitions.writeln(' = ${g.expr_string(field.expr)}; // global') + } else { + g.definitions.writeln(';') + g.global_inits[key].writeln('\t$field.name = ${g.expr_string(field.expr)}; // global') + } + } else { + default_initializer := g.type_default(field.typ) + if default_initializer == '{0}' { + g.definitions.writeln('$mod$styp $field.name = {0}; // global') + } else { + g.definitions.writeln('$mod$styp $field.name; // global') + if field.name !in ['as_cast_type_indexes', 'g_memory_block'] { + g.global_inits[key].writeln('\t$field.name = *($styp*)&(($styp[]){${g.type_default(field.typ)}}[0]); // global') + } + } + } + } +} + +fn (mut g Gen) go_back_out(n int) { + g.out.go_back(n) +} + +const ( + skip_struct_init = ['struct stat', 'struct addrinfo'] +) + +fn (mut g Gen) struct_init(struct_init ast.StructInit) { + styp := g.typ(struct_init.typ) + mut shared_styp := '' // only needed for shared x := St{... + if styp in c.skip_struct_init { + // needed for c++ compilers + g.go_back_out(3) + return + } + sym := g.table.get_final_type_symbol(g.unwrap_generic(struct_init.typ)) + is_amp := g.is_amp + is_multiline := struct_init.fields.len > 5 + g.is_amp = false // reset the flag immediately so that other struct inits in this expr are handled correctly + if is_amp { + g.out.go_back(1) // delete the `&` already generated in `prefix_expr() + } + if g.is_shared && !g.inside_opt_data && !g.is_arraymap_set { + mut shared_typ := struct_init.typ.set_flag(.shared_f) + shared_styp = g.typ(shared_typ) + g.writeln('($shared_styp*)__dup${shared_styp}(&($shared_styp){.mtx = {0}, .val =($styp){') + } else if is_amp || g.inside_cast_in_heap > 0 { + g.write('($styp*)memdup(&($styp){') + } else if struct_init.typ.is_ptr() { + basetyp := g.typ(struct_init.typ.set_nr_muls(0)) + if is_multiline { + g.writeln('&($basetyp){') + } else { + g.write('&($basetyp){') + } + } else { + if is_multiline { + g.writeln('($styp){') + } else { + g.write('($styp){') + } + } + // mut fields := []string{} + mut inited_fields := map[string]int{} // TODO this is done in checker, move to ast node + /* + if struct_init.fields.len == 0 && struct_init.exprs.len > 0 { + // Get fields for {a,b} short syntax. Fields array wasn't set in the parser. + for f in info.fields { + fields << f.name + } + } else { + fields = struct_init.fields + } + */ + if is_multiline { + g.indent++ + } + // User set fields + mut initialized := false + for i, field in struct_init.fields { + inited_fields[field.name] = i + if sym.kind != .struct_ { + field_name := if sym.language == .v { c_name(field.name) } else { field.name } + g.write('.$field_name = ') + if field.typ == 0 { + g.checker_bug('struct init, field.typ is 0', field.pos) + } + field_type_sym := g.table.get_type_symbol(field.typ) + mut cloned := false + if g.is_autofree && !field.typ.is_ptr() && field_type_sym.kind in [.array, .string] { + g.write('/*clone1*/') + if g.gen_clone_assignment(field.expr, field_type_sym, false) { + cloned = true + } + } + if !cloned { + if (field.expected_type.is_ptr() && !field.expected_type.has_flag(.shared_f)) + && !(field.typ.is_ptr() || field.typ.is_pointer()) && !field.typ.is_number() { + g.write('/* autoref */&') + } + g.expr_with_cast(field.expr, field.typ, field.expected_type) + } + if i != struct_init.fields.len - 1 { + if is_multiline { + g.writeln(',') + } else { + g.write(', ') + } + } + initialized = true + } + } + // The rest of the fields are zeroed. + // `inited_fields` is a list of fields that have been init'ed, they are skipped + mut nr_fields := 1 + if sym.kind == .struct_ { + info := sym.info as ast.Struct + nr_fields = info.fields.len + if info.is_union && struct_init.fields.len > 1 { + verror('union must not have more than 1 initializer') + } + if !info.is_union { + mut used_embed_fields := []string{} + init_field_names := info.fields.map(it.name) + // fields that are initialized but belong to the embedding + init_fields_to_embed := struct_init.fields.filter(it.name !in init_field_names) + for embed in info.embeds { + embed_sym := g.table.get_type_symbol(embed) + embed_name := embed_sym.embed_name() + if embed_name !in inited_fields { + embed_info := embed_sym.info as ast.Struct + embed_field_names := embed_info.fields.map(it.name) + fields_to_embed := init_fields_to_embed.filter(it.name !in used_embed_fields + && it.name in embed_field_names) + used_embed_fields << fields_to_embed.map(it.name) + default_init := ast.StructInit{ + typ: embed + fields: fields_to_embed + } + g.write('.$embed_name = ') + g.struct_init(default_init) + if is_multiline { + g.writeln(',') + } else { + g.write(',') + } + initialized = true + } + } + } + // g.zero_struct_fields(info, inited_fields) + // nr_fields = info.fields.len + for mut field in info.fields { + if mut sym.info is ast.Struct { + mut found_equal_fields := 0 + for mut sifield in sym.info.fields { + if sifield.name == field.name { + found_equal_fields++ + break + } + } + if found_equal_fields == 0 { + continue + } + } + if field.name in inited_fields { + sfield := struct_init.fields[inited_fields[field.name]] + field_name := if sym.language == .v { c_name(field.name) } else { field.name } + if sfield.typ == 0 { + continue + } + g.write('.$field_name = ') + field_type_sym := g.table.get_type_symbol(sfield.typ) + mut cloned := false + if g.is_autofree && !sfield.typ.is_ptr() && field_type_sym.kind in [.array, .string] { + g.write('/*clone1*/') + if g.gen_clone_assignment(sfield.expr, field_type_sym, false) { + cloned = true + } + } + if !cloned { + if (sfield.expected_type.is_ptr() && !sfield.expected_type.has_flag(.shared_f)) + && !(sfield.typ.is_ptr() || sfield.typ.is_pointer()) + && !sfield.typ.is_number() { + g.write('/* autoref */&') + } + g.expr_with_cast(sfield.expr, sfield.typ, sfield.expected_type) + } + if is_multiline { + g.writeln(',') + } else { + g.write(',') + } + initialized = true + continue + } + if info.is_union { + // unions thould have exactly one explicit initializer + continue + } + if field.typ.has_flag(.optional) { + field_name := c_name(field.name) + g.write('.$field_name = {EMPTY_STRUCT_INITIALIZATION},') + initialized = true + continue + } + if field.typ in info.embeds { + continue + } + if struct_init.has_update_expr { + g.expr(struct_init.update_expr) + if struct_init.update_expr_type.is_ptr() { + g.write('->') + } else { + g.write('.') + } + g.write(field.name) + } else { + if !g.zero_struct_field(field) { + nr_fields-- + continue + } + } + if is_multiline { + g.writeln(',') + } else { + g.write(',') + } + initialized = true + } + } + if is_multiline { + g.indent-- + } + + if !initialized { + if nr_fields > 0 { + g.write('0') + } else { + g.write('EMPTY_STRUCT_INITIALIZATION') + } + } + + g.write('}') + if g.is_shared && !g.inside_opt_data && !g.is_arraymap_set { + g.write('}, sizeof($shared_styp))') + } else if is_amp || g.inside_cast_in_heap > 0 { + g.write(', sizeof($styp))') + } +} + +fn (mut g Gen) zero_struct_field(field ast.StructField) bool { + sym := g.table.get_type_symbol(field.typ) + if sym.kind == .struct_ { + info := sym.info as ast.Struct + if info.fields.len == 0 { + return false + } + } + field_name := if sym.language == .v { c_name(field.name) } else { field.name } + g.write('.$field_name = ') + if field.has_default_expr { + if sym.kind in [.sum_type, .interface_] { + g.expr_with_cast(field.default_expr, field.default_expr_typ, field.typ) + return true + } + g.expr(field.default_expr) + } else { + g.write(g.type_default(field.typ)) + } + return true +} + +// fn (mut g Gen) zero_struct_fields(info ast.Struct, inited_fields map[string]int) { +// } +// { user | name: 'new name' } +fn (mut g Gen) assoc(node ast.Assoc) { + g.writeln('// assoc') + if node.typ == 0 { + return + } + styp := g.typ(node.typ) + g.writeln('($styp){') + mut inited_fields := map[string]int{} + for i, field in node.fields { + inited_fields[field] = i + } + // Merge inited_fields in the rest of the fields. + sym := g.table.get_type_symbol(node.typ) + info := sym.info as ast.Struct + for field in info.fields { + field_name := c_name(field.name) + if field.name in inited_fields { + g.write('\t.$field_name = ') + g.expr(node.exprs[inited_fields[field.name]]) + g.writeln(', ') + } else { + g.writeln('\t.$field_name = ${node.var_name}.$field_name,') + } + } + g.write('}') + if g.is_amp { + g.write(', sizeof($styp))') + } +} + +[noreturn] +fn verror(s string) { + util.verror('cgen error', s) +} + +[noreturn] +fn (g &Gen) error(s string, pos token.Position) { + ferror := util.formatted_error('cgen error:', s, g.file.path, pos) + eprintln(ferror) + exit(1) +} + +fn (g &Gen) checker_bug(s string, pos token.Position) { + g.error('checker bug; $s', pos) +} + +fn (mut g Gen) write_init_function() { + if g.pref.is_liveshared { + return + } + fn_vinit_start_pos := g.out.len + // ___argv is declared as voidptr here, because that unifies the windows/unix logic + g.writeln('void _vinit(int ___argc, voidptr ___argv) {') + if g.pref.prealloc { + g.writeln('prealloc_vinit();') + } + // NB: the as_cast table should be *before* the other constant initialize calls, + // because it may be needed during const initialization of builtin and during + // calling module init functions too, just in case they do fail... + g.write('\tas_cast_type_indexes = ') + g.writeln(g.as_cast_name_table()) + // + g.writeln('\tbuiltin_init();') + g.writeln('\tvinit_string_literals();') + // + for mod_name in g.table.modules { + g.writeln('\t// Initializations for module $mod_name :') + g.write(g.inits[mod_name].str()) + g.write(g.global_inits[mod_name].str()) + init_fn_name := '${mod_name}.init' + if initfn := g.table.find_fn(init_fn_name) { + if initfn.return_type == ast.void_type && initfn.params.len == 0 { + mod_c_name := util.no_dots(mod_name) + init_fn_c_name := '${mod_c_name}__init' + g.writeln('\t${init_fn_c_name}();') + } + } + } + g.writeln('}') + if g.pref.printfn_list.len > 0 && '_vinit' in g.pref.printfn_list { + println(g.out.after(fn_vinit_start_pos)) + } + // + fn_vcleanup_start_pos := g.out.len + g.writeln('void _vcleanup() {') + if g.is_autofree { + // g.writeln('puts("cleaning up...");') + reversed_table_modules := g.table.modules.reverse() + for mod_name in reversed_table_modules { + g.writeln('\t// Cleanups for module $mod_name :') + g.writeln(g.cleanups[mod_name].str()) + } + g.writeln('\tarray_free(&as_cast_type_indexes);') + } + g.writeln('}') + if g.pref.printfn_list.len > 0 && '_vcleanup' in g.pref.printfn_list { + println(g.out.after(fn_vcleanup_start_pos)) + } + // + needs_constructor := g.pref.is_shared && g.pref.os != .windows + if needs_constructor { + // shared libraries need a way to call _vinit/2. For that purpose, + // provide a constructor/destructor pair, ensuring that all constants + // are initialized just once, and that they will be freed too. + // NB: os.args in this case will be []. + g.writeln('__attribute__ ((constructor))') + g.writeln('void _vinit_caller() {') + g.writeln('\tstatic bool once = false; if (once) {return;} once = true;') + g.writeln('\t_vinit(0,0);') + g.writeln('}') + + g.writeln('__attribute__ ((destructor))') + g.writeln('void _vcleanup_caller() {') + g.writeln('\tstatic bool once = false; if (once) {return;} once = true;') + g.writeln('\t_vcleanup();') + g.writeln('}') + } +} + +const ( + builtins = ['string', 'array', 'DenseArray', 'map', 'Error', 'IError', 'Option'] +) + +fn (mut g Gen) write_builtin_types() { + mut builtin_types := []ast.TypeSymbol{} // builtin types + // builtin types need to be on top + // everything except builtin will get sorted + for builtin_name in c.builtins { + sym := g.table.type_symbols[g.table.type_idxs[builtin_name]] + if sym.kind == .interface_ { + g.write_interface_typedef(sym) + g.write_interface_typesymbol_declaration(sym) + } else { + builtin_types << sym + } + } + g.write_types(builtin_types) +} + +// C struct definitions, ordered +// Sort the types, make sure types that are referenced by other types +// are added before them. +fn (mut g Gen) write_sorted_types() { + mut types := []ast.TypeSymbol{} // structs that need to be sorted + for typ in g.table.type_symbols { + if typ.name !in c.builtins { + types << typ + } + } + // sort structs + types_sorted := g.sort_structs(types) + // Generate C code + g.type_definitions.writeln('// builtin types:') + g.type_definitions.writeln('//------------------ #endbuiltin') + g.write_types(types_sorted) +} + +fn (mut g Gen) write_types(types []ast.TypeSymbol) { + for typ in types { + if typ.name.starts_with('C.') { + continue + } + // sym := g.table.get_type_symbol(typ) + mut name := typ.cname + match mut typ.info { + ast.Struct { + if typ.info.is_generic { + continue + } + if name.contains('_T_') { + g.typedefs.writeln('typedef struct $name $name;') + } + // TODO avoid buffer manip + start_pos := g.type_definitions.len + + mut pre_pragma := '' + mut post_pragma := '' + + for attr in typ.info.attrs { + match attr.name { + '_pack' { + pre_pragma += '#pragma pack(push, $attr.arg)\n' + post_pragma += '#pragma pack(pop)' + } + else {} + } + } + + g.type_definitions.writeln(pre_pragma) + + if typ.info.is_union { + g.type_definitions.writeln('union $name {') + } else { + g.type_definitions.writeln('struct $name {') + } + if typ.info.fields.len > 0 || typ.info.embeds.len > 0 { + for field in typ.info.fields { + // Some of these structs may want to contain + // optionals that may not be defined at this point + // if this is the case then we are going to + // buffer manip out in front of the struct + // write the optional in and then continue + if field.typ.has_flag(.optional) { + // Dont use g.typ() here becuase it will register + // optional and we dont want that + styp, base := g.optional_type_name(field.typ) + if styp !in g.optionals { + last_text := g.type_definitions.after(start_pos).clone() + g.type_definitions.go_back_to(start_pos) + g.optionals << styp + g.typedefs2.writeln('typedef struct $styp $styp;') + g.type_definitions.writeln('${g.optional_type_text(styp, + base)};') + g.type_definitions.write_string(last_text) + } + } + type_name := g.typ(field.typ) + field_name := c_name(field.name) + g.type_definitions.writeln('\t$type_name $field_name;') + } + } else { + g.type_definitions.writeln('\tEMPTY_STRUCT_DECLARATION;') + } + // g.type_definitions.writeln('} $name;\n') + // + ti_attrs := if typ.info.attrs.contains('packed') { + '__attribute__((__packed__))' + } else { + '' + } + g.type_definitions.writeln('}$ti_attrs;\n') + g.type_definitions.writeln(post_pragma) + } + ast.Alias { + // ast.Alias { TODO + } + ast.Thread { + if g.pref.os == .windows { + if name == '__v_thread' { + g.type_definitions.writeln('typedef HANDLE $name;') + } else { + // Windows can only return `u32` (no void*) from a thread, so the + // V gohandle must maintain a pointer to the return value + g.type_definitions.writeln('typedef struct {') + g.type_definitions.writeln('\tvoid* ret_ptr;') + g.type_definitions.writeln('\tHANDLE handle;') + g.type_definitions.writeln('} $name;') + } + } else { + if !g.pref.is_bare { + g.type_definitions.writeln('typedef pthread_t $name;') + } + } + } + ast.SumType { + if typ.info.is_generic { + continue + } + g.typedefs.writeln('typedef struct $name $name;') + g.type_definitions.writeln('') + g.type_definitions.writeln('// Union sum type $name = ') + for variant in typ.info.variants { + g.type_definitions.writeln('// | ${variant:4d} = ${g.typ(variant.idx()):-20s}') + } + g.type_definitions.writeln('struct $name {') + g.type_definitions.writeln('\tunion {') + for variant in typ.info.variants { + variant_sym := g.table.get_type_symbol(variant) + g.type_definitions.writeln('\t\t${g.typ(variant.to_ptr())} _$variant_sym.cname;') + } + g.type_definitions.writeln('\t};') + g.type_definitions.writeln('\tint _typ;') + if typ.info.fields.len > 0 { + g.writeln('\t// pointers to common sumtype fields') + for field in typ.info.fields { + g.type_definitions.writeln('\t${g.typ(field.typ.to_ptr())} $field.name;') + } + } + g.type_definitions.writeln('};') + g.type_definitions.writeln('') + } + ast.ArrayFixed { + elem_sym := g.table.get_type_symbol(typ.info.elem_type) + if !elem_sym.is_builtin() && !typ.info.elem_type.has_flag(.generic) { + // .array_fixed { + styp := typ.cname + // array_fixed_char_300 => char x[300] + // [16]&&&EventListener{} => Array_fixed_main__EventListener_16_ptr3 + // => typedef main__EventListener*** Array_fixed_main__EventListener_16_ptr3 [16] + mut fixed_elem_name := g.typ(typ.info.elem_type.set_nr_muls(0)) + if typ.info.elem_type.is_ptr() { + fixed_elem_name += '*'.repeat(typ.info.elem_type.nr_muls()) + } + len := typ.info.size + if fixed_elem_name.starts_with('C__') { + fixed_elem_name = fixed_elem_name[3..] + } + if elem_sym.info is ast.FnType { + pos := g.out.len + g.write_fn_ptr_decl(&elem_sym.info, '') + fixed_elem_name = g.out.after(pos) + g.out.go_back(fixed_elem_name.len) + mut def_str := 'typedef $fixed_elem_name;' + def_str = def_str.replace_once('(*)', '(*$styp[$len])') + g.type_definitions.writeln(def_str) + } else { + g.type_definitions.writeln('typedef $fixed_elem_name $styp [$len];') + } + } + } + else {} + } + } +} + +// sort structs by dependant fields +fn (g &Gen) sort_structs(typesa []ast.TypeSymbol) []ast.TypeSymbol { + mut dep_graph := depgraph.new_dep_graph() + // types name list + mut type_names := []string{} + for typ in typesa { + type_names << typ.name + } + // loop over types + for t in typesa { + if t.kind == .interface_ { + dep_graph.add(t.name, []) + continue + } + // create list of deps + mut field_deps := []string{} + match mut t.info { + ast.ArrayFixed { + dep := g.table.get_type_symbol(t.info.elem_type).name + if dep in type_names { + field_deps << dep + } + } + ast.Struct { + for embed in t.info.embeds { + dep := g.table.get_type_symbol(embed).name + // skip if not in types list or already in deps + if dep !in type_names || dep in field_deps { + continue + } + field_deps << dep + } + for field in t.info.fields { + dep := g.table.get_type_symbol(field.typ).name + // skip if not in types list or already in deps + if dep !in type_names || dep in field_deps || field.typ.is_ptr() { + continue + } + field_deps << dep + } + } + // ast.Interface {} + else {} + } + // add type and dependant types to graph + dep_graph.add(t.name, field_deps) + } + // sort graph + dep_graph_sorted := dep_graph.resolve() + if !dep_graph_sorted.acyclic { + // this should no longer be called since it's catched in the parser + // TODO: should it be removed? + verror('cgen.sort_structs(): the following structs form a dependency cycle:\n' + + dep_graph_sorted.display_cycles() + + '\nyou can solve this by making one or both of the dependant struct fields references, eg: field &MyStruct' + + '\nif you feel this is an error, please create a new issue here: https://github.com/vlang/v/issues and tag @joe-conigliaro') + } + // sort types + mut types_sorted := []ast.TypeSymbol{} + for node in dep_graph_sorted.nodes { + types_sorted << g.table.type_symbols[g.table.type_idxs[node.name]] + } + return types_sorted +} + +[inline] +fn (g &Gen) nth_stmt_pos(n int) int { + return g.stmt_path_pos[g.stmt_path_pos.len - (1 + n)] +} + +fn (mut g Gen) go_before_stmt(n int) string { + stmt_pos := g.nth_stmt_pos(n) + cur_line := g.out.after(stmt_pos) + g.out.go_back(cur_line.len) + return cur_line +} + +[inline] +fn (mut g Gen) go_before_ternary() string { + return g.go_before_stmt(g.inside_ternary) +} + +fn (mut g Gen) insert_before_stmt(s string) { + cur_line := g.go_before_stmt(0) + g.writeln(s) + g.write(cur_line) +} + +// fn (mut g Gen) start_tmp() { +// } +// If user is accessing the return value eg. in assigment, pass the variable name. +// If the user is not using the optional return value. We need to pass a temp var +// to access its fields (`.ok`, `.error` etc) +// `os.cp(...)` => `Option bool tmp = os__cp(...); if (tmp.state != 0) { ... }` +// Returns the type of the last stmt +fn (mut g Gen) or_block(var_name string, or_block ast.OrExpr, return_type ast.Type) { + cvar_name := c_name(var_name) + mr_styp := g.base_type(return_type) + is_none_ok := return_type == ast.ovoid_type + g.writeln(';') + if is_none_ok { + g.writeln('if (${cvar_name}.state != 0 && ${cvar_name}.err._typ != _IError_None___index) {') + } else { + g.writeln('if (${cvar_name}.state != 0) { /*or block*/ ') + } + if or_block.kind == .block { + if g.inside_or_block { + g.writeln('\terr = ${cvar_name}.err;') + } else { + g.writeln('\tIError err = ${cvar_name}.err;') + } + g.inside_or_block = true + defer { + g.inside_or_block = false + } + stmts := or_block.stmts + if stmts.len > 0 && stmts[or_block.stmts.len - 1] is ast.ExprStmt + && (stmts[stmts.len - 1] as ast.ExprStmt).typ != ast.void_type { + g.indent++ + for i, stmt in stmts { + if i == stmts.len - 1 { + expr_stmt := stmt as ast.ExprStmt + g.stmt_path_pos << g.out.len + g.write('*($mr_styp*) ${cvar_name}.data = ') + old_inside_opt_data := g.inside_opt_data + g.inside_opt_data = true + g.expr_with_cast(expr_stmt.expr, expr_stmt.typ, return_type.clear_flag(.optional)) + g.inside_opt_data = old_inside_opt_data + if g.inside_ternary == 0 { + g.writeln(';') + } + g.stmt_path_pos.delete_last() + } else { + g.stmt(stmt) + } + } + g.indent-- + } else { + g.stmts(stmts) + if stmts.len > 0 && stmts[or_block.stmts.len - 1] is ast.ExprStmt { + g.writeln(';') + } + } + } else if or_block.kind == .propagate { + if g.file.mod.name == 'main' && (isnil(g.fn_decl) || g.fn_decl.is_main) { + // In main(), an `opt()?` call is sugar for `opt() or { panic(err) }` + if g.pref.is_debug { + paline, pafile, pamod, pafn := g.panic_debug_info(or_block.pos) + g.writeln('panic_debug($paline, tos3("$pafile"), tos3("$pamod"), tos3("$pafn"), *${cvar_name}.err.msg );') + } else { + g.writeln('\tpanic_optional_not_set(*${cvar_name}.err.msg);') + } + } else if !isnil(g.fn_decl) && g.fn_decl.is_test { + g.gen_failing_error_propagation_for_test_fn(or_block, cvar_name) + } else { + // In ordinary functions, `opt()?` call is sugar for: + // `opt() or { return err }` + // Since we *do* return, first we have to ensure that + // the defered statements are generated. + g.write_defer_stmts() + // Now that option types are distinct we need a cast here + if g.fn_decl.return_type == ast.void_type { + g.writeln('\treturn;') + } else { + styp := g.typ(g.fn_decl.return_type) + err_obj := g.new_tmp_var() + g.writeln('\t$styp $err_obj;') + g.writeln('\tmemcpy(&$err_obj, &$cvar_name, sizeof(Option));') + g.writeln('\treturn $err_obj;') + } + } + } + g.writeln('}') +} + +[inline] +fn c_name(name_ string) string { + name := util.no_dots(name_) + if name in c.c_reserved_map { + return '_v_$name' + } + return name +} + +fn (mut g Gen) type_default(typ_ ast.Type) string { + typ := g.unwrap_generic(typ_) + if typ.has_flag(.optional) { + return '{0}' + } + // Always set pointers to 0 + if typ.is_ptr() && !typ.has_flag(.shared_f) { + return '0' + } + if typ.idx() < ast.string_type_idx { + // Default values for other types are not needed because of mandatory initialization + return '0' + } + sym := g.table.get_type_symbol(typ) + match sym.kind { + .string { + return '(string){.str=(byteptr)"", .is_lit=1}' + } + .interface_, .sum_type, .array_fixed, .multi_return { + return '{0}' + } + .alias { + return g.type_default((sym.info as ast.Alias).parent_type) + } + .chan { + elem_type := sym.chan_info().elem_type + elemtypstr := g.typ(elem_type) + noscan := g.check_noscan(elem_type) + return 'sync__new_channel_st${noscan}(0, sizeof($elemtypstr))' + } + .array { + elem_typ := sym.array_info().elem_type + elem_sym := g.typ(elem_typ) + mut elem_type_str := util.no_dots(elem_sym) + if elem_type_str.starts_with('C__') { + elem_type_str = elem_type_str[3..] + } + noscan := g.check_noscan(elem_typ) + init_str := '__new_array${noscan}(0, 0, sizeof($elem_type_str))' + if typ.has_flag(.shared_f) { + atyp := '__shared__Array_${g.table.get_type_symbol(elem_typ).cname}' + return '($atyp*)__dup_shared_array(&($atyp){.mtx = {0}, .val =$init_str}, sizeof($atyp))' + } else { + return init_str + } + } + .map { + info := sym.map_info() + key_typ := g.table.get_type_symbol(info.key_type) + hash_fn, key_eq_fn, clone_fn, free_fn := g.map_fn_ptrs(key_typ) + noscan_key := g.check_noscan(info.key_type) + noscan_value := g.check_noscan(info.value_type) + mut noscan := if noscan_key.len != 0 || noscan_value.len != 0 { '_noscan' } else { '' } + if noscan.len != 0 { + if noscan_key.len != 0 { + noscan += '_key' + } + if noscan_value.len != 0 { + noscan += '_value' + } + } + init_str := 'new_map${noscan}(sizeof(${g.typ(info.key_type)}), sizeof(${g.typ(info.value_type)}), $hash_fn, $key_eq_fn, $clone_fn, $free_fn)' + if typ.has_flag(.shared_f) { + mtyp := '__shared__Map_${key_typ.cname}_${g.table.get_type_symbol(info.value_type).cname}' + return '($mtyp*)__dup_shared_map(&($mtyp){.mtx = {0}, .val =$init_str}, sizeof($mtyp))' + } else { + return init_str + } + } + .struct_ { + mut has_none_zero := false + mut init_str := '{' + info := sym.info as ast.Struct + typ_is_shared_f := typ.has_flag(.shared_f) + if sym.language == .v && !typ_is_shared_f { + for field in info.fields { + field_sym := g.table.get_type_symbol(field.typ) + if field.has_default_expr + || field_sym.kind in [.array, .map, .string, .bool, .alias, .size_t, .i8, .i16, .int, .i64, .byte, .u16, .u32, .u64, .char, .voidptr, .byteptr, .charptr, .struct_] { + field_name := c_name(field.name) + if field.has_default_expr { + expr_str := g.expr_string(field.default_expr) + init_str += '.$field_name = $expr_str,' + } else { + init_str += '.$field_name = ${g.type_default(field.typ)},' + } + has_none_zero = true + } + } + } + if has_none_zero { + init_str += '}' + type_name := g.typ(typ) + init_str = '($type_name)' + init_str + } else { + init_str += '0}' + } + if typ.has_flag(.shared_f) { + styp := '__shared__${g.table.get_type_symbol(typ).cname}' + return '($styp*)__dup${styp}(&($styp){.mtx = {0}, .val =$init_str}, sizeof($styp))' + } else { + return init_str + } + } + else { + return '0' + } + } +} + +fn (g &Gen) get_all_test_function_names() []string { + mut tfuncs := []string{} + mut tsuite_begin := '' + mut tsuite_end := '' + for _, f in g.table.fns { + if f.name.ends_with('.testsuite_begin') { + tsuite_begin = f.name + continue + } + if f.name.contains('.test_') { + tfuncs << f.name + continue + } + if f.name.ends_with('.testsuite_end') { + tsuite_end = f.name + continue + } + } + mut all_tfuncs := []string{} + if tsuite_begin.len > 0 { + all_tfuncs << tsuite_begin + } + all_tfuncs << tfuncs + if tsuite_end.len > 0 { + all_tfuncs << tsuite_end + } + return all_tfuncs +} + +fn (g &Gen) is_importing_os() bool { + return 'os' in g.table.imports +} + +fn (mut g Gen) go_expr(node ast.GoExpr) { + line := g.go_before_stmt(0) + mut handle := '' + tmp := g.new_tmp_var() + mut expr := node.call_expr + mut name := expr.name // util.no_dots(expr.name) + // TODO: fn call is duplicated. merge with fn_call(). + for i, concrete_type in expr.concrete_types { + if concrete_type != ast.void_type && concrete_type != 0 { + // Using _T_ to differentiate between get and get_string + // `foo()` => `foo_T_int()` + if i == 0 { + name += '_T' + } + name += '_' + g.typ(concrete_type) + } + } + if expr.is_method { + receiver_sym := g.table.get_type_symbol(expr.receiver_type) + name = receiver_sym.name + '_' + name + } else if mut expr.left is ast.AnonFn { + g.gen_anon_fn_decl(mut expr.left) + fsym := g.table.get_type_symbol(expr.left.typ) + name = fsym.name + } + name = util.no_dots(name) + if g.pref.obfuscate && g.cur_mod.name == 'main' && name.starts_with('main__') { + mut key := expr.name + if expr.is_method { + sym := g.table.get_type_symbol(expr.receiver_type) + key = sym.name + '.' + expr.name + } + g.write('/* obf go: $key */') + name = g.obf_table[key] or { + panic('cgen: obf name "$key" not found, this should never happen') + } + } + g.writeln('// go') + wrapper_struct_name := 'thread_arg_' + name + wrapper_fn_name := name + '_thread_wrapper' + arg_tmp_var := 'arg_' + tmp + g.writeln('$wrapper_struct_name *$arg_tmp_var = malloc(sizeof(thread_arg_$name));') + if expr.is_method { + g.write('$arg_tmp_var->arg0 = ') + // TODO is this needed? + /* + if false && !expr.return_type.is_ptr() { + g.write('&') + } + */ + g.expr(expr.left) + g.writeln(';') + } + for i, arg in expr.args { + g.write('$arg_tmp_var->arg${i + 1} = ') + g.expr(arg.expr) + g.writeln(';') + } + s_ret_typ := g.typ(node.call_expr.return_type) + if g.pref.os == .windows && node.call_expr.return_type != ast.void_type { + g.writeln('$arg_tmp_var->ret_ptr = malloc(sizeof($s_ret_typ));') + } + is_opt := node.call_expr.return_type.has_flag(.optional) + mut gohandle_name := '' + if node.call_expr.return_type == ast.void_type { + gohandle_name = if is_opt { '__v_thread_Option_void' } else { '__v_thread' } + } else { + opt := if is_opt { 'Option_' } else { '' } + gohandle_name = '__v_thread_$opt${g.table.get_type_symbol(g.unwrap_generic(node.call_expr.return_type)).cname}' + } + if g.pref.os == .windows { + simple_handle := if node.is_expr && node.call_expr.return_type != ast.void_type { + 'thread_handle_$tmp' + } else { + 'thread_$tmp' + } + g.writeln('HANDLE $simple_handle = CreateThread(0,0, (LPTHREAD_START_ROUTINE)$wrapper_fn_name, $arg_tmp_var, 0,0);') + g.writeln('if (!$simple_handle) panic_lasterr(tos3("`go ${name}()`: "));') + if node.is_expr && node.call_expr.return_type != ast.void_type { + g.writeln('$gohandle_name thread_$tmp = {') + g.writeln('\t.ret_ptr = $arg_tmp_var->ret_ptr,') + g.writeln('\t.handle = thread_handle_$tmp') + g.writeln('};') + } + if !node.is_expr { + g.writeln('CloseHandle(thread_$tmp);') + } + } else { + g.writeln('pthread_t thread_$tmp;') + g.writeln('int ${tmp}_thr_res = pthread_create(&thread_$tmp, NULL, (void*)$wrapper_fn_name, $arg_tmp_var);') + g.writeln('if (${tmp}_thr_res) panic_error_number(tos3("`go ${name}()`: "), ${tmp}_thr_res);') + if !node.is_expr { + g.writeln('pthread_detach(thread_$tmp);') + } + } + g.writeln('// endgo\n') + if node.is_expr { + handle = 'thread_$tmp' + // create wait handler for this return type if none exists + waiter_fn_name := gohandle_name + '_wait' + if waiter_fn_name !in g.waiter_fns { + g.gowrappers.writeln('\n$s_ret_typ ${waiter_fn_name}($gohandle_name thread) {') + mut c_ret_ptr_ptr := 'NULL' + if node.call_expr.return_type != ast.void_type { + g.gowrappers.writeln('\t$s_ret_typ* ret_ptr;') + c_ret_ptr_ptr = '&ret_ptr' + } + if g.pref.os == .windows { + if node.call_expr.return_type == ast.void_type { + g.gowrappers.writeln('\tu32 stat = WaitForSingleObject(thread, INFINITE);') + } else { + g.gowrappers.writeln('\tu32 stat = WaitForSingleObject(thread.handle, INFINITE);') + g.gowrappers.writeln('\tret_ptr = thread.ret_ptr;') + } + } else { + g.gowrappers.writeln('\tint stat = pthread_join(thread, (void **)$c_ret_ptr_ptr);') + } + g.gowrappers.writeln('\tif (stat != 0) { _v_panic(_SLIT("unable to join thread")); }') + if g.pref.os == .windows { + if node.call_expr.return_type == ast.void_type { + g.gowrappers.writeln('\tCloseHandle(thread);') + } else { + g.gowrappers.writeln('\tCloseHandle(thread.handle);') + } + } + if node.call_expr.return_type != ast.void_type { + g.gowrappers.writeln('\t$s_ret_typ ret = *ret_ptr;') + g.gowrappers.writeln('\tfree(ret_ptr);') + g.gowrappers.writeln('\treturn ret;') + } else { + g.gowrappers.writeln('\treturn;') + } + g.gowrappers.writeln('}') + g.waiter_fns << waiter_fn_name + } + } + // Register the wrapper type and function + if name !in g.threaded_fns { + g.type_definitions.writeln('\ntypedef struct $wrapper_struct_name {') + if expr.is_method { + styp := g.typ(expr.receiver_type) + g.type_definitions.writeln('\t$styp arg0;') + } + need_return_ptr := g.pref.os == .windows && node.call_expr.return_type != ast.void_type + if expr.args.len == 0 && !need_return_ptr { + g.type_definitions.writeln('EMPTY_STRUCT_DECLARATION;') + } else { + for i, arg in expr.args { + styp := g.typ(arg.typ) + g.type_definitions.writeln('\t$styp arg${i + 1};') + } + } + if need_return_ptr { + g.type_definitions.writeln('\tvoid* ret_ptr;') + } + g.type_definitions.writeln('} $wrapper_struct_name;') + thread_ret_type := if g.pref.os == .windows { 'u32' } else { 'void*' } + g.type_definitions.writeln('$thread_ret_type ${wrapper_fn_name}($wrapper_struct_name *arg);') + g.gowrappers.writeln('$thread_ret_type ${wrapper_fn_name}($wrapper_struct_name *arg) {') + if node.call_expr.return_type != ast.void_type { + if g.pref.os == .windows { + g.gowrappers.write_string('\t*(($s_ret_typ*)(arg->ret_ptr)) = ') + } else { + g.gowrappers.writeln('\t$s_ret_typ* ret_ptr = malloc(sizeof($s_ret_typ));') + g.gowrappers.write_string('\t*ret_ptr = ') + } + } else { + g.gowrappers.write_string('\t') + } + if expr.is_method { + unwrapped_rec_type := g.unwrap_generic(expr.receiver_type) + typ_sym := g.table.get_type_symbol(unwrapped_rec_type) + if typ_sym.kind == .interface_ + && (typ_sym.info as ast.Interface).defines_method(expr.name) { + rec_cc_type := g.cc_type(unwrapped_rec_type, false) + receiver_type_name := util.no_dots(rec_cc_type) + g.gowrappers.write_string('${c_name(receiver_type_name)}_name_table[') + g.gowrappers.write_string('arg->arg0') + dot := if expr.left_type.is_ptr() { '->' } else { '.' } + mname := c_name(expr.name) + g.gowrappers.write_string('${dot}_typ]._method_${mname}(') + g.gowrappers.write_string('arg->arg0') + g.gowrappers.write_string('${dot}_object') + } else { + g.gowrappers.write_string('${name}(') + g.gowrappers.write_string('arg->arg0') + } + if expr.args.len > 0 { + g.gowrappers.write_string(', ') + } + } else { + g.gowrappers.write_string('${name}(') + } + if expr.args.len > 0 { + mut has_cast := false + for i in 0 .. expr.args.len { + if g.table.get_type_symbol(expr.expected_arg_types[i]).kind == .interface_ + && g.table.get_type_symbol(expr.args[i].typ).kind != .interface_ { + has_cast = true + break + } + } + if has_cast { + pos := g.out.len + g.call_args(expr) + mut call_args_str := g.out.after(pos) + g.out.go_back(call_args_str.len) + mut rep_group := []string{cap: 2 * expr.args.len} + for i in 0 .. expr.args.len { + rep_group << g.expr_string(expr.args[i].expr) + rep_group << 'arg->arg${i + 1}' + } + call_args_str = call_args_str.replace_each(rep_group) + g.gowrappers.write_string(call_args_str) + } else { + for i in 0 .. expr.args.len { + g.gowrappers.write_string('arg->arg${i + 1}') + if i != expr.args.len - 1 { + g.gowrappers.write_string(', ') + } + } + } + } + g.gowrappers.writeln(');') + g.gowrappers.writeln('\tfree(arg);') + if g.pref.os != .windows && node.call_expr.return_type != ast.void_type { + g.gowrappers.writeln('\treturn ret_ptr;') + } else { + g.gowrappers.writeln('\treturn 0;') + } + g.gowrappers.writeln('}') + g.threaded_fns << name + } + if node.is_expr { + g.empty_line = false + g.write(line) + g.write(handle) + } +} + +fn (mut g Gen) as_cast(node ast.AsCast) { + // Make sure the sum type can be cast to this type (the types + // are the same), otherwise panic. + // g.insert_before(' + styp := g.typ(node.typ) + sym := g.table.get_type_symbol(node.typ) + expr_type_sym := g.table.get_type_symbol(node.expr_type) + if expr_type_sym.info is ast.SumType { + dot := if node.expr_type.is_ptr() { '->' } else { '.' } + g.write('/* as */ *($styp*)__as_cast(') + g.write('(') + g.expr(node.expr) + g.write(')') + g.write(dot) + g.write('_$sym.cname,') + g.write('(') + g.expr(node.expr) + g.write(')') + g.write(dot) + // g.write('typ, /*expected:*/$node.typ)') + sidx := g.type_sidx(node.typ) + expected_sym := g.table.get_type_symbol(node.typ) + g.write('_typ, $sidx) /*expected idx: $sidx, name: $expected_sym.name */ ') + + // fill as cast name table + for variant in expr_type_sym.info.variants { + idx := u32(variant).str() + if idx in g.as_cast_type_names { + continue + } + variant_sym := g.table.get_type_symbol(variant) + g.as_cast_type_names[idx] = variant_sym.name + } + } else { + g.expr(node.expr) + } +} + +fn (g Gen) as_cast_name_table() string { + if g.as_cast_type_names.len == 0 { + return 'new_array_from_c_array(1, 1, sizeof(VCastTypeIndexName), _MOV((VCastTypeIndexName[1]){(VCastTypeIndexName){.tindex = 0,.tname = _SLIT("unknown")}}));\n' + } + mut name_ast := strings.new_builder(1024) + casts_len := g.as_cast_type_names.len + 1 + name_ast.writeln('new_array_from_c_array($casts_len, $casts_len, sizeof(VCastTypeIndexName), _MOV((VCastTypeIndexName[$casts_len]){') + name_ast.writeln('\t\t (VCastTypeIndexName){.tindex = 0, .tname = _SLIT("unknown")}') + for key, value in g.as_cast_type_names { + name_ast.writeln('\t\t, (VCastTypeIndexName){.tindex = $key, .tname = _SLIT("$value")}') + } + name_ast.writeln('\t}));\n') + return name_ast.str() +} + +// Generates interface table and interface indexes +fn (mut g Gen) interface_table() string { + mut sb := strings.new_builder(100) + for ityp in g.table.type_symbols { + if ityp.kind != .interface_ { + continue + } + inter_info := ityp.info as ast.Interface + if inter_info.is_generic { + continue + } + // interface_name is for example Speaker + interface_name := ityp.cname + // generate a struct that references interface methods + methods_struct_name := 'struct _${interface_name}_interface_methods' + mut methods_struct_def := strings.new_builder(100) + methods_struct_def.writeln('$methods_struct_name {') + mut methodidx := map[string]int{} + for k, method in inter_info.methods { + methodidx[method.name] = k + ret_styp := g.typ(method.return_type) + methods_struct_def.write_string('\t$ret_styp (*_method_${c_name(method.name)})(void* _') + // the first param is the receiver, it's handled by `void*` above + for i in 1 .. method.params.len { + arg := method.params[i] + methods_struct_def.write_string(', ${g.typ(arg.typ)} $arg.name') + } + // TODO g.fn_args(method.args[1..], method.is_variadic) + methods_struct_def.writeln(');') + } + methods_struct_def.writeln('};') + // generate an array of the interface methods for the structs using the interface + // as well as case functions from the struct to the interface + mut methods_struct := strings.new_builder(100) + // + iname_table_length := inter_info.types.len + if iname_table_length == 0 { + // msvc can not process `static struct x[0] = {};` + methods_struct.writeln('$methods_struct_name ${interface_name}_name_table[1];') + } else { + if g.pref.build_mode != .build_module { + methods_struct.writeln('$methods_struct_name ${interface_name}_name_table[$iname_table_length] = {') + } else { + methods_struct.writeln('$methods_struct_name ${interface_name}_name_table[$iname_table_length];') + } + } + mut cast_functions := strings.new_builder(100) + mut methods_wrapper := strings.new_builder(100) + methods_wrapper.writeln('// Methods wrapper for interface "$interface_name"') + mut already_generated_mwrappers := map[string]int{} + iinidx_minimum_base := 1000 // NB: NOT 0, to avoid map entries set to 0 later, so `if already_generated_mwrappers[name] > 0 {` works. + mut current_iinidx := iinidx_minimum_base + for st in inter_info.types { + st_sym := g.table.get_type_symbol(st) + // cctype is the Cleaned Concrete Type name, *without ptr*, + // i.e. cctype is always just Cat, not Cat_ptr: + cctype := g.cc_type(st, true) + $if debug_interface_table ? { + eprintln( + '>> interface name: $ityp.name | concrete type: $st.debug() | st symname: ' + + st_sym.name) + } + // Speaker_Cat_index = 0 + interface_index_name := '_${interface_name}_${cctype}_index' + if already_generated_mwrappers[interface_index_name] > 0 { + continue + } + already_generated_mwrappers[interface_index_name] = current_iinidx + current_iinidx++ + if ityp.name != 'vweb.DbInterface' { // TODO remove this + // eprintln('>>> current_iinidx: ${current_iinidx-iinidx_minimum_base} | interface_index_name: $interface_index_name') + sb.writeln('static $interface_name I_${cctype}_to_Interface_${interface_name}($cctype* x);') + mut cast_struct := strings.new_builder(100) + cast_struct.writeln('($interface_name) {') + cast_struct.writeln('\t\t._$cctype = x,') + cast_struct.writeln('\t\t._typ = $interface_index_name,') + for field in inter_info.fields { + cname := c_name(field.name) + field_styp := g.typ(field.typ) + if _ := st_sym.find_field(field.name) { + cast_struct.writeln('\t\t.$cname = ($field_styp*)((char*)x + __offsetof_ptr(x, $cctype, $cname)),') + } else { + // the field is embedded in another struct + cast_struct.write_string('\t\t.$cname = ($field_styp*)((char*)x') + if st == ast.voidptr_type { + cast_struct.write_string('/*.... ast.voidptr_type */') + } else { + for embed_type in st_sym.struct_info().embeds { + embed_sym := g.table.get_type_symbol(embed_type) + if _ := embed_sym.find_field(field.name) { + cast_struct.write_string(' + __offsetof_ptr(x, $cctype, $embed_sym.embed_name()) + __offsetof_ptr(x, $embed_sym.cname, $cname)') + break + } + } + } + cast_struct.writeln('),') + } + } + cast_struct.write_string('\t}') + cast_struct_str := cast_struct.str() + + cast_functions.writeln(' +// Casting functions for converting "$cctype" to interface "$interface_name" +static inline $interface_name I_${cctype}_to_Interface_${interface_name}($cctype* x) { + return $cast_struct_str; +}') + } + + if g.pref.build_mode != .build_module { + methods_struct.writeln('\t{') + } + if st == ast.voidptr_type { + for mname, _ in methodidx { + if g.pref.build_mode != .build_module { + methods_struct.writeln('\t\t._method_${c_name(mname)} = (void*) 0,') + } + } + } + mut methods := st_sym.methods + match st_sym.info { + ast.Struct, ast.Interface, ast.SumType { + if st_sym.info.parent_type.has_flag(.generic) { + parent_sym := g.table.get_type_symbol(st_sym.info.parent_type) + for method in parent_sym.methods { + if method.name in methodidx { + methods << st_sym.find_method_with_generic_parent(method.name) or { + continue + } + } + } + } + } + else {} + } + for method in methods { + mut name := method.name + if inter_info.parent_type.has_flag(.generic) { + parent_sym := g.table.get_type_symbol(inter_info.parent_type) + match mut parent_sym.info { + ast.Struct, ast.Interface, ast.SumType { + name = g.generic_fn_name(parent_sym.info.concrete_types, method.name, + false) + } + else {} + } + } + + if method.name !in methodidx { + // a method that is not part of the interface should be just skipped + continue + } + // .speak = Cat_speak + mut method_call := '${cctype}_$name' + if !method.params[0].typ.is_ptr() { + // inline void Cat_speak_Interface_Animal_method_wrapper(Cat c) { return Cat_speak(*c); } + iwpostfix := '_Interface_${interface_name}_method_wrapper' + methods_wrapper.write_string('static inline ${g.typ(method.return_type)} $method_call${iwpostfix}(') + // + params_start_pos := g.out.len + mut params := method.params.clone() + // hack to mutate typ + params[0] = ast.Param{ + ...params[0] + typ: params[0].typ.set_nr_muls(1) + } + fargs, _, _ := g.fn_args(params, false, voidptr(0)) // second argument is ignored anyway + methods_wrapper.write_string(g.out.cut_last(g.out.len - params_start_pos)) + methods_wrapper.writeln(') {') + methods_wrapper.write_string('\t') + if method.return_type != ast.void_type { + methods_wrapper.write_string('return ') + } + methods_wrapper.writeln('${method_call}(*${fargs.join(', ')});') + methods_wrapper.writeln('}') + // .speak = Cat_speak_Interface_Animal_method_wrapper + method_call += iwpostfix + } + if g.pref.build_mode != .build_module { + methods_struct.writeln('\t\t._method_${c_name(method.name)} = (void*) $method_call,') + } + } + if g.pref.build_mode != .build_module { + methods_struct.writeln('\t},') + } + iin_idx := already_generated_mwrappers[interface_index_name] - iinidx_minimum_base + if g.pref.build_mode != .build_module { + sb.writeln('int $interface_index_name = $iin_idx;') + } else { + sb.writeln('int $interface_index_name;') + } + } + sb.writeln('// ^^^ number of types for interface $interface_name: ${current_iinidx - iinidx_minimum_base}') + if iname_table_length == 0 { + methods_struct.writeln('') + } else { + if g.pref.build_mode != .build_module { + methods_struct.writeln('};') + } + } + // add line return after interface index declarations + sb.writeln('') + if inter_info.methods.len > 0 { + sb.writeln(methods_wrapper.str()) + sb.writeln(methods_struct_def.str()) + sb.writeln(methods_struct.str()) + } + sb.writeln(cast_functions.str()) + } + return sb.str() +} + +fn (mut g Gen) panic_debug_info(pos token.Position) (int, string, string, string) { + paline := pos.line_nr + 1 + if isnil(g.fn_decl) { + return paline, '', 'main', 'C._vinit' + } + pafile := g.fn_decl.file.replace('\\', '/') + pafn := g.fn_decl.name.after('.') + pamod := g.fn_decl.modname() + return paline, pafile, pamod, pafn +} + +pub fn get_guarded_include_text(iname string, imessage string) string { + res := ' + |#if defined(__has_include) + | + |#if __has_include($iname) + |#include $iname + |#else + |#error VERROR_MESSAGE $imessage + |#endif + | + |#else + |#include $iname + |#endif + '.strip_margin() + return res +} + +fn (mut g Gen) trace(fbase string, message string) { + if g.file.path_base == fbase { + println('> g.trace | ${fbase:-10s} | $message') + } +} + +pub fn (mut g Gen) get_array_depth(el_typ ast.Type) int { + typ := g.unwrap_generic(el_typ) + sym := g.table.get_final_type_symbol(typ) + if sym.kind == .array { + info := sym.info as ast.Array + return 1 + g.get_array_depth(info.elem_type) + } else { + return 0 + } +} + +// returns true if `t` includes any pointer(s) - during garbage collection heap regions +// that contain no pointers do not have to be scanned +pub fn (mut g Gen) contains_ptr(el_typ ast.Type) bool { + if el_typ.is_ptr() || el_typ.is_pointer() { + return true + } + typ := g.unwrap_generic(el_typ) + if typ.is_ptr() { + return true + } + sym := g.table.get_final_type_symbol(typ) + if sym.language != .v { + return true + } + match sym.kind { + .i8, .i16, .int, .i64, .byte, .u16, .u32, .u64, .f32, .f64, .char, .size_t, .rune, .bool, + .enum_ { + return false + } + .array_fixed { + info := sym.info as ast.ArrayFixed + return g.contains_ptr(info.elem_type) + } + .struct_ { + info := sym.info as ast.Struct + for embed in info.embeds { + if g.contains_ptr(embed) { + return true + } + } + for field in info.fields { + if g.contains_ptr(field.typ) { + return true + } + } + return false + } + .aggregate { + info := sym.info as ast.Aggregate + for atyp in info.types { + if g.contains_ptr(atyp) { + return true + } + } + return false + } + .multi_return { + info := sym.info as ast.MultiReturn + for mrtyp in info.types { + if g.contains_ptr(mrtyp) { + return true + } + } + return false + } + else { + return true + } + } +} + +fn (mut g Gen) check_noscan(elem_typ ast.Type) string { + if g.pref.gc_mode in [.boehm_full_opt, .boehm_incr_opt] { + if !g.contains_ptr(elem_typ) { + return '_noscan' + } + } + return '' +} diff --git a/v_windows/v/old/vlib/v/gen/c/cheaders.v b/v_windows/v/old/vlib/v/gen/c/cheaders.v new file mode 100644 index 0000000..7db49bd --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/c/cheaders.v @@ -0,0 +1,639 @@ +module c + +// NB: @@@ here serve as placeholders. +// They will be replaced with correct strings +// for each constant, during C code generation. + +// V_COMMIT_HASH is generated by cmd/tools/gen_vc.v . +const c_commit_hash_default = ' +#ifndef V_COMMIT_HASH + #define V_COMMIT_HASH "@@@" +#endif +' + +// V_CURRENT_COMMIT_HASH is updated, when V is rebuilt inside a git repo. +const c_current_commit_hash_default = ' +#ifndef V_CURRENT_COMMIT_HASH + #define V_CURRENT_COMMIT_HASH "@@@" +#endif +' + +const c_concurrency_helpers = ' +typedef struct __shared_map __shared_map; +struct __shared_map { map val; sync__RwMutex mtx; }; +static inline voidptr __dup_shared_map(voidptr src, int sz) { + __shared_map* dest = memdup(src, sz); + sync__RwMutex_init(&dest->mtx); + return dest; +} +typedef struct __shared_array __shared_array; +struct __shared_array { array val; sync__RwMutex mtx; }; +static inline voidptr __dup_shared_array(voidptr src, int sz) { + __shared_array* dest = memdup(src, sz); + sync__RwMutex_init(&dest->mtx); + return dest; +} +static inline void __sort_ptr(uintptr_t a[], bool b[], int l) { + for (int i=1; i0 && a[j-1] > ins) { + a[j] = a[j-1]; + b[j] = b[j-1]; + j--; + } + a[j] = ins; + b[j] = insb; + } +} +' + +const c_common_macros = ' +#define EMPTY_VARG_INITIALIZATION 0 +#define EMPTY_STRUCT_DECLARATION +#define EMPTY_STRUCT_INITIALIZATION +// Due to a tcc bug, the length of an array needs to be specified, but GCC crashes if it is... +#define EMPTY_ARRAY_OF_ELEMS(x,n) (x[]) +#define TCCSKIP(x) x + +#define __NOINLINE __attribute__((noinline)) +#define __IRQHANDLER __attribute__((interrupt)) + +#define __V_architecture 0 +#if defined(__x86_64__) + #define __V_amd64 1 + #undef __V_architecture + #define __V_architecture 1 +#endif + +#if defined(__aarch64__) || defined(__arm64__) + #define __V_arm64 1 + #undef __V_architecture + #define __V_architecture 2 +#endif + +// Using just __GNUC__ for detecting gcc, is not reliable because other compilers define it too: +#ifdef __GNUC__ + #define __V_GCC__ +#endif +#ifdef __TINYC__ + #undef __V_GCC__ +#endif +#ifdef __cplusplus + #undef __V_GCC__ +#endif +#ifdef __clang__ + #undef __V_GCC__ +#endif +#ifdef _MSC_VER + #undef __V_GCC__ + #undef EMPTY_STRUCT_INITIALIZATION + #define EMPTY_STRUCT_INITIALIZATION 0 +#endif + +#ifdef __TINYC__ + #undef EMPTY_STRUCT_DECLARATION + #define EMPTY_STRUCT_DECLARATION char _dummy + #undef EMPTY_ARRAY_OF_ELEMS + #define EMPTY_ARRAY_OF_ELEMS(x,n) (x[n]) + #undef __NOINLINE + #undef __IRQHANDLER + // tcc does not support inlining at all + #define __NOINLINE + #define __IRQHANDLER + #undef TCCSKIP + #define TCCSKIP(x) + // #include + #ifndef _WIN32 + #include + int tcc_backtrace(const char *fmt, ...); + #endif +#endif + +// Use __offsetof_ptr instead of __offset_of, when you *do* have a valid pointer, to avoid UB: +#ifndef __offsetof_ptr + #define __offsetof_ptr(ptr,PTYPE,FIELDNAME) ((size_t)((byte *)&((PTYPE *)ptr)->FIELDNAME - (byte *)ptr)) +#endif + +// for __offset_of +#ifndef __offsetof + #define __offsetof(PTYPE,FIELDNAME) ((size_t)((char *)&((PTYPE *)0)->FIELDNAME - (char *)0)) +#endif + +#define OPTION_CAST(x) (x) + +#ifndef V64_PRINTFORMAT + #ifdef PRIx64 + #define V64_PRINTFORMAT "0x%"PRIx64 + #elif defined(__WIN32__) + #define V64_PRINTFORMAT "0x%I64x" + #elif defined(__linux__) && defined(__LP64__) + #define V64_PRINTFORMAT "0x%lx" + #else + #define V64_PRINTFORMAT "0x%llx" + #endif +#endif + +#if defined(_WIN32) || defined(__CYGWIN__) + #define VV_EXPORTED_SYMBOL extern __declspec(dllexport) + #define VV_LOCAL_SYMBOL static +#else + // 4 < gcc < 5 is used by some older Ubuntu LTS and Centos versions, + // and does not support __has_attribute(visibility) ... + #ifndef __has_attribute + #define __has_attribute(x) 0 // Compatibility with non-clang compilers. + #endif + #if (defined(__GNUC__) && (__GNUC__ >= 4)) || (defined(__clang__) && __has_attribute(visibility)) + #ifdef ARM + #define VV_EXPORTED_SYMBOL extern __attribute__((externally_visible,visibility("default"))) + #else + #define VV_EXPORTED_SYMBOL extern __attribute__((visibility("default"))) + #endif + #define VV_LOCAL_SYMBOL __attribute__ ((visibility ("hidden"))) + #else + #define VV_EXPORTED_SYMBOL extern + #define VV_LOCAL_SYMBOL static + #endif +#endif + +#ifdef __cplusplus + #include + #define _MOV std::move +#else + #define _MOV +#endif + +// tcc does not support has_include properly yet, turn it off completely +#if defined(__TINYC__) && defined(__has_include) +#undef __has_include +#endif + +#if !defined(VNORETURN) + #if defined(__TINYC__) + #include + #define VNORETURN noreturn + #endif + # if !defined(__TINYC__) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + # define VNORETURN _Noreturn + # elif defined(__GNUC__) && __GNUC__ >= 2 + # define VNORETURN __attribute__((noreturn)) + # endif + #ifndef VNORETURN + #define VNORETURN + #endif +#endif + +#if !defined(VUNREACHABLE) + #if defined(__GNUC__) && !defined(__clang__) + #define V_GCC_VERSION (__GNUC__ * 10000L + __GNUC_MINOR__ * 100L + __GNUC_PATCHLEVEL__) + #if (V_GCC_VERSION >= 40500L) + #define VUNREACHABLE() do { __builtin_unreachable(); } while (0) + #endif + #endif + #if defined(__clang__) && defined(__has_builtin) + #if __has_builtin(__builtin_unreachable) + #define VUNREACHABLE() do { __builtin_unreachable(); } while (0) + #endif + #endif + #ifndef VUNREACHABLE + #define VUNREACHABLE() do { } while (0) + #endif + #if defined(__FreeBSD__) && defined(__TINYC__) + #define VUNREACHABLE() do { } while (0) + #endif +#endif + +//likely and unlikely macros +#if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__clang__) + #define _likely_(x) __builtin_expect(x,1) + #define _unlikely_(x) __builtin_expect(x,0) +#else + #define _likely_(x) (x) + #define _unlikely_(x) (x) +#endif + +' + +const c_unsigned_comparison_functions = ' +// unsigned/signed comparisons +static inline bool _us32_gt(uint32_t a, int32_t b) { return a > INT32_MAX || (int32_t)a > b; } +static inline bool _us32_ge(uint32_t a, int32_t b) { return a >= INT32_MAX || (int32_t)a >= b; } +static inline bool _us32_eq(uint32_t a, int32_t b) { return a <= INT32_MAX && (int32_t)a == b; } +static inline bool _us32_ne(uint32_t a, int32_t b) { return a > INT32_MAX || (int32_t)a != b; } +static inline bool _us32_le(uint32_t a, int32_t b) { return a <= INT32_MAX && (int32_t)a <= b; } +static inline bool _us32_lt(uint32_t a, int32_t b) { return a < INT32_MAX && (int32_t)a < b; } +static inline bool _us64_gt(uint64_t a, int64_t b) { return a > INT64_MAX || (int64_t)a > b; } +static inline bool _us64_ge(uint64_t a, int64_t b) { return a >= INT64_MAX || (int64_t)a >= b; } +static inline bool _us64_eq(uint64_t a, int64_t b) { return a <= INT64_MAX && (int64_t)a == b; } +static inline bool _us64_ne(uint64_t a, int64_t b) { return a > INT64_MAX || (int64_t)a != b; } +static inline bool _us64_le(uint64_t a, int64_t b) { return a <= INT64_MAX && (int64_t)a <= b; } +static inline bool _us64_lt(uint64_t a, int64_t b) { return a < INT64_MAX && (int64_t)a < b; } +' + +const c_helper_macros = '//============================== HELPER C MACROS =============================*/ +// _SLIT0 is used as NULL string for literal arguments +// `"" s` is used to enforce a string literal argument +#define _SLIT0 (string){.str=(byteptr)(""), .len=0, .is_lit=1} +#define _SLIT(s) ((string){.str=(byteptr)("" s), .len=(sizeof(s)-1), .is_lit=1}) +#define _SLEN(s, n) ((string){.str=(byteptr)("" s), .len=n, .is_lit=1}) + +// take the address of an rvalue +#define ADDR(type, expr) (&((type[]){expr}[0])) + +// copy something to the heap +#define HEAP(type, expr) ((type*)memdup((void*)&((type[]){expr}[0]), sizeof(type))) +#define HEAP_noscan(type, expr) ((type*)memdup_noscan((void*)&((type[]){expr}[0]), sizeof(type))) + +#define _PUSH_MANY(arr, val, tmp, tmp_typ) {tmp_typ tmp = (val); array_push_many(arr, tmp.data, tmp.len);} +#define _PUSH_MANY_noscan(arr, val, tmp, tmp_typ) {tmp_typ tmp = (val); array_push_many_noscan(arr, tmp.data, tmp.len);} +' + +const c_headers = c_helper_macros + c_unsigned_comparison_functions + c_common_macros + + r' +// c_headers +typedef int (*qsort_callback_func)(const void*, const void*); +#include // TODO remove all these includes, define all function signatures and types manually +#include +#include + +#if defined(_WIN32) || defined(__CYGWIN__) + #define VV_EXPORTED_SYMBOL extern __declspec(dllexport) + #define VV_LOCAL_SYMBOL static +#else + // 4 < gcc < 5 is used by some older Ubuntu LTS and Centos versions, + // and does not support __has_attribute(visibility) ... + #ifndef __has_attribute + #define __has_attribute(x) 0 // Compatibility with non-clang compilers. + #endif + #if (defined(__GNUC__) && (__GNUC__ >= 4)) || (defined(__clang__) && __has_attribute(visibility)) + #ifdef ARM + #define VV_EXPORTED_SYMBOL extern __attribute__((externally_visible,visibility("default"))) + #else + #define VV_EXPORTED_SYMBOL extern __attribute__((visibility("default"))) + #endif + #define VV_LOCAL_SYMBOL __attribute__ ((visibility ("hidden"))) + #else + #define VV_EXPORTED_SYMBOL extern + #define VV_LOCAL_SYMBOL static + #endif +#endif + +#if defined(__TINYC__) && defined(__has_include) +// tcc does not support has_include properly yet, turn it off completely +#undef __has_include +#endif + +#ifndef _WIN32 + #if defined __has_include + #if __has_include () + #include + #else + // Most probably musl OR __ANDROID__ ... + int backtrace (void **__array, int __size) { return 0; } + char **backtrace_symbols (void *const *__array, int __size){ return 0; } + void backtrace_symbols_fd (void *const *__array, int __size, int __fd){} + #endif + #endif +#endif + +#include // for va_list + +//================================== GLOBALS =================================*/ +int load_so(byteptr); +void reload_so(); +void _vinit(int ___argc, voidptr ___argv); +void _vcleanup(); +#define sigaction_size sizeof(sigaction); +#define _ARR_LEN(a) ( (sizeof(a)) / (sizeof(a[0])) ) + +void v_free(voidptr ptr); +voidptr memdup(voidptr src, int sz); + +#if INTPTR_MAX == INT32_MAX + #define TARGET_IS_32BIT 1 +#elif INTPTR_MAX == INT64_MAX + #define TARGET_IS_64BIT 1 +#else + #error "The environment is not 32 or 64-bit." +#endif + +#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ || defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN || defined(__BIG_ENDIAN__) || defined(__ARMEB__) || defined(__THUMBEB__) || defined(__AARCH64EB__) || defined(_MIBSEB) || defined(__MIBSEB) || defined(__MIBSEB__) + #define TARGET_ORDER_IS_BIG 1 +#elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ || defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN || defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || defined(__THUMBEL__) || defined(__AARCH64EL__) || defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__) || defined(_M_AMD64) || defined(_M_X64) || defined(_M_IX86) + #define TARGET_ORDER_IS_LITTLE 1 +#else + #error "Unknown architecture endianness" +#endif + +#ifndef _WIN32 + #include + #include // tolower + #include + #include // sleep + extern char **environ; +#endif + +#if defined(__CYGWIN__) && !defined(_WIN32) + #error Cygwin is not supported, please use MinGW or Visual Studio. +#endif + +#if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__vinix__) || defined(__serenity__) || defined(__sun) + #include + #include // os__wait uses wait on nix +#endif + +#ifdef __OpenBSD__ + #include + #include + #include // os__wait uses wait on nix +#endif + +#ifdef __NetBSD__ + #include // os__wait uses wait on nix +#endif + +#ifdef _WIN32 + #define WINVER 0x0600 + #ifdef _WIN32_WINNT + #undef _WIN32_WINNT + #endif + #define _WIN32_WINNT 0x0600 + #ifndef WIN32_FULL + #define WIN32_LEAN_AND_MEAN + #endif + #ifndef _UNICODE + #define _UNICODE + #endif + #ifndef UNICODE + #define UNICODE + #endif + #include + + #include // _waccess + #include // _wgetcwd + + #ifdef _MSC_VER + // On MSVC these are the same (as long as /volatile:ms is passed) + #define _Atomic volatile + + // MSVC cannot parse some things properly + #undef EMPTY_STRUCT_DECLARATION + #undef OPTION_CAST + + #define EMPTY_STRUCT_DECLARATION char __pad + #define OPTION_CAST(x) + #undef __NOINLINE + #undef __IRQHANDLER + #define __NOINLINE __declspec(noinline) + #define __IRQHANDLER __declspec(naked) + + #include + #pragma comment(lib, "Dbghelp") + #endif +#else + #include + #ifndef PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP + // musl does not have that + #define pthread_rwlockattr_setkind_np(a, b) + #endif +#endif + +// g_live_info is used by live.info() +static void* g_live_info = NULL; + +#if defined(__MINGW32__) || defined(__MINGW64__) || (defined(_WIN32) && defined(__TINYC__)) + #undef PRId64 + #undef PRIi64 + #undef PRIo64 + #undef PRIu64 + #undef PRIx64 + #undef PRIX64 + #define PRId64 "lld" + #define PRIi64 "lli" + #define PRIo64 "llo" + #define PRIu64 "llu" + #define PRIx64 "llx" + #define PRIX64 "llX" +#endif + +#ifdef _VFREESTANDING +#undef _VFREESTANDING +#endif +' + +const c_builtin_types = ' +//================================== builtin types ================================*/ +typedef int64_t i64; +typedef int16_t i16; +typedef int8_t i8; +typedef uint64_t u64; +typedef uint32_t u32; +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint8_t byte; +typedef uint32_t rune; +typedef float f32; +typedef double f64; +typedef int64_t int_literal; +typedef double float_literal; +typedef unsigned char* byteptr; +typedef void* voidptr; +typedef char* charptr; +typedef byte array_fixed_byte_300 [300]; + +typedef struct sync__Channel* chan; + +#ifndef __cplusplus + #ifndef bool + typedef byte bool; + #define true 1 + #define false 0 + #endif +#endif + +typedef u64 (*MapHashFn)(voidptr); +typedef bool (*MapEqFn)(voidptr, voidptr); +typedef void (*MapCloneFn)(voidptr, voidptr); +typedef void (*MapFreeFn)(voidptr); +' + +const c_bare_headers = c_helper_macros + c_unsigned_comparison_functions + c_common_macros + + ' +#define _VFREESTANDING + +typedef long unsigned int size_t; + +// Memory allocation related headers +void *malloc(size_t size); +void *calloc(size_t nitems, size_t size); +void *realloc(void *ptr, size_t size); +void *memcpy(void *dest, void *src, size_t n); +void *memset(void *s, int c, size_t n); +void *memmove(void *dest, void *src, size_t n); + +// varargs implementation, TODO: works on tcc and gcc, but is very unportable and hacky +typedef __builtin_va_list va_list; +#define va_start(a, b) __builtin_va_start(a, b) +#define va_end(a) __builtin_va_end(a) +#define va_arg(a, b) __builtin_va_arg(a, b) +#define va_copy(a, b) __builtin_va_copy(a, b) + +//================================== GLOBALS =================================*/ +int load_so(byteptr); +void reload_so(); +void _vinit(int ___argc, voidptr ___argv); +void _vcleanup(); +#define sigaction_size sizeof(sigaction); +#define _ARR_LEN(a) ( (sizeof(a)) / (sizeof(a[0])) ) + +void v_free(voidptr ptr); +voidptr memdup(voidptr src, int sz); + +' + +const c_wyhash_headers = ' +// ============== wyhash ============== +#ifndef wyhash_final_version_3 +#define wyhash_final_version_3 + +#ifndef WYHASH_CONDOM +// protections that produce different results: +// 1: normal valid behavior +// 2: extra protection against entropy loss (probability=2^-63), aka. "blind multiplication" +#define WYHASH_CONDOM 1 +#endif + +#ifndef WYHASH_32BIT_MUM +// 0: normal version, slow on 32 bit systems +// 1: faster on 32 bit systems but produces different results, incompatible with wy2u0k function +#define WYHASH_32BIT_MUM 0 +#endif + +// includes +#include +#if defined(_MSC_VER) && defined(_M_X64) + #include + #pragma intrinsic(_umul128) +#endif + +// 128bit multiply function +static inline uint64_t _wyrot(uint64_t x) { return (x>>32)|(x<<32); } +static inline void _wymum(uint64_t *A, uint64_t *B){ +#if(WYHASH_32BIT_MUM) + uint64_t hh=(*A>>32)*(*B>>32), hl=(*A>>32)*(uint32_t)*B, lh=(uint32_t)*A*(*B>>32), ll=(uint64_t)(uint32_t)*A*(uint32_t)*B; + #if(WYHASH_CONDOM>1) + *A^=_wyrot(hl)^hh; *B^=_wyrot(lh)^ll; + #else + *A=_wyrot(hl)^hh; *B=_wyrot(lh)^ll; + #endif +#elif defined(__SIZEOF_INT128__) + __uint128_t r=*A; r*=*B; + #if(WYHASH_CONDOM>1) + *A^=(uint64_t)r; *B^=(uint64_t)(r>>64); + #else + *A=(uint64_t)r; *B=(uint64_t)(r>>64); + #endif +#elif defined(_MSC_VER) && defined(_M_X64) + #if(WYHASH_CONDOM>1) + uint64_t a, b; + a=_umul128(*A,*B,&b); + *A^=a; *B^=b; + #else + *A=_umul128(*A,*B,B); + #endif +#else + uint64_t ha=*A>>32, hb=*B>>32, la=(uint32_t)*A, lb=(uint32_t)*B, hi, lo; + uint64_t rh=ha*hb, rm0=ha*lb, rm1=hb*la, rl=la*lb, t=rl+(rm0<<32), c=t>32)+(rm1>>32)+c; + #if(WYHASH_CONDOM>1) + *A^=lo; *B^=hi; + #else + *A=lo; *B=hi; + #endif +#endif +} + +// multiply and xor mix function, aka MUM +static inline uint64_t _wymix(uint64_t A, uint64_t B){ _wymum(&A,&B); return A^B; } + +// endian macros +#ifndef WYHASH_LITTLE_ENDIAN + #ifdef TARGET_ORDER_IS_LITTLE + #define WYHASH_LITTLE_ENDIAN 1 + #else + #define WYHASH_LITTLE_ENDIAN 0 + #endif +#endif + +// read functions +#if (WYHASH_LITTLE_ENDIAN) + static inline uint64_t _wyr8(const uint8_t *p) { uint64_t v; memcpy(&v, p, 8); return v;} + static inline uint64_t _wyr4(const uint8_t *p) { uint32_t v; memcpy(&v, p, 4); return v;} +#elif defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__clang__) + static inline uint64_t _wyr8(const uint8_t *p) { uint64_t v; memcpy(&v, p, 8); return __builtin_bswap64(v);} + static inline uint64_t _wyr4(const uint8_t *p) { uint32_t v; memcpy(&v, p, 4); return __builtin_bswap32(v);} +#elif defined(_MSC_VER) + static inline uint64_t _wyr8(const uint8_t *p) { uint64_t v; memcpy(&v, p, 8); return _byteswap_uint64(v);} + static inline uint64_t _wyr4(const uint8_t *p) { uint32_t v; memcpy(&v, p, 4); return _byteswap_ulong(v);} +#else + static inline uint64_t _wyr8(const uint8_t *p) { + uint64_t v; memcpy(&v, p, 8); + return (((v >> 56) & 0xff)| ((v >> 40) & 0xff00)| ((v >> 24) & 0xff0000)| ((v >> 8) & 0xff000000)| ((v << 8) & 0xff00000000)| ((v << 24) & 0xff0000000000)| ((v << 40) & 0xff000000000000)| ((v << 56) & 0xff00000000000000)); + } + static inline uint64_t _wyr4(const uint8_t *p) { + uint32_t v; memcpy(&v, p, 4); + return (((v >> 24) & 0xff)| ((v >> 8) & 0xff00)| ((v << 8) & 0xff0000)| ((v << 24) & 0xff000000)); + } +#endif +static inline uint64_t _wyr3(const uint8_t *p, size_t k) { return (((uint64_t)p[0])<<16)|(((uint64_t)p[k>>1])<<8)|p[k-1];} +// wyhash main function +static inline uint64_t wyhash(const void *key, size_t len, uint64_t seed, const uint64_t *secret){ + const uint8_t *p=(const uint8_t *)key; seed^=*secret; uint64_t a, b; + if (_likely_(len<=16)) { + if (_likely_(len>=4)) { a=(_wyr4(p)<<32)|_wyr4(p+((len>>3)<<2)); b=(_wyr4(p+len-4)<<32)|_wyr4(p+len-4-((len>>3)<<2)); } + else if (_likely_(len>0)) { a=_wyr3(p,len); b=0; } + else a=b=0; + } else { + size_t i=len; + if (_unlikely_(i>48)) { + uint64_t see1=seed, see2=seed; + do { + seed=_wymix(_wyr8(p)^secret[1],_wyr8(p+8)^seed); + see1=_wymix(_wyr8(p+16)^secret[2],_wyr8(p+24)^see1); + see2=_wymix(_wyr8(p+32)^secret[3],_wyr8(p+40)^see2); + p+=48; i-=48; + } while(_likely_(i>48)); + seed^=see1^see2; + } + while(_unlikely_(i>16)) { seed=_wymix(_wyr8(p)^secret[1],_wyr8(p+8)^seed); i-=16; p+=16; } + a=_wyr8(p+i-16); b=_wyr8(p+i-8); + } + return _wymix(secret[1]^len,_wymix(a^secret[1],b^seed)); +} +// the default secret parameters +static const uint64_t _wyp[4] = {0xa0761d6478bd642full, 0xe7037ed1a0b428dbull, 0x8ebc6af09c88c6e3ull, 0x589965cc75374cc3ull}; + +// a useful 64bit-64bit mix function to produce deterministic pseudo random numbers that can pass BigCrush and PractRand +static inline uint64_t wyhash64(uint64_t A, uint64_t B){ A^=0xa0761d6478bd642full; B^=0xe7037ed1a0b428dbull; _wymum(&A,&B); return _wymix(A^0xa0761d6478bd642full,B^0xe7037ed1a0b428dbull);} + +// the wyrand PRNG that pass BigCrush and PractRand +static inline uint64_t wyrand(uint64_t *seed){ *seed+=0xa0761d6478bd642full; return _wymix(*seed,*seed^0xe7037ed1a0b428dbull);} + +#ifndef __vinix__ +// convert any 64 bit pseudo random numbers to uniform distribution [0,1). It can be combined with wyrand, wyhash64 or wyhash. +static inline double wy2u01(uint64_t r){ const double _wynorm=1.0/(1ull<<52); return (r>>12)*_wynorm;} + +// convert any 64 bit pseudo random numbers to APPROXIMATE Gaussian distribution. It can be combined with wyrand, wyhash64 or wyhash. +static inline double wy2gau(uint64_t r){ const double _wynorm=1.0/(1ull<<20); return ((r&0x1fffff)+((r>>21)&0x1fffff)+((r>>42)&0x1fffff))*_wynorm-3.0;} +#endif + +#if(!WYHASH_32BIT_MUM) +// fast range integer random number generation on [0,k) credit to Daniel Lemire. May not work when WYHASH_32BIT_MUM=1. It can be combined with wyrand, wyhash64 or wyhash. +static inline uint64_t wy2u0k(uint64_t r, uint64_t k){ _wymum(&r,&k); return k; } +#endif +#endif + +#define _IN_MAP(val, m) map_exists(m, val) + +' diff --git a/v_windows/v/old/vlib/v/gen/c/cmain.v b/v_windows/v/old/vlib/v/gen/c/cmain.v new file mode 100644 index 0000000..6e741a6 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/c/cmain.v @@ -0,0 +1,214 @@ +module c + +import v.util +import v.ast + +pub fn (mut g Gen) gen_c_main() { + if !g.has_main { + return + } + if g.pref.is_liveshared { + return + } + g.out.writeln('') + main_fn_start_pos := g.out.len + + is_sokol := 'sokol' in g.table.imports + + if (g.pref.os == .android && g.pref.is_apk) || (g.pref.os == .ios && is_sokol) { + g.gen_c_android_sokol_main() + } else { + g.gen_c_main_header() + g.writeln('\tmain__main();') + g.gen_c_main_footer() + if g.pref.printfn_list.len > 0 && 'main' in g.pref.printfn_list { + println(g.out.after(main_fn_start_pos)) + } + } +} + +fn (mut g Gen) gen_vlines_reset() { + if g.pref.is_vlines { + // At this point, the v files are transpiled. + // The rest is auto generated code, which will not have + // different .v source file/line numbers. + // + // TODO: calculate the proper line here, based on + // the actual C lines in all the buffers + lines_so_far := 1000000 + g.vlines_path = util.vlines_escape_path(g.pref.out_name_c, g.pref.ccompiler) + g.writeln('') + g.writeln('\n// Reset the file/line numbers') + g.writeln('\n#line $lines_so_far "$g.vlines_path"') + g.writeln('') + } +} + +fn (mut g Gen) gen_c_main_function_header() { + if g.pref.os == .windows { + if g.is_gui_app() { + $if msvc { + // This is kinda bad but I dont see a way that is better + g.writeln('#pragma comment(linker, "/SUBSYSTEM:WINDOWS")') + } + // GUI application + g.writeln('int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, LPWSTR cmd_line, int show_cmd){') + g.writeln('\tLPWSTR full_cmd_line = GetCommandLineW(); // NB: do not use cmd_line') + g.writeln('\ttypedef LPWSTR*(WINAPI *cmd_line_to_argv)(LPCWSTR, int*);') + g.writeln('\tHMODULE shell32_module = LoadLibrary(L"shell32.dll");') + g.writeln('\tcmd_line_to_argv CommandLineToArgvW = (cmd_line_to_argv)GetProcAddress(shell32_module, "CommandLineToArgvW");') + g.writeln('\tint ___argc;') + g.writeln('\twchar_t** ___argv = CommandLineToArgvW(full_cmd_line, &___argc);') + } else { + // Console application + g.writeln('int wmain(int ___argc, wchar_t* ___argv[], wchar_t* ___envp[]){') + } + } else { + g.writeln('int main(int ___argc, char** ___argv){') + } +} + +fn (mut g Gen) gen_c_main_header() { + g.gen_c_main_function_header() + if g.pref.gc_mode in [.boehm_full, .boehm_incr, .boehm_full_opt, .boehm_incr_opt, .boehm_leak] { + g.writeln('#if defined(_VGCBOEHM)') + if g.pref.gc_mode == .boehm_leak { + g.writeln('\tGC_set_find_leak(1);') + } + g.writeln('\tGC_INIT();') + if g.pref.gc_mode in [.boehm_incr, .boehm_incr_opt] { + g.writeln('\tGC_enable_incremental();') + } + g.writeln('#endif') + } + g.writeln('\t_vinit(___argc, (voidptr)___argv);') + if g.pref.is_prof { + g.writeln('') + g.writeln('\tatexit(vprint_profile_stats);') + g.writeln('') + } + if g.pref.is_livemain { + g.generate_hotcode_reloading_main_caller() + } +} + +pub fn (mut g Gen) gen_c_main_footer() { + g.writeln('\t_vcleanup();') + g.writeln('\treturn 0;') + g.writeln('}') +} + +pub fn (mut g Gen) gen_c_android_sokol_main() { + // Weave autofree into sokol lifecycle callback(s) + if g.is_autofree { + g.writeln('// Wrapping cleanup/free callbacks for sokol to include _vcleanup() +void (*_vsokol_user_cleanup_ptr)(void); +void (*_vsokol_user_cleanup_cb_ptr)(void *); + +void (_vsokol_cleanup_cb)(void) { + if (_vsokol_user_cleanup_ptr) { + _vsokol_user_cleanup_ptr(); + } + _vcleanup(); +} + +void (_vsokol_cleanup_userdata_cb)(void* user_data) { + if (_vsokol_user_cleanup_cb_ptr) { + _vsokol_user_cleanup_cb_ptr(g_desc.user_data); + } + _vcleanup(); +} +') + } + g.writeln('// The sokol_main entry point on Android +sapp_desc sokol_main(int argc, char* argv[]) { + (void)argc; (void)argv; + + _vinit(argc, (voidptr)argv); + main__main(); +') + if g.is_autofree { + g.writeln(' // Wrap user provided cleanup/free functions for sokol to be able to call _vcleanup() + if (g_desc.cleanup_cb) { + _vsokol_user_cleanup_ptr = g_desc.cleanup_cb; + g_desc.cleanup_cb = _vsokol_cleanup_cb; + } + else if (g_desc.cleanup_userdata_cb) { + _vsokol_user_cleanup_cb_ptr = g_desc.cleanup_userdata_cb; + g_desc.cleanup_userdata_cb = _vsokol_cleanup_userdata_cb; + } +') + } + g.writeln(' return g_desc;') + g.writeln('}') +} + +pub fn (mut g Gen) write_tests_definitions() { + g.includes.writeln('#include // write_tests_main') + g.definitions.writeln('int g_test_oks = 0;') + g.definitions.writeln('int g_test_fails = 0;') + g.definitions.writeln('jmp_buf g_jump_buffer;') +} + +pub fn (mut g Gen) gen_failing_error_propagation_for_test_fn(or_block ast.OrExpr, cvar_name string) { + // in test_() functions, an `opt()?` call is sugar for + // `or { cb_propagate_test_error(@LINE, @FILE, @MOD, @FN, err.msg) }` + // and the test is considered failed + paline, pafile, pamod, pafn := g.panic_debug_info(or_block.pos) + g.writeln('\tmain__cb_propagate_test_error($paline, tos3("$pafile"), tos3("$pamod"), tos3("$pafn"), *(${cvar_name}.err.msg) );') + g.writeln('\tg_test_fails++;') + g.writeln('\tlongjmp(g_jump_buffer, 1);') +} + +pub fn (mut g Gen) gen_failing_return_error_for_test_fn(return_stmt ast.Return, cvar_name string) { + // in test_() functions, a `return error('something')` is sugar for + // `or { err := error('something') cb_propagate_test_error(@LINE, @FILE, @MOD, @FN, err.msg) return err }` + // and the test is considered failed + paline, pafile, pamod, pafn := g.panic_debug_info(return_stmt.pos) + g.writeln('\tmain__cb_propagate_test_error($paline, tos3("$pafile"), tos3("$pamod"), tos3("$pafn"), *(${cvar_name}.err.msg) );') + g.writeln('\tg_test_fails++;') + g.writeln('\tlongjmp(g_jump_buffer, 1);') +} + +pub fn (mut g Gen) gen_c_main_for_tests() { + main_fn_start_pos := g.out.len + g.writeln('') + g.gen_c_main_function_header() + if g.pref.gc_mode in [.boehm_full, .boehm_incr, .boehm_full_opt, .boehm_incr_opt, .boehm_leak] { + g.writeln('#if defined(_VGCBOEHM)') + if g.pref.gc_mode == .boehm_leak { + g.writeln('\tGC_set_find_leak(1);') + } + g.writeln('\tGC_INIT();') + if g.pref.gc_mode in [.boehm_incr, .boehm_incr_opt] { + g.writeln('\tGC_enable_incremental();') + } + g.writeln('#endif') + } + g.writeln('\t_vinit(___argc, (voidptr)___argv);') + all_tfuncs := g.get_all_test_function_names() + if g.pref.is_stats { + g.writeln('\tmain__BenchedTests bt = main__start_testing($all_tfuncs.len, _SLIT("$g.pref.path"));') + } + g.writeln('') + for tname in all_tfuncs { + tcname := util.no_dots(tname) + if g.pref.is_stats { + g.writeln('\tmain__BenchedTests_testing_step_start(&bt, _SLIT("$tcname"));') + } + g.writeln('\tif (!setjmp(g_jump_buffer)) ${tcname}();') + if g.pref.is_stats { + g.writeln('\tmain__BenchedTests_testing_step_end(&bt);') + } + } + g.writeln('') + if g.pref.is_stats { + g.writeln('\tmain__BenchedTests_end_testing(&bt);') + } + g.writeln('\t_vcleanup();') + g.writeln('\treturn g_test_fails > 0;') + g.writeln('}') + if g.pref.printfn_list.len > 0 && 'main' in g.pref.printfn_list { + println(g.out.after(main_fn_start_pos)) + } +} diff --git a/v_windows/v/old/vlib/v/gen/c/comptime.v b/v_windows/v/old/vlib/v/gen/c/comptime.v new file mode 100644 index 0000000..ee45f91 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/c/comptime.v @@ -0,0 +1,676 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module c + +import os +import v.ast +import v.util +import v.pref + +fn (mut g Gen) comptime_selector(node ast.ComptimeSelector) { + g.expr(node.left) + if node.left_type.is_ptr() { + g.write('->') + } else { + g.write('.') + } + // check for field.name + if node.field_expr is ast.SelectorExpr { + if node.field_expr.expr is ast.Ident { + if node.field_expr.expr.name == g.comp_for_field_var + && node.field_expr.field_name == 'name' { + g.write(g.comp_for_field_value.name) + return + } + } + } + g.expr(node.field_expr) +} + +fn (mut g Gen) comptime_call(node ast.ComptimeCall) { + if node.is_embed { + // $embed_file('/path/to/file') + g.gen_embed_file_init(node) + return + } + if node.method_name == 'env' { + // $env('ENV_VAR_NAME') + val := util.cescaped_path(os.getenv(node.args_var)) + g.write('_SLIT("$val")') + return + } + if node.is_vweb { + is_html := node.method_name == 'html' + for stmt in node.vweb_tmpl.stmts { + if stmt is ast.FnDecl { + // insert stmts from vweb_tmpl fn + if stmt.name.starts_with('main.vweb_tmpl') { + if is_html { + g.inside_vweb_tmpl = true + } + g.stmts(stmt.stmts) + g.inside_vweb_tmpl = false + break + } + } + } + if is_html { + // return vweb html template + g.writeln('vweb__Context_html(&app->Context, _tmpl_res_$g.fn_decl.name); strings__Builder_free(&sb); string_free(&_tmpl_res_$g.fn_decl.name);') + } else { + // return $tmpl string + fn_name := g.fn_decl.name.replace('.', '__') + g.writeln('return _tmpl_res_$fn_name;') + } + return + } + g.trace_autofree('// \$method call. sym="$node.sym.name"') + if node.method_name == 'method' { + // `app.$method()` + m := node.sym.find_method(g.comp_for_method) or { return } + /* + vals := m.attrs[0].split('/') + args := vals.filter(it.starts_with(':')).map(it[1..]) + println(vals) + for val in vals { + } + */ + expand_strs := if node.args.len > 0 && m.params.len - 1 >= node.args.len { + arg := node.args[node.args.len - 1] + param := m.params[node.args.len] + + arg.expr is ast.Ident && g.table.type_to_str(arg.typ) == '[]string' + && g.table.type_to_str(param.typ) != '[]string' + } else { + false + } + // check argument length and types + if m.params.len - 1 != node.args.len && !expand_strs { + // do not generate anything if the argument lengths don't match + g.writeln('/* skipping ${node.sym.name}.$m.name due to mismatched arguments list */') + // verror('expected ${m.params.len-1} arguments to method ${node.sym.name}.$m.name, but got $node.args.len') + return + } + // TODO: check argument types + g.write('${util.no_dots(node.sym.name)}_${g.comp_for_method}(') + + // try to see if we need to pass a pointer + if node.left is ast.Ident { + scope := g.file.scope.innermost(node.pos.pos) + if v := scope.find_var(node.left.name) { + if m.params[0].typ.is_ptr() && !v.typ.is_ptr() { + g.write('&') + } + } + } + g.expr(node.left) + if m.params.len > 1 { + g.write(', ') + } + for i in 1 .. m.params.len { + if node.left is ast.Ident { + if m.params[i].name == node.left.name { + continue + } + } + if i - 1 < node.args.len - 1 { + g.expr(node.args[i - 1].expr) + g.write(', ') + } else if !expand_strs && i == node.args.len { + g.expr(node.args[i - 1].expr) + break + } else { + // last argument; try to expand if it's []string + idx := i - node.args.len + if m.params[i].typ.is_int() || m.params[i].typ.idx() == ast.bool_type_idx { + // Gets the type name and cast the string to the type with the string_ function + type_name := g.table.type_symbols[int(m.params[i].typ)].str() + g.write('string_${type_name}(((string*)${node.args[node.args.len - 1]}.data) [$idx])') + } else { + g.write('((string*)${node.args[node.args.len - 1]}.data) [$idx] ') + } + if i < m.params.len - 1 { + g.write(', ') + } + } + } + g.write(' ); // vweb action call with args') + return + } + mut j := 0 + for method in node.sym.methods { + // if method.return_type != ast.void_type { + if method.return_type != node.result_type { + continue + } + if method.params.len != 1 { + continue + } + // receiver := method.args[0] + // if !p.expr_var.ptr { + // p.error('`$p.expr_var.name` needs to be a reference') + // } + amp := '' // if receiver.is_mut && !p.expr_var.ptr { '&' } else { '' } + if node.is_vweb { + if j > 0 { + g.write(' else ') + } + g.write('if (string__eq($node.method_name, _SLIT("$method.name"))) ') + } + g.write('${util.no_dots(node.sym.name)}_${method.name}($amp ') + g.expr(node.left) + g.writeln(');') + j++ + } +} + +fn cgen_attrs(attrs []ast.Attr) []string { + mut res := []string{cap: attrs.len} + for attr in attrs { + // we currently don't quote 'arg' (otherwise we could just use `s := attr.str()`) + mut s := attr.name + if attr.arg.len > 0 { + s += ': $attr.arg' + } + res << '_SLIT("$s")' + } + return res +} + +fn (mut g Gen) comp_at(node ast.AtExpr) { + if node.kind == .vmod_file { + val := cnewlines(node.val.replace('\r', '')) + g.write('_SLIT("$val")') + } else { + val := node.val.replace('\\', '\\\\') + g.write('_SLIT("$val")') + } +} + +fn (mut g Gen) comp_if(node ast.IfExpr) { + if !node.is_expr && !node.has_else && node.branches.len == 1 { + if node.branches[0].stmts.len == 0 { + // empty ifdef; result of target OS != conditional => skip + return + } + if !g.pref.output_cross_c { + if node.branches[0].cond is ast.Ident { + if g.pref.os == (pref.os_from_string(node.branches[0].cond.name) or { + pref.OS._auto + }) { + // Same target OS as the conditional... + // => skip the #if defined ... #endif wrapper + // and just generate the branch statements: + g.indent-- + g.stmts(node.branches[0].stmts) + g.indent++ + return + } + } + } + } + line := if node.is_expr { + stmt_str := g.go_before_stmt(0) + g.write(util.tabs(g.indent)) + stmt_str.trim_space() + } else { + '' + } + mut comp_if_stmts_skip := false // don't write any statements if the condition is false + // (so that for example windows calls don't get generated inside `$if macos` which + // will lead to compilation errors) + + for i, branch in node.branches { + start_pos := g.out.len + if i == node.branches.len - 1 && node.has_else { + g.writeln('#else') + comp_if_stmts_skip = false + } else { + if i == 0 { + g.write('#if ') + } else { + g.write('#elif ') + } + comp_if_stmts_skip = !g.comp_if_cond(branch.cond, branch.pkg_exist) + g.writeln('') + } + expr_str := g.out.last_n(g.out.len - start_pos).trim_space() + g.defer_ifdef = expr_str + if node.is_expr { + len := branch.stmts.len + if len > 0 { + last := branch.stmts[len - 1] as ast.ExprStmt + if len > 1 { + tmp := g.new_tmp_var() + styp := g.typ(last.typ) + g.indent++ + g.writeln('$styp $tmp;') + g.writeln('{') + g.stmts(branch.stmts[0..len - 1]) + g.write('\t$tmp = ') + g.stmt(last) + g.writeln('}') + g.indent-- + g.writeln('$line $tmp;') + } else { + g.write('$line ') + g.stmt(last) + } + } + } else { + // Only wrap the contents in {} if we're inside a function, not on the top level scope + should_create_scope := g.fn_decl != 0 + if should_create_scope { + g.writeln('{') + } + if !comp_if_stmts_skip { + g.stmts(branch.stmts) + } + if should_create_scope { + g.writeln('}') + } + } + g.defer_ifdef = '' + } + g.writeln('#endif') +} + +/* +// returning `false` means the statements inside the $if can be skipped +*/ +// returns the value of the bool comptime expression +fn (mut g Gen) comp_if_cond(cond ast.Expr, pkg_exist bool) bool { + match cond { + ast.BoolLiteral { + g.expr(cond) + return true + } + ast.ParExpr { + g.write('(') + is_cond_true := g.comp_if_cond(cond.expr, pkg_exist) + g.write(')') + return is_cond_true + } + ast.PrefixExpr { + g.write(cond.op.str()) + return g.comp_if_cond(cond.right, pkg_exist) + } + ast.PostfixExpr { + ifdef := g.comp_if_to_ifdef((cond.expr as ast.Ident).name, true) or { + verror(err.msg) + return false + } + g.write('defined($ifdef)') + return true + } + ast.InfixExpr { + match cond.op { + .and, .logical_or { + l := g.comp_if_cond(cond.left, pkg_exist) + g.write(' $cond.op ') + r := g.comp_if_cond(cond.right, pkg_exist) + return if cond.op == .and { l && r } else { l || r } + } + .key_is, .not_is { + left := cond.left + mut name := '' + mut exp_type := ast.Type(0) + got_type := (cond.right as ast.TypeNode).typ + // Handle `$if x is Interface {` + // mut matches_interface := 'false' + if left is ast.TypeNode && cond.right is ast.TypeNode + && g.table.get_type_symbol(got_type).kind == .interface_ { + // `$if Foo is Interface {` + interface_sym := g.table.get_type_symbol(got_type) + if interface_sym.info is ast.Interface { + // q := g.table.get_type_symbol(interface_sym.info.types[0]) + checked_type := g.unwrap_generic(left.typ) + // TODO PERF this check is run twice (also in the checker) + // store the result in a field + is_true := g.table.does_type_implement_interface(checked_type, + got_type) + // true // exp_type in interface_sym.info.types + if cond.op == .key_is { + if is_true { + g.write('1') + } else { + g.write('0') + } + return is_true + } else if cond.op == .not_is { + if is_true { + g.write('0') + } else { + g.write('1') + } + return !is_true + } + // matches_interface = '/*iface:$got_type $exp_type*/ true' + //} + } + } else if left is ast.SelectorExpr { + name = '${left.expr}.$left.field_name' + exp_type = g.comptime_var_type_map[name] + } else if left is ast.TypeNode { + name = left.str() + // this is only allowed for generics currently, otherwise blocked by checker + exp_type = g.unwrap_generic(left.typ) + } + + if cond.op == .key_is { + g.write('$exp_type == $got_type') + return exp_type == got_type + } else { + g.write('$exp_type != $got_type') + return exp_type != got_type + } + } + .eq, .ne { + // TODO Implement `$if method.args.len == 1` + g.write('1') + return true + } + else { + return true + } + } + } + ast.Ident { + ifdef := g.comp_if_to_ifdef(cond.name, false) or { 'true' } // handled in checker + g.write('defined($ifdef)') + return true + } + ast.ComptimeCall { + g.write('$pkg_exist') + return true + } + else { + // should be unreachable, but just in case + g.write('1') + return true + } + } +} + +fn (mut g Gen) comp_for(node ast.CompFor) { + sym := g.table.get_type_symbol(g.unwrap_generic(node.typ)) + g.writeln('/* \$for $node.val_var in ${sym.name}($node.kind.str()) */ {') + g.indent++ + // vweb_result_type := ast.new_type(g.table.find_type_idx('vweb.Result')) + mut i := 0 + // g.writeln('string method = _SLIT("");') + if node.kind == .methods { + mut methods := sym.methods.filter(it.attrs.len == 0) // methods without attrs first + methods_with_attrs := sym.methods.filter(it.attrs.len > 0) // methods with attrs second + methods << methods_with_attrs + if methods.len > 0 { + g.writeln('FunctionData $node.val_var = {0};') + } + for method in methods { // sym.methods { + /* + if method.return_type != vweb_result_type { // ast.void_type { + continue + } + */ + g.comp_for_method = method.name + g.writeln('/* method $i */ {') + g.writeln('\t${node.val_var}.name = _SLIT("$method.name");') + if method.attrs.len == 0 { + g.writeln('\t${node.val_var}.attrs = __new_array_with_default(0, 0, sizeof(string), 0);') + } else { + attrs := cgen_attrs(method.attrs) + g.writeln( + '\t${node.val_var}.attrs = new_array_from_c_array($attrs.len, $attrs.len, sizeof(string), _MOV((string[$attrs.len]){' + + attrs.join(', ') + '}));\n') + } + if method.params.len < 2 { + // 0 or 1 (the receiver) args + g.writeln('\t${node.val_var}.args = __new_array_with_default(0, 0, sizeof(MethodArgs), 0);') + } else { + len := method.params.len - 1 + g.write('\t${node.val_var}.args = new_array_from_c_array($len, $len, sizeof(MethodArgs), _MOV((MethodArgs[$len]){') + // Skip receiver arg + for j, arg in method.params[1..] { + typ := arg.typ.idx() + g.write('{$typ.str(), _SLIT("$arg.name")}') + if j < len - 1 { + g.write(', ') + } + g.comptime_var_type_map['${node.val_var}.args[$j].typ'] = typ + } + g.writeln('}));\n') + } + mut sig := 'anon_fn_' + // skip the first (receiver) arg + for j, arg in method.params[1..] { + // TODO: ignore mut/pts in sig for now + typ := arg.typ.set_nr_muls(0) + sig += '$typ' + if j < method.params.len - 2 { + sig += '_' + } + } + sig += '_$method.return_type' + styp := g.table.find_type_idx(sig) + // println(styp) + // if styp == 0 { } + // TODO: type aliases + ret_typ := method.return_type.idx() + g.writeln('\t${node.val_var}.typ = $styp;') + g.writeln('\t${node.val_var}.return_type = $ret_typ;') + // + g.comptime_var_type_map['${node.val_var}.return_type'] = ret_typ + g.comptime_var_type_map['${node.val_var}.typ'] = styp + g.stmts(node.stmts) + i++ + g.writeln('}') + // + mut delete_keys := []string{} + for key, _ in g.comptime_var_type_map { + if key.starts_with(node.val_var) { + delete_keys << key + } + } + for key in delete_keys { + g.comptime_var_type_map.delete(key) + } + } + } else if node.kind == .fields { + // TODO add fields + if sym.info is ast.Struct { + mut fields := sym.info.fields.filter(it.attrs.len == 0) + fields_with_attrs := sym.info.fields.filter(it.attrs.len > 0) + fields << fields_with_attrs + if fields.len > 0 { + g.writeln('\tFieldData $node.val_var = {0};') + } + for field in fields { + g.comp_for_field_var = node.val_var + g.comp_for_field_value = field + g.writeln('/* field $i */ {') + g.writeln('\t${node.val_var}.name = _SLIT("$field.name");') + if field.attrs.len == 0 { + g.writeln('\t${node.val_var}.attrs = __new_array_with_default(0, 0, sizeof(string), 0);') + } else { + attrs := cgen_attrs(field.attrs) + g.writeln( + '\t${node.val_var}.attrs = new_array_from_c_array($attrs.len, $attrs.len, sizeof(string), _MOV((string[$attrs.len]){' + + attrs.join(', ') + '}));\n') + } + // field_sym := g.table.get_type_symbol(field.typ) + // g.writeln('\t${node.val_var}.typ = _SLIT("$field_sym.name");') + styp := field.typ + g.writeln('\t${node.val_var}.typ = $styp;') + g.writeln('\t${node.val_var}.is_pub = $field.is_pub;') + g.writeln('\t${node.val_var}.is_mut = $field.is_mut;') + g.writeln('\t${node.val_var}.is_shared = ${field.typ.has_flag(.shared_f)};') + g.comptime_var_type_map['${node.val_var}.typ'] = styp + g.stmts(node.stmts) + i++ + g.writeln('}') + } + g.comptime_var_type_map.delete(node.val_var) + } + } else if node.kind == .attributes { + if sym.info is ast.Struct { + if sym.info.attrs.len > 0 { + g.writeln('\tStructAttribute $node.val_var = {0};') + } + for attr in sym.info.attrs { + g.writeln('/* attribute $i */ {') + g.writeln('\t${node.val_var}.name = _SLIT("$attr.name");') + g.writeln('\t${node.val_var}.has_arg = $attr.has_arg;') + g.writeln('\t${node.val_var}.arg = _SLIT("$attr.arg");') + g.writeln('\t${node.val_var}.kind = AttributeKind__$attr.kind;') + g.stmts(node.stmts) + g.writeln('}') + } + } + } + g.indent-- + g.writeln('}// \$for') +} + +fn (mut g Gen) comp_if_to_ifdef(name string, is_comptime_optional bool) ?string { + match name { + // platforms/os-es: + 'windows' { + return '_WIN32' + } + 'ios' { + return '__TARGET_IOS__' + } + 'macos' { + return '__APPLE__' + } + 'mach' { + return '__MACH__' + } + 'darwin' { + return '__DARWIN__' + } + 'hpux' { + return '__HPUX__' + } + 'gnu' { + return '__GNU__' + } + 'qnx' { + return '__QNX__' + } + 'linux' { + return '__linux__' + } + 'serenity' { + return '__serenity__' + } + 'vinix' { + return '__vinix__' + } + 'freebsd' { + return '__FreeBSD__' + } + 'openbsd' { + return '__OpenBSD__' + } + 'netbsd' { + return '__NetBSD__' + } + 'bsd' { + return '__BSD__' + } + 'dragonfly' { + return '__DragonFly__' + } + 'android' { + return '__ANDROID__' + } + 'solaris' { + return '__sun' + } + 'haiku' { + return '__HAIKU__' + } + // + 'js' { + return '_VJS' + } + // compilers: + 'gcc' { + return '__V_GCC__' + } + 'tinyc' { + return '__TINYC__' + } + 'clang' { + return '__clang__' + } + 'mingw' { + return '__MINGW32__' + } + 'msvc' { + return '_MSC_VER' + } + 'cplusplus' { + return '__cplusplus' + } + // other: + 'threads' { + return '__VTHREADS__' + } + 'gcboehm' { + return '_VGCBOEHM' + } + 'debug' { + return '_VDEBUG' + } + 'prod' { + return '_VPROD' + } + 'test' { + return '_VTEST' + } + 'glibc' { + return '__GLIBC__' + } + 'prealloc' { + return '_VPREALLOC' + } + 'no_bounds_checking' { + return 'CUSTOM_DEFINE_no_bounds_checking' + } + 'freestanding' { + return '_VFREESTANDING' + } + // architectures: + 'amd64' { + return '__V_amd64' + } + 'aarch64', 'arm64' { + return '__V_arm64' + } + // bitness: + 'x64' { + return 'TARGET_IS_64BIT' + } + 'x32' { + return 'TARGET_IS_32BIT' + } + // endianness: + 'little_endian' { + return 'TARGET_ORDER_IS_LITTLE' + } + 'big_endian' { + return 'TARGET_ORDER_IS_BIG' + } + else { + if is_comptime_optional + || (g.pref.compile_defines_all.len > 0 && name in g.pref.compile_defines_all) { + return 'CUSTOM_DEFINE_$name' + } + return error('bad os ifdef name "$name"') // should never happen, caught in the checker + } + } + return none +} diff --git a/v_windows/v/old/vlib/v/gen/c/coutput_test.v b/v_windows/v/old/vlib/v/gen/c/coutput_test.v new file mode 100644 index 0000000..516dc37 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/c/coutput_test.v @@ -0,0 +1,174 @@ +import os +import rand +import term +import v.util.diff +import v.util.vtest + +const vexe = @VEXE + +const vroot = os.real_path(@VMODROOT) + +const testdata_folder = os.join_path(vroot, 'vlib', 'v', 'gen', 'c', 'testdata') + +const diff_cmd = diff.find_working_diff_command() or { '' } + +fn test_out_files() ? { + println(term.colorize(term.green, '> testing whether .out files match:')) + os.chdir(vroot) + output_path := os.join_path(os.temp_dir(), 'coutput', 'out') + os.mkdir_all(output_path) ? + defer { + os.rmdir_all(output_path) or {} + } + files := os.ls(testdata_folder) or { [] } + tests := files.filter(it.ends_with('.out')) + if tests.len == 0 { + eprintln('no `.out` tests found in $testdata_folder') + return + } + paths := vtest.filter_vtest_only(tests, basepath: testdata_folder) + mut total_errors := 0 + for out_path in paths { + basename, path, relpath, out_relpath := target2paths(out_path, '.out') + print(term.colorize(term.magenta, 'v run $relpath') + ' == ' + + term.colorize(term.magenta, out_relpath) + ' ') + pexe := os.join_path(output_path, '${basename}.exe') + compilation := os.execute('$vexe -o $pexe $path') + ensure_compilation_succeeded(compilation) + res := os.execute(pexe) + if res.exit_code < 0 { + println('nope') + panic(res.output) + } + mut found := res.output.trim_right('\r\n').replace('\r\n', '\n') + mut expected := os.read_file(out_path) ? + expected = expected.trim_right('\r\n').replace('\r\n', '\n') + if expected.contains('================ V panic ================') { + // panic include backtraces and absolute file paths, so can't do char by char comparison + n_found := normalize_panic_message(found, vroot) + n_expected := normalize_panic_message(expected, vroot) + if found.contains('================ V panic ================') { + if n_found.starts_with(n_expected) { + println(term.green('OK (panic)')) + continue + } else { + // Both have panics, but there was a difference... + // Pass the normalized strings for further reporting. + // There is no point in comparing the backtraces too. + found = n_found + expected = n_expected + } + } + } + if expected != found { + println(term.red('FAIL')) + println(term.header('expected:', '-')) + println(expected) + println(term.header('found:', '-')) + println(found) + if diff_cmd != '' { + println(term.header('difference:', '-')) + println(diff.color_compare_strings(diff_cmd, rand.ulid(), expected, found)) + } else { + println(term.h_divider('-')) + } + total_errors++ + } else { + println(term.green('OK')) + } + } + assert total_errors == 0 +} + +fn test_c_must_have_files() ? { + println(term.colorize(term.green, '> testing whether `.c.must_have` files match:')) + os.chdir(vroot) + output_path := os.join_path(os.temp_dir(), 'coutput', 'c_must_have') + os.mkdir_all(output_path) ? + defer { + os.rmdir_all(output_path) or {} + } + files := os.ls(testdata_folder) or { [] } + tests := files.filter(it.ends_with('.c.must_have')) + if tests.len == 0 { + eprintln('no `.c.must_have` files found in $testdata_folder') + return + } + paths := vtest.filter_vtest_only(tests, basepath: testdata_folder) + mut total_errors := 0 + for must_have_path in paths { + basename, path, relpath, must_have_relpath := target2paths(must_have_path, '.c.must_have') + print(term.colorize(term.magenta, 'v -o - $relpath') + ' matches all line paterns in ' + + term.colorize(term.magenta, must_have_relpath) + ' ') + compilation := os.execute('$vexe -o - $path') + ensure_compilation_succeeded(compilation) + expected_lines := os.read_lines(must_have_path) or { [] } + generated_c_lines := compilation.output.split_into_lines() + mut nmatches := 0 + for idx_expected_line, eline in expected_lines { + if does_line_match_one_of_generated_lines(eline, generated_c_lines) { + nmatches++ + // eprintln('> testing: $must_have_path has line: $eline') + } else { + println(term.red('FAIL')) + eprintln('$must_have_path:${idx_expected_line + 1}: expected match error:') + eprintln('`$vexe -o - $path` does NOT produce expected line:') + eprintln(term.colorize(term.red, eline)) + total_errors++ + continue + } + } + if nmatches == expected_lines.len { + println(term.green('OK')) + } else { + eprintln('> ALL lines:') + eprintln(compilation.output) + } + } + assert total_errors == 0 +} + +fn does_line_match_one_of_generated_lines(line string, generated_c_lines []string) bool { + for cline in generated_c_lines { + if line == cline { + return true + } + if cline.contains(line) { + return true + } + } + return false +} + +fn normalize_panic_message(message string, vroot string) string { + mut msg := message.all_before('=========================================') + // change windows to nix path + s := vroot.replace(os.path_separator, '/') + msg = msg.replace(s + '/', '') + msg = msg.trim_space() + return msg +} + +fn vroot_relative(opath string) string { + nvroot := vroot.replace(os.path_separator, '/') + '/' + npath := opath.replace(os.path_separator, '/') + return npath.replace(nvroot, '') +} + +fn ensure_compilation_succeeded(compilation os.Result) { + if compilation.exit_code < 0 { + panic(compilation.output) + } + if compilation.exit_code != 0 { + panic('compilation failed: $compilation.output') + } +} + +fn target2paths(target_path string, postfix string) (string, string, string, string) { + basename := os.file_name(target_path).replace(postfix, '') + target_dir := os.dir(target_path) + path := os.join_path(target_dir, '${basename}.vv') + relpath := vroot_relative(path) + target_relpath := vroot_relative(target_path) + return basename, path, relpath, target_relpath +} diff --git a/v_windows/v/old/vlib/v/gen/c/ctempvars.v b/v_windows/v/old/vlib/v/gen/c/ctempvars.v new file mode 100644 index 0000000..f0484ed --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/c/ctempvars.v @@ -0,0 +1,25 @@ +module c + +import v.ast + +fn (mut g Gen) new_ctemp_var(expr ast.Expr, expr_type ast.Type) ast.CTempVar { + return ast.CTempVar{ + name: g.new_tmp_var() + typ: expr_type + is_ptr: expr_type.is_ptr() + orig: expr + } +} + +fn (mut g Gen) new_ctemp_var_then_gen(expr ast.Expr, expr_type ast.Type) ast.CTempVar { + x := g.new_ctemp_var(expr, expr_type) + g.gen_ctemp_var(x) + return x +} + +fn (mut g Gen) gen_ctemp_var(tvar ast.CTempVar) { + styp := g.typ(tvar.typ) + g.write('$styp $tvar.name = ') + g.expr(tvar.orig) + g.writeln(';') +} diff --git a/v_windows/v/old/vlib/v/gen/c/dumpexpr.v b/v_windows/v/old/vlib/v/gen/c/dumpexpr.v new file mode 100644 index 0000000..fcb6bbd --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/c/dumpexpr.v @@ -0,0 +1,72 @@ +module c + +import v.ast +import strings + +fn (mut g Gen) dump_expr(node ast.DumpExpr) { + sexpr := ctoslit(node.expr.str()) + fpath := cestring(g.file.path) + line := node.pos.line_nr + 1 + if 'nop_dump' in g.pref.compile_defines { + g.expr(node.expr) + return + } + dump_fn_name := '_v_dump_expr_$node.cname' + (if node.expr_type.is_ptr() { '_ptr' } else { '' }) + g.write(' ${dump_fn_name}(${ctoslit(fpath)}, $line, $sexpr, ') + g.expr(node.expr) + g.write(' )') +} + +fn (mut g Gen) dump_expr_definitions() { + mut dump_typedefs := map[string]bool{} + mut dump_fns := strings.new_builder(100) + for dump_type, cname in g.table.dumps { + to_string_fn_name := g.gen_str_for_type(dump_type) + is_ptr := ast.Type(dump_type).is_ptr() + ptr_asterisk := if is_ptr { '*' } else { '' } + dump_sym := g.table.get_type_symbol(dump_type) + mut str_dumparg_type := '$cname$ptr_asterisk' + if dump_sym.kind == .function { + fninfo := dump_sym.info as ast.FnType + str_dumparg_type = 'DumpFNType_$cname' + tdef_pos := g.out.len + g.write_fn_ptr_decl(&fninfo, str_dumparg_type) + str_tdef := g.out.after(tdef_pos) + g.out.go_back(str_tdef.len) + dump_typedefs['typedef $str_tdef;'] = true + } + dump_fn_name := '_v_dump_expr_$cname' + (if is_ptr { '_ptr' } else { '' }) + if g.writeln_fn_header('$str_dumparg_type ${dump_fn_name}(string fpath, int line, string sexpr, $str_dumparg_type x)', mut + dump_fns) + { + continue + } + dump_fns.writeln('\teprint(${ctoslit('[')});') + dump_fns.writeln('\teprint(fpath);') + dump_fns.writeln('\teprint(${ctoslit(':')});') + dump_fns.writeln('\teprint(int_str(line));') + dump_fns.writeln('\teprint(${ctoslit('] ')});') + // dump_fns.writeln('\t/* dump_type: $dump_type | to_string_fn_name: $to_string_fn_name | is_ptr: $is_ptr | ptr_asterisk: $ptr_asterisk | dump_fn_name: $dump_fn_name | cnam: $cname */') + dump_fns.writeln('\teprint(sexpr);') + dump_fns.writeln('\teprint(${ctoslit(': ')});') + if is_ptr { + dump_fns.writeln('\teprint(${ctoslit('&')});') + } + dump_fns.writeln('\teprintln(${to_string_fn_name}(${ptr_asterisk}x));') + dump_fns.writeln('\treturn x;') + dump_fns.writeln('}') + } + for tdef, _ in dump_typedefs { + g.definitions.writeln(tdef) + } + g.definitions.writeln(dump_fns.str()) +} + +fn (mut g Gen) writeln_fn_header(s string, mut sb strings.Builder) bool { + if g.pref.build_mode == .build_module { + sb.writeln('$s;') + return true + } + sb.writeln('$s {') + return false +} diff --git a/v_windows/v/old/vlib/v/gen/c/embed.v b/v_windows/v/old/vlib/v/gen/c/embed.v new file mode 100644 index 0000000..4ee02eb --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/c/embed.v @@ -0,0 +1,69 @@ +module c + +import os +import v.ast + +fn (mut g Gen) embed_file_is_prod_mode() bool { + if g.pref.is_prod || 'debug_embed_file_in_prod' in g.pref.compile_defines { + return true + } + return false +} + +// gen_embed_file_struct generates C code for `$embed_file('...')` calls. +fn (mut g Gen) gen_embed_file_init(node ast.ComptimeCall) { + g.writeln('(v__embed_file__EmbedFileData){') + g.writeln('\t\t.path = ${ctoslit(node.embed_file.rpath)},') + g.writeln('\t\t.apath = ${ctoslit(node.embed_file.apath)},') + file_size := os.file_size(node.embed_file.apath) + if file_size > 5242880 { + eprintln('Warning: embedding of files >= ~5MB is currently not supported') + } + if g.embed_file_is_prod_mode() { + // Use function generated in Gen.gen_embedded_data() + g.writeln('\t\t.compressed = v__embed_file__find_index_entry_by_path((voidptr)_v_embed_file_index, ${ctoslit(node.embed_file.rpath)})->data,') + } + g.writeln('\t\t.uncompressed = NULL,') + g.writeln('\t\t.free_compressed = 0,') + g.writeln('\t\t.free_uncompressed = 0,') + g.writeln('\t\t.len = $file_size') + g.writeln('} // \$embed_file("$node.embed_file.apath")') +} + +// gen_embedded_data embeds data into the V target executable. +fn (mut g Gen) gen_embedded_data() { + /* + TODO implement compression. + See also the vlib/embed module where decompression should occur. + */ + /* + TODO implement support for large files - right now the setup has problems + // with even just 10 - 50 MB files - the problem is both in V and C compilers. + // maybe we need to write to separate files or have an external tool for large files + // like the `rcc` tool in Qt? + */ + for i, emfile in g.embedded_files { + fbytes := os.read_bytes(emfile.apath) or { panic('Error while embedding file: $err') } + g.embedded_data.write_string('static const unsigned char _v_embed_blob_$i[$fbytes.len] = {\n ') + for j := 0; j < fbytes.len; j++ { + b := fbytes[j].hex() + if j < fbytes.len - 1 { + g.embedded_data.write_string('0x$b,') + } else { + g.embedded_data.write_string('0x$b') + } + if 0 == ((j + 1) % 16) { + g.embedded_data.write_string('\n ') + } + } + g.embedded_data.writeln('\n};') + } + g.embedded_data.writeln('') + g.embedded_data.writeln('const v__embed_file__EmbedFileIndexEntry _v_embed_file_index[] = {') + for i, emfile in g.embedded_files { + g.embedded_data.writeln('\t{$i, { .str=(byteptr)("${cestring(emfile.rpath)}"), .len=${emfile.rpath.len - 1}, .is_lit=1 }, _v_embed_blob_$i},') + } + g.embedded_data.writeln('\t{-1, { .str=(byteptr)(""), .len=0, .is_lit=1 }, NULL}') + g.embedded_data.writeln('};') + // see vlib/v/embed_file/embed_file.v, find_index_entry_by_id/2 and find_index_entry_by_path/2 +} diff --git a/v_windows/v/old/vlib/v/gen/c/fn.v b/v_windows/v/old/vlib/v/gen/c/fn.v new file mode 100644 index 0000000..b0ed915 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/c/fn.v @@ -0,0 +1,1452 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module c + +import v.ast +import v.util + +fn (mut g Gen) is_used_by_main(node ast.FnDecl) bool { + mut is_used_by_main := true + if g.pref.skip_unused { + fkey := node.fkey() + is_used_by_main = g.table.used_fns[fkey] + $if trace_skip_unused_fns ? { + println('> is_used_by_main: $is_used_by_main | node.name: $node.name | fkey: $fkey | node.is_method: $node.is_method') + } + if !is_used_by_main { + $if trace_skip_unused_fns_in_c_code ? { + g.writeln('// trace_skip_unused_fns_in_c_code, $node.name, fkey: $fkey') + } + } + } else { + $if trace_skip_unused_fns_in_c_code ? { + g.writeln('// trace_skip_unused_fns_in_c_code, $node.name, fkey: $node.fkey()') + } + } + return is_used_by_main +} + +fn (mut g Gen) process_fn_decl(node ast.FnDecl) { + if !g.is_used_by_main(node) { + return + } + if g.is_builtin_mod && g.pref.gc_mode == .boehm_leak && node.name == 'malloc' { + g.definitions.write_string('#define _v_malloc GC_MALLOC\n') + return + } + g.gen_attrs(node.attrs) + // g.tmp_count = 0 TODO + mut skip := false + pos := g.out.len + should_bundle_module := util.should_bundle_module(node.mod) + if g.pref.build_mode == .build_module { + // if node.name.contains('parse_text') { + // println('!!! $node.name mod=$node.mod, built=$g.module_built') + // } + // TODO true for not just "builtin" + // TODO: clean this up + mod := if g.is_builtin_mod { 'builtin' } else { node.name.all_before_last('.') } + if (mod != g.module_built && node.mod != g.module_built.after('/')) || should_bundle_module { + // Skip functions that don't have to be generated for this module. + // println('skip bm $node.name mod=$node.mod module_built=$g.module_built') + skip = true + } + if g.is_builtin_mod && g.module_built == 'builtin' && node.mod == 'builtin' { + skip = false + } + if !skip && g.pref.is_verbose { + println('build module `$g.module_built` fn `$node.name`') + } + } + if g.pref.use_cache { + // We are using prebuilt modules, we do not need to generate + // their functions in main.c. + if node.mod != 'main' && node.mod != 'help' && !should_bundle_module && !g.pref.is_test + && node.generic_names.len == 0 { + skip = true + } + } + keep_fn_decl := g.fn_decl + unsafe { + g.fn_decl = &node + } + if node.is_main { + g.has_main = true + } + if node.name == 'backtrace' || node.name == 'backtrace_symbols' + || node.name == 'backtrace_symbols_fd' { + g.write('\n#ifndef __cplusplus\n') + } + g.gen_fn_decl(node, skip) + if node.name == 'backtrace' || node.name == 'backtrace_symbols' + || node.name == 'backtrace_symbols_fd' { + g.write('\n#endif\n') + } + g.fn_decl = keep_fn_decl + if skip { + g.out.go_back_to(pos) + } + if !g.pref.skip_unused { + if node.language != .c { + g.writeln('') + } + } +} + +fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) { + // TODO For some reason, build fails with autofree with this line + // as it's only informative, comment it for now + // g.gen_attrs(it.attrs) + if node.language == .c { + // || node.no_body { + return + } + + tmp_defer_vars := g.defer_vars // must be here because of workflow + if !g.anon_fn { + g.defer_vars = []string{} + } else { + if node.defer_stmts.len > 0 { + g.defer_vars = []string{} + defer { + g.defer_vars = tmp_defer_vars + } + } + } + // Skip [if xxx] if xxx is not defined + /* + for attr in node.attrs { + if !attr.is_comptime_define { + continue + } + if attr.name !in g.pref.compile_defines_all { + // println('skipping [if]') + return + } + } + */ + + g.returned_var_name = '' + // + old_g_autofree := g.is_autofree + if node.is_manualfree { + g.is_autofree = false + } + defer { + g.is_autofree = old_g_autofree + } + // + // if g.fileis('vweb.v') { + // println('\ngen_fn_decl() $node.name $node.is_generic $g.cur_generic_type') + // } + if node.generic_names.len > 0 && g.table.cur_concrete_types.len == 0 { // need the cur_concrete_type check to avoid inf. recursion + // loop thru each generic type and generate a function + for concrete_types in g.table.fn_generic_types[node.name] { + if g.pref.is_verbose { + syms := concrete_types.map(g.table.get_type_symbol(it)) + the_type := syms.map(it.name).join(', ') + println('gen fn `$node.name` for type `$the_type`') + } + g.table.cur_concrete_types = concrete_types + g.gen_fn_decl(node, skip) + } + g.table.cur_concrete_types = [] + return + } + cur_fn_save := g.table.cur_fn + defer { + g.table.cur_fn = cur_fn_save + } + unsafe { + // TODO remove unsafe + g.table.cur_fn = node + } + fn_start_pos := g.out.len + g.write_v_source_line_info(node.pos) + msvc_attrs := g.write_fn_attrs(node.attrs) + // Live + is_livefn := node.attrs.contains('live') + is_livemain := g.pref.is_livemain && is_livefn + is_liveshared := g.pref.is_liveshared && is_livefn + is_livemode := g.pref.is_livemain || g.pref.is_liveshared + is_live_wrap := is_livefn && is_livemode + if is_livefn && !is_livemode { + eprintln('INFO: compile with `v -live $g.pref.path `, if you want to use the [live] function $node.name .') + } + // + mut name := node.name + if name in ['+', '-', '*', '/', '%', '<', '=='] { + name = util.replace_op(name) + } + if node.is_method { + unwrapped_rec_sym := g.table.get_type_symbol(g.unwrap_generic(node.receiver.typ)) + if unwrapped_rec_sym.kind == .placeholder { + return + } + name = g.cc_type(node.receiver.typ, false) + '_' + name + // name = g.table.get_type_symbol(node.receiver.typ).name + '_' + name + } + if node.language == .c { + name = util.no_dots(name) + } else { + name = c_name(name) + } + mut type_name := g.typ(node.return_type) + + name = g.generic_fn_name(g.table.cur_concrete_types, name, true) + + if g.pref.obfuscate && g.cur_mod.name == 'main' && name.starts_with('main__') && !node.is_main + && node.name != 'str' { + mut key := node.name + if node.is_method { + sym := g.table.get_type_symbol(node.receiver.typ) + key = sym.name + '.' + node.name + } + g.writeln('/* obf: $key */') + name = g.obf_table[key] or { + panic('cgen: fn_decl: obf name "$key" not found, this should never happen') + } + } + // if g.pref.show_cc && it.is_builtin { + // println(name) + // } + // type_name := g.ast.Type_to_str(it.return_type) + // Live functions are protected by a mutex, because otherwise they + // can be changed by the live reload thread, *while* they are + // running, with unpredictable results (usually just crashing). + // For this purpose, the actual body of the live function, + // is put under a non publicly accessible function, that is prefixed + // with 'impl_live_' . + if is_livemain { + g.hotcode_fn_names << name + } + mut impl_fn_name := name + if is_live_wrap { + impl_fn_name = 'impl_live_$name' + } + last_fn_c_name_save := g.last_fn_c_name + defer { + g.last_fn_c_name = last_fn_c_name_save + } + g.last_fn_c_name = impl_fn_name + // + if is_live_wrap { + if is_livemain { + g.definitions.write_string('$type_name (* $impl_fn_name)(') + g.write('$type_name no_impl_${name}(') + } + if is_liveshared { + g.definitions.write_string('$type_name ${impl_fn_name}(') + g.write('$type_name ${impl_fn_name}(') + } + } else { + if !(node.is_pub || g.pref.is_debug) { + // Private functions need to marked as static so that they are not exportable in the + // binaries + if g.pref.build_mode != .build_module && !g.pref.use_cache { + // if !(g.pref.build_mode == .build_module && g.is_builtin_mod) { + // If we are building vlib/builtin, we need all private functions like array_get + // to be public, so that all V programs can access them. + g.write('VV_LOCAL_SYMBOL ') + g.definitions.write_string('VV_LOCAL_SYMBOL ') + } + } + fn_header := if msvc_attrs.len > 0 { + '$type_name $msvc_attrs ${name}(' + } else { + '$type_name ${name}(' + } + g.definitions.write_string(fn_header) + g.write(fn_header) + } + arg_start_pos := g.out.len + fargs, fargtypes, heap_promoted := g.fn_args(node.params, node.is_variadic, node.scope) + arg_str := g.out.after(arg_start_pos) + if node.no_body || ((g.pref.use_cache && g.pref.build_mode != .build_module) && node.is_builtin + && !g.is_test) || skip { + // Just a function header. Builtin function bodies are defined in builtin.o + g.definitions.writeln(');') // // NO BODY') + g.writeln(');') + return + } + g.definitions.writeln(');') + g.writeln(') {') + for i, is_promoted in heap_promoted { + if is_promoted { + g.writeln('${fargtypes[i]}* ${fargs[i]} = HEAP(${fargtypes[i]}, _v_toheap_${fargs[i]});') + } + } + for defer_stmt in node.defer_stmts { + g.writeln('bool ${g.defer_flag_var(defer_stmt)} = false;') + for var in defer_stmt.defer_vars { + if var.name in fargs || var.kind == .constant { + continue + } + if var.kind == .variable { + if var.name !in g.defer_vars { + g.defer_vars << var.name + mut deref := '' + if v := var.scope.find_var(var.name) { + if v.is_auto_heap { + deref = '*' + } + } + info := var.obj as ast.Var + g.writeln('${g.typ(info.typ)}$deref $var.name;') + } + } + } + } + if is_live_wrap { + // The live function just calls its implementation dual, while ensuring + // that the call is wrapped by the mutex lock & unlock calls. + // Adding the mutex lock/unlock inside the body of the implementation + // function is not reliable, because the implementation function can do + // an early exit, which will leave the mutex locked. + mut fn_args_list := []string{} + for ia, fa in fargs { + fn_args_list << '${fargtypes[ia]} $fa' + } + mut live_fncall := '${impl_fn_name}(' + fargs.join(', ') + ');' + mut live_fnreturn := '' + if type_name != 'void' { + live_fncall = '$type_name res = $live_fncall' + live_fnreturn = 'return res;' + } + g.definitions.writeln('$type_name ${name}(' + fn_args_list.join(', ') + ');') + g.hotcode_definitions.writeln('$type_name ${name}(' + fn_args_list.join(', ') + '){') + g.hotcode_definitions.writeln(' pthread_mutex_lock(&live_fn_mutex);') + g.hotcode_definitions.writeln(' $live_fncall') + g.hotcode_definitions.writeln(' pthread_mutex_unlock(&live_fn_mutex);') + g.hotcode_definitions.writeln(' $live_fnreturn') + g.hotcode_definitions.writeln('}') + } + // Profiling mode? Start counting at the beginning of the function (save current time). + if g.pref.is_prof && g.pref.build_mode != .build_module { + g.profile_fn(node) + } + // we could be in an anon fn so save outer fn defer stmts + prev_defer_stmts := g.defer_stmts + g.defer_stmts = [] + ctmp := g.tmp_count + g.tmp_count = 0 + defer { + g.tmp_count = ctmp + } + g.stmts(node.stmts) + if node.is_noreturn { + g.writeln('\twhile(1);') + } + // clear g.fn_mut_arg_names + + if !node.has_return { + g.write_defer_stmts_when_needed() + } + if node.is_anon { + g.defer_stmts = prev_defer_stmts + } else { + g.defer_stmts = [] + } + if node.return_type != ast.void_type && node.stmts.len > 0 && node.stmts.last() !is ast.Return + && !node.attrs.contains('_naked') { + default_expr := g.type_default(node.return_type) + // TODO: perf? + if default_expr == '{0}' { + // if node.return_type.idx() == 1 && node.return_type.has_flag(.optional) { + // // The default return for anonymous functions that return `?, + // // should have .ok = true set, otherwise calling them with + // // optfn() or { panic(err) } will cause a panic: + // g.writeln('\treturn (Option_void){0};') + // } else { + g.writeln('\treturn ($type_name)$default_expr;') + // } + } else { + g.writeln('\treturn $default_expr;') + } + } + g.writeln('}') + if g.pref.printfn_list.len > 0 && g.last_fn_c_name in g.pref.printfn_list { + println(g.out.after(fn_start_pos)) + } + for attr in node.attrs { + if attr.name == 'export' { + g.writeln('// export alias: $attr.arg -> $name') + export_alias := '$type_name ${attr.arg}($arg_str)' + g.definitions.writeln('VV_EXPORTED_SYMBOL $export_alias; // exported fn $node.name') + g.writeln('$export_alias {') + g.write('\treturn ${name}(') + g.write(fargs.join(', ')) + g.writeln(');') + g.writeln('}') + } + } +} + +fn (g &Gen) defer_flag_var(stmt &ast.DeferStmt) string { + return '${g.last_fn_c_name}_defer_$stmt.idx_in_fn' +} + +fn (mut g Gen) write_defer_stmts_when_needed() { + // unlock all mutexes, in case we are in a lock statement. defers are not allowed in lock statements + g.unlock_locks() + if g.defer_stmts.len > 0 { + g.write_defer_stmts() + } + if g.defer_profile_code.len > 0 { + g.writeln('') + g.writeln('\t// defer_profile_code') + g.writeln(g.defer_profile_code) + g.writeln('') + } +} + +// fn decl args +fn (mut g Gen) fn_args(args []ast.Param, is_variadic bool, scope &ast.Scope) ([]string, []string, []bool) { + mut fargs := []string{} + mut fargtypes := []string{} + mut heap_promoted := []bool{} + if args.len == 0 { + // in C, `()` is untyped, unlike `(void)` + g.write('void') + } + for i, arg in args { + mut caname := if arg.name == '_' { g.new_tmp_declaration_name() } else { c_name(arg.name) } + typ := g.unwrap_generic(arg.typ) + arg_type_sym := g.table.get_type_symbol(typ) + mut arg_type_name := g.typ(typ) // util.no_dots(arg_type_sym.name) + if arg_type_sym.kind == .function { + info := arg_type_sym.info as ast.FnType + func := info.func + g.write('${g.typ(func.return_type)} (*$caname)(') + g.definitions.write_string('${g.typ(func.return_type)} (*$caname)(') + g.fn_args(func.params, func.is_variadic, voidptr(0)) + g.write(')') + g.definitions.write_string(')') + } else { + mut heap_prom := false + if scope != voidptr(0) { + if arg.name != '_' { + if v := scope.find_var(arg.name) { + if !v.is_stack_obj && v.is_auto_heap { + heap_prom = true + } + } + } + } + var_name_prefix := if heap_prom { '_v_toheap_' } else { '' } + const_prefix := if arg.typ.is_any_kind_of_pointer() && !arg.is_mut + && arg.name.starts_with('const_') { + 'const ' + } else { + '' + } + s := '$const_prefix$arg_type_name $var_name_prefix$caname' + g.write(s) + g.definitions.write_string(s) + fargs << caname + fargtypes << arg_type_name + heap_promoted << heap_prom + } + if i < args.len - 1 { + g.write(', ') + g.definitions.write_string(', ') + } + } + return fargs, fargtypes, heap_promoted +} + +fn (mut g Gen) call_expr(node ast.CallExpr) { + // g.write('/*call expr*/') + // NOTE: everything could be done this way + // see my comment in parser near anon_fn + if node.left is ast.AnonFn { + g.expr(node.left) + } + if node.left is ast.IndexExpr && node.name == '' { + g.is_fn_index_call = true + g.expr(node.left) + g.is_fn_index_call = false + } + if node.should_be_skipped { + return + } + g.inside_call = true + defer { + g.inside_call = false + } + gen_keep_alive := node.is_keep_alive && node.return_type != ast.void_type + && g.pref.gc_mode in [.boehm_full, .boehm_incr, .boehm_full_opt, .boehm_incr_opt] + gen_or := node.or_block.kind != .absent // && !g.is_autofree + is_gen_or_and_assign_rhs := gen_or && !g.discard_or_result + cur_line := if is_gen_or_and_assign_rhs || gen_keep_alive { // && !g.is_autofree { + // `x := foo() or { ...}` + // cut everything that has been generated to prepend optional variable creation + line := g.go_before_stmt(0) + g.out.write_string(util.tabs(g.indent)) + // g.write('/*is_gen_or_and_assign_rhs*/') + line + } else { + '' + } + tmp_opt := if gen_or || gen_keep_alive { g.new_tmp_var() } else { '' } + if gen_or || gen_keep_alive { + mut ret_typ := node.return_type + if gen_or { + ret_typ = ret_typ.set_flag(.optional) + } + styp := g.typ(ret_typ) + g.write('$styp $tmp_opt = ') + } + if node.is_method && !node.is_field { + if node.name == 'writeln' && g.pref.experimental && node.args.len > 0 + && node.args[0].expr is ast.StringInterLiteral + && g.table.get_type_symbol(node.receiver_type).name == 'strings.Builder' { + g.string_inter_literal_sb_optimized(node) + } else { + g.method_call(node) + } + } else { + g.fn_call(node) + } + if gen_or { // && !g.autofree { + // if !g.is_autofree { + g.or_block(tmp_opt, node.or_block, node.return_type) + //} + if is_gen_or_and_assign_rhs { + unwrapped_typ := node.return_type.clear_flag(.optional) + unwrapped_styp := g.typ(unwrapped_typ) + if unwrapped_typ == ast.void_type { + g.write('\n $cur_line') + } else if g.table.get_type_symbol(node.return_type).kind == .multi_return { + g.write('\n $cur_line $tmp_opt /*U*/') + } else { + if !g.inside_const { + g.write('\n $cur_line (*($unwrapped_styp*)${tmp_opt}.data)') + } else { + g.write('\n $cur_line $tmp_opt') + } + } + } + } else if gen_keep_alive { + if node.return_type == ast.void_type { + g.write('\n $cur_line') + } else { + g.write('\n $cur_line $tmp_opt') + } + } + if node.is_noreturn { + if g.inside_ternary == 0 { + g.writeln(';') + g.write('VUNREACHABLE()') + } else { + $if msvc { + // MSVC has no support for the statement expressions used below + } $else { + g.write(', ({VUNREACHABLE();})') + } + } + } +} + +fn (mut g Gen) method_call(node ast.CallExpr) { + // TODO: there are still due to unchecked exprs (opt/some fn arg) + if node.left_type == 0 { + g.checker_bug('CallExpr.left_type is 0 in method_call', node.pos) + } + if node.receiver_type == 0 { + g.checker_bug('CallExpr.receiver_type is 0 in method_call', node.pos) + } + mut unwrapped_rec_type := node.receiver_type + if g.table.cur_fn.generic_names.len > 0 { // in generic fn + unwrapped_rec_type = g.unwrap_generic(node.receiver_type) + } else { // in non-generic fn + sym := g.table.get_type_symbol(node.receiver_type) + match sym.info { + ast.Struct, ast.Interface, ast.SumType { + generic_names := sym.info.generic_types.map(g.table.get_type_symbol(it).name) + if utyp := g.table.resolve_generic_to_concrete(node.receiver_type, generic_names, + sym.info.concrete_types) + { + unwrapped_rec_type = utyp + } + } + else {} + } + } + typ_sym := g.table.get_type_symbol(unwrapped_rec_type) + rec_cc_type := g.cc_type(unwrapped_rec_type, false) + mut receiver_type_name := util.no_dots(rec_cc_type) + if typ_sym.kind == .interface_ && (typ_sym.info as ast.Interface).defines_method(node.name) { + // Speaker_name_table[s._interface_idx].speak(s._object) + $if debug_interface_method_call ? { + eprintln('>>> interface typ_sym.name: $typ_sym.name | receiver_type_name: $receiver_type_name') + } + g.write('${c_name(receiver_type_name)}_name_table[') + g.expr(node.left) + dot := if node.left_type.is_ptr() { '->' } else { '.' } + mname := c_name(node.name) + g.write('${dot}_typ]._method_${mname}(') + g.expr(node.left) + g.write('${dot}_object') + if node.args.len > 0 { + g.write(', ') + g.call_args(node) + } + g.write(')') + return + } + left_sym := g.table.get_type_symbol(node.left_type) + final_left_sym := g.table.get_final_type_symbol(node.left_type) + if left_sym.kind == .array { + match node.name { + 'filter' { + g.gen_array_filter(node) + return + } + 'sort' { + g.gen_array_sort(node) + return + } + 'insert' { + g.gen_array_insert(node) + return + } + 'map' { + g.gen_array_map(node) + return + } + 'prepend' { + g.gen_array_prepend(node) + return + } + 'contains' { + g.gen_array_contains(node) + return + } + 'index' { + g.gen_array_index(node) + return + } + 'wait' { + g.gen_array_wait(node) + return + } + 'any' { + g.gen_array_any(node) + return + } + 'all' { + g.gen_array_all(node) + return + } + else {} + } + } + + if left_sym.kind == .map && node.name == 'delete' { + left_info := left_sym.info as ast.Map + elem_type_str := g.typ(left_info.key_type) + g.write('map_delete(') + if node.left_type.is_ptr() { + g.expr(node.left) + } else { + g.write('&') + g.expr(node.left) + } + g.write(', &($elem_type_str[]){') + g.expr(node.args[0].expr) + g.write('})') + return + } + + if left_sym.kind == .sum_type && node.name == 'type_name' { + g.write('tos3( /* $left_sym.name */ v_typeof_sumtype_${typ_sym.cname}( (') + g.expr(node.left) + dot := if node.left_type.is_ptr() { '->' } else { '.' } + g.write(')${dot}_typ ))') + return + } + if left_sym.kind == .interface_ && node.name == 'type_name' { + g.write('tos3( /* $left_sym.name */ v_typeof_interface_${typ_sym.cname}( (') + g.expr(node.left) + dot := if node.left_type.is_ptr() { '->' } else { '.' } + g.write(')${dot}_typ ))') + return + } + if node.name == 'str' { + mut rec_type := node.receiver_type + if rec_type.has_flag(.shared_f) { + rec_type = rec_type.clear_flag(.shared_f).set_nr_muls(0) + } + g.gen_str_for_type(rec_type) + } + mut has_cast := false + if left_sym.kind == .map && node.name in ['clone', 'move'] { + receiver_type_name = 'map' + } + if final_left_sym.kind == .array + && node.name in ['repeat', 'sort_with_compare', 'free', 'push_many', 'trim', 'first', 'last', 'pop', 'clone', 'reverse', 'slice', 'pointers'] { + if !(left_sym.info is ast.Alias && typ_sym.has_method(node.name)) { + // `array_Xyz_clone` => `array_clone` + receiver_type_name = 'array' + } + if node.name in ['last', 'first', 'pop'] { + return_type_str := g.typ(node.return_type) + has_cast = true + g.write('(*($return_type_str*)') + } + } + mut name := util.no_dots('${receiver_type_name}_$node.name') + mut array_depth := -1 + mut noscan := '' + if left_sym.kind == .array { + needs_depth := node.name in ['clone', 'repeat'] + if needs_depth { + elem_type := (left_sym.info as ast.Array).elem_type + array_depth = g.get_array_depth(elem_type) + } + maybe_noscan := needs_depth + || node.name in ['pop', 'push', 'push_many', 'reverse', 'grow_cap', 'grow_len'] + if maybe_noscan { + info := left_sym.info as ast.Array + noscan = g.check_noscan(info.elem_type) + } + } else if left_sym.kind == .chan { + if node.name in ['close', 'try_pop', 'try_push'] { + name = 'sync__Channel_$node.name' + } + } else if left_sym.kind == .map { + if node.name == 'keys' { + name = 'map_keys' + } + } + if g.pref.obfuscate && g.cur_mod.name == 'main' && name.starts_with('main__') + && node.name != 'str' { + sym := g.table.get_type_symbol(node.receiver_type) + key := sym.name + '.' + node.name + g.write('/* obf method call: $key */') + name = g.obf_table[key] or { + panic('cgen: obf name "$key" not found, this should never happen') + } + } + // Check if expression is: arr[a..b].clone(), arr[a..].clone() + // if so, then instead of calling array_clone(&array_slice(...)) + // call array_clone_static(array_slice(...)) + mut is_range_slice := false + if node.receiver_type.is_ptr() && !node.left_type.is_ptr() { + if node.left is ast.IndexExpr { + idx := node.left.index + if idx is ast.RangeExpr { + // expr is arr[range].clone() + // use array_clone_static instead of array_clone + name = util.no_dots('${receiver_type_name}_${node.name}_static') + is_range_slice = true + } + } + } + name = g.generic_fn_name(node.concrete_types, name, false) + // TODO2 + // g.generate_tmp_autofree_arg_vars(node, name) + // + // if node.receiver_type != 0 { + // g.write('/*${g.typ(node.receiver_type)}*/') + // g.write('/*expr_type=${g.typ(node.left_type)} rec type=${g.typ(node.receiver_type)}*/') + // } + if !node.receiver_type.is_ptr() && node.left_type.is_ptr() && node.name == 'str' { + g.write('ptr_str(') + } else { + if left_sym.kind == .array { + if array_depth >= 0 { + name = name + '_to_depth' + } + g.write('$name${noscan}(') + } else { + g.write('${name}(') + } + } + if node.receiver_type.is_ptr() && (!node.left_type.is_ptr() + || node.from_embed_type != 0 || (node.left_type.has_flag(.shared_f) && node.name != 'str')) { + // The receiver is a reference, but the caller provided a value + // Add `&` automatically. + // TODO same logic in call_args() + if !is_range_slice { + if !node.left.is_lvalue() { + g.write('ADDR($rec_cc_type, ') + has_cast = true + } else { + g.write('&') + } + } + } else if !node.receiver_type.is_ptr() && node.left_type.is_ptr() && node.name != 'str' + && node.from_embed_type == 0 { + if !node.left_type.has_flag(.shared_f) { + g.write('/*rec*/*') + } + } else if !is_range_slice && node.from_embed_type == 0 && node.name != 'str' { + diff := node.left_type.nr_muls() - node.receiver_type.nr_muls() + if diff < 0 { + // TODO + // g.write('&') + } else if diff > 0 { + g.write('/*diff=$diff*/') + g.write([]byte{len: diff, init: `*`}.bytestr()) + } + } + + // if node.left_type.idx() != node.receiver_type.idx() { + // println('${g.typ(node.left_type)} ${g.typ(node.receiver_type)}') + // } + + if g.is_autofree && node.free_receiver && !g.inside_lambda && !g.is_builtin_mod { + // The receiver expression needs to be freed, use the temp var. + fn_name := node.name.replace('.', '_') + arg_name := '_arg_expr_${fn_name}_0_$node.pos.pos' + g.write('/*af receiver arg*/' + arg_name) + } else { + if left_sym.kind == .array && node.left.is_auto_deref_var() + && node.name in ['first', 'last', 'repeat'] { + g.write('*') + } + if node.left is ast.MapInit { + g.write('(map[]){') + g.expr(node.left) + g.write('}[0]') + } else { + g.expr(node.left) + } + if node.from_embed_type != 0 { + embed_name := typ_sym.embed_name() + if node.left_type.is_ptr() { + g.write('->') + } else { + g.write('.') + } + g.write(embed_name) + } + if node.left_type.has_flag(.shared_f) { + g.write('->val') + } + } + if has_cast { + g.write(')') + } + is_variadic := node.expected_arg_types.len > 0 + && node.expected_arg_types[node.expected_arg_types.len - 1].has_flag(.variadic) + if node.args.len > 0 || is_variadic { + g.write(', ') + } + // ///////// + /* + if name.contains('subkeys') { + println('call_args $name $node.arg_types.len') + for t in node.arg_types { + sym := g.table.get_type_symbol(t) + print('$sym.name ') + } + println('') +} + */ + // /////// + g.call_args(node) + if array_depth >= 0 { + g.write(', $array_depth') + } + g.write(')') +} + +fn (mut g Gen) fn_call(node ast.CallExpr) { + // call struct field with fn type + // TODO: test node.left instead + // left & left_type will be `x` and `x type` in `x.fieldfn()` + // will be `0` for `foo()` + mut is_interface_call := false + mut is_selector_call := false + if node.left_type != 0 { + left_sym := g.table.get_type_symbol(node.left_type) + if left_sym.kind == .interface_ { + is_interface_call = true + g.write('(*') + } + g.expr(node.left) + if node.left_type.is_ptr() { + g.write('->') + } else { + g.write('.') + } + is_selector_call = true + } + mut name := node.name + is_print := name in ['print', 'println', 'eprint', 'eprintln', 'panic'] + print_method := name + is_json_encode := name == 'json.encode' + is_json_encode_pretty := name == 'json.encode_pretty' + is_json_decode := name == 'json.decode' + g.is_json_fn = is_json_encode || is_json_encode_pretty || is_json_decode + mut json_type_str := '' + mut json_obj := '' + if g.is_json_fn { + json_obj = g.new_tmp_var() + mut tmp2 := '' + cur_line := g.go_before_stmt(0) + if is_json_encode || is_json_encode_pretty { + g.gen_json_for_type(node.args[0].typ) + json_type_str = g.typ(node.args[0].typ) + // `json__encode` => `json__encode_User` + // encode_name := c_name(name) + '_' + util.no_dots(json_type_str) + encode_name := js_enc_name(json_type_str) + g.writeln('// json.encode') + g.write('cJSON* $json_obj = ${encode_name}(') + if node.args[0].typ.is_ptr() { + g.write('*') + } + g.call_args(node) + g.writeln(');') + tmp2 = g.new_tmp_var() + if is_json_encode { + g.writeln('string $tmp2 = json__json_print($json_obj);') + } else { + g.writeln('string $tmp2 = json__json_print_pretty($json_obj);') + } + } else { + ast_type := node.args[0].expr as ast.TypeNode + // `json.decode(User, s)` => json.decode_User(s) + typ := c_name(g.typ(ast_type.typ)) + fn_name := c_name(name) + '_' + typ + g.gen_json_for_type(ast_type.typ) + g.writeln('// json.decode') + g.write('cJSON* $json_obj = json__json_parse(') + // Skip the first argument in json.decode which is a type + // its name was already used to generate the function call + g.is_js_call = true + g.call_args(node) + g.is_js_call = false + g.writeln(');') + tmp2 = g.new_tmp_var() + g.writeln('Option_$typ $tmp2 = $fn_name ($json_obj);') + } + if !g.is_autofree { + g.write('cJSON_Delete($json_obj); //del') + } + g.write('\n$cur_line') + name = '' + json_obj = tmp2 + } + if node.language == .c { + // Skip "C." + name = util.no_dots(name[2..]) + } else { + name = c_name(name) + } + // Obfuscate only functions in the main module for now + if g.pref.obfuscate && g.cur_mod.name == 'main' && name.starts_with('main__') { + key := node.name + g.write('/* obf call: $key */') + name = g.obf_table[key] or { + panic('cgen: obf name "$key" not found, this should never happen') + } + } + if !is_selector_call { + name = g.generic_fn_name(node.concrete_types, name, false) + } + // TODO2 + // cgen shouldn't modify ast nodes, this should be moved + // g.generate_tmp_autofree_arg_vars(node, name) + // Handle `print(x)` + mut print_auto_str := false + if is_print && node.args[0].typ != ast.string_type { // && !free_tmp_arg_vars { + mut typ := node.args[0].typ + if typ == 0 { + g.checker_bug('print arg.typ is 0', node.pos) + } + mut sym := g.table.get_type_symbol(typ) + if mut sym.info is ast.Alias { + typ = sym.info.parent_type + sym = g.table.get_type_symbol(typ) + } + // check if alias parent also not a string + if typ != ast.string_type { + expr := node.args[0].expr + if g.is_autofree && !typ.has_flag(.optional) { + // Create a temporary variable so that the value can be freed + tmp := g.new_tmp_var() + // tmps << tmp + g.write('string $tmp = ') + g.gen_expr_to_string(expr, typ) + g.writeln('; ${c_name(print_method)}($tmp); string_free(&$tmp);') + } else { + g.write('${c_name(print_method)}(') + g.gen_expr_to_string(expr, typ) + g.write(')') + } + print_auto_str = true + } + } + if !print_auto_str { + if g.pref.is_debug && node.name == 'panic' { + paline, pafile, pamod, pafn := g.panic_debug_info(node.pos) + g.write('panic_debug($paline, tos3("$pafile"), tos3("$pamod"), tos3("$pafn"), ') + g.call_args(node) + g.write(')') + } else { + // Simple function call + // if free_tmp_arg_vars { + // g.writeln(';') + // g.write(cur_line + ' /* <== af cur line*/') + // } + g.write(g.get_ternary_name(name)) + if is_interface_call { + g.write(')') + } + mut tmp_cnt_save := -1 + g.write('(') + if g.is_json_fn { + g.write(json_obj) + } else { + if node.is_keep_alive + && g.pref.gc_mode in [.boehm_full, .boehm_incr, .boehm_full_opt, .boehm_incr_opt] { + cur_line := g.go_before_stmt(0) + tmp_cnt_save = g.keep_alive_call_pregen(node) + g.write(cur_line) + for i in 0 .. node.args.len { + if i > 0 { + g.write(', ') + } + g.write('__tmp_arg_${tmp_cnt_save + i}') + } + } else { + g.call_args(node) + } + } + g.write(')') + if tmp_cnt_save >= 0 { + g.writeln(';') + g.keep_alive_call_postgen(node, tmp_cnt_save) + } + } + } + g.is_json_fn = false +} + +fn (mut g Gen) autofree_call_pregen(node ast.CallExpr) { + // g.writeln('// autofree_call_pregen()') + // Create a temporary var before fn call for each argument in order to free it (only if it's a complex expression, + // like `foo(get_string())` or `foo(a + b)` + mut free_tmp_arg_vars := g.is_autofree && !g.is_builtin_mod && node.args.len > 0 + && !node.args[0].typ.has_flag(.optional) // TODO copy pasta checker.v + if !free_tmp_arg_vars { + return + } + if g.is_js_call { + return + } + if g.inside_const { + return + } + free_tmp_arg_vars = false // set the flag to true only if we have at least one arg to free + g.tmp_count2++ + mut scope := g.file.scope.innermost(node.pos.pos) + // prepend the receiver for now (TODO turn the receiver into a CallArg everywhere?) + mut args := [ast.CallArg{ + typ: node.receiver_type + expr: node.left + is_tmp_autofree: node.free_receiver + }] + args << node.args + // for i, arg in node.args { + for i, arg in args { + if !arg.is_tmp_autofree { + continue + } + if arg.expr is ast.CallExpr { + // Any argument can be an expression that has to be freed. Generate a tmp expression + // for each of those recursively. + g.autofree_call_pregen(arg.expr) + } + free_tmp_arg_vars = true + // t := g.new_tmp_var() + '_arg_expr_${name}_$i' + fn_name := node.name.replace('.', '_') // can't use name... + // t := '_tt${g.tmp_count2}_arg_expr_${fn_name}_$i' + t := '_arg_expr_${fn_name}_${i}_$node.pos.pos' + // g.called_fn_name = name + used := false // scope.known_var(t) + mut s := '$t = ' + if used { + // This means this tmp var name was already used (the same function was called and + // `_arg_fnname_1` was already generated). + // We do not need to declare this variable again, so just generate `t = ...` + // instead of `string t = ...`, and we need to mark this variable as unused, + // so that it's freed after the call. (Used tmp arg vars are not freed to avoid double frees). + if x := scope.find(t) { + match mut x { + ast.Var { x.is_used = false } + else {} + } + } + s = '$t = ' + } else { + scope.register(ast.Var{ + name: t + typ: ast.string_type + is_autofree_tmp: true + pos: node.pos + }) + s = 'string $t = ' + } + // g.expr(arg.expr) + s += g.expr_string(arg.expr) + // g.writeln(';// new af pre') + s += ';// new af2 pre' + g.strs_to_free0 << s + // This tmp arg var will be freed with the rest of the vars at the end of the scope. + } +} + +fn (mut g Gen) autofree_call_postgen(node_pos int) { + // if g.strs_to_free.len == 0 { + // return + // } + // g.writeln('\n/* strs_to_free3: $g.nr_vars_to_free */') + // if g.nr_vars_to_free <= 0 { + // return + // } + /* + for s in g.strs_to_free { + g.writeln('string_free(&$s);') + } + if !g.inside_or_block { + // we need to free the vars both inside the or block (in case of an error) and after it + // if we reset the array here, then the vars will not be freed after the block. + g.strs_to_free = [] + } + */ + if g.inside_vweb_tmpl { + return + } + // g.doing_autofree_tmp = true + // g.write('/* postgen */') + scope := g.file.scope.innermost(node_pos) + for _, obj in scope.objects { + match mut obj { + ast.Var { + // if var.typ == 0 { + // // TODO why 0? + // continue + // } + is_optional := obj.typ.has_flag(.optional) + if is_optional { + // TODO: free optionals + continue + } + if !obj.is_autofree_tmp { + continue + } + if obj.is_used { + // this means this tmp expr var has already been freed + continue + } + obj.is_used = true // TODO bug? sets all vars is_used to true + g.autofree_variable(obj) + // g.nr_vars_to_free-- + } + else {} + } + } + // g.write('/* postgen end */') + // g.doing_autofree_tmp = false +} + +fn (mut g Gen) call_args(node ast.CallExpr) { + args := if g.is_js_call { node.args[1..] } else { node.args } + expected_types := node.expected_arg_types + // only v variadic, C variadic args will be appeneded like normal args + is_variadic := expected_types.len > 0 && expected_types.last().has_flag(.variadic) + && node.language == .v + for i, arg in args { + if is_variadic && i == expected_types.len - 1 { + break + } + use_tmp_var_autofree := g.is_autofree && arg.typ == ast.string_type && arg.is_tmp_autofree + && !g.inside_const && !g.is_builtin_mod + // g.write('/* af=$arg.is_tmp_autofree */') + // some c fn definitions dont have args (cfns.v) or are not updated in checker + // when these are fixed we wont need this check + if i < expected_types.len { + if use_tmp_var_autofree { + if arg.is_tmp_autofree { // && !g.is_js_call { + // We saved expressions in temp variables so that they can be freed later. + // `foo(str + str2) => x := str + str2; foo(x); x.free()` + // g.write('_arg_expr_${g.called_fn_name}_$i') + // Use these variables here. + fn_name := node.name.replace('.', '_') + // name := '_tt${g.tmp_count2}_arg_expr_${fn_name}_$i' + name := '_arg_expr_${fn_name}_${i + 1}_$node.pos.pos' + g.write('/*af arg*/' + name) + } + } else { + if node.concrete_types.len > 0 && arg.expr.is_auto_deref_var() && !arg.is_mut + && !expected_types[i].is_ptr() { + g.write('*') + } + g.ref_or_deref_arg(arg, expected_types[i], node.language) + } + } else { + if use_tmp_var_autofree { + // TODO copypasta, move to an inline fn + fn_name := node.name.replace('.', '_') + // name := '_tt${g.tmp_count2}_arg_expr_${fn_name}_$i' + name := '_arg_expr_${fn_name}_${i + 1}_$node.pos.pos' + g.write('/*af arg2*/' + name) + } else { + g.expr(arg.expr) + } + } + if i < args.len - 1 || is_variadic { + g.write(', ') + } + } + arg_nr := expected_types.len - 1 + if is_variadic { + varg_type := expected_types.last() + variadic_count := args.len - arg_nr + arr_sym := g.table.get_type_symbol(varg_type) + mut arr_info := arr_sym.info as ast.Array + if varg_type.has_flag(.generic) { + if fn_def := g.table.find_fn(node.name) { + if utyp := g.table.resolve_generic_to_concrete(arr_info.elem_type, fn_def.generic_names, + node.concrete_types) + { + arr_info.elem_type = utyp + } + } else { + g.error('unable to find function $node.name', node.pos) + } + } + elem_type := g.typ(arr_info.elem_type) + if args.len > 0 && args[args.len - 1].expr is ast.ArrayDecompose { + g.expr(args[args.len - 1].expr) + } else { + if variadic_count > 0 { + noscan := g.check_noscan(arr_info.elem_type) + g.write('new_array_from_c_array${noscan}($variadic_count, $variadic_count, sizeof($elem_type), _MOV(($elem_type[$variadic_count]){') + for j in arg_nr .. args.len { + g.ref_or_deref_arg(args[j], arr_info.elem_type, node.language) + if j < args.len - 1 { + g.write(', ') + } + } + g.write('}))') + } else { + g.write('__new_array(0, 0, sizeof($elem_type))') + } + } + } +} + +// similar to `autofree_call_pregen()` but only to to handle [keep_args_alive] for C functions +fn (mut g Gen) keep_alive_call_pregen(node ast.CallExpr) int { + g.empty_line = true + g.writeln('// keep_alive_call_pregen()') + // reserve the next tmp_vars for arguments + tmp_cnt_save := g.tmp_count + 1 + g.tmp_count += node.args.len + for i, arg in node.args { + // save all arguments in temp vars (not only pointers) to make sure the + // evaluation order is preserved + expected_type := node.expected_arg_types[i] + typ := g.table.get_type_symbol(expected_type).cname + g.write('$typ __tmp_arg_${tmp_cnt_save + i} = ') + // g.expr(arg.expr) + g.ref_or_deref_arg(arg, expected_type, node.language) + g.writeln(';') + } + g.empty_line = false + return tmp_cnt_save +} + +fn (mut g Gen) keep_alive_call_postgen(node ast.CallExpr, tmp_cnt_save int) { + g.writeln('// keep_alive_call_postgen()') + for i, expected_type in node.expected_arg_types { + if expected_type.is_ptr() || expected_type.is_pointer() { + g.writeln('GC_reachable_here(__tmp_arg_${tmp_cnt_save + i});') + } + } +} + +[inline] +fn (mut g Gen) ref_or_deref_arg(arg ast.CallArg, expected_type ast.Type, lang ast.Language) { + arg_is_ptr := expected_type.is_ptr() || expected_type.idx() in ast.pointer_type_idxs + expr_is_ptr := arg.typ.is_ptr() || arg.typ.idx() in ast.pointer_type_idxs + if expected_type == 0 { + g.checker_bug('ref_or_deref_arg expected_type is 0', arg.pos) + } + exp_sym := g.table.get_type_symbol(expected_type) + mut needs_closing := false + if arg.is_mut && !arg_is_ptr { + g.write('&/*mut*/') + } else if arg_is_ptr && !expr_is_ptr { + if arg.is_mut { + if exp_sym.kind == .array { + if arg.expr is ast.Ident && (arg.expr as ast.Ident).kind == .variable { + g.write('&/*arr*/') + g.expr(arg.expr) + } else { + // Special case for mutable arrays. We can't `&` function + // results, have to use `(array[]){ expr }[0]` hack. + g.write('&/*111*/(array[]){') + g.expr(arg.expr) + g.write('}[0]') + } + return + } + } + if !g.is_json_fn { + if arg.typ == 0 { + g.checker_bug('ref_or_deref_arg arg.typ is 0', arg.pos) + } + arg_typ_sym := g.table.get_type_symbol(arg.typ) + expected_deref_type := if expected_type.is_ptr() { + expected_type.deref() + } else { + expected_type + } + deref_sym := g.table.get_type_symbol(expected_deref_type) + if !((arg_typ_sym.kind == .function) + || deref_sym.kind in [.sum_type, .interface_]) && lang != .c { + if arg.expr.is_lvalue() { + g.write('(voidptr)&/*qq*/') + } else { + mut atype := expected_deref_type + if atype.has_flag(.generic) { + atype = g.unwrap_generic(atype) + } + if atype.has_flag(.generic) { + g.write('(voidptr)&/*qq*/') + } else { + needs_closing = true + g.write('ADDR(${g.typ(atype)}/*qq*/, ') + } + } + } + } + } else if arg.typ.has_flag(.shared_f) && !expected_type.has_flag(.shared_f) { + if expected_type.is_ptr() { + g.write('&') + } + g.expr(arg.expr) + g.write('->val') + return + } + g.expr_with_cast(arg.expr, arg.typ, expected_type) + if needs_closing { + g.write(')') + } +} + +fn (mut g Gen) is_gui_app() bool { + $if windows { + if g.force_main_console { + return false + } + for cf in g.table.cflags { + if cf.value == 'gdi32' { + return true + } + } + } + return false +} + +fn (g &Gen) fileis(s string) bool { + return g.file.path.contains(s) +} + +fn (mut g Gen) write_fn_attrs(attrs []ast.Attr) string { + mut msvc_attrs := '' + for attr in attrs { + match attr.name { + 'inline' { + g.write('inline ') + } + 'noinline' { + // since these are supported by GCC, clang and MSVC, we can consider them officially supported. + g.write('__NOINLINE ') + } + 'noreturn' { + // a `[noreturn]` tag tells the compiler, that a function + // *DOES NOT RETURN* to its callsites. + // See: https://en.cppreference.com/w/c/language/_Noreturn + // Such functions should have no return type. They can be used + // in places where `panic(err)` or `exit(0)` can be used. + // panic/1 and exit/0 themselves will also be marked as + // `[noreturn]` soon. + // These functions should have busy `for{}` loops injected + // at their end, when they do not end by calling other fns + // marked by `[noreturn]`. + g.write('VNORETURN ') + } + 'irq_handler' { + g.write('__IRQHANDLER ') + } + '_cold' { + // GCC/clang attributes + // prefixed by _ to indicate they're for advanced users only and not really supported by V. + // source for descriptions: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes + // The cold attribute on functions is used to inform the compiler that the function is unlikely + // to be executed. The function is optimized for size rather than speed and on many targets it + // is placed into a special subsection of the text section so all cold functions appear close + // together, improving code locality of non-cold parts of program. + g.write('__attribute__((cold)) ') + } + '_constructor' { + // The constructor attribute causes the function to be called automatically before execution + // enters main (). + g.write('__attribute__((constructor)) ') + } + '_destructor' { + // The destructor attribute causes the function to be called automatically after main () + // completes or exit () is called. + g.write('__attribute__((destructor)) ') + } + '_flatten' { + // Generally, inlining into a function is limited. For a function marked with this attribute, + // every call inside this function is inlined, if possible. + g.write('__attribute__((flatten)) ') + } + '_hot' { + // The hot attribute on a function is used to inform the compiler that the function is a hot + // spot of the compiled program. + g.write('__attribute__((hot)) ') + } + '_malloc' { + // This tells the compiler that a function is malloc-like, i.e., that the pointer P returned by + // the function cannot alias any other pointer valid when the function returns, and moreover no + // pointers to valid objects occur in any storage addressed by P. + g.write('__attribute__((malloc)) ') + } + '_pure' { + // Calls to functions whose return value is not affected by changes to the observable state + // of the program and that have no observable effects on such state other than to return a + // value may lend themselves to optimizations such as common subexpression elimination. + // Declaring such functions with the const attribute allows GCC to avoid emitting some calls in + // repeated invocations of the function with the same argument values. + g.write('__attribute__((const)) ') + } + '_naked' { + g.write('__attribute__((naked)) ') + } + 'windows_stdcall' { + // windows attributes (msvc/mingw) + // prefixed by windows to indicate they're for advanced users only and not really supported by V. + msvc_attrs += '__stdcall ' + } + 'console' { + g.force_main_console = true + } + else { + // nothing but keep V happy + } + } + } + return msvc_attrs +} diff --git a/v_windows/v/old/vlib/v/gen/c/index.v b/v_windows/v/old/vlib/v/gen/c/index.v new file mode 100644 index 0000000..26dc455 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/c/index.v @@ -0,0 +1,452 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module c + +import v.ast +import v.util + +fn (mut g Gen) index_expr(node ast.IndexExpr) { + if node.index is ast.RangeExpr { + g.range_expr(node, node.index) + } else { + sym := g.table.get_final_type_symbol(node.left_type) + if sym.kind == .array { + g.index_of_array(node, sym) + } else if sym.kind == .array_fixed { + g.index_of_fixed_array(node, sym) + } else if sym.kind == .map { + g.index_of_map(node, sym) + } else if sym.kind == .string && !node.left_type.is_ptr() { + is_direct_array_access := g.fn_decl != 0 && g.fn_decl.is_direct_arr + if is_direct_array_access { + g.expr(node.left) + g.write('.str[ ') + g.expr(node.index) + g.write(']') + } else { + gen_or := node.or_expr.kind != .absent || node.is_option + if gen_or { + tmp_opt := g.new_tmp_var() + cur_line := g.go_before_stmt(0) + g.out.write_string(util.tabs(g.indent)) + opt_elem_type := g.typ(ast.byte_type.set_flag(.optional)) + g.write('$opt_elem_type $tmp_opt = string_at_with_check(') + g.expr(node.left) + g.write(', ') + g.expr(node.index) + g.writeln(');') + if !node.is_option { + g.or_block(tmp_opt, node.or_expr, ast.byte_type) + } + g.write('\n$cur_line*(byte*)&${tmp_opt}.data') + } else { + g.write('string_at(') + g.expr(node.left) + g.write(', ') + g.expr(node.index) + g.write(')') + } + } + } else { + g.expr(node.left) + g.write('[') + g.expr(node.index) + g.write(']') + } + } +} + +fn (mut g Gen) range_expr(node ast.IndexExpr, range ast.RangeExpr) { + sym := g.table.get_final_type_symbol(node.left_type) + if sym.kind == .string { + g.write('string_substr(') + g.expr(node.left) + } else if sym.kind == .array { + g.write('array_slice(') + if node.left_type.is_ptr() { + g.write('*') + } + g.expr(node.left) + } else if sym.kind == .array_fixed { + // Convert a fixed array to V array when doing `fixed_arr[start..end]` + info := sym.info as ast.ArrayFixed + noscan := g.check_noscan(info.elem_type) + g.write('array_slice(new_array_from_c_array${noscan}(') + g.write('$info.size') + g.write(', $info.size') + g.write(', sizeof(') + if node.left_type.is_ptr() { + g.write('(*') + } + g.expr(node.left) + if node.left_type.is_ptr() { + g.write(')') + } + g.write('[0]), ') + if node.left_type.is_ptr() { + g.write('*') + } + g.expr(node.left) + g.write(')') + } else { + g.expr(node.left) + } + g.write(', ') + if range.has_low { + g.expr(range.low) + } else { + g.write('0') + } + g.write(', ') + if range.has_high { + g.expr(range.high) + } else if sym.kind == .array_fixed { + info := sym.info as ast.ArrayFixed + g.write('$info.size') + } else if node.left_type.is_ptr() { + g.write('(') + g.write('*') + g.expr(node.left) + g.write(')') + g.write('.len') + } else { + g.expr(node.left) + g.write('.len') + } + g.write(')') +} + +fn (mut g Gen) index_of_array(node ast.IndexExpr, sym ast.TypeSymbol) { + gen_or := node.or_expr.kind != .absent || node.is_option + left_is_ptr := node.left_type.is_ptr() + info := sym.info as ast.Array + elem_type_str := g.typ(info.elem_type) + elem_type := info.elem_type + elem_typ := g.table.get_type_symbol(elem_type) + // `vals[i].field = x` is an exception and requires `array_get`: + // `(*(Val*)array_get(vals, i)).field = x;` + is_selector := node.left is ast.SelectorExpr + if g.is_assign_lhs && !is_selector && node.is_setter { + is_direct_array_access := g.fn_decl != 0 && g.fn_decl.is_direct_arr + is_op_assign := g.assign_op != .assign && info.elem_type != ast.string_type + array_ptr_type_str := match elem_typ.kind { + .function { 'voidptr*' } + else { '$elem_type_str*' } + } + if is_direct_array_access { + g.write('(($array_ptr_type_str)') + } else if is_op_assign { + g.write('(*($array_ptr_type_str)array_get(') + if left_is_ptr && !node.left_type.has_flag(.shared_f) { + g.write('*') + } + } else { + g.is_arraymap_set = true // special handling of assign_op and closing with '})' + g.write('array_set(') + if !left_is_ptr || node.left_type.has_flag(.shared_f) { + g.write('&') + } + } + g.expr(node.left) + if node.left_type.has_flag(.shared_f) { + if left_is_ptr { + g.write('->val') + } else { + g.write('.val') + } + } + if is_direct_array_access { + if left_is_ptr && !node.left_type.has_flag(.shared_f) { + g.write('->') + } else { + g.write('.') + } + g.write('data)[') + g.expr(node.index) + g.write(']') + } else { + g.write(', ') + g.expr(node.index) + if !is_op_assign { + mut need_wrapper := true + /* + match node.right { + ast.EnumVal, ast.Ident { + // `&x` is enough for variables and enums + // `&(Foo[]){ ... }` is only needed for function calls and literals + need_wrapper = false + } + else {} + } + */ + if need_wrapper { + if elem_typ.kind == .function { + g.write(', &(voidptr[]) { ') + } else { + g.write(', &($elem_type_str[]) { ') + } + } else { + g.write(', &') + } + } else { + // `x[0] *= y` + g.write('))') + } + } + } else { + is_direct_array_access := g.fn_decl != 0 && g.fn_decl.is_direct_arr + array_ptr_type_str := match elem_typ.kind { + .function { 'voidptr*' } + else { '$elem_type_str*' } + } + // do not clone inside `opt_ok(opt_ok(&(string[]) {..})` before returns + needs_clone := info.elem_type == ast.string_type_idx && g.is_autofree && !(g.inside_return + && g.fn_decl.return_type.has_flag(.optional)) && !g.is_assign_lhs + is_gen_or_and_assign_rhs := gen_or && !g.discard_or_result + cur_line := if is_gen_or_and_assign_rhs { + line := g.go_before_stmt(0) + g.out.write_string(util.tabs(g.indent)) + line + } else { + '' + } + tmp_opt := if gen_or { g.new_tmp_var() } else { '' } + tmp_opt_ptr := if gen_or { g.new_tmp_var() } else { '' } + if gen_or { + g.write('$array_ptr_type_str $tmp_opt_ptr = ($array_ptr_type_str)/*ee elem_ptr_typ */(array_get_with_check(') + } else { + if needs_clone { + g.write('/*2*/string_clone(') + } + if g.is_fn_index_call { + if elem_typ.info is ast.FnType { + g.write('((') + g.write_fn_ptr_decl(&elem_typ.info, '') + g.write(')(*($array_ptr_type_str)/*ee elem_typ */array_get(') + } + if left_is_ptr && !node.left_type.has_flag(.shared_f) { + g.write('*') + } + } else if is_direct_array_access { + g.write('(($array_ptr_type_str)') + } else { + g.write('(*($array_ptr_type_str)/*ee elem_typ */array_get(') + if left_is_ptr && !node.left_type.has_flag(.shared_f) { + g.write('*') + } + } + } + g.expr(node.left) + // TODO: test direct_array_access when 'shared' is implemented + if node.left_type.has_flag(.shared_f) { + if left_is_ptr { + g.write('->val') + } else { + g.write('.val') + } + } + if is_direct_array_access && !gen_or { + if left_is_ptr && !node.left_type.has_flag(.shared_f) { + g.write('->') + } else { + g.write('.') + } + g.write('data)[') + g.expr(node.index) + g.write(']') + } else { + g.write(', ') + g.expr(node.index) + if g.is_fn_index_call { + g.write(')))') + } else { + g.write('))') + } + } + if needs_clone { + g.write(')') + } + if gen_or { + g.writeln(';') + opt_elem_type := g.typ(elem_type.set_flag(.optional)) + g.writeln('$opt_elem_type $tmp_opt = {0};') + g.writeln('if ($tmp_opt_ptr) {') + g.writeln('\t*(($elem_type_str*)&${tmp_opt}.data) = *(($elem_type_str*)$tmp_opt_ptr);') + g.writeln('} else {') + g.writeln('\t${tmp_opt}.state = 2; ${tmp_opt}.err = _v_error(_SLIT("array index out of range"));') + g.writeln('}') + if !node.is_option { + g.or_block(tmp_opt, node.or_expr, elem_type) + } + g.write('\n$cur_line*($elem_type_str*)${tmp_opt}.data') + } + } +} + +fn (mut g Gen) index_of_fixed_array(node ast.IndexExpr, sym ast.TypeSymbol) { + info := sym.info as ast.ArrayFixed + elem_type := info.elem_type + elem_sym := g.table.get_type_symbol(elem_type) + is_fn_index_call := g.is_fn_index_call && elem_sym.info is ast.FnType + + if is_fn_index_call { + g.write('(*') + } + if node.left_type.is_ptr() { + g.write('(*') + g.expr(node.left) + g.write(')') + } else { + g.expr(node.left) + } + g.write('[') + direct := g.fn_decl != 0 && g.fn_decl.is_direct_arr + if direct || node.index is ast.IntegerLiteral { + g.expr(node.index) + } else { + // bounds check + g.write('v_fixed_index(') + g.expr(node.index) + g.write(', $info.size)') + } + g.write(']') + if is_fn_index_call { + g.write(')') + } +} + +fn (mut g Gen) index_of_map(node ast.IndexExpr, sym ast.TypeSymbol) { + gen_or := node.or_expr.kind != .absent || node.is_option + left_is_ptr := node.left_type.is_ptr() + info := sym.info as ast.Map + key_type_str := g.typ(info.key_type) + elem_type := info.value_type + elem_type_str := g.typ(elem_type) + elem_typ := g.table.get_type_symbol(elem_type) + get_and_set_types := elem_typ.kind in [.struct_, .map] + if g.is_assign_lhs && !g.is_arraymap_set && !get_and_set_types { + if g.assign_op == .assign || info.value_type == ast.string_type { + g.is_arraymap_set = true + g.write('map_set(') + } else { + if node.is_setter { + g.write('(*(($elem_type_str*)map_get_and_set((map*)') + } else { + g.write('(*(($elem_type_str*)map_get((map*)') + } + } + if !left_is_ptr || node.left_type.has_flag(.shared_f) { + g.write('&') + } + if node.left is ast.IndexExpr { + g.inside_map_index = true + g.expr(node.left) + g.inside_map_index = false + } else { + g.expr(node.left) + } + if node.left_type.has_flag(.shared_f) { + if left_is_ptr { + g.write('->val') + } else { + g.write('.val') + } + } + g.write(', &($key_type_str[]){') + g.expr(node.index) + g.write('}') + if elem_typ.kind == .function { + g.write(', &(voidptr[]) { ') + } else { + g.arraymap_set_pos = g.out.len + g.write(', &($elem_type_str[]) { ') + } + if g.assign_op != .assign && info.value_type != ast.string_type { + zero := g.type_default(info.value_type) + g.write('$zero })))') + } + } else if g.inside_map_postfix || g.inside_map_infix || g.inside_map_index + || (g.is_assign_lhs && !g.is_arraymap_set && get_and_set_types) { + zero := g.type_default(info.value_type) + if node.is_setter { + g.write('(*($elem_type_str*)map_get_and_set((map*)') + } else { + g.write('(*($elem_type_str*)map_get((map*)') + } + if !left_is_ptr { + g.write('&') + } + g.expr(node.left) + g.write(', &($key_type_str[]){') + g.expr(node.index) + g.write('}, &($elem_type_str[]){ $zero }))') + } else { + zero := g.type_default(info.value_type) + is_gen_or_and_assign_rhs := gen_or && !g.discard_or_result + cur_line := if is_gen_or_and_assign_rhs { + line := g.go_before_stmt(0) + g.out.write_string(util.tabs(g.indent)) + line + } else { + '' + } + tmp_opt := if gen_or { g.new_tmp_var() } else { '' } + tmp_opt_ptr := if gen_or { g.new_tmp_var() } else { '' } + if gen_or { + g.write('$elem_type_str* $tmp_opt_ptr = ($elem_type_str*)/*ee elem_ptr_typ */(map_get_check(') + } else { + if g.is_fn_index_call { + if elem_typ.info is ast.FnType { + g.write('((') + g.write_fn_ptr_decl(&elem_typ.info, '') + g.write(')(*(voidptr*)map_get(') + } + } else if elem_typ.kind == .function { + g.write('(*(voidptr*)map_get(') + } else { + g.write('(*($elem_type_str*)map_get(') + } + } + if !left_is_ptr || node.left_type.has_flag(.shared_f) { + g.write('ADDR(map, ') + g.expr(node.left) + } else { + g.write('(') + g.expr(node.left) + } + if node.left_type.has_flag(.shared_f) { + if left_is_ptr { + g.write('->val') + } else { + g.write('.val') + } + } + g.write('), &($key_type_str[]){') + g.expr(node.index) + g.write('}') + if gen_or { + g.write('))') + } else if g.is_fn_index_call { + g.write(', &(voidptr[]){ $zero })))') + } else if elem_typ.kind == .function { + g.write(', &(voidptr[]){ $zero }))') + } else { + g.write(', &($elem_type_str[]){ $zero }))') + } + if gen_or { + g.writeln(';') + opt_elem_type := g.typ(elem_type.set_flag(.optional)) + g.writeln('$opt_elem_type $tmp_opt = {0};') + g.writeln('if ($tmp_opt_ptr) {') + g.writeln('\t*(($elem_type_str*)&${tmp_opt}.data) = *(($elem_type_str*)$tmp_opt_ptr);') + g.writeln('} else {') + g.writeln('\t${tmp_opt}.state = 2; ${tmp_opt}.err = _v_error(_SLIT("array index out of range"));') + g.writeln('}') + if !node.is_option { + g.or_block(tmp_opt, node.or_expr, elem_type) + } + g.write('\n$cur_line*($elem_type_str*)${tmp_opt}.data') + } + } +} diff --git a/v_windows/v/old/vlib/v/gen/c/infix_expr.v b/v_windows/v/old/vlib/v/gen/c/infix_expr.v new file mode 100644 index 0000000..901e604 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/c/infix_expr.v @@ -0,0 +1,564 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module c + +import v.ast +import v.token +import v.util + +fn (mut g Gen) infix_expr(node ast.InfixExpr) { + if node.auto_locked != '' { + g.writeln('sync__RwMutex_lock(&$node.auto_locked->mtx);') + } + match node.op { + .arrow { + g.infix_expr_arrow_op(node) + } + .eq, .ne { + g.infix_expr_eq_op(node) + } + .gt, .ge, .lt, .le { + g.infix_expr_cmp_op(node) + } + .key_in, .not_in { + g.infix_expr_in_op(node) + } + .key_is, .not_is { + g.infix_expr_is_op(node) + } + .plus, .minus, .mul, .div, .mod { + g.infix_expr_arithmetic_op(node) + } + .left_shift { + g.infix_expr_left_shift_op(node) + } + else { + // `x & y == 0` => `(x & y) == 0` in C + need_par := node.op in [.amp, .pipe, .xor] + if need_par { + g.write('(') + } + g.gen_plain_infix_expr(node) + if need_par { + g.write(')') + } + } + } + if node.auto_locked != '' { + g.writeln(';') + g.write('sync__RwMutex_unlock(&$node.auto_locked->mtx)') + } +} + +// infix_expr_arrow_op generates C code for pushing into channels (chan <- val) +fn (mut g Gen) infix_expr_arrow_op(node ast.InfixExpr) { + left := g.unwrap(node.left_type) + styp := left.sym.cname + elem_type := (left.sym.info as ast.Chan).elem_type + gen_or := node.or_block.kind != .absent + tmp_opt := if gen_or { g.new_tmp_var() } else { '' } + if gen_or { + elem_styp := g.typ(elem_type) + g.register_chan_push_optional_call(elem_styp, styp) + g.write('Option_void $tmp_opt = __Option_${styp}_pushval(') + } else { + g.write('__${styp}_pushval(') + } + g.expr(node.left) + g.write(', ') + g.expr(node.right) + g.write(')') + if gen_or { + g.or_block(tmp_opt, node.or_block, ast.void_type) + } +} + +// infix_expr_eq_op generates code for `==` and `!=` +fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) { + left := g.unwrap(node.left_type) + right := g.unwrap(node.right_type) + has_operator_overloading := g.table.type_has_method(left.sym, '==') + if (left.typ.is_ptr() && right.typ.is_int()) || (right.typ.is_ptr() && left.typ.is_int()) { + g.gen_plain_infix_expr(node) + } else if (left.typ.idx() == ast.string_type_idx || (!has_operator_overloading + && left.unaliased.idx() == ast.string_type_idx)) && node.right is ast.StringLiteral + && (node.right as ast.StringLiteral).val == '' { + // `str == ''` -> `str.len == 0` optimization + g.write('(') + g.expr(node.left) + g.write(')') + arrow := if left.typ.is_ptr() { '->' } else { '.' } + g.write('${arrow}len $node.op 0') + } else if has_operator_overloading { + if node.op == .ne { + g.write('!') + } + g.write(g.typ(left.unaliased.set_nr_muls(0))) + g.write('__eq(') + g.write('*'.repeat(left.typ.nr_muls())) + g.expr(node.left) + g.write(', ') + g.write('*'.repeat(right.typ.nr_muls())) + g.expr(node.right) + g.write(')') + } else if left.typ.idx() == right.typ.idx() + && left.sym.kind in [.array, .array_fixed, .alias, .map, .struct_, .sum_type] { + match left.sym.kind { + .alias { + ptr_typ := g.gen_alias_equality_fn(left.typ) + if node.op == .ne { + g.write('!') + } + g.write('${ptr_typ}_alias_eq(') + if left.typ.is_ptr() { + g.write('*') + } + g.expr(node.left) + g.write(', ') + if right.typ.is_ptr() { + g.write('*') + } + g.expr(node.right) + g.write(')') + } + .array { + ptr_typ := g.gen_array_equality_fn(left.unaliased.clear_flag(.shared_f)) + if node.op == .ne { + g.write('!') + } + g.write('${ptr_typ}_arr_eq(') + if left.typ.is_ptr() && !left.typ.has_flag(.shared_f) { + g.write('*') + } + g.expr(node.left) + if left.typ.has_flag(.shared_f) { + if left.typ.is_ptr() { + g.write('->val') + } else { + g.write('.val') + } + } + g.write(', ') + if right.typ.is_ptr() && !right.typ.has_flag(.shared_f) { + g.write('*') + } + g.expr(node.right) + if right.typ.has_flag(.shared_f) { + if right.typ.is_ptr() { + g.write('->val') + } else { + g.write('.val') + } + } + g.write(')') + } + .array_fixed { + ptr_typ := g.gen_fixed_array_equality_fn(left.unaliased) + if node.op == .ne { + g.write('!') + } + g.write('${ptr_typ}_arr_eq(') + if left.typ.is_ptr() { + g.write('*') + } + if node.left is ast.ArrayInit { + s := g.typ(left.unaliased) + g.write('($s)') + } + g.expr(node.left) + g.write(', ') + if node.right is ast.ArrayInit { + s := g.typ(right.unaliased) + g.write('($s)') + } + g.expr(node.right) + g.write(')') + } + .map { + ptr_typ := g.gen_map_equality_fn(left.unaliased) + if node.op == .ne { + g.write('!') + } + g.write('${ptr_typ}_map_eq(') + if left.typ.is_ptr() { + g.write('*') + } + g.expr(node.left) + g.write(', ') + if right.typ.is_ptr() { + g.write('*') + } + g.expr(node.right) + g.write(')') + } + .struct_ { + ptr_typ := g.gen_struct_equality_fn(left.unaliased) + if node.op == .ne { + g.write('!') + } + g.write('${ptr_typ}_struct_eq(') + if left.typ.is_ptr() { + g.write('*') + } + g.expr(node.left) + g.write(', ') + if right.typ.is_ptr() { + g.write('*') + } + g.expr(node.right) + g.write(')') + } + .sum_type { + ptr_typ := g.gen_sumtype_equality_fn(left.unaliased) + if node.op == .ne { + g.write('!') + } + g.write('${ptr_typ}_sumtype_eq(') + if left.typ.is_ptr() { + g.write('*') + } + g.expr(node.left) + g.write(', ') + if right.typ.is_ptr() { + g.write('*') + } + g.expr(node.right) + g.write(')') + } + else {} + } + } else if left.unaliased.idx() in [ast.u32_type_idx, ast.u64_type_idx] + && right.unaliased.is_signed() { + g.gen_safe_integer_infix_expr( + op: node.op + unsigned_type: left.unaliased + unsigned_expr: node.left + signed_type: right.unaliased + signed_expr: node.right + ) + } else if right.unaliased.idx() in [ast.u32_type_idx, ast.u64_type_idx] + && left.unaliased.is_signed() { + g.gen_safe_integer_infix_expr( + op: node.op + reverse: true + unsigned_type: right.unaliased + unsigned_expr: node.right + signed_type: left.unaliased + signed_expr: node.left + ) + } else { + g.gen_plain_infix_expr(node) + } +} + +// infix_expr_cmp_op generates code for `<`, `<=`, `>`, `>=` +// It handles operator overloading when necessary +fn (mut g Gen) infix_expr_cmp_op(node ast.InfixExpr) { + left := g.unwrap(node.left_type) + right := g.unwrap(node.right_type) + has_operator_overloading := g.table.type_has_method(left.sym, '<') + if left.sym.kind == right.sym.kind && has_operator_overloading { + if node.op in [.le, .ge] { + g.write('!') + } + g.write(g.typ(left.typ.set_nr_muls(0))) + g.write('__lt') + if node.op in [.lt, .ge] { + g.write('(') + g.write('*'.repeat(left.typ.nr_muls())) + g.expr(node.left) + g.write(', ') + g.write('*'.repeat(right.typ.nr_muls())) + g.expr(node.right) + g.write(')') + } else { + g.write('(') + g.write('*'.repeat(right.typ.nr_muls())) + g.expr(node.right) + g.write(', ') + g.write('*'.repeat(left.typ.nr_muls())) + g.expr(node.left) + g.write(')') + } + } else if left.unaliased.idx() in [ast.u32_type_idx, ast.u64_type_idx] + && right.unaliased.is_signed() { + g.gen_safe_integer_infix_expr( + op: node.op + unsigned_type: left.unaliased + unsigned_expr: node.left + signed_type: right.unaliased + signed_expr: node.right + ) + } else if right.unaliased.idx() in [ast.u32_type_idx, ast.u64_type_idx] + && left.unaliased.is_signed() { + g.gen_safe_integer_infix_expr( + op: node.op + reverse: true + unsigned_type: right.unaliased + unsigned_expr: node.right + signed_type: left.unaliased + signed_expr: node.left + ) + } else { + g.gen_plain_infix_expr(node) + } +} + +// infix_expr_in_op generates code for `in` and `!in` +fn (mut g Gen) infix_expr_in_op(node ast.InfixExpr) { + left := g.unwrap(node.left_type) + right := g.unwrap(node.right_type) + if node.op == .not_in { + g.write('!') + } + if right.unaliased_sym.kind == .array { + if mut node.right is ast.ArrayInit { + if node.right.exprs.len > 0 { + // `a in [1,2,3]` optimization => `a == 1 || a == 2 || a == 3` + // avoids an allocation + g.write('(') + g.infix_expr_in_optimization(node.left, node.right) + g.write(')') + return + } + } + fn_name := g.gen_array_contains_method(node.right_type) + g.write('(${fn_name}(') + if right.typ.is_ptr() { + g.write('*') + } + g.expr(node.right) + g.write(', ') + g.expr(node.left) + g.write('))') + return + } else if right.unaliased_sym.kind == .map { + g.write('_IN_MAP(') + if !left.typ.is_ptr() { + styp := g.typ(node.left_type) + g.write('ADDR($styp, ') + g.expr(node.left) + g.write(')') + } else { + g.expr(node.left) + } + g.write(', ') + if !right.typ.is_ptr() { + g.write('ADDR(map, ') + g.expr(node.right) + g.write(')') + } else { + g.expr(node.right) + } + g.write(')') + } else if right.unaliased_sym.kind == .string { + g.write('string_contains(') + g.expr(node.right) + g.write(', ') + g.expr(node.left) + g.write(')') + } +} + +// infix_expr_in_optimization optimizes ` in ` expressions, +// and transform them in a serie of equality comparison +// i.e. `a in [1,2,3]` => `a == 1 || a == 2 || a == 3` +fn (mut g Gen) infix_expr_in_optimization(left ast.Expr, right ast.ArrayInit) { + is_str := right.elem_type.idx() == ast.string_type_idx + elem_sym := g.table.get_type_symbol(right.elem_type) + is_array := elem_sym.kind == .array + for i, array_expr in right.exprs { + if is_str { + g.write('string__eq(') + } else if is_array { + ptr_typ := g.gen_array_equality_fn(right.elem_type) + g.write('${ptr_typ}_arr_eq(') + } + g.expr(left) + if is_str || is_array { + g.write(', ') + } else { + g.write(' == ') + } + g.expr(array_expr) + if is_str || is_array { + g.write(')') + } + if i < right.exprs.len - 1 { + g.write(' || ') + } + } +} + +// infix_expr_is_op generates code for `is` and `!is` +fn (mut g Gen) infix_expr_is_op(node ast.InfixExpr) { + cmp_op := if node.op == .key_is { '==' } else { '!=' } + g.write('(') + g.expr(node.left) + g.write(')') + if node.left_type.is_ptr() { + g.write('->') + } else { + g.write('.') + } + sym := g.table.get_type_symbol(node.left_type) + if sym.kind == .interface_ { + g.write('_typ $cmp_op ') + // `_Animal_Dog_index` + sub_type := match mut node.right { + ast.TypeNode { node.right.typ } + ast.None { g.table.type_idxs['None__'] } + else { ast.Type(0) } + } + sub_sym := g.table.get_type_symbol(sub_type) + g.write('_${c_name(sym.name)}_${c_name(sub_sym.name)}_index') + return + } else if sym.kind == .sum_type { + g.write('_typ $cmp_op ') + } + g.expr(node.right) +} + +// infix_expr_arithmetic_op generates code for `+`, `-`, `*`, `/`, and `%` +// It handles operator overloading when necessary +fn (mut g Gen) infix_expr_arithmetic_op(node ast.InfixExpr) { + left := g.unwrap(node.left_type) + right := g.unwrap(node.right_type) + method := g.table.type_find_method(left.sym, node.op.str()) or { + g.gen_plain_infix_expr(node) + return + } + left_styp := g.typ(left.typ.set_nr_muls(0)) + g.write(left_styp) + g.write('_') + g.write(util.replace_op(node.op.str())) + g.write('(') + g.op_arg(node.left, method.params[0].typ, left.typ) + g.write(', ') + g.op_arg(node.right, method.params[1].typ, right.typ) + g.write(')') +} + +// infix_expr_left_shift_op generates code for the `<<` operator +// This can either be a value pushed into an array or a bit shift +fn (mut g Gen) infix_expr_left_shift_op(node ast.InfixExpr) { + left := g.unwrap(node.left_type) + right := g.unwrap(node.right_type) + if left.unaliased_sym.kind == .array { + // arr << val + tmp_var := g.new_tmp_var() + array_info := left.unaliased_sym.info as ast.Array + noscan := g.check_noscan(array_info.elem_type) + //&& array_info.elem_type != g.unwrap_generic(node.right_type) + if right.unaliased_sym.kind == .array && array_info.elem_type != right.typ { + // push an array => PUSH_MANY, but not if pushing an array to 2d array (`[][]int << []int`) + g.write('_PUSH_MANY${noscan}(') + mut expected_push_many_atype := left.typ + if !expected_push_many_atype.is_ptr() { + // fn f(mut a []int) { a << [1,2,3] } -> type of `a` is `array_int*` -> no need for & + g.write('&') + } else { + expected_push_many_atype = expected_push_many_atype.deref() + } + g.expr(node.left) + g.write(', (') + g.expr_with_cast(node.right, node.right_type, left.unaliased) + styp := g.typ(expected_push_many_atype) + g.write('), $tmp_var, $styp)') + } else { + // push a single element + elem_type_str := g.typ(array_info.elem_type) + elem_sym := g.table.get_type_symbol(array_info.elem_type) + g.write('array_push${noscan}((array*)') + if !left.typ.is_ptr() { + g.write('&') + } + g.expr(node.left) + if elem_sym.kind == .function { + g.write(', _MOV((voidptr[]){ ') + } else { + g.write(', _MOV(($elem_type_str[]){ ') + } + // if g.autofree + needs_clone := array_info.elem_type.idx() == ast.string_type_idx && !g.is_builtin_mod + if needs_clone { + g.write('string_clone(') + } + if right.unaliased_sym.kind == .interface_ && node.right.is_auto_deref_var() { + g.write('*') + } + g.expr_with_cast(node.right, node.right_type, array_info.elem_type) + if needs_clone { + g.write(')') + } + g.write(' }))') + } + } else { + g.gen_plain_infix_expr(node) + } +} + +// gen_plain_infix_expr generates basic code for infix expressions, +// without any overloading of any kind +// i.e. v`a + 1` => c`a + 1` +// It handles auto dereferencing of variables, as well as automatic casting +// (see Gen.expr_with_cast for more details) +fn (mut g Gen) gen_plain_infix_expr(node ast.InfixExpr) { + if node.left_type.is_ptr() && node.left.is_auto_deref_var() { + g.write('*') + } + g.expr(node.left) + g.write(' $node.op.str() ') + g.expr_with_cast(node.right, node.right_type, node.left_type) +} + +fn (mut g Gen) op_arg(expr ast.Expr, expected ast.Type, got ast.Type) { + mut needs_closing := false + mut nr_muls := got.nr_muls() + if expected.is_ptr() { + if nr_muls > 0 { + nr_muls-- + } else { + if expr.is_lvalue() { + g.write('&') + } else { + styp := g.typ(got.set_nr_muls(0)) + g.write('ADDR($styp, ') + needs_closing = true + } + } + } + g.write('*'.repeat(nr_muls)) + g.expr(expr) + if needs_closing { + g.write(')') + } +} + +struct GenSafeIntegerCfg { + op token.Kind + reverse bool + unsigned_type ast.Type + unsigned_expr ast.Expr + signed_type ast.Type + signed_expr ast.Expr +} + +// gen_safe_integer_infix_expr generates code for comparison of +// unsigned and signed integers +fn (mut g Gen) gen_safe_integer_infix_expr(cfg GenSafeIntegerCfg) { + bitsize := if cfg.unsigned_type.idx() == ast.u32_type_idx + && cfg.signed_type.idx() != ast.i64_type_idx { + 32 + } else { + 64 + } + op_idx := int(cfg.op) - int(token.Kind.eq) + op_str := if cfg.reverse { cmp_rev[op_idx] } else { cmp_str[op_idx] } + g.write('_us${bitsize}_${op_str}(') + g.expr(cfg.unsigned_expr) + g.write(',') + g.expr(cfg.signed_expr) + g.write(')') +} diff --git a/v_windows/v/old/vlib/v/gen/c/json.v b/v_windows/v/old/vlib/v/gen/c/json.v new file mode 100644 index 0000000..cfc480d --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/c/json.v @@ -0,0 +1,339 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module c + +import v.ast +import v.util +import strings + +// TODO replace with comptime code generation. +// TODO remove cJSON dependency. +// OLD: User decode_User(string js) { +// now it's +// User decode_User(cJSON* root) { +// User res; +// res.name = decode_string(js_get(root, "name")); +// res.profile = decode_Profile(js_get(root, "profile")); +// return res; +// } +// Codegen json_decode/encode funcs +fn (mut g Gen) gen_json_for_type(typ ast.Type) { + utyp := g.unwrap_generic(typ) + mut dec := strings.new_builder(100) + mut enc := strings.new_builder(100) + sym := g.table.get_type_symbol(utyp) + styp := g.typ(utyp) + if is_js_prim(sym.name) || sym.kind == .enum_ { + return + } + if sym.kind == .array { + // return + } + if sym.name in g.json_types { + return + } + g.json_types << sym.name + // println('gen_json_for_type($sym.name)') + // decode_TYPE funcs receive an actual cJSON* object to decode + // cJSON_Parse(str) call is added by the compiler + // Code gen decoder + dec_fn_name := js_dec_name(styp) + // Make sure that this optional type actually exists + g.register_optional(utyp) + dec_fn_dec := 'Option_$styp ${dec_fn_name}(cJSON* root)' + dec.writeln(' +$dec_fn_dec { + $styp res; + if (!root) { + const char *error_ptr = cJSON_GetErrorPtr(); + if (error_ptr != NULL) { + // fprintf(stderr, "Error in decode() for $styp error_ptr=: %s\\n", error_ptr); + // printf("\\nbad js=%%s\\n", js.str); + return (Option_$styp){.state = 2,.err = _v_error(tos2((byteptr)error_ptr)),.data = {0}}; + } + } +') + g.json_forward_decls.writeln('$dec_fn_dec;') + // Code gen encoder + // encode_TYPE funcs receive an object to encode + enc_fn_name := js_enc_name(styp) + enc_fn_dec := 'cJSON* ${enc_fn_name}($styp val)' + g.json_forward_decls.writeln('$enc_fn_dec;\n') + enc.writeln(' +$enc_fn_dec { +\tcJSON *o;') + if sym.kind == .array { + // Handle arrays + value_type := g.table.value_type(utyp) + // If we have `[]Profile`, have to register a Profile en(de)coder first + g.gen_json_for_type(value_type) + dec.writeln(g.decode_array(value_type)) + enc.writeln(g.encode_array(value_type)) + // enc += g.encode_array(t) + } else if sym.kind == .map { + // Handle maps + m := sym.info as ast.Map + g.gen_json_for_type(m.key_type) + g.gen_json_for_type(m.value_type) + dec.writeln(g.decode_map(m.key_type, m.value_type)) + enc.writeln(g.encode_map(m.key_type, m.value_type)) + } else if sym.kind == .alias { + a := sym.info as ast.Alias + parent_typ := a.parent_type + psym := g.table.get_type_symbol(parent_typ) + if is_js_prim(g.typ(parent_typ)) { + g.gen_json_for_type(parent_typ) + return + } + enc.writeln('\to = cJSON_CreateObject();') + if psym.info !is ast.Struct { + verror('json: $sym.name is not struct') + } + g.gen_struct_enc_dec(psym.info, styp, mut enc, mut dec) + } else { + enc.writeln('\to = cJSON_CreateObject();') + // Structs. Range through fields + if sym.info !is ast.Struct { + verror('json: $sym.name is not struct') + } + g.gen_struct_enc_dec(sym.info, styp, mut enc, mut dec) + } + // cJSON_delete + // p.cgen.fns << '$dec return opt_ok(res); \n}' + dec.writeln('\tOption_$styp ret;') + dec.writeln('\topt_ok(&res, (Option*)&ret, sizeof(res));') + dec.writeln('\treturn ret;\n}') + enc.writeln('\treturn o;\n}') + g.definitions.writeln(dec.str()) + g.gowrappers.writeln(enc.str()) +} + +[inline] +fn (mut g Gen) gen_struct_enc_dec(type_info ast.TypeInfo, styp string, mut enc strings.Builder, mut dec strings.Builder) { + info := type_info as ast.Struct + for field in info.fields { + mut name := field.name + mut is_raw := false + mut is_skip := false + mut is_required := false + for attr in field.attrs { + match attr.name { + 'json' { + name = attr.arg + } + 'skip' { + is_skip = true + } + 'raw' { + is_raw = true + } + 'required' { + is_required = true + } + else {} + } + } + if is_skip { + continue + } + field_type := g.typ(field.typ) + field_sym := g.table.get_type_symbol(field.typ) + // First generate decoding + if is_raw { + dec.writeln('\tres.${c_name(field.name)} = tos5(cJSON_PrintUnformatted(' + + 'js_get(root, "$name")));') + } else { + // Now generate decoders for all field types in this struct + // need to do it here so that these functions are generated first + g.gen_json_for_type(field.typ) + dec_name := js_dec_name(field_type) + if is_js_prim(field_type) { + tmp := g.new_tmp_var() + gen_js_get(styp, tmp, name, mut dec, is_required) + dec.writeln('\tres.${c_name(field.name)} = $dec_name (jsonroot_$tmp);') + } else if field_sym.kind == .enum_ { + tmp := g.new_tmp_var() + gen_js_get(styp, tmp, name, mut dec, is_required) + dec.writeln('\tres.${c_name(field.name)} = json__decode_u64(jsonroot_$tmp);') + } else if field_sym.name == 'time.Time' { + // time struct requires special treatment + // it has to be decoded from a unix timestamp number + tmp := g.new_tmp_var() + gen_js_get(styp, tmp, name, mut dec, is_required) + dec.writeln('\tres.${c_name(field.name)} = time__unix(json__decode_u64(jsonroot_$tmp));') + } else if field_sym.kind == .alias { + alias := field_sym.info as ast.Alias + parent_type := g.typ(alias.parent_type) + parent_dec_name := js_dec_name(parent_type) + if is_js_prim(parent_type) { + tmp := g.new_tmp_var() + gen_js_get(styp, tmp, name, mut dec, is_required) + dec.writeln('\tres.${c_name(field.name)} = $parent_dec_name (jsonroot_$tmp);') + } else { + g.gen_json_for_type(field.typ) + tmp := g.new_tmp_var() + gen_js_get_opt(dec_name, field_type, styp, tmp, name, mut dec, is_required) + dec.writeln('\tres.${c_name(field.name)} = *($field_type*) ${tmp}.data;') + } + } else { + tmp := g.new_tmp_var() + gen_js_get_opt(dec_name, field_type, styp, tmp, name, mut dec, is_required) + dec.writeln('\tres.${c_name(field.name)} = *($field_type*) ${tmp}.data;') + } + } + // Encoding + mut enc_name := js_enc_name(field_type) + if !is_js_prim(field_type) { + if field_sym.kind == .alias { + ainfo := field_sym.info as ast.Alias + enc_name = js_enc_name(g.typ(ainfo.parent_type)) + } + } + if field_sym.kind == .enum_ { + enc.writeln('\tcJSON_AddItemToObject(o, "$name", json__encode_u64(val.${c_name(field.name)}));\n') + } else { + if field_sym.name == 'time.Time' { + // time struct requires special treatment + // it has to be encoded as a unix timestamp number + enc.writeln('\tcJSON_AddItemToObject(o, "$name", json__encode_u64(val.${c_name(field.name)}._v_unix));') + } else { + enc.writeln('\tcJSON_AddItemToObject(o, "$name", ${enc_name}(val.${c_name(field.name)}));\n') + } + } + } +} + +fn gen_js_get(styp string, tmp string, name string, mut dec strings.Builder, is_required bool) { + dec.writeln('\tcJSON *jsonroot_$tmp = js_get(root,"$name");') + if is_required { + dec.writeln('\tif(jsonroot_$tmp == 0) {') + dec.writeln('\t\treturn (Option_$styp){ .state = 2, .err = _v_error(_SLIT("expected field \'$name\' is missing")), .data = {0} };') + dec.writeln('\t}') + } +} + +fn gen_js_get_opt(dec_name string, field_type string, styp string, tmp string, name string, mut dec strings.Builder, is_required bool) { + gen_js_get(styp, tmp, name, mut dec, is_required) + dec.writeln('\tOption_$field_type $tmp = $dec_name (jsonroot_$tmp);') + dec.writeln('\tif(${tmp}.state != 0) {') + dec.writeln('\t\treturn (Option_$styp){ .state = ${tmp}.state, .err = ${tmp}.err, .data = {0} };') + dec.writeln('\t}') +} + +fn js_enc_name(typ string) string { + suffix := if typ.ends_with('*') { typ.replace('*', '') } else { typ } + name := 'json__encode_$suffix' + return util.no_dots(name) +} + +fn js_dec_name(typ string) string { + name := 'json__decode_$typ' + return util.no_dots(name) +} + +fn is_js_prim(typ string) bool { + return typ in ['int', 'string', 'bool', 'f32', 'f64', 'i8', 'i16', 'i64', 'u16', 'u32', 'u64', + 'byte', + ] +} + +fn (mut g Gen) decode_array(value_type ast.Type) string { + styp := g.typ(value_type) + fn_name := js_dec_name(styp) + mut s := '' + if is_js_prim(styp) { + s = '$styp val = ${fn_name}((cJSON *)jsval); ' + } else { + s = ' + Option_$styp val2 = $fn_name ((cJSON *)jsval); + if(val2.state != 0) { + array_free(&res); + return *(Option_Array_$styp*)&val2; + } + $styp val = *($styp*)val2.data; +' + } + noscan := g.check_noscan(value_type) + return ' + if(root && !cJSON_IsArray(root) && !cJSON_IsNull(root)) { + return (Option_Array_$styp){.state = 2, .err = _v_error(string__plus(_SLIT("Json element is not an array: "), tos2((byteptr)cJSON_PrintUnformatted(root)))), .data = {0}}; + } + res = __new_array${noscan}(0, 0, sizeof($styp)); + const cJSON *jsval = NULL; + cJSON_ArrayForEach(jsval, root) + { + $s + array_push${noscan}((array*)&res, &val); + } +' +} + +fn (mut g Gen) encode_array(value_type ast.Type) string { + styp := g.typ(value_type) + fn_name := js_enc_name(styp) + return ' + o = cJSON_CreateArray(); + for (int i = 0; i < val.len; i++){ + cJSON_AddItemToArray(o, $fn_name ( (($styp*)val.data)[i] )); + } +' +} + +fn (mut g Gen) decode_map(key_type ast.Type, value_type ast.Type) string { + styp := g.typ(key_type) + styp_v := g.typ(value_type) + key_type_symbol := g.table.get_type_symbol(key_type) + hash_fn, key_eq_fn, clone_fn, free_fn := g.map_fn_ptrs(key_type_symbol) + fn_name_v := js_dec_name(styp_v) + mut s := '' + if is_js_prim(styp_v) { + s = '$styp_v val = $fn_name_v (js_get(root, jsval->string));' + } else { + s = ' + Option_$styp_v val2 = $fn_name_v (js_get(root, jsval->string)); + if(val2.state != 0) { + map_free(&res); + return *(Option_Map_${styp}_$styp_v*)&val2; + } + $styp_v val = *($styp_v*)val2.data; +' + } + return ' + if(!cJSON_IsObject(root) && !cJSON_IsNull(root)) { + return (Option_Map_${styp}_$styp_v){ .state = 2, .err = _v_error(string__plus(_SLIT("Json element is not an object: "), tos2((byteptr)cJSON_PrintUnformatted(root)))), .data = {0}}; + } + res = new_map(sizeof($styp), sizeof($styp_v), $hash_fn, $key_eq_fn, $clone_fn, $free_fn); + cJSON *jsval = NULL; + cJSON_ArrayForEach(jsval, root) + { + $s + string key = tos2((byteptr)jsval->string); + map_set(&res, &key, &val); + } +' +} + +fn (mut g Gen) encode_map(key_type ast.Type, value_type ast.Type) string { + styp := g.typ(key_type) + styp_v := g.typ(value_type) + fn_name_v := js_enc_name(styp_v) + zero := g.type_default(value_type) + keys_tmp := g.new_tmp_var() + mut key := 'string key = ' + if key_type.is_string() { + key += '(($styp*)${keys_tmp}.data)[i];' + } else { + // key += '${styp}_str((($styp*)${keys_tmp}.data)[i]);' + verror('json: encode only maps with string keys') + } + return ' + o = cJSON_CreateObject(); + Array_$styp $keys_tmp = map_keys(&val); + for (int i = 0; i < ${keys_tmp}.len; ++i) { + $key + cJSON_AddItemToObject(o, (char*) key.str, $fn_name_v ( *($styp_v*) map_get(&val, &key, &($styp_v[]) { $zero } ) ) ); + } + array_free(&$keys_tmp); +' +} diff --git a/v_windows/v/old/vlib/v/gen/c/live.v b/v_windows/v/old/vlib/v/gen/c/live.v new file mode 100644 index 0000000..69709f9 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/c/live.v @@ -0,0 +1,104 @@ +module c + +import v.pref +import v.util + +fn (mut g Gen) generate_hotcode_reloading_declarations() { + if g.pref.os == .windows { + if g.pref.is_livemain { + g.hotcode_definitions.writeln('HANDLE live_fn_mutex = 0;') + } + if g.pref.is_liveshared { + g.hotcode_definitions.writeln('HANDLE live_fn_mutex;') + } + g.hotcode_definitions.writeln(' +void pthread_mutex_lock(HANDLE *m) { + WaitForSingleObject(*m, INFINITE); +} +void pthread_mutex_unlock(HANDLE *m) { + ReleaseMutex(*m); +} +') + } else { + if g.pref.is_livemain { + g.hotcode_definitions.writeln('pthread_mutex_t live_fn_mutex = PTHREAD_MUTEX_INITIALIZER;') + } + if g.pref.is_liveshared { + g.hotcode_definitions.writeln('pthread_mutex_t live_fn_mutex;') + } + } +} + +fn (mut g Gen) generate_hotcode_reloader_code() { + if g.pref.is_liveshared { + g.hotcode_definitions.writeln('') + return + } + // Hot code reloading + if g.pref.is_livemain { + mut phd := '' + mut load_code := []string{} + if g.pref.os != .windows { + for so_fn in g.hotcode_fn_names { + load_code << 'impl_live_$so_fn = dlsym(live_lib, "impl_live_$so_fn");' + } + phd = c.posix_hotcode_definitions_1 + } else { + for so_fn in g.hotcode_fn_names { + load_code << 'impl_live_$so_fn = (void *)GetProcAddress(live_lib, "impl_live_$so_fn"); ' + } + phd = c.windows_hotcode_definitions_1 + } + g.hotcode_definitions.writeln(phd.replace('@LOAD_FNS@', load_code.join('\n'))) + } +} + +const ( + posix_hotcode_definitions_1 = ' +void v_bind_live_symbols(void* live_lib){ + @LOAD_FNS@ +} +' + windows_hotcode_definitions_1 = ' +void v_bind_live_symbols(void* live_lib){ + @LOAD_FNS@ +} +' +) + +fn (mut g Gen) generate_hotcode_reloading_main_caller() { + if !g.pref.is_livemain { + return + } + g.writeln('') + // We are in live code reload mode, so start the .so loader in the background + g.writeln('\t// live code initialization section:') + g.writeln('\t{') + g.writeln('\t\t// initialization of live function pointers') + for fname in g.hotcode_fn_names { + g.writeln('\t\timpl_live_$fname = 0;') + } + vexe := util.cescaped_path(pref.vexe_path()) + file := util.cescaped_path(g.pref.path) + msvc := if g.pref.ccompiler == 'msvc' { '-cc msvc' } else { '' } + so_debug_flag := if g.pref.is_debug { '-cg' } else { '' } + vopts := '$msvc $so_debug_flag -sharedlive -shared' + // + g.writeln('\t\t// start background reloading thread') + if g.pref.os == .windows { + g.writeln('\t\tlive_fn_mutex = CreateMutexA(0, 0, 0);') + } + g.writeln('\t\tv__live__LiveReloadInfo* live_info = v__live__executable__new_live_reload_info(') + g.writeln('\t\t\t\t\t tos2("$file"),') + g.writeln('\t\t\t\t\t tos2("$vexe"),') + g.writeln('\t\t\t\t\t tos2("$vopts"),') + g.writeln('\t\t\t\t\t &live_fn_mutex,') + g.writeln('\t\t\t\t\t v_bind_live_symbols') + g.writeln('\t\t);') + // g_live_info gives access to the LiveReloadInfo methods, + // to the custom user code, through calling v_live_info() + g.writeln('\t\t g_live_info = (void*)live_info;') + g.writeln('\t\tv__live__executable__start_reloader(live_info);') + g.writeln('\t}\t// end of live code initialization section') + g.writeln('') +} diff --git a/v_windows/v/old/vlib/v/gen/c/profile.v b/v_windows/v/old/vlib/v/gen/c/profile.v new file mode 100644 index 0000000..602c7b0 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/c/profile.v @@ -0,0 +1,52 @@ +module c + +import v.ast + +pub struct ProfileCounterMeta { + fn_name string + vpc_name string + vpc_calls string +} + +fn (mut g Gen) profile_fn(fn_decl ast.FnDecl) { + if g.pref.profile_no_inline && fn_decl.attrs.contains('inline') { + g.defer_profile_code = '' + return + } + fn_name := fn_decl.name + if fn_name.starts_with('time.vpc_now') { + g.defer_profile_code = '' + } else { + measure_fn_name := if g.pref.os == .macos { 'time__vpc_now_darwin' } else { 'time__vpc_now' } + fn_profile_counter_name := 'vpc_$g.last_fn_c_name' + fn_profile_counter_name_calls := '${fn_profile_counter_name}_calls' + g.writeln('') + g.writeln('\tdouble _PROF_FN_START = ${measure_fn_name}(); $fn_profile_counter_name_calls++; // $fn_name') + g.writeln('') + g.defer_profile_code = '\t$fn_profile_counter_name += ${measure_fn_name}() - _PROF_FN_START;' + g.pcs_declarations.writeln('double $fn_profile_counter_name = 0.0; u64 $fn_profile_counter_name_calls = 0;') + g.pcs << ProfileCounterMeta{ + fn_name: g.last_fn_c_name + vpc_name: fn_profile_counter_name + vpc_calls: fn_profile_counter_name_calls + } + } +} + +pub fn (mut g Gen) gen_vprint_profile_stats() { + g.pcs_declarations.writeln('void vprint_profile_stats(){') + fstring := '"%14llu %14.3fms %14.0fns %s \\n"' + if g.pref.profile_file == '-' { + for pc_meta in g.pcs { + g.pcs_declarations.writeln('\tif ($pc_meta.vpc_calls) printf($fstring, $pc_meta.vpc_calls, $pc_meta.vpc_name/1000000.0, $pc_meta.vpc_name/$pc_meta.vpc_calls, "$pc_meta.fn_name" );') + } + } else { + g.pcs_declarations.writeln('\tFILE * fp;') + g.pcs_declarations.writeln('\tfp = fopen ("$g.pref.profile_file", "w+");') + for pc_meta in g.pcs { + g.pcs_declarations.writeln('\tif ($pc_meta.vpc_calls) fprintf(fp, $fstring, $pc_meta.vpc_calls, $pc_meta.vpc_name/1000000.0, $pc_meta.vpc_name/$pc_meta.vpc_calls, "$pc_meta.fn_name" );') + } + g.pcs_declarations.writeln('\tfclose(fp);') + } + g.pcs_declarations.writeln('}') +} diff --git a/v_windows/v/old/vlib/v/gen/c/sql.v b/v_windows/v/old/vlib/v/gen/c/sql.v new file mode 100644 index 0000000..68d053c --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/c/sql.v @@ -0,0 +1,826 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license that can be found in the LICENSE file. +module c + +import v.ast +import v.util + +enum SqlExprSide { + left + right +} + +enum SqlType { + sqlite3 + mysql + psql + mssql + unknown +} + +fn (mut g Gen) sql_stmt(node ast.SqlStmt) { + conn := g.new_tmp_var() + g.writeln('') + g.writeln('// orm') + g.write('orm__Connection $conn = (orm__Connection){._') + mut fn_prefix := '' + typ := g.parse_db_type(node.db_expr) + match typ { + .sqlite3 { + fn_prefix = 'sqlite__DB' + } + .mysql { + fn_prefix = 'mysql__Connection' + } + .psql { + fn_prefix = 'pg__DB' + } + else { + verror('This database type `$typ` is not implemented yet in orm') // TODO add better error + } + } + g.write('$fn_prefix = &') + g.expr(node.db_expr) + g.writeln(', ._typ = _orm__Connection_${fn_prefix}_index};') + for line in node.lines { + g.sql_stmt_line(line, conn) + } +} + +fn (mut g Gen) sql_stmt_line(nd ast.SqlStmtLine, expr string) { + mut node := nd + table_name := g.get_table_name(node.table_expr) + g.sql_table_name = g.table.get_type_symbol(node.table_expr.typ).name + res := g.new_tmp_var() + mut subs := false + mut dcheck := false + + if node.kind != .create { + mut fields := []ast.StructField{} + for f in node.fields { + mut skip := false + mut primary := false + for attr in f.attrs { + if attr.name == 'primary' { + primary = true + } + if attr.name == 'skip' { + skip = true + } + } + if !skip && !primary { + fields << f + } + } + node.fields = fields.clone() + unsafe { fields.free() } + } + + if node.kind == .create { + g.write('Option_void $res = orm__Connection_name_table[${expr}._typ]._method_') + g.sql_create_table(node, expr, table_name) + subs = true + } else if node.kind == .drop { + g.write('Option_void $res = orm__Connection_name_table[${expr}._typ]._method_') + g.writeln('drop(${expr}._object, _SLIT("$table_name"));') + subs = true + } else if node.kind == .insert { + arr := g.new_tmp_var() + g.writeln('Array_orm__Primitive $arr = new_array_from_c_array(0, 0, sizeof(orm__Primitive), NULL);') + g.sql_insert(node, expr, table_name, arr, res, '', false, '') + dcheck = true + } else if node.kind == .update { + g.write('Option_void $res = orm__Connection_name_table[${expr}._typ]._method_') + g.sql_update(node, expr, table_name) + } else if node.kind == .delete { + g.write('Option_void $res = orm__Connection_name_table[${expr}._typ]._method_') + g.sql_delete(node, expr, table_name) + } + if !dcheck { + g.writeln('if (${res}.state != 0 && ${res}.err._typ != _IError_None___index) { _v_panic(IError_str(${res}.err)); }') + } + if subs { + for _, sub in node.sub_structs { + g.sql_stmt_line(sub, expr) + } + } +} + +fn (mut g Gen) sql_create_table(node ast.SqlStmtLine, expr string, table_name string) { + g.write('create(${expr}._object, _SLIT("$table_name"), new_array_from_c_array($node.fields.len, $node.fields.len, sizeof(orm__TableField),') + if node.fields.len > 0 { + g.write(' _MOV((orm__TableField[$node.fields.len]){') + for field in node.fields { + sym := g.table.get_type_symbol(field.typ) + g.write('(orm__TableField){') + g.write('.name = _SLIT("$field.name"),') + g.write('.typ = ${int(field.typ)},') + g.write('.is_arr = ${sym.kind == .array}, ') + g.write('.is_time = ${int(g.table.get_type_name(field.typ) == 'time__Time')},') + g.write('.default_val = (string){.str = (byteptr) "$field.default_val", .is_lit = 1},') + g.write('.attrs = new_array_from_c_array($field.attrs.len, $field.attrs.len, sizeof(StructAttribute),') + if field.attrs.len > 0 { + g.write(' _MOV((StructAttribute[$field.attrs.len]){') + for attr in field.attrs { + g.write('(StructAttribute){') + g.write('.name = _SLIT("$attr.name"),') + g.write('.has_arg = ${int(attr.has_arg)},') + g.write('.arg = _SLIT("$attr.arg"),') + g.write('.kind = ${int(attr.kind)},') + g.write('},') + } + g.write('})') + } else { + g.write('NULL') + } + g.write(')') + g.write('},') + } + g.write('})') + } else { + g.write('NULL') + } + g.writeln('));') +} + +fn (mut g Gen) sql_insert(node ast.SqlStmtLine, expr string, table_name string, last_ids_arr string, res string, pid string, is_array bool, fkey string) { + mut subs := []ast.SqlStmtLine{} + mut arrs := []ast.SqlStmtLine{} + mut fkeys := []string{} + mut field_names := []string{} + + for f in node.fields { + sym := g.table.get_type_symbol(f.typ) + if sym.kind == .struct_ { + subs << node.sub_structs[int(f.typ)] + } else if sym.kind == .array { + mut f_key := '' + for attr in f.attrs { + if attr.name == 'fkey' && attr.has_arg && attr.kind == .string { + f_key = attr.arg + } + } + if f_key == '' { + verror('An field which holds an array, needs a fkey defined') + } + fkeys << f_key + info := sym.array_info() + if info.nr_dims == 1 { + arrs << node.sub_structs[int(info.elem_type)] + field_names << f.name + } else { + verror('V ORM only supports 1 dimensional arrays') + } + } + } + + fields := node.fields.filter(g.table.get_type_symbol(it.typ).kind != .array) + + for sub in subs { + g.sql_stmt_line(sub, expr) + g.writeln('array_push(&$last_ids_arr, _MOV((orm__Primitive[]){orm__Connection_name_table[${expr}._typ]._method_last_id(${expr}._object)}));') + } + + g.write('Option_void $res = orm__Connection_name_table[${expr}._typ]._method_') + g.write('insert(${expr}._object, _SLIT("$table_name"), (orm__QueryData){') + + g.write('.fields = new_array_from_c_array($fields.len, $fields.len, sizeof(string),') + if fields.len > 0 { + g.write('_MOV((string[$fields.len]){') + for f in fields { + g.write('_SLIT("${g.get_field_name(f)}"),') + } + g.write('})') + } else { + g.write('NULL') + } + g.write('),') + + g.write('.data = new_array_from_c_array($fields.len, $fields.len, sizeof(orm__Primitive),') + if fields.len > 0 { + g.write(' _MOV((orm__Primitive[$fields.len]){') + mut structs := 0 + for f in fields { + if f.name == fkey { + g.write('$pid, ') + continue + } + mut sym := g.table.get_type_symbol(f.typ) + if sym.kind == .struct_ { + g.write('(*(orm__Primitive*) array_get($last_ids_arr, $structs)),') + structs++ + continue + } + mut typ := sym.cname + if typ == 'time__Time' { + typ = 'time' + } + g.write('orm__${typ}_to_primitive(${node.object_var_name}.$f.name),') + } + g.write('})') + } + g.write('),') + g.write('.types = new_array_from_c_array(0, 0, sizeof(int), NULL),') + g.write('.kinds = new_array_from_c_array(0, 0, sizeof(orm__OperationKind), NULL),') + g.write('.is_and = new_array_from_c_array(0, 0, sizeof(bool), NULL),') + g.writeln('});') + + g.writeln('if (${res}.state != 0 && ${res}.err._typ != _IError_None___index) { _v_panic(IError_str(${res}.err)); }') + if arrs.len > 0 { + mut id_name := g.new_tmp_var() + g.writeln('orm__Primitive $id_name = orm__Connection_name_table[${expr}._typ]._method_last_id(${expr}._object);') + for i, mut arr in arrs { + idx := g.new_tmp_var() + g.writeln('for (int $idx = 0; $idx < ${arr.object_var_name}.${field_names[i]}.len; $idx++) {') + last_ids := g.new_tmp_var() + res_ := g.new_tmp_var() + tmp_var := g.new_tmp_var() + ctyp := g.typ(arr.table_expr.typ) + g.writeln('$ctyp $tmp_var = (*($ctyp*)array_get(${arr.object_var_name}.${field_names[i]}, $idx));') + arr.object_var_name = tmp_var + mut fff := []ast.StructField{} + for f in arr.fields { + mut skip := false + mut primary := false + for attr in f.attrs { + if attr.name == 'primary' { + primary = true + } + if attr.name == 'skip' { + skip = true + } + } + if !skip && !primary { + fff << f + } + } + arr.fields = fff.clone() + unsafe { fff.free() } + g.sql_insert(arr, expr, g.get_table_name(arr.table_expr), last_ids, res_, + id_name, true, fkeys[i]) + g.writeln('}') + } + } +} + +fn (mut g Gen) sql_update(node ast.SqlStmtLine, expr string, table_name string) { + g.write('update(${expr}._object, _SLIT("$table_name"), (orm__QueryData){') + g.write('.kinds = new_array_from_c_array(0, 0, sizeof(orm__OperationKind), NULL),') + g.write('.is_and = new_array_from_c_array(0, 0, sizeof(bool), NULL),') + g.write('.types = new_array_from_c_array(0, 0, sizeof(int), NULL),') + g.write('.fields = new_array_from_c_array($node.updated_columns.len, $node.updated_columns.len, sizeof(string),') + if node.updated_columns.len > 0 { + g.write(' _MOV((string[$node.updated_columns.len]){') + for field in node.updated_columns { + g.write('_SLIT("$field"),') + } + g.write('})') + } else { + g.write('NULL') + } + g.write('),') + g.write('.data = new_array_from_c_array($node.update_exprs.len, $node.update_exprs.len, sizeof(orm__Primitive),') + if node.update_exprs.len > 0 { + g.write(' _MOV((orm__Primitive[$node.update_exprs.len]){') + for e in node.update_exprs { + g.sql_expr_to_orm_primitive(e) + } + g.write('})') + } + g.write('),},') + g.sql_gen_where_data(node.where_expr) + g.writeln(');') +} + +fn (mut g Gen) sql_delete(node ast.SqlStmtLine, expr string, table_name string) { + g.write('_v_delete(${expr}._object, _SLIT("$table_name"),') + g.sql_gen_where_data(node.where_expr) + g.writeln(');') +} + +fn (mut g Gen) sql_expr_to_orm_primitive(expr ast.Expr) { + match expr { + ast.InfixExpr { + g.sql_write_orm_primitive(g.table.find_type_idx('orm.InfixType'), expr) + } + ast.StringLiteral { + g.sql_write_orm_primitive(ast.string_type, expr) + } + ast.IntegerLiteral { + g.sql_write_orm_primitive(ast.int_type, expr) + } + ast.BoolLiteral { + g.sql_write_orm_primitive(ast.bool_type, expr) + } + ast.Ident { + info := expr.info as ast.IdentVar + g.sql_write_orm_primitive(info.typ, expr) + } + ast.SelectorExpr { + g.sql_write_orm_primitive(expr.typ, expr) + } + else { + eprintln(expr) + verror('Unknown expr') + } + } +} + +fn (mut g Gen) sql_write_orm_primitive(t ast.Type, expr ast.Expr) { + mut sym := g.table.get_type_symbol(t) + mut typ := sym.cname + if typ == 'orm__Primitive' { + g.expr(expr) + g.write(',') + return + } + if typ == 'time__Time' { + typ = 'time' + } + if typ == 'orm__InfixType' { + typ = 'infix' + } + g.write('orm__${typ}_to_primitive(') + if expr is ast.InfixExpr { + g.write('(orm__InfixType){') + g.write('.name = _SLIT("$expr.left"),') + mut kind := match expr.op { + .plus { + 'orm__MathOperationKind__add' + } + .minus { + 'orm__MathOperationKind__sub' + } + .div { + 'orm__MathOperationKind__div' + } + .mul { + 'orm__MathOperationKind__mul' + } + else { + '' + } + } + g.write('.operator = $kind,') + g.write('.right = ') + g.sql_expr_to_orm_primitive(expr.right) + g.write('}') + } else { + g.expr(expr) + } + g.write('),') +} + +fn (mut g Gen) sql_where_data(expr ast.Expr, mut fields []string, mut kinds []string, mut data []ast.Expr, mut is_and []bool) { + match expr { + ast.InfixExpr { + g.sql_side = .left + g.sql_where_data(expr.left, mut fields, mut kinds, mut data, mut is_and) + mut kind := match expr.op { + .ne { + 'orm__OperationKind__neq' + } + .eq { + 'orm__OperationKind__eq' + } + .lt { + 'orm__OperationKind__lt' + } + .gt { + 'orm__OperationKind__gt' + } + .ge { + 'orm__OperationKind__ge' + } + .le { + 'orm__OperationKind__le' + } + else { + '' + } + } + if kind == '' { + if expr.op == .logical_or { + is_and << false + } else if expr.op == .and { + is_and << true + } else { + kind = 'orm__OperationKind__eq' + } + } + if expr.left !is ast.InfixExpr && expr.right !is ast.InfixExpr { + kinds << kind + } + g.sql_side = .right + g.sql_where_data(expr.right, mut fields, mut kinds, mut data, mut is_and) + } + ast.Ident { + if g.sql_side == .left { + fields << g.get_field_name(g.get_struct_field(expr.name)) + } else { + data << expr + } + } + ast.StringLiteral { + data << expr + } + ast.IntegerLiteral { + data << expr + } + ast.SelectorExpr { + data << expr + } + ast.BoolLiteral { + data << expr + } + else {} + } +} + +fn (mut g Gen) sql_gen_where_data(where_expr ast.Expr) { + g.write('(orm__QueryData){') + mut fields := []string{} + mut kinds := []string{} + mut data := []ast.Expr{} + mut is_and := []bool{} + g.sql_where_data(where_expr, mut fields, mut kinds, mut data, mut is_and) + g.write('.types = new_array_from_c_array(0, 0, sizeof(int), NULL),') + g.write('.fields = new_array_from_c_array($fields.len, $fields.len, sizeof(string),') + if fields.len > 0 { + g.write(' _MOV((string[$fields.len]){') + for field in fields { + g.write('_SLIT("$field"),') + } + g.write('})') + } else { + g.write('NULL') + } + g.write('),') + + g.write('.data = new_array_from_c_array($data.len, $data.len, sizeof(orm__Primitive),') + if data.len > 0 { + g.write(' _MOV((orm__Primitive[$data.len]){') + for e in data { + g.sql_expr_to_orm_primitive(e) + } + g.write('})') + } + g.write('),') + + g.write('.kinds = new_array_from_c_array($kinds.len, $kinds.len, sizeof(orm__OperationKind),') + if kinds.len > 0 { + g.write(' _MOV((orm__OperationKind[$kinds.len]){') + for k in kinds { + g.write('$k,') + } + g.write('})') + } else { + g.write('NULL') + } + g.write('),') + + g.write('.is_and = new_array_from_c_array($is_and.len, $is_and.len, sizeof(bool),') + if is_and.len > 0 { + g.write(' _MOV((bool[$is_and.len]){') + for b in is_and { + g.write('$b, ') + } + g.write('})') + } else { + g.write('NULL') + } + g.write('),}') +} + +fn (mut g Gen) sql_select_expr(node ast.SqlExpr) { + left := g.go_before_stmt(0) + conn := g.new_tmp_var() + g.writeln('') + g.writeln('// orm') + g.write('orm__Connection $conn = (orm__Connection){._') + mut fn_prefix := '' + typ := g.parse_db_type(node.db_expr) + match typ { + .sqlite3 { + fn_prefix = 'sqlite__DB' + } + .mysql { + fn_prefix = 'mysql__Connection' + } + .psql { + fn_prefix = 'pg__DB' + } + else { + verror('This database type `$typ` is not implemented yet in orm') // TODO add better error + } + } + + g.write('$fn_prefix = &') + g.expr(node.db_expr) + g.writeln(', ._typ = _orm__Connection_${fn_prefix}_index};') + g.sql_select(node, conn, left) +} + +fn (mut g Gen) sql_select(node ast.SqlExpr, expr string, left string) { + mut fields := []ast.StructField{} + mut prim := '' + for f in node.fields { + mut skip := false + for attr in f.attrs { + if attr.name == 'primary' { + prim = f.name + } + if attr.name == 'skip' { + skip = true + } + } + if !skip { + fields << f + } + } + + res := g.new_tmp_var() + table_name := g.get_table_name(node.table_expr) + g.sql_table_name = g.table.get_type_symbol(node.table_expr.typ).name + g.write('Option_Array_Array_orm__Primitive _o$res = orm__Connection_name_table[${expr}._typ]._method_select(${expr}._object, ') + g.write('(orm__SelectConfig){') + g.write('.table = _SLIT("$table_name"),') + g.write('.is_count = $node.is_count,') + g.write('.has_where = $node.has_where,') + g.write('.has_order = $node.has_order,') + if node.has_order { + g.write('.order = _SLIT("') + g.expr(node.order_expr) + g.write('"),') + if node.has_desc { + g.write('.order_type = orm__OrderType__desc,') + } else { + g.write('.order_type = orm__OrderType__asc,') + } + } + g.write('.has_limit = $node.has_limit,') + g.write('.has_offset = $node.has_offset,') + if prim != '' { + g.write('.primary = _SLIT("$prim"),') + } + select_fields := fields.filter(g.table.get_type_symbol(it.typ).kind != .array) + g.write('.fields = new_array_from_c_array($select_fields.len, $select_fields.len, sizeof(string),') + mut types := []int{} + if select_fields.len > 0 { + g.write(' _MOV((string[$select_fields.len]){') + for field in select_fields { + g.write('_SLIT("${g.get_field_name(field)}"),') + sym := g.table.get_type_symbol(field.typ) + if sym.kind == .struct_ { + types << int(ast.int_type) + continue + } + types << int(field.typ) + } + g.write('})') + } else { + g.write('NULL') + } + g.write('),') + g.write('.types = new_array_from_c_array($types.len, $types.len, sizeof(int),') + if types.len > 0 { + g.write(' _MOV((int[$types.len]){') + for typ in types { + g.write('$typ,') + } + g.write('})') + } else { + g.write('NULL') + } + g.write('),},') + + mut exprs := []ast.Expr{} + if node.has_limit { + exprs << node.limit_expr + } + if node.has_offset { + exprs << node.offset_expr + } + g.write('(orm__QueryData) {') + g.write('.types = new_array_from_c_array(0, 0, sizeof(int), NULL),') + g.write('.kinds = new_array_from_c_array(0, 0, sizeof(orm__OperationKind), NULL),') + g.write('.is_and = new_array_from_c_array(0, 0, sizeof(bool), NULL),') + g.write('.data = new_array_from_c_array($exprs.len, $exprs.len, sizeof(orm__Primitive),') + if exprs.len > 0 { + g.write(' _MOV((orm__Primitive[$exprs.len]){') + for e in exprs { + g.sql_expr_to_orm_primitive(e) + } + g.write('})') + } else { + g.write('NULL') + } + g.write(')},') + + if node.has_where { + g.sql_gen_where_data(node.where_expr) + } else { + g.write('(orm__QueryData) {}') + } + g.writeln(');') + g.writeln('if (_o${res}.state != 0 && _o${res}.err._typ != _IError_None___index) { _v_panic(IError_str(_o${res}.err)); }') + g.writeln('Array_Array_orm__Primitive $res = (*(Array_Array_orm__Primitive*)_o${res}.data);') + + if node.is_count { + g.writeln('$left *((*(orm__Primitive*) array_get((*(Array_orm__Primitive*)array_get($res, 0)), 0))._int);') + } else { + tmp := g.new_tmp_var() + styp := g.typ(node.typ) + idx := g.new_tmp_var() + g.writeln('int $idx = 0;') + mut typ_str := '' + if node.is_array { + info := g.table.get_type_symbol(node.typ).array_info() + typ_str = g.typ(info.elem_type) + g.writeln('$styp ${tmp}_array = __new_array(0, ${res}.len, sizeof($typ_str));') + g.writeln('for (; $idx < ${res}.len; $idx++) {') + g.write('\t$typ_str $tmp = ($typ_str) {') + inf := g.table.get_type_symbol(info.elem_type).struct_info() + for i, field in inf.fields { + g.zero_struct_field(field) + if i != inf.fields.len - 1 { + g.write(', ') + } + } + g.writeln('};') + } else { + g.write('$styp $tmp = ($styp){') + info := g.table.get_type_symbol(node.typ).struct_info() + for i, field in info.fields { + g.zero_struct_field(field) + if i != info.fields.len - 1 { + g.write(', ') + } + } + g.writeln('};') + } + + g.writeln('if (${res}.len > 0) {') + for i, field in fields { + sel := '(*(orm__Primitive*) array_get((*(Array_orm__Primitive*) array_get($res, $idx)), $i))' + sym := g.table.get_type_symbol(field.typ) + if sym.kind == .struct_ { + mut sub := node.sub_structs[int(field.typ)] + mut where_expr := sub.where_expr as ast.InfixExpr + mut ident := where_expr.right as ast.Ident + name := sel + s := g.table.find_type_idx('orm.Primitive') + if s != 0 { + if ident.info is ast.IdentVar { + mut info := ident.info as ast.IdentVar + info.typ = s + ident.info = info + } + } + ident.name = name + where_expr.right = ident + sub.where_expr = where_expr + + g.sql_select(sub, expr, '${tmp}.$field.name = ') + } else if sym.kind == .array { + mut fkey := '' + for attr in field.attrs { + if attr.name == 'fkey' && attr.has_arg && attr.kind == .string { + fkey = attr.arg + } + } + if fkey == '' { + verror('An field which holds an array, needs a fkey defined') + } + info := sym.array_info() + arr_typ := info.elem_type + sub := node.sub_structs[int(arr_typ)] + mut where_expr := sub.where_expr as ast.InfixExpr + mut l := where_expr.left as ast.Ident + mut r := where_expr.right as ast.Ident + l.name = fkey + r.name = tmp + where_expr.left = l + where_expr.right = ast.SelectorExpr{ + pos: r.pos + field_name: prim + is_mut: false + expr: r + expr_type: (r.info as ast.IdentVar).typ + typ: ast.int_type + scope: 0 + } + mut arr := ast.SqlExpr{ + typ: field.typ + is_count: sub.is_count + db_expr: sub.db_expr + has_where: sub.has_where + has_offset: sub.has_offset + offset_expr: sub.offset_expr + has_order: sub.has_order + order_expr: sub.order_expr + has_desc: sub.has_desc + is_array: true + pos: sub.pos + has_limit: sub.has_limit + limit_expr: sub.limit_expr + table_expr: sub.table_expr + fields: sub.fields + where_expr: where_expr + } + + g.sql_select(arr, expr, '${tmp}.$field.name = ') + } else { + mut typ := sym.cname + g.writeln('${tmp}.$field.name = *(${sel}._$typ);') + } + } + g.writeln('}') + + if node.is_array { + g.writeln('array_push(&${tmp}_array, _MOV(($typ_str[]){ $tmp }));') + g.writeln('}') + } + + g.write('$left $tmp') + if node.is_array { + g.write('_array') + } + g.writeln(';') + } +} + +fn (mut g Gen) parse_db_type(expr ast.Expr) SqlType { + match expr { + ast.Ident { + if expr.info is ast.IdentVar { + return g.parse_db_from_type_string(g.table.get_type_name(expr.info.typ)) + } + } + ast.SelectorExpr { + return g.parse_db_from_type_string(g.table.get_type_name(expr.typ)) + } + else { + return .unknown + } + } + return .unknown +} + +fn (mut g Gen) parse_db_from_type_string(name string) SqlType { + match name { + 'sqlite.DB' { + return .sqlite3 + } + 'mysql.Connection' { + return .mysql + } + 'pg.DB' { + return .psql + } + 'mssql.Connection' { + return .mssql + } + else { + return .unknown + } + } +} + +fn (mut g Gen) get_table_name(table_expr ast.TypeNode) string { + info := g.table.get_type_symbol(table_expr.typ).struct_info() + mut tablename := util.strip_mod_name(g.table.get_type_symbol(table_expr.typ).name) + for attr in info.attrs { + if attr.kind == .string && attr.name == 'table' && attr.arg != '' { + tablename = attr.arg + break + } + } + return tablename +} + +fn (mut g Gen) get_struct_field(name string) ast.StructField { + info := g.table.get_type_symbol(g.table.type_idxs[g.sql_table_name]).struct_info() + mut f := ast.StructField{} + for field in info.fields { + if field.name == name { + f = field + } + } + return f +} + +fn (mut g Gen) get_field_name(field ast.StructField) string { + mut name := field.name + for attr in field.attrs { + if attr.kind == .string && attr.name == 'sql' && attr.arg != '' { + name = attr.arg + break + } + } + sym := g.table.get_type_symbol(field.typ) + if sym.kind == .struct_ { + name = '${name}_id' + } + return name +} diff --git a/v_windows/v/old/vlib/v/gen/c/str.v b/v_windows/v/old/vlib/v/gen/c/str.v new file mode 100644 index 0000000..a4b9ae4 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/c/str.v @@ -0,0 +1,149 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license that can be found in the LICENSE file. +module c + +import v.ast +import v.util + +fn (mut g Gen) string_literal(node ast.StringLiteral) { + escaped_val := util.smart_quote(node.val, node.is_raw) + if node.language == .c { + g.write('"$escaped_val"') + } else { + g.write('_SLIT("$escaped_val")') + } +} + +// optimize string interpolation in string builders: +// `sb.writeln('a=$a')` => +// `sb.writeln('a='); sb.writeln(a.str())` +fn (mut g Gen) string_inter_literal_sb_optimized(call_expr ast.CallExpr) { + node := call_expr.args[0].expr as ast.StringInterLiteral + // sb_name := g.cur_call_expr.left + // g.go_before_stmt(0) + g.writeln('// sb inter opt') + is_nl := call_expr.name == 'writeln' + // println('optimize sb $call_expr.name') + for i, val in node.vals { + escaped_val := util.smart_quote(val, false) + // if val == '' { + // break + // continue + // } + g.write('strings__Builder_write_string(&') + g.expr(call_expr.left) + g.write(', _SLIT("') + g.write(escaped_val) + g.writeln('"));') + // + if i >= node.exprs.len { + break + } + // if node.expr_types.len <= i || node.exprs.len <= i { + // continue + // } + if is_nl && i == node.exprs.len - 1 { + g.write('strings__Builder_writeln(&') + } else { + g.write('strings__Builder_write_string(&') + } + g.expr(call_expr.left) + g.write(', ') + typ := node.expr_types[i] + g.write(g.typ(typ)) + g.write('_str(') + sym := g.table.get_type_symbol(typ) + if sym.kind != .function { + g.expr(node.exprs[i]) + } + g.writeln('));') + } + g.writeln('') + // println(node.vals) + return +} + +fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype ast.Type) { + is_shared := etype.has_flag(.shared_f) + mut typ := etype + if is_shared { + typ = typ.clear_flag(.shared_f).set_nr_muls(0) + } + mut sym := g.table.get_type_symbol(typ) + // when type is alias, print the aliased value + if mut sym.info is ast.Alias { + parent_sym := g.table.get_type_symbol(sym.info.parent_type) + if parent_sym.has_method('str') { + typ = sym.info.parent_type + sym = parent_sym + } + } + sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info() + if typ.has_flag(.variadic) { + str_fn_name := g.gen_str_for_type(typ) + g.write('${str_fn_name}(') + g.expr(expr) + g.write(')') + } else if typ == ast.string_type { + g.expr(expr) + } else if typ == ast.bool_type { + g.expr(expr) + g.write(' ? _SLIT("true") : _SLIT("false")') + } else if sym.kind == .none_ { + g.write('_SLIT("")') + } else if sym.kind == .enum_ { + is_var := match expr { + ast.SelectorExpr, ast.Ident, ast.CTempVar { true } + else { false } + } + if is_var { + str_fn_name := g.gen_str_for_type(typ) + g.write('${str_fn_name}(') + g.enum_expr(expr) + g.write(')') + } else { + g.write('_SLIT("') + g.enum_expr(expr) + g.write('")') + } + } else if sym_has_str_method + || sym.kind in [.array, .array_fixed, .map, .struct_, .multi_return, .sum_type, .interface_] { + is_ptr := typ.is_ptr() + is_var_mut := expr.is_auto_deref_var() + str_fn_name := g.gen_str_for_type(typ) + if is_ptr && !is_var_mut { + g.write('str_intp(1, _MOV((StrIntpData[]){{_SLIT("&"), $si_s_code ,{.d_s=') + } + g.write('${str_fn_name}(') + if str_method_expects_ptr && !is_ptr { + g.write('&') + } else if (!str_method_expects_ptr && is_ptr && !is_shared) || is_var_mut { + g.write('*') + } + if expr is ast.ArrayInit { + if expr.is_fixed { + s := g.typ(expr.typ) + g.write('($s)') + } + } + g.expr_with_cast(expr, typ, typ) + if is_shared { + g.write('->val') + } + g.write(')') + if is_ptr && !is_var_mut { + g.write('}}}))') + // g.write(')') + } + } else { + str_fn_name := g.gen_str_for_type(typ) + g.write('${str_fn_name}(') + if expr.is_auto_deref_var() { + g.write('*') + } + if sym.kind != .function { + g.expr_with_cast(expr, typ, typ) + } + g.write(')') + } +} diff --git a/v_windows/v/old/vlib/v/gen/c/str_intp.v b/v_windows/v/old/vlib/v/gen/c/str_intp.v new file mode 100644 index 0000000..7ace443 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/c/str_intp.v @@ -0,0 +1,206 @@ +/* +str_intp.v + +Copyright (c) 2019-2021 Dario Deledda. All rights reserved. +Use of this source code is governed by an MIT license +that can be found in the LICENSE file. + +This file contains string interpolation V functions +*/ +module c + +import v.ast +import v.util + +fn (mut g Gen) str_format(node ast.StringInterLiteral, i int) (u64, string) { + mut base := 0 // numeric base + mut upper_case := false // set upercase for the result string + mut typ := g.unwrap_generic(node.expr_types[i]) + sym := g.table.get_type_symbol(typ) + if sym.kind == .alias { + typ = (sym.info as ast.Alias).parent_type + } + mut remove_tail_zeros := false + fspec := node.fmts[i] + mut fmt_type := StrIntpType{} + + // upper cases + if (fspec - `A`) <= (`Z` - `A`) { + upper_case = true + } + + if fspec in [`s`, `S`] { + /* + if node.fwidths[i] == 0 { + fmt_type = .si_s + } else { + fmt_type = .si_s + } + */ + fmt_type = .si_s + } else if typ.is_float() { + if fspec in [`g`, `G`] { + match typ { + ast.f32_type { fmt_type = .si_g32 } + // ast.f64_type { fmt_type = .si_g64 } + else { fmt_type = .si_g64 } + } + remove_tail_zeros = true + } else if fspec in [`e`, `E`] { + match typ { + ast.f32_type { fmt_type = .si_e32 } + // ast.f64_type { fmt_type = .si_e64 } + else { fmt_type = .si_e64 } + } + } else if fspec in [`f`, `F`] { + match typ { + ast.f32_type { fmt_type = .si_f32 } + // ast.f64_type { fmt_type = .si_f64 } + else { fmt_type = .si_f64 } + } + } + } else if typ.is_pointer() { + if fspec in [`x`, `X`] { + base = 16 - 2 // our base start from 2 + } + if fspec in [`p`, `x`, `X`] { + fmt_type = .si_p + } else { + fmt_type = .si_vp + } + } else if typ.is_int() { + if fspec in [`x`, `X`] { + base = 16 - 2 // our base start from 2 + } + // if fspec in [`o`] { + if fspec == `o` { + base = 8 - 2 // our base start from 2 + } + if fspec == `c` { + fmt_type = .si_c + } else { + match typ { + ast.i8_type { fmt_type = .si_i8 } + ast.byte_type { fmt_type = .si_u8 } + ast.i16_type { fmt_type = .si_i16 } + ast.u16_type { fmt_type = .si_u16 } + ast.i64_type { fmt_type = .si_i64 } + ast.u64_type { fmt_type = .si_u64 } + ast.u32_type { fmt_type = .si_u32 } + else { fmt_type = .si_i32 } + } + } + } else { + // TODO: better check this case + fmt_type = .si_p + } + + /* + // pad filling 64bit format + mut pad_ch := u8(0) + if node.fills[i] { + pad_ch = u8(`0`) + } + res := get_str_intp_u64_format(fmt_type, node.fwidths[i], node.precisions[i], remove_tail_zeros, node.pluss[i], pad_ch, base, upper_case) + */ + + // pad filling 32bit format + mut pad_ch := 0 + if node.fills[i] { + pad_ch = 1 + } + res := get_str_intp_u32_format(fmt_type, node.fwidths[i], node.precisions[i], remove_tail_zeros, + node.pluss[i], byte(pad_ch), base, upper_case) + // + return res, fmt_type.str() +} + +fn (mut g Gen) str_val(node ast.StringInterLiteral, i int) { + expr := node.exprs[i] + + typ := g.unwrap_generic(node.expr_types[i]) + if typ == ast.string_type { + if g.inside_vweb_tmpl { + g.write('vweb__filter(') + if expr.is_auto_deref_var() { + g.write('*') + } + g.expr(expr) + g.write(')') + } else { + if expr.is_auto_deref_var() { + g.write('*') + } + g.expr(expr) + } + } else if node.fmts[i] == `s` || typ.has_flag(.variadic) { + g.gen_expr_to_string(expr, typ) + } else if typ.is_number() || typ.is_pointer() || node.fmts[i] == `d` { + if typ.is_signed() && node.fmts[i] in [`x`, `X`, `o`] { + // convert to unsigned first befors C's integer propagation strikes + if typ == ast.i8_type { + g.write('(byte)(') + } else if typ == ast.i16_type { + g.write('(u16)(') + } else if typ == ast.int_type { + g.write('(u32)(') + } else { + g.write('(u64)(') + } + if expr.is_auto_deref_var() { + g.write('*') + } + g.expr(expr) + g.write(')') + } else { + if expr.is_auto_deref_var() { + g.write('*') + } + g.expr(expr) + } + } else { + if expr.is_auto_deref_var() { + g.write('*') + } + g.expr(expr) + } +} + +fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) { + // fn (mut g Gen) str_int2(node ast.StringInterLiteral) { + g.write(' str_intp($node.vals.len, ') + g.write('_MOV((StrIntpData[]){') + for i, val in node.vals { + escaped_val := util.smart_quote(val, false) + + if escaped_val.len > 0 { + g.write('{_SLIT("$escaped_val"), ') + } else { + g.write('{_SLIT0, ') + } + + if i >= node.exprs.len { + // last part of the string + g.write('0, { .d_c = 0 }}') + break + } + + ft_u64, ft_str := g.str_format(node, i) + g.write('0x$ft_u64.hex(), {.d_$ft_str = ') + + // for pointers we need a void* cast + if unsafe { ft_str.str[0] } == `p` { + g.write('(void*)(') + g.str_val(node, i) + g.write(')') + } else { + g.str_val(node, i) + } + + g.write('}}') + if i < (node.vals.len - 1) { + g.write(', ') + } + } + g.write('}))') +} diff --git a/v_windows/v/old/vlib/v/gen/c/testdata/const_references.c.must_have b/v_windows/v/old/vlib/v/gen/c/testdata/const_references.c.must_have new file mode 100644 index 0000000..11a2cb8 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/c/testdata/const_references.c.must_have @@ -0,0 +1,3 @@ +VV_LOCAL_SYMBOL int main__a_const_accepting_fn(int* x, const int* const_x); +VV_LOCAL_SYMBOL int main__a_const_accepting_fn(int* x, const int* const_x) { +main__a_const_accepting_fn(&a, &b) diff --git a/v_windows/v/old/vlib/v/gen/c/testdata/const_references.out b/v_windows/v/old/vlib/v/gen/c/testdata/const_references.out new file mode 100644 index 0000000..01e79c3 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/c/testdata/const_references.out @@ -0,0 +1,3 @@ +1 +2 +3 diff --git a/v_windows/v/old/vlib/v/gen/c/testdata/const_references.vv b/v_windows/v/old/vlib/v/gen/c/testdata/const_references.vv new file mode 100644 index 0000000..ae94cc7 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/c/testdata/const_references.vv @@ -0,0 +1,11 @@ +fn a_const_accepting_fn(x &int, const_x &int) int { + return *x + *const_x +} + +fn main() { + a := 1 + b := 2 + println(a) + println(b) + println(a_const_accepting_fn(&a, &b)) +} diff --git a/v_windows/v/old/vlib/v/gen/c/utils.v b/v_windows/v/old/vlib/v/gen/c/utils.v new file mode 100644 index 0000000..a4768c8 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/c/utils.v @@ -0,0 +1,49 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module c + +import v.ast + +fn (mut g Gen) unwrap_generic(typ ast.Type) ast.Type { + if typ.has_flag(.generic) { + if t_typ := g.table.resolve_generic_to_concrete(typ, g.table.cur_fn.generic_names, + g.table.cur_concrete_types) + { + return t_typ + } + } + return typ +} + +struct Type { + // typ is the original type + typ ast.Type [required] + sym &ast.TypeSymbol [required] + // unaliased is `typ` once aliased have been resolved + // it may not contain informations such as flags and nr_muls + unaliased ast.Type [required] + unaliased_sym &ast.TypeSymbol [required] +} + +// unwrap returns the following variants of a type: +// * generics unwrapped +// * alias unwrapped +fn (mut g Gen) unwrap(typ ast.Type) Type { + no_generic := g.unwrap_generic(typ) + no_generic_sym := g.table.get_type_symbol(no_generic) + if no_generic_sym.kind != .alias { + return Type{ + typ: no_generic + sym: no_generic_sym + unaliased: no_generic + unaliased_sym: no_generic_sym + } + } + return Type{ + typ: no_generic + sym: no_generic_sym + unaliased: no_generic_sym.parent_idx + unaliased_sym: g.table.get_type_symbol(no_generic_sym.parent_idx) + } +} diff --git a/v_windows/v/old/vlib/v/gen/js/builtin_types.v b/v_windows/v/old/vlib/v/gen/js/builtin_types.v new file mode 100644 index 0000000..1bd1c15 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/builtin_types.v @@ -0,0 +1,393 @@ +module js + +import v.ast + +fn (mut g JsGen) to_js_typ_def_val(s string) string { + mut dval := '' + match s { + 'JS.Number' { dval = '0' } + 'JS.String' { dval = '""' } + 'JS.Boolean' { dval = 'false' } + 'JS.Array', 'JS.Map' { dval = '' } + else { dval = '{}' } + } + return dval +} + +fn (mut g JsGen) to_js_typ_val(t ast.Type) string { + sym := g.table.get_type_symbol(t) + mut styp := '' + mut prefix := if g.file.mod.name == 'builtin' { 'new ' } else { '' } + match sym.kind { + .i8, .i16, .int, .i64, .byte, .u8, .u16, .u32, .u64, .f32, .f64, .int_literal, + .float_literal, .size_t { + styp = '$prefix${g.sym_to_js_typ(sym)}(0)' + } + .bool { + styp = '$prefix${g.sym_to_js_typ(sym)}(false)' + } + .string { + styp = '$prefix${g.sym_to_js_typ(sym)}("")' + } + .map { + styp = 'new Map()' + } + .array { + styp = '$prefix${g.sym_to_js_typ(sym)}()' + } + .struct_ { + styp = 'new ${g.js_name(sym.name)}(${g.to_js_typ_def_val(sym.name)})' + } + .voidptr { + styp = 'null' + } + else { + // TODO + styp = 'undefined' + } + } + return styp +} + +fn (mut g JsGen) sym_to_js_typ(sym ast.TypeSymbol) string { + mut styp := '' + match sym.kind { + .i8 { + styp = 'i8' + } + .i16 { + styp = 'i16' + } + .int { + styp = 'int' + } + .i64 { + styp = 'i64' + } + .byte { + styp = 'byte' + } + .u16 { + styp = 'u16' + } + .u32 { + styp = 'u32' + } + .u64 { + styp = 'u64' + } + .f32 { + styp = 'f32' + } + .f64 { + styp = 'f64' + } + .int_literal { + styp = 'int_literal' + } + .float_literal { + styp = 'float_literal' + } + .size_t { + styp = 'size_t' + } + .bool { + styp = 'bool' + } + .string { + styp = 'string' + } + .map { + styp = 'map' + } + .array { + styp = 'array' + } + .voidptr { + styp = 'any' + } + else { + // TODO + styp = 'undefined' + } + } + return styp +} + +// V type to JS type +pub fn (mut g JsGen) typ(t ast.Type) string { + sym := g.table.get_type_symbol(t) + mut styp := '' + match sym.kind { + .placeholder { + // This should never happen: means checker bug + styp = 'any' + } + .void { + styp = 'void' + } + .voidptr { + styp = 'any' + } + .byteptr, .charptr { + styp = '${g.sym_to_js_typ(sym)}' + } + .i8, .i16, .int, .i64, .byte, .u8, .u16, .u32, .u64, .f32, .f64, .int_literal, + .float_literal, .size_t { + styp = '${g.sym_to_js_typ(sym)}' + } + .bool { + styp = '${g.sym_to_js_typ(sym)}' + } + .none_ { + styp = 'undefined' + } + .string, .char { + styp = '${g.sym_to_js_typ(sym)}' + } + // 'array_array_int' => 'number[][]' + .array { + info := sym.info as ast.Array + styp = '${g.sym_to_js_typ(sym)}(${g.typ(info.elem_type)})' + } + .array_fixed { + info := sym.info as ast.ArrayFixed + styp = '${g.sym_to_js_typ(sym)}(${g.typ(info.elem_type)})' + } + .chan { + styp = 'chan' + } + // 'map[string]int' => 'Map' + .map { + info := sym.info as ast.Map + key := g.typ(info.key_type) + val := g.typ(info.value_type) + styp = 'Map<$key, $val>' + } + .any { + styp = 'any' + } + // ns.Foo => alias["Foo"]["prototype"] + .struct_ { + styp = g.struct_typ(sym.name) + } + .generic_struct_inst {} + // 'multi_return_int_int' => '[number, number]' + .multi_return { + info := sym.info as ast.MultiReturn + types := info.types.map(g.typ(it)) + joined := types.join(', ') + styp = '[$joined]' + } + .sum_type { + // TODO: Implement sumtypes + styp = 'union_sym_type' + } + .alias { + // TODO: Implement aliases + styp = 'alias' + } + .enum_ { + // NB: We could declare them as TypeScript enums but TS doesn't like + // our namespacing so these break if declared in a different module. + // Until this is fixed, We need to use the type of an enum's members + // rather than the enum itself, and this can only be 'number' for now + styp = 'number' + } + // 'anon_fn_7_7_1' => '(a number, b number) => void' + .function { + info := sym.info as ast.FnType + styp = g.fn_typ(info.func.params, info.func.return_type) + } + .interface_ { + styp = g.js_name(sym.name) + } + .rune { + styp = 'any' + } + .aggregate { + panic('TODO: unhandled aggregate in JS') + } + .thread { + panic('TODO: unhandled thread in JS') + } + } + /* + else { + println('jsgen.typ: Unhandled type $t') + styp = sym.name + } + */ + if styp.starts_with('JS.') { + return styp[3..] + } + return styp +} + +fn (mut g JsGen) fn_typ(args []ast.Param, return_type ast.Type) string { + mut res := '(' + for i, arg in args { + res += '$arg.name: ${g.typ(arg.typ)}' + if i < args.len - 1 { + res += ', ' + } + } + return res + ') => ' + g.typ(return_type) +} + +fn (mut g JsGen) struct_typ(s string) string { + ns := get_ns(s) + if ns == 'JS' { + return s[3..] + } + mut name := if ns == g.ns.name { s.split('.').last() } else { g.get_alias(s) } + mut styp := '' + for i, v in name.split('.') { + if i == 0 { + styp = v + } else { + styp += '["$v"]' + } + } + if ns in ['', g.ns.name] { + return styp + } + return styp + '["prototype"]' +} + +struct BuiltinPrototypeConfig { + typ_name string + val_name string = 'val' + default_value string + constructor string = 'this.val = val' + value_of string = 'this.val' + to_string string = 'this.val.toString()' + eq string = 'this.val === other.val' + to_jsval string = 'this' + extras string + has_strfn bool +} + +fn (mut g JsGen) gen_builtin_prototype(c BuiltinPrototypeConfig) { + g.writeln('function ${c.typ_name}($c.val_name = $c.default_value) { $c.constructor }') + g.writeln('${c.typ_name}.prototype = {') + g.inc_indent() + g.writeln('$c.val_name: $c.default_value,') + if c.extras.len > 0 { + g.writeln('$c.extras,') + } + for method in g.method_fn_decls[c.typ_name] { + g.inside_def_typ_decl = true + g.gen_method_decl(method) + g.inside_def_typ_decl = false + g.writeln(',') + } + g.writeln('valueOf() { return $c.value_of },') + g.writeln('toString() { return $c.to_string },') + g.writeln('eq(other) { return $c.eq },') + g.writeln('\$toJS() { return $c.to_jsval }, ') + if c.has_strfn { + g.writeln('str() { return new string(this.toString()) }') + } + g.dec_indent() + g.writeln('};\n') +} + +// generate builtin type definitions, used for casting and methods. +fn (mut g JsGen) gen_builtin_type_defs() { + g.inc_indent() + for typ_name in v_types { + // TODO: JsDoc + match typ_name { + 'i8', 'i16', 'int', 'i64', 'u16', 'u32', 'u64', 'int_literal', 'size_t' { + // TODO: Bounds checking + g.gen_builtin_prototype( + typ_name: typ_name + default_value: 'new Number(0)' + constructor: 'this.val = val | 0' + value_of: 'this.val | 0' + to_string: 'this.valueOf().toString()' + eq: 'this.valueOf() === other.valueOf()' + to_jsval: '+this' + ) + } + 'byte' { + g.gen_builtin_prototype( + typ_name: typ_name + default_value: 'new Number(0)' + constructor: 'this.val = typeof(val) == "string" ? val.charCodeAt() : (val | 0)' + value_of: 'this.val | 0' + to_string: 'new string(this.val + "")' + eq: 'this.valueOf() === other.valueOf()' + to_jsval: '+this' + ) + } + 'f32', 'f64', 'float_literal' { + g.gen_builtin_prototype( + typ_name: typ_name + default_value: 'new Number(0)' + to_jsval: '+this' + ) + } + 'bool' { + g.gen_builtin_prototype( + constructor: 'this.val = +val !== 0' + typ_name: typ_name + default_value: 'new Boolean(false)' + to_jsval: '+this != 0' + ) + } + 'string' { + g.gen_builtin_prototype( + typ_name: typ_name + val_name: 'str' + default_value: 'new String("")' + constructor: 'this.str = str.toString(); this.len = this.str.length' + value_of: 'this.str' + to_string: 'this.str' + eq: 'this.str === other.str' + has_strfn: false + to_jsval: 'this.str' + ) + } + 'map' { + g.gen_builtin_prototype( + typ_name: typ_name + val_name: 'map' + default_value: 'new Map()' + constructor: 'this.map = map' + value_of: 'this' + to_string: 'this.map.toString()' + eq: 'vEq(this, other)' + to_jsval: 'this.map' + ) + } + 'array' { + g.gen_builtin_prototype( + typ_name: typ_name + val_name: 'arr' + default_value: 'new Array()' + constructor: 'this.arr = arr' + value_of: 'this' + to_string: 'JSON.stringify(this.arr.map(it => it.valueOf()))' + eq: 'vEq(this, other)' + to_jsval: 'this.arr' + ) + } + 'any' { + g.gen_builtin_prototype( + typ_name: typ_name + val_name: 'any' + default_value: 'null' + constructor: 'this.val = any' + value_of: 'this.val' + to_string: '"&" + this.val' + eq: 'this == other' // compare by ptr + to_jsval: 'this.val.\$toJS()' + ) + } + else {} + } + } + g.dec_indent() +} diff --git a/v_windows/v/old/vlib/v/gen/js/comptime.v b/v_windows/v/old/vlib/v/gen/js/comptime.v new file mode 100644 index 0000000..d4c55ee --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/comptime.v @@ -0,0 +1,311 @@ +module js + +import v.ast +import v.pref + +fn (mut g JsGen) comp_if(node ast.IfExpr) { + if !node.is_expr && !node.has_else && node.branches.len == 1 { + if node.branches[0].stmts.len == 0 { + // empty ifdef; result of target OS != conditional => skip + return + } + } + + for i, branch in node.branches { + if i == node.branches.len - 1 && node.has_else { + g.writeln('else') + } else { + if i == 0 { + g.write('if (') + } else { + g.write('else if (') + } + g.comp_if_cond(branch.cond, branch.pkg_exist) + g.writeln(')') + } + + if node.is_expr { + print('$branch.stmts') + len := branch.stmts.len + if len > 0 { + last := branch.stmts[len - 1] as ast.ExprStmt + if len > 1 { + tmp := g.new_tmp_var() + g.inc_indent() + g.writeln('let $tmp;') + g.writeln('{') + g.stmts(branch.stmts[0..len - 1]) + g.write('\t$tmp = ') + g.stmt(last) + g.writeln('}') + g.dec_indent() + g.writeln('$tmp;') + } else { + g.stmt(last) + } + } + } else { + g.writeln('{') + g.stmts(branch.stmts) + g.writeln('}') + } + } +} + +/* +// returning `false` means the statements inside the $if can be skipped +*/ +// returns the value of the bool comptime expression +fn (mut g JsGen) comp_if_cond(cond ast.Expr, pkg_exist bool) bool { + match cond { + ast.BoolLiteral { + g.expr(cond) + return true + } + ast.ParExpr { + g.write('(') + is_cond_true := g.comp_if_cond(cond.expr, pkg_exist) + g.write(')') + return is_cond_true + } + ast.PrefixExpr { + g.write(cond.op.str()) + return g.comp_if_cond(cond.right, pkg_exist) + } + ast.PostfixExpr { + ifdef := g.comp_if_to_ifdef((cond.expr as ast.Ident).name, true) or { + verror(err.msg) + return false + } + g.write('$ifdef') + return true + } + ast.InfixExpr { + match cond.op { + .and, .logical_or { + l := g.comp_if_cond(cond.left, pkg_exist) + g.write(' $cond.op ') + r := g.comp_if_cond(cond.right, pkg_exist) + return if cond.op == .and { l && r } else { l || r } + } + .key_is, .not_is { + left := cond.left + mut name := '' + mut exp_type := ast.Type(0) + got_type := (cond.right as ast.TypeNode).typ + // Handle `$if x is Interface {` + // mut matches_interface := 'false' + if left is ast.TypeNode && cond.right is ast.TypeNode + && g.table.get_type_symbol(got_type).kind == .interface_ { + // `$if Foo is Interface {` + interface_sym := g.table.get_type_symbol(got_type) + if interface_sym.info is ast.Interface { + // q := g.table.get_type_symbol(interface_sym.info.types[0]) + checked_type := g.unwrap_generic(left.typ) + // TODO PERF this check is run twice (also in the checker) + // store the result in a field + is_true := g.table.does_type_implement_interface(checked_type, + got_type) + // true // exp_type in interface_sym.info.types + if cond.op == .key_is { + if is_true { + g.write('1') + } else { + g.write('0') + } + return is_true + } else if cond.op == .not_is { + if is_true { + g.write('0') + } else { + g.write('1') + } + return !is_true + } + // matches_interface = '/*iface:$got_type $exp_type*/ true' + //} + } + } else if left is ast.SelectorExpr { + name = '${left.expr}.$left.field_name' + exp_type = g.comptime_var_type_map[name] + } else if left is ast.TypeNode { + name = left.str() + // this is only allowed for generics currently, otherwise blocked by checker + exp_type = g.unwrap_generic(left.typ) + } + + if cond.op == .key_is { + g.write('$exp_type == $got_type') + return exp_type == got_type + } else { + g.write('$exp_type != $got_type') + return exp_type != got_type + } + } + .eq, .ne { + // TODO Implement `$if method.args.len == 1` + g.write('1') + return true + } + else { + return true + } + } + } + ast.Ident { + ifdef := g.comp_if_to_ifdef(cond.name, false) or { 'true' } // handled in checker + g.write('$ifdef') + return true + } + ast.ComptimeCall { + g.write('$pkg_exist') + return true + } + else { + // should be unreachable, but just in case + g.write('1') + return true + } + } +} + +fn (mut g JsGen) comp_if_to_ifdef(name string, is_comptime_optional bool) ?string { + match name { + // platforms/os-es: + 'windows' { + return '(\$process.platform == "windows")' + } + 'ios' { + return '(\$process.platform == "darwin")' + } + 'macos' { + return '(\$process.platform == "darwin")' + } + 'mach' { + return '(\$process.platform == "darwin")' + } + 'darwin' { + return '(\$process.platform == "darwin")' + } + 'linux' { + return '(\$process.platform == "linux")' + } + 'freebsd' { + return '(\$process.platform == "freebsd")' + } + 'openbsd' { + return '(\$process.platform == "openbsd")' + } + 'bsd' { + return '(\$process.platform == "freebsd" || (\$process.platform == "openbsd"))' + } + 'android' { + return '(\$process.platform == "android")' + } + 'solaris' { + return '(\$process.platform == "sunos")' + } + 'js_node' { + if g.pref.backend == .js_node { + return 'true' + } else { + return 'false' + } + } + 'js_freestanding' { + if g.pref.backend == .js_freestanding { + return 'true' + } else { + return 'false' + } + } + 'js_browser' { + if g.pref.backend == .js_browser { + return 'true' + } else { + return 'false' + } + } + // + 'js' { + return 'true' + } + // compilers: + 'gcc' { + return 'false' + } + 'tinyc' { + return 'false' + } + 'clang' { + return 'false' + } + 'mingw' { + return 'false' + } + 'msvc' { + return 'false' + } + 'cplusplus' { + return 'false' + } + // other: + 'threads' { + return 'false' + } + 'gcboehm' { + return 'false' + } + // todo(playX): these should return true or false depending on CLI options + 'debug' { + return 'false' + } + 'prod' { + return 'false' + } + 'test' { + return 'false' + } + 'glibc' { + return 'false' + } + 'prealloc' { + return 'false' + } + 'no_bounds_checking' { + return 'CUSTOM_DEFINE_no_bounds_checking' + } + 'freestanding' { + return '_VFREESTANDING' + } + // architectures: + 'amd64' { + return '(\$process.arch == "x64")' + } + 'aarch64', 'arm64' { + return '(\$process.arch == "arm64)' + } + // bitness: + 'x64' { + return '(\$process.arch == "x64")' + } + 'x32' { + return '(\$process.arch == "x32")' + } + // endianness: + 'little_endian' { + return '(\$os.endianess == "LE")' + } + 'big_endian' { + return '(\$os.endianess == "BE")' + } + else { + if is_comptime_optional + || (g.pref.compile_defines_all.len > 0 && name in g.pref.compile_defines_all) { + return 'CUSTOM_DEFINE_$name' + } + return error('bad os ifdef name "$name"') // should never happen, caught in the checker + } + } + return none +} diff --git a/v_windows/v/old/vlib/v/gen/js/fast_deep_equal.js b/v_windows/v/old/vlib/v/gen/js/fast_deep_equal.js new file mode 100644 index 0000000..50d37e8 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/fast_deep_equal.js @@ -0,0 +1,72 @@ +// https://www.npmjs.com/package/fast-deep-equal - 3/3/2021 +const envHasBigInt64Array = typeof BigInt64Array !== 'undefined'; +function vEq(a, b) { + if (a === b) return true; + + if (a && b && typeof a == 'object' && typeof b == 'object') { + if (a.constructor !== b.constructor) return false; + // we want to convert all V types to JS for comparison. + if ('$toJS' in a) + a = a.$toJS(); + + if ('$toJS' in b) + b = b.$toJS(); + + var length, i, keys; + if (Array.isArray(a)) { + length = a.length; + if (length != b.length) return false; + for (i = length; i-- !== 0;) + if (!vEq(a[i], b[i])) return false; + return true; + } + + + if ((a instanceof Map) && (b instanceof Map)) { + if (a.size !== b.size) return false; + for (i of a.entries()) + if (!b.has(i[0])) return false; + for (i of a.entries()) + if (!vEq(i[1], b.get(i[0]))) return false; + return true; + } + + if ((a instanceof Set) && (b instanceof Set)) { + if (a.size !== b.size) return false; + for (i of a.entries()) + if (!b.has(i[0])) return false; + return true; + } + + if (ArrayBuffer.isView(a) && ArrayBuffer.isView(b)) { + length = a.length; + if (length != b.length) return false; + for (i = length; i-- !== 0;) + if (a[i] !== b[i]) return false; + return true; + } + + + if (a.constructor === RegExp) return a.source === b.source && a.flags === b.flags; + if (a.valueOf !== Object.prototype.valueOf) return a.valueOf() === b.valueOf(); + if (a.toString !== Object.prototype.toString) return a.toString() === b.toString(); + + keys = Object.keys(a); + length = keys.length; + if (length !== Object.keys(b).length) return false; + + for (i = length; i-- !== 0;) + if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false; + + for (i = length; i-- !== 0;) { + var key = keys[i]; + + if (!vEq(a[key], b[key])) return false; + } + + return true; + } + + // true if both NaN, false otherwise + return a !== a && b !== b; +}; \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/gen/js/js.v b/v_windows/v/old/vlib/v/gen/js/js.v new file mode 100644 index 0000000..6ec623e --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/js.v @@ -0,0 +1,2103 @@ +module js + +import strings +import v.ast +import v.token +import v.pref +import v.util +import v.util.version +import v.depgraph +import encoding.base64 +import v.gen.js.sourcemap + +struct MutArg { + tmp_var string + expr ast.Expr = ast.empty_expr() +} + +const ( + // https://ecma-international.org/ecma-262/#sec-reserved-words + js_reserved = ['await', 'break', 'case', 'catch', 'class', 'const', 'continue', 'debugger', + 'default', 'delete', 'do', 'else', 'enum', 'export', 'extends', 'finally', 'for', 'function', + 'if', 'implements', 'import', 'in', 'instanceof', 'interface', 'let', 'new', 'package', + 'private', 'protected', 'public', 'return', 'static', 'super', 'switch', 'this', 'throw', + 'try', 'typeof', 'var', 'void', 'while', 'with', 'yield', 'Number', 'String', 'Boolean', + 'Array', 'Map'] + // used to generate type structs + v_types = ['i8', 'i16', 'int', 'i64', 'byte', 'u16', 'u32', 'u64', 'f32', 'f64', + 'int_literal', 'float_literal', 'size_t', 'bool', 'string', 'map', 'array', 'any'] + shallow_equatables = [ast.Kind.i8, .i16, .int, .i64, .byte, .u16, .u32, .u64, .f32, .f64, + .int_literal, .float_literal, .size_t, .bool, .string] +) + +struct SourcemapHelper { + src_path string + src_line u32 + ns_pos u32 +} + +struct Namespace { + name string +mut: + out strings.Builder = strings.new_builder(128) + pub_vars []string + imports map[string]string + indent int + methods map[string][]ast.FnDecl + sourcemap_helper []SourcemapHelper +} + +[heap] +struct JsGen { + pref &pref.Preferences +mut: + table &ast.Table + definitions strings.Builder + ns &Namespace + namespaces map[string]&Namespace + doc &JsDoc + enable_doc bool + file &ast.File + tmp_count int + inside_ternary bool + inside_loop bool + inside_map_set bool // map.set(key, value) + inside_builtin bool + generated_builtin bool + inside_def_typ_decl bool + is_test bool + stmt_start_pos int + defer_stmts []ast.DeferStmt + fn_decl &ast.FnDecl // pointer to the FnDecl we are currently inside otherwise 0 + str_types []string // types that need automatic str() generation + method_fn_decls map[string][]ast.FnDecl + builtin_fns []string // Functions defined in `builtin` + empty_line bool + cast_stack []ast.Type + call_stack []ast.CallExpr + is_vlines_enabled bool // is it safe to generate #line directives when -g is passed + sourcemap sourcemap.SourceMap // maps lines in generated javascrip file to original source files and line + comptime_var_type_map map[string]ast.Type + defer_ifdef string +} + +pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string { + mut g := &JsGen{ + definitions: strings.new_builder(100) + table: table + pref: pref + fn_decl: 0 + empty_line: true + doc: 0 + ns: 0 + enable_doc: true + file: 0 + } + g.doc = new_jsdoc(g) + // TODO: Add '[-no]-jsdoc' flag + if pref.is_prod { + g.enable_doc = false + g.is_vlines_enabled = false + } + g.init() + mut graph := depgraph.new_dep_graph() + if g.pref.sourcemap { + mut sg := sourcemap.generate_empty_map() + g.sourcemap = sg.add_map('', '', g.pref.sourcemap_src_included, 0, 0) + } + // Get class methods + for file in files { + g.file = file + g.enter_namespace(g.file.mod.name) + g.is_test = g.pref.is_test + g.find_class_methods(file.stmts) + g.escape_namespace() + } + + for file in files { + g.file = file + g.enter_namespace(g.file.mod.name) + g.is_test = g.pref.is_test + // store imports + mut imports := []string{} + for imp in g.file.imports { + imports << imp.mod + } + graph.add(g.file.mod.name, imports) + // builtin types + if g.file.mod.name == 'builtin' && !g.generated_builtin { + g.gen_builtin_type_defs() + g.writeln('Object.defineProperty(array.prototype,"len", { get: function() {return new builtin.int(this.arr.length);}, set: function(l) { this.arr.length = l.valueOf(); } }); ') + g.writeln('Object.defineProperty(string.prototype,"len", { get: function() {return new builtin.int(this.str.length);}, set: function(l) {/* ignore */ } }); ') + g.writeln('Object.defineProperty(map.prototype,"len", { get: function() {return new builtin.int(this.map.length);}, set: function(l) { this.map.length = l.valueOf(); } }); ') + g.writeln('Object.defineProperty(array.prototype,"length", { get: function() {return new builtin.int(this.arr.length);}, set: function(l) { this.arr.length = l.valueOf(); } }); ') + g.generated_builtin = true + } + + g.stmts(file.stmts) + // store the current namespace + g.escape_namespace() + } + // resolve imports + deps_resolved := graph.resolve() + nodes := deps_resolved.nodes + mut out := g.hashes() + g.definitions.str() + // equality check for js objects + // TODO: Fix msvc bug that's preventing $embed_file('fast_deep_equal.js') + // unsafe { + // mut eq_fn := $embed_file('fast_deep_equal.js') + // out += eq_fn.data().vstring() + //} + out += fast_deep_eq_fn + for node in nodes { + name := g.js_name(node.name).replace('.', '_') + if g.enable_doc { + out += '/** @namespace $name */\n' + } + out += 'const $name = (function (' + mut namespace := g.namespaces[node.name] + mut first := true + for _, val in namespace.imports { + if !first { + out += ', ' + } + first = false + out += val + } + out += ') {\n\t' + namespace_code := namespace.out.str() + if g.pref.sourcemap { + // calculate current output start line + mut current_line := u32(out.count('\n') + 1) + mut sm_pos := u32(0) + for sourcemap_ns_entry in namespace.sourcemap_helper { + // calculate final generated location in output based on position + current_segment := namespace_code.substr(int(sm_pos), int(sourcemap_ns_entry.ns_pos)) + current_line += u32(current_segment.count('\n')) + current_column := if last_nl_pos := current_segment.last_index('\n') { + u32(current_segment.len - last_nl_pos - 1) + } else { + u32(0) + } + g.sourcemap.add_mapping(sourcemap_ns_entry.src_path, sourcemap.SourcePosition{ + source_line: sourcemap_ns_entry.src_line + source_column: 0 // sourcemap_ns_entry.src_column + }, current_line, current_column, '') + sm_pos = sourcemap_ns_entry.ns_pos + } + } + out += namespace_code + + // public scope + out += '\n' + if g.enable_doc { + out += '\n\t/* module exports */' + } + out += '\n\treturn {' + // export builtin types + if name == 'builtin' { + for typ in js.v_types { + out += '\n\t\t$typ,' + } + } + for i, pub_var in namespace.pub_vars { + out += '\n\t\t$pub_var' + if i < namespace.pub_vars.len - 1 { + out += ',' + } + } + if namespace.pub_vars.len > 0 { + out += '\n\t' + } + out += '};' + out += '\n})(' + first = true + for key, _ in namespace.imports { + if !first { + out += ', ' + } + first = false + out += key.replace('.', '_') + } + out += ');\n' + // generate builtin basic type casts + if name == 'builtin' { + out += '// builtin type casts\n' + out += 'const [' + for i, typ in js.v_types { + if i > 0 { + out += ', ' + } + out += '$typ' + } + out += '] = [' + for i, typ in js.v_types { + if i > 0 { + out += ',' + } + out += '\n\tfunction(val) { return new builtin.${typ}(val) }' + } + out += '\n]\n' + } + } + if pref.is_shared { + // Export, through CommonJS, the module of the entry file if `-shared` was passed + export := nodes[nodes.len - 1].name + out += 'if (typeof module === "object" && module.exports) module.exports = $export;\n' + } + out += '\n' + if g.pref.sourcemap { + out += g.create_sourcemap() + } + return out +} + +fn (g JsGen) create_sourcemap() string { + mut sm := g.sourcemap + mut out := '\n//# sourceMappingURL=data:application/json;base64,' + out += base64.encode(sm.to_json().str().bytes()) + out += '\n' + + return out +} + +pub fn (mut g JsGen) enter_namespace(name string) { + if g.namespaces[name] == 0 { + // create a new namespace + ns := &Namespace{ + name: name + } + g.namespaces[name] = ns + g.ns = ns + } else { + g.ns = g.namespaces[name] + } + g.inside_builtin = name == 'builtin' +} + +pub fn (mut g JsGen) escape_namespace() { + g.ns = &Namespace(0) + g.inside_builtin = false +} + +pub fn (mut g JsGen) push_pub_var(s string) { + g.ns.pub_vars << g.js_name(s) +} + +pub fn (mut g JsGen) find_class_methods(stmts []ast.Stmt) { + for stmt in stmts { + match stmt { + ast.FnDecl { + if stmt.is_method { + // Found struct method, store it to be generated along with the class. + mut class_name := g.table.get_type_name(stmt.receiver.typ) + // Workaround until `map[key] << val` works. + mut arr := g.method_fn_decls[class_name] + arr << stmt + g.method_fn_decls[class_name] = arr + } + } + else {} + } + } +} + +pub fn (mut g JsGen) init() { + g.definitions.writeln('// Generated by the V compiler\n') + g.definitions.writeln('"use strict";') + g.definitions.writeln('') + g.definitions.writeln('var \$global = (new Function("return this"))();') + g.definitions.writeln('function \$ref(value) { this.val = value; } ') + g.definitions.writeln('\$ref.prototype.valueOf = function() { return this.val; } ') + if g.pref.backend != .js_node { + g.definitions.writeln('const \$process = {') + g.definitions.writeln(' arch: "js",') + if g.pref.backend == .js_freestanding { + g.definitions.writeln(' platform: "freestanding"') + } else { + g.definitions.writeln(' platform: "browser"') + } + g.definitions.writeln('}') + + g.definitions.writeln('const \$os = {') + g.definitions.writeln(' endianess: "LE",') + + g.definitions.writeln('}') + } else { + g.definitions.writeln('const \$os = require("os");') + g.definitions.writeln('const \$process = process;') + } +} + +pub fn (g JsGen) hashes() string { + mut res := '// V_COMMIT_HASH $version.vhash()\n' + res += '// V_CURRENT_COMMIT_HASH ${version.githash(g.pref.building_v)}\n' + return res +} + +[noreturn] +fn verror(msg string) { + eprintln('jsgen error: $msg') + exit(1) +} + +[inline] +pub fn (mut g JsGen) gen_indent() { + if g.ns.indent > 0 && g.empty_line { + g.ns.out.write_string(util.tabs(g.ns.indent)) + } + g.empty_line = false +} + +[inline] +pub fn (mut g JsGen) inc_indent() { + g.ns.indent++ +} + +[inline] +pub fn (mut g JsGen) dec_indent() { + g.ns.indent-- +} + +[inline] +pub fn (mut g JsGen) write(s string) { + if g.ns == 0 { + verror('g.write: not in a namespace') + } + g.gen_indent() + g.ns.out.write_string(s) +} + +[inline] +pub fn (mut g JsGen) writeln(s string) { + if g.ns == 0 { + verror('g.writeln: not in a namespace') + } + g.gen_indent() + g.ns.out.writeln(s) + g.empty_line = true +} + +[inline] +pub fn (mut g JsGen) new_tmp_var() string { + g.tmp_count++ + return '_tmp$g.tmp_count' +} + +// 'mod1.mod2.fn' => 'mod1.mod2' +// 'fn' => '' +[inline] +fn get_ns(s string) string { + idx := s.last_index('.') or { return '' } + return s.substr(0, idx) +} + +fn (mut g JsGen) get_alias(name string) string { + ns := get_ns(name) + if ns == '' { + return name + } + alias := g.ns.imports[ns] + if alias == '' { + return name + } + return alias + '.' + name.split('.').last() +} + +fn (mut g JsGen) js_name(name_ string) string { + mut is_js := false + mut name := name_ + if name.starts_with('JS.') { + name = name[3..] + is_js = true + } + ns := get_ns(name) + name = if g.ns == 0 { + name + } else if ns == g.ns.name { + name.split('.').last() + } else { + g.get_alias(name) + } + mut parts := name.split('.') + if !is_js { + for i, p in parts { + if p in js.js_reserved { + parts[i] = 'v_$p' + } + } + } + return parts.join('.') +} + +fn (mut g JsGen) stmts(stmts []ast.Stmt) { + g.inc_indent() + for stmt in stmts { + g.stmt(stmt) + } + g.dec_indent() +} + +[inline] +fn (mut g JsGen) write_v_source_line_info(pos token.Position) { + // g.inside_ternary == 0 && + if g.pref.sourcemap { + g.ns.sourcemap_helper << SourcemapHelper{ + src_path: util.vlines_escape_path(g.file.path, g.pref.ccompiler) + src_line: u32(pos.line_nr + 1) + ns_pos: u32(g.ns.out.len) + } + } + if g.pref.is_vlines && g.is_vlines_enabled { + g.write(' /* ${pos.line_nr + 1} $g.ns.out.len */ ') + } +} + +fn (mut g JsGen) gen_global_decl(node ast.GlobalDecl) { + mod := if g.pref.build_mode == .build_module { 'enumerable: false' } else { 'enumerable: true' } + for field in node.fields { + if field.has_expr { + tmp_var := g.new_tmp_var() + g.write('const $tmp_var = ') + g.expr(field.expr) + g.writeln(';') + g.writeln('Object.defineProperty(\$global,"$field.name", { + configurable: false, + $mod , + writable: true, + value: $tmp_var + } + ); // global') + } else { + // TODO(playXE): Initialize with default value of type + } + } +} + +fn (mut g JsGen) stmt(node ast.Stmt) { + g.stmt_start_pos = g.ns.out.len + match node { + ast.EmptyStmt {} + ast.AsmStmt { + panic('inline asm is not supported by js') + } + ast.AssertStmt { + g.write_v_source_line_info(node.pos) + g.gen_assert_stmt(node) + } + ast.AssignStmt { + g.write_v_source_line_info(node.pos) + g.gen_assign_stmt(node) + } + ast.Block { + g.write_v_source_line_info(node.pos) + g.gen_block(node) + g.writeln('') + } + ast.BranchStmt { + g.write_v_source_line_info(node.pos) + g.gen_branch_stmt(node) + } + ast.CompFor {} + ast.ConstDecl { + g.write_v_source_line_info(node.pos) + g.gen_const_decl(node) + } + ast.DeferStmt { + g.defer_stmts << node + } + ast.EnumDecl { + g.write_v_source_line_info(node.pos) + g.gen_enum_decl(node) + g.writeln('') + } + ast.ExprStmt { + g.write_v_source_line_info(node.pos) + g.gen_expr_stmt(node) + } + ast.FnDecl { + g.write_v_source_line_info(node.pos) + g.fn_decl = unsafe { &node } + g.gen_fn_decl(node) + } + ast.ForCStmt { + g.write_v_source_line_info(node.pos) + g.gen_for_c_stmt(node) + g.writeln('') + } + ast.ForInStmt { + g.write_v_source_line_info(node.pos) + g.gen_for_in_stmt(node) + g.writeln('') + } + ast.ForStmt { + g.write_v_source_line_info(node.pos) + g.gen_for_stmt(node) + g.writeln('') + } + ast.GlobalDecl { + g.write_v_source_line_info(node.pos) + g.gen_global_decl(node) + g.writeln('') + } + ast.GotoLabel { + g.write_v_source_line_info(node.pos) + g.writeln('${g.js_name(node.name)}:') + } + ast.GotoStmt { + // skip: JS has no goto + } + ast.HashStmt { + g.write_v_source_line_info(node.pos) + g.gen_hash_stmt(node) + } + ast.Import { + g.ns.imports[node.mod] = node.alias + } + ast.InterfaceDecl { + g.write_v_source_line_info(node.pos) + g.gen_interface_decl(node) + } + ast.Module { + // skip: namespacing implemented externally + } + ast.NodeError {} + ast.Return { + if g.defer_stmts.len > 0 { + g.gen_defer_stmts() + } + g.gen_return_stmt(node) + } + ast.SqlStmt {} + ast.StructDecl { + g.write_v_source_line_info(node.pos) + g.gen_struct_decl(node) + } + ast.TypeDecl { + // skip JS has no typedecl + } + } +} + +fn (mut g JsGen) expr(node ast.Expr) { + match node { + ast.NodeError {} + ast.EmptyExpr {} + ast.CTempVar { + g.write('/* ast.CTempVar: node.name */') + } + ast.DumpExpr { + g.write('/* ast.DumpExpr: $node.expr */') + } + ast.AnonFn { + g.gen_fn_decl(node.decl) + } + ast.ArrayInit { + g.gen_array_init_expr(node) + } + ast.AsCast { + // skip: JS has no types, so no need to cast + // TODO: Is jsdoc needed here for TS support? + } + ast.Assoc { + // TODO + } + ast.BoolLiteral { + if node.val == true { + g.write('true') + } else { + g.write('false') + } + } + ast.CallExpr { + g.gen_call_expr(node) + } + ast.ChanInit { + // TODO + } + ast.CastExpr { + g.gen_type_cast_expr(node) + } + ast.CharLiteral { + // todo(playX): char type? + g.write("new builtin.string('$node.val')") + } + ast.Comment {} + ast.ConcatExpr { + // TODO + } + ast.EnumVal { + sym := g.table.get_type_symbol(node.typ) + styp := g.js_name(sym.name) + g.write('${styp}.$node.val') + } + ast.FloatLiteral { + g.gen_float_literal_expr(node) + } + ast.GoExpr { + g.gen_go_expr(node) + } + ast.Ident { + g.gen_ident(node) + } + ast.IfExpr { + g.gen_if_expr(node) + } + ast.IfGuardExpr { + // TODO no optionals yet + } + ast.IndexExpr { + g.gen_index_expr(node) + } + ast.InfixExpr { + g.gen_infix_expr(node) + } + ast.IntegerLiteral { + g.gen_integer_literal_expr(node) + } + ast.LockExpr { + g.gen_lock_expr(node) + } + ast.MapInit { + g.gen_map_init_expr(node) + } + ast.MatchExpr { + // TODO + } + ast.None { + // TODO + } + ast.OrExpr { + // TODO + } + ast.ParExpr { + g.write('(') + g.expr(node.expr) + g.write(')') + } + ast.PostfixExpr { + g.expr(node.expr) + g.write(node.op.str()) + } + ast.PrefixExpr { + if node.op in [.amp, .mul] { + // C pointers/references: ignore them + if node.op == .amp { + if !node.right_type.is_pointer() { + // kind of weird way to handle references but it allows us to access type methods easily. + g.write('(function(x) {') + g.write(' return { val: x, __proto__: Object.getPrototypeOf(x), valueOf: function() { return this.val; } }})( ') + g.expr(node.right) + g.write(')') + } else { + g.expr(node.right) + } + } else { + g.write('(') + g.expr(node.right) + g.write(').valueOf()') + } + } else { + g.write(node.op.str()) + g.write('(') + g.expr(node.right) + g.write('.valueOf()') + g.write(')') + } + } + ast.RangeExpr { + // Only used in IndexExpr, requires index type info + } + ast.SelectExpr { + // TODO: to be implemented + } + ast.SelectorExpr { + g.gen_selector_expr(node) + } + ast.SizeOf, ast.IsRefType { + // TODO + } + ast.OffsetOf { + // TODO + } + ast.SqlExpr { + // TODO + } + ast.StringInterLiteral { + g.gen_string_inter_literal(node) + } + ast.StringLiteral { + g.gen_string_literal(node) + } + ast.StructInit { + // TODO: once generic fns/unwrap_generic is implemented + // if node.unresolved { + // g.expr(ast.resolve_init(node, g.unwrap_generic(node.typ), g.table)) + // } else { + // // `user := User{name: 'Bob'}` + // g.gen_struct_init(node) + // } + // `user := User{name: 'Bob'}` + g.gen_struct_init(node) + } + ast.TypeNode { + // skip: JS has no types + // TODO maybe? + } + ast.Likely { + g.write('(') + g.expr(node.expr) + g.write(')') + } + ast.TypeOf { + g.gen_typeof_expr(node) + // TODO: Should this print the V type or the JS type? + } + ast.AtExpr { + g.write('"$node.val"') + } + ast.ComptimeCall { + // TODO + } + ast.ComptimeSelector { + // TODO + } + ast.UnsafeExpr { + g.expr(node.expr) + } + ast.ArrayDecompose {} + } +} + +// TODO +fn (mut g JsGen) gen_assert_stmt(a ast.AssertStmt) { + if !a.is_used { + return + } + g.writeln('// assert') + g.write('if( ') + g.expr(a.expr) + g.write(' ) {') + s_assertion := a.expr.str().replace('"', "'") + mut mod_path := g.file.path.replace('\\', '\\\\') + if g.is_test { + g.writeln(' g_test_oks++;') + g.writeln(' cb_assertion_ok("$mod_path", ${a.pos.line_nr + 1}, "assert $s_assertion", "${g.fn_decl.name}()" );') + g.writeln('} else {') + g.writeln(' g_test_fails++;') + g.writeln(' cb_assertion_failed("$mod_path", ${a.pos.line_nr + 1}, "assert $s_assertion", "${g.fn_decl.name}()" );') + g.writeln(' exit(1);') + g.writeln('}') + return + } + g.writeln('} else {') + g.inc_indent() + g.writeln('builtin.eprintln("$mod_path:${a.pos.line_nr + 1}: FAIL: fn ${g.fn_decl.name}(): assert $s_assertion");') + g.writeln('builtin.exit(1);') + g.dec_indent() + g.writeln('}') +} + +fn (mut g JsGen) gen_assign_stmt(stmt ast.AssignStmt) { + if stmt.left.len > stmt.right.len { + // multi return + g.write('const [') + for i, left in stmt.left { + if !left.is_blank_ident() { + g.expr(left) + } + if i < stmt.left.len - 1 { + g.write(', ') + } + } + g.write('] = ') + g.expr(stmt.right[0]) + g.writeln(';') + } else { + // `a := 1` | `a,b := 1,2` + for i, left in stmt.left { + mut op := stmt.op + if stmt.op == .decl_assign { + op = .assign + } + val := stmt.right[i] + mut is_mut := false + if left is ast.Ident { + is_mut = left.is_mut + if left.kind == .blank_ident || left.name in ['', '_'] { + tmp_var := g.new_tmp_var() + // TODO: Can the tmp_var declaration be omitted? + g.write('const $tmp_var = ') + g.expr(val) + g.writeln(';') + continue + } + } + mut styp := g.typ(stmt.left_types[i]) + if !g.inside_loop && styp.len > 0 { + g.doc.gen_typ(styp) + } + if stmt.op == .decl_assign { + if g.inside_loop || is_mut { + g.write('let ') + } else { + g.write('const ') + } + } + g.expr(left) + mut is_ptr := false + if stmt.op == .assign && stmt.left_types[i].is_ptr() { + is_ptr = true + g.write('.val') + } + if g.inside_map_set && op == .assign { + g.inside_map_set = false + g.write(', ') + g.expr(val) + if is_ptr { + g.write('.val') + } + g.write(')') + } else { + g.write(' $op ') + // TODO: Multiple types?? + should_cast := + (g.table.type_kind(stmt.left_types.first()) in js.shallow_equatables) + && (g.cast_stack.len <= 0 || stmt.left_types.first() != g.cast_stack.last()) + + if should_cast { + g.cast_stack << stmt.left_types.first() + if g.file.mod.name == 'builtin' { + g.write('new ') + } + g.write('${g.typ(stmt.left_types.first())}(') + } + g.expr(val) + if is_ptr { + g.write('.val') + } + if should_cast { + g.write(')') + g.cast_stack.delete_last() + } + } + if g.inside_loop { + g.write('; ') + } else { + g.writeln(';') + } + } + } +} + +fn (mut g JsGen) gen_attrs(attrs []ast.Attr) { + for attr in attrs { + g.writeln('/* [$attr.name] */') + } +} + +fn (mut g JsGen) gen_block(it ast.Block) { + g.writeln('{') + g.stmts(it.stmts) + g.writeln('}') +} + +fn (mut g JsGen) gen_branch_stmt(it ast.BranchStmt) { + // continue or break + g.write(it.kind.str()) + g.writeln(';') +} + +fn (mut g JsGen) gen_const_decl(it ast.ConstDecl) { + for field in it.fields { + g.doc.gen_const(g.typ(field.typ)) + if field.is_pub { + g.push_pub_var(field.name) + } + g.write('const ${g.js_name(field.name)} = ') + g.expr(field.expr) + g.writeln(';') + } + g.writeln('') +} + +fn (mut g JsGen) gen_defer_stmts() { + g.writeln('(function defer() {') + for defer_stmt in g.defer_stmts { + g.stmts(defer_stmt.stmts) + } + g.defer_stmts = [] + g.writeln('})();') +} + +fn (mut g JsGen) gen_enum_decl(it ast.EnumDecl) { + g.doc.gen_enum() + g.writeln('const ${g.js_name(it.name)} = {') + g.inc_indent() + mut i := 0 + for field in it.fields { + g.write('$field.name: ') + if field.has_expr && field.expr is ast.IntegerLiteral { + i = field.expr.val.int() + } + g.writeln('$i,') + i++ + } + g.dec_indent() + g.writeln('};') + if it.is_pub { + g.push_pub_var(it.name) + } +} + +fn (mut g JsGen) gen_expr_stmt(it ast.ExprStmt) { + g.expr(it.expr) + if !it.is_expr && it.expr !is ast.IfExpr && !g.inside_ternary { + g.writeln(';') + } +} + +fn (mut g JsGen) gen_fn_decl(it ast.FnDecl) { + if it.no_body || it.is_method { + // Struct methods are handled by class generation code. + return + } + if g.inside_builtin { + g.builtin_fns << it.name + } + cur_fn_decl := g.fn_decl + g.gen_method_decl(it) + g.fn_decl = cur_fn_decl +} + +fn fn_has_go(node ast.FnDecl) bool { + mut has_go := false + for stmt in node.stmts { + if stmt is ast.ExprStmt { + if stmt.expr is ast.GoExpr { + has_go = true + break + } + } + } + return has_go +} + +fn (mut g JsGen) gen_method_decl(it ast.FnDecl) { + unsafe { + g.fn_decl = &it + } + has_go := fn_has_go(it) + is_main := it.name == 'main.main' + g.gen_attrs(it.attrs) + if is_main { + // there is no concept of main in JS but we do have iife + g.writeln('/* program entry point */') + + g.write('(') + if has_go { + g.write('async ') + } + g.write('function(') + } else if it.is_anon { + g.write('function (') + } else { + mut name := g.js_name(it.name) + c := name[0] + if c in [`+`, `-`, `*`, `/`] { + name = util.replace_op(name) + } + // type_name := g.typ(it.return_type) + // generate jsdoc for the function + g.doc.gen_fn(it) + if has_go { + g.write('async ') + } + if !it.is_method { + g.write('function ') + } else { + if it.attrs.contains('js_getter') { + g.write('get ') + } else if it.attrs.contains('js_setter') { + g.write('set ') + } + } + g.write('${name}(') + if it.is_pub && !it.is_method { + g.push_pub_var(name) + } + } + mut args := it.params + if it.is_method { + args = args[1..] + } + g.fn_args(args, it.is_variadic) + if it.is_method { + if args.len > 0 { + g.write(', ') + } + g.write('${it.params[0].name} = this') + } + g.writeln(') {') + for i, arg in args { + is_varg := i == args.len - 1 && it.is_variadic + if is_varg { + name := g.js_name(arg.name) + g.writeln('$name = new array($name);') + } + } + + g.stmts(it.stmts) + g.write('}') + if is_main { + g.write(')();') + } + if !it.is_anon && !it.is_method { + g.writeln('\n') + } + g.fn_decl = voidptr(0) +} + +fn (mut g JsGen) fn_args(args []ast.Param, is_variadic bool) { + for i, arg in args { + name := g.js_name(arg.name) + is_varg := i == args.len - 1 && is_variadic + if is_varg { + g.write('...$name') + } else { + g.write(name) + } + // if its not the last argument + if i < args.len - 1 { + g.write(', ') + } + } +} + +fn (mut g JsGen) gen_for_c_stmt(it ast.ForCStmt) { + g.inside_loop = true + g.write('for (') + if it.has_init { + g.stmt(it.init) + } else { + g.write('; ') + } + if it.has_cond { + g.write('+') // convert to number or boolean + g.expr(it.cond) + } + g.write('; ') + if it.has_inc { + g.stmt(it.inc) + } + g.writeln(') {') + g.stmts(it.stmts) + g.writeln('}') + g.inside_loop = false +} + +fn (mut g JsGen) gen_for_in_stmt(it ast.ForInStmt) { + if it.is_range { + // `for x in 1..10 {` + mut i := it.val_var + if i in ['', '_'] { + i = g.new_tmp_var() + } + g.inside_loop = true + g.write('for (let $i = ') + g.expr(it.cond) + g.write('; $i < ') + g.expr(it.high) + g.writeln('; $i = new builtin.int($i + 1)) {') + g.inside_loop = false + g.stmts(it.stmts) + g.writeln('}') + } else if it.kind in [.array, .string] || it.cond_type.has_flag(.variadic) { + // `for num in nums {` + val := if it.val_var in ['', '_'] { '_' } else { it.val_var } + // styp := g.typ(it.val_type) + if it.key_var.len > 0 { + g.write('for (const [$it.key_var, $val] of ') + if it.kind == .string { + g.write('Array.from(') + g.expr(it.cond) + if it.cond_type.is_ptr() { + g.write('.valueOf()') + } + g.write('.str.split(\'\').entries(), ([$it.key_var, $val]) => [$it.key_var, ') + if g.ns.name == 'builtin' { + g.write('new ') + } + g.write('byte($val)])') + } else { + g.expr(it.cond) + if it.cond_type.is_ptr() { + g.write('.valueOf()') + } + g.write('.entries()') + } + } else { + g.write('for (const $val of ') + g.expr(it.cond) + if it.cond_type.is_ptr() { + g.write('.valueOf()') + } + if it.kind == .string { + g.write(".str.split('')") + } + // cast characters to bytes + if val !in ['', '_'] && it.kind == .string { + g.write('.map(c => ') + if g.ns.name == 'builtin' { + g.write('new ') + } + g.write('byte(c))') + } + } + g.writeln(') {') + g.stmts(it.stmts) + g.writeln('}') + } else if it.kind == .map { + // `for key, val in map[string]int {` + // key_styp := g.typ(it.key_type) + // val_styp := g.typ(it.val_type) + key := if it.key_var in ['', '_'] { '' } else { it.key_var } + val := if it.val_var in ['', '_'] { '' } else { it.val_var } + g.write('for (let [$key, $val] of ') + g.expr(it.cond) + if it.cond_type.is_ptr() { + g.write('.valueOf()') + } + g.writeln(') {') + g.stmts(it.stmts) + g.writeln('}') + } +} + +fn (mut g JsGen) gen_for_stmt(it ast.ForStmt) { + g.write('while (') + if it.is_inf { + g.write('true') + } else { + g.write('+') // convert expr to number or boolean + g.expr(it.cond) + } + g.writeln(') {') + g.stmts(it.stmts) + g.writeln('}') +} + +fn (mut g JsGen) gen_go_expr(node ast.GoExpr) { + // TODO Handle joinable expressions + // node.is_expr + mut name := node.call_expr.name + if node.call_expr.is_method { + receiver_sym := g.table.get_type_symbol(node.call_expr.receiver_type) + name = receiver_sym.name + '.' + name + } + // todo: please add a name feild without the mod name for ast.CallExpr + if name.starts_with('${node.call_expr.mod}.') { + name = name[node.call_expr.mod.len + 1..] + } + g.writeln('await new Promise(function(resolve){') + g.inc_indent() + g.write('${name}(') + for i, arg in node.call_expr.args { + g.expr(arg.expr) + if i < node.call_expr.args.len - 1 { + g.write(', ') + } + } + g.writeln(');') + g.writeln('resolve();') + g.dec_indent() + g.writeln('});') +} + +fn (mut g JsGen) gen_import_stmt(it ast.Import) { + g.ns.imports[it.mod] = it.alias +} + +fn (mut g JsGen) gen_interface_decl(it ast.InterfaceDecl) { + // JS is dynamically typed, so we don't need any codegen at all + // We just need the JSDoc so TypeScript type checking works + g.doc.gen_interface(it) + // This is a hack to make the interface's type accessible outside its namespace + // TODO: interfaces are always `pub`? + name := g.js_name(it.name) + g.push_pub_var('/** @type $name */\n\t\t$name: undefined') +} + +fn (mut g JsGen) gen_return_stmt(it ast.Return) { + if it.exprs.len == 0 { + // Returns nothing + g.writeln('return;') + return + } + g.write('return ') + if it.exprs.len == 1 { + g.expr(it.exprs[0]) + } else { // Multi return + g.gen_array_init_values(it.exprs) + } + g.writeln(';') +} + +fn (mut g JsGen) gen_hash_stmt(it ast.HashStmt) { + g.writeln(it.val) +} + +fn (mut g JsGen) gen_struct_decl(node ast.StructDecl) { + mut name := node.name + if name.starts_with('JS.') { + return + } + if name in js.v_types && g.ns.name == 'builtin' { + return + } + js_name := g.js_name(name) + g.gen_attrs(node.attrs) + g.doc.gen_fac_fn(node.fields) + g.write('function ${js_name}({ ') + for i, field in node.fields { + g.write('$field.name = ') + if field.has_default_expr { + g.expr(field.default_expr) + } else { + g.write('${g.to_js_typ_val(field.typ)}') + } + if i < node.fields.len - 1 { + g.write(', ') + } + } + g.writeln(' }) {') + g.inc_indent() + for field in node.fields { + g.writeln('this.$field.name = $field.name') + } + g.dec_indent() + g.writeln('};') + g.writeln('${js_name}.prototype = {') + g.inc_indent() + for embed in node.embeds { + etyp := g.typ(embed.typ) + g.writeln('...${etyp}.prototype,') + } + fns := g.method_fn_decls[name] + for field in node.fields { + typ := g.typ(field.typ) + g.doc.gen_typ(typ) + g.write('$field.name: ${g.to_js_typ_val(field.typ)}') + g.writeln(',') + } + for cfn in fns { + g.gen_method_decl(cfn) + g.writeln(',') + } + // gen toString method + fn_names := fns.map(it.name) + if 'toString' !in fn_names { + g.writeln('toString() {') + g.inc_indent() + g.write('return `$js_name {') + for i, field in node.fields { + if i == 0 { + g.write(' ') + } else { + g.write(', ') + } + match g.typ(field.typ).split('.').last() { + 'string' { g.write('$field.name: "\${this["$field.name"].toString()}"') } + else { g.write('$field.name: \${this["$field.name"].toString()} ') } + } + } + g.writeln('}`') + g.dec_indent() + g.writeln('},') + } + g.writeln('\$toJS() { return this; }') + g.dec_indent() + g.writeln('};\n') + if node.is_pub { + g.push_pub_var(name) + } +} + +fn (mut g JsGen) gen_array_init_expr(it ast.ArrayInit) { + // NB: Fixed arrays and regular arrays are handled the same, since fixed arrays: + // 1) Are only available for number types + // 2) Give the code unnecessary complexity + // 3) Have several limitations like missing most `Array.prototype` methods + // 4) Modern engines can optimize regular arrays into typed arrays anyways, + // offering similar performance + g.write('new array(') + g.inc_indent() + + if it.has_len { + t1 := g.new_tmp_var() + t2 := g.new_tmp_var() + g.writeln('(function() {') + g.inc_indent() + g.writeln('const $t1 = [];') + g.write('for (let $t2 = 0; $t2 < ') + g.expr(it.len_expr) + g.writeln('; $t2++) {') + g.inc_indent() + g.write('${t1}.push(') + if it.has_default { + g.expr(it.default_expr) + } else { + // Fill the array with the default values for its type + t := g.to_js_typ_val(it.elem_type) + g.write(t) + } + g.writeln(');') + g.dec_indent() + g.writeln('};') + g.writeln('return $t1;') + g.dec_indent() + g.write('})()') + } else if it.is_fixed && it.exprs.len == 1 { + // [100]byte codegen + t1 := g.new_tmp_var() + t2 := g.new_tmp_var() + g.writeln('(function() {') + g.inc_indent() + g.writeln('const $t1 = [];') + g.write('for (let $t2 = 0; $t2 < ') + g.expr(it.exprs[0]) + g.writeln('; $t2++) {') + g.inc_indent() + g.write('${t1}.push(') + if it.has_default { + g.expr(it.default_expr) + } else { + // Fill the array with the default values for its type + t := g.to_js_typ_val(it.elem_type) + g.write(t) + } + g.writeln(');') + g.dec_indent() + g.writeln('};') + g.writeln('return $t1;') + g.dec_indent() + g.write('})()') + } else { + g.gen_array_init_values(it.exprs) + } + g.dec_indent() + g.write(')') +} + +fn (mut g JsGen) gen_array_init_values(exprs []ast.Expr) { + g.write('[') + for i, expr in exprs { + g.expr(expr) + if i < exprs.len - 1 { + g.write(', ') + } + } + g.write(']') +} + +fn (mut g JsGen) gen_call_expr(it ast.CallExpr) { + g.call_stack << it + mut name := g.js_name(it.name) + call_return_is_optional := it.return_type.has_flag(.optional) + if call_return_is_optional { + g.writeln('(function(){') + g.inc_indent() + g.writeln('try {') + g.inc_indent() + g.write('return builtin.unwrap(') + } + g.expr(it.left) + + if it.is_method { // foo.bar.baz() + sym := g.table.get_type_symbol(it.receiver_type) + g.write('.') + if sym.kind == .array && it.name in ['map', 'filter'] { + // Prevent 'it' from getting shadowed inside the match + node := it + g.write(it.name) + g.write('(') + expr := node.args[0].expr + match expr { + ast.AnonFn { + g.gen_fn_decl(expr.decl) + g.write(')') + return + } + ast.Ident { + if expr.kind == .function { + g.write(g.js_name(expr.name)) + g.write(')') + return + } else if expr.kind == .variable { + v_sym := g.table.get_type_symbol(expr.var_info().typ) + if v_sym.kind == .function { + g.write(g.js_name(expr.name)) + g.write(')') + return + } + } + } + else {} + } + + g.write('it => ') + g.expr(node.args[0].expr) + g.write(')') + return + } + + left_sym := g.table.get_type_symbol(it.left_type) + if left_sym.kind == .array { + node := it + match node.name { + 'insert' { + arg2_sym := g.table.get_type_symbol(node.args[1].typ) + is_arg2_array := arg2_sym.kind == .array && node.args[1].typ == node.left_type + if is_arg2_array { + g.write('insert_many(') + } else { + g.write('insert(') + } + + g.expr(node.args[0].expr) + g.write(',') + if is_arg2_array { + g.expr(node.args[1].expr) + g.write('.arr,') + g.expr(node.args[1].expr) + g.write('.len') + } else { + g.expr(node.args[1].expr) + } + g.write(')') + return + } + 'prepend' { + arg_sym := g.table.get_type_symbol(node.args[0].typ) + is_arg_array := arg_sym.kind == .array && node.args[0].typ == node.left_type + if is_arg_array { + g.write('prepend_many(') + } else { + g.write('prepend(') + } + + if is_arg_array { + g.expr(node.args[0].expr) + g.write('.arr, ') + g.expr(node.args[0].expr) + g.write('.len') + } else { + g.expr(node.args[0].expr) + } + g.write(')') + return + } + else {} + } + } + } else { + if name in g.builtin_fns { + g.write('builtin.') + } + } + g.write('${name}(') + for i, arg in it.args { + g.expr(arg.expr) + if i != it.args.len - 1 { + g.write(', ') + } + } + // end method call + g.write(')') + if call_return_is_optional { + // end unwrap + g.writeln(')') + g.dec_indent() + // begin catch block + g.writeln('} catch(err) {') + g.inc_indent() + // gen or block contents + match it.or_block.kind { + .block { + if it.or_block.stmts.len > 1 { + g.stmts(it.or_block.stmts[..it.or_block.stmts.len - 1]) + } + g.write('return ') + g.stmt(it.or_block.stmts.last()) + } + .propagate { + panicstr := '`optional not set (\${err})`' + if g.file.mod.name == 'main' && g.fn_decl.name == 'main.main' { + g.writeln('return builtin.panic($panicstr)') + } else { + g.writeln('builtin.js_throw(err)') + } + } + else {} + } + // end catch + g.dec_indent() + g.writeln('}') + // end anon fn + g.dec_indent() + g.write('})()') + } + g.call_stack.delete_last() +} + +fn (mut g JsGen) gen_ident(node ast.Ident) { + mut name := g.js_name(node.name) + if node.kind == .blank_ident || name in ['', '_'] { + name = g.new_tmp_var() + } + // TODO `is` + // TODO handle optionals + g.write(name) + + // TODO: Generate .val for basic types +} + +fn (mut g JsGen) gen_lock_expr(node ast.LockExpr) { + // TODO: implement this +} + +fn (mut g JsGen) gen_if_expr(node ast.IfExpr) { + if node.is_comptime { + g.comp_if(node) + return + } + type_sym := g.table.get_type_symbol(node.typ) + // one line ?: + if node.is_expr && node.branches.len >= 2 && node.has_else && type_sym.kind != .void { + // `x := if a > b { } else if { } else { }` + g.write('(') + g.inside_ternary = true + for i, branch in node.branches { + if i > 0 { + g.write(' : ') + } + if i < node.branches.len - 1 || !node.has_else { + g.write('(') + g.expr(branch.cond) + g.write(')') + g.write('.valueOf()') + g.write(' ? ') + } + g.stmts(branch.stmts) + } + g.inside_ternary = false + g.write(')') + } else { + // mut is_guard = false + for i, branch in node.branches { + if i == 0 { + match branch.cond { + ast.IfGuardExpr { + // TODO optionals + } + else { + g.write('if (') + if '$branch.cond' == 'js' { + g.write('true') + } else { + g.write('(') + g.expr(branch.cond) + g.write(')') + g.write('.valueOf()') + } + g.writeln(') {') + } + } + } else if i < node.branches.len - 1 || !node.has_else { + g.write('} else if (') + g.write('(') + g.expr(branch.cond) + g.write(')') + g.write('.valueOf()') + g.writeln(') {') + } else if i == node.branches.len - 1 && node.has_else { + /* + if is_guard { + //g.writeln('} if (!$guard_ok) { /* else */') + } else { + */ + g.writeln('} else {') + // } + } + g.stmts(branch.stmts) + } + /* + if is_guard { + g.write('}') + } + */ + g.writeln('}') + g.writeln('') + } +} + +fn (mut g JsGen) gen_index_expr(expr ast.IndexExpr) { + left_typ := g.table.get_type_symbol(expr.left_type) + // TODO: Handle splice setting if it's implemented + if expr.index is ast.RangeExpr { + g.expr(expr.left) + if expr.left_type.is_ptr() { + g.write('.valueOf()') + } + g.write('.slice(') + if expr.index.has_low { + g.expr(expr.index.low) + } else { + g.write('0') + } + g.write(', ') + if expr.index.has_high { + g.expr(expr.index.high) + } else { + g.expr(expr.left) + if expr.left_type.is_ptr() { + g.write('.valueOf()') + } + g.write('.length') + } + g.write(')') + } else if left_typ.kind == .map { + g.expr(expr.left) + if expr.is_setter { + g.inside_map_set = true + g.write('.map.set(') + } else { + g.write('.map.get(') + } + g.expr(expr.index) + g.write('.toString()') + if !expr.is_setter { + g.write(')') + } + } else if left_typ.kind == .string { + if expr.is_setter { + // TODO: What's the best way to do this? + // 'string'[3] = `o` + } else { + // TODO: Maybe use u16 there? JS String returns values up to 2^16-1 + g.write('new byte(') + g.expr(expr.left) + if expr.left_type.is_ptr() { + g.write('.valueOf()') + } + g.write('.str.charCodeAt(') + g.expr(expr.index) + g.write('))') + } + } else { + // TODO Does this cover all cases? + g.expr(expr.left) + if expr.left_type.is_ptr() { + g.write('.valueOf()') + } + g.write('.arr') + g.write('[+') + g.cast_stack << ast.int_type_idx + g.expr(expr.index) + g.cast_stack.delete_last() + g.write(']') + } +} + +fn (mut g JsGen) gen_infix_expr(it ast.InfixExpr) { + l_sym := g.table.get_type_symbol(it.left_type) + r_sym := g.table.get_type_symbol(it.right_type) + + is_not := it.op in [.not_in, .not_is, .ne] + if is_not { + g.write('!(') + } + + if it.op == .eq || it.op == .ne { + // Shallow equatables + if l_sym.kind in js.shallow_equatables && r_sym.kind in js.shallow_equatables { + // wrap left expr in parens so binary operations will work correctly. + g.write('(') + g.expr(it.left) + g.write(')') + g.write('.eq(') + g.cast_stack << int(l_sym.kind) + g.expr(it.right) + g.cast_stack.delete_last() + g.write(')') + } else { + g.write('vEq(') + g.expr(it.left) + g.write(', ') + g.expr(it.right) + g.write(')') + } + } else if l_sym.kind == .array && it.op == .left_shift { // arr << 1 + g.write('Array.prototype.push.call(') + g.expr(it.left) + g.write('.arr,') + array_info := l_sym.info as ast.Array + // arr << [1, 2] + if r_sym.kind == .array && array_info.elem_type != it.right_type { + g.write('...') + } + g.expr(it.right) + g.write(')') + } else if r_sym.kind in [.array, .map, .string] && it.op in [.key_in, .not_in] { + g.expr(it.right) + if r_sym.kind == .map { + g.write('.map.has(') + } else if r_sym.kind == .string { + g.write('.str.includes(') + } else { + g.write('.\$includes(') + } + g.expr(it.left) + if l_sym.kind == .string { + g.write('.str') + } + g.write(')') + } else if it.op in [.key_is, .not_is] { // foo is Foo + g.expr(it.left) + g.write(' instanceof ') + g.write(g.typ(it.right_type)) + } else { + is_arithmetic := it.op in [token.Kind.plus, .minus, .mul, .div, .mod] + mut needs_cast := is_arithmetic && it.left_type != it.right_type + mut greater_typ := 0 + // todo(playX): looks like this cast is always required to perform .eq operation on types. + if true || needs_cast { + greater_typ = g.greater_typ(it.left_type, it.right_type) + if g.cast_stack.len > 0 { + needs_cast = g.cast_stack.last() != greater_typ + } + } + + if true || needs_cast { + if g.ns.name == 'builtin' { + g.write('new ') + } + g.write('${g.typ(greater_typ)}(') + g.cast_stack << greater_typ + } + g.expr(it.left) + g.write(' $it.op ') + g.expr(it.right) + + if true || needs_cast { + g.cast_stack.delete_last() + g.write(')') + } + } + + if is_not { + g.write(')') + } +} + +fn (mut g JsGen) greater_typ(left ast.Type, right ast.Type) ast.Type { + l := int(left) + r := int(right) + lr := [l, r] + if ast.string_type_idx in lr { + return ast.Type(ast.string_type_idx) + } + should_float := (l in ast.integer_type_idxs && r in ast.float_type_idxs) + || (r in ast.integer_type_idxs && l in ast.float_type_idxs) + if should_float { + if ast.f64_type_idx in lr { + return ast.Type(ast.f64_type_idx) + } + if ast.f32_type_idx in lr { + return ast.Type(ast.f32_type_idx) + } + return ast.Type(ast.float_literal_type) + } + should_int := (l in ast.integer_type_idxs && r in ast.integer_type_idxs) + if should_int { + // cant add to u64 - if (ast.u64_type_idx in lr) { return ast.Type(ast.u64_type_idx) } + // just guessing this order + if ast.i64_type_idx in lr { + return ast.Type(ast.i64_type_idx) + } + if ast.u32_type_idx in lr { + return ast.Type(ast.u32_type_idx) + } + if ast.int_type_idx in lr { + return ast.Type(ast.int_type_idx) + } + if ast.u16_type_idx in lr { + return ast.Type(ast.u16_type_idx) + } + if ast.i16_type_idx in lr { + return ast.Type(ast.i16_type_idx) + } + if ast.byte_type_idx in lr { + return ast.Type(ast.byte_type_idx) + } + if ast.i8_type_idx in lr { + return ast.Type(ast.i8_type_idx) + } + return ast.Type(ast.int_literal_type_idx) + } + return ast.Type(l) +} + +fn (mut g JsGen) gen_map_init_expr(it ast.MapInit) { + // key_typ_sym := g.table.get_type_symbol(it.key_type) + // value_typ_sym := g.table.get_type_symbol(it.value_type) + // key_typ_str := util.no_dots(key_typ_sym.name) + // value_typ_str := util.no_dots(value_typ_sym.name) + g.writeln('new map(') + g.inc_indent() + if it.vals.len > 0 { + g.writeln('new Map([') + g.inc_indent() + for i, key in it.keys { + val := it.vals[i] + g.write('[') + g.expr(key) + g.write(', ') + g.expr(val) + g.write(']') + if i < it.keys.len - 1 { + g.write(',') + } + g.writeln('') + } + g.dec_indent() + g.write('])') + } else { + g.write('new Map()') + } + g.dec_indent() + g.write(')') +} + +fn (mut g JsGen) gen_selector_expr(it ast.SelectorExpr) { + g.expr(it.expr) + if it.expr_type.is_ptr() { + g.write('.valueOf()') + } + g.write('.$it.field_name') +} + +fn (mut g JsGen) gen_string_inter_literal(it ast.StringInterLiteral) { + should_cast := !(g.cast_stack.len > 0 && g.cast_stack.last() == ast.string_type_idx) + if should_cast { + if g.file.mod.name == 'builtin' { + g.write('new ') + } + g.write('string(') + } + g.write('`') + for i, val in it.vals { + escaped_val := val.replace('`', '\\`') + g.write(escaped_val) + if i >= it.exprs.len { + continue + } + expr := it.exprs[i] + fmt := it.fmts[i] + fwidth := it.fwidths[i] + precision := it.precisions[i] + g.write('\${') + if fmt != `_` || fwidth != 0 || precision != 987698 { + // TODO: Handle formatting + g.expr(expr) + } else { + sym := g.table.get_type_symbol(it.expr_types[i]) + g.expr(expr) + if sym.kind == .struct_ && sym.has_method('str') { + g.write('.str()') + } + } + g.write('}') + } + g.write('`') + if should_cast { + g.write(')') + } +} + +fn (mut g JsGen) gen_string_literal(it ast.StringLiteral) { + text := it.val.replace("'", "\\'") + should_cast := !(g.cast_stack.len > 0 && g.cast_stack.last() == ast.string_type_idx) + if true || should_cast { + if g.file.mod.name == 'builtin' { + g.write('new ') + } + g.write('string(') + } + g.write("'$text'") + if true || should_cast { + g.write(')') + } +} + +fn (mut g JsGen) gen_struct_init(it ast.StructInit) { + type_sym := g.table.get_type_symbol(it.typ) + name := type_sym.name + if it.fields.len == 0 { + g.write('new ${g.js_name(name)}({})') + } else { + g.writeln('new ${g.js_name(name)}({') + g.inc_indent() + for i, field in it.fields { + g.write('$field.name: ') + g.expr(field.expr) + if i < it.fields.len - 1 { + g.write(',') + } + g.writeln('') + } + g.dec_indent() + g.write('})') + } +} + +fn (mut g JsGen) gen_typeof_expr(it ast.TypeOf) { + sym := g.table.get_type_symbol(it.expr_type) + if sym.kind == .sum_type { + // TODO: JS sumtypes not implemented yet + } else if sym.kind == .array_fixed { + fixed_info := sym.info as ast.ArrayFixed + typ_name := g.table.get_type_name(fixed_info.elem_type) + g.write('"[$fixed_info.size]$typ_name"') + } else if sym.kind == .function { + info := sym.info as ast.FnType + fn_info := info.func + mut repr := 'fn (' + for i, arg in fn_info.params { + if i > 0 { + repr += ', ' + } + repr += g.table.get_type_name(arg.typ) + } + repr += ')' + if fn_info.return_type != ast.void_type { + repr += ' ${g.table.get_type_name(fn_info.return_type)}' + } + g.write('"$repr"') + } else { + g.write('"$sym.name"') + } +} + +fn (mut g JsGen) gen_type_cast_expr(it ast.CastExpr) { + is_literal := ((it.expr is ast.IntegerLiteral && it.typ in ast.integer_type_idxs) + || (it.expr is ast.FloatLiteral && it.typ in ast.float_type_idxs)) + // Skip cast if type is the same as the parrent caster + if g.cast_stack.len > 0 && is_literal { + if it.typ == g.cast_stack[g.cast_stack.len - 1] { + return + } + } + g.cast_stack << it.typ + typ := g.typ(it.typ) + if !is_literal { + if typ !in js.v_types || g.ns.name == 'builtin' { + g.write('new ') + } + g.write('${typ}(') + } + g.expr(it.expr) + if typ == 'string' && it.expr !is ast.StringLiteral { + g.write('.toString()') + } + if !is_literal { + g.write(')') + } + g.cast_stack.delete_last() +} + +fn (mut g JsGen) gen_integer_literal_expr(it ast.IntegerLiteral) { + typ := ast.Type(ast.int_type) + + // Don't wrap integers for use in JS.foo functions. + // TODO: call.language always seems to be "v", parser bug? + if g.call_stack.len > 0 { + call := g.call_stack[g.call_stack.len - 1] + if call.language == .js { + for t in call.args { + if t.expr is ast.IntegerLiteral { + if t.expr == it { + g.write(it.val) + return + } + } + } + } + } + + // Skip cast if type is the same as the parrent caster + if g.cast_stack.len > 0 { + if g.cast_stack[g.cast_stack.len - 1] in ast.integer_type_idxs { + g.write('new int($it.val)') + return + } + } + + if g.ns.name == 'builtin' { + g.write('new ') + } + + g.write('${g.typ(typ)}($it.val)') +} + +fn (mut g JsGen) gen_float_literal_expr(it ast.FloatLiteral) { + typ := ast.Type(ast.f32_type) + + // Don't wrap integers for use in JS.foo functions. + // TODO: call.language always seems to be "v", parser bug? + if g.call_stack.len > 0 { + call := g.call_stack[g.call_stack.len - 1] + // if call.language == .js { + for i, t in call.args { + if t.expr is ast.FloatLiteral { + if t.expr == it { + if call.expected_arg_types[i] in ast.integer_type_idxs { + g.write(int(it.val.f64()).str()) + } else { + g.write(it.val) + } + return + } + } + } + //} + } + + // Skip cast if type is the same as the parrent caster + if g.cast_stack.len > 0 { + if g.cast_stack[g.cast_stack.len - 1] in ast.float_type_idxs { + g.write('new f32($it.val)') + return + } else if g.cast_stack[g.cast_stack.len - 1] in ast.integer_type_idxs { + g.write(int(it.val.f64()).str()) + return + } + } + + if g.ns.name == 'builtin' { + g.write('new ') + } + + g.write('${g.typ(typ)}($it.val)') +} + +fn (mut g JsGen) unwrap_generic(typ ast.Type) ast.Type { + if typ.has_flag(.generic) { + if t_typ := g.table.resolve_generic_to_concrete(typ, g.table.cur_fn.generic_names, + g.table.cur_concrete_types) + { + return t_typ + } + } + return typ +} diff --git a/v_windows/v/old/vlib/v/gen/js/jsdoc.v b/v_windows/v/old/vlib/v/gen/js/jsdoc.v new file mode 100644 index 0000000..359687e --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/jsdoc.v @@ -0,0 +1,96 @@ +module js + +import v.ast + +struct JsDoc { +mut: + gen &JsGen +} + +fn new_jsdoc(gen &JsGen) &JsDoc { + return &JsDoc{ + gen: gen + } +} + +fn (mut d JsDoc) write(s string) { + if !d.gen.enable_doc { + return + } + d.gen.write(s) +} + +fn (mut d JsDoc) writeln(s string) { + if !d.gen.enable_doc { + return + } + d.gen.writeln(s) +} + +fn (mut d JsDoc) gen_typ(typ string) { + d.writeln('/** @type {$typ} */') +} + +fn (mut d JsDoc) gen_const(typ string) { + d.writeln('/** @constant {$typ} */') +} + +fn (mut d JsDoc) gen_enum() { + // Enum values can only be ints for now + typ := 'number' + d.writeln('/** @enum {$typ} */') +} + +fn (mut d JsDoc) gen_fac_fn(fields []ast.StructField) { + d.writeln('/**') + d.writeln(' * @constructor') + d.write(' * @param {{') + for i, field in fields { + // Marked as optional: structs have default default values, + // so all struct members don't have to be initialized. + d.write('$field.name?: ${d.gen.typ(field.typ)}') + if i < fields.len - 1 { + d.write(', ') + } + } + d.writeln('}} init') + d.writeln('*/') +} + +fn (mut d JsDoc) gen_fn(it ast.FnDecl) { + type_name := d.gen.typ(it.return_type) + d.writeln('/**') + d.writeln(' * @function') + if it.is_deprecated { + d.writeln(' * @deprecated') + } + for i, arg in it.params { + if (it.is_method || it.receiver.typ == 0) && i == 0 { + continue + } + arg_type_name := d.gen.typ(arg.typ) + is_varg := i == it.params.len - 1 && it.is_variadic + name := d.gen.js_name(arg.name) + if is_varg { + d.writeln(' * @param {...$arg_type_name} $name') + } else { + d.writeln(' * @param {$arg_type_name} $name') + } + } + d.writeln(' * @returns {$type_name}') + d.writeln('*/') +} + +fn (mut d JsDoc) gen_interface(it ast.InterfaceDecl) { + name := d.gen.js_name(it.name) + d.writeln('/**') + d.writeln(' * @interface $name') + d.writeln(' * @typedef $name') + for method in it.methods { + // Skip receiver + typ := d.gen.fn_typ(method.params[1..], method.return_type) + method_name := d.gen.js_name(method.name) + d.writeln(' * @property {$typ} $method_name') + } + d.writeln(' */\n') +} diff --git a/v_windows/v/old/vlib/v/gen/js/jsgen_test.v b/v_windows/v/old/vlib/v/gen/js/jsgen_test.v new file mode 100644 index 0000000..06316e8 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/jsgen_test.v @@ -0,0 +1,86 @@ +import os + +const ( + test_dir = os.join_path('vlib', 'v', 'gen', 'js', 'tests') + output_dir = os.join_path(os.temp_dir(), '_js_tests/') + v_options = '-b js -w' + node_options = '' +) + +fn testsuite_end() { + os.rmdir_all(output_dir) or {} +} + +const there_is_node_available = is_nodejs_working() + +const there_is_grep_available = is_grep_working() + +fn test_example_compilation() { + vexe := os.getenv('VEXE') + os.chdir(os.dir(vexe)) + os.mkdir_all(output_dir) or { panic(err) } + files := find_test_files() + for file in files { + path := os.join_path(test_dir, file) + println('Testing $file') + mut v_options_file := v_options + mut node_options_file := node_options + should_create_source_map := file.ends_with('_sourcemap.v') + if should_create_source_map { + println('activate -sourcemap creation') + v_options_file += ' -sourcemap' // activate souremap generation + + println('add node option: --enable-source-maps') // requieres node >=12.12.0 + node_options_file += ' --enable-source-maps' // activate souremap generation + } + v_code := os.system('$vexe $v_options_file -o $output_dir${file}.js $path') + if v_code != 0 { + assert false + } + // Compilation failed + assert v_code == 0 + if !there_is_node_available { + println(' ... skipping running $file, there is no NodeJS present') + continue + } + js_code := os.system('node $output_dir${file}.js') + if js_code != 0 { + assert false + } + // Running failed + assert js_code == 0 + if should_create_source_map { + if there_is_grep_available { + grep_code_sourcemap_found := os.system('grep -q -E "//#\\ssourceMappingURL=data:application/json;base64,[-A-Za-z0-9+/=]+$" $output_dir${file}.js') + assert grep_code_sourcemap_found == 0 + println('file has a source map embeded') + } else { + println(' ... skipping testing for sourcemap $file, there is no grep present') + } + } + } +} + +fn find_test_files() []string { + files := os.ls(test_dir) or { panic(err) } + // The life example never exits, so tests would hang with it, skip + mut tests := files.filter(it.ends_with('.v')).filter(it != 'life.v') + tests.sort() + return tests +} + +fn is_nodejs_working() bool { + node_res := os.execute('node --version') + if node_res.exit_code != 0 { + return false + } + return true +} + +fn is_grep_working() bool { + node_res := os.execute('grep --version') + if node_res.exit_code != 0 { + return false + } + return true +} diff --git a/v_windows/v/old/vlib/v/gen/js/program_test.v b/v_windows/v/old/vlib/v/gen/js/program_test.v new file mode 100644 index 0000000..8ba06ed --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/program_test.v @@ -0,0 +1,98 @@ +import os +import term +import rand +import v.util.vtest +import v.util.diff + +const vexe = @VEXE + +const vroot = @VMODROOT + +const diff_cmd = find_diff_cmd() + +fn find_diff_cmd() string { + return diff.find_working_diff_command() or { '' } +} + +[noreturn] +fn exit_because(msg string) { + eprintln('$msg, tests will not run') + exit(0) +} + +fn test_node_exists() { + res := os.execute('node --version') + if res.exit_code != 0 { + exit_because('node does not exist') + } + if !res.output.starts_with('v') { + exit_because('invalid node version') + } + version := res.output.trim_left('v').int() + if version < 10 { + exit_because('node should be at least version 10, but is currently version: $version') + } + println('Using node version: $version') +} + +fn test_running_programs_compiled_with_the_js_backend() ? { + os.setenv('VCOLORS', 'never', true) + os.chdir(vroot) + test_dir := 'vlib/v/gen/js/tests/testdata' + main_files := get_main_files_in_dir(test_dir) + fails := check_path(test_dir, main_files) ? + assert fails == 0 +} + +fn get_main_files_in_dir(dir string) []string { + mut mfiles := os.walk_ext(dir, '.v') + mfiles.sort() + return mfiles +} + +fn check_path(dir string, tests []string) ?int { + mut nb_fail := 0 + paths := vtest.filter_vtest_only(tests, basepath: vroot) + for path in paths { + program := path.replace(vroot + os.path_separator, '') + program_out := program.replace('.v', '.out') + if !os.exists(program_out) { + os.write_file(program_out, '') ? + } + print(program + ' ') + res := os.execute('$vexe -b js_node run $program') + if res.exit_code < 0 { + panic(res.output) + } + mut expected := os.read_file(program_out) ? + expected = clean_line_endings(expected) + found := clean_line_endings(res.output) + if expected != found { + println(term.red('FAIL')) + println('============') + println('expected $program_out content:') + println(expected) + println('============') + println('found:') + println(found) + println('============\n') + println('diff:') + println(diff.color_compare_strings(diff_cmd, rand.ulid(), found, expected)) + println('============\n') + nb_fail++ + } else { + println(term.green('OK')) + assert true + } + } + return nb_fail +} + +fn clean_line_endings(s string) string { + mut res := s.trim_space() + res = res.replace(' \n', '\n') + res = res.replace(' \r\n', '\n') + res = res.replace('\r\n', '\n') + res = res.trim('\n') + return res +} diff --git a/v_windows/v/old/vlib/v/gen/js/sourcemap/basic_test.v b/v_windows/v/old/vlib/v/gen/js/sourcemap/basic_test.v new file mode 100644 index 0000000..fc61b55 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/sourcemap/basic_test.v @@ -0,0 +1,158 @@ +module sourcemap + +fn test_simple() { + mut sg := generate_empty_map() + mut sm := sg.add_map('hello.js', '/', true, 0, 0) + sm.set_source_content('hello.v', "fn main(){nprintln('Hello World! Helo \$a')\n}") + + mlist := [ + MappingInput{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + name: '' + source_position: SourcePosition{ + source_line: 1 + source_column: 0 + } + }, + MappingInput{ + GenPosition: GenPosition{ + gen_line: 2 + gen_column: 0 + } + name: '' + source_position: SourcePosition{ + source_line: 1 + source_column: 0 + } + }, + MappingInput{ + GenPosition: GenPosition{ + gen_line: 2 + gen_column: 2 + } + name: '' + source_position: SourcePosition{ + source_line: 1 + source_column: 0 + } + }, + MappingInput{ + GenPosition: GenPosition{ + gen_line: 2 + gen_column: 9 + } + name: '' + source_position: SourcePosition{ + source_line: 1 + source_column: 7 + } + }, + MappingInput{ + GenPosition: GenPosition{ + gen_line: 2 + gen_column: 10 + } + name: 'hello_name' + source_position: SourcePosition{ + source_line: 1 + source_column: 8 + } + }, + MappingInput{ + GenPosition: GenPosition{ + gen_line: 2 + gen_column: 13 + } + name: '' + source_position: SourcePosition{ + source_line: 1 + source_column: 0 + } + }, + MappingInput{ + GenPosition: GenPosition{ + gen_line: 2 + gen_column: 14 + } + name: '' + source_position: SourcePosition{ + source_line: 1 + source_column: 12 + } + }, + MappingInput{ + GenPosition: GenPosition{ + gen_line: 2 + gen_column: 27 + } + name: '' + source_position: SourcePosition{ + source_line: 1 + source_column: 0 + } + }, + MappingInput{ + GenPosition: GenPosition{ + gen_line: 2 + gen_column: 28 + } + name: '' + source_position: SourcePosition{ + source_line: 1 + source_column: 0 + } + }, + MappingInput{ + GenPosition: GenPosition{ + gen_line: 2 + gen_column: 29 + } + name: '' + source_position: SourcePosition{ + source_line: 1 + source_column: 0 + } + }, + MappingInput{ + GenPosition: GenPosition{ + gen_line: 3 + gen_column: 0 + } + name: '' + source_position: SourcePosition{ + source_line: 1 + source_column: 0 + } + }, + ] + sm.add_mapping_list('hello.v', mlist) or { panic('x') } + + json_data := sm.to_json() + + expected := '{"version":3,"file":"hello.js","sourceRoot":"\\/","sources":["hello.v"],"sourcesContent":["fn main(){nprintln(\'Hello World! Helo \$a\')\\n}"],"names":["hello_name"],"mappings":"AAAA;AAAA,EAAA,OAAO,CAACA,GAAR,CAAY,aAAZ,CAAA,CAAA;AAAA"}' + assert json_data.str() == expected +} + +fn test_source_null() { + mut sg := generate_empty_map() + mut sm := sg.add_map('hello.js', '/', true, 0, 0) + sm.add_mapping('hello.v', SourcePosition{ + source_line: 0 + source_column: 0 + }, 1, 1, '') + sm.add_mapping('hello_lib1.v', SourcePosition{ + source_line: 0 + source_column: 0 + }, 2, 1, '') + sm.add_mapping('hello_lib2.v', SourcePosition{ + source_line: 0 + source_column: 0 + }, 3, 1, '') + json_data := sm.to_json() + + expected := '{"version":3,"file":"hello.js","sourceRoot":"\\/","sources":["hello.v","hello_lib1.v","hello_lib2.v"],"sourcesContent":[null,null,null],"names":[],"mappings":"CA+\\/\\/\\/\\/\\/HA;CCAA;CCAA"}' + assert json_data.str() == expected +} diff --git a/v_windows/v/old/vlib/v/gen/js/sourcemap/compare_test.v b/v_windows/v/old/vlib/v/gen/js/sourcemap/compare_test.v new file mode 100644 index 0000000..0a755be --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/sourcemap/compare_test.v @@ -0,0 +1,322 @@ +module sourcemap + +fn test_cmp_eq() { + a := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 2 + names_ind: IndexNumber(3) + source_position: SourcePosition{ + source_line: 4 + source_column: 5 + } + } + + b := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 2 + names_ind: IndexNumber(3) + source_position: SourcePosition{ + source_line: 4 + source_column: 5 + } + } + + assert !compare_by_generated_positions_inflated(a, b) +} + +fn test_cmp_name() { + a := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 2 + names_ind: IndexNumber(3) + source_position: SourcePosition{ + source_line: 4 + source_column: 5 + } + } + + b := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 2 + names_ind: IndexNumber(4) + source_position: SourcePosition{ + source_line: 4 + source_column: 5 + } + } + + assert compare_by_generated_positions_inflated(a, b) +} + +fn test_cmp_name_empty() { + a := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 2 + names_ind: IndexNumber(3) + source_position: SourcePosition{ + source_line: 4 + source_column: 5 + } + } + + b := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 2 + names_ind: Empty{} + source_position: SourcePosition{ + source_line: 4 + source_column: 5 + } + } + + assert compare_by_generated_positions_inflated(a, b) +} + +fn test_cmp_name_empty_empty() { + a := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 2 + names_ind: Empty{} + source_position: SourcePosition{ + source_line: 4 + source_column: 5 + } + } + + b := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 2 + names_ind: Empty{} + source_position: SourcePosition{ + source_line: 4 + source_column: 5 + } + } + + assert !compare_by_generated_positions_inflated(a, b) +} + +fn test_cmp_source_position_empty_eq() { + a := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 2 + names_ind: Empty{} + source_position: Empty{} + } + + b := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 2 + names_ind: Empty{} + source_position: Empty{} + } + + assert !compare_by_generated_positions_inflated(a, b) +} + +fn test_cmp_source_position_empty_diff() { + a := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 2 + names_ind: Empty{} + source_position: SourcePosition{ + source_line: 4 + source_column: 5 + } + } + + b := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 2 + names_ind: Empty{} + source_position: Empty{} + } + + assert compare_by_generated_positions_inflated(a, b) +} + +fn test_cmp_source_position_column_diff() { + a := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 2 + names_ind: Empty{} + source_position: SourcePosition{ + source_line: 4 + source_column: 5 + } + } + + b := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 2 + names_ind: Empty{} + source_position: SourcePosition{ + source_line: 4 + source_column: 99 + } + } + + assert compare_by_generated_positions_inflated(a, b) +} + +fn test_cmp_source_position_line_diff() { + a := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 2 + names_ind: Empty{} + source_position: SourcePosition{ + source_line: 4 + source_column: 5 + } + } + + b := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 2 + names_ind: Empty{} + source_position: SourcePosition{ + source_line: 88 + source_column: 99 + } + } + + assert compare_by_generated_positions_inflated(a, b) +} + +fn test_cmp_sources() { + a := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 2 + names_ind: Empty{} + source_position: SourcePosition{ + source_line: 4 + source_column: 5 + } + } + + b := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 99 + names_ind: Empty{} + source_position: SourcePosition{ + source_line: 4 + source_column: 5 + } + } + + assert compare_by_generated_positions_inflated(a, b) +} + +fn test_cmp_gen_column() { + a := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 2 + names_ind: Empty{} + source_position: SourcePosition{ + source_line: 4 + source_column: 5 + } + } + + b := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 99 + } + sources_ind: 2 + names_ind: Empty{} + source_position: SourcePosition{ + source_line: 4 + source_column: 5 + } + } + + assert compare_by_generated_positions_inflated(a, b) +} + +fn test_cmp_gen_line() { + a := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 0 + } + sources_ind: 2 + names_ind: Empty{} + source_position: SourcePosition{ + source_line: 4 + source_column: 5 + } + } + + b := Mapping{ + GenPosition: GenPosition{ + gen_line: 1 + gen_column: 99 + } + sources_ind: 2 + names_ind: Empty{} + source_position: SourcePosition{ + source_line: 4 + source_column: 5 + } + } + + assert compare_by_generated_positions_inflated(a, b) +} diff --git a/v_windows/v/old/vlib/v/gen/js/sourcemap/mappings.v b/v_windows/v/old/vlib/v/gen/js/sourcemap/mappings.v new file mode 100644 index 0000000..d7aff13 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/sourcemap/mappings.v @@ -0,0 +1,170 @@ +module sourcemap + +import v.gen.js.sourcemap.vlq +import io + +struct Empty {} + +pub struct SourcePosition { + source_line u32 + source_column u32 +} + +type IndexNumber = u32 +type SourcePositionType = Empty | SourcePosition +type NameIndexType = Empty | IndexNumber + +struct GenPosition { + gen_line u32 + gen_column u32 +} + +struct MappingInput { + GenPosition + name string + source_position SourcePositionType +} + +struct Mapping { + GenPosition + sources_ind u32 + names_ind NameIndexType + source_position SourcePositionType +} + +struct Mappings { +mut: + sorted bool + last Mapping + values []Mapping +} + +fn new_mappings() Mappings { + return Mappings{ + last: Mapping{ + GenPosition: GenPosition{ + gen_column: 0 + gen_line: 0 + } + } + sorted: true + } +} + +// Add the given source mapping +fn (mut m Mappings) add_mapping(gen_line u32, gen_column u32, sources_ind u32, source_position SourcePositionType, names_ind NameIndexType) { + if !(gen_line > m.last.gen_line + || (gen_line == m.last.gen_line && gen_column >= m.last.gen_column)) { + m.sorted = false + } + m.values << Mapping{ + GenPosition: GenPosition{ + gen_line: gen_line + gen_column: gen_column + } + sources_ind: sources_ind + names_ind: names_ind + source_position: source_position + } +} + +// Returns the flat, sorted array of mappings. The mappings are sorted by generated position. + +fn (mut m Mappings) get_sorted_array() []Mapping { + if !m.sorted { + panic('not implemented') + } + return m.values +} + +fn (mut m Mappings) export_mappings(mut output io.Writer) ? { + mut previous_generated_line := u32(1) + mut previous_generated_column := u32(0) + mut previous_source_index := i64(0) + mut previous_source_line := i64(0) + mut previous_source_column := i64(0) + mut previous_name_index := i64(0) + + line_mappings := m.get_sorted_array() + len := line_mappings.len + for i := 0; i < len; i++ { + mapping := line_mappings[i] + + cloned_generated_line := mapping.gen_line + if cloned_generated_line > 0 { + // Write a ';' for each line between this and last line, way more efficient than storing empty lines or looping... + output.write(';'.repeat(int(cloned_generated_line - previous_generated_line)).bytes()) or { + panic('Writing vql failed!') + } + } + if cloned_generated_line != previous_generated_line { + previous_generated_column = 0 + previous_generated_line = cloned_generated_line + } else { + if i > 0 { + if !compare_by_generated_positions_inflated(mapping, line_mappings[i - 1]) { + continue + } + output.write(','.bytes()) or { panic('Writing vql failed!') } + } + } + + vlq.encode(i64(mapping.gen_column - previous_generated_column), mut &output) ? + previous_generated_column = mapping.gen_column + match mapping.source_position { + Empty {} + SourcePosition { + vlq.encode(i64(mapping.sources_ind - previous_source_index), mut &output) ? + previous_source_index = mapping.sources_ind + // lines are stored 0-based in SourceMap spec version 3 + vlq.encode(i64(mapping.source_position.source_line - 1 - previous_source_line), mut + output) ? + previous_source_line = mapping.source_position.source_line - 1 + vlq.encode(i64(mapping.source_position.source_column - previous_source_column), mut + output) ? + previous_source_column = mapping.source_position.source_column + + match mapping.names_ind { + Empty {} + IndexNumber { + vlq.encode(i64(mapping.names_ind - previous_name_index), mut &output) ? + previous_name_index = mapping.names_ind + } + } + } + } + } +} + +fn compare_by_generated_positions_inflated(mapping_a Mapping, mapping_b Mapping) bool { + if mapping_a.gen_line != mapping_b.gen_line { + return true + } + if mapping_a.gen_column != mapping_b.gen_column { + return true + } + + if mapping_a.sources_ind != mapping_b.sources_ind { + return true + } + + if mapping_a.source_position.type_name() == mapping_b.source_position.type_name() + && mapping_a.source_position is SourcePosition + && mapping_b.source_position is SourcePosition { + if mapping_a.source_position.source_line != mapping_b.source_position.source_line + || mapping_a.source_position.source_column != mapping_b.source_position.source_column { + return true + } + } else { + if mapping_a.source_position.type_name() != mapping_b.source_position.type_name() { + return true + } + } + + if mapping_a.names_ind.type_name() == mapping_b.names_ind.type_name() + && mapping_a.names_ind is IndexNumber && mapping_b.names_ind is IndexNumber { + return mapping_a.names_ind != mapping_b.names_ind + } else { + return mapping_a.names_ind.type_name() != mapping_b.names_ind.type_name() + } +} diff --git a/v_windows/v/old/vlib/v/gen/js/sourcemap/sets.v b/v_windows/v/old/vlib/v/gen/js/sourcemap/sets.v new file mode 100644 index 0000000..70f7482 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/sourcemap/sets.v @@ -0,0 +1,16 @@ +module sourcemap + +struct Sets { +mut: + value map[string]u32 +} + +// adds a new element to a Set if new and returns index position of new or existing element +fn (mut s Sets) add(element string) u32 { + index := s.value[element] or { + index := u32(s.value.len) + s.value[element] = index + return index + } + return index +} diff --git a/v_windows/v/old/vlib/v/gen/js/sourcemap/source_map.v b/v_windows/v/old/vlib/v/gen/js/sourcemap/source_map.v new file mode 100644 index 0000000..44f79d1 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/sourcemap/source_map.v @@ -0,0 +1,131 @@ +module sourcemap + +import io +import os +import x.json2 + +const ( + source_map_version = 3 +) + +type SourceMapJson = map[string]json2.Any + +struct SourceMap { +pub mut: + version int [json: version] + file string [json: file] + source_root string [json: source_root] + sources Sets [json: sources] + sources_content map[string]string + names Sets + mappings Mappings + sources_content_inline bool +} + +struct StringWriter { +pub mut: + bytes []byte +} + +pub fn new_sourcemap(file string, source_root string, sources_content_inline bool) SourceMap { + return SourceMap{ + version: sourcemap.source_map_version + file: file + source_root: source_root + mappings: new_mappings() + sources_content_inline: sources_content_inline + } +} + +// Add a single mapping from original source line and column to the generated source's line and column for this source map being created. +pub fn (mut sm SourceMap) add_mapping(source_name string, source_position SourcePositionType, gen_line u32, gen_column u32, name string) { + assert source_name.len != 0 + + sources_ind := sm.sources.add(source_name) + + names_ind := if name.len != 0 { + NameIndexType(IndexNumber(sm.names.add(name))) + } else { + NameIndexType(Empty{}) + } + sm.mappings.add_mapping(gen_line, gen_column, sources_ind, source_position, names_ind) +} + +// Add multiple mappings from the same source +pub fn (mut sm SourceMap) add_mapping_list(source_name string, mapping_list []MappingInput) ? { + assert source_name.len != 0 + + sources_ind := sm.sources.add(source_name) + + for mapping in mapping_list { + names_ind := if mapping.name.len != 0 { + NameIndexType(IndexNumber(sm.names.add(mapping.name))) + } else { + NameIndexType(Empty{}) + } + sm.mappings.add_mapping(mapping.gen_line, mapping.gen_column, sources_ind, mapping.source_position, + names_ind) + } +} + +// Set the source content for a source file. +pub fn (mut sm SourceMap) set_source_content(source_name string, source_content string) { + sm.sources_content[source_name] = source_content +} + +fn (mut sm SourceMap) export_mappings(mut writer io.Writer) { + sm.mappings.export_mappings(mut writer) or { panic('export failed') } +} + +fn (mut sm SourceMap) export_mappings_string() string { + mut output := StringWriter{} + + sm.mappings.export_mappings(mut output) or { panic('export failed') } + return output.bytes.bytestr() +} + +// create a JSON representing the sourcemap +// Sourcemap Specs http://sourcemaps.info/spec.html +pub fn (mut sm SourceMap) to_json() SourceMapJson { + mut source_map_json := map[string]json2.Any{} + source_map_json['version'] = sm.version + if sm.file != '' { + source_map_json['file'] = json2.Any(sm.file) + } + if sm.source_root != '' { + source_map_json['sourceRoot'] = json2.Any(sm.source_root) + } + mut sources_json := []json2.Any{} + mut sources_content_json := []json2.Any{} + for source_file, _ in sm.sources.value { + sources_json << source_file + if source_file in sm.sources_content { + sources_content_json << sm.sources_content[source_file] + } else { + if sm.sources_content_inline { + if source_file_content := os.read_file(source_file) { + sources_content_json << source_file_content + } else { + sources_content_json << json2.null + } + } else { + sources_content_json << json2.null + } + } + } + source_map_json['sources'] = json2.Any(sources_json) + source_map_json['sourcesContent'] = json2.Any(sources_content_json) + + mut names_json := []json2.Any{} + for name, _ in sm.names.value { + names_json << name + } + source_map_json['names'] = json2.Any(names_json) + source_map_json['mappings'] = sm.export_mappings_string() + return source_map_json +} + +fn (mut w StringWriter) write(buf []byte) ?int { + w.bytes << buf + return buf.len +} diff --git a/v_windows/v/old/vlib/v/gen/js/sourcemap/source_map_generator.v b/v_windows/v/old/vlib/v/gen/js/sourcemap/source_map_generator.v new file mode 100644 index 0000000..f943286 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/sourcemap/source_map_generator.v @@ -0,0 +1,46 @@ +module sourcemap + +struct V3 { + SourceMap +pub: + sections []Section [json: sections] +} + +struct Offset { +pub mut: + line int [json: line] + column int [json: column] +} + +struct Section { +pub mut: + offset Offset [json: offset] + source_map SourceMap [json: map] +} + +struct Generator { +mut: + file string + // source_root string + sections []Section +} + +pub fn generate_empty_map() &Generator { + return &Generator{} +} + +pub fn (mut g Generator) add_map(file string, source_root string, sources_content_inline bool, line_offset int, column_offset int) &SourceMap { + source_map := new_sourcemap(file, source_root, sources_content_inline) + + offset := Offset{ + line: line_offset + column: column_offset + } + + g.sections << Section{ + offset: offset + source_map: source_map + } + + return &source_map +} diff --git a/v_windows/v/old/vlib/v/gen/js/sourcemap/vlq/vlq.v b/v_windows/v/old/vlib/v/gen/js/sourcemap/vlq/vlq.v new file mode 100644 index 0000000..7dfef05 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/sourcemap/vlq/vlq.v @@ -0,0 +1,115 @@ +module vlq + +import io + +const ( + shift = byte(5) + mask = byte((1 << shift) - 1) + continued = byte(1 << shift) + max_i64 = u64(9223372036854775807) + + // index start is: byte - vlq.enc_char_special_plus + enc_index = [62, 0, 0, 0, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51]! + + enc_table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' + + enc_char_start_au = 65 + enc_char_end_zu = 90 + enc_char_start_al = 97 + enc_char_end_zl = 122 + enc_char_start_zero = 48 + enc_char_end_nine = 57 + enc_char_special_plus = 43 + enc_char_special_slash = 47 +) + +[inline] +fn abs64(x i64) u64 { + return if x < 0 { u64(-x) } else { u64(x) } +} + +// Decode a single base64 digit. +[inline] +fn decode64(input byte) byte { + $if debug { + assert input >= vlq.enc_char_special_plus + assert input <= vlq.enc_char_end_zl + } + return byte(vlq.enc_index[input - vlq.enc_char_special_plus]) +} + +// Decode a single VLQ value from the input stream, returning the value. +// +// # Range +// +// Supports all numbers that can be represented by a sign bit and a 63 bit +// absolute value: `[-(2^63 - 1), 2^63 - 1]`. +// +// Note that `i64::MIN = -(2^63)` cannot be represented in that form, and this +// NOT IMPLEMENTED: function will return `Error::Overflowed` when attempting to decode it. +pub fn decode(mut input io.Reader) ?i64 { + mut buf := []byte{len: 1} + + mut accum := u64(0) + mut shifter := 0 + mut digit := byte(0) + + mut keep_going := true + for keep_going { + len := input.read(mut buf) or { return error('Unexpected EOF') } + if len == 0 { + return error('no content') + } + digit = decode64(buf[0]) + keep_going = (digit & vlq.continued) != 0 + + digit_value := u64(digit & vlq.mask) << u32(shifter) // TODO: check Overflow + + accum += digit_value + shifter += vlq.shift + } + + abs_value := accum / 2 + if abs_value > vlq.max_i64 { + return error('Overflow') + } + + // The low bit holds the sign. + return if (accum & 1) != 0 { (-i64(abs_value)) } else { i64(abs_value) } +} + +[inline] +fn encode64(input byte) byte { + $if debug { + assert input < 64 + } + return vlq.enc_table[input] +} + +// Encode a value as Base64 VLQ, sending it to the writer +pub fn encode(value i64, mut output io.Writer) ? { + signed := value < 0 + mut value_u64 := abs64(value) << 1 + if signed { + if value_u64 == 0 { + // Wrapped + value_u64 = vlq.max_i64 + 1 + } + value_u64 |= 1 + } + for { + mut digit := byte(value_u64) & vlq.mask + value_u64 >>= vlq.shift + if value_u64 > 0 { + digit |= vlq.continued + } + bytes := [encode64(digit)] + output.write(bytes) or { return error('Write failed') } + if value_u64 == 0 { + break + } + } +} diff --git a/v_windows/v/old/vlib/v/gen/js/sourcemap/vlq/vlq_decode_test.v b/v_windows/v/old/vlib/v/gen/js/sourcemap/vlq/vlq_decode_test.v new file mode 100644 index 0000000..fdb4acf --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/sourcemap/vlq/vlq_decode_test.v @@ -0,0 +1,52 @@ +module vlq + +import io + +struct TestReader { +pub: + bytes []byte +mut: + i int +} + +struct TestData { + decode_val string + expected i64 +} + +type TestDataList = []TestData + +fn test_decode_a() ? { + decode_values := [ + TestData{'A', 0}, + TestData{'C', 1}, + TestData{'D', -1}, + TestData{'2H', 123}, + TestData{'qxmvrH', 123456789}, + TestData{'+/////B', 1073741823} /* 2^30-1 */, + // TestData{'hgggggggggggI', 9_223_372_036_854_775_808} /* 2^63 */, + ] + + for _, test_data in decode_values { + mut input := make_test_reader(test_data.decode_val) + + res := decode(mut &input) ? + assert res == test_data.expected + } +} + +fn (mut b TestReader) read(mut buf []byte) ?int { + if !(b.i < b.bytes.len) { + return none + } + n := copy(buf, b.bytes[b.i..]) + b.i += n + return n +} + +fn make_test_reader(data string) io.Reader { + buf := &TestReader{ + bytes: data.bytes() + } + return io.new_buffered_reader(reader: buf) +} diff --git a/v_windows/v/old/vlib/v/gen/js/sourcemap/vlq/vlq_encode_test.v b/v_windows/v/old/vlib/v/gen/js/sourcemap/vlq/vlq_encode_test.v new file mode 100644 index 0000000..ad2db3b --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/sourcemap/vlq/vlq_encode_test.v @@ -0,0 +1,35 @@ +module vlq + +struct TestData { + expected string + data_val i64 +} + +struct TestWriter { +pub mut: + bytes []byte +} + +fn test_encode_a() ? { + decode_values := [ + TestData{'A', 0}, + TestData{'C', 1}, + TestData{'D', -1}, + TestData{'2H', 123}, + TestData{'qxmvrH', 123456789}, + TestData{'+/////B', 1073741823} /* 2^30-1 */, + // TestData{'hgggggggggggI', 9_223_372_036_854_775_808} /* 2^63 */, + ] + for _, test_data in decode_values { + mut output := TestWriter{} + + encode(test_data.data_val, mut &output) ? + // dump(output.bytes) + assert output.bytes == test_data.expected.bytes() + } +} + +fn (mut w TestWriter) write(buf []byte) ?int { + w.bytes << buf + return buf.len +} diff --git a/v_windows/v/old/vlib/v/gen/js/temp_fast_deep_equal.v b/v_windows/v/old/vlib/v/gen/js/temp_fast_deep_equal.v new file mode 100644 index 0000000..bca6e61 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/temp_fast_deep_equal.v @@ -0,0 +1,78 @@ +module js + +// TODO: Fix msvc bug that's preventing $embed_file('fast_deep_equal.js') +const ( + fast_deep_eq_fn = "// https://www.npmjs.com/package/fast-deep-equal - 3/3/2021 +const envHasBigInt64Array = typeof BigInt64Array !== 'undefined'; +function vEq(a, b) { + if (a === b) return true; + + if (a && b && typeof a == 'object' && typeof b == 'object') { + if (a.constructor !== b.constructor) return false; + // we want to convert all V types to JS for comparison. + if ('\$toJS' in a) + a = a.\$toJS(); + + if ('\$toJS' in b) + b = b.\$toJS(); + + var length, i, keys; + if (Array.isArray(a)) { + length = a.length; + if (length != b.length) return false; + for (i = length; i-- !== 0;) + if (!vEq(a[i], b[i])) return false; + return true; + } + + + if ((a instanceof Map) && (b instanceof Map)) { + if (a.size !== b.size) return false; + for (i of a.entries()) + if (!b.has(i[0])) return false; + for (i of a.entries()) + if (!vEq(i[1], b.get(i[0]))) return false; + return true; + } + + if ((a instanceof Set) && (b instanceof Set)) { + if (a.size !== b.size) return false; + for (i of a.entries()) + if (!b.has(i[0])) return false; + return true; + } + + if (ArrayBuffer.isView(a) && ArrayBuffer.isView(b)) { + length = a.length; + if (length != b.length) return false; + for (i = length; i-- !== 0;) + if (a[i] !== b[i]) return false; + return true; + } + + + if (a.constructor === RegExp) return a.source === b.source && a.flags === b.flags; + if (a.valueOf !== Object.prototype.valueOf) return a.valueOf() === b.valueOf(); + if (a.toString !== Object.prototype.toString) return a.toString() === b.toString(); + + keys = Object.keys(a); + length = keys.length; + if (length !== Object.keys(b).length) return false; + + for (i = length; i-- !== 0;) + if (!Object.prototype.hasOwnProperty.call(b, keys[i])) return false; + + for (i = length; i-- !== 0;) { + var key = keys[i]; + + if (!vEq(a[key], b[key])) return false; + } + + return true; + } + + // true if both NaN, false otherwise + return a!==a && b!==b; +}; +" +) diff --git a/v_windows/v/old/vlib/v/gen/js/tests/.gitignore b/v_windows/v/old/vlib/v/gen/js/tests/.gitignore new file mode 100644 index 0000000..4c43fe6 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/.gitignore @@ -0,0 +1 @@ +*.js \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/gen/js/tests/array.v b/v_windows/v/old/vlib/v/gen/js/tests/array.v new file mode 100644 index 0000000..f07e1b6 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/array.v @@ -0,0 +1,138 @@ +fn map_cb(s string) string { + return 'CB: $s' +} + +fn filter_cb(n int) bool { + return n < 4 +} + +fn variadic(args ...int) { + println(args) + println(args[0]) + println(args[1]) +} + +fn vararg_test() { + variadic(1, 2, 3) +} + +// TODO Remove `fn main` once vet supports scripts +fn main() { + vararg_test() + + arr1 := ['Hello', 'JS', 'Backend'] + mut arr2 := [1, 2, 3, 4, 5] + + // Array slices + slice1 := arr1[1..3] + slice2 := arr2[..3] + slice3 := arr2[3..] + + // Array indexes + idx1 := slice1[1] + arr2[0] = 1 + arr2[0 + 1] = 2 + println(arr2) + + // TODO: This does not work for now + // arr2[0..1] = arr2[3..4] + // println(arr2) + + // Array push operator + arr2 << 6 + arr2 << [7, 8, 9] + println(arr2) + println('\n\n') + + // String slices + mut slice4 := idx1[..4] + print('Back\t=> ') + println(slice4) // 'Back' + + // String indexes + idx2 := slice4[0] + print('66\t=> ') + println(idx2) + // TODO: + // slice4[3] = `c` + + // Maps + mut m := map[string]string{} + key := 'key' + m[key] = 'value' + val := m['key'] + print('value\t=> ') + println(val) + + // 'in' / '!in' + print('true\t=> ') + println('JS' in arr1) + print('false\t=> ') + println(3 !in arr2) + print('true\t=> ') + println('key' in m) + print('true\t=> ') + println('badkey' !in m) + print('true\t=> ') + println('o' in 'hello') + + // for in + for _ in arr1 {} + println('0 to 8\t=>') + for i, _ in arr2 { + println(i) + } + println('\n\n4 to 5\t=> ') + for _, v in slice3 { + println(v) + } + + println(int(1.5)) + + println('\n\n') + + // map + a := arr1.map('VAL: $it') + b := arr1.map(map_cb) + c := arr1.map(map_cb(it)) + d := arr1.map(fn (a string) string { + return 'ANON: $a' + }) + // I don't know when this would ever be used, + // but it's what the C backend does ¯\_(ツ)_/¯ + e := arr1.map(456) + + println(a) + println(b) + println(c) + println(d) + println(e) + + println('\n\n') + + // filter + aa := arr2.filter(it < 4) + bb := arr2.filter(filter_cb) + cc := arr2.filter(filter_cb(it)) + dd := arr2.filter(fn (a int) bool { + return a < 4 + }) + + println(aa) + println(bb) + println(cc) + println(dd) + + // fixed arrays: implemented as normal arrays + f1 := [1, 2, 3, 4, 5]! + mut f2 := [8]f32{} + f2[0] = f32(1.23) + f3 := ['foo', 'bar']! + f4 := [u64(0xffffffffffffffff), 0xdeadface]! + + println(' +$f1 +$f2 +$f3 +$f4') +} diff --git a/v_windows/v/old/vlib/v/gen/js/tests/auto_deref_args.v b/v_windows/v/old/vlib/v/gen/js/tests/auto_deref_args.v new file mode 100644 index 0000000..a9775e7 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/auto_deref_args.v @@ -0,0 +1,13 @@ +struct Foo { + field &int +} + +fn bar(x int) { + println(x) +} + +fn main() { + x := 4 + foo := Foo{&x} + bar(foo.field) +} diff --git a/v_windows/v/old/vlib/v/gen/js/tests/enum.v b/v_windows/v/old/vlib/v/gen/js/tests/enum.v new file mode 100644 index 0000000..2d1bfc0 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/enum.v @@ -0,0 +1,19 @@ +import v.gen.js.tests.hello + +enum Test { + foo = 2 + bar = 5 + baz +} + +// TODO Remove `fn main` once vet supports scripts +fn main() { + mut a := hello.Ccc.a + a = .b + a = .c + println(a) + + mut b := Test.foo + b = .bar + println(b) +} diff --git a/v_windows/v/old/vlib/v/gen/js/tests/hello/hello.js.v b/v_windows/v/old/vlib/v/gen/js/tests/hello/hello.js.v new file mode 100644 index 0000000..0b3fe40 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/hello/hello.js.v @@ -0,0 +1,5 @@ +module hello + +pub fn raw_js_log() { + #console.log('hello') +} diff --git a/v_windows/v/old/vlib/v/gen/js/tests/hello/hello.v b/v_windows/v/old/vlib/v/gen/js/tests/hello/hello.v new file mode 100644 index 0000000..9b75511 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/hello/hello.v @@ -0,0 +1,33 @@ +module hello + +import v.gen.js.tests.hello.hello1 + +pub const ( + hello = 'Hello' +) + +pub struct Aaa { +pub mut: + foo string +} + +pub fn (mut a Aaa) update(s string) { + a.foo = s +} + +struct Bbb {} + +pub enum Ccc { + a + b = 5 + c +} + +pub fn debugger() string { + v := Bbb{} + return hello.hello +} + +pub fn excited() string { + return '$hello1.nested() $debugger()!' +} diff --git a/v_windows/v/old/vlib/v/gen/js/tests/hello/hello1/hello1.v b/v_windows/v/old/vlib/v/gen/js/tests/hello/hello1/hello1.v new file mode 100644 index 0000000..030e704 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/hello/hello1/hello1.v @@ -0,0 +1,5 @@ +module hello1 + +pub fn nested() string { + return 'Nested' +} diff --git a/v_windows/v/old/vlib/v/gen/js/tests/interface.v b/v_windows/v/old/vlib/v/gen/js/tests/interface.v new file mode 100644 index 0000000..a43cadf --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/interface.v @@ -0,0 +1,47 @@ +struct Dog { + name string + age int +} + +struct Cat { + name string + age int +} + +interface Animal { + say(s string) + greet() int +} + +fn (d Dog) say(s string) { + println('Dog $d.name: "$s"') +} + +fn (c Cat) say(s string) { + println('Cat $c.name: "$s"') +} + +fn (d Dog) greet() int { + d.say('Hello!') + return d.age +} + +fn (c Cat) greet() int { + c.say('Hello!') + return c.age +} + +fn use(a Animal) { + if a is Dog { + println('dog') + } else if a is Cat { + println('cat') + } else { + println('its a bug!') + } +} + +fn main() { + use(Dog{'Doggo', 5}) + use(Cat{'Nyancat', 6}) +} diff --git a/v_windows/v/old/vlib/v/gen/js/tests/interp.v b/v_windows/v/old/vlib/v/gen/js/tests/interp.v new file mode 100644 index 0000000..5b00aa8 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/interp.v @@ -0,0 +1,188 @@ +fn test_fn(s1 string, s2 string) { + print(if s1 == s2 { 'true' } else { 'false' }) + print('\t=> ') + println('"$s1", "$s2"') +} + +fn simple_string_interpolation() { + a := 'Hello' + b := 'World' + res := '$a $b' + test_fn(res, 'Hello World') +} + +fn mixed_string_interpolation() { + num := 7 + str := 'abc' + s1 := 'number=$num' + test_fn(s1, 'number=7') + s2 := 'string=$str' + test_fn(s2, 'string=abc') + s3 := 'a: $num | b: $str' + test_fn(s3, 'a: 7 | b: abc') +} + +fn formatted_string_interpolation() { + x := 'abc' + axb := 'a:$x:b' + test_fn(axb, 'a:abc:b') + x_10 := 'a:${x:10s}:b' + x10_ := 'a:${x:-10s}:b' + test_fn(x_10, 'a: abc:b') + test_fn(x10_, 'a:abc :b') + i := 23 + si_right := '${i:10d}' + si__left := '${i:-10d}' + test_fn(si_right, ' 23') + test_fn(si__left, '23 ') +} + +/* +excape_dollar_in_string() +fn excape_dollar_in_string() { + i := 42 + test_fn('($i)', '(42)') + println('(\$i)'.contains('i') && !'(\$i)'.contains('42')) + println(!'(\\$i)'.contains('i') && '(\\$i)'.contains('42') && '(\\$i)'.contains('\\')) + println('(\\\$i)'.contains('i') && !'(\\\$i)'.contains('42') && '(\\$i)'.contains('\\')) + println(!'(\\\\$i)'.contains('i') && '(\\\\$i)'.contains('42') && '(\\\\$i)'.contains('\\\\')) + test_fn('(${i})', '(42)') + println('(\${i})'.contains('i') && !'(\${i})'.contains('42')) + println(!'(\\${i})'.contains('i') && '(\\${i})'.contains('42') && '(\\${i})'.contains('\\')) + println('(\\\${i})'.contains('i') && !'(\\\${i})'.contains('42') && '(\\${i})'.contains('\\')) + println(!'(\\\\${i})'.contains('i') && '(\\\\${i})'.contains('42') && '(\\\\${i})'.contains('\\\\')) + test_fn(i, 42) +} +*/ + +fn implicit_str() { + i := 42 + test_fn('int $i', 'int 42') + test_fn('$i', '42') + check := '$i' == '42' + // println(check) + text := '$i' + '42' + test_fn(text, '4242') +} + +fn string_interpolation_percent_escaping() { + test := 'hello' + hello := 'world' + x := '%.*s$hello$test |${hello:-30s}|' + test_fn(x, '%.*sworldhello |world |') +} + +fn string_interpolation_string_prefix() { + // `r`, `c` and `js` are also used as a string prefix. + r := 'r' + rr := '$r$r' + test_fn(rr, 'rr') + c := 'c' + cc := '$c$c' + test_fn(cc, 'cc') + js := 'js' + jsjs := '$js$js' + test_fn(jsjs, 'jsjs') +} + +fn interpolation_string_prefix_expr() { + r := 1 + c := 2 + js := 1 + test_fn('>${3 + r}<', '>4<') + test_fn('${r == js} $js', 'true 1') + test_fn('>${js + c} ${js + r == c}<', '>3 true<') +} + +/* +inttypes_string_interpolation() +fn inttypes_string_interpolation() { + c := i8(-103) + uc := byte(217) + uc2 := byte(13) + s := i16(-23456) + us := u16(54321) + i := -1622999040 + ui := u32(3421958087) + vp := voidptr(ui) + bp := byteptr(15541149836) + l := i64(-7694555558525237396) + ul := u64(17234006112912956370) + test_fn('$s $us', '-23456 54321') + test_fn('$ui $i', '3421958087 -1622999040') + test_fn('$l $ul', '-7694555558525237396 17234006112912956370') + test_fn('>${s:11}:${us:-13}<', '> -23456:54321 <') + test_fn('0x${ul:-19x}:${l:22d}', '0xef2b7d4001165bd2 : -7694555558525237396') + test_fn('${c:5}${uc:-7}x', ' -103217 x') + test_fn('${c:x}:${uc:x}:${uc2:02X}', '99:d9:0D') + test_fn('${s:X}:${us:x}:${u16(uc):04x}', 'A460:d431:00d9') + test_fn('${i:x}:${ui:X}:${int(s):x}', '9f430000:CBF6EFC7:ffffa460') + test_fn('${l:x}:${ul:X}', '9537727cad98876c:EF2B7D4001165BD2') + // default pointer format is platform dependent, so try a few + println("platform pointer format: '${vp:p}:$bp'") + test_fn('${vp:p}:$bp', '0xcbf6efc7:0x39e53208c' || + '${vp:p}:$bp' == 'CBF6EFC7:39E53208C' || + '${vp:p}:$bp' == 'cbf6efc7:39e53208c' || + '${vp:p}:$bp' == '00000000CBF6EFC7:000000039E53208C') +} +*/ + +fn utf8_string_interpolation() { + a := 'à-côté' + st := 'Sträßle' + m := '10€' + test_fn('$a $st $m', 'à-côté Sträßle 10€') + zz := '>${a:10}< >${st:-8}< >${m:5}<-' + zz_expected := '> à-côté< >Sträßle < > 10€<-' + // println(' zz: $zz') + // println('zz_expected: $zz_expected') + test_fn(zz, zz_expected) + // e := '\u20AC' // Eurosign doesn' work with MSVC and tcc + e := '€' + test_fn('100.00 $e', '100.00 €') + m2 := 'Москва́' // cyrillic а́: combination of U+0430 and U+0301, UTF-8: d0 b0 cc 81 + d := 'Antonín Dvořák' // latin á: U+00E1, UTF-8: c3 a1 + test_fn(':${m2:7}:${d:-15}:', ': Москва́:Antonín Dvořák :') + g := 'Πελοπόννησος' + test_fn('>${g:-13}<', '>Πελοπόννησος <') +} + +struct Sss { + v1 int + v2 f64 +} + +fn (s Sss) str() string { + return '[$s.v1, ${s.v2:.3f}]' +} + +fn string_interpolation_str_evaluation() { + mut x := Sss{17, 13.455893} + test_fn('$x', '[17, 13.456]') +} + +/* +string_interpolation_with_negative_format_width_should_compile_and_run_without_segfaulting() +fn string_interpolation_with_negative_format_width_should_compile_and_run_without_segfaulting() { + // discovered during debugging VLS + i := 3 + input := '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' + println('---------------------------------------------------------------------------------------------') + println('+60 ${i:10} | input.len: ${input.len:10} | ${input.bytes().hex():60} | $input') + println('-60 ${i:10} | input.len: ${input.len:10} | ${input.bytes().hex():-60} | $input') + println('---------------------------------------------------------------------------------------------') + println(true) +} +*/ + +fn main() { + simple_string_interpolation() + mixed_string_interpolation() + formatted_string_interpolation() + implicit_str() + string_interpolation_percent_escaping() + string_interpolation_string_prefix() + interpolation_string_prefix_expr() + utf8_string_interpolation() + string_interpolation_str_evaluation() +} diff --git a/v_windows/v/old/vlib/v/gen/js/tests/js.v b/v_windows/v/old/vlib/v/gen/js/tests/js.v new file mode 100644 index 0000000..ee7ba08 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/js.v @@ -0,0 +1,141 @@ +import v.gen.js.tests.hello as hl +import v.gen.js.tests.hello.hello1 as hl1 + +const ( + i_am_a_const = 21214 + super = 'amazing keyword' +) + +struct Foo { +mut: + a hl.Aaa +} + +struct Companies { + google int + amazon bool + yahoo string +} + +enum POSITION { + go_back + dont_go_back +} + +fn class(extends string, instanceof int) { + delete := instanceof + _ = delete +} + +fn main() { + println('Hello from V.js!') + println(JS.Math.atan2(1, 0)) + println(JS.eval("console.log('Hello!')")) + mut a := 1 + a *= 2 + a += 3 + println(a) + mut b := hl.Aaa{} + b.update('an update') + println(b) + mut c := Foo{hl.Aaa{}} + c.a.update('another update') + println(c) + println('int(1.5) == "${int(1.5)}"') + d := int(10) + f32(127) + println('typeof (int + f32) == "${typeof(d)}"') + _ = 'done' + { + _ = 'block' + } + _ = POSITION.go_back + _ = hl.Ccc.a + debugger := 'JS keywords' + // TODO: Implement interpolation + await := '$super: $debugger' + mut finally := 'implemented' + println('$await $finally') + dun := i_am_a_const * 20 + 2 + dunn := hl.hello // External constant + _ = hl1.nested() + for i := 0; i < 10; i++ { + } + for i, x in 'hello' { + } + mut evens := []int{} + for x in 1 .. 10 { + y := error_if_even(x) or { x + 1 } + evens << y + } + println(evens) + arr := [1, 2, 3, 4, 5] + for i in arr { + } + ma := map{ + 'str': 'done' + 'ddo': 'baba' + } + // panic('foo') + for m, n in ma { + iss := m + } + go async(0, 'hello') + fn_in_var := fn (number int) { + println('number: $number') + } + hl.debugger() + anon_consumer(hl.excited(), fn (message string) { + println(message) + }) + hl.raw_js_log() + propagation() or { println(err) } +} + +fn anon_consumer(greeting string, anon fn (string)) { + anon(greeting) +} + +fn async(num int, def string) { +} + +[deprecated; inline] +fn hello(game_on int, dummy ...string) (int, int) { + defer { + do := 'not' + } + for dd in dummy { + l := dd + } + return game_on + 2, 221 +} + +fn (it Companies) method() int { + ss := Companies{ + google: 2 + amazon: true + yahoo: 'hello' + } + a, b := hello(2, 'google', 'not google') + glue := if a > 2 { + 'more_glue' + } else if a > 5 { + 'more glueee' + } else { + 'less glue' + } + if a != 2 { + } + return 0 +} + +fn error_if_even(num int) ?int { + if num % 2 == 0 { + return error('number is even') + } + return num +} + +fn propagation() ? { + println('Propagation test:') + return error('"Task failed successfully" - Windows XP') +} diff --git a/v_windows/v/old/vlib/v/gen/js/tests/life.v b/v_windows/v/old/vlib/v/gen/js/tests/life.v new file mode 100644 index 0000000..a89da5a --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/life.v @@ -0,0 +1,98 @@ +fn clear() { + JS.console.clear() +} + +const ( + w = 30 + h = 30 +) + +fn get(game [][]bool, x int, y int) bool { + if y < 0 || x < 0 { + return false + } + if y >= h || x >= w { + return false + } + + return game[y][x] +} + +fn neighbours(game [][]bool, x int, y int) int { + mut count := 0 + if get(game, x - 1, y - 1) { + count++ + } + if get(game, x, y - 1) { + count++ + } + if get(game, x + 1, y - 1) { + count++ + } + if get(game, x - 1, y) { + count++ + } + if get(game, x + 1, y) { + count++ + } + if get(game, x - 1, y + 1) { + count++ + } + if get(game, x, y + 1) { + count++ + } + if get(game, x + 1, y + 1) { + count++ + } + return count +} + +fn step(game [][]bool) [][]bool { + mut new_game := [][]bool{} + for y, row in game { + mut new_row := []bool{} + new_game[y] = new_row + for x, cell in row { + count := neighbours(game, x, y) + new_row[x] = (cell && count in [2, 3]) || count == 3 + } + } + return new_game +} + +fn row_str(row []bool) string { + mut str := '' + for cell in row { + if cell { + str += '◼ ' + } else { + str += '◻ ' + } + } + return str +} + +fn show(game [][]bool) { + clear() + for row in game { + println(row_str(row)) + } +} + +// TODO Remove `fn main` once vet supports scripts +fn main() { + mut game := [][]bool{len: h, init: []bool{len: w}} + + game[11][15] = true + game[11][16] = true + game[12][16] = true + game[10][21] = true + game[12][20] = true + game[12][21] = true + game[12][22] = true + + JS.setInterval(fn () { + show(game) + game = step(game) + }, 500) +} diff --git a/v_windows/v/old/vlib/v/gen/js/tests/optional.v b/v_windows/v/old/vlib/v/gen/js/tests/optional.v new file mode 100644 index 0000000..6d52eac --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/optional.v @@ -0,0 +1,33 @@ +module main + +fn main() { + try_propagation() or { println('captured: $err') } +} + +fn try_propagation() ? { + try_numbers() ? +} + +fn try_numbers() ? { + for x in 1 .. 10 { + y := error_if_even(x) or { x + 1 } + println('$x rounded to $y') + error_if_prime(y) ? + } +} + +fn error_if_even(num int) ?int { + if num % 2 == 0 { + return error('number is even') + } + return num +} + +fn error_if_prime(num int) ?int { + for i in 2 .. num { + if num % i == 0 { + return error('$num is prime') + } + } + return num +} diff --git a/v_windows/v/old/vlib/v/gen/js/tests/simple.v b/v_windows/v/old/vlib/v/gen/js/tests/simple.v new file mode 100644 index 0000000..ec46286 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/simple.v @@ -0,0 +1,5 @@ +module main + +fn main() { + println('hello world') +} diff --git a/v_windows/v/old/vlib/v/gen/js/tests/simple_sourcemap.v b/v_windows/v/old/vlib/v/gen/js/tests/simple_sourcemap.v new file mode 100644 index 0000000..0dd8a64 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/simple_sourcemap.v @@ -0,0 +1,23 @@ +module main + +fn main() { + e := JS.Error{} + s := e.stack + node_version := js_node_process().version + node_main := get_node_main_version(node_version) + if node_main >= 12 { + if s.contains('simple_sourcemap.v:') { + panic('node found no source map!') + } else { + println('source map is working') + } + } else { + println('skiping test! node version >=12.12.0 required. Current Version is $node_version') + } +} + +fn get_node_main_version(str string) int { + a := str.slice(1, int(str.len)) + b := a.split('.') + return b[0].int() +} diff --git a/v_windows/v/old/vlib/v/gen/js/tests/struct.v b/v_windows/v/old/vlib/v/gen/js/tests/struct.v new file mode 100644 index 0000000..306d46a --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/struct.v @@ -0,0 +1,40 @@ +module main + +struct Int { +mut: + value int + test map[string]int + hello []int +} + +fn (mut i Int) add(value int) { + i.value += value +} + +fn (i Int) get() int { + return i.value +} + +struct Config { + foo int + bar string +} + +fn use_config(c Config) { +} + +fn main() { + mut a := Int{ + value: 10 + } + a.add(5) + println(a) // 15 + mut b := Int{} + b.add(10) + println(b.get()) // 10 + use_config(Config{2, 'bar'}) + use_config( + foo: 2 + bar: 'bar' + ) +} diff --git a/v_windows/v/old/vlib/v/gen/js/tests/testdata/array.out b/v_windows/v/old/vlib/v/gen/js/tests/testdata/array.out new file mode 100644 index 0000000..b375fcb --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/testdata/array.out @@ -0,0 +1,231 @@ +3 +1 +2 +4 +255 +256 +2 +4 +131 +4 +1 +2 +3 +5 +4 +4 +[1,5,2,3,4] +5 +4 +5 +[1,5,2,3,4] +[5,2,3,4] +4 +[5,3,4] +3 +[5,3] +2 +[2.5,3.25,4.5,5.75] +true +true +true +true +true +true +true +true +true +true +true +1 +2 +3 +10000 +234 +0 +1 +0 +1 +3 +[1,3] +3 +2 +3 +4 +1 +4 +5 +2 +5 +0 +1 +1.1 +[1,2,3,4] +[1,5,6,2,3,4] +0 +1 +1 +0 +1 +1.1 +[1,2,3,4] +[5,6,1,2,3,4] +5 +true +1.1 +1.1 +1.1 +-123 +-123 +-123 +123 +123 +123 +1.1 +1.1 +1.1 +1 +2 +1 +2 +1 +abc +1 +abc +0 +abc +2 +3 +2 +3 +1 +2 +1 +2 +2 +1 +4 +6 +1 +4 +6 +[4,3,2,1] +true +true +true +true +true +true +true +0 +[0,0,0,0] +[0,7,0,0] +0 +[2,4,6,8,10,12,14,16,18,20] +[2,4,6,8,10,12,14,16,18,20] +[2,4,6,8,10] +2 +[1,2] +0 +1 +-1 +0 +3 +-1 +0 +2 +-1 +1 +2 +-1 +2 +3 +1 +3 +6 +true +true +true +true +true +true +true +true +true +0 +0 +0 +0 +0 +[2,4,6] +["is","awesome"] +[2,3,4,6,8,9,10] +[4,5,6] +[5,10] +[2,4] +[2,4] +[1,2,3,4,5,6] +["v","is","awesome"] +[0,0,0,0,0,0] +0 +[10,20,30,40,50,60] +[1,4,9,16,25,36] +["1","2","3","4","5","6"] +[false,true,false,true,false,true] +["V","IS","AWESOME"] +[false,false,true] +[true,true,false] +[7,7,7] +[1,4,9,16,25,36] +[3,4,5,6,7,8] +[3,9,4,6,12,7] +[] +[true,true,true,true,true,true] +["1a","2a","3a","4a","5a","6a"] +[2,3,4,5,6,7] +[2,3,8] +["1v","2is","3awesome"] +[1,4,9,16,25,36] +[1,25,100] +[1,2,3,4,5,6] +["v","is","awesome"] +[2,3,4] +[2,3,4] +[3,4,5] +[2,3,4] +["1","2","3"] +["1","2","3"] +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +true +["1","3","5","hi"] +[-3,7,42,67,108] +["a","b","c","d","e","f"] +0 +1 +79 diff --git a/v_windows/v/old/vlib/v/gen/js/tests/testdata/array.v b/v_windows/v/old/vlib/v/gen/js/tests/testdata/array.v new file mode 100644 index 0000000..8ef56bc --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/testdata/array.v @@ -0,0 +1,771 @@ +struct Chunk { + val string +} + +struct Kkk { + q []Chunk +} + +const ( + c_n = 5 +) + +struct User { + age int + name string +} + +fn map_test_helper_1(i int) int { + return i * i +} + +fn map_test_helper_2(i int, b string) int { + return i + b.len +} + +fn map_test_helper_3(i int, b []string) int { + return i + b.map(it.len)[i % b.len] +} + +fn filter_test_helper_1(a int) bool { + return a > 3 +} + +fn sum(prev int, curr int) int { + return prev + curr +} + +fn sub(prev int, curr int) int { + return prev - curr +} + +struct Foooj { + a [5]int // c_n +} + +fn double_up(mut a []int) { + for i := 0; i < a.len; i++ { + a[i] = a[i] * 2 + } +} + +fn double_up_v2(mut a []int) { + for i, _ in a { + a[i] = a[i] * 2 // or val*2, doesn't matter + } +} + +fn modify(mut numbers []int) { + numbers[0] = 777 +} + +fn main() { + { + // test pointer + mut arr := []&int{} + a := 1 + b := 2 + c := 3 + arr << &a + arr << &b + arr << &c + assert *arr[0] == 1 + arr[1] = &c + assert *arr[1] == 3 + mut d_arr := [arr] // [][]&int + d_arr << arr + println(*d_arr[0][1]) // 3 + println(*d_arr[1][0]) // 1 + } + { + // test assign + mut arr := [2, 4, 8, 16, 32, 64, 128] + arr[0] = 2 + arr[1] &= 255 + arr[2] |= 255 + arr[3] <<= 4 + arr[4] >>= 4 + arr[5] %= 5 + arr[6] ^= 3 + println(arr[0]) + println(arr[1]) + println(arr[2]) + println(arr[3]) + println(arr[4]) + println(arr[5]) + println(arr[6]) + } + { + // test ints + mut a := [1, 5, 2, 3] + println(a.len) // 4 + println(a[0]) + println(a[2]) + println(a.last()) + + a << 4 + println(a.len) + println(a[4]) + println(a.last()) + + s := a.str() + println(s) + println(a[1]) + println(a.last()) + } + { + // test deleting + mut a := [1, 5, 2, 3, 4] + + println(a.len) + println(a.str()) + + a.delete(0) + + println(a.str()) + println(a.len) // 4 + + a.delete(1) + + println(a.str()) + println(a.len) + a.delete(a.len - 1) + + println(a.str()) + println(a.len) + } + { + // test slice delete + mut a := [1.5, 2.5, 3.25, 4.5, 5.75] + b := a[2..4] + a.delete(0) + // assert a == [2.5, 3.25, 4.5, 5.75] + // assert b == [3.25, 4.5] + println(a) + println(a == [2.5, 3.25, 4.5, 5.75]) + println(b == [3.25, 4.5]) + a = [3.75, 4.25, -1.5, 2.25, 6.0] + c := a[..3] + a.delete(2) + println(a == [3.75, 4.25, 2.25, 6.0]) + println(c == [3.75, 4.25, -1.5]) + } + { + // test delete many + mut a := [1, 2, 3, 4, 5, 6, 7, 8, 9] + b := a[2..6] + a.delete_many(4, 3) + println(a == [1, 2, 3, 4, 8, 9]) + println(b == [3, 4, 5, 6]) + + c := a[..a.len] + a.delete_many(2, 0) // this should just clone + a[1] = 17 + + println(a == [1, 17, 3, 4, 8, 9]) + println(c == [1, 2, 3, 4, 8, 9]) + a.delete_many(0, a.len) + println(a == []int{}) + } + { + // test short + a := [1, 2, 3] + println(a.len == 3) + println(a.cap == 3) + println(a[0]) + println(a[1]) + println(a[2]) + } + { + // test large + mut a := [0].repeat(0) + for i in 0 .. 10000 { + a << i + } + println(a.len) + println(a[234]) + } + { + // test empty + mut chunks := []Chunk{} + a := Chunk{} + println(chunks.len) + chunks << a + println(chunks.len) + chunks = [] + println(chunks.len) + chunks << a + println(chunks.len) + } + { + // test push + mut a := []int{} + a << 1 + a << 3 + println(a[1]) + println(a.str()) + } + { + // test insert + mut a := [1, 2] + a.insert(0, 3) + println(a[0]) + println(a[2]) + println(a.len) + a.insert(1, 4) + println(a[1]) + println(a[2]) + println(a.len) + a.insert(4, 5) + println(a[4]) + println(a[3]) + println(a.len) + mut b := []f64{} + println(b.len) + b.insert(0, f64(1.1)) + println(b.len) + println(b[0]) + } + { + // test insert many + mut a := [3, 4] + a.insert(0, [1, 2]) + println(a) + + b := [5, 6] + a.insert(1, b) + println(a) + } + { + // test prepend + mut a := []int{} + println(a.len) + a.prepend(1) + println(a.len) + println(a[0]) + mut b := []f64{} + + println(b.len) + + b.prepend(f64(1.1)) + + println(b.len) + + println(b[0]) + } + { + // test prepend many + mut a := [3, 4] + a.prepend([1, 2]) + println(a) + b := [5, 6] + a.prepend(b) + println(a) + } + { + // test repeat + { + a := [0].repeat(5) + println(a.len) + println(a[0] == 0 && a[1] == 0 && a[2] == 0 && a[3] == 0 && a[4] == 0) + } + { + a := [1.1].repeat(10) + println(a[0]) + println(a[5]) + println(a[9]) + } + { + a := [i64(-123)].repeat(10) + println(a[0]) + println(a[5]) + println(a[9]) + } + { + a := [u64(123)].repeat(10) + println(a[0]) + println(a[5]) + println(a[9]) + } + { + a := [1.1].repeat(10) + println(a[0]) + println(a[5]) + println(a[9]) + } + { + a := [1, 2].repeat(2) + println(a[0]) + println(a[1]) + println(a[2]) + println(a[3]) + } + { + a := ['1', 'abc'].repeat(2) + println(a[0]) + println(a[1]) + println(a[2]) + println(a[3]) + } + { + mut a := ['1', 'abc'].repeat(0) + println(a.len) + a << 'abc' + println(a[0]) + } + } + { + // todo(playX): deep repeat does not yet work. + /* + // test deep repeat + mut a3 := [[[1, 1], [2, 2], [3, 3]], [[4, 4], [5, 5], [6, 6]]] + r := a3.repeat(3) + a3[1][1][0] = 17 + print(r) + assert r == [ + [[1, 1], [2, 2], [3, 3]], + [[4, 4], [5, 5], [6, 6]], + [[1, 1], [2, 2], [3, 3]], + [[4, 4], [5, 5], [6, 6]], + [[1, 1], [2, 2], [3, 3]], + [[4, 4], [5, 5], [6, 6]], + ] + assert a3 == [[[1, 1], [2, 2], [3, 3]], [[4, 4], [17, 5], + [6, 6], + ]] + */ + } + { + // test right + a := [1, 2, 3, 4] + c := a[1..a.len] + d := a[1..] + println(c[0]) + println(c[1]) + println(d[0]) + println(d[1]) + } + { + // test left + a := [1, 2, 3] + c := a[0..2] + d := a[..2] + println(c[0]) + println(c[1]) + println(d[0]) + println(d[1]) + } + { + // test slice + a := [1, 2, 3, 4] + b := a[2..4] + println(b.len) + println(a[1..2].len) + println(a.len) + } + { + // test push many + mut a := [1, 2, 3] + b := [4, 5, 6] + a << b + println(a.len) + println(a[0]) + println(a[3]) + println(a[5]) + } + { + // test reverse + a := [1, 2, 3, 4] + b := ['test', 'array', 'reverse'] + c := a.reverse() + println(c) + d := b.reverse() + for i, _ in c { + println(c[i] == a[a.len - i - 1]) + } + for i, _ in d { + println(d[i] == b[b.len - i - 1]) + } + e := []int{} + f := e.reverse() + println(f.len) + } + { + // test fixed + mut nums := [4]int{} + // x := nums[1..3] + // assert x.len == 2 + + println(nums) + nums[1] = 7 + println(nums) + nums2 := [5]int{} // c_n + println(nums2[c_n - 1]) + } + { + // test mut slice + /* + todo(playX): slices do not work yet. We have to implement custom wrapper for them. + mut n := [1, 2, 3] + // modify(mut n) + modify(mut n[..2]) + assert n[0] == 777 + modify(mut n[2..]) + assert n[2] == 777 + println(n) + */ + } + { + // test mut arg + mut arr := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + double_up(mut arr) + println(arr.str()) + arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + double_up_v2(mut arr) + println(arr.str()) + } + { + // test doubling + mut nums := [1, 2, 3, 4, 5] + for i in 0 .. nums.len { + nums[i] *= 2 + } + println(nums.str()) + } + { + // test single element + + mut a := [1] + a << 2 + println(a.len) + assert a[0] == 1 + assert a[1] == 2 + println(a) + } + { + // test find index + + // string + a := ['v', 'is', 'great'] + println(a.index('v')) + println(a.index('is')) + println(a.index('gre')) + // int + b := [1, 2, 3, 4] + println(b.index(1)) + println(b.index(4)) + println(b.index(5)) + // byte + c := [0x22, 0x33, 0x55] + println(c.index(0x22)) + println(c.index(0x55)) + println(c.index(0x99)) + // char + d := [`a`, `b`, `c`] + println(d.index(`b`)) + println(d.index(`c`)) + println(d.index(`u`)) + } + { + // test multi + + a := [[1, 2, 3], [4, 5, 6]] + println(a.len) + println(a[0].len) + println(a[0][0]) + println(a[0][2]) + println(a[1][2]) + } + { + // test in + a := [1, 2, 3] + println(1 in a) + println(2 in a) + println(3 in a) + println(!(4 in a)) + println(!(0 in a)) + println(0 !in a) + println(4 !in a) + b := [1, 4, 0] + c := [3, 6, 2, 0] + println(0 in b) + println(0 in c) + } + { + // test reduce + a := [1, 2, 3, 4, 5] + b := a.reduce(sum, 0) + c := a.reduce(sum, 5) + d := a.reduce(sum, -1) + println(b) + println(c) + println(d) + e := [1, 2, 3] + f := e.reduce(sub, 0) + g := e.reduce(sub, -1) + println(f) + println(g) + } + { + a := [1, 2, 3, 4, 5, 6] + b := a.filter(it % 2 == 0) + println(b) + + c := ['v', 'is', 'awesome'] + d := c.filter(it.len > 1) + println(d) + assert d[0] == 'is' + assert d[1] == 'awesome' + //////// + arr := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + println(arr.filter(it % 2 == 0 || it % 3 == 0)) + + mut mut_arr := [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + mut_arr = mut_arr.filter(it < 4) + assert mut_arr.len == 3 + println(a.filter(filter_test_helper_1)) + println([1, 5, 10].filter(filter_test_helper_1)) + } + { + // test anon fn filter + filter_num := fn (i int) bool { + return i % 2 == 0 + } + println([1, 2, 3, 4, 5].filter(filter_num)) + } + { + a := [1, 2, 3, 4].filter(fn (i int) bool { + return i % 2 == 0 + }) + println(a) + } + { + // test map + nums := [1, 2, 3, 4, 5, 6] + strs := ['v', 'is', 'awesome'] + // assert nums.map() == + // assert nums.map(it, 'excessive') == + // identity + println(nums.map(it)) + println(strs.map(it)) + println(nums.map(it - it)) + println(nums.map(it - it)[0]) + // type switch + println(nums.map(it * 10)) + println(nums.map(it * it)) + println(nums.map('$it')) + println(nums.map(it % 2 == 0)) + println(strs.map(it.to_upper())) + println(strs.map(it == 'awesome')) + println(strs.map(it.len in nums)) + println(strs.map(int(7))) + // external func + println(nums.map(map_test_helper_1(it))) + println(nums.map(map_test_helper_2(it, 'bb'))) + println(nums.map(map_test_helper_3(it, strs))) + // empty array as input + println([]int{len: 0}.map(it * 2)) + // nested maps (where it is of same type) + println(nums.map(strs.map(int(7)) == [7, 7, 7])) + println(nums.map('$it' + strs.map('a')[0])) + // assert nums.map(it + strs.map(int(7))[0]) == [8, 9, 10, 11, 12, 13] + println(nums.map(it + strs.map(it.len)[0])) + println(strs.map(it.len + strs.map(it.len)[0])) + // nested (different it types) + // todo(playX): this one produces invalid JS code. + // assert strs.map(it[nums.map(it - it)[0]]) == [byte(`v`), `i`, `a`] + println(nums[0..3].map('$it' + strs.map(it)[it - 1])) + println(nums.map(map_test_helper_1)) + println([1, 5, 10].map(map_test_helper_1)) + println(nums) + println(strs) + } + { + // test anon fn map + add_num := fn (i int) int { + return i + 1 + } + println([1, 2, 3].map(add_num)) + } + { + // test multi anon fn map + a := [1, 2, 3].map(fn (i int) int { + return i + 1 + }) + b := [1, 2, 3].map(fn (i int) int { + return i + 2 + }) + println(a) + println(b) + } + { + // test anon fn arg map + a := [1, 2, 3].map(fn (i int) int { + return i + 1 + }) + println(a) + } + { + // test anon fn arg different type map + i_to_str := fn (i int) string { + return i.str() + } + a := [1, 2, 3].map(i_to_str) + println(a) + } + { + // test anon fn inline different type map + a := [1, 2, 3].map(fn (i int) string { + return i.str() + }) + println(a) + } + { + // test array str + // todo(playX): JS array formatting should match what default builtin impl has. + /* + numbers := [1, 2, 3] + assert numbers == [1, 2, 3] + numbers2 := [numbers, [4, 5, 6]] // dup str() bug + println(numbers2) + assert true + assert numbers.str() == '[1, 2, 3]' + */ + } + { + // test eq + println([5, 6, 7] != [6, 7]) + println([`a`, `b`] == [`a`, `b`]) + println([User{ + age: 22 + name: 'bob' + }] == [User{ + age: 22 + name: 'bob' + }]) + // todo(playX): map cmp does not work yet + /* + assert [map{ + 'bob': 22 + }, map{ + 'tom': 33 + }] == [map{ + 'bob': 22 + }, map{ + 'tom': 33 + }]*/ + println([[1, 2, 3], [4]] == [[1, 2, 3], [4]]) + } + { + // test fixed array eq + a1 := [1, 2, 3]! + println(a1 == [1, 2, 3]!) + println(a1 != [2, 3, 4]!) + + a2 := [[1, 2]!, [3, 4]!]! + println(a2 == [[1, 2]!, [3, 4]!]!) + println(a2 != [[3, 4]!, [1, 2]!]!) + + a3 := [[1, 2], [3, 4]]! + println(a3 == [[1, 2], [3, 4]]!) + println(a3 != [[1, 1], [2, 2]]!) + + a4 := [[`a`, `b`], [`c`, `d`]]! + println(a4 == [[`a`, `b`], [`c`, `d`]]!) + println(a4 != [[`c`, `a`], [`a`, `b`]]!) + + a5 := [['aaa', 'bbb'], ['ccc', 'ddd']]! + println(a5 == [['aaa', 'bbb'], ['ccc', 'ddd']]!) + println(a5 != [['abc', 'def'], ['ccc', 'ddd']]!) + + a6 := [['aaa', 'bbb']!, ['ccc', 'ddd']!]! + println(a6 == [['aaa', 'bbb']!, ['ccc', 'ddd']!]!) + println(a6 != [['aaa', 'bbb']!, ['aaa', 'ddd']!]!) + + a7 := [[1, 2]!, [3, 4]!] + println(a7 == [[1, 2]!, [3, 4]!]) + println(a7 != [[2, 3]!, [1, 2]!]) + + a8 := [['aaa', 'bbb']!, ['ccc', 'ddd']!] + println(a8 == [['aaa', 'bbb']!, ['ccc', 'ddd']!]) + println(a8 != [['bbb', 'aaa']!, ['cccc', 'dddd']!]) + } + { + // test fixed array literal eq + println([1, 2, 3]! == [1, 2, 3]!) + println([1, 1, 1]! != [1, 2, 3]!) + + println([[1, 2], [3, 4]]! == [[1, 2], [3, 4]]!) + println([[1, 1], [2, 2]]! != [[1, 2], [3, 4]]!) + + println([[1, 1]!, [2, 2]!]! == [[1, 1]!, [2, 2]!]!) + println([[1, 1]!, [2, 2]!]! != [[1, 2]!, [2, 3]!]!) + + println([[1, 1]!, [2, 2]!] == [[1, 1]!, [2, 2]!]) + println([[1, 1]!, [2, 2]!] != [[1, 2]!, [2, 3]!]) + } + { + // test sort + mut a := ['hi', '1', '5', '3'] + a.sort() + println(a) + + mut nums := [67, -3, 108, 42, 7] + nums.sort() + println(nums) + assert nums[0] == -3 + assert nums[1] == 7 + assert nums[2] == 42 + assert nums[3] == 67 + assert nums[4] == 108 + // todo(playX): add codegen for comparator fn passed + /* + nums.sort(a < b) + assert nums[0] == -3 + assert nums[1] == 7 + assert nums[2] == 42 + assert nums[3] == 67 + assert nums[4] == 108 + + mut users := [User{22, 'Peter'}, User{20, 'Bob'}, User{25, 'Alice'}] + users.sort(a.age < b.age) + assert users[0].age == 20 + assert users[1].age == 22 + assert users[2].age == 25 + assert users[0].name == 'Bob' + assert users[1].name == 'Peter' + assert users[2].name == 'Alice' + + users.sort(a.age > b.age) + assert users[0].age == 25 + assert users[1].age == 22 + assert users[2].age == 20 + + users.sort(a.name < b.name) // Test sorting by string fields + */ + } + { + // test rune sort + mut bs := [`f`, `e`, `d`, `b`, `c`, `a`] + bs.sort() + println(bs) + + /* + bs.sort(a > b) + println(bs) + assert '$bs' == '[`f`, `e`, `d`, `c`, `b`, `a`]' + + bs.sort(a < b) + println(bs) + assert '$bs' == '[`a`, `b`, `c`, `d`, `e`, `f`]' + */ + } + { + // test f32 sort + mut f := [f32(50.0), 15, 1, 79, 38, 0, 27] + f.sort() + println(f[0]) + println(f[1]) + println(f[6]) + } +} diff --git a/v_windows/v/old/vlib/v/gen/js/tests/testdata/byte_is_space.out b/v_windows/v/old/vlib/v/gen/js/tests/testdata/byte_is_space.out new file mode 100644 index 0000000..d252328 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/testdata/byte_is_space.out @@ -0,0 +1,2 @@ +true +false \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/gen/js/tests/testdata/byte_is_space.v b/v_windows/v/old/vlib/v/gen/js/tests/testdata/byte_is_space.v new file mode 100644 index 0000000..8d99196 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/testdata/byte_is_space.v @@ -0,0 +1,4 @@ +x := ' x' + +println(x[0].is_space()) +println(x[1].is_space()) diff --git a/v_windows/v/old/vlib/v/gen/js/tests/testdata/compare_ints.out b/v_windows/v/old/vlib/v/gen/js/tests/testdata/compare_ints.out new file mode 100644 index 0000000..2db62ac --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/testdata/compare_ints.out @@ -0,0 +1 @@ +2 > 1 \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/gen/js/tests/testdata/compare_ints.v b/v_windows/v/old/vlib/v/gen/js/tests/testdata/compare_ints.v new file mode 100644 index 0000000..f214466 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/testdata/compare_ints.v @@ -0,0 +1,15 @@ +if 2 > 1 { + println('2 > 1') +} + +if 2 < 1 { + println('2 < 1') +} + +if 2 == 1 { + println('2 == 1') +} + +if 2 == 0 { + println('2 == 0') +} diff --git a/v_windows/v/old/vlib/v/gen/js/tests/testdata/hw.out b/v_windows/v/old/vlib/v/gen/js/tests/testdata/hw.out new file mode 100644 index 0000000..95d09f2 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/testdata/hw.out @@ -0,0 +1 @@ +hello world \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/gen/js/tests/testdata/hw.v b/v_windows/v/old/vlib/v/gen/js/tests/testdata/hw.v new file mode 100644 index 0000000..2a082f9 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/testdata/hw.v @@ -0,0 +1 @@ +println('hello world') diff --git a/v_windows/v/old/vlib/v/gen/js/tests/testdata/string_methods.out b/v_windows/v/old/vlib/v/gen/js/tests/testdata/string_methods.out new file mode 100644 index 0000000..de2a355 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/testdata/string_methods.out @@ -0,0 +1,3 @@ +Hello V developer + Hello V +Hello V \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/gen/js/tests/testdata/string_methods.v b/v_windows/v/old/vlib/v/gen/js/tests/testdata/string_methods.v new file mode 100644 index 0000000..1594b48 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/js/tests/testdata/string_methods.v @@ -0,0 +1,3 @@ +println('d Hello V developer'.trim_left(' d')) +println(' Hello V d'.trim_right(' d')) +println('WorldHello V'.trim_prefix('World')) diff --git a/v_windows/v/old/vlib/v/gen/native/amd64.v b/v_windows/v/old/vlib/v/gen/native/amd64.v new file mode 100644 index 0000000..a6d7c67 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/native/amd64.v @@ -0,0 +1,1219 @@ +module native + +import term +import v.ast + +pub struct Amd64 { +mut: + g Gen + // arm64 specific stuff for code generation +} + +// The registers are ordered for faster generation +// push rax => 50 +// push rcx => 51 etc +enum Register { + rax + rcx + rdx + rbx + rsp + rbp + rsi + rdi + eax + edi + edx + r8 + r9 + r10 + r11 + r12 + r13 + r14 + r15 +} + +const ( + fn_arg_registers = [Register.rdi, .rsi, .rdx, .rcx, .r8, .r9] +) + +fn (mut g Gen) dec(reg Register) { + g.write16(0xff48) + match reg { + .rax { g.write8(0xc8) } + .rbx { g.write8(0xcb) } + .rcx { g.write8(0xc9) } + .rsi { g.write8(0xce) } + .rdi { g.write8(0xcf) } + .r12 { g.write8(0xc4) } + else { panic('unhandled inc $reg') } + } + g.println('dec $reg') +} + +fn (mut g Gen) inc(reg Register) { + g.write16(0xff49) + match reg { + .rcx { g.write8(0xc1) } + .r12 { g.write8(0xc4) } + else { panic('unhandled inc $reg') } + } + g.println('inc $reg') +} + +fn (mut g Gen) cmp(reg Register, size Size, val i64) { + // Second byte depends on the size of the value + match size { + ._8 { + g.write8(0x48) + g.write8(0x83) + } + ._32 { + g.write8(0x4a) + g.write8(0x81) + } + else { + panic('unhandled cmp') + } + } + // Third byte depends on the register being compared to + match reg { + .r12 { g.write8(0xfc) } + .rsi { g.write8(0x3f) } + .eax { g.write8(0xf8) } + .rbx { g.write8(0xfb) } + else { panic('unhandled cmp') } + } + match size { + ._8 { + g.write8(int(val)) + } + ._32 { + g.write32(int(val)) + } + else { + panic('unhandled cmp') + } + } + g.println('cmp $reg, $val') +} + +/* +rax // 0 + rcx // 1 + rdx // 2 + rbx // 3 + rsp // 4 + rbp // 5 + rsi // 6 + rdi // 7 +*/ +// `a == 1` +// `cmp DWORD [rbp-0x4],0x1` +fn (mut g Gen) cmp_var(var_name string, val int) { + g.write8(0x81) // 83 for 1 byte? + g.write8(0x7d) + offset := g.get_var_offset(var_name) + g.write8(0xff - offset + 1) + g.write32(val) + g.println('cmp var `$var_name` $val') +} + +// `add DWORD [rbp-0x4], 1` +fn (mut g Gen) inc_var(var_name string) { + g.write16(0x4581) // 83 for 1 byte + offset := g.get_var_offset(var_name) + g.write8(0xff - offset + 1) + g.write32(1) + g.println('inc_var `$var_name`') +} + +enum JumpOp { + je = 0x840f + jne = 0x850f + jge = 0x8d0f + jle = 0x8e0f +} + +fn (mut g Gen) cjmp(op JumpOp) int { + g.write16(u16(op)) + pos := g.pos() + g.write32(placeholder) + g.println('$op') + return int(pos) +} + +// Returns the position of the address to jump to (set later). + +fn (mut g Gen) jmp(addr int) { + g.write8(0xe9) + g.write32(addr) // 0xffffff + g.println('jmp') +} + +fn abs(a i64) i64 { + return if a < 0 { -a } else { a } +} + +fn (mut g Gen) tmp_jle(addr i64) { + // Calculate the relative offset to jump to + // (`addr` is absolute address) + offset := 0xff - int(abs(addr - g.buf.len)) - 1 + g.write8(0x7e) + g.write8(offset) + g.println('jle') +} + +fn (mut g Gen) jl(addr i64) { + offset := 0xff - int(abs(addr - g.buf.len)) - 1 + g.write8(0x7c) + g.write8(offset) + g.println('jl') +} + +fn (g &Gen) abs_to_rel_addr(addr i64) int { + return int(abs(addr - g.buf.len)) - 1 +} + +/* +fn (mut g Gen) jmp(addr i64) { + offset := 0xff - g.abs_to_rel_addr(addr) + g.write8(0xe9) + g.write8(offset) +} +*/ +fn (mut g Gen) mov64(reg Register, val i64) { + match reg { + .rax { + g.write8(0x48) + g.write8(0xb8) + } + .rcx { + g.write8(0x48) + g.write8(0xc7) + g.write8(0xc1) + } + .rbx { + g.write8(0x48) + g.write8(0xc7) + g.write8(0xc3) + } + .rsi { + g.write8(0x48) + g.write8(0xbe) + } + else { + eprintln('unhandled mov64 $reg') + } + } + g.write64(val) + g.println('mov64 $reg, $val') +} + +fn (mut g Gen) mov_reg_to_var(var_offset int, reg Register) { + // 89 7d fc mov DWORD PTR [rbp-0x4],edi + match reg { + .rax, .rsi { + g.write8(0x48) + } + else {} + } + g.write8(0x89) + match reg { + .eax, .rax { g.write8(0x45) } + .edi, .rdi { g.write8(0x7d) } + .rsi { g.write8(0x75) } + .rdx { g.write8(0x55) } + .rcx { g.write8(0x4d) } + else { verror('mov_from_reg $reg') } + } + g.write8(0xff - var_offset + 1) + g.println('mov DWORD PTR[rbp-$var_offset.hex2()],$reg') +} + +fn (mut g Gen) mov_var_to_reg(reg Register, var_offset int) { + // 8b 7d f8 mov edi,DWORD PTR [rbp-0x8] + match reg { + .rax, .rsi { + g.write8(0x48) + } + else {} + } + g.write8(0x8b) + match reg { + .eax, .rax { g.write8(0x45) } + .edi, .rdi { g.write8(0x7d) } + .rsi { g.write8(0x75) } + .rdx { g.write8(0x55) } + .rbx { g.write8(0x5d) } + .rcx { g.write8(0x4d) } + else { verror('mov_var_to_reg $reg') } + } + g.write8(0xff - var_offset + 1) + g.println('mov $reg,DWORD PTR[rbp-$var_offset.hex2()]') +} + +fn (mut g Gen) call(addr int) { + if g.pref.arch == .arm64 { + g.bl() + return + } + // Need to calculate the difference between current position (position after the e8 call) + // and the function to call. + // +5 is to get the posistion "e8 xx xx xx xx" + // Not sure about the -1. + rel := 0xffffffff - (g.buf.len + 5 - addr - 1) + // println('call addr=$addr.hex2() rel_addr=$rel.hex2() pos=$g.buf.len') + g.write8(0xe8) + g.write32(rel) + g.println('call $addr') +} + +fn (mut g Gen) syscall() { + // g.write(0x050f) + g.write8(0x0f) + g.write8(0x05) + g.println('syscall') +} + +pub fn (mut g Gen) ret() { + g.write8(0xc3) + g.println('ret') +} + +pub fn (mut g Gen) push(reg Register) { + if int(reg) < int(Register.r8) { + g.write8(0x50 + int(reg)) + } else { + g.write8(0x41) + g.write8(0x50 + int(reg) - 8) + } + /* + match reg { + .rbp { g.write8(0x55) } + else {} + } + */ + g.println('push $reg') +} + +pub fn (mut g Gen) pop(reg Register) { + g.write8(0x58 + int(reg)) + // TODO r8... + g.println('pop $reg') +} + +pub fn (mut g Gen) sub32(reg Register, val int) { + g.write8(0x48) + g.write8(0x81) + g.write8(0xe8 + int(reg)) // TODO rax is different? + g.write32(val) + g.println('sub32 $reg,$val.hex2()') +} + +pub fn (mut g Gen) sub8(reg Register, val int) { + g.write8(0x48) + g.write8(0x83) + g.write8(0xe8 + int(reg)) // TODO rax is different? + g.write8(val) + g.println('sub8 $reg,$val.hex2()') +} + +pub fn (mut g Gen) sub(reg Register, val int) { + g.write8(0x48) + g.write8(0x81) + g.write8(0xe8 + int(reg)) // TODO rax is different? + g.write32(val) + g.println('add $reg,$val.hex2()') +} + +pub fn (mut g Gen) add(reg Register, val int) { + if reg != .rax { + panic('add only works with .rax') + } + g.write8(0x48) + g.write8(0x05) + g.write32(val) + g.println('add $reg,$val.hex2()') +} + +pub fn (mut g Gen) add8(reg Register, val int) { + g.write8(0x48) + g.write8(0x83) + // g.write8(0xe8 + reg) // TODO rax is different? + g.write8(0xc4) + g.write8(val) + g.println('add8 $reg,$val.hex2()') +} + +fn (mut g Gen) add8_var(reg Register, var_offset int) { + g.write8(0x03) + match reg { + .eax, .rax { g.write8(0x45) } + else { verror('add8_var') } + } + g.write8(0xff - var_offset + 1) + g.println('add8 $reg,DWORD PTR[rbp-$var_offset.hex2()]') +} + +fn (mut g Gen) sub8_var(reg Register, var_offset int) { + g.write8(0x2b) + match reg { + .eax, .rax { g.write8(0x45) } + else { verror('sub8_var') } + } + g.write8(0xff - var_offset + 1) + g.println('sub8 $reg,DWORD PTR[rbp-$var_offset.hex2()]') +} + +fn (mut g Gen) div8_var(reg Register, var_offset int) { + if reg == .rax || reg == .eax { + g.mov_var_to_reg(.rbx, var_offset) + g.div_reg(.rax, .rbx) + g.mov_reg_to_var(var_offset, .rax) + } else { + panic('div8_var invalid source register') + } +} + +fn (mut g Gen) mul8_var(reg Register, var_offset int) { + g.write8(0x0f) + g.write8(0xaf) + match reg { + .eax, .rax { g.write8(0x45) } + else { verror('mul8_var') } + } + g.write8(0xff - var_offset + 1) + g.println('mul8 $reg,DWORD PTR[rbp-$var_offset.hex2()]') +} + +fn (mut g Gen) leave() { + g.write8(0xc9) + g.println('leave') +} + +// returns label's relative address +pub fn (mut g Gen) gen_loop_start(from int) int { + g.mov(.r12, from) + label := g.buf.len + g.inc(.r12) + return label +} + +pub fn (mut g Gen) gen_loop_end(to int, label int) { + g.cmp(.r12, ._8, to) + g.jl(label) +} + +pub fn (mut g Gen) save_main_fn_addr() { + g.main_fn_addr = i64(g.buf.len) +} + +pub fn (mut g Gen) allocate_string(s string, opsize int) int { + g.strings << s + str_pos := g.buf.len + opsize + g.str_pos << str_pos + return 0 +} + +pub fn (mut g Gen) cld_repne_scasb() { + g.write8(0xfc) + g.println('cld') + g.write8(0xf2) + g.write8(0xae) + g.println('repne scasb') +} + +pub fn (mut g Gen) xor(r Register, v int) { + if v == -1 { + match r { + .rcx { + g.write8(0x48) + g.write8(0x83) + g.write8(0xf1) + g.write8(0xff) + g.println('xor rcx, -1') + } + else { + verror('unhandled xor') + } + } + } else { + verror('unhandled xor') + } +} + +// return length in .rax of string pointed by given register +pub fn (mut g Gen) inline_strlen(r Register) { + g.mov_reg(.rdi, r) + g.mov(.rcx, -1) + g.mov(.eax, 0) + g.cld_repne_scasb() + g.xor(.rcx, -1) + g.dec(.rcx) + g.mov_reg(.rax, .rcx) + g.println('strlen rax, $r') +} + +// TODO: strlen of string at runtime +pub fn (mut g Gen) gen_print_reg(r Register, n int) { + mystrlen := true + g.mov_reg(.rsi, r) + if mystrlen { + g.inline_strlen(.rsi) + g.mov_reg(.rdx, .rax) + } else { + g.mov(.edx, n) + } + g.mov(.eax, g.nsyscall_write()) + g.mov(.edi, 1) + g.syscall() +} + +pub fn (mut g Gen) gen_print(s string) { + // + // qq := s + '\n' + // + g.mov(.eax, g.nsyscall_write()) + g.mov(.edi, 1) + // segment_start + 0x9f) // str pos // placeholder + g.mov64(.rsi, g.allocate_string(s, 2)) // for rsi its 2 + g.mov(.edx, s.len) // len + g.syscall() +} + +fn (mut g Gen) nsyscall_write() int { + match g.pref.os { + .linux { + return 1 + } + .macos { + return 0x2000004 + } + else { + verror('unsupported exit syscall for this platform') + } + } + return 0 +} + +fn (mut g Gen) nsyscall_exit() int { + match g.pref.os { + .linux { + return 60 + } + .macos { + return 0x2000001 + } + else { + verror('unsupported exit syscall for this platform') + } + } + return 0 +} + +pub fn (mut a Amd64) gen_exit(mut g Gen, node ast.Expr) { + g.gen_amd64_exit(node) +} + +pub fn (mut g Gen) gen_amd64_exit(expr ast.Expr) { + // ret value + match expr { + ast.CallExpr { + right := expr.return_type + verror('native exit builtin: Unsupported call $right') + } + ast.Ident { + var_offset := g.get_var_offset(expr.name) + g.mov_var_to_reg(.edi, var_offset) + } + ast.IntegerLiteral { + g.mov(.edi, expr.val.int()) + } + else { + verror('native builtin exit expects a numeric argument') + } + } + g.mov(.eax, g.nsyscall_exit()) + g.syscall() +} + +fn (mut g Gen) mov(reg Register, val int) { + if val == -1 { + match reg { + .rax { + g.write8(0x48) + g.write8(0xc7) + g.write8(0xc0) + g.write32(-1) + return + } + .rcx { + g.write8(0x48) + g.write8(0xc7) + g.write8(0xc1) + g.write32(-1) + return + } + else { + verror('unhandled mov $reg, -1') + } + } + } + if val == 0 { + // Optimise to xor reg, reg when val is 0 + match reg { + .eax, .rax { + g.write8(0x31) + g.write8(0xc0) + } + .edi, .rdi { + g.write8(0x31) + g.write8(0xff) + } + .rcx { + g.write8(0x48) + g.write8(0x31) + g.write8(0xc7) + } + .rdx { + g.write8(0x48) + g.write8(0x31) + g.write8(0xd2) + } + .edx { + g.write8(0x31) + g.write8(0xd2) + } + .rsi { + g.write8(0x48) + g.write8(0x31) + g.write8(0xf6) + } + .r12 { + g.write8(0x4d) + g.write8(0x31) + g.write8(0xe4) + } + else { + verror('unhandled mov $reg, $reg') + } + } + g.println('xor $reg, $reg') + } else { + match reg { + .eax, .rax { + g.write8(0xb8) + } + .edi, .rdi { + g.write8(0xbf) + } + .rcx { + g.write8(0xc7) + } + .edx { + g.write8(0xba) + } + .rsi { + g.write8(0x48) + g.write8(0xbe) + } + .r12 { + g.write8(0x41) + g.write8(0xbc) // r11 is 0xbb etc + } + else { + verror('unhandled mov $reg') + } + } + g.write32(val) + g.println('mov $reg, $val') + } +} + +fn (mut g Gen) mul_reg(a Register, b Register) { + if a != .rax { + panic('mul always operates on rax') + } + match b { + .rax { + g.write8(0x48) + g.write8(0xf7) + g.write8(0xe8) + } + .rbx { + g.write8(0x48) + g.write8(0xf7) + g.write8(0xeb) + } + else { + panic('unhandled div $a') + } + } + g.println('mul $a') +} + +fn (mut g Gen) div_reg(a Register, b Register) { + if a != .rax { + panic('div always operates on rax') + } + match b { + .rax { + g.write8(0x48) + g.write8(0xf7) + g.write8(0xf8) + } + .rbx { + g.mov(.edx, 0) + g.write8(0x48) + g.write8(0xf7) + g.write8(0xfb) // idiv ebx + } + else { + panic('unhandled div $a') + } + } + g.println('div $a') +} + +fn (mut g Gen) sub_reg(a Register, b Register) { + if a == .rax && b == .rbx { + g.write8(0x48) + g.write8(0x29) + g.write8(0xd8) + } else { + panic('unhandled add $a, $b') + } + g.println('sub $a, $b') +} + +fn (mut g Gen) add_reg(a Register, b Register) { + if a == .rax && b == .rbx { + g.write8(0x48) + g.write8(0x01) + g.write8(0xd8) + } else if a == .rax && b == .rdi { + g.write8(0x48) + g.write8(0x01) + g.write8(0xf8) + } else { + panic('unhandled add $a, $b') + } + g.println('add $a, $b') +} + +fn (mut g Gen) mov_reg(a Register, b Register) { + if a == .rbp && b == .rsp { + g.write8(0x48) + g.write8(0x89) + } else if a == .rdx && b == .rax { + g.write8(0x48) + g.write8(0x89) + g.write8(0xc2) + } else if a == .rax && b == .rcx { + g.write8(0x48) + g.write8(0x89) + g.write8(0xc8) + } else if a == .rax && b == .rdi { + g.write8(0x48) + g.write8(0x89) + g.write8(0xf8) + } else if a == .rdi && b == .rsi { + g.write8(0x48) + g.write8(0x89) + g.write8(0xf7) + } else if a == .rsi && b == .rax { + g.write8(0x48) + g.write8(0x89) + g.write8(0xc6) + } else { + verror('unhandled mov_reg combination for $a $b') + } + g.println('mov $a, $b') +} + +// generates `mov rbp, rsp` +fn (mut g Gen) mov_rbp_rsp() { + g.write8(0x48) + g.write8(0x89) + g.write8(0xe5) + g.println('mov rbp, rsp') +} + +pub fn (mut g Gen) call_fn(node ast.CallExpr) { + if g.pref.arch == .arm64 { + g.call_fn_arm64(node) + return + } + name := node.name + mut n := name + if !n.contains('.') { + n = 'main.$n' + } + eprintln('call fn ($n)') + addr := g.fn_addr[n] + if addr == 0 { + verror('fn addr of `$name` = 0') + } + // Copy values to registers (calling convention) + // g.mov(.eax, 0) + for i in 0 .. node.args.len { + expr := node.args[i].expr + match expr { + ast.IntegerLiteral { + // `foo(2)` => `mov edi,0x2` + g.mov(native.fn_arg_registers[i], expr.val.int()) + } + ast.Ident { + // `foo(x)` => `mov edi,DWORD PTR [rbp-0x8]` + var_offset := g.get_var_offset(expr.name) + if g.pref.is_verbose { + println('i=$i fn name= $name offset=$var_offset') + println(int(native.fn_arg_registers[i])) + } + g.mov_var_to_reg(native.fn_arg_registers[i], var_offset) + } + else { + verror('unhandled call_fn (name=$name) node: ' + expr.type_name()) + } + } + } + if node.args.len > 6 { + verror('more than 6 args not allowed for now') + } + g.call(int(addr)) + g.println('fn call `${name}()`') + // println('call $name $addr') +} + +fn (mut g Gen) assign_stmt(node ast.AssignStmt) { + // `a := 1` | `a,b := 1,2` + for i, left in node.left { + right := node.right[i] + name := left.str() + // if left is ast.Ident { + // ident := left as ast.Ident + match right { + ast.IntegerLiteral { + // g.allocate_var(name, 4, right.val.int()) + match node.op { + .plus_assign { + dest := g.get_var_offset(name) + g.mov_var_to_reg(.rax, dest) + g.add(.rax, right.val.int()) + g.mov_reg_to_var(dest, .rax) + } + .minus_assign { + dest := g.get_var_offset(name) + g.mov_var_to_reg(.rax, dest) + g.mov_var_to_reg(.rbx, g.get_var_offset(name)) + g.sub_reg(.rax, .rbx) + g.mov_reg_to_var(dest, .rax) + } + .mult_assign { + dest := g.get_var_offset(name) + g.mov_var_to_reg(.rax, dest) + g.mov_var_to_reg(.rbx, g.get_var_offset(name)) + g.mul_reg(.rax, .rbx) + g.mov_reg_to_var(dest, .rax) + } + .div_assign { + dest := g.get_var_offset(name) + g.mov_var_to_reg(.rax, dest) + g.mov_var_to_reg(.rbx, g.get_var_offset(name)) + g.div_reg(.rax, .rbx) + g.mov_reg_to_var(dest, .rax) + } + .decl_assign { + g.allocate_var(name, 4, right.val.int()) + } + .assign { + match node.left_types[i] { + 7 { // ast.IndexExpr { + ie := node.left[i] as ast.IndexExpr + bracket := name.index('[') or { verror('bracket expected') } + var_name := name[0..bracket] + mut dest := g.get_var_offset(var_name) + index := ie.index as ast.IntegerLiteral + dest += index.val.int() * 8 + // TODO check if out of bounds access + g.mov(.rax, right.val.int()) + g.mov_reg_to_var(dest, .rax) + // eprintln('${var_name}[$index] = ${right.val.int()}') + } + else { + tn := node.left[i].type_name() + dump(node.left_types) + verror('unhandled assign type: $tn') + } + } + } + else { + eprintln('ERROR 2') + dump(node) + } + } + } + ast.InfixExpr { + // eprintln('infix') dump(node) dump(right) + g.infix_expr(right) + offset := g.allocate_var(name, 4, 0) + // `mov DWORD PTR [rbp-0x8],eax` + if g.pref.is_verbose { + println('infix assignment $name offset=$offset.hex2()') + } + g.mov_reg_to_var(offset, .eax) + } + ast.Ident { + // eprintln('identr') dump(node) dump(right) + match node.op { + .plus_assign { + dest := g.get_var_offset(name) + g.mov_var_to_reg(.rax, dest) + g.add8_var(.rax, g.get_var_offset(right.name)) + g.mov_reg_to_var(dest, .rax) + } + .minus_assign { + dest := g.get_var_offset(name) + g.mov_var_to_reg(.rax, dest) + g.mov_var_to_reg(.rbx, g.get_var_offset(right.name)) + g.sub_reg(.rax, .rbx) + g.mov_reg_to_var(dest, .rax) + } + .div_assign { + // this should be called when `a /= b` but it's not :? + dest := g.get_var_offset(name) + g.mov_var_to_reg(.rax, dest) + g.mov_var_to_reg(.rbx, g.get_var_offset(right.name)) + g.div_reg(.rax, .rbx) + g.mov_reg_to_var(dest, .rax) + } + .decl_assign { + dest := g.allocate_var(name, 4, 0) + g.mov_var_to_reg(.rax, g.get_var_offset(right.name)) + g.mov_reg_to_var(dest, .rax) + } + .assign { + g.mov_var_to_reg(.rax, g.get_var_offset(right.name)) + g.mov_reg_to_var(g.get_var_offset(name), .rax) + } + else { + eprintln('TODO: unhandled assign ident case') + dump(node) + } + } + // a += b + } + ast.StructInit { + sym := g.table.get_type_symbol(right.typ) + // println(sym) + // println(typeof(sym.info)) + info := sym.info as ast.Struct + for field in info.fields { + field_name := name + '.' + field.name + println(field_name) + g.allocate_var(field_name, 4, 0) + } + } + ast.ArrayInit { + // check if array is empty + mut pos := g.allocate_array(name, 8, right.exprs.len) + // allocate array of right.exprs.len vars + for e in right.exprs { + match e { + ast.IntegerLiteral { + g.mov(.rax, e.val.int()) + g.mov_reg_to_var(pos, .rax) + pos += 8 + } + ast.StringLiteral { + g.mov64(.rsi, g.allocate_string('$e.val', 2)) // for rsi its 2 + g.mov_reg_to_var(pos, .rsi) + pos += 8 + } + else { + dump(e) + verror('unhandled array init type') + } + } + } + } + ast.IndexExpr { + // a := arr[0] + offset := g.allocate_var(name, 4, 0) + if g.pref.is_verbose { + println('infix assignment $name offset=$offset.hex2()') + } + ie := node.right[i] as ast.IndexExpr + var_name := ie.left.str() + mut dest := g.get_var_offset(var_name) + if ie.index is ast.IntegerLiteral { + index := ie.index + dest += index.val.int() * 8 + g.mov_var_to_reg(.rax, dest) + } else if ie.index is ast.Ident { + ident := ie.index + var_offset := g.get_var_offset(ident.name) + g.mov_var_to_reg(.edi, var_offset) + g.mov_var_to_reg(.rax, dest) + g.add_reg(.rax, .rdi) + } else { + verror('only integers and idents can be used as indexes') + } + // TODO check if out of bounds access + g.mov_reg_to_var(offset, .eax) + } + ast.StringLiteral { + dest := g.allocate_var(name, 4, 0) + ie := node.right[i] as ast.StringLiteral + g.mov64(.rsi, g.allocate_string(ie.str(), 2)) // for rsi its 2 + g.mov_reg_to_var(dest, .rsi) + } + ast.CallExpr { + dest := g.allocate_var(name, 4, 0) + g.call_fn(right) + g.mov_reg_to_var(dest, .rax) + g.mov_var_to_reg(.rsi, dest) + } + else { + // dump(node) + g.error_with_pos('native assign_stmt unhandled expr: ' + right.type_name(), + right.position()) + } + } + // } + } +} + +fn (mut g Gen) infix_expr(node ast.InfixExpr) { + if g.pref.is_verbose { + println('infix expr op=$node.op') + } + // TODO + if node.left is ast.InfixExpr { + verror('only simple expressions are supported right now (not more than 2 operands)') + } + match mut node.left { + ast.Ident { + g.mov_var_to_reg(.eax, g.get_var_offset(node.left.name)) + } + else {} + } + if mut node.right is ast.Ident { + var_offset := g.get_var_offset(node.right.name) + match node.op { + .plus { g.add8_var(.eax, var_offset) } + .mul { g.mul8_var(.eax, var_offset) } + .div { g.div8_var(.eax, var_offset) } + .minus { g.sub8_var(.eax, var_offset) } + else {} + } + } +} + +fn (mut g Gen) if_expr(node ast.IfExpr) { + branch := node.branches[0] + infix_expr := branch.cond as ast.InfixExpr + mut cjmp_addr := 0 // location of `jne *00 00 00 00*` + match mut infix_expr.left { + ast.IntegerLiteral { + match mut infix_expr.right { + ast.IntegerLiteral { + // 3 < 4 + a0 := infix_expr.left.val.int() + // a0 := (infix_expr.left as ast.IntegerLiteral).val.int() + a1 := (infix_expr.right as ast.IntegerLiteral).val.int() + // TODO. compute at compile time + g.mov(.eax, a0) + g.cmp(.eax, ._32, a1) + } + ast.Ident { + // 3 < var + // lit := infix_expr.right as ast.IntegerLiteral + // g.cmp_var(infix_expr.left.name, lit.val.int()) + // +not + verror('unsupported if construction') + } + else { + verror('unsupported if construction') + } + } + } + ast.Ident { + match mut infix_expr.right { + ast.IntegerLiteral { + // var < 4 + lit := infix_expr.right as ast.IntegerLiteral + g.cmp_var(infix_expr.left.name, lit.val.int()) + } + ast.Ident { + // var < var2 + verror('unsupported if construction') + } + else { + verror('unsupported if construction') + } + } + } + else { + dump(node) + verror('unhandled infix.left') + } + } + cjmp_addr = match infix_expr.op { + .gt { + g.cjmp(.jle) + } + .lt { + g.cjmp(.jge) + } + .ne { + g.cjmp(.je) + } + .eq { + g.cjmp(.jne) + } + else { + g.cjmp(.je) + } + } + g.stmts(branch.stmts) + // Now that we know where we need to jump if the condition is false, update the `jne` call. + // The value is the relative address, difference between current position and the location + // after `jne 00 00 00 00` + // println('after if g.pos=$g.pos() jneaddr=$cjmp_addr') + g.write32_at(cjmp_addr, int(g.pos() - cjmp_addr - 4)) // 4 is for "00 00 00 00" + + if node.has_else { + verror('else statements not yet supported') + } +} + +fn (mut g Gen) for_stmt(node ast.ForStmt) { + infix_expr := node.cond as ast.InfixExpr + // g.mov(.eax, 0x77777777) + mut jump_addr := 0 // location of `jne *00 00 00 00*` + start := g.pos() + match mut infix_expr.left { + ast.Ident { + lit := infix_expr.right as ast.IntegerLiteral + g.cmp_var(infix_expr.left.name, lit.val.int()) + jump_addr = g.cjmp(.jge) + } + else { + verror('unhandled infix.left') + } + } + g.stmts(node.stmts) + // Go back to `cmp ...` + // Diff between `jmp 00 00 00 00 X` and `cmp` + g.jmp(int(0xffffffff - (g.pos() + 5 - start) + 1)) + // Update the jump addr to current pos + g.write32_at(jump_addr, int(g.pos() - jump_addr - 4)) // 4 is for "00 00 00 00" + g.println('jmp after for') +} + +fn (mut g Gen) fn_decl(node ast.FnDecl) { + if g.pref.is_verbose { + println(term.green('\n$node.name:')) + } + // if g.is_method + if node.is_deprecated { + eprintln('fn_decl: $node.name is deprecated') + } + if node.is_builtin { + eprintln('fn_decl: $node.name is builtin') + } + g.stack_var_pos = 0 + is_main := node.name == 'main.main' + // println('saving addr $node.name $g.buf.len.hex2()') + if is_main { + g.save_main_fn_addr() + } else { + g.register_function_address(node.name) + } + if g.pref.arch == .arm64 { + g.fn_decl_arm64(node) + return + } + + g.push(.rbp) + g.mov_rbp_rsp() + locals_count := node.scope.objects.len + node.params.len + stackframe_size := (locals_count * 8) + 0x10 + g.sub8(.rsp, stackframe_size) + + if node.params.len > 0 { + // g.mov(.r12, 0x77777777) + } + // Copy values from registers to local vars (calling convention) + mut offset := 0 + for i in 0 .. node.params.len { + name := node.params[i].name + // TODO optimize. Right now 2 mov's are used instead of 1. + g.allocate_var(name, 4, 0) + // `mov DWORD PTR [rbp-0x4],edi` + offset += 4 + g.mov_reg_to_var(offset, native.fn_arg_registers[i]) + } + // + g.stmts(node.stmts) + if is_main { + // println('end of main: gen exit') + zero := ast.IntegerLiteral{} + g.gen_exit(zero) + g.ret() + return + } + // g.leave() + g.add8(.rsp, stackframe_size) + g.pop(.rbp) + g.ret() +} + +pub fn (mut x Amd64) allocate_var(name string, size int, initial_val int) { + // do nothing as interface call is crashing +} + +pub fn (mut g Gen) allocate_array(name string, size int, items int) int { + pos := g.allocate_var(name, size, items) + g.stack_var_pos += (size * items) + return pos +} + +pub fn (mut g Gen) allocate_var(name string, size int, initial_val int) int { + // `a := 3` => + // `move DWORD [rbp-0x4],0x3` + match size { + 1 { + // BYTE + g.write8(0xc6) + g.write8(0x45) + } + 4 { + // DWORD + g.write8(0xc7) + g.write8(0x45) + } + 8 { + // QWORD + g.write8(0x48) + g.write8(0xc7) + g.write8(0x45) + } + else { + verror('allocate_var: bad size $size') + } + } + // Generate N in `[rbp-N]` + n := g.stack_var_pos + size + g.write8(0xff - n + 1) + g.stack_var_pos += size + g.var_offset[name] = g.stack_var_pos + // Generate the value assigned to the variable + g.write32(initial_val) + // println('allocate_var(size=$size, initial_val=$initial_val)') + g.println('mov DWORD [rbp-$n.hex2()],$initial_val (Allocate var `$name`)') + return g.stack_var_pos +} diff --git a/v_windows/v/old/vlib/v/gen/native/arm64.v b/v_windows/v/old/vlib/v/gen/native/arm64.v new file mode 100644 index 0000000..f4eb5aa --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/native/arm64.v @@ -0,0 +1,185 @@ +module native + +import v.ast + +enum Arm64Register { + x0 + x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10 + x11 + x12 + x13 + x14 + x15 + x16 +} + +pub struct Arm64 { +mut: + g Gen + // arm64 specific stuff for code generation +} + +pub fn (mut x Arm64) allocate_var(name string, size int, initial_val int) { + eprintln('TODO: allocating var on arm64 ($name) = $size = $initial_val') +} + +fn (mut g Gen) mov_arm(reg Arm64Register, val u64) { + // m := u64(0xffff) + // x := u64(val) + // println('========') + // println(x & ~m) + // println(x & ~(m << 16)) + // g.write32(0x777777) + r := int(reg) + if r == 0 && val == 1 { + g.write32(0xd2800020) + g.println('mov x0, 1') + } else if r >= 0 && r <= 16 { + g.write32(0xd2800000 + int(r) + (int(val) << 5)) + g.println('mov x$r, $val') + } else { + verror('mov_arm unsupported values') + } + /* + if 1 ^ (x & ~m) != 0 { + // println('yep') + g.write32(int(u64(0x52800000) | u64(r) | x << 5)) + g.write32(0x88888888) + g.write32(int(u64(0x52800000) | u64(r) | x >> 11)) + } else if 1 ^ (x & ~(m << 16)) != 0 { + // g.write32(int(u64(0x52800000) | u64(r) | x >> 11)) + // println('yep2') + // g.write32(0x52a00000 | r | val >> 11) + } + */ +} + +pub fn (mut g Gen) fn_decl_arm64(node ast.FnDecl) { + g.gen_arm64_helloworld() + // TODO +} + +pub fn (mut g Gen) call_fn_arm64(node ast.CallExpr) { + name := node.name + // println('call fn $name') + addr := g.fn_addr[name] + if addr == 0 { + verror('fn addr of `$name` = 0') + } + // Copy values to registers (calling convention) + // g.mov_arm(.eax, 0) + for i in 0 .. node.args.len { + expr := node.args[i].expr + match expr { + ast.IntegerLiteral { + // `foo(2)` => `mov edi,0x2` + // g.mov_arm(native.fn_arg_registers[i], expr.val.int()) + } + /* + ast.Ident { + // `foo(x)` => `mov edi,DWORD PTR [rbp-0x8]` + var_offset := g.get_var_offset(expr.name) + if g.pref.is_verbose { + println('i=$i fn name= $name offset=$var_offset') + println(int(native.fn_arg_registers[i])) + } + g.mov_var_to_reg(native.fn_arg_registers[i], var_offset) + } + */ + else { + verror('unhandled call_fn (name=$name) node: ' + expr.type_name()) + } + } + } + if node.args.len > 6 { + verror('more than 6 args not allowed for now') + } + g.call(int(addr)) + g.println('fn call `${name}()`') + // println('call $name $addr') +} + +fn (mut g Gen) gen_arm64_helloworld() { + if g.pref.os == .linux { + g.mov_arm(.x0, 1) + g.adr(.x1, 0x10) + g.mov_arm(.x2, 13) + g.mov_arm(.x8, 64) // write (linux-arm64) + g.svc() + } else { + g.mov_arm(.x0, 0) + g.mov_arm(.x16, 1) + g.svc() + } + zero := ast.IntegerLiteral{} + g.gen_exit(zero) + g.write_string('Hello World!\n') + g.write8(0) // padding? + g.write8(0) + g.write8(0) +} + +fn (mut g Gen) adr(r Arm64Register, delta int) { + g.write32(0x10000000 | int(r) | (delta << 4)) + g.println('adr $r, $delta') +} + +fn (mut g Gen) bl() { + // g.write32(0xa9400000) + g.write32(0x94000000) + g.println('bl 0') +} + +fn (mut g Gen) svc() { + g.write32(0xd4001001) + g.println('svc 0x80') +} + +pub fn (mut c Arm64) gen_exit(mut g Gen, expr ast.Expr) { + mut return_code := u64(0) + match expr { + ast.IntegerLiteral { + return_code = expr.val.u64() + } + else { + verror('native builtin exit expects a numeric argument') + } + } + match c.g.pref.os { + .macos { + c.g.mov_arm(.x0, return_code) + c.g.mov_arm(.x16, 1) // syscall exit + } + .linux { + c.g.mov_arm(.x16, return_code) + c.g.mov_arm(.x8, 93) + c.g.mov_arm(.x0, 0) + } + else { + verror('unsupported os $c.g.pref.os') + } + } + g.svc() +} + +pub fn (mut g Gen) gen_arm64_exit(expr ast.Expr) { + match expr { + ast.IntegerLiteral { + g.mov_arm(.x16, expr.val.u64()) + } + else { + verror('native builtin exit expects a numeric argument') + } + } + g.mov_arm(.x0, 0) + g.svc() +} diff --git a/v_windows/v/old/vlib/v/gen/native/elf.v b/v_windows/v/old/vlib/v/gen/native/elf.v new file mode 100644 index 0000000..88084fe --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/native/elf.v @@ -0,0 +1,113 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module native + +const ( + mag0 = byte(0x7f) + mag1 = `E` + mag2 = `L` + mag3 = `F` + ei_class = 4 + elfclass64 = 2 + elfdata2lsb = 1 + ev_current = 1 +) + +// ELF file types +const ( + elf_osabi = 0 + et_rel = 1 + et_exec = 2 + et_dyn = 3 + e_machine_amd64 = 0x3e + e_machine_arm64 = 0xb7 + shn_xindex = 0xffff + sht_null = 0 +) + +const ( + segment_start = 0x400000 + placeholder = 0 + sevens = 0x77777777 +) + +pub fn (mut g Gen) generate_elf_header() { + g.buf << [byte(native.mag0), native.mag1, native.mag2, native.mag3] + g.buf << native.elfclass64 // file class + g.buf << native.elfdata2lsb // data encoding + g.buf << native.ev_current // file version + g.buf << native.elf_osabi + g.write64(0) // et_rel) // et_rel for .o + g.write16(2) // e_type + if g.pref.arch == .arm64 { + g.write16(native.e_machine_arm64) + } else { + g.write16(native.e_machine_amd64) + } + g.write32(native.ev_current) // e_version + eh_size := 0x40 + phent_size := 0x38 + g.write64(native.segment_start + eh_size + phent_size) // e_entry + g.write64(0x40) // e_phoff + g.write64(0) // e_shoff + g.write32(0) // e_flags + g.write16(eh_size) // e_ehsize + g.write16(phent_size) // e_phentsize + g.write16(1) // e_phnum + g.write16(0) // e_shentsize + g.write16(0) // e_shnum (number of sections) + g.write16(0) // e_shstrndx + // Elf64_Phdr + g.write32(1) // p_type + g.write32(5) // p_flags + g.write64(0) // p_offset + g.write64(native.segment_start) // p_vaddr addr:050 + g.write64(native.segment_start) // + g.file_size_pos = i64(g.buf.len) + g.write64(0) // p_filesz PLACEHOLDER, set to file_size later // addr: 060 + g.write64(0) // p_memsz + g.write64(0x1000) // p_align + // user code starts here at + // address: 00070 and a half + if g.pref.is_verbose { + eprintln('code_start_pos = $g.buf.len.hex()') + } + g.code_start_pos = i64(g.buf.len) + g.debug_pos = g.buf.len + g.call(native.placeholder) // call main function, it's not guaranteed to be the first, we don't know its address yet + g.println('; call fn main') +} + +pub fn (mut g Gen) generate_elf_footer() { + // Return 0 + /* + g.mov(.edi, 0) // ret value + g.mov(.eax, 60) + g.syscall() + */ + // Strings table + // Loop thru all strings and set the right addresses + for i, s in g.strings { + g.write64_at(native.segment_start + g.buf.len, int(g.str_pos[i])) + g.write_string(s) + g.write8(0) + } + // Now we know the file size, set it + file_size := g.buf.len + g.write64_at(file_size, g.file_size_pos) // set file size 64 bit value + g.write64_at(file_size, g.file_size_pos + 8) + if g.pref.arch == .arm64 { + bl_next := u32(0x94000001) + g.write32_at(g.code_start_pos, int(bl_next)) + } else { + // amd64 + // call main function, it's not guaranteed to be the first + // we generated call(0) ("e8 0") + // now need to replace "0" with a relative address of the main function + // +1 is for "e8" + // -5 is for "e8 00 00 00 00" + g.write32_at(g.code_start_pos + 1, int(g.main_fn_addr - g.code_start_pos) - 5) + } + g.create_executable() +} diff --git a/v_windows/v/old/vlib/v/gen/native/elf_obj.v b/v_windows/v/old/vlib/v/gen/native/elf_obj.v new file mode 100644 index 0000000..d3b7f42 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/native/elf_obj.v @@ -0,0 +1,157 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module native + +/* +This file is unused right now, since binaries without sections +are generated. + +But it will be necessary once we have dynamic linking. +*/ +enum SectionType { + null = 0 + progbits = 1 + symtab = 2 + strtab = 3 + rela = 4 +} + +struct SectionConfig { + name string + typ SectionType + flags i64 + data voidptr + is_saa bool + datalen i64 + link int + info int + align i64 + entsize i64 +} + +fn (mut g Gen) section_header(c SectionConfig) { + g.write32(g.sect_header_name_pos) + g.sect_header_name_pos += c.name.len + 1 + g.write32(int(c.typ)) + g.write64(c.flags) + g.write64(0) // sh_addr + g.write64(g.offset) // offset + g.offset += c.datalen + 1 + g.write64(c.datalen) + g.write32(c.link) + g.write32(c.info) + g.write64(c.align) + g.write64(c.entsize) +} + +fn genobj() { + /* + // SHN_UNDEF + mut g := Gen{} + nr_sections := 7 + g.section_header(SectionConfig{ + name: '' + typ: .null + flags:0 + data: 0 + is_saa: false + link: 0 + info:0 + align:0 + entsize: 0 + }) + + /* + for sect in sections { + g.section_header(SectionConfig{ + name:0 + typ: sect.typ + flags: sect.flags + data: sect.data + is_saa: true + datalen: sect.len + link: 0 + info: 0 + align: sect.align + entsize: sect.entsize + }) + + } + */ + + g.section_header(SectionConfig{ + name: '.DATA' + typ: .progbits + flags: 0x2 + //data: sect.data + is_saa: true + datalen: 0xd + link: 0 + info: 0 + align: 1 + entsize: 0 + }) + + g.section_header(SectionConfig{ + name: '.TEXT' + typ: .progbits + flags: 0x2 + //data: sect.data + is_saa: true + datalen: 0xd + link: 0 + info: 0 + align: 1 + entsize: 0 + }) + g.section_header(SectionConfig{ + name: '.shstrtab' + typ: .strtab + flags: 0x2 + //data: sect.data + is_saa: true + datalen: 0x22 + link: 0 + info: 0 + align: 1 + entsize: 0 + }) + g.section_header(SectionConfig{ + name: '.symtab' + typ: .symtab + flags: 0x2 + //data: sect.data + is_saa: true + datalen: 0xd + link: 0 + info: 0 + align: 1 + entsize: 0 + }) + g.section_header(SectionConfig{ + name: '.strtab' + typ: .symtab + flags: 0x2 + //data: sect.data + is_saa: true + datalen: 0xd + link: 0 + info: 0 + align: 1 + entsize: 0 + }) + g.section_header(SectionConfig{ + name: '.rela.TEXT' + typ: .rela + flags: 0x0 + //data: sect.data + is_saa: true + datalen: 0x18 + link: 4 + info: 2 + align: 8 + entsize: 0x18 + }) + */ +} diff --git a/v_windows/v/old/vlib/v/gen/native/gen.v b/v_windows/v/old/vlib/v/gen/native/gen.v new file mode 100644 index 0000000..8df68a9 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/native/gen.v @@ -0,0 +1,454 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module native + +import os +import strings +import v.ast +import v.util +import v.token +import v.errors +import v.pref +import term + +pub const builtins = ['println', 'exit'] + +interface CodeGen { +mut: + g Gen + gen_exit(mut g Gen, expr ast.Expr) + // XXX WHY gen_exit fn (expr ast.Expr) +} + +[heap] +pub struct Gen { + out_name string + pref &pref.Preferences // Preferences shared from V struct +mut: + cgen CodeGen + table &ast.Table + buf []byte + sect_header_name_pos int + offset i64 + str_pos []i64 + strings []string // TODO use a map and don't duplicate strings + file_size_pos i64 + main_fn_addr i64 + code_start_pos i64 // location of the start of the assembly instructions + fn_addr map[string]i64 + var_offset map[string]int // local var stack offset + stack_var_pos int + debug_pos int + errors []errors.Error + warnings []errors.Warning + syms []Symbol + relocs []Reloc + size_pos []int + nlines int +} + +enum Size { + _8 + _16 + _32 + _64 +} + +fn get_backend(arch pref.Arch) ?CodeGen { + match arch { + .arm64 { + return Arm64{} + } + .amd64 { + return Amd64{} + } + else {} + } + return error('unsupported architecture') +} + +pub fn gen(files []&ast.File, table &ast.Table, out_name string, pref &pref.Preferences) (int, int) { + mut g := &Gen{ + table: table + sect_header_name_pos: 0 + out_name: out_name + pref: pref + // TODO: workaround, needs to support recursive init + cgen: get_backend(pref.arch) or { + eprintln('No available backend for this configuration. Use `-a arm64` or `-a amd64`.') + exit(1) + } + } + g.cgen.g = g + g.generate_header() + for file in files { + if file.warnings.len > 0 { + eprintln('Warning: ${file.warnings[0]}') + } + if file.errors.len > 0 { + verror('Error ${file.errors[0]}') + } + g.stmts(file.stmts) + } + g.generate_footer() + return g.nlines, g.buf.len +} + +pub fn (mut g Gen) generate_header() { + match g.pref.os { + .macos { + g.generate_macho_header() + } + .linux { + g.generate_elf_header() + } + .raw { + if g.pref.arch == .arm64 { + g.gen_arm64_helloworld() + } + } + else { + verror('Error: only `raw`, `linux` and `macos` are supported for -os in -native') + } + } +} + +pub fn (mut g Gen) create_executable() { + // Create the binary // should be .o ? + os.write_file_array(g.out_name, g.buf) or { panic(err) } + os.chmod(g.out_name, 0o775) // make it executable + if g.pref.is_verbose { + println('\n$g.out_name: native binary has been successfully generated') + } +} + +pub fn (mut g Gen) generate_footer() { + match g.pref.os { + .macos { + g.generate_macho_footer() + } + .linux { + g.generate_elf_footer() + } + .raw { + g.create_executable() + } + else {} + } +} + +pub fn (mut g Gen) stmts(stmts []ast.Stmt) { + for stmt in stmts { + g.stmt(stmt) + } +} + +pub fn (g &Gen) pos() i64 { + return g.buf.len +} + +fn (mut g Gen) write8(n int) { + // write 1 byte + g.buf << byte(n) +} + +fn (mut g Gen) write16(n int) { + // write 2 bytes + g.buf << byte(n) + g.buf << byte(n >> 8) +} + +fn (mut g Gen) read32_at(at int) int { + return int(g.buf[at] | (g.buf[at + 1] << 8) | (g.buf[at + 2] << 16) | (g.buf[at + 3] << 24)) +} + +fn (mut g Gen) write32(n int) { + // write 4 bytes + g.buf << byte(n) + g.buf << byte(n >> 8) + g.buf << byte(n >> 16) + g.buf << byte(n >> 24) +} + +fn (mut g Gen) write64(n i64) { + // write 8 bytes + g.buf << byte(n) + g.buf << byte(n >> 8) + g.buf << byte(n >> 16) + g.buf << byte(n >> 24) + g.buf << byte(n >> 32) + g.buf << byte(n >> 40) + g.buf << byte(n >> 48) + g.buf << byte(n >> 56) +} + +fn (mut g Gen) write64_at(n i64, at i64) { + // write 8 bytes + g.buf[at] = byte(n) + g.buf[at + 1] = byte(n >> 8) + g.buf[at + 2] = byte(n >> 16) + g.buf[at + 3] = byte(n >> 24) + g.buf[at + 4] = byte(n >> 32) + g.buf[at + 5] = byte(n >> 40) + g.buf[at + 6] = byte(n >> 48) + g.buf[at + 7] = byte(n >> 56) +} + +fn (mut g Gen) write32_at(at i64, n int) { + // write 4 bytes + g.buf[at] = byte(n) + g.buf[at + 1] = byte(n >> 8) + g.buf[at + 2] = byte(n >> 16) + g.buf[at + 3] = byte(n >> 24) +} + +fn (mut g Gen) write_string(s string) { + for c in s { + g.write8(int(c)) + } + // g.write8(0) // null terminated strings +} + +fn (mut g Gen) write_string_with_padding(s string, max int) { + for c in s { + g.write8(int(c)) + } + for _ in 0 .. max - s.len { + g.write8(0) + } +} + +fn (mut g Gen) get_var_offset(var_name string) int { + offset := g.var_offset[var_name] + if offset == 0 { + verror('unknown variable `$var_name`') + } + return offset +} + +pub fn (mut g Gen) gen_print_from_expr(expr ast.Expr, newline bool) { + match expr { + ast.StringLiteral { + if newline { + g.gen_print(expr.val + '\n') + } else { + g.gen_print(expr.val) + } + } + ast.CallExpr { + g.call_fn(expr) + g.gen_print_reg(.rax, 3) + } + ast.Ident { + g.expr(expr) + g.gen_print_reg(.rax, 3) + } + else { + dump(typeof(expr).name) + dump(expr) + verror('expected string as argument for print') + } + } +} + +pub fn (mut g Gen) register_function_address(name string) { + addr := g.pos() + // eprintln('register function $name = $addr') + g.fn_addr[name] = addr +} + +fn (mut g Gen) println(comment string) { + g.nlines++ + if !g.pref.is_verbose { + return + } + addr := g.debug_pos.hex() + // println('$g.debug_pos "$addr"') + print(term.red(strings.repeat(`0`, 6 - addr.len) + addr + ' ')) + for i := g.debug_pos; i < g.buf.len; i++ { + s := g.buf[i].hex() + if s.len == 1 { + print(term.blue('0')) + } + gbihex := g.buf[i].hex() + hexstr := term.blue(gbihex) + ' ' + print(hexstr) + } + g.debug_pos = g.buf.len + println(' ' + comment) +} + +fn (mut g Gen) for_in_stmt(node ast.ForInStmt) { + verror('for-in statement is not yet implemented') + /* + if node.is_range { + // `for x in 1..10 {` + // i := if node.val_var == '_' { g.new_tmp_var() } else { c_name(node.val_var) } + // val_typ := g.table.mktyp(node.val_type) + g.write32(0x3131) // 'for (${g.typ(val_typ)} $i = ') + g.expr(node.cond) + g.write32(0x3232) // ; $i < ') + g.expr(node.high) + g.write32(0x3333) // '; ++$i) {') + } else if node.kind == .array { + } else if node.kind == .array_fixed { + } else if node.kind == .map { + } else if node.kind == .string { + } else if node.kind == .struct_ { + } + */ +} + +pub fn (mut g Gen) gen_exit(node ast.Expr) { + // check node type and then call the cgen method + g.cgen.gen_exit(mut g, node) +} + +fn (mut g Gen) stmt(node ast.Stmt) { + match node { + ast.AssignStmt { + g.assign_stmt(node) + } + ast.Block { + g.stmts(node.stmts) + } + ast.ConstDecl {} + ast.ExprStmt { + g.expr(node.expr) + } + ast.FnDecl { + g.fn_decl(node) + } + ast.ForInStmt { + g.for_in_stmt(node) + } + ast.ForStmt { + g.for_stmt(node) + } + ast.HashStmt { + words := node.val.split(' ') + for word in words { + if word.len != 2 { + verror('opcodes format: xx xx xx xx') + } + b := unsafe { C.strtol(&char(word.str), 0, 16) } + // b := word.byte() + // println('"$word" $b') + g.write8(b) + } + } + ast.Module {} + ast.Return { + // dump(node.exprs[0]) + // if in main + // zero := ast.IntegerLiteral{} + // g.gen_exit(zero) + dump(node) + dump(node.types) + mut s := '?' //${node.exprs[0].val.str()}' + e0 := node.exprs[0] + match e0 { + ast.IntegerLiteral { + // TODO + } + ast.StringLiteral { + s = e0.val.str() + eprintln('jlalala $s') + } + else { + verror('unknown return type') + } + } + g.expr(node.exprs[0]) + g.mov64(.rax, g.allocate_string(s, 2)) + // intel specific + g.add8(.rsp, 0x20) // XXX depends on scope frame size + g.pop(.rbp) + g.ret() + } + ast.StructDecl {} + else { + println('native.stmt(): bad node: ' + node.type_name()) + } + } +} + +fn C.strtol(str &char, endptr &&char, base int) int + +fn (mut g Gen) expr(node ast.Expr) { + match node { + ast.ArrayInit { + verror('array init expr not supported yet') + } + ast.BoolLiteral {} + ast.CallExpr { + if node.name == 'exit' { + g.gen_exit(node.args[0].expr) + return + } + if node.name in ['println', 'print', 'eprintln', 'eprint'] { + expr := node.args[0].expr + g.gen_print_from_expr(expr, node.name in ['println', 'eprintln']) + return + } + g.call_fn(node) + } + ast.FloatLiteral {} + ast.Ident {} + ast.IfExpr { + g.if_expr(node) + } + ast.InfixExpr {} + ast.IntegerLiteral {} + ast.PostfixExpr { + g.postfix_expr(node) + } + ast.StringLiteral {} + ast.StructInit {} + else { + println(term.red('native.expr(): unhandled node: ' + node.type_name())) + } + } +} + +/* +fn (mut g Gen) allocate_var(name string, size int, initial_val int) { + g.cgen.allocate_var(name, size, initial_val) +} +*/ + +fn (mut g Gen) postfix_expr(node ast.PostfixExpr) { + if node.expr !is ast.Ident { + return + } + ident := node.expr as ast.Ident + var_name := ident.name + if node.op == .inc { + g.inc_var(var_name) + } +} + +[noreturn] +fn verror(s string) { + util.verror('native gen error', s) +} + +pub fn (mut g Gen) error_with_pos(s string, pos token.Position) { + // TODO: store a file index in the Position too, + // so that the file path can be retrieved from the pos, instead + // of guessed from the pref.path ... + mut kind := 'error:' + if g.pref.output_mode == .stdout { + ferror := util.formatted_error(kind, s, g.pref.path, pos) + eprintln(ferror) + exit(1) + } else { + g.errors << errors.Error{ + file_path: g.pref.path + pos: pos + reporter: .gen + message: s + } + } +} diff --git a/v_windows/v/old/vlib/v/gen/native/macho.v b/v_windows/v/old/vlib/v/gen/native/macho.v new file mode 100644 index 0000000..b25bf05 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/native/macho.v @@ -0,0 +1,405 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module native + +const ( + s_attr_some_instructions = 0x00000400 + s_attr_pure_instructions = 0x80000000 + s_attr_ext_reloc = 0x00000200 + s_attr_loc_reloc = 0x00000100 + // + macho_symcmd_size = 0x18 + macho_d_size = 0x50 + mh_object = 1 + mh_execute = 2 + lc_dysymtab = 0xb + lc_load_dylib = 0xc + lc_load_dylinker = 0xe + lc_main = 0x80000028 + lc_segment_64 = 0x19 + lc_symtab = 0x2 +) + +struct Symbol { + str_entry int + symbol_typ int + section int + desc int + val i64 + name string + is_ext bool +} + +struct Reloc { + addr int + pcrel int + len int + ext int + typ int + snum int // symbol index (if ext) or infile section number +} + +fn (mut g Gen) macho_segment64_pagezero() { + g.write32(native.lc_segment_64) // LC_SEGMENT_64 + g.write32(72) // cmdsize + g.write_string_with_padding('__PAGEZERO', 16) // section name + g.write64(0) // vmaddr + g.write64(0x1000) // vmsize + g.write64(0) // fileoff + g.write64(0) // filesize + + g.write32(0) // maxprot + g.write32(0) // initprot + g.write32(0) // nsects + g.write32(0) // flags +} + +fn (mut g Gen) macho_segment64_linkedit() { + g.write32(native.lc_segment_64) + g.write32(0x48) // cmdsize + g.write_string_with_padding('__LINKEDIT', 16) + + g.write64(0x3000) // vmaddr + g.write64(0x1000) // vmsize + g.write64(0x1000) // fileoff + g.write64(0) // filesize + g.write32(7) // maxprot + g.write32(3) // initprot + g.write32(0) // nsects + g.write32(0) // flags +} + +fn (mut g Gen) macho_header(ncmds int, bintype int) int { + g.write32(0xfeedfacf) // MH_MAGIC_64 + if g.pref.arch == .arm64 { + g.write32(0x0100000c) // CPU_TYPE_ARM64 + g.write32(0x00000000) // CPU_SUBTYPE_ARM64_ALL + } else { + g.write32(0x01000007) // CPU_TYPE_X64 + g.write32(0x80000003) // CPU_SUBTYPE_X64 + } + g.write32(native.mh_execute) // filetype + g.write32(ncmds) // ncmds + + cmdsize_offset := g.buf.len + g.write32(0) // size of load commands + + g.write32(0) // flags + g.write32(0) // reserved + return cmdsize_offset +} + +fn (mut g Gen) macho_segment64_text() []int { + mut patch := []int{} + g.write32(native.lc_segment_64) // LC_SEGMENT_64 + g.write32(152) // 152 + g.write_string_with_padding('__TEXT', 16) // section name + g.write64(0x100000000) // vmaddr + patch << g.buf.len + g.write64(0x00001000) // + codesize) // vmsize + g.write64(0x00000000) // filesize + patch << g.buf.len + g.write64(0x00001000) // + codesize) // filesize + + g.write32(7) // maxprot + g.write32(5) // initprot + g.write32(1) // nsects + g.write32(0) // flags + + g.write_string_with_padding('__text', 16) // section name + g.write_string_with_padding('__TEXT', 16) // segment name + g.write64(0x0000000100001000) // vmaddr + patch << g.buf.len + g.write64(0) // vmsize + g.write32(4096) // offset + g.write32(0) // align + + g.write32(0) // reloff + g.write32(0) // nreloc + + g.write32(0) // flags + g.write32(0) + + g.write32(0) // reserved1 + g.write32(0) // reserved2 + return patch +} + +fn (mut g Gen) macho_symtab() { + g.write32(native.lc_symtab) + g.write32(24) + g.write32(0x1000) + g.write32(0) + g.write32(0x1000) + g.write32(0) + + // lc_dysymtab + g.write32(native.lc_dysymtab) + g.write32(0x50) + g.write32(0) // ilocalsym + g.write32(0) // nlocalsym + g.write32(0) // iextdefsym + g.write32(0) // nextdefsym + g.write32(0) // iundefsym + g.write32(0) // nundefsym + g.write32(0) // tocoff + g.write32(0) // ntoc + g.write32(0) // modtaboff + g.write32(0) // nmodtab + g.write32(0) // extrefsymoff + g.write32(0) // nextrefsyms + g.write32(0) // indirectsymoff + g.write32(0) // nindirectsyms + g.write32(0) // extreloff + g.write32(0) // nextrel + g.write32(0) // locreloff + g.write32(0) // nlocrel +} + +fn (mut g Gen) macho_dylibs() { + g.write32(native.lc_load_dylinker) + g.write32(32) // cmdsize (must be aligned to int32) + g.write32(12) // offset + g.write_string_with_padding('/usr/lib/dyld', 16) + g.write32(0) // padding // can be removed + + g.write32(native.lc_load_dylib) + g.write32(56) // cmdsize + g.write32(24) // offset + g.write32(0) // ts + g.write32(1) // ver + g.write32(1) // compat + g.write_string_with_padding('/usr/lib/libSystem.B.dylib', 32) +} + +fn (mut g Gen) macho_main(addr int) { + g.write32(int(native.lc_main)) // LC_MAIN + g.write32(24) // cmdsize + g.write32(addr) // entrypoint + g.write32(0) // initial_stacksize +} + +pub fn (mut g Gen) generate_macho_header() { + g.code_start_pos = 0x1000 + g.debug_pos = 0x1000 + cmdsize_offset := g.macho_header(8, native.mh_execute) + g.macho_segment64_pagezero() + + g.size_pos = g.macho_segment64_text() + g.macho_segment64_linkedit() + g.macho_symtab() + g.macho_dylibs() + g.macho_main(0x1000) + + g.write32_at(cmdsize_offset, g.buf.len - 24) + g.write_nulls(0x1000 - g.buf.len) + g.call(0) +} + +fn (mut g Gen) write_nulls(len int) { + pad := 0x1000 - g.buf.len + for _ in 0 .. pad { + g.write8(0) + } +} + +pub fn (mut g Gen) generate_macho_object_header() { + if g.pref.arch == .arm64 { + g.write32(0xfeedfacf) // MH_MAGIC_64 + g.write32(0x0100000c) // CPU_TYPE_ARM64 + g.write32(0x00000000) // CPU_SUBTYPE_ARM64_ALL + } else { + g.write32(0xfeedfacf) // MH_MAGIC_64 + g.write32(0x01000007) // CPU_TYPE_X64 + g.write32(0x00000003) // CPU_SUBTYPE_X64 + } + g.write32(native.mh_object) // MH_OBJECT + text_offset := 0x138 + g.write32(4) // # of load commands + g.write32(text_offset - 0x20) // size of load commands // 0x138-0x20 + // g.write32(0x00002000) // MH_SUBSECTIONS_VIA_SYMBOLS + g.write32(0) // MH_SUBSECTIONS_VIA_SYMBOLS + g.write32(0) // reserved + //// + g.write32(0x19) // LC_SEGMENT_64 + g.write32(0x98) // command size + g.zeroes(16) // segment name + g.write64(0) // VM address + g.write64(0x25) // VM size + g.write64(text_offset) // file offset + g.write64(0x25) // file size + g.write32(0x7) // max vm protection + g.write32(0x7) // initial vm protection + g.write32(0x1) // # of sections + g.write32(0) // flags + //// + g.write_string_with_padding('__text', 16) // section name + g.write_string_with_padding('__TEXT', 16) // segment name + g.write64(0) // address + g.write64(0x25) // size + g.write32(text_offset) // offset + g.write32(0x4) // alignment + g.write32(0x160) // relocation offset + g.write32(0x1) // # of relocations + g.write32(int(native.s_attr_some_instructions | native.s_attr_pure_instructions)) + g.write32(0) + g.write32(0) + g.write32(0) + /// ??? + g.write32(0x32) + g.write32(0x18) + + g.write32(0x01) + g.write32(0x000a0000) // minOS 10.0 + g.write32(0) + g.write32(0) + // lc_symtab + g.sym_table_command() + // + g.write32(native.lc_dysymtab) + g.write32(native.macho_d_size) + g.write32(0) + g.write32(2) + g.write32(2) + g.write32(1) + g.write32(3) + g.write32(1) + for _ in 0 .. 12 { + g.write32(0) + } + if g.pref.is_verbose { + println('commands size = $g.buf.len') + if g.buf.len != 0x138 { + println('macho: invalid header size') + } + } + + if g.pref.arch == .arm64 { + g.gen_arm64_helloworld() + } else { + // do nothing + } +} + +pub fn (mut g Gen) generate_macho_footer() { + codesize := g.buf.len - 0x1000 + g.write_relocs() + g.sym_table() + stringtablesize := g.sym_string_table() + delta := codesize + stringtablesize + 12 // code_offset_end - 0x1000// + stringtablesize + g.write8(0) + for o in g.size_pos { + n := g.read32_at(o) + g.write32_at(o, n + delta) + } + g.write64(0) + // this is amd64-specific + call_delta := int(g.main_fn_addr - g.code_start_pos) - 5 + g.write32_at(g.code_start_pos + 1, call_delta) + g.create_executable() +} + +fn (mut g Gen) sym_table_command() { + g.syms << Symbol{ + str_entry: 0x19 + symbol_typ: 0xe + section: 1 + val: 0 + name: '_start' + is_ext: true + } + g.syms << Symbol{ + str_entry: 0x0e + symbol_typ: 0xe + // symbol_typ: SYM_DEF + section: 1 + val: 0x18 + name: '_puts' + is_ext: false + } + g.syms << Symbol{ + str_entry: 0x01 + symbol_typ: 0xf + // symbol_typ: SYM_DEF + section: 1 + // val: 0x27 + val: 0 + name: 'helloworld' + is_ext: false + } + g.syms << Symbol{ + str_entry: 0x08 + symbol_typ: 0x1 + // symbol_typ: SYM_DEF + section: 0 + // val: 0x27 + val: 0 + name: 'ltmp1' + is_ext: false + } + g.write32(native.lc_symtab) + g.write32(native.macho_symcmd_size) + sym_table_offset := 0x168 + g.write32(sym_table_offset) + g_syms_len := 4 + g.write32(g_syms_len) + str_offset := 0x1a8 + g.write32(str_offset) + str_size := 0x20 + g.write32(str_size) +} + +pub fn (mut g Gen) zeroes(n int) { + for _ in 0 .. n { + g.buf << 0 + } +} + +fn (mut g Gen) write_relocs() { + if g.pref.is_verbose { + println('relocs at $g.buf.len should be 0x160') + } + g.write32(0x8) + g.write32(0x2d000003) +} + +fn (mut g Gen) sym_table() { + // strings first + for sym in g.syms { + // if !sym.is_ext { + g.write_symbol(sym) + //} + } + // now fns (external syms) + /* + for sym in g.syms { + if sym.is_ext { + g.write_symbol(sym) + } + } + */ +} + +fn (mut g Gen) write_symbol(s Symbol) { + // g.write8(0x77) + g.write32(s.str_entry) + g.write8(s.symbol_typ) + g.write8(s.section) + g.write8(0) + g.write8(0) + g.write64(s.val) + // g.write16(s.desc) +} + +fn (mut g Gen) sym_string_table() int { + begin := g.buf.len + g.zeroes(1) + at := i64(0x100000000) + for i, s in g.strings { + g.write64_at(at + g.buf.len, int(g.str_pos[i])) + g.write_string(s) + g.write8(0) + } + return g.buf.len - begin +} diff --git a/v_windows/v/old/vlib/v/gen/native/macho_test.v b/v_windows/v/old/vlib/v/gen/native/macho_test.v new file mode 100644 index 0000000..9da074d --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/native/macho_test.v @@ -0,0 +1,16 @@ +import os +import v.gen.native +import v.pref +import v.ast + +fn test_macho() { + os.chdir(os.temp_dir()) + mut g := native.Gen{ + pref: &pref.Preferences{} + out_name: 'test.bin' + table: ast.new_table() + cgen: native.Amd64{} + } + g.generate_macho_header() + g.generate_macho_footer() +} diff --git a/v_windows/v/old/vlib/v/gen/native/tests/expressions.vv b/v_windows/v/old/vlib/v/gen/native/tests/expressions.vv new file mode 100644 index 0000000..6d36760 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/native/tests/expressions.vv @@ -0,0 +1,60 @@ +fn print_number(n int) { + if n == 0 { + println('0') + } + if n == 1 { + println('1') + } + if n == 2 { + println('2') + } + if n == 3 { + println('3') + } + if n == 4 { + println('4') + } + if n == 5 { + println('5') + } + if n == 6 { + println('6') + } + if n == 7 { + println('7') + } + if n == 8 { + println('8') + } + if n == 9 { + println('9') + } +} + +struct User { + age int +} + +fn print_user(u User) { +} + +fn test_add() { + println('test_add()') + x := 2 + y := 3 + sum := x + y + product := x * y + // diff := y - x + print_number(x) + print_number(y) + print_number(sum) + print_number(product) + // XXX fails on linux-amd64 but works on macos-amd64 + // print_number(diff) +} + +fn main() { + println('start') + test_add() + println('end') +} diff --git a/v_windows/v/old/vlib/v/gen/native/tests/expressions.vv.out b/v_windows/v/old/vlib/v/gen/native/tests/expressions.vv.out new file mode 100644 index 0000000..2232a58 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/native/tests/expressions.vv.out @@ -0,0 +1,8 @@ +start +test_add() +2 +3 +5 +6 +end + diff --git a/v_windows/v/old/vlib/v/gen/native/tests/general.vv b/v_windows/v/old/vlib/v/gen/native/tests/general.vv new file mode 100644 index 0000000..155a039 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/native/tests/general.vv @@ -0,0 +1,87 @@ +fn if_test() { + mut a := 1 + if a == 1 { + println('a == 1') + b := 2 + if b == 2 { + println('b == 2') + } + // if b != 2 { + // println('b != 2') + //} + } + if a == 2 { + println('a == 2') + } + a++ + if a == 2 { + println('now a == 2') + } +} + +fn loop() { + mut i := 0 + for i < 5 { + println('hello') + i++ + } +} + +fn foo(a int) { + println('foo:') + if a == 1 { + println('a == 1') + } + if a == 7 { + println('a == 7') + } + /* + // native gen doesnt support more than one variable + mut b := a + 1 + if b == 2 { + println('b == 2') + } + if b == 3 { + println('b == 3') + } + if b == 8 { + println('b == 8') + } + */ +} + +fn args() { + x := 7 + println('===args===') + foo(1) // prints 1 2 + foo(x) // prints 7 8 + foo(2) // prints 3 +} + +/* +fn expr() { + println('===expr===') + a := 1 + b := 2 + c := a + b + println('c==') + if c == 0 { + println('0') + } + if c == 3 { + println('3') + } +} +*/ + +struct User { + age int + nr_orders int +} + +fn main() { + if_test() + loop() + args() + // expr() +} diff --git a/v_windows/v/old/vlib/v/gen/native/tests/general.vv.out b/v_windows/v/old/vlib/v/gen/native/tests/general.vv.out new file mode 100644 index 0000000..3c73504 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/native/tests/general.vv.out @@ -0,0 +1,14 @@ +a == 1 +b == 2 +now a == 2 +hello +hello +hello +hello +hello +===args=== +foo: +a == 1 +foo: +a == 7 +foo: diff --git a/v_windows/v/old/vlib/v/gen/native/tests/hello.vv b/v_windows/v/old/vlib/v/gen/native/tests/hello.vv new file mode 100644 index 0000000..778a1b4 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/native/tests/hello.vv @@ -0,0 +1,3 @@ +fn main() { + println('hello from native V') +} diff --git a/v_windows/v/old/vlib/v/gen/native/tests/hello.vv.out b/v_windows/v/old/vlib/v/gen/native/tests/hello.vv.out new file mode 100644 index 0000000..20d1b8b --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/native/tests/hello.vv.out @@ -0,0 +1 @@ +hello from native V diff --git a/v_windows/v/old/vlib/v/gen/native/tests/ifs.vv b/v_windows/v/old/vlib/v/gen/native/tests/ifs.vv new file mode 100644 index 0000000..674d4aa --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/native/tests/ifs.vv @@ -0,0 +1,65 @@ +fn print_number(n int) { + if n == 0 { + println('print_number') + } +} + +fn test_add() { + n := 3 + print_number(0) + print_number(1) + if n > 1 { + println('var(3) > 1') + } + /* + if 1 < n { + println('1 < var(3)') + } + if 1 > n { + println('1 > 3 ERROR') + } + */ + if 1 < 3 { + println('1 < 3') + } + if 1 == 1 { + println('1 == 1') + // TODO assert here + } + if 1 != 3 { + println('1 != 3') + // TODO assert here + } + if 3 != 3 { + println('3 != 3 ERROR') + // TODO assert here + } + if 1 > 3 { + println('1 > 3 ERROR') + // TODO assert here + } +} + +/* +fn test_elses() { + println('start else') + if 1 < 2 { + println('ok') + } else { + println('1<2else ERROR') + } + if 1 > 2 { + println('1<2else ERROR') + } else { + println('ok') + } + println('end else') +} +*/ + +fn main() { + println('start') + test_add() + // test_elses() + println('end') +} diff --git a/v_windows/v/old/vlib/v/gen/native/tests/ifs.vv.out b/v_windows/v/old/vlib/v/gen/native/tests/ifs.vv.out new file mode 100644 index 0000000..1298ea4 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/native/tests/ifs.vv.out @@ -0,0 +1,7 @@ +start +print_number +var(3) > 1 +1 < 3 +1 == 1 +1 != 3 +end diff --git a/v_windows/v/old/vlib/v/gen/native/tests/native_test.v b/v_windows/v/old/vlib/v/gen/native/tests/native_test.v new file mode 100644 index 0000000..1ae7bec --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/native/tests/native_test.v @@ -0,0 +1,79 @@ +import os +import benchmark +import term + +const is_verbose = os.getenv('VTEST_SHOW_CMD') != '' + +// TODO some logic copy pasted from valgrind_test.v and compiler_test.v, move to a module +fn test_native() { + $if arm64 { + return + } + // some tests are running fine in macos + if os.user_os() != 'linux' && os.user_os() != 'macos' { + eprintln('native tests only run on Linux and macOS for now.') + exit(0) + } + mut bench := benchmark.new_benchmark() + vexe := os.getenv('VEXE') + vroot := os.dir(vexe) + dir := os.join_path(vroot, 'vlib/v/gen/native/tests') + files := os.ls(dir) or { panic(err) } + // + wrkdir := os.join_path(os.temp_dir(), 'vtests', 'native') + os.mkdir_all(wrkdir) or { panic(err) } + os.chdir(wrkdir) + tests := files.filter(it.ends_with('.vv')) + if tests.len == 0 { + println('no native tests found') + assert false + } + bench.set_total_expected_steps(tests.len) + for test in tests { + bench.step() + full_test_path := os.real_path(os.join_path(dir, test)) + test_file_name := os.file_name(test) + relative_test_path := full_test_path.replace(vroot + '/', '') + work_test_path := '$wrkdir/$test_file_name' + exe_test_path := '$wrkdir/${test_file_name}.exe' + cmd := '"$vexe" -o "$exe_test_path" -b native "$full_test_path"' + if is_verbose { + println(cmd) + } + res_native := os.execute(cmd) + if res_native.exit_code != 0 { + bench.fail() + eprintln(bench.step_message_fail(cmd)) + continue + } + res := os.execute(exe_test_path) + if res.exit_code != 0 { + bench.fail() + eprintln(bench.step_message_fail('$full_test_path failed to run')) + eprintln(res.output) + continue + } + mut expected := os.read_file('$dir/${test}.out') or { panic(err) } + expected = expected.trim_right('\r\n').replace('\r\n', '\n') + mut found := res.output.trim_right('\r\n').replace('\r\n', '\n') + found = found.trim_space() + if expected != found { + println(term.red('FAIL')) + println('============') + println('expected: "$expected" len=$expected.len') + println('============') + println('found:"$found" len=$found.len') + println('============\n') + bench.fail() + continue + } + bench.ok() + eprintln(bench.step_message_ok(relative_test_path)) + } + bench.stop() + eprintln(term.h_divider('-')) + eprintln(bench.total_message('native')) + if bench.nfail > 0 { + exit(1) + } +} diff --git a/v_windows/v/old/vlib/v/gen/native/tests/simple_fn_calls.vv b/v_windows/v/old/vlib/v/gen/native/tests/simple_fn_calls.vv new file mode 100644 index 0000000..0b39c98 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/native/tests/simple_fn_calls.vv @@ -0,0 +1,14 @@ +fn print_greeting() { + println('hello from native V') +} + +fn print_123() { + println('123') +} + +fn main() { + println('start') + print_greeting() + print_123() + println('end') +} diff --git a/v_windows/v/old/vlib/v/gen/native/tests/simple_fn_calls.vv.out b/v_windows/v/old/vlib/v/gen/native/tests/simple_fn_calls.vv.out new file mode 100644 index 0000000..a9c6927 --- /dev/null +++ b/v_windows/v/old/vlib/v/gen/native/tests/simple_fn_calls.vv.out @@ -0,0 +1,4 @@ +start +hello from native V +123 +end diff --git a/v_windows/v/old/vlib/v/live/common.v b/v_windows/v/old/vlib/v/live/common.v new file mode 100644 index 0000000..bd29a90 --- /dev/null +++ b/v_windows/v/old/vlib/v/live/common.v @@ -0,0 +1,69 @@ +module live + +pub const ( + is_used = 1 +) + +pub type FNLinkLiveSymbols = fn (linkcb voidptr) + +pub type FNLiveReloadCB = fn (info &LiveReloadInfo) + +pub struct LiveReloadInfo { +pub: + vexe string // full path to the v compiler + vopts string // v compiler options for a live shared library + original string // full path to the original source file, compiled with -live + live_fn_mutex voidptr // the address of the C mutex, that locks the [live] fns during reloads. + live_linkfn FNLinkLiveSymbols // generated C callback; receives a dlopen handle + so_extension string // .so or .dll + so_name_template string // a sprintf template for the shared libraries location +pub mut: + live_lib voidptr // the result of dl.open + reloads int // how many times a reloading was tried + reloads_ok int // how many times the reloads succeeded + reload_time_ms int // how much time the last reload took (compilation + loading) + last_mod_ts int // a timestamp for when the original was last changed + recheck_period_ms int = 100 // how often do you want to check for changes + cb_recheck FNLiveReloadCB = voidptr(0) // executed periodically + cb_compile_failed FNLiveReloadCB = voidptr(0) // executed when a reload compilation failed + cb_before FNLiveReloadCB = voidptr(0) // executed before a reload try happens + cb_after FNLiveReloadCB = voidptr(0) // executed after a reload try happened, even if failed + cb_locked_before FNLiveReloadCB = voidptr(0) // executed before lib reload, in the mutex section + cb_locked_after FNLiveReloadCB = voidptr(0) // executed after lib reload, in the mutex section + user_ptr voidptr = voidptr(0) // you can set it to anything, then retrieve it in the cb_ fns +} + +// LiveReloadInfo.live_linkfn should be called by the reloader +// to dlsym all live functions. TODO: research a way to implement +// live_linkfn in pure V, without complicating live code generation +// too much. +// +// The callbacks: cb_compile_fail, cb_before, cb_after will be +// executed outside the mutex protected section, so be careful, +// if you modify your data inside them. They can race with your +// [live] functions. +// +// cb_locked_before and cb_locked_after will be executed *inside* +// the mutex protected section. They can NOT race with your [live] +// functions. They should be very quick in what they do though, +// otherwise your live functions can be delayed. +// +// live.info - give user access to program's LiveReloadInfo struct, +// so that the user can set callbacks, read meta information, etc. +pub fn info() &LiveReloadInfo { + if C.g_live_info != 0 { + return unsafe { &LiveReloadInfo(C.g_live_info) } + } + // When the current program is not compiled with -live, simply + // return a new empty struct LiveReloadInfo in order to prevent + // crashes. In this case, the background reloader thread is not + // started, and the structure LiveReloadInfo will not get updated. + // All its fields will be 0, but still safe to access. + mut x := &LiveReloadInfo{} + unsafe { + mut p := &u64(&C.g_live_info) + *p = &u64(x) + _ = p + } + return x +} diff --git a/v_windows/v/old/vlib/v/live/executable/reloader.v b/v_windows/v/old/vlib/v/live/executable/reloader.v new file mode 100644 index 0000000..960f543 --- /dev/null +++ b/v_windows/v/old/vlib/v/live/executable/reloader.v @@ -0,0 +1,163 @@ +module executable + +import os +import time +import dl +import strconv +import v.live + +pub const ( + is_used = 1 +) + +// The live reloader code is implemented here. +// NB: new_live_reload_info will be called by generated C code inside main() +pub fn new_live_reload_info(original string, vexe string, vopts string, live_fn_mutex voidptr, live_linkfn live.FNLinkLiveSymbols) &live.LiveReloadInfo { + file_base := os.file_name(original).replace('.v', '') + so_dir := os.cache_dir() + mut so_extension := dl.dl_ext + $if macos { + so_extension = '.dylib' + } + // $if msvc { so_extension = '.dll' } $else { so_extension = '.so' } + return &live.LiveReloadInfo{ + original: original + vexe: vexe + vopts: vopts + live_fn_mutex: live_fn_mutex + live_linkfn: live_linkfn + so_extension: so_extension + so_name_template: '$so_dir/tmp.%d.$file_base' + live_lib: 0 + reloads: 0 + reload_time_ms: 0 + } +} + +// NB: start_reloader will be called by generated code inside main(), to start +// the hot code reloader thread. start_reloader is executed in the context of +// the original main thread. +pub fn start_reloader(mut r live.LiveReloadInfo) { + // The shared library should be loaded once in the main thread + // If that fails, the program would crash anyway, just provide + // an error message to the user and exit: + r.reloads++ + compile_and_reload_shared_lib(mut r) or { + eprintln(err) + exit(1) + } + go reloader(mut r) +} + +fn elog(r &live.LiveReloadInfo, s string) { + $if debuglive ? { + eprintln(s) + } +} + +fn compile_and_reload_shared_lib(mut r live.LiveReloadInfo) ?bool { + sw := time.new_stopwatch() + new_lib_path := compile_lib(mut r) or { return error('errors while compiling $r.original') } + elog(r, '> compile_and_reload_shared_lib compiled: $new_lib_path') + load_lib(mut r, new_lib_path) + r.reload_time_ms = int(sw.elapsed().milliseconds()) + return true +} + +fn compile_lib(mut r live.LiveReloadInfo) ?string { + new_lib_path, new_lib_path_with_extension := current_shared_library_path(mut r) + cmd := '$r.vexe $r.vopts -o $new_lib_path $r.original' + elog(r, '> compilation cmd: $cmd') + cwatch := time.new_stopwatch() + recompilation_result := os.execute(cmd) + elog(r, 'compilation took: ${cwatch.elapsed().milliseconds()}ms') + if recompilation_result.exit_code != 0 { + eprintln('recompilation error:') + eprintln(recompilation_result.output) + return none + } + if !os.exists(new_lib_path_with_extension) { + eprintln('new_lib_path: $new_lib_path_with_extension does not exist') + return none + } + return new_lib_path_with_extension +} + +fn current_shared_library_path(mut r live.LiveReloadInfo) (string, string) { + lib_path := strconv.v_sprintf(r.so_name_template.replace('\\', '\\\\'), r.reloads) + lib_path_with_extension := lib_path + r.so_extension + return lib_path, lib_path_with_extension +} + +fn load_lib(mut r live.LiveReloadInfo, new_lib_path string) { + elog(r, 'live mutex locking...') + C.pthread_mutex_lock(r.live_fn_mutex) + elog(r, 'live mutex locked') + // + if r.cb_locked_before != voidptr(0) { + r.cb_locked_before(r) + } + // + protected_load_lib(mut r, new_lib_path) + // + r.reloads_ok++ + if r.cb_locked_after != voidptr(0) { + r.cb_locked_after(r) + } + // + elog(r, 'live mutex unlocking...') + C.pthread_mutex_unlock(r.live_fn_mutex) + elog(r, 'live mutex unlocked') +} + +fn protected_load_lib(mut r live.LiveReloadInfo, new_lib_path string) { + if r.live_lib != 0 { + dl.close(r.live_lib) + r.live_lib = C.NULL + } + r.live_lib = dl.open(new_lib_path, dl.rtld_lazy) + if r.live_lib == 0 { + eprintln('opening $new_lib_path failed') + exit(1) + } + r.live_linkfn(r.live_lib) + elog(r, '> load_lib OK, new live_lib: $r.live_lib') + // removing the .so file from the filesystem after dlopen-ing + // it is safe, since it will still be mapped in memory + os.rm(new_lib_path) or {} +} + +// NB: r.reloader() is executed in a new, independent thread +fn reloader(mut r live.LiveReloadInfo) { + // elog(r,'reloader, r: $r') + mut last_ts := os.file_last_mod_unix(r.original) + for { + if r.cb_recheck != voidptr(0) { + r.cb_recheck(r) + } + now_ts := os.file_last_mod_unix(r.original) + if last_ts != now_ts { + r.reloads++ + last_ts = now_ts + r.last_mod_ts = last_ts + if r.cb_before != voidptr(0) { + r.cb_before(r) + } + compile_and_reload_shared_lib(mut r) or { + if r.cb_compile_failed != voidptr(0) { + r.cb_compile_failed(r) + } + if r.cb_after != voidptr(0) { + r.cb_after(r) + } + continue + } + if r.cb_after != voidptr(0) { + r.cb_after(r) + } + } + if r.recheck_period_ms > 0 { + time.sleep(r.recheck_period_ms * time.millisecond) + } + } +} diff --git a/v_windows/v/old/vlib/v/live/live_test.v b/v_windows/v/old/vlib/v/live/live_test.v new file mode 100644 index 0000000..2468075 --- /dev/null +++ b/v_windows/v/old/vlib/v/live/live_test.v @@ -0,0 +1,177 @@ +import os +import time + +/* +The goal of this test, is to simulate a developer, that has run a program, compiled with -live flag. + +It does so by writing a new generated program containing a [live] fn pmessage() string {...} function, +(that program is in `vlib/v/live/live_test_template.vv`) +then runs the generated program at the start *in the background*, +waits some time, so that the program could run a few iterations, then modifies its source +(simulates a developer that has saved a new version of the program source), +then it waits some more, modifies it again and saves it once more. + +On each modification, the running program, should detect that its source code has changed, +and recompile a shared library, which it then it should load, and thus modify its own +behavior at runtime (the pmessage function). + +If everything works fine, the output of the generated program would have changed at least 1-2 times, +which then is detected by the test program (the histogram checks). + +Since this test program is sensitive to coordination (or lack of) of several processes, +it tries to sidestep the coordination issue by polling the file system for the existance +of files, ORIGINAL.txt ... STOP.txt , which are appended to by the generated program. + +NB: That approach of monitoring the state of the running generated program, is clearly not ideal, +but sidesteps the issue of coordinating processes through IPC or stdin/stdout in hopefully +not very flaky way. + +TODO: Cleanup this when/if v has better process control/communication primitives. +*/ +const ( + vexe = os.getenv('VEXE') + tmp_file = os.join_path(os.temp_dir(), 'generated_live_program.tmp.v') + source_file = os.join_path(os.temp_dir(), 'generated_live_program.v') + genexe_file = os.join_path(os.temp_dir(), 'generated_live_program') + output_file = os.join_path(os.temp_dir(), 'generated_live_program.output.txt') + res_original_file = os.join_path(os.temp_dir(), 'ORIGINAL.txt') + res_changed_file = os.join_path(os.temp_dir(), 'CHANGED.txt') + res_another_file = os.join_path(os.temp_dir(), 'ANOTHER.txt') + res_stop_file = os.join_path(os.temp_dir(), 'STOP.txt') + cleanup_files = [tmp_file, source_file, genexe_file, output_file, res_original_file, + res_changed_file, res_another_file, res_stop_file] + live_program_source = get_source_template() +) + +fn get_source_template() string { + src := os.read_file(os.join_path(os.dir(@FILE), 'live_test_template.vv')) or { panic(err) } + return src.replace('#OUTPUT_FILE#', output_file) +} + +fn edefault(name string, default string) string { + res := os.getenv(name) + if res == '' { + return default + } + return res +} + +fn atomic_write_source(source string) { + // NB: here wrtiting is done in 2 steps, since os.write_file can take some time, + // during which the file will be modified, but it will still be not completely written. + // The os.mv after that, guarantees that the reloader will see a complete valid V program. + os.write_file(tmp_file, source) or { panic(err) } + os.mv(tmp_file, source_file) or { panic(err) } +} + +// +fn testsuite_begin() { + if os.user_os() !in ['linux', 'solaris'] && os.getenv('FORCE_LIVE_TEST').len == 0 { + eprintln('Testing the runtime behaviour of -live mode,') + eprintln('is reliable only on Linux/macOS for now.') + eprintln('You can still do it by setting FORCE_LIVE_TEST=1 .') + exit(0) + } + for f in [tmp_file, source_file, output_file, res_original_file, res_changed_file, + res_another_file, res_stop_file] { + os.rm(f) or {} + } + atomic_write_source(live_program_source) +} + +[debuglivetest] +fn vprintln(s string) { + eprintln(s) +} + +fn testsuite_end() { + vprintln('source: $source_file') + vprintln('output: $output_file') + vprintln('---------------------------------------------------------------------------') + output_lines := os.read_lines(output_file) or { + panic('could not read $output_file, error: $err') + } + mut histogram := map[string]int{} + for line in output_lines { + histogram[line] = histogram[line] + 1 + } + for k, v in histogram { + eprintln('> found ${v:5d} times: $k') + } + vprintln('---------------------------------------------------------------------------') + assert histogram['START'] > 0 + assert histogram['ORIGINAL'] > 0 + assert histogram['CHANGED'] + histogram['ANOTHER'] > 0 + // assert histogram['END'] > 0 + for tfile in cleanup_files { + os.rm(tfile) or {} + } +} + +fn change_source(new string) { + time.sleep(100 * time.millisecond) + vprintln('> change ORIGINAL to: $new') + atomic_write_source(live_program_source.replace('ORIGINAL', new)) + wait_for_file(new) +} + +fn wait_for_file(new string) { + time.sleep(100 * time.millisecond) + expected_file := os.join_path(os.temp_dir(), new + '.txt') + eprintln('waiting for $expected_file ...') + max_wait_cycles := edefault('WAIT_CYCLES', '1').int() + for i := 0; i <= max_wait_cycles; i++ { + if i % 25 == 0 { + vprintln(' checking ${i:-10d} for $expected_file ...') + } + if os.exists(expected_file) { + assert true + vprintln('> done.') + time.sleep(100 * time.millisecond) + break + } + time.sleep(5 * time.millisecond) + } +} + +fn setup_cycles_environment() { + mut max_live_cycles := 1000 + mut max_wait_cycles := 400 + if os.user_os() == 'macos' { + // max_live_cycles *= 5 + // max_wait_cycles *= 5 + } + os.setenv('LIVE_CYCLES', '$max_live_cycles', true) + os.setenv('WAIT_CYCLES', '$max_wait_cycles', true) +} + +// +fn test_live_program_can_be_compiled() { + setup_cycles_environment() + eprintln('Compiling...') + os.system('$vexe -nocolor -live -o $genexe_file $source_file') + // + cmd := '$genexe_file > /dev/null &' + eprintln('Running with: $cmd') + res := os.system(cmd) + assert res == 0 + eprintln('... running in the background') + wait_for_file('ORIGINAL') +} + +fn test_live_program_can_be_changed_1() { + change_source('CHANGED') + assert true +} + +fn test_live_program_can_be_changed_2() { + change_source('ANOTHER') + assert true +} + +fn test_live_program_can_be_changed_3() { + change_source('STOP') + change_source('STOP') + change_source('STOP') + assert true +} diff --git a/v_windows/v/old/vlib/v/live/live_test_template.vv b/v_windows/v/old/vlib/v/live/live_test_template.vv new file mode 100644 index 0000000..9630fff --- /dev/null +++ b/v_windows/v/old/vlib/v/live/live_test_template.vv @@ -0,0 +1,66 @@ +module main + +import time +import os +import v.live + +fn append_to_file(fname string, s string) { + mut f := os.open_append(fname) or { + println('>>>> could not open file $fname for appending, err: $err ') + return + } + f.writeln(s) or { + println('>>>> could not write to $fname, err: $err ') + return + } + // info := live.info() + // f.writeln('>>> reloads: ${info.reloads} | ok reloads: ${info.reloads_ok}') + f.close() +} + +fn myprintln(s string) { + append_to_file('#OUTPUT_FILE#', s) + println(s) + os.flush() +} + +[live] +fn pmessage() string { + return 'ORIGINAL' +} + +const ( + delay = 20 +) + +fn edefault(name string, default string) string { + res := os.getenv(name) + if res == '' { + return default + } + return res +} + +fn main() { + mut info := live.info() + info.recheck_period_ms = 5 + myprintln('START') + myprintln('DATE: ' + time.now().str()) + pmessage() + pmessage() + max_cycles := edefault('LIVE_CYCLES', '1').int() + // NB: 1000 * 20 = maximum of ~20s runtime + for i := 0; i < max_cycles; i++ { + s := pmessage() + myprintln(s) + append_to_file(os.resource_abs_path(s + '.txt'), s) + if s == 'STOP' { + break + } + time.sleep(delay * time.millisecond) + } + pmessage() + pmessage() + myprintln('DATE: ' + time.now().str()) + myprintln('END') +} diff --git a/v_windows/v/old/vlib/v/live/sharedlib/live_sharedlib.v b/v_windows/v/old/vlib/v/live/sharedlib/live_sharedlib.v new file mode 100644 index 0000000..6ae965d --- /dev/null +++ b/v_windows/v/old/vlib/v/live/sharedlib/live_sharedlib.v @@ -0,0 +1,7 @@ +module sharedlib + +import v.live + +pub const ( + is_used = live.is_used + 1 +) diff --git a/v_windows/v/old/vlib/v/markused/markused.v b/v_windows/v/old/vlib/v/markused/markused.v new file mode 100644 index 0000000..bad2df9 --- /dev/null +++ b/v_windows/v/old/vlib/v/markused/markused.v @@ -0,0 +1,421 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license that can be found in the LICENSE file. +module markused + +import v.ast +import v.util +import v.pref + +// mark_used walks the AST, starting at main() and marks all used fns transitively +pub fn mark_used(mut table ast.Table, pref &pref.Preferences, ast_files []&ast.File) { + mut all_fns, all_consts, all_globals := all_fn_const_and_global(ast_files) + util.timing_start(@METHOD) + defer { + util.timing_measure(@METHOD) + } + mut all_fn_root_names := [ + 'main.main', + '__new_array', + 'str_intp', + 'format_sb', + '__new_array_with_default', + '__new_array_with_array_default', + 'v_realloc' /* needed for _STR */, + 'malloc', + 'malloc_noscan', + 'vcalloc', + 'vcalloc_noscan', + 'new_array_from_c_array', + 'v_fixed_index', + 'memdup', + 'vstrlen', + '__as_cast', + 'tos', + 'tos2', + 'tos3', + 'isnil', + 'opt_ok', + 'error', + '__print_assert_failure', + // utf8_str_visible_length is used by c/str.v + 'utf8_str_visible_length', + 'compare_ints', + 'compare_u64s', + 'compare_strings', + 'compare_ints_reverse', + 'compare_u64s_reverse', + 'compare_strings_reverse', + 'builtin_init', + // byteptr and charptr + '3.vstring', + '3.vstring_with_len', + '4.vstring', + '4.vstring_with_len', + // byte. methods + '9.str_escaped', + // string. methods + '18.add', + '18.trim_space', + '18.repeat', + '18.replace', + '18.clone', + '18.clone_static', + '18.trim', + '18.substr', + '18.at', + '18.at_with_check', + '18.index_kmp', + // string. ==, !=, etc... + '18.eq', + '18.ne', + '18.lt', + '18.gt', + '18.le', + '18.ge', + 'fast_string_eq', + // other array methods + '20.get', + '20.set', + '20.get_unsafe', + '20.set_unsafe', + '20.get_with_check' /* used for `x := a[i] or {}` */, + '20.clone_static_to_depth', + '20.clone_to_depth', + '20.first', + '20.last', + '20.pointers' /* TODO: handle generic methods calling array primitives more precisely in pool_test.v */, + '20.reverse', + '20.repeat_to_depth', + '20.slice', + '20.slice2', + '59.get', + '59.set', + '65556.last', + '65556.pop', + '65556.push', + '65556.insert_many', + '65556.prepend_many', + '65556.reverse', + '65556.set', + '65556.set_unsafe', + // TODO: process the _vinit const initializations automatically too + 'json.decode_string', + 'json.decode_int', + 'json.decode_bool', + 'json.decode_u64', + 'json.encode_int', + 'json.encode_string', + 'json.encode_bool', + 'json.encode_u64', + 'json.json_print', + 'json.json_parse', + 'main.cb_propagate_test_error', + 'os.getwd', + 'os.init_os_args', + 'os.init_os_args_wide', + ] + + if pref.is_bare { + all_fn_root_names << [ + 'strlen', + 'memcmp', + 'memcpy', + 'realloc', + 'vsnprintf', + 'vsprintf', + ] + } + + if pref.gc_mode in [.boehm_full_opt, .boehm_incr_opt] { + all_fn_root_names << [ + 'memdup_noscan', + '__new_array_noscan', + '__new_array_with_default_noscan', + '__new_array_with_array_default_noscan', + 'new_array_from_c_array_noscan', + '20.clone_static_to_depth_noscan', + '20.clone_to_depth_noscan', + '20.reverse_noscan', + '20.repeat_to_depth_noscan', + '65556.pop_noscan', + '65556.push_noscan', + '65556.push_many_noscan', + '65556.insert_noscan', + '65556.insert_many_noscan', + '65556.prepend_noscan', + '65556.prepend_many_noscan', + '65556.reverse_noscan', + '65556.grow_cap_noscan', + '65556.grow_len_noscan', + ] + } + + for k, mut mfn in all_fns { + $if trace_skip_unused_all_fns ? { + println('k: $k | mfn: $mfn.name') + } + mut method_receiver_typename := '' + if mfn.is_method { + method_receiver_typename = table.type_to_str(mfn.receiver.typ) + } + if method_receiver_typename == '&wyrand.WyRandRNG' { + // WyRandRNG is the default rand pseudo random generator + all_fn_root_names << k + continue + } + if method_receiver_typename == '&strings.Builder' { + // implicit string builders are generated in auto_eq_methods.v + all_fn_root_names << k + continue + } + // auto generated string interpolation functions, may + // call .str or .auto_str methods for user types: + if k.ends_with('.str') || k.ends_with('.auto_str') { + all_fn_root_names << k + continue + } + if k.ends_with('.init') { + all_fn_root_names << k + continue + } + if k.ends_with('.free') { + all_fn_root_names << k + continue + } + + // sync: + if k == 'sync.new_channel_st' { + all_fn_root_names << k + continue + } + if k == 'sync.channel_select' { + all_fn_root_names << k + continue + } + if method_receiver_typename == '&sync.Channel' { + all_fn_root_names << k + continue + } + if k.ends_with('.lock') || k.ends_with('.unlock') || k.ends_with('.rlock') + || k.ends_with('.runlock') { + all_fn_root_names << k + continue + } + // testing framework: + if pref.is_test { + if k.starts_with('test_') || k.contains('.test_') { + all_fn_root_names << k + continue + } + if k.starts_with('testsuite_') || k.contains('.testsuite_') { + // eprintln('>>> test suite: $k') + all_fn_root_names << k + continue + } + } + // public/exported functions can not be skipped, + // especially when producing a shared library: + if mfn.is_pub && pref.is_shared { + all_fn_root_names << k + continue + } + if mfn.name in ['+', '-', '*', '%', '/', '<', '=='] { + // TODO: mark the used operators in the checker + all_fn_root_names << k + continue + } + if pref.prealloc && k.starts_with('prealloc_') { + all_fn_root_names << k + continue + } + } + + // handle assertions and testing framework callbacks: + if pref.is_debug { + all_fn_root_names << 'panic_debug' + } + all_fn_root_names << 'panic_optional_not_set' + if pref.is_test { + all_fn_root_names << 'main.cb_assertion_ok' + all_fn_root_names << 'main.cb_assertion_failed' + if benched_tests_sym := table.find_type('main.BenchedTests') { + bts_type := benched_tests_sym.methods[0].params[0].typ + all_fn_root_names << '${bts_type}.testing_step_start' + all_fn_root_names << '${bts_type}.testing_step_end' + all_fn_root_names << '${bts_type}.end_testing' + all_fn_root_names << 'main.start_testing' + } + } + + // handle interface implementation methods: + for isym in table.type_symbols { + if isym.kind != .interface_ { + continue + } + interface_info := isym.info as ast.Interface + if interface_info.methods.len == 0 { + continue + } + for itype in interface_info.types { + pitype := itype.set_nr_muls(1) + mut itypes := [itype] + if pitype != itype { + itypes << pitype + } + for method in interface_info.methods { + for typ in itypes { + interface_implementation_method_name := '${int(typ)}.$method.name' + $if trace_skip_unused_interface_methods ? { + eprintln('>> isym.name: $isym.name | interface_implementation_method_name: $interface_implementation_method_name') + } + all_fn_root_names << interface_implementation_method_name + } + } + } + } + + // handle vweb magic router methods: + typ_vweb_result := table.find_type_idx('vweb.Result') + if typ_vweb_result != 0 { + all_fn_root_names << 'vweb.filter' + typ_vweb_context := ast.Type(table.find_type_idx('vweb.Context')).set_nr_muls(1) + all_fn_root_names << '${int(typ_vweb_context)}.html' + for vgt in table.used_vweb_types { + sym_app := table.get_type_symbol(vgt) + for m in sym_app.methods { + if m.return_type == typ_vweb_result { + pvgt := vgt.set_nr_muls(1) + // eprintln('vgt: $vgt | pvgt: $pvgt | sym_app.name: $sym_app.name | m.name: $m.name') + all_fn_root_names << '${int(pvgt)}.$m.name' + } + } + } + } + + // handle ORM drivers: + orm_connection_implementations := table.iface_types['orm.Connection'] or { []ast.Type{} } + if orm_connection_implementations.len > 0 { + for k, _ in all_fns { + if k.starts_with('orm.') { + all_fn_root_names << k + } + } + for orm_type in orm_connection_implementations { + all_fn_root_names << '${int(orm_type)}.select' + all_fn_root_names << '${int(orm_type)}.insert' + all_fn_root_names << '${int(orm_type)}.update' + all_fn_root_names << '${int(orm_type)}.delete' + all_fn_root_names << '${int(orm_type)}.create' + all_fn_root_names << '${int(orm_type)}.drop' + all_fn_root_names << '${int(orm_type)}.last_id' + } + } + + // handle -live main programs: + if pref.is_livemain { + all_fn_root_names << 'v.live.executable.start_reloader' + all_fn_root_names << 'v.live.executable.new_live_reload_info' + } + + mut walker := Walker{ + table: table + files: ast_files + all_fns: all_fns + all_consts: all_consts + all_globals: all_globals + pref: pref + } + // println( all_fns.keys() ) + walker.mark_exported_fns() + walker.mark_root_fns(all_fn_root_names) + + if walker.n_asserts > 0 { + walker.fn_decl(mut all_fns['__print_assert_failure']) + } + if table.used_maps > 0 { + for k, mut mfn in all_fns { + mut method_receiver_typename := '' + if mfn.is_method { + method_receiver_typename = table.type_to_str(mfn.receiver.typ) + } + if k in ['new_map', 'new_map_init', 'map_hash_string'] + || method_receiver_typename == '&map' || method_receiver_typename == '&DenseArray' + || k.starts_with('map_') { + walker.fn_decl(mut mfn) + } + if pref.gc_mode in [.boehm_full_opt, .boehm_incr_opt] { + if k in ['new_map_noscan_key', 'new_map_noscan_value', 'new_map_noscan_key_value', + 'new_map_init_noscan_key', 'new_map_init_noscan_value', + 'new_map_init_noscan_key_value', + ] { + walker.fn_decl(mut mfn) + } + } + } + } else { + for map_fn_name in ['new_map', 'new_map_init', 'map_hash_string', 'new_dense_array'] { + walker.used_fns.delete(map_fn_name) + } + for k, mut mfn in all_fns { + if !mfn.is_method { + continue + } + method_receiver_typename := table.type_to_str(mfn.receiver.typ) + if method_receiver_typename in ['&map', '&mapnode', '&SortedMap', '&DenseArray'] { + walker.used_fns.delete(k) + } + } + } + + $if trace_skip_unused_fn_names ? { + for key, _ in walker.used_fns { + println('> used fn key: $key') + } + } + + table.used_fns = walker.used_fns.move() + table.used_consts = walker.used_consts.move() + table.used_globals = walker.used_globals.move() + + $if trace_skip_unused ? { + eprintln('>> t.used_fns: $table.used_fns.keys()') + eprintln('>> t.used_consts: $table.used_consts.keys()') + eprintln('>> t.used_globals: $table.used_globals.keys()') + eprintln('>> walker.table.used_maps: $walker.table.used_maps') + } +} + +fn all_fn_const_and_global(ast_files []&ast.File) (map[string]ast.FnDecl, map[string]ast.ConstField, map[string]ast.GlobalField) { + util.timing_start(@METHOD) + defer { + util.timing_measure(@METHOD) + } + mut all_fns := map[string]ast.FnDecl{} + mut all_consts := map[string]ast.ConstField{} + mut all_globals := map[string]ast.GlobalField{} + for i in 0 .. ast_files.len { + file := ast_files[i] + for node in file.stmts { + match node { + ast.FnDecl { + fkey := node.fkey() + all_fns[fkey] = node + } + ast.ConstDecl { + for cfield in node.fields { + ckey := cfield.name + all_consts[ckey] = cfield + } + } + ast.GlobalDecl { + for gfield in node.fields { + gkey := gfield.name + all_globals[gkey] = gfield + } + } + else {} + } + } + } + return all_fns, all_consts, all_globals +} diff --git a/v_windows/v/old/vlib/v/markused/walker.v b/v_windows/v/old/vlib/v/markused/walker.v new file mode 100644 index 0000000..fea9c09 --- /dev/null +++ b/v_windows/v/old/vlib/v/markused/walker.v @@ -0,0 +1,464 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license that can be found in the LICENSE file. +module markused + +// Walk the entire program starting at fn main and marks used (called) functions. +// Unused functions can be safely skipped by the backends to save CPU time and space. +import v.ast +import v.pref + +pub struct Walker { +pub mut: + table &ast.Table + used_fns map[string]bool // used_fns['println'] == true + used_consts map[string]bool // used_consts['os.args'] == true + used_globals map[string]bool + n_asserts int + pref &pref.Preferences +mut: + files []&ast.File + all_fns map[string]ast.FnDecl + all_consts map[string]ast.ConstField + all_globals map[string]ast.GlobalField +} + +pub fn (mut w Walker) mark_fn_as_used(fkey string) { + $if trace_skip_unused_marked ? { + eprintln(' fn > |$fkey|') + } + w.used_fns[fkey] = true +} + +pub fn (mut w Walker) mark_const_as_used(ckey string) { + $if trace_skip_unused_marked ? { + eprintln(' const > |$ckey|') + } + w.used_consts[ckey] = true + cfield := w.all_consts[ckey] or { return } + w.expr(cfield.expr) +} + +pub fn (mut w Walker) mark_global_as_used(ckey string) { + $if trace_skip_unused_marked ? { + eprintln(' global > |$ckey|') + } + w.used_globals[ckey] = true + gfield := w.all_globals[ckey] or { return } + w.expr(gfield.expr) +} + +pub fn (mut w Walker) mark_root_fns(all_fn_root_names []string) { + for fn_name in all_fn_root_names { + if fn_name !in w.used_fns { + $if trace_skip_unused_roots ? { + println('>>>> $fn_name uses: ') + } + w.fn_decl(mut w.all_fns[fn_name]) + } + } +} + +pub fn (mut w Walker) mark_exported_fns() { + for _, mut func in w.all_fns { + if func.is_exported { + w.fn_decl(mut func) + } + } +} + +pub fn (mut w Walker) stmt(node ast.Stmt) { + match mut node { + ast.EmptyStmt {} + ast.AsmStmt { + w.asm_io(node.output) + w.asm_io(node.input) + } + ast.AssertStmt { + if node.is_used { + w.expr(node.expr) + w.n_asserts++ + } + } + ast.AssignStmt { + w.exprs(node.left) + w.exprs(node.right) + } + ast.Block { + w.stmts(node.stmts) + } + ast.CompFor { + w.stmts(node.stmts) + } + ast.ConstDecl { + w.const_fields(node.fields) + } + ast.ExprStmt { + w.expr(node.expr) + } + ast.FnDecl { + w.fn_decl(mut node) + } + ast.ForCStmt { + w.expr(node.cond) + w.stmt(node.inc) + w.stmts(node.stmts) + } + ast.ForInStmt { + w.expr(node.cond) + w.expr(node.high) + w.stmts(node.stmts) + if node.kind == .map { + w.table.used_maps++ + } + } + ast.ForStmt { + w.expr(node.cond) + w.stmts(node.stmts) + } + ast.Return { + w.exprs(node.exprs) + } + ast.SqlStmt { + w.expr(node.db_expr) + for line in node.lines { + w.expr(line.where_expr) + w.exprs(line.update_exprs) + } + } + ast.StructDecl { + w.struct_fields(node.fields) + } + ast.DeferStmt { + w.stmts(node.stmts) + } + ast.GlobalDecl { + for gf in node.fields { + if gf.has_expr { + w.expr(gf.expr) + } + } + } + ast.BranchStmt {} + ast.EnumDecl {} + ast.GotoLabel {} + ast.GotoStmt {} + ast.HashStmt {} + ast.Import {} + ast.InterfaceDecl {} + ast.Module {} + ast.TypeDecl {} + ast.NodeError {} + } +} + +fn (mut w Walker) asm_io(ios []ast.AsmIO) { + for io in ios { + w.expr(io.expr) + } +} + +fn (mut w Walker) defer_stmts(stmts []ast.DeferStmt) { + for stmt in stmts { + w.stmts(stmt.stmts) + } +} + +fn (mut w Walker) stmts(stmts []ast.Stmt) { + for stmt in stmts { + w.stmt(stmt) + } +} + +fn (mut w Walker) exprs(exprs []ast.Expr) { + for expr in exprs { + w.expr(expr) + } +} + +fn (mut w Walker) expr(node ast.Expr) { + match mut node { + ast.EmptyExpr { + // TODO make sure this doesn't happen + // panic('Walker: EmptyExpr') + } + ast.AnonFn { + w.fn_decl(mut node.decl) + } + ast.ArrayInit { + w.expr(node.len_expr) + w.expr(node.cap_expr) + w.expr(node.default_expr) + w.exprs(node.exprs) + } + ast.Assoc { + w.exprs(node.exprs) + } + ast.ArrayDecompose { + w.expr(node.expr) + } + ast.CallExpr { + w.call_expr(mut node) + } + ast.CastExpr { + w.expr(node.expr) + w.expr(node.arg) + } + ast.ChanInit { + w.expr(node.cap_expr) + } + ast.ConcatExpr { + w.exprs(node.vals) + } + ast.ComptimeSelector { + w.expr(node.left) + w.expr(node.field_expr) + } + ast.ComptimeCall { + w.expr(node.left) + if node.is_vweb { + w.stmts(node.vweb_tmpl.stmts) + } + } + ast.DumpExpr { + w.expr(node.expr) + w.fn_by_name('eprint') + w.fn_by_name('eprintln') + } + ast.GoExpr { + w.expr(node.call_expr) + if w.pref.os == .windows { + w.fn_by_name('panic_lasterr') + w.fn_by_name('winapi_lasterr_str') + } else { + w.fn_by_name('c_error_number_str') + w.fn_by_name('panic_error_number') + } + } + ast.IndexExpr { + w.expr(node.left) + w.expr(node.index) + w.or_block(node.or_expr) + sym := w.table.get_final_type_symbol(node.left_type) + if sym.kind == .map { + w.table.used_maps++ + } + } + ast.InfixExpr { + w.expr(node.left) + w.expr(node.right) + w.or_block(node.or_block) + if node.left_type == 0 { + return + } + sym := w.table.get_type_symbol(node.left_type) + if sym.kind == .struct_ { + if opmethod := sym.find_method(node.op.str()) { + w.fn_decl(mut &ast.FnDecl(opmethod.source_fn)) + } + } + right_sym := w.table.get_type_symbol(node.right_type) + if node.op in [.not_in, .key_in] && right_sym.kind == .map { + w.table.used_maps++ + } + } + ast.IfGuardExpr { + w.expr(node.expr) + } + ast.IfExpr { + w.expr(node.left) + for b in node.branches { + w.expr(b.cond) + w.stmts(b.stmts) + } + } + ast.Ident { + match node.kind { + .constant { + w.mark_const_as_used(node.name) + } + .function { + w.fn_by_name(node.name) + } + .global { + w.mark_global_as_used(node.name) + } + else { + // `.unresolved`, `.blank_ident`, `.variable`, `.function` + // println('>>> else, ast.Ident kind: $node.kind') + } + } + } + ast.Likely { + w.expr(node.expr) + } + ast.MapInit { + w.exprs(node.keys) + w.exprs(node.vals) + w.table.used_maps++ + } + ast.MatchExpr { + w.expr(node.cond) + for b in node.branches { + w.exprs(b.exprs) + w.stmts(b.stmts) + } + } + ast.None {} + ast.ParExpr { + w.expr(node.expr) + } + ast.PrefixExpr { + w.expr(node.right) + } + ast.PostfixExpr { + w.expr(node.expr) + } + ast.RangeExpr { + if node.has_low { + w.expr(node.low) + } + if node.has_high { + w.expr(node.high) + } + } + ast.SizeOf, ast.IsRefType { + w.expr(node.expr) + } + ast.StringInterLiteral { + w.exprs(node.exprs) + } + ast.SelectorExpr { + w.expr(node.expr) + } + ast.SqlExpr { + w.expr(node.db_expr) + w.expr(node.offset_expr) + w.expr(node.order_expr) + w.expr(node.limit_expr) + w.expr(node.where_expr) + } + ast.StructInit { + sym := w.table.get_type_symbol(node.typ) + if sym.kind == .struct_ { + info := sym.info as ast.Struct + for ifield in info.fields { + if ifield.has_default_expr { + w.expr(ifield.default_expr) + } + } + } + if node.has_update_expr { + w.expr(node.update_expr) + } + for sif in node.fields { + w.expr(sif.expr) + } + for sie in node.embeds { + w.expr(sie.expr) + } + } + ast.TypeOf { + w.expr(node.expr) + } + /// + ast.AsCast { + w.expr(node.expr) + } + ast.AtExpr {} + ast.BoolLiteral {} + ast.FloatLiteral {} + ast.CharLiteral {} + ast.IntegerLiteral {} + ast.StringLiteral {} + ast.CTempVar { + w.expr(node.orig) + } + ast.Comment {} + ast.EnumVal {} + ast.LockExpr { + w.stmts(node.stmts) + } + ast.OffsetOf {} + ast.OrExpr { + w.or_block(node) + } + ast.SelectExpr { + for branch in node.branches { + w.stmt(branch.stmt) + w.stmts(branch.stmts) + } + } + ast.TypeNode {} + ast.UnsafeExpr { + w.expr(node.expr) + } + ast.NodeError {} + } +} + +pub fn (mut w Walker) fn_decl(mut node ast.FnDecl) { + if node.language == .c { + return + } + fkey := node.fkey() + if w.used_fns[fkey] { + // This function is already known to be called, meaning it has been processed already. + // Save CPU time and do nothing. + return + } + w.mark_fn_as_used(fkey) + w.stmts(node.stmts) + w.defer_stmts(node.defer_stmts) +} + +pub fn (mut w Walker) call_expr(mut node ast.CallExpr) { + for arg in node.args { + w.expr(arg.expr) + } + if node.language == .c { + return + } + w.expr(node.left) + w.or_block(node.or_block) + // + fn_name := node.fkey() + if w.used_fns[fn_name] { + return + } + w.mark_fn_as_used(fn_name) + stmt := w.all_fns[fn_name] or { return } + if stmt.name == node.name { + if !node.is_method || (node.receiver_type == stmt.receiver.typ) { + w.stmts(stmt.stmts) + } + } +} + +pub fn (mut w Walker) fn_by_name(fn_name string) { + if w.used_fns[fn_name] { + return + } + stmt := w.all_fns[fn_name] or { return } + w.mark_fn_as_used(fn_name) + w.stmts(stmt.stmts) +} + +pub fn (mut w Walker) struct_fields(sfields []ast.StructField) { + for sf in sfields { + if sf.has_default_expr { + w.expr(sf.default_expr) + } + } +} + +pub fn (mut w Walker) const_fields(cfields []ast.ConstField) { + for cf in cfields { + w.expr(cf.expr) + } +} + +pub fn (mut w Walker) or_block(node ast.OrExpr) { + if node.kind == .block { + w.stmts(node.stmts) + } +} diff --git a/v_windows/v/old/vlib/v/parser/assign.v b/v_windows/v/old/vlib/v/parser/assign.v new file mode 100644 index 0000000..76c074a --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/assign.v @@ -0,0 +1,236 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module parser + +import v.ast + +fn (mut p Parser) assign_stmt() ast.Stmt { + mut defer_vars := p.defer_vars + p.defer_vars = []ast.Ident{} + + exprs, comments := p.expr_list() + + if !(p.inside_defer && p.tok.kind == .decl_assign) { + defer_vars << p.defer_vars + } + p.defer_vars = defer_vars + return p.partial_assign_stmt(exprs, comments) +} + +fn (mut p Parser) check_undefined_variables(exprs []ast.Expr, val ast.Expr) ? { + match val { + ast.Ident { + for expr in exprs { + if expr is ast.Ident { + if expr.name == val.name && expr.kind != .blank_ident { + p.error_with_pos('undefined variable: `$val.name`', val.pos) + return error('undefined variable: `$val.name`') + } + } + } + } + ast.ArrayInit { + if val.has_cap { + p.check_undefined_variables(exprs, val.cap_expr) ? + } + if val.has_len { + p.check_undefined_variables(exprs, val.len_expr) ? + } + if val.has_default { + p.check_undefined_variables(exprs, val.default_expr) ? + } + for expr in val.exprs { + p.check_undefined_variables(exprs, expr) ? + } + } + ast.CallExpr { + p.check_undefined_variables(exprs, val.left) ? + for arg in val.args { + p.check_undefined_variables(exprs, arg.expr) ? + } + } + ast.InfixExpr { + p.check_undefined_variables(exprs, val.left) ? + p.check_undefined_variables(exprs, val.right) ? + } + ast.MapInit { + for key in val.keys { + p.check_undefined_variables(exprs, key) ? + } + for value in val.vals { + p.check_undefined_variables(exprs, value) ? + } + } + ast.ParExpr { + p.check_undefined_variables(exprs, val.expr) ? + } + ast.PostfixExpr { + p.check_undefined_variables(exprs, val.expr) ? + } + ast.PrefixExpr { + p.check_undefined_variables(exprs, val.right) ? + } + ast.StringInterLiteral { + for expr_ in val.exprs { + p.check_undefined_variables(exprs, expr_) ? + } + } + else {} + } +} + +fn (mut p Parser) check_cross_variables(exprs []ast.Expr, val ast.Expr) bool { + val_str := val.str() + match val { + ast.Ident { + for expr in exprs { + if expr is ast.Ident { + if expr.name == val.name { + return true + } + } + } + } + ast.IndexExpr { + for expr in exprs { + if expr.str() == val_str { + return true + } + } + } + ast.InfixExpr { + return p.check_cross_variables(exprs, val.left) + || p.check_cross_variables(exprs, val.right) + } + ast.PrefixExpr { + return p.check_cross_variables(exprs, val.right) + } + ast.PostfixExpr { + return p.check_cross_variables(exprs, val.expr) + } + ast.SelectorExpr { + for expr in exprs { + if expr.str() == val_str { + return true + } + } + } + else {} + } + return false +} + +fn (mut p Parser) partial_assign_stmt(left []ast.Expr, left_comments []ast.Comment) ast.Stmt { + p.is_stmt_ident = false + op := p.tok.kind + mut pos := p.tok.position() + p.next() + mut comments := []ast.Comment{cap: 2 * left_comments.len + 1} + comments << left_comments + comments << p.eat_comments() + mut right_comments := []ast.Comment{} + mut right := []ast.Expr{cap: left.len} + right, right_comments = p.expr_list() + comments << right_comments + end_comments := p.eat_comments(same_line: true) + mut has_cross_var := false + if op == .decl_assign { + // a, b := a + 1, b + for r in right { + p.check_undefined_variables(left, r) or { + return p.error('check_undefined_variables failed') + } + } + } else if left.len > 1 { + // a, b = b, a + for r in right { + has_cross_var = p.check_cross_variables(left, r) + if op !in [.assign, .decl_assign] { + return p.error_with_pos('unexpected $op.str(), expecting := or = or comma', + pos) + } + if has_cross_var { + break + } + } + } + mut is_static := false + for i, lx in left { + match mut lx { + ast.Ident { + if op == .decl_assign { + if p.scope.known_var(lx.name) { + return p.error_with_pos('redefinition of `$lx.name`', lx.pos) + } + mut share := ast.ShareType(0) + if lx.info is ast.IdentVar { + iv := lx.info as ast.IdentVar + share = iv.share + if iv.is_static { + if !p.pref.translated && !p.pref.is_fmt && !p.inside_unsafe_fn { + return p.error_with_pos('static variables are supported only in -translated mode or in [unsafe] fn', + lx.pos) + } + is_static = true + } + } + r0 := right[0] + mut v := ast.Var{ + name: lx.name + expr: if left.len == right.len { right[i] } else { ast.empty_expr() } + share: share + is_mut: lx.is_mut || p.inside_for + pos: lx.pos + is_stack_obj: p.inside_for + } + if p.pref.autofree { + if r0 is ast.CallExpr { + // Set correct variable position (after the or block) + // so that autofree doesn't free it in cgen before + // it's declared. (`Or` variables are declared after the or block). + if r0.or_block.pos.pos > 0 && r0.or_block.stmts.len > 0 { + v.is_or = true + // v.pos = r0.or_block.pos. + } + } + } + obj := ast.ScopeObject(v) + lx.obj = obj + p.scope.register(obj) + } + } + ast.IndexExpr { + if op == .decl_assign { + return p.error_with_pos('non-name `$lx.left[$lx.index]` on left side of `:=`', + lx.pos) + } + lx.is_setter = true + } + ast.ParExpr {} + ast.PrefixExpr {} + ast.SelectorExpr { + if op == .decl_assign { + return p.error_with_pos('struct fields can only be declared during the initialization', + lx.pos) + } + } + else { + // TODO: parexpr ( check vars) + // else { p.error_with_pos('unexpected `${typeof(lx)}`', lx.position()) } + } + } + } + pos.update_last_line(p.prev_tok.line_nr) + return ast.AssignStmt{ + op: op + left: left + right: right + comments: comments + end_comments: end_comments + pos: pos + has_cross_var: has_cross_var + is_simple: p.inside_for && p.tok.kind == .lcbr + is_static: is_static + } +} diff --git a/v_windows/v/old/vlib/v/parser/comptime.v b/v_windows/v/old/vlib/v/parser/comptime.v new file mode 100644 index 0000000..7fde830 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/comptime.v @@ -0,0 +1,359 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module parser + +import os +import v.ast +import v.pref +import v.token + +const ( + supported_comptime_calls = ['html', 'tmpl', 'env', 'embed_file', 'pkgconfig'] +) + +// // #include, #flag, #v +fn (mut p Parser) hash() ast.HashStmt { + pos := p.tok.position() + val := p.tok.lit + kind := val.all_before(' ') + p.next() + mut main_str := '' + mut msg := '' + content := val.all_after('$kind ').all_before('//') + if content.contains(' #') { + main_str = content.all_before(' #').trim_space() + msg = content.all_after(' #').trim_space() + } else { + main_str = content.trim_space() + msg = '' + } + return ast.HashStmt{ + mod: p.mod + source_file: p.file_name + val: val + kind: kind + main: main_str + msg: msg + pos: pos + } +} + +fn (mut p Parser) comp_call() ast.ComptimeCall { + err_node := ast.ComptimeCall{ + scope: 0 + } + p.check(.dollar) + start_pos := p.prev_tok.position() + error_msg := 'only `\$tmpl()`, `\$env()`, `\$embed_file()`, `\$pkgconfig()` and `\$vweb.html()` comptime functions are supported right now' + if p.peek_tok.kind == .dot { + n := p.check_name() // skip `vweb.html()` TODO + if n != 'vweb' { + p.error(error_msg) + return err_node + } + p.check(.dot) + } + n := p.check_name() // (.name) + if n !in parser.supported_comptime_calls { + p.error(error_msg) + return err_node + } + is_embed_file := n == 'embed_file' + is_html := n == 'html' + // $env('ENV_VAR_NAME') + p.check(.lpar) + spos := p.tok.position() + if n == 'env' { + s := p.tok.lit + p.check(.string) + p.check(.rpar) + return ast.ComptimeCall{ + scope: 0 + method_name: n + args_var: s + is_env: true + env_pos: spos + pos: spos.extend(p.prev_tok.position()) + } + } + if n == 'pkgconfig' { + s := p.tok.lit + p.check(.string) + p.check(.rpar) + return ast.ComptimeCall{ + scope: 0 + method_name: n + args_var: s + is_pkgconfig: true + env_pos: spos + pos: spos.extend(p.prev_tok.position()) + } + } + literal_string_param := if is_html { '' } else { p.tok.lit } + path_of_literal_string_param := literal_string_param.replace('/', os.path_separator) + if !is_html { + p.check(.string) + } + p.check(.rpar) + // $embed_file('/path/to/file') + if is_embed_file { + mut epath := path_of_literal_string_param + // Validate that the epath exists, and that it is actually a file. + if epath == '' { + p.error_with_pos('supply a valid relative or absolute file path to the file to embed', + spos) + return err_node + } + if !p.pref.is_fmt { + abs_path := os.real_path(epath) + // check absolute path first + if !os.exists(abs_path) { + // ... look relative to the source file: + epath = os.real_path(os.join_path(os.dir(p.file_name), epath)) + if !os.exists(epath) { + p.error_with_pos('"$epath" does not exist so it cannot be embedded', + spos) + return err_node + } + if !os.is_file(epath) { + p.error_with_pos('"$epath" is not a file so it cannot be embedded', + spos) + return err_node + } + } else { + epath = abs_path + } + } + p.register_auto_import('v.embed_file') + return ast.ComptimeCall{ + scope: 0 + is_embed: true + embed_file: ast.EmbeddedFile{ + rpath: literal_string_param + apath: epath + } + pos: start_pos.extend(p.prev_tok.position()) + } + } + // Compile vweb html template to V code, parse that V code and embed the resulting V function + // that returns an html string. + fn_path := p.cur_fn_name.split('_') + fn_path_joined := fn_path.join(os.path_separator) + compiled_vfile_path := os.real_path(p.scanner.file_path.replace('/', os.path_separator)) + tmpl_path := if is_html { '${fn_path.last()}.html' } else { path_of_literal_string_param } + // Looking next to the vweb program + dir := os.dir(compiled_vfile_path) + mut path := os.join_path(dir, fn_path_joined) + path += '.html' + path = os.real_path(path) + if !is_html { + path = os.join_path(dir, tmpl_path) + } + if !os.exists(path) { + if is_html { + // can be in `templates/` + path = os.join_path(dir, 'templates', fn_path_joined) + path += '.html' + } + if !os.exists(path) { + if p.pref.is_fmt { + return ast.ComptimeCall{ + scope: 0 + is_vweb: true + method_name: n + args_var: literal_string_param + pos: start_pos.extend(p.prev_tok.position()) + } + } + if is_html { + p.error('vweb HTML template "$path" not found') + } else { + p.error('template file "$path" not found') + } + return err_node + } + // println('path is now "$path"') + } + tmp_fn_name := p.cur_fn_name.replace('.', '__') + $if trace_comptime ? { + println('>>> compiling comptime template file "$path" for $tmp_fn_name') + } + v_code := p.compile_template_file(path, tmp_fn_name) + $if print_vweb_template_expansions ? { + lines := v_code.split('\n') + for i, line in lines { + println('$path:${i + 1}: $line') + } + } + mut scope := &ast.Scope{ + start_pos: 0 + parent: p.table.global_scope + } + $if trace_comptime ? { + println('') + println('>>> template for $path:') + println(v_code) + println('>>> end of template END') + println('') + } + mut file := parse_comptime(v_code, p.table, p.pref, scope) + file.path = tmpl_path + // copy vars from current fn scope into vweb_tmpl scope + for stmt in file.stmts { + if stmt is ast.FnDecl { + if stmt.name == 'main.vweb_tmpl_$tmp_fn_name' { + // mut tmpl_scope := file.scope.innermost(stmt.body_pos.pos) + mut tmpl_scope := stmt.scope + for _, obj in p.scope.objects { + if obj is ast.Var { + mut v := obj + v.pos = stmt.body_pos + tmpl_scope.register(ast.Var{ + ...v + is_used: true + }) + // set the controller action var to used + // if it's unused in the template it will warn + v.is_used = true + } + } + break + } + } + } + return ast.ComptimeCall{ + scope: 0 + is_vweb: true + vweb_tmpl: file + method_name: n + args_var: literal_string_param + pos: start_pos.extend(p.prev_tok.position()) + } +} + +fn (mut p Parser) comp_for() ast.CompFor { + // p.comp_for() handles these special forms: + // $for method in App(methods) { + // $for field in App(fields) { + p.next() + p.check(.key_for) + var_pos := p.tok.position() + val_var := p.check_name() + p.check(.key_in) + mut typ_pos := p.tok.position() + lang := p.parse_language() + typ := p.parse_any_type(lang, false, false) + typ_pos = typ_pos.extend(p.prev_tok.position()) + p.check(.dot) + for_val := p.check_name() + mut kind := ast.CompForKind.methods + p.open_scope() + if for_val == 'methods' { + p.scope.register(ast.Var{ + name: val_var + typ: p.table.find_type_idx('FunctionData') + pos: var_pos + }) + } else if for_val == 'fields' { + p.scope.register(ast.Var{ + name: val_var + typ: p.table.find_type_idx('FieldData') + pos: var_pos + }) + kind = .fields + } else if for_val == 'attributes' { + p.scope.register(ast.Var{ + name: val_var + typ: p.table.find_type_idx('StructAttribute') + pos: var_pos + }) + kind = .attributes + } else { + p.error_with_pos('unknown kind `$for_val`, available are: `methods`, `fields` or `attributes`', + p.prev_tok.position()) + return ast.CompFor{} + } + spos := p.tok.position() + stmts := p.parse_block() + p.close_scope() + return ast.CompFor{ + val_var: val_var + stmts: stmts + kind: kind + typ: typ + typ_pos: typ_pos + pos: spos.extend(p.tok.position()) + } +} + +// @FN, @STRUCT, @MOD etc. See full list in token.valid_at_tokens +fn (mut p Parser) at() ast.AtExpr { + name := p.tok.lit + kind := match name { + '@FN' { token.AtKind.fn_name } + '@METHOD' { token.AtKind.method_name } + '@MOD' { token.AtKind.mod_name } + '@STRUCT' { token.AtKind.struct_name } + '@FILE' { token.AtKind.file_path } + '@LINE' { token.AtKind.line_nr } + '@COLUMN' { token.AtKind.column_nr } + '@VHASH' { token.AtKind.vhash } + '@VMOD_FILE' { token.AtKind.vmod_file } + '@VEXE' { token.AtKind.vexe_path } + '@VEXEROOT' { token.AtKind.vexeroot_path } + '@VMODROOT' { token.AtKind.vmodroot_path } + '@VROOT' { token.AtKind.vroot_path } // deprecated, use @VEXEROOT or @VMODROOT + else { token.AtKind.unknown } + } + p.next() + return ast.AtExpr{ + name: name + pos: p.tok.position() + kind: kind + } +} + +fn (mut p Parser) comptime_selector(left ast.Expr) ast.Expr { + p.check(.dollar) + start_pos := p.prev_tok.position() + if p.peek_tok.kind == .lpar { + method_pos := p.tok.position() + method_name := p.check_name() + p.mark_var_as_used(method_name) + // `app.$action()` (`action` is a string) + p.check(.lpar) + args := p.call_args() + p.check(.rpar) + if p.tok.kind == .key_orelse { + p.check(.key_orelse) + p.check(.lcbr) + } + return ast.ComptimeCall{ + left: left + method_name: method_name + method_pos: method_pos + scope: p.scope + args_var: '' + args: args + pos: start_pos.extend(p.prev_tok.position()) + } + } + mut has_parens := false + if p.tok.kind == .lpar { + p.check(.lpar) + has_parens = true + } else { + p.warn_with_pos('use brackets instead e.g. `s.$(field.name)` - run vfmt', p.tok.position()) + } + expr := p.expr(0) + if has_parens { + p.check(.rpar) + } + return ast.ComptimeSelector{ + has_parens: has_parens + left: left + field_expr: expr + pos: start_pos.extend(p.prev_tok.position()) + } +} diff --git a/v_windows/v/old/vlib/v/parser/containers.v b/v_windows/v/old/vlib/v/parser/containers.v new file mode 100644 index 0000000..1b22ca8 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/containers.v @@ -0,0 +1,190 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module parser + +import v.ast + +fn (mut p Parser) array_init() ast.ArrayInit { + first_pos := p.tok.position() + mut last_pos := p.tok.position() + p.check(.lsbr) + // p.warn('array_init() exp=$p.expected_type') + mut array_type := ast.void_type + mut elem_type := ast.void_type + mut elem_type_pos := first_pos + mut exprs := []ast.Expr{} + mut ecmnts := [][]ast.Comment{} + mut pre_cmnts := []ast.Comment{} + mut is_fixed := false + mut has_val := false + mut has_type := false + mut has_default := false + mut default_expr := ast.empty_expr() + if p.tok.kind == .rsbr { + last_pos = p.tok.position() + // []typ => `[]` and `typ` must be on the same line + line_nr := p.tok.line_nr + p.next() + // []string + if p.tok.kind in [.name, .amp, .lsbr, .key_shared] && p.tok.line_nr == line_nr { + elem_type_pos = p.tok.position() + elem_type = p.parse_type() + // this is set here because it's a known type, others could be the + // result of expr so we do those in checker + idx := p.table.find_or_register_array(elem_type) + if elem_type.has_flag(.generic) { + array_type = ast.new_type(idx).set_flag(.generic) + } else { + array_type = ast.new_type(idx) + } + has_type = true + } + last_pos = p.tok.position() + } else { + // [1,2,3] or [const]byte + old_inside_array_lit := p.inside_array_lit + p.inside_array_lit = true + pre_cmnts = p.eat_comments() + for i := 0; p.tok.kind !in [.rsbr, .eof]; i++ { + exprs << p.expr(0) + ecmnts << p.eat_comments() + if p.tok.kind == .comma { + p.next() + } + ecmnts.last() << p.eat_comments() + } + p.inside_array_lit = old_inside_array_lit + line_nr := p.tok.line_nr + $if tinyc { + // NB: do not remove the next line without testing + // v selfcompilation with tcc first + tcc_stack_bug := 12345 + _ = tcc_stack_bug + } + last_pos = p.tok.position() + p.check(.rsbr) + if exprs.len == 1 && p.tok.kind in [.name, .amp, .lsbr] && p.tok.line_nr == line_nr { + // [100]byte + elem_type = p.parse_type() + last_pos = p.tok.position() + is_fixed = true + if p.tok.kind == .lcbr { + p.next() + if p.tok.kind != .rcbr { + pos := p.tok.position() + n := p.check_name() + if n != 'init' { + p.error_with_pos('expected `init:`, not `$n`', pos) + return ast.ArrayInit{} + } + p.check(.colon) + has_default = true + default_expr = p.expr(0) + } + last_pos = p.tok.position() + p.check(.rcbr) + } else { + p.warn_with_pos('use e.g. `x := [1]Type{}` instead of `x := [1]Type`', + first_pos.extend(last_pos)) + } + } else { + if p.tok.kind == .not { // && p.tok.line_nr == p.prev_tok.line_nr { + last_pos = p.tok.position() + is_fixed = true + has_val = true + p.next() + } + if p.tok.kind == .not && p.tok.line_nr == p.prev_tok.line_nr { + last_pos = p.tok.position() + p.error_with_pos('use e.g. `[1, 2, 3]!` instead of `[1, 2, 3]!!`', last_pos) + p.next() + } + } + } + if exprs.len == 0 && p.tok.kind != .lcbr && has_type { + if !p.pref.is_fmt { + p.warn_with_pos('use `x := []Type{}` instead of `x := []Type`', first_pos.extend(last_pos)) + } + } + mut has_len := false + mut has_cap := false + mut len_expr := ast.empty_expr() + mut cap_expr := ast.empty_expr() + if p.tok.kind == .lcbr && exprs.len == 0 && array_type != ast.void_type { + // `[]int{ len: 10, cap: 100}` syntax + p.next() + for p.tok.kind != .rcbr { + key := p.check_name() + p.check(.colon) + match key { + 'len' { + has_len = true + len_expr = p.expr(0) + } + 'cap' { + has_cap = true + cap_expr = p.expr(0) + } + 'init' { + has_default = true + default_expr = p.expr(0) + } + else { + p.error('wrong field `$key`, expecting `len`, `cap`, or `init`') + return ast.ArrayInit{} + } + } + if p.tok.kind != .rcbr { + p.check(.comma) + } + } + p.check(.rcbr) + } + pos := first_pos.extend_with_last_line(last_pos, p.prev_tok.line_nr) + return ast.ArrayInit{ + is_fixed: is_fixed + has_val: has_val + mod: p.mod + elem_type: elem_type + typ: array_type + exprs: exprs + ecmnts: ecmnts + pre_cmnts: pre_cmnts + pos: pos + elem_type_pos: elem_type_pos + has_len: has_len + len_expr: len_expr + has_cap: has_cap + has_default: has_default + cap_expr: cap_expr + default_expr: default_expr + } +} + +// parse tokens between braces +fn (mut p Parser) map_init() ast.MapInit { + first_pos := p.prev_tok.position() + mut keys := []ast.Expr{} + mut vals := []ast.Expr{} + mut comments := [][]ast.Comment{} + pre_cmnts := p.eat_comments() + for p.tok.kind !in [.rcbr, .eof] { + key := p.expr(0) + keys << key + p.check(.colon) + val := p.expr(0) + vals << val + if p.tok.kind == .comma { + p.next() + } + comments << p.eat_comments() + } + return ast.MapInit{ + keys: keys + vals: vals + pos: first_pos.extend_with_last_line(p.tok.position(), p.tok.line_nr) + comments: comments + pre_cmnts: pre_cmnts + } +} diff --git a/v_windows/v/old/vlib/v/parser/expr.v b/v_windows/v/old/vlib/v/parser/expr.v new file mode 100644 index 0000000..746e928 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/expr.v @@ -0,0 +1,650 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module parser + +import v.ast +import v.vet +import v.token + +pub fn (mut p Parser) expr(precedence int) ast.Expr { + return p.check_expr(precedence) or { + p.error_with_pos('invalid expression: unexpected $p.tok', p.tok.position()) + } +} + +pub fn (mut p Parser) check_expr(precedence int) ?ast.Expr { + $if trace_parser ? { + tok_pos := p.tok.position() + eprintln('parsing file: ${p.file_name:-30} | tok.kind: ${p.tok.kind:-10} | tok.lit: ${p.tok.lit:-10} | tok_pos: ${tok_pos.str():-45} | expr($precedence)') + } + // println('\n\nparser.expr()') + mut node := ast.empty_expr() + is_stmt_ident := p.is_stmt_ident + p.is_stmt_ident = false + if !p.pref.is_fmt { + p.eat_comments() + } + inside_array_lit := p.inside_array_lit + p.inside_array_lit = false + defer { + p.inside_array_lit = inside_array_lit + } + // Prefix + match p.tok.kind { + .key_mut, .key_shared, .key_atomic, .key_static { + ident := p.parse_ident(ast.Language.v) + node = ident + if p.inside_defer { + if !p.defer_vars.any(it.name == ident.name && it.mod == ident.mod) + && ident.name != 'err' { + p.defer_vars << ident + } + } + p.is_stmt_ident = is_stmt_ident + } + .name, .question { + if p.tok.lit == 'sql' && p.peek_tok.kind == .name { + p.inside_match = true // reuse the same var for perf instead of inside_sql TODO rename + node = p.sql_expr() + p.inside_match = false + } else if p.tok.lit == 'map' && p.peek_tok.kind == .lcbr && !(p.builtin_mod + && p.file_base in ['map.v', 'map_d_gcboehm_opt.v']) { + p.next() // `map` + p.next() // `{` + node = p.map_init() + p.check(.rcbr) // `}` + } else { + if p.inside_if && p.is_generic_name() { + // $if T is string {} + p.expecting_type = true + } + node = p.name_expr() + p.is_stmt_ident = is_stmt_ident + } + } + .string { + node = p.string_expr() + } + .comment { + node = p.comment() + return node + } + .dot { + // .enum_val + node = p.enum_val() + } + .at { + node = p.at() + } + .dollar { + match p.peek_tok.kind { + .name { + return p.comp_call() + } + .key_if { + return p.if_expr(true) + } + else { + return p.error_with_pos('unexpected `$`', p.peek_tok.position()) + } + } + } + .chartoken { + node = ast.CharLiteral{ + val: p.tok.lit + pos: p.tok.position() + } + p.next() + } + .amp, .mul, .not, .bit_not, .arrow { + // &x, *x, !x, ~x, <-x + node = p.prefix_expr() + } + .minus { + // -1, -a + if p.peek_tok.kind == .number { + node = p.parse_number_literal() + } else { + node = p.prefix_expr() + } + } + .key_go { + mut go_expr := p.go_expr() + go_expr.is_expr = true + node = go_expr + } + .key_true, .key_false { + node = ast.BoolLiteral{ + val: p.tok.kind == .key_true + pos: p.tok.position() + } + p.next() + } + .key_match { + node = p.match_expr() + } + .key_select { + node = p.select_expr() + } + .number { + node = p.parse_number_literal() + } + .lpar { + mut pos := p.tok.position() + p.check(.lpar) + node = p.expr(0) + p.check(.rpar) + node = ast.ParExpr{ + expr: node + pos: pos.extend(p.prev_tok.position()) + } + } + .key_if { + node = p.if_expr(false) + } + .key_unsafe { + // unsafe { + mut pos := p.tok.position() + p.next() + if p.inside_unsafe { + return p.error_with_pos('already inside `unsafe` block', pos) + } + p.inside_unsafe = true + p.check(.lcbr) + e := p.expr(0) + p.check(.rcbr) + pos.update_last_line(p.prev_tok.line_nr) + node = ast.UnsafeExpr{ + expr: e + pos: pos + } + p.inside_unsafe = false + } + .key_lock, .key_rlock { + node = p.lock_expr() + } + .lsbr { + if p.expecting_type { + // parse json.decode type (`json.decode([]User, s)`) + node = p.name_expr() + } else if p.is_amp && p.peek_tok.kind == .rsbr && p.peek_token(3).kind != .lcbr { + pos := p.tok.position() + typ := p.parse_type() + typname := p.table.get_type_symbol(typ).name + p.check(.lpar) + expr := p.expr(0) + p.check(.rpar) + node = ast.CastExpr{ + typ: typ + typname: typname + expr: expr + pos: pos + } + } else { + node = p.array_init() + } + } + .key_none { + pos := p.tok.position() + p.next() + node = ast.None{ + pos: pos + } + } + .key_sizeof, .key_isreftype { + is_reftype := p.tok.kind == .key_isreftype + p.next() // sizeof + p.check(.lpar) + pos := p.tok.position() + is_known_var := p.mark_var_as_used(p.tok.lit) + // assume mod. prefix leads to a type + if is_known_var || !(p.known_import(p.tok.lit) || p.tok.kind.is_start_of_type()) { + expr := p.expr(0) + if is_reftype { + node = ast.IsRefType{ + is_type: false + expr: expr + pos: pos + } + } else { + node = ast.SizeOf{ + is_type: false + expr: expr + pos: pos + } + } + } else { + if p.tok.kind == .name { + p.register_used_import(p.tok.lit) + } + save_expr_mod := p.expr_mod + p.expr_mod = '' + arg_type := p.parse_type() + p.expr_mod = save_expr_mod + if is_reftype { + node = ast.IsRefType{ + is_type: true + typ: arg_type + pos: pos + } + } else { + node = ast.SizeOf{ + is_type: true + typ: arg_type + pos: pos + } + } + } + p.check(.rpar) + } + .key_typeof { + spos := p.tok.position() + p.next() + p.check(.lpar) + expr := p.expr(0) + p.check(.rpar) + if p.tok.kind != .dot && p.tok.line_nr == p.prev_tok.line_nr { + p.warn_with_pos('use e.g. `typeof(expr).name` or `sum_type_instance.type_name()` instead', + spos) + } + node = ast.TypeOf{ + expr: expr + pos: spos.extend(p.tok.position()) + } + } + .key_dump { + spos := p.tok.position() + p.next() + p.check(.lpar) + expr := p.expr(0) + p.check(.rpar) + node = ast.DumpExpr{ + expr: expr + pos: spos.extend(p.tok.position()) + } + } + .key_offsetof { + pos := p.tok.position() + p.next() // __offsetof + p.check(.lpar) + st := p.parse_type() + p.check(.comma) + if p.tok.kind != .name { + return p.error_with_pos('unexpected `$p.tok.lit`, expecting struct field', + p.tok.position()) + } + field := p.tok.lit + p.next() + p.check(.rpar) + node = ast.OffsetOf{ + struct_type: st + field: field + pos: pos + } + } + .key_likely, .key_unlikely { + is_likely := p.tok.kind == .key_likely + p.next() + p.check(.lpar) + lpos := p.tok.position() + expr := p.expr(0) + p.check(.rpar) + node = ast.Likely{ + expr: expr + pos: lpos + is_likely: is_likely + } + } + .lcbr { + // TODO: remove this when deprecation will be removed, vfmt should handle it for a while + // Map `{"age": 20}` or `{ x | foo:bar, a:10 }` + p.next() + if p.tok.kind in [.chartoken, .number, .string] { + p.warn_with_pos("deprecated map syntax, use syntax like `map{'age': 20}`", + p.prev_tok.position()) + node = p.map_init() + } else { + // it should be a struct + if p.tok.kind == .name && p.peek_tok.kind == .pipe { + // TODO: remove deprecated + p.warn_with_pos('use e.g. `...struct_var` instead', p.peek_tok.position()) + node = p.assoc() + } else if (p.tok.kind == .name && p.peek_tok.kind == .colon) + || p.tok.kind in [.rcbr, .comment, .ellipsis] { + node = p.struct_init(true) // short_syntax: true + p.warn_with_pos('short struct initalization is deprecated, use explicit struct name', + node.position()) + } else if p.tok.kind == .name { + p.next() + return p.error_with_pos('unexpected $p.tok, expecting `:` after struct field name', + p.tok.position()) + } else { + return p.error_with_pos('unexpected $p.tok, expecting struct field name', + p.tok.position()) + } + } + p.check(.rcbr) + } + .key_fn { + if p.expecting_type { + // Anonymous function type + start_pos := p.tok.position() + return ast.TypeNode{ + typ: p.parse_type() + pos: start_pos.extend(p.prev_tok.position()) + } + } else { + // Anonymous function + node = p.anon_fn() + // its a call + // NOTE: this could be moved to just before the pratt loop + // then anything can be a call, eg. `index[2]()` or `struct.field()` + // but this would take a bit of modification + if p.tok.kind == .lpar { + p.next() + pos := p.tok.position() + args := p.call_args() + p.check(.rpar) + node = ast.CallExpr{ + name: 'anon' + left: node + args: args + pos: pos + scope: p.scope + } + } + return node + } + } + else { + if p.tok.kind != .eof && !(p.tok.kind == .rsbr && p.inside_asm) { + // eof should be handled where it happens + return none + // return p.error_with_pos('invalid expression: unexpected $p.tok', p.tok.position()) + } + } + } + if inside_array_lit { + if p.tok.kind in [.minus, .mul, .amp, .arrow] && p.tok.pos + 1 == p.peek_tok.pos + && p.prev_tok.pos + p.prev_tok.len + 1 != p.peek_tok.pos { + return node + } + } + return p.expr_with_left(node, precedence, is_stmt_ident) +} + +pub fn (mut p Parser) expr_with_left(left ast.Expr, precedence int, is_stmt_ident bool) ast.Expr { + mut node := left + if p.inside_asm && p.prev_tok.position().line_nr < p.tok.position().line_nr { + return node + } + // Infix + for precedence < p.tok.precedence() { + if p.tok.kind == .dot { + node = p.dot_expr(node) + if p.name_error { + return node + } + p.is_stmt_ident = is_stmt_ident + } else if p.tok.kind == .lsbr && (p.inside_fn || p.tok.line_nr == p.prev_tok.line_nr) { + node = p.index_expr(node) + p.is_stmt_ident = is_stmt_ident + if p.tok.kind == .lpar && p.tok.line_nr == p.prev_tok.line_nr && node is ast.IndexExpr { + p.next() + pos := p.tok.position() + args := p.call_args() + p.check(.rpar) + node = ast.CallExpr{ + left: node + args: args + pos: pos + scope: p.scope + } + p.is_stmt_ident = is_stmt_ident + } + } else if p.tok.kind == .key_as { + // sum type as cast `x := SumType as Variant` + if !p.inside_asm { + pos := p.tok.position() + p.next() + typ := p.parse_type() + node = ast.AsCast{ + expr: node + typ: typ + pos: pos + } + } else { + return node + } + } else if p.tok.kind == .left_shift && p.is_stmt_ident { + // arr << elem + tok := p.tok + mut pos := tok.position() + p.next() + right := p.expr(precedence - 1) + pos.update_last_line(p.prev_tok.line_nr) + if mut node is ast.IndexExpr { + node.recursive_mapset_is_setter(true) + } + node = ast.InfixExpr{ + left: node + right: right + op: tok.kind + pos: pos + is_stmt: true + } + } else if p.tok.kind.is_infix() { + if p.tok.kind.is_prefix() && p.tok.line_nr != p.prev_tok.line_nr { + // return early for deref assign `*x = 2` goes to prefix expr + if p.tok.kind == .mul && p.peek_token(2).kind == .assign { + return node + } + // added 10/2020: LATER this will be parsed as PrefixExpr instead + p.warn_with_pos('move infix `$p.tok.kind` operator before new line (if infix intended) or use brackets for a prefix expression', + p.tok.position()) + } + // continue on infix expr + node = p.infix_expr(node) + // return early `if bar is SumType as b {` + if p.tok.kind == .key_as && p.inside_if { + return node + } + } else if p.tok.kind in [.inc, .dec] || (p.tok.kind == .question && p.inside_ct_if_expr) { + // Postfix + // detect `f(x++)`, `a[x++]` + if p.peek_tok.kind in [.rpar, .rsbr] { + if !p.inside_ct_if_expr { + p.warn_with_pos('`$p.tok.kind` operator can only be used as a statement', + p.peek_tok.position()) + } + } + if p.tok.kind in [.inc, .dec] && p.prev_tok.line_nr != p.tok.line_nr { + p.error_with_pos('$p.tok must be on the same line as the previous token', + p.tok.position()) + } + if mut node is ast.IndexExpr { + node.recursive_mapset_is_setter(true) + } + node = ast.PostfixExpr{ + op: p.tok.kind + expr: node + pos: p.tok.position() + } + p.next() + // return node // TODO bring back, only allow ++/-- in exprs in translated code + } else { + return node + } + } + return node +} + +fn (mut p Parser) infix_expr(left ast.Expr) ast.Expr { + op := p.tok.kind + if op == .arrow { + p.or_is_handled = true + p.register_auto_import('sync') + } + precedence := p.tok.precedence() + mut pos := p.tok.position() + p.next() + mut right := ast.empty_expr() + prev_expecting_type := p.expecting_type + if op in [.key_is, .not_is] { + p.expecting_type = true + } + right = p.expr(precedence) + p.expecting_type = prev_expecting_type + if p.pref.is_vet && op in [.key_in, .not_in] && right is ast.ArrayInit + && (right as ast.ArrayInit).exprs.len == 1 { + p.vet_error('Use `var == value` instead of `var in [value]`', pos.line_nr, vet.FixKind.vfmt, + .default) + } + mut or_stmts := []ast.Stmt{} + mut or_kind := ast.OrKind.absent + mut or_pos := p.tok.position() + // allow `x := <-ch or {...}` to handle closed channel + if op == .arrow { + if p.tok.kind == .key_orelse { + p.next() + p.open_scope() + p.scope.register(ast.Var{ + name: 'err' + typ: ast.error_type + pos: p.tok.position() + is_used: true + is_stack_obj: true + }) + or_kind = .block + or_stmts = p.parse_block_no_scope(false) + or_pos = or_pos.extend(p.prev_tok.position()) + p.close_scope() + } + if p.tok.kind == .question { + p.next() + or_kind = .propagate + } + p.or_is_handled = false + } + pos.update_last_line(p.prev_tok.line_nr) + return ast.InfixExpr{ + left: left + right: right + op: op + pos: pos + is_stmt: p.is_stmt_ident + or_block: ast.OrExpr{ + stmts: or_stmts + kind: or_kind + pos: or_pos + } + } +} + +fn (mut p Parser) go_expr() ast.GoExpr { + p.next() + spos := p.tok.position() + expr := p.expr(0) + call_expr := if expr is ast.CallExpr { + expr + } else { + p.error_with_pos('expression in `go` must be a function call', expr.position()) + ast.CallExpr{ + scope: p.scope + } + } + pos := spos.extend(p.prev_tok.position()) + p.register_auto_import('sync.threads') + p.table.gostmts++ + return ast.GoExpr{ + call_expr: call_expr + pos: pos + } +} + +fn (p &Parser) fileis(s string) bool { + return p.file_name.contains(s) +} + +fn (mut p Parser) prefix_expr() ast.Expr { + mut pos := p.tok.position() + op := p.tok.kind + if op == .amp { + p.is_amp = true + } + if op == .arrow { + p.or_is_handled = true + p.register_auto_import('sync') + } + // if op == .mul && !p.inside_unsafe { + // p.warn('unsafe') + // } + p.next() + mut right := p.expr(int(token.Precedence.prefix)) + p.is_amp = false + if op == .amp { + if mut right is ast.CastExpr { + // Handle &Type(x), as well as &&Type(x) etc: + p.recast_as_pointer(mut right, pos) + return right + } + if mut right is ast.SelectorExpr { + // Handle &Type(x).name : + if mut right.expr is ast.CastExpr { + p.recast_as_pointer(mut right.expr, pos) + return right + } + } + if mut right is ast.IndexExpr { + // Handle &u64(x)[idx] : + if mut right.left is ast.CastExpr { + p.recast_as_pointer(mut right.left, pos) + return right + } + } + } + mut or_stmts := []ast.Stmt{} + mut or_kind := ast.OrKind.absent + mut or_pos := p.tok.position() + // allow `x := <-ch or {...}` to handle closed channel + if op == .arrow { + if p.tok.kind == .key_orelse { + p.next() + p.open_scope() + p.scope.register(ast.Var{ + name: 'err' + typ: ast.error_type + pos: p.tok.position() + is_used: true + is_stack_obj: true + }) + or_kind = .block + or_stmts = p.parse_block_no_scope(false) + or_pos = or_pos.extend(p.prev_tok.position()) + p.close_scope() + } + if p.tok.kind == .question { + p.next() + or_kind = .propagate + } + p.or_is_handled = false + } + pos.update_last_line(p.prev_tok.line_nr) + return ast.PrefixExpr{ + op: op + right: right + pos: pos + or_block: ast.OrExpr{ + stmts: or_stmts + kind: or_kind + pos: or_pos + } + } +} + +fn (mut p Parser) recast_as_pointer(mut cast_expr ast.CastExpr, pos token.Position) { + cast_expr.typ = cast_expr.typ.to_ptr() + cast_expr.typname = p.table.get_type_symbol(cast_expr.typ).name + cast_expr.pos = pos.extend(cast_expr.pos) +} diff --git a/v_windows/v/old/vlib/v/parser/fn.v b/v_windows/v/old/vlib/v/parser/fn.v new file mode 100644 index 0000000..b97fa4f --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/fn.v @@ -0,0 +1,959 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module parser + +import v.ast +import v.token +import v.util + +pub fn (mut p Parser) call_expr(language ast.Language, mod string) ast.CallExpr { + first_pos := p.tok.position() + mut fn_name := if language == .c { + 'C.$p.check_name()' + } else if language == .js { + 'JS.$p.check_js_name()' + } else if mod.len > 0 { + '${mod}.$p.check_name()' + } else { + p.check_name() + } + if language != .v { + p.check_for_impure_v(language, first_pos) + } + mut or_kind := ast.OrKind.absent + if fn_name == 'json.decode' { + p.expecting_type = true // Makes name_expr() parse the type `User` in `json.decode(User, txt)` + or_kind = .block + } + + old_expr_mod := p.expr_mod + defer { + p.expr_mod = old_expr_mod + } + p.expr_mod = '' + + mut concrete_types := []ast.Type{} + mut concrete_list_pos := p.tok.position() + if p.tok.kind == .lt { + // `foo(10)` + p.expr_mod = '' + concrete_types = p.parse_generic_type_list() + concrete_list_pos = concrete_list_pos.extend(p.prev_tok.position()) + // In case of `foo()` + // T is unwrapped and registered in the checker. + full_generic_fn_name := if fn_name.contains('.') { fn_name } else { p.prepend_mod(fn_name) } + has_generic := concrete_types.any(it.has_flag(.generic)) + if !has_generic { + // will be added in checker + p.table.register_fn_concrete_types(full_generic_fn_name, concrete_types) + } + } + p.check(.lpar) + args := p.call_args() + last_pos := p.tok.position() + p.check(.rpar) + // ! in mutable methods + if p.tok.kind == .not { + p.next() + } + mut pos := first_pos.extend(last_pos) + mut or_stmts := []ast.Stmt{} // TODO remove unnecessary allocations by just using .absent + mut or_pos := p.tok.position() + if p.tok.kind == .key_orelse { + // `foo() or {}`` + was_inside_or_expr := p.inside_or_expr + p.inside_or_expr = true + p.next() + p.open_scope() + p.scope.register(ast.Var{ + name: 'err' + typ: ast.error_type + pos: p.tok.position() + is_used: true + }) + or_kind = .block + or_stmts = p.parse_block_no_scope(false) + or_pos = or_pos.extend(p.prev_tok.position()) + p.close_scope() + p.inside_or_expr = was_inside_or_expr + } + if p.tok.kind == .question { + // `foo()?` + p.next() + if p.inside_defer { + p.error_with_pos('error propagation not allowed inside `defer` blocks', p.prev_tok.position()) + } + or_kind = .propagate + } + if fn_name in p.imported_symbols { + fn_name = p.imported_symbols[fn_name] + } + comments := p.eat_comments(same_line: true) + pos.update_last_line(p.prev_tok.line_nr) + return ast.CallExpr{ + name: fn_name + name_pos: first_pos + args: args + mod: p.mod + pos: pos + language: language + concrete_types: concrete_types + concrete_list_pos: concrete_list_pos + or_block: ast.OrExpr{ + stmts: or_stmts + kind: or_kind + pos: or_pos + } + scope: p.scope + comments: comments + } +} + +pub fn (mut p Parser) call_args() []ast.CallArg { + mut args := []ast.CallArg{} + start_pos := p.tok.position() + for p.tok.kind != .rpar { + if p.tok.kind == .eof { + p.error_with_pos('unexpected eof reached, while parsing call argument', start_pos) + return [] + } + is_shared := p.tok.kind == .key_shared + is_atomic := p.tok.kind == .key_atomic + is_mut := p.tok.kind == .key_mut || is_shared || is_atomic + if is_mut { + p.next() + } + mut comments := p.eat_comments() + arg_start_pos := p.tok.position() + mut array_decompose := false + if p.tok.kind == .ellipsis { + p.next() + array_decompose = true + } + mut expr := ast.empty_expr() + if p.tok.kind == .name && p.peek_tok.kind == .colon { + // `foo(key:val, key2:val2)` + expr = p.struct_init(true) // short_syntax:true + } else { + expr = p.expr(0) + } + if array_decompose { + expr = ast.ArrayDecompose{ + expr: expr + pos: p.tok.position() + } + } + if mut expr is ast.StructInit { + expr.pre_comments << comments + comments = []ast.Comment{} + } + pos := arg_start_pos.extend(p.prev_tok.position()) + comments << p.eat_comments() + args << ast.CallArg{ + is_mut: is_mut + share: ast.sharetype_from_flags(is_shared, is_atomic) + expr: expr + comments: comments + pos: pos + } + if p.tok.kind != .rpar { + p.check(.comma) + } + } + return args +} + +struct ReceiverParsingInfo { +mut: + name string + pos token.Position + typ ast.Type + type_pos token.Position + is_mut bool + language ast.Language +} + +fn (mut p Parser) fn_decl() ast.FnDecl { + p.top_level_statement_start() + start_pos := p.tok.position() + + mut is_manualfree := p.is_manualfree + mut is_deprecated := false + mut is_direct_arr := false + mut is_keep_alive := false + mut is_exported := false + mut is_unsafe := false + mut is_trusted := false + mut is_noreturn := false + mut is_c2v_variadic := false + for fna in p.attrs { + match fna.name { + 'noreturn' { is_noreturn = true } + 'manualfree' { is_manualfree = true } + 'deprecated' { is_deprecated = true } + 'direct_array_access' { is_direct_arr = true } + 'keep_args_alive' { is_keep_alive = true } + 'export' { is_exported = true } + 'unsafe' { is_unsafe = true } + 'trusted' { is_trusted = true } + 'c2v_variadic' { is_c2v_variadic = true } + else {} + } + } + conditional_ctdefine_idx := p.attrs.find_comptime_define() or { -1 } + is_pub := p.tok.kind == .key_pub + if is_pub { + p.next() + } + p.check(.key_fn) + p.open_scope() + // C. || JS. + mut language := ast.Language.v + if p.tok.kind == .name && p.tok.lit == 'C' { + is_unsafe = !is_trusted + language = ast.Language.c + } else if p.tok.kind == .name && p.tok.lit == 'JS' { + language = ast.Language.js + } + if is_keep_alive && language != .c { + p.error_with_pos('attribute [keep_args_alive] is only supported for C functions', + p.tok.position()) + } + if language != .v { + p.next() + p.check(.dot) + p.check_for_impure_v(language, p.tok.position()) + } + // Receiver? + mut rec := ReceiverParsingInfo{ + typ: ast.void_type + language: language + } + mut is_method := false + mut params := []ast.Param{} + if p.tok.kind == .lpar { + is_method = true + p.fn_receiver(mut params, mut rec) or { return ast.FnDecl{ + scope: 0 + } } + + // rec.language was initialized with language variable. + // So language is changed only if rec.language has been changed. + language = rec.language + } + mut name := '' + name_pos := p.tok.position() + if p.tok.kind == .name { + // TODO high order fn + name = if language == .js { p.check_js_name() } else { p.check_name() } + if language == .v && !p.pref.translated && util.contains_capital(name) && !p.builtin_mod { + p.error_with_pos('function names cannot contain uppercase letters, use snake_case instead', + name_pos) + return ast.FnDecl{ + scope: 0 + } + } + type_sym := p.table.get_type_symbol(rec.typ) + if is_method { + mut is_duplicate := type_sym.has_method(name) + // make sure this is a normal method and not an interface method + if type_sym.kind == .interface_ && is_duplicate { + if type_sym.info is ast.Interface { + // if the method is in info then its an interface method + is_duplicate = !type_sym.info.has_method(name) + } + } + if is_duplicate { + p.error_with_pos('duplicate method `$name`', name_pos) + return ast.FnDecl{ + scope: 0 + } + } + } + if !p.pref.is_fmt { + if !is_method && !p.builtin_mod && name in builtin_functions { + p.error_with_pos('cannot redefine builtin function `$name`', name_pos) + return ast.FnDecl{ + scope: 0 + } + } + if name in p.imported_symbols { + p.error_with_pos('cannot redefine imported function `$name`', name_pos) + return ast.FnDecl{ + scope: 0 + } + } + } + } else if p.tok.kind in [.plus, .minus, .mul, .div, .mod, .lt, .eq] && p.peek_tok.kind == .lpar { + name = p.tok.kind.str() // op_to_fn_name() + if rec.typ == ast.void_type { + p.error_with_pos('cannot use operator overloading with normal functions', + p.tok.position()) + } + p.next() + } else if p.tok.kind in [.ne, .gt, .ge, .le] && p.peek_tok.kind == .lpar { + p.error_with_pos('cannot overload `!=`, `>`, `<=` and `>=` as they are auto generated from `==` and`<`', + p.tok.position()) + } else { + p.error_with_pos('expecting method name', p.tok.position()) + return ast.FnDecl{ + scope: 0 + } + } + // + mut generic_names := p.parse_generic_names() + // generic names can be infer with receiver's generic names + if is_method && rec.typ.has_flag(.generic) && generic_names.len == 0 { + sym := p.table.get_type_symbol(rec.typ) + if sym.info is ast.Struct { + generic_names = sym.info.generic_types.map(p.table.get_type_symbol(it).name) + } + } + // Args + args2, are_args_type_only, mut is_variadic := p.fn_args() + if is_c2v_variadic { + is_variadic = true + } + params << args2 + if !are_args_type_only { + for param in params { + if p.scope.known_var(param.name) { + p.error_with_pos('redefinition of parameter `$param.name`', param.pos) + return ast.FnDecl{ + scope: 0 + } + } + is_stack_obj := !param.typ.has_flag(.shared_f) && (param.is_mut || param.typ.is_ptr()) + p.scope.register(ast.Var{ + name: param.name + typ: param.typ + is_mut: param.is_mut + is_auto_deref: param.is_mut || param.is_auto_rec + is_stack_obj: is_stack_obj + pos: param.pos + is_used: true + is_arg: true + }) + } + } + // Return type + mut return_type_pos := p.tok.position() + mut return_type := ast.void_type + // don't confuse token on the next line: fn decl, [attribute] + same_line := p.tok.line_nr == p.prev_tok.line_nr + if (p.tok.kind.is_start_of_type() && (same_line || p.tok.kind != .lsbr)) + || (same_line && p.tok.kind == .key_fn) { + return_type = p.parse_type() + return_type_pos = return_type_pos.extend(p.prev_tok.position()) + } + mut type_sym_method_idx := 0 + no_body := p.tok.kind != .lcbr + end_pos := p.prev_tok.position() + short_fn_name := name + is_main := short_fn_name == 'main' && p.mod == 'main' + mut is_test := (short_fn_name.starts_with('test_') || short_fn_name.starts_with('testsuite_')) + && (p.file_base.ends_with('_test.v') + || p.file_base.all_before_last('.v').all_before_last('.').ends_with('_test')) + + // Register + if is_method { + mut type_sym := p.table.get_type_symbol(rec.typ) + // Do not allow to modify / add methods to types from other modules + // arrays/maps dont belong to a module only their element types do + // we could also check if kind is .array, .array_fixed, .map instead of mod.len + mut is_non_local := type_sym.mod.len > 0 && type_sym.mod != p.mod && type_sym.language == .v + // check maps & arrays, must be defined in same module as the elem type + if !is_non_local && !(p.builtin_mod && p.pref.is_fmt) && type_sym.kind in [.array, .map] { + elem_type_sym := p.table.get_type_symbol(p.table.value_type(rec.typ)) + is_non_local = elem_type_sym.mod.len > 0 && elem_type_sym.mod != p.mod + && elem_type_sym.language == .v + } + if is_non_local { + p.error_with_pos('cannot define new methods on non-local type $type_sym.name', + rec.type_pos) + return ast.FnDecl{ + scope: 0 + } + } + type_sym_method_idx = type_sym.register_method(ast.Fn{ + name: name + params: params + return_type: return_type + is_variadic: is_variadic + generic_names: generic_names + is_pub: is_pub + is_deprecated: is_deprecated + is_unsafe: is_unsafe + is_main: is_main + is_test: is_test + is_keep_alive: is_keep_alive + // + attrs: p.attrs + is_conditional: conditional_ctdefine_idx != -1 + ctdefine_idx: conditional_ctdefine_idx + // + no_body: no_body + mod: p.mod + }) + } else { + if language == .c { + name = 'C.$name' + } else if language == .js { + name = 'JS.$name' + } else { + name = p.prepend_mod(name) + } + if !p.pref.translated && language == .v && name in p.table.fns { + p.table.redefined_fns << name + } + p.table.register_fn(ast.Fn{ + name: name + params: params + return_type: return_type + is_variadic: is_variadic + generic_names: generic_names + is_pub: is_pub + is_deprecated: is_deprecated + is_noreturn: is_noreturn + is_unsafe: is_unsafe + is_main: is_main + is_test: is_test + is_keep_alive: is_keep_alive + // + attrs: p.attrs + is_conditional: conditional_ctdefine_idx != -1 + ctdefine_idx: conditional_ctdefine_idx + // + no_body: no_body + mod: p.mod + language: language + }) + } + // Body + p.cur_fn_name = name + mut stmts := []ast.Stmt{} + body_start_pos := p.peek_tok.position() + if p.tok.kind == .lcbr { + p.inside_fn = true + p.inside_unsafe_fn = is_unsafe + stmts = p.parse_block_no_scope(true) + p.inside_unsafe_fn = false + p.inside_fn = false + } + if !no_body && are_args_type_only { + p.error_with_pos('functions with type only args can not have bodies', body_start_pos) + return ast.FnDecl{ + scope: 0 + } + } + // if no_body && !name.starts_with('C.') { + // p.error_with_pos('did you mean C.$name instead of $name', start_pos) + // } + fn_decl := ast.FnDecl{ + name: name + mod: p.mod + stmts: stmts + return_type: return_type + return_type_pos: return_type_pos + params: params + is_noreturn: is_noreturn + is_manualfree: is_manualfree + is_deprecated: is_deprecated + is_exported: is_exported + is_direct_arr: is_direct_arr + is_pub: is_pub + is_variadic: is_variadic + is_main: is_main + is_test: is_test + is_keep_alive: is_keep_alive + is_unsafe: is_unsafe + // + attrs: p.attrs + is_conditional: conditional_ctdefine_idx != -1 + ctdefine_idx: conditional_ctdefine_idx + // + receiver: ast.StructField{ + name: rec.name + typ: rec.typ + } + generic_names: generic_names + receiver_pos: rec.pos + is_method: is_method + method_type_pos: rec.type_pos + method_idx: type_sym_method_idx + rec_mut: rec.is_mut + language: language + no_body: no_body + pos: start_pos.extend_with_last_line(end_pos, p.prev_tok.line_nr) + body_pos: body_start_pos + file: p.file_name + is_builtin: p.builtin_mod || p.mod in util.builtin_module_parts + scope: p.scope + label_names: p.label_names + } + p.label_names = [] + p.close_scope() + return fn_decl +} + +fn (mut p Parser) fn_receiver(mut params []ast.Param, mut rec ReceiverParsingInfo) ? { + lpar_pos := p.tok.position() + p.next() // ( + is_shared := p.tok.kind == .key_shared + is_atomic := p.tok.kind == .key_atomic + rec.is_mut = p.tok.kind == .key_mut || is_shared || is_atomic + if rec.is_mut { + p.next() // `mut` + } + rec_start_pos := p.tok.position() + rec.name = p.check_name() + if !rec.is_mut { + rec.is_mut = p.tok.kind == .key_mut + if rec.is_mut { + ptoken2 := p.peek_token(2) // needed to prevent codegen bug, where .position() expects &Token + p.warn_with_pos('use `(mut f Foo)` instead of `(f mut Foo)`', lpar_pos.extend(ptoken2.position())) + } + } + if p.tok.kind == .key_shared { + ptoken2 := p.peek_token(2) // needed to prevent codegen bug, where .position() expects &Token + p.error_with_pos('use `(shared f Foo)` instead of `(f shared Foo)`', lpar_pos.extend(ptoken2.position())) + } + rec.pos = rec_start_pos.extend(p.tok.position()) + is_amp := p.tok.kind == .amp + if p.tok.kind == .name && p.tok.lit == 'JS' { + rec.language = ast.Language.js + } + // if rec.is_mut { + // p.check(.key_mut) + // } + // TODO: talk to alex, should mut be parsed with the type like this? + // or should it be a property of the arg, like this ptr/mut becomes indistinguishable + rec.type_pos = p.tok.position() + rec.typ = p.parse_type_with_mut(rec.is_mut) + if rec.typ.idx() == 0 { + // error is set in parse_type + return error('void receiver type') + } + rec.type_pos = rec.type_pos.extend(p.prev_tok.position()) + if is_amp && rec.is_mut { + p.error_with_pos('use `(mut f Foo)` or `(f &Foo)` instead of `(mut f &Foo)`', + lpar_pos.extend(p.tok.position())) + return error('invalid `mut f &Foo`') + } + if is_shared { + rec.typ = rec.typ.set_flag(.shared_f) + } + if is_atomic { + rec.typ = rec.typ.set_flag(.atomic_f) + } + // optimize method `automatic use fn (a &big_foo) instead of fn (a big_foo)` + type_sym := p.table.get_type_symbol(rec.typ) + mut is_auto_rec := false + if type_sym.kind == .struct_ { + info := type_sym.info as ast.Struct + if !rec.is_mut && !rec.typ.is_ptr() && info.fields.len > 8 { + rec.typ = rec.typ.to_ptr() + is_auto_rec = true + } + } + + params << ast.Param{ + pos: rec_start_pos + name: rec.name + is_mut: rec.is_mut + is_auto_rec: is_auto_rec + typ: rec.typ + type_pos: rec.type_pos + } + p.check(.rpar) + + return +} + +fn (mut p Parser) parse_generic_names() []string { + mut param_names := []string{} + if p.tok.kind != .lt { + return param_names + } + p.check(.lt) + mut first_done := false + mut count := 0 + for p.tok.kind !in [.gt, .eof] { + if first_done { + p.check(.comma) + } + name := p.tok.lit + if name.len > 0 && !name[0].is_capital() { + p.error('generic parameter needs to be uppercase') + } + if name.len > 1 { + p.error('generic parameter name needs to be exactly one char') + } + if !util.is_generic_type_name(p.tok.lit) { + p.error('`$p.tok.lit` is a reserved name and cannot be used for generics') + } + if name in param_names { + p.error('duplicated generic parameter `$name`') + } + if count > 8 { + p.error('cannot have more than 9 generic parameters') + } + p.check(.name) + param_names << name + if p.table.find_type_idx(name) == 0 { + p.table.register_type_symbol(ast.TypeSymbol{ + name: name + cname: util.no_dots(name) + mod: p.mod + kind: .any + is_public: true + }) + } + first_done = true + count++ + } + p.check(.gt) + return param_names +} + +// is_generic_name returns true if the current token is a generic name. +fn (p Parser) is_generic_name() bool { + return p.tok.kind == .name && util.is_generic_type_name(p.tok.lit) +} + +fn (mut p Parser) anon_fn() ast.AnonFn { + pos := p.tok.position() + p.check(.key_fn) + if p.pref.is_script && p.tok.kind == .name { + p.error_with_pos('function declarations in script mode should be before all script statements', + p.tok.position()) + return ast.AnonFn{} + } + old_inside_defer := p.inside_defer + p.inside_defer = false + p.open_scope() + if !p.pref.backend.is_js() { + p.scope.detached_from_parent = true + } + // TODO generics + args, _, is_variadic := p.fn_args() + for arg in args { + if arg.name.len == 0 { + p.error_with_pos('use `_` to name an unused parameter', arg.pos) + } + is_stack_obj := !arg.typ.has_flag(.shared_f) && (arg.is_mut || arg.typ.is_ptr()) + p.scope.register(ast.Var{ + name: arg.name + typ: arg.typ + is_mut: arg.is_mut + pos: arg.pos + is_used: true + is_arg: true + is_stack_obj: is_stack_obj + }) + } + mut same_line := p.tok.line_nr == p.prev_tok.line_nr + mut return_type := ast.void_type + mut return_type_pos := p.tok.position() + // lpar: multiple return types + if same_line { + if (p.tok.kind.is_start_of_type() && (same_line || p.tok.kind != .lsbr)) + || (same_line && p.tok.kind == .key_fn) { + return_type = p.parse_type() + return_type_pos = return_type_pos.extend(p.tok.position()) + } else if p.tok.kind != .lcbr { + p.error_with_pos('expected return type, not $p.tok for anonymous function', + p.tok.position()) + } + } + mut stmts := []ast.Stmt{} + no_body := p.tok.kind != .lcbr + same_line = p.tok.line_nr == p.prev_tok.line_nr + if no_body && same_line { + p.error_with_pos('unexpected $p.tok after anonymous function signature, expecting `{`', + p.tok.position()) + } + mut label_names := []string{} + mut func := ast.Fn{ + params: args + is_variadic: is_variadic + return_type: return_type + } + name := 'anon_fn_${p.unique_prefix}_${p.table.fn_type_signature(func)}_$p.tok.pos' + keep_fn_name := p.cur_fn_name + p.cur_fn_name = name + if p.tok.kind == .lcbr { + tmp := p.label_names + p.label_names = [] + stmts = p.parse_block_no_scope(false) + label_names = p.label_names + p.label_names = tmp + } + p.cur_fn_name = keep_fn_name + p.close_scope() + func.name = name + idx := p.table.find_or_register_fn_type(p.mod, func, true, false) + typ := ast.new_type(idx) + p.inside_defer = old_inside_defer + // name := p.table.get_type_name(typ) + return ast.AnonFn{ + decl: ast.FnDecl{ + name: name + mod: p.mod + stmts: stmts + return_type: return_type + return_type_pos: return_type_pos + params: args + is_variadic: is_variadic + is_method: false + is_anon: true + no_body: no_body + pos: pos.extend(p.prev_tok.position()) + file: p.file_name + scope: p.scope + label_names: label_names + } + typ: typ + } +} + +// part of fn declaration +fn (mut p Parser) fn_args() ([]ast.Param, bool, bool) { + p.check(.lpar) + mut args := []ast.Param{} + mut is_variadic := false + // `int, int, string` (no names, just types) + argname := if p.tok.kind == .name && p.tok.lit.len > 0 && p.tok.lit[0].is_capital() { + p.prepend_mod(p.tok.lit) + } else { + p.tok.lit + } + types_only := p.tok.kind in [.amp, .ellipsis, .key_fn, .lsbr] + || (p.peek_tok.kind == .comma && p.table.known_type(argname)) + || p.peek_tok.kind == .dot || p.peek_tok.kind == .rpar + || (p.tok.kind == .key_mut && (p.peek_token(2).kind == .comma + || p.peek_token(2).kind == .rpar)) + // TODO copy pasta, merge 2 branches + if types_only { + mut arg_no := 1 + for p.tok.kind != .rpar { + if p.tok.kind == .eof { + p.error_with_pos('expecting `)`', p.tok.position()) + return []ast.Param{}, false, false + } + is_shared := p.tok.kind == .key_shared + is_atomic := p.tok.kind == .key_atomic + is_mut := p.tok.kind == .key_mut || is_shared || is_atomic + if is_mut { + p.next() + } + if p.tok.kind == .ellipsis { + p.next() + is_variadic = true + } + pos := p.tok.position() + mut arg_type := p.parse_type() + if arg_type == 0 { + // error is added in parse_type + return []ast.Param{}, false, false + } + if is_mut { + if !arg_type.has_flag(.generic) { + if is_shared { + p.check_fn_shared_arguments(arg_type, pos) + } else if is_atomic { + p.check_fn_atomic_arguments(arg_type, pos) + } else { + p.check_fn_mutable_arguments(arg_type, pos) + } + } else if is_shared || is_atomic { + p.error_with_pos('generic object cannot be `atomic`or `shared`', pos) + return []ast.Param{}, false, false + } + // if arg_type.is_ptr() { + // p.error('cannot mut') + // } + // arg_type = arg_type.to_ptr() + arg_type = arg_type.set_nr_muls(1) + if is_shared { + arg_type = arg_type.set_flag(.shared_f) + } + if is_atomic { + arg_type = arg_type.set_flag(.atomic_f) + } + } + if is_variadic { + arg_type = ast.new_type(p.table.find_or_register_array(arg_type)).set_flag(.variadic) + } + if p.tok.kind == .eof { + p.error_with_pos('expecting `)`', p.prev_tok.position()) + return []ast.Param{}, false, false + } + if p.tok.kind == .comma { + if is_variadic { + p.error_with_pos('cannot use ...(variadic) with non-final parameter no $arg_no', + pos) + return []ast.Param{}, false, false + } + p.next() + } + args << ast.Param{ + pos: pos + name: '' + is_mut: is_mut + typ: arg_type + } + arg_no++ + if arg_no > 1024 { + p.error_with_pos('too many args', pos) + return []ast.Param{}, false, false + } + } + } else { + for p.tok.kind != .rpar { + if p.tok.kind == .eof { + p.error_with_pos('expecting `)`', p.tok.position()) + return []ast.Param{}, false, false + } + is_shared := p.tok.kind == .key_shared + is_atomic := p.tok.kind == .key_atomic + mut is_mut := p.tok.kind == .key_mut || is_shared || is_atomic + if is_mut { + p.next() + } + mut arg_pos := [p.tok.position()] + mut arg_names := [p.check_name()] + mut type_pos := [p.tok.position()] + // `a, b, c int` + for p.tok.kind == .comma { + if !p.pref.is_fmt { + p.warn( + '`fn f(x, y Type)` syntax has been deprecated and will soon be removed. ' + + 'Use `fn f(x Type, y Type)` instead. You can run `v fmt -w "$p.scanner.file_path"` to automatically fix your code.') + } + p.next() + arg_pos << p.tok.position() + arg_names << p.check_name() + type_pos << p.tok.position() + } + if p.tok.kind == .key_mut { + // TODO remove old syntax + if !p.pref.is_fmt { + p.warn_with_pos('use `mut f Foo` instead of `f mut Foo`', p.tok.position()) + } + is_mut = true + } + if p.tok.kind == .key_shared { + p.error_with_pos('use `shared f Foo` instead of `f shared Foo`', p.tok.position()) + } + if p.tok.kind == .ellipsis { + p.next() + is_variadic = true + } + pos := p.tok.position() + mut typ := p.parse_type() + if typ == 0 { + // error is added in parse_type + return []ast.Param{}, false, false + } + if is_mut { + if !typ.has_flag(.generic) { + if is_shared { + p.check_fn_shared_arguments(typ, pos) + } else if is_atomic { + p.check_fn_atomic_arguments(typ, pos) + } else { + p.check_fn_mutable_arguments(typ, pos) + } + } else if is_shared || is_atomic { + p.error_with_pos('generic object cannot be `atomic` or `shared`', + pos) + return []ast.Param{}, false, false + } + typ = typ.set_nr_muls(1) + if is_shared { + typ = typ.set_flag(.shared_f) + } + if is_atomic { + typ = typ.set_flag(.atomic_f) + } + } + if is_variadic { + typ = ast.new_type(p.table.find_or_register_array(typ)).derive(typ).set_flag(.variadic) + } + for i, arg_name in arg_names { + args << ast.Param{ + pos: arg_pos[i] + name: arg_name + is_mut: is_mut + typ: typ + type_pos: type_pos[i] + } + // if typ.typ.kind == .variadic && p.tok.kind == .comma { + if is_variadic && p.tok.kind == .comma { + p.error_with_pos('cannot use ...(variadic) with non-final parameter $arg_name', + arg_pos[i]) + return []ast.Param{}, false, false + } + } + if p.tok.kind == .eof { + p.error_with_pos('expecting `)`', p.prev_tok.position()) + return []ast.Param{}, false, false + } + if p.tok.kind != .rpar { + p.check(.comma) + } + } + } + p.check(.rpar) + return args, types_only, is_variadic +} + +fn (mut p Parser) check_fn_mutable_arguments(typ ast.Type, pos token.Position) { + sym := p.table.get_type_symbol(typ) + if sym.kind in [.array, .array_fixed, .interface_, .map, .placeholder, .struct_, + .generic_struct_inst, .sum_type] { + return + } + if typ.is_ptr() || typ.is_pointer() { + return + } + if sym.kind == .alias { + atyp := (sym.info as ast.Alias).parent_type + p.check_fn_mutable_arguments(atyp, pos) + return + } + p.error_with_pos( + 'mutable arguments are only allowed for arrays, interfaces, maps, pointers, structs or their aliases\n' + + 'return values instead: `fn foo(mut n $sym.name) {` => `fn foo(n $sym.name) $sym.name {`', + pos) +} + +fn (mut p Parser) check_fn_shared_arguments(typ ast.Type, pos token.Position) { + sym := p.table.get_type_symbol(typ) + if sym.kind !in [.array, .struct_, .map, .placeholder] && !typ.is_ptr() { + p.error_with_pos('shared arguments are only allowed for arrays, maps, and structs\n', + pos) + } +} + +fn (mut p Parser) check_fn_atomic_arguments(typ ast.Type, pos token.Position) { + sym := p.table.get_type_symbol(typ) + if sym.kind !in [.u32, .int, .u64] { + p.error_with_pos('atomic arguments are only allowed for 32/64 bit integers\n' + + 'use shared arguments instead: `fn foo(atomic n $sym.name) {` => `fn foo(shared n $sym.name) {`', + pos) + } +} + +fn have_fn_main(stmts []ast.Stmt) bool { + for stmt in stmts { + if stmt is ast.FnDecl { + if stmt.name == 'main.main' && stmt.mod == 'main' { + return true + } + } + } + return false +} diff --git a/v_windows/v/old/vlib/v/parser/for.v b/v_windows/v/old/vlib/v/parser/for.v new file mode 100644 index 0000000..36605c3 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/for.v @@ -0,0 +1,201 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module parser + +import v.ast + +fn (mut p Parser) for_stmt() ast.Stmt { + p.check(.key_for) + mut pos := p.tok.position() + p.open_scope() + p.inside_for = true + if p.tok.kind == .key_match { + return p.error('cannot use `match` in `for` loop') + } + // defer { p.close_scope() } + // Infinite loop + if p.tok.kind == .lcbr { + p.inside_for = false + stmts := p.parse_block_no_scope(false) + pos.update_last_line(p.prev_tok.line_nr) + for_stmt := ast.ForStmt{ + stmts: stmts + pos: pos + is_inf: true + scope: p.scope + } + p.close_scope() + return for_stmt + } else if p.peek_tok.kind in [.decl_assign, .assign, .semicolon] + || p.tok.kind == .semicolon || (p.peek_tok.kind == .comma + && p.peek_token(2).kind != .key_mut && p.peek_token(3).kind != .key_in) { + // `for i := 0; i < 10; i++ {` or `for a,b := 0,1; a < 10; a++ {` + if p.tok.kind == .key_mut { + return p.error('`mut` is not needed in `for ;;` loops: use `for i := 0; i < n; i ++ {`') + } + mut init := ast.empty_stmt() + mut cond := p.new_true_expr() + mut inc := ast.empty_stmt() + mut has_init := false + mut has_cond := false + mut has_inc := false + mut is_multi := p.peek_tok.kind == .comma && p.peek_token(2).kind != .key_mut + && p.peek_token(3).kind != .key_in + if p.peek_tok.kind in [.assign, .decl_assign] || is_multi { + init = p.assign_stmt() + has_init = true + } + // Allow `for ;; i++ {` + // Allow `for i = 0; i < ...` + p.check(.semicolon) + if p.tok.kind != .semicolon { + // Disallow `for i := 0; i++; i < ...` + if p.tok.kind == .name && p.peek_tok.kind in [.inc, .dec] { + return p.error('cannot use $p.tok.lit$p.peek_tok.kind as value') + } + cond = p.expr(0) + has_cond = true + } + p.check(.semicolon) + if !is_multi { + is_multi = p.peek_tok.kind == .comma + } + if p.tok.kind != .lcbr { + inc = p.stmt(false) + has_inc = true + } + p.inside_for = false + stmts := p.parse_block_no_scope(false) + pos.update_last_line(p.prev_tok.line_nr) + for_c_stmt := ast.ForCStmt{ + stmts: stmts + has_init: has_init + has_cond: has_cond + has_inc: has_inc + is_multi: is_multi + init: init + cond: cond + inc: inc + pos: pos + scope: p.scope + } + p.close_scope() + return for_c_stmt + } else if p.peek_tok.kind in [.key_in, .comma] + || (p.tok.kind == .key_mut && p.peek_token(2).kind in [.key_in, .comma]) { + // `for i in vals`, `for i in start .. end`, `for mut user in users`, `for i, mut user in users` + mut val_is_mut := p.tok.kind == .key_mut + mut_pos := p.tok.position() + if val_is_mut { + p.next() + } + key_var_pos := p.tok.position() + mut val_var_pos := p.tok.position() + mut key_var_name := '' + mut val_var_name := p.check_name() + if p.tok.kind == .comma { + if val_is_mut { + p.error_with_pos('index of array or key of map cannot be mutated', mut_pos) + } + p.next() + if p.tok.kind == .key_mut { + // `for i, mut user in users {` + p.next() + val_is_mut = true + } + key_var_name = val_var_name + val_var_pos = p.tok.position() + val_var_name = p.check_name() + if key_var_name == val_var_name && key_var_name != '_' { + return p.error_with_pos('key and value in a for loop cannot be the same', + val_var_pos) + } + if p.scope.known_var(key_var_name) { + return p.error('redefinition of key iteration variable `$key_var_name`') + } + if p.scope.known_var(val_var_name) { + return p.error('redefinition of value iteration variable `$val_var_name`') + } + p.scope.register(ast.Var{ + name: key_var_name + typ: ast.int_type + pos: key_var_pos + is_tmp: true + is_stack_obj: true + }) + } else if p.scope.known_var(val_var_name) { + return p.error('redefinition of value iteration variable `$val_var_name`') + } + p.check(.key_in) + if p.tok.kind == .name && p.tok.lit in [key_var_name, val_var_name] { + return p.error('in a `for x in array` loop, the key or value iteration variable `$p.tok.lit` can not be the same as the array variable') + } + // arr_expr + cond := p.expr(0) + // 0 .. 10 + // start := p.tok.lit.int() + // TODO use RangeExpr + mut high_expr := ast.empty_expr() + mut is_range := false + if p.tok.kind == .dotdot { + is_range = true + p.next() + high_expr = p.expr(0) + p.scope.register(ast.Var{ + name: val_var_name + typ: ast.int_type + pos: val_var_pos + is_tmp: true + is_stack_obj: true + }) + if key_var_name.len > 0 { + return p.error_with_pos('cannot declare index variable with range `for`', + key_var_pos) + } + } else { + // this type will be set in checker + p.scope.register(ast.Var{ + name: val_var_name + pos: val_var_pos + is_mut: val_is_mut + is_auto_deref: val_is_mut + is_tmp: true + is_stack_obj: true + }) + } + p.inside_for = false + stmts := p.parse_block_no_scope(false) + pos.update_last_line(p.prev_tok.line_nr) + // println('nr stmts=$stmts.len') + for_in_stmt := ast.ForInStmt{ + stmts: stmts + cond: cond + key_var: key_var_name + val_var: val_var_name + high: high_expr + is_range: is_range + pos: pos + val_is_mut: val_is_mut + scope: p.scope + } + p.close_scope() + return for_in_stmt + } + // `for cond {` + cond := p.expr(0) + p.inside_for = false + // extra scope for the body + p.open_scope() + stmts := p.parse_block_no_scope(false) + pos.update_last_line(p.prev_tok.line_nr) + for_stmt := ast.ForStmt{ + cond: cond + stmts: stmts + pos: pos + scope: p.scope + } + p.close_scope() + p.close_scope() + return for_stmt +} diff --git a/v_windows/v/old/vlib/v/parser/if_match.v b/v_windows/v/old/vlib/v/parser/if_match.v new file mode 100644 index 0000000..99df181 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/if_match.v @@ -0,0 +1,443 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module parser + +import v.ast +import v.token + +fn (mut p Parser) if_expr(is_comptime bool) ast.IfExpr { + was_inside_if_expr := p.inside_if_expr + was_inside_ct_if_expr := p.inside_ct_if_expr + defer { + p.inside_if_expr = was_inside_if_expr + p.inside_ct_if_expr = was_inside_ct_if_expr + } + p.inside_if_expr = true + is_expr := p.prev_tok.kind == .key_return + mut pos := p.tok.position() + if is_comptime { + p.inside_ct_if_expr = true + p.next() // `$` + pos = p.prev_tok.position().extend(p.tok.position()) + } + mut branches := []ast.IfBranch{} + mut has_else := false + mut comments := []ast.Comment{} + mut prev_guard := false + for p.tok.kind in [.key_if, .key_else] { + p.inside_if = true + start_pos := if is_comptime { + p.prev_tok.position().extend(p.tok.position()) + } else { + p.tok.position() + } + if p.tok.kind == .key_else { + comments << p.eat_comments() + p.check(.key_else) + comments << p.eat_comments() + if p.tok.kind == .key_match { + p.error('cannot use `match` with `if` statements') + return ast.IfExpr{} + } + if p.tok.kind == .lcbr { + // else { + has_else = true + p.inside_if = false + end_pos := p.prev_tok.position() + body_pos := p.tok.position() + p.open_scope() + // only declare `err` if previous branch was an `if` guard + if prev_guard { + p.scope.register(ast.Var{ + name: 'err' + typ: ast.error_type + pos: p.tok.position() + is_used: true + is_stack_obj: true + }) + } + branches << ast.IfBranch{ + stmts: p.parse_block_no_scope(false) + pos: start_pos.extend(end_pos) + body_pos: body_pos.extend(p.tok.position()) + comments: comments + scope: p.scope + } + p.close_scope() + comments = [] + break + } + if is_comptime { + p.check(.dollar) + } + } + // `if` or `else if` + p.check(.key_if) + if p.tok.kind == .key_match { + p.error('cannot use `match` with `if` statements') + return ast.IfExpr{} + } + comments << p.eat_comments() + mut cond := ast.empty_expr() + mut is_guard := false + // `if x := opt() {` + if !is_comptime && p.peek_tok.kind == .decl_assign { + p.open_scope() + is_guard = true + var_pos := p.tok.position() + var_name := p.check_name() + if p.scope.known_var(var_name) { + p.error_with_pos('redefinition of `$var_name`', var_pos) + } + comments << p.eat_comments() + p.check(.decl_assign) + comments << p.eat_comments() + expr := p.expr(0) + cond = ast.IfGuardExpr{ + var_name: var_name + expr: expr + } + p.scope.register(ast.Var{ + name: var_name + expr: cond + pos: var_pos + }) + prev_guard = true + } else { + prev_guard = false + p.comp_if_cond = true + cond = p.expr(0) + p.comp_if_cond = false + } + comments << p.eat_comments() + end_pos := p.prev_tok.position() + body_pos := p.tok.position() + p.inside_if = false + p.open_scope() + stmts := p.parse_block_no_scope(false) + branches << ast.IfBranch{ + cond: cond + stmts: stmts + pos: start_pos.extend(end_pos) + body_pos: body_pos.extend(p.prev_tok.position()) + comments: comments + scope: p.scope + } + p.close_scope() + if is_guard { + p.close_scope() + } + comments = p.eat_comments() + if is_comptime { + if p.tok.kind == .key_else { + p.error('use `\$else` instead of `else` in compile-time `if` branches') + return ast.IfExpr{} + } + if p.tok.kind != .rcbr && p.peek_tok.kind == .key_else { + p.check(.dollar) + } + } + if p.tok.kind != .key_else { + break + } + } + pos.update_last_line(p.prev_tok.line_nr) + if comments.len > 0 { + pos.last_line = comments.last().pos.last_line + } + return ast.IfExpr{ + is_comptime: is_comptime + branches: branches + post_comments: comments + pos: pos + has_else: has_else + is_expr: is_expr + } +} + +fn (mut p Parser) match_expr() ast.MatchExpr { + match_first_pos := p.tok.position() + p.inside_match = true + p.check(.key_match) + mut is_sum_type := false + cond := p.expr(0) + p.inside_match = false + no_lcbr := p.tok.kind != .lcbr + if !no_lcbr { + p.check(.lcbr) + } + comments := p.eat_comments() // comments before the first branch + mut branches := []ast.MatchBranch{} + for p.tok.kind != .eof { + branch_first_pos := p.tok.position() + mut exprs := []ast.Expr{} + mut ecmnts := [][]ast.Comment{} + p.open_scope() + // final else + mut is_else := false + if p.tok.kind == .key_else { + is_else = true + p.next() + } else if (p.tok.kind == .name && !(p.tok.lit == 'C' && p.peek_tok.kind == .dot) + && (((p.tok.lit in ast.builtin_type_names || p.tok.lit[0].is_capital()) + && p.peek_tok.kind != .lpar) || (p.peek_tok.kind == .dot && p.peek_token(2).lit.len > 0 + && p.peek_token(2).lit[0].is_capital()))) || p.tok.kind == .lsbr { + mut types := []ast.Type{} + for { + // Sum type match + parsed_type := p.parse_type() + ecmnts << p.eat_comments() + types << parsed_type + exprs << ast.TypeNode{ + typ: parsed_type + pos: p.prev_tok.position() + } + if p.tok.kind != .comma { + break + } + p.check(.comma) + } + is_sum_type = true + } else { + // Expression match + for { + p.inside_match_case = true + expr := p.expr(0) + ecmnts << p.eat_comments() + p.inside_match_case = false + if p.tok.kind == .dotdot { + p.error_with_pos('match only supports inclusive (`...`) ranges, not exclusive (`..`)', + p.tok.position()) + return ast.MatchExpr{} + } else if p.tok.kind == .ellipsis { + p.next() + expr2 := p.expr(0) + exprs << ast.RangeExpr{ + low: expr + high: expr2 + has_low: true + has_high: true + pos: p.tok.position() + } + } else { + exprs << expr + } + if p.tok.kind != .comma { + break + } + p.check(.comma) + } + } + branch_last_pos := p.prev_tok.position() + // p.warn('match block') + p.inside_match_body = true + stmts := p.parse_block_no_scope(false) + branch_scope := p.scope + p.close_scope() + p.inside_match_body = false + pos := branch_first_pos.extend_with_last_line(branch_last_pos, p.prev_tok.line_nr) + branch_pos := branch_first_pos.extend_with_last_line(p.tok.position(), p.tok.line_nr) + post_comments := p.eat_comments() + branches << ast.MatchBranch{ + exprs: exprs + ecmnts: ecmnts + stmts: stmts + pos: pos + branch_pos: branch_pos + is_else: is_else + post_comments: post_comments + scope: branch_scope + } + if is_else && branches.len == 1 { + p.warn_with_pos('`match` must have at least one non `else` branch', pos) + } + if p.tok.kind == .rcbr || (is_else && no_lcbr) { + break + } + } + match_last_pos := p.tok.position() + mut pos := token.Position{ + line_nr: match_first_pos.line_nr + pos: match_first_pos.pos + len: match_last_pos.pos - match_first_pos.pos + match_last_pos.len + col: match_first_pos.col + } + if p.tok.kind == .rcbr { + p.check(.rcbr) + } + // return ast.StructInit{} + pos.update_last_line(p.prev_tok.line_nr) + return ast.MatchExpr{ + branches: branches + cond: cond + is_sum_type: is_sum_type + pos: pos + comments: comments + } +} + +fn (mut p Parser) select_expr() ast.SelectExpr { + match_first_pos := p.tok.position() + p.check(.key_select) + no_lcbr := p.tok.kind != .lcbr + if !no_lcbr { + p.check(.lcbr) + } + mut branches := []ast.SelectBranch{} + mut has_else := false + mut has_timeout := false + for { + branch_first_pos := p.tok.position() + comment := p.check_comment() // comment before {} + p.open_scope() + // final else + mut is_else := false + mut is_timeout := false + mut stmt := ast.empty_stmt() + if p.tok.kind == .key_else { + if has_timeout { + p.error_with_pos('timeout `> t` and `else` are mutually exclusive `select` keys', + p.tok.position()) + return ast.SelectExpr{} + } + if has_else { + p.error_with_pos('at most one `else` branch allowed in `select` block', + p.tok.position()) + return ast.SelectExpr{} + } + is_else = true + has_else = true + p.next() + } else { + mut is_gt := false + if p.tok.kind == .gt { + is_gt = true + p.note_with_pos('`>` is deprecated and will soon be forbidden - just state the timeout in nanoseconds', + p.tok.position()) + p.next() + } + p.inside_match = true + p.inside_select = true + exprs, comments := p.expr_list() + if exprs.len != 1 { + p.error('only one expression allowed as `select` key') + return ast.SelectExpr{} + } + if p.tok.kind in [.assign, .decl_assign] { + stmt = p.partial_assign_stmt(exprs, comments) + } else { + stmt = ast.ExprStmt{ + expr: exprs[0] + pos: exprs[0].position() + comments: [comment] + is_expr: true + } + } + p.inside_match = false + p.inside_select = false + match mut stmt { + ast.ExprStmt { + mut check_timeout := false + if !stmt.is_expr { + p.error_with_pos('select: invalid expression', stmt.pos) + return ast.SelectExpr{} + } else { + match mut stmt.expr { + ast.InfixExpr { + if stmt.expr.op != .arrow { + check_timeout = true + } else if is_gt { + p.error_with_pos('send expression cannot be used as timeout', + stmt.pos) + } + } + else { + check_timeout = true + } + } + } + if check_timeout { + if has_else { + p.error_with_pos('`else` and timeout value are mutually exclusive `select` keys', + stmt.pos) + return ast.SelectExpr{} + } + if has_timeout { + p.error_with_pos('at most one timeout branch allowed in `select` block', + stmt.pos) + return ast.SelectExpr{} + } + is_timeout = true + has_timeout = true + } + } + ast.AssignStmt { + expr := stmt.right[0] + match expr { + ast.PrefixExpr { + if expr.op != .arrow { + p.error_with_pos('select key: `<-` operator expected', + expr.pos) + return ast.SelectExpr{} + } + } + else { + p.error_with_pos('select key: receive expression expected', + stmt.right[0].position()) + return ast.SelectExpr{} + } + } + } + else { + p.error_with_pos('select: transmission statement, timeout (in ns) or `else` expected', + stmt.pos) + return ast.SelectExpr{} + } + } + } + branch_last_pos := p.tok.position() + p.inside_match_body = true + stmts := p.parse_block_no_scope(false) + p.close_scope() + p.inside_match_body = false + mut pos := token.Position{ + line_nr: branch_first_pos.line_nr + pos: branch_first_pos.pos + len: branch_last_pos.pos - branch_first_pos.pos + branch_last_pos.len + col: branch_first_pos.col + } + post_comments := p.eat_comments() + pos.update_last_line(p.prev_tok.line_nr) + if post_comments.len > 0 { + pos.last_line = post_comments.last().pos.last_line + } + branches << ast.SelectBranch{ + stmt: stmt + stmts: stmts + pos: pos + comment: comment + is_else: is_else + is_timeout: is_timeout + post_comments: post_comments + } + if p.tok.kind == .rcbr || ((is_else || is_timeout) && no_lcbr) { + break + } + } + match_last_pos := p.tok.position() + pos := token.Position{ + line_nr: match_first_pos.line_nr + pos: match_first_pos.pos + len: match_last_pos.pos - match_first_pos.pos + match_last_pos.len + col: match_first_pos.col + } + if p.tok.kind == .rcbr { + p.check(.rcbr) + } + return ast.SelectExpr{ + branches: branches + pos: pos.extend_with_last_line(p.prev_tok.position(), p.prev_tok.line_nr) + has_exception: has_else || has_timeout + } +} diff --git a/v_windows/v/old/vlib/v/parser/lock.v b/v_windows/v/old/vlib/v/parser/lock.v new file mode 100644 index 0000000..9ccb639 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/lock.v @@ -0,0 +1,113 @@ +module parser + +import v.token +import v.ast + +// parse `x` or `x.y.z` - no index, no struct literals (`{` starts lock block) +fn (mut p Parser) lockable() ast.Expr { + mut names := []string{} + mut positions := []token.Position{} + mut pos := p.tok.position() + for { + if p.tok.kind != .name { + p.error_with_pos('unexpected `$p.tok.lit` (field/variable name expected)', + p.tok.position()) + } + names << p.tok.lit + positions << pos + p.next() + if p.tok.kind != .dot { + break + } + p.next() + pos.extend(p.tok.position()) + } + mut expr := ast.Expr(ast.Ident{ + language: ast.Language.v + pos: positions[0] + mod: p.mod + name: names[0] + is_mut: true + info: ast.IdentVar{} + scope: p.scope + }) + for i := 1; i < names.len; i++ { + expr = ast.SelectorExpr{ + expr: expr + field_name: names[i] + next_token: if i < names.len - 1 { token.Kind.dot } else { p.tok.kind } + is_mut: true + pos: positions[i] + scope: p.scope + } + } + return expr +} + +// like `expr_list()` but only lockables are allowed, `{` starts lock block (not struct literal) +fn (mut p Parser) lockable_list() ([]ast.Expr, []ast.Comment) { + mut exprs := []ast.Expr{} + mut comments := []ast.Comment{} + for { + expr := p.lockable() + if expr is ast.Comment { + comments << expr + } else { + exprs << expr + if p.tok.kind != .comma { + break + } + p.next() + } + } + return exprs, comments +} + +fn (mut p Parser) lock_expr() ast.LockExpr { + // TODO Handle aliasing sync + p.register_auto_import('sync') + p.open_scope() + mut pos := p.tok.position() + mut lockeds := []ast.Expr{} + mut comments := []ast.Comment{} + mut is_rlocked := []bool{} + for { + is_rlock := p.tok.kind == .key_rlock + if !is_rlock && p.tok.kind != .key_lock { + p.error_with_pos('unexpected `$p.tok`, expected `lock` or `rlock`', p.tok.position()) + } + p.next() + if p.tok.kind == .lcbr { + break + } + if p.tok.kind == .name { + exprs, comms := p.lockable_list() + for e in exprs { + if !e.is_lockable() { + p.error_with_pos('`$e` cannot be locked - only `x` or `x.y` are supported', + e.position()) + } + lockeds << e + is_rlocked << is_rlock + } + comments << comms + } + if p.tok.kind == .lcbr { + break + } + if p.tok.kind == .semicolon { + p.next() + } + } + stmts := p.parse_block_no_scope(false) + scope := p.scope + p.close_scope() + pos.update_last_line(p.prev_tok.line_nr) + return ast.LockExpr{ + lockeds: lockeds + stmts: stmts + is_rlock: is_rlocked + pos: pos + scope: scope + } +} diff --git a/v_windows/v/old/vlib/v/parser/module.v b/v_windows/v/old/vlib/v/parser/module.v new file mode 100644 index 0000000..50173b8 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/module.v @@ -0,0 +1,66 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module parser + +import v.ast + +// return true if file being parsed imports `mod` +pub fn (p &Parser) known_import(mod string) bool { + return mod in p.imports +} + +fn (p &Parser) prepend_mod(name string) string { + // println('prepend_mod() name=$name p.mod=$p.mod expr_mod=$p.expr_mod') + if p.expr_mod != '' { + return p.expr_mod + '.' + name + } + if p.builtin_mod { + return name + } + return '${p.mod}.$name' +} + +fn (p &Parser) is_used_import(alias string) bool { + return alias in p.used_imports +} + +fn (mut p Parser) register_used_import(alias string) { + if !p.is_used_import(alias) { + p.used_imports << alias + } +} + +fn (mut p Parser) register_auto_import(alias string) { + if alias !in p.imports { + p.imports[alias] = alias + p.table.imports << alias + node := ast.Import{ + pos: p.tok.position() + mod: alias + alias: alias + } + p.ast_imports << node + } + if alias !in p.auto_imports { + p.auto_imports << alias + } + p.register_used_import(alias) +} + +fn (mut p Parser) check_unused_imports() { + if p.pref.is_repl || p.pref.is_fmt { + // The REPL should be much more liberal, and should not warn about + // unused imports, because they probably will be in the next few lines... + // vfmt doesn't care about unused imports either + return + } + for import_m in p.ast_imports { + alias := import_m.alias + mod := import_m.mod + if !p.is_used_import(alias) { + mod_alias := if alias == mod { alias } else { '$alias ($mod)' } + p.warn_with_pos("module '$mod_alias' is imported but never used", import_m.mod_pos) + } + } +} diff --git a/v_windows/v/old/vlib/v/parser/parse_type.v b/v_windows/v/old/vlib/v/parser/parse_type.v new file mode 100644 index 0000000..718e78c --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/parse_type.v @@ -0,0 +1,577 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module parser + +import v.ast +import v.util +import v.token + +pub fn (mut p Parser) parse_array_type() ast.Type { + p.check(.lsbr) + // fixed array + if p.tok.kind in [.number, .name] { + mut fixed_size := 0 + size_expr := p.expr(0) + if p.pref.is_fmt { + fixed_size = 987654321 + } else { + match size_expr { + ast.IntegerLiteral { + fixed_size = size_expr.val.int() + } + ast.Ident { + mut show_non_const_error := false + if const_field := p.table.global_scope.find_const('${p.mod}.$size_expr.name') { + if const_field.expr is ast.IntegerLiteral { + fixed_size = const_field.expr.val.int() + } else { + show_non_const_error = true + } + } else { + if p.pref.is_fmt { + // for vfmt purposes, pretend the constant does exist + // it may have been defined in another .v file: + fixed_size = 1 + } else { + show_non_const_error = true + } + } + if show_non_const_error { + p.error_with_pos('non-constant array bound `$size_expr.name`', + size_expr.pos) + } + } + else { + p.error('expecting `int` for fixed size') + } + } + } + p.check(.rsbr) + elem_type := p.parse_type() + if elem_type.idx() == 0 { + // error is handled by parse_type + return 0 + } + if fixed_size <= 0 { + p.error_with_pos('fixed size cannot be zero or negative', size_expr.position()) + } + // sym := p.table.get_type_symbol(elem_type) + idx := p.table.find_or_register_array_fixed(elem_type, fixed_size, size_expr) + if elem_type.has_flag(.generic) { + return ast.new_type(idx).set_flag(.generic) + } + return ast.new_type(idx) + } + // array + p.check(.rsbr) + elem_type := p.parse_type() + if elem_type.idx() == 0 { + // error is set in parse_type + return 0 + } + mut nr_dims := 1 + // detect attr + not_attr := p.peek_tok.kind != .name && p.peek_token(2).kind !in [.semicolon, .rsbr] + for p.tok.kind == .lsbr && not_attr { + p.next() + p.check(.rsbr) + nr_dims++ + } + idx := p.table.find_or_register_array_with_dims(elem_type, nr_dims) + if elem_type.has_flag(.generic) { + return ast.new_type(idx).set_flag(.generic) + } + return ast.new_type(idx) +} + +pub fn (mut p Parser) parse_map_type() ast.Type { + p.next() + if p.tok.kind != .lsbr { + return ast.map_type + } + p.check(.lsbr) + key_type := p.parse_type() + key_sym := p.table.get_type_symbol(key_type) + is_alias := key_sym.kind == .alias + if key_type.idx() == 0 { + // error is reported in parse_type + return 0 + } + key_type_supported := key_type in [ast.string_type_idx, ast.voidptr_type_idx] + || key_sym.kind in [.enum_, .placeholder, .any] + || ((key_type.is_int() || key_type.is_float() || is_alias) && !key_type.is_ptr()) + if !key_type_supported { + if is_alias { + p.error('cannot use the alias type as the parent type is unsupported') + return 0 + } + s := p.table.type_to_str(key_type) + p.error_with_pos('maps only support string, integer, float, rune, enum or voidptr keys for now (not `$s`)', + p.tok.position()) + return 0 + } + p.check(.rsbr) + value_type := p.parse_type() + if value_type.idx() == 0 { + // error is reported in parse_type + return 0 + } + if value_type.idx() == ast.void_type_idx { + p.error_with_pos('map value type cannot be void', p.tok.position()) + return 0 + } + idx := p.table.find_or_register_map(key_type, value_type) + if key_type.has_flag(.generic) || value_type.has_flag(.generic) { + return ast.new_type(idx).set_flag(.generic) + } + return ast.new_type(idx) +} + +pub fn (mut p Parser) parse_chan_type() ast.Type { + if p.peek_tok.kind != .name && p.peek_tok.kind != .key_mut && p.peek_tok.kind != .amp + && p.peek_tok.kind != .lsbr { + p.next() + return ast.chan_type + } + p.register_auto_import('sync') + p.next() + is_mut := p.tok.kind == .key_mut + elem_type := p.parse_type() + idx := p.table.find_or_register_chan(elem_type, is_mut) + if elem_type.has_flag(.generic) { + return ast.new_type(idx).set_flag(.generic) + } + return ast.new_type(idx) +} + +pub fn (mut p Parser) parse_thread_type() ast.Type { + is_opt := p.peek_tok.kind == .question + if is_opt { + p.next() + } + if p.peek_tok.kind != .name && p.peek_tok.kind != .key_mut && p.peek_tok.kind != .amp + && p.peek_tok.kind != .lsbr { + p.next() + if is_opt { + mut ret_type := ast.void_type + ret_type = ret_type.set_flag(.optional) + idx := p.table.find_or_register_thread(ret_type) + return ast.new_type(idx) + } else { + return ast.thread_type + } + } + if !is_opt { + p.next() + } + ret_type := p.parse_type() + idx := p.table.find_or_register_thread(ret_type) + if ret_type.has_flag(.generic) { + return ast.new_type(idx).set_flag(.generic) + } + return ast.new_type(idx) +} + +pub fn (mut p Parser) parse_multi_return_type() ast.Type { + p.check(.lpar) + mut mr_types := []ast.Type{} + mut has_generic := false + for p.tok.kind != .eof { + mr_type := p.parse_type() + if mr_type.idx() == 0 { + break + } + if mr_type.has_flag(.generic) { + has_generic = true + } + mr_types << mr_type + if p.tok.kind == .comma { + p.next() + } else { + break + } + } + p.check(.rpar) + if mr_types.len == 1 { + // no multi return type needed + return mr_types[0] + } + idx := p.table.find_or_register_multi_return(mr_types) + if has_generic { + return ast.new_type(idx).set_flag(.generic) + } + return ast.new_type(idx) +} + +// given anon name based off signature when `name` is blank +pub fn (mut p Parser) parse_fn_type(name string) ast.Type { + // p.warn('parse fn') + p.check(.key_fn) + mut has_generic := false + line_nr := p.tok.line_nr + args, _, is_variadic := p.fn_args() + for arg in args { + if arg.typ.has_flag(.generic) { + has_generic = true + break + } + } + mut return_type := ast.void_type + mut return_type_pos := token.Position{} + if p.tok.line_nr == line_nr && p.tok.kind.is_start_of_type() && !p.is_attributes() { + return_type_pos = p.tok.position() + return_type = p.parse_type() + if return_type.has_flag(.generic) { + has_generic = true + } + return_type_pos = return_type_pos.extend(p.prev_tok.position()) + } + func := ast.Fn{ + name: name + params: args + is_variadic: is_variadic + return_type: return_type + return_type_pos: return_type_pos + } + // MapFooFn typedefs are manually added in cheaders.v + // because typedefs get generated after the map struct is generated + has_decl := p.builtin_mod && name.starts_with('Map') && name.ends_with('Fn') + idx := p.table.find_or_register_fn_type(p.mod, func, false, has_decl) + if has_generic { + return ast.new_type(idx).set_flag(.generic) + } + return ast.new_type(idx) +} + +pub fn (mut p Parser) parse_type_with_mut(is_mut bool) ast.Type { + typ := p.parse_type() + if is_mut { + return typ.set_nr_muls(1) + } + return typ +} + +// Parses any language indicators on a type. +pub fn (mut p Parser) parse_language() ast.Language { + language := if p.tok.lit == 'C' { + ast.Language.c + } else if p.tok.lit == 'JS' { + ast.Language.js + } else { + ast.Language.v + } + if language != .v { + p.next() + p.check(.dot) + } + return language +} + +pub fn (mut p Parser) parse_type() ast.Type { + // optional + mut is_optional := false + if p.tok.kind == .question { + line_nr := p.tok.line_nr + p.next() + is_optional = true + if p.tok.line_nr > line_nr { + mut typ := ast.void_type + if is_optional { + typ = typ.set_flag(.optional) + } + return typ + } + } + is_shared := p.tok.kind == .key_shared + is_atomic := p.tok.kind == .key_atomic + if is_shared { + p.register_auto_import('sync') + } + mut nr_muls := 0 + if p.tok.kind == .key_mut || is_shared || is_atomic { + nr_muls++ + p.next() + } + if p.tok.kind == .mul { + p.error('use `&Type` instead of `*Type` when declaring references') + return 0 + } + mut nr_amps := 0 + // &Type + for p.tok.kind == .amp { + nr_amps++ + nr_muls++ + p.next() + } + language := p.parse_language() + mut typ := ast.void_type + is_array := p.tok.kind == .lsbr + if p.tok.kind != .lcbr { + pos := p.tok.position() + typ = p.parse_any_type(language, nr_muls > 0, true) + if typ.idx() == 0 { + // error is set in parse_type + return 0 + } + if typ == ast.void_type { + p.error_with_pos('use `?` instead of `?void`', pos) + return 0 + } + } + if is_optional { + typ = typ.set_flag(.optional) + } + if is_shared { + typ = typ.set_flag(.shared_f) + } + if is_atomic { + typ = typ.set_flag(.atomic_f) + } + if nr_muls > 0 { + typ = typ.set_nr_muls(nr_muls) + if is_array && nr_amps > 0 { + p.error('V arrays are already references behind the scenes, +there is no need to use a reference to an array (e.g. use `[]string` instead of `&[]string`). +If you need to modify an array in a function, use a mutable argument instead: `fn foo(mut s []string) {}`.') + return 0 + } + } + return typ +} + +pub fn (mut p Parser) parse_any_type(language ast.Language, is_ptr bool, check_dot bool) ast.Type { + mut name := p.tok.lit + if language == .c { + name = 'C.$name' + } else if language == .js { + name = 'JS.$name' + } else if p.peek_tok.kind == .dot && check_dot { + // `module.Type` + // /if !(p.tok.lit in p.table.imports) { + mut mod := name + mut mod_pos := p.tok.position() + p.next() + p.check(.dot) + mut mod_last_part := mod + for p.peek_tok.kind == .dot { + mod_pos = mod_pos.extend(p.tok.position()) + mod_last_part = p.tok.lit + mod += '.$mod_last_part' + p.next() + p.check(.dot) + } + if !p.known_import(mod) && !p.pref.is_fmt { + mut msg := 'unknown module `$mod`' + if mod.len > mod_last_part.len && p.known_import(mod_last_part) { + msg += '; did you mean `$mod_last_part`?' + } + p.error_with_pos(msg, mod_pos) + return 0 + } + if mod in p.imports { + p.register_used_import(mod) + mod = p.imports[mod] + } + // prefix with full module + name = '${mod}.$p.tok.lit' + if p.tok.lit.len > 0 && !p.tok.lit[0].is_capital() { + p.error('imported types must start with a capital letter') + return 0 + } + } else if p.expr_mod != '' && !p.in_generic_params { // p.expr_mod is from the struct and not from the generic parameter + name = p.expr_mod + '.' + name + } else if name in p.imported_symbols { + name = p.imported_symbols[name] + } else if !p.builtin_mod && name.len > 1 && name !in p.table.type_idxs { + // `Foo` in module `mod` means `mod.Foo` + name = p.mod + '.' + name + } + // p.warn('get type $name') + match p.tok.kind { + .key_fn { + // func + return p.parse_fn_type('') + } + .lsbr { + // array + return p.parse_array_type() + } + .lpar { + // multiple return + if is_ptr { + p.error('parse_type: unexpected `&` before multiple returns') + return 0 + } + return p.parse_multi_return_type() + } + else { + // no p.next() + if name == 'map' { + return p.parse_map_type() + } + if name == 'chan' { + return p.parse_chan_type() + } + if name == 'thread' { + return p.parse_thread_type() + } + mut ret := ast.Type(0) + if name == '' { + // This means the developer is using some wrong syntax like `x: int` instead of `x int` + p.error('expecting type declaration') + } else { + match name { + 'voidptr' { + ret = ast.voidptr_type + } + 'byteptr' { + ret = ast.byteptr_type + } + 'charptr' { + ret = ast.charptr_type + } + 'i8' { + ret = ast.i8_type + } + 'i16' { + ret = ast.i16_type + } + 'int' { + ret = ast.int_type + } + 'i64' { + ret = ast.i64_type + } + 'byte' { + ret = ast.byte_type + } + 'u16' { + ret = ast.u16_type + } + 'u32' { + ret = ast.u32_type + } + 'u64' { + ret = ast.u64_type + } + 'f32' { + ret = ast.f32_type + } + 'f64' { + ret = ast.f64_type + } + 'string' { + ret = ast.string_type + } + 'char' { + ret = ast.char_type + } + 'bool' { + ret = ast.bool_type + } + 'float_literal' { + ret = ast.float_literal_type + } + 'int_literal' { + ret = ast.int_literal_type + } + else { + p.next() + if name.len == 1 && name[0].is_capital() { + return p.parse_generic_template_type(name) + } + if p.tok.kind == .lt { + return p.parse_generic_struct_inst_type(name) + } + return p.parse_enum_or_struct_type(name, language) + } + } + } + p.next() + return ret + } + } +} + +pub fn (mut p Parser) parse_enum_or_struct_type(name string, language ast.Language) ast.Type { + // struct / enum / placeholder + // struct / enum + mut idx := p.table.find_type_idx(name) + if idx > 0 { + return ast.new_type(idx) + } + // not found - add placeholder + idx = p.table.add_placeholder_type(name, language) + // println('NOT FOUND: $name - adding placeholder - $idx') + return ast.new_type(idx) +} + +pub fn (mut p Parser) parse_generic_template_type(name string) ast.Type { + mut idx := p.table.find_type_idx(name) + if idx > 0 { + return ast.new_type(idx).set_flag(.generic) + } + idx = p.table.register_type_symbol(ast.TypeSymbol{ + name: name + cname: util.no_dots(name) + mod: p.mod + kind: .any + is_public: true + }) + return ast.new_type(idx).set_flag(.generic) +} + +pub fn (mut p Parser) parse_generic_struct_inst_type(name string) ast.Type { + mut bs_name := name + mut bs_cname := name + p.next() + p.in_generic_params = true + bs_name += '<' + bs_cname += '_T_' + mut concrete_types := []ast.Type{} + mut is_instance := false + for p.tok.kind != .eof { + gt := p.parse_type() + if !gt.has_flag(.generic) { + is_instance = true + } + gts := p.table.get_type_symbol(gt) + bs_name += gts.name + bs_cname += gts.cname + concrete_types << gt + if p.tok.kind != .comma { + break + } + p.next() + bs_name += ',' + bs_cname += '_' + } + p.check(.gt) + p.in_generic_params = false + bs_name += '>' + // fmt operates on a per-file basis, so is_instance might be not set correctly. Thus it's ignored. + if (is_instance || p.pref.is_fmt) && concrete_types.len > 0 { + mut gt_idx := p.table.find_type_idx(bs_name) + if gt_idx > 0 { + return ast.new_type(gt_idx) + } + gt_idx = p.table.add_placeholder_type(bs_name, .v) + mut parent_idx := p.table.type_idxs[name] + if parent_idx == 0 { + parent_idx = p.table.add_placeholder_type(name, .v) + } + idx := p.table.register_type_symbol(ast.TypeSymbol{ + kind: .generic_struct_inst + name: bs_name + cname: util.no_dots(bs_cname) + mod: p.mod + info: ast.GenericStructInst{ + parent_idx: parent_idx + concrete_types: concrete_types + } + }) + return ast.new_type(idx) + } + return p.parse_enum_or_struct_type(name, .v).set_flag(.generic) +} diff --git a/v_windows/v/old/vlib/v/parser/parser.v b/v_windows/v/old/vlib/v/parser/parser.v new file mode 100644 index 0000000..e17295e --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/parser.v @@ -0,0 +1,3418 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module parser + +import v.scanner +import v.ast +import v.token +import v.pref +import v.util +import v.vet +import v.errors +import os +import hash.fnv1a + +const ( + builtin_functions = ['print', 'println', 'eprint', 'eprintln', 'isnil', 'panic', 'exit'] +) + +pub struct Parser { + pref &pref.Preferences +mut: + file_base string // "hello.v" + file_name string // "/home/user/hello.v" + file_name_dir string // "/home/user" + unique_prefix string // a hash of p.file_name, used for making anon fn generation unique + file_backend_mode ast.Language // .c for .c.v|.c.vv|.c.vsh files; .js for .js.v files, .amd64/.rv32/other arches for .amd64.v/.rv32.v/etc. files, .v otherwise. + scanner &scanner.Scanner + comments_mode scanner.CommentsMode = .skip_comments + // see comment in parse_file + tok token.Token + prev_tok token.Token + peek_tok token.Token + table &ast.Table + language ast.Language + inside_test_file bool // when inside _test.v or _test.vv file + inside_if bool + inside_if_expr bool + inside_ct_if_expr bool + inside_or_expr bool + inside_for bool + inside_fn bool // true even with implicit main + inside_unsafe_fn bool + inside_str_interp bool + inside_array_lit bool + or_is_handled bool // ignore `or` in this expression + builtin_mod bool // are we in the `builtin` module? + mod string // current module name + is_manualfree bool // true when `[manualfree] module abc`, makes *all* fns in the current .v file, opt out of autofree + attrs []ast.Attr // attributes before next decl stmt + expr_mod string // for constructing full type names in parse_type() + scope &ast.Scope + imports map[string]string // alias => mod_name + ast_imports []ast.Import // mod_names + used_imports []string // alias + auto_imports []string // imports, the user does not need to specify + imported_symbols map[string]string + is_amp bool // for generating the right code for `&Foo{}` + returns bool + inside_match bool // to separate `match A { }` from `Struct{}` + inside_select bool // to allow `ch <- Struct{} {` inside `select` + inside_match_case bool // to separate `match_expr { }` from `Struct{}` + inside_match_body bool // to fix eval not used TODO + inside_unsafe bool + is_stmt_ident bool // true while the beginning of a statement is an ident/selector + expecting_type bool // `is Type`, expecting type + errors []errors.Error + warnings []errors.Warning + notices []errors.Notice + vet_errors []vet.Error + cur_fn_name string + label_names []string + in_generic_params bool // indicates if parsing between `<` and `>` of a method/function + name_error bool // indicates if the token is not a name or the name is on another line + n_asm int // controls assembly labels + inside_asm_template bool + inside_asm bool + global_labels []string + inside_defer bool + comp_if_cond bool + defer_vars []ast.Ident +} + +// for tests +pub fn parse_stmt(text string, table &ast.Table, scope &ast.Scope) ast.Stmt { + mut p := Parser{ + scanner: scanner.new_scanner(text, .skip_comments, &pref.Preferences{}) + inside_test_file: true + table: table + pref: &pref.Preferences{} + scope: scope + } + p.init_parse_fns() + util.timing_start('PARSE stmt') + defer { + util.timing_measure_cumulative('PARSE stmt') + } + p.read_first_token() + return p.stmt(false) +} + +pub fn parse_comptime(text string, table &ast.Table, pref &pref.Preferences, scope &ast.Scope) &ast.File { + mut p := Parser{ + scanner: scanner.new_scanner(text, .skip_comments, pref) + table: table + pref: pref + scope: scope + errors: []errors.Error{} + warnings: []errors.Warning{} + } + return p.parse() +} + +pub fn parse_text(text string, path string, table &ast.Table, comments_mode scanner.CommentsMode, pref &pref.Preferences) &ast.File { + mut p := Parser{ + scanner: scanner.new_scanner(text, comments_mode, pref) + comments_mode: comments_mode + table: table + pref: pref + scope: &ast.Scope{ + start_pos: 0 + parent: table.global_scope + } + errors: []errors.Error{} + warnings: []errors.Warning{} + } + p.set_path(path) + return p.parse() +} + +[unsafe] +pub fn (mut p Parser) free() { + unsafe { + p.scanner.free() + } +} + +pub fn (mut p Parser) set_path(path string) { + p.file_name = path + p.file_base = os.base(path) + p.file_name_dir = os.dir(path) + hash := fnv1a.sum64_string(path) + p.unique_prefix = hash.hex_full() + if p.file_base.ends_with('_test.v') || p.file_base.ends_with('_test.vv') { + p.inside_test_file = true + } + before_dot_v := path.before('.v') // also works for .vv and .vsh + language := before_dot_v.all_after_last('.') + langauge_with_underscore := before_dot_v.all_after_last('_') + if language == before_dot_v && langauge_with_underscore == before_dot_v { + p.file_backend_mode = .v + return + } + actual_language := if language == before_dot_v { langauge_with_underscore } else { language } + match actual_language { + 'c' { + p.file_backend_mode = .c + } + 'js' { + p.file_backend_mode = .js + } + else { + arch := pref.arch_from_string(actual_language) or { pref.Arch._auto } + p.file_backend_mode = ast.pref_arch_to_table_language(arch) + if arch == ._auto { + p.file_backend_mode = .v + } + } + } +} + +pub fn parse_file(path string, table &ast.Table, comments_mode scanner.CommentsMode, pref &pref.Preferences) &ast.File { + // NB: when comments_mode == .toplevel_comments, + // the parser gives feedback to the scanner about toplevel statements, so that the scanner can skip + // all the tricky inner comments. This is needed because we do not have a good general solution + // for handling them, and should be removed when we do (the general solution is also needed for vfmt) + mut p := Parser{ + scanner: scanner.new_scanner_file(path, comments_mode, pref) + comments_mode: comments_mode + table: table + pref: pref + scope: &ast.Scope{ + start_pos: 0 + parent: table.global_scope + } + errors: []errors.Error{} + warnings: []errors.Warning{} + } + p.set_path(path) + return p.parse() +} + +pub fn parse_vet_file(path string, table_ &ast.Table, pref &pref.Preferences) (&ast.File, []vet.Error) { + global_scope := &ast.Scope{ + parent: 0 + } + mut p := Parser{ + scanner: scanner.new_scanner_file(path, .parse_comments, pref) + comments_mode: .parse_comments + table: table_ + pref: pref + scope: &ast.Scope{ + start_pos: 0 + parent: global_scope + } + errors: []errors.Error{} + warnings: []errors.Warning{} + } + p.set_path(path) + if p.scanner.text.contains_any_substr(['\n ', ' \n']) { + source_lines := os.read_lines(path) or { []string{} } + for lnumber, line in source_lines { + if line.starts_with(' ') { + p.vet_error('Looks like you are using spaces for indentation.', lnumber, + .vfmt, .space_indent) + } + if line.ends_with(' ') { + p.vet_error('Looks like you have trailing whitespace.', lnumber, .unknown, + .trailing_space) + } + } + } + p.vet_errors << p.scanner.vet_errors + file := p.parse() + return file, p.vet_errors +} + +pub fn (mut p Parser) parse() &ast.File { + util.timing_start('PARSE') + defer { + util.timing_measure_cumulative('PARSE') + } + // comments_mode: comments_mode + p.init_parse_fns() + p.read_first_token() + mut stmts := []ast.Stmt{} + for p.tok.kind == .comment { + stmts << p.comment_stmt() + } + // module + module_decl := p.module_decl() + if module_decl.is_skipped { + stmts.insert(0, ast.Stmt(module_decl)) + } else { + stmts << module_decl + } + // imports + for { + if p.tok.kind == .key_import { + stmts << p.import_stmt() + continue + } + if p.tok.kind == .comment { + stmts << p.comment_stmt() + continue + } + break + } + for { + if p.tok.kind == .eof { + p.check_unused_imports() + break + } + stmt := p.top_stmt() + // clear the attributes after each statement + if !(stmt is ast.ExprStmt && (stmt as ast.ExprStmt).expr is ast.Comment) { + p.attrs = [] + } + stmts << stmt + } + p.scope.end_pos = p.tok.pos + return &ast.File{ + path: p.file_name + path_base: p.file_base + is_test: p.inside_test_file + nr_lines: p.scanner.line_nr + nr_bytes: p.scanner.text.len + mod: module_decl + imports: p.ast_imports + imported_symbols: p.imported_symbols + auto_imports: p.auto_imports + stmts: stmts + scope: p.scope + global_scope: p.table.global_scope + errors: p.errors + warnings: p.warnings + global_labels: p.global_labels + } +} + +/* +struct Queue { +mut: + idx int + mu &sync.Mutex + mu2 &sync.Mutex + paths []string + table &ast.Table + parsed_ast_files []&ast.File + pref &pref.Preferences + global_scope &ast.Scope +} + +fn (mut q Queue) run() { + for { + q.mu.lock() + idx := q.idx + if idx >= q.paths.len { + q.mu.unlock() + return + } + q.idx++ + q.mu.unlock() + println('run(idx=$idx)') + path := q.paths[idx] + file := parse_file(path, q.table, .skip_comments, q.pref, q.global_scope) + q.mu2.lock() + q.parsed_ast_files << file + q.mu2.unlock() + println('run done(idx=$idx)') + } +} +*/ +pub fn parse_files(paths []string, table &ast.Table, pref &pref.Preferences) []&ast.File { + mut timers := util.new_timers(false) + $if time_parsing ? { + timers.should_print = true + } + $if macos { + /* + if pref.is_parallel && paths[0].contains('/array.v') { + println('\n\n\nparse_files() nr_files=$paths.len') + println(paths) + nr_cpus := runtime.nr_cpus() + mut q := &Queue{ + paths: paths + table: table + pref: pref + global_scope: global_scope + mu: sync.new_mutex() + mu2: sync.new_mutex() + } + for _ in 0 .. nr_cpus - 1 { + go q.run() + } + time.sleep(time.second) + println('all done') + return q.parsed_ast_files + } + */ + } + mut files := []&ast.File{} + for path in paths { + timers.start('parse_file $path') + files << parse_file(path, table, .skip_comments, pref) + timers.show('parse_file $path') + } + return files +} + +pub fn (mut p Parser) init_parse_fns() { + // p.prefix_parse_fns = make(100, 100, sizeof(PrefixParseFn)) + // p.prefix_parse_fns[token.Kind.name] = parse_name +} + +pub fn (mut p Parser) read_first_token() { + // need to call next() 2 times to get peek token and current token + p.next() + p.next() +} + +[inline] +pub fn (p &Parser) peek_token(n int) token.Token { + return p.scanner.peek_token(n - 2) +} + +pub fn (mut p Parser) open_scope() { + p.scope = &ast.Scope{ + parent: p.scope + start_pos: p.tok.pos + } +} + +pub fn (mut p Parser) close_scope() { + // p.scope.end_pos = p.tok.pos + // NOTE: since this is usually called after `p.parse_block()` + // ie. when `prev_tok` is rcbr `}` we most likely want `prev_tok` + // we could do the following, but probably not needed in 99% of cases: + // `end_pos = if p.prev_tok.kind == .rcbr { p.prev_tok.pos } else { p.tok.pos }` + p.scope.end_pos = p.prev_tok.pos + p.scope.parent.children << p.scope + p.scope = p.scope.parent +} + +pub fn (mut p Parser) parse_block() []ast.Stmt { + p.open_scope() + stmts := p.parse_block_no_scope(false) + p.close_scope() + return stmts +} + +pub fn (mut p Parser) parse_block_no_scope(is_top_level bool) []ast.Stmt { + p.check(.lcbr) + mut stmts := []ast.Stmt{} + if p.tok.kind != .rcbr { + mut count := 0 + for p.tok.kind !in [.eof, .rcbr] { + stmts << p.stmt(is_top_level) + count++ + if count % 100000 == 0 { + eprintln('parsed $count statements so far from fn $p.cur_fn_name ...') + } + if count > 1000000 { + p.error_with_pos('parsed over $count statements from fn $p.cur_fn_name, the parser is probably stuck', + p.tok.position()) + return [] + } + } + } + if is_top_level { + p.top_level_statement_end() + } + p.check(.rcbr) + return stmts +} + +fn (mut p Parser) next() { + p.prev_tok = p.tok + p.tok = p.peek_tok + p.peek_tok = p.scanner.scan() +} + +fn (mut p Parser) check(expected token.Kind) { + p.name_error = false + if _likely_(p.tok.kind == expected) { + p.next() + } else { + if expected == .name { + p.name_error = true + } + mut s := expected.str() + // quote keywords, punctuation, operators + if token.is_key(s) || (s.len > 0 && !s[0].is_letter()) { + s = '`$s`' + } + p.error('unexpected $p.tok, expecting $s') + } +} + +// JS functions can have multiple dots in their name: +// JS.foo.bar.and.a.lot.more.dots() +fn (mut p Parser) check_js_name() string { + mut name := '' + for p.peek_tok.kind == .dot { + name += '${p.tok.lit}.' + p.next() // .name + p.next() // .dot + } + // last .name + name += p.tok.lit + p.next() + return name +} + +fn (mut p Parser) check_name() string { + name := p.tok.lit + if p.peek_tok.kind == .dot && name in p.imports { + p.register_used_import(name) + } + p.check(.name) + return name +} + +pub fn (mut p Parser) top_stmt() ast.Stmt { + $if trace_parser ? { + tok_pos := p.tok.position() + eprintln('parsing file: ${p.file_name:-30} | tok.kind: ${p.tok.kind:-10} | tok.lit: ${p.tok.lit:-10} | tok_pos: ${tok_pos.str():-45} | top_stmt') + } + for { + match p.tok.kind { + .key_pub { + match p.peek_tok.kind { + .key_const { + return p.const_decl() + } + .key_fn { + return p.fn_decl() + } + .key_struct, .key_union { + return p.struct_decl() + } + .key_interface { + return p.interface_decl() + } + .key_enum { + return p.enum_decl() + } + .key_type { + return p.type_decl() + } + else { + return p.error('wrong pub keyword usage') + } + } + } + .lsbr { + // attrs are stored in `p.attrs` + p.attributes() + continue + } + .key_asm { + return p.asm_stmt(true) + } + .key_interface { + return p.interface_decl() + } + .key_import { + p.error_with_pos('`import x` can only be declared at the beginning of the file', + p.tok.position()) + return p.import_stmt() + } + .key_global { + return p.global_decl() + } + .key_const { + return p.const_decl() + } + .key_fn { + return p.fn_decl() + } + .key_struct { + return p.struct_decl() + } + .dollar { + if_expr := p.if_expr(true) + return ast.ExprStmt{ + expr: if_expr + pos: if_expr.pos + } + } + .hash { + return p.hash() + } + .key_type { + return p.type_decl() + } + .key_enum { + return p.enum_decl() + } + .key_union { + return p.struct_decl() + } + .comment { + return p.comment_stmt() + } + else { + p.inside_fn = true + if p.pref.is_script && !p.pref.is_test { + p.open_scope() + mut stmts := []ast.Stmt{} + for p.tok.kind != .eof { + stmts << p.stmt(false) + } + p.close_scope() + return ast.FnDecl{ + name: 'main.main' + mod: 'main' + is_main: true + stmts: stmts + file: p.file_name + return_type: ast.void_type + scope: p.scope + label_names: p.label_names + } + } else if p.pref.is_fmt { + return p.stmt(false) + } else { + return p.error('bad top level statement ' + p.tok.str()) + } + } + } + } + // TODO remove dummy return statement + // the compiler complains if it's not there + return ast.empty_stmt() +} + +// TODO [if vfmt] +pub fn (mut p Parser) check_comment() ast.Comment { + if p.tok.kind == .comment { + return p.comment() + } + return ast.Comment{} +} + +pub fn (mut p Parser) comment() ast.Comment { + mut pos := p.tok.position() + text := p.tok.lit + num_newlines := text.count('\n') + is_multi := num_newlines > 0 + is_inline := text.len + 4 == p.tok.len // 4: `/` `*` `*` `/` + pos.last_line = pos.line_nr + num_newlines + p.next() + // Filter out false positive space indent vet errors inside comments + if p.vet_errors.len > 0 && is_multi { + p.vet_errors = p.vet_errors.filter(it.typ != .space_indent + || it.pos.line_nr - 1 > pos.last_line || it.pos.line_nr - 1 <= pos.line_nr) + } + return ast.Comment{ + text: text + is_multi: is_multi + is_inline: is_inline + pos: pos + } +} + +pub fn (mut p Parser) comment_stmt() ast.ExprStmt { + comment := p.comment() + return ast.ExprStmt{ + expr: comment + pos: comment.pos + } +} + +struct EatCommentsConfig { + same_line bool // Only eat comments on the same line as the previous token + follow_up bool // Comments directly below the previous token as long as there is no empty line +} + +pub fn (mut p Parser) eat_comments(cfg EatCommentsConfig) []ast.Comment { + mut line := p.prev_tok.line_nr + mut comments := []ast.Comment{} + for { + if p.tok.kind != .comment || (cfg.same_line && p.tok.line_nr > line) + || (cfg.follow_up && (p.tok.line_nr > line + 1 || p.tok.lit.contains('\n'))) { + break + } + comments << p.comment() + if cfg.follow_up { + line = p.prev_tok.line_nr + } + } + return comments +} + +pub fn (mut p Parser) stmt(is_top_level bool) ast.Stmt { + $if trace_parser ? { + tok_pos := p.tok.position() + eprintln('parsing file: ${p.file_name:-30} | tok.kind: ${p.tok.kind:-10} | tok.lit: ${p.tok.lit:-10} | tok_pos: ${tok_pos.str():-45} | stmt($is_top_level)') + } + p.is_stmt_ident = p.tok.kind == .name + match p.tok.kind { + .lcbr { + mut pos := p.tok.position() + stmts := p.parse_block() + pos.last_line = p.prev_tok.line_nr + return ast.Block{ + stmts: stmts + pos: pos + } + } + .key_assert { + p.next() + mut pos := p.tok.position() + expr := p.expr(0) + pos.update_last_line(p.prev_tok.line_nr) + return ast.AssertStmt{ + expr: expr + pos: pos.extend(p.tok.position()) + is_used: p.inside_test_file || !p.pref.is_prod + } + } + .key_for { + return p.for_stmt() + } + .name { + if p.tok.lit == 'sql' && p.peek_tok.kind == .name { + return p.sql_stmt() + } + if p.peek_tok.kind == .colon { + // `label:` + spos := p.tok.position() + name := p.check_name() + if name in p.label_names { + p.error_with_pos('duplicate label `$name`', spos) + } + p.label_names << name + p.next() + if p.tok.kind == .key_for { + for_pos := p.tok.position() + mut stmt := p.stmt(is_top_level) + match mut stmt { + ast.ForStmt { + stmt.label = name + return stmt + } + ast.ForInStmt { + stmt.label = name + return stmt + } + ast.ForCStmt { + stmt.label = name + return stmt + } + else { + p.error_with_pos('unknown kind of For statement', for_pos) + } + } + } + return ast.GotoLabel{ + name: name + pos: spos.extend(p.tok.position()) + } + } else if p.peek_tok.kind == .name { + return p.error_with_pos('unexpected name `$p.peek_tok.lit`', p.peek_tok.position()) + } else if !p.inside_if_expr && !p.inside_match_body && !p.inside_or_expr + && p.peek_tok.kind in [.rcbr, .eof] && !p.mark_var_as_used(p.tok.lit) { + return p.error_with_pos('`$p.tok.lit` evaluated but not used', p.tok.position()) + } + return p.parse_multi_expr(is_top_level) + } + .comment { + return p.comment_stmt() + } + .key_return { + if p.inside_defer { + return p.error_with_pos('`return` not allowed inside `defer` block', p.tok.position()) + } else { + return p.return_stmt() + } + } + .dollar { + match p.peek_tok.kind { + .key_if { + mut pos := p.tok.position() + expr := p.if_expr(true) + pos.update_last_line(p.prev_tok.line_nr) + return ast.ExprStmt{ + expr: expr + pos: pos + } + } + .key_for { + return p.comp_for() + } + .name { + mut pos := p.tok.position() + expr := p.comp_call() + pos.update_last_line(p.prev_tok.line_nr) + return ast.ExprStmt{ + expr: expr + pos: pos + } + } + else { + return p.error_with_pos('unexpected \$', p.tok.position()) + } + } + } + .key_continue, .key_break { + tok := p.tok + line := p.tok.line_nr + p.next() + mut label := '' + if p.tok.line_nr == line && p.tok.kind == .name { + label = p.check_name() + } + return ast.BranchStmt{ + kind: tok.kind + label: label + pos: tok.position() + } + } + .key_unsafe { + return p.unsafe_stmt() + } + .hash { + return p.hash() + } + .key_defer { + if p.inside_defer { + return p.error_with_pos('`defer` blocks cannot be nested', p.tok.position()) + } else { + p.next() + spos := p.tok.position() + p.inside_defer = true + p.defer_vars = []ast.Ident{} + stmts := p.parse_block() + p.inside_defer = false + return ast.DeferStmt{ + stmts: stmts + defer_vars: p.defer_vars.clone() + pos: spos.extend_with_last_line(p.tok.position(), p.prev_tok.line_nr) + } + } + } + .key_go { + go_expr := p.go_expr() + return ast.ExprStmt{ + expr: go_expr + pos: go_expr.pos + } + } + .key_goto { + p.next() + spos := p.tok.position() + name := p.check_name() + return ast.GotoStmt{ + name: name + pos: spos + } + } + .key_const { + return p.error_with_pos('const can only be defined at the top level (outside of functions)', + p.tok.position()) + } + .key_asm { + return p.asm_stmt(false) + } + // literals, 'if', etc. in here + else { + return p.parse_multi_expr(is_top_level) + } + } +} + +fn (mut p Parser) asm_stmt(is_top_level bool) ast.AsmStmt { + p.inside_asm = true + p.inside_asm_template = true + defer { + p.inside_asm = false + p.inside_asm_template = false + } + p.n_asm = 0 + if is_top_level { + p.top_level_statement_start() + } + mut backup_scope := p.scope + + pos := p.tok.position() + + p.check(.key_asm) + mut arch := pref.arch_from_string(p.tok.lit) or { pref.Arch._auto } + mut is_volatile := false + mut is_goto := false + if p.tok.lit == 'volatile' && p.tok.kind == .name { + arch = pref.arch_from_string(p.peek_tok.lit) or { pref.Arch._auto } + is_volatile = true + p.next() + } else if p.tok.kind == .key_goto { + arch = pref.arch_from_string(p.peek_tok.lit) or { pref.Arch._auto } + is_goto = true + p.next() + } + if arch == ._auto && !p.pref.is_fmt { + p.error('unknown assembly architecture') + } + if p.tok.kind != .name { + p.error('must specify assembly architecture') + } else { + p.next() + } + + p.check_for_impure_v(ast.pref_arch_to_table_language(arch), p.prev_tok.position()) + + p.check(.lcbr) + p.scope = &ast.Scope{ + parent: 0 // you shouldn't be able to reference other variables in assembly blocks + detached_from_parent: true + start_pos: p.tok.pos + objects: ast.all_registers(mut p.table, arch) // + } + + mut local_labels := []string{} + // riscv: https://github.com/jameslzhu/riscv-card/blob/master/riscv-card.pdf + // x86: https://www.felixcloutier.com/x86/ + // arm: https://developer.arm.com/documentation/dui0068/b/arm-instruction-reference + mut templates := []ast.AsmTemplate{} + for p.tok.kind !in [.semicolon, .rcbr] { + template_pos := p.tok.position() + mut name := '' + if p.tok.kind == .name && arch == .amd64 && p.tok.lit in ['rex', 'vex', 'xop'] { + name += p.tok.lit + p.next() + for p.tok.kind == .dot { + p.next() + name += '.' + p.tok.lit + p.check(.name) + } + name += ' ' + } + is_directive := p.tok.kind == .dot + if is_directive { + p.next() + } + if p.tok.kind in [.key_in, .key_lock, .key_orelse] { // `in`, `lock`, `or` are v keywords that are also x86/arm/riscv instructions. + name += p.tok.kind.str() + p.next() + } else if p.tok.kind == .number { + name += p.tok.lit + p.next() + } else { + name += p.tok.lit + p.check(.name) + } + // dots are part of instructions for some riscv extensions + if arch in [.rv32, .rv64] { + for p.tok.kind == .dot { + name += '.' + p.next() + name += p.tok.lit + p.check(.name) + } + } + mut is_label := false + + mut args := []ast.AsmArg{} + if p.tok.line_nr == p.prev_tok.line_nr { + args_loop: for { + if p.prev_tok.position().line_nr < p.tok.position().line_nr { + break + } + match p.tok.kind { + .name { + args << p.reg_or_alias() + } + .number { + number_lit := p.parse_number_literal() + match number_lit { + ast.FloatLiteral { + args << ast.FloatLiteral{ + ...number_lit + } + } + ast.IntegerLiteral { + if is_directive { + args << ast.AsmDisp{ + val: number_lit.val + pos: number_lit.pos + } + } else { + args << ast.IntegerLiteral{ + ...number_lit + } + } + } + else { + verror('p.parse_number_literal() invalid output: `$number_lit`') + } + } + } + .chartoken { + args << ast.CharLiteral{ + val: p.tok.lit + pos: p.tok.position() + } + p.next() + } + .colon { + is_label = true + p.next() + local_labels << name + break + } + .lsbr { + args << p.asm_addressing() + } + .rcbr { + break + } + .semicolon { + break + } + else { + p.error('invalid token in assembly block') + } + } + if p.tok.kind == .comma { + p.next() + } else { + break + } + } + // if p.prev_tok.position().line_nr < p.tok.position().line_nr { + // break + // } + } + mut comments := []ast.Comment{} + for p.tok.kind == .comment { + comments << p.comment() + } + if is_directive && name in ['globl', 'global'] { + for arg in args { + p.global_labels << (arg as ast.AsmAlias).name + } + } + templates << ast.AsmTemplate{ + name: name + args: args + comments: comments + is_label: is_label + is_directive: is_directive + pos: template_pos.extend(p.tok.position()) + } + } + mut scope := p.scope + p.scope = backup_scope + p.inside_asm_template = false + mut output, mut input, mut clobbered, mut global_labels := []ast.AsmIO{}, []ast.AsmIO{}, []ast.AsmClobbered{}, []string{} + if !is_top_level { + if p.tok.kind == .semicolon { + output = p.asm_ios(true) + if p.tok.kind == .semicolon { + input = p.asm_ios(false) + } + if p.tok.kind == .semicolon { + // because p.reg_or_alias() requires the scope with registers to recognize registers. + backup_scope = p.scope + p.scope = scope + p.next() + for p.tok.kind == .name { + reg := ast.AsmRegister{ + name: p.tok.lit + typ: 0 + size: -1 + } + p.next() + + mut comments := []ast.Comment{} + for p.tok.kind == .comment { + comments << p.comment() + } + clobbered << ast.AsmClobbered{ + reg: reg + comments: comments + } + + if p.tok.kind in [.rcbr, .semicolon] { + break + } + } + + if is_goto && p.tok.kind == .semicolon { + p.next() + for p.tok.kind == .name { + global_labels << p.tok.lit + p.next() + } + } + } + } + } else if p.tok.kind == .semicolon { + p.error('extended assembly is not allowed as a top level statement') + } + p.scope = backup_scope + p.check(.rcbr) + if is_top_level { + p.top_level_statement_end() + } + scope.end_pos = p.prev_tok.pos + + return ast.AsmStmt{ + arch: arch + is_goto: is_goto + is_volatile: is_volatile + templates: templates + output: output + input: input + clobbered: clobbered + pos: pos.extend(p.prev_tok.position()) + is_basic: is_top_level || output.len + input.len + clobbered.len == 0 + scope: scope + global_labels: global_labels + local_labels: local_labels + } +} + +fn (mut p Parser) reg_or_alias() ast.AsmArg { + p.check(.name) + if p.prev_tok.lit in p.scope.objects { + x := p.scope.objects[p.prev_tok.lit] + if x is ast.AsmRegister { + return ast.AsmArg(x as ast.AsmRegister) + } else { + verror('non-register ast.ScopeObject found in scope') + return ast.AsmDisp{} // should not be reached + } + } else if p.prev_tok.len >= 2 && p.prev_tok.lit[0] in [`b`, `f`] + && p.prev_tok.lit[1..].bytes().all(it.is_digit()) { + return ast.AsmDisp{ + val: p.prev_tok.lit[1..] + p.prev_tok.lit[0].ascii_str() + } + } else { + return ast.AsmAlias{ + name: p.prev_tok.lit + pos: p.prev_tok.position() + } + } +} + +// fn (mut p Parser) asm_addressing() ast.AsmAddressing { +// pos := p.tok.position() +// p.check(.lsbr) +// unknown_addressing_mode := 'unknown addressing mode. supported ones are [displacement], [base], [base + displacement] [index ∗ scale + displacement], [base + index ∗ scale + displacement], [base + index + displacement] [rip + displacement]' +// mut mode := ast.AddressingMode.invalid +// if p.peek_tok.kind == .rsbr { +// if p.tok.kind == .name { +// mode = .base +// } else if p.tok.kind == .number { +// mode = .displacement +// } else { +// p.error(unknown_addressing_mode) +// } +// } else if p.peek_tok.kind == .mul { +// mode = .index_times_scale_plus_displacement +// } else if p.tok.lit == 'rip' { +// mode = .rip_plus_displacement +// } else if p.peek_tok3.kind == .mul { +// mode = .base_plus_index_times_scale_plus_displacement +// } else if p.peek_tok.kind == .plus && p.peek_tok3.kind == .rsbr { +// mode = .base_plus_displacement +// } else if p.peek_tok.kind == .plus && p.peek_tok3.kind == .plus { +// mode = .base_plus_index_plus_displacement +// } else { +// p.error(unknown_addressing_mode) +// } +// mut displacement, mut base, mut index, mut scale := u32(0), ast.AsmArg{}, ast.AsmArg{}, -1 + +// match mode { +// .base { +// base = p.reg_or_alias() +// } +// .displacement { +// displacement = p.tok.lit.u32() +// p.check(.number) +// } +// .base_plus_displacement { +// base = p.reg_or_alias() +// p.check(.plus) +// displacement = p.tok.lit.u32() +// p.check(.number) +// } +// .index_times_scale_plus_displacement { +// index = p.reg_or_alias() +// p.check(.mul) +// scale = p.tok.lit.int() +// p.check(.number) +// p.check(.plus) +// displacement = p.tok.lit.u32() +// p.check(.number) +// } +// .base_plus_index_times_scale_plus_displacement { +// base = p.reg_or_alias() +// p.check(.plus) +// index = p.reg_or_alias() +// p.check(.mul) +// scale = p.tok.lit.int() +// p.check(.number) +// p.check(.plus) +// displacement = p.tok.lit.u32() +// p.check(.number) +// } +// .rip_plus_displacement { +// base = p.reg_or_alias() +// p.check(.plus) +// displacement = p.tok.lit.u32() +// p.check(.number) +// } +// .base_plus_index_plus_displacement { +// base = p.reg_or_alias() +// p.check(.plus) +// index = p.reg_or_alias() +// p.check(.plus) +// displacement = p.tok.lit.u32() +// p.check(.number) +// } +// .invalid {} // there was already an error above +// } + +// p.check(.rsbr) +// return ast.AsmAddressing{ +// base: base +// displacement: displacement +// index: index +// scale: scale +// mode: mode +// pos: pos.extend(p.prev_tok.position()) +// } +// } + +fn (mut p Parser) asm_addressing() ast.AsmAddressing { + pos := p.tok.position() + p.check(.lsbr) + unknown_addressing_mode := 'unknown addressing mode. supported ones are [displacement], [base], [base + displacement], [index ∗ scale + displacement], [base + index ∗ scale + displacement], [base + index + displacement], [rip + displacement]' + // this mess used to look much cleaner before the removal of peek_tok2/3, see above code for cleaner version + if p.peek_tok.kind == .rsbr { // [displacement] or [base] + if p.tok.kind == .name { + base := p.reg_or_alias() + p.check(.rsbr) + return ast.AsmAddressing{ + mode: .base + base: base + pos: pos.extend(p.prev_tok.position()) + } + } else if p.tok.kind == .number { + displacement := if p.tok.kind == .name { + p.reg_or_alias() + } else { + x := ast.AsmArg(ast.AsmDisp{ + val: p.tok.lit + pos: p.tok.position() + }) + p.check(.number) + x + } + p.check(.rsbr) + return ast.AsmAddressing{ + mode: .displacement + displacement: displacement + pos: pos.extend(p.prev_tok.position()) + } + } else { + p.error(unknown_addressing_mode) + } + } + if p.peek_tok.kind == .plus && p.tok.kind == .name { // [base + displacement], [base + index ∗ scale + displacement], [base + index + displacement] or [rip + displacement] + if p.tok.lit == 'rip' { + rip := p.reg_or_alias() + p.next() + + displacement := if p.tok.kind == .name { + p.reg_or_alias() + } else { + x := ast.AsmArg(ast.AsmDisp{ + val: p.tok.lit + pos: p.tok.position() + }) + p.check(.number) + x + } + p.check(.rsbr) + return ast.AsmAddressing{ + mode: .rip_plus_displacement + base: rip + displacement: displacement + pos: pos.extend(p.prev_tok.position()) + } + } + base := p.reg_or_alias() + p.next() + if p.peek_tok.kind == .rsbr { + if p.tok.kind == .number { + displacement := if p.tok.kind == .name { + p.reg_or_alias() + } else { + x := ast.AsmArg(ast.AsmDisp{ + val: p.tok.lit + pos: p.tok.position() + }) + p.check(.number) + x + } + p.check(.rsbr) + return ast.AsmAddressing{ + mode: .base_plus_displacement + base: base + displacement: displacement + pos: pos.extend(p.prev_tok.position()) + } + } else { + p.error(unknown_addressing_mode) + } + } + index := p.reg_or_alias() + if p.tok.kind == .mul { + p.next() + scale := p.tok.lit.int() + p.check(.number) + p.check(.plus) + displacement := if p.tok.kind == .name { + p.reg_or_alias() + } else { + x := ast.AsmArg(ast.AsmDisp{ + val: p.tok.lit + pos: p.tok.position() + }) + p.check(.number) + x + } + p.check(.rsbr) + return ast.AsmAddressing{ + mode: .base_plus_index_times_scale_plus_displacement + base: base + index: index + scale: scale + displacement: displacement + pos: pos.extend(p.prev_tok.position()) + } + } else if p.tok.kind == .plus { + p.next() + displacement := if p.tok.kind == .name { + p.reg_or_alias() + } else { + x := ast.AsmArg(ast.AsmDisp{ + val: p.tok.lit + pos: p.tok.position() + }) + p.check(.number) + x + } + p.check(.rsbr) + return ast.AsmAddressing{ + mode: .base_plus_index_plus_displacement + base: base + index: index + displacement: displacement + pos: pos.extend(p.prev_tok.position()) + } + } + } + if p.peek_tok.kind == .mul { // [index ∗ scale + displacement] + index := p.reg_or_alias() + p.next() + scale := p.tok.lit.int() + p.check(.number) + p.check(.plus) + displacement := if p.tok.kind == .name { + p.reg_or_alias() + } else { + x := ast.AsmArg(ast.AsmDisp{ + val: p.tok.lit + pos: p.tok.position() + }) + p.check(.number) + x + } + p.check(.rsbr) + return ast.AsmAddressing{ + mode: .index_times_scale_plus_displacement + index: index + scale: scale + displacement: displacement + pos: pos.extend(p.prev_tok.position()) + } + } + p.error(unknown_addressing_mode) + return ast.AsmAddressing{} +} + +fn (mut p Parser) asm_ios(output bool) []ast.AsmIO { + mut res := []ast.AsmIO{} + p.check(.semicolon) + if p.tok.kind in [.rcbr, .semicolon] { + return [] + } + for { + pos := p.tok.position() + + mut constraint := '' + if p.tok.kind == .lpar { + constraint = if output { '+r' } else { 'r' } // default constraint, though vfmt fmts to `+r` and `r` + } else { + constraint += match p.tok.kind { + .assign { + '=' + } + .plus { + '+' + } + .mod { + '%' + } + .amp { + '&' + } + else { + '' + } + } + if constraint != '' { + p.next() + } + constraint += p.tok.lit + if p.tok.kind == .at { + p.next() + } else { + p.check(.name) + } + } + mut expr := p.expr(0) + if mut expr is ast.ParExpr { + expr = expr.expr + } else { + p.error('asm in/output must be enclosed in brackets') + } + mut alias := '' + if p.tok.kind == .key_as { + p.next() + alias = p.tok.lit + p.check(.name) + } else if mut expr is ast.Ident { + alias = expr.name + } + // for constraints like `a`, no alias is needed, it is reffered to as rcx + mut comments := []ast.Comment{} + for p.tok.kind == .comment { + comments << p.comment() + } + + res << ast.AsmIO{ + alias: alias + constraint: constraint + expr: expr + comments: comments + pos: pos.extend(p.prev_tok.position()) + } + p.n_asm++ + if p.tok.kind in [.semicolon, .rcbr] { + break + } + } + return res +} + +fn (mut p Parser) expr_list() ([]ast.Expr, []ast.Comment) { + mut exprs := []ast.Expr{} + mut comments := []ast.Comment{} + for { + expr := p.expr(0) + if expr is ast.Comment { + comments << expr + } else { + exprs << expr + if p.tok.kind != .comma { + break + } + p.next() + } + } + return exprs, comments +} + +fn (mut p Parser) is_attributes() bool { + if p.tok.kind != .lsbr { + return false + } + mut i := 0 + for { + tok := p.peek_token(i) + if tok.kind == .eof || tok.line_nr != p.tok.line_nr { + return false + } + if tok.kind == .rsbr { + break + } + i++ + } + peek_rsbr_tok := p.peek_token(i + 1) + if peek_rsbr_tok.line_nr == p.tok.line_nr && peek_rsbr_tok.kind != .rcbr { + return false + } + return true +} + +// when is_top_stmt is true attrs are added to p.attrs +fn (mut p Parser) attributes() { + p.check(.lsbr) + mut has_ctdefine := false + for p.tok.kind != .rsbr { + start_pos := p.tok.position() + attr := p.parse_attr() + if p.attrs.contains(attr.name) { + p.error_with_pos('duplicate attribute `$attr.name`', start_pos.extend(p.prev_tok.position())) + return + } + if attr.kind == .comptime_define { + if has_ctdefine { + p.error_with_pos('only one `[if flag]` may be applied at a time `$attr.name`', + start_pos.extend(p.prev_tok.position())) + return + } else { + has_ctdefine = true + } + } + p.attrs << attr + if p.tok.kind != .semicolon { + if p.tok.kind == .rsbr { + p.next() + break + } + p.error('unexpected $p.tok, expecting `;`') + return + } + p.next() + } + if p.attrs.len == 0 { + p.error_with_pos('attributes cannot be empty', p.prev_tok.position().extend(p.tok.position())) + return + } +} + +fn (mut p Parser) parse_attr() ast.Attr { + mut kind := ast.AttrKind.plain + apos := p.prev_tok.position() + if p.tok.kind == .key_unsafe { + p.next() + return ast.Attr{ + name: 'unsafe' + kind: kind + pos: apos.extend(p.tok.position()) + } + } + mut name := '' + mut has_arg := false + mut arg := '' + mut comptime_cond := ast.empty_expr() + mut comptime_cond_opt := false + if p.tok.kind == .key_if { + kind = .comptime_define + p.next() + p.comp_if_cond = true + p.inside_if_expr = true + p.inside_ct_if_expr = true + comptime_cond = p.expr(0) + p.comp_if_cond = false + p.inside_if_expr = false + p.inside_ct_if_expr = false + if comptime_cond is ast.PostfixExpr { + comptime_cond_opt = true + } + name = comptime_cond.str() + } else if p.tok.kind == .string { + name = p.tok.lit + kind = .string + p.next() + } else { + name = p.check_name() + if p.tok.kind == .colon { + has_arg = true + p.next() + // `name: arg` + if p.tok.kind == .name { + kind = .plain + arg = p.check_name() + } else if p.tok.kind == .number { + kind = .number + arg = p.tok.lit + p.next() + } else if p.tok.kind == .string { // `name: 'arg'` + kind = .string + arg = p.tok.lit + p.next() + } else { + p.error('unexpected $p.tok, an argument is expected after `:`') + } + } + } + return ast.Attr{ + name: name + has_arg: has_arg + arg: arg + kind: kind + ct_expr: comptime_cond + ct_opt: comptime_cond_opt + pos: apos.extend(p.tok.position()) + } +} + +pub fn (mut p Parser) check_for_impure_v(language ast.Language, pos token.Position) { + if language == .v { + // pure V code is always allowed everywhere + return + } + if !p.pref.warn_impure_v { + // the stricter mode is not ON yet => allow everything for now + return + } + if p.file_backend_mode != language { + upcase_language := language.str().to_upper() + if p.file_backend_mode == .v { + p.warn_with_pos('$upcase_language code will not be allowed in pure .v files, please move it to a .${language}.v file instead', + pos) + return + } else { + p.warn_with_pos('$upcase_language code is not allowed in .${p.file_backend_mode}.v files, please move it to a .${language}.v file', + pos) + return + } + } +} + +pub fn (mut p Parser) error(s string) ast.NodeError { + return p.error_with_pos(s, p.tok.position()) +} + +pub fn (mut p Parser) warn(s string) { + p.warn_with_pos(s, p.tok.position()) +} + +pub fn (mut p Parser) note(s string) { + p.note_with_pos(s, p.tok.position()) +} + +pub fn (mut p Parser) error_with_pos(s string, pos token.Position) ast.NodeError { + if p.pref.fatal_errors { + exit(1) + } + mut kind := 'error:' + if p.pref.output_mode == .stdout { + if p.pref.is_verbose { + print_backtrace() + kind = 'parser error:' + } + ferror := util.formatted_error(kind, s, p.file_name, pos) + eprintln(ferror) + exit(1) + } else { + p.errors << errors.Error{ + file_path: p.file_name + pos: pos + reporter: .parser + message: s + } + } + if p.pref.output_mode == .silent { + // Normally, parser errors mean that the parser exits immediately, so there can be only 1 parser error. + // In the silent mode however, the parser continues to run, even though it would have stopped. Some + // of the parser logic does not expect that, and may loop forever. + // The p.next() here is needed, so the parser is more robust, and *always* advances, even in the -silent mode. + p.next() + } + return ast.NodeError{ + idx: p.errors.len - 1 + pos: pos + } +} + +pub fn (mut p Parser) error_with_error(error errors.Error) { + if p.pref.fatal_errors { + exit(1) + } + mut kind := 'error:' + if p.pref.output_mode == .stdout { + if p.pref.is_verbose { + print_backtrace() + kind = 'parser error:' + } + ferror := util.formatted_error(kind, error.message, error.file_path, error.pos) + eprintln(ferror) + exit(1) + } else { + p.errors << error + } + if p.pref.output_mode == .silent { + // Normally, parser errors mean that the parser exits immediately, so there can be only 1 parser error. + // In the silent mode however, the parser continues to run, even though it would have stopped. Some + // of the parser logic does not expect that, and may loop forever. + // The p.next() here is needed, so the parser is more robust, and *always* advances, even in the -silent mode. + p.next() + } +} + +pub fn (mut p Parser) warn_with_pos(s string, pos token.Position) { + if p.pref.warns_are_errors { + p.error_with_pos(s, pos) + return + } + if p.pref.skip_warnings { + return + } + if p.pref.output_mode == .stdout { + ferror := util.formatted_error('warning:', s, p.file_name, pos) + eprintln(ferror) + } else { + p.warnings << errors.Warning{ + file_path: p.file_name + pos: pos + reporter: .parser + message: s + } + } +} + +pub fn (mut p Parser) note_with_pos(s string, pos token.Position) { + if p.pref.skip_warnings { + return + } + if p.pref.output_mode == .stdout { + ferror := util.formatted_error('notice:', s, p.file_name, pos) + eprintln(ferror) + } else { + p.notices << errors.Notice{ + file_path: p.file_name + pos: pos + reporter: .parser + message: s + } + } +} + +pub fn (mut p Parser) vet_error(msg string, line int, fix vet.FixKind, typ vet.ErrorType) { + pos := token.Position{ + line_nr: line + 1 + } + p.vet_errors << vet.Error{ + message: msg + file_path: p.scanner.file_path + pos: pos + kind: .error + fix: fix + typ: typ + } +} + +fn (mut p Parser) parse_multi_expr(is_top_level bool) ast.Stmt { + // in here might be 1) multi-expr 2) multi-assign + // 1, a, c ... } // multi-expression + // a, mut b ... :=/= // multi-assign + // collect things upto hard boundaries + tok := p.tok + mut pos := tok.position() + + mut defer_vars := p.defer_vars + p.defer_vars = []ast.Ident{} + + left, left_comments := p.expr_list() + + if !(p.inside_defer && p.tok.kind == .decl_assign) { + defer_vars << p.defer_vars + } + + p.defer_vars = defer_vars + + left0 := left[0] + if tok.kind == .key_mut && p.tok.kind != .decl_assign { + return p.error('expecting `:=` (e.g. `mut x :=`)') + } + // TODO remove translated + if p.tok.kind in [.assign, .decl_assign] || p.tok.kind.is_assign() { + return p.partial_assign_stmt(left, left_comments) + } else if !p.pref.translated && !p.pref.is_fmt + && tok.kind !in [.key_if, .key_match, .key_lock, .key_rlock, .key_select] { + for node in left { + if node !is ast.CallExpr && (is_top_level || p.tok.kind != .rcbr) + && node !is ast.PostfixExpr && !(node is ast.InfixExpr + && (node as ast.InfixExpr).op in [.left_shift, .arrow]) && node !is ast.ComptimeCall + && node !is ast.SelectorExpr && node !is ast.DumpExpr { + return p.error_with_pos('expression evaluated but not used', node.position()) + } + } + } + pos.update_last_line(p.prev_tok.line_nr) + if left.len == 1 { + return ast.ExprStmt{ + expr: left0 + pos: left0.position() + comments: left_comments + is_expr: p.inside_for + } + } + return ast.ExprStmt{ + expr: ast.ConcatExpr{ + vals: left + pos: tok.position() + } + pos: pos + comments: left_comments + } +} + +pub fn (mut p Parser) parse_ident(language ast.Language) ast.Ident { + // p.warn('name ') + is_shared := p.tok.kind == .key_shared + is_atomic := p.tok.kind == .key_atomic + if is_shared { + p.register_auto_import('sync') + } + mut_pos := p.tok.position() + is_mut := p.tok.kind == .key_mut || is_shared || is_atomic + if is_mut { + p.next() + } + is_static := p.tok.kind == .key_static + if is_static { + p.next() + } + if p.tok.kind == .name { + pos := p.tok.position() + mut name := p.check_name() + if name == '_' { + return ast.Ident{ + tok_kind: p.tok.kind + name: '_' + comptime: p.comp_if_cond + kind: .blank_ident + pos: pos + info: ast.IdentVar{ + is_mut: false + is_static: false + } + scope: p.scope + } + } + if p.inside_match_body && name == 'it' { + // p.warn('it') + } + if p.expr_mod.len > 0 { + name = '${p.expr_mod}.$name' + } + return ast.Ident{ + tok_kind: p.tok.kind + kind: .unresolved + name: name + comptime: p.comp_if_cond + language: language + mod: p.mod + pos: pos + is_mut: is_mut + mut_pos: mut_pos + info: ast.IdentVar{ + is_mut: is_mut + is_static: is_static + share: ast.sharetype_from_flags(is_shared, is_atomic) + } + scope: p.scope + } + } + p.error('unexpected token `$p.tok.lit`') + return ast.Ident{ + scope: p.scope + } +} + +fn (p &Parser) is_typename(t token.Token) bool { + return t.kind == .name && (t.lit[0].is_capital() || p.table.known_type(t.lit)) +} + +// heuristics to detect `func()` from `var < expr` +// 1. `f<[]` is generic(e.g. `f<[]int>`) because `var < []` is invalid +// 2. `f) +// 3. `f` and `f v2` and `v1 < foo < v2` are invalid syntax +// 4. `f` is same as case 3 +// 6. `f 0 && p.tok.lit[0].is_capital() + if lit0_is_capital || p.peek_tok.kind != .lt { + return false + } + mut tok2 := p.peek_token(2) + mut tok3 := p.peek_token(3) + mut tok4 := p.peek_token(4) + mut tok5 := p.peek_token(5) + mut kind2, mut kind3, mut kind4, mut kind5 := tok2.kind, tok3.kind, tok4.kind, tok5.kind + if kind2 == .amp { // if there is a & in front, shift everything left + tok2 = tok3 + kind2 = kind3 + tok3 = tok4 + kind3 = kind4 + tok4 = tok5 + kind4 = kind5 + tok5 = p.peek_token(6) + kind5 = tok5.kind + } + + if kind2 == .lsbr { + // case 1 + return tok3.kind == .rsbr + } + + if kind2 == .name { + if tok2.lit == 'map' && kind3 == .lsbr { + // case 2 + return true + } + return match kind3 { + .gt, .lt { true } // case 3 + .comma { p.is_typename(tok2) } // case 4 + // case 5 and 6 + .dot { kind4 == .name && (kind5 == .gt || (kind5 == .comma && p.is_typename(tok4))) } + else { false } + } + } + return false +} + +const valid_tokens_inside_types = [token.Kind.lsbr, .rsbr, .name, .dot, .comma, .key_fn, .lt] + +fn (mut p Parser) is_generic_cast() bool { + if !p.tok.can_start_type(ast.builtin_type_names) { + return false + } + mut i := 0 + mut level := 0 + mut lt_count := 0 + for { + i++ + tok := p.peek_token(i) + + if tok.kind == .lt { + lt_count++ + level++ + } else if tok.kind == .gt { + level-- + } + if lt_count > 0 && level == 0 { + break + } + + if i > 20 || tok.kind !in parser.valid_tokens_inside_types { + return false + } + } + next_tok := p.peek_token(i + 1) + // `next_tok` is the token following the closing `>` of the generic type: MyType{ + // ^ + // if `next_tok` is a left paren, then the full expression looks something like + // `Foo(` or `Foo(`, which are valid type casts - return true + if next_tok.kind == .lpar { + return true + } + // any other token is not a valid generic cast, however + return false +} + +pub fn (mut p Parser) name_expr() ast.Expr { + prev_tok_kind := p.prev_tok.kind + mut node := ast.empty_expr() + if p.expecting_type { + p.expecting_type = false + // get type position before moving to next + type_pos := p.tok.position() + typ := p.parse_type() + return ast.TypeNode{ + typ: typ + pos: type_pos + } + } + mut language := ast.Language.v + if p.tok.lit == 'C' { + language = ast.Language.c + p.check_for_impure_v(language, p.tok.position()) + } else if p.tok.lit == 'JS' { + language = ast.Language.js + p.check_for_impure_v(language, p.tok.position()) + } + mut mod := '' + // p.warn('resetting') + p.expr_mod = '' + // `map[string]int` initialization + if p.tok.lit == 'map' && p.peek_tok.kind == .lsbr { + map_type := p.parse_map_type() + if p.tok.kind == .lcbr { + p.next() + if p.tok.kind == .rcbr { + p.next() + } else { + p.error('`}` expected; explicit `map` initialization does not support parameters') + } + } + return ast.MapInit{ + typ: map_type + pos: p.prev_tok.position() + } + } + // `chan typ{...}` + if p.tok.lit == 'chan' { + first_pos := p.tok.position() + mut last_pos := first_pos + chan_type := p.parse_chan_type() + mut has_cap := false + mut cap_expr := ast.empty_expr() + p.check(.lcbr) + if p.tok.kind == .rcbr { + last_pos = p.tok.position() + p.next() + } else { + key := p.check_name() + p.check(.colon) + match key { + 'cap' { + has_cap = true + cap_expr = p.expr(0) + } + 'len', 'init' { + return p.error('`$key` cannot be initialized for `chan`. Did you mean `cap`?') + } + else { + return p.error('wrong field `$key`, expecting `cap`') + } + } + last_pos = p.tok.position() + p.check(.rcbr) + } + return ast.ChanInit{ + pos: first_pos.extend(last_pos) + has_cap: has_cap + cap_expr: cap_expr + typ: chan_type + } + } + // Raw string (`s := r'hello \n ') + if p.peek_tok.kind == .string && !p.inside_str_interp && p.peek_token(2).kind != .colon { + if p.tok.lit in ['r', 'c', 'js'] && p.tok.kind == .name { + return p.string_expr() + } else { + // don't allow any other string prefix except `r`, `js` and `c` + return p.error('only `c`, `r`, `js` are recognized string prefixes, but you tried to use `$p.tok.lit`') + } + } + // don't allow r`byte` and c`byte` + if p.tok.lit in ['r', 'c'] && p.peek_tok.kind == .chartoken { + opt := if p.tok.lit == 'r' { '`r` (raw string)' } else { '`c` (c string)' } + return p.error('cannot use $opt with `byte` and `rune`') + } + // Make sure that the var is not marked as used in assignments: `x = 1`, `x += 2` etc + // but only when it's actually used (e.g. `println(x)`) + known_var := if p.peek_tok.kind.is_assign() { + p.scope.known_var(p.tok.lit) + } else { + p.mark_var_as_used(p.tok.lit) + } + // Handle modules + mut is_mod_cast := false + if p.peek_tok.kind == .dot && !known_var && (language != .v || p.known_import(p.tok.lit) + || p.mod.all_after_last('.') == p.tok.lit) { + // p.tok.lit has been recognized as a module + if language == .c { + mod = 'C' + } else if language == .js { + mod = 'JS' + } else { + if p.tok.lit in p.imports { + // mark the imported module as used + p.register_used_import(p.tok.lit) + if p.peek_tok.kind == .dot && p.peek_token(2).kind != .eof + && p.peek_token(2).lit.len > 0 && p.peek_token(2).lit[0].is_capital() { + is_mod_cast = true + } else if p.peek_tok.kind == .dot && p.peek_token(2).kind != .eof + && p.peek_token(2).lit.len == 0 { + // incomplete module selector must be handled by dot_expr instead + ident := p.parse_ident(language) + node = ident + if p.inside_defer { + if !p.defer_vars.any(it.name == ident.name && it.mod == ident.mod) + && ident.name != 'err' { + p.defer_vars << ident + } + } + return node + } + } + // prepend the full import + mod = p.imports[p.tok.lit] + } + p.next() + p.check(.dot) + p.expr_mod = mod + } + lit0_is_capital := if p.tok.kind != .eof && p.tok.lit.len > 0 { + p.tok.lit[0].is_capital() + } else { + false + } + is_optional := p.tok.kind == .question + is_generic_call := p.is_generic_call() + is_generic_cast := p.is_generic_cast() + // p.warn('name expr $p.tok.lit $p.peek_tok.str()') + same_line := p.tok.line_nr == p.peek_tok.line_nr + // `(` must be on same line as name token otherwise it's a ParExpr + if !same_line && p.peek_tok.kind == .lpar { + ident := p.parse_ident(language) + node = ident + if p.inside_defer { + if !p.defer_vars.any(it.name == ident.name && it.mod == ident.mod) + && ident.name != 'err' { + p.defer_vars << ident + } + } + } else if p.peek_tok.kind == .lpar || is_generic_call || is_generic_cast + || (is_optional && p.peek_token(2).kind == .lpar) { + // foo(), foo() or type() cast + mut name := if is_optional { p.peek_tok.lit } else { p.tok.lit } + if mod.len > 0 { + name = '${mod}.$name' + } + name_w_mod := p.prepend_mod(name) + // type cast. TODO: finish + // if name in ast.builtin_type_names { + if (!known_var && (name in p.table.type_idxs || name_w_mod in p.table.type_idxs) + && name !in ['C.stat', 'C.sigaction']) || is_mod_cast || is_generic_cast + || (language == .v && name.len > 0 && name[0].is_capital()) { + // MainLetter(x) is *always* a cast, as long as it is not `C.` + // TODO handle C.stat() + start_pos := p.tok.position() + mut to_typ := p.parse_type() + // this prevents inner casts to also have an `&` + // example: &Foo(malloc(int(num))) + // without the next line int would result in int* + p.is_amp = false + p.check(.lpar) + mut expr := ast.empty_expr() + mut arg := ast.empty_expr() + mut has_arg := false + expr = p.expr(0) + // TODO, string(b, len) + if p.tok.kind == .comma && to_typ.idx() == ast.string_type_idx { + p.next() + arg = p.expr(0) // len + has_arg = true + } + end_pos := p.tok.position() + p.check(.rpar) + node = ast.CastExpr{ + typ: to_typ + typname: p.table.get_type_symbol(to_typ).name + expr: expr + arg: arg + has_arg: has_arg + pos: start_pos.extend(end_pos) + } + p.expr_mod = '' + return node + } else { + // fn call + if is_optional { + p.error_with_pos('unexpected $p.prev_tok', p.prev_tok.position()) + } + node = p.call_expr(language, mod) + } + } else if (p.peek_tok.kind == .lcbr || (p.peek_tok.kind == .lt && lit0_is_capital)) + && (!p.inside_match || (p.inside_select && prev_tok_kind == .arrow && lit0_is_capital)) + && !p.inside_match_case && (!p.inside_if || p.inside_select) + && (!p.inside_for || p.inside_select) { // && (p.tok.lit[0].is_capital() || p.builtin_mod) { + // map.v has struct literal: map{field: expr} + if p.peek_tok.kind == .lcbr && !(p.builtin_mod + && p.file_base in ['map.v', 'map_d_gcboehm_opt.v']) && p.tok.lit == 'map' { + // map{key_expr: val_expr} + p.check(.name) + p.check(.lcbr) + map_init := p.map_init() + p.check(.rcbr) + return map_init + } + return p.struct_init(false) // short_syntax: false + } else if p.peek_tok.kind == .dot && (lit0_is_capital && !known_var && language == .v) { + // T.name + if p.is_generic_name() { + pos := p.tok.position() + name := p.check_name() + p.check(.dot) + field := p.check_name() + pos.extend(p.tok.position()) + return ast.SelectorExpr{ + expr: ast.Ident{ + name: name + scope: p.scope + } + field_name: field + pos: pos + scope: p.scope + } + } + // `Color.green` + mut enum_name := p.check_name() + enum_name_pos := p.prev_tok.position() + if mod != '' { + enum_name = mod + '.' + enum_name + } else { + enum_name = p.imported_symbols[enum_name] or { p.prepend_mod(enum_name) } + } + p.check(.dot) + val := p.check_name() + p.expr_mod = '' + return ast.EnumVal{ + enum_name: enum_name + val: val + pos: enum_name_pos.extend(p.prev_tok.position()) + mod: mod + } + } else if language == .js && p.peek_tok.kind == .dot && p.peek_token(2).kind == .name { + // JS. function call with more than 1 dot + node = p.call_expr(language, mod) + } else { + ident := p.parse_ident(language) + node = ident + if p.inside_defer { + if !p.defer_vars.any(it.name == ident.name && it.mod == ident.mod) + && ident.name != 'err' { + p.defer_vars << ident + } + } + } + p.expr_mod = '' + return node +} + +fn (mut p Parser) index_expr(left ast.Expr) ast.IndexExpr { + // left == `a` in `a[0]` + start_pos := p.tok.position() + p.next() // [ + mut has_low := true + if p.tok.kind == .dotdot { + has_low = false + // [..end] + p.next() + high := p.expr(0) + pos := start_pos.extend(p.tok.position()) + p.check(.rsbr) + return ast.IndexExpr{ + left: left + pos: pos + index: ast.RangeExpr{ + low: ast.empty_expr() + high: high + has_high: true + pos: pos + } + } + } + expr := p.expr(0) // `[expr]` or `[expr..` + mut has_high := false + if p.tok.kind == .dotdot { + // [start..end] or [start..] + p.next() + mut high := ast.empty_expr() + if p.tok.kind != .rsbr { + has_high = true + high = p.expr(0) + } + pos := start_pos.extend(p.tok.position()) + p.check(.rsbr) + return ast.IndexExpr{ + left: left + pos: pos + index: ast.RangeExpr{ + low: expr + high: high + has_high: has_high + has_low: has_low + pos: pos + } + } + } + // [expr] + pos := start_pos.extend(p.tok.position()) + p.check(.rsbr) + mut or_kind := ast.OrKind.absent + mut or_stmts := []ast.Stmt{} + mut or_pos := token.Position{} + if !p.or_is_handled { + // a[i] or { ... } + if p.tok.kind == .key_orelse { + was_inside_or_expr := p.inside_or_expr + or_pos = p.tok.position() + p.next() + p.open_scope() + or_stmts = p.parse_block_no_scope(false) + or_pos = or_pos.extend(p.prev_tok.position()) + p.close_scope() + p.inside_or_expr = was_inside_or_expr + return ast.IndexExpr{ + left: left + index: expr + pos: pos + or_expr: ast.OrExpr{ + kind: .block + stmts: or_stmts + pos: or_pos + } + } + } + // `a[i] ?` + if p.tok.kind == .question { + or_pos = p.tok.position() + or_kind = .propagate + p.next() + } + } + return ast.IndexExpr{ + left: left + index: expr + pos: pos + or_expr: ast.OrExpr{ + kind: or_kind + stmts: or_stmts + pos: or_pos + } + } +} + +fn (mut p Parser) scope_register_it() { + p.scope.register(ast.Var{ + name: 'it' + pos: p.tok.position() + is_used: true + }) +} + +fn (mut p Parser) scope_register_ab() { + p.scope.register(ast.Var{ + name: 'a' + pos: p.tok.position() + is_used: true + }) + p.scope.register(ast.Var{ + name: 'b' + pos: p.tok.position() + is_used: true + }) +} + +fn (mut p Parser) dot_expr(left ast.Expr) ast.Expr { + p.next() + if p.tok.kind == .dollar { + return p.comptime_selector(left) + } + is_generic_call := p.is_generic_call() + name_pos := p.tok.position() + mut field_name := '' + // check if the name is on the same line as the dot + if (p.prev_tok.position().line_nr == name_pos.line_nr) || p.tok.kind != .name { + field_name = p.check_name() + } else { + p.name_error = true + } + is_filter := field_name in ['filter', 'map', 'any', 'all'] + if is_filter || field_name == 'sort' { + p.open_scope() + } + // ! in mutable methods + if p.tok.kind == .not && p.peek_tok.kind == .lpar { + p.next() + } + // Method call + // TODO move to fn.v call_expr() + mut concrete_types := []ast.Type{} + mut concrete_list_pos := p.tok.position() + if is_generic_call { + // `g.foo(10)` + concrete_types = p.parse_generic_type_list() + concrete_list_pos = concrete_list_pos.extend(p.prev_tok.position()) + // In case of `foo()` + // T is unwrapped and registered in the checker. + has_generic := concrete_types.any(it.has_flag(.generic)) + if !has_generic { + // will be added in checker + p.table.register_fn_concrete_types(field_name, concrete_types) + } + } + if p.tok.kind == .lpar { + p.next() + args := p.call_args() + p.check(.rpar) + mut or_stmts := []ast.Stmt{} + mut or_kind := ast.OrKind.absent + mut or_pos := p.tok.position() + if p.tok.kind == .key_orelse { + p.next() + p.open_scope() + p.scope.register(ast.Var{ + name: 'err' + typ: ast.error_type + pos: p.tok.position() + is_used: true + is_stack_obj: true + }) + or_kind = .block + or_stmts = p.parse_block_no_scope(false) + or_pos = or_pos.extend(p.prev_tok.position()) + p.close_scope() + } + // `foo()?` + if p.tok.kind == .question { + p.next() + or_kind = .propagate + } + // + end_pos := p.prev_tok.position() + pos := name_pos.extend(end_pos) + comments := p.eat_comments(same_line: true) + mcall_expr := ast.CallExpr{ + left: left + name: field_name + args: args + name_pos: name_pos + pos: pos + is_method: true + concrete_types: concrete_types + concrete_list_pos: concrete_list_pos + or_block: ast.OrExpr{ + stmts: or_stmts + kind: or_kind + pos: or_pos + } + scope: p.scope + comments: comments + } + if is_filter || field_name == 'sort' { + p.close_scope() + } + return mcall_expr + } + mut is_mut := false + mut mut_pos := token.Position{} + if p.inside_match || p.inside_if_expr { + match left { + ast.Ident, ast.SelectorExpr { + is_mut = left.is_mut + mut_pos = left.mut_pos + } + else {} + } + } + pos := if p.name_error { left.position().extend(name_pos) } else { name_pos } + sel_expr := ast.SelectorExpr{ + expr: left + field_name: field_name + pos: pos + is_mut: is_mut + mut_pos: mut_pos + scope: p.scope + next_token: p.tok.kind + } + if is_filter { + p.close_scope() + } + return sel_expr +} + +fn (mut p Parser) parse_generic_type_list() []ast.Type { + mut types := []ast.Type{} + if p.tok.kind != .lt { + return types + } + p.next() // `<` + mut first_done := false + for p.tok.kind !in [.eof, .gt] { + if first_done { + p.check(.comma) + } + types << p.parse_type() + first_done = true + } + p.check(.gt) // `>` + return types +} + +// `.green` +// `pref.BuildMode.default_mode` +fn (mut p Parser) enum_val() ast.EnumVal { + start_pos := p.tok.position() + p.check(.dot) + val := p.check_name() + return ast.EnumVal{ + val: val + pos: start_pos.extend(p.prev_tok.position()) + } +} + +fn (mut p Parser) filter_string_vet_errors(pos token.Position) { + if p.vet_errors.len == 0 { + return + } + p.vet_errors = p.vet_errors.filter( + (it.typ == .trailing_space && it.pos.line_nr - 1 >= pos.last_line) + || (it.typ != .trailing_space && it.pos.line_nr - 1 > pos.last_line) + || (it.typ == .space_indent && it.pos.line_nr - 1 <= pos.line_nr) + || (it.typ != .space_indent && it.pos.line_nr - 1 < pos.line_nr)) +} + +fn (mut p Parser) string_expr() ast.Expr { + is_raw := p.tok.kind == .name && p.tok.lit == 'r' + is_cstr := p.tok.kind == .name && p.tok.lit == 'c' + if is_raw || is_cstr { + p.next() + } + mut node := ast.empty_expr() + val := p.tok.lit + mut pos := p.tok.position() + pos.last_line = pos.line_nr + val.count('\n') + if p.peek_tok.kind != .str_dollar { + p.next() + p.filter_string_vet_errors(pos) + node = ast.StringLiteral{ + val: val + is_raw: is_raw + language: if is_cstr { ast.Language.c } else { ast.Language.v } + pos: pos + } + return node + } + mut exprs := []ast.Expr{} + mut vals := []string{} + mut has_fmts := []bool{} + mut fwidths := []int{} + mut precisions := []int{} + mut visible_pluss := []bool{} + mut fills := []bool{} + mut fmts := []byte{} + mut fposs := []token.Position{} + // Handle $ interpolation + p.inside_str_interp = true + for p.tok.kind == .string { + vals << p.tok.lit + p.next() + if p.tok.kind != .str_dollar { + break + } + p.next() + exprs << p.expr(0) + mut has_fmt := false + mut fwidth := 0 + mut fwidthneg := false + // 987698 is a magic default value, unlikely to be present in user input. NB: 0 is valid precision + mut precision := 987698 + mut visible_plus := false + mut fill := false + mut fmt := `_` // placeholder + if p.tok.kind == .colon { + p.next() + // ${num:-2d} + if p.tok.kind == .minus { + fwidthneg = true + p.next() + } else if p.tok.kind == .plus { + visible_plus = true + p.next() + } + // ${num:2d} + if p.tok.kind == .number { + fields := p.tok.lit.split('.') + if fields[0].len > 0 && fields[0][0] == `0` { + fill = true + } + fwidth = fields[0].int() + if fwidthneg { + fwidth = -fwidth + } + if fields.len > 1 { + precision = fields[1].int() + } + p.next() + } + if p.tok.kind == .name { + if p.tok.lit.len == 1 { + fmt = p.tok.lit[0] + has_fmt = true + p.next() + } else { + return p.error('format specifier may only be one letter') + } + } + } + fwidths << fwidth + has_fmts << has_fmt + precisions << precision + visible_pluss << visible_plus + fmts << fmt + fills << fill + fposs << p.prev_tok.position() + } + pos = pos.extend(p.prev_tok.position()) + p.filter_string_vet_errors(pos) + node = ast.StringInterLiteral{ + vals: vals + exprs: exprs + need_fmts: has_fmts + fwidths: fwidths + precisions: precisions + pluss: visible_pluss + fills: fills + fmts: fmts + fmt_poss: fposs + pos: pos + } + // need_fmts: prelimery - until checker finds out if really needed + p.inside_str_interp = false + return node +} + +fn (mut p Parser) parse_number_literal() ast.Expr { + mut pos := p.tok.position() + is_neg := p.tok.kind == .minus + if is_neg { + p.next() + pos = pos.extend(p.tok.position()) + } + lit := p.tok.lit + full_lit := if is_neg { '-' + lit } else { lit } + mut node := ast.empty_expr() + if lit.index_any('.eE') >= 0 && lit[..2] !in ['0x', '0X', '0o', '0O', '0b', '0B'] { + node = ast.FloatLiteral{ + val: full_lit + pos: pos + } + } else { + node = ast.IntegerLiteral{ + val: full_lit + pos: pos + } + } + p.next() + return node +} + +fn (mut p Parser) module_decl() ast.Module { + mut module_attrs := []ast.Attr{} + mut attrs_pos := p.tok.position() + if p.tok.kind == .lsbr { + p.attributes() + module_attrs = p.attrs + } + mut name := 'main' + is_skipped := p.tok.kind != .key_module + mut module_pos := token.Position{} + mut name_pos := token.Position{} + mut mod_node := ast.Module{} + if !is_skipped { + p.attrs = [] + module_pos = p.tok.position() + p.next() + name_pos = p.tok.position() + name = p.check_name() + mod_node = ast.Module{ + pos: module_pos + } + if module_pos.line_nr != name_pos.line_nr { + p.error_with_pos('`module` and `$name` must be at same line', name_pos) + return mod_node + } + // NB: this shouldn't be reassigned into name_pos + // as it creates a wrong position when extended + // to module_pos + n_pos := p.tok.position() + if module_pos.line_nr == n_pos.line_nr && p.tok.kind != .comment && p.tok.kind != .eof { + if p.tok.kind == .name { + p.error_with_pos('`module $name`, you can only declare one module, unexpected `$p.tok.lit`', + n_pos) + return mod_node + } else { + p.error_with_pos('`module $name`, unexpected `$p.tok.kind` after module name', + n_pos) + return mod_node + } + } + module_pos = attrs_pos.extend(name_pos) + } + full_name := util.qualify_module(p.pref, name, p.file_name) + p.mod = full_name + p.builtin_mod = p.mod == 'builtin' + mod_node = ast.Module{ + name: full_name + short_name: name + attrs: module_attrs + is_skipped: is_skipped + pos: module_pos + name_pos: name_pos + } + if !is_skipped { + for ma in module_attrs { + match ma.name { + 'manualfree' { + p.is_manualfree = true + } + else { + p.error_with_pos('unknown module attribute `[$ma.name]`', ma.pos) + return mod_node + } + } + } + } + return mod_node +} + +fn (mut p Parser) import_stmt() ast.Import { + import_pos := p.tok.position() + p.check(.key_import) + mut pos := p.tok.position() + mut import_node := ast.Import{ + pos: import_pos.extend(pos) + } + if p.tok.kind == .lpar { + p.error_with_pos('`import()` has been deprecated, use `import x` instead', pos) + return import_node + } + mut mod_name_arr := []string{} + mod_name_arr << p.check_name() + if import_pos.line_nr != pos.line_nr { + p.error_with_pos('`import` statements must be a single line', pos) + return import_node + } + mut mod_alias := mod_name_arr[0] + import_node = ast.Import{ + pos: import_pos.extend(pos) + mod_pos: pos + alias_pos: pos + } + for p.tok.kind == .dot { + p.next() + submod_pos := p.tok.position() + if p.tok.kind != .name { + p.error_with_pos('module syntax error, please use `x.y.z`', submod_pos) + return import_node + } + if import_pos.line_nr != submod_pos.line_nr { + p.error_with_pos('`import` and `submodule` must be at same line', submod_pos) + return import_node + } + submod_name := p.check_name() + mod_name_arr << submod_name + mod_alias = submod_name + pos = pos.extend(submod_pos) + import_node = ast.Import{ + pos: import_pos.extend(pos) + mod_pos: pos + alias_pos: submod_pos + mod: util.qualify_import(p.pref, mod_name_arr.join('.'), p.file_name) + alias: mod_alias + } + } + if mod_name_arr.len == 1 { + import_node = ast.Import{ + pos: import_node.pos + mod_pos: import_node.mod_pos + alias_pos: import_node.alias_pos + mod: util.qualify_import(p.pref, mod_name_arr[0], p.file_name) + alias: mod_alias + } + } + mod_name := import_node.mod + if p.tok.kind == .key_as { + p.next() + alias_pos := p.tok.position() + mod_alias = p.check_name() + if mod_alias == mod_name_arr.last() { + p.error_with_pos('import alias `$mod_name as $mod_alias` is redundant', p.prev_tok.position()) + return import_node + } + import_node = ast.Import{ + pos: import_node.pos.extend(alias_pos) + mod_pos: import_node.mod_pos + alias_pos: alias_pos + mod: import_node.mod + alias: mod_alias + } + } + if p.tok.kind == .lcbr { // import module { fn1, Type2 } syntax + mut initial_syms_pos := p.tok.position() + p.import_syms(mut import_node) + initial_syms_pos = initial_syms_pos.extend(p.tok.position()) + import_node = ast.Import{ + ...import_node + syms_pos: initial_syms_pos + pos: import_node.pos.extend(initial_syms_pos) + } + p.register_used_import(mod_alias) // no `unused import` msg for parent + } + pos_t := p.tok.position() + if import_pos.line_nr == pos_t.line_nr { + if p.tok.kind !in [.lcbr, .eof, .comment] { + p.error_with_pos('cannot import multiple modules at a time', pos_t) + return import_node + } + } + import_node.comments = p.eat_comments(same_line: true) + import_node.next_comments = p.eat_comments(follow_up: true) + p.imports[mod_alias] = mod_name + // if mod_name !in p.table.imports { + p.table.imports << mod_name + p.ast_imports << import_node + // } + return import_node +} + +// import_syms parses the inner part of `import module { submod1, submod2 }` +fn (mut p Parser) import_syms(mut parent ast.Import) { + p.next() + pos_t := p.tok.position() + if p.tok.kind == .rcbr { // closed too early + p.error_with_pos('empty `$parent.mod` import set, remove `{}`', pos_t) + return + } + if p.tok.kind != .name { // not a valid inner name + p.error_with_pos('import syntax error, please specify a valid fn or type name', + pos_t) + return + } + for p.tok.kind == .name { + pos := p.tok.position() + alias := p.check_name() + p.imported_symbols[alias] = parent.mod + '.' + alias + // so we can work with this in fmt+checker + parent.syms << ast.ImportSymbol{ + pos: pos + name: alias + } + if p.tok.kind == .comma { // go again if more than one + p.next() + continue + } + if p.tok.kind == .rcbr { // finish if closing `}` is seen + break + } + } + if p.tok.kind != .rcbr { + p.error_with_pos('import syntax error, no closing `}`', p.tok.position()) + return + } + p.next() +} + +fn (mut p Parser) const_decl() ast.ConstDecl { + p.top_level_statement_start() + start_pos := p.tok.position() + is_pub := p.tok.kind == .key_pub + if is_pub { + p.next() + } + const_pos := p.tok.position() + p.check(.key_const) + is_block := p.tok.kind == .lpar + if is_block { + p.next() // ( + } + mut fields := []ast.ConstField{} + mut comments := []ast.Comment{} + for { + comments = p.eat_comments() + if is_block && p.tok.kind == .eof { + p.error('unexpected eof, expecting ´)´') + return ast.ConstDecl{} + } + if p.tok.kind == .rpar { + break + } + pos := p.tok.position() + name := p.check_name() + if util.contains_capital(name) { + p.warn_with_pos('const names cannot contain uppercase letters, use snake_case instead', + pos) + } + full_name := p.prepend_mod(name) + p.check(.assign) + if p.tok.kind == .key_fn { + p.error('const initializer fn literal is not a constant') + return ast.ConstDecl{} + } + if p.tok.kind == .eof { + p.error('unexpected eof, expecting an expression') + return ast.ConstDecl{} + } + expr := p.expr(0) + field := ast.ConstField{ + name: full_name + mod: p.mod + is_pub: is_pub + expr: expr + pos: pos.extend(expr.position()) + comments: comments + } + fields << field + p.table.global_scope.register(field) + comments = [] + if !is_block { + break + } + } + p.top_level_statement_end() + if is_block { + p.check(.rpar) + } + return ast.ConstDecl{ + pos: start_pos.extend_with_last_line(const_pos, p.prev_tok.line_nr) + fields: fields + is_pub: is_pub + end_comments: comments + is_block: is_block + } +} + +fn (mut p Parser) return_stmt() ast.Return { + first_pos := p.tok.position() + p.next() + // no return + mut comments := p.eat_comments() + if p.tok.kind == .rcbr { + return ast.Return{ + comments: comments + pos: first_pos + } + } + // return exprs + exprs, comments2 := p.expr_list() + comments << comments2 + end_pos := exprs.last().position() + return ast.Return{ + exprs: exprs + comments: comments + pos: first_pos.extend(end_pos) + } +} + +const ( + // modules which allow globals by default + global_enabled_mods = ['rand', 'sokol.sapp'] +) + +// left hand side of `=` or `:=` in `a,b,c := 1,2,3` +fn (mut p Parser) global_decl() ast.GlobalDecl { + if !p.pref.translated && !p.pref.is_livemain && !p.builtin_mod && !p.pref.building_v + && !p.pref.enable_globals && !p.pref.is_fmt && p.mod !in parser.global_enabled_mods { + p.error('use `v -enable-globals ...` to enable globals') + return ast.GlobalDecl{} + } + start_pos := p.tok.position() + p.check(.key_global) + is_block := p.tok.kind == .lpar + if is_block { + p.next() // ( + } + mut fields := []ast.GlobalField{} + mut comments := []ast.Comment{} + for { + comments = p.eat_comments() + if is_block && p.tok.kind == .eof { + p.error('unexpected eof, expecting ´)´') + return ast.GlobalDecl{} + } + if p.tok.kind == .rpar { + break + } + pos := p.tok.position() + name := p.check_name() + has_expr := p.tok.kind == .assign + mut expr := ast.empty_expr() + mut typ := ast.void_type + mut typ_pos := token.Position{} + if has_expr { + p.next() // = + expr = p.expr(0) + match expr { + ast.CastExpr { + typ = (expr as ast.CastExpr).typ + } + ast.StructInit { + typ = (expr as ast.StructInit).typ + } + ast.ArrayInit { + typ = (expr as ast.ArrayInit).typ + } + ast.ChanInit { + typ = (expr as ast.ChanInit).typ + } + ast.BoolLiteral, ast.IsRefType { + typ = ast.bool_type + } + ast.CharLiteral { + typ = ast.char_type + } + ast.FloatLiteral { + typ = ast.f64_type + } + ast.IntegerLiteral, ast.SizeOf { + typ = ast.int_type + } + ast.StringLiteral, ast.StringInterLiteral { + typ = ast.string_type + } + else { + // type will be deduced by checker + } + } + } else { + typ_pos = p.tok.position() + typ = p.parse_type() + } + field := ast.GlobalField{ + name: name + has_expr: has_expr + expr: expr + pos: pos + typ_pos: typ_pos + typ: typ + comments: comments + } + fields << field + p.table.global_scope.register(field) + comments = [] + if !is_block { + break + } + } + if is_block { + p.check(.rpar) + } + return ast.GlobalDecl{ + pos: start_pos.extend(p.prev_tok.position()) + mod: p.mod + fields: fields + end_comments: comments + is_block: is_block + } +} + +fn (mut p Parser) enum_decl() ast.EnumDecl { + p.top_level_statement_start() + is_pub := p.tok.kind == .key_pub + start_pos := p.tok.position() + if is_pub { + p.next() + } + p.check(.key_enum) + end_pos := p.tok.position() + enum_name := p.check_name() + if enum_name.len == 1 { + p.error_with_pos('single letter capital names are reserved for generic template types.', + end_pos) + return ast.EnumDecl{} + } + if enum_name in p.imported_symbols { + p.error_with_pos('cannot register enum `$enum_name`, this type was already imported', + end_pos) + return ast.EnumDecl{} + } + name := p.prepend_mod(enum_name) + p.check(.lcbr) + enum_decl_comments := p.eat_comments() + mut vals := []string{} + // mut default_exprs := []ast.Expr{} + mut fields := []ast.EnumField{} + for p.tok.kind != .eof && p.tok.kind != .rcbr { + pos := p.tok.position() + val := p.check_name() + vals << val + mut expr := ast.empty_expr() + mut has_expr := false + // p.warn('enum val $val') + if p.tok.kind == .assign { + p.next() + expr = p.expr(0) + has_expr = true + } + fields << ast.EnumField{ + name: val + pos: pos + expr: expr + has_expr: has_expr + comments: p.eat_comments(same_line: true) + next_comments: p.eat_comments() + } + } + p.top_level_statement_end() + p.check(.rcbr) + is_flag := p.attrs.contains('flag') + is_multi_allowed := p.attrs.contains('_allow_multiple_values') + if is_flag { + if fields.len > 32 { + p.error('when an enum is used as bit field, it must have a max of 32 fields') + return ast.EnumDecl{} + } + for f in fields { + if f.has_expr { + p.error_with_pos('when an enum is used as a bit field, you can not assign custom values', + f.pos) + return ast.EnumDecl{} + } + } + pubfn := if p.mod == 'main' { 'fn' } else { 'pub fn' } + p.scanner.codegen(' +// +[inline] $pubfn ( e &$enum_name) is_empty() bool { return int(*e) == 0 } +[inline] $pubfn ( e &$enum_name) has(flag $enum_name) bool { return (int(*e) & (int(flag))) != 0 } +[inline] $pubfn (mut e $enum_name) set(flag $enum_name) { unsafe{ *e = ${enum_name}(int(*e) | (int(flag))) } } +[inline] $pubfn (mut e $enum_name) clear(flag $enum_name) { unsafe{ *e = ${enum_name}(int(*e) & ~(int(flag))) } } +[inline] $pubfn (mut e $enum_name) toggle(flag $enum_name) { unsafe{ *e = ${enum_name}(int(*e) ^ (int(flag))) } } +// +') + } + idx := p.table.register_type_symbol(ast.TypeSymbol{ + kind: .enum_ + name: name + cname: util.no_dots(name) + mod: p.mod + info: ast.Enum{ + vals: vals + is_flag: is_flag + is_multi_allowed: is_multi_allowed + } + is_public: is_pub + }) + if idx == -1 { + p.error_with_pos('cannot register enum `$name`, another type with this name exists', + end_pos) + } + + enum_decl := ast.EnumDecl{ + name: name + is_pub: is_pub + is_flag: is_flag + is_multi_allowed: is_multi_allowed + fields: fields + pos: start_pos.extend_with_last_line(end_pos, p.prev_tok.line_nr) + attrs: p.attrs + comments: enum_decl_comments + } + + p.table.register_enum_decl(enum_decl) + + return enum_decl +} + +fn (mut p Parser) type_decl() ast.TypeDecl { + start_pos := p.tok.position() + is_pub := p.tok.kind == .key_pub + if is_pub { + p.next() + } + p.check(.key_type) + end_pos := p.tok.position() + decl_pos := start_pos.extend(end_pos) + name := p.check_name() + if name.len == 1 && name[0].is_capital() { + p.error_with_pos('single letter capital names are reserved for generic template types.', + decl_pos) + return ast.FnTypeDecl{} + } + if name in p.imported_symbols { + p.error_with_pos('cannot register alias `$name`, this type was already imported', + end_pos) + return ast.AliasTypeDecl{} + } + mut sum_variants := []ast.TypeNode{} + generic_types := p.parse_generic_type_list() + decl_pos_with_generics := decl_pos.extend(p.prev_tok.position()) + p.check(.assign) + mut type_pos := p.tok.position() + mut comments := []ast.Comment{} + if p.tok.kind == .key_fn { + // function type: `type mycallback = fn(string, int)` + fn_name := p.prepend_mod(name) + fn_type := p.parse_fn_type(fn_name) + p.table.get_type_symbol(fn_type).is_public = is_pub + type_pos = type_pos.extend(p.tok.position()) + comments = p.eat_comments(same_line: true) + return ast.FnTypeDecl{ + name: fn_name + is_pub: is_pub + typ: fn_type + pos: decl_pos + type_pos: type_pos + comments: comments + } + } + first_type := p.parse_type() // need to parse the first type before we can check if it's `type A = X | Y` + type_alias_pos := p.tok.position() + if p.tok.kind == .pipe { + mut type_end_pos := p.prev_tok.position() + type_pos = type_pos.extend(type_end_pos) + p.next() + sum_variants << ast.TypeNode{ + typ: first_type + pos: type_pos + } + // type SumType = A | B | c + for { + type_pos = p.tok.position() + variant_type := p.parse_type() + // TODO: needs to be its own var, otherwise TCC fails because of a known stack error + prev_tok := p.prev_tok + type_end_pos = prev_tok.position() + type_pos = type_pos.extend(type_end_pos) + sum_variants << ast.TypeNode{ + typ: variant_type + pos: type_pos + } + if p.tok.kind != .pipe { + break + } + p.check(.pipe) + } + variant_types := sum_variants.map(it.typ) + prepend_mod_name := p.prepend_mod(name) + typ := p.table.register_type_symbol(ast.TypeSymbol{ + kind: .sum_type + name: prepend_mod_name + cname: util.no_dots(prepend_mod_name) + mod: p.mod + info: ast.SumType{ + variants: variant_types + is_generic: generic_types.len > 0 + generic_types: generic_types + } + is_public: is_pub + }) + comments = p.eat_comments(same_line: true) + return ast.SumTypeDecl{ + name: name + typ: typ + is_pub: is_pub + variants: sum_variants + generic_types: generic_types + pos: decl_pos + comments: comments + } + } + // type MyType = int + if generic_types.len > 0 { + p.error_with_pos('generic type aliases are not yet implemented', decl_pos_with_generics) + return ast.AliasTypeDecl{} + } + parent_type := first_type + parent_sym := p.table.get_type_symbol(parent_type) + pidx := parent_type.idx() + p.check_for_impure_v(parent_sym.language, decl_pos) + prepend_mod_name := p.prepend_mod(name) + idx := p.table.register_type_symbol(ast.TypeSymbol{ + kind: .alias + name: prepend_mod_name + cname: util.no_dots(prepend_mod_name) + mod: p.mod + parent_idx: pidx + info: ast.Alias{ + parent_type: parent_type + language: parent_sym.language + } + is_public: is_pub + }) + type_end_pos := p.prev_tok.position() + if idx == -1 { + p.error_with_pos('cannot register alias `$name`, another type with this name exists', + decl_pos.extend(type_alias_pos)) + return ast.AliasTypeDecl{} + } + if idx == pidx { + p.error_with_pos('a type alias can not refer to itself: $name', decl_pos.extend(type_alias_pos)) + return ast.AliasTypeDecl{} + } + comments = p.eat_comments(same_line: true) + return ast.AliasTypeDecl{ + name: name + is_pub: is_pub + parent_type: parent_type + type_pos: type_pos.extend(type_end_pos) + pos: decl_pos + comments: comments + } +} + +fn (mut p Parser) assoc() ast.Assoc { + var_name := p.check_name() + pos := p.tok.position() + mut v := p.scope.find_var(var_name) or { + p.error('unknown variable `$var_name`') + return ast.Assoc{ + scope: 0 + } + } + v.is_used = true + mut fields := []string{} + mut vals := []ast.Expr{} + p.check(.pipe) + for p.tok.kind != .eof { + fields << p.check_name() + p.check(.colon) + expr := p.expr(0) + vals << expr + if p.tok.kind == .comma { + p.next() + } + if p.tok.kind == .rcbr { + break + } + } + return ast.Assoc{ + var_name: var_name + fields: fields + exprs: vals + pos: pos + scope: p.scope + } +} + +fn (p &Parser) new_true_expr() ast.Expr { + return ast.BoolLiteral{ + val: true + pos: p.tok.position() + } +} + +[noreturn] +fn verror(s string) { + util.verror('parser error', s) +} + +fn (mut p Parser) top_level_statement_start() { + if p.comments_mode == .toplevel_comments { + p.scanner.set_is_inside_toplevel_statement(true) + p.rewind_scanner_to_current_token_in_new_mode() + $if debugscanner ? { + eprintln('>> p.top_level_statement_start | tidx:${p.tok.tidx:-5} | p.tok.kind: ${p.tok.kind:-10} | p.tok.lit: $p.tok.lit $p.peek_tok.lit ${p.peek_token(2).lit} ${p.peek_token(3).lit} ...') + } + } +} + +fn (mut p Parser) top_level_statement_end() { + if p.comments_mode == .toplevel_comments { + p.scanner.set_is_inside_toplevel_statement(false) + p.rewind_scanner_to_current_token_in_new_mode() + $if debugscanner ? { + eprintln('>> p.top_level_statement_end | tidx:${p.tok.tidx:-5} | p.tok.kind: ${p.tok.kind:-10} | p.tok.lit: $p.tok.lit $p.peek_tok.lit ${p.peek_token(2).lit} ${p.peek_token(3).lit} ...') + } + } +} + +fn (mut p Parser) rewind_scanner_to_current_token_in_new_mode() { + // Go back and rescan some tokens, ensuring that the parser's + // lookahead buffer p.peek_tok .. p.peek_token(3), will now contain + // the correct tokens (possible comments), for the new mode + // This refilling of the lookahead buffer is needed for the + // .toplevel_comments parsing mode. + tidx := p.tok.tidx + p.scanner.set_current_tidx(tidx - 5) + no_token := token.Token{} + p.prev_tok = no_token + p.tok = no_token + p.peek_tok = no_token // requires 2 calls p.next() or check p.tok.kind != token.Kind.unknown + p.next() + for { + p.next() + // eprintln('rewinding to ${p.tok.tidx:5} | goal: ${tidx:5}') + if tidx == p.tok.tidx { + break + } + } +} + +pub fn (mut p Parser) mark_var_as_used(varname string) bool { + if obj := p.scope.find(varname) { + match mut obj { + ast.Var { + obj.is_used = true + return true + } + else {} + } + } + return false +} + +fn (mut p Parser) unsafe_stmt() ast.Stmt { + mut pos := p.tok.position() + p.next() + if p.tok.kind != .lcbr { + return p.error_with_pos('please use `unsafe {`', p.tok.position()) + } + p.next() + if p.inside_unsafe { + return p.error_with_pos('already inside `unsafe` block', pos) + } + if p.tok.kind == .rcbr { + // `unsafe {}` + pos.update_last_line(p.tok.line_nr) + p.next() + return ast.Block{ + is_unsafe: true + pos: pos + } + } + p.inside_unsafe = true + p.open_scope() // needed in case of `unsafe {stmt}` + defer { + p.inside_unsafe = false + p.close_scope() + } + stmt := p.stmt(false) + if p.tok.kind == .rcbr { + if stmt is ast.ExprStmt { + // `unsafe {expr}` + if stmt.expr.is_expr() { + p.next() + pos.update_last_line(p.prev_tok.line_nr) + ue := ast.UnsafeExpr{ + expr: stmt.expr + pos: pos + } + // parse e.g. `unsafe {expr}.foo()` + expr := p.expr_with_left(ue, 0, p.is_stmt_ident) + return ast.ExprStmt{ + expr: expr + pos: pos + } + } + } + } + // unsafe {stmts} + mut stmts := [stmt] + for p.tok.kind != .rcbr { + stmts << p.stmt(false) + } + p.next() + pos.update_last_line(p.tok.line_nr) + return ast.Block{ + stmts: stmts + is_unsafe: true + pos: pos + } +} + +fn (mut p Parser) trace(fbase string, message string) { + if p.file_base == fbase { + println('> p.trace | ${fbase:-10s} | $message') + } +} diff --git a/v_windows/v/old/vlib/v/parser/sql.v b/v_windows/v/old/vlib/v/parser/sql.v new file mode 100644 index 0000000..28a7157 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/sql.v @@ -0,0 +1,262 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module parser + +import v.ast + +fn (mut p Parser) sql_expr() ast.Expr { + // `sql db {` + pos := p.tok.position() + p.check_name() + db_expr := p.check_expr(0) or { + p.error_with_pos('invalid expression: unexpected $p.tok, expecting database', + p.tok.position()) + } + p.check(.lcbr) + p.check(.key_select) + n := p.check_name() + is_count := n == 'count' + mut typ := ast.void_type + if is_count { + p.check_name() // from + typ = ast.int_type + } + table_pos := p.tok.position() + table_type := p.parse_type() // `User` + mut where_expr := ast.empty_expr() + has_where := p.tok.kind == .name && p.tok.lit == 'where' + mut query_one := false // one object is returned, not an array + if has_where { + p.next() + where_expr = p.expr(0) + // `id == x` means that a single object is returned + if !is_count && where_expr is ast.InfixExpr { + e := where_expr as ast.InfixExpr + if e.op == .eq && e.left is ast.Ident { + if e.left.name == 'id' { + query_one = true + } + } + } + } + mut has_limit := false + mut limit_expr := ast.empty_expr() + mut has_offset := false + mut offset_expr := ast.empty_expr() + mut has_order := false + mut order_expr := ast.empty_expr() + mut has_desc := false + if p.tok.kind == .name && p.tok.lit == 'order' { + p.check_name() // `order` + order_pos := p.tok.position() + if p.tok.kind == .name && p.tok.lit == 'by' { + p.check_name() // `by` + } else { + return p.error_with_pos('use `order by` in ORM queries', order_pos) + } + has_order = true + order_expr = p.expr(0) + if p.tok.kind == .name && p.tok.lit == 'desc' { + p.check_name() // `desc` + has_desc = true + } + } + if p.tok.kind == .name && p.tok.lit == 'limit' { + // `limit 1` means that a single object is returned + p.check_name() // `limit` + if p.tok.kind == .number && p.tok.lit == '1' { + query_one = true + } + has_limit = true + limit_expr = p.expr(0) + } + if p.tok.kind == .name && p.tok.lit == 'offset' { + p.check_name() // `offset` + has_offset = true + offset_expr = p.expr(0) + } + if !query_one && !is_count { + // return an array + typ = ast.new_type(p.table.find_or_register_array(table_type)) + } else if !is_count { + // return a single object + // TODO optional + // typ = table_type.set_flag(.optional) + typ = table_type + } + p.check(.rcbr) + return ast.SqlExpr{ + is_count: is_count + typ: typ + db_expr: db_expr + where_expr: where_expr + has_where: has_where + has_limit: has_limit + limit_expr: limit_expr + has_offset: has_offset + offset_expr: offset_expr + has_order: has_order + order_expr: order_expr + has_desc: has_desc + is_array: !query_one + pos: pos.extend(p.prev_tok.position()) + table_expr: ast.TypeNode{ + typ: table_type + pos: table_pos + } + } +} + +// insert user into User +// update User set nr_oders=nr_orders+1 where id == user_id +fn (mut p Parser) sql_stmt() ast.SqlStmt { + mut pos := p.tok.position() + p.inside_match = true + defer { + p.inside_match = false + } + // `sql db {` + p.check_name() + db_expr := p.check_expr(0) or { + p.error_with_pos('invalid expression: unexpected $p.tok, expecting database', + p.tok.position()) + } + // println(typeof(db_expr)) + p.check(.lcbr) + + mut lines := []ast.SqlStmtLine{} + + for p.tok.kind != .rcbr { + lines << p.parse_sql_stmt_line() + } + + p.next() + pos.last_line = p.prev_tok.line_nr + return ast.SqlStmt{ + pos: pos.extend(p.prev_tok.position()) + db_expr: db_expr + lines: lines + } +} + +fn (mut p Parser) parse_sql_stmt_line() ast.SqlStmtLine { + mut n := p.check_name() // insert + pos := p.tok.position() + mut kind := ast.SqlStmtKind.insert + if n == 'delete' { + kind = .delete + } else if n == 'update' { + kind = .update + } else if n == 'create' { + kind = .create + table := p.check_name() + if table != 'table' { + p.error('expected `table` got `$table`') + return ast.SqlStmtLine{} + } + typ := p.parse_type() + typ_pos := p.tok.position() + return ast.SqlStmtLine{ + kind: kind + pos: pos.extend(p.prev_tok.position()) + table_expr: ast.TypeNode{ + typ: typ + pos: typ_pos + } + } + } else if n == 'drop' { + kind = .drop + table := p.check_name() + if table != 'table' { + p.error('expected `table` got `$table`') + return ast.SqlStmtLine{} + } + typ := p.parse_type() + typ_pos := p.tok.position() + return ast.SqlStmtLine{ + kind: kind + pos: pos.extend(p.prev_tok.position()) + table_expr: ast.TypeNode{ + typ: typ + pos: typ_pos + } + } + } + mut inserted_var_name := '' + mut table_type := ast.Type(0) + if kind != .delete { + if kind == .update { + table_type = p.parse_type() + } else if kind == .insert { + expr := p.expr(0) + if expr is ast.Ident { + inserted_var_name = expr.name + } else { + p.error('can only insert variables') + return ast.SqlStmtLine{} + } + } + } + n = p.check_name() // into + mut updated_columns := []string{} + mut update_exprs := []ast.Expr{cap: 5} + if kind == .insert && n != 'into' { + p.error('expecting `into`') + return ast.SqlStmtLine{} + } else if kind == .update { + if n != 'set' { + p.error('expecting `set`') + return ast.SqlStmtLine{} + } + for { + column := p.check_name() + updated_columns << column + p.check(.assign) + update_exprs << p.expr(0) + if p.tok.kind == .comma { + p.check(.comma) + } else { + break + } + } + } else if kind == .delete && n != 'from' { + p.error('expecting `from`') + return ast.SqlStmtLine{} + } + + mut table_pos := p.tok.position() + mut where_expr := ast.empty_expr() + if kind == .insert { + table_pos = p.tok.position() + table_type = p.parse_type() + } else if kind == .update { + p.check_sql_keyword('where') or { return ast.SqlStmtLine{} } + where_expr = p.expr(0) + } else if kind == .delete { + table_pos = p.tok.position() + table_type = p.parse_type() + p.check_sql_keyword('where') or { return ast.SqlStmtLine{} } + where_expr = p.expr(0) + } + return ast.SqlStmtLine{ + table_expr: ast.TypeNode{ + typ: table_type + pos: table_pos + } + object_var_name: inserted_var_name + pos: pos + updated_columns: updated_columns + update_exprs: update_exprs + kind: kind + where_expr: where_expr + } +} + +fn (mut p Parser) check_sql_keyword(name string) ?bool { + if p.check_name() != name { + p.error('orm: expecting `$name`') + return none + } + return true +} diff --git a/v_windows/v/old/vlib/v/parser/struct.v b/v_windows/v/old/vlib/v/parser/struct.v new file mode 100644 index 0000000..f282b51 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/struct.v @@ -0,0 +1,622 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module parser + +import v.ast +import v.token +import v.util + +fn (mut p Parser) struct_decl() ast.StructDecl { + p.top_level_statement_start() + // save attributes, they will be changed later in fields + attrs := p.attrs + p.attrs = [] + start_pos := p.tok.position() + is_pub := p.tok.kind == .key_pub + if is_pub { + p.next() + } + is_union := p.tok.kind == .key_union + if p.tok.kind == .key_struct { + p.next() + } else { + p.check(.key_union) + } + language := if p.tok.lit == 'C' && p.peek_tok.kind == .dot { + ast.Language.c + } else if p.tok.lit == 'JS' && p.peek_tok.kind == .dot { + ast.Language.js + } else { + ast.Language.v + } + if language != .v { + p.next() // C || JS + p.next() // . + } + name_pos := p.tok.position() + p.check_for_impure_v(language, name_pos) + mut name := p.check_name() + // defer { + // if name.contains('App') { + // println('end of struct decl $name') + // } + // } + if name.len == 1 && name[0].is_capital() { + p.error_with_pos('single letter capital names are reserved for generic template types.', + name_pos) + return ast.StructDecl{} + } + generic_types := p.parse_generic_type_list() + no_body := p.tok.kind != .lcbr + if language == .v && no_body { + p.error('`$p.tok.lit` lacks body') + return ast.StructDecl{} + } + if language == .v && !p.builtin_mod && name.len > 0 && !name[0].is_capital() + && !p.pref.translated { + p.error_with_pos('struct name `$name` must begin with capital letter', name_pos) + return ast.StructDecl{} + } + if name.len == 1 { + p.error_with_pos('struct names must have more than one character', name_pos) + return ast.StructDecl{} + } + if name in p.imported_symbols { + p.error_with_pos('cannot register struct `$name`, this type was already imported', + name_pos) + return ast.StructDecl{} + } + mut orig_name := name + if language == .c { + name = 'C.$name' + orig_name = name + } else if language == .js { + name = 'JS.$name' + orig_name = name + } else { + name = p.prepend_mod(name) + } + mut ast_fields := []ast.StructField{} + mut fields := []ast.StructField{} + mut embed_types := []ast.Type{} + mut embeds := []ast.Embed{} + mut embed_field_names := []string{} + mut mut_pos := -1 + mut pub_pos := -1 + mut pub_mut_pos := -1 + mut global_pos := -1 + mut module_pos := -1 + mut is_field_mut := false + mut is_field_pub := false + mut is_field_global := false + mut last_line := p.prev_tok.position().line_nr + 1 + mut end_comments := []ast.Comment{} + if !no_body { + p.check(.lcbr) + for p.tok.kind != .rcbr { + mut comments := []ast.Comment{} + for p.tok.kind == .comment { + comments << p.comment() + if p.tok.kind == .rcbr { + break + } + } + if p.tok.kind == .rcbr { + end_comments = comments.clone() + break + } + if p.tok.kind == .key_pub { + p.next() + if p.tok.kind == .key_mut { + if pub_mut_pos != -1 { + p.error('redefinition of `pub mut` section') + return ast.StructDecl{} + } + p.next() + pub_mut_pos = ast_fields.len + is_field_pub = true + is_field_mut = true + is_field_global = false + } else { + if pub_pos != -1 { + p.error('redefinition of `pub` section') + return ast.StructDecl{} + } + pub_pos = ast_fields.len + is_field_pub = true + is_field_mut = false + is_field_global = false + } + p.check(.colon) + } else if p.tok.kind == .key_mut { + if mut_pos != -1 { + p.error('redefinition of `mut` section') + return ast.StructDecl{} + } + p.next() + p.check(.colon) + mut_pos = ast_fields.len + is_field_pub = false + is_field_mut = true + is_field_global = false + } else if p.tok.kind == .key_global { + if global_pos != -1 { + p.error('redefinition of `global` section') + return ast.StructDecl{} + } + p.next() + p.check(.colon) + global_pos = ast_fields.len + is_field_pub = true + is_field_mut = true + is_field_global = true + } else if p.tok.kind == .key_module { + if module_pos != -1 { + p.error('redefinition of `module` section') + return ast.StructDecl{} + } + p.next() + p.check(.colon) + module_pos = ast_fields.len + is_field_pub = false + is_field_mut = false + is_field_global = false + } + for p.tok.kind == .comment { + comments << p.comment() + if p.tok.kind == .rcbr { + break + } + } + field_start_pos := p.tok.position() + is_embed := ((p.tok.lit.len > 1 && p.tok.lit[0].is_capital()) + || p.peek_tok.kind == .dot) && language == .v && p.peek_tok.kind != .key_fn + is_on_top := ast_fields.len == 0 && !(is_field_mut || is_field_global) + mut field_name := '' + mut typ := ast.Type(0) + mut type_pos := token.Position{} + mut field_pos := token.Position{} + if is_embed { + // struct embedding + type_pos = p.tok.position() + typ = p.parse_type() + ecomments := p.eat_comments() + type_pos = type_pos.extend(p.prev_tok.position()) + if !is_on_top { + p.error_with_pos('struct embedding must be declared at the beginning of the struct body', + type_pos) + return ast.StructDecl{} + } + sym := p.table.get_type_symbol(typ) + if typ in embed_types { + p.error_with_pos('cannot embed `$sym.name` more than once', type_pos) + return ast.StructDecl{} + } + field_name = sym.embed_name() + if field_name in embed_field_names { + p.error_with_pos('duplicate field `$field_name`', type_pos) + return ast.StructDecl{} + } + embed_field_names << field_name + embed_types << typ + embeds << ast.Embed{ + typ: typ + pos: type_pos + comments: ecomments + } + } else { + // struct field + field_name = p.check_name() + for p.tok.kind == .comment { + comments << p.comment() + if p.tok.kind == .rcbr { + break + } + } + typ = p.parse_type() + if typ.idx() == 0 { + // error is set in parse_type + return ast.StructDecl{} + } + type_pos = p.prev_tok.position() + field_pos = field_start_pos.extend(type_pos) + } + // Comments after type (same line) + comments << p.eat_comments() + if p.tok.kind == .lsbr { + // attrs are stored in `p.attrs` + p.attributes() + } + mut default_expr := ast.empty_expr() + mut has_default_expr := false + if !is_embed { + if p.tok.kind == .assign { + // Default value + p.next() + default_expr = p.expr(0) + match mut default_expr { + ast.EnumVal { default_expr.typ = typ } + // TODO: implement all types?? + else {} + } + has_default_expr = true + comments << p.eat_comments() + } + ast_fields << ast.StructField{ + name: field_name + typ: typ + pos: field_pos + type_pos: type_pos + comments: comments + default_expr: default_expr + has_default_expr: has_default_expr + attrs: p.attrs + is_pub: is_embed || is_field_pub + is_mut: is_embed || is_field_mut + is_global: is_field_global + } + } + // save embeds as table fields too, it will be used in generation phase + fields << ast.StructField{ + name: field_name + typ: typ + pos: field_pos + type_pos: type_pos + comments: comments + default_expr: default_expr + has_default_expr: has_default_expr + attrs: p.attrs + is_pub: is_embed || is_field_pub + is_mut: is_embed || is_field_mut + is_global: is_field_global + } + p.attrs = [] + } + p.top_level_statement_end() + last_line = p.tok.line_nr + p.check(.rcbr) + } + t := ast.TypeSymbol{ + kind: .struct_ + language: language + name: name + cname: util.no_dots(name) + mod: p.mod + info: ast.Struct{ + embeds: embed_types + fields: fields + is_typedef: attrs.contains('typedef') + is_union: is_union + is_heap: attrs.contains('heap') + is_generic: generic_types.len > 0 + generic_types: generic_types + attrs: attrs + } + is_public: is_pub + } + if p.table.has_deep_child_no_ref(&t, name) { + p.error_with_pos('invalid recursive struct `$orig_name`', name_pos) + return ast.StructDecl{} + } + mut ret := 0 + // println('reg type symbol $name mod=$p.mod') + ret = p.table.register_type_symbol(t) + // allow duplicate c struct declarations + if ret == -1 && language != .c { + p.error_with_pos('cannot register struct `$name`, another type with this name exists', + name_pos) + return ast.StructDecl{} + } + p.expr_mod = '' + return ast.StructDecl{ + name: name + is_pub: is_pub + fields: ast_fields + pos: start_pos.extend_with_last_line(name_pos, last_line) + mut_pos: mut_pos + pub_pos: pub_pos + pub_mut_pos: pub_mut_pos + global_pos: global_pos + module_pos: module_pos + language: language + is_union: is_union + attrs: attrs + end_comments: end_comments + generic_types: generic_types + embeds: embeds + } +} + +fn (mut p Parser) struct_init(short_syntax bool) ast.StructInit { + first_pos := (if short_syntax && p.prev_tok.kind == .lcbr { p.prev_tok } else { p.tok }).position() + typ := if short_syntax { ast.void_type } else { p.parse_type() } + p.expr_mod = '' + // sym := p.table.get_type_symbol(typ) + // p.warn('struct init typ=$sym.name') + if !short_syntax { + p.check(.lcbr) + } + pre_comments := p.eat_comments() + mut fields := []ast.StructInitField{} + mut i := 0 + no_keys := p.peek_tok.kind != .colon && p.tok.kind != .rcbr && p.tok.kind != .ellipsis // `Vec{a,b,c} + saved_is_amp := p.is_amp + p.is_amp = false + mut update_expr := ast.empty_expr() + mut update_expr_comments := []ast.Comment{} + mut has_update_expr := false + for p.tok.kind !in [.rcbr, .rpar, .eof] { + mut field_name := '' + mut expr := ast.empty_expr() + mut field_pos := token.Position{} + mut first_field_pos := token.Position{} + mut comments := []ast.Comment{} + mut nline_comments := []ast.Comment{} + is_update_expr := fields.len == 0 && p.tok.kind == .ellipsis + if no_keys { + // name will be set later in checker + expr = p.expr(0) + field_pos = expr.position() + first_field_pos = field_pos + comments = p.eat_comments(same_line: true) + } else if is_update_expr { + // struct updating syntax; f2 := Foo{ ...f, name: 'f2' } + p.check(.ellipsis) + update_expr = p.expr(0) + update_expr_comments << p.eat_comments(same_line: true) + has_update_expr = true + } else { + first_field_pos = p.tok.position() + field_name = p.check_name() + p.check(.colon) + expr = p.expr(0) + comments = p.eat_comments(same_line: true) + last_field_pos := expr.position() + field_len := if last_field_pos.len > 0 { + last_field_pos.pos - first_field_pos.pos + last_field_pos.len + } else { + first_field_pos.len + 1 + } + field_pos = token.Position{ + line_nr: first_field_pos.line_nr + pos: first_field_pos.pos + len: field_len + col: first_field_pos.col + } + } + i++ + if p.tok.kind == .comma { + p.next() + } + comments << p.eat_comments(same_line: true) + nline_comments << p.eat_comments() + if !is_update_expr { + fields << ast.StructInitField{ + name: field_name + expr: expr + pos: field_pos + name_pos: first_field_pos + comments: comments + next_comments: nline_comments + parent_type: typ + } + } + } + if !short_syntax { + p.check(.rcbr) + } + p.is_amp = saved_is_amp + return ast.StructInit{ + unresolved: typ.has_flag(.generic) + typ: typ + fields: fields + update_expr: update_expr + update_expr_comments: update_expr_comments + has_update_expr: has_update_expr + name_pos: first_pos + pos: first_pos.extend(if short_syntax { p.tok.position() } else { p.prev_tok.position() }) + is_short: no_keys + pre_comments: pre_comments + } +} + +fn (mut p Parser) interface_decl() ast.InterfaceDecl { + p.top_level_statement_start() + mut pos := p.tok.position() + is_pub := p.tok.kind == .key_pub + if is_pub { + p.next() + } + p.next() // `interface` + language := if p.tok.lit == 'C' && p.peek_tok.kind == .dot { + ast.Language.c + } else if p.tok.lit == 'JS' && p.peek_tok.kind == .dot { + ast.Language.js + } else { + ast.Language.v + } + if language != .v { + p.next() // C || JS + p.next() // . + } + name_pos := p.tok.position() + p.check_for_impure_v(language, name_pos) + modless_name := p.check_name() + interface_name := p.prepend_mod(modless_name).clone() + generic_types := p.parse_generic_type_list() + // println('interface decl $interface_name') + p.check(.lcbr) + pre_comments := p.eat_comments() + if modless_name in p.imported_symbols { + p.error_with_pos('cannot register interface `$interface_name`, this type was already imported', + name_pos) + return ast.InterfaceDecl{} + } + // Declare the type + reg_idx := p.table.register_type_symbol( + is_public: is_pub + kind: .interface_ + name: interface_name + cname: util.no_dots(interface_name) + mod: p.mod + info: ast.Interface{ + types: [] + is_generic: generic_types.len > 0 + generic_types: generic_types + } + ) + if reg_idx == -1 { + p.error_with_pos('cannot register interface `$interface_name`, another type with this name exists', + name_pos) + return ast.InterfaceDecl{} + } + typ := ast.new_type(reg_idx) + mut ts := p.table.get_type_symbol(typ) + mut info := ts.info as ast.Interface + // if methods were declared before, it's an error, ignore them + ts.methods = []ast.Fn{cap: 20} + // Parse fields or methods + mut fields := []ast.StructField{cap: 20} + mut methods := []ast.FnDecl{cap: 20} + mut is_mut := false + mut mut_pos := -1 + mut ifaces := []ast.InterfaceEmbedding{} + for p.tok.kind != .rcbr && p.tok.kind != .eof { + if p.tok.kind == .name && p.tok.lit.len > 0 && p.tok.lit[0].is_capital() { + iface_pos := p.tok.position() + iface_name := p.tok.lit + iface_type := p.parse_type() + comments := p.eat_comments() + ifaces << ast.InterfaceEmbedding{ + name: iface_name + typ: iface_type + pos: iface_pos + comments: comments + } + if p.tok.kind == .rcbr { + break + } + continue + } + if p.tok.kind == .key_mut { + if is_mut { + p.error_with_pos('redefinition of `mut` section', p.tok.position()) + return ast.InterfaceDecl{} + } + p.next() + p.check(.colon) + is_mut = true + mut_pos = fields.len + } + if p.peek_tok.kind == .lpar { + method_start_pos := p.tok.position() + line_nr := p.tok.line_nr + name := p.check_name() + + if name == 'type_name' { + p.error_with_pos('cannot override built-in method `type_name`', method_start_pos) + return ast.InterfaceDecl{} + } + if ts.has_method(name) { + p.error_with_pos('duplicate method `$name`', method_start_pos) + return ast.InterfaceDecl{} + } + if language == .v && util.contains_capital(name) { + p.error('interface methods cannot contain uppercase letters, use snake_case instead') + return ast.InterfaceDecl{} + } + // field_names << name + args2, _, is_variadic := p.fn_args() // TODO merge ast.Param and ast.Arg to avoid this + mut args := [ast.Param{ + name: 'x' + is_mut: is_mut + typ: typ + is_hidden: true + }] + args << args2 + mut method := ast.FnDecl{ + name: name + mod: p.mod + params: args + file: p.file_name + return_type: ast.void_type + is_variadic: is_variadic + is_pub: true + pos: method_start_pos.extend(p.prev_tok.position()) + scope: p.scope + } + if p.tok.kind.is_start_of_type() && p.tok.line_nr == line_nr { + method.return_type_pos = p.tok.position() + method.return_type = p.parse_type() + method.return_type_pos = method.return_type_pos.extend(p.tok.position()) + method.pos = method.pos.extend(method.return_type_pos) + } + mcomments := p.eat_comments(same_line: true) + mnext_comments := p.eat_comments() + method.comments = mcomments + method.next_comments = mnext_comments + methods << method + // println('register method $name') + tmethod := ast.Fn{ + name: name + params: args + pos: method.pos + return_type: method.return_type + is_variadic: is_variadic + is_pub: true + } + ts.register_method(tmethod) + info.methods << tmethod + } else { + // interface fields + field_pos := p.tok.position() + field_name := p.check_name() + mut type_pos := p.tok.position() + field_typ := p.parse_type() + type_pos = type_pos.extend(p.prev_tok.position()) + mut comments := []ast.Comment{} + for p.tok.kind == .comment { + comments << p.comment() + if p.tok.kind == .rcbr { + break + } + } + fields << ast.StructField{ + name: field_name + pos: field_pos + type_pos: type_pos + typ: field_typ + comments: comments + is_pub: true + } + info.fields << ast.StructField{ + name: field_name + typ: field_typ + is_pub: true + is_mut: is_mut + } + } + } + info.ifaces = ifaces.map(it.typ) + ts.info = info + p.top_level_statement_end() + p.check(.rcbr) + pos = pos.extend_with_last_line(p.prev_tok.position(), p.prev_tok.line_nr) + res := ast.InterfaceDecl{ + name: interface_name + language: language + typ: typ + fields: fields + methods: methods + ifaces: ifaces + is_pub: is_pub + pos: pos + pre_comments: pre_comments + generic_types: generic_types + mut_pos: mut_pos + name_pos: name_pos + } + p.table.register_interface(res) + return res +} diff --git a/v_windows/v/old/vlib/v/parser/tests/README.md b/v_windows/v/old/vlib/v/parser/tests/README.md new file mode 100644 index 0000000..7547a55 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/README.md @@ -0,0 +1 @@ +Put here tests, ensuring that the v's parser errors for certain situations. diff --git a/v_windows/v/old/vlib/v/parser/tests/anon_fn_return_type.out b/v_windows/v/old/vlib/v/parser/tests/anon_fn_return_type.out new file mode 100644 index 0000000..fe42d51 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/anon_fn_return_type.out @@ -0,0 +1,6 @@ +vlib/v/parser/tests/anon_fn_return_type.vv:7:22: error: expected return type, not string `hi` for anonymous function + 5 | _ = fn (name string) flag.Flag + 6 | _ = fn (name string) flag.Flag {return flag.Flag{}} + 7 | _ = fn (name string) "hi" + name + | ~~~~ + 8 | diff --git a/v_windows/v/old/vlib/v/parser/tests/anon_fn_return_type.vv b/v_windows/v/old/vlib/v/parser/tests/anon_fn_return_type.vv new file mode 100644 index 0000000..fc50fa0 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/anon_fn_return_type.vv @@ -0,0 +1,8 @@ +import flag + +_ = fn (name string) +_ = fn (name string) {} +_ = fn (name string) flag.Flag +_ = fn (name string) flag.Flag {return flag.Flag{}} +_ = fn (name string) "hi" + name + diff --git a/v_windows/v/old/vlib/v/parser/tests/anon_unused_param.out b/v_windows/v/old/vlib/v/parser/tests/anon_unused_param.out new file mode 100644 index 0000000..a12769b --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/anon_unused_param.out @@ -0,0 +1,4 @@ +vlib/v/parser/tests/anon_unused_param.vv:1:9: error: use `_` to name an unused parameter + 1 | _ = fn (int){} + | ~~~ + 2 | diff --git a/v_windows/v/old/vlib/v/parser/tests/anon_unused_param.vv b/v_windows/v/old/vlib/v/parser/tests/anon_unused_param.vv new file mode 100644 index 0000000..c285cea --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/anon_unused_param.vv @@ -0,0 +1,2 @@ +_ = fn (int){} + diff --git a/v_windows/v/old/vlib/v/parser/tests/array_init.out b/v_windows/v/old/vlib/v/parser/tests/array_init.out new file mode 100644 index 0000000..75bdbe3 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/array_init.out @@ -0,0 +1,12 @@ +vlib/v/parser/tests/array_init.vv:2:7: warning: use `x := []Type{}` instead of `x := []Type` + 1 | fn main() { + 2 | _ := []int + | ~~~~~ + 3 | _ := [1]int + 4 | } +vlib/v/parser/tests/array_init.vv:3:7: warning: use e.g. `x := [1]Type{}` instead of `x := [1]Type` + 1 | fn main() { + 2 | _ := []int + 3 | _ := [1]int + | ~~~~~~ + 4 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/array_init.vv b/v_windows/v/old/vlib/v/parser/tests/array_init.vv new file mode 100644 index 0000000..4bbfb88 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/array_init.vv @@ -0,0 +1,4 @@ +fn main() { + _ := []int + _ := [1]int +} diff --git a/v_windows/v/old/vlib/v/parser/tests/array_pos_err.out b/v_windows/v/old/vlib/v/parser/tests/array_pos_err.out new file mode 100644 index 0000000..8593054 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/array_pos_err.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/array_pos_err.vv:2:2: error: expression evaluated but not used + 1 | fn main() { + 2 | '' in [] + | ~~~~~~~~ + 3 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/parser/tests/array_pos_err.vv b/v_windows/v/old/vlib/v/parser/tests/array_pos_err.vv new file mode 100644 index 0000000..0988140 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/array_pos_err.vv @@ -0,0 +1,3 @@ +fn main() { + '' in [] +} \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/parser/tests/c_struct_no_embed.out b/v_windows/v/old/vlib/v/parser/tests/c_struct_no_embed.out new file mode 100644 index 0000000..89972cd --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/c_struct_no_embed.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/c_struct_no_embed.vv:7:1: error: expecting type declaration + 5 | struct C.Unknown { + 6 | Foo + 7 | } + | ^ \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/parser/tests/c_struct_no_embed.vv b/v_windows/v/old/vlib/v/parser/tests/c_struct_no_embed.vv new file mode 100644 index 0000000..020dce7 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/c_struct_no_embed.vv @@ -0,0 +1,7 @@ +struct Foo { + x int +} + +struct C.Unknown { + Foo +} \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/parser/tests/const_index.out b/v_windows/v/old/vlib/v/parser/tests/const_index.out new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/const_index.out @@ -0,0 +1 @@ + diff --git a/v_windows/v/old/vlib/v/parser/tests/const_index.vv b/v_windows/v/old/vlib/v/parser/tests/const_index.vv new file mode 100644 index 0000000..c629502 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/const_index.vv @@ -0,0 +1,22 @@ +// must not parse 4[deprecated] as index expression +const x = 4 +[deprecated] +fn g() { + a := [3] + // indexing is currently allowed on next line + _ = a + [0] +} + +const y = 5 +[deprecated] +fn h() {} + +const z = 6 +[typedef] +struct C.Foo{} + +// test implicit main allows indexing on next line +a := [3] +_ := a +[0] diff --git a/v_windows/v/old/vlib/v/parser/tests/const_missing_rpar_a.out b/v_windows/v/old/vlib/v/parser/tests/const_missing_rpar_a.out new file mode 100644 index 0000000..e3d0186 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/const_missing_rpar_a.out @@ -0,0 +1,3 @@ +vlib/v/parser/tests/const_missing_rpar_a.vv:3:1: error: unexpected eof, expecting ´)´ + 1 | const ( + 2 | a = 5 diff --git a/v_windows/v/old/vlib/v/parser/tests/const_missing_rpar_a.vv b/v_windows/v/old/vlib/v/parser/tests/const_missing_rpar_a.vv new file mode 100644 index 0000000..38043f9 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/const_missing_rpar_a.vv @@ -0,0 +1,2 @@ +const ( + a = 5 diff --git a/v_windows/v/old/vlib/v/parser/tests/const_missing_rpar_b.out b/v_windows/v/old/vlib/v/parser/tests/const_missing_rpar_b.out new file mode 100644 index 0000000..4bef87e --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/const_missing_rpar_b.out @@ -0,0 +1,3 @@ +vlib/v/parser/tests/const_missing_rpar_b.vv:4:1: error: unexpected eof, expecting ´)´ + 2 | a = 5 + 3 | // foo diff --git a/v_windows/v/old/vlib/v/parser/tests/const_missing_rpar_b.vv b/v_windows/v/old/vlib/v/parser/tests/const_missing_rpar_b.vv new file mode 100644 index 0000000..dbb66af --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/const_missing_rpar_b.vv @@ -0,0 +1,3 @@ +const ( + a = 5 + // foo diff --git a/v_windows/v/old/vlib/v/parser/tests/const_only_keyword.out b/v_windows/v/old/vlib/v/parser/tests/const_only_keyword.out new file mode 100644 index 0000000..1e3de05 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/const_only_keyword.out @@ -0,0 +1,2 @@ +vlib/v/parser/tests/const_only_keyword.vv:2:1: error: unexpected eof, expecting name + 1 | const diff --git a/v_windows/v/old/vlib/v/parser/tests/const_only_keyword.vv b/v_windows/v/old/vlib/v/parser/tests/const_only_keyword.vv new file mode 100644 index 0000000..aaae4e1 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/const_only_keyword.vv @@ -0,0 +1 @@ +const diff --git a/v_windows/v/old/vlib/v/parser/tests/const_unexpected_eof.out b/v_windows/v/old/vlib/v/parser/tests/const_unexpected_eof.out new file mode 100644 index 0000000..8f90da6 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/const_unexpected_eof.out @@ -0,0 +1,2 @@ +vlib/v/parser/tests/const_unexpected_eof.vv:2:1: error: unexpected eof, expecting an expression + 1 | const a = diff --git a/v_windows/v/old/vlib/v/parser/tests/const_unexpected_eof.vv b/v_windows/v/old/vlib/v/parser/tests/const_unexpected_eof.vv new file mode 100644 index 0000000..6210a52 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/const_unexpected_eof.vv @@ -0,0 +1 @@ +const a = diff --git a/v_windows/v/old/vlib/v/parser/tests/dec_use_as_value.out b/v_windows/v/old/vlib/v/parser/tests/dec_use_as_value.out new file mode 100644 index 0000000..91587e1 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/dec_use_as_value.out @@ -0,0 +1,6 @@ +vlib/v/parser/tests/dec_use_as_value.vv:2:16: error: cannot use i-- as value + 1 | fn main() { + 2 | for i := 100; i--; i > 0 { + | ^ + 3 | } + 4 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/dec_use_as_value.vv b/v_windows/v/old/vlib/v/parser/tests/dec_use_as_value.vv new file mode 100644 index 0000000..4774070 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/dec_use_as_value.vv @@ -0,0 +1,4 @@ +fn main() { + for i := 100; i--; i > 0 { + } +} diff --git a/v_windows/v/old/vlib/v/parser/tests/defer_propagate.out b/v_windows/v/old/vlib/v/parser/tests/defer_propagate.out new file mode 100644 index 0000000..93dae37 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/defer_propagate.out @@ -0,0 +1,7 @@ +vlib/v/parser/tests/defer_propagate.vv:9:15: error: error propagation not allowed inside `defer` blocks + 7 | mut a := 0 + 8 | defer { + 9 | a = test1() ? + | ^ + 10 | } + 11 | return a diff --git a/v_windows/v/old/vlib/v/parser/tests/defer_propagate.vv b/v_windows/v/old/vlib/v/parser/tests/defer_propagate.vv new file mode 100644 index 0000000..ad92bb9 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/defer_propagate.vv @@ -0,0 +1,16 @@ +fn test1() ?int { + a := 3 + return a +} + +fn test2() ?int { + mut a := 0 + defer { + a = test1() ? + } + return a + +fn main() { + x := test2() or { -1 } + println(x) +} diff --git a/v_windows/v/old/vlib/v/parser/tests/defer_return.out b/v_windows/v/old/vlib/v/parser/tests/defer_return.out new file mode 100644 index 0000000..2debd10 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/defer_return.out @@ -0,0 +1,7 @@ +vlib/v/parser/tests/defer_return.vv:3:3: error: `return` not allowed inside `defer` block + 1 | fn main() { + 2 | defer { + 3 | return + | ~~~~~~ + 4 | } + 5 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/defer_return.vv b/v_windows/v/old/vlib/v/parser/tests/defer_return.vv new file mode 100644 index 0000000..0bcc740 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/defer_return.vv @@ -0,0 +1,5 @@ +fn main() { + defer { + return + } +} diff --git a/v_windows/v/old/vlib/v/parser/tests/defer_return2.out b/v_windows/v/old/vlib/v/parser/tests/defer_return2.out new file mode 100644 index 0000000..54628e4 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/defer_return2.out @@ -0,0 +1,7 @@ +vlib/v/parser/tests/defer_return2.vv:3:3: error: `return` not allowed inside `defer` block + 1 | fn test1() int { + 2 | defer { + 3 | return 12 + | ~~~~~~ + 4 | } + 5 | a := 3 diff --git a/v_windows/v/old/vlib/v/parser/tests/defer_return2.vv b/v_windows/v/old/vlib/v/parser/tests/defer_return2.vv new file mode 100644 index 0000000..82f62aa --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/defer_return2.vv @@ -0,0 +1,12 @@ +fn test1() int { + defer { + return 12 + } + a := 3 + return a +} + +fn main() { + x := test1() + println(x) +} diff --git a/v_windows/v/old/vlib/v/parser/tests/duplicate_field_embed_err.out b/v_windows/v/old/vlib/v/parser/tests/duplicate_field_embed_err.out new file mode 100644 index 0000000..7fc6311 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/duplicate_field_embed_err.out @@ -0,0 +1,6 @@ +vlib/v/parser/tests/duplicate_field_embed_err.vv:8:2: error: duplicate field `ModFileAndFolder` + 6 | struct Bar { + 7 | ModFileAndFolder + 8 | vmod.ModFileAndFolder + | ~~~~~~~~~~~~~~~~~~~~~ + 9 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/parser/tests/duplicate_field_embed_err.vv b/v_windows/v/old/vlib/v/parser/tests/duplicate_field_embed_err.vv new file mode 100644 index 0000000..d71112f --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/duplicate_field_embed_err.vv @@ -0,0 +1,9 @@ +import v.vmod + +struct ModFileAndFolder { + name int = 5 +} +struct Bar { + ModFileAndFolder + vmod.ModFileAndFolder +} diff --git a/v_windows/v/old/vlib/v/parser/tests/duplicate_type_a.out b/v_windows/v/old/vlib/v/parser/tests/duplicate_type_a.out new file mode 100644 index 0000000..a2324a4 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/duplicate_type_a.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/duplicate_type_a.vv:3:11: error: cannot register interface `Foo`, another type with this name exists + 1 | struct Foo {} + 2 | + 3 | interface Foo {} + | ~~~ diff --git a/v_windows/v/old/vlib/v/parser/tests/duplicate_type_a.vv b/v_windows/v/old/vlib/v/parser/tests/duplicate_type_a.vv new file mode 100644 index 0000000..f47419b --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/duplicate_type_a.vv @@ -0,0 +1,3 @@ +struct Foo {} + +interface Foo {} diff --git a/v_windows/v/old/vlib/v/parser/tests/duplicate_type_b.out b/v_windows/v/old/vlib/v/parser/tests/duplicate_type_b.out new file mode 100644 index 0000000..6831fda --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/duplicate_type_b.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/duplicate_type_b.vv:3:8: error: cannot register struct `Foo`, another type with this name exists + 1 | interface Foo {} + 2 | + 3 | struct Foo {} + | ~~~ diff --git a/v_windows/v/old/vlib/v/parser/tests/duplicate_type_b.vv b/v_windows/v/old/vlib/v/parser/tests/duplicate_type_b.vv new file mode 100644 index 0000000..52aa104 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/duplicate_type_b.vv @@ -0,0 +1,3 @@ +interface Foo {} + +struct Foo {} diff --git a/v_windows/v/old/vlib/v/parser/tests/duplicated_generic_err.out b/v_windows/v/old/vlib/v/parser/tests/duplicated_generic_err.out new file mode 100644 index 0000000..e151407 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/duplicated_generic_err.out @@ -0,0 +1,3 @@ +vlib/v/parser/tests/duplicated_generic_err.vv:1:12: error: duplicated generic parameter `A` + 1 | fn test() {} + | ^ diff --git a/v_windows/v/old/vlib/v/parser/tests/duplicated_generic_err.vv b/v_windows/v/old/vlib/v/parser/tests/duplicated_generic_err.vv new file mode 100644 index 0000000..4b8e345 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/duplicated_generic_err.vv @@ -0,0 +1 @@ +fn test() {} diff --git a/v_windows/v/old/vlib/v/parser/tests/empty_name_expr_err.out b/v_windows/v/old/vlib/v/parser/tests/empty_name_expr_err.out new file mode 100644 index 0000000..07cfb9b --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/empty_name_expr_err.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/empty_name_expr_err.vv:2:9: error: unexpected name `n` + 1 | fn main() { + 2 | return n ?( + | ^ + 3 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/empty_name_expr_err.vv b/v_windows/v/old/vlib/v/parser/tests/empty_name_expr_err.vv new file mode 100644 index 0000000..61d2740 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/empty_name_expr_err.vv @@ -0,0 +1,3 @@ +fn main() { + return n ?( +} diff --git a/v_windows/v/old/vlib/v/parser/tests/expected_type_enum_err.out b/v_windows/v/old/vlib/v/parser/tests/expected_type_enum_err.out new file mode 100644 index 0000000..e378cf7 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/expected_type_enum_err.out @@ -0,0 +1,6 @@ +vlib/v/parser/tests/expected_type_enum_err.vv:6:12: error: expected type is not an enum (`rune`) + 4 | + 5 | fn main() { + 6 | if `c` == .bar {} + | ~~~~ + 7 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/parser/tests/expected_type_enum_err.vv b/v_windows/v/old/vlib/v/parser/tests/expected_type_enum_err.vv new file mode 100644 index 0000000..7385f14 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/expected_type_enum_err.vv @@ -0,0 +1,7 @@ +module main + +enum Test { bar } + +fn main() { + if `c` == .bar {} +} diff --git a/v_windows/v/old/vlib/v/parser/tests/expecting_assign_type_alias.out b/v_windows/v/old/vlib/v/parser/tests/expecting_assign_type_alias.out new file mode 100644 index 0000000..d273ac5 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/expecting_assign_type_alias.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/expecting_assign_type_alias.vv:1:10: error: unexpected name `int`, expecting `=` + 1 | type Ttt int + | ~~~ + 2 | + 3 | fn main() {} \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/parser/tests/expecting_assign_type_alias.vv b/v_windows/v/old/vlib/v/parser/tests/expecting_assign_type_alias.vv new file mode 100644 index 0000000..441cc40 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/expecting_assign_type_alias.vv @@ -0,0 +1,3 @@ +type Ttt int + +fn main() {} diff --git a/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_a.out b/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_a.out new file mode 100644 index 0000000..e83390f --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_a.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/expr_evaluated_but_not_used_a.vv:2:2: error: expression evaluated but not used + 1 | fn main() { + 2 | 'hello' + | ~~~~~~~ + 3 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_a.vv b/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_a.vv new file mode 100644 index 0000000..c4b6515 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_a.vv @@ -0,0 +1,3 @@ +fn main() { + 'hello' +} \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_b.out b/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_b.out new file mode 100644 index 0000000..437f89a --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_b.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/expr_evaluated_but_not_used_b.vv:2:2: error: expression evaluated but not used + 1 | fn main() { + 2 | 22 + | ~~ + 3 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_b.vv b/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_b.vv new file mode 100644 index 0000000..cac3734 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_b.vv @@ -0,0 +1,3 @@ +fn main() { + 22 +} \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_c.out b/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_c.out new file mode 100644 index 0000000..f6dbc23 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_c.out @@ -0,0 +1,7 @@ +vlib/v/parser/tests/expr_evaluated_but_not_used_c.vv:3:5: error: expression evaluated but not used + 1 | fn main() { + 2 | a := 10 + 3 | `b` + | ~~~ + 4 | println(a) + 5 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_c.vv b/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_c.vv new file mode 100644 index 0000000..863ce2f --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_c.vv @@ -0,0 +1,5 @@ +fn main() { + a := 10 + `b` + println(a) +} \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_d.out b/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_d.out new file mode 100644 index 0000000..03639ea --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_d.out @@ -0,0 +1,6 @@ +vlib/v/parser/tests/expr_evaluated_but_not_used_d.vv:4:19: error: expression evaluated but not used + 2 | a := 1 + 3 | b := 2 + 4 | println(a*b), a+b + | ~~~ + 5 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_d.vv b/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_d.vv new file mode 100644 index 0000000..6dbacb7 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_d.vv @@ -0,0 +1,5 @@ +fn main() { + a := 1 + b := 2 + println(a*b), a+b +} diff --git a/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_e.out b/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_e.out new file mode 100644 index 0000000..c312124 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_e.out @@ -0,0 +1,7 @@ +vlib/v/parser/tests/expr_evaluated_but_not_used_e.vv:3:14: error: expression evaluated but not used + 1 | fn main() { + 2 | mut array := [1, 2, 3] + 3 | array << 4, 5 + | ^ + 4 | println(array) + 5 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_e.vv b/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_e.vv new file mode 100644 index 0000000..763624a --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_e.vv @@ -0,0 +1,5 @@ +fn main() { + mut array := [1, 2, 3] + array << 4, 5 + println(array) +} diff --git a/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_if.out b/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_if.out new file mode 100644 index 0000000..f35579a --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_if.out @@ -0,0 +1,7 @@ +vlib/v/parser/tests/expr_evaluated_but_not_used_if.vv:3:5: error: expression evaluated but not used + 1 | fn main() { + 2 | if true { + 3 | 1 + 1 + | ~~~~~ + 4 | 1 + 1 + 5 | } else { diff --git a/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_if.vv b/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_if.vv new file mode 100644 index 0000000..8cd5a96 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_if.vv @@ -0,0 +1,8 @@ +fn main() { + if true { + 1 + 1 + 1 + 1 + } else { + 1 + 1 + } +} diff --git a/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_or.out b/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_or.out new file mode 100644 index 0000000..70b3e7e --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_or.out @@ -0,0 +1,7 @@ +vlib/v/parser/tests/expr_evaluated_but_not_used_or.vv:3:5: error: expression evaluated but not used + 1 | fn main() { + 2 | f() or { + 3 | 0 + | ^ + 4 | 1 + 5 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_or.vv b/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_or.vv new file mode 100644 index 0000000..4a3a82a --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/expr_evaluated_but_not_used_or.vv @@ -0,0 +1,10 @@ +fn main() { + f() or { + 0 + 1 + } +} + +fn f() ?int { + return none +} diff --git a/v_windows/v/old/vlib/v/parser/tests/fn_attributes_duplicate_multiple.out b/v_windows/v/old/vlib/v/parser/tests/fn_attributes_duplicate_multiple.out new file mode 100644 index 0000000..c8015ca --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/fn_attributes_duplicate_multiple.out @@ -0,0 +1,6 @@ +vlib/v/parser/tests/fn_attributes_duplicate_multiple.vv:2:2: error: duplicate attribute `inline` + 1 | [inline] + 2 | [inline] + | ~~~~~~ + 3 | fn foo() {} + 4 | diff --git a/v_windows/v/old/vlib/v/parser/tests/fn_attributes_duplicate_multiple.vv b/v_windows/v/old/vlib/v/parser/tests/fn_attributes_duplicate_multiple.vv new file mode 100644 index 0000000..2e1f223 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/fn_attributes_duplicate_multiple.vv @@ -0,0 +1,7 @@ +[inline] +[inline] +fn foo() {} + +fn main() { + foo() +} \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/parser/tests/fn_attributes_duplicate_single.out b/v_windows/v/old/vlib/v/parser/tests/fn_attributes_duplicate_single.out new file mode 100644 index 0000000..34a90cd --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/fn_attributes_duplicate_single.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/fn_attributes_duplicate_single.vv:1:10: error: duplicate attribute `inline` + 1 | [inline; inline] + | ~~~~~~ + 2 | fn foo() {} + 3 | diff --git a/v_windows/v/old/vlib/v/parser/tests/fn_attributes_duplicate_single.vv b/v_windows/v/old/vlib/v/parser/tests/fn_attributes_duplicate_single.vv new file mode 100644 index 0000000..27d6f55 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/fn_attributes_duplicate_single.vv @@ -0,0 +1,6 @@ +[inline; inline] +fn foo() {} + +fn main() { + foo() +} \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/parser/tests/fn_attributes_empty_err.out b/v_windows/v/old/vlib/v/parser/tests/fn_attributes_empty_err.out new file mode 100644 index 0000000..d021aee --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/fn_attributes_empty_err.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/fn_attributes_empty_err.vv:1:1: error: attributes cannot be empty + 1 | [] fn tt() { + | ~~ + 2 | println('text') + 3 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/fn_attributes_empty_err.vv b/v_windows/v/old/vlib/v/parser/tests/fn_attributes_empty_err.vv new file mode 100644 index 0000000..fd584c2 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/fn_attributes_empty_err.vv @@ -0,0 +1,6 @@ +[] fn tt() { + println('text') +} +fn main() { + tt() +} \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/parser/tests/fn_decl_unexpected_eof.out b/v_windows/v/old/vlib/v/parser/tests/fn_decl_unexpected_eof.out new file mode 100644 index 0000000..aef33f6 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/fn_decl_unexpected_eof.out @@ -0,0 +1,3 @@ +vlib/v/parser/tests/fn_decl_unexpected_eof.vv:1:12: error: unexpected eof, expecting `}` + 1 | fn main() { + | ^ diff --git a/v_windows/v/old/vlib/v/parser/tests/fn_decl_unexpected_eof.vv b/v_windows/v/old/vlib/v/parser/tests/fn_decl_unexpected_eof.vv new file mode 100644 index 0000000..fd74173 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/fn_decl_unexpected_eof.vv @@ -0,0 +1 @@ +fn main() { \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/parser/tests/fn_type_only_args_in_interfaces.out b/v_windows/v/old/vlib/v/parser/tests/fn_type_only_args_in_interfaces.out new file mode 100644 index 0000000..acc1399 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/fn_type_only_args_in_interfaces.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/fn_type_only_args_in_interfaces.vv:22:1: error: `syntax_error` evaluated but not used + 20 | } + 21 | + 22 | syntax_error + | ~~~~~~~~~~~~ diff --git a/v_windows/v/old/vlib/v/parser/tests/fn_type_only_args_in_interfaces.vv b/v_windows/v/old/vlib/v/parser/tests/fn_type_only_args_in_interfaces.vv new file mode 100644 index 0000000..0a3e839 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/fn_type_only_args_in_interfaces.vv @@ -0,0 +1,22 @@ + +struct Type1 {} +struct Type2 {} +struct Type3 {} + + +pub interface Widget1 { + init(Type1, Type2) +} + +pub interface Widget2 { + init(Type1) + draw(Type2, Type3) +} + +pub interface Widget3 { + fnoparams1() + fnoparams2() + draw(Type1, Type2) +} + +syntax_error diff --git a/v_windows/v/old/vlib/v/parser/tests/fn_type_only_args_no_body.out b/v_windows/v/old/vlib/v/parser/tests/fn_type_only_args_no_body.out new file mode 100644 index 0000000..fad0819 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/fn_type_only_args_no_body.out @@ -0,0 +1,6 @@ +vlib/v/parser/tests/fn_type_only_args_no_body.vv:2:1: error: functions with type only args can not have bodies + 1 | fn test(int) { + 2 | } + | ^ + 3 | + 4 | fn main() { diff --git a/v_windows/v/old/vlib/v/parser/tests/fn_type_only_args_no_body.vv b/v_windows/v/old/vlib/v/parser/tests/fn_type_only_args_no_body.vv new file mode 100644 index 0000000..d661c15 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/fn_type_only_args_no_body.vv @@ -0,0 +1,6 @@ +fn test(int) { +} + +fn main() { + test(0) +} diff --git a/v_windows/v/old/vlib/v/parser/tests/fn_type_only_args_unknown_name.out b/v_windows/v/old/vlib/v/parser/tests/fn_type_only_args_unknown_name.out new file mode 100644 index 0000000..1f94768 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/fn_type_only_args_unknown_name.out @@ -0,0 +1,6 @@ +vlib/v/parser/tests/fn_type_only_args_unknown_name.vv:2:1: error: functions with type only args can not have bodies + 1 | fn test(param) { + 2 | } + | ^ + 3 | + 4 | fn main() { diff --git a/v_windows/v/old/vlib/v/parser/tests/fn_type_only_args_unknown_name.vv b/v_windows/v/old/vlib/v/parser/tests/fn_type_only_args_unknown_name.vv new file mode 100644 index 0000000..7bb6b78 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/fn_type_only_args_unknown_name.vv @@ -0,0 +1,6 @@ +fn test(param) { +} + +fn main() { + test(0) +} diff --git a/v_windows/v/old/vlib/v/parser/tests/fn_use_builtin_err.out b/v_windows/v/old/vlib/v/parser/tests/fn_use_builtin_err.out new file mode 100644 index 0000000..5e57fe2 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/fn_use_builtin_err.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/fn_use_builtin_err.vv:1:4: error: cannot redefine builtin function `print` + 1 | fn print(strings ...string) { + | ~~~~~ + 2 | for s in strings { + 3 | println(s) diff --git a/v_windows/v/old/vlib/v/parser/tests/fn_use_builtin_err.vv b/v_windows/v/old/vlib/v/parser/tests/fn_use_builtin_err.vv new file mode 100644 index 0000000..1466e4c --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/fn_use_builtin_err.vv @@ -0,0 +1,9 @@ +fn print(strings ...string) { + for s in strings { + println(s) + } +} + +fn main() { + print('text') +} diff --git a/v_windows/v/old/vlib/v/parser/tests/for.out b/v_windows/v/old/vlib/v/parser/tests/for.out new file mode 100644 index 0000000..acc089b --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/for.out @@ -0,0 +1,4 @@ +vlib/v/parser/tests/for.vv:1:5: error: cannot declare index variable with range `for` + 1 | for i, k in 0..5 { + | ^ + 2 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/for.vv b/v_windows/v/old/vlib/v/parser/tests/for.vv new file mode 100644 index 0000000..1a48f69 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/for.vv @@ -0,0 +1,2 @@ +for i, k in 0..5 { +} diff --git a/v_windows/v/old/vlib/v/parser/tests/for_in_mut_index_of_array.out b/v_windows/v/old/vlib/v/parser/tests/for_in_mut_index_of_array.out new file mode 100644 index 0000000..f8e5a51 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/for_in_mut_index_of_array.out @@ -0,0 +1,7 @@ +vlib/v/parser/tests/for_in_mut_index_of_array.vv:3:6: error: index of array or key of map cannot be mutated + 1 | fn main() { + 2 | mut m := [1, 2, 3] + 3 | for mut i, _ in m { + | ~~~ + 4 | println(i) + 5 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/for_in_mut_index_of_array.vv b/v_windows/v/old/vlib/v/parser/tests/for_in_mut_index_of_array.vv new file mode 100644 index 0000000..eb04d96 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/for_in_mut_index_of_array.vv @@ -0,0 +1,6 @@ +fn main() { + mut m := [1, 2, 3] + for mut i, _ in m { + println(i) + } +} diff --git a/v_windows/v/old/vlib/v/parser/tests/for_in_mut_key_of_map.out b/v_windows/v/old/vlib/v/parser/tests/for_in_mut_key_of_map.out new file mode 100644 index 0000000..6737a53 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/for_in_mut_key_of_map.out @@ -0,0 +1,7 @@ +vlib/v/parser/tests/for_in_mut_key_of_map.vv:3:6: error: index of array or key of map cannot be mutated + 1 | fn main() { + 2 | mut m := map{'foo': 1, 'bar': 2} + 3 | for mut k, _ in m { + | ~~~ + 4 | println(k) + 5 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/for_in_mut_key_of_map.vv b/v_windows/v/old/vlib/v/parser/tests/for_in_mut_key_of_map.vv new file mode 100644 index 0000000..c8a2296 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/for_in_mut_key_of_map.vv @@ -0,0 +1,6 @@ +fn main() { + mut m := map{'foo': 1, 'bar': 2} + for mut k, _ in m { + println(k) + } +} diff --git a/v_windows/v/old/vlib/v/parser/tests/function_variadic_arg_non_final.out b/v_windows/v/old/vlib/v/parser/tests/function_variadic_arg_non_final.out new file mode 100644 index 0000000..7c69a4b --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/function_variadic_arg_non_final.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/function_variadic_arg_non_final.vv:1:6: error: cannot use ...(variadic) with non-final parameter para1 + 1 | fn f(para1 ...int, para2 f32) int { + | ~~~~~ + 2 | return 22 + 3 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/function_variadic_arg_non_final.vv b/v_windows/v/old/vlib/v/parser/tests/function_variadic_arg_non_final.vv new file mode 100644 index 0000000..f7ce72a --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/function_variadic_arg_non_final.vv @@ -0,0 +1,7 @@ +fn f(para1 ...int, para2 f32) int { + return 22 +} + +fn main() { + a := f(11, 1.1) +} diff --git a/v_windows/v/old/vlib/v/parser/tests/generic_lowercase_err.out b/v_windows/v/old/vlib/v/parser/tests/generic_lowercase_err.out new file mode 100644 index 0000000..3a02413 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/generic_lowercase_err.out @@ -0,0 +1,3 @@ +vlib/v/parser/tests/generic_lowercase_err.vv:1:22: error: generic parameter needs to be uppercase + 1 | fn lowercase_generic() {} + | ^ diff --git a/v_windows/v/old/vlib/v/parser/tests/generic_lowercase_err.vv b/v_windows/v/old/vlib/v/parser/tests/generic_lowercase_err.vv new file mode 100644 index 0000000..4d9bcad --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/generic_lowercase_err.vv @@ -0,0 +1 @@ +fn lowercase_generic() {} diff --git a/v_windows/v/old/vlib/v/parser/tests/generic_type_alias_decl.out b/v_windows/v/old/vlib/v/parser/tests/generic_type_alias_decl.out new file mode 100644 index 0000000..95c9e02 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/generic_type_alias_decl.out @@ -0,0 +1,3 @@ +vlib/v/parser/tests/generic_type_alias_decl.vv:1:1: error: generic type aliases are not yet implemented + 1 | type Pointer = &T + | ~~~~~~~~~~~~~~~ diff --git a/v_windows/v/old/vlib/v/parser/tests/generic_type_alias_decl.vv b/v_windows/v/old/vlib/v/parser/tests/generic_type_alias_decl.vv new file mode 100644 index 0000000..b2a177a --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/generic_type_alias_decl.vv @@ -0,0 +1 @@ +type Pointer = &T diff --git a/v_windows/v/old/vlib/v/parser/tests/if_guard_redefinition.out b/v_windows/v/old/vlib/v/parser/tests/if_guard_redefinition.out new file mode 100644 index 0000000..7f2a5b2 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/if_guard_redefinition.out @@ -0,0 +1,7 @@ +vlib/v/parser/tests/if_guard_redefinition.vv:7:8: error: redefinition of `x` + 5 | fn main() { + 6 | x := 1 + 7 | if x := opt_fn() { + | ^ + 8 | println(x) + 9 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/if_guard_redefinition.vv b/v_windows/v/old/vlib/v/parser/tests/if_guard_redefinition.vv new file mode 100644 index 0000000..b54ee90 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/if_guard_redefinition.vv @@ -0,0 +1,10 @@ +fn opt_fn() ?int { + return 2 +} + +fn main() { + x := 1 + if x := opt_fn() { + println(x) + } +} diff --git a/v_windows/v/old/vlib/v/parser/tests/inc_use_as_value.out b/v_windows/v/old/vlib/v/parser/tests/inc_use_as_value.out new file mode 100644 index 0000000..11888f8 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/inc_use_as_value.out @@ -0,0 +1,6 @@ +vlib/v/parser/tests/inc_use_as_value.vv:2:14: error: cannot use i++ as value + 1 | fn main() { + 2 | for i := 0; i++; i < 100 { + | ^ + 3 | } + 4 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/inc_use_as_value.vv b/v_windows/v/old/vlib/v/parser/tests/inc_use_as_value.vv new file mode 100644 index 0000000..885f6b7 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/inc_use_as_value.vv @@ -0,0 +1,4 @@ +fn main() { + for i := 0; i++; i < 100 { + } +} diff --git a/v_windows/v/old/vlib/v/parser/tests/interface_duplicate_interface_method.out b/v_windows/v/old/vlib/v/parser/tests/interface_duplicate_interface_method.out new file mode 100644 index 0000000..eaee38e --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/interface_duplicate_interface_method.out @@ -0,0 +1,6 @@ +vlib/v/parser/tests/interface_duplicate_interface_method.vv:4:2: error: duplicate method `fun` + 2 | interface Abc { + 3 | fun() + 4 | fun() + | ~~~ + 5 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/interface_duplicate_interface_method.vv b/v_windows/v/old/vlib/v/parser/tests/interface_duplicate_interface_method.vv new file mode 100644 index 0000000..4d29383 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/interface_duplicate_interface_method.vv @@ -0,0 +1,5 @@ +// duplicate interface methods in decleration +interface Abc { + fun() + fun() +} diff --git a/v_windows/v/old/vlib/v/parser/tests/interface_duplicate_method.out b/v_windows/v/old/vlib/v/parser/tests/interface_duplicate_method.out new file mode 100644 index 0000000..f83ad2a --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/interface_duplicate_method.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/interface_duplicate_method.vv:5:12: error: duplicate method `foo` + 3 | // duplicate normal method definitions on interface + 4 | fn (a Abc) foo() {} + 5 | fn (a Abc) foo() {} + | ~~~ diff --git a/v_windows/v/old/vlib/v/parser/tests/interface_duplicate_method.vv b/v_windows/v/old/vlib/v/parser/tests/interface_duplicate_method.vv new file mode 100644 index 0000000..ab22330 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/interface_duplicate_method.vv @@ -0,0 +1,5 @@ +interface Abc {} + +// duplicate normal method definitions on interface +fn (a Abc) foo() {} +fn (a Abc) foo() {} \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/parser/tests/invalid_attribute_a.out b/v_windows/v/old/vlib/v/parser/tests/invalid_attribute_a.out new file mode 100644 index 0000000..7ae507c --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/invalid_attribute_a.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/invalid_attribute_a.vv:1:9: error: unexpected token `]`, an argument is expected after `:` + 1 | [foobar:] + | ^ + 2 | fn my_fn_with_invalid_attr() { + 3 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/invalid_attribute_a.vv b/v_windows/v/old/vlib/v/parser/tests/invalid_attribute_a.vv new file mode 100644 index 0000000..971dc28 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/invalid_attribute_a.vv @@ -0,0 +1,3 @@ +[foobar:] +fn my_fn_with_invalid_attr() { +} diff --git a/v_windows/v/old/vlib/v/parser/tests/invalid_attribute_b.out b/v_windows/v/old/vlib/v/parser/tests/invalid_attribute_b.out new file mode 100644 index 0000000..763de97 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/invalid_attribute_b.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/invalid_attribute_b.vv:1:6: error: unexpected token `:`, an argument is expected after `:` + 1 | [foo::] + | ^ + 2 | fn my_fn_with_invalid_attr() { + 3 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/invalid_attribute_b.vv b/v_windows/v/old/vlib/v/parser/tests/invalid_attribute_b.vv new file mode 100644 index 0000000..d6af445 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/invalid_attribute_b.vv @@ -0,0 +1,3 @@ +[foo::] +fn my_fn_with_invalid_attr() { +} diff --git a/v_windows/v/old/vlib/v/parser/tests/invalid_attribute_c.out b/v_windows/v/old/vlib/v/parser/tests/invalid_attribute_c.out new file mode 100644 index 0000000..a30cb42 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/invalid_attribute_c.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/invalid_attribute_c.vv:1:6: error: unexpected token `[`, an argument is expected after `:` + 1 | [bar:[] + | ^ + 2 | fn my_fn_with_invalid_attr() { + 3 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/invalid_attribute_c.vv b/v_windows/v/old/vlib/v/parser/tests/invalid_attribute_c.vv new file mode 100644 index 0000000..527076d --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/invalid_attribute_c.vv @@ -0,0 +1,3 @@ +[bar:[] +fn my_fn_with_invalid_attr() { +} diff --git a/v_windows/v/old/vlib/v/parser/tests/invalid_attribute_d.out b/v_windows/v/old/vlib/v/parser/tests/invalid_attribute_d.out new file mode 100644 index 0000000..3d05dc2 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/invalid_attribute_d.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/invalid_attribute_d.vv:1:9: error: unexpected token `}`, an argument is expected after `:` + 1 | [foobar:} + | ^ + 2 | fn my_fn_with_invalid_attr() { + 3 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/invalid_attribute_d.vv b/v_windows/v/old/vlib/v/parser/tests/invalid_attribute_d.vv new file mode 100644 index 0000000..0b7d2e2 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/invalid_attribute_d.vv @@ -0,0 +1,3 @@ +[foobar:} +fn my_fn_with_invalid_attr() { +} diff --git a/v_windows/v/old/vlib/v/parser/tests/invalid_fn_decl_script_err.out b/v_windows/v/old/vlib/v/parser/tests/invalid_fn_decl_script_err.out new file mode 100644 index 0000000..aead2d0 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/invalid_fn_decl_script_err.out @@ -0,0 +1,7 @@ +vlib/v/parser/tests/invalid_fn_decl_script_err.vv:3:4: error: function declarations in script mode should be before all script statements + 1 | mynum := 10 + 2 | + 3 | fn main() { + | ~~~~ + 4 | println(mynum) + 5 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/parser/tests/invalid_fn_decl_script_err.vv b/v_windows/v/old/vlib/v/parser/tests/invalid_fn_decl_script_err.vv new file mode 100644 index 0000000..196895a --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/invalid_fn_decl_script_err.vv @@ -0,0 +1,5 @@ +mynum := 10 + +fn main() { + println(mynum) +} diff --git a/v_windows/v/old/vlib/v/parser/tests/invalid_recursive_struct1_err.out b/v_windows/v/old/vlib/v/parser/tests/invalid_recursive_struct1_err.out new file mode 100644 index 0000000..8cac758 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/invalid_recursive_struct1_err.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/invalid_recursive_struct1_err.vv:1:8: error: invalid recursive struct `Human` + 1 | struct Human { + | ~~~~~ + 2 | child Human + 3 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/parser/tests/invalid_recursive_struct1_err.vv b/v_windows/v/old/vlib/v/parser/tests/invalid_recursive_struct1_err.vv new file mode 100644 index 0000000..649ca81 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/invalid_recursive_struct1_err.vv @@ -0,0 +1,3 @@ +struct Human { + child Human +} diff --git a/v_windows/v/old/vlib/v/parser/tests/invalid_recursive_struct2_err.out b/v_windows/v/old/vlib/v/parser/tests/invalid_recursive_struct2_err.out new file mode 100644 index 0000000..d784e50 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/invalid_recursive_struct2_err.out @@ -0,0 +1,7 @@ +vlib/v/parser/tests/invalid_recursive_struct2_err.vv:5:8: error: invalid recursive struct `Human` + 3 | } + 4 | + 5 | struct Human { + | ~~~~~ + 6 | child Child + 7 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/parser/tests/invalid_recursive_struct2_err.vv b/v_windows/v/old/vlib/v/parser/tests/invalid_recursive_struct2_err.vv new file mode 100644 index 0000000..0088c8e --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/invalid_recursive_struct2_err.vv @@ -0,0 +1,7 @@ +struct Child { + be Human +} + +struct Human { + child Child +} diff --git a/v_windows/v/old/vlib/v/parser/tests/long_generic_err.out b/v_windows/v/old/vlib/v/parser/tests/long_generic_err.out new file mode 100644 index 0000000..d27f9f0 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/long_generic_err.out @@ -0,0 +1,3 @@ +vlib/v/parser/tests/long_generic_err.vv:1:17: error: generic parameter name needs to be exactly one char + 1 | fn long_generic() {} + | ~~~ diff --git a/v_windows/v/old/vlib/v/parser/tests/long_generic_err.vv b/v_windows/v/old/vlib/v/parser/tests/long_generic_err.vv new file mode 100644 index 0000000..ae3e538 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/long_generic_err.vv @@ -0,0 +1 @@ +fn long_generic() {} diff --git a/v_windows/v/old/vlib/v/parser/tests/map_init.out b/v_windows/v/old/vlib/v/parser/tests/map_init.out new file mode 100644 index 0000000..7185266 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/map_init.out @@ -0,0 +1,6 @@ +vlib/v/parser/tests/map_init.vv:3:22: error: `}` expected; explicit `map` initialization does not support parameters + 1 | fn main() { + 2 | a := map[string]int{} + 3 | b := map[string]f64{cap: 10} + | ~~~ + 4 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/map_init.vv b/v_windows/v/old/vlib/v/parser/tests/map_init.vv new file mode 100644 index 0000000..2ab972d --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/map_init.vv @@ -0,0 +1,4 @@ +fn main() { + a := map[string]int{} + b := map[string]f64{cap: 10} +} diff --git a/v_windows/v/old/vlib/v/parser/tests/map_init_void.out b/v_windows/v/old/vlib/v/parser/tests/map_init_void.out new file mode 100644 index 0000000..fd76d7e --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/map_init_void.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/map_init_void.vv:2:18: error: map value type cannot be void + 1 | fn main() { + 2 | m := map[string]{} + | ^ + 3 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/map_init_void.vv b/v_windows/v/old/vlib/v/parser/tests/map_init_void.vv new file mode 100644 index 0000000..5ad2727 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/map_init_void.vv @@ -0,0 +1,3 @@ +fn main() { + m := map[string]{} +} diff --git a/v_windows/v/old/vlib/v/parser/tests/map_init_void2.out b/v_windows/v/old/vlib/v/parser/tests/map_init_void2.out new file mode 100644 index 0000000..81031dc --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/map_init_void2.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/map_init_void2.vv:1:19: error: expecting type declaration + 1 | fn f(m map[string]) { + | ^ + 2 | println('illegal function') + 3 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/map_init_void2.vv b/v_windows/v/old/vlib/v/parser/tests/map_init_void2.vv new file mode 100644 index 0000000..0b9a8e4 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/map_init_void2.vv @@ -0,0 +1,7 @@ +fn f(m map[string]) { + println('illegal function') +} + +fn main() { + println('Hello world') +} diff --git a/v_windows/v/old/vlib/v/parser/tests/match_range_dotdot_err.out b/v_windows/v/old/vlib/v/parser/tests/match_range_dotdot_err.out new file mode 100644 index 0000000..bc815ed --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/match_range_dotdot_err.out @@ -0,0 +1,7 @@ +vlib/v/parser/tests/match_range_dotdot_err.vv:3:4: error: match only supports inclusive (`...`) ranges, not exclusive (`..`) + 1 | fn test_match() { + 2 | match 5 { + 3 | 0..10 { '0-9' } + | ~~ + 4 | else { 'other' } + 5 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/match_range_dotdot_err.vv b/v_windows/v/old/vlib/v/parser/tests/match_range_dotdot_err.vv new file mode 100644 index 0000000..0c7f112 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/match_range_dotdot_err.vv @@ -0,0 +1,6 @@ +fn test_match() { + match 5 { + 0..10 { '0-9' } + else { 'other' } + } +} diff --git a/v_windows/v/old/vlib/v/parser/tests/method_decl_on_non_local_array.out b/v_windows/v/old/vlib/v/parser/tests/method_decl_on_non_local_array.out new file mode 100644 index 0000000..f01fc00 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/method_decl_on_non_local_array.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/method_decl_on_non_local_array.vv:1:7: error: cannot define new methods on non-local type []int + 1 | fn (a []int) get_number() int { + | ~~~~~ + 2 | return 1 + 3 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/method_decl_on_non_local_array.vv b/v_windows/v/old/vlib/v/parser/tests/method_decl_on_non_local_array.vv new file mode 100644 index 0000000..9822ce5 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/method_decl_on_non_local_array.vv @@ -0,0 +1,3 @@ +fn (a []int) get_number() int { + return 1 +} diff --git a/v_windows/v/old/vlib/v/parser/tests/method_decl_on_non_local_map.out b/v_windows/v/old/vlib/v/parser/tests/method_decl_on_non_local_map.out new file mode 100644 index 0000000..d8459da --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/method_decl_on_non_local_map.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/method_decl_on_non_local_map.vv:1:7: error: cannot define new methods on non-local type map[string]string + 1 | fn (a map[string]string) get_number() int { + | ~~~~~~~~~~~~~~~~~ + 2 | return 1 + 3 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/method_decl_on_non_local_map.vv b/v_windows/v/old/vlib/v/parser/tests/method_decl_on_non_local_map.vv new file mode 100644 index 0000000..f95e719 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/method_decl_on_non_local_map.vv @@ -0,0 +1,3 @@ +fn (a map[string]string) get_number() int { + return 1 +} diff --git a/v_windows/v/old/vlib/v/parser/tests/method_decl_on_non_local_type.out b/v_windows/v/old/vlib/v/parser/tests/method_decl_on_non_local_type.out new file mode 100644 index 0000000..1b57bbd --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/method_decl_on_non_local_type.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/method_decl_on_non_local_type.vv:1:7: error: cannot define new methods on non-local type int + 1 | fn (a int) get_number() int { + | ~~~ + 2 | return 1 + 3 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/method_decl_on_non_local_type.vv b/v_windows/v/old/vlib/v/parser/tests/method_decl_on_non_local_type.vv new file mode 100644 index 0000000..f75565b --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/method_decl_on_non_local_type.vv @@ -0,0 +1,3 @@ +fn (a int) get_number() int { + return 1 +} diff --git a/v_windows/v/old/vlib/v/parser/tests/module_multiple_names_err.out b/v_windows/v/old/vlib/v/parser/tests/module_multiple_names_err.out new file mode 100644 index 0000000..6ae53a8 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/module_multiple_names_err.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/module_multiple_names_err.vv:1:13: error: `module main`, you can only declare one module, unexpected `os` + 1 | module main os + | ~~ + 2 | fn main() { + 3 | println('hello, world') diff --git a/v_windows/v/old/vlib/v/parser/tests/module_multiple_names_err.vv b/v_windows/v/old/vlib/v/parser/tests/module_multiple_names_err.vv new file mode 100644 index 0000000..96158af --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/module_multiple_names_err.vv @@ -0,0 +1,4 @@ +module main os +fn main() { + println('hello, world') +} diff --git a/v_windows/v/old/vlib/v/parser/tests/module_syntax_err.out b/v_windows/v/old/vlib/v/parser/tests/module_syntax_err.out new file mode 100644 index 0000000..38a1905 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/module_syntax_err.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/module_syntax_err.vv:1:12: error: `module main`, unexpected `.` after module name + 1 | module main.os + | ^ + 2 | fn main() { + 3 | println('hello, world') diff --git a/v_windows/v/old/vlib/v/parser/tests/module_syntax_err.vv b/v_windows/v/old/vlib/v/parser/tests/module_syntax_err.vv new file mode 100644 index 0000000..8f8c91f --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/module_syntax_err.vv @@ -0,0 +1,4 @@ +module main.os +fn main() { + println('hello, world') +} diff --git a/v_windows/v/old/vlib/v/parser/tests/multi_argumented_assign_err.out b/v_windows/v/old/vlib/v/parser/tests/multi_argumented_assign_err.out new file mode 100644 index 0000000..4e1a64c --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/multi_argumented_assign_err.out @@ -0,0 +1,7 @@ +vlib/v/parser/tests/multi_argumented_assign_err.vv:3:10: error: unexpected +=, expecting := or = or comma + 1 | fn main() { + 2 | mut a, mut b, mut c := 0,1, 2 + 3 | a, b, c += 1, 2, 4 + | ~~ + 4 | println('$a $b $c') + 5 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/multi_argumented_assign_err.vv b/v_windows/v/old/vlib/v/parser/tests/multi_argumented_assign_err.vv new file mode 100644 index 0000000..26257b8 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/multi_argumented_assign_err.vv @@ -0,0 +1,5 @@ +fn main() { + mut a, mut b, mut c := 0,1, 2 + a, b, c += 1, 2, 4 + println('$a $b $c') +} diff --git a/v_windows/v/old/vlib/v/parser/tests/nested_defer.out b/v_windows/v/old/vlib/v/parser/tests/nested_defer.out new file mode 100644 index 0000000..c5da8dc --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/nested_defer.out @@ -0,0 +1,7 @@ +vlib/v/parser/tests/nested_defer.vv:5:3: error: `defer` blocks cannot be nested + 3 | defer { + 4 | a = 12 + 5 | defer { + | ~~~~~ + 6 | a = 13 + 7 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/nested_defer.vv b/v_windows/v/old/vlib/v/parser/tests/nested_defer.vv new file mode 100644 index 0000000..7abc2d3 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/nested_defer.vv @@ -0,0 +1,14 @@ +fn test1() int { + mut a := 0 + defer { + a = 12 + defer { + a = 13 + } + } + return a + +fn main() { + x := test1() + println(x) +} diff --git a/v_windows/v/old/vlib/v/parser/tests/nested_unsafe_expr.out b/v_windows/v/old/vlib/v/parser/tests/nested_unsafe_expr.out new file mode 100644 index 0000000..1d815ed --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/nested_unsafe_expr.out @@ -0,0 +1,7 @@ +vlib/v/parser/tests/nested_unsafe_expr.vv:4:8: error: already inside `unsafe` block + 2 | a := 0 + 3 | unsafe { + 4 | a += unsafe{2} + | ~~~~~~ + 5 | } + 6 | println(a) diff --git a/v_windows/v/old/vlib/v/parser/tests/nested_unsafe_expr.vv b/v_windows/v/old/vlib/v/parser/tests/nested_unsafe_expr.vv new file mode 100644 index 0000000..1ab098c --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/nested_unsafe_expr.vv @@ -0,0 +1,7 @@ +fn main() { + a := 0 + unsafe { + a += unsafe{2} + } + println(a) +} diff --git a/v_windows/v/old/vlib/v/parser/tests/nested_unsafe_stmt.out b/v_windows/v/old/vlib/v/parser/tests/nested_unsafe_stmt.out new file mode 100644 index 0000000..b76a6e5 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/nested_unsafe_stmt.out @@ -0,0 +1,7 @@ +vlib/v/parser/tests/nested_unsafe_stmt.vv:4:3: error: already inside `unsafe` block + 2 | a := 0 + 3 | unsafe { + 4 | unsafe { + | ~~~~~~ + 5 | a++ + 6 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/nested_unsafe_stmt.vv b/v_windows/v/old/vlib/v/parser/tests/nested_unsafe_stmt.vv new file mode 100644 index 0000000..283e76d --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/nested_unsafe_stmt.vv @@ -0,0 +1,9 @@ +fn main() { + a := 0 + unsafe { + unsafe { + a++ + } + } + println(a) +} diff --git a/v_windows/v/old/vlib/v/parser/tests/operator_normal_fn.out b/v_windows/v/old/vlib/v/parser/tests/operator_normal_fn.out new file mode 100644 index 0000000..9313c0a --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/operator_normal_fn.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/operator_normal_fn.vv:1:4: error: cannot use operator overloading with normal functions + 1 | fn +(x int) int { + | ^ + 2 | return 5 + x + 3 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/operator_normal_fn.vv b/v_windows/v/old/vlib/v/parser/tests/operator_normal_fn.vv new file mode 100644 index 0000000..5a82b31 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/operator_normal_fn.vv @@ -0,0 +1,3 @@ +fn +(x int) int { + return 5 + x +} diff --git a/v_windows/v/old/vlib/v/parser/tests/or_default_missing.out b/v_windows/v/old/vlib/v/parser/tests/or_default_missing.out new file mode 100644 index 0000000..82cfa59 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/or_default_missing.out @@ -0,0 +1,14 @@ +vlib/v/parser/tests/or_default_missing.vv:4:3: error: `or` block must provide a default value of type `int`, or return/continue/break or call a [noreturn] function like panic(err) or exit(1) + 2 | m := [3, 4, 5] + 3 | el := m[4] or { + 4 | println('error') + | ~~~~~~~~~~~~~~~~ + 5 | } + 6 | println(el) +vlib/v/parser/tests/or_default_missing.vv:16:16: error: last statement in the `or {}` block should be an expression of type `int` or exit parent scope + 14 | } + 15 | mut testvar := 0 + 16 | el := m['pp'] or { + | ~~~~ + 17 | testvar = 12 + 18 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/or_default_missing.vv b/v_windows/v/old/vlib/v/parser/tests/or_default_missing.vv new file mode 100644 index 0000000..ad9fa62 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/or_default_missing.vv @@ -0,0 +1,20 @@ +fn test_array_or() { + m := [3, 4, 5] + el := m[4] or { + println('error') + } + println(el) +} + +fn test_map_or() { + m := map{ + 'as': 3 + 'qw': 4 + 'kl': 5 + } + mut testvar := 0 + el := m['pp'] or { + testvar = 12 + } + println('$el $testvar') +} diff --git a/v_windows/v/old/vlib/v/parser/tests/postfix_err.out b/v_windows/v/old/vlib/v/parser/tests/postfix_err.out new file mode 100644 index 0000000..d83ccfa --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/postfix_err.out @@ -0,0 +1,21 @@ +vlib/v/parser/tests/postfix_err.vv:5:10: warning: `++` operator can only be used as a statement + 3 | fn test_postfix() { + 4 | mut x := 1 + 5 | _ = (x++) + | ^ + 6 | x--, x-- // OK + 7 | f(x++) +vlib/v/parser/tests/postfix_err.vv:7:7: warning: `++` operator can only be used as a statement + 5 | _ = (x++) + 6 | x--, x-- // OK + 7 | f(x++) + | ^ + 8 | a := [x] + 9 | _ = a[x--] +vlib/v/parser/tests/postfix_err.vv:9:11: warning: `--` operator can only be used as a statement + 7 | f(x++) + 8 | a := [x] + 9 | _ = a[x--] + | ^ + 10 | } + 11 | diff --git a/v_windows/v/old/vlib/v/parser/tests/postfix_err.vv b/v_windows/v/old/vlib/v/parser/tests/postfix_err.vv new file mode 100644 index 0000000..a1ba568 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/postfix_err.vv @@ -0,0 +1,11 @@ +fn f(i int) {} + +fn test_postfix() { + mut x := 1 + _ = (x++) + x--, x-- // OK + f(x++) + a := [x] + _ = a[x--] +} + diff --git a/v_windows/v/old/vlib/v/parser/tests/postfix_inc.out b/v_windows/v/old/vlib/v/parser/tests/postfix_inc.out new file mode 100644 index 0000000..189a413 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/postfix_inc.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/postfix_inc.vv:3:1: error: token `++` must be on the same line as the previous token + 1 | mut v := 4 + 2 | _ = v + 3 | ++v + | ~~ diff --git a/v_windows/v/old/vlib/v/parser/tests/postfix_inc.vv b/v_windows/v/old/vlib/v/parser/tests/postfix_inc.vv new file mode 100644 index 0000000..3f2fd06 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/postfix_inc.vv @@ -0,0 +1,3 @@ +mut v := 4 +_ = v +++v diff --git a/v_windows/v/old/vlib/v/parser/tests/prefix_first.out b/v_windows/v/old/vlib/v/parser/tests/prefix_first.out new file mode 100644 index 0000000..3a4adc6 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/prefix_first.out @@ -0,0 +1,28 @@ +vlib/v/parser/tests/prefix_first.vv:15:3: warning: move infix `-` operator before new line (if infix intended) or use brackets for a prefix expression + 13 | _ = if true { + 14 | v = 1 + 15 | -1 + | ^ + 16 | } else {1} + 17 | _ = p +vlib/v/parser/tests/prefix_first.vv:27:3: warning: move infix `&` operator before new line (if infix intended) or use brackets for a prefix expression + 25 | _ = opt() or { + 26 | _ = 1 + 27 | &v + | ^ + 28 | } + 29 | } +vlib/v/parser/tests/prefix_first.vv:13:6: error: `if` expression requires an expression as the last statement of every branch + 11 | + 12 | // later this should compile correctly + 13 | _ = if true { + | ~~~~~~~ + 14 | v = 1 + 15 | -1 +vlib/v/parser/tests/prefix_first.vv:25:12: error: last statement in the `or {}` block should be an expression of type `&int` or exit parent scope + 23 | // later this should compile correctly + 24 | v := 3 + 25 | _ = opt() or { + | ~~~~ + 26 | _ = 1 + 27 | &v diff --git a/v_windows/v/old/vlib/v/parser/tests/prefix_first.vv b/v_windows/v/old/vlib/v/parser/tests/prefix_first.vv new file mode 100644 index 0000000..83e180c --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/prefix_first.vv @@ -0,0 +1,29 @@ +// a prefix op can be parsed as an infix op if there's an expression on the line before +// https://github.com/vlang/v/pull/6491 +fn test_prefix() { + mut v := 1 + mut p := &v + // OK, special workaround + unsafe { + v = 1 + *p = 2 + } + + // later this should compile correctly + _ = if true { + v = 1 + -1 + } else {1} + _ = p +} + +fn opt() ?&int {return none} + +fn test_prefix_or() { + // later this should compile correctly + v := 3 + _ = opt() or { + _ = 1 + &v + } +} diff --git a/v_windows/v/old/vlib/v/parser/tests/prohibit_redeclaration_of_builtin_types.out b/v_windows/v/old/vlib/v/parser/tests/prohibit_redeclaration_of_builtin_types.out new file mode 100644 index 0000000..e292e0a --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/prohibit_redeclaration_of_builtin_types.out @@ -0,0 +1,3 @@ +vlib/v/parser/tests/prohibit_redeclaration_of_builtin_types.vv:1:8: error: cannot register struct `Option`, another type with this name exists + 1 | struct Option {} + | ~~~~~~ diff --git a/v_windows/v/old/vlib/v/parser/tests/prohibit_redeclaration_of_builtin_types.vv b/v_windows/v/old/vlib/v/parser/tests/prohibit_redeclaration_of_builtin_types.vv new file mode 100644 index 0000000..26669cc --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/prohibit_redeclaration_of_builtin_types.vv @@ -0,0 +1 @@ +struct Option {} diff --git a/v_windows/v/old/vlib/v/parser/tests/redeclaration_of_imported_fn.out b/v_windows/v/old/vlib/v/parser/tests/redeclaration_of_imported_fn.out new file mode 100644 index 0000000..1c02758 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/redeclaration_of_imported_fn.out @@ -0,0 +1,7 @@ +vlib/v/parser/tests/redeclaration_of_imported_fn.vv:3:4: error: cannot redefine imported function `input` + 1 | import os { input } + 2 | + 3 | fn input() {} + | ~~~~~ + 4 | + 5 | input() diff --git a/v_windows/v/old/vlib/v/parser/tests/redeclaration_of_imported_fn.vv b/v_windows/v/old/vlib/v/parser/tests/redeclaration_of_imported_fn.vv new file mode 100644 index 0000000..45a3b5a --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/redeclaration_of_imported_fn.vv @@ -0,0 +1,5 @@ +import os { input } + +fn input() {} + +input() diff --git a/v_windows/v/old/vlib/v/parser/tests/register_imported_alias.out b/v_windows/v/old/vlib/v/parser/tests/register_imported_alias.out new file mode 100644 index 0000000..3cc0365 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/register_imported_alias.out @@ -0,0 +1,4 @@ +vlib/v/parser/tests/register_imported_alias.vv:2:6: error: cannot register alias `Duration`, this type was already imported + 1 | import time { Duration } + 2 | type Duration = bool + | ~~~~~~~~ diff --git a/v_windows/v/old/vlib/v/parser/tests/register_imported_alias.vv b/v_windows/v/old/vlib/v/parser/tests/register_imported_alias.vv new file mode 100644 index 0000000..0200e5d --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/register_imported_alias.vv @@ -0,0 +1,2 @@ +import time { Duration } +type Duration = bool diff --git a/v_windows/v/old/vlib/v/parser/tests/register_imported_enum.out b/v_windows/v/old/vlib/v/parser/tests/register_imported_enum.out new file mode 100644 index 0000000..89f888b --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/register_imported_enum.out @@ -0,0 +1,4 @@ +vlib/v/parser/tests/register_imported_enum.vv:2:6: error: cannot register enum `Method`, this type was already imported + 1 | import net.http { Method } + 2 | enum Method { foo bar } + | ~~~~~~ diff --git a/v_windows/v/old/vlib/v/parser/tests/register_imported_enum.vv b/v_windows/v/old/vlib/v/parser/tests/register_imported_enum.vv new file mode 100644 index 0000000..bb1bb6e --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/register_imported_enum.vv @@ -0,0 +1,2 @@ +import net.http { Method } +enum Method { foo bar } diff --git a/v_windows/v/old/vlib/v/parser/tests/register_imported_interface.out b/v_windows/v/old/vlib/v/parser/tests/register_imported_interface.out new file mode 100644 index 0000000..58b2029 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/register_imported_interface.out @@ -0,0 +1,4 @@ +vlib/v/parser/tests/register_imported_interface.vv:2:11: error: cannot register interface `Reader`, this type was already imported + 1 | import io { Reader } + 2 | interface Reader {} + | ~~~~~~ diff --git a/v_windows/v/old/vlib/v/parser/tests/register_imported_interface.vv b/v_windows/v/old/vlib/v/parser/tests/register_imported_interface.vv new file mode 100644 index 0000000..c659327 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/register_imported_interface.vv @@ -0,0 +1,2 @@ +import io { Reader } +interface Reader {} diff --git a/v_windows/v/old/vlib/v/parser/tests/register_imported_struct.out b/v_windows/v/old/vlib/v/parser/tests/register_imported_struct.out new file mode 100644 index 0000000..259b831 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/register_imported_struct.out @@ -0,0 +1,4 @@ +vlib/v/parser/tests/register_imported_struct.vv:2:8: error: cannot register struct `File`, this type was already imported + 1 | import os { File } + 2 | struct File {} + | ~~~~ diff --git a/v_windows/v/old/vlib/v/parser/tests/register_imported_struct.vv b/v_windows/v/old/vlib/v/parser/tests/register_imported_struct.vv new file mode 100644 index 0000000..a9c54db --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/register_imported_struct.vv @@ -0,0 +1,2 @@ +import os { File } +struct File {} diff --git a/v_windows/v/old/vlib/v/parser/tests/select_bad_key_1.out b/v_windows/v/old/vlib/v/parser/tests/select_bad_key_1.out new file mode 100644 index 0000000..a9f946c --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/select_bad_key_1.out @@ -0,0 +1,14 @@ +vlib/v/parser/tests/select_bad_key_1.vv:19:3: notice: `>` is deprecated and will soon be forbidden - just state the timeout in nanoseconds + 17 | a++ + 18 | } + 19 | > 50 * time.millisecond { + | ^ + 20 | println('timeout') + 21 | } +vlib/v/parser/tests/select_bad_key_1.vv:39:8: error: select key: receive expression expected + 37 | fn f3_bad(ch1 chan St) { + 38 | select { + 39 | b := 17 { + | ~~ + 40 | println(b) + 41 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/select_bad_key_1.vv b/v_windows/v/old/vlib/v/parser/tests/select_bad_key_1.vv new file mode 100644 index 0000000..3e9da5d --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/select_bad_key_1.vv @@ -0,0 +1,47 @@ +import time + +struct St { + a int +} + +fn f1_good(ch1 chan St, ch2 chan int, ch3 chan int) { + mut a := 5 + select { + a = <- ch3 { + println(a) + } + b := <- ch1 { + println(b.a) + } + ch1 <- a { + a++ + } + > 50 * time.millisecond { + println('timeout') + } + } + println('done') +} + +fn f2_good(ch1 chan St) { + select { + b := <- ch1 { + println(b) + } + else { + println('no channel ready') + } + } +} + +fn f3_bad(ch1 chan St) { + select { + b := 17 { + println(b) + } + } +} + +fn main() {} + + diff --git a/v_windows/v/old/vlib/v/parser/tests/select_bad_key_2.out b/v_windows/v/old/vlib/v/parser/tests/select_bad_key_2.out new file mode 100644 index 0000000..a7a6109 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/select_bad_key_2.out @@ -0,0 +1,7 @@ +vlib/v/parser/tests/select_bad_key_2.vv:7:3: error: invalid type `f64` for timeout - expected integer number of nanoseconds aka `time.Duration` + 5 | println(b) + 6 | } + 7 | a + 7 { + | ~~~~~ + 8 | println("shouldn't get here") + 9 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/select_bad_key_2.vv b/v_windows/v/old/vlib/v/parser/tests/select_bad_key_2.vv new file mode 100644 index 0000000..a3fcda8 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/select_bad_key_2.vv @@ -0,0 +1,13 @@ +fn f3_bad(ch1 chan int) { + a := 3.75 + select { + b := <-ch1 { + println(b) + } + a + 7 { + println("shouldn't get here") + } + } +} + +fn main() {} diff --git a/v_windows/v/old/vlib/v/parser/tests/select_bad_key_3.out b/v_windows/v/old/vlib/v/parser/tests/select_bad_key_3.out new file mode 100644 index 0000000..029add6 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/select_bad_key_3.out @@ -0,0 +1,7 @@ +vlib/v/parser/tests/select_bad_key_3.vv:6:3: error: invalid type `void` for timeout - expected integer number of nanoseconds aka `time.Duration` + 4 | println(b) + 5 | } + 6 | println(7) { + | ~~~~~~~~~~ + 7 | println("shouldn't get here") + 8 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/select_bad_key_3.vv b/v_windows/v/old/vlib/v/parser/tests/select_bad_key_3.vv new file mode 100644 index 0000000..c7bf3a6 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/select_bad_key_3.vv @@ -0,0 +1,12 @@ +fn f3_bad(ch1 chan int) { + select { + b := <-ch1 { + println(b) + } + println(7) { + println("shouldn't get here") + } + } +} + +fn main() {} diff --git a/v_windows/v/old/vlib/v/parser/tests/select_bad_key_4.out b/v_windows/v/old/vlib/v/parser/tests/select_bad_key_4.out new file mode 100644 index 0000000..3fac7dd --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/select_bad_key_4.out @@ -0,0 +1,7 @@ +vlib/v/parser/tests/select_bad_key_4.vv:7:8: error: select key: `<-` operator expected + 5 | println(b) + 6 | } + 7 | c := -a { + | ^ + 8 | println("shouldn't get here") + 9 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/select_bad_key_4.vv b/v_windows/v/old/vlib/v/parser/tests/select_bad_key_4.vv new file mode 100644 index 0000000..bee7402 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/select_bad_key_4.vv @@ -0,0 +1,13 @@ +fn f3_bad(ch1 chan int) { + mut a := 5 + select { + a = <-ch1 { + println(b) + } + c := -a { + println("shouldn't get here") + } + } +} + +fn main() {} diff --git a/v_windows/v/old/vlib/v/parser/tests/select_else_1.out b/v_windows/v/old/vlib/v/parser/tests/select_else_1.out new file mode 100644 index 0000000..9704fbb --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/select_else_1.out @@ -0,0 +1,7 @@ +vlib/v/parser/tests/select_else_1.vv:12:3: error: `else` and timeout value are mutually exclusive `select` keys + 10 | println("shouldn't get here") + 11 | } + 12 | 30 * time.millisecond { + | ~~~~~~~~~~~~~~~~~~~~~ + 13 | println('bad') + 14 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/select_else_1.vv b/v_windows/v/old/vlib/v/parser/tests/select_else_1.vv new file mode 100644 index 0000000..78077f4 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/select_else_1.vv @@ -0,0 +1,18 @@ +import time + +fn f3_bad(ch1 chan int) { + a := 5 + select { + b := <-ch1 { + println(b) + } + else { + println("shouldn't get here") + } + 30 * time.millisecond { + println('bad') + } + } +} + +fn main() {} diff --git a/v_windows/v/old/vlib/v/parser/tests/select_else_2.out b/v_windows/v/old/vlib/v/parser/tests/select_else_2.out new file mode 100644 index 0000000..f927031 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/select_else_2.out @@ -0,0 +1,14 @@ +vlib/v/parser/tests/select_else_2.vv:9:3: notice: `>` is deprecated and will soon be forbidden - just state the timeout in nanoseconds + 7 | println(b) + 8 | } + 9 | > 30 * time.millisecond { + | ^ + 10 | println('bad') + 11 | } +vlib/v/parser/tests/select_else_2.vv:12:3: error: timeout `> t` and `else` are mutually exclusive `select` keys + 10 | println('bad') + 11 | } + 12 | else { + | ~~~~ + 13 | println("shouldn't get here") + 14 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/select_else_2.vv b/v_windows/v/old/vlib/v/parser/tests/select_else_2.vv new file mode 100644 index 0000000..2666dbc --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/select_else_2.vv @@ -0,0 +1,18 @@ +import time + +fn f3_bad(ch1 chan int) { + a := 5 + select { + b := <-ch1 { + println(b) + } + > 30 * time.millisecond { + println('bad') + } + else { + println("shouldn't get here") + } + } +} + +fn main() {} diff --git a/v_windows/v/old/vlib/v/parser/tests/sql_no_db_expr_a.out b/v_windows/v/old/vlib/v/parser/tests/sql_no_db_expr_a.out new file mode 100644 index 0000000..41417ae --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/sql_no_db_expr_a.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/sql_no_db_expr_a.vv:4:1: error: invalid expression: unexpected token `}` + 2 | // SqlStmt + 3 | sql := + 4 | } + | ^ diff --git a/v_windows/v/old/vlib/v/parser/tests/sql_no_db_expr_a.vv b/v_windows/v/old/vlib/v/parser/tests/sql_no_db_expr_a.vv new file mode 100644 index 0000000..5181f2d --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/sql_no_db_expr_a.vv @@ -0,0 +1,4 @@ +fn x() { + // SqlStmt + sql := +} diff --git a/v_windows/v/old/vlib/v/parser/tests/sql_no_db_expr_b.out b/v_windows/v/old/vlib/v/parser/tests/sql_no_db_expr_b.out new file mode 100644 index 0000000..da86e9c --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/sql_no_db_expr_b.out @@ -0,0 +1,6 @@ +vlib/v/parser/tests/sql_no_db_expr_b.vv:3:11: error: invalid expression: unexpected token `:=` + 1 | fn x() { + 2 | // SqlExpr + 3 | x := sql := + | ~~ + 4 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/sql_no_db_expr_b.vv b/v_windows/v/old/vlib/v/parser/tests/sql_no_db_expr_b.vv new file mode 100644 index 0000000..fa36435 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/sql_no_db_expr_b.vv @@ -0,0 +1,4 @@ +fn x() { + // SqlExpr + x := sql := +} diff --git a/v_windows/v/old/vlib/v/parser/tests/string_invalid_prefix_err.out b/v_windows/v/old/vlib/v/parser/tests/string_invalid_prefix_err.out new file mode 100644 index 0000000..416d415 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/string_invalid_prefix_err.out @@ -0,0 +1,6 @@ +vlib/v/parser/tests/string_invalid_prefix_err.vv:2:12: error: only `c`, `r`, `js` are recognized string prefixes, but you tried to use `w` + 1 | fn main() { + 2 | why := w'why' + | ^ + 3 | println(why) + 4 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/string_invalid_prefix_err.vv b/v_windows/v/old/vlib/v/parser/tests/string_invalid_prefix_err.vv new file mode 100644 index 0000000..6c8630c --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/string_invalid_prefix_err.vv @@ -0,0 +1,4 @@ +fn main() { + why := w'why' + println(why) +} diff --git a/v_windows/v/old/vlib/v/parser/tests/struct_embed_duplicate.out b/v_windows/v/old/vlib/v/parser/tests/struct_embed_duplicate.out new file mode 100644 index 0000000..752ed81 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/struct_embed_duplicate.out @@ -0,0 +1,7 @@ +vlib/v/parser/tests/struct_embed_duplicate.vv:7:2: error: cannot embed `Abc` more than once + 5 | struct Xyz { + 6 | Abc + 7 | Abc + | ~~~ + 8 | bar int + 9 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/parser/tests/struct_embed_duplicate.vv b/v_windows/v/old/vlib/v/parser/tests/struct_embed_duplicate.vv new file mode 100644 index 0000000..46563a4 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/struct_embed_duplicate.vv @@ -0,0 +1,9 @@ +struct Abc { + foo int = 5 +} + +struct Xyz { + Abc + Abc + bar int +} diff --git a/v_windows/v/old/vlib/v/parser/tests/struct_embed_unknown_module.out b/v_windows/v/old/vlib/v/parser/tests/struct_embed_unknown_module.out new file mode 100644 index 0000000..5d4542c --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/struct_embed_unknown_module.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/struct_embed_unknown_module.vv:2:2: error: unknown module `custom` + 1 | struct WithEmbed { + 2 | custom.Foo + | ~~~~~~ + 3 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/parser/tests/struct_embed_unknown_module.vv b/v_windows/v/old/vlib/v/parser/tests/struct_embed_unknown_module.vv new file mode 100644 index 0000000..1e084a8 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/struct_embed_unknown_module.vv @@ -0,0 +1,3 @@ +struct WithEmbed { + custom.Foo +} diff --git a/v_windows/v/old/vlib/v/parser/tests/struct_embed_wrong_pos_long_err.out b/v_windows/v/old/vlib/v/parser/tests/struct_embed_wrong_pos_long_err.out new file mode 100644 index 0000000..a4bc579 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/struct_embed_wrong_pos_long_err.out @@ -0,0 +1,7 @@ +vlib/v/parser/tests/struct_embed_wrong_pos_long_err.vv:4:2: error: struct embedding must be declared at the beginning of the struct body + 2 | struct Foo2 { + 3 | mut: + 4 | cli.Command + | ~~~~~~~~~~~ + 5 | } + 6 | fn main() {} \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/parser/tests/struct_embed_wrong_pos_long_err.vv b/v_windows/v/old/vlib/v/parser/tests/struct_embed_wrong_pos_long_err.vv new file mode 100644 index 0000000..8dcc62e --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/struct_embed_wrong_pos_long_err.vv @@ -0,0 +1,6 @@ +import cli +struct Foo2 { +mut: + cli.Command +} +fn main() {} \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/parser/tests/struct_embed_wrong_pos_short_err.out b/v_windows/v/old/vlib/v/parser/tests/struct_embed_wrong_pos_short_err.out new file mode 100644 index 0000000..04240f8 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/struct_embed_wrong_pos_short_err.out @@ -0,0 +1,7 @@ +vlib/v/parser/tests/struct_embed_wrong_pos_short_err.vv:6:2: error: struct embedding must be declared at the beginning of the struct body + 4 | struct Foo2 { + 5 | mut: + 6 | Foo + | ~~~ + 7 | } + 8 | fn main() {} \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/parser/tests/struct_embed_wrong_pos_short_err.vv b/v_windows/v/old/vlib/v/parser/tests/struct_embed_wrong_pos_short_err.vv new file mode 100644 index 0000000..cc270fa --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/struct_embed_wrong_pos_short_err.vv @@ -0,0 +1,8 @@ +struct Foo { + foo string +} +struct Foo2 { +mut: + Foo +} +fn main() {} \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/parser/tests/struct_field_expected.out b/v_windows/v/old/vlib/v/parser/tests/struct_field_expected.out new file mode 100644 index 0000000..9fa06a2 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/struct_field_expected.out @@ -0,0 +1,7 @@ +vlib/v/parser/tests/struct_field_expected.vv:6:2: error: unexpected token `.`, expecting struct field name + 4 | + 5 | x = { + 6 | .a : 'Alpha' + | ^ + 7 | .b : 'Beta' + 8 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/struct_field_expected.vv b/v_windows/v/old/vlib/v/parser/tests/struct_field_expected.vv new file mode 100644 index 0000000..bf4929c --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/struct_field_expected.vv @@ -0,0 +1,9 @@ +enum TestEnum { + a b +} + +x = { + .a : 'Alpha' + .b : 'Beta' +} + diff --git a/v_windows/v/old/vlib/v/parser/tests/struct_field_unknown_module_a.out b/v_windows/v/old/vlib/v/parser/tests/struct_field_unknown_module_a.out new file mode 100644 index 0000000..81ac9db --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/struct_field_unknown_module_a.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/struct_field_unknown_module_a.vv:2:7: error: unknown module `ui` + 1 | struct Inter { + 2 | code ui.KeyCode + | ~~ + 3 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/struct_field_unknown_module_a.vv b/v_windows/v/old/vlib/v/parser/tests/struct_field_unknown_module_a.vv new file mode 100644 index 0000000..31058d5 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/struct_field_unknown_module_a.vv @@ -0,0 +1,3 @@ +struct Inter { + code ui.KeyCode +} diff --git a/v_windows/v/old/vlib/v/parser/tests/struct_field_unknown_module_b.out b/v_windows/v/old/vlib/v/parser/tests/struct_field_unknown_module_b.out new file mode 100644 index 0000000..e467979 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/struct_field_unknown_module_b.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/struct_field_unknown_module_b.vv:2:7: error: unknown module `term.unknownmod` + 1 | struct Inter { + 2 | code term.unknownmod.KeyCode + | ~~~~~~~~~~~~~~~ + 3 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/struct_field_unknown_module_b.vv b/v_windows/v/old/vlib/v/parser/tests/struct_field_unknown_module_b.vv new file mode 100644 index 0000000..3193a7c --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/struct_field_unknown_module_b.vv @@ -0,0 +1,3 @@ +struct Inter { + code term.unknownmod.KeyCode +} diff --git a/v_windows/v/old/vlib/v/parser/tests/struct_field_unknown_module_c.out b/v_windows/v/old/vlib/v/parser/tests/struct_field_unknown_module_c.out new file mode 100644 index 0000000..84bd41e --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/struct_field_unknown_module_c.out @@ -0,0 +1,6 @@ +vlib/v/parser/tests/struct_field_unknown_module_c.vv:4:7: error: unknown module `term.ui`; did you mean `ui`? + 2 | + 3 | struct Inter { + 4 | code term.ui.KeyCode + | ~~~~~~~ + 5 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/struct_field_unknown_module_c.vv b/v_windows/v/old/vlib/v/parser/tests/struct_field_unknown_module_c.vv new file mode 100644 index 0000000..cce4940 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/struct_field_unknown_module_c.vv @@ -0,0 +1,5 @@ +import term.ui + +struct Inter { + code term.ui.KeyCode +} diff --git a/v_windows/v/old/vlib/v/parser/tests/struct_module_section.out b/v_windows/v/old/vlib/v/parser/tests/struct_module_section.out new file mode 100644 index 0000000..3bc03f7 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/struct_module_section.out @@ -0,0 +1,12 @@ +vlib/v/parser/tests/struct_module_section.vv:16:3: error: field `i` of struct `S1` is immutable + 14 | + 15 | mut s := S1{} + 16 | s.i++ + | ^ + 17 | mut s2 := S2{} + 18 | s2.j++ +vlib/v/parser/tests/struct_module_section.vv:18:4: error: field `j` of struct `S2` is immutable + 16 | s.i++ + 17 | mut s2 := S2{} + 18 | s2.j++ + | ^ diff --git a/v_windows/v/old/vlib/v/parser/tests/struct_module_section.vv b/v_windows/v/old/vlib/v/parser/tests/struct_module_section.vv new file mode 100644 index 0000000..d15a49c --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/struct_module_section.vv @@ -0,0 +1,18 @@ +struct S1 { +pub mut: + v byte +module: + i int +} + +struct S2 { +module: + j int +mut: + v byte +} + +mut s := S1{} +s.i++ +mut s2 := S2{} +s2.j++ diff --git a/v_windows/v/old/vlib/v/parser/tests/struct_update_err.out b/v_windows/v/old/vlib/v/parser/tests/struct_update_err.out new file mode 100644 index 0000000..ee72f49 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/struct_update_err.out @@ -0,0 +1,7 @@ +vlib/v/parser/tests/struct_update_err.vv:15:3: error: unexpected token `...`, expecting name + 13 | f2 := Foo{ + 14 | name: 'f2' + 15 | ...f + | ~~~ + 16 | } + 17 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/struct_update_err.vv b/v_windows/v/old/vlib/v/parser/tests/struct_update_err.vv new file mode 100644 index 0000000..46aa523 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/struct_update_err.vv @@ -0,0 +1,17 @@ +struct Foo { + name string + age int +} + +struct Foo2 {} + +fn main() { + f := Foo{ + name: 'test' + age: 18 + } + f2 := Foo{ + name: 'f2' + ...f + } +} diff --git a/v_windows/v/old/vlib/v/parser/tests/too_many_generics_err.out b/v_windows/v/old/vlib/v/parser/tests/too_many_generics_err.out new file mode 100644 index 0000000..82cb5fb --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/too_many_generics_err.out @@ -0,0 +1,3 @@ +vlib/v/parser/tests/too_many_generics_err.vv:1:40: error: cannot have more than 9 generic parameters + 1 | fn too_many() {} + | ^ diff --git a/v_windows/v/old/vlib/v/parser/tests/too_many_generics_err.vv b/v_windows/v/old/vlib/v/parser/tests/too_many_generics_err.vv new file mode 100644 index 0000000..a42f2c1 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/too_many_generics_err.vv @@ -0,0 +1 @@ +fn too_many() {} diff --git a/v_windows/v/old/vlib/v/parser/tests/type_alias_existing_type_err.out b/v_windows/v/old/vlib/v/parser/tests/type_alias_existing_type_err.out new file mode 100644 index 0000000..6ea695f --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/type_alias_existing_type_err.out @@ -0,0 +1,7 @@ +vlib/v/parser/tests/type_alias_existing_type_err.vv:3:1: error: cannot register alias `Foo`, another type with this name exists + 1 | struct Foo{} + 2 | + 3 | type Foo = Foo + | ~~~~~~~~~~~~~~ + 4 | + 5 | fn main() { diff --git a/v_windows/v/old/vlib/v/parser/tests/type_alias_existing_type_err.vv b/v_windows/v/old/vlib/v/parser/tests/type_alias_existing_type_err.vv new file mode 100644 index 0000000..c1aeeed --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/type_alias_existing_type_err.vv @@ -0,0 +1,7 @@ +struct Foo{} + +type Foo = Foo + +fn main() { + +} diff --git a/v_windows/v/old/vlib/v/parser/tests/type_alias_same_type_err.out b/v_windows/v/old/vlib/v/parser/tests/type_alias_same_type_err.out new file mode 100644 index 0000000..c5b3797 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/type_alias_same_type_err.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/type_alias_same_type_err.vv:1:1: error: a type alias can not refer to itself: Foo + 1 | type Foo = Foo + | ~~~~~~~~~~~~~~ + 2 | + 3 | fn main() { diff --git a/v_windows/v/old/vlib/v/parser/tests/type_alias_same_type_err.vv b/v_windows/v/old/vlib/v/parser/tests/type_alias_same_type_err.vv new file mode 100644 index 0000000..4cf7265 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/type_alias_same_type_err.vv @@ -0,0 +1,5 @@ +type Foo = Foo + +fn main() { + +} diff --git a/v_windows/v/old/vlib/v/parser/tests/uncomplete_module_call_err.out b/v_windows/v/old/vlib/v/parser/tests/uncomplete_module_call_err.out new file mode 100644 index 0000000..8acb09d --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/uncomplete_module_call_err.out @@ -0,0 +1,5 @@ +vlib/v/parser/tests/uncomplete_module_call_err.vv:7:1: error: unexpected token `}`, expecting name + 5 | fn main() { + 6 | os. + 7 | } + | ^ diff --git a/v_windows/v/old/vlib/v/parser/tests/uncomplete_module_call_err.vv b/v_windows/v/old/vlib/v/parser/tests/uncomplete_module_call_err.vv new file mode 100644 index 0000000..7a3cb06 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/uncomplete_module_call_err.vv @@ -0,0 +1,7 @@ +module main + +import os + +fn main() { + os. +} diff --git a/v_windows/v/old/vlib/v/parser/tests/unexpected_expr.out b/v_windows/v/old/vlib/v/parser/tests/unexpected_expr.out new file mode 100644 index 0000000..46c1610 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/unexpected_expr.out @@ -0,0 +1,3 @@ +vlib/v/parser/tests/unexpected_expr.vv:1:5: error: invalid expression: unexpected keyword `break` + 1 | _ = break + | ~~~~~ diff --git a/v_windows/v/old/vlib/v/parser/tests/unexpected_expr.vv b/v_windows/v/old/vlib/v/parser/tests/unexpected_expr.vv new file mode 100644 index 0000000..25b0690 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/unexpected_expr.vv @@ -0,0 +1 @@ +_ = break diff --git a/v_windows/v/old/vlib/v/parser/tests/unexpected_keyword.out b/v_windows/v/old/vlib/v/parser/tests/unexpected_keyword.out new file mode 100644 index 0000000..08adaf7 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/unexpected_keyword.out @@ -0,0 +1,7 @@ +vlib/v/parser/tests/unexpected_keyword.vv:5:12: error: expecting method name + 3 | } + 4 | + 5 | fn (s Abc) import(name string) { + | ~~~~~~ + 6 | println(name) + 7 | } diff --git a/v_windows/v/old/vlib/v/parser/tests/unexpected_keyword.vv b/v_windows/v/old/vlib/v/parser/tests/unexpected_keyword.vv new file mode 100644 index 0000000..4d60303 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/unexpected_keyword.vv @@ -0,0 +1,12 @@ +struct Abc { + x int +} + +fn (s Abc) import(name string) { + println(name) +} + +fn main() { + s := Abc{} + s.import('lib') +} diff --git a/v_windows/v/old/vlib/v/parser/tests/unnecessary_mut.out b/v_windows/v/old/vlib/v/parser/tests/unnecessary_mut.out new file mode 100644 index 0000000..4399c9f --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/unnecessary_mut.out @@ -0,0 +1,7 @@ +vlib/v/parser/tests/unnecessary_mut.vv:3:5: error: remove unnecessary `mut` + 1 | fn main() { + 2 | mut x := 0 + 3 | if mut x == 0 { + | ~~~ + 4 | println(true) + 5 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/parser/tests/unnecessary_mut.vv b/v_windows/v/old/vlib/v/parser/tests/unnecessary_mut.vv new file mode 100644 index 0000000..6c7a470 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/unnecessary_mut.vv @@ -0,0 +1,7 @@ +fn main() { + mut x := 0 + if mut x == 0 { + println(true) + } + _ = x +} diff --git a/v_windows/v/old/vlib/v/parser/tests/unnecessary_mut_2.out b/v_windows/v/old/vlib/v/parser/tests/unnecessary_mut_2.out new file mode 100644 index 0000000..5f7dfee --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/unnecessary_mut_2.out @@ -0,0 +1,6 @@ +vlib/v/parser/tests/unnecessary_mut_2.vv:2:9: error: unexpected token `true` + 1 | fn main() { + 2 | if mut true { + | ~~~~ + 3 | println(true) + 4 | } \ No newline at end of file diff --git a/v_windows/v/old/vlib/v/parser/tests/unnecessary_mut_2.vv b/v_windows/v/old/vlib/v/parser/tests/unnecessary_mut_2.vv new file mode 100644 index 0000000..0df3f80 --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tests/unnecessary_mut_2.vv @@ -0,0 +1,5 @@ +fn main() { + if mut true { + println(true) + } +} diff --git a/v_windows/v/old/vlib/v/parser/tmpl.v b/v_windows/v/old/vlib/v/parser/tmpl.v new file mode 100644 index 0000000..1679e5b --- /dev/null +++ b/v_windows/v/old/vlib/v/parser/tmpl.v @@ -0,0 +1,238 @@ +// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +module parser + +import v.token +import v.errors +import os +import strings + +const tmpl_str_start = "sb.write_string('" + +const tmpl_str_end = "' ) " + +enum State { + html + css // ' { + state = .html + } else if is_html_open_tag('script', line) { + state = .js + } else if line == '' { + state = .html + } + if line.contains('@header') { + position := line.index('@header') or { 0 } + p.error_with_error(errors.Error{ + message: "Please use @include 'header' instead of @header (deprecated)" + file_path: template_file + pos: token.Position{ + len: '@header'.len + line_nr: tline_number + pos: start_of_line_pos + position + last_line: lines.len + } + reporter: .parser + }) + } else if line.contains('@footer') { + position := line.index('@footer') or { 0 } + p.error_with_error(errors.Error{ + message: "Please use @include 'footer' instead of @footer (deprecated)" + file_path: template_file + pos: token.Position{ + len: '@footer'.len + line_nr: tline_number + pos: start_of_line_pos + position + last_line: lines.len + } + reporter: .parser + }) + } + if line.contains('@include ') { + lines.delete(i) + mut file_name := line.split("'")[1] + mut file_ext := os.file_ext(file_name) + if file_ext == '' { + file_ext = '.html' + } + file_name = file_name.replace(file_ext, '') + // relative path, starting with the current folder + mut templates_folder := os.real_path(basepath) + if file_name.contains('/') && file_name.starts_with('/') { + // an absolute path + templates_folder = '' + } + file_path := os.real_path(os.join_path(templates_folder, '$file_name$file_ext')) + $if trace_tmpl ? { + eprintln('>>> basepath: "$basepath" , template_file: "$template_file" , fn_name: "$fn_name" , @include line: "$line" , file_name: "$file_name" , file_ext: "$file_ext" , templates_folder: "$templates_folder" , file_path: "$file_path"') + } + file_content := os.read_file(file_path) or { + position := line.index('@include ') or { 0 } + '@include '.len + p.error_with_error(errors.Error{ + message: 'Reading file $file_name from path: $file_path failed' + details: "Failed to @include '$file_name'" + file_path: template_file + pos: token.Position{ + len: '@include '.len + file_name.len + line_nr: tline_number + pos: start_of_line_pos + position + last_line: lines.len + } + reporter: .parser + }) + '' + } + file_splitted := file_content.split_into_lines().reverse() + for f in file_splitted { + tline_number-- + lines.insert(i, f) + } + i-- + } else if line.contains('@js ') { + pos := line.index('@js') or { continue } + source.write_string('') + } else if line.contains('@css ') { + pos := line.index('@css') or { continue } + source.write_string('') + } else if line.contains('@if ') { + source.writeln(parser.tmpl_str_end) + pos := line.index('@if') or { continue } + source.writeln('if ' + line[pos + 4..] + '{') + source.writeln(parser.tmpl_str_start) + } else if line.contains('@end') { + // Remove new line byte + source.go_back(1) + source.writeln(parser.tmpl_str_end) + source.writeln('}') + source.writeln(parser.tmpl_str_start) + } else if line.contains('@else') { + // Remove new line byte + source.go_back(1) + source.writeln(parser.tmpl_str_end) + source.writeln(' } else { ') + source.writeln(parser.tmpl_str_start) + } else if line.contains('@for') { + source.writeln(parser.tmpl_str_end) + pos := line.index('@for') or { continue } + source.writeln('for ' + line[pos + 4..] + '{') + source.writeln(parser.tmpl_str_start) + } else if state == .html && line.contains('span.') && line.ends_with('{') { + // `span.header {` => `` + class := line.find_between('span.', '{').trim_space() + source.writeln('') + in_span = true + } else if state == .html && line.contains('.') && line.ends_with('{') { + // `.header {` => `
      ` + class := line.find_between('.', '{').trim_space() + source.writeln('
      ') + } else if state == .html && line.contains('#') && line.ends_with('{') { + // `#header {` => `